summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGPL662
-rw-r--r--Artistic125
-rw-r--r--CREDITS15
-rw-r--r--FS/Changes5
-rw-r--r--FS/FS.pm442
-rw-r--r--FS/FS/AccessRight.pm314
-rw-r--r--FS/FS/CGI.pm327
-rw-r--r--FS/FS/ClientAPI.pm37
-rw-r--r--FS/FS/ClientAPI/Agent.pm125
-rw-r--r--FS/FS/ClientAPI/MasonComponent.pm46
-rw-r--r--FS/FS/ClientAPI/MyAccount.pm1428
-rw-r--r--FS/FS/ClientAPI/PrepaidPhone.pm253
-rw-r--r--FS/FS/ClientAPI/Signup.pm603
-rw-r--r--FS/FS/ClientAPI/passwd.pm46
-rw-r--r--FS/FS/ClientAPI_SessionCache.pm79
-rw-r--r--FS/FS/Conf.pm2679
-rw-r--r--FS/FS/ConfDefaults.pm86
-rw-r--r--FS/FS/ConfItem.pm63
-rw-r--r--FS/FS/Conf_compat17.pm2402
-rw-r--r--FS/FS/Cron/backup.pm43
-rw-r--r--FS/FS/Cron/bill.pm146
-rw-r--r--FS/FS/Cron/expire_user_pref.pm20
-rw-r--r--FS/FS/Cron/notify.pm149
-rw-r--r--FS/FS/Cron/vacuum.pm23
-rw-r--r--FS/FS/CurrentUser.pm67
-rw-r--r--FS/FS/Daemon.pm100
-rw-r--r--FS/FS/InitHandler.pm91
-rw-r--r--FS/FS/Mason.pm404
-rw-r--r--FS/FS/Mason/Request.pm78
-rw-r--r--FS/FS/Misc.pm852
-rw-r--r--FS/FS/Misc/prune.pm131
-rw-r--r--FS/FS/Msgcat.pm100
-rw-r--r--FS/FS/Pony.pm23
-rw-r--r--FS/FS/Record.pm2810
-rw-r--r--FS/FS/Report.pm46
-rw-r--r--FS/FS/Report/Table.pm27
-rw-r--r--FS/FS/Report/Table/Monthly.pm401
-rw-r--r--FS/FS/Schema.pm2287
-rw-r--r--FS/FS/SearchCache.pm96
-rw-r--r--FS/FS/Setup.pm541
-rw-r--r--FS/FS/TicketSystem.pm30
-rw-r--r--FS/FS/TicketSystem/RT_External.pm353
-rw-r--r--FS/FS/TicketSystem/RT_Internal.pm29
-rw-r--r--FS/FS/TicketSystem/RT_Libs.pm10
-rw-r--r--FS/FS/Tron.pm99
-rw-r--r--FS/FS/UI/Web.pm601
-rw-r--r--FS/FS/UI/Web/small_custview.pm129
-rw-r--r--FS/FS/UI/bytecount.pm96
-rw-r--r--FS/FS/UID.pm392
-rw-r--r--FS/FS/Upgrade.pm249
-rw-r--r--FS/FS/XMLRPC.pm166
-rw-r--r--FS/FS/Yori.pm73
-rw-r--r--FS/FS/access_group.pm162
-rw-r--r--FS/FS/access_groupagent.pm146
-rw-r--r--FS/FS/access_right.pm165
-rw-r--r--FS/FS/access_user.pm481
-rw-r--r--FS/FS/access_user_pref.pm129
-rw-r--r--FS/FS/access_usergroup.pm145
-rw-r--r--FS/FS/acct_rt_transaction.pm316
-rw-r--r--FS/FS/acct_snarf.pm128
-rwxr-xr-xFS/FS/addr_block.pm385
-rw-r--r--FS/FS/agent.pm464
-rw-r--r--FS/FS/agent_payment_gateway.pm139
-rw-r--r--FS/FS/agent_type.pm191
-rw-r--r--FS/FS/banned_pay.pm136
-rw-r--r--FS/FS/cdr.pm782
-rw-r--r--FS/FS/cdr/asterisk.pm45
-rw-r--r--FS/FS/cdr/bell_west.pm122
-rw-r--r--FS/FS/cdr/genband.pm120
-rw-r--r--FS/FS/cdr/genband_meetme.pm17
-rw-r--r--FS/FS/cdr/indosoft.pm71
-rw-r--r--FS/FS/cdr/netcentrex.pm783
-rw-r--r--FS/FS/cdr/nextone.pm26
-rw-r--r--FS/FS/cdr/openser.pm24
-rw-r--r--FS/FS/cdr/simple.pm52
-rw-r--r--FS/FS/cdr/simple2.pm51
-rw-r--r--FS/FS/cdr/taqua.pm171
-rw-r--r--FS/FS/cdr/troop.pm128
-rw-r--r--FS/FS/cdr/unitel.pm39
-rw-r--r--FS/FS/cdr_calltype.pm115
-rw-r--r--FS/FS/cdr_carrier.pm116
-rw-r--r--FS/FS/cdr_type.pm119
-rw-r--r--FS/FS/cdr_upstream_rate.pm138
-rw-r--r--FS/FS/clientapi_session.pm121
-rw-r--r--FS/FS/clientapi_session_field.pm126
-rw-r--r--FS/FS/conf.pm114
-rw-r--r--FS/FS/cust_bill.pm3265
-rw-r--r--FS/FS/cust_bill_ApplicationCommon.pm404
-rw-r--r--FS/FS/cust_bill_event.pm380
-rw-r--r--FS/FS/cust_bill_pay.pm165
-rw-r--r--FS/FS/cust_bill_pay_batch.pm120
-rw-r--r--FS/FS/cust_bill_pay_pkg.pm141
-rw-r--r--FS/FS/cust_bill_pkg.pm676
-rw-r--r--FS/FS/cust_bill_pkg_detail.pm184
-rw-r--r--FS/FS/cust_bill_pkg_display.pm158
-rw-r--r--FS/FS/cust_bill_pkg_tax_location.pm136
-rw-r--r--FS/FS/cust_credit.pm602
-rw-r--r--FS/FS/cust_credit_bill.pm168
-rw-r--r--FS/FS/cust_credit_bill_pkg.pm141
-rw-r--r--FS/FS/cust_credit_refund.pm186
-rw-r--r--FS/FS/cust_event.pm409
-rw-r--r--FS/FS/cust_location.pm196
-rw-r--r--FS/FS/cust_main.pm7151
-rw-r--r--FS/FS/cust_main/Import.pm427
-rw-r--r--FS/FS/cust_main_Mixin.pm269
-rw-r--r--FS/FS/cust_main_county.pm499
-rw-r--r--FS/FS/cust_main_invoice.pm184
-rw-r--r--FS/FS/cust_main_note.pm131
-rw-r--r--FS/FS/cust_pay.pm823
-rw-r--r--FS/FS/cust_pay_batch.pm277
-rw-r--r--FS/FS/cust_pay_pending.pm321
-rw-r--r--FS/FS/cust_pay_refund.pm188
-rw-r--r--FS/FS/cust_pay_void.pm225
-rw-r--r--FS/FS/cust_pkg.pm2775
-rw-r--r--FS/FS/cust_pkg_detail.pm140
-rw-r--r--FS/FS/cust_pkg_option.pm115
-rw-r--r--FS/FS/cust_pkg_reason.pm330
-rw-r--r--FS/FS/cust_refund.pm354
-rw-r--r--FS/FS/cust_svc.pm737
-rw-r--r--FS/FS/cust_svc_option.pm136
-rw-r--r--FS/FS/cust_tax_exempt.pm152
-rw-r--r--FS/FS/cust_tax_exempt_pkg.pm136
-rw-r--r--FS/FS/cust_tax_location.pm336
-rw-r--r--FS/FS/domain_record.pm438
-rw-r--r--FS/FS/export_svc.pm322
-rw-r--r--FS/FS/h_Common.pm124
-rw-r--r--FS/FS/h_cust_bill.pm33
-rw-r--r--FS/FS/h_cust_credit.pm33
-rw-r--r--FS/FS/h_cust_pay.pm33
-rw-r--r--FS/FS/h_cust_pkg.pm34
-rw-r--r--FS/FS/h_cust_pkg_reason.pm34
-rw-r--r--FS/FS/h_cust_svc.pm159
-rw-r--r--FS/FS/h_cust_tax_exempt.pm40
-rw-r--r--FS/FS/h_domain_record.pm33
-rw-r--r--FS/FS/h_svc_acct.pm78
-rw-r--r--FS/FS/h_svc_broadband.pm33
-rw-r--r--FS/FS/h_svc_domain.pm33
-rw-r--r--FS/FS/h_svc_external.pm33
-rw-r--r--FS/FS/h_svc_forward.pm85
-rw-r--r--FS/FS/h_svc_phone.pm33
-rw-r--r--FS/FS/h_svc_www.pm67
-rw-r--r--FS/FS/inventory_class.pm164
-rw-r--r--FS/FS/inventory_item.pm168
-rw-r--r--FS/FS/m2m_Common.pm170
-rw-r--r--FS/FS/m2name_Common.pm177
-rw-r--r--FS/FS/msgcat.pm166
-rw-r--r--FS/FS/nas.pm150
-rw-r--r--FS/FS/option_Common.pm345
-rw-r--r--FS/FS/part_bill_event.pm368
-rw-r--r--FS/FS/part_event.pm442
-rw-r--r--FS/FS/part_event/Action.pm227
-rw-r--r--FS/FS/part_event/Action/addpost.pm20
-rw-r--r--FS/FS/part_event/Action/apply.pm24
-rw-r--r--FS/FS/part_event/Action/bill.pm26
-rw-r--r--FS/FS/part_event/Action/cancel.pm30
-rw-r--r--FS/FS/part_event/Action/collect.pm26
-rw-r--r--FS/FS/part_event/Action/cust_bill_batch.pm25
-rw-r--r--FS/FS/part_event/Action/cust_bill_comp.pm28
-rw-r--r--FS/FS/part_event/Action/cust_bill_fee_percent.pm36
-rw-r--r--FS/FS/part_event/Action/cust_bill_realtime_card.pm28
-rw-r--r--FS/FS/part_event/Action/cust_bill_realtime_check.pm28
-rw-r--r--FS/FS/part_event/Action/cust_bill_realtime_lec.pm28
-rw-r--r--FS/FS/part_event/Action/cust_bill_send.pm23
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_agent.pm42
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_alternate.pm31
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_csv_ftp.pm50
-rw-r--r--FS/FS/part_event/Action/cust_bill_send_if_newest.pm38
-rw-r--r--FS/FS/part_event/Action/cust_bill_spool_csv.pm58
-rw-r--r--FS/FS/part_event/Action/cust_bill_suspend_if_balance.pm42
-rw-r--r--FS/FS/part_event/Action/fee.pm29
-rw-r--r--FS/FS/part_event/Action/pkg_referral_credit.pm60
-rw-r--r--FS/FS/part_event/Action/pkg_referral_credit_pkg.pm57
-rw-r--r--FS/FS/part_event/Action/suspend.pm32
-rw-r--r--FS/FS/part_event/Action/suspend_if_pkgpart.pm40
-rw-r--r--FS/FS/part_event/Action/suspend_unless_pkgpart.pm40
-rw-r--r--FS/FS/part_event/Condition.pm446
-rw-r--r--FS/FS/part_event/Condition/agent.pm37
-rw-r--r--FS/FS/part_event/Condition/agent_type.pm40
-rw-r--r--FS/FS/part_event/Condition/balance.pm48
-rw-r--r--FS/FS/part_event/Condition/balance_age.pm54
-rw-r--r--FS/FS/part_event/Condition/balance_under.pm42
-rw-r--r--FS/FS/part_event/Condition/cust_bill_age.pm46
-rw-r--r--FS/FS/part_event/Condition/cust_bill_has_service.pm54
-rw-r--r--FS/FS/part_event/Condition/cust_bill_owed.pm54
-rw-r--r--FS/FS/part_event/Condition/cust_bill_owed_under.pm49
-rw-r--r--FS/FS/part_event/Condition/cust_pay_batch_declined.pm51
-rw-r--r--FS/FS/part_event/Condition/cust_payments.pm43
-rw-r--r--FS/FS/part_event/Condition/cust_status.pm32
-rw-r--r--FS/FS/part_event/Condition/dundate.pm26
-rw-r--r--FS/FS/part_event/Condition/every.pm67
-rw-r--r--FS/FS/part_event/Condition/has_referral_custnum.pm24
-rw-r--r--FS/FS/part_event/Condition/once.pm55
-rw-r--r--FS/FS/part_event/Condition/once_percust.pm67
-rw-r--r--FS/FS/part_event/Condition/payby.pm50
-rw-r--r--FS/FS/part_event/Condition/pkg_age.pm58
-rw-r--r--FS/FS/part_event/Condition/pkg_class.pm38
-rw-r--r--FS/FS/part_event/Condition/pkg_notchange.pm31
-rw-r--r--FS/FS/part_event/Condition/pkg_pkgpart.pm39
-rw-r--r--FS/FS/part_event/Condition/pkg_recurring.pm31
-rw-r--r--FS/FS/part_event/Condition/pkg_status.pm37
-rw-r--r--FS/FS/part_event/Condition/pkg_unless_pkgpart.pm39
-rw-r--r--FS/FS/part_event_condition.pm352
-rw-r--r--FS/FS/part_event_condition_option.pm151
-rw-r--r--FS/FS/part_event_condition_option_option.pm129
-rw-r--r--FS/FS/part_event_option.pm214
-rw-r--r--FS/FS/part_export.pm470
-rw-r--r--FS/FS/part_export/acct_freeside.pm139
-rw-r--r--FS/FS/part_export/acct_plesk.pm121
-rw-r--r--FS/FS/part_export/acct_sql.pm310
-rw-r--r--FS/FS/part_export/apache.pm47
-rw-r--r--FS/FS/part_export/artera_turbo.pm181
-rw-r--r--FS/FS/part_export/bind.pm35
-rw-r--r--FS/FS/part_export/bind_slave.pm28
-rw-r--r--FS/FS/part_export/bsdshell.pm25
-rw-r--r--FS/FS/part_export/communigate_pro.pm178
-rw-r--r--FS/FS/part_export/communigate_pro_singledomain.pm37
-rw-r--r--FS/FS/part_export/cp.pm161
-rw-r--r--FS/FS/part_export/cpanel.pm192
-rw-r--r--FS/FS/part_export/cyrus.pm120
-rw-r--r--FS/FS/part_export/domain_shellcommands.pm165
-rw-r--r--FS/FS/part_export/domain_sql.pm238
-rw-r--r--FS/FS/part_export/everyone_net.pm132
-rw-r--r--FS/FS/part_export/forward_shellcommands.pm182
-rw-r--r--FS/FS/part_export/globalpops_voip.pm370
-rw-r--r--FS/FS/part_export/http.pm134
-rw-r--r--FS/FS/part_export/infostreet.pm277
-rw-r--r--FS/FS/part_export/internal_diddb.pm134
-rw-r--r--FS/FS/part_export/ldap.pm294
-rw-r--r--FS/FS/part_export/nas_wrapper.pm311
-rw-r--r--FS/FS/part_export/null.pm13
-rw-r--r--FS/FS/part_export/passwdfile.pm18
-rw-r--r--FS/FS/part_export/phone_shellcommands.pm140
-rw-r--r--FS/FS/part_export/phone_sqlradius.pm158
-rw-r--r--FS/FS/part_export/postfix.pm32
-rw-r--r--FS/FS/part_export/prizm.pm549
-rw-r--r--FS/FS/part_export/radiator.pm167
-rw-r--r--FS/FS/part_export/router.pm375
-rw-r--r--FS/FS/part_export/shellcommands.pm401
-rw-r--r--FS/FS/part_export/shellcommands_withdomain.pm112
-rw-r--r--FS/FS/part_export/snmp.pm256
-rw-r--r--FS/FS/part_export/soma.pm412
-rw-r--r--FS/FS/part_export/sqlmail.pm220
-rw-r--r--FS/FS/part_export/sqlradius.pm814
-rw-r--r--FS/FS/part_export/sqlradius_withdomain.pm28
-rw-r--r--FS/FS/part_export/sysvshell.pm25
-rw-r--r--FS/FS/part_export/textradius.pm191
-rw-r--r--FS/FS/part_export/trango.pm434
-rw-r--r--FS/FS/part_export/vitelity.pm239
-rw-r--r--FS/FS/part_export/vpopmail.pm254
-rw-r--r--FS/FS/part_export/www_plesk.pm138
-rw-r--r--FS/FS/part_export/www_shellcommands.pm190
-rw-r--r--FS/FS/part_export_option.pm134
-rw-r--r--FS/FS/part_pkg.pm1333
-rw-r--r--FS/FS/part_pkg/base_delayed.pm52
-rw-r--r--FS/FS/part_pkg/base_rate.pm96
-rw-r--r--FS/FS/part_pkg/bulk.pm96
-rw-r--r--FS/FS/part_pkg/flat.pm211
-rw-r--r--FS/FS/part_pkg/flat_comission.pm67
-rw-r--r--FS/FS/part_pkg/flat_comission_cust.pm65
-rw-r--r--FS/FS/part_pkg/flat_comission_pkg.pm58
-rw-r--r--FS/FS/part_pkg/flat_delayed.pm69
-rw-r--r--FS/FS/part_pkg/flat_introrate.pm68
-rw-r--r--FS/FS/part_pkg/incomplete/billoneday.pm48
-rw-r--r--FS/FS/part_pkg/prepaid.pm40
-rw-r--r--FS/FS/part_pkg/prorate.pm123
-rw-r--r--FS/FS/part_pkg/prorate_delayed.pm67
-rw-r--r--FS/FS/part_pkg/sesmon_hour.pm57
-rw-r--r--FS/FS/part_pkg/sesmon_minute.pm56
-rw-r--r--FS/FS/part_pkg/sql_external.pm77
-rw-r--r--FS/FS/part_pkg/sql_generic.pm88
-rw-r--r--FS/FS/part_pkg/sqlradacct_hour.pm171
-rw-r--r--FS/FS/part_pkg/subscription.pm109
-rw-r--r--FS/FS/part_pkg/voip_cdr.pm612
-rw-r--r--FS/FS/part_pkg/voip_sqlradacct.pm194
-rw-r--r--FS/FS/part_pkg_link.pm157
-rw-r--r--FS/FS/part_pkg_option.pm150
-rw-r--r--FS/FS/part_pkg_taxclass.pm158
-rw-r--r--FS/FS/part_pkg_taxoverride.pm119
-rw-r--r--FS/FS/part_pkg_taxproduct.pm136
-rw-r--r--FS/FS/part_pkg_taxrate.pm405
-rw-r--r--FS/FS/part_pop_local.pm113
-rw-r--r--FS/FS/part_referral.pm208
-rw-r--r--FS/FS/part_svc.pm838
-rw-r--r--FS/FS/part_svc_column.pm120
-rwxr-xr-xFS/FS/part_svc_router.pm33
-rwxr-xr-xFS/FS/part_virtual_field.pm301
-rw-r--r--FS/FS/pay_batch.pm538
-rw-r--r--FS/FS/payby.pm194
-rw-r--r--FS/FS/payinfo_Mixin.pm290
-rw-r--r--FS/FS/payinfo_transaction_Mixin.pm123
-rw-r--r--FS/FS/payment_gateway.pm200
-rw-r--r--FS/FS/payment_gateway_option.pm126
-rw-r--r--FS/FS/phone_avail.pm186
-rw-r--r--FS/FS/pkg_category.pm113
-rw-r--r--FS/FS/pkg_class.pm141
-rw-r--r--FS/FS/pkg_referral.pm126
-rw-r--r--FS/FS/pkg_svc.pm160
-rw-r--r--FS/FS/port.pm154
-rw-r--r--FS/FS/prepay_credit.pm202
-rw-r--r--FS/FS/queue.pm496
-rw-r--r--FS/FS/queue_arg.pm117
-rw-r--r--FS/FS/queue_depend.pm121
-rw-r--r--FS/FS/raddb.pm1912
-rw-r--r--FS/FS/radius_usergroup.pm131
-rw-r--r--FS/FS/rate.pm415
-rw-r--r--FS/FS/rate_detail.pm245
-rw-r--r--FS/FS/rate_prefix.pm160
-rw-r--r--FS/FS/rate_region.pm315
-rw-r--r--FS/FS/reason.pm184
-rw-r--r--FS/FS/reason_type.pm211
-rw-r--r--FS/FS/reg_code.pm223
-rw-r--r--FS/FS/reg_code_pkg.pm139
-rw-r--r--FS/FS/registrar.pm119
-rwxr-xr-xFS/FS/router.pm152
-rw-r--r--FS/FS/session.pm265
-rw-r--r--FS/FS/svc_Common.pm852
-rw-r--r--FS/FS/svc_External_Common.pm199
-rw-r--r--FS/FS/svc_Parent_Mixin.pm103
-rw-r--r--FS/FS/svc_acct.pm2683
-rw-r--r--FS/FS/svc_acct_pop.pm206
-rwxr-xr-xFS/FS/svc_broadband.pm342
-rw-r--r--FS/FS/svc_domain.pm480
-rw-r--r--FS/FS/svc_external.pm204
-rw-r--r--FS/FS/svc_forward.pm371
-rw-r--r--FS/FS/svc_phone.pm341
-rw-r--r--FS/FS/svc_www.pm312
-rw-r--r--FS/FS/tax_class.pm392
-rw-r--r--FS/FS/tax_rate.pm1080
-rw-r--r--FS/FS/type_pkgs.pm130
-rw-r--r--FS/FS/usage_class.pm143
-rw-r--r--FS/MANIFEST436
-rw-r--r--FS/MANIFEST.SKIP1
-rw-r--r--FS/Makefile.PL10
-rwxr-xr-xFS/bin/freeside-addgroup50
-rw-r--r--FS/bin/freeside-addoutsource32
-rw-r--r--FS/bin/freeside-addoutsourceuser18
-rw-r--r--FS/bin/freeside-adduser119
-rwxr-xr-xFS/bin/freeside-apply-credits21
-rw-r--r--FS/bin/freeside-cdrd160
-rw-r--r--FS/bin/freeside-cdrrewrited129
-rwxr-xr-xFS/bin/freeside-count-active-customers17
-rwxr-xr-xFS/bin/freeside-daily104
-rwxr-xr-xFS/bin/freeside-dbdef-create47
-rwxr-xr-xFS/bin/freeside-dedup-cust_bill_pkg_detail-header57
-rwxr-xr-xFS/bin/freeside-delete-addr_blocks31
-rw-r--r--FS/bin/freeside-deloutsource14
-rw-r--r--FS/bin/freeside-deloutsourceuser6
-rw-r--r--FS/bin/freeside-deluser64
-rwxr-xr-xFS/bin/freeside-disable-reasons64
-rwxr-xr-xFS/bin/freeside-email55
-rwxr-xr-xFS/bin/freeside-expiration-alerter241
-rwxr-xr-xFS/bin/freeside-fetch93
-rwxr-xr-xFS/bin/freeside-history-requeue100
-rwxr-xr-xFS/bin/freeside-init-config45
-rwxr-xr-xFS/bin/freeside-monthly91
-rw-r--r--FS/bin/freeside-prepaidd106
-rwxr-xr-xFS/bin/freeside-prune-applications63
-rw-r--r--FS/bin/freeside-queued239
-rw-r--r--FS/bin/freeside-radgroup76
-rw-r--r--FS/bin/freeside-reexport71
-rwxr-xr-xFS/bin/freeside-reset-fixed69
-rw-r--r--FS/bin/freeside-selfservice-server240
-rw-r--r--FS/bin/freeside-setinvoice42
-rwxr-xr-xFS/bin/freeside-setup165
-rwxr-xr-xFS/bin/freeside-sqlradius-dedup-group82
-rw-r--r--FS/bin/freeside-sqlradius-radacctd145
-rwxr-xr-xFS/bin/freeside-sqlradius-reset103
-rw-r--r--FS/bin/freeside-sqlradius-seconds58
-rwxr-xr-xFS/bin/freeside-sqlradius-set-lastlog102
-rwxr-xr-xFS/bin/freeside-upgrade215
-rw-r--r--FS/bin/freeside-yori16
-rw-r--r--FS/t/AccessRight.t5
-rw-r--r--FS/t/CGI.t5
-rw-r--r--FS/t/ClientAPI.t5
-rw-r--r--FS/t/ClientAPI_SessionCache.t5
-rw-r--r--FS/t/Conf.t5
-rw-r--r--FS/t/ConfDefaults.t5
-rw-r--r--FS/t/ConfItem.t5
-rw-r--r--FS/t/Cron-backup.t5
-rw-r--r--FS/t/Cron-bill.t5
-rw-r--r--FS/t/Cron-vacuum.t5
-rw-r--r--FS/t/Daemon.t5
-rw-r--r--FS/t/InitHandler.t5
-rw-r--r--FS/t/Misc.t5
-rw-r--r--FS/t/Msgcat.t5
-rw-r--r--FS/t/Record.t5
-rw-r--r--FS/t/Report-Table-Monthly.t5
-rw-r--r--FS/t/Report-Table.t5
-rw-r--r--FS/t/Report.t5
-rw-r--r--FS/t/SearchCache.t5
-rw-r--r--FS/t/UID.t5
-rw-r--r--FS/t/access_group.t5
-rw-r--r--FS/t/access_groupagent.t5
-rw-r--r--FS/t/access_right.t5
-rw-r--r--FS/t/access_user.t5
-rw-r--r--FS/t/access_user_pref.t5
-rw-r--r--FS/t/access_usergroup.t5
-rw-r--r--FS/t/acct_rt_transaction.t5
-rw-r--r--FS/t/acct_snarf.t5
-rw-r--r--FS/t/addr_block.t5
-rw-r--r--FS/t/agent.t5
-rw-r--r--FS/t/agent_payment_gateway.t5
-rw-r--r--FS/t/agent_type.t5
-rw-r--r--FS/t/banned_pay.t5
-rw-r--r--FS/t/cdr.t5
-rw-r--r--FS/t/cdr_calltype.t5
-rw-r--r--FS/t/cdr_carrier.t5
-rw-r--r--FS/t/cdr_type.t5
-rw-r--r--FS/t/cdr_upstream_rate.t5
-rw-r--r--FS/t/clientapi_session.t5
-rw-r--r--FS/t/clientapi_session_field.t5
-rw-r--r--FS/t/conf.t5
-rw-r--r--FS/t/cust_bill.t5
-rw-r--r--FS/t/cust_bill_ApplicationCommon.t5
-rw-r--r--FS/t/cust_bill_event.t5
-rw-r--r--FS/t/cust_bill_pay.t5
-rw-r--r--FS/t/cust_bill_pay_batch.t5
-rw-r--r--FS/t/cust_bill_pay_pkg.t5
-rw-r--r--FS/t/cust_bill_pkg.t5
-rw-r--r--FS/t/cust_bill_pkg_detail.t5
-rw-r--r--FS/t/cust_bill_pkg_display.t5
-rw-r--r--FS/t/cust_bill_pkg_tax_location.t5
-rw-r--r--FS/t/cust_credit.t5
-rw-r--r--FS/t/cust_credit_bill.t5
-rw-r--r--FS/t/cust_credit_bill_pkg.t5
-rw-r--r--FS/t/cust_credit_refund.t5
-rw-r--r--FS/t/cust_event.t5
-rw-r--r--FS/t/cust_location.t5
-rw-r--r--FS/t/cust_main.t5
-rw-r--r--FS/t/cust_main_Mixin.t5
-rw-r--r--FS/t/cust_main_county.t5
-rw-r--r--FS/t/cust_main_invoice.t5
-rw-r--r--FS/t/cust_main_note.t5
-rw-r--r--FS/t/cust_pay.t5
-rw-r--r--FS/t/cust_pay_batch.t5
-rw-r--r--FS/t/cust_pay_pending.t5
-rw-r--r--FS/t/cust_pay_refund.t5
-rw-r--r--FS/t/cust_pay_void.t5
-rw-r--r--FS/t/cust_pkg.t5
-rw-r--r--FS/t/cust_pkg_detail.t5
-rw-r--r--FS/t/cust_pkg_option.t5
-rw-r--r--FS/t/cust_pkg_reason.t5
-rw-r--r--FS/t/cust_refund.t5
-rw-r--r--FS/t/cust_svc.t5
-rw-r--r--FS/t/cust_svc_option.t5
-rw-r--r--FS/t/cust_tax_exempt.t5
-rw-r--r--FS/t/cust_tax_exempt_pkg.t5
-rw-r--r--FS/t/cust_tax_location.t5
-rw-r--r--FS/t/domain_record.t5
-rw-r--r--FS/t/export_svc.t5
-rw-r--r--FS/t/h_Common.t5
-rw-r--r--FS/t/h_cust_bill.t5
-rw-r--r--FS/t/h_cust_credit.t5
-rw-r--r--FS/t/h_cust_pay.t5
-rw-r--r--FS/t/h_cust_pkg.t5
-rw-r--r--FS/t/h_cust_pkg_reason.t5
-rw-r--r--FS/t/h_cust_svc.t5
-rw-r--r--FS/t/h_cust_tax_exempt.t5
-rw-r--r--FS/t/h_domain_record.t5
-rw-r--r--FS/t/h_svc_acct.t5
-rw-r--r--FS/t/h_svc_broadband.t5
-rw-r--r--FS/t/h_svc_domain.t5
-rw-r--r--FS/t/h_svc_external.t5
-rw-r--r--FS/t/h_svc_forward.t5
-rw-r--r--FS/t/h_svc_www.t5
-rw-r--r--FS/t/inventory_class.t5
-rw-r--r--FS/t/inventory_item.t5
-rw-r--r--FS/t/msgcat.t5
-rw-r--r--FS/t/nas.t5
-rw-r--r--FS/t/option_Common.t5
-rw-r--r--FS/t/part_bill_event.t5
-rw-r--r--FS/t/part_event-Action.t5
-rw-r--r--FS/t/part_event-Condition.t5
-rw-r--r--FS/t/part_event.t5
-rw-r--r--FS/t/part_event_condition.t5
-rw-r--r--FS/t/part_event_condition_option.t5
-rw-r--r--FS/t/part_event_condition_option_option.t5
-rw-r--r--FS/t/part_event_option.t5
-rw-r--r--FS/t/part_export-acct_sql.t5
-rw-r--r--FS/t/part_export-apache.t5
-rw-r--r--FS/t/part_export-bind.t5
-rw-r--r--FS/t/part_export-bind_slave.t5
-rw-r--r--FS/t/part_export-bsdshell.t5
-rw-r--r--FS/t/part_export-communigate_pro.t5
-rw-r--r--FS/t/part_export-communigate_pro_singledomain.t5
-rw-r--r--FS/t/part_export-cp.t5
-rw-r--r--FS/t/part_export-cyrus.t5
-rw-r--r--FS/t/part_export-domain_shellcommands.t5
-rw-r--r--FS/t/part_export-forward_shellcommands.t5
-rw-r--r--FS/t/part_export-http.t5
-rw-r--r--FS/t/part_export-infostreet.t5
-rw-r--r--FS/t/part_export-ldap.t5
-rw-r--r--FS/t/part_export-null.t5
-rw-r--r--FS/t/part_export-passwdfile.t5
-rw-r--r--FS/t/part_export-postfix.t5
-rw-r--r--FS/t/part_export-radiator.t5
-rw-r--r--FS/t/part_export-router.t5
-rw-r--r--FS/t/part_export-shellcommands.t5
-rw-r--r--FS/t/part_export-shellcommands_withdomain.t5
-rw-r--r--FS/t/part_export-sqlmail.t5
-rw-r--r--FS/t/part_export-sqlradius.t5
-rw-r--r--FS/t/part_export-sqlradius_withdomain.t5
-rw-r--r--FS/t/part_export-sysvshell.t5
-rw-r--r--FS/t/part_export-textradius.t5
-rw-r--r--FS/t/part_export-vpopmail.t5
-rw-r--r--FS/t/part_export-www_shellcommands.t5
-rw-r--r--FS/t/part_export.t5
-rw-r--r--FS/t/part_export_option.t5
-rw-r--r--FS/t/part_pkg-flat.t5
-rw-r--r--FS/t/part_pkg-flat_comission.t5
-rw-r--r--FS/t/part_pkg-flat_comission_cust.t5
-rw-r--r--FS/t/part_pkg-flat_comission_pkg.t5
-rw-r--r--FS/t/part_pkg-flat_delayed.t5
-rw-r--r--FS/t/part_pkg-prorate.t5
-rw-r--r--FS/t/part_pkg-sesmon_hour.t5
-rw-r--r--FS/t/part_pkg-sesmon_minute.t5
-rw-r--r--FS/t/part_pkg-sql_external.t5
-rw-r--r--FS/t/part_pkg-sql_generic.t5
-rw-r--r--FS/t/part_pkg-sqlradacct_hour.t5
-rw-r--r--FS/t/part_pkg-subscription.t5
-rw-r--r--FS/t/part_pkg-voip_cdr.t5
-rw-r--r--FS/t/part_pkg-voip_sqlradacct.t5
-rw-r--r--FS/t/part_pkg.t5
-rw-r--r--FS/t/part_pkg_link.t5
-rw-r--r--FS/t/part_pkg_option.t5
-rw-r--r--FS/t/part_pkg_taxclass.t5
-rw-r--r--FS/t/part_pkg_taxoverride.t5
-rw-r--r--FS/t/part_pkg_taxproduct.t5
-rw-r--r--FS/t/part_pkg_taxrate.t5
-rw-r--r--FS/t/part_pop_local.t5
-rw-r--r--FS/t/part_referral.t5
-rw-r--r--FS/t/part_svc.t5
-rw-r--r--FS/t/part_svc_column.t5
-rw-r--r--FS/t/pay_batch.t5
-rw-r--r--FS/t/payby.t5
-rw-r--r--FS/t/payinfo_Mixin.t5
-rw-r--r--FS/t/payment_gateway.t5
-rw-r--r--FS/t/payment_gateway_option.t5
-rw-r--r--FS/t/phone_avail.t5
-rw-r--r--FS/t/pkg_category.t5
-rw-r--r--FS/t/pkg_class.t5
-rw-r--r--FS/t/pkg_referral.t5
-rw-r--r--FS/t/pkg_svc.t5
-rw-r--r--FS/t/port.t5
-rw-r--r--FS/t/prepay_credit.t5
-rw-r--r--FS/t/queue.t5
-rw-r--r--FS/t/queue_arg.t5
-rw-r--r--FS/t/queue_depend.t5
-rw-r--r--FS/t/raddb.t5
-rw-r--r--FS/t/radius_usergroup.t5
-rw-r--r--FS/t/rate.t5
-rw-r--r--FS/t/rate_detail.t5
-rw-r--r--FS/t/rate_prefix.t5
-rw-r--r--FS/t/rate_region.t5
-rw-r--r--FS/t/reason.t5
-rw-r--r--FS/t/reason_type.t5
-rw-r--r--FS/t/reg_code.t5
-rw-r--r--FS/t/reg_code_pkg.t5
-rw-r--r--FS/t/registrar.t5
-rw-r--r--FS/t/router.t5
-rw-r--r--FS/t/session.t5
-rw-r--r--FS/t/svc_Common.t5
-rw-r--r--FS/t/svc_External_Common.t5
-rw-r--r--FS/t/svc_Parent_Mixin.t5
-rw-r--r--FS/t/svc_acct.t5
-rw-r--r--FS/t/svc_acct_pop.t5
-rw-r--r--FS/t/svc_broadband.t5
-rw-r--r--FS/t/svc_domain.t5
-rw-r--r--FS/t/svc_external.t5
-rw-r--r--FS/t/svc_forward.t5
-rw-r--r--FS/t/svc_phone.t5
-rw-r--r--FS/t/svc_www.t5
-rw-r--r--FS/t/tax_class.t5
-rw-r--r--FS/t/tax_rate.t5
-rw-r--r--FS/t/type_pkgs.t5
-rw-r--r--FS/t/usage_class.t5
-rw-r--r--INSTALL4
-rw-r--r--Makefile426
-rw-r--r--README55
-rw-r--r--TODO530
-rwxr-xr-xbin/add-history-records.pl139
-rwxr-xr-xbin/all-postal-no-email22
-rwxr-xr-xbin/apache.export94
-rw-r--r--bin/artera.import75
-rw-r--r--bin/backup-dvd45
-rwxr-xr-xbin/bill188
-rwxr-xr-xbin/bill-as-nextmonth5
-rwxr-xr-xbin/bill-as-nextmonth-BILL5
-rwxr-xr-xbin/bill-as-nextyear5
-rwxr-xr-xbin/bill-as-nextyear-BILL5
-rwxr-xr-xbin/bill-for-nextmonth5
-rwxr-xr-xbin/bill-for-nextyear5
-rwxr-xr-xbin/bill-nextmonth5
-rwxr-xr-xbin/bill-nextyear5
-rw-r--r--bin/billco-upload20
-rwxr-xr-xbin/bind.export195
-rwxr-xr-xbin/bind.import235
-rw-r--r--bin/breakdown-bill-applications25
-rwxr-xr-xbin/bsdshell.export114
-rwxr-xr-xbin/cch_tax_tool59
-rwxr-xr-xbin/cdr.http_and_import108
-rw-r--r--bin/cdr.import28
-rwxr-xr-xbin/cdr.sftp_and_import112
-rwxr-xr-xbin/cdr_calltype.import41
-rwxr-xr-xbin/cdr_upstream_rate.import142
-rw-r--r--bin/create-fetchmailrc47
-rwxr-xr-xbin/customer-faker124
-rwxr-xr-xbin/dbdef-create85
-rwxr-xr-xbin/expand-country29
-rw-r--r--bin/explain-ar-total.sql976
-rw-r--r--bin/find-overapplied27
-rwxr-xr-xbin/fix-sequences69
-rw-r--r--bin/follow-tax-rename52
-rwxr-xr-xbin/freeside-create-initial-data31
-rwxr-xr-xbin/freeside-init60
-rw-r--r--bin/freeside-migrate-events229
-rwxr-xr-xbin/freeside-session-kill103
-rwxr-xr-xbin/freeside-upgrade-unicode72
-rw-r--r--bin/freeside.import146
-rwxr-xr-xbin/fs-migrate-cust_tax_exempt323
-rwxr-xr-xbin/fs-migrate-part_svc41
-rwxr-xr-xbin/fs-migrate-payref31
-rwxr-xr-xbin/fs-migrate-svc_acct_sm227
-rwxr-xr-xbin/fs-radius-add-check68
-rwxr-xr-xbin/fs-radius-add-reply69
-rwxr-xr-xbin/fs-setup542
-rwxr-xr-xbin/generate-prepay35
-rwxr-xr-xbin/generate-raddb53
-rwxr-xr-xbin/generate-table-module92
-rwxr-xr-xbin/generate-tests21
-rwxr-xr-xbin/import-county-tax-rates30
-rwxr-xr-xbin/import-optigold.pl1077
-rwxr-xr-xbin/import-tax-rates56
-rwxr-xr-xbin/ispman.ldap.import114
-rwxr-xr-xbin/japan.pl32
-rwxr-xr-xbin/mapsecrets2access_user87
-rwxr-xr-xbin/masonize80
-rwxr-xr-xbin/passwd.import121
-rwxr-xr-xbin/payment-faker54
-rw-r--r--bin/pg-readonly24
-rwxr-xr-xbin/pg-version13
-rwxr-xr-xbin/pod2x154
-rwxr-xr-xbin/postfix.export122
-rwxr-xr-xbin/postfix_courierimap.import137
-rwxr-xr-xbin/print-schema7
-rwxr-xr-xbin/rate-us.import109
-rw-r--r--bin/rate.delete3
-rwxr-xr-xbin/rate.import95
-rwxr-xr-xbin/reset-cust_credit-otaker88
-rwxr-xr-xbin/rollback38
-rwxr-xr-xbin/rotate-cdrs38
-rwxr-xr-xbin/rt-drop-tables29
-rw-r--r--bin/rt-update-links36
-rw-r--r--bin/sendmail.import178
-rw-r--r--bin/sequences.reset32
-rwxr-xr-xbin/shadow.reimport125
-rwxr-xr-xbin/slony-setup109
-rwxr-xr-xbin/sqlradius-norealm.reimport113
-rw-r--r--bin/sqlradius.import152
-rwxr-xr-xbin/sqlradius.reimport160
-rwxr-xr-xbin/strip-eps20
-rwxr-xr-xbin/svc_acct.export351
-rwxr-xr-xbin/svc_acct.import118
-rwxr-xr-xbin/svc_acct_pop.import59
-rwxr-xr-xbin/svc_acct_sm.export221
-rwxr-xr-xbin/svc_acct_sm.import252
-rwxr-xr-xbin/svc_broadband.renumber84
-rwxr-xr-xbin/svc_domain.erase15
-rwxr-xr-xbin/sysvshell.export112
-rw-r--r--bin/test_scrub48
-rwxr-xr-xbin/tron-scan24
-rw-r--r--conf/address4
-rw-r--r--conf/agent_defaultpkg0
-rw-r--r--conf/alerter_template18
-rw-r--r--conf/blank_logo.eps22
-rw-r--r--conf/company_address2
-rw-r--r--conf/company_name1
-rw-r--r--conf/cust_pkg-change_svcpart0
-rw-r--r--conf/declinetemplate10
-rw-r--r--conf/domain1
-rw-r--r--conf/impending_recur_template20
-rw-r--r--conf/invoice_from1
-rw-r--r--conf/invoice_html226
-rw-r--r--conf/invoice_html_statement124
-rw-r--r--conf/invoice_latex334
-rw-r--r--conf/invoice_latex.diff138
-rw-r--r--conf/invoice_latex_statement244
-rw-r--r--conf/invoice_latexcoupon36
-rw-r--r--conf/invoice_latexfooter1
-rw-r--r--conf/invoice_latexnotes8
-rw-r--r--conf/invoice_latexnotes_statement8
-rw-r--r--conf/invoice_latexsmallfooter1
-rw-r--r--conf/invoice_template26
-rw-r--r--conf/invoice_template_statement26
-rw-r--r--conf/locale1
-rw-r--r--conf/logo.eps13510
-rw-r--r--conf/logo.pngbin0 -> 4887 bytes
-rw-r--r--conf/maxsearchrecordsperpage1
-rw-r--r--conf/payment_receipt_email26
-rw-r--r--conf/registries/internic/from1
-rw-r--r--conf/registries/internic/nameservers3
-rw-r--r--conf/registries/internic/tech_contact1
-rw-r--r--conf/registries/internic/template231
-rw-r--r--conf/registries/internic/to1
-rw-r--r--conf/secrets3
-rw-r--r--conf/shells5
-rw-r--r--conf/show-msgcat-codes0
-rw-r--r--conf/smtpmachine2
-rw-r--r--conf/soadefaultttl1
-rw-r--r--conf/soaexpire1
-rw-r--r--conf/soarefresh1
-rw-r--r--conf/soaretry1
-rw-r--r--conf/ticket_system1
-rw-r--r--conf/welcome_letter121
-rw-r--r--debian/README.Debian25
-rw-r--r--debian/TODO38
-rw-r--r--debian/changelog6
-rw-r--r--debian/compat1
-rw-r--r--debian/config19
-rw-r--r--debian/control59
-rw-r--r--debian/copyright45
-rw-r--r--debian/cron.d4
-rw-r--r--debian/dbconfig-common.install90
-rw-r--r--debian/dbconfig-common.upgrade3
-rw-r--r--debian/freeside-webui.links4
-rw-r--r--debian/freeside.apache-alias.conf1
-rw-r--r--debian/freeside.default12
-rw-r--r--debian/freeside.docs1
-rw-r--r--debian/init.d.ex157
-rw-r--r--debian/init.d.lsb.ex281
-rw-r--r--debian/postinst54
-rw-r--r--debian/postrm48
-rw-r--r--debian/preinst100
-rw-r--r--debian/prerm46
-rwxr-xr-xdebian/rules230
-rw-r--r--debian/templates0
-rwxr-xr-xeg/TEMPLATE_cust_main.import23
-rw-r--r--eg/cdr_template.pm99
-rw-r--r--eg/export_template.pm113
-rw-r--r--eg/part_event-Action-template.pm55
-rw-r--r--eg/part_event-Condition-template.pm57
-rw-r--r--eg/table_template-svc.pm212
-rw-r--r--eg/table_template.pm116
-rwxr-xr-xeg/xmlrpc-example.pl23
-rw-r--r--etc/abbr_state.txt72
-rwxr-xr-xetc/acp_logfile-parse197
-rwxr-xr-xetc/example-direct-cardin67
-rw-r--r--etc/fslongtable.sty439
-rwxr-xr-xetc/megapop.pl114
-rw-r--r--etc/sql-reserved-words.txt103
-rwxr-xr-xfs_passwd/fs_passwd24
-rwxr-xr-xfs_passwd/fs_passwd_server73
-rwxr-xr-xfs_passwd/fs_passwdd49
-rwxr-xr-xfs_radlog/fs_radlogd51
-rwxr-xr-xfs_selfservice/DEPLOY30
-rw-r--r--fs_selfservice/FS-SelfService/Changes6
-rw-r--r--fs_selfservice/FS-SelfService/MANIFEST8
-rw-r--r--fs_selfservice/FS-SelfService/Makefile.PL20
-rw-r--r--fs_selfservice/FS-SelfService/SelfService.pm1707
-rw-r--r--fs_selfservice/FS-SelfService/SelfService/FreeRadiusVoip.pm61
-rw-r--r--fs_selfservice/FS-SelfService/SelfService/XMLRPC.pm88
-rw-r--r--fs_selfservice/FS-SelfService/cgi/ach_payment_results.html13
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent.cgi458
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html7
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html17
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_login.html22
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_logout.html5
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_main.html33
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_menu.html15
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html18
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_provision.html23
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html16
-rw-r--r--fs_selfservice/FS-SelfService/cgi/bill.html15
-rw-r--r--fs_selfservice/FS-SelfService/cgi/card.html73
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/change_bill.html23
-rw-r--r--fs_selfservice/FS-SelfService/cgi/change_password.html51
-rw-r--r--fs_selfservice/FS-SelfService/cgi/change_pay.html73
-rw-r--r--fs_selfservice/FS-SelfService/cgi/change_pkg.html37
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/change_ship.html102
-rw-r--r--fs_selfservice/FS-SelfService/cgi/check.html54
-rw-r--r--fs_selfservice/FS-SelfService/cgi/contact.html135
-rw-r--r--fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi19
-rw-r--r--fs_selfservice/FS-SelfService/cgi/customer_change_pkg.html8
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/customer_order_pkg.html8
-rw-r--r--fs_selfservice/FS-SelfService/cgi/cvv2.html25
-rw-r--r--fs_selfservice/FS-SelfService/cgi/cvv2.pngbin0 -> 3854 bytes
-rw-r--r--fs_selfservice/FS-SelfService/cgi/cvv2_amex.pngbin0 -> 4573 bytes
-rw-r--r--fs_selfservice/FS-SelfService/cgi/decline.html5
-rw-r--r--fs_selfservice/FS-SelfService/cgi/delete_svc.html14
-rw-r--r--fs_selfservice/FS-SelfService/cgi/footer.html3
-rw-r--r--fs_selfservice/FS-SelfService/cgi/images/cross.pngbin0 -> 655 bytes
-rw-r--r--fs_selfservice/FS-SelfService/cgi/images/wait-orange.gifbin0 -> 1849 bytes
-rw-r--r--fs_selfservice/FS-SelfService/cgi/list_customers.html36
-rw-r--r--fs_selfservice/FS-SelfService/cgi/login.html85
-rw-r--r--fs_selfservice/FS-SelfService/cgi/logout.html5
-rw-r--r--fs_selfservice/FS-SelfService/cgi/make_ach_payment.html58
-rw-r--r--fs_selfservice/FS-SelfService/cgi/make_payment.html68
-rw-r--r--fs_selfservice/FS-SelfService/cgi/map.gifbin0 -> 8181 bytes
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/misc/areacodes.cgi18
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/misc/exchanges.cgi18
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/misc/phonenums.cgi18
-rw-r--r--fs_selfservice/FS-SelfService/cgi/myaccount.html94
-rw-r--r--fs_selfservice/FS-SelfService/cgi/myaccount_menu.html94
-rw-r--r--fs_selfservice/FS-SelfService/cgi/order_pkg.html75
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/passwd.cgi61
-rw-r--r--fs_selfservice/FS-SelfService/cgi/passwd.html28
-rw-r--r--fs_selfservice/FS-SelfService/cgi/payment_results.html13
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_change_bill.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_change_password.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_change_pay.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_change_pkg.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_change_ship.html10
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/process_order_pkg.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_order_recharge.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_svc_acct.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_svc_external.html12
-rw-r--r--fs_selfservice/FS-SelfService/cgi/promocode.html14
-rw-r--r--fs_selfservice/FS-SelfService/cgi/provision.html8
-rw-r--r--fs_selfservice/FS-SelfService/cgi/provision_list.html92
-rw-r--r--fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html8
-rw-r--r--fs_selfservice/FS-SelfService/cgi/recharge_prepay.html33
-rw-r--r--fs_selfservice/FS-SelfService/cgi/recharge_results.html21
-rw-r--r--fs_selfservice/FS-SelfService/cgi/regcode.html14
-rw-r--r--fs_selfservice/FS-SelfService/cgi/selfservice.cgi667
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-agentselect.html195
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-alternate.html218
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-billaddress.html307
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-freeoption.html262
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-snarf.html228
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup.cgi387
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup.html424
-rw-r--r--fs_selfservice/FS-SelfService/cgi/stateselect.html134
-rw-r--r--fs_selfservice/FS-SelfService/cgi/success-delayed.html16
-rw-r--r--fs_selfservice/FS-SelfService/cgi/success.html41
-rw-r--r--fs_selfservice/FS-SelfService/cgi/svc_acct.html58
-rw-r--r--fs_selfservice/FS-SelfService/cgi/view_customer.html24
-rw-r--r--fs_selfservice/FS-SelfService/cgi/view_invoice.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/view_support_details.html78
-rw-r--r--fs_selfservice/FS-SelfService/cgi/view_usage.html58
-rw-r--r--fs_selfservice/FS-SelfService/cgi/view_usage_details.html84
-rw-r--r--fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi18
-rw-r--r--fs_selfservice/FS-SelfService/freeside-selfservice-clientd272
-rw-r--r--fs_selfservice/FS-SelfService/freeside-selfservice-xmlrpc-server59
-rwxr-xr-xfs_selfservice/FS-SelfService/ieak.template40
-rw-r--r--fs_selfservice/FS-SelfService/test.pl17
-rw-r--r--fs_selfservice/fri/CHANGE.log271
-rw-r--r--fs_selfservice/fri/LICENSE.txt340
-rw-r--r--fs_selfservice/fri/README.txt123
-rw-r--r--fs_selfservice/fri/includes/ajax.php132
-rw-r--r--fs_selfservice/fri/includes/asi.php156
-rw-r--r--fs_selfservice/fri/includes/bootstrap.php315
-rw-r--r--fs_selfservice/fri/includes/common.php434
-rw-r--r--fs_selfservice/fri/includes/crypt.php81
-rw-r--r--fs_selfservice/fri/includes/database.php72
-rw-r--r--fs_selfservice/fri/includes/display.php222
-rw-r--r--fs_selfservice/fri/includes/freeside.class.php38
-rw-r--r--fs_selfservice/fri/includes/lang.php112
-rw-r--r--fs_selfservice/fri/includes/login.php515
-rw-r--r--fs_selfservice/fri/includes/main.conf.php331
-rw-r--r--fs_selfservice/fri/index.php20
-rw-r--r--fs_selfservice/fri/locale/ari.po590
-rw-r--r--fs_selfservice/fri/locale/ari.utf-8.po590
-rw-r--r--fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.mobin0 -> 4161 bytes
-rw-r--r--fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.po631
-rw-r--r--fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.mobin0 -> 5158 bytes
-rw-r--r--fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.po648
-rw-r--r--fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.mobin0 -> 9562 bytes
-rw-r--r--fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.po616
-rw-r--r--fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.mobin0 -> 6751 bytes
-rw-r--r--fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.po635
-rw-r--r--fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.mobin0 -> 4430 bytes
-rw-r--r--fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.po646
-rw-r--r--fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.mobin0 -> 4051 bytes
-rw-r--r--fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.po645
-rw-r--r--fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.mobin0 -> 16728 bytes
-rw-r--r--fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.po999
-rw-r--r--fs_selfservice/fri/locale/locale.txt37
-rw-r--r--fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.mobin0 -> 2064 bytes
-rw-r--r--fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.po647
-rw-r--r--fs_selfservice/fri/locale/readme.txt37
-rw-r--r--fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.mobin0 -> 7134 bytes
-rw-r--r--fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.po678
-rw-r--r--fs_selfservice/fri/misc/audio.php61
-rw-r--r--fs_selfservice/fri/misc/popup.css10
-rw-r--r--fs_selfservice/fri/misc/recording_popup.php46
-rw-r--r--fs_selfservice/fri/modules.template/blank.module81
-rw-r--r--fs_selfservice/fri/modules/VmX.module661
-rw-r--r--fs_selfservice/fri/modules/billing.module250
-rw-r--r--fs_selfservice/fri/modules/callmonitor.module675
-rw-r--r--fs_selfservice/fri/modules/dashboard.module166
-rw-r--r--fs_selfservice/fri/modules/featurecodes.module152
-rw-r--r--fs_selfservice/fri/modules/followme.module678
-rw-r--r--fs_selfservice/fri/modules/myaccount.module109
-rw-r--r--fs_selfservice/fri/modules/phonefeatures.module342
-rw-r--r--fs_selfservice/fri/modules/settings.module813
-rw-r--r--fs_selfservice/fri/modules/voicemail.module805
-rw-r--r--fs_selfservice/fri/theme/global.css87
-rw-r--r--fs_selfservice/fri/theme/header.css83
-rw-r--r--fs_selfservice/fri/theme/iefixes.css16
-rw-r--r--fs_selfservice/fri/theme/images/arrow-asc.gifbin0 -> 86 bytes
-rw-r--r--fs_selfservice/fri/theme/images/arrow-desc.gifbin0 -> 85 bytes
-rw-r--r--fs_selfservice/fri/theme/layout.css420
-rw-r--r--fs_selfservice/fri/theme/logo.gifbin0 -> 2819 bytes
-rw-r--r--fs_selfservice/fri/theme/main.css13
-rw-r--r--fs_selfservice/fri/theme/navigation.css166
-rw-r--r--fs_selfservice/fri/theme/page.tpl.php78
-rw-r--r--fs_selfservice/fri/theme/spacer.gifbin0 -> 43 bytes
-rw-r--r--fs_selfservice/fri/theme/text.css10
-rw-r--r--fs_selfservice/fri/version.php10
-rwxr-xr-xfs_selfservice/fs_passwd_test19
-rwxr-xr-xfs_selfservice/java/biz/freeside/SelfService.java52
-rwxr-xr-xfs_selfservice/java/freeside_login_example.java45
-rwxr-xr-xfs_selfservice/java/freeside_signup_example.java69
-rw-r--r--fs_selfservice/php/freeside.class.php34
-rw-r--r--fs_selfservice/php/freeside.login_example.php37
-rw-r--r--fs_selfservice/php/freeside_signup_example.php49
-rw-r--r--fs_selfservice/php/login.php90
-rw-r--r--fs_selfservice/php/main.php39
-rw-r--r--fs_selfservice/php/order_renew.php166
-rw-r--r--fs_selfservice/php/process_login.php38
-rw-r--r--fs_selfservice/php/process_payment_order_renew.php74
-rwxr-xr-xhtdocs/browse/agent.cgi72
-rwxr-xr-xhtdocs/browse/agent_type.cgi81
-rwxr-xr-xhtdocs/browse/cust_main_county.cgi65
-rwxr-xr-xhtdocs/browse/part_pkg.cgi81
-rwxr-xr-xhtdocs/browse/part_referral.cgi57
-rwxr-xr-xhtdocs/browse/part_svc.cgi81
-rwxr-xr-xhtdocs/browse/svc_acct_pop.cgi63
-rwxr-xr-xhtdocs/docs/CGI-modules-2.76-patch.txt23
-rw-r--r--htdocs/docs/admin.html6
-rw-r--r--htdocs/docs/billing.html40
-rw-r--r--htdocs/docs/config.html38
-rw-r--r--htdocs/docs/export.html39
-rw-r--r--htdocs/docs/index.html23
-rw-r--r--htdocs/docs/install.html56
-rw-r--r--htdocs/docs/legacy.html34
-rw-r--r--htdocs/docs/man/Bill.txt29
-rw-r--r--htdocs/docs/man/CGI.txt47
-rw-r--r--htdocs/docs/man/Conf.txt47
-rw-r--r--htdocs/docs/man/Invoice.txt23
-rw-r--r--htdocs/docs/man/Record.txt332
-rw-r--r--htdocs/docs/man/SSH.txt63
-rw-r--r--htdocs/docs/man/UID.txt79
-rw-r--r--htdocs/docs/man/agent.txt65
-rw-r--r--htdocs/docs/man/agent_type.txt72
-rw-r--r--htdocs/docs/man/cust_bill.txt140
-rw-r--r--htdocs/docs/man/cust_bill_pkg.txt72
-rw-r--r--htdocs/docs/man/cust_credit.txt75
-rw-r--r--htdocs/docs/man/cust_main.txt200
-rw-r--r--htdocs/docs/man/cust_main_county.txt67
-rw-r--r--htdocs/docs/man/cust_pay.txt66
-rw-r--r--htdocs/docs/man/cust_pkg.txt150
-rw-r--r--htdocs/docs/man/cust_refund.txt66
-rw-r--r--htdocs/docs/man/cust_svc.txt72
-rw-r--r--htdocs/docs/man/dbdef.txt80
-rw-r--r--htdocs/docs/man/dbdef_colgroup.txt51
-rw-r--r--htdocs/docs/man/dbdef_column.txt69
-rw-r--r--htdocs/docs/man/dbdef_index.txt27
-rw-r--r--htdocs/docs/man/dbdef_table.txt94
-rw-r--r--htdocs/docs/man/dbdef_unique.txt27
-rw-r--r--htdocs/docs/man/index.html48
-rw-r--r--htdocs/docs/man/part_pkg.txt73
-rw-r--r--htdocs/docs/man/part_referral.txt63
-rw-r--r--htdocs/docs/man/part_svc.txt69
-rw-r--r--htdocs/docs/man/pkg_svc.txt61
-rw-r--r--htdocs/docs/man/svc_acct.txt168
-rw-r--r--htdocs/docs/man/svc_acct_pop.txt65
-rw-r--r--htdocs/docs/man/svc_acct_sm.txt121
-rw-r--r--htdocs/docs/man/svc_domain.txt131
-rw-r--r--htdocs/docs/man/type_pkgs.txt55
-rw-r--r--htdocs/docs/passwd.html16
-rw-r--r--htdocs/docs/schema.html205
-rw-r--r--htdocs/docs/trouble.html41
-rw-r--r--htdocs/docs/upgrade.html24
-rw-r--r--htdocs/docs/upgrade2.html11
-rwxr-xr-xhtdocs/edit/agent.cgi77
-rwxr-xr-xhtdocs/edit/agent_type.cgi75
-rwxr-xr-xhtdocs/edit/cust_credit.cgi97
-rwxr-xr-xhtdocs/edit/cust_main.cgi214
-rwxr-xr-xhtdocs/edit/cust_main_county-expand.cgi49
-rwxr-xr-xhtdocs/edit/cust_main_county.cgi66
-rwxr-xr-xhtdocs/edit/cust_pay.cgi76
-rwxr-xr-xhtdocs/edit/cust_pkg.cgi137
-rwxr-xr-xhtdocs/edit/part_pkg.cgi102
-rwxr-xr-xhtdocs/edit/part_referral.cgi66
-rwxr-xr-xhtdocs/edit/part_svc.cgi148
-rwxr-xr-xhtdocs/edit/process/agent.cgi53
-rwxr-xr-xhtdocs/edit/process/agent_type.cgi83
-rwxr-xr-xhtdocs/edit/process/cust_credit.cgi70
-rwxr-xr-xhtdocs/edit/process/cust_main.cgi102
-rwxr-xr-xhtdocs/edit/process/cust_main_county-expand.cgi71
-rwxr-xr-xhtdocs/edit/process/cust_main_county.cgi38
-rwxr-xr-xhtdocs/edit/process/cust_pay.cgi57
-rwxr-xr-xhtdocs/edit/process/cust_pkg.cgi73
-rwxr-xr-xhtdocs/edit/process/part_pkg.cgi79
-rwxr-xr-xhtdocs/edit/process/part_referral.cgi45
-rwxr-xr-xhtdocs/edit/process/part_svc.cgi47
-rwxr-xr-xhtdocs/edit/process/svc_acct.cgi87
-rwxr-xr-xhtdocs/edit/process/svc_acct_pop.cgi43
-rwxr-xr-xhtdocs/edit/process/svc_acct_sm.cgi80
-rwxr-xr-xhtdocs/edit/process/svc_domain.cgi78
-rwxr-xr-xhtdocs/edit/svc_acct.cgi191
-rwxr-xr-xhtdocs/edit/svc_acct_pop.cgi67
-rwxr-xr-xhtdocs/edit/svc_acct_sm.cgi219
-rwxr-xr-xhtdocs/edit/svc_domain.cgi120
-rw-r--r--htdocs/images/mid-logo.gifbin10606 -> 0 bytes
-rwxr-xr-xhtdocs/images/sisd.jpgbin22122 -> 0 bytes
-rw-r--r--htdocs/images/small-logo.gifbin5426 -> 0 bytes
-rwxr-xr-xhtdocs/index.html96
-rwxr-xr-xhtdocs/misc/bill.cgi66
-rwxr-xr-xhtdocs/misc/cancel-unaudited.cgi85
-rwxr-xr-xhtdocs/misc/cancel_pkg.cgi54
-rwxr-xr-xhtdocs/misc/expire_pkg.cgi71
-rwxr-xr-xhtdocs/misc/link.cgi72
-rwxr-xr-xhtdocs/misc/print-invoice.cgi57
-rwxr-xr-xhtdocs/misc/process/link.cgi73
-rwxr-xr-xhtdocs/misc/susp_pkg.cgi68
-rwxr-xr-xhtdocs/misc/unsusp_pkg.cgi68
-rwxr-xr-xhtdocs/search/cust_bill.cgi46
-rwxr-xr-xhtdocs/search/cust_bill.html21
-rwxr-xr-xhtdocs/search/cust_main-payinfo.html21
-rwxr-xr-xhtdocs/search/cust_main.cgi235
-rwxr-xr-xhtdocs/search/cust_main.html36
-rwxr-xr-xhtdocs/search/cust_pkg.cgi122
-rwxr-xr-xhtdocs/search/svc_acct.cgi186
-rwxr-xr-xhtdocs/search/svc_acct.html21
-rwxr-xr-xhtdocs/search/svc_acct_sm.cgi128
-rwxr-xr-xhtdocs/search/svc_acct_sm.html23
-rwxr-xr-xhtdocs/search/svc_domain.cgi139
-rwxr-xr-xhtdocs/search/svc_domain.html22
-rwxr-xr-xhtdocs/view/cust_bill.cgi79
-rwxr-xr-xhtdocs/view/cust_main.cgi336
-rwxr-xr-xhtdocs/view/cust_pkg.cgi181
-rwxr-xr-xhtdocs/view/svc_acct.cgi172
-rwxr-xr-xhtdocs/view/svc_acct_sm.cgi114
-rwxr-xr-xhtdocs/view/svc_domain.cgi76
-rw-r--r--htetc/freeside-base1.99.conf21
-rw-r--r--htetc/freeside-base1.conf18
-rw-r--r--htetc/freeside-base2.conf21
-rw-r--r--htetc/freeside-rt.conf36
-rw-r--r--htetc/handler.pl98
-rwxr-xr-xhttemplate/.htaccess3
-rw-r--r--httemplate/autohandler44
-rw-r--r--httemplate/browse/access_group.html106
-rw-r--r--httemplate/browse/access_user.html61
-rw-r--r--httemplate/browse/addr_block.cgi145
-rwxr-xr-xhttemplate/browse/agent.cgi422
-rwxr-xr-xhttemplate/browse/agent_type.cgi61
-rwxr-xr-xhttemplate/browse/cust_main_county.cgi454
-rw-r--r--httemplate/browse/elements/browse.html6
-rw-r--r--httemplate/browse/inventory_class.html93
-rw-r--r--httemplate/browse/invoice_template.html124
-rwxr-xr-xhttemplate/browse/msgcat.cgi44
-rwxr-xr-xhttemplate/browse/nas.cgi82
-rwxr-xr-xhttemplate/browse/part_bill_event.cgi122
-rw-r--r--httemplate/browse/part_event.html167
-rwxr-xr-xhttemplate/browse/part_export.cgi65
-rwxr-xr-xhttemplate/browse/part_pkg.cgi367
-rwxr-xr-xhttemplate/browse/part_pkg_taxproduct.cgi263
-rwxr-xr-xhttemplate/browse/part_referral.html181
-rwxr-xr-xhttemplate/browse/part_svc.cgi215
-rw-r--r--httemplate/browse/part_virtual_field.cgi42
-rw-r--r--httemplate/browse/payment_gateway.html94
-rw-r--r--httemplate/browse/pkg_category.html33
-rw-r--r--httemplate/browse/pkg_class.html46
-rw-r--r--httemplate/browse/rate.cgi64
-rw-r--r--httemplate/browse/rate_detail.html92
-rw-r--r--httemplate/browse/rate_region.html91
-rw-r--r--httemplate/browse/reason.html53
-rw-r--r--httemplate/browse/reason_type.html68
-rw-r--r--httemplate/browse/router.cgi52
-rwxr-xr-xhttemplate/browse/svc_acct_pop.cgi77
-rwxr-xr-xhttemplate/browse/tax_class.html92
-rwxr-xr-xhttemplate/browse/tax_rate.cgi348
-rw-r--r--httemplate/browse/usage_class.html28
-rw-r--r--httemplate/config/config-delete.cgi15
-rw-r--r--httemplate/config/config-download.cgi28
-rw-r--r--httemplate/config/config-image.cgi19
-rw-r--r--httemplate/config/config-process.cgi105
-rw-r--r--httemplate/config/config-view.cgi177
-rw-r--r--httemplate/config/config.cgi331
-rw-r--r--httemplate/docs/AGPL.html672
-rw-r--r--httemplate/docs/about.html53
-rw-r--r--httemplate/docs/ach.html10
-rwxr-xr-xhttemplate/docs/admin.html41
-rw-r--r--httemplate/docs/credits.html175
-rw-r--r--httemplate/docs/cvv2.html24
-rw-r--r--httemplate/docs/ieak.html75
-rw-r--r--httemplate/docs/index.html32
-rwxr-xr-xhttemplate/docs/legacy.html39
-rw-r--r--httemplate/docs/license.html116
-rw-r--r--httemplate/docs/man/FS/part_export/.cvs_is_on_crack0
-rw-r--r--httemplate/docs/overview-new.diabin0 -> 2422 bytes
-rw-r--r--httemplate/docs/overview-new.pngbin0 -> 29062 bytes
-rw-r--r--httemplate/docs/overview.diabin0 -> 2800 bytes
-rw-r--r--httemplate/docs/overview.pngbin0 -> 13064 bytes
-rwxr-xr-xhttemplate/docs/passwd.html23
-rw-r--r--httemplate/docs/schema.diabin0 -> 16364 bytes
-rw-r--r--httemplate/docs/schema.html533
-rw-r--r--httemplate/docs/schema.pngbin0 -> 681043 bytes
-rw-r--r--httemplate/docs/session.html59
-rw-r--r--httemplate/docs/signup.html54
-rwxr-xr-xhttemplate/docs/ssh.html16
-rwxr-xr-xhttemplate/edit/REAL_cust_pkg.cgi186
-rw-r--r--httemplate/edit/access_group.html80
-rw-r--r--httemplate/edit/access_user.html50
-rwxr-xr-xhttemplate/edit/agent.cgi123
-rw-r--r--httemplate/edit/agent_payment_gateway.html68
-rwxr-xr-xhttemplate/edit/agent_type.cgi57
-rw-r--r--httemplate/edit/allocate.html33
-rw-r--r--httemplate/edit/bulk-cust_main_county.html130
-rw-r--r--httemplate/edit/bulk-cust_svc.html95
-rwxr-xr-xhttemplate/edit/cust_bill_pay.cgi14
-rwxr-xr-xhttemplate/edit/cust_credit.cgi68
-rwxr-xr-xhttemplate/edit/cust_credit_bill.cgi14
-rwxr-xr-xhttemplate/edit/cust_credit_refund.cgi14
-rwxr-xr-xhttemplate/edit/cust_main.cgi780
-rw-r--r--httemplate/edit/cust_main/billing.html484
-rw-r--r--httemplate/edit/cust_main/choose_tax_location.html85
-rw-r--r--httemplate/edit/cust_main/contact.html137
-rw-r--r--httemplate/edit/cust_main/select-domain.html67
-rwxr-xr-xhttemplate/edit/cust_main_county-expand.cgi50
-rw-r--r--httemplate/edit/cust_main_county.html62
-rwxr-xr-xhttemplate/edit/cust_main_note.cgi45
-rwxr-xr-xhttemplate/edit/cust_pay.cgi138
-rw-r--r--httemplate/edit/cust_pay_pending.html154
-rwxr-xr-xhttemplate/edit/cust_pay_refund.cgi14
-rwxr-xr-xhttemplate/edit/cust_pkg.cgi150
-rw-r--r--httemplate/edit/cust_pkg_detail.html142
-rwxr-xr-xhttemplate/edit/cust_refund.cgi165
-rw-r--r--httemplate/edit/elements/ApplicationCommon.html177
-rw-r--r--httemplate/edit/elements/edit.html700
-rw-r--r--httemplate/edit/elements/svc_Common.html122
-rw-r--r--httemplate/edit/inventory_class.html16
-rw-r--r--httemplate/edit/invoice_logo.html136
-rw-r--r--httemplate/edit/invoice_template.html69
-rwxr-xr-xhttemplate/edit/msgcat.cgi54
-rwxr-xr-xhttemplate/edit/part_bill_event.cgi570
-rw-r--r--httemplate/edit/part_event.html679
-rw-r--r--httemplate/edit/part_export.cgi123
-rwxr-xr-xhttemplate/edit/part_pkg.cgi637
-rw-r--r--httemplate/edit/part_pkg_taxclass.html32
-rw-r--r--httemplate/edit/part_pkg_taxoverride.html132
-rwxr-xr-xhttemplate/edit/part_referral.html19
-rwxr-xr-xhttemplate/edit/part_svc.cgi361
-rw-r--r--httemplate/edit/part_virtual_field.cgi104
-rw-r--r--httemplate/edit/payment_gateway.html132
-rw-r--r--httemplate/edit/pkg_category.html22
-rw-r--r--httemplate/edit/pkg_class.html28
-rw-r--r--httemplate/edit/prepay_credit.cgi110
-rwxr-xr-xhttemplate/edit/process/REAL_cust_pkg.cgi36
-rw-r--r--httemplate/edit/process/access_group.html28
-rw-r--r--httemplate/edit/process/access_user.html21
-rwxr-xr-xhttemplate/edit/process/addr_block/add.cgi20
-rwxr-xr-xhttemplate/edit/process/addr_block/allocate.cgi16
-rwxr-xr-xhttemplate/edit/process/addr_block/deallocate.cgi20
-rwxr-xr-xhttemplate/edit/process/addr_block/manual_flag.cgi30
-rwxr-xr-xhttemplate/edit/process/addr_block/split.cgi27
-rwxr-xr-xhttemplate/edit/process/agent.cgi16
-rw-r--r--httemplate/edit/process/agent_payment_gateway.html29
-rwxr-xr-xhttemplate/edit/process/agent_type.cgi35
-rw-r--r--httemplate/edit/process/bulk-cust_main_county.html63
-rw-r--r--httemplate/edit/process/bulk-cust_svc.cgi9
-rw-r--r--httemplate/edit/process/change-cust_pkg.html46
-rwxr-xr-xhttemplate/edit/process/cust_bill_pay.cgi13
-rwxr-xr-xhttemplate/edit/process/cust_credit.cgi63
-rwxr-xr-xhttemplate/edit/process/cust_credit_bill.cgi13
-rwxr-xr-xhttemplate/edit/process/cust_credit_refund.cgi13
-rwxr-xr-xhttemplate/edit/process/cust_main.cgi210
-rwxr-xr-xhttemplate/edit/process/cust_main_county-collapse.cgi44
-rwxr-xr-xhttemplate/edit/process/cust_main_county-expand.cgi78
-rw-r--r--httemplate/edit/process/cust_main_county.html13
-rwxr-xr-xhttemplate/edit/process/cust_main_note.cgi54
-rwxr-xr-xhttemplate/edit/process/cust_pay.cgi55
-rw-r--r--httemplate/edit/process/cust_pay_pending.html68
-rwxr-xr-xhttemplate/edit/process/cust_pay_refund.cgi13
-rwxr-xr-xhttemplate/edit/process/cust_pkg.cgi42
-rw-r--r--httemplate/edit/process/cust_pkg_detail.html59
-rwxr-xr-xhttemplate/edit/process/cust_refund.cgi56
-rw-r--r--httemplate/edit/process/cust_svc.cgi30
-rwxr-xr-xhttemplate/edit/process/domain_record.cgi30
-rw-r--r--httemplate/edit/process/elements/ApplicationCommon.html77
-rw-r--r--httemplate/edit/process/elements/process.html268
-rw-r--r--httemplate/edit/process/elements/svc_Common.html15
-rw-r--r--httemplate/edit/process/generic.cgi77
-rw-r--r--httemplate/edit/process/inventory_class.html11
-rw-r--r--httemplate/edit/process/invoice_logo.html25
-rw-r--r--httemplate/edit/process/invoice_template.html15
-rw-r--r--httemplate/edit/process/msgcat.cgi22
-rwxr-xr-xhttemplate/edit/process/part_bill_event.cgi106
-rw-r--r--httemplate/edit/process/part_event.html86
-rw-r--r--httemplate/edit/process/part_export.cgi41
-rwxr-xr-xhttemplate/edit/process/part_pkg.cgi198
-rw-r--r--httemplate/edit/process/part_pkg_taxclass.html53
-rwxr-xr-xhttemplate/edit/process/part_referral.html12
-rwxr-xr-xhttemplate/edit/process/part_svc.cgi9
-rw-r--r--httemplate/edit/process/payment_gateway.html35
-rw-r--r--httemplate/edit/process/pkg_category.html11
-rw-r--r--httemplate/edit/process/pkg_class.html11
-rw-r--r--httemplate/edit/process/prepay_credit.cgi62
-rw-r--r--httemplate/edit/process/quick-charge.cgi68
-rw-r--r--httemplate/edit/process/quick-cust_pkg.cgi63
-rwxr-xr-xhttemplate/edit/process/rate.cgi9
-rw-r--r--httemplate/edit/process/rate_detail.html13
-rwxr-xr-xhttemplate/edit/process/rate_region.cgi57
-rw-r--r--httemplate/edit/process/reason.html12
-rw-r--r--httemplate/edit/process/reason_type.html12
-rw-r--r--httemplate/edit/process/reg_code.cgi45
-rw-r--r--httemplate/edit/process/router.cgi20
-rw-r--r--httemplate/edit/process/svc_Common.html16
-rwxr-xr-xhttemplate/edit/process/svc_acct.cgi64
-rwxr-xr-xhttemplate/edit/process/svc_acct_pop.cgi33
-rw-r--r--httemplate/edit/process/svc_broadband.cgi8
-rwxr-xr-xhttemplate/edit/process/svc_domain.cgi33
-rwxr-xr-xhttemplate/edit/process/svc_external.cgi31
-rwxr-xr-xhttemplate/edit/process/svc_forward.cgi31
-rw-r--r--httemplate/edit/process/svc_phone.html10
-rw-r--r--httemplate/edit/process/svc_www.cgi38
-rw-r--r--httemplate/edit/process/tax_class.html49
-rw-r--r--httemplate/edit/process/tax_rate.html22
-rw-r--r--httemplate/edit/process/usage_class.html11
-rw-r--r--httemplate/edit/quick-charge.html197
-rw-r--r--httemplate/edit/rate.cgi43
-rw-r--r--httemplate/edit/rate_detail.html63
-rw-r--r--httemplate/edit/rate_region.cgi163
-rw-r--r--httemplate/edit/reason.html50
-rw-r--r--httemplate/edit/reason_type.html29
-rw-r--r--httemplate/edit/reg_code.cgi44
-rwxr-xr-xhttemplate/edit/router.cgi44
-rw-r--r--httemplate/edit/svc_Common.html33
-rwxr-xr-xhttemplate/edit/svc_acct.cgi452
-rwxr-xr-xhttemplate/edit/svc_acct_pop.cgi53
-rw-r--r--httemplate/edit/svc_broadband.cgi105
-rwxr-xr-xhttemplate/edit/svc_domain.cgi91
-rw-r--r--httemplate/edit/svc_external.cgi102
-rwxr-xr-xhttemplate/edit/svc_forward.cgi175
-rw-r--r--httemplate/edit/svc_phone.cgi27
-rw-r--r--httemplate/edit/svc_www.cgi240
-rw-r--r--httemplate/edit/tax_class.html36
-rw-r--r--httemplate/edit/tax_rate.html106
-rw-r--r--httemplate/edit/usage_class.html25
-rw-r--r--httemplate/elements/ajaxcontentmws.js185
-rw-r--r--httemplate/elements/calendar-en.js127
-rw-r--r--httemplate/elements/calendar-setup.js200
-rw-r--r--httemplate/elements/calendar-win2k-2.css271
-rw-r--r--httemplate/elements/calendar.js1806
-rw-r--r--httemplate/elements/calendar_stripped.js14
-rw-r--r--httemplate/elements/checkboxes-table-name.html90
-rw-r--r--httemplate/elements/checkboxes-table.html129
-rw-r--r--httemplate/elements/checkboxes.html103
-rw-r--r--httemplate/elements/columnend.html6
-rw-r--r--httemplate/elements/columnnext.html4
-rw-r--r--httemplate/elements/columnstart.html6
-rw-r--r--httemplate/elements/cssexpr.js66
-rw-r--r--httemplate/elements/customer-table.html524
-rw-r--r--httemplate/elements/dashboard-toplist.html113
-rw-r--r--httemplate/elements/error.html4
-rw-r--r--httemplate/elements/errorpage.html11
-rw-r--r--httemplate/elements/fckeditor/editor/css/behaviors/disablehandles.htc15
-rw-r--r--httemplate/elements/fckeditor/editor/css/behaviors/showtableborders.htc36
-rw-r--r--httemplate/elements/fckeditor/editor/css/fck_editorarea.css91
-rw-r--r--httemplate/elements/fckeditor/editor/css/fck_internal.css111
-rw-r--r--httemplate/elements/fckeditor/editor/css/fck_showtableborders_gecko.css42
-rw-r--r--httemplate/elements/fckeditor/editor/css/images/fck_anchor.gifbin0 -> 184 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/css/images/fck_flashlogo.gifbin0 -> 599 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/css/images/fck_hiddenfield.gifbin0 -> 105 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/css/images/fck_pagebreak.gifbin0 -> 54 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.css83
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.js154
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/common/fcknumericfield.htc24
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/common/images/locked.gifbin0 -> 74 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/common/images/reset.gifbin0 -> 104 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/common/images/unlocked.gifbin0 -> 75 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/common/moz-bindings.xml30
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_about.html155
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fckeditor.gifbin0 -> 2044 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fredck.gifbin0 -> 920 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_anchor.html236
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_button.html107
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_checkbox.html107
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_colorselector.html171
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_docprops.html600
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_docprops/fck_document_preview.html113
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_find.html173
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_flash.html146
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash.js286
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash_preview.html46
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_form.html105
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_hiddenfield.html116
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_image.html252
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image.js493
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image_preview.html66
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_link.html293
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_link/fck_link.js698
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_listprop.html116
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_paste.html285
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_radiobutton.html107
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_replace.html156
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_select.html176
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_select/fck_select.js194
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_smiley.html105
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_source.html65
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_specialchar.html113
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages.html64
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/blank.html0
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controlWindow.js87
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controls.html153
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.pl180
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellChecker.js462
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellchecker.html71
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellerStyle.css49
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/wordWindow.js272
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_table.html291
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_tablecell.html255
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_template.html242
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_template/images/template1.gifbin0 -> 375 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_template/images/template2.gifbin0 -> 333 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_template/images/template3.gifbin0 -> 422 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_textarea.html94
-rw-r--r--httemplate/elements/fckeditor/editor/dialog/fck_textfield.html139
-rw-r--r--httemplate/elements/fckeditor/editor/fckdebug.html153
-rw-r--r--httemplate/elements/fckeditor/editor/fckdialog.html324
-rw-r--r--httemplate/elements/fckeditor/editor/fckeditor.html227
-rw-r--r--httemplate/elements/fckeditor/editor/fckeditor.original.html319
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.css88
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.html154
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/basexml.pl63
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/commands.pl158
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/connector.cgi137
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/io.pl131
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/upload_fck.pl667
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/util.pl60
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/frmactualfolder.html67
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/frmcreatefolder.html113
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/frmfolders.html196
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourceslist.html160
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourcetype.html65
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/frmupload.html113
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/ButtonArrow.gifbin0 -> 138 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder.gifbin0 -> 128 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder32.gifbin0 -> 281 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened.gifbin0 -> 132 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened32.gifbin0 -> 264 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderUp.gifbin0 -> 132 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ai.gifbin0 -> 1140 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/avi.gifbin0 -> 454 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/bmp.gifbin0 -> 709 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/cs.gifbin0 -> 224 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/default.icon.gifbin0 -> 177 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/dll.gifbin0 -> 258 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/doc.gifbin0 -> 260 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/exe.gifbin0 -> 170 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/fla.gifbin0 -> 946 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/gif.gifbin0 -> 704 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/htm.gifbin0 -> 1527 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/html.gifbin0 -> 1527 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/jpg.gifbin0 -> 463 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/js.gifbin0 -> 274 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mdb.gifbin0 -> 274 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mp3.gifbin0 -> 454 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/pdf.gifbin0 -> 567 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/png.gifbin0 -> 464 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ppt.gifbin0 -> 254 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/rdp.gifbin0 -> 1493 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swf.gifbin0 -> 725 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swt.gifbin0 -> 724 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/txt.gifbin0 -> 213 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/vsd.gifbin0 -> 277 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xls.gifbin0 -> 271 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xml.gifbin0 -> 408 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/zip.gifbin0 -> 368 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ai.gifbin0 -> 403 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/avi.gifbin0 -> 249 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/bmp.gifbin0 -> 126 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/cs.gifbin0 -> 128 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/default.icon.gifbin0 -> 113 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/dll.gifbin0 -> 132 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/doc.gifbin0 -> 140 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/exe.gifbin0 -> 109 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/fla.gifbin0 -> 382 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/gif.gifbin0 -> 125 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/htm.gifbin0 -> 621 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/html.gifbin0 -> 621 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/jpg.gifbin0 -> 125 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/js.gifbin0 -> 139 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mdb.gifbin0 -> 146 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mp3.gifbin0 -> 249 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/pdf.gifbin0 -> 230 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/png.gifbin0 -> 125 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ppt.gifbin0 -> 139 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/rdp.gifbin0 -> 606 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swf.gifbin0 -> 388 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swt.gifbin0 -> 388 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/txt.gifbin0 -> 122 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/vsd.gifbin0 -> 136 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xls.gifbin0 -> 138 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xml.gifbin0 -> 231 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/zip.gifbin0 -> 235 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/js/common.js55
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/browser/default/js/fckxml.js129
-rw-r--r--httemplate/elements/fckeditor/editor/filemanager/upload/test.html133
-rw-r--r--httemplate/elements/fckeditor/editor/images/anchor.gifbin0 -> 184 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/arrow_ltr.gifbin0 -> 49 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/arrow_rtl.gifbin0 -> 49 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/angel_smile.gifbin0 -> 445 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/angry_smile.gifbin0 -> 453 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/broken_heart.gifbin0 -> 423 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/cake.gifbin0 -> 453 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/confused_smile.gifbin0 -> 322 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/cry_smile.gifbin0 -> 473 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/devil_smile.gifbin0 -> 444 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/embaressed_smile.gifbin0 -> 1077 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/envelope.gifbin0 -> 1030 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/heart.gifbin0 -> 1012 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/kiss.gifbin0 -> 978 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/lightbulb.gifbin0 -> 303 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/omg_smile.gifbin0 -> 342 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/regular_smile.gifbin0 -> 1036 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/sad_smile.gifbin0 -> 1039 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/shades_smile.gifbin0 -> 1059 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/teeth_smile.gifbin0 -> 1064 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_down.gifbin0 -> 992 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_up.gifbin0 -> 989 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/tounge_smile.gifbin0 -> 1055 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/whatchutalkingabout_smile.gifbin0 -> 1034 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/smiley/msn/wink_smile.gifbin0 -> 1041 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/js/fckeditorcode_gecko.js98
-rw-r--r--httemplate/elements/fckeditor/editor/js/fckeditorcode_ie.js99
-rw-r--r--httemplate/elements/fckeditor/editor/lang/_getfontformat.html85
-rw-r--r--httemplate/elements/fckeditor/editor/lang/_translationstatus.txt76
-rw-r--r--httemplate/elements/fckeditor/editor/lang/af.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/ar.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/bg.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/bn.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/bs.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/ca.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/cs.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/da.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/de.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/el.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/en-au.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/en-ca.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/en-uk.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/en.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/eo.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/es.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/et.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/eu.js505
-rw-r--r--httemplate/elements/fckeditor/editor/lang/fa.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/fi.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/fo.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/fr.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/gl.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/he.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/hi.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/hr.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/hu.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/it.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/ja.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/km.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/ko.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/lt.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/lv.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/mn.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/ms.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/nb.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/nl.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/no.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/pl.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/pt-br.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/pt.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/ro.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/ru.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/sk.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/sl.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/sr-latn.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/sr.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/sv.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/th.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/tr.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/uk.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/vi.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/zh-cn.js504
-rw-r--r--httemplate/elements/fckeditor/editor/lang/zh.js504
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/autogrow/fckplugin.js92
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/fck_placeholder.html100
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/fckplugin.js187
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/lang/de.js27
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/lang/en.js27
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/lang/fr.js27
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/lang/it.js27
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/lang/pl.js27
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/placeholder/placeholder.gifbin0 -> 96 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/simplecommands/fckplugin.js29
-rw-r--r--httemplate/elements/fckeditor/editor/plugins/tablecommands/fckplugin.js32
-rw-r--r--httemplate/elements/fckeditor/editor/skins/_fckviewstrips.html121
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/fck_dialog.css137
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/fck_editor.css464
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/fck_strip.gifbin0 -> 4578 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/images/toolbar.arrowright.gifbin0 -> 53 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/images/toolbar.buttonarrow.gifbin0 -> 46 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/images/toolbar.collapse.gifbin0 -> 152 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/images/toolbar.end.gifbin0 -> 43 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/images/toolbar.expand.gifbin0 -> 152 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/images/toolbar.separator.gifbin0 -> 58 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/default/images/toolbar.start.gifbin0 -> 105 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/fck_dialog.css138
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/fck_editor.css476
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/fck_strip.gifbin0 -> 9030 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.arrowright.gifbin0 -> 53 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.bg.gifbin0 -> 73 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.buttonarrow.gifbin0 -> 46 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.collapse.gifbin0 -> 152 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.end.gifbin0 -> 124 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.expand.gifbin0 -> 152 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.separator.gifbin0 -> 67 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.start.gifbin0 -> 99 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/fck_dialog.css141
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/fck_editor.css473
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/fck_strip.gifbin0 -> 4578 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.arrowright.gifbin0 -> 53 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonarrow.gifbin0 -> 46 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonbg.gifbin0 -> 829 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.collapse.gifbin0 -> 152 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.end.gifbin0 -> 43 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.expand.gifbin0 -> 152 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.separator.gifbin0 -> 58 bytes
-rw-r--r--httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.start.gifbin0 -> 105 bytes
-rw-r--r--httemplate/elements/fckeditor/fckconfig.js245
-rw-r--r--httemplate/elements/fckeditor/fckeditor.js214
-rw-r--r--httemplate/elements/fckeditor/fckpackager.xml237
-rw-r--r--httemplate/elements/fckeditor/fckstyles.xml53
-rw-r--r--httemplate/elements/fckeditor/fcktemplates.xml103
-rw-r--r--httemplate/elements/file-upload.html74
-rw-r--r--httemplate/elements/footer.html5
-rw-r--r--httemplate/elements/form-file_upload.html93
-rw-r--r--httemplate/elements/freeside.css16
-rw-r--r--httemplate/elements/header-minimal.html19
-rw-r--r--httemplate/elements/header-popup.html24
-rw-r--r--httemplate/elements/header.html299
-rw-r--r--httemplate/elements/hidden.html11
-rw-r--r--httemplate/elements/htmlarea.html36
-rw-r--r--httemplate/elements/iframecontentmws.js59
-rw-r--r--httemplate/elements/init_calendar.html5
-rw-r--r--httemplate/elements/init_overlib.html9
-rw-r--r--httemplate/elements/input-text.html44
-rw-r--r--httemplate/elements/jsrsClient.js356
-rw-r--r--httemplate/elements/jsrsServer.html4
-rw-r--r--httemplate/elements/location.html154
-rw-r--r--httemplate/elements/mcp_lint.html40
-rw-r--r--httemplate/elements/menu.html427
-rw-r--r--httemplate/elements/menubar.html10
-rw-r--r--httemplate/elements/overlibmws.js620
-rw-r--r--httemplate/elements/overlibmws_crossframe.js53
-rw-r--r--httemplate/elements/overlibmws_draggable.js85
-rw-r--r--httemplate/elements/overlibmws_iframe.js93
-rw-r--r--httemplate/elements/pager.html55
-rw-r--r--httemplate/elements/phonenumber.html40
-rw-r--r--httemplate/elements/popup_link-cust_main.html42
-rw-r--r--httemplate/elements/popup_link-cust_pkg.html47
-rw-r--r--httemplate/elements/popup_link-cust_svc.html47
-rw-r--r--httemplate/elements/popup_link.html49
-rw-r--r--httemplate/elements/popup_link_onclick.html74
-rw-r--r--httemplate/elements/progress-init.html87
-rw-r--r--httemplate/elements/progress-popup.html114
-rw-r--r--httemplate/elements/qlib/box.js29
-rw-r--r--httemplate/elements/qlib/boxctrl.js48
-rw-r--r--httemplate/elements/qlib/boxres.js42
-rw-r--r--httemplate/elements/qlib/button.js74
-rw-r--r--httemplate/elements/qlib/buttonres.js23
-rw-r--r--httemplate/elements/qlib/control.js51
-rw-r--r--httemplate/elements/qlib/counter.js81
-rw-r--r--httemplate/elements/qlib/imagelist.js25
-rw-r--r--httemplate/elements/qlib/label.js72
-rw-r--r--httemplate/elements/qlib/messagebox.js57
-rw-r--r--httemplate/elements/qlib/progress.js73
-rw-r--r--httemplate/elements/qlib/sound.js47
-rw-r--r--httemplate/elements/qlib/sprite.js125
-rw-r--r--httemplate/elements/qlib/window.js25
-rw-r--r--httemplate/elements/qlib/wndctrl.js322
-rw-r--r--httemplate/elements/search-cust_main.html181
-rw-r--r--httemplate/elements/select-access_group.html16
-rw-r--r--httemplate/elements/select-agent.html31
-rw-r--r--httemplate/elements/select-agent_type.html21
-rw-r--r--httemplate/elements/select-agent_types.html30
-rw-r--r--httemplate/elements/select-areacode.html91
-rw-r--r--httemplate/elements/select-cdrbatch.html38
-rw-r--r--httemplate/elements/select-country.html127
-rw-r--r--httemplate/elements/select-county.html160
-rw-r--r--httemplate/elements/select-cust-fields.html24
-rw-r--r--httemplate/elements/select-cust-part_pkg.html41
-rw-r--r--httemplate/elements/select-cust_main-status.html30
-rw-r--r--httemplate/elements/select-cust_pkg-status.html30
-rw-r--r--httemplate/elements/select-did.html84
-rw-r--r--httemplate/elements/select-domain.html13
-rw-r--r--httemplate/elements/select-exchange.html86
-rw-r--r--httemplate/elements/select-month_year.html62
-rw-r--r--httemplate/elements/select-otaker.html27
-rw-r--r--httemplate/elements/select-part_pkg.html38
-rw-r--r--httemplate/elements/select-part_referral.html20
-rw-r--r--httemplate/elements/select-payby.html40
-rw-r--r--httemplate/elements/select-phonenum.html84
-rw-r--r--httemplate/elements/select-pkg_class.html18
-rw-r--r--httemplate/elements/select-rate.html9
-rw-r--r--httemplate/elements/select-state.html66
-rw-r--r--httemplate/elements/select-table.html164
-rw-r--r--httemplate/elements/select-taxclass.html41
-rw-r--r--httemplate/elements/select-taxoverride.html28
-rw-r--r--httemplate/elements/select-taxproduct.html28
-rw-r--r--httemplate/elements/selectlayers.html216
-rw-r--r--httemplate/elements/small_custview.html3
-rw-r--r--httemplate/elements/table-grid.html25
-rw-r--r--httemplate/elements/table.html11
-rw-r--r--httemplate/elements/tablebreak-tr-title.html14
-rw-r--r--httemplate/elements/tr-checkbox-multiple.html40
-rw-r--r--httemplate/elements/tr-checkbox.html25
-rw-r--r--httemplate/elements/tr-checkboxes-table.html20
-rw-r--r--httemplate/elements/tr-fixed-country.html10
-rw-r--r--httemplate/elements/tr-fixed-state.html10
-rw-r--r--httemplate/elements/tr-fixed.html15
-rw-r--r--httemplate/elements/tr-freq.html54
-rw-r--r--httemplate/elements/tr-input-beginning_ending.html75
-rw-r--r--httemplate/elements/tr-input-date-field.html40
-rw-r--r--httemplate/elements/tr-input-lessthan_greaterthan.html13
-rw-r--r--httemplate/elements/tr-input-money.html13
-rw-r--r--httemplate/elements/tr-input-percentage.html8
-rw-r--r--httemplate/elements/tr-input-text.html13
-rw-r--r--httemplate/elements/tr-justtitle.html11
-rw-r--r--httemplate/elements/tr-part_pkg_freq.html24
-rw-r--r--httemplate/elements/tr-password.html4
-rw-r--r--httemplate/elements/tr-pkg_svc.html93
-rw-r--r--httemplate/elements/tr-select-access_group.html22
-rw-r--r--httemplate/elements/tr-select-agent.html33
-rw-r--r--httemplate/elements/tr-select-agent_type.html39
-rw-r--r--httemplate/elements/tr-select-agent_types.html19
-rw-r--r--httemplate/elements/tr-select-cdrbatch.html32
-rw-r--r--httemplate/elements/tr-select-cust-fields.html15
-rw-r--r--httemplate/elements/tr-select-cust_location.html178
-rw-r--r--httemplate/elements/tr-select-cust_main-status.html29
-rw-r--r--httemplate/elements/tr-select-cust_pkg-status.html29
-rw-r--r--httemplate/elements/tr-select-did.html25
-rw-r--r--httemplate/elements/tr-select-domain.html12
-rw-r--r--httemplate/elements/tr-select-from_to.html52
-rw-r--r--httemplate/elements/tr-select-invoice_template.html39
-rw-r--r--httemplate/elements/tr-select-otaker.html10
-rw-r--r--httemplate/elements/tr-select-part_pkg.html39
-rw-r--r--httemplate/elements/tr-select-part_referral.html37
-rw-r--r--httemplate/elements/tr-select-part_svc.html29
-rw-r--r--httemplate/elements/tr-select-payby.html37
-rw-r--r--httemplate/elements/tr-select-pkg_class.html27
-rw-r--r--httemplate/elements/tr-select-rate.html21
-rwxr-xr-xhttemplate/elements/tr-select-reason.html189
-rw-r--r--httemplate/elements/tr-select-table.html20
-rw-r--r--httemplate/elements/tr-select-taxclass.html34
-rw-r--r--httemplate/elements/tr-select-taxoverride.html18
-rw-r--r--httemplate/elements/tr-select-taxproduct.html18
-rw-r--r--httemplate/elements/tr-select.html61
-rw-r--r--httemplate/elements/tr-selectlayers-select.html1
-rw-r--r--httemplate/elements/tr-selectlayers.html25
-rw-r--r--httemplate/elements/tr-selectmultiple-part_pkg.html20
-rw-r--r--httemplate/elements/tr-td-label.html17
-rw-r--r--httemplate/elements/tr-title.html5
-rw-r--r--httemplate/elements/xmenu.css196
-rw-r--r--httemplate/elements/xmenu.js668
-rw-r--r--httemplate/elements/xmenu.top.css211
-rw-r--r--httemplate/elements/xmenu.top.js671
-rw-r--r--httemplate/elements/xmlhttp.html125
-rw-r--r--httemplate/graph/cust_bill_pkg.cgi109
-rw-r--r--httemplate/graph/cust_pkg.cgi63
-rw-r--r--httemplate/graph/elements/monthly.html351
-rw-r--r--httemplate/graph/money_time.cgi98
-rw-r--r--httemplate/graph/report_cust_bill_pkg.html39
-rw-r--r--httemplate/graph/report_cust_pkg.html28
-rw-r--r--httemplate/graph/report_money_time.html43
-rw-r--r--httemplate/images/32clear.gifbin0 -> 815 bytes
-rw-r--r--httemplate/images/ach.pngbin0 -> 29759 bytes
-rw-r--r--httemplate/images/arrow.down.pngbin0 -> 155 bytes
-rw-r--r--httemplate/images/arrow.right.black.pngbin0 -> 160 bytes
-rw-r--r--httemplate/images/arrow.right.pngbin0 -> 160 bytes
-rw-r--r--httemplate/images/background-cheat.pngbin0 -> 338 bytes
-rw-r--r--httemplate/images/black-gradient.pngbin0 -> 397 bytes
-rw-r--r--httemplate/images/black-gray-corner.pngbin0 -> 460 bytes
-rw-r--r--httemplate/images/black-gray-gradient.pngbin0 -> 384 bytes
-rw-r--r--httemplate/images/black-gray-side.pngbin0 -> 198 bytes
-rw-r--r--httemplate/images/black-gray-top.pngbin0 -> 203 bytes
-rw-r--r--httemplate/images/calendar-disabled.pngbin0 -> 209 bytes
-rw-r--r--httemplate/images/calendar.pngbin0 -> 426 bytes
-rw-r--r--httemplate/images/cross.pngbin0 -> 655 bytes
-rw-r--r--httemplate/images/cvv2.pngbin0 -> 7791 bytes
-rw-r--r--httemplate/images/cvv2_amex.pngbin0 -> 9539 bytes
-rw-r--r--httemplate/images/error.pngbin0 -> 666 bytes
-rw-r--r--httemplate/images/menu-left-example.pngbin0 -> 24709 bytes
-rw-r--r--httemplate/images/menu-top-example.pngbin0 -> 22816 bytes
-rw-r--r--httemplate/images/progressbar-empty.pngbin0 -> 90 bytes
-rw-r--r--httemplate/images/progressbar-full.pngbin0 -> 79 bytes
-rw-r--r--httemplate/images/red_telephone_mimooh_01.pngbin0 -> 921 bytes
-rw-r--r--httemplate/images/small-logo.pngbin0 -> 4887 bytes
-rw-r--r--httemplate/images/tick.pngbin0 -> 537 bytes
-rw-r--r--httemplate/images/wait-orange.gifbin0 -> 1849 bytes
-rw-r--r--httemplate/index.html54
-rw-r--r--httemplate/misc/areacodes.cgi24
-rw-r--r--httemplate/misc/batch-cust_pay.html38
-rwxr-xr-xhttemplate/misc/bill.cgi45
-rwxr-xr-xhttemplate/misc/bulk_change_pkg.cgi59
-rwxr-xr-xhttemplate/misc/cancel-unaudited.cgi33
-rw-r--r--httemplate/misc/cancel_cust.html63
-rwxr-xr-xhttemplate/misc/cancel_pkg.html109
-rwxr-xr-xhttemplate/misc/catchall.cgi118
-rw-r--r--httemplate/misc/cdr-import.html61
-rw-r--r--httemplate/misc/cdr.cgi48
-rwxr-xr-xhttemplate/misc/change_pkg.cgi72
-rw-r--r--httemplate/misc/copy-rate_detail.html61
-rw-r--r--httemplate/misc/counties.cgi7
-rwxr-xr-xhttemplate/misc/cust_main-cancel.cgi57
-rw-r--r--httemplate/misc/cust_main-import.cgi148
-rw-r--r--httemplate/misc/cust_main-import_charges.cgi22
-rw-r--r--httemplate/misc/cust_main_note-import.cgi207
-rw-r--r--httemplate/misc/cust_main_note-import.html39
-rw-r--r--httemplate/misc/cust_pay-import.cgi62
-rwxr-xr-xhttemplate/misc/delay_susp_pkg.html70
-rw-r--r--httemplate/misc/delete-agent_payment_gateway.cgi15
-rwxr-xr-xhttemplate/misc/delete-cust_credit.cgi21
-rwxr-xr-xhttemplate/misc/delete-cust_pay.cgi21
-rwxr-xr-xhttemplate/misc/delete-cust_refund.cgi21
-rwxr-xr-xhttemplate/misc/delete-customer.cgi64
-rwxr-xr-xhttemplate/misc/delete-domain_record.cgi20
-rwxr-xr-xhttemplate/misc/delete-part_export.cgi20
-rw-r--r--httemplate/misc/disable-payment_gateway.cgi25
-rw-r--r--httemplate/misc/download-batch.cgi213
-rw-r--r--httemplate/misc/dump.cgi20
-rw-r--r--httemplate/misc/email-customers.html145
-rwxr-xr-xhttemplate/misc/email-invoice.cgi19
-rw-r--r--httemplate/misc/email_events.cgi9
-rw-r--r--httemplate/misc/email_invoice_events.cgi9
-rw-r--r--httemplate/misc/email_invoices.cgi9
-rwxr-xr-xhttemplate/misc/enable_or_disable_tax.html37
-rw-r--r--httemplate/misc/exchanges.cgi24
-rwxr-xr-xhttemplate/misc/fax-invoice.cgi19
-rw-r--r--httemplate/misc/fax_events.cgi9
-rw-r--r--httemplate/misc/fax_invoice_events.cgi9
-rw-r--r--httemplate/misc/fax_invoices.cgi9
-rw-r--r--httemplate/misc/file-upload.html53
-rw-r--r--httemplate/misc/ftp_invoices.cgi9
-rw-r--r--httemplate/misc/inventory_item-import.html67
-rwxr-xr-xhttemplate/misc/link.cgi84
-rw-r--r--httemplate/misc/location.cgi19
-rw-r--r--httemplate/misc/meta-import.cgi79
-rw-r--r--httemplate/misc/order_pkg.html75
-rw-r--r--httemplate/misc/payment.cgi285
-rw-r--r--httemplate/misc/phone_avail-import.html88
-rw-r--r--httemplate/misc/phonenums.cgi29
-rwxr-xr-xhttemplate/misc/print-invoice.cgi19
-rw-r--r--httemplate/misc/print_events.cgi9
-rw-r--r--httemplate/misc/print_invoice_events.cgi9
-rw-r--r--httemplate/misc/print_invoices.cgi9
-rw-r--r--httemplate/misc/process/batch-cust_pay.cgi47
-rwxr-xr-xhttemplate/misc/process/bulk_change_pkg.cgi56
-rwxr-xr-xhttemplate/misc/process/cancel_pkg.html72
-rwxr-xr-xhttemplate/misc/process/catchall.cgi35
-rw-r--r--httemplate/misc/process/cdr-import.html9
-rw-r--r--httemplate/misc/process/copy-rate_detail.html61
-rw-r--r--httemplate/misc/process/cust_main-import.cgi10
-rw-r--r--httemplate/misc/process/cust_main-import_charges.cgi23
-rw-r--r--httemplate/misc/process/cust_main_note-import.cgi82
-rw-r--r--httemplate/misc/process/cust_pay-import.cgi21
-rwxr-xr-xhttemplate/misc/process/delay_susp_pkg.html41
-rwxr-xr-xhttemplate/misc/process/delete-customer.cgi33
-rw-r--r--httemplate/misc/process/email-customers.html9
-rwxr-xr-xhttemplate/misc/process/enable_or_disable_tax.html41
-rw-r--r--httemplate/misc/process/inventory_item-import.html9
-rwxr-xr-xhttemplate/misc/process/link.cgi72
-rw-r--r--httemplate/misc/process/meta-import.cgi190
-rw-r--r--httemplate/misc/process/payment.cgi183
-rw-r--r--httemplate/misc/process/phone_avail-import.html9
-rwxr-xr-xhttemplate/misc/process/recharge_svc.html92
-rwxr-xr-xhttemplate/misc/process/recharge_svc.new85
-rw-r--r--httemplate/misc/process/tax-import.cgi9
-rw-r--r--httemplate/misc/process/tax-upgrade.cgi147
-rw-r--r--httemplate/misc/process/timeworked.html57
-rw-r--r--httemplate/misc/queue.cgi49
-rwxr-xr-xhttemplate/misc/recharge_svc.html105
-rw-r--r--httemplate/misc/spool_invoices.cgi9
-rw-r--r--httemplate/misc/states.cgi7
-rw-r--r--httemplate/misc/svc_acct-domains.cgi31
-rw-r--r--httemplate/misc/tax-import.cgi65
-rwxr-xr-xhttemplate/misc/timeworked.html135
-rwxr-xr-xhttemplate/misc/unadjourn_pkg.cgi17
-rwxr-xr-xhttemplate/misc/unapply-cust_credit.cgi20
-rwxr-xr-xhttemplate/misc/unapply-cust_pay.cgi20
-rwxr-xr-xhttemplate/misc/unexpire_pkg.cgi17
-rwxr-xr-xhttemplate/misc/unprovision.cgi26
-rwxr-xr-xhttemplate/misc/unsusp_pkg.cgi20
-rwxr-xr-xhttemplate/misc/unvoid-cust_pay_void.cgi21
-rw-r--r--httemplate/misc/upload-batch.cgi36
-rwxr-xr-xhttemplate/misc/void-cust_pay.cgi26
-rw-r--r--httemplate/misc/whois.cgi33
-rw-r--r--httemplate/misc/xmlhttp-cust_main-address_standardize.html89
-rw-r--r--httemplate/misc/xmlhttp-cust_main-search.cgi36
-rw-r--r--httemplate/misc/xmlrpc.cgi18
-rw-r--r--httemplate/pref/pref-process.html58
-rw-r--r--httemplate/pref/pref.html124
-rw-r--r--httemplate/search/cdr.html159
-rwxr-xr-xhttemplate/search/cust_bill.html251
-rw-r--r--httemplate/search/cust_bill_event.cgi166
-rwxr-xr-xhttemplate/search/cust_bill_event.html67
-rw-r--r--httemplate/search/cust_bill_pay.html131
-rw-r--r--httemplate/search/cust_bill_pkg.cgi335
-rwxr-xr-xhttemplate/search/cust_credit.html104
-rw-r--r--httemplate/search/cust_credit_bill.html135
-rw-r--r--httemplate/search/cust_credit_refund.html130
-rw-r--r--httemplate/search/cust_event.html285
-rwxr-xr-xhttemplate/search/cust_main-otaker.cgi31
-rw-r--r--httemplate/search/cust_main-zip.html99
-rwxr-xr-xhttemplate/search/cust_main.cgi736
-rwxr-xr-xhttemplate/search/cust_main.html109
-rwxr-xr-xhttemplate/search/cust_pay.cgi7
-rwxr-xr-xhttemplate/search/cust_pay_batch.cgi193
-rwxr-xr-xhttemplate/search/cust_pay_pending.html57
-rwxr-xr-xhttemplate/search/cust_pkg.cgi233
-rw-r--r--httemplate/search/cust_refund.html7
-rw-r--r--httemplate/search/cust_svc.html138
-rw-r--r--httemplate/search/cust_tax_exempt.cgi139
-rw-r--r--httemplate/search/cust_tax_exempt.html31
-rw-r--r--httemplate/search/cust_tax_exempt_pkg.cgi182
-rwxr-xr-xhttemplate/search/elements/cust_pay_or_refund.html301
-rw-r--r--httemplate/search/elements/search.html912
-rw-r--r--httemplate/search/inventory_item.html125
-rwxr-xr-xhttemplate/search/pay_batch.cgi130
-rw-r--r--httemplate/search/pay_batch.html33
-rw-r--r--httemplate/search/phone_avail.html102
-rw-r--r--httemplate/search/prepay_credit.html67
-rw-r--r--httemplate/search/queue.html138
-rw-r--r--httemplate/search/reg_code.html40
-rw-r--r--httemplate/search/report_cdr.html58
-rw-r--r--httemplate/search/report_cust_bill.html36
-rw-r--r--httemplate/search/report_cust_credit.html48
-rw-r--r--httemplate/search/report_cust_event.html65
-rw-r--r--httemplate/search/report_cust_main-zip.html53
-rwxr-xr-xhttemplate/search/report_cust_main.html94
-rw-r--r--httemplate/search/report_cust_pay.html79
-rw-r--r--httemplate/search/report_cust_pay_batch.html44
-rwxr-xr-xhttemplate/search/report_cust_pkg.html149
-rwxr-xr-xhttemplate/search/report_newtax.cgi158
-rwxr-xr-xhttemplate/search/report_newtax.html23
-rw-r--r--httemplate/search/report_prepaid_income.cgi87
-rw-r--r--httemplate/search/report_prepaid_income.html43
-rwxr-xr-xhttemplate/search/report_receivables.cgi197
-rwxr-xr-xhttemplate/search/report_receivables.html35
-rw-r--r--httemplate/search/report_rt_transaction.html24
-rw-r--r--httemplate/search/report_sql.html23
-rwxr-xr-xhttemplate/search/report_svc_acct.html105
-rwxr-xr-xhttemplate/search/report_tax.cgi612
-rwxr-xr-xhttemplate/search/report_tax.html42
-rw-r--r--httemplate/search/rt_transaction.html96
-rw-r--r--httemplate/search/sql.html13
-rw-r--r--httemplate/search/sqlradius.cgi328
-rw-r--r--httemplate/search/sqlradius.html123
-rwxr-xr-xhttemplate/search/svc_acct.cgi296
-rwxr-xr-xhttemplate/search/svc_broadband.cgi123
-rwxr-xr-xhttemplate/search/svc_domain.cgi112
-rwxr-xr-xhttemplate/search/svc_external.cgi153
-rwxr-xr-xhttemplate/search/svc_forward.cgi146
-rw-r--r--httemplate/search/svc_phone.cgi117
-rwxr-xr-xhttemplate/search/svc_www.cgi113
-rw-r--r--httemplate/search/timeworked.html117
-rwxr-xr-xhttemplate/view/REAL_logo.cgi14
-rwxr-xr-xhttemplate/view/cust_bill-logo.cgi31
-rwxr-xr-xhttemplate/view/cust_bill-pdf.cgi28
-rwxr-xr-xhttemplate/view/cust_bill-ps.cgi24
-rwxr-xr-xhttemplate/view/cust_bill.cgi120
-rwxr-xr-xhttemplate/view/cust_main.cgi158
-rw-r--r--httemplate/view/cust_main/billing.html220
-rw-r--r--httemplate/view/cust_main/contacts.html122
-rw-r--r--httemplate/view/cust_main/misc.html110
-rwxr-xr-xhttemplate/view/cust_main/notes.html88
-rwxr-xr-xhttemplate/view/cust_main/packages.html241
-rw-r--r--httemplate/view/cust_main/packages/location.html60
-rw-r--r--httemplate/view/cust_main/packages/package.html215
-rw-r--r--httemplate/view/cust_main/packages/services.html119
-rw-r--r--httemplate/view/cust_main/packages/status.html379
-rw-r--r--httemplate/view/cust_main/payment_history.html413
-rw-r--r--httemplate/view/cust_main/payment_history/credit.html140
-rw-r--r--httemplate/view/cust_main/payment_history/invoice.html34
-rw-r--r--httemplate/view/cust_main/payment_history/payment.html209
-rw-r--r--httemplate/view/cust_main/payment_history/refund.html50
-rw-r--r--httemplate/view/cust_main/payment_history/voided_payment.html37
-rw-r--r--httemplate/view/cust_main/tickets.html84
-rw-r--r--httemplate/view/cust_pay.html135
-rw-r--r--httemplate/view/cust_refund.html142
-rw-r--r--httemplate/view/elements/svc_Common.html152
-rw-r--r--httemplate/view/logo.cgi47
-rw-r--r--httemplate/view/svc_Common.html29
-rwxr-xr-xhttemplate/view/svc_acct.cgi401
-rw-r--r--httemplate/view/svc_broadband.cgi212
-rwxr-xr-xhttemplate/view/svc_domain.cgi161
-rw-r--r--httemplate/view/svc_external.cgi63
-rwxr-xr-xhttemplate/view/svc_forward.cgi105
-rw-r--r--httemplate/view/svc_phone.cgi54
-rw-r--r--httemplate/view/svc_www.cgi104
-rw-r--r--init.d/freeside-init110
-rw-r--r--rpm/INSTALL4
-rw-r--r--rpm/freeside-selfservice.conf11
-rw-r--r--rpm/freeside.spec459
-rw-r--r--rpm/freeside.sysconfig5
-rwxr-xr-xrpm/rpm2Bundle111
-rwxr-xr-x[-rw-r--r--]rt/COPYING (renamed from GPL)0
-rw-r--r--rt/Changelog23227
-rw-r--r--rt/FREESIDE_MODIFIED34
-rw-r--r--rt/Makefile494
-rw-r--r--rt/Makefile.in494
-rwxr-xr-xrt/README365
-rw-r--r--rt/README.Oracle37
-rw-r--r--rt/UPGRADING222
-rw-r--r--rt/aclocal.m4158
-rw-r--r--rt/autom4te.cache/output.02771
-rw-r--r--rt/autom4te.cache/requests94
-rw-r--r--rt/autom4te.cache/traces.0158
-rwxr-xr-xrt/bin/mason_handler.fcgi86
-rw-r--r--rt/bin/mason_handler.fcgi.in86
-rwxr-xr-xrt/bin/mason_handler.scgi67
-rw-r--r--rt/bin/mason_handler.scgi.in67
-rw-r--r--rt/bin/mason_handler.svc260
-rw-r--r--rt/bin/mason_handler.svc.in260
-rw-r--r--rt/bin/rt-commit-handler846
-rw-r--r--rt/bin/rt-crontool298
-rw-r--r--rt/bin/rt-crontool.in298
-rwxr-xr-xrt/bin/rt-mailgate323
-rw-r--r--rt/bin/rt-mailgate.in323
-rw-r--r--rt/bin/rt.in2060
-rwxr-xr-xrt/bin/standalone_httpd.in67
-rw-r--r--rt/bin/webmux.pl.in137
-rw-r--r--rt/config.layout.in127
-rw-r--r--rt/config.log226
-rwxr-xr-xrt/config.status817
-rwxr-xr-xrt/configure3164
-rw-r--r--rt/configure.ac310
-rwxr-xr-xrt/docs/README.docs2
-rw-r--r--rt/docs/Security25
-rw-r--r--rt/docs/design_docs/3.3-schema-redesign.txt57
-rwxr-xr-xrt/docs/design_docs/CARS66
-rwxr-xr-xrt/docs/design_docs/TransactionTypes.txt48
-rw-r--r--rt/docs/design_docs/acls50
-rw-r--r--rt/docs/design_docs/approval_notices8
-rw-r--r--rt/docs/design_docs/approval_template25
-rw-r--r--rt/docs/design_docs/cf_search72
-rw-r--r--rt/docs/design_docs/cli_spec31
-rw-r--r--rt/docs/design_docs/cvs_integration164
-rw-r--r--rt/docs/design_docs/delegation115
-rw-r--r--rt/docs/design_docs/evil_plans162
-rw-r--r--rt/docs/design_docs/groups_notes88
-rw-r--r--rt/docs/design_docs/link-definitions.txt143
-rw-r--r--rt/docs/design_docs/realflow.txt191
-rw-r--r--rt/docs/design_docs/recursive_group_membership_algorithm109
-rw-r--r--rt/docs/design_docs/rql_parser_machine.graphviz32
-rw-r--r--rt/docs/design_docs/rt-mvc32
-rw-r--r--rt/docs/design_docs/ruleset-workflow.txt158
-rw-r--r--rt/docs/design_docs/string-extraction-guide.txt100
-rwxr-xr-xrt/docs/design_docs/subscription-definitions.txt113
-rw-r--r--rt/docs/design_docs/ticket_templates16
-rw-r--r--rt/docs/design_docs/users14
-rw-r--r--rt/docs/rt3-schema-relationships.dot89
-rw-r--r--rt/etc/RT_Config.pm587
-rw-r--r--rt/etc/RT_Config.pm.in594
-rw-r--r--rt/etc/RT_SiteConfig.pm52
-rw-r--r--rt/etc/acl.Informix5
-rw-r--r--rt/etc/acl.Oracle10
-rwxr-xr-xrt/etc/acl.Pg67
-rw-r--r--rt/etc/acl.Sybase6
-rwxr-xr-xrt/etc/acl.mysql9
-rw-r--r--rt/etc/constraints.mysql85
-rw-r--r--rt/etc/drop.Informix19
-rw-r--r--rt/etc/drop.Oracle41
-rw-r--r--rt/etc/initialdata625
-rw-r--r--rt/etc/rt.spec137
-rw-r--r--rt/etc/schema.Informix364
-rwxr-xr-xrt/etc/schema.Pg636
-rw-r--r--rt/etc/schema.SQLite442
-rw-r--r--rt/etc/schema.Sybase444
-rwxr-xr-xrt/etc/schema.mysql463
-rw-r--r--rt/etc/upgrade/3.1.0/acl.Informix4
-rwxr-xr-xrt/etc/upgrade/3.1.0/acl.Oracle4
-rwxr-xr-xrt/etc/upgrade/3.1.0/acl.Pg19
-rwxr-xr-xrt/etc/upgrade/3.1.0/acl.SQLite4
-rwxr-xr-xrt/etc/upgrade/3.1.0/acl.mysql4
-rw-r--r--rt/etc/upgrade/3.1.0/content2
-rw-r--r--rt/etc/upgrade/3.1.0/schema.Informix17
-rw-r--r--rt/etc/upgrade/3.1.0/schema.Oracle17
-rwxr-xr-xrt/etc/upgrade/3.1.0/schema.Pg25
-rw-r--r--rt/etc/upgrade/3.1.0/schema.SQLite21
-rwxr-xr-xrt/etc/upgrade/3.1.0/schema.mysql21
-rw-r--r--rt/etc/upgrade/3.1.15/content7
-rw-r--r--rt/etc/upgrade/3.1.17/content22
-rw-r--r--rt/etc/upgrade/3.3.0/acl.Informix4
-rw-r--r--rt/etc/upgrade/3.3.0/acl.Oracle4
-rw-r--r--rt/etc/upgrade/3.3.0/acl.Pg20
-rw-r--r--rt/etc/upgrade/3.3.0/acl.SQLite4
-rw-r--r--rt/etc/upgrade/3.3.0/acl.mysql4
-rw-r--r--rt/etc/upgrade/3.3.0/content1
-rw-r--r--rt/etc/upgrade/3.3.0/schema.Oracle65
-rw-r--r--rt/etc/upgrade/3.3.0/schema.Pg74
-rw-r--r--rt/etc/upgrade/3.3.0/schema.mysql65
-rw-r--r--rt/etc/upgrade/3.3.11/acl.Oracle4
-rw-r--r--rt/etc/upgrade/3.3.11/acl.Pg4
-rw-r--r--rt/etc/upgrade/3.3.11/acl.SQLite4
-rw-r--r--rt/etc/upgrade/3.3.11/acl.mysql4
-rw-r--r--rt/etc/upgrade/3.3.11/content1
-rw-r--r--rt/etc/upgrade/3.3.11/schema.Oracle0
-rw-r--r--rt/etc/upgrade/3.3.11/schema.Pg11
-rw-r--r--rt/etc/upgrade/3.3.11/schema.SQLite0
-rw-r--r--rt/etc/upgrade/3.3.11/schema.mysql5
-rw-r--r--rt/etc/upgrade/3.5.1/content36
-rw-r--r--rt/html/Admin/CustomFields/GroupRights.html172
-rw-r--r--rt/html/Admin/CustomFields/Modify.html258
-rw-r--r--rt/html/Admin/CustomFields/Objects.html147
-rw-r--r--rt/html/Admin/CustomFields/UserRights.html170
-rw-r--r--rt/html/Admin/CustomFields/index.html93
-rw-r--r--rt/html/Admin/Elements/AddCustomFieldValue74
-rw-r--r--rt/html/Admin/Elements/ConfigureMyRT80
-rw-r--r--rt/html/Admin/Elements/CreateUserCalled50
-rw-r--r--rt/html/Admin/Elements/CustomFieldTabs118
-rw-r--r--rt/html/Admin/Elements/EditCustomField159
-rw-r--r--rt/html/Admin/Elements/EditCustomFieldValues96
-rw-r--r--rt/html/Admin/Elements/EditCustomFields205
-rw-r--r--rt/html/Admin/Elements/EditQueueWatchers78
-rw-r--r--rt/html/Admin/Elements/EditScrip183
-rw-r--r--rt/html/Admin/Elements/EditScrips125
-rw-r--r--rt/html/Admin/Elements/EditTemplates128
-rw-r--r--rt/html/Admin/Elements/EditUserComments56
-rwxr-xr-xrt/html/Admin/Elements/GlobalCustomFieldTabs95
-rw-r--r--rt/html/Admin/Elements/GroupTabs102
-rw-r--r--rt/html/Admin/Elements/Header52
-rw-r--r--rt/html/Admin/Elements/ListGlobalCustomFields61
-rw-r--r--rt/html/Admin/Elements/ListGlobalScrips76
-rw-r--r--rt/html/Admin/Elements/ModifyTemplate84
-rw-r--r--rt/html/Admin/Elements/ObjectCustomFields111
-rw-r--r--rt/html/Admin/Elements/PickCustomFields98
-rw-r--r--rt/html/Admin/Elements/PickObjects81
-rw-r--r--rt/html/Admin/Elements/QueueRightsForUser64
-rw-r--r--rt/html/Admin/Elements/QueueTabs120
-rw-r--r--rt/html/Admin/Elements/SelectCustomFieldLookupType60
-rw-r--r--rt/html/Admin/Elements/SelectCustomFieldType60
-rw-r--r--rt/html/Admin/Elements/SelectGroups62
-rw-r--r--rt/html/Admin/Elements/SelectModifyGroup57
-rw-r--r--rt/html/Admin/Elements/SelectModifyQueue57
-rw-r--r--rt/html/Admin/Elements/SelectModifyUser73
-rw-r--r--rt/html/Admin/Elements/SelectNewGroupMembers99
-rw-r--r--rt/html/Admin/Elements/SelectRights118
-rw-r--r--rt/html/Admin/Elements/SelectScrip72
-rw-r--r--rt/html/Admin/Elements/SelectScripAction73
-rw-r--r--rt/html/Admin/Elements/SelectScripCondition72
-rw-r--r--rt/html/Admin/Elements/SelectSingleOrMultiple67
-rw-r--r--rt/html/Admin/Elements/SelectStage66
-rw-r--r--rt/html/Admin/Elements/SelectTemplate87
-rw-r--r--rt/html/Admin/Elements/SelectUsers64
-rw-r--r--rt/html/Admin/Elements/SystemTabs97
-rw-r--r--rt/html/Admin/Elements/Tabs93
-rwxr-xr-xrt/html/Admin/Elements/ToolTabs80
-rw-r--r--rt/html/Admin/Elements/UserTabs113
-rw-r--r--rt/html/Admin/Global/CustomFields/Groups.html58
-rwxr-xr-xrt/html/Admin/Global/CustomFields/Queue-Tickets.html58
-rwxr-xr-xrt/html/Admin/Global/CustomFields/Queue-Transactions.html58
-rw-r--r--rt/html/Admin/Global/CustomFields/Users.html58
-rw-r--r--rt/html/Admin/Global/CustomFields/index.html93
-rw-r--r--rt/html/Admin/Global/GroupRights.html123
-rw-r--r--rt/html/Admin/Global/MyRT.html104
-rw-r--r--rt/html/Admin/Global/Scrip.html87
-rw-r--r--rt/html/Admin/Global/Scrips.html77
-rw-r--r--rt/html/Admin/Global/Template.html125
-rw-r--r--rt/html/Admin/Global/Templates.html77
-rw-r--r--rt/html/Admin/Global/UserRights.html101
-rw-r--r--rt/html/Admin/Global/index.html94
-rw-r--r--rt/html/Admin/Groups/CustomFields.html48
-rw-r--r--rt/html/Admin/Groups/GroupRights.html119
-rw-r--r--rt/html/Admin/Groups/History.html68
-rw-r--r--rt/html/Admin/Groups/Members.html168
-rw-r--r--rt/html/Admin/Groups/Modify.html174
-rw-r--r--rt/html/Admin/Groups/UserRights.html116
-rw-r--r--rt/html/Admin/Groups/index.html113
-rw-r--r--rt/html/Admin/Queues/CustomField.html87
-rw-r--r--rt/html/Admin/Queues/CustomFields.html72
-rw-r--r--rt/html/Admin/Queues/GroupRights.html134
-rw-r--r--rt/html/Admin/Queues/Modify.html193
-rw-r--r--rt/html/Admin/Queues/People.html210
-rw-r--r--rt/html/Admin/Queues/Scrip.html100
-rw-r--r--rt/html/Admin/Queues/Scrips.html87
-rw-r--r--rt/html/Admin/Queues/Template.html130
-rw-r--r--rt/html/Admin/Queues/Templates.html81
-rw-r--r--rt/html/Admin/Queues/UserRights.html114
-rw-r--r--rt/html/Admin/Queues/index.html86
-rw-r--r--rt/html/Admin/Tools/Configuration.html100
-rw-r--r--rt/html/Admin/Tools/index.html55
-rw-r--r--rt/html/Admin/Users/CustomFields.html71
-rw-r--r--rt/html/Admin/Users/History.html68
-rw-r--r--rt/html/Admin/Users/Memberships.html67
-rw-r--r--rt/html/Admin/Users/Modify.html445
-rw-r--r--rt/html/Admin/Users/MyRT.html132
-rw-r--r--rt/html/Admin/Users/index.html115
-rw-r--r--rt/html/Admin/autohandler53
-rw-r--r--rt/html/Admin/index.html101
-rw-r--r--rt/html/Approvals/Display.html72
-rw-r--r--rt/html/Approvals/Elements/Approve94
-rw-r--r--rt/html/Approvals/Elements/PendingMyApproval111
-rw-r--r--rt/html/Approvals/Elements/ShowDependency109
-rw-r--r--rt/html/Approvals/Elements/Tabs58
-rw-r--r--rt/html/Approvals/index.html90
-rw-r--r--rt/html/Callbacks/ActivityReports/Elements/Tabs/Default7
-rw-r--r--rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default71
-rw-r--r--rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions7
-rw-r--r--rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default13
-rw-r--r--rt/html/Callbacks/kStatistics/Elements/Tabs/Default11
-rw-r--r--rt/html/Developer/CronTool/autohandler9
-rw-r--r--rt/html/Developer/CronTool/index.html116
-rw-r--r--rt/html/Download/CustomFieldValue/dhandler77
-rw-r--r--rt/html/Download/Tabular/dhandler76
-rw-r--r--rt/html/Elements/AddCustomers59
-rw-r--r--rt/html/Elements/BevelBoxRaisedEnd50
-rw-r--r--rt/html/Elements/BevelBoxRaisedStart50
-rw-r--r--rt/html/Elements/Callback92
-rw-r--r--rt/html/Elements/Checkbox63
-rw-r--r--rt/html/Elements/CollectionAsTable/Header125
-rw-r--r--rt/html/Elements/CollectionAsTable/ParseFormat106
-rw-r--r--rt/html/Elements/CollectionAsTable/Row117
-rw-r--r--rt/html/Elements/CreateTicket50
-rw-r--r--rt/html/Elements/EditCustomField99
-rw-r--r--rt/html/Elements/EditCustomFieldBinary62
-rw-r--r--rt/html/Elements/EditCustomFieldCombobox68
-rw-r--r--rt/html/Elements/EditCustomFieldFreeform74
-rw-r--r--rt/html/Elements/EditCustomFieldImage62
-rw-r--r--rt/html/Elements/EditCustomFieldSelect128
-rw-r--r--rt/html/Elements/EditCustomFieldText67
-rw-r--r--rt/html/Elements/EditCustomFieldWikitext67
-rw-r--r--rt/html/Elements/EditCustomers63
-rwxr-xr-xrt/html/Elements/EditLinks177
-rw-r--r--rt/html/Elements/EmailInput0
-rw-r--r--rt/html/Elements/Error86
-rw-r--r--rt/html/Elements/Footer94
-rw-r--r--rt/html/Elements/FreesideInvoiceSearch20
-rw-r--r--rt/html/Elements/FreesideNewCust3
-rw-r--r--rt/html/Elements/FreesideSearch13
-rw-r--r--rt/html/Elements/FreesideSvcSearch11
-rw-r--r--rt/html/Elements/GotoTicket48
-rw-r--r--rt/html/Elements/Header172
-rw-r--r--rt/html/Elements/ListActions65
-rw-r--r--rt/html/Elements/Login138
-rw-r--r--rt/html/Elements/Logo56
-rw-r--r--rt/html/Elements/Menu134
-rw-r--r--rt/html/Elements/MessageBox74
-rw-r--r--rt/html/Elements/MyAdminQueues54
-rw-r--r--rt/html/Elements/MyRT100
-rwxr-xr-xrt/html/Elements/MyReminders73
-rw-r--r--rt/html/Elements/MyRequests49
-rw-r--r--rt/html/Elements/MySupportQueues54
-rw-r--r--rt/html/Elements/MyTickets49
-rw-r--r--rt/html/Elements/PageLayout256
-rw-r--r--rt/html/Elements/QueryString63
-rw-r--r--rt/html/Elements/QueueSummary92
-rw-r--r--rt/html/Elements/QuickCreate71
-rw-r--r--rt/html/Elements/Quicksearch61
-rw-r--r--rt/html/Elements/RT__Ticket/ColumnMap314
-rw-r--r--rt/html/Elements/Refresh69
-rw-r--r--rt/html/Elements/RefreshHomepage51
-rw-r--r--rt/html/Elements/ScrubHTML73
-rw-r--r--rt/html/Elements/Section51
-rw-r--r--rt/html/Elements/SelectAttachmentField56
-rw-r--r--rt/html/Elements/SelectBoolean71
-rw-r--r--rt/html/Elements/SelectCustomFieldOperator64
-rw-r--r--rt/html/Elements/SelectCustomFieldValue65
-rw-r--r--rt/html/Elements/SelectDate75
-rw-r--r--rt/html/Elements/SelectDateRelation60
-rw-r--r--rt/html/Elements/SelectDateType60
-rw-r--r--rt/html/Elements/SelectEqualityOperator64
-rw-r--r--rt/html/Elements/SelectGroups62
-rw-r--r--rt/html/Elements/SelectLang80
-rw-r--r--rt/html/Elements/SelectLinkType61
-rw-r--r--rt/html/Elements/SelectMatch82
-rw-r--r--rt/html/Elements/SelectNewTicketQueue50
-rw-r--r--rt/html/Elements/SelectOwner110
-rw-r--r--rt/html/Elements/SelectQueue97
-rw-r--r--rt/html/Elements/SelectResultsPerPage68
-rw-r--r--rt/html/Elements/SelectSortOrder65
-rw-r--r--rt/html/Elements/SelectStatus67
-rw-r--r--rt/html/Elements/SelectTicketSortBy62
-rw-r--r--rt/html/Elements/SelectTicketTypes58
-rwxr-xr-xrt/html/Elements/SelectTimeUnits57
-rw-r--r--rt/html/Elements/SelectUsers62
-rw-r--r--rt/html/Elements/SelectWatcherType71
-rw-r--r--rt/html/Elements/SetupSessionCookie126
-rw-r--r--rt/html/Elements/ShowCustomFieldBinary51
-rw-r--r--rt/html/Elements/ShowCustomFieldImage53
-rw-r--r--rt/html/Elements/ShowCustomFieldWikitext58
-rw-r--r--rt/html/Elements/ShowCustomFields115
-rw-r--r--rt/html/Elements/ShowLink64
-rwxr-xr-xrt/html/Elements/ShowLinks112
-rw-r--r--rt/html/Elements/ShowMemberships88
-rw-r--r--rt/html/Elements/ShowSearch126
-rw-r--r--rt/html/Elements/SimpleSearch58
-rw-r--r--rt/html/Elements/Submit86
-rw-r--r--rt/html/Elements/Tabs137
-rw-r--r--rt/html/Elements/TicketList179
-rw-r--r--rt/html/Elements/TitleBox51
-rw-r--r--rt/html/Elements/TitleBoxEnd51
-rw-r--r--rt/html/Elements/TitleBoxStart51
-rw-r--r--rt/html/Elements/ValidateCustomFields81
-rw-r--r--rt/html/Helpers/CalPopup.html129
-rw-r--r--rt/html/Helpers/EmailAutocomplete0
-rw-r--r--rt/html/NoAuth/Logout.html74
-rw-r--r--rt/html/NoAuth/Reminder.html50
-rw-r--r--rt/html/NoAuth/css/3.4-compat/body.css75
-rw-r--r--rt/html/NoAuth/css/3.4-compat/footer.css61
-rw-r--r--rt/html/NoAuth/css/3.4-compat/forms.css104
-rw-r--r--rt/html/NoAuth/css/3.4-compat/header.css88
-rw-r--r--rt/html/NoAuth/css/3.4-compat/login.css54
-rw-r--r--rt/html/NoAuth/css/3.4-compat/main.css69
-rw-r--r--rt/html/NoAuth/css/3.4-compat/misc.css49
-rw-r--r--rt/html/NoAuth/css/3.4-compat/nav.css106
-rw-r--r--rt/html/NoAuth/css/3.4-compat/quickbar.css82
-rw-r--r--rt/html/NoAuth/css/3.4-compat/ticket.css50
-rw-r--r--rt/html/NoAuth/css/3.4-compat/titlebox.css103
-rw-r--r--rt/html/NoAuth/css/3.4-compat/transactions.css83
-rw-r--r--rt/html/NoAuth/css/3.5-default/approvals.css97
-rwxr-xr-xrt/html/NoAuth/css/3.5-default/body.css81
-rw-r--r--rt/html/NoAuth/css/3.5-default/footer.css91
-rwxr-xr-xrt/html/NoAuth/css/3.5-default/forms.css136
-rw-r--r--rt/html/NoAuth/css/3.5-default/freeside.css82
-rw-r--r--rt/html/NoAuth/css/3.5-default/header.css152
-rw-r--r--rt/html/NoAuth/css/3.5-default/login.css85
-rw-r--r--rt/html/NoAuth/css/3.5-default/logo.css60
-rw-r--r--rt/html/NoAuth/css/3.5-default/main.css62
-rwxr-xr-xrt/html/NoAuth/css/3.5-default/misc.css92
-rw-r--r--rt/html/NoAuth/css/3.5-default/nav.css163
-rw-r--r--rt/html/NoAuth/css/3.5-default/quickbar.css98
-rw-r--r--rt/html/NoAuth/css/3.5-default/ticket.css57
-rw-r--r--rt/html/NoAuth/css/3.5-default/titlebox.css179
-rwxr-xr-xrt/html/NoAuth/css/3.5-default/transactions.css150
-rw-r--r--rt/html/NoAuth/css/autohandler53
-rw-r--r--rt/html/NoAuth/css/dhandler30
-rw-r--r--rt/html/NoAuth/css/print.css85
-rw-r--r--rt/html/NoAuth/images/autohandler28
-rw-r--r--rt/html/NoAuth/images/bplogo.gifbin0 -> 755 bytes
-rw-r--r--rt/html/NoAuth/images/css/cb-light.gifbin0 -> 186 bytes
-rw-r--r--rt/html/NoAuth/images/css/cb.gifbin0 -> 110 bytes
-rw-r--r--rt/html/NoAuth/images/css/cbr-b2g.gifbin0 -> 135 bytes
-rw-r--r--rt/html/NoAuth/images/css/cbr-b2lb.gifbin0 -> 137 bytes
-rw-r--r--rt/html/NoAuth/images/css/cbr-gray.gifbin0 -> 137 bytes
-rw-r--r--rt/html/NoAuth/images/css/cbr-trans.gifbin0 -> 183 bytes
-rw-r--r--rt/html/NoAuth/images/css/cbr.gifbin0 -> 110 bytes
-rw-r--r--rt/html/NoAuth/images/css/ct-light.gifbin0 -> 162 bytes
-rw-r--r--rt/html/NoAuth/images/css/ct.gifbin0 -> 110 bytes
-rw-r--r--rt/html/NoAuth/images/css/ctr-b2g.gifbin0 -> 136 bytes
-rw-r--r--rt/html/NoAuth/images/css/ctr-b2lb.gifbin0 -> 114 bytes
-rw-r--r--rt/html/NoAuth/images/css/ctr-gray.gifbin0 -> 138 bytes
-rw-r--r--rt/html/NoAuth/images/css/ctr-trans.gifbin0 -> 182 bytes
-rw-r--r--rt/html/NoAuth/images/css/ctr.gifbin0 -> 111 bytes
-rw-r--r--rt/html/NoAuth/images/css/dark-arrow-up.pngbin0 -> 346 bytes
-rw-r--r--rt/html/NoAuth/images/css/dark-arrow.pngbin0 -> 337 bytes
-rw-r--r--rt/html/NoAuth/images/css/fieldbg-autocomplete.gifbin0 -> 1164 bytes
-rw-r--r--rt/html/NoAuth/images/css/light-arrow-up.pngbin0 -> 348 bytes
-rw-r--r--rt/html/NoAuth/images/css/light-arrow.pngbin0 -> 340 bytes
-rw-r--r--rt/html/NoAuth/images/css/rolldown-arrow.gifbin0 -> 83 bytes
-rw-r--r--rt/html/NoAuth/images/css/rolldown-arrow.pngbin0 -> 259 bytes
-rw-r--r--rt/html/NoAuth/images/css/rollup-arrow.gifbin0 -> 82 bytes
-rw-r--r--rt/html/NoAuth/images/favicon.pngbin0 -> 335 bytes
-rw-r--r--rt/html/NoAuth/images/small-logo.pngbin0 -> 4887 bytes
-rw-r--r--rt/html/NoAuth/js/ahah.js80
-rw-r--r--rt/html/NoAuth/js/autohandler53
-rw-r--r--rt/html/NoAuth/js/cascaded.js66
-rw-r--r--rt/html/NoAuth/js/class.js62
-rw-r--r--rt/html/NoAuth/js/combobox.js265
-rw-r--r--rt/html/NoAuth/js/list.js159
-rw-r--r--rt/html/NoAuth/js/scriptaculous/controls.js0
-rw-r--r--rt/html/NoAuth/js/scriptaculous/effects.js0
-rw-r--r--rt/html/NoAuth/js/scriptaculous/prototype.js0
-rw-r--r--rt/html/NoAuth/js/scriptaculous/scriptaculous.js0
-rw-r--r--rt/html/NoAuth/js/titlebox-state.js83
-rw-r--r--rt/html/NoAuth/js/util.js250
-rw-r--r--rt/html/Prefs/Elements/Tabs72
-rw-r--r--rt/html/Prefs/MyRT.html151
-rw-r--r--rt/html/Prefs/Quicksearch.html96
-rw-r--r--rt/html/Prefs/Search.html108
-rw-r--r--rt/html/Prefs/SearchOptions.html114
-rw-r--r--rt/html/REST/1.0/Forms/queue/default170
-rw-r--r--rt/html/REST/1.0/Forms/queue/ns62
-rw-r--r--rt/html/REST/1.0/Forms/ticket/attachments135
-rwxr-xr-xrt/html/REST/1.0/Forms/ticket/comment152
-rw-r--r--rt/html/REST/1.0/Forms/ticket/default345
-rw-r--r--rt/html/REST/1.0/Forms/ticket/history200
-rw-r--r--rt/html/REST/1.0/Forms/ticket/links172
-rwxr-xr-xrt/html/REST/1.0/Forms/ticket/merge96
-rwxr-xr-xrt/html/REST/1.0/Forms/ticket/take135
-rw-r--r--rt/html/REST/1.0/Forms/transaction/default143
-rw-r--r--rt/html/REST/1.0/Forms/user/default188
-rw-r--r--rt/html/REST/1.0/Forms/user/ns65
-rw-r--r--rt/html/REST/1.0/NoAuth/mail-gateway84
-rw-r--r--rt/html/REST/1.0/autohandler56
-rw-r--r--rt/html/REST/1.0/dhandler316
-rw-r--r--rt/html/REST/1.0/logout51
-rw-r--r--rt/html/REST/1.0/search/dhandler56
-rw-r--r--rt/html/REST/1.0/search/ticket158
-rw-r--r--rt/html/REST/1.0/ticket/comment177
-rw-r--r--rt/html/REST/1.0/ticket/link123
-rw-r--r--rt/html/REST/1.0/ticket/merge102
-rwxr-xr-xrt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart39
-rwxr-xr-xrt/html/RTx/Statistics/CallsMultiQueue/index.html330
-rwxr-xr-xrt/html/RTx/Statistics/CallsQueueDay/Elements/Chart29
-rw-r--r--rt/html/RTx/Statistics/CallsQueueDay/Results.tsv191
-rwxr-xr-xrt/html/RTx/Statistics/CallsQueueDay/index.html275
-rwxr-xr-xrt/html/RTx/Statistics/DayOfWeek/Elements/Chart26
-rwxr-xr-xrt/html/RTx/Statistics/DayOfWeek/index.html155
-rwxr-xr-xrt/html/RTx/Statistics/DurationAsString18
-rw-r--r--rt/html/RTx/Statistics/Elements/CollectionAsTable/Header126
-rw-r--r--rt/html/RTx/Statistics/Elements/CollectionAsTable/ParseFormat109
-rw-r--r--rt/html/RTx/Statistics/Elements/CollectionAsTable/Row112
-rw-r--r--rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox103
-rw-r--r--rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage5
-rw-r--r--rt/html/RTx/Statistics/Elements/DateSelectRow55
-rwxr-xr-xrt/html/RTx/Statistics/Elements/DurationAsString18
-rw-r--r--rt/html/RTx/Statistics/Elements/GraphBox27
-rwxr-xr-xrt/html/RTx/Statistics/Elements/SelectMultiQueue81
-rw-r--r--rt/html/RTx/Statistics/Elements/StatColumnMap173
-rwxr-xr-xrt/html/RTx/Statistics/Elements/Tabs72
-rw-r--r--rt/html/RTx/Statistics/FAQ/index.html23
-rwxr-xr-xrt/html/RTx/Statistics/OpenStalled/Elements/Chart27
-rw-r--r--rt/html/RTx/Statistics/OpenStalled/Results.tsv114
-rwxr-xr-xrt/html/RTx/Statistics/OpenStalled/index.html188
-rwxr-xr-xrt/html/RTx/Statistics/Resolution/Elements/Chart29
-rw-r--r--rt/html/RTx/Statistics/Resolution/index.html269
-rwxr-xr-xrt/html/RTx/Statistics/TimeToResolve/Elements/Chart23
-rwxr-xr-xrt/html/RTx/Statistics/TimeToResolve/index.html75
-rwxr-xr-xrt/html/RTx/Statistics/UserTest/Elements/Chart28
-rwxr-xr-xrt/html/RTx/Statistics/UserTest/index.html54
-rwxr-xr-xrt/html/RTx/Statistics/index.html59
-rw-r--r--rt/html/Reports/Activity/ActivityDetail.html83
-rw-r--r--rt/html/Reports/Activity/ActivitySummary.html61
-rw-r--r--rt/html/Reports/Activity/Elements/LimitReport23
-rw-r--r--rt/html/Reports/Activity/Elements/MiniPlot57
-rw-r--r--rt/html/Reports/Activity/Elements/PrintFooter7
-rw-r--r--rt/html/Reports/Activity/Elements/PrintHeader32
-rw-r--r--rt/html/Reports/Activity/Elements/ScreenFooter13
-rw-r--r--rt/html/Reports/Activity/Elements/ScreenHeader8
-rw-r--r--rt/html/Reports/Activity/Elements/Tabs52
-rw-r--r--rt/html/Reports/Activity/Elements/Wrapper16
-rw-r--r--rt/html/Reports/Activity/ResolutionComments.html62
-rw-r--r--rt/html/Reports/Activity/ResolutionStatistics.html95
-rw-r--r--rt/html/Reports/Activity/index.html29
-rw-r--r--rt/html/Search/Build.html832
-rw-r--r--rt/html/Search/Bulk.html396
-rw-r--r--rt/html/Search/Chart188
-rw-r--r--rt/html/Search/Chart.html73
-rwxr-xr-xrt/html/Search/Edit.html88
-rw-r--r--rt/html/Search/Elements/BuildFormatString244
-rw-r--r--rt/html/Search/Elements/Chart139
-rw-r--r--rt/html/Search/Elements/DisplayOptions143
-rw-r--r--rt/html/Search/Elements/EditFormat116
-rw-r--r--rt/html/Search/Elements/EditQuery67
-rw-r--r--rt/html/Search/Elements/EditSearches103
-rw-r--r--rt/html/Search/Elements/NewListActions68
-rw-r--r--rt/html/Search/Elements/PickBasics176
-rw-r--r--rt/html/Search/Elements/PickCFs80
-rw-r--r--rt/html/Search/Elements/PickCriteria82
-rw-r--r--rt/html/Search/Elements/SearchPrivacy55
-rw-r--r--rt/html/Search/Elements/SearchesForObject65
-rw-r--r--rt/html/Search/Elements/SelectAndOr53
-rw-r--r--rt/html/Search/Elements/SelectChartType56
-rw-r--r--rt/html/Search/Elements/SelectGroup67
-rw-r--r--rt/html/Search/Elements/SelectGroupBy63
-rw-r--r--rt/html/Search/Elements/SelectLinks66
-rw-r--r--rt/html/Search/Elements/SelectPersonType84
-rw-r--r--rt/html/Search/Elements/SelectSearchObject60
-rw-r--r--rt/html/Search/Elements/SelectSearchesForObjects69
-rwxr-xr-xrt/html/Search/Results.html177
-rw-r--r--rt/html/Search/Results.rdf87
-rw-r--r--rt/html/Search/Results.tsv134
-rw-r--r--rt/html/Search/Simple.html107
-rw-r--r--rt/html/SelfService/Attachment/dhandler51
-rw-r--r--rt/html/SelfService/Closed.html56
-rw-r--r--rt/html/SelfService/Create.html117
-rwxr-xr-xrt/html/SelfService/CreateTicketInQueue.html63
-rw-r--r--rt/html/SelfService/Display.html235
-rw-r--r--rt/html/SelfService/Elements/GotoTicket48
-rw-r--r--rt/html/SelfService/Elements/Header49
-rw-r--r--rt/html/SelfService/Elements/MyRequests84
-rw-r--r--rt/html/SelfService/Elements/Tabs113
-rw-r--r--rt/html/SelfService/Error.html70
-rw-r--r--rt/html/SelfService/Prefs.html92
-rw-r--r--rt/html/SelfService/Update.html129
-rw-r--r--rt/html/SelfService/index.html54
-rw-r--r--rt/html/Ticket/Attachment/dhandler94
-rw-r--r--rt/html/Ticket/Create.html405
-rw-r--r--rt/html/Ticket/Display.html182
-rw-r--r--rt/html/Ticket/Elements/AddCustomers52
-rw-r--r--rt/html/Ticket/Elements/AddWatchers123
-rw-r--r--rt/html/Ticket/Elements/BulkLinks77
-rw-r--r--rt/html/Ticket/Elements/EditBasics117
-rw-r--r--rt/html/Ticket/Elements/EditCustomField57
-rw-r--r--rt/html/Ticket/Elements/EditCustomFields110
-rw-r--r--rt/html/Ticket/Elements/EditCustomers63
-rw-r--r--rt/html/Ticket/Elements/EditDates77
-rw-r--r--rt/html/Ticket/Elements/EditPeople93
-rw-r--r--rt/html/Ticket/Elements/EditWatchers81
-rwxr-xr-xrt/html/Ticket/Elements/FindAttachments95
-rwxr-xr-xrt/html/Ticket/Elements/LoadTextAttachments94
-rwxr-xr-xrt/html/Ticket/Elements/PreviewScrips133
-rw-r--r--rt/html/Ticket/Elements/Reminders168
-rw-r--r--rt/html/Ticket/Elements/ShowAttachments104
-rw-r--r--rt/html/Ticket/Elements/ShowBasics85
-rw-r--r--rt/html/Ticket/Elements/ShowCustomFields51
-rw-r--r--rt/html/Ticket/Elements/ShowCustomers38
-rw-r--r--rt/html/Ticket/Elements/ShowDates86
-rw-r--r--rt/html/Ticket/Elements/ShowDependencies65
-rw-r--r--rt/html/Ticket/Elements/ShowGroupMembers63
-rw-r--r--rt/html/Ticket/Elements/ShowHistory166
-rw-r--r--rt/html/Ticket/Elements/ShowMembers68
-rw-r--r--rt/html/Ticket/Elements/ShowMessageHeaders92
-rw-r--r--rt/html/Ticket/Elements/ShowMessageStanza84
-rw-r--r--rt/html/Ticket/Elements/ShowPeople68
-rw-r--r--rt/html/Ticket/Elements/ShowQueue9
-rw-r--r--rt/html/Ticket/Elements/ShowRequestor89
-rw-r--r--rt/html/Ticket/Elements/ShowSummary120
-rw-r--r--rt/html/Ticket/Elements/ShowTime55
-rw-r--r--rt/html/Ticket/Elements/ShowTransaction194
-rw-r--r--rt/html/Ticket/Elements/ShowTransactionAttachments215
-rw-r--r--rt/html/Ticket/Elements/ShowUserEntry61
-rw-r--r--rt/html/Ticket/Elements/Tabs250
-rw-r--r--rt/html/Ticket/History.html89
-rw-r--r--rt/html/Ticket/Modify.html91
-rw-r--r--rt/html/Ticket/ModifyAll.html221
-rw-r--r--rt/html/Ticket/ModifyCustomers.html49
-rw-r--r--rt/html/Ticket/ModifyDates.html77
-rw-r--r--rt/html/Ticket/ModifyLinks.html82
-rw-r--r--rt/html/Ticket/ModifyPeople.html94
-rwxr-xr-xrt/html/Ticket/Reminders.html71
-rw-r--r--rt/html/Ticket/ShowEmailRecord.html73
-rw-r--r--rt/html/Ticket/Update.html228
-rw-r--r--rt/html/Tools/Elements/Tabs84
-rw-r--r--rt/html/Tools/MyDay.html117
-rw-r--r--rt/html/Tools/Offline.html166
-rw-r--r--rt/html/Tools/Reports/CreatedByDates.html94
-rw-r--r--rt/html/Tools/Reports/Elements/Tabs89
-rw-r--r--rt/html/Tools/Reports/ResolvedByDates.html95
-rw-r--r--rt/html/Tools/Reports/ResolvedByOwner.html70
-rw-r--r--rt/html/Tools/Reports/index.html50
-rw-r--r--rt/html/Tools/index.html52
-rw-r--r--rt/html/User/Delegation.html107
-rw-r--r--rt/html/User/Elements/DelegateRights109
-rw-r--r--rt/html/User/Elements/GroupTabs84
-rw-r--r--rt/html/User/Elements/Tabs89
-rw-r--r--rt/html/User/Groups/Members.html160
-rw-r--r--rt/html/User/Groups/Modify.html157
-rw-r--r--rt/html/User/Groups/index.html67
-rw-r--r--rt/html/User/Prefs.html289
-rw-r--r--rt/html/Widgets/ComboBox69
-rw-r--r--rt/html/Widgets/SavedSearch158
-rw-r--r--rt/html/Widgets/SelectionBox243
-rw-r--r--rt/html/Widgets/TitleBox54
-rwxr-xr-xrt/html/Widgets/TitleBoxEnd59
-rwxr-xr-xrt/html/Widgets/TitleBoxStart86
-rw-r--r--rt/html/autohandler331
-rw-r--r--rt/html/index.html117
-rw-r--r--rt/html/l52
-rw-r--r--rt/install-sh251
-rw-r--r--rt/lib/RT.pm465
-rw-r--r--rt/lib/RT.pm.in467
-rwxr-xr-xrt/lib/RT/ACE.pm328
-rw-r--r--rt/lib/RT/ACE_Overlay.pm958
-rwxr-xr-xrt/lib/RT/ACL.pm139
-rw-r--r--rt/lib/RT/ACL_Overlay.pm373
-rw-r--r--rt/lib/RT/Action/AutoOpen.pm109
-rwxr-xr-xrt/lib/RT/Action/Autoreply.pm140
-rw-r--r--rt/lib/RT/Action/CreateTickets.pm1476
-rw-r--r--rt/lib/RT/Action/EscalatePriority.pm167
-rwxr-xr-xrt/lib/RT/Action/Generic.pm231
-rwxr-xr-xrt/lib/RT/Action/Notify.pm179
-rwxr-xr-xrt/lib/RT/Action/NotifyAsComment.pm79
-rw-r--r--rt/lib/RT/Action/RecordComment.pm120
-rw-r--r--rt/lib/RT/Action/RecordCorrespondence.pm121
-rw-r--r--rt/lib/RT/Action/ResolveMembers.pm112
-rwxr-xr-xrt/lib/RT/Action/SendEmail.pm1004
-rw-r--r--rt/lib/RT/Action/SetPriority.pm85
-rw-r--r--rt/lib/RT/Action/UserDefined.pm95
-rwxr-xr-xrt/lib/RT/Attachment.pm396
-rw-r--r--rt/lib/RT/Attachment_Overlay.pm618
-rwxr-xr-xrt/lib/RT/Attachments.pm139
-rw-r--r--rt/lib/RT/Attachments_Overlay.pm173
-rw-r--r--rt/lib/RT/Attribute.pm349
-rw-r--r--rt/lib/RT/Attribute_Overlay.pm469
-rw-r--r--rt/lib/RT/Attributes.pm139
-rw-r--r--rt/lib/RT/Attributes_Overlay.pm198
-rw-r--r--rt/lib/RT/Base.pm173
-rw-r--r--rt/lib/RT/CachedGroupMember.pm282
-rw-r--r--rt/lib/RT/CachedGroupMember_Overlay.pm366
-rw-r--r--rt/lib/RT/CachedGroupMembers.pm139
-rw-r--r--rt/lib/RT/CachedGroupMembers_Overlay.pm177
-rw-r--r--rt/lib/RT/Condition/AnyTransaction.pm75
-rw-r--r--rt/lib/RT/Condition/BeforeDue.pm88
-rwxr-xr-xrt/lib/RT/Condition/Generic.pm235
-rw-r--r--rt/lib/RT/Condition/Overdue.pm92
-rw-r--r--rt/lib/RT/Condition/OwnerChange.pm124
-rw-r--r--rt/lib/RT/Condition/PriorityChange.pm82
-rw-r--r--rt/lib/RT/Condition/PriorityExceeds.pm81
-rw-r--r--rt/lib/RT/Condition/QueueChange.pm81
-rw-r--r--rt/lib/RT/Condition/StatusChange.pm83
-rw-r--r--rt/lib/RT/Condition/UserDefined.pm81
-rwxr-xr-xrt/lib/RT/CurrentUser.pm481
-rw-r--r--rt/lib/RT/CustomField.pm421
-rw-r--r--rt/lib/RT/CustomFieldValue.pm318
-rw-r--r--rt/lib/RT/CustomFieldValue_Overlay.pm97
-rw-r--r--rt/lib/RT/CustomFieldValues.pm151
-rw-r--r--rt/lib/RT/CustomFieldValues_Overlay.pm73
-rw-r--r--rt/lib/RT/CustomField_Overlay.pm1300
-rw-r--r--rt/lib/RT/CustomFields.pm150
-rw-r--r--rt/lib/RT/CustomFields_Overlay.pm264
-rw-r--r--rt/lib/RT/Date.pm646
-rw-r--r--rt/lib/RT/EmailParser.pm636
-rw-r--r--rt/lib/RT/Extension/ActivityReports.pm3
-rwxr-xr-xrt/lib/RT/Group.pm282
-rwxr-xr-xrt/lib/RT/GroupMember.pm213
-rw-r--r--rt/lib/RT/GroupMember_Overlay.pm390
-rwxr-xr-xrt/lib/RT/GroupMembers.pm139
-rw-r--r--rt/lib/RT/GroupMembers_Overlay.pm153
-rw-r--r--rt/lib/RT/Group_Overlay.pm1383
-rwxr-xr-xrt/lib/RT/Groups.pm139
-rw-r--r--rt/lib/RT/Groups_Overlay.pm537
-rw-r--r--rt/lib/RT/Handle.pm143
-rw-r--r--rt/lib/RT/I18N.pm501
-rw-r--r--rt/lib/RT/I18N/cs.pm115
-rw-r--r--rt/lib/RT/I18N/cs.po6201
-rw-r--r--rt/lib/RT/I18N/da.po7074
-rw-r--r--rt/lib/RT/I18N/de.po5194
-rw-r--r--rt/lib/RT/I18N/en.po99
-rw-r--r--rt/lib/RT/I18N/es.po6492
-rw-r--r--rt/lib/RT/I18N/fi.po6188
-rw-r--r--rt/lib/RT/I18N/fr.po5117
-rw-r--r--rt/lib/RT/I18N/he.po5414
-rw-r--r--rt/lib/RT/I18N/hu.po5170
-rw-r--r--rt/lib/RT/I18N/i_default.pm110
-rw-r--r--rt/lib/RT/I18N/id.po5520
-rw-r--r--rt/lib/RT/I18N/it.po6655
-rw-r--r--rt/lib/RT/I18N/ja.po6749
-rw-r--r--rt/lib/RT/I18N/nl.po6195
-rw-r--r--rt/lib/RT/I18N/no.po6563
-rw-r--r--rt/lib/RT/I18N/pl.po6715
-rw-r--r--rt/lib/RT/I18N/pt_br.po6531
-rw-r--r--rt/lib/RT/I18N/ru.po6737
-rw-r--r--rt/lib/RT/I18N/sv.po5840
-rw-r--r--rt/lib/RT/I18N/tr.po5079
-rw-r--r--rt/lib/RT/I18N/zh_cn.po8423
-rw-r--r--rt/lib/RT/I18N/zh_tw.po8360
-rw-r--r--rt/lib/RT/Interface/CLI.pm270
-rwxr-xr-xrt/lib/RT/Interface/Email.pm991
-rwxr-xr-xrt/lib/RT/Interface/Email/Auth/GnuPG.pm123
-rw-r--r--rt/lib/RT/Interface/Email/Auth/MailFrom.pm189
-rw-r--r--rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm96
-rw-r--r--rt/lib/RT/Interface/REST.pm288
-rw-r--r--rt/lib/RT/Interface/Web.pm1687
-rw-r--r--rt/lib/RT/Interface/Web/Handler.pm211
-rw-r--r--rt/lib/RT/Interface/Web/Menu.pm68
-rw-r--r--rt/lib/RT/Interface/Web/Menu/Item.pm86
-rwxr-xr-xrt/lib/RT/Interface/Web/QueryBuilder.pm58
-rwxr-xr-xrt/lib/RT/Interface/Web/QueryBuilder/Tree.pm247
-rwxr-xr-xrt/lib/RT/Interface/Web/Standalone.pm84
-rw-r--r--rt/lib/RT/Interface/Web_Vendor.pm201
-rw-r--r--rt/lib/RT/Link.pm326
-rw-r--r--rt/lib/RT/Link_Overlay.pm390
-rw-r--r--rt/lib/RT/Links.pm139
-rw-r--r--rt/lib/RT/Links_Overlay.pm174
-rw-r--r--rt/lib/RT/ObjectCustomField.pm295
-rw-r--r--rt/lib/RT/ObjectCustomFieldValue.pm433
-rw-r--r--rt/lib/RT/ObjectCustomFieldValue_Overlay.pm262
-rw-r--r--rt/lib/RT/ObjectCustomFieldValues.pm150
-rw-r--r--rt/lib/RT/ObjectCustomFieldValues_Overlay.pm155
-rw-r--r--rt/lib/RT/ObjectCustomField_Overlay.pm103
-rw-r--r--rt/lib/RT/ObjectCustomFields.pm150
-rw-r--r--rt/lib/RT/ObjectCustomFields_Overlay.pm115
-rw-r--r--rt/lib/RT/Principal.pm236
-rw-r--r--rt/lib/RT/Principal_Overlay.pm596
-rw-r--r--rt/lib/RT/Principals.pm139
-rw-r--r--rt/lib/RT/Principals_Overlay.pm79
-rwxr-xr-xrt/lib/RT/Queue.pm395
-rw-r--r--rt/lib/RT/Queue_Overlay.pm1137
-rwxr-xr-xrt/lib/RT/Queues.pm139
-rw-r--r--rt/lib/RT/Queues_Overlay.pm144
-rwxr-xr-xrt/lib/RT/Record.pm1941
-rw-r--r--rt/lib/RT/Reminders.pm167
-rw-r--r--rt/lib/RT/Report/Tickets.pm451
-rw-r--r--rt/lib/RT/Report/Tickets/Entry.pm55
-rw-r--r--rt/lib/RT/SavedSearch.pm348
-rw-r--r--rt/lib/RT/SavedSearches.pm190
-rwxr-xr-xrt/lib/RT/Scrip.pm524
-rwxr-xr-xrt/lib/RT/ScripAction.pm303
-rw-r--r--rt/lib/RT/ScripAction_Overlay.pm285
-rwxr-xr-xrt/lib/RT/ScripActions.pm139
-rw-r--r--rt/lib/RT/ScripActions_Overlay.pm114
-rwxr-xr-xrt/lib/RT/ScripCondition.pm326
-rw-r--r--rt/lib/RT/ScripCondition_Overlay.pm238
-rwxr-xr-xrt/lib/RT/ScripConditions.pm139
-rw-r--r--rt/lib/RT/ScripConditions_Overlay.pm114
-rw-r--r--rt/lib/RT/Scrip_Overlay.pm618
-rwxr-xr-xrt/lib/RT/Scrips.pm139
-rw-r--r--rt/lib/RT/Scrips_Overlay.pm371
-rw-r--r--rt/lib/RT/Search/ActiveTicketsInQueue.pm102
-rw-r--r--rt/lib/RT/Search/FromSQL.pm110
-rw-r--r--rt/lib/RT/Search/Generic.pm152
-rw-r--r--rt/lib/RT/Search/Googleish.pm188
-rw-r--r--rt/lib/RT/SearchBuilder.pm389
-rw-r--r--rt/lib/RT/StyleGuide.pod920
-rw-r--r--rt/lib/RT/System.pm190
-rwxr-xr-xrt/lib/RT/Template.pm387
-rw-r--r--rt/lib/RT/Template_Overlay.pm422
-rwxr-xr-xrt/lib/RT/Templates.pm139
-rw-r--r--rt/lib/RT/Templates_Overlay.pm203
-rwxr-xr-xrt/lib/RT/Ticket.pm686
-rw-r--r--rt/lib/RT/Ticket_Overlay.pm3914
-rwxr-xr-xrt/lib/RT/Tickets.pm139
-rw-r--r--rt/lib/RT/Tickets_Overlay.pm3053
-rw-r--r--rt/lib/RT/Tickets_Overlay_SQL.pm587
-rwxr-xr-xrt/lib/RT/Transaction.pm442
-rw-r--r--rt/lib/RT/Transaction_Overlay.pm1134
-rwxr-xr-xrt/lib/RT/Transactions.pm139
-rw-r--r--rt/lib/RT/Transactions_Overlay.pm187
-rw-r--r--rt/lib/RT/URI.pm283
-rw-r--r--rt/lib/RT/URI/base.pm149
-rw-r--r--rt/lib/RT/URI/freeside.pm285
-rw-r--r--rt/lib/RT/URI/freeside/Internal.pm145
-rw-r--r--rt/lib/RT/URI/freeside/XMLRPC.pm122
-rw-r--r--rt/lib/RT/URI/fsck_com_rt.pm270
-rw-r--r--rt/lib/RT/URI/t.pm130
-rwxr-xr-xrt/lib/RT/User.pm878
-rw-r--r--rt/lib/RT/User_Overlay.pm2205
-rwxr-xr-xrt/lib/RT/Users.pm139
-rw-r--r--rt/lib/RT/Users_Overlay.pm677
-rwxr-xr-xrt/lib/RTx/Statistics.pm239
-rw-r--r--rt/lib/RTx/WebCronTool.pm41
-rw-r--r--rt/lib/t/00smoke.t13
-rw-r--r--rt/lib/t/01harness.t12
-rw-r--r--rt/lib/t/02regression.t47
-rw-r--r--rt/lib/t/03web.pl170
-rw-r--r--rt/lib/t/04_send_email.pl506
-rw-r--r--rt/lib/t/create_data.pl136
-rw-r--r--rt/lib/t/data/8859-15-message-series/dir356
-rw-r--r--rt/lib/t/data/8859-15-message-series/msg136
-rw-r--r--rt/lib/t/data/8859-15-message-series/msg236
-rw-r--r--rt/lib/t/data/8859-15-message-series/msg335
-rw-r--r--rt/lib/t/data/8859-15-message-series/msg435
-rw-r--r--rt/lib/t/data/8859-15-message-series/msg535
-rw-r--r--rt/lib/t/data/8859-15-message-series/msg635
-rw-r--r--rt/lib/t/data/8859-15-message-series/msg736
-rw-r--r--rt/lib/t/data/crashes-file-based-parser193
-rw-r--r--rt/lib/t/data/lorem-ipsum5
-rw-r--r--rt/lib/t/data/multipart-alternative-with-umlaut62
-rw-r--r--rt/lib/t/data/multipart-report66
-rw-r--r--rt/lib/t/data/nested-mime-sample396
-rw-r--r--rt/lib/t/data/nested-rfc-822253
-rw-r--r--rt/lib/t/data/new-ticket-from-iso-8859-131
-rw-r--r--rt/lib/t/data/new-ticket-from-iso-8859-1-full38
-rw-r--r--rt/lib/t/data/notes-uuencoded2368
-rw-r--r--rt/lib/t/data/rt-send-cc5
-rw-r--r--rt/lib/t/data/russian-subject-no-content-type42
-rw-r--r--rt/lib/t/data/subject-with-folding-ws10
-rw-r--r--rt/lib/t/data/text-html-in-russian87
-rw-r--r--rt/lib/t/data/text-html-with-umlaut35
-rw-r--r--rt/lib/t/data/very-long-subject12
-rw-r--r--rt/lib/t/regression/00-mason-syntax.t47
-rw-r--r--rt/lib/t/regression/01ticket_link_searching.t159
-rw-r--r--rt/lib/t/regression/02basic_web.t159
-rw-r--r--rt/lib/t/regression/03web_compiliation_errors.t64
-rw-r--r--rt/lib/t/regression/04send_email.t549
-rw-r--r--rt/lib/t/regression/05cronsupport.t91
-rw-r--r--rt/lib/t/regression/06-mime_decoding.t64
-rw-r--r--rt/lib/t/regression/06mailgateway.t663
-rw-r--r--rt/lib/t/regression/07acl.t138
-rw-r--r--rt/lib/t/regression/07rights.t140
-rw-r--r--rt/lib/t/regression/08web_cf_access.t119
-rw-r--r--rt/lib/t/regression/09record_cf_api.t204
-rw-r--r--rt/lib/t/regression/10merge.t72
-rw-r--r--rt/lib/t/regression/11-template-insert.t27
-rw-r--r--rt/lib/t/regression/12-search.t266
-rw-r--r--rt/lib/t/regression/13-attribute-tests.t87
-rw-r--r--rt/lib/t/regression/14linking.t243
-rw-r--r--rt/lib/t/regression/14merge.t31
-rw-r--r--rt/lib/t/regression/15cf_combo_cascade.t49
-rw-r--r--rt/lib/t/regression/15cf_pattern.t54
-rw-r--r--rt/lib/t/regression/15cf_single_values_are_single.t39
-rw-r--r--rt/lib/t/regression/16-transaction_cf_tests.t61
-rw-r--r--rt/lib/t/regression/17custom_search.t88
-rw-r--r--rt/lib/t/regression/17multiple_deleg_revocation.t135
-rw-r--r--rt/lib/t/regression/18custom_frontpage.t75
-rw-r--r--rt/lib/t/regression/18stale_delegations_cleanup.t458
-rw-r--r--rt/lib/t/regression/19-rtname.t38
-rw-r--r--rt/lib/t/regression/19quicksearch.t39
-rw-r--r--rt/lib/t/regression/20-sort-by-requestor.t143
-rw-r--r--rt/lib/t/regression/20savedsearch.t180
-rw-r--r--rt/lib/t/regression/21query-builder.t247
-rw-r--r--rt/lib/t/regression/22search_tix_by_txn.t38
-rw-r--r--rt/lib/t/regression/22search_tix_by_watcher.t228
-rw-r--r--rt/lib/t/regression/23-batch-upload-csv.t47
-rw-r--r--rt/lib/t/regression/23-web_attachments.t60
-rw-r--r--rt/lib/t/regression/23cfsort.t192
-rw-r--r--rt/lib/t/regression/24pawsort.t104
-rw-r--r--rt/lib/t/regression/25scrip_order.t57
-rw-r--r--rt/lib/t/regression/26command_line.t445
-rw-r--r--rt/lib/t/regression/27verp.t9
-rw-r--r--rt/lib/t/regression/mime_tests19
-rw-r--r--rt/lib/t/setup_regression.t34
-rw-r--r--rt/m4/rt_enable_layout.m436
-rw-r--r--rt/m4/rt_expand_var.m418
-rw-r--r--rt/m4/rt_layout.m475
-rw-r--r--rt/m4/rt_subst_expanded_arg.m414
-rw-r--r--rt/sbin/extract-message-catalog274
-rw-r--r--rt/sbin/extract_pod_tests159
-rw-r--r--rt/sbin/factory515
-rw-r--r--rt/sbin/license_tag243
-rw-r--r--rt/sbin/regression_harness56
-rwxr-xr-xrt/sbin/rt-dump-database.in173
-rw-r--r--rt/sbin/rt-setup-database.in712
-rw-r--r--rt/sbin/rt-test-dependencies.in479
-rw-r--r--site_perl/Bill.pm44
-rw-r--r--site_perl/CGI.pm143
-rw-r--r--site_perl/Conf.pm113
-rw-r--r--site_perl/Invoice.pm45
-rw-r--r--site_perl/Record.pm868
-rw-r--r--site_perl/SSH.pm157
-rw-r--r--site_perl/UID.pm209
-rw-r--r--site_perl/agent.pm166
-rw-r--r--site_perl/agent_type.pm161
-rw-r--r--site_perl/cust_bill.pm495
-rw-r--r--site_perl/cust_bill_pkg.pm177
-rw-r--r--site_perl/cust_credit.pm199
-rw-r--r--site_perl/cust_main.pm868
-rw-r--r--site_perl/cust_main_county.pm161
-rw-r--r--site_perl/cust_pay.pm235
-rw-r--r--site_perl/cust_pkg.pm507
-rw-r--r--site_perl/cust_refund.pm233
-rw-r--r--site_perl/cust_svc.pm168
-rw-r--r--site_perl/dbdef.pm174
-rw-r--r--site_perl/dbdef_colgroup.pm107
-rw-r--r--site_perl/dbdef_column.pm175
-rw-r--r--site_perl/dbdef_index.pm43
-rw-r--r--site_perl/dbdef_table.pm249
-rw-r--r--site_perl/dbdef_unique.pm44
-rw-r--r--site_perl/part_pkg.pm168
-rw-r--r--site_perl/part_referral.pm155
-rw-r--r--site_perl/part_svc.pm199
-rw-r--r--site_perl/pkg_svc.pm168
-rw-r--r--site_perl/svc_acct.pm557
-rw-r--r--site_perl/svc_acct_pop.pm163
-rw-r--r--site_perl/svc_acct_sm.pm350
-rw-r--r--site_perl/svc_domain.pm539
-rw-r--r--site_perl/table_template-svc.pm107
-rw-r--r--site_perl/table_template-unique.pm66
-rw-r--r--site_perl/table_template.pm66
-rw-r--r--site_perl/type_pkgs.pm150
-rwxr-xr-xtest/cgi-test558
-rwxr-xr-xtest/dup-test32
2755 files changed, 523174 insertions, 20843 deletions
diff --git a/AGPL b/AGPL
new file mode 100644
index 0000000..939a6f4
--- /dev/null
+++ b/AGPL
@@ -0,0 +1,662 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license
+for software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are
+designed to take away your freedom to share and change the works. By
+contrast, our General Public Licenses are intended to guarantee your
+freedom to share and change all versions of a program--to make sure it
+remains free software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public
+License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further restriction,
+you may remove that term. If a license document contains a further
+restriction but permits relicensing or conveying under this License, you
+may add to a covered work material governed by the terms of that license
+document, provided that the further restriction does not survive such
+relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have permission
+to link or combine any covered work with a work licensed under version 3
+of the GNU General Public License into a single combined work, and to
+convey the resulting work. The terms of this License will continue to
+apply to the part which is the covered work, but the work with which it is
+combined will remain governed by version 3 of the GNU General Public
+License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may differ
+in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero
+General Public License "or any later version" applies to it, you have
+the option of following the terms and conditions either of that
+numbered version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number
+of the GNU Affero General Public License, you may choose any version
+ever published by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that
+proxy's public statement of acceptance of a version permanently
+authorizes you to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/Artistic b/Artistic
deleted file mode 100644
index 4ffc78e..0000000
--- a/Artistic
+++ /dev/null
@@ -1,125 +0,0 @@
- The "Artistic License"
-
- Preamble
-
-The intent of this document is to state the conditions under which a
-Package may be copied, such that the Copyright Holder maintains some
-semblance of artistic control over the development of the Package,
-while giving the users of the package the right to use and distribute
-the Package in a more-or-less customary fashion, plus the right to make
-reasonable modifications.
-
-It also grants you the rights to reuse parts of a Package in your own
-programs without transferring this License to those programs, provided
-that you meet some reasonable requirements.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the
- Copyright Holder, and derivatives of that collection of files
- created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been
- modified, or has been modified in accordance with the wishes
- of the Copyright Holder as specified below.
-
- "Copyright Holder" is whoever is named in the copyright or
- copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing
- this Package.
-
- "Reasonable copying fee" is whatever you can justify on the
- basis of media cost, duplication charges, time of people involved,
- and so on. (You will not be required to justify it to the
- Copyright Holder, but only to the computing community at large
- as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item
- itself, though there may be fees involved in handling the item.
- It also means that recipients of the item may redistribute it
- under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the
-Standard Version of this Package without restriction, provided that you
-duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications
-derived from the Public Domain or from the Copyright Holder. A Package
-modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided
-that you insert a prominent notice in each changed file stating how and
-when you changed that file, and provided that you do at least ONE of the
-following:
-
- a) place your modifications in the Public Domain or otherwise make them
- Freely Available, such as by posting said modifications to Usenet or
- an equivalent medium, or placing the modifications on a major archive
- site such as uunet.uu.net, or by allowing the Copyright Holder to include
- your modifications in the Standard Version of the Package.
-
- b) use the modified Package only within your corporation or organization.
-
- c) rename any non-standard executables so the names do not conflict
- with standard executables, which must also be provided, and provide
- a separate manual page for each non-standard executable that clearly
- documents how it differs from the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or
-executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files,
- together with instructions (in the manual page or equivalent) on where
- to get the Standard Version.
-
- b) accompany the distribution with the machine-readable source of
- the Package with your modifications.
-
- c) give non-standard executables non-standard names, and clearly
- document the differences in manual pages (or equivalent), together
- with instructions on where to get the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this
-Package. You may charge any fee you choose for support of this
-Package. You may not charge a fee for this Package itself. However,
-you may distribute this Package in aggregate with other (possibly
-commercial) programs as part of a larger (possibly commercial) software
-distribution provided that you do not advertise this Package as a
-product of your own.
-
-6. The scripts and library files supplied as input to or produced as
-output from the programs of this Package do not automatically fall
-under the copyright of this Package, but belong to whomever generated
-them, and may be sold commercially, and may be aggregated with this
-Package. If such scripts or library files are aggregated with this
-Package via the so-called "undump" or "unexec" methods of producing a
-binary executable image, then distribution of such an image shall
-neither be construed as a distribution of this Package nor shall it
-fall under the restrictions of Paragraphs 3 and 4, provided that you do
-not represent such an executable image as a Standard Version of this
-Package.
-
-7. You may reuse parts of this Package in your own programs, provided that
-you explicitly state where you got them from, in the source code (and, left
-to your courtesy, in the documentation), duplicating all the associated
-copyright notices and disclaimers. Besides your changes, if any, must be
-clearly marked as such. Parts reused that way will no longer fall under this
-license if, and only if, the name of your program(s) have no immediate
-connection with the name of the Package itself or its associated programs.
-You may then apply whatever restrictions you wish on the reused parts or
-choose to place them in the Public Domain--this will apply only within the
-context of your package.
-
-8. The name of the Copyright Holder may not be used to endorse or promote
-products derived from this software without specific prior written permission.
-
-9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
- The End
diff --git a/CREDITS b/CREDITS
index 87c79a7..2702fe8 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1,14 +1 @@
-Thanks to Matt Simerson <matt@michweb.net> of MichWeb Inc. for documentation
-and pre-release testing. Without his help the documentation in the first
-release would have consisted of a single screenfull of text.
-
-# Steve Cleff <cleff@yahoo.com> did the default background image and is also
-# the creator of Freeside's mascot, Snakeman.
-
-Jerry St. Pierre <jstpi@city.timmins.on.ca> did the "SISD" graphic.
-
-Brian McCane? <bmccane@maxbaud.net> contributed PostgreSQL support, HTML
-style enhancements and many, many bugfixes.
-
-Everything else is my (Ivan Kohler <ivan@sisd.com>) fault.
-
+See httemplate/docs/credits.html
diff --git a/FS/Changes b/FS/Changes
new file mode 100644
index 0000000..c94ef10
--- /dev/null
+++ b/FS/Changes
@@ -0,0 +1,5 @@
+Revision history for Perl extension FS.
+
+0.01 Wed Aug 4 00:13:45 1999
+ - original version; created by h2xs 1.19
+
diff --git a/FS/FS.pm b/FS/FS.pm
new file mode 100644
index 0000000..c4be977
--- /dev/null
+++ b/FS/FS.pm
@@ -0,0 +1,442 @@
+package FS;
+
+use strict;
+use vars qw($VERSION);
+
+$VERSION = '%%%VERSION%%%';
+
+#find missing entries in this file with:
+# for a in `ls *pm | cut -d. -f1`; do grep 'L<FS::'$a'>' ../FS.pm >/dev/null || echo "missing $a" ; done
+
+1;
+__END__
+
+=head1 NAME
+
+FS - Freeside Perl modules
+
+=head1 SYNOPSIS
+
+Freeside perl modules and CLI utilities.
+
+=head2 Utility classes
+
+L<FS::Schema> - Freeside database schema
+
+L<FS::Setup> - Setup subroutines
+
+L<FS::Upgrade> - Upgrade subroutines
+
+L<FS::Conf> - Freeside configuration values
+
+L<FS::ConfItem> - Freeside configuration option meta-data.
+
+L<FS::ConfDefaults> - Freeside configuration default and available values
+
+L<FS::UID> - User class (not yet OO)
+
+L<FS::CurrentUser> - Package representing the current user
+
+L<FS::CGI> - Non OO-subroutines for the web interface.
+
+L<FS::Msgcat> - Message catalog
+
+L<FS::SearchCache> - Search cache
+
+L<FS::AccessRight> - Access control rights.
+
+L<FS::Report> - Report data objects
+
+L<FS::Report::Table> - Report data objects
+
+L<FS::Report::Table::Monthly> - Report data objects
+
+L<FS::XMLRPC> - Backend XML::RPC server
+
+L<FS::Misc> - Miscellaneous subroutines
+
+L<FS::payby> - Payment types
+
+L<FS::ClientAPI_SessionCache> - ClientAPI session cache
+
+L<FS::Pony> - A pony
+
+L<FS::cust_main::Import> - Batch customer importing
+
+=head2 Database record classes
+
+L<FS::Record> - Database record base class
+
+L<FS::m2m_Common> - Mixin class for classes in a many-to-many relationship
+
+L<FS::m2name_Common> - Base class for tables with a related table listing names
+
+L<FS::option_Common> - Base class for option sub-classes
+
+L<FS::conf> - Configuration value class
+
+L<FS::payinfo_Mixin> - Mixin class for records in tables that contain payinfo.
+
+L<FS::access_user> - Employees / internal users
+
+L<FS::access_user_pref> - Employee preferences
+
+L<FS::access_group> - Employee groups
+
+L<FS::access_usergroup> - Employee group membership
+
+L<FS::access_groupagent> - Group reseller access
+
+L<FS::access_right> - Access rights
+
+L<FS::svc_acct_pop> - POP (Point of Presence, not Post
+Office Protocol) class
+
+L<FS::part_pop_local> - Local calling area class
+
+L<FS::part_referral> - Referral class
+
+L<FS::pkg_referral> - Package referral class
+
+L<FS::cust_main_county> - Locale (tax rate) class
+
+L<FS::cust_tax_exempt> - Tax exemption record class
+
+L<FS::cust_tax_exempt_pkg> - Line-item specific tax exemption record class
+
+L<FS::svc_Common> - Service base class
+
+L<FS::svc_Parent_Mixin> - Mixin class for svc_ classes with a parent_svcnum field
+
+L<FS::svc_acct> - Account (shell, RADIUS, POP3) class
+
+L<FS::acct_snarf> - External mail account class
+
+L<FS::acct_rt_transaction> - Time worked application to account class
+
+L<FS::radius_usergroup> - RADIUS groups
+
+L<FS::svc_domain> - Domain class
+
+L<FS::domain_record> - DNS zone entries
+
+L<FS::registrar> - Domain registrar class
+
+L<FS::svc_forward> - Mail forwarding class
+
+L<FS::svc_www> - Web virtual host class.
+
+L<FS::svc_broadband> - DSL, wireless and other broadband class.
+
+L<FS::addr_block> - Address block class
+
+L<FS::router> - Router class
+
+L<FS::part_virtual_field> - Broadband virtual field class
+
+L<FS::svc_phone> - Phone service class
+
+L<FS::phone_avail> - Phone number availability cache
+
+L<FS::cdr> - Call Detail Record class
+
+L<FS::cdr_calltype> - CDR calltype class
+
+L<FS::cdr_carrier> - CDR carrier class
+
+L<FS::cdr_upstream_rate> - CDR upstream rate class
+
+L<FS::cdr_type> - CDR type class
+
+L<FS::svc_external> - Externally tracked service class.
+
+L<FS::inventory_class> - Inventory classes
+
+L<FS::inventory_item> - Inventory items
+
+L<FS::part_svc> - Service definition class
+
+L<FS::part_svc_column> - Column constraint class
+
+L<FS::export_svc> - Class linking service definitions (see L<FS::part_svc>)
+with exports (see L<FS::part_export>)
+
+L<FS::part_export> - External provisioning export class
+
+L<FS::part_export_option> - Export option class
+
+L<FS::pkg_category> - Package category class
+
+L<FS::pkg_class> - Package class class
+
+L<FS::part_pkg> - Package definition class
+
+L<FS::part_pkg_link> - Package definition link class
+
+L<FS::part_pkg_taxclass> - Tax class class
+
+L<FS::part_pkg_option> - Package definition option class
+
+L<FS::pkg_svc> - Class linking package definitions (see L<FS::part_pkg>) with
+service definitions (see L<FS::part_svc>)
+
+L<FS::reg_code> - One-time registration codes
+
+L<FS::reg_code_pkg> - Class linking registration codes (see L<FS::reg_code>) with package definitions (see L<FS::part_pkg>)
+
+L<FS::rate> - Rate plans for call billing
+
+L<FS::rate_region> - Rate regions for call billing
+
+L<FS::rate_prefix> - Rate region prefixes for call billing
+
+L<FS::rate_detail> - Rate plan detail for call billing
+
+L<FS::usage_class> - Usage class class
+
+L<FS::agent> - Agent (reseller) class
+
+L<FS::agent_type> - Agent type class
+
+L<FS::type_pkgs> - Class linking agent types (see L<FS::agent_type>) with package definitions (see L<FS::part_pkg>)
+
+L<FS::payment_gateway> - Payment gateway class
+
+L<FS::payment_gateway_option> - Payment gateway option class
+
+L<FS::agent_payment_gateway> - Agent payment gateway class
+
+L<FS::cust_svc> - Service class
+
+L<FS::cust_pkg> - Customer package class
+
+L<FS::cust_pkg_option> - Customer package option class
+
+L<FS::cust_pkg_detail> - Customer package details class
+
+L<FS::reason_type> - Reason type class
+
+L<FS::reason> - Reason class
+
+L<FS::cust_pkg_reason> - Package reason class
+
+L<FS::cust_main> - Customer class
+
+L<FS::cust_main_location> - Customer location class
+
+L<FS::cust_main_Mixin> - Mixin class for records that contain fields from cust_main
+
+L<FS::cust_main_invoice> - Invoice destination class
+
+L<FS::cust_main_note> - Customer note class
+
+L<FS::banned_pay> - Banned payment information class
+
+L<FS::cust_bill> - Invoice class
+
+L<FS::cust_bill_pkg> - Invoice line item class
+
+L<FS::cust_bill_pkg_detail> - Invoice line item detail class
+
+L<FS::part_bill_event> - (Old) Invoice event definition class
+
+L<FS::cust_bill_event> - (Old) Completed invoice event class
+
+L<FS::part_event> - (New) Billing event definition class
+
+L<FS::part_event_option> - (New) Billing event option class
+
+L<FS::part_event::Condition> - (New) Billing event condition base class
+
+L<FS::part_event::Action> - (New) Billing event action base class
+
+L<FS::part_event_condition> - (New) Billing event condition class
+
+L<FS::part_event_condition_option> - (New) Billing event condition option class
+
+L<FS::part_event_condition_option_option> - (New) Billing event condition compound option class
+
+L<FS::cust_event> - (New) Customer event class
+
+L<FS::cust_bill_ApplicationCommon> - Base class for bill application classes
+
+L<FS::cust_pay> - Payment class
+
+L<FS::cust_pay_pending> - Pending payment class
+
+L<FS::cust_pay_void> - Voided payment class
+
+L<FS::cust_bill_pay> - Payment application class
+
+L<FS::cust_bill_pay_pkg> - Line-item specific payment application class
+
+L<FS::cust_bill_pay_batch> - Batch payment application class
+
+L<FS::cust_credit> - Credit class
+
+L<FS::cust_refund> - Refund class
+
+L<FS::cust_credit_refund> - Refund application to credit class
+
+L<FS::cust_credit_bill> - Credit application to invoice class
+
+L<FS::cust_credit_bill_pkg> - Line-item specific credit application to invoice class
+
+L<FS::cust_pay_refund> - Refund application to payment class
+
+L<FS::pay_batch> - Credit card transaction queue class
+
+L<FS::cust_pay_batch> - Credit card transaction member queue class
+
+L<FS::prepay_credit> - Prepaid "calling card" credit class.
+
+L<FS::nas> - Network Access Server class
+
+L<FS::port> - NAS port class
+
+L<FS::session> - User login session class
+
+L<FS::queue> - Job queue
+
+L<FS::queue_arg> - Job arguments
+
+L<FS::queue_depend> - Job dependencies
+
+L<FS::msgcat> - Message catalogs
+
+L<FS::clientapi_session>
+
+L<FS::clientapi_session_field>
+
+=head2 Historical database record classes
+
+L<FS::h_Common> - History table base class
+
+L<FS::h_cust_pay> - Historical record of customer payment changes
+
+L<FS::h_cust_credit> - Historical record of customer credit changes
+
+L<FS::h_cust_bill> - Historical record of customer tax changes (old-style)
+
+L<FS::h_cust_svc> - Object method for h_cust_svc objects
+
+L<FS::h_cust_tax_exempt> - Historical record of customer tax changes (old-style)
+
+L<FS::h_domain_record> - Historical DNS entry objects
+
+L<FS::h_svc_acct> - Historical account objects
+
+L<FS::h_svc_broadband> - Historical broadband connection objects
+
+L<FS::h_svc_domain> - Historical domain objects
+
+L<FS::h_svc_external> - Historical externally tracked service objects
+
+L<FS::h_svc_forward> - Historical mail forwarding alias objects
+
+L<FS::h_svc_phone> - Historical phone number objects
+
+L<FS::h_svc_www> - Historical web virtual host objects
+
+=head2 Remote API modules
+
+L<FS::SelfService> - Self-service API
+
+L<FS::SelfService::XMLRPC> - Self-service XML-RPC API
+
+=head2 User Interface classes
+
+L<FS::UI::Web> - Web user-interface class
+
+L<FS::UI::bytecount> - Byte counter user-interface class
+
+=head2 Command-line utilities
+
+L<freeside-adduser> - Command line interface to add (freeside) users.
+
+L<freeside-daily> - Run daily billing and collection events.
+
+L<freeside-monthly> - Run monthly billing and invoice collection events.
+
+L<freeside-dbdef-create> - Recreate database schema cache
+
+L<freeside-deluser> - Command line interface to delete (freeside) users.
+
+L<freeside-expiration-alerter> - Emails notifications of credit card expirations.
+
+L<freeside-email> - Prints email addresses of all users on STDOUT
+
+L<freeside-fetch> - Send a freeside page to a list of employees.
+
+L<freeside-prepaidd> - Real-time daemon for prepaid packages
+
+L<freeside-prune-applications> - Removes stray applications of credit, payment to bills, refunds, etc.
+
+L<freeside-queued> - Job queue daemon
+
+L<freeside-radgroup> - Command line utility to manipulate radius groups
+
+L<freeside-reexport> - Command line tool to re-trigger export jobs for existing services
+
+L<freeside-reset-fixed> - Command line tool to set the fixed columns for existing services
+
+L<freeside-sqlradius-dedup-group> - Command line tool to eliminate duplicate usergroup entries from radius tables
+
+L<freeside-sqlradius-radacctd> - Real-time radacct import daemon
+
+L<freeside-sqlradius-reset> - Command line interface to reset and recreate RADIUS SQL tables
+
+L<freeside-sqlradius-seconds> - Command line time-online tool
+
+L<freeside-upgrade> - Upgrades database schema for new freeside verisons.
+
+=head1 Notes
+
+To quote perl(1), "If you're intending to read these straight through for the
+first time, the suggested order will tend to reduce the number of forward
+references."
+
+If you've never used OO modules before,
+http://www.perl.com/doc/FMTEYEWTK/easy_objects.html might help you out.
+
+=head1 DESCRIPTION
+
+Freeside is a billing and administration package for wired and wireless ISPs,
+VoIP, hosting, service and content providers and other online businesses.
+
+The Freeside home page is at <http://www.sisd.com/freeside>.
+
+The main documentation is at <http://www.sisd.com/mediawiki>.
+
+=head1 SUPPORT
+
+A mailing list for users is available. Send a blank message to
+<freeside-users-subscribe@sisd.com> to subscribe.
+
+A mailing list for developers is available. It is intended to be lower volume
+and higher SNR than the users list. Send a blank message to
+<freeside-devel-subscribe@sisd.com> to subscribe.
+
+Commercial support is available; see
+<http://www.sisd.com/freeside/commercial.html>.
+
+=head1 AUTHORS
+
+Primarily Ivan Kohler, with help from many kind folks, including core
+contributors Jeff Finucane, Kristian Hoffman, Jason Hall and Peter Bowen.
+
+See the CREDITS file in the Freeside distribution for a (hopefully) complete
+list and the individal files for details.
+
+=head1 SEE ALSO
+
+perl(1), main Freeside documentation at <http://www.sisd.com/mediawiki/>
+
+=head1 BUGS
+
+Those modules which would be useful separately should be pulled out,
+renamed appropriately and uploaded to CPAN. So far: DBIx::DBSchema, Net::SSH
+and Net::SCP...
+
+=cut
+
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
new file mode 100644
index 0000000..93660e2
--- /dev/null
+++ b/FS/FS/AccessRight.pm
@@ -0,0 +1,314 @@
+package FS::AccessRight;
+
+use strict;
+use vars qw(@rights); # %rights);
+use Tie::IxHash;
+
+=head1 NAME
+
+FS::AccessRight - Access control rights.
+
+=head1 SYNOPSIS
+
+ use FS::AccessRight;
+
+ my @rights = FS::AccessRight->rights;
+
+ #my %rights = FS::AccessRight->rights_categorized;
+ tie my %rights, 'Tie::IxHash', FS::AccessRight->rights_categorized;
+ foreach my $category ( keys %rights ) {
+ my @category_rights = @{ $rights{$category} };
+ }
+
+=head1 DESCRIPTION
+
+Access control rights - Permission to perform specific actions that can be
+assigned to users and/or groups.
+
+=cut
+
+#@rights = (
+# 'Reports' => [
+# '_desc' => 'Access to high-level reporting',
+# ],
+# 'Configuration' => [
+# '_desc' => 'Access to configuration',
+#
+# 'Settings' => {},
+#
+# 'agent' => [
+# '_desc' => 'Master access to reseller configuration',
+# 'agent_type' => {},
+# 'agent' => {},
+# ],
+#
+# 'export_svc_pkg' => [
+# '_desc' => 'Access to export, service and package configuration',
+# 'part_export' => {},
+# 'part_svc' => {},
+# 'part_pkg' => {},
+# 'pkg_class' => {},
+# ],
+#
+# 'billing' => [
+# '_desc' => 'Access to billing configuration',
+# 'payment_gateway' => {},
+# 'part_bill_event' => {},
+# 'prepay_credit' => {},
+# 'rate' => {},
+# 'cust_main_county' => {},
+# ],
+#
+# 'dialup' => [
+# '_desc' => 'Access to dialup configuraiton',
+# 'svc_acct_pop' => {},
+# ],
+#
+# 'broadband' => [
+# '_desc' => 'Access to broadband configuration',
+# 'router' => {},
+# 'addr_block' => {},
+# ],
+#
+# 'misc' => [
+# 'part_referral' => {},
+# 'part_virtual_field' => {},
+# 'msgcat' => {},
+# 'inventory_class' => {},
+# ],
+#
+# },
+#
+#);
+#
+##turn it into a more hash-like structure, but ordered via IxHash
+
+#well, this is what we have for now. getting better.
+tie my %rights, 'Tie::IxHash',
+
+ ###
+ # basic customer rights
+ ###
+ 'Customer rights' => [
+ 'New customer',
+ 'View customer',
+ #'View Customer | View tickets',
+ 'Edit customer',
+ 'Cancel customer',
+ 'Complimentary customer', #aka users-allow_comp
+ { rightname=>'Delete customer', desc=>"Enable customer deletions. Be very careful! Deleting a customer will remove all traces that this customer ever existed! It should probably only be used when auditing a legacy database. Normally, you cancel all of a customer's packages if they cancel service." }, #aka. deletecustomers
+ 'Add customer note', #NEW
+ 'Edit customer note', #NEW
+ 'Bill customer now', #NEW
+ 'Bulk send customer notices', #NEW
+ ],
+
+ ###
+ # customer package rights
+ ###
+ 'Customer package rights' => [
+ 'View customer packages', #NEW
+ 'Order customer package',
+ 'One-time charge',
+ 'Change customer package',
+ 'Bulk change customer packages',
+ 'Edit customer package dates',
+ 'Customize customer package',
+ 'Suspend customer package',
+ 'Suspend customer package later',
+ 'Unsuspend customer package',
+ 'Cancel customer package immediately',
+ 'Cancel customer package later',
+ 'Delay suspension events',
+ 'Add on-the-fly cancel reason', #NEW
+ 'Add on-the-fly suspend reason', #NEW
+ 'Edit customer package invoice details', #NEW
+ 'Edit customer package comments', #NEW
+ ],
+
+ ###
+ # customer service rights
+ ###
+ 'Customer service rights' => [
+ 'View customer services', #NEW
+ 'Provision customer service',
+ 'Recharge customer service', #NEW
+ 'Unprovision customer service',
+ 'Change customer service', #NEWNEW
+ 'Edit usage', #NEW
+ 'Edit home dir', #NEW
+ 'Edit www config', #NEW
+ 'Edit domain catchall', #NEW
+ 'Edit domain nameservice', #NEW
+
+ { rightname=>'View/link unlinked services', global=>1 }, #not agent-virtualizable without more work
+ ],
+
+ ###
+ # customer invoice/financial info rights
+ ###
+ 'Customer invoice / financial info rights' => [
+ 'View invoices',
+ 'Resend invoices', #NEWNEW
+ 'View customer tax exemptions', #yow
+ 'View customer batched payments', #NEW
+ 'View customer pending payments', #NEW
+ 'Edit customer pending payments', #NEW
+ 'View customer billing events', #NEW
+ ],
+
+ ###
+ # customer payment rights
+ ###
+ 'Customer payment rights' => [
+ 'Post payment',
+ 'Post payment batch',
+ 'Apply payment', #NEWNEW
+ { rightname=>'Unapply payment', desc=>'Enable "unapplication" of unclosed payments from specific invoices.' }, #aka. unapplypayments
+ 'Process payment',
+ { rightname=>'Refund payment', desc=>'Enable refund of existing customer payments.' },
+
+ { rightname=>'Delete payment', desc=>'Enable deletion of unclosed payments. Be very careful! Only delete payments that were data-entry errors, not adjustments.' }, #aka. deletepayments Optionally specify one or more comma-separated email addresses to be notified when a payment is deleted.
+
+ ],
+
+ ###
+ # customer credit rights
+ ###
+ 'Customer credit and refund rights' => [
+ 'Post credit',
+ 'Apply credit', #NEWNEW
+ { rightname=>'Unapply credit', desc=>'Enable "unapplication" of unclosed credits.' }, #aka unapplycredits
+ { rightname=>'Delete credit', desc=>'Enable deletion of unclosed credits. Be very careful! Only delete credits that were data-entry errors, not adjustments.' }, #aka. deletecredits Optionally specify one or more comma-separated email addresses to be notified when a credit is deleted.
+ { rightname=>'Post refund', desc=>'Enable posting of check and cash refunds.' },
+# { rightname=>'Process refund', desc=>'Enable processing of generic credit card/ACH refunds (i.e. not associated with a specific prior payment).' },
+ 'Delete refund', #NEW
+ 'Add on-the-fly credit reason', #NEW
+ ],
+
+ ###
+ # customer voiding rights..
+ ###
+ 'Customer void rights' => [
+ { rightname=>'Credit card void', desc=>'Enable local-only voiding of echeck payments in addition to refunds against the payment gateway.' }, #aka. cc-void
+ { rightname=>'Echeck void', desc=>'Enable local-only voiding of echeck payments in addition to refunds against the payment gateway.' }, #aka. echeck-void
+ 'Regular void',
+ { rightname=>'Unvoid', desc=>'Enable unvoiding of voided payments' }, #aka. unvoid
+
+
+ ],
+
+ ###
+ # report/listing rights...
+ ###
+ 'Reporting/listing rights' => [
+ 'List customers',
+ 'List zip codes', #NEW
+ 'List invoices',
+ 'List packages',
+ 'List services',
+
+ { rightname=> 'List rating data', desc=>'Usage reports', global=>1 },
+ 'Billing event reports',
+ 'Financial reports',
+ ],
+
+ ###
+ # misc rights
+ ###
+ 'Miscellaneous rights' => [
+ { rightname=>'Job queue', global=>1 },
+ { rightname=>'Time queue', global=>1 },
+ { rightname=>'Process batches', global=>1 },
+ { rightname=>'Reprocess batches', global=>1 },
+ { rightname=>'Import', global=>1 }, #some of these are ag-virt'ed now? give em their own ACLs
+ { rightname=>'Export', global=>1 },
+ { rightname=> 'Edit rating data', desc=>'Delete CDRs', global=>1 },
+ #],
+ #
+ ###
+ # misc misc rights
+ ###
+ #'Database access rights' => [
+ { rightname=>'Raw SQL', global=>1 }, #NEW
+ ],
+
+ ###
+ # setup/config rights
+ ###
+ 'Configuration rights' => [
+ 'Edit advertising sources',
+ { rightname=>'Edit global advertising sources', global=>1 },
+
+ 'Edit package definitions',
+ { rightname=>'Edit global package definitions', global=>1 },
+
+ 'Edit billing events',
+ { rightname=>'Edit global billing events', global=>1 },
+
+ { rightname=>'Dialup configuration' },
+ { rightname=>'Dialup global configuration', global=>1 },
+
+ { rightname=>'Broadband configuration' },
+ { rightname=>'Broadband global configuration', global=>1 },
+
+ { rightname=>'Configuration', global=>1 }, #most of the rest of the configuraiton is not agent-virtualized
+
+ { rightname=>'Configuration download', }, #description of how it affects
+ #search/elements/search.html
+
+ ],
+
+;
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item rights
+
+Returns a list of right names.
+
+=cut
+
+ sub rights {
+ #my $class = shift;
+ map { ref($_) ? $_->{'rightname'} : $_ } map @{ $rights{$_} }, keys %rights;
+ }
+
+=item rights_info
+
+Returns a list of key-value pairs suitable for assigning to a hash. Keys are
+category names and values are list references of rights. Each element of the
+list reference scalar right name or a hashref with the following keys:
+
+=over 4
+
+=item rightname - Right name
+
+=item desc - Extended right description
+
+=item global - Global flag, indicates that this access right provides access to global data which is shared among all agents.
+
+=back
+
+=cut
+
+sub rights_info {
+ %rights;
+}
+
+=back
+
+=head1 BUGS
+
+Damn those infernal six-legged creatures!
+
+=head1 SEE ALSO
+
+L<FS::access_right>, L<FS::access_group>, L<FS::access_user>
+
+=cut
+
+1;
+
diff --git a/FS/FS/CGI.pm b/FS/FS/CGI.pm
new file mode 100644
index 0000000..7ad1dc2
--- /dev/null
+++ b/FS/FS/CGI.pm
@@ -0,0 +1,327 @@
+package FS::CGI;
+
+use strict;
+use vars qw(@EXPORT_OK @ISA);
+use Exporter;
+use CGI;
+use URI::URL;
+#use CGI::Carp qw(fatalsToBrowser);
+use FS::UID;
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw( header menubar idiot eidiot popurl rooturl table itable ntable
+ myexit http_header);
+
+=head1 NAME
+
+FS::CGI - Subroutines for the web interface
+
+=head1 SYNOPSIS
+
+ use FS::CGI qw(header menubar idiot eidiot popurl);
+
+ print header( 'Title', '' );
+ print header( 'Title', menubar('item', 'URL', ... ) );
+
+ idiot "error message";
+ eidiot "error message";
+
+ $url = popurl; #returns current url
+ $url = popurl(3); #three levels up
+
+=head1 DESCRIPTION
+
+Provides a few common subroutines for the web interface.
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item header TITLE, MENUBAR
+
+Returns an HTML header.
+
+=cut
+
+sub header {
+ use Carp;
+ carp 'FS::CGI::header deprecated; include /elements/header.html instead';
+
+ my($title,$menubar,$etc)=@_; #$etc is for things like onLoad= etc.
+ $etc = '' unless defined $etc;
+
+ my $x = <<END;
+ <HTML>
+ <HEAD>
+ <TITLE>
+ $title
+ </TITLE>
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8"$etc>
+ <FONT SIZE=6>
+ <CENTER>$title</CENTER>
+ </FONT>
+ <BR><!--<BR>-->
+END
+ $x .= $menubar. "<BR><BR>" if $menubar;
+ $x;
+}
+
+=item http_header
+
+Sets an http header.
+
+=cut
+
+sub http_header {
+ my ( $header, $value ) = @_;
+ if (exists $ENV{MOD_PERL}) {
+ if ( defined $HTML::Mason::Commands::r ) { #Mason
+ ## is this the correct pacakge for $r ??? for 1.0x and 1.1x ?
+ if ( $header =~ /^Content-Type$/ ) {
+ $HTML::Mason::Commands::r->content_type($value);
+ } else {
+ $HTML::Mason::Commands::r->header_out( $header => $value );
+ }
+ } else {
+ die "http_header called in unknown environment";
+ }
+ } else {
+ die "http_header called not running under mod_perl";
+ }
+
+}
+
+=item menubar ITEM, URL, ...
+
+Returns an HTML menubar.
+
+=cut
+
+sub menubar { #$menubar=menubar('Main Menu', '../', 'Item', 'url', ... );
+ use Carp;
+ carp 'FS::CGI::menubar deprecated; include /elements/menubar.html instead';
+
+ my($item,$url,@html);
+ while (@_) {
+ ($item,$url)=splice(@_,0,2);
+ next if $item =~ /^\s*Main\s+Menu\s*$/i;
+ push @html, qq!<A HREF="$url">$item</A>!;
+ }
+ join(' | ',@html);
+}
+
+=item idiot ERROR
+
+This is depriciated. Don't use it.
+
+Sends an HTML error message.
+
+=cut
+
+sub idiot {
+ #warn "idiot depriciated";
+ my($error)=@_;
+# my $cgi = &FS::UID::cgi();
+# if ( $cgi->isa('CGI::Base') ) {
+# no strict 'subs';
+# &CGI::Base::SendHeaders;
+# } else {
+# print $cgi->header( @FS::CGI::header );
+# }
+ print <<END;
+<HTML>
+ <HEAD>
+ <TITLE>Error processing your request</TITLE>
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+ </HEAD>
+ <BODY>
+ <CENTER>
+ <H4>Error processing your request</H4>
+ </CENTER>
+ Your request could not be processed because of the following error:
+ <P><B>$error</B>
+ </BODY>
+</HTML>
+END
+
+}
+
+=item eidiot ERROR
+
+This is depriciated. Don't use it.
+
+Sends an HTML error message, then exits.
+
+=cut
+
+sub eidiot {
+ warn "eidiot depriciated";
+ $HTML::Mason::Commands::r->send_http_header
+ if defined $HTML::Mason::Commands::r;
+ idiot(@_);
+ &myexit();
+}
+
+=item myexit
+
+You probably shouldn't use this; but if you must:
+
+If running under mod_perl, calles Apache::exit, otherwise, calls exit.
+
+=cut
+
+sub myexit {
+ if (exists $ENV{MOD_PERL}) {
+
+ if ( defined $HTML::Mason::Commands::m ) { #Mason
+ #$HTML::Mason::Commands::m->flush_buffer();
+ $HTML::Mason::Commands::m->abort();
+ die "shouldn't fall through to here (mason \$m->abort didn't)";
+ } else {
+ #??? well, it is $ENV{MOD_PERL}
+ warn "running under unknown mod_perl environment; trying Apache::exit()";
+ require Apache;
+ Apache::exit();
+ }
+ } else {
+ exit;
+ }
+}
+
+=item popurl LEVEL [URL]
+
+Returns current (or, optionally, passed) URL with LEVEL levels of path removed
+from the end (default 0).
+
+=cut
+
+sub popurl {
+ my $up = shift;
+
+ my $url_string;
+ if ( scalar(@_) ) {
+ $url_string = shift;
+ } else {
+ my $cgi = &FS::UID::cgi;
+ $url_string = $cgi->isa('Apache') ? $cgi->uri : $cgi->url;
+ }
+
+ $url_string =~ s/\?.*//;
+ my $url = new URI::URL ( $url_string );
+ my(@path)=$url->path_components;
+ splice @path, 0-$up;
+ $url->path_components(@path);
+ my $x = $url->as_string;
+ $x .= '/' unless $x =~ /\/$/;
+ $x;
+}
+
+=item rooturl
+
+=cut
+
+sub rooturl {
+ # better to start with the client-provided URL
+ my $cgi = &FS::UID::cgi;
+ my $url_string = $cgi->isa('Apache') ? $cgi->uri : $cgi->url;
+ $url_string =~ s/\?.*//;
+
+ #even though this is kludgy
+ $url_string =~ s{ / index\.html /? $ }
+ {/}x;
+ $url_string =~
+ s{
+ /
+ (browse|config|docs|edit|graph|misc|search|view|pref|rt|elements)
+ /
+ (process/)?
+ ([\w\-\.\/]+)
+ $
+ }
+ {}x;
+
+ #elements because of progress-popup.html...
+ #XXX remove anything from elements that is called directly & prevent
+ #those pages from being served up
+
+ $url_string .= '/' unless $url_string =~ /\/$/;
+
+ $url_string;
+
+}
+
+=item table
+
+Returns HTML tag for beginning a table.
+
+=cut
+
+sub table {
+ use Carp;
+ carp 'FS::CGI::table deprecated; include /elements/table.html instead';
+
+ my $col = shift;
+ if ( $col ) {
+ qq!<TABLE BGCOLOR="$col" BORDER=1 WIDTH="100%" CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">!;
+ } else {
+ '<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">';
+ }
+}
+
+=item itable
+
+Returns HTML tag for beginning an (invisible) table.
+
+=cut
+
+sub itable {
+ my $col = shift;
+ my $cellspacing = shift || 0;
+ my $width = ( scalar(@_) && shift ) ? '' : 'WIDTH="100%"'; #bah
+ if ( $col ) {
+ qq!<TABLE BGCOLOR="$col" BORDER=0 CELLSPACING=$cellspacing $width>!;
+ } else {
+ qq!<TABLE BORDER=0 CELLSPACING=$cellspacing $width>!;
+ }
+}
+
+=item ntable
+
+This is getting silly.
+
+=cut
+
+sub ntable {
+ my $col = shift;
+ my $cellspacing = shift || 0;
+ if ( $col ) {
+ qq!<TABLE BGCOLOR="$col" BORDER=0 CELLSPACING=$cellspacing>!;
+ } else {
+ '<TABLE BORDER CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">';
+ }
+
+}
+
+=back
+
+=head1 BUGS
+
+Not OO.
+
+Not complete.
+
+=head1 SEE ALSO
+
+L<CGI>, L<CGI::Base>
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/ClientAPI.pm b/FS/FS/ClientAPI.pm
new file mode 100644
index 0000000..902f58b
--- /dev/null
+++ b/FS/FS/ClientAPI.pm
@@ -0,0 +1,37 @@
+package FS::ClientAPI;
+
+use strict;
+use vars qw(%handler $domain $DEBUG);
+
+$DEBUG = 0;
+
+%handler = ();
+
+#find modules
+foreach my $INC ( @INC ) {
+ my $glob = "$INC/FS/ClientAPI/*.pm";
+ warn "FS::ClientAPI: searching $glob" if $DEBUG;
+ foreach my $file ( glob($glob) ) {
+ $file =~ /\/(\w+)\.pm$/ or do {
+ warn "unrecognized ClientAPI file: $file";
+ next
+ };
+ my $mod = $1;
+ warn "using FS::ClientAPI::$mod" if $DEBUG;
+ eval "use FS::ClientAPI::$mod;";
+ die "error using FS::ClientAPI::$mod: $@" if $@;
+ }
+}
+
+#---
+
+sub dispatch {
+ my ( $self, $name ) = ( shift, shift );
+ $name =~ s(/)(::)g;
+ my $sub = "FS::ClientAPI::$name";
+ no strict 'refs';
+ &{$sub}(@_);
+}
+
+1;
+
diff --git a/FS/FS/ClientAPI/Agent.pm b/FS/FS/ClientAPI/Agent.pm
new file mode 100644
index 0000000..daede59
--- /dev/null
+++ b/FS/FS/ClientAPI/Agent.pm
@@ -0,0 +1,125 @@
+package FS::ClientAPI::Agent;
+
+#some false laziness w/MyAccount
+
+use strict;
+use vars qw($cache);
+use subs qw(_cache);
+use Digest::MD5 qw(md5_hex);
+use FS::Record qw(qsearchs); # qsearch dbdef dbh);
+use FS::ClientAPI_SessionCache;
+use FS::agent;
+use FS::cust_main qw(smart_search);
+
+sub _cache {
+ $cache ||= new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::Agent',
+ } );
+}
+
+sub agent_login {
+ my $p = shift;
+
+ #don't allow a blank login to first unconfigured agent with no user/pass
+ return { error => 'Must specify your reseller username and password.' }
+ unless length($p->{'username'}) && length($p->{'password'});
+
+ my $agent = qsearchs( 'agent', {
+ 'username' => $p->{'username'},
+ '_password' => $p->{'password'},
+ } );
+
+ unless ( $agent ) { return { error => 'Incorrect password.' } }
+
+ my $session = {
+ 'agentnum' => $agent->agentnum,
+ 'agent' => $agent->agent,
+ };
+
+ my $session_id;
+ do {
+ $session_id = md5_hex(md5_hex(time(). {}. rand(). $$))
+ } until ( ! defined _cache->get($session_id) ); #just in case
+
+ _cache->set( $session_id, $session, '1 hour' );
+
+ { 'error' => '',
+ 'session_id' => $session_id,
+ };
+}
+
+sub agent_logout {
+ my $p = shift;
+ if ( $p->{'session_id'} ) {
+ _cache->remove($p->{'session_id'});
+ return { 'error' => '' };
+ } else {
+ return { 'error' => "Can't resume session" }; #better error message
+ }
+}
+
+sub agent_info {
+ my $p = shift;
+
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ #my %return;
+
+ my $agentnum = $session->{'agentnum'};
+
+ my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } )
+ or return { 'error' => "unknown agentnum $agentnum" };
+
+ { 'error' => '',
+ 'agentnum' => $agentnum,
+ 'agent' => $agent->agent,
+ 'num_prospect' => $agent->num_prospect_cust_main,
+ 'num_active' => $agent->num_active_cust_main,
+ 'num_susp' => $agent->num_susp_cust_main,
+ 'num_cancel' => $agent->num_cancel_cust_main,
+ #%return,
+ };
+
+}
+
+sub agent_list_customers {
+ my $p = shift;
+
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ #my %return;
+
+ my $agentnum = $session->{'agentnum'};
+
+ my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } )
+ or return { 'error' => "unknown agentnum $agentnum" };
+
+ my @cust_main = smart_search( 'search' => $p->{'search'},
+ 'agentnum' => $agentnum,
+ );
+
+ #aggregate searches
+ push @cust_main,
+ map $agent->$_(), map $_.'_cust_main',
+ grep $p->{$_}, qw( prospect active susp cancel );
+
+ #eliminate dups?
+ my %saw = ();
+ @cust_main = grep { !$saw{$_->custnum}++ } @cust_main;
+
+ { customers => [ map {
+ my $cust_main = $_;
+ my $hashref = $cust_main->hashref;
+ $hashref->{$_} = $cust_main->$_()
+ foreach qw(name status statuscolor);
+ delete $hashref->{$_} foreach qw( payinfo paycvv );
+ $hashref;
+ } @cust_main
+ ],
+ }
+
+}
+
+1;
diff --git a/FS/FS/ClientAPI/MasonComponent.pm b/FS/FS/ClientAPI/MasonComponent.pm
new file mode 100644
index 0000000..78ea9bd
--- /dev/null
+++ b/FS/FS/ClientAPI/MasonComponent.pm
@@ -0,0 +1,46 @@
+package FS::ClientAPI::MasonComponent;
+
+use strict;
+use vars qw($DEBUG $me);
+use FS::Mason qw( mason_interps );
+use FS::Conf;
+
+$DEBUG = 0;
+$me = '[FS::ClientAPI::MasonComponent]';
+
+my %allowed_comps = map { $_=>1 } qw(
+ /elements/select-did.html
+ /misc/areacodes.cgi
+ /misc/exchanges.cgi
+ /misc/phonenums.cgi
+);
+
+my $outbuf;
+my( $fs_interp, $rt_interp ) = mason_interps('standalone', 'outbuf'=>\$outbuf);
+
+sub mason_comp {
+ my $packet = shift;
+
+ warn "$me mason_comp called on $packet\n" if $DEBUG;
+
+ my $comp = $packet->{'comp'};
+ unless ( $allowed_comps{$comp} ) {
+ return { 'error' => 'Illegal component' };
+ }
+
+ my @args = $packet->{'args'} ? @{ $packet->{'args'} } : ();
+
+ my $conf = new FS::Conf;
+ $FS::Mason::Request::FSURL = $conf->config('selfservice_server-base_url');
+ $FS::Mason::Request::QUERY_STRING = $packet->{'query_string'} || '';
+
+ $outbuf = '';
+ $fs_interp->exec($comp, @args); #only FS for now alas...
+
+ #errors? (turn off in-line error reporting?)
+
+ return { 'output' => $outbuf };
+
+}
+
+1;
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm
new file mode 100644
index 0000000..c0586af
--- /dev/null
+++ b/FS/FS/ClientAPI/MyAccount.pm
@@ -0,0 +1,1428 @@
+package FS::ClientAPI::MyAccount;
+
+use strict;
+use vars qw( $cache $DEBUG );
+use subs qw( _cache _provision );
+use Data::Dumper;
+use Digest::MD5 qw(md5_hex);
+use Date::Format;
+use Business::CreditCard;
+use Time::Duration;
+use FS::UI::Web::small_custview qw(small_custview); #less doh
+use FS::UI::Web;
+use FS::UI::bytecount;
+use FS::Conf;
+use FS::Record qw(qsearch qsearchs);
+use FS::Msgcat qw(gettext);
+use FS::Misc qw(card_types);
+use FS::ClientAPI_SessionCache;
+use FS::svc_acct;
+use FS::svc_domain;
+use FS::svc_phone;
+use FS::svc_external;
+use FS::part_svc;
+use FS::cust_main;
+use FS::cust_bill;
+use FS::cust_main_county;
+use FS::cust_pkg;
+use FS::payby;
+use FS::acct_rt_transaction;
+use HTML::Entities;
+
+$DEBUG = 0;
+
+#false laziness with FS::cust_main
+BEGIN {
+ eval "use Time::Local;";
+ die "Time::Local minimum version 1.05 required with Perl versions before 5.6"
+ if $] < 5.006 && !defined($Time::Local::VERSION);
+ eval "use Time::Local qw(timelocal_nocheck);";
+}
+
+use vars qw( @cust_main_editable_fields );
+@cust_main_editable_fields = qw(
+ first last company address1 address2 city
+ county state zip country daytime night fax
+ ship_first ship_last ship_company ship_address1 ship_address2 ship_city
+ ship_state ship_zip ship_country ship_daytime ship_night ship_fax
+ payby payinfo payname paystart_month paystart_year payissue payip
+ ss paytype paystate stateid stateid_state
+);
+
+sub _cache {
+ $cache ||= new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::MyAccount',
+ } );
+}
+
+sub login_info {
+ my $p = shift;
+
+ my $conf = new FS::Conf;
+
+ my %info = (
+ 'phone_login' => $conf->exists('selfservice_server-phone_login'),
+ 'single_domain'=> scalar($conf->config('selfservice_server-single_domain')),
+ );
+
+ return \%info;
+
+}
+
+#false laziness w/FS::ClientAPI::passwd::passwd
+sub login {
+ my $p = shift;
+
+ my $conf = new FS::Conf;
+
+ my $svc_x = '';
+ if ( $p->{'domain'} eq 'svc_phone'
+ && $conf->exists('selfservice_server-phone_login') ) {
+
+ my $svc_phone = qsearchs( 'svc_phone', { 'phonenum' => $p->{'username'} } );
+ return { error => 'Number not found.' } unless $svc_phone;
+
+ #XXX?
+ #my $pkg_svc = $svc_acct->cust_svc->pkg_svc;
+ #return { error => 'Only primary user may log in.' }
+ # if $conf->exists('selfservice_server-primary_only')
+ # && ( ! $pkg_svc || $pkg_svc->primary_svc ne 'Y' );
+
+ return { error => 'Incorrect PIN.' }
+ unless $svc_phone->check_pin($p->{'password'});
+
+ $svc_x = $svc_phone;
+
+ } else {
+
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $p->{'domain'} } )
+ or return { error => 'Domain '. $p->{'domain'}. ' not found' };
+
+ my $svc_acct = qsearchs( 'svc_acct', { 'username' => $p->{'username'},
+ 'domsvc' => $svc_domain->svcnum, }
+ );
+ return { error => 'User not found.' } unless $svc_acct;
+
+ #my $pkg_svc = $svc_acct->cust_svc->pkg_svc;
+ #return { error => 'Only primary user may log in.' }
+ # if $conf->exists('selfservice_server-primary_only')
+ # && ( ! $pkg_svc || $pkg_svc->primary_svc ne 'Y' );
+ my $cust_svc = $svc_acct->cust_svc;
+ my $part_pkg = $cust_svc->cust_pkg->part_pkg;
+ return { error => 'Only primary user may log in.' }
+ if $conf->exists('selfservice_server-primary_only')
+ && $cust_svc->svcpart != $part_pkg->svcpart('svc_acct');
+
+ return { error => 'Incorrect password.' }
+ unless $svc_acct->check_password($p->{'password'});
+
+ $svc_x = $svc_acct;
+
+ }
+
+ my $session = {
+ 'svcnum' => $svc_x->svcnum,
+ };
+
+ my $cust_pkg = $svc_x->cust_svc->cust_pkg;
+ if ( $cust_pkg ) {
+ my $cust_main = $cust_pkg->cust_main;
+ $session->{'custnum'} = $cust_main->custnum;
+ }
+
+ my $session_id;
+ do {
+ $session_id = md5_hex(md5_hex(time(). {}. rand(). $$))
+ } until ( ! defined _cache->get($session_id) ); #just in case
+
+ my $timeout = $conf->config('selfservice-session_timeout') || '1 hour';
+ _cache->set( $session_id, $session, $timeout );
+
+ return { 'error' => '',
+ 'session_id' => $session_id,
+ };
+}
+
+sub logout {
+ my $p = shift;
+ if ( $p->{'session_id'} ) {
+ _cache->remove($p->{'session_id'});
+ return { 'error' => '' };
+ } else {
+ return { 'error' => "Can't resume session" }; #better error message
+ }
+}
+
+sub customer_info {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my %return;
+
+ my $conf = new FS::Conf;
+ if ($conf->exists('cust_main-require_address2')) {
+ $return{'require_address2'} = '1';
+ }else{
+ $return{'require_address2'} = '';
+ }
+
+ if ( $custnum ) { #customer record
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ $return{balance} = $cust_main->balance;
+
+ $return{tickets} = [ ($cust_main->tickets) ];
+
+ my @open = map {
+ {
+ invnum => $_->invnum,
+ date => time2str("%b %o, %Y", $_->_date),
+ owed => $_->owed,
+ };
+ } $cust_main->open_cust_bill;
+ $return{open_invoices} = \@open;
+
+ $return{small_custview} =
+ small_custview( $cust_main, $conf->config('countrydefault') );
+
+ $return{name} = $cust_main->first. ' '. $cust_main->get('last');
+
+ for (@cust_main_editable_fields) {
+ $return{$_} = $cust_main->get($_);
+ }
+
+ if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
+ $return{payinfo} = $cust_main->paymask;
+ @return{'month', 'year'} = $cust_main->paydate_monthyear;
+ }
+
+ $return{'invoicing_list'} =
+ join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
+ $return{'postal_invoicing'} =
+ 0 < ( grep { $_ eq 'POST' } $cust_main->invoicing_list );
+
+ if (scalar($conf->config('support_packages'))) {
+ my @support_services = ();
+ foreach ($cust_main->support_services) {
+ my $seconds = $_->svc_x->seconds;
+ my $time_remaining = (($seconds < 0) ? '-' : '' ).
+ int(abs($seconds)/3600)."h".
+ sprintf("%02d",(abs($seconds)%3600)/60)."m";
+ my $cust_pkg = $_->cust_pkg;
+ my $pkgnum = '';
+ my $pkg = '';
+ $pkgnum = $cust_pkg->pkgnum if $cust_pkg;
+ $pkg = $cust_pkg->part_pkg->pkg if $cust_pkg;
+ push @support_services, { svcnum => $_->svcnum,
+ time => $time_remaining,
+ pkgnum => $pkgnum,
+ pkg => $pkg,
+ };
+ }
+ $return{support_services} = \@support_services;
+ }
+
+ } elsif ( $session->{'svcnum'} ) { #no customer record
+
+ my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $session->{'svcnum'} } )
+ or die "unknown svcnum";
+ $return{name} = $svc_acct->email;
+
+ } else {
+
+ return { 'error' => 'Expired session' }; #XXX redirect to login w/this err!
+
+ }
+
+ return { 'error' => '',
+ 'custnum' => $custnum,
+ %return,
+ };
+
+}
+
+sub edit_info {
+ my $p = shift;
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'}
+ or return { 'error' => "no customer record" };
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $new = new FS::cust_main { $cust_main->hash };
+ $new->set( $_ => $p->{$_} )
+ foreach grep { exists $p->{$_} } @cust_main_editable_fields;
+
+ my $payby = '';
+ if (exists($p->{'payby'})) {
+ $p->{'payby'} =~ /^([A-Z]{4})$/
+ or return { 'error' => "illegal_payby " . $p->{'payby'} };
+ $payby = $1;
+ }
+
+ if ( $payby =~ /^(CARD|DCRD)$/ ) {
+
+ $new->paydate($p->{'year'}. '-'. $p->{'month'}. '-01');
+
+ if ( $new->payinfo eq $cust_main->paymask ) {
+ $new->payinfo($cust_main->payinfo);
+ } else {
+ $new->payinfo($p->{'payinfo'});
+ }
+
+ $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
+
+ }elsif ( $payby =~ /^(CHEK|DCHK)$/ ) {
+ my $payinfo;
+ $p->{'payinfo1'} =~ /^([\dx]+)$/
+ or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
+ my $payinfo1 = $1;
+ $p->{'payinfo2'} =~ /^([\dx]+)$/
+ or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
+ my $payinfo2 = $1;
+ $payinfo = $payinfo1. '@'. $payinfo2;
+
+ if ( $payinfo eq $cust_main->paymask ) {
+ $new->payinfo($cust_main->payinfo);
+ } else {
+ $new->payinfo($payinfo);
+ }
+
+ $new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' );
+
+ }elsif ( $payby =~ /^(BILL)$/ ) {
+ } elsif ( $payby ) { #notyet ready
+ return { 'error' => "unknown payby $payby" };
+ }
+
+ my @invoicing_list;
+ if ( exists $p->{'invoicing_list'} || exists $p->{'postal_invoicing'} ) {
+ #false laziness with httemplate/edit/process/cust_main.cgi
+ @invoicing_list = split( /\s*\,\s*/, $p->{'invoicing_list'} );
+ push @invoicing_list, 'POST' if $p->{'postal_invoicing'};
+ } else {
+ @invoicing_list = $cust_main->invoicing_list;
+ }
+
+ my $error = $new->replace($cust_main, \@invoicing_list);
+ return { 'error' => $error } if $error;
+ #$cust_main = $new;
+
+ return { 'error' => '' };
+}
+
+sub payment_info {
+ my $p = shift;
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ ##
+ #generic
+ ##
+
+ use vars qw($payment_info); #cache for performance
+ unless ( $payment_info ) {
+
+ my $conf = new FS::Conf;
+ my %states = map { $_->state => 1 }
+ qsearch('cust_main_county', {
+ 'country' => $conf->config('countrydefault') || 'US'
+ } );
+
+ $payment_info = {
+
+ #list all counties/states/countries
+ 'cust_main_county' =>
+ [ map { $_->hashref } qsearch('cust_main_county', {}) ],
+
+ #shortcut for one-country folks
+ 'states' =>
+ [ sort { $a cmp $b } keys %states ],
+
+ 'card_types' => card_types(),
+
+ 'paytypes' => [ @FS::cust_main::paytypes ],
+
+ 'paybys' => [ $conf->config('signup_server-payby') ],
+
+ 'stateid_label' => FS::Msgcat::_gettext('stateid'),
+ 'stateid_state_label' => FS::Msgcat::_gettext('stateid_state'),
+
+ 'show_ss' => $conf->exists('show_ss'),
+ 'show_stateid' => $conf->exists('show_stateid'),
+ 'show_paystate' => $conf->exists('show_bankstate'),
+ };
+
+ }
+
+ ##
+ #customer-specific
+ ##
+
+ my %return = %$payment_info;
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ $return{balance} = $cust_main->balance;
+
+ $return{payname} = $cust_main->payname
+ || ( $cust_main->first. ' '. $cust_main->get('last') );
+
+ $return{$_} = $cust_main->get($_) for qw(address1 address2 city state zip);
+
+ $return{payby} = $cust_main->payby;
+ $return{stateid_state} = $cust_main->stateid_state;
+
+ if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
+ $return{card_type} = cardtype($cust_main->payinfo);
+ $return{payinfo} = $cust_main->paymask;
+
+ @return{'month', 'year'} = $cust_main->paydate_monthyear;
+
+ }
+
+ if ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
+ my ($payinfo1, $payinfo2) = split '@', $cust_main->paymask;
+ $return{payinfo1} = $payinfo1;
+ $return{payinfo2} = $payinfo2;
+ $return{paytype} = $cust_main->paytype;
+ $return{paystate} = $cust_main->paystate;
+
+ }
+
+ #doubleclick protection
+ my $_date = time;
+ $return{paybatch} = "webui-MyAccount-$_date-$$-". rand() * 2**32;
+
+ return { 'error' => '',
+ %return,
+ };
+
+};
+
+#some false laziness with httemplate/process/payment.cgi - look there for
+#ACH and CVV support stuff
+sub process_payment {
+
+ my $p = shift;
+
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my %return;
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ $p->{'payname'} =~ /^([\w \,\.\-\']+)$/
+ or return { 'error' => gettext('illegal_name'). " payname: ". $p->{'payname'} };
+ my $payname = $1;
+
+ $p->{'paybatch'} =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]*)$/
+ or return { 'error' => gettext('illegal_text'). " paybatch: ". $p->{'paybatch'} };
+ my $paybatch = $1;
+
+ $p->{'payby'} =~ /^([A-Z]{4})$/
+ or return { 'error' => "illegal_payby " . $p->{'payby'} };
+ my $payby = $1;
+
+ #false laziness w/process/payment.cgi
+ my $payinfo;
+ my $paycvv = '';
+ if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+
+ $p->{'payinfo1'} =~ /^([\dx]+)$/
+ or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
+ my $payinfo1 = $1;
+ $p->{'payinfo2'} =~ /^([\dx]+)$/
+ or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
+ my $payinfo2 = $1;
+ $payinfo = $payinfo1. '@'. $payinfo2;
+
+ $payinfo = $cust_main->payinfo
+ if $cust_main->paymask eq $payinfo;
+
+ } elsif ( $payby eq 'CARD' || $payby eq 'DCRD' ) {
+
+ $payinfo = $p->{'payinfo'};
+
+ $payinfo = $cust_main->payinfo
+ if $cust_main->paymask eq $payinfo;
+
+ $payinfo =~ s/\D//g;
+ $payinfo =~ /^(\d{13,16})$/
+ or return { 'error' => gettext('invalid_card') }; # . ": ". $self->payinfo
+ $payinfo = $1;
+
+ validate($payinfo)
+ or return { 'error' => gettext('invalid_card') }; # . ": ". $self->payinfo
+ return { 'error' => gettext('unknown_card_type') }
+ if cardtype($payinfo) eq "Unknown";
+
+ if ( length($p->{'paycvv'}) && $p->{'paycvv'} !~ /^\s*$/ ) {
+ if ( cardtype($payinfo) eq 'American Express card' ) {
+ $p->{'paycvv'} =~ /^\s*(\d{4})\s*$/
+ or return { 'error' => "CVV2 (CID) for American Express cards is four digits." };
+ $paycvv = $1;
+ } else {
+ $p->{'paycvv'} =~ /^\s*(\d{3})\s*$/
+ or return { 'error' => "CVV2 (CVC2/CID) is three digits." };
+ $paycvv = $1;
+ }
+ }
+
+ } else {
+ die "unknown payby $payby";
+ }
+
+ my %payby2fields = (
+ 'CARD' => [ qw( paystart_month paystart_year payissue address1 address2 city state zip payip ) ],
+ 'CHEK' => [ qw( ss paytype paystate stateid stateid_state payip ) ],
+ );
+
+ my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $p->{'amount'},
+ 'quiet' => 1,
+ 'payinfo' => $payinfo,
+ 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01',
+ 'payname' => $payname,
+ 'paybatch' => $paybatch, #this doesn't actually do anything
+ 'paycvv' => $paycvv,
+ map { $_ => $p->{$_} } @{ $payby2fields{$payby} }
+ );
+ return { 'error' => $error } if $error;
+
+ $cust_main->apply_payments;
+
+ if ( $p->{'save'} ) {
+ my $new = new FS::cust_main { $cust_main->hash };
+ if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ $new->set( $_ => $p->{$_} )
+ foreach qw( payname paystart_month paystart_year payissue payip
+ address1 address2 city state zip payinfo );
+ $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
+ } elsif ($payby eq 'CHEK' || $payby eq 'DCHK') {
+ $new->set( $_ => $p->{$_} )
+ foreach qw( payname payip paytype paystate
+ stateid stateid_state );
+ $new->set( 'payinfo' => $payinfo );
+ $new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' );
+ }
+ $new->set( 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01' );
+ my $error = $new->replace($cust_main);
+ return { 'error' => $error } if $error;
+ $cust_main = $new;
+ }
+
+ return { 'error' => '' };
+
+}
+
+sub process_payment_order_pkg {
+ my $p = shift;
+
+ my $hr = process_payment($p);
+ return $hr if $hr->{'error'};
+
+ order_pkg($p);
+}
+
+sub process_payment_order_renew {
+ my $p = shift;
+
+ my $hr = process_payment($p);
+ return $hr if $hr->{'error'};
+
+ order_renew($p);
+}
+
+sub process_prepay {
+
+ my $p = shift;
+
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my %return;
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my( $amount, $seconds, $upbytes, $downbytes, $totalbytes ) = ( 0, 0, 0, 0, 0 );
+ my $error = $cust_main->recharge_prepay( $p->{'prepaid_cardnum'},
+ \$amount,
+ \$seconds,
+ \$upbytes,
+ \$downbytes,
+ \$totalbytes,
+ );
+
+ return { 'error' => $error } if $error;
+
+ return { 'error' => '',
+ 'amount' => $amount,
+ 'seconds' => $seconds,
+ 'duration' => duration_exact($seconds),
+ 'upbytes' => $upbytes,
+ 'upload' => FS::UI::bytecount::bytecount_unexact($upbytes),
+ 'downbytes' => $downbytes,
+ 'download' => FS::UI::bytecount::bytecount_unexact($downbytes),
+ 'totalbytes'=> $totalbytes,
+ 'totalload' => FS::UI::bytecount::bytecount_unexact($totalbytes),
+ };
+
+}
+
+sub invoice {
+ my $p = shift;
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'};
+
+ my $invnum = $p->{'invnum'};
+
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $invnum,
+ 'custnum' => $custnum } )
+ or return { 'error' => "Can't find invnum" };
+
+ #my %return;
+
+ return { 'error' => '',
+ 'invnum' => $invnum,
+ 'invoice_text' => join('', $cust_bill->print_text ),
+ 'invoice_html' => $cust_bill->print_html( { unsquelch_cdr => 1 } ),
+ };
+
+}
+
+sub invoice_logo {
+ my $p = shift;
+
+ #sessioning for this? how do we get the session id to the backend invoice
+ # template so it can add it to the link, blah
+
+ my $templatename = $p->{'templatename'};
+
+ #false laziness-ish w/view/cust_bill-logo.cgi
+
+ my $conf = new FS::Conf;
+ if ( $templatename =~ /^([^\.\/]*)$/ && $conf->exists("logo_$1.png") ) {
+ $templatename = "_$1";
+ } else {
+ $templatename = '';
+ }
+
+ my $filename = "logo$templatename.png";
+
+ return { 'error' => '',
+ 'logo' => $conf->config_binary($filename),
+ 'content_type' => 'image/png', #should allow gif, jpg too
+ };
+}
+
+
+sub list_invoices {
+ my $p = shift;
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my @cust_bill = $cust_main->cust_bill;
+
+ return { 'error' => '',
+ 'invoices' => [ map { { 'invnum' => $_->invnum,
+ '_date' => $_->_date,
+ }
+ } @cust_bill
+ ]
+ };
+}
+
+sub cancel {
+ my $p = shift;
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my @errors = $cust_main->cancel( 'quiet'=>1 );
+
+ my $error = scalar(@errors) ? join(' / ', @errors) : '';
+
+ return { 'error' => $error };
+
+}
+
+sub list_pkgs {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ #return { 'cust_pkg' => [ map { $_->hashref } $cust_main->ncancelled_pkgs ] };
+
+ my $conf = new FS::Conf;
+
+ { 'svcnum' => $session->{'svcnum'},
+ 'custnum' => $custnum,
+ 'cust_pkg' => [ map {
+ { $_->hash,
+ $_->part_pkg->hash,
+ part_svc =>
+ [ map $_->hashref, $_->available_part_svc ],
+ cust_svc =>
+ [ map { my $ref = { $_->hash,
+ label => [ $_->label ],
+ };
+ $ref->{_password} = $_->svc_x->_password
+ if $context eq 'agent'
+ && $conf->exists('agent-showpasswords')
+ && $_->part_svc->svcdb eq 'svc_acct';
+ $ref;
+ } $_->cust_svc
+ ],
+ };
+ } $cust_main->ncancelled_pkgs
+ ],
+ 'small_custview' =>
+ small_custview( $cust_main, $conf->config('countrydefault') ),
+ };
+
+}
+
+sub list_svcs {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my @cust_svc = ();
+ #foreach my $cust_pkg ( $cust_main->ncancelled_pkgs ) {
+ foreach my $cust_pkg ( $p->{'ncancelled'}
+ ? $cust_main->ncancelled_pkgs
+ : $cust_main->unsuspended_pkgs ) {
+ push @cust_svc, @{[ $cust_pkg->cust_svc ]}; #@{[ ]} to force array context
+ }
+ @cust_svc = grep { $_->part_svc->svcdb eq $p->{'svcdb'} } @cust_svc
+ if $p->{'svcdb'};
+
+ #@svc_x = sort { $a->domain cmp $b->domain || $a->username cmp $b->username }
+ # @svc_x;
+
+ {
+ #no#'svcnum' => $session->{'svcnum'},
+ 'custnum' => $custnum,
+ 'svcs' => [ map {
+ my $svc_x = $_->svc_x;
+ my($label, $value) = $_->label;
+ my $part_pkg = $svc_x->cust_svc->cust_pkg->part_pkg;
+
+ { 'svcnum' => $_->svcnum,
+ 'label' => $label,
+ 'value' => $value,
+ 'username' => $svc_x->username,
+ 'email' => $svc_x->email,
+ 'seconds' => $svc_x->seconds,
+ 'upbytes' => FS::UI::bytecount::display_bytecount($svc_x->upbytes),
+ 'downbytes' => FS::UI::bytecount::display_bytecount($svc_x->downbytes),
+ 'totalbytes'=> FS::UI::bytecount::display_bytecount($svc_x->totalbytes),
+ 'recharge_amount' => $part_pkg->option('recharge_amount', 1),
+ 'recharge_seconds' => $part_pkg->option('recharge_seconds', 1),
+ 'recharge_upbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_upbytes', 1)),
+ 'recharge_downbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_downbytes', 1)),
+ 'recharge_totalbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_totalbytes', 1)),
+ # more...
+ };
+ }
+ @cust_svc
+ ],
+ };
+
+}
+
+sub _list_svc_usage {
+ my($svc_acct, $begin, $end) = @_;
+ my @usage = ();
+ foreach my $part_export (
+ map { qsearch ( 'part_export', { 'exporttype' => $_ } ) }
+ qw (sqlradius sqlradius_withdomain')
+ ) {
+
+ push @usage, @ { $part_export->usage_sessions($begin, $end, $svc_acct) };
+ }
+ (@usage);
+}
+
+sub list_svc_usage {
+ _usage_details(\&_list_svc_usage, @_);
+}
+
+sub _list_support_usage {
+ my($svc_acct, $begin, $end) = @_;
+ my @usage = ();
+ foreach ( grep { $begin <= $_->_date && $_->_date <= $end }
+ qsearch('acct_rt_transaction', { 'svcnum' => $svc_acct->svcnum })
+ ) {
+ push @usage, { 'seconds' => $_->seconds,
+ 'support' => $_->support,
+ '_date' => $_->_date,
+ 'id' => $_->transaction_id,
+ 'creator' => $_->creator,
+ 'subject' => $_->subject,
+ 'status' => $_->status,
+ 'ticketid' => $_->ticketid,
+ };
+ }
+ (@usage);
+}
+
+sub list_support_usage {
+ _usage_details(\&_list_support_usage, @_);
+}
+
+sub _usage_details {
+ my ($callback, $p) = (shift,shift);
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'svcnum' => $p->{'svcnum'} };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $svc_acct = qsearchs ( 'svc_acct', $search );
+ return { 'error' => 'No service selected in list_svc_usage' }
+ unless $svc_acct;
+
+ my $freq = $svc_acct->cust_svc->cust_pkg->part_pkg->freq;
+ my $start = $svc_acct->cust_svc->cust_pkg->setup;
+ #my $end = $svc_acct->cust_svc->cust_pkg->bill; # or time?
+ my $end = time;
+
+ unless($p->{beginning}){
+ $p->{beginning} = $svc_acct->cust_svc->cust_pkg->last_bill;
+ $p->{ending} = $end;
+ }
+
+ my (@usage) = &$callback($svc_acct,$p->{beginning},$p->{ending});
+
+ #kinda false laziness with FS::cust_main::bill, but perhaps
+ #we should really change this bit to DateTime and DateTime::Duration
+ #
+ #change this bit to use Date::Manip? CAREFUL with timezones (see
+ # mailing list archive)
+ my ($nsec,$nmin,$nhour,$nmday,$nmon,$nyear) =
+ (localtime($p->{ending}) )[0,1,2,3,4,5];
+ my ($psec,$pmin,$phour,$pmday,$pmon,$pyear) =
+ (localtime($p->{beginning}) )[0,1,2,3,4,5];
+
+ if ( $freq =~ /^\d+$/ ) {
+ $nmon += $freq;
+ until ( $nmon < 12 ) { $nmon -= 12; $nyear++; }
+ $pmon -= $freq;
+ until ( $pmon >= 0 ) { $pmon += 12; $pyear--; }
+ } elsif ( $freq =~ /^(\d+)w$/ ) {
+ my $weeks = $1;
+ $nmday += $weeks * 7;
+ $pmday -= $weeks * 7;
+ } elsif ( $freq =~ /^(\d+)d$/ ) {
+ my $days = $1;
+ $nmday += $days;
+ $pmday -= $days;
+ } elsif ( $freq =~ /^(\d+)h$/ ) {
+ my $hours = $1;
+ $nhour += $hours;
+ $phour -= $hours;
+ } else {
+ return { 'error' => "unparsable frequency: ". $freq };
+ }
+
+ my $previous = timelocal_nocheck($psec,$pmin,$phour,$pmday,$pmon,$pyear);
+ my $next = timelocal_nocheck($nsec,$nmin,$nhour,$nmday,$nmon,$nyear);
+
+ {
+ 'error' => '',
+ 'svcnum' => $p->{svcnum},
+ 'beginning' => $p->{beginning},
+ 'ending' => $p->{ending},
+ 'previous' => ($previous > $start) ? $previous : $start,
+ 'next' => ($next < $end) ? $next : $end,
+ 'usage' => \@usage,
+ };
+}
+
+sub order_pkg {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $status = $cust_main->status;
+ #false laziness w/ClientAPI/Signup.pm
+
+ my $cust_pkg = new FS::cust_pkg ( {
+ 'custnum' => $custnum,
+ 'pkgpart' => $p->{'pkgpart'},
+ } );
+ my $error = $cust_pkg->check;
+ return { 'error' => $error } if $error;
+
+ my @svc = ();
+ unless ( $p->{'svcpart'} eq 'none' ) {
+
+ my $svcdb;
+ my $svcpart = '';
+ if ( $p->{'svcpart'} =~ /^(\d+)$/ ) {
+ $svcpart = $1;
+ my $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } );
+ return { 'error' => "Unknown svcpart $svcpart" } unless $part_svc;
+ $svcdb = $part_svc->svcdb;
+ } else {
+ $svcdb = 'svc_acct';
+ }
+ $svcpart ||= $cust_pkg->part_pkg->svcpart($svcdb);
+
+ my %fields = (
+ 'svc_acct' => [ qw( username domsvc _password sec_phrase popnum ) ],
+ 'svc_domain' => [ qw( domain ) ],
+ 'svc_phone' => [ qw( phonenum pin sip_password ) ],
+ 'svc_external' => [ qw( id title ) ],
+ );
+
+ my $svc_x = "FS::$svcdb"->new( {
+ 'svcpart' => $svcpart,
+ map { $_ => $p->{$_} } @{$fields{$svcdb}}
+ } );
+
+ if ( $svcdb eq 'svc_acct' ) {
+ my @acct_snarf;
+ my $snarfnum = 1;
+ while ( length($p->{"snarf_machine$snarfnum"}) ) {
+ my $acct_snarf = new FS::acct_snarf ( {
+ 'machine' => $p->{"snarf_machine$snarfnum"},
+ 'protocol' => $p->{"snarf_protocol$snarfnum"},
+ 'username' => $p->{"snarf_username$snarfnum"},
+ '_password' => $p->{"snarf_password$snarfnum"},
+ } );
+ $snarfnum++;
+ push @acct_snarf, $acct_snarf;
+ }
+ $svc_x->child_objects( \@acct_snarf );
+ }
+
+ my $y = $svc_x->setdefault; # arguably should be in new method
+ return { 'error' => $y } if $y && !ref($y);
+
+ $error = $svc_x->check;
+ return { 'error' => $error } if $error;
+
+ push @svc, $svc_x;
+
+ }
+
+ use Tie::RefHash;
+ tie my %hash, 'Tie::RefHash';
+ %hash = ( $cust_pkg => \@svc );
+ #msgcat
+ $error = $cust_main->order_pkgs( \%hash, '', 'noexport' => 1 );
+ return { 'error' => $error } if $error;
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('signup_server-realtime') ) {
+
+ my $bill_error = _do_bop_realtime( $cust_main, $status );
+
+ if ($bill_error) {
+ $cust_pkg->cancel('quiet'=>1);
+ return $bill_error;
+ } else {
+ $cust_pkg->reexport;
+ }
+
+ } else {
+ $cust_pkg->reexport;
+ }
+
+ return { error => '', pkgnum => $cust_pkg->pkgnum };
+
+}
+
+sub change_pkg {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $status = $cust_main->status;
+ my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $p->{pkgnum} } )
+ or return { 'error' => "unknown package $p->{pkgnum}" };
+
+ my @newpkg;
+ my $error = FS::cust_pkg::order( $custnum,
+ [$p->{pkgpart}],
+ [$p->{pkgnum}],
+ \@newpkg,
+ );
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('signup_server-realtime') ) {
+
+ my $bill_error = _do_bop_realtime( $cust_main, $status );
+
+ if ($bill_error) {
+ $newpkg[0]->suspend;
+ return $bill_error;
+ } else {
+ $newpkg[0]->reexport;
+ }
+
+ } else {
+ $newpkg[0]->reexport;
+ }
+
+ return { error => '', pkgnum => $cust_pkg->pkgnum };
+
+}
+
+sub order_recharge {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $status = $cust_main->status;
+ my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $p->{'svcnum'} } )
+ or return { 'error' => "unknown service " . $p->{'svcnum'} };
+
+ my $svc_x = $cust_svc->svc_x;
+ my $part_pkg = $cust_svc->cust_pkg->part_pkg;
+
+ my %vhash =
+ map { $_ =~ /^recharge_(.*)$/; $1, $part_pkg->option($_, 1) }
+ qw ( recharge_seconds recharge_upbytes recharge_downbytes
+ recharge_totalbytes );
+ my $amount = $part_pkg->option('recharge_amount', 1);
+
+ my ($l, $v, $d) = $cust_svc->label; # blah
+ my $pkg = "Recharge $v";
+
+ my $bill_error = $cust_main->charge($amount, $pkg,
+ "time: $vhash{seconds}, up: $vhash{upbytes}," .
+ "down: $vhash{downbytes}, total: $vhash{totalbytes}",
+ $part_pkg->taxclass); #meh
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('signup_server-realtime') && !$bill_error ) {
+
+ $bill_error = _do_bop_realtime( $cust_main, $status );
+
+ if ($bill_error) {
+ return $bill_error;
+ } else {
+ my $error = $svc_x->recharge (\%vhash);
+ return { 'error' => $error } if $error;
+ }
+
+ } else {
+ my $error = $bill_error;
+ $error ||= $svc_x->recharge (\%vhash);
+ return { 'error' => $error } if $error;
+ }
+
+ return { error => '', svc => $cust_svc->part_svc->svc };
+
+}
+
+sub _do_bop_realtime {
+ my ($cust_main, $status) = (shift, shift);
+
+ my $old_balance = $cust_main->balance;
+
+ my $bill_error = $cust_main->bill
+ || $cust_main->apply_payments_and_credits
+ || $cust_main->collect('realtime' => 1);
+
+ if ( $cust_main->balance > $old_balance
+ && $cust_main->balance > 0
+ && ( $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ?
+ 1 : $status eq 'suspended' ) ) {
+ #this makes sense. credit is "un-doing" the invoice
+ my $conf = new FS::Conf;
+ $cust_main->credit( sprintf("%.2f", $cust_main->balance - $old_balance ),
+ 'self-service decline',
+ 'reason_type' => $conf->config('signup_credit_type'),
+ );
+ $cust_main->apply_credits( 'order' => 'newest' );
+
+ return { 'error' => '_decline', 'bill_error' => $bill_error };
+ }
+
+ '';
+}
+
+sub renew_info {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my @cust_pkg = sort { $a->bill <=> $b->bill }
+ grep { $_->part_pkg->freq ne '0' }
+ $cust_main->ncancelled_pkgs;
+
+ #return { 'error' => 'No active packages to renew.' } unless @cust_pkg;
+
+ my $total = $cust_main->balance;
+
+ my @array = map {
+ $total += $_->part_pkg->base_recur;
+ my $renew_date = $_->part_pkg->add_freq($_->bill);
+ {
+ 'bill_date' => $_->bill,
+ 'bill_date_pretty' => time2str('%x', $_->bill),
+ 'renew_date' => $renew_date,
+ 'renew_date_pretty' => time2str('%x', $renew_date),
+ 'amount' => sprintf('%.2f', $total),
+ };
+ }
+ @cust_pkg;
+
+ return { 'dates' => \@array };
+
+}
+
+sub order_renew {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $date = $p->{'date'};
+
+ my $now = time;
+
+ #freeside-daily -n -d $date fs_daily $custnum
+ $cust_main->bill_and_collect( 'time' => $date,
+ 'invoice_time' => $now,
+ 'actual_time' => $now,
+ 'check_freq' => '1d',
+ );
+
+ return { 'error' => '' };
+
+}
+
+sub cancel_pkg {
+ my $p = shift;
+ my $session = _cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $pkgnum = $p->{'pkgnum'};
+
+ my $cust_pkg = qsearchs('cust_pkg', { 'custnum' => $custnum,
+ 'pkgnum' => $pkgnum, } )
+ or return { 'error' => "unknown pkgnum $pkgnum" };
+
+ my $error = $cust_pkg->cancel( 'quiet'=>1 );
+ return { 'error' => $error };
+
+}
+
+sub provision_acct {
+ my $p = shift;
+ warn "provision_acct called\n"
+ if $DEBUG;
+
+ return { 'error' => gettext('passwords_dont_match') }
+ if $p->{'_password'} ne $p->{'_password2'};
+ return { 'error' => gettext('empty_password') }
+ unless length($p->{'_password'});
+
+ if ($p->{'domsvc'}) {
+ my %domains = domain_select_hash FS::svc_acct(map { $_ => $p->{$_} }
+ qw ( svcpart pkgnum ) );
+ return { 'error' => gettext('invalid_domain') }
+ unless ($domains{$p->{'domsvc'}});
+ }
+
+ warn "provision_acct calling _provision\n"
+ if $DEBUG;
+ _provision( 'FS::svc_acct',
+ [qw(username _password domsvc)],
+ [qw(username _password domsvc)],
+ $p,
+ @_
+ );
+}
+
+sub provision_external {
+ my $p = shift;
+ #_provision( 'FS::svc_external', [qw(id title)], [qw(id title)], $p, @_ );
+ _provision( 'FS::svc_external',
+ [],
+ [qw(id title)],
+ $p,
+ @_
+ );
+}
+
+sub _provision {
+ my( $class, $fields, $return_fields, $p ) = splice(@_, 0, 4);
+ warn "_provision called for $class\n"
+ if $DEBUG;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $pkgnum = $p->{'pkgnum'};
+
+ warn "searching for custnum $custnum pkgnum $pkgnum\n"
+ if $DEBUG;
+ my $cust_pkg = qsearchs('cust_pkg', { 'custnum' => $custnum,
+ 'pkgnum' => $pkgnum,
+ } )
+ or return { 'error' => "unknown pkgnum $pkgnum" };
+
+ warn "searching for svcpart ". $p->{'svcpart'}. "\n"
+ if $DEBUG;
+ my $part_svc = qsearchs('part_svc', { 'svcpart' => $p->{'svcpart'} } )
+ or return { 'error' => "unknown svcpart $p->{'svcpart'}" };
+
+ warn "creating $class record\n"
+ if $DEBUG;
+ my $svc_x = $class->new( {
+ 'pkgnum' => $p->{'pkgnum'},
+ 'svcpart' => $p->{'svcpart'},
+ map { $_ => $p->{$_} } @$fields
+ } );
+ warn "inserting $class record\n"
+ if $DEBUG;
+ my $error = $svc_x->insert;
+
+ unless ( $error ) {
+ warn "finding inserted record for svcnum ". $svc_x->svcnum. "\n"
+ if $DEBUG;
+ $svc_x = qsearchs($svc_x->table, { 'svcnum' => $svc_x->svcnum })
+ }
+
+ my $return = { 'svc' => $part_svc->svc,
+ 'error' => $error,
+ map { $_ => $svc_x->get($_) } @$return_fields
+ };
+ warn "_provision returning ". Dumper($return). "\n"
+ if $DEBUG;
+ return $return;
+
+}
+
+sub part_svc_info {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $pkgnum = $p->{'pkgnum'};
+
+ my $cust_pkg = qsearchs('cust_pkg', { 'custnum' => $custnum,
+ 'pkgnum' => $pkgnum,
+ } )
+ or return { 'error' => "unknown pkgnum $pkgnum" };
+
+ my $svcpart = $p->{'svcpart'};
+
+ my $pkg_svc = qsearchs('pkg_svc', { 'pkgpart' => $cust_pkg->pkgpart,
+ 'svcpart' => $svcpart, } )
+ or return { 'error' => "unknown svcpart $svcpart for pkgnum $pkgnum" };
+ my $part_svc = $pkg_svc->part_svc;
+
+ my $conf = new FS::Conf;
+
+ return {
+ 'svc' => $part_svc->svc,
+ 'svcdb' => $part_svc->svcdb,
+ 'pkgnum' => $pkgnum,
+ 'svcpart' => $svcpart,
+ 'custnum' => $custnum,
+
+ 'security_phrase' => 0, #XXX !
+ 'svc_acct_pop' => [], #XXX !
+ 'popnum' => '',
+ 'init_popstate' => '',
+ 'popac' => '',
+ 'acstate' => '',
+
+ 'small_custview' =>
+ small_custview( $cust_main, $conf->config('countrydefault') ),
+
+ };
+
+}
+
+sub unprovision_svc {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $svcnum = $p->{'svcnum'};
+
+ my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svcnum, } )
+ or return { 'error' => "unknown svcnum $svcnum" };
+
+ return { 'error' => "Service $svcnum does not belong to customer $custnum" }
+ unless $cust_svc->cust_pkg->custnum == $custnum;
+
+ my $conf = new FS::Conf;
+
+ return { 'svc' => $cust_svc->part_svc->svc,
+ 'error' => $cust_svc->cancel,
+ 'small_custview' =>
+ small_custview( $cust_main, $conf->config('countrydefault') ),
+ };
+
+}
+
+sub myaccount_passwd {
+ my $p = shift;
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ return { 'error' => "New passwords don't match." }
+ if $p->{'new_password'} ne $p->{'new_password2'};
+
+ return { 'error' => 'Enter new password' }
+ unless length($p->{'new_password'});
+
+ #my $search = { 'custnum' => $custnum };
+ #$search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ $custnum =~ /^(\d+)$/ or die "illegal custnum";
+ my $search = " AND custnum = $1";
+ $search .= " AND agentnum = ". $session->{'agentnum'} if $context eq 'agent';
+
+ my $svc_acct = qsearchs( {
+ 'table' => 'svc_acct',
+ 'addl_from' => 'LEFT JOIN cust_svc USING ( svcnum ) '.
+ 'LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ 'LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $p->{'svcnum'}, },
+ 'extra_sql' => $search, #important
+ } )
+ or return { 'error' => "Service not found" };
+
+ $svc_acct->_password($p->{'new_password'});
+ my $error = $svc_acct->replace();
+
+ my($label, $value) = $svc_acct->cust_svc->label;
+
+ return { 'error' => $error,
+ 'label' => $label,
+ 'value' => $value,
+ };
+
+}
+
+#--
+
+sub _custoragent_session_custnum {
+ my $p = shift;
+
+ my($context, $session, $custnum);
+ if ( $p->{'session_id'} ) {
+
+ $context = 'customer';
+ $session = _cache->get($p->{'session_id'})
+ or return ( 'error' => "Can't resume session" ); #better error message
+ $custnum = $session->{'custnum'};
+
+ } elsif ( $p->{'agent_session_id'} ) {
+
+ $context = 'agent';
+ my $agent_cache = new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::Agent',
+ } );
+ $session = $agent_cache->get($p->{'agent_session_id'})
+ or return ( 'error' => "Can't resume session" ); #better error message
+ $custnum = $p->{'custnum'};
+
+ } else {
+ return ( 'error' => "Can't resume session" ); #better error message
+ }
+
+ ($context, $session, $custnum);
+
+}
+
+1;
+
diff --git a/FS/FS/ClientAPI/PrepaidPhone.pm b/FS/FS/ClientAPI/PrepaidPhone.pm
new file mode 100644
index 0000000..00bc0ff
--- /dev/null
+++ b/FS/FS/ClientAPI/PrepaidPhone.pm
@@ -0,0 +1,253 @@
+package FS::ClientAPI::PrepaidPhone;
+
+use strict;
+use vars qw($DEBUG $me);
+use FS::Record qw(qsearchs);
+use FS::rate;
+use FS::svc_phone;
+
+$DEBUG = 0;
+$me = '[FS::ClientAPI::PrepaidPhone]';
+
+#TODO:
+# - shared-secret auth? (set a conf value)
+
+=item call_time HASHREF
+
+HASHREF contains the following parameters:
+
+=over 4
+
+=item src
+
+Source number (with countrycode)
+
+=item dst
+
+Destination number (with countrycode)
+
+=back
+
+Always returns a hashref. If there is an error, the hashref contains a single
+"error" key with the error message as a value. Otherwise, returns a hashref
+with the following keys:
+
+=over 4
+
+=item custnum
+
+Empty if no customer is found associated with the number, customer number
+otherwise.
+
+=item seconds
+
+Number of seconds remaining for a call to destination number
+
+=back
+
+=cut
+
+sub call_time {
+ my $packet = shift;
+
+ my $src = $packet->{'src'};
+ my $dst = $packet->{'dst'};
+
+ my $chargeto;
+ my $rateby;
+ #my $conf = new FS::Conf;
+ #if ( #XXX toll-free? collect?
+ # $phonenum = $dst;
+ #} else { #use the src to find the customer
+ $chargeto = $src;
+ $rateby = $dst;
+ #}
+
+ my( $countrycode, $phonenum );
+ if ( $chargeto #an interesting regex to parse out 1&2 digit countrycodes
+ =~ /^(2[078]|3[0-469]|4[013-9]|5[1-8]|6[0-6]|7|8[1-469]|9[0-58])(\d*)$/
+ || $chargeto =~ /^(\d{3})(\d*)$/
+ )
+ {
+ $countrycode = $1;
+ $phonenum = $2;
+ } else {
+ return { 'error' => "unparsable billing number: $chargeto" };
+ }
+
+
+ my $svc_phone = qsearchs('svc_phone', { 'countrycode' => $countrycode,
+ 'phonenum' => $phonenum,
+ }
+ );
+
+ unless ( $svc_phone ) {
+ return { 'error' => "can't find customer for +$countrycode $phonenum" };
+# return { 'custnum' => '',
+# 'seconds' => 0,
+# #'balance' => 0,
+# };
+ };
+
+ my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
+ my $cust_main = $cust_pkg->cust_main;
+
+ my $part_pkg = $cust_pkg->part_pkg;
+ my @part_pkg = ( $part_pkg, map $_->dst_pkg, $part_pkg->bill_part_pkg_link );
+ #XXX uuh, behavior indeterminate if you have more than one voip_cdr+prefix
+ #add-on, i guess.
+ warn "$me ". scalar(@part_pkg). ': '.
+ join('/', map { $_->plan. $_->option('rating_method') } @part_pkg )
+ if $DEBUG;
+ @part_pkg =
+ grep { $_->plan eq 'voip_cdr' && $_->option('rating_method') eq 'prefix' }
+ @part_pkg;
+
+ my %return = (
+ 'custnum' => $cust_pkg->custnum,
+ #'balance' => $cust_pkg->cust_main->balance,
+ );
+
+ warn "$me: ". scalar(@part_pkg). ': '.
+ join('/', map { $_->plan. $_->option('rating_method') } @part_pkg )
+ if $DEBUG;
+ return \%return unless @part_pkg;
+
+ warn "$me searching for rate ". $part_pkg[0]->option('ratenum')
+ if $DEBUG;
+
+ my $rate = qsearchs('rate', { 'ratenum'=>$part_pkg[0]->option('ratenum') } );
+
+ unless ( $rate ) {
+ my $error = 'ratenum '. $part_pkg[0]->option('ratenum'). ' not found';
+ warn "$me $error"
+ if $DEBUG;
+ return { 'error'=>$error };
+ }
+
+ warn "$me found rate ". $rate->ratenum
+ if $DEBUG;
+
+ #rate the call and arrive at a max # of seconds for the customer's balance
+
+ my( $rate_countrycode, $rate_phonenum );
+ if ( $rateby #this is an interesting regex to parse out 1&2 digit countrycodes
+ =~ /^(2[078]|3[0-469]|4[013-9]|5[1-8]|6[0-6]|7|8[1-469]|9[0-58])(\d*)$/
+ || $rateby =~ /^(\d{3})(\d*)$/
+ )
+ {
+ $rate_countrycode = $1;
+ $rate_phonenum = $2;
+ } else {
+ return { 'error' => "unparsable rating number: $rateby" };
+ }
+
+ my $rate_detail = $rate->dest_detail({ 'countrycode' => $rate_countrycode,
+ 'phonenum' => $rate_phonenum,
+ });
+ unless ( $rate_detail ) {
+ return { 'error'=>"can't find rate for +$rate_countrycode $rate_phonenum"};
+ }
+
+ unless ( $rate_detail->min_charge > 0 ) {
+ #XXX no charge?? return lots of seconds, a default, 0 or what?
+ #return { 'error' => '0 rate for +$rate_countrycode $rate_phonenum; prepaid service not available" };
+ #customer wants no default for now# $return{'seconds'} = 1800; #half hour?!
+ return \%return;
+ }
+
+ #XXX granularity? included minutes? another day...
+ if ( $cust_main->balance >= 0 ) {
+ return { 'error'=>'No balance' };
+ } else {
+ $return{'seconds'} = int(60 * abs($cust_main->balance) / $rate_detail->min_charge);
+ }
+
+ warn "$me returning seconds: ". $return{'seconds'};
+
+ return \%return;
+
+}
+
+=item call_time_nanpa
+
+Like I<call_time>, except countrycode 1 is not required, and all other
+countrycodes must be prefixed with 011.
+
+=cut
+
+# - everything is assumed to be countrycode 1 unless it starts with 011(ccode)
+sub call_time_nanpa {
+ my $packet = shift;
+
+ foreach (qw( src dst )) {
+ if ( $packet->{$_} =~ /^011(\d+)/ ) {
+ $packet->{$_} = $1;
+ } elsif ( $packet->{$_} !~ /^1/ ) {
+ $packet->{$_} = '1'.$packet->{$_};
+ }
+ }
+
+ call_time($packet);
+
+}
+
+=item phonenum_balance HASHREF
+
+HASHREF contains the following parameters:
+
+=over 4
+
+=item countrycode
+
+Optional countrycode. Defaults to 1.
+
+=item phonenum
+
+Phone number.
+
+=back
+
+Always returns a hashref. If there is an error, the hashref contains a single
+"error" key with the error message as a value. Otherwise, returns a hashref
+with the following keys:
+
+=over 4
+
+=item custnum
+
+Empty if no customer is found associated with the number, customer number
+otherwise.
+
+=item balance
+
+Customer balance.
+
+=back
+
+=cut
+
+sub phonenum_balance {
+ my $packet = shift;
+
+ my $svc_phone = qsearchs('svc_phone', {
+ 'countrycode' => ( $packet->{'countrycode'} || 1 ),
+ 'phonenum' => $packet->{'phonenum'},
+ });
+
+ unless ( $svc_phone ) {
+ return { 'custnum' => '',
+ 'balance' => 0,
+ };
+ };
+
+ my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
+
+ return {
+ 'custnum' => $cust_pkg->custnum,
+ 'balance' => $cust_pkg->cust_main->balance,
+ };
+
+}
+
+1;
diff --git a/FS/FS/ClientAPI/Signup.pm b/FS/FS/ClientAPI/Signup.pm
new file mode 100644
index 0000000..5569dfb
--- /dev/null
+++ b/FS/FS/ClientAPI/Signup.pm
@@ -0,0 +1,603 @@
+package FS::ClientAPI::Signup;
+
+use strict;
+use vars qw($DEBUG $me);
+use Data::Dumper;
+use Tie::RefHash;
+use FS::Conf;
+use FS::Record qw(qsearch qsearchs dbdef);
+use FS::Msgcat qw(gettext);
+use FS::Misc qw(card_types);
+use FS::ClientAPI_SessionCache;
+use FS::agent;
+use FS::cust_main_county;
+use FS::part_pkg;
+use FS::svc_acct_pop;
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::svc_acct;
+use FS::svc_phone;
+use FS::acct_snarf;
+use FS::queue;
+use FS::reg_code;
+
+$DEBUG = 0;
+$me = '[FS::ClientAPI::Signup]';
+
+sub signup_info {
+ my $packet = shift;
+
+ warn "$me signup_info called on $packet\n" if $DEBUG;
+
+ my $conf = new FS::Conf;
+ my $svc_x = $conf->config('signup_server-service') || 'svc_acct';
+
+ my $cache = new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::Signup',
+ } );
+ my $signup_info_cache = $cache->get('signup_info_cache');
+
+ if ( $signup_info_cache ) {
+
+ warn "$me loading cached signup info\n" if $DEBUG > 1;
+
+ } else {
+
+ warn "$me populating signup info cache\n" if $DEBUG > 1;
+
+ my $agentnum2part_pkg =
+ {
+ map {
+ my $agent = $_;
+ my $href = $agent->pkgpart_hashref;
+ $agent->agentnum =>
+ [
+ map { { 'payby' => [ $_->payby ],
+ 'freq_pretty' => $_->freq_pretty,
+ 'options' => { $_->options },
+ %{$_->hashref}
+ } }
+ grep { $_->svcpart($svc_x)
+ && ( $href->{ $_->pkgpart }
+ || $_->agentnum == $agent->agentnum
+ )
+ }
+ qsearch( 'part_pkg', { 'disabled' => '' } )
+ ];
+ } qsearch('agent', { 'disabled' => '' })
+ };
+
+ my $msgcat = { map { $_=>gettext($_) }
+ qw( passwords_dont_match invalid_card unknown_card_type
+ not_a empty_password illegal_or_empty_text )
+ };
+ warn "msgcat: ". Dumper($msgcat). "\n" if $DEBUG > 2;
+
+ my $label = { map { $_ => FS::Msgcat::_gettext($_) }
+ qw( stateid stateid_state )
+ };
+ warn "label: ". Dumper($label). "\n" if $DEBUG > 2;
+
+ my @agent_fields = qw( agentnum agent );
+
+ $signup_info_cache = {
+ 'cust_main_county' => [ map $_->hashref,
+ qsearch('cust_main_county', {} )
+ ],
+
+ 'agent' => [ map { my $agent = $_;
+ map { $_ => $agent->get($_) } @agent_fields;
+ }
+ qsearch('agent', { 'disabled' => '' } )
+ ],
+
+ 'part_referral' => [ map $_->hashref,
+ qsearch('part_referral', { 'disabled' => '' } )
+ ],
+
+ 'agentnum2part_pkg' => $agentnum2part_pkg,
+
+ 'svc_acct_pop' => [ map $_->hashref, qsearch('svc_acct_pop',{} ) ],
+
+ 'emailinvoiceonly' => $conf->exists('emailinvoiceonly'),
+
+ 'security_phrase' => $conf->exists('security_phrase'),
+
+ 'payby' => [ $conf->config('signup_server-payby') ],
+
+ 'card_types' => card_types(),
+
+ 'paytypes' => [ @FS::cust_main::paytypes ],
+
+ 'cvv_enabled' => 1,
+
+ 'stateid_enabled' => $conf->exists('show_stateid'),
+
+ 'paystate_enabled' => $conf->exists('show_bankstate'),
+
+ 'ship_enabled' => 1,
+
+ 'msgcat' => $msgcat,
+
+ 'label' => $label,
+
+ 'statedefault' => scalar($conf->config('statedefault')) || 'CA',
+
+ 'countrydefault' => scalar($conf->config('countrydefault')) || 'US',
+
+ 'refnum' => scalar($conf->config('signup_server-default_refnum')),
+
+ 'default_pkgpart' => scalar($conf->config('signup_server-default_pkgpart')),
+
+ 'signup_service' => $svc_x,
+ 'default_svcpart' => scalar($conf->config('signup_server-default_svcpart')),
+
+ 'head' => join("\n", $conf->config('selfservice-head') ),
+ 'body_header' => join("\n", $conf->config('selfservice-body_header') ),
+ 'body_footer' => join("\n", $conf->config('selfservice-body_footer') ),
+ 'body_bgcolor' => scalar( $conf->config('selfservice-body_bgcolor') ),
+ 'box_bgcolor' => scalar( $conf->config('selfservice-box_bgcolor') ),
+
+ 'company_name' => scalar($conf->config('company_name')),
+
+ #per-agent?
+ 'agent_ship_address' => scalar($conf->exists('agent-ship_address')),
+
+ 'no_company' => scalar($conf->exists('signup-no_company')),
+ 'require_phone' => scalar($conf->exists('cust_main-require_phone')),
+ 'recommend_daytime' => scalar($conf->exists('signup-recommend_daytime')),
+ 'recommend_email' => scalar($conf->exists('signup-recommend_email')),
+
+ };
+
+ $cache->set('signup_info_cache', $signup_info_cache);
+
+ }
+
+ my $signup_info = { %$signup_info_cache };
+ warn "$me signup info loaded\n" if $DEBUG > 1;
+ warn Dumper($signup_info). "\n" if $DEBUG > 2;
+
+ my @addl = qw( signup_server-classnum2 signup_server-classnum3 );
+
+ if ( grep { $conf->exists($_) } @addl ) {
+
+ $signup_info->{optional_packages} = [];
+
+ foreach my $addl ( @addl ) {
+
+ warn "$me adding optional package info\n" if $DEBUG > 1;
+
+ my $classnum = $conf->config($addl) or next;
+
+ my @pkgs = map { {
+ 'freq_pretty' => $_->freq_pretty,
+ 'options' => { $_->options },
+ %{ $_->hashref }
+ };
+ }
+ qsearch( 'part_pkg', { classnum => $classnum } );
+
+ push @{$signup_info->{optional_packages}}, \@pkgs;
+
+ warn "$me done adding opt. package info for $classnum\n" if $DEBUG > 1;
+
+ }
+
+ }
+
+ my $agentnum = $packet->{'agentnum'}
+ || $conf->config('signup_server-default_agentnum');
+ $agentnum =~ /^(\d*)$/ or die "illegal agentnum";
+ $agentnum = $1;
+
+ my $session = '';
+ if ( exists $packet->{'session_id'} ) {
+
+ warn "$me loading agent session\n" if $DEBUG > 1;
+ my $cache = new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::Agent',
+ } );
+ $session = $cache->get($packet->{'session_id'});
+ if ( $session ) {
+ $agentnum = $session->{'agentnum'};
+ } else {
+ return { 'error' => "Can't resume session" }; #better error message
+ }
+ warn "$me done loading agent session\n" if $DEBUG > 1;
+
+ } elsif ( exists $packet->{'customer_session_id'} ) {
+
+ warn "$me loading customer session\n" if $DEBUG > 1;
+ my $cache = new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::MyAccount',
+ } );
+ $session = $cache->get($packet->{'customer_session_id'});
+ if ( $session ) {
+ my $custnum = $session->{'custnum'};
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum });
+ return { 'error' => "Can't find your customer record" } unless $cust_main;
+ $agentnum = $cust_main->agentnum;
+ } else {
+ return { 'error' => "Can't resume session" }; #better error message
+ }
+ warn "$me done loading customer session\n" if $DEBUG > 1;
+
+ }
+
+ $signup_info->{'part_pkg'} = [];
+
+ if ( $packet->{'reg_code'} ) {
+
+ warn "$me setting package list via reg_code\n" if $DEBUG > 1;
+
+ $signup_info->{'part_pkg'} =
+ [ map { { 'payby' => [ $_->payby ],
+ 'freq_pretty' => $_->freq_pretty,
+ 'options' => { $_->options },
+ %{$_->hashref}
+ };
+ }
+ grep { $_->svcpart($svc_x) }
+ map { $_->part_pkg }
+ qsearchs( 'reg_code', { 'code' => $packet->{'reg_code'},
+ 'agentnum' => $agentnum, } )
+
+ ];
+
+ $signup_info->{'error'} = 'Unknown registration code'
+ unless @{ $signup_info->{'part_pkg'} };
+
+ warn "$me done setting package list via reg_code\n" if $DEBUG > 1;
+
+ } elsif ( $packet->{'promo_code'} ) {
+
+ warn "$me setting package list via promo_code\n" if $DEBUG > 1;
+
+ $signup_info->{'part_pkg'} =
+ [ map { { 'payby' => [ $_->payby ],
+ 'freq_pretty' => $_->freq_pretty,
+ 'options' => { $_->options },
+ %{$_->hashref}
+ } }
+ grep { $_->svcpart($svc_x) }
+ qsearch( 'part_pkg', { 'promo_code' => {
+ op=>'ILIKE',
+ value=>$packet->{'promo_code'}
+ },
+ 'disabled' => '', } )
+ ];
+
+ $signup_info->{'error'} = 'Unknown promotional code'
+ unless @{ $signup_info->{'part_pkg'} };
+
+ warn "$me done setting package list via promo_code\n" if $DEBUG > 1;
+ }
+
+ if ( $agentnum ) {
+
+ warn "$me setting agent-specific package list\n" if $DEBUG > 1;
+ $signup_info->{'part_pkg'} = $signup_info->{'agentnum2part_pkg'}{$agentnum}
+ unless @{ $signup_info->{'part_pkg'} };
+ warn "$me done setting agent-specific package list\n" if $DEBUG > 1;
+
+ warn "$me setting agent-specific adv. source list\n" if $DEBUG > 1;
+ $signup_info->{'part_referral'} =
+ [
+ map { $_->hashref }
+ qsearch( {
+ 'table' => 'part_referral',
+ 'hashref' => { 'disabled' => '' },
+ 'extra_sql' => "AND ( agentnum = $agentnum ".
+ " OR agentnum IS NULL ) ",
+ },
+ )
+ ];
+ warn "$me done setting agent-specific adv. source list\n" if $DEBUG > 1;
+
+ my $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+
+ $signup_info->{'agent_name'} = $agent->agent;
+
+ $signup_info->{'company_name'} = $conf->config('company_name', $agentnum);
+
+ if ( $signup_info->{'agent_ship_address'} && $agent->agent_custnum ) {
+ my $cust_main = $agent->agent_cust_main;
+ my $prefix = length($cust_main->ship_last) ? 'ship_' : '';
+ $signup_info->{"ship_$_"} = $cust_main->get("$prefix$_")
+ foreach qw( address1 city county state zip country );
+ }
+
+ }
+ # else {
+ # delete $signup_info->{'part_pkg'};
+ #}
+
+ warn "$me sorting package list\n" if $DEBUG > 1;
+ $signup_info->{'part_pkg'} = [ sort { $a->{pkg} cmp $b->{pkg} } # case?
+ @{ $signup_info->{'part_pkg'} }
+ ];
+ warn "$me done sorting package list\n" if $DEBUG > 1;
+
+ if ( exists $packet->{'session_id'} ) {
+ my $agent_signup_info = { %$signup_info };
+ delete $agent_signup_info->{agentnum2part_pkg};
+ $agent_signup_info->{'agent'} = $session->{'agent'};
+ $agent_signup_info;
+ } else {
+ $signup_info;
+ }
+
+}
+
+sub domain_select_hash {
+ my $packet = shift;
+
+ my $response = {};
+
+ if ($packet->{pkgpart}) {
+ my $part_pkg = qsearchs('part_pkg' => { 'pkgpart' => $packet->{pkgpart} } );
+ #$packet->{svcpart} = $part_pkg->svcpart('svc_acct')
+ $packet->{svcpart} = $part_pkg->svcpart
+ if $part_pkg;
+ }
+
+ if ($packet->{svcpart}) {
+ my $part_svc = qsearchs('part_svc' => { 'svcpart' => $packet->{svcpart} } );
+ $response->{'domsvc'} = $part_svc->part_svc_column('domsvc')->columnvalue
+ if ($part_svc && $part_svc->part_svc_column('domsvc')->columnflag eq 'D');
+ }
+
+ $response->{'domains'}
+ = { domain_select_hash FS::svc_acct( map { $_ => $packet->{$_} }
+ qw(svcpart pkgnum)
+ ) };
+
+ $response;
+}
+
+sub new_customer {
+ my $packet = shift;
+
+ my $conf = new FS::Conf;
+ my $svc_x = $conf->config('signup_server-service') || 'svc_acct';
+
+ if ( $svc_x eq 'svc_acct' ) {
+
+ #things that aren't necessary in base class, but are for signup server
+ #return "Passwords don't match"
+ # if $hashref->{'_password'} ne $hashref->{'_password2'}
+ return { 'error' => gettext('empty_password') }
+ unless length($packet->{'_password'});
+ # a bit inefficient for large numbers of pops
+ return { 'error' => gettext('no_access_number_selected') }
+ unless $packet->{'popnum'} || !scalar(qsearch('svc_acct_pop',{} ));
+
+ }
+
+ my $agentnum;
+ if ( exists $packet->{'session_id'} ) {
+ my $cache = new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::Agent',
+ } );
+ my $session = $cache->get($packet->{'session_id'});
+ if ( $session ) {
+ $agentnum = $session->{'agentnum'};
+ } else {
+ return { 'error' => "Can't resume session" }; #better error message
+ }
+ } else {
+ $agentnum = $packet->{agentnum}
+ || $conf->config('signup_server-default_agentnum');
+ }
+
+ #shares some stuff with htdocs/edit/process/cust_main.cgi... take any
+ # common that are still here and library them.
+ my $cust_main = new FS::cust_main ( {
+ #'custnum' => '',
+ 'agentnum' => $agentnum,
+ 'refnum' => $packet->{refnum}
+ || $conf->config('signup_server-default_refnum'),
+
+ map { $_ => $packet->{$_} } qw(
+
+ last first ss company address1 address2
+ city county state zip country
+ daytime night fax stateid stateid_state
+
+ ship_last ship_first ship_ss ship_company ship_address1 ship_address2
+ ship_city ship_county ship_state ship_zip ship_country
+ ship_daytime ship_night ship_fax
+
+ payby
+ payinfo paycvv paydate payname paystate paytype
+ paystart_month paystart_year payissue
+ payip
+
+ referral_custnum comments
+ )
+
+ } );
+
+ my $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+ if ( $conf->exists('agent_ship_address') && $agent->agent_custnum ) {
+ my $agent_cust_main = $agent->agent_cust_main;
+ my $prefix = length($agent_cust_main->ship_last) ? 'ship_' : '';
+ $cust_main->set("ship_$_", $agent_cust_main->get("$prefix$_") )
+ foreach qw( address1 city county state zip country );
+
+ $cust_main->set("ship_$_", $cust_main->get($_))
+ foreach qw( last first );
+
+ }
+
+
+ return { 'error' => "Illegal payment type" }
+ unless grep { $_ eq $packet->{'payby'} }
+ $conf->config('signup_server-payby');
+
+ $cust_main->payinfo($cust_main->daytime)
+ if $cust_main->payby eq 'LECB' && ! $cust_main->payinfo;
+
+ my @invoicing_list = $packet->{'invoicing_list'}
+ ? split( /\s*\,\s*/, $packet->{'invoicing_list'} )
+ : ();
+
+ $packet->{'pkgpart'} =~ /^(\d+)$/ or '' =~ /^()$/;
+ my $pkgpart = $1;
+ return { 'error' => 'Please select a package' } unless $pkgpart; #msgcat
+
+ my $part_pkg =
+ qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } )
+ or return { 'error' => "WARNING: unknown pkgpart: $pkgpart" };
+ my $svcpart = $part_pkg->svcpart($svc_x);
+
+ my $reg_code = '';
+ if ( $packet->{'reg_code'} ) {
+ $reg_code = qsearchs( 'reg_code', { 'code' => $packet->{'reg_code'},
+ 'agentnum' => $agentnum, } )
+ or return { 'error' => 'Unknown registration code' };
+ }
+
+ my $cust_pkg = new FS::cust_pkg ( {
+ #later#'custnum' => $custnum,
+ 'pkgpart' => $packet->{'pkgpart'},
+ 'promo_code' => $packet->{'promo_code'},
+ 'reg_code' => $packet->{'reg_code'},
+ } );
+ #my $error = $cust_pkg->check;
+ #return { 'error' => $error } if $error;
+
+ #should be all auto-magic and shit
+ my $svc;
+ if ( $svc_x eq 'svc_acct' ) {
+
+ $svc = new FS::svc_acct ( {
+ 'svcpart' => $svcpart,
+ map { $_ => $packet->{$_} }
+ qw( username _password sec_phrase popnum ),
+ } );
+
+ my @acct_snarf;
+ my $snarfnum = 1;
+ while ( exists($packet->{"snarf_machine$snarfnum"})
+ && length($packet->{"snarf_machine$snarfnum"}) ) {
+ my $acct_snarf = new FS::acct_snarf ( {
+ 'machine' => $packet->{"snarf_machine$snarfnum"},
+ 'protocol' => $packet->{"snarf_protocol$snarfnum"},
+ 'username' => $packet->{"snarf_username$snarfnum"},
+ '_password' => $packet->{"snarf_password$snarfnum"},
+ } );
+ $snarfnum++;
+ push @acct_snarf, $acct_snarf;
+ }
+ $svc->child_objects( \@acct_snarf );
+
+ } elsif ( $svc_x eq 'svc_phone' ) {
+
+ $svc = new FS::svc_phone ( {
+ 'svcpart' => $svcpart,
+ map { $_ => $packet->{$_} }
+ qw( countrycode phonenum sip_password pin ),
+ } );
+
+ } else {
+ die "unknown signup service $svc_x";
+ }
+
+ my $y = $svc->setdefault; # arguably should be in new method
+ return { 'error' => $y } if $y && !ref($y);
+
+ #$error = $svc->check;
+ #return { 'error' => $error } if $error;
+
+ #setup a job dependancy to delay provisioning
+ my $placeholder = new FS::queue ( {
+ 'job' => 'FS::ClientAPI::Signup::__placeholder',
+ 'status' => 'locked',
+ } );
+ my $error = $placeholder->insert;
+ return { 'error' => $error } if $error;
+
+ use Tie::RefHash;
+ tie my %hash, 'Tie::RefHash';
+ %hash = ( $cust_pkg => [ $svc ] );
+ #msgcat
+ $error = $cust_main->insert(
+ \%hash,
+ \@invoicing_list,
+ 'depend_jobnum' => $placeholder->jobnum,
+ );
+ if ( $error ) {
+ my $perror = $placeholder->delete;
+ $error .= " (Additionally, error removing placeholder: $perror)" if $perror;
+ return { 'error' => $error };
+ }
+
+ if ( $conf->exists('signup_server-realtime') ) {
+
+ #warn "[fs_signup_server] Billing customer...\n" if $Debug;
+
+ my $bill_error = $cust_main->bill;
+ #warn "[fs_signup_server] error billing new customer: $bill_error"
+ # if $bill_error;
+
+ $bill_error = $cust_main->apply_payments_and_credits;
+ #warn "[fs_signup_server] error applying payments and credits for".
+ # " new customer: $bill_error"
+ # if $bill_error;
+
+ $bill_error = $cust_main->collect('realtime' => 1);
+ #warn "[fs_signup_server] error collecting from new customer: $bill_error"
+ # if $bill_error;
+
+ if ( $cust_main->balance > 0 ) {
+
+ #this makes sense. credit is "un-doing" the invoice
+ $cust_main->credit( $cust_main->balance, 'signup server decline',
+ 'reason_type' => $conf->config('signup_credit_type'),
+ );
+ $cust_main->apply_credits;
+
+ #should check list for errors...
+ #$cust_main->suspend;
+ local $FS::svc_Common::noexport_hack = 1;
+ $cust_main->cancel('quiet'=>1);
+
+ my $perror = $placeholder->depended_delete;
+ warn "error removing provisioning jobs after decline: $perror" if $perror;
+ unless ( $perror ) {
+ $perror = $placeholder->delete;
+ warn "error removing placeholder after decline: $perror" if $perror;
+ }
+
+ return { 'error' => '_decline' };
+ }
+
+ }
+
+ if ( $reg_code ) {
+ $error = $reg_code->delete;
+ return { 'error' => $error } if $error;
+ }
+
+ $error = $placeholder->delete;
+ return { 'error' => $error } if $error;
+
+ my %return = ( 'error' => '',
+ 'signup_service' => $svc_x,
+ );
+
+ if ( $svc_x eq 'svc_acct' ) {
+ $return{$_} = $svc->$_() for qw( username _password );
+ } elsif ( $svc_x eq 'svc_phone' ) {
+ $return{$_} = $svc->$_() for qw( countrycode phonenum sip_password pin );
+ } else {
+ die "unknown signup service $svc_x";
+ }
+
+ return \%return;
+
+}
+
+1;
diff --git a/FS/FS/ClientAPI/passwd.pm b/FS/FS/ClientAPI/passwd.pm
new file mode 100644
index 0000000..b22d761
--- /dev/null
+++ b/FS/FS/ClientAPI/passwd.pm
@@ -0,0 +1,46 @@
+package FS::ClientAPI::passwd;
+
+use strict;
+use FS::Record qw(qsearchs);
+use FS::svc_acct;
+use FS::svc_domain;
+
+sub passwd {
+ my $packet = shift;
+
+ my $domain = $FS::ClientAPI::domain || $packet->{'domain'};
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
+ or return { error => "Domain $domain not found" };
+
+ my $old_password = $packet->{'old_password'};
+ my $new_password = $packet->{'new_password'};
+ my $new_gecos = $packet->{'new_gecos'};
+ my $new_shell = $packet->{'new_shell'};
+
+ #false laziness w/FS::ClientAPI::MyAccount::login
+
+ my $svc_acct = qsearchs( 'svc_acct', { 'username' => $packet->{'username'},
+ 'domsvc' => $svc_domain->svcnum, }
+ );
+ return { error => 'User not found.' } unless $svc_acct;
+ return { error => 'Incorrect password.' }
+ unless $svc_acct->check_password($old_password);
+
+ my %hash = $svc_acct->hash;
+ my $new_svc_acct = new FS::svc_acct ( \%hash );
+ $new_svc_acct->setfield('_password', $new_password )
+ if $new_password && $new_password ne $old_password;
+ $new_svc_acct->setfield('finger',$new_gecos) if $new_gecos;
+ $new_svc_acct->setfield('shell',$new_shell) if $new_shell;
+ my $error = $new_svc_acct->replace($svc_acct);
+
+ return { error => $error };
+
+}
+
+sub chfn {}
+
+sub chsh {}
+
+1;
+
diff --git a/FS/FS/ClientAPI_SessionCache.pm b/FS/FS/ClientAPI_SessionCache.pm
new file mode 100644
index 0000000..d72fb39
--- /dev/null
+++ b/FS/FS/ClientAPI_SessionCache.pm
@@ -0,0 +1,79 @@
+package FS::ClientAPI_SessionCache;
+
+use strict;
+use vars qw($module);
+use FS::UID qw(datasrc);
+use FS::Conf;
+
+#ask FS::UID to run this stuff for us later
+install_callback FS::UID sub {
+ my $conf = new FS::Conf;
+ $module = $conf->config('selfservice_server-cache_module')
+ || 'Cache::FileCache';
+};
+
+=head1 NAME
+
+FS::ClientAPI_SessionCache;
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Minimal Cache::Cache-alike interface for storing session cache information.
+Backends to Cache::SharedMemoryCache, Cache::FileCache, or an internal
+implementation which stores information in the clientapi_session and
+clientapi_session_field database tables.
+
+=head1 METHODS
+
+=over 4
+
+=item new
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ unless ( $module =~ /^_Database$/ ) {
+ eval "use $module;";
+ die $@ if $@;
+ my $self = $module->new(@_);
+ $self->set_cache_root('%%%FREESIDE_CACHE%%%/clientapi_session.'.datasrc)
+ if $module =~ /^Cache::FileCache$/;
+ $self;
+ } else {
+ my $self = shift;
+ bless ($self, $class);
+ }
+}
+
+sub get {
+ my($self, $session_id) = @_;
+ die '_Database self-service session cache not yet implemented';
+}
+
+sub set {
+ my($self, $session_id, $session, $expiration) = @_;
+ die '_Database self-service session cache not yet implemented';
+}
+
+sub remove {
+ my($self, $session_id) = @_;
+ die '_Database self-service session cache not yet implemented';
+}
+
+=back
+
+=head1 BUGS
+
+Minimal documentation.
+
+=head1 SEE ALSO
+
+L<Cache::Cache>, L<FS::clientapi_session>, L<FS::clientapi_session_field>
+
+=cut
+
+1;
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
new file mode 100644
index 0000000..b869302
--- /dev/null
+++ b/FS/FS/Conf.pm
@@ -0,0 +1,2679 @@
+package FS::Conf;
+
+use vars qw($base_dir @config_items @base_items @card_types $DEBUG);
+use Carp;
+use IO::File;
+use File::Basename;
+use MIME::Base64;
+use FS::ConfItem;
+use FS::ConfDefaults;
+use FS::Conf_compat17;
+use FS::conf;
+use FS::Record qw(qsearch qsearchs);
+use FS::UID qw(dbh datasrc use_confcompat);
+
+$base_dir = '%%%FREESIDE_CONF%%%';
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::Conf - Freeside configuration values
+
+=head1 SYNOPSIS
+
+ use FS::Conf;
+
+ $conf = new FS::Conf;
+
+ $value = $conf->config('key');
+ @list = $conf->config('key');
+ $bool = $conf->exists('key');
+
+ $conf->touch('key');
+ $conf->set('key' => 'value');
+ $conf->delete('key');
+
+ @config_items = $conf->config_items;
+
+=head1 DESCRIPTION
+
+Read and write Freeside configuration values. Keys currently map to filenames,
+but this may change in the future.
+
+=head1 METHODS
+
+=over 4
+
+=item new
+
+Create a new configuration object.
+
+=cut
+
+sub new {
+ my($proto) = @_;
+ my($class) = ref($proto) || $proto;
+ my($self) = { 'base_dir' => $base_dir };
+ bless ($self, $class);
+}
+
+=item base_dir
+
+Returns the base directory. By default this is /usr/local/etc/freeside.
+
+=cut
+
+sub base_dir {
+ my($self) = @_;
+ my $base_dir = $self->{base_dir};
+ -e $base_dir or die "FATAL: $base_dir doesn't exist!";
+ -d $base_dir or die "FATAL: $base_dir isn't a directory!";
+ -r $base_dir or die "FATAL: Can't read $base_dir!";
+ -x $base_dir or die "FATAL: $base_dir not searchable (executable)!";
+ $base_dir =~ /^(.*)$/;
+ $1;
+}
+
+=item config KEY [ AGENTNUM ]
+
+Returns the configuration value or values (depending on context) for key.
+The optional agent number selects an agent specific value instead of the
+global default if one is present.
+
+=cut
+
+sub _usecompat {
+ my ($self, $method) = (shift, shift);
+ carp "NO CONFIGURATION RECORDS FOUND -- USING COMPATIBILITY MODE"
+ if use_confcompat;
+ my $compat = new FS::Conf_compat17 ("$base_dir/conf." . datasrc);
+ $compat->$method(@_);
+}
+
+sub _config {
+ my($self,$name,$agentnum)=@_;
+ my $hashref = { 'name' => $name };
+ $hashref->{agentnum} = $agentnum;
+ local $FS::Record::conf = undef; # XXX evil hack prevents recursion
+ my $cv = FS::Record::qsearchs('conf', $hashref);
+ if (!$cv && defined($agentnum) && $agentnum) {
+ $hashref->{agentnum} = '';
+ $cv = FS::Record::qsearchs('conf', $hashref);
+ }
+ return $cv;
+}
+
+sub config {
+ my $self = shift;
+ return $self->_usecompat('config', @_) if use_confcompat;
+
+ my($name, $agentnum)=@_;
+
+ carp "FS::Conf->config($name, $agentnum) called"
+ if $DEBUG > 1;
+
+ my $cv = $self->_config($name, $agentnum) or return;
+
+ if ( wantarray ) {
+ my $v = $cv->value;
+ chomp $v;
+ (split "\n", $v, -1);
+ } else {
+ (split("\n", $cv->value))[0];
+ }
+}
+
+=item config_binary KEY [ AGENTNUM ]
+
+Returns the exact scalar value for key.
+
+=cut
+
+sub config_binary {
+ my $self = shift;
+ return $self->_usecompat('config_binary', @_) if use_confcompat;
+
+ my($name,$agentnum)=@_;
+ my $cv = $self->_config($name, $agentnum) or return;
+ decode_base64($cv->value);
+}
+
+=item exists KEY [ AGENTNUM ]
+
+Returns true if the specified key exists, even if the corresponding value
+is undefined.
+
+=cut
+
+sub exists {
+ my $self = shift;
+ return $self->_usecompat('exists', @_) if use_confcompat;
+
+ my($name, $agentnum)=@_;
+
+ carp "FS::Conf->exists($name, $agentnum) called"
+ if $DEBUG > 1;
+
+ defined($self->_config($name, $agentnum));
+}
+
+=item config_orbase KEY SUFFIX
+
+Returns the configuration value or values (depending on context) for
+KEY_SUFFIX, if it exists, otherwise for KEY
+
+=cut
+
+# outmoded as soon as we shift to agentnum based config values
+# well, mostly. still useful for e.g. late notices, etc. in that we want
+# these to fall back to standard values
+sub config_orbase {
+ my $self = shift;
+ return $self->_usecompat('config_orbase', @_) if use_confcompat;
+
+ my( $name, $suffix ) = @_;
+ if ( $self->exists("${name}_$suffix") ) {
+ $self->config("${name}_$suffix");
+ } else {
+ $self->config($name);
+ }
+}
+
+=item key_orbase KEY SUFFIX
+
+If the config value KEY_SUFFIX exists, returns KEY_SUFFIX, otherwise returns
+KEY. Useful for determining which exact configuration option is returned by
+config_orbase.
+
+=cut
+
+sub key_orbase {
+ my $self = shift;
+ #no compat for this...return $self->_usecompat('config_orbase', @_) if use_confcompat;
+
+ my( $name, $suffix ) = @_;
+ if ( $self->exists("${name}_$suffix") ) {
+ "${name}_$suffix";
+ } else {
+ $name;
+ }
+}
+
+=item invoice_templatenames
+
+Returns all possible invoice template names.
+
+=cut
+
+sub invoice_templatenames {
+ my( $self ) = @_;
+
+ my %templatenames = ();
+ foreach my $item ( $self->config_items ) {
+ foreach my $base ( @base_items ) {
+ my( $main, $ext) = split(/\./, $base);
+ $ext = ".$ext" if $ext;
+ if ( $item->key =~ /^${main}_(.+)$ext$/ ) {
+ $templatenames{$1}++;
+ }
+ }
+ }
+
+ sort keys %templatenames;
+
+}
+
+=item touch KEY [ AGENT ];
+
+Creates the specified configuration key if it does not exist.
+
+=cut
+
+sub touch {
+ my $self = shift;
+ return $self->_usecompat('touch', @_) if use_confcompat;
+
+ my($name, $agentnum) = @_;
+ unless ( $self->exists($name, $agentnum) ) {
+ $self->set($name, '', $agentnum);
+ }
+}
+
+=item set KEY VALUE [ AGENTNUM ];
+
+Sets the specified configuration key to the given value.
+
+=cut
+
+sub set {
+ my $self = shift;
+ return $self->_usecompat('set', @_) if use_confcompat;
+
+ my($name, $value, $agentnum) = @_;
+ $value =~ /^(.*)$/s;
+ $value = $1;
+
+ warn "[FS::Conf] SET $name\n" if $DEBUG;
+
+ my $old = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum});
+ my $new = new FS::conf { $old ? $old->hash
+ : ('name' => $name, 'agentnum' => $agentnum)
+ };
+ $new->value($value);
+
+ my $error;
+ if ($old) {
+ $error = $new->replace($old);
+ } else {
+ $error = $new->insert;
+ }
+
+ die "error setting configuration value: $error \n"
+ if $error;
+
+}
+
+=item set_binary KEY VALUE [ AGENTNUM ]
+
+Sets the specified configuration key to an exact scalar value which
+can be retrieved with config_binary.
+
+=cut
+
+sub set_binary {
+ my $self = shift;
+ return if use_confcompat;
+
+ my($name, $value, $agentnum)=@_;
+ $self->set($name, encode_base64($value), $agentnum);
+}
+
+=item delete KEY [ AGENTNUM ];
+
+Deletes the specified configuration key.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return $self->_usecompat('delete', @_) if use_confcompat;
+
+ my($name, $agentnum) = @_;
+ if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum}) ) {
+ warn "[FS::Conf] DELETE $name\n";
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $cv->delete;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die "error setting configuration value: $error \n"
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ }
+}
+
+=item import_config_item CONFITEM DIR
+
+ Imports the item specified by the CONFITEM (see L<FS::ConfItem>) into
+the database as a conf record (see L<FS::conf>). Imports from the file
+in the directory DIR.
+
+=cut
+
+sub import_config_item {
+ my ($self,$item,$dir) = @_;
+ my $key = $item->key;
+ if ( -e "$dir/$key" && ! use_confcompat ) {
+ warn "Inserting $key\n" if $DEBUG;
+ local $/;
+ my $value = readline(new IO::File "$dir/$key");
+ if ($item->type =~ /^(binary|image)$/ ) {
+ $self->set_binary($key, $value);
+ }else{
+ $self->set($key, $value);
+ }
+ }else {
+ warn "Not inserting $key\n" if $DEBUG;
+ }
+}
+
+=item verify_config_item CONFITEM DIR
+
+ Compares the item specified by the CONFITEM (see L<FS::ConfItem>) in
+the database to the legacy file value in DIR.
+
+=cut
+
+sub verify_config_item {
+ return '' if use_confcompat;
+ my ($self,$item,$dir) = @_;
+ my $key = $item->key;
+ my $type = $item->type;
+
+ my $compat = new FS::Conf_compat17 $dir;
+ my $error = '';
+
+ $error .= "$key fails existential comparison; "
+ if $self->exists($key) xor $compat->exists($key);
+
+ if ( $type !~ /^(binary|image)$/ ) {
+
+ {
+ no warnings;
+ $error .= "$key fails scalar comparison; "
+ unless scalar($self->config($key)) eq scalar($compat->config($key));
+ }
+
+ my (@new) = $self->config($key);
+ my (@old) = $compat->config($key);
+ unless ( scalar(@new) == scalar(@old)) {
+ $error .= "$key fails list comparison; ";
+ }else{
+ my $r=1;
+ foreach (@old) { $r=0 if ($_ cmp shift(@new)); }
+ $error .= "$key fails list comparison; "
+ unless $r;
+ }
+
+ } else {
+
+ $error .= "$key fails binary comparison; "
+ unless scalar($self->config_binary($key)) eq scalar($compat->config_binary($key));
+
+ }
+
+#remove deprecated config on our own terms, not freeside-upgrade's
+# if ($error =~ /existential comparison/ && $item->section eq 'deprecated') {
+# my $proto;
+# for ( @config_items ) { $proto = $_; last if $proto->key eq $key; }
+# unless ($proto->key eq $key) {
+# warn "removed config item $error\n" if $DEBUG;
+# $error = '';
+# }
+# }
+
+ $error;
+}
+
+#item _orbase_items OPTIONS
+#
+#Returns all of the possible extensible config items as FS::ConfItem objects.
+#See #L<FS::ConfItem>. OPTIONS consists of name value pairs. Possible
+#options include
+#
+# dir - the directory to search for configuration option files instead
+# of using the conf records in the database
+#
+#cut
+
+#quelle kludge
+sub _orbase_items {
+ my ($self, %opt) = @_;
+
+ my $listmaker = sub { my $v = shift;
+ $v =~ s/_/!_/g;
+ if ( $v =~ /\.(png|eps)$/ ) {
+ $v =~ s/\./!_%./;
+ }else{
+ $v .= '!_%';
+ }
+ map { $_->name }
+ FS::Record::qsearch( 'conf',
+ {},
+ '',
+ "WHERE name LIKE '$v' ESCAPE '!'"
+ );
+ };
+
+ if (exists($opt{dir}) && $opt{dir}) {
+ $listmaker = sub { my $v = shift;
+ if ( $v =~ /\.(png|eps)$/ ) {
+ $v =~ s/\./_*./;
+ }else{
+ $v .= '_*';
+ }
+ map { basename $_ } glob($opt{dir}. "/$v" );
+ };
+ }
+
+ ( map {
+ my $proto;
+ my $base = $_;
+ for ( @config_items ) { $proto = $_; last if $proto->key eq $base; }
+ die "don't know about $base items" unless $proto->key eq $base;
+
+ map { new FS::ConfItem {
+ 'key' => $_,
+ 'section' => $proto->section,
+ 'description' => 'Alternate ' . $proto->description . ' See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Invoice_templates">billing documentation</a> for details.',
+ 'type' => $proto->type,
+ };
+ } &$listmaker($base);
+ } @base_items,
+ );
+}
+
+=item config_items
+
+Returns all of the possible global/default configuration items as
+FS::ConfItem objects. See L<FS::ConfItem>.
+
+=cut
+
+sub config_items {
+ my $self = shift;
+ return $self->_usecompat('config_items', @_) if use_confcompat;
+
+ ( @config_items, $self->_orbase_items(@_) );
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item init-config DIR
+
+Imports the configuration items from DIR (1.7 compatible)
+to conf records in the database.
+
+=cut
+
+sub init_config {
+ my $dir = shift;
+
+ {
+ local $FS::UID::use_confcompat = 0;
+ my $conf = new FS::Conf;
+ foreach my $item ( $conf->config_items(dir => $dir) ) {
+ $conf->import_config_item($item, $dir);
+ my $error = $conf->verify_config_item($item, $dir);
+ return $error if $error;
+ }
+
+ my $compat = new FS::Conf_compat17 $dir;
+ foreach my $item ( $compat->config_items ) {
+ my $error = $conf->verify_config_item($item, $dir);
+ return $error if $error;
+ }
+ }
+
+ $FS::UID::use_confcompat = 0;
+ ''; #success
+}
+
+=back
+
+=head1 BUGS
+
+If this was more than just crud that will never be useful outside Freeside I'd
+worry that config_items is freeside-specific and icky.
+
+=head1 SEE ALSO
+
+"Configuration" in the web interface (config/config.cgi).
+
+=cut
+
+#Business::CreditCard
+@card_types = (
+ "VISA card",
+ "MasterCard",
+ "Discover card",
+ "American Express card",
+ "Diner's Club/Carte Blanche",
+ "enRoute",
+ "JCB",
+ "BankCard",
+ "Switch",
+ "Solo",
+);
+
+@base_items = qw (
+ invoice_template
+ invoice_latex
+ invoice_latexreturnaddress
+ invoice_latexfooter
+ invoice_latexsmallfooter
+ invoice_latexnotes
+ invoice_latexcoupon
+ invoice_html
+ invoice_htmlreturnaddress
+ invoice_htmlfooter
+ invoice_htmlnotes
+ logo.png
+ logo.eps
+ );
+
+@config_items = map { new FS::ConfItem $_ } (
+
+ {
+ 'key' => 'address',
+ 'section' => 'deprecated',
+ 'description' => 'This configuration option is no longer used. See <a href="#invoice_template">invoice_template</a> instead.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'alerter_template',
+ 'section' => 'billing',
+ 'description' => 'Template file for billing method expiration alerts. See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Credit_cards_and_Electronic_checks">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ 'per-agent' => 1,
+ },
+
+ {
+ 'key' => 'apacheip',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>apache</i> <a href="../browse/part_export.cgi">export</a> instead. Used to be the current IP address to assign to new virtual hosts',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'encryption',
+ 'section' => 'billing',
+ 'description' => 'Enable encryption of credit cards.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'encryptionmodule',
+ 'section' => 'billing',
+ 'description' => 'Use which module for encryption?',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'encryptionpublickey',
+ 'section' => 'billing',
+ 'description' => 'Your RSA Public Key - Required if Encryption is turned on.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'encryptionprivatekey',
+ 'section' => 'billing',
+ 'description' => 'Your RSA Private Key - Including this will enable the "Bill Now" feature. However if the system is compromised, a hacker can use this key to decode the stored credit card information. This is generally not a good idea.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'business-onlinepayment',
+ 'section' => 'billing',
+ 'description' => '<a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support, at least three lines: processor, login, and password. An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\'). Optional additional lines are passed to Business::OnlinePayment as %processor_options.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-ach',
+ 'section' => 'billing',
+ 'description' => 'Alternate <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support for ACH transactions (defaults to regular <b>business-onlinepayment</b>). At least three lines: processor, login, and password. An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\'). Optional additional lines are passed to Business::OnlinePayment as %processor_options.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-description',
+ 'section' => 'billing',
+ 'description' => 'String passed as the description field to <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a>. Evaluated as a double-quoted perl string, with the following variables available: <code>$agent</code> (the agent name), and <code>$pkgs</code> (a comma-separated list of packages for which these charges apply)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-email-override',
+ 'section' => 'billing',
+ 'description' => 'Email address used instead of customer email address when submitting a BOP transaction.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-email_customer',
+ 'section' => 'billing',
+ 'description' => 'Controls the "email_customer" flag used by some Business::OnlinePayment processors to enable customer receipts.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'countrydefault',
+ 'section' => 'UI',
+ 'description' => 'Default two-letter country code (if not supplied, the default is `US\')',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'date_format',
+ 'section' => 'UI',
+ 'description' => 'Format for displaying dates',
+ 'type' => 'select',
+ 'select_hash' => [
+ '%m/%d/%Y' => 'MM/DD/YYYY',
+ '%Y/%m/%d' => 'YYYY/MM/DD',
+ ],
+ },
+
+ {
+ 'key' => 'deletecustomers',
+ 'section' => 'UI',
+ 'description' => 'Enable customer deletions. Be very careful! Deleting a customer will remove all traces that this customer ever existed! It should probably only be used when auditing a legacy database. Normally, you cancel all of a customers\' packages if they cancel service.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'deletepayments',
+ 'section' => 'billing',
+ 'description' => 'Enable deletion of unclosed payments. Really, with voids this is pretty much not recommended in any situation anymore. Be very careful! Only delete payments that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a payment is deleted.',
+ 'type' => [qw( checkbox text )],
+ },
+
+ {
+ 'key' => 'deletecredits',
+ 'section' => 'deprecated',
+ 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable deletion of unclosed credits. Be very careful! Only delete credits that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a credit is deleted.',
+ 'type' => [qw( checkbox text )],
+ },
+
+ {
+ 'key' => 'deleterefunds',
+ 'section' => 'billing',
+ 'description' => 'Enable deletion of unclosed refunds. Be very careful! Only delete refunds that were data-entry errors, not adjustments.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'dirhash',
+ 'section' => 'shell',
+ 'description' => 'Optional numeric value to control directory hashing. If positive, hashes directories for the specified number of levels from the front of the username. If negative, hashes directories for the specified number of levels from the end of the username. Some examples: <ul><li>1: user -> <a href="#home">/home</a>/u/user<li>2: user -> <a href="#home">/home</a>/u/s/user<li>-1: user -> <a href="#home">/home</a>/r/user<li>-2: user -> <a href="#home">home</a>/r/e/user</ul>',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'disable_customer_referrals',
+ 'section' => 'UI',
+ 'description' => 'Disable new customer-to-customer referrals in the web interface',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'editreferrals',
+ 'section' => 'UI',
+ 'description' => 'Enable advertising source modification for existing customers',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emailinvoiceonly',
+ 'section' => 'billing',
+ 'description' => 'Disables postal mail invoices',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'disablepostalinvoicedefault',
+ 'section' => 'billing',
+ 'description' => 'Disables postal mail invoices as the default option in the UI. Be careful not to setup customers which are not sent invoices. See <a href ="#emailinvoiceauto">emailinvoiceauto</a>.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emailinvoiceauto',
+ 'section' => 'billing',
+ 'description' => 'Automatically adds new accounts to the email invoice list',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emailinvoiceautoalways',
+ 'section' => 'billing',
+ 'description' => 'Automatically adds new accounts to the email invoice list even when the list contains email addresses',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'exclude_ip_addr',
+ 'section' => '',
+ 'description' => 'Exclude these from the list of available broadband service IP addresses. (One per line)',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'auto_router',
+ 'section' => '',
+ 'description' => 'Automatically choose the correct router/block based on supplied ip address when possible while provisioning broadband services',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'hidecancelledpackages',
+ 'section' => 'UI',
+ 'description' => 'Prevent cancelled packages from showing up in listings (though they will still be in the database)',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'hidecancelledcustomers',
+ 'section' => 'UI',
+ 'description' => 'Prevent customers with only cancelled packages from showing up in listings (though they will still be in the database)',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'home',
+ 'section' => 'shell',
+ 'description' => 'For new users, prefixed to username to create a directory name. Should have a leading but not a trailing slash.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'invoice_from',
+ 'section' => 'required',
+ 'description' => 'Return address on email invoices',
+ 'type' => 'text',
+ 'per_agent' => 1,
+ },
+
+ {
+ 'key' => 'invoice_subject',
+ 'section' => 'billing',
+ 'description' => 'Subject: header on email invoices. Defaults to "Invoice". The following substitutions are available: $name, $name_short, $invoice_number, and $invoice_date.',
+ 'type' => 'text',
+ 'per_agent' => 1,
+ },
+
+ {
+ 'key' => 'invoice_template',
+ 'section' => 'billing',
+ 'description' => 'Text template file for invoices. Used if no invoice_html template is defined, and also seen by users using non-HTML capable mail clients. See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Plaintext_invoice_templates">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_html',
+ 'section' => 'billing',
+ 'description' => 'Optional HTML template for invoices. See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#HTML_invoice_templates">billing documentation</a> for details.',
+
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlnotes',
+ 'section' => 'billing',
+ 'description' => 'Notes section for HTML invoices. Defaults to the same data in invoice_latexnotes if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlfooter',
+ 'section' => 'billing',
+ 'description' => 'Footer for HTML invoices. Defaults to the same data in invoice_latexfooter if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlreturnaddress',
+ 'section' => 'billing',
+ 'description' => 'Return address for HTML invoices. Defaults to the same data in invoice_latexreturnaddress if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latex',
+ 'section' => 'billing',
+ 'description' => 'Optional LaTeX template for typeset PostScript invoices. See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Typeset_.28LaTeX.29_invoice_templates">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexnotes',
+ 'section' => 'billing',
+ 'description' => 'Notes section for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexfooter',
+ 'section' => 'billing',
+ 'description' => 'Footer for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexcoupon',
+ 'section' => 'billing',
+ 'description' => 'Remittance coupon for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexreturnaddress',
+ 'section' => 'billing',
+ 'description' => 'Return address for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexsmallfooter',
+ 'section' => 'billing',
+ 'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_email_pdf',
+ 'section' => 'billing',
+ 'description' => 'Send PDF invoice as an attachment to emailed invoices. By default, includes the plain text invoice as the email body, unless invoice_email_pdf_note is set.',
+ 'type' => 'checkbox'
+ },
+
+ {
+ 'key' => 'invoice_email_pdf_note',
+ 'section' => 'billing',
+ 'description' => 'If defined, this text will replace the default plain text invoice as the body of emailed PDF invoices.',
+ 'type' => 'textarea'
+ },
+
+
+ {
+ 'key' => 'invoice_default_terms',
+ 'section' => 'billing',
+ 'description' => 'Optional default invoice term, used to calculate a due date printed on invoices.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'Payable upon receipt', 'Net 0', 'Net 10', 'Net 15', 'Net 20', 'Net 30', 'Net 45', 'Net 60' ],
+ },
+
+ {
+ 'key' => 'invoice_sections',
+ 'section' => 'billing',
+ 'description' => 'Split invoice into sections and label according to package class when enabled.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'separate_usage',
+ 'section' => 'billing',
+ 'description' => 'Split the rated call usage into a separate line from the recurring charges.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'payment_receipt_email',
+ 'section' => 'billing',
+ 'description' => 'Template file for payment receipts. Payment receipts are sent to the customer email invoice destination(s) when a payment is received. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available: <ul><li><code>$date</code> <li><code>$name</code> <li><code>$paynum</code> - Freeside payment number <li><code>$paid</code> - Amount of payment <li><code>$payby</code> - Payment type (Card, Check, Electronic check, etc.) <li><code>$payinfo</code> - Masked credit card number or check number <li><code>$balance</code> - New balance</ul>',
+ 'type' => [qw( checkbox textarea )],
+ },
+
+ {
+ 'key' => 'lpr',
+ 'section' => 'required',
+ 'description' => 'Print command for paper invoices, for example `lpr -h\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'lpr-postscript_prefix',
+ 'section' => 'billing',
+ 'description' => 'Raw printer commands prepended to the beginning of postscript print jobs (evaluated as a double-quoted perl string - backslash escapes are available)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'lpr-postscript_suffix',
+ 'section' => 'billing',
+ 'description' => 'Raw printer commands added to the end of postscript print jobs (evaluated as a double-quoted perl string - backslash escapes are available)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'money_char',
+ 'section' => '',
+ 'description' => 'Currency symbol - defaults to `$\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'defaultrecords',
+ 'section' => 'BIND',
+ 'description' => 'DNS entries to add automatically when creating a domain',
+ 'type' => 'editlist',
+ 'editlist_parts' => [ { type=>'text' },
+ { type=>'immutable', value=>'IN' },
+ { type=>'select',
+ select_enum=>{ map { $_=>$_ } qw(A CNAME MX NS TXT)} },
+ { type=> 'text' }, ],
+ },
+
+ {
+ 'key' => 'passwordmin',
+ 'section' => 'password',
+ 'description' => 'Minimum password length (default 6)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'passwordmax',
+ 'section' => 'password',
+ 'description' => 'Maximum password length (default 8) (don\'t set this over 12 if you need to import or export crypt() passwords)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'password-noampersand',
+ 'section' => 'password',
+ 'description' => 'Disallow ampersands in passwords',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'password-noexclamation',
+ 'section' => 'password',
+ 'description' => 'Disallow exclamations in passwords (Not setting this could break old text Livingston or Cistron Radius servers)',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'referraldefault',
+ 'section' => 'UI',
+ 'description' => 'Default referral, specified by refnum',
+ 'type' => 'text',
+ },
+
+# {
+# 'key' => 'registries',
+# 'section' => 'required',
+# 'description' => 'Directory which contains domain registry information. Each registry is a directory.',
+# },
+
+ {
+ 'key' => 'maxsearchrecordsperpage',
+ 'section' => 'UI',
+ 'description' => 'If set, number of search records to return per page.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'session-start',
+ 'section' => 'session',
+ 'description' => 'If defined, the command which is executed on the Freeside machine when a session begins. The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'session-stop',
+ 'section' => 'session',
+ 'description' => 'If defined, the command which is executed on the Freeside machine when a session ends. The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'shells',
+ 'section' => 'shell',
+ 'description' => 'Legal shells (think /etc/shells). You probably want to `cut -d: -f7 /etc/passwd | sort | uniq\' initially so that importing doesn\'t fail with `Illegal shell\' errors, then remove any special entries afterwords. A blank line specifies that an empty shell is permitted.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'showpasswords',
+ 'section' => 'UI',
+ 'description' => 'Display unencrypted user passwords in the backend (employee) web interface',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signupurl',
+ 'section' => 'UI',
+ 'description' => 'if you are using customer-to-customer referrals, and you enter the URL of your <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Self-Service_Installation">signup server CGI</a>, the customer view screen will display a customized link to the signup server with the appropriate customer as referral',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'smtpmachine',
+ 'section' => 'required',
+ 'description' => 'SMTP relay for Freeside\'s outgoing mail',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soadefaultttl',
+ 'section' => 'BIND',
+ 'description' => 'SOA default TTL for new domains.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soaemail',
+ 'section' => 'BIND',
+ 'description' => 'SOA email for new domains, in BIND form (`.\' instead of `@\'), with trailing `.\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soaexpire',
+ 'section' => 'BIND',
+ 'description' => 'SOA expire for new domains',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soamachine',
+ 'section' => 'BIND',
+ 'description' => 'SOA machine for new domains, with trailing `.\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soarefresh',
+ 'section' => 'BIND',
+ 'description' => 'SOA refresh for new domains',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soaretry',
+ 'section' => 'BIND',
+ 'description' => 'SOA retry for new domains',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'statedefault',
+ 'section' => 'UI',
+ 'description' => 'Default state or province (if not supplied, the default is `CA\')',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'unsuspendauto',
+ 'section' => 'billing',
+ 'description' => 'Enables the automatic unsuspension of suspended packages when a customer\'s balance due changes from positive to zero or negative as the result of a payment or credit',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'unsuspend-always_adjust_next_bill_date',
+ 'section' => 'billing',
+ 'description' => 'Global override that causes unsuspensions to always adjust the next bill date under any circumstances. This is now controlled on a per-package bases - probably best not to use this option unless you are a legacy installation that requires this behaviour.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'usernamemin',
+ 'section' => 'username',
+ 'description' => 'Minimum username length (default 2)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'usernamemax',
+ 'section' => 'username',
+ 'description' => 'Maximum username length',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'username-ampersand',
+ 'section' => 'username',
+ 'description' => 'Allow the ampersand character (&amp;) in usernames. Be careful when using this option in conjunction with <a href="../browse/part_export.cgi">exports</a> which execute shell commands, as the ampersand will be interpreted by the shell if not quoted.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-letter',
+ 'section' => 'username',
+ 'description' => 'Usernames must contain at least one letter',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-letterfirst',
+ 'section' => 'username',
+ 'description' => 'Usernames must start with a letter',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-noperiod',
+ 'section' => 'username',
+ 'description' => 'Disallow periods in usernames',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-nounderscore',
+ 'section' => 'username',
+ 'description' => 'Disallow underscores in usernames',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-nodash',
+ 'section' => 'username',
+ 'description' => 'Disallow dashes in usernames',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-uppercase',
+ 'section' => 'username',
+ 'description' => 'Allow uppercase characters in usernames. Not recommended for use with FreeRADIUS with MySQL backend, which is case-insensitive by default.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-percent',
+ 'section' => 'username',
+ 'description' => 'Allow the percent character (%) in usernames.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'safe-part_bill_event',
+ 'section' => 'UI',
+ 'description' => 'Validates invoice event expressions against a preset list. Useful for webdemos, annoying to powerusers.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'show_ss',
+ 'section' => 'UI',
+ 'description' => 'Turns on display/collection of social security numbers in the web interface. Sometimes required by electronic check (ACH) processors.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'show_stateid',
+ 'section' => 'UI',
+ 'description' => "Turns on display/collection of driver's license/state issued id numbers in the web interface. Sometimes required by electronic check (ACH) processors.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'show_bankstate',
+ 'section' => 'UI',
+ 'description' => "Turns on display/collection of state for bank accounts in the web interface. Sometimes required by electronic check (ACH) processors.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'agent_defaultpkg',
+ 'section' => 'UI',
+ 'description' => 'Setting this option will cause new packages to be available to all agent types by default.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'legacy_link',
+ 'section' => 'UI',
+ 'description' => 'Display options in the web interface to link legacy pre-Freeside services.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'legacy_link-steal',
+ 'section' => 'UI',
+ 'description' => 'Allow "stealing" an already-audited service from one customer (or package) to another using the link function.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'queue_dangerous_controls',
+ 'section' => 'UI',
+ 'description' => 'Enable queue modification controls on account pages and for new jobs. Unless you are a developer working on new export code, you should probably leave this off to avoid causing provisioning problems.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'security_phrase',
+ 'section' => 'password',
+ 'description' => 'Enable the tracking of a "security phrase" with each account. Not recommended, as it is vulnerable to social engineering.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'locale',
+ 'section' => 'UI',
+ 'description' => 'Message locale',
+ 'type' => 'select',
+ 'select_enum' => [ qw(en_US) ],
+ },
+
+ {
+ 'key' => 'signup_server-payby',
+ 'section' => '',
+ 'description' => 'Acceptable payment types for the signup server',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB PREPAY BILL COMP) ],
+ },
+
+ {
+ 'key' => 'signup_server-default_agentnum',
+ 'section' => '',
+ 'description' => 'Default agent for the signup server',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::agent;
+ map { $_->agentnum => $_->agent }
+ FS::Record::qsearch('agent', { disabled=>'' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::agent;
+ my $agent = FS::Record::qsearchs(
+ 'agent', { 'agentnum'=>shift }
+ );
+ $agent ? $agent->agent : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-default_refnum',
+ 'section' => '',
+ 'description' => 'Default advertising source for the signup server',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::part_referral;
+ map { $_->refnum => $_->referral }
+ FS::Record::qsearch( 'part_referral',
+ { 'disabled' => '' }
+ );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::part_referral;
+ my $part_referral = FS::Record::qsearchs(
+ 'part_referral', { 'refnum'=>shift } );
+ $part_referral ? $part_referral->referral : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-default_pkgpart',
+ 'section' => '',
+ 'description' => 'Default package for the signup server',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ map { $_->pkgpart => $_->pkg.' - '.$_->comment }
+ FS::Record::qsearch( 'part_pkg',
+ { 'disabled' => ''}
+ );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ my $part_pkg = FS::Record::qsearchs(
+ 'part_pkg', { 'pkgpart'=>shift }
+ );
+ $part_pkg
+ ? $part_pkg->pkg.' - '.$part_pkg->comment
+ : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-default_svcpart',
+ 'section' => '',
+ 'description' => 'Default svcpart for the signup server - only necessary for services that trigger special provisioning widgets (such as DID provisioning).',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::part_svc;
+ map { $_->svcpart => $_->svc }
+ FS::Record::qsearch( 'part_svc',
+ { 'disabled' => ''}
+ );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::part_svc;
+ my $part_svc = FS::Record::qsearchs(
+ 'part_svc', { 'svcpart'=>shift }
+ );
+ $part_svc ? $part_svc->svc : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-service',
+ 'section' => '',
+ 'description' => 'Service for the signup server - "Account (svc_acct)" is the default setting, or "Phone number (svc_phone)" for ITSP signup',
+ 'type' => 'select',
+ 'select_hash' => [
+ 'svc_acct' => 'Account (svc_acct)',
+ 'svc_phone' => 'Phone number (svc_phone)',
+ ],
+ },
+
+ {
+ 'key' => 'selfservice_server-base_url',
+ 'section' => '',
+ 'description' => 'Base URL for the self-service web interface - necessary for special provisioning widgets to find their way.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'show-msgcat-codes',
+ 'section' => 'UI',
+ 'description' => 'Show msgcat codes in error messages. Turn this option on before reporting errors to the mailing list.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signup_server-realtime',
+ 'section' => '',
+ 'description' => 'Run billing for signup server signups immediately, and do not provision accounts which subsequently have a balance.',
+ 'type' => 'checkbox',
+ },
+ {
+ 'key' => 'signup_server-classnum2',
+ 'section' => '',
+ 'description' => 'Package Class for first optional purchase',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ map { $_->classnum => $_->classname }
+ FS::Record::qsearch('pkg_class', {} );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ my $pkg_class = FS::Record::qsearchs(
+ 'pkg_class', { 'classnum'=>shift }
+ );
+ $pkg_class ? $pkg_class->classname : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-classnum3',
+ 'section' => '',
+ 'description' => 'Package Class for second optional purchase',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ map { $_->classnum => $_->classname }
+ FS::Record::qsearch('pkg_class', {} );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ my $pkg_class = FS::Record::qsearchs(
+ 'pkg_class', { 'classnum'=>shift }
+ );
+ $pkg_class ? $pkg_class->classname : '';
+ },
+ },
+
+ {
+ 'key' => 'backend-realtime',
+ 'section' => '',
+ 'description' => 'Run billing for backend signups immediately.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'declinetemplate',
+ 'section' => 'billing',
+ 'description' => 'Template file for credit card decline emails.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'emaildecline',
+ 'section' => 'billing',
+ 'description' => 'Enable emailing of credit card decline notices.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emaildecline-exclude',
+ 'section' => 'billing',
+ 'description' => 'List of error messages that should not trigger email decline notices, one per line.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cancelmessage',
+ 'section' => 'billing',
+ 'description' => 'Template file for cancellation emails.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cancelsubject',
+ 'section' => 'billing',
+ 'description' => 'Subject line for cancellation emails.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'emailcancel',
+ 'section' => 'billing',
+ 'description' => 'Enable emailing of cancellation notices. Make sure to fill in the cancelmessage and cancelsubject configuration values as well.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'require_cardname',
+ 'section' => 'billing',
+ 'description' => 'Require an "Exact name on card" to be entered explicitly; don\'t default to using the first and last name.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'enable_taxclasses',
+ 'section' => 'billing',
+ 'description' => 'Enable per-package tax classes',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'require_taxclasses',
+ 'section' => 'billing',
+ 'description' => 'Require a taxclass to be entered for every package',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'enable_taxproducts',
+ 'section' => 'billing',
+ 'description' => 'Enable per-package mapping to new style tax classes',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'ignore_incalculable_taxes',
+ 'section' => 'billing',
+ 'description' => 'Prefer to invoice without tax over not billing at all',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'welcome_email',
+ 'section' => '',
+ 'description' => 'Template file for welcome email. Welcome emails are sent to the customer email invoice destination(s) each time a svc_acct record is created. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code></ul>',
+ 'type' => 'textarea',
+ 'per_agent' => 1,
+ },
+
+ {
+ 'key' => 'welcome_email-from',
+ 'section' => '',
+ 'description' => 'From: address header for welcome email',
+ 'type' => 'text',
+ 'per_agent' => 1,
+ },
+
+ {
+ 'key' => 'welcome_email-subject',
+ 'section' => '',
+ 'description' => 'Subject: header for welcome email',
+ 'type' => 'text',
+ 'per_agent' => 1,
+ },
+
+ {
+ 'key' => 'welcome_email-mimetype',
+ 'section' => '',
+ 'description' => 'MIME type for welcome email',
+ 'type' => 'select',
+ 'select_enum' => [ 'text/plain', 'text/html' ],
+ 'per_agent' => 1,
+ },
+
+ {
+ 'key' => 'welcome_letter',
+ 'section' => '',
+ 'description' => 'Optional LaTex template file for a printed welcome letter. A welcome letter is printed the first time a cust_pkg record is created. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation and the billing documentation for details on the template substitution language. A variable exists for each fieldname in the customer record (<code>$first, $last, etc</code>). The following additional variables are available<ul><li><code>$payby</code> - a friendler represenation of the field<li><code>$payinfo</code> - the masked payment information<li><code>$expdate</code> - the time at which the payment method expires (a UNIX timestamp)<li><code>$returnaddress</code> - the invoice return address for this customer\'s agent</ul>',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'warning_email',
+ 'section' => '',
+ 'description' => 'Template file for warning email. Warning emails are sent to the customer email invoice destination(s) each time a svc_acct record has its usage drop below a threshold or 0. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code> <li><code>$column</code> <li><code>$amount</code> <li><code>$threshold</code></ul>',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'warning_email-from',
+ 'section' => '',
+ 'description' => 'From: address header for warning email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-cc',
+ 'section' => '',
+ 'description' => 'Additional recipient(s) (comma separated) for warning email when remaining usage reaches zero.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-subject',
+ 'section' => '',
+ 'description' => 'Subject: header for warning email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-mimetype',
+ 'section' => '',
+ 'description' => 'MIME type for warning email',
+ 'type' => 'select',
+ 'select_enum' => [ 'text/plain', 'text/html' ],
+ },
+
+ {
+ 'key' => 'payby',
+ 'section' => 'billing',
+ 'description' => 'Available payment types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD COMP) ],
+ },
+
+ {
+ 'key' => 'payby-default',
+ 'section' => 'UI',
+ 'description' => 'Default payment type. HIDE disables display of billing information and sets customers to BILL.',
+ 'type' => 'select',
+ 'select_enum' => [ '', qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD COMP HIDE) ],
+ },
+
+ {
+ 'key' => 'paymentforcedtobatch',
+ 'section' => 'deprecated',
+ 'description' => 'See batch-enable_payby and realtime-disable_payby. Used to (for CHEK): Cause per customer payment entry to be forced to a batch processor rather than performed realtime.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-notes',
+ 'section' => 'UI',
+ 'description' => 'Extra HTML to be displayed on the Account View screen.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'radius-password',
+ 'section' => '',
+ 'description' => 'RADIUS attribute for plain-text passwords.',
+ 'type' => 'select',
+ 'select_enum' => [ 'Password', 'User-Password' ],
+ },
+
+ {
+ 'key' => 'radius-ip',
+ 'section' => '',
+ 'description' => 'RADIUS attribute for IP addresses.',
+ 'type' => 'select',
+ 'select_enum' => [ 'Framed-IP-Address', 'Framed-Address' ],
+ },
+
+ {
+ 'key' => 'svc_acct-alldomains',
+ 'section' => '',
+ 'description' => 'Allow accounts to select any domain in the database. Normally accounts can only select from the domain set in the service definition and those purchased by the customer.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'dump-scpdest',
+ 'section' => '',
+ 'description' => 'destination for scp database dumps: user@host:/path',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'dump-pgpid',
+ 'section' => '',
+ 'description' => "Optional PGP public key user or key id for database dumps. The public key should exist on the freeside user's public keyring, and the gpg binary and GnuPG perl module should be installed.",
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cvv-save',
+ 'section' => 'billing',
+ 'description' => 'Save CVV2 information after the initial transaction for the selected credit card types. Enabling this option may be in violation of your merchant agreement(s), so please check them carefully before enabling this option for any credit card types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => \@card_types,
+ },
+
+ {
+ 'key' => 'allow_negative_charges',
+ 'section' => 'billing',
+ 'description' => 'Allow negative charges. Normally not used unless importing data from a legacy system that requires this.',
+ 'type' => 'checkbox',
+ },
+ {
+ 'key' => 'auto_unset_catchall',
+ 'section' => '',
+ 'description' => 'When canceling a svc_acct that is the email catchall for one or more svc_domains, automatically set their catchall fields to null. If this option is not set, the attempt will simply fail.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'system_usernames',
+ 'section' => 'username',
+ 'description' => 'A list of system usernames that cannot be edited or removed, one per line. Use a bare username to prohibit modification/deletion of the username in any domain, or username@domain to prohibit modification/deletetion of a specific username and domain.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cust_pkg-change_svcpart',
+ 'section' => '',
+ 'description' => "When changing packages, move services even if svcparts don't match between old and new pacakge definitions.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'disable_autoreverse',
+ 'section' => 'BIND',
+ 'description' => 'Disable automatic synchronization of reverse-ARPA entries.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_www-enable_subdomains',
+ 'section' => '',
+ 'description' => 'Enable selection of specific subdomains for virtual host creation.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_www-usersvc_svcpart',
+ 'section' => '',
+ 'description' => 'Allowable service definition svcparts for virtual hosts, one per line.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'selfservice_server-primary_only',
+ 'section' => '',
+ 'description' => 'Only allow primary accounts to access self-service functionality.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'selfservice_server-phone_login',
+ 'section' => '',
+ 'description' => 'Allow login to self-service with phone number and PIN.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'selfservice_server-single_domain',
+ 'section' => '',
+ 'description' => 'If specified, only use this one domain for self-service access.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'card_refund-days',
+ 'section' => 'billing',
+ 'description' => 'After a payment, the number of days a refund link will be available for that payment. Defaults to 120.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'agent-showpasswords',
+ 'section' => '',
+ 'description' => 'Display unencrypted user passwords in the agent (reseller) interface',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'global_unique-username',
+ 'section' => 'username',
+ 'description' => 'Global username uniqueness control: none (usual setting - check uniqueness per exports), username (all usernames are globally unique, regardless of domain or exports), or username@domain (all username@domain pairs are globally unique, regardless of exports). disabled turns off duplicate checking completely and is STRONGLY NOT RECOMMENDED unless you REALLY need to turn this off.',
+ 'type' => 'select',
+ 'select_enum' => [ 'none', 'username', 'username@domain', 'disabled' ],
+ },
+
+ {
+ 'key' => 'global_unique-phonenum',
+ 'section' => '',
+ 'description' => 'Global phone number uniqueness control: none (usual setting - check countrycode+phonenumun uniqueness per exports), or countrycode+phonenum (all countrycode+phonenum pairs are globally unique, regardless of exports). disabled turns off duplicate checking completely and is STRONGLY NOT RECOMMENDED unless you REALLY need to turn this off.',
+ 'type' => 'select',
+ 'select_enum' => [ 'none', 'countrycode+phonenum', 'disabled' ],
+ },
+
+ {
+ 'key' => 'svc_external-skip_manual',
+ 'section' => 'UI',
+ 'description' => 'When provisioning svc_external services, skip manual entry of id and title fields in the UI. Usually used in conjunction with an export that populates these fields (i.e. artera_turbo).',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_external-display_type',
+ 'section' => 'UI',
+ 'description' => 'Select a specific svc_external type to enable some UI changes specific to that type (i.e. artera_turbo).',
+ 'type' => 'select',
+ 'select_enum' => [ 'generic', 'artera_turbo', ],
+ },
+
+ {
+ 'key' => 'ticket_system',
+ 'section' => '',
+ 'description' => 'Ticketing system integration. <b>RT_Internal</b> uses the built-in RT ticketing system (see the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:RT_Installation">integrated ticketing installation instructions</a>). <b>RT_External</b> accesses an external RT installation in a separate database (local or remote).',
+ 'type' => 'select',
+ #'select_enum' => [ '', qw(RT_Internal RT_Libs RT_External) ],
+ 'select_enum' => [ '', qw(RT_Internal RT_External) ],
+ },
+
+ {
+ 'key' => 'ticket_system-default_queueid',
+ 'section' => '',
+ 'description' => 'Default queue used when creating new customer tickets.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub {
+ my $conf = new FS::Conf;
+ if ( $conf->config('ticket_system') ) {
+ eval "use FS::TicketSystem;";
+ die $@ if $@;
+ FS::TicketSystem->queues();
+ } else {
+ ();
+ }
+ },
+ 'option_sub' => sub {
+ my $conf = new FS::Conf;
+ if ( $conf->config('ticket_system') ) {
+ eval "use FS::TicketSystem;";
+ die $@ if $@;
+ FS::TicketSystem->queue(shift);
+ } else {
+ '';
+ }
+ },
+ },
+
+ {
+ 'key' => 'ticket_system-priority_reverse',
+ 'section' => '',
+ 'description' => 'Enable this to consider lower numbered priorities more important. A bad habit we picked up somewhere. You probably want to avoid it and use the default.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'ticket_system-custom_priority_field',
+ 'section' => '',
+ 'description' => 'Custom field from the ticketing system to use as a custom priority classification.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'ticket_system-custom_priority_field-values',
+ 'section' => '',
+ 'description' => 'Values for the custom field from the ticketing system to break down and sort customer ticket lists.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'ticket_system-custom_priority_field_queue',
+ 'section' => '',
+ 'description' => 'Ticketing system queue in which the custom field specified in ticket_system-custom_priority_field is located.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'ticket_system-rt_external_datasrc',
+ 'section' => '',
+ 'description' => 'With external RT integration, the DBI data source for the external RT installation, for example, <code>DBI:Pg:user=rt_user;password=rt_word;host=rt.example.com;dbname=rt</code>',
+ 'type' => 'text',
+
+ },
+
+ {
+ 'key' => 'ticket_system-rt_external_url',
+ 'section' => '',
+ 'description' => 'With external RT integration, the URL for the external RT installation, for example, <code>https://rt.example.com/rt</code>',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'company_name',
+ 'section' => 'required',
+ 'description' => 'Your company name',
+ 'type' => 'text',
+ 'per_agent' => 1, #XXX just FS/FS/ClientAPI/Signup.pm
+ },
+
+ {
+ 'key' => 'company_address',
+ 'section' => 'required',
+ 'description' => 'Your company address',
+ 'type' => 'textarea',
+ 'per_agent' => 1,
+ },
+
+ {
+ 'key' => 'address2-search',
+ 'section' => 'UI',
+ 'description' => 'Enable a "Unit" search box which searches the second address field. Useful for multi-tenant applications. See also: cust_main-require_address2',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-require_address2',
+ 'section' => 'UI',
+ 'description' => 'Second address field is required (on service address only, if billing and service addresses differ). Also enables "Unit" labeling of address2 on customer view and edit pages. Useful for multi-tenant applications. See also: address2-search',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'agent-ship_address',
+ 'section' => '',
+ 'description' => "Use the agent's master service address as the service address (only ship_address2 can be entered, if blank on the master address). Useful for multi-tenant applications.",
+ 'type' => 'checkbox',
+ },
+
+ { 'key' => 'referral_credit',
+ 'section' => 'deprecated',
+ 'description' => "Used to enable one-time referral credits in the amount of one month <i>referred</i> customer's recurring fee (irregardless of frequency). Replace with a billing event on appropriate packages.",
+ 'type' => 'checkbox',
+ },
+
+ { 'key' => 'selfservice_server-cache_module',
+ 'section' => '',
+ 'description' => 'Module used to store self-service session information. All modules handle any number of self-service servers. Cache::SharedMemoryCache is appropriate for a single database / single Freeside server. Cache::FileCache is useful for multiple databases on a single server, or when IPC::ShareLite is not available (i.e. FreeBSD).', # _Database stores session information in the database and is appropriate for multiple Freeside servers, but may be slower.',
+ 'type' => 'select',
+ 'select_enum' => [ 'Cache::SharedMemoryCache', 'Cache::FileCache', ], # '_Database' ],
+ },
+
+ {
+ 'key' => 'hylafax',
+ 'section' => 'billing',
+ 'description' => 'Options for a HylaFAX server to enable the FAX invoice destination. They should be in the form of a space separated list of arguments to the Fax::Hylafax::Client::sendfax subroutine. You probably shouldn\'t override things like \'docfile\'. *Note* Only supported when using typeset invoices (see the invoice_latex configuration option).',
+ 'type' => [qw( checkbox textarea )],
+ },
+
+ {
+ 'key' => 'cust_bill-ftpformat',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - format.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'default', 'billco', ],
+ },
+
+ {
+ 'key' => 'cust_bill-ftpserver',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-ftpusername',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-ftppassword',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-ftpdir',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-spoolformat',
+ 'section' => 'billing',
+ 'description' => 'Enable spooling of raw invoice data - format.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'default', 'billco', ],
+ },
+
+ {
+ 'key' => 'cust_bill-spoolagent',
+ 'section' => 'billing',
+ 'description' => 'Enable per-agent spooling of raw invoice data.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-usage_suspend',
+ 'section' => 'billing',
+ 'description' => 'Suspends the package an account belongs to when svc_acct.seconds or a bytecount is decremented to 0 or below (accounts with an empty seconds and up|down|totalbytes value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-usage_unsuspend',
+ 'section' => 'billing',
+ 'description' => 'Unuspends the package an account belongs to when svc_acct.seconds or a bytecount is incremented from 0 or below to a positive value (accounts with an empty seconds and up|down|totalbytes value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-usage_threshold',
+ 'section' => 'billing',
+ 'description' => 'The threshold (expressed as percentage) of acct.seconds or acct.up|down|totalbytes at which a warning message is sent to a service holder. Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd. Defaults to 80.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust-fields',
+ 'section' => 'UI',
+ 'description' => 'Which customer fields to display on reports by default',
+ 'type' => 'select',
+ 'select_hash' => [ FS::ConfDefaults->cust_fields_avail() ],
+ },
+
+ {
+ 'key' => 'cust_pkg-display_times',
+ 'section' => 'UI',
+ 'description' => 'Display full timestamps (not just dates) for customer packages. Useful if you are doing real-time things like hourly prepaid.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_pkg-always_show_location',
+ 'section' => 'UI',
+ 'description' => "Always display package locations, even when they're all the default service address.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-edit_uid',
+ 'section' => 'shell',
+ 'description' => 'Allow UID editing.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-edit_gid',
+ 'section' => 'shell',
+ 'description' => 'Allow GID editing.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'zone-underscore',
+ 'section' => 'BIND',
+ 'description' => 'Allow underscores in zone names. As underscores are illegal characters in zone names, this option is not recommended.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'echeck-nonus',
+ 'section' => 'billing',
+ 'description' => 'Disable ABA-format account checking for Electronic Check payment info',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'voip-cust_cdr_spools',
+ 'section' => '',
+ 'description' => 'Enable the per-customer option for individual CDR spools.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'voip-cust_cdr_squelch',
+ 'section' => '',
+ 'description' => 'Enable the per-customer option for not printing CDR on invoices.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_forward-arbitrary_dst',
+ 'section' => '',
+ 'description' => "Allow forwards to point to arbitrary strings that don't necessarily look like email addresses. Only used when using forwards for weird, non-email things.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'tax-ship_address',
+ 'section' => 'billing',
+ 'description' => 'By default, tax calculations are done based on the billing address. Enable this switch to calculate tax based on the shipping address instead.',
+ 'type' => 'checkbox',
+ }
+,
+ {
+ 'key' => 'tax-pkg_address',
+ 'section' => 'billing',
+ 'description' => 'By default, tax calculations are done based on the billing address. Enable this switch to calculate tax based on the package address instead (when present).',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'invoice-ship_address',
+ 'section' => 'billing',
+ 'description' => 'Enable this switch to include the ship address on the invoice.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'invoice-unitprice',
+ 'section' => 'billing',
+ 'description' => 'This switch enables unit pricing on the invoice.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'postal_invoice-fee_pkgpart',
+ 'section' => 'billing',
+ 'description' => 'This allows selection of a package to insert on invoices for customers with postal invoices selected.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ map { $_->pkgpart => $_->pkg }
+ FS::Record::qsearch('part_pkg', { disabled=>'' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ my $part_pkg = FS::Record::qsearchs(
+ 'part_pkg', { 'pkgpart'=>shift }
+ );
+ $part_pkg ? $part_pkg->pkg : '';
+ },
+ },
+
+ {
+ 'key' => 'postal_invoice-recurring_only',
+ 'section' => 'billing',
+ 'description' => 'The postal invoice fee is omitted on invoices without reucrring charges when this is set.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'batch-enable',
+ 'section' => 'deprecated', #make sure batch-enable_payby is set for
+ #everyone before removing
+ 'description' => 'Enable credit card and/or ACH batching - leave disabled for real-time installations.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'batch-enable_payby',
+ 'section' => 'billing',
+ 'description' => 'Enable batch processing for the specified payment types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [qw( CARD CHEK )],
+ },
+
+ {
+ 'key' => 'realtime-disable_payby',
+ 'section' => 'billing',
+ 'description' => 'Disable realtime processing for the specified payment types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [qw( CARD CHEK )],
+ },
+
+ {
+ 'key' => 'batch-default_format',
+ 'section' => 'billing',
+ 'description' => 'Default format for batches.',
+ 'type' => 'select',
+ 'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch',
+ 'csv-chase_canada-E-xactBatch', 'BoM', 'PAP',
+ 'ach-spiritone',
+ ]
+ },
+
+ {
+ 'key' => 'batch-fixed_format-CARD',
+ 'section' => 'billing',
+ 'description' => 'Fixed (unchangeable) format for credit card batches.',
+ 'type' => 'select',
+ 'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM', 'PAP' ,
+ 'csv-chase_canada-E-xactBatch', 'BoM', 'PAP' ]
+ },
+
+ {
+ 'key' => 'batch-fixed_format-CHEK',
+ 'section' => 'billing',
+ 'description' => 'Fixed (unchangeable) format for electronic check batches.',
+ 'type' => 'select',
+ 'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM', 'PAP',
+ 'ach-spiritone',
+ ]
+ },
+
+ {
+ 'key' => 'batch-increment_expiration',
+ 'section' => 'billing',
+ 'description' => 'Increment expiration date years in batches until cards are current. Make sure this is acceptable to your batching provider before enabling.',
+ 'type' => 'checkbox'
+ },
+
+ {
+ 'key' => 'batchconfig-BoM',
+ 'section' => 'billing',
+ 'description' => 'Configuration for Bank of Montreal batching, seven lines: 1. Origin ID, 2. Datacenter, 3. Typecode, 4. Short name, 5. Long name, 6. Bank, 7. Bank account',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'batchconfig-PAP',
+ 'section' => 'billing',
+ 'description' => 'Configuration for PAP batching, seven lines: 1. Origin ID, 2. Datacenter, 3. Typecode, 4. Short name, 5. Long name, 6. Bank, 7. Bank account',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'batchconfig-csv-chase_canada-E-xactBatch',
+ 'section' => 'billing',
+ 'description' => 'Gateway ID for Chase Canada E-xact batching',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'payment_history-years',
+ 'section' => 'UI',
+ 'description' => 'Number of years of payment history to show by default. Currently defaults to 2.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_main-use_comments',
+ 'section' => 'UI',
+ 'description' => 'Display free form comments on the customer edit screen. Useful as a scratch pad.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-disable_notes',
+ 'section' => 'UI',
+ 'description' => 'Disable new style customer notes - timestamped and user identified customer notes. Useful in tracking who did what.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main_note-display_times',
+ 'section' => 'UI',
+ 'description' => 'Display full timestamps (not just dates) for customer notes.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-ticket_statuses',
+ 'section' => 'UI',
+ 'description' => 'Show tickets with these statuses on the customer view page.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [qw( new open stalled resolved rejected deleted )],
+ },
+
+ {
+ 'key' => 'cust_main-max_tickets',
+ 'section' => 'UI',
+ 'description' => 'Maximum number of tickets to show on the customer view page.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_main-skeleton_tables',
+ 'section' => '',
+ 'description' => 'Tables which will have skeleton records inserted into them for each customer. Syntax for specifying tables is unfortunately a tricky perl data structure for now.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cust_main-skeleton_custnum',
+ 'section' => '',
+ 'description' => 'Customer number specifying the source data to copy into skeleton tables for new customers.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_main-enable_birthdate',
+ 'section' => 'UI',
+ 'descritpion' => 'Enable tracking of a birth date with each customer record',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'support-key',
+ 'section' => '',
+ 'description' => 'A support key enables access to commercial services delivered over the network, such as the payroll module, access to the internal ticket system, priority support and optional backups.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'card-types',
+ 'section' => 'billing',
+ 'description' => 'Select one or more card types to enable only those card types. If no card types are selected, all card types are available.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => \@card_types,
+ },
+
+ {
+ 'key' => 'disable-fuzzy',
+ 'section' => 'UI',
+ 'description' => 'Disable fuzzy searching. Speeds up searching for large sites, but only shows exact matches.',
+ 'type' => 'checkbox',
+ },
+
+ { 'key' => 'pkg_referral',
+ 'section' => '',
+ 'description' => 'Enable package-specific advertising sources.',
+ 'type' => 'checkbox',
+ },
+
+ { 'key' => 'pkg_referral-multiple',
+ 'section' => '',
+ 'description' => 'In addition, allow multiple advertising sources to be associated with a single package.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'dashboard-toplist',
+ 'section' => 'UI',
+ 'description' => 'List of items to display on the top of the front page',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'impending_recur_template',
+ 'section' => 'billing',
+ 'description' => 'Template file for alerts about looming first time recurrant billing. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitition language. Also see packages with a <a href="../browse/part_pkg.cgi">flat price plan</a> The following variables are available<ul><li><code>$packages</code> allowing <code>$packages->[0]</code> thru <code>$packages->[n]</code> <li><code>$package</code> the first package, same as <code>$packages->[0]</code> <li><code>$recurdates</code> allowing <code>$recurdates->[0]</code> thru <code>$recurdates->[n]</code> <li><code>$recurdate</code> the first recurdate, same as <code>$recurdate->[0]</code> <li><code>$first</code> <li><code>$last</code></ul>',
+# <li><code>$payby</code> <li><code>$expdate</code> most likely only confuse
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'logo.png',
+ 'section' => 'billing', #?
+ 'description' => 'Company logo for HTML invoices and the backoffice interface, in PNG format. Suggested size somewhere near 92x62.',
+ 'type' => 'image',
+ 'per_agent' => 1, #XXX just view/logo.cgi, which is for the global
+ #old-style editor anyway...?
+ },
+
+ {
+ 'key' => 'logo.eps',
+ 'section' => 'billing', #?
+ 'description' => 'Company logo for printed and PDF invoices, in EPS format.',
+ 'type' => 'binary',
+ 'per_agent' => 1, #XXX as above, kinda
+ },
+
+ {
+ 'key' => 'selfservice-ignore_quantity',
+ 'section' => '',
+ 'description' => 'Ignores service quantity restrictions in self-service context. Strongly not recommended - just set your quantities correctly in the first place.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'selfservice-session_timeout',
+ 'section' => '',
+ 'description' => 'Self-service session timeout. Defaults to 1 hour.',
+ 'type' => 'select',
+ 'select_enum' => [ '1 hour', '2 hours', '4 hours', '8 hours', '1 day', '1 week', ],
+ },
+
+ {
+ 'key' => 'disable_setup_suspended_pkgs',
+ 'section' => 'billing',
+ 'description' => 'Disables charging of setup fees for suspended packages.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'password-generated-allcaps',
+ 'section' => 'password',
+ 'description' => 'Causes passwords automatically generated to consist entirely of capital letters',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'datavolume-forcemegabytes',
+ 'section' => 'UI',
+ 'description' => 'All data volumes are expressed in megabytes',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'datavolume-significantdigits',
+ 'section' => 'UI',
+ 'description' => 'number of significant digits to use to represent data volumes',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'disable_void_after',
+ 'section' => 'billing',
+ 'description' => 'Number of seconds after which freeside won\'t attempt to VOID a payment first when performing a refund.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'disable_line_item_date_ranges',
+ 'section' => 'billing',
+ 'description' => 'Prevent freeside from automatically generating date ranges on invoice line items.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'support_packages',
+ 'section' => '',
+ 'description' => 'A list of packages eligible for RT ticket time transfer, one pkgpart per line.', #this should really be a select multiple, or specified in the packages themselves...
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cust_main-require_phone',
+ 'section' => '',
+ 'description' => 'Require daytime or night phone for all customer records.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-require_invoicing_list_email',
+ 'section' => '',
+ 'description' => 'Email address field is required: require at least one invoicing email address for all customer records.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-display_paid_time_remaining',
+ 'section' => '',
+ 'description' => 'Show paid time remaining in addition to time remaining.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cancel_credit_type',
+ 'section' => 'billing',
+ 'description' => 'The group to use for new, automatically generated credit reasons resulting from cancellation.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ map { $_->typenum => $_->type }
+ FS::Record::qsearch('reason_type', { class=>'R' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ my $reason_type = FS::Record::qsearchs(
+ 'reason_type', { 'typenum' => shift }
+ );
+ $reason_type ? $reason_type->type : '';
+ },
+ },
+
+ {
+ 'key' => 'referral_credit_type',
+ 'section' => 'deprecated',
+ 'description' => 'Used to be the group to use for new, automatically generated credit reasons resulting from referrals. Now set in a package billing event for the referral.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ map { $_->typenum => $_->type }
+ FS::Record::qsearch('reason_type', { class=>'R' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ my $reason_type = FS::Record::qsearchs(
+ 'reason_type', { 'typenum' => shift }
+ );
+ $reason_type ? $reason_type->type : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_credit_type',
+ 'section' => 'billing',
+ 'description' => 'The group to use for new, automatically generated credit reasons resulting from signup and self-service declines.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ map { $_->typenum => $_->type }
+ FS::Record::qsearch('reason_type', { class=>'R' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ my $reason_type = FS::Record::qsearchs(
+ 'reason_type', { 'typenum' => shift }
+ );
+ $reason_type ? $reason_type->type : '';
+ },
+ },
+
+ {
+ 'key' => 'cust_main-agent_custid-format',
+ 'section' => '',
+ 'description' => 'Enables searching of various formatted values in cust_main.agent_custid',
+ 'type' => 'select',
+ 'select_hash' => [
+ '' => 'Numeric only',
+ 'ww?d+' => 'Numeric with one or two letter prefix',
+ ],
+ },
+
+ {
+ 'key' => 'card_masking_method',
+ 'section' => 'UI',
+ 'description' => 'Digits to display when masking credit cards. Note that the first six digits are necessary to canonically identify the credit card type (Visa/MC, Amex, Discover, Maestro, etc.) in all cases. The first four digits can identify the most common credit card types in most cases (Visa/MC, Amex, and Discover). The first two digits can distinguish between Visa/MC and Amex. Note: You should manually remove stored paymasks if you change this value on an existing database, to avoid problems using stored cards.',
+ 'type' => 'select',
+ 'select_hash' => [
+ '' => '123456xxxxxx1234',
+ 'first6last2' => '123456xxxxxxxx12',
+ 'first4last4' => '1234xxxxxxxx1234',
+ 'first4last2' => '1234xxxxxxxxxx12',
+ 'first2last4' => '12xxxxxxxxxx1234',
+ 'first2last2' => '12xxxxxxxxxxxx12',
+ 'first0last4' => 'xxxxxxxxxxxx1234',
+ 'first0last2' => 'xxxxxxxxxxxxxx12',
+ ],
+ },
+
+ {
+ 'key' => 'disable_previous_balance',
+ 'section' => 'billing',
+ 'description' => 'Disable inclusion of previous balancem payment, and credit lines on invoices',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'usps_webtools-userid',
+ 'section' => 'UI',
+ 'description' => 'Production UserID for USPS web tools. Enables USPS address standardization. See the <a href="http://www.usps.com/webtools/">USPS website</a>, register and agree not to use the tools for batch purposes.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'usps_webtools-password',
+ 'section' => 'UI',
+ 'description' => 'Production password for USPS web tools. Enables USPS address standardization. See <a href="http://www.usps.com/webtools/">USPS website</a>, register and agree not to use the tools for batch purposes.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_main-auto_standardize_address',
+ 'section' => 'UI',
+ 'description' => 'When using USPS web tools, automatically standardize the address without asking.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'disable_acl_changes',
+ 'section' => '',
+ 'description' => 'Disable all ACL changes, for demos.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-edit_agent_custid',
+ 'section' => 'UI',
+ 'description' => 'Enable editing of the agent_custid field.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-default_agent_custid',
+ 'section' => 'UI',
+ 'description' => 'Display the agent_custid field instead of the custnum field.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-auto_agent_custid',
+ 'section' => 'UI',
+ 'description' => 'Automatically assign an agent_custid - select format',
+ 'type' => 'select',
+ 'select_hash' => [ '' => 'No',
+ '1YMMXXXXXXXX' => '1YMMXXXXXXXX',
+ ],
+ },
+
+ {
+ 'key' => 'cust_main-default_areacode',
+ 'section' => 'UI',
+ 'description' => 'Default area code for customers.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'mcp_svcpart',
+ 'section' => '',
+ 'description' => 'Master Control Program svcpart. Leave this blank.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-max_same_services',
+ 'section' => 'billing',
+ 'description' => 'Maximum number of the same service to list individually on invoices before condensing to a single line listing the number of services. Defaults to 5.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'suspend_email_admin',
+ 'section' => '',
+ 'description' => 'Destination admin email address to enable suspension notices',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'email_report-subject',
+ 'section' => '',
+ 'description' => 'Subject for reports emailed by freeside-fetch. Defaults to "Freeside report".',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'selfservice-head',
+ 'section' => '',
+ 'description' => 'HTML for the HEAD section of the self-service interface, typically used for LINK stylesheet tags',
+ 'type' => 'textarea', #htmlarea?
+ },
+
+
+ {
+ 'key' => 'selfservice-body_header',
+ 'section' => '',
+ 'description' => 'HTML header for the self-service interface',
+ 'type' => 'textarea', #htmlarea?
+ },
+
+ {
+ 'key' => 'selfservice-body_footer',
+ 'section' => '',
+ 'description' => 'HTML header for the self-service interface',
+ 'type' => 'textarea', #htmlarea?
+ },
+
+
+ {
+ 'key' => 'selfservice-body_bgcolor',
+ 'section' => '',
+ 'description' => 'HTML background color for the self-service interface, for example, #FFFFFF',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'selfservice-box_bgcolor',
+ 'section' => '',
+ 'description' => 'HTML color for self-service interface input boxes, for example, #C0C0C0"',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'signup-no_company',
+ 'section' => '',
+ 'description' => "Don't display a field for company name on signup.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signup-recommend_email',
+ 'section' => '',
+ 'description' => 'Encourage the entry of an invoicing email address on signup.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signup-recommend_daytime',
+ 'section' => '',
+ 'description' => 'Encourage the entry of a daytime phone number invoicing email address on signup.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_phone-radius-default_password',
+ 'section' => '',
+ 'description' => 'Default password when exporting svc_phone records to RADIUS',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'svc_phone-allow_alpha_phonenum',
+ 'section' => '',
+ 'description' => 'Allow letters in phone numbers.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'default_phone_countrycode',
+ 'section' => '',
+ 'description' => 'Default countrcode',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cdr-charged_party-accountcode',
+ 'section' => '',
+ 'description' => 'Set the charged_party field of CDRs to the accountcode.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cdr-charged_party_rewrite',
+ 'section' => '',
+ 'description' => 'Do charged party rewriting in the freeside-cdrrewrited daemon; useful if CDRs are being dropped off directly in the database and require special charged_party processing such as cdr-charged_party-accountcode.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cdr-taqua-da_rewrite',
+ 'section' => '',
+ 'description' => 'For the Taqua CDR format, a comma-separated list of directory assistance 800 numbers. Any CDRs with these numbers as "BilledNumber" will be rewritten to the "CallingPartyNumber" (and CallType "12") on import.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_pkg-show_autosuspend',
+ 'section' => 'UI',
+ 'description' => 'Show package auto-suspend dates. Use with caution for now; can slow down customer view for large insallations.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cdr-asterisk_forward_rewrite',
+ 'section' => '',
+ 'description' => 'Enable special processing for CDRs representing forwarded calls: For CDRs that have a dcontext that starts with "Local/" but does not match dst, set charged_party to dst, parse a new dst from dstchannel, and set amaflags to "2" ("BILL"/"BILLING").',
+ 'type' => 'checkbox',
+ },
+
+);
+
+1;
diff --git a/FS/FS/ConfDefaults.pm b/FS/FS/ConfDefaults.pm
new file mode 100644
index 0000000..a5d74ec
--- /dev/null
+++ b/FS/FS/ConfDefaults.pm
@@ -0,0 +1,86 @@
+package FS::ConfDefaults;
+
+=head1 NAME
+
+FS::ConfDefaults - Freeside configuration default and available values
+
+=head1 SYNOPSIS
+
+ use FS::ConfDefaults;
+
+ @avail_cust_fields = FS::ConfDefaults->cust_fields_avail();
+
+=head1 DESCRIPTION
+
+Just a small class to keep config default and available values
+
+=head1 METHODS
+
+=over 4
+
+=item cust_fields_avail
+
+Returns a list, suitable for assigning to a hash, of available values and
+labels for customer fields values.
+
+=cut
+
+# XXX should use msgcat for "Day phone" and "Night phone", but how?
+sub cust_fields_avail { (
+
+ 'Cust. Status | Customer' =>
+ 'Status | Last, First or Company (Last, First)',
+ 'Cust# | Cust. Status | Customer' =>
+ 'custnum | Status | Last, First or Company (Last, First)',
+
+ 'Cust. Status | Name | Company' =>
+ 'Status | Last, First | Company',
+ 'Cust# | Cust. Status | Name | Company' =>
+ 'custnum | Status | Last, First | Company',
+
+ 'Cust. Status | (bill) Customer | (service) Customer' =>
+ 'Status | Last, First or Company (Last, First) | (same for service contact if present)',
+ 'Cust# | Cust. Status | (bill) Customer | (service) Customer' =>
+ 'custnum | Status | Last, First or Company (Last, First) | (same for service contact if present)',
+
+ 'Cust. Status | (bill) Name | (bill) Company | (service) Name | (service) Company' =>
+ 'Status | Last, First | Company | (same for service contact if present)',
+ 'Cust# | Cust. Status | (bill) Name | (bill) Company | (service) Name | (service) Company' =>
+ 'custnum | Status | Last, First | Company | (same for service contact if present)',
+
+ 'Cust# | Cust. Status | Name | Company | Address 1 | Address 2 | City | State | Zip | Country | Day phone | Night phone | Invoicing email(s)' =>
+ 'custnum | Status | Last, First | Company | (all address fields) | Day phone | Night phone | Invoicing email(s)',
+
+ 'Cust# | Cust. Status | Name | Company | Address 1 | Address 2 | City | State | Zip | Country | Day phone | Night phone | Fax number | Invoicing email(s) | Payment Type' =>
+ 'custnum | Status | Last, First | Company | (all address fields) | (all phones) | Invoicing email(s) | Payment Type',
+
+ 'Cust# | Cust. Status | Name | Company | Address 1 | Address 2 | City | State | Zip | Country | Day phone | Night phone | Fax number | Invoicing email(s) | Payment Type | Current Balance' =>
+ 'custnum | Status | Last, First | Company | (all address fields) | (all phones) | Invoicing email(s) | Payment Type | Current Balance',
+
+ 'Cust# | Cust. Status | (bill) Name | (bill) Company | (bill) Address 1 | (bill) Address 2 | (bill) City | (bill) State | (bill) Zip | (bill) Country | (bill) Day phone | (bill) Night phone | (service) Name | (service) Company | (service) Address 1 | (service) Address 2 | (service) City | (service) State | (service) Zip | (service) Country | (service) Day phone | (service) Night phone | Invoicing email(s)' =>
+ 'custnum | Status | Last, First | Company | (all address fields) | Day phone | Night phone | (same for service address if present) | Invoicing email(s)',
+
+ 'Cust# | Cust. Status | (bill) Name | (bill) Company | (bill) Address 1 | (bill) Address 2 | (bill) City | (bill) State | (bill) Zip | (bill) Country | (bill) Day phone | (bill) Night phone | (bill) Fax number | (service) Name | (service) Company | (service) Address 1 | (service) Address 2 | (service) City | (service) State | (service) Zip | (service) Country | (service) Day phone | (service) Night phone | (service) Fax number | Invoicing email(s) | Payment Type' =>
+ 'custnum | Status | Last, First | Company | (all address fields) | (all phones) | (same for service address if present) | Invoicing email(s) | Payment Type',
+
+ 'Cust# | Cust. Status | (bill) Name | (bill) Company | (bill) Address 1 | (bill) Address 2 | (bill) City | (bill) State | (bill) Zip | (bill) Country | (bill) Day phone | (bill) Night phone | (bill) Fax number | (service) Name | (service) Company | (service) Address 1 | (service) Address 2 | (service) City | (service) State | (service) Zip | (service) Country | (service) Day phone | (service) Night phone | (service) Fax number | Invoicing email(s) | Payment Type | Current Balance' =>
+ 'custnum | Status | Last, First | Company | (all address fields) | (all phones) | (same for service address if present) | Invoicing email(s) | Payment Type | Current Balance',
+
+ 'Invoicing email(s)' => 'Invoicing email(s)',
+ 'Cust# | Invoicing email(s)' => 'custnum | Invoicing email(s)',
+
+); }
+
+=back
+
+=head1 BUGS
+
+Not yet.
+
+=head1 SEE ALSO
+
+L<FS::Conf>
+
+=cut
+
+1;
diff --git a/FS/FS/ConfItem.pm b/FS/FS/ConfItem.pm
new file mode 100644
index 0000000..a0e997a
--- /dev/null
+++ b/FS/FS/ConfItem.pm
@@ -0,0 +1,63 @@
+package FS::ConfItem;
+
+=head1 NAME
+
+FS::ConfItem - Configuration option meta-data.
+
+=head1 SYNOPSIS
+
+ use FS::Conf;
+ @config_items = $conf->config_items;
+
+ foreach $item ( @config_items ) {
+ $key = $item->key;
+ $section = $item->section;
+ $description = $item->description;
+ }
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=over 4
+
+=item new
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = @_ ? shift : {};
+ bless ($self, $class);
+}
+
+=item key
+
+=item section
+
+=item description
+
+=cut
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $field = $AUTOLOAD;
+ $field =~ s/.*://;
+ $self->{$field};
+}
+
+=back
+
+=head1 BUGS
+
+Terse docs.
+
+=head1 SEE ALSO
+
+L<FS::Conf>
+
+=cut
+
+1;
+
diff --git a/FS/FS/Conf_compat17.pm b/FS/FS/Conf_compat17.pm
new file mode 100644
index 0000000..0f2e193
--- /dev/null
+++ b/FS/FS/Conf_compat17.pm
@@ -0,0 +1,2402 @@
+package FS::Conf_compat17;
+
+use vars qw($default_dir $base_dir @config_items @card_types $DEBUG );
+use IO::File;
+use File::Basename;
+use FS::ConfItem;
+use FS::ConfDefaults;
+
+$base_dir = '%%%FREESIDE_CONF%%%';
+$default_dir = '%%%FREESIDE_CONF%%%';
+
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::Conf - Freeside configuration values
+
+=head1 SYNOPSIS
+
+ use FS::Conf;
+
+ $conf = new FS::Conf "/config/directory";
+
+ $FS::Conf::default_dir = "/config/directory";
+ $conf = new FS::Conf;
+
+ $dir = $conf->dir;
+
+ $value = $conf->config('key');
+ @list = $conf->config('key');
+ $bool = $conf->exists('key');
+
+ $conf->touch('key');
+ $conf->set('key' => 'value');
+ $conf->delete('key');
+
+ @config_items = $conf->config_items;
+
+=head1 DESCRIPTION
+
+Read and write Freeside configuration values. Keys currently map to filenames,
+but this may change in the future.
+
+=head1 METHODS
+
+=over 4
+
+=item new [ DIRECTORY ]
+
+Create a new configuration object. A directory arguement is required if
+$FS::Conf::default_dir has not been set.
+
+=cut
+
+sub new {
+ my($proto,$dir) = @_;
+ my($class) = ref($proto) || $proto;
+ my($self) = { 'dir' => $dir || $default_dir,
+ 'base_dir' => $base_dir,
+ };
+ bless ($self, $class);
+}
+
+=item dir
+
+Returns the conf directory.
+
+=cut
+
+sub dir {
+ my($self) = @_;
+ my $dir = $self->{dir};
+ -e $dir or die "FATAL: $dir doesn't exist!";
+ -d $dir or die "FATAL: $dir isn't a directory!";
+ -r $dir or die "FATAL: Can't read $dir!";
+ -x $dir or die "FATAL: $dir not searchable (executable)!";
+ $dir =~ /^(.*)$/;
+ $1;
+}
+
+=item base_dir
+
+Returns the base directory. By default this is /usr/local/etc/freeside.
+
+=cut
+
+sub base_dir {
+ my($self) = @_;
+ my $base_dir = $self->{base_dir};
+ -e $base_dir or die "FATAL: $base_dir doesn't exist!";
+ -d $base_dir or die "FATAL: $base_dir isn't a directory!";
+ -r $base_dir or die "FATAL: Can't read $base_dir!";
+ -x $base_dir or die "FATAL: $base_dir not searchable (executable)!";
+ $base_dir =~ /^(.*)$/;
+ $1;
+}
+
+=item config KEY
+
+Returns the configuration value or values (depending on context) for key.
+
+=cut
+
+sub config {
+ my($self,$file)=@_;
+ my($dir)=$self->dir;
+ my $fh = new IO::File "<$dir/$file" or return;
+ if ( wantarray ) {
+ map {
+ /^(.*)$/
+ or die "Illegal line (array context) in $dir/$file:\n$_\n";
+ $1;
+ } <$fh>;
+ } else {
+ <$fh> =~ /^(.*)$/
+ or die "Illegal line (scalar context) in $dir/$file:\n$_\n";
+ $1;
+ }
+}
+
+=item config_binary KEY
+
+Returns the exact scalar value for key.
+
+=cut
+
+sub config_binary {
+ my($self,$file)=@_;
+ my($dir)=$self->dir;
+ my $fh = new IO::File "<$dir/$file" or return;
+ local $/;
+ my $content = <$fh>;
+ $content;
+}
+
+=item exists KEY
+
+Returns true if the specified key exists, even if the corresponding value
+is undefined.
+
+=cut
+
+sub exists {
+ my($self,$file)=@_;
+ my($dir) = $self->dir;
+ -e "$dir/$file";
+}
+
+=item config_orbase KEY SUFFIX
+
+Returns the configuration value or values (depending on context) for
+KEY_SUFFIX, if it exists, otherwise for KEY
+
+=cut
+
+sub config_orbase {
+ my( $self, $file, $suffix ) = @_;
+ if ( $self->exists("${file}_$suffix") ) {
+ $self->config("${file}_$suffix");
+ } else {
+ $self->config($file);
+ }
+}
+
+=item touch KEY
+
+Creates the specified configuration key if it does not exist.
+
+=cut
+
+sub touch {
+ my($self, $file) = @_;
+ my $dir = $self->dir;
+ unless ( $self->exists($file) ) {
+ warn "[FS::Conf] TOUCH $file\n" if $DEBUG;
+ system('touch', "$dir/$file");
+ }
+}
+
+=item set KEY VALUE
+
+Sets the specified configuration key to the given value.
+
+=cut
+
+sub set {
+ my($self, $file, $value) = @_;
+ my $dir = $self->dir;
+ $value =~ /^(.*)$/s;
+ $value = $1;
+ unless ( join("\n", @{[ $self->config($file) ]}) eq $value ) {
+ warn "[FS::Conf] SET $file\n" if $DEBUG;
+# warn "$dir" if is_tainted($dir);
+# warn "$dir" if is_tainted($file);
+ chmod 0644, "$dir/$file";
+ my $fh = new IO::File ">$dir/$file" or return;
+ chmod 0644, "$dir/$file";
+ print $fh "$value\n";
+ }
+}
+#sub is_tainted {
+# return ! eval { join('',@_), kill 0; 1; };
+# }
+
+=item delete KEY
+
+Deletes the specified configuration key.
+
+=cut
+
+sub delete {
+ my($self, $file) = @_;
+ my $dir = $self->dir;
+ if ( $self->exists($file) ) {
+ warn "[FS::Conf] DELETE $file\n";
+ unlink "$dir/$file";
+ }
+}
+
+=item config_items
+
+Returns all of the possible configuration items as FS::ConfItem objects. See
+L<FS::ConfItem>.
+
+=cut
+
+sub config_items {
+ my $self = shift;
+ #quelle kludge
+ @config_items,
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => 'Alternate template file for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_template_*')
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => 'Alternate HTML template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_html_*')
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ ($latexname = $basename ) =~ s/latex/html/;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => "Alternate Notes section for HTML invoices. Defaults to the same data in $latexname if not specified.",
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_htmlnotes_*')
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => 'Alternate LaTeX template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_latex_*')
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => 'Alternate Notes section for LaTeX typeset PostScript invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_latexnotes_*')
+ );
+}
+
+=back
+
+=head1 BUGS
+
+If this was more than just crud that will never be useful outside Freeside I'd
+worry that config_items is freeside-specific and icky.
+
+=head1 SEE ALSO
+
+"Configuration" in the web interface (config/config.cgi).
+
+httemplate/docs/config.html
+
+=cut
+
+#Business::CreditCard
+@card_types = (
+ "VISA card",
+ "MasterCard",
+ "Discover card",
+ "American Express card",
+ "Diner's Club/Carte Blanche",
+ "enRoute",
+ "JCB",
+ "BankCard",
+ "Switch",
+ "Solo",
+);
+
+@config_items = map { new FS::ConfItem $_ } (
+
+ {
+ 'key' => 'address',
+ 'section' => 'deprecated',
+ 'description' => 'This configuration option is no longer used. See <a href="#invoice_template">invoice_template</a> instead.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'alerter_template',
+ 'section' => 'billing',
+ 'description' => 'Template file for billing method expiration alerts. See the <a href="../docs/billing.html#invoice_template">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'apacheroot',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>www_shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead. The directory containing Apache virtual hosts',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'apacheip',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>apache</i> <a href="../browse/part_export.cgi">export</a> instead. Used to be the current IP address to assign to new virtual hosts',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'apachemachine',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>www_shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead. A machine with the apacheroot directory and user home directories. The existance of this file enables setup of virtual host directories, and, in conjunction with the `home\' configuration file, symlinks into user home directories.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'apachemachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>apache</i> <a href="../browse/part_export.cgi">export</a> instead. Used to be Apache machines, one per line. This enables export of `/etc/apache/vhosts.conf\', which can be included in your Apache configuration via the <a href="http://www.apache.org/docs/mod/core.html#include">Include</a> directive.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'bindprimary',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>bind</i> <a href="../browse/part_export.cgi">export</a> instead. Your BIND primary nameserver. This enables export of /var/named/named.conf and zone files into /var/named',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'bindsecondaries',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>bind_slave</i> <a href="../browse/part_export.cgi">export</a> instead. Your BIND secondary nameservers, one per line. This enables export of /var/named/named.conf',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'encryption',
+ 'section' => 'billing',
+ 'description' => 'Enable encryption of credit cards.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'encryptionmodule',
+ 'section' => 'billing',
+ 'description' => 'Use which module for encryption?',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'encryptionpublickey',
+ 'section' => 'billing',
+ 'description' => 'Your RSA Public Key - Required if Encryption is turned on.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'encryptionprivatekey',
+ 'section' => 'billing',
+ 'description' => 'Your RSA Private Key - Including this will enable the "Bill Now" feature. However if the system is compromised, a hacker can use this key to decode the stored credit card information. This is generally not a good idea.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'business-onlinepayment',
+ 'section' => 'billing',
+ 'description' => '<a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support, at least three lines: processor, login, and password. An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\'). Optional additional lines are passed to Business::OnlinePayment as %processor_options.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-ach',
+ 'section' => 'billing',
+ 'description' => 'Alternate <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support for ACH transactions (defaults to regular <b>business-onlinepayment</b>). At least three lines: processor, login, and password. An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\'). Optional additional lines are passed to Business::OnlinePayment as %processor_options.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-description',
+ 'section' => 'billing',
+ 'description' => 'String passed as the description field to <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a>. Evaluated as a double-quoted perl string, with the following variables available: <code>$agent</code> (the agent name), and <code>$pkgs</code> (a comma-separated list of packages for which these charges apply)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-email-override',
+ 'section' => 'billing',
+ 'description' => 'Email address used instead of customer email address when submitting a BOP transaction.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'bsdshellmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>bsdshell</i> <a href="../browse/part_export.cgi">export</a> instead. Your BSD flavored shell (and mail) machines, one per line. This enables export of `/etc/passwd\' and `/etc/master.passwd\'.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'business-onlinepayment-email_customer',
+ 'section' => 'billing',
+ 'description' => 'Controls the "email_customer" flag used by some Business::OnlinePayment processors to enable customer receipts.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'countrydefault',
+ 'section' => 'UI',
+ 'description' => 'Default two-letter country code (if not supplied, the default is `US\')',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'date_format',
+ 'section' => 'UI',
+ 'description' => 'Format for displaying dates',
+ 'type' => 'select',
+ 'select_hash' => [
+ '%m/%d/%Y' => 'MM/DD/YYYY',
+ '%Y/%m/%d' => 'YYYY/MM/DD',
+ ],
+ },
+
+ {
+ 'key' => 'cyrus',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>cyrus</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to integrate with <a href="http://asg.web.cmu.edu/cyrus/imapd/">Cyrus IMAP Server</a>, three lines: IMAP server, admin username, and admin password. Cyrus::IMAP::Admin should be installed locally and the connection to the server secured.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cp_app',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>cp</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to integrate with <a href="http://www.cp.net/">Critial Path Account Provisioning Protocol</a>, four lines: "host:port", username, password, and workgroup (for new users).',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'deletecustomers',
+ 'section' => 'UI',
+ 'description' => 'Enable customer deletions. Be very careful! Deleting a customer will remove all traces that this customer ever existed! It should probably only be used when auditing a legacy database. Normally, you cancel all of a customers\' packages if they cancel service.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'deletepayments',
+ 'section' => 'billing',
+ 'description' => 'Enable deletion of unclosed payments. Really, with voids this is pretty much not recommended in any situation anymore. Be very careful! Only delete payments that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a payment is deleted.',
+ 'type' => [qw( checkbox text )],
+ },
+
+ {
+ 'key' => 'deletecredits',
+ 'section' => 'deprecated',
+ 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable deletion of unclosed credits. Be very careful! Only delete credits that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a credit is deleted.',
+ 'type' => [qw( checkbox text )],
+ },
+
+ {
+ 'key' => 'deleterefunds',
+ 'section' => 'billing',
+ 'description' => 'Enable deletion of unclosed refunds. Be very careful! Only delete refunds that were data-entry errors, not adjustments.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'unapplypayments',
+ 'section' => 'deprecated',
+ 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable "unapplication" of unclosed payments.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'unapplycredits',
+ 'section' => 'deprecated',
+ 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to nable "unapplication" of unclosed credits.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'dirhash',
+ 'section' => 'shell',
+ 'description' => 'Optional numeric value to control directory hashing. If positive, hashes directories for the specified number of levels from the front of the username. If negative, hashes directories for the specified number of levels from the end of the username. Some examples: <ul><li>1: user -> <a href="#home">/home</a>/u/user<li>2: user -> <a href="#home">/home</a>/u/s/user<li>-1: user -> <a href="#home">/home</a>/r/user<li>-2: user -> <a href="#home">home</a>/r/e/user</ul>',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'disable_customer_referrals',
+ 'section' => 'UI',
+ 'description' => 'Disable new customer-to-customer referrals in the web interface',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'editreferrals',
+ 'section' => 'UI',
+ 'description' => 'Enable advertising source modification for existing customers',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emailinvoiceonly',
+ 'section' => 'billing',
+ 'description' => 'Disables postal mail invoices',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'disablepostalinvoicedefault',
+ 'section' => 'billing',
+ 'description' => 'Disables postal mail invoices as the default option in the UI. Be careful not to setup customers which are not sent invoices. See <a href ="#emailinvoiceauto">emailinvoiceauto</a>.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emailinvoiceauto',
+ 'section' => 'billing',
+ 'description' => 'Automatically adds new accounts to the email invoice list',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emailinvoiceautoalways',
+ 'section' => 'billing',
+ 'description' => 'Automatically adds new accounts to the email invoice list even when the list contains email addresses',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'exclude_ip_addr',
+ 'section' => '',
+ 'description' => 'Exclude these from the list of available broadband service IP addresses. (One per line)',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'erpcdmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, ERPCD is no longer supported. Used to be ERPCD authentication machines, one per line. This enables export of `/usr/annex/acp_passwd\' and `/usr/annex/acp_dialup\'',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'hidecancelledpackages',
+ 'section' => 'UI',
+ 'description' => 'Prevent cancelled packages from showing up in listings (though they will still be in the database)',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'hidecancelledcustomers',
+ 'section' => 'UI',
+ 'description' => 'Prevent customers with only cancelled packages from showing up in listings (though they will still be in the database)',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'home',
+ 'section' => 'required',
+ 'description' => 'For new users, prefixed to username to create a directory name. Should have a leading but not a trailing slash.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'icradiusmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to enable radcheck and radreply table population - by default in the Freeside database, or in the database specified by the <a href="http://rootwood.haze.st/aspside/config/config-view.cgi#icradius_secrets">icradius_secrets</a> config option (the radcheck and radreply tables needs to be created manually). You do not need to use MySQL for your Freeside database to export to an ICRADIUS/FreeRADIUS MySQL database with this option. <blockquote><b>ADDITIONAL DEPRECATED FUNCTIONALITY</b> (instead use <a href="http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Database_Administration.html#Replication">MySQL replication</a> or point icradius_secrets to the external database) - your <a href="ftp://ftp.cheapnet.net/pub/icradius">ICRADIUS</a> machines or <a href="http://www.freeradius.org/">FreeRADIUS</a> (with MySQL authentication) machines, one per line. Machines listed in this file will have the radcheck table exported to them. Each line should contain four items, separted by whitespace: machine name, MySQL database name, MySQL username, and MySQL password. For example: <CODE>"radius.isp.tld&nbsp;radius_db&nbsp;radius_user&nbsp;passw0rd"</CODE></blockquote>',
+ 'type' => [qw( checkbox textarea )],
+ },
+
+ {
+ 'key' => 'icradius_mysqldest',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> <a href="../browse/part_export.cgi">export</a> instead. Used to be the destination directory for the MySQL databases, on the ICRADIUS/FreeRADIUS machines. Defaults to "/usr/local/var/".',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'icradius_mysqlsource',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> <a href="../browse/part_export.cgi">export</a> instead. Used to be the source directory for for the MySQL radcheck table files, on the Freeside machine. Defaults to "/usr/local/var/freeside".',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'icradius_secrets',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to specify a database for ICRADIUS/FreeRADIUS export. Three lines: DBI data source, username and password.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_from',
+ 'section' => 'required',
+ 'description' => 'Return address on email invoices',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'invoice_template',
+ 'section' => 'required',
+ 'description' => 'Required template file for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_html',
+ 'section' => 'billing',
+ 'description' => 'Optional HTML template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlnotes',
+ 'section' => 'billing',
+ 'description' => 'Notes section for HTML invoices. Defaults to the same data in invoice_latexnotes if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlfooter',
+ 'section' => 'billing',
+ 'description' => 'Footer for HTML invoices. Defaults to the same data in invoice_latexfooter if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_htmlreturnaddress',
+ 'section' => 'billing',
+ 'description' => 'Return address for HTML invoices. Defaults to the same data in invoice_latexreturnaddress if not specified.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latex',
+ 'section' => 'billing',
+ 'description' => 'Optional LaTeX template for typeset PostScript invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexnotes',
+ 'section' => 'billing',
+ 'description' => 'Notes section for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexfooter',
+ 'section' => 'billing',
+ 'description' => 'Footer for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexcoupon',
+ 'section' => 'billing',
+ 'description' => 'Remittance coupon for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexreturnaddress',
+ 'section' => 'billing',
+ 'description' => 'Return address for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexsmallfooter',
+ 'section' => 'billing',
+ 'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_email_pdf',
+ 'section' => 'billing',
+ 'description' => 'Send PDF invoice as an attachment to emailed invoices. By default, includes the plain text invoice as the email body, unless invoice_email_pdf_note is set.',
+ 'type' => 'checkbox'
+ },
+
+ {
+ 'key' => 'invoice_email_pdf_note',
+ 'section' => 'billing',
+ 'description' => 'If defined, this text will replace the default plain text invoice as the body of emailed PDF invoices.',
+ 'type' => 'textarea'
+ },
+
+
+ {
+ 'key' => 'invoice_default_terms',
+ 'section' => 'billing',
+ 'description' => 'Optional default invoice term, used to calculate a due date printed on invoices.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'Payable upon receipt', 'Net 0', 'Net 10', 'Net 15', 'Net 30', 'Net 45', 'Net 60' ],
+ },
+
+ {
+ 'key' => 'invoice_send_receipts',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, this used to send an invoice copy on payments and credits. See the payment_receipt_email and XXXX instead.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'payment_receipt_email',
+ 'section' => 'billing',
+ 'description' => 'Template file for payment receipts. Payment receipts are sent to the customer email invoice destination(s) when a payment is received. See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available: <ul><li><code>$date</code> <li><code>$name</code> <li><code>$paynum</code> - Freeside payment number <li><code>$paid</code> - Amount of payment <li><code>$payby</code> - Payment type (Card, Check, Electronic check, etc.) <li><code>$payinfo</code> - Masked credit card number or check number <li><code>$balance</code> - New balance</ul>',
+ 'type' => [qw( checkbox textarea )],
+ },
+
+ {
+ 'key' => 'lpr',
+ 'section' => 'required',
+ 'description' => 'Print command for paper invoices, for example `lpr -h\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'maildisablecatchall',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, now the default. Turning this option on used to disable the requirement that each virtual domain have a catch-all mailbox.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'lpr-postscript_prefix',
+ 'section' => 'billing',
+ 'description' => 'Raw printer commands prepended to the beginning of postscript print jobs (evaluated as a double-quoted perl string - backslash escapes are available)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'lpr-postscript_suffix',
+ 'section' => 'billing',
+ 'description' => 'Raw printer commands added to the end of postscript print jobs (evaluated as a double-quoted perl string - backslash escapes are available)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'money_char',
+ 'section' => '',
+ 'description' => 'Currency symbol - defaults to `$\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'mxmachines',
+ 'section' => 'deprecated',
+ 'description' => 'MX entries for new domains, weight and machine, one per line, with trailing `.\'',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'nsmachines',
+ 'section' => 'deprecated',
+ 'description' => 'NS nameservers for new domains, one per line, with trailing `.\'',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'defaultrecords',
+ 'section' => 'BIND',
+ 'description' => 'DNS entries to add automatically when creating a domain',
+ 'type' => 'editlist',
+ 'editlist_parts' => [ { type=>'text' },
+ { type=>'immutable', value=>'IN' },
+ { type=>'select',
+ select_enum=>{ map { $_=>$_ } qw(A CNAME MX NS TXT)} },
+ { type=> 'text' }, ],
+ },
+
+ {
+ 'key' => 'arecords',
+ 'section' => 'deprecated',
+ 'description' => 'A list of tab seperated CNAME records to add automatically when creating a domain',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cnamerecords',
+ 'section' => 'deprecated',
+ 'description' => 'A list of tab seperated CNAME records to add automatically when creating a domain',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'nismachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>. Your NIS master (not slave master) machines, one per line. This enables export of `/etc/global/passwd\' and `/etc/global/shadow\'.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'passwordmin',
+ 'section' => 'password',
+ 'description' => 'Minimum password length (default 6)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'passwordmax',
+ 'section' => 'password',
+ 'description' => 'Maximum password length (default 8) (don\'t set this over 12 if you need to import or export crypt() passwords)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'password-noampersand',
+ 'section' => 'password',
+ 'description' => 'Disallow ampersands in passwords',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'password-noexclamation',
+ 'section' => 'password',
+ 'description' => 'Disallow exclamations in passwords (Not setting this could break old text Livingston or Cistron Radius servers)',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'qmailmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add <i>qmail</i> and <i>shellcommands</i> <a href="../browse/part_export.cgi">exports</a> instead. This option used to export `/var/qmail/control/virtualdomains\', `/var/qmail/control/recipientmap\', and `/var/qmail/control/rcpthosts\'. Setting this option (even if empty) also turns on user `.qmail-extension\' file maintenance in conjunction with the <b>shellmachine</b> option.',
+ 'type' => [qw( checkbox textarea )],
+ },
+
+ {
+ 'key' => 'radiusmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to export to be: your RADIUS authentication machines, one per line. This enables export of `/etc/raddb/users\'.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'referraldefault',
+ 'section' => 'UI',
+ 'description' => 'Default referral, specified by refnum',
+ 'type' => 'text',
+ },
+
+# {
+# 'key' => 'registries',
+# 'section' => 'required',
+# 'description' => 'Directory which contains domain registry information. Each registry is a directory.',
+# },
+
+ {
+ 'key' => 'report_template',
+ 'section' => 'deprecated',
+ 'description' => 'Deprecated template file for reports.',
+ 'type' => 'textarea',
+ },
+
+
+ {
+ 'key' => 'maxsearchrecordsperpage',
+ 'section' => 'UI',
+ 'description' => 'If set, number of search records to return per page.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'sendmailconfigpath',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>sendmail</i> <a href="../browse/part_export.cgi">export</a> instead. Used to be sendmail configuration file path. Defaults to `/etc\'. Many newer distributions use `/etc/mail\'.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'sendmailmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>sendmail</i> <a href="../browse/part_export.cgi">export</a> instead. Used to be sendmail machines, one per line. This enables export of `/etc/virtusertable\' and `/etc/sendmail.cw\'.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'sendmailrestart',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>sendmail</i> <a href="../browse/part_export.cgi">export</a> instead. Used to define the command which is run on sendmail machines after files are copied.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'session-start',
+ 'section' => 'session',
+ 'description' => 'If defined, the command which is executed on the Freeside machine when a session begins. The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'session-stop',
+ 'section' => 'session',
+ 'description' => 'If defined, the command which is executed on the Freeside machine when a session ends. The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'shellmachine',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to contain a single machine with user home directories mounted. This enables home directory creation, renaming and archiving/deletion. In conjunction with `qmailmachines\', it also enables `.qmail-extension\' file maintenance.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'shellmachine-useradd',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to contain command(s) to run on shellmachine when an account is created. If the <b>shellmachine</b> option is set but this option is not, <code>useradd -d $dir -m -s $shell -u $uid $username</code> is the default. If this option is set but empty, <code>cp -pr /etc/skel $dir; chown -R $uid.$gid $dir</code> is the default instead. Otherwise the value is evaluated as a double-quoted perl string, with the following variables available: <code>$username</code>, <code>$uid</code>, <code>$gid</code>, <code>$dir</code>, and <code>$shell</code>.',
+ 'type' => [qw( checkbox text )],
+ },
+
+ {
+ 'key' => 'shellmachine-userdel',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to contain command(s) to run on shellmachine when an account is deleted. If the <b>shellmachine</b> option is set but this option is not, <code>userdel $username</code> is the default. If this option is set but empty, <code>rm -rf $dir</code> is the default instead. Otherwise the value is evaluated as a double-quoted perl string, with the following variables available: <code>$username</code> and <code>$dir</code>.',
+ 'type' => [qw( checkbox text )],
+ },
+
+ {
+ 'key' => 'shellmachine-usermod',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to contain command(s) to run on shellmachine when an account is modified. If the <b>shellmachine</b> option is set but this option is empty, <code>[ -d $old_dir ] &amp;&amp; mv $old_dir $new_dir || ( chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; find . -depth -print | cpio -pdm $new_dir; chmod u-t $new_dir; chown -R $uid.$gid $new_dir; rm -rf $old_dir )</code> is the default. Otherwise the contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$old_dir</code>, <code>$new_dir</code>, <code>$uid</code> and <code>$gid</code>.',
+ #'type' => [qw( checkbox text )],
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'shellmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>sysvshell</i> <a href="../browse/part_export.cgi">export</a> instead. Your Linux and System V flavored shell (and mail) machines, one per line. This enables export of `/etc/passwd\' and `/etc/shadow\' files.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'shells',
+ 'section' => 'required',
+ 'description' => 'Legal shells (think /etc/shells). You probably want to `cut -d: -f7 /etc/passwd | sort | uniq\' initially so that importing doesn\'t fail with `Illegal shell\' errors, then remove any special entries afterwords. A blank line specifies that an empty shell is permitted.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'showpasswords',
+ 'section' => 'UI',
+ 'description' => 'Display unencrypted user passwords in the backend (employee) web interface',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signupurl',
+ 'section' => 'UI',
+ 'description' => 'if you are using customer-to-customer referrals, and you enter the URL of your <a href="../docs/signup.html">signup server CGI</a>, the customer view screen will display a customized link to the signup server with the appropriate customer as referral',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'smtpmachine',
+ 'section' => 'required',
+ 'description' => 'SMTP relay for Freeside\'s outgoing mail',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soadefaultttl',
+ 'section' => 'BIND',
+ 'description' => 'SOA default TTL for new domains.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soaemail',
+ 'section' => 'BIND',
+ 'description' => 'SOA email for new domains, in BIND form (`.\' instead of `@\'), with trailing `.\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soaexpire',
+ 'section' => 'BIND',
+ 'description' => 'SOA expire for new domains',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soamachine',
+ 'section' => 'BIND',
+ 'description' => 'SOA machine for new domains, with trailing `.\'',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soarefresh',
+ 'section' => 'BIND',
+ 'description' => 'SOA refresh for new domains',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'soaretry',
+ 'section' => 'BIND',
+ 'description' => 'SOA retry for new domains',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'statedefault',
+ 'section' => 'UI',
+ 'description' => 'Default state or province (if not supplied, the default is `CA\')',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'radiusprepend',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, real-time text radius now edits an existing file in place - just (turn off freeside-queued and) edit your RADIUS users file directly. The contents used to be be prepended to the top of the RADIUS users file (text exports only).',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'textradiusprepend',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, use RADIUS check attributes instead. The contents used to be prepended to the first line of a user\'s RADIUS entry in text exports.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'unsuspendauto',
+ 'section' => 'billing',
+ 'description' => 'Enables the automatic unsuspension of suspended packages when a customer\'s balance due changes from positive to zero or negative as the result of a payment or credit',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'unsuspend-always_adjust_next_bill_date',
+ 'section' => 'billing',
+ 'description' => 'Global override that causes unsuspensions to always adjust the next bill date under any circumstances. This is now controlled on a per-package bases - probably best not to use this option unless you are a legacy installation that requires this behaviour.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'usernamemin',
+ 'section' => 'username',
+ 'description' => 'Minimum username length (default 2)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'usernamemax',
+ 'section' => 'username',
+ 'description' => 'Maximum username length',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'username-ampersand',
+ 'section' => 'username',
+ 'description' => 'Allow the ampersand character (&amp;) in usernames. Be careful when using this option in conjunction with <a href="../browse/part_export.cgi">exports</a> which execute shell commands, as the ampersand will be interpreted by the shell if not quoted.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-letter',
+ 'section' => 'username',
+ 'description' => 'Usernames must contain at least one letter',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-letterfirst',
+ 'section' => 'username',
+ 'description' => 'Usernames must start with a letter',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-noperiod',
+ 'section' => 'username',
+ 'description' => 'Disallow periods in usernames',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-nounderscore',
+ 'section' => 'username',
+ 'description' => 'Disallow underscores in usernames',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-nodash',
+ 'section' => 'username',
+ 'description' => 'Disallow dashes in usernames',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-uppercase',
+ 'section' => 'username',
+ 'description' => 'Allow uppercase characters in usernames',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username-percent',
+ 'section' => 'username',
+ 'description' => 'Allow the percent character (%) in usernames.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'username_policy',
+ 'section' => 'deprecated',
+ 'description' => 'This file controls the mechanism for preventing duplicate usernames in passwd/radius files exported from svc_accts. This should be one of \'prepend domsvc\' \'append domsvc\' \'append domain\' or \'append @domain\'',
+ 'type' => 'select',
+ 'select_enum' => [ 'prepend domsvc', 'append domsvc', 'append domain', 'append @domain' ],
+ #'type' => 'text',
+ },
+
+ {
+ 'key' => 'vpopmailmachines',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>vpopmail</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to contain your vpopmail pop toasters, one per line. Each line is of the form "machinename vpopdir vpopuid vpopgid". For example: <code>poptoaster.domain.tld /home/vpopmail 508 508</code> Note: vpopuid and vpopgid are values taken from the vpopmail machine\'s /etc/passwd',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'vpopmailrestart',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, add a <i>vpopmail</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to define the shell commands to run on vpopmail machines after files are copied. An example can be found in eg/vpopmailrestart of the source distribution.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'safe-part_pkg',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, obsolete. Used to validate package definition setup and recur expressions against a preset list. Useful for webdemos, annoying to powerusers.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'safe-part_bill_event',
+ 'section' => 'UI',
+ 'description' => 'Validates invoice event expressions against a preset list. Useful for webdemos, annoying to powerusers.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'show_ss',
+ 'section' => 'UI',
+ 'description' => 'Turns on display/collection of SS# in the web interface.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'show_stateid',
+ 'section' => 'UI',
+ 'description' => "Turns on display/collection of driver's license/state issued id numbers in the web interface. Sometimes required by electronic check (ACH) processors.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'show_bankstate',
+ 'section' => 'UI',
+ 'description' => "Turns on display/collection of state for bank accounts in the web interface. Sometimes required by electronic check (ACH) processors.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'agent_defaultpkg',
+ 'section' => 'UI',
+ 'description' => 'Setting this option will cause new packages to be available to all agent types by default.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'legacy_link',
+ 'section' => 'UI',
+ 'description' => 'Display options in the web interface to link legacy pre-Freeside services.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'legacy_link-steal',
+ 'section' => 'UI',
+ 'description' => 'Allow "stealing" an already-audited service from one customer (or package) to another using the link function.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'queue_dangerous_controls',
+ 'section' => 'UI',
+ 'description' => 'Enable queue modification controls on account pages and for new jobs. Unless you are a developer working on new export code, you should probably leave this off to avoid causing provisioning problems.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'security_phrase',
+ 'section' => 'password',
+ 'description' => 'Enable the tracking of a "security phrase" with each account. Not recommended, as it is vulnerable to social engineering.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'locale',
+ 'section' => 'UI',
+ 'description' => 'Message locale',
+ 'type' => 'select',
+ 'select_enum' => [ qw(en_US) ],
+ },
+
+ {
+ 'key' => 'selfservice_server-quiet',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, the self-service server no longer sends superfluous decline and cancel emails. Used to disable decline and cancel emails generated by transactions initiated by the selfservice server.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signup_server-quiet',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, the signup server is now part of the self-service server and no longer sends superfluous decline and cancel emails. Used to disable decline and cancel emails generated by transactions initiated by the signup server. Does not disable welcome emails.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signup_server-payby',
+ 'section' => '',
+ 'description' => 'Acceptable payment types for the signup server',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB PREPAY BILL COMP) ],
+ },
+
+ {
+ 'key' => 'signup_server-email',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, this feature is no longer available. See the ***fill me in*** report instead. Used to contain a comma-separated list of email addresses to receive notification of signups via the signup server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'signup_server-default_agentnum',
+ 'section' => '',
+ 'description' => 'Default agent for the signup server',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::agent;
+ map { $_->agentnum => $_->agent }
+ FS::Record::qsearch('agent', { disabled=>'' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::agent;
+ my $agent = FS::Record::qsearchs(
+ 'agent', { 'agentnum'=>shift }
+ );
+ $agent ? $agent->agent : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-default_refnum',
+ 'section' => '',
+ 'description' => 'Default advertising source for the signup server',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::part_referral;
+ map { $_->refnum => $_->referral }
+ FS::Record::qsearch( 'part_referral',
+ { 'disabled' => '' }
+ );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::part_referral;
+ my $part_referral = FS::Record::qsearchs(
+ 'part_referral', { 'refnum'=>shift } );
+ $part_referral ? $part_referral->referral : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-default_pkgpart',
+ 'section' => '',
+ 'description' => 'Default pakcage for the signup server',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ map { $_->pkgpart => $_->pkg.' - '.$_->comment }
+ FS::Record::qsearch( 'part_pkg',
+ { 'disabled' => ''}
+ );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ my $part_pkg = FS::Record::qsearchs(
+ 'part_pkg', { 'pkgpart'=>shift }
+ );
+ $part_pkg
+ ? $part_pkg->pkg.' - '.$part_pkg->comment
+ : '';
+ },
+ },
+
+ {
+ 'key' => 'show-msgcat-codes',
+ 'section' => 'UI',
+ 'description' => 'Show msgcat codes in error messages. Turn this option on before reporting errors to the mailing list.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'signup_server-realtime',
+ 'section' => '',
+ 'description' => 'Run billing for signup server signups immediately, and do not provision accounts which subsequently have a balance.',
+ 'type' => 'checkbox',
+ },
+ {
+ 'key' => 'signup_server-classnum2',
+ 'section' => '',
+ 'description' => 'Package Class for first optional purchase',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ map { $_->classnum => $_->classname }
+ FS::Record::qsearch('pkg_class', {} );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ my $pkg_class = FS::Record::qsearchs(
+ 'pkg_class', { 'classnum'=>shift }
+ );
+ $pkg_class ? $pkg_class->classname : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_server-classnum3',
+ 'section' => '',
+ 'description' => 'Package Class for second optional purchase',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ map { $_->classnum => $_->classname }
+ FS::Record::qsearch('pkg_class', {} );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::pkg_class;
+ my $pkg_class = FS::Record::qsearchs(
+ 'pkg_class', { 'classnum'=>shift }
+ );
+ $pkg_class ? $pkg_class->classname : '';
+ },
+ },
+
+ {
+ 'key' => 'backend-realtime',
+ 'section' => '',
+ 'description' => 'Run billing for backend signups immediately.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'declinetemplate',
+ 'section' => 'billing',
+ 'description' => 'Template file for credit card decline emails.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'emaildecline',
+ 'section' => 'billing',
+ 'description' => 'Enable emailing of credit card decline notices.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'emaildecline-exclude',
+ 'section' => 'billing',
+ 'description' => 'List of error messages that should not trigger email decline notices, one per line.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cancelmessage',
+ 'section' => 'billing',
+ 'description' => 'Template file for cancellation emails.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cancelsubject',
+ 'section' => 'billing',
+ 'description' => 'Subject line for cancellation emails.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'emailcancel',
+ 'section' => 'billing',
+ 'description' => 'Enable emailing of cancellation notices.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'require_cardname',
+ 'section' => 'billing',
+ 'description' => 'Require an "Exact name on card" to be entered explicitly; don\'t default to using the first and last name.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'enable_taxclasses',
+ 'section' => 'billing',
+ 'description' => 'Enable per-package tax classes',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'require_taxclasses',
+ 'section' => 'billing',
+ 'description' => 'Require a taxclass to be entered for every package',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'welcome_email',
+ 'section' => '',
+ 'description' => 'Template file for welcome email. Welcome emails are sent to the customer email invoice destination(s) each time a svc_acct record is created. See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code></ul>',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'welcome_email-from',
+ 'section' => '',
+ 'description' => 'From: address header for welcome email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'welcome_email-subject',
+ 'section' => '',
+ 'description' => 'Subject: header for welcome email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'welcome_email-mimetype',
+ 'section' => '',
+ 'description' => 'MIME type for welcome email',
+ 'type' => 'select',
+ 'select_enum' => [ 'text/plain', 'text/html' ],
+ },
+
+ {
+ 'key' => 'welcome_letter',
+ 'section' => '',
+ 'description' => 'Optional LaTex template file for a printed welcome letter. A welcome letter is printed the first time a cust_pkg record is created. See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation and the billing documentation for details on the template substitution language. A variable exists for each fieldname in the customer record (<code>$first, $last, etc</code>). The following additional variables are available<ul><li><code>$payby</code> - a friendler represenation of the field<li><code>$payinfo</code> - the masked payment information<li><code>$expdate</code> - the time at which the payment method expires (a UNIX timestamp)<li><code>$returnaddress</code> - the invoice return address for this customer\'s agent</ul>',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'warning_email',
+ 'section' => '',
+ 'description' => 'Template file for warning email. Warning emails are sent to the customer email invoice destination(s) each time a svc_acct record has its usage drop below a threshold or 0. See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code> <li><code>$column</code> <li><code>$amount</code> <li><code>$threshold</code></ul>',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'warning_email-from',
+ 'section' => '',
+ 'description' => 'From: address header for warning email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-cc',
+ 'section' => '',
+ 'description' => 'Additional recipient(s) (comma separated) for warning email when remaining usage reaches zero.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-subject',
+ 'section' => '',
+ 'description' => 'Subject: header for warning email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-mimetype',
+ 'section' => '',
+ 'description' => 'MIME type for warning email',
+ 'type' => 'select',
+ 'select_enum' => [ 'text/plain', 'text/html' ],
+ },
+
+ {
+ 'key' => 'payby',
+ 'section' => 'billing',
+ 'description' => 'Available payment types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD COMP) ],
+ },
+
+ {
+ 'key' => 'payby-default',
+ 'section' => 'UI',
+ 'description' => 'Default payment type. HIDE disables display of billing information and sets customers to BILL.',
+ 'type' => 'select',
+ 'select_enum' => [ '', qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD COMP HIDE) ],
+ },
+
+ {
+ 'key' => 'paymentforcedtobatch',
+ 'section' => 'UI',
+ 'description' => 'Causes per customer payment entry to be forced to a batch processor rather than performed realtime.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-notes',
+ 'section' => 'UI',
+ 'description' => 'Extra HTML to be displayed on the Account View screen.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'radius-password',
+ 'section' => '',
+ 'description' => 'RADIUS attribute for plain-text passwords.',
+ 'type' => 'select',
+ 'select_enum' => [ 'Password', 'User-Password' ],
+ },
+
+ {
+ 'key' => 'radius-ip',
+ 'section' => '',
+ 'description' => 'RADIUS attribute for IP addresses.',
+ 'type' => 'select',
+ 'select_enum' => [ 'Framed-IP-Address', 'Framed-Address' ],
+ },
+
+ {
+ 'key' => 'svc_acct-alldomains',
+ 'section' => '',
+ 'description' => 'Allow accounts to select any domain in the database. Normally accounts can only select from the domain set in the service definition and those purchased by the customer.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'dump-scpdest',
+ 'section' => '',
+ 'description' => 'destination for scp database dumps: user@host:/path',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'dump-pgpid',
+ 'section' => '',
+ 'description' => "Optional PGP public key user or key id for database dumps. The public key should exist on the freeside user's public keyring, and the gpg binary and GnuPG perl module should be installed.",
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'users-allow_comp',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, enable the <i>Complimentary customer</i> access right instead. Was: Usernames (Freeside users, created with <a href="../docs/man/bin/freeside-adduser.html">freeside-adduser</a>) which can create complimentary customers, one per line. If no usernames are entered, all users can create complimentary accounts.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cvv-save',
+ 'section' => 'billing',
+ 'description' => 'Save CVV2 information after the initial transaction for the selected credit card types. Enabling this option may be in violation of your merchant agreement(s), so please check them carefully before enabling this option for any credit card types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => \@card_types,
+ },
+
+ {
+ 'key' => 'allow_negative_charges',
+ 'section' => 'billing',
+ 'description' => 'Allow negative charges. Normally not used unless importing data from a legacy system that requires this.',
+ 'type' => 'checkbox',
+ },
+ {
+ 'key' => 'auto_unset_catchall',
+ 'section' => '',
+ 'description' => 'When canceling a svc_acct that is the email catchall for one or more svc_domains, automatically set their catchall fields to null. If this option is not set, the attempt will simply fail.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'system_usernames',
+ 'section' => 'username',
+ 'description' => 'A list of system usernames that cannot be edited or removed, one per line. Use a bare username to prohibit modification/deletion of the username in any domain, or username@domain to prohibit modification/deletetion of a specific username and domain.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cust_pkg-change_svcpart',
+ 'section' => '',
+ 'description' => "When changing packages, move services even if svcparts don't match between old and new pacakge definitions.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'disable_autoreverse',
+ 'section' => 'BIND',
+ 'description' => 'Disable automatic synchronization of reverse-ARPA entries.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_www-enable_subdomains',
+ 'section' => '',
+ 'description' => 'Enable selection of specific subdomains for virtual host creation.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_www-usersvc_svcpart',
+ 'section' => '',
+ 'description' => 'Allowable service definition svcparts for virtual hosts, one per line.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'selfservice_server-primary_only',
+ 'section' => '',
+ 'description' => 'Only allow primary accounts to access self-service functionality.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'card_refund-days',
+ 'section' => 'billing',
+ 'description' => 'After a payment, the number of days a refund link will be available for that payment. Defaults to 120.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'agent-showpasswords',
+ 'section' => '',
+ 'description' => 'Display unencrypted user passwords in the agent (reseller) interface',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'global_unique-username',
+ 'section' => 'username',
+ 'description' => 'Global username uniqueness control: none (usual setting - check uniqueness per exports), username (all usernames are globally unique, regardless of domain or exports), or username@domain (all username@domain pairs are globally unique, regardless of exports). disabled turns off duplicate checking completely and is STRONGLY NOT RECOMMENDED unless you REALLY need to turn this off.',
+ 'type' => 'select',
+ 'select_enum' => [ 'none', 'username', 'username@domain', 'disabled' ],
+ },
+
+ {
+ 'key' => 'svc_external-skip_manual',
+ 'section' => 'UI',
+ 'description' => 'When provisioning svc_external services, skip manual entry of id and title fields in the UI. Usually used in conjunction with an export that populates these fields (i.e. artera_turbo).',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_external-display_type',
+ 'section' => 'UI',
+ 'description' => 'Select a specific svc_external type to enable some UI changes specific to that type (i.e. artera_turbo).',
+ 'type' => 'select',
+ 'select_enum' => [ 'generic', 'artera_turbo', ],
+ },
+
+ {
+ 'key' => 'ticket_system',
+ 'section' => '',
+ 'description' => 'Ticketing system integration. <b>RT_Internal</b> uses the built-in RT ticketing system (see the <a href="../docs/install-rt">integrated ticketing installation instructions</a>). <b>RT_External</b> accesses an external RT installation in a separate database (local or remote).',
+ 'type' => 'select',
+ #'select_enum' => [ '', qw(RT_Internal RT_Libs RT_External) ],
+ 'select_enum' => [ '', qw(RT_Internal RT_External) ],
+ },
+
+ {
+ 'key' => 'ticket_system-default_queueid',
+ 'section' => '',
+ 'description' => 'Default queue used when creating new customer tickets.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub {
+ my $conf = new FS::Conf;
+ if ( $conf->config('ticket_system') ) {
+ eval "use FS::TicketSystem;";
+ die $@ if $@;
+ FS::TicketSystem->queues();
+ } else {
+ ();
+ }
+ },
+ 'option_sub' => sub {
+ my $conf = new FS::Conf;
+ if ( $conf->config('ticket_system') ) {
+ eval "use FS::TicketSystem;";
+ die $@ if $@;
+ FS::TicketSystem->queue(shift);
+ } else {
+ '';
+ }
+ },
+ },
+
+ {
+ 'key' => 'ticket_system-custom_priority_field',
+ 'section' => '',
+ 'description' => 'Custom field from the ticketing system to use as a custom priority classification.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'ticket_system-custom_priority_field-values',
+ 'section' => '',
+ 'description' => 'Values for the custom field from the ticketing system to break down and sort customer ticket lists.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'ticket_system-custom_priority_field_queue',
+ 'section' => '',
+ 'description' => 'Ticketing system queue in which the custom field specified in ticket_system-custom_priority_field is located.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'ticket_system-rt_external_datasrc',
+ 'section' => '',
+ 'description' => 'With external RT integration, the DBI data source for the external RT installation, for example, <code>DBI:Pg:user=rt_user;password=rt_word;host=rt.example.com;dbname=rt</code>',
+ 'type' => 'text',
+
+ },
+
+ {
+ 'key' => 'ticket_system-rt_external_url',
+ 'section' => '',
+ 'description' => 'With external RT integration, the URL for the external RT installation, for example, <code>https://rt.example.com/rt</code>',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'company_name',
+ 'section' => 'required',
+ 'description' => 'Your company name',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'echeck-void',
+ 'section' => 'deprecated',
+ 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable local-only voiding of echeck payments in addition to refunds against the payment gateway',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cc-void',
+ 'section' => 'deprecated',
+ 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable local-only voiding of credit card payments in addition to refunds against the payment gateway',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'unvoid',
+ 'section' => 'deprecated',
+ 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable unvoiding of voided payments',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'address2-search',
+ 'section' => 'UI',
+ 'description' => 'Enable a "Unit" search box which searches the second address field',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-require_address2',
+ 'section' => 'UI',
+ 'description' => 'Second address field is required (on service address only, if billing and service addresses differ). Also enables "Unit" labeling of address2 on customer view and edit pages. Useful for multi-tenant applications. See also: address2-search',
+ 'type' => 'checkbox',
+ },
+
+ { 'key' => 'referral_credit',
+ 'section' => 'billing',
+ 'description' => "Enables one-time referral credits in the amount of one month <i>referred</i> customer's recurring fee (irregardless of frequency).",
+ 'type' => 'checkbox',
+ },
+
+ { 'key' => 'selfservice_server-cache_module',
+ 'section' => '',
+ 'description' => 'Module used to store self-service session information. All modules handle any number of self-service servers. Cache::SharedMemoryCache is appropriate for a single database / single Freeside server. Cache::FileCache is useful for multiple databases on a single server, or when IPC::ShareLite is not available (i.e. FreeBSD).', # _Database stores session information in the database and is appropriate for multiple Freeside servers, but may be slower.',
+ 'type' => 'select',
+ 'select_enum' => [ 'Cache::SharedMemoryCache', 'Cache::FileCache', ], # '_Database' ],
+ },
+
+ {
+ 'key' => 'hylafax',
+ 'section' => 'billing',
+ 'description' => 'Options for a HylaFAX server to enable the FAX invoice destination. They should be in the form of a space separated list of arguments to the Fax::Hylafax::Client::sendfax subroutine. You probably shouldn\'t override things like \'docfile\'. *Note* Only supported when using typeset invoices (see the invoice_latex configuration option).',
+ 'type' => [qw( checkbox textarea )],
+ },
+
+ {
+ 'key' => 'cust_bill-ftpformat',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - format.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'default', 'billco', ],
+ },
+
+ {
+ 'key' => 'cust_bill-ftpserver',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-ftpusername',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-ftppassword',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-ftpdir',
+ 'section' => 'billing',
+ 'description' => 'Enable FTP of raw invoice data - server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-spoolformat',
+ 'section' => 'billing',
+ 'description' => 'Enable spooling of raw invoice data - format.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'default', 'billco', ],
+ },
+
+ {
+ 'key' => 'cust_bill-spoolagent',
+ 'section' => 'billing',
+ 'description' => 'Enable per-agent spooling of raw invoice data.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-usage_suspend',
+ 'section' => 'billing',
+ 'description' => 'Suspends the package an account belongs to when svc_acct.seconds or a bytecount is decremented to 0 or below (accounts with an empty seconds and up|down|totalbytes value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-usage_unsuspend',
+ 'section' => 'billing',
+ 'description' => 'Unuspends the package an account belongs to when svc_acct.seconds or a bytecount is incremented from 0 or below to a positive value (accounts with an empty seconds and up|down|totalbytes value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-usage_threshold',
+ 'section' => 'billing',
+ 'description' => 'The threshold (expressed as percentage) of acct.seconds or acct.up|down|totalbytes at which a warning message is sent to a service holder. Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd. Defaults to 80.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust-fields',
+ 'section' => 'UI',
+ 'description' => 'Which customer fields to display on reports by default',
+ 'type' => 'select',
+ 'select_hash' => [ FS::ConfDefaults->cust_fields_avail() ],
+ },
+
+ {
+ 'key' => 'cust_pkg-display_times',
+ 'section' => 'UI',
+ 'description' => 'Display full timestamps (not just dates) for customer packages. Useful if you are doing real-time things like hourly prepaid.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-edit_uid',
+ 'section' => 'shell',
+ 'description' => 'Allow UID editing.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_acct-edit_gid',
+ 'section' => 'shell',
+ 'description' => 'Allow GID editing.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'zone-underscore',
+ 'section' => 'BIND',
+ 'description' => 'Allow underscores in zone names. As underscores are illegal characters in zone names, this option is not recommended.',
+ 'type' => 'checkbox',
+ },
+
+ #these should become per-user...
+ {
+ 'key' => 'vonage-username',
+ 'section' => '',
+ 'description' => 'Vonage Click2Call username (see <a href="https://secure.click2callu.com/">https://secure.click2callu.com/</a>)',
+ 'type' => 'text',
+ },
+ {
+ 'key' => 'vonage-password',
+ 'section' => '',
+ 'description' => 'Vonage Click2Call username (see <a href="https://secure.click2callu.com/">https://secure.click2callu.com/</a>)',
+ 'type' => 'text',
+ },
+ {
+ 'key' => 'vonage-fromnumber',
+ 'section' => '',
+ 'description' => 'Vonage Click2Call number (see <a href="https://secure.click2callu.com/">https://secure.click2callu.com/</a>)',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'echeck-nonus',
+ 'section' => 'billing',
+ 'description' => 'Disable ABA-format account checking for Electronic Check payment info',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'voip-cust_cdr_spools',
+ 'section' => '',
+ 'description' => 'Enable the per-customer option for individual CDR spools.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'svc_forward-arbitrary_dst',
+ 'section' => '',
+ 'description' => "Allow forwards to point to arbitrary strings that don't necessarily look like email addresses. Only used when using forwards for weird, non-email things.",
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'tax-ship_address',
+ 'section' => 'billing',
+ 'description' => 'By default, tax calculations are done based on the billing address. Enable this switch to calculate tax based on the shipping address instead. Note: Tax reports can take a long time when enabled.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'invoice-ship_address',
+ 'section' => 'billing',
+ 'description' => 'Enable this switch to include the ship address on the invoice.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'invoice-unitprice',
+ 'section' => 'billing',
+ 'description' => 'This switch enables unit pricing on the invoice.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'postal_invoice-fee_pkgpart',
+ 'section' => 'billing',
+ 'description' => 'This allows selection of a package to insert on invoices for customers with postal invoices selected.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ map { $_->pkgpart => $_->pkg }
+ FS::Record::qsearch('part_pkg', { disabled=>'' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::part_pkg;
+ my $part_pkg = FS::Record::qsearchs(
+ 'part_pkg', { 'pkgpart'=>shift }
+ );
+ $part_pkg ? $part_pkg->pkg : '';
+ },
+ },
+
+ {
+ 'key' => 'batch-enable',
+ 'section' => 'billing',
+ 'description' => 'Enable credit card and/or ACH batching - leave disabled for real-time installations.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'batch-enable_payby',
+ 'section' => 'billing',
+ 'description' => 'Enable batch processing for the specified payment types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [qw( CARD CHEK )],
+ },
+
+ {
+ 'key' => 'realtime-disable_payby',
+ 'section' => 'billing',
+ 'description' => 'Disable realtime processing for the specified payment types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [qw( CARD CHEK )],
+ },
+
+ {
+ 'key' => 'batch-default_format',
+ 'section' => 'billing',
+ 'description' => 'Default format for batches.',
+ 'type' => 'select',
+ 'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch',
+ 'csv-chase_canada-E-xactBatch', 'BoM', 'PAP',
+ 'ach-spiritone',
+ ]
+ },
+
+ {
+ 'key' => 'batch-fixed_format-CARD',
+ 'section' => 'billing',
+ 'description' => 'Fixed (unchangeable) format for credit card batches.',
+ 'type' => 'select',
+ 'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM', 'PAP' ,
+ 'csv-chase_canada-E-xactBatch', 'BoM', 'PAP' ]
+ },
+
+ {
+ 'key' => 'batch-fixed_format-CHEK',
+ 'section' => 'billing',
+ 'description' => 'Fixed (unchangeable) format for electronic check batches.',
+ 'type' => 'select',
+ 'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM', 'PAP',
+ 'ach-spiritone',
+ ]
+ },
+
+ {
+ 'key' => 'batch-increment_expiration',
+ 'section' => 'billing',
+ 'description' => 'Increment expiration date years in batches until cards are current. Make sure this is acceptable to your batching provider before enabling.',
+ 'type' => 'checkbox'
+ },
+
+ {
+ 'key' => 'batchconfig-BoM',
+ 'section' => 'billing',
+ 'description' => 'Configuration for Bank of Montreal batching, seven lines: 1. Origin ID, 2. Datacenter, 3. Typecode, 4. Short name, 5. Long name, 6. Bank, 7. Bank account',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'batchconfig-PAP',
+ 'section' => 'billing',
+ 'description' => 'Configuration for PAP batching, seven lines: 1. Origin ID, 2. Datacenter, 3. Typecode, 4. Short name, 5. Long name, 6. Bank, 7. Bank account',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'batchconfig-csv-chase_canada-E-xactBatch',
+ 'section' => 'billing',
+ 'description' => 'Gateway ID for Chase Canada E-xact batching',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'payment_history-years',
+ 'section' => 'UI',
+ 'description' => 'Number of years of payment history to show by default. Currently defaults to 2.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_main-use_comments',
+ 'section' => 'UI',
+ 'description' => 'Display free form comments on the customer edit screen. Useful as a scratch pad.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-disable_notes',
+ 'section' => 'UI',
+ 'description' => 'Disable new style customer notes - timestamped and user identified customer notes. Useful in tracking who did what.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main_note-display_times',
+ 'section' => 'UI',
+ 'description' => 'Display full timestamps (not just dates) for customer notes.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-ticket_statuses',
+ 'section' => 'UI',
+ 'description' => 'Show tickets with these statuses on the customer view page.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [qw( new open stalled resolved rejected deleted )],
+ },
+
+ {
+ 'key' => 'cust_main-max_tickets',
+ 'section' => 'UI',
+ 'description' => 'Maximum number of tickets to show on the customer view page.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_main-skeleton_tables',
+ 'section' => '',
+ 'description' => 'Tables which will have skeleton records inserted into them for each customer. Syntax for specifying tables is unfortunately a tricky perl data structure for now.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cust_main-skeleton_custnum',
+ 'section' => '',
+ 'description' => 'Customer number specifying the source data to copy into skeleton tables for new customers.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_main-enable_birthdate',
+ 'section' => 'UI',
+ 'descritpion' => 'Enable tracking of a birth date with each customer record',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'support-key',
+ 'section' => '',
+ 'description' => 'A support key enables access to commercial services delivered over the network, such as the payroll module, access to the internal ticket system, priority support and optional backups.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'card-types',
+ 'section' => 'billing',
+ 'description' => 'Select one or more card types to enable only those card types. If no card types are selected, all card types are available.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => \@card_types,
+ },
+
+ {
+ 'key' => 'dashboard-toplist',
+ 'section' => 'UI',
+ 'description' => 'List of items to display on the top of the front page',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'impending_recur_template',
+ 'section' => 'billing',
+ 'description' => 'Template file for alerts about looming first time recurrant billing. See the <a href="http://search.cpan.org/~mjd/Text-Template.pm">Text::Template</a> documentation for details on the template substitition language. Also see packages with a <a href="../browse/part_pkg.cgi">flat price plan</a> The following variables are available<ul><li><code>$packages</code> allowing <code>$packages->[0]</code> thru <code>$packages->[n]</code> <li><code>$package</code> the first package, same as <code>$packages->[0]</code> <li><code>$recurdates</code> allowing <code>$recurdates->[0]</code> thru <code>$recurdates->[n]</code> <li><code>$recurdate</code> the first recurdate, same as <code>$recurdate->[0]</code> <li><code>$first</code> <li><code>$last</code></ul>',
+# <li><code>$payby</code> <li><code>$expdate</code> most likely only confuse
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'selfservice-session_timeout',
+ 'section' => '',
+ 'description' => 'Self-service session timeout. Defaults to 1 hour.',
+ 'type' => 'select',
+ 'select_enum' => [ '1 hour', '2 hours', '4 hours', '8 hours', '1 day', '1 week', ],
+ },
+
+ {
+ 'key' => 'disable_setup_suspended_pkgs',
+ 'section' => 'billing',
+ 'description' => 'Disables charging of setup fees for suspended packages.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-require_phone',
+ 'section' => '',
+ 'description' => 'Require daytime or night for all customer records.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-require_invoicing_list_email',
+ 'section' => '',
+ 'description' => 'Email address field is required: require at least one invoicing email address for all customer records.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'password-generated-allcaps',
+ 'section' => 'password',
+ 'description' => 'Causes passwords automatically generated to consist entirely of capital letters',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'datavolume-forcemegabytes',
+ 'section' => 'UI',
+ 'description' => 'All data volumes are expressed in megabytes',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'datavolume-significantdigits',
+ 'section' => 'UI',
+ 'description' => 'number of significant digits to use to represent data volumes',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'disable_void_after',
+ 'section' => 'billing',
+ 'description' => 'Number of seconds after which freeside won\'t attempt to VOID a payment first when performing a refund.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'disable_line_item_date_ranges',
+ 'section' => 'billing',
+ 'description' => 'Prevent freeside from automatically generating date ranges on invoice line items.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cancel_credit_type',
+ 'section' => 'billing',
+ 'description' => 'The group to use for new, automatically generated credit reasons resulting from cancellation.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ map { $_->typenum => $_->type }
+ FS::Record::qsearch('reason_type', { class=>'R' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ my $reason_type = FS::Record::qsearchs(
+ 'reason_type', { 'typenum' => shift }
+ );
+ $reason_type ? $reason_type->type : '';
+ },
+ },
+
+ {
+ 'key' => 'referral_credit_type',
+ 'section' => 'billing',
+ 'description' => 'The group to use for new, automatically generated credit reasons resulting from referrals.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ map { $_->typenum => $_->type }
+ FS::Record::qsearch('reason_type', { class=>'R' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ my $reason_type = FS::Record::qsearchs(
+ 'reason_type', { 'typenum' => shift }
+ );
+ $reason_type ? $reason_type->type : '';
+ },
+ },
+
+ {
+ 'key' => 'signup_credit_type',
+ 'section' => 'billing',
+ 'description' => 'The group to use for new, automatically generated credit reasons resulting from signup and self-service declines.',
+ 'type' => 'select-sub',
+ 'options_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ map { $_->typenum => $_->type }
+ FS::Record::qsearch('reason_type', { class=>'R' } );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::reason_type;
+ my $reason_type = FS::Record::qsearchs(
+ 'reason_type', { 'typenum' => shift }
+ );
+ $reason_type ? $reason_type->type : '';
+ },
+ },
+
+ {
+ 'key' => 'cust_main-agent_custid-format',
+ 'section' => '',
+ 'description' => 'Enables searching of various formatted values in cust_main.agent_custid',
+ 'type' => 'select',
+ 'select_hash' => [
+ '' => 'Numeric only',
+ 'ww?d+' => 'Numeric with one or two letter prefix',
+ ],
+ },
+
+ {
+ 'key' => 'card_masking_method',
+ 'section' => 'UI',
+ 'description' => 'Digits to display when masking credit cards. Note that the first six digits are necessary to canonically identify the credit card type (Visa/MC, Amex, Discover, Maestro, etc.) in all cases. The first four digits can identify the most common credit card types in most cases (Visa/MC, Amex, and Discover). The first two digits can distinguish between Visa/MC and Amex.',
+ 'type' => 'select',
+ 'select_hash' => [
+ '' => '123456xxxxxx1234',
+ 'first6last2' => '123456xxxxxxxx12',
+ 'first4last4' => '1234xxxxxxxx1234',
+ 'first4last2' => '1234xxxxxxxxxx12',
+ 'first2last4' => '12xxxxxxxxxx1234',
+ 'first2last2' => '12xxxxxxxxxxxx12',
+ 'first0last4' => 'xxxxxxxxxxxx1234',
+ 'first0last2' => 'xxxxxxxxxxxxxx12',
+ ],
+ },
+
+ {
+ 'key' => 'disable_previous_balance',
+ 'section' => 'billing',
+ 'description' => 'Disable inclusion of previous balance lines on invoices',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'disable_acl_changes',
+ 'section' => '',
+ 'description' => 'Disable all ACL changes, for demos.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-edit_agent_custid',
+ 'section' => 'UI',
+ 'description' => 'Enable editing of the agent_custid field.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'cust_main-default_areacode',
+ 'section' => 'UI',
+ 'description' => 'Default area code for customers.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'cust_bill-max_same_services',
+ 'section' => 'billing',
+ 'description' => 'Maximum number of the same service to list individually on invoices before condensing to a single line listing the number of services. Defaults to 5.',
+ 'type' => 'text',
+ },
+
+);
+
+1;
+
diff --git a/FS/FS/Cron/backup.pm b/FS/FS/Cron/backup.pm
new file mode 100644
index 0000000..204069a
--- /dev/null
+++ b/FS/FS/Cron/backup.pm
@@ -0,0 +1,43 @@
+package FS::Cron::backup;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK );
+use Exporter;
+use FS::UID qw(driver_name datasrc);
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( backup_scp );
+
+sub backup_scp {
+ my $conf = new FS::Conf;
+ my $dest = $conf->config('dump-scpdest');
+ if ( $dest ) {
+ datasrc =~ /dbname=([\w\.]+)$/ or die "unparsable datasrc ". datasrc;
+ my $database = $1;
+ eval "use Net::SCP qw(scp);";
+ die $@ if $@;
+ if ( driver_name eq 'Pg' ) {
+ system("pg_dump $database >/var/tmp/$database.sql")
+ } else {
+ die "database dumps not yet supported for ". driver_name;
+ }
+ if ( $conf->config('dump-pgpid') ) {
+ eval 'use GnuPG;';
+ die $@ if $@;
+ my $gpg = new GnuPG;
+ $gpg->encrypt( plaintext => "/var/tmp/$database.sql",
+ output => "/var/tmp/$database.gpg",
+ recipient => $conf->config('dump-pgpid'),
+ );
+ chmod 0600, '/var/tmp/$database.gpg';
+ scp("/var/tmp/$database.gpg", $dest);
+ unlink "/var/tmp/$database.gpg" or die $!;
+ } else {
+ chmod 0600, '/var/tmp/$database.sql';
+ scp("/var/tmp/$database.sql", $dest);
+ }
+ unlink "/var/tmp/$database.sql" or die $!;
+ }
+}
+
+1;
diff --git a/FS/FS/Cron/bill.pm b/FS/FS/Cron/bill.pm
new file mode 100644
index 0000000..ad6498c
--- /dev/null
+++ b/FS/FS/Cron/bill.pm
@@ -0,0 +1,146 @@
+package FS::Cron::bill;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK );
+use Exporter;
+use Date::Parse;
+use FS::UID qw(dbh);
+use FS::Record qw(qsearchs);
+use FS::cust_main;
+use FS::part_event;
+use FS::part_event_condition;
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw ( bill );
+
+sub bill {
+
+ my %opt = @_;
+
+ my $check_freq = $opt{'check_freq'} || '1d';
+
+ my $debug = 0;
+ $debug = 1 if $opt{'v'};
+ $debug = $opt{'l'} if $opt{'l'};
+
+ $FS::cust_main::DEBUG = $debug;
+ #$FS::cust_event::DEBUG = $opt{'l'} if $opt{'l'};
+
+ my @search = ();
+
+ push @search, "cust_main.payby = '". $opt{'p'}. "'"
+ if $opt{'p'};
+ push @search, "cust_main.agentnum = ". $opt{'a'}
+ if $opt{'a'};
+
+ if ( @ARGV ) {
+ push @search, "( ".
+ join(' OR ', map "cust_main.custnum = $_", @ARGV ).
+ " )";
+ }
+
+ ###
+ # generate where_pkg/where_event search clause
+ ###
+
+ #we're at now now (and later).
+ my($time)= $opt{'d'} ? str2time($opt{'d'}) : $^T;
+ $time += $opt{'y'} * 86400 if $opt{'y'};
+
+ my $invoice_time = $opt{'n'} ? $^T : $time;
+
+ # select * from cust_main where
+ my $where_pkg = <<"END";
+ 0 < ( select count(*) from cust_pkg
+ where cust_main.custnum = cust_pkg.custnum
+ and ( cancel is null or cancel = 0 )
+ and ( setup is null or setup = 0
+ or bill is null or bill <= $time
+ or ( expire is not null and expire <= $^T )
+ or ( adjourn is not null and adjourn <= $^T )
+ )
+ )
+END
+
+ my $where_event = join(' OR ', map {
+ my $eventtable = $_;
+
+ my $join = FS::part_event_condition->join_conditions_sql( $eventtable );
+ my $where = FS::part_event_condition->where_conditions_sql( $eventtable,
+ 'time'=>$time,
+ );
+
+ my $are_part_event =
+ "0 < ( SELECT COUNT(*) FROM part_event $join
+ WHERE check_freq = '$check_freq'
+ AND eventtable = '$eventtable'
+ AND ( disabled = '' OR disabled IS NULL )
+ AND $where
+ )
+ ";
+
+ if ( $eventtable eq 'cust_main' ) {
+ $are_part_event;
+ } else {
+ "0 < ( SELECT COUNT(*) FROM $eventtable
+ WHERE cust_main.custnum = $eventtable.custnum
+ AND $are_part_event
+ )
+ ";
+ }
+
+ } FS::part_event->eventtables);
+
+ push @search, "( $where_pkg OR $where_event )";
+
+ ###
+ # get a list of custnums
+ ###
+
+ warn "searching for customers:\n". join("\n", @search). "\n"
+ if $opt{'v'} || $opt{'l'};
+
+ my $sth = dbh->prepare(
+ "SELECT custnum FROM cust_main".
+ " WHERE ". join(' AND ', @search)
+ ) or die dbh->errstr;
+
+ $sth->execute or die $sth->errstr;
+
+ my @custnums = map { $_->[0] } @{ $sth->fetchall_arrayref };
+
+ ###
+ # for each custnum, queue or make one customer object and bill
+ # (one at a time, to reduce memory footprint with large #s of customers)
+ ###
+
+ foreach my $custnum ( @custnums ) {
+
+ my %args = (
+ 'time' => $time,
+ 'invoice_time' => $invoice_time,
+ 'actual_time' => $^T, #when freeside-bill was started
+ #(not, when using -m, freeside-queued)
+ 'check_freq' => $check_freq,
+ 'resetup' => ( $opt{'s'} ? $opt{'s'} : 0 ),
+ );
+
+ if ( $opt{'m'} ) {
+
+ #add job to queue that calls bill_and_collect with options
+ my $queue = new FS::queue {
+ 'job' => 'FS::cust_main::queued_bill',
+ 'secure' => 'Y',
+ };
+ my $error = $queue->insert( 'custnum'=>$custnum, %args );
+
+ } else {
+
+ my $cust_main = qsearchs( 'cust_main', { 'custnum' => $custnum } );
+ $cust_main->bill_and_collect( %args, 'debug' => $debug );
+
+ }
+
+ }
+
+}
diff --git a/FS/FS/Cron/expire_user_pref.pm b/FS/FS/Cron/expire_user_pref.pm
new file mode 100644
index 0000000..3226927
--- /dev/null
+++ b/FS/FS/Cron/expire_user_pref.pm
@@ -0,0 +1,20 @@
+package FS::Cron::expire_user_pref;
+
+use vars qw( @ISA @EXPORT_OK);
+use Exporter;
+use FS::UID qw(dbh);
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( expire_user_pref );
+
+sub expire_user_pref {
+ my $sql = "DELETE FROM access_user_pref WHERE expiration IS NOT NULL".
+ " AND expiration < ?";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute(time) or die $sth->errstr;
+
+ dbh->commit or die dbh->errstr if $FS::UID::AutoCommit
+
+}
+
+1;
diff --git a/FS/FS/Cron/notify.pm b/FS/FS/Cron/notify.pm
new file mode 100644
index 0000000..23cf920
--- /dev/null
+++ b/FS/FS/Cron/notify.pm
@@ -0,0 +1,149 @@
+package FS::Cron::notify;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK $DEBUG );
+use Exporter;
+use FS::UID qw( dbh driver_name );
+use FS::Record qw(qsearch);
+use FS::cust_main;
+use FS::cust_pkg;
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw ( notify_flat_delay );
+$DEBUG = 0;
+
+sub notify_flat_delay {
+
+ my %opt = @_;
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ $DEBUG = 1 if $opt{'v'};
+
+ #we're at now now (and later).
+ my($time) = $^T;
+
+ my $integer = driver_name =~ /^mysql/ ? 'SIGNED' : 'INTEGER';
+
+ # select * from cust_pkg where
+ my $where_pkg = <<"END";
+ where ( cancel is null or cancel = 0 )
+ and ( bill > 0 )
+ and
+ 0 < ( select count(*) from part_pkg
+ where cust_pkg.pkgpart = part_pkg.pkgpart
+ and part_pkg.plan = 'flat_delayed'
+ and 0 < ( select count(*) from part_pkg_option
+ where part_pkg.pkgpart = part_pkg_option.pkgpart
+ and part_pkg_option.optionname = 'recur_notify'
+ and part_pkg_option.optionvalue > 0
+ and 0 <= ( $time
+ + CAST( part_pkg_option.optionvalue AS $integer )
+ * 86400
+ - cust_pkg.bill
+ )
+ and ( cust_pkg.expire is null
+ or cust_pkg.expire > ( $time
+ + CAST( part_pkg_option.optionvalue AS $integer )
+ * 86400
+ )
+END
+
+#/* and ( cust_pkg.adjourn is null
+# or cust_pkg.adjourn > $time
+#-- Should notify suspended ones + cast(part_pkg_option.optionvalue as $integer)
+# * 86400
+#*/
+
+ $where_pkg .= <<"END";
+ )
+ )
+ )
+ and
+ 0 = ( select count(*) from cust_pkg_option
+ where cust_pkg.pkgnum = cust_pkg_option.pkgnum
+ and cust_pkg_option.optionname = 'impending_recur_notification_sent'
+ and cust_pkg_option.optionvalue = 1
+ )
+END
+
+ if ($opt{a}) {
+ $where_pkg .= <<END;
+ and 0 < ( select count(*) from cust_main
+ where cust_pkg.custnum = cust_main.custnum
+ and cust_main.agentnum = $opt{a}
+ )
+END
+ }
+
+ my @cust_pkg;
+ if ( @ARGV ) {
+ $where_pkg .= "and ( " . join( "OR ", map { "custnum = $_" } @ARGV) . " )";
+ }
+
+ my $orderby = "order by custnum, bill";
+
+ my $extra_sql = "$where_pkg $orderby";
+
+ @cust_pkg = qsearch('cust_pkg', {}, '', $extra_sql );
+
+ my @packages = ();
+ my @recurdates = ();
+ my @cust_pkgs = ();
+ while ( scalar(@cust_pkg) ) {
+ my $cust_main = $cust_pkg[0]->cust_main;
+ my $custnum = $cust_pkg[0]->custnum;
+ warn "working on $custnum" if $DEBUG;
+ while (scalar(@cust_pkg)){
+ last if ($cust_pkg[0]->custnum != $custnum);
+ warn "storing information on " . $cust_pkg[0]->pkgnum if $DEBUG;
+ push @packages, $cust_pkg[0]->part_pkg->pkg;
+ push @recurdates, $cust_pkg[0]->bill;
+ push @cust_pkgs, $cust_pkg[0];
+ shift @cust_pkg;
+ }
+ my $error =
+ $cust_main->notify( 'impending_recur_template',
+ 'extra_fields' => { 'packages' => \@packages,
+ 'recurdates' => \@recurdates,
+ 'package' => $packages[0],
+ 'recurdate' => $recurdates[0],
+ },
+ );
+ warn "Error notifying, custnum ". $cust_main->custnum. ": $error" if $error;
+
+ unless ($error) {
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ for (@cust_pkgs) {
+ my %options = ($_->options, 'impending_recur_notification_sent' => 1 );
+ $error = $_->replace( $_, options => \%options );
+ if ($error){
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ die "Error updating package options for customer". $cust_main->custnum.
+ ": $error" if $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ }
+
+ @packages = ();
+ @recurdates = ();
+ @cust_pkgs = ();
+
+ }
+
+ dbh->commit or die dbh->errstr if $oldAutoCommit;
+
+}
+
+1;
diff --git a/FS/FS/Cron/vacuum.pm b/FS/FS/Cron/vacuum.pm
new file mode 100644
index 0000000..075572d
--- /dev/null
+++ b/FS/FS/Cron/vacuum.pm
@@ -0,0 +1,23 @@
+package FS::Cron::vacuum;
+
+use vars qw( @ISA @EXPORT_OK);
+use Exporter;
+use FS::UID qw(driver_name dbh);
+use FS::Schema qw(dbdef);
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( vacuum );
+
+sub vacuum {
+
+ if ( driver_name eq 'Pg' ) {
+ dbh->{AutoCommit} = 1; #so we can vacuum
+ foreach my $table ( dbdef->tables ) {
+ my $sth = dbh->prepare("VACUUM ANALYZE $table") or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ }
+ }
+
+}
+
+1;
diff --git a/FS/FS/CurrentUser.pm b/FS/FS/CurrentUser.pm
new file mode 100644
index 0000000..bcd337d
--- /dev/null
+++ b/FS/FS/CurrentUser.pm
@@ -0,0 +1,67 @@
+package FS::CurrentUser;
+
+use vars qw($CurrentUser $upgrade_hack);
+
+#not at compile-time, circular dependancey causes trouble
+#use FS::Record qw(qsearchs);
+#use FS::access_user;
+
+$upgrade_hack = 0;
+
+=head1 NAME
+
+FS::CurrentUser - Package representing the current user
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=cut
+
+sub load_user {
+ my( $class, $user ) = @_; #, $pass
+
+ if ( $upgrade_hack ) {
+ return $CurrentUser = new FS::CurrentUser::BootstrapUser;
+ }
+
+ #return "" if $user =~ /^fs_(queue|selfservice)$/;
+
+ #not the best thing in the world...
+ eval "use FS::Record qw(qsearchs);";
+ die $@ if $@;
+ eval "use FS::access_user;";
+ die $@ if $@;
+
+ $CurrentUser = qsearchs('access_user', {
+ 'username' => $user,
+ #'_password' =>
+ 'disabled' => '',
+ } );
+
+ die "unknown user: $user" unless $CurrentUser; # or bad password
+
+ $CurrentUser;
+}
+
+=head1 BUGS
+
+Creepy crawlies
+
+=head1 SEE ALSO
+
+=cut
+
+package FS::CurrentUser::BootstrapUser;
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+}
+
+sub AUTOLOAD { 1 };
+
+1;
+
diff --git a/FS/FS/Daemon.pm b/FS/FS/Daemon.pm
new file mode 100644
index 0000000..ca18134
--- /dev/null
+++ b/FS/FS/Daemon.pm
@@ -0,0 +1,100 @@
+package FS::Daemon;
+
+use vars qw( @ISA @EXPORT_OK );
+use vars qw( $pid_dir $me $pid_file $sigint $sigterm $logfile );
+use Exporter;
+use Fcntl qw(:flock);
+use POSIX qw(setsid);
+use IO::File;
+use Date::Format;
+
+#this is a simple refactoring of the stuff from freeside-queued, just to
+#avoid duplicate code. eventually this should use something from CPAN.
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(
+ daemonize1 drop_root daemonize2 myexit logfile sigint sigterm
+);
+%EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ] );
+
+$pid_dir = '/var/run';
+
+sub daemonize1 {
+ $me = shift;
+
+ $pid_file = "$pid_dir/$me";
+ $pid_file .= '.'.shift if scalar(@_);
+ $pid_file .= '.pid';
+
+ chdir "/" or die "Can't chdir to /: $!";
+ open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
+ defined(my $pid = fork) or die "Can't fork: $!";
+ if ( $pid ) {
+ print "$me started with pid $pid\n"; #logging to $log_file\n";
+ exit unless $pid_file;
+ my $pidfh = new IO::File ">$pid_file" or exit;
+ print $pidfh "$pid\n";
+ exit;
+ }
+
+ #sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
+ #$SIG{CHLD} = \&REAPER;
+ $sigterm = 0;
+ $sigint = 0;
+ $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; };
+ $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; };
+}
+
+sub drop_root {
+ my $freeside_gid = scalar(getgrnam('freeside'))
+ or die "can't find freeside group\n";
+ $) = $freeside_gid;
+ $( = $freeside_gid;
+ #if freebsd can't setuid(), presumably it can't setgid() either. grr fleabsd
+ ($(,$)) = ($),$();
+ $) = $freeside_gid;
+
+ $> = $FS::UID::freeside_uid;
+ $< = $FS::UID::freeside_uid;
+ #freebsd is sofa king broken, won't setuid()
+ ($<,$>) = ($>,$<);
+ $> = $FS::UID::freeside_uid;
+}
+
+sub daemonize2 {
+ open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
+ setsid or die "Can't start a new session: $!";
+ open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
+
+ $SIG{__DIE__} = \&_die;
+ $SIG{__WARN__} = \&_logmsg;
+
+ warn "$me starting\n";
+}
+
+sub sigint { $sigint; }
+sub sigterm { $sigterm; }
+
+sub logfile { $logfile = shift; } #_logmsg('test'); }
+
+sub myexit {
+ unlink $pid_file if -e $pid_file;
+ exit;
+}
+
+sub _die {
+ my $msg = shift;
+ unlink $pid_file if -e $pid_file;
+ _logmsg($msg);
+}
+
+sub _logmsg {
+ chomp( my $msg = shift );
+ my $log = new IO::File ">>$logfile";
+ flock($log, LOCK_EX);
+ seek($log, 0, 2);
+ print $log "[". time2str("%a %b %e %T %Y",time). "] [$$] $msg\n";
+ flock($log, LOCK_UN);
+ close $log;
+}
+
diff --git a/FS/FS/InitHandler.pm b/FS/FS/InitHandler.pm
new file mode 100644
index 0000000..5038cf3
--- /dev/null
+++ b/FS/FS/InitHandler.pm
@@ -0,0 +1,91 @@
+package FS::InitHandler;
+
+# this leaks memory under graceful restarts and i wouldn't use it on any
+# modern server. useful for very slow machines with memory to spare, just
+# always do a full restart
+
+use strict;
+use vars qw($DEBUG);
+use FS::UID qw(adminsuidsetup);
+use FS::Record;
+
+$DEBUG = 1;
+
+sub handler {
+
+ use Date::Format;
+ use Date::Parse;
+ use Tie::IxHash;
+ use HTML::Entities;
+ use IO::Handle;
+ use IO::File;
+ use String::Approx;
+ use HTML::Widgets::SelectLayers 0.02;
+ #use FS::UID;
+ #use FS::Record;
+ use FS::Conf;
+ use FS::CGI;
+ use FS::Msgcat;
+
+ use FS::agent;
+ use FS::agent_type;
+ use FS::domain_record;
+ use FS::cust_bill;
+ use FS::cust_bill_pay;
+ use FS::cust_credit;
+ use FS::cust_credit_bill;
+ use FS::cust_main;
+ use FS::cust_main_county;
+ use FS::cust_pay;
+ use FS::cust_pkg;
+ use FS::cust_refund;
+ use FS::cust_svc;
+ use FS::nas;
+ use FS::part_bill_event;
+ use FS::part_pkg;
+ use FS::part_referral;
+ use FS::part_svc;
+ use FS::pkg_svc;
+ use FS::port;
+ use FS::queue;
+ use FS::raddb;
+ use FS::session;
+ use FS::svc_acct;
+ use FS::svc_acct_pop;
+ use FS::svc_domain;
+ use FS::svc_forward;
+ use FS::svc_www;
+ use FS::type_pkgs;
+ use FS::part_export;
+ use FS::part_export_option;
+ use FS::export_svc;
+ use FS::msgcat;
+
+ warn "[FS::InitHandler] handler called\n" if $DEBUG;
+
+ #this is sure to be broken on freebsd
+ $> = $FS::UID::freeside_uid;
+
+ open(MAPSECRETS,"<$FS::UID::conf_dir/mapsecrets")
+ or die "can't read $FS::UID::conf_dir/mapsecrets: $!";
+
+ my %seen;
+ while (<MAPSECRETS>) {
+ next if /^\s*(#|$)/;
+ /^([\w\-\.]+)\s(.*)$/
+ or do { warn "strange line in mapsecrets: $_"; next; };
+ my($user, $datasrc) = ($1, $2);
+ next if $seen{$datasrc}++;
+ warn "[FS::InitHandler] preloading $datasrc for $user\n" if $DEBUG;
+ adminsuidsetup($user);
+ }
+
+ close MAPSECRETS;
+
+ #lalala probably broken on freebsd
+ ($<, $>) = ($>, $<);
+ $< = 0;
+
+}
+
+1;
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
new file mode 100644
index 0000000..ee777a4
--- /dev/null
+++ b/FS/FS/Mason.pm
@@ -0,0 +1,404 @@
+package FS::Mason;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK );
+use Exporter;
+use HTML::Mason 1.27; #http://www.masonhq.com/?ApacheModPerl2Redirect
+use HTML::Mason::Interp;
+use HTML::Mason::Compiler::ToObject;
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( mason_interps );
+
+=head1 NAME
+
+FS::Mason - Initialize the Mason environment
+
+=head1 SYNOPSIS
+
+ use FS::Mason qw( mason_interps );
+
+ my( $fs_interp, $rt_interp ) = mason_interps('apache');
+
+ #OR
+
+ my( $fs_interp, $rt_interp ) = mason_interps('standalone'); #XXX name?
+
+=head1 DESCRIPTION
+
+Initializes the Mason environment, loads all Freeside and RT libraries, etc.
+
+=cut
+
+# List of modules that you want to use from components (see Admin
+# manual for details)
+{
+ package HTML::Mason::Commands;
+
+ use strict;
+ use vars qw( %session );
+ use CGI 3.29 qw(-private_tempfiles); #3.29 to fix RT attachment problems
+ #use CGI::Carp qw(fatalsToBrowser);
+ use CGI::Cookie;
+ use List::Util qw( max min );
+ use Data::Dumper;
+ use Date::Format;
+ use Date::Parse;
+ use Time::Local;
+ use Time::Duration;
+ use DateTime;
+ use DateTime::Format::Strptime;
+ use Lingua::EN::Inflect qw(PL);
+ use Tie::IxHash;
+ use URI::URL;
+ use URI::Escape;
+ use HTML::Entities;
+ use HTML::TreeBuilder;
+ use HTML::FormatText;
+ use JSON;
+ use MIME::Base64;
+ use IO::Handle;
+ use IO::File;
+ use IO::Scalar;
+ #not actually using this yet anyway...# use IPC::Run3 0.036;
+ use Net::Whois::Raw qw(whois);
+ if ( $] < 5.006 ) {
+ eval "use Net::Whois::Raw 0.32 qw(whois)";
+ die $@ if $@;
+ }
+ use Text::CSV_XS;
+ use Spreadsheet::WriteExcel;
+ use Business::CreditCard 0.30; #for mask-aware cardtype()
+ use NetAddr::IP;
+ use String::Approx qw(amatch);
+ use Chart::LinesPoints;
+ use Chart::Mountain;
+ use Color::Scheme;
+ use HTML::Widgets::SelectLayers 0.07; #should go away in favor of
+ #selectlayers.html
+ use Locale::Country;
+ use Business::US::USPS::WebTools::AddressStandardization;
+ use FS;
+ use FS::UID qw( getotaker dbh datasrc driver_name );
+ use FS::Record qw( qsearch qsearchs fields dbdef
+ str2time_sql str2time_sql_closing
+ );
+ use FS::Conf;
+ use FS::CGI qw(header menubar table itable ntable idiot
+ eidiot myexit http_header);
+ use FS::UI::Web qw(svc_url);
+ use FS::UI::Web::small_custview qw(small_custview);
+ use FS::UI::bytecount;
+ use FS::Msgcat qw(gettext geterror);
+ use FS::Misc qw( send_email send_fax states_hash counties state_label );
+ use FS::Report::Table::Monthly;
+ use FS::TicketSystem;
+ use FS::Tron qw( tron_lint );
+
+ use FS::agent;
+ use FS::agent_type;
+ use FS::domain_record;
+ use FS::cust_bill;
+ use FS::cust_bill_pay;
+ use FS::cust_credit;
+ use FS::cust_credit_bill;
+ use FS::cust_main qw(smart_search);
+ use FS::cust_main::Import;
+ use FS::cust_main_county;
+ use FS::cust_location;
+ use FS::cust_pay;
+ use FS::cust_pkg;
+ use FS::part_pkg_taxclass;
+ use FS::cust_pkg_reason;
+ use FS::cust_refund;
+ use FS::cust_credit_refund;
+ use FS::cust_pay_refund;
+ use FS::cust_svc;
+ use FS::nas;
+ use FS::part_bill_event;
+ use FS::part_event;
+ use FS::part_event_condition;
+ use FS::part_pkg;
+ use FS::part_referral;
+ use FS::part_svc;
+ use FS::part_svc_router;
+ use FS::part_virtual_field;
+ use FS::pay_batch;
+ use FS::pkg_svc;
+ use FS::port;
+ use FS::queue qw(joblisting);
+ use FS::raddb;
+ use FS::session;
+ use FS::svc_acct;
+ use FS::svc_acct_pop qw(popselector);
+ use FS::acct_rt_transaction;
+ use FS::svc_domain;
+ use FS::svc_forward;
+ use FS::svc_www;
+ use FS::router;
+ use FS::addr_block;
+ use FS::svc_broadband;
+ use FS::svc_external;
+ use FS::type_pkgs;
+ use FS::part_export;
+ use FS::part_export_option;
+ use FS::export_svc;
+ use FS::msgcat;
+ use FS::rate;
+ use FS::rate_region;
+ use FS::rate_prefix;
+ use FS::rate_detail;
+ use FS::usage_class;
+ use FS::payment_gateway;
+ use FS::agent_payment_gateway;
+ use FS::XMLRPC;
+ use FS::payby;
+ use FS::cdr;
+ use FS::inventory_class;
+ use FS::inventory_item;
+ use FS::pkg_category;
+ use FS::pkg_class;
+ use FS::access_user;
+ use FS::access_user_pref;
+ use FS::access_group;
+ use FS::access_usergroup;
+ use FS::access_groupagent;
+ use FS::access_right;
+ use FS::AccessRight;
+ use FS::svc_phone;
+ use FS::reason_type;
+ use FS::reason;
+ use FS::cust_main_note;
+ use FS::tax_class;
+ use FS::cust_tax_location;
+ use FS::part_pkg_taxproduct;
+ use FS::part_pkg_taxoverride;
+ use FS::part_pkg_taxrate;
+ use FS::tax_rate;
+
+ if ( %%%RT_ENABLED%%% ) {
+ eval '
+ use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" );
+ use vars qw($Nobody $SystemUser);
+ use RT;
+ use RT::Tickets;
+ use RT::Transactions;
+ use RT::Users;
+ use RT::CurrentUser;
+ use RT::Templates;
+ use RT::Queues;
+ use RT::ScripActions;
+ use RT::ScripConditions;
+ use RT::Scrips;
+ use RT::Groups;
+ use RT::GroupMembers;
+ use RT::CustomFields;
+ use RT::CustomFieldValues;
+ use RT::ObjectCustomFieldValues;
+
+ #blah. manually updated from RT::Interface::Web::Handler
+ use RT::Interface::Web;
+ use MIME::Entity;
+ use Text::Wrapper;
+ use Time::ParseDate;
+ use Time::HiRes;
+ use HTML::Scrubber;
+
+ #blah. not even in RT::Interface::Web::Handler, just in
+ #html/NoAuth/css/dhandler and rt-test-dependencies. ask for it here
+ #to throw a real error instead of just a mysterious unstyled RT
+ use CSS::Squish 0.06;
+
+ #slow, unreliable, segfaults and is optional
+ #see rt/html/Ticket/Elements/ShowTransactionAttachments
+ #use Text::Quoted;
+
+ #?#use File::Path qw( rmtree );
+ #?#use File::Glob qw( bsd_glob );
+ #?#use File::Spec::Unix;
+
+ ';
+ die $@ if $@;
+ }
+
+ *CGI::redirect = sub {
+ my $self = shift;
+ my $cookie = '';
+ if ( $_[0] eq '-cookie' ) { #this isn't actually used at the moment
+ (my $x, $cookie) = (shift, shift);
+ $HTML::Mason::r->err_headers_out->add( 'Set-cookie' => $cookie );
+ }
+ my $location = shift;
+
+ use vars qw($m);
+
+ # false laziness w/below
+ if ( defined(@DBIx::Profile::ISA) ) {
+
+ if ( $FS::CurrentUser::CurrentUser->option('show_db_profile') ) {
+
+ #profiling redirect
+
+ my $page =
+ qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
+ '<BR><BR><PRE>'.
+ ( UNIVERSAL::can(dbh, 'sprintProfile')
+ ? encode_entities(dbh->sprintProfile())
+ : 'DBIx::Profile missing sprintProfile method;'.
+ 'unpatched or too old?' ).
+ #"\n\n". &sprintAutoProfile(). '</PRE>'.
+ "\n\n". '</PRE>'.
+ '</BODY></HTML>';
+
+
+ dbh->{'private_profile'} = {};
+ return $page;
+
+ } else {
+
+ #clear db profile, but normal redirect
+ dbh->{'private_profile'} = {};
+ $m->redirect($location);
+ '';
+
+ }
+
+ } else { #normal redirect
+
+ $m->redirect($location);
+ '';
+
+ }
+
+ };
+
+ sub include {
+ use vars qw($m);
+ $m->scomp(@_);
+ }
+
+ sub errorpage {
+ use vars qw($m);
+ $m->comp('/elements/errorpage.html', @_);
+ }
+
+ sub redirect {
+ my( $location ) = @_;
+ use vars qw($m);
+ $m->clear_buffer;
+ #false laziness w/above
+ if ( defined(@DBIx::Profile::ISA) ) {
+
+ if ( $FS::CurrentUser::CurrentUser->option('show_db_profile') ) {
+
+ #profiling redirect
+
+ $m->print(
+ qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
+ '<BR><BR><PRE>'.
+ ( UNIVERSAL::can(dbh, 'sprintProfile')
+ ? encode_entities(dbh->sprintProfile())
+ : 'DBIx::Profile missing sprintProfile method;'.
+ 'unpatched or too old?' ).
+ #"\n\n". &sprintAutoProfile(). '</PRE>'.
+ "\n\n". '</PRE>'.
+ '</BODY></HTML>'
+ );
+
+ dbh->{'private_profile'} = {};
+
+ } else {
+
+ #clear db profile, but normal redirect
+ dbh->{'private_profile'} = {};
+ $m->redirect($location);
+
+ }
+
+ } else { #normal redirect
+
+ $m->redirect($location);
+
+ }
+
+ }
+
+} # end package HTML::Mason::Commands;
+
+=head1 SUBROUTINE
+
+=over 4
+
+=item mason_interps [ MODE ]
+
+Returns a list consisting of two HTML::Mason::Interp objects, the first for
+Freeside pages, and the second for RT pages.
+
+#MODE can be 'apache' or 'standalone'. If not specified, defaults to 'apache'.
+
+=cut
+
+sub mason_interps {
+ my $mode = shift || 'apache';
+ my %opt = @_;
+
+ #my $request_class = 'HTML::Mason::Request'.
+ #( $mode eq 'apache' ? '::ApacheHandler' : '' );
+ my $request_class = 'FS::Mason::Request';
+
+ #not entirely sure it belongs here, but what the hey
+ if ( %%%RT_ENABLED%%% ) {
+ RT::LoadConfig();
+ }
+
+ my %interp = (
+ request_class => $request_class,
+ data_dir => '%%%MASONDATA%%%',
+ error_mode => 'output',
+ error_format => 'html',
+ ignore_warnings_expr => '.',
+ comp_root => [
+ [ 'freeside'=>'%%%FREESIDE_DOCUMENT_ROOT%%%' ],
+ [ 'rt' =>'%%%FREESIDE_DOCUMENT_ROOT%%%/rt' ],
+ ],
+ );
+
+ $interp{out_method} = $opt{outbuf} if $mode eq 'standalone' && $opt{outbuf};
+
+ my $fs_interp = new HTML::Mason::Interp (
+ %interp,
+ escape_flags => { 'js_string' => sub {
+ #${$_[0]} =~ s/(['\\\n])/'\\'.($1 eq "\n" ? 'n' : $1)/ge;
+ ${$_[0]} =~ s/(['\\])/\\$1/g;
+ ${$_[0]} =~ s/\n/\\n/g;
+ ${$_[0]} = "'". ${$_[0]}. "'";
+ }
+ },
+ );
+
+ my $rt_interp = new HTML::Mason::Interp (
+ %interp,
+ escape_flags => { 'h' => \&RT::Interface::Web::EscapeUTF8 },
+ compiler => HTML::Mason::Compiler::ToObject->new(
+ default_escape_flags => 'h',
+ allow_globals => [qw(%session)],
+ ),
+ );
+
+ ( $fs_interp, $rt_interp );
+
+}
+
+=back
+
+=head1 BUGS
+
+Lurking in the darkness...
+
+=head1 SEE ALSO
+
+L<HTML::Mason>, L<FS>, L<RT>
+
+=cut
+
+1;
diff --git a/FS/FS/Mason/Request.pm b/FS/FS/Mason/Request.pm
new file mode 100644
index 0000000..0a1df87
--- /dev/null
+++ b/FS/FS/Mason/Request.pm
@@ -0,0 +1,78 @@
+package FS::Mason::Request;
+
+use strict;
+use warnings;
+use vars qw( $FSURL $QUERY_STRING );
+use base 'HTML::Mason::Request';
+
+$FSURL = 'http://Set/FS_Mason_Request_FSURL/in_standalone_mode/';
+$QUERY_STRING = '';
+
+sub new {
+ my $class = shift;
+
+ my $superclass = $HTML::Mason::ApacheHandler::VERSION ?
+ 'HTML::Mason::Request::ApacheHandler' :
+ $HTML::Mason::CGIHandler::VERSION ?
+ 'HTML::Mason::Request::CGI' :
+ 'HTML::Mason::Request';
+
+ $class->alter_superclass( $superclass );
+
+ #huh... shouldn't alter_superclass take care of this for us?
+ __PACKAGE__->valid_params( %{ $superclass->valid_params() } );
+
+ my %opt = @_;
+ my $mode = $superclass =~ /Apache/i ? 'apache' : 'standalone';
+ freeside_setup($opt{'comp'}, $mode);
+
+ $class->SUPER::new(@_);
+
+}
+
+sub freeside_setup {
+
+ my( $filename, $mode ) = @_;
+
+ #warn "initializing for $filename\n";
+
+ if ( $filename !~ /\/rt\/.*NoAuth/ ) { #not RT images/JS
+
+ package HTML::Mason::Commands;
+ use vars qw( $cgi $p $fsurl );
+ use FS::UID qw( cgisuidsetup );
+ use FS::CGI qw( popurl rooturl );
+
+ if ( $mode eq 'apache' ) {
+ $cgi = new CGI;
+ &cgisuidsetup($cgi);
+ #&cgisuidsetup($r);
+ $fsurl = rooturl();
+ $p = popurl(2);
+ } elsif ( $mode eq 'standalone' ) {
+ $cgi = new CGI $FS::Mason::Request::QUERY_STRING; #better keep setting
+ #if you set it once
+ $FS::UID::cgi = $cgi;
+ $fsurl = $FS::Mason::Request::FSURL; #kludgy, but what the hell
+ $p = popurl(2, "$fsurl$filename");
+ } else {
+ die "unknown mode $mode";
+ }
+
+ } elsif ( $filename =~ /\/rt\/REST\/.*NoAuth/ ) {
+
+ package HTML::Mason::Commands; #?
+ use FS::UID qw( adminsuidsetup );
+
+ #need to log somebody in for the mail gw
+
+ ##old installs w/fs_selfs or selfserv??
+ #&adminsuidsetup('fs_selfservice');
+
+ &adminsuidsetup('fs_queue');
+
+ }
+
+}
+
+1;
diff --git a/FS/FS/Misc.pm b/FS/FS/Misc.pm
new file mode 100644
index 0000000..5231350
--- /dev/null
+++ b/FS/FS/Misc.pm
@@ -0,0 +1,852 @@
+package FS::Misc;
+
+use strict;
+use vars qw ( @ISA @EXPORT_OK $DEBUG );
+use Exporter;
+use Carp;
+use Data::Dumper;
+use IPC::Run qw( run timeout ); # for _pslatex
+use IPC::Run3; # for do_print... should just use IPC::Run i guess
+use File::Temp;
+#do NOT depend on any FS:: modules here, causes weird (sometimes unreproducable
+#until on client machine) dependancy loops. put them in FS::Misc::Something
+#instead
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( generate_email send_email send_fax
+ states_hash counties state_label
+ card_types
+ generate_ps generate_pdf do_print
+ csv_from_fixed
+ );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::Misc - Miscellaneous subroutines
+
+=head1 SYNOPSIS
+
+ use FS::Misc qw(send_email);
+
+ send_email();
+
+=head1 DESCRIPTION
+
+Miscellaneous subroutines. This module contains miscellaneous subroutines
+called from multiple other modules. These are not OO or necessarily related,
+but are collected here to elimiate code duplication.
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item generate_email OPTION => VALUE ...
+
+Options:
+
+=over 4
+
+=item from
+
+Sender address, required
+
+=item to
+
+Recipient address, required
+
+=item subject
+
+email subject, required
+
+=item html_body
+
+Email body (HTML alternative). Arrayref of lines, or scalar.
+
+Will be placed inside an HTML <BODY> tag.
+
+=item text_body
+
+Email body (Text alternative). Arrayref of lines, or scalar.
+
+=back
+
+Returns an argument list to be passsed to L<send_email>.
+
+=cut
+
+#false laziness w/FS::cust_bill::generate_email
+
+use MIME::Entity;
+use HTML::Entities;
+
+sub generate_email {
+ my %args = @_;
+
+ my $me = '[FS::Misc::generate_email]';
+
+ my %return = (
+ 'from' => $args{'from'},
+ 'to' => $args{'to'},
+ 'subject' => $args{'subject'},
+ );
+
+ #if (ref($args{'to'}) eq 'ARRAY') {
+ # $return{'to'} = $args{'to'};
+ #} else {
+ # $return{'to'} = [ grep { $_ !~ /^(POST|FAX)$/ }
+ # $self->cust_main->invoicing_list
+ # ];
+ #}
+
+ warn "$me creating HTML/text multipart message"
+ if $DEBUG;
+
+ $return{'nobody'} = 1;
+
+ my $alternative = build MIME::Entity
+ 'Type' => 'multipart/alternative',
+ 'Encoding' => '7bit',
+ 'Disposition' => 'inline'
+ ;
+
+ my $data;
+ if ( ref($args{'text_body'}) eq 'ARRAY' ) {
+ $data = $args{'text_body'};
+ } else {
+ $data = [ split(/\n/, $args{'text_body'}) ];
+ }
+
+ $alternative->attach(
+ 'Type' => 'text/plain',
+ #'Encoding' => 'quoted-printable',
+ 'Encoding' => '7bit',
+ 'Data' => $data,
+ 'Disposition' => 'inline',
+ );
+
+ my @html_data;
+ if ( ref($args{'html_body'}) eq 'ARRAY' ) {
+ @html_data = @{ $args{'html_body'} };
+ } else {
+ @html_data = split(/\n/, $args{'html_body'});
+ }
+
+ $alternative->attach(
+ 'Type' => 'text/html',
+ 'Encoding' => 'quoted-printable',
+ 'Data' => [ '<html>',
+ ' <head>',
+ ' <title>',
+ ' '. encode_entities($return{'subject'}),
+ ' </title>',
+ ' </head>',
+ ' <body bgcolor="#e8e8e8">',
+ @html_data,
+ ' </body>',
+ '</html>',
+ ],
+ 'Disposition' => 'inline',
+ #'Filename' => 'invoice.pdf',
+ );
+
+ #no other attachment:
+ # multipart/related
+ # multipart/alternative
+ # text/plain
+ # text/html
+
+ $return{'content-type'} = 'multipart/related';
+ $return{'mimeparts'} = [ $alternative ];
+ $return{'type'} = 'multipart/alternative'; #Content-Type of first part...
+ #$return{'disposition'} = 'inline';
+
+ %return;
+
+}
+
+=item send_email OPTION => VALUE ...
+
+Options:
+
+=over 4
+
+=item from
+
+(required)
+
+=item to
+
+(required) comma-separated scalar or arrayref of recipients
+
+=item subject
+
+(required)
+
+=item content-type
+
+(optional) MIME type for the body
+
+=item body
+
+(required unless I<nobody> is true) arrayref of body text lines
+
+=item mimeparts
+
+(optional, but required if I<nobody> is true) arrayref of MIME::Entity->build PARAMHASH refs or MIME::Entity objects. These will be passed as arguments to MIME::Entity->attach().
+
+=item nobody
+
+(optional) when set true, send_email will ignore the I<body> option and simply construct a message with the given I<mimeparts>. In this case,
+I<content-type>, if specified, overrides the default "multipart/mixed" for the outermost MIME container.
+
+=item content-encoding
+
+(optional) when using nobody, optional top-level MIME
+encoding which, if specified, overrides the default "7bit".
+
+=item type
+
+(optional) type parameter for multipart/related messages
+
+=back
+
+=cut
+
+use vars qw( $conf );
+use Date::Format;
+use Mail::Header;
+use Mail::Internet 2.00;
+use MIME::Entity;
+use FS::UID;
+
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+} );
+
+sub send_email {
+ my(%options) = @_;
+ if ( $DEBUG ) {
+ my %doptions = %options;
+ $doptions{'body'} = '(full body not shown in debug)';
+ warn "FS::Misc::send_email called with options:\n ". Dumper(\%doptions);
+# join("\n", map { " $_: ". $options{$_} } keys %options ). "\n"
+ }
+
+ $ENV{MAILADDRESS} = $options{'from'};
+ my $to = ref($options{to}) ? join(', ', @{ $options{to} } ) : $options{to};
+
+ my @mimeargs = ();
+ my @mimeparts = ();
+ if ( $options{'nobody'} ) {
+
+ croak "'mimeparts' option required when 'nobody' option given\n"
+ unless $options{'mimeparts'};
+
+ @mimeparts = @{$options{'mimeparts'}};
+
+ @mimeargs = (
+ 'Type' => ( $options{'content-type'} || 'multipart/mixed' ),
+ 'Encoding' => ( $options{'content-encoding'} || '7bit' ),
+ );
+
+ } else {
+
+ @mimeparts = @{$options{'mimeparts'}}
+ if ref($options{'mimeparts'}) eq 'ARRAY';
+
+ if (scalar(@mimeparts)) {
+
+ @mimeargs = (
+ 'Type' => 'multipart/mixed',
+ 'Encoding' => '7bit',
+ );
+
+ unshift @mimeparts, {
+ 'Type' => ( $options{'content-type'} || 'text/plain' ),
+ 'Data' => $options{'body'},
+ 'Encoding' => ( $options{'content-type'} ? '-SUGGEST' : '7bit' ),
+ 'Disposition' => 'inline',
+ };
+
+ } else {
+
+ @mimeargs = (
+ 'Type' => ( $options{'content-type'} || 'text/plain' ),
+ 'Data' => $options{'body'},
+ 'Encoding' => ( $options{'content-type'} ? '-SUGGEST' : '7bit' ),
+ );
+
+ }
+
+ }
+
+ my $domain;
+ if ( $options{'from'} =~ /\@([\w\.\-]+)/ ) {
+ $domain = $1;
+ } else {
+ warn 'no domain found in invoice from address '. $options{'from'}.
+ '; constructing Message-ID @example.com';
+ $domain = 'example.com';
+ }
+ my $message_id = join('.', rand()*(2**32), $$, time). "\@$domain";
+
+ my $message = MIME::Entity->build(
+ 'From' => $options{'from'},
+ 'To' => $to,
+ 'Sender' => $options{'from'},
+ 'Reply-To' => $options{'from'},
+ 'Date' => time2str("%a, %d %b %Y %X %z", time),
+ 'Subject' => $options{'subject'},
+ 'Message-ID' => "<$message_id>",
+ @mimeargs,
+ );
+
+ if ( $options{'type'} ) {
+ #false laziness w/cust_bill::generate_email
+ $message->head->replace('Content-type',
+ $message->mime_type.
+ '; boundary="'. $message->head->multipart_boundary. '"'.
+ '; type='. $options{'type'}
+ );
+ }
+
+ foreach my $part (@mimeparts) {
+
+ if ( UNIVERSAL::isa($part, 'MIME::Entity') ) {
+
+ warn "attaching MIME part from MIME::Entity object\n"
+ if $DEBUG;
+ $message->add_part($part);
+
+ } elsif ( ref($part) eq 'HASH' ) {
+
+ warn "attaching MIME part from hashref:\n".
+ join("\n", map " $_: ".$part->{$_}, keys %$part ). "\n"
+ if $DEBUG;
+ $message->attach(%$part);
+
+ } else {
+ croak "mimepart $part isn't a hashref or MIME::Entity object!";
+ }
+
+ }
+
+ my $smtpmachine = $conf->config('smtpmachine');
+ $!=0;
+
+ $message->mysmtpsend( 'Host' => $smtpmachine,
+ 'MailFrom' => $options{'from'},
+ );
+
+}
+
+#this kludges a "mysmtpsend" method into Mail::Internet for send_email above
+#now updated for MailTools v2!
+package Mail::Internet;
+
+use Mail::Address;
+use Net::SMTP;
+use Net::Domain;
+
+sub Mail::Internet::mysmtpsend($@) {
+ my ($self, %opt) = @_;
+
+ my $host = $opt{Host};
+ my $envelope = $opt{MailFrom}; # || mailaddress();
+ my $quit = 1;
+
+ my ($smtp, @hello);
+
+ push @hello, Hello => $opt{Hello}
+ if defined $opt{Hello};
+
+ push @hello, Port => $opt{Port}
+ if exists $opt{Port};
+
+ push @hello, Debug => $opt{Debug}
+ if exists $opt{Debug};
+
+# if(!defined $host)
+# { local $SIG{__DIE__};
+# my @hosts = qw(mailhost localhost);
+# unshift @hosts, split /\:/, $ENV{SMTPHOSTS}
+# if defined $ENV{SMTPHOSTS};
+#
+# foreach $host (@hosts)
+# { $smtp = eval { Net::SMTP->new($host, @hello) };
+# last if defined $smtp;
+# }
+# }
+# elsif(ref($host) && UNIVERSAL::isa($host,'Net::SMTP'))
+ if(ref($host) && UNIVERSAL::isa($host,'Net::SMTP'))
+ { $smtp = $host;
+ $quit = 0;
+ }
+ else
+ { #local $SIG{__DIE__};
+ #$smtp = eval { Net::SMTP->new($host, @hello) };
+ $smtp = Net::SMTP->new($host, @hello);
+ }
+
+ unless ( defined($smtp) ) {
+ my $err = $!;
+ $err =~ s/Invalid argument/Unknown host/;
+ return "can't connect to $host: $err"
+ }
+
+ my $head = $self->cleaned_header_dup;
+
+ $head->delete('Bcc');
+
+ # Who is it to
+
+ my @rcpt = map { ref $_ ? @$_ : $_ } grep { defined } @opt{'To','Cc','Bcc'};
+ @rcpt = map { $head->get($_) } qw(To Cc Bcc)
+ unless @rcpt;
+
+ my @addr = map {$_->address} Mail::Address->parse(@rcpt);
+ #@addr or return ();
+ return 'No valid destination addresses found!'
+ unless(@addr);
+
+ # Send it
+
+ my $ok = $smtp->mail($envelope)
+ && $smtp->to(@addr)
+ && $smtp->data(join("", @{$head->header}, "\n", @{$self->body}));
+
+ #$quit && $smtp->quit;
+ #$ok ? @addr : ();
+ if ( $ok ) {
+ $quit && $smtp->quit;
+ return '';
+ } else {
+ return $smtp->code. ' '. $smtp->message;
+ }
+}
+package FS::Misc;
+#eokludge
+
+=item send_fax OPTION => VALUE ...
+
+Options:
+
+I<dialstring> - (required) 10-digit phone number w/ area code
+
+I<docdata> - (required) Array ref containing PostScript or TIFF Class F document
+
+-or-
+
+I<docfile> - (required) Filename of PostScript TIFF Class F document
+
+...any other options will be passed to L<Fax::Hylafax::Client::sendfax>
+
+
+=cut
+
+sub send_fax {
+
+ my %options = @_;
+
+ die 'HylaFAX support has not been configured.'
+ unless $conf->exists('hylafax');
+
+ eval {
+ require Fax::Hylafax::Client;
+ };
+
+ if ($@) {
+ if ($@ =~ /^Can't locate Fax.*/) {
+ die "You must have Fax::Hylafax::Client installed to use invoice faxing."
+ } else {
+ die $@;
+ }
+ }
+
+ my %hylafax_opts = map { split /\s+/ } $conf->config('hylafax');
+
+ die 'Called send_fax without a \'dialstring\'.'
+ unless exists($options{'dialstring'});
+
+ if (exists($options{'docdata'}) and ref($options{'docdata'}) eq 'ARRAY') {
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ my $fh = new File::Temp(
+ TEMPLATE => 'faxdoc.'. $options{'dialstring'} . '.XXXXXXXX',
+ DIR => $dir,
+ UNLINK => 0,
+ ) or die "can't open temp file: $!\n";
+
+ $options{docfile} = $fh->filename;
+
+ print $fh @{$options{'docdata'}};
+ close $fh;
+
+ delete $options{'docdata'};
+ }
+
+ die 'Called send_fax without a \'docfile\' or \'docdata\'.'
+ unless exists($options{'docfile'});
+
+ #FIXME: Need to send canonical dialstring to HylaFAX, but this only
+ # works in the US.
+
+ $options{'dialstring'} =~ s/[^\d\+]//g;
+ if ($options{'dialstring'} =~ /^\d{10}$/) {
+ $options{dialstring} = '+1' . $options{'dialstring'};
+ } else {
+ return 'Invalid dialstring ' . $options{'dialstring'} . '.';
+ }
+
+ my $faxjob = &Fax::Hylafax::Client::sendfax(%options, %hylafax_opts);
+
+ if ($faxjob->success) {
+ warn "Successfully queued fax to '$options{dialstring}' with jobid " .
+ $faxjob->jobid
+ if $DEBUG;
+ return '';
+ } else {
+ return 'Error while sending FAX: ' . $faxjob->trace;
+ }
+
+}
+
+=item states_hash COUNTRY
+
+Returns a list of key/value pairs containing state (or other sub-country
+division) abbriviations and names.
+
+=cut
+
+use FS::Record qw(qsearch);
+use Locale::SubCountry;
+
+sub states_hash {
+ my($country) = @_;
+
+ my @states =
+# sort
+ map { s/[\n\r]//g; $_; }
+ map { $_->state; }
+ qsearch({
+ 'select' => 'state',
+ 'table' => 'cust_main_county',
+ 'hashref' => { 'country' => $country },
+ 'extra_sql' => 'GROUP BY state',
+ });
+
+ #it could throw a fatal "Invalid country code" error (for example "AX")
+ my $subcountry = eval { new Locale::SubCountry($country) }
+ or return ( '', '(n/a)' );
+
+ #"i see your schwartz is as big as mine!"
+ map { ( $_->[0] => $_->[1] ) }
+ sort { $a->[1] cmp $b->[1] }
+ map { [ $_ => state_label($_, $subcountry) ] }
+ @states;
+}
+
+=item counties STATE COUNTRY
+
+Returns a list of counties for this state and country.
+
+=cut
+
+sub counties {
+ my( $state, $country ) = @_;
+
+ sort map { s/[\n\r]//g; $_; }
+ map { $_->county }
+ qsearch({
+ 'select' => 'DISTINCT county',
+ 'table' => 'cust_main_county',
+ 'hashref' => { 'state' => $state,
+ 'country' => $country,
+ },
+ });
+}
+
+=item state_label STATE COUNTRY_OR_LOCALE_SUBCOUNRY_OBJECT
+
+=cut
+
+sub state_label {
+ my( $state, $country ) = @_;
+
+ unless ( ref($country) ) {
+ $country = eval { new Locale::SubCountry($country) }
+ or return'(n/a)';
+
+ }
+
+ # US kludge to avoid changing existing behaviour
+ # also we actually *use* the abbriviations...
+ my $full_name = $country->country_code eq 'US'
+ ? ''
+ : $country->full_name($state);
+
+ $full_name = '' if $full_name eq 'unknown';
+ $full_name =~ s/\(see also.*\)\s*$//;
+ $full_name .= " ($state)" if $full_name;
+
+ $full_name || $state || '(n/a)';
+
+}
+
+=item card_types
+
+Returns a hash reference of the accepted credit card types. Keys are shorter
+identifiers and values are the longer strings used by the system (see
+L<Business::CreditCard>).
+
+=cut
+
+#$conf from above
+
+sub card_types {
+ my $conf = new FS::Conf;
+
+ my %card_types = (
+ #displayname #value (Business::CreditCard)
+ "VISA" => "VISA card",
+ "MasterCard" => "MasterCard",
+ "Discover" => "Discover card",
+ "American Express" => "American Express card",
+ "Diner's Club/Carte Blanche" => "Diner's Club/Carte Blanche",
+ "enRoute" => "enRoute",
+ "JCB" => "JCB",
+ "BankCard" => "BankCard",
+ "Switch" => "Switch",
+ "Solo" => "Solo",
+ );
+ my @conf_card_types = grep { ! /^\s*$/ } $conf->config('card-types');
+ if ( @conf_card_types ) {
+ #perhaps the hash is backwards for this, but this way works better for
+ #usage in selfservice
+ %card_types = map { $_ => $card_types{$_} }
+ grep {
+ my $d = $_;
+ grep { $card_types{$d} eq $_ } @conf_card_types
+ }
+ keys %card_types;
+ }
+
+ \%card_types;
+}
+
+=item generate_ps FILENAME
+
+Returns an postscript rendition of the LaTex file, as a scalar.
+FILENAME does not contain the .tex suffix and is unlinked by this function.
+
+=cut
+
+use String::ShellQuote;
+
+sub generate_ps {
+ my $file = shift;
+
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ chdir($dir);
+
+ _pslatex($file);
+
+ system('dvips', '-q', '-t', 'letter', "$file.dvi", '-o', "$file.ps" ) == 0
+ or die "dvips failed";
+
+ open(POSTSCRIPT, "<$file.ps")
+ or die "can't open $file.ps: $! (error in LaTeX template?)\n";
+
+ unlink("$file.dvi", "$file.log", "$file.aux", "$file.ps", "$file.tex");
+
+ my $ps = '';
+
+ if ( $conf->exists('lpr-postscript_prefix') ) {
+ my $prefix = $conf->config('lpr-postscript_prefix');
+ $ps .= eval qq("$prefix");
+ }
+
+ while (<POSTSCRIPT>) {
+ $ps .= $_;
+ }
+
+ close POSTSCRIPT;
+
+ if ( $conf->exists('lpr-postscript_suffix') ) {
+ my $suffix = $conf->config('lpr-postscript_suffix');
+ $ps .= eval qq("$suffix");
+ }
+
+ return $ps;
+
+}
+
+=item generate_pdf FILENAME
+
+Returns an PDF rendition of the LaTex file, as a scalar. FILENAME does not
+contain the .tex suffix and is unlinked by this function.
+
+=cut
+
+use String::ShellQuote;
+
+sub generate_pdf {
+ my $file = shift;
+
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ chdir($dir);
+
+ #system('pdflatex', "$file.tex");
+ #system('pdflatex', "$file.tex");
+ #! LaTeX Error: Unknown graphics extension: .eps.
+
+ _pslatex($file);
+
+ my $sfile = shell_quote $file;
+
+ #system('dvipdf', "$file.dvi", "$file.pdf" );
+ system(
+ "dvips -q -t letter -f $sfile.dvi ".
+ "| gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$sfile.pdf ".
+ " -c save pop -"
+ ) == 0
+ or die "dvips | gs failed: $!";
+
+ open(PDF, "<$file.pdf")
+ or die "can't open $file.pdf: $! (error in LaTeX template?)\n";
+
+ unlink("$file.dvi", "$file.log", "$file.aux", "$file.pdf", "$file.tex");
+
+ my $pdf = '';
+ while (<PDF>) {
+ $pdf .= $_;
+ }
+
+ close PDF;
+
+ return $pdf;
+
+}
+
+sub _pslatex {
+ my $file = shift;
+
+ #my $sfile = shell_quote $file;
+
+ my @cmd = (
+ 'latex',
+ '-interaction=batchmode',
+ '\AtBeginDocument{\RequirePackage{pslatex}}',
+ '\def\PSLATEXTMP{\futurelet\PSLATEXTMP\PSLATEXTMPB}',
+ '\def\PSLATEXTMPB{\ifx\PSLATEXTMP\nonstopmode\else\input\fi}',
+ '\PSLATEXTMP',
+ "$file.tex"
+ );
+
+ my $timeout = 30; #? should be more than enough
+
+ for ( 1, 2 ) {
+
+ local($SIG{CHLD}) = sub {};
+ run( \@cmd, '>'=>'/dev/null', '2>'=>'/dev/null', timeout($timeout) )
+ or die "pslatex $file.tex failed; see $file.log for details?\n";
+
+ }
+
+}
+
+=item print ARRAYREF
+
+Sends the lines in ARRAYREF to the printer.
+
+=cut
+
+sub do_print {
+ my $data = shift;
+
+ my $lpr = $conf->config('lpr');
+
+ my $outerr = '';
+ run3 $lpr, $data, \$outerr, \$outerr;
+ if ( $? ) {
+ $outerr = ": $outerr" if length($outerr);
+ die "Error from $lpr (exit status ". ($?>>8). ")$outerr\n";
+ }
+
+}
+
+=item csv_from_fixed, FILEREF COUNTREF, [ LENGTH_LISTREF, [ CALLBACKS_LISTREF ] ]
+
+Converts the filehandle referenced by FILEREF from fixed length record
+lines to a CSV file according to the lengths specified in LENGTH_LISTREF.
+The CALLBACKS_LISTREF refers to a correpsonding list of coderefs. Each
+should return the value to be substituted in place of its single argument.
+
+Returns false on success or an error if one occurs.
+
+=cut
+
+sub csv_from_fixed {
+ my( $fhref, $countref, $lengths, $callbacks) = @_;
+
+ eval { require Text::CSV_XS; };
+ return $@ if $@;
+
+ my $ofh = $$fhref;
+ my $unpacker = new Text::CSV_XS;
+ my $total = 0;
+ my $template = join('', map {$total += $_; "A$_"} @$lengths) if $lengths;
+
+ my $dir = "%%%FREESIDE_CACHE%%%/cache.$FS::UID::datasrc";
+ my $fh = new File::Temp( TEMPLATE => "FILE.csv.XXXXXXXX",
+ DIR => $dir,
+ UNLINK => 0,
+ ) or return "can't open temp file: $!\n"
+ if $template;
+
+ while ( defined(my $line=<$ofh>) ) {
+ $$countref++;
+ if ( $template ) {
+ my $column = 0;
+
+ chomp $line;
+ return "unexpected input at line $$countref: $line".
+ " -- expected $total but received ". length($line)
+ unless length($line) == $total;
+
+ $unpacker->combine( map { my $i = $column++;
+ defined( $callbacks->[$i] )
+ ? &{ $callbacks->[$i] }( $_ )
+ : $_
+ } unpack( $template, $line )
+ )
+ or return "invalid data for CSV: ". $unpacker->error_input;
+
+ print $fh $unpacker->string(), "\n"
+ or return "can't write temp file: $!\n";
+ }
+ }
+
+ if ( $template ) { close $$fhref; $$fhref = $fh }
+
+ seek $$fhref, 0, 0;
+ '';
+}
+
+
+=back
+
+=head1 BUGS
+
+This package exists.
+
+=head1 SEE ALSO
+
+L<FS::UID>, L<FS::CGI>, L<FS::Record>, the base documentation.
+
+L<Fax::Hylafax::Client>
+
+=cut
+
+1;
diff --git a/FS/FS/Misc/prune.pm b/FS/FS/Misc/prune.pm
new file mode 100644
index 0000000..3f0c79d
--- /dev/null
+++ b/FS/FS/Misc/prune.pm
@@ -0,0 +1,131 @@
+package FS::Misc::prune;
+
+use strict;
+use vars qw ( @ISA @EXPORT_OK $DEBUG );
+use Exporter;
+use FS::Record qw(dbh qsearch);
+use FS::cust_credit_refund;
+#use FS::cust_credit_bill;
+#use FS::cust_bill_pay;
+#use FS::cust_pay_refund;
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( prune_applications );
+
+=head1 NAME
+
+FS::Misc::prune - misc. pruning subroutines
+
+=head1 SYNOPSIS
+
+use FS::Misc::prune qw(prune_applications);
+
+prune_applications();
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item prune_applications OPTION_HASH
+
+Removes applications of credits to refunds in the event that the database
+is corrupt and either the credits or refunds are missing (see
+L<FS::cust_credit>, L<FS::cust_refund>, and L<FS::cust_credit_refund>).
+If the OPTION_HASH contains the element 'dry_run' then a report of
+affected records is returned rather than actually deleting the records.
+
+=cut
+
+sub prune_applications {
+ my $options = shift;
+ my $dbh = dbh;
+
+ local $DEBUG = 1 if exists($options->{debug});
+
+ my $ccr = <<EOW;
+ WHERE
+ 0 = (select count(*) from cust_credit
+ where cust_credit_refund.crednum = cust_credit.crednum)
+ or
+ 0 = (select count(*) from cust_refund
+ where cust_credit_refund.refundnum = cust_refund.refundnum)
+EOW
+ my $ccb = <<EOW;
+ WHERE
+ 0 = (select count(*) from cust_credit
+ where cust_credit_bill.crednum = cust_credit.crednum)
+ or
+ 0 = (select count(*) from cust_bill
+ where cust_credit_bill.invnum = cust_bill.invnum)
+EOW
+ my $cbp = <<EOW;
+ WHERE
+ 0 = (select count(*) from cust_bill
+ where cust_bill_pay.invnum = cust_bill.invnum)
+ or
+ 0 = (select count(*) from cust_pay
+ where cust_bill_pay.paynum = cust_pay.paynum)
+EOW
+ my $cpr = <<EOW;
+ WHERE
+ 0 = (select count(*) from cust_pay
+ where cust_pay_refund.paynum = cust_pay.paynum)
+ or
+ 0 = (select count(*) from cust_refund
+ where cust_pay_refund.refundnum = cust_refund.refundnum)
+EOW
+
+ my %strays = (
+ 'cust_credit_refund' => { clause => $ccr,
+ link1 => 'crednum',
+ link2 => 'refundnum',
+ },
+# 'cust_credit_bill' => { clause => $ccb,
+# link1 => 'crednum',
+# link2 => 'refundnum',
+# },
+# 'cust_bill_pay' => { clause => $cbp,
+# link1 => 'crednum',
+# link2 => 'refundnum',
+# },
+# 'cust_pay_refund' => { clause => $cpr,
+# link1 => 'crednum',
+# link2 => 'refundnum',
+# },
+ );
+
+ if ( exists($options->{dry_run}) ) {
+ my @response = ();
+ foreach my $table (keys %strays) {
+ my $clause = $strays{$table}->{clause};
+ my $link1 = $strays{$table}->{link1};
+ my $link2 = $strays{$table}->{link2};
+ my @rec = qsearch($table, {}, '', $clause);
+ my $keyname = $rec[0]->primary_key if $rec[0];
+ foreach (@rec) {
+ push @response, "$table " .$_->$keyname . " claims attachment to ".
+ "$link1 " . $_->$link1 . " and $link2 " . $_->$link2 . "\n";
+ }
+ }
+ return (@response);
+ } else {
+ foreach (keys %strays) {
+ my $statement = "DELETE FROM $_ " . $strays{$_}->{clause};
+ warn $statement if $DEBUG;
+ my $sth = $dbh->prepare($statement)
+ or die $dbh->errstr;
+ $sth->execute
+ or die $sth->errstr;
+ }
+ return ();
+ }
+}
+
+=back
+
+=head1 BUGS
+
+=cut
+
+1;
+
diff --git a/FS/FS/Msgcat.pm b/FS/FS/Msgcat.pm
new file mode 100644
index 0000000..70933b2
--- /dev/null
+++ b/FS/FS/Msgcat.pm
@@ -0,0 +1,100 @@
+package FS::Msgcat;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK $conf $locale $debug );
+use Exporter;
+use FS::UID;
+#use FS::Record qw( qsearchs ); # wtf? won't import...
+use FS::Record;
+#use FS::Conf; #wtf? causes dependency loops too.
+use FS::msgcat;
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw( gettext geterror );
+
+FS::UID->install_callback( sub {
+ eval "use FS::Conf;";
+ die $@ if $@;
+ $conf = new FS::Conf;
+ $locale = $conf->config('locale') || 'en_US';
+ $debug = $conf->exists('show-msgcat-codes')
+});
+
+=head1 NAME
+
+FS::Msgcat - Message catalog functions
+
+=head1 SYNOPSIS
+
+ use FS::Msgcat qw(gettext geterror);
+
+ #simple interface for retreiving messages...
+ $message = gettext('msgcode');
+ #or errors (includes the error code)
+ $message = geterror('msgcode');
+
+=head1 DESCRIPTION
+
+FS::Msgcat provides functions to use the message catalog. If you want to
+maintain the message catalog database, see L<FS::msgcat> instead.
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item gettext MSGCODE
+
+Returns the full message for the supplied message code.
+
+=cut
+
+sub gettext {
+ $debug ? geterror(@_) : _gettext(@_);
+}
+
+sub _gettext {
+ my $msgcode = shift;
+ my $msgcat = FS::Record::qsearchs('msgcat', {
+ 'msgcode' => $msgcode,
+ 'locale' => $locale
+ } );
+ if ( $msgcat ) {
+ $msgcat->msg;
+ } else {
+ warn "WARNING: message for msgcode $msgcode in locale $locale not found";
+ $msgcode;
+ }
+
+}
+
+=item geterror MSGCODE
+
+Returns the full message for the supplied message code, including the message
+code.
+
+=cut
+
+sub geterror {
+ my $msgcode = shift;
+ my $msg = _gettext($msgcode);
+ if ( $msg eq $msgcode ) {
+ "Error code $msgcode (message for locale $locale not found)";
+ } else {
+ "$msg (error code $msgcode)";
+ }
+}
+
+=back
+
+=head1 BUGS
+
+i18n/l10n, eek
+
+=head1 SEE ALSO
+
+L<FS::msgcat>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/Pony.pm b/FS/FS/Pony.pm
new file mode 100644
index 0000000..c37dd78
--- /dev/null
+++ b/FS/FS/Pony.pm
@@ -0,0 +1,23 @@
+package FS::Pony;
+
+=head1 NAME
+
+FS::Pony - A pony
+
+=head1 SYNOPSYS
+
+use FS::Pony; # <-- yours!
+
+=head1 DESCRIPTION
+
+We told you it came with a pony.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+http://420.am/~ivan/nopony.jpg
+
+=cut
+
+1;
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
new file mode 100644
index 0000000..2d0263b
--- /dev/null
+++ b/FS/FS/Record.pm
@@ -0,0 +1,2810 @@
+package FS::Record;
+
+use strict;
+use vars qw( $AUTOLOAD @ISA @EXPORT_OK $DEBUG
+ $conf $conf_encryption $me
+ %virtual_fields_cache
+ $nowarn_identical $nowarn_classload
+ $no_update_diff $no_check_foreign
+ );
+use Exporter;
+use Carp qw(carp cluck croak confess);
+use Scalar::Util qw( blessed );
+use File::CounterFile;
+use Locale::Country;
+use Text::CSV_XS;
+use File::Slurp qw( slurp );
+use DBI qw(:sql_types);
+use DBIx::DBSchema 0.33;
+use FS::UID qw(dbh getotaker datasrc driver_name);
+use FS::CurrentUser;
+use FS::Schema qw(dbdef);
+use FS::SearchCache;
+use FS::Msgcat qw(gettext);
+#use FS::Conf; #dependency loop bs, in install_callback below instead
+
+use FS::part_virtual_field;
+
+use Tie::IxHash;
+
+@ISA = qw(Exporter);
+
+#export dbdef for now... everything else expects to find it here
+@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch
+ str2time_sql str2time_sql_closing );
+
+$DEBUG = 0;
+$me = '[FS::Record]';
+
+$nowarn_identical = 0;
+$nowarn_classload = 0;
+$no_update_diff = 0;
+$no_check_foreign = 0;
+
+my $rsa_module;
+my $rsa_loaded;
+my $rsa_encrypt;
+my $rsa_decrypt;
+
+$conf = '';
+$conf_encryption = '';
+FS::UID->install_callback( sub {
+ eval "use FS::Conf;";
+ die $@ if $@;
+ $conf = FS::Conf->new;
+ $conf_encryption = $conf->exists('encryption');
+ $File::CounterFile::DEFAULT_DIR = $conf->base_dir . "/counters.". datasrc;
+} );
+
+
+=head1 NAME
+
+FS::Record - Database record objects
+
+=head1 SYNOPSIS
+
+ use FS::Record;
+ use FS::Record qw(dbh fields qsearch qsearchs);
+
+ $record = new FS::Record 'table', \%hash;
+ $record = new FS::Record 'table', { 'column' => 'value', ... };
+
+ $record = qsearchs FS::Record 'table', \%hash;
+ $record = qsearchs FS::Record 'table', { 'column' => 'value', ... };
+ @records = qsearch FS::Record 'table', \%hash;
+ @records = qsearch FS::Record 'table', { 'column' => 'value', ... };
+
+ $table = $record->table;
+ $dbdef_table = $record->dbdef_table;
+
+ $value = $record->get('column');
+ $value = $record->getfield('column');
+ $value = $record->column;
+
+ $record->set( 'column' => 'value' );
+ $record->setfield( 'column' => 'value' );
+ $record->column('value');
+
+ %hash = $record->hash;
+
+ $hashref = $record->hashref;
+
+ $error = $record->insert;
+
+ $error = $record->delete;
+
+ $error = $new_record->replace($old_record);
+
+ # external use deprecated - handled by the database (at least for Pg, mysql)
+ $value = $record->unique('column');
+
+ $error = $record->ut_float('column');
+ $error = $record->ut_floatn('column');
+ $error = $record->ut_number('column');
+ $error = $record->ut_numbern('column');
+ $error = $record->ut_snumber('column');
+ $error = $record->ut_snumbern('column');
+ $error = $record->ut_money('column');
+ $error = $record->ut_text('column');
+ $error = $record->ut_textn('column');
+ $error = $record->ut_alpha('column');
+ $error = $record->ut_alphan('column');
+ $error = $record->ut_phonen('column');
+ $error = $record->ut_anything('column');
+ $error = $record->ut_name('column');
+
+ $quoted_value = _quote($value,'table','field');
+
+ #deprecated
+ $fields = hfields('table');
+ if ( $fields->{Field} ) { # etc.
+
+ @fields = fields 'table'; #as a subroutine
+ @fields = $record->fields; #as a method call
+
+
+=head1 DESCRIPTION
+
+(Mostly) object-oriented interface to database records. Records are currently
+implemented on top of DBI. FS::Record is intended as a base class for
+table-specific classes to inherit from, i.e. FS::cust_main.
+
+=head1 CONSTRUCTORS
+
+=over 4
+
+=item new [ TABLE, ] HASHREF
+
+Creates a new record. It doesn't store it in the database, though. See
+L<"insert"> for that.
+
+Note that the object stores this hash reference, not a distinct copy of the
+hash it points to. You can ask the object for a copy with the I<hash>
+method.
+
+TABLE can only be omitted when a dervived class overrides the table method.
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+
+ unless ( defined ( $self->table ) ) {
+ $self->{'Table'} = shift;
+ carp "warning: FS::Record::new called with table name ". $self->{'Table'}
+ unless $nowarn_classload;
+ }
+
+ $self->{'Hash'} = shift;
+
+ foreach my $field ( grep !defined($self->{'Hash'}{$_}), $self->fields ) {
+ $self->{'Hash'}{$field}='';
+ }
+
+ $self->_rebless if $self->can('_rebless');
+
+ $self->{'modified'} = 0;
+
+ $self->_cache($self->{'Hash'}, shift) if $self->can('_cache') && @_;
+
+ $self;
+}
+
+sub new_or_cached {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+
+ $self->{'Table'} = shift unless defined ( $self->table );
+
+ my $hashref = $self->{'Hash'} = shift;
+ my $cache = shift;
+ if ( defined( $cache->cache->{$hashref->{$cache->key}} ) ) {
+ my $obj = $cache->cache->{$hashref->{$cache->key}};
+ $obj->_cache($hashref, $cache) if $obj->can('_cache');
+ $obj;
+ } else {
+ $cache->cache->{$hashref->{$cache->key}} = $self->new($hashref, $cache);
+ }
+
+}
+
+sub create {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ if ( defined $self->table ) {
+ cluck "create constructor is deprecated, use new!";
+ $self->new(@_);
+ } else {
+ croak "FS::Record::create called (not from a subclass)!";
+ }
+}
+
+=item qsearch PARAMS_HASHREF | TABLE, HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ, ADDL_FROM
+
+Searches the database for all records matching (at least) the key/value pairs
+in HASHREF. Returns all the records found as `FS::TABLE' objects if that
+module is loaded (i.e. via `use FS::cust_main;'), otherwise returns FS::Record
+objects.
+
+The preferred usage is to pass a hash reference of named parameters:
+
+ my @records = qsearch( {
+ 'table' => 'table_name',
+ 'hashref' => { 'field' => 'value'
+ 'field' => { 'op' => '<',
+ 'value' => '420',
+ },
+ },
+
+ #these are optional...
+ 'select' => '*',
+ 'extra_sql' => 'AND field ',
+ 'order_by' => 'ORDER BY something',
+ #'cache_obj' => '', #optional
+ 'addl_from' => 'LEFT JOIN othtable USING ( field )',
+ 'debug' => 1,
+ }
+ );
+
+Much code still uses old-style positional parameters, this is also probably
+fine in the common case where there are only two parameters:
+
+ my @records = qsearch( 'table', { 'field' => 'value' } );
+
+###oops, argh, FS::Record::new only lets us create database fields.
+#Normal behaviour if SELECT is not specified is `*', as in
+#C<SELECT * FROM table WHERE ...>. However, there is an experimental new
+#feature where you can specify SELECT - remember, the objects returned,
+#although blessed into the appropriate `FS::TABLE' package, will only have the
+#fields you specify. This might have unwanted results if you then go calling
+#regular FS::TABLE methods
+#on it.
+
+=cut
+
+my %TYPE = (); #for debugging
+
+sub _is_fs_float {
+ my ($type, $value) = @_;
+ if ( ( $type =~ /(numeric)/i && $value =~ /^[+-]?\d+(\.\d+)?$/ ) ||
+ ( $type =~ /(real|float4)/i && $value =~ /[-+]?\d*\.?\d+([eE][-+]?\d+)?/)
+ ) {
+ return 1;
+ }
+ '';
+}
+
+sub qsearch {
+ my($stable, $record, $select, $extra_sql, $order_by, $cache, $addl_from );
+ my $debug = '';
+ if ( ref($_[0]) ) { #hashref for now, eventually maybe accept a list too
+ my $opt = shift;
+ $stable = $opt->{'table'} or die "table name is required";
+ $record = $opt->{'hashref'} || {};
+ $select = $opt->{'select'} || '*';
+ $extra_sql = $opt->{'extra_sql'} || '';
+ $order_by = $opt->{'order_by'} || '';
+ $cache = $opt->{'cache_obj'} || '';
+ $addl_from = $opt->{'addl_from'} || '';
+ $debug = $opt->{'debug'} || '';
+ } else {
+ ($stable, $record, $select, $extra_sql, $cache, $addl_from ) = @_;
+ $select ||= '*';
+ }
+
+ #$stable =~ /^([\w\_]+)$/ or die "Illegal table: $table";
+ #for jsearch
+ $stable =~ /^([\w\s\(\)\.\,\=]+)$/ or die "Illegal table: $stable";
+ $stable = $1;
+ my $dbh = dbh;
+
+ my $table = $cache ? $cache->table : $stable;
+ my $dbdef_table = dbdef->table($table)
+ or die "No schema for table $table found - ".
+ "do you need to run freeside-upgrade?";
+ my $pkey = $dbdef_table->primary_key;
+
+ my @real_fields = grep exists($record->{$_}), real_fields($table);
+ my @virtual_fields;
+ if ( eval 'scalar(@FS::'. $table. '::ISA);' ) {
+ @virtual_fields = grep exists($record->{$_}), "FS::$table"->virtual_fields;
+ } else {
+ cluck "warning: FS::$table not loaded; virtual fields not searchable"
+ unless $nowarn_classload;
+ @virtual_fields = ();
+ }
+
+ my $statement = "SELECT $select FROM $stable";
+ $statement .= " $addl_from" if $addl_from;
+ if ( @real_fields or @virtual_fields ) {
+ $statement .= ' WHERE '. join(' AND ',
+ get_real_fields($table, $record, \@real_fields) ,
+ get_virtual_fields($table, $pkey, $record, \@virtual_fields),
+ );
+ }
+
+ $statement .= " $extra_sql" if defined($extra_sql);
+ $statement .= " $order_by" if defined($order_by);
+
+ warn "[debug]$me $statement\n" if $DEBUG > 1 || $debug;
+ my $sth = $dbh->prepare($statement)
+ or croak "$dbh->errstr doing $statement";
+
+ my $bind = 1;
+
+ foreach my $field (
+ grep defined( $record->{$_} ) && $record->{$_} ne '', @real_fields
+ ) {
+
+ my $value = $record->{$field};
+ my $op = (ref($value) && $value->{op}) ? $value->{op} : '=';
+ $value = $value->{'value'} if ref($value);
+ my $type = dbdef->table($table)->column($field)->type;
+
+ my $TYPE = SQL_VARCHAR;
+ if ( $type =~ /(big)?(int|serial)/i && $value =~ /^\d+(\.\d+)?$/ ) {
+ $TYPE = SQL_INTEGER;
+
+ #DBD::Pg 1.49: Cannot bind ... unknown sql_type 6 with SQL_FLOAT
+ #fixed by DBD::Pg 2.11.8
+ #can change back to SQL_FLOAT in early-mid 2010, once everyone's upgraded
+ } elsif ( _is_fs_float( $type, $value ) ) {
+ $TYPE = SQL_DECIMAL;
+ }
+
+ if ( $DEBUG > 2 ) {
+ no strict 'refs';
+ %TYPE = map { &{"DBI::$_"}() => $_ } @{ $DBI::EXPORT_TAGS{sql_types} }
+ unless keys %TYPE;
+ warn " bind_param $bind (for field $field), $value, TYPE $TYPE{$TYPE}\n";
+ }
+
+ #if this needs to be re-enabled, it needs to use a custom op like
+ #"APPROX=" or something (better name?, not '=', to avoid affecting other
+ # searches
+ #if ($TYPE eq SQL_DECIMAL && $op eq 'APPROX=' ) {
+ # # these values are arbitrary; better (faster?) ones welcome
+ # $sth->bind_param($bind++, $value*1.00001, { TYPE => $TYPE } );
+ # $sth->bind_param($bind++, $value*.99999, { TYPE => $TYPE } );
+ #} else {
+ $sth->bind_param($bind++, $value, { TYPE => $TYPE } );
+ #}
+
+ }
+
+# $sth->execute( map $record->{$_},
+# grep defined( $record->{$_} ) && $record->{$_} ne '', @fields
+# ) or croak "Error executing \"$statement\": ". $sth->errstr;
+
+ $sth->execute or croak "Error executing \"$statement\": ". $sth->errstr;
+
+ if ( eval 'scalar(@FS::'. $table. '::ISA);' ) {
+ @virtual_fields = "FS::$table"->virtual_fields;
+ } else {
+ cluck "warning: FS::$table not loaded; virtual fields not returned either"
+ unless $nowarn_classload;
+ @virtual_fields = ();
+ }
+
+ my %result;
+ tie %result, "Tie::IxHash";
+ my @stuff = @{ $sth->fetchall_arrayref( {} ) };
+ if ( $pkey && scalar(@stuff) && $stuff[0]->{$pkey} ) {
+ %result = map { $_->{$pkey}, $_ } @stuff;
+ } else {
+ @result{@stuff} = @stuff;
+ }
+
+ $sth->finish;
+
+ if ( keys(%result) and @virtual_fields ) {
+ $statement =
+ "SELECT virtual_field.recnum, part_virtual_field.name, ".
+ "virtual_field.value ".
+ "FROM part_virtual_field JOIN virtual_field USING (vfieldpart) ".
+ "WHERE part_virtual_field.dbtable = '$table' AND ".
+ "virtual_field.recnum IN (".
+ join(',', keys(%result)). ") AND part_virtual_field.name IN ('".
+ join(q!', '!, @virtual_fields) . "')";
+ warn "[debug]$me $statement\n" if $DEBUG > 1;
+ $sth = $dbh->prepare($statement) or croak "$dbh->errstr doing $statement";
+ $sth->execute or croak "Error executing \"$statement\": ". $sth->errstr;
+
+ foreach (@{ $sth->fetchall_arrayref({}) }) {
+ my $recnum = $_->{recnum};
+ my $name = $_->{name};
+ my $value = $_->{value};
+ if (exists($result{$recnum})) {
+ $result{$recnum}->{$name} = $value;
+ }
+ }
+ }
+ my @return;
+ if ( eval 'scalar(@FS::'. $table. '::ISA);' ) {
+ if ( eval 'FS::'. $table. '->can(\'new\')' eq \&new ) {
+ #derivied class didn't override new method, so this optimization is safe
+ if ( $cache ) {
+ @return = map {
+ new_or_cached( "FS::$table", { %{$_} }, $cache )
+ } values(%result);
+ } else {
+ @return = map {
+ new( "FS::$table", { %{$_} } )
+ } values(%result);
+ }
+ } else {
+ #okay, its been tested
+ # warn "untested code (class FS::$table uses custom new method)";
+ @return = map {
+ eval 'FS::'. $table. '->new( { %{$_} } )';
+ } values(%result);
+ }
+
+ # Check for encrypted fields and decrypt them.
+ ## only in the local copy, not the cached object
+ if ( $conf_encryption
+ && eval 'defined(@FS::'. $table . '::encrypted_fields)' ) {
+ foreach my $record (@return) {
+ foreach my $field (eval '@FS::'. $table . '::encrypted_fields') {
+ # Set it directly... This may cause a problem in the future...
+ $record->setfield($field, $record->decrypt($record->getfield($field)));
+ }
+ }
+ }
+ } else {
+ cluck "warning: FS::$table not loaded; returning FS::Record objects"
+ unless $nowarn_classload;
+ @return = map {
+ FS::Record->new( $table, { %{$_} } );
+ } values(%result);
+ }
+ return @return;
+}
+
+## makes this easier to read
+
+sub get_virtual_fields {
+ my $table = shift;
+ my $pkey = shift;
+ my $record = shift;
+ my $virtual_fields = shift;
+
+ return
+ ( map {
+ my $op = '=';
+ my $column = $_;
+ if ( ref($record->{$_}) ) {
+ $op = $record->{$_}{'op'} if $record->{$_}{'op'};
+ if ( uc($op) eq 'ILIKE' ) {
+ $op = 'LIKE';
+ $record->{$_}{'value'} = lc($record->{$_}{'value'});
+ $column = "LOWER($_)";
+ }
+ $record->{$_} = $record->{$_}{'value'};
+ }
+
+ # ... EXISTS ( SELECT name, value FROM part_virtual_field
+ # JOIN virtual_field
+ # ON part_virtual_field.vfieldpart = virtual_field.vfieldpart
+ # WHERE recnum = svc_acct.svcnum
+ # AND (name, value) = ('egad', 'brain') )
+
+ my $value = $record->{$_};
+
+ my $subq;
+
+ $subq = ($value ? 'EXISTS ' : 'NOT EXISTS ') .
+ "( SELECT part_virtual_field.name, virtual_field.value ".
+ "FROM part_virtual_field JOIN virtual_field ".
+ "ON part_virtual_field.vfieldpart = virtual_field.vfieldpart ".
+ "WHERE virtual_field.recnum = ${table}.${pkey} ".
+ "AND part_virtual_field.name = '${column}'".
+ ($value ?
+ " AND virtual_field.value ${op} '${value}'"
+ : "") . ")";
+ $subq;
+
+ } @{ $virtual_fields } ) ;
+}
+
+sub get_real_fields {
+ my $table = shift;
+ my $record = shift;
+ my $real_fields = shift;
+
+ ## this huge map was previously inline, just broke it out to help read the qsearch method, should be optimized for readability
+ return (
+ map {
+
+ my $op = '=';
+ my $column = $_;
+ my $type = dbdef->table($table)->column($column)->type;
+ my $value = $record->{$column};
+ $value = $value->{'value'} if ref($value);
+ if ( ref($record->{$_}) ) {
+ $op = $record->{$_}{'op'} if $record->{$_}{'op'};
+ #$op = 'LIKE' if $op =~ /^ILIKE$/i && driver_name ne 'Pg';
+ if ( uc($op) eq 'ILIKE' ) {
+ $op = 'LIKE';
+ $record->{$_}{'value'} = lc($record->{$_}{'value'});
+ $column = "LOWER($_)";
+ }
+ $record->{$_} = $record->{$_}{'value'}
+ }
+
+ if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) {
+ if ( $op eq '=' ) {
+ if ( driver_name eq 'Pg' ) {
+ if ( $type =~ /(int|numeric|real|float4|(big)?serial)/i ) {
+ qq-( $column IS NULL )-;
+ } else {
+ qq-( $column IS NULL OR $column = '' )-;
+ }
+ } else {
+ qq-( $column IS NULL OR $column = "" )-;
+ }
+ } elsif ( $op eq '!=' ) {
+ if ( driver_name eq 'Pg' ) {
+ if ( $type =~ /(int|numeric|real|float4|(big)?serial)/i ) {
+ qq-( $column IS NOT NULL )-;
+ } else {
+ qq-( $column IS NOT NULL AND $column != '' )-;
+ }
+ } else {
+ qq-( $column IS NOT NULL AND $column != "" )-;
+ }
+ } else {
+ if ( driver_name eq 'Pg' ) {
+ qq-( $column $op '' )-;
+ } else {
+ qq-( $column $op "" )-;
+ }
+ }
+ #if this needs to be re-enabled, it needs to use a custom op like
+ #"APPROX=" or something (better name?, not '=', to avoid affecting other
+ # searches
+ #} elsif ( $op eq 'APPROX=' && _is_fs_float( $type, $value ) ) {
+ # ( "$column <= ?", "$column >= ?" );
+ } else {
+ "$column $op ?";
+ }
+ } @{ $real_fields } );
+}
+
+=item by_key PRIMARY_KEY_VALUE
+
+This is a class method that returns the record with the given primary key
+value. This method is only useful in FS::Record subclasses. For example:
+
+ my $cust_main = FS::cust_main->by_key(1); # retrieve customer with custnum 1
+
+is equivalent to:
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => 1 } );
+
+=cut
+
+sub by_key {
+ my ($class, $pkey_value) = @_;
+
+ my $table = $class->table
+ or croak "No table for $class found";
+
+ my $dbdef_table = dbdef->table($table)
+ or die "No schema for table $table found - ".
+ "do you need to create it or run dbdef-create?";
+ my $pkey = $dbdef_table->primary_key
+ or die "No primary key for table $table";
+
+ return qsearchs($table, { $pkey => $pkey_value });
+}
+
+=item jsearch TABLE, HASHREF, SELECT, EXTRA_SQL, PRIMARY_TABLE, PRIMARY_KEY
+
+Experimental JOINed search method. Using this method, you can execute a
+single SELECT spanning multiple tables, and cache the results for subsequent
+method calls. Interface will almost definately change in an incompatible
+fashion.
+
+Arguments:
+
+=cut
+
+sub jsearch {
+ my($table, $record, $select, $extra_sql, $ptable, $pkey ) = @_;
+ my $cache = FS::SearchCache->new( $ptable, $pkey );
+ my %saw;
+ ( $cache,
+ grep { !$saw{$_->getfield($pkey)}++ }
+ qsearch($table, $record, $select, $extra_sql, $cache )
+ );
+}
+
+=item qsearchs PARAMS_HASHREF | TABLE, HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ, ADDL_FROM
+
+Same as qsearch, except that if more than one record matches, it B<carp>s but
+returns the first. If this happens, you either made a logic error in asking
+for a single item, or your data is corrupted.
+
+=cut
+
+sub qsearchs { # $result_record = &FS::Record:qsearchs('table',\%hash);
+ my $table = $_[0];
+ my(@result) = qsearch(@_);
+ cluck "warning: Multiple records in scalar search ($table)"
+ if scalar(@result) > 1;
+ #should warn more vehemently if the search was on a primary key?
+ scalar(@result) ? ($result[0]) : ();
+}
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item table
+
+Returns the table name.
+
+=cut
+
+sub table {
+# cluck "warning: FS::Record::table deprecated; supply one in subclass!";
+ my $self = shift;
+ $self -> {'Table'};
+}
+
+=item dbdef_table
+
+Returns the DBIx::DBSchema::Table object for the table.
+
+=cut
+
+sub dbdef_table {
+ my($self)=@_;
+ my($table)=$self->table;
+ dbdef->table($table);
+}
+
+=item primary_key
+
+Returns the primary key for the table.
+
+=cut
+
+sub primary_key {
+ my $self = shift;
+ my $pkey = $self->dbdef_table->primary_key;
+}
+
+=item get, getfield COLUMN
+
+Returns the value of the column/field/key COLUMN.
+
+=cut
+
+sub get {
+ my($self,$field) = @_;
+ # to avoid "Use of unitialized value" errors
+ if ( defined ( $self->{Hash}->{$field} ) ) {
+ $self->{Hash}->{$field};
+ } else {
+ '';
+ }
+}
+sub getfield {
+ my $self = shift;
+ $self->get(@_);
+}
+
+=item set, setfield COLUMN, VALUE
+
+Sets the value of the column/field/key COLUMN to VALUE. Returns VALUE.
+
+=cut
+
+sub set {
+ my($self,$field,$value) = @_;
+ $self->{'modified'} = 1;
+ $self->{'Hash'}->{$field} = $value;
+}
+sub setfield {
+ my $self = shift;
+ $self->set(@_);
+}
+
+=item AUTLOADED METHODS
+
+$record->column is a synonym for $record->get('column');
+
+$record->column('value') is a synonym for $record->set('column','value');
+
+=cut
+
+# readable/safe
+sub AUTOLOAD {
+ my($self,$value)=@_;
+ my($field)=$AUTOLOAD;
+ $field =~ s/.*://;
+ if ( defined($value) ) {
+ confess "errant AUTOLOAD $field for $self (arg $value)"
+ unless blessed($self) && $self->can('setfield');
+ $self->setfield($field,$value);
+ } else {
+ confess "errant AUTOLOAD $field for $self (no args)"
+ unless blessed($self) && $self->can('getfield');
+ $self->getfield($field);
+ }
+}
+
+# efficient
+#sub AUTOLOAD {
+# my $field = $AUTOLOAD;
+# $field =~ s/.*://;
+# if ( defined($_[1]) ) {
+# $_[0]->setfield($field, $_[1]);
+# } else {
+# $_[0]->getfield($field);
+# }
+#}
+
+=item hash
+
+Returns a list of the column/value pairs, usually for assigning to a new hash.
+
+To make a distinct duplicate of an FS::Record object, you can do:
+
+ $new = new FS::Record ( $old->table, { $old->hash } );
+
+=cut
+
+sub hash {
+ my($self) = @_;
+ confess $self. ' -> hash: Hash attribute is undefined'
+ unless defined($self->{'Hash'});
+ %{ $self->{'Hash'} };
+}
+
+=item hashref
+
+Returns a reference to the column/value hash. This may be deprecated in the
+future; if there's a reason you can't just use the autoloaded or get/set
+methods, speak up.
+
+=cut
+
+sub hashref {
+ my($self) = @_;
+ $self->{'Hash'};
+}
+
+=item modified
+
+Returns true if any of this object's values have been modified with set (or via
+an autoloaded method). Doesn't yet recognize when you retreive a hashref and
+modify that.
+
+=cut
+
+sub modified {
+ my $self = shift;
+ $self->{'modified'};
+}
+
+=item select_for_update
+
+Selects this record with the SQL "FOR UPDATE" command. This can be useful as
+a mutex.
+
+=cut
+
+sub select_for_update {
+ my $self = shift;
+ my $primary_key = $self->primary_key;
+ qsearchs( {
+ 'select' => '*',
+ 'table' => $self->table,
+ 'hashref' => { $primary_key => $self->$primary_key() },
+ 'extra_sql' => 'FOR UPDATE',
+ } );
+}
+
+=item lock_table
+
+Locks this table with a database-driver specific lock method. This is used
+as a mutex in order to do a duplicate search.
+
+For PostgreSQL, does "LOCK TABLE tablename IN SHARE ROW EXCLUSIVE MODE".
+
+For MySQL, does a SELECT FOR UPDATE on the duplicate_lock table.
+
+Errors are fatal; no useful return value.
+
+Note: To use this method for new tables other than svc_acct and svc_phone,
+edit freeside-upgrade and add those tables to the duplicate_lock list.
+
+=cut
+
+sub lock_table {
+ my $self = shift;
+ my $table = $self->table;
+
+ warn "$me locking $table table\n" if $DEBUG;
+
+ if ( driver_name =~ /^Pg/i ) {
+
+ dbh->do("LOCK TABLE $table IN SHARE ROW EXCLUSIVE MODE")
+ or die dbh->errstr;
+
+ } elsif ( driver_name =~ /^mysql/i ) {
+
+ dbh->do("SELECT * FROM duplicate_lock
+ WHERE lockname = '$table'
+ FOR UPDATE"
+ ) or die dbh->errstr;
+
+ } else {
+
+ die "unknown database ". driver_name. "; don't know how to lock table";
+
+ }
+
+ warn "$me acquired $table table lock\n" if $DEBUG;
+
+}
+
+=item insert
+
+Inserts this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my $saved = {};
+
+ warn "$self -> insert" if $DEBUG;
+
+ my $error = $self->check;
+ return $error if $error;
+
+ #single-field unique keys are given a value if false
+ #(like MySQL's AUTO_INCREMENT or Pg SERIAL)
+ foreach ( $self->dbdef_table->unique_singles) {
+ $self->unique($_) unless $self->getfield($_);
+ }
+
+ #and also the primary key, if the database isn't going to
+ my $primary_key = $self->dbdef_table->primary_key;
+ my $db_seq = 0;
+ if ( $primary_key ) {
+ my $col = $self->dbdef_table->column($primary_key);
+
+ $db_seq =
+ uc($col->type) =~ /^(BIG)?SERIAL\d?/
+ || ( driver_name eq 'Pg'
+ && defined($col->default)
+ && $col->default =~ /^nextval\(/i
+ )
+ || ( driver_name eq 'mysql'
+ && defined($col->local)
+ && $col->local =~ /AUTO_INCREMENT/i
+ );
+ $self->unique($primary_key) unless $self->getfield($primary_key) || $db_seq;
+ }
+
+ my $table = $self->table;
+
+ # Encrypt before the database
+ if ( defined(eval '@FS::'. $table . '::encrypted_fields')
+ && scalar( eval '@FS::'. $table . '::encrypted_fields')
+ && $conf->exists('encryption')
+ ) {
+ foreach my $field (eval '@FS::'. $table . '::encrypted_fields') {
+ $self->{'saved'} = $self->getfield($field);
+ $self->setfield($field, $self->encrypt($self->getfield($field)));
+ }
+ }
+
+ #false laziness w/delete
+ my @real_fields =
+ grep { defined($self->getfield($_)) && $self->getfield($_) ne "" }
+ real_fields($table)
+ ;
+ my @values = map { _quote( $self->getfield($_), $table, $_) } @real_fields;
+ #eslaf
+
+ my $statement = "INSERT INTO $table ";
+ if ( @real_fields ) {
+ $statement .=
+ "( ".
+ join( ', ', @real_fields ).
+ ") VALUES (".
+ join( ', ', @values ).
+ ")"
+ ;
+ } else {
+ $statement .= 'DEFAULT VALUES';
+ }
+ warn "[debug]$me $statement\n" if $DEBUG > 1;
+ my $sth = dbh->prepare($statement) or return dbh->errstr;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ $sth->execute or return $sth->errstr;
+
+ # get inserted id from the database, if applicable & needed
+ if ( $db_seq && ! $self->getfield($primary_key) ) {
+ warn "[debug]$me retreiving sequence from database\n" if $DEBUG;
+
+ my $insertid = '';
+
+ if ( driver_name eq 'Pg' ) {
+
+ #my $oid = $sth->{'pg_oid_status'};
+ #my $i_sql = "SELECT $primary_key FROM $table WHERE oid = ?";
+
+ my $default = $self->dbdef_table->column($primary_key)->default;
+ unless ( $default =~ /^nextval\(\(?'"?([\w\.]+)"?'/i ) {
+ dbh->rollback if $FS::UID::AutoCommit;
+ return "can't parse $table.$primary_key default value".
+ " for sequence name: $default";
+ }
+ my $sequence = $1;
+
+ my $i_sql = "SELECT currval('$sequence')";
+ my $i_sth = dbh->prepare($i_sql) or do {
+ dbh->rollback if $FS::UID::AutoCommit;
+ return dbh->errstr;
+ };
+ $i_sth->execute() or do { #$i_sth->execute($oid)
+ dbh->rollback if $FS::UID::AutoCommit;
+ return $i_sth->errstr;
+ };
+ $insertid = $i_sth->fetchrow_arrayref->[0];
+
+ } elsif ( driver_name eq 'mysql' ) {
+
+ $insertid = dbh->{'mysql_insertid'};
+ # work around mysql_insertid being null some of the time, ala RT :/
+ unless ( $insertid ) {
+ warn "WARNING: DBD::mysql didn't return mysql_insertid; ".
+ "using SELECT LAST_INSERT_ID();";
+ my $i_sql = "SELECT LAST_INSERT_ID()";
+ my $i_sth = dbh->prepare($i_sql) or do {
+ dbh->rollback if $FS::UID::AutoCommit;
+ return dbh->errstr;
+ };
+ $i_sth->execute or do {
+ dbh->rollback if $FS::UID::AutoCommit;
+ return $i_sth->errstr;
+ };
+ $insertid = $i_sth->fetchrow_arrayref->[0];
+ }
+
+ } else {
+
+ dbh->rollback if $FS::UID::AutoCommit;
+ return "don't know how to retreive inserted ids from ". driver_name.
+ ", try using counterfiles (maybe run dbdef-create?)";
+
+ }
+
+ $self->setfield($primary_key, $insertid);
+
+ }
+
+ my @virtual_fields =
+ grep defined($self->getfield($_)) && $self->getfield($_) ne "",
+ $self->virtual_fields;
+ if (@virtual_fields) {
+ my %v_values = map { $_, $self->getfield($_) } @virtual_fields;
+
+ my $vfieldpart = $self->vfieldpart_hashref;
+
+ my $v_statement = "INSERT INTO virtual_field(recnum, vfieldpart, value) ".
+ "VALUES (?, ?, ?)";
+
+ my $v_sth = dbh->prepare($v_statement) or do {
+ dbh->rollback if $FS::UID::AutoCommit;
+ return dbh->errstr;
+ };
+
+ foreach (keys(%v_values)) {
+ $v_sth->execute($self->getfield($primary_key),
+ $vfieldpart->{$_},
+ $v_values{$_})
+ or do {
+ dbh->rollback if $FS::UID::AutoCommit;
+ return $v_sth->errstr;
+ };
+ }
+ }
+
+
+ my $h_sth;
+ if ( defined dbdef->table('h_'. $table) ) {
+ my $h_statement = $self->_h_statement('insert');
+ warn "[debug]$me $h_statement\n" if $DEBUG > 2;
+ $h_sth = dbh->prepare($h_statement) or do {
+ dbh->rollback if $FS::UID::AutoCommit;
+ return dbh->errstr;
+ };
+ } else {
+ $h_sth = '';
+ }
+ $h_sth->execute or return $h_sth->errstr if $h_sth;
+
+ dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
+
+ # Now that it has been saved, reset the encrypted fields so that $new
+ # can still be used.
+ foreach my $field (keys %{$saved}) {
+ $self->setfield($field, $saved->{$field});
+ }
+
+ '';
+}
+
+=item add
+
+Depriciated (use insert instead).
+
+=cut
+
+sub add {
+ cluck "warning: FS::Record::add deprecated!";
+ insert @_; #call method in this scope
+}
+
+=item delete
+
+Delete this record from the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ my $statement = "DELETE FROM ". $self->table. " WHERE ". join(' AND ',
+ map {
+ $self->getfield($_) eq ''
+ #? "( $_ IS NULL OR $_ = \"\" )"
+ ? ( driver_name eq 'Pg'
+ ? "$_ IS NULL"
+ : "( $_ IS NULL OR $_ = \"\" )"
+ )
+ : "$_ = ". _quote($self->getfield($_),$self->table,$_)
+ } ( $self->dbdef_table->primary_key )
+ ? ( $self->dbdef_table->primary_key)
+ : real_fields($self->table)
+ );
+ warn "[debug]$me $statement\n" if $DEBUG > 1;
+ my $sth = dbh->prepare($statement) or return dbh->errstr;
+
+ my $h_sth;
+ if ( defined dbdef->table('h_'. $self->table) ) {
+ my $h_statement = $self->_h_statement('delete');
+ warn "[debug]$me $h_statement\n" if $DEBUG > 2;
+ $h_sth = dbh->prepare($h_statement) or return dbh->errstr;
+ } else {
+ $h_sth = '';
+ }
+
+ my $primary_key = $self->dbdef_table->primary_key;
+ my $v_sth;
+ my @del_vfields;
+ my $vfp = $self->vfieldpart_hashref;
+ foreach($self->virtual_fields) {
+ next if $self->getfield($_) eq '';
+ unless(@del_vfields) {
+ my $st = "DELETE FROM virtual_field WHERE recnum = ? AND vfieldpart = ?";
+ $v_sth = dbh->prepare($st) or return dbh->errstr;
+ }
+ push @del_vfields, $_;
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $rc = $sth->execute or return $sth->errstr;
+ #not portable #return "Record not found, statement:\n$statement" if $rc eq "0E0";
+ $h_sth->execute or return $h_sth->errstr if $h_sth;
+ $v_sth->execute($self->getfield($primary_key), $vfp->{$_})
+ or return $v_sth->errstr
+ foreach (@del_vfields);
+
+ dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
+
+ #no need to needlessly destoy the data either (causes problems actually)
+ #undef $self; #no need to keep object!
+
+ '';
+}
+
+=item del
+
+Depriciated (use delete instead).
+
+=cut
+
+sub del {
+ cluck "warning: FS::Record::del deprecated!";
+ &delete(@_); #call method in this scope
+}
+
+=item replace OLD_RECORD
+
+Replace the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my ($new, $old) = (shift, shift);
+
+ $old = $new->replace_old unless defined($old);
+
+ warn "[debug]$me $new ->replace $old\n" if $DEBUG;
+
+ if ( $new->can('replace_check') ) {
+ my $error = $new->replace_check($old);
+ return $error if $error;
+ }
+
+ return "Records not in same table!" unless $new->table eq $old->table;
+
+ my $primary_key = $old->dbdef_table->primary_key;
+ return "Can't change primary key $primary_key ".
+ 'from '. $old->getfield($primary_key).
+ ' to ' . $new->getfield($primary_key)
+ if $primary_key
+ && ( $old->getfield($primary_key) ne $new->getfield($primary_key) );
+
+ my $error = $new->check;
+ return $error if $error;
+
+ # Encrypt for replace
+ my $saved = {};
+ if ($conf->exists('encryption') && defined(eval '@FS::'. $new->table . '::encrypted_fields')) {
+ foreach my $field (eval '@FS::'. $new->table . '::encrypted_fields') {
+ $saved->{$field} = $new->getfield($field);
+ $new->setfield($field, $new->encrypt($new->getfield($field)));
+ }
+ }
+
+ #my @diff = grep $new->getfield($_) ne $old->getfield($_), $old->fields;
+ my %diff = map { ($new->getfield($_) ne $old->getfield($_))
+ ? ($_, $new->getfield($_)) : () } $old->fields;
+
+ unless (keys(%diff) || $no_update_diff ) {
+ carp "[warning]$me $new -> replace $old: records identical"
+ unless $nowarn_identical;
+ return '';
+ }
+
+ my $statement = "UPDATE ". $old->table. " SET ". join(', ',
+ map {
+ "$_ = ". _quote($new->getfield($_),$old->table,$_)
+ } real_fields($old->table)
+ ). ' WHERE '.
+ join(' AND ',
+ map {
+
+ if ( $old->getfield($_) eq '' ) {
+
+ #false laziness w/qsearch
+ if ( driver_name eq 'Pg' ) {
+ my $type = $old->dbdef_table->column($_)->type;
+ if ( $type =~ /(int|(big)?serial)/i ) {
+ qq-( $_ IS NULL )-;
+ } else {
+ qq-( $_ IS NULL OR $_ = '' )-;
+ }
+ } else {
+ qq-( $_ IS NULL OR $_ = "" )-;
+ }
+
+ } else {
+ "$_ = ". _quote($old->getfield($_),$old->table,$_);
+ }
+
+ } ( $primary_key ? ( $primary_key ) : real_fields($old->table) )
+ )
+ ;
+ warn "[debug]$me $statement\n" if $DEBUG > 1;
+ my $sth = dbh->prepare($statement) or return dbh->errstr;
+
+ my $h_old_sth;
+ if ( defined dbdef->table('h_'. $old->table) ) {
+ my $h_old_statement = $old->_h_statement('replace_old');
+ warn "[debug]$me $h_old_statement\n" if $DEBUG > 2;
+ $h_old_sth = dbh->prepare($h_old_statement) or return dbh->errstr;
+ } else {
+ $h_old_sth = '';
+ }
+
+ my $h_new_sth;
+ if ( defined dbdef->table('h_'. $new->table) ) {
+ my $h_new_statement = $new->_h_statement('replace_new');
+ warn "[debug]$me $h_new_statement\n" if $DEBUG > 2;
+ $h_new_sth = dbh->prepare($h_new_statement) or return dbh->errstr;
+ } else {
+ $h_new_sth = '';
+ }
+
+ # For virtual fields we have three cases with different SQL
+ # statements: add, replace, delete
+ my $v_add_sth;
+ my $v_rep_sth;
+ my $v_del_sth;
+ my (@add_vfields, @rep_vfields, @del_vfields);
+ my $vfp = $old->vfieldpart_hashref;
+ foreach(grep { exists($diff{$_}) } $new->virtual_fields) {
+ if($diff{$_} eq '') {
+ # Delete
+ unless(@del_vfields) {
+ my $st = "DELETE FROM virtual_field WHERE recnum = ? ".
+ "AND vfieldpart = ?";
+ warn "[debug]$me $st\n" if $DEBUG > 2;
+ $v_del_sth = dbh->prepare($st) or return dbh->errstr;
+ }
+ push @del_vfields, $_;
+ } elsif($old->getfield($_) eq '') {
+ # Add
+ unless(@add_vfields) {
+ my $st = "INSERT INTO virtual_field (value, recnum, vfieldpart) ".
+ "VALUES (?, ?, ?)";
+ warn "[debug]$me $st\n" if $DEBUG > 2;
+ $v_add_sth = dbh->prepare($st) or return dbh->errstr;
+ }
+ push @add_vfields, $_;
+ } else {
+ # Replace
+ unless(@rep_vfields) {
+ my $st = "UPDATE virtual_field SET value = ? ".
+ "WHERE recnum = ? AND vfieldpart = ?";
+ warn "[debug]$me $st\n" if $DEBUG > 2;
+ $v_rep_sth = dbh->prepare($st) or return dbh->errstr;
+ }
+ push @rep_vfields, $_;
+ }
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $rc = $sth->execute or return $sth->errstr;
+ #not portable #return "Record not found (or records identical)." if $rc eq "0E0";
+ $h_old_sth->execute or return $h_old_sth->errstr if $h_old_sth;
+ $h_new_sth->execute or return $h_new_sth->errstr if $h_new_sth;
+
+ $v_del_sth->execute($old->getfield($primary_key),
+ $vfp->{$_})
+ or return $v_del_sth->errstr
+ foreach(@del_vfields);
+
+ $v_add_sth->execute($new->getfield($_),
+ $old->getfield($primary_key),
+ $vfp->{$_})
+ or return $v_add_sth->errstr
+ foreach(@add_vfields);
+
+ $v_rep_sth->execute($new->getfield($_),
+ $old->getfield($primary_key),
+ $vfp->{$_})
+ or return $v_rep_sth->errstr
+ foreach(@rep_vfields);
+
+ dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
+
+ # Now that it has been saved, reset the encrypted fields so that $new
+ # can still be used.
+ foreach my $field (keys %{$saved}) {
+ $new->setfield($field, $saved->{$field});
+ }
+
+ '';
+
+}
+
+sub replace_old {
+ my( $self ) = shift;
+ warn "[$me] replace called with no arguments; autoloading old record\n"
+ if $DEBUG;
+
+ my $primary_key = $self->dbdef_table->primary_key;
+ if ( $primary_key ) {
+ $self->by_key( $self->$primary_key() ) #this is what's returned
+ or croak "can't find ". $self->table. ".$primary_key ".
+ $self->$primary_key();
+ } else {
+ croak $self->table. " has no primary key; pass old record as argument";
+ }
+
+}
+
+=item rep
+
+Depriciated (use replace instead).
+
+=cut
+
+sub rep {
+ cluck "warning: FS::Record::rep deprecated!";
+ replace @_; #call method in this scope
+}
+
+=item check
+
+Checks virtual fields (using check_blocks). Subclasses should still provide
+a check method to validate real fields, foreign keys, etc., and call this
+method via $self->SUPER::check.
+
+(FIXME: Should this method try to make sure that it I<is> being called from
+a subclass's check method, to keep the current semantics as far as possible?)
+
+=cut
+
+sub check {
+ #confess "FS::Record::check not implemented; supply one in subclass!";
+ my $self = shift;
+
+ foreach my $field ($self->virtual_fields) {
+ for ($self->getfield($field)) {
+ # See notes on check_block in FS::part_virtual_field.
+ eval $self->pvf($field)->check_block;
+ if ( $@ ) {
+ #this is bad, probably want to follow the stack backtrace up and see
+ #wtf happened
+ my $err = "Fatal error checking $field for $self";
+ cluck "$err: $@";
+ return "$err (see log for backtrace): $@";
+
+ }
+ $self->setfield($field, $_);
+ }
+ }
+ '';
+}
+
+=item process_batch_import JOB OPTIONS_HASHREF PARAMS
+
+Processes a batch import as a queued JSRPC job
+
+JOB is an FS::queue entry.
+
+OPTIONS_HASHREF can have the following keys:
+
+=over 4
+
+=item table
+
+Table name (required).
+
+=item params
+
+Listref of field names for static fields. They will be given values from the
+PARAMS hashref and passed as a "params" hashref to batch_import.
+
+=item formats
+
+Formats hashref. Keys are field names, values are listrefs that define the
+format.
+
+Each listref value can be a column name or a code reference. Coderefs are run
+with the row object, data and a FS::Conf object as the three parameters.
+For example, this coderef does the same thing as using the "columnname" string:
+
+ sub {
+ my( $record, $data, $conf ) = @_;
+ $record->columnname( $data );
+ },
+
+Coderefs are run after all "column name" fields are assigned.
+
+=item format_types
+
+Optional format hashref of types. Keys are field names, values are "csv",
+"xls" or "fixedlength". Overrides automatic determination of file type
+from extension.
+
+=item format_headers
+
+Optional format hashref of header lines. Keys are field names, values are 0
+for no header, 1 to ignore the first line, or to higher numbers to ignore that
+number of lines.
+
+=item format_sep_chars
+
+Optional format hashref of CSV sep_chars. Keys are field names, values are the
+CSV separation character.
+
+=item format_fixedlenth_formats
+
+Optional format hashref of fixed length format defintiions. Keys are field
+names, values Parse::FixedLength listrefs of field definitions.
+
+=item default_csv
+
+Set true to default to CSV file type if the filename does not contain a
+recognizable ".csv" or ".xls" extension (and type is not pre-specified by
+format_types).
+
+=back
+
+PARAMS is a base64-encoded Storable string containing the POSTed data as
+a hash ref. It normally contains at least one field, "uploaded files",
+generated by /elements/file-upload.html and containing the list of uploaded
+files. Currently only supports a single file named "file".
+
+=cut
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process_batch_import {
+ my($job, $opt) = ( shift, shift );
+
+ my $table = $opt->{table};
+ my @pass_params = @{ $opt->{params} };
+ my %formats = %{ $opt->{formats} };
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ my $files = $param->{'uploaded_files'}
+ or die "No files provided.\n";
+
+ my (%files) = map { /^(\w+):([\.\w]+)$/ ? ($1,$2):() } split /,/, $files;
+
+ my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc. '/';
+ my $file = $dir. $files{'file'};
+
+ my $error =
+ FS::Record::batch_import( {
+ #class-static
+ table => $table,
+ formats => \%formats,
+ format_types => $opt->{format_types},
+ format_headers => $opt->{format_headers},
+ format_sep_chars => $opt->{format_sep_chars},
+ format_fixedlength_formats => $opt->{format_fixedlength_formats},
+ #per-import
+ job => $job,
+ file => $file,
+ #type => $type,
+ format => $param->{format},
+ params => { map { $_ => $param->{$_} } @pass_params },
+ #?
+ default_csv => $opt->{default_csv},
+ } );
+
+ unlink $file;
+
+ die "$error\n" if $error;
+}
+
+=item batch_import PARAM_HASHREF
+
+Class method for batch imports. Available params:
+
+=over 4
+
+=item table
+
+=item formats
+
+=item format_types
+
+=item format_headers
+
+=item format_sep_chars
+
+=item format_fixedlength_formats
+
+=item params
+
+=item job
+
+FS::queue object, will be updated with progress
+
+=item file
+
+=item type
+
+csv, xls or fixedlength
+
+=item format
+
+=item empty_ok
+
+=back
+
+=cut
+
+sub batch_import {
+ my $param = shift;
+
+ warn "$me batch_import call with params: \n". Dumper($param)
+ if $DEBUG;
+
+ my $table = $param->{table};
+ my $formats = $param->{formats};
+
+ my $job = $param->{job};
+ my $file = $param->{file};
+ my $format = $param->{'format'};
+ my $params = $param->{params} || {};
+
+ die "unknown format $format" unless exists $formats->{ $format };
+
+ my $type = $param->{'format_types'}
+ ? $param->{'format_types'}{ $format }
+ : $param->{type} || 'csv';
+
+ unless ( $type ) {
+ if ( $file =~ /\.(\w+)$/i ) {
+ $type = lc($1);
+ } else {
+ #or error out???
+ warn "can't parse file type from filename $file; defaulting to CSV";
+ $type = 'csv';
+ }
+ $type = 'csv'
+ if $param->{'default_csv'} && $type ne 'xls';
+ }
+
+ my $header = $param->{'format_headers'}
+ ? $param->{'format_headers'}{ $param->{'format'} }
+ : 0;
+
+ my $sep_char = $param->{'format_sep_chars'}
+ ? $param->{'format_sep_chars'}{ $param->{'format'} }
+ : ',';
+
+ my $fixedlength_format =
+ $param->{'format_fixedlength_formats'}
+ ? $param->{'format_fixedlength_formats'}{ $param->{'format'} }
+ : '';
+
+ my @fields = @{ $formats->{ $format } };
+
+ my $row = 0;
+ my $count;
+ my $parser;
+ my @buffer = ();
+ if ( $type eq 'csv' || $type eq 'fixedlength' ) {
+
+ if ( $type eq 'csv' ) {
+
+ my %attr = ();
+ $attr{sep_char} = $sep_char if $sep_char;
+ $parser = new Text::CSV_XS \%attr;
+
+ } elsif ( $type eq 'fixedlength' ) {
+
+ eval "use Parse::FixedLength;";
+ die $@ if $@;
+ $parser = new Parse::FixedLength $fixedlength_format;
+
+ } else {
+ die "Unknown file type $type\n";
+ }
+
+ @buffer = split(/\r?\n/, slurp($file) );
+ splice(@buffer, 0, ($header || 0) );
+ $count = scalar(@buffer);
+
+ } elsif ( $type eq 'xls' ) {
+
+ eval "use Spreadsheet::ParseExcel;";
+ die $@ if $@;
+
+ eval "use DateTime::Format::Excel;";
+ #for now, just let the error be thrown if it is used, since only CDR
+ # formats bill_west and troop use it, not other excel-parsing things
+ #die $@ if $@;
+
+ my $excel = Spreadsheet::ParseExcel::Workbook->new->Parse($file);
+
+ $parser = $excel->{Worksheet}[0]; #first sheet
+
+ $count = $parser->{MaxRow} || $parser->{MinRow};
+ $count++;
+
+ $row = $header || 0;
+
+ } else {
+ die "Unknown file type $type\n";
+ }
+
+ #my $columns;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $line;
+ my $imported = 0;
+ my( $last, $min_sec ) = ( time, 5 ); #progressbar foo
+ while (1) {
+
+ my @columns = ();
+ if ( $type eq 'csv' ) {
+
+ last unless scalar(@buffer);
+ $line = shift(@buffer);
+
+ $parser->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $parser->error_input();
+ };
+ @columns = $parser->fields();
+
+ } elsif ( $type eq 'fixedlength' ) {
+
+ @columns = $parser->parse($line);
+
+ } elsif ( $type eq 'xls' ) {
+
+ last if $row > ($parser->{MaxRow} || $parser->{MinRow})
+ || ! $parser->{Cells}[$row];
+
+ my @row = @{ $parser->{Cells}[$row] };
+ @columns = map $_->{Val}, @row;
+
+ #my $z = 'A';
+ #warn $z++. ": $_\n" for @columns;
+
+ } else {
+ die "Unknown file type $type\n";
+ }
+
+ my @later = ();
+ my %hash = %$params;
+
+ foreach my $field ( @fields ) {
+
+ my $value = shift @columns;
+
+ if ( ref($field) eq 'CODE' ) {
+ #&{$field}(\%hash, $value);
+ push @later, $field, $value;
+ } else {
+ #??? $hash{$field} = $value if length($value);
+ $hash{$field} = $value if defined($value) && length($value);
+ }
+
+ }
+
+ my $class = "FS::$table";
+
+ my $record = $class->new( \%hash );
+
+ while ( scalar(@later) ) {
+ my $sub = shift @later;
+ my $data = shift @later;
+ &{$sub}($record, $data, $conf); # $record->&{$sub}($data, $conf);
+ }
+
+ my $error = $record->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert record". ( $line ? " for $line" : '' ). ": $error";
+ }
+
+ $row++;
+ $imported++;
+
+ if ( $job && time - $min_sec > $last ) { #progress bar
+ $job->update_statustext( int(100 * $imported / $count) );
+ $last = time;
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;;
+
+ return "Empty file!" unless $imported || $param->{empty_ok};
+
+ ''; #no error
+
+}
+
+sub _h_statement {
+ my( $self, $action, $time ) = @_;
+
+ $time ||= time;
+
+ my @fields =
+ grep { defined($self->getfield($_)) && $self->getfield($_) ne "" }
+ real_fields($self->table);
+ ;
+
+ # If we're encrypting then don't ever store the payinfo or CVV2 in the history....
+ # You can see if it changed by the paymask...
+ if ($conf && $conf->exists('encryption') ) {
+ @fields = grep $_ ne 'payinfo' && $_ ne 'cvv2', @fields;
+ }
+ my @values = map { _quote( $self->getfield($_), $self->table, $_) } @fields;
+
+ "INSERT INTO h_". $self->table. " ( ".
+ join(', ', qw(history_date history_user history_action), @fields ).
+ ") VALUES (".
+ join(', ', $time, dbh->quote(getotaker()), dbh->quote($action), @values).
+ ")"
+ ;
+}
+
+=item unique COLUMN
+
+B<Warning>: External use is B<deprecated>.
+
+Replaces COLUMN in record with a unique number, using counters in the
+filesystem. Used by the B<insert> method on single-field unique columns
+(see L<DBIx::DBSchema::Table>) and also as a fallback for primary keys
+that aren't SERIAL (Pg) or AUTO_INCREMENT (mysql).
+
+Returns the new value.
+
+=cut
+
+sub unique {
+ my($self,$field) = @_;
+ my($table)=$self->table;
+
+ croak "Unique called on field $field, but it is ",
+ $self->getfield($field),
+ ", not null!"
+ if $self->getfield($field);
+
+ #warn "table $table is tainted" if is_tainted($table);
+ #warn "field $field is tainted" if is_tainted($field);
+
+ my($counter) = new File::CounterFile "$table.$field",0;
+# hack for web demo
+# getotaker() =~ /^([\w\-]{1,16})$/ or die "Illegal CGI REMOTE_USER!";
+# my($user)=$1;
+# my($counter) = new File::CounterFile "$user/$table.$field",0;
+# endhack
+
+ my $index = $counter->inc;
+ $index = $counter->inc while qsearchs($table, { $field=>$index } );
+
+ $index =~ /^(\d*)$/;
+ $index=$1;
+
+ $self->setfield($field,$index);
+
+}
+
+=item ut_float COLUMN
+
+Check/untaint floating point numeric data: 1.1, 1, 1.1e10, 1e10. May not be
+null. If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_float {
+ my($self,$field)=@_ ;
+ ($self->getfield($field) =~ /^\s*(\d+\.\d+)\s*$/ ||
+ $self->getfield($field) =~ /^\s*(\d+)\s*$/ ||
+ $self->getfield($field) =~ /^\s*(\d+\.\d+e\d+)\s*$/ ||
+ $self->getfield($field) =~ /^\s*(\d+e\d+)\s*$/)
+ or return "Illegal or empty (float) $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+=item ut_floatn COLUMN
+
+Check/untaint floating point numeric data: 1.1, 1, 1.1e10, 1e10. May be
+null. If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+#false laziness w/ut_ipn
+sub ut_floatn {
+ my( $self, $field ) = @_;
+ if ( $self->getfield($field) =~ /^()$/ ) {
+ $self->setfield($field,'');
+ '';
+ } else {
+ $self->ut_float($field);
+ }
+}
+
+=item ut_sfloat COLUMN
+
+Check/untaint signed floating point numeric data: 1.1, 1, 1.1e10, 1e10.
+May not be null. If there is an error, returns the error, otherwise returns
+false.
+
+=cut
+
+sub ut_sfloat {
+ my($self,$field)=@_ ;
+ ($self->getfield($field) =~ /^\s*(-?\d+\.\d+)\s*$/ ||
+ $self->getfield($field) =~ /^\s*(-?\d+)\s*$/ ||
+ $self->getfield($field) =~ /^\s*(-?\d+\.\d+[eE]-?\d+)\s*$/ ||
+ $self->getfield($field) =~ /^\s*(-?\d+[eE]-?\d+)\s*$/)
+ or return "Illegal or empty (float) $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+=item ut_sfloatn COLUMN
+
+Check/untaint signed floating point numeric data: 1.1, 1, 1.1e10, 1e10. May be
+null. If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_sfloatn {
+ my( $self, $field ) = @_;
+ if ( $self->getfield($field) =~ /^()$/ ) {
+ $self->setfield($field,'');
+ '';
+ } else {
+ $self->ut_sfloat($field);
+ }
+}
+
+=item ut_snumber COLUMN
+
+Check/untaint signed numeric data (whole numbers). If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub ut_snumber {
+ my($self, $field) = @_;
+ $self->getfield($field) =~ /^\s*(-?)\s*(\d+)\s*$/
+ or return "Illegal or empty (numeric) $field: ". $self->getfield($field);
+ $self->setfield($field, "$1$2");
+ '';
+}
+
+=item ut_snumbern COLUMN
+
+Check/untaint signed numeric data (whole numbers). If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub ut_snumbern {
+ my($self, $field) = @_;
+ $self->getfield($field) =~ /^\s*(-?)\s*(\d*)\s*$/
+ or return "Illegal (numeric) $field: ". $self->getfield($field);
+ if ($1) {
+ return "Illegal (numeric) $field: ". $self->getfield($field)
+ unless $2;
+ }
+ $self->setfield($field, "$1$2");
+ '';
+}
+
+=item ut_number COLUMN
+
+Check/untaint simple numeric data (whole numbers). May not be null. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_number {
+ my($self,$field)=@_;
+ $self->getfield($field) =~ /^\s*(\d+)\s*$/
+ or return "Illegal or empty (numeric) $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_numbern COLUMN
+
+Check/untaint simple numeric data (whole numbers). May be null. If there is
+an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_numbern {
+ my($self,$field)=@_;
+ $self->getfield($field) =~ /^\s*(\d*)\s*$/
+ or return "Illegal (numeric) $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_money COLUMN
+
+Check/untaint monetary numbers. May be negative. Set to 0 if null. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_money {
+ my($self,$field)=@_;
+ $self->setfield($field, 0) if $self->getfield($field) eq '';
+ $self->getfield($field) =~ /^\s*(\-)?\s*(\d*)(\.\d{2})?\s*$/
+ or return "Illegal (money) $field: ". $self->getfield($field);
+ #$self->setfield($field, "$1$2$3" || 0);
+ $self->setfield($field, ( ($1||''). ($2||''). ($3||'') ) || 0);
+ '';
+}
+
+=item ut_text COLUMN
+
+Check/untaint text. Alphanumerics, spaces, and the following punctuation
+symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? / = [ ]
+May not be null. If there is an error, returns the error, otherwise returns
+false.
+
+=cut
+
+sub ut_text {
+ my($self,$field)=@_;
+ #warn "msgcat ". \&msgcat. "\n";
+ #warn "notexist ". \&notexist. "\n";
+ #warn "AUTOLOAD ". \&AUTOLOAD. "\n";
+ $self->getfield($field)
+ =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]+)$/
+ or return gettext('illegal_or_empty_text'). " $field: ".
+ $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_textn COLUMN
+
+Check/untaint text. Alphanumerics, spaces, and the following punctuation
+symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? /
+May be null. If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_textn {
+ my($self,$field)=@_;
+ $self->getfield($field)
+ =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/
+ or return gettext('illegal_text'). " $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_alpha COLUMN
+
+Check/untaint alphanumeric strings (no spaces). May not be null. If there is
+an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_alpha {
+ my($self,$field)=@_;
+ $self->getfield($field) =~ /^(\w+)$/
+ or return "Illegal or empty (alphanumeric) $field: ".
+ $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_alpha COLUMN
+
+Check/untaint alphanumeric strings (no spaces). May be null. If there is an
+error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_alphan {
+ my($self,$field)=@_;
+ $self->getfield($field) =~ /^(\w*)$/
+ or return "Illegal (alphanumeric) $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_alpha_lower COLUMN
+
+Check/untaint lowercase alphanumeric strings (no spaces). May not be null. If
+there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_alpha_lower {
+ my($self,$field)=@_;
+ $self->getfield($field) =~ /[[:upper:]]/
+ and return "Uppercase characters are not permitted in $field";
+ $self->ut_alpha($field);
+}
+
+=item ut_phonen COLUMN [ COUNTRY ]
+
+Check/untaint phone numbers. May be null. If there is an error, returns
+the error, otherwise returns false.
+
+Takes an optional two-letter ISO country code; without it or with unsupported
+countries, ut_phonen simply calls ut_alphan.
+
+=cut
+
+sub ut_phonen {
+ my( $self, $field, $country ) = @_;
+ return $self->ut_alphan($field) unless defined $country;
+ my $phonen = $self->getfield($field);
+ if ( $phonen eq '' ) {
+ $self->setfield($field,'');
+ } elsif ( $country eq 'US' || $country eq 'CA' ) {
+ $phonen =~ s/\D//g;
+ $phonen = $conf->config('cust_main-default_areacode').$phonen
+ if length($phonen)==7 && $conf->config('cust_main-default_areacode');
+ $phonen =~ /^(\d{3})(\d{3})(\d{4})(\d*)$/
+ or return gettext('illegal_phone'). " $field: ". $self->getfield($field);
+ $phonen = "$1-$2-$3";
+ $phonen .= " x$4" if $4;
+ $self->setfield($field,$phonen);
+ } else {
+ warn "warning: don't know how to check phone numbers for country $country";
+ return $self->ut_textn($field);
+ }
+ '';
+}
+
+=item ut_hex COLUMN
+
+Check/untaint hexadecimal values.
+
+=cut
+
+sub ut_hex {
+ my($self, $field) = @_;
+ $self->getfield($field) =~ /^([\da-fA-F]+)$/
+ or return "Illegal (hex) $field: ". $self->getfield($field);
+ $self->setfield($field, uc($1));
+ '';
+}
+
+=item ut_hexn COLUMN
+
+Check/untaint hexadecimal values. May be null.
+
+=cut
+
+sub ut_hexn {
+ my($self, $field) = @_;
+ $self->getfield($field) =~ /^([\da-fA-F]*)$/
+ or return "Illegal (hex) $field: ". $self->getfield($field);
+ $self->setfield($field, uc($1));
+ '';
+}
+=item ut_ip COLUMN
+
+Check/untaint ip addresses. IPv4 only for now.
+
+=cut
+
+sub ut_ip {
+ my( $self, $field ) = @_;
+ $self->getfield($field) =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
+ or return "Illegal (IP address) $field: ". $self->getfield($field);
+ for ( $1, $2, $3, $4 ) { return "Illegal (IP address) $field" if $_ > 255; }
+ $self->setfield($field, "$1.$2.$3.$4");
+ '';
+}
+
+=item ut_ipn COLUMN
+
+Check/untaint ip addresses. IPv4 only for now. May be null.
+
+=cut
+
+sub ut_ipn {
+ my( $self, $field ) = @_;
+ if ( $self->getfield($field) =~ /^()$/ ) {
+ $self->setfield($field,'');
+ '';
+ } else {
+ $self->ut_ip($field);
+ }
+}
+
+=item ut_coord COLUMN [ LOWER [ UPPER ] ]
+
+Check/untaint coordinates.
+Accepts the following forms:
+DDD.DDDDD
+-DDD.DDDDD
+DDD MM.MMM
+-DDD MM.MMM
+DDD MM SS
+-DDD MM SS
+DDD MM MMM
+-DDD MM MMM
+
+The "DDD MM SS" and "DDD MM MMM" are potentially ambiguous.
+The latter form (that is, the MMM are thousands of minutes) is
+assumed if the "MMM" is exactly three digits or two digits > 59.
+
+To be safe, just use the DDD.DDDDD form.
+
+If LOWER or UPPER are specified, then the coordinate is checked
+for lower and upper bounds, respectively.
+
+=cut
+
+sub ut_coord {
+
+ my ($self, $field) = (shift, shift);
+
+ my $lower = shift if scalar(@_);
+ my $upper = shift if scalar(@_);
+ my $coord = $self->getfield($field);
+ my $neg = $coord =~ s/^(-)//;
+
+ my ($d, $m, $s) = (0, 0, 0);
+
+ if (
+ (($d) = ($coord =~ /^(\s*\d{1,3}(?:\.\d+)?)\s*$/)) ||
+ (($d, $m) = ($coord =~ /^(\s*\d{1,3})\s+(\d{1,2}(?:\.\d+))\s*$/)) ||
+ (($d, $m, $s) = ($coord =~ /^(\s*\d{1,3})\s+(\d{1,2})\s+(\d{1,3})\s*$/))
+ ) {
+ $s = (((($s =~ /^\d{3}$/) or $s > 59) ? ($s / 1000) : ($s / 60)) / 60);
+ $m = $m / 60;
+ if ($m > 59) {
+ return "Invalid (coordinate with minutes > 59) $field: "
+ . $self->getfield($field);
+ }
+
+ $coord = ($neg ? -1 : 1) * sprintf('%.8f', $d + $m + $s);
+
+ if (defined($lower) and ($coord < $lower)) {
+ return "Invalid (coordinate < $lower) $field: "
+ . $self->getfield($field);;
+ }
+
+ if (defined($upper) and ($coord > $upper)) {
+ return "Invalid (coordinate > $upper) $field: "
+ . $self->getfield($field);;
+ }
+
+ $self->setfield($field, $coord);
+ return '';
+ }
+
+ return "Invalid (coordinate) $field: " . $self->getfield($field);
+
+}
+
+=item ut_coordn COLUMN [ LOWER [ UPPER ] ]
+
+Same as ut_coord, except optionally null.
+
+=cut
+
+sub ut_coordn {
+
+ my ($self, $field) = (shift, shift);
+
+ if ($self->getfield($field) =~ /^$/) {
+ return '';
+ } else {
+ return $self->ut_coord($field, @_);
+ }
+
+}
+
+
+=item ut_domain COLUMN
+
+Check/untaint host and domain names.
+
+=cut
+
+sub ut_domain {
+ my( $self, $field ) = @_;
+ #$self->getfield($field) =~/^(\w+\.)*\w+$/
+ $self->getfield($field) =~/^(([\w\-]+\.)*\w+)$/
+ or return "Illegal (domain) $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_name COLUMN
+
+Check/untaint proper names; allows alphanumerics, spaces and the following
+punctuation: , . - '
+
+May not be null.
+
+=cut
+
+sub ut_name {
+ my( $self, $field ) = @_;
+ $self->getfield($field) =~ /^([\w \,\.\-\']+)$/
+ or return gettext('illegal_name'). " $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_zip COLUMN
+
+Check/untaint zip codes.
+
+=cut
+
+my @zip_reqd_countries = qw( AU CA US ); #CA, US implicit...
+
+sub ut_zip {
+ my( $self, $field, $country ) = @_;
+
+ if ( $country eq 'US' ) {
+
+ $self->getfield($field) =~ /^\s*(\d{5}(\-\d{4})?)\s*$/
+ or return gettext('illegal_zip'). " $field for country $country: ".
+ $self->getfield($field);
+ $self->setfield($field, $1);
+
+ } elsif ( $country eq 'CA' ) {
+
+ $self->getfield($field) =~ /^\s*([A-Z]\d[A-Z])\s*(\d[A-Z]\d)\s*$/i
+ or return gettext('illegal_zip'). " $field for country $country: ".
+ $self->getfield($field);
+ $self->setfield($field, "$1 $2");
+
+ } else {
+
+ if ( $self->getfield($field) =~ /^\s*$/
+ && ( !$country || ! grep { $_ eq $country } @zip_reqd_countries )
+ )
+ {
+ $self->setfield($field,'');
+ } else {
+ $self->getfield($field) =~ /^\s*(\w[\w\-\s]{2,8}\w)\s*$/
+ or return gettext('illegal_zip'). " $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ }
+
+ }
+
+ '';
+}
+
+=item ut_country COLUMN
+
+Check/untaint country codes. Country names are changed to codes, if possible -
+see L<Locale::Country>.
+
+=cut
+
+sub ut_country {
+ my( $self, $field ) = @_;
+ unless ( $self->getfield($field) =~ /^(\w\w)$/ ) {
+ if ( $self->getfield($field) =~ /^([\w \,\.\(\)\']+)$/
+ && country2code($1) ) {
+ $self->setfield($field,uc(country2code($1)));
+ }
+ }
+ $self->getfield($field) =~ /^(\w\w)$/
+ or return "Illegal (country) $field: ". $self->getfield($field);
+ $self->setfield($field,uc($1));
+ '';
+}
+
+=item ut_anything COLUMN
+
+Untaints arbitrary data. Be careful.
+
+=cut
+
+sub ut_anything {
+ my( $self, $field ) = @_;
+ $self->getfield($field) =~ /^(.*)$/s
+ or return "Illegal $field: ". $self->getfield($field);
+ $self->setfield($field,$1);
+ '';
+}
+
+=item ut_enum COLUMN CHOICES_ARRAYREF
+
+Check/untaint a column, supplying all possible choices, like the "enum" type.
+
+=cut
+
+sub ut_enum {
+ my( $self, $field, $choices ) = @_;
+ foreach my $choice ( @$choices ) {
+ if ( $self->getfield($field) eq $choice ) {
+ $self->setfield($choice);
+ return '';
+ }
+ }
+ return "Illegal (enum) field $field: ". $self->getfield($field);
+}
+
+=item ut_foreign_key COLUMN FOREIGN_TABLE FOREIGN_COLUMN
+
+Check/untaint a foreign column key. Call a regular ut_ method (like ut_number)
+on the column first.
+
+=cut
+
+sub ut_foreign_key {
+ my( $self, $field, $table, $foreign ) = @_;
+ return '' if $no_check_foreign;
+ qsearchs($table, { $foreign => $self->getfield($field) })
+ or return "Can't find ". $self->table. ".$field ". $self->getfield($field).
+ " in $table.$foreign";
+ '';
+}
+
+=item ut_foreign_keyn COLUMN FOREIGN_TABLE FOREIGN_COLUMN
+
+Like ut_foreign_key, except the null value is also allowed.
+
+=cut
+
+sub ut_foreign_keyn {
+ my( $self, $field, $table, $foreign ) = @_;
+ $self->getfield($field)
+ ? $self->ut_foreign_key($field, $table, $foreign)
+ : '';
+}
+
+=item ut_agentnum_acl COLUMN [ NULL_RIGHT | NULL_RIGHT_LISTREF ]
+
+Checks this column as an agentnum, taking into account the current users's
+ACLs. NULL_RIGHT or NULL_RIGHT_LISTREF, if specified, indicates the access
+right or rights allowing no agentnum.
+
+=cut
+
+sub ut_agentnum_acl {
+ my( $self, $field ) = (shift, shift);
+ my $null_acl = scalar(@_) ? shift : [];
+ $null_acl = [ $null_acl ] unless ref($null_acl);
+
+ my $error = $self->ut_foreign_keyn($field, 'agent', 'agentnum');
+ return "Illegal agentnum: $error" if $error;
+
+ my $curuser = $FS::CurrentUser::CurrentUser;
+
+ if ( $self->$field() ) {
+
+ return "Access denied"
+ unless $curuser->agentnum($self->$field());
+
+ } else {
+
+ return "Access denied"
+ unless grep $curuser->access_right($_), @$null_acl;
+
+ }
+
+ '';
+
+}
+
+=item virtual_fields [ TABLE ]
+
+Returns a list of virtual fields defined for the table. This should not
+be exported, and should only be called as an instance or class method.
+
+=cut
+
+sub virtual_fields {
+ my $self = shift;
+ my $table;
+ $table = $self->table or confess "virtual_fields called on non-table";
+
+ confess "Unknown table $table" unless dbdef->table($table);
+
+ return () unless dbdef->table('part_virtual_field');
+
+ unless ( $virtual_fields_cache{$table} ) {
+ my $query = 'SELECT name from part_virtual_field ' .
+ "WHERE dbtable = '$table'";
+ my $dbh = dbh;
+ my $result = $dbh->selectcol_arrayref($query);
+ confess "Error executing virtual fields query: $query: ". $dbh->errstr
+ if $dbh->err;
+ $virtual_fields_cache{$table} = $result;
+ }
+
+ @{$virtual_fields_cache{$table}};
+
+}
+
+
+=item fields [ TABLE ]
+
+This is a wrapper for real_fields and virtual_fields. Code that called
+fields before should probably continue to call fields.
+
+=cut
+
+sub fields {
+ my $something = shift;
+ my $table;
+ if($something->isa('FS::Record')) {
+ $table = $something->table;
+ } else {
+ $table = $something;
+ $something = "FS::$table";
+ }
+ return (real_fields($table), $something->virtual_fields());
+}
+
+=item pvf FIELD_NAME
+
+Returns the FS::part_virtual_field object corresponding to a field in the
+record (specified by FIELD_NAME).
+
+=cut
+
+sub pvf {
+ my ($self, $name) = (shift, shift);
+
+ if(grep /^$name$/, $self->virtual_fields) {
+ return qsearchs('part_virtual_field', { dbtable => $self->table,
+ name => $name } );
+ }
+ ''
+}
+
+=item vfieldpart_hashref TABLE
+
+Returns a hashref of virtual field names and vfieldparts applicable to the given
+TABLE.
+
+=cut
+
+sub vfieldpart_hashref {
+ my $self = shift;
+ my $table = $self->table;
+
+ return {} unless dbdef->table('part_virtual_field');
+
+ my $dbh = dbh;
+ my $statement = "SELECT vfieldpart, name FROM part_virtual_field WHERE ".
+ "dbtable = '$table'";
+ my $sth = $dbh->prepare($statement);
+ $sth->execute or croak "Execution of '$statement' failed: ".$dbh->errstr;
+ return { map { $_->{name}, $_->{vfieldpart} }
+ @{$sth->fetchall_arrayref({})} };
+
+}
+
+=item encrypt($value)
+
+Encrypts the credit card using a combination of PK to encrypt and uuencode to armour.
+
+Returns the encrypted string.
+
+You should generally not have to worry about calling this, as the system handles this for you.
+
+=cut
+
+sub encrypt {
+ my ($self, $value) = @_;
+ my $encrypted;
+
+ if ($conf->exists('encryption')) {
+ if ($self->is_encrypted($value)) {
+ # Return the original value if it isn't plaintext.
+ $encrypted = $value;
+ } else {
+ $self->loadRSA;
+ if (ref($rsa_encrypt) =~ /::RSA/) { # We Can Encrypt
+ # RSA doesn't like the empty string so let's pack it up
+ # The database doesn't like the RSA data so uuencode it
+ my $length = length($value)+1;
+ $encrypted = pack("u*",$rsa_encrypt->encrypt(pack("Z$length",$value)));
+ } else {
+ die ("You can't encrypt w/o a valid RSA engine - Check your installation or disable encryption");
+ }
+ }
+ }
+ return $encrypted;
+}
+
+=item is_encrypted($value)
+
+Checks to see if the string is encrypted and returns true or false (1/0) to indicate it's status.
+
+=cut
+
+
+sub is_encrypted {
+ my ($self, $value) = @_;
+ # Possible Bug - Some work may be required here....
+
+ if ($value =~ /^M/ && length($value) > 80) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+=item decrypt($value)
+
+Uses the private key to decrypt the string. Returns the decryoted string or undef on failure.
+
+You should generally not have to worry about calling this, as the system handles this for you.
+
+=cut
+
+sub decrypt {
+ my ($self,$value) = @_;
+ my $decrypted = $value; # Will return the original value if it isn't encrypted or can't be decrypted.
+ if ($conf->exists('encryption') && $self->is_encrypted($value)) {
+ $self->loadRSA;
+ if (ref($rsa_decrypt) =~ /::RSA/) {
+ my $encrypted = unpack ("u*", $value);
+ $decrypted = unpack("Z*", eval{$rsa_decrypt->decrypt($encrypted)});
+ if ($@) {warn "Decryption Failed"};
+ }
+ }
+ return $decrypted;
+}
+
+sub loadRSA {
+ my $self = shift;
+ #Initialize the Module
+ $rsa_module = 'Crypt::OpenSSL::RSA'; # The Default
+
+ if ($conf->exists('encryptionmodule') && $conf->config_binary('encryptionmodule') ne '') {
+ $rsa_module = $conf->config('encryptionmodule');
+ }
+
+ if (!$rsa_loaded) {
+ eval ("require $rsa_module"); # No need to import the namespace
+ $rsa_loaded++;
+ }
+ # Initialize Encryption
+ if ($conf->exists('encryptionpublickey') && $conf->config_binary('encryptionpublickey') ne '') {
+ my $public_key = join("\n",$conf->config('encryptionpublickey'));
+ $rsa_encrypt = $rsa_module->new_public_key($public_key);
+ }
+
+ # Intitalize Decryption
+ if ($conf->exists('encryptionprivatekey') && $conf->config_binary('encryptionprivatekey') ne '') {
+ my $private_key = join("\n",$conf->config('encryptionprivatekey'));
+ $rsa_decrypt = $rsa_module->new_private_key($private_key);
+ }
+}
+
+=item h_search ACTION
+
+Given an ACTION, either "insert", or "delete", returns the appropriate history
+record corresponding to this record, if any.
+
+=cut
+
+sub h_search {
+ my( $self, $action ) = @_;
+
+ my $table = $self->table;
+ $table =~ s/^h_//;
+
+ my $primary_key = dbdef->table($table)->primary_key;
+
+ qsearchs({
+ 'table' => "h_$table",
+ 'hashref' => { $primary_key => $self->$primary_key(),
+ 'history_action' => $action,
+ },
+ });
+
+}
+
+=item h_date ACTION
+
+Given an ACTION, either "insert", or "delete", returns the timestamp of the
+appropriate history record corresponding to this record, if any.
+
+=cut
+
+sub h_date {
+ my($self, $action) = @_;
+ my $h = $self->h_search($action);
+ $h ? $h->history_date : '';
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item real_fields [ TABLE ]
+
+Returns a list of the real columns in the specified table. Called only by
+fields() and other subroutines elsewhere in FS::Record.
+
+=cut
+
+sub real_fields {
+ my $table = shift;
+
+ my($table_obj) = dbdef->table($table);
+ confess "Unknown table $table" unless $table_obj;
+ $table_obj->columns;
+}
+
+=item _quote VALUE, TABLE, COLUMN
+
+This is an internal function used to construct SQL statements. It returns
+VALUE DBI-quoted (see L<DBI/"quote">) unless VALUE is a number and the column
+type (see L<DBIx::DBSchema::Column>) does not end in `char' or `binary'.
+
+=cut
+
+sub _quote {
+ my($value, $table, $column) = @_;
+ my $column_obj = dbdef->table($table)->column($column);
+ my $column_type = $column_obj->type;
+ my $nullable = $column_obj->null;
+
+ warn " $table.$column: $value ($column_type".
+ ( $nullable ? ' NULL' : ' NOT NULL' ).
+ ")\n" if $DEBUG > 2;
+
+ if ( $value eq '' && $nullable ) {
+ 'NULL'
+ } elsif ( $value eq '' && $column_type =~ /^(int|numeric)/ ) {
+ cluck "WARNING: Attempting to set non-null integer $table.$column null; ".
+ "using 0 instead";
+ 0;
+ } elsif ( $value =~ /^\d+(\.\d+)?$/ &&
+ ! $column_type =~ /(char|binary|text)$/i ) {
+ $value;
+ } else {
+ dbh->quote($value);
+ }
+}
+
+=item hfields TABLE
+
+This is deprecated. Don't use it.
+
+It returns a hash-type list with the fields of this record's table set true.
+
+=cut
+
+sub hfields {
+ carp "warning: hfields is deprecated";
+ my($table)=@_;
+ my(%hash);
+ foreach (fields($table)) {
+ $hash{$_}=1;
+ }
+ \%hash;
+}
+
+sub _dump {
+ my($self)=@_;
+ join("\n", map {
+ "$_: ". $self->getfield($_). "|"
+ } (fields($self->table)) );
+}
+
+sub DESTROY { return; }
+
+#sub DESTROY {
+# my $self = shift;
+# #use Carp qw(cluck);
+# #cluck "DESTROYING $self";
+# warn "DESTROYING $self";
+#}
+
+#sub is_tainted {
+# return ! eval { join('',@_), kill 0; 1; };
+# }
+
+=item str2time_sql [ DRIVER_NAME ]
+
+Returns a function to convert to unix time based on database type, such as
+"EXTRACT( EPOCH FROM" for Pg or "UNIX_TIMESTAMP(" for mysql. See
+the str2time_sql_closing method to return a closing string rather than just
+using a closing parenthesis as previously suggested.
+
+You can pass an optional driver name such as "Pg", "mysql" or
+$dbh->{Driver}->{Name} to return a function for that database instead of
+the current database.
+
+=cut
+
+sub str2time_sql {
+ my $driver = shift || driver_name;
+
+ return 'UNIX_TIMESTAMP(' if $driver =~ /^mysql/i;
+ return 'EXTRACT( EPOCH FROM ' if $driver =~ /^Pg/i;
+
+ warn "warning: unknown database type $driver; guessing how to convert ".
+ "dates to UNIX timestamps";
+ return 'EXTRACT(EPOCH FROM ';
+
+}
+
+=item str2time_sql_closing [ DRIVER_NAME ]
+
+Returns the closing suffix of a function to convert to unix time based on
+database type, such as ")::integer" for Pg or ")" for mysql.
+
+You can pass an optional driver name such as "Pg", "mysql" or
+$dbh->{Driver}->{Name} to return a function for that database instead of
+the current database.
+
+=cut
+
+sub str2time_sql_closing {
+ my $driver = shift || driver_name;
+
+ return ' )::INTEGER ' if $driver =~ /^Pg/i;
+ return ' ) ';
+}
+
+=back
+
+=head1 BUGS
+
+This module should probably be renamed, since much of the functionality is
+of general use. It is not completely unlike Adapter::DBI (see below).
+
+Exported qsearch and qsearchs should be deprecated in favor of method calls
+(against an FS::Record object like the old search and searchs that qsearch
+and qsearchs were on top of.)
+
+The whole fields / hfields mess should be removed.
+
+The various WHERE clauses should be subroutined.
+
+table string should be deprecated in favor of DBIx::DBSchema::Table.
+
+No doubt we could benefit from a Tied hash. Documenting how exists / defined
+true maps to the database (and WHERE clauses) would also help.
+
+The ut_ methods should ask the dbdef for a default length.
+
+ut_sqltype (like ut_varchar) should all be defined
+
+A fallback check method should be provided which uses the dbdef.
+
+The ut_money method assumes money has two decimal digits.
+
+The Pg money kludge in the new method only strips `$'.
+
+The ut_phonen method only checks US-style phone numbers.
+
+The _quote function should probably use ut_float instead of a regex.
+
+All the subroutines probably should be methods, here or elsewhere.
+
+Probably should borrow/use some dbdef methods where appropriate (like sub
+fields)
+
+As of 1.14, DBI fetchall_hashref( {} ) doesn't set fetchrow_hashref NAME_lc,
+or allow it to be set. Working around it is ugly any way around - DBI should
+be fixed. (only affects RDBMS which return uppercase column names)
+
+ut_zip should take an optional country like ut_phone.
+
+=head1 SEE ALSO
+
+L<DBIx::DBSchema>, L<FS::UID>, L<DBI>
+
+Adapter::DBI from Ch. 11 of Advanced Perl Programming by Sriram Srinivasan.
+
+http://poop.sf.net/
+
+=cut
+
+1;
+
diff --git a/FS/FS/Report.pm b/FS/FS/Report.pm
new file mode 100644
index 0000000..181fea2
--- /dev/null
+++ b/FS/FS/Report.pm
@@ -0,0 +1,46 @@
+package FS::Report;
+
+use strict;
+
+=head1 NAME
+
+FS::Report - Report data objects
+
+=head1 SYNOPSIS
+
+ #see the more speicific report objects, currently only FS::Report::Table
+
+=head1 DESCRIPTION
+
+See the more specific report objects, currently only FS::Report::Table
+
+=head1 METHODS
+
+=over 4
+
+=item new [ OPTION => VALUE ... ]
+
+Constructor. Takes a list of options and their values.
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = @_ ? ( ref($_[0]) ? shift : { @_ } ) : {};
+ bless( $self, $class );
+}
+
+=back
+
+=head1 BUGS
+
+Documentation.
+
+=head1 SEE ALSO
+
+L<FS::Report::Table>, reports in the web interface.
+
+=cut
+
+1;
diff --git a/FS/FS/Report/Table.pm b/FS/FS/Report/Table.pm
new file mode 100644
index 0000000..9f636fa
--- /dev/null
+++ b/FS/FS/Report/Table.pm
@@ -0,0 +1,27 @@
+package FS::Report::Table;
+
+use strict;
+use vars qw( @ISA );
+use FS::Report;
+
+@ISA = qw( FS::Report );
+
+=head1 NAME
+
+FS::Report::Table - Tables of report data
+
+=head1 SYNOPSIS
+
+See the more specific report objects, currently only FS::Report::Table::Monthly
+
+=head1 BUGS
+
+Documentation.
+
+=head1 SEE ALSO
+
+L<FS::Report::Table::Monthly>, reports in the web interface.
+
+=cut
+
+1;
diff --git a/FS/FS/Report/Table/Monthly.pm b/FS/FS/Report/Table/Monthly.pm
new file mode 100644
index 0000000..d75f0be
--- /dev/null
+++ b/FS/FS/Report/Table/Monthly.pm
@@ -0,0 +1,401 @@
+package FS::Report::Table::Monthly;
+
+use strict;
+use vars qw( @ISA );
+use Time::Local;
+use FS::UID qw( dbh );
+use FS::Report::Table;
+use FS::CurrentUser;
+
+@ISA = qw( FS::Report::Table );
+
+=head1 NAME
+
+FS::Report::Table::Monthly - Tables of report data, indexed monthly
+
+=head1 SYNOPSIS
+
+ use FS::Report::Table::Monthly;
+
+ my $report = new FS::Report::Table::Monthly (
+ 'items' => [ 'invoiced', 'netsales', 'credits', 'receipts', ],
+ 'start_month' => 4,
+ 'start_year' => 2000,
+ 'end_month' => 4,
+ 'end_year' => 2020,
+ #opt
+ 'agentnum' => 54
+ 'params' => [ [ 'paramsfor', 'item_one' ], [ 'item', 'two' ] ], # ...
+ 'remove_empty' => 1, #collapse empty rows, default 0
+ 'item_labels' => [ ], #useful with remove_empty
+ );
+
+ my $data = $report->data;
+
+=head1 METHODS
+
+=over 4
+
+=item data
+
+Returns a hashref of data (!! describe)
+
+=cut
+
+sub data {
+ my $self = shift;
+
+ #use Data::Dumper;
+ #warn Dumper($self);
+
+ my $smonth = $self->{'start_month'};
+ my $syear = $self->{'start_year'};
+ my $emonth = $self->{'end_month'};
+ my $eyear = $self->{'end_year'};
+ my $agentnum = $self->{'agentnum'};
+
+ my %data;
+
+ while ( $syear < $eyear || ( $syear == $eyear && $smonth < $emonth+1 ) ) {
+
+ push @{$data{label}}, "$smonth/$syear";
+
+ my $speriod = timelocal(0,0,0,1,$smonth-1,$syear);
+ push @{$data{speriod}}, $speriod;
+ if ( ++$smonth == 13 ) { $syear++; $smonth=1; }
+ my $eperiod = timelocal(0,0,0,1,$smonth-1,$syear);
+ push @{$data{eperiod}}, $eperiod;
+
+ my $col = 0;
+ my @row = ();
+ foreach my $item ( @{$self->{'items'}} ) {
+ my @param = $self->{'params'} ? @{ $self->{'params'}[$col] }: ();
+ my $value = $self->$item($speriod, $eperiod, $agentnum, @param);
+ #push @{$data{$item}}, $value;
+ push @{$data{data}->[$col++]}, $value;
+ }
+
+ }
+
+ #these need to get generalized, sheesh
+ $data{'items'} = $self->{'items'};
+ $data{'item_labels'} = $self->{'item_labels'} || $self->{'items'};
+ $data{'colors'} = $self->{'colors'};
+ $data{'links'} = $self->{'links'} || [];
+
+ #use Data::Dumper;
+ #warn Dumper(\%data);
+
+ if ( $self->{'remove_empty'} ) {
+
+ #warn "removing empty rows\n";
+
+ my $col = 0;
+ #these need to get generalized, sheesh
+ my @newitems = ();
+ my @newlabels = ();
+ my @newdata = ();
+ my @newcolors = ();
+ my @newlinks = ();
+ foreach my $item ( @{$self->{'items'}} ) {
+
+ if ( grep { $_ != 0 } @{$data{'data'}->[$col]} ) {
+ push @newitems, $data{'items'}->[$col];
+ push @newlabels, $data{'item_labels'}->[$col];
+ push @newdata, $data{'data'}->[$col];
+ push @newcolors, $data{'colors'}->[$col];
+ push @newlinks, $data{'links'}->[$col];
+ }
+
+ $col++;
+ }
+
+ $data{'items'} = \@newitems;
+ $data{'item_labels'} = \@newlabels;
+ $data{'data'} = \@newdata;
+ $data{'colors'} = \@newcolors;
+ $data{'links'} = \@newlinks;
+
+ }
+
+ #use Data::Dumper;
+ #warn Dumper(\%data);
+
+ \%data;
+
+}
+
+sub invoiced { #invoiced
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+
+ $self->scalar_sql("
+ SELECT SUM(charged)
+ FROM cust_bill
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum)
+ );
+
+}
+
+sub netsales { #net sales
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+
+ $self->invoiced($speriod,$eperiod,$agentnum)
+ - $self->credits( $speriod,$eperiod,$agentnum);
+}
+
+#deferred revenue
+
+sub cashflow {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+
+ $self->payments($speriod, $eperiod, $agentnum)
+ - $self->refunds( $speriod, $eperiod, $agentnum);
+}
+
+sub netcashflow {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+
+ $self->receipts($speriod, $eperiod, $agentnum)
+ - $self->netrefunds( $speriod, $eperiod, $agentnum);
+}
+
+sub payments {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $self->scalar_sql("
+ SELECT SUM(paid)
+ FROM cust_pay
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum)
+ );
+}
+
+sub credits {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $self->scalar_sql("
+ SELECT SUM(amount)
+ FROM cust_credit
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum)
+ );
+}
+
+sub refunds {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $self->scalar_sql("
+ SELECT SUM(refund)
+ FROM cust_refund
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum)
+ );
+}
+
+sub netcredits {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $self->scalar_sql("
+ SELECT SUM(cust_credit_bill.amount)
+ FROM cust_credit_bill
+ LEFT JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ 'cust_bill._date'
+ )
+ );
+}
+
+sub receipts { #net payments
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $self->scalar_sql("
+ SELECT SUM(cust_bill_pay.amount)
+ FROM cust_bill_pay
+ LEFT JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ 'cust_bill._date'
+ )
+ );
+}
+
+sub netrefunds {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $self->scalar_sql("
+ SELECT SUM(cust_credit_refund.amount)
+ FROM cust_credit_refund
+ LEFT JOIN cust_credit USING ( crednum )
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ 'cust_credit._date'
+ )
+ );
+}
+
+#these should be auto-generated or $AUTOLOADed or something
+sub invoiced_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->invoiced($speriod, $eperiod, $agentnum);
+}
+
+sub netsales_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->netsales($speriod, $eperiod, $agentnum);
+}
+
+sub receipts_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->receipts($speriod, $eperiod, $agentnum);
+}
+
+sub payments_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->payments($speriod, $eperiod, $agentnum);
+}
+
+sub credits_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->credits($speriod, $eperiod, $agentnum);
+}
+
+sub netcredits_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->netcredits($speriod, $eperiod, $agentnum);
+}
+
+sub cashflow_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->cashflow($speriod, $eperiod, $agentnum);
+}
+
+sub netcashflow_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->cashflow($speriod, $eperiod, $agentnum);
+}
+
+sub refunds_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->refunds($speriod, $eperiod, $agentnum);
+}
+
+sub netrefunds_12mo {
+ my( $self, $speriod, $eperiod, $agentnum ) = @_;
+ $speriod = $self->_subtract_11mo($speriod);
+ $self->netrefunds($speriod, $eperiod, $agentnum);
+}
+
+
+#not being too bad with the false laziness
+use Time::Local qw(timelocal);
+sub _subtract_11mo {
+ my($self, $time) = @_;
+ my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($time) )[0,1,2,3,4,5];
+ $mon -= 11;
+ if ( $mon < 0 ) { $mon+=12; $year--; }
+ timelocal($sec,$min,$hour,$mday,$mon,$year);
+}
+
+sub cust_bill_pkg {
+ my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_;
+
+ my $where = '';
+ if ( $opt{'classnum'} =~ /^(\d+)$/ ) {
+ if ( $1 == 0 ) {
+ $where = "classnum IS NULL";
+ } else {
+ $where = "classnum = $1";
+ }
+ }
+
+ $agentnum ||= $opt{'agentnum'};
+
+ $self->scalar_sql("
+ SELECT SUM(cust_bill_pkg.setup + cust_bill_pkg.recur)
+ FROM cust_bill_pkg
+ LEFT JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum )
+ LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart )
+ WHERE pkgnum != 0
+ AND $where
+ AND ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum)
+ );
+
+}
+
+sub setup_pkg { shift->pkg_field( @_, 'setup' ); }
+sub susp_pkg { shift->pkg_field( @_, 'susp' ); }
+sub cancel_pkg { shift->pkg_field( @_, 'cancel'); }
+
+sub pkg_field {
+ my( $self, $speriod, $eperiod, $agentnum, $field ) = @_;
+ $self->scalar_sql("
+ SELECT COUNT(*) FROM cust_pkg
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ "cust_pkg.$field",
+ )
+ );
+
+}
+
+#this is going to be harder..
+#sub unsusp_pkg {
+# my( $self, $speriod, $eperiod, $agentnum ) = @_;
+# $self->scalar_sql("
+# SELECT COUNT(*) FROM h_cust_pkg
+# WHERE
+#
+#}
+
+sub in_time_period_and_agent {
+ my( $self, $speriod, $eperiod, $agentnum ) = splice(@_, 0, 4);
+ my $col = @_ ? shift() : '_date';
+
+ my $sql = "$col >= $speriod AND $col < $eperiod";
+
+ #agent selection
+ $sql .= " AND cust_main.agentnum = $agentnum"
+ if $agentnum;
+
+ #agent virtualization
+ $sql .= ' AND '.
+ $FS::CurrentUser::CurrentUser->agentnums_sql( 'table'=>'cust_main' );
+
+ $sql;
+}
+
+sub scalar_sql {
+ my( $self, $sql ) = ( shift, shift );
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute
+ or die "Unexpected error executing statement $sql: ". $sth->errstr;
+ $sth->fetchrow_arrayref->[0] || 0;
+}
+
+=back
+
+=head1 BUGS
+
+Documentation.
+
+=head1 SEE ALSO
+
+=cut
+
+1;
+
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
new file mode 100644
index 0000000..885eaaa
--- /dev/null
+++ b/FS/FS/Schema.pm
@@ -0,0 +1,2287 @@
+package FS::Schema;
+
+use vars qw(@ISA @EXPORT_OK $DEBUG $setup_hack %dbdef_cache);
+use subs qw(reload_dbdef);
+use Exporter;
+use DBIx::DBSchema 0.33;
+use DBIx::DBSchema::Table;
+use DBIx::DBSchema::Column 0.06;
+use DBIx::DBSchema::Index;
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw( dbdef dbdef_dist reload_dbdef );
+
+$DEBUG = 0;
+$me = '[FS::Schema]';
+
+=head1 NAME
+
+FS::Schema - Freeside database schema
+
+=head1 SYNOPSYS
+
+ use FS::Schema qw(dbdef dbdef_dist reload_dbdef);
+
+ $dbdef = reload_dbdef;
+ $dbdef = reload_dbdef "/non/standard/filename";
+ $dbdef = dbdef;
+ $dbdef_dist = dbdef_dist;
+
+=head1 DESCRIPTION
+
+This class represents the database schema.
+
+=head1 METHODS
+
+=over 4
+
+=item reload_dbdef([FILENAME])
+
+Load a database definition (see L<DBIx::DBSchema>), optionally from a
+non-default filename. This command is executed at startup unless
+I<$FS::Schema::setup_hack> is true. Returns a DBIx::DBSchema object.
+
+=cut
+
+sub reload_dbdef {
+ my $file = shift;
+
+ unless ( exists $dbdef_cache{$file} ) {
+ warn "[debug]$me loading dbdef for $file\n" if $DEBUG;
+ $dbdef_cache{$file} = DBIx::DBSchema->load( $file )
+ or die "can't load database schema from $file: $DBIx::DBSchema::errstr\n";
+ } else {
+ warn "[debug]$me re-using cached dbdef for $file\n" if $DEBUG;
+ }
+ $dbdef = $dbdef_cache{$file};
+}
+
+=item dbdef
+
+Returns the current database definition (represents the current database,
+assuming it is up-to-date). See L<DBIx::DBSchema>.
+
+=cut
+
+sub dbdef { $dbdef; }
+
+=item dbdef_dist [ DATASRC ]
+
+Returns the current canoical database definition as defined in this file.
+
+Optionally, pass a DBI data source to enable syntax specific to that database.
+Currently, this enables "TYPE=InnoDB" for MySQL databases.
+
+=cut
+
+sub dbdef_dist {
+ my $datasrc = @_ ? shift : '';
+
+ my $local_options = '';
+ if ( $datasrc =~ /^dbi:mysql/i ) {
+ $local_options = 'TYPE=InnoDB';
+ }
+
+ ###
+ # create a dbdef object from the old data structure
+ ###
+
+ my $tables_hashref = tables_hashref();
+
+ #turn it into objects
+ my $dbdef = new DBIx::DBSchema map {
+
+ my $tablename = $_;
+ my $indexnum = 1;
+
+ my @columns;
+ while (@{$tables_hashref->{$tablename}{'columns'}}) {
+ #my($name, $type, $null, $length, $default, $local) =
+ my @coldef =
+ splice @{$tables_hashref->{$tablename}{'columns'}}, 0, 6;
+ my %hash = map { $_ => shift @coldef }
+ qw( name type null length default local );
+
+ unless ( defined $hash{'default'} ) {
+ warn "$tablename:\n".
+ join('', map "$_ => $hash{$_}\n", keys %hash) ;# $stop = <STDIN>;
+ }
+
+ push @columns, new DBIx::DBSchema::Column ( \%hash );
+ }
+
+ #false laziness w/sub indices in DBIx::DBSchema::DBD (well, sorta)
+ #and sub sql_create_table in DBIx::DBSchema::Table (slighty more?)
+ my $unique = $tables_hashref->{$tablename}{'unique'};
+ my $index = $tables_hashref->{$tablename}{'index'};
+ my @indices = ();
+ push @indices, map {
+ DBIx::DBSchema::Index->new({
+ 'name' => $tablename. $indexnum++,
+ 'unique' => 1,
+ 'columns' => $_,
+ });
+ }
+ @$unique;
+ push @indices, map {
+ DBIx::DBSchema::Index->new({
+ 'name' => $tablename. $indexnum++,
+ 'unique' => 0,
+ 'columns' => $_,
+ });
+ }
+ @$index;
+
+ DBIx::DBSchema::Table->new({
+ 'name' => $tablename,
+ 'primary_key' => $tables_hashref->{$tablename}{'primary_key'},
+ 'columns' => \@columns,
+ 'indices' => \@indices,
+ 'local_options' => $local_options,
+ });
+
+ } keys %$tables_hashref;
+
+ if ( $DEBUG ) {
+ warn "[debug]$me initial dbdef_dist created ($dbdef) with tables:\n";
+ warn "[debug]$me $_\n" foreach $dbdef->tables;
+ }
+
+ #add radius attributes to svc_acct
+ #
+ #my($svc_acct)=$dbdef->table('svc_acct');
+ #
+ #my($attribute);
+ #foreach $attribute (@attributes) {
+ # $svc_acct->addcolumn ( new DBIx::DBSchema::Column (
+ # 'radius_'. $attribute,
+ # 'varchar',
+ # 'NULL',
+ # $char_d,
+ # ));
+ #}
+ #
+ #foreach $attribute (@check_attributes) {
+ # $svc_acct->addcolumn( new DBIx::DBSchema::Column (
+ # 'rc_'. $attribute,
+ # 'varchar',
+ # 'NULL',
+ # $char_d,
+ # ));
+ #}
+
+ #create history tables (false laziness w/create-history-tables)
+ foreach my $table (
+ grep { ! /^clientapi_session/ }
+ grep { ! /^h_/ }
+ $dbdef->tables
+ ) {
+ my $tableobj = $dbdef->table($table)
+ or die "unknown table $table";
+
+ my %indices = $tableobj->indices;
+
+ my %h_indices = map {
+ ( "h_$_" =>
+ DBIx::DBSchema::Index->new({
+ 'name' => 'h_'. $indices{$_}->name,
+ 'unique' => 0,
+ 'columns' => [ @{$indices{$_}->columns} ],
+ })
+ );
+ }
+ keys %indices;
+
+ $h_indices{"h_${table}_srckey"} = DBIx::DBSchema::Index->new({
+ 'name' => "h_${table}_srckey",
+ 'unique' => 0,
+ 'columns' => [ 'history_action', #right?
+ $tableobj->primary_key,
+ ],
+ });
+
+ $h_indices{"h_${table}_srckey2"} = DBIx::DBSchema::Index->new({
+ 'name' => "h_${table}_srckey2",
+ 'unique' => 0,
+ 'columns' => [ 'history_date',
+ $tableobj->primary_key,
+ ],
+ });
+
+ my $h_tableobj = DBIx::DBSchema::Table->new( {
+ 'name' => "h_$table",
+ 'primary_key' => 'historynum',
+ 'indices' => \%h_indices,
+ 'local_options' => $local_options,
+ 'columns' => [
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'historynum',
+ 'type' => 'serial',
+ 'null' => 'NOT NULL',
+ 'length' => '',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'history_date',
+ 'type' => 'int',
+ 'null' => 'NULL',
+ 'length' => '',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'history_user',
+ 'type' => 'varchar',
+ 'null' => 'NOT NULL',
+ 'length' => '80',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'history_action',
+ 'type' => 'varchar',
+ 'null' => 'NOT NULL',
+ 'length' => '80',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ map {
+ my $column = $tableobj->column($_);
+
+ #clone so as to not disturb the original
+ $column = DBIx::DBSchema::Column->new( {
+ map { $_ => $column->$_() }
+ qw( name type null length default local )
+ } );
+
+ if ( $column->type =~ /^(\w*)SERIAL$/i ) {
+ $column->type('int');
+ $column->null('NULL');
+ }
+ #$column->default('')
+ # if $column->default =~ /^nextval\(/i;
+ #( my $local = $column->local ) =~ s/AUTO_INCREMENT//i;
+ #$column->local($local);
+ $column;
+ } $tableobj->columns
+ ],
+ } );
+ $dbdef->addtable($h_tableobj);
+ }
+
+ if ( $datasrc =~ /^dbi:mysql/i ) {
+
+ my $dup_lock_table = DBIx::DBSchema::Table->new( {
+ 'name' => 'duplicate_lock',
+ 'primary_key' => 'duplocknum',
+ 'local_options' => $local_options,
+ 'columns' => [
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'duplocknum',
+ 'type' => 'serial',
+ 'null' => 'NOT NULL',
+ 'length' => '',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'lockname',
+ 'type' => 'varchar',
+ 'null' => 'NOT NULL',
+ 'length' => '80',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ ],
+ 'indices' => { 'duplicate_lock1' =>
+ DBIx::DBSchema::Index->new({
+ 'name' => 'duplicate_lock1',
+ 'unique' => 1,
+ 'columns' => [ 'lockname' ],
+ })
+ },
+ } );
+
+ $dbdef->addtable($dup_lock_table);
+
+ }
+
+ $dbdef;
+
+}
+
+sub tables_hashref {
+
+ my $char_d = 80; #default maxlength for text fields
+
+ #my(@date_type) = ( 'timestamp', '', '' );
+ my @date_type = ( 'int', 'NULL', '' );
+ my @perl_type = ( 'text', 'NULL', '' );
+ my @money_type = ( 'decimal', '', '10,2' );
+ my @money_typen = ( 'decimal', 'NULL', '10,2' );
+
+ my $username_len = 32; #usernamemax config file
+
+ # name type nullability length default local
+
+ return {
+
+ 'agent' => {
+ 'columns' => [
+ 'agentnum', 'serial', '', '', '', '',
+ 'agent', 'varchar', '', $char_d, '', '',
+ 'typenum', 'int', '', '', '', '',
+ 'ticketing_queueid', 'int', 'NULL', '', '', '',
+ 'invoice_template', 'varchar', 'NULL', $char_d, '', '',
+ 'agent_custnum', 'int', 'NULL', '', '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ 'username', 'varchar', 'NULL', $char_d, '', '', #deprecated
+ '_password', 'varchar', 'NULL', $char_d, '', '', #deprecated
+ 'freq', 'int', 'NULL', '', '', '', #deprecated (never used)
+ 'prog', @perl_type, '', '', #deprecated (never used)
+ ],
+ 'primary_key' => 'agentnum',
+ #'unique' => [ [ 'agent_custnum' ] ], #one agent per customer?
+ #insert is giving it a value, tho..
+ #'index' => [ ['typenum'], ['disabled'] ],
+ 'unique' => [],
+ 'index' => [ ['typenum'], ['disabled'], ['agent_custnum'] ],
+ },
+
+ 'agent_type' => {
+ 'columns' => [
+ 'typenum', 'serial', '', '', '', '',
+ 'atype', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'typenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'type_pkgs' => {
+ 'columns' => [
+ 'typepkgnum', 'serial', '', '', '', '',
+ 'typenum', 'int', '', '', '', '',
+ 'pkgpart', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'typepkgnum',
+ 'unique' => [ ['typenum', 'pkgpart'] ],
+ 'index' => [ ['typenum'] ],
+ },
+
+ 'cust_bill' => {
+ 'columns' => [
+ 'invnum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'charged', @money_type, '', '',
+ 'printed', 'int', '', '', '', '',
+ 'closed', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'invnum',
+ 'unique' => [],
+ 'index' => [ ['custnum'], ['_date'] ],
+ },
+
+ 'cust_bill_event' => {
+ 'columns' => [
+ 'eventnum', 'serial', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ 'eventpart', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'status', 'varchar', '', $char_d, '', '',
+ 'statustext', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'eventnum',
+ #no... there are retries now #'unique' => [ [ 'eventpart', 'invnum' ] ],
+ 'unique' => [],
+ 'index' => [ ['invnum'], ['status'], ['eventpart'] ],
+ },
+
+ 'part_bill_event' => {
+ 'columns' => [
+ 'eventpart', 'serial', '', '', '', '',
+ 'freq', 'varchar', 'NULL', $char_d, '', '',
+ 'payby', 'char', '', 4, '', '',
+ 'event', 'varchar', '', $char_d, '', '',
+ 'eventcode', @perl_type, '', '',
+ 'seconds', 'int', 'NULL', '', '', '',
+ 'weight', 'int', '', '', '', '',
+ 'plan', 'varchar', 'NULL', $char_d, '', '',
+ 'plandata', 'text', 'NULL', '', '', '',
+ 'reason', 'int', 'NULL', '', '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'eventpart',
+ 'unique' => [],
+ 'index' => [ ['payby'], ['disabled'], ],
+ },
+
+ 'part_event' => {
+ 'columns' => [
+ 'eventpart', 'serial', '', '', '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ 'event', 'varchar', '', $char_d, '', '',
+ 'eventtable', 'varchar', '', $char_d, '', '',
+ 'check_freq', 'varchar', 'NULL', $char_d, '', '',
+ 'weight', 'int', '', '', '', '',
+ 'action', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'eventpart',
+ 'unique' => [],
+ 'index' => [ ['agentnum'], ['eventtable'], ['check_freq'], ['disabled'], ],
+ },
+
+ 'part_event_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'eventpart', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'eventpart' ], [ 'optionname' ] ],
+ },
+
+ 'part_event_condition' => {
+ 'columns' => [
+ 'eventconditionnum', 'serial', '', '', '', '',
+ 'eventpart', 'int', '', '', '', '',
+ 'conditionname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'eventconditionnum',
+ 'unique' => [],
+ 'index' => [ [ 'eventpart' ], [ 'conditionname' ] ],
+ },
+
+ 'part_event_condition_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'eventconditionnum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'eventconditionnum' ], [ 'optionname' ] ],
+ },
+
+ 'part_event_condition_option_option' => {
+ 'columns' => [
+ 'optionoptionnum', 'serial', '', '', '', '',
+ 'optionnum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionoptionnum',
+ 'unique' => [],
+ 'index' => [ [ 'optionnum' ], [ 'optionname' ] ],
+ },
+
+ 'cust_event' => {
+ 'columns' => [
+ 'eventnum', 'serial', '', '', '', '',
+ 'eventpart', 'int', '', '', '', '',
+ 'tablenum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'status', 'varchar', '', $char_d, '', '',
+ 'statustext', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'eventnum',
+ #no... there are retries now #'unique' => [ [ 'eventpart', 'invnum' ] ],
+ 'unique' => [],
+ 'index' => [ ['eventpart'], ['tablenum'], ['status'] ],
+ },
+
+ 'cust_bill_pkg' => {
+ 'columns' => [
+ 'billpkgnum', 'serial', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ 'pkgnum', 'int', '', '', '', '',
+ 'pkgpart_override', 'int', 'NULL', '', '', '',
+ 'setup', @money_type, '', '',
+ 'recur', @money_type, '', '',
+ 'sdate', @date_type, '', '',
+ 'edate', @date_type, '', '',
+ 'itemdesc', 'varchar', 'NULL', $char_d, '', '',
+ 'section', 'varchar', 'NULL', $char_d, '', '',
+ 'quantity', 'int', 'NULL', '', '', '',
+ 'unitsetup', @money_typen, '', '',
+ 'unitrecur', @money_typen, '', '',
+ ],
+ 'primary_key' => 'billpkgnum',
+ 'unique' => [],
+ 'index' => [ ['invnum'], [ 'pkgnum' ] ],
+ },
+
+ 'cust_bill_pkg_detail' => {
+ 'columns' => [
+ 'detailnum', 'serial', '', '', '', '',
+ 'billpkgnum', 'int', 'NULL', '', '', '', # should not be nullable
+ 'pkgnum', 'int', 'NULL', '', '', '', # deprecated
+ 'invnum', 'int', 'NULL', '', '', '', # deprecated
+ 'amount', @money_typen, '', '',
+ 'format', 'char', 'NULL', 1, '', '',
+ 'classnum', 'char', 'NULL', 1, '', '',
+ 'detail', 'varchar', '', 255, '', '',
+ ],
+ 'primary_key' => 'detailnum',
+ 'unique' => [],
+ 'index' => [ [ 'billpkgnum' ], [ 'classnum' ], [ 'pkgnum', 'invnum' ] ],
+ },
+
+ 'cust_bill_pkg_display' => {
+ 'columns' => [
+ 'billpkgdisplaynum', 'serial', '', '', '', '',
+ 'billpkgnum', 'int', '', '', '', '',
+ 'section', 'varchar', 'NULL', $char_d, '', '',
+ #'unitsetup', @money_typen, '', '', #override the linked real one?
+ #'unitrecur', @money_typen, '', '', #this too?
+ 'post_total', 'char', 'NULL', 1, '', '',
+ 'type', 'char', 'NULL', 1, '', '',
+ 'summary', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'billpkgdisplaynum',
+ 'unique' => [],
+ 'index' => [ ['billpkgnum'], ],
+ },
+
+ 'cust_bill_pkg_tax_location' => {
+ 'columns' => [
+ 'billpkgtaxlocationnum', 'serial', '', '', '', '',
+ 'billpkgnum', 'int', '', '', '', '',
+ 'taxnum', 'int', '', '', '', '',
+ 'taxtype', 'varchar', $char_d, '', '', '',
+ 'pkgnum', 'int', '', '', '', '',
+ 'locationnum', 'int', '', '', '', '', #redundant?
+ 'amount', @money_type, '', '',
+ ],
+ 'primary_key' => 'billpkgtaxlocationnum',
+ 'unique' => [],
+ 'index' => [ [ 'billpkgnum' ], [ 'taxnum' ], [ 'pkgnum' ], [ 'locationnum' ] ],
+ },
+
+ 'cust_credit' => {
+ 'columns' => [
+ 'crednum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'amount', @money_type, '', '',
+ 'otaker', 'varchar', '', 32, '', '',
+ 'reason', 'text', 'NULL', '', '', '',
+ 'reasonnum', 'int', 'NULL', '', '', '',
+ 'addlinfo', 'text', 'NULL', '', '', '',
+ 'closed', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'crednum',
+ 'unique' => [],
+ 'index' => [ ['custnum'], ['_date'] ],
+ },
+
+ 'cust_credit_bill' => {
+ 'columns' => [
+ 'creditbillnum', 'serial', '', '', '', '',
+ 'crednum', 'int', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'amount', @money_type, '', '',
+ ],
+ 'primary_key' => 'creditbillnum',
+ 'unique' => [],
+ 'index' => [ ['crednum'], ['invnum'] ],
+ },
+
+ 'cust_credit_bill_pkg' => {
+ 'columns' => [
+ 'creditbillpkgnum', 'serial', '', '', '', '',
+ 'creditbillnum', 'int', '', '', '', '',
+ 'billpkgnum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ 'setuprecur', 'varchar', '', $char_d, '', '',
+ 'sdate', @date_type, '', '',
+ 'edate', @date_type, '', '',
+ ],
+ 'primary_key' => 'creditbillpkgnum',
+ 'unique' => [],
+ 'index' => [ [ 'creditbillnum' ], [ 'billpkgnum' ], ],
+ },
+
+ 'cust_main' => {
+ 'columns' => [
+ 'custnum', 'serial', '', '', '', '',
+ 'agentnum', 'int', '', '', '', '',
+ 'agent_custid', 'varchar', 'NULL', $char_d, '', '',
+ 'custbatch', 'varchar', 'NULL', $char_d, '', '',
+# 'titlenum', 'int', 'NULL', '', '', '',
+ 'last', 'varchar', '', $char_d, '', '',
+# 'middle', 'varchar', 'NULL', $char_d, '', '',
+ 'first', 'varchar', '', $char_d, '', '',
+ 'ss', 'varchar', 'NULL', 11, '', '',
+ 'stateid', 'varchar', 'NULL', $char_d, '', '',
+ 'stateid_state', 'varchar', 'NULL', $char_d, '', '',
+ 'birthdate' ,@date_type, '', '',
+ 'signupdate',@date_type, '', '',
+ 'dundate', @date_type, '', '',
+ 'company', 'varchar', 'NULL', $char_d, '', '',
+ 'address1', 'varchar', '', $char_d, '', '',
+ 'address2', 'varchar', 'NULL', $char_d, '', '',
+ 'city', 'varchar', '', $char_d, '', '',
+ 'county', 'varchar', 'NULL', $char_d, '', '',
+ 'state', 'varchar', 'NULL', $char_d, '', '',
+ 'zip', 'varchar', 'NULL', 10, '', '',
+ 'country', 'char', '', 2, '', '',
+ 'daytime', 'varchar', 'NULL', 20, '', '',
+ 'night', 'varchar', 'NULL', 20, '', '',
+ 'fax', 'varchar', 'NULL', 12, '', '',
+ 'ship_last', 'varchar', 'NULL', $char_d, '', '',
+# 'ship_middle', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_first', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_company', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_address1', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_address2', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_city', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_county', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_state', 'varchar', 'NULL', $char_d, '', '',
+ 'ship_zip', 'varchar', 'NULL', 10, '', '',
+ 'ship_country', 'char', 'NULL', 2, '', '',
+ 'ship_daytime', 'varchar', 'NULL', 20, '', '',
+ 'ship_night', 'varchar', 'NULL', 20, '', '',
+ 'ship_fax', 'varchar', 'NULL', 12, '', '',
+ 'payby', 'char', '', 4, '', '',
+ 'payinfo', 'varchar', 'NULL', 512, '', '',
+ 'paycvv', 'varchar', 'NULL', 512, '', '',
+ 'paymask', 'varchar', 'NULL', $char_d, '', '',
+ #'paydate', @date_type, '', '',
+ 'paydate', 'varchar', 'NULL', 10, '', '',
+ 'paystart_month', 'int', 'NULL', '', '', '',
+ 'paystart_year', 'int', 'NULL', '', '', '',
+ 'payissue', 'varchar', 'NULL', 2, '', '',
+ 'payname', 'varchar', 'NULL', $char_d, '', '',
+ 'paystate', 'varchar', 'NULL', $char_d, '', '',
+ 'paytype', 'varchar', 'NULL', $char_d, '', '',
+ 'payip', 'varchar', 'NULL', 15, '', '',
+ 'geocode', 'varchar', 'NULL', 20, '', '',
+ 'tax', 'char', 'NULL', 1, '', '',
+ 'otaker', 'varchar', '', 32, '', '',
+ 'refnum', 'int', '', '', '', '',
+ 'referral_custnum', 'int', 'NULL', '', '', '',
+ 'comments', 'text', 'NULL', '', '', '',
+ 'spool_cdr','char', 'NULL', 1, '', '',
+ 'squelch_cdr','char', 'NULL', 1, '', '',
+ 'invoice_terms', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'custnum',
+ 'unique' => [ [ 'agentnum', 'agent_custid' ] ],
+ #'index' => [ ['last'], ['company'] ],
+ 'index' => [
+ [ 'agentnum' ], [ 'refnum' ], [ 'custbatch' ],
+ [ 'referral_custnum' ],
+ [ 'payby' ], [ 'paydate' ],
+ #billing
+ [ 'last' ], [ 'company' ],
+ [ 'county' ], [ 'state' ], [ 'country' ],
+ [ 'zip' ],
+ [ 'daytime' ], [ 'night' ], [ 'fax' ],
+ #shipping
+ [ 'ship_last' ], [ 'ship_company' ],
+ [ 'ship_county' ], [ 'ship_state' ], [ 'ship_country' ],
+ [ 'ship_zip' ],
+ [ 'ship_daytime' ], [ 'ship_night' ], [ 'ship_fax' ],
+ ],
+ },
+
+ #eventually use for billing & ship from cust_main too
+ #for now, just cust_pkg locations
+ 'cust_location' => {
+ 'columns' => [
+ 'locationnum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'address1', 'varchar', '', $char_d, '', '',
+ 'address2', 'varchar', 'NULL', $char_d, '', '',
+ 'city', 'varchar', '', $char_d, '', '',
+ 'county', 'varchar', 'NULL', $char_d, '', '',
+ 'state', 'varchar', 'NULL', $char_d, '', '',
+ 'zip', 'varchar', 'NULL', 10, '', '',
+ 'country', 'char', '', 2, '', '',
+ 'geocode', 'varchar', 'NULL', 20, '', '',
+ ],
+ 'primary_key' => 'locationnum',
+ 'unique' => [],
+ 'index' => [ [ 'custnum' ],
+ [ 'county' ], [ 'state' ], [ 'country' ], [ 'zip' ],
+ ],
+ },
+
+ 'cust_main_invoice' => {
+ 'columns' => [
+ 'destnum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'dest', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'destnum',
+ 'unique' => [],
+ 'index' => [ ['custnum'], ],
+ },
+
+ 'cust_main_note' => {
+ 'columns' => [
+ 'notenum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'otaker', 'varchar', '', 32, '', '',
+ 'comments', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'notenum',
+ 'unique' => [],
+ 'index' => [ [ 'custnum' ], [ '_date' ], ],
+ },
+
+ 'cust_main_county' => { #county+state+country are checked off the
+ #cust_main_county for validation and to provide
+ # a tax rate.
+ 'columns' => [
+ 'taxnum', 'serial', '', '', '', '',
+ 'state', 'varchar', 'NULL', $char_d, '', '',
+ 'county', 'varchar', 'NULL', $char_d, '', '',
+ 'country', 'char', '', 2, '', '',
+ 'taxclass', 'varchar', 'NULL', $char_d, '', '',
+ 'exempt_amount', @money_type, '', '',
+ 'tax', 'real', '', '', '', '', #tax %
+ 'taxname', 'varchar', 'NULL', $char_d, '', '',
+ 'setuptax', 'char', 'NULL', 1, '', '', # Y = setup tax exempt
+ 'recurtax', 'char', 'NULL', 1, '', '', # Y = recur tax exempt
+ ],
+ 'primary_key' => 'taxnum',
+ 'unique' => [],
+ # 'unique' => [ ['taxnum'], ['state', 'county'] ],
+ 'index' => [ [ 'county' ], [ 'state' ], [ 'country' ],
+ [ 'taxclass' ],
+ ],
+ },
+
+ 'tax_rate' => {
+ 'columns' => [
+ 'taxnum', 'serial', '', '', '', '',
+ 'geocode', 'varchar', 'NULL', $char_d, '', '',#cch provides 10 char
+ 'data_vendor', 'varchar', 'NULL', $char_d, '', '',#auto update source
+ 'location', 'varchar', 'NULL', $char_d, '', '',#provided by tax authority
+ 'taxclassnum', 'int', '', '', '', '',
+ 'effective_date', @date_type, '', '',
+ 'tax', 'real', '', '', '', '', # tax %
+ 'excessrate', 'real', 'NULL','', '', '', # second tax %
+ 'taxbase', @money_typen, '', '', # amount at first tax rate
+ 'taxmax', @money_typen, '', '', # maximum about at both rates
+ 'usetax', 'real', 'NULL', '', '', '', # tax % when non-local
+ 'useexcessrate', 'real', 'NULL', '', '', '', # second tax % when non-local
+ 'unittype', 'int', 'NULL', '', '', '', # for fee
+ 'fee', 'real', 'NULL', '', '', '', # amount tax per unit
+ 'excessfee', 'real', 'NULL', '', '', '', # second amount tax per unit
+ 'feebase', 'real', 'NULL', '', '', '', # units taxed at first rate
+ 'feemax', 'real', 'NULL', '', '', '', # maximum number of unit taxed
+ 'maxtype', 'int', 'NULL', '', '', '', # indicator of how thresholds accumulate
+ 'taxname', 'varchar', 'NULL', $char_d, '', '', # may appear on invoice
+ 'taxauth', 'int', 'NULL', '', '', '', # tax authority
+ 'basetype', 'int', 'NULL', '', '', '', # indicator of basis for tax
+ 'passtype', 'int', 'NULL', '', '', '', # indicator declaring how item should be shown
+ 'passflag', 'char', 'NULL', 1, '', '', # Y = required to list as line item, N = Prohibited
+ 'setuptax', 'char', 'NULL', 1, '', '', # Y = setup tax exempt
+ 'recurtax', 'char', 'NULL', 1, '', '', # Y = recur tax exempt
+ 'manual', 'char', 'NULL', 1, '', '', # Y = manually edited
+ 'disabled', 'char', 'NULL', 1, '', '', # Y = tax disabled
+ ],
+ 'primary_key' => 'taxnum',
+ 'unique' => [],
+ 'index' => [ ['taxclassnum'], ['data_vendor', 'geocode'] ],
+ },
+
+ 'cust_tax_location' => {
+ 'columns' => [
+ 'custlocationnum', 'serial', '', '', '', '',
+ 'data_vendor', 'varchar', 'NULL', $char_d, '', '', # update source
+ 'city', 'varchar', 'NULL', $char_d, '', '',
+ 'postalcity', 'varchar', 'NULL', $char_d, '', '',
+ 'county', 'varchar', 'NULL', $char_d, '', '',
+ 'zip', 'char', '', 5, '', '',
+ 'state', 'char', '', 2, '', '',
+ 'plus4hi', 'char', 'NULL', 4, '', '',
+ 'plus4lo', 'char', 'NULL', 4, '', '',
+ 'default_location','char', 'NULL', 1, '', '', # Y = default for zip
+ 'cityflag', 'char', 'NULL', 1, '', '', # I(n)/O(out)/B(oth)/NULL
+ 'geocode', 'varchar', '', 20, '', '',
+ ],
+ 'primary_key' => 'custlocationnum',
+ 'unique' => [],
+ 'index' => [ [ 'zip', 'plus4lo', 'plus4hi' ] ],
+ },
+
+ 'tax_class' => {
+ 'columns' => [
+ 'taxclassnum', 'serial', '', '', '', '',
+ 'data_vendor', 'varchar', 'NULL', $char_d, '', '',
+ 'taxclass', 'varchar', '', $char_d, '', '',
+ 'description', 'varchar', '', 2*$char_d, '', '',
+ ],
+ 'primary_key' => 'taxclassnum',
+ 'unique' => [ [ 'data_vendor', 'taxclass' ] ],
+ 'index' => [],
+ },
+
+ 'cust_pay_pending' => {
+ 'columns' => [
+ 'paypendingnum','serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'paid', @money_type, '', '',
+ '_date', @date_type, '', '',
+ 'payby', 'char', '', 4, '', '', #CARD/BILL/COMP, should
+ # be index into payby
+ # table eventually
+ 'payinfo', 'varchar', 'NULL', 512, '', '', #see cust_main above
+ 'paymask', 'varchar', 'NULL', $char_d, '', '',
+ 'paydate', 'varchar', 'NULL', 10, '', '',
+ #'paybatch', 'varchar', 'NULL', $char_d, '', '', #for auditing purposes.
+ 'payunique', 'varchar', 'NULL', $char_d, '', '', #separate paybatch "unique" functions from current usage
+
+ 'status', 'varchar', '', $char_d, '', '',
+ 'statustext', 'text', 'NULL', '', '', '',
+ 'gatewaynum', 'int', 'NULL', '', '', '',
+ #'cust_balance', @money_type, '', '',
+ 'paynum', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'paypendingnum',
+ 'unique' => [ [ 'payunique' ] ],
+ 'index' => [ [ 'custnum' ], [ 'status' ], ],
+ },
+
+ 'cust_pay' => {
+ 'columns' => [
+ 'paynum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'paid', @money_type, '', '',
+ 'otaker', 'varchar', 'NULL', 32, '', '', #NULL for the upgrade so we can create & populate the field
+ 'payby', 'char', '', 4, '', '', # CARD/BILL/COMP, should be
+ # index into payby table
+ # eventually
+ 'payinfo', 'varchar', 'NULL', 512, '', '', #see cust_main above
+ 'paymask', 'varchar', 'NULL', $char_d, '', '',
+ 'paydate', 'varchar', 'NULL', 10, '', '',
+ 'paybatch', 'varchar', 'NULL', $char_d, '', '', #for auditing purposes.
+ 'payunique', 'varchar', 'NULL', $char_d, '', '', #separate paybatch "unique" functions from current usage
+ 'closed', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'paynum',
+ #i guess not now, with cust_pay_pending, if we actually make it here, we _do_ want to record it# 'unique' => [ [ 'payunique' ] ],
+ 'index' => [ [ 'custnum' ], [ 'paybatch' ], [ 'payby' ], [ '_date' ] ],
+ },
+
+ 'cust_pay_void' => {
+ 'columns' => [
+ 'paynum', 'int', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'paid', @money_type, '', '',
+ '_date', @date_type, '', '',
+ 'payby', 'char', '', 4, '', '', # CARD/BILL/COMP, should be
+ # index into payby table
+ # eventually
+ 'payinfo', 'varchar', 'NULL', 512, '', '', #see cust_main above
+ 'paymask', 'varchar', 'NULL', $char_d, '', '',
+ 'paybatch', 'varchar', 'NULL', $char_d, '', '', #for auditing purposes.
+ 'closed', 'char', 'NULL', 1, '', '',
+ 'void_date', @date_type, '', '',
+ 'reason', 'varchar', 'NULL', $char_d, '', '',
+ 'otaker', 'varchar', '', 32, '', '',
+ ],
+ 'primary_key' => 'paynum',
+ 'unique' => [],
+ 'index' => [ [ 'custnum' ] ],
+ },
+
+ 'cust_bill_pay' => {
+ 'columns' => [
+ 'billpaynum', 'serial', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ 'paynum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ '_date', @date_type, '', '',
+ ],
+ 'primary_key' => 'billpaynum',
+ 'unique' => [],
+ 'index' => [ [ 'paynum' ], [ 'invnum' ] ],
+ },
+
+ 'cust_bill_pay_batch' => {
+ 'columns' => [
+ 'billpaynum', 'serial', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ 'paybatchnum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ '_date', @date_type, '', '',
+ ],
+ 'primary_key' => 'billpaynum',
+ 'unique' => [],
+ 'index' => [ [ 'paybatchnum' ], [ 'invnum' ] ],
+ },
+
+ 'cust_bill_pay_pkg' => {
+ 'columns' => [
+ 'billpaypkgnum', 'serial', '', '', '', '',
+ 'billpaynum', 'int', '', '', '', '',
+ 'billpkgnum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ 'setuprecur', 'varchar', '', $char_d, '', '',
+ 'sdate', @date_type, '', '',
+ 'edate', @date_type, '', '',
+ ],
+ 'primary_key' => 'billpaypkgnum',
+ 'unique' => [],
+ 'index' => [ [ 'billpaynum' ], [ 'billpkgnum' ], ],
+ },
+
+ 'pay_batch' => { #batches of payments to an external processor
+ 'columns' => [
+ 'batchnum', 'serial', '', '', '', '',
+ 'payby', 'char', '', 4, '', '', # CARD/CHEK
+ 'status', 'char', 'NULL', 1, '', '',
+ 'download', @date_type, '', '',
+ 'upload', @date_type, '', '',
+ ],
+ 'primary_key' => 'batchnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'cust_pay_batch' => { #what's this used for again? list of customers
+ #in current CARD batch? (necessarily CARD?)
+ 'columns' => [
+ 'paybatchnum', 'serial', '', '', '', '',
+ 'batchnum', 'int', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'last', 'varchar', '', $char_d, '', '',
+ 'first', 'varchar', '', $char_d, '', '',
+ 'address1', 'varchar', '', $char_d, '', '',
+ 'address2', 'varchar', 'NULL', $char_d, '', '',
+ 'city', 'varchar', '', $char_d, '', '',
+ 'state', 'varchar', 'NULL', $char_d, '', '',
+ 'zip', 'varchar', 'NULL', 10, '', '',
+ 'country', 'char', '', 2, '', '',
+ # 'trancode', 'int', '', '', '', ''
+ 'payby', 'char', '', 4, '', '', # CARD/BILL/COMP, should be
+ 'payinfo', 'varchar', '', 512, '', '',
+ #'exp', @date_type, '', ''
+ 'exp', 'varchar', 'NULL', 11, '', '',
+ 'payname', 'varchar', 'NULL', $char_d, '', '',
+ 'amount', @money_type, '', '',
+ 'status', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'paybatchnum',
+ 'unique' => [],
+ 'index' => [ ['batchnum'], ['invnum'], ['custnum'] ],
+ },
+
+ 'cust_pkg' => {
+ 'columns' => [
+ 'pkgnum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'pkgpart', 'int', '', '', '', '',
+ 'locationnum', 'int', 'NULL', '', '', '',
+ 'otaker', 'varchar', '', 32, '', '',
+ 'setup', @date_type, '', '',
+ 'bill', @date_type, '', '',
+ 'last_bill', @date_type, '', '',
+ 'susp', @date_type, '', '',
+ 'adjourn', @date_type, '', '',
+ 'cancel', @date_type, '', '',
+ 'expire', @date_type, '', '',
+ 'change_date', @date_type, '', '',
+ 'change_pkgnum', 'int', 'NULL', '', '', '',
+ 'change_pkgpart', 'int', 'NULL', '', '', '',
+ 'change_locationnum', 'int', 'NULL', '', '', '',
+ 'manual_flag', 'char', 'NULL', 1, '', '',
+ 'quantity', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'pkgnum',
+ 'unique' => [],
+ 'index' => [ ['custnum'], ['pkgpart'], [ 'locationnum' ],
+ ['setup'], ['last_bill'], ['bill'], ['susp'], ['adjourn'],
+ ['expire'], ['cancel'],
+ ['change_date'],
+ ],
+ },
+
+ 'cust_pkg_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'pkgnum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'pkgnum' ], [ 'optionname' ] ],
+ },
+
+ 'cust_pkg_detail' => {
+ 'columns' => [
+ 'pkgdetailnum', 'serial', '', '', '', '',
+ 'pkgnum', 'int', '', '', '', '',
+ 'detail', 'varchar', '', $char_d, '', '',
+ 'detailtype', 'char', '', 1, '', '', # "I"nvoice or "C"omment
+ 'weight', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'pkgdetailnum',
+ 'unique' => [],
+ 'index' => [ [ 'pkgnum', 'detailtype' ] ],
+ },
+
+ 'cust_pkg_reason' => {
+ 'columns' => [
+ 'num', 'serial', '', '', '', '',
+ 'pkgnum', 'int', '', '', '', '',
+ 'reasonnum','int', '', '', '', '',
+ 'action', 'char', 'NULL', 1, '', '', #should not be nullable
+ 'otaker', 'varchar', '', 32, '', '',
+ 'date', @date_type, '', '',
+ ],
+ 'primary_key' => 'num',
+ 'unique' => [],
+ 'index' => [ [ 'pkgnum' ], [ 'reasonnum' ], ['action'], ],
+ },
+
+ 'cust_refund' => {
+ 'columns' => [
+ 'refundnum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'refund', @money_type, '', '',
+ 'otaker', 'varchar', '', 32, '', '',
+ 'reason', 'varchar', '', $char_d, '', '',
+ 'payby', 'char', '', 4, '', '', # CARD/BILL/COMP, should
+ # be index into payby
+ # table eventually
+ 'payinfo', 'varchar', 'NULL', 512, '', '', #see cust_main above
+ 'paymask', 'varchar', 'NULL', $char_d, '', '',
+ 'paybatch', 'varchar', 'NULL', $char_d, '', '',
+ 'closed', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'refundnum',
+ 'unique' => [],
+ 'index' => [ ['custnum'], ['_date'] ],
+ },
+
+ 'cust_credit_refund' => {
+ 'columns' => [
+ 'creditrefundnum', 'serial', '', '', '', '',
+ 'crednum', 'int', '', '', '', '',
+ 'refundnum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ '_date', @date_type, '', '',
+ ],
+ 'primary_key' => 'creditrefundnum',
+ 'unique' => [],
+ 'index' => [ ['crednum'], ['refundnum'] ],
+ },
+
+
+ 'cust_svc' => {
+ 'columns' => [
+ 'svcnum', 'serial', '', '', '', '',
+ 'pkgnum', 'int', 'NULL', '', '', '',
+ 'svcpart', 'int', '', '', '', '',
+ 'overlimit', @date_type, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
+ },
+
+ 'cust_svc_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'svcnum' ], [ 'optionname' ] ],
+ },
+
+ 'part_pkg' => {
+ 'columns' => [
+ 'pkgpart', 'serial', '', '', '', '',
+ 'pkg', 'varchar', '', $char_d, '', '',
+ 'comment', 'varchar', '', $char_d, '', '',
+ 'promo_code', 'varchar', 'NULL', $char_d, '', '',
+ 'setup', @perl_type, '', '',
+ 'freq', 'varchar', '', $char_d, '', '', #billing frequency
+ 'recur', @perl_type, '', '',
+ 'setuptax', 'char', 'NULL', 1, '', '',
+ 'recurtax', 'char', 'NULL', 1, '', '',
+ 'plan', 'varchar', 'NULL', $char_d, '', '',
+ 'plandata', 'text', 'NULL', '', '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ 'taxclass', 'varchar', 'NULL', $char_d, '', '',
+ 'classnum', 'int', 'NULL', '', '', '',
+ 'taxproductnum', 'int', 'NULL', '', '', '',
+ 'pay_weight', 'real', 'NULL', '', '', '',
+ 'credit_weight', 'real', 'NULL', '', '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+
+ ],
+ 'primary_key' => 'pkgpart',
+ 'unique' => [],
+ 'index' => [ [ 'promo_code' ], [ 'disabled' ], [ 'agentnum' ], ],
+ },
+
+ 'part_pkg_link' => {
+ 'columns' => [
+ 'pkglinknum', 'serial', '', '', '', '',
+ 'src_pkgpart', 'int', '', '', '', '',
+ 'dst_pkgpart', 'int', '', '', '', '',
+ 'link_type', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'pkglinknum',
+ 'unique' => [ [ 'src_pkgpart', 'dst_pkgpart', 'link_type' ] ],
+ 'index' => [ [ 'src_pkgpart' ] ],
+ },
+
+ 'part_pkg_taxclass' => {
+ 'columns' => [
+ 'taxclassnum', 'serial', '', '', '', '',
+ 'taxclass', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'taxclassnum',
+ 'unique' => [ [ 'taxclass' ] ],
+ 'index' => [],
+ },
+
+ 'part_pkg_taxproduct' => {
+ 'columns' => [
+ 'taxproductnum', 'serial', '', '', '', '',
+ 'data_vendor', 'varchar', 'NULL', $char_d, '', '',
+ 'taxproduct', 'varchar', '', $char_d, '', '',
+ 'description', 'varchar', '', 3*$char_d, '', '',
+ ],
+ 'primary_key' => 'taxproductnum',
+ 'unique' => [ [ 'data_vendor', 'taxproduct' ] ],
+ 'index' => [],
+ },
+
+ 'part_pkg_taxrate' => {
+ 'columns' => [
+ 'pkgtaxratenum', 'serial', '', '', '', '',
+ 'data_vendor', 'varchar', 'NULL', $char_d, '', '', # update source
+ 'geocode', 'varchar', 'NULL', $char_d, '', '', # cch provides 10
+ 'taxproductnum', 'int', '', '', '', '',
+ 'city', 'varchar', 'NULL', $char_d, '', '', # tax_location?
+ 'county', 'varchar', 'NULL', $char_d, '', '',
+ 'state', 'varchar', 'NULL', $char_d, '', '',
+ 'local', 'varchar', 'NULL', $char_d, '', '',
+ 'country', 'char', 'NULL', 2, '', '',
+ 'taxclassnumtaxed', 'int', 'NULL', '', '', '',
+ 'taxcattaxed', 'varchar', 'NULL', $char_d, '', '',
+ 'taxclassnum', 'int', 'NULL', '', '', '',
+ 'effdate', @date_type, '', '',
+ 'taxable', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'pkgtaxratenum',
+ 'unique' => [],
+ 'index' => [ [ 'data_vendor', 'geocode', 'taxproductnum' ] ],
+ },
+
+ 'part_pkg_taxoverride' => {
+ 'columns' => [
+ 'taxoverridenum', 'serial', '', '', '', '',
+ 'pkgpart', 'serial', '', '', '', '',
+ 'taxclassnum', 'serial', '', '', '', '',
+ 'usage_class', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'taxoverridenum',
+ 'unique' => [],
+ 'index' => [ [ 'pkgpart' ], [ 'taxclassnum' ] ],
+ },
+
+# 'part_title' => {
+# 'columns' => [
+# 'titlenum', 'int', '', '',
+# 'title', 'varchar', '', $char_d,
+# ],
+# 'primary_key' => 'titlenum',
+# 'unique' => [ [] ],
+# 'index' => [ [] ],
+# },
+
+ 'pkg_svc' => {
+ 'columns' => [
+ 'pkgsvcnum', 'serial', '', '', '', '',
+ 'pkgpart', 'int', '', '', '', '',
+ 'svcpart', 'int', '', '', '', '',
+ 'quantity', 'int', '', '', '', '',
+ 'primary_svc','char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'pkgsvcnum',
+ 'unique' => [ ['pkgpart', 'svcpart'] ],
+ 'index' => [ ['pkgpart'] ],
+ },
+
+ 'part_referral' => {
+ 'columns' => [
+ 'refnum', 'serial', '', '', '', '',
+ 'referral', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'refnum',
+ 'unique' => [],
+ 'index' => [ ['disabled'], ['agentnum'], ],
+ },
+
+ 'part_svc' => {
+ 'columns' => [
+ 'svcpart', 'serial', '', '', '', '',
+ 'svc', 'varchar', '', $char_d, '', '',
+ 'svcdb', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'svcpart',
+ 'unique' => [],
+ 'index' => [ [ 'disabled' ] ],
+ },
+
+ 'part_svc_column' => {
+ 'columns' => [
+ 'columnnum', 'serial', '', '', '', '',
+ 'svcpart', 'int', '', '', '', '',
+ 'columnname', 'varchar', '', 64, '', '',
+ 'columnvalue', 'varchar', 'NULL', $char_d, '', '',
+ 'columnflag', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'columnnum',
+ 'unique' => [ [ 'svcpart', 'columnname' ] ],
+ 'index' => [ [ 'svcpart' ] ],
+ },
+
+ #(this should be renamed to part_pop)
+ 'svc_acct_pop' => {
+ 'columns' => [
+ 'popnum', 'serial', '', '', '', '',
+ 'city', 'varchar', '', $char_d, '', '',
+ 'state', 'varchar', '', $char_d, '', '',
+ 'ac', 'char', '', 3, '', '',
+ 'exch', 'char', '', 3, '', '',
+ 'loc', 'char', 'NULL', 4, '', '', #NULL for legacy purposes
+ ],
+ 'primary_key' => 'popnum',
+ 'unique' => [],
+ 'index' => [ [ 'state' ] ],
+ },
+
+ 'part_pop_local' => {
+ 'columns' => [
+ 'localnum', 'serial', '', '', '', '',
+ 'popnum', 'int', '', '', '', '',
+ 'city', 'varchar', 'NULL', $char_d, '', '',
+ 'state', 'char', 'NULL', 2, '', '',
+ 'npa', 'char', '', 3, '', '',
+ 'nxx', 'char', '', 3, '', '',
+ ],
+ 'primary_key' => 'localnum',
+ 'unique' => [],
+ 'index' => [ [ 'npa', 'nxx' ], [ 'popnum' ] ],
+ },
+
+ 'svc_acct' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'username', 'varchar', '', $username_len, '', '',
+ '_password', 'varchar', '', 512, '', '',
+ '_password_encoding', 'varchar', 'NULL', $char_d, '', '',
+ 'sec_phrase', 'varchar', 'NULL', $char_d, '', '',
+ 'popnum', 'int', 'NULL', '', '', '',
+ 'uid', 'int', 'NULL', '', '', '',
+ 'gid', 'int', 'NULL', '', '', '',
+ 'finger', 'varchar', 'NULL', $char_d, '', '',
+ 'dir', 'varchar', 'NULL', $char_d, '', '',
+ 'shell', 'varchar', 'NULL', $char_d, '', '',
+ 'quota', 'varchar', 'NULL', $char_d, '', '',
+ 'slipip', 'varchar', 'NULL', 15, '', '', #four TINYINTs, bah.
+ 'seconds', 'int', 'NULL', '', '', '', #uhhhh
+ 'seconds_threshold', 'int', 'NULL', '', '', '',
+ 'upbytes', 'bigint', 'NULL', '', '', '',
+ 'upbytes_threshold', 'bigint', 'NULL', '', '', '',
+ 'downbytes', 'bigint', 'NULL', '', '', '',
+ 'downbytes_threshold', 'bigint', 'NULL', '', '', '',
+ 'totalbytes','bigint', 'NULL', '', '', '',
+ 'totalbytes_threshold', 'bigint', 'NULL', '', '', '',
+ 'domsvc', 'int', '', '', '', '',
+ 'last_login', @date_type, '', '',
+ 'last_logout', @date_type, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ #'unique' => [ [ 'username', 'domsvc' ] ],
+ 'unique' => [],
+ 'index' => [ ['username'], ['domsvc'] ],
+ },
+
+ 'acct_rt_transaction' => {
+ 'columns' => [
+ 'svcrtid', 'int', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'transaction_id', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'seconds', 'int', '', '', '', '', #uhhhh
+ 'support', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'svcrtid',
+ 'unique' => [],
+ 'index' => [ ['svcnum', 'transaction_id'] ],
+ },
+
+ #'svc_charge' => {
+ # 'columns' => [
+ # 'svcnum', 'int', '', '',
+ # 'amount', @money_type,
+ # ],
+ # 'primary_key' => 'svcnum',
+ # 'unique' => [ [] ],
+ # 'index' => [ [] ],
+ #},
+
+ 'svc_domain' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'domain', 'varchar', '', $char_d, '', '',
+ 'suffix', 'varchar', 'NULL', $char_d, '', '',
+ 'catchall', 'int', 'NULL', '', '', '',
+ 'parent_svcnum', 'int', 'NULL', '', '', '',
+ 'registrarnum', 'int', 'NULL', '', '', '',
+ 'registrarkey', 'varchar', 'NULL', 512, '', '',
+ 'setup_date', @date_type, '', '',
+ 'renewal_interval', 'int', 'NULL', '', '', '',
+ 'expiration_date', @date_type, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [ ],
+ 'index' => [ ['domain'] ],
+ },
+
+ 'domain_record' => {
+ 'columns' => [
+ 'recnum', 'serial', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'reczone', 'varchar', '', 255, '', '',
+ 'recaf', 'char', '', 2, '', '',
+ 'rectype', 'varchar', '', 5, '', '',
+ 'recdata', 'varchar', '', 255, '', '',
+ ],
+ 'primary_key' => 'recnum',
+ 'unique' => [],
+ 'index' => [ ['svcnum'] ],
+ },
+
+ 'registrar' => {
+ 'columns' => [
+ 'registrarnum', 'serial', '', '', '', '',
+ 'registrarname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'registrarnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'svc_forward' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'srcsvc', 'int', 'NULL', '', '', '',
+ 'src', 'varchar', 'NULL', 255, '', '',
+ 'dstsvc', 'int', 'NULL', '', '', '',
+ 'dst', 'varchar', 'NULL', 255, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [ ['srcsvc'], ['dstsvc'] ],
+ },
+
+ 'svc_www' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'recnum', 'int', '', '', '', '',
+ 'usersvc', 'int', 'NULL', '', '', '',
+ 'config', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ #'svc_wo' => {
+ # 'columns' => [
+ # 'svcnum', 'int', '', '',
+ # 'svcnum', 'int', '', '',
+ # 'svcnum', 'int', '', '',
+ # 'worker', 'varchar', '', $char_d,
+ # '_date', @date_type,
+ # ],
+ # 'primary_key' => 'svcnum',
+ # 'unique' => [ [] ],
+ # 'index' => [ [] ],
+ #},
+
+ 'prepay_credit' => {
+ 'columns' => [
+ 'prepaynum', 'serial', '', '', '', '',
+ 'identifier', 'varchar', '', $char_d, '', '',
+ 'amount', @money_type, '', '',
+ 'seconds', 'int', 'NULL', '', '', '',
+ 'upbytes', 'bigint', 'NULL', '', '', '',
+ 'downbytes', 'bigint', 'NULL', '', '', '',
+ 'totalbytes', 'bigint', 'NULL', '', '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'prepaynum',
+ 'unique' => [ ['identifier'] ],
+ 'index' => [],
+ },
+
+ 'port' => {
+ 'columns' => [
+ 'portnum', 'serial', '', '', '', '',
+ 'ip', 'varchar', 'NULL', 15, '', '',
+ 'nasport', 'int', 'NULL', '', '', '',
+ 'nasnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'portnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'nas' => {
+ 'columns' => [
+ 'nasnum', 'serial', '', '', '', '',
+ 'nas', 'varchar', '', $char_d, '', '',
+ 'nasip', 'varchar', '', 15, '', '',
+ 'nasfqdn', 'varchar', '', $char_d, '', '',
+ 'last', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'nasnum',
+ 'unique' => [ [ 'nas' ], [ 'nasip' ] ],
+ 'index' => [ [ 'last' ] ],
+ },
+
+# 'session' => {
+# 'columns' => [
+# 'sessionnum', 'serial', '', '', '', '',
+# 'portnum', 'int', '', '', '', '',
+# 'svcnum', 'int', '', '', '', '',
+# 'login', @date_type, '', '',
+# 'logout', @date_type, '', '',
+# ],
+# 'primary_key' => 'sessionnum',
+# 'unique' => [],
+# 'index' => [ [ 'portnum' ] ],
+# },
+
+ 'queue' => {
+ 'columns' => [
+ 'jobnum', 'serial', '', '', '', '',
+ 'job', 'text', '', '', '', '',
+ '_date', 'int', '', '', '', '',
+ 'status', 'varchar', '', $char_d, '', '',
+ 'statustext', 'text', 'NULL', '', '', '',
+ 'svcnum', 'int', 'NULL', '', '', '',
+ 'custnum', 'int', 'NULL', '', '', '',
+ 'secure', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'jobnum',
+ 'unique' => [],
+ 'index' => [ [ 'job' ], [ 'svcnum' ], [ 'custnum' ], [ 'status' ] ],
+ },
+
+ 'queue_arg' => {
+ 'columns' => [
+ 'argnum', 'serial', '', '', '', '',
+ 'jobnum', 'int', '', '', '', '',
+ 'arg', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'argnum',
+ 'unique' => [],
+ 'index' => [ [ 'jobnum' ] ],
+ },
+
+ 'queue_depend' => {
+ 'columns' => [
+ 'dependnum', 'serial', '', '', '', '',
+ 'jobnum', 'int', '', '', '', '',
+ 'depend_jobnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'dependnum',
+ 'unique' => [],
+ 'index' => [ [ 'jobnum' ], [ 'depend_jobnum' ] ],
+ },
+
+ 'export_svc' => {
+ 'columns' => [
+ 'exportsvcnum' => 'serial', '', '', '', '',
+ 'exportnum' => 'int', '', '', '', '',
+ 'svcpart' => 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'exportsvcnum',
+ 'unique' => [ [ 'exportnum', 'svcpart' ] ],
+ 'index' => [ [ 'exportnum' ], [ 'svcpart' ] ],
+ },
+
+ 'part_export' => {
+ 'columns' => [
+ 'exportnum', 'serial', '', '', '', '',
+ 'machine', 'varchar', '', $char_d, '', '',
+ 'exporttype', 'varchar', '', $char_d, '', '',
+ 'nodomain', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'exportnum',
+ 'unique' => [],
+ 'index' => [ [ 'machine' ], [ 'exporttype' ] ],
+ },
+
+ 'part_export_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'exportnum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'exportnum' ], [ 'optionname' ] ],
+ },
+
+ 'radius_usergroup' => {
+ 'columns' => [
+ 'usergroupnum', 'serial', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'groupname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'usergroupnum',
+ 'unique' => [],
+ 'index' => [ [ 'svcnum' ], [ 'groupname' ] ],
+ },
+
+ 'msgcat' => {
+ 'columns' => [
+ 'msgnum', 'serial', '', '', '', '',
+ 'msgcode', 'varchar', '', $char_d, '', '',
+ 'locale', 'varchar', '', 16, '', '',
+ 'msg', 'text', '', '', '', '',
+ ],
+ 'primary_key' => 'msgnum',
+ 'unique' => [ [ 'msgcode', 'locale' ] ],
+ 'index' => [],
+ },
+
+ 'cust_tax_exempt' => {
+ 'columns' => [
+ 'exemptnum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'taxnum', 'int', '', '', '', '',
+ 'year', 'int', '', '', '', '',
+ 'month', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ ],
+ 'primary_key' => 'exemptnum',
+ 'unique' => [ [ 'custnum', 'taxnum', 'year', 'month' ] ],
+ 'index' => [],
+ },
+
+ 'cust_tax_exempt_pkg' => {
+ 'columns' => [
+ 'exemptpkgnum', 'serial', '', '', '', '',
+ #'custnum', 'int', '', '', '', ''
+ 'billpkgnum', 'int', '', '', '', '',
+ 'taxnum', 'int', '', '', '', '',
+ 'year', 'int', '', '', '', '',
+ 'month', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ ],
+ 'primary_key' => 'exemptpkgnum',
+ 'unique' => [],
+ 'index' => [ [ 'taxnum', 'year', 'month' ],
+ [ 'billpkgnum' ],
+ [ 'taxnum' ]
+ ],
+ },
+
+ 'router' => {
+ 'columns' => [
+ 'routernum', 'serial', '', '', '', '',
+ 'routername', 'varchar', '', $char_d, '', '',
+ 'svcnum', 'int', 'NULL', '', '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'routernum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'part_svc_router' => {
+ 'columns' => [
+ 'svcrouternum', 'serial', '', '', '', '',
+ 'svcpart', 'int', '', '', '', '',
+ 'routernum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'svcrouternum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'addr_block' => {
+ 'columns' => [
+ 'blocknum', 'serial', '', '', '', '',
+ 'routernum', 'int', '', '', '', '',
+ 'ip_gateway', 'varchar', '', 15, '', '',
+ 'ip_netmask', 'int', '', '', '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ 'manual_flag', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'blocknum',
+ 'unique' => [ [ 'blocknum', 'routernum' ] ],
+ 'index' => [],
+ },
+
+ 'svc_broadband' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'description', 'varchar', 'NULL', $char_d, '', '',
+ 'blocknum', 'int', '', '', '', '',
+ 'speed_up', 'int', '', '', '', '',
+ 'speed_down', 'int', '', '', '', '',
+ 'ip_addr', 'varchar', '', 15, '', '',
+ 'mac_addr', 'varchar', 'NULL', 12, '', '',
+ 'authkey', 'varchar', 'NULL', 32, '', '',
+ 'latitude', 'decimal', 'NULL', '', '', '',
+ 'longitude', 'decimal', 'NULL', '', '', '',
+ 'altitude', 'decimal', 'NULL', '', '', '',
+ 'vlan_profile', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [ [ 'mac_addr' ] ],
+ 'index' => [],
+ },
+
+ 'part_virtual_field' => {
+ 'columns' => [
+ 'vfieldpart', 'serial', '', '', '', '',
+ 'dbtable', 'varchar', '', 32, '', '',
+ 'name', 'varchar', '', 32, '', '',
+ 'check_block', 'text', 'NULL', '', '', '',
+ 'length', 'int', 'NULL', '', '', '',
+ 'list_source', 'text', 'NULL', '', '', '',
+ 'label', 'varchar', 'NULL', 80, '', '',
+ ],
+ 'primary_key' => 'vfieldpart',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'virtual_field' => {
+ 'columns' => [
+ 'vfieldnum', 'serial', '', '', '', '',
+ 'recnum', 'int', '', '', '', '',
+ 'vfieldpart', 'int', '', '', '', '',
+ 'value', 'varchar', '', 128, '', '',
+ ],
+ 'primary_key' => 'vfieldnum',
+ 'unique' => [ [ 'vfieldpart', 'recnum' ] ],
+ 'index' => [],
+ },
+
+ 'acct_snarf' => {
+ 'columns' => [
+ 'snarfnum', 'int', '', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'machine', 'varchar', '', 255, '', '',
+ 'protocol', 'varchar', '', $char_d, '', '',
+ 'username', 'varchar', '', $char_d, '', '',
+ '_password', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'snarfnum',
+ 'unique' => [],
+ 'index' => [ [ 'svcnum' ] ],
+ },
+
+ 'svc_external' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'id', 'int', 'NULL', '', '', '',
+ 'title', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'cust_pay_refund' => {
+ 'columns' => [
+ 'payrefundnum', 'serial', '', '', '', '',
+ 'paynum', 'int', '', '', '', '',
+ 'refundnum', 'int', '', '', '', '',
+ '_date', @date_type, '', '',
+ 'amount', @money_type, '', '',
+ ],
+ 'primary_key' => 'payrefundnum',
+ 'unique' => [],
+ 'index' => [ ['paynum'], ['refundnum'] ],
+ },
+
+ 'part_pkg_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'pkgpart', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'pkgpart' ], [ 'optionname' ] ],
+ },
+
+ 'rate' => {
+ 'columns' => [
+ 'ratenum', 'serial', '', '', '', '',
+ 'ratename', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'ratenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'rate_detail' => {
+ 'columns' => [
+ 'ratedetailnum', 'serial', '', '', '', '',
+ 'ratenum', 'int', '', '', '', '',
+ 'orig_regionnum', 'int', 'NULL', '', '', '',
+ 'dest_regionnum', 'int', '', '', '', '',
+ 'min_included', 'int', '', '', '', '',
+ #'min_charge', @money_type, '', '',
+ 'min_charge', 'decimal', '', '10,5', '', '',
+ 'sec_granularity', 'int', '', '', '', '',
+ #time period (link to table of periods)?
+ 'classnum', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'ratedetailnum',
+ 'unique' => [ [ 'ratenum', 'orig_regionnum', 'dest_regionnum' ] ],
+ 'index' => [ [ 'ratenum', 'dest_regionnum' ] ],
+ },
+
+ 'rate_region' => {
+ 'columns' => [
+ 'regionnum', 'serial', '', '', '', '',
+ 'regionname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'regionnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'rate_prefix' => {
+ 'columns' => [
+ 'prefixnum', 'serial', '', '', '', '',
+ 'regionnum', 'int', '', '', '', '',
+ 'countrycode', 'varchar', '', 3, '', '',
+ 'npa', 'varchar', 'NULL', 10, '', '', #actually the whole prefix
+ 'nxx', 'varchar', 'NULL', 3, '', '', #actually not used
+ ],
+ 'primary_key' => 'prefixnum',
+ 'unique' => [],
+ 'index' => [ [ 'countrycode' ], [ 'regionnum' ] ],
+ },
+
+ 'usage_class' => {
+ 'columns' => [
+ 'classnum', 'serial', '', '', '', '',
+ 'classname', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'classnum',
+ 'unique' => [],
+ 'index' => [ ['disabled'] ],
+ },
+
+ 'reg_code' => {
+ 'columns' => [
+ 'codenum', 'serial', '', '', '', '',
+ 'code', 'varchar', '', $char_d, '', '',
+ 'agentnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'codenum',
+ 'unique' => [ [ 'agentnum', 'code' ] ],
+ 'index' => [ [ 'agentnum' ] ],
+ },
+
+ 'reg_code_pkg' => {
+ 'columns' => [
+ 'codepkgnum', 'serial', '', '', '', '',
+ 'codenum', 'int', '', '', '', '',
+ 'pkgpart', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'codepkgnum',
+ 'unique' => [ [ 'codenum', 'pkgpart' ] ],
+ 'index' => [ [ 'codenum' ] ],
+ },
+
+ 'clientapi_session' => {
+ 'columns' => [
+ 'sessionnum', 'serial', '', '', '', '',
+ 'sessionid', 'varchar', '', $char_d, '', '',
+ 'namespace', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'sessionnum',
+ 'unique' => [ [ 'sessionid', 'namespace' ] ],
+ 'index' => [],
+ },
+
+ 'clientapi_session_field' => {
+ 'columns' => [
+ 'fieldnum', 'serial', '', '', '', '',
+ 'sessionnum', 'int', '', '', '', '',
+ 'fieldname', 'varchar', '', $char_d, '', '',
+ 'fieldvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'fieldnum',
+ 'unique' => [ [ 'sessionnum', 'fieldname' ] ],
+ 'index' => [],
+ },
+
+ 'payment_gateway' => {
+ 'columns' => [
+ 'gatewaynum', 'serial', '', '', '', '',
+ 'gateway_module', 'varchar', '', $char_d, '', '',
+ 'gateway_username', 'varchar', 'NULL', $char_d, '', '',
+ 'gateway_password', 'varchar', 'NULL', $char_d, '', '',
+ 'gateway_action', 'varchar', 'NULL', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'gatewaynum',
+ 'unique' => [],
+ 'index' => [ [ 'disabled' ] ],
+ },
+
+ 'payment_gateway_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '', '', '',
+ 'gatewaynum', 'int', '', '', '', '',
+ 'optionname', 'varchar', '', $char_d, '', '',
+ 'optionvalue', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'gatewaynum' ], [ 'optionname' ] ],
+ },
+
+ 'agent_payment_gateway' => {
+ 'columns' => [
+ 'agentgatewaynum', 'serial', '', '', '', '',
+ 'agentnum', 'int', '', '', '', '',
+ 'gatewaynum', 'int', '', '', '', '',
+ 'cardtype', 'varchar', 'NULL', $char_d, '', '',
+ 'taxclass', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'agentgatewaynum',
+ 'unique' => [],
+ 'index' => [ [ 'agentnum', 'cardtype' ], ],
+ },
+
+ 'banned_pay' => {
+ 'columns' => [
+ 'bannum', 'serial', '', '', '', '',
+ 'payby', 'char', '', 4, '', '',
+ 'payinfo', 'varchar', '', 128, '', '', #say, a 512-big digest _hex encoded
+ #'paymask', 'varchar', 'NULL', $char_d, '', ''
+ '_date', @date_type, '', '',
+ 'otaker', 'varchar', '', 32, '', '',
+ 'reason', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'bannum',
+ 'unique' => [ [ 'payby', 'payinfo' ] ],
+ 'index' => [],
+ },
+
+ 'pkg_category' => {
+ 'columns' => [
+ 'categorynum', 'serial', '', '', '', '',
+ 'categoryname', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'categorynum',
+ 'unique' => [],
+ 'index' => [ ['disabled'] ],
+ },
+
+ 'pkg_class' => {
+ 'columns' => [
+ 'classnum', 'serial', '', '', '', '',
+ 'classname', 'varchar', '', $char_d, '', '',
+ 'categorynum', 'int', 'NULL', '', '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'classnum',
+ 'unique' => [],
+ 'index' => [ ['disabled'] ],
+ },
+
+ 'cdr' => {
+ 'columns' => [
+ # qw( name type null length default local );
+
+ ###
+ #asterisk fields
+ ###
+
+ 'acctid', 'bigserial', '', '', '', '',
+ #'calldate', 'TIMESTAMP with time zone', '', '', \'now()', '',
+ 'calldate', 'timestamp', '', '', \'now()', '',
+ 'clid', 'varchar', '', $char_d, \"''", '',
+ 'src', 'varchar', '', $char_d, \"''", '',
+ 'dst', 'varchar', '', $char_d, \"''", '',
+ 'dcontext', 'varchar', '', $char_d, \"''", '',
+ 'channel', 'varchar', '', $char_d, \"''", '',
+ 'dstchannel', 'varchar', '', $char_d, \"''", '',
+ 'lastapp', 'varchar', '', $char_d, \"''", '',
+ 'lastdata', 'varchar', '', $char_d, \"''", '',
+
+ #these don't seem to be logged by most of the SQL cdr_* modules
+ #except tds under sql-illegal names, so;
+ # ... don't rely on them for rating?
+ # and, what they hey, i went ahead and changed the names and data types
+ # to freeside-style dates...
+ #'start', 'timestamp', 'NULL', '', '', '',
+ #'answer', 'timestamp', 'NULL', '', '', '',
+ #'end', 'timestamp', 'NULL', '', '', '',
+ 'startdate', @date_type, '', '',
+ 'answerdate', @date_type, '', '',
+ 'enddate', @date_type, '', '',
+ #
+
+ 'duration', 'int', '', '', 0, '',
+ 'billsec', 'int', '', '', 0, '',
+ 'disposition', 'varchar', '', 45, \"''", '',
+ 'amaflags', 'int', '', '', 0, '',
+ 'accountcode', 'varchar', '', 20, \"''", '',
+ 'uniqueid', 'varchar', '', 32, \"''", '',
+ 'userfield', 'varchar', '', 255, \"''", '',
+
+ ###
+ # fields for unitel/RSLCOM/convergent that don't map well to asterisk
+ # defaults
+ ###
+
+ #cdr_type: Usage = 1, S&E = 7, OC&C = 8
+ 'cdrtypenum', 'int', 'NULL', '', '', '',
+
+ 'charged_party', 'varchar', 'NULL', $char_d, '', '',
+
+ 'upstream_currency', 'char', 'NULL', 3, '', '',
+ 'upstream_price', 'decimal', 'NULL', '10,2', '', '',
+ 'upstream_rateplanid', 'int', 'NULL', '', '', '', #?
+
+ # how it was rated internally...
+ 'ratedetailnum', 'int', 'NULL', '', '', '',
+ 'rated_price', 'decimal', 'NULL', '10,2', '', '',
+
+ 'distance', 'decimal', 'NULL', '', '', '',
+ 'islocal', 'int', 'NULL', '', '', '', # '', '', 0, '' instead?
+
+ #cdr_calltype: the big list in appendix 2
+ 'calltypenum', 'int', 'NULL', '', '', '',
+
+ 'description', 'varchar', 'NULL', $char_d, '', '',
+ 'quantity', 'int', 'NULL', '', '', '',
+
+ #cdr_carrier: Telstra =1, Optus = 2, RSL COM = 3
+ 'carrierid', 'int', 'NULL', '', '', '',
+
+ 'upstream_rateid', 'int', 'NULL', '', '', '',
+
+ ###
+ #and now for our own fields
+ ###
+
+ # a svcnum... right..?
+ 'svcnum', 'int', 'NULL', '', '', '',
+
+ #NULL, done (or something)
+ 'freesidestatus', 'varchar', 'NULL', 32, '', '',
+
+ #NULL, done (or something)
+ 'freesiderewritestatus', 'varchar', 'NULL', 32, '', '',
+
+ 'cdrbatch', 'varchar', 'NULL', $char_d, '', '',
+
+ ],
+ 'primary_key' => 'acctid',
+ 'unique' => [],
+ 'index' => [ [ 'calldate' ], [ 'src' ], [ 'dst' ], [ 'charged_party' ], [ 'accountcode' ], [ 'freesidestatus' ], [ 'freesiderewritestatus' ], [ 'cdrbatch' ], ],
+ },
+
+ 'cdr_calltype' => {
+ 'columns' => [
+ 'calltypenum', 'serial', '', '', '', '',
+ 'calltypename', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'calltypenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'cdr_type' => {
+ 'columns' => [
+ 'cdrtypenum' => 'serial', '', '', '', '',
+ 'cdrtypename' => 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'cdrtypenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'cdr_carrier' => {
+ 'columns' => [
+ 'carrierid' => 'serial', '', '', '', '',
+ 'carriername' => 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'carrierid',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ #map upstream rateid to ours...
+ 'cdr_upstream_rate' => {
+ 'columns' => [
+ 'upstreamratenum', 'serial', '', '', '', '',
+ 'upstream_rateid', 'varchar', '', $char_d, '', '',
+ 'ratedetailnum', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'upstreamratenum', #XXX need a primary key
+ 'unique' => [ [ 'upstream_rateid' ] ], #unless we add another field, yeah
+ 'index' => [],
+ },
+
+ #'cdr_file' => {
+ # 'columns' => [
+ # 'filenum', 'serial', '', '', '', '',
+ # 'filename', 'varchar', '', '', '', '',
+ # 'status', 'varchar', 'NULL', '', '', '',
+ # ],
+ # 'primary_key' => 'filenum',
+ # 'unique' => [ [ 'filename' ], ], #just change the index if we need to
+ # # agent-virtualize or have a customer
+ # # with dup-filename needs or something
+ # # (only used by cdr.http_and_import for
+ # # chrissakes)
+ # 'index' => [],
+ #},
+
+ 'inventory_item' => {
+ 'columns' => [
+ 'itemnum', 'serial', '', '', '', '',
+ 'classnum', 'int', '', '', '', '',
+ 'item', 'varchar', '', $char_d, '', '',
+ 'svcnum', 'int', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'itemnum',
+ 'unique' => [ [ 'classnum', 'item' ] ],
+ 'index' => [ [ 'classnum' ], [ 'svcnum' ] ],
+ },
+
+ 'inventory_class' => {
+ 'columns' => [
+ 'classnum', 'serial', '', '', '', '',
+ 'classname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'classnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'access_user' => {
+ 'columns' => [
+ 'usernum', 'serial', '', '', '', '',
+ 'username', 'varchar', '', $char_d, '', '',
+ '_password', 'varchar', '', $char_d, '', '',
+ 'last', 'varchar', '', $char_d, '', '',
+ 'first', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'usernum',
+ 'unique' => [ [ 'username' ] ],
+ 'index' => [],
+ },
+
+ 'access_user_pref' => {
+ 'columns' => [
+ 'prefnum', 'serial', '', '', '', '',
+ 'usernum', 'int', '', '', '', '',
+ 'prefname', 'varchar', '', $char_d, '', '',
+ 'prefvalue', 'text', 'NULL', '', '', '',
+ 'expiration', @date_type, '', '',
+ ],
+ 'primary_key' => 'prefnum',
+ 'unique' => [],
+ 'index' => [ [ 'usernum' ] ],
+ },
+
+ 'access_group' => {
+ 'columns' => [
+ 'groupnum', 'serial', '', '', '', '',
+ 'groupname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'groupnum',
+ 'unique' => [ [ 'groupname' ] ],
+ 'index' => [],
+ },
+
+ 'access_usergroup' => {
+ 'columns' => [
+ 'usergroupnum', 'serial', '', '', '', '',
+ 'usernum', 'int', '', '', '', '',
+ 'groupnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'usergroupnum',
+ 'unique' => [ [ 'usernum', 'groupnum' ] ],
+ 'index' => [ [ 'usernum' ] ],
+ },
+
+ 'access_groupagent' => {
+ 'columns' => [
+ 'groupagentnum', 'serial', '', '', '', '',
+ 'groupnum', 'int', '', '', '', '',
+ 'agentnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'groupagentnum',
+ 'unique' => [ [ 'groupnum', 'agentnum' ] ],
+ 'index' => [ [ 'groupnum' ] ],
+ },
+
+ 'access_right' => {
+ 'columns' => [
+ 'rightnum', 'serial', '', '', '', '',
+ 'righttype', 'varchar', '', $char_d, '', '',
+ 'rightobjnum', 'int', '', '', '', '',
+ 'rightname', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'rightnum',
+ 'unique' => [ [ 'righttype', 'rightobjnum', 'rightname' ] ],
+ 'index' => [],
+ },
+
+ 'svc_phone' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '', '', '',
+ 'countrycode', 'varchar', '', 3, '', '',
+ 'phonenum', 'varchar', '', 15, '', '', #12 ?
+ 'pin', 'varchar', 'NULL', $char_d, '', '',
+ 'sip_password', 'varchar', 'NULL', $char_d, '', '',
+ 'phone_name', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [ [ 'countrycode', 'phonenum' ] ],
+ },
+
+ 'phone_avail' => {
+ 'columns' => [
+ 'availnum', 'serial', '', '', '', '',
+ 'exportnum', 'int', '', '', '', '',
+ 'countrycode', 'varchar', '', 3, '', '',
+ 'state', 'char', 'NULL', 2, '', '',
+ 'npa', 'char', '', 3, '', '',
+ 'nxx', 'char', 'NULL', 3, '', '',
+ 'station', 'char', 'NULL', 4, '', '',
+ 'name', 'varchar', 'NULL', $char_d, '', '',
+ 'svcnum', 'int', 'NULL', '', '', '',
+ 'availbatch', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'availnum',
+ 'unique' => [],
+ 'index' => [ [ 'exportnum', 'countrycode', 'state' ], #npa search
+ [ 'exportnum', 'countrycode', 'npa' ], #nxx search
+ [ 'exportnum', 'countrycode', 'npa', 'nxx' ],#station search
+ [ 'exportnum', 'countrycode', 'npa', 'nxx', 'station' ], # #
+ [ 'svcnum' ],
+ [ 'availbatch' ],
+ ],
+ },
+
+ 'reason_type' => {
+ 'columns' => [
+ 'typenum', 'serial', '', '', '', '',
+ 'class', 'char', '', 1, '', '',
+ 'type', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'typenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'reason' => {
+ 'columns' => [
+ 'reasonnum', 'serial', '', '', '', '',
+ 'reason_type', 'int', '', '', '', '',
+ 'reason', 'text', '', '', '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'reasonnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'conf' => {
+ 'columns' => [
+ 'confnum', 'serial', '', '', '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ 'name', 'varchar', '', $char_d, '', '',
+ 'value', 'text', 'NULL', '', '', '',
+ ],
+ 'primary_key' => 'confnum',
+ 'unique' => [ [ 'agentnum', 'name' ]],
+ 'index' => [],
+ },
+
+ 'pkg_referral' => {
+ 'columns' => [
+ 'pkgrefnum', 'serial', '', '', '', '',
+ 'pkgnum', 'int', '', '', '', '',
+ 'refnum', 'int', '', '', '', '',
+ ],
+ 'primary_key' => 'pkgrefnum',
+ 'unique' => [ [ 'pkgnum', 'refnum' ] ],
+ 'index' => [ [ 'pkgnum' ], [ 'refnum' ] ],
+ },
+ # name type nullability length default local
+
+ #'new_table' => {
+ # 'columns' => [
+ # 'num', 'serial', '', '', '', '',
+ # ],
+ # 'primary_key' => 'num',
+ # 'unique' => [],
+ # 'index' => [],
+ #},
+
+ };
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<DBIx::DBSchema>
+
+=cut
+
+1;
+
diff --git a/FS/FS/SearchCache.pm b/FS/FS/SearchCache.pm
new file mode 100644
index 0000000..4218acf
--- /dev/null
+++ b/FS/FS/SearchCache.pm
@@ -0,0 +1,96 @@
+package FS::SearchCache;
+
+use strict;
+use vars qw($DEBUG);
+#use Carp qw(carp cluck croak confess);
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::SearchCache - cache
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=over 4
+
+=item new
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my( $table, $key ) = @_;
+ warn "table $table\n" if $DEBUG > 1;
+ warn "key $key\n" if $DEBUG > 1;
+ my $self = { 'table' => $table,
+ 'key' => $key,
+ 'cache' => {},
+ 'subcache' => {},
+ };
+ bless ($self, $class);
+
+ $self;
+}
+
+=item table
+
+=cut
+
+sub table { my $self = shift; $self->{table}; }
+
+=item key
+
+=cut
+
+sub key { my $self = shift; $self->{key}; }
+
+=item cache
+
+=cut
+
+sub cache { my $self = shift; $self->{cache}; }
+
+=item subcache
+
+=cut
+
+sub subcache {
+ my $self = shift;
+ my $col = shift;
+ my $table = shift;
+ my $keyval = shift;
+ if ( exists $self->{subcache}->{$col}->{$keyval} ) {
+ warn "returning existing subcache for $keyval ($col)".
+ "$self->{subcache}->{$col}->{$keyval}\n" if $DEBUG;
+ return $self->{subcache}->{$col}->{$keyval};
+ } else {
+ #my $tablekey = @_ ? shift : $col;
+ my $tablekey = $col;
+ my $subcache = ref($self)->new( $table, $tablekey );
+ $self->{subcache}->{$col}->{$keyval} = $subcache;
+ warn "creating new subcache $table $tablekey: $subcache\n" if $DEBUG;
+ $subcache;
+ }
+}
+
+=back
+
+=head1 BUGS
+
+Dismal documentation.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_main>
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/Setup.pm b/FS/FS/Setup.pm
new file mode 100644
index 0000000..cba3c7e
--- /dev/null
+++ b/FS/FS/Setup.pm
@@ -0,0 +1,541 @@
+package FS::Setup;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK );
+use Exporter;
+#use Tie::DxHash;
+use Tie::IxHash;
+use FS::UID qw( dbh driver_name );
+use FS::Record;
+
+use FS::svc_domain;
+$FS::svc_domain::whois_hack = 1;
+$FS::svc_domain::whois_hack = 1;
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( create_initial_data );
+
+=head1 NAME
+
+FS::Setup - Database setup
+
+=head1 SYNOPSIS
+
+ use FS::Setup;
+
+=head1 DESCRIPTION
+
+Currently this module simply provides a place to store common subroutines for
+database setup.
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item
+
+=cut
+
+sub create_initial_data {
+ my %opt = @_;
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ $FS::UID::AutoCommit = 0;
+
+ populate_locales();
+
+ populate_duplock();
+
+ #initial_data data
+ populate_initial_data(%opt);
+
+ populate_access();
+
+ populate_msgcat();
+
+ if ( $oldAutoCommit ) {
+ dbh->commit or die dbh->errstr;
+ }
+
+}
+
+sub populate_locales {
+
+ use Locale::Country;
+ use FS::cust_main_county;
+
+ #cust_main_county
+ foreach my $country ( sort map uc($_), all_country_codes ) {
+ _add_country($country);
+ }
+
+}
+
+sub populate_addl_locales {
+
+ my %addl = (
+ 'US' => {
+ 'FM' => 'Federated States of Micronesia',
+ 'MH' => 'Marshall Islands',
+ 'PW' => 'Palau',
+ 'AA' => "Armed Forces Americas (except Canada)",
+ 'AE' => "Armed Forces Europe / Canada / Middle East / Africa",
+ 'AP' => "Armed Forces Pacific",
+ },
+ );
+
+ foreach my $country ( keys %addl ) {
+ foreach my $state ( keys %{ $addl{$country} } ) {
+ # $longname = $addl{$country}{$state};
+ _add_locale( 'country'=>$country, 'state'=>$state);
+ }
+ }
+
+}
+
+sub _add_country {
+
+ use Locale::SubCountry;
+
+ my( $country ) = shift;
+
+ my $subcountry = eval { new Locale::SubCountry($country) };
+ my @states = $subcountry ? $subcountry->all_codes : undef;
+
+ if ( !scalar(@states) || ( scalar(@states)==1 && !defined($states[0]) ) ) {
+
+ _add_locale( 'country'=>$country );
+
+ } else {
+
+ if ( $states[0] =~ /^(\d+|\w)$/ ) {
+ @states = map $subcountry->full_name($_), @states
+ }
+
+ foreach my $state ( @states ) {
+ _add_locale( 'country'=>$country, 'state'=>$state);
+ }
+
+ }
+
+}
+
+sub _add_locale {
+ my $cust_main_county = new FS::cust_main_county( { 'tax'=>0, @_ });
+ my $error = $cust_main_county->insert;
+ die $error if $error;
+}
+
+sub populate_duplock {
+
+ return unless driver_name =~ /^mysql/i;
+
+ my $sth = dbh->prepare(
+ "INSERT INTO duplicate_lock ( lockname ) VALUES ( 'svc_acct' )"
+ ) or die dbh->errstr;
+
+ $sth->execute or die $sth->errstr;
+
+}
+
+sub populate_initial_data {
+ my %opt = @_;
+
+ my $data = initial_data(%opt);
+
+ foreach my $table ( keys %$data ) {
+
+ #warn "popuilating $table\n";
+
+ my $class = "FS::$table";
+ eval "use $class;";
+ die $@ if $@;
+
+ $class->_populate_initial_data(%opt)
+ if $class->can('_populate_inital_data');
+
+ my @records = @{ $data->{$table} };
+
+ foreach my $record ( @records ) {
+
+ my $args = delete($record->{'_insert_args'}) || [];
+ my $object = $class->new( $record );
+ my $error = $object->insert( @$args );
+ die "error inserting record into $table: $error\n"
+ if $error;
+
+ #my $pkey = $object->primary_key;
+ #my $pkeyvalue = $object->$pkey();
+ #warn " inserted $pkeyvalue\n";
+
+ }
+
+ }
+
+}
+
+sub initial_data {
+ my %opt = @_;
+
+ #tie my %hash, 'Tie::DxHash',
+ tie my %hash, 'Tie::IxHash',
+
+ #superuser group
+ 'access_group' => [
+ { 'groupname' => 'Superuser' },
+ ],
+
+ #reason types
+ 'reason_type' => [],
+
+#XXX need default new-style billing events
+# #billing events
+# 'part_bill_event' => [
+# { 'payby' => 'CARD',
+# 'event' => 'Batch card',
+# 'seconds' => 0,
+# 'eventcode' => '$cust_bill->batch_card(%options);',
+# 'weight' => 40,
+# 'plan' => 'batch-card',
+# },
+# { 'payby' => 'BILL',
+# 'event' => 'Send invoice',
+# 'seconds' => 0,
+# 'eventcode' => '$cust_bill->send();',
+# 'weight' => 50,
+# 'plan' => 'send',
+# },
+# { 'payby' => 'DCRD',
+# 'event' => 'Send invoice',
+# 'seconds' => 0,
+# 'eventcode' => '$cust_bill->send();',
+# 'weight' => 50,
+# 'plan' => 'send',
+# },
+# { 'payby' => 'DCHK',
+# 'event' => 'Send invoice',
+# 'seconds' => 0,
+# 'eventcode' => '$cust_bill->send();',
+# 'weight' => 50,
+# 'plan' => 'send',
+# },
+# { 'payby' => 'DCLN',
+# 'event' => 'Suspend',
+# 'seconds' => 0,
+# 'eventcode' => '$cust_bill->suspend();',
+# 'weight' => 40,
+# 'plan' => 'suspend',
+# },
+# #{ 'payby' => 'DCLN',
+# # 'event' => 'Retriable',
+# # 'seconds' => 0,
+# # 'eventcode' => '$cust_bill_event->retriable();',
+# # 'weight' => 60,
+# # 'plan' => 'retriable',
+# #},
+# ],
+
+ #you must create a service definition. An example of a service definition
+ #would be a dial-up account or a domain. First, it is necessary to create a
+ #domain definition. Click on View/Edit service definitions and Add a new
+ #service definition with Table svc_domain (and no modifiers).
+ 'part_svc' => [
+ { 'svc' => 'Domain',
+ 'svcdb' => 'svc_domain',
+ }
+ ],
+
+ #Now that you have created your first service, you must create a package
+ #including this service which you can sell to customers. Zero, one, or many
+ #services are bundled into a package. Click on View/Edit package
+ #definitions and Add a new package definition which includes quantity 1 of
+ #the svc_domain service you created above.
+ 'part_pkg' => [
+ { 'pkg' => 'System Domain',
+ 'comment' => '(NOT FOR CUSTOMERS)',
+ 'freq' => '0',
+ 'plan' => 'flat',
+ '_insert_args' => [
+ 'pkg_svc' => { 1 => 1 }, # XXX
+ 'primary_svc' => 1, #XXX
+ 'options' => {
+ 'setup_fee' => '0',
+ 'recur_fee' => '0',
+ },
+ ],
+ },
+ ],
+
+ #After you create your first package, then you must define who is able to
+ #sell that package by creating an agent type. An example of an agent type
+ #would be an internal sales representitive which sells regular and
+ #promotional packages, as opposed to an external sales representitive
+ #which would only sell regular packages of services. Click on View/Edit
+ #agent types and Add a new agent type.
+ 'agent_type' => [
+ { 'atype' => 'Internal' },
+ ],
+
+ #Allow this agent type to sell the package you created above.
+ 'type_pkgs' => [
+ { 'typenum' => 1, #XXX
+ 'pkgpart' => 1, #XXX
+ },
+ ],
+
+ #After creating a new agent type, you must create an agent. Click on
+ #View/Edit agents and Add a new agent.
+ 'agent' => [
+ { 'agent' => 'Internal',
+ 'typenum' => 1, # XXX
+ },
+ ],
+
+ #Set up at least one Advertising source. Advertising sources will help you
+ #keep track of how effective your advertising is, tracking where customers
+ #heard of your service offerings. You must create at least one advertising
+ #source. If you do not wish to use the referral functionality, simply
+ #create a single advertising source only. Click on View/Edit advertising
+ #sources and Add a new advertising source.
+ 'part_referral' => [
+ { 'referral' => 'Internal', },
+ ],
+
+ #Click on New Customer and create a new customer for your system accounts
+ #with billing type Complimentary. Leave the First package dropdown set to
+ #(none).
+ 'cust_main' => [
+ { 'agentnum' => 1, #XXX
+ 'refnum' => 1, #XXX
+ 'first' => 'System',
+ 'last' => 'Accounts',
+ 'address1' => '1234 System Lane',
+ 'city' => 'Systemtown',
+ 'state' => 'CA',
+ 'zip' => '54321',
+ 'country' => 'US',
+ 'payby' => 'COMP',
+ 'payinfo' => 'system', #or something
+ 'paydate' => '1/2037',
+ },
+ ],
+
+ #From the Customer View screen of the newly created customer, order the
+ #package you defined above.
+ 'cust_pkg' => [
+ { 'custnum' => 1, #XXX
+ 'pkgpart' => 1, #XXX
+ },
+ ],
+
+ #From the Package View screen of the newly created package, choose
+ #(Provision) to add the customer's service for this new package.
+ #Add your own domain.
+ 'svc_domain' => [
+ { 'domain' => $opt{'domain'},
+ 'pkgnum' => 1, #XXX
+ 'svcpart' => 1, #XXX
+ 'action' => 'N', #pseudo-field
+ },
+ ],
+
+ #Go back to View/Edit service definitions on the main menu, and Add a new
+ #service definition with Table svc_acct. Select your domain in the domsvc
+ #Modifier. Set Fixed to define a service locked-in to this domain, or
+ #Default to define a service which may select from among this domain and
+ #the customer's domains.
+
+ #not yet....
+
+ #)
+
+ #usage classes
+ 'usage_class' => [],
+
+ ;
+
+ \%hash;
+
+}
+
+sub populate_access {
+
+ use FS::AccessRight;
+ use FS::access_right;
+
+ foreach my $rightname ( FS::AccessRight->rights ) {
+ my $access_right = new FS::access_right {
+ 'righttype' => 'FS::access_group',
+ 'rightobjnum' => 1, #$supergroup->groupnum,
+ 'rightname' => $rightname,
+ };
+ my $ar_error = $access_right->insert;
+ die $ar_error if $ar_error;
+ }
+
+ #foreach my $agent ( qsearch('agent', {} ) ) {
+ my $access_groupagent = new FS::access_groupagent {
+ 'groupnum' => 1, #$supergroup->groupnum,
+ 'agentnum' => 1, #$agent->agentnum,
+ };
+ my $aga_error = $access_groupagent->insert;
+ die $aga_error if $aga_error;
+ #}
+
+}
+
+sub populate_msgcat {
+
+ use FS::Record qw(qsearch);
+ use FS::msgcat;
+
+ foreach my $del_msgcat ( qsearch('msgcat', {}) ) {
+ my $error = $del_msgcat->delete;
+ die $error if $error;
+ }
+
+ my %messages = msgcat_messages();
+
+ foreach my $msgcode ( keys %messages ) {
+ foreach my $locale ( keys %{$messages{$msgcode}} ) {
+ my $msgcat = new FS::msgcat( {
+ 'msgcode' => $msgcode,
+ 'locale' => $locale,
+ 'msg' => $messages{$msgcode}{$locale},
+ });
+ my $error = $msgcat->insert;
+ die $error if $error;
+ }
+ }
+
+}
+
+sub msgcat_messages {
+
+ # 'msgcode' => {
+ # 'en_US' => 'Message',
+ # },
+
+ (
+
+ 'passwords_dont_match' => {
+ 'en_US' => "Passwords don't match",
+ },
+
+ 'invalid_card' => {
+ 'en_US' => 'Invalid credit card number',
+ },
+
+ 'unknown_card_type' => {
+ 'en_US' => 'Unknown card type',
+ },
+
+ 'not_a' => {
+ 'en_US' => 'Not a ',
+ },
+
+ 'empty_password' => {
+ 'en_US' => 'Empty password',
+ },
+
+ 'no_access_number_selected' => {
+ 'en_US' => 'No access number selected',
+ },
+
+ 'illegal_text' => {
+ 'en_US' => 'Illegal (text)',
+ #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in field',
+ },
+
+ 'illegal_or_empty_text' => {
+ 'en_US' => 'Illegal or empty (text)',
+ #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in required field',
+ },
+
+ 'illegal_username' => {
+ 'en_US' => 'Illegal username',
+ },
+
+ 'illegal_password' => {
+ 'en_US' => 'Illegal password (',
+ },
+
+ 'illegal_password_characters' => {
+ 'en_US' => ' characters)',
+ },
+
+ 'username_in_use' => {
+ 'en_US' => 'Username in use',
+ },
+
+ 'phonenum_in_use' => {
+ 'en_US' => 'Phone number in use',
+ },
+
+ 'illegal_email_invoice_address' => {
+ 'en_US' => 'Illegal email invoice address',
+ },
+
+ 'illegal_name' => {
+ 'en_US' => 'Illegal (name)',
+ #'en_US' => 'Only letters, numbers, spaces and the following punctuation symbols are permitted: , . - \' in field',
+ },
+
+ 'illegal_phone' => {
+ 'en_US' => 'Illegal (phone)',
+ #'en_US' => '',
+ },
+
+ 'illegal_zip' => {
+ 'en_US' => 'Illegal (zip)',
+ #'en_US' => '',
+ },
+
+ 'expired_card' => {
+ 'en_US' => 'Expired card',
+ },
+
+ 'daytime' => {
+ 'en_US' => 'Day Phone',
+ },
+
+ 'night' => {
+ 'en_US' => 'Night Phone',
+ },
+
+ 'svc_external-id' => {
+ 'en_US' => 'External ID',
+ },
+
+ 'svc_external-title' => {
+ 'en_US' => 'Title',
+ },
+
+ 'stateid' => {
+ 'en_US' => 'Driver\'s License',
+ },
+
+ 'stateid_state' => {
+ 'en_US' => 'Driver\'s License State',
+ },
+
+ 'invalid_domain' => {
+ 'en_US' => 'Invalid domain',
+ },
+
+ );
+}
+
+=back
+
+=head1 BUGS
+
+Sure.
+
+=head1 SEE ALSO
+
+=cut
+
+1;
+
diff --git a/FS/FS/TicketSystem.pm b/FS/FS/TicketSystem.pm
new file mode 100644
index 0000000..a80a827
--- /dev/null
+++ b/FS/FS/TicketSystem.pm
@@ -0,0 +1,30 @@
+package FS::TicketSystem;
+
+use strict;
+use vars qw( $conf $system $AUTOLOAD );
+use FS::Conf;
+use FS::UID;
+
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+ $system = $conf->config('ticket_system');
+} );
+
+sub AUTOLOAD {
+ my $self = shift;
+
+ my($sub)=$AUTOLOAD;
+ $sub =~ s/.*://;
+
+ my $conf = new FS::Conf;
+ die "FS::TicketSystem::$AUTOLOAD called, but no ticket system configured\n"
+ unless $system;
+
+ eval "use FS::TicketSystem::$system;";
+ die $@ if $@;
+
+ $self .= "::$system";
+ $self->$sub(@_);
+}
+
+1;
diff --git a/FS/FS/TicketSystem/RT_External.pm b/FS/FS/TicketSystem/RT_External.pm
new file mode 100644
index 0000000..3a9c7e8
--- /dev/null
+++ b/FS/FS/TicketSystem/RT_External.pm
@@ -0,0 +1,353 @@
+package FS::TicketSystem::RT_External;
+
+use strict;
+use vars qw( $DEBUG $me $conf $dbh $default_queueid $external_url
+ $priority_reverse
+ $priority_field $priority_field_queue $field
+ );
+use URI::Escape;
+use FS::UID qw(dbh);
+use FS::Record qw(qsearchs);
+use FS::cust_main;
+
+$me = '[FS::TicketSystem::RT_External]';
+$DEBUG = 0;
+
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+ $default_queueid = $conf->config('ticket_system-default_queueid');
+ $priority_reverse = $conf->exists('ticket_system-priority_reverse');
+ $priority_field =
+ $conf->config('ticket_system-custom_priority_field');
+ if ( $priority_field ) {
+ $priority_field_queue =
+ $conf->config('ticket_system-custom_priority_field_queue');
+
+ $field = $priority_field_queue
+ ? $priority_field_queue. '.%7B'. $priority_field. '%7D'
+ : $priority_field;
+ } else {
+ $priority_field_queue = '';
+ $field = '';
+ }
+
+ $external_url = '';
+ $dbh = dbh;
+ if ($conf->config('ticket_system') eq 'RT_External') {
+ my ($datasrc, $user, $pass) = $conf->config('ticket_system-rt_external_datasrc');
+ $dbh = DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 })
+ or die "RT_External DBI->connect error: $DBI::errstr\n";
+
+ $external_url = $conf->config('ticket_system-rt_external_url');
+ }
+
+ #kludge... should *use* the id... but good enough for now
+ if ( $priority_field_queue =~ /^(\d+)$/ ) {
+ my $id = $1;
+ my $sql = 'SELECT Name FROM Queues WHERE Id = ?';
+ my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
+ $sth->execute($id) or die $sth->errstr. " executing $sql";
+
+ $priority_field_queue = $sth->fetchrow_arrayref->[0];
+
+ }
+
+} );
+
+sub num_customer_tickets {
+ my( $self, $custnum, $priority ) = @_;
+
+ my( $from_sql, @param) = $self->_from_customer( $custnum, $priority );
+
+ my $sql = "SELECT COUNT(*) $from_sql";
+ warn "$me $sql (@param)" if $DEBUG;
+ my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
+ $sth->execute(@param) or die $sth->errstr. " executing $sql";
+
+ $sth->fetchrow_arrayref->[0];
+
+}
+
+sub customer_tickets {
+ my( $self, $custnum, $limit, $priority ) = @_;
+ $limit ||= 0;
+
+ my( $from_sql, @param) = $self->_from_customer( $custnum, $priority );
+ my $sql = "
+ SELECT Tickets.*,
+ Queues.Name AS Queue,
+ Users.Name AS Owner,
+ position(Tickets.Status in 'newopenstalledresolvedrejecteddeleted')
+ AS svalue
+ ". ( length($priority) ? ", ObjectCustomFieldValues.Content" : '' )."
+ $from_sql
+ ORDER BY svalue,
+ Priority ". ( $priority_reverse ? 'ASC' : 'DESC' ). ",
+ id DESC
+ LIMIT $limit
+ ";
+ warn "$me $sql (@param)" if $DEBUG;
+ my $sth = $dbh->prepare($sql) or die $dbh->errstr. "preparing $sql";
+ $sth->execute(@param) or die $sth->errstr. "executing $sql";
+
+ #munge column names??? #httemplate/view/cust_main/tickets.html has column
+ #names that might not make sense now...
+ $sth->fetchall_arrayref({});
+
+}
+
+sub _from_customer {
+ my( $self, $custnum, $priority ) = @_;
+
+ my @param = ();
+ my $join = '';
+ my $where = '';
+ if ( defined($priority) ) {
+
+ my $queue_sql = " ObjectCustomFields.ObjectId = ( SELECT id FROM Queues
+ WHERE Queues.Name = ? )
+ OR ( ? = '' AND ObjectCustomFields.ObjectId = 0 )";
+
+ my $customfield_sql =
+ "customfield = (
+ SELECT CustomFields.Id FROM CustomFields
+ JOIN ObjectCustomFields
+ ON ( CustomFields.id = ObjectCustomFields.CustomField )
+ WHERE LookupType = 'RT::Queue-RT::Ticket'
+ AND Name = ?
+ AND ( $queue_sql )
+ )";
+
+ push @param, $priority_field,
+ $priority_field_queue,
+ $priority_field_queue;
+
+ if ( length($priority) ) {
+ #$where = "
+ # and ? = ( select content from TicketCustomFieldValues
+ # where ticket = tickets.id
+ # and customfield = ( select id from customfields
+ # where name = ?
+ # and ( $queue_sql )
+ # )
+ # )
+ #";
+ unshift @param, $priority;
+
+ $join = "JOIN ObjectCustomFieldValues
+ ON ( Tickets.id = ObjectCustomFieldValues.ObjectId )";
+
+ $where = " AND Content = ?
+ AND ObjectCustomFieldValues.Disabled != 1
+ AND ObjectType = 'RT::Ticket'
+ AND $customfield_sql";
+
+ } else {
+
+ $where =
+ "AND 0 = ( SELECT COUNT(*) FROM ObjectCustomFieldValues
+ WHERE ObjectId = Tickets.id
+ AND ObjectType = 'RT::Ticket'
+ AND $customfield_sql
+ )
+ ";
+ }
+
+ }
+
+ my $sql = "
+ FROM Tickets
+ JOIN Queues ON ( Tickets.Queue = Queues.id )
+ JOIN Links ON ( Tickets.id = Links.LocalBase )
+ JOIN Users ON ( Tickets.Owner = Users.id )
+ $join
+ WHERE ( ". join(' OR ', map "Status = '$_'", $self->statuses ). " )
+ AND Target = 'freeside://freeside/cust_main/$custnum'
+ $where
+ ";
+
+ ( $sql, @param );
+
+}
+
+sub statuses {
+ #my $self = shift;
+ my @statuses = grep { ! /^\s*$/ } $conf->config('cust_main-ticket_statuses');
+ @statuses = (qw( new open stalled )) unless scalar(@statuses);
+ @statuses;
+}
+
+sub href_customer_tickets {
+ my( $self, $custnum ) = ( shift, shift );
+ my( $priority, @statuses);
+ if ( ref($_[0]) ) {
+ my $opt = shift;
+ $priority = $opt->{'priority'};
+ @statuses = $opt->{'statuses'} ? @{$opt->{'statuses'}} : $self->statuses;
+ } else {
+ $priority = shift;
+ @statuses = $self->statuses;
+ }
+
+ #my $href = $self->baseurl;
+
+ #i snarfed this from an RT bookmarked search, then unescaped (some of) it with
+ #perl -npe 's/%([0-9A-F]{2})/pack('C', hex($1))/eg;'
+
+ #$href .=
+ my $href =
+ "Search/Results.html?Order=ASC&".
+ "Query= MemberOf = 'freeside://freeside/cust_main/$custnum' ".
+ #" AND ( Status = 'open' OR Status = 'new' OR Status = 'stalled' )"
+ " AND ( ". join(' OR ', map "Status = '$_'", @statuses ). " ) "
+ ;
+
+ if ( defined($priority) && $field && $priority_field_queue ) {
+ $href .= " AND Queue = '$priority_field_queue' ";
+ }
+ if ( defined($priority) && $field ) {
+ $href .= " AND 'CF.$field' ";
+ if ( $priority ) {
+ $href .= "= '$priority' ";
+ } else {
+ $href .= "IS 'NULL' "; #this is "RTQL", not SQL
+ }
+ }
+
+ #$href =
+ uri_escape($href);
+ #eventually should unescape all of it...
+
+ $href .= '&Rows=100'.
+ '&OrderBy=id&Page=1'.
+ '&Format=%27%20%20%20%3Cb%3E%3Ca%20href%3D%22'.
+ $self->baseurl.
+ 'Ticket%2FDisplay.html%3Fid%3D__id__%22%3E__id__%3C%2Fa%3E%3C%2Fb%3E%2FTITLE%3A%23%27%2C%20%0A%27%3Cb%3E%3Ca%20href%3D%22'.
+ $self->baseurl.
+ 'Ticket%2FDisplay.html%3Fid%3D__id__%22%3E__Subject__%3C%2Fa%3E%3C%2Fb%3E%2FTITLE%3ASubject%27%2C%20%0A%27__Status__%27%2C%20';
+
+ if ( defined($priority) && $field ) {
+ $href .= '%0A%27__CustomField.'. $field. '__%2FTITLE%3ASeverity%27%2C%20';
+ }
+
+ $href .= '%0A%27__QueueName__%27%2C%20%0A%27__OwnerName__%27%2C%20%0A%27__Priority__%27%2C%20%0A%27__NEWLINE__%27%2C%20%0A%27%27%2C%20%0A%27%3Csmall%3E__Requestors__%3C%2Fsmall%3E%27%2C%20%0A%27%3Csmall%3E__CreatedRelative__%3C%2Fsmall%3E%27%2C';
+
+ if ( defined($priority) && $field ) {
+ $href .= '%20%0A%27__-__%27%2C';
+ }
+
+ $href .= '%20%0A%27%3Csmall%3E__ToldRelative__%3C%2Fsmall%3E%27%2C%20%0A%27%3Csmall%3E__LastUpdatedRelative__%3C%2Fsmall%3E%27%2C%20%0A%27%3Csmall%3E__TimeLeft__%3C%2Fsmall%3E%27';
+
+ #$href =
+ #uri_escape($href);
+
+ $self->baseurl. $href;
+
+}
+
+sub href_new_ticket {
+ my( $self, $custnum_or_cust_main, $requestors ) = @_;
+
+ my( $custnum, $cust_main );
+ if ( ref($custnum_or_cust_main) ) {
+ $cust_main = $custnum_or_cust_main;
+ $custnum = $cust_main->custnum;
+ } else {
+ $custnum = $custnum_or_cust_main;
+ $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+ }
+ my $queueid = $cust_main->agent->ticketing_queueid || $default_queueid;
+
+ $self->baseurl.
+ 'Ticket/Create.html?'.
+ "Queue=$queueid".
+ "&new-MemberOf=freeside://freeside/cust_main/$custnum".
+ ( $requestors ? '&Requestors='. uri_escape($requestors) : '' )
+ ;
+}
+
+sub href_ticket {
+ my($self, $ticketnum) = @_;
+ $self->baseurl. 'Ticket/Display.html?id='.$ticketnum;
+}
+
+sub queues {
+ my($self) = @_;
+
+ my $sql = "SELECT id, Name FROM Queues WHERE Disabled = 0";
+ my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
+ $sth->execute() or die $sth->errstr. " executing $sql";
+
+ map { $_->[0] => $_->[1] } @{ $sth->fetchall_arrayref([]) };
+
+}
+
+sub queue {
+ my($self, $queueid) = @_;
+
+ return '' unless $queueid;
+
+ my $sql = "SELECT Name FROM Queues WHERE id = ?";
+ my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
+ $sth->execute($queueid) or die $sth->errstr. " executing $sql";
+
+ my $rows = $sth->fetchrow_arrayref;
+ $rows ? $rows->[0] : '';
+
+}
+
+sub baseurl {
+ #my $self = shift;
+ $external_url. '/';
+}
+
+sub _retrieve_single_value {
+ my( $self, $sql ) = @_;
+
+ warn "$me $sql" if $DEBUG;
+ my $sth = $dbh->prepare($sql) or die $dbh->errstr. "preparing $sql";
+ $sth->execute or die $sth->errstr. "executing $sql";
+
+ my $arrayref = $sth->fetchrow_arrayref;
+ $arrayref ? $arrayref->[0] : $arrayref;
+}
+
+sub transaction_creator {
+ my( $self, $transaction_id ) = @_;
+
+ my $sql = "SELECT Name FROM Transactions JOIN Users ON ".
+ "Transactions.Creator=Users.id WHERE Transactions.id = ".
+ $transaction_id;
+
+ $self->_retrieve_single_value($sql);
+}
+
+sub transaction_ticketid {
+ my( $self, $transaction_id ) = @_;
+
+ my $sql = "SELECT ObjectId FROM Transactions WHERE Transactions.id = ".
+ $transaction_id;
+
+ $self->_retrieve_single_value($sql);
+}
+
+sub transaction_subject {
+ my( $self, $transaction_id ) = @_;
+
+ my $sql = "SELECT Subject FROM Transactions JOIN Tickets ON ObjectId=".
+ "Tickets.id WHERE Transactions.id = ". $transaction_id;
+
+ $self->_retrieve_single_value($sql);
+}
+
+sub transaction_status {
+ my( $self, $transaction_id ) = @_;
+
+ my $sql = "SELECT Status FROM Transactions JOIN Tickets ON ObjectId=".
+ "Tickets.id WHERE Transactions.id = ". $transaction_id;
+
+ $self->_retrieve_single_value($sql);
+}
+
+1;
+
diff --git a/FS/FS/TicketSystem/RT_Internal.pm b/FS/FS/TicketSystem/RT_Internal.pm
new file mode 100644
index 0000000..d24a96c
--- /dev/null
+++ b/FS/FS/TicketSystem/RT_Internal.pm
@@ -0,0 +1,29 @@
+package FS::TicketSystem::RT_Internal;
+
+use strict;
+use vars qw( @ISA );
+use FS::UID qw(dbh);
+use FS::CGI qw(popurl);
+use FS::TicketSystem::RT_Libs;
+
+@ISA = qw( FS::TicketSystem::RT_Libs );
+
+sub sql_num_customer_tickets {
+ "( select count(*) from tickets
+ join links on ( tickets.id = links.localbase )
+ where ( status = 'new' or status = 'open' or status = 'stalled' )
+ and target = 'freeside://freeside/cust_main/' || custnum
+ )";
+}
+
+sub baseurl {
+ #my $self = shift;
+ if ( $RT::URI::freeside::URL ) {
+ $RT::URI::freeside::URL. '/rt/';
+ } else {
+ 'http://you_need_to_set_RT_URI_freeside_URL_in_SiteConfig.pm/';
+ }
+}
+
+1;
+
diff --git a/FS/FS/TicketSystem/RT_Libs.pm b/FS/FS/TicketSystem/RT_Libs.pm
new file mode 100644
index 0000000..aebe8c5
--- /dev/null
+++ b/FS/FS/TicketSystem/RT_Libs.pm
@@ -0,0 +1,10 @@
+package FS::TicketSystem::RT_Libs;
+
+use strict;
+use vars qw( @ISA );
+use FS::TicketSystem::RT_External;
+
+@ISA = qw( FS::TicketSystem::RT_External );
+
+1;
+
diff --git a/FS/FS/Tron.pm b/FS/FS/Tron.pm
new file mode 100644
index 0000000..26ab639
--- /dev/null
+++ b/FS/FS/Tron.pm
@@ -0,0 +1,99 @@
+package FS::Tron;
+# a program to monitor outside systems
+
+use strict;
+use warnings;
+use base 'Exporter';
+use Net::SSH qw( sshopen2 ); #sshopen3 );
+use FS::Record qw( qsearchs );
+use FS::svc_external;
+use FS::cust_svc_option;
+
+our @EXPORT_OK = qw( tron_scan tron_lint);
+
+our %desired = (
+ #lenient for now, so we can fix up important stuff
+ 'freeside_version' => qr/^1\.(7\.3|9\.0)/,
+ 'debian_version' => qr/^4/,
+ 'apache_mpm' => qw/^(Prefork|$)/,
+
+ #payment gateway survey
+# 'payment_gateway' => qw/^authorizenet$/,
+
+ #stuff to add/replace later
+ #'pg_version' => qr/^8\.[1-9]/,
+ #'apache_version' => qr/^2/,
+ #'apache_mpm' => qw/^Prefork/,
+);
+
+sub tron_scan {
+ my $cust_svc = shift;
+
+ my $svc_external;
+ if ( ref($cust_svc) ) {
+ $svc_external = $cust_svc->svc_x;
+ } else {
+ $svc_external = qsearchs('svc_external', { 'svcnum' => $cust_svc } );
+ $cust_svc = $svc_external->cust_svc;
+ }
+
+ #don't scan again if things are okay
+ my $bad = 0;
+ foreach my $option ( keys %desired ) {
+ my $current = $cust_svc->option($option);
+ $bad++ unless $current =~ $desired{$option};
+ }
+ return '' unless $bad;
+
+ #do the scan
+ my %hash = ();
+ my $machine = $svc_external->title; # or better as a cust_svc_option??
+ sshopen2($machine, *READER, *WRITER, '/usr/local/bin/freeside-yori all');
+ while (<READER>) {
+ chomp;
+ my($option, $value) = split(/: ?/);
+ next unless defined($option) && exists($desired{$option});
+ $hash{$option} = $value;
+ }
+ close READER;
+ close WRITER;
+
+ unless ( keys %hash ) {
+ return "error scanning $machine\n";
+ }
+
+ # store the results
+ foreach my $option ( keys %hash ) {
+ my %opthash = ( 'optionname' => $option,
+ 'svcnum' => $cust_svc->svcnum,
+ );
+ my $cust_svc_option = qsearchs('cust_svc_option', \%opthash )
+ || new FS::cust_svc_option \%opthash;
+ next if $cust_svc_option->optionvalue eq $hash{$option};
+ $cust_svc_option->optionvalue( $hash{$option} );
+ my $error = $cust_svc_option->optionnum
+ ? $cust_svc_option->replace
+ : $cust_svc_option->insert;
+ return $error if $error;
+ }
+
+ '';
+
+}
+
+sub tron_lint {
+ my $cust_svc = shift;
+
+ my @lint;
+ foreach my $option ( keys %desired ) {
+ my $current = $cust_svc->option($option);
+ push @lint, "$option is $current" unless $current =~ $desired{$option};
+ }
+
+ push @lint, 'unchecked' unless scalar($cust_svc->options);
+
+ @lint;
+
+}
+
+1;
diff --git a/FS/FS/UI/Web.pm b/FS/FS/UI/Web.pm
new file mode 100644
index 0000000..3c52ca5
--- /dev/null
+++ b/FS/FS/UI/Web.pm
@@ -0,0 +1,601 @@
+package FS::UI::Web;
+
+use strict;
+use vars qw($DEBUG @ISA @EXPORT_OK $me);
+use Exporter;
+use FS::Conf;
+use FS::Record qw(dbdef);
+use FS::cust_main; # are sql_balance and sql_date_balance in the right module?
+
+#use vars qw(@ISA);
+#use FS::UI
+#@ISA = qw( FS::UI );
+@ISA = qw( Exporter );
+
+@EXPORT_OK = qw( svc_url );
+
+$DEBUG = 0;
+$me = '[FS::UID::Web]';
+
+###
+# date parsing
+###
+
+use Date::Parse;
+sub parse_beginning_ending {
+ my($cgi, $prefix) = @_;
+ $prefix .= '_' if $prefix;
+
+ my $beginning = 0;
+ if ( $cgi->param($prefix.'begin') =~ /^(\d+)$/ ) {
+ $beginning = $1;
+ } elsif ( $cgi->param($prefix.'beginning') =~ /^([ 0-9\-\/]{1,64})$/ ) {
+ $beginning = str2time($1) || 0;
+ }
+
+ my $ending = 4294967295; #2^32-1
+ if ( $cgi->param($prefix.'end') =~ /^(\d+)$/ ) {
+ $ending = $1 - 1;
+ } elsif ( $cgi->param($prefix.'ending') =~ /^([ 0-9\-\/]{1,64})$/ ) {
+ #probably need an option to turn off the + 86399
+ $ending = str2time($1) + 86399;
+ }
+
+ ( $beginning, $ending );
+}
+
+=item svc_url
+
+Returns a service URL, first checking to see if there is a service-specific
+page to link to, otherwise to a generic service handling page. Options are
+passed as a list of name-value pairs, and include:
+
+=over 4
+
+=item * m - Mason request object ($m)
+
+=item * action - The action for which to construct "edit", "view", or "search"
+
+=item ** part_svc - Service definition (see L<FS::part_svc>)
+
+=item ** svcdb - Service table
+
+=item *** query - Query string
+
+=item *** svc - FS::cust_svc or FS::svc_* object
+
+=item ahref - Optional flag, if set true returns <A HREF="$url"> instead of just the URL.
+
+=back
+
+* Required fields
+
+** part_svc OR svcdb is required
+
+*** query OR svc is required
+
+=cut
+
+ # ##
+ # #required
+ # ##
+ # 'm' => $m, #mason request object
+ # 'action' => 'edit', #or 'view'
+ #
+ # 'part_svc' => $part_svc, #usual
+ # #OR
+ # 'svcdb' => 'svc_table',
+ #
+ # 'query' => #optional query string
+ # # (pass a blank string if you want a "raw" URL to add your
+ # # own svcnum to)
+ # #OR
+ # 'svc' => $svc_x, #or $cust_svc, it just needs a svcnum
+ #
+ # ##
+ # #optional
+ # ##
+ # 'ahref' => 1, # if set true, returns <A HREF="$url">
+
+use FS::CGI qw(rooturl);
+sub svc_url {
+ my %opt = @_;
+
+ #? return '' unless ref($opt{part_svc});
+
+ my $svcdb = $opt{svcdb} || $opt{part_svc}->svcdb;
+ my $query = exists($opt{query}) ? $opt{query} : $opt{svc}->svcnum;
+ my $url;
+ warn "$me [svc_url] checking for /$opt{action}/$svcdb.cgi component"
+ if $DEBUG;
+ if ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.cgi") ) {
+ $url = "$svcdb.cgi?";
+ } else {
+
+ my $generic = $opt{action} eq 'search' ? 'cust_svc' : 'svc_Common';
+
+ $url = "$generic.html?svcdb=$svcdb;";
+ $url .= 'svcnum=' if $query =~ /^\d+(;|$)/ or $query eq '';
+ }
+
+ import FS::CGI 'rooturl'; #WTF! why is this necessary
+ my $return = rooturl(). "$opt{action}/$url$query";
+
+ $return = qq!<A HREF="$return">! if $opt{ahref};
+
+ $return;
+}
+
+sub svc_link {
+ my($m, $part_svc, $cust_svc) = @_ or return '';
+ svc_X_link( $part_svc->svc, @_ );
+}
+
+sub svc_label_link {
+ my($m, $part_svc, $cust_svc) = @_ or return '';
+ svc_X_link( ($cust_svc->label)[1], @_ );
+}
+
+sub svc_X_link {
+ my ($x, $m, $part_svc, $cust_svc) = @_ or return '';
+
+ return $x
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+ my $ahref = svc_url(
+ 'ahref' => 1,
+ 'm' => $m,
+ 'action' => 'view',
+ 'part_svc' => $part_svc,
+ 'svc' => $cust_svc,
+ );
+
+ "$ahref$x</A>";
+}
+
+#this probably needs an ACL too...
+sub svc_export_links {
+ my ($m, $part_svc, $cust_svc) = @_ or return '';
+
+ my $ahref = $cust_svc->export_links;
+
+ join('', @$ahref);
+}
+
+sub parse_lt_gt {
+ my($cgi, $field) = @_;
+
+ my @search = ();
+
+ my %op = (
+ 'lt' => '<',
+ 'gt' => '>',
+ );
+
+ foreach my $op (keys %op) {
+
+ warn "checking for ${field}_$op field\n"
+ if $DEBUG;
+
+ if ( $cgi->param($field."_$op") =~ /^\s*\$?\s*(-?[\d\,\s]+(\.\d\d)?)\s*$/ ) {
+
+ my $num = $1;
+ $num =~ s/[\,\s]+//g;
+ my $search = "$field $op{$op} $num";
+ push @search, $search;
+
+ warn "found ${field}_$op field; adding search element $search\n"
+ if $DEBUG;
+ }
+
+ }
+
+ @search;
+
+}
+
+###
+# cust_main report subroutines
+###
+
+
+=item cust_header [ CUST_FIELDS_VALUE ]
+
+Returns an array of customer information headers according to the supplied
+customer fields value, or if no value is supplied, the B<cust-fields>
+configuration value.
+
+=cut
+
+use vars qw( @cust_fields @cust_colors @cust_styles @cust_aligns );
+
+sub cust_header {
+
+ warn "FS::UI:Web::cust_header called"
+ if $DEBUG;
+
+ my $conf = new FS::Conf;
+
+ my %header2method = (
+ 'Customer' => 'name',
+ 'Cust. Status' => 'ucfirst_cust_status',
+ 'Cust#' => 'custnum',
+ 'Name' => 'contact',
+ 'Company' => 'company',
+ '(bill) Customer' => 'name',
+ '(service) Customer' => 'ship_name',
+ '(bill) Name' => 'contact',
+ '(service) Name' => 'ship_contact',
+ '(bill) Company' => 'company',
+ '(service) Company' => 'ship_company',
+ 'Address 1' => 'address1',
+ 'Address 2' => 'address2',
+ 'City' => 'city',
+ 'State' => 'state',
+ 'Zip' => 'zip',
+ 'Country' => 'country_full',
+ 'Day phone' => 'daytime', # XXX should use msgcat, but how?
+ 'Night phone' => 'night', # XXX should use msgcat, but how?
+ 'Fax number' => 'fax',
+ '(bill) Address 1' => 'address1',
+ '(bill) Address 2' => 'address2',
+ '(bill) City' => 'city',
+ '(bill) State' => 'state',
+ '(bill) Zip' => 'zip',
+ '(bill) Country' => 'country_full',
+ '(bill) Day phone' => 'daytime', # XXX should use msgcat, but how?
+ '(bill) Night phone' => 'night', # XXX should use msgcat, but how?
+ '(bill) Fax number' => 'fax',
+ '(service) Address 1' => 'ship_address1',
+ '(service) Address 2' => 'ship_address2',
+ '(service) City' => 'ship_city',
+ '(service) State' => 'ship_state',
+ '(service) Zip' => 'ship_zip',
+ '(service) Country' => 'ship_country_full',
+ '(service) Day phone' => 'ship_daytime', # XXX should use msgcat, how?
+ '(service) Night phone' => 'ship_night', # XXX should use msgcat, how?
+ '(service) Fax number' => 'ship_fax',
+ 'Invoicing email(s)' => 'invoicing_list_emailonly_scalar',
+ 'Payment Type' => 'payby',
+ 'Current Balance' => 'current_balance',
+ );
+ $header2method{'Cust#'} = 'display_custnum'
+ if $conf->exists('cust_main-default_agent_custid');
+
+ my %header2colormethod = (
+ 'Cust. Status' => 'cust_statuscolor',
+ );
+ my %header2style = (
+ 'Cust. Status' => 'b',
+ );
+ my %header2align = (
+ 'Cust. Status' => 'c',
+ );
+
+ my $cust_fields;
+ my @cust_header;
+ if ( @_ && $_[0] ) {
+
+ warn " using supplied cust-fields override".
+ " (ignoring cust-fields config file)"
+ if $DEBUG;
+ $cust_fields = shift;
+
+ } else {
+
+ if ( $conf->exists('cust-fields')
+ && $conf->config('cust-fields') =~ /^([\w\. \|\#\(\)]+):?/
+ )
+ {
+ warn " found cust-fields configuration value"
+ if $DEBUG;
+ $cust_fields = $1;
+ } else {
+ warn " no cust-fields configuration value found; using default 'Cust. Status | Customer'"
+ if $DEBUG;
+ $cust_fields = 'Cust. Status | Customer';
+ }
+
+ }
+
+ @cust_header = split(/ \| /, $cust_fields);
+ @cust_fields = map { $header2method{$_} || $_ } @cust_header;
+ @cust_colors = map { exists $header2colormethod{$_}
+ ? $header2colormethod{$_}
+ : ''
+ }
+ @cust_header;
+ @cust_styles = map { exists $header2style{$_} ? $header2style{$_} : '' }
+ @cust_header;
+ @cust_aligns = map { exists $header2align{$_} ? $header2align{$_} : 'l' }
+ @cust_header;
+
+ #my $svc_x = shift;
+ @cust_header;
+}
+
+=item cust_sql_fields [ CUST_FIELDS_VALUE ]
+
+Returns a list of fields for the SELECT portion of an SQL query.
+
+As with L<the cust_header subroutine|/cust_header>, the fields returned are
+defined by the supplied customer fields setting, or if no customer fields
+setting is supplied, the <B>cust-fields</B> configuration value.
+
+=cut
+
+sub cust_sql_fields {
+
+ my @fields = qw( last first company );
+ push @fields, map "ship_$_", @fields;
+ push @fields, 'country';
+
+ cust_header(@_);
+ #inefficientish, but tiny lists and only run once per page
+
+ my @add_fields = qw( address1 address2 city state zip daytime night fax );
+ push @fields,
+ grep { my $field = $_; grep { $_ eq $field } @cust_fields }
+ ( @add_fields, ( map "ship_$_", @add_fields ), 'payby' );
+
+ my @extra_fields = ();
+ if (grep { $_ eq 'current_balance' } @cust_fields) {
+ push @extra_fields, FS::cust_main->balance_sql . " AS current_balance";
+ }
+
+ map("cust_main.$_", @fields), @extra_fields;
+}
+
+=item cust_fields OBJECT [ CUST_FIELDS_VALUE ]
+
+Given an object that contains fields from cust_main (say, from a
+JOINed search. See httemplate/search/svc_* for examples), returns an array
+of customer information, or "(unlinked)" if this service is not linked to a
+customer.
+
+As with L<the cust_header subroutine|/cust_header>, the fields returned are
+defined by the supplied customer fields setting, or if no customer fields
+setting is supplied, the <B>cust-fields</B> configuration value.
+
+=cut
+
+sub cust_fields {
+ my $record = shift;
+ warn "FS::UI::Web::cust_fields called for $record ".
+ "(cust_fields: @cust_fields)"
+ if $DEBUG > 1;
+
+ #cust_header(@_) unless @cust_fields; #now need to cache to keep cust_fields
+ # #override incase we were passed as a sub
+
+ my $seen_unlinked = 0;
+ map {
+ if ( $record->custnum ) {
+ warn " $record -> $_"
+ if $DEBUG > 1;
+ $record->$_(@_);
+ } else {
+ warn " ($record unlinked)"
+ if $DEBUG > 1;
+ $seen_unlinked++ ? '' : '(unlinked)';
+ }
+ } @cust_fields;
+}
+
+=item cust_colors
+
+Returns an array of subroutine references (or empty strings) for returning
+customer information colors.
+
+As with L<the cust_header subroutine|/cust_header>, the fields returned are
+defined by the supplied customer fields setting, or if no customer fields
+setting is supplied, the <B>cust-fields</B> configuration value.
+
+=cut
+
+sub cust_colors {
+ map {
+ my $method = $_;
+ if ( $method ) {
+ sub { shift->$method(@_) };
+ } else {
+ '';
+ }
+ } @cust_colors;
+}
+
+=item cust_styles
+
+Returns an array of customer information styles.
+
+As with L<the cust_header subroutine|/cust_header>, the fields returned are
+defined by the supplied customer fields setting, or if no customer fields
+setting is supplied, the <B>cust-fields</B> configuration value.
+
+=cut
+
+sub cust_styles {
+ map {
+ if ( $_ ) {
+ $_;
+ } else {
+ '';
+ }
+ } @cust_styles;
+}
+
+=item cust_aligns
+
+Returns an array or scalar (depending on context) of customer information
+alignments.
+
+As with L<the cust_header subroutine|/cust_header>, the fields returned are
+defined by the supplied customer fields setting, or if no customer fields
+setting is supplied, the <B>cust-fields</B> configuration value.
+
+=cut
+
+sub cust_aligns {
+ if ( wantarray ) {
+ @cust_aligns;
+ } else {
+ join('', @cust_aligns);
+ }
+}
+
+###
+# begin JSRPC code...
+###
+
+package FS::UI::Web::JSRPC;
+
+use strict;
+use vars qw($DEBUG);
+use Carp;
+use Storable qw(nfreeze);
+use MIME::Base64;
+use JSON;
+use FS::UID qw(getotaker);
+use FS::Record qw(qsearchs);
+use FS::queue;
+
+$DEBUG = 0;
+
+sub new {
+ my $class = shift;
+ my $self = {
+ env => {},
+ job => shift,
+ cgi => shift,
+ };
+
+ bless $self, $class;
+
+ croak "CGI object required as second argument" unless $self->{'cgi'};
+
+ return $self;
+}
+
+sub process {
+
+ my $self = shift;
+
+ my $cgi = $self->{'cgi'};
+
+ # XXX this should parse JSON foo and build a proper data structure
+ my @args = $cgi->param('arg');
+
+ #work around konqueror bug!
+ @args = map { s/\x00$//; $_; } @args;
+
+ my $sub = $cgi->param('sub'); #????
+
+ warn "FS::UI::Web::JSRPC::process:\n".
+ " cgi=$cgi\n".
+ " sub=$sub\n".
+ " args=".join(', ',@args)."\n"
+ if $DEBUG;
+
+ if ( $sub eq 'start_job' ) {
+
+ $self->start_job(@args);
+
+ } elsif ( $sub eq 'job_status' ) {
+
+ $self->job_status(@args);
+
+ } else {
+
+ die "unknown sub $sub";
+
+ }
+
+}
+
+sub start_job {
+ my $self = shift;
+
+ warn "FS::UI::Web::start_job: ". join(', ', @_) if $DEBUG;
+# my %param = @_;
+ my %param = ();
+ while ( @_ ) {
+ my( $field, $value ) = splice(@_, 0, 2);
+ unless ( exists( $param{$field} ) ) {
+ $param{$field} = $value;
+ } elsif ( ! ref($param{$field}) ) {
+ $param{$field} = [ $param{$field}, $value ];
+ } else {
+ push @{$param{$field}}, $value;
+ }
+ }
+ $param{CurrentUser} = getotaker();
+ warn "FS::UI::Web::start_job\n".
+ join('', map {
+ if ( ref($param{$_}) ) {
+ " $_ => [ ". join(', ', @{$param{$_}}). " ]\n";
+ } else {
+ " $_ => $param{$_}\n";
+ }
+ } keys %param )
+ if $DEBUG;
+
+ #first get the CGI params shipped off to a job ASAP so an id can be returned
+ #to the caller
+
+ my $job = new FS::queue { 'job' => $self->{'job'} };
+
+ #too slow to insert all the cgi params as individual args..,?
+ #my $error = $queue->insert('_JOB', $cgi->Vars);
+
+ #warn 'froze string of size '. length(nfreeze(\%param)). " for job args\n"
+ # if $DEBUG;
+
+ my $error = $job->insert( '_JOB', encode_base64(nfreeze(\%param)) );
+
+ if ( $error ) {
+
+ warn "job not inserted: $error\n"
+ if $DEBUG;
+
+ $error; #this doesn't seem to be handled well,
+ # will trigger "illegal jobnum" below?
+ # (should never be an error inserting the job, though, only thing
+ # would be Pg f%*kage)
+ } else {
+
+ warn "job inserted successfully with jobnum ". $job->jobnum. "\n"
+ if $DEBUG;
+
+ $job->jobnum;
+ }
+
+}
+
+sub job_status {
+ my( $self, $jobnum ) = @_; #$url ???
+
+ sleep 1; # XXX could use something better...
+
+ my $job;
+ if ( $jobnum =~ /^(\d+)$/ ) {
+ $job = qsearchs('queue', { 'jobnum' => $jobnum } );
+ } else {
+ die "FS::UI::Web::job_status: illegal jobnum $jobnum\n";
+ }
+
+ my @return;
+ if ( $job && $job->status ne 'failed' ) {
+ @return = ( 'progress', $job->statustext );
+ } elsif ( !$job ) { #handle job gone case : job successful
+ # so close popup, redirect parent window...
+ @return = ( 'complete' );
+ } else {
+ @return = ( 'error', $job ? $job->statustext : $jobnum );
+ }
+
+ objToJson(\@return);
+
+}
+
+1;
+
diff --git a/FS/FS/UI/Web/small_custview.pm b/FS/FS/UI/Web/small_custview.pm
new file mode 100644
index 0000000..f8e2020
--- /dev/null
+++ b/FS/FS/UI/Web/small_custview.pm
@@ -0,0 +1,129 @@
+package FS::UI::Web::small_custview;
+
+use strict;
+use vars qw(@EXPORT_OK @ISA);
+use Exporter;
+use FS::Msgcat;
+use FS::Record qw(qsearchs);
+use FS::cust_main;
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw( small_custview );
+
+=item small_custview CUSTNUM || CUST_MAIN_OBJECT, COUNTRYDEFAULT, NOBALANCE_FLAG, URL
+
+Sheesh. I did switch to mason, but this is still hanging around. Figure out
+some better way to sling mason components to self-service & RT.
+
+=cut
+
+sub small_custview {
+
+ my $arg = shift;
+ my $countrydefault = shift || 'US';
+ my $nobalance = shift;
+ my $url = shift;
+
+ my $cust_main = ref($arg) ? $arg
+ : qsearchs('cust_main', { 'custnum' => $arg } )
+ or die "unknown custnum $arg";
+
+ my $html;
+
+ $html = qq!View <A HREF="$url?! . $cust_main->custnum . '">'
+ if $url;
+
+ $html .= 'Customer #<B>'. $cust_main->display_custnum. '</B></A>'.
+ ' - <B><FONT COLOR="#'. $cust_main->statuscolor. '">'.
+ ucfirst($cust_main->status). '</FONT></B>'.
+ ntable('#e8e8e8'). '<TR><TD VALIGN="top">'. ntable("#cccccc",2).
+ '<TR><TD ALIGN="right" VALIGN="top">Billing<BR>Address</TD><TD BGCOLOR="#ffffff">'.
+ $cust_main->getfield('last'). ', '. $cust_main->first. '<BR>';
+
+ $html .= $cust_main->company. '<BR>' if $cust_main->company;
+ $html .= $cust_main->address1. '<BR>';
+ $html .= $cust_main->address2. '<BR>' if $cust_main->address2;
+ $html .= $cust_main->city. ', '. $cust_main->state. ' '. $cust_main->zip. '<BR>';
+ $html .= $cust_main->country. '<BR>'
+ if $cust_main->country && $cust_main->country ne $countrydefault;
+
+ $html .= '</TD></TR><TR><TD></TD><TD BGCOLOR="#ffffff">';
+ if ( $cust_main->daytime && $cust_main->night ) {
+ $html .= ( FS::Msgcat::_gettext('daytime') || 'Day' ).
+ ' '. $cust_main->daytime.
+ '<BR>'. ( FS::Msgcat::_gettext('night') || 'Night' ).
+ ' '. $cust_main->night;
+ } elsif ( $cust_main->daytime || $cust_main->night ) {
+ $html .= $cust_main->daytime || $cust_main->night;
+ }
+ if ( $cust_main->fax ) {
+ $html .= '<BR>Fax '. $cust_main->fax;
+ }
+
+ $html .= '</TD></TR></TABLE></TD>';
+
+ if ( defined $cust_main->dbdef_table->column('ship_last') ) {
+
+ my $pre = $cust_main->ship_last ? 'ship_' : '';
+
+ $html .= '<TD VALIGN="top">'. ntable("#cccccc",2).
+ '<TR><TD ALIGN="right" VALIGN="top">Service<BR>Address</TD><TD BGCOLOR="#ffffff">'.
+ $cust_main->get("${pre}last"). ', '.
+ $cust_main->get("${pre}first"). '<BR>';
+ $html .= $cust_main->get("${pre}company"). '<BR>'
+ if $cust_main->get("${pre}company");
+ $html .= $cust_main->get("${pre}address1"). '<BR>';
+ $html .= $cust_main->get("${pre}address2"). '<BR>'
+ if $cust_main->get("${pre}address2");
+ $html .= $cust_main->get("${pre}city"). ', '.
+ $cust_main->get("${pre}state"). ' '.
+ $cust_main->get("${pre}zip"). '<BR>';
+ $html .= $cust_main->get("${pre}country"). '<BR>'
+ if $cust_main->get("${pre}country")
+ && $cust_main->get("${pre}country") ne $countrydefault;
+
+ $html .= '</TD></TR><TR><TD></TD><TD BGCOLOR="#ffffff">';
+
+ if ( $cust_main->get("${pre}daytime") && $cust_main->get("${pre}night") ) {
+ use FS::Msgcat;
+ $html .= ( FS::Msgcat::_gettext('daytime') || 'Day' ).
+ ' '. $cust_main->get("${pre}daytime").
+ '<BR>'. ( FS::Msgcat::_gettext('night') || 'Night' ).
+ ' '. $cust_main->get("${pre}night");
+ } elsif ( $cust_main->get("${pre}daytime")
+ || $cust_main->get("${pre}night") ) {
+ $html .= $cust_main->get("${pre}daytime")
+ || $cust_main->get("${pre}night");
+ }
+ if ( $cust_main->get("${pre}fax") ) {
+ $html .= '<BR>Fax '. $cust_main->get("${pre}fax");
+ }
+
+ $html .= '</TD></TR></TABLE></TD>';
+ }
+
+ $html .= '</TR></TABLE>';
+
+ $html .= '<BR>Balance: <B>$'. $cust_main->balance. '</B><BR>'
+ unless $nobalance;
+
+ # last payment might be good here too?
+
+ $html;
+}
+
+#bah. don't want to pull in all of FS::CGI, that's the whole problem in the
+#first place
+sub ntable {
+ my $col = shift;
+ my $cellspacing = shift || 0;
+ if ( $col ) {
+ qq!<TABLE BGCOLOR="$col" BORDER=0 CELLSPACING=$cellspacing>!;
+ } else {
+ '<TABLE BORDER CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">';
+ }
+
+}
+
+1;
+
diff --git a/FS/FS/UI/bytecount.pm b/FS/FS/UI/bytecount.pm
new file mode 100644
index 0000000..0891e6d
--- /dev/null
+++ b/FS/FS/UI/bytecount.pm
@@ -0,0 +1,96 @@
+package FS::UI::bytecount;
+
+use strict;
+use vars qw($DEBUG $me);
+use FS::Conf;
+use Number::Format 1.50;
+
+$DEBUG = 0;
+$me = '[FS::UID::bytecount]';
+
+=head1 NAME
+
+FS::UI::bytecount - Subroutines for parsing and displaying byte counters
+
+=head1 SYNOPSIS
+
+ use FS::UI::bytecount;
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item bytecount_unexact COUNT
+
+Returns a two decimal place value for COUNT followed by bytes, Kbytes, Mbytes,
+or GBytes as appropriate.
+
+=cut
+
+sub bytecount_unexact {
+ my $bc = shift;
+ return("$bc bytes")
+ if ($bc < 1000);
+ return(sprintf("%.2f Kbytes", $bc/1024))
+ if ($bc < 1000000);
+ return(sprintf("%.2f Mbytes", $bc/1048576))
+ if ($bc < 1000000000);
+ return(sprintf("%.2f Gbytes", $bc/1073741824));
+}
+
+=item parse_bytecount AMOUNT
+
+Accepts a number (digits and a decimal point) possibly followed by k, m, g, or
+t (and an optional 'b') in either case. Returns a pure number representing
+the input or the input itself if unparsable. Discards commas as noise.
+
+=cut
+
+sub parse_bytecount {
+ my $bc = shift;
+ return $bc if (($bc =~ tr/.//) > 1);
+ $bc =~ /^\s*([,\d.]*)\s*([kKmMgGtT]?)[bB]?\s*$/ or return $bc;
+ my $base = $1;
+ $base =~ tr/,//d;
+ return $bc unless length $base;
+ my $exponent = index ' kmgt', lc($2);
+ return $bc if ($exponent < 0 && $2);
+ $exponent = 0 if ($exponent < 0);
+ return int($base * 1024 ** $exponent); #bytecounts are integer values
+}
+
+=item display_bytecount AMOUNT
+
+Converts a pure number to a value followed possibly followed by k, m, g, or
+t via Number::Format
+
+=cut
+
+sub display_bytecount {
+ my $bc = shift;
+ return $bc unless ($bc =~ /^(\d+)$/);
+ my $conf = new FS::Conf;
+ my $f = new Number::Format;
+ my $precision = ( $conf->exists('datavolume-significantdigits') &&
+ $conf->config('datavolume-significantdigits') =~ /^\s*\d+\s*$/ )
+ ? $conf->config('datavolume-significantdigits')
+ : 3;
+ my $unit = $conf->exists('datavolume-forcemegabytes') ? 'M' : 'A';
+
+ return $f->format_bytes($bc, precision => $precision, unit => $unit);
+}
+
+=back
+
+=head1 BUGS
+
+Fly
+
+=head1 SEE ALSO
+
+L<Number::Format>
+
+=cut
+
+1;
+
diff --git a/FS/FS/UID.pm b/FS/FS/UID.pm
new file mode 100644
index 0000000..40d29c1
--- /dev/null
+++ b/FS/FS/UID.pm
@@ -0,0 +1,392 @@
+package FS::UID;
+
+use strict;
+use vars qw(
+ @ISA @EXPORT_OK $DEBUG $me $cgi $dbh $freeside_uid $user
+ $conf_dir $cache_dir $secrets $datasrc $db_user $db_pass %callback @callback
+ $driver_name $AutoCommit $callback_hack $use_confcompat
+);
+use subs qw(
+ getsecrets cgisetotaker
+);
+use Exporter;
+use Carp qw(carp croak cluck confess);
+use DBI;
+use IO::File;
+use FS::CurrentUser;
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(checkeuid checkruid cgisuidsetup adminsuidsetup forksuidsetup
+ getotaker dbh datasrc getsecrets driver_name myconnect
+ use_confcompat);
+
+$DEBUG = 0;
+$me = '[FS::UID]';
+
+$freeside_uid = scalar(getpwnam('freeside'));
+
+$conf_dir = "%%%FREESIDE_CONF%%%";
+$cache_dir = "%%%FREESIDE_CACHE%%%";
+
+$AutoCommit = 1; #ours, not DBI
+$use_confcompat = 1;
+$callback_hack = 0;
+
+=head1 NAME
+
+FS::UID - Subroutines for database login and assorted other stuff
+
+=head1 SYNOPSIS
+
+ use FS::UID qw(adminsuidsetup cgisuidsetup dbh datasrc getotaker
+ checkeuid checkruid);
+
+ adminsuidsetup $user;
+
+ $cgi = new CGI;
+ $dbh = cgisuidsetup($cgi);
+
+ $dbh = dbh;
+
+ $datasrc = datasrc;
+
+ $driver_name = driver_name;
+
+=head1 DESCRIPTION
+
+Provides a hodgepodge of subroutines.
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item adminsuidsetup USER
+
+Sets the user to USER (see config.html from the base documentation).
+Cleans the environment.
+Make sure the script is running as freeside, or setuid freeside.
+Opens a connection to the database.
+Swaps real and effective UIDs.
+Runs any defined callbacks (see below).
+Returns the DBI database handle (usually you don't need this).
+
+=cut
+
+sub adminsuidsetup {
+ $dbh->disconnect if $dbh;
+ &forksuidsetup(@_);
+}
+
+sub forksuidsetup {
+ $user = shift;
+ my $olduser = $user;
+ warn "$me forksuidsetup starting for $user\n" if $DEBUG;
+
+ if ( $FS::CurrentUser::upgrade_hack ) {
+ $user = 'fs_bootstrap';
+ } else {
+ croak "fatal: adminsuidsetup called without arguements" unless $user;
+
+ $user =~ /^([\w\-\.]+)$/ or croak "fatal: illegal user $user";
+ $user = $1;
+ }
+
+ $ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin';
+ $ENV{'SHELL'} = '/bin/sh';
+ $ENV{'IFS'} = " \t\n";
+ $ENV{'CDPATH'} = '';
+ $ENV{'ENV'} = '';
+ $ENV{'BASH_ENV'} = '';
+
+ croak "Not running uid freeside (\$>=$>, \$<=$<)\n" unless checkeuid();
+
+ warn "$me forksuidsetup connecting to database\n" if $DEBUG;
+ if ( $FS::CurrentUser::upgrade_hack && $olduser ) {
+ $dbh = &myconnect($olduser);
+ } else {
+ $dbh = &myconnect();
+ }
+ warn "$me forksuidsetup connected to database with handle $dbh\n" if $DEBUG;
+
+ warn "$me forksuidsetup loading schema\n" if $DEBUG;
+ use FS::Schema qw(reload_dbdef dbdef);
+ reload_dbdef("$conf_dir/dbdef.$datasrc")
+ unless $FS::Schema::setup_hack;
+
+ warn "$me forksuidsetup deciding upon config system to use\n" if $DEBUG;
+
+ if ( ! $FS::Schema::setup_hack && dbdef->table('conf') ) {
+
+ my $sth = $dbh->prepare("SELECT COUNT(*) FROM conf") or die $dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my $confcount = $sth->fetchrow_arrayref->[0];
+
+ if ($confcount) {
+ $use_confcompat = 0;
+ }else{
+ warn "NO CONFIGURATION RECORDS FOUND";
+ }
+
+ } else {
+ warn "NO CONFIGURATION TABLE FOUND";
+ }
+
+ unless ( $callback_hack ) {
+ warn "$me calling callbacks\n" if $DEBUG;
+ foreach ( keys %callback ) {
+ &{$callback{$_}};
+ # breaks multi-database installs # delete $callback{$_}; #run once
+ }
+
+ &{$_} foreach @callback;
+ } else {
+ warn "$me skipping callbacks (callback_hack set)\n" if $DEBUG;
+ }
+
+ warn "$me forksuidsetup loading user\n" if $DEBUG;
+ FS::CurrentUser->load_user($user);
+
+ $dbh;
+}
+
+sub myconnect {
+ DBI->connect( getsecrets(@_), { 'AutoCommit' => 0,
+ 'ChopBlanks' => 1,
+ 'ShowErrorStatement' => 1,
+ }
+ )
+ or die "DBI->connect error: $DBI::errstr\n";
+}
+
+=item install_callback
+
+A package can install a callback to be run in adminsuidsetup by passing
+a coderef to the FS::UID->install_callback class method. If adminsuidsetup has
+run already, the callback will also be run immediately.
+
+ $coderef = sub { warn "Hi, I'm returning your call!" };
+ FS::UID->install_callback($coderef);
+
+ install_callback FS::UID sub {
+ warn "Hi, I'm returning your call!"
+ };
+
+=cut
+
+sub install_callback {
+ my $class = shift;
+ my $callback = shift;
+ push @callback, $callback;
+ &{$callback} if $dbh;
+}
+
+=item cgisuidsetup CGI_object
+
+Takes a single argument, which is a CGI (see L<CGI>) or Apache (see L<Apache>)
+object (CGI::Base is depriciated). Runs cgisetotaker and then adminsuidsetup.
+
+=cut
+
+sub cgisuidsetup {
+ $cgi=shift;
+ if ( $cgi->isa('CGI::Base') ) {
+ carp "Use of CGI::Base is depriciated";
+ } elsif ( $cgi->isa('Apache') ) {
+
+ } elsif ( ! $cgi->isa('CGI') ) {
+ croak "fatal: unrecognized object $cgi";
+ }
+ cgisetotaker;
+ adminsuidsetup($user);
+}
+
+=item cgi
+
+Returns the CGI (see L<CGI>) object.
+
+=cut
+
+sub cgi {
+ carp "warning: \$FS::UID::cgi isa Apache" if $cgi->isa('Apache');
+ $cgi;
+}
+
+=item dbh
+
+Returns the DBI database handle.
+
+=cut
+
+sub dbh {
+ $dbh;
+}
+
+=item datasrc
+
+Returns the DBI data source.
+
+=cut
+
+sub datasrc {
+ $datasrc;
+}
+
+=item driver_name
+
+Returns just the driver name portion of the DBI data source.
+
+=cut
+
+sub driver_name {
+ return $driver_name if defined $driver_name;
+ $driver_name = ( split(':', $datasrc) )[1];
+}
+
+sub suidsetup {
+ croak "suidsetup depriciated";
+}
+
+=item getotaker
+
+Returns the current Freeside user.
+
+=cut
+
+sub getotaker {
+ $user;
+}
+
+=item cgisetotaker
+
+Sets and returns the CGI REMOTE_USER. $cgi should be defined as a CGI.pm
+object (see L<CGI>) or an Apache object (see L<Apache>). Support for CGI::Base
+and derived classes is depriciated.
+
+=cut
+
+sub cgisetotaker {
+ if ( $cgi && $cgi->isa('CGI::Base') && defined $cgi->var('REMOTE_USER')) {
+ carp "Use of CGI::Base is depriciated";
+ $user = lc ( $cgi->var('REMOTE_USER') );
+ } elsif ( $cgi && $cgi->isa('CGI') && defined $cgi->remote_user ) {
+ $user = lc ( $cgi->remote_user );
+ } elsif ( $cgi && $cgi->isa('Apache') ) {
+ $user = lc ( $cgi->connection->user );
+ } else {
+ die "fatal: Can't get REMOTE_USER! for cgi $cgi - you need to setup ".
+ "Apache user authentication as documented in httemplate/docs/install.html";
+ }
+ $user;
+}
+
+=item checkeuid
+
+Returns true if effective UID is that of the freeside user.
+
+=cut
+
+sub checkeuid {
+ #$> = $freeside_uid unless $>; #huh. mpm-itk hack
+ ( $> == $freeside_uid );
+}
+
+=item checkruid
+
+Returns true if the real UID is that of the freeside user.
+
+=cut
+
+sub checkruid {
+ ( $< == $freeside_uid );
+}
+
+=item getsecrets [ USER ]
+
+Sets the user to USER, if supplied.
+Sets and returns the DBI datasource, username and password for this user from
+the `/usr/local/etc/freeside/mapsecrets' file.
+
+=cut
+
+sub getsecrets {
+ my($setuser) = shift;
+ $user = $setuser if $setuser;
+
+ if ( -e "$conf_dir/mapsecrets" ) {
+ die "No user!" unless $user;
+ my($line) = grep /^\s*($user|\*)\s/,
+ map { /^(.*)$/; $1 } readline(new IO::File "$conf_dir/mapsecrets");
+ confess "User $user not found in mapsecrets!" unless $line;
+ $line =~ /^\s*($user|\*)\s+(.*)$/;
+ $secrets = $2;
+ die "Illegal mapsecrets line for user?!" unless $secrets;
+ } else {
+ # no mapsecrets file at all, so do the default thing
+ $secrets = 'secrets';
+ }
+
+ ($datasrc, $db_user, $db_pass) =
+ map { /^(.*)$/; $1 } readline(new IO::File "$conf_dir/$secrets")
+ or die "Can't get secrets: $conf_dir/$secrets: $!\n";
+ undef $driver_name;
+ ($datasrc, $db_user, $db_pass);
+}
+
+=item use_confcompat
+
+Returns true whenever we should use 1.7 configuration compatibility.
+
+=cut
+
+sub use_confcompat {
+ $use_confcompat;
+}
+
+=back
+
+=head1 CALLBACKS
+
+Warning: this interface is (still) likely to change in future releases.
+
+New (experimental) callback interface:
+
+A package can install a callback to be run in adminsuidsetup by passing
+a coderef to the FS::UID->install_callback class method. If adminsuidsetup has
+run already, the callback will also be run immediately.
+
+ $coderef = sub { warn "Hi, I'm returning your call!" };
+ FS::UID->install_callback($coderef);
+
+ install_callback FS::UID sub {
+ warn "Hi, I'm returning your call!"
+ };
+
+Old (deprecated) callback interface:
+
+A package can install a callback to be run in adminsuidsetup by putting a
+coderef into the hash %FS::UID::callback :
+
+ $coderef = sub { warn "Hi, I'm returning your call!" };
+ $FS::UID::callback{'Package::Name'} = $coderef;
+
+=head1 BUGS
+
+Too many package-global variables.
+
+Not OO.
+
+No capabilities yet. When mod_perl and Authen::DBI are implemented,
+cgisuidsetup will go away as well.
+
+Goes through contortions to support non-OO syntax with multiple datasrc's.
+
+Callbacks are (still) inelegant.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<CGI>, L<DBI>, config.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
new file mode 100644
index 0000000..97f24d4
--- /dev/null
+++ b/FS/FS/Upgrade.pm
@@ -0,0 +1,249 @@
+package FS::Upgrade;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK $DEBUG );
+use Exporter;
+use Tie::IxHash;
+use FS::UID qw( dbh driver_name );
+use FS::Conf;
+use FS::Record qw(qsearchs str2time_sql);
+
+use FS::svc_domain;
+$FS::svc_domain::whois_hack = 1;
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( upgrade upgrade_sqlradius );
+
+$DEBUG = 1;
+
+=head1 NAME
+
+FS::Upgrade - Database upgrade routines
+
+=head1 SYNOPSIS
+
+ use FS::Upgrade;
+
+=head1 DESCRIPTION
+
+Currently this module simply provides a place to store common subroutines for
+database upgrades.
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item
+
+=cut
+
+sub upgrade {
+ my %opt = @_;
+
+ my $data = upgrade_data(%opt);
+
+ foreach my $table ( keys %$data ) {
+
+ my $class = "FS::$table";
+ eval "use $class;";
+ die $@ if $@;
+
+ if ( $class->can('_upgrade_data') ) {
+ warn "Upgrading $table...\n";
+
+ my $start = time;
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ $FS::UID::AutoCommit = 0;
+
+ $class->_upgrade_data(%opt);
+
+ if ( $oldAutoCommit ) {
+ dbh->commit or die dbh->errstr;
+ }
+
+ #warn "\e[1K\rUpgrading $table... done in ". (time-$start). " seconds\n";
+ warn " done in ". (time-$start). " seconds\n";
+
+ } else {
+ warn "WARNING: asked for upgrade of $table,".
+ " but FS::$table has no _upgrade_data method\n";
+ }
+
+# my @records = @{ $data->{$table} };
+#
+# foreach my $record ( @records ) {
+# my $args = delete($record->{'_upgrade_args'}) || [];
+# my $object = $class->new( $record );
+# my $error = $object->insert( @$args );
+# die "error inserting record into $table: $error\n"
+# if $error;
+# }
+
+ }
+
+}
+
+
+sub upgrade_data {
+ my %opt = @_;
+
+ tie my %hash, 'Tie::IxHash',
+
+ #msgcat
+ 'msgcat' => [],
+
+ #reason type and reasons
+ 'reason_type' => [],
+ 'reason' => [],
+ 'cust_pkg_reason' => [],
+
+ #need part_pkg before cust_credit...
+ 'part_pkg' => [],
+
+ #customer credits
+ 'cust_credit' => [],
+
+ #duplicate history records
+ 'h_cust_svc' => [],
+
+ #populate cust_pay.otaker
+ 'cust_pay' => [],
+
+ #populate part_pkg_taxclass for starters
+ 'part_pkg_taxclass' => [],
+
+ #remove bad pending records
+ 'cust_pay_pending' => [],
+
+ #replace invnum and pkgnum with billpkgnum
+ 'cust_bill_pkg_detail' => [],
+
+ #usage_classes if we have none
+ 'usage_class' => [],
+
+ #fixup access rights
+ 'access_right' => [],
+
+ ;
+
+ \%hash;
+
+}
+
+sub upgrade_sqlradius {
+ #my %opt = @_;
+
+ my $conf = new FS::Conf;
+
+ my @part_export = FS::part_export::sqlradius->all_sqlradius_withaccounting();
+
+ foreach my $part_export ( @part_export ) {
+
+ my $errmsg = 'Error adding FreesideStatus to '.
+ $part_export->option('datasrc'). ': ';
+
+ my $dbh = DBI->connect(
+ ( map $part_export->option($_), qw ( datasrc username password ) ),
+ { PrintError => 0, PrintWarn => 0 }
+ ) or do {
+ warn $errmsg.$DBI::errstr;
+ next;
+ };
+
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+ my $group = "UserName";
+ $group .= ",Realm"
+ if ( ref($part_export) =~ /withdomain/ );
+
+ my $sth_alter = $dbh->prepare(
+ "ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL"
+ );
+ if ( $sth_alter ) {
+ if ( $sth_alter->execute ) {
+ my $sth_update = $dbh->prepare(
+ "UPDATE radacct SET FreesideStatus = 'done' WHERE FreesideStatus IS NULL"
+ ) or die $errmsg.$dbh->errstr;
+ $sth_update->execute or die $errmsg.$sth_update->errstr;
+ } else {
+ my $error = $sth_alter->errstr;
+ warn $errmsg.$error unless $error =~ /Duplicate column name/i;
+ }
+ } else {
+ my $error = $dbh->errstr;
+ warn $errmsg.$error; #unless $error =~ /exists/i;
+ }
+
+ my $sth_index = $dbh->prepare(
+ "CREATE INDEX FreesideStatus ON radacct ( FreesideStatus )"
+ );
+ if ( $sth_index ) {
+ unless ( $sth_index->execute ) {
+ my $error = $sth_index->errstr;
+ warn $errmsg.$error unless $error =~ /Duplicate key name/i;
+ }
+ } else {
+ my $error = $dbh->errstr;
+ warn $errmsg.$error; #unless $error =~ /exists/i;
+ }
+
+ my $sth = $dbh->prepare("SELECT UserName,
+ Realm,
+ $str2time max(AcctStartTime)),
+ $str2time max(AcctStopTime))
+ FROM radacct
+ WHERE FreesideStatus = 'done'
+ AND AcctStartTime != 0
+ AND AcctStopTime != 0
+ GROUP BY $group
+ ")
+ or die $errmsg.$dbh->errstr;
+ $sth->execute() or die $errmsg.$sth->errstr;
+
+ while (my $row = $sth->fetchrow_arrayref ) {
+ my ($username, $realm, $start, $stop) = @$row;
+
+ $username = lc($username) unless $conf->exists('username-uppercase');
+
+ my $exportnum = $part_export->exportnum;
+ my $extra_sql = " AND exportnum = $exportnum ".
+ " AND exportsvcnum IS NOT NULL ";
+
+ if ( ref($part_export) =~ /withdomain/ ) {
+ $extra_sql = " AND '$realm' = ( SELECT domain FROM svc_domain
+ WHERE svc_domain.svcnum = svc_acct.domsvc ) ";
+ }
+
+ my $svc_acct = qsearchs({
+ 'select' => 'svc_acct.*',
+ 'table' => 'svc_acct',
+ 'addl_from' => 'LEFT JOIN cust_svc USING ( svcnum )'.
+ 'LEFT JOIN export_svc USING ( svcpart )',
+ 'hashref' => { 'username' => $username },
+ 'extra_sql' => $extra_sql,
+ });
+
+ if ($svc_acct) {
+ $svc_acct->last_login($start)
+ if $start && (!$svc_acct->last_login || $start > $svc_acct->last_login);
+ $svc_acct->last_logout($stop)
+ if $stop && (!$svc_acct->last_logout || $stop > $svc_acct->last_logout);
+ }
+ }
+ }
+
+}
+
+=back
+
+=head1 BUGS
+
+Sure.
+
+=head1 SEE ALSO
+
+=cut
+
+1;
+
diff --git a/FS/FS/XMLRPC.pm b/FS/FS/XMLRPC.pm
new file mode 100644
index 0000000..fb0e5ac
--- /dev/null
+++ b/FS/FS/XMLRPC.pm
@@ -0,0 +1,166 @@
+package FS::XMLRPC;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Frontier::RPC2;
+
+# Instead of 'use'ing freeside modules on the fly below, just preload them now.
+use FS;
+use FS::CGI;
+use FS::Conf;
+use FS::Record;
+use FS::cust_main;
+
+use Data::Dumper;
+
+@ISA = qw( );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::XMLRPC - Object methods for handling XMLRPC requests
+
+=head1 SYNOPSIS
+
+ use FS::XMLRPC;
+
+ $xmlrpc = new FS::XMLRPC;
+
+ ($error, $response_xml) = $xmlrpc->serve($request_xml);
+
+=head1 DESCRIPTION
+
+The FS::XMLRPC object is a mechanisim to access read-only data from freeside's subroutines. It does not, at least not at this point, give you the ability to access methods of freeside objects remotely. It can, however, be used to call subroutines such as FS::cust_main::smart_search and FS::Record::qsearch.
+
+See the serve method below for calling syntax.
+
+=head1 METHODS
+
+=over 4
+
+=item new
+
+Provides a FS::XMLRPC object used to handle incoming XMLRPC requests.
+
+=cut
+
+sub new {
+
+ my $class = shift;
+ my $self = {};
+ bless($self, $class);
+
+ $self->{_coder} = new Frontier::RPC2;
+
+ return $self;
+
+}
+
+=item serve REQUEST_XML_SCALAR
+
+The serve method takes a scalar containg an XMLRPC request for one of freeside's subroutines (not object methods). Parameters passed in the 'methodCall' will be passed as a list to the subroutine untouched. The return value of the called subroutine _must_ be a freeside object reference (eg. qsearchs) or a list of freeside object references (eg. qsearch, smart_search), _and_, the object(s) returned must support the hashref method. This will be checked first by calling UNIVERSAL::can('FS::class::subroutine', 'hashref').
+
+Return value is an XMLRPC methodResponse containing the results of the call. The result of the subroutine call itself will be coded in the methodResponse as an array of structs, regardless of whether there was many or a single object returned. In other words, after you decode the response, you'll always have an array.
+
+=cut
+
+sub serve {
+
+ my ($self, $request_xml) = (shift, shift);
+ my $response_xml;
+
+ my $coder = $self->{_coder};
+ my $call = $coder->decode($request_xml);
+
+ warn "Got methodCall with method_name='" . $call->{method_name} . "'"
+ if $DEBUG;
+
+ $response_xml = $coder->encode_response(&_serve($call->{method_name}, $call->{value}));
+
+ return ('', $response_xml);
+
+}
+
+sub _serve { #Subroutine, not method
+
+ my ($method_name, $params) = (shift, shift);
+
+
+ #die 'Called _serve without parameters' unless ref($params) eq 'ARRAY';
+ $params = [] unless (ref($params) eq 'ARRAY');
+
+ if ($method_name =~ /^(\w+)\.(\w+)/) {
+
+ #my ($class, $sub) = split(/\./, $method_name);
+ my ($class, $sub) = ($1, $2);
+ my $fssub = "FS::${class}::${sub}";
+ warn "fssub: ${fssub}" if $DEBUG;
+ warn "params: " . Dumper($params) if $DEBUG;
+
+ my @result;
+
+ if ($class eq 'Conf') { #Special case for FS::Conf because we need an obj.
+
+ if ($sub eq 'config') {
+ my $conf = new FS::Conf;
+ @result = ($conf->config(@$params));
+ } else {
+ warn "FS::XMLRPC: Can't call undefined subroutine '${fssub}'";
+ }
+
+ } else {
+
+ unless (UNIVERSAL::can("FS::${class}", $sub)) {
+ warn "FS::XMLRPC: Can't call undefined subroutine '${fssub}'";
+ # Should we encode an error in the response,
+ # or just break silently to the remote caller and complain locally?
+ return [];
+ }
+
+ eval {
+ no strict 'refs';
+ my $fssub = "FS::${class}::${sub}";
+ @result = (&$fssub(@$params));
+ };
+
+ if ($@) {
+ warn "FS::XMLRPC: Error while calling '${fssub}': $@";
+ return [];
+ }
+
+ }
+
+ warn Dumper(@result) if $DEBUG;
+
+ if (grep { UNIVERSAL::can($_, 'hashref') ? 0 : 1 } @result) {
+ #warn "FS::XMLRPC: One or more objects returned from '${fssub}' doesn't " .
+ # "support the 'hashref' method.";
+
+ # If they're not FS::Record decendants, just return the results unmap'd?
+ # This is more flexible, but possibly more error-prone.
+ return [ @result ];
+ } else {
+ return [ map { $_->hashref } @result ];
+ }
+ } elsif ($method_name eq 'version') {
+ return [ $FS::VERSION ];
+ } # else...
+
+ warn "Unhandle XMLRPC request '${method_name}'";
+ return [];
+
+}
+
+=head1 BUGS
+
+Probably lots.
+
+=head1 SEE ALSO
+
+L<Frontier::RPC2>.
+
+=cut
+
+1;
+
diff --git a/FS/FS/Yori.pm b/FS/FS/Yori.pm
new file mode 100644
index 0000000..8ecb05a
--- /dev/null
+++ b/FS/FS/Yori.pm
@@ -0,0 +1,73 @@
+package FS::Yori;
+# a reporting program, to report information to the MCP
+
+use strict;
+use base 'Exporter';
+
+our @EXPORT_OK = qw( reports report );
+
+sub reports { #should be autogenerated i guess
+ qw( freeside_version debian_version pg_version
+ apache_version apache_mpm
+ payment_gateways
+ );
+ #ssh_vulnkey
+}
+
+sub report {
+ my $report = shift;
+ $report =~ /^(\w+)$/ or die;
+ eval "report_$report();";
+}
+
+sub report_all {
+ foreach my $report ( reports() ) {
+ print "$report: ". report($report). "\n";
+ }
+}
+
+sub report_freeside_version {
+ chomp( my $fs_version =
+ `grep '^VERSION=' /home/ivan/freeside/Makefile | cut -d= -f2`
+ );
+ $fs_version;
+}
+
+sub report_debian_version {
+ chomp( my $deb_version = `cat /etc/debian_version` );
+ $deb_version;
+}
+
+sub report_pg_version {
+ chomp( my $pg_version = `echo 'show server_version' | psql -t freeside` );
+ chomp($pg_version); #two?
+ $pg_version =~ s/^ +//;
+ $pg_version;
+}
+
+sub report_apache_version {
+ chomp( my $apache_version =
+ `/usr/sbin/apache2 -v | head -1 | cut -d: -f2 | cut -d/ -f2 | cut -d' ' -f1`
+ );
+ $apache_version;
+}
+
+sub report_apache_mpm {
+ chomp( my $apache_mpm =
+ `/usr/sbin/apache2 -V | grep '^Server MPM' | cut -d: -f2`
+ );
+ $apache_mpm =~ s/^ +//;
+ $apache_mpm;
+}
+
+sub report_payment_gateways {
+ my @gateways = split(/\n/,
+ `aptitude -F '%c %p' search 'libbusiness-onlinepayment-.*' | grep '^i ' | grep -v '^i libbusiness-onlinepayment-perl' | cut -c29- | cut -d- -f1`
+ );
+ join(', ', @gateways);
+}
+
+#sub report_ssh_vulnkey{
+# my $ssh_vulnkey = `ssh-vulnkey -a | grep COMPROMISED`;
+# $ssh_vulnkey;
+#}
diff --git a/FS/FS/access_group.pm b/FS/FS/access_group.pm
new file mode 100644
index 0000000..b5b693a
--- /dev/null
+++ b/FS/FS/access_group.pm
@@ -0,0 +1,162 @@
+package FS::access_group;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::m2name_Common;
+use FS::access_groupagent;
+use FS::access_right;
+
+@ISA = qw(FS::m2m_Common FS::m2name_Common FS::Record);
+
+=head1 NAME
+
+FS::access_group - Object methods for access_group records
+
+=head1 SYNOPSIS
+
+ use FS::access_group;
+
+ $record = new FS::access_group \%hash;
+ $record = new FS::access_group { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_group object represents an access group. FS::access_group inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item groupnum - primary key
+
+=item groupname - Access group name
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new access group. To add the access group to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'access_group'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid access group. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('groupnum')
+ || $self->ut_text('groupname')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item access_groupagent
+
+Returns all associated FS::access_groupagent records.
+
+=cut
+
+sub access_groupagent {
+ my $self = shift;
+ qsearch('access_groupagent', { 'groupnum' => $self->groupnum } );
+}
+
+=item access_rights
+
+Returns all associated FS::access_right records.
+
+=cut
+
+sub access_rights {
+ my $self = shift;
+ qsearch('access_right', { 'righttype' => 'FS::access_group',
+ 'rightobjnum' => $self->groupnum
+ }
+ );
+}
+
+=item access_right RIGHTNAME
+
+Returns the specified FS::access_right record. Can be used as a boolean, to
+test if this group has the given RIGHTNAME.
+
+=cut
+
+sub access_right {
+ my( $self, $name ) = @_;
+ qsearchs('access_right', { 'righttype' => 'FS::access_group',
+ 'rightobjnum' => $self->groupnum,
+ 'rightname' => $name,
+ }
+ );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/access_groupagent.pm b/FS/FS/access_groupagent.pm
new file mode 100644
index 0000000..bacc013
--- /dev/null
+++ b/FS/FS/access_groupagent.pm
@@ -0,0 +1,146 @@
+package FS::access_groupagent;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::agent;
+use FS::access_group;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_groupagent - Object methods for access_groupagent records
+
+=head1 SYNOPSIS
+
+ use FS::access_groupagent;
+
+ $record = new FS::access_groupagent \%hash;
+ $record = new FS::access_groupagent { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_groupagent object represents an group reseller virtualization. FS::access_groupagent inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item groupagentnum - primary key
+
+=item groupnum -
+
+=item agentnum -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new group reseller virtualization. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'access_groupagent'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid group reseller virtualization. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('groupagentnum')
+ || $self->ut_foreign_key('groupnum', 'access_group', 'groupnum')
+ || $self->ut_foreign_key('agentnum', 'agent', 'agentnum')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item agent
+
+Returns the associated FS::agent object.
+
+=cut
+
+sub agent {
+ my $self = shift;
+ qsearchs('agent', { 'agentnum' => $self->agentnum } );
+}
+
+=item access_group
+
+Returns the associated FS::access_group object.
+
+=cut
+
+sub access_group {
+ my $self = shift;
+ qsearchs('access_group', { 'groupnum' => $self->groupnum } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm
new file mode 100644
index 0000000..4f8d1e9
--- /dev/null
+++ b/FS/FS/access_right.pm
@@ -0,0 +1,165 @@
+package FS::access_right;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_right - Object methods for access_right records
+
+=head1 SYNOPSIS
+
+ use FS::access_right;
+
+ $record = new FS::access_right \%hash;
+ $record = new FS::access_right { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_right object represents a granted access right. FS::access_right
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item rightnum - primary key
+
+=item righttype -
+
+=item rightobjnum -
+
+=item rightname -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new right. To add the right to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'access_right'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid right. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('rightnum')
+ || $self->ut_text('righttype')
+ || $self->ut_text('rightobjnum')
+ || $self->ut_text('rightname')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+sub _upgrade_data { # class method
+ my ($class, %opts) = @_;
+
+ my @unmigrated = ( qsearch( 'access_right',
+ { 'righttype'=>'FS::access_group',
+ 'rightname'=>'Engineering configuration',
+ }
+ ),
+ qsearch( 'access_right',
+ { 'righttype'=>'FS::access_group',
+ 'rightname'=>'Engineering global configuration',
+ }
+ )
+ );
+ foreach ( @unmigrated ) {
+ my $rightname = $_->rightname;
+ $rightname =~ s/Engineering/Dialup/;
+ $_->rightname($rightname);
+ my $error = $_->replace;
+ die "Failed to update access right: $error"
+ if $error;
+ my $broadband = new FS::access_right { $_->hash };
+ $rightname =~ s/Dialup/Broadband/;
+ $broadband->rightnum('');
+ $broadband->rightname($rightname);
+ $error = $broadband->insert;
+ die "Failed to insert access right: $error"
+ if $error;
+ }
+
+ '';
+
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/access_user.pm b/FS/FS/access_user.pm
new file mode 100644
index 0000000..cf56fd8
--- /dev/null
+++ b/FS/FS/access_user.pm
@@ -0,0 +1,481 @@
+package FS::access_user;
+
+use strict;
+use vars qw( @ISA $DEBUG $me $htpasswd_file );
+use FS::UID;
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::m2m_Common;
+use FS::option_Common;
+use FS::access_user_pref;
+use FS::access_usergroup;
+use FS::agent;
+
+@ISA = qw( FS::m2m_Common FS::option_Common FS::Record );
+#@ISA = qw( FS::m2m_Common FS::option_Common );
+
+$DEBUG = 0;
+$me = '[FS::access_user]';
+
+#kludge htpasswd for now (i hope this bootstraps okay)
+FS::UID->install_callback( sub {
+ my $conf = new FS::Conf;
+ $htpasswd_file = $conf->base_dir. '/htpasswd';
+} );
+
+=head1 NAME
+
+FS::access_user - Object methods for access_user records
+
+=head1 SYNOPSIS
+
+ use FS::access_user;
+
+ $record = new FS::access_user \%hash;
+ $record = new FS::access_user { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_user object represents an internal access user. FS::access_user inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item usernum - primary key
+
+=item username -
+
+=item _password -
+
+=item last -
+
+=item first -
+
+=item disabled - empty or 'Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new internal access user. To add the user to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'access_user'; }
+
+sub _option_table { 'access_user_pref'; }
+sub _option_namecol { 'prefname'; }
+sub _option_valuecol { 'prefvalue'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ my $error = $self->check;
+ return $error if $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $error = $self->htpasswd_kludge();
+ if ( $error ) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $error;
+ }
+
+ $error = $self->SUPER::insert(@_);
+
+ if ( $error ) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+
+ #make sure it isn't a dup username? or you could nuke people's passwords
+ #blah. really just should do our own login w/cookies
+ #and auth out of the db in the first place
+ #my $hterror = $self->htpasswd_kludge('-D');
+ #$error .= " - additionally received error cleaning up htpasswd file: $hterror"
+ return $error;
+
+ } else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+ }
+
+}
+
+sub htpasswd_kludge {
+ my $self = shift;
+
+ #awful kludge to skip setting htpasswd for fs_* users
+ return '' if $self->username =~ /^fs_/;
+
+ unshift @_, '-c' unless -e $htpasswd_file;
+ if (
+ system('htpasswd', '-b', @_,
+ $htpasswd_file,
+ $self->username,
+ $self->_password,
+ ) == 0
+ )
+ {
+ return '';
+ } else {
+ return 'htpasswd exited unsucessfully';
+ }
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error =
+ $self->SUPER::delete(@_)
+ || $self->htpasswd_kludge('-D')
+ ;
+
+ if ( $error ) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $error;
+ } else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+ }
+
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my $new = shift;
+
+ my $old = ( ref($_[0]) eq ref($new) )
+ ? shift
+ : $new->replace_old;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $new->_password ne $old->_password ) {
+ my $error = $new->htpasswd_kludge();
+ if ( $error ) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $new->SUPER::replace($old, @_);
+
+ if ( $error ) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $error;
+ } else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+ }
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid internal access user. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('usernum')
+ || $self->ut_alpha_lower('username')
+ || $self->ut_text('_password')
+ || $self->ut_text('last')
+ || $self->ut_text('first')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item name
+
+Returns a name string for this user: "Last, First".
+
+=cut
+
+sub name {
+ my $self = shift;
+ $self->get('last'). ', '. $self->first;
+}
+
+=item access_usergroup
+
+=cut
+
+sub access_usergroup {
+ my $self = shift;
+ qsearch( 'access_usergroup', { 'usernum' => $self->usernum } );
+}
+
+#=item access_groups
+#
+#=cut
+#
+#sub access_groups {
+#
+#}
+#
+#=item access_groupnames
+#
+#=cut
+#
+#sub access_groupnames {
+#
+#}
+
+=item agentnums
+
+Returns a list of agentnums this user can view (via group membership).
+
+=cut
+
+sub agentnums {
+ my $self = shift;
+ my $sth = dbh->prepare(
+ "SELECT DISTINCT agentnum FROM access_usergroup
+ JOIN access_groupagent USING ( groupnum )
+ WHERE usernum = ?"
+ ) or die dbh->errstr;
+ $sth->execute($self->usernum) or die $sth->errstr;
+ map { $_->[0] } @{ $sth->fetchall_arrayref };
+}
+
+=item agentnums_href
+
+Returns a hashref of agentnums this user can view.
+
+=cut
+
+sub agentnums_href {
+ my $self = shift;
+ scalar( { map { $_ => 1 } $self->agentnums } );
+}
+
+=item agentnums_sql [ HASHREF | OPTION => VALUE ... ]
+
+Returns an sql fragement to select only agentnums this user can view.
+
+Options are passed as a hashref or a list. Available options are:
+
+=over 4
+
+=item null
+
+The frament will also allow the selection of null agentnums.
+
+=item null_right
+
+The fragment will also allow the selection of null agentnums if the current
+user has the provided access right
+
+=item table
+
+Optional table name in which agentnum is being checked. Sometimes required to
+resolve 'column reference "agentnum" is ambiguous' errors.
+
+=back
+
+=cut
+
+sub agentnums_sql {
+ my( $self ) = shift;
+ my %opt = ref($_[0]) ? %{$_[0]} : @_;
+
+ my $agentnum = $opt{'table'} ? $opt{'table'}.'.agentnum' : 'agentnum';
+
+# my @agentnums = map { "$agentnum = $_" } $self->agentnums;
+ my @agentnums = ();
+ push @agentnums, "$agentnum IN (". join(',', $self->agentnums). ')';
+
+ push @agentnums, "$agentnum IS NULL"
+ if $opt{'null'}
+ || ( $opt{'null_right'} && $self->access_right($opt{'null_right'}) );
+
+ return ' 1 = 0 ' unless scalar(@agentnums);
+ '( '. join( ' OR ', @agentnums ). ' )';
+
+}
+
+=item agentnum
+
+Returns true if the user can view the specified agent.
+
+=cut
+
+sub agentnum {
+ my( $self, $agentnum ) = @_;
+ my $sth = dbh->prepare(
+ "SELECT COUNT(*) FROM access_usergroup
+ JOIN access_groupagent USING ( groupnum )
+ WHERE usernum = ? AND agentnum = ?"
+ ) or die dbh->errstr;
+ $sth->execute($self->usernum, $agentnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item agents
+
+Returns the list of agents this user can view (via group membership), as
+FS::agent objects.
+
+=cut
+
+sub agents {
+ my $self = shift;
+ qsearch({
+ 'table' => 'agent',
+ 'hashref' => { disabled=>'' },
+ 'extra_sql' => ' AND '. $self->agentnums_sql,
+ });
+}
+
+=item access_right RIGHTNAME | LISTREF
+
+Given a right name or a list reference of right names, returns true if this
+user has this right, or, for a list, one of the rights (currently via group
+membership, eventually also via user overrides).
+
+=cut
+
+sub access_right {
+ my( $self, $rightname ) = @_;
+
+ $rightname = [ $rightname ] unless ref($rightname);
+
+ warn "$me access_right called on ". join(', ', @$rightname). "\n"
+ if $DEBUG;
+
+ #some caching of ACL requests for low-hanging fruit perf improvement
+ #since we get a new $CurrentUser object each page view there shouldn't be any
+ #issues with stickiness
+ if ( $self->{_ACLcache} ) {
+
+ unless ( grep !exists($self->{_ACLcache}{$_}), @$rightname ) {
+ warn "$me ACL cache hit for ". join(', ', @$rightname). "\n"
+ if $DEBUG;
+ return grep $self->{_ACLcache}{$_}, @$rightname
+ }
+
+ warn "$me ACL cache miss for ". join(', ', @$rightname). "\n"
+ if $DEBUG;
+
+ } else {
+
+ warn "initializing ACL cache\n"
+ if $DEBUG;
+ $self->{_ACLcache} = {};
+
+ }
+
+ my $has_right = ' rightname IN ('. join(',', map '?', @$rightname ). ') ';
+
+ my $sth = dbh->prepare("
+ SELECT groupnum FROM access_usergroup
+ LEFT JOIN access_group USING ( groupnum )
+ LEFT JOIN access_right
+ ON ( access_group.groupnum = access_right.rightobjnum )
+ WHERE usernum = ?
+ AND righttype = 'FS::access_group'
+ AND $has_right
+ LIMIT 1
+ ") or die dbh->errstr;
+ $sth->execute($self->usernum, @$rightname) or die $sth->errstr;
+ my $row = $sth->fetchrow_arrayref;
+
+ my $return = $row ? $row->[0] : '';
+
+ #just caching the single-rightname hits should be enough of a win for now
+ if ( scalar(@$rightname) == 1 ) {
+ $self->{_ACLcache}{${$rightname}[0]} = $return;
+ }
+
+ $return;
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/access_user_pref.pm b/FS/FS/access_user_pref.pm
new file mode 100644
index 0000000..a445d31
--- /dev/null
+++ b/FS/FS/access_user_pref.pm
@@ -0,0 +1,129 @@
+package FS::access_user_pref;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_user_pref - Object methods for access_user_pref records
+
+=head1 SYNOPSIS
+
+ use FS::access_user_pref;
+
+ $record = new FS::access_user_pref \%hash;
+ $record = new FS::access_user_pref { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_user_pref object represents an per-user preference. Preferenaces
+are also used to store transient state information (server-side "cookies").
+FS::access_user_pref inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item prefnum - primary key
+
+=item usernum - Internal access user (see L<FS::access_user>)
+
+=item prefname -
+
+=item prefvalue -
+
+=item expiration -
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new preference. To add the preference to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'access_user_pref'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid preference. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('prefnum')
+ || $self->ut_number('usernum')
+ || $self->ut_text('prefname')
+ #|| $self->ut_textn('prefvalue')
+ || $self->ut_anything('prefvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::access_user>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/access_usergroup.pm b/FS/FS/access_usergroup.pm
new file mode 100644
index 0000000..8e83060
--- /dev/null
+++ b/FS/FS/access_usergroup.pm
@@ -0,0 +1,145 @@
+package FS::access_usergroup;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::access_user;
+use FS::access_group;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::access_usergroup - Object methods for access_usergroup records
+
+=head1 SYNOPSIS
+
+ use FS::access_usergroup;
+
+ $record = new FS::access_usergroup \%hash;
+ $record = new FS::access_usergroup { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::access_usergroup object represents an internal access user's membership
+in a group. FS::access_usergroup inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item usergroupnum - primary key
+
+=item usernum -
+
+=item groupnum -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'access_usergroup'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('usergroupnum')
+ || $self->ut_number('usernum')
+ || $self->ut_number('groupnum')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item access_user
+
+=cut
+
+sub access_user {
+ my $self = shift;
+ qsearchs( 'access_user', { 'usernum' => $self->usernum } );
+}
+
+=item access_group
+
+=cut
+
+sub access_group {
+ my $self = shift;
+ qsearchs( 'access_group', { 'groupnum' => $self->groupnum } );
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/acct_rt_transaction.pm b/FS/FS/acct_rt_transaction.pm
new file mode 100644
index 0000000..ef0a275
--- /dev/null
+++ b/FS/FS/acct_rt_transaction.pm
@@ -0,0 +1,316 @@
+package FS::acct_rt_transaction;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::acct_rt_transaction - Object methods for acct_rt_transaction records
+
+=head1 SYNOPSIS
+
+ use FS::acct_rt_transaction;
+
+ $record = new FS::acct_rt_transaction \%hash;
+ $record = new FS::acct_rt_transaction { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::acct_rt_transaction object represents an application of time
+from a rt transaction to a svc_acct. FS::acct_rt_transaction inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item svcrtid
+
+Primary key
+
+=item svcnum
+
+The svcnum of the svc_acct to which the time applies
+
+=item transaction_id
+
+The id of the rt transtaction from which the time applies
+
+=item seconds
+
+The amount of time applied from tickets
+
+=item support
+
+The amount of time applied to support services
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new acct_rt_transaction. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'acct_rt_transaction'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+ my( $self, %options ) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::insert($options{options} ? %{$options{options}} : ());
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $svc_acct = qsearchs('svc_acct', {'svcnum' => $self->svcnum});
+ unless ($svc_acct) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't find svc_acct " . $self->svcnum;
+ }
+
+ $error = $svc_acct->decrement_seconds($self->support);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error incrementing service seconds: $error";
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $svc_acct = qsearchs('svc_acct', {'svcnum' => $self->svcnum});
+ unless ($svc_acct) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't find svc_acct " . $self->svcnum;
+ }
+
+ $error = $svc_acct->increment_seconds($self->support);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error incrementing service seconds: $error";
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid acct_rt_transaction. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my ($selfref) = $self->hashref;
+
+ my $error =
+ $self->ut_numbern('svcrtid')
+ || $self->ut_numbern('svcnum')
+ || $self->ut_number('transaction_id')
+ || $self->ut_numbern('_date')
+ || $self->ut_snumber('seconds')
+ || $self->ut_snumber('support')
+ ;
+ return $error if $error;
+
+ $self->_date(time) unless $self->_date;
+
+ if ($selfref->{custnum}) {
+ my $conf = new FS::Conf;
+ my %packages = map { $_ => 1 } $conf->config('support_packages');
+ my $cust_main = qsearchs('cust_main',{ 'custnum' => $selfref->{custnum} } );
+ return "Invalid custnum: " . $selfref->{custnum} unless $cust_main;
+
+ my (@svcs) = map { $_->svcnum } $cust_main->support_services;
+ return "svcnum ". $self->svcnum. " invalid for custnum ".$selfref->{custnum}
+ unless (!$self->svcnum || scalar(grep { $_ == $self->svcnum } @svcs));
+
+ $self->svcnum($svcs[0]) unless $self->svcnum;
+ return "Can't find support service for custnum ".$selfref->{custnum}
+ unless $self->svcnum;
+ }
+
+ $self->SUPER::check;
+}
+
+=item creator
+
+Returns the creator of the RT transaction associated with this object.
+
+=cut
+
+sub creator {
+ my $self = shift;
+ FS::TicketSystem->transaction_creator($self->transaction_id);
+}
+
+=item ticketid
+
+Returns the number of the RT ticket associated with this object.
+
+=cut
+
+sub ticketid {
+ my $self = shift;
+ FS::TicketSystem->transaction_ticketid($self->transaction_id);
+}
+
+=item subject
+
+Returns the subject of the RT ticket associated with this object.
+
+=cut
+
+sub subject {
+ my $self = shift;
+ FS::TicketSystem->transaction_subject($self->transaction_id);
+}
+
+=item status
+
+Returns the status of the RT ticket associated with this object.
+
+=cut
+
+sub status {
+ my $self = shift;
+ FS::TicketSystem->transaction_status($self->transaction_id);
+}
+
+=item batch_insert SVC_ACCT_RT_TRANSACTION_OBJECT, ...
+
+Class method which inserts multiple time applications. Takes a list of
+FS::acct_rt_transaction objects. If there is an error inserting any
+application, the entire transaction is rolled back, i.e. all time is applied
+or none is.
+
+For example:
+
+ my $errors = FS::acct_rt_transaction->batch_insert(@transactions);
+ if ( $error ) {
+ #success; all payments were inserted
+ } else {
+ #failure; no payments were inserted.
+ }
+
+=cut
+
+sub batch_insert {
+ my $self = shift; #class method
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error;
+ foreach (@_) {
+ $error = $_->insert;
+ last if $error;
+ }
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ } else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ }
+
+ $error;
+
+}
+
+=back
+
+=head1 BUGS
+
+Possibly the delete method or others.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/acct_snarf.pm b/FS/FS/acct_snarf.pm
new file mode 100644
index 0000000..b4e88bf
--- /dev/null
+++ b/FS/FS/acct_snarf.pm
@@ -0,0 +1,128 @@
+package FS::acct_snarf;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record;
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::acct_snarf - Object methods for acct_snarf records
+
+=head1 SYNOPSIS
+
+ use FS::acct_snarf;
+
+ $record = new FS::acct_snarf \%hash;
+ $record = new FS::acct_snarf { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::svc_acct object represents an external mail account, typically for
+download of mail. FS::acct_snarf inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item snarfnum - primary key
+
+=item svcnum - Account (see L<FS::svc_acct>)
+
+=item machine - external machine to download mail from
+
+=item protocol - protocol (pop3, imap, etc.)
+
+=item username - external login username
+
+=item _password - external login password
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'acct_snarf'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid external mail account. If
+there is an error, returns the error, otherwise returns false. Called by the
+insert and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+ my $error =
+ $self->ut_numbern('snarfnum')
+ || $self->ut_number('svcnum')
+ || $self->ut_foreign_key('svcnum', 'svc_acct', 'svcnum')
+ || $self->ut_domain('machine')
+ || $self->ut_alphan('protocol')
+ || $self->ut_textn('username')
+ ;
+ return $error if $error;
+
+ $self->_password =~ /^[^\t\n]*$/ or return "illegal password";
+ $self->_password($1);
+
+ ''; #no error
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
new file mode 100755
index 0000000..0fe2476
--- /dev/null
+++ b/FS/FS/addr_block.pm
@@ -0,0 +1,385 @@
+package FS::addr_block;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs qsearch dbh );
+use FS::router;
+use FS::svc_broadband;
+use FS::Conf;
+use NetAddr::IP;
+use Carp qw( carp );
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::addr_block - Object methods for addr_block records
+
+=head1 SYNOPSIS
+
+ use FS::addr_block;
+
+ $record = new FS::addr_block \%hash;
+ $record = new FS::addr_block { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::addr_block record describes an address block assigned for broadband
+access. FS::addr_block inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item blocknum - primary key, used in FS::svc_broadband to associate
+services to the block.
+
+=item routernum - the router (see FS::router) to which this
+block is assigned.
+
+=item ip_gateway - the gateway address used by customers within this block.
+
+=item ip_netmask - the netmask of the block, expressed as an integer.
+
+=item manual_flag - prohibit automatic ip assignment from this block when true.
+
+=item agentnum - optional agent number (see L<FS::agent>)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Create a new record. To add the record to the database, see "insert".
+
+=cut
+
+sub table { 'addr_block'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
+
+sub delete {
+ my $self = shift;
+ return 'Block must be deallocated before deletion'
+ if $self->router;
+
+ $self->SUPER::delete;
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+At present it's not possible to reallocate a block to a different router
+except by deallocating it first, which requires that none of its addresses
+be assigned. This is probably as it should be.
+
+sub replace_check {
+ my ( $new, $old ) = ( shift, shift );
+
+ unless($new->routernum == $old->routernum) {
+ my @svc = $self->svc_broadband;
+ if (@svc) {
+ return 'Block has assigned addresses: '.
+ join ', ', map {$_->ip_addr} @svc;
+ }
+
+ return 'Block is already allocated'
+ if($new->routernum && $old->routernum);
+
+ }
+
+ '';
+}
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_number('routernum')
+ || $self->ut_ip('ip_gateway')
+ || $self->ut_number('ip_netmask')
+ || $self->ut_enum('manual_flag', [ '', 'Y' ])
+ || $self->ut_agentnum_acl('agentnum', 'Broadband global configuration')
+ ;
+ return $error if $error;
+
+
+ # A routernum of 0 indicates an unassigned block and is allowed
+ return "Unknown routernum"
+ if ($self->routernum and not $self->router);
+
+ my $self_addr = $self->NetAddr;
+ return "Cannot parse address: ". $self->ip_gateway . '/' . $self->ip_netmask
+ unless $self_addr;
+
+ if (not $self->blocknum) {
+ my @block = grep {
+ my $block_addr = $_->NetAddr;
+ if($block_addr->contains($self_addr)
+ or $self_addr->contains($block_addr)) { $_; };
+ } qsearch( 'addr_block', {});
+ foreach(@block) {
+ return "Block intersects existing block ".$_->ip_gateway."/".$_->ip_netmask;
+ }
+ }
+
+ $self->SUPER::check;
+}
+
+
+=item router
+
+Returns the FS::router object corresponding to this object. If the
+block is unassigned, returns undef.
+
+=cut
+
+sub router {
+ my $self = shift;
+ return qsearchs('router', { routernum => $self->routernum });
+}
+
+=item svc_broadband
+
+Returns a list of FS::svc_broadband objects associated
+with this object.
+
+=cut
+
+sub svc_broadband {
+ my $self = shift;
+ return qsearch('svc_broadband', { blocknum => $self->blocknum });
+}
+
+=item NetAddr
+
+Returns a NetAddr::IP object for this block's address and netmask.
+
+=cut
+
+sub NetAddr {
+ my $self = shift;
+ new NetAddr::IP ($self->ip_gateway, $self->ip_netmask);
+}
+
+=item cidr
+
+Returns a CIDR string for this block's address and netmask, i.e. 10.4.20.0/24
+
+=cut
+
+sub cidr {
+ my $self = shift;
+ $self->NetAddr->cidr;
+}
+
+=item next_free_addr
+
+Returns a NetAddr::IP object corresponding to the first unassigned address
+in the block (other than the network, broadcast, or gateway address). If
+there are no free addresses, returns false. There are never free addresses
+when manual_flag is true.
+
+=cut
+
+sub next_free_addr {
+ my $self = shift;
+
+ return '' if $self->manual_flag;
+
+ my $conf = new FS::Conf;
+ my @excludeaddr = $conf->config('exclude_ip_addr');
+
+my @used =
+( (map { $_->NetAddr->addr }
+ ($self,
+ qsearch('svc_broadband', { blocknum => $self->blocknum }))
+ ), @excludeaddr
+);
+
+ my @free = $self->NetAddr->hostenum;
+ while (my $ip = shift @free) {
+ if (not grep {$_ eq $ip->addr;} @used) { return $ip; };
+ }
+
+ '';
+
+}
+
+=item allocate -- deprecated
+
+Allocates this address block to a router. Takes an FS::router object
+as an argument.
+
+At present it's not possible to reallocate a block to a different router
+except by deallocating it first, which requires that none of its addresses
+be assigned. This is probably as it should be.
+
+=cut
+
+sub allocate {
+ my ($self, $router) = @_;
+ carp "deallocate deprecated -- use replace";
+
+ return 'Block must be allocated to a router'
+ unless(ref $router eq 'FS::router');
+
+ my $new = new FS::addr_block {$self->hash};
+ $new->routernum($router->routernum);
+ return $new->replace($self);
+
+}
+
+=item deallocate -- deprecated
+
+Deallocates the block (i.e. sets the routernum to 0). If any addresses in the
+block are assigned to services, it fails.
+
+=cut
+
+sub deallocate {
+ carp "deallocate deprecated -- use replace";
+ my $self = shift;
+
+ my $new = new FS::addr_block {$self->hash};
+ $new->routernum(0);
+ return $new->replace($self);
+}
+
+=item split_block
+
+Splits this address block into two equal blocks, occupying the same space as
+the original block. The first of the two will also have the same blocknum.
+The gateway address of each block will be set to the first usable address, i.e.
+(network address)+1. Since this method is designed for use on unallocated
+blocks, this is probably the correct behavior.
+
+(At present, splitting allocated blocks is disallowed. Anyone who wants to
+implement this is reminded that each split costs three addresses, and any
+customers who were using these addresses will have to be moved; depending on
+how full the block was before being split, they might have to be moved to a
+different block. Anyone who I<still> wants to implement it is asked to tie it
+to a configuration switch so that site admins can disallow it.)
+
+=cut
+
+sub split_block {
+
+ # We should consider using Attribute::Handlers/Aspect/Hook::LexWrap/
+ # something to atomicize functions, so that we can say
+ #
+ # sub split_block : atomic {
+ #
+ # instead of repeating all this AutoCommit verbage in every
+ # sub that does more than one database operation.
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $self = shift;
+ my $error;
+
+ if ($self->router) {
+ return 'Block is already allocated';
+ }
+
+ #TODO: Smallest allowed block should be a config option.
+ if ($self->NetAddr->masklen() ge 30) {
+ return 'Cannot split blocks with a mask length >= 30';
+ }
+
+ my (@new, @ip);
+ $ip[0] = $self->NetAddr;
+ @ip = map {$_->first()} $ip[0]->split($self->ip_netmask + 1);
+
+ foreach (0,1) {
+ $new[$_] = new FS::addr_block {$self->hash};
+ $new[$_]->ip_gateway($ip[$_]->addr);
+ $new[$_]->ip_netmask($ip[$_]->masklen);
+ }
+
+ $new[1]->blocknum('');
+
+ $error = $new[0]->replace($self);
+ if ($error) {
+ $dbh->rollback;
+ return $error;
+ }
+
+ $error = $new[1]->insert;
+ if ($error) {
+ $dbh->rollback;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return '';
+}
+
+=item merge
+
+To be implemented.
+
+=item agent
+
+Returns the agent (see L<FS::agent>) for this address block, if one exists.
+
+=cut
+
+sub agent {
+ qsearchs('agent', { 'agentnum' => shift->agentnum } );
+}
+
+=item label
+
+Returns text including the router name, gateway ip, and netmask for this
+block.
+
+=cut
+
+sub label {
+ my $self = shift;
+ my $router = $self->router;
+ ($router ? $router->routername : '(unallocated)'). ':'. $self->NetAddr;
+}
+
+=back
+
+=head1 BUGS
+
+Minimum block size should be a config option. It's hardcoded at /30 right
+now because that's the smallest block that makes any sense at all.
+
+=cut
+
+1;
+
diff --git a/FS/FS/agent.pm b/FS/FS/agent.pm
new file mode 100644
index 0000000..ff0a2b1
--- /dev/null
+++ b/FS/FS/agent.pm
@@ -0,0 +1,464 @@
+package FS::agent;
+
+use strict;
+use vars qw( @ISA );
+#use Crypt::YAPassGen;
+use FS::Record qw( dbh qsearch qsearchs );
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::agent_type;
+use FS::reg_code;
+use FS::TicketSystem;
+
+@ISA = qw( FS::m2m_Common FS::Record );
+
+=head1 NAME
+
+FS::agent - Object methods for agent records
+
+=head1 SYNOPSIS
+
+ use FS::agent;
+
+ $record = new FS::agent \%hash;
+ $record = new FS::agent { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $agent_type = $record->agent_type;
+
+ $hashref = $record->pkgpart_hashref;
+ #may purchase $pkgpart if $hashref->{$pkgpart};
+
+=head1 DESCRIPTION
+
+An FS::agent object represents an agent. Every customer has an agent. Agents
+can be used to track things like resellers or salespeople. FS::agent inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item agentnum - primary key (assigned automatically for new agents)
+
+=item agent - Text name of this agent
+
+=item typenum - Agent type (see L<FS::agent_type>)
+
+=item ticketing_queueid - Ticketing Queue
+
+=item invoice_template - Invoice template name
+
+=item agent_custnum - Optional agent customer (see L<FS::cust_main>)
+
+=item disabled - Disabled flag, empty or 'Y'
+
+=item prog - Deprecated (never used)
+
+=item freq - Deprecated (never used)
+
+=item username - (Deprecated) Username for the Agent interface
+
+=item _password - (Deprecated) Password for the Agent interface
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new agent. To add the agent to the database, see L<"insert">.
+
+=cut
+
+sub table { 'agent'; }
+
+=item insert
+
+Adds this agent to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this agent from the database. Only agents with no customers can be
+deleted. If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete an agent with customers!"
+ if qsearch( 'cust_main', { 'agentnum' => $self->agentnum } );
+
+ $self->SUPER::delete;
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid agent. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('agentnum')
+ || $self->ut_text('agent')
+ || $self->ut_number('typenum')
+ || $self->ut_numbern('freq')
+ || $self->ut_textn('prog')
+ || $self->ut_textn('invoice_template')
+ || $self->ut_foreign_keyn('agent_custnum', 'cust_main', 'custnum' )
+ ;
+ return $error if $error;
+
+ if ( $self->dbdef_table->column('disabled') ) {
+ $error = $self->ut_enum('disabled', [ '', 'Y' ] );
+ return $error if $error;
+ }
+
+ if ( $self->dbdef_table->column('username') ) {
+ $error = $self->ut_alphan('username');
+ return $error if $error;
+ if ( length($self->username) ) {
+ my $conflict = qsearchs('agent', { 'username' => $self->username } );
+ return 'duplicate agent username (with '. $conflict->agent. ')'
+ if $conflict && $conflict->agentnum != $self->agentnum;
+ $error = $self->ut_text('password'); # ut_text... arbitrary choice
+ } else {
+ $self->_password('');
+ }
+ }
+
+ return "Unknown typenum!"
+ unless $self->agent_type;
+
+ $self->SUPER::check;
+}
+
+=item agent_type
+
+Returns the FS::agent_type object (see L<FS::agent_type>) for this agent.
+
+=cut
+
+sub agent_type {
+ my $self = shift;
+ qsearchs( 'agent_type', { 'typenum' => $self->typenum } );
+}
+
+=item agent_cust_main
+
+Returns the FS::cust_main object (see L<FS::cust_main>), if any, for this
+agent.
+
+=cut
+
+sub agent_cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->agent_custnum } );
+}
+
+=item pkgpart_hashref
+
+Returns a hash reference. The keys of the hash are pkgparts. The value is
+true if this agent may purchase the specified package definition. See
+L<FS::part_pkg>.
+
+=cut
+
+sub pkgpart_hashref {
+ my $self = shift;
+ $self->agent_type->pkgpart_hashref;
+}
+
+=item ticketing_queue
+
+Returns the queue name corresponding with the id from the I<ticketing_queueid>
+field, or the empty string.
+
+=cut
+
+sub ticketing_queue {
+ my $self = shift;
+ FS::TicketSystem->queue($self->ticketing_queueid);
+};
+
+=item num_prospect_cust_main
+
+Returns the number of prospects (customers with no packages ever ordered) for
+this agent.
+
+=cut
+
+sub num_prospect_cust_main {
+ shift->num_sql(FS::cust_main->prospect_sql);
+}
+
+sub num_sql {
+ my( $self, $sql ) = @_;
+ my $statement = "SELECT COUNT(*) FROM cust_main WHERE agentnum = ? AND $sql";
+ my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
+ $sth->execute($self->agentnum) or die $sth->errstr. " executing $statement";
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item prospect_cust_main
+
+Returns the prospects (customers with no packages ever ordered) for this agent,
+as cust_main objects.
+
+=cut
+
+sub prospect_cust_main {
+ shift->cust_main_sql(FS::cust_main->prospect_sql);
+}
+
+sub cust_main_sql {
+ my( $self, $sql ) = @_;
+ qsearch( 'cust_main',
+ { 'agentnum' => $self->agentnum },
+ '',
+ " AND $sql"
+ );
+}
+
+=item num_active_cust_main
+
+Returns the number of active customers for this agent (customers with active
+recurring packages).
+
+=cut
+
+sub num_active_cust_main {
+ shift->num_sql(FS::cust_main->active_sql);
+}
+
+=item active_cust_main
+
+Returns the active customers for this agent, as cust_main objects.
+
+=cut
+
+sub active_cust_main {
+ shift->cust_main_sql(FS::cust_main->active_sql);
+}
+
+=item num_inactive_cust_main
+
+Returns the number of inactive customers for this agent (customers with no
+active recurring packages, but otherwise unsuspended/uncancelled).
+
+=cut
+
+sub num_inactive_cust_main {
+ shift->num_sql(FS::cust_main->inactive_sql);
+}
+
+=item inactive_cust_main
+
+Returns the inactive customers for this agent, as cust_main objects.
+
+=cut
+
+sub inactive_cust_main {
+ shift->cust_main_sql(FS::cust_main->inactive_sql);
+}
+
+
+=item num_susp_cust_main
+
+Returns the number of suspended customers for this agent.
+
+=cut
+
+sub num_susp_cust_main {
+ shift->num_sql(FS::cust_main->susp_sql);
+}
+
+=item susp_cust_main
+
+Returns the suspended customers for this agent, as cust_main objects.
+
+=cut
+
+sub susp_cust_main {
+ shift->cust_main_sql(FS::cust_main->susp_sql);
+}
+
+=item num_cancel_cust_main
+
+Returns the number of cancelled customer for this agent.
+
+=cut
+
+sub num_cancel_cust_main {
+ shift->num_sql(FS::cust_main->cancel_sql);
+}
+
+=item cancel_cust_main
+
+Returns the cancelled customers for this agent, as cust_main objects.
+
+=cut
+
+sub cancel_cust_main {
+ shift->cust_main_sql(FS::cust_main->cancel_sql);
+}
+
+=item num_active_cust_pkg
+
+Returns the number of active customer packages for this agent.
+
+=cut
+
+sub num_active_cust_pkg {
+ shift->num_pkg_sql(FS::cust_pkg->active_sql);
+}
+
+sub num_pkg_sql {
+ my( $self, $sql ) = @_;
+ my $statement =
+ "SELECT COUNT(*) FROM cust_pkg LEFT JOIN cust_main USING ( custnum )".
+ " WHERE agentnum = ? AND $sql";
+ my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
+ $sth->execute($self->agentnum) or die $sth->errstr. "executing $statement";
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item num_inactive_cust_pkg
+
+Returns the number of inactive customer packages (one-time packages otherwise
+unsuspended/uncancelled) for this agent.
+
+=cut
+
+sub num_inactive_cust_pkg {
+ shift->num_pkg_sql(FS::cust_pkg->inactive_sql);
+}
+
+=item num_susp_cust_pkg
+
+Returns the number of suspended customer packages for this agent.
+
+=cut
+
+sub num_susp_cust_pkg {
+ shift->num_pkg_sql(FS::cust_pkg->susp_sql);
+}
+
+=item num_cancel_cust_pkg
+
+Returns the number of cancelled customer packages for this agent.
+
+=cut
+
+sub num_cancel_cust_pkg {
+ shift->num_pkg_sql(FS::cust_pkg->cancel_sql);
+}
+
+=item generate_reg_codes NUM PKGPART_ARRAYREF
+
+Generates the specified number of registration codes, allowing purchase of the
+specified package definitions. Returns an array reference of the newly
+generated codes, or a scalar error message.
+
+=cut
+
+#false laziness w/prepay_credit::generate
+sub generate_reg_codes {
+ my( $self, $num, $pkgparts ) = @_;
+
+ my @codeset = ( 'A'..'Z' );
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my @codes = ();
+ for ( 1 ... $num ) {
+ my $reg_code = new FS::reg_code {
+ 'agentnum' => $self->agentnum,
+ 'code' => join('', map($codeset[int(rand $#codeset)], (0..7) ) ),
+ };
+ my $error = $reg_code->insert($pkgparts);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ push @codes, $reg_code->code;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ \@codes;
+
+}
+
+=item num_reg_code
+
+Returns the number of unused registration codes for this agent.
+
+=cut
+
+sub num_reg_code {
+ my $self = shift;
+ my $sth = dbh->prepare(
+ "SELECT COUNT(*) FROM reg_code WHERE agentnum = ?"
+ ) or die dbh->errstr;
+ $sth->execute($self->agentnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item num_prepay_credit
+
+Returns the number of unused prepaid cards for this agent.
+
+=cut
+
+sub num_prepay_credit {
+ my $self = shift;
+ my $sth = dbh->prepare(
+ "SELECT COUNT(*) FROM prepay_credit WHERE agentnum = ?"
+ ) or die dbh->errstr;
+ $sth->execute($self->agentnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::agent_type>, L<FS::cust_main>, L<FS::part_pkg>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/agent_payment_gateway.pm b/FS/FS/agent_payment_gateway.pm
new file mode 100644
index 0000000..bd99d0c
--- /dev/null
+++ b/FS/FS/agent_payment_gateway.pm
@@ -0,0 +1,139 @@
+package FS::agent_payment_gateway;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::payment_gateway;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::agent_payment_gateway - Object methods for agent_payment_gateway records
+
+=head1 SYNOPSIS
+
+ use FS::agent_payment_gateway;
+
+ $record = new FS::agent_payment_gateway \%hash;
+ $record = new FS::agent_payment_gateway { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::agent_payment_gateway object represents a payment gateway override for
+a specific agent. FS::agent_payment_gateway inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item agentgatewaynum - primary key
+
+=item agentnum -
+
+=item gatewaynum -
+
+=item cardtype -
+
+=item taxclass -
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new override. To add the override to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'agent_payment_gateway'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid override. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('agentgatewaynum')
+ || $self->ut_foreign_key('agentnum', 'agent', 'agentnum')
+ || $self->ut_foreign_key('gatewaynum', 'payment_gateway', 'gatewaynum' )
+ || $self->ut_textn('cardtype')
+ || $self->ut_textn('taxclass')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item payment_gateway
+
+=cut
+
+sub payment_gateway {
+ my $self = shift;
+ qsearchs('payment_gateway', { 'gatewaynum' => $self->gatewaynum } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::payment_gateway>, L<FS::agent>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/agent_type.pm b/FS/FS/agent_type.pm
new file mode 100644
index 0000000..2660bb4
--- /dev/null
+++ b/FS/FS/agent_type.pm
@@ -0,0 +1,191 @@
+package FS::agent_type;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch );
+use FS::m2m_Common;
+use FS::agent;
+use FS::type_pkgs;
+
+@ISA = qw( FS::m2m_Common FS::Record );
+
+=head1 NAME
+
+FS::agent_type - Object methods for agent_type records
+
+=head1 SYNOPSIS
+
+ use FS::agent_type;
+
+ $record = new FS::agent_type \%hash;
+ $record = new FS::agent_type { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $hashref = $record->pkgpart_hashref;
+ #may purchase $pkgpart if $hashref->{$pkgpart};
+
+ @type_pkgs = $record->type_pkgs;
+
+ @pkgparts = $record->pkgpart;
+
+=head1 DESCRIPTION
+
+An FS::agent_type object represents an agent type. Every agent (see
+L<FS::agent>) has an agent type. Agent types define which packages (see
+L<FS::part_pkg>) may be purchased by customers (see L<FS::cust_main>), via
+FS::type_pkgs records (see L<FS::type_pkgs>). FS::agent_type inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item typenum - primary key (assigned automatically for new agent types)
+
+=item atype - Text name of this agent type
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new agent type. To add the agent type to the database, see
+L<"insert">.
+
+=cut
+
+sub table { 'agent_type'; }
+
+=item insert
+
+Adds this agent type to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this agent type from the database. Only agent types with no agents
+can be deleted. If there is an error, returns the error, otherwise returns
+false.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete an agent_type with agents!"
+ if qsearch( 'agent', { 'typenum' => $self->typenum } );
+
+ $self->SUPER::delete;
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid agent type. If there is an
+error, returns the error, otherwise returns false. Called by the insert and
+replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('typenum')
+ or $self->ut_text('atype')
+ or $self->SUPER::check;
+
+}
+
+=item pkgpart_hashref
+
+Returns a hash reference. The keys of the hash are pkgparts. The value is
+true iff this agent may purchase the specified package definition. See
+L<FS::part_pkg>.
+
+=cut
+
+sub pkgpart_hashref {
+ my $self = shift;
+ my %pkgpart;
+ #$pkgpart{$_}++ foreach $self->pkgpart;
+ # not compatible w/5.004_04 (fixed in 5.004_05)
+ foreach ( $self->pkgpart ) { $pkgpart{$_}++; }
+ \%pkgpart;
+}
+
+=item type_pkgs
+
+Returns all FS::type_pkgs objects (see L<FS::type_pkgs>) for this agent type.
+
+=cut
+
+sub type_pkgs {
+ my $self = shift;
+ qsearch('type_pkgs', { 'typenum' => $self->typenum } );
+}
+
+=item type_pkgs_enabled
+
+Returns all FS::type_pkg objects (see L<FS::type_pkgs>) that link to enabled
+package definitions (see L<FS::part_pkg>).
+
+An additional strange feature is that the returned type_pkg objects also have
+all fields of the associated part_pkg object.
+
+=cut
+
+sub type_pkgs_enabled {
+ my $self = shift;
+ qsearch({
+ 'table' => 'type_pkgs',
+ 'addl_from' => 'JOIN part_pkg USING ( pkgpart )',
+ 'hashref' => { 'typenum' => $self->typenum },
+ 'extra_sql' => " AND ( disabled = '' OR disabled IS NULL )".
+ " ORDER BY pkg",
+ });
+}
+
+=item pkgpart
+
+Returns the pkgpart of all package definitions (see L<FS::part_pkg>) for this
+agent type.
+
+=cut
+
+sub pkgpart {
+ my $self = shift;
+ map $_->pkgpart, $self->type_pkgs;
+}
+
+=back
+
+=head1 BUGS
+
+type_pkgs_enabled should order itself by something (pkg?)
+
+type_pkgs_enabled should populate something that caches for the part_pkg method
+rather than add fields to this object, right? In fact we need a "poop" object
+framework that does that automatically for any joined search at some point....
+right?
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::agent>, L<FS::type_pkgs>, L<FS::cust_main>,
+L<FS::part_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/banned_pay.pm b/FS/FS/banned_pay.pm
new file mode 100644
index 0000000..1ad87f5
--- /dev/null
+++ b/FS/FS/banned_pay.pm
@@ -0,0 +1,136 @@
+package FS::banned_pay;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::UID qw( getotaker );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::banned_pay - Object methods for banned_pay records
+
+=head1 SYNOPSIS
+
+ use FS::banned_pay;
+
+ $record = new FS::banned_pay \%hash;
+ $record = new FS::banned_pay { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::banned_pay object represents an banned credit card or ACH account.
+FS::banned_pay inherits from FS::Record. The following fields are currently
+supported:
+
+=over 4
+
+=item bannum - primary key
+
+=item payby - I<CARD> or I<CHEK>
+
+=item payinfo - fingerprint of banned card (base64-encoded MD5 digest)
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item otaker - order taker (assigned automatically, see L<FS::UID>)
+
+=item reason - reason (text)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new ban. To add the ban to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'banned_pay'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid ban. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('bannum')
+ || $self->ut_enum('payby', [ 'CARD', 'CHEK' ] )
+ || $self->ut_text('payinfo')
+ || $self->ut_numbern('_date')
+ || $self->ut_textn('reason')
+ ;
+ return $error if $error;
+
+ $self->_date(time) unless $self->_date;
+
+ $self->otaker(getotaker);
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm
new file mode 100644
index 0000000..67c5c1c
--- /dev/null
+++ b/FS/FS/cdr.pm
@@ -0,0 +1,782 @@
+package FS::cdr;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK $DEBUG );
+use Exporter;
+use Tie::IxHash;
+use Date::Parse;
+use Date::Format;
+use Time::Local;
+use FS::UID qw( dbh );
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs );
+use FS::cdr_type;
+use FS::cdr_calltype;
+use FS::cdr_carrier;
+use FS::cdr_upstream_rate;
+
+@ISA = qw(FS::Record);
+@EXPORT_OK = qw( _cdr_date_parser_maker _cdr_min_parser_maker );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::cdr - Object methods for cdr records
+
+=head1 SYNOPSIS
+
+ use FS::cdr;
+
+ $record = new FS::cdr \%hash;
+ $record = new FS::cdr { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cdr object represents an Call Data Record, typically from a telephony
+system or provider of some sort. FS::cdr inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item acctid - primary key
+
+=item calldate - Call timestamp (SQL timestamp)
+
+=item clid - Caller*ID with text
+
+=item src - Caller*ID number / Source number
+
+=item dst - Destination extension
+
+=item dcontext - Destination context
+
+=item channel - Channel used
+
+=item dstchannel - Destination channel if appropriate
+
+=item lastapp - Last application if appropriate
+
+=item lastdata - Last application data
+
+=item startdate - Start of call (UNIX-style integer timestamp)
+
+=item answerdate - Answer time of call (UNIX-style integer timestamp)
+
+=item enddate - End time of call (UNIX-style integer timestamp)
+
+=item duration - Total time in system, in seconds
+
+=item billsec - Total time call is up, in seconds
+
+=item disposition - What happened to the call: ANSWERED, NO ANSWER, BUSY
+
+=item amaflags - What flags to use: BILL, IGNORE etc, specified on a per channel basis like accountcode.
+
+=cut
+
+ #ignore the "omit" and "documentation" AMAs??
+ #AMA = Automated Message Accounting.
+ #default: Sets the system default.
+ #omit: Do not record calls.
+ #billing: Mark the entry for billing
+ #documentation: Mark the entry for documentation.
+
+=item accountcode - CDR account number to use: account
+
+=item uniqueid - Unique channel identifier (Unitel/RSLCOM Event ID)
+
+=item userfield - CDR user-defined field
+
+=item cdr_type - CDR type - see L<FS::cdr_type> (Usage = 1, S&E = 7, OC&C = 8)
+
+=item charged_party - Service number to be billed
+
+=item upstream_currency - Wholesale currency from upstream
+
+=item upstream_price - Wholesale price from upstream
+
+=item upstream_rateplanid - Upstream rate plan ID
+
+=item rated_price - Rated (or re-rated) price
+
+=item distance - km (need units field?)
+
+=item islocal - Local - 1, Non Local = 0
+
+=item calltypenum - Type of call - see L<FS::cdr_calltype>
+
+=item description - Description (cdr_type 7&8 only) (used for cust_bill_pkg.itemdesc)
+
+=item quantity - Number of items (cdr_type 7&8 only)
+
+=item carrierid - Upstream Carrier ID (see L<FS::cdr_carrier>)
+
+=cut
+
+#Telstra =1, Optus = 2, RSL COM = 3
+
+=item upstream_rateid - Upstream Rate ID
+
+=item svcnum - Link to customer service (see L<FS::cust_svc>)
+
+=item freesidestatus - NULL, done (or something)
+
+=item freesiderewritestatus - NULL, done (or something)
+
+=item cdrbatch
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new CDR. To add the CDR to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cdr'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid CDR. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+Note: Unlike most types of records, we don't want to "reject" a CDR and we want
+to process them as quickly as possible, so we allow the database to check most
+of the data.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+# we don't want to "reject" a CDR like other sorts of input...
+# my $error =
+# $self->ut_numbern('acctid')
+## || $self->ut_('calldate')
+# || $self->ut_text('clid')
+# || $self->ut_text('src')
+# || $self->ut_text('dst')
+# || $self->ut_text('dcontext')
+# || $self->ut_text('channel')
+# || $self->ut_text('dstchannel')
+# || $self->ut_text('lastapp')
+# || $self->ut_text('lastdata')
+# || $self->ut_numbern('startdate')
+# || $self->ut_numbern('answerdate')
+# || $self->ut_numbern('enddate')
+# || $self->ut_number('duration')
+# || $self->ut_number('billsec')
+# || $self->ut_text('disposition')
+# || $self->ut_number('amaflags')
+# || $self->ut_text('accountcode')
+# || $self->ut_text('uniqueid')
+# || $self->ut_text('userfield')
+# || $self->ut_numbern('cdrtypenum')
+# || $self->ut_textn('charged_party')
+## || $self->ut_n('upstream_currency')
+## || $self->ut_n('upstream_price')
+# || $self->ut_numbern('upstream_rateplanid')
+## || $self->ut_n('distance')
+# || $self->ut_numbern('islocal')
+# || $self->ut_numbern('calltypenum')
+# || $self->ut_textn('description')
+# || $self->ut_numbern('quantity')
+# || $self->ut_numbern('carrierid')
+# || $self->ut_numbern('upstream_rateid')
+# || $self->ut_numbern('svcnum')
+# || $self->ut_textn('freesidestatus')
+# || $self->ut_textn('freesiderewritestatus')
+# ;
+# return $error if $error;
+
+ $self->calldate( $self->startdate_sql )
+ if !$self->calldate && $self->startdate;
+
+ #was just for $format eq 'taqua' but can't see the harm... add something to
+ #disable if it becomes a problem
+ if ( $self->duration eq '' && $self->enddate && $self->startdate ) {
+ $self->duration( $self->enddate - $self->startdate );
+ }
+ if ( $self->billsec eq '' && $self->enddate && $self->answerdate ) {
+ $self->billsec( $self->enddate - $self->answerdate );
+ }
+
+ $self->set_charged_party;
+
+ #check the foreign keys even?
+ #do we want to outright *reject* the CDR?
+ my $error =
+ $self->ut_numbern('acctid')
+
+ #add a config option to turn these back on if someone needs 'em
+ #
+ # #Usage = 1, S&E = 7, OC&C = 8
+ # || $self->ut_foreign_keyn('cdrtypenum', 'cdr_type', 'cdrtypenum' )
+ #
+ # #the big list in appendix 2
+ # || $self->ut_foreign_keyn('calltypenum', 'cdr_calltype', 'calltypenum' )
+ #
+ # # Telstra =1, Optus = 2, RSL COM = 3
+ # || $self->ut_foreign_keyn('carrierid', 'cdr_carrier', 'carrierid' )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item set_charged_party
+
+If the charged_party field is already set, does nothing. Otherwise:
+
+If the cdr-charged_party-accountcode config option is enabled, sets the
+charged_party to the accountcode.
+
+Otherwise sets the charged_party normally: to the src field in most cases,
+or to the dst field if it is a toll free number.
+
+=cut
+
+sub set_charged_party {
+ my $self = shift;
+
+ unless ( $self->charged_party ) {
+
+ my $conf = new FS::Conf;
+
+ if ( $conf->exists('cdr-charged_party-accountcode') && $self->accountcode ){
+
+ $self->charged_party( $self->accountcode );
+
+ } else {
+
+ if ( $self->dst =~ /^(\+?1)?8[02-8]{2}/ ) {
+ $self->charged_party($self->dst);
+ } else {
+ $self->charged_party($self->src);
+ }
+
+ }
+
+ }
+
+}
+
+=item set_status_and_rated_price STATUS [ RATED_PRICE ]
+
+Sets the status to the provided string. If there is an error, returns the
+error, otherwise returns false.
+
+=cut
+
+sub set_status_and_rated_price {
+ my($self, $status, $rated_price) = @_;
+ $self->freesidestatus($status);
+ $self->rated_price($rated_price);
+ $self->replace();
+}
+
+=item calldate_unix
+
+Parses the calldate in SQL string format and returns a UNIX timestamp.
+
+=cut
+
+sub calldate_unix {
+ str2time(shift->calldate);
+}
+
+=item startdate_sql
+
+Parses the startdate in UNIX timestamp format and returns a string in SQL
+format.
+
+=cut
+
+sub startdate_sql {
+ my($sec,$min,$hour,$mday,$mon,$year) = localtime(shift->startdate);
+ $mon++;
+ $year += 1900;
+ "$year-$mon-$mday $hour:$min:$sec";
+}
+
+=item cdr_carrier
+
+Returns the FS::cdr_carrier object associated with this CDR, or false if no
+carrierid is defined.
+
+=cut
+
+my %carrier_cache = ();
+
+sub cdr_carrier {
+ my $self = shift;
+ return '' unless $self->carrierid;
+ $carrier_cache{$self->carrierid} ||=
+ qsearchs('cdr_carrier', { 'carrierid' => $self->carrierid } );
+}
+
+=item carriername
+
+Returns the carrier name (see L<FS::cdr_carrier>), or the empty string if
+no FS::cdr_carrier object is assocated with this CDR.
+
+=cut
+
+sub carriername {
+ my $self = shift;
+ my $cdr_carrier = $self->cdr_carrier;
+ $cdr_carrier ? $cdr_carrier->carriername : '';
+}
+
+=item cdr_calltype
+
+Returns the FS::cdr_calltype object associated with this CDR, or false if no
+calltypenum is defined.
+
+=cut
+
+my %calltype_cache = ();
+
+sub cdr_calltype {
+ my $self = shift;
+ return '' unless $self->calltypenum;
+ $calltype_cache{$self->calltypenum} ||=
+ qsearchs('cdr_calltype', { 'calltypenum' => $self->calltypenum } );
+}
+
+=item calltypename
+
+Returns the call type name (see L<FS::cdr_calltype>), or the empty string if
+no FS::cdr_calltype object is assocated with this CDR.
+
+=cut
+
+sub calltypename {
+ my $self = shift;
+ my $cdr_calltype = $self->cdr_calltype;
+ $cdr_calltype ? $cdr_calltype->calltypename : '';
+}
+
+=item cdr_upstream_rate
+
+Returns the upstream rate mapping (see L<FS::cdr_upstream_rate>), or the empty
+string if no FS::cdr_upstream_rate object is associated with this CDR.
+
+=cut
+
+sub cdr_upstream_rate {
+ my $self = shift;
+ return '' unless $self->upstream_rateid;
+ qsearchs('cdr_upstream_rate', { 'upstream_rateid' => $self->upstream_rateid })
+ or '';
+}
+
+=item _convergent_format COLUMN [ COUNTRYCODE ]
+
+Returns the number in COLUMN formatted as follows:
+
+If the country code does not match COUNTRYCODE (default "61"), it is returned
+unchanged.
+
+If the country code does match COUNTRYCODE (default "61"), it is removed. In
+addiiton, "0" is prepended unless the number starts with 13, 18 or 19. (???)
+
+=cut
+
+sub _convergent_format {
+ my( $self, $field ) = ( shift, shift );
+ my $countrycode = scalar(@_) ? shift : '61'; #+61 = australia
+ #my $number = $self->$field();
+ my $number = $self->get($field);
+ #if ( $number =~ s/^(\+|011)$countrycode// ) {
+ if ( $number =~ s/^\+$countrycode// ) {
+ $number = "0$number"
+ unless $number =~ /^1[389]/; #???
+ }
+ $number;
+}
+
+=item downstream_csv [ OPTION => VALUE, ... ]
+
+=cut
+
+my %export_names = (
+ 'convergent' => {},
+ 'simple' => {
+ 'name' => 'Simple',
+ 'invoice_header' => "Date,Time,Name,Destination,Duration,Price",
+ },
+ 'simple2' => {
+ 'name' => 'Simple with source',
+ 'invoice_header' => "Date,Time,Called From,Destination,Duration,Price",
+ #"Date,Time,Name,Called From,Destination,Duration,Price",
+ },
+ 'default' => {
+ 'name' => 'Default',
+ 'invoice_header' => 'Date,Time,Number,Destination,Duration,Price',
+ },
+ 'source_default' => {
+ 'name' => 'Default with source',
+ 'invoice_header' => 'Caller,Date,Time,Number,Destination,Duration,Price',
+ },
+);
+
+my %export_formats = (
+ 'convergent' => [
+ 'carriername', #CARRIER
+ sub { shift->_convergent_format('src') }, #SERVICE_NUMBER
+ sub { shift->_convergent_format('charged_party') }, #CHARGED_NUMBER
+ sub { time2str('%Y-%m-%d', shift->calldate_unix ) }, #DATE
+ sub { time2str('%T', shift->calldate_unix ) }, #TIME
+ 'billsec', #'duration', #DURATION
+ sub { shift->_convergent_format('dst') }, #NUMBER_DIALED
+ '', #XXX add (from prefixes in most recent email) #FROM_DESC
+ '', #XXX add (from prefixes in most recent email) #TO_DESC
+ 'calltypename', #CLASS_CODE
+ 'rated_price', #PRICE
+ sub { shift->rated_price ? 'Y' : 'N' }, #RATED
+ '', #OTHER_INFO
+ ],
+ 'simple' => [
+ sub { time2str('%D', shift->calldate_unix ) }, #DATE
+ sub { time2str('%r', shift->calldate_unix ) }, #TIME
+ 'userfield', #USER
+ 'dst', #NUMBER_DIALED
+ sub { sprintf('%.2fm', shift->billsec / 60 ) }, #DURATION
+ #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
+ sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
+ ],
+ 'simple2' => [
+ sub { time2str('%D', shift->calldate_unix ) }, #DATE
+ sub { time2str('%r', shift->calldate_unix ) }, #TIME
+ #'userfield', #USER
+ 'dst', #NUMBER_DIALED
+ 'src', #called from
+ sub { sprintf('%.2fm', shift->billsec / 60 ) }, #DURATION
+ #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
+ sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
+ ],
+ 'default' => [
+
+ #DATE
+ sub { time2str('%D', shift->calldate_unix ) },
+ # #time2str("%Y %b %d - %r", $cdr->calldate_unix ),
+
+ #TIME
+ sub { time2str('%r', shift->calldate_unix ) },
+ # time2str("%c", $cdr->calldate_unix), #XXX this should probably be a config option dropdown so they can select US vs- rest of world dates or whatnot
+
+ #DEST ("Number")
+ sub { my($cdr, %opt) = @_; $opt{pretty_dst} || $cdr->dst; },
+
+ #REGIONNAME ("Destination")
+ sub { my($cdr, %opt) = @_; $opt{dst_regionname}; },
+
+ #DURATION
+ sub { my($cdr, %opt) = @_;
+ $opt{minutes}. ( $opt{granularity} ? 'm' : ' call' );
+ },
+
+ #PRICE
+ sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; },
+
+ ],
+);
+$export_formats{'source_default'} = [ 'src', @{ $export_formats{'default'} }, ];
+
+sub downstream_csv {
+ my( $self, %opt ) = @_;
+
+ my $format = $opt{'format'}; # 'convergent';
+ return "Unknown format $format" unless exists $export_formats{$format};
+
+ #my $conf = new FS::Conf;
+ #$opt{'money_char'} ||= $conf->config('money_char') || '$';
+ $opt{'money_char'} ||= FS::Conf->new->config('money_char') || '$';
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+ my $csv = new Text::CSV_XS;
+
+ my @columns =
+ map {
+ ref($_) ? &{$_}($self, %opt) : $self->$_();
+ }
+ @{ $export_formats{$format} };
+
+ my $status = $csv->combine(@columns);
+ die "FS::CDR: error combining ". $csv->error_input(). "into downstream CSV"
+ unless $status;
+
+ $csv->string;
+
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item invoice_formats
+
+Returns an ordered list of key value pairs containing invoice format names
+as keys (for use with part_pkg::voip_cdr) and "pretty" format names as values.
+
+=cut
+
+sub invoice_formats {
+ map { ($_ => $export_names{$_}->{'name'}) }
+ grep { $export_names{$_}->{'invoice_header'} }
+ keys %export_names;
+}
+
+=item invoice_header FORMAT
+
+Returns a scalar containing the CSV column header for invoice format FORMAT.
+
+=cut
+
+sub invoice_header {
+ my $format = shift;
+ $export_names{$format}->{'invoice_header'};
+}
+
+=item import_formats
+
+Returns an ordered list of key value pairs containing import format names
+as keys (for use with batch_import) and "pretty" format names as values.
+
+=cut
+
+#false laziness w/part_pkg & part_export
+
+my %cdr_info;
+foreach my $INC ( @INC ) {
+ warn "globbing $INC/FS/cdr/*.pm\n" if $DEBUG;
+ foreach my $file ( glob("$INC/FS/cdr/*.pm") ) {
+ warn "attempting to load CDR format info from $file\n" if $DEBUG;
+ $file =~ /\/(\w+)\.pm$/ or do {
+ warn "unrecognized file in $INC/FS/cdr/: $file\n";
+ next;
+ };
+ my $mod = $1;
+ my $info = eval "use FS::cdr::$mod; ".
+ "\\%FS::cdr::$mod\::info;";
+ if ( $@ ) {
+ die "error using FS::cdr::$mod (skipping): $@\n" if $@;
+ next;
+ }
+ unless ( keys %$info ) {
+ warn "no %info hash found in FS::cdr::$mod, skipping\n";
+ next;
+ }
+ warn "got CDR format info from FS::cdr::$mod: $info\n" if $DEBUG;
+ if ( exists($info->{'disabled'}) && $info->{'disabled'} ) {
+ warn "skipping disabled CDR format FS::cdr::$mod" if $DEBUG;
+ next;
+ }
+ $cdr_info{$mod} = $info;
+ }
+}
+
+tie my %import_formats, 'Tie::IxHash',
+ map { $_ => $cdr_info{$_}->{'name'} }
+ sort { $cdr_info{$a}->{'weight'} <=> $cdr_info{$b}->{'weight'} }
+ grep { exists($cdr_info{$_}->{'import_fields'}) }
+ keys %cdr_info;
+
+sub import_formats {
+ %import_formats;
+}
+
+sub _cdr_min_parser_maker {
+ my $field = shift;
+ my @fields = ref($field) ? @$field : ($field);
+ @fields = qw( billsec duration ) unless scalar(@fields) && $fields[0];
+ return sub {
+ my( $cdr, $min ) = @_;
+ my $sec = eval { _cdr_min_parse($min) };
+ die "error parsing seconds for @fields from $min minutes: $@\n" if $@;
+ $cdr->$_($sec) foreach @fields;
+ };
+}
+
+sub _cdr_min_parse {
+ my $min = shift;
+ sprintf('%.0f', $min * 60 );
+}
+
+sub _cdr_date_parser_maker {
+ my $field = shift;
+ my @fields = ref($field) ? @$field : ($field);
+ return sub {
+ my( $cdr, $datestring ) = @_;
+ my $unixdate = eval { _cdr_date_parse($datestring) };
+ die "error parsing date for @fields from $datestring: $@\n" if $@;
+ $cdr->$_($unixdate) foreach @fields;
+ };
+}
+
+sub _cdr_date_parse {
+ my $date = shift;
+
+ return '' unless length($date); #that's okay, it becomes NULL
+
+ my($year, $mon, $day, $hour, $min, $sec);
+
+ #$date =~ /^\s*(\d{4})[\-\/]\(\d{1,2})[\-\/](\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})\s*$/
+ #taqua #2007-10-31 08:57:24.113000000
+
+ if ( $date =~ /^\s*(\d{4})\D(\d{1,2})\D(\d{1,2})\s+(\d{1,2})\D(\d{1,2})\D(\d{1,2})(\D|$)/ ) {
+ ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
+ } elsif ( $date =~ /^\s*(\d{1,2})\D(\d{1,2})\D(\d{4})\s+(\d{1,2})\D(\d{1,2})\D(\d{1,2})(\D|$)/ ) {
+ ($mon, $day, $year, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
+ } else {
+ die "unparsable date: $date"; #maybe we shouldn't die...
+ }
+
+ return '' if $year == 1900 && $mon == 1 && $day == 1
+ && $hour == 0 && $min == 0 && $sec == 0;
+
+ timelocal($sec, $min, $hour, $day, $mon-1, $year);
+}
+
+=item batch_import HASHREF
+
+Imports CDR records. Available options are:
+
+=over 4
+
+=item file
+
+Filename
+
+=item format
+
+=item params
+
+Hash reference of preset fields, typically cdrbatch
+
+=item empty_ok
+
+Set true to prevent throwing an error on empty imports
+
+=back
+
+=cut
+
+my %import_options = (
+ 'table' => 'cdr',
+
+ 'formats' => { map { $_ => $cdr_info{$_}->{'import_fields'}; }
+ keys %cdr_info
+ },
+
+ #drop the || 'csv' to allow auto xls for csv types?
+ 'format_types' => { map { $_ => ( lc($cdr_info{$_}->{'type'}) || 'csv' ); }
+ keys %cdr_info
+ },
+
+ 'format_headers' => { map { $_ => ( $cdr_info{$_}->{'header'} || 0 ); }
+ keys %cdr_info
+ },
+
+ 'format_sep_chars' => { map { $_ => $cdr_info{$_}->{'sep_char'}; }
+ keys %cdr_info
+ },
+
+ 'format_fixedlength_formats' =>
+ { map { $_ => $cdr_info{$_}->{'fixedlength_format'}; }
+ keys %cdr_info
+ },
+);
+
+sub _import_options {
+ \%import_options;
+}
+
+sub batch_import {
+ my $opt = shift;
+
+ my $iopt = _import_options;
+ $opt->{$_} = $iopt->{$_} foreach keys %$iopt;
+
+ FS::Record::batch_import( $opt );
+
+}
+
+=item process_batch_import
+
+=cut
+
+sub process_batch_import {
+ my $job = shift;
+
+ my $opt = _import_options;
+ $opt->{'params'} = [ 'format', 'cdrbatch' ];
+
+ FS::Record::process_batch_import( $job, $opt, @_ );
+
+}
+# if ( $format eq 'simple' ) { #should be a callback or opt in FS::cdr::simple
+# @columns = map { s/^ +//; $_; } @columns;
+# }
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cdr/asterisk.pm b/FS/FS/cdr/asterisk.pm
new file mode 100644
index 0000000..8b29642
--- /dev/null
+++ b/FS/FS/cdr/asterisk.pm
@@ -0,0 +1,45 @@
+package FS::cdr::asterisk;
+
+use strict;
+use vars qw(@ISA %info);
+use FS::cdr qw(_cdr_date_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+#http://www.the-asterisk-book.com/unstable/funktionen-cdr.html
+my %amaflags = (
+ DEFAULT => 0,
+ OMIT => 1, #asterisk 1.4+
+ IGNORE => 1, #asterisk 1.2
+ BILLING => 2, #asterisk 1.4+
+ BILL => 2, #asterisk 1.2
+ DOCUMENTATION => 3,
+ #? '' => 0,
+);
+
+%info = (
+ 'name' => 'Asterisk',
+ 'weight' => 10,
+ 'import_fields' => [
+ 'accountcode',
+ 'src',
+ 'dst',
+ 'dcontext',
+ 'clid',
+ 'channel',
+ 'dstchannel',
+ 'lastapp',
+ 'lastdata',
+ _cdr_date_parser_maker('startdate'),
+ _cdr_date_parser_maker('answerdate'),
+ _cdr_date_parser_maker('enddate'),
+ 'duration',
+ 'billsec',
+ 'disposition',
+ sub { my($cdr, $amaflags) = @_; $cdr->amaflags($amaflags{$amaflags}); },
+ 'uniqueid',
+ 'userfield',
+ ],
+);
+
+1;
diff --git a/FS/FS/cdr/bell_west.pm b/FS/FS/cdr/bell_west.pm
new file mode 100644
index 0000000..f745bb1
--- /dev/null
+++ b/FS/FS/cdr/bell_west.pm
@@ -0,0 +1,122 @@
+package FS::cdr::bell_west;
+
+use strict;
+use base qw( FS::cdr );
+use vars qw( %info $tmp_mon $tmp_mday $tmp_year );
+use Time::Local;
+#use FS::cdr qw( _cdr_date_parser_maker _cdr_min_parser_maker );
+
+%info = (
+ 'name' => 'Bell West',
+ 'weight' => 210,
+ 'header' => 1,
+ 'type' => 'xls',
+
+ 'import_fields' => [
+
+ # CDR FIELD / REQUIRED / Notes
+
+ # CHG TYPE / No / Internal Code only (no need to import)
+ sub {},
+
+ # ACCOUNT # / No / Internal Number only (no need to import)
+ sub {},
+
+ # DATE / Yes / "DATE" Excel date format MM/DD/YYYY
+ # XXX false laziness w/troop.pm
+ sub { my($cdr, $date) = @_;
+
+ my $datetime = DateTime::Format::Excel->parse_datetime( $date );
+ $tmp_mon = $datetime->mon_0;
+ $tmp_mday = $datetime->mday;
+ $tmp_year = $datetime->year;
+ },
+
+ # CUST NO / Yes / "TIME" "075959" Text based time
+ # Note: This is really the start time but Bell header says "Cust No" which
+ # is wrong
+ sub { my($cdr, $time) = @_;
+ #my($sec, $min, $hour, $mday, $mon, $year)= localtime($cdr->startdate);
+ $time =~ /^(\d{2})(\d{2})(\d{2})$/
+ or die "unparsable time: $time"; #maybe we shouldn't die...
+ #$cdr->startdate( timelocal($3, $2, $1 ,$mday, $mon, $year) );
+ $cdr->startdate(
+ timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year)
+ );
+ },
+
+ # BTN / Yes / Main billing number but not DID or real number
+ # (put in SRC field)
+ 'src',
+
+ # ORIG CITY / No / We will use your Freeside rating and description name
+ 'channel',
+
+ # TERM / YES / All calls should be billed, however all calls are
+ # missing "1+" and "011+" & DIR ASST = "411"
+ 'dst',
+
+ # TERM CITY / No / We will use your Freeside rating and description name
+ 'dstchannel',
+
+ # WTN / Yes / Bill to number (put in "charged_party")
+ 'charged_party',
+
+ # CODE / Yes / Account Code (security) and we need on invoice
+ 'accountcode',
+
+ # PROV/COUNTRY / No / We will use your Freeside rating and description name
+ # (but use this to add "011" for "International" calls)
+ sub { my( $cdr, $prov ) = @_;
+ my $pre = ( $prov =~ /^\s*International\s*/i ) ? '011' : '1';
+ $cdr->dst( $pre. $cdr->dst ) unless $cdr->dst =~ /^$pre/;
+ },
+
+ # CALL TYPE / Possibly / Not sure if you need this to determine correct
+ # billing method ?
+ # DDD normal call (Direct Dial Dsomething? ="LD"?)
+ # TF Toll Free
+ # (toll free dst# should be sufficient to rate)
+ # DAT Directory AssisTance
+ # (dst# 411 "area code" should be sufficient to rate)
+ # DNS (Another sort of directory assistance?... only one record with
+ # "8195551212" in the dst#)
+ 'dcontext', #probably don't need... map to cdr_type? calltypenum?
+
+ # DURATION Yes Units = seconds
+ 'billsec', #need to trim .00 ?
+
+ # AMOUNT CHARGED No Will use Freeside rating and description name
+ sub { my( $cdr, $amount) = @_;
+ $amount =~ s/^\$//;
+ $cdr->upstream_price( $amount );
+ },
+
+ ],
+
+);
+
+1;
+
+__END__
+
+CHG TYPE (unused)
+ACCOUNT # (unused)
+
+DATE startdate (+ CUST NO)
+CUST NO (startdate time)
+ - Start of call (UNIX-style integer timestamp)
+
+BTN *src - Caller*ID number / Source number
+ORIG CITY channel - Channel used
+TERM # *dst - Destination extension
+TERM CITY dstchannel - Destination channel if appropriate
+WTN *charged_party - Service number to be billed
+CODE *accountcode - CDR account number to use: account
+
+PROV/COUNTRY (used to prefix TERM # w/ 1 or 011)
+
+CALL TYPE dcontext - Destination context
+DURATION *billsec - Total time call is up, in seconds
+AMOUNT CHARGED *upstream_price - Wholesale price from upstream
+
diff --git a/FS/FS/cdr/genband.pm b/FS/FS/cdr/genband.pm
new file mode 100644
index 0000000..619d908
--- /dev/null
+++ b/FS/FS/cdr/genband.pm
@@ -0,0 +1,120 @@
+package FS::cdr::genband;
+
+use strict;
+use vars qw(@ISA %info);
+use FS::cdr qw(_cdr_date_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'GenBand (Tekelec)', #'Genband G6 (Tekelec T6000)',
+ 'weight' => 140,
+ 'type' => 'fixedlength',
+ 'fixedlength_format' => [qw(
+ Type:2:1:2
+ Sequence:4:3:6
+ OIDCall:30:7:36
+ StartTime:19:37:55
+ AnswerTime:19:56:74
+ EndTime:19:75:93
+ SourceName:30:94:123
+ SourceEndName:30:124:153
+ SourceCallerID:20:154:173
+ SourceCallerName:30:174:203
+ DestinationName:30:204:233
+ DestinationEndName:30:234:263
+ DestCallerID:20:264:283
+ DestCallerIDInfo:30:284:313
+ DialedDigits:30:314:343
+ Billing:30:344:373
+ AuthCode:30:374:403
+ CallDirection:1:404:404
+ ExtendedCall:1:405:405
+ ExternalCall:1:406:406
+ Duration:9:407:415
+ SIPCallID:64:416:479
+ IncomingDigits:30:480:509
+ OutpulsedDigits:30:510:539
+ CarrierIdentificationCode:4:540:543
+ CompletionReason:4:544:547
+ OriginationPartition:30:548:577
+ DestinationPartition:30:578:607
+ BilledSourceDID:20:608:627
+ OriginalCall:30:628:657
+ VideoCall:1:658:658
+ )],
+ 'import_fields' => [
+ sub {}, #Type:2:1:2
+ sub {}, #Sequence:4:3:6
+ 'uniqueid', #OIDCall:30:7:36
+ _cdr_date_parser_maker('startdate'), #StartTime:19:37:55
+ _cdr_date_parser_maker('answerdate'), #AnswerTime:19:56:74
+ _cdr_date_parser_maker('enddate'), #EndTime:19:75:93
+ sub {}, #SourceName:30:94:123
+ 'channel', #SourceEndName:30:124:153
+ 'src', #SourceCallerID:20:154:173
+ 'clid', #SourceCallerName:30:174:203
+ sub {}, #DestinationName:30:204:233
+ 'dstchannel', #DestinationEndName:30:234:263
+ 'dst', #DestCallerID:20:264:283
+ sub {}, #DestCallerIDInfo:30:284:313
+ sub {}, #DialedDigits:30:314:343
+ sub {}, #Billing:30:344:373
+ sub {}, #AuthCode:30:374:403
+ sub {}, #CallDirection:1:404:404
+ sub {}, #ExtendedCall:1:405:405
+ sub {}, #ExternalCall:1:406:406
+ sub { my( $cdr, $duration ) = @_;
+ $cdr->duration($duration);
+ $cdr->billsec($duration); }, #'duration', #Duration:9:407:415
+ sub {}, #SIPCallID:64:416:479
+ sub {}, #IncomingDigits:30:480:509
+ sub {}, #OutpulsedDigits:30:510:539
+ sub {}, #CarrierIdentificationCode:4:540:543
+ sub {}, #CompletionReason:4:544:547
+ sub {}, #OriginationPartition:30:548:577
+ sub {}, #DestinationPartition:30:578:607
+ sub {}, #BilledSourceDID:20:608:627
+ sub {}, #OriginalCall:30:628:657
+ sub {}, #VideoCall:1:658:658
+ ],
+);
+# acctid - primary key
+# calldate - Call timestamp (SQL timestamp)
+# clid - Caller*ID with text
+# src - Caller*ID number / Source number
+# dst - Destination extension
+# dcontext - Destination context
+# channel - Channel used
+# dstchannel - Destination channel if appropriate
+# lastapp - Last application if appropriate
+# lastdata - Last application data
+# startdate - Start of call (UNIX-style integer timestamp)
+# answerdate - Answer time of call (UNIX-style integer timestamp)
+# enddate - End time of call (UNIX-style integer timestamp)
+# duration - Total time in system, in seconds
+# billsec - Total time call is up, in seconds
+# disposition - What happened to the call: ANSWERED, NO ANSWER, BUSY
+# amaflags - What flags to use: BILL, IGNORE etc, specified on a per
+# channel basis like accountcode.
+# accountcode - CDR account number to use: account
+# uniqueid - Unique channel identifier (Unitel/RSLCOM Event ID)
+# userfield - CDR user-defined field
+# cdr_type - CDR type - see FS::cdr_type (Usage = 1, S&E = 7, OC&C = 8)
+# charged_party - Service number to be billed
+# upstream_currency - Wholesale currency from upstream
+# upstream_price - Wholesale price from upstream
+# upstream_rateplanid - Upstream rate plan ID
+# rated_price - Rated (or re-rated) price
+# distance - km (need units field?)
+# islocal - Local - 1, Non Local = 0
+# calltypenum - Type of call - see FS::cdr_calltype
+# description - Description (cdr_type 7&8 only) (used for
+# cust_bill_pkg.itemdesc)
+# quantity - Number of items (cdr_type 7&8 only)
+# carrierid - Upstream Carrier ID (see FS::cdr_carrier)
+# upstream_rateid - Upstream Rate ID
+# svcnum - Link to customer service (see FS::cust_svc)
+# freesidestatus - NULL, done (or something)
+
+1;
diff --git a/FS/FS/cdr/genband_meetme.pm b/FS/FS/cdr/genband_meetme.pm
new file mode 100644
index 0000000..d87dd8f
--- /dev/null
+++ b/FS/FS/cdr/genband_meetme.pm
@@ -0,0 +1,17 @@
+package FS::cdr::genband_meetme;
+
+use strict;
+use vars qw(@ISA %info);
+use FS::cdr qw(_cdr_date_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'Genband (Tekelec) Meet-Me Conference', #'Genband G6 (Tekelec T6000) Meet-Me Conference Log Records',
+ 'weight' => 145,
+ 'disabled' => 1,
+ 'import_fields' => [
+ ],
+);
+
+1;
diff --git a/FS/FS/cdr/indosoft.pm b/FS/FS/cdr/indosoft.pm
new file mode 100644
index 0000000..cb25089
--- /dev/null
+++ b/FS/FS/cdr/indosoft.pm
@@ -0,0 +1,71 @@
+package FS::cdr::indosoft;
+
+use strict;
+use base qw( FS::cdr );
+use vars qw( %info );
+use FS::cdr qw( _cdr_date_parser_maker _cdr_min_parser_maker );
+
+%info = (
+ 'name' => 'Indosoft Conference Bridge',
+ 'weight' => 300,
+ 'header' => 1,
+ 'type' => 'csv',
+
+ #listref of what to do with each field from the CDR, in order
+ 'import_fields' => [
+
+ #cdr_id
+ 'uniqueid',
+
+ #connect_time
+ _cdr_date_parser_maker( ['startdate', 'answerdate' ] ),
+
+ #disconnect_time
+ _cdr_date_parser_maker('enddate'),
+
+ #account_id
+ 'accountcode',
+
+ #conference_id
+ 'userfield',
+
+ #client_id
+ 'charged_party',
+
+ #pin_used
+ 'dcontext',
+
+ #channel
+ 'channel',
+
+ #clid
+ #'src',
+ sub { my($cdr, $clid) = @_;
+ $cdr->clid( $clid ); #because they called it 'clid' explicitly
+ $cdr->src( $clid );
+ },
+
+ #dnis
+ 'dst',
+
+ #call_status
+ 'disposition',
+
+ #conf_billing_code
+ 'lastapp', #arbitrary
+
+ #participant_id
+ 'lastdata', #arbitrary
+
+ #codr_id
+ 'dstchannel', #arbitrary
+
+ #call_type
+ 'description',
+
+ ],
+
+);
+
+1;
+
diff --git a/FS/FS/cdr/netcentrex.pm b/FS/FS/cdr/netcentrex.pm
new file mode 100644
index 0000000..7ccc3df
--- /dev/null
+++ b/FS/FS/cdr/netcentrex.pm
@@ -0,0 +1,783 @@
+package FS::cdr::netcentrex;
+
+use strict;
+use vars qw(@ISA %info);
+use FS::cdr qw(_cdr_date_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+#close enough http://wiki.freeswitch.org/wiki/Hangup_causes
+#my %disposition = (
+# 16 => 'ANSWERED',
+# 17 => 'BUSY',
+# 18 => 'NO USER RESPONSE',
+# 19 => 'NO ANSWER',
+# 156 => '??' #???
+#);
+
+%info = (
+ 'name' => 'NetCentrex',
+ 'weight' => 150,
+ 'type' => 'csv',
+ 'sep_char' => ';',
+ 'import_fields' => [
+ '', #00 SU Identifier
+ '', #01 SU IP Address
+ '', #02 Conference ID
+ '', #03 Call ID
+ '', #04 Leg number (all 0)
+ _cdr_date_parser_maker('startdate'), #05 Authorize timestamp
+ _cdr_date_parser_maker('answerdate'), #06 Start timestamp
+ 'billsec', #'duration', #07 Duration
+ _e164_parser_maker('src'), #08 Caller
+ _e164_parser_maker('dst'), #09 Callee
+ 'channel', #10 Source IP
+ 'dstchannel', #11 Destination IP
+ 'userfield', #12 selector Tag
+ '', #13 *service Tag
+ '', #14 *announcement Tag
+ '', #15 *route Table Tag
+ '', #16 vTrunkGroup Tag
+ '', #17 vTrunk Tag XXX ? another userfield?
+ '', #18 *termination Tag
+ '', #19 *location group Tag
+ '', #20 *GK Originating IP
+ '', #21 *GK Terminating IP
+ '', #22 *GK Originating Domain
+ '', #23 *GK Terminating Domain
+ '', #24 Malicious Call (all 0)
+ '', #25 Service (all 0)
+ 'disposition', #26 Termination Cause 16/17/18/156
+ '', #27 Simulation Call (all 0) supposedly don't bill 1
+ '', #28 Type (all C)
+ _cdr_date_parser_maker('enddate'), #29 ReleaseTimeStamp
+ #seems empty from here in sampes...
+ '', #30
+ '', #31
+ '', #32
+ '', #33
+ '', #34
+ '', #35
+ '', #36
+ '', #37
+ '', #38
+ '', #39
+ '', #40
+ '', #41
+ '', #42
+ '', #43
+ '', #44
+ '', #45
+ '', #46
+ '', #47
+ '', #48
+ '', #49
+ '', #50
+
+ # * empty
+ ],
+
+);
+
+sub _e164_parser_maker {
+ my $field = shift;
+ return sub {
+ my( $cdr, $e164 ) = @_;
+ eval { $cdr->$field( _e164_parse($e164) ); };
+ die "error parsing e164 for $field from $e164: $@\n" if $@;
+ };
+}
+
+my %e164_types = (
+ '000000' => '',
+ '100005' => '',
+ '100009' => '',
+ '100012' => '',
+ '100014' => '',
+ '100015' => '',
+ '100016' => '',
+ '300000' => '',
+);
+
+sub _e164_parse {
+ my $e164 = shift;
+
+ $e164 =~ s/^e164://;
+
+ my ($type, $number);
+ if ( $e164 =~ /^O(\d+)$/ ) {
+ $type = ''; #?
+ $number = $1;
+ } elsif ( $e164 =~ /^(\d{6})(\d+)$/ ) {
+ $type = $1;
+ $number = $2;
+ } else {
+ $type = '';
+ $number = $e164; #unparsable...
+ }
+ #$type...?
+ $number;
+}
+
+1;
+
+=pod
+
+ calldate - Call timestamp (SQL timestamp)
+ clid - Caller*ID with text
+ src - Caller*ID number / Source number
+ dst - Destination extension
+ dcontext - Destination context
+ channel - Channel used
+ dstchannel - Destination channel if appropriate
+ lastapp - Last application if appropriate
+ lastdata - Last application data
+ startdate - Start of call (UNIX-style integer timestamp)
+ answerdate - Answer time of call (UNIX-style integer timestamp)
+ enddate - End time of call (UNIX-style integer timestamp)
+ duration - Total time in system, in seconds
+ billsec - Total time call is up, in seconds
+ disposition - What happened to the call: ANSWERED, NO ANSWER, BUSY
+ amaflags - What flags to use: BILL, IGNORE etc, specified on a per
+ channel basis like accountcode.
+ accountcode - CDR account number to use: account
+ uniqueid - Unique channel identifier (Unitel/RSLCOM Event ID)
+ userfield - CDR user-defined field
+ cdr_type - CDR type - see FS::cdr_type (Usage = 1, S&E = 7, OC&C = 8)
+ charged_party - Service number to be billed
+ upstream_currency - Wholesale currency from upstream
+ upstream_price - Wholesale price from upstream
+ upstream_rateplanid - Upstream rate plan ID
+ rated_price - Rated (or re-rated) price
+ distance - km (need units field?)
+ islocal - Local - 1, Non Local = 0
+ calltypenum - Type of call - see FS::cdr_calltype
+ description - Description (cdr_type 7&8 only) (used for
+ cust_bill_pkg.itemdesc)
+ quantity - Number of items (cdr_type 7&8 only)
+ carrierid - Upstream Carrier ID (see FS::cdr_carrier)
+ upstream_rateid - Upstream Rate ID
+ svcnum - Link to customer service (see FS::cust_svc)
+ freesidestatus - NULL, done (or something)
+ cdrbatch
+
+No. Field Type/Length Format / Remarks Description Example
+00 SU Identifier String This field is never empty. SU Identifier (as defined by su- su01
+ <= 16 chars core.ini/[SU]/SUInstance key at SU
+ 192.168.121.1
+ initialization).
+ By default, the SUInstance is set to
+ a string that represents the SU
+ private IP address.
+01 SU IP address String ipv4:xx.xx.xx.xx<:port> SU IP address (and ASM port) as ipv4:213.56.136.29: 2518
+ <= 26 chars provided by su-
+ This field is never empty.
+ crouting.ini/[crRouting]/localASMa
+ ddress key.
+02 Conference ID String When [CDR_FIELDS] Unique call session identifier Advised format
+ <= 64 chars ReadlIDFormat is set to 1 in provided by the SU, as received in (ReadlIDFormat=1):
+ ncx-cdr-wrapper.ini (advised call initiation message (H.225 910a4b12 cd67d93f
+ format): conferenceID field in Setup or 4300abd2 cc10a0a0
+ ARQ).
+ 4x4 bytes as an hexadecimal RealIDFormat=0:
+ string; double words are
+ 12.123.54.125.67.235.255.2
+ space-separated
+ 31.9.12.4.3.7.19.245.65
+ When [CDR_FIELDS]
+ ReadlIDFormat is set to 0 in
+ ncx-cdr-wrapper.ini:
+ 16xdecimal notation of a 1-
+ byte number (0..255), dot-
+ separated.
+ This field is never empty.
+03 Call ID String When [CDR_FIELDS] Call identifier provided by the ASM Advised format
+ <= 64 chars ReadlIDFormat is set to 1 in in the SU (it can be the CallID or (ReadlIDFormat=1):
+ ncx-cdr-wrapper.ini (advised the RealCallID according to what is 910a4b12 cd67d93f
+ format): set in the ncx-cdr-wrapper.ini 4300abd2 cc10a0a0
+ UseRealCallID field). It is received
+ 4x4 bytes as an hexadecimal RealIDFormat=0:
+ in call initiation message (H.225
+ string; double words are
+ callID field in Setup or ARQ). 12.123.54.125.67.235.255.2
+ space-separated
+ 31.9.12.4.3.7.19.245.65
+ When [CDR_FIELDS]
+ ReadlIDFormat is set to 0 in
+ ncx-cdr-wrapper.ini:
+ 16xdecimal notation of a 1-
+ byte number (0..255), dot-
+ separated.
+ This field may be empty if no
+ H.225 callID is present in
+ ARQ.
+04 Leg number Integer Always set to 0 when the call Call attempt index, starting at 0. 0
+ ~ 1 char is not deflected. Incremented whenever a call leg
+ to a new destination is created.
+ This field is never empty.
+ A single call without any call
+ forward service will only have 1
+ CDR line, whose Leg number is set
+ to 0.
+ If a call is redirected (on
+ CFU/CFB/CNFR), it will generate a
+ second CDR line, leg number 1.
+ The leg number is then
+ incremented on each subsequent
+ redirection.
+
+05 Authorize Long It can have two formats as Authorize date and time of the call 1039189431
+ timestamp 10 chars given in the ncx-cdr- leg => enable to have a date and
+ wrapper.ini by the time if a call is not connected.
+ TimestampFormat field. UTC.
+ If TimestampFormat is set to This is the ARQ or SETUP or
+ 0, the result string INVITE reception timestamp for
+ corresponds to the "epoch" the first call leg. For next tickets,
+ time, the number of elapsed this is the call deflection processing
+ seconds since 1970/01/01 start time. Thus, this value may
+ 00:00:00 (UTC) vary in tickets related to a
+ complete call.
+ If TimestampFormat is set to
+ 1, the result string is 20 chars
+ in length (format: YYYY-MM-
+ DD HH:MM:SS)
+ NOTE: if you choose
+ TimestampFormat = 0 you
+ can have the tenth of second
+ (UseTenthOfSecond = 1) or
+ the micro second
+ (UseMicroSecond = 1)
+ NOTE: you can hide
+ timestamp equal to 0 (or
+ 1970/01/01 00:00:00) with
+ the key HideNullTimestamp
+ set to 1.
+ This field is never empty.
+06 Start timestamp Long It can have two formats as Starting date and time of the call 1039189431
+ 10 chars given in the ncx-cdr- leg. UTC.
+ wrapper.ini by the
+ This is the CONNECT or OK (after
+ TimestampFormat field.
+ INVITE) reception timestamp. It is
+ If TimestampFormat is set to set to the same value for all tickets
+ 0, the result string related to a call.
+ corresponds to the "epoch"
+ time, the number of elapsed
+ seconds since 1970/01/01
+ 00:00:00 (UTC)
+ If TimestampFormat is set to
+ 1, the result string is 20 chars
+ in length (format: YYYY-MM-
+ DD HH:MM:SS)
+ 0 (or 1970/01/01 00:00:00)
+ means the connection was not
+ established for this call leg.
+ NOTE: if you choose
+ TimestampFormat = 0 you
+ can have the tenth of second
+ (UseTenthOfSecond = 1) or
+ the micro second
+ (UseMicroSecond = 1)
+ NOTE: you can hide
+ timestamp equal to 0 (or
+ 1970/01/01 00:00:00) with
+ the key HideNullTimestamp
+ set to 1.
+ This field may be empty if the
+ call is not connected.
+07 Duration Long In seconds (0 means the Duration of the call leg (in 6
+ <= 10 chars connection was not seconds), after the connection was
+ established for this call leg). established.
+ NOTE: you can have the tenth Set to 0 for SIP NOTIFICATION
+ of second (UseTenthOfSecond and SIP MESSAGE reports.
+ = 1) or the micro second
+ (UseMicroSecond = 1)
+ This field is never empty.
+08 Caller String e164:[number] or h323:[alias] Main Source Alias in pivot format e164:0010033575
+ or email:[alias] (provided by the ASM)
+ <= 128 chars
+ This field may be empty if the If pivot format cannot be
+ Caller pivot alias cannot be computed then the main source
+ computed. alias is presented in originating
+ format and the "O" char is inserted
+ See Use Cases section for
+ at the beginning of the alias or
+ possible cases.
+ number.
+ NOTE: the phone-context and
+ trunk-context are set if present.
+09 Callee String e164:[number] or h323:[alias] E.164 Called Party Number alias or e164:0010033762
+ or email:[alias] H323 destination ID in pivot
+ <= 128 chars
+ format (provided by the ASM)
+ This field may be empty if the
+ Callee pivot alias cannot be If pivot format cannot be
+ computed. computed then the originating
+ format is presented and the "O"
+ char is inserted at the beginning of
+ the alias or number.
+ NOTE: the phone-context and
+ trunk-context are set if present.
+10 Source IP String ipv4:xx.xx.xx.xx<:port> If ncx-cdr-wrapper.ini/useFullIP = ipv4:192.168.1.2:34123
+ 0:
+ <= 26 chars This field may be empty if the
+ Source IP cannot be retrieved Source IP address of the caller, as
+ in IP message mode. used for IP filtering (thus, may be
+ either Packet IP address or
+ CallSignalAddress, depending on
+ su-
+ crouting.ini/[defaultH323Parameter
+ s]/ipFiltering key
+ It can also be changed by the
+ selector "extended actions"
+ parameter. See "selector extended
+ actions" dedicated documentation
+ for further information.
+ If ncx-cdr-wrapper.ini/useFullIP =
+ 1:
+ Source IP packet address for the
+ call leg
+11 Destination IP String ipv4:xx.xx.xx.xx<:port> If ncx-cdr-wrapper.ini/useFullIP = ipv4:213.56.162.17
+ 0:
+ <= 26 chars This field may be empty if
+ destination IP cannot be Destination IP signaling address
+ resolved. for the call leg
+ If ncx-cdr-wrapper.ini/useFullIP =
+ 1:
+ Destination IP packet address for
+ the call leg
+ NOTE: Can be different from the
+ signaling address when routing
+ through a proxy group. This field
+ refers to the proxy IP address.
+ Otherwise IP signaling address and
+ IP packet address are the same.
+12 selector Tag String This field is empty for non Extensible tag. See extension tag in=33231412345,vp=165,si
+ <= 199 chars Business Services managed format below. =123 tz=Europe/Berlin,
+ sources and for Sites with no
+ Selector Tag placed on the selector
+ PSTN ranges allocated.
+ for this call
+ See [ref: 2] and [ref: 3] for further
+ 2 2
+ information.
+13 service Tag Full alphanumeric This field is empty for now. Service Tag placed on the selector
+ string or on the vTrunkGroup for this call.
+ See [ref: 2] and [ref: 3] for further
+ 2
+ information.
+14 announcement Full alphanumeric This field is empty for now. Announcement Tag placed on the
+ Tag string selector, routeTable or
+ vTrunkGroup for this call.
+ See [ref: 2]and [ref: 3] for further
+ 2
+ information.
+15 route Table Tag Full alphanumeric This field is empty for now. Route table Tag placed on the
+ string route table for this call.
+ See [ref: 2] and [ref: 3] for further
+ 2 2
+ information.
+16 vTrunkGroup Full alphanumeric This field is empty for now. vTrunkGroupTag placed on the
+ Tag string vTrunkGroup for this call.
+ See [ref: 2] and [ref: 3] for further
+ 2
+ information.
+17 vTrunk Tag String This field is empty for non Extensible tag. See extension tag in=33156341289,vp=4232,s
+ <= 199 chars Business Services managed format below. i=132,tz=Europe/Paris
+ destinations and for Sites with
+ vTrunk Tag placed on the vTrunk
+ no PSTN ranges allocated.
+ for this call.
+ See [ref: 2] and [ref: 3] for further
+ 2 2
+ information.
+18 termination Tag Full alphanumeric This field is empty for now. Termination Tag placed on the
+ string Termination for this call.
+ See [ref: 2] and [ref: 3] for further
+ 2 2
+ information.
+19 location group Full alphanumeric This field is empty for now. location group Tag placed on the
+ Tag string selector for this call.
+ See [ref: 2] and [ref: 3] for further
+ 2 2
+ information.
+20 GK Originating Full alphanumeric This field is empty for now. Parameter provided by the ASM in
+ IP string the SU (reserved for future usage).
+21 GK Terminating Full alphanumeric This field is empty for now. Parameter provided by the ASM in
+ IP string the SU (reserved for future usage).
+22 GK Originating Full alphanumeric This field is empty for now. Parameter provided by the ASM in
+ Domain string the SU (reserved for future usage).
+23 GK Terminating Full alphanumeric This field is empty for now. Parameter provided by the ASM in
+ Domain string the SU (reserved for future usage).
+24 Malicious Call Boolean 0/1 Indicate if a call is malicious or 0
+ not. All calls to a specific called
+ 1 char
+ party will be tagged as malicious
+ when the malicious feature has
+ been activated.
+25 Service Long 0..31 Bit mask for activated services for 6: at least one
+ <= 3 chars this call. TECHNOLOGY and one
+ This field is never empty.
+ REMOVE service objects
+ This is a combination between the
+ have been used during
+ following values:
+ routing process
+ 1: if at least one CLIR service
+ 10: at least one BASIC-
+ object has been used during
+ XACTION and one REMOVE
+ routing process
+ service objects have been
+ 2: if at least one REMOVE service used during routing process
+ object has been used during
+ routing process
+ 4: if at least one TECHNOLOGY
+ service object has been used
+ during routing process
+ 8: if at least one BASIC-XACTION
+ service object has been used
+ during routing process
+ 16: if at least one SUBSTITUTION
+ service object has been used
+ during routing process
+ This is independent from the su-
+ crouting.ini configuration file and
+ in particular from the SPE
+ activation.
+26 Termination Long Causes in the range [1-127] Cause of the call termination. 16
+ Cause <= 3 chars are standard Q.850 causes
+ Causes >= 128 are specific
+ Comverse extension causes.
+ See [ref. 5] for possible values
+ and meanings.
+ This field is never empty.
+27 Simulation Call Boolean 0/1 Indicates if a call is a simulation 0
+ 1 char call or not.
+ This field is never empty.
+ SIMULATION CALLS MUST NOT BE
+ BILLED.
+ Simulation calls can only be
+ generated through the Telnet
+ interface (tests and diagnostic
+ only).
+28 Type One character Optional field depending on Type of CDR: C
+ the UseType entry in ncx-cdr-
+ 1 char - Call ('C'): for INVITE and SETUP
+ wrapper.ini. If set to 1, a
+ value in this field will be - Notification ('N') for SIP
+ always printed: 'C' by default. NOTIFICATION
+ 'C', 'N' or 'M'. - Message ('M') for SIP MESSAGE
+ This field is never empty.
+29 ReleaseTimeSta Long Optional field depending of Release date of the leg. 1039189431
+ mp 10 chars the UseReleaseTimeStamp
+ entry in ncx-cdr-wrapper.ini.
+ It can have two formats as
+ given in the ncx-cdr-
+ wrapper.ini by the
+ TimestampFormat field.
+ If TimestampFormat is set to
+ 0, the result string
+ corresponds to the "epoch"
+ time, the number of elapsed
+ seconds since 1970/01/01
+ 00:00:00 (UTC)
+ If TimestampFormat is set to
+ 1, the result string is 20 chars
+ in length (format: YYYY-MM-
+ DD HH:MM:SS)
+ NOTE: if you choose
+ TimestampFormat = 0 you
+ can have the tenth of second
+ (UseTenthOfSecond = 1) or
+ the micro second
+ (UseMicroSecond = 1)
+ NOTE: you can hide
+ timestamp equal to 0 (or
+ 1970/01/01 00:00:00) with
+ the key HideNullTimestamp
+ set to 1.
+ This field is empty when no
+ CRR message is received and
+ therefore it will be empty for
+ the CDR describing presence
+ message (SIP NOTIFY and SIP
+ MESSAGE). It is also empty
+ when the CDR is closed by the
+ AMU (e.g. if the SU is
+ detected as DOWN).
+ In all other cases, this field is
+ never empty
+30 cgIdentity Tag Full alphanumeric Optional: this field is filled if Extensible tag for Calling Party. pu=33231345123,pr=23
+ string usecgidentitytag is set to 1 in See extension tag format below.
+ <= 132 chars ncx-cdr-wrapper.ini.
+ This field is empty for non
+ Business Services/class V
+ managed sources.
+ The content of this field differs
+ between BS and MyCall
+ solutions.
+31 cdIdentity Tag Full alphanumeric Optional: this field is filled if Extensible tag for Called Party. See pr=1111,bi=ADMIN
+ string usecdidentitytag is set to 1 in extension tag format below.
+ <= 132 chars ncx-cdr-wrapper.ini
+ This field is empty for non
+ Business Services/class V
+ managed destinations.
+ The content of this field differs
+ between BS and MyCall
+ solutions.
+32 Originating String Optional: this field is filled if E.164 Main Source alias or H323 e164:0010033575
+ Caller <= 128 chars useoriginatingcaller is set to 1 source ID in originating format (as
+ in ncx-cdr-wrapper.ini received from the network)
+ e164:[number] or h323:[alias] The Main Source alias is computed
+ or email:[alias] according to su-core.ini
+ configuration.
+ NOTE: the phone-context and
+ trunk-context are set if present.
+33 Originating String Optional: this field is filled if E.164 Main Destination alias or e164:0010033762
+ Callee <= 128 chars useoriginatingcallee is set to 1 H323 destination ID in originating
+ in ncx-cdr-wrapper.ini format (as received from the
+ network)
+ e164:[number] or h323:[alias]
+ or email:[alias] The Main Destination alias is
+ computed according to su-core.ini
+ configuration.
+ NOTE: the phone-context and
+ trunk-context are set if present.
+34 Terminating String Optional: this field is filled if E.164 Calling Party Number alias or e164:0010033575
+ Caller <= 128 chars useterminatingcaller is set to 1 H323 source ID in terminating
+ in ncx-cdr-wrapper.ini format (as provided to the
+ network).
+ e164:[number] or h323:[alias]
+ or email:[alias] NOTE: the phone-context and
+ trunk-context are set if present.
+35 Terminating String Optional: this field is filled if E.164 Called Party Number alias or e164:0010033762
+ Callee <= 128 chars useterminatingcallee is set to H323 destination ID in terminating
+ 1 in ncx-cdr-wrapper.ini. format (as provided to the
+ network).
+ e164:[number] or h323:[alias]
+ or email:[alias] NOTE: the phone-context and
+ trunk-context are set if present.
+ This field may be empty if no
+ terminating destination aliases
+ can be computed by the CRE
+ (missing vtrunk transformation
+ or unable to found a vtrunk
+ for whatever routing reason),
+ or if the pivot to terminating
+ destination alias
+ transformation leads to an
+ empty alias.
+36 Network Long Optional: this field is filled if For H.323 the network timestamp 1039189431
+ Timestamp 10 chars usenetworkcompletiontimesta is measured at the first Progress or
+ mp is set to 1 in ncx-cdr- ALERT or CONNECT received by
+ wrapper.ini. the CCS for direct call.
+ For redirected call, the network
+ It can have two formats as
+ timestamp is measured by the
+ given in the ncx-cdr-
+ CCS at the redirection decision
+ wrapper.ini by the
+ point,
+ TimestampFormat field.
+ NOTE: For H.323 calls, the tcp-ack
+ If TimestampFormat is set to
+ of the outgoing TCP connection is
+ 0, the result string
+ not considered in the measure of
+ corresponds to the "epoch"
+ network timestamp
+ time, the number of elapsed
+ seconds since 1970/01/01 For SIP the network timestamp is
+ 00:00:00 (UTC) measured at the first SESSION
+ PROGRESS or RINGING or OK
+ If TimestampFormat is set to
+ received by the CCS for direct call.
+ 1, the result string is 20 chars
+ in length (format: YYYY-MM- The network timestamp is
+ DD HH:MM:SS) measured at the redirection
+ decision point for redirected call.
+ NOTE: if you choose
+ TimestampFormat = 0 you
+ can have the tenth of second
+ (UseTenthOfSecond = 1) or
+ the micro second
+ (UseMicroSecond = 1)
+ NOTE: you can hide
+ timestamp equal to 0 (or
+ 1970/01/01 00:00:00) with
+ the key HideNullTimestamp
+ set to 1.
+ This field may be empty if the
+ callee does not answer.
+37 Targeted Integer Optional: this field is filled if Provides information on the 12
+ adaptor UseTargetedAdaptors is set to adaptor that has been used: "1"
+ <= 2 chars
+ 1 in ncx-cdr-wrapper.ini. for adaptor1, "2" for adaptor2 and
+ "12" for adaptor1 and adaptor2
+ "1", "2" or "12"
+ See the amu-core.ini file section
+ for further details on adaptors
+ definition.
+38 Adaptor1 errors String Optional: this field is filled if Report errors on adaptor1 at the cra,crr
+ UseAdaptor1Errors is set to 1 adaptor API level.
+ <= 15 chars
+ in ncx-cdr-wrapper.ini.
+ "nca" (error on the new call
+ authorize)
+ "cra" (error on the call re-
+ authorize)
+ "ncr" (error on the new call
+ report)
+ "crr" (error on the call release
+ report)
+ When several errors occurred,
+ comma separated notation will
+ be used.
+ Empty when no error has
+ been detected.
+39 Source signaling String Optional: this field is filled in Source IP signaling address for the ipv4:192.168.1.2:34123
+ IP only if useFullIP is set to 1 in call leg.
+ <= 26 chars
+ the ncx-cdr-wrapper.ini file.
+ It can be changed by the selector
+ ipv4:xx.xx.xx.xx<:port> "extended actions" parameter. See
+ "selector extended actions"
+ This field may be empty if the
+ dedicated documentation for
+ Source IP cannot be retrieved
+ further information.
+ in IP message mode.
+40 Destination String Optional: this fields is filled in Destination IP signaling address ipv4:213.56.162.17
+ signaling IP only if useFullIP is set to 1 in for the call leg
+ <= 26 chars
+ ncx-cdr-wrapper.ini file.
+ ipv4:xx.xx.xx.xx<:port>, can
+ be empty if destination IP
+ cannot be resolved.
+41 Source point Unsigned integer Optional: this field is filled in SS7 point code, node identifier 1234
+ code only if usePC is set to 1 in the
+ <= 5 chars
+ ncx-cdr-wrapper.ini file.
+ SIP: FROM header [TG-TEL]:
+ PC is Encoded in the trunk-
+ group part of a "tel" URI
+ extension (see also RFC
+ 3966).
+ H.323: H.225/circuitInfo:
+ Encoded in an
+ sourceCircuitID.cic.pointCode.
+42 Destination point Unsigned integer Optional: this field is filled in SS7 point code, node identifier 1234
+ code only if usePC is set to 1 in the
+ <= 5 chars
+ ncx-cdr-wrapper.ini file.
+ SIP: TO header [TG-TEL]: PC
+ is encoded in the trunk-group
+ part of a "tel" URI extension
+ (see also RFC 3966).
+ H.323: H.225/circuitInfo:
+ Encoded in a
+ destinationCircuitID.cic.pointC
+ ode.
+43 Origination tag Full alphanumeric Optional: this field is filled in Origination tag placed on the crr=...,poi=...
+ string only if useOriginationTag is origination for this call.
+ set to 1 in the ncx-cdr-
+ wrapper.ini file.
+44 Proxy group tag Full alphanumeric Optional: this field is filled in Proxy group Tag placed on the
+ string only if useProxyGroupTag is proxy group for this call.
+ set to 1 in the ncx-cdr-
+ wrapper.ini file.
+ This field is empty for now.
+45 Advice of Charge String Optional: this field only is filled AOC received. rend=10.2,unit=EURO
+ in if UseAoc is set to 1 in ncx-
+ <= 50 chars cdr-wrapper.ini file. Available with CCS 3.8.4.
+ This field may be empty if
+ AOC service is not used or if
+ no AOC value is available.
+ <aocType>=<amount>,unit=
+ <string> with:
+ 1. <aocType> (max length:
+ 7 chars):
+ Received AOC-D: 'rduring'
+ Received AOC-E, 'rend'
+ Other AOC types are not yet
+ supported by the su-core and
+ therefore are ignored.
+ 2. <amount> (max length:
+ 14 chars):
+ The amount is decoded from
+ the received AOC-D or AOC-E.
+ This value is mandatory in an
+ AOC.
+ 3. unit=<string> (max length:
+ 15 chars):
+ The unit string is the decoded
+ unit value in the received
+ AOC-D or AOC-E. This value is
+ mandatory in an AOC.
+46 Routing Context String Optional Routing context of the leg. basic
+ <= 5 chars 3 possible values: For IMS calls, routing context has
+ the value "orig" or "term".
+ - basic Otherwise, it is set to "basic".
+ - orig
+ Dependencies:
+ - term
+ - amu-core-4.8.0
+ - adaptor-generic-cdr-
+ 1.8.0
+ - ncx-cdr-wrapper-1.8.0
+47 Originating String Optional: this field is filled if E164 Main Source alias or H323 e164:33762
+ Original Caller <= 128 chars useoriginatingoriginalcaller is source ID in originating format (as
+ set to 1 in ncx-cdr- received from the network) of the
+ wrapper.ini. original caller.
+ e164:[number] or h323:[alias] The main source alias is computed
+ or email:[alias] according to su-core.ini
+ configuration.
+ NOTE: the phone-context and
+ trunk-context are set if present.
+ Dependencies:
+ - amu-core-4.10.0
+ - adaptor-generic-cdr-
+ 1.10.0
+ - ncx-cdr-wrapper-1.10.0
+48 Pivot Original String Optional: this field is filled if E164 Main Source alias or H323 E164:0010033762
+ Caller <= 128 chars usepivotoriginalcaller is set to source ID in pivot format (as
+ 1 in ncx-cdr-wrapper.ini. received from the network) of the
+ original caller
+ e164:[number] or h323:[alias]
+ or email:[alias] They are sent if present by SU if
+ su-
+ crouting.ini/[compatibility]/aliasRe
+ porting is 5_0_0 or greater
+ NOTE: the phone-context and
+ trunk-context are set if present.
+ Dependencies:
+ - amu-core-4.10.0
+ - adaptor-generic-cdr-
+ 1.10.0
+ - ncx-cdr-wrapper-1.10.0
+49 Terminating String Optional: this field is filled if E164 Main Source alias or H323 E164:0010033762
+ Original Caller <= 128 chars useterminatingoriginalcaller is source ID in terminating format (as
+ set to 1 in ncx-cdr- received from the network) of the
+ wrapper.ini. original caller.
+ e164:[number] or h323:[alias] They are sent if present by SU if
+ or email:[alias] su-
+ crouting.ini/[compatibility]/aliasRe
+ porting is 5_0_0 or greater
+ NOTE: the phone-context and
+ trunk-context are set if present.
+ Dependencies:
+ - amu-core-4.10.0
+ - adaptor-generic-cdr-
+ 1.10.0
+ - ncx-cdr-wrapper-1.10.0
+50 Pivotclir Boolean Optional: this field is filled if Pivot CLIR calculated with caller clir=0
+ UsePivotClir is set to 1 in ncx- information.
+ 6 chars cdr-wrapper.ini.
+ Dependencies:
+ 0 means that Calling Line
+ Identification is showed. - amu-core-4.12.0
+ 1 means that Calling Line - adaptor-generic-cdr-
+ Identification is hidden. 1.12.0
+ - ncx-cdr-wrapper-1.12.0
+
diff --git a/FS/FS/cdr/nextone.pm b/FS/FS/cdr/nextone.pm
new file mode 100644
index 0000000..22e6e86
--- /dev/null
+++ b/FS/FS/cdr/nextone.pm
@@ -0,0 +1,26 @@
+package FS::cdr::nextone;
+
+use strict;
+use vars qw(@ISA %info);
+use FS::cdr qw(_cdr_date_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'Nextone',
+ 'weight' => 200,
+ 'header' => 1,
+ 'import_fields' => [
+ 'userfield', #CallZoneData ???userfield
+ 'channel', #OrigGw
+ 'dstchannel', #TermGw
+ sub { my( $cdr, $duration ) = @_;
+ $cdr->duration($duration);
+ $cdr->billsec($duration); }, #Duration
+ 'dst', #CallDTMF
+ 'src', #Ani
+ 'startdate', #DateTimeInt
+ ],
+);
+
+1;
diff --git a/FS/FS/cdr/openser.pm b/FS/FS/cdr/openser.pm
new file mode 100644
index 0000000..87fb822
--- /dev/null
+++ b/FS/FS/cdr/openser.pm
@@ -0,0 +1,24 @@
+package FS::cdr::openser;
+
+use strict;
+use vars qw(@ISA %info);
+use FS::cdr qw(_cdr_date_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'OpenSER',
+ 'weight' => 15,
+ 'header' => 1,
+ 'import_fields' => [
+ _cdr_date_parser_maker('startdate'),
+ _cdr_date_parser_maker('enddate'),
+ 'src',
+ 'dst',
+ 'duration',
+ 'channel',
+ 'dstchannel',
+ ],
+);
+
+1;
diff --git a/FS/FS/cdr/simple.pm b/FS/FS/cdr/simple.pm
new file mode 100644
index 0000000..197b0eb
--- /dev/null
+++ b/FS/FS/cdr/simple.pm
@@ -0,0 +1,52 @@
+package FS::cdr::simple;
+
+use strict;
+use vars qw( @ISA %info $tmp_mon $tmp_mday $tmp_year );
+use Time::Local;
+use FS::cdr qw(_cdr_min_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'Simple',
+ 'weight' => 20,
+ 'header' => 1,
+ 'import_fields' => [
+
+ # Date (MM/DD/YY)
+ sub { my($cdr, $date) = @_;
+ $date =~ /^(\d{1,2})\/(\d{1,2})\/(\d\d(\d\d)?)$/
+ or die "unparsable date: $date"; #maybe we shouldn't die...
+ #$cdr->startdate( timelocal(0, 0, 0 ,$2, $1-1, $3) );
+ ($tmp_mday, $tmp_mon, $tmp_year) = ( $2, $1-1, $3 );
+ },
+
+ # Time
+ sub { my($cdr, $time) = @_;
+ #my($sec, $min, $hour, $mday, $mon, $year)= localtime($cdr->startdate);
+ $time =~ /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/
+ or die "unparsable time: $time"; #maybe we shouldn't die...
+ #$cdr->startdate( timelocal($3, $2, $1 ,$mday, $mon, $year) );
+ $cdr->startdate(
+ timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year)
+ );
+ },
+
+ # Source_Number
+ 'src',
+
+ # Terminating_Number
+ 'dst',
+
+ # Duration
+ _cdr_min_parser_maker, #( [qw( billsec duration)] ),
+ #sub { my($cdr, $min) = @_;
+ # my $sec = sprintf('%.0f', $min * 60 );
+ # $cdr->billsec( $sec );
+ # $cdr->duration( $sec );
+ # },
+
+ ],
+);
+
+1;
diff --git a/FS/FS/cdr/simple2.pm b/FS/FS/cdr/simple2.pm
new file mode 100644
index 0000000..2e4fb90
--- /dev/null
+++ b/FS/FS/cdr/simple2.pm
@@ -0,0 +1,51 @@
+package FS::cdr::simple2;
+
+use strict;
+use vars qw( @ISA %info $tmp_mon $tmp_mday $tmp_year );
+use Time::Local;
+use FS::cdr qw(_cdr_min_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'Simple (Prerated)',
+ 'weight' => 25,
+ 'header' => 1,
+ 'import_fields' => [
+ sub {}, #TEXT_TIME (redundant w/Time)
+ sub {}, #Blank
+ 'src', #Calling.
+
+ #Date (YY/MM/DD)
+ sub { my($cdr, $date) = @_;
+ $date =~ /^(\d\d(\d\d)?)\/(\d{1,2})\/(\d{1,2})$/
+ or die "unparsable date: $date"; #maybe we shouldn't die...
+ #$cdr->startdate( timelocal(0, 0, 0 ,$3, $2-1, $1) );
+ ($tmp_mday, $tmp_mon, $tmp_year) = ( $4, $3-1, $1 );
+ },
+
+ #Time
+ sub { my($cdr, $time) = @_;
+ $time =~ /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/
+ or die "unparsable time: $time"; #maybe we shouldn't die...
+ #$cdr->startdate( timelocal($3, $2, $1 ,$mday, $mon, $year) );
+ $cdr->startdate(
+ timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year)
+ );
+ },
+
+ 'dst', #Dest
+ 'userfield', #? #DestinationDesc
+
+ #Min
+ _cdr_min_parser_maker, #( [qw( billsec duration)] ),
+
+ sub {}, #Rate XXX do something w/this, informationally???
+ 'upstream_price', #Total
+
+ 'accountcode', #ServCode
+ 'description', #Service_Type
+ ],
+);
+
+
diff --git a/FS/FS/cdr/taqua.pm b/FS/FS/cdr/taqua.pm
new file mode 100644
index 0000000..3052f83
--- /dev/null
+++ b/FS/FS/cdr/taqua.pm
@@ -0,0 +1,171 @@
+package FS::cdr::taqua;
+
+use strict;
+use vars qw(@ISA %info $da_rewrite);
+use FS::cdr qw(_cdr_date_parser_maker);
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'Taqua',
+ 'weight' => 130,
+ 'header' => 1,
+ 'import_fields' => [ #some of these are kind arbitrary...
+
+ #0
+ 'cdrtypenum', #RecordType
+ sub { my($cdr, $field) = @_; }, #all10#RecordVersion
+ sub { my($cdr, $field) = @_; }, #OrigShelfNumber
+ sub { my($cdr, $field) = @_; }, #OrigCardNumber
+ sub { my($cdr, $field) = @_; }, #OrigCircuit
+ sub { my($cdr, $field) = @_; }, #OrigCircuitType
+ 'uniqueid', #SequenceNumber
+ 'accountcode', #SessionNumber
+ 'src', #CallingPartyNumber
+ 'dst', #CalledPartyNumber
+
+ #10
+ _cdr_date_parser_maker('startdate'), #CallArrivalTime
+ _cdr_date_parser_maker('enddate'), #CallCompletionTime
+
+ #Disposition
+ #sub { my($cdr, $d ) = @_; $cdr->disposition( $disposition{$d}): },
+ 'disposition',
+ # -1 => '',
+ # 0 => '',
+ # 100 => '',
+ # 101 => '',
+ # 102 => '',
+ # 103 => '',
+ # 104 => '',
+ # 105 => '',
+ # 201 => '',
+ # 203 => '',
+
+ _cdr_date_parser_maker('answerdate'), #DispositionTime
+ sub { my($cdr, $field) = @_; }, #TCAP
+ sub { my($cdr, $field) = @_; }, #OutboundCarrierConnectTime
+ sub { my($cdr, $field) = @_; }, #OutboundCarrierDisconnectTime
+
+ #TermTrunkGroup
+ #it appears channels are actually part of trunk groups, but this data
+ #is interesting and we need a source and destination place to put it
+ 'dstchannel', #TermTrunkGroup
+
+
+ sub { my($cdr, $field) = @_; }, #TermShelfNumber
+ sub { my($cdr, $field) = @_; }, #TermCardNumber
+
+ #20
+ sub { my($cdr, $field) = @_; }, #TermCircuit
+ sub { my($cdr, $field) = @_; }, #TermCircuitType
+ 'carrierid', #OutboundCarrierId
+
+ #BillingNumber
+ #'charged_party',
+ sub {
+ my( $cdr, $field, $conf ) = @_;
+
+ #could be more efficient for the no config case, if anyone ever needs that
+ $da_rewrite ||= $conf->config('cdr-taqua-da_rewrite');
+
+ if ( $da_rewrite && $field =~ /\d/ ) {
+ my $rewrite = $da_rewrite;
+ $rewrite =~ s/\s//g;
+ my @rewrite = split(',', $conf->config('cdr-taqua-da_rewrite') );
+ if ( grep { $field eq $_ } @rewrite ) {
+ $cdr->charged_party( $cdr->src() );
+ $cdr->calltypenum(12);
+ return;
+ }
+ }
+ $cdr->charged_party($field);
+ },
+
+ sub { my($cdr, $field) = @_; }, #SubscriberNumber
+ 'lastapp', #ServiceName
+ sub { my($cdr, $field) = @_; }, #some weirdness #ChargeTime
+ 'lastdata', #ServiceInformation
+ sub { my($cdr, $field) = @_; }, #FacilityInfo
+ sub { my($cdr, $field) = @_; }, #all 1900-01-01 0#CallTraceTime
+
+ #30
+ sub { my($cdr, $field) = @_; }, #all-1#UniqueIndicator
+ sub { my($cdr, $field) = @_; }, #all-1#PresentationIndicator
+ sub { my($cdr, $field) = @_; }, #empty#Pin
+ 'calltypenum', #CallType
+
+ #nothing below is used by QIS...
+
+ sub { my($cdr, $field) = @_; }, #Balt/empty #OrigRateCenter
+ sub { my($cdr, $field) = @_; }, #Balt/empty #TermRateCenter
+
+ #OrigTrunkGroup
+ #it appears channels are actually part of trunk groups, but this data
+ #is interesting and we need a source and destination place to put it
+ 'channel', #OrigTrunkGroup
+
+ 'userfield', #empty#UserDefined
+ sub { my($cdr, $field) = @_; }, #empty#PseudoDestinationNumber
+ sub { my($cdr, $field) = @_; }, #all-1#PseudoCarrierCode
+
+ #40
+ sub { my($cdr, $field) = @_; }, #empty#PseudoANI
+ sub { my($cdr, $field) = @_; }, #all-1#PseudoFacilityInfo
+ sub { my($cdr, $field) = @_; }, #OrigDialedDigits
+ sub { my($cdr, $field) = @_; }, #all-1#OrigOutboundCarrier
+ sub { my($cdr, $field) = @_; }, #IncomingCarrierID
+ 'dcontext', #JurisdictionInfo
+ sub { my($cdr, $field) = @_; }, #OrigDestDigits
+ sub { my($cdr, $field) = @_; }, #huh?#InsertTime
+ sub { my($cdr, $field) = @_; }, #key
+ sub { my($cdr, $field) = @_; }, #empty#AMALineNumber
+
+ #50
+ sub { my($cdr, $field) = @_; }, #empty#AMAslpID
+ sub { my($cdr, $field) = @_; }, #empty#AMADigitsDialedWC
+ sub { my($cdr, $field) = @_; }, #OpxOffHook
+ sub { my($cdr, $field) = @_; }, #OpxOnHook
+
+ #acctid - primary key
+ #AUTO #calldate - Call timestamp (SQL timestamp)
+#clid - Caller*ID with text
+ #XXX src - Caller*ID number / Source number
+ #XXX dst - Destination extension
+ #dcontext - Destination context
+ #channel - Channel used
+ #dstchannel - Destination channel if appropriate
+ #lastapp - Last application if appropriate
+ #lastdata - Last application data
+ #startdate - Start of call (UNIX-style integer timestamp)
+ #answerdate - Answer time of call (UNIX-style integer timestamp)
+ #enddate - End time of call (UNIX-style integer timestamp)
+ #HACK#duration - Total time in system, in seconds
+ #HACK#XXX billsec - Total time call is up, in seconds
+ #disposition - What happened to the call: ANSWERED, NO ANSWER, BUSY
+#INT amaflags - What flags to use: BILL, IGNORE etc, specified on a per channel basis like accountcode.
+ #accountcode - CDR account number to use: account
+
+ #uniqueid - Unique channel identifier (Unitel/RSLCOM Event ID)
+ #userfield - CDR user-defined field
+
+ #X cdrtypenum - CDR type - see FS::cdr_type (Usage = 1, S&E = 7, OC&C = 8)
+ #XXX charged_party - Service number to be billed
+#upstream_currency - Wholesale currency from upstream
+#X upstream_price - Wholesale price from upstream
+#upstream_rateplanid - Upstream rate plan ID
+#rated_price - Rated (or re-rated) price
+#distance - km (need units field?)
+#islocal - Local - 1, Non Local = 0
+#calltypenum - Type of call - see FS::cdr_calltype
+#X description - Description (cdr_type 7&8 only) (used for cust_bill_pkg.itemdesc)
+#quantity - Number of items (cdr_type 7&8 only)
+#carrierid - Upstream Carrier ID (see FS::cdr_carrier)
+#upstream_rateid - Upstream Rate ID
+
+ #svcnum - Link to customer service (see FS::cust_svc)
+ #freesidestatus - NULL, done (or something)
+ ],
+);
+
+1;
diff --git a/FS/FS/cdr/troop.pm b/FS/FS/cdr/troop.pm
new file mode 100644
index 0000000..020af2b
--- /dev/null
+++ b/FS/FS/cdr/troop.pm
@@ -0,0 +1,128 @@
+package FS::cdr::troop;
+
+use strict;
+use base qw( FS::cdr );
+use vars qw( %info $tmp_mon $tmp_mday $tmp_year );
+use Time::Local;
+#use FS::cdr qw( _cdr_date_parser_maker _cdr_min_parser_maker );
+
+%info = (
+ 'name' => 'Troop',
+ 'weight' => 220,
+ 'header' => 2,
+ 'type' => 'xls',
+
+ 'import_fields' => [
+
+ # CDR FIELD / REQUIRED / Notes
+
+ # / No / CDR sequence number
+ sub {},
+
+ # WTN / Yes
+ 'charged_party',
+
+ # Account Code / Yes / Account Code (security) and we need on invoice
+ 'accountcode',
+
+ # DT / Yes / "DATE" Excel
+ # XXX false laziness w/bell_west.pm
+ sub { my($cdr, $date) = @_;
+
+ my $datetime = DateTime::Format::Excel->parse_datetime( $date );
+ $tmp_mon = $datetime->mon_0;
+ $tmp_mday = $datetime->mday;
+ $tmp_year = $datetime->year;
+ },
+
+ # Time / Yes / "TIME" excel
+ sub { my($cdr, $time) = @_;
+ #my($sec, $min, $hour, $mday, $mon, $year)= localtime($cdr->startdate);
+
+ #$sec = $time * 86400;
+ my $sec = int( $time * 86400 + .5);
+
+ #$cdr->startdate( timelocal($3, $2, $1 ,$mday, $mon, $year) );
+ $cdr->startdate(
+ timelocal(0, 0, 0, $tmp_mday, $tmp_mon, $tmp_year) + $sec
+ );
+ },
+
+
+ # Dur. / Yes / Units = seconds
+ 'billsec',
+
+ # OVS Type / Maybe / add "011" to international calls
+ # N = DOM LD / normal
+ # Z = INTL LD
+ # O = INTL LD
+ # others...?
+ sub { my($cdr, $ovs) = @_;
+ my $pre = ( $ovs =~ /^\s*[OZ]\s*$/i ) ? '011' : '1';
+ $cdr->dst( $pre. $cdr->dst ) unless $cdr->dst =~ /^$pre/;
+ },
+
+ # Number / YES
+ 'src',
+
+ # City / No
+ 'channel',
+
+ # Prov/State / No / We will use your Freeside rating and description name
+ sub { my($cdr, $state) = @_;
+ $cdr->channel( $cdr->channel. ", $state" )
+ if $state;
+ },
+
+ # Number / Yes
+ 'dst',
+
+ # City / No
+ 'dstchannel',
+
+ # Prov/State / No / We will use your Freeside rating and description name
+ sub { my($cdr, $state) = @_;
+ $cdr->dstchannel( $cdr->dstchannel. ", $state" )
+ if $state;
+ },
+
+ # OVS / Maybe
+ # Would help to add "011" to international calls (if you are willing)
+ # (using ovs above)
+ sub { my($cdr, $ovs) = @_;
+ my @ignore = ( 'BELL', 'CANADA', 'UNITED STATES', );
+ $cdr->dstchannel( $cdr->dstchannel. ", $ovs" )
+ if $ovs && ! grep { $ovs =~ /^\s*$_\s*$/ } @ignore;
+ },
+
+ # CC Ind. / No / Does show if Calling card but should not be required
+ #'N' or 'E'
+ sub {},
+
+ # Call Charge / No / Bell billing info and is not required
+ 'upstream_price',
+
+ # Account # / No / Bell billing info and is not required
+ sub {},
+
+ # Net Charge / No / Bell billing info and is not required
+ sub {},
+
+ # Surcharge / No / Taxes and is not required
+ sub {},
+
+ # GST / No / Taxes and is not required
+ sub {},
+
+ # PST / No / Taxes and is not required
+ sub {},
+
+ # HST / No / Taxes and is not required
+ sub {},
+
+ ],
+
+);
+
+1;
+
diff --git a/FS/FS/cdr/unitel.pm b/FS/FS/cdr/unitel.pm
new file mode 100644
index 0000000..df34a57
--- /dev/null
+++ b/FS/FS/cdr/unitel.pm
@@ -0,0 +1,39 @@
+package FS::cdr::unitel;
+
+use strict;
+use vars qw(@ISA %info);
+use FS::cdr;
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'Unitel/RSLCOM',
+ 'weight' => 500,
+ 'import_fields' => [
+ 'uniqueid',
+ #'cdr_type',
+ 'cdrtypenum',
+ 'calldate', # may need massaging? huh maybe not...
+ #'billsec', #XXX duration and billsec?
+ sub { $_[0]->billsec( $_[1] );
+ $_[0]->duration( $_[1] );
+ },
+ 'src',
+ 'dst', # XXX needs to have "+61" prepended unless /^\+/ ???
+ 'charged_party',
+ 'upstream_currency',
+ 'upstream_price',
+ 'upstream_rateplanid',
+ 'distance',
+ 'islocal',
+ 'calltypenum',
+ 'startdate', #XXX needs massaging
+ 'enddate', #XXX same
+ 'description',
+ 'quantity',
+ 'carrierid',
+ 'upstream_rateid',
+ ]
+);
+
+1;
diff --git a/FS/FS/cdr_calltype.pm b/FS/FS/cdr_calltype.pm
new file mode 100644
index 0000000..fe45608
--- /dev/null
+++ b/FS/FS/cdr_calltype.pm
@@ -0,0 +1,115 @@
+package FS::cdr_calltype;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cdr_calltype - Object methods for cdr_calltype records
+
+=head1 SYNOPSIS
+
+ use FS::cdr_calltype;
+
+ $record = new FS::cdr_calltype \%hash;
+ $record = new FS::cdr_calltype { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cdr_calltype object represents an CDR call type. FS::cdr_calltype
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item calltypenum - primary key
+
+=item calltypename - CDR call type name
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new call type. To add the call type to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cdr_calltype'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid call type. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('calltypenum')
+ || $self->ut_text('calltypename')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cdr_carrier.pm b/FS/FS/cdr_carrier.pm
new file mode 100644
index 0000000..609c939
--- /dev/null
+++ b/FS/FS/cdr_carrier.pm
@@ -0,0 +1,116 @@
+package FS::cdr_carrier;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cdr_carrier - Object methods for cdr_carrier records
+
+=head1 SYNOPSIS
+
+ use FS::cdr_carrier;
+
+ $record = new FS::cdr_carrier \%hash;
+ $record = new FS::cdr_carrier { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cdr_carrier object represents an CDR carrier or upstream.
+FS::cdr_carrier inherits from FS::Record. The following fields are currently
+supported:
+
+=over 4
+
+=item carrierid - primary key
+
+=item carriername - Carrier name
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new carrier. To add the carrier to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cdr_carrier'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid carrier. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('carrierid')
+ || $self->ut_text('carriername')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cdr_type.pm b/FS/FS/cdr_type.pm
new file mode 100644
index 0000000..e258bf8
--- /dev/null
+++ b/FS/FS/cdr_type.pm
@@ -0,0 +1,119 @@
+package FS::cdr_type;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cdr_type - Object methods for cdr_type records
+
+=head1 SYNOPSIS
+
+ use FS::cdr_type;
+
+ $record = new FS::cdr_type \%hash;
+ $record = new FS::cdr_type { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cdr_type object represents an CDR type. FS::cdr_type inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item cdrtypenum - primary key
+
+=item typename - CDR type name
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new CDR type. To add the CDR type to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cdr_type'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid CDR type. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('cdrtypenum')
+ || $self->ut_text('typename')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cdr_upstream_rate.pm b/FS/FS/cdr_upstream_rate.pm
new file mode 100644
index 0000000..2fd9782
--- /dev/null
+++ b/FS/FS/cdr_upstream_rate.pm
@@ -0,0 +1,138 @@
+package FS::cdr_upstream_rate;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::rate_detail;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cdr_upstream_rate - Object methods for cdr_upstream_rate records
+
+=head1 SYNOPSIS
+
+ use FS::cdr_upstream_rate;
+
+ $record = new FS::cdr_upstream_rate \%hash;
+ $record = new FS::cdr_upstream_rate { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cdr_upstream_rate object represents an upstream rate mapping to
+internal rate detail (see L<FS::rate_detail>). FS::cdr_upstream_rate inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item upstreamratenum - primary key
+
+=item upstream_rateid - CDR upstream Rate ID (cdr.upstream_rateid - see L<FS::cdr>)
+
+=item ratedetailnum - Rate detail - see L<FS::rate_detail>
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new upstream rate mapping. To add the upstream rate to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cdr_upstream_rate'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid upstream rate. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('upstreamratenum')
+ #|| $self->ut_number('upstream_rateid')
+ || $self->ut_alpha('upstream_rateid')
+ #|| $self->ut_text('upstream_rateid')
+ || $self->ut_foreign_key('ratedetailnum', 'rate_detail', 'ratedetailnum' )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item rate_detail
+
+Returns the internal rate detail object for this upstream rate (see
+L<FS::rate_detail>).
+
+=cut
+
+sub rate_detail {
+ my $self = shift;
+ qsearchs('rate_detail', { 'ratedetailnum' => $self->ratedetailnum } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/clientapi_session.pm b/FS/FS/clientapi_session.pm
new file mode 100644
index 0000000..f71a126
--- /dev/null
+++ b/FS/FS/clientapi_session.pm
@@ -0,0 +1,121 @@
+package FS::clientapi_session;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::clientapi_session - Object methods for clientapi_session records
+
+=head1 SYNOPSIS
+
+ use FS::clientapi_session;
+
+ $record = new FS::clientapi_session \%hash;
+ $record = new FS::clientapi_session { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::clientapi_session object represents an FS::ClientAPI session.
+FS::clientapi_session inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item sessionnum - primary key
+
+=item sessionid - session ID
+
+=item namespace - session namespace
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'clientapi_session'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('primary_key')
+ || $self->ut_number('validate_other_fields')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::ClientAPI>, <FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/clientapi_session_field.pm b/FS/FS/clientapi_session_field.pm
new file mode 100644
index 0000000..bfa487d
--- /dev/null
+++ b/FS/FS/clientapi_session_field.pm
@@ -0,0 +1,126 @@
+package FS::clientapi_session_field;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::clientapi_session_field - Object methods for clientapi_session_field records
+
+=head1 SYNOPSIS
+
+ use FS::clientapi_session_field;
+
+ $record = new FS::clientapi_session_field \%hash;
+ $record = new FS::clientapi_session_field { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::clientapi_session_field object represents a FS::ClientAPI session data
+field. FS::clientapi_session_field inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item fieldnum - primary key
+
+=item sessionnum - Base ClientAPI sesison (see L<FS::clientapi_session>)
+
+=item fieldname
+
+=item fieldvalie
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'clientapi_session_field'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('primary_key')
+ || $self->ut_number('validate_other_fields')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::clientapi_session>, L<FS::ClientAPI>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/conf.pm b/FS/FS/conf.pm
new file mode 100644
index 0000000..3faab14
--- /dev/null
+++ b/FS/FS/conf.pm
@@ -0,0 +1,114 @@
+package FS::conf;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::conf - Object methods for conf records
+
+=head1 SYNOPSIS
+
+ use FS::conf;
+
+ $record = new FS::conf \%hash;
+ $record = new FS::conf { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::conf object represents a configuration value. FS::conf inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item confnum - primary key
+
+=item agentnum - the agent to which this configuration value applies
+
+=item name - the name of the configuration value
+
+=item value - the configuration value
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new configuration value. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'conf'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid configuration value. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('confnum')
+ || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
+ || $self->ut_text('name')
+ || $self->ut_anything('value')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
new file mode 100644
index 0000000..704b350
--- /dev/null
+++ b/FS/FS/cust_bill.pm
@@ -0,0 +1,3265 @@
+package FS::cust_bill;
+
+use strict;
+use vars qw( @ISA $DEBUG $me $conf $money_char );
+use vars qw( $invoice_lines @buf ); #yuck
+use Fcntl qw(:flock); #for spool_csv
+use List::Util qw(min max);
+use Date::Format;
+use Text::Template 1.20;
+use File::Temp 0.14;
+use String::ShellQuote;
+use HTML::Entities;
+use Locale::Country;
+use FS::UID qw( datasrc );
+use FS::Misc qw( send_email send_fax generate_ps generate_pdf do_print );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::cust_main_Mixin;
+use FS::cust_main;
+use FS::cust_bill_pkg;
+use FS::cust_bill_pkg_display;
+use FS::cust_credit;
+use FS::cust_pay;
+use FS::cust_pkg;
+use FS::cust_credit_bill;
+use FS::pay_batch;
+use FS::cust_pay_batch;
+use FS::cust_bill_event;
+use FS::cust_event;
+use FS::part_pkg;
+use FS::cust_bill_pay;
+use FS::cust_bill_pay_batch;
+use FS::part_bill_event;
+use FS::payby;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+$DEBUG = 0;
+$me = '[FS::cust_bill]';
+
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+ $money_char = $conf->config('money_char') || '$';
+} );
+
+=head1 NAME
+
+FS::cust_bill - Object methods for cust_bill records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill;
+
+ $record = new FS::cust_bill \%hash;
+ $record = new FS::cust_bill { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ ( $total_previous_balance, @previous_cust_bill ) = $record->previous;
+
+ @cust_bill_pkg_objects = $cust_bill->cust_bill_pkg;
+
+ ( $total_previous_credits, @previous_cust_credit ) = $record->cust_credit;
+
+ @cust_pay_objects = $cust_bill->cust_pay;
+
+ $tax_amount = $record->tax;
+
+ @lines = $cust_bill->print_text;
+ @lines = $cust_bill->print_text $time;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill object represents an invoice; a declaration that a customer
+owes you money. The specific charges are itemized as B<cust_bill_pkg> records
+(see L<FS::cust_bill_pkg>). FS::cust_bill inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item invnum - primary key (assigned automatically for new invoices)
+
+=item custnum - customer (see L<FS::cust_main>)
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item charged - amount of this invoice
+
+=item printed - deprecated
+
+=item closed - books closed flag, empty or `Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new invoice. To add the invoice to the database, see L<"insert">.
+Invoices are normally created by calling the bill method of a customer object
+(see L<FS::cust_main>).
+
+=cut
+
+sub table { 'cust_bill'; }
+
+sub cust_linked { $_[0]->cust_main_custnum; }
+sub cust_unlinked_msg {
+ my $self = shift;
+ "WARNING: can't find cust_main.custnum ". $self->custnum.
+ ' (cust_bill.invnum '. $self->invnum. ')';
+}
+
+=item insert
+
+Adds this invoice to the database ("Posts" the invoice). If there is an error,
+returns the error, otherwise returns false.
+
+=item delete
+
+This method now works but you probably shouldn't use it. Instead, apply a
+credit against the invoice.
+
+Using this method to delete invoices outright is really, really bad. There
+would be no record you ever posted this invoice, and there are no check to
+make sure charged = 0 or that there are no associated cust_bill_pkg records.
+
+Really, don't use it.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return "Can't delete closed invoice" if $self->closed =~ /^Y/i;
+ $self->SUPER::delete(@_);
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+Only printed may be changed. printed is normally updated by calling the
+collect method of a customer object (see L<FS::cust_main>).
+
+=cut
+
+#replace can be inherited from Record.pm
+
+# replace_check is now the preferred way to #implement replace data checks
+# (so $object->replace() works without an argument)
+
+sub replace_check {
+ my( $new, $old ) = ( shift, shift );
+ return "Can't change custnum!" unless $old->custnum == $new->custnum;
+ #return "Can't change _date!" unless $old->_date eq $new->_date;
+ return "Can't change _date!" unless $old->_date == $new->_date;
+ return "Can't change charged!" unless $old->charged == $new->charged
+ || $old->charged == 0;
+
+ '';
+}
+
+=item check
+
+Checks all fields to make sure this is a valid invoice. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('invnum')
+ || $self->ut_number('custnum')
+ || $self->ut_numbern('_date')
+ || $self->ut_money('charged')
+ || $self->ut_numbern('printed')
+ || $self->ut_enum('closed', [ '', 'Y' ])
+ ;
+ return $error if $error;
+
+ return "Unknown customer"
+ unless qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+
+ $self->_date(time) unless $self->_date;
+
+ $self->printed(0) if $self->printed eq '';
+
+ $self->SUPER::check;
+}
+
+=item previous
+
+Returns a list consisting of the total previous balance for this customer,
+followed by the previous outstanding invoices (as FS::cust_bill objects also).
+
+=cut
+
+sub previous {
+ my $self = shift;
+ my $total = 0;
+ my @cust_bill = sort { $a->_date <=> $b->_date }
+ grep { $_->owed != 0 && $_->_date < $self->_date }
+ qsearch( 'cust_bill', { 'custnum' => $self->custnum } )
+ ;
+ foreach ( @cust_bill ) { $total += $_->owed; }
+ $total, @cust_bill;
+}
+
+=item cust_bill_pkg
+
+Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
+
+=cut
+
+sub cust_bill_pkg {
+ my $self = shift;
+ qsearch(
+ { 'table' => 'cust_bill_pkg',
+ 'hashref' => { 'invnum' => $self->invnum },
+ 'order_by' => 'ORDER BY billpkgnum',
+ }
+ );
+}
+
+=item cust_pkg
+
+Returns the packages (see L<FS::cust_pkg>) corresponding to the line items for
+this invoice.
+
+=cut
+
+sub cust_pkg {
+ my $self = shift;
+ my @cust_pkg = map { $_->cust_pkg } $self->cust_bill_pkg;
+ my %saw = ();
+ grep { ! $saw{$_->pkgnum}++ } @cust_pkg;
+}
+
+=item open_cust_bill_pkg
+
+Returns the open line items for this invoice.
+
+Note that cust_bill_pkg with both setup and recur fees are returned as two
+separate line items, each with only one fee.
+
+=cut
+
+# modeled after cust_main::open_cust_bill
+sub open_cust_bill_pkg {
+ my $self = shift;
+
+ # grep { $_->owed > 0 } $self->cust_bill_pkg
+
+ my %other = ( 'recur' => 'setup',
+ 'setup' => 'recur', );
+ my @open = ();
+ foreach my $field ( qw( recur setup )) {
+ push @open, map { $_->set( $other{$field}, 0 ); $_; }
+ grep { $_->owed($field) > 0 }
+ $self->cust_bill_pkg;
+ }
+
+ @open;
+}
+
+=item cust_bill_event
+
+Returns the completed invoice events (deprecated, old-style events - see L<FS::cust_bill_event>) for this invoice.
+
+=cut
+
+sub cust_bill_event {
+ my $self = shift;
+ qsearch( 'cust_bill_event', { 'invnum' => $self->invnum } );
+}
+
+=item num_cust_bill_event
+
+Returns the number of completed invoice events (deprecated, old-style events - see L<FS::cust_bill_event>) for this invoice.
+
+=cut
+
+sub num_cust_bill_event {
+ my $self = shift;
+ my $sql =
+ "SELECT COUNT(*) FROM cust_bill_event WHERE invnum = ?";
+ my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
+ $sth->execute($self->invnum) or die $sth->errstr. " executing $sql";
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item cust_event
+
+Returns the new-style customer billing events (see L<FS::cust_event>) for this invoice.
+
+=cut
+
+#false laziness w/cust_pkg.pm
+sub cust_event {
+ my $self = shift;
+ qsearch({
+ 'table' => 'cust_event',
+ 'addl_from' => 'JOIN part_event USING ( eventpart )',
+ 'hashref' => { 'tablenum' => $self->invnum },
+ 'extra_sql' => " AND eventtable = 'cust_bill' ",
+ });
+}
+
+=item num_cust_event
+
+Returns the number of new-style customer billing events (see L<FS::cust_event>) for this invoice.
+
+=cut
+
+#false laziness w/cust_pkg.pm
+sub num_cust_event {
+ my $self = shift;
+ my $sql =
+ "SELECT COUNT(*) FROM cust_event JOIN part_event USING ( eventpart ) ".
+ " WHERE tablenum = ? AND eventtable = 'cust_bill'";
+ my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
+ $sth->execute($self->invnum) or die $sth->errstr. " executing $sql";
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item cust_main
+
+Returns the customer (see L<FS::cust_main>) for this invoice.
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+=item cust_suspend_if_balance_over AMOUNT
+
+Suspends the customer associated with this invoice if the total amount owed on
+this invoice and all older invoices is greater than the specified amount.
+
+Returns a list: an empty list on success or a list of errors.
+
+=cut
+
+sub cust_suspend_if_balance_over {
+ my( $self, $amount ) = ( shift, shift );
+ my $cust_main = $self->cust_main;
+ if ( $cust_main->total_owed_date($self->_date) < $amount ) {
+ return ();
+ } else {
+ $cust_main->suspend(@_);
+ }
+}
+
+=item cust_credit
+
+Depreciated. See the cust_credited method.
+
+ #Returns a list consisting of the total previous credited (see
+ #L<FS::cust_credit>) and unapplied for this customer, followed by the previous
+ #outstanding credits (FS::cust_credit objects).
+
+=cut
+
+sub cust_credit {
+ use Carp;
+ croak "FS::cust_bill->cust_credit depreciated; see ".
+ "FS::cust_bill->cust_credit_bill";
+ #my $self = shift;
+ #my $total = 0;
+ #my @cust_credit = sort { $a->_date <=> $b->_date }
+ # grep { $_->credited != 0 && $_->_date < $self->_date }
+ # qsearch('cust_credit', { 'custnum' => $self->custnum } )
+ #;
+ #foreach (@cust_credit) { $total += $_->credited; }
+ #$total, @cust_credit;
+}
+
+=item cust_pay
+
+Depreciated. See the cust_bill_pay method.
+
+#Returns all payments (see L<FS::cust_pay>) for this invoice.
+
+=cut
+
+sub cust_pay {
+ use Carp;
+ croak "FS::cust_bill->cust_pay depreciated; see FS::cust_bill->cust_bill_pay";
+ #my $self = shift;
+ #sort { $a->_date <=> $b->_date }
+ # qsearch( 'cust_pay', { 'invnum' => $self->invnum } )
+ #;
+}
+
+=item cust_bill_pay
+
+Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice.
+
+=cut
+
+sub cust_bill_pay {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum } );
+}
+
+=item cust_credited
+
+Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice.
+
+=cut
+
+sub cust_credited {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum } )
+ ;
+}
+
+=item tax
+
+Returns the tax amount (see L<FS::cust_bill_pkg>) for this invoice.
+
+=cut
+
+sub tax {
+ my $self = shift;
+ my $total = 0;
+ my @taxlines = qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum ,
+ 'pkgnum' => 0 } );
+ foreach (@taxlines) { $total += $_->setup; }
+ $total;
+}
+
+=item owed
+
+Returns the amount owed (still outstanding) on this invoice, which is charged
+minus all payment applications (see L<FS::cust_bill_pay>) and credit
+applications (see L<FS::cust_credit_bill>).
+
+=cut
+
+sub owed {
+ my $self = shift;
+ my $balance = $self->charged;
+ $balance -= $_->amount foreach ( $self->cust_bill_pay );
+ $balance -= $_->amount foreach ( $self->cust_credited );
+ $balance = sprintf( "%.2f", $balance);
+ $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
+ $balance;
+}
+
+=item apply_payments_and_credits
+
+=cut
+
+sub apply_payments_and_credits {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update; #mutex
+
+ my @payments = grep { $_->unapplied > 0 } $self->cust_main->cust_pay;
+ my @credits = grep { $_->credited > 0 } $self->cust_main->cust_credit;
+
+ while ( $self->owed > 0 and ( @payments || @credits ) ) {
+
+ my $app = '';
+ if ( @payments && @credits ) {
+
+ #decide which goes first by weight of top (unapplied) line item
+
+ my @open_lineitems = $self->open_cust_bill_pkg;
+
+ my $max_pay_weight =
+ max( map { $_->part_pkg->pay_weight || 0 }
+ grep { $_ }
+ map { $_->cust_pkg }
+ @open_lineitems
+ );
+ my $max_credit_weight =
+ max( map { $_->part_pkg->credit_weight || 0 }
+ grep { $_ }
+ map { $_->cust_pkg }
+ @open_lineitems
+ );
+
+ #if both are the same... payments first? it has to be something
+ if ( $max_pay_weight >= $max_credit_weight ) {
+ $app = 'pay';
+ } else {
+ $app = 'credit';
+ }
+
+ } elsif ( @payments ) {
+ $app = 'pay';
+ } elsif ( @credits ) {
+ $app = 'credit';
+ } else {
+ die "guru meditation #12 and 35";
+ }
+
+ if ( $app eq 'pay' ) {
+
+ my $payment = shift @payments;
+
+ $app = new FS::cust_bill_pay {
+ 'paynum' => $payment->paynum,
+ 'amount' => sprintf('%.2f', min( $payment->unapplied, $self->owed ) ),
+ };
+
+ } elsif ( $app eq 'credit' ) {
+
+ my $credit = shift @credits;
+
+ $app = new FS::cust_credit_bill {
+ 'crednum' => $credit->crednum,
+ 'amount' => sprintf('%.2f', min( $credit->credited, $self->owed ) ),
+ };
+
+ } else {
+ die "guru meditation #12 and 35";
+ }
+
+ $app->invnum( $self->invnum );
+
+ my $error = $app->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error inserting ". $app->table. " record: $error";
+ }
+ die $error if $error;
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+
+}
+
+=item generate_email OPTION => VALUE ...
+
+Options:
+
+=over 4
+
+=item from
+
+sender address, required
+
+=item tempate
+
+alternate template name, optional
+
+=item print_text
+
+text attachment arrayref, optional
+
+=item subject
+
+email subject, optional
+
+=back
+
+Returns an argument list to be passed to L<FS::Misc::send_email>.
+
+=cut
+
+use MIME::Entity;
+
+sub generate_email {
+
+ my $self = shift;
+ my %args = @_;
+
+ my $me = '[FS::cust_bill::generate_email]';
+
+ my %return = (
+ 'from' => $args{'from'},
+ 'subject' => (($args{'subject'}) ? $args{'subject'} : 'Invoice'),
+ );
+
+ if (ref($args{'to'}) eq 'ARRAY') {
+ $return{'to'} = $args{'to'};
+ } else {
+ $return{'to'} = [ grep { $_ !~ /^(POST|FAX)$/ }
+ $self->cust_main->invoicing_list
+ ];
+ }
+
+ if ( $conf->exists('invoice_html') ) {
+
+ warn "$me creating HTML/text multipart message"
+ if $DEBUG;
+
+ $return{'nobody'} = 1;
+
+ my $alternative = build MIME::Entity
+ 'Type' => 'multipart/alternative',
+ 'Encoding' => '7bit',
+ 'Disposition' => 'inline'
+ ;
+
+ my $data;
+ if ( $conf->exists('invoice_email_pdf')
+ and scalar($conf->config('invoice_email_pdf_note')) ) {
+
+ warn "$me using 'invoice_email_pdf_note' in multipart message"
+ if $DEBUG;
+ $data = [ map { $_ . "\n" }
+ $conf->config('invoice_email_pdf_note')
+ ];
+
+ } else {
+
+ warn "$me not using 'invoice_email_pdf_note' in multipart message"
+ if $DEBUG;
+ if ( ref($args{'print_text'}) eq 'ARRAY' ) {
+ $data = $args{'print_text'};
+ } else {
+ $data = [ $self->print_text('', $args{'template'}) ];
+ }
+
+ }
+
+ $alternative->attach(
+ 'Type' => 'text/plain',
+ #'Encoding' => 'quoted-printable',
+ 'Encoding' => '7bit',
+ 'Data' => $data,
+ 'Disposition' => 'inline',
+ );
+
+ $args{'from'} =~ /\@([\w\.\-]+)/;
+ my $from = $1 || 'example.com';
+ my $content_id = join('.', rand()*(2**32), $$, time). "\@$from";
+
+ my $path = "$FS::UID::conf_dir/conf.$FS::UID::datasrc";
+ my $file;
+ if ( defined($args{'template'}) && length($args{'template'})
+ && -e "$path/logo_". $args{'template'}. ".png"
+ )
+ {
+ $file = "$path/logo_". $args{'template'}. ".png";
+ } else {
+ $file = "$path/logo.png";
+ }
+
+ my $image = build MIME::Entity
+ 'Type' => 'image/png',
+ 'Encoding' => 'base64',
+ 'Path' => $file,
+ 'Filename' => 'logo.png',
+ 'Content-ID' => "<$content_id>",
+ ;
+
+ $alternative->attach(
+ 'Type' => 'text/html',
+ 'Encoding' => 'quoted-printable',
+ 'Data' => [ '<html>',
+ ' <head>',
+ ' <title>',
+ ' '. encode_entities($return{'subject'}),
+ ' </title>',
+ ' </head>',
+ ' <body bgcolor="#e8e8e8">',
+ $self->print_html('', $args{'template'}, $content_id),
+ ' </body>',
+ '</html>',
+ ],
+ 'Disposition' => 'inline',
+ #'Filename' => 'invoice.pdf',
+ );
+
+ if ( $conf->exists('invoice_email_pdf') ) {
+
+ #attaching pdf too:
+ # multipart/mixed
+ # multipart/related
+ # multipart/alternative
+ # text/plain
+ # text/html
+ # image/png
+ # application/pdf
+
+ my $related = build MIME::Entity 'Type' => 'multipart/related',
+ 'Encoding' => '7bit';
+
+ #false laziness w/Misc::send_email
+ $related->head->replace('Content-type',
+ $related->mime_type.
+ '; boundary="'. $related->head->multipart_boundary. '"'.
+ '; type=multipart/alternative'
+ );
+
+ $related->add_part($alternative);
+
+ $related->add_part($image);
+
+ my $pdf = build MIME::Entity $self->mimebuild_pdf('', $args{'template'});
+
+ $return{'mimeparts'} = [ $related, $pdf ];
+
+ } else {
+
+ #no other attachment:
+ # multipart/related
+ # multipart/alternative
+ # text/plain
+ # text/html
+ # image/png
+
+ $return{'content-type'} = 'multipart/related';
+ $return{'mimeparts'} = [ $alternative, $image ];
+ $return{'type'} = 'multipart/alternative'; #Content-Type of first part...
+ #$return{'disposition'} = 'inline';
+
+ }
+
+ } else {
+
+ if ( $conf->exists('invoice_email_pdf') ) {
+ warn "$me creating PDF attachment"
+ if $DEBUG;
+
+ #mime parts arguments a la MIME::Entity->build().
+ $return{'mimeparts'} = [
+ { $self->mimebuild_pdf('', $args{'template'}) }
+ ];
+ }
+
+ if ( $conf->exists('invoice_email_pdf')
+ and scalar($conf->config('invoice_email_pdf_note')) ) {
+
+ warn "$me using 'invoice_email_pdf_note'"
+ if $DEBUG;
+ $return{'body'} = [ map { $_ . "\n" }
+ $conf->config('invoice_email_pdf_note')
+ ];
+
+ } else {
+
+ warn "$me not using 'invoice_email_pdf_note'"
+ if $DEBUG;
+ if ( ref($args{'print_text'}) eq 'ARRAY' ) {
+ $return{'body'} = $args{'print_text'};
+ } else {
+ $return{'body'} = [ $self->print_text('', $args{'template'}) ];
+ }
+
+ }
+
+ }
+
+ %return;
+
+}
+
+=item mimebuild_pdf
+
+Returns a list suitable for passing to MIME::Entity->build(), representing
+this invoice as PDF attachment.
+
+=cut
+
+sub mimebuild_pdf {
+ my $self = shift;
+ (
+ 'Type' => 'application/pdf',
+ 'Encoding' => 'base64',
+ 'Data' => [ $self->print_pdf(@_) ],
+ 'Disposition' => 'attachment',
+ 'Filename' => 'invoice.pdf',
+ );
+}
+
+=item send [ TEMPLATENAME [ , AGENTNUM [ , INVOICE_FROM ] ] ]
+
+Sends this invoice to the destinations configured for this customer: sends
+email, prints and/or faxes. See L<FS::cust_main_invoice>.
+
+TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
+
+AGENTNUM, if specified, means that this invoice will only be sent for customers
+of the specified agent or agent(s). AGENTNUM can be a scalar agentnum (for a
+single agent) or an arrayref of agentnums.
+
+INVOICE_FROM, if specified, overrides the default email invoice From: address.
+
+AMOUNT, if specified, only sends the invoice if the total amount owed on this
+invoice and all older invoices is greater than the specified amount.
+
+=cut
+
+sub queueable_send {
+ my %opt = @_;
+
+ my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } )
+ or die "invalid invoice number: " . $opt{invnum};
+
+ my @args = ( $opt{template}, $opt{agentnum} );
+ push @args, $opt{invoice_from}
+ if exists($opt{invoice_from}) && $opt{invoice_from};
+
+ my $error = $self->send( @args );
+ die $error if $error;
+
+}
+
+sub send {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+ if ( scalar(@_) && $_[0] ) {
+ my $agentnums = ref($_[0]) ? shift : [ shift ];
+ return 'N/A' unless grep { $_ == $self->cust_main->agentnum } @$agentnums;
+ }
+
+ my $invoice_from =
+ scalar(@_)
+ ? shift
+ : ( $self->_agent_invoice_from || #XXX should go away
+ $conf->config('invoice_from', $self->cust_main->agentnum )
+ );
+
+ my $balance_over = ( scalar(@_) && $_[0] !~ /^\s*$/ ) ? shift : 0;
+
+ return ''
+ unless $self->cust_main->total_owed_date($self->_date) > $balance_over;
+
+ my @invoicing_list = $self->cust_main->invoicing_list;
+
+ #$self->email_invoice($template, $invoice_from)
+ $self->email($template, $invoice_from)
+ if grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list;
+
+ #$self->print_invoice($template)
+ $self->print($template)
+ if grep { $_ eq 'POST' } @invoicing_list; #postal
+
+ $self->fax_invoice($template)
+ if grep { $_ eq 'FAX' } @invoicing_list; #fax
+
+ '';
+
+}
+
+=item email [ TEMPLATENAME [ , INVOICE_FROM ] ]
+
+Emails this invoice.
+
+TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
+
+INVOICE_FROM, if specified, overrides the default email invoice From: address.
+
+=cut
+
+sub queueable_email {
+ my %opt = @_;
+
+ my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } )
+ or die "invalid invoice number: " . $opt{invnum};
+
+ my @args = ( $opt{template} );
+ push @args, $opt{invoice_from}
+ if exists($opt{invoice_from}) && $opt{invoice_from};
+
+ my $error = $self->email( @args );
+ die $error if $error;
+
+}
+
+#sub email_invoice {
+sub email {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+ my $invoice_from =
+ scalar(@_)
+ ? shift
+ : ( $self->_agent_invoice_from || #XXX should go away
+ $conf->config('invoice_from', $self->cust_main->agentnum )
+ );
+
+
+ my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ }
+ $self->cust_main->invoicing_list;
+
+ #better to notify this person than silence
+ @invoicing_list = ($invoice_from) unless @invoicing_list;
+
+ my $subject = $self->email_subject($template);
+
+ my $error = send_email(
+ $self->generate_email(
+ 'from' => $invoice_from,
+ 'to' => [ grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ],
+ 'subject' => $subject,
+ 'template' => $template,
+ )
+ );
+ die "can't email invoice: $error\n" if $error;
+ #die "$error\n" if $error;
+
+}
+
+sub email_subject {
+ my $self = shift;
+
+ #my $template = scalar(@_) ? shift : '';
+ #per-template?
+
+ my $subject = $conf->config('invoice_subject', $self->cust_main->agentnum)
+ || 'Invoice';
+
+ my $cust_main = $self->cust_main;
+ my $name = $cust_main->name;
+ my $name_short = $cust_main->name_short;
+ my $invoice_number = $self->invnum;
+ my $invoice_date = $self->_date_pretty;
+
+ eval qq("$subject");
+}
+
+=item lpr_data [ TEMPLATENAME ]
+
+Returns the postscript or plaintext for this invoice as an arrayref.
+
+TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
+
+=cut
+
+sub lpr_data {
+ my( $self, $template) = @_;
+ $conf->exists('invoice_latex')
+ ? [ $self->print_ps('', $template) ]
+ : [ $self->print_text('', $template) ];
+}
+
+=item print [ TEMPLATENAME ]
+
+Prints this invoice.
+
+TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
+
+=cut
+
+#sub print_invoice {
+sub print {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+
+ do_print $self->lpr_data($template);
+}
+
+=item fax_invoice [ TEMPLATENAME ]
+
+Faxes this invoice.
+
+TEMPLATENAME, if specified, is the name of a suffix for alternate invoices.
+
+=cut
+
+sub fax_invoice {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+
+ die 'FAX invoice destination not (yet?) supported with plain text invoices.'
+ unless $conf->exists('invoice_latex');
+
+ my $dialstring = $self->cust_main->getfield('fax');
+ #Check $dialstring?
+
+ my $error = send_fax( 'docdata' => $self->lpr_data($template),
+ 'dialstring' => $dialstring,
+ );
+ die $error if $error;
+
+}
+
+=item ftp_invoice [ TEMPLATENAME ]
+
+Sends this invoice data via FTP.
+
+TEMPLATENAME is unused?
+
+=cut
+
+sub ftp_invoice {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+
+ $self->send_csv(
+ 'protocol' => 'ftp',
+ 'server' => $conf->config('cust_bill-ftpserver'),
+ 'username' => $conf->config('cust_bill-ftpusername'),
+ 'password' => $conf->config('cust_bill-ftppassword'),
+ 'dir' => $conf->config('cust_bill-ftpdir'),
+ 'format' => $conf->config('cust_bill-ftpformat'),
+ );
+}
+
+=item spool_invoice [ TEMPLATENAME ]
+
+Spools this invoice data (see L<FS::spool_csv>)
+
+TEMPLATENAME is unused?
+
+=cut
+
+sub spool_invoice {
+ my $self = shift;
+ my $template = scalar(@_) ? shift : '';
+
+ $self->spool_csv(
+ 'format' => $conf->config('cust_bill-spoolformat'),
+ 'agent_spools' => $conf->exists('cust_bill-spoolagent'),
+ );
+}
+
+=item send_if_newest [ TEMPLATENAME [ , AGENTNUM [ , INVOICE_FROM ] ] ]
+
+Like B<send>, but only sends the invoice if it is the newest open invoice for
+this customer.
+
+=cut
+
+sub send_if_newest {
+ my $self = shift;
+
+ return ''
+ if scalar(
+ grep { $_->owed > 0 }
+ qsearch('cust_bill', {
+ 'custnum' => $self->custnum,
+ #'_date' => { op=>'>', value=>$self->_date },
+ 'invnum' => { op=>'>', value=>$self->invnum },
+ } )
+ );
+
+ $self->send(@_);
+}
+
+=item send_csv OPTION => VALUE, ...
+
+Sends invoice as a CSV data-file to a remote host with the specified protocol.
+
+Options are:
+
+protocol - currently only "ftp"
+server
+username
+password
+dir
+
+The file will be named "N-YYYYMMDDHHMMSS.csv" where N is the invoice number
+and YYMMDDHHMMSS is a timestamp.
+
+See L</print_csv> for a description of the output format.
+
+=cut
+
+sub send_csv {
+ my($self, %opt) = @_;
+
+ #create file(s)
+
+ my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/cust_bill";
+ mkdir $spooldir, 0700 unless -d $spooldir;
+
+ my $tracctnum = $self->invnum. time2str('-%Y%m%d%H%M%S', time);
+ my $file = "$spooldir/$tracctnum.csv";
+
+ my ( $header, $detail ) = $self->print_csv(%opt, 'tracctnum' => $tracctnum );
+
+ open(CSV, ">$file") or die "can't open $file: $!";
+ print CSV $header;
+
+ print CSV $detail;
+
+ close CSV;
+
+ my $net;
+ if ( $opt{protocol} eq 'ftp' ) {
+ eval "use Net::FTP;";
+ die $@ if $@;
+ $net = Net::FTP->new($opt{server}) or die @$;
+ } else {
+ die "unknown protocol: $opt{protocol}";
+ }
+
+ $net->login( $opt{username}, $opt{password} )
+ or die "can't FTP to $opt{username}\@$opt{server}: login error: $@";
+
+ $net->binary or die "can't set binary mode";
+
+ $net->cwd($opt{dir}) or die "can't cwd to $opt{dir}";
+
+ $net->put($file) or die "can't put $file: $!";
+
+ $net->quit;
+
+ unlink $file;
+
+}
+
+=item spool_csv
+
+Spools CSV invoice data.
+
+Options are:
+
+=over 4
+
+=item format - 'default' or 'billco'
+
+=item dest - if set (to POST, EMAIL or FAX), only sends spools invoices if the customer has the corresponding invoice destinations set (see L<FS::cust_main_invoice>).
+
+=item agent_spools - if set to a true value, will spool to per-agent files rather than a single global file
+
+=item balanceover - if set, only spools the invoice if the total amount owed on this invoice and all older invoices is greater than the specified amount.
+
+=back
+
+=cut
+
+sub spool_csv {
+ my($self, %opt) = @_;
+
+ my $cust_main = $self->cust_main;
+
+ if ( $opt{'dest'} ) {
+ my %invoicing_list = map { /^(POST|FAX)$/ or 'EMAIL' =~ /^(.*)$/; $1 => 1 }
+ $cust_main->invoicing_list;
+ return 'N/A' unless $invoicing_list{$opt{'dest'}}
+ || ! keys %invoicing_list;
+ }
+
+ if ( $opt{'balanceover'} ) {
+ return 'N/A'
+ if $cust_main->total_owed_date($self->_date) < $opt{'balanceover'};
+ }
+
+ my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/cust_bill";
+ mkdir $spooldir, 0700 unless -d $spooldir;
+
+ my $tracctnum = $self->invnum. time2str('-%Y%m%d%H%M%S', time);
+
+ my $file =
+ "$spooldir/".
+ ( $opt{'agent_spools'} ? 'agentnum'.$cust_main->agentnum : 'spool' ).
+ ( lc($opt{'format'}) eq 'billco' ? '-header' : '' ) .
+ '.csv';
+
+ my ( $header, $detail ) = $self->print_csv(%opt, 'tracctnum' => $tracctnum );
+
+ open(CSV, ">>$file") or die "can't open $file: $!";
+ flock(CSV, LOCK_EX);
+ seek(CSV, 0, 2);
+
+ print CSV $header;
+
+ if ( lc($opt{'format'}) eq 'billco' ) {
+
+ flock(CSV, LOCK_UN);
+ close CSV;
+
+ $file =
+ "$spooldir/".
+ ( $opt{'agent_spools'} ? 'agentnum'.$cust_main->agentnum : 'spool' ).
+ '-detail.csv';
+
+ open(CSV,">>$file") or die "can't open $file: $!";
+ flock(CSV, LOCK_EX);
+ seek(CSV, 0, 2);
+ }
+
+ print CSV $detail;
+
+ flock(CSV, LOCK_UN);
+ close CSV;
+
+ return '';
+
+}
+
+=item print_csv OPTION => VALUE, ...
+
+Returns CSV data for this invoice.
+
+Options are:
+
+format - 'default' or 'billco'
+
+Returns a list consisting of two scalars. The first is a single line of CSV
+header information for this invoice. The second is one or more lines of CSV
+detail information for this invoice.
+
+If I<format> is not specified or "default", the fields of the CSV file are as
+follows:
+
+record_type, invnum, custnum, _date, charged, first, last, company, address1, address2, city, state, zip, country, pkg, setup, recur, sdate, edate
+
+=over 4
+
+=item record type - B<record_type> is either C<cust_bill> or C<cust_bill_pkg>
+
+B<record_type> is C<cust_bill> for the initial header line only. The
+last five fields (B<pkg> through B<edate>) are irrelevant, and all other
+fields are filled in.
+
+B<record_type> is C<cust_bill_pkg> for detail lines. Only the first two fields
+(B<record_type> and B<invnum>) and the last five fields (B<pkg> through B<edate>)
+are filled in.
+
+=item invnum - invoice number
+
+=item custnum - customer number
+
+=item _date - invoice date
+
+=item charged - total invoice amount
+
+=item first - customer first name
+
+=item last - customer first name
+
+=item company - company name
+
+=item address1 - address line 1
+
+=item address2 - address line 1
+
+=item city
+
+=item state
+
+=item zip
+
+=item country
+
+=item pkg - line item description
+
+=item setup - line item setup fee (one or both of B<setup> and B<recur> will be defined)
+
+=item recur - line item recurring fee (one or both of B<setup> and B<recur> will be defined)
+
+=item sdate - start date for recurring fee
+
+=item edate - end date for recurring fee
+
+=back
+
+If I<format> is "billco", the fields of the header CSV file are as follows:
+
+ +-------------------------------------------------------------------+
+ | FORMAT HEADER FILE |
+ |-------------------------------------------------------------------|
+ | Field | Description | Name | Type | Width |
+ | 1 | N/A-Leave Empty | RC | CHAR | 2 |
+ | 2 | N/A-Leave Empty | CUSTID | CHAR | 15 |
+ | 3 | Transaction Account No | TRACCTNUM | CHAR | 15 |
+ | 4 | Transaction Invoice No | TRINVOICE | CHAR | 15 |
+ | 5 | Transaction Zip Code | TRZIP | CHAR | 5 |
+ | 6 | Transaction Company Bill To | TRCOMPANY | CHAR | 30 |
+ | 7 | Transaction Contact Bill To | TRNAME | CHAR | 30 |
+ | 8 | Additional Address Unit Info | TRADDR1 | CHAR | 30 |
+ | 9 | Bill To Street Address | TRADDR2 | CHAR | 30 |
+ | 10 | Ancillary Billing Information | TRADDR3 | CHAR | 30 |
+ | 11 | Transaction City Bill To | TRCITY | CHAR | 20 |
+ | 12 | Transaction State Bill To | TRSTATE | CHAR | 2 |
+ | 13 | Bill Cycle Close Date | CLOSEDATE | CHAR | 10 |
+ | 14 | Bill Due Date | DUEDATE | CHAR | 10 |
+ | 15 | Previous Balance | BALFWD | NUM* | 9 |
+ | 16 | Pmt/CR Applied | CREDAPPLY | NUM* | 9 |
+ | 17 | Total Current Charges | CURRENTCHG | NUM* | 9 |
+ | 18 | Total Amt Due | TOTALDUE | NUM* | 9 |
+ | 19 | Total Amt Due | AMTDUE | NUM* | 9 |
+ | 20 | 30 Day Aging | AMT30 | NUM* | 9 |
+ | 21 | 60 Day Aging | AMT60 | NUM* | 9 |
+ | 22 | 90 Day Aging | AMT90 | NUM* | 9 |
+ | 23 | Y/N | AGESWITCH | CHAR | 1 |
+ | 24 | Remittance automation | SCANLINE | CHAR | 100 |
+ | 25 | Total Taxes & Fees | TAXTOT | NUM* | 9 |
+ | 26 | Customer Reference Number | CUSTREF | CHAR | 15 |
+ | 27 | Federal Tax*** | FEDTAX | NUM* | 9 |
+ | 28 | State Tax*** | STATETAX | NUM* | 9 |
+ | 29 | Other Taxes & Fees*** | OTHERTAX | NUM* | 9 |
+ +-------+-------------------------------+------------+------+-------+
+
+If I<format> is "billco", the fields of the detail CSV file are as follows:
+
+ FORMAT FOR DETAIL FILE
+ | | | |
+ Field | Description | Name | Type | Width
+ 1 | N/A-Leave Empty | RC | CHAR | 2
+ 2 | N/A-Leave Empty | CUSTID | CHAR | 15
+ 3 | Account Number | TRACCTNUM | CHAR | 15
+ 4 | Invoice Number | TRINVOICE | CHAR | 15
+ 5 | Line Sequence (sort order) | LINESEQ | NUM | 6
+ 6 | Transaction Detail | DETAILS | CHAR | 100
+ 7 | Amount | AMT | NUM* | 9
+ 8 | Line Format Control** | LNCTRL | CHAR | 2
+ 9 | Grouping Code | GROUP | CHAR | 2
+ 10 | User Defined | ACCT CODE | CHAR | 15
+
+=cut
+
+sub print_csv {
+ my($self, %opt) = @_;
+
+ eval "use Text::CSV_XS";
+ die $@ if $@;
+
+ my $cust_main = $self->cust_main;
+
+ my $csv = Text::CSV_XS->new({'always_quote'=>1});
+
+ if ( lc($opt{'format'}) eq 'billco' ) {
+
+ my $taxtotal = 0;
+ $taxtotal += $_->{'amount'} foreach $self->_items_tax;
+
+ my $duedate = $self->due_date2str('%m/%d/%Y'); #date_format?
+
+ my( $previous_balance, @unused ) = $self->previous; #previous balance
+
+ my $pmt_cr_applied = 0;
+ $pmt_cr_applied += $_->{'amount'}
+ foreach ( $self->_items_payments, $self->_items_credits ) ;
+
+ my $totaldue = sprintf('%.2f', $self->owed + $previous_balance);
+
+ $csv->combine(
+ '', # 1 | N/A-Leave Empty CHAR 2
+ '', # 2 | N/A-Leave Empty CHAR 15
+ $opt{'tracctnum'}, # 3 | Transaction Account No CHAR 15
+ $self->invnum, # 4 | Transaction Invoice No CHAR 15
+ $cust_main->zip, # 5 | Transaction Zip Code CHAR 5
+ $cust_main->company, # 6 | Transaction Company Bill To CHAR 30
+ #$cust_main->payname, # 7 | Transaction Contact Bill To CHAR 30
+ $cust_main->contact, # 7 | Transaction Contact Bill To CHAR 30
+ $cust_main->address2, # 8 | Additional Address Unit Info CHAR 30
+ $cust_main->address1, # 9 | Bill To Street Address CHAR 30
+ '', # 10 | Ancillary Billing Information CHAR 30
+ $cust_main->city, # 11 | Transaction City Bill To CHAR 20
+ $cust_main->state, # 12 | Transaction State Bill To CHAR 2
+
+ # XXX ?
+ time2str("%m/%d/%Y", $self->_date), # 13 | Bill Cycle Close Date CHAR 10
+
+ # XXX ?
+ $duedate, # 14 | Bill Due Date CHAR 10
+
+ $previous_balance, # 15 | Previous Balance NUM* 9
+ $pmt_cr_applied, # 16 | Pmt/CR Applied NUM* 9
+ sprintf("%.2f", $self->charged), # 17 | Total Current Charges NUM* 9
+ $totaldue, # 18 | Total Amt Due NUM* 9
+ $totaldue, # 19 | Total Amt Due NUM* 9
+ '', # 20 | 30 Day Aging NUM* 9
+ '', # 21 | 60 Day Aging NUM* 9
+ '', # 22 | 90 Day Aging NUM* 9
+ 'N', # 23 | Y/N CHAR 1
+ '', # 24 | Remittance automation CHAR 100
+ $taxtotal, # 25 | Total Taxes & Fees NUM* 9
+ $self->custnum, # 26 | Customer Reference Number CHAR 15
+ '0', # 27 | Federal Tax*** NUM* 9
+ sprintf("%.2f", $taxtotal), # 28 | State Tax*** NUM* 9
+ '0', # 29 | Other Taxes & Fees*** NUM* 9
+ );
+
+ } else {
+
+ $csv->combine(
+ 'cust_bill',
+ $self->invnum,
+ $self->custnum,
+ time2str("%x", $self->_date),
+ sprintf("%.2f", $self->charged),
+ ( map { $cust_main->getfield($_) }
+ qw( first last company address1 address2 city state zip country ) ),
+ map { '' } (1..5),
+ ) or die "can't create csv";
+ }
+
+ my $header = $csv->string. "\n";
+
+ my $detail = '';
+ if ( lc($opt{'format'}) eq 'billco' ) {
+
+ my $lineseq = 0;
+ foreach my $item ( $self->_items_pkg ) {
+
+ $csv->combine(
+ '', # 1 | N/A-Leave Empty CHAR 2
+ '', # 2 | N/A-Leave Empty CHAR 15
+ $opt{'tracctnum'}, # 3 | Account Number CHAR 15
+ $self->invnum, # 4 | Invoice Number CHAR 15
+ $lineseq++, # 5 | Line Sequence (sort order) NUM 6
+ $item->{'description'}, # 6 | Transaction Detail CHAR 100
+ $item->{'amount'}, # 7 | Amount NUM* 9
+ '', # 8 | Line Format Control** CHAR 2
+ '', # 9 | Grouping Code CHAR 2
+ '', # 10 | User Defined CHAR 15
+ );
+
+ $detail .= $csv->string. "\n";
+
+ }
+
+ } else {
+
+ foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
+
+ my($pkg, $setup, $recur, $sdate, $edate);
+ if ( $cust_bill_pkg->pkgnum ) {
+
+ ($pkg, $setup, $recur, $sdate, $edate) = (
+ $cust_bill_pkg->part_pkg->pkg,
+ ( $cust_bill_pkg->setup != 0
+ ? sprintf("%.2f", $cust_bill_pkg->setup )
+ : '' ),
+ ( $cust_bill_pkg->recur != 0
+ ? sprintf("%.2f", $cust_bill_pkg->recur )
+ : '' ),
+ ( $cust_bill_pkg->sdate
+ ? time2str("%x", $cust_bill_pkg->sdate)
+ : '' ),
+ ($cust_bill_pkg->edate
+ ?time2str("%x", $cust_bill_pkg->edate)
+ : '' ),
+ );
+
+ } else { #pkgnum tax
+ next unless $cust_bill_pkg->setup != 0;
+ my $itemdesc = defined $cust_bill_pkg->dbdef_table->column('itemdesc')
+ ? ( $cust_bill_pkg->itemdesc || 'Tax' )
+ : 'Tax';
+ ($pkg, $setup, $recur, $sdate, $edate) =
+ ( $itemdesc, sprintf("%10.2f",$cust_bill_pkg->setup), '', '', '' );
+ }
+
+ $csv->combine(
+ 'cust_bill_pkg',
+ $self->invnum,
+ ( map { '' } (1..11) ),
+ ($pkg, $setup, $recur, $sdate, $edate)
+ ) or die "can't create csv";
+
+ $detail .= $csv->string. "\n";
+
+ }
+
+ }
+
+ ( $header, $detail );
+
+}
+
+=item comp
+
+Pays this invoice with a compliemntary payment. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub comp {
+ my $self = shift;
+ my $cust_pay = new FS::cust_pay ( {
+ 'invnum' => $self->invnum,
+ 'paid' => $self->owed,
+ '_date' => '',
+ 'payby' => 'COMP',
+ 'payinfo' => $self->cust_main->payinfo,
+ 'paybatch' => '',
+ } );
+ $cust_pay->insert;
+}
+
+=item realtime_card
+
+Attempts to pay this invoice with a credit card payment via a
+Business::OnlinePayment realtime gateway. See
+http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment
+for supported processors.
+
+=cut
+
+sub realtime_card {
+ my $self = shift;
+ $self->realtime_bop( 'CC', @_ );
+}
+
+=item realtime_ach
+
+Attempts to pay this invoice with an electronic check (ACH) payment via a
+Business::OnlinePayment realtime gateway. See
+http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment
+for supported processors.
+
+=cut
+
+sub realtime_ach {
+ my $self = shift;
+ $self->realtime_bop( 'ECHECK', @_ );
+}
+
+=item realtime_lec
+
+Attempts to pay this invoice with phone bill (LEC) payment via a
+Business::OnlinePayment realtime gateway. See
+http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment
+for supported processors.
+
+=cut
+
+sub realtime_lec {
+ my $self = shift;
+ $self->realtime_bop( 'LEC', @_ );
+}
+
+sub realtime_bop {
+ my( $self, $method ) = @_;
+
+ my $cust_main = $self->cust_main;
+ my $balance = $cust_main->balance;
+ my $amount = ( $balance < $self->owed ) ? $balance : $self->owed;
+ $amount = sprintf("%.2f", $amount);
+ return "not run (balance $balance)" unless $amount > 0;
+
+ my $description = 'Internet Services';
+ if ( $conf->exists('business-onlinepayment-description') ) {
+ my $dtempl = $conf->config('business-onlinepayment-description');
+
+ my $agent_obj = $cust_main->agent
+ or die "can't retreive agent for $cust_main (agentnum ".
+ $cust_main->agentnum. ")";
+ my $agent = $agent_obj->agent;
+ my $pkgs = join(', ',
+ map { $_->part_pkg->pkg }
+ grep { $_->pkgnum } $self->cust_bill_pkg
+ );
+ $description = eval qq("$dtempl");
+ }
+
+ $cust_main->realtime_bop($method, $amount,
+ 'description' => $description,
+ 'invnum' => $self->invnum,
+ );
+
+}
+
+=item batch_card OPTION => VALUE...
+
+Adds a payment for this invoice to the pending credit card batch (see
+L<FS::cust_pay_batch>), or, if the B<realtime> option is set to a true value,
+runs the payment using a realtime gateway.
+
+=cut
+
+sub batch_card {
+ my ($self, %options) = @_;
+ my $cust_main = $self->cust_main;
+
+ $options{invnum} = $self->invnum;
+
+ $cust_main->batch_card(%options);
+}
+
+sub _agent_template {
+ my $self = shift;
+ $self->cust_main->agent_template;
+}
+
+sub _agent_invoice_from {
+ my $self = shift;
+ $self->cust_main->agent_invoice_from;
+}
+
+=item print_text [ TIME [ , TEMPLATE ] ]
+
+Returns an text invoice, as a list of lines.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub print_text {
+ my( $self, $today, $template ) = @_;
+
+ my %params = ( 'format' => 'template' );
+ $params{'time'} = $today if $today;
+ $params{'template'} = $template if $template;
+
+ $self->print_generic( %params );
+}
+
+=item print_latex [ TIME [ , TEMPLATE ] ]
+
+Internal method - returns a filename of a filled-in LaTeX template for this
+invoice (Note: add ".tex" to get the actual filename), and a filename of
+an associated logo (with the .eps extension included).
+
+See print_ps and print_pdf for methods that return PostScript and PDF output.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub print_latex {
+ my( $self, $today, $template ) = @_;
+
+ my %params = ( 'format' => 'latex' );
+ $params{'time'} = $today if $today;
+ $params{'template'} = $template if $template;
+
+ $template ||= $self->_agent_template;
+
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ my $lh = new File::Temp( TEMPLATE => 'invoice.'. $self->invnum. '.XXXXXXXX',
+ DIR => $dir,
+ SUFFIX => '.eps',
+ UNLINK => 0,
+ ) or die "can't open temp file: $!\n";
+
+ my $agentnum = $self->cust_main->agentnum;
+
+ if ( $template && $conf->exists("logo_${template}.eps", $agentnum) ) {
+ print $lh $conf->config_binary("logo_${template}.eps", $agentnum)
+ or die "can't write temp file: $!\n";
+ } else {
+ print $lh $conf->config_binary('logo.eps', $agentnum)
+ or die "can't write temp file: $!\n";
+ }
+ close $lh;
+ $params{'logo_file'} = $lh->filename;
+
+ my @filled_in = $self->print_generic( %params );
+
+ my $fh = new File::Temp( TEMPLATE => 'invoice.'. $self->invnum. '.XXXXXXXX',
+ DIR => $dir,
+ SUFFIX => '.tex',
+ UNLINK => 0,
+ ) or die "can't open temp file: $!\n";
+ print $fh join('', @filled_in );
+ close $fh;
+
+ $fh->filename =~ /^(.*).tex$/ or die "unparsable filename: ". $fh->filename;
+ return ($1, $params{'logo_file'});
+
+}
+
+=item print_generic OPTIONS_HASH
+
+Internal method - returns a filled-in template for this invoice as a scalar.
+
+See print_ps and print_pdf for methods that return PostScript and PDF output.
+
+Non optional options include
+ format - latex, html, template
+
+Optional options include
+
+template - a value used as a suffix for a configuration template
+
+time - a value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+cid -
+
+unsquelch_cdr - overrides any per customer cdr squelching when true
+
+=cut
+
+#what's with all the sprintf('%10.2f')'s in here? will it cause any
+# (alignment?) problems to change them all to '%.2f' ?
+sub print_generic {
+
+ my( $self, %params ) = @_;
+ my $today = $params{today} ? $params{today} : time;
+ warn "FS::cust_bill::print_generic called on $self with suffix $params{template}\n"
+ if $DEBUG;
+
+ my $format = $params{format};
+ die "Unknown format: $format"
+ unless $format =~ /^(latex|html|template)$/;
+
+ my $cust_main = $self->cust_main;
+ $cust_main->payname( $cust_main->first. ' '. $cust_main->getfield('last') )
+ unless $cust_main->payname
+ && $cust_main->payby !~ /^(CARD|DCRD|CHEK|DCHK)$/;
+
+ my %delimiters = ( 'latex' => [ '[@--', '--@]' ],
+ 'html' => [ '<%=', '%>' ],
+ 'template' => [ '{', '}' ],
+ );
+
+ #create the template
+ my $template = $params{template} ? $params{template} : $self->_agent_template;
+ my $templatefile = "invoice_$format";
+ $templatefile .= "_$template"
+ if length($template);
+ my @invoice_template = map "$_\n", $conf->config($templatefile)
+ or die "cannot load config data $templatefile";
+
+ my $old_latex = '';
+ if ( $format eq 'latex' && grep { /^%%Detail/ } @invoice_template ) {
+ #change this to a die when the old code is removed
+ warn "old-style invoice template $templatefile; ".
+ "patch with conf/invoice_latex.diff or use new conf/invoice_latex*\n";
+ $old_latex = 'true';
+ @invoice_template = _translate_old_latex_format(@invoice_template);
+ }
+
+ my $text_template = new Text::Template(
+ TYPE => 'ARRAY',
+ SOURCE => \@invoice_template,
+ DELIMITERS => $delimiters{$format},
+ );
+
+ $text_template->compile()
+ or die "Can't compile $templatefile: $Text::Template::ERROR\n";
+
+
+ # additional substitution could possibly cause breakage in existing templates
+ my %convert_maps = (
+ 'latex' => {
+ 'notes' => sub { map "$_", @_ },
+ 'footer' => sub { map "$_", @_ },
+ 'smallfooter' => sub { map "$_", @_ },
+ 'returnaddress' => sub { map "$_", @_ },
+ 'coupon' => sub { map "$_", @_ },
+ },
+ 'html' => {
+ 'notes' =>
+ sub {
+ map {
+ s/%%(.*)$/<!-- $1 -->/g;
+ s/\\section\*\{\\textsc\{(.)(.*)\}\}/<p><b><font size="+1">$1<\/font>\U$2<\/b>/g;
+ s/\\begin\{enumerate\}/<ol>/g;
+ s/\\item / <li>/g;
+ s/\\end\{enumerate\}/<\/ol>/g;
+ s/\\textbf\{(.*)\}/<b>$1<\/b>/g;
+ s/\\\\\*/<br>/g;
+ s/\\dollar ?/\$/g;
+ s/\\#/#/g;
+ s/~/&nbsp;/g;
+ $_;
+ } @_
+ },
+ 'footer' =>
+ sub { map { s/~/&nbsp;/g; s/\\\\\*?\s*$/<BR>/; $_; } @_ },
+ 'smallfooter' =>
+ sub { map { s/~/&nbsp;/g; s/\\\\\*?\s*$/<BR>/; $_; } @_ },
+ 'returnaddress' =>
+ sub {
+ map {
+ s/~/&nbsp;/g;
+ s/\\\\\*?\s*$/<BR>/;
+ s/\\hyphenation\{[\w\s\-]+}//;
+ s/\\([&])/$1/g;
+ $_;
+ } @_
+ },
+ 'coupon' => sub { "" },
+ },
+ 'template' => {
+ 'notes' =>
+ sub {
+ map {
+ s/%%.*$//g;
+ s/\\section\*\{\\textsc\{(.*)\}\}/\U$1/g;
+ s/\\begin\{enumerate\}//g;
+ s/\\item / * /g;
+ s/\\end\{enumerate\}//g;
+ s/\\textbf\{(.*)\}/$1/g;
+ s/\\\\\*/ /;
+ s/\\dollar ?/\$/g;
+ $_;
+ } @_
+ },
+ 'footer' =>
+ sub { map { s/~/ /g; s/\\\\\*?\s*$/\n/; $_; } @_ },
+ 'smallfooter' =>
+ sub { map { s/~/ /g; s/\\\\\*?\s*$/\n/; $_; } @_ },
+ 'returnaddress' =>
+ sub {
+ map {
+ s/~/ /g;
+ s/\\\\\*?\s*$/\n/; # dubious
+ s/\\hyphenation\{[\w\s\-]+}//;
+ $_;
+ } @_
+ },
+ 'coupon' => sub { "" },
+ },
+ );
+
+
+ # hashes for differing output formats
+ my %nbsps = ( 'latex' => '~',
+ 'html' => '', # '&nbps;' would be nice
+ 'template' => '', # not used
+ );
+ my $nbsp = $nbsps{$format};
+
+ my %escape_functions = ( 'latex' => \&_latex_escape,
+ 'html' => \&encode_entities,
+ 'template' => sub { shift },
+ );
+ my $escape_function = $escape_functions{$format};
+
+ my %date_formats = ( 'latex' => '%b %o, %Y',
+ 'html' => '%b&nbsp;%o,&nbsp;%Y',
+ 'template' => '%s',
+ );
+ my $date_format = $date_formats{$format};
+
+ my %embolden_functions = ( 'latex' => sub { return '\textbf{'. shift(). '}'
+ },
+ 'html' => sub { return '<b>'. shift(). '</b>'
+ },
+ 'template' => sub { shift },
+ );
+ my $embolden_function = $embolden_functions{$format};
+
+
+ # generate template variables
+ my $returnaddress;
+ if (
+ defined( $conf->config_orbase( "invoice_${format}returnaddress",
+ $template
+ )
+ )
+ && length( $conf->config_orbase( "invoice_${format}returnaddress",
+ $template
+ )
+ )
+ ) {
+
+ $returnaddress = join("\n",
+ $conf->config_orbase("invoice_${format}returnaddress", $template)
+ );
+
+ } elsif ( grep /\S/,
+ $conf->config_orbase('invoice_latexreturnaddress', $template) ) {
+
+ my $convert_map = $convert_maps{$format}{'returnaddress'};
+ $returnaddress =
+ join( "\n",
+ &$convert_map( $conf->config_orbase( "invoice_latexreturnaddress",
+ $template
+ )
+ )
+ );
+ } elsif ( grep /\S/, $conf->config('company_address', $self->cust_main->agentnum) ) {
+
+ my $convert_map = $convert_maps{$format}{'returnaddress'};
+ $returnaddress = join( "\n", &$convert_map(
+ map { s/( {2,})/'~' x length($1)/eg;
+ s/$/\\\\\*/;
+ $_
+ }
+ ( $conf->config('company_name', $self->cust_main->agentnum),
+ $conf->config('company_address', $self->cust_main->agentnum),
+ )
+ )
+ );
+
+ } else {
+
+ my $warning = "Couldn't find a return address; ".
+ "do you need to set the company_address configuration value?";
+ warn "$warning\n";
+ $returnaddress = $nbsp;
+ #$returnaddress = $warning;
+
+ }
+
+ my %invoice_data = (
+ 'company_name' => scalar( $conf->config('company_name', $self->cust_main->agentnum) ),
+ 'company_address' => join("\n", $conf->config('company_address', $self->cust_main->agentnum) ). "\n",
+ 'custnum' => $cust_main->display_custnum,
+ 'invnum' => $self->invnum,
+ 'date' => time2str($date_format, $self->_date),
+ 'today' => time2str('%b %o, %Y', $today),
+ 'agent' => &$escape_function($cust_main->agent->agent),
+ 'agent_custid' => &$escape_function($cust_main->agent_custid),
+ 'payname' => &$escape_function($cust_main->payname),
+ 'company' => &$escape_function($cust_main->company),
+ 'address1' => &$escape_function($cust_main->address1),
+ 'address2' => &$escape_function($cust_main->address2),
+ 'city' => &$escape_function($cust_main->city),
+ 'state' => &$escape_function($cust_main->state),
+ 'zip' => &$escape_function($cust_main->zip),
+ 'fax' => &$escape_function($cust_main->fax),
+ 'returnaddress' => $returnaddress,
+ #'quantity' => 1,
+ 'terms' => $self->terms,
+ 'template' => $template, #params{'template'},
+ #'notes' => join("\n", $conf->config('invoice_latexnotes') ),
+ # better hang on to conf_dir for a while
+ 'conf_dir' => "$FS::UID::conf_dir/conf.$FS::UID::datasrc",
+ 'page' => 1,
+ 'total_pages' => 1,
+ 'current_charges' => sprintf("%.2f", $self->charged),
+ 'duedate' => $self->due_date2str('%m/%d/%Y'), #date_format?
+ 'ship_enable' => $conf->exists('invoice-ship_address'),
+ 'unitprices' => $conf->exists('invoice-unitprice'),
+ );
+
+ my $countrydefault = $conf->config('countrydefault') || 'US';
+ my $prefix = $cust_main->has_ship_address ? 'ship_' : '';
+ foreach ( qw( contact company address1 address2 city state zip country fax) ){
+ my $method = $prefix.$_;
+ $invoice_data{"ship_$_"} = _latex_escape($cust_main->$method);
+ }
+ $invoice_data{'ship_country'} = ''
+ if ( $invoice_data{'ship_country'} eq $countrydefault );
+
+ $invoice_data{'cid'} = $params{'cid'}
+ if $params{'cid'};
+
+ if ( $cust_main->country eq $countrydefault ) {
+ $invoice_data{'country'} = '';
+ } else {
+ $invoice_data{'country'} = &$escape_function(code2country($cust_main->country));
+ }
+
+ my @address = ();
+ $invoice_data{'address'} = \@address;
+ push @address,
+ $cust_main->payname.
+ ( ( $cust_main->payby eq 'BILL' ) && $cust_main->payinfo
+ ? " (P.O. #". $cust_main->payinfo. ")"
+ : ''
+ )
+ ;
+ push @address, $cust_main->company
+ if $cust_main->company;
+ push @address, $cust_main->address1;
+ push @address, $cust_main->address2
+ if $cust_main->address2;
+ push @address,
+ $cust_main->city. ", ". $cust_main->state. " ". $cust_main->zip;
+ push @address, $invoice_data{'country'}
+ if $invoice_data{'country'};
+ push @address, ''
+ while (scalar(@address) < 5);
+
+ $invoice_data{'logo_file'} = $params{'logo_file'}
+ if $params{'logo_file'};
+
+ my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
+# my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
+ #my $balance_due = $self->owed + $pr_total - $cr_total;
+ my $balance_due = $self->owed + $pr_total;
+ $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
+ $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
+
+ #do variable substitution in notes, footer, smallfooter
+ foreach my $include (qw( notes footer smallfooter coupon )) {
+
+ my $inc_file = $conf->key_orbase("invoice_${format}$include", $template);
+ my @inc_src;
+
+ if ( $conf->exists($inc_file) && length( $conf->config($inc_file) ) ) {
+
+ @inc_src = $conf->config($inc_file);
+
+ } else {
+
+ $inc_file = $conf->key_orbase("invoice_latex$include", $template);
+
+ my $convert_map = $convert_maps{$format}{$include};
+
+ @inc_src = map { s/\[\@--/$delimiters{$format}[0]/g;
+ s/--\@\]/$delimiters{$format}[1]/g;
+ $_;
+ }
+ &$convert_map( $conf->config($inc_file) );
+
+ }
+
+ my $inc_tt = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", @inc_src ],
+ DELIMITERS => $delimiters{$format},
+ ) or die "Can't create new Text::Template object: $Text::Template::ERROR";
+
+ unless ( $inc_tt->compile() ) {
+ my $error = "Can't compile $inc_file template: $Text::Template::ERROR\n";
+ warn $error. "Template:\n". join('', map "$_\n", @inc_src);
+ die $error;
+ }
+
+ $invoice_data{$include} = $inc_tt->fill_in( HASH => \%invoice_data );
+
+ $invoice_data{$include} =~ s/\n+$//
+ if ($format eq 'latex');
+ }
+
+ $invoice_data{'po_line'} =
+ ( $cust_main->payby eq 'BILL' && $cust_main->payinfo )
+ ? &$escape_function("Purchase Order #". $cust_main->payinfo)
+ : $nbsp;
+
+ my %money_chars = ( 'latex' => '',
+ 'html' => $conf->config('money_char') || '$',
+ 'template' => '',
+ );
+ my $money_char = $money_chars{$format};
+
+ my %other_money_chars = ( 'latex' => '\dollar ',#XXX should be a config too
+ 'html' => $conf->config('money_char') || '$',
+ 'template' => '',
+ );
+ my $other_money_char = $other_money_chars{$format};
+
+ my @detail_items = ();
+ my @total_items = ();
+ my @buf = ();
+ my @sections = ();
+
+ $invoice_data{'detail_items'} = \@detail_items;
+ $invoice_data{'total_items'} = \@total_items;
+ $invoice_data{'buf'} = \@buf;
+ $invoice_data{'sections'} = \@sections;
+
+ my $previous_section = { 'description' => 'Previous Charges',
+ 'subtotal' => $other_money_char.
+ sprintf('%.2f', $pr_total),
+ };
+
+ my $taxtotal = 0;
+ my $tax_section = { 'description' => 'Taxes, Surcharges, and Fees',
+ 'subtotal' => $taxtotal }; # adjusted below
+
+ my $adjusttotal = 0;
+ my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
+ 'subtotal' => 0 }; # adjusted below
+
+ my $unsquelched = $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y';
+ my $multisection = $conf->exists('invoice_sections', $cust_main->agentnum);
+ my $late_sections = [];
+ if ( $multisection ) {
+ push @sections, $self->_items_sections( $late_sections );
+ }else{
+ push @sections, { 'description' => '', 'subtotal' => '' };
+ }
+
+ foreach my $line_item ( $conf->exists('disable_previous_balance')
+ ? ()
+ : $self->_items_previous
+ )
+ {
+ my $detail = {
+ ext_description => [],
+ };
+ $detail->{'ref'} = $line_item->{'pkgnum'};
+ $detail->{'quantity'} = 1;
+ $detail->{'section'} = $previous_section;
+ $detail->{'description'} = &$escape_function($line_item->{'description'});
+ if ( exists $line_item->{'ext_description'} ) {
+ @{$detail->{'ext_description'}} = map {
+ &$escape_function($_);
+ } @{$line_item->{'ext_description'}};
+ }
+ $detail->{'amount'} = ( $old_latex ? '' : $money_char).
+ $line_item->{'amount'};
+ $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
+
+ push @detail_items, $detail;
+ push @buf, [ $detail->{'description'},
+ $money_char. sprintf("%10.2f", $line_item->{'amount'}),
+ ];
+ }
+
+ if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
+ push @buf, ['','-----------'];
+ push @buf, [ 'Total Previous Balance',
+ $money_char. sprintf("%10.2f", $pr_total) ];
+ push @buf, ['',''];
+ }
+
+ foreach my $section (@sections, @$late_sections) {
+
+ $section->{'subtotal'} = $other_money_char.
+ sprintf('%.2f', $section->{'subtotal'})
+ if $multisection;
+
+ if ( $section->{'description'} ) {
+ push @buf, ( [ &$escape_function($section->{'description'}), '' ],
+ [ '', '' ],
+ );
+ }
+
+ my %options = ();
+ $options{'section'} = $section if $multisection;
+ $options{'format'} = $format;
+ $options{'escape_function'} = $escape_function;
+ $options{'format_function'} = sub { () } unless $unsquelched;
+ $options{'unsquelched'} = $unsquelched;
+
+ foreach my $line_item ( $self->_items_pkg(%options) ) {
+ my $detail = {
+ ext_description => [],
+ };
+ $detail->{'ref'} = $line_item->{'pkgnum'};
+ $detail->{'quantity'} = $line_item->{'quantity'};
+ $detail->{'section'} = $section;
+ $detail->{'description'} = &$escape_function($line_item->{'description'});
+ if ( exists $line_item->{'ext_description'} ) {
+ @{$detail->{'ext_description'}} = @{$line_item->{'ext_description'}};
+ }
+ $detail->{'amount'} = ( $old_latex ? '' : $money_char ).
+ $line_item->{'amount'};
+ $detail->{'unit_amount'} = ( $old_latex ? '' : $money_char ).
+ $line_item->{'unit_amount'};
+ $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
+
+ push @detail_items, $detail;
+ push @buf, ( [ $detail->{'description'},
+ $money_char. sprintf("%10.2f", $line_item->{'amount'}),
+ ],
+ map { [ " ". $_, '' ] } @{$detail->{'ext_description'}},
+ );
+ }
+
+ if ( $section->{'description'} ) {
+ push @buf, ( ['','-----------'],
+ [ $section->{'description'}. ' sub-total',
+ $money_char. sprintf("%10.2f", $section->{'subtotal'})
+ ],
+ [ '', '' ],
+ [ '', '' ],
+ );
+ }
+
+ }
+
+ if ( $multisection && !$conf->exists('disable_previous_balance') ) {
+ unshift @sections, $previous_section if $pr_total;
+ }
+
+ foreach my $tax ( $self->_items_tax ) {
+
+ $taxtotal += $tax->{'amount'};
+
+ my $description = &$escape_function( $tax->{'description'} );
+ my $amount = sprintf( '%.2f', $tax->{'amount'} );
+
+ if ( $multisection ) {
+
+ my $money = $old_latex ? '' : $money_char;
+ push @detail_items, {
+ ext_description => [],
+ ref => '',
+ quantity => '',
+ description => $description,
+ amount => $money. $amount,
+ product_code => '',
+ section => $tax_section,
+ };
+
+ } else {
+
+ push @total_items, {
+ 'total_item' => $description,
+ 'total_amount' => $other_money_char. $amount,
+ };
+
+ }
+
+ push @buf,[ $description,
+ $money_char. $amount,
+ ];
+
+ }
+
+ if ( $taxtotal ) {
+ my $total = {};
+ $total->{'total_item'} = 'Sub-total';
+ $total->{'total_amount'} =
+ $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
+
+ if ( $multisection ) {
+ $tax_section->{'subtotal'} = $other_money_char.
+ sprintf('%.2f', $taxtotal);
+ $tax_section->{'pretotal'} = 'New charges sub-total '.
+ $total->{'total_amount'};
+ push @sections, $tax_section if $taxtotal;
+ }else{
+ unshift @total_items, $total;
+ }
+ }
+ $invoice_data{'taxtotal'} = sprintf('%.2f', $taxtotal);
+
+ push @buf,['','-----------'];
+ push @buf,[( $conf->exists('disable_previous_balance')
+ ? 'Total Charges'
+ : 'Total New Charges'
+ ),
+ $money_char. sprintf("%10.2f",$self->charged) ];
+ push @buf,['',''];
+
+ {
+ my $total = {};
+ $total->{'total_item'} = &$embolden_function('Total');
+ $total->{'total_amount'} =
+ &$embolden_function(
+ $other_money_char.
+ sprintf( '%.2f',
+ $self->charged + ( $conf->exists('disable_previous_balance')
+ ? 0
+ : $pr_total
+ )
+ )
+ );
+ if ( $multisection ) {
+ $adjust_section->{'pretotal'} = 'New charges total '. $other_money_char.
+ sprintf('%.2f', $self->charged );
+ }else{
+ push @total_items, $total;
+ }
+ push @buf,['','-----------'];
+ push @buf,['Total Charges',
+ $money_char.
+ sprintf( '%10.2f', $self->charged +
+ ( $conf->exists('disable_previous_balance')
+ ? 0
+ : $pr_total
+ )
+ )
+ ];
+ push @buf,['',''];
+ }
+
+ unless ( $conf->exists('disable_previous_balance') ) {
+ #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
+
+ # credits
+ my $credittotal = 0;
+ foreach my $credit ( $self->_items_credits ) {
+ my $total;
+ $total->{'total_item'} = &$escape_function($credit->{'description'});
+ $credittotal += $credit->{'amount'};
+ $total->{'total_amount'} = '-'. $other_money_char. $credit->{'amount'};
+ $adjusttotal += $credit->{'amount'};
+ if ( $multisection ) {
+ my $money = $old_latex ? '' : $money_char;
+ push @detail_items, {
+ ext_description => [],
+ ref => '',
+ quantity => '',
+ description => &$escape_function($credit->{'description'}),
+ amount => $money. $credit->{'amount'},
+ product_code => '',
+ section => $adjust_section,
+ };
+ }else{
+ push @total_items, $total;
+ }
+ }
+ $invoice_data{'credittotal'} = sprintf('%.2f', $credittotal);
+
+ # credits (again)
+ foreach ( $self->cust_credited ) {
+
+ #something more elaborate if $_->amount ne $_->cust_credit->credited ?
+
+ my $reason = substr($_->cust_credit->reason,0,32);
+ $reason .= '...' if length($reason) < length($_->cust_credit->reason);
+ $reason = " ($reason) " if $reason;
+ push @buf,[
+ "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")". $reason,
+ $money_char. sprintf("%10.2f",$_->amount)
+ ];
+ }
+
+ # payments
+ my $paymenttotal = 0;
+ foreach my $payment ( $self->_items_payments ) {
+ my $total = {};
+ $total->{'total_item'} = &$escape_function($payment->{'description'});
+ $paymenttotal += $payment->{'amount'};
+ $total->{'total_amount'} = '-'. $other_money_char. $payment->{'amount'};
+ $adjusttotal += $payment->{'amount'};
+ if ( $multisection ) {
+ my $money = $old_latex ? '' : $money_char;
+ push @detail_items, {
+ ext_description => [],
+ ref => '',
+ quantity => '',
+ description => &$escape_function($payment->{'description'}),
+ amount => $money. $payment->{'amount'},
+ product_code => '',
+ section => $adjust_section,
+ };
+ }else{
+ push @total_items, $total;
+ }
+ push @buf, [ $payment->{'description'},
+ $money_char. sprintf("%10.2f", $payment->{'amount'}),
+ ];
+ }
+ $invoice_data{'paymenttotal'} = sprintf('%.2f', $paymenttotal);
+
+ if ( $multisection ) {
+ $adjust_section->{'subtotal'} = $other_money_char.
+ sprintf('%.2f', $adjusttotal);
+ push @sections, $adjust_section;
+ }
+
+ {
+ my $total;
+ $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
+ $total->{'total_amount'} =
+ &$embolden_function(
+ $other_money_char. sprintf('%.2f', $self->owed + $pr_total )
+ );
+ if ( $multisection ) {
+ $adjust_section->{'posttotal'} = $total->{'total_item'}. ' '.
+ $total->{'total_amount'};
+ }else{
+ push @total_items, $total;
+ }
+ push @buf,['','-----------'];
+ push @buf,[$self->balance_due_msg, $money_char.
+ sprintf("%10.2f", $balance_due ) ];
+ }
+ }
+
+ if ( $multisection ) {
+ push @sections, @$late_sections
+ if $unsquelched;
+ }
+
+ $invoice_lines = 0;
+ my $wasfunc = 0;
+ foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
+ /invoice_lines\((\d*)\)/;
+ $invoice_lines += $1 || scalar(@buf);
+ $wasfunc=1;
+ }
+ die "no invoice_lines() functions in template?"
+ if ( $format eq 'template' && !$wasfunc );
+
+ if ($format eq 'template') {
+
+ if ( $invoice_lines ) {
+ $invoice_data{'total_pages'} = int( scalar(@buf) / $invoice_lines );
+ $invoice_data{'total_pages'}++
+ if scalar(@buf) % $invoice_lines;
+ }
+
+ #setup subroutine for the template
+ sub FS::cust_bill::_template::invoice_lines {
+ my $lines = shift || scalar(@FS::cust_bill::_template::buf);
+ map {
+ scalar(@FS::cust_bill::_template::buf)
+ ? shift @FS::cust_bill::_template::buf
+ : [ '', '' ];
+ }
+ ( 1 .. $lines );
+ }
+
+ my $lines;
+ my @collect;
+ while (@buf) {
+ push @collect, split("\n",
+ $text_template->fill_in( HASH => \%invoice_data,
+ PACKAGE => 'FS::cust_bill::_template'
+ )
+ );
+ $FS::cust_bill::_template::page++;
+ }
+ map "$_\n", @collect;
+ }else{
+ warn "filling in template for invoice ". $self->invnum. "\n"
+ if $DEBUG;
+ warn join("\n", map " $_ => ". $invoice_data{$_}, keys %invoice_data). "\n"
+ if $DEBUG > 1;
+
+ $text_template->fill_in(HASH => \%invoice_data);
+ }
+}
+
+=item print_ps [ TIME [ , TEMPLATE ] ]
+
+Returns an postscript invoice, as a scalar.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub print_ps {
+ my $self = shift;
+
+ my ($file, $lfile) = $self->print_latex(@_);
+ my $ps = generate_ps($file);
+ unlink($lfile);
+
+ $ps;
+}
+
+=item print_pdf [ TIME [ , TEMPLATE ] ]
+
+Returns an PDF invoice, as a scalar.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub print_pdf {
+ my $self = shift;
+
+ my ($file, $lfile) = $self->print_latex(@_);
+ my $pdf = generate_pdf($file);
+ unlink($lfile);
+
+ $pdf;
+}
+
+=item print_html [ TIME [ , TEMPLATE [ , CID ] ] ]
+
+Returns an HTML invoice, as a scalar.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+CID is a MIME Content-ID used to create a "cid:" URL for the logo image, used
+when emailing the invoice as part of a multipart/related MIME email.
+
+=cut
+
+sub print_html {
+ my $self = shift;
+ my %params;
+ if ( ref $_[0] ) {
+ %params = %{ shift() };
+ }else{
+ $params{'time'} = shift;
+ $params{'template'} = shift;
+ $params{'cid'} = shift;
+ }
+
+ $params{'format'} = 'html';
+
+ $self->print_generic( %params );
+}
+
+# quick subroutine for print_latex
+#
+# There are ten characters that LaTeX treats as special characters, which
+# means that they do not simply typeset themselves:
+# # $ % & ~ _ ^ \ { }
+#
+# TeX ignores blanks following an escaped character; if you want a blank (as
+# in "10% of ..."), you have to "escape" the blank as well ("10\%\ of ...").
+
+sub _latex_escape {
+ my $value = shift;
+ $value =~ s/([#\$%&~_\^{}])( )?/"\\$1". ( ( defined($2) && length($2) ) ? "\\$2" : '' )/ge;
+ $value =~ s/([<>])/\$$1\$/g;
+ $value;
+}
+
+#utility methods for print_*
+
+sub _translate_old_latex_format {
+ warn "_translate_old_latex_format called\n"
+ if $DEBUG;
+
+ my @template = ();
+ while ( @_ ) {
+ my $line = shift;
+
+ if ( $line =~ /^%%Detail\s*$/ ) {
+
+ push @template, q![@--!,
+ q! foreach my $_tr_line (@detail_items) {!,
+ q! if ( scalar ($_tr_item->{'ext_description'} ) ) {!,
+ q! $_tr_line->{'description'} .= !,
+ q! "\\tabularnewline\n~~".!,
+ q! join( "\\tabularnewline\n~~",!,
+ q! @{$_tr_line->{'ext_description'}}!,
+ q! );!,
+ q! }!;
+
+ while ( ( my $line_item_line = shift )
+ !~ /^%%EndDetail\s*$/ ) {
+ $line_item_line =~ s/'/\\'/g; # nice LTS
+ $line_item_line =~ s/\\/\\\\/g; # escape quotes and backslashes
+ $line_item_line =~ s/\$(\w+)/'. \$_tr_line->{$1}. '/g;
+ push @template, " \$OUT .= '$line_item_line';";
+ }
+
+ push @template, '}',
+ '--@]';
+
+ } elsif ( $line =~ /^%%TotalDetails\s*$/ ) {
+
+ push @template, '[@--',
+ ' foreach my $_tr_line (@total_items) {';
+
+ while ( ( my $total_item_line = shift )
+ !~ /^%%EndTotalDetails\s*$/ ) {
+ $total_item_line =~ s/'/\\'/g; # nice LTS
+ $total_item_line =~ s/\\/\\\\/g; # escape quotes and backslashes
+ $total_item_line =~ s/\$(\w+)/'. \$_tr_line->{$1}. '/g;
+ push @template, " \$OUT .= '$total_item_line';";
+ }
+
+ push @template, '}',
+ '--@]';
+
+ } else {
+ $line =~ s/\$(\w+)/[\@-- \$$1 --\@]/g;
+ push @template, $line;
+ }
+
+ }
+
+ if ($DEBUG) {
+ warn "$_\n" foreach @template;
+ }
+
+ (@template);
+}
+
+sub terms {
+ my $self = shift;
+
+ #check for an invoice- specific override (eventually)
+
+ #check for a customer- specific override
+ return $self->cust_main->invoice_terms
+ if $self->cust_main->invoice_terms;
+
+ #use configured default or default default
+ $conf->config('invoice_default_terms') || 'Payable upon receipt';
+}
+
+sub due_date {
+ my $self = shift;
+ my $duedate = '';
+ if ( $self->terms =~ /^\s*Net\s*(\d+)\s*$/ ) {
+ $duedate = $self->_date() + ( $1 * 86400 );
+ }
+ $duedate;
+}
+
+sub due_date2str {
+ my $self = shift;
+ $self->due_date ? time2str(shift, $self->due_date) : '';
+}
+
+sub balance_due_msg {
+ my $self = shift;
+ my $msg = 'Balance Due';
+ return $msg unless $self->terms;
+ if ( $self->due_date ) {
+ $msg .= ' - Please pay by '. $self->due_date2str('%x');
+ } elsif ( $self->terms ) {
+ $msg .= ' - '. $self->terms;
+ }
+ $msg;
+}
+
+sub balance_due_date {
+ my $self = shift;
+ my $duedate = '';
+ if ( $conf->exists('invoice_default_terms')
+ && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) {
+ $duedate = time2str("%m/%d/%Y", $self->_date + ($1*86400) );
+ }
+ $duedate;
+}
+
+=item invnum_date_pretty
+
+Returns a string with the invoice number and date, for example:
+"Invoice #54 (3/20/2008)"
+
+=cut
+
+sub invnum_date_pretty {
+ my $self = shift;
+ 'Invoice #'. $self->invnum. ' ('. $self->_date_pretty. ')';
+}
+
+=item _date_pretty
+
+Returns a string with the date, for example: "3/20/2008"
+
+=cut
+
+sub _date_pretty {
+ my $self = shift;
+ time2str('%x', $self->_date);
+}
+
+sub _items_sections {
+ my $self = shift;
+ my $late = shift;
+
+ my %s = ();
+ my %l = ();
+
+ foreach my $cust_bill_pkg ( $self->cust_bill_pkg )
+ {
+
+ if ( $cust_bill_pkg->pkgnum > 0 ) {
+ my $usage = $cust_bill_pkg->usage;
+
+ foreach my $display ($cust_bill_pkg->cust_bill_pkg_display) {
+ my $desc = $display->section;
+ my $type = $display->type;
+
+ if ( $display->post_total ) {
+ if (! $type || $type eq 'S') {
+ $l{$desc} += $cust_bill_pkg->setup
+ if ( $cust_bill_pkg->setup != 0 );
+ }
+
+ if (! $type) {
+ $l{$desc} += $cust_bill_pkg->recur
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'R') {
+ $l{$desc} += $cust_bill_pkg->recur - $usage
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'U') {
+ $l{$desc} += $usage;
+ }
+
+ } else {
+ if (! $type || $type eq 'S') {
+ $s{$desc} += $cust_bill_pkg->setup
+ if ( $cust_bill_pkg->setup != 0 );
+ }
+
+ if (! $type) {
+ $s{$desc} += $cust_bill_pkg->recur
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'R') {
+ $s{$desc} += $cust_bill_pkg->recur - $usage
+ if ( $cust_bill_pkg->recur != 0 );
+ }
+
+ if ($type && $type eq 'U') {
+ $s{$desc} += $usage;
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ push @$late, map { { 'description' => $_,
+ 'subtotal' => $l{$_},
+ 'post_total' => 1,
+ } } sort keys %l;
+
+ map { {'description' => $_, 'subtotal' => $s{$_}} } sort keys %s;
+
+}
+
+sub _items {
+ my $self = shift;
+
+ #my @display = scalar(@_)
+ # ? @_
+ # : qw( _items_previous _items_pkg );
+ # #: qw( _items_pkg );
+ # #: qw( _items_previous _items_pkg _items_tax _items_credits _items_payments );
+ my @display = qw( _items_previous _items_pkg );
+
+ my @b = ();
+ foreach my $display ( @display ) {
+ push @b, $self->$display(@_);
+ }
+ @b;
+}
+
+sub _items_previous {
+ my $self = shift;
+ my $cust_main = $self->cust_main;
+ my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
+ my @b = ();
+ foreach ( @pr_cust_bill ) {
+ push @b, {
+ 'description' => 'Previous Balance, Invoice #'. $_->invnum.
+ ' ('. time2str('%x',$_->_date). ')',
+ #'pkgpart' => 'N/A',
+ 'pkgnum' => 'N/A',
+ 'amount' => sprintf("%.2f", $_->owed),
+ };
+ }
+ @b;
+
+ #{
+ # 'description' => 'Previous Balance',
+ # #'pkgpart' => 'N/A',
+ # 'pkgnum' => 'N/A',
+ # 'amount' => sprintf("%10.2f", $pr_total ),
+ # 'ext_description' => [ map {
+ # "Invoice ". $_->invnum.
+ # " (". time2str("%x",$_->_date). ") ".
+ # sprintf("%10.2f", $_->owed)
+ # } @pr_cust_bill ],
+
+ #};
+}
+
+sub _items_pkg {
+ my $self = shift;
+ my @cust_bill_pkg = grep { $_->pkgnum } $self->cust_bill_pkg;
+ $self->_items_cust_bill_pkg(\@cust_bill_pkg, @_);
+}
+
+sub _taxsort {
+ return 0 unless $a cmp $b;
+ return -1 if $b eq 'Tax';
+ return 1 if $a eq 'Tax';
+ return -1 if $b eq 'Other surcharges';
+ return 1 if $a eq 'Other surcharges';
+ $a cmp $b;
+}
+
+sub _items_tax {
+ my $self = shift;
+ my @cust_bill_pkg = sort _taxsort grep { ! $_->pkgnum } $self->cust_bill_pkg;
+ $self->_items_cust_bill_pkg(\@cust_bill_pkg, @_);
+}
+
+sub _items_cust_bill_pkg {
+ my $self = shift;
+ my $cust_bill_pkg = shift;
+ my %opt = @_;
+
+ my $format = $opt{format} || '';
+ my $escape_function = $opt{escape_function} || sub { shift };
+ my $format_function = $opt{format_function} || '';
+ my $unsquelched = $opt{unsquelched} || '';
+ my $section = $opt{section}->{description} if $opt{section};
+
+ my @b = ();
+ foreach my $cust_bill_pkg ( @$cust_bill_pkg )
+ {
+ foreach my $display ( grep { defined($section)
+ ? $_->section eq $section
+ : 1
+ }
+ $cust_bill_pkg->cust_bill_pkg_display
+ )
+ {
+
+ my $type = $display->type;
+
+ my $cust_pkg = $cust_bill_pkg->cust_pkg;
+
+ my $desc = $cust_bill_pkg->desc;
+ $desc = substr($desc, 0, 50). '...'
+ if $format eq 'latex' && length($desc) > 50;
+
+ my %details_opt = ( 'format' => $format,
+ 'escape_function' => $escape_function,
+ 'format_function' => $format_function,
+ );
+
+ if ( $cust_bill_pkg->pkgnum > 0 ) {
+
+ if ( $cust_bill_pkg->setup != 0 && (!$type || $type eq 'S') ) {
+
+ my $description = $desc;
+ $description .= ' Setup' if $cust_bill_pkg->recur != 0;
+
+ my @d = map &{$escape_function}($_),
+ $cust_pkg->h_labels_short($self->_date);
+ push @d, $cust_bill_pkg->details(%details_opt)
+ if $cust_bill_pkg->recur == 0;
+
+ push @b, {
+ description => $description,
+ #pkgpart => $part_pkg->pkgpart,
+ pkgnum => $cust_bill_pkg->pkgnum,
+ amount => sprintf("%.2f", $cust_bill_pkg->setup),
+ unit_amount => sprintf("%.2f", $cust_bill_pkg->unitsetup),
+ quantity => $cust_bill_pkg->quantity,
+ ext_description => \@d,
+ };
+
+ }
+
+ if ( $cust_bill_pkg->recur != 0 &&
+ ( !$type || $type eq 'R' || $type eq 'U' )
+ )
+ {
+
+ my $is_summary = $display->summary;
+ my $description = $is_summary ? "Usage charges" : $desc;
+
+ unless ( $conf->exists('disable_line_item_date_ranges') ) {
+ $description .= " (" . time2str("%x", $cust_bill_pkg->sdate).
+ " - ". time2str("%x", $cust_bill_pkg->edate). ")";
+ }
+
+ #at least until cust_bill_pkg has "past" ranges in addition to
+ #the "future" sdate/edate ones... see #3032
+ my @d = ();
+ push @d, map &{$escape_function}($_),
+ $cust_pkg->h_labels_short($self->_date)
+ #$cust_bill_pkg->edate,
+ #$cust_bill_pkg->sdate),
+ ;
+
+ @d = () if ($cust_bill_pkg->itemdesc || $is_summary);
+ push @d, $cust_bill_pkg->details(%details_opt)
+ unless ($is_summary || $type && $type eq 'R');
+
+ my $amount = 0;
+ if (!$type) {
+ $amount = $cust_bill_pkg->recur;
+ }elsif($type eq 'R') {
+ $amount = $cust_bill_pkg->recur - $cust_bill_pkg->usage;
+ }elsif($type eq 'U') {
+ $amount = $cust_bill_pkg->usage;
+ }
+
+ push @b, {
+ description => $description,
+ #pkgpart => $part_pkg->pkgpart,
+ pkgnum => $cust_bill_pkg->pkgnum,
+ amount => sprintf("%.2f", $amount),
+ unit_amount => sprintf("%.2f", $cust_bill_pkg->unitrecur),
+ quantity => $cust_bill_pkg->quantity,
+ ext_description => \@d,
+ } unless ( $type eq 'U' && ! $amount );
+
+ }
+
+ } else { #pkgnum tax or one-shot line item (??)
+
+ if ( $cust_bill_pkg->setup != 0 ) {
+ push @b, {
+ 'description' => $desc,
+ 'amount' => sprintf("%.2f", $cust_bill_pkg->setup),
+ };
+ }
+ if ( $cust_bill_pkg->recur != 0 ) {
+ push @b, {
+ 'description' => "$desc (".
+ time2str("%x", $cust_bill_pkg->sdate). ' - '.
+ time2str("%x", $cust_bill_pkg->edate). ')',
+ 'amount' => sprintf("%.2f", $cust_bill_pkg->recur),
+ };
+ }
+
+ }
+
+ }
+
+ }
+
+ @b;
+
+}
+
+sub _items_credits {
+ my $self = shift;
+
+ my @b;
+ #credits
+ foreach ( $self->cust_credited ) {
+
+ #something more elaborate if $_->amount ne $_->cust_credit->credited ?
+
+ my $reason = $_->cust_credit->reason;
+ #my $reason = substr($_->cust_credit->reason,0,32);
+ #$reason .= '...' if length($reason) < length($_->cust_credit->reason);
+ $reason = " ($reason) " if $reason;
+ push @b, {
+ #'description' => 'Credit ref\#'. $_->crednum.
+ # " (". time2str("%x",$_->cust_credit->_date) .")".
+ # $reason,
+ 'description' => 'Credit applied '.
+ time2str("%x",$_->cust_credit->_date). $reason,
+ 'amount' => sprintf("%.2f",$_->amount),
+ };
+ }
+ #foreach ( @cr_cust_credit ) {
+ # push @buf,[
+ # "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
+ # $money_char. sprintf("%10.2f",$_->credited)
+ # ];
+ #}
+
+ @b;
+
+}
+
+sub _items_payments {
+ my $self = shift;
+
+ my @b;
+ #get & print payments
+ foreach ( $self->cust_bill_pay ) {
+
+ #something more elaborate if $_->amount ne ->cust_pay->paid ?
+
+ push @b, {
+ 'description' => "Payment received ".
+ time2str("%x",$_->cust_pay->_date ),
+ 'amount' => sprintf("%.2f", $_->amount )
+ };
+ }
+
+ @b;
+
+}
+
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item process_reprint
+
+=cut
+
+sub process_reprint {
+ process_re_X('print', @_);
+}
+
+=item process_reemail
+
+=cut
+
+sub process_reemail {
+ process_re_X('email', @_);
+}
+
+=item process_refax
+
+=cut
+
+sub process_refax {
+ process_re_X('fax', @_);
+}
+
+=item process_reftp
+
+=cut
+
+sub process_reftp {
+ process_re_X('ftp', @_);
+}
+
+=item respool
+
+=cut
+
+sub process_respool {
+ process_re_X('spool', @_);
+}
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process_re_X {
+ my( $method, $job ) = ( shift, shift );
+ warn "$me process_re_X $method for job $job\n" if $DEBUG;
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ re_X(
+ $method,
+ $job,
+ %$param,
+ );
+
+}
+
+sub re_X {
+ my($method, $job, %param ) = @_;
+ if ( $DEBUG ) {
+ warn "re_X $method for job $job with param:\n".
+ join( '', map { " $_ => ". $param{$_}. "\n" } keys %param );
+ }
+
+ #some false laziness w/search/cust_bill.html
+ my $distinct = '';
+ my $orderby = 'ORDER BY cust_bill._date';
+
+ my $extra_sql = ' WHERE '. FS::cust_bill->search_sql(\%param);
+
+ my $addl_from = 'LEFT JOIN cust_main USING ( custnum )';
+
+ my @cust_bill = qsearch( {
+ #'select' => "cust_bill.*",
+ 'table' => 'cust_bill',
+ 'addl_from' => $addl_from,
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $orderby,
+ 'debug' => 1,
+ } );
+
+ $method .= '_invoice' unless $method eq 'email' || $method eq 'print';
+
+ warn " $me re_X $method: ". scalar(@cust_bill). " invoices found\n"
+ if $DEBUG;
+
+ my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
+ foreach my $cust_bill ( @cust_bill ) {
+ $cust_bill->$method();
+
+ if ( $job ) { #progressbar foo
+ $num++;
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $num / scalar(@cust_bill) )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ }
+
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item owed_sql
+
+Returns an SQL fragment to retreive the amount owed (charged minus credited and paid).
+
+=cut
+
+sub owed_sql {
+ my $class = shift;
+ 'charged - '. $class->paid_sql. ' - '. $class->credited_sql;
+}
+
+=item net_sql
+
+Returns an SQL fragment to retreive the net amount (charged minus credited).
+
+=cut
+
+sub net_sql {
+ my $class = shift;
+ 'charged - '. $class->credited_sql;
+}
+
+=item paid_sql
+
+Returns an SQL fragment to retreive the amount paid against this invoice.
+
+=cut
+
+sub paid_sql {
+ #my $class = shift;
+ "( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum )";
+}
+
+=item credited_sql
+
+Returns an SQL fragment to retreive the amount credited against this invoice.
+
+=cut
+
+sub credited_sql {
+ #my $class = shift;
+ "( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )";
+}
+
+=item search_sql HASHREF
+
+Class method which returns an SQL WHERE fragment to search for parameters
+specified in HASHREF. Valid parameters are
+
+=over 4
+
+=item begin
+
+Epoch date (UNIX timestamp) setting a lower bound for _date values
+
+=item end
+
+Epoch date (UNIX timestamp) setting an upper bound for _date values
+
+=item invnum_min
+
+=item invnum_max
+
+=item agentnum
+
+=item owed
+
+=item net
+
+=item days
+
+=item newest_percust
+
+=back
+
+Note: validates all passed-in data; i.e. safe to use with unchecked CGI params.
+
+=cut
+
+sub search_sql {
+ my($class, $param) = @_;
+ if ( $DEBUG ) {
+ warn "$me search_sql called with params: \n".
+ join("\n", map { " $_: ". $param->{$_} } keys %$param ). "\n";
+ }
+
+ my @search = ();
+
+ if ( $param->{'begin'} =~ /^(\d+)$/ ) {
+ push @search, "cust_bill._date >= $1";
+ }
+ if ( $param->{'end'} =~ /^(\d+)$/ ) {
+ push @search, "cust_bill._date < $1";
+ }
+ if ( $param->{'invnum_min'} =~ /^(\d+)$/ ) {
+ push @search, "cust_bill.invnum >= $1";
+ }
+ if ( $param->{'invnum_max'} =~ /^(\d+)$/ ) {
+ push @search, "cust_bill.invnum <= $1";
+ }
+ if ( $param->{'agentnum'} =~ /^(\d+)$/ ) {
+ push @search, "cust_main.agentnum = $1";
+ }
+
+ push @search, '0 != '. FS::cust_bill->owed_sql
+ if $param->{'open'};
+
+ push @search, '0 != '. FS::cust_bill->net_sql
+ if $param->{'net'};
+
+ push @search, "cust_bill._date < ". (time-86400*$param->{'days'})
+ if $param->{'days'};
+
+ if ( $param->{'newest_percust'} ) {
+
+ #$distinct = 'DISTINCT ON ( cust_bill.custnum )';
+ #$orderby = 'ORDER BY cust_bill.custnum ASC, cust_bill._date DESC';
+
+ my @newest_where = map { my $x = $_;
+ $x =~ s/\bcust_bill\./newest_cust_bill./g;
+ $x;
+ }
+ grep ! /^cust_main./, @search;
+ my $newest_where = scalar(@newest_where)
+ ? ' AND '. join(' AND ', @newest_where)
+ : '';
+
+
+ push @search, "cust_bill._date = (
+ SELECT(MAX(newest_cust_bill._date)) FROM cust_bill AS newest_cust_bill
+ WHERE newest_cust_bill.custnum = cust_bill.custnum
+ $newest_where
+ )";
+
+ }
+
+ my $curuser = $FS::CurrentUser::CurrentUser;
+ if ( $curuser->username eq 'fs_queue'
+ && $param->{'CurrentUser'} =~ /^(\w+)$/ ) {
+ my $username = $1;
+ my $newuser = qsearchs('access_user', {
+ 'username' => $username,
+ 'disabled' => '',
+ } );
+ if ( $newuser ) {
+ $curuser = $newuser;
+ } else {
+ warn "$me WARNING: (fs_queue) can't find CurrentUser $username\n";
+ }
+ }
+
+ push @search, $curuser->agentnums_sql;
+
+ join(' AND ', @search );
+
+}
+
+=back
+
+=head1 BUGS
+
+The delete method.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_main>, L<FS::cust_bill_pay>, L<FS::cust_pay>,
+L<FS::cust_bill_pkg>, L<FS::cust_bill_credit>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_ApplicationCommon.pm b/FS/FS/cust_bill_ApplicationCommon.pm
new file mode 100644
index 0000000..af7e087
--- /dev/null
+++ b/FS/FS/cust_bill_ApplicationCommon.pm
@@ -0,0 +1,404 @@
+package FS::cust_bill_ApplicationCommon;
+
+use strict;
+use vars qw( @ISA $DEBUG $me );
+use List::Util qw(min);
+use FS::Schema qw( dbdef );
+use FS::Record qw( qsearch qsearchs dbh );
+
+@ISA = qw( FS::Record );
+
+$DEBUG = 0;
+$me = '[FS::cust_bill_ApplicationCommon]';
+
+=head1 NAME
+
+FS::cust_bill_ApplicationCommon - Base class for bill application classes
+
+=head1 SYNOPSIS
+
+use FS::cust_bill_ApplicationCommon;
+
+@ISA = qw( FS::cust_bill_ApplicationCommon );
+
+sub _app_source_name { 'payment'; }
+sub _app_source_table { 'cust_pay'; }
+sub _app_lineitem_breakdown_table { 'cust_bill_pay_pkg'; }
+
+=head1 DESCRIPTION
+
+FS::cust_bill_ApplicationCommon is intended as a base class for classes which
+represent application of things to invoices, currently payments
+(see L<FS::cust_bill_pay>) or credits (see L<FS::cust_credit_bill>).
+
+=head1 METHODS
+
+=over 4
+
+=item insert
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::insert(@_)
+ || $self->apply_to_lineitems;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $app ( $self->lineitem_applications ) {
+ my $error = $app->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item apply_to_lineitems
+
+Auto-applies this invoice application to specific line items, if possible.
+
+=cut
+
+sub apply_to_lineitems {
+ my $self = shift;
+
+ my @apply = ();
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my @open = $self->cust_bill->open_cust_bill_pkg; #FOR UPDATE...?
+ warn "$me ". scalar(@open). " open line items for invoice ".
+ $self->cust_bill->invnum. ": ". join(', ', @open). "\n"
+ if $DEBUG;
+ my $total = 0;
+ $total += $_->setup + $_->recur foreach @open;
+ $total = sprintf('%.2f', $total);
+
+ if ( $self->amount > $total ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't apply a ". $self->_app_source_name. ' of $'. $self->amount.
+ " greater than the remaining owed on line items (\$$total)";
+ }
+
+ #easy cases:
+ # - one lineitem (a simple special case of:)
+ # - amount is for whole invoice (well, all of remaining lineitem links)
+ if ( $self->amount == $total ) {
+
+ warn "$me application amount covers remaining balance of invoice in full;".
+ "applying to those lineitems\n"
+ if $DEBUG;
+
+ #@apply = map { [ $_, $_->amount ]; } @open;
+ @apply = map { [ $_, $_->setup || $_->recur ]; } @open;
+
+ } else {
+
+ #slightly magic case:
+ # - amount exactly and uniquely matches a single open lineitem
+ # (you must be trying to pay or credit that item, then)
+
+ my @same = grep { $_->setup == $self->amount
+ || $_->recur == $self->amount
+ }
+ @open;
+ if ( scalar(@same) == 1 ) {
+ warn "$me application amount exactly and uniquely matches one lineitem;".
+ " applying to that lineitem\n"
+ if $DEBUG;
+ @apply = map { [ $_, $self->amount ]; } @same
+ }
+
+ }
+
+ unless ( @apply ) {
+
+ warn "$me applying amount based on package weights\n"
+ if $DEBUG;
+
+ #and the rest:
+ # - apply based on weights...
+
+ my $weight_col = $self->_app_part_pkg_weight_column;
+ my @openweight = map {
+ my $open = $_;
+ my $cust_pkg = $open->cust_pkg;
+ my $weight =
+ $cust_pkg
+ ? ( $cust_pkg->part_pkg->$weight_col() || 0 )
+ : 0; #default or per-tax weight?
+ [ $open, $weight ]
+ }
+ @open;
+
+ my %saw = ();
+ my @weights = sort { $b <=> $a } # highest weight first
+ grep { ! $saw{$_}++ } # want a list of unique weights
+ map { $_->[1] }
+ @openweight;
+
+ my $remaining_amount = $self->amount;
+ foreach my $weight ( @weights ) {
+
+ #i hate it when my schwartz gets tangled
+ my @items = map { $_->[0] } grep { $weight == $_->[1] } @openweight;
+
+ my $itemtotal = 0;
+ foreach my $item (@items) { $itemtotal += $item->setup || $item->recur; }
+ my $applytotal = min( $itemtotal, $remaining_amount );
+ $remaining_amount -= $applytotal;
+
+ warn "$me applying $applytotal ($remaining_amount remaining)".
+ " to ". scalar(@items). " lineitems with weight $weight\n"
+ if $DEBUG;
+
+ #if some items are less than applytotal/num_items, then apply then in full
+ my $lessflag;
+ do {
+ $lessflag = 0;
+
+ #no, not sprintf("%.2f",
+ # we want this rounded DOWN for purposes of checking for line items
+ # less than it, we don't want .66666 becoming .67 and causing this
+ # to trigger when it shouldn't
+ my $applyeach = int( 100 * $applytotal / scalar(@items) ) / 100;
+
+ my @newitems = ();
+ foreach my $item ( @items ) {
+ my $itemamount = $item->setup || $item->recur;
+ if ( $itemamount < $applyeach ) {
+ warn "$me applying full $itemamount".
+ " to small line item (cust_bill_pkg ". $item->billpkgnum. ")\n"
+ if $DEBUG;
+ push @apply, [ $item, $itemamount ];
+ $applytotal -= $itemamount;
+ $lessflag=1;
+ } else {
+ push @newitems, $item;
+ }
+ }
+ @items = @newitems;
+
+ } while ( $lessflag );
+
+ #and now that we've fallen out of the loop, distribute the rest equally...
+
+ # should cust_bill_pay_pkg and cust_credit_bill_pkg amount columns
+ # become real instead of numeric(10,2) ??? no..
+ my $applyeach = sprintf("%.2f", $applytotal / scalar(@items) );
+
+ my @equi_apply = map { [ $_, $applyeach ] } @items;
+
+ # or should we futz with pennies instead? yes, bah!
+ my $diff =
+ sprintf('%.0f', 100 * ( $applytotal - $applyeach * scalar(@items) ) );
+ $diff = 0 if $diff eq '-0'; #yay ieee fp
+ if ( abs($diff) > scalar(@items) ) {
+ #we must have done something really wrong, the difference is more than
+ #a penny an item
+ $dbh->rollback if $oldAutoCommit;
+ return 'Error distributing pennies applying '. $self->_app_source_name.
+ " - can't distribute difference of $diff pennies".
+ ' among '. scalar(@items). ' line items';
+ }
+
+ warn "$me futzing with $diff pennies difference\n"
+ if $DEBUG && $diff;
+
+ my $futz = 0;
+ while ( $diff != 0 && $futz < scalar(@equi_apply) ) {
+ if ( $diff > 0 ) {
+ $equi_apply[$futz++]->[1] += .01;
+ $diff -= 1;
+ } elsif ( $diff < 0 ) {
+ $equi_apply[$futz++]->[1] -= .01;
+ $diff += 1;
+ } else {
+ die "guru exception #5 (in fortran tongue the answer)";
+ }
+ }
+
+ if ( sprintf('%.0f', $diff ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "couldn't futz with pennies enough: still $diff left";
+ }
+
+ if ( $DEBUG ) {
+ warn "$me applying ". $_->[1].
+ " to line item (cust_bill_pkg ". $_->[0]->billpkgnum. ")\n"
+ foreach @equi_apply;
+ }
+
+
+ push @apply, @equi_apply;
+
+ #$remaining_amount -= $applytotal;
+ last unless $remaining_amount;
+
+ }
+
+ }
+
+ # do the applicaiton(s)
+ my $table = $self->lineitem_breakdown_table;
+ my $source_key = dbdef->table($self->table)->primary_key;
+ my $applied = 0;
+ foreach my $apply ( @apply ) {
+ my ( $cust_bill_pkg, $amount ) = @$apply;
+ $applied += $amount;
+ my $application = "FS::$table"->new( {
+ $source_key => $self->$source_key(),
+ 'billpkgnum' => $cust_bill_pkg->billpkgnum,
+ 'amount' => sprintf('%.2f', $amount),
+ 'setuprecur' => ( $cust_bill_pkg->setup > 0 ? 'setup' : 'recur' ),
+ 'sdate' => $cust_bill_pkg->sdate,
+ 'edate' => $cust_bill_pkg->edate,
+ });
+ my $error = $application->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ #everything should always be applied to line items in full now... sanity check
+ $applied = sprintf('%.2f', $applied);
+ unless ( $applied == $self->amount ) {
+ $dbh->rollback if $oldAutoCommit;
+ return 'Error applying '. $self->_app_source_name. ' of $'. $self->amount.
+ ' to line items - only $'. $applied. ' was applied.';
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item lineitem_applications
+
+Returns all the specific line item applications for this invoice application.
+
+=cut
+
+sub lineitem_applications {
+ my $self = shift;
+ my $primary_key = dbdef->table($self->table)->primary_key;
+ qsearch({
+ 'table' => $self->lineitem_breakdown_table,
+ 'hashref' => { $primary_key => $self->$primary_key() },
+ });
+
+}
+
+=item cust_bill
+
+Returns the invoice (see L<FS::cust_bill>)
+
+=cut
+
+sub cust_bill {
+ my $self = shift;
+ qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
+}
+
+=item applied_to_invoice
+
+Returns a string representing the invoice (see L<FS::cust_bill>), for example:
+"applied to Invoice #54 (3/20/2008)"
+
+=cut
+
+sub applied_to_invoice {
+ my $self = shift;
+ 'applied to '. $self->cust_bill->invnum_date_pretty;
+}
+
+=item lineitem_breakdown_table
+
+=cut
+
+sub lineitem_breakdown_table {
+ my $self = shift;
+ $self->_load_table($self->_app_lineitem_breakdown_table);
+}
+
+sub _load_table {
+ my( $self, $table ) = @_;
+ eval "use FS::$table";
+ die $@ if $@;
+ $table;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_bill_pay> and L<FS::cust_bill_pay_pkg>,
+L<FS::cust_credit_bill> and L<FS::cust_credit_bill_pkg>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_event.pm b/FS/FS/cust_bill_event.pm
new file mode 100644
index 0000000..7c2ad37
--- /dev/null
+++ b/FS/FS/cust_bill_event.pm
@@ -0,0 +1,380 @@
+package FS::cust_bill_event;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_main_Mixin;
+use FS::cust_bill;
+use FS::part_bill_event;
+
+@ISA = qw(FS::cust_main_Mixin FS::Record);
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::cust_bill_event - Object methods for cust_bill_event records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_event;
+
+ $record = new FS::cust_bill_event \%hash;
+ $record = new FS::cust_bill_event { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_event object represents an complete invoice event.
+FS::cust_bill_event inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item eventnum
+
+Primary key
+
+=item invnum
+
+Invoice (see L<FS::cust_bill>)
+
+=item eventpart
+
+Event definition (see L<FS::part_bill_event>)
+
+=item _date
+
+Specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item status
+
+Event status: B<done> or B<failed>
+
+=item statustext
+
+Additional status detail (i.e. error message)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new completed invoice event. To add the compelted invoice event to
+the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_bill_event'; }
+
+sub cust_linked { $_[0]->cust_main_custnum; }
+sub cust_unlinked_msg {
+ my $self = shift;
+ "WARNING: can't find cust_main.custnum ". $self->custnum.
+ ' (cust_bill.invnum '. $self->invnum. ')';
+}
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid completed invoice event. If
+there is an error, returns the error, otherwise returns false. Called by the
+insert and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error = $self->ut_numbern('eventnum')
+ || $self->ut_number('invnum')
+ || $self->ut_number('eventpart')
+ || $self->ut_number('_date')
+ || $self->ut_enum('status', [qw( done failed )])
+ || $self->ut_anything('statustext')
+ ;
+
+ return "Unknown eventpart ". $self->eventpart
+ unless my $part_bill_event =
+ qsearchs( 'part_bill_event' ,{ 'eventpart' => $self->eventpart } );
+
+ return "Unknown invnum ". $self->invnum
+ unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
+
+ $self->SUPER::check;
+}
+
+=item part_bill_event
+
+Returns the invoice event definition (see L<FS::part_bill_event>) for this
+completed invoice event.
+
+=cut
+
+sub part_bill_event {
+ my $self = shift;
+ qsearchs( 'part_bill_event', { 'eventpart' => $self->eventpart } );
+}
+
+=item cust_bill
+
+Returns the invoice (see L<FS::cust_bill>) for this completed invoice event.
+
+=cut
+
+sub cust_bill {
+ my $self = shift;
+ qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
+}
+
+=item retry
+
+Changes the status of this event from B<done> to B<failed>, allowing it to be
+retried.
+
+=cut
+
+sub retry {
+ my $self = shift;
+ return '' unless $self->status eq 'done';
+ my $old = ref($self)->new( { $self->hash } );
+ $self->status('failed');
+ $self->replace($old);
+}
+
+=item retryable
+
+Changes the statustext of this event to B<retriable>, rendering it
+retriable (should retry be called).
+
+=cut
+
+sub retriable {
+ my $self = shift;
+ return '' unless $self->status eq 'done';
+ my $old = ref($self)->new( { $self->hash } );
+ $self->statustext('retriable');
+ $self->replace($old);
+}
+
+=item search_sql HASHREF
+
+Class method which returns an SQL WHERE fragment to search for parameters
+specified in HASHREF. Valid parameters are
+
+=over 4
+
+=item agentnum
+
+=item beginning
+
+An epoch date setting a lower bound for _date values
+
+=item ending
+
+An epoch date setting a upper bound for _date values
+
+=item failed
+
+Limits the search to failed events if true
+
+=item payby
+
+Requires that the search be JOIN'd to part_bill_event # Bug?
+
+=item invnum
+
+=item currentuser
+
+Specifies the user for agent virtualization
+
+=back
+
+=cut
+
+sub search_sql {
+ my ($class, $params) = @_;
+ my @search = ();
+
+ push @search, "agentnum = ". $params->{agentnum} if $params->{agentnum};
+
+ push @search, "cust_bill_event._date >= ". $params->{beginning}
+ if $params->{beginning};
+ push @search, "cust_bill_event._date <= ". $params->{ending}
+ if $params->{ending};
+
+ push @search, "statustext != ''",
+ "statustext IS NOT NULL",
+ "statustext != 'N/A'"
+ if $params->{failed};
+
+ push @search, "part_bill_event.payby = '". $params->{payby}. "'"
+ if $params->{payby};
+
+ push @search, "cust_bill_event.invnum = '". $params->{invnum}. "'"
+ if $params->{invnum};
+
+ my $currentuser = $params->{currentuser} || $params->{CurrentUser};
+ if ($currentuser) {
+ my $access_user = qsearchs('access_user', { username => $currentuser });
+ if ($access_user) {
+ push @search, $access_user->agentnums_sql;
+ }else{
+ push @search, "1=0";
+ }
+ }else{
+ push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+ }
+
+ join(' AND ', @search );
+
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item reprint
+
+=cut
+
+sub process_reprint {
+ process_re_X('print', @_);
+}
+
+=item reemail
+
+=cut
+
+sub process_reemail {
+ process_re_X('email', @_);
+}
+
+=item refax
+
+=cut
+
+sub process_refax {
+ process_re_X('fax', @_);
+}
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process_re_X {
+ my( $method, $job ) = ( shift, shift );
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ re_X(
+ $method,
+ $param,
+ $job,
+ );
+
+}
+
+sub re_X {
+ my($method, $param, $job) = @_;
+
+ my $where = FS::cust_bill_event->search_sql($param);
+ $where = " WHERE plan LIKE 'send%'". ( $where ? " AND $where" : "" );
+
+ my $from = 'LEFT JOIN part_bill_event USING ( eventpart )'.
+ 'LEFT JOIN cust_bill USING ( invnum )'.
+ 'LEFT JOIN cust_main USING ( custnum )';
+
+ my @cust_bill_event = qsearch( 'cust_bill_event', {}, '', $where, '', $from );
+
+ my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
+ foreach my $cust_bill_event ( @cust_bill_event ) {
+
+ $cust_bill_event->cust_bill->$method(
+ $cust_bill_event->part_bill_event->templatename
+ );
+
+ if ( $job ) { #progressbar foo
+ $num++;
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $num / scalar(@cust_bill_event) )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ }
+
+ #this doesn't work, but it would be nice
+ #if ( $job ) { #progressbar foo
+ # my $error = $job->update_statustext(
+ # scalar(@cust_bill_event). " invoices re-${method}ed"
+ # );
+ # die $error if $error;
+ #}
+
+}
+
+=back
+
+=head1 BUGS
+
+Far too early in the morning.
+
+=head1 SEE ALSO
+
+L<FS::part_bill_event>, L<FS::cust_bill>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pay.pm b/FS/FS/cust_bill_pay.pm
new file mode 100644
index 0000000..b7ba2b7
--- /dev/null
+++ b/FS/FS/cust_bill_pay.pm
@@ -0,0 +1,165 @@
+package FS::cust_bill_pay;
+
+use strict;
+use vars qw( @ISA $conf );
+use FS::Record qw( qsearchs );
+use FS::cust_main_Mixin;
+use FS::cust_bill_ApplicationCommon;
+use FS::cust_bill;
+use FS::cust_pay;
+
+@ISA = qw( FS::cust_main_Mixin FS::cust_bill_ApplicationCommon );
+
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+} );
+
+=head1 NAME
+
+FS::cust_bill_pay - Object methods for cust_bill_pay records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pay;
+
+ $record = new FS::cust_bill_pay \%hash;
+ $record = new FS::cust_bill_pay { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pay object represents the application of a payment to a
+specific invoice. FS::cust_bill_pay inherits from
+FS::cust_bill_ApplicationCommon and FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item billpaynum - primary key (assigned automatically)
+
+=item invnum - Invoice (see L<FS::cust_bill>)
+
+=item paynum - Payment (see L<FS::cust_pay>)
+
+=item amount - Amount of the payment to apply to the specific invoice.
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+=cut
+
+sub table { 'cust_bill_pay'; }
+
+sub _app_source_name { 'payment'; }
+sub _app_source_table { 'cust_pay'; }
+sub _app_lineitem_breakdown_table { 'cust_bill_pay_pkg'; }
+sub _app_part_pkg_weight_column { 'pay_weight'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this payment application, unless the closed flag for the parent payment
+(see L<FS::cust_pay>) is set.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return "Can't delete application for closed payment"
+ if $self->cust_pay->closed =~ /^Y/i;
+ return "Can't delete application for closed invoice"
+ if $self->cust_bill->closed =~ /^Y/i;
+ $self->SUPER::delete(@_);
+}
+
+=item replace OLD_RECORD
+
+Currently unimplemented (accounting reasons).
+
+=cut
+
+sub replace {
+ return "Can't modify application of payment!";
+}
+
+=item check
+
+Checks all fields to make sure this is a valid payment application. If there
+is an error, returns the error, otherwise returns false. Called by the insert
+method.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('billpaynum')
+ || $self->ut_foreign_key('paynum', 'cust_pay', 'paynum' )
+ || $self->ut_foreign_key('invnum', 'cust_bill', 'invnum' )
+ || $self->ut_numbern('_date')
+ || $self->ut_money('amount')
+ ;
+ return $error if $error;
+
+ return "amount must be > 0" if $self->amount <= 0;
+
+ $self->_date(time) unless $self->_date;
+
+ return "Cannot apply more than remaining value of invoice"
+ unless $self->amount <= $self->cust_bill->owed;
+
+ return "Cannot apply more than remaining value of payment"
+ unless $self->amount <= $self->cust_pay->unapplied;
+
+ $self->SUPER::check;
+}
+
+=item cust_pay
+
+Returns the payment (see L<FS::cust_pay>)
+
+=cut
+
+sub cust_pay {
+ my $self = shift;
+ qsearchs( 'cust_pay', { 'paynum' => $self->paynum } );
+}
+
+=back
+
+=head1 BUGS
+
+Delete and replace methods.
+
+=head1 SEE ALSO
+
+L<FS::cust_pay>, L<FS::cust_bill>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pay_batch.pm b/FS/FS/cust_bill_pay_batch.pm
new file mode 100644
index 0000000..30fb744
--- /dev/null
+++ b/FS/FS/cust_bill_pay_batch.pm
@@ -0,0 +1,120 @@
+package FS::cust_bill_pay_batch;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_bill_pay_batch - Object methods for cust_bill_pay_batch records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pay_batch;
+
+ $record = new FS::cust_bill_pay_batch \%hash;
+ $record = new FS::cust_bill_pay_batch { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pay_batch object represents a relationship between a
+customer's bill and a batch. FS::cust_bill_pay_batch inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item billpaynum - primary key
+
+=item invnum - customer's bill (invoice)
+
+=item paybatchnum - entry in cust_pay_batch table
+
+=item amount -
+
+=item _date -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_bill_pay_batch'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('billpaynum')
+ || $self->ut_number('invnum')
+ || $self->ut_number('paybatchnum')
+ || $self->ut_money('amount')
+ || $self->ut_numbern('_date')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+Just hangs there.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pay_pkg.pm b/FS/FS/cust_bill_pay_pkg.pm
new file mode 100644
index 0000000..cdbace9
--- /dev/null
+++ b/FS/FS/cust_bill_pay_pkg.pm
@@ -0,0 +1,141 @@
+package FS::cust_bill_pay_pkg;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_bill_pay_pkg - Object methods for cust_bill_pay_pkg records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pay_pkg;
+
+ $record = new FS::cust_bill_pay_pkg \%hash;
+ $record = new FS::cust_bill_pay_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pay_pkg object represents application of a payment (see
+L<FS::cust_bill_pay>) to a specific line item within an invoice (see
+L<FS::cust_bill_pkg>). FS::cust_bill_pay_pkg inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item billpaypkgnum - primary key
+
+=item billpaynum - Payment application to the overall invoice (see L<FS::cust_bill_pay>)
+
+=item billpkgnum - Line item to which payment is applied (see L<FS::cust_bill_pkg>)
+
+=item amount - Amount of the payment applied to this line item.
+
+=item setuprecur - 'setup' or 'recur', designates whether the payment was applied to the setup or recurring portion of the line item.
+
+=item sdate - starting date of recurring fee
+
+=item edate - ending date of recurring fee
+
+=back
+
+sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">. Also
+see L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_bill_pay_pkg'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid payment application. If there
+is an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('billpaypkgnum')
+ || $self->ut_foreign_key('billpaynum', 'cust_bill_pay', 'billpaynum' )
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum' )
+ || $self->ut_money('amount')
+ || $self->ut_enum('setuprecur', [ 'setup', 'recur' ] )
+ || $self->ut_numbern('sdate')
+ || $self->ut_numbern('edate')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+B<setuprecur> field is a kludge to compensate for cust_bill_pkg having separate
+setup and recur fields. It should be removed once that's fixed.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
new file mode 100644
index 0000000..6c0589a
--- /dev/null
+++ b/FS/FS/cust_bill_pkg.pm
@@ -0,0 +1,676 @@
+package FS::cust_bill_pkg;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Record qw( qsearch qsearchs dbdef dbh );
+use FS::cust_main_Mixin;
+use FS::cust_pkg;
+use FS::part_pkg;
+use FS::cust_bill;
+use FS::cust_bill_pkg_detail;
+use FS::cust_bill_pkg_display;
+use FS::cust_bill_pay_pkg;
+use FS::cust_credit_bill_pkg;
+use FS::cust_tax_exempt_pkg;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::cust_bill_pkg - Object methods for cust_bill_pkg records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pkg;
+
+ $record = new FS::cust_bill_pkg \%hash;
+ $record = new FS::cust_bill_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pkg object represents an invoice line item.
+FS::cust_bill_pkg inherits from FS::Record. The following fields are currently
+supported:
+
+=over 4
+
+=item billpkgnum - primary key
+
+=item invnum - invoice (see L<FS::cust_bill>)
+
+=item pkgnum - package (see L<FS::cust_pkg>) or 0 for the special virtual sales tax package, or -1 for the virtual line item (itemdesc is used for the line)
+
+=item pkgpart_override - optional package definition (see L<FS::part_pkg>) override
+=item setup - setup fee
+
+=item recur - recurring fee
+
+=item sdate - starting date of recurring fee
+
+=item edate - ending date of recurring fee
+
+=item itemdesc - Line item description (overrides normal package description)
+
+=item quantity - If not set, defaults to 1
+
+=item unitsetup - If not set, defaults to setup
+
+=item unitrecur - If not set, defaults to recur
+
+=back
+
+sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">. Also
+see L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new line item. To add the line item to the database, see
+L<"insert">. Line items are normally created by calling the bill method of a
+customer object (see L<FS::cust_main>).
+
+=cut
+
+sub table { 'cust_bill_pkg'; }
+
+=item insert
+
+Adds this line item to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( defined dbdef->table('cust_bill_pkg_detail') && $self->get('details') ) {
+ foreach my $detail ( @{$self->get('details')} ) {
+ my $cust_bill_pkg_detail = new FS::cust_bill_pkg_detail {
+ 'billpkgnum' => $self->billpkgnum,
+ 'format' => (ref($detail) ? $detail->[0] : '' ),
+ 'detail' => (ref($detail) ? $detail->[1] : $detail ),
+ 'amount' => (ref($detail) ? $detail->[2] : '' ),
+ 'classnum' => (ref($detail) ? $detail->[3] : '' ),
+ };
+ $error = $cust_bill_pkg_detail->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ if ( defined dbdef->table('cust_bill_pkg_display') && $self->get('display') ){
+ foreach my $cust_bill_pkg_display ( @{ $self->get('display') } ) {
+ $cust_bill_pkg_display->billpkgnum($self->billpkgnum);
+ $error = $cust_bill_pkg_display->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ if ( $self->_cust_tax_exempt_pkg ) {
+ foreach my $cust_tax_exempt_pkg ( @{$self->_cust_tax_exempt_pkg} ) {
+ $cust_tax_exempt_pkg->billpkgnum($self->billpkgnum);
+ $error = $cust_tax_exempt_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ my $tax_location = $self->get('cust_bill_pkg_tax_location');
+ if ( $tax_location ) {
+ foreach my $cust_bill_pkg_tax_location ( @$tax_location ) {
+ $cust_bill_pkg_tax_location->billpkgnum($self->billpkgnum);
+ warn $cust_bill_pkg_tax_location;
+ $error = $cust_bill_pkg_tax_location->insert;
+ warn $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item delete
+
+Currently unimplemented. I don't remove line items because there would then be
+no record the items ever existed (which is bad, no?)
+
+=cut
+
+sub delete {
+ return "Can't delete cust_bill_pkg records!";
+}
+
+#alas, bin/follow-tax-rename
+#
+#=item replace OLD_RECORD
+#
+#Currently unimplemented. This would be even more of an accounting nightmare
+#than deleteing the items. Just don't do it.
+#
+#=cut
+#
+#sub replace {
+# return "Can't modify cust_bill_pkg records!";
+#}
+
+=item check
+
+Checks all fields to make sure this is a valid line item. If there is an
+error, returns the error, otherwise returns false. Called by the insert
+method.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('billpkgnum')
+ || $self->ut_snumber('pkgnum')
+ || $self->ut_number('invnum')
+ || $self->ut_money('setup')
+ || $self->ut_money('recur')
+ || $self->ut_numbern('sdate')
+ || $self->ut_numbern('edate')
+ || $self->ut_textn('itemdesc')
+ ;
+ return $error if $error;
+
+ #if ( $self->pkgnum != 0 ) { #allow unchecked pkgnum 0 for tax! (add to part_pkg?)
+ if ( $self->pkgnum > 0 ) { #allow -1 for non-pkg line items and 0 for tax (add to part_pkg?)
+ return "Unknown pkgnum ". $self->pkgnum
+ unless qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
+ }
+
+ return "Unknown invnum"
+ unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
+
+ $self->SUPER::check;
+}
+
+=item cust_pkg
+
+Returns the package (see L<FS::cust_pkg>) for this invoice line item.
+
+=cut
+
+sub cust_pkg {
+ my $self = shift;
+ qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
+}
+
+=item part_pkg
+
+Returns the package definition for this invoice line item.
+
+=cut
+
+sub part_pkg {
+ my $self = shift;
+ if ( $self->pkgpart_override ) {
+ qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart_override } );
+ } else {
+ $self->cust_pkg->part_pkg;
+ }
+}
+
+=item cust_bill
+
+Returns the invoice (see L<FS::cust_bill>) for this invoice line item.
+
+=cut
+
+sub cust_bill {
+ my $self = shift;
+ qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
+}
+
+=item details [ OPTION => VALUE ... ]
+
+Returns an array of detail information for the invoice line item.
+
+Currently available options are: I<format> I<escape_function>
+
+If I<format> is set to html or latex then the array members are improved
+for tabular appearance in those environments if possible.
+
+If I<escape_function> is set then the array members are processed by this
+function before being returned.
+
+=cut
+
+sub details {
+ my ( $self, %opt ) = @_;
+ my $format = $opt{format} || '';
+ my $escape_function = $opt{escape_function} || sub { shift };
+ return () unless defined dbdef->table('cust_bill_pkg_detail');
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+ my $csv = new Text::CSV_XS;
+
+ my $format_sub = sub { my $detail = shift;
+ $csv->parse($detail) or return "can't parse $detail";
+ join(' - ', map { &$escape_function($_) }
+ $csv->fields
+ );
+ };
+
+ $format_sub = sub { my $detail = shift;
+ $csv->parse($detail) or return "can't parse $detail";
+ join('</TD><TD>', map { &$escape_function($_) }
+ $csv->fields
+ );
+ }
+ if $format eq 'html';
+
+ $format_sub = sub { my $detail = shift;
+ $csv->parse($detail) or return "can't parse $detail";
+ #join(' & ', map { '\small{'. &$escape_function($_). '}' }
+ # $csv->fields );
+ my $result = '';
+ my $column = 1;
+ foreach ($csv->fields) {
+ $result .= ' & ' if $column > 1;
+ if ($column > 6) { # KLUDGE ALERT!
+ $result .= '\multicolumn{1}{l}{\scriptsize{'.
+ &$escape_function($_). '}}';
+ }else{
+ $result .= '\scriptsize{'. &$escape_function($_). '}';
+ }
+ $column++;
+ }
+ $result;
+ }
+ if $format eq 'latex';
+
+ $format_sub = $opt{format_function} if $opt{format_function};
+
+ map { ( $_->format eq 'C'
+ ? &{$format_sub}( $_->detail )
+ : &{$escape_function}( $_->detail )
+ )
+ }
+ qsearch ({ 'table' => 'cust_bill_pkg_detail',
+ 'hashref' => { 'billpkgnum' => $self->billpkgnum },
+ 'order_by' => 'ORDER BY detailnum',
+ });
+ #qsearch ( 'cust_bill_pkg_detail', { 'lineitemnum' => $self->lineitemnum });
+}
+
+=item desc
+
+Returns a description for this line item. For typical line items, this is the
+I<pkg> field of the corresponding B<FS::part_pkg> object (see L<FS::part_pkg>).
+For one-shot line items and named taxes, it is the I<itemdesc> field of this
+line item, and for generic taxes, simply returns "Tax".
+
+=cut
+
+sub desc {
+ my $self = shift;
+
+ if ( $self->pkgnum > 0 ) {
+ $self->itemdesc || $self->part_pkg->pkg;
+ } else {
+ $self->itemdesc || 'Tax';
+ }
+}
+
+=item owed_setup
+
+Returns the amount owed (still outstanding) on this line item's setup fee,
+which is the amount of the line item minus all payment applications (see
+L<FS::cust_bill_pay_pkg> and credit applications (see
+L<FS::cust_credit_bill_pkg>).
+
+=cut
+
+sub owed_setup {
+ my $self = shift;
+ $self->owed('setup', @_);
+}
+
+=item owed_recur
+
+Returns the amount owed (still outstanding) on this line item's recurring fee,
+which is the amount of the line item minus all payment applications (see
+L<FS::cust_bill_pay_pkg> and credit applications (see
+L<FS::cust_credit_bill_pkg>).
+
+=cut
+
+sub owed_recur {
+ my $self = shift;
+ $self->owed('recur', @_);
+}
+
+# modeled after cust_bill::owed...
+sub owed {
+ my( $self, $field ) = @_;
+ my $balance = $self->$field();
+ $balance -= $_->amount foreach ( $self->cust_bill_pay_pkg($field) );
+ $balance -= $_->amount foreach ( $self->cust_credit_bill_pkg($field) );
+ $balance = sprintf( '%.2f', $balance );
+ $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
+ $balance;
+}
+
+sub cust_bill_pay_pkg {
+ my( $self, $field ) = @_;
+ qsearch( 'cust_bill_pay_pkg', { 'billpkgnum' => $self->billpkgnum,
+ 'setuprecur' => $field,
+ }
+ );
+}
+
+sub cust_credit_bill_pkg {
+ my( $self, $field ) = @_;
+ qsearch( 'cust_credit_bill_pkg', { 'billpkgnum' => $self->billpkgnum,
+ 'setuprecur' => $field,
+ }
+ );
+}
+
+=item units
+
+Returns the number of billing units (for tax purposes) represented by this,
+line item.
+
+=cut
+
+sub units {
+ my $self = shift;
+ $self->pkgnum ? $self->part_pkg->calc_units($self->cust_pkg) : 0; # 1?
+}
+
+=item quantity
+
+=cut
+
+sub quantity {
+ my( $self, $value ) = @_;
+ if ( defined($value) ) {
+ $self->setfield('quantity', $value);
+ }
+ $self->getfield('quantity') || 1;
+}
+
+=item unitsetup
+
+=cut
+
+sub unitsetup {
+ my( $self, $value ) = @_;
+ if ( defined($value) ) {
+ $self->setfield('unitsetup', $value);
+ }
+ $self->getfield('unitsetup') eq ''
+ ? $self->getfield('setup')
+ : $self->getfield('unitsetup');
+}
+
+=item unitrecur
+
+=cut
+
+sub unitrecur {
+ my( $self, $value ) = @_;
+ if ( defined($value) ) {
+ $self->setfield('unitrecur', $value);
+ }
+ $self->getfield('unitrecur') eq ''
+ ? $self->getfield('recur')
+ : $self->getfield('unitrecur');
+}
+
+=item disintegrate
+
+Returns a list of cust_bill_pkg objects each with no more than a single class
+(including setup or recur) of charge.
+
+=cut
+
+sub disintegrate {
+ my $self = shift;
+ # XXX this goes away with cust_bill_pkg refactor
+
+ my $cust_bill_pkg = new FS::cust_bill_pkg { $self->hash };
+ my %cust_bill_pkg = ();
+
+ $cust_bill_pkg{setup} = $cust_bill_pkg if $cust_bill_pkg->setup;
+ $cust_bill_pkg{recur} = $cust_bill_pkg if $cust_bill_pkg->recur;
+
+
+ #split setup and recur
+ if ($cust_bill_pkg->setup && $cust_bill_pkg->recur) {
+ my $cust_bill_pkg_recur = new FS::cust_bill_pkg { $cust_bill_pkg->hash };
+ $cust_bill_pkg->set('details', []);
+ $cust_bill_pkg->recur(0);
+ $cust_bill_pkg->unitrecur(0);
+ $cust_bill_pkg->type('');
+ $cust_bill_pkg_recur->setup(0);
+ $cust_bill_pkg_recur->unitsetup(0);
+ $cust_bill_pkg{recur} = $cust_bill_pkg_recur;
+
+ }
+
+ #split usage from recur
+ my $usage = sprintf( "%.2f", $cust_bill_pkg{recur}->usage );
+ warn "usage is $usage\n" if $DEBUG;
+ if ($usage) {
+ my $cust_bill_pkg_usage =
+ new FS::cust_bill_pkg { $cust_bill_pkg{recur}->hash };
+ $cust_bill_pkg_usage->recur( $usage );
+ $cust_bill_pkg_usage->type( 'U' );
+ my $recur = sprintf( "%.2f", $cust_bill_pkg{recur}->recur - $usage );
+ $cust_bill_pkg{recur}->recur( $recur );
+ $cust_bill_pkg{recur}->type( '' );
+ $cust_bill_pkg{recur}->set('details', []);
+ $cust_bill_pkg{''} = $cust_bill_pkg_usage;
+ }
+
+ #subdivide usage by usage_class
+ if (exists($cust_bill_pkg{''})) {
+ foreach my $class (grep { $_ } $self->usage_classes) {
+ my $usage = sprintf( "%.2f", $cust_bill_pkg{''}->usage($class) );
+ my $cust_bill_pkg_usage =
+ new FS::cust_bill_pkg { $cust_bill_pkg{''}->hash };
+ $cust_bill_pkg_usage->recur( $usage );
+ $cust_bill_pkg_usage->set('details', []);
+ my $classless = sprintf( "%.2f", $cust_bill_pkg{''}->recur - $usage );
+ $cust_bill_pkg{''}->recur( $classless );
+ $cust_bill_pkg{$class} = $cust_bill_pkg_usage;
+ }
+ delete $cust_bill_pkg{''} unless $cust_bill_pkg{''}->recur;
+ }
+
+# # sort setup,recur,'', and the rest numeric && return
+# my @result = map { $cust_bill_pkg{$_} }
+# sort { my $ad = ($a=~/^\d+$/); my $bd = ($b=~/^\d+$/);
+# ( $ad cmp $bd ) || ( $ad ? $a<=>$b : $b cmp $a )
+# }
+# keys %cust_bill_pkg;
+#
+# return (@result);
+
+ %cust_bill_pkg;
+}
+
+=item usage CLASSNUM
+
+Returns the amount of the charge associated with usage class CLASSNUM if
+CLASSNUM is defined. Otherwise returns the total charge associated with
+usage.
+
+=cut
+
+sub usage {
+ my( $self, $classnum ) = @_;
+ my $sum = 0;
+ my @values = ();
+
+ if ( $self->get('details') ) {
+
+ @values =
+ map { $_->[2] }
+ grep { ref($_) && ( defined($classnum) ? $_->[3] eq $classnum : 1 ) }
+ @{ $self->get('details') };
+
+ }else{
+
+ my $hashref = { 'billpkgnum' => $self->billpkgnum };
+ $hashref->{ 'classnum' } = $classnum if defined($classnum);
+ @values = map { $_->amount } qsearch('cust_bill_pkg_detail', $hashref);
+
+ }
+
+ foreach ( @values ) {
+ $sum += $_ if $_;
+ }
+ $sum;
+}
+
+=item usage_classes
+
+Returns a list of usage classnums associated with this invoice line's
+details.
+
+=cut
+
+sub usage_classes {
+ my( $self ) = @_;
+
+ if ( $self->get('details') ) {
+
+ my %seen = ();
+ foreach my $detail ( grep { ref($_) } @{$self->get('details')} ) {
+ $seen{ $detail->[3] } = 1;
+ }
+ keys %seen;
+
+ }else{
+
+ map { $_->classnum }
+ qsearch({ table => 'cust_bill_pkg_detail',
+ hashref => { billpkgnum => $self->billpkgnum },
+ select => 'DISTINCT classnum',
+ });
+
+ }
+
+}
+
+=item cust_bill_pkg_display [ type => TYPE ]
+
+Returns an array of display information for the invoice line item optionally
+limited to 'TYPE'.
+
+=cut
+
+sub cust_bill_pkg_display {
+ my ( $self, %opt ) = @_;
+
+ my $default =
+ new FS::cust_bill_pkg_display { billpkgnum =>$self->billpkgnum };
+
+ return ( $default ) unless defined dbdef->table('cust_bill_pkg_display');#hmmm
+
+ my $type = $opt{type} if exists $opt{type};
+ my @result;
+
+ if ( scalar( $self->get('display') ) ) {
+ @result = grep { defined($type) ? ($type eq $_->type) : 1 }
+ @{ $self->get('display') };
+ }else{
+ my $hashref = { 'billpkgnum' => $self->billpkgnum };
+ $hashref->{type} = $type if defined($type);
+
+ @result = qsearch ({ 'table' => 'cust_bill_pkg_display',
+ 'hashref' => { 'billpkgnum' => $self->billpkgnum },
+ 'order_by' => 'ORDER BY billpkgdisplaynum',
+ });
+ }
+
+ push @result, $default unless ( scalar(@result) || $type );
+
+ @result;
+
+}
+
+# reserving this name for my friends FS::{tax_rate|cust_main_county}::taxline
+# and FS::cust_main::bill
+
+sub _cust_tax_exempt_pkg {
+ my ( $self ) = @_;
+
+ $self->{Hash}->{_cust_tax_exempt_pkg} or
+ $self->{Hash}->{_cust_tax_exempt_pkg} = [];
+
+}
+
+
+=back
+
+=head1 BUGS
+
+setup and recur shouldn't be separate fields. There should be one "amount"
+field and a flag to tell you if it is a setup/one-time fee or a recurring fee.
+
+A line item with both should really be two separate records (preserving
+sdate and edate for setup fees for recurring packages - that information may
+be valuable later). Invoice generation (cust_main::bill), invoice printing
+(cust_bill), tax reports (report_tax.cgi) and line item reports
+(cust_bill_pkg.cgi) would need to be updated.
+
+owed_setup and owed_recur could then be repaced by just owed, and
+cust_bill::open_cust_bill_pkg and
+cust_bill_ApplicationCommon::apply_to_lineitems could be simplified.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_bill>, L<FS::cust_pkg>, L<FS::cust_main>, schema.html
+from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pkg_detail.pm b/FS/FS/cust_bill_pkg_detail.pm
new file mode 100644
index 0000000..8a48888
--- /dev/null
+++ b/FS/FS/cust_bill_pkg_detail.pm
@@ -0,0 +1,184 @@
+package FS::cust_bill_pkg_detail;
+
+use strict;
+use vars qw( @ISA $me $DEBUG );
+use FS::Record qw( qsearch qsearchs dbdef );
+use FS::cust_bill_pkg;
+
+@ISA = qw(FS::Record);
+$me = '[ FS::cust_bill_pkg_detail ]';
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::cust_bill_pkg_detail - Object methods for cust_bill_pkg_detail records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pkg_detail;
+
+ $record = new FS::cust_bill_pkg_detail \%hash;
+ $record = new FS::cust_bill_pkg_detail { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pkg_detail object represents additional detail information for
+an invoice line item (see L<FS::cust_bill_pkg>). FS::cust_bill_pkg_detail
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item detailnum - primary key
+
+=item billpkgnum - link to cust_bill_pkg
+
+=item detail - detail description
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new line item detail. To add the line item detail to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_bill_pkg_detail'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid line item detail. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('detailnum')
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum')
+ || $self->ut_enum('format', [ '', 'C' ] )
+ || $self->ut_text('detail')
+ || $self->SUPER::check
+ ;
+
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+sub _upgrade_data { # class method
+
+ my ($class, %opts) = @_;
+
+ warn "$me upgrading $class\n" if $DEBUG;
+
+ if ( defined( dbdef->table($class->table)->column('billpkgnum') ) &&
+ defined( dbdef->table($class->table)->column('invnum') ) &&
+ defined( dbdef->table($class->table)->column('pkgnum') )
+ ) {
+
+ warn "$me Checking for unmigrated invoice line item details\n" if $DEBUG;
+
+ my @cbpd = qsearch({ 'table' => $class->table,
+ 'hashref' => {},
+ 'extra_sql' => 'WHERE invnum IS NOT NULL AND '.
+ 'pkgnum IS NOT NULL',
+ });
+
+ if (scalar(@cbpd)) {
+ warn "$me Found unmigrated invoice line item details\n" if $DEBUG;
+
+ foreach my $cbpd ( @cbpd ) {
+ my $detailnum = $cbpd->detailnum;
+ warn "$me Contemplating detail $detailnum\n" if $DEBUG > 1;
+ my $cust_bill_pkg =
+ qsearchs({ 'table' => 'cust_bill_pkg',
+ 'hashref' => { 'invnum' => $cbpd->invnum,
+ 'pkgnum' => $cbpd->pkgnum,
+ },
+ 'order_by' => 'ORDER BY billpkgnum LIMIT 1',
+ });
+ if ($cust_bill_pkg) {
+ $cbpd->billpkgnum($cust_bill_pkg->billpkgnum);
+ $cbpd->invnum('');
+ $cbpd->pkgnum('');
+ my $error = $cbpd->replace;
+
+ warn "*** WARNING: error replacing line item detail ".
+ "(cust_bill_pkg_detail) $detailnum: $error ***\n"
+ if $error;
+ } else {
+ warn "Found orphaned line item detail $detailnum during upgrade.\n";
+ }
+
+ } # foreach $cbpd
+
+ } # if @cbpd
+
+ } # if billpkgnum, invnum, and pkgnum columns defined
+
+ '';
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_bill_pkg>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pkg_display.pm b/FS/FS/cust_bill_pkg_display.pm
new file mode 100644
index 0000000..93c6e87
--- /dev/null
+++ b/FS/FS/cust_bill_pkg_display.pm
@@ -0,0 +1,158 @@
+package FS::cust_bill_pkg_display;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_bill_pkg_display - Object methods for cust_bill_pkg_display records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pkg_display;
+
+ $record = new FS::cust_bill_pkg_display \%hash;
+ $record = new FS::cust_bill_pkg_display { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pkg_display object represents line item display information.
+FS::cust_bill_pkg_display inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item billpkgdisplaynum
+
+primary key
+
+=item billpkgnum
+
+billpkgnum
+
+=item section
+
+section
+
+=cut
+
+sub section {
+ my ( $self, $value ) = @_;
+ if ( defined($value) ) {
+ $self->setfield('section', $value);
+ } else {
+ $self->getfield('section') || $self->cust_bill_pkg->part_pkg->categoryname;
+ }
+}
+
+=item post_total
+
+post_total
+
+=item type
+
+type
+
+=item summary
+
+summary
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new line item display object. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_bill_pkg_display'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid line item display object.
+If there is an error, returns the error, otherwise returns false. Called by
+the insert and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('billpkgdisplaynum')
+ || $self->ut_number('billpkgnum')
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum')
+ || $self->ut_textn('section')
+ || $self->ut_enum('post_total', [ '', 'Y' ])
+ || $self->ut_enum('type', [ '', 'S', 'R', 'U' ])
+ || $self->ut_enum('summary', [ '', 'Y' ])
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item cust_bill_pkg
+
+Returns the associated cust_bill_pkg (see L<FS::cust_bill_pkg>) for this
+line item display object.
+
+=cut
+
+sub cust_bill_pkg {
+ my $self = shift;
+ qsearchs( 'cust_bill_pkg', { 'billpkgnum' => $self->billpkgnum } ) ;
+}
+
+=back
+
+=head1 BUGS
+
+
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_bill_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_bill_pkg_tax_location.pm b/FS/FS/cust_bill_pkg_tax_location.pm
new file mode 100644
index 0000000..db65237
--- /dev/null
+++ b/FS/FS/cust_bill_pkg_tax_location.pm
@@ -0,0 +1,136 @@
+package FS::cust_bill_pkg_tax_location;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_bill_pkg;
+use FS::cust_pkg;
+use FS::cust_location;
+
+=head1 NAME
+
+FS::cust_bill_pkg_tax_location - Object methods for cust_bill_pkg_tax_location records
+
+=head1 SYNOPSIS
+
+ use FS::cust_bill_pkg_tax_location;
+
+ $record = new FS::cust_bill_pkg_tax_location \%hash;
+ $record = new FS::cust_bill_pkg_tax_location { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pkg_tax_location object represents an record of taxation
+based on package location. FS::cust_bill_pkg_tax_location inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item billpkgtaxlocationnum
+
+billpkgtaxlocationnum
+
+=item billpkgnum
+
+billpkgnum
+
+=item taxnum
+
+taxnum
+
+=item taxtype
+
+taxtype
+
+=item pkgnum
+
+pkgnum
+
+=item locationnum
+
+locationnum
+
+=item amount
+
+amount
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_bill_pkg_tax_location'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('billpkgtaxlocationnum')
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum' )
+ || $self->ut_number('taxnum') #cust_bill_pkg/tax_rate key, based on taxtype
+ || $self->ut_enum('taxtype', [ qw( FS::cust_main_county FS::tax_rate ) ] )
+ || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum' )
+ || $self->ut_foreign_key('locationnum', 'cust_location', 'locationnum' )
+ || $self->ut_money('amount')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
new file mode 100644
index 0000000..47a8119
--- /dev/null
+++ b/FS/FS/cust_credit.pm
@@ -0,0 +1,602 @@
+package FS::cust_credit;
+
+use strict;
+use vars qw( @ISA $conf $unsuspendauto $me $DEBUG );
+use Date::Format;
+use FS::UID qw( dbh getotaker );
+use FS::Misc qw(send_email);
+use FS::Record qw( qsearch qsearchs dbdef );
+use FS::cust_main_Mixin;
+use FS::cust_main;
+use FS::cust_refund;
+use FS::cust_credit_bill;
+use FS::part_pkg;
+use FS::reason_type;
+use FS::reason;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+$me = '[ FS::cust_credit ]';
+$DEBUG = 0;
+
+#ask FS::UID to run this stuff for us later
+$FS::UID::callback{'FS::cust_credit'} = sub {
+
+ $conf = new FS::Conf;
+ $unsuspendauto = $conf->exists('unsuspendauto');
+
+};
+
+our %reasontype_map = ( 'referral_credit_type' => 'Referral Credit',
+ 'cancel_credit_type' => 'Cancellation Credit',
+ 'signup_credit_type' => 'Self-Service Credit',
+ );
+
+=head1 NAME
+
+FS::cust_credit - Object methods for cust_credit records
+
+=head1 SYNOPSIS
+
+ use FS::cust_credit;
+
+ $record = new FS::cust_credit \%hash;
+ $record = new FS::cust_credit { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_credit object represents a credit; the equivalent of a negative
+B<cust_bill> record (see L<FS::cust_bill>). FS::cust_credit inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item crednum
+
+Primary key (assigned automatically for new credits)
+
+=item custnum
+
+Customer (see L<FS::cust_main>)
+
+=item amount
+
+Amount of the credit
+
+=item _date
+
+Specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item otaker
+
+Order taker (assigned automatically, see L<FS::UID>)
+
+=item reason
+
+Text ( deprecated )
+
+=item reasonnum
+
+Reason (see L<FS::reason>)
+
+=item addlinfo
+
+Text
+
+=item closed
+
+Books closed flag, empty or `Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new credit. To add the credit to the database, see L<"insert">.
+
+=cut
+
+sub table { 'cust_credit'; }
+sub cust_linked { $_[0]->cust_main_custnum; }
+sub cust_unlinked_msg {
+ my $self = shift;
+ "WARNING: can't find cust_main.custnum ". $self->custnum.
+ ' (cust_credit.crednum '. $self->crednum. ')';
+}
+
+=item insert
+
+Adds this credit to the database ("Posts" the credit). If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub insert {
+ my ($self, %options) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $cust_main = qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+ my $old_balance = $cust_main->balance;
+
+ unless ($self->reasonnum) {
+ my $result = $self->reason( $self->getfield('reason'),
+ exists($options{ 'reason_type' })
+ ? ('reason_type' => $options{ 'reason_type' })
+ : (),
+ );
+ unless($result) {
+ $dbh->rollback if $oldAutoCommit;
+ return "failed to set reason for $me: ". $dbh->errstr;
+ }
+ }
+
+ $self->setfield('reason', '');
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error inserting $self: $error";
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ #false laziness w/ cust_credit::insert
+ if ( $unsuspendauto && $old_balance && $cust_main->balance <= 0 ) {
+ my @errors = $cust_main->unsuspend;
+ #return
+ # side-fx with nested transactions? upstack rolls back?
+ warn "WARNING:Errors unsuspending customer ". $cust_main->custnum. ": ".
+ join(' / ', @errors)
+ if @errors;
+ }
+ #eslaf
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Unless the closed flag is set, deletes this credit and all associated
+applications (see L<FS::cust_credit_bill>). In most cases, you want to use
+the void method instead to leave a record of the deleted credit.
+
+=cut
+
+# very similar to FS::cust_pay::delete
+sub delete {
+ my $self = shift;
+ return "Can't delete closed credit" if $self->closed =~ /^Y/i;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $cust_credit_bill ( $self->cust_credit_bill ) {
+ my $error = $cust_credit_bill->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ foreach my $cust_credit_refund ( $self->cust_credit_refund ) {
+ my $error = $cust_credit_refund->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $conf->config('deletecredits') ne '' ) {
+
+ my $cust_main = $self->cust_main;
+
+ my $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->cust_main->agentnum),
+ #invoice_from??? well as good as any
+ 'to' => $conf->config('deletecredits'),
+ 'subject' => 'FREESIDE NOTIFICATION: Credit deleted',
+ 'body' => [
+ "This is an automatic message from your Freeside installation\n",
+ "informing you that the following credit has been deleted:\n",
+ "\n",
+ 'crednum: '. $self->crednum. "\n",
+ 'custnum: '. $self->custnum.
+ " (". $cust_main->last. ", ". $cust_main->first. ")\n",
+ 'amount: $'. sprintf("%.2f", $self->amount). "\n",
+ 'date: '. time2str("%a %b %e %T %Y", $self->_date). "\n",
+ 'reason: '. $self->reason. "\n",
+ ],
+ );
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't send credit deletion notification: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item replace OLD_RECORD
+
+You can, but probably shouldn't modify credits...
+
+=cut
+
+sub replace {
+ #return "Can't modify credit!"
+ my $self = shift;
+ return "Can't modify closed credit" if $self->closed =~ /^Y/i;
+ $self->SUPER::replace(@_);
+}
+
+=item check
+
+Checks all fields to make sure this is a valid credit. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->otaker(getotaker) unless ($self->otaker);
+
+ my $error =
+ $self->ut_numbern('crednum')
+ || $self->ut_number('custnum')
+ || $self->ut_numbern('_date')
+ || $self->ut_money('amount')
+ || $self->ut_alpha('otaker')
+ || $self->ut_textn('reason')
+ || $self->ut_foreign_key('reasonnum', 'reason', 'reasonnum')
+ || $self->ut_textn('addlinfo')
+ || $self->ut_enum('closed', [ '', 'Y' ])
+ ;
+ return $error if $error;
+
+ return "amount must be > 0 " if $self->amount <= 0;
+
+ return "Unknown customer"
+ unless qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+
+ $self->_date(time) unless $self->_date;
+
+ $self->SUPER::check;
+}
+
+=item cust_credit_refund
+
+Returns all refund applications (see L<FS::cust_credit_refund>) for this credit.
+
+=cut
+
+sub cust_credit_refund {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_credit_refund', { 'crednum' => $self->crednum } )
+ ;
+}
+
+=item cust_credit_bill
+
+Returns all application to invoices (see L<FS::cust_credit_bill>) for this
+credit.
+
+=cut
+
+sub cust_credit_bill {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_credit_bill', { 'crednum' => $self->crednum } )
+ ;
+}
+
+=item unapplied
+
+Returns the amount of this credit that is still unapplied/outstanding;
+amount minus all refund applications (see L<FS::cust_credit_refund>) and
+applications to invoices (see L<FS::cust_credit_bill>).
+
+=cut
+
+sub unapplied {
+ my $self = shift;
+ my $amount = $self->amount;
+ $amount -= $_->amount foreach ( $self->cust_credit_refund );
+ $amount -= $_->amount foreach ( $self->cust_credit_bill );
+ sprintf( "%.2f", $amount );
+}
+
+=item credited
+
+Deprecated name for the unapplied method.
+
+=cut
+
+sub credited {
+ my $self = shift;
+ #carp "cust_credit->credited deprecated; use ->unapplied";
+ $self->unapplied(@_);
+}
+
+=item cust_main
+
+Returns the customer (see L<FS::cust_main>) for this credit.
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+
+=item reason
+
+Returns the text of the associated reason (see L<FS::reason>) for this credit.
+
+=cut
+
+sub reason {
+ my ($self, $value, %options) = @_;
+ my $dbh = dbh;
+ my $reason;
+ my $typenum = $options{'reason_type'};
+
+ my $oldAutoCommit = $FS::UID::AutoCommit; # this should already be in
+ local $FS::UID::AutoCommit = 0; # a transaction if it matters
+
+ if ( defined( $value ) ) {
+ my $hashref = { 'reason' => $value };
+ $hashref->{'reason_type'} = $typenum if $typenum;
+ my $addl_from = "LEFT JOIN reason_type ON ( reason_type = typenum ) ";
+ my $extra_sql = " AND reason_type.class='R'";
+
+ $reason = qsearchs( { 'table' => 'reason',
+ 'hashref' => $hashref,
+ 'addl_from' => $addl_from,
+ 'extra_sql' => $extra_sql,
+ } );
+
+ if (!$reason && $typenum) {
+ $reason = new FS::reason( { 'reason_type' => $typenum,
+ 'reason' => $value,
+ 'disabled' => 'Y',
+ } );
+ $reason->insert and $reason = undef;
+ }
+
+ $self->reasonnum($reason ? $reason->reasonnum : '') ;
+ warn "$me reason used in set mode with non-existant reason -- clearing"
+ unless $reason;
+ }
+ $reason = qsearchs( 'reason', { 'reasonnum' => $self->reasonnum } );
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ( $reason ? $reason->reason : '' ).
+ ( $self->addlinfo ? ' '.$self->addlinfo : '' );
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+sub _upgrade_data { # class method
+ my ($class, %opts) = @_;
+
+ warn "$me upgrading $class\n" if $DEBUG;
+
+ if (defined dbdef->table($class->table)->column('reason')) {
+
+ warn "$me Checking for unmigrated reasons\n" if $DEBUG;
+
+ my @cust_credits = qsearch({ 'table' => $class->table,
+ 'hashref' => {},
+ 'extra_sql' => 'WHERE reason IS NOT NULL',
+ });
+
+ if (scalar(grep { $_->getfield('reason') =~ /\S/ } @cust_credits)) {
+ warn "$me Found unmigrated reasons\n" if $DEBUG;
+ my $hashref = { 'class' => 'R', 'type' => 'Legacy' };
+ my $reason_type = qsearchs( 'reason_type', $hashref );
+ unless ($reason_type) {
+ $reason_type = new FS::reason_type( $hashref );
+ my $error = $reason_type->insert();
+ die "$class had error inserting FS::reason_type into database: $error\n"
+ if $error;
+ }
+
+ $hashref = { 'reason_type' => $reason_type->typenum,
+ 'reason' => '(none)'
+ };
+ my $noreason = qsearchs( 'reason', $hashref );
+ unless ($noreason) {
+ $hashref->{'disabled'} = 'Y';
+ $noreason = new FS::reason( $hashref );
+ my $error = $noreason->insert();
+ die "can't insert legacy reason '(none)' into database: $error\n"
+ if $error;
+ }
+
+ foreach my $cust_credit ( @cust_credits ) {
+ my $reason = $cust_credit->getfield('reason');
+ warn "Contemplating reason $reason\n" if $DEBUG > 1;
+ if ($reason =~ /\S/) {
+ $cust_credit->reason($reason, 'reason_type' => $reason_type->typenum)
+ or die "can't insert legacy reason $reason into database\n";
+ }else{
+ $cust_credit->reasonnum($noreason->reasonnum);
+ }
+
+ $cust_credit->setfield('reason', '');
+ my $error = $cust_credit->replace;
+
+ warn "*** WARNING: error replacing reason in $class ".
+ $cust_credit->crednum. ": $error ***\n"
+ if $error;
+ }
+ }
+
+ warn "$me Ensuring existance of auto reasons\n" if $DEBUG;
+
+ foreach ( keys %reasontype_map ) {
+ unless ($conf->config($_)) { # hmmmm
+# warn "$me Found $_ reason type lacking\n" if $DEBUG;
+# my $hashref = { 'class' => 'R', 'type' => $reasontype_map{$_} };
+ my $hashref = { 'class' => 'R', 'type' => 'Legacy' };
+ my $reason_type = qsearchs( 'reason_type', $hashref );
+ unless ($reason_type) {
+ $reason_type = new FS::reason_type( $hashref );
+ my $error = $reason_type->insert();
+ die "$class had error inserting FS::reason_type into database: $error\n"
+ if $error;
+ }
+ $conf->set($_, $reason_type->typenum);
+ }
+ }
+
+ warn "$me Ensuring commission packages have a reason type\n" if $DEBUG;
+
+ my $hashref = { 'class' => 'R', 'type' => 'Legacy' };
+ my $reason_type = qsearchs( 'reason_type', $hashref );
+ unless ($reason_type) {
+ $reason_type = new FS::reason_type( $hashref );
+ my $error = $reason_type->insert();
+ die "$class had error inserting FS::reason_type into database: $error\n"
+ if $error;
+ }
+
+ my @plans = qw( flat_comission flat_comission_cust flat_comission_pkg );
+ foreach my $plan ( @plans ) {
+ foreach my $pkg ( qsearch('part_pkg', { 'plan' => $plan } ) ) {
+ unless ($pkg->option('reason_type', 1) ) {
+ my $plandata = $pkg->plandata.
+ "reason_type=". $reason_type->typenum. "\n";
+ $pkg->plandata($plandata);
+ my $error =
+ $pkg->replace( undef,
+ 'pkg_svc' => { map { $_->svcpart => $_->quantity }
+ $pkg->pkg_svc
+ },
+ 'primary_svc' => $pkg->svcpart,
+ );
+ die "failed setting reason_type option: $error"
+ if $error;
+ }
+ }
+ }
+ }
+
+ '';
+
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item unapplied_sql
+
+Returns an SQL fragment to retreive the unapplied amount.
+
+=cut
+
+sub unapplied_sql {
+ #my $class = shift;
+
+ "amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ";
+
+}
+
+=item credited_sql
+
+Deprecated name for the unapplied_sql method.
+
+=cut
+
+sub credited_sql {
+ #my $class = shift;
+
+ #carp "cust_credit->credited_sql deprecated; use ->unapplied_sql";
+
+ #$class->unapplied_sql(@_);
+ unapplied_sql();
+}
+
+=back
+
+=head1 BUGS
+
+The delete method. The replace method.
+
+B<credited> and B<credited_sql> are now called B<unapplied> and
+B<unapplied_sql>. The old method names should start to give warnings.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_credit_refund>, L<FS::cust_refund>,
+L<FS::cust_credit_bill> L<FS::cust_bill>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_credit_bill.pm b/FS/FS/cust_credit_bill.pm
new file mode 100644
index 0000000..375c885
--- /dev/null
+++ b/FS/FS/cust_credit_bill.pm
@@ -0,0 +1,168 @@
+package FS::cust_credit_bill;
+
+use strict;
+use vars qw( @ISA $conf );
+use FS::UID qw( getotaker );
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_main_Mixin;
+use FS::cust_bill_ApplicationCommon;
+use FS::cust_bill;
+use FS::cust_credit;
+
+@ISA = qw( FS::cust_main_Mixin FS::cust_bill_ApplicationCommon );
+
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+} );
+
+=head1 NAME
+
+FS::cust_credit_bill - Object methods for cust_credit_bill records
+
+=head1 SYNOPSIS
+
+ use FS::cust_credit_bill;
+
+ $record = new FS::cust_credit_bill \%hash;
+ $record = new FS::cust_credit_bill { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_credit_bill object represents application of a credit (see
+L<FS::cust_credit>) to an invoice (see L<FS::cust_bill>). FS::cust_credit_bill
+inherits from FS::cust_bill_ApplicationCommon and FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item creditbillnum - primary key
+
+=item crednum - credit being applied
+
+=item invnum - invoice to which credit is applied (see L<FS::cust_bill>)
+
+=item amount - amount of the credit applied
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new cust_credit_bill. To add the cust_credit_bill to the database,
+see L<"insert">.
+
+=cut
+
+sub table { 'cust_credit_bill'; }
+
+sub _app_source_name { 'credit'; }
+sub _app_source_table { 'cust_credit'; }
+sub _app_lineitem_breakdown_table { 'cust_credit_bill_pkg'; }
+sub _app_part_pkg_weight_column { 'credit_weight'; }
+
+=item insert
+
+Adds this cust_credit_bill to the database ("Posts" all or part of a credit).
+If there is an error, returns the error, otherwise returns false.
+
+=item delete
+
+Currently unimplemented.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return "Can't delete application for closed credit"
+ if $self->cust_credit->closed =~ /^Y/i;
+ return "Can't delete application for closed invoice"
+ if $self->cust_bill->closed =~ /^Y/i;
+ $self->SUPER::delete(@_);
+}
+
+=item replace OLD_RECORD
+
+Application of credits may not be modified.
+
+=cut
+
+sub replace {
+ return "Can't modify application of credit!"
+}
+
+=item check
+
+Checks all fields to make sure this is a valid credit application. If there
+is an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('creditbillnum')
+ || $self->ut_foreign_key('crednum', 'cust_credit', 'crednum')
+ || $self->ut_foreign_key('invnum', 'cust_bill', 'invnum' )
+ || $self->ut_numbern('_date')
+ || $self->ut_money('amount')
+ ;
+ return $error if $error;
+
+ return "amount must be > 0" if $self->amount <= 0;
+
+ $self->_date(time) unless $self->_date;
+
+ return "Cannot apply more than remaining value of credit"
+ unless $self->amount <= $self->cust_credit->credited;
+
+ return "Cannot apply more than remaining value of invoice"
+ unless $self->amount <= $self->cust_bill->owed;
+
+ $self->SUPER::check;
+}
+
+=item sub cust_credit
+
+Returns the credit (see L<FS::cust_credit>)
+
+=cut
+
+sub cust_credit {
+ my $self = shift;
+ qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
+}
+
+=back
+
+=head1 BUGS
+
+The delete method.
+
+This probably should have been called cust_bill_credit.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_bill>, L<FS::cust_credit>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_credit_bill_pkg.pm b/FS/FS/cust_credit_bill_pkg.pm
new file mode 100644
index 0000000..7252be5
--- /dev/null
+++ b/FS/FS/cust_credit_bill_pkg.pm
@@ -0,0 +1,141 @@
+package FS::cust_credit_bill_pkg;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_credit_bill_pkg - Object methods for cust_credit_bill_pkg records
+
+=head1 SYNOPSIS
+
+ use FS::cust_credit_bill_pkg;
+
+ $record = new FS::cust_credit_bill_pkg \%hash;
+ $record = new FS::cust_credit_bill_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_credit_bill_pkg object represents application of a credit (see
+L<FS::cust_credit_bill>) to a specific line item within an invoice
+(see L<FS::cust_bill_pkg>). FS::cust_credit_bill_pkg inherits from FS::Record.
+The following fields are currently supported:
+
+=over 4
+
+=item creditbillpkg - primary key
+
+=item creditbillnum - Credit application to the overall invoice (see L<FS::cust_credit::bill>)
+
+=item billpkgnum - Line item to which credit is applied (see L<FS::cust_bill_pkg>)
+
+=item amount - Amount of the credit applied to this line item.
+
+=item setuprecur - 'setup' or 'recur', designates whether the payment was applied to the setup or recurring portion of the line item.
+
+=item sdate - starting date of recurring fee
+
+=item edate - ending date of recurring fee
+
+=back
+
+sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">. Also
+see L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_credit_bill_pkg'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid credit applicaiton. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('creditbillpkgnum')
+ || $self->ut_foreign_key('creditbillnum', 'cust_credit_bill', 'creditbillnum')
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum' )
+ || $self->ut_money('amount')
+ || $self->ut_enum('setuprecur', [ 'setup', 'recur' ] )
+ || $self->ut_numbern('sdate')
+ || $self->ut_numbern('edate')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+B<setuprecur> field is a kludge to compensate for cust_bill_pkg having separate
+setup and recur fields. It should be removed once that's fixed.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_credit_refund.pm b/FS/FS/cust_credit_refund.pm
new file mode 100644
index 0000000..9fc03f2
--- /dev/null
+++ b/FS/FS/cust_credit_refund.pm
@@ -0,0 +1,186 @@
+package FS::cust_credit_refund;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::cust_main_Mixin;
+use FS::cust_credit;
+use FS::cust_refund;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+=head1 NAME
+
+FS::cust_credit_refund - Object methods for cust_bill_pay records
+
+=head1 SYNOPSIS
+
+ use FS::cust_credit_refund;
+
+ $record = new FS::cust_credit_refund \%hash;
+ $record = new FS::cust_credit_refund { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_credit_refund represents the application of a refund to a specific
+credit. FS::cust_credit_refund inherits from FS::Record. The following fields
+are currently supported:
+
+=over 4
+
+=item creditrefundnum - primary key (assigned automatically)
+
+=item crednum - Credit (see L<FS::cust_credit>)
+
+=item refundnum - Refund (see L<FS::cust_refund>)
+
+=item amount - Amount of the refund to apply to the specific credit.
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+=cut
+
+sub table { 'cust_credit_refund'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ return "Can't apply refund to closed credit"
+ if $self->cust_credit->closed =~ /^Y/i;
+ return "Can't apply credit to closed refund"
+ if $self->cust_refund->closed =~ /^Y/i;
+ $self->SUPER::insert(@_);
+}
+
+=item delete
+
+Remove this cust_credit_refund from the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return "Can't remove refund from closed credit"
+ if $self->cust_credit->closed =~ /^Y/i;
+ return "Can't remove credit from closed refund"
+ if $self->cust_refund->closed =~ /^Y/i;
+ $self->SUPER::delete(@_);
+}
+
+=item replace OLD_RECORD
+
+Currently unimplemented (accounting reasons).
+
+=cut
+
+sub replace {
+ return "Can't (yet?) modify cust_credit_refund records!";
+}
+
+=item check
+
+Checks all fields to make sure this is a valid refund application. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+method.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('creditrefundnum')
+ || $self->ut_number('crednum')
+ || $self->ut_number('refundnum')
+ || $self->ut_money('amount')
+ || $self->ut_numbern('_date')
+ ;
+ return $error if $error;
+
+ return "amount must be > 0" if $self->amount <= 0;
+
+ return "unknown cust_credit.crednum: ". $self->crednum
+ unless my $cust_credit =
+ qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
+
+ return "Unknown refund"
+ unless my $cust_refund =
+ qsearchs( 'cust_refund', { 'refundnum' => $self->refundnum } );
+
+ $self->_date(time) unless $self->_date;
+
+ return "Cannot apply more than remaining value of credit"
+ unless $self->amount <= $cust_credit->credited;
+
+ return "Cannot apply more than remaining value of refund"
+ unless $self->amount <= $cust_refund->unapplied;
+
+ $self->SUPER::check;
+}
+
+=item cust_refund
+
+Returns the refund (see L<FS::cust_refund>)
+
+=cut
+
+sub cust_refund {
+ my $self = shift;
+ qsearchs( 'cust_refund', { 'refundnum' => $self->refundnum } );
+}
+
+=item cust_credit
+
+Returns the credit (see L<FS::cust_credit>)
+
+=cut
+
+sub cust_credit {
+ my $self = shift;
+ qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
+}
+
+=back
+
+=head1 BUGS
+
+Delete and replace methods.
+
+the checks for over-applied refunds could be better done like the ones in
+cust_bill_credit
+
+=head1 SEE ALSO
+
+L<FS::cust_credit>, L<FS::cust_refund>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_event.pm b/FS/FS/cust_event.pm
new file mode 100644
index 0000000..5ca8167
--- /dev/null
+++ b/FS/FS/cust_event.pm
@@ -0,0 +1,409 @@
+package FS::cust_event;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Carp qw( croak confess );
+use FS::Record qw( qsearch qsearchs dbdef );
+use FS::cust_main_Mixin;
+use FS::part_event;
+#for cust_X
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::cust_bill;
+
+@ISA = qw(FS::cust_main_Mixin FS::Record);
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::cust_event - Object methods for cust_event records
+
+=head1 SYNOPSIS
+
+ use FS::cust_event;
+
+ $record = new FS::cust_event \%hash;
+ $record = new FS::cust_event { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_event object represents an completed event. FS::cust_event
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item eventnum - primary key
+
+=item eventpart - event definition (see L<FS::part_event>)
+
+=item tablenum - customer, package or invoice, depending on the value of part_event.eventtable (see L<FS::cust_main>, L<FS::cust_pkg>, and L<FS::cust_bill>)
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item status - event status: B<new>, B<locked>, B<done> or B<failed>. Note: B<done> indicates the event is complete and should not be retried (statustext may still be set to an optional message), while B<failed> indicates the event failed and should be retried.
+
+=item statustext - additional status detail (i.e. error or progress message)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new completed invoice event. To add the compelted invoice event to
+the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_event'; }
+
+sub cust_linked { $_[0]->cust_main_custnum; }
+sub cust_unlinked_msg {
+ my $self = shift;
+ "WARNING: can't find cust_main.custnum ". $self->custnum;
+ #' (cust_bill.invnum '. $self->invnum. ')';
+}
+sub custnum {
+ my $self = shift;
+ $self->cust_main_custnum(@_) || $self->SUPER::custnum(@_);
+}
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid completed invoice event. If
+there is an error, returns the error, otherwise returns false. Called by the
+insert and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error = $self->ut_numbern('eventnum')
+ || $self->ut_foreign_key('eventpart', 'part_event', 'eventpart')
+ ;
+ return $error if $error;
+
+ my $eventtable = $self->part_event->eventtable;
+ my $dbdef_eventtable = dbdef->table( $eventtable );
+
+ $error =
+ $self->ut_foreign_key( 'tablenum',
+ $eventtable,
+ $dbdef_eventtable->primary_key
+ )
+ || $self->ut_number('_date')
+ || $self->ut_enum('status', [qw( new locked done failed )])
+ || $self->ut_anything('statustext')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item part_event
+
+Returns the event definition (see L<FS::part_event>) for this completed event.
+
+=cut
+
+sub part_event {
+ my $self = shift;
+ qsearchs( 'part_event', { 'eventpart' => $self->eventpart } );
+}
+
+=item cust_X
+
+Returns the customer, package, invoice or batched payment (see
+L<FS::cust_main>, L<FS::cust_pkg>, L<FS::cust_bill> or L<FS::cust_pay_batch>)
+for this completed invoice event.
+
+=cut
+
+sub cust_bill {
+ croak "FS::cust_event::cust_bill called";
+}
+
+sub cust_X {
+ my $self = shift;
+ my $eventtable = $self->part_event->eventtable;
+ my $dbdef_table = dbdef->table( $eventtable );
+ my $primary_key = $dbdef_table->primary_key;
+ qsearchs( $eventtable, { $primary_key => $self->tablenum } );
+}
+
+=item test_conditions [ OPTION => VALUE ... ]
+
+Tests conditions for this event, returns true if all conditions are satisfied,
+false otherwise.
+
+=cut
+
+sub test_conditions {
+ my( $self, %opt ) = @_;
+ my $part_event = $self->part_event;
+ my $object = $self->cust_X;
+ my @conditions = $part_event->part_event_condition;
+ $opt{'cust_event'} = $self;
+
+ #no unsatisfied conditions
+ #! grep ! $_->condition( $object, %opt ), @conditions;
+ my @unsatisfied = grep ! $_->condition( $object, %opt ), @conditions;
+
+ if ( $opt{'stats_hashref'} ) {
+ foreach my $unsat (@unsatisfied) {
+ $opt{'stats_hashref'}->{$unsat->conditionname}++;
+ }
+ }
+
+ ! @unsatisfied;
+}
+
+=item do_event
+
+Runs the event action.
+
+=cut
+
+sub do_event {
+ my $self = shift;
+
+ my $part_event = $self->part_event;
+
+ my $object = $self->cust_X;
+ my $obj_pkey = $object->primary_key;
+ my $for = "for ". $object->table. " ". $object->$obj_pkey();
+ warn "running cust_event ". $self->eventnum.
+ " (". $part_event->action. ") $for\n"
+ if $DEBUG;
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+
+ my $error;
+ {
+ local $SIG{__DIE__}; # don't want Mason __DIE__ handler active
+ $error = eval { $part_event->do_action($object); };
+ }
+
+ my $status = '';
+ my $statustext = '';
+ if ( $@ ) {
+ $status = 'failed';
+ #$statustext = $@;
+ $statustext = "Error running ". $part_event->action. " action: $@";
+ } elsif ( $error ) {
+ $status = 'done';
+ $statustext = $error;
+ } else {
+ $status = 'done';
+ }
+
+ #replace or add myself
+ $self->_date(time);
+ $self->status($status);
+ $self->statustext($statustext);
+
+ $error = $self->eventnum ? $self->replace : $self->insert;
+ if ( $error ) {
+ #this is why we need that locked state...
+ my $e = 'WARNING: Event run but database not updated - '.
+ 'error replacing or inserting cust_event '. $self->eventnum.
+ " $for: $error\n";
+ warn $e;
+ return $e;
+ }
+
+ '';
+
+}
+
+=item retry
+
+Changes the status of this event from B<done> to B<failed>, allowing it to be
+retried.
+
+=cut
+
+sub retry {
+ my $self = shift;
+ return '' unless $self->status eq 'done';
+ my $old = ref($self)->new( { $self->hash } );
+ $self->status('failed');
+ $self->replace($old);
+}
+
+#=item retryable
+#
+#Changes the statustext of this event to B<retriable>, rendering it
+#retriable (should retry be called).
+#
+#=cut
+
+sub retriable {
+ confess "cust_event->retriable called";
+ my $self = shift;
+ return '' unless $self->status eq 'done';
+ my $old = ref($self)->new( { $self->hash } );
+ $self->statustext('retriable');
+ $self->replace($old);
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item reprint
+
+=cut
+
+sub process_reprint {
+ process_re_X('print', @_);
+}
+
+=item reemail
+
+=cut
+
+sub process_reemail {
+ process_re_X('email', @_);
+}
+
+=item refax
+
+=cut
+
+sub process_refax {
+ process_re_X('fax', @_);
+}
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process_re_X {
+ my( $method, $job ) = ( shift, shift );
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ re_X(
+ $method,
+ $param->{'beginning'},
+ $param->{'ending'},
+ $param->{'failed'},
+ $job,
+ );
+
+}
+
+#this needs some updating based on the 1.7 cust_bill_event.pm still, i think
+sub re_X {
+ my($method, $beginning, $ending, $failed, $job) = @_;
+
+ my $from = 'LEFT JOIN part_event USING ( eventpart )';
+
+ # yuck! hardcoed *AND* sequential scans!
+ my $where = " WHERE action LIKE 'cust_bill_send%'".
+ " AND cust_event._date >= $beginning".
+ " AND cust_event._date <= $ending";
+ $where .= " AND statustext != '' AND statustext IS NOT NULL"
+ if $failed;
+
+ my @cust_event = qsearch({
+ 'table' => 'cust_event',
+ 'addl_from' => $from,
+ 'hashref' => {},
+ 'extra_sql' => $where,
+ });
+
+ my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
+ foreach my $cust_event ( @cust_event ) {
+
+ # XXX
+ $cust_event->cust_bill->$method(
+ $cust_event->part_event->templatename
+ || $cust_event->cust_main->agent_template
+ );
+
+ if ( $job ) { #progressbar foo
+ $num++;
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $num / scalar(@cust_event) )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ }
+
+ #this doesn't work, but it would be nice
+ #if ( $job ) { #progressbar foo
+ # my $error = $job->update_statustext(
+ # scalar(@cust_event). " invoices re-${method}ed"
+ # );
+ # die $error if $error;
+ #}
+
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::part_event>, L<FS::cust_bill>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_location.pm b/FS/FS/cust_location.pm
new file mode 100644
index 0000000..50d2a18
--- /dev/null
+++ b/FS/FS/cust_location.pm
@@ -0,0 +1,196 @@
+package FS::cust_location;
+
+use strict;
+use base qw( FS::Record );
+use Locale::Country;
+use FS::Record qw( qsearch ); #qsearchs );
+use FS::cust_main;
+use FS::cust_main_county;
+
+=head1 NAME
+
+FS::cust_location - Object methods for cust_location records
+
+=head1 SYNOPSIS
+
+ use FS::cust_location;
+
+ $record = new FS::cust_location \%hash;
+ $record = new FS::cust_location { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_location object represents a customer location. FS::cust_location
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item locationnum
+
+primary key
+
+=item custnum
+
+custnum
+
+=item address1
+
+Address line one (required)
+
+=item address2
+
+Address line two (optional)
+
+=item city
+
+City
+
+=item county
+
+County (optional, see L<FS::cust_main_county>)
+
+=item state
+
+State (see L<FS::cust_main_county>)
+
+=item zip
+
+Zip
+
+=item country
+
+Country (see L<FS::cust_main_county>)
+
+=item geocode
+
+Geocode
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new location. To add the location to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_location'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid location. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+#some false laziness w/cust_main, but since it should eventually lose these
+#fields anyway...
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('locationnum')
+ || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
+ || $self->ut_text('address1')
+ || $self->ut_textn('address2')
+ || $self->ut_text('city')
+ || $self->ut_textn('county')
+ || $self->ut_textn('state')
+ || $self->ut_country('country')
+ || $self->ut_zip('zip', $self->country)
+ || $self->ut_alphan('geocode')
+ ;
+ return $error if $error;
+
+ unless ( qsearch('cust_main_county', {
+ 'country' => $self->country,
+ 'state' => '',
+ } ) ) {
+ return "Unknown state/county/country: ".
+ $self->state. "/". $self->county. "/". $self->country
+ unless qsearch('cust_main_county',{
+ 'state' => $self->state,
+ 'county' => $self->county,
+ 'country' => $self->country,
+ } );
+ }
+
+ $self->SUPER::check;
+}
+
+=item country_full
+
+Returns this locations's full country name
+
+=cut
+
+sub country_full {
+ my $self = shift;
+ code2country($self->country);
+}
+
+=item line
+
+Returns this location on one line
+
+=cut
+
+sub line {
+ my $self = shift;
+ my $cydefault = FS::conf->new->config('countrydefault') || 'US';
+
+ my $line = $self->address1;
+ $line .= ', '. $self->address2 if $self->address2;
+ $line .= ', '. $self->city;
+ $line .= ' ('. $self->county. ' county)' if $self->county;
+ $line .= ', '. $self->state if $self->state;
+ $line .= ' '. $self->zip if $self->zip;
+ $line .= ' '. code2country($self->country) if $self->country ne $cydefault;
+
+ $line;
+}
+
+=back
+
+=head1 BUGS
+
+Not yet used for cust_main billing and shipping addresses.
+
+=head1 SEE ALSO
+
+L<FS::cust_main_county>, L<FS::cust_pkg>, L<FS::Record>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
new file mode 100644
index 0000000..865632f
--- /dev/null
+++ b/FS/FS/cust_main.pm
@@ -0,0 +1,7151 @@
+package FS::cust_main;
+
+require 5.006;
+use strict;
+use vars qw( @ISA @EXPORT_OK $DEBUG $me $conf @encrypted_fields
+ $import $skip_fuzzyfiles $ignore_expired_card @paytypes);
+use vars qw( $realtime_bop_decline_quiet ); #ugh
+use Safe;
+use Carp;
+use Exporter;
+use Scalar::Util qw( blessed );
+use Time::Local qw(timelocal);
+use Data::Dumper;
+use Tie::IxHash;
+use Digest::MD5 qw(md5_base64);
+use Date::Format;
+#use Date::Manip;
+use File::Temp qw( tempfile );
+use String::Approx qw(amatch);
+use Business::CreditCard 0.28;
+use Locale::Country;
+use FS::UID qw( getotaker dbh driver_name );
+use FS::Record qw( qsearchs qsearch dbdef );
+use FS::Misc qw( generate_email send_email generate_ps do_print );
+use FS::Msgcat qw(gettext);
+use FS::cust_pkg;
+use FS::cust_svc;
+use FS::cust_bill;
+use FS::cust_bill_pkg;
+use FS::cust_bill_pkg_display;
+use FS::cust_bill_pkg_tax_location;
+use FS::cust_pay;
+use FS::cust_pay_pending;
+use FS::cust_pay_void;
+use FS::cust_pay_batch;
+use FS::cust_credit;
+use FS::cust_refund;
+use FS::part_referral;
+use FS::cust_main_county;
+use FS::cust_location;
+use FS::tax_rate;
+use FS::cust_tax_location;
+use FS::part_pkg_taxrate;
+use FS::agent;
+use FS::cust_main_invoice;
+use FS::cust_credit_bill;
+use FS::cust_bill_pay;
+use FS::prepay_credit;
+use FS::queue;
+use FS::part_pkg;
+use FS::part_event;
+use FS::part_event_condition;
+#use FS::cust_event;
+use FS::type_pkgs;
+use FS::payment_gateway;
+use FS::agent_payment_gateway;
+use FS::banned_pay;
+use FS::payinfo_Mixin;
+use FS::TicketSystem;
+
+@ISA = qw( FS::payinfo_Mixin FS::Record );
+
+@EXPORT_OK = qw( smart_search );
+
+$realtime_bop_decline_quiet = 0;
+
+# 1 is mostly method/subroutine entry and options
+# 2 traces progress of some operations
+# 3 is even more information including possibly sensitive data
+$DEBUG = 0;
+$me = '[FS::cust_main]';
+
+$import = 0;
+$skip_fuzzyfiles = 0;
+$ignore_expired_card = 0;
+
+@encrypted_fields = ('payinfo', 'paycvv');
+@paytypes = ('', 'Personal checking', 'Personal savings', 'Business checking', 'Business savings');
+
+#ask FS::UID to run this stuff for us later
+#$FS::UID::callback{'FS::cust_main'} = sub {
+install_callback FS::UID sub {
+ $conf = new FS::Conf;
+ #yes, need it for stuff below (prolly should be cached)
+};
+
+sub _cache {
+ my $self = shift;
+ my ( $hashref, $cache ) = @_;
+ if ( exists $hashref->{'pkgnum'} ) {
+ #@{ $self->{'_pkgnum'} } = ();
+ my $subcache = $cache->subcache( 'pkgnum', 'cust_pkg', $hashref->{custnum});
+ $self->{'_pkgnum'} = $subcache;
+ #push @{ $self->{'_pkgnum'} },
+ FS::cust_pkg->new_or_cached($hashref, $subcache) if $hashref->{pkgnum};
+ }
+}
+
+=head1 NAME
+
+FS::cust_main - Object methods for cust_main records
+
+=head1 SYNOPSIS
+
+ use FS::cust_main;
+
+ $record = new FS::cust_main \%hash;
+ $record = new FS::cust_main { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ @cust_pkg = $record->all_pkgs;
+
+ @cust_pkg = $record->ncancelled_pkgs;
+
+ @cust_pkg = $record->suspended_pkgs;
+
+ $error = $record->bill;
+ $error = $record->bill %options;
+ $error = $record->bill 'time' => $time;
+
+ $error = $record->collect;
+ $error = $record->collect %options;
+ $error = $record->collect 'invoice_time' => $time,
+ ;
+
+=head1 DESCRIPTION
+
+An FS::cust_main object represents a customer. FS::cust_main inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item custnum
+
+Primary key (assigned automatically for new customers)
+
+=item agentnum
+
+Agent (see L<FS::agent>)
+
+=item refnum
+
+Advertising source (see L<FS::part_referral>)
+
+=item first
+
+First name
+
+=item last
+
+Last name
+
+=item ss
+
+Cocial security number (optional)
+
+=item company
+
+(optional)
+
+=item address1
+
+=item address2
+
+(optional)
+
+=item city
+
+=item county
+
+(optional, see L<FS::cust_main_county>)
+
+=item state
+
+(see L<FS::cust_main_county>)
+
+=item zip
+
+=item country
+
+(see L<FS::cust_main_county>)
+
+=item daytime
+
+phone (optional)
+
+=item night
+
+phone (optional)
+
+=item fax
+
+phone (optional)
+
+=item ship_first
+
+Shipping first name
+
+=item ship_last
+
+Shipping last name
+
+=item ship_company
+
+(optional)
+
+=item ship_address1
+
+=item ship_address2
+
+(optional)
+
+=item ship_city
+
+=item ship_county
+
+(optional, see L<FS::cust_main_county>)
+
+=item ship_state
+
+(see L<FS::cust_main_county>)
+
+=item ship_zip
+
+=item ship_country
+
+(see L<FS::cust_main_county>)
+
+=item ship_daytime
+
+phone (optional)
+
+=item ship_night
+
+phone (optional)
+
+=item ship_fax
+
+phone (optional)
+
+=item payby
+
+Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
+
+=item payinfo
+
+Payment Information (See L<FS::payinfo_Mixin> for data format)
+
+=item paymask
+
+Masked payinfo (See L<FS::payinfo_Mixin> for how this works)
+
+=item paycvv
+
+Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
+
+=item paydate
+
+Expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
+
+=item paystart_month
+
+Start date month (maestro/solo cards only)
+
+=item paystart_year
+
+Start date year (maestro/solo cards only)
+
+=item payissue
+
+Issue number (maestro/solo cards only)
+
+=item payname
+
+Name on card or billing name
+
+=item payip
+
+IP address from which payment information was received
+
+=item tax
+
+Tax exempt, empty or `Y'
+
+=item otaker
+
+Order taker (assigned automatically, see L<FS::UID>)
+
+=item comments
+
+Comments (optional)
+
+=item referral_custnum
+
+Referring customer number
+
+=item spool_cdr
+
+Enable individual CDR spooling, empty or `Y'
+
+=item dundate
+
+A suggestion to events (see L<FS::part_bill_event">) to delay until this unix timestamp
+
+=item squelch_cdr
+
+Discourage individual CDR printing, empty or `Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new customer. To add the customer to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_main'; }
+
+=item insert [ CUST_PKG_HASHREF [ , INVOICING_LIST_ARYREF ] [ , OPTION => VALUE ... ] ]
+
+Adds this customer to the database. If there is an error, returns the error,
+otherwise returns false.
+
+CUST_PKG_HASHREF: If you pass a Tie::RefHash data structure to the insert
+method containing FS::cust_pkg and FS::svc_I<tablename> objects, all records
+are inserted atomicly, or the transaction is rolled back. Passing an empty
+hash reference is equivalent to not supplying this parameter. There should be
+a better explanation of this, but until then, here's an example:
+
+ use Tie::RefHash;
+ tie %hash, 'Tie::RefHash'; #this part is important
+ %hash = (
+ $cust_pkg => [ $svc_acct ],
+ ...
+ );
+ $cust_main->insert( \%hash );
+
+INVOICING_LIST_ARYREF: If you pass an arrarref to the insert method, it will
+be set as the invoicing list (see L<"invoicing_list">). Errors return as
+expected and rollback the entire transaction; it is not necessary to call
+check_invoicing_list first. The invoicing_list is set after the records in the
+CUST_PKG_HASHREF above are inserted, so it is now possible to set an
+invoicing_list destination to the newly-created svc_acct. Here's an example:
+
+ $cust_main->insert( {}, [ $email, 'POST' ] );
+
+Currently available options are: I<depend_jobnum> and I<noexport>.
+
+If I<depend_jobnum> is set, all provisioning jobs will have a dependancy
+on the supplied jobnum (they will not run until the specific job completes).
+This can be used to defer provisioning until some action completes (such
+as running the customer's credit card successfully).
+
+The I<noexport> option is deprecated. If I<noexport> is set true, no
+provisioning jobs (exports) are scheduled. (You can schedule them later with
+the B<reexport> method.)
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my $cust_pkgs = @_ ? shift : {};
+ my $invoicing_list = @_ ? shift : '';
+ my %options = @_;
+ warn "$me insert called with options ".
+ join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $prepay_identifier = '';
+ my( $amount, $seconds ) = ( 0, 0 );
+ my $payby = '';
+ if ( $self->payby eq 'PREPAY' ) {
+
+ $self->payby('BILL');
+ $prepay_identifier = $self->payinfo;
+ $self->payinfo('');
+
+ warn " looking up prepaid card $prepay_identifier\n"
+ if $DEBUG > 1;
+
+ my $error = $self->get_prepay($prepay_identifier, \$amount, \$seconds);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ #return "error applying prepaid card (transaction rolled back): $error";
+ return $error;
+ }
+
+ $payby = 'PREP' if $amount;
+
+ } elsif ( $self->payby =~ /^(CASH|WEST|MCRD)$/ ) {
+
+ $payby = $1;
+ $self->payby('BILL');
+ $amount = $self->paid;
+
+ }
+
+ warn " inserting $self\n"
+ if $DEBUG > 1;
+
+ $self->signupdate(time) unless $self->signupdate;
+
+ $self->auto_agent_custid()
+ if $conf->config('cust_main-auto_agent_custid') && ! $self->agent_custid;
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ #return "inserting cust_main record (transaction rolled back): $error";
+ return $error;
+ }
+
+ warn " setting invoicing list\n"
+ if $DEBUG > 1;
+
+ if ( $invoicing_list ) {
+ $error = $self->check_invoicing_list( $invoicing_list );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ #return "checking invoicing_list (transaction rolled back): $error";
+ return $error;
+ }
+ $self->invoicing_list( $invoicing_list );
+ }
+
+ if ( $conf->config('cust_main-skeleton_tables')
+ && $conf->config('cust_main-skeleton_custnum') ) {
+
+ warn " inserting skeleton records\n"
+ if $DEBUG > 1;
+
+ my $error = $self->start_copy_skel;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ warn " ordering packages\n"
+ if $DEBUG > 1;
+
+ $error = $self->order_pkgs($cust_pkgs, \$seconds, %options);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $seconds ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "No svc_acct record to apply pre-paid time";
+ }
+
+ if ( $amount ) {
+ warn " inserting initial $payby payment of $amount\n"
+ if $DEBUG > 1;
+ $error = $self->insert_cust_pay($payby, $amount, $prepay_identifier);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "inserting payment (transaction rolled back): $error";
+ }
+ }
+
+ unless ( $import || $skip_fuzzyfiles ) {
+ warn " queueing fuzzyfiles update\n"
+ if $DEBUG > 1;
+ $error = $self->queue_fuzzyfiles_update;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "updating fuzzy search cache: $error";
+ }
+ }
+
+ warn " insert complete; committing transaction\n"
+ if $DEBUG > 1;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+use File::CounterFile;
+sub auto_agent_custid {
+ my $self = shift;
+
+ my $format = $conf->config('cust_main-auto_agent_custid');
+ my $agent_custid;
+ if ( $format eq '1YMMXXXXXXXX' ) {
+
+ my $counter = new File::CounterFile 'cust_main.agent_custid';
+ $counter->lock;
+
+ my $ym = 100000000000 + time2str('%y%m00000000', time);
+ if ( $ym > $counter->value ) {
+ $counter->{'value'} = $agent_custid = $ym;
+ $counter->{'updated'} = 1;
+ } else {
+ $agent_custid = $counter->inc;
+ }
+
+ $counter->unlock;
+
+ } else {
+ die "Unknown cust_main-auto_agent_custid format: $format";
+ }
+
+ $self->agent_custid($agent_custid);
+
+}
+
+sub start_copy_skel {
+ my $self = shift;
+
+ #'mg_user_preference' => {},
+ #'mg_user_indicator_profile.user_indicator_profile_id' => { 'mg_profile_indicator.profile_indicator_id' => { 'mg_profile_details.profile_detail_id' }, },
+ #'mg_watchlist_header.watchlist_header_id' => { 'mg_watchlist_details.watchlist_details_id' },
+ #'mg_user_grid_header.grid_header_id' => { 'mg_user_grid_details.user_grid_details_id' },
+ #'mg_portfolio_header.portfolio_header_id' => { 'mg_portfolio_trades.portfolio_trades_id' => { 'mg_portfolio_trades_positions.portfolio_trades_positions_id' } },
+ my @tables = eval(join('\n',$conf->config('cust_main-skeleton_tables')));
+ die $@ if $@;
+
+ _copy_skel( 'cust_main', #tablename
+ $conf->config('cust_main-skeleton_custnum'), #sourceid
+ $self->custnum, #destid
+ @tables, #child tables
+ );
+}
+
+#recursive subroutine, not a method
+sub _copy_skel {
+ my( $table, $sourceid, $destid, %child_tables ) = @_;
+
+ my $primary_key;
+ if ( $table =~ /^(\w+)\.(\w+)$/ ) {
+ ( $table, $primary_key ) = ( $1, $2 );
+ } else {
+ my $dbdef_table = dbdef->table($table);
+ $primary_key = $dbdef_table->primary_key
+ or return "$table has no primary key".
+ " (or do you need to run dbdef-create?)";
+ }
+
+ warn " _copy_skel: $table.$primary_key $sourceid to $destid for ".
+ join (', ', keys %child_tables). "\n"
+ if $DEBUG > 2;
+
+ foreach my $child_table_def ( keys %child_tables ) {
+
+ my $child_table;
+ my $child_pkey = '';
+ if ( $child_table_def =~ /^(\w+)\.(\w+)$/ ) {
+ ( $child_table, $child_pkey ) = ( $1, $2 );
+ } else {
+ $child_table = $child_table_def;
+
+ $child_pkey = dbdef->table($child_table)->primary_key;
+ # or return "$table has no primary key".
+ # " (or do you need to run dbdef-create?)\n";
+ }
+
+ my $sequence = '';
+ if ( keys %{ $child_tables{$child_table_def} } ) {
+
+ return "$child_table has no primary key".
+ " (run dbdef-create or try specifying it?)\n"
+ unless $child_pkey;
+
+ #false laziness w/Record::insert and only works on Pg
+ #refactor the proper last-inserted-id stuff out of Record::insert if this
+ # ever gets use for anything besides a quick kludge for one customer
+ my $default = dbdef->table($child_table)->column($child_pkey)->default;
+ $default =~ /^nextval\(\(?'"?([\w\.]+)"?'/i
+ or return "can't parse $child_table.$child_pkey default value ".
+ " for sequence name: $default";
+ $sequence = $1;
+
+ }
+
+ my @sel_columns = grep { $_ ne $primary_key }
+ dbdef->table($child_table)->columns;
+ my $sel_columns = join(', ', @sel_columns );
+
+ my @ins_columns = grep { $_ ne $child_pkey } @sel_columns;
+ my $ins_columns = ' ( '. join(', ', $primary_key, @ins_columns ). ' ) ';
+ my $placeholders = ' ( ?, '. join(', ', map '?', @ins_columns ). ' ) ';
+
+ my $sel_st = "SELECT $sel_columns FROM $child_table".
+ " WHERE $primary_key = $sourceid";
+ warn " $sel_st\n"
+ if $DEBUG > 2;
+ my $sel_sth = dbh->prepare( $sel_st )
+ or return dbh->errstr;
+
+ $sel_sth->execute or return $sel_sth->errstr;
+
+ while ( my $row = $sel_sth->fetchrow_hashref ) {
+
+ warn " selected row: ".
+ join(', ', map { "$_=".$row->{$_} } keys %$row ). "\n"
+ if $DEBUG > 2;
+
+ my $statement =
+ "INSERT INTO $child_table $ins_columns VALUES $placeholders";
+ my $ins_sth =dbh->prepare($statement)
+ or return dbh->errstr;
+ my @param = ( $destid, map $row->{$_}, @ins_columns );
+ warn " $statement: [ ". join(', ', @param). " ]\n"
+ if $DEBUG > 2;
+ $ins_sth->execute( @param )
+ or return $ins_sth->errstr;
+
+ #next unless keys %{ $child_tables{$child_table} };
+ next unless $sequence;
+
+ #another section of that laziness
+ my $seq_sql = "SELECT currval('$sequence')";
+ my $seq_sth = dbh->prepare($seq_sql) or return dbh->errstr;
+ $seq_sth->execute or return $seq_sth->errstr;
+ my $insertid = $seq_sth->fetchrow_arrayref->[0];
+
+ # don't drink soap! recurse! recurse! okay!
+ my $error =
+ _copy_skel( $child_table_def,
+ $row->{$child_pkey}, #sourceid
+ $insertid, #destid
+ %{ $child_tables{$child_table_def} },
+ );
+ return $error if $error;
+
+ }
+
+ }
+
+ return '';
+
+}
+
+=item order_pkg HASHREF | OPTION => VALUE ...
+
+Orders a single package.
+
+Options may be passed as a list of key/value pairs or as a hash reference.
+Options are:
+
+=over 4
+
+=item cust_pkg
+
+FS::cust_pkg object
+
+=item cust_location
+
+Optional FS::cust_location object
+
+=item svcs
+
+Optional arryaref of FS::svc_* service objects.
+
+=item depend_jobnum
+
+If this option is set to a job queue jobnum (see L<FS::queue>), all provisioning
+jobs will have a dependancy on the supplied job (they will not run until the
+specific job completes). This can be used to defer provisioning until some
+action completes (such as running the customer's credit card successfully).
+
+=back
+
+=cut
+
+sub order_pkg {
+ my $self = shift;
+ my $opt = ref($_[0]) ? shift : { @_ };
+
+ warn "$me order_pkg called with options ".
+ join(', ', map { "$_: $opt->{$_}" } keys %$opt ). "\n"
+ if $DEBUG;
+
+ my $cust_pkg = $opt->{'cust_pkg'};
+ my $seconds = $opt->{'seconds'};
+ my $svcs = $opt->{'svcs'} || [];
+
+ my %svc_options = ();
+ $svc_options{'depend_jobnum'} = $opt->{'depend_jobnum'}
+ if exists($opt->{'depend_jobnum'}) && $opt->{'depend_jobnum'};
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $opt->{'cust_location'} &&
+ ( ! $cust_pkg->locationnum || $cust_pkg->locationnum == -1 ) ) {
+ my $error = $opt->{'cust_location'}->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "inserting cust_location (transaction rolled back): $error";
+ }
+ $cust_pkg->locationnum($opt->{'cust_location'}->locationnum);
+ }
+
+ $cust_pkg->custnum( $self->custnum );
+
+ my $error = $cust_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "inserting cust_pkg (transaction rolled back): $error";
+ }
+
+ foreach my $svc_something ( @{ $opt->{'svcs'} } ) {
+ if ( $svc_something->svcnum ) {
+ my $old_cust_svc = $svc_something->cust_svc;
+ my $new_cust_svc = new FS::cust_svc { $old_cust_svc->hash };
+ $new_cust_svc->pkgnum( $cust_pkg->pkgnum);
+ $error = $new_cust_svc->replace($old_cust_svc);
+ } else {
+ $svc_something->pkgnum( $cust_pkg->pkgnum );
+ if ( $seconds && $$seconds && $svc_something->isa('FS::svc_acct') ) {
+ $svc_something->seconds( $svc_something->seconds + $$seconds );
+ $$seconds = 0;
+ }
+ $error = $svc_something->insert(%svc_options);
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "inserting svc_ (transaction rolled back): $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+
+}
+
+=item order_pkgs HASHREF, [ SECONDSREF, [ , OPTION => VALUE ... ] ]
+
+Like the insert method on an existing record, this method orders multiple
+packages and included services atomicaly. Pass a Tie::RefHash data structure
+to this method containing FS::cust_pkg and FS::svc_I<tablename> objects.
+There should be a better explanation of this, but until then, here's an
+example:
+
+ use Tie::RefHash;
+ tie %hash, 'Tie::RefHash'; #this part is important
+ %hash = (
+ $cust_pkg => [ $svc_acct ],
+ ...
+ );
+ $cust_main->order_pkgs( \%hash, \'0', 'noexport'=>1 );
+
+Services can be new, in which case they are inserted, or existing unaudited
+services, in which case they are linked to the newly-created package.
+
+Currently available options are: I<depend_jobnum> and I<noexport>.
+
+If I<depend_jobnum> is set, all provisioning jobs will have a dependancy
+on the supplied jobnum (they will not run until the specific job completes).
+This can be used to defer provisioning until some action completes (such
+as running the customer's credit card successfully).
+
+The I<noexport> option is deprecated. If I<noexport> is set true, no
+provisioning jobs (exports) are scheduled. (You can schedule them later with
+the B<reexport> method for each cust_pkg object. Using the B<reexport> method
+on the cust_main object is not recommended, as existing services will also be
+reexported.)
+
+=cut
+
+sub order_pkgs {
+ my $self = shift;
+ my $cust_pkgs = shift;
+ my $seconds = shift;
+ my %options = @_;
+
+ warn "$me order_pkgs called with options ".
+ join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ local $FS::svc_Common::noexport_hack = 1 if $options{'noexport'};
+
+ foreach my $cust_pkg ( keys %$cust_pkgs ) {
+
+ my $error = $self->order_pkg( 'cust_pkg' => $cust_pkg,
+ 'svcs' => $cust_pkgs->{$cust_pkg},
+ 'seconds' => $seconds,
+ 'depend_jobnum' => $options{'depend_jobnum'},
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+}
+
+=item recharge_prepay IDENTIFIER | PREPAY_CREDIT_OBJ [ , AMOUNTREF, SECONDSREF, UPBYTEREF, DOWNBYTEREF ]
+
+Recharges this (existing) customer with the specified prepaid card (see
+L<FS::prepay_credit>), specified either by I<identifier> or as an
+FS::prepay_credit object. If there is an error, returns the error, otherwise
+returns false.
+
+Optionally, four scalar references can be passed as well. They will have their
+values filled in with the amount, number of seconds, and number of upload and
+download bytes applied by this prepaid
+card.
+
+=cut
+
+sub recharge_prepay {
+ my( $self, $prepay_credit, $amountref, $secondsref,
+ $upbytesref, $downbytesref, $totalbytesref ) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my( $amount, $seconds, $upbytes, $downbytes, $totalbytes) = ( 0, 0, 0, 0, 0 );
+
+ my $error = $self->get_prepay($prepay_credit, \$amount,
+ \$seconds, \$upbytes, \$downbytes, \$totalbytes)
+ || $self->increment_seconds($seconds)
+ || $self->increment_upbytes($upbytes)
+ || $self->increment_downbytes($downbytes)
+ || $self->increment_totalbytes($totalbytes)
+ || $self->insert_cust_pay_prepay( $amount,
+ ref($prepay_credit)
+ ? $prepay_credit->identifier
+ : $prepay_credit
+ );
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( defined($amountref) ) { $$amountref = $amount; }
+ if ( defined($secondsref) ) { $$secondsref = $seconds; }
+ if ( defined($upbytesref) ) { $$upbytesref = $upbytes; }
+ if ( defined($downbytesref) ) { $$downbytesref = $downbytes; }
+ if ( defined($totalbytesref) ) { $$totalbytesref = $totalbytes; }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item get_prepay IDENTIFIER | PREPAY_CREDIT_OBJ , AMOUNTREF, SECONDSREF
+
+Looks up and deletes a prepaid card (see L<FS::prepay_credit>),
+specified either by I<identifier> or as an FS::prepay_credit object.
+
+References to I<amount> and I<seconds> scalars should be passed as arguments
+and will be incremented by the values of the prepaid card.
+
+If the prepaid card specifies an I<agentnum> (see L<FS::agent>), it is used to
+check or set this customer's I<agentnum>.
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+
+sub get_prepay {
+ my( $self, $prepay_credit, $amountref, $secondsref,
+ $upref, $downref, $totalref) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ unless ( ref($prepay_credit) ) {
+
+ my $identifier = $prepay_credit;
+
+ $prepay_credit = qsearchs(
+ 'prepay_credit',
+ { 'identifier' => $prepay_credit },
+ '',
+ 'FOR UPDATE'
+ );
+
+ unless ( $prepay_credit ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Invalid prepaid card: ". $identifier;
+ }
+
+ }
+
+ if ( $prepay_credit->agentnum ) {
+ if ( $self->agentnum && $self->agentnum != $prepay_credit->agentnum ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "prepaid card not valid for agent ". $self->agentnum;
+ }
+ $self->agentnum($prepay_credit->agentnum);
+ }
+
+ my $error = $prepay_credit->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "removing prepay_credit (transaction rolled back): $error";
+ }
+
+ $$amountref += $prepay_credit->amount;
+ $$secondsref += $prepay_credit->seconds;
+ $$upref += $prepay_credit->upbytes;
+ $$downref += $prepay_credit->downbytes;
+ $$totalref += $prepay_credit->totalbytes;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item increment_upbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of upbytes. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_upbytes {
+ _increment_column( shift, 'upbytes', @_);
+}
+
+=item increment_downbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of downbytes. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_downbytes {
+ _increment_column( shift, 'downbytes', @_);
+}
+
+=item increment_totalbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of totalbytes. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_totalbytes {
+ _increment_column( shift, 'totalbytes', @_);
+}
+
+=item increment_seconds SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of seconds. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_seconds {
+ _increment_column( shift, 'seconds', @_);
+}
+
+=item _increment_column AMOUNT
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of seconds or bytes. If there is an error, returns
+the error, otherwise returns false.
+
+=cut
+
+sub _increment_column {
+ my( $self, $column, $amount ) = @_;
+ warn "$me increment_column called: $column, $amount\n"
+ if $DEBUG;
+
+ return '' unless $amount;
+
+ my @cust_pkg = grep { $_->part_pkg->svcpart('svc_acct') }
+ $self->ncancelled_pkgs;
+
+ if ( ! @cust_pkg ) {
+ return 'No packages with primary or single services found'.
+ ' to apply pre-paid time';
+ } elsif ( scalar(@cust_pkg) > 1 ) {
+ #maybe have a way to specify the package/account?
+ return 'Multiple packages found to apply pre-paid time';
+ }
+
+ my $cust_pkg = $cust_pkg[0];
+ warn " found package pkgnum ". $cust_pkg->pkgnum. "\n"
+ if $DEBUG > 1;
+
+ my @cust_svc =
+ $cust_pkg->cust_svc( $cust_pkg->part_pkg->svcpart('svc_acct') );
+
+ if ( ! @cust_svc ) {
+ return 'No account found to apply pre-paid time';
+ } elsif ( scalar(@cust_svc) > 1 ) {
+ return 'Multiple accounts found to apply pre-paid time';
+ }
+
+ my $svc_acct = $cust_svc[0]->svc_x;
+ warn " found service svcnum ". $svc_acct->pkgnum.
+ ' ('. $svc_acct->email. ")\n"
+ if $DEBUG > 1;
+
+ $column = "increment_$column";
+ $svc_acct->$column($amount);
+
+}
+
+=item insert_cust_pay_prepay AMOUNT [ PAYINFO ]
+
+Inserts a prepayment in the specified amount for this customer. An optional
+second argument can specify the prepayment identifier for tracking purposes.
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub insert_cust_pay_prepay {
+ shift->insert_cust_pay('PREP', @_);
+}
+
+=item insert_cust_pay_cash AMOUNT [ PAYINFO ]
+
+Inserts a cash payment in the specified amount for this customer. An optional
+second argument can specify the payment identifier for tracking purposes.
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub insert_cust_pay_cash {
+ shift->insert_cust_pay('CASH', @_);
+}
+
+=item insert_cust_pay_west AMOUNT [ PAYINFO ]
+
+Inserts a Western Union payment in the specified amount for this customer. An
+optional second argument can specify the prepayment identifier for tracking
+purposes. If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub insert_cust_pay_west {
+ shift->insert_cust_pay('WEST', @_);
+}
+
+sub insert_cust_pay {
+ my( $self, $payby, $amount ) = splice(@_, 0, 3);
+ my $payinfo = scalar(@_) ? shift : '';
+
+ my $cust_pay = new FS::cust_pay {
+ 'custnum' => $self->custnum,
+ 'paid' => sprintf('%.2f', $amount),
+ #'_date' => #date the prepaid card was purchased???
+ 'payby' => $payby,
+ 'payinfo' => $payinfo,
+ };
+ $cust_pay->insert;
+
+}
+
+=item reexport
+
+This method is deprecated. See the I<depend_jobnum> option to the insert and
+order_pkgs methods for a better way to defer provisioning.
+
+Re-schedules all exports by calling the B<reexport> method of all associated
+packages (see L<FS::cust_pkg>). If there is an error, returns the error;
+otherwise returns false.
+
+=cut
+
+sub reexport {
+ my $self = shift;
+
+ carp "WARNING: FS::cust_main::reexport is deprectated; ".
+ "use the depend_jobnum option to insert or order_pkgs to delay export";
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $cust_pkg ( $self->ncancelled_pkgs ) {
+ my $error = $cust_pkg->reexport;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item delete NEW_CUSTNUM
+
+This deletes the customer. If there is an error, returns the error, otherwise
+returns false.
+
+This will completely remove all traces of the customer record. This is not
+what you want when a customer cancels service; for that, cancel all of the
+customer's packages (see L</cancel>).
+
+If the customer has any uncancelled packages, you need to pass a new (valid)
+customer number for those packages to be transferred to. Cancelled packages
+will be deleted. Did I mention that this is NOT what you want when a customer
+cancels service and that you really should be looking see L<FS::cust_pkg/cancel>?
+
+You can't delete a customer with invoices (see L<FS::cust_bill>),
+or credits (see L<FS::cust_credit>), payments (see L<FS::cust_pay>) or
+refunds (see L<FS::cust_refund>).
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $self->cust_bill ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't delete a customer with invoices";
+ }
+ if ( $self->cust_credit ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't delete a customer with credits";
+ }
+ if ( $self->cust_pay ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't delete a customer with payments";
+ }
+ if ( $self->cust_refund ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't delete a customer with refunds";
+ }
+
+ my @cust_pkg = $self->ncancelled_pkgs;
+ if ( @cust_pkg ) {
+ my $new_custnum = shift;
+ unless ( qsearchs( 'cust_main', { 'custnum' => $new_custnum } ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Invalid new customer number: $new_custnum";
+ }
+ foreach my $cust_pkg ( @cust_pkg ) {
+ my %hash = $cust_pkg->hash;
+ $hash{'custnum'} = $new_custnum;
+ my $new_cust_pkg = new FS::cust_pkg ( \%hash );
+ my $error = $new_cust_pkg->replace($cust_pkg,
+ options => { $cust_pkg->options },
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+ my @cancelled_cust_pkg = $self->all_pkgs;
+ foreach my $cust_pkg ( @cancelled_cust_pkg ) {
+ my $error = $cust_pkg->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ foreach my $cust_main_invoice ( #(email invoice destinations, not invoices)
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } )
+ ) {
+ my $error = $cust_main_invoice->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item replace [ OLD_RECORD ] [ INVOICING_LIST_ARYREF ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+INVOICING_LIST_ARYREF: If you pass an arrarref to the insert method, it will
+be set as the invoicing list (see L<"invoicing_list">). Errors return as
+expected and rollback the entire transaction; it is not necessary to call
+check_invoicing_list first. Here's an example:
+
+ $new_cust_main->replace( $old_cust_main, [ $email, 'POST' ] );
+
+=cut
+
+sub replace {
+ my $self = shift;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $self->replace_old;
+
+ my @param = @_;
+
+ warn "$me replace called\n"
+ if $DEBUG;
+
+ my $curuser = $FS::CurrentUser::CurrentUser;
+ if ( $self->payby eq 'COMP'
+ && $self->payby ne $old->payby
+ && ! $curuser->access_right('Complimentary customer')
+ )
+ {
+ return "You are not permitted to create complimentary accounts.";
+ }
+
+ local($ignore_expired_card) = 1
+ if $old->payby =~ /^(CARD|DCRD)$/
+ && $self->payby =~ /^(CARD|DCRD)$/
+ && ( $old->payinfo eq $self->payinfo || $old->paymask eq $self->paymask );
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::replace($old);
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( @param ) { # INVOICING_LIST_ARYREF
+ my $invoicing_list = shift @param;
+ $error = $self->check_invoicing_list( $invoicing_list );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $self->invoicing_list( $invoicing_list );
+ }
+
+ if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ &&
+ grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) {
+ # card/check/lec info has changed, want to retry realtime_ invoice events
+ my $error = $self->retry_realtime;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ unless ( $import || $skip_fuzzyfiles ) {
+ $error = $self->queue_fuzzyfiles_update;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "updating fuzzy search cache: $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item queue_fuzzyfiles_update
+
+Used by insert & replace to update the fuzzy search cache
+
+=cut
+
+sub queue_fuzzyfiles_update {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
+ my $error = $queue->insert( map $self->getfield($_),
+ qw(first last company)
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "queueing job (transaction rolled back): $error";
+ }
+
+ if ( $self->ship_last ) {
+ $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
+ $error = $queue->insert( map $self->getfield("ship_$_"),
+ qw(first last company)
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "queueing job (transaction rolled back): $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid customer record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ warn "$me check BEFORE: \n". $self->_dump
+ if $DEBUG > 2;
+
+ my $error =
+ $self->ut_numbern('custnum')
+ || $self->ut_number('agentnum')
+ || $self->ut_textn('agent_custid')
+ || $self->ut_number('refnum')
+ || $self->ut_textn('custbatch')
+ || $self->ut_name('last')
+ || $self->ut_name('first')
+ || $self->ut_snumbern('birthdate')
+ || $self->ut_snumbern('signupdate')
+ || $self->ut_textn('company')
+ || $self->ut_text('address1')
+ || $self->ut_textn('address2')
+ || $self->ut_text('city')
+ || $self->ut_textn('county')
+ || $self->ut_textn('state')
+ || $self->ut_country('country')
+ || $self->ut_anything('comments')
+ || $self->ut_numbern('referral_custnum')
+ || $self->ut_textn('stateid')
+ || $self->ut_textn('stateid_state')
+ || $self->ut_textn('invoice_terms')
+ || $self->ut_alphan('geocode')
+ ;
+
+ #barf. need message catalogs. i18n. etc.
+ $error .= "Please select an advertising source."
+ if $error =~ /^Illegal or empty \(numeric\) refnum: /;
+ return $error if $error;
+
+ return "Unknown agent"
+ unless qsearchs( 'agent', { 'agentnum' => $self->agentnum } );
+
+ return "Unknown refnum"
+ unless qsearchs( 'part_referral', { 'refnum' => $self->refnum } );
+
+ return "Unknown referring custnum: ". $self->referral_custnum
+ unless ! $self->referral_custnum
+ || qsearchs( 'cust_main', { 'custnum' => $self->referral_custnum } );
+
+ if ( $self->ss eq '' ) {
+ $self->ss('');
+ } else {
+ my $ss = $self->ss;
+ $ss =~ s/\D//g;
+ $ss =~ /^(\d{3})(\d{2})(\d{4})$/
+ or return "Illegal social security number: ". $self->ss;
+ $self->ss("$1-$2-$3");
+ }
+
+
+# bad idea to disable, causes billing to fail because of no tax rates later
+# unless ( $import ) {
+ unless ( qsearch('cust_main_county', {
+ 'country' => $self->country,
+ 'state' => '',
+ } ) ) {
+ return "Unknown state/county/country: ".
+ $self->state. "/". $self->county. "/". $self->country
+ unless qsearch('cust_main_county',{
+ 'state' => $self->state,
+ 'county' => $self->county,
+ 'country' => $self->country,
+ } );
+ }
+# }
+
+ $error =
+ $self->ut_phonen('daytime', $self->country)
+ || $self->ut_phonen('night', $self->country)
+ || $self->ut_phonen('fax', $self->country)
+ || $self->ut_zip('zip', $self->country)
+ ;
+ return $error if $error;
+
+ if ( $conf->exists('cust_main-require_phone')
+ && ! length($self->daytime) && ! length($self->night)
+ ) {
+
+ my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/
+ ? 'Day Phone'
+ : FS::Msgcat::_gettext('daytime');
+ my $night_label = FS::Msgcat::_gettext('night') =~ /^(night)?$/
+ ? 'Night Phone'
+ : FS::Msgcat::_gettext('night');
+
+ return "$daytime_label or $night_label is required"
+
+ }
+
+ if ( $self->has_ship_address
+ && scalar ( grep { $self->getfield($_) ne $self->getfield("ship_$_") }
+ $self->addr_fields )
+ )
+ {
+ my $error =
+ $self->ut_name('ship_last')
+ || $self->ut_name('ship_first')
+ || $self->ut_textn('ship_company')
+ || $self->ut_text('ship_address1')
+ || $self->ut_textn('ship_address2')
+ || $self->ut_text('ship_city')
+ || $self->ut_textn('ship_county')
+ || $self->ut_textn('ship_state')
+ || $self->ut_country('ship_country')
+ ;
+ return $error if $error;
+
+ #false laziness with above
+ unless ( qsearchs('cust_main_county', {
+ 'country' => $self->ship_country,
+ 'state' => '',
+ } ) ) {
+ return "Unknown ship_state/ship_county/ship_country: ".
+ $self->ship_state. "/". $self->ship_county. "/". $self->ship_country
+ unless qsearch('cust_main_county',{
+ 'state' => $self->ship_state,
+ 'county' => $self->ship_county,
+ 'country' => $self->ship_country,
+ } );
+ }
+ #eofalse
+
+ $error =
+ $self->ut_phonen('ship_daytime', $self->ship_country)
+ || $self->ut_phonen('ship_night', $self->ship_country)
+ || $self->ut_phonen('ship_fax', $self->ship_country)
+ || $self->ut_zip('ship_zip', $self->ship_country)
+ ;
+ return $error if $error;
+
+ return "Unit # is required."
+ if $self->ship_address2 =~ /^\s*$/
+ && $conf->exists('cust_main-require_address2');
+
+ } else { # ship_ info eq billing info, so don't store dup info in database
+
+ $self->setfield("ship_$_", '')
+ foreach $self->addr_fields;
+
+ return "Unit # is required."
+ if $self->address2 =~ /^\s*$/
+ && $conf->exists('cust_main-require_address2');
+
+ }
+
+ #$self->payby =~ /^(CARD|DCRD|CHEK|DCHK|LECB|BILL|COMP|PREPAY|CASH|WEST|MCRD)$/
+ # or return "Illegal payby: ". $self->payby;
+ #$self->payby($1);
+ FS::payby->can_payby($self->table, $self->payby)
+ or return "Illegal payby: ". $self->payby;
+
+ $error = $self->ut_numbern('paystart_month')
+ || $self->ut_numbern('paystart_year')
+ || $self->ut_numbern('payissue')
+ || $self->ut_textn('paytype')
+ ;
+ return $error if $error;
+
+ if ( $self->payip eq '' ) {
+ $self->payip('');
+ } else {
+ $error = $self->ut_ip('payip');
+ return $error if $error;
+ }
+
+ # If it is encrypted and the private key is not availaible then we can't
+ # check the credit card.
+
+ my $check_payinfo = 1;
+
+ if ($self->is_encrypted($self->payinfo)) {
+ $check_payinfo = 0;
+ }
+
+ if ( $check_payinfo && $self->payby =~ /^(CARD|DCRD)$/ ) {
+
+ my $payinfo = $self->payinfo;
+ $payinfo =~ s/\D//g;
+ $payinfo =~ /^(\d{13,16})$/
+ or return gettext('invalid_card'); # . ": ". $self->payinfo;
+ $payinfo = $1;
+ $self->payinfo($payinfo);
+ validate($payinfo)
+ or return gettext('invalid_card'); # . ": ". $self->payinfo;
+
+ return gettext('unknown_card_type')
+ if cardtype($self->payinfo) eq "Unknown";
+
+ my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
+ if ( $ban ) {
+ return 'Banned credit card: banned on '.
+ time2str('%a %h %o at %r', $ban->_date).
+ ' by '. $ban->otaker.
+ ' (ban# '. $ban->bannum. ')';
+ }
+
+ if (length($self->paycvv) && !$self->is_encrypted($self->paycvv)) {
+ if ( cardtype($self->payinfo) eq 'American Express card' ) {
+ $self->paycvv =~ /^(\d{4})$/
+ or return "CVV2 (CID) for American Express cards is four digits.";
+ $self->paycvv($1);
+ } else {
+ $self->paycvv =~ /^(\d{3})$/
+ or return "CVV2 (CVC2/CID) is three digits.";
+ $self->paycvv($1);
+ }
+ } else {
+ $self->paycvv('');
+ }
+
+ my $cardtype = cardtype($payinfo);
+ if ( $cardtype =~ /^(Switch|Solo)$/i ) {
+
+ return "Start date or issue number is required for $cardtype cards"
+ unless $self->paystart_month && $self->paystart_year or $self->payissue;
+
+ return "Start month must be between 1 and 12"
+ if $self->paystart_month
+ and $self->paystart_month < 1 || $self->paystart_month > 12;
+
+ return "Start year must be 1990 or later"
+ if $self->paystart_year
+ and $self->paystart_year < 1990;
+
+ return "Issue number must be beween 1 and 99"
+ if $self->payissue
+ and $self->payissue < 1 || $self->payissue > 99;
+
+ } else {
+ $self->paystart_month('');
+ $self->paystart_year('');
+ $self->payissue('');
+ }
+
+ } elsif ( $check_payinfo && $self->payby =~ /^(CHEK|DCHK)$/ ) {
+
+ my $payinfo = $self->payinfo;
+ $payinfo =~ s/[^\d\@]//g;
+ if ( $conf->exists('echeck-nonus') ) {
+ $payinfo =~ /^(\d+)\@(\d+)$/ or return 'invalid echeck account@aba';
+ $payinfo = "$1\@$2";
+ } else {
+ $payinfo =~ /^(\d+)\@(\d{9})$/ or return 'invalid echeck account@aba';
+ $payinfo = "$1\@$2";
+ }
+ $self->payinfo($payinfo);
+ $self->paycvv('');
+
+ my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
+ if ( $ban ) {
+ return 'Banned ACH account: banned on '.
+ time2str('%a %h %o at %r', $ban->_date).
+ ' by '. $ban->otaker.
+ ' (ban# '. $ban->bannum. ')';
+ }
+
+ } elsif ( $self->payby eq 'LECB' ) {
+
+ my $payinfo = $self->payinfo;
+ $payinfo =~ s/\D//g;
+ $payinfo =~ /^1?(\d{10})$/ or return 'invalid btn billing telephone number';
+ $payinfo = $1;
+ $self->payinfo($payinfo);
+ $self->paycvv('');
+
+ } elsif ( $self->payby eq 'BILL' ) {
+
+ $error = $self->ut_textn('payinfo');
+ return "Illegal P.O. number: ". $self->payinfo if $error;
+ $self->paycvv('');
+
+ } elsif ( $self->payby eq 'COMP' ) {
+
+ my $curuser = $FS::CurrentUser::CurrentUser;
+ if ( ! $self->custnum
+ && ! $curuser->access_right('Complimentary customer')
+ )
+ {
+ return "You are not permitted to create complimentary accounts."
+ }
+
+ $error = $self->ut_textn('payinfo');
+ return "Illegal comp account issuer: ". $self->payinfo if $error;
+ $self->paycvv('');
+
+ } elsif ( $self->payby eq 'PREPAY' ) {
+
+ my $payinfo = $self->payinfo;
+ $payinfo =~ s/\W//g; #anything else would just confuse things
+ $self->payinfo($payinfo);
+ $error = $self->ut_alpha('payinfo');
+ return "Illegal prepayment identifier: ". $self->payinfo if $error;
+ return "Unknown prepayment identifier"
+ unless qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
+ $self->paycvv('');
+
+ }
+
+ if ( $self->paydate eq '' || $self->paydate eq '-' ) {
+ return "Expiration date required"
+ unless $self->payby =~ /^(BILL|PREPAY|CHEK|DCHK|LECB|CASH|WEST|MCRD)$/;
+ $self->paydate('');
+ } else {
+ my( $m, $y );
+ if ( $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/ ) {
+ ( $m, $y ) = ( $1, length($2) == 4 ? $2 : "20$2" );
+ } elsif ( $self->paydate =~ /^(20)?(\d{2})[\/\-](\d{1,2})[\/\-]\d+$/ ) {
+ ( $m, $y ) = ( $3, "20$2" );
+ } else {
+ return "Illegal expiration date: ". $self->paydate;
+ }
+ $self->paydate("$y-$m-01");
+ my($nowm,$nowy)=(localtime(time))[4,5]; $nowm++; $nowy+=1900;
+ return gettext('expired_card')
+ if !$import
+ && !$ignore_expired_card
+ && ( $y<$nowy || ( $y==$nowy && $1<$nowm ) );
+ }
+
+ if ( $self->payname eq '' && $self->payby !~ /^(CHEK|DCHK)$/ &&
+ ( ! $conf->exists('require_cardname')
+ || $self->payby !~ /^(CARD|DCRD)$/ )
+ ) {
+ $self->payname( $self->first. " ". $self->getfield('last') );
+ } else {
+ $self->payname =~ /^([\w \,\.\-\'\&]+)$/
+ or return gettext('illegal_name'). " payname: ". $self->payname;
+ $self->payname($1);
+ }
+
+ foreach my $flag (qw( tax spool_cdr squelch_cdr )) {
+ $self->$flag() =~ /^(Y?)$/ or return "Illegal $flag: ". $self->$flag();
+ $self->$flag($1);
+ }
+
+ $self->otaker(getotaker) unless $self->otaker;
+
+ warn "$me check AFTER: \n". $self->_dump
+ if $DEBUG > 2;
+
+ $self->SUPER::check;
+}
+
+=item addr_fields
+
+Returns a list of fields which have ship_ duplicates.
+
+=cut
+
+sub addr_fields {
+ qw( last first company
+ address1 address2 city county state zip country
+ daytime night fax
+ );
+}
+
+=item has_ship_address
+
+Returns true if this customer record has a separate shipping address.
+
+=cut
+
+sub has_ship_address {
+ my $self = shift;
+ scalar( grep { $self->getfield("ship_$_") ne '' } $self->addr_fields );
+}
+
+=item all_pkgs
+
+Returns all packages (see L<FS::cust_pkg>) for this customer.
+
+=cut
+
+sub all_pkgs {
+ my $self = shift;
+
+ return $self->num_pkgs unless wantarray;
+
+ my @cust_pkg = ();
+ if ( $self->{'_pkgnum'} ) {
+ @cust_pkg = values %{ $self->{'_pkgnum'}->cache };
+ } else {
+ @cust_pkg = qsearch( 'cust_pkg', { 'custnum' => $self->custnum });
+ }
+
+ sort sort_packages @cust_pkg;
+}
+
+=item cust_pkg
+
+Synonym for B<all_pkgs>.
+
+=cut
+
+sub cust_pkg {
+ shift->all_pkgs(@_);
+}
+
+=item cust_location
+
+Returns all locations (see L<FS::cust_location>) for this customer.
+
+=cut
+
+sub cust_location {
+ my $self = shift;
+ qsearch('cust_location', { 'custnum' => $self->custnum } );
+}
+
+=item ncancelled_pkgs
+
+Returns all non-cancelled packages (see L<FS::cust_pkg>) for this customer.
+
+=cut
+
+sub ncancelled_pkgs {
+ my $self = shift;
+
+ return $self->num_ncancelled_pkgs unless wantarray;
+
+ my @cust_pkg = ();
+ if ( $self->{'_pkgnum'} ) {
+
+ warn "$me ncancelled_pkgs: returning cached objects"
+ if $DEBUG > 1;
+
+ @cust_pkg = grep { ! $_->getfield('cancel') }
+ values %{ $self->{'_pkgnum'}->cache };
+
+ } else {
+
+ warn "$me ncancelled_pkgs: searching for packages with custnum ".
+ $self->custnum. "\n"
+ if $DEBUG > 1;
+
+ @cust_pkg =
+ qsearch( 'cust_pkg', {
+ 'custnum' => $self->custnum,
+ 'cancel' => '',
+ });
+ push @cust_pkg,
+ qsearch( 'cust_pkg', {
+ 'custnum' => $self->custnum,
+ 'cancel' => 0,
+ });
+ }
+
+ sort sort_packages @cust_pkg;
+
+}
+
+# This should be generalized to use config options to determine order.
+sub sort_packages {
+ if ( $a->get('cancel') and $b->get('cancel') ) {
+ $a->pkgnum <=> $b->pkgnum;
+ } elsif ( $a->get('cancel') or $b->get('cancel') ) {
+ return -1 if $b->get('cancel');
+ return 1 if $a->get('cancel');
+ return 0;
+ } else {
+ $a->pkgnum <=> $b->pkgnum;
+ }
+}
+
+=item suspended_pkgs
+
+Returns all suspended packages (see L<FS::cust_pkg>) for this customer.
+
+=cut
+
+sub suspended_pkgs {
+ my $self = shift;
+ grep { $_->susp } $self->ncancelled_pkgs;
+}
+
+=item unflagged_suspended_pkgs
+
+Returns all unflagged suspended packages (see L<FS::cust_pkg>) for this
+customer (thouse packages without the `manual_flag' set).
+
+=cut
+
+sub unflagged_suspended_pkgs {
+ my $self = shift;
+ return $self->suspended_pkgs
+ unless dbdef->table('cust_pkg')->column('manual_flag');
+ grep { ! $_->manual_flag } $self->suspended_pkgs;
+}
+
+=item unsuspended_pkgs
+
+Returns all unsuspended (and uncancelled) packages (see L<FS::cust_pkg>) for
+this customer.
+
+=cut
+
+sub unsuspended_pkgs {
+ my $self = shift;
+ grep { ! $_->susp } $self->ncancelled_pkgs;
+}
+
+=item num_cancelled_pkgs
+
+Returns the number of cancelled packages (see L<FS::cust_pkg>) for this
+customer.
+
+=cut
+
+sub num_cancelled_pkgs {
+ shift->num_pkgs("cust_pkg.cancel IS NOT NULL AND cust_pkg.cancel != 0");
+}
+
+sub num_ncancelled_pkgs {
+ shift->num_pkgs("( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )");
+}
+
+sub num_pkgs {
+ my( $self ) = shift;
+ my $sql = scalar(@_) ? shift : '';
+ $sql = "AND $sql" if $sql && $sql !~ /^\s*$/ && $sql !~ /^\s*AND/i;
+ my $sth = dbh->prepare(
+ "SELECT COUNT(*) FROM cust_pkg WHERE custnum = ? $sql"
+ ) or die dbh->errstr;
+ $sth->execute($self->custnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item unsuspend
+
+Unsuspends all unflagged suspended packages (see L</unflagged_suspended_pkgs>
+and L<FS::cust_pkg>) for this customer. Always returns a list: an empty list
+on success or a list of errors.
+
+=cut
+
+sub unsuspend {
+ my $self = shift;
+ grep { $_->unsuspend } $self->suspended_pkgs;
+}
+
+=item suspend
+
+Suspends all unsuspended packages (see L<FS::cust_pkg>) for this customer.
+
+Returns a list: an empty list on success or a list of errors.
+
+=cut
+
+sub suspend {
+ my $self = shift;
+ grep { $_->suspend(@_) } $self->unsuspended_pkgs;
+}
+
+=item suspend_if_pkgpart HASHREF | PKGPART [ , PKGPART ... ]
+
+Suspends all unsuspended packages (see L<FS::cust_pkg>) matching the listed
+PKGPARTs (see L<FS::part_pkg>). Preferred usage is to pass a hashref instead
+of a list of pkgparts; the hashref has the following keys:
+
+=over 4
+
+=item pkgparts - listref of pkgparts
+
+=item (other options are passed to the suspend method)
+
+=back
+
+
+Returns a list: an empty list on success or a list of errors.
+
+=cut
+
+sub suspend_if_pkgpart {
+ my $self = shift;
+ my (@pkgparts, %opt);
+ if (ref($_[0]) eq 'HASH'){
+ @pkgparts = @{$_[0]{pkgparts}};
+ %opt = %{$_[0]};
+ }else{
+ @pkgparts = @_;
+ }
+ grep { $_->suspend(%opt) }
+ grep { my $pkgpart = $_->pkgpart; grep { $pkgpart eq $_ } @pkgparts }
+ $self->unsuspended_pkgs;
+}
+
+=item suspend_unless_pkgpart HASHREF | PKGPART [ , PKGPART ... ]
+
+Suspends all unsuspended packages (see L<FS::cust_pkg>) unless they match the
+given PKGPARTs (see L<FS::part_pkg>). Preferred usage is to pass a hashref
+instead of a list of pkgparts; the hashref has the following keys:
+
+=over 4
+
+=item pkgparts - listref of pkgparts
+
+=item (other options are passed to the suspend method)
+
+=back
+
+Returns a list: an empty list on success or a list of errors.
+
+=cut
+
+sub suspend_unless_pkgpart {
+ my $self = shift;
+ my (@pkgparts, %opt);
+ if (ref($_[0]) eq 'HASH'){
+ @pkgparts = @{$_[0]{pkgparts}};
+ %opt = %{$_[0]};
+ }else{
+ @pkgparts = @_;
+ }
+ grep { $_->suspend(%opt) }
+ grep { my $pkgpart = $_->pkgpart; ! grep { $pkgpart eq $_ } @pkgparts }
+ $self->unsuspended_pkgs;
+}
+
+=item cancel [ OPTION => VALUE ... ]
+
+Cancels all uncancelled packages (see L<FS::cust_pkg>) for this customer.
+
+Available options are:
+
+=over 4
+
+=item quiet - can be set true to supress email cancellation notices.
+
+=item reason - can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason.
+
+=item ban - can be set true to ban this customer's credit card or ACH information, if present.
+
+=back
+
+Always returns a list: an empty list on success or a list of errors.
+
+=cut
+
+sub cancel {
+ my( $self, %opt ) = @_;
+
+ warn "$me cancel called on customer ". $self->custnum. " with options ".
+ join(', ', map { "$_: $opt{$_}" } keys %opt ). "\n"
+ if $DEBUG;
+
+ return ( 'access denied' )
+ unless $FS::CurrentUser::CurrentUser->access_right('Cancel customer');
+
+ if ( $opt{'ban'} && $self->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/ ) {
+
+ #should try decryption (we might have the private key)
+ # and if not maybe queue a job for the server that does?
+ return ( "Can't (yet) ban encrypted credit cards" )
+ if $self->is_encrypted($self->payinfo);
+
+ my $ban = new FS::banned_pay $self->_banned_pay_hashref;
+ my $error = $ban->insert;
+ return ( $error ) if $error;
+
+ }
+
+ my @pkgs = $self->ncancelled_pkgs;
+
+ warn "$me cancelling ". scalar($self->ncancelled_pkgs). "/".
+ scalar(@pkgs). " packages for customer ". $self->custnum. "\n"
+ if $DEBUG;
+
+ grep { $_ } map { $_->cancel(%opt) } $self->ncancelled_pkgs;
+}
+
+sub _banned_pay_hashref {
+ my $self = shift;
+
+ my %payby2ban = (
+ 'CARD' => 'CARD',
+ 'DCRD' => 'CARD',
+ 'CHEK' => 'CHEK',
+ 'DCHK' => 'CHEK'
+ );
+
+ {
+ 'payby' => $payby2ban{$self->payby},
+ 'payinfo' => md5_base64($self->payinfo),
+ #don't ever *search* on reason! #'reason' =>
+ };
+}
+
+=item notes
+
+Returns all notes (see L<FS::cust_main_note>) for this customer.
+
+=cut
+
+sub notes {
+ my $self = shift;
+ #order by?
+ qsearch( 'cust_main_note',
+ { 'custnum' => $self->custnum },
+ '',
+ 'ORDER BY _DATE DESC'
+ );
+}
+
+=item agent
+
+Returns the agent (see L<FS::agent>) for this customer.
+
+=cut
+
+sub agent {
+ my $self = shift;
+ qsearchs( 'agent', { 'agentnum' => $self->agentnum } );
+}
+
+=item bill_and_collect
+
+Cancels and suspends any packages due, generates bills, applies payments and
+cred
+
+Warns on errors (Does not currently: If there is an error, returns the error, otherwise returns false.)
+
+Options are passed as name-value pairs. Currently available options are:
+
+=over 4
+
+=item time
+
+Bills the customer as if it were that time. Specified as a UNIX timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion functions. For example:
+
+ use Date::Parse;
+ ...
+ $cust_main->bill( 'time' => str2time('April 20th, 2001') );
+
+=item invoice_time
+
+Used in conjunction with the I<time> option, this option specifies the date of for the generated invoices. Other calculations, such as whether or not to generate the invoice in the first place, are not affected.
+
+=item check_freq
+
+"1d" for the traditional, daily events (the default), or "1m" for the new monthly events (part_event.check_freq)
+
+=item resetup
+
+If set true, re-charges setup fees.
+
+=item debug
+
+Debugging level. Default is 0 (no debugging), or can be set to 1 (passed-in options), 2 (traces progress), 3 (more information), or 4 (include full search queries)
+
+=back
+
+=cut
+
+sub bill_and_collect {
+ my( $self, %options ) = @_;
+
+ ###
+ # cancel packages
+ ###
+
+ #$options{actual_time} not $options{time} because freeside-daily -d is for
+ #pre-printing invoices
+ my @cancel_pkgs = grep { $_->expire && $_->expire <= $options{actual_time} }
+ $self->ncancelled_pkgs;
+
+ foreach my $cust_pkg ( @cancel_pkgs ) {
+ my $cpr = $cust_pkg->last_cust_pkg_reason('expire');
+ my $error = $cust_pkg->cancel($cpr ? ( 'reason' => $cpr->reasonnum,
+ 'reason_otaker' => $cpr->otaker
+ )
+ : ()
+ );
+ warn "Error cancelling expired pkg ". $cust_pkg->pkgnum.
+ " for custnum ". $self->custnum. ": $error"
+ if $error;
+ }
+
+ ###
+ # suspend packages
+ ###
+
+ #$options{actual_time} not $options{time} because freeside-daily -d is for
+ #pre-printing invoices
+ my @susp_pkgs =
+ grep { ! $_->susp
+ && ( ( $_->part_pkg->is_prepaid
+ && $_->bill
+ && $_->bill < $options{actual_time}
+ )
+ || ( $_->adjourn
+ && $_->adjourn <= $options{actual_time}
+ )
+ )
+ }
+ $self->ncancelled_pkgs;
+
+ foreach my $cust_pkg ( @susp_pkgs ) {
+ my $cpr = $cust_pkg->last_cust_pkg_reason('adjourn')
+ if ($cust_pkg->adjourn && $cust_pkg->adjourn < $^T);
+ my $error = $cust_pkg->suspend($cpr ? ( 'reason' => $cpr->reasonnum,
+ 'reason_otaker' => $cpr->otaker
+ )
+ : ()
+ );
+
+ warn "Error suspending package ". $cust_pkg->pkgnum.
+ " for custnum ". $self->custnum. ": $error"
+ if $error;
+ }
+
+ ###
+ # bill and collect
+ ###
+
+ my $error = $self->bill( %options );
+ warn "Error billing, custnum ". $self->custnum. ": $error" if $error;
+
+ $self->apply_payments_and_credits;
+
+ $error = $self->collect( %options );
+ warn "Error collecting, custnum". $self->custnum. ": $error" if $error;
+
+}
+
+=item bill OPTIONS
+
+Generates invoices (see L<FS::cust_bill>) for this customer. Usually used in
+conjunction with the collect method by calling B<bill_and_collect>.
+
+If there is an error, returns the error, otherwise returns false.
+
+Options are passed as name-value pairs. Currently available options are:
+
+=over 4
+
+=item resetup
+
+If set true, re-charges setup fees.
+
+=item time
+
+Bills the customer as if it were that time. Specified as a UNIX timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion functions. For example:
+
+ use Date::Parse;
+ ...
+ $cust_main->bill( 'time' => str2time('April 20th, 2001') );
+
+=item pkg_list
+
+An array ref of specific packages (objects) to attempt billing, instead trying all of them.
+
+ $cust_main->bill( pkg_list => [$pkg1, $pkg2] );
+
+=item invoice_time
+
+Used in conjunction with the I<time> option, this option specifies the date of for the generated invoices. Other calculations, such as whether or not to generate the invoice in the first place, are not affected.
+
+=back
+
+=cut
+
+sub bill {
+ my( $self, %options ) = @_;
+ return '' if $self->payby eq 'COMP';
+ warn "$me bill customer ". $self->custnum. "\n"
+ if $DEBUG;
+
+ my $time = $options{'time'} || time;
+ my $invoice_time = $options{'invoice_time'} || $time;
+
+ #put below somehow?
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update; #mutex
+
+ my @cust_bill_pkg = ();
+
+ ###
+ # find the packages which are due for billing, find out how much they are
+ # & generate invoice database.
+ ###
+
+ my( $total_setup, $total_recur, $postal_charge ) = ( 0, 0, 0 );
+ my %taxlisthash;
+ my @precommit_hooks = ();
+
+ my @cust_pkgs = qsearch('cust_pkg', { 'custnum' => $self->custnum } );
+ foreach my $cust_pkg (@cust_pkgs) {
+
+ #NO!! next if $cust_pkg->cancel;
+ next if $cust_pkg->getfield('cancel');
+
+ warn " bill package ". $cust_pkg->pkgnum. "\n" if $DEBUG > 1;
+
+ #? to avoid use of uninitialized value errors... ?
+ $cust_pkg->setfield('bill', '')
+ unless defined($cust_pkg->bill);
+
+ #my $part_pkg = $cust_pkg->part_pkg;
+
+ my $real_pkgpart = $cust_pkg->pkgpart;
+ my %hash = $cust_pkg->hash;
+
+ foreach my $part_pkg ( $cust_pkg->part_pkg->self_and_bill_linked ) {
+
+ $cust_pkg->set($_, $hash{$_}) foreach qw ( setup last_bill bill );
+
+ my $error =
+ $self->_make_lines( 'part_pkg' => $part_pkg,
+ 'cust_pkg' => $cust_pkg,
+ 'precommit_hooks' => \@precommit_hooks,
+ 'line_items' => \@cust_bill_pkg,
+ 'setup' => \$total_setup,
+ 'recur' => \$total_recur,
+ 'tax_matrix' => \%taxlisthash,
+ 'time' => $time,
+ 'options' => \%options,
+ );
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ } #foreach my $part_pkg
+
+ } #foreach my $cust_pkg
+
+ unless ( @cust_bill_pkg ) { #don't create an invoice w/o line items
+ #but do commit any package date cycling that happened
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return '';
+ }
+
+ my $postal_pkg = $self->charge_postal_fee();
+ if ( $postal_pkg && !ref( $postal_pkg ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't charge postal invoice fee for customer ".
+ $self->custnum. ": $postal_pkg";
+ }
+ if ( $postal_pkg &&
+ ( scalar( grep { $_->recur && $_->recur > 0 } @cust_bill_pkg) ||
+ !$conf->exists('postal_invoice-recurring_only')
+ )
+ )
+ {
+ foreach my $part_pkg ( $postal_pkg->part_pkg->self_and_bill_linked ) {
+ my $error =
+ $self->_make_lines( 'part_pkg' => $part_pkg,
+ 'cust_pkg' => $postal_pkg,
+ 'precommit_hooks' => \@precommit_hooks,
+ 'line_items' => \@cust_bill_pkg,
+ 'setup' => \$total_setup,
+ 'recur' => \$total_recur,
+ 'tax_matrix' => \%taxlisthash,
+ 'time' => $time,
+ 'options' => \%options,
+ );
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ warn "having a look at the taxes we found...\n" if $DEBUG > 2;
+
+ # keys are tax names (as printed on invoices / itemdesc )
+ # values are listrefs of taxlisthash keys (internal identifiers)
+ my %taxname = ();
+
+ # keys are taxlisthash keys (internal identifiers)
+ # values are (cumulative) amounts
+ my %tax = ();
+
+ # keys are taxlisthash keys (internal identifiers)
+ # values are listrefs of cust_bill_pkg_tax_location hashrefs
+ my %tax_location = ();
+
+ foreach my $tax ( keys %taxlisthash ) {
+ my $tax_object = shift @{ $taxlisthash{$tax} };
+ warn "found ". $tax_object->taxname. " as $tax\n" if $DEBUG > 2;
+ my $hashref_or_error =
+ $tax_object->taxline( $taxlisthash{$tax},
+ 'custnum' => $self->custnum,
+ 'invoice_time' => $invoice_time
+ );
+ unless ( ref($hashref_or_error) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $hashref_or_error;
+ }
+ unshift @{ $taxlisthash{$tax} }, $tax_object;
+
+ my $name = $hashref_or_error->{'name'};
+ my $amount = $hashref_or_error->{'amount'};
+
+ #warn "adding $amount as $name\n";
+ $taxname{ $name } ||= [];
+ push @{ $taxname{ $name } }, $tax;
+
+ $tax{ $tax } += $amount;
+
+ $tax_location{ $tax } ||= [];
+ if ( $tax_object->get('pkgnum') || $tax_object->get('locationnum') ) {
+ push @{ $tax_location{ $tax } },
+ {
+ 'taxnum' => $tax_object->taxnum,
+ 'taxtype' => ref($tax_object),
+ 'pkgnum' => $tax_object->get('pkgnum'),
+ 'locationnum' => $tax_object->get('locationnum'),
+ 'amount' => sprintf('%.2f', $amount ),
+ };
+ }
+
+ }
+
+ #move the cust_tax_exempt_pkg records to the cust_bill_pkgs we will commit
+ my %packagemap = map { $_->pkgnum => $_ } @cust_bill_pkg;
+ foreach my $tax ( keys %taxlisthash ) {
+ foreach ( @{ $taxlisthash{$tax} }[1 ... scalar(@{ $taxlisthash{$tax} })] ) {
+ next unless ref($_) eq 'FS::cust_bill_pkg'; # shouldn't happen
+
+ push @{ $packagemap{$_->pkgnum}->_cust_tax_exempt_pkg },
+ splice( @{ $_->_cust_tax_exempt_pkg } );
+ }
+ }
+
+ #some taxes are taxed
+ my %totlisthash;
+
+ warn "finding taxed taxes...\n" if $DEBUG > 2;
+ foreach my $tax ( keys %taxlisthash ) {
+ my $tax_object = shift @{ $taxlisthash{$tax} };
+ warn "found possible taxed tax ". $tax_object->taxname. " we call $tax\n"
+ if $DEBUG > 2;
+ next unless $tax_object->can('tax_on_tax');
+
+ foreach my $tot ( $tax_object->tax_on_tax( $self ) ) {
+ my $totname = ref( $tot ). ' '. $tot->taxnum;
+
+ warn "checking $totname which we call ". $tot->taxname. " as applicable\n"
+ if $DEBUG > 2;
+ next unless exists( $taxlisthash{ $totname } ); # only increase
+ # existing taxes
+ warn "adding $totname to taxed taxes\n" if $DEBUG > 2;
+ if ( exists( $totlisthash{ $totname } ) ) {
+ push @{ $totlisthash{ $totname } }, $tax{ $tax };
+ }else{
+ $totlisthash{ $totname } = [ $tot, $tax{ $tax } ];
+ }
+ }
+ }
+
+ warn "having a look at taxed taxes...\n" if $DEBUG > 2;
+ foreach my $tax ( keys %totlisthash ) {
+ my $tax_object = shift @{ $totlisthash{$tax} };
+ warn "found previously found taxed tax ". $tax_object->taxname. "\n"
+ if $DEBUG > 2;
+ my $listref_or_error =
+ $tax_object->taxline( $totlisthash{$tax},
+ 'custnum' => $self->custnum,
+ 'invoice_time' => $invoice_time
+ );
+ unless (ref($listref_or_error)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $listref_or_error;
+ }
+
+ warn "adding taxed tax amount ". $listref_or_error->[1].
+ " as ". $tax_object->taxname. "\n"
+ if $DEBUG;
+ $tax{ $tax } += $listref_or_error->[1];
+ }
+
+ #consolidate and create tax line items
+ warn "consolidating and generating...\n" if $DEBUG > 2;
+ foreach my $taxname ( keys %taxname ) {
+ my $tax = 0;
+ my %seen = ();
+ my @cust_bill_pkg_tax_location = ();
+ warn "adding $taxname\n" if $DEBUG > 1;
+ foreach my $taxitem ( @{ $taxname{$taxname} } ) {
+ next if $seen{$taxitem}++;
+ warn "adding $tax{$taxitem}\n" if $DEBUG > 1;
+ $tax += $tax{$taxitem};
+ push @cust_bill_pkg_tax_location,
+ map { new FS::cust_bill_pkg_tax_location $_ }
+ @{ $tax_location{ $taxitem } };
+ }
+ next unless $tax;
+
+ $tax = sprintf('%.2f', $tax );
+ $total_setup = sprintf('%.2f', $total_setup+$tax );
+
+ push @cust_bill_pkg, new FS::cust_bill_pkg {
+ 'pkgnum' => 0,
+ 'setup' => $tax,
+ 'recur' => 0,
+ 'sdate' => '',
+ 'edate' => '',
+ 'itemdesc' => $taxname,
+ 'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location,
+ };
+
+ }
+
+ my $charged = sprintf('%.2f', $total_setup + $total_recur );
+
+ #create the new invoice
+ my $cust_bill = new FS::cust_bill ( {
+ 'custnum' => $self->custnum,
+ '_date' => ( $invoice_time ),
+ 'charged' => $charged,
+ } );
+ my $error = $cust_bill->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't create invoice for customer #". $self->custnum. ": $error";
+ }
+
+ foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
+ $cust_bill_pkg->invnum($cust_bill->invnum);
+ my $error = $cust_bill_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't create invoice line item: $error";
+ }
+ }
+
+
+ foreach my $hook ( @precommit_hooks ) {
+ eval {
+ &{$hook}; #($self) ?
+ };
+ if ( $@ ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "$@ running precommit hook $hook\n";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+}
+
+
+sub _make_lines {
+ my ($self, %params) = @_;
+
+ my $part_pkg = $params{part_pkg} or die "no part_pkg specified";
+ my $cust_pkg = $params{cust_pkg} or die "no cust_pkg specified";
+ my $precommit_hooks = $params{precommit_hooks} or die "no package specified";
+ my $cust_bill_pkgs = $params{line_items} or die "no line buffer specified";
+ my $total_setup = $params{setup} or die "no setup accumulator specified";
+ my $total_recur = $params{recur} or die "no recur accumulator specified";
+ my $taxlisthash = $params{tax_matrix} or die "no tax accumulator specified";
+ my $time = $params{'time'} or die "no time specified";
+ my (%options) = %{$params{options}}; #hmmm only for 'resetup'
+
+ my $dbh = dbh;
+ my $real_pkgpart = $cust_pkg->pkgpart;
+ my %hash = $cust_pkg->hash;
+ my $old_cust_pkg = new FS::cust_pkg \%hash;
+
+ my @details = ();
+
+ my $lineitems = 0;
+
+ $cust_pkg->pkgpart($part_pkg->pkgpart);
+
+ ###
+ # bill setup
+ ###
+
+ my $setup = 0;
+ my $unitsetup = 0;
+ if ( ! $cust_pkg->setup &&
+ (
+ ( $conf->exists('disable_setup_suspended_pkgs') &&
+ ! $cust_pkg->getfield('susp')
+ ) || ! $conf->exists('disable_setup_suspended_pkgs')
+ )
+ || $options{'resetup'}
+ ) {
+
+ warn " bill setup\n" if $DEBUG > 1;
+ $lineitems++;
+
+ $setup = eval { $cust_pkg->calc_setup( $time, \@details ) };
+ return "$@ running calc_setup for $cust_pkg\n"
+ if $@;
+
+ $unitsetup = $cust_pkg->part_pkg->unit_setup || $setup; #XXX uuh
+
+ $cust_pkg->setfield('setup', $time)
+ unless $cust_pkg->setup;
+ #do need it, but it won't get written to the db
+ #|| $cust_pkg->pkgpart != $real_pkgpart;
+
+ }
+
+ ###
+ # bill recurring fee
+ ###
+
+ #XXX unit stuff here too
+ my $recur = 0;
+ my $unitrecur = 0;
+ my $sdate;
+ if ( ! $cust_pkg->getfield('susp') and
+ ( $part_pkg->getfield('freq') ne '0' &&
+ ( $cust_pkg->getfield('bill') || 0 ) <= $time
+ )
+ || ( $part_pkg->plan eq 'voip_cdr'
+ && $part_pkg->option('bill_every_call')
+ )
+ ) {
+
+ # XXX should this be a package event? probably. events are called
+ # at collection time at the moment, though...
+ $part_pkg->reset_usage($cust_pkg, 'debug'=>$DEBUG)
+ if $part_pkg->can('reset_usage');
+ #don't want to reset usage just cause we want a line item??
+ #&& $part_pkg->pkgpart == $real_pkgpart;
+
+ warn " bill recur\n" if $DEBUG > 1;
+ $lineitems++;
+
+ # XXX shared with $recur_prog
+ $sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
+
+ #over two params! lets at least switch to a hashref for the rest...
+ my $increment_next_bill = ( $part_pkg->freq ne '0'
+ && ( $cust_pkg->getfield('bill') || 0 ) <= $time
+ );
+ my %param = ( 'precommit_hooks' => $precommit_hooks,
+ 'increment_next_bill' => $increment_next_bill,
+ );
+
+ $recur = eval { $cust_pkg->calc_recur( \$sdate, \@details, \%param ) };
+ return "$@ running calc_recur for $cust_pkg\n"
+ if ( $@ );
+
+ if ( $increment_next_bill ) {
+
+ my $next_bill = $part_pkg->add_freq($sdate);
+ return "unparsable frequency: ". $part_pkg->freq
+ if $next_bill == -1;
+
+ #pro-rating magic - if $recur_prog fiddled $sdate, want to use that
+ # only for figuring next bill date, nothing else, so, reset $sdate again
+ # here
+ $sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
+ #no need, its in $hash{last_bill}# my $last_bill = $cust_pkg->last_bill;
+ $cust_pkg->last_bill($sdate);
+
+ $cust_pkg->setfield('bill', $next_bill );
+
+ }
+
+ }
+
+ warn "\$setup is undefined" unless defined($setup);
+ warn "\$recur is undefined" unless defined($recur);
+ warn "\$cust_pkg->bill is undefined" unless defined($cust_pkg->bill);
+
+ ###
+ # If there's line items, create em cust_bill_pkg records
+ # If $cust_pkg has been modified, update it (if we're a real pkgpart)
+ ###
+
+ if ( $lineitems ) {
+
+ if ( $cust_pkg->modified && $cust_pkg->pkgpart == $real_pkgpart ) {
+ # hmm.. and if just the options are modified in some weird price plan?
+
+ warn " package ". $cust_pkg->pkgnum. " modified; updating\n"
+ if $DEBUG >1;
+
+ my $error = $cust_pkg->replace( $old_cust_pkg,
+ 'options' => { $cust_pkg->options },
+ );
+ return "Error modifying pkgnum ". $cust_pkg->pkgnum. ": $error"
+ if $error; #just in case
+ }
+
+ $setup = sprintf( "%.2f", $setup );
+ $recur = sprintf( "%.2f", $recur );
+ if ( $setup < 0 && ! $conf->exists('allow_negative_charges') ) {
+ return "negative setup $setup for pkgnum ". $cust_pkg->pkgnum;
+ }
+ if ( $recur < 0 && ! $conf->exists('allow_negative_charges') ) {
+ return "negative recur $recur for pkgnum ". $cust_pkg->pkgnum;
+ }
+
+ if ( $setup != 0 || $recur != 0 ) {
+
+ warn " charges (setup=$setup, recur=$recur); adding line items\n"
+ if $DEBUG > 1;
+
+ my @cust_pkg_detail = map { $_->detail } $cust_pkg->cust_pkg_detail('I');
+ if ( $DEBUG > 1 ) {
+ warn " adding customer package invoice detail: $_\n"
+ foreach @cust_pkg_detail;
+ }
+ push @details, @cust_pkg_detail;
+
+ my $cust_bill_pkg = new FS::cust_bill_pkg {
+ 'pkgnum' => $cust_pkg->pkgnum,
+ 'setup' => $setup,
+ 'unitsetup' => $unitsetup,
+ 'recur' => $recur,
+ 'unitrecur' => $unitrecur,
+ 'quantity' => $cust_pkg->quantity,
+ 'details' => \@details,
+ };
+
+ if ( $part_pkg->option('recur_temporality', 1) eq 'preceding' ) {
+ $cust_bill_pkg->sdate( $hash{last_bill} );
+ $cust_bill_pkg->edate( $sdate - 86399 ); #60s*60m*24h-1
+ } else { #if ( $part_pkg->option('recur_temporality', 1) eq 'upcoming' ) {
+ $cust_bill_pkg->sdate( $sdate );
+ $cust_bill_pkg->edate( $cust_pkg->bill );
+ }
+
+ $cust_bill_pkg->pkgpart_override($part_pkg->pkgpart)
+ unless $part_pkg->pkgpart == $real_pkgpart;
+
+ $$total_setup += $setup;
+ $$total_recur += $recur;
+
+ ###
+ # handle taxes
+ ###
+
+ my $error =
+ $self->_handle_taxes($part_pkg, $taxlisthash, $cust_bill_pkg, $cust_pkg);
+ return $error if $error;
+
+ push @$cust_bill_pkgs, $cust_bill_pkg;
+
+ } #if $setup != 0 || $recur != 0
+
+ } #if $line_items
+
+ '';
+
+}
+
+sub _handle_taxes {
+ my $self = shift;
+ my $part_pkg = shift;
+ my $taxlisthash = shift;
+ my $cust_bill_pkg = shift;
+ my $cust_pkg = shift;
+
+ my %cust_bill_pkg = ();
+ my %taxes = ();
+
+ my @classes;
+ #push @classes, $cust_bill_pkg->usage_classes if $cust_bill_pkg->type eq 'U';
+ push @classes, $cust_bill_pkg->usage_classes if $cust_bill_pkg->usage;
+ push @classes, 'setup' if $cust_bill_pkg->setup;
+ push @classes, 'recur' if $cust_bill_pkg->recur;
+
+ if ( $self->tax !~ /Y/i && $self->payby ne 'COMP' ) {
+
+ if ( $conf->exists('enable_taxproducts')
+ && ( scalar($part_pkg->part_pkg_taxoverride)
+ || $part_pkg->has_taxproduct
+ )
+ )
+ {
+
+ if ( $conf->exists('tax-pkg_address') && $cust_pkg->locationnum ) {
+ return "fatal: Can't (yet) use tax-pkg_address with taxproducts";
+ }
+
+ foreach my $class (@classes) {
+ my $err_or_ref = $self->_gather_taxes( $part_pkg, $class );
+ return $err_or_ref unless ref($err_or_ref);
+ $taxes{$class} = $err_or_ref;
+ }
+
+ unless (exists $taxes{''}) {
+ my $err_or_ref = $self->_gather_taxes( $part_pkg, '' );
+ return $err_or_ref unless ref($err_or_ref);
+ $taxes{''} = $err_or_ref;
+ }
+
+ } else {
+
+ my @loc_keys = qw( state county country );
+ my %taxhash;
+ if ( $conf->exists('tax-pkg_address') && $cust_pkg->locationnum ) {
+ my $cust_location = $cust_pkg->cust_location;
+ %taxhash = map { $_ => $cust_location->$_() } @loc_keys;
+ } else {
+ my $prefix =
+ ( $conf->exists('tax-ship_address') && length($self->ship_last) )
+ ? 'ship_'
+ : '';
+ %taxhash = map { $_ => $self->get("$prefix$_") } @loc_keys;
+ }
+
+ $taxhash{'taxclass'} = $part_pkg->taxclass;
+
+ my @taxes = qsearch( 'cust_main_county', \%taxhash );
+
+ my %taxhash_elim = %taxhash;
+
+ my @elim = qw( taxclass county state );
+ while ( !scalar(@taxes) && scalar(@elim) ) {
+ $taxhash_elim{ shift(@elim) } = '';
+ @taxes = qsearch( 'cust_main_county', \%taxhash_elim );
+ }
+
+ if ( $conf->exists('tax-pkg_address') && $cust_pkg->locationnum ) {
+ foreach (@taxes) {
+ $_->set('pkgnum', $cust_pkg->pkgnum );
+ $_->set('locationnum', $cust_pkg->locationnum );
+ }
+ }
+
+ $taxes{''} = [ @taxes ];
+ $taxes{'setup'} = [ @taxes ];
+ $taxes{'recur'} = [ @taxes ];
+ $taxes{$_} = [ @taxes ] foreach (@classes);
+
+ # maybe eliminate this entirely, along with all the 0% records
+ unless ( @taxes ) {
+ return
+ "fatal: can't find tax rate for state/county/country/taxclass ".
+ join('/', map $taxhash{$_}, qw(state county country taxclass) );
+ }
+
+ } #if $conf->exists('enable_taxproducts') ...
+
+ }
+
+ my @display = ();
+ if ( $conf->exists('separate_usage') ) {
+ my $section = $cust_pkg->part_pkg->option('usage_section', 'Hush!');
+ my $summary = $cust_pkg->part_pkg->option('summarize_usage', 'Hush!');
+ push @display, new FS::cust_bill_pkg_display { type => 'S' };
+ push @display, new FS::cust_bill_pkg_display { type => 'R' };
+ push @display, new FS::cust_bill_pkg_display { type => 'U',
+ section => $section
+ };
+ if ($section && $summary) {
+ $display[2]->post_total('Y');
+ push @display, new FS::cust_bill_pkg_display { type => 'U',
+ summary => 'Y',
+ }
+ }
+ }
+ $cust_bill_pkg->set('display', \@display);
+
+ my %tax_cust_bill_pkg = $cust_bill_pkg->disintegrate;
+ foreach my $key (keys %tax_cust_bill_pkg) {
+ my @taxes = @{ $taxes{$key} || [] };
+ my $tax_cust_bill_pkg = $tax_cust_bill_pkg{$key};
+
+ foreach my $tax ( @taxes ) {
+
+ my $taxname = ref( $tax ). ' taxnum'. $tax->taxnum;
+# $taxname .= ' pkgnum'. $cust_pkg->pkgnum.
+# ' locationnum'. $cust_pkg->locationnum
+# if $conf->exists('tax-pkg_address') && $cust_pkg->locationnum;
+
+ if ( exists( $taxlisthash->{ $taxname } ) ) {
+ push @{ $taxlisthash->{ $taxname } }, $tax_cust_bill_pkg;
+ }else{
+ $taxlisthash->{ $taxname } = [ $tax, $tax_cust_bill_pkg ];
+ }
+ }
+ }
+
+ '';
+}
+
+sub _gather_taxes {
+ my $self = shift;
+ my $part_pkg = shift;
+ my $class = shift;
+
+ my @taxes = ();
+ my $geocode = $self->geocode('cch');
+
+ my @taxclassnums = map { $_->taxclassnum }
+ $part_pkg->part_pkg_taxoverride($class);
+
+ unless (@taxclassnums) {
+ @taxclassnums = map { $_->taxclassnum }
+ $part_pkg->part_pkg_taxrate('cch', $geocode, $class);
+ }
+ warn "Found taxclassnum values of ". join(',', @taxclassnums)
+ if $DEBUG;
+
+ my $extra_sql =
+ "AND (".
+ join(' OR ', map { "taxclassnum = $_" } @taxclassnums ). ")";
+
+ @taxes = qsearch({ 'table' => 'tax_rate',
+ 'hashref' => { 'geocode' => $geocode, },
+ 'extra_sql' => $extra_sql,
+ })
+ if scalar(@taxclassnums);
+
+ warn "Found taxes ".
+ join(',', map{ ref($_). " ". $_->get($_->primary_key) } @taxes). "\n"
+ if $DEBUG;
+
+ [ @taxes ];
+
+}
+
+=item collect OPTIONS
+
+(Attempt to) collect money for this customer's outstanding invoices (see
+L<FS::cust_bill>). Usually used after the bill method.
+
+Actions are now triggered by billing events; see L<FS::part_event> and the
+billing events web interface. Old-style invoice events (see
+L<FS::part_bill_event>) have been deprecated.
+
+If there is an error, returns the error, otherwise returns false.
+
+Options are passed as name-value pairs.
+
+Currently available options are:
+
+=over 4
+
+=item invoice_time
+
+Use this time when deciding when to print invoices and late notices on those invoices. The default is now. It is specified as a UNIX timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item retry
+
+Retry card/echeck/LEC transactions even when not scheduled by invoice events.
+
+=item quiet
+
+set true to surpress email card/ACH decline notices.
+
+=item check_freq
+
+"1d" for the traditional, daily events (the default), or "1m" for the new monthly events (part_event.check_freq)
+
+=item payby
+
+allows for one time override of normal customer billing method
+
+=item debug
+
+Debugging level. Default is 0 (no debugging), or can be set to 1 (passed-in options), 2 (traces progress), 3 (more information), or 4 (include full search queries)
+
+
+=back
+
+=cut
+
+sub collect {
+ my( $self, %options ) = @_;
+ my $invoice_time = $options{'invoice_time'} || time;
+
+ #put below somehow?
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update; #mutex
+
+ if ( $DEBUG ) {
+ my $balance = $self->balance;
+ warn "$me collect customer ". $self->custnum. ": balance $balance\n"
+ }
+
+ if ( exists($options{'retry_card'}) ) {
+ carp 'retry_card option passed to collect is deprecated; use retry';
+ $options{'retry'} ||= $options{'retry_card'};
+ }
+ if ( exists($options{'retry'}) && $options{'retry'} ) {
+ my $error = $self->retry_realtime;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ # false laziness w/pay_batch::import_results
+
+ my $due_cust_event = $self->due_cust_event(
+ 'debug' => ( $options{'debug'} || 0 ),
+ 'time' => $invoice_time,
+ 'check_freq' => $options{'check_freq'},
+ );
+ unless( ref($due_cust_event) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $due_cust_event;
+ }
+
+ foreach my $cust_event ( @$due_cust_event ) {
+
+ #XXX lock event
+
+ #re-eval event conditions (a previous event could have changed things)
+ unless ( $cust_event->test_conditions( 'time' => $invoice_time ) ) {
+ #don't leave stray "new/locked" records around
+ my $error = $cust_event->delete;
+ if ( $error ) {
+ #gah, even with transactions
+ $dbh->commit if $oldAutoCommit; #well.
+ return $error;
+ }
+ next;
+ }
+
+ {
+ local $realtime_bop_decline_quiet = 1 if $options{'quiet'};
+ warn " running cust_event ". $cust_event->eventnum. "\n"
+ if $DEBUG > 1;
+
+
+ #if ( my $error = $cust_event->do_event(%options) ) { #XXX %options?
+ if ( my $error = $cust_event->do_event() ) {
+ #XXX wtf is this? figure out a proper dealio with return value
+ #from do_event
+ # gah, even with transactions.
+ $dbh->commit if $oldAutoCommit; #well.
+ return $error;
+ }
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item due_cust_event [ HASHREF | OPTION => VALUE ... ]
+
+Inserts database records for and returns an ordered listref of new events due
+for this customer, as FS::cust_event objects (see L<FS::cust_event>). If no
+events are due, an empty listref is returned. If there is an error, returns a
+scalar error message.
+
+To actually run the events, call each event's test_condition method, and if
+still true, call the event's do_event method.
+
+Options are passed as a hashref or as a list of name-value pairs. Available
+options are:
+
+=over 4
+
+=item check_freq
+
+Search only for events of this check frequency (how often events of this type are checked); currently "1d" (daily, the default) and "1m" (monthly) are recognized.
+
+=item time
+
+"Current time" for the events.
+
+=item debug
+
+Debugging level. Default is 0 (no debugging), or can be set to 1 (passed-in options), 2 (traces progress), 3 (more information), or 4 (include full search queries)
+
+=item eventtable
+
+Only return events for the specified eventtable (by default, events of all eventtables are returned)
+
+=item objects
+
+Explicitly pass the objects to be tested (typically used with eventtable).
+
+=item testonly
+
+Set to true to return the objects, but not actually insert them into the
+database.
+
+=back
+
+=cut
+
+sub due_cust_event {
+ my $self = shift;
+ my %opt = ref($_[0]) ? %{ $_[0] } : @_;
+
+ #???
+ #my $DEBUG = $opt{'debug'}
+ local($DEBUG) = $opt{'debug'}
+ if defined($opt{'debug'}) && $opt{'debug'} > $DEBUG;
+
+ warn "$me due_cust_event called with options ".
+ join(', ', map { "$_: $opt{$_}" } keys %opt). "\n"
+ if $DEBUG;
+
+ $opt{'time'} ||= time;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update #mutex
+ unless $opt{testonly};
+
+ ###
+ # 1: find possible events (initial search)
+ ###
+
+ my @cust_event = ();
+
+ my @eventtable = $opt{'eventtable'}
+ ? ( $opt{'eventtable'} )
+ : FS::part_event->eventtables_runorder;
+
+ foreach my $eventtable ( @eventtable ) {
+
+ my @objects;
+ if ( $opt{'objects'} ) {
+
+ @objects = @{ $opt{'objects'} };
+
+ } else {
+
+ #my @objects = $self->eventtable(); # sub cust_main { @{ [ $self ] }; }
+ @objects = ( $eventtable eq 'cust_main' )
+ ? ( $self )
+ : ( $self->$eventtable() );
+
+ }
+
+ my @e_cust_event = ();
+
+ my $cross = "CROSS JOIN $eventtable";
+ $cross .= ' LEFT JOIN cust_main USING ( custnum )'
+ unless $eventtable eq 'cust_main';
+
+ foreach my $object ( @objects ) {
+
+ #this first search uses the condition_sql magic for optimization.
+ #the more possible events we can eliminate in this step the better
+
+ my $cross_where = '';
+ my $pkey = $object->primary_key;
+ $cross_where = "$eventtable.$pkey = ". $object->$pkey();
+
+ my $join = FS::part_event_condition->join_conditions_sql( $eventtable );
+ my $extra_sql =
+ FS::part_event_condition->where_conditions_sql( $eventtable,
+ 'time'=>$opt{'time'}
+ );
+ my $order = FS::part_event_condition->order_conditions_sql( $eventtable );
+
+ $extra_sql = "AND $extra_sql" if $extra_sql;
+
+ #here is the agent virtualization
+ $extra_sql .= " AND ( part_event.agentnum IS NULL
+ OR part_event.agentnum = ". $self->agentnum. ' )';
+
+ $extra_sql .= " $order";
+
+ warn "searching for events for $eventtable ". $object->$pkey. "\n"
+ if $opt{'debug'} > 2;
+ my @part_event = qsearch( {
+ 'debug' => ( $opt{'debug'} > 3 ? 1 : 0 ),
+ 'select' => 'part_event.*',
+ 'table' => 'part_event',
+ 'addl_from' => "$cross $join",
+ 'hashref' => { 'check_freq' => ( $opt{'check_freq'} || '1d' ),
+ 'eventtable' => $eventtable,
+ 'disabled' => '',
+ },
+ 'extra_sql' => "AND $cross_where $extra_sql",
+ } );
+
+ if ( $DEBUG > 2 ) {
+ my $pkey = $object->primary_key;
+ warn " ". scalar(@part_event).
+ " possible events found for $eventtable ". $object->$pkey(). "\n";
+ }
+
+ push @e_cust_event, map { $_->new_cust_event($object) } @part_event;
+
+ }
+
+ warn " ". scalar(@e_cust_event).
+ " subtotal possible cust events found for $eventtable\n"
+ if $DEBUG > 1;
+
+ push @cust_event, @e_cust_event;
+
+ }
+
+ warn " ". scalar(@cust_event).
+ " total possible cust events found in initial search\n"
+ if $DEBUG; # > 1;
+
+ ##
+ # 2: test conditions
+ ##
+
+ my %unsat = ();
+
+ @cust_event = grep $_->test_conditions( 'time' => $opt{'time'},
+ 'stats_hashref' => \%unsat ),
+ @cust_event;
+
+ warn " ". scalar(@cust_event). " cust events left satisfying conditions\n"
+ if $DEBUG; # > 1;
+
+ warn " invalid conditions not eliminated with condition_sql:\n".
+ join('', map " $_: ".$unsat{$_}."\n", keys %unsat )
+ if $DEBUG; # > 1;
+
+ ##
+ # 3: insert
+ ##
+
+ unless( $opt{testonly} ) {
+ foreach my $cust_event ( @cust_event ) {
+
+ my $error = $cust_event->insert();
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ##
+ # 4: return
+ ##
+
+ warn " returning events: ". Dumper(@cust_event). "\n"
+ if $DEBUG > 2;
+
+ \@cust_event;
+
+}
+
+=item retry_realtime
+
+Schedules realtime / batch credit card / electronic check / LEC billing
+events for for retry. Useful if card information has changed or manual
+retry is desired. The 'collect' method must be called to actually retry
+the transaction.
+
+Implementation details: For either this customer, or for each of this
+customer's open invoices, changes the status of the first "done" (with
+statustext error) realtime processing event to "failed".
+
+=cut
+
+sub retry_realtime {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #a little false laziness w/due_cust_event (not too bad, really)
+
+ my $join = FS::part_event_condition->join_conditions_sql;
+ my $order = FS::part_event_condition->order_conditions_sql;
+ my $mine =
+ '( '
+ . join ( ' OR ' , map {
+ "( part_event.eventtable = " . dbh->quote($_)
+ . " AND tablenum IN( SELECT " . dbdef->table($_)->primary_key . " from $_ where custnum = " . dbh->quote( $self->custnum ) . "))" ;
+ } FS::part_event->eventtables)
+ . ') ';
+
+ #here is the agent virtualization
+ my $agent_virt = " ( part_event.agentnum IS NULL
+ OR part_event.agentnum = ". $self->agentnum. ' )';
+
+ #XXX this shouldn't be hardcoded, actions should declare it...
+ my @realtime_events = qw(
+ cust_bill_realtime_card
+ cust_bill_realtime_check
+ cust_bill_realtime_lec
+ cust_bill_batch
+ );
+
+ my $is_realtime_event = ' ( '. join(' OR ', map "part_event.action = '$_'",
+ @realtime_events
+ ).
+ ' ) ';
+
+ my @cust_event = qsearchs({
+ 'table' => 'cust_event',
+ 'select' => 'cust_event.*',
+ 'addl_from' => "LEFT JOIN part_event USING ( eventpart ) $join",
+ 'hashref' => { 'status' => 'done' },
+ 'extra_sql' => " AND statustext IS NOT NULL AND statustext != '' ".
+ " AND $mine AND $is_realtime_event AND $agent_virt $order" # LIMIT 1"
+ });
+
+ my %seen_invnum = ();
+ foreach my $cust_event (@cust_event) {
+
+ #max one for the customer, one for each open invoice
+ my $cust_X = $cust_event->cust_X;
+ next if $seen_invnum{ $cust_event->part_event->eventtable eq 'cust_bill'
+ ? $cust_X->invnum
+ : 0
+ }++
+ or $cust_event->part_event->eventtable eq 'cust_bill'
+ && ! $cust_X->owed;
+
+ my $error = $cust_event->retry;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error scheduling event for retry: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item realtime_bop METHOD AMOUNT [ OPTION => VALUE ... ]
+
+Runs a realtime credit card, ACH (electronic check) or phone bill transaction
+via a Business::OnlinePayment realtime gateway. See
+L<http://420.am/business-onlinepayment> for supported gateways.
+
+Available methods are: I<CC>, I<ECHECK> and I<LEC>
+
+Available options are: I<description>, I<invnum>, I<quiet>, I<paynum_ref>, I<payunique>
+
+The additional options I<payname>, I<address1>, I<address2>, I<city>, I<state>,
+I<zip>, I<payinfo> and I<paydate> are also available. Any of these options,
+if set, will override the value from the customer record.
+
+I<description> is a free-text field passed to the gateway. It defaults to
+"Internet services".
+
+If an I<invnum> is specified, this payment (if successful) is applied to the
+specified invoice. If you don't specify an I<invnum> you might want to
+call the B<apply_payments> method.
+
+I<quiet> can be set true to surpress email decline notices.
+
+I<paynum_ref> can be set to a scalar reference. It will be filled in with the
+resulting paynum, if any.
+
+I<payunique> is a unique identifier for this payment.
+
+(moved from cust_bill) (probably should get realtime_{card,ach,lec} here too)
+
+=cut
+
+sub realtime_bop {
+ my( $self, $method, $amount, %options ) = @_;
+ if ( $DEBUG ) {
+ warn "$me realtime_bop: $method $amount\n";
+ warn " $_ => $options{$_}\n" foreach keys %options;
+ }
+
+ $options{'description'} ||= 'Internet services';
+
+ return $self->fake_bop($method, $amount, %options) if $options{'fake'};
+
+ eval "use Business::OnlinePayment";
+ die $@ if $@;
+
+ my $payinfo = exists($options{'payinfo'})
+ ? $options{'payinfo'}
+ : $self->payinfo;
+
+ my %method2payby = (
+ 'CC' => 'CARD',
+ 'ECHECK' => 'CHEK',
+ 'LEC' => 'LECB',
+ );
+
+ ###
+ # check for banned credit card/ACH
+ ###
+
+ my $ban = qsearchs('banned_pay', {
+ 'payby' => $method2payby{$method},
+ 'payinfo' => md5_base64($payinfo),
+ } );
+ return "Banned credit card" if $ban;
+
+ ###
+ # select a gateway
+ ###
+
+ my $taxclass = '';
+ if ( $options{'invnum'} ) {
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $options{'invnum'} } );
+ die "invnum ". $options{'invnum'}. " not found" unless $cust_bill;
+ my @taxclasses =
+ map { $_->part_pkg->taxclass }
+ grep { $_ }
+ map { $_->cust_pkg }
+ $cust_bill->cust_bill_pkg;
+ unless ( grep { $taxclasses[0] ne $_ } @taxclasses ) { #unless there are
+ #different taxclasses
+ $taxclass = $taxclasses[0];
+ }
+ }
+
+ #look for an agent gateway override first
+ my $cardtype;
+ if ( $method eq 'CC' ) {
+ $cardtype = cardtype($payinfo);
+ } elsif ( $method eq 'ECHECK' ) {
+ $cardtype = 'ACH';
+ } else {
+ $cardtype = $method;
+ }
+
+ my $override =
+ qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => $cardtype,
+ taxclass => $taxclass, } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => '',
+ taxclass => $taxclass, } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => $cardtype,
+ taxclass => '', } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => '',
+ taxclass => '', } );
+
+ my $payment_gateway = '';
+ my( $processor, $login, $password, $action, @bop_options );
+ if ( $override ) { #use a payment gateway override
+
+ $payment_gateway = $override->payment_gateway;
+
+ $processor = $payment_gateway->gateway_module;
+ $login = $payment_gateway->gateway_username;
+ $password = $payment_gateway->gateway_password;
+ $action = $payment_gateway->gateway_action;
+ @bop_options = $payment_gateway->options;
+
+ } else { #use the standard settings from the config
+
+ ( $processor, $login, $password, $action, @bop_options ) =
+ $self->default_payment_gateway($method);
+
+ }
+
+ ###
+ # massage data
+ ###
+
+ my $address = exists($options{'address1'})
+ ? $options{'address1'}
+ : $self->address1;
+ my $address2 = exists($options{'address2'})
+ ? $options{'address2'}
+ : $self->address2;
+ $address .= ", ". $address2 if length($address2);
+
+ my $o_payname = exists($options{'payname'})
+ ? $options{'payname'}
+ : $self->payname;
+ my($payname, $payfirst, $paylast);
+ if ( $o_payname && $method ne 'ECHECK' ) {
+ ($payname = $o_payname) =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
+ or return "Illegal payname $payname";
+ ($payfirst, $paylast) = ($1, $2);
+ } else {
+ $payfirst = $self->getfield('first');
+ $paylast = $self->getfield('last');
+ $payname = "$payfirst $paylast";
+ }
+
+ my @invoicing_list = $self->invoicing_list_emailonly;
+ if ( $conf->exists('emailinvoiceautoalways')
+ || $conf->exists('emailinvoiceauto') && ! @invoicing_list
+ || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
+ push @invoicing_list, $self->all_emails;
+ }
+
+ my $email = ($conf->exists('business-onlinepayment-email-override'))
+ ? $conf->config('business-onlinepayment-email-override')
+ : $invoicing_list[0];
+
+ my %content = ();
+
+ my $payip = exists($options{'payip'})
+ ? $options{'payip'}
+ : $self->payip;
+ $content{customer_ip} = $payip
+ if length($payip);
+
+ $content{invoice_number} = $options{'invnum'}
+ if exists($options{'invnum'}) && length($options{'invnum'});
+
+ $content{email_customer} =
+ ( $conf->exists('business-onlinepayment-email_customer')
+ || $conf->exists('business-onlinepayment-email-override') );
+
+ my $paydate = '';
+ if ( $method eq 'CC' ) {
+
+ $content{card_number} = $payinfo;
+ $paydate = exists($options{'paydate'})
+ ? $options{'paydate'}
+ : $self->paydate;
+ $paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ $content{expiration} = "$2/$1";
+
+ my $paycvv = exists($options{'paycvv'})
+ ? $options{'paycvv'}
+ : $self->paycvv;
+ $content{cvv2} = $paycvv
+ if length($paycvv);
+
+ my $paystart_month = exists($options{'paystart_month'})
+ ? $options{'paystart_month'}
+ : $self->paystart_month;
+
+ my $paystart_year = exists($options{'paystart_year'})
+ ? $options{'paystart_year'}
+ : $self->paystart_year;
+
+ $content{card_start} = "$paystart_month/$paystart_year"
+ if $paystart_month && $paystart_year;
+
+ my $payissue = exists($options{'payissue'})
+ ? $options{'payissue'}
+ : $self->payissue;
+ $content{issue_number} = $payissue if $payissue;
+
+ $content{recurring_billing} = 'YES'
+ if qsearch('cust_pay', { 'custnum' => $self->custnum,
+ 'payby' => 'CARD',
+ 'payinfo' => $payinfo,
+ } )
+ || qsearch('cust_pay', { 'custnum' => $self->custnum,
+ 'payby' => 'CARD',
+ 'paymask' => $self->mask_payinfo('CARD', $payinfo),
+ } );
+
+
+ } elsif ( $method eq 'ECHECK' ) {
+ ( $content{account_number}, $content{routing_code} ) =
+ split('@', $payinfo);
+ $content{bank_name} = $o_payname;
+ $content{bank_state} = exists($options{'paystate'})
+ ? $options{'paystate'}
+ : $self->getfield('paystate');
+ $content{account_type} = exists($options{'paytype'})
+ ? uc($options{'paytype'}) || 'CHECKING'
+ : uc($self->getfield('paytype')) || 'CHECKING';
+ $content{account_name} = $payname;
+ $content{customer_org} = $self->company ? 'B' : 'I';
+ $content{state_id} = exists($options{'stateid'})
+ ? $options{'stateid'}
+ : $self->getfield('stateid');
+ $content{state_id_state} = exists($options{'stateid_state'})
+ ? $options{'stateid_state'}
+ : $self->getfield('stateid_state');
+ $content{customer_ssn} = exists($options{'ss'})
+ ? $options{'ss'}
+ : $self->ss;
+ } elsif ( $method eq 'LEC' ) {
+ $content{phone} = $payinfo;
+ }
+
+ ###
+ # run transaction(s)
+ ###
+
+ my $balance = exists( $options{'balance'} )
+ ? $options{'balance'}
+ : $self->balance;
+
+ $self->select_for_update; #mutex ... just until we get our pending record in
+
+ #the checks here are intended to catch concurrent payments
+ #double-form-submission prevention is taken care of in cust_pay_pending::check
+
+ #check the balance
+ return "The customer's balance has changed; $method transaction aborted."
+ if $self->balance < $balance;
+ #&& $self->balance < $amount; #might as well anyway?
+
+ #also check and make sure there aren't *other* pending payments for this cust
+
+ my @pending = qsearch('cust_pay_pending', {
+ 'custnum' => $self->custnum,
+ 'status' => { op=>'!=', value=>'done' }
+ });
+ return "A payment is already being processed for this customer (".
+ join(', ', map 'paypendingnum '. $_->paypendingnum, @pending ).
+ "); $method transaction aborted."
+ if scalar(@pending);
+
+ #okay, good to go, if we're a duplicate, cust_pay_pending will kick us out
+
+ my $cust_pay_pending = new FS::cust_pay_pending {
+ 'custnum' => $self->custnum,
+ #'invnum' => $options{'invnum'},
+ 'paid' => $amount,
+ '_date' => '',
+ 'payby' => $method2payby{$method},
+ 'payinfo' => $payinfo,
+ 'paydate' => $paydate,
+ 'status' => 'new',
+ 'gatewaynum' => ( $payment_gateway ? $payment_gateway->gatewaynum : '' ),
+ };
+ $cust_pay_pending->payunique( $options{payunique} )
+ if defined($options{payunique}) && length($options{payunique});
+ my $cpp_new_err = $cust_pay_pending->insert; #mutex lost when this is inserted
+ return $cpp_new_err if $cpp_new_err;
+
+ my( $action1, $action2 ) = split(/\s*\,\s*/, $action );
+
+ my $transaction = new Business::OnlinePayment( $processor, @bop_options );
+ $transaction->content(
+ 'type' => $method,
+ 'login' => $login,
+ 'password' => $password,
+ 'action' => $action1,
+ 'description' => $options{'description'},
+ 'amount' => $amount,
+ #'invoice_number' => $options{'invnum'},
+ 'customer_id' => $self->custnum,
+ 'last_name' => $paylast,
+ 'first_name' => $payfirst,
+ 'name' => $payname,
+ 'address' => $address,
+ 'city' => ( exists($options{'city'})
+ ? $options{'city'}
+ : $self->city ),
+ 'state' => ( exists($options{'state'})
+ ? $options{'state'}
+ : $self->state ),
+ 'zip' => ( exists($options{'zip'})
+ ? $options{'zip'}
+ : $self->zip ),
+ 'country' => ( exists($options{'country'})
+ ? $options{'country'}
+ : $self->country ),
+ 'referer' => 'http://cleanwhisker.420.am/', #XXX fix referer :/
+ 'email' => $email,
+ 'phone' => $self->daytime || $self->night,
+ %content, #after
+ );
+
+ $cust_pay_pending->status('pending');
+ my $cpp_pending_err = $cust_pay_pending->replace;
+ return $cpp_pending_err if $cpp_pending_err;
+
+ #config?
+ my $BOP_TESTING = 0;
+ my $BOP_TESTING_SUCCESS = 1;
+
+ unless ( $BOP_TESTING ) {
+ $transaction->submit();
+ } else {
+ if ( $BOP_TESTING_SUCCESS ) {
+ $transaction->is_success(1);
+ $transaction->authorization('fake auth');
+ } else {
+ $transaction->is_success(0);
+ $transaction->error_message('fake failure');
+ }
+ }
+
+ if ( $transaction->is_success() && $action2 ) {
+
+ $cust_pay_pending->status('authorized');
+ my $cpp_authorized_err = $cust_pay_pending->replace;
+ return $cpp_authorized_err if $cpp_authorized_err;
+
+ my $auth = $transaction->authorization;
+ my $ordernum = $transaction->can('order_number')
+ ? $transaction->order_number
+ : '';
+
+ my $capture =
+ new Business::OnlinePayment( $processor, @bop_options );
+
+ my %capture = (
+ %content,
+ type => $method,
+ action => $action2,
+ login => $login,
+ password => $password,
+ order_number => $ordernum,
+ amount => $amount,
+ authorization => $auth,
+ description => $options{'description'},
+ );
+
+ foreach my $field (qw( authorization_source_code returned_ACI
+ transaction_identifier validation_code
+ transaction_sequence_num local_transaction_date
+ local_transaction_time AVS_result_code )) {
+ $capture{$field} = $transaction->$field() if $transaction->can($field);
+ }
+
+ $capture->content( %capture );
+
+ $capture->submit();
+
+ unless ( $capture->is_success ) {
+ my $e = "Authorization successful but capture failed, custnum #".
+ $self->custnum. ': '. $capture->result_code.
+ ": ". $capture->error_message;
+ warn $e;
+ return $e;
+ }
+
+ }
+
+ $cust_pay_pending->status($transaction->is_success() ? 'captured' : 'declined');
+ my $cpp_captured_err = $cust_pay_pending->replace;
+ return $cpp_captured_err if $cpp_captured_err;
+
+ ###
+ # remove paycvv after initial transaction
+ ###
+
+ #false laziness w/misc/process/payment.cgi - check both to make sure working
+ # correctly
+ if ( defined $self->dbdef_table->column('paycvv')
+ && length($self->paycvv)
+ && ! grep { $_ eq cardtype($payinfo) } $conf->config('cvv-save')
+ ) {
+ my $error = $self->remove_cvv;
+ if ( $error ) {
+ warn "WARNING: error removing cvv: $error\n";
+ }
+ }
+
+ ###
+ # result handling
+ ###
+
+ if ( $transaction->is_success() ) {
+
+ my $paybatch = '';
+ if ( $payment_gateway ) { # agent override
+ $paybatch = $payment_gateway->gatewaynum. '-';
+ }
+
+ $paybatch .= "$processor:". $transaction->authorization;
+
+ $paybatch .= ':'. $transaction->order_number
+ if $transaction->can('order_number')
+ && length($transaction->order_number);
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $self->custnum,
+ 'invnum' => $options{'invnum'},
+ 'paid' => $amount,
+ '_date' => '',
+ 'payby' => $method2payby{$method},
+ 'payinfo' => $payinfo,
+ 'paybatch' => $paybatch,
+ 'paydate' => $paydate,
+ } );
+ #doesn't hurt to know, even though the dup check is in cust_pay_pending now
+ $cust_pay->payunique( $options{payunique} )
+ if defined($options{payunique}) && length($options{payunique});
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #start a transaction, insert the cust_pay and set cust_pay_pending.status to done in a single transction
+
+ my $error = $cust_pay->insert($options{'manual'} ? ( 'manual' => 1 ) : () );
+
+ if ( $error ) {
+ $cust_pay->invnum(''); #try again with no specific invnum
+ my $error2 = $cust_pay->insert( $options{'manual'} ?
+ ( 'manual' => 1 ) : ()
+ );
+ if ( $error2 ) {
+ # gah. but at least we have a record of the state we had to abort in
+ # from cust_pay_pending now.
+ my $e = "WARNING: $method captured but payment not recorded - ".
+ "error inserting payment ($processor): $error2".
+ " (previously tried insert with invnum #$options{'invnum'}" .
+ ": $error ) - pending payment saved as paypendingnum ".
+ $cust_pay_pending->paypendingnum. "\n";
+ warn $e;
+ return $e;
+ }
+ }
+
+ if ( $options{'paynum_ref'} ) {
+ ${ $options{'paynum_ref'} } = $cust_pay->paynum;
+ }
+
+ $cust_pay_pending->status('done');
+ $cust_pay_pending->statustext('captured');
+ $cust_pay_pending->paynum($cust_pay->paynum);
+ my $cpp_done_err = $cust_pay_pending->replace;
+
+ if ( $cpp_done_err ) {
+
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ my $e = "WARNING: $method captured but payment not recorded - ".
+ "error updating status for paypendingnum ".
+ $cust_pay_pending->paypendingnum. ": $cpp_done_err \n";
+ warn $e;
+ return $e;
+
+ } else {
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return ''; #no error
+
+ }
+
+ } else {
+
+ my $perror = "$processor error: ". $transaction->error_message;
+
+ unless ( $transaction->error_message ) {
+
+ my $t_response;
+ if ( $transaction->can('response_page') ) {
+ $t_response = {
+ 'page' => ( $transaction->can('response_page')
+ ? $transaction->response_page
+ : ''
+ ),
+ 'code' => ( $transaction->can('response_code')
+ ? $transaction->response_code
+ : ''
+ ),
+ 'headers' => ( $transaction->can('response_headers')
+ ? $transaction->response_headers
+ : ''
+ ),
+ };
+ } else {
+ $t_response .=
+ "No additional debugging information available for $processor";
+ }
+
+ $perror .= "No error_message returned from $processor -- ".
+ ( ref($t_response) ? Dumper($t_response) : $t_response );
+
+ }
+
+ if ( !$options{'quiet'} && !$realtime_bop_decline_quiet
+ && $conf->exists('emaildecline')
+ && grep { $_ ne 'POST' } $self->invoicing_list
+ && ! grep { $transaction->error_message =~ /$_/ }
+ $conf->config('emaildecline-exclude')
+ ) {
+ my @templ = $conf->config('declinetemplate');
+ my $template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", @templ ],
+ ) or return "($perror) can't create template: $Text::Template::ERROR";
+ $template->compile()
+ or return "($perror) can't compile template: $Text::Template::ERROR";
+
+ my $templ_hash = { error => $transaction->error_message };
+
+ my $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->agentnum ),
+ 'to' => [ grep { $_ ne 'POST' } $self->invoicing_list ],
+ 'subject' => 'Your payment could not be processed',
+ 'body' => [ $template->fill_in(HASH => $templ_hash) ],
+ );
+
+ $perror .= " (also received error sending decline notification: $error)"
+ if $error;
+
+ }
+
+ $cust_pay_pending->status('done');
+ $cust_pay_pending->statustext("declined: $perror");
+ my $cpp_done_err = $cust_pay_pending->replace;
+ if ( $cpp_done_err ) {
+ my $e = "WARNING: $method declined but pending payment not resolved - ".
+ "error updating status for paypendingnum ".
+ $cust_pay_pending->paypendingnum. ": $cpp_done_err \n";
+ warn $e;
+ $perror = "$e ($perror)";
+ }
+
+ return $perror;
+ }
+
+}
+
+=item fake_bop
+
+=cut
+
+sub fake_bop {
+ my( $self, $method, $amount, %options ) = @_;
+
+ if ( $options{'fake_failure'} ) {
+ return "Error: No error; test failure requested with fake_failure";
+ }
+
+ my %method2payby = (
+ 'CC' => 'CARD',
+ 'ECHECK' => 'CHEK',
+ 'LEC' => 'LECB',
+ );
+
+ #my $paybatch = '';
+ #if ( $payment_gateway ) { # agent override
+ # $paybatch = $payment_gateway->gatewaynum. '-';
+ #}
+ #
+ #$paybatch .= "$processor:". $transaction->authorization;
+ #
+ #$paybatch .= ':'. $transaction->order_number
+ # if $transaction->can('order_number')
+ # && length($transaction->order_number);
+
+ my $paybatch = 'FakeProcessor:54:32';
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $self->custnum,
+ 'invnum' => $options{'invnum'},
+ 'paid' => $amount,
+ '_date' => '',
+ 'payby' => $method2payby{$method},
+ #'payinfo' => $payinfo,
+ 'payinfo' => '4111111111111111',
+ 'paybatch' => $paybatch,
+ #'paydate' => $paydate,
+ 'paydate' => '2012-05-01',
+ } );
+ $cust_pay->payunique( $options{payunique} ) if length($options{payunique});
+
+ my $error = $cust_pay->insert($options{'manual'} ? ( 'manual' => 1 ) : () );
+
+ if ( $error ) {
+ $cust_pay->invnum(''); #try again with no specific invnum
+ my $error2 = $cust_pay->insert( $options{'manual'} ?
+ ( 'manual' => 1 ) : ()
+ );
+ if ( $error2 ) {
+ # gah, even with transactions.
+ my $e = 'WARNING: Card/ACH debited but database not updated - '.
+ "error inserting (fake!) payment: $error2".
+ " (previously tried insert with invnum #$options{'invnum'}" .
+ ": $error )";
+ warn $e;
+ return $e;
+ }
+ }
+
+ if ( $options{'paynum_ref'} ) {
+ ${ $options{'paynum_ref'} } = $cust_pay->paynum;
+ }
+
+ return ''; #no error
+
+}
+
+=item default_payment_gateway
+
+=cut
+
+sub default_payment_gateway {
+ my( $self, $method ) = @_;
+
+ die "Real-time processing not enabled\n"
+ unless $conf->exists('business-onlinepayment');
+
+ #load up config
+ my $bop_config = 'business-onlinepayment';
+ $bop_config .= '-ach'
+ if $method =~ /^(ECHECK|CHEK)$/ && $conf->exists($bop_config. '-ach');
+ my ( $processor, $login, $password, $action, @bop_options ) =
+ $conf->config($bop_config);
+ $action ||= 'normal authorization';
+ pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/;
+ die "No real-time processor is enabled - ".
+ "did you set the business-onlinepayment configuration value?\n"
+ unless $processor;
+
+ ( $processor, $login, $password, $action, @bop_options )
+}
+
+=item remove_cvv
+
+Removes the I<paycvv> field from the database directly.
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub remove_cvv {
+ my $self = shift;
+ my $sth = dbh->prepare("UPDATE cust_main SET paycvv = '' WHERE custnum = ?")
+ or return dbh->errstr;
+ $sth->execute($self->custnum)
+ or return $sth->errstr;
+ $self->paycvv('');
+ '';
+}
+
+=item realtime_refund_bop METHOD [ OPTION => VALUE ... ]
+
+Refunds a realtime credit card, ACH (electronic check) or phone bill transaction
+via a Business::OnlinePayment realtime gateway. See
+L<http://420.am/business-onlinepayment> for supported gateways.
+
+Available methods are: I<CC>, I<ECHECK> and I<LEC>
+
+Available options are: I<amount>, I<reason>, I<paynum>, I<paydate>
+
+Most gateways require a reference to an original payment transaction to refund,
+so you probably need to specify a I<paynum>.
+
+I<amount> defaults to the original amount of the payment if not specified.
+
+I<reason> specifies a reason for the refund.
+
+I<paydate> specifies the expiration date for a credit card overriding the
+value from the customer record or the payment record. Specified as yyyy-mm-dd
+
+Implementation note: If I<amount> is unspecified or equal to the amount of the
+orignal payment, first an attempt is made to "void" the transaction via
+the gateway (to cancel a not-yet settled transaction) and then if that fails,
+the normal attempt is made to "refund" ("credit") the transaction via the
+gateway is attempted.
+
+#The additional options I<payname>, I<address1>, I<address2>, I<city>, I<state>,
+#I<zip>, I<payinfo> and I<paydate> are also available. Any of these options,
+#if set, will override the value from the customer record.
+
+#If an I<invnum> is specified, this payment (if successful) is applied to the
+#specified invoice. If you don't specify an I<invnum> you might want to
+#call the B<apply_payments> method.
+
+=cut
+
+#some false laziness w/realtime_bop, not enough to make it worth merging
+#but some useful small subs should be pulled out
+sub realtime_refund_bop {
+ my( $self, $method, %options ) = @_;
+ if ( $DEBUG ) {
+ warn "$me realtime_refund_bop: $method refund\n";
+ warn " $_ => $options{$_}\n" foreach keys %options;
+ }
+
+ eval "use Business::OnlinePayment";
+ die $@ if $@;
+
+ ###
+ # look up the original payment and optionally a gateway for that payment
+ ###
+
+ my $cust_pay = '';
+ my $amount = $options{'amount'};
+
+ my( $processor, $login, $password, @bop_options ) ;
+ my( $auth, $order_number ) = ( '', '', '' );
+
+ if ( $options{'paynum'} ) {
+
+ warn " paynum: $options{paynum}\n" if $DEBUG > 1;
+ $cust_pay = qsearchs('cust_pay', { paynum=>$options{'paynum'} } )
+ or return "Unknown paynum $options{'paynum'}";
+ $amount ||= $cust_pay->paid;
+
+ $cust_pay->paybatch =~ /^((\d+)\-)?(\w+):\s*([\w\-\/ ]*)(:([\w\-]+))?$/
+ or return "Can't parse paybatch for paynum $options{'paynum'}: ".
+ $cust_pay->paybatch;
+ my $gatewaynum = '';
+ ( $gatewaynum, $processor, $auth, $order_number ) = ( $2, $3, $4, $6 );
+
+ if ( $gatewaynum ) { #gateway for the payment to be refunded
+
+ my $payment_gateway =
+ qsearchs('payment_gateway', { 'gatewaynum' => $gatewaynum } );
+ die "payment gateway $gatewaynum not found"
+ unless $payment_gateway;
+
+ $processor = $payment_gateway->gateway_module;
+ $login = $payment_gateway->gateway_username;
+ $password = $payment_gateway->gateway_password;
+ @bop_options = $payment_gateway->options;
+
+ } else { #try the default gateway
+
+ my( $conf_processor, $unused_action );
+ ( $conf_processor, $login, $password, $unused_action, @bop_options ) =
+ $self->default_payment_gateway($method);
+
+ return "processor of payment $options{'paynum'} $processor does not".
+ " match default processor $conf_processor"
+ unless $processor eq $conf_processor;
+
+ }
+
+
+ } else { # didn't specify a paynum, so look for agent gateway overrides
+ # like a normal transaction
+
+ my $cardtype;
+ if ( $method eq 'CC' ) {
+ $cardtype = cardtype($self->payinfo);
+ } elsif ( $method eq 'ECHECK' ) {
+ $cardtype = 'ACH';
+ } else {
+ $cardtype = $method;
+ }
+ my $override =
+ qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => $cardtype,
+ taxclass => '', } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => '',
+ taxclass => '', } );
+
+ if ( $override ) { #use a payment gateway override
+
+ my $payment_gateway = $override->payment_gateway;
+
+ $processor = $payment_gateway->gateway_module;
+ $login = $payment_gateway->gateway_username;
+ $password = $payment_gateway->gateway_password;
+ #$action = $payment_gateway->gateway_action;
+ @bop_options = $payment_gateway->options;
+
+ } else { #use the standard settings from the config
+
+ my $unused_action;
+ ( $processor, $login, $password, $unused_action, @bop_options ) =
+ $self->default_payment_gateway($method);
+
+ }
+
+ }
+ return "neither amount nor paynum specified" unless $amount;
+
+ my %content = (
+ 'type' => $method,
+ 'login' => $login,
+ 'password' => $password,
+ 'order_number' => $order_number,
+ 'amount' => $amount,
+ 'referer' => 'http://cleanwhisker.420.am/', #XXX fix referer :/
+ );
+ $content{authorization} = $auth
+ if length($auth); #echeck/ACH transactions have an order # but no auth
+ #(at least with authorize.net)
+
+ my $disable_void_after;
+ if ($conf->exists('disable_void_after')
+ && $conf->config('disable_void_after') =~ /^(\d+)$/) {
+ $disable_void_after = $1;
+ }
+
+ #first try void if applicable
+ if ( $cust_pay && $cust_pay->paid == $amount
+ && (
+ ( not defined($disable_void_after) )
+ || ( time < ($cust_pay->_date + $disable_void_after ) )
+ )
+ ) {
+ warn " attempting void\n" if $DEBUG > 1;
+ my $void = new Business::OnlinePayment( $processor, @bop_options );
+ $void->content( 'action' => 'void', %content );
+ $void->submit();
+ if ( $void->is_success ) {
+ my $error = $cust_pay->void($options{'reason'});
+ if ( $error ) {
+ # gah, even with transactions.
+ my $e = 'WARNING: Card/ACH voided but database not updated - '.
+ "error voiding payment: $error";
+ warn $e;
+ return $e;
+ }
+ warn " void successful\n" if $DEBUG > 1;
+ return '';
+ }
+ }
+
+ warn " void unsuccessful, trying refund\n"
+ if $DEBUG > 1;
+
+ #massage data
+ my $address = $self->address1;
+ $address .= ", ". $self->address2 if $self->address2;
+
+ my($payname, $payfirst, $paylast);
+ if ( $self->payname && $method ne 'ECHECK' ) {
+ $payname = $self->payname;
+ $payname =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
+ or return "Illegal payname $payname";
+ ($payfirst, $paylast) = ($1, $2);
+ } else {
+ $payfirst = $self->getfield('first');
+ $paylast = $self->getfield('last');
+ $payname = "$payfirst $paylast";
+ }
+
+ my @invoicing_list = $self->invoicing_list_emailonly;
+ if ( $conf->exists('emailinvoiceautoalways')
+ || $conf->exists('emailinvoiceauto') && ! @invoicing_list
+ || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
+ push @invoicing_list, $self->all_emails;
+ }
+
+ my $email = ($conf->exists('business-onlinepayment-email-override'))
+ ? $conf->config('business-onlinepayment-email-override')
+ : $invoicing_list[0];
+
+ my $payip = exists($options{'payip'})
+ ? $options{'payip'}
+ : $self->payip;
+ $content{customer_ip} = $payip
+ if length($payip);
+
+ my $payinfo = '';
+ if ( $method eq 'CC' ) {
+
+ if ( $cust_pay ) {
+ $content{card_number} = $payinfo = $cust_pay->payinfo;
+ (exists($options{'paydate'}) ? $options{'paydate'} : $cust_pay->paydate)
+ =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/ &&
+ ($content{expiration} = "$2/$1"); # where available
+ } else {
+ $content{card_number} = $payinfo = $self->payinfo;
+ (exists($options{'paydate'}) ? $options{'paydate'} : $self->paydate)
+ =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ $content{expiration} = "$2/$1";
+ }
+
+ } elsif ( $method eq 'ECHECK' ) {
+
+ if ( $cust_pay ) {
+ $payinfo = $cust_pay->payinfo;
+ } else {
+ $payinfo = $self->payinfo;
+ }
+ ( $content{account_number}, $content{routing_code} )= split('@', $payinfo );
+ $content{bank_name} = $self->payname;
+ $content{account_type} = 'CHECKING';
+ $content{account_name} = $payname;
+ $content{customer_org} = $self->company ? 'B' : 'I';
+ $content{customer_ssn} = $self->ss;
+ } elsif ( $method eq 'LEC' ) {
+ $content{phone} = $payinfo = $self->payinfo;
+ }
+
+ #then try refund
+ my $refund = new Business::OnlinePayment( $processor, @bop_options );
+ my %sub_content = $refund->content(
+ 'action' => 'credit',
+ 'customer_id' => $self->custnum,
+ 'last_name' => $paylast,
+ 'first_name' => $payfirst,
+ 'name' => $payname,
+ 'address' => $address,
+ 'city' => $self->city,
+ 'state' => $self->state,
+ 'zip' => $self->zip,
+ 'country' => $self->country,
+ 'email' => $email,
+ 'phone' => $self->daytime || $self->night,
+ %content, #after
+ );
+ warn join('', map { " $_ => $sub_content{$_}\n" } keys %sub_content )
+ if $DEBUG > 1;
+ $refund->submit();
+
+ return "$processor error: ". $refund->error_message
+ unless $refund->is_success();
+
+ my %method2payby = (
+ 'CC' => 'CARD',
+ 'ECHECK' => 'CHEK',
+ 'LEC' => 'LECB',
+ );
+
+ my $paybatch = "$processor:". $refund->authorization;
+ $paybatch .= ':'. $refund->order_number
+ if $refund->can('order_number') && $refund->order_number;
+
+ while ( $cust_pay && $cust_pay->unapplied < $amount ) {
+ my @cust_bill_pay = $cust_pay->cust_bill_pay;
+ last unless @cust_bill_pay;
+ my $cust_bill_pay = pop @cust_bill_pay;
+ my $error = $cust_bill_pay->delete;
+ last if $error;
+ }
+
+ my $cust_refund = new FS::cust_refund ( {
+ 'custnum' => $self->custnum,
+ 'paynum' => $options{'paynum'},
+ 'refund' => $amount,
+ '_date' => '',
+ 'payby' => $method2payby{$method},
+ 'payinfo' => $payinfo,
+ 'paybatch' => $paybatch,
+ 'reason' => $options{'reason'} || 'card or ACH refund',
+ } );
+ my $error = $cust_refund->insert;
+ if ( $error ) {
+ $cust_refund->paynum(''); #try again with no specific paynum
+ my $error2 = $cust_refund->insert;
+ if ( $error2 ) {
+ # gah, even with transactions.
+ my $e = 'WARNING: Card/ACH refunded but database not updated - '.
+ "error inserting refund ($processor): $error2".
+ " (previously tried insert with paynum #$options{'paynum'}" .
+ ": $error )";
+ warn $e;
+ return $e;
+ }
+ }
+
+ ''; #no error
+
+}
+
+=item batch_card OPTION => VALUE...
+
+Adds a payment for this invoice to the pending credit card batch (see
+L<FS::cust_pay_batch>), or, if the B<realtime> option is set to a true value,
+runs the payment using a realtime gateway.
+
+=cut
+
+sub batch_card {
+ my ($self, %options) = @_;
+
+ my $amount;
+ if (exists($options{amount})) {
+ $amount = $options{amount};
+ }else{
+ $amount = sprintf("%.2f", $self->balance - $self->in_transit_payments);
+ }
+ return '' unless $amount > 0;
+
+ my $invnum = delete $options{invnum};
+ my $payby = $options{invnum} || $self->payby; #dubious
+
+ if ($options{'realtime'}) {
+ return $self->realtime_bop( FS::payby->payby2bop($self->payby),
+ $amount,
+ %options,
+ );
+ }
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #this needs to handle mysql as well as Pg, like svc_acct.pm
+ #(make it into a common function if folks need to do batching with mysql)
+ $dbh->do("LOCK TABLE pay_batch IN SHARE ROW EXCLUSIVE MODE")
+ or return "Cannot lock pay_batch: " . $dbh->errstr;
+
+ my %pay_batch = (
+ 'status' => 'O',
+ 'payby' => FS::payby->payby2payment($payby),
+ );
+
+ my $pay_batch = qsearchs( 'pay_batch', \%pay_batch );
+
+ unless ( $pay_batch ) {
+ $pay_batch = new FS::pay_batch \%pay_batch;
+ my $error = $pay_batch->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die "error creating new batch: $error\n";
+ }
+ }
+
+ my $old_cust_pay_batch = qsearchs('cust_pay_batch', {
+ 'batchnum' => $pay_batch->batchnum,
+ 'custnum' => $self->custnum,
+ } );
+
+ foreach (qw( address1 address2 city state zip country payby payinfo paydate
+ payname )) {
+ $options{$_} = '' unless exists($options{$_});
+ }
+
+ my $cust_pay_batch = new FS::cust_pay_batch ( {
+ 'batchnum' => $pay_batch->batchnum,
+ 'invnum' => $invnum || 0, # is there a better value?
+ # this field should be
+ # removed...
+ # cust_bill_pay_batch now
+ 'custnum' => $self->custnum,
+ 'last' => $self->getfield('last'),
+ 'first' => $self->getfield('first'),
+ 'address1' => $options{address1} || $self->address1,
+ 'address2' => $options{address2} || $self->address2,
+ 'city' => $options{city} || $self->city,
+ 'state' => $options{state} || $self->state,
+ 'zip' => $options{zip} || $self->zip,
+ 'country' => $options{country} || $self->country,
+ 'payby' => $options{payby} || $self->payby,
+ 'payinfo' => $options{payinfo} || $self->payinfo,
+ 'exp' => $options{paydate} || $self->paydate,
+ 'payname' => $options{payname} || $self->payname,
+ 'amount' => $amount, # consolidating
+ } );
+
+ $cust_pay_batch->paybatchnum($old_cust_pay_batch->paybatchnum)
+ if $old_cust_pay_batch;
+
+ my $error;
+ if ($old_cust_pay_batch) {
+ $error = $cust_pay_batch->replace($old_cust_pay_batch)
+ } else {
+ $error = $cust_pay_batch->insert;
+ }
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die $error;
+ }
+
+ my $unapplied = $self->total_unapplied_credits
+ + $self->total_unapplied_payments
+ + $self->in_transit_payments;
+ foreach my $cust_bill ($self->open_cust_bill) {
+ #$dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ my $cust_bill_pay_batch = new FS::cust_bill_pay_batch {
+ 'invnum' => $cust_bill->invnum,
+ 'paybatchnum' => $cust_pay_batch->paybatchnum,
+ 'amount' => $cust_bill->owed,
+ '_date' => time,
+ };
+ if ($unapplied >= $cust_bill_pay_batch->amount){
+ $unapplied -= $cust_bill_pay_batch->amount;
+ next;
+ }else{
+ $cust_bill_pay_batch->amount(sprintf ( "%.2f",
+ $cust_bill_pay_batch->amount - $unapplied )); $unapplied = 0;
+ }
+ $error = $cust_bill_pay_batch->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item apply_payments_and_credits
+
+Applies unapplied payments and credits.
+
+In most cases, this new method should be used in place of sequential
+apply_payments and apply_credits methods.
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub apply_payments_and_credits {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update; #mutex
+
+ foreach my $cust_bill ( $self->open_cust_bill ) {
+ my $error = $cust_bill->apply_payments_and_credits;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error applying: $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+
+}
+
+=item apply_credits OPTION => VALUE ...
+
+Applies (see L<FS::cust_credit_bill>) unapplied credits (see L<FS::cust_credit>)
+to outstanding invoice balances in chronological order (or reverse
+chronological order if the I<order> option is set to B<newest>) and returns the
+value of any remaining unapplied credits available for refund (see
+L<FS::cust_refund>).
+
+Dies if there is an error.
+
+=cut
+
+sub apply_credits {
+ my $self = shift;
+ my %opt = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update; #mutex
+
+ unless ( $self->total_unapplied_credits ) {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return 0;
+ }
+
+ my @credits = sort { $b->_date <=> $a->_date} (grep { $_->credited > 0 }
+ qsearch('cust_credit', { 'custnum' => $self->custnum } ) );
+
+ my @invoices = $self->open_cust_bill;
+ @invoices = sort { $b->_date <=> $a->_date } @invoices
+ if defined($opt{'order'}) && $opt{'order'} eq 'newest';
+
+ my $credit;
+ foreach my $cust_bill ( @invoices ) {
+ my $amount;
+
+ if ( !defined($credit) || $credit->credited == 0) {
+ $credit = pop @credits or last;
+ }
+
+ if ($cust_bill->owed >= $credit->credited) {
+ $amount=$credit->credited;
+ }else{
+ $amount=$cust_bill->owed;
+ }
+
+ my $cust_credit_bill = new FS::cust_credit_bill ( {
+ 'crednum' => $credit->crednum,
+ 'invnum' => $cust_bill->invnum,
+ 'amount' => $amount,
+ } );
+ my $error = $cust_credit_bill->insert;
+ if ( $error ) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ die $error;
+ }
+
+ redo if ($cust_bill->owed > 0);
+
+ }
+
+ my $total_unapplied_credits = $self->total_unapplied_credits;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return $total_unapplied_credits;
+}
+
+=item apply_payments
+
+Applies (see L<FS::cust_bill_pay>) unapplied payments (see L<FS::cust_pay>)
+to outstanding invoice balances in chronological order.
+
+ #and returns the value of any remaining unapplied payments.
+
+Dies if there is an error.
+
+=cut
+
+sub apply_payments {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update; #mutex
+
+ #return 0 unless
+
+ my @payments = sort { $b->_date <=> $a->_date }
+ grep { $_->unapplied > 0 }
+ $self->cust_pay;
+
+ my @invoices = sort { $a->_date <=> $b->_date}
+ grep { $_->owed > 0 }
+ $self->cust_bill;
+
+ my $payment;
+
+ foreach my $cust_bill ( @invoices ) {
+ my $amount;
+
+ if ( !defined($payment) || $payment->unapplied == 0 ) {
+ $payment = pop @payments or last;
+ }
+
+ if ( $cust_bill->owed >= $payment->unapplied ) {
+ $amount = $payment->unapplied;
+ } else {
+ $amount = $cust_bill->owed;
+ }
+
+ my $cust_bill_pay = new FS::cust_bill_pay ( {
+ 'paynum' => $payment->paynum,
+ 'invnum' => $cust_bill->invnum,
+ 'amount' => $amount,
+ } );
+ my $error = $cust_bill_pay->insert;
+ if ( $error ) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ die $error;
+ }
+
+ redo if ( $cust_bill->owed > 0);
+
+ }
+
+ my $total_unapplied_payments = $self->total_unapplied_payments;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return $total_unapplied_payments;
+}
+
+=item total_owed
+
+Returns the total owed for this customer on all invoices
+(see L<FS::cust_bill/owed>).
+
+=cut
+
+sub total_owed {
+ my $self = shift;
+ $self->total_owed_date(2145859200); #12/31/2037
+}
+
+=item total_owed_date TIME
+
+Returns the total owed for this customer on all invoices with date earlier than
+TIME. TIME is specified as a UNIX timestamp; see L<perlfunc/"time">). Also
+see L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub total_owed_date {
+ my $self = shift;
+ my $time = shift;
+ my $total_bill = 0;
+ foreach my $cust_bill (
+ grep { $_->_date <= $time }
+ qsearch('cust_bill', { 'custnum' => $self->custnum, } )
+ ) {
+ $total_bill += $cust_bill->owed;
+ }
+ sprintf( "%.2f", $total_bill );
+}
+
+=item total_paid
+
+Returns the total amount of all payments.
+
+=cut
+
+sub total_paid {
+ my $self = shift;
+ my $total = 0;
+ $total += $_->paid foreach $self->cust_pay;
+ sprintf( "%.2f", $total );
+}
+
+=item total_unapplied_credits
+
+Returns the total outstanding credit (see L<FS::cust_credit>) for this
+customer. See L<FS::cust_credit/credited>.
+
+=item total_credited
+
+Old name for total_unapplied_credits. Don't use.
+
+=cut
+
+sub total_credited {
+ #carp "total_credited deprecated, use total_unapplied_credits";
+ shift->total_unapplied_credits(@_);
+}
+
+sub total_unapplied_credits {
+ my $self = shift;
+ my $total_credit = 0;
+ $total_credit += $_->credited foreach $self->cust_credit;
+ sprintf( "%.2f", $total_credit );
+}
+
+=item total_unapplied_payments
+
+Returns the total unapplied payments (see L<FS::cust_pay>) for this customer.
+See L<FS::cust_pay/unapplied>.
+
+=cut
+
+sub total_unapplied_payments {
+ my $self = shift;
+ my $total_unapplied = 0;
+ $total_unapplied += $_->unapplied foreach $self->cust_pay;
+ sprintf( "%.2f", $total_unapplied );
+}
+
+=item total_unapplied_refunds
+
+Returns the total unrefunded refunds (see L<FS::cust_refund>) for this
+customer. See L<FS::cust_refund/unapplied>.
+
+=cut
+
+sub total_unapplied_refunds {
+ my $self = shift;
+ my $total_unapplied = 0;
+ $total_unapplied += $_->unapplied foreach $self->cust_refund;
+ sprintf( "%.2f", $total_unapplied );
+}
+
+=item balance
+
+Returns the balance for this customer (total_owed plus total_unrefunded, minus
+total_unapplied_credits minus total_unapplied_payments).
+
+=cut
+
+sub balance {
+ my $self = shift;
+ sprintf( "%.2f",
+ $self->total_owed
+ + $self->total_unapplied_refunds
+ - $self->total_unapplied_credits
+ - $self->total_unapplied_payments
+ );
+}
+
+=item balance_date TIME
+
+Returns the balance for this customer, only considering invoices with date
+earlier than TIME (total_owed_date minus total_credited minus
+total_unapplied_payments). TIME is specified as a UNIX timestamp; see
+L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion
+functions.
+
+=cut
+
+sub balance_date {
+ my $self = shift;
+ my $time = shift;
+ sprintf( "%.2f",
+ $self->total_owed_date($time)
+ + $self->total_unapplied_refunds
+ - $self->total_unapplied_credits
+ - $self->total_unapplied_payments
+ );
+}
+
+=item in_transit_payments
+
+Returns the total of requests for payments for this customer pending in
+batches in transit to the bank. See L<FS::pay_batch> and L<FS::cust_pay_batch>
+
+=cut
+
+sub in_transit_payments {
+ my $self = shift;
+ my $in_transit_payments = 0;
+ foreach my $pay_batch ( qsearch('pay_batch', {
+ 'status' => 'I',
+ } ) ) {
+ foreach my $cust_pay_batch ( qsearch('cust_pay_batch', {
+ 'batchnum' => $pay_batch->batchnum,
+ 'custnum' => $self->custnum,
+ } ) ) {
+ $in_transit_payments += $cust_pay_batch->amount;
+ }
+ }
+ sprintf( "%.2f", $in_transit_payments );
+}
+
+=item paydate_monthyear
+
+Returns a two-element list consisting of the month and year of this customer's
+paydate (credit card expiration date for CARD customers)
+
+=cut
+
+sub paydate_monthyear {
+ my $self = shift;
+ if ( $self->paydate =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #Pg date format
+ ( $2, $1 );
+ } elsif ( $self->paydate =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+ ( $1, $3 );
+ } else {
+ ('', '');
+ }
+}
+
+=item invoicing_list [ ARRAYREF ]
+
+If an arguement is given, sets these email addresses as invoice recipients
+(see L<FS::cust_main_invoice>). Errors are not fatal and are not reported
+(except as warnings), so use check_invoicing_list first.
+
+Returns a list of email addresses (with svcnum entries expanded).
+
+Note: You can clear the invoicing list by passing an empty ARRAYREF. You can
+check it without disturbing anything by passing nothing.
+
+This interface may change in the future.
+
+=cut
+
+sub invoicing_list {
+ my( $self, $arrayref ) = @_;
+
+ if ( $arrayref ) {
+ my @cust_main_invoice;
+ if ( $self->custnum ) {
+ @cust_main_invoice =
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ } else {
+ @cust_main_invoice = ();
+ }
+ foreach my $cust_main_invoice ( @cust_main_invoice ) {
+ #warn $cust_main_invoice->destnum;
+ unless ( grep { $cust_main_invoice->address eq $_ } @{$arrayref} ) {
+ #warn $cust_main_invoice->destnum;
+ my $error = $cust_main_invoice->delete;
+ warn $error if $error;
+ }
+ }
+ if ( $self->custnum ) {
+ @cust_main_invoice =
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ } else {
+ @cust_main_invoice = ();
+ }
+ my %seen = map { $_->address => 1 } @cust_main_invoice;
+ foreach my $address ( @{$arrayref} ) {
+ next if exists $seen{$address} && $seen{$address};
+ $seen{$address} = 1;
+ my $cust_main_invoice = new FS::cust_main_invoice ( {
+ 'custnum' => $self->custnum,
+ 'dest' => $address,
+ } );
+ my $error = $cust_main_invoice->insert;
+ warn $error if $error;
+ }
+ }
+
+ if ( $self->custnum ) {
+ map { $_->address }
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ } else {
+ ();
+ }
+
+}
+
+=item check_invoicing_list ARRAYREF
+
+Checks these arguements as valid input for the invoicing_list method. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub check_invoicing_list {
+ my( $self, $arrayref ) = @_;
+
+ foreach my $address ( @$arrayref ) {
+
+ if ($address eq 'FAX' and $self->getfield('fax') eq '') {
+ return 'Can\'t add FAX invoice destination with a blank FAX number.';
+ }
+
+ my $cust_main_invoice = new FS::cust_main_invoice ( {
+ 'custnum' => $self->custnum,
+ 'dest' => $address,
+ } );
+ my $error = $self->custnum
+ ? $cust_main_invoice->check
+ : $cust_main_invoice->checkdest
+ ;
+ return $error if $error;
+
+ }
+
+ return "Email address required"
+ if $conf->exists('cust_main-require_invoicing_list_email')
+ && ! grep { $_ !~ /^([A-Z]+)$/ } @$arrayref;
+
+ '';
+}
+
+=item set_default_invoicing_list
+
+Sets the invoicing list to all accounts associated with this customer,
+overwriting any previous invoicing list.
+
+=cut
+
+sub set_default_invoicing_list {
+ my $self = shift;
+ $self->invoicing_list($self->all_emails);
+}
+
+=item all_emails
+
+Returns the email addresses of all accounts provisioned for this customer.
+
+=cut
+
+sub all_emails {
+ my $self = shift;
+ my %list;
+ foreach my $cust_pkg ( $self->all_pkgs ) {
+ my @cust_svc = qsearch('cust_svc', { 'pkgnum' => $cust_pkg->pkgnum } );
+ my @svc_acct =
+ map { qsearchs('svc_acct', { 'svcnum' => $_->svcnum } ) }
+ grep { qsearchs('svc_acct', { 'svcnum' => $_->svcnum } ) }
+ @cust_svc;
+ $list{$_}=1 foreach map { $_->email } @svc_acct;
+ }
+ keys %list;
+}
+
+=item invoicing_list_addpost
+
+Adds postal invoicing to this customer. If this customer is already configured
+to receive postal invoices, does nothing.
+
+=cut
+
+sub invoicing_list_addpost {
+ my $self = shift;
+ return if grep { $_ eq 'POST' } $self->invoicing_list;
+ my @invoicing_list = $self->invoicing_list;
+ push @invoicing_list, 'POST';
+ $self->invoicing_list(\@invoicing_list);
+}
+
+=item invoicing_list_emailonly
+
+Returns the list of email invoice recipients (invoicing_list without non-email
+destinations such as POST and FAX).
+
+=cut
+
+sub invoicing_list_emailonly {
+ my $self = shift;
+ warn "$me invoicing_list_emailonly called"
+ if $DEBUG;
+ grep { $_ !~ /^([A-Z]+)$/ } $self->invoicing_list;
+}
+
+=item invoicing_list_emailonly_scalar
+
+Returns the list of email invoice recipients (invoicing_list without non-email
+destinations such as POST and FAX) as a comma-separated scalar.
+
+=cut
+
+sub invoicing_list_emailonly_scalar {
+ my $self = shift;
+ warn "$me invoicing_list_emailonly_scalar called"
+ if $DEBUG;
+ join(', ', $self->invoicing_list_emailonly);
+}
+
+=item referral_cust_main [ DEPTH [ EXCLUDE_HASHREF ] ]
+
+Returns an array of customers referred by this customer (referral_custnum set
+to this custnum). If DEPTH is given, recurses up to the given depth, returning
+customers referred by customers referred by this customer and so on, inclusive.
+The default behavior is DEPTH 1 (no recursion).
+
+=cut
+
+sub referral_cust_main {
+ my $self = shift;
+ my $depth = @_ ? shift : 1;
+ my $exclude = @_ ? shift : {};
+
+ my @cust_main =
+ map { $exclude->{$_->custnum}++; $_; }
+ grep { ! $exclude->{ $_->custnum } }
+ qsearch( 'cust_main', { 'referral_custnum' => $self->custnum } );
+
+ if ( $depth > 1 ) {
+ push @cust_main,
+ map { $_->referral_cust_main($depth-1, $exclude) }
+ @cust_main;
+ }
+
+ @cust_main;
+}
+
+=item referral_cust_main_ncancelled
+
+Same as referral_cust_main, except only returns customers with uncancelled
+packages.
+
+=cut
+
+sub referral_cust_main_ncancelled {
+ my $self = shift;
+ grep { scalar($_->ncancelled_pkgs) } $self->referral_cust_main;
+}
+
+=item referral_cust_pkg [ DEPTH ]
+
+Like referral_cust_main, except returns a flat list of all unsuspended (and
+uncancelled) packages for each customer. The number of items in this list may
+be useful for comission calculations (perhaps after a C<grep { my $pkgpart = $_->pkgpart; grep { $_ == $pkgpart } @commission_worthy_pkgparts> } $cust_main-> ).
+
+=cut
+
+sub referral_cust_pkg {
+ my $self = shift;
+ my $depth = @_ ? shift : 1;
+
+ map { $_->unsuspended_pkgs }
+ grep { $_->unsuspended_pkgs }
+ $self->referral_cust_main($depth);
+}
+
+=item referring_cust_main
+
+Returns the single cust_main record for the customer who referred this customer
+(referral_custnum), or false.
+
+=cut
+
+sub referring_cust_main {
+ my $self = shift;
+ return '' unless $self->referral_custnum;
+ qsearchs('cust_main', { 'custnum' => $self->referral_custnum } );
+}
+
+=item credit AMOUNT, REASON [ , OPTION => VALUE ... ]
+
+Applies a credit to this customer. If there is an error, returns the error,
+otherwise returns false.
+
+REASON can be a text string, an FS::reason object, or a scalar reference to
+a reasonnum. If a text string, it will be automatically inserted as a new
+reason, and a 'reason_type' option must be passed to indicate the
+FS::reason_type for the new reason.
+
+An I<addlinfo> option may be passed to set the credit's I<addlinfo> field.
+
+Any other options are passed to FS::cust_credit::insert.
+
+=cut
+
+sub credit {
+ my( $self, $amount, $reason, %options ) = @_;
+
+ my $cust_credit = new FS::cust_credit {
+ 'custnum' => $self->custnum,
+ 'amount' => $amount,
+ };
+
+ if ( ref($reason) ) {
+
+ if ( ref($reason) eq 'SCALAR' ) {
+ $cust_credit->reasonnum( $$reason );
+ } else {
+ $cust_credit->reasonnum( $reason->reasonnum );
+ }
+
+ } else {
+ $cust_credit->set('reason', $reason)
+ }
+
+ $cust_credit->addlinfo( delete $options{'addlinfo'} )
+ if exists($options{'addlinfo'});
+
+ $cust_credit->insert(%options);
+
+}
+
+=item charge AMOUNT [ PKG [ COMMENT [ TAXCLASS ] ] ]
+
+Creates a one-time charge for this customer. If there is an error, returns
+the error, otherwise returns false.
+
+=cut
+
+sub charge {
+ my $self = shift;
+ my ( $amount, $quantity, $pkg, $comment, $classnum, $additional );
+ my ( $setuptax, $taxclass ); #internal taxes
+ my ( $taxproduct, $override ); #vendor (CCH) taxes
+ if ( ref( $_[0] ) ) {
+ $amount = $_[0]->{amount};
+ $quantity = exists($_[0]->{quantity}) ? $_[0]->{quantity} : 1;
+ $pkg = exists($_[0]->{pkg}) ? $_[0]->{pkg} : 'One-time charge';
+ $comment = exists($_[0]->{comment}) ? $_[0]->{comment}
+ : '$'. sprintf("%.2f",$amount);
+ $setuptax = exists($_[0]->{setuptax}) ? $_[0]->{setuptax} : '';
+ $taxclass = exists($_[0]->{taxclass}) ? $_[0]->{taxclass} : '';
+ $classnum = exists($_[0]->{classnum}) ? $_[0]->{classnum} : '';
+ $additional = $_[0]->{additional};
+ $taxproduct = $_[0]->{taxproductnum};
+ $override = { '' => $_[0]->{tax_override} };
+ }else{
+ $amount = shift;
+ $quantity = 1;
+ $pkg = @_ ? shift : 'One-time charge';
+ $comment = @_ ? shift : '$'. sprintf("%.2f",$amount);
+ $setuptax = '';
+ $taxclass = @_ ? shift : '';
+ $additional = [];
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $part_pkg = new FS::part_pkg ( {
+ 'pkg' => $pkg,
+ 'comment' => $comment,
+ 'plan' => 'flat',
+ 'freq' => 0,
+ 'disabled' => 'Y',
+ 'classnum' => $classnum ? $classnum : '',
+ 'setuptax' => $setuptax,
+ 'taxclass' => $taxclass,
+ 'taxproductnum' => $taxproduct,
+ } );
+
+ my %options = ( ( map { ("additional_info$_" => $additional->[$_] ) }
+ ( 0 .. @$additional - 1 )
+ ),
+ 'additional_count' => scalar(@$additional),
+ 'setup_fee' => $amount,
+ );
+
+ my $error = $part_pkg->insert( options => \%options,
+ tax_overrides => $override,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkgpart = $part_pkg->pkgpart;
+ my %type_pkgs = ( 'typenum' => $self->agent->typenum, 'pkgpart' => $pkgpart );
+ unless ( qsearchs('type_pkgs', \%type_pkgs ) ) {
+ my $type_pkgs = new FS::type_pkgs \%type_pkgs;
+ $error = $type_pkgs->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $cust_pkg = new FS::cust_pkg ( {
+ 'custnum' => $self->custnum,
+ 'pkgpart' => $pkgpart,
+ 'quantity' => $quantity,
+ } );
+
+ $error = $cust_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+#=item charge_postal_fee
+#
+#Applies a one time charge this customer. If there is an error,
+#returns the error, returns the cust_pkg charge object or false
+#if there was no charge.
+#
+#=cut
+#
+# This should be a customer event. For that to work requires that bill
+# also be a customer event.
+
+sub charge_postal_fee {
+ my $self = shift;
+
+ my $pkgpart = $conf->config('postal_invoice-fee_pkgpart');
+ return '' unless ($pkgpart && grep { $_ eq 'POST' } $self->invoicing_list);
+
+ my $cust_pkg = new FS::cust_pkg ( {
+ 'custnum' => $self->custnum,
+ 'pkgpart' => $pkgpart,
+ 'quantity' => 1,
+ } );
+
+ my $error = $cust_pkg->insert;
+ $error ? $error : $cust_pkg;
+}
+
+=item cust_bill
+
+Returns all the invoices (see L<FS::cust_bill>) for this customer.
+
+=cut
+
+sub cust_bill {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch('cust_bill', { 'custnum' => $self->custnum, } )
+}
+
+=item open_cust_bill
+
+Returns all the open (owed > 0) invoices (see L<FS::cust_bill>) for this
+customer.
+
+=cut
+
+sub open_cust_bill {
+ my $self = shift;
+ grep { $_->owed > 0 } $self->cust_bill;
+}
+
+=item cust_credit
+
+Returns all the credits (see L<FS::cust_credit>) for this customer.
+
+=cut
+
+sub cust_credit {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_credit', { 'custnum' => $self->custnum } )
+}
+
+=item cust_pay
+
+Returns all the payments (see L<FS::cust_pay>) for this customer.
+
+=cut
+
+sub cust_pay {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay', { 'custnum' => $self->custnum } )
+}
+
+=item cust_pay_void
+
+Returns all voided payments (see L<FS::cust_pay_void>) for this customer.
+
+=cut
+
+sub cust_pay_void {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay_void', { 'custnum' => $self->custnum } )
+}
+
+=item cust_pay_batch
+
+Returns all batched payments (see L<FS::cust_pay_void>) for this customer.
+
+=cut
+
+sub cust_pay_batch {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay_batch', { 'custnum' => $self->custnum } )
+}
+
+=item cust_pay_pending
+
+Returns all pending payments (see L<FS::cust_pay_pending>) for this customer
+(without status "done").
+
+=cut
+
+sub cust_pay_pending {
+ my $self = shift;
+ return $self->num_cust_pay_pending unless wantarray;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay_pending', {
+ 'custnum' => $self->custnum,
+ 'status' => { op=>'!=', value=>'done' },
+ },
+ );
+}
+
+=item num_cust_pay_pending
+
+Returns the number of pending payments (see L<FS::cust_pay_pending>) for this
+customer (without status "done"). Also called automatically when the
+cust_pay_pending method is used in a scalar context.
+
+=cut
+
+sub num_cust_pay_pending {
+ my $self = shift;
+ my $sql = " SELECT COUNT(*) FROM cust_pay_pending ".
+ " WHERE custnum = ? AND status != 'done' ";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute($self->custnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item cust_refund
+
+Returns all the refunds (see L<FS::cust_refund>) for this customer.
+
+=cut
+
+sub cust_refund {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_refund', { 'custnum' => $self->custnum } )
+}
+
+=item display_custnum
+
+Returns the displayed customer number for this customer: agent_custid if
+cust_main-default_agent_custid is set and it has a value, custnum otherwise.
+
+=cut
+
+sub display_custnum {
+ my $self = shift;
+ if ( $conf->exists('cust_main-default_agent_custid') && $self->agent_custid ){
+ return $self->agent_custid;
+ } else {
+ return $self->custnum;
+ }
+}
+
+=item name
+
+Returns a name string for this customer, either "Company (Last, First)" or
+"Last, First".
+
+=cut
+
+sub name {
+ my $self = shift;
+ my $name = $self->contact;
+ $name = $self->company. " ($name)" if $self->company;
+ $name;
+}
+
+=item ship_name
+
+Returns a name string for this (service/shipping) contact, either
+"Company (Last, First)" or "Last, First".
+
+=cut
+
+sub ship_name {
+ my $self = shift;
+ if ( $self->get('ship_last') ) {
+ my $name = $self->ship_contact;
+ $name = $self->ship_company. " ($name)" if $self->ship_company;
+ $name;
+ } else {
+ $self->name;
+ }
+}
+
+=item name_short
+
+Returns a name string for this customer, either "Company" or "First Last".
+
+=cut
+
+sub name_short {
+ my $self = shift;
+ $self->company !~ /^\s*$/ ? $self->company : $self->contact_firstlast;
+}
+
+=item ship_name_short
+
+Returns a name string for this (service/shipping) contact, either "Company"
+or "First Last".
+
+=cut
+
+sub ship_name_short {
+ my $self = shift;
+ if ( $self->get('ship_last') ) {
+ $self->ship_company !~ /^\s*$/
+ ? $self->ship_company
+ : $self->ship_contact_firstlast;
+ } else {
+ $self->name_company_or_firstlast;
+ }
+}
+
+=item contact
+
+Returns this customer's full (billing) contact name only, "Last, First"
+
+=cut
+
+sub contact {
+ my $self = shift;
+ $self->get('last'). ', '. $self->first;
+}
+
+=item ship_contact
+
+Returns this customer's full (shipping) contact name only, "Last, First"
+
+=cut
+
+sub ship_contact {
+ my $self = shift;
+ $self->get('ship_last')
+ ? $self->get('ship_last'). ', '. $self->ship_first
+ : $self->contact;
+}
+
+=item contact_firstlast
+
+Returns this customers full (billing) contact name only, "First Last".
+
+=cut
+
+sub contact_firstlast {
+ my $self = shift;
+ $self->first. ' '. $self->get('last');
+}
+
+=item ship_contact_firstlast
+
+Returns this customer's full (shipping) contact name only, "First Last".
+
+=cut
+
+sub ship_contact_firstlast {
+ my $self = shift;
+ $self->get('ship_last')
+ ? $self->first. ' '. $self->get('ship_last')
+ : $self->contact_firstlast;
+}
+
+=item country_full
+
+Returns this customer's full country name
+
+=cut
+
+sub country_full {
+ my $self = shift;
+ code2country($self->country);
+}
+
+=item geocode DATA_VENDOR
+
+Returns a value for the customer location as encoded by DATA_VENDOR.
+Currently this only makes sense for "CCH" as DATA_VENDOR.
+
+=cut
+
+sub geocode {
+ my ($self, $data_vendor) = (shift, shift); #always cch for now
+
+ my $geocode = $self->get('geocode'); #XXX only one data_vendor for geocode
+ return $geocode if $geocode;
+
+ my $prefix = ( $conf->exists('tax-ship_address') && length($self->ship_last) )
+ ? 'ship_'
+ : '';
+
+ my ($zip,$plus4) = split /-/, $self->get("${prefix}zip")
+ if $self->country eq 'US';
+
+ #CCH specific location stuff
+ my $extra_sql = "AND plus4lo <= '$plus4' AND plus4hi >= '$plus4'";
+
+ my @cust_tax_location =
+ qsearch( {
+ 'table' => 'cust_tax_location',
+ 'hashref' => { 'zip' => $zip, 'data_vendor' => $data_vendor },
+ 'extra_sql' => $extra_sql,
+ 'order_by' => 'ORDER BY plus4hi',#overlapping with distinct ends
+ }
+ );
+ $geocode = $cust_tax_location[0]->geocode
+ if scalar(@cust_tax_location);
+
+ $geocode;
+}
+
+=item cust_status
+
+=item status
+
+Returns a status string for this customer, currently:
+
+=over 4
+
+=item prospect - No packages have ever been ordered
+
+=item active - One or more recurring packages is active
+
+=item inactive - No active recurring packages, but otherwise unsuspended/uncancelled (the inactive status is new - previously inactive customers were mis-identified as cancelled)
+
+=item suspended - All non-cancelled recurring packages are suspended
+
+=item cancelled - All recurring packages are cancelled
+
+=back
+
+=cut
+
+sub status { shift->cust_status(@_); }
+
+sub cust_status {
+ my $self = shift;
+ for my $status (qw( prospect active inactive suspended cancelled )) {
+ my $method = $status.'_sql';
+ my $numnum = ( my $sql = $self->$method() ) =~ s/cust_main\.custnum/?/g;
+ my $sth = dbh->prepare("SELECT $sql") or die dbh->errstr;
+ $sth->execute( ($self->custnum) x $numnum )
+ or die "Error executing 'SELECT $sql': ". $sth->errstr;
+ return $status if $sth->fetchrow_arrayref->[0];
+ }
+}
+
+=item ucfirst_cust_status
+
+=item ucfirst_status
+
+Returns the status with the first character capitalized.
+
+=cut
+
+sub ucfirst_status { shift->ucfirst_cust_status(@_); }
+
+sub ucfirst_cust_status {
+ my $self = shift;
+ ucfirst($self->cust_status);
+}
+
+=item statuscolor
+
+Returns a hex triplet color string for this customer's status.
+
+=cut
+
+use vars qw(%statuscolor);
+tie %statuscolor, 'Tie::IxHash',
+ 'prospect' => '7e0079', #'000000', #black? naw, purple
+ 'active' => '00CC00', #green
+ 'inactive' => '0000CC', #blue
+ 'suspended' => 'FF9900', #yellow
+ 'cancelled' => 'FF0000', #red
+;
+
+sub statuscolor { shift->cust_statuscolor(@_); }
+
+sub cust_statuscolor {
+ my $self = shift;
+ $statuscolor{$self->cust_status};
+}
+
+=item tickets
+
+Returns an array of hashes representing the customer's RT tickets.
+
+=cut
+
+sub tickets {
+ my $self = shift;
+
+ my $num = $conf->config('cust_main-max_tickets') || 10;
+ my @tickets = ();
+
+ if ( $conf->config('ticket_system') ) {
+ unless ( $conf->config('ticket_system-custom_priority_field') ) {
+
+ @tickets = @{ FS::TicketSystem->customer_tickets($self->custnum, $num) };
+
+ } else {
+
+ foreach my $priority (
+ $conf->config('ticket_system-custom_priority_field-values'), ''
+ ) {
+ last if scalar(@tickets) >= $num;
+ push @tickets,
+ @{ FS::TicketSystem->customer_tickets( $self->custnum,
+ $num - scalar(@tickets),
+ $priority,
+ )
+ };
+ }
+ }
+ }
+ (@tickets);
+}
+
+# Return services representing svc_accts in customer support packages
+sub support_services {
+ my $self = shift;
+ my %packages = map { $_ => 1 } $conf->config('support_packages');
+
+ grep { $_->pkg_svc && $_->pkg_svc->primary_svc eq 'Y' }
+ grep { $_->part_svc->svcdb eq 'svc_acct' }
+ map { $_->cust_svc }
+ grep { exists $packages{ $_->pkgpart } }
+ $self->ncancelled_pkgs;
+
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item statuses
+
+Class method that returns the list of possible status strings for customers
+(see L<the status method|/status>). For example:
+
+ @statuses = FS::cust_main->statuses();
+
+=cut
+
+sub statuses {
+ #my $self = shift; #could be class...
+ keys %statuscolor;
+}
+
+=item prospect_sql
+
+Returns an SQL expression identifying prospective cust_main records (customers
+with no packages ever ordered)
+
+=cut
+
+use vars qw($select_count_pkgs);
+$select_count_pkgs =
+ "SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum";
+
+sub select_count_pkgs_sql {
+ $select_count_pkgs;
+}
+
+sub prospect_sql { "
+ 0 = ( $select_count_pkgs )
+"; }
+
+=item active_sql
+
+Returns an SQL expression identifying active cust_main records (customers with
+active recurring packages).
+
+=cut
+
+sub active_sql { "
+ 0 < ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. "
+ )
+"; }
+
+=item inactive_sql
+
+Returns an SQL expression identifying inactive cust_main records (customers with
+no active recurring packages, but otherwise unsuspended/uncancelled).
+
+=cut
+
+sub inactive_sql { "
+ 0 = ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. " )
+ AND
+ 0 < ( $select_count_pkgs AND ". FS::cust_pkg->inactive_sql. " )
+"; }
+
+=item susp_sql
+=item suspended_sql
+
+Returns an SQL expression identifying suspended cust_main records.
+
+=cut
+
+
+sub suspended_sql { susp_sql(@_); }
+sub susp_sql { "
+ 0 < ( $select_count_pkgs AND ". FS::cust_pkg->suspended_sql. " )
+ AND
+ 0 = ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. " )
+"; }
+
+=item cancel_sql
+=item cancelled_sql
+
+Returns an SQL expression identifying cancelled cust_main records.
+
+=cut
+
+sub cancelled_sql { cancel_sql(@_); }
+sub cancel_sql {
+
+ my $recurring_sql = FS::cust_pkg->recurring_sql;
+ my $cancelled_sql = FS::cust_pkg->cancelled_sql;
+
+ "
+ 0 < ( $select_count_pkgs )
+ AND 0 < ( $select_count_pkgs AND $recurring_sql AND $cancelled_sql )
+ AND 0 = ( $select_count_pkgs AND $recurring_sql
+ AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ )
+ AND 0 = ( $select_count_pkgs AND ". FS::cust_pkg->inactive_sql. " )
+ ";
+
+}
+
+=item uncancel_sql
+=item uncancelled_sql
+
+Returns an SQL expression identifying un-cancelled cust_main records.
+
+=cut
+
+sub uncancelled_sql { uncancel_sql(@_); }
+sub uncancel_sql { "
+ ( 0 < ( $select_count_pkgs
+ AND ( cust_pkg.cancel IS NULL
+ OR cust_pkg.cancel = 0
+ )
+ )
+ OR 0 = ( $select_count_pkgs )
+ )
+"; }
+
+=item balance_sql
+
+Returns an SQL fragment to retreive the balance.
+
+=cut
+
+sub balance_sql { "
+ ( SELECT COALESCE( SUM(charged), 0 ) FROM cust_bill
+ WHERE cust_bill.custnum = cust_main.custnum )
+ - ( SELECT COALESCE( SUM(paid), 0 ) FROM cust_pay
+ WHERE cust_pay.custnum = cust_main.custnum )
+ - ( SELECT COALESCE( SUM(amount), 0 ) FROM cust_credit
+ WHERE cust_credit.custnum = cust_main.custnum )
+ + ( SELECT COALESCE( SUM(refund), 0 ) FROM cust_refund
+ WHERE cust_refund.custnum = cust_main.custnum )
+"; }
+
+=item balance_date_sql START_TIME [ END_TIME [ OPTION => VALUE ... ] ]
+
+Returns an SQL fragment to retreive the balance for this customer, only
+considering invoices with date earlier than START_TIME, and optionally not
+later than END_TIME (total_owed_date minus total_unapplied_credits minus
+total_unapplied_payments).
+
+Times are specified as SQL fragments or numeric
+UNIX timestamps; see L<perlfunc/"time">). Also see L<Time::Local> and
+L<Date::Parse> for conversion functions. The empty string can be passed
+to disable that time constraint completely.
+
+Available options are:
+
+=over 4
+
+=item unapplied_date
+
+set to true to disregard unapplied credits, payments and refunds outside the specified time period - by default the time period restriction only applies to invoices (useful for reporting, probably a bad idea for event triggering)
+
+=item total
+
+(unused. obsolete?)
+set to true to remove all customer comparison clauses, for totals
+
+=item where
+
+(unused. obsolete?)
+WHERE clause hashref (elements "AND"ed together) (typically used with the total option)
+
+=item join
+
+(unused. obsolete?)
+JOIN clause (typically used with the total option)
+
+=back
+
+=cut
+
+sub balance_date_sql {
+ my( $class, $start, $end, %opt ) = @_;
+
+ my $owed = FS::cust_bill->owed_sql;
+ my $unapp_refund = FS::cust_refund->unapplied_sql;
+ my $unapp_credit = FS::cust_credit->unapplied_sql;
+ my $unapp_pay = FS::cust_pay->unapplied_sql;
+
+ my $j = $opt{'join'} || '';
+
+ my $owed_wh = $class->_money_table_where( 'cust_bill', $start,$end,%opt );
+ my $refund_wh = $class->_money_table_where( 'cust_refund', $start,$end,%opt );
+ my $credit_wh = $class->_money_table_where( 'cust_credit', $start,$end,%opt );
+ my $pay_wh = $class->_money_table_where( 'cust_pay', $start,$end,%opt );
+
+ " ( SELECT COALESCE(SUM($owed), 0) FROM cust_bill $j $owed_wh )
+ + ( SELECT COALESCE(SUM($unapp_refund), 0) FROM cust_refund $j $refund_wh )
+ - ( SELECT COALESCE(SUM($unapp_credit), 0) FROM cust_credit $j $credit_wh )
+ - ( SELECT COALESCE(SUM($unapp_pay), 0) FROM cust_pay $j $pay_wh )
+ ";
+
+}
+
+=item _money_table_where TABLE START_TIME [ END_TIME [ OPTION => VALUE ... ] ]
+
+Helper method for balance_date_sql; name (and usage) subject to change
+(suggestions welcome).
+
+Returns a WHERE clause for the specified monetary TABLE (cust_bill,
+cust_refund, cust_credit or cust_pay).
+
+If TABLE is "cust_bill" or the unapplied_date option is true, only
+considers records with date earlier than START_TIME, and optionally not
+later than END_TIME .
+
+=cut
+
+sub _money_table_where {
+ my( $class, $table, $start, $end, %opt ) = @_;
+
+ my @where = ();
+ push @where, "cust_main.custnum = $table.custnum" unless $opt{'total'};
+ if ( $table eq 'cust_bill' || $opt{'unapplied_date'} ) {
+ push @where, "$table._date <= $start" if defined($start) && length($start);
+ push @where, "$table._date > $end" if defined($end) && length($end);
+ }
+ push @where, @{$opt{'where'}} if $opt{'where'};
+ my $where = scalar(@where) ? 'WHERE '. join(' AND ', @where ) : '';
+
+ $where;
+
+}
+
+=item search_sql HASHREF
+
+(Class method)
+
+Returns a qsearch hash expression to search for parameters specified in HREF.
+Valid parameters are
+
+=over 4
+
+=item agentnum
+
+=item status
+
+=item cancelled_pkgs
+
+bool
+
+=item signupdate
+
+listref of start date, end date
+
+=item payby
+
+listref
+
+=item current_balance
+
+listref (list returned by FS::UI::Web::parse_lt_gt($cgi, 'current_balance'))
+
+=item cust_fields
+
+=item flattened_pkgs
+
+bool
+
+=back
+
+=cut
+
+sub search_sql {
+ my ($class, $params) = @_;
+
+ my $dbh = dbh;
+
+ my @where = ();
+ my $orderby;
+
+ ##
+ # parse agent
+ ##
+
+ if ( $params->{'agentnum'} =~ /^(\d+)$/ and $1 ) {
+ push @where,
+ "cust_main.agentnum = $1";
+ }
+
+ ##
+ # parse status
+ ##
+
+ #prospect active inactive suspended cancelled
+ if ( grep { $params->{'status'} eq $_ } FS::cust_main->statuses() ) {
+ my $method = $params->{'status'}. '_sql';
+ #push @where, $class->$method();
+ push @where, FS::cust_main->$method();
+ }
+
+ ##
+ # parse cancelled package checkbox
+ ##
+
+ my $pkgwhere = "";
+
+ $pkgwhere .= "AND (cancel = 0 or cancel is null)"
+ unless $params->{'cancelled_pkgs'};
+
+ ##
+ # dates
+ ##
+
+ foreach my $field (qw( signupdate )) {
+
+ next unless exists($params->{$field});
+
+ my($beginning, $ending) = @{$params->{$field}};
+
+ push @where,
+ "cust_main.$field IS NOT NULL",
+ "cust_main.$field >= $beginning",
+ "cust_main.$field <= $ending";
+
+ $orderby ||= "ORDER BY cust_main.$field";
+
+ }
+
+ ###
+ # payby
+ ###
+
+ my @payby = grep /^([A-Z]{4})$/, @{ $params->{'payby'} };
+ if ( @payby ) {
+ push @where, '( '. join(' OR ', map "cust_main.payby = '$_'", @payby). ' )';
+ }
+
+ ##
+ # amounts
+ ##
+
+ #my $balance_sql = $class->balance_sql();
+ my $balance_sql = FS::cust_main->balance_sql();
+
+ push @where, map { s/current_balance/$balance_sql/; $_ }
+ @{ $params->{'current_balance'} };
+
+ ##
+ # custbatch
+ ##
+
+ if ( $params->{'custbatch'} =~ /^([\w\/\-\:\.]+)$/ and $1 ) {
+ push @where,
+ "cust_main.custbatch = '$1'";
+ }
+
+ ##
+ # setup queries, subs, etc. for the search
+ ##
+
+ $orderby ||= 'ORDER BY custnum';
+
+ # here is the agent virtualization
+ push @where, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+ my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
+
+ my $addl_from = 'LEFT JOIN cust_pkg USING ( custnum ) ';
+
+ my $count_query = "SELECT COUNT(*) FROM cust_main $extra_sql";
+
+ my $select = join(', ',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields($params->{'cust_fields'}),
+ );
+
+ my(@extra_headers) = ();
+ my(@extra_fields) = ();
+
+ if ($params->{'flattened_pkgs'}) {
+
+ if ($dbh->{Driver}->{Name} eq 'Pg') {
+
+ $select .= ", array_to_string(array(select pkg from cust_pkg left join part_pkg using ( pkgpart ) where cust_main.custnum = cust_pkg.custnum $pkgwhere),'|') as magic";
+
+ }elsif ($dbh->{Driver}->{Name} =~ /^mysql/i) {
+ $select .= ", GROUP_CONCAT(pkg SEPARATOR '|') as magic";
+ $addl_from .= " LEFT JOIN part_pkg using ( pkgpart )";
+ }else{
+ warn "warning: unknown database type ". $dbh->{Driver}->{Name}.
+ "omitting packing information from report.";
+ }
+
+ my $header_query = "SELECT COUNT(cust_pkg.custnum = cust_main.custnum) AS count FROM cust_main $addl_from $extra_sql $pkgwhere group by cust_main.custnum order by count desc limit 1";
+
+ my $sth = dbh->prepare($header_query) or die dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+ my $headerrow = $sth->fetchrow_arrayref;
+ my $headercount = $headerrow ? $headerrow->[0] : 0;
+ while($headercount) {
+ unshift @extra_headers, "Package ". $headercount;
+ unshift @extra_fields, eval q!sub {my $c = shift;
+ my @a = split '\|', $c->magic;
+ my $p = $a[!.--$headercount. q!];
+ $p;
+ };!;
+ }
+
+ }
+
+ my $sql_query = {
+ 'table' => 'cust_main',
+ 'select' => $select,
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $orderby,
+ 'count_query' => $count_query,
+ 'extra_headers' => \@extra_headers,
+ 'extra_fields' => \@extra_fields,
+ };
+
+}
+
+=item email_search_sql HASHREF
+
+(Class method)
+
+Emails a notice to the specified customers.
+
+Valid parameters are those of the L<search_sql> method, plus the following:
+
+=over 4
+
+=item from
+
+From: address
+
+=item subject
+
+Email Subject:
+
+=item html_body
+
+HTML body
+
+=item text_body
+
+Text body
+
+=item job
+
+Optional job queue job for status updates.
+
+=back
+
+Returns an error message, or false for success.
+
+If an error occurs during any email, stops the enture send and returns that
+error. Presumably if you're getting SMTP errors aborting is better than
+retrying everything.
+
+=cut
+
+sub email_search_sql {
+ my($class, $params) = @_;
+
+ my $from = delete $params->{from};
+ my $subject = delete $params->{subject};
+ my $html_body = delete $params->{html_body};
+ my $text_body = delete $params->{text_body};
+
+ my $job = delete $params->{'job'};
+
+ my $sql_query = $class->search_sql($params);
+
+ my $count_query = delete($sql_query->{'count_query'});
+ my $count_sth = dbh->prepare($count_query)
+ or die "Error preparing $count_query: ". dbh->errstr;
+ $count_sth->execute
+ or die "Error executing $count_query: ". $count_sth->errstr;
+ my $count_arrayref = $count_sth->fetchrow_arrayref;
+ my $num_cust = $count_arrayref->[0];
+
+ #my @extra_headers = @{ delete($sql_query->{'extra_headers'}) };
+ #my @extra_fields = @{ delete($sql_query->{'extra_fields'}) };
+
+
+ my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
+
+ #eventually order+limit magic to reduce memory use?
+ foreach my $cust_main ( qsearch($sql_query) ) {
+
+ my $to = $cust_main->invoicing_list_emailonly_scalar;
+ next unless $to;
+
+ my $error = send_email(
+ generate_email(
+ 'from' => $from,
+ 'to' => $to,
+ 'subject' => $subject,
+ 'html_body' => $html_body,
+ 'text_body' => $text_body,
+ )
+ );
+ return $error if $error;
+
+ if ( $job ) { #progressbar foo
+ $num++;
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $num / $num_cust )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ }
+
+ return '';
+}
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process_email_search_sql {
+ my $job = shift;
+ #warn "$me process_re_X $method for job $job\n" if $DEBUG;
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ $param->{'job'} = $job;
+
+ my $error = FS::cust_main->email_search_sql( $param );
+ die $error if $error;
+
+}
+
+=item fuzzy_search FUZZY_HASHREF [ HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ ]
+
+Performs a fuzzy (approximate) search and returns the matching FS::cust_main
+records. Currently, I<first>, I<last> and/or I<company> may be specified (the
+appropriate ship_ field is also searched).
+
+Additional options are the same as FS::Record::qsearch
+
+=cut
+
+sub fuzzy_search {
+ my( $self, $fuzzy, $hash, @opt) = @_;
+ #$self
+ $hash ||= {};
+ my @cust_main = ();
+
+ check_and_rebuild_fuzzyfiles();
+ foreach my $field ( keys %$fuzzy ) {
+
+ my $all = $self->all_X($field);
+ next unless scalar(@$all);
+
+ my %match = ();
+ $match{$_}=1 foreach ( amatch( $fuzzy->{$field}, ['i'], @$all ) );
+
+ my @fcust = ();
+ foreach ( keys %match ) {
+ push @fcust, qsearch('cust_main', { %$hash, $field=>$_}, @opt);
+ push @fcust, qsearch('cust_main', { %$hash, "ship_$field"=>$_}, @opt);
+ }
+ my %fsaw = ();
+ push @cust_main, grep { ! $fsaw{$_->custnum}++ } @fcust;
+ }
+
+ # we want the components of $fuzzy ANDed, not ORed, but still don't want dupes
+ my %saw = ();
+ @cust_main = grep { ++$saw{$_->custnum} == scalar(keys %$fuzzy) } @cust_main;
+
+ @cust_main;
+
+}
+
+=item masked FIELD
+
+Returns a masked version of the named field
+
+=cut
+
+sub masked {
+my ($self,$field) = @_;
+
+# Show last four
+
+'x'x(length($self->getfield($field))-4).
+ substr($self->getfield($field), (length($self->getfield($field))-4));
+
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item smart_search OPTION => VALUE ...
+
+Accepts the following options: I<search>, the string to search for. The string
+will be searched for as a customer number, phone number, name or company name,
+as an exact, or, in some cases, a substring or fuzzy match (see the source code
+for the exact heuristics used); I<no_fuzzy_on_exact>, causes smart_search to
+skip fuzzy matching when an exact match is found.
+
+Any additional options are treated as an additional qualifier on the search
+(i.e. I<agentnum>).
+
+Returns a (possibly empty) array of FS::cust_main objects.
+
+=cut
+
+sub smart_search {
+ my %options = @_;
+
+ #here is the agent virtualization
+ my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+ my @cust_main = ();
+
+ my $skip_fuzzy = delete $options{'no_fuzzy_on_exact'};
+ my $search = delete $options{'search'};
+ ( my $alphanum_search = $search ) =~ s/\W//g;
+
+ if ( $alphanum_search =~ /^1?(\d{3})(\d{3})(\d{4})(\d*)$/ ) { #phone# search
+
+ #false laziness w/Record::ut_phone
+ my $phonen = "$1-$2-$3";
+ $phonen .= " x$4" if $4;
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { %options },
+ 'extra_sql' => ( scalar(keys %options) ? ' AND ' : ' WHERE ' ).
+ ' ( '.
+ join(' OR ', map "$_ = '$phonen'",
+ qw( daytime night fax
+ ship_daytime ship_night ship_fax )
+ ).
+ ' ) '.
+ " AND $agentnums_sql", #agent virtualization
+ } );
+
+ unless ( @cust_main || $phonen =~ /x\d+$/ ) { #no exact match
+ #try looking for matches with extensions unless one was specified
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { %options },
+ 'extra_sql' => ( scalar(keys %options) ? ' AND ' : ' WHERE ' ).
+ ' ( '.
+ join(' OR ', map "$_ LIKE '$phonen\%'",
+ qw( daytime night
+ ship_daytime ship_night )
+ ).
+ ' ) '.
+ " AND $agentnums_sql", #agent virtualization
+ } );
+
+ }
+
+ # custnum search (also try agent_custid), with some tweaking options if your
+ # legacy cust "numbers" have letters
+ }
+
+ if ( $search =~ /^\s*(\d+)\s*$/
+ || ( $conf->config('cust_main-agent_custid-format') eq 'ww?d+'
+ && $search =~ /^\s*(\w\w?\d+)\s*$/
+ )
+ )
+ {
+
+ my $num = $1;
+
+ if ( $num <= 2147483647 ) { #need a bigint custnum? wow.
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $num, %options },
+ 'extra_sql' => " AND $agentnums_sql", #agent virtualization
+ } );
+ }
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'agent_custid' => $num, %options },
+ 'extra_sql' => " AND $agentnums_sql", #agent virtualization
+ } );
+
+ } elsif ( $search =~ /^\s*(\S.*\S)\s+\((.+), ([^,]+)\)\s*$/ ) {
+
+ my($company, $last, $first) = ( $1, $2, $3 );
+
+ # "Company (Last, First)"
+ #this is probably something a browser remembered,
+ #so just do an exact search
+
+ foreach my $prefix ( '', 'ship_' ) {
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { $prefix.'first' => $first,
+ $prefix.'last' => $last,
+ $prefix.'company' => $company,
+ %options,
+ },
+ 'extra_sql' => " AND $agentnums_sql",
+ } );
+ }
+
+ } elsif ( $search =~ /^\s*(\S.*\S)\s*$/ ) { # value search
+ # try (ship_){last,company}
+
+ my $value = lc($1);
+
+ # # remove "(Last, First)" in "Company (Last, First)", otherwise the
+ # # full strings the browser remembers won't work
+ # $value =~ s/\([\w \,\.\-\']*\)$//; #false laziness w/Record::ut_name
+
+ use Lingua::EN::NameParse;
+ my $NameParse = new Lingua::EN::NameParse(
+ auto_clean => 1,
+ allow_reversed => 1,
+ );
+
+ my($last, $first) = ( '', '' );
+ #maybe disable this too and just rely on NameParse?
+ if ( $value =~ /^(.+),\s*([^,]+)$/ ) { # Last, First
+
+ ($last, $first) = ( $1, $2 );
+
+ #} elsif ( $value =~ /^(.+)\s+(.+)$/ ) {
+ } elsif ( ! $NameParse->parse($value) ) {
+
+ my %name = $NameParse->components;
+ $first = $name{'given_name_1'};
+ $last = $name{'surname_1'};
+
+ }
+
+ if ( $first && $last ) {
+
+ my($q_last, $q_first) = ( dbh->quote($last), dbh->quote($first) );
+
+ #exact
+ my $sql = scalar(keys %options) ? ' AND ' : ' WHERE ';
+ $sql .= "
+ ( ( LOWER(last) = $q_last AND LOWER(first) = $q_first )
+ OR ( LOWER(ship_last) = $q_last AND LOWER(ship_first) = $q_first )
+ )";
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => \%options,
+ 'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization
+ } );
+
+ # or it just be something that was typed in... (try that in a sec)
+
+ }
+
+ my $q_value = dbh->quote($value);
+
+ #exact
+ my $sql = scalar(keys %options) ? ' AND ' : ' WHERE ';
+ $sql .= " ( LOWER(last) = $q_value
+ OR LOWER(company) = $q_value
+ OR LOWER(ship_last) = $q_value
+ OR LOWER(ship_company) = $q_value
+ )";
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => \%options,
+ 'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization
+ } );
+
+ #no exact match, trying substring/fuzzy
+ #always do substring & fuzzy (unless they're explicity config'ed off)
+ #getting complaints searches are not returning enough
+ unless ( @cust_main && $skip_fuzzy || $conf->exists('disable-fuzzy') ) {
+
+ #still some false laziness w/search_sql (was search/cust_main.cgi)
+
+ #substring
+
+ my @hashrefs = (
+ { 'company' => { op=>'ILIKE', value=>"%$value%" }, },
+ { 'ship_company' => { op=>'ILIKE', value=>"%$value%" }, },
+ );
+
+ if ( $first && $last ) {
+
+ push @hashrefs,
+ { 'first' => { op=>'ILIKE', value=>"%$first%" },
+ 'last' => { op=>'ILIKE', value=>"%$last%" },
+ },
+ { 'ship_first' => { op=>'ILIKE', value=>"%$first%" },
+ 'ship_last' => { op=>'ILIKE', value=>"%$last%" },
+ },
+ ;
+
+ } else {
+
+ push @hashrefs,
+ { 'last' => { op=>'ILIKE', value=>"%$value%" }, },
+ { 'ship_last' => { op=>'ILIKE', value=>"%$value%" }, },
+ ;
+ }
+
+ foreach my $hashref ( @hashrefs ) {
+
+ push @cust_main, qsearch( {
+ 'table' => 'cust_main',
+ 'hashref' => { %$hashref,
+ %options,
+ },
+ 'extra_sql' => " AND $agentnums_sql", #agent virtualizaiton
+ } );
+
+ }
+
+ #fuzzy
+ my @fuzopts = (
+ \%options, #hashref
+ '', #select
+ " AND $agentnums_sql", #extra_sql #agent virtualization
+ );
+
+ if ( $first && $last ) {
+ push @cust_main, FS::cust_main->fuzzy_search(
+ { 'last' => $last, #fuzzy hashref
+ 'first' => $first }, #
+ @fuzopts
+ );
+ }
+ foreach my $field ( 'last', 'company' ) {
+ push @cust_main,
+ FS::cust_main->fuzzy_search( { $field => $value }, @fuzopts );
+ }
+
+ }
+
+ #eliminate duplicates
+ my %saw = ();
+ @cust_main = grep { !$saw{$_->custnum}++ } @cust_main;
+
+ }
+
+ @cust_main;
+
+}
+
+=item email_search
+
+Accepts the following options: I<email>, the email address to search for. The
+email address will be searched for as an email invoice destination and as an
+svc_acct account.
+
+#Any additional options are treated as an additional qualifier on the search
+#(i.e. I<agentnum>).
+
+Returns a (possibly empty) array of FS::cust_main objects (but usually just
+none or one).
+
+=cut
+
+sub email_search {
+ my %options = @_;
+
+ local($DEBUG) = 1;
+
+ my $email = delete $options{'email'};
+
+ #we're only being used by RT at the moment... no agent virtualization yet
+ #my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+ my @cust_main = ();
+
+ if ( $email =~ /([^@]+)\@([^@]+)/ ) {
+
+ my ( $user, $domain ) = ( $1, $2 );
+
+ warn "$me smart_search: searching for $user in domain $domain"
+ if $DEBUG;
+
+ push @cust_main,
+ map $_->cust_main,
+ qsearch( {
+ 'table' => 'cust_main_invoice',
+ 'hashref' => { 'dest' => $email },
+ }
+ );
+
+ push @cust_main,
+ map $_->cust_main,
+ grep $_,
+ map $_->cust_svc->cust_pkg,
+ qsearch( {
+ 'table' => 'svc_acct',
+ 'hashref' => { 'username' => $user, },
+ 'extra_sql' =>
+ 'AND ( SELECT domain FROM svc_domain
+ WHERE svc_acct.domsvc = svc_domain.svcnum
+ ) = '. dbh->quote($domain),
+ }
+ );
+ }
+
+ my %saw = ();
+ @cust_main = grep { !$saw{$_->custnum}++ } @cust_main;
+
+ warn "$me smart_search: found ". scalar(@cust_main). " unique customers"
+ if $DEBUG;
+
+ @cust_main;
+
+}
+
+=item check_and_rebuild_fuzzyfiles
+
+=cut
+
+use vars qw(@fuzzyfields);
+@fuzzyfields = ( 'last', 'first', 'company' );
+
+sub check_and_rebuild_fuzzyfiles {
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ rebuild_fuzzyfiles() if grep { ! -e "$dir/cust_main.$_" } @fuzzyfields
+}
+
+=item rebuild_fuzzyfiles
+
+=cut
+
+sub rebuild_fuzzyfiles {
+
+ use Fcntl qw(:flock);
+
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ mkdir $dir, 0700 unless -d $dir;
+
+ foreach my $fuzzy ( @fuzzyfields ) {
+
+ open(LOCK,">>$dir/cust_main.$fuzzy")
+ or die "can't open $dir/cust_main.$fuzzy: $!";
+ flock(LOCK,LOCK_EX)
+ or die "can't lock $dir/cust_main.$fuzzy: $!";
+
+ open (CACHE,">$dir/cust_main.$fuzzy.tmp")
+ or die "can't open $dir/cust_main.$fuzzy.tmp: $!";
+
+ foreach my $field ( $fuzzy, "ship_$fuzzy" ) {
+ my $sth = dbh->prepare("SELECT $field FROM cust_main".
+ " WHERE $field != '' AND $field IS NOT NULL");
+ $sth->execute or die $sth->errstr;
+
+ while ( my $row = $sth->fetchrow_arrayref ) {
+ print CACHE $row->[0]. "\n";
+ }
+
+ }
+
+ close CACHE or die "can't close $dir/cust_main.$fuzzy.tmp: $!";
+
+ rename "$dir/cust_main.$fuzzy.tmp", "$dir/cust_main.$fuzzy";
+ close LOCK;
+ }
+
+}
+
+=item all_X
+
+=cut
+
+sub all_X {
+ my( $self, $field ) = @_;
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ open(CACHE,"<$dir/cust_main.$field")
+ or die "can't open $dir/cust_main.$field: $!";
+ my @array = map { chomp; $_; } <CACHE>;
+ close CACHE;
+ \@array;
+}
+
+=item append_fuzzyfiles LASTNAME COMPANY
+
+=cut
+
+sub append_fuzzyfiles {
+ #my( $first, $last, $company ) = @_;
+
+ &check_and_rebuild_fuzzyfiles;
+
+ use Fcntl qw(:flock);
+
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+
+ foreach my $field (qw( first last company )) {
+ my $value = shift;
+
+ if ( $value ) {
+
+ open(CACHE,">>$dir/cust_main.$field")
+ or die "can't open $dir/cust_main.$field: $!";
+ flock(CACHE,LOCK_EX)
+ or die "can't lock $dir/cust_main.$field: $!";
+
+ print CACHE "$value\n";
+
+ flock(CACHE,LOCK_UN)
+ or die "can't unlock $dir/cust_main.$field: $!";
+ close CACHE;
+ }
+
+ }
+
+ 1;
+}
+
+=item batch_charge
+
+=cut
+
+sub batch_charge {
+ my $param = shift;
+ #warn join('-',keys %$param);
+ my $fh = $param->{filehandle};
+ my @fields = @{$param->{fields}};
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+
+ my $csv = new Text::CSV_XS;
+ #warn $csv;
+ #warn $fh;
+
+ my $imported = 0;
+ #my $columns;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #while ( $columns = $csv->getline($fh) ) {
+ my $line;
+ while ( defined($line=<$fh>) ) {
+
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+
+ my @columns = $csv->fields();
+ #warn join('-',@columns);
+
+ my %row = ();
+ foreach my $field ( @fields ) {
+ $row{$field} = shift @columns;
+ }
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $row{'custnum'} } );
+ unless ( $cust_main ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "unknown custnum $row{'custnum'}";
+ }
+
+ if ( $row{'amount'} > 0 ) {
+ my $error = $cust_main->charge($row{'amount'}, $row{'pkg'});
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $imported++;
+ } elsif ( $row{'amount'} < 0 ) {
+ my $error = $cust_main->credit( sprintf( "%.2f", 0-$row{'amount'} ),
+ $row{'pkg'} );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $imported++;
+ } else {
+ #hmm?
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return "Empty file!" unless $imported;
+
+ ''; #no error
+
+}
+
+=item notify CUSTOMER_OBJECT TEMPLATE_NAME OPTIONS
+
+Sends a templated email notification to the customer (see L<Text::Template>).
+
+OPTIONS is a hash and may include
+
+I<from> - the email sender (default is invoice_from)
+
+I<to> - comma-separated scalar or arrayref of recipients
+ (default is invoicing_list)
+
+I<subject> - The subject line of the sent email notification
+ (default is "Notice from company_name")
+
+I<extra_fields> - a hashref of name/value pairs which will be substituted
+ into the template
+
+The following variables are vavailable in the template.
+
+I<$first> - the customer first name
+I<$last> - the customer last name
+I<$company> - the customer company
+I<$payby> - a description of the method of payment for the customer
+ # would be nice to use FS::payby::shortname
+I<$payinfo> - the account information used to collect for this customer
+I<$expdate> - the expiration of the customer payment in seconds from epoch
+
+=cut
+
+sub notify {
+ my ($self, $template, %options) = @_;
+
+ return unless $conf->exists($template);
+
+ my $from = $conf->config('invoice_from', $self->agentnum)
+ if $conf->exists('invoice_from', $self->agentnum);
+ $from = $options{from} if exists($options{from});
+
+ my $to = join(',', $self->invoicing_list_emailonly);
+ $to = $options{to} if exists($options{to});
+
+ my $subject = "Notice from " . $conf->config('company_name', $self->agentnum)
+ if $conf->exists('company_name', $self->agentnum);
+ $subject = $options{subject} if exists($options{subject});
+
+ my $notify_template = new Text::Template (TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n",
+ $conf->config($template)]
+ )
+ or die "can't create new Text::Template object: Text::Template::ERROR";
+ $notify_template->compile()
+ or die "can't compile template: Text::Template::ERROR";
+
+ $FS::notify_template::_template::company_name =
+ $conf->config('company_name', $self->agentnum);
+ $FS::notify_template::_template::company_address =
+ join("\n", $conf->config('company_address', $self->agentnum) ). "\n";
+
+ my $paydate = $self->paydate || '2037-12-31';
+ $FS::notify_template::_template::first = $self->first;
+ $FS::notify_template::_template::last = $self->last;
+ $FS::notify_template::_template::company = $self->company;
+ $FS::notify_template::_template::payinfo = $self->mask_payinfo;
+ my $payby = $self->payby;
+ my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
+ my $expire_time = timelocal(0,0,0,$payday,--$paymonth,$payyear);
+
+ #credit cards expire at the end of the month/year of their exp date
+ if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ $FS::notify_template::_template::payby = 'credit card';
+ ($paymonth < 11) ? $paymonth++ : ($paymonth=0, $payyear++);
+ $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
+ $expire_time--;
+ }elsif ($payby eq 'COMP') {
+ $FS::notify_template::_template::payby = 'complimentary account';
+ }else{
+ $FS::notify_template::_template::payby = 'current method';
+ }
+ $FS::notify_template::_template::expdate = $expire_time;
+
+ for (keys %{$options{extra_fields}}){
+ no strict "refs";
+ ${"FS::notify_template::_template::$_"} = $options{extra_fields}->{$_};
+ }
+
+ send_email(from => $from,
+ to => $to,
+ subject => $subject,
+ body => $notify_template->fill_in( PACKAGE =>
+ 'FS::notify_template::_template' ),
+ );
+
+}
+
+=item generate_letter CUSTOMER_OBJECT TEMPLATE_NAME OPTIONS
+
+Generates a templated notification to the customer (see L<Text::Template>).
+
+OPTIONS is a hash and may include
+
+I<extra_fields> - a hashref of name/value pairs which will be substituted
+ into the template. These values may override values mentioned below
+ and those from the customer record.
+
+The following variables are available in the template instead of or in addition
+to the fields of the customer record.
+
+I<$payby> - a description of the method of payment for the customer
+ # would be nice to use FS::payby::shortname
+I<$payinfo> - the masked account information used to collect for this customer
+I<$expdate> - the expiration of the customer payment method in seconds from epoch
+I<$returnaddress> - the return address defaults to invoice_latexreturnaddress or company_address
+
+=cut
+
+sub generate_letter {
+ my ($self, $template, %options) = @_;
+
+ return unless $conf->exists($template);
+
+ my $letter_template = new Text::Template
+ ( TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", $conf->config($template)],
+ DELIMITERS => [ '[@--', '--@]' ],
+ )
+ or die "can't create new Text::Template object: Text::Template::ERROR";
+
+ $letter_template->compile()
+ or die "can't compile template: Text::Template::ERROR";
+
+ my %letter_data = map { $_ => $self->$_ } $self->fields;
+ $letter_data{payinfo} = $self->mask_payinfo;
+
+ #my $paydate = $self->paydate || '2037-12-31';
+ my $paydate = $self->paydate =~ /^\S+$/ ? $self->paydate : '2037-12-31';
+
+ my $payby = $self->payby;
+ my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
+ my $expire_time = timelocal(0,0,0,$payday,--$paymonth,$payyear);
+
+ #credit cards expire at the end of the month/year of their exp date
+ if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ $letter_data{payby} = 'credit card';
+ ($paymonth < 11) ? $paymonth++ : ($paymonth=0, $payyear++);
+ $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
+ $expire_time--;
+ }elsif ($payby eq 'COMP') {
+ $letter_data{payby} = 'complimentary account';
+ }else{
+ $letter_data{payby} = 'current method';
+ }
+ $letter_data{expdate} = $expire_time;
+
+ for (keys %{$options{extra_fields}}){
+ $letter_data{$_} = $options{extra_fields}->{$_};
+ }
+
+ unless(exists($letter_data{returnaddress})){
+ my $retadd = join("\n", $conf->config_orbase( 'invoice_latexreturnaddress',
+ $self->agent_template)
+ );
+ if ( length($retadd) ) {
+ $letter_data{returnaddress} = $retadd;
+ } elsif ( grep /\S/, $conf->config('company_address', $self->agentnum) ) {
+ $letter_data{returnaddress} =
+ join( '\\*'."\n", map s/( {2,})/'~' x length($1)/eg,
+ $conf->config('company_address', $self->agentnum)
+ );
+ } else {
+ $letter_data{returnaddress} = '~';
+ }
+ }
+
+ $letter_data{conf_dir} = "$FS::UID::conf_dir/conf.$FS::UID::datasrc";
+
+ $letter_data{company_name} = $conf->config('company_name', $self->agentnum);
+
+ my $dir = $FS::UID::conf_dir."/cache.". $FS::UID::datasrc;
+ my $fh = new File::Temp( TEMPLATE => 'letter.'. $self->custnum. '.XXXXXXXX',
+ DIR => $dir,
+ SUFFIX => '.tex',
+ UNLINK => 0,
+ ) or die "can't open temp file: $!\n";
+
+ $letter_template->fill_in( OUTPUT => $fh, HASH => \%letter_data );
+ close $fh;
+ $fh->filename =~ /^(.*).tex$/ or die "unparsable filename: ". $fh->filename;
+ return $1;
+}
+
+=item print_ps TEMPLATE
+
+Returns an postscript letter filled in from TEMPLATE, as a scalar.
+
+=cut
+
+sub print_ps {
+ my $self = shift;
+ my $file = $self->generate_letter(@_);
+ FS::Misc::generate_ps($file);
+}
+
+=item print TEMPLATE
+
+Prints the filled in template.
+
+TEMPLATE is the name of a L<Text::Template> to fill in and print.
+
+=cut
+
+sub queueable_print {
+ my %opt = @_;
+
+ my $self = qsearchs('cust_main', { 'custnum' => $opt{custnum} } )
+ or die "invalid customer number: " . $opt{custvnum};
+
+ my $error = $self->print( $opt{template} );
+ die $error if $error;
+}
+
+sub print {
+ my ($self, $template) = (shift, shift);
+ do_print [ $self->print_ps($template) ];
+}
+
+#these three subs should just go away once agent stuff is all config overrides
+
+sub agent_template {
+ my $self = shift;
+ $self->_agent_plandata('agent_templatename');
+}
+
+sub agent_invoice_from {
+ my $self = shift;
+ $self->_agent_plandata('agent_invoice_from');
+}
+
+sub _agent_plandata {
+ my( $self, $option ) = @_;
+
+ #yuck. this whole thing needs to be reconciled better with 1.9's idea of
+ #agent-specific Conf
+
+ use FS::part_event::Condition;
+
+ my $agentnum = $self->agentnum;
+
+ my $regexp = '';
+ if ( driver_name =~ /^Pg/i ) {
+ $regexp = '~';
+ } elsif ( driver_name =~ /^mysql/i ) {
+ $regexp = 'REGEXP';
+ } else {
+ die "don't know how to use regular expressions in ". driver_name. " databases";
+ }
+
+ my $part_event_option =
+ qsearchs({
+ 'select' => 'part_event_option.*',
+ 'table' => 'part_event_option',
+ 'addl_from' => q{
+ LEFT JOIN part_event USING ( eventpart )
+ LEFT JOIN part_event_option AS peo_agentnum
+ ON ( part_event.eventpart = peo_agentnum.eventpart
+ AND peo_agentnum.optionname = 'agentnum'
+ AND peo_agentnum.optionvalue }. $regexp. q{ '(^|,)}. $agentnum. q{(,|$)'
+ )
+ LEFT JOIN part_event_condition
+ ON ( part_event.eventpart = part_event_condition.eventpart
+ AND part_event_condition.conditionname = 'cust_bill_age'
+ )
+ LEFT JOIN part_event_condition_option
+ ON ( part_event_condition.eventconditionnum = part_event_condition_option.eventconditionnum
+ AND part_event_condition_option.optionname = 'age'
+ )
+ },
+ #'hashref' => { 'optionname' => $option },
+ #'hashref' => { 'part_event_option.optionname' => $option },
+ 'extra_sql' =>
+ " WHERE part_event_option.optionname = ". dbh->quote($option).
+ " AND action = 'cust_bill_send_agent' ".
+ " AND ( disabled IS NULL OR disabled != 'Y' ) ".
+ " AND peo_agentnum.optionname = 'agentnum' ".
+ " AND ( agentnum IS NULL OR agentnum = $agentnum ) ".
+ " ORDER BY
+ CASE WHEN part_event_condition_option.optionname IS NULL
+ THEN -1
+ ELSE ". FS::part_event::Condition->age2seconds_sql('part_event_condition_option.optionvalue').
+ " END
+ , part_event.weight".
+ " LIMIT 1"
+ });
+
+ unless ( $part_event_option ) {
+ return $self->agent->invoice_template || ''
+ if $option eq 'agent_templatename';
+ return '';
+ }
+
+ $part_event_option->optionvalue;
+
+}
+
+sub queued_bill {
+ ## actual sub, not a method, designed to be called from the queue.
+ ## sets up the customer, and calls the bill_and_collect
+ my (%args) = @_; #, ($time, $invoice_time, $check_freq, $resetup) = @_;
+ my $cust_main = qsearchs( 'cust_main', { custnum => $args{'custnum'} } );
+ $cust_main->bill_and_collect(
+ %args,
+ );
+}
+
+=back
+
+=head1 BUGS
+
+The delete method.
+
+The delete method should possibly take an FS::cust_main object reference
+instead of a scalar customer number.
+
+Bill and collect options should probably be passed as references instead of a
+list.
+
+There should probably be a configuration file with a list of allowed credit
+card types.
+
+No multiple currency support (probably a larger project than just this module).
+
+payinfo_masked false laziness with cust_pay.pm and cust_refund.pm
+
+Birthdates rely on negative epoch values.
+
+The payby for card/check batches is broken. With mixed batching, bad
+things will happen.
+
+B<collect> I<invoice_time> should be renamed I<time>, like B<bill>.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_pkg>, L<FS::cust_bill>, L<FS::cust_credit>
+L<FS::agent>, L<FS::part_referral>, L<FS::cust_main_county>,
+L<FS::cust_main_invoice>, L<FS::UID>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_main/Import.pm b/FS/FS/cust_main/Import.pm
new file mode 100644
index 0000000..f477323
--- /dev/null
+++ b/FS/FS/cust_main/Import.pm
@@ -0,0 +1,427 @@
+package FS::cust_main::Import;
+
+use strict;
+use vars qw( $DEBUG $conf );
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+use Date::Parse;
+use File::Slurp qw( slurp );
+use FS::UID qw( dbh );
+use FS::Record qw( qsearchs );
+use FS::cust_main;
+use FS::svc_acct;
+use FS::svc_external;
+use FS::svc_phone;
+use FS::part_referral;
+
+$DEBUG = 0;
+
+install_callback FS::UID sub {
+ $conf = new FS::Conf;
+};
+
+=head1 NAME
+
+FS::cust_main::Import - Batch customer importing
+
+=head1 SYNOPSIS
+
+ use FS::cust_main::Import;
+
+ #import
+ FS::cust_main::Import::batch_import( {
+ file => $file, #filename
+ type => $type, #csv or xls
+ format => $format, #extended, extended-plus_company, svc_external,
+ # or svc_external_svc_phone
+ agentnum => $agentnum,
+ refnum => $refnum,
+ pkgpart => $pkgpart,
+ job => $job, #optional job queue job, for progressbar updates
+ custbatch => $custbatch, #optional batch unique identifier
+ } );
+ die $error if $error;
+
+ #ajax helper
+ use FS::UI::Web::JSRPC;
+ my $server =
+ new FS::UI::Web::JSRPC 'FS::cust_main::Import::process_batch_import', $cgi;
+ print $server->process;
+
+=head1 DESCRIPTION
+
+Batch customer importing.
+
+=head1 SUBROUTINES
+
+=item process_batch_import
+
+Load a batch import as a queued JSRPC job
+
+=cut
+
+sub process_batch_import {
+ my $job = shift;
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ my $files = $param->{'uploaded_files'}
+ or die "No files provided.\n";
+
+ my (%files) = map { /^(\w+):([\.\w]+)$/ ? ($1,$2):() } split /,/, $files;
+
+ my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc. '/';
+ my $file = $dir. $files{'file'};
+
+ my $type;
+ if ( $file =~ /\.(\w+)$/i ) {
+ $type = lc($1);
+ } else {
+ #or error out???
+ warn "can't parse file type from filename $file; defaulting to CSV";
+ $type = 'csv';
+ }
+
+ my $error =
+ FS::cust_main::Import::batch_import( {
+ job => $job,
+ file => $file,
+ type => $type,
+ custbatch => $param->{custbatch},
+ agentnum => $param->{'agentnum'},
+ refnum => $param->{'refnum'},
+ pkgpart => $param->{'pkgpart'},
+ #'fields' => [qw( cust_pkg.setup dayphone first last address1 address2
+ # city state zip comments )],
+ 'format' => $param->{'format'},
+ } );
+
+ unlink $file;
+
+ die "$error\n" if $error;
+
+}
+
+=item batch_import
+
+=cut
+
+
+#some false laziness w/cdr.pm now
+sub batch_import {
+ my $param = shift;
+
+ my $job = $param->{job};
+
+ my $filename = $param->{file};
+ my $type = $param->{type} || 'csv';
+
+ my $custbatch = $param->{custbatch};
+
+ my $agentnum = $param->{agentnum};
+ my $refnum = $param->{refnum};
+ my $pkgpart = $param->{pkgpart};
+
+ my $format = $param->{'format'};
+
+ my @fields;
+ my $payby;
+ if ( $format eq 'simple' ) {
+ @fields = qw( cust_pkg.setup dayphone first last
+ address1 address2 city state zip comments );
+ $payby = 'BILL';
+ } elsif ( $format eq 'extended' ) {
+ @fields = qw( agent_custid refnum
+ last first address1 address2 city state zip country
+ daytime night
+ ship_last ship_first ship_address1 ship_address2
+ ship_city ship_state ship_zip ship_country
+ payinfo paycvv paydate
+ invoicing_list
+ cust_pkg.pkgpart
+ svc_acct.username svc_acct._password
+ );
+ $payby = 'BILL';
+ } elsif ( $format eq 'extended-plus_company' ) {
+ @fields = qw( agent_custid refnum
+ last first company address1 address2 city state zip country
+ daytime night
+ ship_last ship_first ship_company ship_address1 ship_address2
+ ship_city ship_state ship_zip ship_country
+ payinfo paycvv paydate
+ invoicing_list
+ cust_pkg.pkgpart
+ svc_acct.username svc_acct._password
+ );
+ $payby = 'BILL';
+ } elsif ( $format =~ /^svc_external/ ) {
+ @fields = qw( agent_custid refnum
+ last first company address1 address2 city state zip country
+ daytime night
+ ship_last ship_first ship_company ship_address1 ship_address2
+ ship_city ship_state ship_zip ship_country
+ payinfo paycvv paydate
+ invoicing_list
+ cust_pkg.pkgpart cust_pkg.bill
+ svc_external.id svc_external.title
+ );
+ push @fields, map "svc_phone.$_", qw( countrycode phonenum sip_password pin)
+ if $format eq 'svc_external_svc_phone';
+ $payby = 'BILL';
+ } else {
+ die "unknown format $format";
+ }
+
+ my $count;
+ my $parser;
+ my @buffer = ();
+ if ( $type eq 'csv' ) {
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+
+ $parser = new Text::CSV_XS;
+
+ @buffer = split(/\r?\n/, slurp($filename) );
+ $count = scalar(@buffer);
+
+ } elsif ( $type eq 'xls' ) {
+
+ eval "use Spreadsheet::ParseExcel;";
+ die $@ if $@;
+
+ my $excel = Spreadsheet::ParseExcel::Workbook->new->Parse($filename);
+ $parser = $excel->{Worksheet}[0]; #first sheet
+
+ $count = $parser->{MaxRow} || $parser->{MinRow};
+ $count++;
+
+ } else {
+ die "Unknown file type $type\n";
+ }
+
+ #my $columns;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $line;
+ my $row = 0;
+ my( $last, $min_sec ) = ( time, 5 ); #progressbar foo
+ while (1) {
+
+ my @columns = ();
+ if ( $type eq 'csv' ) {
+
+ last unless scalar(@buffer);
+ $line = shift(@buffer);
+
+ $parser->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $parser->error_input();
+ };
+ @columns = $parser->fields();
+
+ } elsif ( $type eq 'xls' ) {
+
+ last if $row > ($parser->{MaxRow} || $parser->{MinRow})
+ || ! $parser->{Cells}[$row];
+
+ my @row = @{ $parser->{Cells}[$row] };
+ @columns = map $_->{Val}, @row;
+
+ #my $z = 'A';
+ #warn $z++. ": $_\n" for @columns;
+
+ } else {
+ die "Unknown file type $type\n";
+ }
+
+ #warn join('-',@columns);
+
+ my %cust_main = (
+ custbatch => $custbatch,
+ agentnum => $agentnum,
+ refnum => $refnum,
+ country => $conf->config('countrydefault') || 'US',
+ payby => $payby, #default
+ paydate => '12/2037', #default
+ );
+ my $billtime = time;
+ my %cust_pkg = ( pkgpart => $pkgpart );
+ my %svc_x = ();
+ foreach my $field ( @fields ) {
+
+ if ( $field =~ /^cust_pkg\.(pkgpart|setup|bill|susp|adjourn|expire|cancel)$/ ) {
+
+ #$cust_pkg{$1} = str2time( shift @$columns );
+ if ( $1 eq 'pkgpart' ) {
+ $cust_pkg{$1} = shift @columns;
+ } elsif ( $1 eq 'setup' ) {
+ $billtime = str2time(shift @columns);
+ } else {
+ $cust_pkg{$1} = str2time( shift @columns );
+ }
+
+ } elsif ( $field =~ /^svc_acct\.(username|_password)$/ ) {
+
+ $svc_x{$1} = shift @columns;
+
+ } elsif ( $field =~ /^svc_external\.(id|title)$/ ) {
+
+ $svc_x{$1} = shift @columns;
+
+ } elsif ( $field =~ /^svc_phone\.(countrycode|phonenum|sip_password|pin)$/ ) {
+ $svc_x{$1} = shift @columns;
+
+ } else {
+
+ #refnum interception
+ if ( $field eq 'refnum' && $columns[0] !~ /^\s*(\d+)\s*$/ ) {
+
+ my $referral = $columns[0];
+ my %hash = ( 'referral' => $referral,
+ 'agentnum' => $agentnum,
+ 'disabled' => '',
+ );
+
+ my $part_referral = qsearchs('part_referral', \%hash )
+ || new FS::part_referral \%hash;
+
+ unless ( $part_referral->refnum ) {
+ my $error = $part_referral->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't auto-insert advertising source: $referral: $error";
+ }
+ }
+
+ $columns[0] = $part_referral->refnum;
+ }
+
+ my $value = shift @columns;
+ $cust_main{$field} = $value if length($value);
+ }
+ }
+
+ $cust_main{'payby'} = 'CARD'
+ if defined $cust_main{'payinfo'}
+ && length $cust_main{'payinfo'};
+
+ my $invoicing_list = $cust_main{'invoicing_list'}
+ ? [ delete $cust_main{'invoicing_list'} ]
+ : [];
+
+ my $cust_main = new FS::cust_main ( \%cust_main );
+
+ use Tie::RefHash;
+ tie my %hash, 'Tie::RefHash'; #this part is important
+
+ if ( $cust_pkg{'pkgpart'} ) {
+ my $cust_pkg = new FS::cust_pkg ( \%cust_pkg );
+
+ my @svc_x = ();
+ my $svcdb = '';
+ if ( $svc_x{'username'} ) {
+ $svcdb = 'svc_acct';
+ } elsif ( $svc_x{'id'} || $svc_x{'title'} ) {
+ $svcdb = 'svc_external';
+ }
+
+ my $svc_phone = '';
+ if ( $svc_x{'countrycode'} || $svc_x{'phonenum'} ) {
+ $svc_phone = FS::svc_phone->new( {
+ map { $_ => delete($svc_x{$_}) }
+ qw( countrycode phonenum sip_password pin)
+ } );
+ }
+
+ if ( $svcdb || $svc_phone ) {
+ my $part_pkg = $cust_pkg->part_pkg;
+ unless ( $part_pkg ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "unknown pkgpart: ". $cust_pkg{'pkgpart'};
+ }
+ if ( $svcdb ) {
+ $svc_x{svcpart} = $part_pkg->svcpart_unique_svcdb( $svcdb );
+ my $class = "FS::$svcdb";
+ push @svc_x, $class->new( \%svc_x );
+ }
+ if ( $svc_phone ) {
+ $svc_phone->svcpart( $part_pkg->svcpart_unique_svcdb('svc_phone') );
+ push @svc_x, $svc_phone;
+ }
+ }
+
+ $hash{$cust_pkg} = \@svc_x;
+ }
+
+ my $error = $cust_main->insert( \%hash, $invoicing_list );
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert customer". ( $line ? " for $line" : '' ). ": $error";
+ }
+
+ if ( $format eq 'simple' ) {
+
+ #false laziness w/bill.cgi
+ $error = $cust_main->bill( 'time' => $billtime );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't bill customer for $line: $error";
+ }
+
+ $error = $cust_main->apply_payments_and_credits;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't bill customer for $line: $error";
+ }
+
+ $error = $cust_main->collect();
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't collect customer for $line: $error";
+ }
+
+ }
+
+ $row++;
+
+ if ( $job && time - $min_sec > $last ) { #progress bar
+ $job->update_statustext( int(100 * $row / $count) );
+ $last = time;
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;;
+
+ return "Empty file!" unless $row;
+
+ ''; #no error
+
+}
+
+=head1 BUGS
+
+Not enough documentation.
+
+=head1 SEE ALSO
+
+L<FS::cust_main>, L<FS::cust_pkg>,
+L<FS::svc_acct>, L<FS::svc_external>, L<FS::svc_phone>
+
+=cut
+
+1;
diff --git a/FS/FS/cust_main_Mixin.pm b/FS/FS/cust_main_Mixin.pm
new file mode 100644
index 0000000..ced0a1f
--- /dev/null
+++ b/FS/FS/cust_main_Mixin.pm
@@ -0,0 +1,269 @@
+package FS::cust_main_Mixin;
+
+use strict;
+use vars qw( $DEBUG );
+use FS::UID qw(dbh);
+use FS::cust_main;
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::cust_main_Mixin - Mixin class for records that contain fields from cust_main
+
+=head1 SYNOPSIS
+
+package FS::some_table;
+use vars qw(@ISA);
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+=head1 DESCRIPTION
+
+This is a mixin class for records that contain fields from the cust_main table,
+for example, from a JOINed search. See httemplate/search/ for examples.
+
+=head1 METHODS
+
+=over 4
+
+=item name
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<name> method, or "(unlinked)" if this object is not linked to
+a customer.
+
+=cut
+
+sub cust_unlinked_msg { '(unlinked)'; }
+sub cust_linked { $_[0]->custnum; }
+
+sub name {
+ my $self = shift;
+ $self->cust_linked
+ ? FS::cust_main::name($self)
+ : $self->cust_unlinked_msg;
+}
+
+=item ship_name
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<ship_name> method, or "(unlinked)" if this object is not
+linked to a customer.
+
+=cut
+
+sub ship_name {
+ my $self = shift;
+ $self->cust_linked
+ ? FS::cust_main::ship_name($self)
+ : $self->cust_unlinked_msg;
+}
+
+=item contact
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<contact> method, or "(unlinked)" if this object is not linked
+to a customer.
+
+=cut
+
+sub contact {
+ my $self = shift;
+ $self->cust_linked
+ ? FS::cust_main::contact($self)
+ : $self->cust_unlinked_msg;
+}
+
+=item ship_contact
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<ship_contact> method, or "(unlinked)" if this object is not
+linked to a customer.
+
+=cut
+
+sub ship_contact {
+ my $self = shift;
+ $self->cust_linked
+ ? FS::cust_main::ship_contact($self)
+ : $self->cust_unlinked_msg;
+}
+
+=item country_full
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<country_full> method, or "(unlinked)" if this object is not
+linked to a customer.
+
+=cut
+
+sub country_full {
+ my $self = shift;
+ $self->cust_linked
+ ? FS::cust_main::country_full($self)
+ : $self->cust_unlinked_msg;
+}
+
+=item invoicing_list_emailonly
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<invoicing_list_emailonly> method, or "(unlinked)" if this
+object is not linked to a customer.
+
+=cut
+
+sub invoicing_list_emailonly {
+ my $self = shift;
+ warn "invoicing_list_email only called on $self, ".
+ "custnum ". $self->custnum. "\n"
+ if $DEBUG;
+ $self->cust_linked
+ ? FS::cust_main::invoicing_list_emailonly($self)
+ : $self->cust_unlinked_msg;
+}
+
+=item invoicing_list_emailonly_scalar
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<invoicing_list_emailonly_scalar> method, or "(unlinked)" if
+this object is not linked to a customer.
+
+=cut
+
+sub invoicing_list_emailonly_scalar {
+ my $self = shift;
+ warn "invoicing_list_emailonly called on $self, ".
+ "custnum ". $self->custnum. "\n"
+ if $DEBUG;
+ $self->cust_linked
+ ? FS::cust_main::invoicing_list_emailonly_scalar($self)
+ : $self->cust_unlinked_msg;
+}
+
+=item invoicing_list
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<invoicing_list> method, or "(unlinked)" if this object is not
+linked to a customer.
+
+Note: this method is read-only.
+
+=cut
+
+#read-only
+sub invoicing_list {
+ my $self = shift;
+ $self->cust_linked
+ ? FS::cust_main::invoicing_list($self)
+ : ();
+}
+
+=item status
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<status> method, or "(unlinked)" if this object is not linked to
+a customer.
+
+=cut
+
+sub cust_status {
+ my $self = shift;
+ return $self->cust_unlinked_msg unless $self->cust_linked;
+
+ #FS::cust_main::status($self)
+ #false laziness w/actual cust_main::status
+ # (make sure FS::cust_main methods are called)
+ for my $status (qw( prospect active inactive suspended cancelled )) {
+ my $method = $status.'_sql';
+ my $sql = FS::cust_main->$method();;
+ my $numnum = ( $sql =~ s/cust_main\.custnum/?/g );
+ my $sth = dbh->prepare("SELECT $sql") or die dbh->errstr;
+ $sth->execute( ($self->custnum) x $numnum )
+ or die "Error executing 'SELECT $sql': ". $sth->errstr;
+ return $status if $sth->fetchrow_arrayref->[0];
+ }
+}
+
+=item ucfirst_cust_status
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<ucfirst_status> method, or "(unlinked)" if this object is not
+linked to a customer.
+
+=cut
+
+sub ucfirst_cust_status {
+ my $self = shift;
+ $self->cust_linked
+ ? ucfirst( $self->cust_status(@_) )
+ : $self->cust_unlinked_msg;
+}
+
+=item cust_statuscolor
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+FS::cust_main I<statuscol> method, or "000000" if this object is not linked to
+a customer.
+
+=cut
+
+sub cust_statuscolor {
+ my $self = shift;
+
+ $self->cust_linked
+ ? FS::cust_main::cust_statuscolor($self)
+ : '000000';
+}
+
+=item prospect_sql
+
+=item active_sql
+
+=item inactive_sql
+
+=item suspended_sql
+
+=item cancelled_sql
+
+Given an object that contains fields from cust_main (say, from a JOINed
+search; see httemplate/search/ for examples), returns the equivalent of the
+corresponding FS::cust_main method, or "0" if this object is not linked to
+a customer.
+
+=cut
+
+foreach my $sub (qw( prospect active inactive suspended cancelled )) {
+ eval "
+ sub ${sub}_sql {
+ my \$self = shift;
+ \$self->cust_linked
+ ? FS::cust_main::${sub}_sql(\$self)
+ : '0';
+ }
+ ";
+ die $@ if $@;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_main>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_main_county.pm b/FS/FS/cust_main_county.pm
new file mode 100644
index 0000000..bb60abb
--- /dev/null
+++ b/FS/FS/cust_main_county.pm
@@ -0,0 +1,499 @@
+package FS::cust_main_county;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK $conf
+ @cust_main_county %cust_main_county $countyflag );
+use Exporter;
+use FS::Record qw( qsearch dbh );
+use FS::cust_bill_pkg;
+use FS::cust_bill;
+use FS::cust_pkg;
+use FS::part_pkg;
+use FS::cust_tax_exempt;
+use FS::cust_tax_exempt_pkg;
+
+@ISA = qw( FS::Record );
+@EXPORT_OK = qw( regionselector );
+
+@cust_main_county = ();
+$countyflag = '';
+
+#ask FS::UID to run this stuff for us later
+$FS::UID::callback{'FS::cust_main_county'} = sub {
+ $conf = new FS::Conf;
+};
+
+=head1 NAME
+
+FS::cust_main_county - Object methods for cust_main_county objects
+
+=head1 SYNOPSIS
+
+ use FS::cust_main_county;
+
+ $record = new FS::cust_main_county \%hash;
+ $record = new FS::cust_main_county { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ ($county_html, $state_html, $country_html) =
+ FS::cust_main_county::regionselector( $county, $state, $country );
+
+=head1 DESCRIPTION
+
+An FS::cust_main_county object represents a tax rate, defined by locale.
+FS::cust_main_county inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item taxnum - primary key (assigned automatically for new tax rates)
+
+=item state
+
+=item county
+
+=item country
+
+=item tax - percentage
+
+=item taxclass
+
+=item exempt_amount
+
+=item taxname - if defined, printed on invoices instead of "Tax"
+
+=item setuptax - if 'Y', this tax does not apply to setup fees
+
+=item recurtax - if 'Y', this tax does not apply to recurring fees
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new tax rate. To add the tax rate to the database, see L<"insert">.
+
+=cut
+
+sub table { 'cust_main_county'; }
+
+=item insert
+
+Adds this tax rate to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this tax rate from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid tax rate. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->exempt_amount(0) unless $self->exempt_amount;
+
+ $self->ut_numbern('taxnum')
+ || $self->ut_anything('state')
+ || $self->ut_textn('county')
+ || $self->ut_text('country')
+ || $self->ut_float('tax')
+ || $self->ut_textn('taxclass') # ...
+ || $self->ut_money('exempt_amount')
+ || $self->ut_textn('taxname')
+ || $self->ut_enum('setuptax', [ '', 'Y' ] )
+ || $self->ut_enum('recurtax', [ '', 'Y' ] )
+ || $self->SUPER::check
+ ;
+
+}
+
+sub taxname {
+ my $self = shift;
+ if ( $self->dbdef_table->column('taxname') ) {
+ return $self->setfield('taxname', $_[0]) if @_;
+ return $self->getfield('taxname');
+ }
+ return '';
+}
+
+sub setuptax {
+ my $self = shift;
+ if ( $self->dbdef_table->column('setuptax') ) {
+ return $self->setfield('setuptax', $_[0]) if @_;
+ return $self->getfield('setuptax');
+ }
+ return '';
+}
+
+sub recurtax {
+ my $self = shift;
+ if ( $self->dbdef_table->column('recurtax') ) {
+ return $self->setfield('recurtax', $_[0]) if @_;
+ return $self->getfield('recurtax');
+ }
+ return '';
+}
+
+=item sql_taxclass_sameregion
+
+Returns an SQL WHERE fragment or the empty string to search for entries
+with different tax classes.
+
+=cut
+
+#hmm, description above could be better...
+
+sub sql_taxclass_sameregion {
+ my $self = shift;
+
+ my $same_query = 'SELECT taxclass FROM cust_main_county '.
+ ' WHERE taxnum != ? AND country = ?';
+ my @same_param = ( 'taxnum', 'country' );
+ foreach my $opt_field (qw( state county )) {
+ if ( $self->$opt_field() ) {
+ $same_query .= " AND $opt_field = ?";
+ push @same_param, $opt_field;
+ } else {
+ $same_query .= " AND $opt_field IS NULL";
+ }
+ }
+
+ my @taxclasses = $self->_list_sql( \@same_param, $same_query );
+
+ return '' unless scalar(@taxclasses);
+
+ '( taxclass IS NULL OR ( '. #only if !$self->taxclass ??
+ join(' AND ', map { 'taxclass != '.dbh->quote($_) } @taxclasses ).
+ ' ) ) ';
+}
+
+sub _list_sql {
+ my( $self, $param, $sql ) = @_;
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute( map $self->$_(), @$param )
+ or die "Unexpected error executing statement $sql: ". $sth->errstr;
+ map $_->[0], @{ $sth->fetchall_arrayref };
+}
+
+=item taxline TAXABLES_ARRAYREF, [ OPTION => VALUE ... ]
+
+Returns a listref of a name and an amount of tax calculated for the list of
+packages or amounts referenced by TAXABLES_ARRAYREF. Returns a scalar error
+message on error.
+
+Options include custnum and invoice_date and are hints to this method
+
+=cut
+
+sub taxline {
+ my( $self, $taxables, %opt ) = @_;
+
+ my @exemptions = ();
+ push @exemptions, @{ $_->_cust_tax_exempt_pkg }
+ for grep { ref($_) } @$taxables;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $name = $self->taxname || 'Tax';
+ my $amount = 0;
+
+ foreach my $cust_bill_pkg (@$taxables) {
+
+ my $cust_pkg = $cust_bill_pkg->cust_pkg;
+ my $cust_bill = $cust_pkg->cust_bill if $cust_pkg;
+ my $custnum = $cust_pkg ? $cust_pkg->custnum : $opt{custnum};
+ my $part_pkg = $cust_bill_pkg->part_pkg;
+ my $invoice_date = $cust_bill ? $cust_bill->_date : $opt{invoice_date};
+
+ my $taxable_charged = 0;
+ $taxable_charged += $cust_bill_pkg->setup
+ unless $part_pkg->setuptax =~ /^Y$/i
+ || $self->setuptax =~ /^Y$/i;
+ $taxable_charged += $cust_bill_pkg->recur
+ unless $part_pkg->recurtax =~ /^Y$/i
+ || $self->recurtax =~ /^Y$/i;
+
+ next unless $taxable_charged;
+
+ if ( $self->exempt_amount && $self->exempt_amount > 0 ) {
+ #my ($mon,$year) = (localtime($cust_bill_pkg->sdate) )[4,5];
+ my ($mon,$year) =
+ (localtime( $cust_bill_pkg->sdate || $invoice_date ) )[4,5];
+ $mon++;
+ my $freq = $part_pkg->freq || 1;
+ if ( $freq !~ /(\d+)$/ ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "daily/weekly package definitions not (yet?)".
+ " compatible with monthly tax exemptions";
+ }
+ my $taxable_per_month =
+ sprintf("%.2f", $taxable_charged / $freq );
+
+ #call the whole thing off if this customer has any old
+ #exemption records...
+ my @cust_tax_exempt =
+ qsearch( 'cust_tax_exempt' => { custnum=> $custnum } );
+ if ( @cust_tax_exempt ) {
+ $dbh->rollback if $oldAutoCommit;
+ return
+ 'this customer still has old-style tax exemption records; '.
+ 'run bin/fs-migrate-cust_tax_exempt?';
+ }
+
+ foreach my $which_month ( 1 .. $freq ) {
+
+ #maintain the new exemption table now
+ my $sql = "
+ SELECT SUM(amount)
+ FROM cust_tax_exempt_pkg
+ LEFT JOIN cust_bill_pkg USING ( billpkgnum )
+ LEFT JOIN cust_bill USING ( invnum )
+ WHERE custnum = ?
+ AND taxnum = ?
+ AND year = ?
+ AND month = ?
+ ";
+ my $sth = dbh->prepare($sql) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "fatal: can't lookup exising exemption: ". dbh->errstr;
+ };
+ $sth->execute(
+ $custnum,
+ $self->taxnum,
+ 1900+$year,
+ $mon,
+ ) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "fatal: can't lookup exising exemption: ". dbh->errstr;
+ };
+ my $existing_exemption = $sth->fetchrow_arrayref->[0] || 0;
+
+ foreach ( grep { $_->taxnum == $self->taxnum &&
+ $_->month == $mon &&
+ $_->year == 1900+$year
+ } @exemptions
+ )
+ {
+ $existing_exemption += $_->amount;
+ }
+
+ my $remaining_exemption =
+ $self->exempt_amount - $existing_exemption;
+ if ( $remaining_exemption > 0 ) {
+ my $addl = $remaining_exemption > $taxable_per_month
+ ? $taxable_per_month
+ : $remaining_exemption;
+ $taxable_charged -= $addl;
+
+ my $cust_tax_exempt_pkg = new FS::cust_tax_exempt_pkg ( {
+ 'taxnum' => $self->taxnum,
+ 'year' => 1900+$year,
+ 'month' => $mon,
+ 'amount' => sprintf("%.2f", $addl ),
+ } );
+ if ($cust_bill_pkg->billpkgnum) {
+ $cust_tax_exempt_pkg->billpkgnum($cust_bill_pkg->billpkgnum);
+ my $error = $cust_tax_exempt_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "fatal: can't insert cust_tax_exempt_pkg: $error";
+ }
+ }else{
+ push @exemptions, $cust_tax_exempt_pkg;
+ push @{ $cust_bill_pkg->_cust_tax_exempt_pkg }, $cust_tax_exempt_pkg;
+ } # if $cust_bill_pkg->billpkgnum
+ } # if $remaining_exemption > 0
+
+ #++
+ $mon++;
+ #until ( $mon < 12 ) { $mon -= 12; $year++; }
+ until ( $mon < 13 ) { $mon -= 12; $year++; }
+
+ } #foreach $which_month
+
+ } #if $tax->exempt_amount
+
+ $taxable_charged = sprintf( "%.2f", $taxable_charged);
+
+ $amount += $taxable_charged * $self->tax / 100
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return {
+ 'name' => $name,
+ 'amount' => $amount,
+ };
+
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item regionselector [ COUNTY STATE COUNTRY [ PREFIX [ ONCHANGE [ DISABLED ] ] ] ]
+
+=cut
+
+sub regionselector {
+ my ( $selected_county, $selected_state, $selected_country,
+ $prefix, $onchange, $disabled ) = @_;
+
+ $prefix = '' unless defined $prefix;
+
+ $countyflag = 0;
+
+# unless ( @cust_main_county ) { #cache
+ @cust_main_county = qsearch('cust_main_county', {} );
+ foreach my $c ( @cust_main_county ) {
+ $countyflag=1 if $c->county;
+ #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
+ $cust_main_county{$c->country}{$c->state}{$c->county} = 1;
+ }
+# }
+ $countyflag=1 if $selected_county;
+
+ my $script_html = <<END;
+ <SCRIPT>
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+ function ${prefix}country_changed(what) {
+ country = what.options[what.selectedIndex].text;
+ for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
+ what.form.${prefix}state.options[i] = null;
+END
+ #what.form.${prefix}state.options[0] = new Option('', '', false, true);
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ ( my $dstate = $state ) =~ s/[\n\r]//g;
+ my $text = $dstate || '(n/a)';
+ $script_html .= qq!opt(what.form.${prefix}state, "$dstate", "$text");\n!;
+ }
+ $script_html .= "}\n";
+ }
+
+ $script_html .= <<END;
+ }
+ function ${prefix}state_changed(what) {
+END
+
+ if ( $countyflag ) {
+ $script_html .= <<END;
+ state = what.options[what.selectedIndex].text;
+ country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
+ for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
+ what.form.${prefix}county.options[i] = null;
+END
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ $script_html .= "\nif ( state == \"$state\" ) {\n";
+ #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
+ foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
+ my $text = $county || '(n/a)';
+ $script_html .=
+ qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
+ }
+ $script_html .= "}\n";
+ }
+ $script_html .= "}\n";
+ }
+ }
+
+ $script_html .= <<END;
+ }
+ </SCRIPT>
+END
+
+ my $county_html = $script_html;
+ if ( $countyflag ) {
+ $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$onchange" $disabled>!;
+ $county_html .= '</SELECT>';
+ } else {
+ $county_html .=
+ qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$selected_county">!;
+ }
+
+ my $state_html = qq!<SELECT NAME="${prefix}state" !.
+ qq!onChange="${prefix}state_changed(this); $onchange" $disabled>!;
+ foreach my $state ( sort keys %{ $cust_main_county{$selected_country} } ) {
+ my $text = $state || '(n/a)';
+ my $selected = $state eq $selected_state ? 'SELECTED' : '';
+ $state_html .= qq(\n<OPTION $selected VALUE="$state">$text</OPTION>);
+ }
+ $state_html .= '</SELECT>';
+
+ $state_html .= '</SELECT>';
+
+ my $country_html = qq!<SELECT NAME="${prefix}country" !.
+ qq!onChange="${prefix}country_changed(this); $onchange" $disabled>!;
+ my $countrydefault = $conf->config('countrydefault') || 'US';
+ foreach my $country (
+ sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
+ keys %cust_main_county
+ ) {
+ my $selected = $country eq $selected_country ? ' SELECTED' : '';
+ $country_html .= qq(\n<OPTION$selected VALUE="$country">$country</OPTION>");
+ }
+ $country_html .= '</SELECT>';
+
+ ($county_html, $state_html, $country_html);
+
+}
+
+=back
+
+=head1 BUGS
+
+regionselector? putting web ui components in here? they should probably live
+somewhere else...
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_main>, L<FS::cust_bill>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_main_invoice.pm b/FS/FS/cust_main_invoice.pm
new file mode 100644
index 0000000..71148ca
--- /dev/null
+++ b/FS/FS/cust_main_invoice.pm
@@ -0,0 +1,184 @@
+package FS::cust_main_invoice;
+
+use strict;
+use vars qw(@ISA $conf);
+use Exporter;
+use FS::Record qw( qsearchs );
+use FS::Conf;
+use FS::cust_main;
+use FS::svc_acct;
+use FS::Msgcat qw(gettext);
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::cust_main_invoice - Object methods for cust_main_invoice records
+
+=head1 SYNOPSIS
+
+ use FS::cust_main_invoice;
+
+ $record = new FS::cust_main_invoice \%hash;
+ $record = new FS::cust_main_invoice { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $email_address = $record->address;
+
+=head1 DESCRIPTION
+
+An FS::cust_main_invoice object represents an invoice destination. FS::cust_main_invoice inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item destnum - primary key
+
+=item custnum - customer (see L<FS::cust_main>)
+
+=item dest - Invoice destination: If numeric, a svcnum (see L<FS::svc_acct>), if string, a literal email address, `POST' to enable mailing (the default if no cust_main_invoice records exist), or `FAX' to enable faxing via a HylaFAX server.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new invoice destination. To add the invoice destination to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_main_invoice'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my ( $new, $old ) = ( shift, shift );
+
+ return "Can't change custnum!" unless $old->custnum == $new->custnum;
+
+ $new->SUPER::replace($old);
+}
+
+
+=item check
+
+Checks all fields to make sure this is a valid invoice destination. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error = $self->ut_numbern('destnum')
+ || $self->ut_number('custnum')
+ || $self->checkdest;
+ ;
+ return $error if $error;
+
+ return "Unknown customer"
+ unless qsearchs('cust_main',{ 'custnum' => $self->custnum });
+
+ $self->SUPER::check;
+}
+
+=item checkdest
+
+Checks the dest field only.
+
+#If it finds that the account ends in the
+#same domain configured as the B<domain> configuration file, it will change the
+#invoice destination from an email address to a service number (see
+#L<FS::svc_acct>).
+
+=cut
+
+sub checkdest {
+ my $self = shift;
+
+ my $error = $self->ut_text('dest');
+ return $error if $error;
+
+ if ( $self->dest =~ /^(POST|FAX)$/ ) {
+ #contemplate our navel
+ } elsif ( $self->dest =~ /^(\d+)$/ ) {
+ return "Unknown local account (specified by svcnum: ". $self->dest. ")"
+ unless qsearchs( 'svc_acct', { 'svcnum' => $self->dest } );
+ } elsif ( $self->dest =~ /^\s*([\w\.\-\&\+]+)\@(([\w\.\-]+\.)+\w+)\s*$/ ) {
+ my($user, $domain) = ($1, $2);
+ $self->dest("$1\@$2");
+ } else {
+ return gettext("illegal_email_invoice_address"). ': '. $self->dest;
+ }
+
+ ''; #no error
+}
+
+=item address
+
+Returns the literal email address for this record (or `POST' or `FAX').
+
+=cut
+
+sub address {
+ my $self = shift;
+ if ( $self->dest =~ /^(\d+)$/ ) {
+ my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $1 } )
+ or return undef;
+ $svc_acct->email;
+ } else {
+ $self->dest;
+ }
+}
+
+=item cust_main
+
+Returns the parent customer object (see L<FS::cust_main>).
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_main>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_main_note.pm b/FS/FS/cust_main_note.pm
new file mode 100644
index 0000000..4732d12
--- /dev/null
+++ b/FS/FS/cust_main_note.pm
@@ -0,0 +1,131 @@
+package FS::cust_main_note;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_main_note - Object methods for cust_main_note records
+
+=head1 SYNOPSIS
+
+ use FS::cust_main_note;
+
+ $record = new FS::cust_main_note \%hash;
+ $record = new FS::cust_main_note { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_main_note object represents a note attachted to a customer.
+FS::cust_main_note inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item notenum - primary key
+
+=item custnum -
+
+=item _date -
+
+=item otaker -
+
+=item comments -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new customer note. To add the note to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_main_note'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('notenum')
+ || $self->ut_number('custnum')
+ || $self->ut_numbern('_date')
+ || $self->ut_text('otaker')
+ || $self->ut_anything('comments')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+Lurking in the cracks.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
new file mode 100644
index 0000000..583a724
--- /dev/null
+++ b/FS/FS/cust_pay.pm
@@ -0,0 +1,823 @@
+package FS::cust_pay;
+
+use strict;
+use vars qw( @ISA $DEBUG $me $conf @encrypted_fields
+ $unsuspendauto $ignore_noapply
+ );
+use Date::Format;
+use Business::CreditCard;
+use Text::Template;
+use FS::UID qw( getotaker );
+use FS::Misc qw( send_email );
+use FS::Record qw( dbh qsearch qsearchs );
+use FS::payby;
+use FS::cust_main_Mixin;
+use FS::payinfo_transaction_Mixin;
+use FS::cust_bill;
+use FS::cust_bill_pay;
+use FS::cust_pay_refund;
+use FS::cust_main;
+use FS::cust_pay_void;
+
+@ISA = qw( FS::payinfo_transaction_Mixin FS::cust_main_Mixin FS::Record );
+
+$DEBUG = 1;
+
+$me = '[FS::cust_pay]';
+
+$ignore_noapply = 0;
+
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+ $unsuspendauto = $conf->exists('unsuspendauto');
+} );
+
+@encrypted_fields = ('payinfo');
+
+=head1 NAME
+
+FS::cust_pay - Object methods for cust_pay objects
+
+=head1 SYNOPSIS
+
+ use FS::cust_pay;
+
+ $record = new FS::cust_pay \%hash;
+ $record = new FS::cust_pay { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pay object represents a payment; the transfer of money from a
+customer. FS::cust_pay inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item paynum - primary key (assigned automatically for new payments)
+
+=item custnum - customer (see L<FS::cust_main>)
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item paid - Amount of this payment
+
+=item otaker - order taker (assigned automatically, see L<FS::UID>)
+
+=item payby - Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
+
+=item payinfo - Payment Information (See L<FS::payinfo_Mixin> for data format)
+
+=item paymask - Masked payinfo (See L<FS::payinfo_Mixin> for how this works)
+
+=item paybatch - text field for tracking card processing or other batch grouping
+
+=item payunique - Optional unique identifer to prevent duplicate transactions.
+
+=item closed - books closed flag, empty or `Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new payment. To add the payment to the databse, see L<"insert">.
+
+=cut
+
+sub table { 'cust_pay'; }
+sub cust_linked { $_[0]->cust_main_custnum; }
+sub cust_unlinked_msg {
+ my $self = shift;
+ "WARNING: can't find cust_main.custnum ". $self->custnum.
+ ' (cust_pay.paynum '. $self->paynum. ')';
+}
+
+=item insert
+
+Adds this payment to the database.
+
+For backwards-compatibility and convenience, if the additional field invnum
+is defined, an FS::cust_bill_pay record for the full amount of the payment
+will be created. In this case, custnum is optional. An hash of optional
+arguments may be passed. Currently "manual" is supported. If true, a
+payment receipt is sent instead of a statement when 'payment_receipt_email'
+configuration option is set.
+
+=cut
+
+sub insert {
+ my ($self, %options) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $cust_bill;
+ if ( $self->invnum ) {
+ $cust_bill = qsearchs('cust_bill', { 'invnum' => $self->invnum } )
+ or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unknown cust_bill.invnum: ". $self->invnum;
+ };
+ $self->custnum($cust_bill->custnum );
+ }
+
+
+ my $error = $self->check;
+ return $error if $error;
+
+ my $cust_main = $self->cust_main;
+ my $old_balance = $cust_main->balance;
+
+ $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error inserting $self: $error";
+ }
+
+ if ( $self->invnum ) {
+ my $cust_bill_pay = new FS::cust_bill_pay {
+ 'invnum' => $self->invnum,
+ 'paynum' => $self->paynum,
+ 'amount' => $self->paid,
+ '_date' => $self->_date,
+ };
+ $error = $cust_bill_pay->insert;
+ if ( $error ) {
+ if ( $ignore_noapply ) {
+ warn "warning: error inserting $cust_bill_pay: $error ".
+ "(ignore_noapply flag set; inserting cust_pay record anyway)\n";
+ } else {
+ $dbh->rollback if $oldAutoCommit;
+ return "error inserting $cust_bill_pay: $error";
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ #false laziness w/ cust_credit::insert
+ if ( $unsuspendauto && $old_balance && $cust_main->balance <= 0 ) {
+ my @errors = $cust_main->unsuspend;
+ #return
+ # side-fx with nested transactions? upstack rolls back?
+ warn "WARNING:Errors unsuspending customer ". $cust_main->custnum. ": ".
+ join(' / ', @errors)
+ if @errors;
+ }
+ #eslaf
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ #my $cust_main = $self->cust_main;
+ if ( $conf->exists('payment_receipt_email')
+ && grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list
+ ) {
+
+ $cust_bill ||= ($cust_main->cust_bill)[-1]; #rather inefficient though?
+
+ my $error;
+ if ( ( exists($options{'manual'}) && $options{'manual'} )
+ || ! $conf->exists('invoice_html_statement')
+ || ! $cust_bill
+ ) {
+
+ my $receipt_template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", $conf->config('payment_receipt_email') ],
+ ) or do {
+ warn "can't create payment receipt template: $Text::Template::ERROR";
+ return '';
+ };
+
+ my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ }
+ $cust_main->invoicing_list;
+
+ my $payby = $self->payby;
+ my $payinfo = $self->payinfo;
+ $payby =~ s/^BILL$/Check/ if $payinfo;
+ $payinfo = $self->paymask if $payby eq 'CARD' || $payby eq 'CHEK';
+ $payby =~ s/^CHEK$/Electronic check/;
+
+ $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->cust_main->agentnum),
+ #invoice_from??? well as good as any
+ 'to' => \@invoicing_list,
+ 'subject' => 'Payment receipt',
+ 'body' => [ $receipt_template->fill_in( HASH => {
+ 'date' => time2str("%a %B %o, %Y", $self->_date),
+ 'name' => $cust_main->name,
+ 'paynum' => $self->paynum,
+ 'paid' => sprintf("%.2f", $self->paid),
+ 'payby' => ucfirst(lc($payby)),
+ 'payinfo' => $payinfo,
+ 'balance' => $cust_main->balance,
+ } ) ],
+ );
+
+ } else {
+
+ my $queue = new FS::queue {
+ 'paynum' => $self->paynum,
+ 'job' => 'FS::cust_bill::queueable_email',
+ };
+ $error = $queue->insert(
+ 'invnum' => $cust_bill->invnum,
+ 'template' => 'statement',
+ );
+
+ }
+
+ if ( $error ) {
+ warn "can't send payment receipt/statement: $error";
+ }
+
+ }
+
+ '';
+
+}
+
+=item void [ REASON ]
+
+Voids this payment: deletes the payment and all associated applications and
+adds a record of the voided payment to the FS::cust_pay_void table.
+
+=cut
+
+sub void {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $cust_pay_void = new FS::cust_pay_void ( {
+ map { $_ => $self->get($_) } $self->fields
+ } );
+ $cust_pay_void->reason(shift) if scalar(@_);
+ my $error = $cust_pay_void->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $error = $self->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Unless the closed flag is set, deletes this payment and all associated
+applications (see L<FS::cust_bill_pay> and L<FS::cust_pay_refund>). In most
+cases, you want to use the void method instead to leave a record of the
+deleted payment.
+
+=cut
+
+# very similar to FS::cust_credit::delete
+sub delete {
+ my $self = shift;
+ return "Can't delete closed payment" if $self->closed =~ /^Y/i;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $app ( $self->cust_bill_pay, $self->cust_pay_refund ) {
+ my $error = $app->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $conf->config('deletepayments') ne '' ) {
+
+ my $cust_main = $self->cust_main;
+
+ my $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->cust_main->agentnum),
+ #invoice_from??? well as good as any
+ 'to' => $conf->config('deletepayments'),
+ 'subject' => 'FREESIDE NOTIFICATION: Payment deleted',
+ 'body' => [
+ "This is an automatic message from your Freeside installation\n",
+ "informing you that the following payment has been deleted:\n",
+ "\n",
+ 'paynum: '. $self->paynum. "\n",
+ 'custnum: '. $self->custnum.
+ " (". $cust_main->last. ", ". $cust_main->first. ")\n",
+ 'paid: $'. sprintf("%.2f", $self->paid). "\n",
+ 'date: '. time2str("%a %b %e %T %Y", $self->_date). "\n",
+ 'payby: '. $self->payby. "\n",
+ 'payinfo: '. $self->paymask. "\n",
+ 'paybatch: '. $self->paybatch. "\n",
+ ],
+ );
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't send payment deletion notification: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item replace OLD_RECORD
+
+You can, but probably shouldn't modify payments...
+
+=cut
+
+sub replace {
+ #return "Can't modify payment!"
+ my $self = shift;
+ return "Can't modify closed payment" if $self->closed =~ /^Y/i;
+ $self->SUPER::replace(@_);
+}
+
+=item check
+
+Checks all fields to make sure this is a valid payment. If there is an error,
+returns the error, otherwise returns false. Called by the insert method.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->otaker(getotaker) unless ($self->otaker);
+
+ my $error =
+ $self->ut_numbern('paynum')
+ || $self->ut_numbern('custnum')
+ || $self->ut_numbern('_date')
+ || $self->ut_money('paid')
+ || $self->ut_alpha('otaker')
+ || $self->ut_textn('paybatch')
+ || $self->ut_textn('payunique')
+ || $self->ut_enum('closed', [ '', 'Y' ])
+ || $self->payinfo_check()
+ ;
+ return $error if $error;
+
+ return "paid must be > 0 " if $self->paid <= 0;
+
+ return "unknown cust_main.custnum: ". $self->custnum
+ unless $self->invnum
+ || qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+
+ $self->_date(time) unless $self->_date;
+
+#i guess not now, with cust_pay_pending, if we actually make it here, we _do_ want to record it
+# # UNIQUE index should catch this too, without race conditions, but this
+# # should give a better error message the other 99.9% of the time...
+# if ( length($self->payunique)
+# && qsearchs('cust_pay', { 'payunique' => $self->payunique } ) ) {
+# #well, it *could* be a better error message
+# return "duplicate transaction".
+# " - a payment with unique identifer ". $self->payunique.
+# " already exists";
+# }
+
+ $self->SUPER::check;
+}
+
+=item batch_insert CUST_PAY_OBJECT, ...
+
+Class method which inserts multiple payments. Takes a list of FS::cust_pay
+objects. Returns a list, each element representing the status of inserting the
+corresponding payment - empty. If there is an error inserting any payment, the
+entire transaction is rolled back, i.e. all payments are inserted or none are.
+
+For example:
+
+ my @errors = FS::cust_pay->batch_insert(@cust_pay);
+ my $num_errors = scalar(grep $_, @errors);
+ if ( $num_errors == 0 ) {
+ #success; all payments were inserted
+ } else {
+ #failure; no payments were inserted.
+ }
+
+=cut
+
+sub batch_insert {
+ my $self = shift; #class method
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $errors = 0;
+
+ my @errors = map {
+ my $error = $_->insert( 'manual' => 1 );
+ if ( $error ) {
+ $errors++;
+ } else {
+ $_->cust_main->apply_payments;
+ }
+ $error;
+ } @_;
+
+ if ( $errors ) {
+ $dbh->rollback if $oldAutoCommit;
+ } else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ }
+
+ @errors;
+
+}
+
+=item cust_bill_pay
+
+Returns all applications to invoices (see L<FS::cust_bill_pay>) for this
+payment.
+
+=cut
+
+sub cust_bill_pay {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date
+ || $a->invnum <=> $b->invnum }
+ qsearch( 'cust_bill_pay', { 'paynum' => $self->paynum } )
+ ;
+}
+
+=item cust_pay_refund
+
+Returns all applications of refunds (see L<FS::cust_pay_refund>) to this
+payment.
+
+=cut
+
+sub cust_pay_refund {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay_refund', { 'paynum' => $self->paynum } )
+ ;
+}
+
+
+=item unapplied
+
+Returns the amount of this payment that is still unapplied; which is
+paid minus all payment applications (see L<FS::cust_bill_pay>) and refund
+applications (see L<FS::cust_pay_refund>).
+
+=cut
+
+sub unapplied {
+ my $self = shift;
+ my $amount = $self->paid;
+ $amount -= $_->amount foreach ( $self->cust_bill_pay );
+ $amount -= $_->amount foreach ( $self->cust_pay_refund );
+ sprintf("%.2f", $amount );
+}
+
+=item unrefunded
+
+Returns the amount of this payment that has not been refuned; which is
+paid minus all refund applications (see L<FS::cust_pay_refund>).
+
+=cut
+
+sub unrefunded {
+ my $self = shift;
+ my $amount = $self->paid;
+ $amount -= $_->amount foreach ( $self->cust_pay_refund );
+ sprintf("%.2f", $amount );
+}
+
+=item amount
+
+Returns the "paid" field.
+
+=cut
+
+sub amount {
+ my $self = shift;
+ $self->paid();
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item unapplied_sql
+
+Returns an SQL fragment to retreive the unapplied amount.
+
+=cut
+
+sub unapplied_sql {
+ #my $class = shift;
+
+ "paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ";
+
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+use FS::h_cust_pay;
+
+sub _upgrade_data { #class method
+ my ($class, %opts) = @_;
+
+ warn "$me upgrading $class\n" if $DEBUG;
+
+ #not the most efficient, but hey, it only has to run once
+
+ my $where = "WHERE ( otaker IS NULL OR otaker = '' OR otaker = 'ivan' ) ".
+ " AND 0 < ( SELECT COUNT(*) FROM cust_main ".
+ " WHERE cust_main.custnum = cust_pay.custnum ) ";
+
+ my $count_sql = "SELECT COUNT(*) FROM cust_pay $where";
+
+ my $sth = dbh->prepare($count_sql) or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my $total = $sth->fetchrow_arrayref->[0];
+ #warn "$total cust_pay records to update\n"
+ # if $DEBUG;
+ local($DEBUG) = 2 if $total > 1000; #could be a while, force progress info
+
+ my $count = 0;
+ my $lastprog = 0;
+
+ my @cust_pay = qsearch( {
+ 'table' => 'cust_pay',
+ 'hashref' => {},
+ 'extra_sql' => $where,
+ 'order_by' => 'ORDER BY paynum',
+ } );
+
+ foreach my $cust_pay (@cust_pay) {
+
+ my $h_cust_pay = $cust_pay->h_search('insert');
+ if ( $h_cust_pay ) {
+ next if $cust_pay->otaker eq $h_cust_pay->history_user;
+ $cust_pay->otaker($h_cust_pay->history_user);
+ } else {
+ $cust_pay->otaker('legacy');
+ }
+
+ delete $FS::payby::hash{'COMP'}->{cust_pay}; #quelle kludge
+ my $error = $cust_pay->replace;
+
+ if ( $error ) {
+ warn " *** WARNING: Error updating order taker for payment paynum ".
+ $cust_pay->paynun. ": $error\n";
+ next;
+ }
+
+ $FS::payby::hash{'COMP'}->{cust_pay} = ''; #restore it
+
+ $count++;
+ if ( $DEBUG > 1 && $lastprog + 30 < time ) {
+ warn "$me $count/$total (". sprintf('%.2f',100*$count/$total). '%)'. "\n";
+ $lastprog = time;
+ }
+
+ }
+
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item batch_import HASHREF
+
+Inserts new payments.
+
+=cut
+
+sub batch_import {
+ my $param = shift;
+
+ my $fh = $param->{filehandle};
+ my $agentnum = $param->{agentnum};
+ my $format = $param->{'format'};
+ my $paybatch = $param->{'paybatch'};
+
+ # here is the agent virtualization
+ my $extra_sql = ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+ my @fields;
+ my $payby;
+ if ( $format eq 'simple' ) {
+ @fields = qw( custnum agent_custid paid payinfo );
+ $payby = 'BILL';
+ } elsif ( $format eq 'extended' ) {
+ die "unimplemented\n";
+ @fields = qw( );
+ $payby = 'BILL';
+ } else {
+ die "unknown format $format";
+ }
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+
+ my $csv = new Text::CSV_XS;
+
+ my $imported = 0;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $line;
+ while ( defined($line=<$fh>) ) {
+
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+
+ my @columns = $csv->fields();
+
+ my %cust_pay = (
+ payby => $payby,
+ paybatch => $paybatch,
+ );
+
+ my $cust_main;
+ foreach my $field ( @fields ) {
+
+ if ( $field eq 'agent_custid'
+ && $agentnum
+ && $columns[0] =~ /\S+/ )
+ {
+
+ my $agent_custid = $columns[0];
+ my %hash = ( 'agent_custid' => $agent_custid,
+ 'agentnum' => $agentnum,
+ );
+
+ if ( $cust_pay{'custnum'} !~ /^\s*$/ ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't specify custnum with agent_custid $agent_custid";
+ }
+
+ $cust_main = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => \%hash,
+ 'extra_sql' => $extra_sql,
+ });
+
+ unless ( $cust_main ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't find customer with agent_custid $agent_custid";
+ }
+
+ $field = 'custnum';
+ $columns[0] = $cust_main->custnum;
+ }
+
+ $cust_pay{$field} = shift @columns;
+ }
+
+ my $cust_pay = new FS::cust_pay( \%cust_pay );
+ my $error = $cust_pay->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert payment for $line: $error";
+ }
+
+ if ( $format eq 'simple' ) {
+ # include agentnum for less surprise?
+ $cust_main = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $cust_pay->custnum },
+ 'extra_sql' => $extra_sql,
+ })
+ unless $cust_main;
+
+ unless ( $cust_main ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't find customer to which payments apply at line: $line";
+ }
+
+ $error = $cust_main->apply_payments_and_credits;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't apply payments to customer for $line: $error";
+ }
+
+ }
+
+ $imported++;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return "Empty file!" unless $imported;
+
+ ''; #no error
+
+}
+
+=back
+
+=head1 BUGS
+
+Delete and replace methods.
+
+=head1 SEE ALSO
+
+L<FS::cust_pay_pending>, L<FS::cust_bill_pay>, L<FS::cust_bill>, L<FS::Record>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pay_batch.pm b/FS/FS/cust_pay_batch.pm
new file mode 100644
index 0000000..9ef1e1c
--- /dev/null
+++ b/FS/FS/cust_pay_batch.pm
@@ -0,0 +1,277 @@
+package FS::cust_pay_batch;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Carp qw( confess );
+use Business::CreditCard 0.28;
+use FS::Record qw(dbh qsearch qsearchs);
+use FS::payinfo_Mixin;
+use FS::cust_main;
+use FS::cust_bill;
+
+@ISA = qw( FS::payinfo_Mixin FS::Record );
+
+# 1 is mostly method/subroutine entry and options
+# 2 traces progress of some operations
+# 3 is even more information including possibly sensitive data
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::cust_pay_batch - Object methods for batch cards
+
+=head1 SYNOPSIS
+
+ use FS::cust_pay_batch;
+
+ $record = new FS::cust_pay_batch \%hash;
+ $record = new FS::cust_pay_batch { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ #deprecated# $error = $record->retriable;
+
+=head1 DESCRIPTION
+
+An FS::cust_pay_batch object represents a credit card transaction ready to be
+batched (sent to a processor). FS::cust_pay_batch inherits from FS::Record.
+Typically called by the collect method of an FS::cust_main object. The
+following fields are currently supported:
+
+=over 4
+
+=item paybatchnum - primary key (automatically assigned)
+
+=item batchnum - indentifies group in batch
+
+=item payby - CARD/CHEK/LECB/BILL/COMP
+
+=item payinfo
+
+=item exp - card expiration
+
+=item amount
+
+=item invnum - invoice
+
+=item custnum - customer
+
+=item payname - name on card
+
+=item first - name
+
+=item last - name
+
+=item address1
+
+=item address2
+
+=item city
+
+=item state
+
+=item zip
+
+=item country
+
+=item status
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_pay_batch'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid transaction. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('paybatchnum')
+ || $self->ut_numbern('trancode') #deprecated
+ || $self->ut_money('amount')
+ || $self->ut_number('invnum')
+ || $self->ut_number('custnum')
+ || $self->ut_text('address1')
+ || $self->ut_textn('address2')
+ || $self->ut_text('city')
+ || $self->ut_textn('state')
+ ;
+
+ return $error if $error;
+
+ $self->getfield('last') =~ /^([\w \,\.\-\']+)$/ or return "Illegal last name";
+ $self->setfield('last',$1);
+
+ $self->first =~ /^([\w \,\.\-\']+)$/ or return "Illegal first name";
+ $self->first($1);
+
+ $error = $self->payinfo_check();
+ return $error if $error;
+
+ if ( $self->exp eq '' ) {
+ return "Expiration date required"
+ unless $self->payby =~ /^(CHEK|DCHK|LECB|WEST)$/;
+ $self->exp('');
+ } else {
+ if ( $self->exp =~ /^(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})$/ ) {
+ $self->exp("$1-$2-$3");
+ } elsif ( $self->exp =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/ ) {
+ if ( length($2) == 4 ) {
+ $self->exp("$2-$1-01");
+ } elsif ( $2 > 98 ) { #should pry change to check for "this year"
+ $self->exp("19$2-$1-01");
+ } else {
+ $self->exp("20$2-$1-01");
+ }
+ } else {
+ return "Illegal expiration date";
+ }
+ }
+
+ if ( $self->payname eq '' ) {
+ $self->payname( $self->first. " ". $self->getfield('last') );
+ } else {
+ $self->payname =~ /^([\w \,\.\-\']+)$/
+ or return "Illegal billing name";
+ $self->payname($1);
+ }
+
+ #we have lots of old zips in there... don't hork up batch results cause of em
+ $self->zip =~ /^\s*(\w[\w\-\s]{2,8}\w)\s*$/
+ or return "Illegal zip: ". $self->zip;
+ $self->zip($1);
+
+ $self->country =~ /^(\w\w)$/ or return "Illegal country: ". $self->country;
+ $self->country($1);
+
+ #$error = $self->ut_zip('zip', $self->country);
+ #return $error if $error;
+
+ #check invnum, custnum, ?
+
+ $self->SUPER::check;
+}
+
+=item cust_main
+
+Returns the customer (see L<FS::cust_main>) for this batched credit card
+payment.
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+#you know what, screw this in the new world of events. we should be able to
+#get the event defs to retry (remove once.pm condition, add every.pm) without
+#mucking about with statuses of previous cust_event records. right?
+#
+#=item retriable
+#
+#Marks the corresponding event (see L<FS::cust_bill_event>) for this batched
+#credit card payment as retriable. Useful if the corresponding financial
+#institution account was declined for temporary reasons and/or a manual
+#retry is desired.
+#
+#Implementation details: For the named customer's invoice, changes the
+#statustext of the 'done' (without statustext) event to 'retriable.'
+#
+#=cut
+
+sub retriable {
+
+ confess "deprecated method cust_pay_batch->retriable called; try removing ".
+ "the once condition and adding an every condition?";
+
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE'; #Hmm
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $self->invnum } )
+ or return "event $self->eventnum references nonexistant invoice $self->invnum";
+
+ warn "cust_pay_batch->retriable working with self of " . $self->paybatchnum . " and invnum of " . $self->invnum;
+ my @cust_bill_event =
+ sort { $a->part_bill_event->seconds <=> $b->part_bill_event->seconds }
+ grep {
+ $_->part_bill_event->eventcode =~ /\$cust_bill->batch_card/
+ && $_->status eq 'done'
+ && ! $_->statustext
+ }
+ $cust_bill->cust_bill_event;
+ # complain loudly if scalar(@cust_bill_event) > 1 ?
+ my $error = $cust_bill_event[0]->retriable;
+ if ($error ) {
+ # gah, even with transactions.
+ $dbh->commit if $oldAutoCommit; #well.
+ return "error marking invoice event retriable: $error";
+ }
+ '';
+}
+
+=back
+
+=head1 BUGS
+
+There should probably be a configuration file with a list of allowed credit
+card types.
+
+=head1 SEE ALSO
+
+L<FS::cust_main>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pay_pending.pm b/FS/FS/cust_pay_pending.pm
new file mode 100644
index 0000000..bbabd24
--- /dev/null
+++ b/FS/FS/cust_pay_pending.pm
@@ -0,0 +1,321 @@
+package FS::cust_pay_pending;
+
+use strict;
+use vars qw( @ISA @encrypted_fields );
+use FS::Record qw( qsearch qsearchs dbh ); #dbh for _upgrade_data
+use FS::payinfo_transaction_Mixin;
+use FS::cust_main_Mixin;
+use FS::cust_main;
+use FS::cust_pay;
+
+@ISA = qw( FS::payinfo_transaction_Mixin FS::cust_main_Mixin FS::Record );
+
+@encrypted_fields = ('payinfo');
+
+=head1 NAME
+
+FS::cust_pay_pending - Object methods for cust_pay_pending records
+
+=head1 SYNOPSIS
+
+ use FS::cust_pay_pending;
+
+ $record = new FS::cust_pay_pending \%hash;
+ $record = new FS::cust_pay_pending { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pay_pending object represents an pending payment. It reflects
+local state through the multiple stages of processing a real-time transaction
+with an external gateway. FS::cust_pay_pending inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item paypendingnum
+
+Primary key
+
+=item custnum
+
+Customer (see L<FS::cust_main>)
+
+=item paid
+
+Amount of this payment
+
+=item _date
+
+Specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item payby
+
+Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
+
+=item payinfo
+
+Payment Information (See L<FS::payinfo_Mixin> for data format)
+
+=item paymask
+
+Masked payinfo (See L<FS::payinfo_Mixin> for how this works)
+
+=item paydate
+
+Expiration date
+
+=item payunique
+
+Unique identifer to prevent duplicate transactions.
+
+=item status
+
+Pending transaction status, one of the following:
+
+=over 4
+
+=item new
+
+Aquires basic lock on payunique
+
+=item pending
+
+Transaction is pending with the gateway
+
+=item authorized
+
+Only used for two-stage transactions that require a separate capture step
+
+=item captured
+
+Transaction completed with payment gateway (sucessfully), not yet recorded in
+the database
+
+=item declined
+
+Transaction completed with payment gateway (declined), not yet recorded in
+the database
+
+=item done
+
+Transaction recorded in database
+
+=back
+
+=item statustext
+
+Additional status information.
+
+=cut
+
+#=item cust_balance -
+
+=item paynum -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new pending payment. To add the pending payment to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_pay_pending'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid pending payment. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('paypendingnum')
+ || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
+ || $self->ut_money('paid')
+ || $self->ut_numbern('_date')
+ || $self->ut_textn('payunique')
+ || $self->ut_text('status')
+ #|| $self->ut_textn('statustext')
+ || $self->ut_anything('statustext')
+ #|| $self->ut_money('cust_balance')
+ || $self->ut_foreign_keyn('paynum', 'cust_pay', 'paynum' )
+ || $self->payinfo_check() #payby/payinfo/paymask/paydate
+ ;
+ return $error if $error;
+
+ $self->_date(time) unless $self->_date;
+
+ # UNIQUE index should catch this too, without race conditions, but this
+ # should give a better error message the other 99.9% of the time...
+ if ( length($self->payunique) ) {
+ my $cust_pay_pending = qsearchs('cust_pay_pending', {
+ 'payunique' => $self->payunique,
+ 'paypendingnum' => { op=>'!=', value=>$self->paypendingnum },
+ });
+ if ( $cust_pay_pending ) {
+ #well, it *could* be a better error message
+ return "duplicate transaction - a payment with unique identifer ".
+ $self->payunique. " already exists";
+ }
+ }
+
+ $self->SUPER::check;
+}
+
+#these two are kind-of false laziness w/cust_main::realtime_bop
+#(currently only used when resolving pending payments manually)
+
+=item insert_cust_pay
+
+Sets the status of this pending pament to "done" (with statustext
+"captured (manual)"), and inserts a payment record (see L<FS::cust_pay>).
+
+Currently only used when resolving pending payments manually.
+
+=cut
+
+sub insert_cust_pay {
+ my $self = shift;
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $self->custnum,
+ 'paid' => $self->paid,
+ '_date' => $self->_date, #better than passing '' for now
+ 'payby' => $self->payby,
+ 'payinfo' => $self->payinfo,
+ 'paybatch' => $self->paybatch,
+ 'paydate' => $self->paydate,
+ } );
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #start a transaction, insert the cust_pay and set cust_pay_pending.status to done in a single transction
+
+ my $error = $cust_pay->insert;#($options{'manual'} ? ( 'manual' => 1 ) : () );
+
+ if ( $error ) {
+ # gah.
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $error;
+ }
+
+ $self->status('done');
+ $self->statustext('captured (manual)');
+ $self->paynum($cust_pay->paynum);
+ my $cpp_done_err = $self->replace;
+
+ if ( $cpp_done_err ) {
+
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ return $cpp_done_err;
+
+ } else {
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return ''; #no error
+
+ }
+
+}
+
+=item decline
+
+Sets the status of this pending pament to "done" (with statustext
+"declined (manual)").
+
+Currently only used when resolving pending payments manually.
+
+=cut
+
+sub decline {
+ my $self = shift;
+
+ #could send decline email too? doesn't seem useful in manual resolution
+
+ $self->status('done');
+ $self->statustext("declined (manual)");
+ $self->replace;
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+sub _upgrade_data { #class method
+ my ($class, %opts) = @_;
+
+ my $sql =
+ "DELETE FROM cust_pay_pending WHERE status = 'new' AND _date < ".(time-600);
+
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pay_refund.pm b/FS/FS/cust_pay_refund.pm
new file mode 100644
index 0000000..cb9dbce
--- /dev/null
+++ b/FS/FS/cust_pay_refund.pm
@@ -0,0 +1,188 @@
+package FS::cust_pay_refund;
+
+use strict;
+use vars qw( @ISA ); #$conf );
+use FS::UID qw( getotaker );
+use FS::Record qw( qsearchs ); # qsearch );
+use FS::cust_main;
+use FS::cust_pay;
+use FS::cust_refund;
+
+@ISA = qw( FS::Record );
+
+#ask FS::UID to run this stuff for us later
+#FS::UID->install_callback( sub {
+# $conf = new FS::Conf;
+#} );
+
+=head1 NAME
+
+FS::cust_pay_refund - Object methods for cust_pay_refund records
+
+=head1 SYNOPSIS
+
+ use FS::cust_pay_refund;
+
+ $record = new FS::cust_pay_refund \%hash;
+ $record = new FS::cust_pay_refund { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pay_refund object represents application of a refund (see
+L<FS::cust_refund>) to an payment (see L<FS::cust_pay>). FS::cust_pay_refund
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item payrefundnum - primary key
+
+=item paynum - credit being applied
+
+=item refundnum - invoice to which credit is applied (see L<FS::cust_bill>)
+
+=item amount - amount of the credit applied
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new cust_pay_refund. To add the cust_pay_refund to the database,
+see L<"insert">.
+
+=cut
+
+sub table { 'cust_pay_refund'; }
+
+=item insert
+
+Adds this cust_pay_refund to the database. If there is an error, returns the
+error, otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ return "Can't apply refund to closed payment"
+ if $self->cust_pay->closed =~ /^Y/i;
+ return "Can't apply payment to closed refund"
+ if $self->cust_refund->closed =~ /^Y/i;
+ $self->SUPER::insert(@_);
+}
+
+=item delete
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return "Can't remove refund from closed payment"
+ if $self->cust_pay->closed =~ /^Y/i;
+ return "Can't remove payment from closed refund"
+ if $self->cust_refund->closed =~ /^Y/i;
+ $self->SUPER::delete(@_);
+}
+
+=item replace OLD_RECORD
+
+Application of refunds to payments may not be modified.
+
+=cut
+
+sub replace {
+ return "Can't modify application of a refund to payment!"
+}
+
+=item check
+
+Checks all fields to make sure this is a valid refund application to a payment.
+If there is an error, returns the error, otherwise returns false. Called by
+the insert and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('payrefundnum')
+ || $self->ut_number('paynum')
+ || $self->ut_number('refundnum')
+ || $self->ut_numbern('_date')
+ || $self->ut_money('amount')
+ ;
+ return $error if $error;
+
+ return "amount must be > 0" if $self->amount <= 0;
+
+ return "Unknown payment"
+ unless my $cust_pay =
+ qsearchs( 'cust_pay', { 'paynum' => $self->paynum } );
+
+ return "Unknown refund"
+ unless my $cust_refund =
+ qsearchs( 'cust_refund', { 'refundnum' => $self->refundnum } );
+
+ $self->_date(time) unless $self->_date;
+
+ return 'Cannot apply ($'. $self->amount. ') more than'.
+ ' remaining value of refund ($'. $cust_refund->unapplied. ')'
+ unless $self->amount <= $cust_refund->unapplied;
+
+ return "Cannot apply more than remaining value of payment"
+ unless $self->amount <= $cust_pay->unapplied;
+
+ $self->SUPER::check;
+}
+
+=item sub cust_pay
+
+Returns the payment (see L<FS::cust_pay>)
+
+=cut
+
+sub cust_pay {
+ my $self = shift;
+ qsearchs( 'cust_pay', { 'paynum' => $self->paynum } );
+}
+
+=item cust_refund
+
+Returns the refund (see L<FS::cust_refund>)
+
+=cut
+
+sub cust_refund {
+ my $self = shift;
+ qsearchs( 'cust_refund', { 'refundnum' => $self->refundnum } );
+}
+
+=back
+
+=head1 BUGS
+
+The delete method.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_refund>, L<FS::cust_bill>, L<FS::cust_credit>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pay_void.pm b/FS/FS/cust_pay_void.pm
new file mode 100644
index 0000000..de05f71
--- /dev/null
+++ b/FS/FS/cust_pay_void.pm
@@ -0,0 +1,225 @@
+package FS::cust_pay_void;
+use strict;
+use vars qw( @ISA @encrypted_fields );
+use Business::CreditCard;
+use FS::UID qw(getotaker);
+use FS::Record qw(qsearchs dbh fields); # qsearch );
+use FS::cust_pay;
+#use FS::cust_bill;
+#use FS::cust_bill_pay;
+#use FS::cust_pay_refund;
+#use FS::cust_main;
+
+@ISA = qw( FS::Record FS::payinfo_Mixin );
+
+@encrypted_fields = ('payinfo');
+
+=head1 NAME
+
+FS::cust_pay_void - Object methods for cust_pay_void objects
+
+=head1 SYNOPSIS
+
+ use FS::cust_pay_void;
+
+ $record = new FS::cust_pay_void \%hash;
+ $record = new FS::cust_pay_void { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pay_void object represents a voided payment. The following fields
+are currently supported:
+
+=over 4
+
+=item paynum - primary key (assigned automatically for new payments)
+
+=item custnum - customer (see L<FS::cust_main>)
+
+=item paid - Amount of this payment
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item payby - `CARD' (credit cards), `CHEK' (electronic check/ACH),
+`LECB' (phone bill billing), `BILL' (billing), `CASH' (cash),
+`WEST' (Western Union), `MCRD' (Manual credit card), or `COMP' (free)
+
+=item payinfo - card number, check #, or comp issuer (4-8 lowercase alphanumerics; think username), respectively
+
+=item paybatch - text field for tracking card processing
+
+=item closed - books closed flag, empty or `Y'
+
+=item void_date
+
+=item reason
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new payment. To add the payment to the databse, see L<"insert">.
+
+=cut
+
+sub table { 'cust_pay_void'; }
+
+=item insert
+
+Adds this voided payment to the database.
+
+=item unvoid
+
+"Un-void"s this payment: Deletes the voided payment from the database and adds
+back a normal payment.
+
+=cut
+
+sub unvoid {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $cust_pay = new FS::cust_pay ( {
+ map { $_ => $self->get($_) } fields('cust_pay')
+ } );
+ my $error = $cust_pay->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $error = $self->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Deletes this voided payment. You probably don't want to use this directly; see
+the B<unvoid> method to add the original payment back.
+
+=item replace OLD_RECORD
+
+Currently unimplemented.
+
+=cut
+
+sub replace {
+ return "Can't modify voided payments!";
+}
+
+=item check
+
+Checks all fields to make sure this is a valid voided payment. If there is an
+error, returns the error, otherwise returns false. Called by the insert
+method.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('paynum')
+ || $self->ut_numbern('custnum')
+ || $self->ut_money('paid')
+ || $self->ut_number('_date')
+ || $self->ut_textn('paybatch')
+ || $self->ut_enum('closed', [ '', 'Y' ])
+ || $self->ut_numbern('void_date')
+ || $self->ut_textn('reason')
+ ;
+ return $error if $error;
+
+ return "paid must be > 0 " if $self->paid <= 0;
+
+ return "unknown cust_main.custnum: ". $self->custnum
+ unless $self->invnum
+ || qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+
+ $self->void_date(time) unless $self->void_date;
+
+ $self->payby =~ /^(CARD|CHEK|LECB|BILL|COMP|PREP|CASH|WEST|MCRD)$/
+ or return "Illegal payby";
+ $self->payby($1);
+
+ #false laziness with cust_refund::check
+ if ( $self->payby eq 'CARD' ) {
+ my $payinfo = $self->payinfo;
+ $payinfo =~ s/\D//g;
+ $self->payinfo($payinfo);
+ if ( $self->payinfo ) {
+ $self->payinfo =~ /^(\d{13,16})$/
+ or return "Illegal (mistyped?) credit card number (payinfo)";
+ $self->payinfo($1);
+ validate($self->payinfo) or return "Illegal credit card number";
+ return "Unknown card type" if cardtype($self->payinfo) eq "Unknown";
+ } else {
+ $self->payinfo('N/A');
+ }
+
+ } else {
+ $error = $self->ut_textn('payinfo');
+ return $error if $error;
+ }
+
+ $self->otaker(getotaker);
+
+ $self->SUPER::check;
+}
+
+=item cust_main
+
+Returns the parent customer object (see L<FS::cust_main>).
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+=back
+
+=head1 BUGS
+
+Delete and replace methods.
+
+=head1 SEE ALSO
+
+L<FS::cust_pay>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
new file mode 100644
index 0000000..dd6db1b
--- /dev/null
+++ b/FS/FS/cust_pkg.pm
@@ -0,0 +1,2775 @@
+package FS::cust_pkg;
+
+use strict;
+use vars qw(@ISA $disable_agentcheck $DEBUG);
+use Scalar::Util qw( blessed );
+use List::Util qw(max);
+use Tie::IxHash;
+use FS::UID qw( getotaker dbh );
+use FS::Misc qw( send_email );
+use FS::Record qw( qsearch qsearchs );
+use FS::m2m_Common;
+use FS::cust_main_Mixin;
+use FS::cust_svc;
+use FS::part_pkg;
+use FS::cust_main;
+use FS::cust_location;
+use FS::pkg_svc;
+use FS::cust_bill_pkg;
+use FS::cust_pkg_detail;
+use FS::cust_event;
+use FS::h_cust_svc;
+use FS::reg_code;
+use FS::part_svc;
+use FS::cust_pkg_reason;
+use FS::reason;
+use FS::UI::Web;
+
+# need to 'use' these instead of 'require' in sub { cancel, suspend, unsuspend,
+# setup }
+# because they load configuration by setting FS::UID::callback (see TODO)
+use FS::svc_acct;
+use FS::svc_domain;
+use FS::svc_www;
+use FS::svc_forward;
+
+# for sending cancel emails in sub cancel
+use FS::Conf;
+
+@ISA = qw( FS::m2m_Common FS::cust_main_Mixin FS::option_Common FS::Record );
+
+$DEBUG = 0;
+
+$disable_agentcheck = 0;
+
+sub _cache {
+ my $self = shift;
+ my ( $hashref, $cache ) = @_;
+ #if ( $hashref->{'pkgpart'} ) {
+ if ( $hashref->{'pkg'} ) {
+ # #@{ $self->{'_pkgnum'} } = ();
+ # my $subcache = $cache->subcache('pkgpart', 'part_pkg');
+ # $self->{'_pkgpart'} = $subcache;
+ # #push @{ $self->{'_pkgnum'} },
+ # FS::part_pkg->new_or_cached($hashref, $subcache);
+ $self->{'_pkgpart'} = FS::part_pkg->new($hashref);
+ }
+ if ( exists $hashref->{'svcnum'} ) {
+ #@{ $self->{'_pkgnum'} } = ();
+ my $subcache = $cache->subcache('svcnum', 'cust_svc', $hashref->{pkgnum});
+ $self->{'_svcnum'} = $subcache;
+ #push @{ $self->{'_pkgnum'} },
+ FS::cust_svc->new_or_cached($hashref, $subcache) if $hashref->{svcnum};
+ }
+}
+
+=head1 NAME
+
+FS::cust_pkg - Object methods for cust_pkg objects
+
+=head1 SYNOPSIS
+
+ use FS::cust_pkg;
+
+ $record = new FS::cust_pkg \%hash;
+ $record = new FS::cust_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->cancel;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $part_pkg = $record->part_pkg;
+
+ @labels = $record->labels;
+
+ $seconds = $record->seconds_since($timestamp);
+
+ $error = FS::cust_pkg::order( $custnum, \@pkgparts );
+ $error = FS::cust_pkg::order( $custnum, \@pkgparts, \@remove_pkgnums ] );
+
+=head1 DESCRIPTION
+
+An FS::cust_pkg object represents a customer billing item. FS::cust_pkg
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item pkgnum
+
+Primary key (assigned automatically for new billing items)
+
+=item custnum
+
+Customer (see L<FS::cust_main>)
+
+=item pkgpart
+
+Billing item definition (see L<FS::part_pkg>)
+
+=item locationnum
+
+Optional link to package location (see L<FS::location>)
+
+=item setup
+
+date
+
+=item bill
+
+date (next bill date)
+
+=item last_bill
+
+last bill date
+
+=item adjourn
+
+date
+
+=item susp
+
+date
+
+=item expire
+
+date
+
+=item cancel
+
+date
+
+=item otaker
+
+order taker (assigned automatically if null, see L<FS::UID>)
+
+=item manual_flag
+
+If this field is set to 1, disables the automatic
+unsuspension of this package when using the B<unsuspendauto> config option.
+
+=item quantity
+
+If not set, defaults to 1
+
+=item change_date
+
+Date of change from previous package
+
+=item change_pkgnum
+
+Previous pkgnum
+
+=item change_pkgpart
+
+Previous pkgpart
+
+=item change_locationnum
+
+Previous locationnum
+
+=back
+
+Note: setup, last_bill, bill, adjourn, susp, expire, cancel and change_date
+are specified as UNIX timestamps; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Create a new billing item. To add the item to the database, see L<"insert">.
+
+=cut
+
+sub table { 'cust_pkg'; }
+sub cust_linked { $_[0]->cust_main_custnum; }
+sub cust_unlinked_msg {
+ my $self = shift;
+ "WARNING: can't find cust_main.custnum ". $self->custnum.
+ ' (cust_pkg.pkgnum '. $self->pkgnum. ')';
+}
+
+=item insert [ OPTION => VALUE ... ]
+
+Adds this billing item to the database ("Orders" the item). If there is an
+error, returns the error, otherwise returns false.
+
+If the additional field I<promo_code> is defined instead of I<pkgpart>, it
+will be used to look up the package definition and agent restrictions will be
+ignored.
+
+If the additional field I<refnum> is defined, an FS::pkg_referral record will
+be created and inserted. Multiple FS::pkg_referral records can be created by
+setting I<refnum> to an array reference of refnums or a hash reference with
+refnums as keys. If no I<refnum> is defined, a default FS::pkg_referral
+record will be created corresponding to cust_main.refnum.
+
+The following options are available:
+
+=over 4
+
+=item change
+
+If set true, supresses any referral credit to a referring customer.
+
+=item options
+
+cust_pkg_option records will be created
+
+=back
+
+=cut
+
+sub insert {
+ my( $self, %options ) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::insert($options{options} ? %{$options{options}} : ());
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $self->refnum($self->cust_main->refnum) unless $self->refnum;
+ $self->refnum( [ $self->refnum ] ) unless ref($self->refnum);
+ $self->process_m2m( 'link_table' => 'pkg_referral',
+ 'target_table' => 'part_referral',
+ 'params' => $self->refnum,
+ );
+
+ #if ( $self->reg_code ) {
+ # my $reg_code = qsearchs('reg_code', { 'code' => $self->reg_code } );
+ # $error = $reg_code->delete;
+ # if ( $error ) {
+ # $dbh->rollback if $oldAutoCommit;
+ # return $error;
+ # }
+ #}
+
+ my $conf = new FS::Conf;
+
+ if ($conf->config('welcome_letter') && $self->cust_main->num_pkgs == 1) {
+ my $queue = new FS::queue {
+ 'job' => 'FS::cust_main::queueable_print',
+ };
+ $error = $queue->insert(
+ 'custnum' => $self->custnum,
+ 'template' => 'welcome_letter',
+ );
+
+ if ($error) {
+ warn "can't send welcome letter: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item delete
+
+This method now works but you probably shouldn't use it.
+
+You don't want to delete billing items, because there would then be no record
+the customer ever purchased the item. Instead, see the cancel method.
+
+=cut
+
+#sub delete {
+# return "Can't delete cust_pkg records!";
+#}
+
+=item replace [ OLD_RECORD ] [ HASHREF | OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+Currently, custnum, setup, bill, adjourn, susp, expire, and cancel may be changed.
+
+Changing pkgpart may have disasterous effects. See the order subroutine.
+
+setup and bill are normally updated by calling the bill method of a customer
+object (see L<FS::cust_main>).
+
+suspend is normally updated by the suspend and unsuspend methods.
+
+cancel is normally updated by the cancel method (and also the order subroutine
+in some cases).
+
+Available options are:
+
+=over 4
+
+=item reason
+
+can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason.
+
+=item reason_otaker
+
+the access_user (see L<FS::access_user>) providing the reason
+
+=item options
+
+hashref of keys and values - cust_pkg_option records will be created, updated or removed as appopriate
+
+=back
+
+=cut
+
+sub replace {
+ my $new = shift;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $new->replace_old;
+
+ my $options =
+ ( ref($_[0]) eq 'HASH' )
+ ? shift
+ : { @_ };
+
+ #return "Can't (yet?) change pkgpart!" if $old->pkgpart != $new->pkgpart;
+ return "Can't change otaker!" if $old->otaker ne $new->otaker;
+
+ #allow this *sigh*
+ #return "Can't change setup once it exists!"
+ # if $old->getfield('setup') &&
+ # $old->getfield('setup') != $new->getfield('setup');
+
+ #some logic for bill, susp, cancel?
+
+ local($disable_agentcheck) = 1 if $old->pkgpart == $new->pkgpart;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $method ( qw(adjourn expire) ) { # How many reasons?
+ if ($options->{'reason'} && $new->$method && $old->$method ne $new->$method) {
+ my $error = $new->insert_reason(
+ 'reason' => $options->{'reason'},
+ 'date' => $new->$method,
+ 'action' => $method,
+ 'reason_otaker' => $options->{'reason_otaker'},
+ );
+ if ( $error ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Error inserting cust_pkg_reason: $error";
+ }
+ }
+ }
+
+ #save off and freeze RADIUS attributes for any associated svc_acct records
+ my @svc_acct = ();
+ if ( $old->part_pkg->is_prepaid || $new->part_pkg->is_prepaid ) {
+
+ #also check for specific exports?
+ # to avoid spurious modify export events
+ @svc_acct = map { $_->svc_x }
+ grep { $_->part_svc->svcdb eq 'svc_acct' }
+ $old->cust_svc;
+
+ $_->snapshot foreach @svc_acct;
+
+ }
+
+ my $error = $new->SUPER::replace($old,
+ $options->{options} ? $options->{options} : ()
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ #for prepaid packages,
+ #trigger export of new RADIUS Expiration attribute when cust_pkg.bill changes
+ foreach my $old_svc_acct ( @svc_acct ) {
+ my $new_svc_acct = new FS::svc_acct { $old_svc_acct->hash };
+ my $s_error = $new_svc_acct->replace($old_svc_acct);
+ if ( $s_error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $s_error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid billing item. If there is an
+error, returns the error, otherwise returns false. Called by the insert and
+replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->locationnum('')
+ if defined($self->locationnum) && length($self->locationnum)
+ && ( $self->locationnum == 0 || $self->locationnum == -1 );
+
+ my $error =
+ $self->ut_numbern('pkgnum')
+ || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
+ || $self->ut_numbern('pkgpart')
+ || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum')
+ || $self->ut_numbern('setup')
+ || $self->ut_numbern('bill')
+ || $self->ut_numbern('susp')
+ || $self->ut_numbern('cancel')
+ || $self->ut_numbern('adjourn')
+ || $self->ut_numbern('expire')
+ ;
+ return $error if $error;
+
+ if ( $self->reg_code ) {
+
+ unless ( grep { $self->pkgpart == $_->pkgpart }
+ map { $_->reg_code_pkg }
+ qsearchs( 'reg_code', { 'code' => $self->reg_code,
+ 'agentnum' => $self->cust_main->agentnum })
+ ) {
+ return "Unknown registration code";
+ }
+
+ } elsif ( $self->promo_code ) {
+
+ my $promo_part_pkg =
+ qsearchs('part_pkg', {
+ 'pkgpart' => $self->pkgpart,
+ 'promo_code' => { op=>'ILIKE', value=>$self->promo_code },
+ } );
+ return 'Unknown promotional code' unless $promo_part_pkg;
+
+ } else {
+
+ unless ( $disable_agentcheck ) {
+ my $agent =
+ qsearchs( 'agent', { 'agentnum' => $self->cust_main->agentnum } );
+ my $pkgpart_href = $agent->pkgpart_hashref;
+ return "agent ". $agent->agentnum.
+ " can't purchase pkgpart ". $self->pkgpart
+ unless $pkgpart_href->{ $self->pkgpart };
+ }
+
+ $error = $self->ut_foreign_key('pkgpart', 'part_pkg', 'pkgpart' );
+ return $error if $error;
+
+ }
+
+ $self->otaker(getotaker) unless $self->otaker;
+ $self->otaker =~ /^(\w{1,32})$/ or return "Illegal otaker";
+ $self->otaker($1);
+
+ if ( $self->dbdef_table->column('manual_flag') ) {
+ $self->manual_flag('') if $self->manual_flag eq ' ';
+ $self->manual_flag =~ /^([01]?)$/
+ or return "Illegal manual_flag ". $self->manual_flag;
+ $self->manual_flag($1);
+ }
+
+ $self->SUPER::check;
+}
+
+=item cancel [ OPTION => VALUE ... ]
+
+Cancels and removes all services (see L<FS::cust_svc> and L<FS::part_svc>)
+in this package, then cancels the package itself (sets the cancel field to
+now).
+
+Available options are:
+
+=over 4
+
+=item quiet - can be set true to supress email cancellation notices.
+
+=item time - can be set to cancel the package based on a specific future or historical date. Using time ensures that the remaining amount is calculated correctly. Note however that this is an immediate cancel and just changes the date. You are PROBABLY looking to expire the account instead of using this.
+
+=item reason - can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason.
+
+=item date - can be set to a unix style timestamp to specify when to cancel (expire)
+
+=back
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub cancel {
+ my( $self, %options ) = @_;
+ my $error;
+
+ warn "cust_pkg::cancel called with options".
+ join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $old = $self->select_for_update;
+
+ if ( $old->get('cancel') || $self->get('cancel') ) {
+ dbh->rollback if $oldAutoCommit;
+ return ""; # no error
+ }
+
+ my $date = $options{date} if $options{date}; # expire/cancel later
+ $date = '' if ($date && $date <= time); # complain instead?
+
+ my $cancel_time = $options{'time'} || time;
+
+ if ( $options{'reason'} ) {
+ $error = $self->insert_reason( 'reason' => $options{'reason'},
+ 'action' => $date ? 'expire' : 'cancel',
+ 'date' => $date ? $date : $cancel_time,
+ 'reason_otaker' => $options{'reason_otaker'},
+ );
+ if ( $error ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Error inserting cust_pkg_reason: $error";
+ }
+ }
+
+ my %svc;
+ unless ( $date ) {
+ foreach my $cust_svc (
+ #schwartz
+ map { $_->[0] }
+ sort { $a->[1] <=> $b->[1] }
+ map { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; }
+ qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } )
+ ) {
+
+ my $error = $cust_svc->cancel;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error cancelling cust_svc: $error";
+ }
+ }
+
+ # Add a credit for remaining service
+ my $remaining_value = $self->calc_remain(time=>$cancel_time);
+ if ( $remaining_value > 0 && !$options{'no_credit'} ) {
+ my $conf = new FS::Conf;
+ my $error = $self->cust_main->credit(
+ $remaining_value,
+ 'Credit for unused time on '. $self->part_pkg->pkg,
+ 'reason_type' => $conf->config('cancel_credit_type'),
+ );
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error crediting customer \$$remaining_value for unused time on".
+ $self->part_pkg->pkg. ": $error";
+ }
+ }
+ }
+
+ my %hash = $self->hash;
+ $date ? ($hash{'expire'} = $date) : ($hash{'cancel'} = $cancel_time);
+ my $new = new FS::cust_pkg ( \%hash );
+ $error = $new->replace( $self, options => { $self->options } );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return '' if $date; #no errors
+
+ my $conf = new FS::Conf;
+ my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list;
+ if ( !$options{'quiet'} && $conf->exists('emailcancel') && @invoicing_list ) {
+ my $conf = new FS::Conf;
+ my $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->cust_main->agentnum),
+ 'to' => \@invoicing_list,
+ 'subject' => ( $conf->config('cancelsubject') || 'Cancellation Notice' ),
+ 'body' => [ map "$_\n", $conf->config('cancelmessage') ],
+ );
+ #should this do something on errors?
+ }
+
+ ''; #no errors
+
+}
+
+=item cancel_if_expired [ NOW_TIMESTAMP ]
+
+Cancels this package if its expire date has been reached.
+
+=cut
+
+sub cancel_if_expired {
+ my $self = shift;
+ my $time = shift || time;
+ return '' unless $self->expire && $self->expire <= $time;
+ my $error = $self->cancel;
+ if ( $error ) {
+ return "Error cancelling expired pkg ". $self->pkgnum. " for custnum ".
+ $self->custnum. ": $error";
+ }
+ '';
+}
+
+=item unexpire
+
+Cancels any pending expiration (sets the expire field to null).
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub unexpire {
+ my( $self, %options ) = @_;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $old = $self->select_for_update;
+
+ my $pkgnum = $old->pkgnum;
+ if ( $old->get('cancel') || $self->get('cancel') ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Can't unexpire cancelled package $pkgnum";
+ # or at least it's pointless
+ }
+
+ unless ( $old->get('expire') && $self->get('expire') ) {
+ dbh->rollback if $oldAutoCommit;
+ return ""; # no error
+ }
+
+ my %hash = $self->hash;
+ $hash{'expire'} = '';
+ my $new = new FS::cust_pkg ( \%hash );
+ $error = $new->replace( $self, options => { $self->options } );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ''; #no errors
+
+}
+
+=item suspend [ OPTION => VALUE ... ]
+
+Suspends all services (see L<FS::cust_svc> and L<FS::part_svc>) in this
+package, then suspends the package itself (sets the susp field to now).
+
+Available options are:
+
+=over 4
+
+=item reason - can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason.
+
+=item date - can be set to a unix style timestamp to specify when to suspend (adjourn)
+
+=back
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub suspend {
+ my( $self, %options ) = @_;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $old = $self->select_for_update;
+
+ my $pkgnum = $old->pkgnum;
+ if ( $old->get('cancel') || $self->get('cancel') ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Can't suspend cancelled package $pkgnum";
+ }
+
+ if ( $old->get('susp') || $self->get('susp') ) {
+ dbh->rollback if $oldAutoCommit;
+ return ""; # no error # complain on adjourn?
+ }
+
+ my $date = $options{date} if $options{date}; # adjourn/suspend later
+ $date = '' if ($date && $date <= time); # complain instead?
+
+ if ( $date && $old->get('expire') && $old->get('expire') < $date ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Package $pkgnum expires before it would be suspended.";
+ }
+
+ my $suspend_time = $options{'time'} || time;
+
+ if ( $options{'reason'} ) {
+ $error = $self->insert_reason( 'reason' => $options{'reason'},
+ 'action' => $date ? 'adjourn' : 'suspend',
+ 'date' => $date ? $date : $suspend_time,
+ 'reason_otaker' => $options{'reason_otaker'},
+ );
+ if ( $error ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Error inserting cust_pkg_reason: $error";
+ }
+ }
+
+ unless ( $date ) {
+
+ my @labels = ();
+
+ foreach my $cust_svc (
+ qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } )
+ ) {
+ my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $cust_svc->svcpart } );
+
+ $part_svc->svcdb =~ /^([\w\-]+)$/ or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "Illegal svcdb value in part_svc!";
+ };
+ my $svcdb = $1;
+ require "FS/$svcdb.pm";
+
+ my $svc = qsearchs( $svcdb, { 'svcnum' => $cust_svc->svcnum } );
+ if ($svc) {
+ $error = $svc->suspend;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ my( $label, $value ) = $cust_svc->label;
+ push @labels, "$label: $value";
+ }
+ }
+
+ my $conf = new FS::Conf;
+ if ( $conf->config('suspend_email_admin') ) {
+
+ my $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->cust_main->agentnum),
+ #invoice_from ??? well as good as any
+ 'to' => $conf->config('suspend_email_admin'),
+ 'subject' => 'FREESIDE NOTIFICATION: Customer package suspended',
+ 'body' => [
+ "This is an automatic message from your Freeside installation\n",
+ "informing you that the following customer package has been suspended:\n",
+ "\n",
+ 'Customer: #'. $self->custnum. ' '. $self->cust_main->name. "\n",
+ 'Package : #'. $self->pkgnum. " (". $self->part_pkg->pkg_comment. ")\n",
+ ( map { "Service : $_\n" } @labels ),
+ ],
+ );
+
+ if ( $error ) {
+ warn "WARNING: can't send suspension admin email (suspending anyway): ".
+ "$error\n";
+ }
+
+ }
+
+ }
+
+ my %hash = $self->hash;
+ if ( $date ) {
+ $hash{'adjourn'} = $date;
+ } else {
+ $hash{'susp'} = $suspend_time;
+ }
+ my $new = new FS::cust_pkg ( \%hash );
+ $error = $new->replace( $self, options => { $self->options } );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ''; #no errors
+}
+
+=item unsuspend [ OPTION => VALUE ... ]
+
+Unsuspends all services (see L<FS::cust_svc> and L<FS::part_svc>) in this
+package, then unsuspends the package itself (clears the susp field and the
+adjourn field if it is in the past).
+
+Available options are:
+
+=over 4
+
+=item adjust_next_bill
+
+Can be set true to adjust the next bill date forward by
+the amount of time the account was inactive. This was set true by default
+since 1.4.2 and 1.5.0pre6; however, starting with 1.7.0 this needs to be
+explicitly requested. Price plans for which this makes sense (anniversary-date
+based than prorate or subscription) could have an option to enable this
+behaviour?
+
+=back
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub unsuspend {
+ my( $self, %opt ) = @_;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $old = $self->select_for_update;
+
+ my $pkgnum = $old->pkgnum;
+ if ( $old->get('cancel') || $self->get('cancel') ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Can't unsuspend cancelled package $pkgnum";
+ }
+
+ unless ( $old->get('susp') && $self->get('susp') ) {
+ dbh->rollback if $oldAutoCommit;
+ return ""; # no error # complain instead?
+ }
+
+ foreach my $cust_svc (
+ qsearch('cust_svc',{'pkgnum'=> $self->pkgnum } )
+ ) {
+ my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $cust_svc->svcpart } );
+
+ $part_svc->svcdb =~ /^([\w\-]+)$/ or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "Illegal svcdb value in part_svc!";
+ };
+ my $svcdb = $1;
+ require "FS/$svcdb.pm";
+
+ my $svc = qsearchs( $svcdb, { 'svcnum' => $cust_svc->svcnum } );
+ if ($svc) {
+ $error = $svc->unsuspend;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ }
+
+ my %hash = $self->hash;
+ my $inactive = time - $hash{'susp'};
+
+ my $conf = new FS::Conf;
+
+ $hash{'bill'} = ( $hash{'bill'} || $hash{'setup'} ) + $inactive
+ if ( $opt{'adjust_next_bill'}
+ || $conf->exists('unsuspend-always_adjust_next_bill_date') )
+ && $inactive > 0 && ( $hash{'bill'} || $hash{'setup'} );
+
+ $hash{'susp'} = '';
+ $hash{'adjourn'} = '' if $hash{'adjourn'} < time;
+ my $new = new FS::cust_pkg ( \%hash );
+ $error = $new->replace( $self, options => { $self->options } );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ''; #no errors
+}
+
+=item unadjourn
+
+Cancels any pending suspension (sets the adjourn field to null).
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub unadjourn {
+ my( $self, %options ) = @_;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $old = $self->select_for_update;
+
+ my $pkgnum = $old->pkgnum;
+ if ( $old->get('cancel') || $self->get('cancel') ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Can't unadjourn cancelled package $pkgnum";
+ # or at least it's pointless
+ }
+
+ if ( $old->get('susp') || $self->get('susp') ) {
+ dbh->rollback if $oldAutoCommit;
+ return "Can't unadjourn suspended package $pkgnum";
+ # perhaps this is arbitrary
+ }
+
+ unless ( $old->get('adjourn') && $self->get('adjourn') ) {
+ dbh->rollback if $oldAutoCommit;
+ return ""; # no error
+ }
+
+ my %hash = $self->hash;
+ $hash{'adjourn'} = '';
+ my $new = new FS::cust_pkg ( \%hash );
+ $error = $new->replace( $self, options => { $self->options } );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ''; #no errors
+
+}
+
+
+=item change HASHREF | OPTION => VALUE ...
+
+Changes this package: cancels it and creates a new one, with a different
+pkgpart or locationnum or both. All services are transferred to the new
+package (no change will be made if this is not possible).
+
+Options may be passed as a list of key/value pairs or as a hash reference.
+Options are:
+
+=over 4
+
+=item locaitonnum
+
+New locationnum, to change the location for this package.
+
+=item cust_location
+
+New FS::cust_location object, to create a new location and assign it
+to this package.
+
+=item pkgpart
+
+New pkgpart (see L<FS::part_pkg>).
+
+=item refnum
+
+New refnum (see L<FS::part_referral>).
+
+=back
+
+At least one option must be specified (otherwise, what's the point?)
+
+Returns either the new FS::cust_pkg object or a scalar error.
+
+For example:
+
+ my $err_or_new_cust_pkg = $old_cust_pkg->change
+
+=cut
+
+#some false laziness w/order
+sub change {
+ my $self = shift;
+ my $opt = ref($_[0]) ? shift : { @_ };
+
+# my ($custnum, $pkgparts, $remove_pkgnum, $return_cust_pkg, $refnum) = @_;
+#
+
+ my $conf = new FS::Conf;
+
+ # Transactionize this whole mess
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error;
+
+ my %hash = ();
+
+ my $time = time;
+
+ #$hash{$_} = $self->$_() foreach qw( last_bill bill );
+
+ #$hash{$_} = $self->$_() foreach qw( setup );
+
+ $hash{'setup'} = $time if $self->setup;
+
+ $hash{'change_date'} = $time;
+ $hash{"change_$_"} = $self->$_()
+ foreach qw( pkgnum pkgpart locationnum );
+
+ if ( $opt->{'cust_location'} &&
+ ( ! $opt->{'locationnum'} || $opt->{'locationnum'} == -1 ) ) {
+ $error = $opt->{'cust_location'}->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "inserting cust_location (transaction rolled back): $error";
+ }
+ $opt->{'locationnum'} = $opt->{'cust_location'}->locationnum;
+ }
+
+ # Create the new package.
+ my $cust_pkg = new FS::cust_pkg {
+ custnum => $self->custnum,
+ pkgpart => ( $opt->{'pkgpart'} || $self->pkgpart ),
+ refnum => ( $opt->{'refnum'} || $self->refnum ),
+ locationnum => ( $opt->{'locationnum'} || $self->locationnum ),
+ %hash,
+ };
+
+ $error = $cust_pkg->insert( 'change' => 1 );
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ # Transfer services and cancel old package.
+
+ $error = $self->transfer($cust_pkg);
+ if ($error and $error == 0) {
+ # $old_pkg->transfer failed.
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $error > 0 && $conf->exists('cust_pkg-change_svcpart') ) {
+ warn "trying transfer again with change_svcpart option\n" if $DEBUG;
+ $error = $self->transfer($cust_pkg, 'change_svcpart'=>1 );
+ if ($error and $error == 0) {
+ # $old_pkg->transfer failed.
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ if ($error > 0) {
+ # Transfers were successful, but we still had services left on the old
+ # package. We can't change the package under this circumstances, so abort.
+ $dbh->rollback if $oldAutoCommit;
+ return "Unable to transfer all services from package ". $self->pkgnum;
+ }
+
+ #Good to go, cancel old package.
+ $error = $self->cancel( quiet=>1 );
+ if ($error) {
+ $dbh->rollback;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ $cust_pkg;
+
+}
+
+=item last_bill
+
+Returns the last bill date, or if there is no last bill date, the setup date.
+Useful for billing metered services.
+
+=cut
+
+sub last_bill {
+ my $self = shift;
+ return $self->setfield('last_bill', $_[0]) if @_;
+ return $self->getfield('last_bill') if $self->getfield('last_bill');
+ my $cust_bill_pkg = qsearchs('cust_bill_pkg', { 'pkgnum' => $self->pkgnum,
+ 'edate' => $self->bill, } );
+ $cust_bill_pkg ? $cust_bill_pkg->sdate : $self->setup || 0;
+}
+
+=item last_cust_pkg_reason ACTION
+
+Returns the most recent ACTION FS::cust_pkg_reason associated with the package.
+Returns false if there is no reason or the package is not currenly ACTION'd
+ACTION is one of adjourn, susp, cancel, or expire.
+
+=cut
+
+sub last_cust_pkg_reason {
+ my ( $self, $action ) = ( shift, shift );
+ my $date = $self->get($action);
+ qsearchs( {
+ 'table' => 'cust_pkg_reason',
+ 'hashref' => { 'pkgnum' => $self->pkgnum,
+ 'action' => substr(uc($action), 0, 1),
+ 'date' => $date,
+ },
+ 'order_by' => 'ORDER BY num DESC LIMIT 1',
+ } );
+}
+
+=item last_reason ACTION
+
+Returns the most recent ACTION FS::reason associated with the package.
+Returns false if there is no reason or the package is not currenly ACTION'd
+ACTION is one of adjourn, susp, cancel, or expire.
+
+=cut
+
+sub last_reason {
+ my $cust_pkg_reason = shift->last_cust_pkg_reason(@_);
+ $cust_pkg_reason->reason
+ if $cust_pkg_reason;
+}
+
+=item part_pkg
+
+Returns the definition for this billing item, as an FS::part_pkg object (see
+L<FS::part_pkg>).
+
+=cut
+
+sub part_pkg {
+ my $self = shift;
+ #exists( $self->{'_pkgpart'} )
+ $self->{'_pkgpart'}
+ ? $self->{'_pkgpart'}
+ : qsearchs( 'part_pkg', { 'pkgpart' => $self->pkgpart } );
+}
+
+=item old_cust_pkg
+
+Returns the cancelled package this package was changed from, if any.
+
+=cut
+
+sub old_cust_pkg {
+ my $self = shift;
+ return '' unless $self->change_pkgnum;
+ qsearchs('cust_pkg', { 'pkgnum' => $self->change_pkgnum } );
+}
+
+=item calc_setup
+
+Calls the I<calc_setup> of the FS::part_pkg object associated with this billing
+item.
+
+=cut
+
+sub calc_setup {
+ my $self = shift;
+ $self->part_pkg->calc_setup($self, @_);
+}
+
+=item calc_recur
+
+Calls the I<calc_recur> of the FS::part_pkg object associated with this billing
+item.
+
+=cut
+
+sub calc_recur {
+ my $self = shift;
+ $self->part_pkg->calc_recur($self, @_);
+}
+
+=item calc_remain
+
+Calls the I<calc_remain> of the FS::part_pkg object associated with this
+billing item.
+
+=cut
+
+sub calc_remain {
+ my $self = shift;
+ $self->part_pkg->calc_remain($self, @_);
+}
+
+=item calc_cancel
+
+Calls the I<calc_cancel> of the FS::part_pkg object associated with this
+billing item.
+
+=cut
+
+sub calc_cancel {
+ my $self = shift;
+ $self->part_pkg->calc_cancel($self, @_);
+}
+
+=item cust_bill_pkg
+
+Returns any invoice line items for this package (see L<FS::cust_bill_pkg>).
+
+=cut
+
+sub cust_bill_pkg {
+ my $self = shift;
+ qsearch( 'cust_bill_pkg', { 'pkgnum' => $self->pkgnum } );
+}
+
+=item cust_pkg_detail [ DETAILTYPE ]
+
+Returns any customer package details for this package (see
+L<FS::cust_pkg_detail>).
+
+DETAILTYPE can be set to "I" for invoice details or "C" for comments.
+
+=cut
+
+sub cust_pkg_detail {
+ my $self = shift;
+ my %hash = ( 'pkgnum' => $self->pkgnum );
+ $hash{detailtype} = shift if @_;
+ qsearch({
+ 'table' => 'cust_pkg_detail',
+ 'hashref' => \%hash,
+ 'order_by' => 'ORDER BY weight, pkgdetailnum',
+ });
+}
+
+=item set_cust_pkg_detail DETAILTYPE [ DETAIL, DETAIL, ... ]
+
+Sets customer package details for this package (see L<FS::cust_pkg_detail>).
+
+DETAILTYPE can be set to "I" for invoice details or "C" for comments.
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub set_cust_pkg_detail {
+ my( $self, $detailtype, @details ) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $current ( $self->cust_pkg_detail($detailtype) ) {
+ my $error = $current->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error removing old detail: $error";
+ }
+ }
+
+ foreach my $detail ( @details ) {
+ my $cust_pkg_detail = new FS::cust_pkg_detail {
+ 'pkgnum' => $self->pkgnum,
+ 'detailtype' => $detailtype,
+ 'detail' => $detail,
+ };
+ my $error = $cust_pkg_detail->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error adding new detail: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item cust_event
+
+Returns the new-style customer billing events (see L<FS::cust_event>) for this invoice.
+
+=cut
+
+#false laziness w/cust_bill.pm
+sub cust_event {
+ my $self = shift;
+ qsearch({
+ 'table' => 'cust_event',
+ 'addl_from' => 'JOIN part_event USING ( eventpart )',
+ 'hashref' => { 'tablenum' => $self->pkgnum },
+ 'extra_sql' => " AND eventtable = 'cust_pkg' ",
+ });
+}
+
+=item num_cust_event
+
+Returns the number of new-style customer billing events (see L<FS::cust_event>) for this invoice.
+
+=cut
+
+#false laziness w/cust_bill.pm
+sub num_cust_event {
+ my $self = shift;
+ my $sql =
+ "SELECT COUNT(*) FROM cust_event JOIN part_event USING ( eventpart ) ".
+ " WHERE tablenum = ? AND eventtable = 'cust_pkg'";
+ my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
+ $sth->execute($self->pkgnum) or die $sth->errstr. " executing $sql";
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item cust_svc [ SVCPART ]
+
+Returns the services for this package, as FS::cust_svc objects (see
+L<FS::cust_svc>). If a svcpart is specified, return only the matching
+services.
+
+=cut
+
+sub cust_svc {
+ my $self = shift;
+
+ if ( @_ ) {
+ return qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum,
+ 'svcpart' => shift, } );
+ }
+
+ #if ( $self->{'_svcnum'} ) {
+ # values %{ $self->{'_svcnum'}->cache };
+ #} else {
+ $self->_sort_cust_svc(
+ [ qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } ) ]
+ );
+ #}
+
+}
+
+=item overlimit [ SVCPART ]
+
+Returns the services for this package which have exceeded their
+usage limit as FS::cust_svc objects (see L<FS::cust_svc>). If a svcpart
+is specified, return only the matching services.
+
+=cut
+
+sub overlimit {
+ my $self = shift;
+ grep { $_->overlimit } $self->cust_svc;
+}
+
+=item h_cust_svc END_TIMESTAMP [ START_TIMESTAMP ]
+
+Returns historical services for this package created before END TIMESTAMP and
+(optionally) not cancelled before START_TIMESTAMP, as FS::h_cust_svc objects
+(see L<FS::h_cust_svc>).
+
+=cut
+
+sub h_cust_svc {
+ my $self = shift;
+
+ $self->_sort_cust_svc(
+ [ qsearch( 'h_cust_svc',
+ { 'pkgnum' => $self->pkgnum, },
+ FS::h_cust_svc->sql_h_search(@_),
+ )
+ ]
+ );
+}
+
+sub _sort_cust_svc {
+ my( $self, $arrayref ) = @_;
+
+ map { $_->[0] }
+ sort { $b->[1] cmp $a->[1] or $a->[2] <=> $b->[2] }
+ map {
+ my $pkg_svc = qsearchs( 'pkg_svc', { 'pkgpart' => $self->pkgpart,
+ 'svcpart' => $_->svcpart } );
+ [ $_,
+ $pkg_svc ? $pkg_svc->primary_svc : '',
+ $pkg_svc ? $pkg_svc->quantity : 0,
+ ];
+ }
+ @$arrayref;
+
+}
+
+=item num_cust_svc [ SVCPART ]
+
+Returns the number of provisioned services for this package. If a svcpart is
+specified, counts only the matching services.
+
+=cut
+
+sub num_cust_svc {
+ my $self = shift;
+ my $sql = 'SELECT COUNT(*) FROM cust_svc WHERE pkgnum = ?';
+ $sql .= ' AND svcpart = ?' if @_;
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute($self->pkgnum, @_) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item available_part_svc
+
+Returns a list of FS::part_svc objects representing services included in this
+package but not yet provisioned. Each FS::part_svc object also has an extra
+field, I<num_avail>, which specifies the number of available services.
+
+=cut
+
+sub available_part_svc {
+ my $self = shift;
+ grep { $_->num_avail > 0 }
+ map {
+ my $part_svc = $_->part_svc;
+ $part_svc->{'Hash'}{'num_avail'} = #evil encapsulation-breaking
+ $_->quantity - $self->num_cust_svc($_->svcpart);
+ $part_svc;
+ }
+ $self->part_pkg->pkg_svc;
+}
+
+=item part_svc
+
+Returns a list of FS::part_svc objects representing provisioned and available
+services included in this package. Each FS::part_svc object also has the
+following extra fields:
+
+=over 4
+
+=item num_cust_svc (count)
+
+=item num_avail (quantity - count)
+
+=item cust_pkg_svc (services) - array reference containing the provisioned services, as cust_svc objects
+
+svcnum
+label -> ($cust_svc->label)[1]
+
+=back
+
+=cut
+
+sub part_svc {
+ my $self = shift;
+
+ #XXX some sort of sort order besides numeric by svcpart...
+ my @part_svc = sort { $a->svcpart <=> $b->svcpart } map {
+ my $pkg_svc = $_;
+ my $part_svc = $pkg_svc->part_svc;
+ my $num_cust_svc = $self->num_cust_svc($part_svc->svcpart);
+ $part_svc->{'Hash'}{'num_cust_svc'} = $num_cust_svc; #more evil
+ $part_svc->{'Hash'}{'num_avail'} =
+ max( 0, $pkg_svc->quantity - $num_cust_svc );
+ $part_svc->{'Hash'}{'cust_pkg_svc'} = [ $self->cust_svc($part_svc->svcpart) ];
+ $part_svc;
+ } $self->part_pkg->pkg_svc;
+
+ #extras
+ push @part_svc, map {
+ my $part_svc = $_;
+ my $num_cust_svc = $self->num_cust_svc($part_svc->svcpart);
+ $part_svc->{'Hash'}{'num_cust_svc'} = $num_cust_svc; #speak no evail
+ $part_svc->{'Hash'}{'num_avail'} = 0; #0-$num_cust_svc ?
+ $part_svc->{'Hash'}{'cust_pkg_svc'} = [ $self->cust_svc($part_svc->svcpart) ];
+ $part_svc;
+ } $self->extra_part_svc;
+
+ @part_svc;
+
+}
+
+=item extra_part_svc
+
+Returns a list of FS::part_svc objects corresponding to services in this
+package which are still provisioned but not (any longer) available in the
+package definition.
+
+=cut
+
+sub extra_part_svc {
+ my $self = shift;
+
+ my $pkgnum = $self->pkgnum;
+ my $pkgpart = $self->pkgpart;
+
+ qsearch( {
+ 'table' => 'part_svc',
+ 'hashref' => {},
+ 'extra_sql' => "WHERE 0 = ( SELECT COUNT(*) FROM pkg_svc
+ WHERE pkg_svc.svcpart = part_svc.svcpart
+ AND pkg_svc.pkgpart = $pkgpart
+ AND quantity > 0
+ )
+ AND 0 < ( SELECT count(*)
+ FROM cust_svc
+ LEFT JOIN cust_pkg using ( pkgnum )
+ WHERE cust_svc.svcpart = part_svc.svcpart
+ AND pkgnum = $pkgnum
+ )",
+ } );
+}
+
+=item status
+
+Returns a short status string for this package, currently:
+
+=over 4
+
+=item not yet billed
+
+=item one-time charge
+
+=item active
+
+=item suspended
+
+=item cancelled
+
+=back
+
+=cut
+
+sub status {
+ my $self = shift;
+
+ my $freq = length($self->freq) ? $self->freq : $self->part_pkg->freq;
+
+ return 'cancelled' if $self->get('cancel');
+ return 'suspended' if $self->susp;
+ return 'not yet billed' unless $self->setup;
+ return 'one-time charge' if $freq =~ /^(0|$)/;
+ return 'active';
+}
+
+=item statuses
+
+Class method that returns the list of possible status strings for packages
+(see L<the status method|/status>). For example:
+
+ @statuses = FS::cust_pkg->statuses();
+
+=cut
+
+tie my %statuscolor, 'Tie::IxHash',
+ 'not yet billed' => '000000',
+ 'one-time charge' => '000000',
+ 'active' => '00CC00',
+ 'suspended' => 'FF9900',
+ 'cancelled' => 'FF0000',
+;
+
+sub statuses {
+ my $self = shift; #could be class...
+ grep { $_ !~ /^(not yet billed)$/ } #this is a dumb status anyway
+ # mayble split btw one-time vs. recur
+ keys %statuscolor;
+}
+
+=item statuscolor
+
+Returns a hex triplet color string for this package's status.
+
+=cut
+
+sub statuscolor {
+ my $self = shift;
+ $statuscolor{$self->status};
+}
+
+=item labels
+
+Returns a list of lists, calling the label method for all services
+(see L<FS::cust_svc>) of this billing item.
+
+=cut
+
+sub labels {
+ my $self = shift;
+ map { [ $_->label ] } $self->cust_svc;
+}
+
+=item h_labels END_TIMESTAMP [ START_TIMESTAMP ]
+
+Like the labels method, but returns historical information on services that
+were active as of END_TIMESTAMP and (optionally) not cancelled before
+START_TIMESTAMP.
+
+Returns a list of lists, calling the label method for all (historical) services
+(see L<FS::h_cust_svc>) of this billing item.
+
+=cut
+
+sub h_labels {
+ my $self = shift;
+ map { [ $_->label(@_) ] } $self->h_cust_svc(@_);
+}
+
+=item h_labels_short END_TIMESTAMP [ START_TIMESTAMP ]
+
+Like h_labels, except returns a simple flat list, and shortens long
+(currently >5 or the cust_bill-max_same_services configuration value) lists of
+identical services to one line that lists the service label and the number of
+individual services rather than individual items.
+
+=cut
+
+sub h_labels_short {
+ my $self = shift;
+
+ my $conf = new FS::Conf;
+ my $max_same_services = $conf->config('cust_bill-max_same_services') || 5;
+
+ my %labels;
+ #tie %labels, 'Tie::IxHash';
+ push @{ $labels{$_->[0]} }, $_->[1]
+ foreach $self->h_labels(@_);
+ my @labels;
+ foreach my $label ( keys %labels ) {
+ my %seen = ();
+ my @values = grep { ! $seen{$_}++ } @{ $labels{$label} };
+ my $num = scalar(@values);
+ if ( $num > $max_same_services ) {
+ push @labels, "$label ($num)";
+ } else {
+ push @labels, map { "$label: $_" } @values;
+ }
+ }
+
+ @labels;
+
+}
+
+=item cust_main
+
+Returns the parent customer object (see L<FS::cust_main>).
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+=item cust_location
+
+Returns the location object, if any (see L<FS::cust_location>).
+
+=cut
+
+sub cust_location {
+ my $self = shift;
+ return '' unless $self->locationnum;
+ qsearchs( 'cust_location', { 'locationnum' => $self->locationnum } );
+}
+
+=item cust_location_or_main
+
+If this package is associated with a location, returns the locaiton (see
+L<FS::cust_location>), otherwise returns the customer (see L<FS::cust_main>).
+
+=cut
+
+sub cust_location_or_main {
+ my $self = shift;
+ $self->cust_location || $self->cust_main;
+}
+
+=item seconds_since TIMESTAMP
+
+Returns the number of seconds all accounts (see L<FS::svc_acct>) in this
+package have been online since TIMESTAMP, according to the session monitor.
+
+TIMESTAMP is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub seconds_since {
+ my($self, $since) = @_;
+ my $seconds = 0;
+
+ foreach my $cust_svc (
+ grep { $_->part_svc->svcdb eq 'svc_acct' } $self->cust_svc
+ ) {
+ $seconds += $cust_svc->seconds_since($since);
+ }
+
+ $seconds;
+
+}
+
+=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END
+
+Returns the numbers of seconds all accounts (see L<FS::svc_acct>) in this
+package have been online between TIMESTAMP_START (inclusive) and TIMESTAMP_END
+(exclusive).
+
+TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
+L<perlfunc/"time">. Also see L<Time::Local> and L<Date::Parse> for conversion
+functions.
+
+
+=cut
+
+sub seconds_since_sqlradacct {
+ my($self, $start, $end) = @_;
+
+ my $seconds = 0;
+
+ foreach my $cust_svc (
+ grep {
+ my $part_svc = $_->part_svc;
+ $part_svc->svcdb eq 'svc_acct'
+ && scalar($part_svc->part_export('sqlradius'));
+ } $self->cust_svc
+ ) {
+ $seconds += $cust_svc->seconds_since_sqlradacct($start, $end);
+ }
+
+ $seconds;
+
+}
+
+=item attribute_since_sqlradacct TIMESTAMP_START TIMESTAMP_END ATTRIBUTE
+
+Returns the sum of the given attribute for all accounts (see L<FS::svc_acct>)
+in this package for sessions ending between TIMESTAMP_START (inclusive) and
+TIMESTAMP_END
+(exclusive).
+
+TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
+L<perlfunc/"time">. Also see L<Time::Local> and L<Date::Parse> for conversion
+functions.
+
+=cut
+
+sub attribute_since_sqlradacct {
+ my($self, $start, $end, $attrib) = @_;
+
+ my $sum = 0;
+
+ foreach my $cust_svc (
+ grep {
+ my $part_svc = $_->part_svc;
+ $part_svc->svcdb eq 'svc_acct'
+ && scalar($part_svc->part_export('sqlradius'));
+ } $self->cust_svc
+ ) {
+ $sum += $cust_svc->attribute_since_sqlradacct($start, $end, $attrib);
+ }
+
+ $sum;
+
+}
+
+=item quantity
+
+=cut
+
+sub quantity {
+ my( $self, $value ) = @_;
+ if ( defined($value) ) {
+ $self->setfield('quantity', $value);
+ }
+ $self->getfield('quantity') || 1;
+}
+
+=item transfer DEST_PKGNUM | DEST_CUST_PKG, [ OPTION => VALUE ... ]
+
+Transfers as many services as possible from this package to another package.
+
+The destination package can be specified by pkgnum by passing an FS::cust_pkg
+object. The destination package must already exist.
+
+Services are moved only if the destination allows services with the correct
+I<svcpart> (not svcdb), unless the B<change_svcpart> option is set true. Use
+this option with caution! No provision is made for export differences
+between the old and new service definitions. Probably only should be used
+when your exports for all service definitions of a given svcdb are identical.
+(attempt a transfer without it first, to move all possible svcpart-matching
+services)
+
+Any services that can't be moved remain in the original package.
+
+Returns an error, if there is one; otherwise, returns the number of services
+that couldn't be moved.
+
+=cut
+
+sub transfer {
+ my ($self, $dest_pkgnum, %opt) = @_;
+
+ my $remaining = 0;
+ my $dest;
+ my %target;
+
+ if (ref ($dest_pkgnum) eq 'FS::cust_pkg') {
+ $dest = $dest_pkgnum;
+ $dest_pkgnum = $dest->pkgnum;
+ } else {
+ $dest = qsearchs('cust_pkg', { pkgnum => $dest_pkgnum });
+ }
+
+ return ('Package does not exist: '.$dest_pkgnum) unless $dest;
+
+ foreach my $pkg_svc ( $dest->part_pkg->pkg_svc ) {
+ $target{$pkg_svc->svcpart} = $pkg_svc->quantity;
+ }
+
+ foreach my $cust_svc ($dest->cust_svc) {
+ $target{$cust_svc->svcpart}--;
+ }
+
+ my %svcpart2svcparts = ();
+ if ( exists $opt{'change_svcpart'} && $opt{'change_svcpart'} ) {
+ warn "change_svcpart option received, creating alternates list\n" if $DEBUG;
+ foreach my $svcpart ( map { $_->svcpart } $self->cust_svc ) {
+ next if exists $svcpart2svcparts{$svcpart};
+ my $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } );
+ $svcpart2svcparts{$svcpart} = [
+ map { $_->[0] }
+ sort { $b->[1] cmp $a->[1] or $a->[2] <=> $b->[2] }
+ map {
+ my $pkg_svc = qsearchs( 'pkg_svc', { 'pkgpart' => $dest->pkgpart,
+ 'svcpart' => $_ } );
+ [ $_,
+ $pkg_svc ? $pkg_svc->primary_svc : '',
+ $pkg_svc ? $pkg_svc->quantity : 0,
+ ];
+ }
+
+ grep { $_ != $svcpart }
+ map { $_->svcpart }
+ qsearch('part_svc', { 'svcdb' => $part_svc->svcdb } )
+ ];
+ warn "alternates for svcpart $svcpart: ".
+ join(', ', @{$svcpart2svcparts{$svcpart}}). "\n"
+ if $DEBUG;
+ }
+ }
+
+ foreach my $cust_svc ($self->cust_svc) {
+ if($target{$cust_svc->svcpart} > 0) {
+ $target{$cust_svc->svcpart}--;
+ my $new = new FS::cust_svc { $cust_svc->hash };
+ $new->pkgnum($dest_pkgnum);
+ my $error = $new->replace($cust_svc);
+ return $error if $error;
+ } elsif ( exists $opt{'change_svcpart'} && $opt{'change_svcpart'} ) {
+ if ( $DEBUG ) {
+ warn "looking for alternates for svcpart ". $cust_svc->svcpart. "\n";
+ warn "alternates to consider: ".
+ join(', ', @{$svcpart2svcparts{$cust_svc->svcpart}}). "\n";
+ }
+ my @alternate = grep {
+ warn "considering alternate svcpart $_: ".
+ "$target{$_} available in new package\n"
+ if $DEBUG;
+ $target{$_} > 0;
+ } @{$svcpart2svcparts{$cust_svc->svcpart}};
+ if ( @alternate ) {
+ warn "alternate(s) found\n" if $DEBUG;
+ my $change_svcpart = $alternate[0];
+ $target{$change_svcpart}--;
+ my $new = new FS::cust_svc { $cust_svc->hash };
+ $new->svcpart($change_svcpart);
+ $new->pkgnum($dest_pkgnum);
+ my $error = $new->replace($cust_svc);
+ return $error if $error;
+ } else {
+ $remaining++;
+ }
+ } else {
+ $remaining++
+ }
+ }
+ return $remaining;
+}
+
+=item reexport
+
+This method is deprecated. See the I<depend_jobnum> option to the insert and
+order_pkgs methods in FS::cust_main for a better way to defer provisioning.
+
+=cut
+
+sub reexport {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $cust_svc ( $self->cust_svc ) {
+ #false laziness w/svc_Common::insert
+ my $svc_x = $cust_svc->svc_x;
+ foreach my $part_export ( $cust_svc->part_svc->part_export ) {
+ my $error = $part_export->export_insert($svc_x);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item recurring_sql
+
+Returns an SQL expression identifying recurring packages.
+
+=cut
+
+sub recurring_sql { "
+ '0' != ( select freq from part_pkg
+ where cust_pkg.pkgpart = part_pkg.pkgpart )
+"; }
+
+=item onetime_sql
+
+Returns an SQL expression identifying one-time packages.
+
+=cut
+
+sub onetime_sql { "
+ '0' = ( select freq from part_pkg
+ where cust_pkg.pkgpart = part_pkg.pkgpart )
+"; }
+
+=item active_sql
+
+Returns an SQL expression identifying active packages.
+
+=cut
+
+sub active_sql { "
+ ". $_[0]->recurring_sql(). "
+ AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
+"; }
+
+=item inactive_sql
+
+Returns an SQL expression identifying inactive packages (one-time packages
+that are otherwise unsuspended/uncancelled).
+
+=cut
+
+sub inactive_sql { "
+ ". $_[0]->onetime_sql(). "
+ AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
+"; }
+
+=item susp_sql
+=item suspended_sql
+
+Returns an SQL expression identifying suspended packages.
+
+=cut
+
+sub suspended_sql { susp_sql(@_); }
+sub susp_sql {
+ #$_[0]->recurring_sql(). ' AND '.
+ "
+ ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ AND cust_pkg.susp IS NOT NULL AND cust_pkg.susp != 0
+ ";
+}
+
+=item cancel_sql
+=item cancelled_sql
+
+Returns an SQL exprression identifying cancelled packages.
+
+=cut
+
+sub cancelled_sql { cancel_sql(@_); }
+sub cancel_sql {
+ #$_[0]->recurring_sql(). ' AND '.
+ "cust_pkg.cancel IS NOT NULL AND cust_pkg.cancel != 0";
+}
+
+=item search_sql HASHREF
+
+(Class method)
+
+Returns a qsearch hash expression to search for parameters specified in HASHREF.
+Valid parameters are
+
+=over 4
+
+=item agentnum
+
+=item magic
+
+active, inactive, suspended, cancel (or cancelled)
+
+=item status
+
+active, inactive, suspended, one-time charge, inactive, cancel (or cancelled)
+
+=item classnum
+
+=item pkgpart
+
+list specified how?
+
+=item setup
+
+arrayref of beginning and ending epoch date
+
+=item last_bill
+
+arrayref of beginning and ending epoch date
+
+=item bill
+
+arrayref of beginning and ending epoch date
+
+=item adjourn
+
+arrayref of beginning and ending epoch date
+
+=item susp
+
+arrayref of beginning and ending epoch date
+
+=item expire
+
+arrayref of beginning and ending epoch date
+
+=item cancel
+
+arrayref of beginning and ending epoch date
+
+=item query
+
+pkgnum or APKG_pkgnum
+
+=item cust_fields
+
+a value suited to passing to FS::UI::Web::cust_header
+
+=item CurrentUser
+
+specifies the user for agent virtualization
+
+=back
+
+=cut
+
+sub search_sql {
+ my ($class, $params) = @_;
+ my @where = ();
+
+ ##
+ # parse agent
+ ##
+
+ if ( $params->{'agentnum'} =~ /^(\d+)$/ and $1 ) {
+ push @where,
+ "cust_main.agentnum = $1";
+ }
+
+ ##
+ # parse status
+ ##
+
+ if ( $params->{'magic'} eq 'active'
+ || $params->{'status'} eq 'active' ) {
+
+ push @where, FS::cust_pkg->active_sql();
+
+ } elsif ( $params->{'magic'} eq 'inactive'
+ || $params->{'status'} eq 'inactive' ) {
+
+ push @where, FS::cust_pkg->inactive_sql();
+
+ } elsif ( $params->{'magic'} eq 'suspended'
+ || $params->{'status'} eq 'suspended' ) {
+
+ push @where, FS::cust_pkg->suspended_sql();
+
+ } elsif ( $params->{'magic'} =~ /^cancell?ed$/
+ || $params->{'status'} =~ /^cancell?ed$/ ) {
+
+ push @where, FS::cust_pkg->cancelled_sql();
+
+ } elsif ( $params->{'status'} =~ /^(one-time charge|inactive)$/ ) {
+
+ push @where, FS::cust_pkg->inactive_sql();
+
+ }
+
+ ###
+ # parse package class
+ ###
+
+ #false lazinessish w/graph/cust_bill_pkg.cgi
+ my $classnum = 0;
+ my @pkg_class = ();
+ if ( exists($params->{'classnum'})
+ && $params->{'classnum'} =~ /^(\d*)$/
+ )
+ {
+ $classnum = $1;
+ if ( $classnum ) { #a specific class
+ push @where, "classnum = $classnum";
+
+ #@pkg_class = ( qsearchs('pkg_class', { 'classnum' => $classnum } ) );
+ #die "classnum $classnum not found!" unless $pkg_class[0];
+ #$title .= $pkg_class[0]->classname.' ';
+
+ } elsif ( $classnum eq '' ) { #the empty class
+
+ push @where, "classnum IS NULL";
+ #$title .= 'Empty class ';
+ #@pkg_class = ( '(empty class)' );
+ } elsif ( $classnum eq '0' ) {
+ #@pkg_class = qsearch('pkg_class', {} ); # { 'disabled' => '' } );
+ #push @pkg_class, '(empty class)';
+ } else {
+ die "illegal classnum";
+ }
+ }
+ #eslaf
+
+ ###
+ # parse part_pkg
+ ###
+
+ my $pkgpart = join (' OR pkgpart=',
+ grep {$_} map { /^(\d+)$/; } ($params->{'pkgpart'}));
+ push @where, '(pkgpart=' . $pkgpart . ')' if $pkgpart;
+
+ ###
+ # parse dates
+ ###
+
+ my $orderby = '';
+
+ #false laziness w/report_cust_pkg.html
+ my %disable = (
+ 'all' => {},
+ 'one-time charge' => { 'last_bill'=>1, 'bill'=>1, 'adjourn'=>1, 'susp'=>1, 'expire'=>1, 'cancel'=>1, },
+ 'active' => { 'susp'=>1, 'cancel'=>1 },
+ 'suspended' => { 'cancel' => 1 },
+ 'cancelled' => {},
+ '' => {},
+ );
+
+ foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
+
+ next unless exists($params->{$field});
+
+ my($beginning, $ending) = @{$params->{$field}};
+
+ next if $beginning == 0 && $ending == 4294967295;
+
+ push @where,
+ "cust_pkg.$field IS NOT NULL",
+ "cust_pkg.$field >= $beginning",
+ "cust_pkg.$field <= $ending";
+
+ $orderby ||= "ORDER BY cust_pkg.$field";
+
+ }
+
+ $orderby ||= 'ORDER BY bill';
+
+ ###
+ # parse magic, legacy, etc.
+ ###
+
+ if ( $params->{'magic'} &&
+ $params->{'magic'} =~ /^(active|inactive|suspended|cancell?ed)$/
+ ) {
+
+ $orderby = 'ORDER BY pkgnum';
+
+ if ( $params->{'pkgpart'} =~ /^(\d+)$/ ) {
+ push @where, "pkgpart = $1";
+ }
+
+ } elsif ( $params->{'query'} eq 'pkgnum' ) {
+
+ $orderby = 'ORDER BY pkgnum';
+
+ } elsif ( $params->{'query'} eq 'APKG_pkgnum' ) {
+
+ $orderby = 'ORDER BY pkgnum';
+
+ push @where, '0 < (
+ SELECT count(*) FROM pkg_svc
+ WHERE pkg_svc.pkgpart = cust_pkg.pkgpart
+ AND pkg_svc.quantity > ( SELECT count(*) FROM cust_svc
+ WHERE cust_svc.pkgnum = cust_pkg.pkgnum
+ AND cust_svc.svcpart = pkg_svc.svcpart
+ )
+ )';
+
+ }
+
+ ##
+ # setup queries, links, subs, etc. for the search
+ ##
+
+ # here is the agent virtualization
+ if ($params->{CurrentUser}) {
+ my $access_user =
+ qsearchs('access_user', { username => $params->{CurrentUser} });
+
+ if ($access_user) {
+ push @where, $access_user->agentnums_sql('table'=>'cust_main');
+ }else{
+ push @where, "1=0";
+ }
+ }else{
+ push @where, $FS::CurrentUser::CurrentUser->agentnums_sql('table'=>'cust_main');
+ }
+
+ my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
+
+ my $addl_from = 'LEFT JOIN cust_main USING ( custnum ) '.
+ 'LEFT JOIN part_pkg USING ( pkgpart ) '.
+ 'LEFT JOIN pkg_class USING ( classnum ) ';
+
+ my $count_query = "SELECT COUNT(*) FROM cust_pkg $addl_from $extra_sql";
+
+ my $sql_query = {
+ 'table' => 'cust_pkg',
+ 'hashref' => {},
+ 'select' => join(', ',
+ 'cust_pkg.*',
+ ( map "part_pkg.$_", qw( pkg freq ) ),
+ 'pkg_class.classname',
+ 'cust_main.custnum as cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(
+ $params->{'cust_fields'}
+ ),
+ ),
+ 'extra_sql' => "$extra_sql $orderby",
+ 'addl_from' => $addl_from,
+ 'count_query' => $count_query,
+ };
+
+}
+
+=item location_sql
+
+Returns a list: the first item is an SQL fragment identifying matching
+packages/customers via location (taking into account shipping and package
+address taxation, if enabled), and subsequent items are the parameters to
+substitute for the placeholders in that fragment.
+
+=cut
+
+sub location_sql {
+ my($class, %opt) = @_;
+ my $ornull = $opt{'ornull'};
+
+ my $conf = new FS::Conf;
+
+ # '?' placeholders in _location_sql_where
+ my @bill_param;
+ if ( $ornull ) {
+ @bill_param = qw( county county state state state country );
+ } else {
+ @bill_param = qw( county state state country );
+ }
+ unshift @bill_param, 'county'; # unless $nec;
+
+ my $main_where;
+ my @main_param;
+ if ( $conf->exists('tax-ship_address') ) {
+
+ $main_where = "(
+ ( ( ship_last IS NULL OR ship_last = '' )
+ AND ". _location_sql_where('cust_main', '', $ornull ). "
+ )
+ OR ( ship_last IS NOT NULL AND ship_last != ''
+ AND ". _location_sql_where('cust_main', 'ship_', $ornull ). "
+ )
+ )";
+ # AND payby != 'COMP'
+
+ @main_param = ( @bill_param, @bill_param );
+
+ } else {
+
+ $main_where = _location_sql_where('cust_main'); # AND payby != 'COMP'
+ @main_param = @bill_param;
+
+ }
+
+ my $where;
+ my @param;
+ if ( $conf->exists('tax-pkg_address') ) {
+
+ my $loc_where = _location_sql_where( 'cust_location', '', $ornull );
+
+ $where = " (
+ ( cust_pkg.locationnum IS NULL AND $main_where )
+ OR ( cust_pkg.locationnum IS NOT NULL AND $loc_where )
+ )
+ ";
+ @param = ( @main_param, @bill_param );
+
+ } else {
+
+ $where = $main_where;
+ @param = @main_param;
+
+ }
+
+ ( $where, @param );
+
+}
+
+#subroutine, helper for location_sql
+sub _location_sql_where {
+ my $table = shift;
+ my $prefix = @_ ? shift : '';
+ my $ornull = @_ ? shift : '';
+
+# $ornull = $ornull ? " OR ( ? IS NULL AND $table.${prefix}county IS NULL ) " : '';
+
+ $ornull = $ornull ? ' OR ? IS NULL ' : '';
+
+ my $or_empty_county = " OR ( ? = '' AND $table.${prefix}county IS NULL ) ";
+ my $or_empty_state = " OR ( ? = '' AND $table.${prefix}state IS NULL ) ";
+
+ "
+ ( $table.${prefix}county = ? $or_empty_county $ornull )
+ AND ( $table.${prefix}state = ? $or_empty_state $ornull )
+ AND $table.${prefix}country = ?
+ ";
+}
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item order CUSTNUM, PKGPARTS_ARYREF, [ REMOVE_PKGNUMS_ARYREF [ RETURN_CUST_PKG_ARRAYREF [ REFNUM ] ] ]
+
+CUSTNUM is a customer (see L<FS::cust_main>)
+
+PKGPARTS is a list of pkgparts specifying the the billing item definitions (see
+L<FS::part_pkg>) to order for this customer. Duplicates are of course
+permitted.
+
+REMOVE_PKGNUMS is an optional list of pkgnums specifying the billing items to
+remove for this customer. The services (see L<FS::cust_svc>) are moved to the
+new billing items. An error is returned if this is not possible (see
+L<FS::pkg_svc>). An empty arrayref is equivalent to not specifying this
+parameter.
+
+RETURN_CUST_PKG_ARRAYREF, if specified, will be filled in with the
+newly-created cust_pkg objects.
+
+REFNUM, if specified, will specify the FS::pkg_referral record to be created
+and inserted. Multiple FS::pkg_referral records can be created by
+setting I<refnum> to an array reference of refnums or a hash reference with
+refnums as keys. If no I<refnum> is defined, a default FS::pkg_referral
+record will be created corresponding to cust_main.refnum.
+
+=cut
+
+sub order {
+ my ($custnum, $pkgparts, $remove_pkgnum, $return_cust_pkg, $refnum) = @_;
+
+ my $conf = new FS::Conf;
+
+ # Transactionize this whole mess
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error;
+# my $cust_main = qsearchs('cust_main', { custnum => $custnum });
+# return "Customer not found: $custnum" unless $cust_main;
+
+ my @old_cust_pkg = map { qsearchs('cust_pkg', { pkgnum => $_ }) }
+ @$remove_pkgnum;
+
+ my $change = scalar(@old_cust_pkg) != 0;
+
+ my %hash = ();
+ if ( scalar(@old_cust_pkg) == 1 && scalar(@$pkgparts) == 1 ) {
+
+ my $err_or_cust_pkg =
+ $old_cust_pkg[0]->change( 'pkgpart' => $pkgparts->[0],
+ 'refnum' => $refnum,
+ );
+
+ unless (ref($err_or_cust_pkg)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_cust_pkg;
+ }
+
+ push @$return_cust_pkg, $err_or_cust_pkg;
+ return '';
+
+ }
+
+ # Create the new packages.
+ foreach my $pkgpart (@$pkgparts) {
+ my $cust_pkg = new FS::cust_pkg { custnum => $custnum,
+ pkgpart => $pkgpart,
+ refnum => $refnum,
+ %hash,
+ };
+ $error = $cust_pkg->insert( 'change' => $change );
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ push @$return_cust_pkg, $cust_pkg;
+ }
+ # $return_cust_pkg now contains refs to all of the newly
+ # created packages.
+
+ # Transfer services and cancel old packages.
+ foreach my $old_pkg (@old_cust_pkg) {
+
+ foreach my $new_pkg (@$return_cust_pkg) {
+ $error = $old_pkg->transfer($new_pkg);
+ if ($error and $error == 0) {
+ # $old_pkg->transfer failed.
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ if ( $error > 0 && $conf->exists('cust_pkg-change_svcpart') ) {
+ warn "trying transfer again with change_svcpart option\n" if $DEBUG;
+ foreach my $new_pkg (@$return_cust_pkg) {
+ $error = $old_pkg->transfer($new_pkg, 'change_svcpart'=>1 );
+ if ($error and $error == 0) {
+ # $old_pkg->transfer failed.
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ if ($error > 0) {
+ # Transfers were successful, but we went through all of the
+ # new packages and still had services left on the old package.
+ # We can't cancel the package under the circumstances, so abort.
+ $dbh->rollback if $oldAutoCommit;
+ return "Unable to transfer all services from package ".$old_pkg->pkgnum;
+ }
+ $error = $old_pkg->cancel( quiet=>1 );
+ if ($error) {
+ $dbh->rollback;
+ return $error;
+ }
+ }
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item bulk_change PKGPARTS_ARYREF, REMOVE_PKGNUMS_ARYREF [ RETURN_CUST_PKG_ARRAYREF ]
+
+A bulk change method to change packages for multiple customers.
+
+PKGPARTS is a list of pkgparts specifying the the billing item definitions (see
+L<FS::part_pkg>) to order for each customer. Duplicates are of course
+permitted.
+
+REMOVE_PKGNUMS is an list of pkgnums specifying the billing items to
+replace. The services (see L<FS::cust_svc>) are moved to the
+new billing items. An error is returned if this is not possible (see
+L<FS::pkg_svc>).
+
+RETURN_CUST_PKG_ARRAYREF, if specified, will be filled in with the
+newly-created cust_pkg objects.
+
+=cut
+
+sub bulk_change {
+ my ($pkgparts, $remove_pkgnum, $return_cust_pkg) = @_;
+
+ # Transactionize this whole mess
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my @errors;
+ my @old_cust_pkg = map { qsearchs('cust_pkg', { pkgnum => $_ }) }
+ @$remove_pkgnum;
+
+ while(scalar(@old_cust_pkg)) {
+ my @return = ();
+ my $custnum = $old_cust_pkg[0]->custnum;
+ my (@remove) = map { $_->pkgnum }
+ grep { $_->custnum == $custnum } @old_cust_pkg;
+ @old_cust_pkg = grep { $_->custnum != $custnum } @old_cust_pkg;
+
+ my $error = order $custnum, $pkgparts, \@remove, \@return;
+
+ push @errors, $error
+ if $error;
+ push @$return_cust_pkg, @return;
+ }
+
+ if (scalar(@errors)) {
+ $dbh->rollback if $oldAutoCommit;
+ return join(' / ', @errors);
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item insert_reason
+
+Associates this package with a (suspension or cancellation) reason (see
+L<FS::cust_pkg_reason>, possibly inserting a new reason on the fly (see
+L<FS::reason>).
+
+Available options are:
+
+=over 4
+
+=item reason
+
+can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason.
+
+=item reason_otaker
+
+the access_user (see L<FS::access_user>) providing the reason
+
+=item date
+
+a unix timestamp
+
+=item action
+
+the action (cancel, susp, adjourn, expire) associated with the reason
+
+=back
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub insert_reason {
+ my ($self, %options) = @_;
+
+ my $otaker = $options{reason_otaker} ||
+ $FS::CurrentUser::CurrentUser->username;
+
+ my $reasonnum;
+ if ( $options{'reason'} =~ /^(\d+)$/ ) {
+
+ $reasonnum = $1;
+
+ } elsif ( ref($options{'reason'}) ) {
+
+ return 'Enter a new reason (or select an existing one)'
+ unless $options{'reason'}->{'reason'} !~ /^\s*$/;
+
+ my $reason = new FS::reason({
+ 'reason_type' => $options{'reason'}->{'typenum'},
+ 'reason' => $options{'reason'}->{'reason'},
+ });
+ my $error = $reason->insert;
+ return $error if $error;
+
+ $reasonnum = $reason->reasonnum;
+
+ } else {
+ return "Unparsable reason: ". $options{'reason'};
+ }
+
+ my $cust_pkg_reason =
+ new FS::cust_pkg_reason({ 'pkgnum' => $self->pkgnum,
+ 'reasonnum' => $reasonnum,
+ 'otaker' => $otaker,
+ 'action' => substr(uc($options{'action'}),0,1),
+ 'date' => $options{'date'}
+ ? $options{'date'}
+ : time,
+ });
+
+ $cust_pkg_reason->insert;
+}
+
+=item set_usage USAGE_VALUE_HASHREF
+
+USAGE_VALUE_HASHREF is a hashref of svc_acct usage columns and the amounts
+to which they should be set (see L<FS::svc_acct>). Currently seconds,
+upbytes, downbytes, and totalbytes are appropriate keys.
+
+All svc_accts which are part of this package have their values reset.
+
+=cut
+
+sub set_usage {
+ my ($self, $valueref) = @_;
+
+ foreach my $cust_svc ($self->cust_svc){
+ my $svc_x = $cust_svc->svc_x;
+ $svc_x->set_usage($valueref)
+ if $svc_x->can("set_usage");
+ }
+}
+
+=item recharge USAGE_VALUE_HASHREF
+
+USAGE_VALUE_HASHREF is a hashref of svc_acct usage columns and the amounts
+to which they should be set (see L<FS::svc_acct>). Currently seconds,
+upbytes, downbytes, and totalbytes are appropriate keys.
+
+All svc_accts which are part of this package have their values incremented.
+
+=cut
+
+sub recharge {
+ my ($self, $valueref) = @_;
+
+ foreach my $cust_svc ($self->cust_svc){
+ my $svc_x = $cust_svc->svc_x;
+ $svc_x->recharge($valueref)
+ if $svc_x->can("recharge");
+ }
+}
+
+=back
+
+=head1 BUGS
+
+sub order is not OO. Perhaps it should be moved to FS::cust_main and made so?
+
+In sub order, the @pkgparts array (passed by reference) is clobbered.
+
+Also in sub order, no money is adjusted. Once FS::part_pkg defines a standard
+method to pass dates to the recur_prog expression, it should do so.
+
+FS::svc_acct, FS::svc_domain, FS::svc_www, FS::svc_ip and FS::svc_forward are
+loaded via 'use' at compile time, rather than via 'require' in sub { setup,
+suspend, unsuspend, cancel } because they use %FS::UID::callback to load
+configuration values. Probably need a subroutine which decides what to do
+based on whether or not we've fetched the user yet, rather than a hash. See
+FS::UID and the TODO.
+
+Now that things are transactional should the check in the insert method be
+moved to check ?
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_main>, L<FS::part_pkg>, L<FS::cust_svc>,
+L<FS::pkg_svc>, schema.html from the base documentation
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pkg_detail.pm b/FS/FS/cust_pkg_detail.pm
new file mode 100644
index 0000000..e2d8987
--- /dev/null
+++ b/FS/FS/cust_pkg_detail.pm
@@ -0,0 +1,140 @@
+package FS::cust_pkg_detail;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record; # qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_pkg_detail - Object methods for cust_pkg_detail records
+
+=head1 SYNOPSIS
+
+ use FS::cust_pkg_detail;
+
+ $record = new FS::cust_pkg_detail \%hash;
+ $record = new FS::cust_pkg_detail { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pkg_detail object represents additional customer package details.
+FS::cust_pkg_detail inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item pkgdetailnum
+
+primary key
+
+=item pkgnum
+
+pkgnum (see L<FS::cust_pkg>)
+
+=item detail
+
+detail
+
+=item detailtype
+
+"I" for Invoice details or "C" for comments
+
+=item weight
+
+Optional display weight
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_pkg_detail'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('pkgdetailnum')
+ || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum')
+ || $self->ut_text('detail')
+ || $self->ut_enum('detailtype', [ 'I', 'C' ] )
+ || $self->ut_numbern('weight')
+ ;
+ return $error if $error;
+
+ $self->weight(0) unless $self->weight;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_pkg>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pkg_option.pm b/FS/FS/cust_pkg_option.pm
new file mode 100644
index 0000000..43a1530
--- /dev/null
+++ b/FS/FS/cust_pkg_option.pm
@@ -0,0 +1,115 @@
+package FS::cust_pkg_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_pkg_option - Object methods for cust_pkg_option records
+
+=head1 SYNOPSIS
+
+ use FS::cust_pkg_option;
+
+ $record = new FS::cust_pkg_option \%hash;
+ $record = new FS::cust_pkg_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pkg_option object represents an option key an value for a
+customer package. FS::cust_pkg_option inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item pkgnum -
+
+=item optionname -
+
+=item optionvalue -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new option. To add the option to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_pkg_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid option. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum')
+ || $self->ut_text('optionname')
+ || $self->ut_textn('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_pkg_reason.pm b/FS/FS/cust_pkg_reason.pm
new file mode 100644
index 0000000..4037513
--- /dev/null
+++ b/FS/FS/cust_pkg_reason.pm
@@ -0,0 +1,330 @@
+package FS::cust_pkg_reason;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_pkg_reason - Object methods for cust_pkg_reason records
+
+=head1 SYNOPSIS
+
+ use FS::cust_pkg_reason;
+
+ $record = new FS::cust_pkg_reason \%hash;
+ $record = new FS::cust_pkg_reason { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_pkg_reason object represents a relationship between a cust_pkg
+and a reason, for example cancellation or suspension reasons.
+FS::cust_pkg_reason inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item num - primary key
+
+=item pkgnum -
+
+=item reasonnum -
+
+=item otaker -
+
+=item date -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new cust_pkg_reason. To add the example to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_pkg_reason'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid cust_pkg_reason. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('num')
+ || $self->ut_number('pkgnum')
+ || $self->ut_number('reasonnum')
+ || $self->ut_enum('action', [ 'A', 'C', 'E', 'S' ])
+ || $self->ut_text('otaker')
+ || $self->ut_numbern('date')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item reason
+
+Returns the reason (see L<FS::reason>) associated with this cust_pkg_reason.
+
+=cut
+
+sub reason {
+ my $self = shift;
+ qsearchs( 'reason', { 'reasonnum' => $self->reasonnum } );
+}
+
+=item reasontext
+
+Returns the text of the reason (see L<FS::reason>) associated with this
+cust_pkg_reason.
+
+=cut
+
+sub reasontext {
+ my $reason = shift->reason;
+ $reason ? $reason->reason : '';
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+use FS::h_cust_pkg;
+use FS::h_cust_pkg_reason;
+
+sub _upgrade_data { # class method
+ my ($class, %opts) = @_;
+
+ my $test_cust_pkg_reason = new FS::cust_pkg_reason;
+ return '' unless $test_cust_pkg_reason->dbdef_table->column('action');
+
+ my $count = 0;
+ my @unmigrated = qsearch('cust_pkg_reason', { 'action' => '' } );
+ foreach ( @unmigrated ) {
+
+ my @history_cust_pkg_reason = qsearch( 'h_cust_pkg_reason', { $_->hash } );
+
+ next unless scalar(@history_cust_pkg_reason) == 1;
+
+ my %action_value = ( op => 'LIKE',
+ value => 'replace_%',
+ );
+ my $hashref = { pkgnum => $_->pkgnum,
+ history_date => $history_cust_pkg_reason[0]->history_date,
+ history_action => { %action_value },
+ };
+
+ my @history = qsearch({ table => 'h_cust_pkg',
+ hashref => $hashref,
+ order_by => 'ORDER BY history_action',
+ });
+
+ my $fuzz = 0;
+ while (scalar(@history) < 2 && $fuzz < 3) {
+ $hashref->{history_date}++;
+ $hashref->{history_action} = { %action_value }; # qsearch distorts this!
+ $fuzz++;
+ push @history, qsearch({ table => 'h_cust_pkg',
+ hashref => $hashref,
+ order_by => 'ORDER BY history_action',
+ });
+ }
+
+ next unless scalar(@history) == 2;
+
+ my @new = grep { $_->history_action eq 'replace_new' } @history;
+ my @old = grep { $_->history_action eq 'replace_old' } @history;
+
+ next if (scalar(@new) == 2 || scalar(@old) == 2);
+
+ if ( !$old[0]->get('cancel') && $new[0]->get('cancel') ) {
+ $_->action('C');
+ }elsif( !$old[0]->susp && $new[0]->susp ){
+ $_->action('S');
+ }elsif( $new[0]->expire &&
+ (!$old[0]->expire || !$old[0]->expire != $new[0]->expire )
+ ){
+ $_->action('E');
+ $_->date($new[0]->expire);
+ }elsif( $new[0]->adjourn &&
+ (!$old[0]->adjourn || $old[0]->adjourn != $new[0]->adjourn )
+ ){
+ $_->action('A');
+ $_->date($new[0]->adjourn);
+ }
+
+ my $error = $_->replace
+ if $_->modified;
+
+ die $error if $error;
+
+ $count++;
+ }
+
+ #remove nullability if scalar(@migrated) - $count == 0 && ->column('action');
+
+ #seek expirations/adjourns without reason
+ foreach my $field qw( expire adjourn cancel susp ) {
+ my $addl_from =
+ "LEFT JOIN h_cust_pkg ON ".
+ "(cust_pkg_reason.pkgnum = h_cust_pkg.pkgnum AND".
+ " cust_pkg_reason.date = h_cust_pkg.$field AND".
+ " history_action = 'replace_new')";
+
+ my $extra_sql = 'AND h_cust_pkg.pkgnum IS NULL';
+
+ my @unmigrated = qsearch({ table => 'cust_pkg_reason',
+ hashref => { action => uc(substr($field,0,1)) },
+ addl_from => $addl_from,
+ select => 'cust_pkg_reason.*',
+ extra_sql => $extra_sql,
+ });
+ foreach ( @unmigrated ) {
+
+ my %action_value = ( op => 'LIKE',
+ value => 'replace_%',
+ );
+ my $hashref = { pkgnum => $_->pkgnum,
+ history_date => $_->date,
+ history_action => { %action_value },
+ };
+
+ my @history = qsearch({ table => 'h_cust_pkg',
+ hashref => $hashref,
+ order_by => 'ORDER BY history_action',
+ });
+
+ my $fuzz = 0;
+ while (scalar(@history) < 2 && $fuzz < 3) {
+ $hashref->{history_date}++;
+ $hashref->{history_action} = { %action_value }; # qsearch distorts this!
+ $fuzz++;
+ push @history, qsearch({ table => 'h_cust_pkg',
+ hashref => $hashref,
+ order_by => 'ORDER BY history_action',
+ });
+ }
+
+ next unless scalar(@history) == 2;
+
+ my @new = grep { $_->history_action eq 'replace_new' } @history;
+ my @old = grep { $_->history_action eq 'replace_old' } @history;
+
+ next if (scalar(@new) == 2 || scalar(@old) == 2);
+
+ $_->date($new[0]->get($field))
+ if ( $new[0]->get($field) &&
+ ( !$old[0]->get($field) ||
+ $old[0]->get($field) != $new[0]->get($field)
+ )
+ );
+
+ my $error = $_->replace
+ if $_->modified;
+
+ die $error if $error;
+ }
+ }
+
+ #seek cancels/suspends without reason, but with expire/adjourn reason
+ foreach my $field qw( cancel susp ) {
+
+ my %precursor_map = ( 'cancel' => 'expire', 'susp' => 'adjourn' );
+ my $precursor = $precursor_map{$field};
+ my $preaction = uc(substr($precursor,0,1));
+ my $action = uc(substr($field,0,1));
+ my $addl_from =
+ "LEFT JOIN cust_pkg_reason ON ".
+ "(cust_pkg.pkgnum = cust_pkg_reason.pkgnum AND".
+ " cust_pkg.$precursor = cust_pkg_reason.date AND".
+ " cust_pkg_reason.action = '$preaction') ".
+ "LEFT JOIN cust_pkg_reason AS target ON ".
+ "(cust_pkg.pkgnum = target.pkgnum AND".
+ " cust_pkg.$field = target.date AND".
+ " target.action = '$action')"
+ ;
+
+ my $extra_sql = "WHERE target.pkgnum IS NULL AND ".
+ "cust_pkg.$field IS NOT NULL AND ".
+ "cust_pkg.$field < cust_pkg.$precursor + 86400 AND ".
+ "cust_pkg_reason.action = '$preaction'";
+
+ my @unmigrated = qsearch({ table => 'cust_pkg',
+ hashref => { },
+ select => 'cust_pkg.*',
+ addl_from => $addl_from,
+ extra_sql => $extra_sql,
+ });
+ foreach ( @unmigrated ) {
+ my $cpr = new FS::cust_pkg_reason { $_->last_cust_pkg_reason($precursor)->hash, 'num' => '' };
+ $cpr->date($_->get($field));
+ $cpr->action($action);
+
+ my $error = $cpr->insert;
+ die $error if $error;
+ }
+ }
+
+ '';
+
+}
+
+=back
+
+=head1 BUGS
+
+Here be termites. Don't use on wooden computers.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_refund.pm b/FS/FS/cust_refund.pm
new file mode 100644
index 0000000..abc131e
--- /dev/null
+++ b/FS/FS/cust_refund.pm
@@ -0,0 +1,354 @@
+package FS::cust_refund;
+
+use strict;
+use vars qw( @ISA @encrypted_fields );
+use Business::CreditCard;
+use FS::UID qw(getotaker);
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::cust_main_Mixin;
+use FS::payinfo_transaction_Mixin;
+use FS::cust_credit;
+use FS::cust_credit_refund;
+use FS::cust_pay_refund;
+use FS::cust_main;
+
+@ISA = qw( FS::payinfo_transaction_Mixin FS::cust_main_Mixin FS::Record );
+
+@encrypted_fields = ('payinfo');
+
+=head1 NAME
+
+FS::cust_refund - Object method for cust_refund objects
+
+=head1 SYNOPSIS
+
+ use FS::cust_refund;
+
+ $record = new FS::cust_refund \%hash;
+ $record = new FS::cust_refund { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_refund represents a refund: the transfer of money to a customer;
+equivalent to a negative payment (see L<FS::cust_pay>). FS::cust_refund
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item refundnum - primary key (assigned automatically for new refunds)
+
+=item custnum - customer (see L<FS::cust_main>)
+
+=item refund - Amount of the refund
+
+=item reason - Reason for the refund
+
+=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item payby - Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
+
+=item payinfo - Payment Information (See L<FS::payinfo_Mixin> for data format)
+
+=item paymask - Masked payinfo (See L<FS::payinfo_Mixin> for how this works)
+
+=item paybatch - text field for tracking card processing
+
+=item otaker - order taker (assigned automatically, see L<FS::UID>)
+
+=item closed - books closed flag, empty or `Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new refund. To add the refund to the database, see L<"insert">.
+
+=cut
+
+sub table { 'cust_refund'; }
+
+=item insert
+
+Adds this refund to the database.
+
+For backwards-compatibility and convenience, if the additional field crednum is
+defined, an FS::cust_credit_refund record for the full amount of the refund
+will be created. Or (this time for convenience and consistancy), if the
+additional field paynum is defined, an FS::cust_pay_refund record for the full
+amount of the refund will be created. In both cases, custnum is optional.
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $self->crednum ) {
+ my $cust_credit = qsearchs('cust_credit', { 'crednum' => $self->crednum } )
+ or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unknown cust_credit.crednum: ". $self->crednum;
+ };
+ $self->custnum($cust_credit->custnum);
+ } elsif ( $self->paynum ) {
+ my $cust_pay = qsearchs('cust_pay', { 'paynum' => $self->paynum } )
+ or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unknown cust_pay.paynum: ". $self->paynum;
+ };
+ $self->custnum($cust_pay->custnum);
+ }
+
+ my $error = $self->check;
+ return $error if $error;
+
+ $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $self->crednum ) {
+ my $cust_credit_refund = new FS::cust_credit_refund {
+ 'crednum' => $self->crednum,
+ 'refundnum' => $self->refundnum,
+ 'amount' => $self->refund,
+ '_date' => $self->_date,
+ };
+ $error = $cust_credit_refund->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ #$self->custnum($cust_credit_refund->cust_credit->custnum);
+ } elsif ( $self->paynum ) {
+ my $cust_pay_refund = new FS::cust_pay_refund {
+ 'paynum' => $self->paynum,
+ 'refundnum' => $self->refundnum,
+ 'amount' => $self->refund,
+ '_date' => $self->_date,
+ };
+ $error = $cust_pay_refund->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Unless the closed flag is set, deletes this refund and all associated
+applications (see L<FS::cust_credit_refund> and L<FS::cust_pay_refund>).
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return "Can't delete closed refund" if $self->closed =~ /^Y/i;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $cust_credit_refund ( $self->cust_credit_refund ) {
+ my $error = $cust_credit_refund->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ foreach my $cust_pay_refund ( $self->cust_pay_refund ) {
+ my $error = $cust_pay_refund->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item replace OLD_RECORD
+
+Modifying a refund? Well, don't say I didn't warn you.
+
+=cut
+
+sub replace {
+ my $self = shift;
+ $self->SUPER::replace(@_);
+}
+
+=item check
+
+Checks all fields to make sure this is a valid refund. If there is an error,
+returns the error, otherwise returns false. Called by the insert method.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->otaker(getotaker) unless ($self->otaker);
+
+ my $error =
+ $self->ut_numbern('refundnum')
+ || $self->ut_numbern('custnum')
+ || $self->ut_money('refund')
+ || $self->ut_alpha('otaker')
+ || $self->ut_text('reason')
+ || $self->ut_numbern('_date')
+ || $self->ut_textn('paybatch')
+ || $self->ut_enum('closed', [ '', 'Y' ])
+ ;
+ return $error if $error;
+
+ return "refund must be > 0 " if $self->refund <= 0;
+
+ $self->_date(time) unless $self->_date;
+
+ return "unknown cust_main.custnum: ". $self->custnum
+ unless $self->crednum
+ || qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+
+ $error = $self->payinfo_check;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item cust_credit_refund
+
+Returns all applications to credits (see L<FS::cust_credit_refund>) for this
+refund.
+
+=cut
+
+sub cust_credit_refund {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_credit_refund', { 'refundnum' => $self->refundnum } )
+ ;
+}
+
+=item cust_pay_refund
+
+Returns all applications to payments (see L<FS::cust_pay_refund>) for this
+refund.
+
+=cut
+
+sub cust_pay_refund {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay_refund', { 'refundnum' => $self->refundnum } )
+ ;
+}
+
+=item unapplied
+
+Returns the amount of this refund that is still unapplied; which is
+amount minus all credit applications (see L<FS::cust_credit_refund>) and
+payment applications (see L<FS::cust_pay_refund>).
+
+=cut
+
+sub unapplied {
+ my $self = shift;
+ my $amount = $self->refund;
+ $amount -= $_->amount foreach ( $self->cust_credit_refund );
+ $amount -= $_->amount foreach ( $self->cust_pay_refund );
+ sprintf("%.2f", $amount );
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item unapplied_sql
+
+Returns an SQL fragment to retreive the unapplied amount.
+
+=cut
+
+sub unapplied_sql {
+ #my $class = shift;
+
+ "refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ";
+
+}
+
+=back
+
+=head1 BUGS
+
+Delete and replace methods.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_credit>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm
new file mode 100644
index 0000000..30b2390
--- /dev/null
+++ b/FS/FS/cust_svc.pm
@@ -0,0 +1,737 @@
+package FS::cust_svc;
+
+use strict;
+use vars qw( @ISA $DEBUG $me $ignore_quantity );
+use Carp;
+#use Scalar::Util qw( blessed );
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs dbh str2time_sql );
+use FS::cust_pkg;
+use FS::part_pkg;
+use FS::part_svc;
+use FS::pkg_svc;
+use FS::domain_record;
+use FS::part_export;
+use FS::cdr;
+
+#most FS::svc_ classes are autoloaded in svc_x emthod
+use FS::svc_acct; #this one is used in the cache stuff
+
+@ISA = qw( FS::cust_main_Mixin FS::option_Common ); #FS::Record );
+
+$DEBUG = 0;
+$me = '[cust_svc]';
+
+$ignore_quantity = 0;
+
+sub _cache {
+ my $self = shift;
+ my ( $hashref, $cache ) = @_;
+ if ( $hashref->{'username'} ) {
+ $self->{'_svc_acct'} = FS::svc_acct->new($hashref, '');
+ }
+ if ( $hashref->{'svc'} ) {
+ $self->{'_svcpart'} = FS::part_svc->new($hashref);
+ }
+}
+
+=head1 NAME
+
+FS::cust_svc - Object method for cust_svc objects
+
+=head1 SYNOPSIS
+
+ use FS::cust_svc;
+
+ $record = new FS::cust_svc \%hash
+ $record = new FS::cust_svc { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ ($label, $value) = $record->label;
+
+=head1 DESCRIPTION
+
+An FS::cust_svc represents a service. FS::cust_svc inherits from FS::Record.
+The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key (assigned automatically for new services)
+
+=item pkgnum - Package (see L<FS::cust_pkg>)
+
+=item svcpart - Service definition (see L<FS::part_svc>)
+
+=item overlimit - date the service exceeded its usage limit
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new service. To add the refund to the database, see L<"insert">.
+Services are normally created by creating FS::svc_ objects (see
+L<FS::svc_acct>, L<FS::svc_domain>, and L<FS::svc_forward>, among others).
+
+=cut
+
+sub table { 'cust_svc'; }
+
+=item insert
+
+Adds this service to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this service from the database. If there is an error, returns the
+error, otherwise returns false. Note that this only removes the cust_svc
+record - you should probably use the B<cancel> method instead.
+
+=item cancel
+
+Cancels the relevant service by calling the B<cancel> method of the associated
+FS::svc_XXX object (i.e. an FS::svc_acct object or FS::svc_domain object),
+deleting the FS::svc_XXX record and then deleting this record.
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub cancel {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $part_svc = $self->part_svc;
+
+ $part_svc->svcdb =~ /^([\w\-]+)$/ or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "Illegal svcdb value in part_svc!";
+ };
+ my $svcdb = $1;
+ require "FS/$svcdb.pm";
+
+ my $svc = $self->svc_x;
+ if ($svc) {
+
+ my $error = $svc->cancel;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error canceling service: $error";
+ }
+ $error = $svc->delete; #this deletes this cust_svc record as well
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error deleting service: $error";
+ }
+
+ } else {
+
+ #huh?
+ warn "WARNING: no svc_ record found for svcnum ". $self->svcnum.
+ "; deleting cust_svc only\n";
+
+ my $error = $self->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error deleting cust_svc: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ''; #no errors
+
+}
+
+=item overlimit [ ACTION ]
+
+Retrieves or sets the overlimit date. If ACTION is absent, return
+the present value of overlimit. If ACTION is present, it can
+have the value 'suspend' or 'unsuspend'. In the case of 'suspend' overlimit
+is set to the current time if it is not already set. The 'unsuspend' value
+causes the time to be cleared.
+
+If there is an error on setting, returns the error, otherwise returns false.
+
+=cut
+
+sub overlimit {
+ my $self = shift;
+ my $action = shift or return $self->getfield('overlimit');
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $action eq 'suspend' ) {
+ $self->setfield('overlimit', time) unless $self->getfield('overlimit');
+ }elsif ( $action eq 'unsuspend' ) {
+ $self->setfield('overlimit', '');
+ }else{
+ die "unexpected action value: $action";
+ }
+
+ local $ignore_quantity = 1;
+ my $error = $self->replace;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error setting overlimit: $error";
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ''; #no errors
+
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+# my $new = shift;
+#
+# my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+# ? shift
+# : $new->replace_old;
+ my ( $new, $old ) = ( shift, shift );
+ $old = $new->replace_old unless defined($old);
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $new->svcpart != $old->svcpart ) {
+ my $svc_x = $new->svc_x;
+ my $new_svc_x = ref($svc_x)->new({$svc_x->hash, svcpart=>$new->svcpart });
+ local($FS::Record::nowarn_identical) = 1;
+ my $error = $new_svc_x->replace($svc_x);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error if $error;
+ }
+ }
+
+ #my $error = $new->SUPER::replace($old, @_);
+ my $error = $new->SUPER::replace($old);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error if $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid service. If there is an error,
+returns the error, otherwise returns false. Called by the insert and
+replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ || $self->ut_numbern('pkgnum')
+ || $self->ut_number('svcpart')
+ || $self->ut_numbern('overlimit')
+ ;
+ return $error if $error;
+
+ my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
+ return "Unknown svcpart" unless $part_svc;
+
+ if ( $self->pkgnum ) {
+ my $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
+ return "Unknown pkgnum" unless $cust_pkg;
+ my $pkg_svc = qsearchs( 'pkg_svc', {
+ 'pkgpart' => $cust_pkg->pkgpart,
+ 'svcpart' => $self->svcpart,
+ });
+ # or new FS::pkg_svc ( { 'pkgpart' => $cust_pkg->pkgpart,
+ # 'svcpart' => $self->svcpart,
+ # 'quantity' => 0 } );
+ my $quantity = $pkg_svc ? $pkg_svc->quantity : 0;
+
+ my @cust_svc = qsearch('cust_svc', {
+ 'pkgnum' => $self->pkgnum,
+ 'svcpart' => $self->svcpart,
+ });
+ return "Already ". scalar(@cust_svc). " ". $part_svc->svc.
+ " services for pkgnum ". $self->pkgnum
+ if scalar(@cust_svc) >= $quantity && !$ignore_quantity;
+ }
+
+ $self->SUPER::check;
+}
+
+=item part_svc
+
+Returns the definition for this service, as a FS::part_svc object (see
+L<FS::part_svc>).
+
+=cut
+
+sub part_svc {
+ my $self = shift;
+ $self->{'_svcpart'}
+ ? $self->{'_svcpart'}
+ : qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
+}
+
+=item cust_pkg
+
+Returns the package this service belongs to, as a FS::cust_pkg object (see
+L<FS::cust_pkg>).
+
+=cut
+
+sub cust_pkg {
+ my $self = shift;
+ qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
+}
+
+=item pkg_svc
+
+Returns the pkg_svc record for for this service, if applicable.
+
+=cut
+
+sub pkg_svc {
+ my $self = shift;
+ my $cust_pkg = $self->cust_pkg;
+ return undef unless $cust_pkg;
+
+ qsearchs( 'pkg_svc', { 'svcpart' => $self->svcpart,
+ 'pkgpart' => $cust_pkg->pkgpart,
+ }
+ );
+}
+
+=item date_inserted
+
+Returns the date this service was inserted.
+
+=cut
+
+sub date_inserted {
+ my $self = shift;
+ $self->h_date('insert');
+}
+
+=item label
+
+Returns a list consisting of:
+- The name of this service (from part_svc)
+- A meaningful identifier (username, domain, or mail alias)
+- The table name (i.e. svc_domain) for this service
+- svcnum
+
+Usage example:
+
+ my($label, $value, $svcdb) = $cust_svc->label;
+
+=cut
+
+sub label {
+ my $self = shift;
+ carp "FS::cust_svc::label called on $self" if $DEBUG;
+ my $svc_x = $self->svc_x
+ or return "can't find ". $self->part_svc->svcdb. '.svcnum '. $self->svcnum;
+
+ $self->_svc_label($svc_x);
+}
+
+sub _svc_label {
+ my( $self, $svc_x ) = ( shift, shift );
+
+ (
+ $self->part_svc->svc,
+ $svc_x->label(@_),
+ $self->part_svc->svcdb,
+ $self->svcnum
+ );
+
+}
+
+=item export_links
+
+Returns a list of html elements associated with this services exports.
+
+=cut
+
+sub export_links {
+ my $self = shift;
+ my $svc_x = $self->svc_x
+ or return "can't find ". $self->part_svc->svcdb. '.svcnum '. $self->svcnum;
+
+ $svc_x->export_links;
+}
+
+=item svc_x
+
+Returns the FS::svc_XXX object for this service (i.e. an FS::svc_acct object or
+FS::svc_domain object, etc.)
+
+=cut
+
+sub svc_x {
+ my $self = shift;
+ my $svcdb = $self->part_svc->svcdb;
+ if ( $svcdb eq 'svc_acct' && $self->{'_svc_acct'} ) {
+ $self->{'_svc_acct'};
+ } else {
+ require "FS/$svcdb.pm";
+ warn "$me svc_x: part_svc.svcpart ". $self->part_svc->svcpart.
+ ", so searching for $svcdb.svcnum ". $self->svcnum. "\n"
+ if $DEBUG;
+ qsearchs( $svcdb, { 'svcnum' => $self->svcnum } );
+ }
+}
+
+=item seconds_since TIMESTAMP
+
+See L<FS::svc_acct/seconds_since>. Equivalent to
+$cust_svc->svc_x->seconds_since, but more efficient. Meaningless for records
+where B<svcdb> is not "svc_acct".
+
+=cut
+
+#note: implementation here, POD in FS::svc_acct
+sub seconds_since {
+ my($self, $since) = @_;
+ my $dbh = dbh;
+ my $sth = $dbh->prepare(' SELECT SUM(logout-login) FROM session
+ WHERE svcnum = ?
+ AND login >= ?
+ AND logout IS NOT NULL'
+ ) or die $dbh->errstr;
+ $sth->execute($self->svcnum, $since) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END
+
+See L<FS::svc_acct/seconds_since_sqlradacct>. Equivalent to
+$cust_svc->svc_x->seconds_since_sqlradacct, but more efficient. Meaningless
+for records where B<svcdb> is not "svc_acct".
+
+=cut
+
+#note: implementation here, POD in FS::svc_acct
+sub seconds_since_sqlradacct {
+ my($self, $start, $end) = @_;
+
+ my $mes = "$me seconds_since_sqlradacct:";
+
+ my $svc_x = $self->svc_x;
+
+ my @part_export = $self->part_svc->part_export_usage;
+ die "no accounting-capable exports are enabled for ". $self->part_svc->svc.
+ " service definition"
+ unless @part_export;
+ #or return undef;
+
+ my $seconds = 0;
+ foreach my $part_export ( @part_export ) {
+
+ next if $part_export->option('ignore_accounting');
+
+ warn "$mes connecting to sqlradius database\n"
+ if $DEBUG;
+
+ my $dbh = DBI->connect( map { $part_export->option($_) }
+ qw(datasrc username password) )
+ or die "can't connect to sqlradius database: ". $DBI::errstr;
+
+ warn "$mes connected to sqlradius database\n"
+ if $DEBUG;
+
+ #select a unix time conversion function based on database type
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+
+ my $username = $part_export->export_username($svc_x);
+
+ my $query;
+
+ warn "$mes finding closed sessions completely within the given range\n"
+ if $DEBUG;
+
+ my $sth = $dbh->prepare("SELECT SUM(acctsessiontime)
+ FROM radacct
+ WHERE UserName = ?
+ AND $str2time AcctStartTime) >= ?
+ AND $str2time AcctStopTime ) < ?
+ AND $str2time AcctStopTime ) > 0
+ AND AcctStopTime IS NOT NULL"
+ ) or die $dbh->errstr;
+ $sth->execute($username, $start, $end) or die $sth->errstr;
+ my $regular = $sth->fetchrow_arrayref->[0];
+
+ warn "$mes finding open sessions which start in the range\n"
+ if $DEBUG;
+
+ # count session start->range end
+ $query = "SELECT SUM( ? - $str2time AcctStartTime ) )
+ FROM radacct
+ WHERE UserName = ?
+ AND $str2time AcctStartTime ) >= ?
+ AND $str2time AcctStartTime ) < ?
+ AND ( ? - $str2time AcctStartTime ) ) < 86400
+ AND ( $str2time AcctStopTime ) = 0
+ OR AcctStopTime IS NULL )";
+ $sth = $dbh->prepare($query) or die $dbh->errstr;
+ $sth->execute($end, $username, $start, $end, $end)
+ or die $sth->errstr. " executing query $query";
+ my $start_during = $sth->fetchrow_arrayref->[0];
+
+ warn "$mes finding closed sessions which start before the range but stop during\n"
+ if $DEBUG;
+
+ #count range start->session end
+ $sth = $dbh->prepare("SELECT SUM( $str2time AcctStopTime ) - ? )
+ FROM radacct
+ WHERE UserName = ?
+ AND $str2time AcctStartTime ) < ?
+ AND $str2time AcctStopTime ) >= ?
+ AND $str2time AcctStopTime ) < ?
+ AND $str2time AcctStopTime ) > 0
+ AND AcctStopTime IS NOT NULL"
+ ) or die $dbh->errstr;
+ $sth->execute($start, $username, $start, $start, $end ) or die $sth->errstr;
+ my $end_during = $sth->fetchrow_arrayref->[0];
+
+ warn "$mes finding closed sessions which start before the range but stop after\n"
+ if $DEBUG;
+
+ # count range start->range end
+ # don't count open sessions anymore (probably missing stop record)
+ $sth = $dbh->prepare("SELECT COUNT(*)
+ FROM radacct
+ WHERE UserName = ?
+ AND $str2time AcctStartTime ) < ?
+ AND ( $str2time AcctStopTime ) >= ?
+ )"
+ # OR AcctStopTime = 0
+ # OR AcctStopTime IS NULL )"
+ ) or die $dbh->errstr;
+ $sth->execute($username, $start, $end ) or die $sth->errstr;
+ my $entire_range = ($end-$start) * $sth->fetchrow_arrayref->[0];
+
+ $seconds += $regular + $end_during + $start_during + $entire_range;
+
+ warn "$mes done finding sessions\n"
+ if $DEBUG;
+
+ }
+
+ $seconds;
+
+}
+
+=item attribute_since_sqlradacct TIMESTAMP_START TIMESTAMP_END ATTRIBUTE
+
+See L<FS::svc_acct/attribute_since_sqlradacct>. Equivalent to
+$cust_svc->svc_x->attribute_since_sqlradacct, but more efficient. Meaningless
+for records where B<svcdb> is not "svc_acct".
+
+=cut
+
+#note: implementation here, POD in FS::svc_acct
+#(false laziness w/seconds_since_sqlradacct above)
+sub attribute_since_sqlradacct {
+ my($self, $start, $end, $attrib) = @_;
+
+ my $mes = "$me attribute_since_sqlradacct:";
+
+ my $svc_x = $self->svc_x;
+
+ my @part_export = $self->part_svc->part_export_usage;
+ die "no accounting-capable exports are enabled for ". $self->part_svc->svc.
+ " service definition"
+ unless @part_export;
+ #or return undef;
+
+ my $sum = 0;
+
+ foreach my $part_export ( @part_export ) {
+
+ next if $part_export->option('ignore_accounting');
+
+ warn "$mes connecting to sqlradius database\n"
+ if $DEBUG;
+
+ my $dbh = DBI->connect( map { $part_export->option($_) }
+ qw(datasrc username password) )
+ or die "can't connect to sqlradius database: ". $DBI::errstr;
+
+ warn "$mes connected to sqlradius database\n"
+ if $DEBUG;
+
+ #select a unix time conversion function based on database type
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+
+ my $username = $part_export->export_username($svc_x);
+
+ warn "$mes SUMing $attrib sessions\n"
+ if $DEBUG;
+
+ my $sth = $dbh->prepare("SELECT SUM($attrib)
+ FROM radacct
+ WHERE UserName = ?
+ AND $str2time AcctStopTime ) >= ?
+ AND $str2time AcctStopTime ) < ?
+ AND AcctStopTime IS NOT NULL"
+ ) or die $dbh->errstr;
+ $sth->execute($username, $start, $end) or die $sth->errstr;
+
+ $sum += $sth->fetchrow_arrayref->[0];
+
+ warn "$mes done SUMing sessions\n"
+ if $DEBUG;
+
+ }
+
+ $sum;
+
+}
+
+=item get_session_history TIMESTAMP_START TIMESTAMP_END
+
+See L<FS::svc_acct/get_session_history>. Equivalent to
+$cust_svc->svc_x->get_session_history, but more efficient. Meaningless for
+records where B<svcdb> is not "svc_acct".
+
+=cut
+
+sub get_session_history {
+ my($self, $start, $end, $attrib) = @_;
+
+ #$attrib ???
+
+ my @part_export = $self->part_svc->part_export_usage;
+ die "no accounting-capable exports are enabled for ". $self->part_svc->svc.
+ " service definition"
+ unless @part_export;
+ #or return undef;
+
+ my @sessions = ();
+
+ foreach my $part_export ( @part_export ) {
+ push @sessions,
+ @{ $part_export->usage_sessions( $start, $end, $self->svc_x ) };
+ }
+
+ @sessions;
+
+}
+
+=item get_cdrs_for_update
+
+Returns (and SELECTs "FOR UPDATE") all unprocessed (freesidestatus NULL) CDR
+objects (see L<FS::cdr>) associated with this service.
+
+CDRs are associated with svc_phone services via svc_phone.phonenum
+
+=cut
+
+sub get_cdrs_for_update {
+ my($self, %options) = @_;
+
+ my @fields = ( 'charged_party' );
+ push @fields, 'src' unless $options{'disable_src'};
+
+ #CDRs are now associated with svc_phone services via svc_phone.phonenum
+ #return () unless $self->svc_x->isa('FS::svc_phone');
+ return () unless $self->part_svc->svcdb eq 'svc_phone';
+ my $number = $self->svc_x->phonenum;
+
+ my $prefix = $options{'default_prefix'};
+
+ my @where = map " $_ = '$number' ", @fields;
+ push @where, map " $_ = '$prefix$number' ", @fields
+ if length($prefix);
+ if ( $prefix =~ /^\+(\d+)$/ ) {
+ push @where, map " $_ = '$1$number' ", @fields
+ }
+
+ my $extra_sql = ' AND ( '. join(' OR ', @where ). ' ) ';
+
+ my @cdrs =
+ qsearch( {
+ 'table' => 'cdr',
+ 'hashref' => { 'freesidestatus' => '', },
+ 'extra_sql' => "$extra_sql FOR UPDATE",
+ } );
+
+ @cdrs;
+}
+
+=back
+
+=head1 BUGS
+
+Behaviour of changing the svcpart of cust_svc records is undefined and should
+possibly be prohibited, and pkg_svc records are not checked.
+
+pkg_svc records are not checked in general (here).
+
+Deleting this record doesn't check or delete the svc_* record associated
+with this record.
+
+In seconds_since_sqlradacct, specifying a DATASRC/USERNAME/PASSWORD instead of
+a DBI database handle is not yet implemented.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_pkg>, L<FS::part_svc>, L<FS::pkg_svc>,
+schema.html from the base documentation
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_svc_option.pm b/FS/FS/cust_svc_option.pm
new file mode 100644
index 0000000..0a242d5
--- /dev/null
+++ b/FS/FS/cust_svc_option.pm
@@ -0,0 +1,136 @@
+package FS::cust_svc_option;
+
+use strict;
+use vars qw( @ISA );
+#use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_svc_option - Object methods for cust_svc_option records
+
+=head1 SYNOPSIS
+
+ use FS::cust_svc_option;
+
+ $record = new FS::cust_svc_option \%hash;
+ $record = new FS::cust_svc_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_svc_option object represents an customer service option.
+ FS::cust_svc_option inherits from FS::Record. The following fields are
+ currently supported:
+
+=over 4
+
+=item optionnum
+
+primary key
+
+=item svcnum
+
+svcnum (see L<FS::cust_svc>)
+
+=item optionname
+
+Option Name
+
+=item optionvalue
+
+Option Value
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new option. To add the option to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_svc_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid option. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('svcnum', 'cust_svc', 'svcnum')
+ || $self->ut_alpha('optionname')
+ || $self->ut_anything('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_tax_exempt.pm b/FS/FS/cust_tax_exempt.pm
new file mode 100644
index 0000000..045421c
--- /dev/null
+++ b/FS/FS/cust_tax_exempt.pm
@@ -0,0 +1,152 @@
+package FS::cust_tax_exempt;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_main_Mixin;
+use FS::cust_main;
+use FS::cust_main_county;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+=head1 NAME
+
+FS::cust_tax_exempt - Object methods for cust_tax_exempt records
+
+=head1 SYNOPSIS
+
+ use FS::cust_tax_exempt;
+
+ $record = new FS::cust_tax_exempt \%hash;
+ $record = new FS::cust_tax_exempt { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_tax_exempt object represents a record of an old-style customer tax
+exemption. Currently this is only used for "texas tax". FS::cust_tax_exempt
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item exemptnum - primary key
+
+=item custnum - customer (see L<FS::cust_main>)
+
+=item taxnum - tax rate (see L<FS::cust_main_county>)
+
+=item year
+
+=item month
+
+=item amount
+
+=back
+
+=head1 NOTE
+
+Old-style customer tax exemptions are only useful for legacy migrations - if
+you are looking for current customer tax exemption data see
+L<FS::cust_tax_exempt_pkg>.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new exemption record. To add the example to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_tax_exempt'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('exemptnum')
+ || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
+ || $self->ut_foreign_key('taxnum', 'cust_main_county', 'taxnum')
+ || $self->ut_number('year') #check better
+ || $self->ut_number('month') #check better
+ || $self->ut_money('amount')
+ || $self->SUPER::check
+ ;
+}
+
+=item cust_main_county
+
+Returns the FS::cust_main_county object associated with this tax exemption.
+
+=cut
+
+sub cust_main_county {
+ my $self = shift;
+ qsearchs( 'cust_main_county', { 'taxnum' => $self->taxnum } );
+}
+
+=back
+
+=head1 BUGS
+
+Texas tax is a royal pain in the ass.
+
+=head1 SEE ALSO
+
+L<FS::cust_main_county>, L<FS::cust_main>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_tax_exempt_pkg.pm b/FS/FS/cust_tax_exempt_pkg.pm
new file mode 100644
index 0000000..128921b
--- /dev/null
+++ b/FS/FS/cust_tax_exempt_pkg.pm
@@ -0,0 +1,136 @@
+package FS::cust_tax_exempt_pkg;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_main_Mixin;
+use FS::cust_bill_pkg;
+use FS::cust_main_county;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+=head1 NAME
+
+FS::cust_tax_exempt_pkg - Object methods for cust_tax_exempt_pkg records
+
+=head1 SYNOPSIS
+
+ use FS::cust_tax_exempt_pkg;
+
+ $record = new FS::cust_tax_exempt_pkg \%hash;
+ $record = new FS::cust_tax_exempt_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_tax_exempt_pkg object represents a record of a customer tax
+exemption. Currently this is only used for "texas tax". FS::cust_tax_exempt
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item exemptpkgnum - primary key
+
+=item billpkgnum - invoice line item (see L<FS::cust_bill_pkg>)
+
+=item taxnum - tax rate (see L<FS::cust_main_county>)
+
+=item year
+
+=item month
+
+=item amount
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new exemption record. To add the examption record to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_tax_exempt_pkg'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid exemption record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('exemptnum')
+# || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
+ || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum')
+ || $self->ut_foreign_key('taxnum', 'cust_main_county', 'taxnum')
+ || $self->ut_number('year') #check better
+ || $self->ut_number('month') #check better
+ || $self->ut_money('amount')
+ || $self->SUPER::check
+ ;
+}
+
+=back
+
+=head1 BUGS
+
+Texas tax is still a royal pain in the ass.
+
+=head1 SEE ALSO
+
+L<FS::cust_main_county>, L<FS::cust_bill_pkg>, L<FS::Record>, schema.html from
+the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_tax_location.pm b/FS/FS/cust_tax_location.pm
new file mode 100644
index 0000000..b7437a0
--- /dev/null
+++ b/FS/FS/cust_tax_location.pm
@@ -0,0 +1,336 @@
+package FS::cust_tax_location;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::Misc qw ( csv_from_fixed );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::cust_tax_location - Object methods for cust_tax_location records
+
+=head1 SYNOPSIS
+
+ use FS::cust_tax_location;
+
+ $record = new FS::cust_tax_location \%hash;
+ $record = new FS::cust_tax_location { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_tax_location object represents a mapping between a customer and
+a tax location. FS::cust_tax_location inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item custlocationnum
+
+primary key
+
+=item data_vendor
+
+a tax data vendor
+
+=item zip
+
+=item state
+
+=item plus4hi
+
+the upper bound of the last 4 zip code digits
+
+=item plus4lo
+
+the lower bound of the last 4 zip code digits
+
+=item default_location
+
+'Y' when this record represents the default for zip
+
+=item geocode - the foreign key into FS::part_pkg_tax_rate and FS::tax_rate
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new cust_tax_location. To add the cust_tax_location to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'cust_tax_location'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid cust_tax_location. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('custlocationnum')
+ || $self->ut_text('data_vendor')
+ || $self->ut_textn('city')
+ || $self->ut_textn('postalcity')
+ || $self->ut_textn('county')
+ || $self->ut_text('state')
+ || $self->ut_numbern('plus4hi')
+ || $self->ut_numbern('plus4lo')
+ || $self->ut_enum('default', [ '', ' ', 'Y' ] ) # wtf?
+ || $self->ut_enum('cityflag', [ '', 'I', 'O', 'B' ] )
+ || $self->ut_alpha('geocode')
+ ;
+ return $error if $error;
+
+ #ugh! cch canada weirdness
+ if ($self->state eq 'CN') {
+ $error = "Illegal cch canadian zip"
+ unless $self->zip =~ /^[A-Z]$/;
+ } else {
+ $error = $self->ut_number('zip', $self->state eq 'CN' ? 'CA' : 'US');
+ }
+ return $error if $error;
+
+ #ugh! cch canada weirdness
+ return "must specify either city/county or plus4lo/plus4hi"
+ unless ( $self->plus4lo && $self->plus4hi ||
+ ($self->city || $self->state eq 'CN') && $self->county
+ );
+
+ $self->SUPER::check;
+}
+
+
+sub batch_import {
+ my ($param, $job) = @_;
+
+ my $fh = $param->{filehandle};
+ my $format = $param->{'format'};
+
+ my $imported = 0;
+ my @fields;
+ my $hook;
+
+ my @column_lengths = ();
+ my @column_callbacks = ();
+ if ( $format =~ /^cch-fixed/ ) {
+ $format =~ s/-fixed//;
+ my $f = $format;
+ my $update = 0;
+ $f =~ s/-update// && ($update = 1);
+ if ($f eq 'cch') {
+ push @column_lengths, qw( 5 2 4 4 10 1 );
+ } elsif ( $f eq 'cch-zip' ) {
+ push @column_lengths, qw( 5 28 25 2 28 5 1 1 10 1 2 );
+ } else {
+ return "Unknown format: $format";
+ }
+ push @column_lengths, 1 if $update;
+ }
+
+ my $line;
+ my ( $count, $last, $min_sec ) = (0, time, 5); #progressbar
+ if ( $job || scalar(@column_lengths) ) {
+ my $error = csv_from_fixed(\$fh, \$count, \@column_lengths);
+ return $error if $error;
+ }
+
+ if ( $format eq 'cch' || $format eq 'cch-update' ) {
+ @fields = qw( zip state plus4lo plus4hi geocode default );
+ push @fields, 'actionflag' if $format eq 'cch-update';
+
+ $imported++ if $format eq 'cch-update'; #empty file ok
+
+ $hook = sub {
+ my $hash = shift;
+
+ $hash->{'data_vendor'} = 'cch';
+
+ if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
+ delete($hash->{actionflag});
+
+ my $cust_tax_location = qsearchs('cust_tax_location', $hash);
+ return "Can't find cust_tax_location to delete: ".
+ join(" ", map { "$_ => ". $hash->{$_} } @fields)
+ unless $cust_tax_location;
+
+ my $error = $cust_tax_location->delete;
+ return $error if $error;
+
+ delete($hash->{$_}) foreach (keys %$hash);
+ }
+
+ delete($hash->{'actionflag'});
+
+ '';
+
+ };
+
+ } elsif ( $format eq 'cch-zip' || $format eq 'cch-update-zip' ) {
+ @fields = qw( zip city county state postalcity countyfips countydef default geocode cityflag unique );
+ push @fields, 'actionflag' if $format eq 'cch-update-zip';
+
+ $imported++ if $format eq 'cch-update'; #empty file ok
+
+ $hook = sub {
+ my $hash = shift;
+
+ $hash->{'data_vendor'} = 'cch-zip';
+ delete($hash->{$_}) foreach qw( countyfips countydef unique );
+
+ $hash->{'cityflag'} =~ s/ //g;
+
+ if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
+ delete($hash->{actionflag});
+
+ my $cust_tax_location = qsearchs('cust_tax_location', $hash);
+ return "Can't find cust_tax_location to delete: ".
+ join(" ", map { "$_ => ". $hash->{$_} } @fields)
+ unless $cust_tax_location;
+
+ my $error = $cust_tax_location->delete;
+ return $error if $error;
+
+ delete($hash->{$_}) foreach (keys %$hash);
+ }
+
+ delete($hash->{'actionflag'});
+
+ '';
+
+ };
+
+ } elsif ( $format eq 'extended' ) {
+ die "unimplemented\n";
+ @fields = qw( );
+ } else {
+ die "unknown format $format";
+ }
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+
+ my $csv = new Text::CSV_XS;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ while ( defined($line=<$fh>) ) {
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my @columns = $csv->fields();
+
+ my %cust_tax_location = ( 'data_vendor' => $format );;
+ foreach my $field ( @fields ) {
+ $cust_tax_location{$field} = shift @columns;
+ }
+ if ( scalar( @columns ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unexpected trailing columns in line (wrong format?): $line";
+ }
+
+ my $error = &{$hook}(\%cust_tax_location);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ next unless scalar(keys %cust_tax_location);
+
+ my $cust_tax_location = new FS::cust_tax_location( \%cust_tax_location );
+ $error = $cust_tax_location->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert cust_tax_location for $line: $error";
+ }
+
+ $imported++;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return "Empty file!" unless ( $imported || $format =~ /^cch-update/ );
+
+ ''; #no error
+
+}
+
+=back
+
+=head1 BUGS
+
+The author should be informed of any you find.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/domain_record.pm b/FS/FS/domain_record.pm
new file mode 100644
index 0000000..6513abf
--- /dev/null
+++ b/FS/FS/domain_record.pm
@@ -0,0 +1,438 @@
+package FS::domain_record;
+
+use strict;
+use vars qw( @ISA $noserial_hack $DEBUG );
+use FS::Conf;
+#use FS::Record qw( qsearch qsearchs );
+use FS::Record qw( qsearchs dbh );
+use FS::svc_domain;
+use FS::svc_www;
+
+@ISA = qw(FS::Record);
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::domain_record - Object methods for domain_record records
+
+=head1 SYNOPSIS
+
+ use FS::domain_record;
+
+ $record = new FS::domain_record \%hash;
+ $record = new FS::domain_record { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::domain_record object represents an entry in a DNS zone.
+FS::domain_record inherits from FS::Record. The following fields are currently
+supported:
+
+=over 4
+
+=item recnum - primary key
+
+=item svcnum - Domain (see L<FS::svc_domain>) of this entry
+
+=item reczone - partial (or full) zone for this entry
+
+=item recaf - address family for this entry, currently only `IN' is recognized.
+
+=item rectype - record type for this entry (A, MX, etc.)
+
+=item recdata - data for this entry
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new entry. To add the entry to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'domain_record'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $self->rectype eq '_mstr' ) { #delete all other records
+ foreach my $domain_record ( reverse $self->svc_domain->domain_record ) {
+ my $error = $domain_record->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ unless ( $self->rectype =~ /^(SOA|_mstr)$/ ) {
+ my $error = $self->increment_serial;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $conf = new FS::Conf;
+ if ( $self->rectype =~ /^A$/ && ! $conf->exists('disable_autoreverse') ) {
+ my $reverse = $self->reverse_record;
+ if ( $reverse && ! $reverse->recnum ) {
+ my $error = $reverse->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error adding corresponding reverse-ARPA record: $error";
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete a domain record which has a website!"
+ if qsearchs( 'svc_www', { 'recnum' => $self->recnum } );
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ unless ( $self->rectype =~ /^(SOA|_mstr)$/ ) {
+ my $error = $self->increment_serial;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $conf = new FS::Conf;
+ if ( $self->rectype =~ /^A$/ && ! $conf->exists('disable_autoreverse') ) {
+ my $reverse = $self->reverse_record;
+ if ( $reverse && $reverse->recnum && $reverse->recdata eq $self->zone.'.' ){
+ my $error = $reverse->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error removing corresponding reverse-ARPA record: $error";
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::replace(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ unless ( $self->rectype eq 'SOA' ) {
+ my $error = $self->increment_serial;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid entry. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('recnum')
+ || $self->ut_number('svcnum')
+ ;
+ return $error if $error;
+
+ return "Unknown svcnum (in svc_domain)"
+ unless qsearchs('svc_domain', { 'svcnum' => $self->svcnum } );
+
+ my $conf = new FS::Conf;
+
+ if ( $conf->exists('zone-underscore') ) {
+ $self->reczone =~ /^(@|[a-z0-9_\.\-\*]+)$/i
+ or return "Illegal reczone: ". $self->reczone;
+ $self->reczone($1);
+ } else {
+ $self->reczone =~ /^(@|[a-z0-9\.\-\*]+)$/i
+ or return "Illegal reczone: ". $self->reczone;
+ $self->reczone($1);
+ }
+
+ $self->recaf =~ /^(IN)$/ or return "Illegal recaf: ". $self->recaf;
+ $self->recaf($1);
+
+ $self->rectype =~ /^(SOA|NS|MX|A|PTR|CNAME|TXT|_mstr)$/
+ or return "Illegal rectype (only SOA NS MX A PTR CNAME TXT recognized): ".
+ $self->rectype;
+ $self->rectype($1);
+
+ return "Illegal reczone for ". $self->rectype. ": ". $self->reczone
+ if $self->rectype !~ /^MX$/i && $self->reczone =~ /\*/;
+
+ if ( $self->rectype eq 'SOA' ) {
+ my $recdata = $self->recdata;
+ $recdata =~ s/\s+/ /g;
+ $recdata =~ /^([a-z0-9\.\-]+ [\w\-\+]+\.[a-z0-9\.\-]+ \( ((\d+|((\d+[WDHMS])+)) ){5}\))$/i
+ or return "Illegal data for SOA record: $recdata";
+ $self->recdata($1);
+ } elsif ( $self->rectype eq 'NS' ) {
+ $self->recdata =~ /^([a-z0-9\.\-]+)$/i
+ or return "Illegal data for NS record: ". $self->recdata;
+ $self->recdata($1);
+ } elsif ( $self->rectype eq 'MX' ) {
+ $self->recdata =~ /^(\d+)\s+([a-z0-9\.\-]+)$/i
+ or return "Illegal data for MX record: ". $self->recdata;
+ $self->recdata("$1 $2");
+ } elsif ( $self->rectype eq 'A' ) {
+ $self->recdata =~ /^((\d{1,3}\.){3}\d{1,3})$/
+ or return "Illegal data for A record: ". $self->recdata;
+ $self->recdata($1);
+ } elsif ( $self->rectype eq 'PTR' ) {
+ if ( $conf->exists('zone-underscore') ) {
+ $self->recdata =~ /^([a-z0-9_\.\-]+)$/i
+ or return "Illegal data for PTR record: ". $self->recdata;
+ $self->recdata($1);
+ } else {
+ $self->recdata =~ /^([a-z0-9\.\-]+)$/i
+ or return "Illegal data for PTR record: ". $self->recdata;
+ $self->recdata($1);
+ }
+ } elsif ( $self->rectype eq 'CNAME' ) {
+ $self->recdata =~ /^([a-z0-9\.\-]+|\@)$/i
+ or return "Illegal data for CNAME record: ". $self->recdata;
+ $self->recdata($1);
+ } elsif ( $self->rectype eq 'TXT' ) {
+ if ( $self->recdata =~ /^((?:\S+)|(?:".+"))$/ ) {
+ $self->recdata($1);
+ } else {
+ $self->recdata('"'. $self->recdata. '"'); #?
+ }
+ # or return "Illegal data for TXT record: ". $self->recdata;
+ } elsif ( $self->rectype eq '_mstr' ) {
+ $self->recdata =~ /^((\d{1,3}\.){3}\d{1,3})$/
+ or return "Illegal data for _master pseudo-record: ". $self->recdata;
+ } else {
+ die "ack!";
+ }
+
+ $self->SUPER::check;
+}
+
+=item increment_serial
+
+=cut
+
+sub increment_serial {
+ return '' if $noserial_hack;
+ my $self = shift;
+
+ my $soa = qsearchs('domain_record', {
+ svcnum => $self->svcnum,
+ reczone => '@',
+ recaf => 'IN',
+ rectype => 'SOA', } )
+ || qsearchs('domain_record', {
+ svcnum => $self->svcnum,
+ reczone => $self->svc_domain->domain.'.',
+ recaf => 'IN',
+ rectype => 'SOA',
+ } )
+ or return "soa record not found; can't increment serial";
+
+ my $data = $soa->recdata;
+ $data =~ s/(\(\D*)(\d+)/$1.($2+1)/e; #well, it works.
+
+ my %hash = $soa->hash;
+ $hash{recdata} = $data;
+ my $new = new FS::domain_record \%hash;
+ $new->replace($soa);
+}
+
+=item svc_domain
+
+Returns the domain (see L<FS::svc_domain>) for this record.
+
+=cut
+
+sub svc_domain {
+ my $self = shift;
+ qsearchs('svc_domain', { svcnum => $self->svcnum } );
+}
+
+=item zone
+
+Returns the canonical zone name.
+
+=cut
+
+sub zone {
+ my $self = shift;
+ my $zone = $self->reczone; # or die ?
+ if ( $zone =~ /\.$/ ) {
+ $zone =~ s/\.$//;
+ } else {
+ my $svc_domain = $self->svc_domain; # or die ?
+ $zone .= '.'. $svc_domain->domain;
+ $zone =~ s/^\@\.//;
+ }
+ $zone;
+}
+
+=item reverse_record
+
+Returns the corresponding reverse-ARPA record as another FS::domain_record
+object. If the specific record does not exist in the database but the
+reverse-ARPA zone itself does, an appropriate new record is created. If no
+reverse-ARPA zone is available at all, returns false.
+
+(You can test whether or not record itself exists in the database or is a new
+object that might need to be inserted by checking the recnum field)
+
+Mostly used by the insert and delete methods - probably should see them for
+examples.
+
+=cut
+
+sub reverse_record {
+ my $self = shift;
+ warn "reverse_record called\n" if $DEBUG;
+ #should support classless reverse-ARPA ala rfc2317 too
+ $self->recdata =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
+ or return '';
+ my $domain = "$3.$2.$1.in-addr.arpa";
+ my $ptr_reczone = $4;
+ warn "reverse_record: searching for domain: $domain\n" if $DEBUG;
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
+ or return '';
+ warn "reverse_record: found domain: $domain\n" if $DEBUG;
+ my %hash = (
+ 'svcnum' => $svc_domain->svcnum,
+ 'reczone' => $ptr_reczone,
+ 'recaf' => 'IN',
+ 'rectype' => 'PTR',
+ );
+ qsearchs('domain_record', \%hash )
+ or new FS::domain_record { %hash, 'recdata' => $self->zone.'.' };
+}
+
+=back
+
+=head1 BUGS
+
+The data validation doesn't check everything it could. In particular,
+there is no protection against bad data that passes the regex, duplicate
+SOA records, forgetting the trailing `.', impossible IP addersses, etc. Of
+course, it's still better than editing the zone files directly. :)
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/export_svc.pm b/FS/FS/export_svc.pm
new file mode 100644
index 0000000..0370f5f
--- /dev/null
+++ b/FS/FS/export_svc.pm
@@ -0,0 +1,322 @@
+package FS::export_svc;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::part_export;
+use FS::part_svc;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::export_svc - Object methods for export_svc records
+
+=head1 SYNOPSIS
+
+ use FS::export_svc;
+
+ $record = new FS::export_svc \%hash;
+ $record = new FS::export_svc { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::export_svc object links a service definition (see L<FS::part_svc>) to
+an export (see L<FS::part_export>). FS::export_svc inherits from FS::Record.
+The following fields are currently supported:
+
+=over 4
+
+=item exportsvcnum - primary key
+
+=item exportnum - export (see L<FS::part_export>)
+
+=item svcpart - service definition (see L<FS::part_svc>)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'export_svc'; }
+
+=item insert [ JOB, OFFSET, MULTIPLIER ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+TODOC: JOB, OFFSET, MULTIPLIER
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my( $job, $offset, $mult ) = ( '', 0, 100);
+ $job = shift if @_;
+ $offset = shift if @_;
+ $mult = shift if @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->check;
+ return $error if $error;
+
+ #check for duplicates!
+ my @checks = ();
+ my $svcdb = $self->part_svc->svcdb;
+ if ( $svcdb eq 'svc_acct' ) {
+
+ if ( $self->part_export->nodomain =~ /^Y/i ) {
+ push @checks, {
+ label => 'usernames',
+ method => 'username',
+ sortby => sub { $a cmp $b },
+ };
+ } else {
+ push @checks, {
+ label => 'username@domain',
+ method => 'email',
+ sortby => sub {
+ my($auser, $adomain) = split('@', $a);
+ my($buser, $bdomain) = split('@', $b);
+ $adomain cmp $bdomain || $auser cmp $buser;
+ },
+ };
+ }
+
+ unless ( $self->part_svc->part_svc_column('uid')->columnflag eq 'F' ) {
+ push @checks, {
+ label => 'uids',
+ method => 'uid',
+ sortby => sub { $a <=> $b },
+ };
+ }
+
+ } elsif ( $svcdb eq 'svc_domain' ) {
+ push @checks, {
+ label => 'domains',
+ method => 'domain',
+ sortby => sub { $a cmp $b },
+ };
+ } else {
+ warn "WARNING: No duplicate checking done on merge of $svcdb exports";
+ }
+
+ if ( @checks ) {
+
+ my $done = 0;
+ my $percheck = $mult / scalar(@checks);
+
+ foreach my $check ( @checks ) {
+
+ if ( $job ) {
+ $error = $job->update_statustext(int( $offset + ($done+.33) *$percheck ));
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my @current_svc = $self->part_export->svc_x;
+ #warn "current: ". scalar(@current_svc). " $current_svc[0]\n";
+
+ if ( $job ) {
+ $error = $job->update_statustext(int( $offset + ($done+.67) *$percheck ));
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my @new_svc = $self->part_svc->svc_x;
+ #warn "new: ". scalar(@new_svc). " $new_svc[0]\n";
+
+ if ( $job ) {
+ $error = $job->update_statustext(int( $offset + ($done+1) *$percheck ));
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $method = $check->{'method'};
+ my %cur_svc = map { $_->$method() => $_ } @current_svc;
+ my @dup_svc = grep { $cur_svc{$_->$method()} } @new_svc;
+ #my @diff_customer = grep {
+ # $_->cust_pkg->custnum != $cur_svc{$_->$method()}->cust_pkg->custnum
+ # } @dup_svc;
+
+
+
+ if ( @dup_svc ) { #aye, that's the rub
+ #error out for now, eventually accept different options of adjustments
+ # to make to allow us to continue forward
+ $dbh->rollback if $oldAutoCommit;
+
+ my @diff_customer_svc = grep {
+ my $cust_pkg = $_->cust_svc->cust_pkg;
+ my $custnum = $cust_pkg ? $cust_pkg->custnum : 0;
+ my $other_cust_pkg = $cur_svc{$_->$method()}->cust_svc->cust_pkg;
+ my $other_custnum = $other_cust_pkg ? $other_cust_pkg->custnum : 0;
+ $custnum != $other_custnum;
+ } @dup_svc;
+
+ my $label = $check->{'label'};
+ my $sortby = $check->{'sortby'};
+ return "Can't export ".
+ $self->part_svc->svcpart.':'.$self->part_svc->svc. " service to ".
+ $self->part_export->exportnum.':'.$self->part_export->exporttype.
+ ' on '. $self->part_export->machine.
+ ' : '. scalar(@dup_svc). " duplicate $label".
+ ' ('. scalar(@diff_customer_svc). " from different customers)".
+ ": ". join(', ', sort $sortby map { $_->$method() } @dup_svc )
+ #": ". join(', ', sort $sortby map { $_->$method() } @diff_customer_svc )
+ ;
+ }
+
+ $done++;
+ }
+
+ } #end of duplicate check, whew
+
+ $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+# if ( $self->part_svc->svcdb eq 'svc_acct' ) {
+#
+# if ( $self->part_export->nodomain =~ /^Y/i ) {
+#
+# select username from svc_acct where svcpart = $svcpart
+# group by username having count(*) > 1;
+#
+# } else {
+#
+# select username, domain
+# from svc_acct
+# join svc_domain on ( svc_acct.domsvc = svc_domain.svcnum )
+# group by username, domain having count(*) > 1;
+#
+# }
+#
+# } elsif ( $self->part_svc->svcdb eq 'svc_domain' ) {
+#
+# #similar but easier domain checking one
+#
+# } #etc.?
+#
+# my @services =
+# map { $_->part_svc }
+# grep { $_->svcpart != $self->svcpart }
+# $self->part_export->export_svc;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('exportsvcnum')
+ || $self->ut_number('exportnum')
+ || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
+ || $self->ut_number('svcpart')
+ || $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
+ || $self->SUPER::check
+ ;
+}
+
+=item part_export
+
+Returns the FS::part_export object (see L<FS::part_export>).
+
+=cut
+
+sub part_export {
+ my $self = shift;
+ qsearchs( 'part_export', { 'exportnum' => $self->exportnum } );
+}
+
+=item part_svc
+
+Returns the FS::part_svc object (see L<FS::part_svc>).
+
+=cut
+
+sub part_svc {
+ my $self = shift;
+ qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::part_export>, L<FS::part_svc>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_Common.pm b/FS/FS/h_Common.pm
new file mode 100644
index 0000000..ca13e1b
--- /dev/null
+++ b/FS/FS/h_Common.pm
@@ -0,0 +1,124 @@
+package FS::h_Common;
+
+use strict;
+use FS::Record qw(dbdef);
+use Carp qw(confess);
+
+=head1 NAME
+
+FS::h_Common - History table "mixin" common base class
+
+=head1 SYNOPSIS
+
+package FS::h_tablename;
+@ISA = qw( FS::h_Common FS::tablename );
+
+sub table { 'h_table_name'; }
+
+sub insert { return "can't insert history records manually"; }
+sub delete { return "can't delete history records"; }
+sub replace { return "can't modify history records"; }
+
+=head1 DESCRIPTION
+
+FS::h_Common is intended as a "mixin" base class for history table classes to
+inherit from.
+
+=head1 METHODS
+
+=over 4
+
+=item sql_h_search END_TIMESTAMP [ START_TIMESTAMP ]
+
+Returns an a list consisting of the "SELECT", "EXTRA_SQL", SQL fragments, a
+placeholder for "CACHE_OBJ" and an "AS" SQL fragment, to search for the
+appropriate history records created before END_TIMESTAMP and (optionally) not
+deleted before START_TIMESTAMP.
+
+=cut
+
+sub sql_h_search {
+ my( $self, $end ) = ( shift, shift );
+
+ my $table = $self->table;
+ my $real_table = ($table =~ /^h_(.*)$/) ? $1 : $table;
+ my $pkey = dbdef->table($real_table)->primary_key
+ or die "can't (yet) search history table $real_table without a primary key";
+
+ unless ($end) {
+ confess 'Called sql_h_search without END_TIMESTAMP';
+ }
+
+ my( $notdeleted, $notdeleted_mr ) = ( '', '' );
+ if ( scalar(@_) && $_[0] ) {
+ $notdeleted =
+ "AND 0 = ( SELECT COUNT(*) FROM $table as notdel
+ WHERE notdel.$pkey = maintable.$pkey
+ AND notdel.history_action = 'delete'
+ AND notdel.history_date > maintable.history_date
+ AND notdel.history_date <= $_[0]
+ )";
+ $notdeleted_mr =
+ "AND 0 = ( SELECT COUNT(*) FROM $table as notdel_mr
+ WHERE notdel_mr.$pkey = mostrecent.$pkey
+ AND notdel_mr.history_action = 'delete'
+ AND notdel_mr.history_date > mostrecent.history_date
+ AND notdel_mr.history_date <= $_[0]
+ )";
+ }
+
+ (
+ #"DISTINCT ON ( $pkey ) *",
+ "*",
+
+ "AND history_date <= $end
+ AND ( history_action = 'insert'
+ OR history_action = 'replace_new'
+ )
+ $notdeleted
+ AND history_date = ( SELECT MAX(mostrecent.history_date)
+ FROM $table AS mostrecent
+ WHERE mostrecent.$pkey = maintable.$pkey
+ AND mostrecent.history_date <= $end
+ AND ( mostrecent.history_action = 'insert'
+ OR mostrecent.history_action = 'replace_new'
+ )
+ $notdeleted_mr
+ )
+
+ ORDER BY $pkey ASC",
+ #ORDER BY $pkey ASC, history_date DESC",
+
+ '',
+
+ 'AS maintable',
+ );
+
+}
+
+=item sql_h_searchs END_TIMESTAMP [ START_TIMESTAMP ]
+
+Like sql_h_search, but limited to the single most recent record (before
+END_TIMESTAMP)
+
+=cut
+
+sub sql_h_searchs {
+ my $self = shift;
+ my($select, $where, $cacheobj, $as) = $self->sql_h_search(@_);
+ $where .= ' LIMIT 1';
+ ($select, $where, $cacheobj, $as);
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_cust_bill.pm b/FS/FS/h_cust_bill.pm
new file mode 100644
index 0000000..7a3d811
--- /dev/null
+++ b/FS/FS/h_cust_bill.pm
@@ -0,0 +1,33 @@
+package FS::h_cust_bill;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::cust_bill;
+
+@ISA = qw( FS::h_Common FS::cust_bill );
+
+sub table { 'h_cust_bill' };
+
+=head1 NAME
+
+FS::h_cust_bill - Historical record of customer tax changes (old-style)
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_cust_bill object represents historical changes to invoices.
+FS::h_cust_bill inherits from FS::h_Common and FS::cust_bill.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_bill>, L<FS::h_Common>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_cust_credit.pm b/FS/FS/h_cust_credit.pm
new file mode 100644
index 0000000..1425a26
--- /dev/null
+++ b/FS/FS/h_cust_credit.pm
@@ -0,0 +1,33 @@
+package FS::h_cust_credit;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::cust_credit;
+
+@ISA = qw( FS::h_Common FS::cust_credit );
+
+sub table { 'h_cust_credit' };
+
+=head1 NAME
+
+FS::h_cust_credit - Historical record of customer credit changes
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_cust_credit object represents historical changes to credits.
+FS::h_cust_credit inherits from FS::h_Common and FS::cust_credit.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_credit>, L<FS::h_Common>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_cust_pay.pm b/FS/FS/h_cust_pay.pm
new file mode 100644
index 0000000..6434b3f
--- /dev/null
+++ b/FS/FS/h_cust_pay.pm
@@ -0,0 +1,33 @@
+package FS::h_cust_pay;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::cust_pay;
+
+@ISA = qw( FS::h_Common FS::cust_pay );
+
+sub table { 'h_cust_pay' };
+
+=head1 NAME
+
+FS::h_cust_pay - Historical record of customer payment changes
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_cust_pay object represents historical changes to payments.
+FS::h_cust_pay inherits from FS::h_Common and FS::cust_pay.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_pay>, L<FS::h_Common>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_cust_pkg.pm b/FS/FS/h_cust_pkg.pm
new file mode 100644
index 0000000..e796f41
--- /dev/null
+++ b/FS/FS/h_cust_pkg.pm
@@ -0,0 +1,34 @@
+package FS::h_cust_pkg;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::cust_pkg;
+
+@ISA = qw( FS::h_Common FS::cust_pkg );
+
+sub table { 'h_cust_pkg' };
+
+=head1 NAME
+
+FS::h_cust_pkg - Historical record of customer package changes
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_cust_pkg object represents historical changes to packages.
+FS::h_cust_pkg inherits from FS::h_Common and FS::cust_pkg.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_pkg>, L<FS::h_Common>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/h_cust_pkg_reason.pm b/FS/FS/h_cust_pkg_reason.pm
new file mode 100644
index 0000000..dda2009
--- /dev/null
+++ b/FS/FS/h_cust_pkg_reason.pm
@@ -0,0 +1,34 @@
+package FS::h_cust_pkg_reason;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::cust_pkg_reason;
+
+@ISA = qw( FS::h_Common FS::cust_pkg_reason );
+
+sub table { 'h_cust_pkg_reason' };
+
+=head1 NAME
+
+FS::h_cust_pkg_reason - Historical record of customer package changes
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_cust_pkg_reason object represents historical changes to packages.
+FS::h_cust_pkg_reason inherits from FS::h_Common and FS::cust_pkg_reason.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_pkg_reason>, L<FS::h_Common>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/h_cust_svc.pm b/FS/FS/h_cust_svc.pm
new file mode 100644
index 0000000..e030436
--- /dev/null
+++ b/FS/FS/h_cust_svc.pm
@@ -0,0 +1,159 @@
+package FS::h_cust_svc;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Carp;
+use FS::Record qw(qsearchs);
+use FS::h_Common;
+use FS::cust_svc;
+
+@ISA = qw( FS::h_Common FS::cust_svc );
+
+$DEBUG = 0;
+
+sub table { 'h_cust_svc'; }
+
+=head1 NAME
+
+FS::h_cust_svc - Object method for h_cust_svc objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_cust_svc object represents a historical service. FS::h_cust_svc
+inherits from FS::h_Common and FS::cust_svc.
+
+=head1 METHODS
+
+=over 4
+
+=item date_deleted
+
+Returns the date this service was deleted, if any.
+
+=cut
+
+sub date_deleted {
+ my $self = shift;
+ $self->h_date('delete');
+}
+
+=item label END_TIMESTAMP [ START_TIMESTAMP ]
+
+Returns a label for this historical service, if the service was created before
+END_TIMESTAMP and (optionally) not deleted before START_TIMESTAMP. Otherwise,
+returns an empty list.
+
+If a service is found, returns a list consisting of:
+- The name of this historical service (from part_svc)
+- A meaningful identifier (username, domain, or mail alias)
+- The table name (i.e. svc_domain) for this historical service
+
+=cut
+
+sub label {
+ my $self = shift;
+ carp "FS::h_cust_svc::label called on $self" if $DEBUG;
+ my $svc_x = $self->h_svc_x(@_);
+ return () unless $svc_x;
+ my $part_svc = $self->part_svc;
+
+ unless ($svc_x) {
+ carp "can't find h_". $self->part_svc->svcdb. '.svcnum '. $self->svcnum if $DEBUG;
+ return $part_svc->svc, 'n/a', $part_svc->svcdb;
+ }
+
+ my @label;
+ eval { @label = $self->_svc_label($svc_x, @_); };
+
+ if ($@) {
+ carp 'while resolving history record for svcdb/svcnum ' .
+ $part_svc->svcdb . '/' . $self->svcnum . ': ' . $@ if $DEBUG;
+ return $part_svc->svc, 'n/a', $part_svc->svcdb;
+ } else {
+ return @label;
+ }
+
+}
+
+=item h_svc_x END_TIMESTAMP [ START_TIMESTAMP ]
+
+Returns the FS::h_svc_XXX object for this service as of END_TIMESTAMP (i.e. an
+FS::h_svc_acct object or FS::h_svc_domain object, etc.) and (optionally) not
+cancelled before START_TIMESTAMP.
+
+=cut
+
+#false laziness w/cust_pkg::h_cust_svc
+sub h_svc_x {
+ my $self = shift;
+ my $svcdb = $self->part_svc->svcdb;
+
+ warn "requiring FS/h_$svcdb.pm" if $DEBUG;
+ require "FS/h_$svcdb.pm";
+ my $svc_x = qsearchs(
+ "h_$svcdb",
+ { 'svcnum' => $self->svcnum, },
+ "FS::h_$svcdb"->sql_h_searchs(@_),
+ ) || $self->SUPER::svc_x;
+
+ if ($svc_x) {
+ carp "Using $svcdb in place of missing h_${svcdb} record."
+ if ($svc_x->isa('FS::' . $svcdb) and $DEBUG);
+ return $svc_x;
+ } else {
+ return '';
+ }
+
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+use FS::UID qw( driver_name dbh );
+
+sub _upgrade_data { # class method
+ my ($class, %opts) = @_;
+
+ warn "[FS::h_cust_svc] upgrading $class\n" if $DEBUG;
+
+ return if driver_name =~ /^mysql/; #You can't specify target table 'h_cust_svc' for update in FROM clause
+
+ my $sql = "
+ DELETE FROM h_cust_svc
+ WHERE history_action = 'delete'
+ AND historynum != ( SELECT min(historynum) FROM h_cust_svc AS main
+ WHERE main.history_date = h_cust_svc.history_date
+ AND main.history_user = h_cust_svc.history_user
+ AND main.svcnum = h_cust_svc.svcnum
+ AND main.svcpart = h_cust_svc.svcpart
+ AND ( main.pkgnum = h_cust_svc.pkgnum
+ OR ( main.pkgnum IS NULL AND h_cust_svc.pkgnum IS NULL )
+ )
+ AND ( main.overlimit = h_cust_svc.overlimit
+ OR ( main.overlimit IS NULL AND h_cust_svc.overlimit IS NULL )
+ )
+ )
+ ";
+
+ warn $sql if $DEBUG;
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::cust_svc>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_cust_tax_exempt.pm b/FS/FS/h_cust_tax_exempt.pm
new file mode 100644
index 0000000..9d2318b
--- /dev/null
+++ b/FS/FS/h_cust_tax_exempt.pm
@@ -0,0 +1,40 @@
+package FS::h_cust_tax_exempt;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::cust_tax_exempt;
+
+@ISA = qw( FS::h_Common FS::cust_tax_exempt );
+
+sub table { 'h_cust_tax_exempt' };
+
+=head1 NAME
+
+FS::h_cust_tax_exempt - Historical record of customer tax changes (old-style)
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_cust_tax_exempt object represents historical changes to old-style
+customer tax exemptions. FS::h_cust_tax_exempt inherits from FS::h_Common and
+FS::cust_tax_exempt.
+
+=head1 NOTE
+
+Old-style customer tax exemptions are only useful for legacy migrations - if
+you are looking for current customer tax exemption data see
+L<FS::cust_tax_exempt_pkg>.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_tax_exempt>, L<FS::cust_tax_exempt_pkg>, L<FS::h_Common>,
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_domain_record.pm b/FS/FS/h_domain_record.pm
new file mode 100644
index 0000000..0ab974f
--- /dev/null
+++ b/FS/FS/h_domain_record.pm
@@ -0,0 +1,33 @@
+package FS::h_domain_record;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::domain_record;
+
+@ISA = qw( FS::h_Common FS::domain_record );
+
+sub table { 'h_domain_record' };
+
+=head1 NAME
+
+FS::h_domain_record - Historical DNS entry objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_domain_record object represents a historical entry in a DNS zone.
+FS::h_domain_record inherits from FS::h_Common and FS::domain_record.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_external>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_acct.pm b/FS/FS/h_svc_acct.pm
new file mode 100644
index 0000000..247d20c
--- /dev/null
+++ b/FS/FS/h_svc_acct.pm
@@ -0,0 +1,78 @@
+package FS::h_svc_acct;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Carp qw(carp);
+use FS::Record qw(qsearchs);
+use FS::h_Common;
+use FS::svc_acct;
+use FS::svc_domain;
+use FS::h_svc_domain;
+
+@ISA = qw( FS::h_Common FS::svc_acct );
+
+$DEBUG = 0;
+
+sub table { 'h_svc_acct' };
+
+=head1 NAME
+
+FS::h_svc_acct - Historical account objects
+
+=head1 SYNOPSIS
+
+=head1 METHODS
+
+=over 4
+
+=item svc_domain
+
+=cut
+
+sub svc_domain {
+ my $self = shift;
+ qsearchs( 'h_svc_domain',
+ { 'svcnum' => $self->domsvc },
+ FS::h_svc_domain->sql_h_searchs(@_),
+ );
+}
+
+=item domain
+
+Returns the domain associated with this account.
+
+=cut
+
+sub domain {
+ my $self = shift;
+ die "svc_acct.domsvc is null for svcnum ". $self->svcnum unless $self->domsvc;
+
+ my $svc_domain = $self->svc_domain(@_) || $self->SUPER::svc_domain()
+ or die 'no history svc_domain.svcnum for svc_acct.domsvc ' . $self->domsvc;
+
+ carp 'Using FS::svc_acct record in place of missing FS::h_svc_acct record.'
+ if ($svc_domain->isa('FS::svc_acct') and $DEBUG);
+
+ $svc_domain->domain;
+
+}
+
+
+=back
+
+=head1 DESCRIPTION
+
+An FS::h_svc_acct object represents a historical account. FS::h_svc_acct
+inherits from FS::h_Common and FS::svc_acct.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_acct>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_broadband.pm b/FS/FS/h_svc_broadband.pm
new file mode 100644
index 0000000..d6038fb
--- /dev/null
+++ b/FS/FS/h_svc_broadband.pm
@@ -0,0 +1,33 @@
+package FS::h_svc_broadband;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::svc_broadband;
+
+@ISA = qw( FS::h_Common FS::svc_broadband );
+
+sub table { 'h_svc_broadband' };
+
+=head1 NAME
+
+FS::h_svc_broadband - Historical broadband connection objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_svc_broadband object represents a historical broadband connection.
+FS::h_svc_broadband inherits from FS::h_Common and FS::svc_broadband.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_broadband>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_domain.pm b/FS/FS/h_svc_domain.pm
new file mode 100644
index 0000000..60d54f7
--- /dev/null
+++ b/FS/FS/h_svc_domain.pm
@@ -0,0 +1,33 @@
+package FS::h_svc_domain;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::svc_domain;
+
+@ISA = qw( FS::h_Common FS::svc_domain );
+
+sub table { 'h_svc_domain' };
+
+=head1 NAME
+
+FS::h_svc_domain - Historical domain objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_svc_domain object represents a historical domain. FS::h_svc_domain
+inherits from FS::h_Common and FS::svc_domain.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_domain>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_external.pm b/FS/FS/h_svc_external.pm
new file mode 100644
index 0000000..5eb7064
--- /dev/null
+++ b/FS/FS/h_svc_external.pm
@@ -0,0 +1,33 @@
+package FS::h_svc_external;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::svc_external;
+
+@ISA = qw( FS::h_Common FS::svc_external );
+
+sub table { 'h_svc_external' };
+
+=head1 NAME
+
+FS::h_svc_external - Historical externally tracked service objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_svc_external object represents a historical externally tracked service.
+FS::h_svc_external inherits from FS::h_Common and FS::svc_external.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_external>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_forward.pm b/FS/FS/h_svc_forward.pm
new file mode 100644
index 0000000..25b2039
--- /dev/null
+++ b/FS/FS/h_svc_forward.pm
@@ -0,0 +1,85 @@
+package FS::h_svc_forward;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Record qw(qsearchs);
+use FS::h_Common;
+use FS::svc_forward;
+use FS::svc_acct;
+use FS::h_svc_acct;
+
+use Carp qw(carp);
+
+$DEBUG = 0;
+
+@ISA = qw( FS::h_Common FS::svc_forward );
+
+sub table { 'h_svc_forward' };
+
+=head1 NAME
+
+FS::h_svc_forward - Historical mail forwarding alias objects
+
+=head1 SYNOPSIS
+
+=head1 METHODS
+
+=over 4
+
+=item srcsvc_acct
+
+=cut
+
+sub srcsvc_acct {
+ my $self = shift;
+ my $h_svc_acct = qsearchs(
+ 'h_svc_acct',
+ { 'svcnum' => $self->srcsvc },
+ FS::h_svc_acct->sql_h_searchs(@_),
+ ) || $self->SUPER::srcsvc_acct
+ or die "no history svc_acct.svcnum for svc_forward.srcsvc ". $self->srcsvc;
+
+ carp 'Using svc_acct in place of missing h_svc_acct record.'
+ if ($h_svc_acct->isa('FS::domain_record') and $DEBUG);
+
+ return $h_svc_acct;
+
+}
+
+=item dstsvc_acct
+
+=cut
+
+sub dstsvc_acct {
+ my $self = shift;
+ my $h_svc_acct = qsearchs(
+ 'h_svc_acct',
+ { 'svcnum' => $self->dstsvc },
+ FS::h_svc_acct->sql_h_searchs(@_),
+ ) || $self->SUPER::dstsvc_acct
+ or die "no history svc_acct.svcnum for svc_forward.dstsvc ". $self->dstsvc;
+
+ carp 'Using svc_acct in place of missing h_svc_acct record.'
+ if ($h_svc_acct->isa('FS::domain_record') and $DEBUG);
+
+ return $h_svc_acct;
+}
+
+=back
+
+=head1 DESCRIPTION
+
+An FS::h_svc_forward object represents a historical mail forwarding alias.
+FS::h_svc_forward inherits from FS::h_Common and FS::svc_forward.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_forward>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_phone.pm b/FS/FS/h_svc_phone.pm
new file mode 100644
index 0000000..95898c7
--- /dev/null
+++ b/FS/FS/h_svc_phone.pm
@@ -0,0 +1,33 @@
+package FS::h_svc_phone;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+use FS::svc_phone;
+
+@ISA = qw( FS::h_Common FS::svc_phone );
+
+sub table { 'h_svc_phone' };
+
+=head1 NAME
+
+FS::h_svc_phone - Historical phone number objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_svc_phone object represents a historical phone number.
+FS::h_svc_phone inherits from FS::h_Common and FS::svc_phone.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_phone>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/h_svc_www.pm b/FS/FS/h_svc_www.pm
new file mode 100644
index 0000000..2a3b6dc
--- /dev/null
+++ b/FS/FS/h_svc_www.pm
@@ -0,0 +1,67 @@
+package FS::h_svc_www;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Carp qw(carp);
+use FS::Record qw(qsearchs);
+use FS::h_Common;
+use FS::svc_www;
+use FS::h_domain_record;
+
+@ISA = qw( FS::h_Common FS::svc_www );
+
+$DEBUG = 0;
+
+sub table { 'h_svc_www' };
+
+=head1 NAME
+
+FS::h_svc_www - Historical web virtual host objects
+
+=head1 SYNOPSIS
+
+=head1 METHODS
+
+=over 4
+
+=item domain_record
+
+=cut
+
+sub domain_record {
+ my $self = shift;
+
+ carp 'Called FS::h_svc_www->domain_record on svcnum ' . $self->svcnum if $DEBUG;
+
+ my $domain_record = qsearchs(
+ 'h_domain_record',
+ { 'recnum' => $self->recnum },
+ FS::h_domain_record->sql_h_searchs(@_),
+ ) || $self->SUPER::domain_record
+ or die "no history domain_record.recnum for svc_www.recnum ". $self->domsvc;
+
+ carp 'Using domain_record in place of missing h_domain_record record.'
+ if ($domain_record->isa('FS::domain_record') and $DEBUG);
+
+ return $domain_record;
+
+}
+
+=back
+
+=head1 DESCRIPTION
+
+An FS::h_svc_www object represents a historical web virtual host.
+FS::h_svc_www inherits from FS::h_Common and FS::svc_www.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_www>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/inventory_class.pm b/FS/FS/inventory_class.pm
new file mode 100644
index 0000000..508889b
--- /dev/null
+++ b/FS/FS/inventory_class.pm
@@ -0,0 +1,164 @@
+package FS::inventory_class;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( dbh qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::inventory_class - Object methods for inventory_class records
+
+=head1 SYNOPSIS
+
+ use FS::inventory_class;
+
+ $record = new FS::inventory_class \%hash;
+ $record = new FS::inventory_class { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::inventory_class object represents a class of inventory, such as "DID
+numbers" or "physical equipment serials". FS::inventory_class inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item classnum - primary key
+
+=item classname - Name of this class
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new inventory class. To add the class to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'inventory_class'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid inventory class. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('classnum')
+ || $self->ut_textn('classname')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item num_avail
+
+Returns the number of available (unused/unallocated) inventory items of this
+class (see L<FS::inventory_item>).
+
+=cut
+
+sub num_avail {
+ shift->num_sql('( svcnum IS NULL OR svcnum = 0 )');
+}
+
+sub num_sql {
+ my( $self, $sql ) = @_;
+ $sql = "AND $sql" if length($sql);
+ my $statement =
+ "SELECT COUNT(*) FROM inventory_item WHERE classnum = ? $sql";
+ my $sth = dbh->prepare($statement) or die dbh->errstr. " preparing $statement";
+ $sth->execute($self->classnum) or die $sth->errstr. " executing $statement";
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item num_used
+
+Returns the number of used (allocated) inventory items of this class (see
+L<FS::inventory_class>).
+
+=cut
+
+sub num_used {
+ shift->num_sql("svcnum IS NOT NULL AND svcnum > 0 ");
+}
+
+=item num_total
+
+Returns the total number of inventory items of this class (see
+L<FS::inventory_class>).
+
+=cut
+
+sub num_total {
+ shift->num_sql('');
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::inventory_item>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/inventory_item.pm b/FS/FS/inventory_item.pm
new file mode 100644
index 0000000..3bba1cd
--- /dev/null
+++ b/FS/FS/inventory_item.pm
@@ -0,0 +1,168 @@
+package FS::inventory_item;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( dbh qsearch qsearchs );
+use FS::cust_main_Mixin;
+use FS::inventory_class;
+use FS::cust_svc;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+=head1 NAME
+
+FS::inventory_item - Object methods for inventory_item records
+
+=head1 SYNOPSIS
+
+ use FS::inventory_item;
+
+ $record = new FS::inventory_item \%hash;
+ $record = new FS::inventory_item { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::inventory_item object represents a specific piece of (real or virtual)
+inventory, such as a specific DID or serial number. FS::inventory_item
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item itemnum - primary key
+
+=item classnum - Inventory class (see L<FS::inventory_class>)
+
+=item item - Item identifier (unique within its inventory class)
+
+=item svcnum - Customer servcie (see L<FS::cust_svc>)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new item. To add the item to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'inventory_item'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid item. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('itemnum')
+ || $self->ut_foreign_key('classnum', 'inventory_class', 'classnum' )
+ || $self->ut_text('item')
+ || $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum' )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item cust_svc
+
+Returns the customer service associated with this inventory item, if the
+item has been used (see L<FS::cust_svc>).
+
+=cut
+
+sub cust_svc {
+ my $self = shift;
+ return '' unless $self->svcnum;
+ qsearchs( 'cust_svc', { 'svcnum' => $self->svcnum } );
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item process_batch_import
+
+=cut
+
+sub process_batch_import {
+ my $job = shift;
+
+ my $opt = { 'table' => 'inventory_item',
+ #'params' => [ 'itembatch', 'classnum', ],
+ 'params' => [ 'classnum', ],
+ 'formats' => { 'default' => [ 'item' ] },
+ 'default_csv' => 1,
+ };
+
+ FS::Record::process_batch_import( $job, $opt, @_ );
+
+}
+
+=back
+
+=head1 BUGS
+
+maybe batch_import should be a regular method in FS::inventory_class
+
+=head1 SEE ALSO
+
+L<inventory_class>, L<cust_svc>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/m2m_Common.pm b/FS/FS/m2m_Common.pm
new file mode 100644
index 0000000..6774a48
--- /dev/null
+++ b/FS/FS/m2m_Common.pm
@@ -0,0 +1,170 @@
+package FS::m2m_Common;
+
+use strict;
+use vars qw( @ISA $DEBUG $me );
+use FS::Schema qw( dbdef );
+use FS::Record qw( qsearch qsearchs dbh );
+
+#hmm. well. we seem to be used as a mixin.
+#@ISA = qw( FS::Record );
+
+$DEBUG = 0;
+$me = '[FS::m2m_Common]';
+
+=head1 NAME
+
+FS::m2m_Common - Mixin class for classes in a many-to-many relationship
+
+=head1 SYNOPSIS
+
+use FS::m2m_Common;
+
+@ISA = qw( FS::m2m_Common FS::Record );
+
+=head1 DESCRIPTION
+
+FS::m2m_Common is intended as a mixin class for classes which have a
+many-to-many relationship with another table (via a linking table).
+
+It is currently assumed that the link table contains two fields named the same
+as the primary keys of the base and target tables, but you can ovverride this
+assumption if your table is different.
+
+=head1 METHODS
+
+=over 4
+
+=item process_m2m OPTION => VALUE, ...
+
+Available options:
+
+=over 4
+
+=item link_table (required)
+
+=item target_table (required)
+
+=item params (required)
+
+hashref; keys are primary key values in target_table (values are boolean). For convenience, keys may optionally be prefixed with the name
+of the primary key, as in "agentnum54" instead of "54", or passed as an arrayref
+of values.
+
+=item base_field (optional)
+
+base field, defaults to primary key of this base table
+
+=item target_field (optional)
+
+target field, defaults to the primary key of the target table
+
+=item hashref (optional)
+
+static hashref further qualifying the m2m fields
+
+=cut
+
+sub process_m2m {
+ my( $self, %opt ) = @_;
+
+ #use Data::Dumper;
+ #warn "$me process_m2m called on $self with options:\n". Dumper(%opt)
+ warn "$me process_m2m called on $self"
+ if $DEBUG;
+
+ my $self_pkey = $self->dbdef_table->primary_key;
+ my $base_field = $opt{'base_field'} || $self_pkey;
+ my $hashref = $opt{'hashref'} || {};
+ $hashref->{$base_field} = $self->$self_pkey();
+
+ my $link_table = $self->_load_table($opt{'link_table'});
+
+ my $target_table = $self->_load_table($opt{'target_table'});
+ my $target_field = $opt{'target_field'}
+ || dbdef->table($target_table)->primary_key;
+
+ if ( ref($opt{'params'}) eq 'ARRAY' ) {
+ $opt{'params'} = { map { $_=>1 } @{$opt{'params'}} };
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $del_obj (
+ grep {
+ my $targetnum = $_->$target_field();
+ ( ! $opt{'params'}->{$targetnum}
+ && ! $opt{'params'}->{"$target_field$targetnum"}
+ );
+ }
+ qsearch( $link_table, $hashref )
+ ) {
+ my $error = $del_obj->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ foreach my $add_targetnum (
+ grep { ! qsearchs( $link_table, { %$hashref, $target_field => $_ } ) }
+ map { /^($target_field)?(\d+)$/; $2; }
+ grep { /^($target_field)?(\d+)$/ }
+ grep { $opt{'params'}->{$_} }
+ keys %{ $opt{'params'} }
+ ) {
+
+ my $add_obj = "FS::$link_table"->new( {
+ %$hashref,
+ $target_field => $add_targetnum,
+ });
+ my $error = $add_obj->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+sub _load_table {
+ my( $self, $table ) = @_;
+ eval "use FS::$table";
+ die $@ if $@;
+ $table;
+}
+
+#=item target_table
+#
+#=cut
+#
+#sub target_table {
+# my $self = shift;
+# my $target_table = $self->_target_table;
+# eval "use FS::$target_table";
+# die $@ if $@;
+# $target_table;
+#}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/m2name_Common.pm b/FS/FS/m2name_Common.pm
new file mode 100644
index 0000000..e9dcee9
--- /dev/null
+++ b/FS/FS/m2name_Common.pm
@@ -0,0 +1,177 @@
+package FS::m2name_Common;
+
+use strict;
+use vars qw( $DEBUG $me );
+use Carp;
+use FS::Schema qw( dbdef );
+use FS::Record qw( qsearchs ); #qsearch dbh );
+
+$DEBUG = 0;
+
+$me = '[FS::m2name_Common]';
+
+=head1 NAME
+
+FS::m2name_Common - Mixin class for tables with a related table listing names
+
+=head1 SYNOPSIS
+
+use FS::m2name_Common;
+
+@ISA = qw( FS::m2name_Common FS::Record );
+
+=head1 DESCRIPTION
+
+FS::m2name_Common is intended as a mixin class for classes which have a
+related table that lists names.
+
+=head1 METHODS
+
+=over 4
+
+=item process_m2name OPTION => VALUE, ...
+
+Available options:
+
+link_table (required) - Table into which the records are inserted.
+
+num_col (optional) - Column in link_table which links to the primary key of the base table. If not specified, it is assumed this has the same name.
+
+name_col (required) - Name of the column in link_table that stores the string names.
+
+names_list (required) - List reference of the possible string name values.
+
+params (required) - Hashref of keys and values, often passed as C<scalar($cgi->Vars)> from a form. Processing is controlled by the B<param_style param> option.
+
+param_style (required) - Controls processing of B<params>. I<'link_table.value checkboxes'> specifies that parameters keys are in the form C<link_table.name>, and the values are booleans controlling whether or not to insert that name into link_table. I<'name_colN values'> specifies that parameter keys are in the form C<name_col0>, C<name_col1>, and so on, and values are the names inserted into link_table.
+
+args_callback (optional) - Coderef. Optional callback that may modify arguments for insert and replace operations. The callback is run with four arguments: the first argument is object being inserted or replaced (i.e. FS::I<link_table> object), the second argument is a prefix to use when retreiving CGI arguements from the params hashref, the third argument is the params hashref (see above), and the final argument is a listref of arguments that the callback should modify.
+
+=cut
+
+sub process_m2name {
+ my( $self, %opt ) = @_;
+
+ my $self_pkey = $self->dbdef_table->primary_key;
+ my $link_sourcekey = $opt{'num_col'} || $self_pkey;
+
+ my $link_table = $self->_load_table($opt{'link_table'});
+
+ my $link_static = $opt{'link_static'} || {};
+
+ warn "$me processing m2name from ". $self->table. ".$link_sourcekey".
+ " to $link_table\n"
+ if $DEBUG;
+
+ foreach my $name ( @{ $opt{'names_list'} } ) {
+
+ warn "$me checking $name\n" if $DEBUG;
+
+ my $name_col = $opt{'name_col'};
+
+ my $obj = qsearchs( $link_table, {
+ $link_sourcekey => $self->$self_pkey(),
+ $name_col => $name,
+ %$link_static,
+ });
+
+ my $param = '';
+ my $prefix = '';
+ if ( $opt{'param_style'} =~ /link_table.value\s+checkboxes/i ) {
+ #access_group.html style
+ my $paramname = "$link_table.$name";
+ $param = $opt{'params'}->{$paramname};
+ } elsif ( $opt{'param_style'} =~ /name_colN values/i ) {
+ #part_event.html style
+
+ my @fields = grep { /^$name_col\d+$/ }
+ keys %{$opt{'params'}};
+
+ $param = grep { $name eq $opt{'params'}->{$_} } @fields;
+
+ if ( $param ) {
+ #this depends on their being one condition per name...
+ #which needs to be enforced on the edit page...
+ #(it is on part_event and access_group edit)
+ foreach my $field (@fields) {
+ $prefix = "$field." if $name eq $opt{'params'}->{$field};
+ }
+ warn "$me prefix $prefix\n" if $DEBUG;
+ }
+ } else { #??
+ croak "unknown param_style: ". $opt{'param_style'};
+ $param = $opt{'params'}->{$name};
+ }
+
+ if ( $obj && ! $param ) {
+
+ warn "$me deleting $name\n" if $DEBUG;
+
+ my $d_obj = $obj; #need to save $obj for below.
+ my $error = $d_obj->delete;
+ die "error deleting $d_obj for $link_table.$name: $error" if $error;
+
+ } elsif ( $param && ! $obj ) {
+
+ warn "$me inserting $name\n" if $DEBUG;
+
+ #ok to clobber it now (but bad form nonetheless?)
+ #$obj = new "FS::$link_table" ( {
+ $obj = "FS::$link_table"->new( {
+ $link_sourcekey => $self->$self_pkey(),
+ $opt{'name_col'} => $name,
+ %$link_static,
+ });
+
+ my @args = ();
+ if ( $opt{'args_callback'} ) { #edit/process/part_event.html
+ &{ $opt{'args_callback'} }( $obj,
+ $prefix,
+ $opt{'params'},
+ \@args
+ );
+ }
+
+ my $error = $obj->insert( @args );
+ die "error inserting $obj for $link_table.$name: $error" if $error;
+
+ } elsif ( $param && $obj && $opt{'args_callback'} ) {
+
+ my @args = ();
+ if ( $opt{'args_callback'} ) { #edit/process/part_event.html
+ &{ $opt{'args_callback'} }( $obj,
+ $prefix,
+ $opt{'params'},
+ \@args
+ );
+ }
+
+ my $error = $obj->replace( $obj, @args );
+ die "error replacing $obj for $link_table.$name: $error" if $error;
+
+ }
+
+ }
+
+ '';
+}
+
+sub _load_table {
+ my( $self, $table ) = @_;
+ eval "use FS::$table";
+ die $@ if $@;
+ $table;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/msgcat.pm b/FS/FS/msgcat.pm
new file mode 100644
index 0000000..d1224f3
--- /dev/null
+++ b/FS/FS/msgcat.pm
@@ -0,0 +1,166 @@
+package FS::msgcat;
+
+use strict;
+use vars qw( @ISA );
+use Exporter;
+use FS::UID;
+use FS::Record; # qw( qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::msgcat - Object methods for message catalog entries
+
+=head1 SYNOPSIS
+
+ use FS::msgcat;
+
+ $record = new FS::msgcat \%hash;
+ $record = new FS::msgcat { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::msgcat object represents an message catalog entry. FS::msgcat inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item msgnum - primary key
+
+=item msgcode - Error code
+
+=item locale - Locale
+
+=item msg - Message
+
+=back
+
+If you just want to B<use> message catalogs, see L<FS::Msgcat>.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new message catalog entry. To add the message catalog entry to the
+database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'msgcat'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid message catalog entry. If there
+is an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('msgnum')
+ || $self->ut_text('msgcode')
+ || $self->ut_text('msg')
+ ;
+ return $error if $error;
+
+ $self->locale =~ /^([\w\@]+)$/ or return "illegal locale: ". $self->locale;
+ $self->locale($1);
+
+ $self->SUPER::check
+}
+
+
+sub _upgrade_data { #class method
+ my( $class, %opts) = @_;
+
+ eval "use FS::Setup;";
+ die $@ if $@;
+
+ #"repopulate_msgcat", false laziness w/FS::Setup::populate_msgcat
+
+ my %messages = FS::Setup::msgcat_messages();
+
+ foreach my $msgcode ( keys %messages ) {
+ foreach my $locale ( keys %{$messages{$msgcode}} ) {
+ my %msgcat = (
+ 'msgcode' => $msgcode,
+ 'locale' => $locale,
+ #'msg' => $messages{$msgcode}{$locale},
+ );
+ #my $msgcat = qsearchs('msgcat', \%msgcat);
+ my $msgcat = FS::Record::qsearchs('msgcat', \%msgcat); #wtf?
+ next if $msgcat;
+
+ $msgcat = new FS::msgcat( {
+ %msgcat,
+ 'msg' => $messages{$msgcode}{$locale},
+ } );
+ my $error = $msgcat->insert;
+ die $error if $error;
+ }
+ }
+
+}
+
+=back
+
+=head1 BUGS
+
+i18n/l10n, eek
+
+=head1 SEE ALSO
+
+L<FS::Msgcat>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/nas.pm b/FS/FS/nas.pm
new file mode 100644
index 0000000..97b0ea1
--- /dev/null
+++ b/FS/FS/nas.pm
@@ -0,0 +1,150 @@
+package FS::nas;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw(qsearchs); #qsearch);
+use FS::UID qw( dbh );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::nas - Object methods for nas records
+
+=head1 SYNOPSIS
+
+ use FS::nas;
+
+ $record = new FS::nas \%hash;
+ $record = new FS::nas {
+ 'nasnum' => 1,
+ 'nasip' => '10.4.20.23',
+ 'nasfqdn' => 'box1.brc.nv.us.example.net',
+ };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->heartbeat($timestamp);
+
+=head1 DESCRIPTION
+
+An FS::nas object represents an Network Access Server on your network, such as
+a terminal server or equivalent. FS::nas inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item nasnum - primary key
+
+=item nas - NAS name
+
+=item nasip - NAS ip address
+
+=item nasfqdn - NAS fully-qualified domain name
+
+=item last - timestamp indicating the last instant the NAS was in a known
+ state (used by the session monitoring).
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new NAS. To add the NAS to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'nas'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid NAS. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('nasnum')
+ || $self->ut_text('nas')
+ || $self->ut_ip('nasip')
+ || $self->ut_domain('nasfqdn')
+ || $self->ut_numbern('last')
+ || $self->SUPER::check
+ ;
+}
+
+=item heartbeat TIMESTAMP
+
+Updates the timestamp for this nas
+
+=cut
+
+sub heartbeat {
+ my($self, $timestamp) = @_;
+ my $dbh = dbh;
+ my $sth =
+ $dbh->prepare("UPDATE nas SET last = ? WHERE nasnum = ? AND last < ?");
+ $sth->execute($timestamp, $self->nasnum, $timestamp) or die $sth->errstr;
+ $self->last($timestamp);
+}
+
+=back
+
+=head1 BUGS
+
+heartbeat method uses SQL directly and doesn't update history tables.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/option_Common.pm b/FS/FS/option_Common.pm
new file mode 100644
index 0000000..a786ae3
--- /dev/null
+++ b/FS/FS/option_Common.pm
@@ -0,0 +1,345 @@
+package FS::option_Common;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Scalar::Util qw( blessed );
+use FS::Record qw( qsearch qsearchs dbh );
+
+@ISA = qw( FS::Record );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::option_Common - Base class for option sub-classes
+
+=head1 SYNOPSIS
+
+use FS::option_Common;
+
+@ISA = qw( FS::option_Common );
+
+#optional for non-standard names
+sub _option_table { 'table_name'; } #defaults to ${table}_option
+sub _option_namecol { 'column_name'; } #defaults to optionname
+sub _option_valuecol { 'column_name'; } #defaults to optionvalue
+
+=head1 DESCRIPTION
+
+FS::option_Common is intended as a base class for classes which have a
+simple one-to-many class associated with them, used to store a hash-like data
+structure of keys and values.
+
+=head1 METHODS
+
+=over 4
+
+=item insert [ HASHREF | OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+If a list or hash reference of options is supplied, option records are also
+created.
+
+=cut
+
+#false laziness w/queue.pm
+sub insert {
+ my $self = shift;
+ my $options =
+ ( ref($_[0]) eq 'HASH' )
+ ? shift
+ : { @_ };
+ warn "FS::option_Common::insert called on $self with options ".
+ join(', ', map "$_ => ".$options->{$_}, keys %$options)
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkey = $self->primary_key;
+ my $option_table = $self->option_table;
+
+ my $namecol = $self->_option_namecol;
+ my $valuecol = $self->_option_valuecol;
+
+ foreach my $optionname ( keys %{$options} ) {
+
+ my $optionvalue = $options->{$optionname};
+
+ my $href = {
+ $pkey => $self->get($pkey),
+ $namecol => $optionname,
+ $valuecol => ( ref($optionvalue) || $optionvalue ),
+ };
+
+ #my $option_record = eval "new FS::$option_table \$href";
+ #if ( $@ ) {
+ # $dbh->rollback if $oldAutoCommit;
+ # return $@;
+ #}
+ my $option_record = "FS::$option_table"->new($href);
+
+ my @args = ();
+ push @args, $optionvalue if ref($optionvalue); #only hashes supported so far
+
+ $error = $option_record->insert(@args);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Delete this record from the database. Any associated option records are also
+deleted.
+
+=cut
+
+#foreign keys would make this much less tedious... grr dumb mysql
+sub delete {
+ my $self = shift;
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkey = $self->primary_key;
+ #my $option_table = $self->option_table;
+
+ foreach my $obj ( $self->option_objects ) {
+ my $error = $obj->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item replace [ OLD_RECORD ] [ HASHREF | OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+If a list or hash reference of options is supplied, option records are created
+or modified.
+
+=cut
+
+sub replace {
+ my $self = shift;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $self->replace_old;
+
+ my $options =
+ ( ref($_[0]) eq 'HASH' )
+ ? shift
+ : { @_ };
+
+ warn "FS::option_Common::replace called on $self with options ".
+ join(', ', map "$_ => ". $options->{$_}, keys %$options)
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::replace($old);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkey = $self->primary_key;
+ my $option_table = $self->option_table;
+
+ my $namecol = $self->_option_namecol;
+ my $valuecol = $self->_option_valuecol;
+
+ foreach my $optionname ( keys %{$options} ) {
+
+ warn "FS::option_Common::replace: inserting or replacing option: $optionname"
+ if $DEBUG > 1;
+
+ my $oldopt = qsearchs( $option_table, {
+ $pkey => $self->get($pkey),
+ $namecol => $optionname,
+ } );
+
+ my $optionvalue = $options->{$optionname};
+
+ my %oldhash = $oldopt ? $oldopt->hash : ();
+
+ my $href = {
+ %oldhash,
+ $pkey => $self->get($pkey),
+ $namecol => $optionname,
+ $valuecol => ( ref($optionvalue) || $optionvalue ),
+ };
+
+ #my $newopt = eval "new FS::$option_table \$href";
+ #if ( $@ ) {
+ # $dbh->rollback if $oldAutoCommit;
+ # return $@;
+ #}
+ my $newopt = "FS::$option_table"->new($href);
+
+ my $opt_pkey = $newopt->primary_key;
+
+ $newopt->$opt_pkey($oldopt->$opt_pkey) if $oldopt;
+
+ my @args = ();
+ push @args, $optionvalue if ref($optionvalue); #only hashes supported so far
+
+ warn "FS::option_Common::replace: ".
+ ( $oldopt ? "$newopt -> replace($oldopt)" : "$newopt -> insert" )
+ if $DEBUG > 2;
+ my $error = $oldopt ? $newopt->replace($oldopt, @args)
+ : $newopt->insert( @args);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ #remove extraneous old options
+ foreach my $opt (
+ grep { !exists $options->{$_->$namecol()} } $old->option_objects
+ ) {
+ my $error = $opt->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item option_objects
+
+Returns all options as FS::I<tablename>_option objects.
+
+=cut
+
+sub option_objects {
+ my $self = shift;
+ my $pkey = $self->primary_key;
+ my $option_table = $self->option_table;
+ qsearch($option_table, { $pkey => $self->get($pkey) } );
+}
+
+=item options
+
+Returns a list of option names and values suitable for assigning to a hash.
+
+=cut
+
+sub options {
+ my $self = shift;
+ my $namecol = $self->_option_namecol;
+ my $valuecol = $self->_option_valuecol;
+ map { $_->$namecol() => $_->$valuecol() } $self->option_objects;
+}
+
+=item option OPTIONNAME
+
+Returns the option value for the given name, or the empty string.
+
+=cut
+
+sub option {
+ my $self = shift;
+ my $pkey = $self->primary_key;
+ my $option_table = $self->option_table;
+ my $namecol = $self->_option_namecol;
+ my $valuecol = $self->_option_valuecol;
+ my $hashref = {
+ $pkey => $self->get($pkey),
+ $namecol => shift,
+ };
+ warn "$self -> option: searching for ".
+ join(' / ', map { "$_ => ". $hashref->{$_} } keys %$hashref )
+ if $DEBUG;
+ my $obj = qsearchs($option_table, $hashref);
+ $obj ? $obj->$valuecol() : '';
+}
+
+
+sub option_table {
+ my $self = shift;
+ my $option_table = $self->_option_table;
+ eval "use FS::$option_table";
+ die $@ if $@;
+ $option_table;
+}
+
+#defaults
+sub _option_table { shift->table .'_option'; }
+sub _option_namecol { 'optionname'; }
+sub _option_valuecol { 'optionvalue'; }
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_bill_event.pm b/FS/FS/part_bill_event.pm
new file mode 100644
index 0000000..4e7aa52
--- /dev/null
+++ b/FS/FS/part_bill_event.pm
@@ -0,0 +1,368 @@
+package FS::part_bill_event;
+
+use strict;
+use vars qw( @ISA $DEBUG @EXPORT_OK );
+use Carp qw(cluck confess);
+use FS::Record qw( dbh qsearch qsearchs );
+use FS::Conf;
+use FS::cust_main;
+use FS::cust_bill;
+
+@ISA = qw( FS::Record );
+@EXPORT_OK = qw( due_events );
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::part_bill_event - Object methods for part_bill_event records
+
+=head1 SYNOPSIS
+
+ use FS::part_bill_event;
+
+ $record = new FS::part_bill_event \%hash;
+ $record = new FS::part_bill_event { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->do_event( $direct_object );
+
+ @events = due_events ( { 'record' => $event_triggering_record,
+ 'payby' => $payby,
+ 'event_time => $_date,
+ 'extra_sql => $extra } );
+
+=head1 DESCRIPTION
+
+An FS::part_bill_event object represents a deprecated, old-style invoice event
+definition - a callback which is triggered when an invoice is a certain amount
+of time overdue. FS::part_bill_event inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item eventpart - primary key
+
+=item payby - CARD, DCRD, CHEK, DCHK, LECB, BILL, or COMP
+
+=item event - event name
+
+=item eventcode - event action
+
+=item seconds - how long after the invoice date events of this type are triggered
+
+=item weight - ordering for events with identical seconds
+
+=item plan - eventcode plan
+
+=item plandata - additional plan data
+
+=item reason - an associated reason for this event to fire
+
+=item disabled - Disabled flag, empty or `Y'
+
+=back
+
+=head1 NOTE
+
+Old-style invoice events are only useful for legacy migrations - if you are
+looking for current events see L<FS::part_event>.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new invoice event definition. To add the invoice event definition to
+the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_bill_event'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid invoice event definition. If
+there is an error, returns the error, otherwise returns false. Called by the
+insert and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ $self->weight(0) unless $self->weight;
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('safe-part_bill_event') ) {
+ my $error = $self->ut_anything('eventcode');
+ return $error if $error;
+
+ my $c = $self->eventcode;
+
+ #yay, these regexen will go away with the event refactor
+
+ $c =~ /^\s*\$cust_main\->(suspend|cancel|invoicing_list_addpost|bill|collect)\(\);\s*("";)?\s*$/
+
+ or $c =~ /^\s*\$cust_bill\->(comp|realtime_(card|ach|lec)|batch_card|send)\((%options)*\);\s*$/
+
+ or $c =~ /^\s*\$cust_bill\->send(_if_newest)?\(\'[\w\-\s]+\'\s*(,\s*(\d+|\[\s*\d+(,\s*\d+)*\s*\])\s*,\s*'[\w\@\.\-\+]*'\s*)?\);\s*$/
+
+# or $c =~ /^\s*\$cust_main\->apply_payments; \$cust_main->apply_credits; "";\s*$/
+ or $c =~ /^\s*\$cust_main\->apply_payments_and_credits; "";\s*$/
+
+ or $c =~ /^\s*\$cust_main\->charge\( \s*\d*\.?\d*\s*,\s*\'[\w \!\@\#\$\%\&\(\)\-\+\;\:\"\,\.\?\/]*\'\s*\);\s*$/
+
+ or $c =~ /^\s*\$cust_main\->suspend_(if|unless)_pkgpart\([\d\,\s]*\);\s*$/
+
+ or $c =~ /^\s*\$cust_bill\->cust_suspend_if_balance_over\([\d\.\s]*\);\s*$/
+
+ or do {
+ #log
+ return "illegal eventcode: $c";
+ };
+
+ }
+
+ my $error = $self->ut_numbern('eventpart')
+ || $self->ut_enum('payby', [qw( CARD DCLN DCRD CHEK DCHK LECB BILL COMP )] )
+ || $self->ut_text('event')
+ || $self->ut_anything('eventcode')
+ || $self->ut_number('seconds')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ || $self->ut_number('weight')
+ || $self->ut_textn('plan')
+ || $self->ut_anything('plandata')
+ || $self->ut_numbern('reason')
+ ;
+ #|| $self->ut_snumber('seconds')
+ return $error if $error;
+
+ #quelle kludge
+ if ( $self->plandata =~ /^(agent_)?templatename\s+(.*)$/m ) {
+ my $name= $2;
+
+ foreach my $file (qw( template
+ latex latexnotes latexreturnaddress latexfooter
+ latexsmallfooter
+ html htmlnotes htmlreturnaddress htmlfooter
+ ))
+ {
+ unless ( $conf->exists("invoice_${file}_$name") ) {
+ $conf->set(
+ "invoice_${file}_$name" =>
+ join("\n", $conf->config("invoice_$file") )
+ );
+ }
+ }
+ }
+
+ if ($self->reason){
+ my $reasonr = qsearchs('reason', {'reasonnum' => $self->reason});
+ return "Unknown reason" unless $reasonr;
+ }
+
+ $self->SUPER::check;
+}
+
+=item templatename
+
+Returns the alternate invoice template name, if any, or false if there is
+no alternate template for this invoice event.
+
+=cut
+
+sub templatename {
+ my $self = shift;
+ if ( $self->plan =~ /^send_(alternate|agent)$/
+ && $self->plandata =~ /^(agent_)?templatename (.*)$/m
+ )
+ {
+ $2;
+ } else {
+ '';
+ }
+}
+
+=item due_events
+
+Returns the list of events due, if any, or false if there is none.
+Requires record and payby, but event_time and extra_sql are optional.
+
+=cut
+
+sub due_events {
+ my ($record, $payby, $event_time, $extra_sql) = @_;
+
+ #cluck "DEPRECATED: FS::part_bill_event::due_events called on $record";
+ confess "DEPRECATED: FS::part_bill_event::due_events called on $record";
+
+ my $interval = 0;
+ if ($record->_date){
+ $event_time = time unless $event_time;
+ $interval = $event_time - $record->_date;
+ }
+ sort { $a->seconds <=> $b->seconds
+ || $a->weight <=> $b->weight
+ || $a->eventpart <=> $b->eventpart }
+ grep { ref($record) ne 'FS::cust_bill' || $_->eventcode !~ /honor_dundate/
+ || $event_time > $record->cust_main->dundate
+ }
+ grep { $_->seconds <= ( $interval )
+ && ! qsearch( 'cust_bill_event', {
+ 'invnum' => $record->get($record->dbdef_table->primary_key),
+ 'eventpart' => $_->eventpart,
+ 'status' => 'done',
+ } )
+ }
+ qsearch( {
+ 'table' => 'part_bill_event',
+ 'hashref' => { 'payby' => $payby,
+ 'disabled' => '', },
+ 'extra_sql' => $extra_sql,
+ } );
+
+
+}
+
+=item do_event
+
+Performs the event and returns any errors that occur.
+Requires a record on which to perform the event.
+Should only be performed inside a transaction.
+
+=cut
+
+sub do_event {
+ my ($self, $object, %options) = @_;
+
+ #cluck "DEPRECATED: FS::part_bill_event::do_event called on $self";
+ confess "DEPRECATED: FS::part_bill_event::do_event called on $self";
+
+ warn " calling event (". $self->eventcode. ") for " . $object->table . " " ,
+ $object->get($object->dbdef_table->primary_key) . "\n" if $DEBUG > 1;
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+
+ # for "callback" -- heh
+ my $cust_main = $object->cust_main;
+ my $cust_bill;
+ if ($object->table eq 'cust_bill'){
+ $cust_bill = $object;
+ }
+ my $cust_pay_batch;
+ if ($object->table eq 'cust_pay_batch'){
+ $cust_pay_batch = $object;
+ }
+
+ my $error;
+ {
+ local $SIG{__DIE__}; # don't want Mason __DIE__ handler active
+ $error = eval $self->eventcode;
+ }
+
+ my $status = '';
+ my $statustext = '';
+ if ( $@ ) {
+ $status = 'failed';
+ $statustext = $@;
+ } elsif ( $error ) {
+ $status = 'done';
+ $statustext = $error;
+ } else {
+ $status = 'done';
+ }
+
+ #add cust_bill_event
+ my $cust_bill_event = new FS::cust_bill_event {
+# 'invnum' => $object->get($object->dbdef_table->primary_key),
+ 'invnum' => $object->invnum,
+ 'eventpart' => $self->eventpart,
+ '_date' => time,
+ 'status' => $status,
+ 'statustext' => $statustext,
+ };
+ $error = $cust_bill_event->insert;
+ if ( $error ) {
+ my $e = 'WARNING: Event run but database not updated - '.
+ 'error inserting cust_bill_event, invnum #'. $object->invnum .
+ ', eventpart '. $self->eventpart.": $error";
+ warn $e;
+ return $e;
+ }
+ '';
+}
+
+=item reasontext
+
+Returns the text of any reason associated with this event.
+
+=cut
+
+sub reasontext {
+ my $self = shift;
+ my $r = qsearchs('reason', { 'reasonnum' => $self->reason });
+ if ($r){
+ $r->reason;
+ }else{
+ '';
+ }
+}
+
+=back
+
+=head1 BUGS
+
+The whole "eventcode" idea is bunk. This should be refactored with subclasses
+like part_pkg/ and part_export/
+
+=head1 SEE ALSO
+
+L<FS::cust_bill>, L<FS::cust_bill_event>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_event.pm b/FS/FS/part_event.pm
new file mode 100644
index 0000000..6f2c536
--- /dev/null
+++ b/FS/FS/part_event.pm
@@ -0,0 +1,442 @@
+package FS::part_event;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Carp qw(confess);
+use FS::Record qw( dbh qsearch qsearchs );
+use FS::option_Common;
+use FS::m2name_Common;
+use FS::Conf;
+use FS::part_event_option;
+use FS::part_event_condition;
+use FS::cust_event;
+use FS::agent;
+
+@ISA = qw( FS::m2name_Common FS::option_Common ); # FS::Record );
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::part_event - Object methods for part_event records
+
+=head1 SYNOPSIS
+
+ use FS::part_event;
+
+ $record = new FS::part_event \%hash;
+ $record = new FS::part_event { 'column' => 'value' };
+
+ $error = $record->insert( { 'option' => 'value' } );
+ $error = $record->insert( \%options );
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->do_event( $direct_object );
+
+=head1 DESCRIPTION
+
+An FS::part_event object represents an event definition - a billing, collection
+or other callback which is triggered when certain customer, invoice, package or
+other conditions are met. FS::part_event inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item eventpart - primary key
+
+=item agentnum - Optional agentnum (see L<FS::agent>)
+
+=item event - event name
+
+=item eventtable - table name against which this event is triggered; currently "cust_bill" (the traditional invoice events), "cust_main" (customer events) or "cust_pkg (package events)
+
+=item check_freq - how often events of this type are checked; currently "1d" (daily) and "1m" (monthly) are recognized. Note that the apprioriate freeside-daily and/or freeside-monthly cron job needs to be in place.
+
+=item weight - ordering for events
+
+=item action - event action (like part_bill_event.plan - eventcode plan)
+
+=item disabled - Disabled flag, empty or `Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new invoice event definition. To add the invoice event definition to
+the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_event'; }
+
+=item insert [ HASHREF ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+If a list or hash reference of options is supplied, part_export_option records
+are created (see L<FS::part_event_option>).
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD [ HASHREF | OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+If a list or hash reference of options is supplied, part_event_option
+records are created or modified (see L<FS::part_event_option>).
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid invoice event definition. If
+there is an error, returns the error, otherwise returns false. Called by the
+insert and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ $self->weight(0) unless $self->weight;
+
+ my $error =
+ $self->ut_numbern('eventpart')
+ || $self->ut_text('event')
+ || $self->ut_enum('eventtable', [ 'cust_bill', 'cust_main', 'cust_pkg' ] )
+ || $self->ut_enum('check_freq', [ '1d', '1m' ])
+ || $self->ut_number('weight')
+ || $self->ut_alpha('action')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ || $self->ut_agentnum_acl('agentnum', 'Edit global billing events')
+ ;
+ return $error if $error;
+
+ #XXX check action to make sure a module exists?
+ # well it'll die in _rebless...
+
+ $self->SUPER::check;
+}
+
+=item _rebless
+
+Reblesses the object into the FS::part_event::Action::ACTION class, where
+ACTION is the object's I<action> field.
+
+=cut
+
+sub _rebless {
+ my $self = shift;
+ my $action = $self->action or return $self;
+ #my $class = ref($self). "::$action";
+ my $class = "FS::part_event::Action::$action";
+ eval "use $class";
+ die $@ if $@;
+ bless($self, $class); # unless $@;
+ $self;
+}
+
+=item part_event_condition
+
+Returns the conditions associated with this event, as FS::part_event_condition
+objects (see L<FS::part_event_condition>)
+
+=cut
+
+sub part_event_condition {
+ my $self = shift;
+ qsearch( 'part_event_condition', { 'eventpart' => $self->eventpart } );
+}
+
+=item new_cust_event OBJECT
+
+Creates a new customer event (see L<FS::cust_event>) for the provided object.
+
+=cut
+
+sub new_cust_event {
+ my( $self, $object ) = @_;
+
+ confess "**** $object is not a ". $self->eventtable
+ if ref($object) ne "FS::". $self->eventtable;
+
+ my $pkey = $object->primary_key;
+
+ new FS::cust_event {
+ 'eventpart' => $self->eventpart,
+ 'tablenum' => $object->$pkey(),
+ '_date' => time, #i think we always want the real "now" here.
+ 'status' => 'new',
+ };
+}
+
+#surely this doesn't work
+sub reasontext { confess "part_event->reasontext deprecated"; }
+#=item reasontext
+#
+#Returns the text of any reason associated with this event.
+#
+#=cut
+#
+#sub reasontext {
+# my $self = shift;
+# my $r = qsearchs('reason', { 'reasonnum' => $self->reason });
+# if ($r){
+# $r->reason;
+# }else{
+# '';
+# }
+#}
+
+=item agent
+
+Returns the associated agent for this event, if any, as an FS::agent object.
+
+=cut
+
+sub agent {
+ my $self = shift;
+ qsearchs('agent', { 'agentnum' => $self->agentnum } );
+}
+
+=item templatename
+
+Returns the alternate invoice template name, if any, or false if there is
+no alternate template for this event.
+
+=cut
+
+sub templatename {
+
+ my $self = shift;
+ if ( $self->action =~ /^cust_bill_send_(alternate|agent)$/
+ && ( $self->option('agent_templatename')
+ || $self->option('templatename') )
+ )
+ {
+ $self->option('agent_templatename')
+ || $self->option('templatename');
+
+ } else {
+ '';
+ }
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item eventtable_labels
+
+Returns a hash reference of labels for eventtable values,
+i.e. 'cust_main'=>'Customer'
+
+=cut
+
+sub eventtable_labels {
+ #my $class = shift;
+
+ tie my %hash, 'Tie::IxHash',
+ 'cust_pkg' => 'Package',
+ 'cust_bill' => 'Invoice',
+ 'cust_main' => 'Customer',
+ 'cust_pay_batch' => 'Batch payment',
+ ;
+
+ \%hash
+}
+
+=item eventtable_pkey_sql
+
+Returns a hash reference of full SQL primary key names for eventtable values,
+i.e. 'cust_main'=>'cust_main.custnum'
+
+=cut
+
+sub eventtable_pkey_sql {
+ my $class = shift;
+
+ my $hashref = $class->eventtable_pkey;
+
+ my %hash = map { $_ => "$_.". $hashref->{$_} } keys %$hashref;
+
+ \%hash;
+}
+
+=item eventtable_pkey
+
+Returns a hash reference of full SQL primary key names for eventtable values,
+i.e. 'cust_main'=>'custnum'
+
+=cut
+
+sub eventtable_pkey {
+ #my $class = shift;
+
+ {
+ 'cust_main' => 'custnum',
+ 'cust_bill' => 'invnum',
+ 'cust_pkg' => 'pkgnum',
+ 'cust_pay_batch' => 'paybatchnum',
+ };
+}
+
+=item eventtables
+
+Returns a list of eventtable values (default ordering; suited for display).
+
+=cut
+
+sub eventtables {
+ my $class = shift;
+ my $eventtables = $class->eventtable_labels;
+ keys %$eventtables;
+}
+
+=item eventtables_runorder
+
+Returns a list of eventtable values (run order).
+
+=cut
+
+sub eventtables_runorder {
+ shift->eventtables; #same for now
+}
+
+=item check_freq_labels
+
+Returns a hash reference of labels for check_freq values,
+i.e. '1d'=>'daily'
+
+=cut
+
+sub check_freq_labels {
+ #my $class = shift;
+
+ #Tie::IxHash??
+ {
+ '1d' => 'daily',
+ '1m' => 'monthly',
+ };
+}
+
+=item actions [ EVENTTABLE ]
+
+Return information about the available actions. If an eventtable is specified,
+only return information about actions available for that eventtable.
+
+Information is returned as key-value pairs. Keys are event names. Values are
+hashrefs with the following keys:
+
+=over 4
+
+=item description
+
+=item eventtable_hashref
+
+=item option_fields
+
+=item default_weight
+
+=item deprecated
+
+=back
+
+See L<FS::part_event::Action> for more information.
+
+=cut
+
+#false laziness w/part_event_condition.pm
+#some false laziness w/part_export & part_pkg
+my %actions;
+foreach my $INC ( @INC ) {
+ foreach my $file ( glob("$INC/FS/part_event/Action/*.pm") ) {
+ warn "attempting to load Action from $file\n" if $DEBUG;
+ $file =~ /\/(\w+)\.pm$/ or do {
+ warn "unrecognized file in $INC/FS/part_event/Action/: $file\n";
+ next;
+ };
+ my $mod = $1;
+ eval "use FS::part_event::Action::$mod;";
+ if ( $@ ) {
+ die "error using FS::part_event::Action::$mod (skipping): $@\n" if $@;
+ #warn "error using FS::part_event::Action::$mod (skipping): $@\n" if $@;
+ #next;
+ }
+ $actions{$mod} = {
+ ( map { $_ => "FS::part_event::Action::$mod"->$_() }
+ qw( description eventtable_hashref default_weight deprecated )
+ #option_fields_hashref
+ ),
+ 'option_fields' => [ "FS::part_event::Action::$mod"->option_fields() ],
+ };
+ }
+}
+
+sub actions {
+ my( $class, $eventtable ) = @_;
+ (
+ map { $_ => $actions{$_} }
+ sort { $actions{$a}->{'default_weight'}<=>$actions{$b}->{'default_weight'} }
+ $class->all_actions( $eventtable )
+ );
+
+}
+
+=item all_actions [ EVENTTABLE ]
+
+Returns a list of just the action names
+
+=cut
+
+sub all_actions {
+ my ( $class, $eventtable ) = @_;
+
+ grep { !$eventtable || $actions{$_}->{'eventtable_hashref'}{$eventtable} }
+ keys %actions
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::part_event_option>, L<FS::part_event_condition>, L<FS::cust_main>,
+L<FS::cust_pkg>, L<FS::cust_bill>, L<FS::cust_bill_event>, L<FS::Record>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_event/Action.pm b/FS/FS/part_event/Action.pm
new file mode 100644
index 0000000..57239d7
--- /dev/null
+++ b/FS/FS/part_event/Action.pm
@@ -0,0 +1,227 @@
+package FS::part_event::Action;
+
+use strict;
+use base qw( FS::part_event );
+use Tie::IxHash;
+
+=head1 NAME
+
+FS::part_event::Action - Base class for event actions
+
+=head1 SYNOPSIS
+
+package FS::part_event::Action::myaction;
+
+use base FS::part_event::Action;
+
+=head1 DESCRIPTION
+
+FS::part_event::Action is a base class for event action classes.
+
+=head1 METHODS
+
+These methods are implemented in each action class.
+
+=over 4
+
+=item description
+
+Action classes must define a description method. This method should return a
+scalar description of the action.
+
+=item eventtable_hashref
+
+Action classes must define a eventtable_hashref method if they can only be
+triggered against some kinds of tables. This method should return a hash
+reference of eventtables (values set true indicate the action can be performed):
+
+ sub eventtable_hashref {
+ { 'cust_main' => 1,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 0,
+ 'cust_pay_batch' => 0,
+ };
+ }
+
+=cut
+
+#fallback
+sub eventtable_hashref {
+ { 'cust_main' => 1,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 1,
+ 'cust_pay_batch' => 1,
+ };
+}
+
+=item option_fields
+
+Action classes may define an option_fields method to indicate that they
+accept one or more options.
+
+This method should return a list of option names and option descriptions.
+Each option description can be a scalar description, for simple options, or a
+hashref with the following values:
+
+=over 4
+
+=item label - Description
+
+=item type - Currently text, money, checkbox, checkbox-multiple, select, select-agent, select-pkg_class, select-part_referral, select-table, fixed, hidden, (others can be implemented as httemplate/elements/tr-TYPE.html mason components). Defaults to text.
+
+=item size - Size for text fields
+
+=item options - For checkbox-multiple and select, a list reference of available option values.
+
+=item option_labels - For select, a hash reference of availble option values and labels.
+
+=item value - for checkbox, fixed, hidden
+
+=item table - for select-table
+
+=item name_col - for select-table
+
+=item NOTE: See httemplate/elements/select-table.html for a full list of the optinal options for the select-table type
+
+=back
+
+NOTE: A database connection is B<not> yet available when this subroutine is
+executed.
+
+Example:
+
+ sub option_fields {
+ (
+ 'field' => 'description',
+
+ 'another_field' => { 'label'=>'Amount', 'type'=>'money', },
+
+ 'third_field' => { 'label' => 'Types',
+ 'type' => 'select',
+ 'options' => [ 'h', 's' ],
+ 'option_labels' => { 'h' => 'Happy',
+ 's' => 'Sad',
+ },
+ );
+ }
+
+=cut
+
+#fallback
+sub option_fields {
+ ();
+}
+
+=item default_weight
+
+Action classes may define a default weighting. Weights control execution order
+relative to other actions (that are triggered at the same time).
+
+=cut
+
+#fallback
+sub default_weight {
+ 100;
+}
+
+=item deprecated
+
+Action classes may define a deprecated method that returns true, indicating
+that this action is deprecated.
+
+=cut
+
+#default
+sub deprecated {
+ 0;
+}
+
+=item do_action CUSTOMER_EVENT_OBJECT
+
+Action classes must define an action method. This method is triggered if
+all conditions have been met.
+
+The object which triggered the event (an FS::cust_main, FS::cust_bill or
+FS::cust_pkg object) is passed as an argument.
+
+To retreive option values, call the option method on the desired option, i.e.:
+
+ my( $self, $cust_object ) = @_;
+ $value_of_field = $self->option('field');
+
+To indicate sucessful completion, simply return. Optionally, you can return a
+string of information status information about the sucessful completion, or
+simply return the empty string.
+
+To indicate a failure and that this event should retry, die with the desired
+error message.
+
+=back
+
+=head1 BASE METHODS
+
+These methods are defined in the base class for use in action classes.
+
+=over 4
+
+=item cust_main CUST_OBJECT
+
+Return the customer object (see L<FS::cust_main>) associated with the provided
+object (the object itself if it is already a customer object).
+
+=cut
+
+sub cust_main {
+ my( $self, $cust_object ) = @_;
+
+ $cust_object->isa('FS::cust_main') ? $cust_object : $cust_object->cust_main;
+
+}
+
+=item option_label OPTIONNAME
+
+Returns the label for the specified option name.
+
+=cut
+
+sub option_label {
+ my( $self, $optionname ) = @_;
+
+ my %option_fields = $self->option_fields;
+
+ ref( $option_fields{$optionname} )
+ ? $option_fields{$optionname}->{'label'}
+ : $option_fields{$optionname}
+ or $optionname;
+}
+
+=item option_fields_hashref
+
+Returns the option fields as an (ordered) hash reference.
+
+=cut
+
+sub option_fields_hashref {
+ my $self = shift;
+ tie my %hash, 'Tie::IxHash', $self->option_fields;
+ \%hash;
+}
+
+=item option_fields_listref
+
+Returns just the option field names as a list reference.
+
+=cut
+
+sub option_fields_listref {
+ my $self = shift;
+ my $hashref = $self->option_fields_hashref;
+ [ keys %$hashref ];
+}
+
+=back
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_event/Action/addpost.pm b/FS/FS/part_event/Action/addpost.pm
new file mode 100644
index 0000000..f92e72e
--- /dev/null
+++ b/FS/FS/part_event/Action/addpost.pm
@@ -0,0 +1,20 @@
+package FS::part_event::Action::addpost;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Add postal invoicing'; }
+
+sub default_weight { 20; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ $cust_main->invoicing_list_addpost();
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/apply.pm b/FS/FS/part_event/Action/apply.pm
new file mode 100644
index 0000000..823d1e0
--- /dev/null
+++ b/FS/FS/part_event/Action/apply.pm
@@ -0,0 +1,24 @@
+package FS::part_event::Action::apply;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ 'Apply unapplied payments and credits';
+}
+
+sub deprecated { 1; }
+
+sub default_weight { 70; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ $cust_main->apply_payments_and_credits;
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/bill.pm b/FS/FS/part_event/Action/bill.pm
new file mode 100644
index 0000000..b96614d
--- /dev/null
+++ b/FS/FS/part_event/Action/bill.pm
@@ -0,0 +1,26 @@
+package FS::part_event::Action::bill;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ #'Generate invoices (normally only used with a <i>Late Fee</i> event)';
+ 'Generate invoices (normally only used with a Late Fee event)';
+}
+
+sub deprecated { 1; }
+
+sub default_weight { 60; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ my $error = $cust_main->bill;
+ die $error if $error;
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cancel.pm b/FS/FS/part_event/Action/cancel.pm
new file mode 100644
index 0000000..b9d6d29
--- /dev/null
+++ b/FS/FS/part_event/Action/cancel.pm
@@ -0,0 +1,30 @@
+package FS::part_event::Action::cancel;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Cancel'; }
+
+sub option_fields {
+ (
+ 'reasonnum' => { 'label' => 'Reason',
+ 'type' => 'select-reason',
+ 'reason_class' => 'C',
+ },
+ );
+}
+
+sub default_weight { 20; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ my $error = $cust_main->cancel( 'reason' => $self->option('reasonnum') );
+ die $error if $error;
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/collect.pm b/FS/FS/part_event/Action/collect.pm
new file mode 100644
index 0000000..9881440
--- /dev/null
+++ b/FS/FS/part_event/Action/collect.pm
@@ -0,0 +1,26 @@
+package FS::part_event::Action::collect;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ #'Collect on invoices (normally only used with a <i>Late Fee</i> and <i>Generate Invoice</i> events)';
+ 'Collect on invoices (normally only used with a Late Fee and Generate Invoice events)';
+}
+
+sub deprecated { 1; }
+
+sub default_weight { 80; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ my $error = $cust_main->collect;
+ die $error if $error;
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_batch.pm b/FS/FS/part_event/Action/cust_bill_batch.pm
new file mode 100644
index 0000000..50c306a
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_batch.pm
@@ -0,0 +1,25 @@
+package FS::part_event::Action::cust_bill_batch;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Add card or check to a pending batch'; }
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub default_weight { 40; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->batch_card; # ( %options ); #XXX options??
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_comp.pm b/FS/FS/part_event/Action/cust_bill_comp.pm
new file mode 100644
index 0000000..76fd274
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_comp.pm
@@ -0,0 +1,28 @@
+package FS::part_event::Action::cust_bill_comp;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Pay invoice with a complimentary "payment"'; }
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub default_weight { 30; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ my $error = $cust_bill->comp;
+ die $error if $error;
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_fee_percent.pm b/FS/FS/part_event/Action/cust_bill_fee_percent.pm
new file mode 100644
index 0000000..570fd63
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_fee_percent.pm
@@ -0,0 +1,36 @@
+package FS::part_event::Action::cust_bill_fee_percent;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Late fee (percentage of invoice)'; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub option_fields {
+ (
+ 'percent' => { label=>'Percent', size=>2, },
+ 'reason' => 'Reason',
+ );
+}
+
+sub default_weight { 10; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ my $error = $cust_main->charge(
+ sprintf('%.2f', $cust_bill->owed * $self->option('percent') / 100 ),
+ $self->option('reason')
+ );
+ die $error if $error;
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_realtime_card.pm b/FS/FS/part_event/Action/cust_bill_realtime_card.pm
new file mode 100644
index 0000000..c1fdba9
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_realtime_card.pm
@@ -0,0 +1,28 @@
+package FS::part_event::Action::cust_bill_realtime_card;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ #'Run card with a <a href="http://420.am/business-onlinepayment/">Business::OnlinePayment</a> realtime gateway';
+ 'Run card with a Business::OnlinePayment realtime gateway';
+}
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub default_weight { 30; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->realtime_card;
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_realtime_check.pm b/FS/FS/part_event/Action/cust_bill_realtime_check.pm
new file mode 100644
index 0000000..11b13a9
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_realtime_check.pm
@@ -0,0 +1,28 @@
+package FS::part_event::Action::cust_bill_realtime_check;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ #'Run check with a <a href="http://420.am/business-onlinepayment/">Business::OnlinePayment</a> realtime gateway';
+ 'Run check with a Business::OnlinePayment realtime gateway';
+}
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub default_weight { 30; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->realtime_ach;
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_realtime_lec.pm b/FS/FS/part_event/Action/cust_bill_realtime_lec.pm
new file mode 100644
index 0000000..cd03ddc
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_realtime_lec.pm
@@ -0,0 +1,28 @@
+package FS::part_event::Action::cust_bill_realtime_lec;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ #'Run phone bill ("LEC") billing with a <a href="http://420.am/business-onlinepayment/">Business::OnlinePayment</a> realtime gateway';
+ 'Run phone bill ("LEC") billing with a Business::OnlinePayment realtime gateway';
+}
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub default_weight { 30; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->realtime_lec;
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_send.pm b/FS/FS/part_event/Action/cust_bill_send.pm
new file mode 100644
index 0000000..663caf1
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_send.pm
@@ -0,0 +1,23 @@
+package FS::part_event::Action::cust_bill_send;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Send invoice (email/print/fax)'; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub default_weight { 50; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->send;
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_send_agent.pm b/FS/FS/part_event/Action/cust_bill_send_agent.pm
new file mode 100644
index 0000000..670a32c
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_send_agent.pm
@@ -0,0 +1,42 @@
+package FS::part_event::Action::cust_bill_send_agent;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ 'Send invoice (email/print/fax) with alternate template, for specific agents';
+}
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub option_fields {
+ (
+ 'agentnum' => { label => 'Only for agent(s)',
+ type => 'select-agent',
+ multiple => 1
+ },
+ 'agent_templatename' => { label => 'Template',
+ type => 'select-invoice_template',
+ },
+ 'agent_invoice_from' => 'Invoice email From: address',
+ );
+}
+
+sub default_weight { 50; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->send(
+ $self->option('agent_templatename'),
+ [ split(/\s*,\s*/, $self->option('agentnum') ) ],
+ $self->option('agent_invoice_from'),
+ );
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_send_alternate.pm b/FS/FS/part_event/Action/cust_bill_send_alternate.pm
new file mode 100644
index 0000000..cfd9264
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_send_alternate.pm
@@ -0,0 +1,31 @@
+package FS::part_event::Action::cust_bill_send_alternate;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Send invoice (email/print/fax) with alternate template'; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub option_fields {
+ (
+ 'templatename' => { label => 'Template',
+ type => 'select-invoice_template',
+ },
+ );
+}
+
+sub default_weight { 50; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->send( $self->option('templatename') );
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_send_csv_ftp.pm b/FS/FS/part_event/Action/cust_bill_send_csv_ftp.pm
new file mode 100644
index 0000000..bf47268
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_send_csv_ftp.pm
@@ -0,0 +1,50 @@
+package FS::part_event::Action::cust_bill_send_csv_ftp;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Upload CSV invoice data to an FTP server'; }
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub option_fields {
+ (
+ 'ftpformat' => { label => 'Format',
+ type =>'select',
+ options => ['default', 'billco'],
+ option_labels => { 'default' => 'Default',
+ 'billco' => 'Billco',
+ },
+ },
+ 'ftpserver' => 'FTP server',
+ 'ftpusername' => 'FTP username',
+ 'ftppassword' => 'FTP password',
+ 'ftpdir' => 'FTP directory',
+ );
+}
+
+sub default_weight { 50; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->send_csv(
+ 'protocol' => 'ftp',
+ 'server' => $self->option('ftpserver'),
+ 'username' => $self->option('ftpusername'),
+ 'password' => $self->option('ftppassword'),
+ 'dir' => $self->option('ftpdir'),
+ 'format' => $self->option('ftpformat'),
+ );
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_send_if_newest.pm b/FS/FS/part_event/Action/cust_bill_send_if_newest.pm
new file mode 100644
index 0000000..083da8b
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_send_if_newest.pm
@@ -0,0 +1,38 @@
+package FS::part_event::Action::cust_bill_send_if_newest;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ 'Send invoice (email/print/fax) with alternate template, if it is still the newest invoice (useful for late notices - set to 31 days or later)';
+}
+
+# XXX is this handled better by something against customers??
+#sub deprecated {
+# 1;
+#}
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub option_fields {
+ (
+ 'if_newest_templatename' => { label => 'Template',
+ type => 'select-invoice_template',
+ },
+ );
+}
+
+sub default_weight { 50; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->send( $self->option('templatename') );
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_spool_csv.pm b/FS/FS/part_event/Action/cust_bill_spool_csv.pm
new file mode 100644
index 0000000..f20ee46
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_spool_csv.pm
@@ -0,0 +1,58 @@
+package FS::part_event::Action::cust_bill_spool_csv;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Spool CSV invoice data'; }
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub option_fields {
+ (
+ 'spoolformat' => { label => 'Format',
+ type => 'select',
+ options => ['default', 'billco'],
+ option_labels => { 'default' => 'Default',
+ 'billco' => 'Billco',
+ },
+ },
+ 'spooldest' => { label => 'For destination',
+ type => 'select',
+ options => [ '', qw( POST EMAIL FAX ) ],
+ option_labels => { '' => '(all)',
+ 'POST' => 'Postal Mail',
+ 'EMAIL' => 'Email',
+ 'FAX' => 'Fax',
+ },
+ },
+ 'spoolbalanceover' => { label =>
+ 'If balance (this invoice and previous) over',
+ type => 'money',
+ },
+ 'spoolagent_spools' => { label => 'Individual per-agent spools',
+ type => 'checkbox',
+ },
+ );
+}
+
+sub default_weight { 50; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ $cust_bill->spool_csv(
+ 'format' => $self->option('spoolformat'),
+ 'dest' => $self->option('spooldest'),
+ 'balanceover' => $self->option('spoolbalanceover'),
+ 'agent_spools' => $self->option('spoolagent_spools'),
+ );
+}
+
+1;
diff --git a/FS/FS/part_event/Action/cust_bill_suspend_if_balance.pm b/FS/FS/part_event/Action/cust_bill_suspend_if_balance.pm
new file mode 100644
index 0000000..13188ab
--- /dev/null
+++ b/FS/FS/part_event/Action/cust_bill_suspend_if_balance.pm
@@ -0,0 +1,42 @@
+package FS::part_event::Action::cust_bill_suspend_if_balance;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Suspend if balance (this invoice and previous) over'; }
+
+sub deprecated { 1; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1 };
+}
+
+sub option_fields {
+ (
+ 'balanceover' => { label=>'Balance over', type=>'money', }, # size=>7 },
+ 'reasonnum' => { 'label' => 'Reason',
+ 'type' => 'select-reason',
+ 'reason_class' => 'S',
+ },
+ );
+}
+
+sub default_weight { 10; }
+
+sub do_action {
+ my( $self, $cust_bill ) = @_;
+
+ #my $cust_main = $self->cust_main($cust_bill);
+ my $cust_main = $cust_bill->cust_main;
+
+ my @err = $cust_bill->cust_suspend_if_balance_over(
+ $self->option('balanceover'),
+ 'reason' => $self->option('reasonnum'),
+ );
+
+ die join(' / ', @err) if scalar(@err);
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/fee.pm b/FS/FS/part_event/Action/fee.pm
new file mode 100644
index 0000000..3cf50fb
--- /dev/null
+++ b/FS/FS/part_event/Action/fee.pm
@@ -0,0 +1,29 @@
+package FS::part_event::Action::fee;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Late fee (flat)'; }
+
+sub option_fields {
+ (
+ 'charge' => { label=>'Amount', type=>'money', }, # size=>7, },
+ 'reason' => 'Reason',
+ );
+}
+
+sub default_weight { 10; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ my $error = $cust_main->charge( $self->option('charge'), $self->option('reason') );
+
+ die $error if $error;
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/pkg_referral_credit.pm b/FS/FS/part_event/Action/pkg_referral_credit.pm
new file mode 100644
index 0000000..98d9820
--- /dev/null
+++ b/FS/FS/part_event/Action/pkg_referral_credit.pm
@@ -0,0 +1,60 @@
+package FS::part_event::Action::pkg_referral_credit;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Credit the referring customer a specific amount'; }
+
+sub eventtable_hashref {
+ { 'cust_pkg' => 1 };
+}
+
+sub option_fields {
+ (
+ 'reasonnum' => { 'label' => 'Credit reason',
+ 'type' => 'select-reason',
+ 'reason_class' => 'R',
+ },
+ 'amount' => { 'label' => 'Credit amount',
+ 'type' => 'money',
+ },
+ );
+
+}
+
+#a little false laziness w/pkg_referral_credit_pkg
+sub do_action {
+ my( $self, $cust_pkg ) = @_;
+
+ my $cust_main = $self->cust_main($cust_pkg);
+
+# my $part_pkg = $cust_pkg->part_pkg;
+
+ return 'No referring customer' unless $cust_main->referral_custnum;
+
+ my $referring_cust_main = $cust_main->referring_cust_main;
+ return 'Referring customer is cancelled'
+ if $referring_cust_main->status eq 'cancelled';
+
+ my $amount = $self->_calc_referral_credit($cust_pkg);
+ my $reasonnum = $self->option('reasonnum');
+
+ my $error = $referring_cust_main->credit(
+ $amount,
+ \$reasonnum,
+ 'addlinfo' =>
+ 'for customer #'. $cust_main->display_custnum. ': '.$cust_main->name,
+ );
+ die "Error crediting customer ". $cust_main->referral_custnum.
+ " for referral: $error"
+ if $error;
+
+}
+
+sub _calc_referral_credit {
+ my( $self, $cust_pkg ) = @_;
+
+ $self->option('amount');
+}
+
+1;
diff --git a/FS/FS/part_event/Action/pkg_referral_credit_pkg.pm b/FS/FS/part_event/Action/pkg_referral_credit_pkg.pm
new file mode 100644
index 0000000..08cf9a8
--- /dev/null
+++ b/FS/FS/part_event/Action/pkg_referral_credit_pkg.pm
@@ -0,0 +1,57 @@
+package FS::part_event::Action::pkg_referral_credit_pkg;
+
+use strict;
+use base qw( FS::part_event::Action::pkg_referral_credit );
+
+sub description { 'Credit the referring customer an amount based on the referred package'; }
+
+#sub eventtable_hashref {
+# { 'cust_pkg' => 1 };
+#}
+
+sub option_fields {
+ (
+ 'reasonnum' => { 'label' => 'Credit reason',
+ 'type' => 'select-reason',
+ 'reason_class' => 'R',
+ },
+ 'percent' => { 'label' => 'Percent',
+ 'type' => 'input-percentage',
+ 'default' => '100',
+ },
+ 'what' => { 'label' => 'Of',
+ 'type' => 'select',
+ #also add some way to specify in the package def, no?
+ 'options' => [ qw( base_recur_permonth ) ],
+ 'labels' => { 'base_recur_permonth' => 'Base monthly fee', },
+ },
+ );
+
+}
+
+sub _calc_referral_credit {
+ my( $self, $cust_pkg ) = @_;
+
+ my $cust_main = $self->cust_main($cust_pkg);
+
+ my $part_pkg = $cust_pkg->part_pkg;
+
+ my $what = $self->option('what');
+
+ if ( $what eq 'base_recur_permonth' ) { #huh. yuck.
+ if ( $part_pkg->freq !~ /^\d+$/ ) {
+ die 'WARNING: Not crediting customer '. $cust_main->referral_custnum.
+ ' for package '. $cust_pkg->pkgnum.
+ ' ( customer '. $cust_pkg->custnum. ')'.
+ ' - Referral credits not (yet) available for '.
+ ' packages with '. $part_pkg->freq_pretty. ' frequency';
+ }
+ }
+
+ my $percent = $self->option('percent');
+
+ sprintf('%.2f', $part_pkg->$what($cust_pkg) * $percent / 100 );
+
+}
+
+1;
diff --git a/FS/FS/part_event/Action/suspend.pm b/FS/FS/part_event/Action/suspend.pm
new file mode 100644
index 0000000..c77728e
--- /dev/null
+++ b/FS/FS/part_event/Action/suspend.pm
@@ -0,0 +1,32 @@
+package FS::part_event::Action::suspend;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Suspend'; }
+
+sub option_fields {
+ (
+ 'reasonnum' => { 'label' => 'Reason',
+ 'type' => 'select-reason',
+ 'reason_class' => 'S',
+ },
+ );
+}
+
+sub default_weight { 10; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ my @err = $cust_main->suspend( 'reason' => $self->option('reasonnum') );
+
+ die join(' / ', @err) if scalar(@err);
+
+ '';
+
+}
+
+1;
diff --git a/FS/FS/part_event/Action/suspend_if_pkgpart.pm b/FS/FS/part_event/Action/suspend_if_pkgpart.pm
new file mode 100644
index 0000000..6f2007c
--- /dev/null
+++ b/FS/FS/part_event/Action/suspend_if_pkgpart.pm
@@ -0,0 +1,40 @@
+package FS::part_event::Action::suspend_if_pkgpart;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Suspend packages'; }
+
+#i should be deprecated in favor of using the if_pkgpart condition
+
+sub option_fields {
+ (
+ 'if_pkgpart' => { 'label' => 'Suspend packages:',
+ 'type' => 'select-part_pkg',
+ 'multiple' => 1,
+ },
+ 'reasonnum' => { 'label' => 'Reason',
+ 'type' => 'select-reason',
+ 'reason_class' => 'S',
+ },
+ );
+}
+
+sub default_weight { 10; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ my @err = $cust_main->suspend_if_pkgpart( {
+ 'pkgparts' => [ split(/\s*,\s*/, $self->option('if_pkgpart') ) ],
+ 'reason' => $self->option('reasonnum'),
+ } );
+
+ die join(' / ', @err) if scalar(@err);
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Action/suspend_unless_pkgpart.pm b/FS/FS/part_event/Action/suspend_unless_pkgpart.pm
new file mode 100644
index 0000000..efc7a2d
--- /dev/null
+++ b/FS/FS/part_event/Action/suspend_unless_pkgpart.pm
@@ -0,0 +1,40 @@
+package FS::part_event::Action::suspend_unless_pkgpart;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description { 'Suspend packages except'; }
+
+#i should be deprecated in favor of using the unless_pkgpart condition
+
+sub option_fields {
+ (
+ 'unless_pkgpart' => { 'label' => 'Suspend packages except:',
+ 'type' => 'select-part_pkg',
+ 'multiple' => 1,
+ },
+ 'reasonnum' => { 'label' => 'Reason',
+ 'type' => 'select-reason',
+ 'reason_class' => 'S',
+ },
+ );
+}
+
+sub default_weight { 10; }
+
+sub do_action {
+ my( $self, $cust_object ) = @_;
+
+ my $cust_main = $self->cust_main($cust_object);
+
+ my @err = $cust_main->suspend_unless_pkgpart( {
+ 'pkgparts' => [ split(/\s*,\s*/, $self->option('unless_pkgpart') ) ],
+ 'reason' => $self->option('reasonnum'),
+ } );
+
+ die join(' / ', @err) if scalar(@err);
+
+ '';
+}
+
+1;
diff --git a/FS/FS/part_event/Condition.pm b/FS/FS/part_event/Condition.pm
new file mode 100644
index 0000000..544b560
--- /dev/null
+++ b/FS/FS/part_event/Condition.pm
@@ -0,0 +1,446 @@
+package FS::part_event::Condition;
+
+use strict;
+use base qw( FS::part_event_condition );
+use Time::Local qw(timelocal_nocheck);
+use FS::UID qw( driver_name );
+
+=head1 NAME
+
+FS::part_event::Condition - Base class for event conditions
+
+=head1 SYNOPSIS
+
+package FS::part_event::Condition::mycondition;
+
+use base FS::part_event::Condition;
+
+=head1 DESCRIPTION
+
+FS::part_event::Condition is a base class for event conditions classes.
+
+=head1 METHODS
+
+These methods are implemented in each condition class.
+
+=over 4
+
+=item description
+
+Condition classes must define a description method. This method should return
+a scalar description of the condition.
+
+=item eventtable_hashref
+
+Condition classes must define an eventtable_hashref method if they can only be
+tested against some kinds of tables. This method should return a hash reference
+of eventtables (values set true indicate the condition can be tested):
+
+ sub eventtable_hashref {
+ { 'cust_main' => 1,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 0,
+ 'cust_pay_batch' => 0,
+ };
+ }
+
+=cut
+
+#fallback
+sub eventtable_hashref {
+ { 'cust_main' => 1,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 1,
+ 'cust_pay_batch' => 1,
+ };
+}
+
+=item option_fields
+
+Condition classes may define an option_fields method to indicate that they
+accept one or more options.
+
+This method should return a list of option names and option descriptions.
+Each option description can be a scalar description, for simple options, or a
+hashref with the following values:
+
+=over 4
+
+=item label - Description
+
+=item type - Currently text, money, checkbox, checkbox-multiple, select, select-agent, select-pkg_class, select-part_referral, select-table, fixed, hidden, (others can be implemented as httemplate/elements/tr-TYPE.html mason components). Defaults to text.
+
+=item options - For checkbox-multiple and select, a list reference of available option values.
+
+=item option_labels - For checkbox-multiple (and select?), a hash reference of availble option values and labels.
+
+=item value - for checkbox, fixed, hidden (also a default for text, money, more?)
+
+=item table - for select-table
+
+=item name_col - for select-table
+
+=item NOTE: See httemplate/elements/select-table.html for a full list of the optinal options for the select-table type
+
+=back
+
+NOTE: A database connection is B<not> yet available when this subroutine is
+executed.
+
+Example:
+
+ sub option_fields {
+ (
+ 'field' => 'description',
+
+ 'another_field' => { 'label'=>'Amount', 'type'=>'money', },
+
+ 'third_field' => { 'label' => 'Types',
+ 'type' => 'checkbox-multiple',
+ 'options' => [ 'h', 's' ],
+ 'option_labels' => { 'h' => 'Happy',
+ 's' => 'Sad',
+ },
+ );
+ }
+
+=cut
+
+#fallback
+sub option_fields {
+ ();
+}
+
+=item condition CUSTOMER_EVENT_OBJECT
+
+Condition classes must define a condition method. This method is evaluated
+to determine if the condition has been met. The object which triggered the
+event (an FS::cust_main, FS::cust_bill or FS::cust_pkg object) is passed as
+the first argument. Additional arguments are list of key-value pairs.
+
+To retreive option values, call the option method on the desired option, i.e.:
+
+ my( $self, $cust_object, %opts ) = @_;
+ $value_of_field = $self->option('field');
+
+Available additional arguments:
+
+ $time = $opt{'time'}; #use this instead of time or $^T
+
+ $cust_event = $opt{'cust_event'}; #to retreive the cust_event object being tested
+
+Return a true value if the condition has been met, and a false value if it has
+not.
+
+=item condition_sql EVENTTABLE
+
+Condition classes may optionally define a condition_sql method. This B<class>
+method should return an SQL fragment that tests for this condition. The
+fragment is evaluated and a true value of this expression indicates that the
+condition has been met. The event table (cust_main, cust_bill or cust_pkg) is
+passed as an argument.
+
+This method is used for optimizing event queries. You may want to add indices
+for any columns referenced. It is acceptable to return an SQL fragment which
+partially tests the condition; doing so will still reduce the number of
+records which much be returned and tested with the B<condition> method.
+
+=cut
+
+# fallback.
+sub condition_sql {
+ my( $class, $eventtable ) = @_;
+ #...
+ 'true';
+}
+
+=item disabled
+
+Condition classes may optionally define a disabled method. Returning a true
+value disbles the condition entirely.
+
+=cut
+
+sub disabled {
+ 0;
+}
+
+=item implicit_flag
+
+This is used internally by the I<once> and I<balance> conditions. You probably
+do B<not> want to define this method for new custom conditions, unless you're
+sure you want B<every> new action to start with your condition.
+
+Condition classes may define an implicit_flag method that returns true to
+indicate that all new events should start with this condition. (Currently,
+condition classes which do so should be applicable to all kinds of
+I<eventtable>s.) The numeric value of the flag also defines the ordering of
+implicit conditions.
+
+=cut
+
+#fallback
+sub implicit_flag { 0; }
+
+=item remove_warning
+
+Again, used internally by the I<once> and I<balance> conditions; probably not
+a good idea for new custom conditions.
+
+Condition classes may define a remove_warning method containing a string
+warning message to enable a confirmation dialog triggered when the condition
+is removed from an event.
+
+=cut
+
+#fallback
+sub remove_warning { ''; }
+
+=item order_sql
+
+This is used internally by the I<balance_age> and I<cust_bill_age> conditions
+to declare ordering; probably not of general use for new custom conditions.
+
+=item order_sql_weight
+
+In conjunction with order_sql, this defines which order the ordering fragments
+supplied by different B<order_sql> should be used.
+
+=cut
+
+sub order_sql_weight { ''; }
+
+=back
+
+=head1 BASE METHODS
+
+These methods are defined in the base class for use in condition classes.
+
+=over 4
+
+=item cust_main CUST_OBJECT
+
+Return the customer object (see L<FS::cust_main>) associated with the provided
+object (the object itself if it is already a customer object).
+
+=cut
+
+sub cust_main {
+ my( $self, $cust_object ) = @_;
+
+ $cust_object->isa('FS::cust_main') ? $cust_object : $cust_object->cust_main;
+
+}
+
+=item option_label OPTIONNAME
+
+Returns the label for the specified option name.
+
+=cut
+
+sub option_label {
+ my( $self, $optionname ) = @_;
+
+ my %option_fields = $self->option_fields;
+
+ ref( $option_fields{$optionname} )
+ ? $option_fields{$optionname}->{'label'}
+ : $option_fields{$optionname}
+ or $optionname;
+}
+
+=back
+
+=item option_age_from OPTION FROM_TIMESTAMP
+
+Retreives a condition option, parses it from a frequency (such as "1d", "1w" or
+"12m"), and subtracts that interval from the supplied timestamp. It is
+primarily intended for use in B<condition>.
+
+=cut
+
+sub option_age_from {
+ my( $self, $option, $time ) = @_;
+ my $age = $self->option($option);
+ $age = '0m' unless length($age);
+
+ my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($time) )[0,1,2,3,4,5];
+
+ if ( $age =~ /^(\d+)m$/i ) {
+ $mon -= $1;
+ until ( $mon >= 0 ) { $mon += 12; $year--; }
+ } elsif ( $age =~ /^(\d+)y$/i ) {
+ $year -= $1;
+ } elsif ( $age =~ /^(\d+)w$/i ) {
+ $mday -= $1 * 7;
+ } elsif ( $age =~ /^(\d+)d$/i ) {
+ $mday -= $1;
+ } elsif ( $age =~ /^(\d+)h$/i ) {
+ $hour -= $hour;
+ } else {
+ die "unparsable age: $age";
+ }
+
+ timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year);
+
+}
+
+=item condition_sql_option OPTION
+
+This is a class method that returns an SQL fragment for retreiving a condition
+option. It is primarily intended for use in B<condition_sql>.
+
+=cut
+
+sub condition_sql_option {
+ my( $class, $option ) = @_;
+
+ ( my $condname = $class ) =~ s/^.*:://;
+
+ "( SELECT optionvalue FROM part_event_condition_option
+ WHERE part_event_condition_option.eventconditionnum =
+ cond_$condname.eventconditionnum
+ AND part_event_condition_option.optionname = '$option'
+ )";
+}
+
+=item condition_sql_option_age_from OPTION FROM_TIMESTAMP
+
+This is a class method that returns an SQL fragment that will retreive a
+condition option, parse it from a frequency (such as "1d", "1w" or "12m"),
+and subtract that interval from the supplied timestamp. It is primarily
+intended for use in B<condition_sql>.
+
+=cut
+
+sub condition_sql_option_age_from {
+ my( $class, $option, $from ) = @_;
+
+ my $value = $class->condition_sql_option($option);
+
+# my $str2time = str2time_sql;
+
+ if ( driver_name =~ /^Pg/i ) {
+
+ #can we do better with Pg now that we have $from? yes we can, bob
+ "( $from - EXTRACT( EPOCH FROM REPLACE( $value, 'm', 'mon')::interval ) )";
+
+ } elsif ( driver_name =~ /^mysql/i ) {
+
+ #hmm... is there a way we can save $value? we're just an expression, hmm
+ #we might be able to do something like "AS ${option}_value" except we get
+ #used in more complicated expressions and we need some sort of unique
+ #identifer passed down too... yow
+
+ "CASE WHEN $value IS NULL OR $value = ''
+ THEN $from
+ WHEN $value LIKE '%m'
+ THEN UNIX_TIMESTAMP(
+ FROM_UNIXTIME($from) - INTERVAL REPLACE( $value, 'm', '' ) MONTH
+ )
+ WHEN $value LIKE '%y'
+ THEN UNIX_TIMESTAMP(
+ FROM_UNIXTIME($from) - INTERVAL REPLACE( $value, 'y', '' ) YEAR
+ )
+ WHEN $value LIKE '%w'
+ THEN UNIX_TIMESTAMP(
+ FROM_UNIXTIME($from) - INTERVAL REPLACE( $value, 'w', '' ) WEEK
+ )
+ WHEN $value LIKE '%d'
+ THEN UNIX_TIMESTAMP(
+ FROM_UNIXTIME($from) - INTERVAL REPLACE( $value, 'd', '' ) DAY
+ )
+ WHEN $value LIKE '%h'
+ THEN UNIX_TIMESTAMP(
+ FROM_UNIXTIME($from) - INTERVAL REPLACE( $value, 'h', '' ) HOUR
+ )
+ END
+ "
+ } else {
+
+ die "FATAL: don't know how to subtract frequencies from dates for ".
+ driver_name. " databases";
+
+ }
+
+}
+
+=item condition_sql_option_age OPTION
+
+This is a class method that returns an SQL fragment for retreiving a condition
+option, and additionaly parsing it from a frequency (such as "1d", "1w" or
+"12m") into an approximate number of seconds.
+
+Note that since months vary in length, the results of this method should B<not>
+be used in computations (use condition_sql_option_age_from for that). They are
+useful for for ordering and comparison to other ages.
+
+This method is primarily intended for use in B<order_sql>.
+
+=cut
+
+sub condition_sql_option_age {
+ my( $class, $option ) = @_;
+ $class->age2seconds_sql( $class->condition_sql_option($option) );
+}
+
+=item age2seconds_sql
+
+Class method returns an SQL fragment for parsing an arbitrary frequeny (such
+as "1d", "1w", "12m", "2y" or "12h") into an approximate number of seconds.
+
+Approximate meaning: months are considered to be 30 days, years to be
+365.25 days. Otherwise the numbers of seconds returned is exact.
+
+=cut
+
+sub age2seconds_sql {
+ my( $class, $value ) = @_;
+
+ if ( driver_name =~ /^Pg/i ) {
+
+ "EXTRACT( EPOCH FROM REPLACE( $value, 'm', 'mon')::interval )";
+
+ } elsif ( driver_name =~ /^mysql/i ) {
+
+ #hmm... is there a way we can save $value? we're just an expression, hmm
+ #we might be able to do something like "AS ${option}_age" except we get
+ #used in more complicated expressions and we need some sort of unique
+ #identifer passed down too... yow
+ # 2592000 = 30d "1 month"
+ # 31557600 = 365.25d "1 year"
+
+ "CASE WHEN $value IS NULL OR $value = ''
+ THEN 0
+ WHEN $value LIKE '%m'
+ THEN REPLACE( $value, 'm', '' ) * 2592000
+ WHEN $value LIKE '%y'
+ THEN REPLACE( $value, 'y', '' ) * 31557600
+ WHEN $value LIKE '%w'
+ THEN REPLACE( $value, 'w', '' ) * 604800
+ WHEN $value LIKE '%d'
+ THEN REPLACE( $value, 'd', '' ) * 86400
+ WHEN $value LIKE '%h'
+ THEN REPLACE( $value, 'h', '' ) * 3600
+ END
+ "
+ } else {
+
+ die "FATAL: don't know how to approximate frequencies for ". driver_name.
+ " databases";
+
+ }
+
+}
+
+=head1 NEW CONDITION CLASSES
+
+A module should be added in FS/FS/part_event/Condition/ which implements the
+methods desribed above in L</METHODS>. An example may be found in the
+eg/part_event-Condition-template.pm file.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/part_event/Condition/agent.pm b/FS/FS/part_event/Condition/agent.pm
new file mode 100644
index 0000000..da428c1
--- /dev/null
+++ b/FS/FS/part_event/Condition/agent.pm
@@ -0,0 +1,37 @@
+package FS::part_event::Condition::agent;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+# see the FS::part_event::Condition manpage for full documentation on each
+# of the required and optional methods.
+
+sub description {
+ 'Agent';
+}
+
+sub option_fields {
+ (
+ 'agentnum' => { label=>'Agent', type=>'select-agent', },
+ );
+}
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $agentnum = $self->option('agentnum');
+
+ $cust_main->agentnum == $agentnum;
+
+}
+
+#sub condition_sql {
+# my( $self, $table ) = @_;
+#
+# 'true';
+#}
+
+1;
diff --git a/FS/FS/part_event/Condition/agent_type.pm b/FS/FS/part_event/Condition/agent_type.pm
new file mode 100644
index 0000000..54c8932
--- /dev/null
+++ b/FS/FS/part_event/Condition/agent_type.pm
@@ -0,0 +1,40 @@
+package FS::part_event::Condition::agent_type;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+# see the FS::part_event::Condition manpage for full documentation on each
+# of the required and optional methods.
+
+sub description {
+ 'Agent Type';
+}
+
+sub option_fields {
+ (
+ 'typenum' => { label => 'Agent Type',
+ type => 'select-agent_type',
+ disable_empty => 1,
+ },
+ );
+}
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $typenum = $self->option('typenum');
+
+ $cust_main->agent->typenum == $typenum;
+
+}
+
+#sub condition_sql {
+# my( $self, $table ) = @_;
+#
+# 'true';
+#}
+
+1;
diff --git a/FS/FS/part_event/Condition/balance.pm b/FS/FS/part_event/Condition/balance.pm
new file mode 100644
index 0000000..65670c0
--- /dev/null
+++ b/FS/FS/part_event/Condition/balance.pm
@@ -0,0 +1,48 @@
+package FS::part_event::Condition::balance;
+
+use strict;
+use FS::cust_main;
+
+use base qw( FS::part_event::Condition );
+
+sub description { 'Customer balance'; }
+
+sub implicit_flag { 20; }
+
+sub remove_warning {
+ 'Are you sure you want to remove this condition? Doing so will allow this event to run even if the customer has no outstanding balance. Perhaps you want to reset "Balance over" to 0 instead of removing the condition entirely?'; #better error msg?
+}
+
+sub option_fields {
+ (
+ 'balance' => { 'label' => 'Balance over',
+ 'type' => 'money',
+ 'value' => '0.00', #default
+ },
+ );
+}
+
+sub condition {
+ my($self, $object) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $over = $self->option('balance');
+ $over = 0 unless length($over);
+
+ $cust_main->balance > $over;
+}
+
+sub condition_sql {
+ my( $class, $table ) = @_;
+
+ my $over = $class->condition_sql_option('balance');
+
+ my $balance_sql = FS::cust_main->balance_sql;
+
+ "$balance_sql > CAST( $over AS numeric )";
+
+}
+
+1;
+
diff --git a/FS/FS/part_event/Condition/balance_age.pm b/FS/FS/part_event/Condition/balance_age.pm
new file mode 100644
index 0000000..f1a9707
--- /dev/null
+++ b/FS/FS/part_event/Condition/balance_age.pm
@@ -0,0 +1,54 @@
+package FS::part_event::Condition::balance_age;
+
+use strict;
+use base qw( FS::part_event::Condition );
+
+sub description { 'Customer balance age'; }
+
+sub option_fields {
+ (
+ 'balance' => { 'label' => 'Balance over',
+ 'type' => 'money',
+ 'value' => '0.00', #default
+ },
+ 'age' => { 'label' => 'Age',
+ 'type' => 'freq',
+ },
+ );
+}
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $over = $self->option('balance');
+ $over = 0 unless length($over);
+
+ my $age = $self->option_age_from('age', $opt{'time'} );
+
+ $cust_main->balance_date($age) > $over;
+}
+
+sub condition_sql {
+ my( $class, $table, %opt ) = @_;
+
+ my $over = $class->condition_sql_option('balance');
+ my $age = $class->condition_sql_option_age_from('age', $opt{'time'});
+
+ my $balance_sql = FS::cust_main->balance_date_sql( $age );
+
+ "$balance_sql > CAST( $over AS numeric )";
+}
+
+sub order_sql {
+ shift->condition_sql_option_age('age');
+}
+
+use FS::UID qw( driver_name );
+
+sub order_sql_weight {
+ 10;
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/balance_under.pm b/FS/FS/part_event/Condition/balance_under.pm
new file mode 100644
index 0000000..9c71590
--- /dev/null
+++ b/FS/FS/part_event/Condition/balance_under.pm
@@ -0,0 +1,42 @@
+package FS::part_event::Condition::balance_under;
+
+use strict;
+use FS::cust_main;
+
+use base qw( FS::part_event::Condition );
+
+sub description { 'Customer balance (under)'; }
+
+sub option_fields {
+ (
+ 'balance' => { 'label' => 'Balance under (or equal to)',
+ 'type' => 'money',
+ 'value' => '0.00', #default
+ },
+ );
+}
+
+sub condition {
+ my($self, $object) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $under = $self->option('balance');
+ $under = 0 unless length($under);
+
+ $cust_main->balance <= $under;
+}
+
+sub condition_sql {
+ my( $class, $table ) = @_;
+
+ my $under = $class->condition_sql_option('balance');
+
+ my $balance_sql = FS::cust_main->balance_sql;
+
+ "$balance_sql <= CAST( $under AS numeric )";
+
+}
+
+1;
+
diff --git a/FS/FS/part_event/Condition/cust_bill_age.pm b/FS/FS/part_event/Condition/cust_bill_age.pm
new file mode 100644
index 0000000..f343673
--- /dev/null
+++ b/FS/FS/part_event/Condition/cust_bill_age.pm
@@ -0,0 +1,46 @@
+package FS::part_event::Condition::cust_bill_age;
+
+use strict;
+use base qw( FS::part_event::Condition );
+
+sub description { 'Invoice age'; }
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 0,
+ };
+}
+
+sub option_fields {
+ (
+ 'age' => { label=>'Age', type=>'freq', },
+ );
+}
+
+sub condition {
+ my( $self, $cust_bill, %opt ) = @_;
+
+ my $age = $self->option_age_from('age', $opt{'time'} );
+
+ $cust_bill->_date <= $age;
+
+}
+
+sub condition_sql {
+ my( $class, $table, %opt ) = @_;
+
+ my $age = $class->condition_sql_option_age_from('age', $opt{'time'} );
+
+ "cust_bill._date <= $age";
+}
+
+sub order_sql {
+ shift->condition_sql_option_age('age');
+}
+
+sub order_sql_weight {
+ 0;
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/cust_bill_has_service.pm b/FS/FS/part_event/Condition/cust_bill_has_service.pm
new file mode 100644
index 0000000..91d75dd
--- /dev/null
+++ b/FS/FS/part_event/Condition/cust_bill_has_service.pm
@@ -0,0 +1,54 @@
+package FS::part_event::Condition::cust_bill_has_service;
+
+use strict;
+use FS::cust_bill;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+ 'Invoice is billing for a certain service type';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 0,
+ };
+}
+
+# could not find component for path '/elements/tr-select-part_svc.html'
+# sub disabled { 1; }
+
+sub option_fields {
+ (
+ 'has_service' => { 'label' => 'Has service',
+ 'type' => 'select-part_svc',
+ },
+ );
+}
+
+sub condition {
+ #my($self, $cust_bill, %opt) = @_;
+ my($self, $cust_bill) = @_;
+
+ my $servicenum = $self->option('has_service');
+ grep { $servicenum == $_->svcpart }
+ map { $_->cust_pkg->cust_svc }
+ $cust_bill->cust_bill_pkg ;
+}
+
+sub condition_sql {
+ my( $class, $table ) = @_;
+
+ my $servicenum = $class->condition_sql_option('has_service');
+ my $sql = qq| 0 < ( SELECT COUNT(cs.svcpart)
+ FROM cust_bill_pkg cbp, cust_svc cs
+ WHERE cbp.invnum = cust_bill.invnum
+ AND cs.pkgnum = cbp.pkgnum
+ AND cs.svcpart = CAST( $servicenum AS integer )
+ )
+ |;
+ return $sql;
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/cust_bill_owed.pm b/FS/FS/part_event/Condition/cust_bill_owed.pm
new file mode 100644
index 0000000..0fd9922
--- /dev/null
+++ b/FS/FS/part_event/Condition/cust_bill_owed.pm
@@ -0,0 +1,54 @@
+package FS::part_event::Condition::cust_bill_owed;
+
+use strict;
+use FS::cust_bill;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+ 'Amount owed on specific invoice';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 0,
+ };
+}
+
+sub implicit_flag { 30; }
+
+sub remove_warning {
+ 'Are you sure you want to remove this condition? Doing so will allow this event to run even for invoices which have no outstanding balance. Perhaps you want to reset "Amount owed over" to 0 instead of removing the condition entirely?'; #better error msg?
+}
+
+sub option_fields {
+ (
+ 'owed' => { 'label' => 'Amount owed over',
+ 'type' => 'money',
+ 'value' => '0.00', #default
+ },
+ );
+}
+
+sub condition {
+ #my($self, $cust_bill, %opt) = @_;
+ my($self, $cust_bill) = @_;
+
+ my $over = $self->option('owed');
+ $over = 0 unless length($over);
+
+ $cust_bill->owed > $over;
+}
+
+sub condition_sql {
+ my( $class, $table ) = @_;
+
+ my $over = $class->condition_sql_option('owed');
+
+ my $owed_sql = FS::cust_bill->owed_sql;
+
+ "$owed_sql > CAST( $over AS numeric )";
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/cust_bill_owed_under.pm b/FS/FS/part_event/Condition/cust_bill_owed_under.pm
new file mode 100644
index 0000000..a0bf92f
--- /dev/null
+++ b/FS/FS/part_event/Condition/cust_bill_owed_under.pm
@@ -0,0 +1,49 @@
+package FS::part_event::Condition::cust_bill_owed_under;
+
+use strict;
+use FS::cust_bill;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+ 'Amount owed on specific invoice (under)';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 0,
+ };
+}
+
+sub option_fields {
+ (
+ 'owed' => { 'label' => 'Amount owed under (or equal to)',
+ 'type' => 'money',
+ 'value' => '0.00', #default
+ },
+ );
+}
+
+sub condition {
+ #my($self, $cust_bill, %opt) = @_;
+ my($self, $cust_bill) = @_;
+
+ my $under = $self->option('owed');
+ $under = 0 unless length($under);
+
+ $cust_bill->owed <= $under;
+
+}
+
+sub condition_sql {
+ my( $class, $table ) = @_;
+
+ my $under = $class->condition_sql_option('owed');
+
+ my $owed_sql = FS::cust_bill->owed_sql;
+
+ "$owed_sql <= CAST( $under AS numeric )";
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/cust_pay_batch_declined.pm b/FS/FS/part_event/Condition/cust_pay_batch_declined.pm
new file mode 100644
index 0000000..b3a8d70
--- /dev/null
+++ b/FS/FS/part_event/Condition/cust_pay_batch_declined.pm
@@ -0,0 +1,51 @@
+package FS::part_event::Condition::cust_pay_batch_declined;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+ 'Batch payment declined';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 0,
+ 'cust_pay_batch' => 1,
+ };
+}
+
+#sub option_fields {
+# (
+# 'field' => 'description',
+#
+# 'another_field' => { 'label'=>'Amount', 'type'=>'money', },
+#
+# 'third_field' => { 'label' => 'Types',
+# 'type' => 'checkbox-multiple',
+# 'options' => [ 'h', 's' ],
+# 'option_labels' => { 'h' => 'Happy',
+# 's' => 'Sad',
+# },
+# );
+#}
+
+sub condition {
+ my($self, $cust_pay_batch, %opt) = @_;
+
+ #my $cust_main = $self->cust_main($object);
+ #my $value_of_field = $self->option('field');
+ #my $time = $opt{'time'}; #use this instead of time or $^T
+
+ $cust_pay_batch->status =~ /Declined/i;
+
+}
+
+#sub condition_sql {
+# my( $class, $table ) = @_;
+# #...
+# 'true';
+#}
+
+1;
diff --git a/FS/FS/part_event/Condition/cust_payments.pm b/FS/FS/part_event/Condition/cust_payments.pm
new file mode 100644
index 0000000..41ef6c7
--- /dev/null
+++ b/FS/FS/part_event/Condition/cust_payments.pm
@@ -0,0 +1,43 @@
+package FS::part_event::Condition::cust_payments;
+
+use strict;
+use base qw( FS::part_event::Condition );
+
+sub description { 'Customer total payments'; }
+
+sub option_fields {
+ (
+ 'over' => { 'label' => 'Customer total payments at least',
+ 'type' => 'money',
+ 'value' => '0.00', #default
+ },
+ );
+}
+
+sub condition {
+ my($self, $object) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $over = $self->option('over');
+ $over = 0 unless length($over);
+
+ $cust_main->total_paid >= $over;
+
+}
+
+#XXX add for efficiency. could use cust_main::total_paid_sql
+#use FS::cust_main;
+#sub condition_sql {
+# my( $class, $table ) = @_;
+#
+# my $over = $class->condition_sql_option('balance');
+#
+# my $balance_sql = FS::cust_main->balance_sql;
+#
+# "$balance_sql > $over";
+#
+#}
+
+1;
+
diff --git a/FS/FS/part_event/Condition/cust_status.pm b/FS/FS/part_event/Condition/cust_status.pm
new file mode 100644
index 0000000..fbdff25
--- /dev/null
+++ b/FS/FS/part_event/Condition/cust_status.pm
@@ -0,0 +1,32 @@
+package FS::part_event::Condition::cust_status;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+use FS::Record qw( qsearch );
+
+sub description {
+ 'Customer Status';
+}
+
+#something like this
+sub option_fields {
+ (
+ 'status' => { 'label' => 'Customer Status',
+ 'type' => 'select-cust_main-status',
+ 'multiple' => 1,
+ },
+ );
+}
+
+sub condition {
+ my( $self, $object) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ #XXX test
+ my $hashref = $self->option('status') || {};
+ $hashref->{ $cust_main->status };
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/dundate.pm b/FS/FS/part_event/Condition/dundate.pm
new file mode 100644
index 0000000..ee2a95f
--- /dev/null
+++ b/FS/FS/part_event/Condition/dundate.pm
@@ -0,0 +1,26 @@
+package FS::part_event::Condition::dundate;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+ "Skip until customer dun date is reached";
+}
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ $cust_main->dundate <= $opt{time};
+
+}
+
+#sub condition_sql {
+# my( $self, $table ) = @_;
+#
+# 'true';
+#}
+
+1;
diff --git a/FS/FS/part_event/Condition/every.pm b/FS/FS/part_event/Condition/every.pm
new file mode 100644
index 0000000..3408b0a
--- /dev/null
+++ b/FS/FS/part_event/Condition/every.pm
@@ -0,0 +1,67 @@
+package FS::part_event::Condition::every;
+
+use strict;
+use FS::UID qw( dbh );
+use FS::Record qw( qsearch );
+use FS::cust_event;
+
+use base qw( FS::part_event::Condition );
+
+sub description { "Don't retry failures more often than specified interval"; }
+
+sub option_fields {
+ (
+ 'retry_delay' => { label=>'Retry after', type=>'freq', value=>'1d', },
+ 'max_tries' => { label=>'Maximum # of attempts', type=>'text', size=>3, },
+ );
+}
+
+my %after = (
+ 'h' => 3600,
+ 'd' => 86400,
+ 'w' => 604800,
+ 'm' => 2592000, #well, 30 days... presumably people would mostly use d or w
+ '' => 2592000,
+ 'y' => 31536000, #well, 365 days...
+);
+
+my $sql =
+ "SELECT COUNT(*) FROM cust_event WHERE eventpart = ? AND tablenum = ?";
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $obj_pkey = $object->primary_key;
+ my $tablenum = $object->$obj_pkey();
+
+ if ( $self->option('max_tries') =~ /^\s*(\d+)\s*$/ ) {
+ my $max_tries = $1;
+ my $sth = dbh->prepare($sql)
+ or die dbh->errstr. " preparing: $sql";
+ $sth->execute($self->eventpart, $tablenum)
+ or die $sth->errstr. " executing: $sql";
+ my $tries = $sth->fetchrow_arrayref->[0];
+ return 0 if $tries >= $max_tries;
+ }
+
+ my $time = $opt{'time'};
+ my $retry_delay = $self->option('retry_delay');
+ $retry_delay =~ /^(\d+)([hdwmy]?)$/
+ or die "unparsable retry_delay: $retry_delay";
+ my $date_after = $time - $1 * $after{$2};
+
+ my $sth = dbh->prepare("$sql AND date > ?") # AND status = 'failed' "
+ or die dbh->errstr. " preparing: $sql";
+ $sth->execute($self->eventpart, $tablenum, $date_after)
+ or die $sth->errstr. " executing: $sql";
+ ! $sth->fetchrow_arrayref->[0];
+
+}
+
+#sub condition_sql {
+# my( $self, $table ) = @_;
+#
+# 'true';
+#}
+
+1;
diff --git a/FS/FS/part_event/Condition/has_referral_custnum.pm b/FS/FS/part_event/Condition/has_referral_custnum.pm
new file mode 100644
index 0000000..d43d6c0
--- /dev/null
+++ b/FS/FS/part_event/Condition/has_referral_custnum.pm
@@ -0,0 +1,24 @@
+package FS::part_event::Condition::has_referral_custnum;
+
+use strict;
+use FS::cust_main;
+
+use base qw( FS::part_event::Condition );
+
+sub description { 'Customer has a referring customer'; }
+
+sub condition {
+ my($self, $object) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ $cust_main->referral_custnum;
+}
+
+sub condition_sql {
+ #my( $class, $table ) = @_;
+
+ "cust_main.referral_custnum IS NOT NULL";
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/once.pm b/FS/FS/part_event/Condition/once.pm
new file mode 100644
index 0000000..5a9161f
--- /dev/null
+++ b/FS/FS/part_event/Condition/once.pm
@@ -0,0 +1,55 @@
+package FS::part_event::Condition::once;
+
+use strict;
+use FS::Record qw( qsearch );
+use FS::part_event;
+use FS::cust_event;
+
+use base qw( FS::part_event::Condition );
+
+sub description { "Don't run this event again after it has completed sucessfully"; }
+
+sub implicit_flag { 10; }
+
+sub remove_warning {
+ 'Are you sure you want to remove this condition? Doing so will allow this event to run every time the other conditions are satisfied, even if it has already run sucessfully.'; #better error msg?
+}
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $obj_pkey = $object->primary_key;
+ my $tablenum = $object->$obj_pkey();
+
+ my @existing = qsearch( {
+ 'table' => 'cust_event',
+ 'hashref' => {
+ 'eventpart' => $self->eventpart,
+ 'tablenum' => $tablenum,
+ 'status' => { op=>'!=', value=>'failed' },
+ },
+ 'extra_sql' => ( $opt{'cust_event'}->eventnum =~ /^(\d+)$/
+ ? " AND eventnum != $1 "
+ : ''
+ ),
+ } );
+
+ ! scalar(@existing);
+
+}
+
+sub condition_sql {
+ my( $self, $table ) = @_;
+
+ my %tablenum = %{ FS::part_event->eventtable_pkey_sql };
+
+ "0 = ( SELECT COUNT(*) FROM cust_event
+ WHERE cust_event.eventpart = part_event.eventpart
+ AND cust_event.tablenum = $tablenum{$table}
+ AND status != 'failed'
+ )
+ ";
+
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/once_percust.pm b/FS/FS/part_event/Condition/once_percust.pm
new file mode 100644
index 0000000..b8a8fbf
--- /dev/null
+++ b/FS/FS/part_event/Condition/once_percust.pm
@@ -0,0 +1,67 @@
+package FS::part_event::Condition::once_percust;
+
+use strict;
+use FS::Record qw( qsearch );
+use FS::part_event;
+use FS::cust_event;
+
+use base qw( FS::part_event::Condition );
+
+sub description { "Don't run more than once per customer"; }
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 1,
+ };
+}
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $obj_pkey = $object->primary_key;
+ my $obj_table = $object->table;
+ my $custnum = $object->custnum;
+
+ my @where = (
+ "tablenum IN ( SELECT $obj_pkey FROM $obj_table WHERE custnum = $custnum )"
+ );
+ if ( $opt{'cust_event'}->eventnum =~ /^(\d+)$/ ) {
+ push @where, " eventnum != $1 ";
+ }
+ my $extra_sql = ' AND '. join(' AND ', @where);
+
+ my @existing = qsearch( {
+ 'table' => 'cust_event',
+ 'hashref' => {
+ 'eventpart' => $self->eventpart,
+ #'tablenum' => $tablenum,
+ 'status' => { op=>'!=', value=>'failed' },
+ },
+ 'extra_sql' => $extra_sql,
+ } );
+
+ ! scalar(@existing);
+
+}
+
+#XXX test?
+sub condition_sql {
+ my( $self, $table ) = @_;
+
+ my %pkey = %{ FS::part_event->eventtable_pkey };
+
+ my $pkey = $pkey{$table};
+
+ "0 = ( SELECT COUNT(*) FROM cust_event
+ WHERE cust_event.eventpart = part_event.eventpart
+ AND cust_event.tablenum IN (
+ SELECT $pkey FROM $table AS once_percust
+ WHERE once_percust.custnum = cust_main.custnum )
+ AND status != 'failed'
+ )
+ ";
+
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/payby.pm b/FS/FS/part_event/Condition/payby.pm
new file mode 100644
index 0000000..d931568
--- /dev/null
+++ b/FS/FS/part_event/Condition/payby.pm
@@ -0,0 +1,50 @@
+package FS::part_event::Condition::payby;
+
+use strict;
+use Tie::IxHash;
+use FS::payby;
+
+use base qw( FS::part_event::Condition );
+
+sub description {
+ #'customer payment types: ';
+ 'Customer payment type';
+}
+
+#something like this
+tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2longname;
+sub option_fields {
+ (
+ 'payby' => {
+ label => 'Customer payment type',
+ #type => 'select-multiple',
+ type => 'checkbox-multiple',
+ options => [ keys %payby ],
+ option_labels => \%payby,
+ },
+ );
+}
+
+sub condition {
+ my( $self, $object ) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ #uuh.. all right? test this.
+ my $hashref = $self->option('payby') || {};
+ $hashref->{ $cust_main->payby };
+
+}
+
+#sub condition_sql {
+# my( $self, $table ) = @_;
+#
+# #uuh... yeah... something like this. test it for sure.
+#
+# my @payby = keys %{ $self->option('payby') };
+#
+# ' ( '. join(' OR ', map { "cust_main.payby = '$_'" } @payby ). ' ) ';
+#
+#}
+
+1;
diff --git a/FS/FS/part_event/Condition/pkg_age.pm b/FS/FS/part_event/Condition/pkg_age.pm
new file mode 100644
index 0000000..8b3b4c9
--- /dev/null
+++ b/FS/FS/part_event/Condition/pkg_age.pm
@@ -0,0 +1,58 @@
+package FS::part_event::Condition::pkg_age;
+
+use strict;
+use base qw( FS::part_event::Condition );
+use FS::Record qw( qsearch );
+
+sub description {
+ 'Package Age';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 1,
+ };
+}
+
+#something like this
+sub option_fields {
+ (
+ 'age' => { 'label' => 'Package date age',
+ 'type' => 'freq',
+ },
+ 'field' => { 'label' => 'Compare date',
+ 'type' => 'select',
+ 'options' =>
+ [qw( setup last_bill bill adjourn susp expire cancel )],
+ 'labels' => {
+ 'setup' => 'Setup date',
+ 'last_bill' => 'Last bill date',
+ 'bill' => 'Next bill date',
+ 'adjourn' => 'Adjournment date',
+ 'susp' => 'Suspension date',
+ 'expire' => 'Expiration date',
+ 'cancel' => 'Cancellation date',
+ },
+ },
+ );
+}
+
+sub condition {
+ my( $self, $cust_pkg, %opt ) = @_;
+
+ my $age = $self->option_age_from('age', $opt{'time'} );
+
+ my $pkg_date = $cust_pkg->get( $self->option('field') );
+
+ $pkg_date && $pkg_date <= $age;
+
+}
+
+#XXX write me for efficiency
+#sub condition_sql {
+#
+#}
+
+1;
+
diff --git a/FS/FS/part_event/Condition/pkg_class.pm b/FS/FS/part_event/Condition/pkg_class.pm
new file mode 100644
index 0000000..8c9031c
--- /dev/null
+++ b/FS/FS/part_event/Condition/pkg_class.pm
@@ -0,0 +1,38 @@
+package FS::part_event::Condition::pkg_class;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+use FS::Record qw( qsearch );
+use FS::pkg_class;
+
+sub description {
+ 'Package Class';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 1,
+ };
+}
+
+#something like this
+sub option_fields {
+ (
+ 'pkgclass' => { 'label' => 'Package Class',
+ 'type' => 'select-pkg_class',
+ 'multiple' => 1,
+ },
+ );
+}
+
+sub condition {
+ my( $self, $cust_pkg ) = @_;
+
+ #XXX test
+ my $hashref = $self->option('pkgclass') || {};
+ $hashref->{ $cust_pkg->part_pkg->classnum };
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/pkg_notchange.pm b/FS/FS/part_event/Condition/pkg_notchange.pm
new file mode 100644
index 0000000..4c103c2
--- /dev/null
+++ b/FS/FS/part_event/Condition/pkg_notchange.pm
@@ -0,0 +1,31 @@
+package FS::part_event::Condition::pkg_notchange;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+use FS::Record qw( qsearch );
+
+sub description {
+ 'Package is a new order, not a change';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 1,
+ };
+}
+
+sub condition {
+ my( $self, $cust_pkg ) = @_;
+
+ ! $cust_pkg->change_date;
+
+}
+
+sub condition_sql {
+ '( cust_pkg.change_date IS NULL OR cust_pkg.change_date = 0 )';
+}
+
+1;
+
diff --git a/FS/FS/part_event/Condition/pkg_pkgpart.pm b/FS/FS/part_event/Condition/pkg_pkgpart.pm
new file mode 100644
index 0000000..6adef8e
--- /dev/null
+++ b/FS/FS/part_event/Condition/pkg_pkgpart.pm
@@ -0,0 +1,39 @@
+package FS::part_event::Condition::pkg_pkgpart;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+sub description { 'Package definitions'; }
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 1,
+ };
+}
+
+sub option_fields {
+ (
+ 'if_pkgpart' => { 'label' => 'Only packages: ',
+ 'type' => 'select-part_pkg',
+ 'multiple' => 1,
+ },
+ );
+}
+
+sub condition {
+ my( $self, $cust_pkg) = @_;
+
+ #XXX test
+ my $if_pkgpart = $self->option('if_pkgpart') || {};
+ $if_pkgpart->{ $cust_pkg->pkgpart };
+
+}
+
+#XXX
+#sub condition_sql {
+#
+#}
+
+1;
diff --git a/FS/FS/part_event/Condition/pkg_recurring.pm b/FS/FS/part_event/Condition/pkg_recurring.pm
new file mode 100644
index 0000000..1b66821
--- /dev/null
+++ b/FS/FS/part_event/Condition/pkg_recurring.pm
@@ -0,0 +1,31 @@
+package FS::part_event::Condition::pkg_recurring;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+sub description { 'Package is recurring'; }
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 1,
+ };
+}
+
+sub condition {
+ my( $self, $cust_pkg ) = @_;
+
+ $cust_pkg->part_pkg->freq !~ /^0+\D?$/; #just in case, probably just != '0'
+
+}
+
+
+#XXX join part_pkg USING (pkgpart)
+# part_pkg.freq != '0'
+#sub condition_sql {
+#
+#}
+
+1;
+
diff --git a/FS/FS/part_event/Condition/pkg_status.pm b/FS/FS/part_event/Condition/pkg_status.pm
new file mode 100644
index 0000000..6c1c9cc
--- /dev/null
+++ b/FS/FS/part_event/Condition/pkg_status.pm
@@ -0,0 +1,37 @@
+package FS::part_event::Condition::pkg_status;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+use FS::Record qw( qsearch );
+
+sub description {
+ 'Package Status';
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 1,
+ };
+}
+
+#something like this
+sub option_fields {
+ (
+ 'status' => { 'label' => 'Package Status',
+ 'type' => 'select-cust_pkg-status',
+ 'multiple' => 1,
+ },
+ );
+}
+
+sub condition {
+ my( $self, $cust_pkg ) = @_;
+
+ #XXX test
+ my $hashref = $self->option('status') || {};
+ $hashref->{ $cust_pkg->status };
+}
+
+1;
diff --git a/FS/FS/part_event/Condition/pkg_unless_pkgpart.pm b/FS/FS/part_event/Condition/pkg_unless_pkgpart.pm
new file mode 100644
index 0000000..47fa8c3
--- /dev/null
+++ b/FS/FS/part_event/Condition/pkg_unless_pkgpart.pm
@@ -0,0 +1,39 @@
+package FS::part_event::Condition::pkg_unless_pkgpart;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+sub description { 'Except package definitions'; }
+
+sub eventtable_hashref {
+ { 'cust_main' => 0,
+ 'cust_bill' => 0,
+ 'cust_pkg' => 1,
+ };
+}
+
+sub option_fields {
+ (
+ 'unless_pkgpart' => { 'label' => 'Except packages: ',
+ 'type' => 'select-part_pkg',
+ 'multiple' => 1,
+ },
+ );
+}
+
+sub condition {
+ my( $self, $cust_pkg) = @_;
+
+ #XXX test
+ my $unless_pkgpart = $self->option('unless_pkgpart') || {};
+ ! $unless_pkgpart->{ $cust_pkg->pkgpart };
+
+}
+
+#XXX
+#sub condition_sql {
+#
+#}
+
+1;
diff --git a/FS/FS/part_event_condition.pm b/FS/FS/part_event_condition.pm
new file mode 100644
index 0000000..d13e849
--- /dev/null
+++ b/FS/FS/part_event_condition.pm
@@ -0,0 +1,352 @@
+package FS::part_event_condition;
+
+use strict;
+use vars qw( @ISA $DEBUG @SKIP_CONDITION_SQL );
+use FS::UID qw(dbh);
+use FS::Record qw( qsearch qsearchs );
+use FS::option_Common;
+use FS::part_event; #for order_conditions_sql...
+
+@ISA = qw( FS::option_Common ); # FS::Record );
+$DEBUG = 0;
+
+@SKIP_CONDITION_SQL = ();
+
+=head1 NAME
+
+FS::part_event_condition - Object methods for part_event_condition records
+
+=head1 SYNOPSIS
+
+ use FS::part_event_condition;
+
+ $record = new FS::part_event_condition \%hash;
+ $record = new FS::part_event_condition { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_event_condition object represents an event condition.
+FS::part_event_condition inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item eventconditionnum - primary key
+
+=item eventpart - Event definition (see L<FS::part_event>)
+
+=item conditionname - Condition name - defines which FS::part_event::Condition::I<conditionname> evaluates this condition
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new event. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_event_condition'; }
+
+=item insert [ HASHREF | OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+If a list or hash reference of options is supplied, part_event_condition_option
+records are created (see L<FS::part_event_condition_option>).
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD [ HASHREF | OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+If a list or hash reference of options is supplied, part_event_condition_option
+records are created or modified (see L<FS::part_event_condition_option>).
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('eventconditionnum')
+ || $self->ut_foreign_key('eventpart', 'part_event', 'eventpart')
+ || $self->ut_alpha('conditionname')
+ ;
+ return $error if $error;
+
+ #XXX check conditionname to make sure a module exists?
+ # well it'll die in _rebless...
+
+ $self->SUPER::check;
+}
+
+
+=item _rebless
+
+Reblesses the object into the FS::part_event::Condition::CONDITIONNAME class,
+where CONDITIONNAME is the object's I<conditionname> field.
+
+=cut
+
+sub _rebless {
+ my $self = shift;
+ my $conditionname = $self->conditionname;
+ #my $class = ref($self). "::$conditionname";
+ my $class = "FS::part_event::Condition::$conditionname";
+ eval "use $class";
+ die $@ if $@;
+ bless($self, $class); #unless $@;
+ $self;
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item conditions [ EVENTTABLE ]
+
+Return information about the available conditions. If an eventtable is
+specified, only return information about conditions available for that
+eventtable.
+
+Information is returned as key-value pairs. Keys are condition names. Values
+are hashrefs with the following keys:
+
+=over 4
+
+=item description
+
+=item option_fields
+
+# =item default_weight
+
+# =item deprecated
+
+=back
+
+See L<FS::part_event::Condition> for more information.
+
+=cut
+
+#false laziness w/part_event.pm
+#some false laziness w/part_export & part_pkg
+my %conditions;
+foreach my $INC ( @INC ) {
+ foreach my $file ( glob("$INC/FS/part_event/Condition/*.pm") ) {
+ warn "attempting to load Condition from $file\n" if $DEBUG;
+ $file =~ /\/(\w+)\.pm$/ or do {
+ warn "unrecognized file in $INC/FS/part_event/Condition/: $file\n";
+ next;
+ };
+ my $mod = $1;
+ my $fullmod = "FS::part_event::Condition::$mod";
+ eval "use $fullmod;";
+ if ( $@ ) {
+ die "error using $fullmod (skipping): $@\n" if $@;
+ #warn "error using $fullmod (skipping): $@\n" if $@;
+ #next;
+ }
+ if ( $fullmod->disabled ) {
+ warn "$fullmod is disabled; skipping\n";
+ next;
+ }
+ #my $full_condition_sql = $fullmod. '::condition_sql';
+ my $condition_sql_coderef = sub { $fullmod->condition_sql(@_) };
+ my $order_sql_coderef = $fullmod->can('order_sql')
+ ? sub { $fullmod->order_sql(@_) }
+ : '';
+ $conditions{$mod} = {
+ ( map { $_ => $fullmod->$_() }
+ qw( description eventtable_hashref
+ implicit_flag remove_warning
+ order_sql_weight
+ )
+ # deprecated
+ #option_fields_hashref
+ ),
+ 'option_fields' => [ $fullmod->option_fields() ],
+ 'condition_sql' => $condition_sql_coderef,
+ 'order_sql' => $order_sql_coderef,
+ };
+ }
+}
+
+sub conditions {
+ my( $class, $eventtable ) = @_;
+ (
+ map { $_ => $conditions{$_} }
+# sort { $conditions{$a}->{'default_weight'}<=>$conditions{$b}->{'default_weight'} }
+# sort by ?
+ $class->all_conditionnames( $eventtable )
+ );
+
+}
+
+=item all_conditionnames [ EVENTTABLE ]
+
+Returns a list of just the condition names
+
+=cut
+
+sub all_conditionnames {
+ my ( $class, $eventtable ) = @_;
+
+ grep { !$eventtable || $conditions{$_}->{'eventtable_hashref'}{$eventtable} }
+ keys %conditions
+}
+
+=item join_conditions_sql [ EVENTTABLE ]
+
+Returns an SQL fragment selecting joining all condition options for an event as
+tables titled "cond_I<conditionname>". Typically used in conjunction with
+B<where_conditions_sql>.
+
+=cut
+
+sub join_conditions_sql {
+ my ( $class, $eventtable ) = @_;
+ my %conditions = $class->conditions( $eventtable );
+
+ join(' ',
+ map {
+ "LEFT JOIN part_event_condition AS cond_$_".
+ " ON ( part_event.eventpart = cond_$_.eventpart".
+ " AND cond_$_.conditionname = ". dbh->quote($_).
+ " )";
+ }
+ keys %conditions
+ );
+
+}
+
+=item where_conditions_sql [ EVENTTABLE [ , OPTION => VALUE, ... ] ]
+
+Returns an SQL fragment to select events which have unsatisfied conditions.
+Must be used in conjunction with B<join_conditions_sql>.
+
+The only current option is "time", the current time (or "pretend" current time
+as passed to freeside-daily), as a UNIX timestamp.
+
+=cut
+
+sub where_conditions_sql {
+ my ( $class, $eventtable, %options ) = @_;
+
+ my $time = $options{'time'};
+
+ my %conditions = $class->conditions( $eventtable );
+
+ my $where = join(' AND ',
+ map {
+ my $conditionname = $_;
+ my $coderef = $conditions{$conditionname}->{condition_sql};
+ my $sql = &$coderef( $eventtable, 'time'=>$time );
+ die "$coderef is not a CODEREF" unless ref($coderef) eq 'CODE';
+ "( cond_$conditionname.conditionname IS NULL OR $sql )";
+ }
+ grep { my $cond = $_;
+ ! grep { $_ eq $cond } @SKIP_CONDITION_SQL
+ }
+ keys %conditions
+ );
+
+ $where;
+}
+
+=item order_conditions_sql [ EVENTTABLE ]
+
+Returns an SQL fragment to order selected events. Must be used in conjunction
+with B<join_conditions_sql>.
+
+=cut
+
+sub order_conditions_sql {
+ my( $class, $eventtable ) = @_;
+
+ my %conditions = $class->conditions( $eventtable );
+
+ my $eventtables = join(' ', FS::part_event->eventtables_runorder);
+
+ my $order_by = join(', ',
+ "position( part_event.eventtable in ' $eventtables ')",
+ ( map {
+ my $conditionname = $_;
+ my $coderef = $conditions{$conditionname}->{order_sql};
+ my $sql = &$coderef( $eventtable );
+ "CASE WHEN cond_$conditionname.conditionname IS NULL
+ THEN -1
+ ELSE $sql
+ END
+ ";
+ }
+ sort { $conditions{$a}->{order_sql_weight}
+ <=> $conditions{$b}->{order_sql_weight}
+ }
+ grep { $conditions{$_}->{order_sql} }
+ keys %conditions
+ ),
+ 'part_event.weight'
+ );
+
+ "ORDER BY $order_by";
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::part_event::Condition>, L<FS::part_event>, L<FS::Record>, schema.html from
+the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_event_condition_option.pm b/FS/FS/part_event_condition_option.pm
new file mode 100644
index 0000000..3256dc0
--- /dev/null
+++ b/FS/FS/part_event_condition_option.pm
@@ -0,0 +1,151 @@
+package FS::part_event_condition_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::option_Common;
+use FS::part_event_condition;
+
+@ISA = qw( FS::option_Common ); # FS::Record);
+
+=head1 NAME
+
+FS::part_event_condition_option - Object methods for part_event_condition_option records
+
+=head1 SYNOPSIS
+
+ use FS::part_event_condition_option;
+
+ $record = new FS::part_event_condition_option \%hash;
+ $record = new FS::part_event_condition_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_event_condition_option object represents an event condition option.
+FS::part_event_condition_option inherits from FS::Record. The following fields
+are currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item eventconditionnum - Event condition (see L<FS::part_event_condition>)
+
+=item optionname - Option name
+
+=item optionvalue - Option value
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_event_condition_option'; }
+
+=item insert [ HASHREF | OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+If a list or hash reference of options is supplied,
+part_event_condition_option_option records are created (see
+L<FS::part_event_condition_option_option>).
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD [ HASHREF | OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+If a list or hash reference of options is supplied,
+part_event_condition_option_option records are created or modified (see
+L<FS::part_event_condition_option_option>).
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('eventconditionnum',
+ 'part_event_condition', 'eventconditionnum')
+ || $self->ut_text('optionname')
+ || $self->ut_textn('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+#this makes the nested options magically show up as perl refs
+#move it to a mixin class if we need nested options again
+sub optionvalue {
+ my $self = shift;
+ if ( scalar(@_) ) { #setting, no magic (here, insert takes care of it)
+ $self->set('optionvalue', @_);
+ } else { #getting, magic
+ my $optionvalue = $self->get('optionvalue');
+ if ( $optionvalue eq 'HASH' ) {
+ return { $self->options };
+ } else {
+ $optionvalue;
+ }
+ }
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::part_event_condition>, L<FS::part_event_condition_option_option>,
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_event_condition_option_option.pm b/FS/FS/part_event_condition_option_option.pm
new file mode 100644
index 0000000..7396c22
--- /dev/null
+++ b/FS/FS/part_event_condition_option_option.pm
@@ -0,0 +1,129 @@
+package FS::part_event_condition_option_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::part_event_condition_option;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_event_condition_option_option - Object methods for part_event_condition_option_option records
+
+=head1 SYNOPSIS
+
+ use FS::part_event_condition_option_option;
+
+ $record = new FS::part_event_condition_option_option \%hash;
+ $record = new FS::part_event_condition_option_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_event_condition_option_option object represents a nested event
+condition option. FS::part_event_condition_option_option inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item optionoptionnum - primary key
+
+=item optionnum - Parent option (see L<FS::part_event_option>)
+
+=item optionname - Option name
+
+=item optionvalue - Option value
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_event_condition_option_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionoptionnum')
+ || $self->ut_foreign_key('optionnum',
+ 'part_event_condition_option', 'optionnum' )
+ || $self->ut_text('optionname')
+ || $self->ut_textn('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::part_event_condition_option>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_event_option.pm b/FS/FS/part_event_option.pm
new file mode 100644
index 0000000..09b7756
--- /dev/null
+++ b/FS/FS/part_event_option.pm
@@ -0,0 +1,214 @@
+package FS::part_event_option;
+
+use strict;
+use vars qw( @ISA );
+use Scalar::Util qw( blessed );
+use FS::UID qw( dbh );
+use FS::Record qw( qsearch qsearchs );
+use FS::part_event;
+use FS::reason;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_event_option - Object methods for part_event_option records
+
+=head1 SYNOPSIS
+
+ use FS::part_event_option;
+
+ $record = new FS::part_event_option \%hash;
+ $record = new FS::part_event_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_event_option object represents an event definition option (action
+option). FS::part_event_option inherits from FS::Record. The following fields
+are currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item eventpart - Event definition (see L<FS::part_event>)
+
+=item optionname - Option name
+
+=item optionvalue - Option value
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_event_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( $self->optionname eq 'reasonnum' && $self->optionvalue eq 'HASH' ) {
+
+ my $error = $self->insert_reason(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace [ OLD_RECORD ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $self->replace_old;
+
+ if ( $self->optionname eq 'reasonnum' ) {
+ warn "reasonnum: ". $self->optionvalue;
+ }
+ if ( $self->optionname eq 'reasonnum' && $self->optionvalue eq 'HASH' ) {
+
+ my $error = $self->insert_reason(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ my $error = $self->SUPER::replace($old);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('eventpart', 'part_event', 'eventpart' )
+ || $self->ut_text('optionname')
+ || $self->ut_textn('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+sub insert_reason {
+ my( $self, $reason ) = @_;
+
+ my $reason_obj = new FS::reason({
+ 'reason_type' => $reason->{'typenum'},
+ 'reason' => $reason->{'reason'},
+ });
+
+ $reason_obj->insert or $self->optionvalue( $reason_obj->reasonnum ) and '';
+
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::part_event>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm
new file mode 100644
index 0000000..16aad6d
--- /dev/null
+++ b/FS/FS/part_export.pm
@@ -0,0 +1,470 @@
+package FS::part_export;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK $DEBUG %exports );
+use Exporter;
+use Tie::IxHash;
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::option_Common;
+use FS::part_svc;
+use FS::part_export_option;
+use FS::export_svc;
+
+#for export modules, though they should probably just use it themselves
+use FS::queue;
+
+@ISA = qw( FS::option_Common );
+@EXPORT_OK = qw(export_info);
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::part_export - Object methods for part_export records
+
+=head1 SYNOPSIS
+
+ use FS::part_export;
+
+ $record = new FS::part_export \%hash;
+ $record = new FS::part_export { 'column' => 'value' };
+
+ #($new_record, $options) = $template_recored->clone( $svcpart );
+
+ $error = $record->insert( { 'option' => 'value' } );
+ $error = $record->insert( \%options );
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_export object represents an export of Freeside data to an external
+provisioning system. FS::part_export inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item exportnum - primary key
+
+=item machine - Machine name
+
+=item exporttype - Export type
+
+=item nodomain - blank or "Y" : usernames are exported to this service with no domain
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new export. To add the export to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_export'; }
+
+=cut
+
+#=item clone SVCPART
+#
+#An alternate constructor. Creates a new export by duplicating an existing
+#export. The given svcpart is assigned to the new export.
+#
+#Returns a list consisting of the new export object and a hashref of options.
+#
+#=cut
+#
+#sub clone {
+# my $self = shift;
+# my $class = ref($self);
+# my %hash = $self->hash;
+# $hash{'exportnum'} = '';
+# $hash{'svcpart'} = shift;
+# ( $class->new( \%hash ),
+# { map { $_->optionname => $_->optionvalue }
+# qsearch('part_export_option', { 'exportnum' => $self->exportnum } )
+# }
+# );
+#}
+
+=item insert HASHREF
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+If a hash reference of options is supplied, part_export_option records are
+created (see L<FS::part_export_option>).
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+#foreign keys would make this much less tedious... grr dumb mysql
+sub delete {
+ my $self = shift;
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ foreach my $export_svc ( $self->export_svc ) {
+ my $error = $export_svc->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid export. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+ my $error =
+ $self->ut_numbern('exportnum')
+ || $self->ut_domain('machine')
+ || $self->ut_alpha('exporttype')
+ ;
+ return $error if $error;
+
+ $self->nodomain =~ /^(Y?)$/ or return "Illegal nodomain: ". $self->nodomain;
+ $self->nodomain($1);
+
+ $self->deprecated(1); #BLAH
+
+ #check exporttype?
+
+ $self->SUPER::check;
+}
+
+#=item part_svc
+#
+#Returns the service definition (see L<FS::part_svc>) for this export.
+#
+#=cut
+#
+#sub part_svc {
+# my $self = shift;
+# qsearchs('part_svc', { svcpart => $self->svcpart } );
+#}
+
+sub part_svc {
+ use Carp;
+ croak "FS::part_export::part_svc deprecated";
+ #confess "FS::part_export::part_svc deprecated";
+}
+
+=item svc_x
+
+Returns a list of associated FS::svc_* records.
+
+=cut
+
+sub svc_x {
+ my $self = shift;
+ map { $_->svc_x } $self->cust_svc;
+}
+
+=item cust_svc
+
+Returns a list of associated FS::cust_svc records.
+
+=cut
+
+sub cust_svc {
+ my $self = shift;
+ map { qsearch('cust_svc', { 'svcpart' => $_->svcpart } ) }
+ grep { qsearch('cust_svc', { 'svcpart' => $_->svcpart } ) }
+ $self->export_svc;
+}
+
+=item export_svc
+
+Returns a list of associated FS::export_svc records.
+
+=cut
+
+sub export_svc {
+ my $self = shift;
+ qsearch('export_svc', { 'exportnum' => $self->exportnum } );
+}
+
+=item part_export_option
+
+Returns all options as FS::part_export_option objects (see
+L<FS::part_export_option>).
+
+=cut
+
+sub part_export_option {
+ my $self = shift;
+ $self->option_objects;
+}
+
+=item options
+
+Returns a list of option names and values suitable for assigning to a hash.
+
+=item option OPTIONNAME
+
+Returns the option value for the given name, or the empty string.
+
+=item _rebless
+
+Reblesses the object into the FS::part_export::EXPORTTYPE class, where
+EXPORTTYPE is the object's I<exporttype> field. There should be better docs
+on how to create new exports, but until then, see L</NEW EXPORT CLASSES>.
+
+=cut
+
+sub _rebless {
+ my $self = shift;
+ my $exporttype = $self->exporttype;
+ my $class = ref($self). "::$exporttype";
+ eval "use $class;";
+ #die $@ if $@;
+ bless($self, $class) unless $@;
+ $self;
+}
+
+#these should probably all go away, just let the subclasses define em
+
+=item export_insert SVC_OBJECT
+
+=cut
+
+sub export_insert {
+ my $self = shift;
+ #$self->rebless;
+ $self->_export_insert(@_);
+}
+
+#sub AUTOLOAD {
+# my $self = shift;
+# $self->rebless;
+# my $method = $AUTOLOAD;
+# #$method =~ s/::(\w+)$/::_$1/; #infinite loop prevention
+# $method =~ s/::(\w+)$/_$1/; #infinite loop prevention
+# $self->$method(@_);
+#}
+
+=item export_replace NEW OLD
+
+=cut
+
+sub export_replace {
+ my $self = shift;
+ #$self->rebless;
+ $self->_export_replace(@_);
+}
+
+=item export_delete
+
+=cut
+
+sub export_delete {
+ my $self = shift;
+ #$self->rebless;
+ $self->_export_delete(@_);
+}
+
+=item export_suspend
+
+=cut
+
+sub export_suspend {
+ my $self = shift;
+ #$self->rebless;
+ $self->_export_suspend(@_);
+}
+
+=item export_unsuspend
+
+=cut
+
+sub export_unsuspend {
+ my $self = shift;
+ #$self->rebless;
+ $self->_export_unsuspend(@_);
+}
+
+#fallbacks providing useful error messages intead of infinite loops
+sub _export_insert {
+ my $self = shift;
+ return "_export_insert: unknown export type ". $self->exporttype;
+}
+
+sub _export_replace {
+ my $self = shift;
+ return "_export_replace: unknown export type ". $self->exporttype;
+}
+
+sub _export_delete {
+ my $self = shift;
+ return "_export_delete: unknown export type ". $self->exporttype;
+}
+
+#call svcdb-specific fallbacks
+
+sub _export_suspend {
+ my $self = shift;
+ #warn "warning: _export_suspened unimplemented for". ref($self);
+ my $svc_x = shift;
+ my $new = $svc_x->clone_suspended;
+ $self->_export_replace( $new, $svc_x );
+}
+
+sub _export_unsuspend {
+ my $self = shift;
+ #warn "warning: _export_unsuspend unimplemented for ". ref($self);
+ my $svc_x = shift;
+ my $old = $svc_x->clone_kludge_unsuspend;
+ $self->_export_replace( $svc_x, $old );
+}
+
+=item export_links SVC_OBJECT ARRAYREF
+
+Adds a list of web elements to ARRAYREF specific to this export and SVC_OBJECT.
+The elements are displayed in the UI to lead the the operator to external
+configuration, monitoring, and similar tools.
+
+=cut
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item export_info [ SVCDB ]
+
+Returns a hash reference of the exports for the given I<svcdb>, or if no
+I<svcdb> is specified, for all exports. The keys of the hash are
+I<exporttype>s and the values are again hash references containing information
+on the export:
+
+ 'desc' => 'Description',
+ 'options' => {
+ 'option' => { label=>'Option Label' },
+ 'option2' => { label=>'Another label' },
+ },
+ 'nodomain' => 'Y', #or ''
+ 'notes' => 'Additional notes',
+
+=cut
+
+sub export_info {
+ #warn $_[0];
+ return $exports{$_[0]} || {} if @_;
+ #{ map { %{$exports{$_}} } keys %exports };
+ my $r = { map { %{$exports{$_}} } keys %exports };
+}
+
+#=item exporttype2svcdb EXPORTTYPE
+#
+#Returns the applicable I<svcdb> for an I<exporttype>.
+#
+#=cut
+#
+#sub exporttype2svcdb {
+# my $exporttype = $_[0];
+# foreach my $svcdb ( keys %exports ) {
+# return $svcdb if grep { $exporttype eq $_ } keys %{$exports{$svcdb}};
+# }
+# '';
+#}
+
+#false laziness w/part_pkg & cdr
+foreach my $INC ( @INC ) {
+ foreach my $file ( glob("$INC/FS/part_export/*.pm") ) {
+ warn "attempting to load export info from $file\n" if $DEBUG;
+ $file =~ /\/(\w+)\.pm$/ or do {
+ warn "unrecognized file in $INC/FS/part_export/: $file\n";
+ next;
+ };
+ my $mod = $1;
+ my $info = eval "use FS::part_export::$mod; ".
+ "\\%FS::part_export::$mod\::info;";
+ if ( $@ ) {
+ die "error using FS::part_export::$mod (skipping): $@\n" if $@;
+ next;
+ }
+ unless ( keys %$info ) {
+ warn "no %info hash found in FS::part_export::$mod, skipping\n"
+ unless $mod =~ /^(passwdfile|null)$/; #hack but what the heck
+ next;
+ }
+ warn "got export info from FS::part_export::$mod: $info\n" if $DEBUG;
+ no strict 'refs';
+ foreach my $svc (
+ ref($info->{'svc'}) ? @{$info->{'svc'}} : $info->{'svc'}
+ ) {
+ unless ( $svc ) {
+ warn "blank svc for FS::part_export::$mod (skipping)\n";
+ next;
+ }
+ $exports{$svc}->{$mod} = $info;
+ }
+ }
+}
+
+=back
+
+=head1 NEW EXPORT CLASSES
+
+A module should be added in FS/FS/part_export/ (an example may be found in
+eg/export_template.pm)
+
+=head1 BUGS
+
+Hmm... cust_export class (not necessarily a database table...) ... ?
+
+deprecated column...
+
+=head1 SEE ALSO
+
+L<FS::part_export_option>, L<FS::export_svc>, L<FS::svc_acct>,
+L<FS::svc_domain>,
+L<FS::svc_forward>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_export/acct_freeside.pm b/FS/FS/part_export/acct_freeside.pm
new file mode 100644
index 0000000..3c287ca
--- /dev/null
+++ b/FS/FS/part_export/acct_freeside.pm
@@ -0,0 +1,139 @@
+package FS::part_export::acct_freeside;
+
+use vars qw( @ISA %info $DEBUG );
+use Data::Dumper;
+use Tie::IxHash;
+use FS::part_export;
+#use FS::Record qw( qsearch qsearchs );
+use Frontier::Client;
+
+@ISA = qw(FS::part_export);
+
+$DEBUG = 1;
+
+tie my %options, 'Tie::IxHash',
+ 'xmlrpc_url' => { label => 'Full URL to target Freeside xmlrpc.cgi', },
+ 'ss_username' => { label => 'Self-service username', },
+ 'ss_domain' => { label => 'Self-service domain', },
+ 'ss_password' => { label => 'Self-service password', },
+ 'domsvc' => { label => 'Domain svcnum on target machine', },
+ 'pkgnum' => { label => 'Customer package pkgnum on target machine', },
+ 'svcpart' => { label => 'Service definition svcpart on target machine', },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to another Freeside server',
+ 'options' => \%options,
+ 'notes' => <<END
+Real-time export to another Freeside server via self-service.
+Requires installation of
+<a href="http://search.cpan.org/dist/Frontier-Client">Frontier::Client</a>
+from CPAN and setup of an appropriate bulk customer on the other Freeside server.
+END
+);
+
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+
+ my $result = $self->_freeside_command('provision_acct',
+ 'pkgnum' => $self->option('pkgnum'),
+ 'svcpart' => $self->option('svcpart'),
+ 'username' => $svc_acct->username,
+ '_password' => $svc_acct->_password,
+ '_password2' => $svc_acct->_password,
+ 'domsvc' => $self->option('domsvc'),
+ );
+
+ $result->{error} || '';
+
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ my $svcnum = $self->_freeside_find_svc( $old );
+ return $svcnum unless $svcnum =~ /^(\d+)$/;
+
+ #only pw change supported for now...
+ my $result = $self->_freeside_command( 'myaccount_passwd',
+ 'svcnum' => $svcnum,
+ 'new_password' => $new->_password,
+ 'new_password2' => $new->_password,
+ );
+
+ $result->{error} || '';
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ my $svcnum = $self->_freeside_find_svc( $svc_acct );
+ return $svcnum unless $svcnum =~ /^(\d+)$/;
+
+ my $result = $self->_freeside_command( 'unprovision_svc', 'svcnum'=>$svcnum );
+
+ $result->{'error'} || '';
+
+}
+
+sub _freeside_find_svc {
+ my( $self, $svc_acct ) = ( shift, shift );
+
+ my $list_svcs = $self->_freeside_command( 'list_svcs', 'svcdb'=>'svc_acct' );
+ my @svc = grep { $svc_acct->username eq $_->{username}
+ #&& compare domains
+ } @{ $list_svcs->{svcs} };
+
+ return 'multiple services found on target FS' if scalar(@svc) > 1;
+ return 'no service found on target FS' if scalar(@svc) == 0; #shouldn't be fatal?
+
+ $svc[0]->{'svcnum'};
+
+}
+
+sub _freeside_command {
+ my( $self, $method, @args ) = @_;
+
+ my %login = (
+ 'username' => $self->option('ss_username'),
+ 'domain' => $self->option('ss_domain'),
+ 'password' => $self->option('ss_password'),
+ );
+ my $login_result = $self->_do_freeside_command( 'login', %login );
+ return $login_result if $login_result->{error};
+ my $session_id = $login_result->{session_id};
+
+ #could reuse $session id for replace & delete where we have to find then delete..
+
+ my %command = (
+ session_id => $session_id,
+ @args
+ );
+ my $result = $self->_do_freeside_command( $method, %command );
+
+ $result;
+
+}
+
+sub _do_freeside_command {
+ my( $self, $method, %args ) = @_;
+
+ # a questionable choice... but it'll do for now.
+ eval "use Frontier::Client;";
+ die $@ if $@;
+
+ #reuse?
+ my $conn = Frontier::Client->new( url => $self->option('xmlrpc_url') );
+
+ warn "sending FS selfservice $method: ". Dumper(\%args)
+ if $DEBUG;
+ my $result = $conn->call("FS.SelfService.XMLRPC.$method", \%args);
+ warn "FS selfservice $method response: ". Dumper($result)
+ if $DEBUG;
+
+ $result;
+
+}
+
+1;
diff --git a/FS/FS/part_export/acct_plesk.pm b/FS/FS/part_export/acct_plesk.pm
new file mode 100644
index 0000000..1be820a
--- /dev/null
+++ b/FS/FS/part_export/acct_plesk.pm
@@ -0,0 +1,121 @@
+package FS::part_export::acct_plesk;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'URL' => { label=>'URL' },
+ 'login' => { label=>'Login' },
+ 'password' => { label=>'Password' },
+ 'debug' => { label=>'Enable debugging',
+ type=>'checkbox' },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to Plesk managed mail service',
+ 'options'=> \%options,
+ 'notes' => <<'END'
+Real-time export to
+<a href="http://www.swsoft.com/">Plesk</a> managed server.
+Requires installation of
+<a href="http://search.cpan.org/dist/Net-Plesk">Net::Plesk</a>
+from CPAN.
+END
+);
+
+sub rebless { shift; }
+
+# experiment: want the status of these right away (don't want account to
+# create or whatever and then get error in the queue from dup username or
+# something), so no queueing
+
+sub _export_insert {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ $self->_plesk_command( 'mail_add',
+ $svc_acct->domain,
+ $svc_acct->username,
+ $svc_acct->_password,
+ ) ||
+ $self->_export_unsuspend($svc_acct);
+}
+
+sub _plesk_command {
+ my( $self, $method, $domain, @args ) = @_;
+
+ eval "use Net::Plesk;";
+ return $@ if $@;
+
+ local($Net::Plesk::DEBUG) = 1
+ if $self->option('debug');
+
+ my $plesk = new Net::Plesk (
+ 'POST' => $self->option('URL'),
+ ':HTTP_AUTH_LOGIN' => $self->option('login'),
+ ':HTTP_AUTH_PASSWD' => $self->option('password'),
+ );
+
+ my $dresponse = $plesk->domain_get( $domain );
+ return $dresponse->errortext unless $dresponse->is_success;
+ my $domainID = $dresponse->id;
+
+ my $response = $plesk->$method($dresponse->id, @args);
+ return $response->errortext unless $response->is_success;
+ '';
+
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ return "can't change domain with Plesk"
+ if $old->domain ne $new->domain;
+ return "can't change username with Plesk"
+ if $old->username ne $new->username;
+ return '' unless $old->_password ne $new->_password;
+
+ $self->_plesk_command( 'mail_set',
+ $new->domain,
+ $new->username,
+ $new->_password,
+ $old->cust_svc->cust_pkg->susp ? 0 : 1,
+ );
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ $self->_plesk_command( 'mail_remove',
+ $svc_acct->domain,
+ $svc_acct->username,
+ );
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ $self->_plesk_command( 'mail_set',
+ $svc_acct->domain,
+ $svc_acct->username,
+ $svc_acct->_password,
+ 0,
+ );
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ $self->_plesk_command( 'mail_set',
+ $svc_acct->domain,
+ $svc_acct->username,
+ $svc_acct->_password,
+ 1,
+ );
+}
+
+1;
+
diff --git a/FS/FS/part_export/acct_sql.pm b/FS/FS/part_export/acct_sql.pm
new file mode 100644
index 0000000..9f1ae7b
--- /dev/null
+++ b/FS/FS/part_export/acct_sql.pm
@@ -0,0 +1,310 @@
+package FS::part_export::acct_sql;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+#use Digest::MD5 qw(md5_hex);
+use FS::Record; #qw(qsearchs);
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'datasrc' => { label => 'DBI data source' },
+ 'username' => { label => 'Database username' },
+ 'password' => { label => 'Database password' },
+ 'table' => { label => 'Database table' },
+ 'schema' => { label =>
+ 'Database schema mapping to Freeside methods.',
+ type => 'textarea',
+ },
+ 'static' => { label =>
+ 'Database schema mapping to static values.',
+ type => 'textarea',
+ },
+ 'primary_key' => { label => 'Database primary key' },
+ 'crypt' => { label => 'Password encryption',
+ type=>'select', options=>[qw(crypt md5)],
+ default=>'crypt',
+ },
+;
+
+tie my %vpopmail_map, 'Tie::IxHash',
+ 'pw_name' => 'username',
+ 'pw_domain' => 'domain',
+ 'pw_passwd' => 'crypt_password',
+ 'pw_uid' => 'uid',
+ 'pw_gid' => 'gid',
+ 'pw_gecos' => 'finger',
+ 'pw_dir' => 'dir',
+ #'pw_shell' => 'shell',
+ 'pw_shell' => 'quota',
+;
+my $vpopmail_map = join('\n', map "$_ $vpopmail_map{$_}", keys %vpopmail_map );
+
+tie my %postfix_courierimap_mailbox_map, 'Tie::IxHash',
+ 'username' => 'email',
+ 'password' => '_password',
+ 'crypt' => 'crypt_password',
+ 'name' => 'finger',
+ 'maildir' => 'virtual_maildir',
+ 'domain' => 'domain',
+ 'svcnum' => 'svcnum',
+;
+my $postfix_courierimap_mailbox_map =
+ join('\n', map "$_ $postfix_courierimap_mailbox_map{$_}",
+ keys %postfix_courierimap_mailbox_map );
+
+tie my %postfix_courierimap_alias_map, 'Tie::IxHash',
+ 'address' => 'email',
+ 'goto' => 'email',
+ 'domain' => 'domain',
+ 'svcnum' => 'svcnum',
+;
+my $postfix_courierimap_alias_map =
+ join('\n', map "$_ $postfix_courierimap_alias_map{$_}",
+ keys %postfix_courierimap_alias_map );
+
+tie my %postfix_native_mailbox_map, 'Tie::IxHash',
+ 'userid' => 'email',
+ 'uid' => 'uid',
+ 'gid' => 'gid',
+ 'password' => 'ldap_password',
+ 'mail' => 'domain_slash_username',
+;
+my $postfix_native_mailbox_map =
+ join('\n', map "$_ $postfix_native_mailbox_map{$_}",
+ keys %postfix_native_mailbox_map );
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export of accounts to SQL databases '.
+ '(vpopmail, Postfix+Courier IMAP, others?)',
+ 'options' => \%options,
+ 'nodomain' => '',
+ 'notes' => <<END
+Export accounts (svc_acct records) to SQL databases. Currently has default
+configurations for vpopmail and Postfix+Courier IMAP but intended to be
+configurable for other schemas as well.
+
+<BR><BR>In contrast to sqlmail, this is intended to export just svc_acct
+records only, rather than a single export for svc_acct, svc_forward and
+svc_domain records, to export in "default" database schemas rather than
+configure the MTA or POP/IMAP server for a Freeside-specific schema, and
+to be configured for different mail server setups.
+
+<BR><BR>Use these buttons for some useful presets:
+<UL>
+ <li><INPUT TYPE="button" VALUE="vpopmail" onClick='
+ this.form.table.value = "vpopmail";
+ this.form.schema.value = "$vpopmail_map";
+ this.form.primary_key.value = "pw_name, pw_domain";
+ '>
+ <LI><INPUT TYPE="button" VALUE="postfix_courierimap_mailbox" onClick='
+ this.form.table.value = "mailbox";
+ this.form.schema.value = "$postfix_courierimap_mailbox_map";
+ this.form.primary_key.value = "username";
+ '>
+ <LI><INPUT TYPE="button" VALUE="postfix_courierimap_alias" onClick='
+ this.form.table.value = "alias";
+ this.form.schema.value = "$postfix_courierimap_alias_map";
+ this.form.primary_key.value = "address";
+ '>
+ <LI><INPUT TYPE="button" VALUE="postfix_native_mailbox" onClick='
+ this.form.table.value = "users";
+ this.form.schema.value = "$postfix_native_mailbox_map";
+ this.form.primary_key.value = "userid";
+ '>
+</UL>
+END
+);
+
+sub _schema_map { shift->_map('schema'); }
+sub _static_map { shift->_map('static'); }
+
+sub _map {
+ my $self = shift;
+ map { /^\s*(\S+)\s*(\S+)\s*$/ } split("\n", $self->option(shift) );
+}
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+
+ my %schema = $self->_schema_map;
+ my %static = $self->_static_map;
+
+ my %record = (
+
+ ( map { $_ => $static{$_} } keys %static ),
+
+ ( map { my $value = $schema{$_};
+ my @arg = ();
+ push @arg, $self->option('crypt')
+ if $value eq 'crypt_password' && $self->option('crypt');
+ $_ => $svc_acct->$value(@arg);
+ } keys %schema
+ ),
+
+ );
+
+ my $err_or_queue =
+ $self->acct_sql_queue(
+ $svc_acct->svcnum,
+ 'insert',
+ $self->option('table'),
+ %record
+ );
+ return $err_or_queue unless ref($err_or_queue);
+
+ '';
+
+}
+
+sub _export_replace {
+ my($self, $new, $old) = (shift, shift, shift);
+
+ my %schema = $self->_schema_map;
+ my %static = $self->_static_map;
+
+ my @primary_key = ();
+ if ( $self->option('primary_key') =~ /,/ ) {
+ foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
+ my $keymap = $schema{$key};
+ push @primary_key, $old->$keymap();
+ }
+ } else {
+ my $keymap = $schema{$self->option('primary_key')};
+ push @primary_key, $old->$keymap();
+ }
+
+ my %record = (
+
+ ( map { $_ => $static{$_} } keys %static ),
+
+ ( map { my $value = $schema{$_};
+ my @arg = ();
+ push @arg, $self->option('crypt')
+ if $value eq 'crypt_password' && $self->option('crypt');
+ $_ => $new->$value(@arg);
+ } keys %schema
+ ),
+
+ );
+
+ my $err_or_queue = $self->acct_sql_queue(
+ $new->svcnum,
+ 'replace',
+ $self->option('table'),
+ $self->option('primary_key'), @primary_key,
+ %record,
+ );
+ return $err_or_queue unless ref($err_or_queue);
+ '';
+}
+
+sub _export_delete {
+ my ( $self, $svc_acct ) = (shift, shift);
+
+ my %schema = $self->_schema_map;
+
+ my %primary_key = ();
+ if ( $self->option('primary_key') =~ /,/ ) {
+ foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
+ my $keymap = $schema{$key};
+ $primary_key{ $key } = $svc_acct->$keymap();
+ }
+ } else {
+ my $keymap = $schema{$self->option('primary_key')};
+ $primary_key{ $self->option('primary_key') } = $svc_acct->$keymap(),
+ }
+
+ my $err_or_queue = $self->acct_sql_queue(
+ $svc_acct->svcnum,
+ 'delete',
+ $self->option('table'),
+ %primary_key,
+ #$self->option('primary_key') => $svc_acct->$keymap(),
+ );
+ return $err_or_queue unless ref($err_or_queue);
+ '';
+}
+
+sub acct_sql_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::acct_sql::acct_sql_$method",
+ };
+ $queue->insert(
+ $self->option('datasrc'),
+ $self->option('username'),
+ $self->option('password'),
+ @_,
+ ) or $queue;
+}
+
+sub acct_sql_insert { #subroutine, not method
+ my $dbh = acct_sql_connect(shift, shift, shift);
+ my( $table, %record ) = @_;
+
+ my $sth = $dbh->prepare(
+ "INSERT INTO $table ( ". join(", ", keys %record).
+ " ) VALUES ( ". join(", ", map '?', keys %record ). " )"
+ ) or die $dbh->errstr;
+
+ $sth->execute( values(%record) )
+ or die "can't insert into $table table: ". $sth->errstr;
+
+ $dbh->disconnect;
+}
+
+sub acct_sql_delete { #subroutine, not method
+ my $dbh = acct_sql_connect(shift, shift, shift);
+ my( $table, %record ) = @_;
+
+ my $sth = $dbh->prepare(
+ "DELETE FROM $table WHERE ". join(' AND ', map "$_ = ? ", keys %record )
+ ) or die $dbh->errstr;
+
+ $sth->execute( map $record{$_}, keys %record )
+ or die "can't delete from $table table: ". $sth->errstr;
+
+ $dbh->disconnect;
+}
+
+sub acct_sql_replace { #subroutine, not method
+ my $dbh = acct_sql_connect(shift, shift, shift);
+
+ my( $table, $pkey ) = ( shift, shift );
+
+ my %primary_key = ();
+ if ( $pkey =~ /,/ ) {
+ foreach my $key ( split(/\s*,\s*/, $pkey ) ) {
+ $primary_key{$key} = shift;
+ }
+ } else {
+ $primary_key{$pkey} = shift;
+ }
+
+ my %record = @_;
+
+ my $sth = $dbh->prepare(
+ "UPDATE $table".
+ ' SET '. join(', ', map "$_ = ?", keys %record ).
+ ' WHERE '. join(' AND ', map "$_ = ?", keys %primary_key )
+ ) or die $dbh->errstr;
+
+ $sth->execute( values(%record), values(%primary_key) );
+
+ $dbh->disconnect;
+}
+
+sub acct_sql_connect {
+ #my($datasrc, $username, $password) = @_;
+ #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
+ DBI->connect(@_) or die $DBI::errstr;
+}
+
+1;
+
diff --git a/FS/FS/part_export/apache.pm b/FS/FS/part_export/apache.pm
new file mode 100644
index 0000000..35b00cc
--- /dev/null
+++ b/FS/FS/part_export/apache.pm
@@ -0,0 +1,47 @@
+package FS::part_export::apache;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::null;
+
+@ISA = qw(FS::part_export::null);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'httpd_conf' => { label=>'httpd.conf snippet location',
+ default=>'/etc/apache/httpd-freeside.conf', },
+ 'restart' => { label=>'Apache restart command',
+ default=>'apachectl graceful',
+ },
+ 'template' => {
+ label => 'Template',
+ type => 'textarea',
+ default => <<'END',
+<VirtualHost $domain> #generic
+#<VirtualHost ip.addr> #preferred, http://httpd.apache.org/docs/dns-caveats.html
+DocumentRoot /var/www/$zone
+ServerName $zone
+ServerAlias *.$zone
+#BandWidthModule On
+#LargeFileLimit 4096 12288
+#FrontpageEnable on
+</VirtualHost>
+
+END
+ },
+;
+
+%info = (
+ 'svc' => 'svc_www',
+ 'desc' => 'Export an Apache httpd.conf file snippet.',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Batch export of an httpd.conf snippet from a template. Typically used with
+something like <code>Include /etc/apache/httpd-freeside.conf</code> in
+httpd.conf. <a href="http://search.cpan.org/dist/File-Rsync">File::Rsync</a>
+must be installed. Run bin/apache.export to export the files.
+END
+);
+
+1;
+
diff --git a/FS/FS/part_export/artera_turbo.pm b/FS/FS/part_export/artera_turbo.pm
new file mode 100644
index 0000000..c006db9
--- /dev/null
+++ b/FS/FS/part_export/artera_turbo.pm
@@ -0,0 +1,181 @@
+package FS::part_export::artera_turbo;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::Record qw(qsearch);
+use FS::part_export;
+use FS::cust_svc;
+use FS::svc_external;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'rid' => { 'label' => 'Reseller ID (RID)' },
+ 'username' => { 'label' => 'Reseller username', },
+ 'password' => { 'label' => 'Reseller password', },
+ 'pid' => { 'label' => 'Artera Product ID', },
+ 'priceid' => { 'label' => 'Artera Price ID', },
+ 'agent_aid' => { 'label' => 'Export agentnum values to Artera AID',
+ 'type' => 'checkbox',
+ },
+ 'aid' => { 'label' => 'Artera Agent ID to use if not using agentnum values', },
+ 'production' => { 'label' => 'Production mode (leave unchecked for staging)',
+ 'type' => 'checkbox',
+ },
+ 'debug' => { 'label' => 'Enable debug logging',
+ 'type' => 'checkbox',
+ },
+ 'enable_edit' => { 'label' => 'Enable local editing of Artera serial numbers and key codes (note that the changes will NOT be exported to Artera)',
+ 'type' => 'checkbox',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_external',
+ #'svc' => [qw( svc_acct svc_forward )],
+ 'desc' =>
+ 'Real-time export to Artera Turbo Reseller API',
+ 'options' => \%options,
+ #'nodomain' => 'Y',
+ 'notes' => <<'END'
+Real-time export to <a href="http://www.arteraturbo.com/">Artera Turbo</a>
+Reseller API. Requires installation of
+<a href="http://search.cpan.org/dist/Net-Artera">Net::Artera</a>
+from CPAN. You probably also want to:
+<UL>
+ <LI>In the configuration UI section: set the <B>svc_external-skip_manual</B> and <B>svc_external-display_type</B> configuration values.
+ <LI>In the message catalog: set <B>svc_external-id</B> to <I>Artera Serial Number</I> and set <B>svc_external-title</B> to <I>Artera Key Code</I>.
+</UL>
+END
+);
+
+sub rebless { shift; }
+
+sub _new_Artera {
+ my $self = shift;
+
+ my $artera = new Net::Artera (
+ map { $_ => $self->option($_) }
+ qw( rid username password production )
+ );
+}
+
+
+sub _export_insert {
+ my($self, $svc_external) = (shift, shift);
+
+ # want the ASN (serial) and AKC (key code) right away
+
+ eval "use Net::Artera;";
+ return $@ if $@;
+ $Net::Artera::DEBUG = 1 if $self->option('debug');
+ my $artera = $self->_new_Artera;
+
+ my $cust_pkg = $svc_external->cust_svc->cust_pkg;
+ my $part_pkg = $cust_pkg->part_pkg;
+ my @svc_acct = grep { $_->table eq 'svc_acct' }
+ map { $_->svc_x }
+ sort { my $svcpart = $part_pkg->svcpart('svc_acct');
+ ($b->svcpart==$svcpart) cmp ($a->svcpart==$svcpart); }
+ qsearch('cust_svc', { 'pkgnum' => $cust_pkg->pkgnum } );
+ my $email = scalar(@svc_acct) ? $svc_acct[0]->email : '';
+
+ my $cust_main = $cust_pkg->cust_main;
+
+ my $result = $artera->newOrder(
+ 'pid' => $self->option('pid'),
+ 'priceid' => $self->option('priceid'),
+ 'email' => $email,
+ 'cname' => $cust_main->name,
+ 'ref' => $svc_external->svcnum,
+ 'aid' => ( $self->option('agent_aid')
+ ? $cust_main->agentnum
+ : $self->option('aid') ),
+ 'add1' => $cust_main->address1,
+ 'add2' => $cust_main->address2,
+ 'add3' => $cust_main->city,
+ 'add4' => $cust_main->state,
+ 'zip' => $cust_main->zip,
+ 'cid' => $cust_main->country,
+ 'phone' => $cust_main->daytime || $cust_main->night,
+ 'fax' => $cust_main->fax,
+ );
+
+ if ( $result->{'id'} == 1 ) {
+ my $new = new FS::svc_external { $svc_external->hash };
+ $new->id(sprintf('%010d', $result->{'ASN'}));
+ $new->title( substr('0000000000'.uc($result->{'AKC'}), -10) );
+ $new->replace($svc_external);
+ } else {
+ $result->{'message'} || 'No response from Artera';
+ }
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return '' if $self->option('enable_edit');
+ return "can't change serial number with Artera"
+ if $old->id != $new->id && $old->id;
+ return "can't change key code with Artera"
+ if $old->title ne $new->title && $old->title;
+ '';
+}
+
+sub _export_delete {
+ my( $self, $svc_external ) = (shift, shift);
+ $self->queue_statusChange(17, $svc_external);
+}
+
+sub _export_suspend {
+ my( $self, $svc_external ) = (shift, shift);
+ $self->queue_statusChange(16, $svc_external);
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_external ) = (shift, shift);
+ $self->queue_statusChange(15, $svc_external);
+}
+
+sub queue_statusChange {
+ my( $self, $status, $svc_external ) = @_;
+
+ my $queue = new FS::queue {
+ 'svcnum' => $svc_external->svcnum,
+ 'job' => 'FS::part_export::artera_turbo::statusChange',
+ };
+ $queue->insert(
+ ( map { $self->option($_) }
+ qw( rid username password production ) ),
+ $status,
+ $svc_external->id,
+ $svc_external->title,
+ $self->option('debug'),
+ );
+}
+
+sub statusChange {
+ my( $rid, $username, $password, $prod, $status, $id, $title, $debug ) = @_;
+
+ eval "use Net::Artera;";
+ return $@ if $@;
+ $Net::Artera::DEBUG = 1 if $debug;
+
+ my $artera = new Net::Artera (
+ 'rid' => $rid,
+ 'username' => $username,
+ 'password' => $password,
+ 'production' => $prod,
+ );
+
+ my $result = $artera->statusChange(
+ 'asn' => sprintf('%010d', $id),
+ 'akc' => substr("0000000000$title", -10),
+ 'statusid' => $status,
+ );
+
+ die $result->{'message'} unless $result->{'id'} == 1;
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/bind.pm b/FS/FS/part_export/bind.pm
new file mode 100644
index 0000000..1ef7b65
--- /dev/null
+++ b/FS/FS/part_export/bind.pm
@@ -0,0 +1,35 @@
+package FS::part_export::bind;
+
+use vars qw(@ISA %info %options);
+use Tie::IxHash;
+use FS::part_export::null;
+
+@ISA = qw(FS::part_export::null);
+
+tie %options, 'Tie::IxHash',
+ 'named_conf' => { label => 'named.conf location',
+ default=> '/etc/bind/named.conf' },
+ 'zonepath' => { label => 'path to zone files',
+ default=> '/etc/bind/', },
+ 'bind_release' => { label => 'ISC BIND Release',
+ type => 'select',
+ options => [qw(BIND8 BIND9)],
+ default => 'BIND8' },
+ 'bind9_minttl' => { label => 'The minttl required by bind9 and RFC1035.',
+ default => '1D' },
+ 'reload' => { label => 'Optional reload command. If not specified, defaults to "ndc" under BIND8 and "rndc" under BIND9.', },
+;
+
+%info = (
+ 'svc' => 'svc_domain',
+ 'desc' => 'Batch export to BIND named',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Batch export of BIND zone and configuration files to a primary nameserver.
+<a href="http://search.cpan.org/search?dist=File-Rsync">File::Rsync</a>
+must be installed. Run bin/bind.export to export the files.
+END
+);
+
+1;
+
diff --git a/FS/FS/part_export/bind_slave.pm b/FS/FS/part_export/bind_slave.pm
new file mode 100644
index 0000000..c89325f
--- /dev/null
+++ b/FS/FS/part_export/bind_slave.pm
@@ -0,0 +1,28 @@
+package FS::part_export::bind_slave;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::null;
+
+@ISA = qw(FS::part_export::null);
+
+tie my %options, 'Tie::IxHash',
+ 'master' => { label=> 'Master IP address(s) (semicolon-separated)' },
+ %FS::part_export::bind::options,
+;
+delete $options{'zonepath'};
+
+%info = (
+ 'svc' => 'svc_domain',
+ 'desc' =>'Batch export to slave BIND named',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Batch export of BIND configuration file to a secondary nameserver. Zones are
+slaved from the listed masters.
+<a href="http://search.cpan.org/dist/File-Rsync">File::Rsync</a>
+must be installed. Run bin/bind.export to export the files.
+END
+);
+
+1;
+
diff --git a/FS/FS/part_export/bsdshell.pm b/FS/FS/part_export/bsdshell.pm
new file mode 100644
index 0000000..7b5feb2
--- /dev/null
+++ b/FS/FS/part_export/bsdshell.pm
@@ -0,0 +1,25 @@
+package FS::part_export::bsdshell;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::passwdfile;
+
+@ISA = qw(FS::part_export::passwdfile);
+
+tie my %options, 'Tie::IxHash', %FS::part_export::passwdfile::options;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' =>
+ 'Batch export of /etc/passwd and /etc/master.passwd files (BSD)',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => <<'END'
+MD5 crypt requires installation of
+<a href="http://search.cpan.org/dist/Crypt-PasswdMD5">Crypt::PasswdMD5</a>
+from CPAN. Run bin/bsdshell.export to export the files.
+END
+);
+
+1;
+
diff --git a/FS/FS/part_export/communigate_pro.pm b/FS/FS/part_export/communigate_pro.pm
new file mode 100644
index 0000000..ecb3780
--- /dev/null
+++ b/FS/FS/part_export/communigate_pro.pm
@@ -0,0 +1,178 @@
+package FS::part_export::communigate_pro;
+
+use vars qw(@ISA %info %options);
+use Tie::IxHash;
+use FS::part_export;
+use FS::queue;
+
+@ISA = qw(FS::part_export);
+
+tie %options, 'Tie::IxHash',
+ 'port' => { label=>'Port number', default=>'106', },
+ 'login' => { label=>'The administrator account name. The name can contain a domain part.', },
+ 'password' => { label=>'The administrator account password.', },
+ 'accountType' => { label=>'Type for newly-created accounts',
+ type=>'select',
+ options=>[qw( MultiMailbox TextMailbox MailDirMailbox )],
+ default=>'MultiMailbox',
+ },
+ 'externalFlag' => { label=> 'Create accounts with an external (visible for legacy mailers) INBOX.',
+ type=>'checkbox',
+ },
+ 'AccessModes' => { label=>'Access modes',
+ default=>'Mail POP IMAP PWD WebMail WebSite',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to a CommuniGate Pro mail server',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Real time export to a
+<a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a>
+mail server. The
+<a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a>
+must be installed as CGP::CLI.
+END
+);
+
+sub rebless { shift; }
+
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ $svc_acct->email;
+}
+
+sub _export_insert {
+ my( $self, $svc_acct ) = (shift, shift);
+ my @options = ( $svc_acct->svcnum, 'CreateAccount',
+ 'accountName' => $self->export_username($svc_acct),
+ 'accountType' => $self->option('accountType'),
+ 'AccessModes' => $self->option('AccessModes'),
+ 'RealName' => $svc_acct->finger,
+ 'Password' => $svc_acct->_password,
+ );
+ push @options, 'MaxAccountSize' => $svc_acct->quota if $svc_acct->quota;
+ push @options, 'externalFlag' => $self->option('externalFlag')
+ if $self->option('externalFlag');
+
+ $self->communigate_pro_queue( @options );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return "can't (yet) change username with CommuniGate Pro"
+ if $old->username ne $new->username;
+ return "can't (yet) change domain with CommuniGate Pro"
+ if $self->export_username($old) ne $self->export_username($new);
+ return "can't (yet) change GECOS with CommuniGate Pro"
+ if $old->finger ne $new->finger;
+ return "can't (yet) change quota with CommuniGate Pro"
+ if $old->quota ne $new->quota;
+ return '' unless $old->username ne $new->username
+ || $old->_password ne $new->_password
+ || $old->finger ne $new->finger
+ || $old->quota ne $new->quota;
+
+ return '' if '*SUSPENDED* '. $old->_password eq $new->_password;
+
+ #my $err_or_queue = $self->communigate_pro_queue( $new->svcnum,'RenameAccount',
+ # $old->email, $new->email );
+ #return $err_or_queue unless ref($err_or_queue);
+ #my $jobnum = $err_or_queue->jobnum;
+
+ $self->communigate_pro_queue( $new->svcnum, 'SetAccountPassword',
+ $self->export_username($new), $new->_password )
+ if $new->_password ne $old->_password;
+
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->communigate_pro_queue( $svc_acct->svcnum, 'DeleteAccount',
+ $self->export_username($svc_acct),
+ );
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->communigate_pro_queue( $svc_acct->svcnum, 'UpdateAccountSettings',
+ 'accountName' => $self->export_username($svc_acct),
+ 'AccessModes' => 'Mail',
+ );
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->communigate_pro_queue( $svc_acct->svcnum, 'UpdateAccountSettings',
+ 'accountName' => $self->export_username($svc_acct),
+ 'AccessModes' => $self->option('AccessModes'),
+ );
+}
+
+sub communigate_pro_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my @kludge_methods = qw(CreateAccount UpdateAccountSettings);
+ my $sub = 'communigate_pro_command';
+ $sub = $method if grep { $method eq $_ } @kludge_methods;
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::communigate_pro::$sub",
+ };
+ $queue->insert(
+ $self->machine,
+ $self->option('port'),
+ $self->option('login'),
+ $self->option('password'),
+ $method,
+ @_,
+ );
+
+}
+
+sub CreateAccount {
+ my( $machine, $port, $login, $password, $method, %args ) = @_;
+ my $accountName = delete $args{'accountName'};
+ my $accountType = delete $args{'accountType'};
+ my $externalFlag = delete $args{'externalFlag'};
+ $args{'AccessModes'} = [ split(' ', $args{'AccessModes'}) ];
+ my @args = ( accountName => $accountName,
+ accountType => $accountType,
+ settings => \%args,
+ );
+ #externalFlag => $externalFlag,
+ push @args, externalFlag => $externalFlag if $externalFlag;
+
+ communigate_pro_command( $machine, $port, $login, $password, $method, @args );
+
+}
+
+sub UpdateAccountSettings {
+ my( $machine, $port, $login, $password, $method, %args ) = @_;
+ my $accountName = delete $args{'accountName'};
+ $args{'AccessModes'} = [ split(' ', $args{'AccessModes'}) ];
+ @args = ( $accountName, \%args );
+ communigate_pro_command( $machine, $port, $login, $password, $method, @args );
+}
+
+sub communigate_pro_command { #subroutine, not method
+ my( $machine, $port, $login, $password, $method, @args ) = @_;
+
+ eval "use CGP::CLI";
+
+ my $cli = new CGP::CLI( {
+ 'PeerAddr' => $machine,
+ 'PeerPort' => $port,
+ 'login' => $login,
+ 'password' => $password,
+ } ) or die "Can't login to CGPro: $CGP::ERR_STRING\n";
+
+ $cli->$method(@args) or die "CGPro error: ". $cli->getErrMessage;
+
+ $cli->Logout; # or die "Can't logout of CGPro: $CGP::ERR_STRING\n";
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/communigate_pro_singledomain.pm b/FS/FS/part_export/communigate_pro_singledomain.pm
new file mode 100644
index 0000000..e25043f
--- /dev/null
+++ b/FS/FS/part_export/communigate_pro_singledomain.pm
@@ -0,0 +1,37 @@
+package FS::part_export::communigate_pro_singledomain;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::communigate_pro;
+
+@ISA = qw(FS::part_export::communigate_pro);
+
+tie my %options, 'Tie::IxHash', %FS::part_export::communigate_pro::options,
+ 'domain' => { label=>'Domain', },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' =>
+ 'Real-time export to a CommuniGate Pro mail server, one domain only',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => <<'END'
+Real time export to a
+<a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a>
+mail server. This is an unusual export to CommuniGate Pro that forces all
+accounts into a single domain. As CommuniGate Pro supports multiple domains,
+unless you have a specific reason for using this export, you probably want to
+use the communigate_pro export instead. The
+<a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a>
+must be installed as CGP::CLI.
+END
+);
+
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ $svc_acct->username. '@'. $self->option('domain');
+}
+
+1;
+
diff --git a/FS/FS/part_export/cp.pm b/FS/FS/part_export/cp.pm
new file mode 100644
index 0000000..96fa437
--- /dev/null
+++ b/FS/FS/part_export/cp.pm
@@ -0,0 +1,161 @@
+package FS::part_export::cp;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'port' => { label=>'Port number' },
+ 'username' => { label=>'Username' },
+ 'password' => { label=>'Password' },
+ 'domain' => { label=>'Domain' },
+ 'workgroup' => { label=>'Default Workgroup' },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to Critical Path Account Provisioning Protocol',
+ 'options'=> \%options,
+ 'notes' => <<'END'
+Real-time export to
+<a href="http://www.cp.net/">Critial Path Account Provisioning Protocol</a>.
+Requires installation of
+<a href="http://search.cpan.org/dist/Net-APP">Net::APP</a>
+from CPAN.
+END
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->cp_queue( $svc_acct->svcnum, 'create_mailbox',
+ 'Mailbox' => $svc_acct->username,
+ 'Password' => $svc_acct->_password,
+ 'Workgroup' => $self->option('workgroup'),
+ 'Domain' => $svc_acct->domain,
+ );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return "can't change domain with Critical Path"
+ if $old->domain ne $new->domain;
+ return "can't change username with Critical Path" #CP no longer supports this
+ if $old->username ne $new->username;
+ return '' unless $old->_password ne $new->_password;
+ $self->cp_queue( $new->svcnum, 'replace', $new->domain,
+ $old->username, $new->username, $old->_password, $new->_password );
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->cp_queue( $svc_acct->svcnum, 'delete_mailbox',
+ 'Mailbox' => $svc_acct->username,
+ 'Domain' => $svc_acct->domain,
+ );
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->cp_queue( $svc_acct->svcnum, 'set_mailbox_status',
+ 'Mailbox' => $svc_acct->username,
+ 'Domain' => $svc_acct->domain,
+ 'OTHER' => 'T',
+ 'OTHER_SUSPEND' => 'T',
+ );
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->cp_queue( $svc_acct->svcnum, 'set_mailbox_status',
+ 'Mailbox' => $svc_acct->username,
+ 'Domain' => $svc_acct->domain,
+ 'PAYMENT' => 'F',
+ 'OTHER' => 'F',
+ 'OTHER_SUSPEND' => 'F',
+ 'OTHER_BOUNCE' => 'F',
+ );
+}
+
+sub cp_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => 'FS::part_export::cp::cp_command',
+ };
+ $queue->insert(
+ $self->machine,
+ $self->option('port'),
+ $self->option('username'),
+ $self->option('password'),
+ $self->option('domain'),
+ $method,
+ @_,
+ );
+}
+
+sub cp_command { #subroutine, not method
+ my($host, $port, $username, $password, $login_domain, $method, @args) = @_;
+
+ #quelle hack
+ if ( $method eq 'replace' ) {
+
+ my( $domain, $old_username, $new_username, $old_password, $new_password)
+ = @args;
+
+ if ( $old_username ne $new_username ) {
+ cp_command($host, $port, $username, $password, 'rename_mailbox',
+ Domain => $domain,
+ Old_Mailbox => $old_username,
+ New_Mailbox => $new_username,
+ );
+ }
+
+ #my $other = 'F';
+ if ( $new_password =~ /^\*SUSPENDED\* (.*)$/ ) {
+ $new_password = $1;
+ # $other = 'T';
+ }
+ #cp_command($host, $port, $username, $password, $login_domain,
+ # 'set_mailbox_status',
+ # Domain => $domain,
+ # Mailbox => $new_username,
+ # Other => $other,
+ # Other_Bounce => $other,
+ #);
+
+ if ( $old_password ne $new_password ) {
+ cp_command($host, $port, $username, $password, $login_domain,
+ 'change_mailbox',
+ Domain => $domain,
+ Mailbox => $new_username,
+ Password => $new_password,
+ );
+ }
+
+ return;
+ }
+ #eof quelle hack
+
+ eval "use Net::APP;";
+
+ my $app = new Net::APP (
+ "$host:$port",
+ User => $username,
+ Password => $password,
+ Domain => $login_domain,
+ Timeout => 60,
+ #Debug => 1,
+ ) or die "$@\n";
+
+ $app->$method( @args );
+
+ die $app->message."\n" unless $app->ok;
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/cpanel.pm b/FS/FS/part_export/cpanel.pm
new file mode 100644
index 0000000..0ad00df
--- /dev/null
+++ b/FS/FS/part_export/cpanel.pm
@@ -0,0 +1,192 @@
+package FS::part_export::cpanel;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote access username' },
+ 'accesshash' => { label=>'Remote access key', type=>'textarea' },
+ 'debug' => { label=>'Enable debugging', type=>'checkbox' },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to Cpanel control panel.',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => 'Real time export to a the <a href="http://www.cpanel.net/">Cpanel</a> control panel software. Service definition names are exported as Cpanel packages. Requires installation of the Cpanel::Accounting perl module distributed with Cpanel.',
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+ $err_or_queue = $self->cpanel_queue( $svc_acct->svcnum, 'insert',
+ $svc_acct->domain,
+ $svc_acct->username,
+ $svc_acct->_password,
+ $svc_acct->cust_svc->part_svc->svc,
+ );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return "can't change username with cpanel"
+ if $old->username ne $new->username;
+ return "can't change password with cpanel"
+ if $old->_passsword ne $new->_password;
+ return "can't change domain with cpanel"
+ if $old->domain ne $new->domain;
+
+ '';
+
+ ##return '' unless $old->_password ne $new->_password;
+ #$err_or_queue = $self->cpanel_queue( $new->svcnum,
+ # 'replace', $new->username, $new->_password );
+ #ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $err_or_queue = $self->cpanel_queue( $svc_acct->svcnum,
+ 'delete', $svc_acct->username
+ );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $err_or_queue = $self->cpanel_queue( $svc_acct->svcnum,
+ 'suspend', $svc_acct->username );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $err_or_queue = $self->cpanel_queue( $svc_acct->svcnum,
+ 'unsuspend', $svc_acct->username );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+
+sub cpanel_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::cpanel::cpanel_$method",
+ };
+ $queue->insert(
+ $self->machine,
+ $self->option('user'),
+ $self->option('accesshash'),
+ $self->option('debug'),
+ @_
+ ) or $queue;
+}
+
+
+sub cpanel_insert { #subroutine, not method
+ my( $machine, $user, $accesshash, $debug ) = splice(@_,0,4);
+
+# my $whm = cpanel_connect($machine, $user, $accesshash, $debug);
+# warn " cpanel->createacct ". join(', ', @_). "\n"
+# if $debug;
+# my $response = $whm->createacct(@_);
+# die $whm->{'error'} if $whm->{'error'};
+# warn " cpanel response: $response\n"
+# if $debug;
+
+ warn "cpanel_insert: attempting web interface to add POP"
+ if $debug;
+
+ my($domain, $username, $password, $svc) = @_;
+
+ use LWP::UserAgent;
+ use HTTP::Request::Common qw(POST);
+
+ my $url =
+ "http://$user:$accesshash\@$domain:2082/frontend/x/mail/addpop2.html";
+
+ my $ua = LWP::UserAgent->new();
+
+ #$req->authorization_basic($user, $accesshash);
+
+ my $res = $ua->request(
+ POST( $url,
+ [
+ 'email' => $username,
+ 'domain' => $domain,
+ 'password' => $password,
+ 'quota' => 10, #?
+ ]
+ )
+ );
+
+ die "Error submitting data to $url: ". $res->status_line
+ unless $res->is_success;
+
+ die "Username in use"
+ if $res->content =~ /exists/;
+
+ die "Account not created: ". $res->content
+ if $res->content =~ /failure/;
+
+}
+
+#sub cpanel_replace { #subroutine, not method
+#}
+
+sub cpanel_delete { #subroutine, not method
+ my( $machine, $user, $accesshash, $debug ) = splice(@_,0,4);
+ my $whm = cpanel_connect($machine, $user, $accesshash, $debug);
+ warn " cpanel->killacct ". join(', ', @_). "\n"
+ if $debug;
+ my $response = $whm->killacct(shift);
+ die $whm->{'error'} if $whm->{'error'};
+ warn " cpanel response: $response\n"
+ if $debug;
+}
+
+sub cpanel_suspend { #subroutine, not method
+ my( $machine, $user, $accesshash, $debug ) = splice(@_,0,4);
+ my $whm = cpanel_connect($machine, $user, $accesshash, $debug);
+ warn " cpanel->suspend ". join(', ', @_). "\n"
+ if $debug;
+ my $response = $whm->suspend(shift);
+ die $whm->{'error'} if $whm->{'error'};
+ warn " cpanel response: $response\n"
+ if $debug;
+}
+
+sub cpanel_unsuspend { #subroutine, not method
+ my( $machine, $user, $accesshash, $debug ) = splice(@_,0,4);
+ my $whm = cpanel_connect($machine, $user, $accesshash, $debug);
+ warn " cpanel->unsuspend ". join(', ', @_). "\n"
+ if $debug;
+ my $response = $whm->unsuspend(shift);
+ die $whm->{'error'} if $whm->{'error'};
+ warn " cpanel response: $response\n"
+ if $debug;
+}
+
+sub cpanel_connect {
+ my( $host, $user, $accesshash, $debug ) = @_;
+
+ eval "use Cpanel::Accounting;";
+ die $@ if $@;
+
+ warn "creating new Cpanel::Accounting connection to $user@$host\n"
+ if $debug;
+
+ my $whm = new Cpanel::Accounting;
+ $whm->{'host'} = $host;
+ $whm->{'user'} = $user;
+ $whm->{'accesshash'} = $accesshash;
+ $whm->{'usessl'} = 1;
+
+ $whm;
+}
diff --git a/FS/FS/part_export/cyrus.pm b/FS/FS/part_export/cyrus.pm
new file mode 100644
index 0000000..84c9e5a
--- /dev/null
+++ b/FS/FS/part_export/cyrus.pm
@@ -0,0 +1,120 @@
+package FS::part_export::cyrus;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'server' => { label=>'IMAP server' },
+ 'username' => { label=>'Admin username' },
+ 'password' => { label=>'Admin password' },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to Cyrus IMAP server',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => <<'END'
+Integration with
+<a href="http://asg.web.cmu.edu/cyrus/imapd/">Cyrus IMAP Server</a>.
+Cyrus::IMAP::Admin should be installed locally and the connection to the
+server secured. <B>svc_acct.quota</B>, if available, is used to set the
+Cyrus quota.
+END
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+ $self->cyrus_queue( $svc_acct->svcnum, 'insert',
+ $svc_acct->username, $svc_acct->quota );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return "can't change username using Cyrus"
+ if $old->username ne $new->username;
+ return '';
+# #return '' unless $old->_password ne $new->_password;
+# $self->cyrus_queue( $new->svcnum,
+# 'replace', $new->username, $new->_password );
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->cyrus_queue( $svc_acct->svcnum, 'delete',
+ $svc_acct->username );
+}
+
+#a good idea to queue anything that could fail or take any time
+sub cyrus_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::cyrus::cyrus_$method",
+ };
+ $queue->insert(
+ $self->option('server'),
+ $self->option('username'),
+ $self->option('password'),
+ @_
+ );
+}
+
+sub cyrus_insert { #subroutine, not method
+ my $client = cyrus_connect(shift, shift, shift);
+ my( $username, $quota ) = @_;
+ my $rc = $client->create("user.$username");
+ my $error = $client->error;
+ die "creating user.$username: $error" if $error;
+
+ $rc = $client->setacl("user.$username", $username => 'all' );
+ $error = $client->error;
+ die "setacl user.$username: $error" if $error;
+
+ if ( $quota ) {
+ $rc = $client->setquota("user.$username", 'STORAGE' => $quota );
+ $error = $client->error;
+ die "setquota user.$username: $error" if $error;
+ }
+
+}
+
+sub cyrus_delete { #subroutine, not method
+ my ( $server, $admin_username, $password_username, $username ) = @_;
+ my $client = cyrus_connect($server, $admin_username, $password_username);
+
+ my $rc = $client->setacl("user.$username", $admin_username => 'all' );
+ my $error = $client->error;
+ die $error if $error;
+
+ $rc = $client->delete("user.$username");
+ $error = $client->error;
+ die $error if $error;
+}
+
+sub cyrus_connect {
+
+ my( $server, $admin_username, $admin_password ) = @_;
+
+ eval "use Cyrus::IMAP::Admin;";
+
+ my $client = Cyrus::IMAP::Admin->new($server);
+ $client->authenticate(
+ -user => $admin_username,
+ -mechanism => "login",
+ -password => $admin_password,
+ );
+ $client;
+
+}
+
+#sub cyrus_replace { #subroutine, not method
+#}
+
+1;
+
diff --git a/FS/FS/part_export/domain_shellcommands.pm b/FS/FS/part_export/domain_shellcommands.pm
new file mode 100644
index 0000000..994c113
--- /dev/null
+++ b/FS/FS/part_export/domain_shellcommands.pm
@@ -0,0 +1,165 @@
+package FS::part_export::domain_shellcommands;
+
+use strict;
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'useradd' => { label=>'Insert command',
+ default=>'',
+ },
+ 'userdel' => { label=>'Delete command',
+ default=>'',
+ },
+ 'usermod' => { label=>'Modify command',
+ default=>'',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_domain',
+ 'desc' => 'Run remote commands via SSH, for domains (qmail, ISPMan).',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Run remote commands via SSH, for domains. You will need to
+<a href="../docs/ssh.html">setup SSH for unattended operation</a>.
+<BR><BR>Use these buttons for some useful presets:
+<UL>
+ <LI>
+ <INPUT TYPE="button" VALUE="qmail catchall .qmail-domain-default maintenance" onClick='
+ this.form.useradd.value = "[ \"$uid\" -a \"$gid\" -a \"$dir\" -a \"$qdomain\" ] && [ -e $dir/.qmail-$qdomain-default ] || { touch $dir/.qmail-$qdomain-default; chown $uid:$gid $dir/.qmail-$qdomain-default; }";
+ this.form.userdel.value = "";
+ this.form.usermod.value = "";
+ '>
+ <LI>
+ <INPUT TYPE="button" VALUE="ISPMan CLI" onClick='
+ this.form.useradd.value = "/usr/local/ispman/bin/ispman.addDomain -d $domain changeme";
+ this.form.userdel.value = "/usr/local/ispman/bin/ispman.deleteDomain -d $domain";
+ this.form.usermod.value = "";
+ '>
+</UL>
+The following variables are available for interpolation (prefixed with <code>new_</code> or <code>old_</code> for replace operations):
+<UL>
+ <LI><code>$domain</code>
+ <LI><code>$qdomain</code> - domain with periods replaced by colons
+ <LI><code>$uid</code> - of catchall account
+ <LI><code>$gid</code> - of catchall account
+ <LI><code>$dir</code> - home directory of catchall account
+ <LI>All other fields in
+ <a href="../docs/schema.html#svc_domain">svc_domain</a> are also available.
+</UL>
+END
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self) = shift;
+ $self->_export_command('useradd', @_);
+}
+
+sub _export_delete {
+ my($self) = shift;
+ $self->_export_command('userdel', @_);
+}
+
+sub _export_command {
+ my ( $self, $action, $svc_domain) = (shift, shift, shift);
+ my $command = $self->option($action);
+ return '' if $command =~ /^\s*$/;
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${$_} = $svc_domain->getfield($_) foreach $svc_domain->fields;
+ }
+ ( $qdomain = $domain ) =~ s/\./:/g; #see dot-qmail(5): EXTENSION ADDRESSES
+
+ if ( $svc_domain->catchall ) {
+ no strict 'refs';
+ my $svc_acct = $svc_domain->catchall_svc_acct;
+ ${$_} = $svc_acct->getfield($_) foreach qw(uid gid dir);
+ } else {
+ no strict 'refs';
+ ${$_} = '' foreach qw(uid gid dir);
+ }
+
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $svc_domain->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+sub _export_replace {
+ my($self, $new, $old ) = (shift, shift, shift);
+ my $command = $self->option('usermod');
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${"old_$_"} = $old->getfield($_) foreach $old->fields;
+ ${"new_$_"} = $new->getfield($_) foreach $new->fields;
+ }
+ ( $old_qdomain = $old_domain ) =~ s/\./:/g; #see dot-qmail(5): EXTENSION ADDRESSES
+ ( $new_qdomain = $new_domain ) =~ s/\./:/g; #see dot-qmail(5): EXTENSION ADDRESSES
+
+ {
+ no strict 'refs';
+
+ if ( $old->catchall ) {
+ my $svc_acct = $old->catchall_svc_acct;
+ ${"old_$_"} = $svc_acct->getfield($_) foreach qw(uid gid dir);
+ } else {
+ ${"old_$_"} = '' foreach qw(uid gid dir);
+ }
+ if ( $new->catchall ) {
+ my $svc_acct = $new->catchall_svc_acct;
+ ${"new_$_"} = $svc_acct->getfield($_) foreach qw(uid gid dir);
+ } else {
+ ${"new_$_"} = '' foreach qw(uid gid dir);
+ }
+
+ }
+
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $new->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+#a good idea to queue anything that could fail or take any time
+sub shellcommands_queue {
+ my( $self, $svcnum ) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::domain_shellcommands::ssh_cmd",
+ };
+ $queue->insert( @_ );
+}
+
+sub ssh_cmd { #subroutine, not method
+ use Net::SSH '0.08';
+ &Net::SSH::ssh_cmd( { @_ } );
+}
+
+#sub shellcommands_insert { #subroutine, not method
+#}
+#sub shellcommands_replace { #subroutine, not method
+#}
+#sub shellcommands_delete { #subroutine, not method
+#}
+
+1;
+
diff --git a/FS/FS/part_export/domain_sql.pm b/FS/FS/part_export/domain_sql.pm
new file mode 100644
index 0000000..0ce1b16
--- /dev/null
+++ b/FS/FS/part_export/domain_sql.pm
@@ -0,0 +1,238 @@
+package FS::part_export::domain_sql;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+#quite a bit of false laziness w/acct_sql - some stuff should be generalized
+#out to a "dababase base class"
+
+tie my %options, 'Tie::IxHash',
+ 'datasrc' => { label => 'DBI data source' },
+ 'username' => { label => 'Database username' },
+ 'password' => { label => 'Database password' },
+ 'table' => { label => 'Database table' },
+ 'schema' => { label =>
+ 'Database schema mapping to Freeside methods.',
+ type => 'textarea',
+ },
+ 'static' => { label =>
+ 'Database schema mapping to static values.',
+ type => 'textarea',
+ },
+ 'primary_key' => { label => 'Database primary key' },
+;
+
+tie my %postfix_transport_map, 'Tie::IxHash',
+ 'domain' => 'domain'
+;
+my $postfix_transport_map =
+ join('\n', map "$_ $postfix_transport_map{$_}",
+ keys %postfix_transport_map );
+tie my %postfix_transport_static, 'Tie::IxHash',
+ 'transport' => 'virtual:',
+;
+my $postfix_transport_static =
+ join('\n', map "$_ $postfix_transport_static{$_}",
+ keys %postfix_transport_static );
+
+%info = (
+ 'svc' => 'svc_domain',
+ 'desc' => 'Real time export of domains to SQL databases '.
+ '(postfix, others?)',
+ 'options' => \%options,
+ 'notes' => <<END
+Export domains (svc_domain records) to SQL databases. Currently this is a
+simple export with a default for Postfix, but it can be extended for other
+uses.
+
+<BR><BR>Use these buttons for useful presets:
+<UL>
+ <LI><INPUT TYPE="button" VALUE="postfix_transport" onClick='
+ this.form.table.value = "transport";
+ this.form.schema.value = "$postfix_transport_map";
+ this.form.static.value = "$postfix_transport_static";
+ this.form.primary_key.value = "domain";
+ '>
+</UL>
+END
+);
+
+sub _schema_map { shift->_map('schema'); }
+sub _static_map { shift->_map('static'); }
+
+sub _map {
+ my $self = shift;
+ map { /^\s*(\S+)\s*(\S+)\s*$/ } split("\n", $self->option(shift) );
+}
+
+sub _export_insert {
+ my($self, $svc_domain) = (shift, shift);
+
+ my %schema = $self->_schema_map;
+ my %static = $self->_static_map;
+
+ my %record = ( ( map { $_ => $static{$_} } keys %static ),
+ ( map { my $method = $schema{$_};
+ $_ => $svc_domain->$method();
+ }
+ keys %schema
+ )
+ );
+
+ my $err_or_queue =
+ $self->domain_sql_queue(
+ $svc_domain->svcnum,
+ 'insert',
+ $self->option('table'),
+ %record
+ );
+ return $err_or_queue unless ref($err_or_queue);
+
+ '';
+}
+
+sub _export_replace {
+ my($self, $new, $old) = (shift, shift, shift);
+
+ my %schema = $self->_schema_map;
+ my %static = $self->_static_map;
+
+ my @primary_key = ();
+ if ( $self->option('primary_key') =~ /,/ ) {
+ foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
+ my $keymap = $schema{$key};
+ push @primary_key, $old->$keymap();
+ }
+ } else {
+ my $keymap = $map{$self->option('primary_key')};
+ push @primary_key, $old->$keymap();
+ }
+
+ my %record = ( ( map { $_ => $static{$_} } keys %static ),
+ ( map { my $method = $schema{$_};
+ $_ => $new->$method();
+ }
+ keys %schema
+ )
+ );
+
+ my $err_or_queue = $self->domain_sql_queue(
+ $new->svcnum,
+ 'replace',
+ $self->option('table'),
+ $self->option('primary_key'), @primary_key,
+ %record,
+ );
+ return $err_or_queue unless ref($err_or_queue);
+ '';
+}
+
+sub _export_delete {
+ my ( $self, $svc_domain ) = (shift, shift);
+
+ my %schema = $self->_schema_map;
+ my %static = $self->_static_map;
+
+ my %primary_key = ();
+ if ( $self->option('primary_key') =~ /,/ ) {
+ foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
+ my $keymap = $map{$key};
+ $primary_key{ $key } = $svc_domain->$keymap();
+ }
+ } else {
+ my $keymap = $map{$self->option('primary_key')};
+ $primary_key{ $self->option('primary_key') } = $svc_domain->$keymap(),
+ }
+
+ my $err_or_queue = $self->domain_sql_queue(
+ $svc_domain->svcnum,
+ 'delete',
+ $self->option('table'),
+ %primary_key,
+ #$self->option('primary_key') => $svc_domain->$keymap(),
+ );
+ return $err_or_queue unless ref($err_or_queue);
+ '';
+}
+
+sub domain_sql_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::domain_sql::domain_sql_$method",
+ };
+ $queue->insert(
+ $self->option('datasrc'),
+ $self->option('username'),
+ $self->option('password'),
+ @_,
+ ) or $queue;
+}
+
+sub domain_sql_insert { #subroutine, not method
+ my $dbh = domain_sql_connect(shift, shift, shift);
+ my( $table, %record ) = @_;
+
+ my $sth = $dbh->prepare(
+ "INSERT INTO $table ( ". join(", ", keys %record).
+ " ) VALUES ( ". join(", ", map '?', keys %record ). " )"
+ ) or die $dbh->errstr;
+
+ $sth->execute( values(%record) )
+ or die "can't insert into $table table: ". $sth->errstr;
+
+ $dbh->disconnect;
+}
+
+sub domain_sql_delete { #subroutine, not method
+ my $dbh = domain_sql_connect(shift, shift, shift);
+ my( $table, %record ) = @_;
+
+ my $sth = $dbh->prepare(
+ "DELETE FROM $table WHERE ". join(' AND ', map "$_ = ? ", keys %record )
+ ) or die $dbh->errstr;
+
+ $sth->execute( map $record{$_}, keys %record )
+ or die "can't delete from $table table: ". $sth->errstr;
+
+ $dbh->disconnect;
+}
+
+sub domain_sql_replace { #subroutine, not method
+ my $dbh = domain_sql_connect(shift, shift, shift);
+
+ my( $table, $pkey ) = ( shift, shift );
+
+ my %primary_key = ();
+ if ( $pkey =~ /,/ ) {
+ foreach my $key ( split(/\s*,\s*/, $pkey ) ) {
+ $primary_key{$key} = shift;
+ }
+ } else {
+ $primary_key{$pkey} = shift;
+ }
+
+ my %record = @_;
+
+ my $sth = $dbh->prepare(
+ "UPDATE $table".
+ ' SET '. join(', ', map "$_ = ?", keys %record ).
+ ' WHERE '. join(' AND ', map "$_ = ?", keys %primary_key )
+ ) or die $dbh->errstr;
+
+ $sth->execute( values(%record), values(%primary_key) );
+
+ $dbh->disconnect;
+}
+
+sub domain_sql_connect {
+ #my($datasrc, $username, $password) = @_;
+ #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
+ DBI->connect(@_) or die $DBI::errstr;
+}
+
+1;
+
diff --git a/FS/FS/part_export/everyone_net.pm b/FS/FS/part_export/everyone_net.pm
new file mode 100644
index 0000000..e04318e
--- /dev/null
+++ b/FS/FS/part_export/everyone_net.pm
@@ -0,0 +1,132 @@
+package FS::part_export::everyone_net;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'clientID' => { label=>'clientID' },
+ 'password' => { label=>'Password' },
+ #'workgroup' => { label=>'Default Workgroup' },
+ 'debug' => { label=>'Enable debugging',
+ type=>'checkbox' },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to Everyone.net outsourced mail service',
+ 'options'=> \%options,
+ 'notes' => <<'END'
+Real-time export to
+<a href="http://www.cp.net/">Everyone.net</a> via the XRC Remote API.
+Requires installation of
+<a href="http://search.cpan.org/dist/Net-XRC">Net::XRC</a>
+from CPAN.
+END
+);
+
+sub rebless { shift; }
+
+# experiement: want the status of these right away (don't want account to
+# create or whatever and then get error in the queue from dup username or
+# something), so no queueing
+
+sub _export_insert {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ eval "use Net::XRC qw(:types);";
+ return $@ if $@;
+
+ $self->_xrc_command( 'createUser',
+ $svc_acct->domain,
+ [],
+ string($svc_acct->username),
+ string($svc_acct->_password),
+ );
+}
+
+sub _xrc_command {
+ my( $self, $method, $domain, @args ) = @_;
+
+ eval "use Net::XRC qw(:types);";
+ return $@ if $@;
+
+ local($Net::XRC::DEBUG) = 1
+ if $self->option('debug');
+
+ my $xrc = new Net::XRC (
+ 'clientID' => $self->option('clientID'),
+ 'password' => $self->option('password'),
+ );
+
+ my $dresponse = $xrc->lookupMXReadyClientIDByEmailDomain( string($domain) );
+ return $dresponse->error unless $dresponse->is_success;
+ my $clientID = $dresponse->content;
+ return "clientID for domain $domain not found"
+ if $clientID == -1;
+
+ my $response = $xrc->$method($clientID, @args);
+ return $response->error unless $response->is_success;
+ '';
+
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ eval "use Net::XRC qw(:types);";
+ return $@ if $@;
+
+ return "can't change domain with Everyone.net"
+ if $old->domain ne $new->domain;
+ return "can't change username with Everyone.net"
+ if $old->username ne $new->username;
+ return '' unless $old->_password ne $new->_password;
+
+ $self->_xrc_command( 'setUserPassword',
+ $new->domain,
+ string($new->username),
+ string($new->_password),
+ );
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ eval "use Net::XRC qw(:types);";
+ return $@ if $@;
+
+ $self->_xrc_command( 'deleteUser',
+ $svc_acct->domain,
+ string($svc_acct->username),
+ );
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ eval "use Net::XRC qw(:types);";
+ return $@ if $@;
+
+ $self->_xrc_command( 'suspendUser',
+ $svc_acct->domain,
+ string($svc_acct->username),
+ );
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ eval "use Net::XRC qw(:types);";
+ return $@ if $@;
+
+ $self->_xrc_command( 'unsuspendUser',
+ $svc_acct->domain,
+ string($svc_acct->username),
+ );
+}
+
+1;
+
diff --git a/FS/FS/part_export/forward_shellcommands.pm b/FS/FS/part_export/forward_shellcommands.pm
new file mode 100644
index 0000000..cee24e4
--- /dev/null
+++ b/FS/FS/part_export/forward_shellcommands.pm
@@ -0,0 +1,182 @@
+package FS::part_export::forward_shellcommands;
+
+use strict;
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'useradd' => { label=>'Insert command',
+ default=>'',
+ },
+ 'userdel' => { label=>'Delete command',
+ default=>'',
+ },
+ 'usermod' => { label=>'Modify command',
+ default=>'',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_forward',
+ 'desc' => 'Run remote commands via SSH, for forwards',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Run remote commands via SSH, for forwards. You will need to
+<a href="../docs/ssh.html">setup SSH for unattended operation</a>.
+<BR><BR>Use these buttons for some useful presets:
+<UL>
+ <LI>
+ <INPUT TYPE="button" VALUE="text vpopmail maintenance" onClick='
+ this.form.useradd.value = "[ -d /home/vpopmail/domains/$domain/$username ] && { echo \"$destination\" > /home/vpopmail/domains/$domain/$username/.qmail; chown vpopmail:vchkpw /home/vpopmail/domains/$domain/$username/.qmail; }";
+ this.form.userdel.value = "rm /home/vpopmail/domains/$domain/$username/.qmail";
+ this.form.usermod.value = "mv /home/vpopmail/domains/$old_domain/$old_username/.qmail /home/vpopmail/domains/$new_domain/$new_username; [ \"$old_destination\" != \"$new_destination\" ] && { echo \"$new_destination\" > /home/vpopmail/domains/$new_domain/$new_username/.qmail; chown vpopmail:vchkpw /home/vpopmail/domains/$new_domain/$new_username/.qmail; }";
+ '>
+ <LI>
+ <INPUT TYPE="button" VALUE="ISPMan CLI" onClick='
+ this.form.useradd.value = "";
+ this.form.userdel.value = "";
+ this.form.usermod.value = "";
+ '>
+</UL>
+The following variables are available for interpolation (prefixed with
+<code>new_</code> or <code>old_</code> for replace operations):
+<UL>
+ <LI><code>$username</code> - username of forward source
+ <LI><code>$domain</code> - domain of forward source
+ <LI><code>$source</code> - forward source ($username@$domain)
+ <LI><code>$destination</code> - forward destination
+ <LI>All other fields in <a href="../docs/schema.html#svc_forward">svc_forward</a> are also available.
+</UL>
+END
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self) = shift;
+ $self->_export_command('useradd', @_);
+}
+
+sub _export_delete {
+ my($self) = shift;
+ $self->_export_command('userdel', @_);
+}
+
+sub _export_command {
+ my ( $self, $action, $svc_forward ) = (shift, shift, shift);
+ my $command = $self->option($action);
+ return '' if $command =~ /^\s*$/;
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${$_} = $svc_forward->getfield($_) foreach $svc_forward->fields;
+ }
+
+ if ( $svc_forward->srcsvc ) {
+ my $srcsvc_acct = $svc_forward->srcsvc_acct;
+ $username = $srcsvc_acct->username;
+ $domain = $srcsvc_acct->domain;
+ $source = $srcsvc_acct->email;
+ } else {
+ $source = $svc_forward->src;
+ ( $username, $domain ) = split(/\@/, $source);
+ }
+
+ if ($svc_forward->dstsvc) {
+ $destination = $svc_forward->dstsvc_acct->email;
+ } else {
+ $destination = $svc_forward->dst;
+ }
+
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $svc_forward->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ my $command = $self->option('usermod');
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${"old_$_"} = $old->getfield($_) foreach $old->fields;
+ ${"new_$_"} = $new->getfield($_) foreach $new->fields;
+ }
+
+ if ( $old->srcsvc ) {
+ my $srcsvc_acct = $old->srcsvc_acct;
+ $old_username = $srcsvc_acct->username;
+ $old_domain = $srcsvc_acct->domain;
+ $old_source = $srcsvc_acct->email;
+ } else {
+ $old_source = $old->src;
+ ( $old_username, $old_domain ) = split(/\@/, $old_source);
+ }
+
+ if ( $old->dstsvc ) {
+ $old_destination = $old->dstsvc_acct->email;
+ } else {
+ $old_destination = $old->dst;
+ }
+
+ if ( $new->srcsvc ) {
+ my $srcsvc_acct = $new->srcsvc_acct;
+ $new_username = $srcsvc_acct->username;
+ $new_domain = $srcsvc_acct->domain;
+ $new_source = $srcsvc_acct->email;
+ } else {
+ $new_source = $new->src;
+ ( $new_username, $new_domain ) = split(/\@/, $new_source);
+ }
+
+ if ( $new->dstsvc ) {
+ $new_destination = $new->dstsvc_acct->email;
+ } else {
+ $new_destination = $new->dst;
+ }
+
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $new->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+#a good idea to queue anything that could fail or take any time
+sub shellcommands_queue {
+ my( $self, $svcnum ) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::forward_shellcommands::ssh_cmd",
+ };
+ $queue->insert( @_ );
+}
+
+sub ssh_cmd { #subroutine, not method
+ use Net::SSH '0.08';
+ &Net::SSH::ssh_cmd( { @_ } );
+}
+
+#sub shellcommands_insert { #subroutine, not method
+#}
+#sub shellcommands_replace { #subroutine, not method
+#}
+#sub shellcommands_delete { #subroutine, not method
+#}
+
+1;
+
diff --git a/FS/FS/part_export/globalpops_voip.pm b/FS/FS/part_export/globalpops_voip.pm
new file mode 100644
index 0000000..3bd5783
--- /dev/null
+++ b/FS/FS/part_export/globalpops_voip.pm
@@ -0,0 +1,370 @@
+package FS::part_export::globalpops_voip;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::Record qw(qsearch dbh);
+use FS::part_export;
+use FS::phone_avail;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'login' => { label=>'GlobalPOPs Media Services API login' },
+ 'password' => { label=>'GlobalPOPs Media Services API password' },
+ 'endpointgroup' => { label=>'GlobalPOPs endpoint group number' },
+ 'dry_run' => { label=>"Test mode - don't actually provision" },
+;
+
+%info = (
+ 'svc' => 'svc_phone',
+ 'desc' => 'Provision phone numbers to GlobalPOPs VoIP',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Requires installation of
+<a href="http://search.cpan.org/dist/Net-GlobalPOPs-MediaServicesAPI">Net::GlobalPOPs::MediaServicesAPI</a>
+from CPAN.
+END
+);
+
+sub rebless { shift; }
+
+sub get_dids {
+ my $self = shift;
+ my %opt = ref($_[0]) ? %{$_[0]} : @_;
+
+ my %search = ();
+ # 'orderby' => 'npa', #but it doesn't seem to work :/
+
+ if ( $opt{'areacode'} && $opt{'exchange'} ) { #return numbers
+ %getdids = ( 'npa' => $opt{'areacode'},
+ 'nxx' => $opt{'exchange'},
+ );
+ } elsif ( $opt{'areacode'} ) { #return city (npa-nxx-XXXX)
+ %getdids = ( 'npa' => $opt{'areacode'} );
+ } elsif ( $opt{'state'} ) {
+
+ my @avail = qsearch({
+ 'table' => 'phone_avail',
+ 'hashref' => { 'exportnum' => $self->exportnum,
+ 'countrycode' => '1', #don't hardcode me when gp goes int'l
+ 'state' => $opt{'state'},
+ },
+ 'order_by' => 'ORDER BY npa',
+ });
+
+ return [ map $_->npa, @avail ] if @avail; #return cached area codes instead
+
+ #otherwise, search for em
+ %getdids = ( 'state' => $opt{'state'} );
+
+ }
+
+ my $dids = $self->gp_command('getDIDs', %getdids);
+
+ #use Data::Dumper;
+ #warn Dumper($dids);
+
+ my $search = $dids->{'search'};
+
+ if ( $search->{'statuscode'} == 302200 ) {
+ return [];
+ } elsif ( $search->{'statuscode'} != 100 ) {
+ die "Error running globalpop getDIDs: ".
+ $search->{'statuscode'}. ': '. $search->{'status'}; #die??
+ }
+
+ my @return = ();
+
+ #my $latas = $search->{state}{lata};
+ my %latas;
+ if ( grep $search->{state}{lata}{$_}, qw(name rate_center) ) {
+ %latas = map $search->{state}{lata}{$_},
+ qw(name rate_center);
+ } else {
+ %latas = %{ $search->{state}{lata} };
+ }
+
+ foreach my $lata ( keys %latas ) {
+
+ #warn "LATA $lata";
+
+ #my $l = $latas{$lata};
+ #$l = $l->{rate_center} if exists $l->{rate_center};
+
+ my $lata_dids = $self->gp_command('getDIDs', %getdids, 'lata'=>$lata);
+ my $lata_search = $lata_dids->{'search'};
+ unless ( $lata_search->{'statuscode'} == 100 ) {
+ die "Error running globalpop getDIDs: ". $lata_search->{'status'}; #die??
+ }
+
+ my $l = $lata_search->{state}{lata}{'rate_center'};
+
+ #use Data::Dumper;
+ #warn Dumper($l);
+
+ my %rate_center;
+ if ( grep $l->{$_}, qw(name friendlyname) ) {
+ %rate_center = map $l->{$_},
+ qw(name friendlyname);
+ } else {
+ %rate_center = %$l;
+ }
+
+ foreach my $rate_center ( keys %rate_center ) {
+
+ #warn "rate center $rate_center";
+
+ my $rc = $rate_center{$rate_center};
+ $rc = $rc->{friendlyname} if exists $rc->{friendlyname};
+
+ my @r = ();
+ if ( exists($rc->{npa}) ) {
+ @r = ($rc);
+ } else {
+ @r = map { { 'name'=>$_, %{ $rc->{$_} } }; } keys %$rc
+ }
+
+ foreach my $r (@r) {
+
+ my @npa = ();
+ if ( exists($r->{npa}{name}) ) {
+ @npa = ($r->{npa})
+ } else {
+ @npa = map { { 'name'=>$_, %{ $r->{npa}{$_} } } } keys %{ $r->{npa} };
+ }
+
+ foreach my $npa (@npa) {
+
+ if ( $opt{'areacode'} && $opt{'exchange'} ) { #return numbers
+
+ #warn Dumper($npa);
+
+ my $tn = $npa->{nxx}{tn} || $npa->{nxx}{$opt{'exchange'}}{tn};
+
+ my @tn = ref($tn) ? @$tn : ($tn);
+ #push @return, @tn;
+ push @return, map {
+ if ( /^\s*(\d{3})(\d{3})(\d{4})\s*$/ ) {
+ "$1-$2-$3";
+ } else {
+ $_;
+ }
+ }
+ @tn;
+
+ } elsif ( $opt{'areacode'} ) { #return city (npa-nxx-XXXX)
+
+ if ( $npa->{nxx}{name} ) {
+ @nxx = ( $npa->{nxx}{name} );
+ } else {
+ @nxx = keys %{ $npa->{nxx} };
+ }
+
+ push @return, map { $r->{name}. ' ('. $npa->{name}. "-$_-XXXX)"; }
+ @nxx;
+
+ } elsif ( $opt{'state'} ) { #and not other things, then return areacode
+ #my $ac = $npa->{name};
+ #use Data::Dumper;
+ #warn Dumper($r) unless length($ac) == 3;
+
+ push @return, $npa->{name}
+ unless grep { $_ eq $npa->{name} } @return;
+
+ } else {
+ warn "WARNING: returning nothing for get_dids without known options"; #?
+ }
+
+ } #foreach my $npa
+
+ } #foreach my $r
+
+ } #foreach my $rate_center
+
+ } #foreach my $lata
+
+ if ( $opt{'areacode'} && $opt{'exchange'} ) { #return numbers
+ @return = sort { $a cmp $b } @return; #string comparison actually dwiw
+ } elsif ( $opt{'areacode'} ) { #return city (npa-nxx-XXXX)
+ @return = sort { lc($a) cmp lc($b) } @return;
+ } elsif ( $opt{'state'} ) { #and not other things, then return areacode
+
+ #populate cache
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $errmsg = 'WARNING: error populating phone availability cache: ';
+ my $error = '';
+ foreach my $return (@return) {
+ my $phone_avail = new FS::phone_avail {
+ 'exportnum' => $self->exportnum,
+ 'countrycode' => '1', #don't hardcode me when gp goes int'l
+ 'state' => $opt{'state'},
+ 'npa' => $return,
+ };
+ $error = $phone_avail->insert();
+ if ( $error ) {
+ warn $errmsg.$error;
+ last;
+ }
+ }
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ } else {
+ $dbh->commit or warn $errmsg.$dbh->errstr if $oldAutoCommit;
+ }
+
+ #end populate cache
+
+ #@return = sort { (split(' ', $a))[0] <=> (split(' ', $b))[0] } @return;
+ @return = sort { $a <=> $b } @return;
+ } else {
+ warn "WARNING: returning nothing for get_dids without known options"; #?
+ }
+
+ \@return;
+
+}
+
+sub gp_command {
+ my( $self, $command, @args ) = @_;
+
+ eval "use Net::GlobalPOPs::MediaServicesAPI;";
+ die $@ if $@;
+
+ my $gp = Net::GlobalPOPs::MediaServicesAPI->new(
+ 'login' => $self->option('login'),
+ 'password' => $self->option('password'),
+ #'debug' => $debug,
+ );
+
+ $gp->$command(@args);
+}
+
+
+sub _export_insert {
+ my( $self, $svc_phone ) = (shift, shift);
+
+ return '' if $self->option('dry_run');
+
+ #we want to provision and catch errors now, not queue
+
+ my $r = $self->gp_command('reserveDID',
+ 'did' => $svc_phone->phonenum,
+ 'minutes' => 1,
+ 'endpointgroup' => $self->option('endpointgroup'),
+ );
+
+ my $rdid = $r->{did};
+
+ if ( $rdid->{'statuscode'} != 100 ) {
+ return "Error running globalpop reserveDID: ".
+ $rdid->{'statuscode'}. ': '. $rdid->{'status'};
+ }
+
+ my $a = $self->gp_command('assignDID',
+ 'did' => $svc_phone->phonenum,
+ 'endpointgroup' => $self->option('endpointgroup'),
+ #'rewrite'
+ #'cnam'
+ );
+
+ my $adid = $a->{did};
+
+ if ( $adid->{'statuscode'} != 100 ) {
+ return "Error running globalpop assignDID: ".
+ $adid->{'statuscode'}. ': '. $adid->{'status'};
+ }
+
+ '';
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ #hmm, what's to change?
+ '';
+}
+
+sub _export_delete {
+ my( $self, $svc_phone ) = (shift, shift);
+
+ return '' if $self->option('dry_run');
+
+ #probably okay to queue the deletion...?
+ #but hell, let's do it inline anyway, who wants phone numbers hanging around
+
+ my $r = $self->gp_command('releaseDID',
+ 'did' => $svc_phone->phonenum,
+ );
+
+ my $rdid = $r->{did};
+
+ if ( $rdid->{'statuscode'} != 100 ) {
+ return "Error running globalpop releaseDID: ".
+ $rdid->{'statuscode'}. ': '. $rdid->{'status'};
+ }
+
+ '';
+}
+
+sub _export_suspend {
+ my( $self, $svc_phone ) = (shift, shift);
+ #nop for now
+ '';
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_phone ) = (shift, shift);
+ #nop for now
+ '';
+}
+
+#hmm, might forgo queueing entirely for most things, data is too much of a pita
+#sub globalpops_voip_queue {
+# my( $self, $svcnum, $method ) = (shift, shift, shift);
+# my $queue = new FS::queue {
+# 'svcnum' => $svcnum,
+# 'job' => 'FS::part_export::globalpops_voip::globalpops_voip_command',
+# };
+# $queue->insert(
+# $self->option('login'),
+# $self->option('password'),
+# $method,
+# @_,
+# );
+#}
+
+sub globalpops_voip_command {
+ my($login, $password, $method, @args) = @_;
+
+ eval "use Net::GlobalPOPs::MediaServicesAPI;";
+ die $@ if $@;
+
+ my $gp = new Net::GlobalPOPs
+ 'login' => $login,
+ 'password' => $password,
+ #'debug' => 1,
+ ;
+
+ my $return = $gp->$method( @args );
+
+ #$return->{'status'}
+ #$return->{'statuscode'}
+
+ die $return->{'status'} if $return->{'statuscode'};
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/http.pm b/FS/FS/part_export/http.pm
new file mode 100644
index 0000000..55d8329
--- /dev/null
+++ b/FS/FS/part_export/http.pm
@@ -0,0 +1,134 @@
+package FS::part_export::http;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'method' => { label =>'Method',
+ type =>'select',
+ #options =>[qw(POST GET)],
+ options =>[qw(POST)],
+ default =>'POST' },
+ 'url' => { label => 'URL', default => 'http://', },
+ 'insert_data' => {
+ label => 'Insert data',
+ type => 'textarea',
+ default => join("\n",
+ 'DomainName $svc_x->domain',
+ 'Email ( grep { $_ !~ /^(POST|FAX)$/ } $svc_x->cust_svc->cust_pkg->cust_main->invoicing_list)[0]',
+ 'test 1',
+ 'reseller $svc_x->cust_svc->cust_pkg->part_pkg->pkg =~ /reseller/i',
+ ),
+ },
+ 'delete_data' => {
+ label => 'Delete data',
+ type => 'textarea',
+ default => join("\n",
+ ),
+ },
+ 'replace_data' => {
+ label => 'Replace data',
+ type => 'textarea',
+ default => join("\n",
+ ),
+ },
+;
+
+%info = (
+ 'svc' => 'svc_domain',
+ 'desc' => 'Send an HTTP or HTTPS GET or POST request',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Send an HTTP or HTTPS GET or POST to the specified URL. For HTTPS support,
+<a href="http://search.cpan.org/dist/Crypt-SSLeay">Crypt::SSLeay</a>
+or <a href="http://search.cpan.org/dist/IO-Socket-SSL">IO::Socket::SSL</a>
+is required.
+END
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my $self = shift;
+ $self->_export_command('insert', @_);
+}
+
+sub _export_delete {
+ my $self = shift;
+ $self->_export_command('delete', @_);
+}
+
+sub _export_command {
+ my( $self, $action, $svc_x ) = ( shift, shift, shift );
+
+ return unless $self->option("${action}_data");
+
+ $self->http_queue( $svc_x->svcnum,
+ $self->option('method'),
+ $self->option('url'),
+ map {
+ /^\s*(\S+)\s+(.*)$/ or /()()/;
+ my( $field, $value_expression ) = ( $1, $2 );
+ my $value = eval $value_expression;
+ die $@ if $@;
+ ( $field, $value );
+ } split(/\n/, $self->option("${action}_data") )
+ );
+
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = ( shift, shift, shift );
+
+ return unless $self->option('replace_data');
+
+ $self->http_queue( $svc_x->svcnum,
+ $self->option('method'),
+ $self->option('url'),
+ map {
+ /^\s*(\S+)\s+(.*)$/ or /()()/;
+ my( $field, $value_expression ) = ( $1, $2 );
+ die $@ if $@;
+ ( $field, $value );
+ } split(/\n/, $self->option('replace_data') )
+ );
+
+}
+
+sub http_queue {
+ my($self, $svcnum) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::http::http",
+ };
+ $queue->insert( @_ );
+}
+
+sub http {
+ my($method, $url, @data) = @_;
+
+ $method = lc($method);
+
+ eval "use LWP::UserAgent;";
+ die "using LWP::UserAgent: $@" if $@;
+ eval "use HTTP::Request::Common;";
+ die "using HTTP::Request::Common: $@" if $@;
+
+ my $ua = LWP::UserAgent->new;
+
+ #my $response = $ua->$method(
+ # $url, \%data,
+ # 'Content-Type'=>'application/x-www-form-urlencoded'
+ #);
+ my $req = HTTP::Request::Common::POST( $url, \@data );
+ my $response = $ua->request($req);
+
+ die $response->error_as_HTML if $response->is_error;
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/infostreet.pm b/FS/FS/part_export/infostreet.pm
new file mode 100644
index 0000000..ef16c7c
--- /dev/null
+++ b/FS/FS/part_export/infostreet.pm
@@ -0,0 +1,277 @@
+package FS::part_export::infostreet;
+
+use vars qw(@ISA %info %infostreet2cust_main $DEBUG);
+use Tie::IxHash;
+use FS::UID qw(dbh);
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'url' => { label=>'XML-RPC Access URL', },
+ 'login' => { label=>'InfoStreet login', },
+ 'password' => { label=>'InfoStreet password', },
+ 'groupID' => { label=>'InfoStreet groupID', },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to InfoStreet streetSmartAPI',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => <<'END'
+Real-time export to
+<a href="http://www.infostreet.com/">InfoStreet</a> streetSmartAPI.
+Requires installation of
+<a href="http://search.cpan.org/dist/Frontier-Client">Frontier::Client</a> from CPAN.
+END
+);
+
+$DEBUG = 0;
+
+%infostreet2cust_main = (
+ 'firstName' => 'first',
+ 'lastName' => 'last',
+ 'address1' => 'address1',
+ 'address2' => 'address2',
+ 'city' => 'city',
+ 'state' => 'state',
+ 'zipCode' => 'zip',
+ 'country' => 'country',
+ 'phoneNumber' => 'daytime',
+ 'faxNumber' => 'night', #noment-request...
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my( $self, $svc_acct ) = (shift, shift);
+ my $cust_main = $svc_acct->cust_svc->cust_pkg->cust_main;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $err_or_queue = $self->infostreet_err_or_queue( $svc_acct->svcnum,
+ 'createUser', $svc_acct->username, $svc_acct->_password );
+ return $err_or_queue unless ref($err_or_queue);
+ my $jobnum = $err_or_queue->jobnum;
+
+ my %contact_info = ( map {
+ $_ => $cust_main->getfield( $infostreet2cust_main{$_} );
+ } keys %infostreet2cust_main );
+
+ my @emails = grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list;
+ $contact_info{'email'} = $emails[0] if @emails;
+
+ #this one is kinda noment-specific
+ $contact_info{'organization'} = $cust_main->agent->agent;
+
+ $err_or_queue = $self->infostreet_queueContact( $svc_acct->svcnum,
+ $svc_acct->username, %contact_info );
+ return $err_or_queue unless ref($err_or_queue);
+
+ # If a quota has been specified set the quota because it is not the default
+ $err_or_queue = $self->infostreet_queueSetQuota( $svc_acct->svcnum,
+ $svc_acct->username, $svc_acct->quota ) if $svc_acct->quota;
+ return $err_or_queue unless ref($err_or_queue);
+
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ return $error if $error;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return "can't change username with InfoStreet"
+ if $old->username ne $new->username;
+
+ # If the quota has changed then do the export to setQuota
+ my $err_or_queue = $self->infostreet_queueSetQuota( $new->svcnum, $new->username, $new->quota )
+ if ( $old->quota != $new->quota );
+ return $err_or_queue unless ref($err_or_queue);
+
+
+ return '' unless $old->_password ne $new->_password;
+ $self->infostreet_queue( $new->svcnum,
+ 'passwd', $new->username, $new->_password );
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->infostreet_queue( $svc_acct->svcnum,
+ 'purgeAccount,releaseUsername', $svc_acct->username );
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->infostreet_queue( $svc_acct->svcnum,
+ 'setStatus', $svc_acct->username, 'DISABLED' );
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->infostreet_queue( $svc_acct->svcnum,
+ 'setStatus', $svc_acct->username, 'ACTIVE' );
+}
+
+sub infostreet_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => 'FS::part_export::infostreet::infostreet_command',
+ };
+ $queue->insert(
+ $self->option('url'),
+ $self->option('login'),
+ $self->option('password'),
+ $self->option('groupID'),
+ $method,
+ @_,
+ );
+}
+
+#ick false laziness
+sub infostreet_err_or_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => 'FS::part_export::infostreet::infostreet_command',
+ };
+ $queue->insert(
+ $self->option('url'),
+ $self->option('login'),
+ $self->option('password'),
+ $self->option('groupID'),
+ $method,
+ @_,
+ ) or $queue;
+}
+
+sub infostreet_queueContact {
+ my( $self, $svcnum ) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => 'FS::part_export::infostreet::infostreet_setContact',
+ };
+ $queue->insert(
+ $self->option('url'),
+ $self->option('login'),
+ $self->option('password'),
+ $self->option('groupID'),
+ @_,
+ ) or $queue;
+}
+
+sub infostreet_setContact {
+ my($url, $is_username, $is_password, $groupID, $username, %contact_info) = @_;
+ my $accountID = infostreet_command($url, $is_username, $is_password, $groupID,
+ 'getAccountID', $username);
+ foreach my $field ( keys %contact_info ) {
+ infostreet_command($url, $is_username, $is_password, $groupID,
+ 'setContactField', [ 'int'=>$accountID ], $field, $contact_info{$field} );
+ }
+
+}
+
+sub infostreet_queueSetQuota {
+
+ my( $self, $svcnum) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => 'FS::part_export::infostreet::infostreet_setQuota',
+ };
+
+ $queue->insert(
+ $self->option('url'),
+ $self->option('login'),
+ $self->option('password'),
+ $self->option('groupID'),
+ @_,
+ ) or $queue;
+
+}
+
+sub infostreet_setQuota {
+ my($url, $is_username, $is_password, $groupID, $username, $quota) = @_;
+ infostreet_command($url, $is_username, $is_password, $groupID, 'setQuota', $username, [ 'int'=> $quota ] );
+}
+
+
+sub infostreet_command { #subroutine, not method
+ my($url, $username, $password, $groupID, $method, @args) = @_;
+
+ warn "[FS::part_export::infostreet] $method ".join(' ', @args)."\n" if $DEBUG;
+
+ #quelle hack
+ if ( $method =~ /,/ ) {
+ foreach my $part ( split(/,\s*/, $method) ) {
+ infostreet_command($url, $username, $password, $groupID, $part, @args);
+ }
+ return;
+ }
+
+ eval "use Frontier::Client;";
+ die $@ if $@;
+
+ eval 'sub Frontier::RPC2::String::repr {
+ my $self = shift;
+ my $value = $$self;
+ $value =~ s/([&<>\"])/$Frontier::RPC2::char_entities{$1}/ge;
+ $value;
+ }';
+ die $@ if $@;
+
+ my $conn = Frontier::Client->new( url => $url );
+ my $key_result = $conn->call( 'authenticate', $username, $password, $groupID);
+ my %key_result = _infostreet_parse($key_result);
+ die $key_result{error} unless $key_result{success};
+ my $key = $key_result{data};
+
+ #my $result = $conn->call($method, $key, @args);
+ my $result = $conn->call( $method, $key,
+ map {
+ if ( ref($_) ) {
+ my( $type, $value) = @{$_};
+ $conn->$type($value);
+ } else {
+ $conn->string($_);
+ }
+ } @args );
+ my %result = _infostreet_parse($result);
+ die $result{error} unless $result{success};
+
+ $result->{data};
+
+}
+
+#sub infostreet_command_byid { #subroutine, not method;
+# my($url, $username, $password, $groupID, $method, @args ) = @_;
+#
+# infostreet_command
+#
+#}
+
+sub _infostreet_parse { #subroutine, not method
+ my $arg = shift;
+ map {
+ my $value = $arg->{$_};
+ #warn ref($value);
+ $value = $value->value()
+ if ref($value) && $value->isa('Frontier::RPC2::DataType');
+ $_=>$value;
+ } keys %$arg;
+}
+
+1;
+
diff --git a/FS/FS/part_export/internal_diddb.pm b/FS/FS/part_export/internal_diddb.pm
new file mode 100644
index 0000000..a330cb0
--- /dev/null
+++ b/FS/FS/part_export/internal_diddb.pm
@@ -0,0 +1,134 @@
+package FS::part_export::internal_diddb;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+use FS::phone_avail;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'countrycode' => { label => 'Country code', 'default' => '1', },
+;
+
+%info = (
+ 'svc' => 'svc_phone',
+ 'desc' => 'Provision phone numbers from the internal DID database',
+ 'notes' => 'After adding the export, DIDs may be imported under Tools -> Importing -> Import phone numbers (DIDs)',
+ 'options' => \%options,
+);
+
+sub rebless { shift; }
+
+sub get_dids {
+ my $self = shift;
+ my %opt = ref($_[0]) ? %{$_[0]} : @_;
+
+ my %hash = ( 'countrycode' => ( $self->option('countrycode') || '1' ),
+ 'exportnum' => $self->exportnum,
+ 'svcnum' => '',
+ );
+
+ if ( $opt{'areacode'} && $opt{'exchange'} ) { #return numbers
+
+ $hash{npa} = $opt{areacode};
+ $hash{nxx} = $opt{exchange};
+
+ return [ map { $_->npa. '-'. $_->nxx. '-'. $_->station }
+ qsearch({ 'table' => 'phone_avail',
+ 'hashref' => \%hash,
+ 'order_by' => 'ORDER BY station',
+ })
+ ];
+
+ } elsif ( $opt{'areacode'} ) { #return city (npa-nxx-XXXX)
+
+ $hash{npa} = $opt{areacode};
+
+ return [ map { '('. $_->npa. '-'. $_->nxx. '-XXXX)' }
+ qsearch({ 'select' => 'DISTINCT npa, nxx',
+ 'table' => 'phone_avail',
+ 'hashref' => \%hash,
+ 'order_by' => 'ORDER BY nxx',
+ })
+ ];
+
+ } elsif ( $opt{'state'} ) { #return aracodes
+
+ $hash{state} = $opt{state};
+
+ return [ map { $_->npa }
+ qsearch({ 'select' => 'DISTINCT npa',
+ 'table' => 'phone_avail',
+ 'hashref' => \%hash,
+ 'order_by' => 'ORDER BY npa',
+ })
+ ];
+
+ } else {
+ die "FS::part_export::internal_diddb::get_dids called without options\n";
+ }
+
+}
+
+sub _export_insert { #link phone_avail to svcnum
+ my( $self, $svc_phone ) = (shift, shift);
+
+ $svc_phone->phonenum =~ /^(\d{3})(\d{3})(\d+)$/
+ or return "unparsable phone number: ". $svc_phone->phonenum;
+ my( $npa, $nxx, $station ) = ($1, $2, $3);
+
+ my $phone_avail = qsearchs('phone_avail', {
+ 'countrycode' => ( $self->option('countrycode') || '1' ),
+ 'exportnum' => $self->exportnum,
+ 'svcnum' => '',
+ 'npa' => $npa,
+ 'nxx' => $nxx,
+ 'station' => $station,
+ });
+
+ return "number not available: ". $svc_phone->phonenum
+ unless $phone_avail;
+
+ $phone_avail->svcnum($svc_phone->svcnum);
+
+ $phone_avail->replace;
+
+}
+
+sub _export_delete { #unlink phone_avail from svcnum
+ my( $self, $svc_phone ) = (shift, shift);
+
+ $svc_phone->phonenum =~ /^(\d{3})(\d{3})(\d+)$/
+ or return "unparsable phone number: ". $svc_phone->phonenum;
+ my( $npa, $nxx, $station ) = ($1, $2, $3);
+
+ my $phone_avail = qsearchs('phone_avail', {
+ 'countrycode' => ( $self->option('countrycode') || '1'),
+ 'exportnum' => $self->exportnum,
+ 'svcnum' => $svc_phone->svcnum,
+ #these too?
+ 'npa' => $npa,
+ 'nxx' => $nxx,
+ 'station' => $station,
+ });
+
+ unless ( $phone_avail ) {
+ warn "WARNING: can't find number to return to availability: ".
+ $svc_phone->phonenum;
+ return;
+ }
+
+ $phone_avail->svcnum('');
+
+ $phone_avail->replace;
+
+}
+
+sub _export_replace { ''; }
+sub _export_suspend { ''; }
+sub _export_unsuspend { ''; }
+
+1;
+
diff --git a/FS/FS/part_export/ldap.pm b/FS/FS/part_export/ldap.pm
new file mode 100644
index 0000000..823d99d
--- /dev/null
+++ b/FS/FS/part_export/ldap.pm
@@ -0,0 +1,294 @@
+package FS::part_export::ldap;
+
+use vars qw(@ISA %info @saltset);
+use Tie::IxHash;
+use FS::Record qw( dbh );
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'dn' => { label=>'Root DN' },
+ 'password' => { label=>'Root DN password' },
+ 'userdn' => { label=>'User DN' },
+ 'attributes' => { label=>'Attributes',
+ type=>'textarea',
+ default=>join("\n",
+ 'uid $username',
+ 'mail $username\@$domain',
+ 'uidno $uid',
+ 'gidno $gid',
+ 'cn $first',
+ 'sn $last',
+ 'mailquota $quota',
+ 'vmail',
+ 'location',
+ 'mailtag',
+ 'mailhost',
+ 'mailmessagestore $dir',
+ 'userpassword $crypt_password',
+ 'hint',
+ 'answer $sec_phrase',
+ 'objectclass top,person,inetOrgPerson',
+ ),
+ },
+ 'radius' => { label=>'Export RADIUS attributes', type=>'checkbox', },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to LDAP',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Real-time export to arbitrary LDAP attributes. Requires installation of
+<a href="http://search.cpan.org/dist/Net-LDAP">Net::LDAP</a> from CPAN.
+END
+);
+
+@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+
+ #false laziness w/shellcommands.pm
+ {
+ no strict 'refs';
+ ${$_} = $svc_acct->getfield($_) foreach $svc_acct->fields;
+ ${$_} = $svc_acct->$_() foreach qw( domain );
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ if ( $cust_pkg ) {
+ my $cust_main = $cust_pkg->cust_main;
+ ${$_} = $cust_main->getfield($_) foreach qw(first last);
+ }
+ }
+ $crypt_password = ''; #surpress "used only once" warnings
+ $crypt_password = '{crypt}'. crypt( $svc_acct->_password,
+ $saltset[int(rand(64))].$saltset[int(rand(64))] );
+
+ my $username_attrib;
+ my %attrib = map { /^\s*(\w+)\s+(.*\S)\s*$/;
+ $username_attrib = $1 if $2 eq '$username';
+ ( $1 => eval(qq("$2")) ); }
+ grep { /^\s*(\w+)\s+(.*\S)\s*$/ }
+ split("\n", $self->option('attributes'));
+
+ if ( $self->option('radius') ) {
+ foreach my $table (qw(reply check)) {
+ my $method = "radius_$table";
+ my %radius = $svc_acct->$method();
+ foreach my $radius ( keys %radius ) {
+ ( my $ldap = $radius ) =~ s/\-//g;
+ $attrib{$ldap} = $radius{$radius};
+ }
+ }
+ }
+
+ my $err_or_queue = $self->ldap_queue( $svc_acct->svcnum, 'insert',
+ #$svc_acct->username,
+ $username_attrib,
+ %attrib );
+ return $err_or_queue unless ref($err_or_queue);
+
+ #groups with LDAP?
+ #my @groups = $svc_acct->radius_groups;
+ #if ( @groups ) {
+ # my $err_or_queue = $self->ldap_queue(
+ # $svc_acct->svcnum, 'usergroup_insert',
+ # $svc_acct->username, @groups );
+ # return $err_or_queue unless ref($err_or_queue);
+ #}
+
+ '';
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ return "can't (yet?) change username with ldap"
+ if $old->username ne $new->username;
+
+ return "ldap replace unimplemented";
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $jobnum = '';
+ #if ( $old->username ne $new->username ) {
+ # my $err_or_queue = $self->ldap_queue( $new->svcnum, 'rename',
+ # $new->username, $old->username );
+ # unless ( ref($err_or_queue) ) {
+ # $dbh->rollback if $oldAutoCommit;
+ # return $err_or_queue;
+ # }
+ # $jobnum = $err_or_queue->jobnum;
+ #}
+
+ foreach my $table (qw(reply check)) {
+ my $method = "radius_$table";
+ my %new = $new->$method();
+ my %old = $old->$method();
+ if ( grep { !exists $old{$_} #new attributes
+ || $new{$_} ne $old{$_} #changed
+ } keys %new
+ ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'insert',
+ $table, $new->username, %new );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ my @del = grep { !exists $new{$_} } keys %old;
+ if ( @del ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'attrib_delete',
+ $table, $new->username, @del );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+ }
+
+ # (sorta) false laziness with FS::svc_acct::replace
+ my @oldgroups = @{$old->usergroup}; #uuuh
+ my @newgroups = $new->radius_groups;
+ my @delgroups = ();
+ foreach my $oldgroup ( @oldgroups ) {
+ if ( grep { $oldgroup eq $_ } @newgroups ) {
+ @newgroups = grep { $oldgroup ne $_ } @newgroups;
+ next;
+ }
+ push @delgroups, $oldgroup;
+ }
+
+ if ( @delgroups ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'usergroup_delete',
+ $new->username, @delgroups );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ if ( @newgroups ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'usergroup_insert',
+ $new->username, @newgroups );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ return "ldap delete unimplemented";
+ my $err_or_queue = $self->ldap_queue( $svc_acct->svcnum, 'delete',
+ $svc_acct->username );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub ldap_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::ldap::ldap_$method",
+ };
+ $queue->insert(
+ $self->machine,
+ $self->option('dn'),
+ $self->option('password'),
+ $self->option('userdn'),
+ @_,
+ ) or $queue;
+}
+
+sub ldap_insert { #subroutine, not method
+ my $ldap = ldap_connect(shift, shift, shift);
+ my( $userdn, $username_attrib, %attrib ) = @_;
+
+ $userdn = "$username_attrib=$attrib{$username_attrib}, $userdn"
+ if $username_attrib;
+ #icky hack, but should be unsurprising to the LDAPers
+ foreach my $key ( grep { $attrib{$_} =~ /,/ } keys %attrib ) {
+ $attrib{$key} = [ split(/,/, $attrib{$key}) ];
+ }
+
+ my $status = $ldap->add( $userdn, attrs => [ %attrib ] );
+ die 'LDAP error: '. $status->error. "\n" if $status->is_error;
+
+ $ldap->unbind;
+}
+
+#sub ldap_delete { #subroutine, not method
+# my $dbh = ldap_connect(shift, shift, shift);
+# my $username = shift;
+#
+# foreach my $table (qw( radcheck radreply usergroup )) {
+# my $sth = $dbh->prepare( "DELETE FROM $table WHERE UserName = ?" );
+# $sth->execute($username)
+# or die "can't delete from $table table: ". $sth->errstr;
+# }
+# $dbh->disconnect;
+#}
+
+sub ldap_connect {
+ my( $machine, $dn, $password ) = @_;
+ my %bind_options;
+ $bind_options{password} = $password if length($password);
+
+ eval "use Net::LDAP";
+ die $@ if $@;
+
+ my $ldap = Net::LDAP->new($machine) or die $@;
+ my $status = $ldap->bind( $dn, %bind_options );
+ die 'LDAP error: '. $status->error. "\n" if $status->is_error;
+
+ $ldap;
+}
+
+1;
+
diff --git a/FS/FS/part_export/nas_wrapper.pm b/FS/FS/part_export/nas_wrapper.pm
new file mode 100644
index 0000000..2499ba3
--- /dev/null
+++ b/FS/FS/part_export/nas_wrapper.pm
@@ -0,0 +1,311 @@
+package FS::part_export::nas_wrapper;
+
+=head1 FS::part_export::nas_wrapper
+
+This is a meta-export that triggers other exports for FS::svc_broadband objects
+based on a set of configurable conditions. These conditions are defined by the
+following FS::router virtual fields:
+
+=over 4
+
+=item nas_conf - Per-router meta-export configuration. See L</"nas_conf Syntax">.
+
+=back
+
+=head2 nas_conf Syntax
+
+export_name|routernum[,routernum]|[field,condition[,field,condition]][||...]
+
+=over 4
+
+=item export_name - Name or exportnum of the export to be executed. In order to specify export options you must use the exportnum form. (ex. 'router' for FS::part_export::router).
+
+=item routernum - FS::router routernum corresponding to the desired FS::router for which this export will be run.
+
+=item field - FS::svc_broadband field (real or virtual). The following condition (regex) will be matched against the value of this field.
+
+=item condition - A regular expression to be match against the value of the previously listed FS::svc_broadband field.
+
+=back
+
+If multiple routernum's are specified, then the export will be triggered for each router listed. If multiple field/condition pairs are present, then the results of the matches will be and'd. Note that if a false match is found, the rest of the matches may not be checked.
+
+You can specify multiple export/router/condition sets by concatenating them with '||'.
+
+=cut
+
+use strict;
+use vars qw(@ISA %info $me $DEBUG);
+
+use FS::Record qw(qsearchs);
+use FS::part_export;
+
+use Tie::IxHash;
+use Data::Dumper qw(Dumper);
+
+@ISA = qw(FS::part_export);
+$me = '[' . __PACKAGE__ . ']';
+$DEBUG = 0;
+
+%info = (
+ 'svc' => 'svc_broadband',
+ 'desc' => 'A meta-export that triggers other svc_broadband exports.',
+ 'options' => {},
+ 'notes' => '',
+);
+
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self) = shift;
+ $self->_export_command('insert', @_);
+}
+
+sub _export_delete {
+ my($self) = shift;
+ $self->_export_command('delete', @_);
+}
+
+sub _export_suspend {
+ my($self) = shift;
+ $self->_export_command('suspend', @_);
+}
+
+sub _export_unsuspend {
+ my($self) = shift;
+ $self->_export_command('unsuspend', @_);
+}
+
+sub _export_replace {
+ my($self) = shift;
+ $self->_export_command('replace', @_);
+}
+
+sub _export_command {
+ my ( $self, $action, $svc_broadband) = (shift, shift, shift);
+
+ my ($new, $old);
+ if ($action eq 'replace') {
+ $new = $svc_broadband;
+ $old = shift;
+ }
+
+ my $router = $svc_broadband->addr_block->router;
+
+ return '' unless grep(/^nas_conf$/, $router->fields);
+ my $nas_conf = $router->nas_conf;
+
+ my $child_exports = &_parse_nas_conf($nas_conf);
+
+ my $error = '';
+
+ my $queue_child_exports = {};
+
+ # Similar to FS::svc_Common::replace, calling insert, delete, and replace
+ # exports where necessary depending on which conditions match.
+ if ($action eq 'replace') {
+
+ my @new_child_exports = ();
+ my @old_child_exports = ();
+
+ # Find all the matching "new" child exports.
+ foreach my $child_export (@$child_exports) {
+ my $match = &_test_child_export_conditions(
+ $child_export->{'conditions'},
+ $new,
+ );
+
+ if ($match) {
+ push @new_child_exports, $child_export;
+ }
+ }
+
+ # Find all the matching "old" child exports.
+ foreach my $child_export (@$child_exports) {
+ my $match = &_test_child_export_conditions(
+ $child_export->{'conditions'},
+ $old,
+ );
+
+ if ($match) {
+ push @old_child_exports, $child_export;
+ }
+ }
+
+ # Insert exports for new.
+ push @{$queue_child_exports->{'insert'}}, (
+ map {
+ my $new_child_export = $_;
+ if (! grep { $new_child_export eq $_ } @old_child_exports) {
+ $new_child_export->{'args'} = [ $new ];
+ $new_child_export;
+ } else {
+ ();
+ }
+ } @new_child_exports
+ );
+
+ # Replace exports for new and old.
+ push @{$queue_child_exports->{'replace'}}, (
+ map {
+ my $new_child_export = $_;
+ if (grep { $new_child_export eq $_ } @old_child_exports) {
+ $new_child_export->{'args'} = [ $new, $old ];
+ $new_child_export;
+ } else {
+ ();
+ }
+ } @new_child_exports
+ );
+
+ # Delete exports for old.
+ push @{$queue_child_exports->{'delete'}}, (
+ grep {
+ my $old_child_export = $_;
+ if (! grep { $old_child_export eq $_ } @new_child_exports) {
+ $old_child_export->{'args'} = [ $old ];
+ $old_child_export;
+ } else {
+ ();
+ }
+ } @old_child_exports
+ );
+
+ } else {
+
+ foreach my $child_export (@$child_exports) {
+ my $match = &_test_child_export_conditions(
+ $child_export->{'conditions'},
+ $svc_broadband,
+ );
+
+ if ($match) {
+ $child_export->{'args'} = [ $svc_broadband ];
+ push @{$queue_child_exports->{$action}}, $child_export;
+ }
+ }
+
+ }
+
+ warn "[debug]$me Dispatching child exports... "
+ . &Dumper($queue_child_exports) if $DEBUG;
+
+ # Actually call the child exports now, with their preset action and arguments.
+ foreach my $_action (keys(%$queue_child_exports)) {
+
+ foreach my $_child_export (@{$queue_child_exports->{$_action}}) {
+ $error = &_dispatch_child_export(
+ $_child_export,
+ $_action,
+ @{$_child_export->{'args'}},
+ @_,
+ );
+
+ # Bail if there's an error queueing one of the exports.
+ # This will all get rolled-back.
+ return $error if $error;
+ }
+
+ }
+
+ return '';
+
+}
+
+
+sub _parse_nas_conf {
+
+ my $nas_conf = shift;
+ my @child_exports = ();
+
+ foreach my $cond_set ($nas_conf =~ m/(.*?[^\\])(?:\|\||$)/g) {
+
+ warn "[debug]$me cond_set is '$cond_set'" if $DEBUG;
+
+ my @args = $cond_set =~ m/(.*?[^\\])(?:\||$)/g;
+
+ my %child_export = (
+ 'export' => $args[0],
+ 'routernum' => [ split(/,\s*/, $args[1]) ],
+ 'conditions' => { @args[2..$#args] },
+ );
+
+ warn "[debug]$me " . Dumper(\%child_export) if $DEBUG;
+
+ push @child_exports, { %child_export };
+
+ }
+
+ return \@child_exports;
+
+}
+
+sub _dispatch_child_export {
+
+ my ($child_export, $action, @args) = (shift, shift, @_);
+
+ my $child_export_name = $child_export->{'export'};
+ my @routernums = @{$child_export->{'routernum'}};
+
+ my $error = '';
+
+ # And the real hack begins...
+
+ my $child_part_export;
+ if ($child_export_name =~ /^(\d+)$/) {
+ my $exportnum = $1;
+ $child_part_export = qsearchs('part_export', { exportnum => $exportnum });
+ unless ($child_part_export) {
+ return "No such FS::part_export with exportnum '$exportnum'";
+ }
+
+ $child_export_name = $child_part_export->exporttype;
+ } else {
+ $child_part_export = new FS::part_export {
+ 'exporttype' => $child_export_name,
+ 'machine' => 'bogus',
+ };
+ }
+
+ warn "[debug]$me running export '$child_export_name' for routernum(s) '"
+ . join(',', @routernums) . "'" if $DEBUG;
+
+ my $cmd_method = "_export_$action";
+
+ foreach my $routernum (@routernums) {
+ $error ||= $child_part_export->$cmd_method(
+ @args,
+ 'routernum' => $routernum,
+ );
+ last if $error;
+ }
+
+ warn "[debug]$me export '$child_export_name' returned '$error'"
+ if $DEBUG;
+
+ return $error;
+
+}
+
+sub _test_child_export_conditions {
+
+ my ($conditions, $svc_broadband) = (shift, shift);
+
+ my $match = 1;
+ foreach my $cond_field (keys %$conditions) {
+ my $cond_regex = $conditions->{$cond_field};
+ warn "[debug]$me Condition: $cond_field =~ /$cond_regex/" if $DEBUG;
+ unless ($svc_broadband->get($cond_field) =~ /$cond_regex/) {
+ $match = 0;
+ last;
+ }
+ }
+
+ return $match;
+
+}
+
+
+1;
+
diff --git a/FS/FS/part_export/null.pm b/FS/FS/part_export/null.pm
new file mode 100644
index 0000000..0145af3
--- /dev/null
+++ b/FS/FS/part_export/null.pm
@@ -0,0 +1,13 @@
+package FS::part_export::null;
+
+use vars qw(@ISA);
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+sub rebless { shift; }
+
+sub _export_insert {}
+sub _export_replace {}
+sub _export_delete {}
+
diff --git a/FS/FS/part_export/passwdfile.pm b/FS/FS/part_export/passwdfile.pm
new file mode 100644
index 0000000..2978d25
--- /dev/null
+++ b/FS/FS/part_export/passwdfile.pm
@@ -0,0 +1,18 @@
+package FS::part_export::passwdfile;
+
+use strict;
+use vars qw(@ISA %options);
+use Tie::IxHash;
+use FS::part_export::null;
+
+@ISA = qw(FS::part_export::null);
+
+tie %options, 'Tie::IxHash',
+ 'crypt' => { label=>'Password encryption',
+ type=>'select', options=>[qw(crypt md5)],
+ default=>'crypt',
+ },
+;
+
+1;
+
diff --git a/FS/FS/part_export/phone_shellcommands.pm b/FS/FS/part_export/phone_shellcommands.pm
new file mode 100644
index 0000000..fbb7a0b
--- /dev/null
+++ b/FS/FS/part_export/phone_shellcommands.pm
@@ -0,0 +1,140 @@
+package FS::part_export::phone_shellcommands;
+
+use strict;
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use String::ShellQuote;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+#TODO
+#- modify command (get something from freepbx for changing PINs)
+#- suspension/unsuspension
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root', },
+ 'useradd' => { label=>'Insert command', },
+ 'userdel' => { label=>'Delete command', },
+ 'usermod' => { label=>'Modify command', },
+ 'suspend' => { label=>'Suspension command', },
+ 'unsuspend' => { label=>'Unsuspension command', },
+;
+
+%info = (
+ 'svc' => 'svc_phone',
+ 'desc' => 'Run remote commands via SSH, for phone numbers',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Run remote commands via SSH, for phone numbers. You will need to
+<a href="../docs/ssh.html">setup SSH for unattended operation</a>.
+<BR><BR>Use these buttons for some useful presets:
+<UL>
+ <LI>
+ <INPUT TYPE="button" VALUE="FreePBX (build_exten CLI module needed)" onClick='
+ this.form.user.value = "root";
+ this.form.useradd.value = "build_exten.php --create --exten $phonenum --directdid 1$phonenum --sip-secret $sip_password --name $cust_name --vm-password $pin && /usr/share/asterisk/bin/module_admin reload";
+ this.form.userdel.value = "build_exten.php --delete --exten $phonenum && /usr/share/asterisk/bin/module_admin reload";
+ this.form.usermod.value = "build_exten.php --modify --exten $new_phonenum --directdid 1$new_phonenum --sip-secret $new_sip_password --name $new_cust_name --vm-password $new_pin && /usr/share/asterisk/bin/module_admin reload";
+ this.form.suspend.value = "";
+ this.form.unsuspend.value = "";
+ '> (Important note: Reduce freeside-queued "max_kids" to 1 when using FreePBX integration)
+ </UL>
+
+The following variables are available for interpolation (prefixed with new_ or
+old_ for replace operations):
+<UL>
+ <LI><code>$countrycode</code> - Country code
+ <LI><code>$phonenum</code> - Phone number
+ <LI><code>$sip_password</code> - SIP secret (quoted for the shell)
+ <LI><code>$pin</code> - Personal identification number
+ <LI><code>$cust_name</code> - Customer name (quoted for the shell)
+</UL>
+END
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self) = shift;
+ $self->_export_command('useradd', @_);
+}
+
+sub _export_delete {
+ my($self) = shift;
+ $self->_export_command('userdel', @_);
+}
+
+sub _export_suspend {
+ my($self) = shift;
+ $self->_export_command('suspend', @_);
+}
+
+sub _export_unsuspend {
+ my($self) = shift;
+ $self->_export_command('unsuspend', @_);
+}
+
+sub _export_command {
+ my ( $self, $action, $svc_phone) = (shift, shift, shift);
+ my $command = $self->option($action);
+ return '' if $command =~ /^\s*$/;
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${$_} = $svc_phone->getfield($_) foreach $svc_phone->fields;
+ }
+ my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
+ my $cust_name = $cust_pkg ? $cust_pkg->cust_main->name : '';
+ $cust_name = shell_quote $cust_name;
+ my $sip_password = shell_quote $svc_phone->sip_password;
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $svc_phone->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+sub _export_replace {
+ my($self, $new, $old ) = (shift, shift, shift);
+ my $command = $self->option('usermod');
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${"old_$_"} = $old->getfield($_) foreach $old->fields;
+ ${"new_$_"} = $new->getfield($_) foreach $new->fields;
+ }
+
+ my $cust_pkg = $new->cust_svc->cust_pkg;
+ my $new_cust_name = $cust_pkg ? $cust_pkg->cust_main->name : '';
+ $new_cust_name = shell_quote $new_cust_name;
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $new->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+#a good idea to queue anything that could fail or take any time
+sub shellcommands_queue {
+ my( $self, $svcnum ) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::phone_shellcommands::ssh_cmd",
+ };
+ $queue->insert( @_ );
+}
+
+sub ssh_cmd { #subroutine, not method
+ use Net::SSH '0.08';
+ &Net::SSH::ssh_cmd( { @_ } );
+}
+
diff --git a/FS/FS/part_export/phone_sqlradius.pm b/FS/FS/part_export/phone_sqlradius.pm
new file mode 100644
index 0000000..24f7845
--- /dev/null
+++ b/FS/FS/part_export/phone_sqlradius.pm
@@ -0,0 +1,158 @@
+package FS::part_export::phone_sqlradius;
+
+use vars qw(@ISA $DEBUG %info );
+use Tie::IxHash;
+use FS::Record qw( dbh str2time_sql ); #qsearch qsearchs );
+#use FS::part_export;
+use FS::part_export::sqlradius qw(sqlradius_connect);
+#use FS::svc_phone;
+#use FS::export_svc;
+#use Carp qw( cluck );
+
+@ISA = qw(FS::part_export::sqlradius);
+
+$DEBUG = 0;
+
+tie %options, 'Tie::IxHash',
+ 'datasrc' => { label=>'DBI data source ' },
+ 'username' => { label=>'Database username' },
+ 'password' => { label=>'Database password' },
+ 'ignore_accounting' => {
+ type => 'checkbox',
+ label => 'Ignore accounting records from this database'
+ },
+ 'hide_ip' => {
+ type => 'checkbox',
+ label => 'Hide IP address information on session reports',
+ },
+ 'hide_data' => {
+ type => 'checkbox',
+ label => 'Hide download/upload information on session reports',
+ },
+
+ #should be default for this one, right?
+ #'show_called_station' => {
+ # type => 'checkbox',
+ # label => 'Show the Called-Station-ID on session reports',
+ #},
+
+ #N/A
+ #'overlimit_groups' => { label => 'Radius groups to assign to svc_acct which has exceeded its bandwidth or time limit', } ,
+ #'groups_susp_reason' => { label =>
+ # 'Radius group mapping to reason (via template user) (svcnum|username|username@domain reasonnum|reason)',
+ # type => 'textarea',
+ # },
+
+;
+
+%info = (
+ 'svc' => 'svc_phone',
+ 'desc' => 'Real-time export to SQL-backed RADIUS (FreeRADIUS, ICRADIUS) for phone provisioning and rating',
+ 'options' => \%options,
+ 'notes' => <<END,
+Real-time export of <b>radcheck</b> table
+<!--, <b>radreply</b> and <b>usergroup</b>-- tables>
+to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a>
+or <a href="http://radius.innercite.com/">ICRADIUS</a>.
+<br><br>
+
+This export is for phone/VoIP provisioning and rating. For a regular RADIUS
+export, see sqlradius.
+<br><br>
+
+<!--An existing RADIUS database will be updated in realtime, but you can use
+<a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.9:Documentation:Developer/bin/freeside-phone_sqlradius-reset">freeside-phone_sqlradius-reset</a>
+to delete the entire RADIUS database and repopulate the tables from the
+Freeside database.
+<br><br>
+-->
+
+See the
+<a href="http://search.cpan.org/dist/DBI/DBI.pm#connect">DBI documentation</a>
+and the
+<a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a>
+for the exact syntax of a DBI data source.
+
+END
+);
+
+sub rebless { shift; }
+
+sub export_username {
+ my($self, $svc_phone) = (shift, shift);
+ $svc_phone->countrycode. $svc_phone->phonenum;
+}
+
+sub _export_suspend {}
+sub _export_unsuspend {}
+
+#probably harmless that we ->can('usage_sessions').... ?
+
+#we want to feed these into CDRs, not update svc_acct records
+sub update_svc {
+ my $self = shift;
+
+ my $fdbh = dbh;
+ my $dbh = sqlradius_connect( map $self->option($_),
+ qw( datasrc username password ) );
+
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+
+ my @fields = qw( radacctid username realm acctsessiontime );
+
+ my @param = ();
+ my $where = '';
+
+ my $sth = $dbh->prepare("
+ SELECT RadAcctId, UserName, AcctSessionTime,
+ $str2time AcctStartTime), $str2time AcctStopTime),
+ CallingStationID, CalledStationID
+ FROM radacct
+ WHERE FreesideStatus IS NULL
+ AND AcctStopTime != 0
+ ") or die $dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+
+ while ( my $row = $sth->fetchrow_arrayref ) {
+ my( $RadAcctId, $UserName, $AcctSessionTime,
+ $AcctStartTime, $AcctStopTime,
+ $CallingStationID, $CalledStationID,
+ )= @$row;
+ warn "processing record: ".
+ "$RadAcctId ($UserName for ${AcctSessionTime}s"
+ if $DEBUG;
+
+ my $oldAutoCommit = $FS::UID::AutoCommit; # can't undo side effects, but at
+ local $FS::UID::AutoCommit = 0; # least we can avoid over counting
+
+ my $cdr = new FS::cdr {
+ 'src' => $CallingStationID,
+ 'charged_party' => $UserName,
+ 'dst' => $CalledStationID,
+ 'startdate' => $AcctStartTime,
+ 'enddate' => $AcctStopTime,
+ 'duration' => $AcctStopTime - $AcctStartTime,
+ 'billsec' => $AcctSessionTime,
+ };
+
+ my $errinfo = "for RADIUS detail RadAcctID $RadAcctId ".
+ "(UserName $UserName)";
+
+ my $error = $cdr->insert;
+ my $status = $error ? 'skipped' : 'done';
+
+ warn "setting FreesideStatus to $status $errinfo\n" if $DEBUG;
+ my $psth = $dbh->prepare("UPDATE radacct
+ SET FreesideStatus = ?
+ WHERE RadAcctId = ?"
+ ) or die $dbh->errstr;
+ $psth->execute($status, $RadAcctId) or die $psth->errstr;
+
+ $fdbh->commit or die $fdbh->errstr if $oldAutoCommit;
+
+ }
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/postfix.pm b/FS/FS/part_export/postfix.pm
new file mode 100644
index 0000000..4fd19ee
--- /dev/null
+++ b/FS/FS/part_export/postfix.pm
@@ -0,0 +1,32 @@
+package FS::part_export::postfix;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::null;
+
+@ISA = qw(FS::part_export::null);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'aliases' => { label=>'aliases file location', default=>'/etc/aliases' },
+ 'virtual' => { label=>'virtual file location', default=>'/etc/postfix/virtual' },
+ 'mydomain' => { label=>'local domain', default=>'' },
+ 'newaliases' => { label=>'newaliases command', default=>'newaliases' },
+ 'postmap' => { label=>'postmap command',
+ default=>'postmap hash:/etc/postfix/virtual', },
+ 'reload' => { label=>'reload command',
+ default=>'postfix reload' },
+;
+
+%info = (
+ 'svc' => 'svc_forward',
+ 'desc' => 'Postfix text files',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Batch export of Postfix aliases and virtual files.
+<a href="http://search.cpan.org/dist/File-Rsync">File::Rsync</a>
+must be installed. Run bin/postfix.export to export the files.
+END
+);
+
+1;
diff --git a/FS/FS/part_export/prizm.pm b/FS/FS/part_export/prizm.pm
new file mode 100644
index 0000000..2d4d858
--- /dev/null
+++ b/FS/FS/part_export/prizm.pm
@@ -0,0 +1,549 @@
+package FS::part_export::prizm;
+
+use vars qw(@ISA %info %options $DEBUG);
+use Tie::IxHash;
+use FS::Record qw(fields dbh);
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+$DEBUG = 1;
+
+tie %options, 'Tie::IxHash',
+ 'url' => { label => 'Northbound url', default=>'https://localhost:8443/prizm/nbi' },
+ 'user' => { label => 'Northbound username', default=>'nbi' },
+ 'password' => { label => 'Password', default => '' },
+ 'ems' => { label => 'Full EMS', type => 'checkbox' },
+ 'always_bam' => { label => 'Always activate/suspend authentication', type => 'checkbox' },
+ 'element_name_length' => { label => 'Size of siteName (best left blank)' },
+;
+
+my $notes = <<'EOT';
+Real-time export of <b>svc_broadband</b>, <b>cust_pkg</b>, and <b>cust_main</b>
+record data to Motorola
+<a href="http://motorola.canopywireless.com/products/prizm/">Canopy Prizm
+software</a> via the Northbound interface.<br><br>
+
+Freeside will attempt to create an element in an existing network with the
+values provided in svc_broadband. Of particular interest are
+<ul>
+ <li> mac address - used to identify the element
+ <li> vlan profile - an exact match for a vlan profiles defined in prizm
+ <li> ip address - defines the management ip address of the prizm element
+ <li> latitude - GPS latitude
+ <li> longitude - GPS longitude
+ <li> altitude - GPS altitude
+</ul>
+
+In addition freeside attempts to set the service plan name in prizm to the
+name of the package in which the service resides.
+
+The service is associated with a customer in prizm as well, and freeside
+will create the customer should none already exist with import id matching
+the freeside customer number. The following fields are set.
+
+<ul>
+ <li> importId - the freeside customer number
+ <li> customerType - freeside
+ <li> customerName - the name associated with the freeside shipping address
+ <li> address1 - the shipping address
+ <li> address2
+ <li> city
+ <li> state
+ <li> zipCode
+ <li> country
+ <li> workPhone - the daytime phone number
+ <li> homePhone - the night phone number
+ <li> freesideId - the freeside customer number
+</ul>
+
+ Additionally set on the element are
+<ul>
+ <li> Site Name - The shipping name followed by the service broadband description field
+ <li> Site Location - the shipping address
+ <li> Site Contact - the daytime and night phone numbers
+</ul>
+
+Freeside provisions, suspends, and unsuspends elements BAM only unless the
+'Full EMS' checkbox is checked.<br><br>
+
+When freeside provisions an element the siteName is copied internally by
+prizm in such a manner that it is possible for the value to exceed the size
+of the column used in the prizm database. Therefore freeside truncates
+by default this value to 50 characters. It is thought that this
+column is the account_name column of the element_user_account table. It
+may be possible to lift this limit by modifying the prizm database and
+setting a new appropriate value on this export. This is untested and
+possibly harmful.
+
+EOT
+
+%info = (
+ 'svc' => 'svc_broadband',
+ 'desc' => 'Real-time export to Northbound Interface',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => $notes,
+);
+
+sub prizm_command {
+ my ($self,$namespace,$method) = (shift,shift,shift);
+
+ eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
+ die $@ if $@;
+
+ my $prizm = new Net::Prizm (
+ namespace => $namespace,
+ url => $self->option('url'),
+ user => $self->option('user'),
+ password => $self->option('password'),
+ );
+
+ $prizm->$method(@_);
+}
+
+sub queued_prizm_command { # subroutine
+ my( $url, $user, $password, $namespace, $method, @args ) = @_;
+
+ eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
+ die $@ if $@;
+
+ my $prizm = new Net::Prizm (
+ namespace => $namespace,
+ url => $url,
+ user => $user,
+ password => $password,
+ );
+
+ $err_or_som = $prizm->$method( @args);
+
+ die $err_or_som
+ unless ref($err_or_som);
+
+ '';
+
+}
+
+sub _export_insert {
+ my( $self, $svc ) = ( shift, shift );
+
+ my $cust_main = $svc->cust_svc->cust_pkg->cust_main;
+
+ my $err_or_som = $self->prizm_command('CustomerIfService', 'getCustomers',
+ ['import_id'],
+ [$cust_main->custnum],
+ ['='],
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ my $pre = '';
+ if ( defined $cust_main->dbdef_table->column('ship_last') ) {
+ $pre = $cust_main->ship_last ? 'ship_' : '';
+ }
+ my $name = $pre ? $cust_main->ship_name : $cust_main->name;
+ my $location = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
+ qw (address1 address2 city state zip)
+ );
+ my $contact = join(" ", map { my $method = "$pre$_"; $cust_main->$method }
+ qw (daytime night)
+ );
+
+ my $pcustomer;
+ if ($err_or_som->result->[0]) {
+ $pcustomer = $err_or_som->result->[0]->customerId;
+ }else{
+ my $chashref = $cust_main->hashref;
+ my $customerinfo = {
+ importId => $cust_main->custnum,
+ customerName => $name,
+ customerType => 'freeside',
+ address1 => $chashref->{"${pre}address1"},
+ address2 => $chashref->{"${pre}address2"},
+ city => $chashref->{"${pre}city"},
+ state => $chashref->{"${pre}state"},
+ zipCode => $chashref->{"${pre}zip"},
+ workPhone => $chashref->{"${pre}daytime"},
+ homePhone => $chashref->{"${pre}night"},
+ email => @{[$cust_main->invoicing_list_emailonly]}[0],
+ extraFieldNames => [ 'country', 'freesideId',
+ ],
+ extraFieldValues => [ $chashref->{"${pre}country"}, $cust_main->custnum,
+ ],
+ };
+
+ $err_or_som = $self->prizm_command('CustomerIfService', 'addCustomer',
+ $customerinfo);
+ return $err_or_som
+ unless ref($err_or_som);
+
+ $pcustomer = $err_or_som->result;
+ }
+ warn "multiple prizm customers found for $cust_main->custnum"
+ if scalar(@$pcustomer) > 1;
+
+# #kinda big question/expensive
+# $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
+# ['Network Default Gateway Address'],
+# [$svc->addr_block->ip_gateway],
+# ['='],
+# );
+# return $err_or_som
+# unless ref($err_or_som);
+#
+# return "No elements in network" unless exists $err_or_som->result->[0];
+
+ my $networkid = 0;
+# for (my $i = 0; $i < $err_or_som->result->[0]->attributeNames; $i++) {
+# if ($err_or_som->result->[0]->attributeNames->[$i] eq "Network.ID"){
+# $networkid = $err_or_som->result->[0]->attributeValues->[$i];
+# last;
+# }
+# }
+
+ my $element_name_length = 50;
+ $element_name_length = $1
+ if $self->option('element_name_length') =~ /^\s*(\d+)\s*$/;
+ $err_or_som = $self->prizm_command('NetworkIfService', 'addProvisionedElement',
+ $networkid,
+ $svc->mac_addr,
+ substr($name . " " . $svc->description,
+ 0, $element_name_length),
+ $location,
+ $contact,
+ sprintf("%032X", $svc->authkey),
+ $svc->cust_svc->cust_pkg->part_pkg->pkg,
+ $svc->vlan_profile,
+ ($self->option('ems') ? 1 : 0 ),
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ my (@names) = ('Management IP',
+ 'GPS Latitude',
+ 'GPS Longitude',
+ 'GPS Altitude',
+ 'Site Name',
+ 'Site Location',
+ 'Site Contact',
+ );
+ my (@values) = ($svc->ip_addr,
+ $svc->latitude,
+ $svc->longitude,
+ $svc->altitude,
+ $name . " " . $svc->description,
+ $location,
+ $contact,
+ );
+ $element = $err_or_som->result->elementId;
+ $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
+ [ $element ],
+ \@names,
+ \@values,
+ 0,
+ 1,
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
+ [ $element ],
+ $svc->vlan_profile,
+ 0,
+ 1,
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
+ [ $element ],
+ $svc->cust_svc->cust_pkg->part_pkg->pkg,
+ 0,
+ 1,
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ $err_or_som = $self->prizm_command('NetworkIfService',
+ 'activateNetworkElements',
+ [ $element ],
+ 1,
+ ( $self->option('ems') ? 1 : 0 ),
+ );
+
+ return $err_or_som
+ unless ref($err_or_som);
+
+ $err_or_som = $self->prizm_command('CustomerIfService',
+ 'addElementToCustomer',
+ 0,
+ $cust_main->custnum,
+ 0,
+ $svc->mac_addr,
+ );
+
+ return $err_or_som
+ unless ref($err_or_som);
+
+ '';
+}
+
+sub _export_delete {
+ my( $self, $svc ) = ( shift, shift );
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $cust_pkg = $svc->cust_svc->cust_pkg;
+
+ my $depend = [];
+
+ if ($cust_pkg) {
+ my $queue = new FS::queue {
+ 'svcnum' => $svc->svcnum,
+ 'job' => 'FS::part_export::prizm::queued_prizm_command',
+ };
+ my $error = $queue->insert(
+ ( map { $self->option($_) }
+ qw( url user password ) ),
+ 'CustomerIfService',
+ 'removeElementFromCustomer',
+ 0,
+ $cust_pkg->custnum,
+ 0,
+ $svc->mac_addr,
+ );
+
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ push @$depend, $queue->jobnum;
+ }
+
+ my $err_or_queue =
+ $self->queue_statuschange('deleteElement', $depend, $svc, 1);
+
+ unless (ref($err_or_queue)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = ( shift, shift, shift );
+
+ my $err_or_som = $self->prizm_command('NetworkIfService', 'getPrizmElements',
+ [ 'MAC Address' ],
+ [ $old->mac_addr ],
+ [ '=' ],
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ return "Can't find prizm element for " . $old->mac_addr
+ unless $err_or_som->result->[0];
+
+ my %freeside2prizm = ( mac_addr => 'MAC Address',
+ ip_addr => 'Management IP',
+ latitude => 'GPS Latitude',
+ longitude => 'GPS Longitude',
+ altitude => 'GPS Altitude',
+ authkey => 'Authentication Key',
+ );
+
+ my (@values);
+ my (@names) = map { push @values, $new->$_; $freeside2prizm{$_} }
+ grep { $old->$_ ne $new->$_ }
+ grep { exists($freeside2prizm{$_}) }
+ fields( 'svc_broadband' );
+
+ if ($old->description ne $new->description) {
+ my $cust_main = $old->cust_svc->cust_pkg->cust_main;
+ my $name = defined($cust_main->dbdef_table->column('ship_last'))
+ ? $cust_main->ship_name
+ : $cust_main->name;
+ push @values, $name . " " . $new->description;
+ push @names, "Site Name";
+ }
+
+ my $element = $err_or_som->result->[0]->elementId;
+
+ $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfig',
+ [ $element ],
+ \@names,
+ \@values,
+ 0,
+ 1,
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
+ [ $element ],
+ $new->vlan_profile,
+ 0,
+ 1,
+ )
+ if $old->vlan_profile ne $new->vlan_profile;
+
+ return $err_or_som
+ unless ref($err_or_som);
+
+ $err_or_som = $self->prizm_command('NetworkIfService', 'setElementConfigSet',
+ [ $element ],
+ $new->cust_svc->cust_pkg->part_pkg->pkg,
+ 0,
+ 1,
+ );
+ return $err_or_som
+ unless ref($err_or_som);
+
+ '';
+
+}
+
+sub _export_suspend {
+ my( $self, $svc ) = ( shift, shift );
+ my $depend = [];
+ my $ems = $self->option('ems') ? 1 : 0;
+ my $err_or_queue = '';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $err_or_queue =
+ $self->queue_statuschange('suspendNetworkElements', [], $svc, 1, $ems);
+ unless (ref($err_or_queue)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ push @$depend, $err_or_queue->jobnum;
+
+ if ($ems && $self->option('always_bam')) {
+ $err_or_queue =
+ $self->queue_statuschange('suspendNetworkElements', $depend, $svc, 1, 0);
+ unless (ref($err_or_queue)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+sub _export_unsuspend {
+ my( $self, $svc ) = ( shift, shift );
+ my $depend = [];
+ my $ems = $self->option('ems') ? 1 : 0;
+ my $err_or_queue = '';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ($ems && $self->option('always_bam')) {
+ $err_or_queue =
+ $self->queue_statuschange('activateNetworkElements', [], $svc, 1, 0);
+ unless (ref($err_or_queue)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ push @$depend, $err_or_queue->jobnum;
+ }
+
+ $err_or_queue =
+ $self->queue_statuschange('activateNetworkElements', $depend, $svc, 1, $ems);
+ unless (ref($err_or_queue)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+sub export_links {
+ my( $self, $svc, $arrayref ) = ( shift, shift, shift );
+
+ push @$arrayref, '<A HREF="http://'. $svc->ip_addr. '">SM</A>';
+
+ '';
+}
+
+sub queue_statuschange {
+ my( $self, $method, $jobs, $svc, @args ) = @_;
+
+ # already in a transaction and can't die here
+
+ my $queue = new FS::queue {
+ 'svcnum' => $svc->svcnum,
+ 'job' => 'FS::part_export::prizm::statuschange',
+ };
+ my $error = $queue->insert(
+ ( map { $self->option($_) }
+ qw( url user password ) ),
+ $method,
+ $svc->mac_addr,
+ @args,
+ );
+
+ unless ($error) { # successful insertion
+ foreach my $job ( @$jobs ) {
+ $error ||= $queue->depend_insert($job);
+ }
+ }
+
+ $error or $queue;
+}
+
+sub statuschange { # subroutine
+ my( $url, $user, $password, $method, $mac_addr, @args) = @_;
+
+ eval "use Net::Prizm 0.04 qw(CustomerInfo PrizmElement);";
+ die $@ if $@;
+
+ my $prizm = new Net::Prizm (
+ namespace => 'NetworkIfService',
+ url => $url,
+ user => $user,
+ password => $password,
+ );
+
+ my $err_or_som = $prizm->getPrizmElements( [ 'MAC Address' ],
+ [ $mac_addr ],
+ [ '=' ],
+ );
+ die $err_or_som
+ unless ref($err_or_som);
+
+ die "Can't find prizm element for " . $mac_addr
+ unless $err_or_som->result->[0];
+
+ my $arg1;
+ # yuck!
+ if ($method =~ /suspendNetworkElements/ || $method =~ /activateNetworkElements/) {
+ $arg1 = [ $err_or_som->result->[0]->elementId ];
+ }else{
+ $arg1 = $err_or_som->result->[0]->elementId;
+ }
+ $err_or_som = $prizm->$method( $arg1, @args );
+
+ die $err_or_som
+ unless ref($err_or_som);
+
+ '';
+
+}
+
+
+1;
diff --git a/FS/FS/part_export/radiator.pm b/FS/FS/part_export/radiator.pm
new file mode 100644
index 0000000..2ac3edb
--- /dev/null
+++ b/FS/FS/part_export/radiator.pm
@@ -0,0 +1,167 @@
+package FS::part_export::radiator;
+
+use vars qw(@ISA %info $radusers);
+use Tie::IxHash;
+use FS::part_export::sqlradius;
+
+tie my %options, 'Tie::IxHash', %FS::part_export::sqlradius::options;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to RADIATOR',
+ 'options' => \%options,
+ 'nodomain' => '',
+ 'notes' => <<'END',
+Real-time export of the <b>radusers</b> table to any SQL database in
+<a href="http://www.open.com.au/radiator/">Radiator</a>-native format.
+To setup accounting, see the RADIATOR documentation for hooks to update
+a standard <b>radacct</b> table.
+END
+);
+
+@ISA = qw(FS::part_export::sqlradius); #for regular sqlradius accounting
+
+$radusers = 'RADUSERS'; #MySQL is case sensitive about table names! huh
+
+#sub export_username {
+# my($self, $svc_acct) = (shift, shift);
+# $svc_acct->email;
+#}
+
+sub _export_insert {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ $self->radiator_queue(
+ $svc_acct->svcnum,
+ 'insert',
+ $self->_radiator_hash($svc_acct),
+ );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+# return "can't (yet) change domain with radiator export"
+# if $old->domain ne $new->domain;
+# return "can't (yet) change username with radiator export"
+# if $old->username ne $new->username;
+
+ $self->radiator_queue(
+ $new->svcnum,
+ 'replace',
+ $self->export_username($old),
+ $self->_radiator_hash($new),
+ );
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ $self->radiator_queue(
+ $svc_acct->svcnum,
+ 'delete',
+ $self->export_username($svc_acct),
+ );
+}
+
+sub _radiator_hash {
+ my( $self, $svc_acct ) = @_;
+ my %hash = (
+ 'username' => $self->export_username($svc_acct),
+ 'pass_word' => $svc_acct->crypt_password,
+ 'fullname' => $svc_acct->finger,
+ map { my $method = "radius_$_"; $_ => $svc_acct->$method(); }
+ qw( framed_filter_id framed_mtu framed_netmask framed_protocol
+ framed_routing login_host login_service login_tcp_port )
+ );
+ $hash{'timeleft'} = $svc_acct->seconds
+ if $svc_acct->seconds =~ /^\d+$/;
+ $hash{'staticaddress'} = $svc_acct->slipip
+ if $svc_acct->slipip =~ /^[\d\.]+$/; # and $self->slipip ne '0.0.0.0';
+
+ $hash{'servicename'} = ( $svc_acct->radius_groups )[0];
+
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ $hash{'validto'} = $cust_pkg->bill
+ if $cust_pkg && $cust_pkg->part_pkg->is_prepaid && $cust_pkg->bill;
+
+ #some other random stuff, should probably be attributes or virtual fields
+ #$hash{'state'} = 0; #only inserts
+ #$hash{'badlogins'} = 0; #only inserts
+ $hash{'maxlogins'} = 1;
+ $hash{'addeddate'} = $cust_pkg->setup
+ if $cust_pkg && $cust_pkg->setup;
+ $hash{'validfrom'} = $cust_pkg->last_bill || $cust_pkg->setup
+ if $cust_pkg && ( $cust_pkg->last_bill || $cust_pkg->setup );
+ $hash{'state'} = $cust_pkg->susp ? 1 : 0
+ if $cust_pkg;
+
+ %hash;
+}
+
+sub radiator_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::radiator::radiator_$method",
+ };
+ $queue->insert(
+ $self->option('datasrc'),
+ $self->option('username'),
+ $self->option('password'),
+ @_,
+ ); # or $queue;
+}
+
+sub radiator_insert { #subroutine, not method
+ my $dbh = radiator_connect(shift, shift, shift);
+ my %hash = @_;
+ $hash{'state'} = 0; #see "random stuff" above
+ $hash{'badlogins'} = 0; #see "random stuff" above
+
+ my $sth = $dbh->prepare(
+ "INSERT INTO $radusers ( ". join(', ', keys %hash ). ' ) '.
+ 'VALUES ( '. join(', ', map '?', keys %hash ). ' ) '
+ ) or die $dbh->errstr;
+ $sth->execute( values %hash )
+ or die $sth->errstr;
+
+ $dbh->disconnect;
+
+}
+
+sub radiator_replace { #subroutine, not method
+ my $dbh = radiator_connect(shift, shift, shift);
+ my ( $old_username, %hash ) = @_;
+
+ my $sth = $dbh->prepare(
+ "UPDATE $radusers SET ". join(', ', map " $_ = ?", keys %hash ).
+ ' WHERE username = ?'
+ ) or die $dbh->errstr;
+ $sth->execute( values(%hash), $old_username )
+ or die $sth->errstr;
+
+ $dbh->disconnect;
+}
+
+sub radiator_delete { #subroutine, not method
+ my $dbh = radiator_connect(shift, shift, shift);
+ my ( $username ) = @_;
+
+ my $sth = $dbh->prepare(
+ "DELETE FROM $radusers WHERE username = ?"
+ ) or die $dbh->errstr;
+ $sth->execute( $username )
+ or die $sth->errstr;
+
+ $dbh->disconnect;
+}
+
+
+sub radiator_connect {
+ #my($datasrc, $username, $password) = @_;
+ #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
+ DBI->connect(@_) or die $DBI::errstr;
+}
+
+1;
diff --git a/FS/FS/part_export/router.pm b/FS/FS/part_export/router.pm
new file mode 100644
index 0000000..42aa51c
--- /dev/null
+++ b/FS/FS/part_export/router.pm
@@ -0,0 +1,375 @@
+package FS::part_export::router;
+
+=head1 FS::part_export::router
+
+This export connects to a router and transmits commands via telnet or SSH.
+It requires the following custom router fields:
+
+=head1 Required custom fields
+
+=over 4
+
+=item admin_address - IP address (or hostname) to connect.
+
+=item admin_user - Username for the router.
+
+=item admin_password - Password for the router.
+
+=item admin_protocol - Protocol to use for the router. 'telnet' or 'ssh'. The ssh protocol only support password-less (ie. RSA key) authentication. As such, the admin_password field isn't used if ssh is specified.
+
+=item admin_timeout - Time in seconds to wait for a connection.
+
+=item admin_prompt - A regular expression matching the router's prompt. See Net::Telnet for details. Only applies to the 'telnet' protocol.
+
+=item admin_cmd_insert - Insert export command.
+
+=item admin_cmd_insert_error - Insert export command error pattern.
+
+=item admin_cmd_delete - Delete export command.
+
+=item admin_cmd_delete_error - Delete export command error pattern.
+
+=item admin_cmd_replace - Replace export command.
+
+=item admin_cmd_replace_error - Replace export command error pattern.
+
+=item admin_cmd_suspend - Suspend export command.
+
+=item admin_cmd_suspend_error - Support export command error pattern.
+
+=item admin_cmd_unsuspend - Unsuspend export command.
+
+=item admin_cmd_unsuspend_error - Unsuspend export command error pattern.
+
+The admin_cmd_* virtual fields, if set, will be processed in one of two ways. After being expanded, they will be run on the router specified by admin_address using the protocol specified by admin_protocol.
+
+=over 4
+
+=item Text::Template
+
+If the export command contains the string [@--, then it will be processed with Text::Template using [@-- and --@] as delimeters.
+
+=item eval
+
+If the export command does not contain [@--, it will be double quoted and eval'd.
+
+=back
+
+The admin_cmd_*_error virtual fields, if set, define a regular expression that will be matched against the output of the command being run. If the pattern matches, an error will be raised using the output as the error.
+
+If any of the required router virtual fields are not defined, then the export silently declines.
+
+=back
+
+The export itself takes no options.
+
+=cut
+
+use strict;
+use vars qw(@ISA %info $me $DEBUG);
+use Tie::IxHash;
+use Text::Template;
+
+use FS::Record qw(qsearchs);
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'protocol' => {
+ label=>'Protocol',
+ type =>'select',
+ options => [qw(telnet ssh)],
+ default => 'telnet'},
+;
+
+%info = (
+ 'svc' => 'svc_broadband',
+ 'desc' => 'Send a command to a router.',
+ 'options' => \%options,
+ 'notes' => 'Installation of Net::Telnet from CPAN is required for telnet connections. This export will execute if the following virtual fields are set on the router: admin_user, admin_password, admin_address, admin_timeout, admin_prompt. Option virtual fields are: admin_cmd_insert, admin_cmd_replace, admin_cmd_delete, admin_cmd_suspend, admin_cmd_unsuspend. See the module documentation for a full list of required/supported router virtual fields.',
+);
+
+$me = '[' . __PACKAGE__ . ']';
+$DEBUG = 1;
+
+
+sub rebless { shift; }
+
+sub _field_prefix { 'admin'; }
+
+sub _req_router_fields {
+ map {
+ $_[0]->_field_prefix . '_' . $_
+ } (qw(address prompt user));
+}
+
+sub _export_insert {
+ my($self) = shift;
+ warn "Running insert for " . ref($self);
+ $self->_export_command('insert', @_);
+}
+
+sub _export_delete {
+ my($self) = shift;
+ $self->_export_command('delete', @_);
+}
+
+sub _export_suspend {
+ my($self) = shift;
+ $self->_export_command('suspend', @_);
+}
+
+sub _export_unsuspend {
+ my($self) = shift;
+ $self->_export_command('unsuspend', @_);
+}
+
+sub _export_replace {
+ my($self) = shift;
+ $self->_export_command('replace', @_);
+}
+
+sub _export_command {
+ my ($self, $action, $svc_broadband) = (shift, shift, shift);
+ my ($error, $old);
+
+ if ($action eq 'replace') {
+ $old = shift;
+ }
+
+ warn "[debug]$me Processing action '$action'" if $DEBUG;
+
+ # fetch router info
+ my $router = $self->_get_router($svc_broadband, @_);
+ unless ($router) {
+ return "Unable to lookup router for $action export";
+ }
+
+ unless ($self->_check_router_fields($router)) {
+ # Virtual fields aren't defined. Exit silently.
+ warn "[debug]$me Required router virtual fields not defined. Returning..."
+ if $DEBUG;
+ return '';
+ }
+
+ my $args;
+ ($error, $args) = $self->_prepare_args(
+ $action,
+ $router,
+ $svc_broadband,
+ ($old ? $old : ()),
+ @_
+ );
+
+ if ($error) {
+ # Error occured while preparing args.
+ return $error;
+ } elsif (not defined $args) {
+ # Silently decline.
+ warn "[debug]$me Declining '$action' export" if $DEBUG;
+ return '';
+ } # else ... queue the export.
+
+ warn "[debug]$me Queueing with args: " . join(', ', @$args) if $DEBUG;
+
+ return(
+ $self->_queue(
+ $svc_broadband->svcnum,
+ $self->_get_cmd_sub($svc_broadband, $router),
+ @$args
+ )
+ );
+
+}
+
+sub _prepare_args {
+
+ my ($self, $action, $router, $svc_broadband) = (shift, shift, shift, shift);
+ my $old = shift if ($action eq 'replace');
+ my $error = '';
+
+ my $field_prefix = $self->_field_prefix;
+ my $command = $router->getfield("${field_prefix}_cmd_${action}");
+ unless ($command) {
+ warn "[debug]$me router custom field '${field_prefix}_cmd_$action' "
+ . "is not defined." if $DEBUG;
+ return '';
+ }
+
+ if ($command =~ /\[\@--/) { # Use Text::Template
+
+ my $template_data = {};
+
+ if ($action eq 'replace') {
+ $template_data->{"old_$_"} = $old->getfield($_) foreach $old->fields;
+ $template_data->{"new_$_"} = $svc_broadband->getfield($_)
+ foreach $svc_broadband->fields;
+ } else {
+ $template_data->{$_} = $svc_broadband->getfield($_)
+ foreach $svc_broadband->fields;
+ }
+
+ my $template = new Text::Template (
+ TYPE => 'STRING',
+ SOURCE => $command,
+ DELIMITERS => [ '[@--', '--@]' ],
+ ) or return "Unable to construct template for router command: "
+ . $Text::Template::ERROR;
+
+ $command = $template->fill_in(
+ HASH => $template_data,
+ BROKEN_ARG => \$error,
+ BROKEN => sub {
+ my %bargs = @_;
+ my $err = $bargs{'arg'};
+ $$err = $bargs{'error'};
+ return undef;
+ },
+ );
+
+ if (not defined $command or $error) {
+ $error ||= $Text::Template::ERROR;
+ return "Unable to fill-in template for router command: $error";
+ }
+
+ } else { # Use eval
+ no strict 'vars';
+ no strict 'refs';
+
+ if ($action eq 'replace') {
+ ${"old_$_"} = $old->getfield($_) foreach $old->fields;
+ ${"new_$_"} = $svc_broadband->getfield($_) foreach $svc_broadband->fields;
+ $command = eval(qq("$command"));
+ } else {
+ ${$_} = $svc_broadband->getfield($_) foreach $svc_broadband->fields;
+ $command = eval(qq("$command"));
+ }
+ return $@ if $@;
+ }
+
+ my $args = [
+ 'user' => $router->getfield($field_prefix . '_user'),
+ 'password' => $router->getfield($field_prefix . '_password'),
+ 'host' => $router->getfield($field_prefix . '_address'),
+ 'Timeout' => $router->getfield($field_prefix . '_timeout'),
+ 'Prompt' => $router->getfield($field_prefix . '_prompt'),
+ 'command' => $command,
+ ];
+
+ my $error_check = $router->getfield("${field_prefix}_cmd_${action}_error");
+ push(@$args, ('error_check' => $error_check)) if ($error_check);
+
+ return('', $args);
+
+}
+
+sub _get_cmd_sub {
+
+ my ($self, $svc_broadband, $router) = (shift, shift, shift);
+
+ my $protocol = (
+ $router->getfield($self->_field_prefix . '_protocol') =~ /^(telnet|ssh)$/
+ ) ? $1 : 'telnet';
+
+ return(ref($self)."::".$protocol."_cmd");
+
+}
+
+sub _check_router_fields {
+
+ my ($self, $router, $action) = (shift, shift, shift);
+ my @check_fields = $self->_req_router_fields;
+
+ foreach (@check_fields) {
+ if ($router->getfield($_) eq '') {
+ warn "[debug]$me Required field '$_' is unset" if $DEBUG;
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+}
+
+sub _queue {
+ my( $self, $svcnum, $cmd_sub ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ };
+ $queue->job($cmd_sub);
+ $queue->insert(@_);
+}
+
+sub _get_router {
+ my ($self, $svc_broadband, %args) = (shift, shift, shift, @_);
+
+ my $router;
+ if ($args{'routernum'}) {
+ $router = qsearchs('router', { routernum => $args{'routernum'}});
+ } else {
+ $router = $svc_broadband->addr_block->router;
+ }
+
+ return($router);
+
+}
+
+
+# Subroutines
+sub ssh_cmd {
+ my %arg = @_;
+
+ eval 'use Net::SSH \'0.08\'';
+ die $@ if $@;
+
+ my @out = &Net::SSH::ssh_cmd( { @_ } );
+ my $error = &_cmd_error_check(\%arg, \@out);
+
+ die ("Error while processing ssh command: $error") if $error;
+
+ return '';
+
+}
+
+sub telnet_cmd {
+ my %arg = @_;
+
+ eval 'use Net::Telnet';
+ die $@ if $@;
+
+ my $t = new Net::Telnet (Timeout => $arg{'Timeout'},
+ Prompt => $arg{'Prompt'});
+ $t->open($arg{'host'});
+ $t->login($arg{'user'}, $arg{'password'});
+ my @out = $t->cmd($arg{'command'});
+ my $error = &_cmd_error_check(\%arg, \@out);
+
+ die ("Error while processing telnet command: $error") if $error;
+
+ return '';
+
+}
+
+sub _cmd_error_check {
+ my ($arg, $out) = (shift, shift);
+
+ die "_cmd_error_check called without proper arguments"
+ unless (ref($arg) eq 'HASH' and ref($out) eq 'ARRAY');
+
+ unless (exists($arg->{'error_check'}) and $arg->{'error_check'} ne '') {
+ #Preserve default behaviour and return output if a check isn't defined.
+ warn "Output from router command: " . join('', @$out) if $DEBUG;
+ return '';
+ }
+
+ my $error_check = $arg->{'error_check'};
+ foreach (@$out) {
+ return $_ if /$error_check/;
+ }
+
+ return '';
+
+}
+
+1;
diff --git a/FS/FS/part_export/shellcommands.pm b/FS/FS/part_export/shellcommands.pm
new file mode 100644
index 0000000..c55fa36
--- /dev/null
+++ b/FS/FS/part_export/shellcommands.pm
@@ -0,0 +1,401 @@
+package FS::part_export::shellcommands;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use String::ShellQuote;
+use FS::part_export;
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'useradd' => { label=>'Insert command',
+ default=>'useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username'
+ #default=>'cp -pr /etc/skel $dir; chown -R $uid.$gid $dir'
+ },
+ 'useradd_stdin' => { label=>'Insert command STDIN',
+ type =>'textarea',
+ default=>'',
+ },
+ 'userdel' => { label=>'Delete command',
+ default=>'userdel -r $username',
+ #default=>'rm -rf $dir',
+ },
+ 'userdel_stdin' => { label=>'Delete command STDIN',
+ type =>'textarea',
+ default=>'',
+ },
+ 'usermod' => { label=>'Modify command',
+ default=>'usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -g $new_gid -p $new_crypt_password $old_username',
+ #default=>'[ -d $old_dir ] && mv $old_dir $new_dir || ( '.
+ # 'chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; '.
+ # 'find . -depth -print | cpio -pdm $new_dir; '.
+ # 'chmod u-t $new_dir; chown -R $uid.$gid $new_dir; '.
+ # 'rm -rf $old_dir'.
+ #')'
+ },
+ 'usermod_stdin' => { label=>'Modify command STDIN',
+ type =>'textarea',
+ default=>'',
+ },
+ 'usermod_pwonly' => { label=>'Disallow username, domain, uid, gid, and dir changes', #and RADIUS group changes',
+ type =>'checkbox',
+ },
+ 'usermod_nousername' => { label=>'Disallow just username changes',
+ type =>'checkbox',
+ },
+ 'suspend' => { label=>'Suspension command',
+ default=>'usermod -L $username',
+ },
+ 'suspend_stdin' => { label=>'Suspension command STDIN',
+ default=>'',
+ },
+ 'unsuspend' => { label=>'Unsuspension command',
+ default=>'usermod -U $username',
+ },
+ 'unsuspend_stdin' => { label=>'Unsuspension command STDIN',
+ default=>'',
+ },
+ 'crypt' => { label => 'Default password encryption',
+ type=>'select', options=>[qw(crypt md5)],
+ default => 'crypt',
+ },
+ 'groups_susp_reason' => { label =>
+ 'Radius group mapping to reason (via template user)',
+ type => 'textarea',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' =>
+ 'Real-time export via remote SSH (i.e. useradd, userdel, etc.)',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => <<'END'
+Run remote commands via SSH. Usernames are considered unique (also see
+shellcommands_withdomain). You probably want this if the commands you are
+running will not accept a domain as a parameter. You will need to
+<a href="../docs/ssh.html">setup SSH for unattended operation</a>.
+
+<BR><BR>Use these buttons for some useful presets:
+<UL>
+ <LI>
+ <INPUT TYPE="button" VALUE="Linux" onClick='
+ this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username";
+ this.form.useradd_stdin.value = "";
+ this.form.userdel.value = "userdel -r $username";
+ this.form.userdel_stdin.value="";
+ this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -g $new_gid -p $new_crypt_password $old_username";
+ this.form.usermod_stdin.value = "";
+ this.form.suspend.value = "usermod -L $username";
+ this.form.suspend_stdin.value="";
+ this.form.unsuspend.value = "usermod -U $username";
+ this.form.unsuspend_stdin.value="";
+ '>
+ <LI>
+ <INPUT TYPE="button" VALUE="FreeBSD before 4.10 / 5.3" onClick='
+ this.form.useradd.value = "lockf /etc/passwd.lock pw useradd $username -d $dir -m -s $shell -u $uid -c $finger -h 0";
+ this.form.useradd_stdin.value = "$_password\n";
+ this.form.userdel.value = "lockf /etc/passwd.lock pw userdel $username -r"; this.form.userdel_stdin.value="";
+ this.form.usermod.value = "lockf /etc/passwd.lock pw usermod $old_username -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -g $new_gid -c $new_finger -h 0";
+ this.form.usermod_stdin.value = "$new__password\n"; this.form.suspend.value = "lockf /etc/passwd.lock pw lock $username";
+ this.form.suspend_stdin.value="";
+ this.form.unsuspend.value = "lockf /etc/passwd.lock pw unlock $username"; this.form.unsuspend_stdin.value="";
+ '>
+ Note: On FreeBSD versions before 5.3 and 4.10 (4.10 is after 4.9, not
+ 4.1!), due to deficient locking in pw(1), you must disable the chpass(1),
+ chsh(1), chfn(1), passwd(1), and vipw(1) commands, or replace them with
+ wrappers that prepend "lockf /etc/passwd.lock". Alternatively, apply the
+ patch in
+ <A HREF="http://www.freebsd.org/cgi/query-pr.cgi?pr=23501">FreeBSD PR#23501</A>
+ and use the "FreeBSD 4.10 / 5.3 or later" button below.
+ <LI>
+ <INPUT TYPE="button" VALUE="FreeBSD 4.10 / 5.3 or later" onClick='
+ this.form.useradd.value = "pw useradd $username -d $dir -m -s $shell -u $uid -g $gid -c $finger -h 0";
+ this.form.useradd_stdin.value = "$_password\n";
+ this.form.userdel.value = "pw userdel $username -r";
+ this.form.userdel_stdin.value="";
+ this.form.usermod.value = "pw usermod $old_username -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -g $new_gid -c $new_finger -h 0";
+ this.form.usermod_stdin.value = "$new__password\n";
+ this.form.suspend.value = "pw lock $username";
+ this.form.suspend_stdin.value="";
+ this.form.unsuspend.value = "pw unlock $username";
+ this.form.unsuspend_stdin.value="";
+ '>
+ <LI>
+ <INPUT TYPE="button" VALUE="NetBSD/OpenBSD" onClick='
+ this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username";
+ this.form.useradd_stdin.value = "";
+ this.form.userdel.value = "userdel -r $username";
+ this.form.userdel_stdin.value="";
+ this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -g $new_gid -p $new_crypt_password $old_username";
+ this.form.usermod_stdin.value = "";
+ this.form.suspend.value = "";
+ this.form.suspend_stdin.value="";
+ this.form.unsuspend.value = "";
+ this.form.unsuspend_stdin.value="";
+ '>
+ <LI>
+ <INPUT TYPE="button" VALUE="Just maintain directories (use with sysvshell or bsdshell)" onClick='
+ this.form.useradd.value = "cp -pr /etc/skel $dir; chown -R $uid.$gid $dir"; this.form.useradd_stdin.value = "";
+ this.form.usermod.value = "[ -d $old_dir ] && mv $old_dir $new_dir || ( chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; find . -depth -print | cpio -pdm $new_dir; chmod u-t $new_dir; chown -R $new_uid.$new_gid $new_dir; rm -rf $old_dir )";
+ this.form.usermod_stdin.value = "";
+ this.form.userdel.value = "rm -rf $dir";
+ this.form.userdel_stdin.value="";
+ this.form.suspend.value = "";
+ this.form.suspend_stdin.value="";
+ this.form.unsuspend.value = "";
+ this.form.unsuspend_stdin.value="";
+ '>
+</UL>
+
+The following variables are available for interpolation (prefixed with new_ or
+old_ for replace operations):
+<UL>
+ <LI><code>$username</code>
+ <LI><code>$_password</code>
+ <LI><code>$quoted_password</code> - unencrypted password, already quoted for the shell (do not add additional quotes).
+ <LI><code>$crypt_password</code> - encrypted password. When used on the command line (rather than STDIN), it will be quoted for the shell already (do not add additional quotes).
+ <LI><code>$ldap_password</code> - Password in LDAP/RFC2307 format (for example, "{PLAIN}himom", "{CRYPT}94pAVyK/4oIBk" or "{MD5}5426824942db4253f87a1009fd5d2d4"). When used on the command line (rather than STDIN), it will be quoted for the shell already (do not add additional quotes).
+ <LI><code>$uid</code>
+ <LI><code>$gid</code>
+ <LI><code>$finger</code> - GECOS. When used on the command line (rather than STDIN), it will be quoted for the shell already (do not add additional quotes).
+ <LI><code>$first</code> - First name of GECOS. When used on the command line (rather than STDIN), it will be quoted for the shell already (do not add additional quotes).
+ <LI><code>$last</code> - Last name of GECOS. When used on the command line (rather than STDIN), it will be quoted for the shell already (do not add additional quotes).
+ <LI><code>$dir</code> - home directory
+ <LI><code>$shell</code>
+ <LI><code>$quota</code>
+ <LI><code>@radius_groups</code>
+ <LI><code>$reasonnum (when suspending)</code>
+ <LI><code>$reasontext (when suspending)</code>
+ <LI><code>$reasontypenum (when suspending)</code>
+ <LI><code>$reasontypetext (when suspending)</code>
+ <LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.
+</UL>
+END
+);
+
+sub _groups_susp_reason_map { shift->_map('groups_susp_reason'); }
+
+sub _map {
+ my $self = shift;
+ map { reverse(/^\s*(\S+)\s*(.*)\s*$/) } split("\n", $self->option(shift) );
+}
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self) = shift;
+ $self->_export_command('useradd', @_);
+}
+
+sub _export_delete {
+ my($self) = shift;
+ $self->_export_command('userdel', @_);
+}
+
+sub _export_suspend {
+ my($self) = shift;
+ $self->_export_command_or_super('suspend', @_);
+}
+
+sub _export_unsuspend {
+ my($self) = shift;
+ $self->_export_command_or_super('unsuspend', @_);
+}
+
+sub _export_command_or_super {
+ my($self, $action) = (shift, shift);
+ if ( $self->option($action) =~ /^\s*$/ ) {
+ my $method = "SUPER::_export_$action";
+ $self->$method(@_);
+ } else {
+ $self->_export_command($action, @_);
+ }
+};
+
+sub _export_command {
+ my ( $self, $action, $svc_acct) = (shift, shift, shift);
+ my $command = $self->option($action);
+ return '' if $command =~ /^\s*$/;
+ my $stdin = $self->option($action."_stdin");
+
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${$_} = $svc_acct->getfield($_) foreach $svc_acct->fields;
+
+ # snarfs are unused at this point?
+ my $count = 1;
+ foreach my $acct_snarf ( $svc_acct->acct_snarf ) {
+ ${"snarf_$_$count"} = shell_quote( $acct_snarf->get($_) )
+ foreach qw( machine username _password );
+ $count++;
+ }
+ }
+
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ if ( $cust_pkg ) {
+ $email = ( grep { $_ !~ /^(POST|FAX)$/ } $cust_pkg->cust_main->invoicing_list )[0];
+ } else {
+ $email = '';
+ }
+
+ $finger =~ /^(.*)\s+(\S+)$/ or $finger =~ /^((.*))$/;
+ ($first, $last ) = ( $1, $2 );
+ $domain = $svc_acct->domain;
+
+ $quoted_password = shell_quote $_password;
+
+ $crypt_password = $svc_acct->crypt_password( $self->option('crypt') );
+ $ldap_password = $svc_acct->ldap_password( $self->option('crypt') );
+
+ @radius_groups = $svc_acct->radius_groups;
+
+ my ($reasonnum, $reasontext, $reasontypenum, $reasontypetext);
+ if ( $cust_pkg && $action eq 'suspend' &&
+ (my $r = $cust_pkg->last_reason('susp')) )
+ {
+ $reasonnum = $r->reasonnum;
+ $reasontext = $r->reason;
+ $reasontypenum = $r->reason_type;
+ $reasontypetext = $r->reasontype->type;
+
+ my %reasonmap = $self->_groups_susp_reason_map;
+ my $userspec = '';
+ $userspec = $reasonmap{$reasonnum}
+ if exists($reasonmap{$reasonnum});
+ $userspec = $reasonmap{$reasontext}
+ if (!$userspec && exists($reasonmap{$reasontext}));
+
+ my $suspend_user;
+ if ( $userspec =~ /^\d+$/ ) {
+ $suspend_user = qsearchs( 'svc_acct', { 'svcnum' => $userspec } );
+ } elsif ( $userspec =~ /^\S+\@\S+$/ ) {
+ my ($username,$domain) = split(/\@/, $userspec);
+ for my $user (qsearch( 'svc_acct', { 'username' => $username } )){
+ $suspend_user = $user if $userspec eq $user->email;
+ }
+ } elsif ($userspec) {
+ $suspend_user = qsearchs( 'svc_acct', { 'username' => $userspec } );
+ }
+
+ @radius_groups = $suspend_user->radius_groups
+ if $suspend_user;
+
+ } else {
+ $reasonnum = $reasontext = $reasontypenum = $reasontypetext = '';
+ }
+
+ my $stdin_string = eval(qq("$stdin"));
+
+ $first = shell_quote $first;
+ $last = shell_quote $last;
+ $finger = shell_quote $finger;
+ $crypt_password = shell_quote $crypt_password;
+ $ldap_password = shell_quote $ldap_password;
+
+ my $command_string = eval(qq("$command"));
+
+ $self->shellcommands_queue( $svc_acct->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => $command_string,
+ stdin_string => $stdin_string,
+ );
+}
+
+sub _export_replace {
+ my($self, $new, $old ) = (shift, shift, shift);
+ my $command = $self->option('usermod');
+ my $stdin = $self->option('usermod_stdin');
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${"old_$_"} = $old->getfield($_) foreach $old->fields;
+ ${"new_$_"} = $new->getfield($_) foreach $new->fields;
+ }
+ $new_finger =~ /^(.*)\s+(\S+)$/ or $new_finger =~ /^((.*))$/;
+ ($new_first, $new_last ) = ( $1, $2 );
+ $quoted_new__password = shell_quote $new__password; #old, wrong?
+ $new_quoted_password = shell_quote $new__password; #new, better?
+ $old_domain = $old->domain;
+ $new_domain = $new->domain;
+
+ $new_crypt_password = $new->crypt_password( $self->option('crypt') );
+ $new_ldap_password = $new->ldap_password( $self->option('crypt') );
+
+ @old_radius_groups = $old->radius_groups;
+ @new_radius_groups = $new->radius_groups;
+
+ my $error = '';
+ if ( $self->option('usermod_pwonly') || $self->option('usermod_nousername') ){
+ if ( $old_username ne $new_username ) {
+ $error ||= "can't change username";
+ }
+ }
+ if ( $self->option('usermod_pwonly') ) {
+ if ( $old_domain ne $new_domain ) {
+ $error ||= "can't change domain";
+ }
+ if ( $old_uid != $new_uid ) {
+ $error ||= "can't change uid";
+ }
+ if ( $old_gid != $new_gid ) {
+ $error ||= "can't change gid";
+ }
+ if ( $old_dir ne $new_dir ) {
+ $error ||= "can't change dir";
+ }
+ #if ( join("\n", sort @old_radius_groups) ne
+ # join("\n", sort @new_radius_groups) ) {
+ # $error ||= "can't change RADIUS groups";
+ #}
+ }
+ return $error. ' ('. $self->exporttype. ' to '. $self->machine. ')'
+ if $error;
+
+ my $stdin_string = eval(qq("$stdin"));
+
+ $new_first = shell_quote $new_first;
+ $new_last = shell_quote $new_last;
+ $new_finger = shell_quote $new_finger;
+ $new_crypt_password = shell_quote $new_crypt_password;
+ $new_ldap_password = shell_quote $new_ldap_password;
+
+ my $command_string = eval(qq("$command"));
+
+ $self->shellcommands_queue( $new->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => $command_string,
+ stdin_string => $stdin_string,
+ );
+}
+
+#a good idea to queue anything that could fail or take any time
+sub shellcommands_queue {
+ my( $self, $svcnum ) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::shellcommands::ssh_cmd",
+ };
+ $queue->insert( @_ );
+}
+
+sub ssh_cmd { #subroutine, not method
+ use Net::SSH '0.08';
+ &Net::SSH::ssh_cmd( { @_ } );
+}
+
+#sub shellcommands_insert { #subroutine, not method
+#}
+#sub shellcommands_replace { #subroutine, not method
+#}
+#sub shellcommands_delete { #subroutine, not method
+#}
+
+1;
+
diff --git a/FS/FS/part_export/shellcommands_withdomain.pm b/FS/FS/part_export/shellcommands_withdomain.pm
new file mode 100644
index 0000000..7c5d904
--- /dev/null
+++ b/FS/FS/part_export/shellcommands_withdomain.pm
@@ -0,0 +1,112 @@
+package FS::part_export::shellcommands_withdomain;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::shellcommands;
+
+@ISA = qw(FS::part_export::shellcommands);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'useradd' => { label=>'Insert command',
+ #default=>''
+ },
+ 'useradd_stdin' => { label=>'Insert command STDIN',
+ type =>'textarea',
+ #default=>"$_password\n$_password\n",
+ },
+ 'userdel' => { label=>'Delete command',
+ #default=>'',
+ },
+ 'userdel_stdin' => { label=>'Delete command STDIN',
+ type =>'textarea',
+ #default=>'',
+ },
+ 'usermod' => { label=>'Modify command',
+ default=>'',
+ },
+ 'usermod_stdin' => { label=>'Modify command STDIN',
+ type =>'textarea',
+ #default=>"$_password\n$_password\n",
+ },
+ 'usermod_pwonly' => { label=>'Disallow username, domain, uid, dir and RADIUS group changes',
+ type =>'checkbox',
+ },
+ 'usermod_nousername' => { label=>'Disallow just username changes',
+ type =>'checkbox',
+ },
+ 'suspend' => { label=>'Suspension command',
+ default=>'',
+ },
+ 'suspend_stdin' => { label=>'Suspension command STDIN',
+ default=>'',
+ },
+ 'unsuspend' => { label=>'Unsuspension command',
+ default=>'',
+ },
+ 'unsuspend_stdin' => { label=>'Unsuspension command STDIN',
+ default=>'',
+ },
+ 'crypt' => { label => 'Default password encryption',
+ type=>'select', options=>[qw(crypt md5)],
+ default => 'crypt',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export via remote SSH (vpopmail, ISPMan)',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Run remote commands via SSH. username@domain (rather than just usernames) are
+considered unique (also see shellcommands). You probably want this if the
+commands you are running will accept a domain as a parameter, and will allow
+the same username with different domains. You will need to
+<a href="../docs/ssh.html">setup SSH for unattended operation</a>.
+
+<BR><BR>Use these buttons for some useful presets:
+<UL>
+ <LI><INPUT TYPE="button" VALUE="vpopmail" onClick='
+ this.form.useradd.value = "/home/vpopmail/bin/vadduser $username\\\@$domain $quoted_password";
+ this.form.useradd_stdin.value = "";
+ this.form.userdel.value = "/home/vpopmail/bin/vdeluser $username\\\@$domain";
+ this.form.userdel_stdin.value="";
+ this.form.usermod.value = "/home/vpopmail/bin/vpasswd $new_username\\\@$new_domain $new_quoted_password";
+ this.form.usermod_stdin.value = "";
+ this.form.usermod_pwonly.checked = true;
+ '>
+ <LI><INPUT TYPE="button" VALUE="ISPMan CLI" onClick='
+ this.form.useradd.value = "/usr/local/ispman/bin/ispman.addUser -d $domain -f $first -l $last -q $quota -p $quoted_password $username";
+ this.form.useradd_stdin.value = "";
+ this.form.userdel.value = "/usr/local/ispman/bin/ispman.delUser -d $domain $username";
+ this.form.userdel_stdin.value="";
+ this.form.usermod.value = "/usr/local/ispman/bin/ispman.passwd.user $new_username\\\@$new_domain $new_quoted_password";
+ this.form.usermod_stdin.value = "";
+ this.form.usermod_pwonly.checked = true;
+ '>
+</UL>
+
+The following variables are available for interpolation (prefixed with
+<code>new_</code> or <code>old_</code> for replace operations):
+<UL>
+ <LI><code>$username</code>
+ <LI><code>$domain</code>
+ <LI><code>$_password</code>
+ <LI><code>$quoted_password</code> - unencrypted password, already quoted for the shell (do not add additional quotes)
+ <LI><code>$crypt_password</code> - encrypted password, already quoted for the shell (do not add additional quotes)
+ <LI><code>$uid</code>
+ <LI><code>$gid</code>
+ <LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)
+ <LI><code>$first</code> - First name of GECOS, already quoted for the shell (do not add additional quotes)
+ <LI><code>$last</code> - Last name of GECOS, already quoted for the shell (do not add additional quotes)
+ <LI><code>$dir</code> - home directory
+ <LI><code>$shell</code>
+ <LI><code>$quota</code>
+ <LI><code>@radius_groups</code>
+ <LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.
+</UL>
+END
+);
+
+1;
+
diff --git a/FS/FS/part_export/snmp.pm b/FS/FS/part_export/snmp.pm
new file mode 100644
index 0000000..81b3c7e
--- /dev/null
+++ b/FS/FS/part_export/snmp.pm
@@ -0,0 +1,256 @@
+package FS::part_export::snmp;
+
+=head1 FS::part_export::snmp
+
+This export sends SNMP SETs to a router using the Net::SNMP package. It requires the following custom fields to be defined on a router. If any of the required custom fields are not present, then the export will exit quietly.
+
+=head1 Required custom fields
+
+=over 4
+
+=item snmp_address - IP address (or hostname) of the router/agent
+
+=item snmp_comm - R/W SNMP community of the router/agent
+
+=item snmp_version - SNMP version of the router/agent
+
+=back
+
+=head1 Optional custom fields
+
+=over 4
+
+=item snmp_cmd_insert - SNMP SETs to perform on insert. See L</Formatting>
+
+=item snmp_cmd_replace - SNMP SETs to perform on replace. See L</Formatting>
+
+=item snmp_cmd_delete - SNMP SETs to perform on delete. See L</Formatting>
+
+=item snmp_cmd_suspend - SNMP SETs to perform on suspend. See L</Formatting>
+
+=item snmp_cmd_unsuspend - SNMP SETs to perform on unsuspend. See L</Formatting>
+
+=back
+
+=head1 Formatting
+
+The values for the snmp_cmd_* fields should be formatted as follows:
+
+<OID>|<Data Type>|<expr>[||<OID>|<Data Type>|<expr>[...]]
+
+=over 4
+
+=item OID - SNMP object ID (ex. 1.3.6.1.4.1.1.20). If the OID string starts with a '.', then the Private Enterprise OID (1.3.6.1.4.1) is prepended.
+
+=item Data Type - SNMP data types understood by L<Net::SNMP>, as well as HEX_STRING for convenience. ex. INTEGER, OCTET_STRING, IPADDRESS, ...
+
+=item expr - Expression to be eval'd by freeside. By default, the expression is double quoted and eval'd with all FS::svc_broadband fields available as scalars (ex. $svcnum, $ip_addr, $speed_up). However, if the expression contains a non-escaped double quote, the expression is eval'd without being double quoted. In this case, the expression must be a block of valid perl code that returns the desired value.
+
+You must escape non-delimiter pipes ("|") with a backslash.
+
+=back
+
+=head1 Examples
+
+This is an example for exporting to a Trango Access5830 AP. Newlines inserted for clarity.
+
+=over 4
+
+=item snmp_cmd_delete -
+
+1.3.6.1.4.1.5454.1.20.3.5.1|INTEGER|50||
+1.3.6.1.4.1.5454.1.20.3.5.8|INTEGER|1|
+
+=item snmp_cmd_insert -
+
+1.3.6.1.4.1.5454.1.20.3.5.1|INTEGER|50||
+1.3.6.1.4.1.5454.1.20.3.5.2|HEX_STRING|join("",$radio_addr =~ /[0-9a-fA-F]{2}/g)||
+1.3.6.1.4.1.5454.1.20.3.5.7|INTEGER|1|
+
+=item snmp_cmd_replace -
+
+1.3.6.1.4.1.5454.1.20.3.5.1|INTEGER|50||
+1.3.6.1.4.1.5454.1.20.3.5.8|INTEGER|1||1.3.6.1.4.1.5454.1.20.3.5.1|INTEGER|50||
+1.3.6.1.4.1.5454.1.20.3.5.2|HEX_STRING|join("",$new_radio_addr =~ /[0-9a-fA-F]{2}/g)||
+1.3.6.1.4.1.5454.1.20.3.5.7|INTEGER|1|
+
+=back
+
+=cut
+
+
+use strict;
+use vars qw(@ISA %info $me $DEBUG);
+use Tie::IxHash;
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+use FS::part_export::router;
+
+@ISA = qw(FS::part_export::router);
+
+tie my %options, 'Tie::IxHash', ();
+
+%info = (
+ 'svc' => 'svc_broadband',
+ 'desc' => 'Sends SNMP SETs to an SNMP agent.',
+ 'options' => \%options,
+ 'notes' => 'Requires Net::SNMP. See the documentation for FS::part_export::snmp for required virtual fields and usage information.',
+);
+
+$me= '[' . __PACKAGE__ . ']';
+$DEBUG = 1;
+
+
+sub _field_prefix { 'snmp'; }
+
+sub _req_router_fields {
+ map {
+ $_[0]->_field_prefix . '_' . $_
+ } (qw(address comm version));
+}
+
+sub _get_cmd_sub {
+
+ my ($self, $svc_broadband, $router) = (shift, shift, shift);
+
+ return(ref($self) . '::snmp_cmd');
+
+}
+
+sub _prepare_args {
+
+ my ($self, $action, $router) = (shift, shift, shift);
+ my ($svc_broadband) = shift;
+ my $old;
+ my $field_prefix = $self->_field_prefix;
+
+ if ($action eq 'replace') { $old = shift; }
+
+ my $raw_cmd = $router->getfield("${field_prefix}_cmd_${action}");
+ unless ($raw_cmd) {
+ warn "[debug]$me router custom field '${field_prefix}_cmd_$action' "
+ . "is not defined." if $DEBUG;
+ return '';
+ }
+
+ my $args = [
+ '-hostname' => $router->getfield($field_prefix.'_address'),
+ '-version' => $router->getfield($field_prefix.'_version'),
+ '-community' => $router->getfield($field_prefix.'_comm'),
+ ];
+
+ my @varbindlist = ();
+
+ foreach my $snmp_cmd ($raw_cmd =~ m/(.*?[^\\])(?:\|\||$)/g) {
+
+ warn "[debug]$me snmp_cmd is '$snmp_cmd'" if $DEBUG;
+
+ my ($oid, $type, $expr) = $snmp_cmd =~ m/(.*?[^\\])(?:\||$)/g;
+
+ if ($oid =~ /^([\d\.]+)$/) {
+ $oid = $1;
+ $oid = ($oid =~ /^\./) ? '1.3.6.1.4.1' . $oid : $oid;
+ } else {
+ return "Invalid SNMP OID '$oid'";
+ }
+
+ if ($type =~ /^([A-Z_\d]+)$/) {
+ $type = $1;
+ } else {
+ return "Invalid SNMP ASN.1 type '$type'";
+ }
+
+ if ($expr =~ /^(.*)$/) {
+ $expr = $1;
+ } else {
+ return "Invalid expression '$expr'";
+ }
+
+ {
+ no strict 'vars';
+ no strict 'refs';
+
+ if ($action eq 'replace') {
+ ${"old_$_"} = $old->getfield($_) foreach $old->fields;
+ ${"new_$_"} = $svc_broadband->getfield($_) foreach $svc_broadband->fields;
+ $expr = ($expr =~/[^\\]"/) ? eval($expr) : eval(qq("$expr"));
+ } else {
+ ${$_} = $svc_broadband->getfield($_) foreach $svc_broadband->fields;
+ $expr = ($expr =~/[^\\]"/) ? eval($expr) : eval(qq("$expr"));
+ }
+ return $@ if $@;
+ }
+
+ push @varbindlist, ($oid, $type, $expr);
+
+ }
+
+ push @$args, ('-varbindlist', @varbindlist);
+
+ return('', $args);
+
+}
+
+sub snmp_cmd {
+ eval "use Net::SNMP;";
+ die $@ if $@;
+
+ my %args = ();
+ my @varbindlist = ();
+ while (scalar(@_)) {
+ my $key = shift;
+ if ($key eq '-varbindlist') {
+ push @varbindlist, @_;
+ last;
+ } else {
+ $args{$key} = shift;
+ }
+ }
+
+ my $i = 0;
+ while ($i*3 < scalar(@varbindlist)) {
+ my $type_index = ($i*3)+1;
+ my $type_name = $varbindlist[$type_index];
+
+ # Implementing HEX_STRING outselves since Net::SNMP doesn't. Ewwww!
+ if ($type_name eq 'HEX_STRING') {
+ my $value_index = $type_index + 1;
+ $type_name = 'OCTET_STRING';
+ $varbindlist[$value_index] = pack('H*', $varbindlist[$value_index]);
+ }
+
+ my $type = eval "Net::SNMP::$type_name";
+ if ($@ or not defined $type) {
+ warn $@ if $DEBUG;
+ die "snmp_cmd error: Unable to lookup type '$type_name'";
+ }
+
+ $varbindlist[$type_index] = $type;
+ } continue {
+ $i++;
+ }
+
+ my ($snmp, $error) = Net::SNMP->session(%args);
+ die "snmp_cmd error: $error" unless($snmp);
+
+ my $res = $snmp->set_request('-varbindlist' => \@varbindlist);
+ unless($res) {
+ $error = $snmp->error;
+ $snmp->close;
+ die "snmp_cmd error: " . $error;
+ }
+
+ $snmp->close;
+
+ return '';
+
+}
+
+
+=head1 BUGS
+
+Plenty, I'm sure.
+
+=cut
+
+1;
diff --git a/FS/FS/part_export/soma.pm b/FS/FS/part_export/soma.pm
new file mode 100644
index 0000000..c73d9f9
--- /dev/null
+++ b/FS/FS/part_export/soma.pm
@@ -0,0 +1,412 @@
+package FS::part_export::soma;
+
+use vars qw(@ISA %info %options $DEBUG);
+use Tie::IxHash;
+use FS::Record qw(fields dbh);
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+$DEBUG = 1;
+
+tie %options, 'Tie::IxHash',
+ 'url' => { label => 'Soma OSS-API url', default=>'https://localhost:8088/ossapi/services' },
+ 'data_app_id' => { label => 'SOMA Data Application Id', default => '' },
+;
+
+my $notes = <<'EOT';
+Real-time export of <b>svc_external</b> and <b>svc_broadband</b> record data
+to SOMA Networks <a href="http://www.somanetworks.com">platform</a> via the
+OSS-API.<br><br>
+
+Freeside will attempt to create/delete a cpe for the ESN provided in
+svc_external. If a data application id is provided then freeside will
+use the values provided in svc_broadband to manage the attributes and
+features of that cpe.
+
+EOT
+
+%info = (
+ 'svc' => [ qw ( svc_broadband svc_external ) ],
+ 'desc' => 'Real-time export to SOMA platform',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => $notes,
+);
+
+sub _export_insert {
+ my( $self, $svc ) = ( shift, shift );
+
+ warn "_export_insert called for service ". $svc->svcnum
+ if $DEBUG;
+
+ my %args = ( url => $self->option('url'), method => '_queueable_insert' );
+
+ $args{esn} = $self->esn($svc) or return 'No ESN found!';
+
+ my $svcdb = $svc->cust_svc->part_svc->svcdb;
+ $args{svcdb} = $svcdb;
+ if ( $svcdb eq 'svc_external' ) {
+ #do nothing
+ } elsif ( $svcdb eq 'svc_broadband' ){
+ $args{data_app_id} = $self->option('data_app_id')
+ } else {
+ return "Don't know how to provision $svcdb";
+ }
+
+ warn "dispatching statuschange" if $DEBUG;
+
+ eval { statuschange(%args) };
+ return $@ if $@;
+
+ '';
+}
+
+sub _export_delete {
+ my( $self, $svc ) = ( shift, shift );
+
+ my %args = ( url => $self->option('url'), method => '_queueable_delete' );
+
+ $args{esn} = $self->esn($svc) or return 'No ESN found!';
+
+ my $svcdb = $svc->cust_svc->part_svc->svcdb;
+ $args{svcdb} = $svcdb;
+ if ( $svcdb eq 'svc_external' ) {
+ #do nothing
+ } elsif ( $svcdb eq 'svc_broadband' ){
+ $args{data_app_id} = $self->option('data_app_id')
+ } else {
+ return "Don't know how to provision $svcdb";
+ }
+
+ eval { statuschange(%args) };
+ return $@ if $@;
+
+ '';
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = ( shift, shift, shift );
+
+ my %args = ( url => $self->option('url'), method => '_queueable_replace' );
+
+ $args{esn} = $self->esn($old) or return 'No old ESN found!';
+ $args{new_esn} = $self->esn($new) or return 'No new ESN found!';
+
+ my $svcdb = $old->cust_svc->part_svc->svcdb;
+ $args{svcdb} = $svcdb;
+ if ( $svcdb eq 'svc_external' ) {
+ #do nothing
+ } elsif ( $svcdb eq 'svc_broadband' ){
+ $args{data_app_id} = $self->option('data_app_id')
+ } else {
+ return "Don't know how to provision $svcdb";
+ }
+
+ eval { statuschange(%args) };
+ return $@ if $@;
+
+ '';
+}
+
+sub _export_suspend {
+ my( $self, $svc ) = ( shift, shift );
+
+ $self->queue_statuschange('_queueable_suspend', $svc);
+}
+
+sub _export_unsuspend {
+ my( $self, $svc ) = ( shift, shift );
+
+ $self->queue_statuschange('_queueable_unsuspend', $svc);
+}
+
+sub queue_statuschange {
+ my( $self, $method, $svc ) = @_;
+
+ my %args = ( url => $self->option('url'), method => $method );
+
+ my $svcdb = $svc->cust_svc->part_svc->svcdb;
+ $args{svcdb} = $svcdb;
+ if ( $svcdb eq 'svc_external' ) {
+ #do absolutely nothing
+ return '';
+ } elsif ( $svcdb eq 'svc_broadband' ){
+ $args{data_app_id} = $self->option('data_app_id')
+ } else {
+ return "Don't know how to provision $svcdb";
+ }
+
+ $args{esn} = $self->esn($svc);
+
+ my $queue = new FS::queue {
+ 'svcnum' => $svc->svcnum,
+ 'job' => 'FS::part_export::soma::statuschange',
+ };
+ my $error = $queue->insert( %args );
+
+ return $error if $error;
+
+ '';
+
+}
+
+sub statuschange { # subroutine
+ my( %options ) = @_;
+
+ warn "statuschange called with options ".
+ join (', ', map { "$_ => $options{$_}" } keys(%options))
+ if $DEBUG;
+
+ my $method = $options{method};
+
+ eval "use Net::Soma 0.01 qw(ApplicationDef ApplicationInstance
+ AttributeDef AttributeInstance);";
+ die $@ if $@;
+
+ my %soma_objects = ();
+ foreach my $service ( qw ( CPECollection CPEAccess AppCatalog Applications ) )
+ {
+ $soma_objects{$service} = new Net::Soma ( namespace => $service."Service",
+ url => $options{'url'},
+ die_on_fault => 1,
+ );
+ }
+
+ my $cpeid = eval {$soma_objects{CPECollection}->getCPEByESN( $options{esn} )};
+ warn "failed to find CPE with ESN $options{esn}"
+ if ($DEBUG && !$cpeid);
+
+ if ( $method eq '_queueable_insert' && $options{svcdb} eq 'svc_external' ) {
+ if ( !$cpeid ) {
+ # only type 1 is used at this time
+ $cpeid = $soma_objects{CPECollection}->createCPE( $options{esn}, 1 );
+ } else {
+ $soma_objects{CPECollection}->releaseCPE( $cpeid );
+ die "Soma element for $options{esn} already exists";
+ }
+ }
+
+ die "Can't find soma element for $options{esn}"
+ unless $cpeid;
+
+ warn "dispatching $method from statuschange" if $DEBUG;
+ &{$method}( \%soma_objects, $cpeid, %options );
+
+}
+
+sub _queueable_insert {
+ my( $soma_objects, $cpeid, %options ) = @_;
+
+ warn "_queueable_insert called for $cpeid with options ".
+ join (', ', map { "$_ => $options{$_}" } keys(%options))
+ if $DEBUG;
+
+ my $appid = $options{data_app_id};
+ if ($appid) {
+ my $application =
+ $soma_objects->{AppCatalog}
+ ->getDefaultApplicationInstance($appid, $cpeid);
+
+ my $attribute =
+ $soma_objects->{AppCatalog}
+ ->getDefaultApplicationAttributeInstance(2, 1, $cpeid);
+ $attribute->value('G');
+
+ my $i = 0;
+ foreach my $instance (@{$application->attributes}) {
+ unless ($instance->definitionId == $attribute->definitionId) {
+ $i++; next;
+ }
+ $application->attributes->[$i] = $attribute;
+ last;
+ }
+
+ $soma_objects->{Applications}->subscribeApp( $cpeid, $application );
+ }
+
+ $soma_objects->{CPECollection}->releaseCPE( $cpeid );
+
+ '';
+}
+
+sub _queueable_delete {
+ my( $soma_objects, $cpeid, %options ) = @_;
+
+ my $appid = $options{data_app_id};
+ my $norelease;
+
+ if ($appid) {
+ my $applications =
+ $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
+
+ my $instance_id;
+ foreach $application (@$applications) {
+ next unless $application->definitionId == $appid;
+ $instance_id = $application->instanceId;
+ }
+
+ $soma_objects->{Applications}->unsubscribeApp( $cpeid, $instance_id );
+
+ } else {
+
+ $soma_objects->{CPECollection}->deleteCPE($cpeid);
+ $norelease = 1;
+
+ }
+
+ $soma_objects->{CPECollection}->releaseCPE( $cpeid ) unless $norelease;
+
+ '';
+}
+
+sub _queueable_replace {
+ my( $soma_objects, $cpeid, %options ) = @_;
+
+ my $appid = $options{data_app_id} || '';
+
+ if (exists($options{data_app_id})) {
+ my $applications =
+ $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
+
+ my $instance_id;
+ foreach $application (@$applications) {
+ next unless $application->internalName eq 'dataApplication';
+ if ($application->definitionId != $options{data_app_id}) {
+ $instance_id = $application->instanceId;
+ $soma_objects->{Applications}->unsubscribeApp( $cpeid, $instance_id );
+ }
+ }
+
+ if ($appid && !$instance_id ) {
+ my $application =
+ $soma_objects->{AppCatalog}
+ ->getDefaultApplicationInstance($appid, $cpeid);
+
+ $soma_objects->{Applications}->subscribeApp( $cpeid, $application );
+ }
+
+ } else {
+
+ $soma_objects->{CPEAccess}->switchCPE($cpeid, $options{new_esn})
+ unless( $options{new_esn} eq $options{esn});
+
+ }
+
+ $soma_objects->{CPECollection}->releaseCPE( $cpeid );
+
+ '';
+}
+
+sub _queueable_suspend {
+ my( $soma_objects, $cpeid, %options ) = @_;
+
+ my $appid = $options{data_app_id};
+
+ if ($appid) {
+ my $applications =
+ $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
+
+ my $instance_id;
+ foreach $application (@$applications) {
+ next unless $application->definitionId == $appid;
+
+ $instance_id = $application->instanceId;
+ my $app_def =
+ $soma_objects->{AppCatalog}->getApplicationDef($appid, $cpeid);
+ my @attr_def = grep { $_->internalName eq 'status' }
+ @{$app_def->attributes};
+
+ foreach my $attribute ( @{$application->attributes} ) {
+ next unless $attribute->definitionId == $attr_def[0]->definitionId;
+ $attribute->{value} = 'S';
+
+ $soma_objects->{Applications}->setAppAttribute( $cpeid,
+ $instance_id,
+ $attribute
+ );
+ }
+
+ }
+
+ } else {
+
+ #do nothing
+
+ }
+
+ $soma_objects->{CPECollection}->releaseCPE( $cpeid );
+
+ '';
+}
+
+sub _queueable_unsuspend {
+ my( $soma_objects, $cpeid, %options ) = @_;
+
+ my $appid = $options{data_app_id};
+
+ if ($appid) {
+ my $applications =
+ $soma_objects->{Applications}->getSubscribedApplications( $cpeid );
+
+ my $instance_id;
+ foreach $application (@$applications) {
+ next unless $application->definitionId == $appid;
+
+ $instance_id = $application->instanceId;
+ my $app_def =
+ $soma_objects->{AppCatalog}->getApplicationDef($appid, $cpeid);
+ my @attr_def = grep { $_->internalName eq 'status' }
+ @{$app_def->attributes};
+
+ foreach my $attribute ( @{$application->attributes} ) {
+ next unless $attribute->definitionId == $attr_def[0]->definitionId;
+ $attribute->{value} = 'E';
+
+ $soma_objects->{Applications}->setAppAttribute( $cpeid,
+ $instance_id,
+ $attribute
+ );
+ }
+
+ }
+
+ } else {
+
+ #do nothing
+
+ }
+
+ $soma_objects->{CPECollection}->releaseCPE( $cpeid );
+
+ '';
+}
+
+sub esn {
+ my ( $self, $svc ) = @_;
+ my $svcdb = $svc->cust_svc->part_svc->svcdb;
+
+ if ($svcdb eq 'svc_external') {
+ my $esn = $svc->title;
+ $esn =~ /^\s*([\da-fA-F]{1,16})\s*$/ && ($esn = $1);
+ return sprintf( '%016s', $esn );
+ }
+
+ my $cust_pkg = $svc->cust_svc->cust_pkg;
+ return '' unless $cust_pkg;
+
+ my @cust_svc = grep { $_->part_svc->svcdb eq 'svc_external' &&
+ scalar( $_->part_svc->part_export('soma') )
+ }
+ $cust_pkg->cust_svc;
+ return '' unless scalar(@cust_svc);
+ warn "part_export::soma found multiple ESNs for cust_svc ". $svc->svcnum
+ if scalar( @cust_svc ) > 1;
+
+ my $esn = $cust_svc[0]->svc_x->title;
+ $esn =~ /^\s*([\da-fA-F]{1,16})\s*$/ && ($esn = $1);
+
+ sprintf( '%016s', $esn );
+}
+
+
+1;
diff --git a/FS/FS/part_export/sqlmail.pm b/FS/FS/part_export/sqlmail.pm
new file mode 100644
index 0000000..cbdaf7f
--- /dev/null
+++ b/FS/FS/part_export/sqlmail.pm
@@ -0,0 +1,220 @@
+package FS::part_export::sqlmail;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use Digest::MD5 qw(md5_hex);
+use FS::Record qw(qsearchs);
+use FS::part_export;
+use FS::svc_domain;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'datasrc' => { label => 'DBI data source' },
+ 'username' => { label => 'Database username' },
+ 'password' => { label => 'Database password' },
+ 'server_type' => {
+ label => 'Server type',
+ type => 'select',
+ options => [qw(dovecot_plain dovecot_crypt dovecot_digest_md5 courier_plain
+ courier_crypt)],
+ default => ['dovecot_plain'], },
+ 'svc_acct_table' => { label => 'User Table', default => 'user_acct' },
+ 'svc_forward_table' => { label => 'Forward Table', default => 'forward' },
+ 'svc_domain_table' => { label => 'Domain Table', default => 'domain' },
+ 'svc_acct_fields' => { label => 'svc_acct Export Fields',
+ default => 'username _password domsvc svcnum' },
+ 'svc_forward_fields' => { label => 'svc_forward Export Fields',
+ default => 'srcsvc dstsvc dst' },
+ 'svc_domain_fields' => { label => 'svc_domain Export Fields',
+ default => 'domain svcnum catchall' },
+ 'resolve_dstsvc' => { label => q{Resolve svc_forward.dstsvc to an email address and store it in dst. (Doesn't require that you also export dstsvc.)},
+ type => 'checkbox' },
+;
+
+%info = (
+ 'svc' => [qw( svc_acct svc_domain svc_forward )],
+ 'desc' => 'Real-time export to SQL-backed mail server',
+ 'options' => \%options,
+ 'nodomain' => '',
+ 'notes' => <<'END'
+Database schema can be made to work with Courier IMAP, Exim and Dovecot.
+Others could work but are untested. (more detailed description from
+Kristian / fire2wire? )
+END
+);
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc) = (shift, shift);
+ # this is a svc_something.
+
+ my $svcdb = $svc->cust_svc->part_svc->svcdb;
+ my $export_table = $self->option($svcdb . '_table')
+ or die('Export table not defined for svcdb: ' . $svcdb);
+ my @export_fields = split(/\s+/, $self->option($svcdb . '_fields'));
+ my $svchash = update_values($self, $svc, $svcdb);
+
+ foreach my $key (keys(%$svchash)) {
+ unless (grep { $key eq $_ } @export_fields) {
+ delete $svchash->{$key};
+ }
+ }
+
+ my $error = $self->sqlmail_queue( $svc->svcnum, 'insert',
+ $self->option('server_type'), $export_table,
+ (map { ($_, $svchash->{$_}); } keys(%$svchash)));
+ return $error if $error;
+ '';
+
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ my $svcdb = $new->cust_svc->part_svc->svcdb;
+ my $export_table = $self->option($svcdb . '_table')
+ or die('Export table not defined for svcdb: ' . $svcdb);
+ my @export_fields = split(/\s+/, $self->option($svcdb . '_fields'));
+ my $svchash = update_values($self, $new, $svcdb);
+
+ foreach my $key (keys(%$svchash)) {
+ unless (grep { $key eq $_ } @export_fields) {
+ delete $svchash->{$key};
+ }
+ }
+
+ my $error = $self->sqlmail_queue( $new->svcnum, 'replace',
+ $old->svcnum, $self->option('server_type'), $export_table,
+ (map { ($_, $svchash->{$_}); } keys(%$svchash)));
+ return $error if $error;
+ '';
+
+}
+
+sub _export_delete {
+ my( $self, $svc ) = (shift, shift);
+
+ my $svcdb = $svc->cust_svc->part_svc->svcdb;
+ my $table = $self->option($svcdb . '_table')
+ or die('Export table not defined for svcdb: ' . $svcdb);
+
+ $self->sqlmail_queue( $svc->svcnum, 'delete', $table,
+ $svc->svcnum );
+}
+
+sub sqlmail_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::sqlmail::sqlmail_$method",
+ };
+ $queue->insert(
+ $self->option('datasrc'),
+ $self->option('username'),
+ $self->option('password'),
+ @_,
+ );
+}
+
+sub sqlmail_insert { #subroutine, not method
+ my $dbh = sqlmail_connect(shift, shift, shift);
+ my( $server_type, $table ) = (shift, shift);
+
+ my %attrs = @_;
+
+ map { $attrs{$_} = $attrs{$_} ? qq!'$attrs{$_}'! : 'NULL'; } keys(%attrs);
+ my $query = sprintf("INSERT INTO %s (%s) values (%s)",
+ $table, join(",", keys(%attrs)),
+ join(',', values(%attrs)));
+
+ $dbh->do($query) or die $dbh->errstr;
+ $dbh->disconnect;
+
+ '';
+}
+
+sub sqlmail_delete { #subroutine, not method
+ my $dbh = sqlmail_connect(shift, shift, shift);
+ my( $table, $svcnum ) = @_;
+
+ $dbh->do("DELETE FROM $table WHERE svcnum = $svcnum") or die $dbh->errstr;
+ $dbh->disconnect;
+
+ '';
+}
+
+sub sqlmail_replace {
+ my $dbh = sqlmail_connect(shift, shift, shift);
+ my($oldsvcnum, $server_type, $table) = (shift, shift, shift);
+
+ my %attrs = @_;
+ map { $attrs{$_} = $attrs{$_} ? qq!'$attrs{$_}'! : 'NULL'; } keys(%attrs);
+
+ my $query = "SELECT COUNT(*) FROM $table WHERE svcnum = $oldsvcnum";
+ my $result = $dbh->selectrow_arrayref($query) or die $dbh->errstr;
+
+ if (@$result[0] == 0) {
+ $query = sprintf("INSERT INTO %s (%s) values (%s)",
+ $table, join(",", keys(%attrs)),
+ join(',', values(%attrs)));
+ $dbh->do($query) or die $dbh->errstr;
+ } else {
+ $query = sprintf('UPDATE %s SET %s WHERE svcnum = %s',
+ $table, join(', ', map {"$_ = $attrs{$_}"} keys(%attrs)),
+ $oldsvcnum);
+ $dbh->do($query) or die $dbh->errstr;
+ }
+
+ $dbh->disconnect;
+
+ '';
+}
+
+sub sqlmail_connect {
+ DBI->connect(@_) or die $DBI::errstr;
+}
+
+sub update_values {
+
+ # Update records to conform to a particular server_type.
+
+ my ($self, $svc, $svcdb) = (shift,shift,shift);
+ my $svchash = { %{$svc->hashref} } or return ''; # We need a copy.
+
+ if ($svcdb eq 'svc_acct') {
+ if ($self->option('server_type') eq 'courier_crypt') {
+ my $salt = join '', ('.', '/', 0..9,'A'..'Z', 'a'..'z')[rand 64, rand 64];
+ $svchash->{_password} = crypt($svchash->{_password}, $salt);
+
+ } elsif ($self->option('server_type') eq 'dovecot_plain') {
+ $svchash->{_password} = '{PLAIN}' . $svchash->{_password};
+
+ } elsif ($self->option('server_type') eq 'dovecot_crypt') {
+ my $salt = join '', ('.', '/', 0..9,'A'..'Z', 'a'..'z')[rand 64, rand 64];
+ $svchash->{_password} = '{CRYPT}' . crypt($svchash->{_password}, $salt);
+
+ } elsif ($self->option('server_type') eq 'dovecot_digest_md5') {
+ my $svc_domain = qsearchs('svc_domain', { svcnum => $svc->domsvc });
+ die('Unable to lookup svc_domain with domsvc: ' . $svc->domsvc)
+ unless ($svc_domain);
+
+ my $domain = $svc_domain->domain;
+ my $md5hash = '{DIGEST-MD5}' . md5_hex(join(':', $svchash->{username},
+ $domain, $svchash->{_password}));
+ $svchash->{_password} = $md5hash;
+ }
+ } elsif ($svcdb eq 'svc_forward') {
+ if ($self->option('resolve_dstsvc') && $svc->dstsvc_acct) {
+ $svchash->{dst} = $svc->dstsvc_acct->username . '@' .
+ $svc->dstsvc_acct->svc_domain->domain;
+ }
+ }
+
+ return($svchash);
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/sqlradius.pm b/FS/FS/part_export/sqlradius.pm
new file mode 100644
index 0000000..20475c5
--- /dev/null
+++ b/FS/FS/part_export/sqlradius.pm
@@ -0,0 +1,814 @@
+package FS::part_export::sqlradius;
+
+use vars qw(@ISA @EXPORT_OK $DEBUG %info %options $notes1 $notes2);
+use Exporter;
+use Tie::IxHash;
+use FS::Record qw( dbh qsearch qsearchs str2time_sql );
+use FS::part_export;
+use FS::svc_acct;
+use FS::export_svc;
+use Carp qw( cluck );
+
+@ISA = qw(FS::part_export);
+@EXPORT_OK = qw( sqlradius_connect );
+
+$DEBUG = 0;
+
+tie %options, 'Tie::IxHash',
+ 'datasrc' => { label=>'DBI data source ' },
+ 'username' => { label=>'Database username' },
+ 'password' => { label=>'Database password' },
+ 'ignore_accounting' => {
+ type => 'checkbox',
+ label => 'Ignore accounting records from this database'
+ },
+ 'hide_ip' => {
+ type => 'checkbox',
+ label => 'Hide IP address information on session reports',
+ },
+ 'hide_data' => {
+ type => 'checkbox',
+ label => 'Hide download/upload information on session reports',
+ },
+ 'show_called_station' => {
+ type => 'checkbox',
+ label => 'Show the Called-Station-ID on session reports',
+ },
+ 'overlimit_groups' => { label => 'Radius groups to assign to svc_acct which has exceeded its bandwidth or time limit', } ,
+ 'groups_susp_reason' => { label =>
+ 'Radius group mapping to reason (via template user) (svcnum|username|username@domain reasonnum|reason)',
+ type => 'textarea',
+ },
+
+;
+
+$notes1 = <<'END';
+Real-time export of <b>radcheck</b>, <b>radreply</b> and <b>usergroup</b>
+tables to any SQL database for
+<a href="http://www.freeradius.org/">FreeRADIUS</a>
+or <a href="http://radius.innercite.com/">ICRADIUS</a>.
+END
+
+$notes2 = <<'END';
+An existing RADIUS database will be updated in realtime, but you can use
+<a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.9:Documentation:Developer/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a>
+to delete the entire RADIUS database and repopulate the tables from the
+Freeside database. See the
+<a href="http://search.cpan.org/dist/DBI/DBI.pm#connect">DBI documentation</a>
+and the
+<a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a>
+for the exact syntax of a DBI data source.
+<ul>
+ <li>Using FreeRADIUS 0.9.0 with the PostgreSQL backend, the db_postgresql.sql schema and postgresql.conf queries contain incompatible changes. This is fixed in 0.9.1. Only new installs with 0.9.0 and PostgreSQL are affected - upgrades and other database backends and versions are unaffected.
+ <li>Using ICRADIUS, add a dummy "op" column to your database:
+ <blockquote><code>
+ ALTER&nbsp;TABLE&nbsp;radcheck&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;'=='<br>
+ ALTER&nbsp;TABLE&nbsp;radreply&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;'=='<br>
+ ALTER&nbsp;TABLE&nbsp;radgroupcheck&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;'=='<br>
+ ALTER&nbsp;TABLE&nbsp;radgroupreply&nbsp;ADD&nbsp;COLUMN&nbsp;op&nbsp;VARCHAR(2)&nbsp;NOT&nbsp;NULL&nbsp;DEFAULT&nbsp;'=='
+ </code></blockquote>
+ <li>Using Radiator, see the
+ <a href="http://www.open.com.au/radiator/faq.html#38">Radiator FAQ</a>
+ for configuration information.
+</ul>
+END
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to SQL-backed RADIUS (FreeRADIUS, ICRADIUS)',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => $notes1.
+ 'This export does not export RADIUS realms (see also '.
+ 'sqlradius_withdomain). '.
+ $notes2
+);
+
+sub _groups_susp_reason_map { map { reverse( /^\s*(\S+)\s*(.*)$/ ) }
+ split( "\n", shift->option('groups_susp_reason'));
+}
+
+sub rebless { shift; }
+
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ warn "export_username called on $self with arg $svc_acct" if $DEBUG > 1;
+ $svc_acct->username;
+}
+
+sub _export_insert {
+ my($self, $svc_x) = (shift, shift);
+
+ foreach my $table (qw(reply check)) {
+ my $method = "radius_$table";
+ my %attrib = $svc_x->$method();
+ next unless keys %attrib;
+ my $err_or_queue = $self->sqlradius_queue( $svc_x->svcnum, 'insert',
+ $table, $self->export_username($svc_x), %attrib );
+ return $err_or_queue unless ref($err_or_queue);
+ }
+ my @groups = $svc_x->radius_groups;
+ if ( @groups ) {
+ cluck localtime(). ": queuing usergroup_insert for ". $svc_x->svcnum.
+ " (". $self->export_username($svc_x). " with ". join(", ", @groups)
+ if $DEBUG;
+ my $err_or_queue = $self->sqlradius_queue(
+ $svc_x->svcnum, 'usergroup_insert',
+ $self->export_username($svc_x), @groups );
+ return $err_or_queue unless ref($err_or_queue);
+ }
+ '';
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $jobnum = '';
+ if ( $self->export_username($old) ne $self->export_username($new) ) {
+ my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'rename',
+ $self->export_username($new), $self->export_username($old) );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ $jobnum = $err_or_queue->jobnum;
+ }
+
+ foreach my $table (qw(reply check)) {
+ my $method = "radius_$table";
+ my %new = $new->$method();
+ my %old = $old->$method();
+ if ( grep { !exists $old{$_} #new attributes
+ || $new{$_} ne $old{$_} #changed
+ } keys %new
+ ) {
+ my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'insert',
+ $table, $self->export_username($new), %new );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ my @del = grep { !exists $new{$_} } keys %old;
+ if ( @del ) {
+ my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'attrib_delete',
+ $table, $self->export_username($new), @del );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+ }
+
+ my $error;
+ my (@oldgroups) = $old->radius_groups;
+ my (@newgroups) = $new->radius_groups;
+ $error = $self->sqlreplace_usergroups( $new->svcnum,
+ $self->export_username($new),
+ $jobnum ? $jobnum : '',
+ \@oldgroups,
+ \@newgroups,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ my $new = $svc_acct->clone_suspended;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'insert',
+ 'check', $self->export_username($new), $new->radius_check );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+
+ my $error;
+ my (@newgroups) = $self->suspended_usergroups($svc_acct);
+ $error =
+ $self->sqlreplace_usergroups( $new->svcnum,
+ $self->export_username($new),
+ '',
+ $svc_acct->usergroup,
+ \@newgroups,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $err_or_queue = $self->sqlradius_queue( $svc_acct->svcnum, 'insert',
+ 'check', $self->export_username($svc_acct), $svc_acct->radius_check );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+
+ my $error;
+ my (@oldgroups) = $self->suspended_usergroups($svc_acct);
+ $error = $self->sqlreplace_usergroups( $svc_acct->svcnum,
+ $self->export_username($svc_acct),
+ '',
+ \@oldgroups,
+ $svc_acct->usergroup,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+sub _export_delete {
+ my( $self, $svc_x ) = (shift, shift);
+ my $err_or_queue = $self->sqlradius_queue( $svc_x->svcnum, 'delete',
+ $self->export_username($svc_x) );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub sqlradius_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::sqlradius::sqlradius_$method",
+ };
+ $queue->insert(
+ $self->option('datasrc'),
+ $self->option('username'),
+ $self->option('password'),
+ @_,
+ ) or $queue;
+}
+
+sub suspended_usergroups {
+ my ($self, $svc_acct) = (shift, shift);
+
+ return () unless $svc_acct;
+
+ #false laziness with FS::part_export::shellcommands
+ #subclass part_export?
+
+ my $r = $svc_acct->cust_svc->cust_pkg->last_reason('susp');
+ my %reasonmap = $self->_groups_susp_reason_map;
+ my $userspec = '';
+ if ($r) {
+ $userspec = $reasonmap{$r->reasonnum}
+ if exists($reasonmap{$r->reasonnum});
+ $userspec = $reasonmap{$r->reason}
+ if (!$userspec && exists($reasonmap{$r->reason}));
+ }
+ my $suspend_user;
+ if ($userspec =~ /^d+$/ ){
+ $suspend_user = qsearchs( 'svc_acct', { 'svcnum' => $userspec } );
+ }elsif ($userspec =~ /^\S+\@\S+$/){
+ my ($username,$domain) = split(/\@/, $userspec);
+ for my $user (qsearch( 'svc_acct', { 'username' => $username } )){
+ $suspend_user = $user if $userspec eq $user->email;
+ }
+ }elsif ($userspec){
+ $suspend_user = qsearchs( 'svc_acct', { 'username' => $userspec } );
+ }
+ #esalf
+ return $suspend_user->radius_groups if $suspend_user;
+ ();
+}
+
+sub sqlradius_insert { #subroutine, not method
+ my $dbh = sqlradius_connect(shift, shift, shift);
+ my( $table, $username, %attributes ) = @_;
+
+ foreach my $attribute ( keys %attributes ) {
+
+ my $s_sth = $dbh->prepare(
+ "SELECT COUNT(*) FROM rad$table WHERE UserName = ? AND Attribute = ?"
+ ) or die $dbh->errstr;
+ $s_sth->execute( $username, $attribute ) or die $s_sth->errstr;
+
+ if ( $s_sth->fetchrow_arrayref->[0] ) {
+
+ my $u_sth = $dbh->prepare(
+ "UPDATE rad$table SET Value = ? WHERE UserName = ? AND Attribute = ?"
+ ) or die $dbh->errstr;
+ $u_sth->execute($attributes{$attribute}, $username, $attribute)
+ or die $u_sth->errstr;
+
+ } else {
+
+ my $i_sth = $dbh->prepare(
+ "INSERT INTO rad$table ( UserName, Attribute, op, Value ) ".
+ "VALUES ( ?, ?, ?, ? )"
+ ) or die $dbh->errstr;
+ $i_sth->execute(
+ $username,
+ $attribute,
+ ( $attribute eq 'Password' ? '==' : ':=' ),
+ $attributes{$attribute},
+ ) or die $i_sth->errstr;
+
+ }
+
+ }
+ $dbh->disconnect;
+}
+
+sub sqlradius_usergroup_insert { #subroutine, not method
+ my $dbh = sqlradius_connect(shift, shift, shift);
+ my( $username, @groups ) = @_;
+
+ my $s_sth = $dbh->prepare(
+ "SELECT COUNT(*) FROM usergroup WHERE UserName = ? AND GroupName = ?"
+ ) or die $dbh->errstr;
+
+ my $sth = $dbh->prepare(
+ "INSERT INTO usergroup ( UserName, GroupName ) VALUES ( ?, ? )"
+ ) or die $dbh->errstr;
+
+ foreach my $group ( @groups ) {
+ $s_sth->execute( $username, $group ) or die $s_sth->errstr;
+ if ($s_sth->fetchrow_arrayref->[0]) {
+ warn localtime() . ": sqlradius_usergroup_insert attempted to reinsert " .
+ "$group for $username\n"
+ if $DEBUG;
+ next;
+ }
+ $sth->execute( $username, $group )
+ or die "can't insert into groupname table: ". $sth->errstr;
+ }
+ $dbh->disconnect;
+}
+
+sub sqlradius_usergroup_delete { #subroutine, not method
+ my $dbh = sqlradius_connect(shift, shift, shift);
+ my( $username, @groups ) = @_;
+
+ my $sth = $dbh->prepare(
+ "DELETE FROM usergroup WHERE UserName = ? AND GroupName = ?"
+ ) or die $dbh->errstr;
+ foreach my $group ( @groups ) {
+ $sth->execute( $username, $group )
+ or die "can't delete from groupname table: ". $sth->errstr;
+ }
+ $dbh->disconnect;
+}
+
+sub sqlradius_rename { #subroutine, not method
+ my $dbh = sqlradius_connect(shift, shift, shift);
+ my($new_username, $old_username) = @_;
+ foreach my $table (qw(radreply radcheck usergroup )) {
+ my $sth = $dbh->prepare("UPDATE $table SET Username = ? WHERE UserName = ?")
+ or die $dbh->errstr;
+ $sth->execute($new_username, $old_username)
+ or die "can't update $table: ". $sth->errstr;
+ }
+ $dbh->disconnect;
+}
+
+sub sqlradius_attrib_delete { #subroutine, not method
+ my $dbh = sqlradius_connect(shift, shift, shift);
+ my( $table, $username, @attrib ) = @_;
+
+ foreach my $attribute ( @attrib ) {
+ my $sth = $dbh->prepare(
+ "DELETE FROM rad$table WHERE UserName = ? AND Attribute = ?" )
+ or die $dbh->errstr;
+ $sth->execute($username,$attribute)
+ or die "can't delete from rad$table table: ". $sth->errstr;
+ }
+ $dbh->disconnect;
+}
+
+sub sqlradius_delete { #subroutine, not method
+ my $dbh = sqlradius_connect(shift, shift, shift);
+ my $username = shift;
+
+ foreach my $table (qw( radcheck radreply usergroup )) {
+ my $sth = $dbh->prepare( "DELETE FROM $table WHERE UserName = ?" );
+ $sth->execute($username)
+ or die "can't delete from $table table: ". $sth->errstr;
+ }
+ $dbh->disconnect;
+}
+
+sub sqlradius_connect {
+ #my($datasrc, $username, $password) = @_;
+ #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
+ DBI->connect(@_) or die $DBI::errstr;
+}
+
+sub sqlreplace_usergroups {
+ my ($self, $svcnum, $username, $jobnum, $old, $new) = @_;
+
+ # (sorta) false laziness with FS::svc_acct::replace
+ my @oldgroups = @$old;
+ my @newgroups = @$new;
+ my @delgroups = ();
+ foreach my $oldgroup ( @oldgroups ) {
+ if ( grep { $oldgroup eq $_ } @newgroups ) {
+ @newgroups = grep { $oldgroup ne $_ } @newgroups;
+ next;
+ }
+ push @delgroups, $oldgroup;
+ }
+
+ if ( @delgroups ) {
+ my $err_or_queue = $self->sqlradius_queue( $svcnum, 'usergroup_delete',
+ $username, @delgroups );
+ return $err_or_queue
+ unless ref($err_or_queue);
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ return $error if $error;
+ }
+ }
+
+ if ( @newgroups ) {
+ cluck localtime(). ": queuing usergroup_insert for $svcnum ($username) ".
+ "with ". join(", ", @newgroups)
+ if $DEBUG;
+ my $err_or_queue = $self->sqlradius_queue( $svcnum, 'usergroup_insert',
+ $username, @newgroups );
+ return $err_or_queue
+ unless ref($err_or_queue);
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ return $error if $error;
+ }
+ }
+ '';
+}
+
+
+#--
+
+=item usage_sessions HASHREF
+
+=item usage_sessions TIMESTAMP_START TIMESTAMP_END [ SVC_ACCT [ IP [ PREFIX [ SQL_SELECT ] ] ] ]
+
+New-style: pass a hashref with the following keys:
+
+=over 4
+
+=item stoptime_start - Lower bound for AcctStopTime, as a UNIX timestamp
+
+=item stoptime_end - Upper bound for AcctStopTime, as a UNIX timestamp
+
+=item open_sessions - Only show records with no AcctStopTime (typically used without stoptime_* options and with starttime_* options instead)
+
+=item starttime_start - Lower bound for AcctStartTime, as a UNIX timestamp
+
+=item starttime_end - Upper bound for AcctStartTime, as a UNIX timestamp
+
+=item svc_acct
+
+=item ip
+
+=item prefix
+
+=back
+
+Old-style:
+
+TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
+L<perlfunc/"time">. Also see L<Time::Local> and L<Date::Parse> for conversion
+functions.
+
+SVC_ACCT, if specified, limits the results to the specified account.
+
+IP, if specified, limits the results to the specified IP address.
+
+PREFIX, if specified, limits the results to records with a matching
+Called-Station-ID.
+
+#SQL_SELECT defaults to * if unspecified. It can be useful to set it to
+#SUM(acctsessiontime) or SUM(AcctInputOctets), etc.
+
+Returns an arrayref of hashrefs with the following fields:
+
+=over 4
+
+=item username
+
+=item framedipaddress
+
+=item acctstarttime
+
+=item acctstoptime
+
+=item acctsessiontime
+
+=item acctinputoctets
+
+=item acctoutputoctets
+
+=item calledstationid
+
+=back
+
+=cut
+
+#some false laziness w/cust_svc::seconds_since_sqlradacct
+
+sub usage_sessions {
+ my( $self ) = shift;
+
+ my $opt = {};
+ my($start, $end, $svc_acct, $ip, $prefix) = ( '', '', '', '', '');
+ if ( ref($_[0]) ) {
+ $opt = shift;
+ $start = $opt->{stoptime_start};
+ $end = $opt->{stoptime_end};
+ $svc_acct = $opt->{svc_acct};
+ $ip = $opt->{ip};
+ $prefix = $opt->{prefix};
+ } else {
+ ( $start, $end ) = splice(@_, 0, 2);
+ $svc_acct = @_ ? shift : '';
+ $ip = @_ ? shift : '';
+ $prefix = @_ ? shift : '';
+ #my $select = @_ ? shift : '*';
+ }
+
+ $end ||= 2147483647;
+
+ return [] if $self->option('ignore_accounting');
+
+ my $dbh = sqlradius_connect( map $self->option($_),
+ qw( datasrc username password ) );
+
+ #select a unix time conversion function based on database type
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+
+ my @fields = (
+ qw( username realm framedipaddress
+ acctsessiontime acctinputoctets acctoutputoctets
+ calledstationid
+ ),
+ "$str2time acctstarttime ) as acctstarttime",
+ "$str2time acctstoptime ) as acctstoptime",
+ );
+
+ my @param = ();
+ my @where = ();
+
+ if ( $svc_acct ) {
+ my $username = $self->export_username($svc_acct);
+ if ( $svc_acct =~ /^([^@]+)\@([^@]+)$/ ) {
+ push @where, '( UserName = ? OR ( UserName = ? AND Realm = ? ) )';
+ push @param, $username, $1, $2;
+ } else {
+ push @where, 'UserName = ?';
+ push @param, $username;
+ }
+ }
+
+ if ( length($ip) ) {
+ push @where, ' FramedIPAddress = ?';
+ push @param, $ip;
+ }
+
+ if ( length($prefix) ) {
+ #assume sip: for now, else things get ugly trying to match /^\w+:$prefix/
+ push @where, " CalledStationID LIKE 'sip:$prefix\%'";
+ }
+
+ if ( $start ) {
+ push @where, "$str2time AcctStopTime ) >= ?";
+ push @param, $start;
+ }
+ if ( $end ) {
+ push @where, "$str2time AcctStopTime ) <= ?";
+ push @param, $end;
+ }
+ if ( $opt->{open_sessions} ) {
+ push @where, 'AcctStopTime IS NULL';
+ }
+ if ( $opt->{starttime_start} ) {
+ push @where, "$str2time AcctStartTime ) >= ?";
+ push @param, $opt->{starttime_start};
+ }
+ if ( $opt->{starttime_end} ) {
+ push @where, "$str2time AcctStartTime ) <= ?";
+ push @param, $opt->{starttime_end};
+ }
+
+ my $where = join(' AND ', @where);
+ $where = "WHERE $where" if $where;
+
+ my $sth = $dbh->prepare('SELECT '. join(', ', @fields).
+ " FROM radacct
+ $where
+ ORDER BY AcctStartTime DESC
+ ") or die $dbh->errstr;
+ $sth->execute(@param) or die $sth->errstr;
+
+ [ map { { %$_ } } @{ $sth->fetchall_arrayref({}) } ];
+
+}
+
+=item update_svc_acct
+
+=cut
+
+sub update_svc {
+ my $self = shift;
+
+ my $conf = new FS::Conf;
+
+ my $fdbh = dbh;
+ my $dbh = sqlradius_connect( map $self->option($_),
+ qw( datasrc username password ) );
+
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+ my @fields = qw( radacctid username realm acctsessiontime );
+
+ my @param = ();
+ my $where = '';
+
+ my $sth = $dbh->prepare("
+ SELECT RadAcctId, UserName, Realm, AcctSessionTime,
+ $str2time AcctStartTime), $str2time AcctStopTime),
+ AcctInputOctets, AcctOutputOctets
+ FROM radacct
+ WHERE FreesideStatus IS NULL
+ AND AcctStopTime != 0
+ ") or die $dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+
+ while ( my $row = $sth->fetchrow_arrayref ) {
+ my($RadAcctId, $UserName, $Realm, $AcctSessionTime, $AcctStartTime,
+ $AcctStopTime, $AcctInputOctets, $AcctOutputOctets) = @$row;
+ warn "processing record: ".
+ "$RadAcctId ($UserName\@$Realm for ${AcctSessionTime}s"
+ if $DEBUG;
+
+ $UserName = lc($UserName) unless $conf->exists('username-uppercase');
+
+ #my %search = ( 'username' => $UserName );
+
+ my $extra_sql = '';
+ if ( ref($self) =~ /withdomain/ ) { #well...
+ $extra_sql = " AND '$Realm' = ( SELECT domain FROM svc_domain
+ WHERE svc_domain.svcnum = svc_acct.domsvc ) ";
+ }
+
+ my $oldAutoCommit = $FS::UID::AutoCommit; # can't undo side effects, but at
+ local $FS::UID::AutoCommit = 0; # least we can avoid over counting
+
+ my @svc_acct =
+ grep { qsearch( 'export_svc', { 'exportnum' => $self->exportnum,
+ 'svcpart' => $_->cust_svc->svcpart, } )
+ }
+ qsearch( 'svc_acct',
+ { 'username' => $UserName },
+ '',
+ $extra_sql
+ );
+
+ my $errinfo = "for RADIUS detail RadAcctID $RadAcctId ".
+ "(UserName $UserName, Realm $Realm)";
+ my $status = 'skipped';
+ if ( !@svc_acct ) {
+ warn "WARNING: no svc_acct record found $errinfo - skipping\n";
+ } elsif ( scalar(@svc_acct) > 1 ) {
+ warn "WARNING: multiple svc_acct records found $errinfo - skipping\n";
+ } else {
+
+ my $svc_acct = $svc_acct[0];
+ warn "found svc_acct ". $svc_acct->svcnum. " $errinfo\n" if $DEBUG;
+
+ $svc_acct->last_login($AcctStartTime);
+ $svc_acct->last_logout($AcctStopTime);
+
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ if ( $cust_pkg && $AcctStopTime < ( $cust_pkg->last_bill
+ || $cust_pkg->setup ) ) {
+ $status = 'skipped (too old)';
+ } else {
+ my @st;
+ push @st, _try_decrement($svc_acct, 'seconds', $AcctSessionTime );
+ push @st, _try_decrement($svc_acct, 'upbytes', $AcctInputOctets );
+ push @st, _try_decrement($svc_acct, 'downbytes', $AcctOutputOctets );
+ push @st, _try_decrement($svc_acct, 'totalbytes', $AcctInputOctets
+ + $AcctOutputOctets);
+ $status=join(' ', @st);
+ }
+ }
+
+ warn "setting FreesideStatus to $status $errinfo\n" if $DEBUG;
+ my $psth = $dbh->prepare("UPDATE radacct
+ SET FreesideStatus = ?
+ WHERE RadAcctId = ?"
+ ) or die $dbh->errstr;
+ $psth->execute($status, $RadAcctId) or die $psth->errstr;
+
+ $fdbh->commit or die $fdbh->errstr if $oldAutoCommit;
+
+ }
+
+}
+
+sub _try_decrement {
+ my ($svc_acct, $column, $amount) = @_;
+ if ( $svc_acct->$column !~ /^$/ ) {
+ warn " svc_acct.$column found (". $svc_acct->$column.
+ ") - decrementing\n"
+ if $DEBUG;
+ my $method = 'decrement_' . $column;
+ my $error = $svc_acct->$method($amount);
+ die $error if $error;
+ return 'done';
+ } else {
+ warn " no existing $column value for svc_acct - skipping\n" if $DEBUG;
+ }
+ return 'skipped';
+}
+
+###
+#class methods
+###
+
+sub all_sqlradius {
+ #my $class = shift;
+
+ #don't just look for ->can('usage_sessions'), we're sqlradius-specific
+ # (radiator is supposed to be setup with a radacct table)
+ #i suppose it would be more slick to look for things that inherit from us..
+
+ my @part_export = ();
+ push @part_export, qsearch('part_export', { 'exporttype' => $_ } )
+ foreach qw( sqlradius sqlradius_withdomain radiator phone_sqlradius );
+ @part_export;
+}
+
+sub all_sqlradius_withaccounting {
+ my $class = shift;
+ grep { ! $_->option('ignore_accounting') } $class->all_sqlradius;
+}
+
+1;
+
diff --git a/FS/FS/part_export/sqlradius_withdomain.pm b/FS/FS/part_export/sqlradius_withdomain.pm
new file mode 100644
index 0000000..e5a7151
--- /dev/null
+++ b/FS/FS/part_export/sqlradius_withdomain.pm
@@ -0,0 +1,28 @@
+package FS::part_export::sqlradius_withdomain;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::sqlradius;
+
+tie my %options, 'Tie::IxHash', %FS::part_export::sqlradius::options;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to SQL-backed RADIUS (FreeRADIUS, ICRADIUS) with realms',
+ 'options' => \%options,
+ 'nodomain' => '',
+ 'notes' => $FS::part_export::sqlradius::notes1.
+ 'This export exports domains to RADIUS realms (see also '.
+ 'sqlradius). '.
+ $FS::part_export::sqlradius::notes2
+);
+
+@ISA = qw(FS::part_export::sqlradius);
+
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ $svc_acct->email;
+}
+
+1;
+
diff --git a/FS/FS/part_export/sysvshell.pm b/FS/FS/part_export/sysvshell.pm
new file mode 100644
index 0000000..244c3bf
--- /dev/null
+++ b/FS/FS/part_export/sysvshell.pm
@@ -0,0 +1,25 @@
+package FS::part_export::sysvshell;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export::passwdfile;
+
+@ISA = qw(FS::part_export::passwdfile);
+
+tie my %options, 'Tie::IxHash', %FS::part_export::passwdfile::options;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' =>
+ 'Batch export of /etc/passwd and /etc/shadow files (Linux, Solaris)',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => <<'END'
+MD5 crypt requires installation of
+<a href="http://search.cpan.org/dist/Crypt-PasswdMD5">Crypt::PasswdMD5</a>
+from CPAN. Run bin/sysvshell.export to export the files.
+END
+);
+
+1;
+
diff --git a/FS/FS/part_export/textradius.pm b/FS/FS/part_export/textradius.pm
new file mode 100644
index 0000000..3cd7039
--- /dev/null
+++ b/FS/FS/part_export/textradius.pm
@@ -0,0 +1,191 @@
+package FS::part_export::textradius;
+
+use vars qw(@ISA %info $prefix);
+use Fcntl qw(:flock);
+use Tie::IxHash;
+use FS::UID qw(datasrc);
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'users' => { label=>'users file location', default=>'/etc/raddb/users' },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' =>
+ 'Real-time export to a text /etc/raddb/users file (Livingston, Cistron)',
+ 'options' => \%options,
+ 'notes' => <<'END'
+This will edit a text RADIUS users file in place on a remote server.
+Requires installation of
+<a href="http://search.cpan.org/dist/RADIUS-UserFile">RADIUS::UserFile</a>
+from CPAN. If using RADIUS::UserFile 1.01, make sure to apply
+<a href="http://rt.cpan.org/NoAuth/Bug.html?id=1210">this patch</a>. Also
+make sure <a href="http://rsync.samba.org/">rsync</a> is installed on the
+remote machine, and <a href="../docs/ssh.html">SSH is setup for unattended
+operation</a>.
+END
+);
+
+$prefix = "%%%FREESIDE_CONF%%%/export.";
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+ $err_or_queue = $self->textradius_queue( $svc_acct->svcnum, 'insert',
+ $svc_acct->username, $svc_acct->radius_check, '-', $svc_acct->radius_reply);
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return "can't (yet?) change username with textradius"
+ if $old->username ne $new->username;
+ #return '' unless $old->_password ne $new->_password;
+ $err_or_queue = $self->textradius_queue( $new->svcnum, 'insert',
+ $new->username, $new->radius_check, '-', $new->radius_reply);
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $err_or_queue = $self->textradius_queue( $svc_acct->svcnum, 'delete',
+ $svc_acct->username );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+#a good idea to queue anything that could fail or take any time
+sub textradius_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::textradius::textradius_$method",
+ };
+ $queue->insert(
+ $self->option('user')||'root',
+ $self->machine,
+ $self->option('users'),
+ @_,
+ ) or $queue;
+}
+
+sub textradius_insert { #subroutine, not method
+ my( $user, $host, $users, $username, @attributes ) = @_;
+
+ #silly arg processing
+ my($att, @check);
+ push @check, $att while @attributes && ($att=shift @attributes) ne '-';
+ my %check = @check;
+ my %reply = @attributes;
+
+ my $file = textradius_download($user, $host, $users);
+
+ eval "use RADIUS::UserFile;";
+ die $@ if $@;
+
+ my $userfile = new RADIUS::UserFile(
+ File => $file,
+ Who => [ $username ],
+ Check_Items => [ keys %check ],
+ ) or die "error parsing $file";
+
+ $userfile->remove($username);
+ $userfile->add(
+ Who => $username,
+ Attributes => { %check, %reply },
+ Comment => 'user added by Freeside',
+ ) or die "error adding to $file";
+
+ $userfile->update( Who => [ $username ] )
+ or die "error updating $file";
+
+ textradius_upload($user, $host, $users);
+
+}
+
+sub textradius_delete { #subroutine, not method
+ my( $user, $host, $users, $username ) = @_;
+
+ my $file = textradius_download($user, $host, $users);
+
+ eval "use RADIUS::UserFile;";
+ die $@ if $@;
+
+ my $userfile = new RADIUS::UserFile(
+ File => $file,
+ Who => [ $username ],
+ ) or die "error parsing $file";
+
+ $userfile->remove($username);
+
+ $userfile->update( Who => [ $username ] )
+ or die "error updating $file";
+
+ textradius_upload($user, $host, $users);
+}
+
+sub textradius_download {
+ my( $user, $host, $users ) = @_;
+
+ my $dir = $prefix. datasrc;
+ mkdir $dir, 0700 or die $! unless -d $dir;
+ $dir .= "/$host";
+ mkdir $dir, 0700 or die $! unless -d $dir;
+
+ my $dest = "$dir/users";
+
+ eval "use File::Rsync;";
+ die $@ if $@;
+ my $rsync = File::Rsync->new({ rsh => 'ssh' });
+
+ open(LOCK, "+>>$dest.lock")
+ and flock(LOCK,LOCK_EX)
+ or die "can't open $dest.lock: $!";
+
+ $rsync->exec( {
+ src => "$user\@$host:$users",
+ dest => $dest,
+ } ); # true/false return value from exec is not working, alas
+ if ( $rsync->err ) {
+ die "error downloading $user\@$host:$users : ".
+ 'exit status: '. $rsync->status. ', '.
+ 'STDERR: '. join(" / ", $rsync->err). ', '.
+ 'STDOUT: '. join(" / ", $rsync->out);
+ }
+
+ $dest;
+}
+
+sub textradius_upload {
+ my( $user, $host, $users ) = @_;
+
+ my $dir = $prefix. datasrc. "/$host";
+
+ eval "use File::Rsync;";
+ die $@ if $@;
+ my $rsync = File::Rsync->new({
+ rsh => 'ssh',
+ #dry_run => 1,
+ });
+ $rsync->exec( {
+ src => "$dir/users",
+ dest => "$user\@$host:$users",
+ } ); # true/false return value from exec is not working, alas
+ if ( $rsync->err ) {
+ die "error uploading to $user\@$host:$users : ".
+ 'exit status: '. $rsync->status. ', '.
+ 'STDERR: '. join(" / ", $rsync->err). ', '.
+ 'STDOUT: '. join(" / ", $rsync->out);
+ }
+
+ flock(LOCK,LOCK_UN);
+ close LOCK;
+
+}
+
+1;
+
diff --git a/FS/FS/part_export/trango.pm b/FS/FS/part_export/trango.pm
new file mode 100644
index 0000000..e7f1126
--- /dev/null
+++ b/FS/FS/part_export/trango.pm
@@ -0,0 +1,434 @@
+package FS::part_export::trango;
+
+=head1 FS::part_export::trango
+
+This export sends SNMP SETs to a router using the Net::SNMP package. It requires the following custom fields to be defined on a router. If any of the required custom fields are not present, then the export will exit quietly.
+
+=head1 Required custom fields
+
+=over 4
+
+=item trango_address - IP address (or hostname) of the Trango AP.
+
+=item trango_comm - R/W SNMP community of the Trango AP.
+
+=item trango_ap_type - Trango AP Model. Currently 'access5830' is the only supported option.
+
+=back
+
+=head1 Optional custom fields
+
+=over 4
+
+=item trango_baseid - Base ID of the Trango AP. See L</"Generating SU IDs">.
+
+=item trango_apid - AP ID of the Trango AP. See L</"Generating SU IDs">.
+
+=back
+
+=head1 Generating SU IDs
+
+This export will/must generate a unique SU ID for each service exported to a Trango AP. It can be done such that SU IDs are globally unique, unique per Base ID, or unique per Base ID/AP ID pair. This is accomplished by setting neither trango_baseid and trango_apid, only trango_baseid, or both trango_baseid and trango_apid, respectively. An SU ID will be generated if the FS::svc_broadband virtual field specified by suid_field export option is unset, otherwise the existing value will be used.
+
+=head1 Device Support
+
+This export has been tested with the Trango Access5830 AP.
+
+
+=cut
+
+
+use strict;
+use vars qw(@ISA %info $me $DEBUG $trango_mib $counter_dir);
+
+use FS::UID qw(dbh datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export::snmp;
+
+use Tie::IxHash;
+use File::CounterFile;
+use Data::Dumper qw(Dumper);
+
+@ISA = qw(FS::part_export::snmp);
+
+tie my %options, 'Tie::IxHash', (
+ 'suid_field' => {
+ 'label' => 'Trango SU ID field',
+ 'default' => 'trango_suid',
+ 'notes' => 'Name of the FS::svc_broadband virtual field that will contain the SU ID.',
+ },
+ 'mac_field' => {
+ 'label' => 'Trango MAC address field',
+ 'default' => '',
+ 'notes' => 'Name of the FS::svc_broadband virtual field that will contain the SU\'s MAC address.',
+ },
+);
+
+%info = (
+ 'svc' => 'svc_broadband',
+ 'desc' => 'Sends SNMP SETs to a Trango AP.',
+ 'options' => \%options,
+ 'notes' => 'Requires Net::SNMP. See the documentation for FS::part_export::trango for required virtual fields and usage information.',
+);
+
+$me= '[' . __PACKAGE__ . ']';
+$DEBUG = 1;
+
+$trango_mib = {
+ 'access5830' => {
+ 'snmpversion' => 'snmpv1',
+ 'varbinds' => {
+ 'insert' => [
+ { # sudbDeleteOrAddID
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.1',
+ 'type' => 'INTEGER',
+ 'value' => \&_trango_access5830_sudbDeleteOrAddId,
+ },
+ { # sudbAddMac
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.2',
+ 'type' => 'HEX_STRING',
+ 'value' => \&_trango_access5830_sudbAddMac,
+ },
+ { # sudbAddSU
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.7',
+ 'type' => 'INTEGER',
+ 'value' => 1,
+ },
+ ],
+ 'delete' => [
+ { # sudbDeleteOrAddID
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.1',
+ 'type' => 'INTEGER',
+ 'value' => \&_trango_access5830_sudbDeleteOrAddId,
+ },
+ { # sudbDeleteSU
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.8',
+ 'type' => 'INTEGER',
+ 'value' => 1,
+ },
+ ],
+ 'replace' => [
+ { # sudbDeleteOrAddID
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.1',
+ 'type' => 'INTEGER',
+ 'value' => \&_trango_access5830_sudbDeleteOrAddId,
+ },
+ { # sudbDeleteSU
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.8',
+ 'type' => 'INTEGER',
+ 'value' => 1,
+ },
+ { # sudbDeleteOrAddID
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.1',
+ 'type' => 'INTEGER',
+ 'value' => \&_trango_access5830_sudbDeleteOrAddId,
+ },
+ { # sudbAddMac
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.2',
+ 'type' => 'HEX_STRING',
+ 'value' => \&_trango_access5830_sudbAddMac,
+ },
+ { # sudbAddSU
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.7',
+ 'type' => 'INTEGER',
+ 'value' => 1,
+ },
+ ],
+ 'suspend' => [
+ { # sudbDeleteOrAddID
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.1',
+ 'type' => 'INTEGER',
+ 'value' => \&_trango_access5830_sudbDeleteOrAddId,
+ },
+ { # sudbDeleteSU
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.8',
+ 'type' => 'INTEGER',
+ 'value' => 1,
+ },
+ ],
+ 'unsuspend' => [
+ { # sudbDeleteOrAddID
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.1',
+ 'type' => 'INTEGER',
+ 'value' => \&_trango_access5830_sudbDeleteOrAddId,
+ },
+ { # sudbAddMac
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.2',
+ 'type' => 'HEX_STRING',
+ 'value' => \&_trango_access5830_sudbAddMac,
+ },
+ { # sudbAddSU
+ 'oid' => '1.3.6.1.4.1.5454.1.20.3.5.7',
+ 'type' => 'INTEGER',
+ 'value' => 1,
+ },
+ ],
+ },
+ },
+};
+
+
+sub _field_prefix { 'trango'; }
+
+sub _req_router_fields {
+ map {
+ $_[0]->_field_prefix . '_' . $_
+ } (qw(address comm ap_type suid_field));
+}
+
+sub _get_cmd_sub {
+
+ return('FS::part_export::snmp::snmp_cmd');
+
+}
+
+sub _prepare_args {
+
+ my ($self, $action, $router) = (shift, shift, shift);
+ my ($svc_broadband) = shift;
+ my $old = shift if $action eq 'replace';
+ my $field_prefix = $self->_field_prefix;
+ my $error;
+
+ my $ap_type = $router->getfield($field_prefix . '_ap_type');
+
+ unless (exists $trango_mib->{$ap_type}) {
+ return "Unsupported Trango AP type '$ap_type'";
+ }
+
+ $error = $self->_check_suid(
+ $action, $router, $svc_broadband, ($old) ? $old : ()
+ );
+ return $error if $error;
+
+ $error = $self->_check_mac(
+ $action, $router, $svc_broadband, ($old) ? $old : ()
+ );
+ return $error if $error;
+
+ my $ap_mib = $trango_mib->{$ap_type};
+
+ my $args = [
+ '-hostname' => $router->getfield($field_prefix.'_address'),
+ '-version' => $ap_mib->{'snmpversion'},
+ '-community' => $router->getfield($field_prefix.'_comm'),
+ ];
+
+ my @varbindlist = ();
+
+ foreach my $oid (@{$ap_mib->{'varbinds'}->{$action}}) {
+ warn "[debug]$me Processing OID '" . $oid->{'oid'} . "'" if $DEBUG;
+ my $value;
+ if (ref($oid->{'value'}) eq 'CODE') {
+ eval {
+ $value = &{$oid->{'value'}}(
+ $self, $action, $router, $svc_broadband,
+ (($old) ? $old : ()),
+ );
+ };
+ return "While processing OID '" . $oid->{'oid'} . "':" . $@
+ if $@;
+ } else {
+ $value = $oid->{'value'};
+ }
+
+ warn "[debug]$me Value for OID '" . $oid->{'oid'} . "': " if $DEBUG;
+
+ if (defined $value) { # Skip OIDs with undefined values.
+ push @varbindlist, ($oid->{'oid'}, $oid->{'type'}, $value);
+ }
+ }
+
+
+ push @$args, ('-varbindlist', @varbindlist);
+
+ return('', $args);
+
+}
+
+sub _check_suid {
+
+ my ($self, $action, $router, $svc_broadband) = (shift, shift, shift, shift);
+ my $old = shift if $action eq 'replace';
+ my $error;
+
+ my $suid_field = $self->option('suid_field');
+ unless (grep {$_ eq $suid_field} $svc_broadband->fields) {
+ return "Missing Trango SU ID field. "
+ . "See the trango export options for more info.";
+ }
+
+ my $suid = $svc_broadband->getfield($suid_field);
+ if ($action eq 'replace') {
+ my $old_suid = $old->getfield($suid_field);
+
+ if ($old_suid ne '' and $old_suid ne $suid) {
+ return 'Cannot change Trango SU ID';
+ }
+ }
+
+ if (not $suid =~ /^\d+$/ and $action ne 'delete') {
+ my $new_suid = eval { $self->_get_next_suid($router); };
+ return "Error while getting next Trango SU ID: $@" if ($@);
+
+ warn "[debug]$me Got new SU ID: $new_suid" if $DEBUG;
+ $svc_broadband->set($suid_field, $new_suid);
+
+ #FIXME: Probably a bad hack.
+ # We need to update the SU ID field in the database.
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::svc_Common::noexport_hack = 1;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $svcnum = $svc_broadband->svcnum;
+
+ my $old_svc = qsearchs('svc_broadband', { svcnum => $svcnum });
+ unless ($old_svc) {
+ return "Unable to retrieve svc_broadband with svcnum '$svcnum";
+ }
+
+ my $svcpart = $svc_broadband->svcpart
+ ? $svc_broadband->svcpart
+ : $svc_broadband->cust_svc->svcpart;
+
+ my $new_svc = new FS::svc_broadband {
+ $old_svc->hash,
+ $suid_field => $new_suid,
+ svcpart => $svcpart,
+ };
+
+ $error = $new_svc->check;
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error while updating the Trango SU ID: $error" if $error;
+ }
+
+ warn "[debug]$me Updating svc_broadband with SU ID '$new_suid'...\n" .
+ &Dumper($new_svc) if $DEBUG;
+
+ $error = eval { $new_svc->replace($old_svc); };
+
+ if ($@ or $error) {
+ $error ||= $@;
+ $dbh->rollback if $oldAutoCommit;
+ return "Error while updating the Trango SU ID: $error" if $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ }
+
+ return '';
+
+}
+
+sub _check_mac {
+
+ my ($self, $action, $router, $svc_broadband) = (shift, shift, shift, shift);
+ my $old = shift if $action eq 'replace';
+
+ my $mac_field = $self->option('mac_field');
+ unless (grep {$_ eq $mac_field} $svc_broadband->fields) {
+ return "Missing Trango MAC address field. "
+ . "See the trango export options for more info.";
+ }
+
+ my $mac_addr = $svc_broadband->getfield($mac_field);
+ unless (length(join('', $mac_addr =~ /[0-9a-fA-F]/g)) == 12) {
+ return "Invalid Trango MAC address: $mac_addr";
+ }
+
+ return('');
+
+}
+
+sub _get_next_suid {
+
+ my ($self, $router) = (shift, shift);
+
+ my $counter_dir = '/usr/local/etc/freeside/export.'. datasrc . '/trango';
+ my $baseid = $router->getfield('trango_baseid');
+ my $apid = $router->getfield('trango_apid');
+
+ my $counter_file_suffix = '';
+ if ($baseid ne '') {
+ $counter_file_suffix .= "_B$baseid";
+ if ($apid ne '') {
+ $counter_file_suffix .= "_A$apid";
+ }
+ }
+
+ my $counter_file = $counter_dir . '/SUID' . $counter_file_suffix;
+
+ warn "[debug]$me Using SUID counter file '$counter_file'";
+
+ my $suid = eval {
+ mkdir $counter_dir, 0700 unless -d $counter_dir;
+
+ my $cf = new File::CounterFile($counter_file, 0);
+ $cf->inc;
+ };
+
+ die "Error generating next Trango SU ID: $@" if (not $suid or $@);
+
+ return($suid);
+
+}
+
+
+
+# Trango-specific subroutines for generating varbind values.
+#
+# All subs should die on error, and return undef to decline. OIDs that
+# decline will not be added to varbinds.
+
+sub _trango_access5830_sudbDeleteOrAddId {
+
+ my ($self, $action, $router) = (shift, shift, shift);
+ my ($svc_broadband) = shift;
+ my $old = shift if $action eq 'replace';
+
+ my $suid = $svc_broadband->getfield($self->option('suid_field'));
+
+ # Sanity check.
+ unless ($suid =~ /^\d+$/) {
+ if ($action eq 'delete') {
+ # Silently ignore. If we don't have a valid SU ID now, we probably
+ # never did.
+ return undef;
+ } else {
+ die "Invalid Trango SU ID '$suid'";
+ }
+ }
+
+ return ($suid);
+
+}
+
+sub _trango_access5830_sudbAddMac {
+
+ my ($self, $action, $router) = (shift, shift, shift);
+ my ($svc_broadband) = shift;
+ my $old = shift if $action eq 'replace';
+
+ my $mac_addr = $svc_broadband->getfield($self->option('mac_field'));
+ $mac_addr = join('', $mac_addr =~ /[0-9a-fA-F]/g);
+
+ # Sanity check.
+ die "Invalid Trango MAC address '$mac_addr'" unless (length($mac_addr)==12);
+
+ return($mac_addr);
+
+}
+
+
+=head1 BUGS
+
+Plenty, I'm sure.
+
+=cut
+
+
+1;
diff --git a/FS/FS/part_export/vitelity.pm b/FS/FS/part_export/vitelity.pm
new file mode 100644
index 0000000..bec3837
--- /dev/null
+++ b/FS/FS/part_export/vitelity.pm
@@ -0,0 +1,239 @@
+package FS::part_export::vitelity;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::Record qw(qsearch dbh);
+use FS::part_export;
+use FS::phone_avail;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'login' => { label=>'Vitelity API login' },
+ 'pass' => { label=>'Vitelity API password' },
+ 'dry_run' => { label=>"Test mode - don't actually provision" },
+;
+
+%info = (
+ 'svc' => 'svc_phone',
+ 'desc' => 'Provision phone numbers to Vitelity',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Requires installation of
+<a href="http://search.cpan.org/dist/Net-Vitelity">Net::Vitelity</a>
+from CPAN.
+END
+);
+
+sub rebless { shift; }
+
+sub get_dids {
+ my $self = shift;
+ my %opt = ref($_[0]) ? %{$_[0]} : @_;
+
+ my %search = ();
+ # 'orderby' => 'npa', #but it doesn't seem to work :/
+
+ my $method = '';
+
+ if ( $opt{'areacode'} && $opt{'exchange'} ) { #return numbers
+
+ return [
+ map { join('-', $_->npx, $_->nxx, $_->station ) }
+ qsearch({
+ 'table' => 'phone_avail',
+ 'hashref' => { 'exportnum' => $self->exportnum,
+ 'countrycode' => '1',
+ 'state' => $opt{'state'},
+ 'npa' => $opt{'areacode'},
+ 'nxx' => $opt{'exchange'},
+ },
+ 'order_by' => 'ORDER BY name', #?
+ })
+ ];
+
+ } elsif ( $opt{'areacode'} ) { #return city (npa-nxx-XXXX)
+
+ return [
+ map { $_->name. ' ('. $_->npa. '-'. $_->nxx. '-XXXX)' }
+ qsearch({
+ 'select' => 'DISTINCT ON ( name, npa, nxx ) *',
+ 'table' => 'phone_avail',
+ 'hashref' => { 'exportnum' => $self->exportnum,
+ 'countrycode' => '1',
+ 'state' => $opt{'state'},
+ 'npa' => $opt{'areacode'},
+ },
+ 'order_by' => 'ORDER BY name', #?
+ })
+ ];
+
+ } elsif ( $opt{'state'} ) { #and not other things, then return areacode
+
+ #XXX need to flush the cache at some point :/
+
+ my @avail = qsearch({
+ 'select' => 'DISTINCT npa',
+ 'table' => 'phone_avail',
+ 'hashref' => { 'exportnum' => $self->exportnum,
+ 'countrycode' => '1', #don't hardcode me when gp goes intl
+ 'state' => $opt{'state'},
+ },
+ 'order_by' => 'ORDER BY npa',
+ });
+
+ return [ map $_->npa, @avail ] if @avail; #return cached area codes instead
+
+ #otherwise, search for em
+
+ my @ratecenters = $self->vitelity_command( 'listavailratecenters',
+ 'state' => $opt{'state'},
+ );
+
+ if ( $ratecenters[0] eq 'unavailable' ) {
+ return [];
+ } elsif ( $ratecenters[0] eq 'missingdata' ) {
+ die "missingdata error running Vitelity API"; #die?
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $errmsg = 'WARNING: error populating phone availability cache: ';
+
+ my %npa = ();
+ foreach my $ratecenter (@ratecenters) {
+
+ my @dids = $self->vitelity_command( 'listlocal',
+ 'state' => $opt{'state'},
+ 'ratecenter' => $ratecenter,
+ );
+
+ if ( $dids[0] eq 'unavailable' ) {
+ next;
+ } elsif ( $dids[0] eq 'missingdata' ) {
+ die "missingdata error running Vitelity API"; #die?
+ }
+
+ foreach my $did ( @dids ) {
+ $did =~ /^(\d{3})(\d{3})(\d{4})$/ or die "unparsable did $did\n";
+ my($npa, $nxx, $station) = ($1, $2, $3);
+ $npa{$npa}++;
+
+ my $phone_avail = new FS::phone_avail {
+ 'exportnum' => $self->exportnum,
+ 'countrycode' => '1', #don't hardcode me when vitelity goes int'l
+ 'state' => $opt{'state'},
+ 'npa' => $npa,
+ 'nxx' => $nxx,
+ 'station' => $station,
+ 'name' => $ratecenter,
+ };
+
+ $error = $phone_avail->insert();
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die $errmsg.$error;
+ }
+
+ }
+
+ }
+
+ $dbh->commit or warn $errmsg.$dbh->errstr if $oldAutoCommit;
+
+ my @return = sort { $a <=> $b } keys %npa;
+ #@return = sort { (split(' ', $a))[0] <=> (split(' ', $b))[0] } @return;
+
+ return \@return;
+
+ } else {
+ die "get_dids called without state or areacode options";
+ }
+
+}
+
+sub vitelity_command {
+ my( $self, $command, @args ) = @_;
+
+ eval "use Net::Vitelity;";
+ die $@ if $@;
+
+ my $vitelity = Net::Vitelity->new(
+ 'login' => $self->option('login'),
+ 'pass' => $self->option('pass'),
+ #'debug' => $debug,
+ );
+
+ $vitelity->$command(@args);
+}
+
+sub _export_insert {
+ my( $self, $svc_phone ) = (shift, shift);
+
+ return '' if $self->option('dry_run');
+
+ #we want to provision and catch errors now, not queue
+
+ my $result = $self->vitelity_command('getlocaldid',
+ 'did' => $svc_phone->phonenum,
+#XXX
+#Options: type=perminute OR type=unlimited OR type=your-pri OR
+# routesip=route_to_this_subaccount
+ );
+
+ if ( $result ne 'success' ) {
+ return "Error running Vitelity getlocaldid: $result";
+ }
+
+ '';
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ #hmm, what's to change?
+ '';
+}
+
+sub _export_delete {
+ my( $self, $svc_phone ) = (shift, shift);
+
+ return '' if $self->option('dry_run');
+
+ #probably okay to queue the deletion...?
+ #but hell, let's do it inline anyway, who wants phone numbers hanging around
+
+ my $result = $self->vitelity_command('removedid',
+ 'did' => $svc_phone->phonenum,
+ );
+
+ if ( $result ne 'success' ) {
+ return "Error running Vitelity getlocaldid: $result";
+ }
+
+ '';
+}
+
+sub _export_suspend {
+ my( $self, $svc_phone ) = (shift, shift);
+ #nop for now
+ '';
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_phone ) = (shift, shift);
+ #nop for now
+ '';
+}
+
+1;
+
diff --git a/FS/FS/part_export/vpopmail.pm b/FS/FS/part_export/vpopmail.pm
new file mode 100644
index 0000000..4cda657
--- /dev/null
+++ b/FS/FS/part_export/vpopmail.pm
@@ -0,0 +1,254 @@
+package FS::part_export::vpopmail;
+
+use vars qw(@ISA %info @saltset $exportdir);
+use Fcntl qw(:flock);
+use Tie::IxHash;
+use File::Path;
+use FS::UID qw( datasrc );
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ #'machine' => { label=>'vpopmail machine', },
+ 'dir' => { label=>'directory', }, # ?more info? default?
+ 'uid' => { label=>'vpopmail uid' },
+ 'gid' => { label=>'vpopmail gid' },
+ 'restart' => { label=> 'vpopmail restart command',
+ default=> 'cd /home/vpopmail/domains; for domain in *; do /home/vpopmail/bin/vmkpasswd $domain; done; /var/qmail/bin/qmail-newu; killall -HUP qmail-send',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real-time export to vpopmail text files',
+ 'options' => \%options,
+ 'notes' => <<'END'
+This export is currently unmaintained. See shellcommands_withdomain for an
+export that uses vpopmail CLI commands instead.<BR>
+<BR>
+Real time export to <a href="http://inter7.com/vpopmail/">vpopmail</a> text
+files. <a href="http://search.cpan.org/dist/File-Rsync">File::Rsync</a>
+must be installed, and you will need to
+<a href="../docs/ssh.html">setup SSH for unattended operation</a>
+to <b>vpopmail</b>@<i>export.host</i>.
+END
+);
+
+@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+ $self->vpopmail_queue( $svc_acct->svcnum, 'insert',
+ $svc_acct->username,
+ crypt($svc_acct->_password,$saltset[int(rand(64))].$saltset[int(rand(64))]),
+ $svc_acct->domain,
+ $svc_acct->quota,
+ $svc_acct->finger,
+ );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ my $cpassword = crypt(
+ $new->_password, $saltset[int(rand(64))].$saltset[int(rand(64))]
+ );
+
+ return "can't change username with vpopmail"
+ if $old->username ne $new->username;
+
+ #no.... if mail can't be preserved, better to disallow username changes
+ #if ($old->username ne $new->username || $old->domain ne $new->domain ) {
+ # vpopmail_queue( $svc_acct->svcnum, 'delete',
+ # $old->username, $old->domain
+ # );
+ # vpopmail_queue( $svc_acct->svcnum, 'insert',
+ # $new->username,
+ # $cpassword,
+ # $new->domain,
+ # );
+
+ return '' unless $old->_password ne $new->_password;
+
+ $self->vpopmail_queue( $new->svcnum, 'replace',
+ $new->username, $cpassword, $new->domain, $new->quota, $new->finger );
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->vpopmail_queue( $svc_acct->svcnum, 'delete',
+ $svc_acct->username, $svc_acct->domain );
+}
+
+#a good idea to queue anything that could fail or take any time
+sub vpopmail_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+
+ my $exportdir = "%%%FREESIDE_EXPORT%%%/export." . datasrc;
+ mkdir $exportdir, 0700 or die $! unless -d $exportdir;
+ $exportdir .= "/vpopmail";
+ mkdir $exportdir, 0700 or die $! unless -d $exportdir;
+ $exportdir .= '/'. $self->machine;
+ mkdir $exportdir, 0700 or die $! unless -d $exportdir;
+ mkdir "$exportdir/domains", 0700 or die $! unless -d "$exportdir/domains";
+
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::vpopmail::vpopmail_$method",
+ };
+ $queue->insert(
+ $exportdir,
+ $self->machine,
+ $self->option('dir'),
+ $self->option('uid'),
+ $self->option('gid'),
+ $self->option('restart'),
+ @_
+ );
+}
+
+sub vpopmail_insert { #subroutine, not method
+ my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
+ my( $username, $password, $domain, $quota, $finger ) = @_;
+
+ mkdir "$exportdir/domains/$domain", 0700 or die $!
+ unless -d "$exportdir/domains/$domain";
+
+ (open(VPASSWD, ">>$exportdir/domains/$domain/vpasswd")
+ and flock(VPASSWD,LOCK_EX)
+ ) or die "can't open vpasswd file for $username\@$domain: ".
+ "$exportdir/domains/$domain/vpasswd: $!";
+ print VPASSWD join(":",
+ $username,
+ $password,
+ '1',
+ '0',
+ $finger,
+ "$dir/domains/$domain/$username",
+ $quota ? $quota.'S' : 'NOQUOTA',
+ ), "\n";
+
+ flock(VPASSWD,LOCK_UN);
+ close(VPASSWD);
+
+ for my $mkdir (
+ grep { ! -d $_ } map { "$exportdir/domains/$domain/$username$_" }
+ ( '', qw( /Maildir /Maildir/cur /Maildir/new /Maildir/tmp ) )
+ ) {
+ mkdir $mkdir, 0700 or die "can't mkdir $mkdir: $!";
+ }
+
+ vpopmail_sync( $exportdir, $machine, $dir, $uid, $gid, $restart );
+
+}
+
+sub vpopmail_replace { #subroutine, not method
+ my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
+ my( $username, $password, $domain, $quota, $finger ) = @_;
+
+ (open(VPASSWD, "$exportdir/domains/$domain/vpasswd")
+ and flock(VPASSWD,LOCK_EX)
+ ) or die "can't open $exportdir/domains/$domain/vpasswd: $!";
+
+ open(VPASSWDTMP, ">$exportdir/domains/$domain/vpasswd.tmp")
+ or die "Can't open $exportdir/domains/$domain/vpasswd.tmp: $!";
+
+ while (<VPASSWD>) {
+ my ($mailbox, $pw, $vuid, $vgid, $vfinger, $vdir, $vquota, @rest) =
+ split(':', $_);
+ if ( $username ne $mailbox ) {
+ print VPASSWDTMP $_;
+ next
+ }
+ print VPASSWDTMP join (':',
+ $mailbox,
+ $password,
+ '1',
+ '0',
+ $finger,
+ "$dir/domains/$domain/$username", #$vdir
+ $quota ? $quota.'S' : 'NOQUOTA',
+ ), "\n";
+ }
+
+ close(VPASSWDTMP);
+
+ rename "$exportdir/domains/$domain/vpasswd.tmp", "$exportdir/domains/$domain/vpasswd"
+ or die "Can't rename $exportdir/domains/$domain/vpasswd.tmp: $!";
+
+ flock(VPASSWD,LOCK_UN);
+ close(VPASSWD);
+
+ vpopmail_sync( $exportdir, $machine, $dir, $uid, $gid, $restart );
+
+}
+
+sub vpopmail_delete { #subroutine, not method
+ my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
+ my( $username, $domain ) = @_;
+
+ (open(VPASSWD, "$exportdir/domains/$domain/vpasswd")
+ and flock(VPASSWD,LOCK_EX)
+ ) or die "can't open $exportdir/domains/$domain/vpasswd: $!";
+
+ open(VPASSWDTMP, ">$exportdir/domains/$domain/vpasswd.tmp")
+ or die "Can't open $exportdir/domains/$domain/vpasswd.tmp: $!";
+
+ while (<VPASSWD>) {
+ my ($mailbox, $rest) = split(':', $_);
+ print VPASSWDTMP $_ unless $username eq $mailbox;
+ }
+
+ close(VPASSWDTMP);
+
+ rename "$exportdir/domains/$domain/vpasswd.tmp",
+ "$exportdir/domains/$domain/vpasswd"
+ or die "Can't rename $exportdir/domains/$domain/vpasswd.tmp: $!";
+
+ flock(VPASSWD,LOCK_UN);
+ close(VPASSWD);
+
+ rmtree "$exportdir/domains/$domain/$username"
+ or die "can't rmtree $exportdir/domains/$domain/$username: $!";
+
+ vpopmail_sync( $exportdir, $machine, $dir, $uid, $gid, $restart );
+}
+
+sub vpopmail_sync {
+ my( $exportdir, $machine, $dir, $uid, $gid, $restart ) = splice @_,0,6;
+
+ chdir $exportdir;
+# my @args = ( $rsync, "-rlpt", "-e", $ssh, "domains/",
+# "vpopmail\@$machine:$dir/domains/" );
+# system {$args[0]} @args;
+
+ eval "use File::Rsync;";
+ die $@ if $@;
+
+ my $rsync = File::Rsync->new({ rsh => 'ssh' });
+
+ $rsync->exec( {
+ recursive => 1,
+ perms => 1,
+ times => 1,
+ src => "$exportdir/domains/",
+ dest => "vpopmail\@$machine:$dir/domains/",
+ } ); # true/false return value from exec is not working, alas
+ if ( $rsync->err ) {
+ die "error uploading to vpopmail\@$machine:$dir/domains/ : ".
+ 'exit status: '. $rsync->status. ', '.
+ 'STDERR: '. join(" / ", $rsync->err). ', '.
+ 'STDOUT: '. join(" / ", $rsync->out);
+ }
+
+ eval "use Net::SSH qw(ssh);";
+ die $@ if $@;
+
+ ssh("vpopmail\@$machine", $restart) if $restart;
+}
+
+1;
+
diff --git a/FS/FS/part_export/www_plesk.pm b/FS/FS/part_export/www_plesk.pm
new file mode 100644
index 0000000..82d5557
--- /dev/null
+++ b/FS/FS/part_export/www_plesk.pm
@@ -0,0 +1,138 @@
+package FS::part_export::www_plesk;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'URL' => { label=>'URL' },
+ 'login' => { label=>'Login' },
+ 'password' => { label=>'Password' },
+ 'template' => { label=>'Domain Template' },
+ 'web' => { label=>'Host Website',
+ type=>'checkbox' },
+ 'debug' => { label=>'Enable debugging',
+ type=>'checkbox' },
+;
+
+%info = (
+ 'svc' => 'svc_www',
+ 'desc' => 'Real-time export to Plesk managed hosting service',
+ 'options'=> \%options,
+ 'notes' => <<'END'
+Real-time export to
+<a href="http://www.swsoft.com/">Plesk</a> managed server.
+Requires installation of
+<a href="http://search.cpan.org/dist/Net-Plesk">Net::Plesk</a>
+from CPAN.
+END
+);
+
+sub rebless { shift; }
+
+# experiment: want the status of these right away (don't want account to
+# create or whatever and then get error in the queue from dup username or
+# something), so no queueing
+
+sub _export_insert {
+ my( $self, $www ) = ( shift, shift );
+
+ eval "use Net::Plesk;";
+ return $@ if $@;
+
+ my $plesk = new Net::Plesk (
+ 'POST' => $self->option('URL'),
+ ':HTTP_AUTH_LOGIN' => $self->option('login'),
+ ':HTTP_AUTH_PASSWD' => $self->option('password'),
+ );
+
+ my $gcresp = $plesk->client_get( $www->svc_acct->username );
+ return $gcresp->errortext
+ unless $gcresp->is_success;
+
+ unless ($gcresp->id) {
+ my $cust_main = $www->cust_svc->cust_pkg->cust_main;
+ $gcresp = $plesk->client_add( $cust_main->name,
+ $www->svc_acct->username,
+ $www->svc_acct->_password,
+ $cust_main->daytime,
+ $cust_main->fax,
+ $cust_main->invoicing_list->[0],
+ $cust_main->address1 . $cust_main->address2,
+ $cust_main->city,
+ $cust_main->state,
+ $cust_main->zip,
+ $cust_main->country,
+ );
+ return $gcresp->errortext
+ unless $gcresp->is_success;
+ }
+
+ $plesk->client_ippool_add_ip ( $gcresp->id,
+ $www->domain_record->recdata,
+ );
+
+ if ($self->option('web')) {
+ $self->_plesk_command( 'domain_add',
+ $www->domain_record->svc_domain->domain,
+ $gcresp->id,
+ $www->domain_record->recdata,
+ $self->option('template')?$self->option('template'):'',
+ $www->svc_acct->username,
+ $www->svc_acct->_password,
+ );
+ }else{
+ $self->_plesk_command( 'domain_add',
+ $www->domain_record->svc_domain->domain,
+ $gcresp->id,
+ $www->domain_record->recdata,
+ $self->option('template')?$self->option('template'):'',
+ );
+ }
+}
+
+sub _plesk_command {
+ my( $self, $method, @args ) = @_;
+
+ eval "use Net::Plesk;";
+ return $@ if $@;
+
+ local($Net::Plesk::DEBUG) = 1
+ if $self->option('debug');
+
+ my $plesk = new Net::Plesk (
+ 'POST' => $self->option('URL'),
+ ':HTTP_AUTH_LOGIN' => $self->option('login'),
+ ':HTTP_AUTH_PASSWD' => $self->option('password'),
+ );
+
+ my $response = $plesk->$method(@args);
+ return $response->errortext unless $response->is_success;
+ '';
+
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+
+ return "can't change domain with Plesk"
+ if $old->domain_record->svc_domain->domain ne
+ $new->domain_record->svc_domain->domain;
+
+ return "can't change client with Plesk"
+ if $old->svc_acct->username ne
+ $new->svc_acct->username;
+
+ return '';
+
+}
+
+sub _export_delete {
+ my( $self, $www ) = ( shift, shift );
+ $self->_plesk_command( 'domain_del', $www->domain_record->svc_domain->domain);
+}
+
+1;
+
diff --git a/FS/FS/part_export/www_shellcommands.pm b/FS/FS/part_export/www_shellcommands.pm
new file mode 100644
index 0000000..7e4be9c
--- /dev/null
+++ b/FS/FS/part_export/www_shellcommands.pm
@@ -0,0 +1,190 @@
+package FS::part_export::www_shellcommands;
+
+use strict;
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'useradd' => { label=>'Insert command',
+ default=>'mkdir $homedir/$zone; chown $username $homedir/$zone; ln -s $homedir/$zone /var/www/$zone',
+ },
+ 'userdel' => { label=>'Delete command',
+ default=>'[ -n "$zone" ] && rm -rf /var/www/$zone; rm -rf $homedir/$zone',
+ },
+ 'usermod' => { label=>'Modify command',
+ default=>'[ -n "$old_zone" ] && rm /var/www/$old_zone; [ "$old_zone" != "$new_zone" -a -n "$new_zone" ] && ( mv $old_homedir/$old_zone $new_homedir/$new_zone; ln -sf $new_homedir/$new_zone /var/www/$new_zone ); [ "$old_username" != "$new_username" ] && chown -R $new_username $new_homedir/$new_zone; ln -sf $new_homedir/$new_zone /var/www/$new_zone',
+ },
+ 'suspend' => { label=>'Suspension command',
+ default=>'[ -n "$zone" ] && chmod 0 /var/www/$zone',
+ },
+ 'unsuspend'=> { label=>'Unsuspension command',
+ default=>'[ -n "$zone" ] && chmod 755 /var/www/$zone',
+ },
+;
+
+%info = (
+ 'svc' => 'svc_www',
+ 'desc' => 'Run remote commands via SSH, for virtual web sites (directory maintenance, FrontPage, ISPMan)',
+ 'options' => \%options,
+ 'notes' => <<'END'
+Run remote commands via SSH, for virtual web sites. You will need to
+<a href="../docs/ssh.html">setup SSH for unattended operation</a>.
+<BR><BR>Use these buttons for some useful presets:
+<UL>
+ <LI>
+ <INPUT TYPE="button" VALUE="Maintain directories" onClick='
+ this.form.user.value = "root";
+ this.form.useradd.value = "mkdir $homedir/$zone; chown $username $homedir/$zone; ln -s $homedir/$zone /var/www/$zone";
+ this.form.userdel.value = "[ -n \"$zone\" ] && rm -rf /var/www/$zone; rm -rf $homedir/$zone";
+ this.form.usermod.value = "[ -n \"$old_zone\" ] && rm /var/www/$old_zone; [ \"$old_zone\" != \"$new_zone\" -a -n \"$new_zone\" ] && ( mv $old_homedir/$old_zone $new_homedir/$new_zone; ln -sf $new_homedir/$new_zone /var/www/$new_zone ); [ \"$old_username\" != \"$new_username\" ] && chown -R $new_username $new_homedir/$new_zone; ln -sf $new_homedir/$new_zone /var/www/$new_zone";
+ this.form.suspend.value = "[ -n \"$zone\" ] && chmod 0 /var/www/$zone";
+ this.form.unsuspend.value = "[ -n \"$zone\" ] && chmod 755 /var/www/$zone";
+ '>
+ <LI>
+ <INPUT TYPE="button" VALUE="FrontPage extensions" onClick='
+ this.form.user.value = "root";
+ this.form.useradd.value = "/usr/local/frontpage/version5.0/bin/owsadm.exe -o install -p 80 -m $zone -xu $username -xg www-data -s /etc/apache/httpd.conf -u $username -pw $_password";
+ this.form.userdel.value = "/usr/local/frontpage/version5.0/bin/owsadm.exe -o uninstall -p 80 -m $zone -s /etc/apache/httpd.conf";
+ this.form.usermod.value = "";
+ this.form.suspend.value = "";
+ this.form.unsuspend.value = "";
+ '>
+ <LI>
+ <INPUT TYPE="button" VALUE="ISPMan CLI" onClick='
+ this.form.user.value = "root";
+ this.form.useradd.value = "/usr/local/ispman/bin/ispman.addvhost -d $domain $bare_zone";
+ this.form.userdel.value = "/usr/local/ispman/bin/ispman.deletevhost -d $domain $bare_zone";
+ this.form.usermod.value = "";
+ this.form.suspend.value = "";
+ this.form.unsuspend.value = "";
+ '></UL>
+The following variables are available for interpolation (prefixed with
+<code>new_</code> or <code>old_</code> for replace operations):
+<UL>
+ <LI><code>$zone</code> - fully-qualified zone of this virtual host
+ <LI><code>$bare_zone</code> - just the zone of this virtual host, without the domain portion
+ <LI><code>$domain</code> - base domain
+ <LI><code>$username</code>
+ <LI><code>$_password</code>
+ <LI><code>$homedir</code>
+ <LI>All other fields in <a href="../docs/schema.html#svc_www">svc_www</a>
+ are also available.
+</UL>
+END
+);
+
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self) = shift;
+ $self->_export_command('useradd', @_);
+}
+
+sub _export_delete {
+ my($self) = shift;
+ $self->_export_command('userdel', @_);
+}
+
+sub _export_suspend {
+ my($self) = shift;
+ $self->_export_command('suspend', @_);
+}
+
+sub _export_unsuspend {
+ my($self) = shift;
+ $self->_export_command('unsuspend', @_);
+}
+
+sub _export_command {
+ my ( $self, $action, $svc_www) = (shift, shift, shift);
+ my $command = $self->option($action);
+ return '' if $command =~ /^\s*$/;
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${$_} = $svc_www->getfield($_) foreach $svc_www->fields;
+ }
+ my $domain_record = $svc_www->domain_record; # or die ?
+ my $zone = $domain_record->zone; # or die ?
+ my $domain = $domain_record->svc_domain->domain;
+ ( my $bare_zone = $zone ) =~ s/\.$domain$//;
+ my $svc_acct = $svc_www->svc_acct; # or die ?
+ my $username = $svc_acct->username;
+ my $_password = $svc_acct->_password;
+ my $homedir = $svc_acct->dir; # or die ?
+
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $svc_www->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+sub _export_replace {
+ my($self, $new, $old ) = (shift, shift, shift);
+ my $command = $self->option('usermod');
+
+ #set variable for the command
+ no strict 'vars';
+ {
+ no strict 'refs';
+ ${"old_$_"} = $old->getfield($_) foreach $old->fields;
+ ${"new_$_"} = $new->getfield($_) foreach $new->fields;
+ }
+ my $old_domain_record = $old->domain_record; # or die ?
+ my $old_zone = $old_domain_record->zone; # or die ?
+ my $old_domain = $old_domain_record->svc_domain->domain;
+ ( my $old_bare_zone = $old_zone ) =~ s/\.$old_domain$//;
+ my $old_svc_acct = $old->svc_acct; # or die ?
+ my $old_username = $old_svc_acct->username;
+ my $old_homedir = $old_svc_acct->dir; # or die ?
+
+ my $new_domain_record = $new->domain_record; # or die ?
+ my $new_zone = $new_domain_record->zone; # or die ?
+ my $new_domain = $new_domain_record->svc_domain->domain;
+ ( my $new_bare_zone = $new_zone ) =~ s/\.$new_domain$//;
+ my $new_svc_acct = $new->svc_acct; # or die ?
+ my $new_username = $new_svc_acct->username;
+ #my $new__password = $new_svc_acct->_password;
+ my $new_homedir = $new_svc_acct->dir; # or die ?
+
+ #done setting variables for the command
+
+ $self->shellcommands_queue( $new->svcnum,
+ user => $self->option('user')||'root',
+ host => $self->machine,
+ command => eval(qq("$command")),
+ );
+}
+
+#a good idea to queue anything that could fail or take any time
+sub shellcommands_queue {
+ my( $self, $svcnum ) = (shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::www_shellcommands::ssh_cmd",
+ };
+ $queue->insert( @_ );
+}
+
+sub ssh_cmd { #subroutine, not method
+ use Net::SSH '0.08';
+ &Net::SSH::ssh_cmd( { @_ } );
+}
+
+#sub shellcommands_insert { #subroutine, not method
+#}
+#sub shellcommands_replace { #subroutine, not method
+#}
+#sub shellcommands_delete { #subroutine, not method
+#}
+
diff --git a/FS/FS/part_export_option.pm b/FS/FS/part_export_option.pm
new file mode 100644
index 0000000..e759404
--- /dev/null
+++ b/FS/FS/part_export_option.pm
@@ -0,0 +1,134 @@
+package FS::part_export_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::part_export;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_export_option - Object methods for part_export_option records
+
+=head1 SYNOPSIS
+
+ use FS::part_export_option;
+
+ $record = new FS::part_export_option \%hash;
+ $record = new FS::part_export_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_export_option object represents an export option.
+FS::part_export_option inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item exportnum - export (see L<FS::part_export>)
+
+=item optionname - option name
+
+=item optionvalue - option value
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new export option. To add the export option to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_export_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid export option. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
+ || $self->ut_alpha('optionname')
+ || $self->ut_anything('optionvalue')
+ ;
+ return $error if $error;
+
+ return "Unknown exportnum: ". $self->exportnum
+ unless qsearchs('part_export', { 'exportnum' => $self->exportnum } );
+
+ #check options & values?
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+Possibly.
+
+=head1 SEE ALSO
+
+L<FS::part_export>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm
new file mode 100644
index 0000000..ef24b53
--- /dev/null
+++ b/FS/FS/part_pkg.pm
@@ -0,0 +1,1333 @@
+package FS::part_pkg;
+
+use strict;
+use vars qw( @ISA %plans $DEBUG $setup_hack );
+use Carp qw(carp cluck confess);
+use Scalar::Util qw( blessed );
+use Time::Local qw( timelocal_nocheck );
+use Tie::IxHash;
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs dbh dbdef );
+use FS::pkg_svc;
+use FS::part_svc;
+use FS::cust_pkg;
+use FS::agent_type;
+use FS::type_pkgs;
+use FS::part_pkg_option;
+use FS::pkg_class;
+use FS::agent;
+use FS::part_pkg_taxoverride;
+use FS::part_pkg_taxproduct;
+use FS::part_pkg_link;
+
+@ISA = qw( FS::m2m_Common FS::option_Common );
+$DEBUG = 0;
+$setup_hack = 0;
+
+=head1 NAME
+
+FS::part_pkg - Object methods for part_pkg objects
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg;
+
+ $record = new FS::part_pkg \%hash
+ $record = new FS::part_pkg { 'column' => 'value' };
+
+ $custom_record = $template_record->clone;
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ @pkg_svc = $record->pkg_svc;
+
+ $svcnum = $record->svcpart;
+ $svcnum = $record->svcpart( 'svc_acct' );
+
+=head1 DESCRIPTION
+
+An FS::part_pkg object represents a package definition. FS::part_pkg
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item pkgpart - primary key (assigned automatically for new package definitions)
+
+=item pkg - Text name of this package definition (customer-viewable)
+
+=item comment - Text name of this package definition (non-customer-viewable)
+
+=item classnum - Optional package class (see L<FS::pkg_class>)
+
+=item promo_code - Promotional code
+
+=item setup - Setup fee expression (deprecated)
+
+=item freq - Frequency of recurring fee
+
+=item recur - Recurring fee expression (deprecated)
+
+=item setuptax - Setup fee tax exempt flag, empty or `Y'
+
+=item recurtax - Recurring fee tax exempt flag, empty or `Y'
+
+=item taxclass - Tax class
+
+=item plan - Price plan
+
+=item plandata - Price plan data (deprecated - see L<FS::part_pkg_option> instead)
+
+=item disabled - Disabled flag, empty or `Y'
+
+=item pay_weight - Weight (relative to credit_weight and other package definitions) that controls payment application to specific line items.
+
+=item credit_weight - Weight (relative to other package definitions) that controls credit application to specific line items.
+
+=item agentnum - Optional agentnum (see L<FS::agent>)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new package definition. To add the package definition to
+the database, see L<"insert">.
+
+=cut
+
+sub table { 'part_pkg'; }
+
+=item clone
+
+An alternate constructor. Creates a new package definition by duplicating
+an existing definition. A new pkgpart is assigned and `(CUSTOM) ' is prepended
+to the comment field. To add the package definition to the database, see
+L<"insert">.
+
+=cut
+
+sub clone {
+ my $self = shift;
+ my $class = ref($self);
+ my %hash = $self->hash;
+ $hash{'pkgpart'} = '';
+ $hash{'comment'} = "(CUSTOM) ". $hash{'comment'}
+ unless $hash{'comment'} =~ /^\(CUSTOM\) /;
+ #new FS::part_pkg ( \%hash ); # ?
+ new $class ( \%hash ); # ?
+}
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this package definition to the database. If there is an error,
+returns the error, otherwise returns false.
+
+Currently available options are: I<pkg_svc>, I<primary_svc>, I<cust_pkg>,
+I<custnum_ref> and I<options>.
+
+If I<pkg_svc> is set to a hashref with svcparts as keys and quantities as
+values, appropriate FS::pkg_svc records will be inserted.
+
+If I<primary_svc> is set to the svcpart of the primary service, the appropriate
+FS::pkg_svc record will be updated.
+
+If I<cust_pkg> is set to a pkgnum of a FS::cust_pkg record (or the FS::cust_pkg
+record itself), the object will be updated to point to this package definition.
+
+In conjunction with I<cust_pkg>, if I<custnum_ref> is set to a scalar reference,
+the scalar will be updated with the custnum value from the cust_pkg record.
+
+If I<tax_overrides> is set to a hashref with usage classes as keys and comma
+separated tax class numbers as values, appropriate FS::part_pkg_taxoverride
+records will be inserted.
+
+If I<options> is set to a hashref of options, appropriate FS::part_pkg_option
+records will be inserted.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my %options = @_;
+ warn "FS::part_pkg::insert called on $self with options ".
+ join(', ', map "$_=>$options{$_}", keys %options)
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ warn " inserting part_pkg record" if $DEBUG;
+ my $error = $self->SUPER::insert( $options{options} );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('agent_defaultpkg') ) {
+ warn " agent_defaultpkg set; allowing all agents to purchase package"
+ if $DEBUG;
+ foreach my $agent_type ( qsearch('agent_type', {} ) ) {
+ my $type_pkgs = new FS::type_pkgs({
+ 'typenum' => $agent_type->typenum,
+ 'pkgpart' => $self->pkgpart,
+ });
+ my $error = $type_pkgs->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ warn " inserting part_pkg_taxoverride records" if $DEBUG;
+ my %overrides = %{ $options{'tax_overrides'} || {} };
+ foreach my $usage_class ( keys %overrides ) {
+ my @overrides = (grep "$_", split (',', $overrides{$usage_class}) );
+ my $error = $self->process_m2m (
+ 'link_table' => 'part_pkg_taxoverride',
+ 'target_table' => 'tax_class',
+ 'hashref' => { 'usage_class' => $usage_class },
+ 'params' => \@overrides,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ warn " inserting pkg_svc records" if $DEBUG;
+ my $pkg_svc = $options{'pkg_svc'} || {};
+ foreach my $part_svc ( qsearch('part_svc', {} ) ) {
+ my $quantity = $pkg_svc->{$part_svc->svcpart} || 0;
+ my $primary_svc =
+ ( $options{'primary_svc'} && $options{'primary_svc'}==$part_svc->svcpart )
+ ? 'Y'
+ : '';
+
+ my $pkg_svc = new FS::pkg_svc( {
+ 'pkgpart' => $self->pkgpart,
+ 'svcpart' => $part_svc->svcpart,
+ 'quantity' => $quantity,
+ 'primary_svc' => $primary_svc,
+ } );
+ my $error = $pkg_svc->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ if ( $options{'cust_pkg'} ) {
+ warn " updating cust_pkg record " if $DEBUG;
+ my $old_cust_pkg =
+ ref($options{'cust_pkg'})
+ ? $options{'cust_pkg'}
+ : qsearchs('cust_pkg', { pkgnum => $options{'cust_pkg'} } );
+ ${ $options{'custnum_ref'} } = $old_cust_pkg->custnum
+ if $options{'custnum_ref'};
+ my %hash = $old_cust_pkg->hash;
+ $hash{'pkgpart'} = $self->pkgpart,
+ my $new_cust_pkg = new FS::cust_pkg \%hash;
+ local($FS::cust_pkg::disable_agentcheck) = 1;
+ my $error = $new_cust_pkg->replace($old_cust_pkg);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error modifying cust_pkg record: $error";
+ }
+ }
+
+ warn " commiting transaction" if $DEBUG;
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+=item delete
+
+Currently unimplemented.
+
+=cut
+
+sub delete {
+ return "Can't (yet?) delete package definitions.";
+# check & make sure the pkgpart isn't in cust_pkg or type_pkgs?
+}
+
+=item replace OLD_RECORD [ , OPTION => VALUE ... ]
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+Currently available options are: I<pkg_svc>, I<primary_svc> and I<options>
+
+If I<pkg_svc> is set to a hashref with svcparts as keys and quantities as
+values, the appropriate FS::pkg_svc records will be replaced.
+
+If I<primary_svc> is set to the svcpart of the primary service, the appropriate
+FS::pkg_svc record will be updated.
+
+If I<options> is set to a hashref, the appropriate FS::part_pkg_option records
+will be replaced.
+
+=cut
+
+sub replace {
+ my $new = shift;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $new->replace_old;
+
+ my $options =
+ ( ref($_[0]) eq 'HASH' )
+ ? shift
+ : { @_ };
+
+ $options->{options} = {} unless defined($options->{options});
+
+ warn "FS::part_pkg::replace called on $new to replace $old with options".
+ join(', ', map "$_ => ". $options->{$_}, keys %$options)
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #plandata shit stays in replace for upgrades until after 2.0 (or edit
+ #_upgrade_data)
+ warn " saving legacy plandata" if $DEBUG;
+ my $plandata = $new->get('plandata');
+ $new->set('plandata', '');
+
+ warn " deleting old part_pkg_option records" if $DEBUG;
+ foreach my $part_pkg_option ( $old->part_pkg_option ) {
+ my $error = $part_pkg_option->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ warn " replacing part_pkg record" if $DEBUG;
+ my $error = $new->SUPER::replace($old, $options->{options} );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ warn " inserting part_pkg_option records for plandata: $plandata|" if $DEBUG;
+ foreach my $part_pkg_option (
+ map { /^(\w+)=(.*)$/ or do { $dbh->rollback if $oldAutoCommit;
+ return "illegal plandata: $plandata";
+ };
+ new FS::part_pkg_option {
+ 'pkgpart' => $new->pkgpart,
+ 'optionname' => $1,
+ 'optionvalue' => $2,
+ };
+ }
+ split("\n", $plandata)
+ ) {
+ my $error = $part_pkg_option->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ warn " replacing pkg_svc records" if $DEBUG;
+ my $pkg_svc = $options->{'pkg_svc'} || {};
+ foreach my $part_svc ( qsearch('part_svc', {} ) ) {
+ my $quantity = $pkg_svc->{$part_svc->svcpart} || 0;
+ my $primary_svc =
+ ( defined($options->{'primary_svc'})
+ && $options->{'primary_svc'} == $part_svc->svcpart
+ )
+ ? 'Y'
+ : '';
+
+
+ my $old_pkg_svc = qsearchs('pkg_svc', {
+ 'pkgpart' => $old->pkgpart,
+ 'svcpart' => $part_svc->svcpart,
+ } );
+ my $old_quantity = $old_pkg_svc ? $old_pkg_svc->quantity : 0;
+ my $old_primary_svc =
+ ( $old_pkg_svc && $old_pkg_svc->dbdef_table->column('primary_svc') )
+ ? $old_pkg_svc->primary_svc
+ : '';
+ next unless $old_quantity != $quantity || $old_primary_svc ne $primary_svc;
+
+ my $new_pkg_svc = new FS::pkg_svc( {
+ 'pkgsvcnum' => ( $old_pkg_svc ? $old_pkg_svc->pkgsvcnum : '' ),
+ 'pkgpart' => $new->pkgpart,
+ 'svcpart' => $part_svc->svcpart,
+ 'quantity' => $quantity,
+ 'primary_svc' => $primary_svc,
+ } );
+ my $error = $old_pkg_svc
+ ? $new_pkg_svc->replace($old_pkg_svc)
+ : $new_pkg_svc->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ warn " commiting transaction" if $DEBUG;
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item check
+
+Checks all fields to make sure this is a valid package definition. If
+there is an error, returns the error, otherwise returns false. Called by the
+insert and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+ warn "FS::part_pkg::check called on $self" if $DEBUG;
+
+ for (qw(setup recur plandata)) {
+ #$self->set($_=>0) if $self->get($_) =~ /^\s*$/; }
+ return "Use of $_ field is deprecated; set a plan and options: ".
+ $self->get($_)
+ if length($self->get($_));
+ $self->set($_, '');
+ }
+
+ if ( $self->dbdef_table->column('freq')->type =~ /(int)/i ) {
+ my $error = $self->ut_number('freq');
+ return $error if $error;
+ } else {
+ $self->freq =~ /^(\d+[hdw]?)$/
+ or return "Illegal or empty freq: ". $self->freq;
+ $self->freq($1);
+ }
+
+ my @null_agentnum_right = ( 'Edit global package definitions' );
+ push @null_agentnum_right, 'One-time charge'
+ if $self->freq =~ /^0/;
+ push @null_agentnum_right, 'Customize customer package'
+ if $self->disabled eq 'Y'; #good enough
+
+ my $error = $self->ut_numbern('pkgpart')
+ || $self->ut_text('pkg')
+ || $self->ut_text('comment')
+ || $self->ut_textn('promo_code')
+ || $self->ut_alphan('plan')
+ || $self->ut_enum('setuptax', [ '', 'Y' ] )
+ || $self->ut_enum('recurtax', [ '', 'Y' ] )
+ || $self->ut_textn('taxclass')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ || $self->ut_floatn('pay_weight')
+ || $self->ut_floatn('credit_weight')
+ || $self->ut_numbern('taxproductnum')
+ || $self->ut_foreign_keyn('taxproductnum',
+ 'part_pkg_taxproduct',
+ 'taxproductnum'
+ )
+ || ( $setup_hack
+ ? $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum' )
+ : $self->ut_agentnum_acl('agentnum', \@null_agentnum_right)
+ )
+ || $self->SUPER::check
+ ;
+ return $error if $error;
+
+ if ( $self->classnum !~ /^$/ ) {
+ my $error = $self->ut_foreign_key('classnum', 'pkg_class', 'classnum');
+ return $error if $error;
+ } else {
+ $self->classnum('');
+ }
+
+ return 'Unknown plan '. $self->plan
+ unless exists($plans{$self->plan});
+
+ my $conf = new FS::Conf;
+ return 'Taxclass is required'
+ if ! $self->taxclass && $conf->exists('require_taxclasses');
+
+ '';
+}
+
+=item pkg_comment
+
+Returns an (internal) string representing this package. Currently,
+"pkgpart: pkg - comment", is returned. "pkg - comment" may be returned in the
+future, omitting pkgpart.
+
+=cut
+
+sub pkg_comment {
+ my $self = shift;
+
+ #$self->pkg. ' - '. $self->comment;
+ #$self->pkg. ' ('. $self->comment. ')';
+ $self->pkgpart. ': '. $self->pkg. ' - '. $self->comment;
+}
+
+=item pkg_class
+
+Returns the package class, as an FS::pkg_class object, or the empty string
+if there is no package class.
+
+=cut
+
+sub pkg_class {
+ my $self = shift;
+ if ( $self->classnum ) {
+ qsearchs('pkg_class', { 'classnum' => $self->classnum } );
+ } else {
+ return '';
+ }
+}
+
+=item categoryname
+
+Returns the package category name, or the empty string if there is no package
+category.
+
+=cut
+
+sub categoryname {
+ my $self = shift;
+ my $pkg_class = $self->pkg_class;
+ $pkg_class
+ ? $pkg_class->categoryname
+ : '';
+}
+
+=item classname
+
+Returns the package class name, or the empty string if there is no package
+class.
+
+=cut
+
+sub classname {
+ my $self = shift;
+ my $pkg_class = $self->pkg_class;
+ $pkg_class
+ ? $pkg_class->classname
+ : '';
+}
+
+=item agent
+
+Returns the associated agent for this event, if any, as an FS::agent object.
+
+=cut
+
+sub agent {
+ my $self = shift;
+ qsearchs('agent', { 'agentnum' => $self->agentnum } );
+}
+
+=item pkg_svc [ HASHREF | OPTION => VALUE ]
+
+Returns all FS::pkg_svc objects (see L<FS::pkg_svc>) for this package
+definition (with non-zero quantity).
+
+One option is available, I<disable_linked>. If set true it will return the
+services for this package definition alone, omitting services from any add-on
+packages.
+
+=cut
+
+=item type_pkgs
+
+Returns all FS::type_pkgs objects (see L<FS::type_pkgs>) for this package
+definition.
+
+=cut
+
+sub type_pkgs {
+ my $self = shift;
+ qsearch('type_pkgs', { 'pkgpart' => $self->pkgpart } );
+}
+
+sub pkg_svc {
+ my $self = shift;
+
+# #sort { $b->primary cmp $a->primary }
+# grep { $_->quantity }
+# qsearch( 'pkg_svc', { 'pkgpart' => $self->pkgpart } );
+
+ my $opt = ref($_[0]) ? $_[0] : { @_ };
+ my %pkg_svc = map { $_->svcpart => $_ }
+ grep { $_->quantity }
+ qsearch( 'pkg_svc', { 'pkgpart' => $self->pkgpart } );
+
+ unless ( $opt->{disable_linked} ) {
+ foreach my $dst_pkg ( map $_->dst_pkg, $self->svc_part_pkg_link ) {
+ my @pkg_svc = grep { $_->quantity }
+ qsearch( 'pkg_svc', { pkgpart=>$dst_pkg->pkgpart } );
+ foreach my $pkg_svc ( @pkg_svc ) {
+ if ( $pkg_svc{$pkg_svc->svcpart} ) {
+ my $quantity = $pkg_svc{$pkg_svc->svcpart}->quantity;
+ $pkg_svc{$pkg_svc->svcpart}->quantity($quantity + $pkg_svc->quantity);
+ } else {
+ $pkg_svc{$pkg_svc->svcpart} = $pkg_svc;
+ }
+ }
+ }
+ }
+
+ values(%pkg_svc);
+
+}
+
+=item svcpart [ SVCDB ]
+
+Returns the svcpart of the primary service definition (see L<FS::part_svc>)
+associated with this package definition (see L<FS::pkg_svc>). Returns
+false if there not a primary service definition or exactly one service
+definition with quantity 1, or if SVCDB is specified and does not match the
+svcdb of the service definition,
+
+=cut
+
+sub svcpart {
+ my $self = shift;
+ my $svcdb = scalar(@_) ? shift : '';
+ my @svcdb_pkg_svc =
+ grep { ( $svcdb eq $_->part_svc->svcdb || !$svcdb ) } $self->pkg_svc;
+ my @pkg_svc = grep { $_->primary_svc =~ /^Y/i } @svcdb_pkg_svc;
+ @pkg_svc = grep {$_->quantity == 1 } @svcdb_pkg_svc
+ unless @pkg_svc;
+ return '' if scalar(@pkg_svc) != 1;
+ $pkg_svc[0]->svcpart;
+}
+
+=item svcpart_unique_svcdb SVCDB
+
+Returns the svcpart of the a service definition (see L<FS::part_svc>) matching
+SVCDB associated with this package definition (see L<FS::pkg_svc>). Returns
+false if there not a primary service definition for SVCDB or there are multiple
+service definitions for SVCDB.
+
+=cut
+
+sub svcpart_unique_svcdb {
+ my( $self, $svcdb ) = @_;
+ my @svcdb_pkg_svc = grep { ( $svcdb eq $_->part_svc->svcdb ) } $self->pkg_svc;
+ return '' if scalar(@svcdb_pkg_svc) != 1;
+ $svcdb_pkg_svc[0]->svcpart;
+}
+
+=item payby
+
+Returns a list of the acceptable payment types for this package. Eventually
+this should come out of a database table and be editable, but currently has the
+following logic instead:
+
+If the package is free, the single item B<BILL> is
+returned, otherwise, the single item B<CARD> is returned.
+
+(CHEK? LEC? Probably shouldn't accept those by default, prone to abuse)
+
+=cut
+
+sub payby {
+ my $self = shift;
+ if ( $self->is_free ) {
+ ( 'BILL' );
+ } else {
+ ( 'CARD' );
+ }
+}
+
+=item is_free
+
+Returns true if this package is free.
+
+=cut
+
+sub is_free {
+ my $self = shift;
+ unless ( $self->plan ) {
+ $self->setup =~ /^\s*0+(\.0*)?\s*$/
+ && $self->recur =~ /^\s*0+(\.0*)?\s*$/;
+ } elsif ( $self->can('is_free_options') ) {
+ not grep { $_ !~ /^\s*0*(\.0*)?\s*$/ }
+ map { $self->option($_) }
+ $self->is_free_options;
+ } else {
+ warn "FS::part_pkg::is_free: FS::part_pkg::". $self->plan. " subclass ".
+ "provides neither is_free_options nor is_free method; returning false";
+ 0;
+ }
+}
+
+
+sub freqs_href {
+ #method, class method or sub? #my $self = shift;
+
+ tie my %freq, 'Tie::IxHash',
+ '0' => '(no recurring fee)',
+ '1h' => 'hourly',
+ '1d' => 'daily',
+ '2d' => 'every two days',
+ '3d' => 'every three days',
+ '1w' => 'weekly',
+ '2w' => 'biweekly (every 2 weeks)',
+ '1' => 'monthly',
+ '45d' => 'every 45 days',
+ '2' => 'bimonthly (every 2 months)',
+ '3' => 'quarterly (every 3 months)',
+ '4' => 'every 4 months',
+ '137d' => 'every 4 1/2 months (137 days)',
+ '6' => 'semiannually (every 6 months)',
+ '12' => 'annually',
+ '13' => 'every 13 months (annually +1 month)',
+ '24' => 'biannually (every 2 years)',
+ '36' => 'triannually (every 3 years)',
+ '48' => '(every 4 years)',
+ '60' => '(every 5 years)',
+ '120' => '(every 10 years)',
+ ;
+
+ \%freq;
+
+}
+
+=item freq_pretty
+
+Returns an english representation of the I<freq> field, such as "monthly",
+"weekly", "semi-annually", etc.
+
+=cut
+
+sub freq_pretty {
+ my $self = shift;
+ my $freq = $self->freq;
+
+ #my $freqs_href = $self->freqs_href;
+ my $freqs_href = freqs_href();
+
+ if ( exists($freqs_href->{$freq}) ) {
+ $freqs_href->{$freq};
+ } else {
+ my $interval = 'month';
+ if ( $freq =~ /^(\d+)([hdw])$/ ) {
+ my %interval = ( 'h' => 'hour', 'd'=>'day', 'w'=>'week' );
+ $interval = $interval{$2};
+ }
+ if ( $1 == 1 ) {
+ "every $interval";
+ } else {
+ "every $freq ${interval}s";
+ }
+ }
+}
+
+=item add_freq TIMESTAMP
+
+Adds the frequency of this package to the provided timestamp and returns
+the resulting timestamp, or -1 if the frequency of this package could not be
+parsed (shouldn't happen).
+
+=cut
+
+sub add_freq {
+ my( $self, $date ) = @_;
+ my $freq = $self->freq;
+
+ #change this bit to use Date::Manip? CAREFUL with timezones (see
+ # mailing list archive)
+ my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($date) )[0,1,2,3,4,5];
+
+ if ( $self->freq =~ /^\d+$/ ) {
+ $mon += $self->freq;
+ until ( $mon < 12 ) { $mon -= 12; $year++; }
+ } elsif ( $self->freq =~ /^(\d+)w$/ ) {
+ my $weeks = $1;
+ $mday += $weeks * 7;
+ } elsif ( $self->freq =~ /^(\d+)d$/ ) {
+ my $days = $1;
+ $mday += $days;
+ } elsif ( $self->freq =~ /^(\d+)h$/ ) {
+ my $hours = $1;
+ $hour += $hours;
+ } else {
+ return -1;
+ }
+
+ timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year);
+}
+
+=item plandata
+
+For backwards compatibility, returns the plandata field as well as all options
+from FS::part_pkg_option.
+
+=cut
+
+sub plandata {
+ my $self = shift;
+ carp "plandata is deprecated";
+ if ( @_ ) {
+ $self->SUPER::plandata(@_);
+ } else {
+ my $plandata = $self->get('plandata');
+ my %options = $self->options;
+ $plandata .= join('', map { "$_=$options{$_}\n" } keys %options );
+ $plandata;
+ }
+}
+
+=item part_pkg_option
+
+Returns all options as FS::part_pkg_option objects (see
+L<FS::part_pkg_option>).
+
+=cut
+
+sub part_pkg_option {
+ my $self = shift;
+ qsearch('part_pkg_option', { 'pkgpart' => $self->pkgpart } );
+}
+
+=item options
+
+Returns a list of option names and values suitable for assigning to a hash.
+
+=cut
+
+sub options {
+ my $self = shift;
+ map { $_->optionname => $_->optionvalue } $self->part_pkg_option;
+}
+
+=item option OPTIONNAME
+
+Returns the option value for the given name, or the empty string.
+
+=cut
+
+sub option {
+ my( $self, $opt, $ornull ) = @_;
+ my $part_pkg_option =
+ qsearchs('part_pkg_option', {
+ pkgpart => $self->pkgpart,
+ optionname => $opt,
+ } );
+ return $part_pkg_option->optionvalue if $part_pkg_option;
+ my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }
+ split("\n", $self->get('plandata') );
+ return $plandata{$opt} if exists $plandata{$opt};
+ cluck "WARNING: (pkgpart ". $self->pkgpart. ") Package def option $opt ".
+ "not found in options or plandata!\n"
+ unless $ornull;
+ '';
+}
+
+=item bill_part_pkg_link
+
+Returns the associated part_pkg_link records (see L<FS::part_pkg_link>).
+
+=cut
+
+sub bill_part_pkg_link {
+ shift->_part_pkg_link('bill', @_);
+}
+
+=item svc_part_pkg_link
+
+Returns the associated part_pkg_link records (see L<FS::part_pkg_link>).
+
+=cut
+
+sub svc_part_pkg_link {
+ shift->_part_pkg_link('svc', @_);
+}
+
+sub _part_pkg_link {
+ my( $self, $type ) = @_;
+ qsearch('part_pkg_link', { 'src_pkgpart' => $self->pkgpart,
+ 'link_type' => $type,
+ }
+ );
+}
+
+sub self_and_bill_linked {
+ shift->_self_and_linked('bill', @_);
+}
+
+sub _self_and_linked {
+ my( $self, $type ) = @_;
+
+ ( $self,
+ map { $_->dst_pkg->_self_and_linked($type) }
+ $self->_part_pkg_link($type)
+ );
+}
+
+=item part_pkg_taxoverride [ CLASS ]
+
+Returns all associated FS::part_pkg_taxoverride objects (see
+L<FS::part_pkg_taxoverride>). Limits the returned set to those
+of class CLASS if defined. Class may be one of 'setup', 'recur',
+the empty string (default), or a usage class number (see L<FS::usage_class>).
+When a class is specified, the empty string class (default) is returned
+if no more specific values exist.
+
+=cut
+
+sub part_pkg_taxoverride {
+ my $self = shift;
+ my $class = shift;
+
+ my $hashref = { 'pkgpart' => $self->pkgpart };
+ $hashref->{'usage_class'} = $class if defined($class);
+ my @overrides = qsearch('part_pkg_taxoverride', $hashref );
+
+ unless ( scalar(@overrides) || !defined($class) || !$class ){
+ $hashref->{'usage_class'} = '';
+ @overrides = qsearch('part_pkg_taxoverride', $hashref );
+ }
+
+ @overrides;
+}
+
+=item has_taxproduct
+
+Returns true if this package has any taxproduct associated with it.
+
+=cut
+
+sub has_taxproduct {
+ my $self = shift;
+
+ $self->taxproductnum ||
+ scalar( grep { $_ =~/^usage_taxproductnum_/ && $self->option($_) }
+ keys %{ {$self->options} }
+ )
+
+}
+
+
+=item taxproduct [ CLASS ]
+
+Returns the associated tax product for this package definition (see
+L<FS::part_pkg_taxproduct>). CLASS may be one of 'setup', 'recur' or
+the usage classnum (see L<FS::usage_class>). Returns the default
+tax product for this record if the more specific CLASS value does
+not exist.
+
+=cut
+
+sub taxproduct {
+ my $self = shift;
+ my $class = shift;
+
+ my $part_pkg_taxproduct;
+
+ my $taxproductnum = $self->taxproductnum;
+ if ($class) {
+ my $class_taxproductnum = $self->option("usage_taxproductnum_$class", 1);
+ $taxproductnum = $class_taxproductnum
+ if $class_taxproductnum
+ }
+
+ $part_pkg_taxproduct =
+ qsearchs( 'part_pkg_taxproduct', { 'taxproductnum' => $taxproductnum } );
+
+ unless ($part_pkg_taxproduct || $taxproductnum eq $self->taxproductnum ) {
+ $taxproductnum = $self->taxproductnum;
+ $part_pkg_taxproduct =
+ qsearchs( 'part_pkg_taxproduct', { 'taxproductnum' => $taxproductnum } );
+ }
+
+ $part_pkg_taxproduct;
+}
+
+=item taxproduct_description [ CLASS ]
+
+Returns the description of the associated tax product for this package
+definition (see L<FS::part_pkg_taxproduct>).
+
+=cut
+
+sub taxproduct_description {
+ my $self = shift;
+ my $part_pkg_taxproduct = $self->taxproduct(@_);
+ $part_pkg_taxproduct ? $part_pkg_taxproduct->description : '';
+}
+
+=item part_pkg_taxrate DATA_PROVIDER, GEOCODE, [ CLASS ]
+
+Returns the package to taxrate m2m records for this package in the location
+specified by GEOCODE (see L<FS::part_pkg_taxrate>) and usage class CLASS.
+CLASS may be one of 'setup', 'recur', or one of the usage classes numbers
+(see L<FS::usage_class>).
+
+=cut
+
+sub _expand_cch_taxproductnum {
+ my $self = shift;
+ my $class = shift;
+ my $part_pkg_taxproduct = $self->taxproduct($class);
+
+ my ($a,$b,$c,$d) = ( $part_pkg_taxproduct
+ ? ( split ':', $part_pkg_taxproduct->taxproduct )
+ : ()
+ );
+ $a = '' unless $a; $b = '' unless $b; $c = '' unless $c; $d = '' unless $d;
+ my $extra_sql = "AND ( taxproduct = '$a:$b:$c:$d'
+ OR taxproduct = '$a:$b:$c:'
+ OR taxproduct = '$a:$b:".":$d'
+ OR taxproduct = '$a:$b:".":' )";
+ map { $_->taxproductnum } qsearch( { 'table' => 'part_pkg_taxproduct',
+ 'hashref' => { 'data_vendor'=>'cch' },
+ 'extra_sql' => $extra_sql,
+ } );
+
+}
+
+sub part_pkg_taxrate {
+ my $self = shift;
+ my ($data_vendor, $geocode, $class) = @_;
+
+ my $dbh = dbh;
+ my $extra_sql = 'WHERE part_pkg_taxproduct.data_vendor = '.
+ dbh->quote($data_vendor);
+
+ # CCH oddness in m2m
+ $extra_sql .= ' AND ('.
+ join(' OR ', map{ 'geocode = '. $dbh->quote(substr($geocode, 0, $_)) }
+ qw(10 5 2)
+ ).
+ ')';
+ # much more CCH oddness in m2m -- this is kludgy
+ my @tpnums = $self->_expand_cch_taxproductnum($class);
+ if (scalar(@tpnums)) {
+ $extra_sql .= ' AND ('.
+ join(' OR ', map{ "taxproductnum = $_" } @tpnums ).
+ ')';
+ } else {
+ $extra_sql .= ' AND ( 0 = 1 )';
+ }
+
+ my $addl_from = 'LEFT JOIN part_pkg_taxproduct USING ( taxproductnum )';
+ my $order_by = 'ORDER BY taxclassnum, length(geocode) desc, length(taxproduct) desc';
+ my $select = 'DISTINCT ON(taxclassnum) *, taxproduct';
+
+ # should qsearch preface columns with the table to facilitate joins?
+ qsearch( { 'table' => 'part_pkg_taxrate',
+ 'select' => $select,
+ 'hashref' => { # 'data_vendor' => $data_vendor,
+ # 'taxproductnum' => $self->taxproductnum,
+ },
+ 'addl_from' => $addl_from,
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $order_by,
+ } );
+}
+
+=item _rebless
+
+Reblesses the object into the FS::part_pkg::PLAN class (if available), where
+PLAN is the object's I<plan> field. There should be better docs
+on how to create new price plans, but until then, see L</NEW PLAN CLASSES>.
+
+=cut
+
+sub _rebless {
+ my $self = shift;
+ my $plan = $self->plan;
+ unless ( $plan ) {
+ cluck "no price plan found for pkgpart ". $self->pkgpart. "\n"
+ if $DEBUG;
+ return $self;
+ }
+ return $self if ref($self) =~ /::$plan$/; #already blessed into plan subclass
+ my $class = ref($self). "::$plan";
+ warn "reblessing $self into $class" if $DEBUG;
+ eval "use $class;";
+ die $@ if $@;
+ bless($self, $class) unless $@;
+ $self;
+}
+
+#fallbacks that eval the setup and recur fields, for backwards compat
+
+sub calc_setup {
+ my $self = shift;
+ warn 'no price plan class for '. $self->plan. ", eval-ing setup\n";
+ $self->_calc_eval('setup', @_);
+}
+
+sub calc_recur {
+ my $self = shift;
+ warn 'no price plan class for '. $self->plan. ", eval-ing recur\n";
+ $self->_calc_eval('recur', @_);
+}
+
+use vars qw( $sdate @details );
+sub _calc_eval {
+ #my( $self, $field, $cust_pkg ) = @_;
+ my( $self, $field, $cust_pkg, $sdateref, $detailsref ) = @_;
+ *sdate = $sdateref;
+ *details = $detailsref;
+ $self->$field() =~ /^(.*)$/
+ or die "Illegal $field (pkgpart ". $self->pkgpart. '): '.
+ $self->$field(). "\n";
+ my $prog = $1;
+ return 0 if $prog =~ /^\s*$/;
+ my $value = eval $prog;
+ die $@ if $@;
+ $value;
+}
+
+#fallback that return 0 for old legacy packages with no plan
+
+sub calc_remain { 0; }
+sub calc_cancel { 0; }
+sub calc_units { 0; }
+
+=item format OPTION DATA
+
+Returns data formatted according to the function 'format' described
+in the plan info. Returns DATA if no such function exists.
+
+=cut
+
+sub format {
+ my ($self, $option, $data) = (shift, shift, shift);
+ if (exists($plans{$self->plan}->{fields}->{$option}{format})) {
+ &{$plans{$self->plan}->{fields}->{$option}{format}}($data);
+ }else{
+ $data;
+ }
+}
+
+=item parse OPTION DATA
+
+Returns data parsed according to the function 'parse' described
+in the plan info. Returns DATA if no such function exists.
+
+=cut
+
+sub parse {
+ my ($self, $option, $data) = (shift, shift, shift);
+ if (exists($plans{$self->plan}->{fields}->{$option}{parse})) {
+ &{$plans{$self->plan}->{fields}->{$option}{parse}}($data);
+ }else{
+ $data;
+ }
+}
+
+=back
+
+=cut
+
+=head1 CLASS METHODS
+
+=over 4
+
+=cut
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+sub _upgrade_data { # class method
+ my($class, %opts) = @_;
+
+ warn "[FS::part_pkg] upgrading $class\n" if $DEBUG;
+
+ my @part_pkg = qsearch({
+ 'table' => 'part_pkg',
+ 'extra_sql' => "WHERE ". join(' OR ',
+ ( map "($_ IS NOT NULL AND $_ != '' )",
+ qw( plandata setup recur ) ),
+ 'plan IS NULL', "plan = '' ",
+ ),
+ });
+
+ foreach my $part_pkg (@part_pkg) {
+
+ unless ( $part_pkg->plan ) {
+
+ $part_pkg->plan('flat');
+
+ if ( $part_pkg->setup =~ /^\s*([\d\.]+)\s*$/ ) {
+
+ my $opt = new FS::part_pkg_option {
+ 'pkgpart' => $part_pkg->pkgpart,
+ 'optionname' => 'setup_fee',
+ 'optionvalue' => $1,
+ };
+ my $error = $opt->insert;
+ die $error if $error;
+
+ $part_pkg->setup('');
+
+ } else {
+ die "Can't parse part_pkg.setup for fee; convert pkgnum ".
+ $part_pkg->pkgnum. " manually: ". $part_pkg->setup. "\n";
+ }
+
+ if ( $part_pkg->recur =~ /^\s*([\d\.]+)\s*$/ ) {
+
+ my $opt = new FS::part_pkg_option {
+ 'pkgpart' => $part_pkg->pkgpart,
+ 'optionname' => 'recur_fee',
+ 'optionvalue' => $1,
+ };
+ my $error = $opt->insert;
+ die $error if $error;
+
+ $part_pkg->recur('');
+
+ } else {
+ die "Can't parse part_pkg.setup for fee; convert pkgnum ".
+ $part_pkg->pkgnum. " manually: ". $part_pkg->setup. "\n";
+ }
+
+ }
+
+ $part_pkg->replace; #this should take care of plandata, right?
+
+ }
+
+}
+
+=item curuser_pkgs_sql
+
+Returns an SQL fragment for searching for packages the current user can
+use, either via part_pkg.agentnum directly, or via agent type (see
+L<FS::type_pkgs>).
+
+=cut
+
+sub curuser_pkgs_sql {
+ #my($class) = shift;
+
+ my $agentnums = join(',', $FS::CurrentUser::CurrentUser->agentnums);
+
+ "
+ (
+ agentnum IS NOT NULL
+ OR
+ 0 < ( SELECT COUNT(*)
+ FROM type_pkgs
+ LEFT JOIN agent_type USING ( typenum )
+ LEFT JOIN agent AS typeagent USING ( typenum )
+ WHERE type_pkgs.pkgpart = part_pkg.pkgpart
+ AND typeagent.agentnum IN ($agentnums)
+ )
+ )
+ ";
+
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item plan_info
+
+=cut
+
+#false laziness w/part_export & cdr
+my %info;
+foreach my $INC ( @INC ) {
+ warn "globbing $INC/FS/part_pkg/*.pm\n" if $DEBUG;
+ foreach my $file ( glob("$INC/FS/part_pkg/*.pm") ) {
+ warn "attempting to load plan info from $file\n" if $DEBUG;
+ $file =~ /\/(\w+)\.pm$/ or do {
+ warn "unrecognized file in $INC/FS/part_pkg/: $file\n";
+ next;
+ };
+ my $mod = $1;
+ my $info = eval "use FS::part_pkg::$mod; ".
+ "\\%FS::part_pkg::$mod\::info;";
+ if ( $@ ) {
+ die "error using FS::part_pkg::$mod (skipping): $@\n" if $@;
+ next;
+ }
+ unless ( keys %$info ) {
+ warn "no %info hash found in FS::part_pkg::$mod, skipping\n";
+ next;
+ }
+ warn "got plan info from FS::part_pkg::$mod: $info\n" if $DEBUG;
+ if ( exists($info->{'disabled'}) && $info->{'disabled'} ) {
+ warn "skipping disabled plan FS::part_pkg::$mod" if $DEBUG;
+ next;
+ }
+ $info{$mod} = $info;
+ }
+}
+
+tie %plans, 'Tie::IxHash',
+ map { $_ => $info{$_} }
+ sort { $info{$a}->{'weight'} <=> $info{$b}->{'weight'} }
+ keys %info;
+
+sub plan_info {
+ \%plans;
+}
+
+
+=back
+
+=head1 NEW PLAN CLASSES
+
+A module should be added in FS/FS/part_pkg/ Eventually, an example may be
+found in eg/plan_template.pm. Until then, it is suggested that you use the
+other modules in FS/FS/part_pkg/ as a guide.
+
+=head1 BUGS
+
+The delete method is unimplemented.
+
+setup and recur semantics are not yet defined (and are implemented in
+FS::cust_bill. hmm.). now they're deprecated and need to go.
+
+plandata should go
+
+part_pkg_taxrate is Pg specific
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_pkg>, L<FS::type_pkgs>, L<FS::pkg_svc>, L<Safe>.
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg/base_delayed.pm b/FS/FS/part_pkg/base_delayed.pm
new file mode 100644
index 0000000..df50376
--- /dev/null
+++ b/FS/FS/part_pkg/base_delayed.pm
@@ -0,0 +1,52 @@
+package FS::part_pkg::base_delayed;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::base_rate;
+
+@ISA = qw(FS::part_pkg::base_rate);
+
+%info = (
+ 'name' => 'Free (or setup fee) for X days, then base rate'.
+ ' (anniversary billing)',
+ 'shortname' => 'Bulk (manual from "units" option), w/intro period',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'free_days' => { 'name' => 'Initial free days',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring base fee for this package',
+ 'default' => 0,
+ },
+ 'recur_notify' => { 'name' => 'Number of days before recurring billing'.
+ ' commences to notify customer. (0 means'.
+ ' no warning)',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ },
+ 'fieldorder' => [ 'free_days', 'setup_fee', 'recur_fee', 'recur_notify',
+ 'unused_credit'
+ ],
+ #'setup' => '\'my $d = $cust_pkg->bill || $time; $d += 86400 * \' + what.free_days.value + \'; $cust_pkg->bill($d); $cust_pkg_mod_flag=1; \' + what.setup_fee.value',
+ #'recur' => 'what.recur_fee.value',
+ 'weight' => 54, #&g!
+);
+
+sub calc_setup {
+ my($self, $cust_pkg, $time ) = @_;
+
+ my $d = $cust_pkg->bill || $time;
+ $d += 86400 * $self->option('free_days');
+ $cust_pkg->bill($d);
+
+ $self->option('setup_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/base_rate.pm b/FS/FS/part_pkg/base_rate.pm
new file mode 100644
index 0000000..64636d9
--- /dev/null
+++ b/FS/FS/part_pkg/base_rate.pm
@@ -0,0 +1,96 @@
+package FS::part_pkg::base_rate;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch);
+use FS::part_pkg;
+
+@ISA = qw(FS::part_pkg);
+
+%info = (
+ 'name' => 'Base rate (anniversary billing, Times units ordered)',
+ # XXX it multiplies recurring fee by cust_pkg option "units", how to
+ # express that
+ 'shortname' => 'Bulk (manual from "units" option)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring Base fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'externalid' => { 'name' => 'Optional External ID',
+ 'default' => '',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'unused_credit',
+ 'externalid' ],
+ 'weight' => 52,
+);
+
+sub calc_setup {
+ my($self, $cust_pkg, $sdate, $details ) = @_;
+
+ my $i = 0;
+ my $count = $self->option( 'additional_count', 'quiet' ) || 0;
+ while ($i < $count) {
+ push @$details, $self->option( 'additional_info' . $i++ );
+ }
+
+ $self->option('setup_fee');
+}
+
+sub calc_recur {
+ my($self, $cust_pkg) = @_;
+ $self->base_recur($cust_pkg);
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ my $units = $cust_pkg->option('units') ? $cust_pkg->option('units') : 1 ;
+ # default to 1 if not found
+ sprintf("%.2f",
+ ($self->option('recur_fee') * $units )
+ );
+}
+
+sub calc_remain {
+ my ($self, $cust_pkg) = @_;
+ my $time = time; #should be able to pass this in for credit calculation
+ my $next_bill = $cust_pkg->getfield('bill') || 0;
+ my $last_bill = $cust_pkg->last_bill || 0;
+ return 0 if ! $self->base_recur
+ || ! $self->option('unused_credit', 1)
+ || ! $last_bill
+ || ! $next_bill
+ || $next_bill < $time;
+
+ my %sec = (
+ 'h' => 3600, # 60 * 60
+ 'd' => 86400, # 60 * 60 * 24
+ 'w' => 604800, # 60 * 60 * 24 * 7
+ 'm' => 2629744, # 60 * 60 * 24 * 365.2422 / 12
+ );
+
+ $self->freq =~ /^(\d+)([hdwm]?)$/
+ or die 'unparsable frequency: '. $self->freq;
+ my $freq_sec = $1 * $sec{$2||'m'};
+ return 0 unless $freq_sec;
+
+ sprintf("%.2f", $self->base_recur * ( $next_bill - $time ) / $freq_sec );
+
+}
+
+sub is_free_options {
+ qw( setup_fee recur_fee );
+}
+
+sub is_prepaid {
+ 0; #no, we're postpaid
+}
+
+1;
diff --git a/FS/FS/part_pkg/bulk.pm b/FS/FS/part_pkg/bulk.pm
new file mode 100644
index 0000000..63d344d
--- /dev/null
+++ b/FS/FS/part_pkg/bulk.pm
@@ -0,0 +1,96 @@
+package FS::part_pkg::bulk;
+
+use strict;
+use vars qw(@ISA $DEBUG $me %info);
+use Date::Format;
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+$DEBUG = 0;
+$me = '[FS::part_pkg::bulk]';
+
+%info = (
+ 'name' => 'Bulk billing based on number of active services',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for the entire bulk package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for the entire bulk package',
+ 'default' => 0,
+ },
+ 'svc_setup_fee' => { 'name' => 'Setup fee for each new service',
+ 'default' => 0,
+ },
+ 'svc_recur_fee' => { 'name' => 'Recurring fee for each service',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'svc_setup_fee', 'svc_recur_fee',
+ 'unused_credit', ],
+ 'weight' => 50,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg, $sdate, $details ) = @_;
+
+ my $conf = new FS::Conf;
+ my $money_char = $conf->config('money_char') || '$';
+
+ my $svc_setup_fee = $self->option('svc_setup_fee');
+
+ my $last_bill = $cust_pkg->last_bill;
+
+ my $total_svc_charge = 0;
+
+ warn "$me billing for bulk services from ". time2str('%x', $last_bill).
+ " to ". time2str('%x', $$sdate). "\n"
+ if $DEBUG;
+
+ # END START
+ foreach my $h_svc ( $cust_pkg->h_cust_svc( $$sdate, $last_bill ) ) {
+
+ my @label = $h_svc->label( $$sdate, $last_bill );
+ die "fatal: no historical label found, wtf?" unless scalar(@label); #?
+ #my $svc_details = $label[0].': '. $label[1]. ': ';
+ my $svc_details = $label[1]. ': ';
+
+ my $svc_charge = 0;
+
+ my $svc_start = $h_svc->date_inserted;
+ if ( $svc_start < $last_bill ) {
+ $svc_start = $last_bill;
+ } elsif ( $svc_setup_fee ) {
+ $svc_charge += $svc_setup_fee;
+ $svc_details .= $money_char. sprintf('%.2f setup, ', $svc_setup_fee);
+ }
+
+ my $svc_end = $h_svc->date_deleted;
+ $svc_end = ( !$svc_end || $svc_end > $$sdate ) ? $$sdate : $svc_end;
+
+ $svc_charge = $self->option('svc_recur_fee') * ( $svc_end - $svc_start )
+ / ( $$sdate - $last_bill );
+
+ $svc_details .= $money_char. sprintf('%.2f', $svc_charge ).
+ ' ('. time2str('%x', $svc_start).
+ ' - '. time2str('%x', $svc_end ). ')'
+ if $self->option('svc_recur_fee');
+
+ push @$details, $svc_details;
+ $total_svc_charge += $svc_charge;
+
+ }
+
+ sprintf("%.2f", $self->base_recur($cust_pkg) + $total_svc_charge );
+}
+
+sub is_free_options {
+ qw( setup_fee recur_fee svc_setup_fee svc_recur_fee );
+}
+
+1;
+
diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm
new file mode 100644
index 0000000..3ac44c4
--- /dev/null
+++ b/FS/FS/part_pkg/flat.pm
@@ -0,0 +1,211 @@
+package FS::part_pkg::flat;
+
+use strict;
+use vars qw(@ISA %info);
+use Tie::IxHash;
+#use FS::Record qw(qsearch);
+use FS::UI::bytecount;
+use FS::part_pkg;
+
+@ISA = qw(FS::part_pkg);
+
+tie my %temporalities, 'Tie::IxHash',
+ 'upcoming' => "Upcoming (future)",
+ 'preceding' => "Preceding (past)",
+;
+
+%info = (
+ 'name' => 'Flat rate (anniversary billing)',
+ 'shortname' => 'Anniversary',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+
+ #false laziness w/voip_cdr.pm
+ 'recur_temporality' => { 'name' => 'Charge recurring fee for period',
+ 'type' => 'select',
+ 'select_options' => \%temporalities,
+ },
+
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'externalid' => { 'name' => 'Optional External ID',
+ 'default' => '',
+ },
+ 'seconds' => { 'name' => 'Time limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ },
+ 'upbytes' => { 'name' => 'Upload limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'downbytes' => { 'name' => 'Download limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'totalbytes' => { 'name' => 'Transfer limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_amount' => { 'name' => 'Cost of recharge for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*(\.\d{2})?$/ },
+ },
+ 'recharge_seconds' => { 'name' => 'Recharge time for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ },
+ 'recharge_upbytes' => { 'name' => 'Recharge upload for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_downbytes' => { 'name' => 'Recharge download for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_totalbytes' => { 'name' => 'Recharge transfer for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'usage_rollover' => { 'name' => 'Allow usage from previous period to roll '.
+ ' over into current period',
+ 'type' => 'checkbox',
+ },
+ 'recharge_reset' => { 'name' => 'Reset usage to these values on manual '.
+ 'package recharge',
+ 'type' => 'checkbox',
+ },
+ },
+ 'fieldorder' => [qw( setup_fee recur_fee recur_temporality unused_credit
+ seconds upbytes downbytes totalbytes
+ recharge_amount recharge_seconds recharge_upbytes
+ recharge_downbytes recharge_totalbytes
+ usage_rollover recharge_reset externalid
+ )
+ ],
+ 'weight' => 10,
+);
+
+sub calc_setup {
+ my($self, $cust_pkg, $sdate, $details ) = @_;
+
+ my $i = 0;
+ my $count = $self->option( 'additional_count', 'quiet' ) || 0;
+ while ($i < $count) {
+ push @$details, $self->option( 'additional_info' . $i++ );
+ }
+
+ my $quantity = $cust_pkg->quantity || 1;
+
+ sprintf("%.2f", $quantity * $self->unit_setup($cust_pkg, $sdate, $details) );
+}
+
+sub unit_setup {
+ my($self, $cust_pkg, $sdate, $details ) = @_;
+
+ $self->option('setup_fee');
+}
+
+sub calc_recur {
+ my($self, $cust_pkg) = @_;
+
+ #my $last_bill = $cust_pkg->last_bill;
+ my $last_bill = $cust_pkg->get('last_bill'); #->last_bill falls back to setup
+
+ return 0
+ if $self->option('recur_temporality', 1) eq 'preceding' && $last_bill == 0;
+
+ $self->base_recur($cust_pkg);
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee', 1) || 0;
+}
+
+sub base_recur_permonth {
+ my($self, $cust_pkg) = @_; #$cust_pkg?
+
+ return 0 unless $self->freq =~ /^\d+$/ && $self->freq > 0;
+
+ sprintf('%.2f', $self->base_recur / $self->freq );
+}
+
+sub calc_remain {
+ my ($self, $cust_pkg, %options) = @_;
+
+ my $time;
+ if ($options{'time'}) {
+ $time = $options{'time'};
+ } else {
+ $time = time;
+ }
+
+ my $next_bill = $cust_pkg->getfield('bill') || 0;
+
+ #my $last_bill = $cust_pkg->last_bill || 0;
+ my $last_bill = $cust_pkg->get('last_bill') || 0; #->last_bill falls back to setup
+
+ return 0 if ! $self->base_recur
+ || ! $self->option('unused_credit', 1)
+ || ! $last_bill
+ || ! $next_bill
+ || $next_bill < $time;
+
+ my %sec = (
+ 'h' => 3600, # 60 * 60
+ 'd' => 86400, # 60 * 60 * 24
+ 'w' => 604800, # 60 * 60 * 24 * 7
+ 'm' => 2629744, # 60 * 60 * 24 * 365.2422 / 12
+ );
+
+ $self->freq =~ /^(\d+)([hdwm]?)$/
+ or die 'unparsable frequency: '. $self->freq;
+ my $freq_sec = $1 * $sec{$2||'m'};
+ return 0 unless $freq_sec;
+
+ sprintf("%.2f", $self->base_recur * ( $next_bill - $time ) / $freq_sec );
+
+}
+
+sub is_free_options {
+ qw( setup_fee recur_fee );
+}
+
+sub is_prepaid {
+ 0; #no, we're postpaid
+}
+
+sub reset_usage {
+ my($self, $cust_pkg, %opt) = @_;
+ warn " resetting usage counters" if $opt{debug} > 1;
+ my %values = map { $_, $self->option($_) }
+ grep { $self->option($_, 'hush') }
+ qw(seconds upbytes downbytes totalbytes);
+ if ($self->option('usage_rollover', 1)) {
+ $cust_pkg->recharge(\%values);
+ }else{
+ $cust_pkg->set_usage(\%values);
+ }
+}
+
+1;
diff --git a/FS/FS/part_pkg/flat_comission.pm b/FS/FS/part_pkg/flat_comission.pm
new file mode 100644
index 0000000..1f57e4f
--- /dev/null
+++ b/FS/FS/part_pkg/flat_comission.pm
@@ -0,0 +1,67 @@
+package FS::part_pkg::flat_comission;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Flat rate with recurring commission per (any) active package',
+ 'shortname' => 'Commission per (any) active package',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'comission_amount' => { 'name' => 'Commission amount per month (per active package)',
+ 'default' => 0,
+ },
+ 'comission_depth' => { 'name' => 'Number of layers',
+ 'default' => 1,
+ },
+ 'reason_type' => { 'name' => 'Reason type for commission credits',
+ 'type' => 'select',
+ 'select_table' => 'reason_type',
+ 'select_hash' => { 'class' => 'R' },
+ 'select_key' => 'typenum',
+ 'select_label' => 'type',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'unused_credit', 'comission_depth', 'comission_amount', 'reason_type' ],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => '\'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar($cust_pkg->cust_main->referral_cust_pkg(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
+ 'weight' => 62,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg ) = @_;
+
+ my $amount = $self->option('comission_amount');
+ my $num_active = scalar(
+ $cust_pkg->cust_main->referral_cust_pkg( $self->option('comission_depth') )
+ );
+
+ my $commission = sprintf('%.2f', $amount*$num_active);
+
+ if ( $commission > 0 ) {
+
+ my $error =
+ $cust_pkg->cust_main->credit( $commission, "commission",
+ 'reason_type'=>$self->option('reason_type'),
+ );
+ die $error if $error;
+
+ }
+
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/flat_comission_cust.pm b/FS/FS/part_pkg/flat_comission_cust.pm
new file mode 100644
index 0000000..e2034cd
--- /dev/null
+++ b/FS/FS/part_pkg/flat_comission_cust.pm
@@ -0,0 +1,65 @@
+package FS::part_pkg::flat_comission_cust;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Flat rate with recurring commission per active customer',
+ 'shortname' => 'Commission per active customer',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'comission_amount' => { 'name' => 'Commission amount per month (per active customer)',
+ 'default' => 0,
+ },
+ 'comission_depth' => { 'name' => 'Number of layers',
+ 'default' => 1,
+ },
+ 'reason_type' => { 'name' => 'Reason type for commission credits',
+ 'type' => 'select_table',
+ 'select_table' => 'reason_type',
+ 'select_hash' => { 'class' => 'R' },
+ 'select_key' => 'typenum',
+ 'select_label' => 'type',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'unused_credit', 'comission_depth', 'comission_amount', 'reason_type' ],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => '\'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar($cust_pkg->cust_main->referral_cust_main_ncancelled(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
+ 'weight' => '60',
+);
+
+sub calc_recur {
+ my($self, $cust_pkg ) = @_;
+
+ my $amount = $self->option('comission_amount');
+ my $num_active = scalar(
+ $cust_pkg->cust_main->referral_cust_main_ncancelled(
+ $self->option('comission_depth')
+ )
+ );
+
+ if ( $amount && $num_active ) {
+ my $error =
+ $cust_pkg->cust_main->credit( $amount*$num_active, "commission",
+ 'reason_type'=>$self->option('reason_type'),
+ );
+ die $error if $error;
+ }
+
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/flat_comission_pkg.pm b/FS/FS/part_pkg/flat_comission_pkg.pm
new file mode 100644
index 0000000..0a66ff0
--- /dev/null
+++ b/FS/FS/part_pkg/flat_comission_pkg.pm
@@ -0,0 +1,58 @@
+package FS::part_pkg::flat_comission_pkg;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Flat rate with recurring commission per (selected) active package',
+ 'shortname' => 'Commission per (selected) active package',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'comission_amount' => { 'name' => 'Commission amount per month (per uncancelled package)',
+ 'default' => 0,
+ },
+ 'comission_depth' => { 'name' => 'Number of layers',
+ 'default' => 1,
+ },
+ 'comission_pkgpart' => { 'name' => 'Applicable packages<BR><FONT SIZE="-1">(hold <b>ctrl</b> to select multiple packages)</FONT>',
+ 'type' => 'select_multiple',
+ 'select_table' => 'part_pkg',
+ 'select_hash' => { 'disabled' => '' } ,
+ 'select_key' => 'pkgpart',
+ 'select_label' => 'pkg',
+ },
+ 'reason_type' => { 'name' => 'Reason type for commission credits',
+ 'type' => 'select',
+ 'select_table' => 'reason_type',
+ 'select_hash' => { 'class' => 'R' } ,
+ 'select_key' => 'typenum',
+ 'select_label' => 'type',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'unused_credit', 'comission_depth', 'comission_amount', 'comission_pkgpart', 'reason_type' ],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => '""; var pkgparts = ""; for ( var c=0; c < document.flat_comission_pkg.comission_pkgpart.options.length; c++ ) { if (document.flat_comission_pkg.comission_pkgpart.options[c].selected) { pkgparts = pkgparts + document.flat_comission_pkg.comission_pkgpart.options[c].value + \', \'; } } what.recur.value = \'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar( grep { my $pkgpart = $_->pkgpart; grep { $_ == $pkgpart } ( \' + pkgparts + \' ) } $cust_pkg->cust_main->referral_cust_pkg(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
+ #'disabled' => 1,
+ 'weight' => '64',
+);
+
+# XXX this needs to be fixed!!!
+sub calc_recur {
+ my($self, $cust_pkg ) = @_;
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/flat_delayed.pm b/FS/FS/part_pkg/flat_delayed.pm
new file mode 100644
index 0000000..4a2f1ba
--- /dev/null
+++ b/FS/FS/part_pkg/flat_delayed.pm
@@ -0,0 +1,69 @@
+package FS::part_pkg::flat_delayed;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Free (or setup fee) for X days, then flat rate'.
+ ' (anniversary billing)',
+ 'shortname' => 'Anniversary, with intro period',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'free_days' => { 'name' => 'Initial free days',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'recur_notify' => { 'name' => 'Number of days before recurring billing'.
+ ' commences to notify customer. (0 means'.
+ ' no warning)',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ },
+ 'fieldorder' => [ 'free_days', 'setup_fee', 'recur_fee', 'recur_notify',
+ 'unused_credit'
+ ],
+ #'setup' => '\'my $d = $cust_pkg->bill || $time; $d += 86400 * \' + what.free_days.value + \'; $cust_pkg->bill($d); $cust_pkg_mod_flag=1; \' + what.setup_fee.value',
+ #'recur' => 'what.recur_fee.value',
+ 'weight' => 12,
+);
+
+sub calc_setup {
+ my($self, $cust_pkg, $time ) = @_;
+
+ my $d = $cust_pkg->bill || $time;
+ $d += 86400 * $self->option('free_days');
+ $cust_pkg->bill($d);
+
+ $self->option('setup_fee');
+}
+
+sub calc_remain {
+ my ($self, $cust_pkg, %options) = @_;
+ my $next_bill = $cust_pkg->getfield('bill') || 0;
+ my $last_bill = $cust_pkg->last_bill || 0;
+ my $free_days = $self->option('free_days');
+
+ return 0 if $last_bill + (86400 * $free_days) == $next_bill
+ && $last_bill == $cust_pkg->setup;
+
+ return 0 if ! $self->base_recur
+ || ! $self->option('unused_credit', 1)
+ || ! $last_bill
+ || ! $next_bill;
+
+ return $self->SUPER::calc_remain($cust_pkg, %options);
+}
+
+1;
diff --git a/FS/FS/part_pkg/flat_introrate.pm b/FS/FS/part_pkg/flat_introrate.pm
new file mode 100644
index 0000000..2568afa
--- /dev/null
+++ b/FS/FS/part_pkg/flat_introrate.pm
@@ -0,0 +1,68 @@
+package FS::part_pkg::flat_introrate;
+
+use strict;
+use vars qw(@ISA %info $DEBUG $DEBUG_PRE);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+use Date::Manip qw(DateCalc UnixDate ParseDate);
+
+@ISA = qw(FS::part_pkg::flat);
+$DEBUG = 0;
+$DEBUG_PRE = '[' . __PACKAGE__ . ']: ';
+
+%info = (
+ 'name' => 'Introductory price for X months, then flat rate,'.
+ 'relative to setup date (anniversary billing)',
+ 'shortname' => 'Anniversary, with intro price',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'intro_fee' => { 'name' => 'Introductory recurring free for this package',
+ 'default' => 0,
+ },
+ 'intro_duration' => { 'name' => 'Duration of the introductory period, ' .
+ 'in number of months',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'intro_duration', 'intro_fee', 'recur_fee', 'unused_credit' ],
+ 'weight' => 14,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg, $time ) = @_;
+
+ my ($duration) = ($self->option('intro_duration') =~ /^(\d+)$/);
+ unless ($duration) {
+ die "Invalid intro_duration: " . $self->option('intro_duration');
+ }
+
+ my $setup = &ParseDate('epoch ' . $cust_pkg->getfield('setup'));
+ my $intro_end = &DateCalc($setup, "+${duration} month");
+ my $recur;
+
+ warn $DEBUG_PRE . "\$duration = ${duration}" if $DEBUG;
+ warn $DEBUG_PRE . "\$intro_end = ${intro_end}" if $DEBUG;
+ warn $DEBUG_PRE . "$$time < " . &UnixDate($intro_end, '%s') if $DEBUG;
+
+ if ($$time < &UnixDate($intro_end, '%s')) {
+ $recur = $self->option('intro_fee');
+ } else {
+ $recur = $self->option('recur_fee');
+ }
+
+ $recur;
+
+}
+
+
+1;
diff --git a/FS/FS/part_pkg/incomplete/billoneday.pm b/FS/FS/part_pkg/incomplete/billoneday.pm
new file mode 100644
index 0000000..8740547
--- /dev/null
+++ b/FS/FS/part_pkg/incomplete/billoneday.pm
@@ -0,0 +1,48 @@
+package FS::part_pkg::billoneday;
+
+use strict;
+use vars qw(@ISA %info);
+use Time::Local qw(timelocal);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'charge a full month every (selectable) billing day',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'cutoff_day' => { 'name' => 'billing day',
+ 'default' => 1,
+ },
+
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee','cutoff_day'],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => '\'my $mnow = $sdate; my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($sdate) )[0,1,2,3,4,5]; $sdate = timelocal(0,0,0,$self->option('cutoff_day'),$mon,$year); \' + what.recur_fee.value',
+ 'freq' => 'm',
+ 'weight' => 30,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg, $sdate ) = @_;
+
+ my $mnow = $$sdate;
+ my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
+ my $mstart = timelocal(0,0,0,$self->option('cutoff_day'),$mon,$year);
+ my $mend = timelocal(0,0,0,$self->option('cutoff_day'), $mon == 11 ? 0 : $mon+1, $year+($mon==11));
+
+ if($mday > $self->option('cutoff_date') and $mstart != $mnow ) {
+ $$sdate = timelocal(0,0,0,$self->option('cutoff_day'), $mon == 11 ? 0 : $mon+1, $year+($mon==11));
+ }
+ else{
+ $$sdate = timelocal(0,0,0,$self->option('cutoff_day'), $mon, $year);
+ }
+ $self->option('recur_fee');
+}
+1;
diff --git a/FS/FS/part_pkg/prepaid.pm b/FS/FS/part_pkg/prepaid.pm
new file mode 100644
index 0000000..4499d0e
--- /dev/null
+++ b/FS/FS/part_pkg/prepaid.pm
@@ -0,0 +1,40 @@
+package FS::part_pkg::prepaid;
+
+use strict;
+use vars qw(@ISA %info %recur_action);
+use Tie::IxHash;
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+tie %recur_action, 'Tie::IxHash',
+ 'suspend' => 'suspend',
+ 'cancel' => 'cancel',
+;
+
+%info = (
+ 'name' => 'Prepaid, flat rate',
+ #'name' => 'Prepaid (no automatic recurring)', #maybe use it here too
+ 'shortname' => 'Prepaid, no automatic cycle',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'One-time setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Initial and recharge fee for this package',
+ 'default' => 0,
+ },
+ 'recur_action' => { 'name' => 'Action to take upon reaching end of prepaid preiod',
+ 'type' => 'select',
+ 'select_options' => \%recur_action,
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'recur_action', ],
+ 'weight' => 25,
+);
+
+sub is_prepaid {
+ 1;
+}
+
+1;
+
diff --git a/FS/FS/part_pkg/prorate.pm b/FS/FS/part_pkg/prorate.pm
new file mode 100644
index 0000000..d3ca77a
--- /dev/null
+++ b/FS/FS/part_pkg/prorate.pm
@@ -0,0 +1,123 @@
+package FS::part_pkg::prorate;
+
+use strict;
+use vars qw(@ISA %info);
+use Time::Local qw(timelocal);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'First partial month pro-rated, then flat-rate (selectable billing day)',
+ 'shortname' => 'Prorate (Nth of month billing)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'cutoff_day' => { 'name' => 'Billing Day (1 - 28)',
+ 'default' => 1,
+ },
+ 'seconds' => { 'name' => 'Time limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ },
+ 'upbytes' => { 'name' => 'Upload limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'downbytes' => { 'name' => 'Download limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'totalbytes' => { 'name' => 'Transfer limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_amount' => { 'name' => 'Cost of recharge for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*(\.\d{2})?$/ },
+ },
+ 'recharge_seconds' => { 'name' => 'Recharge time for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ },
+ 'recharge_upbytes' => { 'name' => 'Recharge upload for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_downbytes' => { 'name' => 'Recharge download for this package', 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_totalbytes' => { 'name' => 'Recharge transfer for this package', 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'usage_rollover' => { 'name' => 'Allow usage from previous period to roll '.
+ 'over into current period',
+ 'type' => 'checkbox',
+ },
+ 'recharge_reset' => { 'name' => 'Reset usage to these values on manual '.
+ 'package recharge',
+ 'type' => 'checkbox',
+ },
+
+ #it would be better if this had to be turned on, its confusing
+ 'externalid' => { 'name' => 'Optional External ID',
+ 'default' => '',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'unused_credit', 'cutoff_day',
+ 'seconds', 'upbyte', 'downbytes', 'totalbytes',
+ 'recharge_amount', 'recharge_seconds', 'recharge_upbytes',
+ 'recharge_downbytes', 'recharge_totalbytes',
+ 'usage_rollover', 'recharge_reset', 'externalid', ],
+ 'freq' => 'm',
+ 'weight' => 20,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg, $sdate ) = @_;
+ my $cutoff_day = $self->option('cutoff_day', 1) || 1;
+ my $mnow = $$sdate;
+ my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
+ my $mend;
+ my $mstart;
+
+ if ( $mday >= $cutoff_day ) {
+ $mend =
+ timelocal(0,0,0,$cutoff_day, $mon == 11 ? 0 : $mon+1, $year+($mon==11));
+ $mstart =
+ timelocal(0,0,0,$cutoff_day,$mon,$year);
+
+ } else {
+ $mend = timelocal(0,0,0,$cutoff_day, $mon, $year);
+ if ($mon==0) {$mon=11;$year--;} else {$mon--;}
+ $mstart= timelocal(0,0,0,$cutoff_day,$mon,$year);
+ }
+
+ $$sdate = $mstart;
+ my $permonth = $self->option('recur_fee') / $self->freq;
+
+ $permonth * ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
+}
+
+1;
diff --git a/FS/FS/part_pkg/prorate_delayed.pm b/FS/FS/part_pkg/prorate_delayed.pm
new file mode 100644
index 0000000..1d22798
--- /dev/null
+++ b/FS/FS/part_pkg/prorate_delayed.pm
@@ -0,0 +1,67 @@
+package FS::part_pkg::prorate_delayed;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg;
+
+@ISA = qw(FS::part_pkg::prorate);
+
+%info = (
+ 'name' => 'Free (or setup fee) for X days, then prorate, then flat-rate ' .
+ '(1st of month billing)',
+ 'shortname' => 'Prorate (Nth of month billing), with intro period', #??
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'free_days' => { 'name' => 'Initial free days',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'recur_notify' => { 'name' => 'Number of days before recurring billing'.
+ ' commences to notify customer. (0 means'.
+ ' no warning)',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ },
+ 'fieldorder' => [ 'free_days', 'setup_fee', 'recur_fee', 'unused_credit' ],
+ #'setup' => '\'my $d = $cust_pkg->bill || $time; $d += 86400 * \' + what.free_days.value + \'; $cust_pkg->bill($d); $cust_pkg_mod_flag=1; \' + what.setup_fee.value',
+ #'recur' => 'what.recur_fee.value',
+ 'weight' => 22,
+);
+
+sub calc_setup {
+ my($self, $cust_pkg, $time ) = @_;
+
+ my $d = $cust_pkg->bill || $time;
+ $d += 86400 * $self->option('free_days');
+ $cust_pkg->bill($d);
+
+ $self->option('setup_fee');
+}
+
+sub calc_remain {
+ my ($self, $cust_pkg, %options) = @_;
+ my $next_bill = $cust_pkg->getfield('bill') || 0;
+ my $last_bill = $cust_pkg->last_bill || 0;
+ my $free_days = $self->option('free_days');
+
+ return 0 if $last_bill + (86400 * $free_days) == $next_bill
+ && $last_bill == $cust_pkg->setup;
+
+ return 0 if ! $self->base_recur
+ || ! $self->option('unused_credit', 1)
+ || ! $last_bill
+ || ! $next_bill;
+
+ return $self->SUPER::calc_remain($cust_pkg, %options);
+}
+
+1;
diff --git a/FS/FS/part_pkg/sesmon_hour.pm b/FS/FS/part_pkg/sesmon_hour.pm
new file mode 100644
index 0000000..22ece95
--- /dev/null
+++ b/FS/FS/part_pkg/sesmon_hour.pm
@@ -0,0 +1,57 @@
+package FS::part_pkg::sesmon_hour;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Base charge plus charge per-hour from the session monitor',
+ 'shortname' => 'Session monitor (per-hour)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Base recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'recur_included_hours' => { 'name' => 'Hours included',
+ 'default' => 0,
+ },
+ 'recur_hourly_charge' => { 'name' => 'Additional charge per hour',
+ 'default' => 0,
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'unused_credit', 'recur_included_hours', 'recur_hourly_charge' ],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => '\'my $hours = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; \' + what.recur_fee.value + \' + \' + what.recur_hourly_charge.value + \' * $hours;\'',
+ 'weight' => 80,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg ) = @_;
+
+ my $hours = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 3600;
+ $hours -= $self->option('recur_included_hours');
+ $hours = 0 if $hours < 0;
+
+ $self->option('recur_fee') + $hours * $self->option('recur_hourly_charge');
+
+}
+
+sub is_free_options {
+ qw( setup_fee recur_fee recur_hourly_charge );
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/sesmon_minute.pm b/FS/FS/part_pkg/sesmon_minute.pm
new file mode 100644
index 0000000..7386df6
--- /dev/null
+++ b/FS/FS/part_pkg/sesmon_minute.pm
@@ -0,0 +1,56 @@
+package FS::part_pkg::sesmon_minute;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Base charge plus charge per-minute from the session monitor',
+ 'shortname' => 'Session monitor (per-minute)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Base recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'recur_included_min' => { 'name' => 'Minutes included',
+ 'default' => 0,
+ },
+ 'recur_minly_charge' => { 'name' => 'Additional charge per minute',
+ 'default' => 0,
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'unused_credit', 'recur_included_min', 'recur_minly_charge' ],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => '\'my $min = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 60 - \' + what.recur_included_min.value + \'; $min = 0 if $min < 0; \' + what.recur_fee.value + \' + \' + what.recur_minly_charge.value + \' * $min;\'',
+ 'weight' => 80,
+);
+
+
+sub calc_recur {
+ my( $self, $cust_pkg ) = @);
+ my $min = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 60;
+ $min -= $self->option('recur_included_min');
+ $min = 0 if $min < 0;
+
+ $self->option('recur_fee') + $min * $self->option('recur_minly_charge');
+}
+
+sub is_free_options {
+ qw( setup_fee recur_fee recur_minly_charge );
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/sql_external.pm b/FS/FS/part_pkg/sql_external.pm
new file mode 100644
index 0000000..70f9f04
--- /dev/null
+++ b/FS/FS/part_pkg/sql_external.pm
@@ -0,0 +1,77 @@
+package FS::part_pkg::sql_external;
+
+use strict;
+use vars qw(@ISA %info);
+use DBI;
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Base charge plus additional fees for external services from a configurable SQL query',
+ 'shortname' => 'External SQL query',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Base recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'datasrc' => { 'name' => 'DBI data source',
+ 'default' => '',
+ },
+ 'db_username' => { 'name' => 'Database username',
+ 'default' => '',
+ },
+ 'db_password' => { 'name' => 'Database password',
+ 'default' => '',
+ },
+ 'query' => { 'name' => 'SQL query',
+ 'default' => '',
+ },
+ },
+ 'fieldorder' => [qw( setup_fee recur_fee unused_credit datasrc db_username db_password query )],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => q!'my $dbh = DBI->connect("' + what.datasrc.value + '", "' + what.db_username.value + '", "' + what.db_password.value + '" ) or die $DBI::errstr; my $sth = $dbh->prepare("' + what.query.value + '") or die $dbh->errstr; my $price = ' + what.recur_fee.value + '; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq "svc_external" } $cust_pkg->cust_svc ){ my $id = $cust_svc->svc_x->id; $sth->execute($id) or die $sth->errstr; $price += $sth->fetchrow_arrayref->[0]; } $price;'!,
+ 'weight' => '58',
+);
+
+sub calc_recur {
+ my($self, $cust_pkg ) = @_;
+
+ my $dbh = DBI->connect( map { $self->option($_) }
+ qw( datasrc db_username db_password )
+ )
+ or die $DBI::errstr;
+
+ my $sth = $dbh->prepare( $self->option('query') )
+ or die $dbh->errstr;
+
+ my $price = $self->option('recur_fee');
+
+ foreach my $cust_svc (
+ grep { $_->part_svc->svcdb eq "svc_external" } $cust_pkg->cust_svc
+ ) {
+ my $id = $cust_svc->svc_x->id;
+ $sth->execute($id) or die $sth->errstr;
+ $price += $sth->fetchrow_arrayref->[0];
+ }
+
+ $price;
+}
+
+sub is_free {
+ 0;
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/sql_generic.pm b/FS/FS/part_pkg/sql_generic.pm
new file mode 100644
index 0000000..5a6a11a
--- /dev/null
+++ b/FS/FS/part_pkg/sql_generic.pm
@@ -0,0 +1,88 @@
+package FS::part_pkg::sql_generic;
+
+use strict;
+use vars qw(@ISA %info);
+use DBI;
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Base charge plus a per-domain metered rate from a configurable SQL query',
+ 'shortname' => 'Bulk (per-domain from SQL query)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Base recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'recur_included' => { 'name' => 'Units included',
+ 'default' => 0,
+ },
+ 'recur_unit_charge' => { 'name' => 'Additional charge per unit',
+ 'default' => 0,
+ },
+ 'datasrc' => { 'name' => 'DBI data source',
+ 'default' => '',
+ },
+ 'db_username' => { 'name' => 'Database username',
+ 'default' => '',
+ },
+ 'db_password' => { 'name' => 'Database username',
+ 'default' => '',
+ },
+ 'query' => { 'name' => 'SQL query',
+ 'default' => '',
+ },
+ },
+ 'fieldorder' => [qw( setup_fee recur_fee unused_credit recur_included recur_unit_charge datasrc db_username db_password query )],
+ # 'setup' => 'what.setup_fee.value',
+ # 'recur' => '\'my $dbh = DBI->connect(\"\' + what.datasrc.value + \'\", \"\' + what.db_username.value + \'\") or die $DBI::errstr; \'',
+ #'recur' => '\'my $dbh = DBI->connect(\"\' + what.datasrc.value + \'\", \"\' + what.db_username.value + \'\", \"\' + what.db_password.value + \'\" ) or die $DBI::errstr; my $sth = $dbh->prepare(\"\' + what.query.value + \'\") or die $dbh->errstr; my $units = 0; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq \"svc_domain\" } $cust_pkg->cust_svc ) { my $domain = $cust_svc->svc_x->domain; $sth->execute($domain) or die $sth->errstr; $units += $sth->fetchrow_arrayref->[0]; } $units -= \' + what.recur_included.value + \'; $units = 0 if $units < 0; \' + what.recur_fee.value + \' + $units * \' + what.recur_unit_charge.value + \';\'',
+ #'recur' => '\'my $dbh = DBI->connect("\' + what.datasrc.value + \'", "\' + what.db_username.value + \'", "\' what.db_password.value + \'" ) or die $DBI::errstr; my $sth = $dbh->prepare("\' + what.query.value + \'") or die $dbh->errstr; my $units = 0; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq "svc_domain" } $cust_pkg->cust_svc ) { my $domain = $cust_svc->svc_x->domain; $sth->execute($domain) or die $sth->errstr; $units += $sth->fetchrow_arrayref->[0]; } $units -= \' + what.recur_included.value + \'; $units = 0 if $units < 0; \' + what.recur_fee.value + \' + $units * \' + what.recur_unit_charge + \';\'',
+ 'weight' => '56',
+);
+
+sub calc_recur {
+ my($self, $cust_pkg ) = @_;
+
+ my $dbh = DBI->connect( map { $self->option($_) }
+ qw( datasrc db_username db_password )
+ )
+ or die $DBI::errstr;
+
+ my $sth = $dbh->prepare( $self->option('query') )
+ or die $dbh->errstr;
+
+ my $units = 0;
+ foreach my $cust_svc (
+ grep { $_->part_svc->svcdb eq "svc_domain" } $cust_pkg->cust_svc
+ ) {
+ my $domain = $cust_svc->svc_x->domain;
+ $sth->execute($domain) or die $sth->errstr;
+
+ $units += $sth->fetchrow_arrayref->[0];
+ }
+
+ $units -= $self->option('recur_included');
+ $units = 0 if $units < 0;
+
+ $self->option('recur_fee') + $units * $self->option('recur_unit_charge');
+}
+
+sub is_free_options {
+ qw( setup_fee recur_fee recur_unit_charge );
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/sqlradacct_hour.pm b/FS/FS/part_pkg/sqlradacct_hour.pm
new file mode 100644
index 0000000..c86956a
--- /dev/null
+++ b/FS/FS/part_pkg/sqlradacct_hour.pm
@@ -0,0 +1,171 @@
+package FS::part_pkg::sqlradacct_hour;
+
+use strict;
+use vars qw(@ISA %info);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'Base charge plus per-hour (and for data) from an SQL RADIUS radacct table',
+ 'shortname' => 'Usage charges from RADIUS',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Base recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+
+ 'recur_included_hours' => { 'name' => 'Hours included',
+ 'default' => 0,
+ },
+ 'recur_hourly_charge' => { 'name' => 'Additional charge per hour',
+ 'default' => 0,
+ },
+ 'recur_hourly_cap' => { 'name' => 'Maximum overage charge for hours'.
+ ' (0 means no cap)',
+
+ 'default' => 0,
+ },
+
+ 'recur_included_input' => { 'name' => 'Upload megabytes included',
+ 'default' => 0,
+ },
+ 'recur_input_charge' => { 'name' =>
+ 'Additional charge per megabyte upload',
+ 'default' => 0,
+ },
+ 'recur_input_cap' => { 'name' => 'Maximum overage charge for upload'.
+ ' (0 means no cap)',
+ 'default' => 0,
+ },
+
+ 'recur_included_output' => { 'name' => 'Download megabytes included',
+ 'default' => 0,
+ },
+ 'recur_output_charge' => { 'name' =>
+ 'Additional charge per megabyte download',
+ 'default' => 0,
+ },
+ 'recur_output_cap' => { 'name' => 'Maximum overage charge for download'.
+ ' (0 means no cap)',
+ 'default' => 0,
+ },
+
+ 'recur_included_total' => { 'name' =>
+ 'Total megabytes included',
+ 'default' => 0,
+ },
+ 'recur_total_charge' => { 'name' =>
+ 'Additional charge per megabyte total',
+ 'default' => 0,
+ },
+ 'recur_total_cap' => { 'name' => 'Maximum overage charge for total'.
+ ' megabytes (0 means no cap)',
+ 'default' => 0,
+ },
+
+ 'global_cap' => { 'name' => 'Global cap on all overage charges'.
+ ' (0 means no cap)',
+ 'default' => 0,
+ },
+
+ },
+ 'fieldorder' => [qw( setup_fee recur_fee unused_credit recur_included_hours recur_hourly_charge recur_hourly_cap recur_included_input recur_input_charge recur_input_cap recur_included_output recur_output_charge recur_output_cap recur_included_total recur_total_charge recur_total_cap global_cap )],
+ #'setup' => 'what.setup_fee.value',
+ #'recur' => '\'my $last_bill = $cust_pkg->last_bill; my $hours = $cust_pkg->seconds_since_sqlradacct($last_bill, $sdate ) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; my $input = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctInputOctets\" ) / 1048576; my $output = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctOutputOctets\" ) / 1048576; my $total = $input + $output - \' + what.recur_included_total.value + \'; $total = 0 if $total < 0; my $input = $input - \' + what.recur_included_input.value + \'; $input = 0 if $input < 0; my $output = $output - \' + what.recur_included_output.value + \'; $output = 0 if $output < 0; my $totalcharge = sprintf(\"%.2f\", \' + what.recur_total_charge.value + \' * $total); my $inputcharge = sprintf(\"%.2f\", \' + what.recur_input_charge.value + \' * $input); my $outputcharge = sprintf(\"%.2f\", \' + what.recur_output_charge.value + \' * $output); my $hourscharge = sprintf(\"%.2f\", \' + what.recur_hourly_charge.value + \' * $hours); if ( \' + what.recur_total_charge.value + \' > 0 ) { push @details, \"Last month\\\'s data \". sprintf(\"%.1f\", $total). \" megs: \\\$$totalcharge\" } if ( \' + what.recur_input_charge.value + \' > 0 ) { push @details, \"Last month\\\'s download \". sprintf(\"%.1f\", $input). \" megs: \\\$$inputcharge\" } if ( \' + what.recur_output_charge.value + \' > 0 ) { push @details, \"Last month\\\'s upload \". sprintf(\"%.1f\", $output). \" megs: \\\$$outputcharge\" } if ( \' + what.recur_hourly_charge.value + \' > 0 ) { push @details, \"Last month\\\'s time \". sprintf(\"%.1f\", $hours). \" hours: \\\$$hourscharge\"; } \' + what.recur_fee.value + \' + $hourscharge + $inputcharge + $outputcharge + $totalcharge ;\'',
+ 'weight' => 40,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg, $sdate, $details ) = @_;
+
+ my $last_bill = $cust_pkg->last_bill;
+ my $hours = $cust_pkg->seconds_since_sqlradacct($last_bill, $$sdate ) / 3600;
+ $hours -= $self->option('recur_included_hours');
+ $hours = 0 if $hours < 0;
+
+ my $input = $cust_pkg->attribute_since_sqlradacct( $last_bill,
+ $$sdate,
+ 'AcctInputOctets' )
+ / 1048576;
+
+ my $output = $cust_pkg->attribute_since_sqlradacct( $last_bill,
+ $$sdate,
+ 'AcctOutputOctets' )
+ / 1048576;
+
+ my $total = $input + $output - $self->option('recur_included_total');
+ $total = 0 if $total < 0;
+ $input = $input - $self->option('recur_included_input');
+ $input = 0 if $input < 0;
+ $output = $output - $self->option('recur_included_output');
+ $output = 0 if $output < 0;
+
+ my $totalcharge =
+ $total * sprintf('%.2f', $self->option('recur_total_charge'));
+ $totalcharge = $self->option('recur_total_cap')
+ if $self->option('recur_total_cap')
+ && $totalcharge > $self->option('recur_total_cap');
+
+ my $inputcharge =
+ $input * sprintf('%.2f', $self->option('recur_input_charge'));
+ $inputcharge = $self->option('recur_input_cap')
+ if $self->option('recur_input_cap')
+ && $inputcharge > $self->option('recur_input_cap');
+
+ my $outputcharge =
+ $output * sprintf('%.2f', $self->option('recur_output_charge'));
+ $outputcharge = $self->option('recur_output_cap')
+ if $self->option('recur_output_cap')
+ && $outputcharge > $self->option('recur_output_cap');
+
+ my $hourscharge =
+ $hours * sprintf('%.2f', $self->option('recur_hourly_charge'));
+ $hourscharge = $self->option('recur_hourly_cap')
+ if $self->option('recur_hourly_cap')
+ && $hourscharge > $self->option('recur_hourly_cap');
+
+ if ( $self->option('recur_total_charge') > 0 ) {
+ push @$details, "Last month's data ".
+ sprintf('%.1f', $total). " megs: $totalcharge";
+ }
+ if ( $self->option('recur_input_charge') > 0 ) {
+ push @$details, "Last month's download ".
+ sprintf('%.1f', $input). " megs: $inputcharge";
+ }
+ if ( $self->option('recur_output_charge') > 0 ) {
+ push @$details, "Last month's upload ".
+ sprintf('%.1f', $output). " megs: $outputcharge";
+ }
+ if ( $self->option('recur_hourly_charge') > 0 ) {
+ push @$details, "Last month\'s time ".
+ sprintf('%.1f', $hours). " hours: $hourscharge";
+ }
+
+ my $charges = $hourscharge + $inputcharge + $outputcharge + $totalcharge;
+ if ( $self->option('global_cap') && $charges > $self->option('global_cap') ) {
+ $charges = $self->option('global_cap');
+ push @$details, "Usage charges capped at: $charges";
+ }
+
+ $self->option('recur_fee') + $charges;
+}
+
+sub is_free_options {
+ qw( setup_fee recur_fee recur_hourly_charge
+ recur_input_charge recur_output_charge recur_total_charge );
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/subscription.pm b/FS/FS/part_pkg/subscription.pm
new file mode 100644
index 0000000..dbf6d79
--- /dev/null
+++ b/FS/FS/part_pkg/subscription.pm
@@ -0,0 +1,109 @@
+package FS::part_pkg::subscription;
+
+use strict;
+use vars qw(@ISA %info);
+use Time::Local qw(timelocal);
+#use FS::Record qw(qsearch qsearchs);
+use FS::part_pkg::flat;
+
+@ISA = qw(FS::part_pkg::flat);
+
+%info = (
+ 'name' => 'First partial month full charge, then flat-rate (selectable billing day)',
+ 'shortname' => 'Subscription (Nth of month, full charge for first)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Recurring fee for this package',
+ 'default' => 0,
+ },
+ 'cutoff_day' => { 'name' => 'billing day',
+ 'default' => 1,
+ },
+ 'seconds' => { 'name' => 'Time limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ },
+ 'upbytes' => { 'name' => 'Upload limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'downbytes' => { 'name' => 'Download limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'totalbytes' => { 'name' => 'Transfer limit for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_amount' => { 'name' => 'Cost of recharge for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*(\.\d{2})?$/ },
+ },
+ 'recharge_seconds' => { 'name' => 'Recharge time for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ },
+ 'recharge_upbytes' => { 'name' => 'Recharge upload for this package',
+ 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_downbytes' => { 'name' => 'Recharge download for this package', 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'recharge_totalbytes' => { 'name' => 'Recharge transfer for this package', 'default' => '',
+ 'check' => sub { shift =~ /^\d*$/ },
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'usage_rollover' => { 'name' => 'Allow usage from previous period to roll '.
+ 'over into current period',
+ 'type' => 'checkbox',
+ },
+ 'recharge_reset' => { 'name' => 'Reset usage to these values on manual '.
+ 'package recharge',
+ 'type' => 'checkbox',
+ },
+
+ #it would be better if this had to be turned on, its confusing
+ 'externalid' => { 'name' => 'Optional External ID',
+ 'default' => '',
+ },
+ },
+ 'fieldorder' => [ 'setup_fee', 'recur_fee', 'cutoff_day', 'seconds',
+ 'upbytes', 'downbytes', 'totalbytes',
+ 'recharge_amount', 'recharge_seconds', 'recharge_upbytes',
+ 'recharge_downbytes', 'recharge_totalbytes',
+ 'usage_rollover', 'recharge_reset', 'externalid' ],
+ 'freq' => 'm',
+ 'weight' => 30,
+);
+
+sub calc_recur {
+ my($self, $cust_pkg, $sdate ) = @_;
+ my $cutoff_day = $self->option('cutoff_day', 1) || 1;
+ my $mnow = $$sdate;
+ my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
+
+ if ( $mday < $cutoff_day ) {
+ if ($mon==0) {$mon=11;$year--;}
+ else {$mon--;}
+ }
+
+ $$sdate = timelocal(0,0,0,$cutoff_day,$mon,$year);
+
+ $self->option('recur_fee');
+}
+
+1;
diff --git a/FS/FS/part_pkg/voip_cdr.pm b/FS/FS/part_pkg/voip_cdr.pm
new file mode 100644
index 0000000..a691fda
--- /dev/null
+++ b/FS/FS/part_pkg/voip_cdr.pm
@@ -0,0 +1,612 @@
+package FS::part_pkg::voip_cdr;
+
+use strict;
+use vars qw(@ISA $DEBUG %info);
+use Date::Format;
+use Tie::IxHash;
+use FS::Conf;
+use FS::Record qw(qsearchs qsearch);
+use FS::part_pkg::flat;
+use FS::cdr;
+use FS::rate;
+use FS::rate_prefix;
+use FS::rate_detail;
+
+@ISA = qw(FS::part_pkg::flat);
+
+$DEBUG = 0;
+
+tie my %rating_method, 'Tie::IxHash',
+ 'prefix' => 'Rate calls by using destination prefix to look up a region and rate according to the internal prefix and rate tables',
+ 'upstream' => 'Rate calls based on upstream data: If the call type is "1", map the upstream rate ID directly to an internal rate (rate_detail), otherwise, pass the upstream price through directly.',
+ 'upstream_simple' => 'Simply pass through and charge the "upstream_price" amount.',
+;
+
+#tie my %cdr_location, 'Tie::IxHash',
+# 'internal' => 'Internal: CDR records imported into the internal CDR table',
+# 'external' => 'External: CDR records queried directly from an external '.
+# 'Asterisk (or other?) CDR table',
+#;
+
+tie my %temporalities, 'Tie::IxHash',
+ 'upcoming' => "Upcoming (future)",
+ 'preceding' => "Preceding (past)",
+;
+
+%info = (
+ 'name' => 'VoIP rating by plan of CDR records in an internal (or external) SQL table',
+ 'shortname' => 'VoIP/telco CDR rating (standard)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Base recurring fee for this package',
+ 'default' => 0,
+ },
+
+ #false laziness w/flat.pm
+ 'recur_temporality' => { 'name' => 'Charge recurring fee for period',
+ 'type' => 'select',
+ 'select_options' => \%temporalities,
+ },
+
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+
+ 'rating_method' => { 'name' => 'Region rating method',
+ 'type' => 'radio',
+ 'options' => \%rating_method,
+ },
+
+ 'ratenum' => { 'name' => 'Rate plan',
+ 'type' => 'select',
+ 'select_table' => 'rate',
+ 'select_key' => 'ratenum',
+ 'select_label' => 'ratename',
+ },
+
+ 'ignore_unrateable' => { 'name' => 'Ignore calls without a rate in the rate tables. By default, the system will throw a fatal error upon encountering unrateable calls.',
+ 'type' => 'checkbox',
+ },
+
+ 'default_prefix' => { 'name' => 'Default prefix optionally prepended to customer DID numbers when searching for CDR records',
+ 'default' => '+1',
+ },
+
+ 'disable_src' => { 'name' => 'Disable rating of CDR records based on the "src" field in addition to "charged_party"',
+ 'type' => 'checkbox'
+ },
+
+ 'domestic_prefix' => { 'name' => 'Destination prefix for domestic CDR records',
+ 'default' => '1',
+ },
+
+# 'domestic_prefix_required' => { 'name' => 'Require explicit destination prefix for domestic CDR records',
+# 'type' => 'checkbox',
+# },
+
+ 'international_prefix' => { 'name' => 'Destination prefix for international CDR records',
+ 'default' => '011',
+ },
+
+ 'disable_tollfree' => { 'name' => 'Disable automatic toll-free processing',
+ 'type' => 'checkbox',
+ },
+
+ 'use_amaflags' => { 'name' => 'Do not charge for CDRs where the amaflags field is not set to "2" ("BILL"/"BILLING").',
+ 'type' => 'checkbox',
+ },
+
+ 'use_disposition' => { 'name' => 'Do not charge for CDRs where the disposition flag is not set to "ANSWERED".',
+ 'type' => 'checkbox',
+ },
+
+ 'use_disposition_taqua' => { 'name' => 'Do not charge for CDRs where the disposition is not set to "100" (Taqua).',
+ 'type' => 'checkbox',
+ },
+
+ 'use_carrierid' => { 'name' => 'Do not charge for CDRs where the Carrier ID is not set to: ',
+ },
+
+ 'use_cdrtypenum' => { 'name' => 'Do not charge for CDRs where the CDR Type is not set to: ',
+ },
+
+ 'skip_dcontext' => { 'name' => 'Do not charge for CDRs where the dcontext is set to any of these (comma-separated) values:',
+ },
+
+ 'skip_dstchannel_prefix' => { 'name' => 'Do not charge for CDRs where the dstchannel starts with:',
+ },
+
+ 'use_duration' => { 'name' => 'Calculate usage based on the duration field instead of the billsec field',
+ 'type' => 'checkbox',
+ },
+
+ '411_rewrite' => { 'name' => 'Rewrite these (comma-separated) destination numbers to 411 for rating purposes (also ignore any carrierid check): ',
+ },
+
+ 'output_format' => { 'name' => 'CDR invoice display format',
+ 'type' => 'select',
+ 'select_options' => { FS::cdr::invoice_formats() },
+ 'default' => 'default', #XXX test
+ },
+
+ 'usage_section' => { 'name' => 'Section in which to place separate usage charges',
+ },
+
+ 'summarize_usage' => { 'name' => 'Include usage summary with recurring charges when usage is in separate section',
+ 'type' => 'checkbox',
+ },
+
+ 'bill_every_call' => { 'name' => 'Generate an invoice immediately for every call. Useful for prepaid.',
+ 'type' => 'checkbox',
+ },
+
+ #XXX also have option for an external db
+# 'cdr_location' => { 'name' => 'CDR database location'
+# 'type' => 'select',
+# 'select_options' => \%cdr_location,
+# 'select_callback' => {
+# 'external' => {
+# 'enable' => [ 'datasrc', 'username', 'password' ],
+# },
+# 'internal' => {
+# 'disable' => [ 'datasrc', 'username', 'password' ],
+# }
+# },
+# },
+# 'datasrc' => { 'name' => 'DBI data source for external CDR table',
+# 'disabled' => 'Y',
+# },
+# 'username' => { 'name' => 'External database username',
+# 'disabled' => 'Y',
+# },
+# 'password' => { 'name' => 'External database password',
+# 'disabled' => 'Y',
+# },
+
+ },
+ 'fieldorder' => [qw(
+ setup_fee recur_fee recur_temporality unused_credit
+ rating_method ratenum ignore_unrateable
+ default_prefix
+ disable_src
+ domestic_prefix international_prefix
+ disable_tollfree
+ use_amaflags use_disposition
+ use_disposition_taqua use_carrierid use_cdrtypenum
+ skip_dcontext skip_dstchannel_prefix
+ use_duration
+ 411_rewrite
+ output_format summarize_usage usage_section
+ bill_every_call
+ )
+ ],
+ 'weight' => 40,
+);
+
+sub calc_setup {
+ my($self, $cust_pkg ) = @_;
+ $self->option('setup_fee');
+}
+
+#false laziness w/voip_sqlradacct calc_recur resolve it if that one ever gets used again
+sub calc_recur {
+ my($self, $cust_pkg, $sdate, $details, $param ) = @_;
+
+ #my $last_bill = $cust_pkg->last_bill;
+ my $last_bill = $cust_pkg->get('last_bill'); #->last_bill falls back to setup
+
+ return 0
+ if $self->option('recur_temporality', 1) eq 'preceding' && $last_bill == 0;
+
+ my $ratenum = $cust_pkg->part_pkg->option('ratenum');
+
+ my $spool_cdr = $cust_pkg->cust_main->spool_cdr;
+
+ my %included_min = ();
+
+ my $charges = 0;
+
+ my $downstream_cdr = '';
+
+ my $rating_method = $self->option('rating_method') || 'prefix';
+ my $intl = $self->option('international_prefix') || '011';
+ my $domestic_prefix = $self->option('domestic_prefix');
+ my $disable_tollfree = $self->option('disable_tollfree');
+ my $ignore_unrateable = $self->option('ignore_unrateable', 'Hush!');
+ my $use_duration = $self->option('use_duration');
+
+ my $output_format = $self->option('output_format', 'Hush!')
+ || ( $rating_method eq 'upstream_simple'
+ ? 'simple'
+ : 'default'
+ );
+
+ my @dirass = ();
+ if ( $self->option('411_rewrite') ) {
+ my $dirass = $self->option('411_rewrite');
+ $dirass =~ s/\s//g;
+ @dirass = split(',', $dirass);
+ }
+
+ #for check_chargable, so we don't keep looking up options inside the loop
+ my %opt_cache = ();
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+ my $csv = new Text::CSV_XS;
+
+ foreach my $cust_svc (
+ grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc
+ ) {
+
+ foreach my $cdr (
+ $cust_svc->get_cdrs_for_update(
+ 'disable_src' => $self->option('disable_src'),
+ 'default_prefix' => $self->option('default_prefix'),
+ ) # $last_bill, $$sdate )
+ ) {
+ if ( $DEBUG > 1 ) {
+ warn "rating CDR $cdr\n".
+ join('', map { " $_ => ". $cdr->{$_}. "\n" } keys %$cdr );
+ }
+
+ my $rate_detail;
+ my( $rate_region, $regionnum );
+ my $pretty_destnum;
+ my $charge = '';
+ my $classnum = '';
+ my @call_details = ();
+ if ( $rating_method eq 'prefix' ) {
+
+ my $da_rewrote = 0;
+ if ( length($cdr->dst) && grep { $cdr->dst eq $_ } @dirass ){
+ $cdr->dst('411');
+ $da_rewrote = 1;
+ }
+
+ my $reason = $self->check_chargable( $cdr,
+ 'da_rewrote' => $da_rewrote,
+ 'option_cache' => \%opt_cache,
+ );
+
+ if ( $reason ) {
+
+ warn "not charging for CDR ($reason)\n" if $DEBUG;
+ $charge = 0;
+
+ } else {
+
+ ###
+ # look up rate details based on called station id
+ # (or calling station id for toll free calls)
+ ###
+
+ my( $to_or_from, $number );
+ if ( $cdr->dst =~ /^(\+?1)?8([02-8])\1/
+ && ! $disable_tollfree
+ )
+ { #tollfree call
+ $to_or_from = 'from';
+ $number = $cdr->src;
+ } else { #regular call
+ $to_or_from = 'to';
+ $number = $cdr->dst;
+ }
+
+ warn "parsing call $to_or_from $number\n" if $DEBUG;
+
+ #remove non-phone# stuff and whitespace
+ $number =~ s/\s//g;
+# my $proto = '';
+# $dest =~ s/^(\w+):// and $proto = $1; #sip:
+# my $siphost = '';
+# $dest =~ s/\@(.*)$// and $siphost = $1; # @10.54.32.1, @sip.example.com
+
+ #determine the country code
+ my $countrycode;
+ if ( $number =~ /^$intl(((\d)(\d))(\d))(\d+)$/
+ || $number =~ /^\+(((\d)(\d))(\d))(\d+)$/
+ )
+ {
+
+ my( $three, $two, $one, $u1, $u2, $rest ) = ( $1,$2,$3,$4,$5,$6 );
+ #first look for 1 digit country code
+ if ( qsearch('rate_prefix', { 'countrycode' => $one } ) ) {
+ $countrycode = $one;
+ $number = $u1.$u2.$rest;
+ } elsif ( qsearch('rate_prefix', { 'countrycode' => $two } ) ) { #or 2
+ $countrycode = $two;
+ $number = $u2.$rest;
+ } else { #3 digit country code
+ $countrycode = $three;
+ $number = $rest;
+ }
+
+ } else {
+ $countrycode = $domestic_prefix || '1';
+ $number =~ s/^$countrycode//;# if length($number) > 10;
+ }
+
+ warn "rating call $to_or_from +$countrycode $number\n" if $DEBUG;
+ $pretty_destnum = "+$countrycode $number";
+
+ my $rate = qsearchs('rate', { 'ratenum' => $ratenum })
+ or die "ratenum $ratenum not found!";
+
+ $rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
+ 'phonenum' => $number,
+ });
+
+ if ( $rate_detail ) {
+
+ $rate_region = $rate_detail->dest_region;
+ $regionnum = $rate_region->regionnum;
+ warn " found rate for regionnum $regionnum ".
+ "and rate detail $rate_detail\n"
+ if $DEBUG;
+
+ } elsif ( $ignore_unrateable ) {
+
+ $rate_region = '';
+ $regionnum = '';
+ #code below will throw a warning & skip
+
+ } else {
+
+ die "FATAL: no rate_detail found in ".
+ $rate->ratenum. ":". $rate->ratename. " rate plan ".
+ "for +$countrycode $number (CDR acctid ". $cdr->acctid. "); ".
+ "add a rate or set ignore_unrateable flag on the package def\n";
+ }
+
+ }
+
+ } elsif ( $rating_method eq 'upstream' ) { #XXX this was convergent, not currently used. very much becoming the odd one out. remove?
+
+ if ( $cdr->cdrtypenum == 1 ) { #rate based on upstream rateid
+
+ $rate_detail = $cdr->cdr_upstream_rate->rate_detail;
+
+ $regionnum = $rate_detail->dest_regionnum;
+ $rate_region = $rate_detail->dest_region;
+
+ $pretty_destnum = $cdr->dst;
+
+ warn " found rate for regionnum $regionnum and ".
+ "rate detail $rate_detail\n"
+ if $DEBUG;
+
+ } else { #pass upstream price through
+
+ $charge = sprintf('%.2f', $cdr->upstream_price);
+ $charges += $charge;
+
+ @call_details = (
+ #time2str("%Y %b %d - %r", $cdr->calldate_unix ),
+ time2str("%c", $cdr->calldate_unix), #XXX this should probably be a config option dropdown so they can select US vs- rest of world dates or whatnot
+ 'N/A', #minutes...
+ '$'.$charge,
+ #$pretty_destnum,
+ $cdr->description, #$rate_region->regionname,
+ );
+
+ }
+
+ } elsif ( $rating_method eq 'upstream_simple' ) {
+
+ #XXX $charge = sprintf('%.2f', $cdr->upstream_price);
+ $charge = sprintf('%.3f', $cdr->upstream_price);
+ $charges += $charge;
+
+ @call_details = ($cdr->downstream_csv( 'format' => $output_format,
+ 'charge' => $charge,
+ )
+ );
+ $classnum = $cdr->calltypenum;
+
+ } else {
+ die "don't know how to rate CDRs using method: $rating_method\n";
+ }
+
+ ###
+ # find the price and add detail to the invoice
+ ###
+
+ # if $rate_detail is not found, skip this CDR... i.e.
+ # don't add it to invoice, don't set its status to done,
+ # don't call downstream_csv or something on it...
+ # but DO emit a warning...
+ #if ( ! $rate_detail && ! scalar(@call_details) ) {}
+ if ( ! $rate_detail && $charge eq '' ) {
+
+ warn "no rate_detail found for CDR.acctid: ". $cdr->acctid.
+ "; skipping\n"
+
+ } else { # there *is* a rate_detail (or call_details), proceed...
+
+ unless ( @call_details || ( $charge ne '' && $charge == 0 ) ) {
+
+ $included_min{$regionnum} = $rate_detail->min_included
+ unless exists $included_min{$regionnum};
+
+ my $granularity = $rate_detail->sec_granularity;
+
+ # length($cdr->billsec) ? $cdr->billsec : $cdr->duration;
+ my $seconds = $use_duration ? $cdr->duration : $cdr->billsec;
+
+ $seconds += $granularity - ( $seconds % $granularity )
+ if $seconds # don't granular-ize 0 billsec calls (bills them)
+ && $granularity; # 0 is per call
+ my $minutes = sprintf("%.1f", $seconds / 60);
+ $minutes =~ s/\.0$// if $granularity == 60;
+
+ # per call rather than per minute
+ $minutes = 1 unless $granularity;
+
+ $included_min{$regionnum} -= $minutes;
+
+ if ( $included_min{$regionnum} < 0 ) {
+ my $charge_min = 0 - $included_min{$regionnum};
+ $included_min{$regionnum} = 0;
+ $charge = sprintf('%.2f', $rate_detail->min_charge * $charge_min );
+ $charges += $charge;
+ }
+
+ # this is why we need regionnum/rate_region....
+ warn " (rate region $rate_region)\n" if $DEBUG;
+
+ @call_details = (
+ $cdr->downstream_csv( 'format' => $output_format,
+ 'granularity' => $granularity,
+ 'minutes' => $minutes,
+ 'charge' => $charge,
+ 'pretty_dst' => $pretty_destnum,
+ 'dst_regionname' => $rate_region->regionname,
+ )
+ );
+
+ $classnum = $rate_detail->classnum;
+
+ }
+
+ if ( $charge > 0 ) {
+ #just use FS::cust_bill_pkg_detail objects?
+ my $call_details;
+
+ #if ( $self->option('rating_method') eq 'upstream_simple' ) {
+ if ( scalar(@call_details) == 1 ) {
+ $call_details = [ 'C', $call_details[0], $charge, $classnum ];
+ } else { #only used for $rating_method eq 'upstream' now
+ $csv->combine(@call_details);
+ $call_details = [ 'C', $csv->string, $charge, $classnum ];
+ }
+ warn " adding details on charge to invoice: [ ".
+ join(', ', @{$call_details} ). " ]"
+ if ( $DEBUG && ref($call_details) );
+ push @$details, $call_details; #\@call_details,
+ }
+
+ # if the customer flag is on, call "downstream_csv" or something
+ # like it to export the call downstream!
+ # XXX price plan option to pick format, or something...
+ $downstream_cdr .= $cdr->downstream_csv( 'format' => 'convergent' )
+ if $spool_cdr;
+
+ my $error = $cdr->set_status_and_rated_price('done', $charge);
+ die $error if $error;
+
+ }
+
+ } # $cdr
+
+ } # $cust_svc
+
+ unshift @$details, [ 'C', FS::cdr::invoice_header($output_format) ]
+ if @$details && $rating_method ne 'upstream';
+
+ if ( $spool_cdr && length($downstream_cdr) ) {
+
+ use FS::UID qw(datasrc);
+ my $dir = '/usr/local/etc/freeside/export.'. datasrc. '/cdr';
+ mkdir $dir, 0700 unless -d $dir;
+ $dir .= '/'. $cust_pkg->custnum.
+ mkdir $dir, 0700 unless -d $dir;
+ my $filename = time2str("$dir/CDR%Y%m%d-spool.CSV", time); #XXX invoice date instead? would require changing the order things are generated in cust_main::bill insert cust_bill first - with transactions it could be done though
+
+ push @{ $param->{'precommit_hooks'} },
+ sub {
+ #lock the downstream spool file and append the records
+ use Fcntl qw(:flock);
+ use IO::File;
+ my $spool = new IO::File ">>$filename"
+ or die "can't open $filename: $!\n";
+ flock( $spool, LOCK_EX)
+ or die "can't lock $filename: $!\n";
+ seek($spool, 0, 2)
+ or die "can't seek to end of $filename: $!\n";
+ print $spool $downstream_cdr;
+ flock( $spool, LOCK_UN );
+ close $spool;
+ };
+
+ } #if ( $spool_cdr && length($downstream_cdr) )
+
+ $charges += $self->option('recur_fee')
+ if $param->{'increment_next_bill'};
+
+ $charges;
+}
+
+#returns a reason why not to rate this CDR, or false if the CDR is chargeable
+sub check_chargable {
+ my( $self, $cdr, %flags ) = @_;
+
+ #should have some better way of checking these options from a hash
+ #or something
+
+ my @opt = qw(
+ use_amaflags
+ use_disposition
+ use_disposition_taqua
+ use_carrierid
+ use_cdrtypenum
+ skip_dcontext
+ skip_dstchannel_prefix;
+ );
+ foreach my $opt (grep !exists($flags{option_cache}->{$_}), @opt ) {
+ $flags{option_cache}->{$opt} = $self->option($opt);
+ }
+ my %opt = %{ $flags{option_cache} };
+
+ return 'amaflags != 2'
+ if $opt{'use_amaflags'} && $cdr->amaflags != 2;
+
+ return 'disposition != ANSWERED'
+ if $opt{'use_disposition'} && $cdr->disposition ne 'ANSWERED';
+
+ return "disposition != 100"
+ if $opt{'use_disposition_taqua'} && $cdr->disposition != 100;
+
+ return "carrierid != $opt{'use_carrierid'}"
+ if length($opt{'use_carrierid'})
+ && $cdr->carrierid ne $opt{'use_carrierid'} #ne otherwise 0 matches ''
+ && ! $flags{'da_rewrote'};
+
+ return "cdrtypenum != $opt{'use_cdrtypenum'}"
+ if length($opt{'use_cdrtypenum'})
+ && $cdr->cdrtypenum ne $opt{'use_cdrtypenum'}; #ne otherwise 0 matches ''
+
+ return "dcontext IN ( $opt{'skip_dcontext'} )"
+ if $opt{'skip_dcontext'} =~ /\S/
+ && grep { $cdr->dcontext eq $_ } split(/\s*,\s*/, $opt{'skip_dcontext'});
+
+ my $len = length($opt{'skip_dstchannel_prefix'});
+ return "dstchannel starts with $opt{'skip_dstchannel_prefix'}"
+ if $len
+ && substr($cdr->dstchannel, 0, $len) eq $opt{'skip_dstchannel_prefix'};
+
+ #all right then, rate it
+ '';
+}
+
+sub is_free {
+ 0;
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee');
+}
+
+# This equates svc_phone records; perhaps svc_phone should have a field
+# to indicate it represents a line
+sub calc_units {
+ my($self, $cust_pkg ) = @_;
+ scalar(grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc);
+}
+
+1;
+
diff --git a/FS/FS/part_pkg/voip_sqlradacct.pm b/FS/FS/part_pkg/voip_sqlradacct.pm
new file mode 100644
index 0000000..b4f0cf9
--- /dev/null
+++ b/FS/FS/part_pkg/voip_sqlradacct.pm
@@ -0,0 +1,194 @@
+package FS::part_pkg::voip_sqlradacct;
+
+use strict;
+use vars qw(@ISA $DEBUG %info);
+use Date::Format;
+use FS::Record qw(qsearchs qsearch);
+use FS::part_pkg::flat;
+#use FS::rate;
+use FS::rate_prefix;
+
+@ISA = qw(FS::part_pkg::flat);
+
+$DEBUG = 1;
+
+%info = (
+ 'disabled' => 1, #they're sucked into our CDR table now instead
+ 'name' => 'VoIP rating by plan of CDR records in an SQL RADIUS radacct table',
+ 'shortname' => 'VoIP/telco CDR rating (external RADIUS)',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_fee' => { 'name' => 'Base recurring fee for this package',
+ 'default' => 0,
+ },
+ 'unused_credit' => { 'name' => 'Credit the customer for the unused portion'.
+ ' of service at cancellation',
+ 'type' => 'checkbox',
+ },
+ 'ratenum' => { 'name' => 'Rate plan',
+ 'type' => 'select',
+ 'select_table' => 'rate',
+ 'select_key' => 'ratenum',
+ 'select_label' => 'ratename',
+ },
+ },
+ 'fieldorder' => [qw( setup_fee recur_fee unused_credit ratenum ignore_unrateable )],
+ 'weight' => 40,
+);
+
+sub calc_setup {
+ my($self, $cust_pkg ) = @_;
+ $self->option('setup_fee');
+}
+
+#false laziness w/voip_cdr... resolve it if this one ever gets used again
+sub calc_recur {
+ my($self, $cust_pkg, $sdate, $details ) = @_;
+
+ my $last_bill = $cust_pkg->last_bill;
+
+ my $ratenum = $cust_pkg->part_pkg->option('ratenum');
+
+ my %included_min = ();
+
+ my $charges = 0;
+
+ foreach my $cust_svc (
+ grep { $_->part_svc->svcdb eq 'svc_acct' } $cust_pkg->cust_svc
+ ) {
+
+ foreach my $session (
+ $cust_svc->get_session_history( $last_bill, $$sdate )
+ ) {
+ if ( $DEBUG > 1 ) {
+ warn "rating session $session\n".
+ join('', map { " $_ => ". $session->{$_}. "\n" } keys %$session );
+ }
+
+ ###
+ # look up rate details based on called station id
+ ###
+
+ my $dest = $session->{'calledstationid'};
+
+ #remove non-phone# stuff and whitespace
+ $dest =~ s/\s//g;
+ my $proto = '';
+ $dest =~ s/^(\w+):// and $proto = $1; #sip:
+ my $siphost = '';
+ $dest =~ s/\@(.*)$// and $siphost = $1; # @10.54.32.1, @sip.example.com
+
+ #determine the country code
+ my $countrycode;
+ if ( $dest =~ /^011(((\d)(\d))(\d))(\d+)$/ ) {
+
+ my( $three, $two, $one, $u1, $u2, $rest ) = ( $1, $2, $3, $4, $5, $6 );
+ #first look for 1 digit country code
+ if ( qsearch('rate_prefix', { 'countrycode' => $one } ) ) {
+ $countrycode = $one;
+ $dest = $u1.$u2.$rest;
+ } elsif ( qsearch('rate_prefix', { 'countrycode' => $two } ) ) { #or 2
+ $countrycode = $two;
+ $dest = $u2.$rest;
+ } else { #3 digit country code
+ $countrycode = $three;
+ $dest = $rest;
+ }
+
+ } else {
+ $countrycode = '1';
+ $dest =~ s/^1//;# if length($dest) > 10;
+ }
+
+ warn "rating call to +$countrycode $dest\n" if $DEBUG;
+
+ #find a rate prefix, first look at most specific (4 digits) then 3, etc.,
+ # finally trying the country code only
+ my $rate_prefix = '';
+ for my $len ( reverse(1..6) ) {
+ $rate_prefix = qsearchs('rate_prefix', {
+ 'countrycode' => $countrycode,
+ #'npa' => { op=> 'LIKE', value=> substr($dest, 0, $len) }
+ 'npa' => substr($dest, 0, $len),
+ } ) and last;
+ }
+ $rate_prefix ||= qsearchs('rate_prefix', {
+ 'countrycode' => $countrycode,
+ 'npa' => '',
+ });
+
+ die "Can't find rate for call to +$countrycode $dest\n"
+ unless $rate_prefix;
+
+ my $regionnum = $rate_prefix->regionnum;
+ my $rate_detail = qsearchs('rate_detail', {
+ 'ratenum' => $ratenum,
+ 'dest_regionnum' => $regionnum,
+ } );
+
+ warn " found rate for regionnum $regionnum ".
+ "and rate detail $rate_detail\n"
+ if $DEBUG;
+
+ ###
+ # find the price and add detail to the invoice
+ ###
+
+ $included_min{$regionnum} = $rate_detail->min_included
+ unless exists $included_min{$regionnum};
+
+ my $granularity = $rate_detail->sec_granularity;
+ my $seconds = $session->{'acctsessiontime'};
+ $seconds += $granularity - ( $seconds % $granularity );
+ my $minutes = sprintf("%.1f", $seconds / 60);
+ $minutes =~ s/\.0$// if $granularity == 60;
+
+ $included_min{$regionnum} -= $minutes;
+
+ my $charge = 0;
+ if ( $included_min{$regionnum} < 0 ) {
+ my $charge_min = 0 - $included_min{$regionnum};
+ $included_min{$regionnum} = 0;
+ $charge = sprintf('%.2f', $rate_detail->min_charge * $charge_min );
+ $charges += $charge;
+ }
+
+ my $rate_region = $rate_prefix->rate_region;
+ warn " (rate region $rate_region)\n" if $DEBUG;
+
+ my @call_details = (
+ #time2str("%Y %b %d - %r", $session->{'acctstarttime'}),
+ time2str("%c", $session->{'acctstarttime'}),
+ $minutes.'m',
+ '$'.$charge,
+ "+$countrycode $dest",
+ $rate_region->regionname,
+ );
+
+ warn " adding details on charge to invoice: ".
+ join(' - ', @call_details )
+ if $DEBUG;
+
+ push @$details, join(' - ', @call_details); #\@call_details,
+
+ } # $session
+
+ } # $cust_svc
+
+ $self->option('recur_fee') + $charges;
+
+}
+
+sub is_free {
+ 0;
+}
+
+sub base_recur {
+ my($self, $cust_pkg) = @_;
+ $self->option('recur_fee');
+}
+
+1;
+
diff --git a/FS/FS/part_pkg_link.pm b/FS/FS/part_pkg_link.pm
new file mode 100644
index 0000000..f517360
--- /dev/null
+++ b/FS/FS/part_pkg_link.pm
@@ -0,0 +1,157 @@
+package FS::part_pkg_link;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs );
+use FS::part_pkg;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_pkg_link - Object methods for part_pkg_link records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_link;
+
+ $record = new FS::part_pkg_link \%hash;
+ $record = new FS::part_pkg_link { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_link object represents an link from one package definition to
+another. FS::part_pkg_link inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item pkglinknum
+
+primary key
+
+=item src_pkgpart
+
+Source package (see L<FS::part_pkg>)
+
+=item dst_pkgpart
+
+Destination package (see L<FS::part_pkg>)
+
+=item link_type
+
+Link type - currently, "bill" (source package bills a line item from target
+package), or "svc" (source package includes services from target package).
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new link. To add the link to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_pkg_link'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid link. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('pkglinknum')
+ || $self->ut_foreign_key('src_pkgpart', 'part_pkg', 'pkgpart')
+ || $self->ut_foreign_key('dst_pkgpart', 'part_pkg', 'pkgpart')
+ || $self->ut_text('link_type', [ 'bill', 'svc' ] )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item src_pkg
+
+Returns the source part_pkg object (see L<FS::part_pkg>).
+
+=cut
+
+sub src_pkg {
+ my $self = shift;
+ qsearchs('part_pkg', { 'pkgpart' => $self->src_pkgpart } );
+}
+
+=item dst_pkg
+
+Returns the source part_pkg object (see L<FS::part_pkg>).
+
+=cut
+
+sub dst_pkg {
+ my $self = shift;
+ qsearchs('part_pkg', { 'pkgpart' => $self->dst_pkgpart } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg_option.pm b/FS/FS/part_pkg_option.pm
new file mode 100644
index 0000000..9708f11
--- /dev/null
+++ b/FS/FS/part_pkg_option.pm
@@ -0,0 +1,150 @@
+package FS::part_pkg_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::part_pkg;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_pkg_option - Object methods for part_pkg_option records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_option;
+
+ $record = new FS::part_pkg_option \%hash;
+ $record = new FS::part_pkg_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_option object represents an package definition option.
+FS::part_pkg_option inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item pkgpart - package definition (see L<FS::part_pkg>)
+
+=item optionname - option name
+
+=item optionvalue - option value
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new package definition option. To add the package definition option
+to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_pkg_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid package definition option. If
+there is an error, returns the error, otherwise returns false. Called by the
+insert and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('pkgpart', 'part_pkg', 'pkgpart')
+ || $self->ut_alpha('optionname')
+ || $self->ut_anything('optionvalue')
+ ;
+ return $error if $error;
+
+ #check options & values?
+
+ $self->SUPER::check;
+}
+
+=back
+
+=cut
+
+#
+# Used by FS::Upgrade to migrate to a new database.
+#
+#
+
+sub _upgrade_data { # class method
+ my ($class, %opts) = @_;
+
+ my $sql = "UPDATE part_pkg_option SETUP optionname = 'recur_fee'".
+ " WHERE optionname = 'recur_flat'";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+
+ '';
+
+}
+
+=head1 BUGS
+
+Possibly.
+
+=head1 SEE ALSO
+
+L<FS::part_pkg>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg_taxclass.pm b/FS/FS/part_pkg_taxclass.pm
new file mode 100644
index 0000000..fda200e
--- /dev/null
+++ b/FS/FS/part_pkg_taxclass.pm
@@ -0,0 +1,158 @@
+package FS::part_pkg_taxclass;
+
+use strict;
+use vars qw( @ISA );
+use FS::UID qw(dbh);
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_pkg_taxclass - Object methods for part_pkg_taxclass records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_taxclass;
+
+ $record = new FS::part_pkg_taxclass \%hash;
+ $record = new FS::part_pkg_taxclass { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_taxclass object represents a tax class. FS::part_pkg_taxclass
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item taxclassnum
+
+Primary key
+
+=item taxclass
+
+Tax class
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new tax class. To add the tax class to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'part_pkg_taxclass'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid tax class. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('taxclassnum')
+ || $self->ut_text('taxclass')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=cut
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+
+sub _upgrade_data { # class method
+ my ($class, %opts) = @_;
+
+ my $sth = dbh->prepare('
+ SELECT DISTINCT taxclass
+ FROM cust_main_county
+ LEFT JOIN part_pkg_taxclass USING ( taxclass )
+ WHERE taxclassnum IS NULL
+ AND taxclass IS NOT NULL
+ ') or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my %taxclass = map { $_->[0] => 1 } @{$sth->fetchall_arrayref};
+ my @taxclass = grep $_, keys %taxclass;
+
+ foreach my $taxclass ( @taxclass ) {
+
+ my $part_pkg_taxclass = new FS::part_pkg_taxclass ( {
+ 'taxclass' => $taxclass,
+ } );
+ my $error = $part_pkg_taxclass->insert;
+ die $error if $error;
+
+ }
+
+}
+
+=head1 BUGS
+
+Other tables (cust_main_county, part_pkg, agent_payment_gateway) have a text
+taxclass instead of a key to this table.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg_taxoverride.pm b/FS/FS/part_pkg_taxoverride.pm
new file mode 100644
index 0000000..0fdfa50
--- /dev/null
+++ b/FS/FS/part_pkg_taxoverride.pm
@@ -0,0 +1,119 @@
+package FS::part_pkg_taxoverride;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_pkg_taxoverride - Object methods for part_pkg_taxoverride records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_taxoverride;
+
+ $record = new FS::part_pkg_taxoverride \%hash;
+ $record = new FS::part_pkg_taxoverride { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_taxoverride object represents a manual mapping of a
+package to tax rates. FS::part_pkg_taxoverride inherits from FS::Record.
+The following fields are currently supported:
+
+=over 4
+
+=item taxoverridenum
+
+Primary key
+
+=item pkgpart
+
+The package definition id
+
+=item taxclassnum
+
+The tax class id
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new tax override. To add the tax product to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'part_pkg_taxoverride'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid tax product. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('taxoverridenum')
+ || $self->ut_foreign_key('pkgpart', 'part_pkg', 'pkgpart')
+ || $self->ut_foreign_key('taxclassnum', 'tax_class', 'taxclassnum')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=cut
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg_taxproduct.pm b/FS/FS/part_pkg_taxproduct.pm
new file mode 100644
index 0000000..c66fb8c
--- /dev/null
+++ b/FS/FS/part_pkg_taxproduct.pm
@@ -0,0 +1,136 @@
+package FS::part_pkg_taxproduct;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_pkg_taxproduct - Object methods for part_pkg_taxproduct records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_taxproduct;
+
+ $record = new FS::part_pkg_taxproduct \%hash;
+ $record = new FS::part_pkg_taxproduct { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_taxproduct object represents a tax product.
+FS::part_pkg_taxproduct inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item taxproductnum
+
+Primary key
+
+=item data_vendor
+
+Tax data vendor
+
+=item taxproduct
+
+Tax product id from the vendor
+
+=item description
+
+A human readable description of the id in taxproduct
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new tax product. To add the tax product to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'part_pkg_taxproduct'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete a tax product which has attached package tax rates!"
+ if qsearch( 'part_pkg_taxrate', { 'taxproductnum' => $self->taxproductnum } );
+
+ return "Can't delete a tax product which has attached packages!"
+ if qsearch( 'part_pkg', { 'taxproductnum' => $self->taxproductnum } );
+
+ $self->SUPER::delete(@_);
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid tax product. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('taxproductnum')
+ || $self->ut_textn('data_vendor')
+ || $self->ut_text('taxproduct')
+ || $self->ut_textn('description')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=cut
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_pkg_taxrate.pm b/FS/FS/part_pkg_taxrate.pm
new file mode 100644
index 0000000..6d1414a
--- /dev/null
+++ b/FS/FS/part_pkg_taxrate.pm
@@ -0,0 +1,405 @@
+package FS::part_pkg_taxrate;
+
+use strict;
+use vars qw( @ISA );
+use Date::Parse;
+use FS::UID qw(dbh);
+use FS::Record qw( qsearch qsearchs );
+use FS::part_pkg_taxproduct;
+use FS::Misc qw(csv_from_fixed);
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_pkg_taxrate - Object methods for part_pkg_taxrate records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_taxrate;
+
+ $record = new FS::part_pkg_taxrate \%hash;
+ $record = new FS::part_pkg_taxrate { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_taxrate object maps packages onto tax rates.
+FS::part_pkg_taxrate inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item pkgtaxratenum
+
+Primary key
+
+=item data_vendor
+
+Tax data vendor
+
+=item geocode
+
+Tax vendor location code
+
+=item taxproductnum
+
+Class of package for tax purposes, Index into FS::part_pkg_taxproduct
+
+=item city
+
+city
+
+=item county
+
+county
+
+=item state
+
+state
+
+=item local
+
+local
+
+=item country
+
+country
+
+=item taxclassnum
+
+Class of tax index into FS::tax_taxclass and FS::tax_rate
+
+=item taxclassnumtaxed
+
+Class of tax taxed by this entry.
+
+=item taxable
+
+taxable
+
+=item effdate
+
+effdate
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new customer (location), package, tax rate mapping. To add the
+mapping to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'part_pkg_taxrate'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid tax rate mapping. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('pkgtaxratenum')
+ || $self->ut_textn('data_vendor')
+ || $self->ut_textn('geocode')
+ || $self->
+ ut_foreign_key('taxproductnum', 'part_pkg_taxproduct', 'taxproductnum')
+ || $self->ut_textn('city')
+ || $self->ut_textn('county')
+ || $self->ut_textn('state')
+ || $self->ut_textn('local')
+ || $self->ut_text('country')
+ || $self->ut_foreign_keyn('taxclassnumtaxed', 'tax_class', 'taxclassnum')
+ || $self->ut_foreign_key('taxclassnum', 'tax_class', 'taxclassnum')
+ || $self->ut_snumbern('effdate')
+ || $self->ut_enum('taxable', [ 'Y', '' ])
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item batch_import
+
+Loads part_pkg_taxrate records from an external CSV file. If there is
+an error, returns the error, otherwise returns false.
+
+=cut
+
+sub batch_import {
+ my ($param, $job) = @_;
+
+ my $fh = $param->{filehandle};
+ my $format = $param->{'format'};
+
+ my $imported = 0;
+ my @fields;
+ my $hook;
+
+ my @column_lengths = ();
+ my @column_callbacks = ();
+ if ( $format eq 'cch-fixed' || $format eq 'cch-fixed-update' ) {
+ $format =~ s/-fixed//;
+ my $date_format = sub { my $r='';
+ /^(\d{4})(\d{2})(\d{2})$/ && ($r="$1/$2/$3");
+ $r;
+ };
+ $column_callbacks[16] = $date_format;
+ push @column_lengths, qw( 28 25 2 1 10 4 30 3 100 2 2 2 2 1 2 2 8 1 );
+ push @column_lengths, 1 if $format eq 'cch-update';
+ }
+
+ my $line;
+ my ( $count, $last, $min_sec ) = (0, time, 5); #progressbar
+ if ( $job || scalar(@column_callbacks) ) {
+ my $error =
+ csv_from_fixed(\$fh, \$count, \@column_lengths, \@column_callbacks);
+ return $error if $error;
+ }
+
+ if ( $format eq 'cch' || $format eq 'cch-update' ) {
+ @fields = qw( city county state local geocode group groupdesc item
+ itemdesc provider customer taxtypetaxed taxcattaxed
+ taxable taxtype taxcat effdate rectype );
+ push @fields, 'actionflag' if $format eq 'cch-update';
+
+ $imported++ if $format eq 'cch-update'; #empty file ok
+
+ $hook = sub {
+ my $hash = shift;
+
+ unless ( $hash->{'rectype'} eq 'R' or $hash->{'rectype'} eq 'T' ) {
+ delete($hash->{$_}) for (keys %$hash);
+ return;
+ }
+
+ $hash->{'data_vendor'} = 'cch';
+
+ my %providers = ( '00' => 'Regulated LEC',
+ '01' => 'Regulated IXC',
+ '02' => 'Unregulated LEC',
+ '03' => 'Unregulated IXC',
+ '04' => 'ISP',
+ '05' => 'Wireless',
+ );
+
+ my %customers = ( '00' => 'Residential',
+ '01' => 'Commercial',
+ '02' => 'Industrial',
+ '09' => 'Lifeline',
+ '10' => 'Senior Citizen',
+ );
+
+ my $taxproduct =
+ join(':', map{ $hash->{$_} } qw(group item provider customer ) );
+
+ my %part_pkg_taxproduct = ( 'data_vendor' => 'cch',
+ 'taxproduct' => $taxproduct,
+ );
+
+ my $part_pkg_taxproduct = qsearchs( 'part_pkg_taxproduct',
+ { %part_pkg_taxproduct }
+ );
+
+ unless ($part_pkg_taxproduct) {
+ return "Can't find part_pkg_taxproduct for txmatrix deletion: ".
+ join(" ", map { "$_ => ". $hash->{$_} } @fields)
+ if ($hash->{'actionfield'} && $hash->{'actionflag'} eq 'D');
+
+ $part_pkg_taxproduct{'description'} =
+ join(' : ', (map{ $hash->{$_} } qw(groupdesc itemdesc)),
+ $providers{$hash->{'provider'}},
+ $customers{$hash->{'customer'}},
+ );
+ $part_pkg_taxproduct = new FS::part_pkg_taxproduct \%part_pkg_taxproduct;
+ my $error = $part_pkg_taxproduct->insert;
+ return "Error inserting tax product (part_pkg_taxproduct): $error"
+ if $error;
+
+ }
+ $hash->{'taxproductnum'} = $part_pkg_taxproduct->taxproductnum;
+
+ delete($hash->{$_})
+ for qw(group groupdesc item itemdesc provider customer rectype );
+
+ my %map = ( 'taxclassnum' => [ 'taxtype', 'taxcat' ],
+ 'taxclassnumtaxed' => [ 'taxtypetaxed', 'taxcattaxed' ],
+ );
+
+ for my $item (keys %map) {
+ my $class = join(':', map($hash->{$_}, @{$map{$item}}));
+ my $tax_class =
+ qsearchs( 'tax_class',
+ { data_vendor => 'cch',
+ 'taxclass' => $class,
+ }
+ );
+ $hash->{$item} = $tax_class->taxclassnum
+ if $tax_class;
+
+ return "Can't find tax class for txmatrix deletion: ".
+ join(" ", map { "$_ => ". $hash->{$_} } @fields)
+ if ( $hash->{'actionflag'} && $hash->{'actionflag'} eq 'D' &&
+ !$tax_class && $class ne ':'
+ );
+
+ delete($hash->{$_}) foreach @{$map{$item}};
+ }
+
+ $hash->{'effdate'} = str2time($hash->{'effdate'});
+ $hash->{'country'} = 'US'; # CA is available
+
+ delete($hash->{'taxable'}) if ($hash->{'taxable'} eq 'N');
+
+ if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
+ delete($hash->{actionflag});
+
+ my $part_pkg_taxrate = qsearchs('part_pkg_taxrate', $hash);
+ return "Can't find part_pkg_taxrate to delete: ".
+ #join(" ", map { "$_ => ". $hash->{$_} } @fields)
+ join(" ", map { "$_ => *". $hash->{$_}. '*' } keys(%$hash) )
+ unless $part_pkg_taxrate;
+
+ my $error = $part_pkg_taxrate->delete;
+ return $error if $error;
+
+ delete($hash->{$_}) foreach (keys %$hash);
+ }
+
+ delete($hash->{actionflag});
+
+ '';
+ };
+
+ } elsif ( $format eq 'extended' ) {
+ die "unimplemented\n";
+ @fields = qw( );
+ $hook = sub {};
+ } else {
+ die "unknown format $format";
+ }
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+
+ my $csv = new Text::CSV_XS;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ while ( defined($line=<$fh>) ) {
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+
+
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my @columns = $csv->fields();
+
+ my %part_pkg_taxrate = ( 'data_vendor' => $format );
+ foreach my $field ( @fields ) {
+ $part_pkg_taxrate{$field} = shift @columns;
+ }
+ if ( scalar( @columns ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unexpected trailing columns in line (wrong format?): $line";
+ }
+
+ my $error = &{$hook}(\%part_pkg_taxrate);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ next unless scalar(keys %part_pkg_taxrate);
+
+
+ my $part_pkg_taxrate = new FS::part_pkg_taxrate( \%part_pkg_taxrate );
+ $error = $part_pkg_taxrate->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert part_pkg_taxrate for $line: $error";
+ }
+
+ $imported++;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return "Empty file!" unless ( $imported || $format eq 'cch-update' );
+
+ ''; #no error
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/part_pop_local.pm b/FS/FS/part_pop_local.pm
new file mode 100644
index 0000000..01c59df
--- /dev/null
+++ b/FS/FS/part_pop_local.pm
@@ -0,0 +1,113 @@
+package FS::part_pop_local;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record; # qw( qsearchs );
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::part_pop_local - Object methods for part_pop_local records
+
+=head1 SYNOPSIS
+
+ use FS::part_pop_local;
+
+ $record = new FS::part_pop_local \%hash;
+ $record = new FS::part_pop_local { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pop_local object represents a local call area. Each
+FS::part_pop_local record maps a NPA/NXX (area code and exchange) to the POP
+(see L<FS::svc_acct_pop>) which is a local call. FS::part_pop_local inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item localnum - primary key (assigned automatically for new accounts)
+
+=item popnum - see L<FS::svc_acct_pop>
+
+=item city
+
+=item state
+
+=item npa - area code
+
+=item nxx - exchange
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new point of presence (if only it were that easy!). To add the
+point of presence to the database, see L<"insert">.
+
+=cut
+
+sub table { 'part_pop_local'; }
+
+=item insert
+
+Adds this point of presence to the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item delete
+
+Removes this point of presence from the database.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid point of presence. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('localnum')
+ or $self->ut_numbern('popnum')
+ or $self->ut_text('city')
+ or $self->ut_text('state')
+ or $self->ut_number('npa')
+ or $self->ut_number('nxx')
+ or $self->SUPER::check
+ ;
+
+}
+
+=back
+
+=head1 BUGS
+
+US/CA-centric.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::svc_acct_pop>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_referral.pm b/FS/FS/part_referral.pm
new file mode 100644
index 0000000..c94c57e
--- /dev/null
+++ b/FS/FS/part_referral.pm
@@ -0,0 +1,208 @@
+package FS::part_referral;
+
+use strict;
+use vars qw( @ISA $setup_hack );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::agent;
+
+@ISA = qw( FS::Record );
+$setup_hack = 0;
+
+=head1 NAME
+
+FS::part_referral - Object methods for part_referral objects
+
+=head1 SYNOPSIS
+
+ use FS::part_referral;
+
+ $record = new FS::part_referral \%hash
+ $record = new FS::part_referral { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_referral represents a advertising source - where a customer heard
+of your services. This can be used to track the effectiveness of a particular
+piece of advertising, for example. FS::part_referral inherits from FS::Record.
+The following fields are currently supported:
+
+=over 4
+
+=item refnum - primary key (assigned automatically for new referrals)
+
+=item referral - Text name of this advertising source
+
+=item disabled - Disabled flag, empty or 'Y'
+
+=item agentnum - Optional agentnum (see L<FS::agent>)
+
+=back
+
+=head1 NOTE
+
+These were called B<referrals> before version 1.4.0 - the name was changed
+so as not to be confused with the new customer-to-customer referrals.
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new advertising source. To add the referral to the database, see
+L<"insert">.
+
+=cut
+
+sub table { 'part_referral'; }
+
+=item insert
+
+Adds this advertising source to the database. If there is an error, returns
+the error, otherwise returns false.
+
+=item delete
+
+Currently unimplemented.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ return "Can't (yet?) delete part_referral records";
+ #need to make sure no customers have this referral!
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid advertising source. If there is
+an error, returns the error, otherwise returns false. Called by the insert and
+replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error = $self->ut_numbern('refnum')
+ || $self->ut_text('referral')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ #|| $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
+ || ( $setup_hack
+ ? $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum' )
+ : $self->ut_agentnum_acl('agentnum', 'Edit global advertising sources')
+ )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item agent
+
+Returns the associated agent for this referral, if any, as an FS::agent object.
+
+=cut
+
+sub agent {
+ my $self = shift;
+ qsearchs('agent', { 'agentnum' => $self->agentnum } );
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item acl_agentnum_sql [ INCLUDE_GLOBAL_BOOL ]
+
+Returns an SQL fragment for searching for part_referral records allowed by the
+current users's agent ACLs (and "Edit global advertising sources" right).
+
+Pass a true value to include global advertising sources (for example, when
+simply using rather than editing advertising sources).
+
+=cut
+
+sub acl_agentnum_sql {
+ my $self = shift;
+
+ my $curuser = $FS::CurrentUser::CurrentUser;
+ my $sql = $curuser->agentnums_sql;
+ $sql = " ( $sql OR agentnum IS NULL ) "
+ if $curuser->access_right('Edit global advertising sources')
+ or defined($_[0]) && $_[0];
+
+ $sql;
+
+}
+
+=item all_part_referral [ INCLUDE_GLOBAL_BOOL ]
+
+Returns all part_referral records allowed by the current users's agent ACLs
+(and "Edit global advertising sources" right).
+
+Pass a true value to include global advertising sources (for example, when
+simply using rather than editing advertising sources).
+
+=cut
+
+sub all_part_referral {
+ my $self = shift;
+
+ qsearch({
+ 'table' => 'part_referral',
+ 'extra_sql' => ' WHERE '. $self->acl_agentnum_sql(@_). ' ORDER BY refnum ',
+ });
+
+}
+
+=item num_part_referral [ INCLUDE_GLOBAL_BOOL ]
+
+Returns the number of part_referral records allowed by the current users's
+agent ACLs (and "Edit global advertising sources" right).
+
+=cut
+
+sub num_part_referral {
+ my $self = shift;
+
+ my $sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM part_referral WHERE '. $self->acl_agentnum_sql(@_)
+ ) or die dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=back
+
+=head1 BUGS
+
+The delete method is unimplemented.
+
+`Advertising source'. Yes, it's a sucky name. The only other ones I could
+come up with were "Marketing channel" and "Heard Abouts" and those are
+definately both worse.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_main>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm
new file mode 100644
index 0000000..580038b
--- /dev/null
+++ b/FS/FS/part_svc.pm
@@ -0,0 +1,838 @@
+package FS::part_svc;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use Tie::IxHash;
+use FS::Record qw( qsearch qsearchs fields dbh );
+use FS::Schema qw( dbdef );
+use FS::part_svc_column;
+use FS::part_export;
+use FS::export_svc;
+use FS::cust_svc;
+
+@ISA = qw(FS::Record);
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::part_svc - Object methods for part_svc objects
+
+=head1 SYNOPSIS
+
+ use FS::part_svc;
+
+ $record = new FS::part_svc \%hash
+ $record = new FS::part_svc { 'column' => 'value' };
+
+ $error = $record->insert;
+ $error = $record->insert( [ 'pseudofield' ] );
+ $error = $record->insert( [ 'pseudofield' ], \%exportnums );
+
+ $error = $new_record->replace($old_record);
+ $error = $new_record->replace($old_record, '1.3-COMPAT', [ 'pseudofield' ] );
+ $error = $new_record->replace($old_record, '1.3-COMPAT', [ 'pseudofield' ], \%exportnums );
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_svc represents a service definition. FS::part_svc inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item svcpart - primary key (assigned automatically for new service definitions)
+
+=item svc - text name of this service definition
+
+=item svcdb - table used for this service. See L<FS::svc_acct>,
+L<FS::svc_domain>, and L<FS::svc_forward>, among others.
+
+=item disabled - Disabled flag, empty or `Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new service definition. To add the service definition to the
+database, see L<"insert">.
+
+=cut
+
+sub table { 'part_svc'; }
+
+=item insert [ EXTRA_FIELDS_ARRAYREF [ , EXPORTNUMS_HASHREF [ , JOB ] ] ]
+
+Adds this service definition to the database. If there is an error, returns
+the error, otherwise returns false.
+
+The following pseudo-fields may be defined, and will be maintained in
+the part_svc_column table appropriately (see L<FS::part_svc_column>).
+
+=over 4
+
+=item I<svcdb>__I<field> - Default or fixed value for I<field> in I<svcdb>.
+
+=item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null or empty (no default), `D' for default, `F' for fixed (unchangeable), `M' for manual selection from inventory, or `A' for automatic selection from inventory. For virtual fields, can also be 'X' for excluded.
+
+=back
+
+If you want to add part_svc_column records for fields that do not exist as
+(real or virtual) fields in the I<svcdb> table, make sure to list then in
+EXTRA_FIELDS_ARRAYREF also.
+
+If EXPORTNUMS_HASHREF is specified (keys are exportnums and values are
+boolean), the appopriate export_svc records will be inserted.
+
+TODOC: JOB
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my @fields = ();
+ my @exportnums = ();
+ @fields = @{shift(@_)} if @_;
+ if ( @_ ) {
+ my $exportnums = shift;
+ @exportnums = grep $exportnums->{$_}, keys %$exportnums;
+ }
+ my $job = '';
+ $job = shift if @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ # add part_svc_column records
+
+ my $svcdb = $self->svcdb;
+# my @rows = map { /^${svcdb}__(.*)$/; $1 }
+# grep ! /_flag$/,
+# grep /^${svcdb}__/,
+# fields('part_svc');
+ foreach my $field (
+ grep { $_ ne 'svcnum'
+ && defined( $self->getfield($svcdb.'__'.$_.'_flag') )
+ } (fields($svcdb), @fields)
+ ) {
+ my $part_svc_column = $self->part_svc_column($field);
+ my $previous = qsearchs('part_svc_column', {
+ 'svcpart' => $self->svcpart,
+ 'columnname' => $field,
+ } );
+
+ my $flag = $self->getfield($svcdb.'__'.$field.'_flag');
+ #if ( uc($flag) =~ /^([DFMAX])$/ ) {
+ if ( uc($flag) =~ /^([A-Z])$/ ) { #part_svc_column will test it
+ my $parser = FS::part_svc->svc_table_fields($svcdb)->{$field}->{parse}
+ || sub { shift };
+ $part_svc_column->setfield('columnflag', $1);
+ $part_svc_column->setfield('columnvalue',
+ &$parser($self->getfield($svcdb.'__'.$field))
+ );
+ if ( $previous ) {
+ $error = $part_svc_column->replace($previous);
+ } else {
+ $error = $part_svc_column->insert;
+ }
+ } else {
+ $error = $previous ? $previous->delete : '';
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ # add export_svc records
+ my $slice = 100/scalar(@exportnums) if @exportnums;
+ my $done = 0;
+ foreach my $exportnum ( @exportnums ) {
+ my $export_svc = new FS::export_svc ( {
+ 'exportnum' => $exportnum,
+ 'svcpart' => $self->svcpart,
+ } );
+ $error = $export_svc->insert($job, $slice*$done++, $slice);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+=item delete
+
+Currently unimplemented. Set the "disabled" field instead.
+
+=cut
+
+sub delete {
+ return "Can't (yet?) delete service definitions.";
+# check & make sure the svcpart isn't in cust_svc or pkg_svc (in any packages)?
+}
+
+=item replace OLD_RECORD [ '1.3-COMPAT' [ , EXTRA_FIELDS_ARRAYREF [ , EXPORTNUMS_HASHREF [ , JOB ] ] ] ]
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+TODOC: 1.3-COMPAT
+
+TODOC: EXTRA_FIELDS_ARRAYREF (same as insert method)
+
+TODOC: JOB
+
+=cut
+
+sub replace {
+ my ( $new, $old ) = ( shift, shift );
+ my $compat = '';
+ my @fields = ();
+ my $exportnums;
+ my $job = '';
+ if ( @_ && $_[0] eq '1.3-COMPAT' ) {
+ shift;
+ $compat = '1.3';
+ @fields = @{shift(@_)} if @_;
+ $exportnums = @_ ? shift : '';
+ $job = shift if @_;
+ } else {
+ return 'non-1.3-COMPAT interface not yet written';
+ #not yet implemented
+ }
+
+ return "Can't change svcdb for an existing service definition!"
+ unless $old->svcdb eq $new->svcdb;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $new->SUPER::replace( $old );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $compat eq '1.3' ) {
+
+ # maintain part_svc_column records
+
+ my $svcdb = $new->svcdb;
+ foreach my $field (
+ grep { $_ ne 'svcnum'
+ && defined( $new->getfield($svcdb.'__'.$_.'_flag') )
+ } (fields($svcdb),@fields)
+ ) {
+ my $part_svc_column = $new->part_svc_column($field);
+ my $previous = qsearchs('part_svc_column', {
+ 'svcpart' => $new->svcpart,
+ 'columnname' => $field,
+ } );
+
+ my $flag = $new->getfield($svcdb.'__'.$field.'_flag');
+ #if ( uc($flag) =~ /^([DFMAX])$/ ) {
+ if ( uc($flag) =~ /^([A-Z])$/ ) { #part_svc_column will test it
+ my $parser = FS::part_svc->svc_table_fields($svcdb)->{$field}->{parse}
+ || sub { shift };
+ $part_svc_column->setfield('columnflag', $1);
+ $part_svc_column->setfield('columnvalue',
+ &$parser($new->getfield($svcdb.'__'.$field))
+ );
+ if ( $previous ) {
+ $error = $part_svc_column->replace($previous);
+ } else {
+ $error = $part_svc_column->insert;
+ }
+ } else {
+ $error = $previous ? $previous->delete : '';
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ # maintain export_svc records
+
+ if ( $exportnums ) {
+
+ #false laziness w/ edit/process/agent_type.cgi
+ my @new_export_svc = ();
+ foreach my $part_export ( qsearch('part_export', {}) ) {
+ my $exportnum = $part_export->exportnum;
+ my $hashref = {
+ 'exportnum' => $exportnum,
+ 'svcpart' => $new->svcpart,
+ };
+ my $export_svc = qsearchs('export_svc', $hashref);
+
+ if ( $export_svc && ! $exportnums->{$exportnum} ) {
+ $error = $export_svc->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ } elsif ( ! $export_svc && $exportnums->{$exportnum} ) {
+ push @new_export_svc, new FS::export_svc ( $hashref );
+ }
+
+ }
+
+ my $slice = 100/scalar(@new_export_svc) if @new_export_svc;
+ my $done = 0;
+ foreach my $export_svc (@new_export_svc) {
+ $error = $export_svc->insert($job, $slice*$done++, $slice);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ if ( $job ) {
+ $error = $job->update_statustext( int( $slice * $done ) );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ }
+
+ } else {
+ $dbh->rollback if $oldAutoCommit;
+ return 'non-1.3-COMPAT interface not yet written';
+ #not yet implemented
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+=item check
+
+Checks all fields to make sure this is a valid service definition. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error;
+ $error=
+ $self->ut_numbern('svcpart')
+ || $self->ut_text('svc')
+ || $self->ut_alpha('svcdb')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ ;
+ return $error if $error;
+
+ my @fields = eval { fields( $self->svcdb ) }; #might die
+ return "Unknown svcdb: ". $self->svcdb. " (Error: $@)"
+ unless @fields;
+
+ $self->SUPER::check;
+}
+
+=item part_svc_column COLUMNNAME
+
+Returns the part_svc_column object (see L<FS::part_svc_column>) for the given
+COLUMNNAME, or a new part_svc_column object if none exists.
+
+=cut
+
+sub part_svc_column {
+ my( $self, $columnname) = @_;
+ $self->svcpart &&
+ qsearchs('part_svc_column', {
+ 'svcpart' => $self->svcpart,
+ 'columnname' => $columnname,
+ }
+ ) or new FS::part_svc_column {
+ 'svcpart' => $self->svcpart,
+ 'columnname' => $columnname,
+ };
+}
+
+=item all_part_svc_column
+
+=cut
+
+sub all_part_svc_column {
+ my $self = shift;
+ qsearch('part_svc_column', { 'svcpart' => $self->svcpart } );
+}
+
+=item part_export [ EXPORTTYPE ]
+
+Returns a list of all exports (see L<FS::part_export>) for this service, or,
+if an export type is specified, only returns exports of the given type.
+
+=cut
+
+sub part_export {
+ my $self = shift;
+ my %search;
+ $search{'exporttype'} = shift if @_;
+ map { qsearchs('part_export', { 'exportnum' => $_->exportnum, %search } ) }
+ qsearch('export_svc', { 'svcpart' => $self->svcpart } );
+}
+
+=item part_export_usage
+
+Returns a list of any exports (see L<FS::part_export>) for this service that
+are capable of reporting usage information.
+
+=cut
+
+sub part_export_usage {
+ my $self = shift;
+ grep $_->can('usage_sessions'), $self->part_export;
+}
+
+=item part_export_did
+
+Returns a list of any exports (see L<FS::part_export>) for this service that
+are capable of returing available DID (phone number) information.
+
+=cut
+
+sub part_export_did {
+ my $self = shift;
+ grep $_->can('get_dids'), $self->part_export;
+}
+
+
+=item cust_svc [ PKGPART ]
+
+Returns a list of associated customer services (FS::cust_svc records).
+
+If a PKGPART is specified, returns the customer services which are contained
+within packages of that type (see L<FS::part_pkg>). If PKGPARTis specified as
+B<0>, returns unlinked customer services.
+
+=cut
+
+sub cust_svc {
+ my $self = shift;
+
+ my $hashref = { 'svcpart' => $self->svcpart };
+
+ my( $addl_from, $extra_sql ) = ( '', '' );
+ if ( @_ ) {
+ my $pkgpart = shift;
+ if ( $pkgpart =~ /^(\d+)$/ ) {
+ $addl_from = 'LEFT JOIN cust_pkg USING ( pkgnum )';
+ $extra_sql = "AND pkgpart = $1";
+ } elsif ( $pkgpart eq '0' ) {
+ $hashref->{'pkgnum'} = '';
+ }
+ }
+
+ qsearch({
+ 'table' => 'cust_svc',
+ 'addl_from' => $addl_from,
+ 'hashref' => $hashref,
+ 'extra_sql' => $extra_sql,
+ });
+}
+
+=item num_cust_svc [ PKGPART ]
+
+Returns the number of associated customer services (FS::cust_svc records).
+
+If a PKGPART is specified, returns the number of customer services which are
+contained within packages of that type (see L<FS::part_pkg>). If PKGPART
+is specified as B<0>, returns the number of unlinked customer services.
+
+=cut
+
+sub num_cust_svc {
+ my $self = shift;
+
+ my @param = ( $self->svcpart );
+
+ my( $join, $and ) = ( '', '' );
+ if ( @_ ) {
+ my $pkgpart = shift;
+ if ( $pkgpart ) {
+ $join = 'LEFT JOIN cust_pkg USING ( pkgnum )';
+ $and = 'AND pkgpart = ?';
+ push @param, $pkgpart;
+ } elsif ( $pkgpart eq '0' ) {
+ $and = 'AND pkgnum IS NULL';
+ }
+ }
+
+ my $sth = dbh->prepare(
+ "SELECT COUNT(*) FROM cust_svc $join WHERE svcpart = ? $and"
+ ) or die dbh->errstr;
+ $sth->execute(@param)
+ or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item svc_x
+
+Returns a list of associated FS::svc_* records.
+
+=cut
+
+sub svc_x {
+ my $self = shift;
+ map { $_->svc_x } $self->cust_svc;
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=cut
+
+my $svc_defs;
+sub _svc_defs {
+
+ return $svc_defs if $svc_defs; #cache
+
+ my $conf = new FS::Conf;
+
+ #false laziness w/part_pkg.pm::plan_info
+
+ my %info;
+ foreach my $INC ( @INC ) {
+ warn "globbing $INC/FS/svc_*.pm\n" if $DEBUG;
+ foreach my $file ( glob("$INC/FS/svc_*.pm") ) {
+
+ warn "attempting to load service table info from $file\n" if $DEBUG;
+ $file =~ /\/(\w+)\.pm$/ or do {
+ warn "unrecognized file in $INC/FS/: $file\n";
+ next;
+ };
+ my $mod = $1;
+
+ if ( $mod =~ /^svc_[A-Z]/ or $mod =~ /^svc_acct_pop$/ ) {
+ warn "skipping FS::$mod" if $DEBUG;
+ next;
+ }
+
+ eval "use FS::$mod;";
+ if ( $@ ) {
+ die "error using FS::$mod (skipping): $@\n" if $@;
+ next;
+ }
+ unless ( UNIVERSAL::can("FS::$mod", 'table_info') ) {
+ warn "FS::$mod has no table_info method; skipping";
+ next;
+ }
+
+ my $info = "FS::$mod"->table_info;
+ unless ( keys %$info ) {
+ warn "FS::$mod->table_info doesn't return info, skipping\n";
+ next;
+ }
+ warn "got info from FS::$mod: $info\n" if $DEBUG;
+ if ( exists($info->{'disabled'}) && $info->{'disabled'} ) {
+ warn "skipping disabled service FS::$mod" if $DEBUG;
+ next;
+ }
+ $info{$mod} = $info;
+ }
+ }
+
+ tie my %svc_defs, 'Tie::IxHash',
+ map { $_ => $info{$_}->{'fields'} }
+ sort { $info{$a}->{'display_weight'} <=> $info{$b}->{'display_weight'} }
+ keys %info,
+ ;
+
+ # yuck. maybe this won't be so bad when virtual fields become real fields
+ my %vfields;
+ foreach my $svcdb (grep dbdef->table($_), keys %svc_defs ) {
+ eval "use FS::$svcdb;";
+ my $self = "FS::$svcdb"->new;
+ $vfields{$svcdb} = {};
+ foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
+ my $pvf = $self->pvf($field);
+ my @list = $pvf->list;
+ if (scalar @list) {
+ $svc_defs{$svcdb}->{$field} = { desc => $pvf->label,
+ type => 'select',
+ select_list => \@list };
+ } else {
+ $svc_defs{$svcdb}->{$field} = $pvf->label;
+ } #endif
+ $vfields{$svcdb}->{$field} = $pvf;
+ warn "\$vfields{$svcdb}->{$field} = $pvf"
+ if $DEBUG;
+ } #next $field
+ } #next $svcdb
+
+ $svc_defs = \%svc_defs; #cache
+
+}
+
+=item svc_tables
+
+Returns a list of all svc_ tables.
+
+=cut
+
+sub svc_tables {
+ my $class = shift;
+ my $svc_defs = $class->_svc_defs;
+ grep { defined( dbdef->table($_) ) } keys %$svc_defs;
+}
+
+=item svc_table_fields TABLE
+
+Given a table name, returns a hashref of field names. The field names
+returned are those with additional (service-definition related) information,
+not necessarily all database fields of the table. Pseudo-fields may also
+be returned (i.e. svc_acct.usergroup).
+
+Each value of the hashref is another hashref, which can have one or more of
+the following keys:
+
+=over 4
+
+=item label - Description of the field
+
+=item def_label - Optional description of the field in the context of service definitions
+
+=item type - Currently "text", "select", "disabled", or "radius_usergroup_selector"
+
+=item disable_default - This field should not allow a default value in service definitions
+
+=item disable_fixed - This field should not allow a fixed value in service definitions
+
+=item disable_inventory - This field should not allow inventory values in service definitions
+
+=item select_list - If type is "text", this can be a listref of possible values.
+
+=item select_table - An alternative to select_list, this defines a database table with the possible choices.
+
+=item select_key - Used with select_table, this is the field name of keys
+
+=item select_label - Used with select_table, this is the field name of labels
+
+=back
+
+=cut
+
+#maybe this should move and be a class method in svc_Common.pm
+sub svc_table_fields {
+ my($class, $table) = @_;
+ my $svc_defs = $class->_svc_defs;
+ my $def = $svc_defs->{$table};
+
+ foreach ( grep !ref($def->{$_}), keys %$def ) {
+
+ #normalize the shortcut in %info hash
+ $def->{$_} = { 'label' => $def->{$_} };
+
+ $def->{$_}{'type'} ||= 'text';
+
+ }
+
+ $def;
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item process
+
+Job-queue processor for web interface adds/edits
+
+=cut
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process {
+ my $job = shift;
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ my $old = qsearchs('part_svc', { 'svcpart' => $param->{'svcpart'} })
+ if $param->{'svcpart'};
+
+ $param->{'svc_acct__usergroup'} =
+ ref($param->{'svc_acct__usergroup'})
+ ? join(',', @{$param->{'svc_acct__usergroup'}} )
+ : $param->{'svc_acct__usergroup'};
+
+ my $new = new FS::part_svc ( {
+ map {
+ $_ => $param->{$_};
+ # } qw(svcpart svc svcdb)
+ } ( fields('part_svc'),
+ map { my $svcdb = $_;
+ my @fields = fields($svcdb);
+ push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge
+
+ map {
+ if ( $param->{ $svcdb.'__'.$_.'_flag' } =~ /^[MA]$/ ) {
+ $param->{ $svcdb.'__'.$_ } =
+ delete( $param->{ $svcdb.'__'.$_.'_classnum' } );
+ }
+ if ( $param->{ $svcdb.'__'.$_.'_flag' } =~ /^S$/ ) {
+ $param->{ $svcdb.'__'.$_} =
+ ref($param->{ $svcdb.'__'.$_})
+ ? join(',', @{$param->{ $svcdb.'__'.$_ }} )
+ : $param->{ $svcdb.'__'.$_ };
+ }
+ ( $svcdb.'__'.$_, $svcdb.'__'.$_.'_flag' );
+ }
+ @fields;
+
+ } FS::part_svc->svc_tables()
+ )
+ } );
+
+ my %exportnums =
+ map { $_->exportnum => ( $param->{'exportnum'.$_->exportnum} || '') }
+ qsearch('part_export', {} );
+
+ my $error;
+ if ( $param->{'svcpart'} ) {
+ $error = $new->replace( $old,
+ '1.3-COMPAT',
+ [ 'usergroup' ],
+ \%exportnums,
+ $job
+ );
+ } else {
+ $error = $new->insert( [ 'usergroup' ],
+ \%exportnums,
+ $job,
+ );
+ $param->{'svcpart'} = $new->getfield('svcpart');
+ }
+
+ die "$error\n" if $error;
+}
+
+=item process_bulk_cust_svc
+
+Job-queue processor for web interface bulk customer service changes
+
+=cut
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process_bulk_cust_svc {
+ my $job = shift;
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ my $old_part_svc =
+ qsearchs('part_svc', { 'svcpart' => $param->{'old_svcpart'} } );
+
+ die "Must select a new service definition\n" unless $param->{'new_svcpart'};
+
+ #the rest should be abstracted out to to its own subroutine?
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ local( $FS::cust_svc::ignore_quantity ) = 1;
+
+ my $total = $old_part_svc->num_cust_svc( $param->{'pkgpart'} );
+
+ my $n = 0;
+ foreach my $old_cust_svc ( $old_part_svc->cust_svc( $param->{'pkgpart'} ) ) {
+
+ my $new_cust_svc = new FS::cust_svc { $old_cust_svc->hash };
+
+ $new_cust_svc->svcpart( $param->{'new_svcpart'} );
+ my $error = $new_cust_svc->replace($old_cust_svc);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die "$error\n" if $error;
+ }
+
+ $error = $job->update_statustext( int( 100 * ++$n / $total ) );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die $error if $error;
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=head1 BUGS
+
+Delete is unimplemented.
+
+The list of svc_* tables is no longer hardcoded, but svc_acct_pop is skipped
+as a special case until it is renamed.
+
+all_part_svc_column methods should be documented
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::part_svc_column>, L<FS::part_pkg>, L<FS::pkg_svc>,
+L<FS::cust_svc>, L<FS::svc_acct>, L<FS::svc_forward>, L<FS::svc_domain>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_svc_column.pm b/FS/FS/part_svc_column.pm
new file mode 100644
index 0000000..d2b8fd9
--- /dev/null
+++ b/FS/FS/part_svc_column.pm
@@ -0,0 +1,120 @@
+package FS::part_svc_column;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( fields );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::part_svc_column - Object methods for part_svc_column objects
+
+=head1 SYNOPSIS
+
+ use FS::part_svc_column;
+
+ $record = new FS::part_svc_column \%hash
+ $record = new FS::part_svc_column { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_svc_column record represents a service definition column
+constraint. FS::part_svc_column inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item columnnum - primary key (assigned automatcially for new records)
+
+=item svcpart - service definition (see L<FS::part_svc>)
+
+=item columnname - column name in part_svc.svcdb table
+
+=item columnvalue - default or fixed value for the column
+
+=item columnflag - null or empty (no default), `D' for default, `F' for fixed (unchangeable), `S' for selectable choice, `M' for manual selection from inventory, or `A' for automatic selection from inventory. For virtual fields, can also be 'X' for excluded.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new column constraint. To add the column constraint to the database, see L<"insert">.
+
+=cut
+
+sub table { 'part_svc_column'; }
+
+=item insert
+
+Adds this service definition to the database. If there is an error, returns
+the error, otherwise returns false.
+
+=item delete
+
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('columnnum')
+ || $self->ut_number('svcpart')
+ || $self->ut_alpha('columnname')
+ || $self->ut_anything('columnvalue')
+ ;
+ return $error if $error;
+
+ $self->columnflag =~ /^([DFSMAX])$/
+ or return "illegal columnflag ". $self->columnflag;
+ $self->columnflag(uc($1));
+
+ if ( $self->columnflag =~ /^[MA]$/ ) {
+ $error =
+ $self->ut_foreign_key( 'columnvalue', 'inventory_class', 'classnum' );
+ return $error if $error;
+ }
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::part_svc>, L<FS::part_pkg>, L<FS::pkg_svc>,
+L<FS::cust_svc>, L<FS::svc_acct>, L<FS::svc_forward>, L<FS::svc_domain>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_svc_router.pm b/FS/FS/part_svc_router.pm
new file mode 100755
index 0000000..df04cc9
--- /dev/null
+++ b/FS/FS/part_svc_router.pm
@@ -0,0 +1,33 @@
+package FS::part_svc_router;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw(qsearchs);
+use FS::router;
+use FS::part_svc;
+
+@ISA = qw(FS::Record);
+
+sub table { 'part_svc_router'; }
+
+sub check {
+ my $self = shift;
+ my $error =
+ $self->ut_numbern('svcrouternum')
+ || $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
+ || $self->ut_foreign_key('routernum', 'router', 'routernum');
+ return $error if $error;
+ ''; #no error
+}
+
+sub router {
+ my $self = shift;
+ return qsearchs('router', { routernum => $self->routernum });
+}
+
+sub part_svc {
+ my $self = shift;
+ return qsearchs('part_svc', { svcpart => $self->svcpart });
+}
+
+1;
diff --git a/FS/FS/part_virtual_field.pm b/FS/FS/part_virtual_field.pm
new file mode 100755
index 0000000..f5a4161
--- /dev/null
+++ b/FS/FS/part_virtual_field.pm
@@ -0,0 +1,301 @@
+package FS::part_virtual_field;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record;
+use FS::Schema qw( dbdef );
+use CGI qw(escapeHTML);
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::part_virtual_field - Object methods for part_virtual_field records
+
+=head1 SYNOPSIS
+
+ use FS::part_virtual_field;
+
+ $record = new FS::part_virtual_field \%hash;
+ $record = new FS::part_virtual_field { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_virtual_field object represents the definition of a virtual field
+(see the BACKGROUND section). FS::part_virtual_field contains the name and
+base table of the field, as well as validation rules and UI hints about the
+display of the field. The actual data is stored in FS::virtual_field; see
+its manpage for details.
+
+FS::part_virtual_field inherits from FS::Record. The following fields are
+currently supported:
+
+=over 2
+
+=item vfieldpart - primary key (assigned automatically)
+
+=item name - name of the field
+
+=item dbtable - table for which this virtual field is defined
+
+=item check_block - Perl code to validate/normalize data
+
+=item list_source - Perl code to generate a list of values (UI hint)
+
+=item length - expected length of the value (UI hint)
+
+=item label - descriptive label for the field (UI hint)
+
+=item sequence - sort key (UI hint; unimplemented)
+
+=back
+
+=head1 BACKGROUND
+
+"Form is none other than emptiness,
+ and emptiness is none other than form."
+-- Heart Sutra
+
+The virtual field mechanism allows site admins to make trivial changes to
+the Freeside database schema without modifying the code. Specifically, the
+user can add custom-defined 'fields' to the set of data tracked by Freeside
+about objects such as customers and services. These fields are not associated
+with any logic in the core Freeside system, but may be referenced in peripheral
+code such as exports, price calculations, or alternate interfaces, or may just
+be stored in the database for future reference.
+
+This system was originally devised for svc_broadband, which (by necessity)
+comprises such a wide range of access technologies that no static set of fields
+could contain all the information needed by the exports. In an appalling
+display of False Laziness, a parallel mechanism was implemented for the
+router table, to store properties such as passwords to configure routers.
+
+The original system treated svc_broadband custom fields (sb_fields) as records
+in a completely separate table. Any code that accessed or manipulated these
+fields had to be aware that they were I<not> fields in svc_broadband, but
+records in sb_field. For example, code that inserted a svc_broadband with
+several custom fields had to create an FS::svc_broadband object, call its
+insert() method, and then create several FS::sb_field objects and call I<their>
+insert() methods.
+
+This created a problem for exports. The insert method on any FS::svc_Common
+object (including svc_broadband) automatically triggers exports after the
+record has been inserted. However, at this point, the sb_fields had not yet
+been inserted, so the export could not rely on their presence, which was the
+original purpose of sb_fields.
+
+Hence the new system. Virtual fields are appended to the field list of every
+record at the FS::Record level, whether the object is created ex nihilo with
+new() or fetched with qsearch(). The fields() method now returns a list of
+both real and virtual fields. The insert(), replace(), and delete() methods
+now update both the base table and the virtual fields, in a single transaction.
+
+A new method is provided, virtual_fields(), which gives only the virtual
+fields. UI code that dynamically generates form widgets to edit virtual field
+data should use this to figure out what fields are defined. (See below.)
+
+Subclasses may override virtual_fields() to restrict the set of virtual
+fields available. Some discipline and sanity on the part of the programmer
+are required; in particular, this function should probably not depend on any
+fields in the record other than the primary key, since the others may change
+after the object is instantiated. (Making it depend on I<virtual> fields is
+just asking for pain.) One use of this is seen in FS::svc_Common; another
+possibility is field-level access control based on FS::UID::getotaker().
+
+As a trivial case, a subclass may opt out of supporting virtual fields with
+the following code:
+
+sub virtual_fields { () }
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Create a new record. To add the record to the database, see "insert".
+
+=cut
+
+sub table { 'part_virtual_field'; }
+sub virtual_fields { () }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+If there is an error, returns the error, otherwise returns false.
+Called by the insert and replace methods.
+
+=back
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error = $self->ut_text('name') ||
+ $self->ut_text('dbtable') ||
+ $self->ut_number('length')
+ ;
+ return $error if $error;
+
+ # Make sure it's a real table with a numeric primary key
+ my ($table, $pkey);
+ if($table = dbdef->table($self->dbtable)) {
+ if($pkey = $table->primary_key) {
+ if($table->column($pkey)->type =~ /int/i) {
+ # this is what it should be
+ } else {
+ $error = "$table.$pkey is not an integer";
+ }
+ } else {
+ $error = "$table does not have a single-field primary key";
+ }
+ } else {
+ $error = "$table does not exist in the schema";
+ }
+ return $error if $error;
+
+ # Possibly some sanity checks for check_block and list_source?
+
+ $self->SUPER::check;
+}
+
+=item list
+
+Evaluates list_source.
+
+=cut
+
+sub list {
+ my $self = shift;
+ return () unless $self->list_source;
+
+ my @opts = eval($self->list_source);
+ if($@) {
+ warn $@;
+ return ();
+ } else {
+ return @opts;
+ }
+}
+
+=item widget UI_TYPE MODE [ VALUE ]
+
+Generates UI code for a widget suitable for editing/viewing the field, based on
+list_source and length.
+
+The only UI_TYPE currently supported is 'HTML', and the only MODE is 'view'.
+Others will be added later.
+
+In HTML, all widgets are assumed to be table rows. View widgets look like
+<TR><TD ALIGN="right">Label</TD><TD BGCOLOR="#ffffff">Value</TD></TR>
+
+(Most of the display style stuff, such as the colors, should probably go into
+a separate module specific to the UI. That can wait, though. The API for
+this function won't change.)
+
+VALUE (optional) is the current value of the field.
+
+=cut
+
+sub widget {
+ my $self = shift;
+ my ($ui_type, $mode, $value) = @_;
+ my $text;
+ my $label = $self->label || $self->name;
+
+ if ($ui_type eq 'HTML') {
+ if ($mode eq 'view') {
+ $text = q!<TR><TD ALIGN="right">! . $label .
+ q!</TD><TD BGCOLOR="#ffffff">! . $value .
+ q!</TD></TR>! . "\n";
+ } elsif ($mode eq 'edit') {
+ $text = q!<TR><TD ALIGN="right">! . $label .
+ q!</TD><TD>!;
+ if ($self->list_source) {
+ $text .= q!<SELECT NAME="! . $self->name .
+ q!" SIZE=1>! . "\n";
+ foreach ($self->list) {
+ $text .= q!<OPTION VALUE="! . $_ . q!"!;
+ $text .= ' SELECTED' if ($_ eq $value);
+ $text .= '>' . $_ . '</OPTION>' . "\n";
+ }
+ } else {
+ $text .= q!<INPUT NAME="! . $self->name .
+ q!" VALUE="! . escapeHTML($value) . q!"!;
+ if ($self->length) {
+ $text .= q! SIZE="! . $self->length . q!"!;
+ }
+ $text .= '>';
+ }
+ $text .= q!</TD></TR>! . "\n";
+ } else {
+ return '';
+ }
+ } else {
+ return '';
+ }
+ return $text;
+}
+
+=head1 NOTES
+
+=head2 Semantics of check_block:
+
+This has been changed from the sb_field implementation to make check_blocks
+simpler and more natural to Perl programmers who work on things other than
+Freeside.
+
+The check_block is eval'd with the (proposed) new value of the field in $_,
+and the object to be updated in $self. Its return value is ignored. The
+check_block may change the value of $_ to override the proposed value, or
+call die() (with an appropriate error message) to reject the update entirely;
+the error string will be returned as the output of the check() method.
+
+This makes check_blocks like
+
+C<s/foo/bar/>
+
+do what you expect.
+
+The check_block is expected NOT to do anything freaky to $self, like modifying
+other fields or calling $self->check(). You have been warned.
+
+(FIXME: Rewrite some of the warnings from part_sb_field and insert here.)
+
+=head1 BUGS
+
+None. It's absolutely falwless.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::virtual_field>
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/pay_batch.pm b/FS/FS/pay_batch.pm
new file mode 100644
index 0000000..5448b03
--- /dev/null
+++ b/FS/FS/pay_batch.pm
@@ -0,0 +1,538 @@
+package FS::pay_batch;
+
+use strict;
+use vars qw( @ISA );
+use Time::Local;
+use Text::CSV_XS;
+use FS::Record qw( dbh qsearch qsearchs );
+use FS::cust_pay;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::pay_batch - Object methods for pay_batch records
+
+=head1 SYNOPSIS
+
+ use FS::pay_batch;
+
+ $record = new FS::pay_batch \%hash;
+ $record = new FS::pay_batch { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::pay_batch object represents an payment batch. FS::pay_batch inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item batchnum - primary key
+
+=item payby - CARD or CHEK
+
+=item status - O (Open), I (In-transit), or R (Resolved)
+
+=item download -
+
+=item upload -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new batch. To add the batch to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'pay_batch'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid batch. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('batchnum')
+ || $self->ut_enum('payby', [ 'CARD', 'CHEK' ])
+ || $self->ut_enum('status', [ 'O', 'I', 'R' ])
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item rebalance
+
+=cut
+
+sub rebalance {
+ my $self = shift;
+}
+
+=item set_status
+
+=cut
+
+sub set_status {
+ my $self = shift;
+ $self->status(shift);
+ $self->download(time)
+ if $self->status eq 'I' && ! $self->download;
+ $self->upload(time)
+ if $self->status eq 'R' && ! $self->upload;
+ $self->replace();
+}
+
+=item import_results OPTION => VALUE, ...
+
+Import batch results.
+
+Options are:
+
+I<filehandle> - open filehandle of results file.
+
+I<format> - "csv-td_canada_trust-merchant_pc_batch", "csv-chase_canada-E-xactBatch", "ach-spiritone", or "PAP"
+
+=cut
+
+sub import_results {
+ my $self = shift;
+
+ my $param = ref($_[0]) ? shift : { @_ };
+ my $fh = $param->{'filehandle'};
+ my $format = $param->{'format'};
+
+ my $filetype; # CSV, Fixed80, Fixed264
+ my @fields;
+ my $formatre; # for Fixed.+
+ my @values;
+ my $begin_condition;
+ my $end_condition;
+ my $end_hook;
+ my $hook;
+ my $approved_condition;
+ my $declined_condition;
+
+ if ( $format eq 'csv-td_canada_trust-merchant_pc_batch' ) {
+
+ $filetype = "CSV";
+
+ @fields = (
+ 'paybatchnum', # Reference#: Invoice number of the transaction
+ 'paid', # Amount: Amount of the transaction. Dollars and cents
+ # with no decimal entered.
+ '', # Card Type: 0 - MCrd, 1 - Visa, 2 - AMEX, 3 - Discover,
+ # 4 - Insignia, 5 - Diners/EnRoute, 6 - JCB
+ '_date', # Transaction Date: Date the Transaction was processed
+ 'time', # Transaction Time: Time the transaction was processed
+ 'payinfo', # Card Number: Card number for the transaction
+ '', # Expiry Date: Expiry date of the card
+ '', # Auth#: Authorization number entered for force post
+ # transaction
+ 'type', # Transaction Type: 0 - purchase, 40 - refund,
+ # 20 - force post
+ 'result', # Processing Result: 3 - Approval,
+ # 4 - Declined/Amount over limit,
+ # 5 - Invalid/Expired/stolen card,
+ # 6 - Comm Error
+ '', # Terminal ID: Terminal ID used to process the transaction
+ );
+
+ $end_condition = sub {
+ my $hash = shift;
+ $hash->{'type'} eq '0BC';
+ };
+
+ $end_hook = sub {
+ my( $hash, $total) = @_;
+ $total = sprintf("%.2f", $total);
+ my $batch_total = sprintf("%.2f", $hash->{'paybatchnum'} / 100 );
+ return "Our total $total does not match bank total $batch_total!"
+ if $total != $batch_total;
+ '';
+ };
+
+ $hook = sub {
+ my $hash = shift;
+ $hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} / 100 );
+ $hash->{'_date'} = timelocal( substr($hash->{'time'}, 4, 2),
+ substr($hash->{'time'}, 2, 2),
+ substr($hash->{'time'}, 0, 2),
+ substr($hash->{'_date'}, 6, 2),
+ substr($hash->{'_date'}, 4, 2)-1,
+ substr($hash->{'_date'}, 0, 4)-1900, );
+ };
+
+ $approved_condition = sub {
+ my $hash = shift;
+ $hash->{'type'} eq '0' && $hash->{'result'} == 3;
+ };
+
+ $declined_condition = sub {
+ my $hash = shift;
+ $hash->{'type'} eq '0' && ( $hash->{'result'} == 4
+ || $hash->{'result'} == 5 );
+ };
+
+
+ }elsif ( $format eq 'csv-chase_canada-E-xactBatch' ) {
+
+ $filetype = "CSV";
+
+ @fields = (
+ '', # Internal(bank) id of the transaction
+ '', # Transaction Type: 00 - purchase, 01 - preauth,
+ # 02 - completion, 03 - forcepost,
+ # 04 - refund, 05 - auth,
+ # 06 - purchase corr, 07 - refund corr,
+ # 08 - void 09 - void return
+ '', # gateway used to process this transaction
+ 'paid', # Amount: Amount of the transaction. Dollars and cents
+ # with decimal entered.
+ 'auth', # Auth#: Authorization number (if approved)
+ 'payinfo', # Card Number: Card number for the transaction
+ '', # Expiry Date: Expiry date of the card
+ '', # Cardholder Name
+ 'bankcode', # Bank response code (3 alphanumeric)
+ 'bankmess', # Bank response message
+ 'etgcode', # ETG response code (2 alphanumeric)
+ 'etgmess', # ETG response message
+ '', # Returned customer number for the transaction
+ 'paybatchnum', # Reference#: paybatch number of the transaction
+ '', # Reference#: Invoice number of the transaction
+ 'result', # Processing Result: Approved of Declined
+ );
+
+ $end_condition = sub {
+ '';
+ };
+
+ $hook = sub {
+ my $hash = shift;
+ my $cpb = shift;
+ $hash->{'paid'} = sprintf("%.2f", $hash->{'paid'}); #hmmmm
+ $hash->{'_date'} = time; # got a better one?
+ $hash->{'payinfo'} = $cpb->{'payinfo'}
+ if( substr($hash->{'payinfo'}, -4) eq substr($cpb->{'payinfo'}, -4) );
+ };
+
+ $approved_condition = sub {
+ my $hash = shift;
+ $hash->{'etgcode'} eq '00' && $hash->{'result'} eq "Approved";
+ };
+
+ $declined_condition = sub {
+ my $hash = shift;
+ $hash->{'etgcode'} ne '00' # internal processing error
+ || ( $hash->{'result'} eq "Declined" );
+ };
+
+
+ }elsif ( $format eq 'PAP' ) {
+
+ $filetype = "Fixed264";
+
+ @fields = (
+ 'recordtype', # We are interested in the 'D' or debit records
+ 'batchnum', # Record#: batch number we used when sending the file
+ 'datacenter', # Where in the bowels of the bank the data was processed
+ 'paid', # Amount: Amount of the transaction. Dollars and cents
+ # with no decimal entered.
+ '_date', # Transaction Date: Date the Transaction was processed
+ 'bank', # Routing information
+ 'payinfo', # Account number for the transaction
+ 'paybatchnum', # Reference#: Invoice number of the transaction
+ );
+
+ $formatre = '^(.).{19}(.{4})(.{3})(.{10})(.{6})(.{9})(.{12}).{110}(.{19}).{71}$';
+
+ $end_condition = sub {
+ my $hash = shift;
+ $hash->{'recordtype'} eq 'W';
+ };
+
+ $end_hook = sub {
+ my( $hash, $total) = @_;
+ $total = sprintf("%.2f", $total);
+ my $batch_total = $hash->{'datacenter'}.$hash->{'paid'}.
+ substr($hash->{'_date'},0,1); # YUCK!
+ $batch_total = sprintf("%.2f", $batch_total / 100 );
+ return "Our total $total does not match bank total $batch_total!"
+ if $total != $batch_total;
+ '';
+ };
+
+ $hook = sub {
+ my $hash = shift;
+ $hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} / 100 );
+ my $tmpdate = timelocal( 0,0,1,1,0,substr($hash->{'_date'}, 0, 3)+2000);
+ $tmpdate += 86400*(substr($hash->{'_date'}, 3, 3)-1) ;
+ $hash->{'_date'} = $tmpdate;
+ $hash->{'payinfo'} = $hash->{'payinfo'} . '@' . $hash->{'bank'};
+ };
+
+ $approved_condition = sub {
+ 1;
+ };
+
+ $declined_condition = sub {
+ 0;
+ };
+
+ }elsif ( $format eq 'ach-spiritone' ) {
+
+ $filetype = "CSV";
+
+ @fields = (
+ '', # Name
+ 'paybatchnum', # ID: Number of the transaction
+ 'aba', # ABA Number for the transaction
+ 'payinfo', # Bank Account Number for the transaction
+ '', # Transaction Type: 27 - debit
+ 'paid', # Amount: Amount of the transaction. Dollars and cents
+ # with decimal entered.
+ '', # Default Transaction Type
+ '', # Default Amount: Dollars and cents with decimal entered.
+ );
+
+ $end_condition = sub {
+ '';
+ };
+
+ $hook = sub {
+ my $hash = shift;
+ $hash->{'_date'} = time; # got a better one?
+ $hash->{'payinfo'} = $hash->{'payinfo'} . '@' . $hash->{'aba'};
+ };
+
+ $approved_condition = sub {
+ 1;
+ };
+
+ $declined_condition = sub {
+ 0;
+ };
+
+
+ } else {
+ return "Unknown format $format";
+ }
+
+ my $csv = new Text::CSV_XS;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $reself = $self->select_for_update;
+
+ unless ( $reself->status eq 'I' ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "batchnum ". $self->batchnum. "no longer in transit";
+ };
+
+ my $error = $self->set_status('R');
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error
+ }
+
+ my $total = 0;
+ my $line;
+ while ( defined($line=<$fh>) ) {
+
+ next if $line =~ /^\s*$/; #skip blank lines
+
+ if ($filetype eq "CSV") {
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+ @values = $csv->fields();
+ }elsif ($filetype eq "Fixed80" || $filetype eq "Fixed264"){
+ @values = $line =~ /$formatre/;
+ unless (@values) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $line;
+ };
+ }else{
+ $dbh->rollback if $oldAutoCommit;
+ return "Unknown file type $filetype";
+ }
+
+ my %hash;
+ foreach my $field ( @fields ) {
+ my $value = shift @values;
+ next unless $field;
+ $hash{$field} = $value;
+ }
+
+ if ( &{$end_condition}(\%hash) ) {
+ my $error = &{$end_hook}(\%hash, $total);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ last;
+ }
+
+ my $cust_pay_batch =
+ qsearchs('cust_pay_batch', { 'paybatchnum' => $hash{'paybatchnum'}+0 } );
+ unless ( $cust_pay_batch ) {
+ return "unknown paybatchnum $hash{'paybatchnum'}\n";
+ }
+ my $custnum = $cust_pay_batch->custnum,
+ my $payby = $cust_pay_batch->payby,
+
+ my $new_cust_pay_batch = new FS::cust_pay_batch { $cust_pay_batch->hash };
+
+ &{$hook}(\%hash, $cust_pay_batch->hashref);
+
+ if ( &{$approved_condition}(\%hash) ) {
+
+ $new_cust_pay_batch->status('Approved');
+
+ } elsif ( &{$declined_condition}(\%hash) ) {
+
+ $new_cust_pay_batch->status('Declined');
+
+ }
+
+ my $error = $new_cust_pay_batch->replace($cust_pay_batch);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error updating status of paybatchnum $hash{'paybatchnum'}: $error\n";
+ }
+
+ if ( $new_cust_pay_batch->status =~ /Approved/i ) {
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $custnum,
+ 'payby' => $payby,
+ 'paybatch' => $self->batchnum,
+ map { $_ => $hash{$_} } (qw( paid _date payinfo )),
+ } );
+ $error = $cust_pay->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error adding payment paybatchnum $hash{'paybatchnum'}: $error\n";
+ }
+ $total += $hash{'paid'};
+
+ $cust_pay->cust_main->apply_payments;
+
+ } elsif ( $new_cust_pay_batch->status =~ /Declined/i ) {
+
+ #false laziness w/cust_main::collect
+
+ my $due_cust_event = $new_cust_pay_batch->cust_main->due_cust_event(
+ #'check_freq' => '1d', #?
+ 'eventtable' => 'cust_pay_batch',
+ 'objects' => [ $new_cust_pay_batch ],
+ );
+ unless( ref($due_cust_event) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $due_cust_event;
+ }
+
+ foreach my $cust_event ( @$due_cust_event ) {
+
+ #XXX lock event
+
+ #re-eval event conditions (a previous event could have changed things)
+ next unless $cust_event->test_conditions;
+
+ if ( my $error = $cust_event->do_event() ) {
+ # gah, even with transactions.
+ #$dbh->commit if $oldAutoCommit; #well.
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ }
+
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=back
+
+=head1 BUGS
+
+status is somewhat redundant now that download and upload exist
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/payby.pm b/FS/FS/payby.pm
new file mode 100644
index 0000000..b54e5d9
--- /dev/null
+++ b/FS/FS/payby.pm
@@ -0,0 +1,194 @@
+package FS::payby;
+
+use strict;
+use vars qw(%hash %payby2bop);
+use Tie::IxHash;
+use Business::CreditCard;
+
+
+=head1 NAME
+
+FS::payby - Object methods for payment type records
+
+=head1 SYNOPSIS
+
+ use FS::payby;
+
+ #for now...
+
+ my @payby = FS::payby->payby;
+
+ my $bool = FS::payby->can_payby('cust_main', 'CARD');
+
+ tie my %payby, 'Tie::IxHash', FS::payby->payby2longname
+
+ my @cust_payby = FS::payby->cust_payby;
+
+ tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2longname
+
+=head1 DESCRIPTION
+
+Payment types.
+
+=head1 METHODS
+
+=over 4
+
+=item
+
+=cut
+
+# paybys can be any/all of:
+# - a customer payment type (cust_main.payby)
+# - a payment or refund type (cust_pay.payby, cust_pay_batch.payby, cust_refund.payby)
+# - an event type (part_bill_event.payby)
+
+tie %hash, 'Tie::IxHash',
+ 'CARD' => {
+ tinyname => 'card',
+ shortname => 'Credit card',
+ longname => 'Credit card (automatic)',
+ },
+ 'DCRD' => {
+ tinyname => 'card',
+ shortname => 'Credit card',
+ longname => 'Credit card (on-demand)',
+ cust_pay => 'CARD', #this is a customer type only, payments are CARD...
+ },
+ 'CHEK' => {
+ tinyname => 'check',
+ shortname => 'Electronic check',
+ longname => 'Electronic check (automatic)',
+ },
+ 'DCHK' => {
+ tinyname => 'check',
+ shortname => 'Electronic check',
+ longname => 'Electronic check (on-demand)',
+ cust_pay => 'CHEK', #this is a customer type only, payments are CHEK...
+ },
+ 'LECB' => {
+ tinyname => 'phone bill',
+ shortname => 'Phone bill billing',
+ longname => 'Phone bill billing',
+ },
+ 'BILL' => {
+ tinyname => 'billing',
+ shortname => 'Billing',
+ payname => 'Check',
+ longname => 'Billing',
+ },
+ 'PREP' => {
+ tinyname => 'prepaid card',
+ shortname => 'Prepaid card',
+ longname => 'Prepaid card',
+ cust_main => 'BILL', #this is a payment type only, customers go to BILL...
+ },
+ 'CASH' => {
+ tinyname => 'cash',
+ shortname => 'Cash', # initial payment, then billing
+ longname => 'Cash',
+ cust_main => 'BILL', #this is a payment type only, customers go to BILL...
+ },
+ 'WEST' => {
+ tinyname => 'western union',
+ shortname => 'Western Union', # initial payment, then billing
+ longname => 'Western Union',
+ cust_main => 'BILL', #this is a payment type only, customers go to BILL...
+ },
+ 'MCRD' => { #not the same as DCRD
+ tinyname => 'card',
+ shortname => 'Manual credit card', # initial payment, then billing
+ longname => 'Manual credit card',
+ cust_main => 'BILL', #this is a payment type only, customers go to BILL...
+ },
+ 'COMP' => {
+ tinyname => 'comp',
+ shortname => 'Complimentary',
+ longname => 'Complimentary',
+ cust_pay => '', # (free) is depricated as a payment type in cust_pay
+ },
+ 'CBAK' => {
+ tinyname => 'chargeback',
+ shortname => 'Chargeback',
+ longname => 'Chargeback',
+ cust_main => '', # not a customer type
+ },
+;
+
+sub payby {
+ keys %hash;
+}
+
+sub can_payby {
+ my( $self, $table, $payby ) = @_;
+
+ #return "Illegal payby" unless $hash{$payby};
+ return 0 unless $hash{$payby};
+
+ $table = 'cust_pay' if $table =~ /^cust_(pay_pending|pay_batch|pay_void|refund)$/;
+ return 0 if exists( $hash{$payby}->{$table} );
+
+ return 1;
+}
+
+sub payby2longname {
+ my $self = shift;
+ map { $_ => $hash{$_}->{longname} } $self->payby;
+}
+
+sub shortname {
+ my( $self, $payby ) = @_;
+ $hash{$payby}->{shortname};
+}
+
+sub payname {
+ my( $self, $payby ) = @_;
+ #$hash{$payby}->{payname} || $hash{$payby}->{shortname};
+ exists($hash{$payby}->{payname})
+ ? $hash{$payby}->{payname}
+ : $hash{$payby}->{shortname};
+}
+
+sub longname {
+ my( $self, $payby ) = @_;
+ $hash{$payby}->{longname};
+}
+
+%payby2bop = (
+ 'CARD' => 'CC',
+ 'CHEK' => 'ECHECK',
+);
+
+sub payby2bop {
+ my( $self, $payby ) = @_;
+ $payby2bop{ $self->payby2payment($payby) };
+}
+
+sub payby2payment {
+ my( $self, $payby ) = @_;
+ $hash{$payby}{'cust_pay'} || $payby;
+}
+
+sub cust_payby {
+ my $self = shift;
+ grep { ! exists $hash{$_}->{cust_main} } $self->payby;
+}
+
+sub cust_payby2longname {
+ my $self = shift;
+ map { $_ => $hash{$_}->{longname} } $self->cust_payby;
+}
+
+=back
+
+=head1 BUGS
+
+This should eventually be an actual database table, and all tables that
+currently have a char payby field should have a foreign key into here instead.
+
+=head1 SEE ALSO
+
+=cut
+
+1;
+
diff --git a/FS/FS/payinfo_Mixin.pm b/FS/FS/payinfo_Mixin.pm
new file mode 100644
index 0000000..99cca6a
--- /dev/null
+++ b/FS/FS/payinfo_Mixin.pm
@@ -0,0 +1,290 @@
+package FS::payinfo_Mixin;
+
+use strict;
+use Business::CreditCard;
+use FS::payby;
+
+=head1 NAME
+
+FS::payinfo_Mixin - Mixin class for records in tables that contain payinfo.
+
+=head1 SYNOPSIS
+
+package FS::some_table;
+use vars qw(@ISA);
+@ISA = qw( FS::payinfo_Mixin FS::Record );
+
+=head1 DESCRIPTION
+
+This is a mixin class for records that contain payinfo.
+
+This class handles the following functions for payinfo...
+
+Payment Mask (Generation and Storage)
+Data Validation (parent checks need to be sure to call this)
+Pretty printing
+
+=head1 FIELDS
+
+=over 4
+
+=item payby
+
+The following payment types (payby) are supported:
+
+For Customers (cust_main):
+'CARD' (credit card - automatic), 'DCRD' (credit card - on-demand),
+'CHEK' (electronic check - automatic), 'DCHK' (electronic check - on-demand),
+'LECB' (Phone bill billing), 'BILL' (billing), 'COMP' (free), or
+'PREPAY' (special billing type: applies a credit and sets billing type to I<BILL> - see L<FS::prepay_credit>)
+
+For Refunds (cust_refund):
+'CARD' (credit cards), 'CHEK' (electronic check/ACH),
+'LECB' (Phone bill billing), 'BILL' (billing), 'CASH' (cash),
+'WEST' (Western Union), 'MCRD' (Manual credit card), 'CBAK' Chargeback, or 'COMP' (free)
+
+
+For Payments (cust_pay):
+'CARD' (credit cards), 'CHEK' (electronic check/ACH),
+'LECB' (phone bill billing), 'BILL' (billing), 'PREP' (prepaid card),
+'CASH' (cash), 'WEST' (Western Union), or 'MCRD' (Manual credit card)
+'COMP' (free) is depricated as a payment type in cust_pay
+
+=cut
+
+# was this supposed to do something?
+
+#sub payby {
+# my($self,$payby) = @_;
+# if ( defined($payby) ) {
+# $self->setfield('payby', $payby);
+# }
+# return $self->getfield('payby')
+#}
+
+=item payinfo
+
+Payment information (payinfo) can be one of the following types:
+
+Card Number, P.O., comp issuer (4-8 lowercase alphanumerics; think username) or prepayment identifier (see L<FS::prepay_credit>)
+
+=cut
+
+sub payinfo {
+ my($self,$payinfo) = @_;
+ if ( defined($payinfo) ) {
+ $self->setfield('payinfo', $payinfo); # This is okay since we are the 'setter'
+ $self->paymask($self->mask_payinfo());
+ } else {
+ $payinfo = $self->getfield('payinfo'); # This is okay since we are the 'getter'
+ return $payinfo;
+ }
+}
+
+=item paycvv
+
+Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
+
+=cut
+
+sub paycvv {
+ my($self,$paycvv) = @_;
+ # This is only allowed in cust_main... Even then it really shouldn't be stored...
+ if ($self->table eq 'cust_main') {
+ if ( defined($paycvv) ) {
+ $self->setfield('paycvv', $paycvv); # This is okay since we are the 'setter'
+ } else {
+ $paycvv = $self->getfield('paycvv'); # This is okay since we are the 'getter'
+ return $paycvv;
+ }
+ } else {
+# warn "This doesn't work for other tables besides cust_main
+ '';
+ }
+}
+
+=item paymask
+
+=cut
+
+sub paymask {
+ my($self, $paymask) = @_;
+
+ if ( defined($paymask) && $paymask ne '' ) {
+ # I hate this little bit of magic... I don't expect it to cause a problem,
+ # but who knows... If the payinfo is passed in masked then ignore it and
+ # set it based on the payinfo. The only guy that should call this in this
+ # way is... $self->payinfo
+ $self->setfield('paymask', $self->mask_payinfo());
+
+ } else {
+
+ $paymask=$self->getfield('paymask');
+ if (!defined($paymask) || $paymask eq '') {
+ # Generate it if it's blank - Note that we're not going to set it - just
+ # generate
+ $paymask = $self->mask_payinfo();
+ }
+
+ }
+
+ return $paymask;
+}
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item mask_payinfo [ PAYBY, PAYINFO ]
+
+This method converts the payment info (credit card, bank account, etc.) into a
+masked string.
+
+Optionally, an arbitrary payby and payinfo can be passed.
+
+=cut
+
+sub mask_payinfo {
+ my $self = shift;
+ my $payby = scalar(@_) ? shift : $self->payby;
+ my $payinfo = scalar(@_) ? shift : $self->payinfo;
+
+ # Check to see if it's encrypted...
+ my $paymask;
+ if ( $self->is_encrypted($payinfo) ) {
+ $paymask = 'N/A';
+ } else {
+ # if not, mask it...
+ if ($payby eq 'CARD' || $payby eq 'DCRD' || $payby eq 'MCRD') {
+ # Credit Cards
+ my $conf = new FS::Conf;
+ my $mask_method = $conf->config('card_masking_method') || 'first6last4';
+ $mask_method =~ /^first(\d+)last(\d+)$/
+ or die "can't parse card_masking_method $mask_method";
+ my($first, $last) = ($1, $2);
+
+ $paymask = substr($payinfo,0,$first).
+ 'x'x(length($payinfo)-$first-$last).
+ substr($payinfo,(length($payinfo)-$last));
+ } elsif ($payby eq 'CHEK' || $payby eq 'DCHK' ) {
+ # Checks (Show last 2 @ bank)
+ my( $account, $aba ) = split('@', $payinfo );
+ $paymask = 'x'x(length($account)-2).
+ substr($account,(length($account)-2))."@".$aba;
+ } else { # Tie up loose ends
+ $paymask = $payinfo;
+ }
+ }
+ return $paymask;
+}
+
+=item payinfo_check
+
+Checks payby and payinfo.
+
+For Customers (cust_main):
+'CARD' (credit card - automatic), 'DCRD' (credit card - on-demand),
+'CHEK' (electronic check - automatic), 'DCHK' (electronic check - on-demand),
+'LECB' (Phone bill billing), 'BILL' (billing), 'COMP' (free), or
+'PREPAY' (special billing type: applies a credit - see L<FS::prepay_credit> and sets billing type to I<BILL>)
+
+For Refunds (cust_refund):
+'CARD' (credit cards), 'CHEK' (electronic check/ACH),
+'LECB' (Phone bill billing), 'BILL' (billing), 'CASH' (cash),
+'WEST' (Western Union), 'MCRD' (Manual credit card), 'CBAK' (Chargeback), or 'COMP' (free)
+
+For Payments (cust_pay):
+'CARD' (credit cards), 'CHEK' (electronic check/ACH),
+'LECB' (phone bill billing), 'BILL' (billing), 'PREP' (prepaid card),
+'CASH' (cash), 'WEST' (Western Union), or 'MCRD' (Manual credit card)
+'COMP' (free) is depricated as a payment type in cust_pay
+
+=cut
+
+sub payinfo_check {
+ my $self = shift;
+
+ FS::payby->can_payby($self->table, $self->payby)
+ or return "Illegal payby: ". $self->payby;
+
+ if ( $self->payby eq 'CARD' && ! $self->is_encrypted($self->payinfo) ) {
+ my $payinfo = $self->payinfo;
+ $payinfo =~ s/\D//g;
+ $self->payinfo($payinfo);
+ if ( $self->payinfo ) {
+ $self->payinfo =~ /^(\d{13,16})$/
+ or return "Illegal (mistyped?) credit card number (payinfo)";
+ $self->payinfo($1);
+ validate($self->payinfo) or return "Illegal credit card number";
+ return "Unknown card type" if cardtype($self->payinfo) eq "Unknown";
+ } else {
+ $self->payinfo('N/A'); #???
+ }
+ } else {
+ if ( $self->is_encrypted($self->payinfo) ) {
+ #something better? all it would cause is a decryption error anyway?
+ my $error = $self->ut_anything('payinfo');
+ return $error if $error;
+ } else {
+ my $error = $self->ut_textn('payinfo');
+ return $error if $error;
+ }
+ }
+
+ '';
+
+}
+
+=item payby_payinfo_pretty
+
+Returns payment method and information (suitably masked, if applicable) as
+a human-readable string, such as:
+
+ Card #54xxxxxxxxxxxx32
+
+or
+
+ Check #119006
+
+=cut
+
+sub payby_payinfo_pretty {
+ my $self = shift;
+ if ( $self->payby eq 'CARD' ) {
+ 'Card #'. $self->paymask;
+ } elsif ( $self->payby eq 'CHEK' ) {
+ 'E-check acct#'. $self->payinfo;
+ } elsif ( $self->payby eq 'BILL' ) {
+ 'Check #'. $self->payinfo;
+ } elsif ( $self->payby eq 'PREP' ) {
+ 'Prepaid card #'. $self->payinfo;
+ } elsif ( $self->payby eq 'CASH' ) {
+ 'Cash '. $self->payinfo;
+ } elsif ( $self->payby eq 'WEST' ) {
+ 'Western Union'; #. $self->payinfo;
+ } elsif ( $self->payby eq 'MCRD' ) {
+ 'Manual credit card'; #. $self->payinfo;
+ } else {
+ $self->payby. ' '. $self->payinfo;
+ }
+}
+
+=back
+
+=head1 BUGS
+
+Future items?
+ Encryption - In the Future (Pull from Record.pm)
+ Bad Card Stuff - In the Future (Integrate Banned Pay)
+ Currency - In the Future
+
+=head1 SEE ALSO
+
+L<FS::payby>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/payinfo_transaction_Mixin.pm b/FS/FS/payinfo_transaction_Mixin.pm
new file mode 100644
index 0000000..19419de
--- /dev/null
+++ b/FS/FS/payinfo_transaction_Mixin.pm
@@ -0,0 +1,123 @@
+package FS::payinfo_transaction_Mixin;
+
+use strict;
+use vars qw( @ISA );
+use FS::payby;
+use FS::payinfo_Mixin;
+use FS::Record qw(qsearchs);
+use FS::cust_main;
+use FS::payment_gateway;
+
+@ISA = qw( FS::payinfo_Mixin );
+
+=head1 NAME
+
+FS::payinfo_transaction_Mixin - Mixin class for records in tables that represent transactions.
+
+=head1 SYNOPSIS
+
+package FS::some_table;
+use vars qw(@ISA);
+@ISA = qw( FS::payinfo_transaction_Mixin FS::Record );
+
+=head1 DESCRIPTION
+
+This is a mixin class for records that represent transactions: that contain
+payinfo and paybatch. Currently FS::cust_pay and FS::cust_refund
+
+=head1 METHODS
+
+=over 4
+
+=item cust_main
+
+Returns the parent customer object (see L<FS::cust_main>).
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+=item payby_name
+
+Returns a name for the payby field.
+
+=cut
+
+sub payby_name {
+ my $self = shift;
+ if ( $self->payby eq 'BILL' ) { #kludge
+ 'Check';
+ } else {
+ FS::payby->shortname( $self->payby );
+ }
+}
+
+=item gatewaynum
+
+Returns a gatewaynum for the processing gateway.
+
+=item processor
+
+Returns a name for the processing gateway.
+
+=item authorization
+
+Returns a name for the processing gateway.
+
+=item order_number
+
+Returns a name for the processing gateway.
+
+=cut
+
+sub gatewaynum { shift->_parse_paybatch->{'gatewaynum'}; }
+sub processor { shift->_parse_paybatch->{'processor'}; }
+sub authorization { shift->_parse_paybatch->{'authorization'}; }
+sub order_number { shift->_parse_paybatch->{'order_number'}; }
+
+#sucks that this stuff is in paybatch like this in the first place,
+#but at least other code can start to use new field names
+#(code nicked from FS::cust_main::realtime_refund_bop)
+sub _parse_paybatch {
+ my $self = shift;
+
+ $self->paybatch =~ /^((\d+)\-)?(\w+):\s*([\w\-\/ ]*)(:([\w\-]+))?$/
+ or return {};
+ #"Can't parse paybatch for paynum $options{'paynum'}: ".
+ # $cust_pay->paybatch;
+
+ my( $gatewaynum, $processor, $auth, $order_number ) = ( $2, $3, $4, $6 );
+
+ if ( $gatewaynum ) { #gateway for the payment to be refunded
+
+ my $payment_gateway =
+ qsearchs('payment_gateway', { 'gatewaynum' => $gatewaynum } );
+
+ die "payment gateway $gatewaynum not found" #?
+ unless $payment_gateway;
+
+ $processor = $payment_gateway->gateway_module;
+
+ }
+
+ {
+ 'gatewaynum' => $gatewaynum,
+ 'processor' => $processor,
+ 'authorization' => $auth,
+ 'order_number' => $order_number,
+ };
+
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::payinfo_Mixin>
+
+=cut
+
+1;
diff --git a/FS/FS/payment_gateway.pm b/FS/FS/payment_gateway.pm
new file mode 100644
index 0000000..35b4f08
--- /dev/null
+++ b/FS/FS/payment_gateway.pm
@@ -0,0 +1,200 @@
+package FS::payment_gateway;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::option_Common;
+use FS::agent_payment_gateway;
+
+@ISA = qw( FS::option_Common );
+
+=head1 NAME
+
+FS::payment_gateway - Object methods for payment_gateway records
+
+=head1 SYNOPSIS
+
+ use FS::payment_gateway;
+
+ $record = new FS::payment_gateway \%hash;
+ $record = new FS::payment_gateway { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::payment_gateway object represents an payment gateway.
+FS::payment_gateway inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item gatewaynum - primary key
+
+=item gateway_module - Business::OnlinePayment:: module name
+
+=item gateway_username - payment gateway username
+
+=item gateway_password - payment gateway password
+
+=item gateway_action - optional action or actions (multiple actions are separated with `,': for example: `Authorization Only, Post Authorization'). Defaults to `Normal Authorization'.
+
+=item disabled - Disabled flag, empty or 'Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new payment gateway. To add the payment gateway to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'payment_gateway'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid payment gateway. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('gatewaynum')
+ || $self->ut_alpha('gateway_module')
+ || $self->ut_textn('gateway_username')
+ || $self->ut_anything('gateway_password')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ #|| $self->ut_textn('gateway_action')
+ ;
+ return $error if $error;
+
+ if ( $self->gateway_action ) {
+ my @actions = split(/,\s*/, $self->gateway_action);
+ $self->gateway_action(
+ join( ',', map { /^(Normal Authorization|Authorization Only|Credit|Post Authorization)$/
+ or return "Unknown action $_";
+ $1
+ }
+ @actions
+ )
+ );
+ } else {
+ $self->gateway_action('Normal Authorization');
+ }
+
+ $self->SUPER::check;
+}
+
+=item agent_payment_gateway
+
+Returns any agent overrides for this payment gateway.
+
+=cut
+
+sub agent_payment_gateway {
+ my $self = shift;
+ qsearch('agent_payment_gateway', { 'gatewaynum' => $self->gatewaynum } );
+}
+
+=item disable
+
+Disables this payment gateway: deletes all associated agent_payment_gateway
+overrides and sets the I<disabled> field to "B<Y>".
+
+=cut
+
+sub disable {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $agent_payment_gateway ( $self->agent_payment_gateway ) {
+ my $error = $agent_payment_gateway->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error deleting agent_payment_gateway override: $error";
+ }
+ }
+
+ $self->disabled('Y');
+ my $error = $self->replace();
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error disabling payment_gateway: $error";
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/payment_gateway_option.pm b/FS/FS/payment_gateway_option.pm
new file mode 100644
index 0000000..0576022
--- /dev/null
+++ b/FS/FS/payment_gateway_option.pm
@@ -0,0 +1,126 @@
+package FS::payment_gateway_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::payment_gateway_option - Object methods for payment_gateway_option records
+
+=head1 SYNOPSIS
+
+ use FS::payment_gateway_option;
+
+ $record = new FS::payment_gateway_option \%hash;
+ $record = new FS::payment_gateway_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::payment_gateway_option object represents an option key and value for
+a payment gateway. FS::payment_gateway_option inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item gatewaynum -
+
+=item optionname -
+
+=item optionvalue -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new option. To add the option to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'payment_gateway_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid option. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('optionnum')
+ || $self->ut_foreign_key('gatewaynum', 'payment_gateway', 'gatewaynum')
+ || $self->ut_text('optionname')
+ || $self->ut_textn('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/phone_avail.pm b/FS/FS/phone_avail.pm
new file mode 100644
index 0000000..55b44ec
--- /dev/null
+++ b/FS/FS/phone_avail.pm
@@ -0,0 +1,186 @@
+package FS::phone_avail;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::phone_avail - Phone number availability cache
+
+=head1 SYNOPSIS
+
+ use FS::phone_avail;
+
+ $record = new FS::phone_avail \%hash;
+ $record = new FS::phone_avail { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::phone_avail object represents availability of phone service.
+FS::phone_avail inherits from FS::Record. The following fields are currently
+supported:
+
+=over 4
+
+=item availnum
+
+primary key
+
+=item exportnum
+
+exportnum
+
+=item countrycode
+
+countrycode
+
+=item state
+
+state
+
+=item npa
+
+npa
+
+=item nxx
+
+nxx
+
+=item station
+
+station
+
+=item name
+
+Optional name
+
+=item svcnum
+
+svcnum
+
+=item availbatch
+
+availbatch
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'phone_avail'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('availnum')
+ || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum' )
+ || $self->ut_number('countrycode')
+ || $self->ut_alphan('state')
+ || $self->ut_number('npa')
+ || $self->ut_numbern('nxx')
+ || $self->ut_numbern('station')
+ || $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum' )
+ || $self->ut_textn('availbatch')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+sub process_batch_import {
+ my $job = shift;
+
+ my $numsub = sub {
+ my( $phone_avail, $value ) = @_;
+ $value =~ s/\D//g;
+ $value =~ /^(\d{3})(\d{3})(\d+)$/ or die "unparsable number $value\n";
+ #( $hash->{npa}, $hash->{nxx}, $hash->{station} ) = ( $1, $2, $3 );
+ $phone_avail->npa($1);
+ $phone_avail->nxx($2);
+ $phone_avail->station($3);
+ };
+
+ my $opt = { 'table' => 'phone_avail',
+ 'params' => [ 'availbatch', 'exportnum', 'countrycode' ],
+ 'formats' => { 'default' => [ 'state', $numsub ] },
+ };
+
+ FS::Record::process_batch_import( $job, $opt, @_ );
+
+}
+
+=back
+
+=head1 BUGS
+
+Sparse documentation.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/pkg_category.pm b/FS/FS/pkg_category.pm
new file mode 100644
index 0000000..69578c9
--- /dev/null
+++ b/FS/FS/pkg_category.pm
@@ -0,0 +1,113 @@
+package FS::pkg_category;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch );
+use FS::part_pkg;
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::pkg_category - Object methods for pkg_category records
+
+=head1 SYNOPSIS
+
+ use FS::pkg_category;
+
+ $record = new FS::pkg_category \%hash;
+ $record = new FS::pkg_category { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::pkg_category object represents an package category. Every package class
+(see L<FS::pkg_class>) has, optionally, a package category. FS::pkg_category
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item categorynum - primary key (assigned automatically for new package categoryes)
+
+=item categoryname - Text name of this package category
+
+=item disabled - Disabled flag, empty or 'Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new package category. To add the package category to the database, see
+L<"insert">.
+
+=cut
+
+sub table { 'pkg_category'; }
+
+=item insert
+
+Adds this package category to the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item delete
+
+Deletes this package category from the database. Only package categoryes with no
+associated package definitions can be deleted. If there is an error, returns
+the error, otherwise returns false.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete an pkg_category with pkg_class records!"
+ if qsearch( 'pkg_class', { 'categorynum' => $self->categorynum } );
+
+ $self->SUPER::delete;
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid package category. If there is an
+error, returns the error, otherwise returns false. Called by the insert and
+replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('categorynum')
+ or $self->ut_text('categoryname')
+ or $self->SUPER::check;
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::part_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/pkg_class.pm b/FS/FS/pkg_class.pm
new file mode 100644
index 0000000..254282f
--- /dev/null
+++ b/FS/FS/pkg_class.pm
@@ -0,0 +1,141 @@
+package FS::pkg_class;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs qsearch );
+use FS::part_pkg;
+use FS::pkg_category;
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::pkg_class - Object methods for pkg_class records
+
+=head1 SYNOPSIS
+
+ use FS::pkg_class;
+
+ $record = new FS::pkg_class \%hash;
+ $record = new FS::pkg_class { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::pkg_class object represents an package class. Every package definition
+(see L<FS::part_pkg>) has, optionally, a package class. FS::pkg_class inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item classnum - primary key (assigned automatically for new package classes)
+
+=item classname - Text name of this package class
+
+=item categorynum - Number of associated pkg_category (see L<FS::pkg_category>)
+
+=item disabled - Disabled flag, empty or 'Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new package class. To add the package class to the database, see
+L<"insert">.
+
+=cut
+
+sub table { 'pkg_class'; }
+
+=item insert
+
+Adds this package class to the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item delete
+
+Deletes this package class from the database. Only package classes with no
+associated package definitions can be deleted. If there is an error, returns
+the error, otherwise returns false.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete an pkg_class with part_pkg records!"
+ if qsearch( 'part_pkg', { 'classnum' => $self->classnum } );
+
+ $self->SUPER::delete;
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid package class. If there is an
+error, returns the error, otherwise returns false. Called by the insert and
+replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('classnum')
+ or $self->ut_text('classname')
+ or $self->ut_foreign_keyn('categorynum', 'pkg_category', 'categorynum')
+ or $self->SUPER::check;
+
+}
+
+=item pkg_category
+
+Returns the pkg_category record associated with this class, or false if there
+is none.
+
+=cut
+
+sub pkg_category {
+ my $self = shift;
+ qsearchs('pkg_category', { 'categorynum' => $self->categorynum } );
+}
+
+=item categoryname
+
+Returns the category name associated with this class, or false if there
+is none.
+
+=cut
+
+sub categoryname {
+ my $pkg_category = shift->pkg_category;
+ $pkg_category->categoryname if $pkg_category;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::part_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/pkg_referral.pm b/FS/FS/pkg_referral.pm
new file mode 100644
index 0000000..333c2bf
--- /dev/null
+++ b/FS/FS/pkg_referral.pm
@@ -0,0 +1,126 @@
+package FS::pkg_referral;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::pkg_referral - Object methods for pkg_referral records
+
+=head1 SYNOPSIS
+
+ use FS::pkg_referral;
+
+ $record = new FS::pkg_referral \%hash;
+ $record = new FS::pkg_referral { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::pkg_referral object represents the association of an advertising source
+with a specific customer package (purchase). FS::pkg_referral inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item pkgrefnum - primary key
+
+=item pkgnum - Customer package. See L<FS::cust_pkg>
+
+=item refnum - Advertising source. See L<FS::part_referral>
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'pkg_referral'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('pkgrefnum')
+ || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum' )
+ || $self->ut_foreign_key('refnum', 'part_referral', 'refnum' )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+Multiple pkg_referral records for a single package (configured off by default)
+still seems weird.
+
+=head1 SEE ALSO
+
+L<FS::part_referral>, L<FS::cust_pkg>, L<FS::Record>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/pkg_svc.pm b/FS/FS/pkg_svc.pm
new file mode 100644
index 0000000..9f3a4a1
--- /dev/null
+++ b/FS/FS/pkg_svc.pm
@@ -0,0 +1,160 @@
+package FS::pkg_svc;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs );
+use FS::part_pkg;
+use FS::part_svc;
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::pkg_svc - Object methods for pkg_svc records
+
+=head1 SYNOPSIS
+
+ use FS::pkg_svc;
+
+ $record = new FS::pkg_svc \%hash;
+ $record = new FS::pkg_svc { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $part_pkg = $record->part_pkg;
+
+ $part_svc = $record->part_svc;
+
+=head1 DESCRIPTION
+
+An FS::pkg_svc record links a billing item definition (see L<FS::part_pkg>) to
+a service definition (see L<FS::part_svc>). FS::pkg_svc inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item pkgsvcnum - primary key
+
+=item pkgpart - Billing item definition (see L<FS::part_pkg>)
+
+=item svcpart - Service definition (see L<FS::part_svc>)
+
+=item quantity - Quantity of this service definition that this billing item
+definition includes
+
+=item primary_svc - primary flag, empty or 'Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Create a new record. To add the record to the database, see L<"insert">.
+
+=cut
+
+sub table { 'pkg_svc'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my( $new, $old ) = ( shift, shift );
+
+ $old = $new->replace_old unless defined($old);
+
+ return "Can't change pkgpart!" if $old->pkgpart != $new->pkgpart;
+ return "Can't change svcpart!" if $old->svcpart != $new->svcpart;
+
+ $new->SUPER::replace($old);
+}
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error;
+ $error =
+ $self->ut_numbern('pkgsvcnum')
+ || $self->ut_number('pkgpart')
+ || $self->ut_number('svcpart')
+ || $self->ut_number('quantity')
+ ;
+ return $error if $error;
+
+ return "Unknown pkgpart!" unless $self->part_pkg;
+ return "Unknown svcpart!" unless $self->part_svc;
+
+ if ( $self->dbdef_table->column('primary_svc') ) {
+ $error = $self->ut_enum('primary_svc', [ '', 'Y' ] );
+ return $error if $error;
+ }
+
+ $self->SUPER::check;
+}
+
+=item part_pkg
+
+Returns the FS::part_pkg object (see L<FS::part_pkg>).
+
+=cut
+
+sub part_pkg {
+ my $self = shift;
+ qsearchs( 'part_pkg', { 'pkgpart' => $self->pkgpart } );
+}
+
+=item part_svc
+
+Returns the FS::part_svc object (see L<FS::part_svc>).
+
+=cut
+
+sub part_svc {
+ my $self = shift;
+ qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::part_pkg>, L<FS::part_svc>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/port.pm b/FS/FS/port.pm
new file mode 100644
index 0000000..c26ca85
--- /dev/null
+++ b/FS/FS/port.pm
@@ -0,0 +1,154 @@
+package FS::port;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs );
+use FS::nas;
+use FS::session;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::port - Object methods for port records
+
+=head1 SYNOPSIS
+
+ use FS::port;
+
+ $record = new FS::port \%hash;
+ $record = new FS::port { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $session = $port->session;
+
+=head1 DESCRIPTION
+
+An FS::port object represents an individual port on a NAS. FS::port inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item portnum - primary key
+
+=item ip - IP address of this port
+
+=item nasport - port number on the NAS
+
+=item nasnum - NAS this port is on - see L<FS::nas>
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new port. To add the port to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'port'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid port. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+ my $error =
+ $self->ut_numbern('portnum')
+ || $self->ut_ipn('ip')
+ || $self->ut_numbern('nasport')
+ || $self->ut_number('nasnum');
+ ;
+ return $error if $error;
+ return "Either ip or nasport must be specified"
+ unless $self->ip || $self->nasport;
+ return "Unknown nasnum"
+ unless qsearchs('nas', { 'nasnum' => $self->nasnum } );
+ $self->SUPER::check;
+}
+
+=item session
+
+Returns the currently open session on this port, or if no session is currently
+open, the most recent session. See L<FS::session>.
+
+=cut
+
+sub session {
+ my $self = shift;
+ qsearchs('session', { 'portnum' => $self->portnum }, '*',
+ 'ORDER BY login DESC LIMIT 1' );
+}
+
+=back
+
+=head1 BUGS
+
+The session method won't deal well if you have multiple open sessions on a
+port, for example if your RADIUS server drops B<stop> records. Suggestions for
+how to deal with this sort of lossage welcome; should we close the session
+when we get a new session on that port? Tag it as invalid somehow? Close it
+one second after it was opened? *sigh* Maybe FS::session shouldn't let you
+create overlapping sessions, at least folks will find out their logging is
+dropping records.
+
+If you think the above refers multiple user logins you need to read the
+manpages again.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/prepay_credit.pm b/FS/FS/prepay_credit.pm
new file mode 100644
index 0000000..302ba37
--- /dev/null
+++ b/FS/FS/prepay_credit.pm
@@ -0,0 +1,202 @@
+package FS::prepay_credit;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw(qsearchs dbh);
+use FS::agent;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::prepay_credit - Object methods for prepay_credit records
+
+=head1 SYNOPSIS
+
+ use FS::prepay_credit;
+
+ $record = new FS::prepay_credit \%hash;
+ $record = new FS::prepay_credit {
+ 'identifier' => '4198123455512121'
+ 'amount' => '19.95',
+ };
+
+ $record = new FS::prepay_credit {
+ 'identifier' => '4198123455512121'
+ 'seconds' => '7200',
+ };
+
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::prepay_credit object represents a pre-paid card. FS::prepay_credit
+inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item field - description
+
+=item identifier - identifier entered by the user to receive the credit
+
+=item amount - amount of the credit
+
+=item seconds - time amount of credit (see L<FS::svc_acct/seconds>)
+
+=item agentnum - optional agent (see L<FS::agent>) for this prepaid card
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new pre-paid credit. To add the pre-paid credit to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'prepay_credit'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid pre-paid credit. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $identifier = $self->identifier;
+ $identifier =~ s/\W//g; #anything else would just confuse things
+ $self->identifier($identifier);
+
+ $self->ut_numbern('prepaynum')
+ || $self->ut_alpha('identifier')
+ || $self->ut_money('amount')
+ || $self->ut_numbern('seconds')
+ || $self->ut_numbern('upbytes')
+ || $self->ut_numbern('downbytes')
+ || $self->ut_numbern('totalbytes')
+ || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
+ || $self->SUPER::check
+ ;
+
+}
+
+=item agent
+
+Returns the agent (see L<FS::agent>) for this prepaid card, if any.
+
+=cut
+
+sub agent {
+ my $self = shift;
+ qsearchs('agent', { 'agentnum' => $self->agentnum } );
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item generate NUM TYPE HASHREF
+
+Generates the specified number of prepaid cards. Returns an array reference of
+the newly generated card identifiers, or a scalar error message.
+
+=cut
+
+#false laziness w/agent::generate_reg_codes
+sub generate {
+ my( $num, $type, $hashref ) = @_;
+
+ my @codeset = ();
+ push @codeset, ( 'A'..'Z' ) if $type =~ /alpha/;
+ push @codeset, ( '1'..'9' ) if $type =~ /numeric/;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $condup = 0; #don't retry forever
+
+ my @cards = ();
+ for ( 1 ... $num ) {
+
+ my $identifier = join('', map($codeset[int(rand $#codeset)], (0..7) ) );
+
+ redo if qsearchs('prepay_credit',{identifier=>$identifier}) && $condup++<23;
+ $condup = 0;
+
+ my $prepay_credit = new FS::prepay_credit {
+ 'identifier' => $identifier,
+ %$hashref,
+ };
+ my $error = $prepay_credit->check || $prepay_credit->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "(inserting prepay_credit) $error";
+ }
+ push @cards, $prepay_credit->identifier;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ \@cards;
+
+}
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_acct>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/queue.pm b/FS/FS/queue.pm
new file mode 100644
index 0000000..381e418
--- /dev/null
+++ b/FS/FS/queue.pm
@@ -0,0 +1,496 @@
+package FS::queue;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK $DEBUG $conf $jobnums);
+use Exporter;
+use FS::UID qw(myconnect);
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs dbh );
+#use FS::queue;
+use FS::queue_arg;
+use FS::queue_depend;
+use FS::cust_svc;
+
+@ISA = qw(FS::Record);
+@EXPORT_OK = qw( joblisting );
+
+$DEBUG = 0;
+
+$FS::UID::callback{'FS::queue'} = sub {
+ $conf = new FS::Conf;
+};
+
+$jobnums = '';
+
+=head1 NAME
+
+FS::queue - Object methods for queue records
+
+=head1 SYNOPSIS
+
+ use FS::queue;
+
+ $record = new FS::queue \%hash;
+ $record = new FS::queue { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::queue object represents an queued job. FS::queue inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item jobnum
+
+Primary key
+
+=item job
+
+Fully-qualified subroutine name
+
+=item status
+
+Job status (new, locked, or failed)
+
+=item statustext
+
+Freeform text status message
+
+=item _date
+
+UNIX timestamp
+
+=item svcnum
+
+Optional link to service (see L<FS::cust_svc>).
+
+=item custnum
+
+Optional link to customer (see L<FS::cust_main>).
+
+=item secure
+
+Secure flag, 'Y' indicates that when using encryption, the job needs to be
+run on a machine with the private key.
+
+=cut
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new job. To add the job to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'queue'; }
+
+=item insert [ ARGUMENT, ARGUMENT... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+If any arguments are supplied, a queue_arg record for each argument is also
+created (see L<FS::queue_arg>).
+
+=cut
+
+#false laziness w/part_export.pm
+sub insert {
+ my( $self, @args ) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my %args = ();
+ {
+ no warnings "misc";
+ %args = @args;
+ }
+
+ $self->custnum( $args{'custnum'} ) if $args{'custnum'};
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ foreach my $arg ( @args ) {
+ my $queue_arg = new FS::queue_arg ( {
+ 'jobnum' => $self->jobnum,
+ 'arg' => $arg,
+ } );
+ $error = $queue_arg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ if ( $jobnums ) {
+ warn "jobnums global is active: $jobnums\n" if $DEBUG;
+ push @$jobnums, $self->jobnum;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Delete this record from the database. Any corresponding queue_arg records are
+deleted as well
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my @del = qsearch( 'queue_arg', { 'jobnum' => $self->jobnum } );
+ push @del, qsearch( 'queue_depend', { 'depend_jobnum' => $self->jobnum } );
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ foreach my $del ( @del ) {
+ $error = $del->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid job. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+ my $error =
+ $self->ut_numbern('jobnum')
+ || $self->ut_anything('job')
+ || $self->ut_numbern('_date')
+ || $self->ut_enum('status',['', qw( new locked failed )])
+ || $self->ut_anything('statustext')
+ || $self->ut_numbern('svcnum')
+ ;
+ return $error if $error;
+
+ $error = $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum');
+ $self->svcnum('') if $error;
+
+ $self->status('new') unless $self->status;
+ $self->_date(time) unless $self->_date;
+
+ $self->SUPER::check;
+}
+
+=item args
+
+Returns a list of the arguments associated with this job.
+
+=cut
+
+sub args {
+ my $self = shift;
+ map $_->arg, qsearch( 'queue_arg',
+ { 'jobnum' => $self->jobnum },
+ '',
+ 'ORDER BY argnum'
+ );
+}
+
+=item cust_svc
+
+Returns the FS::cust_svc object associated with this job, if any.
+
+=cut
+
+sub cust_svc {
+ my $self = shift;
+ qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
+}
+
+=item queue_depend
+
+Returns the FS::queue_depend objects associated with this job, if any.
+(Dependancies that must complete before this job can be run).
+
+=cut
+
+sub queue_depend {
+ my $self = shift;
+ qsearch('queue_depend', { 'jobnum' => $self->jobnum } );
+}
+
+=item depend_insert OTHER_JOBNUM
+
+Inserts a dependancy for this job - it will not be run until the other job
+specified completes. If there is an error, returns the error, otherwise
+returns false.
+
+When using job dependancies, you should wrap the insertion of all relevant jobs
+in a database transaction.
+
+=cut
+
+sub depend_insert {
+ my($self, $other_jobnum) = @_;
+ my $queue_depend = new FS::queue_depend ( {
+ 'jobnum' => $self->jobnum,
+ 'depend_jobnum' => $other_jobnum,
+ } );
+ $queue_depend->insert;
+}
+
+=item queue_depended
+
+Returns the FS::queue_depend objects that associate other jobs with this job,
+if any. (The jobs that are waiting for this job to complete before they can
+run).
+
+=cut
+
+sub queue_depended {
+ my $self = shift;
+ qsearch('queue_depend', { 'depend_jobnum' => $self->jobnum } );
+}
+
+=item depended_delete
+
+Deletes the other queued jobs (FS::queue objects) that are waiting for this
+job, if any. If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub depended_delete {
+ my $self = shift;
+ my $error;
+ foreach my $job (
+ map { qsearchs('queue', { 'jobnum' => $_->jobnum } ) } $self->queue_depended
+ ) {
+ $error = $job->depended_delete;
+ return $error if $error;
+ $error = $job->delete;
+ return $error if $error
+ }
+}
+
+=item update_statustext VALUE
+
+Updates the statustext value of this job to supplied value, in the database.
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+use vars qw($_update_statustext_dbh);
+sub update_statustext {
+ my( $self, $statustext ) = @_;
+ return '' if $statustext eq $self->statustext;
+ warn "updating statustext for $self to $statustext" if $DEBUG;
+
+ $_update_statustext_dbh ||= myconnect;
+
+ my $sth = $_update_statustext_dbh->prepare(
+ 'UPDATE queue set statustext = ? WHERE jobnum = ?'
+ ) or return $_update_statustext_dbh->errstr;
+
+ $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
+ $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
+ $self->statustext($statustext);
+ '';
+
+ #my $new = new FS::queue { $self->hash };
+ #$new->statustext($statustext);
+ #my $error = $new->replace($self);
+ #return $error if $error;
+ #$self->statustext($statustext);
+ #'';
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item joblisting HASHREF NOACTIONS
+
+=cut
+
+sub joblisting {
+ my($hashref, $noactions) = @_;
+
+ use Date::Format;
+ use HTML::Entities;
+ use FS::CGI;
+
+ my @queue = qsearch( 'queue', $hashref );
+ return '' unless scalar(@queue);
+
+ my $p = FS::CGI::popurl(2);
+
+ my $html = qq!<FORM ACTION="$p/misc/queue.cgi" METHOD="POST">!.
+ FS::CGI::table(). <<END;
+ <TR>
+ <TH COLSPAN=2>Job</TH>
+ <TH>Args</TH>
+ <TH>Date</TH>
+ <TH>Status</TH>
+END
+ $html .= '<TH>Account</TH>' unless $hashref->{svcnum};
+ $html .= '</TR>';
+
+ my $dangerous = $conf->exists('queue_dangerous_controls');
+
+ my $areboxes = 0;
+
+ foreach my $queue ( sort {
+ $a->getfield('jobnum') <=> $b->getfield('jobnum')
+ } @queue ) {
+ my $queue_hashref = $queue->hashref;
+ my $jobnum = $queue->jobnum;
+
+ my $args;
+ if ( $dangerous || $queue->job !~ /^FS::part_export::/ || !$noactions ) {
+ $args = encode_entities( join(' ', $queue->args) );
+ } else {
+ $args = '';
+ }
+
+ my $date = time2str( "%a %b %e %T %Y", $queue->_date );
+ my $status = $queue->status;
+ $status .= ': '. $queue->statustext if $queue->statustext;
+ my @queue_depend = $queue->queue_depend;
+ $status .= ' (waiting for '.
+ join(', ', map { $_->depend_jobnum } @queue_depend ).
+ ')'
+ if @queue_depend;
+ my $changable = $dangerous
+ || ( ! $noactions && $status =~ /^failed/ || $status =~ /^locked/ );
+ if ( $changable ) {
+ $status .=
+ qq! (&nbsp;<A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A>&nbsp;|!.
+ qq!&nbsp;<A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A>&nbsp;)!;
+ }
+ my $cust_svc = $queue->cust_svc;
+
+ $html .= <<END;
+ <TR>
+ <TD>$jobnum</TD>
+ <TD>$queue_hashref->{job}</TD>
+ <TD>$args</TD>
+ <TD>$date</TD>
+ <TD>$status</TD>
+END
+
+ unless ( $hashref->{svcnum} ) {
+ my $account;
+ if ( $cust_svc ) {
+ my $table = $cust_svc->part_svc->svcdb;
+ my $label = ( $cust_svc->label )[1];
+ $account = qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
+ qq!">$label</A>!;
+ } else {
+ $account = '';
+ }
+ $html .= "<TD>$account</TD>";
+ }
+
+ if ( $changable ) {
+ $areboxes=1;
+ $html .=
+ qq!<TD><INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1"></TD>!;
+
+ }
+
+ $html .= '</TR>';
+
+}
+
+ $html .= '</TABLE>';
+
+ if ( $areboxes ) {
+ $html .= '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
+ '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>';
+ }
+
+ $html;
+
+}
+
+=back
+
+=head1 BUGS
+
+$jobnums global
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/queue_arg.pm b/FS/FS/queue_arg.pm
new file mode 100644
index 0000000..c96ff12
--- /dev/null
+++ b/FS/FS/queue_arg.pm
@@ -0,0 +1,117 @@
+package FS::queue_arg;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::queue_arg - Object methods for queue_arg records
+
+=head1 SYNOPSIS
+
+ use FS::queue_arg;
+
+ $record = new FS::queue_arg \%hash;
+ $record = new FS::queue_arg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::queue_arg object represents job argument. FS::queue_arg inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item argnum - primary key
+
+=item jobnum - see L<FS::queue>
+
+=item arg - argument
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new argument. To add the argument to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'queue_arg'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid argument. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+ my $error =
+ $self->ut_numbern('argnum')
+ || $self->ut_numbern('jobnum')
+ || $self->ut_anything('arg')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::queue>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/queue_depend.pm b/FS/FS/queue_depend.pm
new file mode 100644
index 0000000..99a22c5
--- /dev/null
+++ b/FS/FS/queue_depend.pm
@@ -0,0 +1,121 @@
+package FS::queue_depend;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::queue;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::queue_depend - Object methods for queue_depend records
+
+=head1 SYNOPSIS
+
+ use FS::queue_depend;
+
+ $record = new FS::queue_depend \%hash;
+ $record = new FS::queue_depend { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::queue_depend object represents an job dependancy. FS::queue_depend
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item dependnum - primary key
+
+=item jobnum - source jobnum (see L<FS::queue>).
+
+=item depend_jobnum - dependancy jobnum (see L<FS::queue>)
+
+=back
+
+The job specified by B<jobnum> depends on the job specified B<depend_jobnum> -
+the B<jobnum> job will not be run until the B<depend_jobnum> job has completed
+successfully (or manually removed).
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new dependancy. To add the dependancy to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'queue_depend'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid dependancy. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('dependnum')
+ || $self->ut_foreign_key('jobnum', 'queue', 'jobnum')
+ || $self->ut_foreign_key('depend_jobnum', 'queue', 'jobnum')
+ || $self->SUPER::check
+ ;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::queue>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/raddb.pm b/FS/FS/raddb.pm
new file mode 100644
index 0000000..506b325
--- /dev/null
+++ b/FS/FS/raddb.pm
@@ -0,0 +1,1912 @@
+package FS::raddb;
+use vars qw(%attrib);
+
+%attrib = (
+ '3com_user_access_level' => '3Com-User-Access-Level',
+ '3gpp2_accounting_contain' => '3GPP2-Accounting-Container',
+ '3gpp2_acct_stop_trigger' => '3GPP2-Acct-Stop-Trigger',
+ '3gpp2_active_time' => '3GPP2-Active-Time',
+ '3gpp2_airlink_priority' => '3GPP2-Airlink-Priority',
+ '3gpp2_airlink_record_typ' => '3GPP2-Airlink-Record-Type',
+ '3gpp2_airlink_sequence_n' => '3GPP2-Airlink-Sequence-Number',
+ '3gpp2_allowed_diffserv_m' => '3GPP2-Allowed-Diffserv-Marking',
+ '3gpp2_allowed_persistent' => '3GPP2-Allowed-Persistent-TFTs',
+ '3gpp2_bad_ppp_frame_coun' => '3GPP2-Bad-PPP-Frame-Count',
+ '3gpp2_begin_session' => '3GPP2-Begin-Session',
+ '3gpp2_bsid' => '3GPP2-BSID',
+ '3gpp2_compulsory_tunnel_' => '3GPP2-Compulsory-Tunnel-Indicator',
+ '3gpp2_correlation_id' => '3GPP2-Correlation-Id',
+ '3gpp2_dcch_frame_size' => '3GPP2-DCCH-Frame-Size',
+ '3gpp2_diffserv_class_opt' => '3GPP2-Diffserv-Class-Option',
+ '3gpp2_disconnect_reason' => '3GPP2-Disconnect-Reason',
+ '3gpp2_dns_update_capabil' => '3GPP2-DNS-Update-Capability',
+ '3gpp2_dns_update_require' => '3GPP2-DNS-Update-Required',
+ '3gpp2_esn' => '3GPP2-ESN',
+ '3gpp2_fch_frame_size' => '3GPP2-FCH-Frame-Size',
+ '3gpp2_foreign_agent_addr' => '3GPP2-Foreign-Agent-Address',
+ '3gpp2_forward_dcch_mux_o' => '3GPP2-Forward-DCCH-Mux-Option',
+ '3gpp2_forward_dcch_rc' => '3GPP2-Forward-DCCH-RC',
+ '3gpp2_forward_fch_mux_op' => '3GPP2-Forward-FCH-Mux-Option',
+ '3gpp2_forward_fch_rc' => '3GPP2-Forward-FCH-RC',
+ '3gpp2_forward_pdch_rc' => '3GPP2-Forward-PDCH-RC',
+ '3gpp2_forward_traffic_ty' => '3GPP2-Forward-Traffic-Type',
+ '3gpp2_home_agent_ip_addr' => '3GPP2-Home-Agent-IP-Address',
+ '3gpp2_ike_preshared_secr' => '3GPP2-Ike-Preshared-Secret-Request',
+ '3gpp2_inbound_mobile_ip_' => '3GPP2-Inbound-Mobile-IP-Sig-Octets',
+ '3gpp2_ip_qos' => '3GPP2-IP-QoS',
+ '3gpp2_ip_technology' => '3GPP2-IP-Technology',
+ '3gpp2_keyid' => '3GPP2-KeyID',
+ '3gpp2_last_user_activity' => '3GPP2-Last-User-Activity-Time',
+ '3gpp2_mip_lifetime' => '3GPP2-MIP-Lifetime',
+ '3gpp2_mn_aaa_removal_ind' => '3GPP2-MN-AAA-Removal-Indication',
+ '3gpp2_mn_ha_shared_key' => '3GPP2-MN-HA-Shared-Key',
+ '3gpp2_mn_ha_spi' => '3GPP2-MN-HA-SPI',
+ '3gpp2_module_orig_term_i' => '3GPP2-Module-Orig-Term-Indicator',
+ '3gpp2_number_active_tran' => '3GPP2-Number-Active-Transitions',
+ '3gpp2_originating_number' => '3GPP2-Originating-Number-SDBs',
+ '3gpp2_originating_sdb_oc' => '3GPP2-Originating-SDB-OCtet-Count',
+ '3gpp2_outbound_mobile_ip' => '3GPP2-Outbound-Mobile-IP-Sig-Octets',
+ '3gpp2_pcf_ip_address' => '3GPP2-PCF-IP-Address',
+ '3gpp2_pre_shared_secret' => '3GPP2-Pre-Shared-Secret',
+ '3gpp2_prepaid_acct_capab' => '3GPP2-Prepaid-acct-Capability',
+ '3gpp2_prepaid_acct_quota' => '3GPP2-Prepaid-Acct-Quota',
+ '3gpp2_prepaid_tariff_swi' => '3GPP2-PrePaid-Tariff-Switching',
+ '3gpp2_received_hdlc_octe' => '3GPP2-Received-HDLC-Octets',
+ '3gpp2_release_indicator' => '3GPP2-Release-Indicator',
+ '3gpp2_remote_address_tab' => '3GPP2-Remote-Address-Table-Index',
+ '3gpp2_remote_ip_address' => '3GPP2-Remote-IP-Address',
+ '3gpp2_remote_ipv4_addr_o' => '3GPP2-Remote-IPv4-Addr-Octet-Count',
+ '3gpp2_remote_ipv6_addres' => '3GPP2-Remote-IPv6-Address',
+ '3gpp2_remote_ipv6_octet_' => '3GPP2-Remote-IPv6-Octet-Count',
+ '3gpp2_reverse_dcch_mux_o' => '3GPP2-Reverse-DCCH-Mux-Option',
+ '3gpp2_reverse_dhhc_rc' => '3GPP2-Reverse-DHHC-RC',
+ '3gpp2_reverse_fch_mux_op' => '3GPP2-Reverse-FCH-Mux-Option',
+ '3gpp2_reverse_fch_rc' => '3GPP2-Reverse-FCH-RC',
+ '3gpp2_reverse_traffic_ty' => '3GPP2-Reverse-Traffic-Type',
+ '3gpp2_reverse_tunnel_spe' => '3GPP2-Reverse-Tunnel-Spec',
+ '3gpp2_rn_packet_data_ina' => '3GPP2-RN-Packet-Data-Inactivity-Timer',
+ '3gpp2_s_key' => '3GPP2-S-Key',
+ '3gpp2_s_lifetime' => '3GPP2-S-Lifetime',
+ '3gpp2_s_request' => '3GPP2-S-Request',
+ '3gpp2_security_level' => '3GPP2-Security-Level',
+ '3gpp2_service_option' => '3GPP2-Service-Option',
+ '3gpp2_service_option_pro' => '3GPP2-Service-Option-Profile',
+ '3gpp2_service_reference_' => '3GPP2-Service-Reference-Id',
+ '3gpp2_session_continue' => '3GPP2-Session-Continue',
+ '3gpp2_session_terminatio' => '3GPP2-Session-Termination-Capability',
+ '3gpp2_terminating_number' => '3GPP2-Terminating-Number-SDBs',
+ '3gpp2_terminating_sdb_oc' => '3GPP2-Terminating-SDB-Octet-Count',
+ '3gpp2_user_id' => '3GPP2-User-Id',
+ '3gpp_charging_characteri' => '3GPP-Charging-Characteristics',
+ '3gpp_charging_gateway_ad' => '3GPP-Charging-Gateway-Address',
+ '3gpp_charging_gateway_ip' => '3GPP-Charging-Gateway-IPv6-Address',
+ '3gpp_charging_id' => '3GPP-Charging-ID',
+ '3gpp_ggsn_address' => '3GPP-GGSN-Address',
+ '3gpp_ggsn_ipv6_address' => '3GPP-GGSN-IPv6-Address',
+ '3gpp_ggsn_mcc_mnc' => '3GPP-GGSN-MCC-MNC',
+ '3gpp_gprs_negotiated_qos' => '3GPP-GPRS-Negotiated-QoS-profile',
+ '3gpp_imsi' => '3GPP-IMSI',
+ '3gpp_imsi_mcc_mnc' => '3GPP-IMSI-MCC-MNC',
+ '3gpp_ipv6_dns_servers' => '3GPP-IPv6-DNS-Servers',
+ '3gpp_nsapi' => '3GPP-NSAPI',
+ '3gpp_pdp_type' => '3GPP-PDP-Type',
+ '3gpp_selection_mode' => '3GPP-Selection-Mode',
+ '3gpp_session_stop_indica' => '3GPP-Session-Stop-Indicator',
+ '3gpp_sgsn_address' => '3GPP-SGSN-Address',
+ '3gpp_sgsn_ipv6_address' => '3GPP-SGSN-IPv6-Address',
+ 'aat_assign_ip_pool' => 'AAT-Assign-IP-Pool',
+ 'aat_atm_direct' => 'AAT-ATM-Direct',
+ 'aat_atm_traffic_profile' => 'AAT-ATM-Traffic-Profile',
+ 'aat_atm_vci' => 'AAT-ATM-VCI',
+ 'aat_atm_vpi' => 'AAT-ATM-VPI',
+ 'aat_client_primary_dns' => 'AAT-Client-Primary-DNS',
+ 'aat_client_primary_wins_' => 'AAT-Client-Primary-WINS-NBNS',
+ 'aat_client_secondary_win' => 'AAT-Client-Secondary-WINS-NBNS',
+ 'aat_data_filter' => 'AAT-Data-Filter',
+ 'aat_input_octets_diff' => 'AAT-Input-Octets-Diff',
+ 'aat_ip_pool_definition' => 'AAT-IP-Pool-Definition',
+ 'aat_ip_tos' => 'AAT-IP-TOS',
+ 'aat_ip_tos_apply_to' => 'AAT-IP-TOS-Apply-To',
+ 'aat_ip_tos_precedence' => 'AAT-IP-TOS-Precedence',
+ 'aat_mcast_client' => 'AAT-MCast-Client',
+ 'aat_output_octets_diff' => 'AAT-Output-Octets-Diff',
+ 'aat_ppp_address' => 'AAT-PPP-Address',
+ 'aat_require_auth' => 'AAT-Require-Auth',
+ 'aat_source_ip_check' => 'AAT-Source-IP-Check',
+ 'aat_user_mac_address' => 'AAT-User-MAC-Address',
+ 'aat_vrouter_name' => 'AAT-Vrouter-Name',
+ 'acc_access_community' => 'Acc-Access-Community',
+ 'acc_access_partition' => 'Acc-Access-Partition',
+ 'acc_acct_on_off_reason' => 'Acc-Acct-On-Off-Reason',
+ 'acc_ace_token' => 'Acc-Ace-Token',
+ 'acc_ace_token_ttl' => 'Acc-Ace-Token-Ttl',
+ 'acc_apsm_oversubscribed' => 'Acc-Apsm-Oversubscribed',
+ 'acc_bridging_support' => 'Acc-Bridging-Support',
+ 'acc_callback_cbcp_type' => 'Acc-Callback-CBCP-Type',
+ 'acc_callback_delay' => 'Acc-Callback-Delay',
+ 'acc_callback_mode' => 'Acc-Callback-Mode',
+ 'acc_callback_num_valid' => 'Acc-Callback-Num-Valid',
+ 'acc_ccp_option' => 'Acc-Ccp-Option',
+ 'acc_clearing_cause' => 'Acc-Clearing-Cause',
+ 'acc_clearing_location' => 'Acc-Clearing-Location',
+ 'acc_connect_rx_speed' => 'Acc-Connect-Rx-Speed',
+ 'acc_connect_tx_speed' => 'Acc-Connect-Tx-Speed',
+ 'acc_customer_id' => 'Acc-Customer-Id',
+ 'acc_dial_port_index' => 'Acc-Dial-Port-Index',
+ 'acc_dialout_auth_mode' => 'Acc-Dialout-Auth-Mode',
+ 'acc_dialout_auth_passwor' => 'Acc-Dialout-Auth-Password',
+ 'acc_dialout_auth_usernam' => 'Acc-Dialout-Auth-Username',
+ 'acc_dns_server_pri' => 'Acc-Dns-Server-Pri',
+ 'acc_dns_server_sec' => 'Acc-Dns-Server-Sec',
+ 'acc_igmp_admin_state' => 'Acc-Igmp-Admin-State',
+ 'acc_igmp_version' => 'Acc-Igmp-Version',
+ 'acc_input_errors' => 'Acc-Input-Errors',
+ 'acc_ip_compression' => 'Acc-Ip-Compression',
+ 'acc_ip_gateway_pri' => 'Acc-Ip-Gateway-Pri',
+ 'acc_ip_gateway_sec' => 'Acc-Ip-Gateway-Sec',
+ 'acc_ip_pool_name' => 'Acc-Ip-Pool-Name',
+ 'acc_ipx_compression' => 'Acc-Ipx-Compression',
+ 'acc_ml_call_threshold' => 'Acc-ML-Call-Threshold',
+ 'acc_ml_clear_threshold' => 'Acc-ML-Clear-Threshold',
+ 'acc_ml_damping_factor' => 'Acc-ML-Damping-Factor',
+ 'acc_ml_mlx_admin_state' => 'Acc-ML-MLX-Admin-State',
+ 'acc_modem_error_protocol' => 'Acc-Modem-Error-Protocol',
+ 'acc_modem_modulation_typ' => 'Acc-Modem-Modulation-Type',
+ 'acc_nbns_server_pri' => 'Acc-Nbns-Server-Pri',
+ 'acc_nbns_server_sec' => 'Acc-Nbns-Server-Sec',
+ 'acc_output_errors' => 'Acc-Output-Errors',
+ 'acc_reason_code' => 'Acc-Reason-Code',
+ 'acc_request_type' => 'Acc-Request-Type',
+ 'acc_route_policy' => 'Acc-Route-Policy',
+ 'acc_service_profile' => 'Acc-Service-Profile',
+ 'acc_tunnel_port' => 'Acc-Tunnel-Port',
+ 'acc_tunnel_secret' => 'Acc-Tunnel-Secret',
+ 'acc_vpsm_reject_cause' => 'Acc-Vpsm-Reject-Cause',
+ 'acct_authentic' => 'Acct-Authentic',
+ 'acct_delay_time' => 'Acct-Delay-Time',
+ 'acct_dyn_ac_ent' => 'Acct_Dyn_Ac_Ent',
+ 'acct_dyn_ac_enu' => 'Acct-Dyn-Ac-Ent',
+ 'acct_input_gigawords' => 'Acct-Input-Gigawords',
+ 'acct_input_octets' => 'Acct-Input-Octets',
+ 'acct_input_octets_64' => 'Acct_Input_Octets_64',
+ 'acct_input_octets_65' => 'Acct-Input-Octets-64',
+ 'acct_input_packets' => 'Acct-Input-Packets',
+ 'acct_input_packets_64' => 'Acct_Input_Packets_64',
+ 'acct_input_packets_65' => 'Acct-Input-Packets-64',
+ 'acct_interim_interval' => 'Acct-Interim-Interval',
+ 'acct_link_count' => 'Acct-Link-Count',
+ 'acct_mcast_in_octets' => 'Acct_Mcast_In_Octets',
+ 'acct_mcast_in_octett' => 'Acct-Mcast-In-Octets',
+ 'acct_mcast_in_packets' => 'Acct_Mcast_In_Packets',
+ 'acct_mcast_in_packett' => 'Acct-Mcast-In-Packets',
+ 'acct_mcast_out_octets' => 'Acct_Mcast_Out_Octets',
+ 'acct_mcast_out_octett' => 'Acct-Mcast-Out-Octets',
+ 'acct_mcast_out_packets' => 'Acct_Mcast_Out_Packets',
+ 'acct_mcast_out_packett' => 'Acct-Mcast-Out-Packets',
+ 'acct_multi_session_id' => 'Acct-Multi-Session-Id',
+ 'acct_output_gigawords' => 'Acct-Output-Gigawords',
+ 'acct_output_octets' => 'Acct-Output-Octets',
+ 'acct_output_octets_64' => 'Acct_Output_Octets_64',
+ 'acct_output_octets_65' => 'Acct-Output-Octets-64',
+ 'acct_output_packets' => 'Acct-Output-Packets',
+ 'acct_output_packets_64' => 'Acct_Output_Packets_64',
+ 'acct_output_packets_65' => 'Acct-Output-Packets-64',
+ 'acct_session_gigawords' => 'Acct-Session-Gigawords',
+ 'acct_session_id' => 'Acct-Session-Id',
+ 'acct_session_input_gigaw' => 'Acct-Session-Input-Gigawords',
+ 'acct_session_input_octet' => 'Acct-Session-Input-Octets',
+ 'acct_session_octets' => 'Acct-Session-Octets',
+ 'acct_session_output_giga' => 'Acct-Session-Output-Gigawords',
+ 'acct_session_output_octe' => 'Acct-Session-Output-Octets',
+ 'acct_session_start_time' => 'Acct-Session-Start-Time',
+ 'acct_session_time' => 'Acct-Session-Time',
+ 'acct_status_type' => 'Acct-Status-Type',
+ 'acct_terminate_cause' => 'Acct-Terminate-Cause',
+ 'acct_tunnel_connection' => 'Acct-Tunnel-Connection',
+ 'acct_tunnel_packets_lost' => 'Acct-Tunnel-Packets-Lost',
+ 'acct_type' => 'Acct-Type',
+ 'acct_unique_session_id' => 'Acct-Unique-Session-Id',
+ 'add_prefix' => 'Add-Prefix',
+ 'add_suffix' => 'Add-Suffix',
+ 'alteon_service_type' => 'Alteon-Service-Type',
+ 'altiga_access_hours_g_u' => 'Altiga-Access-Hours-G/U',
+ 'altiga_allow_alpha_only_' => 'Altiga-Allow-Alpha-Only-Passwords-G',
+ 'altiga_ipsec_allow_passw' => 'Altiga-IPSec-Allow-Passwd-Store-G/U',
+ 'altiga_ipsec_authenticat' => 'Altiga-IPSec-Authentication-G',
+ 'altiga_ipsec_banner_g' => 'Altiga-IPSec-Banner-G',
+ 'altiga_ipsec_default_dom' => 'Altiga-IPSec-Default-Domain-G',
+ 'altiga_ipsec_l2l_keepali' => 'Altiga-IPSec-L2L-Keepalives-G',
+ 'altiga_ipsec_mode_config' => 'Altiga-IPSec-Mode-Config-G',
+ 'altiga_ipsec_over_nat_g' => 'Altiga-IPSec-Over-NAT-G',
+ 'altiga_ipsec_over_nat_po' => 'Altiga-IPSec-Over-NAT-Port-Num-G',
+ 'altiga_ipsec_sec_associa' => 'Altiga-IPSec-Sec-Association-G/U',
+ 'altiga_ipsec_secondary_d' => 'Altiga-IPSec-Secondary-Domains-G',
+ 'altiga_ipsec_split_tunne' => 'Altiga-IPSec-Split-Tunnel-List-G',
+ 'altiga_ipsec_tunnel_type' => 'Altiga-IPSec-Tunnel-Type-G',
+ 'altiga_ipsec_user_group_' => 'Altiga-IPSec-User-Group-Lock-G',
+ 'altiga_l2tp_encryption_g' => 'Altiga-L2TP-Encryption-G',
+ 'altiga_l2tp_min_authenti' => 'Altiga-L2TP-Min-Authentication-G/U',
+ 'altiga_min_password_leng' => 'Altiga-Min-Password-Length-G',
+ 'altiga_pptp_encryption_g' => 'Altiga-PPTP-Encryption-G',
+ 'altiga_pptp_min_authenti' => 'Altiga-PPTP-Min-Authentication-G/U',
+ 'altiga_primary_dns_g' => 'Altiga-Primary-DNS-G',
+ 'altiga_primary_wins_g' => 'Altiga-Primary-WINS-G',
+ 'altiga_priority_on_sep_g' => 'Altiga-Priority-on-SEP-G/U',
+ 'altiga_secondary_dns_g' => 'Altiga-Secondary-DNS-G',
+ 'altiga_secondary_wins_g' => 'Altiga-Secondary-WINS-G',
+ 'altiga_sep_card_assignme' => 'Altiga-SEP-Card-Assignment-G/U',
+ 'altiga_simultaneous_logi' => 'Altiga-Simultaneous-Logins-G/U',
+ 'altiga_tunneling_protoco' => 'Altiga-Tunneling-Protocols-G/U',
+ 'altiga_use_client_addres' => 'Altiga-Use-Client-Address-G/U',
+ 'annex_acct_servers' => 'Annex-Acct-Servers',
+ 'annex_addr_resolution_pr' => 'Annex-Addr-Resolution-Protocol',
+ 'annex_addr_resolution_se' => 'Annex-Addr-Resolution-Servers',
+ 'annex_audit_level' => 'Annex-Audit-Level',
+ 'annex_authen_servers' => 'Annex-Authen-Servers',
+ 'annex_begin_modulation' => 'Annex-Begin-Modulation',
+ 'annex_begin_receive_line' => 'Annex-Begin-Receive-Line-Level',
+ 'annex_callback_portlist' => 'Annex-Callback-Portlist',
+ 'annex_cli_command' => 'Annex-CLI-Command',
+ 'annex_cli_filter' => 'Annex-CLI-Filter',
+ 'annex_compression_protoc' => 'Annex-Compression-Protocol',
+ 'annex_connect_progress' => 'Annex-Connect-Progress',
+ 'annex_disconnect_reason' => 'Annex-Disconnect-Reason',
+ 'annex_domain_name' => 'Annex-Domain-Name',
+ 'annex_edo' => 'Annex-EDO',
+ 'annex_end_modulation' => 'Annex-End-Modulation',
+ 'annex_end_receive_line_l' => 'Annex-End-Receive-Line-Level',
+ 'annex_error_correction_p' => 'Annex-Error-Correction-Prot',
+ 'annex_filter' => 'Annex-Filter',
+ 'annex_host_allow' => 'Annex-Host-Allow',
+ 'annex_host_restrict' => 'Annex-Host-Restrict',
+ 'annex_input_filter' => 'Annex-Input-Filter',
+ 'annex_keypress_timeout' => 'Annex-Keypress-Timeout',
+ 'annex_local_ip_address' => 'Annex-Local-IP-Address',
+ 'annex_local_username' => 'Annex-Local-Username',
+ 'annex_logical_channel_nu' => 'Annex-Logical-Channel-Number',
+ 'annex_maximum_call_durat' => 'Annex-Maximum-Call-Duration',
+ 'annex_modem_disc_reason' => 'Annex-Modem-Disc-Reason',
+ 'annex_mrru' => 'Annex-MRRU',
+ 'annex_multicast_rate_lim' => 'Annex-Multicast-Rate-Limit',
+ 'annex_multilink_id' => 'Annex-Multilink-Id',
+ 'annex_num_in_multilink' => 'Annex-Num-In-Multilink',
+ 'annex_output_filter' => 'Annex-Output-Filter',
+ 'annex_pool_id' => 'Annex-Pool-Id',
+ 'annex_port' => 'Annex-Port',
+ 'annex_ppp_trace_level' => 'Annex-PPP-Trace-Level',
+ 'annex_pre_input_octets' => 'Annex-Pre-Input-Octets',
+ 'annex_pre_input_packets' => 'Annex-Pre-Input-Packets',
+ 'annex_pre_output_octets' => 'Annex-Pre-Output-Octets',
+ 'annex_pre_output_packets' => 'Annex-Pre-Output-Packets',
+ 'annex_primary_dns_server' => 'Annex-Primary-DNS-Server',
+ 'annex_primary_nbns_serve' => 'Annex-Primary-NBNS-Server',
+ 'annex_product_name' => 'Annex-Product-Name',
+ 'annex_rate_reneg_req_rcv' => 'Annex-Rate-Reneg-Req-Rcvd',
+ 'annex_rate_reneg_req_sen' => 'Annex-Rate-Reneg-Req-Sent',
+ 'annex_re_chap_timeout' => 'Annex-Re-CHAP-Timeout',
+ 'annex_receive_speed' => 'Annex-Receive-Speed',
+ 'annex_retrain_requests_r' => 'Annex-Retrain-Requests-Rcvd',
+ 'annex_retrain_requests_s' => 'Annex-Retrain-Requests-Sent',
+ 'annex_retransmitted_pack' => 'Annex-Retransmitted-Packets',
+ 'annex_sec_profile_index' => 'Annex-Sec-Profile-Index',
+ 'annex_secondary_dns_serv' => 'Annex-Secondary-DNS-Server',
+ 'annex_secondary_nbns_ser' => 'Annex-Secondary-NBNS-Server',
+ 'annex_signal_to_noise_ra' => 'Annex-Signal-to-Noise-Ratio',
+ 'annex_sw_version' => 'Annex-SW-Version',
+ 'annex_syslog_tap' => 'Annex-Syslog-Tap',
+ 'annex_system_disc_reason' => 'Annex-System-Disc-Reason',
+ 'annex_transmit_speed' => 'Annex-Transmit-Speed',
+ 'annex_transmitted_packet' => 'Annex-Transmitted-Packets',
+ 'annex_tunnel_authen_mode' => 'Annex-Tunnel-Authen-Mode',
+ 'annex_tunnel_authen_type' => 'Annex-Tunnel-Authen-Type',
+ 'annex_unauthenticated_ti' => 'Annex-Unauthenticated-Time',
+ 'annex_user_level' => 'Annex-User-Level',
+ 'annex_user_server_locati' => 'Annex-User-Server-Location',
+ 'annex_wan_number' => 'Annex-Wan-Number',
+ 'arap_challenge_response' => 'ARAP-Challenge-Response',
+ 'arap_features' => 'ARAP-Features',
+ 'arap_password' => 'ARAP-Password',
+ 'arap_security' => 'ARAP-Security',
+ 'arap_security_data' => 'ARAP-Security-Data',
+ 'arap_zone_access' => 'ARAP-Zone-Access',
+ 'ascend_access_intercept_' => 'Ascend-Access-Intercept-LEA',
+ 'ascend_access_intercepta' => 'Ascend-Access-Intercept-Log',
+ 'ascend_add_seconds' => 'Ascend-Add-Seconds',
+ 'ascend_appletalk_peer_mo' => 'Ascend-Appletalk-Peer-Mode',
+ 'ascend_appletalk_route' => 'Ascend-Appletalk-Route',
+ 'ascend_ara_pw' => 'Ascend-Ara-PW',
+ 'ascend_assign_ip_client' => 'Ascend-Assign-IP-Client',
+ 'ascend_assign_ip_global_' => 'Ascend-Assign-IP-Global-Pool',
+ 'ascend_assign_ip_pool' => 'Ascend-Assign-IP-Pool',
+ 'ascend_assign_ip_server' => 'Ascend-Assign-IP-Server',
+ 'ascend_atm_connect_group' => 'Ascend-ATM-Connect-Group',
+ 'ascend_atm_connect_vci' => 'Ascend-ATM-Connect-Vci',
+ 'ascend_atm_connect_vpi' => 'Ascend-ATM-Connect-Vpi',
+ 'ascend_atm_direct' => 'Ascend-ATM-Direct',
+ 'ascend_atm_direct_profil' => 'Ascend-ATM-Direct-Profile',
+ 'ascend_atm_fault_managem' => 'Ascend-ATM-Fault-Management',
+ 'ascend_atm_group' => 'Ascend-ATM-Group',
+ 'ascend_atm_loopback_cell' => 'Ascend-ATM-Loopback-Cell-Loss',
+ 'ascend_atm_vci' => 'Ascend-ATM-Vci',
+ 'ascend_atm_vpi' => 'Ascend-ATM-Vpi',
+ 'ascend_auth_delay' => 'Ascend-Auth-Delay',
+ 'ascend_auth_type' => 'Ascend-Auth-Type',
+ 'ascend_authen_alias' => 'Ascend-Authen-Alias',
+ 'ascend_backup' => 'Ascend-Backup',
+ 'ascend_bacp_enable' => 'Ascend-BACP-Enable',
+ 'ascend_base_channel_coun' => 'Ascend-Base-Channel-Count',
+ 'ascend_bi_directional_au' => 'Ascend-Bi-Directional-Auth',
+ 'ascend_billing_number' => 'Ascend-Billing-Number',
+ 'ascend_bir_bridge_group' => 'Ascend-BIR-Bridge-Group',
+ 'ascend_bir_enable' => 'Ascend-BIR-Enable',
+ 'ascend_bir_proxy' => 'Ascend-BIR-Proxy',
+ 'ascend_bridge' => 'Ascend-Bridge',
+ 'ascend_bridge_address' => 'Ascend-Bridge-Address',
+ 'ascend_bridge_non_pppoe' => 'Ascend-Bridge-Non-PPPoE',
+ 'ascend_cache_refresh' => 'Ascend-Cache-Refresh',
+ 'ascend_cache_time' => 'Ascend-Cache-Time',
+ 'ascend_call_attempt_limi' => 'Ascend-Call-Attempt-Limit',
+ 'ascend_call_block_durati' => 'Ascend-Call-Block-Duration',
+ 'ascend_call_by_call' => 'Ascend-Call-By-Call',
+ 'ascend_call_direction' => 'Ascend-Call-Direction',
+ 'ascend_call_filter' => 'Ascend-Call-Filter',
+ 'ascend_call_type' => 'Ascend-Call-Type',
+ 'ascend_callback' => 'Ascend-Callback',
+ 'ascend_callback_delay' => 'Ascend-Callback-Delay',
+ 'ascend_calling_id_number' => 'Ascend-Calling-Id-Number-Plan',
+ 'ascend_calling_id_presen' => 'Ascend-Calling-Id-Presentatn',
+ 'ascend_calling_id_screen' => 'Ascend-Calling-Id-Screening',
+ 'ascend_calling_id_type_o' => 'Ascend-Calling-Id-Type-Of-Num',
+ 'ascend_calling_subaddres' => 'Ascend-Calling-Subaddress',
+ 'ascend_cbcp_delay' => 'Ascend-CBCP-Delay',
+ 'ascend_cbcp_enable' => 'Ascend-CBCP-Enable',
+ 'ascend_cbcp_mode' => 'Ascend-CBCP-Mode',
+ 'ascend_cbcp_trunk_group' => 'Ascend-CBCP-Trunk-Group',
+ 'ascend_cir_timer' => 'Ascend-CIR-Timer',
+ 'ascend_ckt_type' => 'Ascend-Ckt-Type',
+ 'ascend_client_assign_dns' => 'Ascend-Client-Assign-DNS',
+ 'ascend_client_assign_win' => 'Ascend-Client-Assign-WINS',
+ 'ascend_client_gateway' => 'Ascend-Client-Gateway',
+ 'ascend_client_primary_dn' => 'Ascend-Client-Primary-DNS',
+ 'ascend_client_primary_wi' => 'Ascend-Client-Primary-WINS',
+ 'ascend_client_secondary_' => 'Ascend-Client-Secondary-WINS',
+ 'ascend_client_secondarya' => 'Ascend-Client-Secondary-DNS',
+ 'ascend_connect_progress' => 'Ascend-Connect-Progress',
+ 'ascend_data_filter' => 'Ascend-Data-Filter',
+ 'ascend_data_rate' => 'Ascend-Data-Rate',
+ 'ascend_data_svc' => 'Ascend-Data-Svc',
+ 'ascend_dba_monitor' => 'Ascend-DBA-Monitor',
+ 'ascend_dec_channel_count' => 'Ascend-Dec-Channel-Count',
+ 'ascend_destination_nas_p' => 'Ascend-Destination-Nas-Port',
+ 'ascend_dhcp_maximum_leas' => 'Ascend-DHCP-Maximum-Leases',
+ 'ascend_dhcp_pool_number' => 'Ascend-DHCP-Pool-Number',
+ 'ascend_dhcp_reply' => 'Ascend-DHCP-Reply',
+ 'ascend_dial_number' => 'Ascend-Dial-Number',
+ 'ascend_dialed_number' => 'Ascend-Dialed-Number',
+ 'ascend_dialout_allowed' => 'Ascend-Dialout-Allowed',
+ 'ascend_disconnect_cause' => 'Ascend-Disconnect-Cause',
+ 'ascend_dropped_octets' => 'Ascend-Dropped-Octets',
+ 'ascend_dropped_packets' => 'Ascend-Dropped-Packets',
+ 'ascend_dsl_cir_recv_limi' => 'Ascend-Dsl-CIR-Recv-Limit',
+ 'ascend_dsl_cir_xmit_limi' => 'Ascend-Dsl-CIR-Xmit-Limit',
+ 'ascend_dsl_downstream_li' => 'Ascend-Dsl-Downstream-Limit',
+ 'ascend_dsl_rate_mode' => 'Ascend-Dsl-Rate-Mode',
+ 'ascend_dsl_rate_type' => 'Ascend-Dsl-Rate-Type',
+ 'ascend_dsl_upstream_limi' => 'Ascend-Dsl-Upstream-Limit',
+ 'ascend_egress_enabled' => 'Ascend-Egress-Enabled',
+ 'ascend_endpoint_disc' => 'Ascend-Endpoint-Disc',
+ 'ascend_event_type' => 'Ascend-Event-Type',
+ 'ascend_expect_callback' => 'Ascend-Expect-Callback',
+ 'ascend_fcp_parameter' => 'Ascend-FCP-Parameter',
+ 'ascend_filter' => 'Ascend-Filter',
+ 'ascend_filter_required' => 'Ascend-Filter-Required',
+ 'ascend_first_dest' => 'Ascend-First-Dest',
+ 'ascend_force_56' => 'Ascend-Force-56',
+ 'ascend_fr_08_mode' => 'Ascend-FR-08-Mode',
+ 'ascend_fr_circuit_name' => 'Ascend-FR-Circuit-Name',
+ 'ascend_fr_dce_n392' => 'Ascend-FR-DCE-N392',
+ 'ascend_fr_dce_n393' => 'Ascend-FR-DCE-N393',
+ 'ascend_fr_direct' => 'Ascend-FR-Direct',
+ 'ascend_fr_direct_dlci' => 'Ascend-FR-Direct-DLCI',
+ 'ascend_fr_direct_profile' => 'Ascend-FR-Direct-Profile',
+ 'ascend_fr_dlci' => 'Ascend-FR-DLCI',
+ 'ascend_fr_dte_n392' => 'Ascend-FR-DTE-N392',
+ 'ascend_fr_dte_n393' => 'Ascend-FR-DTE-N393',
+ 'ascend_fr_link_mgt' => 'Ascend-FR-Link-Mgt',
+ 'ascend_fr_link_status_dl' => 'Ascend-FR-Link-Status-DLCI',
+ 'ascend_fr_linkup' => 'Ascend-FR-LinkUp',
+ 'ascend_fr_n391' => 'Ascend-FR-N391',
+ 'ascend_fr_nailed_grp' => 'Ascend-FR-Nailed-Grp',
+ 'ascend_fr_profile_name' => 'Ascend-FR-Profile-Name',
+ 'ascend_fr_svc_addr' => 'Ascend-FR-SVC-Addr',
+ 'ascend_fr_t391' => 'Ascend-FR-T391',
+ 'ascend_fr_t392' => 'Ascend-FR-T392',
+ 'ascend_fr_type' => 'Ascend-FR-Type',
+ 'ascend_ft1_caller' => 'Ascend-FT1-Caller',
+ 'ascend_global_call_id' => 'Ascend-Global-Call-Id',
+ 'ascend_group' => 'Ascend-Group',
+ 'ascend_h323_conference_i' => 'Ascend-H323-Conference-Id',
+ 'ascend_h323_dialed_time' => 'Ascend-H323-Dialed-Time',
+ 'ascend_h323_fegw_address' => 'Ascend-H323-Fegw-Address',
+ 'ascend_h323_gatekeeper' => 'Ascend-H323-Gatekeeper',
+ 'ascend_handle_ipx' => 'Ascend-Handle-IPX',
+ 'ascend_history_weigh_typ' => 'Ascend-History-Weigh-Type',
+ 'ascend_home_agent_ip_add' => 'Ascend-Home-Agent-IP-Addr',
+ 'ascend_home_agent_passwo' => 'Ascend-Home-Agent-Password',
+ 'ascend_home_agent_udp_po' => 'Ascend-Home-Agent-UDP-Port',
+ 'ascend_home_network_name' => 'Ascend-Home-Network-Name',
+ 'ascend_host_info' => 'Ascend-Host-Info',
+ 'ascend_idle_limit' => 'Ascend-Idle-Limit',
+ 'ascend_if_netmask' => 'Ascend-IF-Netmask',
+ 'ascend_inc_channel_count' => 'Ascend-Inc-Channel-Count',
+ 'ascend_inter_arrival_jit' => 'Ascend-Inter-Arrival-Jitter',
+ 'ascend_ip_direct' => 'Ascend-IP-Direct',
+ 'ascend_ip_pool_chaining' => 'Ascend-IP-Pool-Chaining',
+ 'ascend_ip_pool_definitio' => 'Ascend-IP-Pool-Definition',
+ 'ascend_ip_tos' => 'Ascend-IP-TOS',
+ 'ascend_ip_tos_apply_to' => 'Ascend-IP-TOS-Apply-To',
+ 'ascend_ip_tos_precedence' => 'Ascend-IP-TOS-Precedence',
+ 'ascend_ipsec_profile' => 'Ascend-IPSEC-Profile',
+ 'ascend_ipx_alias' => 'Ascend-IPX-Alias',
+ 'ascend_ipx_header_compre' => 'Ascend-IPX-Header-Compression',
+ 'ascend_ipx_node_addr' => 'Ascend-IPX-Node-Addr',
+ 'ascend_ipx_peer_mode' => 'Ascend-IPX-Peer-Mode',
+ 'ascend_ipx_route' => 'Ascend-IPX-Route',
+ 'ascend_link_compression' => 'Ascend-Link-Compression',
+ 'ascend_max_shared_users' => 'Ascend-Max-Shared-Users',
+ 'ascend_maximum_call_dura' => 'Ascend-Maximum-Call-Duration',
+ 'ascend_maximum_channels' => 'Ascend-Maximum-Channels',
+ 'ascend_maximum_time' => 'Ascend-Maximum-Time',
+ 'ascend_menu_item' => 'Ascend-Menu-Item',
+ 'ascend_menu_selector' => 'Ascend-Menu-Selector',
+ 'ascend_metric' => 'Ascend-Metric',
+ 'ascend_minimum_channels' => 'Ascend-Minimum-Channels',
+ 'ascend_modem_portno' => 'Ascend-Modem-PortNo',
+ 'ascend_modem_shelfno' => 'Ascend-Modem-ShelfNo',
+ 'ascend_modem_slotno' => 'Ascend-Modem-SlotNo',
+ 'ascend_mpp_idle_percent' => 'Ascend-MPP-Idle-Percent',
+ 'ascend_mtu' => 'Ascend-MTU',
+ 'ascend_multicast_client' => 'Ascend-Multicast-Client',
+ 'ascend_multicast_gleave_' => 'Ascend-Multicast-GLeave-Delay',
+ 'ascend_multicast_rate_li' => 'Ascend-Multicast-Rate-Limit',
+ 'ascend_multilink_id' => 'Ascend-Multilink-ID',
+ 'ascend_nas_port_format' => 'Ascend-NAS-Port-Format',
+ 'ascend_netware_timeout' => 'Ascend-Netware-timeout',
+ 'ascend_num_in_multilink' => 'Ascend-Num-In-Multilink',
+ 'ascend_number_sessions' => 'Ascend-Number-Sessions',
+ 'ascend_numbering_plan_id' => 'Ascend-Numbering-Plan-ID',
+ 'ascend_owner_ip_addr' => 'Ascend-Owner-IP-Addr',
+ 'ascend_port_redir_portnu' => 'Ascend-Port-Redir-Portnum',
+ 'ascend_port_redir_protoc' => 'Ascend-Port-Redir-Protocol',
+ 'ascend_port_redir_server' => 'Ascend-Port-Redir-Server',
+ 'ascend_ppp_address' => 'Ascend-PPP-Address',
+ 'ascend_ppp_async_map' => 'Ascend-PPP-Async-Map',
+ 'ascend_ppp_vj_1172' => 'Ascend-PPP-VJ-1172',
+ 'ascend_ppp_vj_slot_comp' => 'Ascend-PPP-VJ-Slot-Comp',
+ 'ascend_pppoe_enable' => 'Ascend-PPPoE-Enable',
+ 'ascend_pre_input_octets' => 'Ascend-Pre-Input-Octets',
+ 'ascend_pre_input_packets' => 'Ascend-Pre-Input-Packets',
+ 'ascend_pre_output_octets' => 'Ascend-Pre-Output-Octets',
+ 'ascend_pre_output_packet' => 'Ascend-Pre-Output-Packets',
+ 'ascend_preempt_limit' => 'Ascend-Preempt-Limit',
+ 'ascend_presession_time' => 'Ascend-PreSession-Time',
+ 'ascend_pri_number_type' => 'Ascend-PRI-Number-Type',
+ 'ascend_primary_home_agen' => 'Ascend-Primary-Home-Agent',
+ 'ascend_private_route' => 'Ascend-Private-Route',
+ 'ascend_private_route_req' => 'Ascend-Private-Route-Required',
+ 'ascend_private_route_tab' => 'Ascend-Private-Route-Table-ID',
+ 'ascend_pw_lifetime' => 'Ascend-PW-Lifetime',
+ 'ascend_pw_warntime' => 'Ascend-PW-Warntime',
+ 'ascend_qos_downstream' => 'Ascend-QOS-Downstream',
+ 'ascend_qos_upstream' => 'Ascend-QOS-Upstream',
+ 'ascend_receive_secret' => 'Ascend-Receive-Secret',
+ 'ascend_recv_name' => 'Ascend-Recv-Name',
+ 'ascend_redirect_number' => 'Ascend-Redirect-Number',
+ 'ascend_remote_addr' => 'Ascend-Remote-Addr',
+ 'ascend_remote_fw' => 'Ascend-Remote-FW',
+ 'ascend_remove_seconds' => 'Ascend-Remove-Seconds',
+ 'ascend_require_auth' => 'Ascend-Require-Auth',
+ 'ascend_route_appletalk' => 'Ascend-Route-Appletalk',
+ 'ascend_route_ip' => 'Ascend-Route-IP',
+ 'ascend_route_ipx' => 'Ascend-Route-IPX',
+ 'ascend_secondary_home_ag' => 'Ascend-Secondary-Home-Agent',
+ 'ascend_seconds_of_histor' => 'Ascend-Seconds-Of-History',
+ 'ascend_send_auth' => 'Ascend-Send-Auth',
+ 'ascend_send_passwd' => 'Ascend-Send-Passwd',
+ 'ascend_send_secret' => 'Ascend-Send-Secret',
+ 'ascend_service_type' => 'Ascend-Service-Type',
+ 'ascend_session_svr_key' => 'Ascend-Session-Svr-Key',
+ 'ascend_session_type' => 'Ascend-Session-Type',
+ 'ascend_shared_profile_en' => 'Ascend-Shared-Profile-Enable',
+ 'ascend_source_auth' => 'Ascend-Source-Auth',
+ 'ascend_source_ip_check' => 'Ascend-Source-IP-Check',
+ 'ascend_svc_enabled' => 'Ascend-SVC-Enabled',
+ 'ascend_target_util' => 'Ascend-Target-Util',
+ 'ascend_telnet_profile' => 'Ascend-Telnet-Profile',
+ 'ascend_temporary_rtes' => 'Ascend-Temporary-Rtes',
+ 'ascend_third_prompt' => 'Ascend-Third-Prompt',
+ 'ascend_token_expiry' => 'Ascend-Token-Expiry',
+ 'ascend_token_idle' => 'Ascend-Token-Idle',
+ 'ascend_token_immediate' => 'Ascend-Token-Immediate',
+ 'ascend_traffic_shaper' => 'Ascend-Traffic-Shaper',
+ 'ascend_transit_number' => 'Ascend-Transit-Number',
+ 'ascend_ts_idle_limit' => 'Ascend-TS-Idle-Limit',
+ 'ascend_ts_idle_mode' => 'Ascend-TS-Idle-Mode',
+ 'ascend_tunnel_vrouter_na' => 'Ascend-Tunnel-VRouter-Name',
+ 'ascend_tunneling_protoco' => 'Ascend-Tunneling-Protocol',
+ 'ascend_user_acct_base' => 'Ascend-User-Acct-Base',
+ 'ascend_user_acct_host' => 'Ascend-User-Acct-Host',
+ 'ascend_user_acct_key' => 'Ascend-User-Acct-Key',
+ 'ascend_user_acct_port' => 'Ascend-User-Acct-Port',
+ 'ascend_user_acct_time' => 'Ascend-User-Acct-Time',
+ 'ascend_user_acct_type' => 'Ascend-User-Acct-Type',
+ 'ascend_uu_info' => 'Ascend-UU-Info',
+ 'ascend_vrouter_name' => 'Ascend-VRouter-Name',
+ 'ascend_x25_cug' => 'Ascend-X25-Cug',
+ 'ascend_x25_nui' => 'Ascend-X25-Nui',
+ 'ascend_x25_nui_password_' => 'Ascend-X25-Nui-Password-Prompt',
+ 'ascend_x25_nui_prompt' => 'Ascend-X25-Nui-Prompt',
+ 'ascend_x25_pad_alias_1' => 'Ascend-X25-Pad-Alias-1',
+ 'ascend_x25_pad_alias_2' => 'Ascend-X25-Pad-Alias-2',
+ 'ascend_x25_pad_alias_3' => 'Ascend-X25-Pad-Alias-3',
+ 'ascend_x25_pad_banner' => 'Ascend-X25-Pad-Banner',
+ 'ascend_x25_pad_prompt' => 'Ascend-X25-Pad-Prompt',
+ 'ascend_x25_pad_x3_parame' => 'Ascend-X25-Pad-X3-Parameters',
+ 'ascend_x25_pad_x3_profil' => 'Ascend-X25-Pad-X3-Profile',
+ 'ascend_x25_profile_name' => 'Ascend-X25-Profile-Name',
+ 'ascend_x25_reverse_charg' => 'Ascend-X25-Reverse-Charging',
+ 'ascend_x25_rpoa' => 'Ascend-X25-Rpoa',
+ 'ascend_x25_x121_address' => 'Ascend-X25-X121-Address',
+ 'ascend_xmit_rate' => 'Ascend-Xmit-Rate',
+ 'assigned_ip_address' => 'Assigned_IP_Address',
+ 'assigned_ip_addrest' => 'Assigned-IP-Address',
+ 'auth_type' => 'Auth-Type',
+ 'autz_type' => 'Autz-Type',
+ 'bg_aging_time' => 'BG_Aging_Time',
+ 'bg_aging_timf' => 'BG-Aging-Time',
+ 'bg_path_cost' => 'BG_Path_Cost',
+ 'bg_path_cosu' => 'BG-Path-Cost',
+ 'bg_span_dis' => 'BG_Span_Dis',
+ 'bg_span_dit' => 'BG-Span-Dis',
+ 'bg_trans_bpdu' => 'BG_Trans_BPDU',
+ 'bg_trans_bpdv' => 'BG-Trans-BPDU',
+ 'bind_auth_context' => 'Bind_Auth_Context',
+ 'bind_auth_contexu' => 'Bind-Auth-Context',
+ 'bind_auth_max_sessions' => 'Bind_Auth_Max_Sessions',
+ 'bind_auth_max_sessiont' => 'Bind-Auth-Max-Sessions',
+ 'bind_auth_protocol' => 'Bind_Auth_Protocol',
+ 'bind_auth_protocom' => 'Bind-Auth-Protocol',
+ 'bind_auth_service_grp' => 'Bind_Auth_Service_Grp',
+ 'bind_auth_service_grq' => 'Bind-Auth-Service-Grp',
+ 'bind_bypass_bypass' => 'Bind_Bypass_Bypass',
+ 'bind_bypass_bypast' => 'Bind-Bypass-Bypass',
+ 'bind_bypass_context' => 'Bind_Bypass_Context',
+ 'bind_bypass_contexu' => 'Bind-Bypass-Context',
+ 'bind_dot1q_port' => 'Bind_Dot1q_Port',
+ 'bind_dot1q_poru' => 'Bind-Dot1q-Port',
+ 'bind_dot1q_slot' => 'Bind_Dot1q_Slot',
+ 'bind_dot1q_slou' => 'Bind-Dot1q-Slot',
+ 'bind_dot1q_vlan_tag_id' => 'Bind_Dot1q_Vlan_Tag_Id',
+ 'bind_dot1q_vlan_tag_ie' => 'Bind-Dot1q-Vlan-Tag-Id',
+ 'bind_int_context' => 'Bind_Int_Context',
+ 'bind_int_contexu' => 'Bind-Int-Context',
+ 'bind_int_interface_name' => 'Bind_Int_Interface_Name',
+ 'bind_int_interface_namf' => 'Bind-Int-Interface-Name',
+ 'bind_l2tp_flow_control' => 'Bind_L2TP_Flow_Control',
+ 'bind_l2tp_flow_controm' => 'Bind-L2TP-Flow-Control',
+ 'bind_l2tp_tunnel_name' => 'Bind_L2TP_Tunnel_Name',
+ 'bind_l2tp_tunnel_namf' => 'Bind-L2TP-Tunnel-Name',
+ 'bind_ses_context' => 'Bind_Ses_Context',
+ 'bind_ses_contexu' => 'Bind-Ses-Context',
+ 'bind_sub_password' => 'Bind_Sub_Password',
+ 'bind_sub_passwore' => 'Bind-Sub-Password',
+ 'bind_sub_user_at_context' => 'Bind_Sub_User_At_Context',
+ 'bind_sub_user_at_contexu' => 'Bind-Sub-User-At-Context',
+ 'bind_tun_context' => 'Bind_Tun_Context',
+ 'bind_tun_contexu' => 'Bind-Tun-Context',
+ 'bind_type' => 'Bind_Type',
+ 'bind_typf' => 'Bind-Type',
+ 'bintec_bibodialtable' => 'BinTec-biboDialTable',
+ 'bintec_biboppptable' => 'BinTec-biboPPPTable',
+ 'bintec_ipextiftable' => 'BinTec-ipExtIfTable',
+ 'bintec_ipextrttable' => 'BinTec-ipExtRtTable',
+ 'bintec_ipfiltertable' => 'BinTec-ipFilterTable',
+ 'bintec_ipnatpresettable' => 'BinTec-ipNatPresetTable',
+ 'bintec_ipqostable' => 'BinTec-ipQoSTable',
+ 'bintec_iproutetable' => 'BinTec-ipRouteTable',
+ 'bintec_ipxcirctable' => 'BinTec-ipxCircTable',
+ 'bintec_ipxstaticroutetab' => 'BinTec-ipxStaticRouteTable',
+ 'bintec_ipxstaticservtabl' => 'BinTec-ipxStaticServTable',
+ 'bintec_ospfiftable' => 'BinTec-ospfIfTable',
+ 'bintec_pppextiftable' => 'BinTec-pppExtIfTable',
+ 'bintec_qosiftable' => 'BinTec-qosIfTable',
+ 'bintec_qospolicytable' => 'BinTec-qosPolicyTable',
+ 'bintec_ripcirctable' => 'BinTec-ripCircTable',
+ 'bintec_sapcirctable' => 'BinTec-sapCircTable',
+ 'bridge_group' => 'Bridge_Group',
+ 'bridge_grouq' => 'Bridge-Group',
+ 'cabletron_protocol_calla' => 'Cabletron-Protocol-Callable',
+ 'cabletron_protocol_enabl' => 'Cabletron-Protocol-Enable',
+ 'call_id' => 'call-id',
+ 'callback_id' => 'Callback-Id',
+ 'callback_number' => 'Callback-Number',
+ 'called_station_id' => 'Called-Station-Id',
+ 'caller_id' => 'Caller-ID',
+ 'calling_station_id' => 'Calling-Station-Id',
+ 'cbbsm_bandwidth' => 'CBBSM-Bandwidth',
+ 'challenge_state' => 'Challenge-State',
+ 'chap_challenge' => 'CHAP-Challenge',
+ 'chap_password' => 'CHAP-Password',
+ 'char_noecho' => 'Char-Noecho',
+ 'cisco_abort_cause' => 'Cisco-Abort-Cause',
+ 'cisco_account_info' => 'Cisco-Account-Info',
+ 'cisco_assign_ip_pool' => 'Cisco-Assign-IP-Pool',
+ 'cisco_avpair' => 'Cisco-AVPair',
+ 'cisco_call_filter' => 'Cisco-Call-Filter',
+ 'cisco_call_type' => 'Cisco-Call-Type',
+ 'cisco_command_code' => 'Cisco-Command-Code',
+ 'cisco_control_info' => 'Cisco-Control-Info',
+ 'cisco_data_filter' => 'Cisco-Data-Filter',
+ 'cisco_data_rate' => 'Cisco-Data-Rate',
+ 'cisco_disconnect_cause' => 'Cisco-Disconnect-Cause',
+ 'cisco_email_server_ack_f' => 'Cisco-Email-Server-Ack-Flag',
+ 'cisco_email_server_addre' => 'Cisco-Email-Server-Address',
+ 'cisco_fax_account_id_ori' => 'Cisco-Fax-Account-Id-Origin',
+ 'cisco_fax_auth_status' => 'Cisco-Fax-Auth-Status',
+ 'cisco_fax_connect_speed' => 'Cisco-Fax-Connect-Speed',
+ 'cisco_fax_coverpage_flag' => 'Cisco-Fax-Coverpage-Flag',
+ 'cisco_fax_dsn_address' => 'Cisco-Fax-Dsn-Address',
+ 'cisco_fax_dsn_flag' => 'Cisco-Fax-Dsn-Flag',
+ 'cisco_fax_mdn_address' => 'Cisco-Fax-Mdn-Address',
+ 'cisco_fax_mdn_flag' => 'Cisco-Fax-Mdn-Flag',
+ 'cisco_fax_modem_time' => 'Cisco-Fax-Modem-Time',
+ 'cisco_fax_msg_id' => 'Cisco-Fax-Msg-Id',
+ 'cisco_fax_pages' => 'Cisco-Fax-Pages',
+ 'cisco_fax_process_abort_' => 'Cisco-Fax-Process-Abort-Flag',
+ 'cisco_fax_recipient_coun' => 'Cisco-Fax-Recipient-Count',
+ 'cisco_gateway_id' => 'Cisco-Gateway-Id',
+ 'cisco_idle_limit' => 'Cisco-Idle-Limit',
+ 'cisco_ip_direct' => 'Cisco-IP-Direct',
+ 'cisco_ip_pool_definition' => 'Cisco-IP-Pool-Definition',
+ 'cisco_link_compression' => 'Cisco-Link-Compression',
+ 'cisco_maximum_channels' => 'Cisco-Maximum-Channels',
+ 'cisco_maximum_time' => 'Cisco-Maximum-Time',
+ 'cisco_multilink_id' => 'Cisco-Multilink-ID',
+ 'cisco_nas_port' => 'Cisco-NAS-Port',
+ 'cisco_num_in_multilink' => 'Cisco-Num-In-Multilink',
+ 'cisco_port_used' => 'Cisco-Port-Used',
+ 'cisco_ppp_async_map' => 'Cisco-PPP-Async-Map',
+ 'cisco_ppp_vj_slot_comp' => 'Cisco-PPP-VJ-Slot-Comp',
+ 'cisco_pre_input_octets' => 'Cisco-Pre-Input-Octets',
+ 'cisco_pre_input_packets' => 'Cisco-Pre-Input-Packets',
+ 'cisco_pre_output_octets' => 'Cisco-Pre-Output-Octets',
+ 'cisco_pre_output_packets' => 'Cisco-Pre-Output-Packets',
+ 'cisco_presession_time' => 'Cisco-PreSession-Time',
+ 'cisco_pw_lifetime' => 'Cisco-PW-Lifetime',
+ 'cisco_route_ip' => 'Cisco-Route-IP',
+ 'cisco_service_info' => 'Cisco-Service-Info',
+ 'cisco_target_util' => 'Cisco-Target-Util',
+ 'cisco_xmit_rate' => 'Cisco-Xmit-Rate',
+ 'class' => 'Class',
+ 'client_dns_pri' => 'Client_DNS_Pri',
+ 'client_dns_prj' => 'Client-DNS-Pri',
+ 'client_dns_sec' => 'Client_DNS_Sec',
+ 'client_dns_sed' => 'Client-DNS-Sec',
+ 'client_id' => 'Client-Id',
+ 'client_ip_address' => 'Client-IP-Address',
+ 'client_port_dnis' => 'Client-Port-DNIS',
+ 'client_port_id' => 'Client-Port-Id',
+ 'colubris_avpair' => 'Colubris-AVPair',
+ 'configuration_token' => 'Configuration-Token',
+ 'connect_info' => 'Connect-Info',
+ 'connect_rate' => 'Connect-Rate',
+ 'context_name' => 'Context_Name',
+ 'context_namf' => 'Context-Name',
+ 'crypt_password' => 'Crypt-Password',
+ 'current_time' => 'Current-Time',
+ 'cvpn3000_access_hours' => 'CVPN3000-Access-Hours',
+ 'cvpn3000_allow_network_e' => 'CVPN3000-Allow-Network-Extension-Mode',
+ 'cvpn3000_auth_server_pas' => 'CVPN3000-Auth-Server-Password',
+ 'cvpn3000_auth_server_pri' => 'CVPN3000-Auth-Server-Priority',
+ 'cvpn3000_auth_server_typ' => 'CVPN3000-Auth-Server-Type',
+ 'cvpn3000_authd_user_idle' => 'CVPN3000-Authd-User-Idle-Timeout',
+ 'cvpn3000_cisco_ip_phone_' => 'CVPN3000-Cisco-IP-Phone-Bypass',
+ 'cvpn3000_dhcp_network_sc' => 'CVPN3000-DHCP-Network-Scope',
+ 'cvpn3000_ike_keep_alives' => 'CVPN3000-IKE-Keep-Alives',
+ 'cvpn3000_ipsec_allow_pas' => 'CVPN3000-IPSec-Allow-Passwd-Store',
+ 'cvpn3000_ipsec_auth_on_r' => 'CVPN3000-IPSec-Auth-On-Rekey',
+ 'cvpn3000_ipsec_authentic' => 'CVPN3000-IPSec-Authentication',
+ 'cvpn3000_ipsec_authoriza' => 'CVPN3000-IPSec-Authorization-Type',
+ 'cvpn3000_ipsec_authorizb' => 'CVPN3000-IPSec-Authorization-Required',
+ 'cvpn3000_ipsec_backup_se' => 'CVPN3000-IPSec-Backup-Servers',
+ 'cvpn3000_ipsec_backup_sf' => 'CVPN3000-IPSec-Backup-Server-List',
+ 'cvpn3000_ipsec_banner1' => 'CVPN3000-IPSec-Banner1',
+ 'cvpn3000_ipsec_banner2' => 'CVPN3000-IPSec-Banner2',
+ 'cvpn3000_ipsec_client_fw' => 'CVPN3000-IPSec-Client-Fw-Filter-Name',
+ 'cvpn3000_ipsec_client_fx' => 'CVPN3000-IPSec-Client-Fw-Filter-Opt',
+ 'cvpn3000_ipsec_confidenc' => 'CVPN3000-IPSec-Confidence-Level',
+ 'cvpn3000_ipsec_default_d' => 'CVPN3000-IPSec-Default-Domain',
+ 'cvpn3000_ipsec_dn_field' => 'CVPN3000-IPSec-DN-Field',
+ 'cvpn3000_ipsec_group_nam' => 'CVPN3000-IPSec-Group-Name',
+ 'cvpn3000_ipsec_ike_peer_' => 'CVPN3000-IPSec-IKE-Peer-ID-Check',
+ 'cvpn3000_ipsec_ip_compre' => 'CVPN3000-IPSec-IP-Compression',
+ 'cvpn3000_ipsec_ltl_keepa' => 'CVPN3000-IPSec-LTL-Keepalives',
+ 'cvpn3000_ipsec_mode_conf' => 'CVPN3000-IPSec-Mode-Config',
+ 'cvpn3000_ipsec_over_udp' => 'CVPN3000-IPSec-Over-UDP',
+ 'cvpn3000_ipsec_over_udp_' => 'CVPN3000-IPSec-Over-UDP-Port',
+ 'cvpn3000_ipsec_reqrd_cli' => 'CVPN3000-IPSec-Reqrd-Client-Fw-Cap',
+ 'cvpn3000_ipsec_sec_assoc' => 'CVPN3000-IPSec-Sec-Association',
+ 'cvpn3000_ipsec_split_dns' => 'CVPN3000-IPSec-Split-DNS-Names',
+ 'cvpn3000_ipsec_split_tun' => 'CVPN3000-IPSec-Split-Tunnel-List',
+ 'cvpn3000_ipsec_split_tuo' => 'CVPN3000-IPSec-Split-Tunneling-Policy',
+ 'cvpn3000_ipsec_tunnel_ty' => 'CVPN3000-IPSec-Tunnel-Type',
+ 'cvpn3000_ipsec_user_grou' => 'CVPN3000-IPSec-User-Group-Lock',
+ 'cvpn3000_l2tp_encryption' => 'CVPN3000-L2TP-Encryption',
+ 'cvpn3000_l2tp_min_auth_p' => 'CVPN3000-L2TP-Min-Auth-Protocol',
+ 'cvpn3000_l2tp_mppc_compr' => 'CVPN3000-L2TP-MPPC-Compression',
+ 'cvpn3000_leap_bypass' => 'CVPN3000-LEAP-Bypass',
+ 'cvpn3000_ms_client_icpt_' => 'CVPN3000-MS-Client-Icpt-DHCP-Conf-Msg',
+ 'cvpn3000_ms_client_subne' => 'CVPN3000-MS-Client-Subnet-Mask',
+ 'cvpn3000_partition_max_s' => 'CVPN3000-Partition-Max-Sessions',
+ 'cvpn3000_partition_mobil' => 'CVPN3000-Partition-Mobile-IP-Key',
+ 'cvpn3000_partition_mobim' => 'CVPN3000-Partition-Mobile-IP-Address',
+ 'cvpn3000_partition_mobin' => 'CVPN3000-Partition-Mobile-IP-SPI',
+ 'cvpn3000_partition_premi' => 'CVPN3000-Partition-Premise-Router',
+ 'cvpn3000_partition_prima' => 'CVPN3000-Partition-Primary-DHCP',
+ 'cvpn3000_partition_secon' => 'CVPN3000-Partition-Secondary-DHCP',
+ 'cvpn3000_pptp_encryption' => 'CVPN3000-PPTP-Encryption',
+ 'cvpn3000_pptp_min_auth_p' => 'CVPN3000-PPTP-Min-Auth-Protocol',
+ 'cvpn3000_pptp_mppc_compr' => 'CVPN3000-PPTP-MPPC-Compression',
+ 'cvpn3000_primary_dns' => 'CVPN3000-Primary-DNS',
+ 'cvpn3000_primary_wins' => 'CVPN3000-Primary-WINS',
+ 'cvpn3000_priority_on_sep' => 'CVPN3000-Priority-On-SEP',
+ 'cvpn3000_reqrd_client_fw' => 'CVPN3000-Reqrd-Client-Fw-Vendor-Code',
+ 'cvpn3000_reqrd_client_fx' => 'CVPN3000-Reqrd-Client-Fw-Product-Code',
+ 'cvpn3000_reqrd_client_fy' => 'CVPN3000-Reqrd-Client-Fw-Description',
+ 'cvpn3000_request_auth_ve' => 'CVPN3000-Request-Auth-Vector',
+ 'cvpn3000_require_hw_clie' => 'CVPN3000-Require-HW-Client-Auth',
+ 'cvpn3000_require_individ' => 'CVPN3000-Require-Individual-User-Auth',
+ 'cvpn3000_secondary_dns' => 'CVPN3000-Secondary-DNS',
+ 'cvpn3000_secondary_wins' => 'CVPN3000-Secondary-WINS',
+ 'cvpn3000_sep_card_assign' => 'CVPN3000-SEP-Card-Assignment',
+ 'cvpn3000_simultaneous_lo' => 'CVPN3000-Simultaneous-Logins',
+ 'cvpn3000_strip_realm' => 'CVPN3000-Strip-Realm',
+ 'cvpn3000_tunneling_proto' => 'CVPN3000-Tunneling-Protocols',
+ 'cvpn3000_use_client_addr' => 'CVPN3000-Use-Client-Address',
+ 'cvpn3000_user_auth_serve' => 'CVPN3000-User-Auth-Server-Name',
+ 'cvpn3000_user_auth_servf' => 'CVPN3000-User-Auth-Server-Port',
+ 'cvpn3000_user_auth_servg' => 'CVPN3000-User-Auth-Server-Secret',
+ 'cvpn5000_client_assigned' => 'CVPN5000-Client-Assigned-IP',
+ 'cvpn5000_client_assignee' => 'CVPN5000-Client-Assigned-IPX',
+ 'cvpn5000_client_real_ip' => 'CVPN5000-Client-Real-IP',
+ 'cvpn5000_echo' => 'CVPN5000-Echo',
+ 'cvpn5000_tunnel_throughp' => 'CVPN5000-Tunnel-Throughput',
+ 'cvpn5000_vpn_groupinfo' => 'CVPN5000-VPN-GroupInfo',
+ 'cvpn5000_vpn_password' => 'CVPN5000-VPN-Password',
+ 'cvx_assign_ip_pool' => 'CVX-Assign-IP-Pool',
+ 'cvx_client_assign_dns' => 'CVX-Client-Assign-DNS',
+ 'cvx_data_filter' => 'CVX-Data-Filter',
+ 'cvx_data_rate' => 'CVX-Data-Rate',
+ 'cvx_disconnect_cause' => 'CVX-Disconnect-Cause',
+ 'cvx_identification' => 'CVX-Identification',
+ 'cvx_idle_limit' => 'CVX-Idle-Limit',
+ 'cvx_ipsvc_aznlvl' => 'CVX-IPSVC-AZNLVL',
+ 'cvx_ipsvc_mask' => 'CVX-IPSVC-Mask',
+ 'cvx_maximum_channels' => 'CVX-Maximum-Channels',
+ 'cvx_modem_begin_modulati' => 'CVX-Modem-Begin-Modulation',
+ 'cvx_modem_begin_recv_lin' => 'CVX-Modem-Begin-Recv-Line-Lvl',
+ 'cvx_modem_data_compressi' => 'CVX-Modem-Data-Compression',
+ 'cvx_modem_end_modulation' => 'CVX-Modem-End-Modulation',
+ 'cvx_modem_end_recv_line_' => 'CVX-Modem-End-Recv-Line-Lvl',
+ 'cvx_modem_error_correcti' => 'CVX-Modem-Error-Correction',
+ 'cvx_modem_local_rate_neg' => 'CVX-Modem-Local-Rate-Negs',
+ 'cvx_modem_local_retrains' => 'CVX-Modem-Local-Retrains',
+ 'cvx_modem_remote_rate_ne' => 'CVX-Modem-Remote-Rate-Negs',
+ 'cvx_modem_remote_retrain' => 'CVX-Modem-Remote-Retrains',
+ 'cvx_modem_retx_packets' => 'CVX-Modem-ReTx-Packets',
+ 'cvx_modem_snr' => 'CVX-Modem-SNR',
+ 'cvx_modem_tx_packets' => 'CVX-Modem-Tx-Packets',
+ 'cvx_multicast_client' => 'CVX-Multicast-Client',
+ 'cvx_multicast_rate_limit' => 'CVX-Multicast-Rate-Limit',
+ 'cvx_multilink_group_numb' => 'CVX-Multilink-Group-Number',
+ 'cvx_multilink_match_info' => 'CVX-Multilink-Match-Info',
+ 'cvx_ppp_address' => 'CVX-PPP-Address',
+ 'cvx_ppp_log_mask' => 'CVX-PPP-Log-Mask',
+ 'cvx_presession_time' => 'CVX-PreSession-Time',
+ 'cvx_primary_dns' => 'CVX-Primary-DNS',
+ 'cvx_radius_redirect' => 'CVX-Radius-Redirect',
+ 'cvx_secondary_dns' => 'CVX-Secondary-DNS',
+ 'cvx_ss7_session_id_type' => 'CVX-SS7-Session-ID-Type',
+ 'cvx_vpop_id' => 'CVX-VPOP-ID',
+ 'cvx_xmit_rate' => 'CVX-Xmit-Rate',
+ 'dhcp_max_leases' => 'DHCP_Max_Leases',
+ 'dhcp_max_leaset' => 'DHCP-Max-Leases',
+ 'dialback_name' => 'Dialback-Name',
+ 'dialback_no' => 'Dialback-No',
+ 'digest_algorithm' => 'Digest-Algorithm',
+ 'digest_attributes' => 'Digest-Attributes',
+ 'digest_body_digest' => 'Digest-Body-Digest',
+ 'digest_cnonce' => 'Digest-CNonce',
+ 'digest_method' => 'Digest-Method',
+ 'digest_nonce' => 'Digest-Nonce',
+ 'digest_nonce_count' => 'Digest-Nonce-Count',
+ 'digest_qop' => 'Digest-QOP',
+ 'digest_realm' => 'Digest-Realm',
+ 'digest_response' => 'Digest-Response',
+ 'digest_uri' => 'Digest-URI',
+ 'digest_user_name' => 'Digest-User-Name',
+ 'eap_code' => 'EAP-Code',
+ 'eap_id' => 'EAP-Id',
+ 'eap_md5_password' => 'EAP-MD5-Password',
+ 'eap_message' => 'EAP-Message',
+ 'eap_sim_any_id_req' => 'EAP-Sim-ANY_ID_REQ',
+ 'eap_sim_checkcode' => 'EAP-Sim-CHECKCODE',
+ 'eap_sim_counter' => 'EAP-Sim-COUNTER',
+ 'eap_sim_counter_too_smal' => 'EAP-Sim-COUNTER_TOO_SMALL',
+ 'eap_sim_encr_data' => 'EAP-Sim-ENCR_DATA',
+ 'eap_sim_extra' => 'EAP-Sim-EXTRA',
+ 'eap_sim_fullauth_id_req' => 'EAP-Sim-FULLAUTH_ID_REQ',
+ 'eap_sim_hmac' => 'EAP-Sim-HMAC',
+ 'eap_sim_identity' => 'EAP-Sim-IDENTITY',
+ 'eap_sim_imsi' => 'EAP-Sim-IMSI',
+ 'eap_sim_iv' => 'EAP-Sim-IV',
+ 'eap_sim_kc1' => 'EAP-Sim-KC1',
+ 'eap_sim_kc2' => 'EAP-Sim-KC2',
+ 'eap_sim_kc3' => 'EAP-Sim-KC3',
+ 'eap_sim_key' => 'EAP-Sim-KEY',
+ 'eap_sim_mac' => 'EAP-Sim-MAC',
+ 'eap_sim_next_pseudonum' => 'EAP-Sim-NEXT_PSEUDONUM',
+ 'eap_sim_next_reauth_id' => 'EAP-Sim-NEXT_REAUTH_ID',
+ 'eap_sim_nonce_mt' => 'EAP-Sim-NONCE_MT',
+ 'eap_sim_nonce_s' => 'EAP-Sim-NONCE_S',
+ 'eap_sim_notification' => 'EAP-Sim-NOTIFICATION',
+ 'eap_sim_padding' => 'EAP-Sim-PADDING',
+ 'eap_sim_permanent_id_req' => 'EAP-Sim-PERMANENT_ID_REQ',
+ 'eap_sim_rand' => 'EAP-Sim-RAND',
+ 'eap_sim_rand1' => 'EAP-Sim-Rand1',
+ 'eap_sim_rand2' => 'EAP-Sim-Rand2',
+ 'eap_sim_rand3' => 'EAP-Sim-Rand3',
+ 'eap_sim_selected_version' => 'EAP-Sim-SELECTED_VERSION',
+ 'eap_sim_sres1' => 'EAP-Sim-SRES1',
+ 'eap_sim_sres2' => 'EAP-Sim-SRES2',
+ 'eap_sim_sres3' => 'EAP-Sim-SRES3',
+ 'eap_sim_state' => 'EAP-Sim-State',
+ 'eap_sim_subtype' => 'EAP-Sim-Subtype',
+ 'eap_sim_version_list' => 'EAP-Sim-VERSION_LIST',
+ 'eap_tls_require_client_c' => 'EAP-TLS-Require-Client-Cert',
+ 'eap_type' => 'EAP-Type',
+ 'eap_type_gtc' => 'EAP-Type-GTC',
+ 'eap_type_identity' => 'EAP-Type-Identity',
+ 'eap_type_leap' => 'EAP-Type-LEAP',
+ 'eap_type_md5' => 'EAP-Type-MD5',
+ 'eap_type_nak' => 'EAP-Type-NAK',
+ 'eap_type_notification' => 'EAP-Type-Notification',
+ 'eap_type_otp' => 'EAP-Type-OTP',
+ 'eap_type_peap' => 'EAP-Type-PEAP',
+ 'eap_type_sim' => 'EAP-Type-SIM',
+ 'eap_type_sim2' => 'EAP-Type-SIM2',
+ 'eap_type_tls' => 'EAP-Type-TLS',
+ 'eap_type_ttls' => 'EAP-Type-TTLS',
+ 'error_cause' => 'Error-Cause',
+ 'erx_address_pool_name' => 'ERX-Address-Pool-Name',
+ 'erx_alternate_cli_access' => 'ERX-Alternate-Cli-Access-Level',
+ 'erx_alternate_cli_vroute' => 'ERX-Alternate-Cli-Vrouter-Name',
+ 'erx_atm_mbs' => 'ERX-Atm-MBS',
+ 'erx_atm_pcr' => 'ERX-Atm-PCR',
+ 'erx_atm_scr' => 'ERX-Atm-SCR',
+ 'erx_atm_service_category' => 'ERX-Atm-Service-Category',
+ 'erx_bearer_type' => 'ERX-Bearer-Type',
+ 'erx_cli_allow_all_vr_acc' => 'ERX-Cli-Allow-All-VR-Access',
+ 'erx_cli_initial_access_l' => 'ERX-Cli-Initial-Access-Level',
+ 'erx_dial_out_number' => 'ERX-Dial-Out-Number',
+ 'erx_egress_policy_name' => 'ERX-Egress-Policy-Name',
+ 'erx_egress_statistics' => 'ERX-Egress-Statistics',
+ 'erx_framed_ip_route_tag' => 'ERX-Framed-Ip-Route-Tag',
+ 'erx_igmp_enable' => 'ERX-Igmp-Enable',
+ 'erx_ingress_policy_name' => 'ERX-Ingress-Policy-Name',
+ 'erx_ingress_statistics' => 'ERX-Ingress-Statistics',
+ 'erx_input_gigapkts' => 'ERX-Input-Gigapkts',
+ 'erx_ipv6_local_interface' => 'ERX-IpV6-Local-Interface',
+ 'erx_ipv6_primary_dns' => 'ERX-Ipv6-Primary-Dns',
+ 'erx_ipv6_secondary_dns' => 'ERX-Ipv6-Secondary-Dns',
+ 'erx_ipv6_virtual_router' => 'ERX-IpV6-Virtual-Router',
+ 'erx_local_loopback_inter' => 'ERX-Local-Loopback-Interface',
+ 'erx_maximum_bps' => 'ERX-Maximum-BPS',
+ 'erx_minimum_bps' => 'ERX-Minimum-BPS',
+ 'erx_output_gigapkts' => 'ERX-Output-Gigapkts',
+ 'erx_ppp_auth_protocol' => 'ERX-PPP-Auth-Protocol',
+ 'erx_ppp_password' => 'ERX-PPP-Password',
+ 'erx_ppp_username' => 'ERX-PPP-Username',
+ 'erx_pppoe_description' => 'ERX-Pppoe-Description',
+ 'erx_pppoe_max_sessions' => 'ERX-Pppoe-Max-Sessions',
+ 'erx_pppoe_url' => 'ERX-Pppoe-Url',
+ 'erx_primary_dns' => 'ERX-Primary-Dns',
+ 'erx_primary_wins' => 'ERX-Primary-Wins',
+ 'erx_qos_profile_interfac' => 'ERX-Qos-Profile-Interface-Type',
+ 'erx_qos_profile_name' => 'ERX-Qos-Profile-Name',
+ 'erx_redirect_vr_name' => 'ERX-Redirect-VR-Name',
+ 'erx_sa_validate' => 'ERX-Sa-Validate',
+ 'erx_secondary_dns' => 'ERX-Secondary-Dns',
+ 'erx_secondary_wins' => 'ERX-Secondary-Wins',
+ 'erx_service_bundle' => 'ERX-Service-Bundle',
+ 'erx_tunnel_interface_id' => 'ERX-Tunnel-Interface-Id',
+ 'erx_tunnel_maximum_sessi' => 'ERX-Tunnel-Maximum-Sessions',
+ 'erx_tunnel_nas_port_meth' => 'ERX-Tunnel-Nas-Port-Method',
+ 'erx_tunnel_password' => 'ERX-Tunnel-Password',
+ 'erx_tunnel_tos' => 'ERX-Tunnel-Tos',
+ 'erx_tunnel_virtual_route' => 'ERX-Tunnel-Virtual-Router',
+ 'erx_virtual_router_name' => 'ERX-Virtual-Router-Name',
+ 'event_timestamp' => 'Event-Timestamp',
+ 'exec_program' => 'Exec-Program',
+ 'exec_program_wait' => 'Exec-Program-Wait',
+ 'expiration' => 'Expiration',
+ 'extreme_netlogin_only' => 'Extreme-Netlogin-Only',
+ 'extreme_netlogin_url' => 'Extreme-Netlogin-Url',
+ 'extreme_netlogin_url_des' => 'Extreme-Netlogin-Url-Desc',
+ 'extreme_netlogin_vlan' => 'Extreme-Netlogin-Vlan',
+ 'fall_through' => 'Fall-Through',
+ 'filter_id' => 'Filter-Id',
+ 'foundry_command_exceptio' => 'Foundry-Command-Exception-Flag',
+ 'foundry_command_string' => 'Foundry-Command-String',
+ 'foundry_inm_privilege' => 'Foundry-INM-Privilege',
+ 'foundry_privilege_level' => 'Foundry-Privilege-Level',
+ 'framed_address' => 'Framed-Address',
+ 'framed_appletalk_link' => 'Framed-AppleTalk-Link',
+ 'framed_appletalk_network' => 'Framed-AppleTalk-Network',
+ 'framed_appletalk_zone' => 'Framed-AppleTalk-Zone',
+ 'framed_callback_id' => 'Framed-Callback-Id',
+ 'framed_compression' => 'Framed-Compression',
+ 'framed_filter_id' => 'Framed-Filter-Id',
+ 'framed_interface_id' => 'Framed-Interface-Id',
+ 'framed_ip_address' => 'Framed-IP-Address',
+ 'framed_ip_netmask' => 'Framed-IP-Netmask',
+ 'framed_ipv6_pool' => 'Framed-IPv6-Pool',
+ 'framed_ipv6_prefix' => 'Framed-IPv6-Prefix',
+ 'framed_ipv6_route' => 'Framed-IPv6-Route',
+ 'framed_ipx_network' => 'Framed-IPX-Network',
+ 'framed_mtu' => 'Framed-MTU',
+ 'framed_netmask' => 'Framed-Netmask',
+ 'framed_pool' => 'Framed-Pool',
+ 'framed_protocol' => 'Framed-Protocol',
+ 'framed_route' => 'Framed-Route',
+ 'framed_routing' => 'Framed-Routing',
+ 'freeradius_proxied_to' => 'FreeRADIUS-Proxied-To',
+ 'gandalf_around_the_corne' => 'Gandalf-Around-The-Corner',
+ 'gandalf_authentication_s' => 'Gandalf-Authentication-String',
+ 'gandalf_calling_line_id_' => 'Gandalf-Calling-Line-ID-1',
+ 'gandalf_calling_line_ida' => 'Gandalf-Calling-Line-ID-2',
+ 'gandalf_channel_group_na' => 'Gandalf-Channel-Group-Name-1',
+ 'gandalf_channel_group_nb' => 'Gandalf-Channel-Group-Name-2',
+ 'gandalf_compression_stat' => 'Gandalf-Compression-Status',
+ 'gandalf_dial_prefix_name' => 'Gandalf-Dial-Prefix-Name-1',
+ 'gandalf_dial_prefix_namf' => 'Gandalf-Dial-Prefix-Name-2',
+ 'gandalf_fwd_broadcast_in' => 'Gandalf-Fwd-Broadcast-In',
+ 'gandalf_fwd_broadcast_ou' => 'Gandalf-Fwd-Broadcast-Out',
+ 'gandalf_fwd_multicast_in' => 'Gandalf-Fwd-Multicast-In',
+ 'gandalf_fwd_multicast_ou' => 'Gandalf-Fwd-Multicast-Out',
+ 'gandalf_fwd_unicast_in' => 'Gandalf-Fwd-Unicast-In',
+ 'gandalf_fwd_unicast_out' => 'Gandalf-Fwd-Unicast-Out',
+ 'gandalf_hunt_group' => 'Gandalf-Hunt-Group',
+ 'gandalf_ipx_spoofing_sta' => 'Gandalf-IPX-Spoofing-State',
+ 'gandalf_ipx_watchdog_spo' => 'Gandalf-IPX-Watchdog-Spoof',
+ 'gandalf_min_outgoing_bea' => 'Gandalf-Min-Outgoing-Bearer',
+ 'gandalf_modem_mode' => 'Gandalf-Modem-Mode',
+ 'gandalf_modem_required_1' => 'Gandalf-Modem-Required-1',
+ 'gandalf_modem_required_2' => 'Gandalf-Modem-Required-2',
+ 'gandalf_operational_mode' => 'Gandalf-Operational-Modes',
+ 'gandalf_phone_number_1' => 'Gandalf-Phone-Number-1',
+ 'gandalf_phone_number_2' => 'Gandalf-Phone-Number-2',
+ 'gandalf_ppp_authenticati' => 'Gandalf-PPP-Authentication',
+ 'gandalf_ppp_ncp_type' => 'Gandalf-PPP-NCP-Type',
+ 'gandalf_remote_lan_name' => 'Gandalf-Remote-LAN-Name',
+ 'gandalf_sap_group_name_1' => 'Gandalf-SAP-Group-Name-1',
+ 'gandalf_sap_group_name_2' => 'Gandalf-SAP-Group-Name-2',
+ 'gandalf_sap_group_name_3' => 'Gandalf-SAP-Group-Name-3',
+ 'gandalf_sap_group_name_4' => 'Gandalf-SAP-Group-Name-4',
+ 'gandalf_sap_group_name_5' => 'Gandalf-SAP-Group-Name-5',
+ 'garderos_location_name' => 'Garderos-Location-Name',
+ 'garderos_service_name' => 'Garderos-Service-Name',
+ 'group' => 'Group',
+ 'group_name' => 'Group-Name',
+ 'gw_final_xlated_cdn' => 'gw-final-xlated-cdn',
+ 'gw_rxd_cdn' => 'gw-rxd-cdn',
+ 'h323_billing_model' => 'h323-billing-model',
+ 'h323_call_origin' => 'h323-call-origin',
+ 'h323_call_type' => 'h323-call-type',
+ 'h323_conf_id' => 'h323-conf-id',
+ 'h323_connect_time' => 'h323-connect-time',
+ 'h323_credit_amount' => 'h323-credit-amount',
+ 'h323_credit_time' => 'h323-credit-time',
+ 'h323_currency' => 'h323-currency',
+ 'h323_disconnect_cause' => 'h323-disconnect-cause',
+ 'h323_disconnect_time' => 'h323-disconnect-time',
+ 'h323_gw_id' => 'h323-gw-id',
+ 'h323_incoming_conf_id' => 'h323-incoming-conf-id',
+ 'h323_preferred_lang' => 'h323-preferred-lang',
+ 'h323_prompt_id' => 'h323-prompt-id',
+ 'h323_redirect_ip_address' => 'h323-redirect-ip-address',
+ 'h323_redirect_number' => 'h323-redirect-number',
+ 'h323_remote_address' => 'h323-remote-address',
+ 'h323_return_code' => 'h323-return-code',
+ 'h323_setup_time' => 'h323-setup-time',
+ 'h323_time_and_day' => 'h323-time-and-day',
+ 'h323_voice_quality' => 'h323-voice-quality',
+ 'hint' => 'Hint',
+ 'huntgroup_name' => 'Huntgroup-Name',
+ 'idle_timeout' => 'Idle-Timeout',
+ 'incoming_req_uri' => 'incoming-req-uri',
+ 'initial_modulation_type' => 'Initial-Modulation-Type',
+ 'ip3_ip_option' => 'IP3-IP-Option',
+ 'ip3_rdata_rate' => 'IP3-RData-Rate',
+ 'ip3_xdata_rate' => 'IP3-XData-Rate',
+ 'ip_address_pool_name' => 'Ip_Address_Pool_Name',
+ 'ip_address_pool_namf' => 'Ip-Address-Pool-Name',
+ 'ip_host_addr' => 'Ip_Host_Addr',
+ 'ip_host_adds' => 'Ip-Host-Addr',
+ 'ip_tos_field' => 'IP_TOS_Field',
+ 'ip_tos_fiele' => 'IP-TOS-Field',
+ 'itk_acct_serv_ip' => 'ITK-Acct-Serv-IP',
+ 'itk_acct_serv_prot' => 'ITK-Acct-Serv-Prot',
+ 'itk_auth_req_type' => 'ITK-Auth-Req-Type',
+ 'itk_auth_serv_ip' => 'ITK-Auth-Serv-IP',
+ 'itk_auth_serv_prot' => 'ITK-Auth-Serv-Prot',
+ 'itk_banner' => 'ITK-Banner',
+ 'itk_channel_binding' => 'ITK-Channel-Binding',
+ 'itk_ddi' => 'ITK-DDI',
+ 'itk_dest_no' => 'ITK-Dest-No',
+ 'itk_dialout_type' => 'ITK-Dialout-Type',
+ 'itk_filter_rule' => 'ITK-Filter-Rule',
+ 'itk_ftp_auth_ip' => 'ITK-Ftp-Auth-IP',
+ 'itk_ip_pool' => 'ITK-IP-Pool',
+ 'itk_isdn_prot' => 'ITK-ISDN-Prot',
+ 'itk_modem_init_string' => 'ITK-Modem-Init-String',
+ 'itk_modem_pool_id' => 'ITK-Modem-Pool-Id',
+ 'itk_nas_name' => 'ITK-NAS-Name',
+ 'itk_password_prompt' => 'ITK-Password-Prompt',
+ 'itk_ppp_auth_type' => 'ITK-PPP-Auth-Type',
+ 'itk_ppp_client_server_mo' => 'ITK-PPP-Client-Server-Mode',
+ 'itk_ppp_compression_prot' => 'ITK-PPP-Compression-Prot',
+ 'itk_prompt' => 'ITK-Prompt',
+ 'itk_provider_id' => 'ITK-Provider-Id',
+ 'itk_start_delay' => 'ITK-Start-Delay',
+ 'itk_tunnel_ip' => 'ITK-Tunnel-IP',
+ 'itk_tunnel_prot' => 'ITK-Tunnel-Prot',
+ 'itk_usergroup' => 'ITK-Usergroup',
+ 'itk_username' => 'ITK-Username',
+ 'itk_username_prompt' => 'ITK-Username-Prompt',
+ 'itk_users_default_entry' => 'ITK-Users-Default-Entry',
+ 'itk_users_default_pw' => 'ITK-Users-Default-Pw',
+ 'itk_welcome_message' => 'ITK-Welcome-Message',
+ 'juniper_allow_commands' => 'Juniper-Allow-Commands',
+ 'juniper_allow_configurat' => 'Juniper-Allow-Configuration',
+ 'juniper_deny_commands' => 'Juniper-Deny-Commands',
+ 'juniper_deny_configurati' => 'Juniper-Deny-Configuration',
+ 'juniper_local_user_name' => 'Juniper-Local-User-Name',
+ 'karlnet_turbocell_name' => 'KarlNet-TurboCell-Name',
+ 'karlnet_turbocell_opmode' => 'KarlNet-TurboCell-OpMode',
+ 'karlnet_turbocell_opstat' => 'KarlNet-TurboCell-OpState',
+ 'karlnet_turbocell_txrate' => 'KarlNet-TurboCell-TxRate',
+ 'lac_port' => 'LAC_Port',
+ 'lac_port_type' => 'LAC_Port_Type',
+ 'lac_port_typf' => 'LAC-Port-Type',
+ 'lac_poru' => 'LAC-Port',
+ 'lac_real_port' => 'LAC_Real_Port',
+ 'lac_real_port_type' => 'LAC_Real_Port_Type',
+ 'lac_real_port_typf' => 'LAC-Real-Port-Type',
+ 'lac_real_poru' => 'LAC-Real-Port',
+ 'ldap_group' => 'Ldap-Group',
+ 'ldap_userdn' => 'Ldap-UserDn',
+ 'le_admin_group' => 'LE-Admin-Group',
+ 'le_advice_of_charge' => 'LE-Advice-of-Charge',
+ 'le_connect_detail' => 'LE-Connect-Detail',
+ 'le_ip_gateway' => 'LE-IP-Gateway',
+ 'le_ip_pool' => 'LE-IP-Pool',
+ 'le_ipsec_active_profile' => 'LE-IPSec-Active-Profile',
+ 'le_ipsec_deny_action' => 'LE-IPSec-Deny-Action',
+ 'le_ipsec_log_options' => 'LE-IPSec-Log-Options',
+ 'le_ipsec_outsource_profi' => 'LE-IPSec-Outsource-Profile',
+ 'le_ipsec_passive_profile' => 'LE-IPSec-Passive-Profile',
+ 'le_modem_info' => 'LE-Modem-Info',
+ 'le_multicast_client' => 'LE-Multicast-Client',
+ 'le_nat_inmap' => 'LE-NAT-Inmap',
+ 'le_nat_log_options' => 'LE-NAT-Log-Options',
+ 'le_nat_other_session_tim' => 'LE-NAT-Other-Session-Timeout',
+ 'le_nat_outmap' => 'LE-NAT-Outmap',
+ 'le_nat_outsource_inmap' => 'LE-NAT-Outsource-Inmap',
+ 'le_nat_outsource_outmap' => 'LE-NAT-Outsource-Outmap',
+ 'le_nat_sess_dir_fail_act' => 'LE-NAT-Sess-Dir-Fail-Action',
+ 'le_nat_tcp_session_timeo' => 'LE-NAT-TCP-Session-Timeout',
+ 'le_terminate_detail' => 'LE-Terminate-Detail',
+ 'lm_password' => 'LM-Password',
+ 'local_web_acct_duration' => 'Local-Web-Acct-Duration',
+ 'local_web_acct_interim_r' => 'Local-Web-Acct-Interim-Rx-Bytes',
+ 'local_web_acct_interim_s' => 'Local-Web-Acct-Interim-Rx-Gigawords',
+ 'local_web_acct_interim_t' => 'Local-Web-Acct-Interim-Tx-Bytes',
+ 'local_web_acct_interim_u' => 'Local-Web-Acct-Interim-Tx-Gigawords',
+ 'local_web_acct_interim_v' => 'Local-Web-Acct-Interim-Tx-Mgmt',
+ 'local_web_acct_interim_w' => 'Local-Web-Acct-Interim-Rx-Mgmt',
+ 'local_web_acct_rx_mgmt' => 'Local-Web-Acct-Rx-Mgmt',
+ 'local_web_acct_time' => 'Local-Web-Acct-Time',
+ 'local_web_acct_tx_mgmt' => 'Local-Web-Acct-Tx-Mgmt',
+ 'local_web_border_router' => 'Local-Web-Border-Router',
+ 'local_web_client_ip' => 'Local-Web-Client-Ip',
+ 'local_web_reauth_counter' => 'Local-Web-Reauth-Counter',
+ 'local_web_rx_limit' => 'Local-Web-Rx-Limit',
+ 'local_web_tx_limit' => 'Local-Web-Tx-Limit',
+ 'login_callback_number' => 'Login-Callback-Number',
+ 'login_host' => 'Login-Host',
+ 'login_ip_host' => 'Login-IP-Host',
+ 'login_ipv6_host' => 'Login-IPv6-Host',
+ 'login_lat_group' => 'Login-LAT-Group',
+ 'login_lat_node' => 'Login-LAT-Node',
+ 'login_lat_port' => 'Login-LAT-Port',
+ 'login_lat_service' => 'Login-LAT-Service',
+ 'login_port' => 'Login-Port',
+ 'login_service' => 'Login-Service',
+ 'login_tcp_port' => 'Login-TCP-Port',
+ 'login_time' => 'Login-Time',
+ 'mcast_maxgroups' => 'Mcast_MaxGroups',
+ 'mcast_maxgroupt' => 'Mcast-MaxGroups',
+ 'mcast_receive' => 'Mcast_Receive',
+ 'mcast_receivf' => 'Mcast-Receive',
+ 'mcast_send' => 'Mcast_Send',
+ 'mcast_sene' => 'Mcast-Send',
+ 'medium_type' => 'Medium_Type',
+ 'medium_typf' => 'Medium-Type',
+ 'menu' => 'Menu',
+ 'merit_proxy_action' => 'Merit-Proxy-Action',
+ 'merit_user_id' => 'Merit-User-Id',
+ 'merit_user_realm' => 'Merit-User-Realm',
+ 'message_authenticator' => 'Message-Authenticator',
+ 'method' => 'method',
+ 'mikrotik_group' => 'Mikrotik-Group',
+ 'mikrotik_recv_limit' => 'Mikrotik-Recv-Limit',
+ 'mikrotik_xmit_limit' => 'Mikrotik-Xmit-Limit',
+ 'module_failure_message' => 'Module-Failure-Message',
+ 'module_success_message' => 'Module-Success-Message',
+ 'motorola_canopy_cirenabl' => 'Motorola-Canopy-CIRENABLE',
+ 'motorola_canopy_dlba' => 'Motorola-Canopy-DLBA',
+ 'motorola_canopy_enable' => 'Motorola-Canopy-Enable',
+ 'motorola_canopy_higherbw' => 'Motorola-Canopy-HIGHERBW',
+ 'motorola_canopy_hpcenabl' => 'Motorola-Canopy-HPCENABLE',
+ 'motorola_canopy_hpsdldr' => 'Motorola-Canopy-HPSDLDR',
+ 'motorola_canopy_hpsuldr' => 'Motorola-Canopy-HPSULDR',
+ 'motorola_canopy_lpsdldr' => 'Motorola-Canopy-LPSDLDR',
+ 'motorola_canopy_lpsuldr' => 'Motorola-Canopy-LPSULDR',
+ 'motorola_canopy_sdldr' => 'Motorola-Canopy-SDLDR',
+ 'motorola_canopy_shared_s' => 'Motorola-Canopy-Shared-Secret',
+ 'motorola_canopy_suldr' => 'Motorola-Canopy-SULDR',
+ 'motorola_canopy_ulba' => 'Motorola-Canopy-ULBA',
+ 'ms_acct_auth_type' => 'MS-Acct-Auth-Type',
+ 'ms_acct_eap_type' => 'MS-Acct-EAP-Type',
+ 'ms_arap_pw_change_reason' => 'MS-ARAP-PW-Change-Reason',
+ 'ms_bap_usage' => 'MS-BAP-Usage',
+ 'ms_chap2_cpw' => 'MS-CHAP2-CPW',
+ 'ms_chap2_response' => 'MS-CHAP2-Response',
+ 'ms_chap2_success' => 'MS-CHAP2-Success',
+ 'ms_chap_challenge' => 'MS-CHAP-Challenge',
+ 'ms_chap_cpw_1' => 'MS-CHAP-CPW-1',
+ 'ms_chap_cpw_2' => 'MS-CHAP-CPW-2',
+ 'ms_chap_domain' => 'MS-CHAP-Domain',
+ 'ms_chap_error' => 'MS-CHAP-Error',
+ 'ms_chap_lm_enc_pw' => 'MS-CHAP-LM-Enc-PW',
+ 'ms_chap_mppe_keys' => 'MS-CHAP-MPPE-Keys',
+ 'ms_chap_nt_enc_pw' => 'MS-CHAP-NT-Enc-PW',
+ 'ms_chap_response' => 'MS-CHAP-Response',
+ 'ms_chap_use_ntlm_auth' => 'MS-CHAP-Use-NTLM-Auth',
+ 'ms_filter' => 'MS-Filter',
+ 'ms_link_drop_time_limit' => 'MS-Link-Drop-Time-Limit',
+ 'ms_link_utilization_thre' => 'MS-Link-Utilization-Threshold',
+ 'ms_mppe_encryption_polic' => 'MS-MPPE-Encryption-Policy',
+ 'ms_mppe_encryption_type' => 'MS-MPPE-Encryption-Type',
+ 'ms_mppe_encryption_types' => 'MS-MPPE-Encryption-Types',
+ 'ms_mppe_recv_key' => 'MS-MPPE-Recv-Key',
+ 'ms_mppe_send_key' => 'MS-MPPE-Send-Key',
+ 'ms_new_arap_password' => 'MS-New-ARAP-Password',
+ 'ms_old_arap_password' => 'MS-Old-ARAP-Password',
+ 'ms_primary_dns_server' => 'MS-Primary-DNS-Server',
+ 'ms_primary_nbns_server' => 'MS-Primary-NBNS-Server',
+ 'ms_ras_vendor' => 'MS-RAS-Vendor',
+ 'ms_ras_version' => 'MS-RAS-Version',
+ 'ms_secondary_dns_server' => 'MS-Secondary-DNS-Server',
+ 'ms_secondary_nbns_server' => 'MS-Secondary-NBNS-Server',
+ 'multi_link_flag' => 'Multi-Link-Flag',
+ 'nas_identifier' => 'NAS-Identifier',
+ 'nas_ip_address' => 'NAS-IP-Address',
+ 'nas_ipv6_address' => 'NAS-IPv6-Address',
+ 'nas_port' => 'NAS-Port',
+ 'nas_port_id' => 'NAS-Port-Id',
+ 'nas_port_type' => 'NAS-Port-Type',
+ 'nas_real_port' => 'NAS_Real_Port',
+ 'nas_real_poru' => 'NAS-Real-Port',
+ 'navini_avpair' => 'Navini-AVPair',
+ 'next_hop_dn' => 'next-hop-dn',
+ 'next_hop_ip' => 'next-hop-ip',
+ 'nn_data_rate' => 'NN-Data-Rate',
+ 'nn_data_rate_ceiling' => 'NN-Data-Rate-Ceiling',
+ 'nn_homenode' => 'NN-Homenode',
+ 'nn_homeservice' => 'NN-Homeservice',
+ 'nn_homeservice_name' => 'NN-Homeservice-Name',
+ 'no_such_attribute' => 'No-Such-Attribute',
+ 'nokia_charging_id' => 'Nokia-Charging-Id',
+ 'nokia_ggsn_ip_address' => 'Nokia-GGSN-IP-Address',
+ 'nokia_imsi' => 'Nokia-IMSI',
+ 'nokia_prepaid_ind' => 'Nokia-Prepaid-Ind',
+ 'nokia_sgsn_ip_address' => 'Nokia-SGSN-IP-Address',
+ 'nomadix_bw_down' => 'Nomadix-Bw-Down',
+ 'nomadix_bw_up' => 'Nomadix-Bw-Up',
+ 'nomadix_config_url' => 'Nomadix-Config-URL',
+ 'nomadix_endofsession' => 'Nomadix-EndofSession',
+ 'nomadix_expiration' => 'Nomadix-Expiration',
+ 'nomadix_goodbye_url' => 'Nomadix-Goodbye-URL',
+ 'nomadix_ip_upsell' => 'Nomadix-IP-Upsell',
+ 'nomadix_logoff_url' => 'Nomadix-Logoff-URL',
+ 'nomadix_maxbytesdown' => 'Nomadix-MaxBytesDown',
+ 'nomadix_maxbytesup' => 'Nomadix-MaxBytesUp',
+ 'nomadix_net_vlan' => 'Nomadix-Net-VLAN',
+ 'nomadix_subnet' => 'Nomadix-Subnet',
+ 'nomadix_url_redirection' => 'Nomadix-URL-Redirection',
+ 'ns_admin_privilege' => 'NS-Admin-Privilege',
+ 'ns_mta_md5_password' => 'NS-MTA-MD5-Password',
+ 'ns_primary_dns' => 'NS-Primary-DNS',
+ 'ns_primary_wins' => 'NS-Primary-WINS',
+ 'ns_secondary_dns' => 'NS-Secondary-DNS',
+ 'ns_secondary_wins' => 'NS-Secondary-WINS',
+ 'ns_user_group' => 'NS-User-Group',
+ 'ns_vsys_name' => 'NS-VSYS-Name',
+ 'nt_password' => 'NT-Password',
+ 'ntlm_user_name' => 'NTLM-User-Name',
+ 'old_password' => 'Old-Password',
+ 'outgoing_req_uri' => 'outgoing-req-uri',
+ 'packet_dst_port' => 'Packet-Dst-Port',
+ 'packet_type' => 'Packet-Type',
+ 'pam_auth' => 'Pam-Auth',
+ 'password' => 'Password',
+ 'password_retry' => 'Password-Retry',
+ 'police_burst' => 'Police_Burst',
+ 'police_bursu' => 'Police-Burst',
+ 'police_rate' => 'Police_Rate',
+ 'police_ratf' => 'Police-Rate',
+ 'pool_name' => 'Pool-Name',
+ 'port_limit' => 'Port-Limit',
+ 'port_message' => 'Port-Message',
+ 'post_auth_type' => 'Post-Auth-Type',
+ 'post_proxy_type' => 'Post-Proxy-Type',
+ 'postauth_type' => 'PostAuth-Type',
+ 'pppoe_motm' => 'PPPOE_MOTM',
+ 'pppoe_motn' => 'PPPOE-MOTM',
+ 'pppoe_url' => 'PPPOE_URL',
+ 'pppoe_urm' => 'PPPOE-URL',
+ 'pre_acct_type' => 'Pre-Acct-Type',
+ 'pre_proxy_type' => 'Pre-Proxy-Type',
+ 'prefix' => 'Prefix',
+ 'prev_hop_ip' => 'prev-hop-ip',
+ 'prev_hop_via' => 'prev-hop-via',
+ 'prompt' => 'Prompt',
+ 'propel_accelerate' => 'Propel-Accelerate',
+ 'propel_client_ip_address' => 'Propel-Client-IP-Address',
+ 'propel_client_nas_ip_add' => 'Propel-Client-NAS-IP-Address',
+ 'propel_client_source_id' => 'Propel-Client-Source-ID',
+ 'propel_dialed_digits' => 'Propel-Dialed-Digits',
+ 'proxy_state' => 'Proxy-State',
+ 'proxy_to_realm' => 'Proxy-To-Realm',
+ 'pvc_circuit_padding' => 'PVC_Circuit_Padding',
+ 'pvc_circuit_paddinh' => 'PVC-Circuit-Padding',
+ 'pvc_encapsulation_type' => 'PVC_Encapsulation_Type',
+ 'pvc_encapsulation_typf' => 'PVC-Encapsulation-Type',
+ 'pvc_profile_name' => 'PVC_Profile_Name',
+ 'pvc_profile_namf' => 'PVC-Profile-Name',
+ 'quintum_avpair' => 'Quintum-AVPair',
+ 'quintum_h323_billing_mod' => 'Quintum-h323-billing-model',
+ 'quintum_h323_call_origin' => 'Quintum-h323-call-origin',
+ 'quintum_h323_call_type' => 'Quintum-h323-call-type',
+ 'quintum_h323_conf_id' => 'Quintum-h323-conf-id',
+ 'quintum_h323_connect_tim' => 'Quintum-h323-connect-time',
+ 'quintum_h323_credit_amou' => 'Quintum-h323-credit-amount',
+ 'quintum_h323_credit_time' => 'Quintum-h323-credit-time',
+ 'quintum_h323_currency_ty' => 'Quintum-h323-currency-type',
+ 'quintum_h323_disconnect_' => 'Quintum-h323-disconnect-time',
+ 'quintum_h323_disconnecta' => 'Quintum-h323-disconnect-cause',
+ 'quintum_h323_gw_id' => 'Quintum-h323-gw-id',
+ 'quintum_h323_incoming_co' => 'Quintum-h323-incoming-conf-id',
+ 'quintum_h323_preferred_l' => 'Quintum-h323-preferred-lang',
+ 'quintum_h323_prompt_id' => 'Quintum-h323-prompt-id',
+ 'quintum_h323_redirect_ip' => 'Quintum-h323-redirect-ip-address',
+ 'quintum_h323_redirect_nu' => 'Quintum-h323-redirect-number',
+ 'quintum_h323_remote_addr' => 'Quintum-h323-remote-address',
+ 'quintum_h323_return_code' => 'Quintum-h323-return-code',
+ 'quintum_h323_setup_time' => 'Quintum-h323-setup-time',
+ 'quintum_h323_time_and_da' => 'Quintum-h323-time-and-day',
+ 'quintum_h323_voice_quali' => 'Quintum-h323-voice-quality',
+ 'quintum_nas_port' => 'Quintum-NAS-Port',
+ 'rate_limit_burst' => 'Rate_Limit_Burst',
+ 'rate_limit_bursu' => 'Rate-Limit-Burst',
+ 'rate_limit_rate' => 'Rate_Limit_Rate',
+ 'rate_limit_ratf' => 'Rate-Limit-Rate',
+ 'realm' => 'Realm',
+ 'redcreek_tunneled_dns_se' => 'RedCreek-Tunneled-DNS-Server',
+ 'redcreek_tunneled_domain' => 'RedCreek-Tunneled-DomainName',
+ 'redcreek_tunneled_gatewa' => 'RedCreek-Tunneled-Gateway',
+ 'redcreek_tunneled_hostna' => 'RedCreek-Tunneled-HostName',
+ 'redcreek_tunneled_ip_add' => 'RedCreek-Tunneled-IP-Addr',
+ 'redcreek_tunneled_ip_net' => 'RedCreek-Tunneled-IP-Netmask',
+ 'redcreek_tunneled_search' => 'RedCreek-Tunneled-Search-List',
+ 'redcreek_tunneled_wins_s' => 'RedCreek-Tunneled-WINS-Server1',
+ 'redcreek_tunneled_wins_t' => 'RedCreek-Tunneled-WINS-Server2',
+ 'replicate_to_realm' => 'Replicate-To-Realm',
+ 'reply_message' => 'Reply-Message',
+ 'response_packet_type' => 'Response-Packet-Type',
+ 'rewrite_rule' => 'Rewrite-Rule',
+ 'sdx_service_name' => 'Sdx-Service-Name',
+ 'sdx_session_volume_quota' => 'Sdx-Session-Volume-Quota',
+ 'sdx_tunnel_disconnect_ca' => 'Sdx-Tunnel-Disconnect-Cause-Info',
+ 'service_type' => 'Service-Type',
+ 'session' => 'Session',
+ 'session_error_code' => 'Session_Error_Code',
+ 'session_error_codf' => 'Session-Error-Code',
+ 'session_error_msg' => 'Session_Error_Msg',
+ 'session_error_msh' => 'Session-Error-Msg',
+ 'session_protocol' => 'session-protocol',
+ 'session_timeout' => 'Session-Timeout',
+ 'session_type' => 'Session-Type',
+ 'shasta_service_profile' => 'Shasta-Service-Profile',
+ 'shasta_user_privilege' => 'Shasta-User-Privilege',
+ 'shasta_vpn_name' => 'Shasta-VPN-Name',
+ 'shiva_acct_serv_switch' => 'Shiva-Acct-Serv-Switch',
+ 'shiva_called_number' => 'Shiva-Called-Number',
+ 'shiva_calling_number' => 'Shiva-Calling-Number',
+ 'shiva_compression_type' => 'Shiva-Compression-Type',
+ 'shiva_connect_reason' => 'Shiva-Connect-Reason',
+ 'shiva_customer_id' => 'Shiva-Customer-Id',
+ 'shiva_disconnect_reason' => 'Shiva-Disconnect-Reason',
+ 'shiva_event_flags' => 'Shiva-Event-Flags',
+ 'shiva_function' => 'Shiva-Function',
+ 'shiva_link_protocol' => 'Shiva-Link-Protocol',
+ 'shiva_link_speed' => 'Shiva-Link-Speed',
+ 'shiva_links_in_bundle' => 'Shiva-Links-In-Bundle',
+ 'shiva_network_protocols' => 'Shiva-Network-Protocols',
+ 'shiva_session_id' => 'Shiva-Session-Id',
+ 'shiva_type_of_service' => 'Shiva-Type-Of-Service',
+ 'shiva_user_attributes' => 'Shiva-User-Attributes',
+ 'simultaneous_use' => 'Simultaneous-Use',
+ 'sip_from' => 'Sip-From',
+ 'sip_hdr' => 'sip-hdr',
+ 'sip_method' => 'Sip-Method',
+ 'sip_to' => 'Sip-To',
+ 'sip_translated_request_u' => 'Sip-Translated-Request-URI',
+ 'smb_account_ctrl' => 'SMB-Account-CTRL',
+ 'smb_account_ctrl_text' => 'SMB-Account-CTRL-TEXT',
+ 'sonicwall_user_group' => 'SonicWall-User-Group',
+ 'sonicwall_user_privilege' => 'SonicWall-User-Privilege',
+ 'source_validation' => 'Source_Validation',
+ 'source_validatioo' => 'Source-Validation',
+ 'sql_group' => 'Sql-Group',
+ 'sql_user_name' => 'SQL-User-Name',
+ 'ss3_firewall_user_privil' => 'SS3-Firewall-User-Privilege',
+ 'st_acct_vc_connection_id' => 'ST-Acct-VC-Connection-Id',
+ 'st_policy_name' => 'ST-Policy-Name',
+ 'st_primary_dns_server' => 'ST-Primary-DNS-Server',
+ 'st_primary_nbns_server' => 'ST-Primary-NBNS-Server',
+ 'st_secondary_dns_server' => 'ST-Secondary-DNS-Server',
+ 'st_secondary_nbns_server' => 'ST-Secondary-NBNS-Server',
+ 'st_service_domain' => 'ST-Service-Domain',
+ 'st_service_name' => 'ST-Service-Name',
+ 'state' => 'State',
+ 'strip_user_name' => 'Strip-User-Name',
+ 'stripped_user_name' => 'Stripped-User-Name',
+ 'subscriber' => 'subscriber',
+ 'suffix' => 'Suffix',
+ 'telebit_accounting_info' => 'Telebit-Accounting-Info',
+ 'telebit_activate_command' => 'Telebit-Activate-Command',
+ 'telebit_login_command' => 'Telebit-Login-Command',
+ 'telebit_port_name' => 'Telebit-Port-Name',
+ 'termination_action' => 'Termination-Action',
+ 'termination_menu' => 'Termination-Menu',
+ 'trapeze_encryption_type' => 'Trapeze-Encryption-Type',
+ 'trapeze_end_date' => 'Trapeze-End-Date',
+ 'trapeze_mobility_profile' => 'Trapeze-Mobility-Profile',
+ 'trapeze_ssid' => 'Trapeze-SSID',
+ 'trapeze_start_date' => 'Trapeze-Start-Date',
+ 'trapeze_time_of_day' => 'Trapeze-Time-Of-Day',
+ 'trapeze_url' => 'Trapeze-URL',
+ 'trapeze_vlan_name' => 'Trapeze-VLAN-Name',
+ 'tty_level_max' => 'TTY_Level_Max',
+ 'tty_level_may' => 'TTY-Level-Max',
+ 'tty_level_start' => 'TTY_Level_Start',
+ 'tty_level_staru' => 'TTY-Level-Start',
+ 'tunnel_algorithm' => 'Tunnel_Algorithm',
+ 'tunnel_algorithn' => 'Tunnel-Algorithm',
+ 'tunnel_assignment_id' => 'Tunnel-Assignment-Id',
+ 'tunnel_client_auth_id' => 'Tunnel-Client-Auth-Id',
+ 'tunnel_client_endpoint' => 'Tunnel-Client-Endpoint',
+ 'tunnel_cmd_timeout' => 'Tunnel_Cmd_Timeout',
+ 'tunnel_cmd_timeouu' => 'Tunnel-Cmd-Timeout',
+ 'tunnel_connection_id' => 'Tunnel-Connection-Id',
+ 'tunnel_context' => 'Tunnel_Context',
+ 'tunnel_contexu' => 'Tunnel-Context',
+ 'tunnel_deadtime' => 'Tunnel_Deadtime',
+ 'tunnel_deadtimf' => 'Tunnel-Deadtime',
+ 'tunnel_dnis' => 'Tunnel_DNIS',
+ 'tunnel_dnit' => 'Tunnel-DNIS',
+ 'tunnel_domain' => 'Tunnel_Domain',
+ 'tunnel_domaio' => 'Tunnel-Domain',
+ 'tunnel_function' => 'Tunnel_Function',
+ 'tunnel_functioo' => 'Tunnel-Function',
+ 'tunnel_group' => 'Tunnel_Group',
+ 'tunnel_grouq' => 'Tunnel-Group',
+ 'tunnel_l2f_second_passwo' => 'Tunnel_L2F_Second_Password',
+ 'tunnel_l2f_second_passwp' => 'Tunnel-L2F-Second-Password',
+ 'tunnel_local_name' => 'Tunnel_Local_Name',
+ 'tunnel_local_namf' => 'Tunnel-Local-Name',
+ 'tunnel_max_sessions' => 'Tunnel_Max_Sessions',
+ 'tunnel_max_sessiont' => 'Tunnel-Max-Sessions',
+ 'tunnel_max_tunnels' => 'Tunnel_Max_Tunnels',
+ 'tunnel_max_tunnelt' => 'Tunnel-Max-Tunnels',
+ 'tunnel_medium_type' => 'Tunnel-Medium-Type',
+ 'tunnel_password' => 'Tunnel-Password',
+ 'tunnel_police_burst' => 'Tunnel_Police_Burst',
+ 'tunnel_police_bursu' => 'Tunnel-Police-Burst',
+ 'tunnel_police_rate' => 'Tunnel_Police_Rate',
+ 'tunnel_police_ratf' => 'Tunnel-Police-Rate',
+ 'tunnel_preference' => 'Tunnel-Preference',
+ 'tunnel_private_group_id' => 'Tunnel-Private-Group-Id',
+ 'tunnel_rate_limit_burst' => 'Tunnel_Rate_Limit_Burst',
+ 'tunnel_rate_limit_bursu' => 'Tunnel-Rate-Limit-Burst',
+ 'tunnel_rate_limit_rate' => 'Tunnel_Rate_Limit_Rate',
+ 'tunnel_rate_limit_ratf' => 'Tunnel-Rate-Limit-Rate',
+ 'tunnel_remote_name' => 'Tunnel_Remote_Name',
+ 'tunnel_remote_namf' => 'Tunnel-Remote-Name',
+ 'tunnel_retransmit' => 'Tunnel_Retransmit',
+ 'tunnel_retransmiu' => 'Tunnel-Retransmit',
+ 'tunnel_server_auth_id' => 'Tunnel-Server-Auth-Id',
+ 'tunnel_server_endpoint' => 'Tunnel-Server-Endpoint',
+ 'tunnel_session_auth' => 'Tunnel_Session_Auth',
+ 'tunnel_session_auth_ctx' => 'Tunnel_Session_Auth_Ctx',
+ 'tunnel_session_auth_cty' => 'Tunnel-Session-Auth-Ctx',
+ 'tunnel_session_auth_serv' => 'Tunnel_Session_Auth_Service_Grp',
+ 'tunnel_session_auth_serw' => 'Tunnel-Session-Auth-Service-Grp',
+ 'tunnel_session_auti' => 'Tunnel-Session-Auth',
+ 'tunnel_type' => 'Tunnel-Type',
+ 'tunnel_window' => 'Tunnel_Window',
+ 'tunnel_windox' => 'Tunnel-Window',
+ 'unix_ftp_gid' => 'Unix-FTP-GID',
+ 'unix_ftp_group_ids' => 'Unix-FTP-Group-Ids',
+ 'unix_ftp_group_names' => 'Unix-FTP-Group-Names',
+ 'unix_ftp_home' => 'Unix-FTP-Home',
+ 'unix_ftp_shell' => 'Unix-FTP-Shell',
+ 'unix_ftp_uid' => 'Unix-FTP-UID',
+ 'user_category' => 'User-Category',
+ 'user_name' => 'User-Name',
+ 'user_name_is_star' => 'User-Name-Is-Star',
+ 'user_password' => 'User-Password',
+ 'user_profile' => 'User-Profile',
+ 'user_service_type' => 'User-Service-Type',
+ 'usr_accm_type' => 'USR-ACCM-Type',
+ 'usr_acct_reason_code' => 'USR-Acct-Reason-Code',
+ 'usr_actual_voltage' => 'USR-Actual-Voltage',
+ 'usr_appletalk' => 'USR-Appletalk',
+ 'usr_appletalk_network_ra' => 'USR-Appletalk-Network-Range',
+ 'usr_at_call_input_filter' => 'USR-AT-Call-Input-Filter',
+ 'usr_at_call_output_filte' => 'USR-AT-Call-Output-Filter',
+ 'usr_at_input_filter' => 'USR-AT-Input-Filter',
+ 'usr_at_output_filter' => 'USR-AT-Output-Filter',
+ 'usr_at_rtmp_input_filter' => 'USR-AT-RTMP-Input-Filter',
+ 'usr_at_rtmp_output_filte' => 'USR-AT-RTMP-Output-Filter',
+ 'usr_at_zip_input_filter' => 'USR-AT-Zip-Input-Filter',
+ 'usr_at_zip_output_filter' => 'USR-AT-Zip-Output-Filter',
+ 'usr_auth_mode' => 'USR-Auth-Mode',
+ 'usr_back_channel_data_ra' => 'USR-Back-Channel-Data-Rate',
+ 'usr_bearer_capabilities' => 'USR-Bearer-Capabilities',
+ 'usr_block_error_count_li' => 'USR-Block-Error-Count-Limit',
+ 'usr_blocks_received' => 'USR-Blocks-Received',
+ 'usr_blocks_resent' => 'USR-Blocks-Resent',
+ 'usr_blocks_sent' => 'USR-Blocks-Sent',
+ 'usr_bridging' => 'USR-Bridging',
+ 'usr_call_arrival_in_gmt' => 'USR-Call-Arrival-in-GMT',
+ 'usr_call_arrival_time' => 'USR-Call-Arrival-Time',
+ 'usr_call_connect_in_gmt' => 'USR-Call-Connect-in-GMT',
+ 'usr_call_connecting_time' => 'USR-Call-Connecting-Time',
+ 'usr_call_end_date_time' => 'USR-Call-End-Date-Time',
+ 'usr_call_end_time' => 'USR-Call-End-Time',
+ 'usr_call_error_code' => 'USR-Call-Error-Code',
+ 'usr_call_event_code' => 'USR-Call-Event-Code',
+ 'usr_call_reference_numbe' => 'USR-Call-Reference-Number',
+ 'usr_call_start_date_time' => 'USR-Call-Start-Date-Time',
+ 'usr_call_terminate_in_gm' => 'USR-Call-Terminate-in-GMT',
+ 'usr_call_type' => 'USR-Call-Type',
+ 'usr_callback_type' => 'USR-Callback-Type',
+ 'usr_called_party_number' => 'USR-Called-Party-Number',
+ 'usr_calling_party_number' => 'USR-Calling-Party-Number',
+ 'usr_card_type' => 'USR-Card-Type',
+ 'usr_ccp_algorithm' => 'USR-CCP-Algorithm',
+ 'usr_cdma_call_reference_' => 'USR-CDMA-Call-Reference-Number',
+ 'usr_channel' => 'USR-Channel',
+ 'usr_channel_connected_to' => 'USR-Channel-Connected-To',
+ 'usr_channel_decrement' => 'USR-Channel-Decrement',
+ 'usr_channel_expansion' => 'USR-Channel-Expansion',
+ 'usr_characters_received' => 'USR-Characters-Received',
+ 'usr_characters_sent' => 'USR-Characters-Sent',
+ 'usr_chassis_call_channel' => 'USR-Chassis-Call-Channel',
+ 'usr_chassis_call_slot' => 'USR-Chassis-Call-Slot',
+ 'usr_chassis_call_span' => 'USR-Chassis-Call-Span',
+ 'usr_chassis_slot' => 'USR-Chassis-Slot',
+ 'usr_chassis_temp_thresho' => 'USR-Chassis-Temp-Threshold',
+ 'usr_chassis_temperature' => 'USR-Chassis-Temperature',
+ 'usr_chat_script_name' => 'USR-Chat-Script-Name',
+ 'usr_compression_algorith' => 'USR-Compression-Algorithm',
+ 'usr_compression_reset_mo' => 'USR-Compression-Reset-Mode',
+ 'usr_compression_type' => 'USR-Compression-Type',
+ 'usr_connect_speed' => 'USR-Connect-Speed',
+ 'usr_connect_term_reason' => 'USR-Connect-Term-Reason',
+ 'usr_connect_time' => 'USR-Connect-Time',
+ 'usr_connect_time_limit' => 'USR-Connect-Time-Limit',
+ 'usr_cusr_hat_script_rule' => 'USR-CUSR-hat-Script-Rules',
+ 'usr_default_dte_data_rat' => 'USR-Default-DTE-Data-Rate',
+ 'usr_device_connected_to' => 'USR-Device-Connected-To',
+ 'usr_disconnect_cause_ind' => 'USR-Disconnect-Cause-Indicator',
+ 'usr_dnis_reauthenticatio' => 'USR-DNIS-ReAuthentication',
+ 'usr_ds0' => 'USR-DS0',
+ 'usr_ds0s' => 'USR-DS0s',
+ 'usr_dte_data_idle_timout' => 'USR-DTE-Data-Idle-Timout',
+ 'usr_dte_ring_no_answer_l' => 'USR-DTE-Ring-No-Answer-Limit',
+ 'usr_dtr_false_timeout' => 'USR-DTR-False-Timeout',
+ 'usr_dtr_true_timeout' => 'USR-DTR-True-Timeout',
+ 'usr_end_time' => 'USR-End-Time',
+ 'usr_equalization_type' => 'USR-Equalization-Type',
+ 'usr_esn' => 'USR-ESN',
+ 'usr_et_bridge_call_outpu' => 'USR-ET-Bridge-Call-Output-Filte',
+ 'usr_et_bridge_input_filt' => 'USR-ET-Bridge-Input-Filter',
+ 'usr_et_bridge_output_fil' => 'USR-ET-Bridge-Output-Filter',
+ 'usr_event_date_time' => 'USR-Event-Date-Time',
+ 'usr_event_id' => 'USR-Event-Id',
+ 'usr_expansion_algorithm' => 'USR-Expansion-Algorithm',
+ 'usr_expected_voltage' => 'USR-Expected-Voltage',
+ 'usr_failure_to_connect_r' => 'USR-Failure-to-Connect-Reason',
+ 'usr_fallback_enabled' => 'USR-Fallback-Enabled',
+ 'usr_fallback_limit' => 'USR-Fallback-Limit',
+ 'usr_filter_zones' => 'USR-Filter-Zones',
+ 'usr_final_rx_link_data_r' => 'USR-Final-Rx-Link-Data-Rate',
+ 'usr_final_tx_link_data_r' => 'USR-Final-Tx-Link-Data-Rate',
+ 'usr_framed_ip_address_po' => 'USR-Framed_IP_Address_Pool_Name',
+ 'usr_framed_ipx_route' => 'USR-Framed-IPX-Route',
+ 'usr_gateway_ip_address' => 'USR-Gateway-IP-Address',
+ 'usr_harc_disconnect_code' => 'USR-HARC-Disconnect-Code',
+ 'usr_host_type' => 'USR-Host-Type',
+ 'usr_ids0_call_type' => 'USR-IDS0-Call-Type',
+ 'usr_igmp_maximum_respons' => 'USR-IGMP-Maximum-Response-Time',
+ 'usr_igmp_query_interval' => 'USR-IGMP-Query-Interval',
+ 'usr_igmp_robustness' => 'USR-IGMP-Robustness',
+ 'usr_igmp_routing' => 'USR-IGMP-Routing',
+ 'usr_igmp_version' => 'USR-IGMP-Version',
+ 'usr_imsi' => 'USR-IMSI',
+ 'usr_initial_rx_link_data' => 'USR-Initial-Rx-Link-Data-Rate',
+ 'usr_initial_tx_link_data' => 'USR-Initial-Tx-Link-Data-Rate',
+ 'usr_interface_index' => 'USR-Interface-Index',
+ 'usr_ip' => 'USR-IP',
+ 'usr_ip_call_input_filter' => 'USR-IP-Call-Input-Filter',
+ 'usr_ip_call_output_filte' => 'USR-IP-Call-Output-Filter',
+ 'usr_ip_default_route_opt' => 'USR-IP-Default-Route-Option',
+ 'usr_ip_rip_input_filter' => 'USR-IP-RIP-Input-Filter',
+ 'usr_ip_rip_output_filter' => 'USR-IP-RIP-Output-Filter',
+ 'usr_ip_rip_policies' => 'USR-IP-RIP-Policies',
+ 'usr_ip_rip_simple_auth_p' => 'USR-IP-RIP-Simple-Auth-Password',
+ 'usr_ip_saa_filter' => 'USR-IP-SAA-Filter',
+ 'usr_ipx' => 'USR-IPX',
+ 'usr_ipx_call_input_filte' => 'USR-IPX-Call-Input-Filter',
+ 'usr_ipx_call_output_filt' => 'USR-IPX-Call-Output-Filter',
+ 'usr_ipx_rip_input_filter' => 'USR-IPX-RIP-Input-Filter',
+ 'usr_ipx_rip_output_filte' => 'USR-IPX-RIP-Output-Filter',
+ 'usr_ipx_routing' => 'USR-IPX-Routing',
+ 'usr_ipx_wan' => 'USR-IPX-WAN',
+ 'usr_iwf_call_identifier' => 'USR-IWF-Call-Identifier',
+ 'usr_iwf_ip_address' => 'USR-IWF-IP-Address',
+ 'usr_keypress_timeout' => 'USR-Keypress-Timeout',
+ 'usr_last_callers_number_' => 'USR-Last-Callers-Number-ANI',
+ 'usr_last_number_dialed_i' => 'USR-Last-Number-Dialed-In-DNIS',
+ 'usr_last_number_dialed_o' => 'USR-Last-Number-Dialed-Out',
+ 'usr_line_reversals' => 'USR-Line-Reversals',
+ 'usr_local_framed_ip_addr' => 'USR-Local-Framed-IP-Addr',
+ 'usr_local_ip_address' => 'USR-Local-IP-Address',
+ 'usr_log_filter_packets' => 'USR-Log-Filter-Packets',
+ 'usr_max_channels' => 'USR-Max-Channels',
+ 'usr_mbi_ct_bchannel_used' => 'USR-Mbi_Ct_BChannel_Used',
+ 'usr_mbi_ct_pri_card_slot' => 'USR-Mbi_Ct_PRI_Card_Slot',
+ 'usr_mbi_ct_pri_card_span' => 'USR-Mbi_Ct_PRI_Card_Span_Line',
+ 'usr_mbi_ct_tdm_time_slot' => 'USR-Mbi_Ct_TDM_Time_Slot',
+ 'usr_mic' => 'USR-MIC',
+ 'usr_min_compression_size' => 'USR-Min-Compression-Size',
+ 'usr_mobile_ip_address' => 'USR-Mobile-IP-Address',
+ 'usr_mobile_numbytes_rxed' => 'USR-Mobile-NumBytes-Rxed',
+ 'usr_mobile_numbytes_txed' => 'USR-Mobile-NumBytes-Txed',
+ 'usr_mobileip_home_agent_' => 'USR-MobileIP-Home-Agent-Address',
+ 'usr_modem_group' => 'USR-Modem-Group',
+ 'usr_modem_setup_time' => 'USR-Modem-Setup-Time',
+ 'usr_modem_training_time' => 'USR-Modem-Training-Time',
+ 'usr_modulation_type' => 'USR-Modulation-Type',
+ 'usr_mp_edo' => 'USR-MP-EDO',
+ 'usr_mp_edo_hiper' => 'USR-MP-EDO-HIPER',
+ 'usr_mp_mrru' => 'USR-MP-MRRU',
+ 'usr_mpip_tunnel_originat' => 'USR-MPIP-Tunnel-Originator',
+ 'usr_multicast_forwarding' => 'USR-Multicast-Forwarding',
+ 'usr_multicast_proxy' => 'USR-Multicast-Proxy',
+ 'usr_multicast_receive' => 'USR-Multicast-Receive',
+ 'usr_nas_type' => 'USR-NAS-Type',
+ 'usr_nfas_id' => 'USR-NFAS-ID',
+ 'usr_num_fax_pages_proces' => 'USR-Num-Fax-Pages-Processed',
+ 'usr_number_of_blers' => 'USR-Number-of-Blers',
+ 'usr_number_of_characters' => 'USR-Number-Of-Characters-Lost',
+ 'usr_number_of_fallbacks' => 'USR-Number-of-Fallbacks',
+ 'usr_number_of_link_naks' => 'USR-Number-of-Link-NAKs',
+ 'usr_number_of_link_timeo' => 'USR-Number-of-Link-Timeouts',
+ 'usr_number_of_rings_limi' => 'USR-Number-of-Rings-Limit',
+ 'usr_number_of_upshifts' => 'USR-Number-of-Upshifts',
+ 'usr_orig_nas_type' => 'USR-Orig-NAS-Type',
+ 'usr_originate_answer_mod' => 'USR-Originate-Answer-Mode',
+ 'usr_ospf_addressless_ind' => 'USR-OSPF-Addressless-Index',
+ 'usr_packet_bus_session' => 'USR-Packet-Bus-Session',
+ 'usr_physical_state' => 'USR-Physical-State',
+ 'usr_port_tap' => 'USR-Port-Tap',
+ 'usr_port_tap_address' => 'USR-Port-Tap-Address',
+ 'usr_port_tap_facility' => 'USR-Port-Tap-Facility',
+ 'usr_port_tap_format' => 'USR-Port-Tap-Format',
+ 'usr_port_tap_output' => 'USR-Port-Tap-Output',
+ 'usr_port_tap_priority' => 'USR-Port-Tap-Priority',
+ 'usr_power_supply_number' => 'USR-Power-Supply-Number',
+ 'usr_primary_dns_server' => 'USR-Primary_DNS_Server',
+ 'usr_primary_nbns_server' => 'USR-Primary_NBNS_Server',
+ 'usr_pw_cutoff' => 'USR-PW_Cutoff',
+ 'usr_pw_framed_routing_v2' => 'USR-PW_Framed_Routing_V2',
+ 'usr_pw_index' => 'USR-PW_Index',
+ 'usr_pw_packet' => 'USR-PW_Packet',
+ 'usr_pw_tunnel_authentica' => 'USR-PW_Tunnel_Authentication',
+ 'usr_pw_usr_ifilter_ip' => 'USR-PW_USR_IFilter_IP',
+ 'usr_pw_usr_ifilter_ipx' => 'USR-PW_USR_IFilter_IPX',
+ 'usr_pw_usr_ofilter_ip' => 'USR-PW_USR_OFilter_IP',
+ 'usr_pw_usr_ofilter_ipx' => 'USR-PW_USR_OFilter_IPX',
+ 'usr_pw_usr_ofilter_sap' => 'USR-PW_USR_OFilter_SAP',
+ 'usr_pw_vpn_gateway' => 'USR-PW_VPN_Gateway',
+ 'usr_pw_vpn_id' => 'USR-PW_VPN_ID',
+ 'usr_pw_vpn_name' => 'USR-PW_VPN_Name',
+ 'usr_pw_vpn_neighbor' => 'USR-PW_VPN_Neighbor',
+ 'usr_q931_call_reference_' => 'USR-Q931-Call-Reference-Value',
+ 'usr_rad_dvmrp_metric' => 'USR-Rad-Dvmrp-Metric',
+ 'usr_rad_location_type' => 'USR-Rad-Location-Type',
+ 'usr_rad_multicast_routin' => 'USR-Rad-Multicast-Routing-Ttl',
+ 'usr_rad_multicast_routio' => 'USR-Rad-Multicast-Routing-RtLim',
+ 'usr_rad_multicast_routip' => 'USR-Rad-Multicast-Routing-Proto',
+ 'usr_rad_multicast_routiq' => 'USR-Rad-Multicast-Routing-Bound',
+ 'usr_re_chap_timeout' => 'USR-Re-Chap-Timeout',
+ 'usr_receive_acc_map' => 'USR-Receive-Acc-Map',
+ 'usr_reply_script1' => 'USR-Reply-Script1',
+ 'usr_reply_script2' => 'USR-Reply-Script2',
+ 'usr_reply_script3' => 'USR-Reply-Script3',
+ 'usr_reply_script4' => 'USR-Reply-Script4',
+ 'usr_reply_script5' => 'USR-Reply-Script5',
+ 'usr_reply_script6' => 'USR-Reply-Script6',
+ 'usr_request_type' => 'USR-Request-Type',
+ 'usr_retrains_granted' => 'USR-Retrains-Granted',
+ 'usr_retrains_requested' => 'USR-Retrains-Requested',
+ 'usr_rmmie_firmware_build' => 'USR-RMMIE-Firmware-Build-Date',
+ 'usr_rmmie_firmware_versi' => 'USR-RMMIE-Firmware-Version',
+ 'usr_rmmie_last_update_ev' => 'USR-RMMIE-Last-Update-Event',
+ 'usr_rmmie_last_update_ti' => 'USR-RMMIE-Last-Update-Time',
+ 'usr_rmmie_manufacturer_i' => 'USR-RMMIE-Manufacturer-ID',
+ 'usr_rmmie_num_of_updates' => 'USR-RMMIE-Num-Of-Updates',
+ 'usr_rmmie_planned_discon' => 'USR-RMMIE-Planned-Disconnect',
+ 'usr_rmmie_product_code' => 'USR-RMMIE-Product-Code',
+ 'usr_rmmie_pwrlvl_farecho' => 'USR-RMMIE-PwrLvl-FarEcho-Canc',
+ 'usr_rmmie_pwrlvl_nearech' => 'USR-RMMIE-PwrLvl-NearEcho-Canc',
+ 'usr_rmmie_pwrlvl_noise_l' => 'USR-RMMIE-PwrLvl-Noise-Lvl',
+ 'usr_rmmie_pwrlvl_xmit_lv' => 'USR-RMMIE-PwrLvl-Xmit-Lvl',
+ 'usr_rmmie_rcv_pwrlvl_330' => 'USR-RMMIE-Rcv-PwrLvl-3300Hz',
+ 'usr_rmmie_rcv_pwrlvl_375' => 'USR-RMMIE-Rcv-PwrLvl-3750Hz',
+ 'usr_rmmie_rcv_tot_pwrlvl' => 'USR-RMMIE-Rcv-Tot-PwrLvl',
+ 'usr_rmmie_serial_number' => 'USR-RMMIE-Serial-Number',
+ 'usr_rmmie_status' => 'USR-RMMIE-Status',
+ 'usr_rmmie_x2_status' => 'USR-RMMIE-x2-Status',
+ 'usr_routing_protocol' => 'USR-Routing-Protocol',
+ 'usr_sap_filter_in' => 'USR-SAP-Filter-In',
+ 'usr_secondary_dns_server' => 'USR-Secondary_DNS_Server',
+ 'usr_secondary_nbns_serve' => 'USR-Secondary_NBNS_Server',
+ 'usr_security_login_limit' => 'USR-Security-Login-Limit',
+ 'usr_security_resp_limit' => 'USR-Security-Resp-Limit',
+ 'usr_send_name' => 'USR-Send-Name',
+ 'usr_send_password' => 'USR-Send-Password',
+ 'usr_send_script1' => 'USR-Send-Script1',
+ 'usr_send_script2' => 'USR-Send-Script2',
+ 'usr_send_script3' => 'USR-Send-Script3',
+ 'usr_send_script4' => 'USR-Send-Script4',
+ 'usr_send_script5' => 'USR-Send-Script5',
+ 'usr_send_script6' => 'USR-Send-Script6',
+ 'usr_server_time' => 'USR-Server-Time',
+ 'usr_service_option' => 'USR-Service-Option',
+ 'usr_simplified_mnp_level' => 'USR-Simplified-MNP-Levels',
+ 'usr_simplified_v42bis_us' => 'USR-Simplified-V42bis-Usage',
+ 'usr_slot_connected_to' => 'USR-Slot-Connected-To',
+ 'usr_speed_of_connection' => 'USR-Speed-Of-Connection',
+ 'usr_spoofing' => 'USR-Spoofing',
+ 'usr_start_time' => 'USR-Start-Time',
+ 'usr_supports_tags' => 'USR-Supports-Tags',
+ 'usr_sync_async_mode' => 'USR-Sync-Async-Mode',
+ 'usr_syslog_tap' => 'USR-Syslog-Tap',
+ 'usr_terminal_type' => 'USR-Terminal-Type',
+ 'usr_transmit_acc_map' => 'USR-Transmit-Acc-Map',
+ 'usr_tunnel_auth_hostname' => 'USR-Tunnel-Auth-Hostname',
+ 'usr_tunnel_security' => 'USR-Tunnel-Security',
+ 'usr_tunnel_switch_endpoi' => 'USR-Tunnel-Switch-Endpoint',
+ 'usr_tunneled_mlpp' => 'USR-Tunneled-MLPP',
+ 'usr_unauthenticated_time' => 'USR-Unauthenticated-Time',
+ 'usr_vpn_encrypter' => 'USR-VPN-Encrypter',
+ 'usr_vpn_gw_location_id' => 'USR-VPN-GW-Location-Id',
+ 'usr_vts_session_key' => 'USR-VTS-Session-Key',
+ 'vendor_specific' => 'Vendor-Specific',
+ 'versanet_termination_cau' => 'Versanet-Termination-Cause',
+ 'vnc_pppoe_cbq_rx' => 'VNC-PPPoE-CBQ-RX',
+ 'vnc_pppoe_cbq_rx_fallbac' => 'VNC-PPPoE-CBQ-RX-Fallback',
+ 'vnc_pppoe_cbq_tx' => 'VNC-PPPoE-CBQ-TX',
+ 'vnc_pppoe_cbq_tx_fallbac' => 'VNC-PPPoE-CBQ-TX-Fallback',
+ 'vnc_splash' => 'VNC-Splash',
+ 'wispr_bandwidth_max_down' => 'WISPr-Bandwidth-Max-Down',
+ 'wispr_bandwidth_max_up' => 'WISPr-Bandwidth-Max-Up',
+ 'wispr_bandwidth_min_down' => 'WISPr-Bandwidth-Min-Down',
+ 'wispr_bandwidth_min_up' => 'WISPr-Bandwidth-Min-Up',
+ 'wispr_billing_class_of_s' => 'WISPr-Billing-Class-Of-Service',
+ 'wispr_location_id' => 'WISPr-Location-ID',
+ 'wispr_location_name' => 'WISPr-Location-Name',
+ 'wispr_logoff_url' => 'WISPr-Logoff-URL',
+ 'wispr_redirection_url' => 'WISPr-Redirection-URL',
+ 'wispr_session_terminate_' => 'WISPr-Session-Terminate-Time',
+ 'wispr_session_terminatea' => 'WISPr-Session-Terminate-End-Of-Day',
+ 'x_ascend_add_seconds' => 'X-Ascend-Add-Seconds',
+ 'x_ascend_ara_pw' => 'X-Ascend-Ara-PW',
+ 'x_ascend_assign_ip_clien' => 'X-Ascend-Assign-IP-Client',
+ 'x_ascend_assign_ip_globa' => 'X-Ascend-Assign-IP-Global-Pool',
+ 'x_ascend_assign_ip_pool' => 'X-Ascend-Assign-IP-Pool',
+ 'x_ascend_assign_ip_serve' => 'X-Ascend-Assign-IP-Server',
+ 'x_ascend_authen_alias' => 'X-Ascend-Authen-Alias',
+ 'x_ascend_backup' => 'X-Ascend-Backup',
+ 'x_ascend_bacp_enable' => 'X-Ascend-BACP-Enable',
+ 'x_ascend_base_channel_co' => 'X-Ascend-Base-Channel-Count',
+ 'x_ascend_billing_number' => 'X-Ascend-Billing-Number',
+ 'x_ascend_bridge' => 'X-Ascend-Bridge',
+ 'x_ascend_bridge_address' => 'X-Ascend-Bridge-Address',
+ 'x_ascend_call_attempt_li' => 'X-Ascend-Call-Attempt-Limit',
+ 'x_ascend_call_block_dura' => 'X-Ascend-Call-Block-Duration',
+ 'x_ascend_call_by_call' => 'X-Ascend-Call-By-Call',
+ 'x_ascend_call_filter' => 'X-Ascend-Call-Filter',
+ 'x_ascend_call_type' => 'X-Ascend-Call-Type',
+ 'x_ascend_callback' => 'X-Ascend-Callback',
+ 'x_ascend_client_assign_d' => 'X-Ascend-Client-Assign-DNS',
+ 'x_ascend_client_gateway' => 'X-Ascend-Client-Gateway',
+ 'x_ascend_client_primary_' => 'X-Ascend-Client-Primary-DNS',
+ 'x_ascend_client_secondar' => 'X-Ascend-Client-Secondary-DNS',
+ 'x_ascend_connect_progres' => 'X-Ascend-Connect-Progress',
+ 'x_ascend_data_filter' => 'X-Ascend-Data-Filter',
+ 'x_ascend_data_rate' => 'X-Ascend-Data-Rate',
+ 'x_ascend_data_svc' => 'X-Ascend-Data-Svc',
+ 'x_ascend_dba_monitor' => 'X-Ascend-DBA-Monitor',
+ 'x_ascend_dec_channel_cou' => 'X-Ascend-Dec-Channel-Count',
+ 'x_ascend_dhcp_maximum_le' => 'X-Ascend-DHCP-Maximum-Leases',
+ 'x_ascend_dhcp_pool_numbe' => 'X-Ascend-DHCP-Pool-Number',
+ 'x_ascend_dhcp_reply' => 'X-Ascend-DHCP-Reply',
+ 'x_ascend_dial_number' => 'X-Ascend-Dial-Number',
+ 'x_ascend_dialout_allowed' => 'X-Ascend-Dialout-Allowed',
+ 'x_ascend_disconnect_caus' => 'X-Ascend-Disconnect-Cause',
+ 'x_ascend_event_type' => 'X-Ascend-Event-Type',
+ 'x_ascend_expect_callback' => 'X-Ascend-Expect-Callback',
+ 'x_ascend_fcp_parameter' => 'X-Ascend-FCP-Parameter',
+ 'x_ascend_first_dest' => 'X-Ascend-First-Dest',
+ 'x_ascend_force_56' => 'X-Ascend-Force-56',
+ 'x_ascend_fr_circuit_name' => 'X-Ascend-FR-Circuit-Name',
+ 'x_ascend_fr_dce_n392' => 'X-Ascend-FR-DCE-N392',
+ 'x_ascend_fr_dce_n393' => 'X-Ascend-FR-DCE-N393',
+ 'x_ascend_fr_direct' => 'X-Ascend-FR-Direct',
+ 'x_ascend_fr_direct_dlci' => 'X-Ascend-FR-Direct-DLCI',
+ 'x_ascend_fr_direct_profi' => 'X-Ascend-FR-Direct-Profile',
+ 'x_ascend_fr_dlci' => 'X-Ascend-FR-DLCI',
+ 'x_ascend_fr_dte_n392' => 'X-Ascend-FR-DTE-N392',
+ 'x_ascend_fr_dte_n393' => 'X-Ascend-FR-DTE-N393',
+ 'x_ascend_fr_link_mgt' => 'X-Ascend-FR-Link-Mgt',
+ 'x_ascend_fr_linkup' => 'X-Ascend-FR-LinkUp',
+ 'x_ascend_fr_n391' => 'X-Ascend-FR-N391',
+ 'x_ascend_fr_nailed_grp' => 'X-Ascend-FR-Nailed-Grp',
+ 'x_ascend_fr_profile_name' => 'X-Ascend-FR-Profile-Name',
+ 'x_ascend_fr_t391' => 'X-Ascend-FR-T391',
+ 'x_ascend_fr_t392' => 'X-Ascend-FR-T392',
+ 'x_ascend_fr_type' => 'X-Ascend-FR-Type',
+ 'x_ascend_ft1_caller' => 'X-Ascend-FT1-Caller',
+ 'x_ascend_group' => 'X-Ascend-Group',
+ 'x_ascend_handle_ipx' => 'X-Ascend-Handle-IPX',
+ 'x_ascend_history_weigh_t' => 'X-Ascend-History-Weigh-Type',
+ 'x_ascend_home_agent_ip_a' => 'X-Ascend-Home-Agent-IP-Addr',
+ 'x_ascend_home_agent_pass' => 'X-Ascend-Home-Agent-Password',
+ 'x_ascend_home_agent_udp_' => 'X-Ascend-Home-Agent-UDP-Port',
+ 'x_ascend_home_network_na' => 'X-Ascend-Home-Network-Name',
+ 'x_ascend_host_info' => 'X-Ascend-Host-Info',
+ 'x_ascend_idle_limit' => 'X-Ascend-Idle-Limit',
+ 'x_ascend_if_netmask' => 'X-Ascend-IF-Netmask',
+ 'x_ascend_inc_channel_cou' => 'X-Ascend-Inc-Channel-Count',
+ 'x_ascend_ip_direct' => 'X-Ascend-IP-Direct',
+ 'x_ascend_ip_pool_definit' => 'X-Ascend-IP-Pool-Definition',
+ 'x_ascend_ipx_alias' => 'X-Ascend-IPX-Alias',
+ 'x_ascend_ipx_node_addr' => 'X-Ascend-IPX-Node-Addr',
+ 'x_ascend_ipx_peer_mode' => 'X-Ascend-IPX-Peer-Mode',
+ 'x_ascend_ipx_route' => 'X-Ascend-IPX-Route',
+ 'x_ascend_link_compressio' => 'X-Ascend-Link-Compression',
+ 'x_ascend_maximum_call_du' => 'X-Ascend-Maximum-Call-Duration',
+ 'x_ascend_maximum_channel' => 'X-Ascend-Maximum-Channels',
+ 'x_ascend_maximum_time' => 'X-Ascend-Maximum-Time',
+ 'x_ascend_menu_item' => 'X-Ascend-Menu-Item',
+ 'x_ascend_menu_selector' => 'X-Ascend-Menu-Selector',
+ 'x_ascend_metric' => 'X-Ascend-Metric',
+ 'x_ascend_minimum_channel' => 'X-Ascend-Minimum-Channels',
+ 'x_ascend_modem_portno' => 'X-Ascend-Modem-PortNo',
+ 'x_ascend_modem_shelfno' => 'X-Ascend-Modem-ShelfNo',
+ 'x_ascend_modem_slotno' => 'X-Ascend-Modem-SlotNo',
+ 'x_ascend_mpp_idle_percen' => 'X-Ascend-MPP-Idle-Percent',
+ 'x_ascend_multicast_clien' => 'X-Ascend-Multicast-Client',
+ 'x_ascend_multicast_rate_' => 'X-Ascend-Multicast-Rate-Limit',
+ 'x_ascend_multilink_id' => 'X-Ascend-Multilink-ID',
+ 'x_ascend_netware_timeout' => 'X-Ascend-Netware-timeout',
+ 'x_ascend_num_in_multilin' => 'X-Ascend-Num-In-Multilink',
+ 'x_ascend_number_sessions' => 'X-Ascend-Number-Sessions',
+ 'x_ascend_ppp_address' => 'X-Ascend-PPP-Address',
+ 'x_ascend_ppp_async_map' => 'X-Ascend-PPP-Async-Map',
+ 'x_ascend_ppp_vj_1172' => 'X-Ascend-PPP-VJ-1172',
+ 'x_ascend_ppp_vj_slot_com' => 'X-Ascend-PPP-VJ-Slot-Comp',
+ 'x_ascend_pre_input_octet' => 'X-Ascend-Pre-Input-Octets',
+ 'x_ascend_pre_input_packe' => 'X-Ascend-Pre-Input-Packets',
+ 'x_ascend_pre_output_octe' => 'X-Ascend-Pre-Output-Octets',
+ 'x_ascend_pre_output_pack' => 'X-Ascend-Pre-Output-Packets',
+ 'x_ascend_preempt_limit' => 'X-Ascend-Preempt-Limit',
+ 'x_ascend_presession_time' => 'X-Ascend-PreSession-Time',
+ 'x_ascend_pri_number_type' => 'X-Ascend-PRI-Number-Type',
+ 'x_ascend_primary_home_ag' => 'X-Ascend-Primary-Home-Agent',
+ 'x_ascend_pw_lifetime' => 'X-Ascend-PW-Lifetime',
+ 'x_ascend_pw_warntime' => 'X-Ascend-PW-Warntime',
+ 'x_ascend_receive_secret' => 'X-Ascend-Receive-Secret',
+ 'x_ascend_remote_addr' => 'X-Ascend-Remote-Addr',
+ 'x_ascend_remove_seconds' => 'X-Ascend-Remove-Seconds',
+ 'x_ascend_require_auth' => 'X-Ascend-Require-Auth',
+ 'x_ascend_route_ip' => 'X-Ascend-Route-IP',
+ 'x_ascend_route_ipx' => 'X-Ascend-Route-IPX',
+ 'x_ascend_secondary_home_' => 'X-Ascend-Secondary-Home-Agent',
+ 'x_ascend_seconds_of_hist' => 'X-Ascend-Seconds-Of-History',
+ 'x_ascend_send_auth' => 'X-Ascend-Send-Auth',
+ 'x_ascend_send_passwd' => 'X-Ascend-Send-Passwd',
+ 'x_ascend_send_secret' => 'X-Ascend-Send-Secret',
+ 'x_ascend_session_svr_key' => 'X-Ascend-Session-Svr-Key',
+ 'x_ascend_shared_profile_' => 'X-Ascend-Shared-Profile-Enable',
+ 'x_ascend_target_util' => 'X-Ascend-Target-Util',
+ 'x_ascend_temporary_rtes' => 'X-Ascend-Temporary-Rtes',
+ 'x_ascend_third_prompt' => 'X-Ascend-Third-Prompt',
+ 'x_ascend_token_expiry' => 'X-Ascend-Token-Expiry',
+ 'x_ascend_token_idle' => 'X-Ascend-Token-Idle',
+ 'x_ascend_token_immediate' => 'X-Ascend-Token-Immediate',
+ 'x_ascend_transit_number' => 'X-Ascend-Transit-Number',
+ 'x_ascend_ts_idle_limit' => 'X-Ascend-TS-Idle-Limit',
+ 'x_ascend_ts_idle_mode' => 'X-Ascend-TS-Idle-Mode',
+ 'x_ascend_tunneling_proto' => 'X-Ascend-Tunneling-Protocol',
+ 'x_ascend_user_acct_base' => 'X-Ascend-User-Acct-Base',
+ 'x_ascend_user_acct_host' => 'X-Ascend-User-Acct-Host',
+ 'x_ascend_user_acct_key' => 'X-Ascend-User-Acct-Key',
+ 'x_ascend_user_acct_port' => 'X-Ascend-User-Acct-Port',
+ 'x_ascend_user_acct_time' => 'X-Ascend-User-Acct-Time',
+ 'x_ascend_user_acct_type' => 'X-Ascend-User-Acct-Type',
+ 'x_ascend_xmit_rate' => 'X-Ascend-Xmit-Rate',
+ 'xedia_address_pool' => 'Xedia-Address-Pool',
+ 'xedia_client_access_netw' => 'Xedia-Client-Access-Network',
+ 'xedia_dns_server' => 'Xedia-DNS-Server',
+ 'xedia_netbios_server' => 'Xedia-NetBios-Server',
+ 'xedia_ppp_echo_interval' => 'Xedia-PPP-Echo-Interval',
+ 'xedia_ssh_privileges' => 'Xedia-SSH-Privileges',
+
+ #NETC.NET.AU (RADIATOR?)
+ 'authentication_type' => 'Authentication-Type',
+
+ #wtxs (dunno)
+ #'radius_operator' => 'Radius-Operator',
+
+);
+
+1;
diff --git a/FS/FS/radius_usergroup.pm b/FS/FS/radius_usergroup.pm
new file mode 100644
index 0000000..9bba057
--- /dev/null
+++ b/FS/FS/radius_usergroup.pm
@@ -0,0 +1,131 @@
+package FS::radius_usergroup;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::svc_acct;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::radius_usergroup - Object methods for radius_usergroup records
+
+=head1 SYNOPSIS
+
+ use FS::radius_usergroup;
+
+ $record = new FS::radius_usergroup \%hash;
+ $record = new FS::radius_usergroup { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::radius_usergroup object links an account (see L<FS::svc_acct>) with a
+RADIUS group. FS::radius_usergroup inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item usergroupnum - primary key
+
+=item svcnum - Account (see L<FS::svc_acct>).
+
+=item groupname - group name
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'radius_usergroup'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+#inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+#inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+#inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('usergroupnum')
+ || $self->ut_number('svcnum')
+ || $self->ut_foreign_key('svcnum','svc_acct','svcnum')
+ || $self->ut_text('groupname')
+ || $self->SUPER::check
+ ;
+}
+
+=item svc_acct
+
+Returns the account associated with this record (see L<FS::svc_acct>).
+
+=cut
+
+sub svc_acct {
+ my $self = shift;
+ qsearchs('svc_acct', { svcnum => $self->svcnum } );
+}
+
+=back
+
+=head1 BUGS
+
+Don't let 'em get you down.
+
+=head1 SEE ALSO
+
+L<svc_acct>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/rate.pm b/FS/FS/rate.pm
new file mode 100644
index 0000000..6430ff0
--- /dev/null
+++ b/FS/FS/rate.pm
@@ -0,0 +1,415 @@
+package FS::rate;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Record qw( qsearch qsearchs dbh fields );
+use FS::rate_detail;
+
+@ISA = qw(FS::Record);
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::rate - Object methods for rate records
+
+=head1 SYNOPSIS
+
+ use FS::rate;
+
+ $record = new FS::rate \%hash;
+ $record = new FS::rate { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::rate object represents an rate plan. FS::rate inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item ratenum - primary key
+
+=item ratename
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new rate plan. To add the rate plan to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'rate'; }
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+Currently available options are: I<rate_detail>
+
+If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
+objects will have their ratenum field set and will be inserted after this
+record.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my %options = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->check;
+ return $error if $error;
+
+ $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $options{'rate_detail'} ) {
+
+ my( $num, $last, $min_sec ) = (0, time, 5); #progressbar foo
+
+ foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
+
+ $rate_detail->ratenum($self->ratenum);
+ $error = $rate_detail->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $options{'job'} ) {
+ $num++;
+ if ( time - $min_sec > $last ) {
+ my $error = $options{'job'}->update_statustext(
+ int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $last = time;
+ }
+ }
+
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD [ , OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+Currently available options are: I<rate_detail>
+
+If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
+objects will have their ratenum field set and will be inserted after this
+record. Any existing rate_detail records associated with this record will be
+deleted.
+
+=cut
+
+sub replace {
+ my ($new, $old) = (shift, shift);
+ my %options = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+# my @old_rate_detail = ();
+# @old_rate_detail = $old->rate_detail if $options{'rate_detail'};
+
+ my $error = $new->SUPER::replace($old);
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+# foreach my $old_rate_detail ( @old_rate_detail ) {
+#
+# my $error = $old_rate_detail->delete;
+# if ($error) {
+# $dbh->rollback if $oldAutoCommit;
+# return $error;
+# }
+#
+# if ( $options{'job'} ) {
+# $num++;
+# if ( time - $min_sec > $last ) {
+# my $error = $options{'job'}->update_statustext(
+# int( 50 * $num / scalar( @old_rate_detail ) )
+# );
+# if ( $error ) {
+# $dbh->rollback if $oldAutoCommit;
+# return $error;
+# }
+# $last = time;
+# }
+# }
+#
+# }
+ if ( $options{'rate_detail'} ) {
+ my $sth = $dbh->prepare('DELETE FROM rate_detail WHERE ratenum = ?') or do {
+ $dbh->rollback if $oldAutoCommit;
+ return $dbh->errstr;
+ };
+
+ $sth->execute($old->ratenum) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return $sth->errstr;
+ };
+
+ my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
+# $num = 0;
+ foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
+
+ $rate_detail->ratenum($new->ratenum);
+ $error = $rate_detail->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $options{'job'} ) {
+ $num++;
+ if ( time - $min_sec > $last ) {
+ my $error = $options{'job'}->update_statustext(
+ int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $last = time;
+ }
+ }
+
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid rate plan. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('ratenum')
+ || $self->ut_text('ratename')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item dest_detail REGIONNUM | RATE_REGION_OBJECTD | HASHREF
+
+Returns the rate detail (see L<FS::rate_detail>) for this rate to the
+specificed destination. Destination can be specified as an FS::rate_detail
+object or regionnum (see L<FS::rate_detail>), or as a hashref with two keys:
+I<countrycode> and I<phonenum>.
+
+=cut
+
+sub dest_detail {
+ my $self = shift;
+
+ my $regionnum;
+ if ( ref($_[0]) eq 'HASH' ) {
+
+ my $countrycode = $_[0]->{'countrycode'};
+ my $phonenum = $_[0]->{'phonenum'};
+
+ #find a rate prefix, first look at most specific (4 digits) then 3, etc.,
+ # finally trying the country code only
+ my $rate_prefix = '';
+ for my $len ( reverse(1..10) ) {
+ $rate_prefix = qsearchs('rate_prefix', {
+ 'countrycode' => $countrycode,
+ #'npa' => { op=> 'LIKE', value=> substr($number, 0, $len) }
+ 'npa' => substr($phonenum, 0, $len),
+ } ) and last;
+ }
+ $rate_prefix ||= qsearchs('rate_prefix', {
+ 'countrycode' => $countrycode,
+ 'npa' => '',
+ });
+
+ #
+ #die "Can't find rate for call $to_or_from +$countrycode $number\n"
+ die "Can't find rate for +$countrycode $phonenum\n"
+ unless $rate_prefix;
+
+ $regionnum = $rate_prefix->regionnum;
+
+ #$rate_region = $rate_prefix->rate_region;
+
+ } else {
+ $regionnum = ref($_[0]) ? shift->regionnum : shift;
+ }
+
+ qsearchs( 'rate_detail', { 'ratenum' => $self->ratenum,
+ 'dest_regionnum' => $regionnum, } );
+}
+
+=item rate_detail
+
+Returns all region-specific details (see L<FS::rate_detail>) for this rate.
+
+=cut
+
+sub rate_detail {
+ my $self = shift;
+ qsearch( 'rate_detail', { 'ratenum' => $self->ratenum } );
+}
+
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item process
+
+Experimental job-queue processor for web interface adds/edits
+
+=cut
+
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
+sub process {
+ my $job = shift;
+
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ my $old = qsearchs('rate', { 'ratenum' => $param->{'ratenum'} } )
+ if $param->{'ratenum'};
+
+ my @rate_detail = map {
+
+ my $regionnum = $_->regionnum;
+ if ( $param->{"sec_granularity$regionnum"} ) {
+
+ new FS::rate_detail {
+ 'dest_regionnum' => $regionnum,
+ map { $_ => $param->{"$_$regionnum"} }
+ qw( min_included min_charge sec_granularity )
+ };
+
+ } else {
+
+ new FS::rate_detail {
+ 'dest_regionnum' => $regionnum,
+ 'min_included' => 0,
+ 'min_charge' => 0,
+ 'sec_granularity' => '60'
+ };
+
+ }
+
+ } qsearch('rate_region', {} );
+
+ my $rate = new FS::rate {
+ map { $_ => $param->{$_} }
+ fields('rate')
+ };
+
+ my $error = '';
+ if ( $param->{'ratenum'} ) {
+ warn "$rate replacing $old (". $param->{'ratenum'}. ")\n" if $DEBUG;
+ $error = $rate->replace( $old,
+ 'rate_detail' => \@rate_detail,
+ 'job' => $job,
+ );
+ } else {
+ warn "inserting $rate\n" if $DEBUG;
+ $error = $rate->insert( 'rate_detail' => \@rate_detail,
+ 'job' => $job,
+ );
+ #$ratenum = $rate->getfield('ratenum');
+ }
+
+ die "$error\n" if $error;
+
+}
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/rate_detail.pm b/FS/FS/rate_detail.pm
new file mode 100644
index 0000000..62c0fa1
--- /dev/null
+++ b/FS/FS/rate_detail.pm
@@ -0,0 +1,245 @@
+package FS::rate_detail;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::rate;
+use FS::rate_region;
+use Tie::IxHash;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::rate_detail - Object methods for rate_detail records
+
+=head1 SYNOPSIS
+
+ use FS::rate_detail;
+
+ $record = new FS::rate_detail \%hash;
+ $record = new FS::rate_detail { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::rate_detail object represents an call plan rate. FS::rate_detail
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item ratedetailnum - primary key
+
+=item ratenum - rate plan (see L<FS::rate>)
+
+=item orig_regionnum - call origination region
+
+=item dest_regionnum - call destination region
+
+=item min_included - included minutes
+
+=item min_charge - charge per minute
+
+=item sec_granularity - granularity in seconds, i.e. 6 or 60; 0 for per-call
+
+=item classnum - usage class (see L<FS::usage_class>) if any for this rate
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new call plan rate. To add the call plan rate to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'rate_detail'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid call plan rate. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('ratedetailnum')
+ || $self->ut_foreign_key('ratenum', 'rate', 'ratenum')
+ || $self->ut_foreign_keyn('orig_regionnum', 'rate_region', 'regionnum' )
+ || $self->ut_foreign_key('dest_regionnum', 'rate_region', 'regionnum' )
+ || $self->ut_number('min_included')
+
+ #|| $self->ut_money('min_charge')
+ #good enough for now...
+ || $self->ut_float('min_charge')
+
+ || $self->ut_number('sec_granularity')
+
+ || $self->ut_foreign_keyn('classnum', 'usage_class', 'classnum' )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item rate
+
+Returns the parent call plan (see L<FS::rate>) associated with this call plan
+rate.
+
+=cut
+
+sub rate {
+ my $self = shift;
+ qsearchs('rate', { 'ratenum' => $self->ratenum } );
+}
+
+=item orig_region
+
+Returns the origination region (see L<FS::rate_region>) associated with this
+call plan rate.
+
+=cut
+
+sub orig_region {
+ my $self = shift;
+ qsearchs('rate_region', { 'regionnum' => $self->orig_regionnum } );
+}
+
+=item dest_region
+
+Returns the destination region (see L<FS::rate_region>) associated with this
+call plan rate.
+
+=cut
+
+sub dest_region {
+ my $self = shift;
+ qsearchs('rate_region', { 'regionnum' => $self->dest_regionnum } );
+}
+
+=item dest_regionname
+
+Returns the name of the destination region (see L<FS::rate_region>) associated
+with this call plan rate.
+
+=cut
+
+sub dest_regionname {
+ my $self = shift;
+ $self->dest_region->regionname;
+}
+
+=item dest_regionname
+
+Returns a short list of the prefixes for the destination region
+(see L<FS::rate_region>) associated with this call plan rate.
+
+=cut
+
+sub dest_prefixes_short {
+ my $self = shift;
+ $self->dest_region->prefixes_short;
+}
+
+=item classname
+
+Returns the name of the usage class (see L<FS::usage_class>) associated with
+this call plan rate.
+
+=cut
+
+sub classname {
+ my $self = shift;
+ my $usage_class = qsearchs('usage_class', { classnum => $self->classnum });
+ $usage_class ? $usage_class->classname : '';
+}
+
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item granularities
+
+ Returns an (ordered) hash of granularity => name pairs
+
+=cut
+
+tie my %granularities, 'Tie::IxHash',
+ '1', => '1 second',
+ '6' => '6 second',
+ '30' => '30 second', # '1/2 minute',
+ '60' => 'minute',
+ '0' => 'call',
+;
+
+sub granularities {
+ %granularities;
+}
+
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::rate>, L<FS::rate_region>, L<FS::Record>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/rate_prefix.pm b/FS/FS/rate_prefix.pm
new file mode 100644
index 0000000..ce780fe
--- /dev/null
+++ b/FS/FS/rate_prefix.pm
@@ -0,0 +1,160 @@
+package FS::rate_prefix;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::rate_region;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::rate_prefix - Object methods for rate_prefix records
+
+=head1 SYNOPSIS
+
+ use FS::rate_prefix;
+
+ $record = new FS::rate_prefix \%hash;
+ $record = new FS::rate_prefix { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::rate_prefix object represents an call rating prefix. FS::rate_prefix
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item prefixnum - primary key
+
+=item regionnum - call ration region (see L<FS::rate_region>)
+
+=item countrycode
+
+=item npa
+
+=item nxx
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new prefix. To add the prefix to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'rate_prefix'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid prefix. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('prefixnum')
+ || $self->ut_foreign_key('regionnum', 'rate_region', 'regionnum' )
+ || $self->ut_number('countrycode')
+ || $self->ut_numbern('npa')
+ || $self->ut_numbern('nxx')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item rate_region
+
+Returns the rate region (see L<FS::rate_region>) for this prefix.
+
+=cut
+
+sub rate_region {
+ my $self = shift;
+ qsearchs('rate_region', { 'regionnum' => $self->regionnum } );
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item all_countrycodes
+
+Returns a list of all countrycodes listed in rate_prefix
+
+=cut
+
+sub all_countrycodes {
+ #my $class = shift;
+ my $sql =
+ "SELECT DISTINCT(countrycode) FROM rate_prefix ORDER BY countrycode";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ map $_->[0], @{ $sth->fetchall_arrayref };
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::rate_region>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/rate_region.pm b/FS/FS/rate_region.pm
new file mode 100644
index 0000000..0e65223
--- /dev/null
+++ b/FS/FS/rate_region.pm
@@ -0,0 +1,315 @@
+package FS::rate_region;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::rate_prefix;
+use FS::rate_detail;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::rate_region - Object methods for rate_region records
+
+=head1 SYNOPSIS
+
+ use FS::rate_region;
+
+ $record = new FS::rate_region \%hash;
+ $record = new FS::rate_region { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::rate_region object represents an call rating region. FS::rate_region
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item regionnum - primary key
+
+=item regionname
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new region. To add the region to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'rate_region'; }
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+Currently available options are: I<rate_prefix> and I<dest_detail>
+
+If I<rate_prefix> is set to an array reference of FS::rate_prefix objects, the
+objects will have their regionnum field set and will be inserted after this
+record.
+
+If I<dest_detail> is set to an array reference of FS::rate_detail objects, the
+objects will have their dest_regionnum field set and will be inserted after
+this record.
+
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my %options = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->check;
+ return $error if $error;
+
+ $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $options{'rate_prefix'} ) {
+ foreach my $rate_prefix ( @{$options{'rate_prefix'}} ) {
+ $rate_prefix->regionnum($self->regionnum);
+ $error = $rate_prefix->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ if ( $options{'dest_detail'} ) {
+ foreach my $rate_detail ( @{$options{'dest_detail'}} ) {
+ $rate_detail->dest_regionnum($self->regionnum);
+ $error = $rate_detail->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD [ , OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+Currently available options are: I<rate_prefix> and I<dest_detail>
+
+If I<rate_prefix> is set to an array reference of FS::rate_prefix objects, the
+objects will have their regionnum field set and will be inserted after this
+record. Any existing rate_prefix records associated with this record will be
+deleted.
+
+If I<dest_detail> is set to an array reference of FS::rate_detail objects, the
+objects will have their dest_regionnum field set and will be inserted after
+this record. Any existing rate_detail records associated with this record will
+be deleted.
+
+=cut
+
+sub replace {
+ my ($new, $old) = (shift, shift);
+ my %options = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my @old_rate_prefix = ();
+ @old_rate_prefix = $old->rate_prefix if $options{'rate_prefix'};
+ my @old_dest_detail = ();
+ @old_dest_detail = $old->dest_detail if $options{'dest_detail'};
+
+ my $error = $new->SUPER::replace($old);
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ foreach my $old_rate_prefix ( @old_rate_prefix ) {
+ my $error = $old_rate_prefix->delete;
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ foreach my $old_dest_detail ( @old_dest_detail ) {
+ my $error = $old_dest_detail->delete;
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ foreach my $rate_prefix ( @{$options{'rate_prefix'}} ) {
+ $rate_prefix->regionnum($new->regionnum);
+ $error = $rate_prefix->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ foreach my $rate_detail ( @{$options{'dest_detail'}} ) {
+ $rate_detail->dest_regionnum($new->regionnum);
+ $error = $rate_detail->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item check
+
+Checks all fields to make sure this is a valid region. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('regionnum')
+ || $self->ut_text('regionname')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item rate_prefix
+
+Returns all prefixes (see L<FS::rate_prefix>) for this region.
+
+=cut
+
+sub rate_prefix {
+ my $self = shift;
+
+ sort { $a->countrycode cmp $b->countrycode
+ or $a->npa cmp $b->npa
+ or $a->nxx cmp $b->nxx
+ }
+ qsearch( 'rate_prefix', { 'regionnum' => $self->regionnum } );
+}
+
+=item dest_detail
+
+Returns all rate details (see L<FS::rate_detail>) for this region as a
+destionation.
+
+=cut
+
+sub dest_detail {
+ my $self = shift;
+ qsearch( 'rate_detail', { 'dest_regionnum' => $self->regionnum, } );
+}
+
+=item prefixes_short
+
+Returns a string representing all the prefixes for this region.
+
+=cut
+
+sub prefixes_short {
+ my $self = shift;
+
+ my $countrycode = '';
+ my $out = '';
+
+ foreach my $rate_prefix ( $self->rate_prefix ) {
+ if ( $countrycode ne $rate_prefix->countrycode ) {
+ $out =~ s/, $//;
+ $countrycode = $rate_prefix->countrycode;
+ $out.= " +$countrycode ";
+ }
+ my $npa = $rate_prefix->npa;
+ if ( $countrycode eq '1' ) {
+ #$out .= '('. substr( $npa, 0, 3 ). ')';
+ $out .= substr( $npa, 0, 3 );
+ $out .= ' '. substr( $npa, 3 ) if length($npa) > 3;
+ } else {
+ $out .= $rate_prefix->npa;
+ }
+ $out .= '-'. $rate_prefix->nxx if $rate_prefix->nxx;
+ $out .= ', ';
+ }
+ $out =~ s/, $//;
+
+ $out;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/reason.pm b/FS/FS/reason.pm
new file mode 100644
index 0000000..5311ec5
--- /dev/null
+++ b/FS/FS/reason.pm
@@ -0,0 +1,184 @@
+package FS::reason;
+
+use strict;
+use vars qw( @ISA $DEBUG $me );
+use DBIx::DBSchema;
+use DBIx::DBSchema::Table;
+use DBIx::DBSchema::Column;
+use FS::Record qw( qsearch qsearchs dbh dbdef );
+use FS::reason_type;
+
+@ISA = qw(FS::Record);
+$DEBUG = 0;
+$me = '[FS::reason]';
+
+=head1 NAME
+
+FS::reason - Object methods for reason records
+
+=head1 SYNOPSIS
+
+ use FS::reason;
+
+ $record = new FS::reason \%hash;
+ $record = new FS::reason { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::reason object represents a reason message. FS::reason inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item reasonnum - primary key
+
+=item reason_type - index into FS::reason_type
+
+=item reason - text of the reason
+
+=item disabled - 'Y' or ''
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new reason. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'reason'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid reason. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('reasonnum')
+ || $self->ut_text('reason')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item reasontype
+
+Returns the reason_type (see <I>FS::reason_type</I>) associated with this reason.
+
+=cut
+
+sub reasontype {
+ qsearchs( 'reason_type', { 'typenum' => shift->reason_type } );
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+#
+#
+
+sub _upgrade_data { # class method
+ my ($self, %opts) = @_;
+ my $dbh = dbh;
+
+ warn "$me upgrading $self\n" if $DEBUG;
+
+ my $column = dbdef->table($self->table)->column('reason');
+ unless ($column->type eq 'text') { # assume history matches main table
+
+ # ideally this would be supported in DBIx-DBSchema and friends
+ warn "$me Shifting reason column to type 'text'\n" if $DEBUG;
+ foreach my $table ( $self->table, 'h_'. $self->table ) {
+ my @sql = ();
+
+ $column = dbdef->table($self->table)->column('reason');
+ my $columndef = $column->line($dbh);
+ $columndef =~ s/varchar\(\d+\)/text/i;
+
+ if ( $dbh->{Driver}->{Name} eq 'Pg' ) {
+
+ my $notnull = $columndef =~ s/not null//i;
+ push @sql,"ALTER TABLE $table RENAME reason TO freeside_upgrade_reason";
+ push @sql,"ALTER TABLE $table ADD $columndef";
+ push @sql,"UPDATE $table SET reason = freeside_upgrade_reason";
+ push @sql,"ALTER TABLE $table ALTER reason SET NOT NULL"
+ if $notnull;
+ push @sql,"ALTER TABLE $table DROP freeside_upgrade_reason";
+
+ } elsif ( $dbh->{Driver}->{Name} =~ /^mysql/i ){
+
+ #crap, this isn't working
+ #push @sql,"ALTER TABLE $table MODIFY reason ". $column->line($dbh);
+ warn "WARNING: reason table upgrade not yet supported for mysql, sorry";
+
+ } else {
+ die "watchu talkin' 'bout, Willis? (unsupported database type)";
+ }
+
+ foreach (@sql) {
+ my $sth = $dbh->prepare($_) or die $dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ }
+ }
+ }
+
+ '';
+
+}
+=back
+
+=head1 BUGS
+
+Here be termintes. Don't use on wooden computers.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/reason_type.pm b/FS/FS/reason_type.pm
new file mode 100644
index 0000000..482ea34
--- /dev/null
+++ b/FS/FS/reason_type.pm
@@ -0,0 +1,211 @@
+package FS::reason_type;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+our %class_name = (
+ 'C' => 'cancel',
+ 'R' => 'credit',
+ 'S' => 'suspend',
+);
+
+our %class_purpose = (
+ 'C' => 'explain why a customer package was cancelled',
+ 'R' => 'explain why a customer was credited',
+ 'S' => 'explain why a customer package was suspended',
+);
+
+=head1 NAME
+
+FS::reason_type - Object methods for reason_type records
+
+=head1 SYNOPSIS
+
+ use FS::reason_type;
+
+ $record = new FS::reason_type \%hash;
+ $record = new FS::reason_type { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::reason_type object represents a grouping of reasons. FS::reason_type
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item typenum - primary key
+
+=item class - currently 'C', 'R', or 'S' for cancel, credit, or suspend
+
+=item type - name of the type of reason
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new reason_type. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'reason_type'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid reason_type. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('typenum')
+ || $self->ut_enum('class', [ keys %class_name ] )
+ || $self->ut_text('type')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item reasons
+
+Returns a list of all reasons associated with this type.
+
+=cut
+
+sub reasons {
+ qsearch( 'reason', { 'reason_type' => shift->typenum } );
+}
+
+=item enabled_reasons
+
+Returns a list of enabled reasons associated with this type.
+
+=cut
+
+sub enabled_reasons {
+ qsearch( 'reason', { 'reason_type' => shift->typenum,
+ 'enabled' => '',
+ } );
+}
+
+# _populate_initial_data
+#
+# Used by FS::Setup to initialize a new database.
+#
+#
+
+sub _populate_initial_data { # class method
+ my ($self, %opts) = @_;
+
+ my $conf = new FS::Conf;
+
+ foreach ( keys %class_name ) {
+ my $object = $self->new( {'class' => $_,
+ 'type' => ucfirst($class_name{$_}). ' Reason',
+ } );
+ my $error = $object->insert();
+ die "error inserting $self into database: $error\n"
+ if $error;
+ }
+
+ my $object = qsearchs('reason_type', { 'class' => 'R' });
+ die "can't find credit reason type just inserted!\n"
+ unless $object;
+
+ foreach ( keys %FS::cust_credit::reasontype_map ) {
+# my $object = $self->new( {'class' => 'R',
+# 'type' => $FS::cust_credit::reasontype_map{$_},
+# } );
+# my $error = $object->insert();
+# die "error inserting $self into database: $error\n"
+# if $error;
+# # or clause for 1.7.x
+ $conf->set($_, $object->typenum)
+ or die "failed setting config";
+ }
+
+ '';
+
+}
+
+# _upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+#
+#
+
+sub _upgrade_data { # class method
+ my ($self, %opts) = @_;
+
+ foreach ( keys %class_name ) {
+ unless (scalar(qsearch('reason_type', { 'class' => $_ }))) {
+ my $object = $self->new( {'class' => $_,
+ 'type' => ucfirst($class_name{$_}),
+ } );
+ my $error = $object->insert();
+ die "error inserting $self into database: $error\n"
+ if $error;
+ }
+ }
+
+ '';
+
+}
+
+=back
+
+=head1 BUGS
+
+Here be termintes. Don't use on wooden computers.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/reg_code.pm b/FS/FS/reg_code.pm
new file mode 100644
index 0000000..f48ccf0
--- /dev/null
+++ b/FS/FS/reg_code.pm
@@ -0,0 +1,223 @@
+package FS::reg_code;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw(qsearch dbh);
+use FS::agent;
+use FS::reg_code_pkg;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::reg_code - One-time registration codes
+
+=head1 SYNOPSIS
+
+ use FS::reg_code;
+
+ $record = new FS::reg_code \%hash;
+ $record = new FS::reg_code { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::reg_code object is a one-time registration code. FS::reg_code inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item codenum - primary key
+
+=item code - registration code string
+
+=item agentnum - Agent (see L<FS::agent>)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new registration code. To add the code to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'reg_code'; }
+
+=item insert [ PKGPART_ARRAYREF ]
+
+Adds this record to the database. If an arrayref of pkgparts
+(see L<FS::part_pkg>) is specified, the appropriate reg_code_pkg records
+(see L<FS::reg_code_pkg>) will be inserted.
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( @_ ) {
+ my $pkgparts = shift;
+ foreach my $pkgpart ( @$pkgparts ) {
+ my $reg_code_pkg = new FS::reg_code_pkg ( {
+ 'codenum' => $self->codenum,
+ 'pkgpart' => $pkgpart,
+ } );
+ $error = $reg_code_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item delete
+
+Delete this record (and all associated reg_code_pkg records) from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $reg_code_pkg ( $self->reg_code_pkg ) {
+ my $error = $reg_code_pkg->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid registration code. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('codenum')
+ || $self->ut_alpha('code')
+ || $self->ut_foreign_key('agentnum', 'agent', 'agentnum')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item part_pkg
+
+Returns all package definitions (see L<FS::part_pkg> for this registration
+code.
+
+=cut
+
+sub part_pkg {
+ my $self = shift;
+ map { $_->part_pkg } $self->reg_code_pkg;
+}
+
+=item reg_code_pkg
+
+Returns all FS::reg_code_pkg records for this registration code.
+
+=cut
+
+sub reg_code_pkg {
+ my $self = shift;
+ qsearch('reg_code_pkg', { 'codenum' => $self->codenum } );
+}
+
+
+=back
+
+=head1 BUGS
+
+Feeping creaturitis.
+
+=head1 SEE ALSO
+
+L<FS::reg_code_pkg>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/reg_code_pkg.pm b/FS/FS/reg_code_pkg.pm
new file mode 100644
index 0000000..837b755
--- /dev/null
+++ b/FS/FS/reg_code_pkg.pm
@@ -0,0 +1,139 @@
+package FS::reg_code_pkg;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw(qsearchs);
+use FS::reg_code;
+use FS::part_pkg;
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::reg_code_pkg - Class linking registration codes (see L<FS::reg_code>) with package definitions (see L<FS::part_pkg>)
+
+=head1 SYNOPSIS
+
+ use FS::reg_code_pkg;
+
+ $record = new FS::reg_code_pkg \%hash;
+ $record = new FS::reg_code_pkg { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::reg_code_pkg object links a registration code to a package definition.
+FS::table_name inherits from FS::Record. The following fields are currently
+supported:
+
+=over 4
+
+=item codepkgnum - primary key
+
+=item codenum - registration code (see L<FS::reg_code>)
+
+=item pkgpart - package definition (see L<FS::part_pkg>)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new registration code. To add the registration code to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'reg_code_pkg'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('codepkgnum')
+ || $self->ut_foreign_key('codenum', 'reg_code', 'codenum')
+ || $self->ut_foreign_key('pkgpart', 'part_pkg', 'pkgpart')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item part_pkg
+
+Returns the package definition (see L<FS::part_pkg>)
+
+=cut
+
+sub part_pkg {
+ my $self = shift;
+ qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart } );
+}
+
+=back
+
+=head1 BUGS
+
+Feeping creaturitis.
+
+=head1 SEE ALSO
+
+L<FS::reg_code_pkg>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/registrar.pm b/FS/FS/registrar.pm
new file mode 100644
index 0000000..cf5dc49
--- /dev/null
+++ b/FS/FS/registrar.pm
@@ -0,0 +1,119 @@
+package FS::registrar;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::registrar - Object methods for registrar records
+
+=head1 SYNOPSIS
+
+ use FS::registrar;
+
+ $record = new FS::registrar \%hash;
+ $record = new FS::registrar { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::registrar object represents a registrar. FS::registrar inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item registrarnum - primary key
+
+=item registrarname -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new registrar. To add the registrar to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'registrar'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid registrar. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('registrarnum')
+ || $self->ut_text('registrarname')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/router.pm b/FS/FS/router.pm
new file mode 100755
index 0000000..7a9fda3
--- /dev/null
+++ b/FS/FS/router.pm
@@ -0,0 +1,152 @@
+package FS::router;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs qsearch );
+use FS::addr_block;
+
+@ISA = qw( FS::Record FS::m2m_Common );
+
+=head1 NAME
+
+FS::router - Object methods for router records
+
+=head1 SYNOPSIS
+
+ use FS::router;
+
+ $record = new FS::router \%hash;
+ $record = new FS::router { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::router record describes a broadband router, such as a DSLAM or a wireless
+ access point. FS::router inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item routernum - primary key
+
+=item routername - descriptive name for the router
+
+=item svcnum - svcnum of the owning FS::svc_broadband, if appropriate
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Create a new record. To add the record to the database, see "insert".
+
+=cut
+
+sub table { 'router'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('routernum')
+ || $self->ut_text('routername')
+ || $self->ut_agentnum_acl('agentnum', 'Broadband global configuration')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item addr_block
+
+Returns a list of FS::addr_block objects (address blocks) associated
+with this object.
+
+=cut
+
+sub addr_block {
+ my $self = shift;
+ return qsearch('addr_block', { routernum => $self->routernum });
+}
+
+=item part_svc_router
+
+Returns a list of FS::part_svc_router objects associated with this
+object. This is unlikely to be useful for any purpose other than retrieving
+the associated FS::part_svc objects. See below.
+
+=cut
+
+sub part_svc_router {
+ my $self = shift;
+ return qsearch('part_svc_router', { routernum => $self->routernum });
+}
+
+=item part_svc
+
+Returns a list of FS::part_svc objects associated with this object.
+
+=cut
+
+sub part_svc {
+ my $self = shift;
+ return map { qsearchs('part_svc', { svcpart => $_->svcpart }) }
+ $self->part_svc_router;
+}
+
+=item agent
+
+Returns the agent associated with this router, if any.
+
+=cut
+
+sub agent {
+ qsearchs('agent', { 'agentnum' => shift->agentnum });
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+FS::svc_broadband, FS::router, FS::addr_block, FS::part_svc,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/session.pm b/FS/FS/session.pm
new file mode 100644
index 0000000..615c8ae
--- /dev/null
+++ b/FS/FS/session.pm
@@ -0,0 +1,265 @@
+package FS::session;
+
+use strict;
+use vars qw( @ISA $conf $start $stop );
+use FS::UID qw( dbh );
+use FS::Record qw( qsearchs );
+use FS::svc_acct;
+use FS::port;
+use FS::nas;
+
+@ISA = qw(FS::Record);
+
+$FS::UID::callback{'FS::session'} = sub {
+ $conf = new FS::Conf;
+ $start = $conf->exists('session-start') ? $conf->config('session-start') : '';
+ $stop = $conf->exists('session-stop') ? $conf->config('session-stop') : '';
+};
+
+=head1 NAME
+
+FS::session - Object methods for session records
+
+=head1 SYNOPSIS
+
+ use FS::session;
+
+ $record = new FS::session \%hash;
+ $record = new FS::session {
+ 'portnum' => 1,
+ 'svcnum' => 2,
+ 'login' => $timestamp,
+ 'logout' => $timestamp,
+ };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->nas_heartbeat($timestamp);
+
+=head1 DESCRIPTION
+
+An FS::session object represents an user login session. FS::session inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item sessionnum - primary key
+
+=item portnum - NAS port for this session - see L<FS::port>
+
+=item svcnum - User for this session - see L<FS::svc_acct>
+
+=item login - timestamp indicating the beginning of this user session.
+
+=item logout - timestamp indicating the end of this user session. May be null,
+ which indicates a currently open session.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new session. To add the session to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'session'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false. If the `login' field is empty, it is replaced with
+the current time.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ $error = $self->check;
+ return $error if $error;
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( qsearchs('session', { 'portnum' => $self->portnum, 'logout' => '' } ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "a session on that port is already open!";
+ }
+
+ $self->setfield('login', time()) unless $self->getfield('login');
+
+ $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $self->nas_heartbeat($self->getfield('login'));
+
+ #session-starting callback
+ #redundant with heartbeat, yuck
+ my $port = qsearchs('port',{'portnum'=>$self->portnum});
+ my $nas = qsearchs('nas',{'nasnum'=>$port->nasnum});
+ #kcuy
+ my( $ip, $nasip, $nasfqdn ) = ( $port->ip, $nas->nasip, $nas->nasfqdn );
+ system( eval qq("$start") ) if $start;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false. If the `logout' field is empty,
+it is replaced with the current time.
+
+=cut
+
+sub replace {
+ my($self, $old) = @_;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $error = $self->check;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $self->setfield('logout', time()) unless $self->getfield('logout');
+
+ $error = $self->SUPER::replace($old);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $self->nas_heartbeat($self->getfield('logout'));
+
+ #session-ending callback
+ #redundant with heartbeat, yuck
+ my $port = qsearchs('port',{'portnum'=>$self->portnum});
+ my $nas = qsearchs('nas',{'nasnum'=>$port->nasnum});
+ #kcuy
+ my( $ip, $nasip, $nasfqdn ) = ( $port->ip, $nas->nasip, $nas->nasfqdn );
+ system( eval qq("$stop") ) if $stop;
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+=item check
+
+Checks all fields to make sure this is a valid session. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+ my $error =
+ $self->ut_numbern('sessionnum')
+ || $self->ut_number('portnum')
+ || $self->ut_number('svcnum')
+ || $self->ut_numbern('login')
+ || $self->ut_numbern('logout')
+ ;
+ return $error if $error;
+ return "Unknown svcnum"
+ unless qsearchs('svc_acct', { 'svcnum' => $self->svcnum } );
+ $self->SUPER::check;
+}
+
+=item nas_heartbeat
+
+Heartbeats the nas associated with this session (see L<FS::nas>).
+
+=cut
+
+sub nas_heartbeat {
+ my $self = shift;
+ my $port = qsearchs('port',{'portnum'=>$self->portnum});
+ my $nas = qsearchs('nas',{'nasnum'=>$port->nasnum});
+ $nas->heartbeat(shift);
+}
+
+=item svc_acct
+
+Returns the svc_acct record associated with this session (see L<FS::svc_acct>).
+
+=cut
+
+sub svc_acct {
+ my $self = shift;
+ qsearchs('svc_acct', { 'svcnum' => $self->svcnum } );
+}
+
+=back
+
+=head1 BUGS
+
+Maybe you shouldn't be able to insert a session if there's currently an open
+session on that port. Or maybe the open session on that port should be flagged
+as problematic? autoclosed? *sigh*
+
+Hmm, sessions refer to current svc_acct records... probably need to constrain
+deletions to svc_acct records such that no svc_acct records are deleted which
+have a session (even if long-closed).
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm
new file mode 100644
index 0000000..da1cfe1
--- /dev/null
+++ b/FS/FS/svc_Common.pm
@@ -0,0 +1,852 @@
+package FS::svc_Common;
+
+use strict;
+use vars qw( @ISA $noexport_hack $DEBUG $me );
+use Carp qw( cluck carp croak ); #specify cluck have to specify them all..
+use Scalar::Util qw( blessed );
+use FS::Record qw( qsearch qsearchs fields dbh );
+use FS::cust_main_Mixin;
+use FS::cust_svc;
+use FS::part_svc;
+use FS::queue;
+use FS::cust_main;
+use FS::inventory_item;
+use FS::inventory_class;
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
+
+$me = '[FS::svc_Common]';
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::svc_Common - Object method for all svc_ records
+
+=head1 SYNOPSIS
+
+use FS::svc_Common;
+
+@ISA = qw( FS::svc_Common );
+
+=head1 DESCRIPTION
+
+FS::svc_Common is intended as a base class for table-specific classes to
+inherit from, i.e. FS::svc_acct. FS::svc_Common inherits from FS::Record.
+
+=head1 METHODS
+
+=over 4
+
+=item search_sql_field FIELD STRING
+
+Class method which returns an SQL fragment to search for STRING in FIELD.
+
+It is now case-insensitive by default.
+
+=cut
+
+sub search_sql_field {
+ my( $class, $field, $string ) = @_;
+ my $table = $class->table;
+ my $q_string = dbh->quote($string);
+ "LOWER($table.$field) = LOWER($q_string)";
+}
+
+#fallback for services that don't provide a search...
+sub search_sql {
+ #my( $class, $string ) = @_;
+ '1 = 0'; #false
+}
+
+=item new
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+
+ unless ( defined ( $self->table ) ) {
+ $self->{'Table'} = shift;
+ carp "warning: FS::Record::new called with table name ". $self->{'Table'};
+ }
+
+ #$self->{'Hash'} = shift;
+ my $newhash = shift;
+ $self->{'Hash'} = { map { $_ => $newhash->{$_} } qw(svcnum svcpart) };
+
+ $self->setdefault( $self->_fieldhandlers )
+ unless $self->svcnum;
+
+ $self->{'Hash'}{$_} = $newhash->{$_}
+ foreach grep { defined($newhash->{$_}) && length($newhash->{$_}) }
+ keys %$newhash;
+
+ foreach my $field ( grep !defined($self->{'Hash'}{$_}), $self->fields ) {
+ $self->{'Hash'}{$field}='';
+ }
+
+ $self->_rebless if $self->can('_rebless');
+
+ $self->{'modified'} = 0;
+
+ $self->_cache($self->{'Hash'}, shift) if $self->can('_cache') && @_;
+
+ $self;
+}
+
+#empty default
+sub _fieldhandlers { {}; }
+
+sub virtual_fields {
+
+ # This restricts the fields based on part_svc_column and the svcpart of
+ # the service. There are four possible cases:
+ # 1. svcpart passed as part of the svc_x hash.
+ # 2. svcpart fetched via cust_svc based on svcnum.
+ # 3. No svcnum or svcpart. In this case, return ALL the fields with
+ # dbtable eq $self->table.
+ # 4. Called via "fields('svc_acct')" or something similar. In this case
+ # there is no $self object.
+
+ my $self = shift;
+ my $svcpart;
+ my @vfields = $self->SUPER::virtual_fields;
+
+ return @vfields unless (ref $self); # Case 4
+
+ if ($self->svcpart) { # Case 1
+ $svcpart = $self->svcpart;
+ } elsif ( $self->svcnum
+ && qsearchs('cust_svc',{'svcnum'=>$self->svcnum} )
+ ) { #Case 2
+ $svcpart = $self->cust_svc->svcpart;
+ } else { # Case 3
+ $svcpart = '';
+ }
+
+ if ($svcpart) { #Cases 1 and 2
+ my %flags = map { $_->columnname, $_->columnflag } (
+ qsearch ('part_svc_column', { svcpart => $svcpart } )
+ );
+ return grep { not ( defined($flags{$_}) && $flags{$_} eq 'X') } @vfields;
+ } else { # Case 3
+ return @vfields;
+ }
+ return ();
+}
+
+=item label
+
+svc_Common provides a fallback label subroutine that just returns the svcnum.
+
+=cut
+
+sub label {
+ my $self = shift;
+ cluck "warning: ". ref($self). " not loaded or missing label method; ".
+ "using svcnum";
+ $self->svcnum;
+}
+
+=item check
+
+Checks the validity of fields in this record.
+
+At present, this does nothing but call FS::Record::check (which, in turn,
+does nothing but run virtual field checks).
+
+=cut
+
+sub check {
+ my $self = shift;
+ $self->SUPER::check;
+}
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+Currently available options are: I<jobnums>, I<child_objects> and
+I<depend_jobnum>.
+
+If I<jobnum> is set to an array reference, the jobnums of any export jobs will
+be added to the referenced array.
+
+If I<child_objects> is set to an array reference of FS::tablename objects (for
+example, FS::acct_snarf objects), they will have their svcnum field set and
+will be inserted after this record, but before any exports are run. Each
+element of the array can also optionally be a two-element array reference
+containing the child object and the name of an alternate field to be filled in
+with the newly-inserted svcnum, for example C<[ $svc_forward, 'srcsvc' ]>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+If I<export_args> is set to an array reference, the referenced list will be
+passed to export commands.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my %options = @_;
+ warn "[$me] insert called with options ".
+ join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
+ if $DEBUG;
+
+ my @jobnums = ();
+ local $FS::queue::jobnums = \@jobnums;
+ warn "[$me] insert: set \$FS::queue::jobnums to $FS::queue::jobnums\n"
+ if $DEBUG;
+ my $objects = $options{'child_objects'} || [];
+ my $depend_jobnums = $options{'depend_jobnum'} || [];
+ $depend_jobnums = [ $depend_jobnums ] unless ref($depend_jobnums);
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $svcnum = $self->svcnum;
+ my $cust_svc = $svcnum ? qsearchs('cust_svc',{'svcnum'=>$self->svcnum}) : '';
+ #unless ( $svcnum ) {
+ if ( !$svcnum or !$cust_svc ) {
+ $cust_svc = new FS::cust_svc ( {
+ #hua?# 'svcnum' => $svcnum,
+ 'svcnum' => $self->svcnum,
+ 'pkgnum' => $self->pkgnum,
+ 'svcpart' => $self->svcpart,
+ } );
+ my $error = $cust_svc->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $svcnum = $self->svcnum($cust_svc->svcnum);
+ } else {
+ #$cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
+ unless ( $cust_svc ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "no cust_svc record found for svcnum ". $self->svcnum;
+ }
+ $self->pkgnum($cust_svc->pkgnum);
+ $self->svcpart($cust_svc->svcpart);
+ }
+
+ my $error = $self->set_auto_inventory
+ || $self->check
+ || $self->_check_duplicate
+ || $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ foreach my $object ( @$objects ) {
+ my($field, $obj);
+ if ( ref($object) eq 'ARRAY' ) {
+ ($obj, $field) = @$object;
+ } else {
+ $obj = $object;
+ $field = 'svcnum';
+ }
+ $obj->$field($self->svcnum);
+ $error = $obj->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ #new-style exports!
+ unless ( $noexport_hack ) {
+
+ warn "[$me] insert: \$FS::queue::jobnums is $FS::queue::jobnums\n"
+ if $DEBUG;
+
+ my $export_args = $options{'export_args'} || [];
+
+ foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+ my $error = $part_export->export_insert($self, @$export_args);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "exporting to ". $part_export->exporttype.
+ " (transaction rolled back): $error";
+ }
+ }
+
+ foreach my $depend_jobnum ( @$depend_jobnums ) {
+ warn "[$me] inserting dependancies on supplied job $depend_jobnum\n"
+ if $DEBUG;
+ foreach my $jobnum ( @jobnums ) {
+ my $queue = qsearchs('queue', { 'jobnum' => $jobnum } );
+ warn "[$me] inserting dependancy for job $jobnum on $depend_jobnum\n"
+ if $DEBUG;
+ my $error = $queue->depend_insert($depend_jobnum);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error queuing job dependancy: $error";
+ }
+ }
+ }
+
+ }
+
+ if ( exists $options{'jobnums'} ) {
+ push @{ $options{'jobnums'} }, @jobnums;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+#fallbacks
+sub _check_duplcate { ''; }
+sub table_dupcheck_fields { (); }
+
+=item delete [ , OPTION => VALUE ... ]
+
+Deletes this account from the database. If there is an error, returns the
+error, otherwise returns false.
+
+The corresponding FS::cust_svc record will be deleted as well.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ my %options = @_;
+ my $export_args = $options{'export_args'} || [];
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::delete
+ || $self->export('delete', @$export_args)
+ || $self->return_inventory
+ || $self->cust_svc->delete
+ ;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+=item replace [ OLD_RECORD ] [ HASHREF | OPTION => VALUE ]
+
+Replaces OLD_RECORD with this one. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub replace {
+ my $new = shift;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $new->replace_old;
+
+ my $options =
+ ( ref($_[0]) eq 'HASH' )
+ ? shift
+ : { @_ };
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $new->set_auto_inventory;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ #redundant, but so any duplicate fields are maniuplated as appropriate
+ # (svc_phone.phonenum)
+ $error = $new->check;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ #if ( $old->username ne $new->username || $old->domsvc != $new->domsvc ) {
+ if ( grep { $old->$_ ne $new->$_ } $new->table_dupcheck_fields ) {
+
+ $new->svcpart( $new->cust_svc->svcpart ) unless $new->svcpart;
+ $error = $new->_check_duplicate;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $error = $new->SUPER::replace($old);
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ #new-style exports!
+ unless ( $noexport_hack ) {
+
+ my $export_args = $options->{'export_args'} || [];
+
+ #not quite false laziness, but same pattern as FS::svc_acct::replace and
+ #FS::part_export::sqlradius::_export_replace. List::Compare or something
+ #would be useful but too much of a pain in the ass to deploy
+
+ my @old_part_export = $old->cust_svc->part_svc->part_export;
+ my %old_exportnum = map { $_->exportnum => 1 } @old_part_export;
+ my @new_part_export =
+ $new->svcpart
+ ? qsearchs('part_svc', { svcpart=>$new->svcpart } )->part_export
+ : $new->cust_svc->part_svc->part_export;
+ my %new_exportnum = map { $_->exportnum => 1 } @new_part_export;
+
+ foreach my $delete_part_export (
+ grep { ! $new_exportnum{$_->exportnum} } @old_part_export
+ ) {
+ my $error = $delete_part_export->export_delete($old, @$export_args);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error deleting, export to ". $delete_part_export->exporttype.
+ " (transaction rolled back): $error";
+ }
+ }
+
+ foreach my $replace_part_export (
+ grep { $old_exportnum{$_->exportnum} } @new_part_export
+ ) {
+ my $error =
+ $replace_part_export->export_replace( $new, $old, @$export_args);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error exporting to ". $replace_part_export->exporttype.
+ " (transaction rolled back): $error";
+ }
+ }
+
+ foreach my $insert_part_export (
+ grep { ! $old_exportnum{$_->exportnum} } @new_part_export
+ ) {
+ my $error = $insert_part_export->export_insert($new, @$export_args );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error inserting export to ". $insert_part_export->exporttype.
+ " (transaction rolled back): $error";
+ }
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item setfixed
+
+Sets any fixed fields for this service (see L<FS::part_svc>). If there is an
+error, returns the error, otherwise returns the FS::part_svc object (use ref()
+to test the return). Usually called by the check method.
+
+=cut
+
+sub setfixed {
+ my $self = shift;
+ $self->setx('F', @_);
+}
+
+=item setdefault
+
+Sets all fields to their defaults (see L<FS::part_svc>), overriding their
+current values. If there is an error, returns the error, otherwise returns
+the FS::part_svc object (use ref() to test the return).
+
+=cut
+
+sub setdefault {
+ my $self = shift;
+ $self->setx('D', @_ );
+}
+
+=item set_default_and_fixed
+
+=cut
+
+sub set_default_and_fixed {
+ my $self = shift;
+ $self->setx( [ 'D', 'F' ], @_ );
+}
+
+=item setx FLAG | FLAG_ARRAYREF , [ CALLBACK_HASHREF ]
+
+Sets fields according to the passed in flag or arrayref of flags.
+
+Optionally, a hashref of field names and callback coderefs can be passed.
+If a coderef exists for a given field name, instead of setting the field,
+the coderef is called with the column value (part_svc_column.columnvalue)
+as the single parameter.
+
+=cut
+
+sub setx {
+ my $self = shift;
+ my $x = shift;
+ my @x = ref($x) ? @$x : ($x);
+ my $coderef = scalar(@_) ? shift : $self->_fieldhandlers;
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ ;
+ return $error if $error;
+
+ my $part_svc = $self->part_svc;
+ return "Unknown svcpart" unless $part_svc;
+
+ #set default/fixed/whatever fields from part_svc
+
+ foreach my $part_svc_column (
+ grep { my $f = $_->columnflag; grep { $f eq $_ } @x } #columnflag in @x
+ $part_svc->all_part_svc_column
+ ) {
+
+ my $columnname = $part_svc_column->columnname;
+ my $columnvalue = $part_svc_column->columnvalue;
+
+ $columnvalue = &{ $coderef->{$columnname} }( $self, $columnvalue )
+ if exists( $coderef->{$columnname} );
+ $self->setfield( $columnname, $columnvalue );
+
+ }
+
+ $part_svc;
+
+}
+
+sub part_svc {
+ my $self = shift;
+
+ #get part_svc
+ my $svcpart;
+ if ( $self->get('svcpart') ) {
+ $svcpart = $self->get('svcpart');
+ } elsif ( $self->svcnum && qsearchs('cust_svc', {'svcnum'=>$self->svcnum}) ) {
+ my $cust_svc = $self->cust_svc;
+ return "Unknown svcnum" unless $cust_svc;
+ $svcpart = $cust_svc->svcpart;
+ }
+
+ qsearchs( 'part_svc', { 'svcpart' => $svcpart } );
+
+}
+
+=item set_auto_inventory
+
+Sets any fields which auto-populate from inventory (see L<FS::part_svc>).
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub set_auto_inventory {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ ;
+ return $error if $error;
+
+ my $part_svc = $self->part_svc;
+ return "Unkonwn svcpart" unless $part_svc;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #set default/fixed/whatever fields from part_svc
+ my $table = $self->table;
+ foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
+ my $part_svc_column = $part_svc->part_svc_column($field);
+ if ( $part_svc_column->columnflag eq 'A' && $self->$field() eq '' ) {
+
+ my $classnum = $part_svc_column->columnvalue;
+ my $inventory_item = qsearchs({
+ 'table' => 'inventory_item',
+ 'hashref' => { 'classnum' => $classnum,
+ 'svcnum' => '',
+ },
+ 'extra_sql' => 'LIMIT 1 FOR UPDATE',
+ });
+
+ unless ( $inventory_item ) {
+ $dbh->rollback if $oldAutoCommit;
+ my $inventory_class =
+ qsearchs('inventory_class', { 'classnum' => $classnum } );
+ return "Can't find inventory_class.classnum $classnum"
+ unless $inventory_class;
+ return "Out of ". $inventory_class->classname. "s\n"; #Lingua:: BS
+ #for pluralizing
+ }
+
+ $inventory_item->svcnum( $self->svcnum );
+ my $ierror = $inventory_item->replace();
+ if ( $ierror ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error provisioning inventory: $ierror";
+
+ }
+
+ $self->setfield( $field, $inventory_item->item );
+
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item return_inventory
+
+=cut
+
+sub return_inventory {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $inventory_item ( $self->inventory_item ) {
+ $inventory_item->svcnum('');
+ my $error = $inventory_item->replace();
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error returning inventory: $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+}
+
+=item inventory_item
+
+Returns the inventory items associated with this svc_ record, as
+FS::inventory_item objects (see L<FS::inventory_item>.
+
+=cut
+
+sub inventory_item {
+ my $self = shift;
+ qsearch({
+ 'table' => 'inventory_item',
+ 'hashref' => { 'svcnum' => $self->svcnum, },
+ });
+}
+
+=item cust_svc
+
+Returns the cust_svc record associated with this svc_ record, as a FS::cust_svc
+object (see L<FS::cust_svc>).
+
+=cut
+
+sub cust_svc {
+ my $self = shift;
+ qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
+}
+
+=item suspend
+
+Runs export_suspend callbacks.
+
+=cut
+
+sub suspend {
+ my $self = shift;
+ my %options = @_;
+ my $export_args = $options{'export_args'} || [];
+ $self->export('suspend', @$export_args);
+}
+
+=item unsuspend
+
+Runs export_unsuspend callbacks.
+
+=cut
+
+sub unsuspend {
+ my $self = shift;
+ my %options = @_;
+ my $export_args = $options{'export_args'} || [];
+ $self->export('unsuspend', @$export_args);
+}
+
+=item export_links
+
+Runs export_links callbacks and returns the links.
+
+=cut
+
+sub export_links {
+ my $self = shift;
+ my $return = [];
+ $self->export('links', $return);
+ $return;
+}
+
+=item export HOOK [ EXPORT_ARGS ]
+
+Runs the provided export hook (i.e. "suspend", "unsuspend") for this service.
+
+=cut
+
+sub export {
+ my( $self, $method ) = ( shift, shift );
+
+ $method = "export_$method" unless $method =~ /^export_/;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #new-style exports!
+ unless ( $noexport_hack ) {
+ foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+ next unless $part_export->can($method);
+ my $error = $part_export->$method($self, @_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error exporting $method event to ". $part_export->exporttype.
+ " (transaction rolled back): $error";
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item overlimit
+
+Sets or retrieves overlimit date.
+
+=cut
+
+sub overlimit {
+ my $self = shift;
+ $self->cust_svc->overlimit(@_);
+}
+
+=item cancel
+
+Stub - returns false (no error) so derived classes don't need to define this
+methods. Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+This method is called *before* the deletion step which actually deletes the
+services. This method should therefore only be used for "pre-deletion"
+cancellation steps, if necessary.
+
+=cut
+
+sub cancel { ''; }
+
+=item clone_suspended
+
+Constructor used by FS::part_export::_export_suspend fallback. Stub returning
+same object for svc_ classes which don't implement a suspension fallback
+(everything except svc_acct at the moment). Document better.
+
+=cut
+
+sub clone_suspended {
+ shift;
+}
+
+=item clone_kludge_unsuspend
+
+Constructor used by FS::part_export::_export_unsuspend fallback. Stub returning
+same object for svc_ classes which don't implement a suspension fallback
+(everything except svc_acct at the moment). Document better.
+
+=cut
+
+sub clone_kludge_unsuspend {
+ shift;
+}
+
+=back
+
+=head1 BUGS
+
+The setfixed method return value.
+
+B<export> method isn't used by insert and replace methods yet.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, schema.html
+from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_External_Common.pm b/FS/FS/svc_External_Common.pm
new file mode 100644
index 0000000..a5805aa
--- /dev/null
+++ b/FS/FS/svc_External_Common.pm
@@ -0,0 +1,199 @@
+package FS::svc_External_Common;
+
+use strict;
+use vars qw(@ISA);
+use FS::svc_Common;
+
+@ISA = qw( FS::svc_Common );
+
+=head1 NAME
+
+FS::svc_external - Object methods for svc_external records
+
+=head1 SYNOPSIS
+
+ use FS::svc_external;
+
+ $record = new FS::svc_external \%hash;
+ $record = new FS::svc_external { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+FS::svc_External_Common is intended as a base class for table-specific classes
+to inherit from. FS::svc_External_Common is used for services which connect
+to externally tracked services via "id" and "table" fields.
+
+FS::svc_External_Common inherits from FS::svc_Common.
+
+The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key
+
+=item id - unique number of external record
+
+=item title - for invoice line items
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item search_sql
+
+Provides a default search_sql method which returns an SQL fragment to search
+the B<title> field.
+
+=cut
+
+sub search_sql {
+ my($class, $string) = @_;
+ $class->search_sql_field('title', $string);
+}
+
+=item new HASHREF
+
+Creates a new external service. To add the external service to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+=item label
+
+Returns a string identifying this external service in the form "id:title"
+
+=cut
+
+sub label {
+ my $self = shift;
+ $self->id. ':'. $self->title;
+}
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this external service to the database. If there is an error, returns the
+error, otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+Currently available options are: I<depend_jobnum>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+=cut
+
+#sub insert {
+# my $self = shift;
+# my $error;
+#
+# $error = $self->SUPER::insert(@_);
+# return $error if $error;
+#
+# '';
+#}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+#sub delete {
+# my $self = shift;
+# my $error;
+#
+# $error = $self->SUPER::delete;
+# return $error if $error;
+#
+# '';
+#}
+
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+#sub replace {
+# my ( $new, $old ) = ( shift, shift );
+# my $error;
+#
+# $error = $new->SUPER::replace($old);
+# return $error if $error;
+#
+# '';
+#}
+
+=item suspend
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid external service. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $x = $self->setfixed;
+ return $x unless ref($x);
+ my $part_svc = $x;
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ || $self->ut_numbern('id')
+ || $self->ut_textn('title')
+ ;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
+L<FS::cust_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_Parent_Mixin.pm b/FS/FS/svc_Parent_Mixin.pm
new file mode 100644
index 0000000..4501baf
--- /dev/null
+++ b/FS/FS/svc_Parent_Mixin.pm
@@ -0,0 +1,103 @@
+package FS::svc_Parent_Mixin;
+
+use strict;
+use NEXT;
+use FS::Record qw(qsearch qsearchs);
+use FS::cust_svc;
+
+=head1 NAME
+
+FS::svc_Parent_Mixin - Mixin class for svc_ classes with a parent_svcnum field
+
+=head1 SYNOPSIS
+
+package FS::svc_table;
+use vars qw(@ISA);
+@ISA = qw( FS::svc_Parent_Mixin FS::svc_Common );
+
+=head1 DESCRIPTION
+
+This is a mixin class for svc_ classes that contain a parent_svcnum field.
+
+=cut
+
+=head1 METHODS
+
+=over 4
+
+=item parent_cust_svc
+
+Returns the parent FS::cust_svc object.
+
+=cut
+
+sub parent_cust_svc {
+ my $self = shift;
+ qsearchs('cust_svc', { 'svcnum' => $self->parent_svcnum } );
+}
+
+=item parent_svc_x
+
+Returns the corresponding parent FS::svc_ object.
+
+=cut
+
+sub parent_svc_x {
+ my $self = shift;
+ $self->parent_cust_svc->svc_x;
+}
+
+=item children_cust_svc
+
+Returns a list of any child FS::cust_svc objects.
+
+Note: This is not recursive; it only returns direct children.
+
+=cut
+
+sub children_cust_svc {
+ my $self = shift;
+ qsearch('cust_svc', { 'parent_svcnum' => $self->svcnum } );
+}
+
+=item children_svc_x
+
+Returns the corresponding list of child FS::svc_ objects.
+
+=cut
+
+sub children_svc_x {
+ my $self = shift;
+ map { $_->svc_x } $self->children_cust_svc;
+}
+
+=item check
+
+This class provides a check subroutine which takes care of checking the
+parent_svcnum field. The svc_ class which uses it will call SUPER::check at
+the end of its own checks, and this class will call NEXT::check to pass
+the check "up the chain" (see L<NEXT>).
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_foreign_keyn('parent_svcnum', 'cust_svc', 'svcnum')
+ || $self->NEXT::check;
+
+}
+
+=back
+
+=head1 BUGS
+
+Do we need a recursive child finder for multi-layered children?
+
+=head1 SEE ALSO
+
+L<FS::svc_Common>, L<FS::Record>
+
+=cut
+
+1;
diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm
new file mode 100644
index 0000000..6f11051
--- /dev/null
+++ b/FS/FS/svc_acct.pm
@@ -0,0 +1,2683 @@
+package FS::svc_acct;
+
+use strict;
+use vars qw( @ISA $DEBUG $me $conf $skip_fuzzyfiles
+ $dir_prefix @shells $usernamemin
+ $usernamemax $passwordmin $passwordmax
+ $username_ampersand $username_letter $username_letterfirst
+ $username_noperiod $username_nounderscore $username_nodash
+ $username_uppercase $username_percent
+ $password_noampersand $password_noexclamation
+ $warning_template $warning_from $warning_subject $warning_mimetype
+ $warning_cc
+ $smtpmachine
+ $radius_password $radius_ip
+ $dirhash
+ @saltset @pw_set );
+use Scalar::Util qw( blessed );
+use Carp;
+use Fcntl qw(:flock);
+use Date::Format;
+use Crypt::PasswdMD5 1.2;
+use Data::Dumper;
+use Authen::Passphrase;
+use FS::UID qw( datasrc driver_name );
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs fields dbh dbdef );
+use FS::Msgcat qw(gettext);
+use FS::UI::bytecount;
+use FS::svc_Common;
+use FS::cust_svc;
+use FS::part_svc;
+use FS::svc_acct_pop;
+use FS::cust_main_invoice;
+use FS::svc_domain;
+use FS::raddb;
+use FS::queue;
+use FS::radius_usergroup;
+use FS::export_svc;
+use FS::part_export;
+use FS::svc_forward;
+use FS::svc_www;
+use FS::cdr;
+
+@ISA = qw( FS::svc_Common );
+
+$DEBUG = 0;
+$me = '[FS::svc_acct]';
+
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+ $dir_prefix = $conf->config('home');
+ @shells = $conf->config('shells');
+ $usernamemin = $conf->config('usernamemin') || 2;
+ $usernamemax = $conf->config('usernamemax');
+ $passwordmin = $conf->config('passwordmin') || 6;
+ $passwordmax = $conf->config('passwordmax') || 8;
+ $username_letter = $conf->exists('username-letter');
+ $username_letterfirst = $conf->exists('username-letterfirst');
+ $username_noperiod = $conf->exists('username-noperiod');
+ $username_nounderscore = $conf->exists('username-nounderscore');
+ $username_nodash = $conf->exists('username-nodash');
+ $username_uppercase = $conf->exists('username-uppercase');
+ $username_ampersand = $conf->exists('username-ampersand');
+ $username_percent = $conf->exists('username-percent');
+ $password_noampersand = $conf->exists('password-noexclamation');
+ $password_noexclamation = $conf->exists('password-noexclamation');
+ $dirhash = $conf->config('dirhash') || 0;
+ if ( $conf->exists('warning_email') ) {
+ $warning_template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", $conf->config('warning_email') ]
+ ) or warn "can't create warning email template: $Text::Template::ERROR";
+ $warning_from = $conf->config('warning_email-from'); # || 'your-isp-is-dum'
+ $warning_subject = $conf->config('warning_email-subject') || 'Warning';
+ $warning_mimetype = $conf->config('warning_email-mimetype') || 'text/plain';
+ $warning_cc = $conf->config('warning_email-cc');
+ } else {
+ $warning_template = '';
+ $warning_from = '';
+ $warning_subject = '';
+ $warning_mimetype = '';
+ $warning_cc = '';
+ }
+ $smtpmachine = $conf->config('smtpmachine');
+ $radius_password = $conf->config('radius-password') || 'Password';
+ $radius_ip = $conf->config('radius-ip') || 'Framed-IP-Address';
+ @pw_set = ( 'A'..'Z' ) if $conf->exists('password-generated-allcaps');
+}
+);
+
+@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
+@pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '!', '.', ',' );
+
+sub _cache {
+ my $self = shift;
+ my ( $hashref, $cache ) = @_;
+ if ( $hashref->{'svc_acct_svcnum'} ) {
+ $self->{'_domsvc'} = FS::svc_domain->new( {
+ 'svcnum' => $hashref->{'domsvc'},
+ 'domain' => $hashref->{'svc_acct_domain'},
+ 'catchall' => $hashref->{'svc_acct_catchall'},
+ } );
+ }
+}
+
+=head1 NAME
+
+FS::svc_acct - Object methods for svc_acct records
+
+=head1 SYNOPSIS
+
+ use FS::svc_acct;
+
+ $record = new FS::svc_acct \%hash;
+ $record = new FS::svc_acct { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+ %hash = $record->radius;
+
+ %hash = $record->radius_reply;
+
+ %hash = $record->radius_check;
+
+ $domain = $record->domain;
+
+ $svc_domain = $record->svc_domain;
+
+ $email = $record->email;
+
+ $seconds_since = $record->seconds_since($timestamp);
+
+=head1 DESCRIPTION
+
+An FS::svc_acct object represents an account. FS::svc_acct inherits from
+FS::svc_Common. The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key (assigned automatcially for new accounts)
+
+=item username
+
+=item _password - generated if blank
+
+=item _password_encoding - plain, crypt, ldap (or empty for autodetection)
+
+=item sec_phrase - security phrase
+
+=item popnum - Point of presence (see L<FS::svc_acct_pop>)
+
+=item uid
+
+=item gid
+
+=item finger - GECOS
+
+=item dir - set automatically if blank (and uid is not)
+
+=item shell
+
+=item quota - (unimplementd)
+
+=item slipip - IP address
+
+=item seconds -
+
+=item upbytes -
+
+=item downbytes -
+
+=item totalbytes -
+
+=item domsvc - svcnum from svc_domain
+
+=item radius_I<Radius_Attribute> - I<Radius-Attribute> (reply)
+
+=item rc_I<Radius_Attribute> - I<Radius-Attribute> (check)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new account. To add the account to the database, see L<"insert">.
+
+=cut
+
+sub table_info {
+ {
+ 'name' => 'Account',
+ 'longname_plural' => 'Access accounts and mailboxes',
+ 'sorts' => [ 'username', 'uid', 'seconds', 'last_login' ],
+ 'display_weight' => 10,
+ 'cancel_weight' => 50,
+ 'fields' => {
+ 'dir' => 'Home directory',
+ 'uid' => {
+ label => 'UID',
+ def_label => 'UID (set to fixed and blank for no UIDs)',
+ type => 'text',
+ },
+ 'slipip' => 'IP address',
+ # 'popnum' => qq!<A HREF="$p/browse/svc_acct_pop.cgi/">POP number</A>!,
+ 'popnum' => {
+ label => 'Access number',
+ type => 'select',
+ select_table => 'svc_acct_pop',
+ select_key => 'popnum',
+ select_label => 'city',
+ disable_select => 1,
+ },
+ 'username' => {
+ label => 'Username',
+ type => 'text',
+ disable_default => 1,
+ disable_fixed => 1,
+ disable_select => 1,
+ },
+ 'quota' => {
+ label => 'Quota',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ },
+ '_password' => 'Password',
+ 'gid' => {
+ label => 'GID',
+ def_label => 'GID (when blank, defaults to UID)',
+ type => 'text',
+ },
+ 'shell' => {
+ #desc =>'Shell (all service definitions should have a default or fixed shell that is present in the <b>shells</b> configuration file, set to blank for no shell tracking)',
+ label => 'Shell',
+ def_label=> 'Shell (set to blank for no shell tracking)',
+ type =>'select',
+ #select_list => [ $conf->config('shells') ],
+ select_list => [ $conf ? $conf->config('shells') : () ],
+ disable_inventory => 1,
+ disable_select => 1,
+ },
+ 'finger' => 'Real name (GECOS)',
+ 'domsvc' => {
+ label => 'Domain',
+ #def_label => 'svcnum from svc_domain',
+ type => 'select',
+ select_table => 'svc_domain',
+ select_key => 'svcnum',
+ select_label => 'domain',
+ disable_inventory => 1,
+
+ },
+ 'usergroup' => {
+ label => 'RADIUS groups',
+ type => 'radius_usergroup_selector',
+ disable_inventory => 1,
+ disable_select => 1,
+ },
+ 'seconds' => { label => 'Seconds',
+ label_sort => 'with Time Remaining',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ },
+ 'upbytes' => { label => 'Upload',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'downbytes' => { label => 'Download',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'totalbytes'=> { label => 'Total up and download',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'seconds_threshold' => { label => 'Seconds threshold',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ },
+ 'upbytes_threshold' => { label => 'Upload threshold',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'downbytes_threshold' => { label => 'Download threshold',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'totalbytes_threshold'=> { label => 'Total up and download threshold',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ 'format' => \&FS::UI::bytecount::display_bytecount,
+ 'parse' => \&FS::UI::bytecount::parse_bytecount,
+ },
+ 'last_login'=> {
+ label => 'Last login',
+ type => 'disabled',
+ },
+ 'last_logout'=> {
+ label => 'Last logout',
+ type => 'disabled',
+ },
+ },
+ };
+}
+
+sub table { 'svc_acct'; }
+
+sub table_dupcheck_fields { ( 'username', 'domsvc' ); }
+
+sub _fieldhandlers {
+ {
+ #false laziness with edit/svc_acct.cgi
+ 'usergroup' => sub {
+ my( $self, $groups ) = @_;
+ if ( ref($groups) eq 'ARRAY' ) {
+ $groups;
+ } elsif ( length($groups) ) {
+ [ split(/\s*,\s*/, $groups) ];
+ } else {
+ [];
+ }
+ },
+ };
+}
+
+sub last_login {
+ shift->_lastlog('in', @_);
+}
+
+sub last_logout {
+ shift->_lastlog('out', @_);
+}
+
+sub _lastlog {
+ my( $self, $op, $time ) = @_;
+
+ if ( defined($time) ) {
+ warn "$me last_log$op called on svcnum ". $self->svcnum.
+ ' ('. $self->email. "): $time\n"
+ if $DEBUG;
+
+ my $dbh = dbh;
+
+ my $sql = "UPDATE svc_acct SET last_log$op = ? WHERE svcnum = ?";
+ warn "$me $sql\n"
+ if $DEBUG;
+
+ my $sth = $dbh->prepare( $sql )
+ or die "Error preparing $sql: ". $dbh->errstr;
+ my $rv = $sth->execute($time, $self->svcnum);
+ die "Error executing $sql: ". $sth->errstr
+ unless defined($rv);
+ die "Can't update last_log$op for svcnum". $self->svcnum
+ if $rv == 0;
+
+ $self->{'Hash'}->{"last_log$op"} = $time;
+ }else{
+ $self->getfield("last_log$op");
+ }
+}
+
+=item search_sql STRING
+
+Class method which returns an SQL fragment to search for the given string.
+
+=cut
+
+sub search_sql {
+ my( $class, $string ) = @_;
+ if ( $string =~ /^([^@]+)@([^@]+)$/ ) {
+ my( $username, $domain ) = ( $1, $2 );
+ my $q_username = dbh->quote($username);
+ my @svc_domain = qsearch('svc_domain', { 'domain' => $domain } );
+ if ( @svc_domain ) {
+ "svc_acct.username = $q_username AND ( ".
+ join( ' OR ', map { "svc_acct.domsvc = ". $_->svcnum; } @svc_domain ).
+ " )";
+ } else {
+ '1 = 0'; #false
+ }
+ } elsif ( $string =~ /^(\d{1,3}\.){3}\d{1,3}$/ ) {
+ ' ( '.
+ $class->search_sql_field('slipip', $string ).
+ ' OR '.
+ $class->search_sql_field('username', $string ).
+ ' ) ';
+ } else {
+ $class->search_sql_field('username', $string);
+ }
+}
+
+=item label [ END_TIMESTAMP [ START_TIMESTAMP ] ]
+
+Returns the "username@domain" string for this account.
+
+END_TIMESTAMP and START_TIMESTAMP can optionally be passed when dealing with
+history records.
+
+=cut
+
+sub label {
+ my $self = shift;
+ $self->email(@_);
+}
+
+=cut
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this account to the database. If there is an error, returns the error,
+otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+The additional field I<usergroup> can optionally be defined; if so it should
+contain an arrayref of group names. See L<FS::radius_usergroup>.
+
+The additional field I<child_objects> can optionally be defined; if so it
+should contain an arrayref of FS::tablename objects. They will have their
+svcnum fields set and will be inserted after this record, but before any
+exports are run. Each element of the array can also optionally be a
+two-element array reference containing the child object and the name of an
+alternate field to be filled in with the newly-inserted svcnum, for example
+C<[ $svc_forward, 'srcsvc' ]>
+
+Currently available options are: I<depend_jobnum>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+(TODOC: L<FS::queue> and L<freeside-queued>)
+
+(TODOC: new exports!)
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my %options = @_;
+
+ if ( $DEBUG ) {
+ warn "[$me] insert called on $self: ". Dumper($self).
+ "\nwith options: ". Dumper(%options);
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->check;
+ return $error if $error;
+
+ if ( $self->svcnum && qsearchs('cust_svc',{'svcnum'=>$self->svcnum}) ) {
+ my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
+ unless ( $cust_svc ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "no cust_svc record found for svcnum ". $self->svcnum;
+ }
+ $self->pkgnum($cust_svc->pkgnum);
+ $self->svcpart($cust_svc->svcpart);
+ }
+
+ my @jobnums;
+ $error = $self->SUPER::insert(
+ 'jobnums' => \@jobnums,
+ 'child_objects' => $self->child_objects,
+ %options,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $self->usergroup ) {
+ foreach my $groupname ( @{$self->usergroup} ) {
+ my $radius_usergroup = new FS::radius_usergroup ( {
+ svcnum => $self->svcnum,
+ groupname => $groupname,
+ } );
+ my $error = $radius_usergroup->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ unless ( $skip_fuzzyfiles ) {
+ $error = $self->queue_fuzzyfiles_update;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "updating fuzzy search cache: $error";
+ }
+ }
+
+ my $cust_pkg = $self->cust_svc->cust_pkg;
+
+ if ( $cust_pkg ) {
+ my $cust_main = $cust_pkg->cust_main;
+ my $agentnum = $cust_main->agentnum;
+
+ if ( $conf->exists('emailinvoiceautoalways')
+ || $conf->exists('emailinvoiceauto')
+ && ! $cust_main->invoicing_list_emailonly
+ ) {
+ my @invoicing_list = $cust_main->invoicing_list;
+ push @invoicing_list, $self->email;
+ $cust_main->invoicing_list(\@invoicing_list);
+ }
+
+ #welcome email
+ my ($to,$welcome_template,$welcome_from,$welcome_subject,$welcome_subject_template,$welcome_mimetype)
+ = ('','','','','','');
+
+ if ( $conf->exists('welcome_email', $agentnum) ) {
+ $welcome_template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", $conf->config('welcome_email', $agentnum) ]
+ ) or warn "can't create welcome email template: $Text::Template::ERROR";
+ $welcome_from = $conf->config('welcome_email-from', $agentnum);
+ # || 'your-isp-is-dum'
+ $welcome_subject = $conf->config('welcome_email-subject', $agentnum)
+ || 'Welcome';
+ $welcome_subject_template = new Text::Template (
+ TYPE => 'STRING',
+ SOURCE => $welcome_subject,
+ ) or warn "can't create welcome email subject template: $Text::Template::ERROR";
+ $welcome_mimetype = $conf->config('welcome_email-mimetype', $agentnum)
+ || 'text/plain';
+ }
+ if ( $welcome_template && $cust_pkg ) {
+ my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
+ if ( $to ) {
+
+ my %hash = (
+ 'custnum' => $self->custnum,
+ 'username' => $self->username,
+ 'password' => $self->_password,
+ 'first' => $cust_main->first,
+ 'last' => $cust_main->getfield('last'),
+ 'pkg' => $cust_pkg->part_pkg->pkg,
+ );
+ my $wqueue = new FS::queue {
+ 'svcnum' => $self->svcnum,
+ 'job' => 'FS::svc_acct::send_email'
+ };
+ my $error = $wqueue->insert(
+ 'to' => $to,
+ 'from' => $welcome_from,
+ 'subject' => $welcome_subject_template->fill_in( HASH => \%hash, ),
+ 'mimetype' => $welcome_mimetype,
+ 'body' => $welcome_template->fill_in( HASH => \%hash, ),
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error queuing welcome email: $error";
+ }
+
+ if ( $options{'depend_jobnum'} ) {
+ warn "$me depend_jobnum found; adding to welcome email dependancies"
+ if $DEBUG;
+ if ( ref($options{'depend_jobnum'}) ) {
+ warn "$me adding jobs ". join(', ', @{$options{'depend_jobnum'}} ).
+ "to welcome email dependancies"
+ if $DEBUG;
+ push @jobnums, @{ $options{'depend_jobnum'} };
+ } else {
+ warn "$me adding job $options{'depend_jobnum'} ".
+ "to welcome email dependancies"
+ if $DEBUG;
+ push @jobnums, $options{'depend_jobnum'};
+ }
+ }
+
+ foreach my $jobnum ( @jobnums ) {
+ my $error = $wqueue->depend_insert($jobnum);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error queuing welcome email job dependancy: $error";
+ }
+ }
+
+ }
+
+ }
+
+ } # if ( $cust_pkg )
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+}
+
+=item delete
+
+Deletes this account from the database. If there is an error, returns the
+error, otherwise returns false.
+
+The corresponding FS::cust_svc record will be deleted as well.
+
+(TODOC: new exports!)
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "can't delete system account" if $self->_check_system;
+
+ return "Can't delete an account which is a (svc_forward) source!"
+ if qsearch( 'svc_forward', { 'srcsvc' => $self->svcnum } );
+
+ return "Can't delete an account which is a (svc_forward) destination!"
+ if qsearch( 'svc_forward', { 'dstsvc' => $self->svcnum } );
+
+ return "Can't delete an account with (svc_www) web service!"
+ if qsearch( 'svc_www', { 'usersvc' => $self->svcnum } );
+
+ # what about records in session ? (they should refer to history table)
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $cust_main_invoice (
+ qsearch( 'cust_main_invoice', { 'dest' => $self->svcnum } )
+ ) {
+ unless ( defined($cust_main_invoice) ) {
+ warn "WARNING: something's wrong with qsearch";
+ next;
+ }
+ my %hash = $cust_main_invoice->hash;
+ $hash{'dest'} = $self->email;
+ my $new = new FS::cust_main_invoice \%hash;
+ my $error = $new->replace($cust_main_invoice);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ foreach my $svc_domain (
+ qsearch( 'svc_domain', { 'catchall' => $self->svcnum } )
+ ) {
+ my %hash = new FS::svc_domain->hash;
+ $hash{'catchall'} = '';
+ my $new = new FS::svc_domain \%hash;
+ my $error = $new->replace($svc_domain);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ foreach my $radius_usergroup (
+ qsearch('radius_usergroup', { 'svcnum' => $self->svcnum } )
+ ) {
+ my $error = $radius_usergroup->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+The additional field I<usergroup> can optionally be defined; if so it should
+contain an arrayref of group names. See L<FS::radius_usergroup>.
+
+
+=cut
+
+sub replace {
+ my $new = shift;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $new->replace_old;
+
+ warn "$me replacing $old with $new\n" if $DEBUG;
+
+ my $error;
+
+ return "can't modify system account" if $old->_check_system;
+
+ {
+ #no warnings 'numeric'; #alas, a 5.006-ism
+ local($^W) = 0;
+
+ foreach my $xid (qw( uid gid )) {
+
+ return "Can't change $xid!"
+ if ! $conf->exists("svc_acct-edit_$xid")
+ && $old->$xid() != $new->$xid()
+ && $new->cust_svc->part_svc->part_svc_column($xid)->columnflag ne 'F'
+ }
+
+ }
+
+ #change homdir when we change username
+ $new->setfield('dir', '') if $old->username ne $new->username;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ # redundant, but so $new->usergroup gets set
+ $error = $new->check;
+ return $error if $error;
+
+ $old->usergroup( [ $old->radius_groups ] );
+ if ( $DEBUG ) {
+ warn $old->email. " old groups: ". join(' ',@{$old->usergroup}). "\n";
+ warn $new->email. "new groups: ". join(' ',@{$new->usergroup}). "\n";
+ }
+ if ( $new->usergroup ) {
+ #(sorta) false laziness with FS::part_export::sqlradius::_export_replace
+ my @newgroups = @{$new->usergroup};
+ foreach my $oldgroup ( @{$old->usergroup} ) {
+ if ( grep { $oldgroup eq $_ } @newgroups ) {
+ @newgroups = grep { $oldgroup ne $_ } @newgroups;
+ next;
+ }
+ my $radius_usergroup = qsearchs('radius_usergroup', {
+ svcnum => $old->svcnum,
+ groupname => $oldgroup,
+ } );
+ my $error = $radius_usergroup->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error deleting radius_usergroup $oldgroup: $error";
+ }
+ }
+
+ foreach my $newgroup ( @newgroups ) {
+ my $radius_usergroup = new FS::radius_usergroup ( {
+ svcnum => $new->svcnum,
+ groupname => $newgroup,
+ } );
+ my $error = $radius_usergroup->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error adding radius_usergroup $newgroup: $error";
+ }
+ }
+
+ }
+
+ $error = $new->SUPER::replace($old, @_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error if $error;
+ }
+
+ if ( $new->username ne $old->username && ! $skip_fuzzyfiles ) {
+ $error = $new->queue_fuzzyfiles_update;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "updating fuzzy search cache: $error";
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+}
+
+=item queue_fuzzyfiles_update
+
+Used by insert & replace to update the fuzzy search cache
+
+=cut
+
+sub queue_fuzzyfiles_update {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $queue = new FS::queue {
+ 'svcnum' => $self->svcnum,
+ 'job' => 'FS::svc_acct::append_fuzzyfiles'
+ };
+ my $error = $queue->insert($self->username);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "queueing job (transaction rolled back): $error";
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+
+=item suspend
+
+Suspends this account by calling export-specific suspend hooks. If there is
+an error, returns the error, otherwise returns false.
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=cut
+
+sub suspend {
+ my $self = shift;
+ return "can't suspend system account" if $self->_check_system;
+ $self->SUPER::suspend(@_);
+}
+
+=item unsuspend
+
+Unsuspends this account by by calling export-specific suspend hooks. If there
+is an error, returns the error, otherwise returns false.
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=cut
+
+sub unsuspend {
+ my $self = shift;
+ my %hash = $self->hash;
+ if ( $hash{_password} =~ /^\*SUSPENDED\* (.*)$/ ) {
+ $hash{_password} = $1;
+ my $new = new FS::svc_acct ( \%hash );
+ my $error = $new->replace($self);
+ return $error if $error;
+ }
+
+ $self->SUPER::unsuspend(@_);
+}
+
+=item cancel
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+If the B<auto_unset_catchall> configuration option is set, this method will
+automatically remove any references to the canceled service in the catchall
+field of svc_domain. This allows packages that contain both a svc_domain and
+its catchall svc_acct to be canceled in one step.
+
+=cut
+
+sub cancel {
+ # Only one thing to do at this level
+ my $self = shift;
+ foreach my $svc_domain (
+ qsearch( 'svc_domain', { catchall => $self->svcnum } ) ) {
+ if($conf->exists('auto_unset_catchall')) {
+ my %hash = $svc_domain->hash;
+ $hash{catchall} = '';
+ my $new = new FS::svc_domain ( \%hash );
+ my $error = $new->replace($svc_domain);
+ return $error if $error;
+ } else {
+ return "cannot unprovision svc_acct #".$self->svcnum.
+ " while assigned as catchall for svc_domain #".$svc_domain->svcnum;
+ }
+ }
+
+ $self->SUPER::cancel(@_);
+}
+
+
+=item check
+
+Checks all fields to make sure this is a valid service. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+Sets any fixed values; see L<FS::part_svc>.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my($recref) = $self->hashref;
+
+ my $x = $self->setfixed( $self->_fieldhandlers );
+ return $x unless ref($x);
+ my $part_svc = $x;
+
+ if ( $part_svc->part_svc_column('usergroup')->columnflag eq "F" ) {
+ $self->usergroup(
+ [ split(',', $part_svc->part_svc_column('usergroup')->columnvalue) ] );
+ }
+
+ my $error = $self->ut_numbern('svcnum')
+ #|| $self->ut_number('domsvc')
+ || $self->ut_foreign_key('domsvc', 'svc_domain', 'svcnum' )
+ || $self->ut_textn('sec_phrase')
+ || $self->ut_snumbern('seconds')
+ || $self->ut_snumbern('upbytes')
+ || $self->ut_snumbern('downbytes')
+ || $self->ut_snumbern('totalbytes')
+ || $self->ut_enum( '_password_encoding',
+ [ '', qw( plain crypt ldap ) ]
+ )
+ ;
+ return $error if $error;
+
+ my $ulen = $usernamemax || $self->dbdef_table->column('username')->length;
+ if ( $username_uppercase ) {
+ $recref->{username} =~ /^([a-z0-9_\-\.\&\%]{$usernamemin,$ulen})$/i
+ or return gettext('illegal_username'). " ($usernamemin-$ulen): ". $recref->{username};
+ $recref->{username} = $1;
+ } else {
+ $recref->{username} =~ /^([a-z0-9_\-\.\&\%]{$usernamemin,$ulen})$/
+ or return gettext('illegal_username'). " ($usernamemin-$ulen): ". $recref->{username};
+ $recref->{username} = $1;
+ }
+
+ if ( $username_letterfirst ) {
+ $recref->{username} =~ /^[a-z]/ or return gettext('illegal_username');
+ } elsif ( $username_letter ) {
+ $recref->{username} =~ /[a-z]/ or return gettext('illegal_username');
+ }
+ if ( $username_noperiod ) {
+ $recref->{username} =~ /\./ and return gettext('illegal_username');
+ }
+ if ( $username_nounderscore ) {
+ $recref->{username} =~ /_/ and return gettext('illegal_username');
+ }
+ if ( $username_nodash ) {
+ $recref->{username} =~ /\-/ and return gettext('illegal_username');
+ }
+ unless ( $username_ampersand ) {
+ $recref->{username} =~ /\&/ and return gettext('illegal_username');
+ }
+ unless ( $username_percent ) {
+ $recref->{username} =~ /\%/ and return gettext('illegal_username');
+ }
+
+ $recref->{popnum} =~ /^(\d*)$/ or return "Illegal popnum: ".$recref->{popnum};
+ $recref->{popnum} = $1;
+ return "Unknown popnum" unless
+ ! $recref->{popnum} ||
+ qsearchs('svc_acct_pop',{'popnum'=> $recref->{popnum} } );
+
+ unless ( $part_svc->part_svc_column('uid')->columnflag eq 'F' ) {
+
+ $recref->{uid} =~ /^(\d*)$/ or return "Illegal uid";
+ $recref->{uid} = $1 eq '' ? $self->unique('uid') : $1;
+
+ $recref->{gid} =~ /^(\d*)$/ or return "Illegal gid";
+ $recref->{gid} = $1 eq '' ? $recref->{uid} : $1;
+ #not all systems use gid=uid
+ #you can set a fixed gid in part_svc
+
+ return "Only root can have uid 0"
+ if $recref->{uid} == 0
+ && $recref->{username} !~ /^(root|toor|smtp)$/;
+
+ unless ( $recref->{username} eq 'sync' ) {
+ if ( grep $_ eq $recref->{shell}, @shells ) {
+ $recref->{shell} = (grep $_ eq $recref->{shell}, @shells)[0];
+ } else {
+ return "Illegal shell \`". $self->shell. "\'; ".
+ "shells configuration value contains: @shells";
+ }
+ } else {
+ $recref->{shell} = '/bin/sync';
+ }
+
+ } else {
+ $recref->{gid} ne '' ?
+ return "Can't have gid without uid" : ( $recref->{gid}='' );
+ #$recref->{dir} ne '' ?
+ # return "Can't have directory without uid" : ( $recref->{dir}='' );
+ $recref->{shell} ne '' ?
+ return "Can't have shell without uid" : ( $recref->{shell}='' );
+ }
+
+ unless ( $part_svc->part_svc_column('dir')->columnflag eq 'F' ) {
+
+ $recref->{dir} =~ /^([\/\w\-\.\&]*)$/
+ or return "Illegal directory: ". $recref->{dir};
+ $recref->{dir} = $1;
+ return "Illegal directory"
+ if $recref->{dir} =~ /(^|\/)\.+(\/|$)/; #no .. component
+ return "Illegal directory"
+ if $recref->{dir} =~ /\&/ && ! $username_ampersand;
+ unless ( $recref->{dir} ) {
+ $recref->{dir} = $dir_prefix . '/';
+ if ( $dirhash > 0 ) {
+ for my $h ( 1 .. $dirhash ) {
+ $recref->{dir} .= substr($recref->{username}, $h-1, 1). '/';
+ }
+ } elsif ( $dirhash < 0 ) {
+ for my $h ( reverse $dirhash .. -1 ) {
+ $recref->{dir} .= substr($recref->{username}, $h, 1). '/';
+ }
+ }
+ $recref->{dir} .= $recref->{username};
+ ;
+ }
+
+ }
+
+ # $error = $self->ut_textn('finger');
+ # return $error if $error;
+ if ( $self->getfield('finger') eq '' ) {
+ my $cust_pkg = $self->svcnum
+ ? $self->cust_svc->cust_pkg
+ : qsearchs('cust_pkg', { 'pkgnum' => $self->getfield('pkgnum') } );
+ if ( $cust_pkg ) {
+ my $cust_main = $cust_pkg->cust_main;
+ $self->setfield('finger', $cust_main->first.' '.$cust_main->get('last') );
+ }
+ }
+ $self->getfield('finger') =~
+ /^([\w \t\!\@\#\$\%\&\(\)\-\+\;\'\"\,\.\?\/\*\<\>]*)$/
+ or return "Illegal finger: ". $self->getfield('finger');
+ $self->setfield('finger', $1);
+
+ $recref->{quota} =~ /^(\w*)$/ or return "Illegal quota";
+ $recref->{quota} = $1;
+
+ unless ( $part_svc->part_svc_column('slipip')->columnflag eq 'F' ) {
+ if ( $recref->{slipip} eq '' ) {
+ $recref->{slipip} = '';
+ } elsif ( $recref->{slipip} eq '0e0' ) {
+ $recref->{slipip} = '0e0';
+ } else {
+ $recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/
+ or return "Illegal slipip: ". $self->slipip;
+ $recref->{slipip} = $1;
+ }
+
+ }
+
+ #arbitrary RADIUS stuff; allow ut_textn for now
+ foreach ( grep /^radius_/, fields('svc_acct') ) {
+ $self->ut_textn($_);
+ }
+
+ if ( $recref->{_password_encoding} eq 'ldap' ) {
+
+ if ( $recref->{_password} =~ /^(\{[\w\-]+\})(!?.{0,64})$/ ) {
+ $recref->{_password} = uc($1).$2;
+ } else {
+ return 'Illegal (ldap-encoded) password: '. $recref->{_password};
+ }
+
+ } elsif ( $recref->{_password_encoding} eq 'crypt' ) {
+
+ if ( $recref->{_password} =~
+ #/^(\$\w+\$.*|[\w\+\/]{13}|_[\w\+\/]{19}|\*)$/
+ /^(!!?)?(\$\w+\$.*|[\w\+\/\.]{13}|_[\w\+\/\.]{19}|\*)$/
+ ) {
+
+ $recref->{_password} = ( defined($1) ? $1 : '' ). $2;
+
+ } else {
+ return 'Illegal (crypt-encoded) password: '. $recref->{_password};
+ }
+
+ } elsif ( $recref->{_password_encoding} eq 'plain' ) {
+
+ #generate a password if it is blank
+ $recref->{_password} = join('',map($pw_set[ int(rand $#pw_set) ], (0..7) ) )
+ unless length( $recref->{_password} );
+
+ if ( $recref->{_password} =~ /^([^\t\n]{$passwordmin,$passwordmax})$/ ) {
+ $recref->{_password} = $1;
+ } else {
+ return gettext('illegal_password'). " $passwordmin-$passwordmax ".
+ FS::Msgcat::_gettext('illegal_password_characters').
+ ": ". $recref->{_password};
+ }
+
+ if ( $password_noampersand ) {
+ $recref->{_password} =~ /\&/ and return gettext('illegal_password');
+ }
+ if ( $password_noexclamation ) {
+ $recref->{_password} =~ /\!/ and return gettext('illegal_password');
+ }
+
+ } else {
+
+ #carp "warning: _password_encoding unspecified\n";
+
+ #generate a password if it is blank
+ unless ( length( $recref->{_password} ) ) {
+
+ $recref->{_password} =
+ join('',map($pw_set[ int(rand $#pw_set) ], (0..7) ) );
+ $recref->{_password_encoding} = 'plain';
+
+ } else {
+
+ #if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{4,16})$/ ) {
+ if ( $recref->{_password} =~ /^((\*SUSPENDED\* |!!?)?)([^\t\n]{$passwordmin,$passwordmax})$/ ) {
+ $recref->{_password} = $1.$3;
+ $recref->{_password_encoding} = 'plain';
+ } elsif ( $recref->{_password} =~
+ /^((\*SUSPENDED\* |!!?)?)([\w\.\/\$\;\+]{13,64})$/
+ ) {
+ $recref->{_password} = $1.$3;
+ $recref->{_password_encoding} = 'crypt';
+ } elsif ( $recref->{_password} eq '*' ) {
+ $recref->{_password} = '*';
+ $recref->{_password_encoding} = 'crypt';
+ } elsif ( $recref->{_password} eq '!' ) {
+ $recref->{_password_encoding} = 'crypt';
+ $recref->{_password} = '!';
+ } elsif ( $recref->{_password} eq '!!' ) {
+ $recref->{_password} = '!!';
+ $recref->{_password_encoding} = 'crypt';
+ } else {
+ #return "Illegal password";
+ return gettext('illegal_password'). " $passwordmin-$passwordmax ".
+ FS::Msgcat::_gettext('illegal_password_characters').
+ ": ". $recref->{_password};
+ }
+
+ }
+
+ }
+
+ $self->SUPER::check;
+
+}
+
+=item _check_system
+
+Internal function to check the username against the list of system usernames
+from the I<system_usernames> configuration value. Returns true if the username
+is listed on the system username list.
+
+=cut
+
+sub _check_system {
+ my $self = shift;
+ scalar( grep { $self->username eq $_ || $self->email eq $_ }
+ $conf->config('system_usernames')
+ );
+}
+
+=item _check_duplicate
+
+Internal method to check for duplicates usernames, username@domain pairs and
+uids.
+
+If the I<global_unique-username> configuration value is set to B<username> or
+B<username@domain>, enforces global username or username@domain uniqueness.
+
+In all cases, check for duplicate uids and usernames or username@domain pairs
+per export and with identical I<svcpart> values.
+
+=cut
+
+sub _check_duplicate {
+ my $self = shift;
+
+ my $global_unique = $conf->config('global_unique-username') || 'none';
+ return '' if $global_unique eq 'disabled';
+
+ $self->lock_table;
+
+ my $part_svc = qsearchs('part_svc', { 'svcpart' => $self->svcpart } );
+ unless ( $part_svc ) {
+ return 'unknown svcpart '. $self->svcpart;
+ }
+
+ my @dup_user = grep { !$self->svcnum || $_->svcnum != $self->svcnum }
+ qsearch( 'svc_acct', { 'username' => $self->username } );
+ return gettext('username_in_use')
+ if $global_unique eq 'username' && @dup_user;
+
+ my @dup_userdomain = grep { !$self->svcnum || $_->svcnum != $self->svcnum }
+ qsearch( 'svc_acct', { 'username' => $self->username,
+ 'domsvc' => $self->domsvc } );
+ return gettext('username_in_use')
+ if $global_unique eq 'username@domain' && @dup_userdomain;
+
+ my @dup_uid;
+ if ( $part_svc->part_svc_column('uid')->columnflag ne 'F'
+ && $self->username !~ /^(toor|(hyla)?fax)$/ ) {
+ @dup_uid = grep { !$self->svcnum || $_->svcnum != $self->svcnum }
+ qsearch( 'svc_acct', { 'uid' => $self->uid } );
+ } else {
+ @dup_uid = ();
+ }
+
+ if ( @dup_user || @dup_userdomain || @dup_uid ) {
+ my $exports = FS::part_export::export_info('svc_acct');
+ my %conflict_user_svcpart;
+ my %conflict_userdomain_svcpart = ( $self->svcpart => 'SELF', );
+
+ foreach my $part_export ( $part_svc->part_export ) {
+
+ #this will catch to the same exact export
+ my @svcparts = map { $_->svcpart } $part_export->export_svc;
+
+ #this will catch to exports w/same exporthost+type ???
+ #my @other_part_export = qsearch('part_export', {
+ # 'machine' => $part_export->machine,
+ # 'exporttype' => $part_export->exporttype,
+ #} );
+ #foreach my $other_part_export ( @other_part_export ) {
+ # push @svcparts, map { $_->svcpart }
+ # qsearch('export_svc', { 'exportnum' => $part_export->exportnum });
+ #}
+
+ #my $nodomain = $exports->{$part_export->exporttype}{'nodomain'};
+ #silly kludge to avoid uninitialized value errors
+ my $nodomain = exists( $exports->{$part_export->exporttype}{'nodomain'} )
+ ? $exports->{$part_export->exporttype}{'nodomain'}
+ : '';
+ if ( $nodomain =~ /^Y/i ) {
+ $conflict_user_svcpart{$_} = $part_export->exportnum
+ foreach @svcparts;
+ } else {
+ $conflict_userdomain_svcpart{$_} = $part_export->exportnum
+ foreach @svcparts;
+ }
+ }
+
+ foreach my $dup_user ( @dup_user ) {
+ my $dup_svcpart = $dup_user->cust_svc->svcpart;
+ if ( exists($conflict_user_svcpart{$dup_svcpart}) ) {
+ return "duplicate username ". $self->username.
+ ": conflicts with svcnum ". $dup_user->svcnum.
+ " via exportnum ". $conflict_user_svcpart{$dup_svcpart};
+ }
+ }
+
+ foreach my $dup_userdomain ( @dup_userdomain ) {
+ my $dup_svcpart = $dup_userdomain->cust_svc->svcpart;
+ if ( exists($conflict_userdomain_svcpart{$dup_svcpart}) ) {
+ return "duplicate username\@domain ". $self->email.
+ ": conflicts with svcnum ". $dup_userdomain->svcnum.
+ " via exportnum ". $conflict_userdomain_svcpart{$dup_svcpart};
+ }
+ }
+
+ foreach my $dup_uid ( @dup_uid ) {
+ my $dup_svcpart = $dup_uid->cust_svc->svcpart;
+ if ( exists($conflict_user_svcpart{$dup_svcpart})
+ || exists($conflict_userdomain_svcpart{$dup_svcpart}) ) {
+ return "duplicate uid ". $self->uid.
+ ": conflicts with svcnum ". $dup_uid->svcnum.
+ " via exportnum ".
+ ( $conflict_user_svcpart{$dup_svcpart}
+ || $conflict_userdomain_svcpart{$dup_svcpart} );
+ }
+ }
+
+ }
+
+ return '';
+
+}
+
+=item radius
+
+Depriciated, use radius_reply instead.
+
+=cut
+
+sub radius {
+ carp "FS::svc_acct::radius depriciated, use radius_reply";
+ $_[0]->radius_reply;
+}
+
+=item radius_reply
+
+Returns key/value pairs, suitable for assigning to a hash, for any RADIUS
+reply attributes of this record.
+
+Note that this is now the preferred method for reading RADIUS attributes -
+accessing the columns directly is discouraged, as the column names are
+expected to change in the future.
+
+=cut
+
+sub radius_reply {
+ my $self = shift;
+
+ return %{ $self->{'radius_reply'} }
+ if exists $self->{'radius_reply'};
+
+ my %reply =
+ map {
+ /^(radius_(.*))$/;
+ my($column, $attrib) = ($1, $2);
+ #$attrib =~ s/_/\-/g;
+ ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) );
+ } grep { /^radius_/ && $self->getfield($_) } fields( $self->table );
+
+ if ( $self->slipip && $self->slipip ne '0e0' ) {
+ $reply{$radius_ip} = $self->slipip;
+ }
+
+ if ( $self->seconds !~ /^$/ ) {
+ $reply{'Session-Timeout'} = $self->seconds;
+ }
+
+ %reply;
+}
+
+=item radius_check
+
+Returns key/value pairs, suitable for assigning to a hash, for any RADIUS
+check attributes of this record.
+
+Note that this is now the preferred method for reading RADIUS attributes -
+accessing the columns directly is discouraged, as the column names are
+expected to change in the future.
+
+=cut
+
+sub radius_check {
+ my $self = shift;
+
+ return %{ $self->{'radius_check'} }
+ if exists $self->{'radius_check'};
+
+ my %check =
+ map {
+ /^(rc_(.*))$/;
+ my($column, $attrib) = ($1, $2);
+ #$attrib =~ s/_/\-/g;
+ ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) );
+ } grep { /^rc_/ && $self->getfield($_) } fields( $self->table );
+
+
+ my($pw_attrib, $password) = $self->radius_password;
+ $check{$pw_attrib} = $password;
+
+ my $cust_svc = $self->cust_svc;
+ die "FATAL: no cust_svc record for svc_acct.svcnum ". $self->svcnum. "\n"
+ unless $cust_svc;
+ my $cust_pkg = $cust_svc->cust_pkg;
+ if ( $cust_pkg && $cust_pkg->part_pkg->is_prepaid && $cust_pkg->bill ) {
+ $check{'Expiration'} = time2str('%B %e %Y %T', $cust_pkg->bill ); #http://lists.cistron.nl/pipermail/freeradius-users/2005-January/040184.html
+ }
+
+ %check;
+
+}
+
+=item radius_password
+
+Returns a key/value pair containing the RADIUS attribute name and value
+for the password.
+
+=cut
+
+sub radius_password {
+ my $self = shift;
+
+ my($pw_attrib, $password);
+ if ( $self->_password_encoding eq 'ldap' ) {
+
+ $pw_attrib = 'Password-With-Header';
+ $password = $self->_password;
+
+ } elsif ( $self->_password_encoding eq 'crypt' ) {
+
+ $pw_attrib = 'Crypt-Password';
+ $password = $self->_password;
+
+ } elsif ( $self->_password_encoding eq 'plain' ) {
+
+ $pw_attrib = $radius_password; #Cleartext-Password? man rlm_pap
+ $password = $self->_password;
+
+ } else {
+
+ $pw_attrib = length($password) <= 12 ? $radius_password : 'Crypt-Password';
+ $password = $self->_password;
+
+ }
+
+ ($pw_attrib, $password);
+
+}
+
+=item snapshot
+
+This method instructs the object to "snapshot" or freeze RADIUS check and
+reply attributes to the current values.
+
+=cut
+
+#bah, my english is too broken this morning
+#Of note is the "Expiration" attribute, which, for accounts in prepaid packages, is typically defined on-the-fly as the associated packages cust_pkg.bill. (This is used by
+#the FS::cust_pkg's replace method to trigger the correct export updates when
+#package dates change)
+
+sub snapshot {
+ my $self = shift;
+
+ $self->{$_} = { $self->$_() }
+ foreach qw( radius_reply radius_check );
+
+}
+
+=item forget_snapshot
+
+This methos instructs the object to forget any previously snapshotted
+RADIUS check and reply attributes.
+
+=cut
+
+sub forget_snapshot {
+ my $self = shift;
+
+ delete $self->{$_}
+ foreach qw( radius_reply radius_check );
+
+}
+
+=item domain [ END_TIMESTAMP [ START_TIMESTAMP ] ]
+
+Returns the domain associated with this account.
+
+END_TIMESTAMP and START_TIMESTAMP can optionally be passed when dealing with
+history records.
+
+=cut
+
+sub domain {
+ my $self = shift;
+ die "svc_acct.domsvc is null for svcnum ". $self->svcnum unless $self->domsvc;
+ my $svc_domain = $self->svc_domain(@_)
+ or die "no svc_domain.svcnum for svc_acct.domsvc ". $self->domsvc;
+ $svc_domain->domain;
+}
+
+=item svc_domain
+
+Returns the FS::svc_domain record for this account's domain (see
+L<FS::svc_domain>).
+
+=cut
+
+# FS::h_svc_acct has a history-aware svc_domain override
+
+sub svc_domain {
+ my $self = shift;
+ $self->{'_domsvc'}
+ ? $self->{'_domsvc'}
+ : qsearchs( 'svc_domain', { 'svcnum' => $self->domsvc } );
+}
+
+=item cust_svc
+
+Returns the FS::cust_svc record for this account (see L<FS::cust_svc>).
+
+=cut
+
+#inherited from svc_Common
+
+=item email [ END_TIMESTAMP [ START_TIMESTAMP ] ]
+
+Returns an email address associated with the account.
+
+END_TIMESTAMP and START_TIMESTAMP can optionally be passed when dealing with
+history records.
+
+=cut
+
+sub email {
+ my $self = shift;
+ $self->username. '@'. $self->domain(@_);
+}
+
+=item acct_snarf
+
+Returns an array of FS::acct_snarf records associated with the account.
+If the acct_snarf table does not exist or there are no associated records,
+an empty list is returned
+
+=cut
+
+sub acct_snarf {
+ my $self = shift;
+ return () unless dbdef->table('acct_snarf');
+ eval "use FS::acct_snarf;";
+ die $@ if $@;
+ qsearch('acct_snarf', { 'svcnum' => $self->svcnum } );
+}
+
+=item decrement_upbytes OCTETS
+
+Decrements the I<upbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub decrement_upbytes {
+ shift->_op_usage('-', 'upbytes', @_);
+}
+
+=item increment_upbytes OCTETS
+
+Increments the I<upbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_upbytes {
+ shift->_op_usage('+', 'upbytes', @_);
+}
+
+=item decrement_downbytes OCTETS
+
+Decrements the I<downbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub decrement_downbytes {
+ shift->_op_usage('-', 'downbytes', @_);
+}
+
+=item increment_downbytes OCTETS
+
+Increments the I<downbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_downbytes {
+ shift->_op_usage('+', 'downbytes', @_);
+}
+
+=item decrement_totalbytes OCTETS
+
+Decrements the I<totalbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub decrement_totalbytes {
+ shift->_op_usage('-', 'totalbytes', @_);
+}
+
+=item increment_totalbytes OCTETS
+
+Increments the I<totalbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_totalbytes {
+ shift->_op_usage('+', 'totalbytes', @_);
+}
+
+=item decrement_seconds SECONDS
+
+Decrements the I<seconds> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub decrement_seconds {
+ shift->_op_usage('-', 'seconds', @_);
+}
+
+=item increment_seconds SECONDS
+
+Increments the I<seconds> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_seconds {
+ shift->_op_usage('+', 'seconds', @_);
+}
+
+
+my %op2action = (
+ '-' => 'suspend',
+ '+' => 'unsuspend',
+);
+my %op2condition = (
+ '-' => sub { my($self, $column, $amount) = @_;
+ $self->$column - $amount <= 0;
+ },
+ '+' => sub { my($self, $column, $amount) = @_;
+ $self->$column + $amount > 0;
+ },
+);
+my %op2warncondition = (
+ '-' => sub { my($self, $column, $amount) = @_;
+ my $threshold = $column . '_threshold';
+ $self->$column - $amount <= $self->$threshold + 0;
+ },
+ '+' => sub { my($self, $column, $amount) = @_;
+ $self->$column + $amount > 0;
+ },
+);
+
+sub _op_usage {
+ my( $self, $op, $column, $amount ) = @_;
+
+ warn "$me _op_usage called for $column on svcnum ". $self->svcnum.
+ ' ('. $self->email. "): $op $amount\n"
+ if $DEBUG;
+
+ return '' unless $amount;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $sql = "UPDATE svc_acct SET $column = ".
+ " CASE WHEN $column IS NULL THEN 0 ELSE $column END ". #$column||0
+ " $op ? WHERE svcnum = ?";
+ warn "$me $sql\n"
+ if $DEBUG;
+
+ my $sth = $dbh->prepare( $sql )
+ or die "Error preparing $sql: ". $dbh->errstr;
+ my $rv = $sth->execute($amount, $self->svcnum);
+ die "Error executing $sql: ". $sth->errstr
+ unless defined($rv);
+ die "Can't update $column for svcnum". $self->svcnum
+ if $rv == 0;
+
+ my $action = $op2action{$op};
+
+ if ( &{$op2condition{$op}}($self, $column, $amount) &&
+ ( $action eq 'suspend' && !$self->overlimit
+ || $action eq 'unsuspend' && $self->overlimit )
+ ) {
+ foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+ if ($part_export->option('overlimit_groups')) {
+ my ($new,$old);
+ my $other = new FS::svc_acct $self->hashref;
+ my $groups = &{ $self->_fieldhandlers->{'usergroup'} }
+ ($self, $part_export->option('overlimit_groups'));
+ $other->usergroup( $groups );
+ if ($action eq 'suspend'){
+ $new = $other; $old = $self;
+ }else{
+ $new = $self; $old = $other;
+ }
+ my $error = $part_export->export_replace($new, $old);
+ $error ||= $self->overlimit($action);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error replacing radius groups in export, ${op}: $error";
+ }
+ }
+ }
+ }
+
+ if ( $conf->exists("svc_acct-usage_$action")
+ && &{$op2condition{$op}}($self, $column, $amount) ) {
+ #my $error = $self->$action();
+ my $error = $self->cust_svc->cust_pkg->$action();
+ # $error ||= $self->overlimit($action);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error ${action}ing: $error";
+ }
+ }
+
+ if ($warning_template && &{$op2warncondition{$op}}($self, $column, $amount)) {
+ my $wqueue = new FS::queue {
+ 'svcnum' => $self->svcnum,
+ 'job' => 'FS::svc_acct::reached_threshold',
+ };
+
+ my $to = '';
+ if ($op eq '-'){
+ $to = $warning_cc if &{$op2condition{$op}}($self, $column, $amount);
+ }
+
+ # x_threshold race
+ my $error = $wqueue->insert(
+ 'svcnum' => $self->svcnum,
+ 'op' => $op,
+ 'column' => $column,
+ 'to' => $to,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error queuing threshold activity: $error";
+ }
+ }
+
+ warn "$me update successful; committing\n"
+ if $DEBUG;
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+sub set_usage {
+ my( $self, $valueref ) = @_;
+
+ warn "$me set_usage called for svcnum ". $self->svcnum.
+ ' ('. $self->email. "): ".
+ join(', ', map { "$_ => " . $valueref->{$_}} keys %$valueref) . "\n"
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ local $FS::svc_Common::noexport_hack = 1;
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $reset = 0;
+ my %handyhash = ();
+ foreach my $field (keys %$valueref){
+ $reset = 1 if $valueref->{$field};
+ $self->setfield($field, $valueref->{$field});
+ $self->setfield( $field.'_threshold',
+ int($self->getfield($field)
+ * ( $conf->exists('svc_acct-usage_threshold')
+ ? 1 - $conf->config('svc_acct-usage_threshold')/100
+ : 0.20
+ )
+ )
+ );
+ $handyhash{$field} = $self->getfield($field);
+ $handyhash{$field.'_threshold'} = $self->getfield($field.'_threshold');
+ }
+ #my $error = $self->replace; #NO! we avoid the call to ->check for
+ #die $error if $error; #services not explicity changed via the UI
+
+ my $sql = "UPDATE svc_acct SET " .
+ join (',', map { "$_ = ?" } (keys %handyhash) ).
+ " WHERE svcnum = ?";
+
+ warn "$me $sql\n"
+ if $DEBUG;
+
+ if (scalar(keys %handyhash)) {
+ my $sth = $dbh->prepare( $sql )
+ or die "Error preparing $sql: ". $dbh->errstr;
+ my $rv = $sth->execute((values %handyhash), $self->svcnum);
+ die "Error executing $sql: ". $sth->errstr
+ unless defined($rv);
+ die "Can't update usage for svcnum ". $self->svcnum
+ if $rv == 0;
+ }
+
+ if ( $reset ) {
+ my $error;
+
+ if ($self->overlimit) {
+ $error = $self->overlimit('unsuspend');
+ foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+ if ($part_export->option('overlimit_groups')) {
+ my $old = new FS::svc_acct $self->hashref;
+ my $groups = &{ $self->_fieldhandlers->{'usergroup'} }
+ ($self, $part_export->option('overlimit_groups'));
+ $old->usergroup( $groups );
+ $error ||= $part_export->export_replace($self, $old);
+ }
+ }
+ }
+
+ if ( $conf->exists("svc_acct-usage_unsuspend")) {
+ $error ||= $self->cust_svc->cust_pkg->unsuspend;
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error unsuspending: $error";
+ }
+ }
+
+ warn "$me update successful; committing\n"
+ if $DEBUG;
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+
+=item recharge HASHREF
+
+ Increments usage columns by the amount specified in HASHREF as
+ column=>amount pairs.
+
+=cut
+
+sub recharge {
+ my ($self, $vhash) = @_;
+
+ if ( $DEBUG ) {
+ warn "[$me] recharge called on $self: ". Dumper($self).
+ "\nwith vhash: ". Dumper($vhash);
+ }
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+ my $error = '';
+
+ foreach my $column (keys %$vhash){
+ $error ||= $self->_op_usage('+', $column, $vhash->{$column});
+ }
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ }else{
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ }
+ return $error;
+}
+
+=item is_rechargeable
+
+Returns true if this svc_account can be "recharged" and false otherwise.
+
+=cut
+
+sub is_rechargable {
+ my $self = shift;
+ $self->seconds ne ''
+ || $self->upbytes ne ''
+ || $self->downbytes ne ''
+ || $self->totalbytes ne '';
+}
+
+=item seconds_since TIMESTAMP
+
+Returns the number of seconds this account has been online since TIMESTAMP,
+according to the session monitor (see L<FS::Session>).
+
+TIMESTAMP is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+#note: POD here, implementation in FS::cust_svc
+sub seconds_since {
+ my $self = shift;
+ $self->cust_svc->seconds_since(@_);
+}
+
+=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END
+
+Returns the numbers of seconds this account has been online between
+TIMESTAMP_START (inclusive) and TIMESTAMP_END (exclusive), according to an
+external SQL radacct table, specified via sqlradius export. Sessions which
+started in the specified range but are still open are counted from session
+start to the end of the range (unless they are over 1 day old, in which case
+they are presumed missing their stop record and not counted). Also, sessions
+which end in the range but started earlier are counted from the start of the
+range to session end. Finally, sessions which start before the range but end
+after are counted for the entire range.
+
+TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
+L<perlfunc/"time">. Also see L<Time::Local> and L<Date::Parse> for conversion
+functions.
+
+=cut
+
+#note: POD here, implementation in FS::cust_svc
+sub seconds_since_sqlradacct {
+ my $self = shift;
+ $self->cust_svc->seconds_since_sqlradacct(@_);
+}
+
+=item attribute_since_sqlradacct TIMESTAMP_START TIMESTAMP_END ATTRIBUTE
+
+Returns the sum of the given attribute for all accounts (see L<FS::svc_acct>)
+in this package for sessions ending between TIMESTAMP_START (inclusive) and
+TIMESTAMP_END (exclusive).
+
+TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
+L<perlfunc/"time">. Also see L<Time::Local> and L<Date::Parse> for conversion
+functions.
+
+=cut
+
+#note: POD here, implementation in FS::cust_svc
+sub attribute_since_sqlradacct {
+ my $self = shift;
+ $self->cust_svc->attribute_since_sqlradacct(@_);
+}
+
+=item get_session_history TIMESTAMP_START TIMESTAMP_END
+
+Returns an array of hash references of this customers login history for the
+given time range. (document this better)
+
+=cut
+
+sub get_session_history {
+ my $self = shift;
+ $self->cust_svc->get_session_history(@_);
+}
+
+=item last_login_text
+
+Returns text describing the time of last login.
+
+=cut
+
+sub last_login_text {
+ my $self = shift;
+ $self->last_login ? ctime($self->last_login) : 'unknown';
+}
+
+=item get_cdrs TIMESTAMP_START TIMESTAMP_END [ 'OPTION' => 'VALUE ... ]
+
+=cut
+
+sub get_cdrs {
+ my($self, $start, $end, %opt ) = @_;
+
+ my $did = $self->username; #yup
+
+ my $prefix = $opt{'default_prefix'}; #convergent.au '+61'
+
+ my $for_update = $opt{'for_update'} ? 'FOR UPDATE' : '';
+
+ #SELECT $for_update * FROM cdr
+ # WHERE calldate >= $start #need a conversion
+ # AND calldate < $end #ditto
+ # AND ( charged_party = "$did"
+ # OR charged_party = "$prefix$did" #if length($prefix);
+ # OR ( ( charged_party IS NULL OR charged_party = '' )
+ # AND
+ # ( src = "$did" OR src = "$prefix$did" ) # if length($prefix)
+ # )
+ # )
+ # AND ( freesidestatus IS NULL OR freesidestatus = '' )
+
+ my $charged_or_src;
+ if ( length($prefix) ) {
+ $charged_or_src =
+ " AND ( charged_party = '$did'
+ OR charged_party = '$prefix$did'
+ OR ( ( charged_party IS NULL OR charged_party = '' )
+ AND
+ ( src = '$did' OR src = '$prefix$did' )
+ )
+ )
+ ";
+ } else {
+ $charged_or_src =
+ " AND ( charged_party = '$did'
+ OR ( ( charged_party IS NULL OR charged_party = '' )
+ AND
+ src = '$did'
+ )
+ )
+ ";
+
+ }
+
+ qsearch(
+ 'select' => "$for_update *",
+ 'table' => 'cdr',
+ 'hashref' => {
+ #( freesidestatus IS NULL OR freesidestatus = '' )
+ 'freesidestatus' => '',
+ },
+ 'extra_sql' => $charged_or_src,
+
+ );
+
+}
+
+=item radius_groups
+
+Returns all RADIUS groups for this account (see L<FS::radius_usergroup>).
+
+=cut
+
+sub radius_groups {
+ my $self = shift;
+ if ( $self->usergroup ) {
+ confess "explicitly specified usergroup not an arrayref: ". $self->usergroup
+ unless ref($self->usergroup) eq 'ARRAY';
+ #when provisioning records, export callback runs in svc_Common.pm before
+ #radius_usergroup records can be inserted...
+ @{$self->usergroup};
+ } else {
+ map { $_->groupname }
+ qsearch('radius_usergroup', { 'svcnum' => $self->svcnum } );
+ }
+}
+
+=item clone_suspended
+
+Constructor used by FS::part_export::_export_suspend fallback. Document
+better.
+
+=cut
+
+sub clone_suspended {
+ my $self = shift;
+ my %hash = $self->hash;
+ $hash{_password} = join('',map($pw_set[ int(rand $#pw_set) ], (0..7) ) );
+ new FS::svc_acct \%hash;
+}
+
+=item clone_kludge_unsuspend
+
+Constructor used by FS::part_export::_export_unsuspend fallback. Document
+better.
+
+=cut
+
+sub clone_kludge_unsuspend {
+ my $self = shift;
+ my %hash = $self->hash;
+ $hash{_password} = '';
+ new FS::svc_acct \%hash;
+}
+
+=item check_password
+
+Checks the supplied password against the (possibly encrypted) password in the
+database. Returns true for a successful authentication, false for no match.
+
+Currently supported encryptions are: classic DES crypt() and MD5
+
+=cut
+
+sub check_password {
+ my($self, $check_password) = @_;
+
+ #remove old-style SUSPENDED kludge, they should be allowed to login to
+ #self-service and pay up
+ ( my $password = $self->_password ) =~ s/^\*SUSPENDED\* //;
+
+ if ( $self->_password_encoding eq 'ldap' ) {
+
+ my $auth = from_rfc2307 Authen::Passphrase $self->_password;
+ return $auth->match($check_password);
+
+ } elsif ( $self->_password_encoding eq 'crypt' ) {
+
+ my $auth = from_crypt Authen::Passphrase $self->_password;
+ return $auth->match($check_password);
+
+ } elsif ( $self->_password_encoding eq 'plain' ) {
+
+ return $check_password eq $password;
+
+ } else {
+
+ #XXX this could be replaced with Authen::Passphrase stuff
+
+ if ( $password =~ /^(\*|!!?)$/ ) { #no self-service login
+ return 0;
+ } elsif ( length($password) < 13 ) { #plaintext
+ $check_password eq $password;
+ } elsif ( length($password) == 13 ) { #traditional DES crypt
+ crypt($check_password, $password) eq $password;
+ } elsif ( $password =~ /^\$1\$/ ) { #MD5 crypt
+ unix_md5_crypt($check_password, $password) eq $password;
+ } elsif ( $password =~ /^\$2a?\$/ ) { #Blowfish
+ warn "Can't check password: Blowfish encryption not yet supported, ".
+ "svcnum ". $self->svcnum. "\n";
+ 0;
+ } else {
+ warn "Can't check password: Unrecognized encryption for svcnum ".
+ $self->svcnum. "\n";
+ 0;
+ }
+
+ }
+
+}
+
+=item crypt_password [ DEFAULT_ENCRYPTION_TYPE ]
+
+Returns an encrypted password, either by passing through an encrypted password
+in the database or by encrypting a plaintext password from the database.
+
+The optional DEFAULT_ENCRYPTION_TYPE parameter can be set to I<crypt> (classic
+UNIX DES crypt), I<md5> (md5 crypt supported by most modern Linux and BSD
+distrubtions), or (eventually) I<blowfish> (blowfish hashing supported by
+OpenBSD, SuSE, other Linux distibutions with pam_unix2, etc.). The default
+encryption type is only used if the password is not already encrypted in the
+database.
+
+=cut
+
+sub crypt_password {
+ my $self = shift;
+
+ if ( $self->_password_encoding eq 'ldap' ) {
+
+ if ( $self->_password =~ /^\{(PLAIN|CLEARTEXT)\}(.+)$/ ) {
+ my $plain = $2;
+
+ #XXX this could be replaced with Authen::Passphrase stuff
+
+ my $encryption = ( scalar(@_) && $_[0] ) ? shift : 'crypt';
+ if ( $encryption eq 'crypt' ) {
+ crypt(
+ $self->_password,
+ $saltset[int(rand(64))].$saltset[int(rand(64))]
+ );
+ } elsif ( $encryption eq 'md5' ) {
+ unix_md5_crypt( $self->_password );
+ } elsif ( $encryption eq 'blowfish' ) {
+ croak "unknown encryption method $encryption";
+ } else {
+ croak "unknown encryption method $encryption";
+ }
+
+ } elsif ( $self->_password =~ /^\{CRYPT\}(.+)$/ ) {
+ $1;
+ }
+
+ } elsif ( $self->_password_encoding eq 'crypt' ) {
+
+ return $self->_password;
+
+ } elsif ( $self->_password_encoding eq 'plain' ) {
+
+ #XXX this could be replaced with Authen::Passphrase stuff
+
+ my $encryption = ( scalar(@_) && $_[0] ) ? shift : 'crypt';
+ if ( $encryption eq 'crypt' ) {
+ crypt(
+ $self->_password,
+ $saltset[int(rand(64))].$saltset[int(rand(64))]
+ );
+ } elsif ( $encryption eq 'md5' ) {
+ unix_md5_crypt( $self->_password );
+ } elsif ( $encryption eq 'blowfish' ) {
+ croak "unknown encryption method $encryption";
+ } else {
+ croak "unknown encryption method $encryption";
+ }
+
+ } else {
+
+ if ( length($self->_password) == 13
+ || $self->_password =~ /^\$(1|2a?)\$/
+ || $self->_password =~ /^(\*|NP|\*LK\*|!!?)$/
+ )
+ {
+ $self->_password;
+ } else {
+
+ #XXX this could be replaced with Authen::Passphrase stuff
+
+ my $encryption = ( scalar(@_) && $_[0] ) ? shift : 'crypt';
+ if ( $encryption eq 'crypt' ) {
+ crypt(
+ $self->_password,
+ $saltset[int(rand(64))].$saltset[int(rand(64))]
+ );
+ } elsif ( $encryption eq 'md5' ) {
+ unix_md5_crypt( $self->_password );
+ } elsif ( $encryption eq 'blowfish' ) {
+ croak "unknown encryption method $encryption";
+ } else {
+ croak "unknown encryption method $encryption";
+ }
+
+ }
+
+ }
+
+}
+
+=item ldap_password [ DEFAULT_ENCRYPTION_TYPE ]
+
+Returns an encrypted password in "LDAP" format, with a curly-bracked prefix
+describing the format, for example, "{PLAIN}himom", "{CRYPT}94pAVyK/4oIBk" or
+"{MD5}5426824942db4253f87a1009fd5d2d4".
+
+The optional DEFAULT_ENCRYPTION_TYPE is not yet used, but the idea is for it
+to work the same as the B</crypt_password> method.
+
+=cut
+
+sub ldap_password {
+ my $self = shift;
+ #eventually should check a "password-encoding" field
+
+ if ( $self->_password_encoding eq 'ldap' ) {
+
+ return $self->_password;
+
+ } elsif ( $self->_password_encoding eq 'crypt' ) {
+
+ if ( length($self->_password) == 13 ) { #crypt
+ return '{CRYPT}'. $self->_password;
+ } elsif ( $self->_password =~ /^\$1\$(.*)$/ && length($1) == 31 ) { #passwdMD5
+ return '{MD5}'. $1;
+ #} elsif ( $self->_password =~ /^\$2a?\$(.*)$/ ) { #Blowfish
+ # die "Blowfish encryption not supported in this context, svcnum ".
+ # $self->svcnum. "\n";
+ } else {
+ warn "encryption method not (yet?) supported in LDAP context";
+ return '{CRYPT}*'; #unsupported, should not auth
+ }
+
+ } elsif ( $self->_password_encoding eq 'plain' ) {
+
+ return '{PLAIN}'. $self->_password;
+
+ #return '{CLEARTEXT}'. $self->_password; #?
+
+ } else {
+
+ if ( length($self->_password) == 13 ) { #crypt
+ return '{CRYPT}'. $self->_password;
+ } elsif ( $self->_password =~ /^\$1\$(.*)$/ && length($1) == 31 ) { #passwdMD5
+ return '{MD5}'. $1;
+ } elsif ( $self->_password =~ /^\$2a?\$(.*)$/ ) { #Blowfish
+ warn "Blowfish encryption not supported in this context, svcnum ".
+ $self->svcnum. "\n";
+ return '{CRYPT}*';
+
+ #are these two necessary anymore?
+ } elsif ( $self->_password =~ /^(\w{48})$/ ) { #LDAP SSHA
+ return '{SSHA}'. $1;
+ } elsif ( $self->_password =~ /^(\w{64})$/ ) { #LDAP NS-MTA-MD5
+ return '{NS-MTA-MD5}'. $1;
+
+ } else { #plaintext
+ return '{PLAIN}'. $self->_password;
+
+ #return '{CLEARTEXT}'. $self->_password; #?
+
+ #XXX this could be replaced with Authen::Passphrase stuff if it gets used
+ #my $encryption = ( scalar(@_) && $_[0] ) ? shift : 'crypt';
+ #if ( $encryption eq 'crypt' ) {
+ # return '{CRYPT}'. crypt(
+ # $self->_password,
+ # $saltset[int(rand(64))].$saltset[int(rand(64))]
+ # );
+ #} elsif ( $encryption eq 'md5' ) {
+ # unix_md5_crypt( $self->_password );
+ #} elsif ( $encryption eq 'blowfish' ) {
+ # croak "unknown encryption method $encryption";
+ #} else {
+ # croak "unknown encryption method $encryption";
+ #}
+ }
+
+ }
+
+}
+
+=item domain_slash_username
+
+Returns $domain/$username/
+
+=cut
+
+sub domain_slash_username {
+ my $self = shift;
+ $self->domain. '/'. $self->username. '/';
+}
+
+=item virtual_maildir
+
+Returns $domain/maildirs/$username/
+
+=cut
+
+sub virtual_maildir {
+ my $self = shift;
+ $self->domain. '/maildirs/'. $self->username. '/';
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item send_email
+
+This is the FS::svc_acct job-queue-able version. It still uses
+FS::Misc::send_email under-the-hood.
+
+=cut
+
+sub send_email {
+ my %opt = @_;
+
+ eval "use FS::Misc qw(send_email)";
+ die $@ if $@;
+
+ $opt{mimetype} ||= 'text/plain';
+ $opt{mimetype} .= '; charset="iso-8859-1"' unless $opt{mimetype} =~ /charset/;
+
+ my $error = send_email(
+ 'from' => $opt{from},
+ 'to' => $opt{to},
+ 'subject' => $opt{subject},
+ 'content-type' => $opt{mimetype},
+ 'body' => [ map "$_\n", split("\n", $opt{body}) ],
+ );
+ die $error if $error;
+}
+
+=item check_and_rebuild_fuzzyfiles
+
+=cut
+
+sub check_and_rebuild_fuzzyfiles {
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ -e "$dir/svc_acct.username"
+ or &rebuild_fuzzyfiles;
+}
+
+=item rebuild_fuzzyfiles
+
+=cut
+
+sub rebuild_fuzzyfiles {
+
+ use Fcntl qw(:flock);
+
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+
+ #username
+
+ open(USERNAMELOCK,">>$dir/svc_acct.username")
+ or die "can't open $dir/svc_acct.username: $!";
+ flock(USERNAMELOCK,LOCK_EX)
+ or die "can't lock $dir/svc_acct.username: $!";
+
+ my @all_username = map $_->getfield('username'), qsearch('svc_acct', {});
+
+ open (USERNAMECACHE,">$dir/svc_acct.username.tmp")
+ or die "can't open $dir/svc_acct.username.tmp: $!";
+ print USERNAMECACHE join("\n", @all_username), "\n";
+ close USERNAMECACHE or die "can't close $dir/svc_acct.username.tmp: $!";
+
+ rename "$dir/svc_acct.username.tmp", "$dir/svc_acct.username";
+ close USERNAMELOCK;
+
+}
+
+=item all_username
+
+=cut
+
+sub all_username {
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ open(USERNAMECACHE,"<$dir/svc_acct.username")
+ or die "can't open $dir/svc_acct.username: $!";
+ my @array = map { chomp; $_; } <USERNAMECACHE>;
+ close USERNAMECACHE;
+ \@array;
+}
+
+=item append_fuzzyfiles USERNAME
+
+=cut
+
+sub append_fuzzyfiles {
+ my $username = shift;
+
+ &check_and_rebuild_fuzzyfiles;
+
+ use Fcntl qw(:flock);
+
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+
+ open(USERNAME,">>$dir/svc_acct.username")
+ or die "can't open $dir/svc_acct.username: $!";
+ flock(USERNAME,LOCK_EX)
+ or die "can't lock $dir/svc_acct.username: $!";
+
+ print USERNAME "$username\n";
+
+ flock(USERNAME,LOCK_UN)
+ or die "can't unlock $dir/svc_acct.username: $!";
+ close USERNAME;
+
+ 1;
+}
+
+
+
+=item radius_usergroup_selector GROUPS_ARRAYREF [ SELECTNAME ]
+
+=cut
+
+sub radius_usergroup_selector {
+ my $sel_groups = shift;
+ my %sel_groups = map { $_=>1 } @$sel_groups;
+
+ my $selectname = shift || 'radius_usergroup';
+
+ my $dbh = dbh;
+ my $sth = $dbh->prepare(
+ 'SELECT DISTINCT(groupname) FROM radius_usergroup ORDER BY groupname'
+ ) or die $dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+ my @all_groups = map { $_->[0] } @{$sth->fetchall_arrayref};
+
+ my $html = <<END;
+ <SCRIPT>
+ function ${selectname}_doadd(object) {
+ var myvalue = object.${selectname}_add.value;
+ var optionName = new Option(myvalue,myvalue,false,true);
+ var length = object.$selectname.length;
+ object.$selectname.options[length] = optionName;
+ object.${selectname}_add.value = "";
+ }
+ </SCRIPT>
+ <SELECT MULTIPLE NAME="$selectname">
+END
+
+ foreach my $group ( @all_groups ) {
+ $html .= qq(<OPTION VALUE="$group");
+ if ( $sel_groups{$group} ) {
+ $html .= ' SELECTED';
+ $sel_groups{$group} = 0;
+ }
+ $html .= ">$group</OPTION>\n";
+ }
+ foreach my $group ( grep { $sel_groups{$_} } keys %sel_groups ) {
+ $html .= qq(<OPTION VALUE="$group" SELECTED>$group</OPTION>\n);
+ };
+ $html .= '</SELECT>';
+
+ $html .= qq!<BR><INPUT TYPE="text" NAME="${selectname}_add">!.
+ qq!<INPUT TYPE="button" VALUE="Add new group" onClick="${selectname}_doadd(this.form)">!;
+
+ $html;
+}
+
+=item reached_threshold
+
+Performs some activities when svc_acct thresholds (such as number of seconds
+remaining) are reached.
+
+=cut
+
+sub reached_threshold {
+ my %opt = @_;
+
+ my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } );
+ die "Cannot find svc_acct with svcnum " . $opt{'svcnum'} unless $svc_acct;
+
+ if ( $opt{'op'} eq '+' ){
+ $svc_acct->setfield( $opt{'column'}.'_threshold',
+ int($svc_acct->getfield($opt{'column'})
+ * ( $conf->exists('svc_acct-usage_threshold')
+ ? $conf->config('svc_acct-usage_threshold')/100
+ : 0.80
+ )
+ )
+ );
+ my $error = $svc_acct->replace;
+ die $error if $error;
+ }elsif ( $opt{'op'} eq '-' ){
+
+ my $threshold = $svc_acct->getfield( $opt{'column'}.'_threshold' );
+ return '' if ($threshold eq '' );
+
+ $svc_acct->setfield( $opt{'column'}.'_threshold', 0 );
+ my $error = $svc_acct->replace;
+ die $error if $error; # email next time, i guess
+
+ if ( $warning_template ) {
+ eval "use FS::Misc qw(send_email)";
+ die $@ if $@;
+
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ my $cust_main = $cust_pkg->cust_main;
+
+ my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ }
+ $cust_main->invoicing_list,
+ ($opt{'to'} ? $opt{'to'} : ())
+ );
+
+ my $mimetype = $warning_mimetype;
+ $mimetype .= '; charset="iso-8859-1"' unless $opt{mimetype} =~ /charset/;
+
+ my $body = $warning_template->fill_in( HASH => {
+ 'custnum' => $cust_main->custnum,
+ 'username' => $svc_acct->username,
+ 'password' => $svc_acct->_password,
+ 'first' => $cust_main->first,
+ 'last' => $cust_main->getfield('last'),
+ 'pkg' => $cust_pkg->part_pkg->pkg,
+ 'column' => $opt{'column'},
+ 'amount' => $opt{'column'} =~/bytes/
+ ? FS::UI::bytecount::display_bytecount($svc_acct->getfield($opt{'column'}))
+ : $svc_acct->getfield($opt{'column'}),
+ 'threshold' => $opt{'column'} =~/bytes/
+ ? FS::UI::bytecount::display_bytecount($threshold)
+ : $threshold,
+ } );
+
+
+ my $error = send_email(
+ 'from' => $warning_from,
+ 'to' => $to,
+ 'subject' => $warning_subject,
+ 'content-type' => $mimetype,
+ 'body' => [ map "$_\n", split("\n", $body) ],
+ );
+ die $error if $error;
+ }
+ }else{
+ die "unknown op: " . $opt{'op'};
+ }
+}
+
+=back
+
+=head1 BUGS
+
+The $recref stuff in sub check should be cleaned up.
+
+The suspend, unsuspend and cancel methods update the database, but not the
+current object. This is probably a bug as it's unexpected and
+counterintuitive.
+
+radius_usergroup_selector? putting web ui components in here? they should
+probably live somewhere else...
+
+insertion of RADIUS group stuff in insert could be done with child_objects now
+(would probably clean up export of them too)
+
+=head1 SEE ALSO
+
+L<FS::svc_Common>, edit/part_svc.cgi from an installed web interface,
+export.html from the base documentation, L<FS::Record>, L<FS::Conf>,
+L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, L<FS::queue>,
+L<freeside-queued>), L<FS::svc_acct_pop>,
+schema.html from the base documentation.
+
+=cut
+
+=item domain_select_hash %OPTIONS
+
+Returns a hash SVCNUM => DOMAIN ... representing the domains this customer
+may at present purchase.
+
+Currently available options are: I<pkgnum> I<svcpart>
+
+=cut
+
+sub domain_select_hash {
+ my ($self, %options) = @_;
+ my %domains = ();
+ my $part_svc;
+ my $cust_pkg;
+
+ if (ref($self)) {
+ $part_svc = $self->part_svc;
+ $cust_pkg = $self->cust_svc->cust_pkg
+ if $self->cust_svc;
+ }
+
+ $part_svc = qsearchs('part_svc', { 'svcpart' => $options{svcpart} })
+ if $options{'svcpart'};
+
+ $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $options{pkgnum} })
+ if $options{'pkgnum'};
+
+ if ($part_svc && ( $part_svc->part_svc_column('domsvc')->columnflag eq 'S'
+ || $part_svc->part_svc_column('domsvc')->columnflag eq 'F')) {
+ %domains = map { $_->svcnum => $_->domain }
+ map { qsearchs('svc_domain', { 'svcnum' => $_ }) }
+ split(',', $part_svc->part_svc_column('domsvc')->columnvalue);
+ }elsif ($cust_pkg && !$conf->exists('svc_acct-alldomains') ) {
+ %domains = map { $_->svcnum => $_->domain }
+ map { qsearchs('svc_domain', { 'svcnum' => $_->svcnum }) }
+ map { qsearch('cust_svc', { 'pkgnum' => $_->pkgnum } ) }
+ qsearch('cust_pkg', { 'custnum' => $cust_pkg->custnum });
+ }else{
+ %domains = map { $_->svcnum => $_->domain } qsearch('svc_domain', {} );
+ }
+
+ if ($part_svc && $part_svc->part_svc_column('domsvc')->columnflag eq 'D') {
+ my $svc_domain = qsearchs('svc_domain',
+ { 'svcnum' => $part_svc->part_svc_column('domsvc')->columnvalue } );
+ if ( $svc_domain ) {
+ $domains{$svc_domain->svcnum} = $svc_domain->domain;
+ }else{
+ warn "unknown svc_domain.svcnum for part_svc_column domsvc: ".
+ $part_svc->part_svc_column('domsvc')->columnvalue;
+
+ }
+ }
+
+ (%domains);
+}
+
+1;
+
diff --git a/FS/FS/svc_acct_pop.pm b/FS/FS/svc_acct_pop.pm
new file mode 100644
index 0000000..de41f5b
--- /dev/null
+++ b/FS/FS/svc_acct_pop.pm
@@ -0,0 +1,206 @@
+package FS::svc_acct_pop;
+
+use strict;
+use vars qw( @ISA @EXPORT_OK @svc_acct_pop %svc_acct_pop );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw( FS::Record Exporter );
+@EXPORT_OK = qw( popselector );
+
+=head1 NAME
+
+FS::svc_acct_pop - Object methods for svc_acct_pop records
+
+=head1 SYNOPSIS
+
+ use FS::svc_acct_pop;
+
+ $record = new FS::svc_acct_pop \%hash;
+ $record = new FS::svc_acct_pop { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $html = FS::svc_acct_pop::popselector( $popnum, $state );
+
+=head1 DESCRIPTION
+
+An FS::svc_acct object represents an point of presence. FS::svc_acct_pop
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item popnum - primary key (assigned automatically for new accounts)
+
+=item city
+
+=item state
+
+=item ac - area code
+
+=item exch - exchange
+
+=item loc - rest of number
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new point of presence (if only it were that easy!). To add the
+point of presence to the database, see L<"insert">.
+
+=cut
+
+sub table { 'svc_acct_pop'; }
+
+=item insert
+
+Adds this point of presence to the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item delete
+
+Removes this point of presence from the database.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid point of presence. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ $self->ut_numbern('popnum')
+ or $self->ut_text('city')
+ or $self->ut_text('state')
+ or $self->ut_number('ac')
+ or $self->ut_number('exch')
+ or $self->ut_numbern('loc')
+ or $self->SUPER::check
+ ;
+
+}
+
+=item text
+
+Returns:
+
+"$city, $state ($ac)/$exch"
+
+=cut
+
+sub text {
+ my $self = shift;
+ $self->city. ', '. $self->state.
+ ' ('. $self->ac. ')/'. $self->exch. '-'. $self->loc;
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item popselector [ POPNUM [ STATE ] ]
+
+=cut
+
+#horrible false laziness with signup.cgi (pull special-case for 0 & 1
+# pop code out from signup.cgi??)
+sub popselector {
+ my( $popnum, $state ) = @_;
+
+ unless ( @svc_acct_pop ) { #cache pop list
+ @svc_acct_pop = qsearch('svc_acct_pop', {} );
+ %svc_acct_pop = ();
+ push @{$svc_acct_pop{$_->state}}, $_ foreach @svc_acct_pop;
+ }
+
+ my $text = <<END;
+ <SCRIPT>
+ function opt(what,href,text) {
+ var optionName = new Option(text, href, false, false)
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function popstate_changed(what) {
+ state = what.options[what.selectedIndex].text;
+ what.form.popnum.options.length = 0
+ what.form.popnum.options[0] = new Option("", "", false, true);
+END
+
+ foreach my $popstate ( sort { $a cmp $b } keys %svc_acct_pop ) {
+ $text .= "\nif ( state == \"$popstate\" ) {\n";
+
+ foreach my $pop ( @{$svc_acct_pop{$popstate}}) {
+ my $o_popnum = $pop->popnum;
+ my $poptext = $pop->text;
+ $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n"
+ }
+ $text .= "}\n";
+ }
+
+ $text .= "}\n</SCRIPT>\n";
+
+ $text .=
+ qq!<SELECT NAME="popstate" SIZE=1 onChange="popstate_changed(this)">!.
+ qq!<OPTION> !;
+ $text .= "<OPTION>$_" foreach sort { $a cmp $b } keys %svc_acct_pop;
+ $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD><TD>';
+
+ $text .= qq!<SELECT NAME="popnum" SIZE=1><OPTION> !;
+ my @initial_select;
+ if ( scalar(@svc_acct_pop) > 100 ) {
+ @initial_select = qsearchs( 'svc_acct_pop', { 'popnum' => $popnum } );
+ } else {
+ @initial_select = @svc_acct_pop;
+ }
+ foreach my $pop ( @initial_select ) {
+ $text .= qq!<OPTION VALUE="!. $pop->popnum. '"'.
+ ( ( $popnum && $pop->popnum == $popnum ) ? ' SELECTED' : '' ). ">".
+ $pop->text;
+ }
+ $text .= '</SELECT>';
+
+ $text;
+
+}
+
+=back
+
+=head1 BUGS
+
+It should be renamed to part_pop.
+
+popselector? putting web ui components in here? they should probably live
+somewhere else...
+
+popselector: pull special-case for 0 & 1 pop code out from signup.cgi
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::svc_acct>, L<FS::part_pop_local>, schema.html from the
+base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm
new file mode 100755
index 0000000..b808527
--- /dev/null
+++ b/FS/FS/svc_broadband.pm
@@ -0,0 +1,342 @@
+package FS::svc_broadband;
+
+use strict;
+use vars qw(@ISA $conf);
+use FS::Record qw( qsearchs qsearch dbh );
+use FS::svc_Common;
+use FS::cust_svc;
+use FS::addr_block;
+use NetAddr::IP;
+
+@ISA = qw( FS::svc_Common );
+
+$FS::UID::callback{'FS::svc_broadband'} = sub {
+ $conf = new FS::Conf;
+};
+
+=head1 NAME
+
+FS::svc_broadband - Object methods for svc_broadband records
+
+=head1 SYNOPSIS
+
+ use FS::svc_broadband;
+
+ $record = new FS::svc_broadband \%hash;
+ $record = new FS::svc_broadband { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+An FS::svc_broadband object represents a 'broadband' Internet connection, such
+as a DSL, cable modem, or fixed wireless link. These services are assumed to
+have the following properties:
+
+FS::svc_broadband inherits from FS::svc_Common. The following fields are
+currently supported:
+
+=over 4
+
+=item svcnum - primary key
+
+=item blocknum - see FS::addr_block
+
+=item
+speed_up - maximum upload speed, in bits per second. If set to zero, upload
+speed will be unlimited. Exports that do traffic shaping should handle this
+correctly, and not blindly set the upload speed to zero and kill the customer's
+connection.
+
+=item
+speed_down - maximum download speed, as above
+
+=item ip_addr - the customer's IP address. If the customer needs more than one
+IP address, set this to the address of the customer's router. As a result, the
+customer's router will have the same address for both its internal and external
+interfaces thus saving address space. This has been found to work on most NAT
+routers available.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new svc_broadband. To add the record to the database, see
+"insert".
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table_info {
+ {
+ 'name' => 'Broadband',
+ 'name_plural' => 'Broadband services',
+ 'longname_plural' => 'Fixed (username-less) broadband services',
+ 'display_weight' => 50,
+ 'cancel_weight' => 70,
+ 'fields' => {
+ 'description' => 'Descriptive label for this particular device.',
+ 'speed_down' => 'Maximum download speed for this service in Kbps. 0 denotes unlimited.',
+ 'speed_up' => 'Maximum upload speed for this service in Kbps. 0 denotes unlimited.',
+ 'ip_addr' => 'IP address. Leave blank for automatic assignment.',
+ 'blocknum' => { 'label' => 'Address block',
+ 'type' => 'select',
+ 'select_table' => 'addr_block',
+ 'select_key' => 'blocknum',
+ 'select_label' => 'cidr',
+ 'disable_inventory' => 1,
+ },
+ },
+ };
+}
+
+sub table { 'svc_broadband'; }
+
+=item search_sql STRING
+
+Class method which returns an SQL fragment to search for the given string.
+
+=cut
+
+sub search_sql {
+ my( $class, $string ) = @_;
+ if ( $string =~ /^(\d{1,3}\.){3}\d{1,3}$/ ) {
+ $class->search_sql_field('ip_addr', $string );
+ }elsif ( $string =~ /^([a-fA-F0-9]{12})$/ ) {
+ $class->search_sql_field('mac_addr', uc($string));
+ }elsif ( $string =~ /^(([a-fA-F0-9]{1,2}:){5}([a-fA-F0-9]{1,2}))$/ ) {
+ $class->search_sql_field('mac_addr', uc("$2$3$4$5$6$7") );
+ } else {
+ '1 = 0'; #false
+ }
+}
+
+=item label
+
+Returns the IP address.
+
+=cut
+
+sub label {
+ my $self = shift;
+ $self->ip_addr;
+}
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+The additional fields pkgnum and svcpart (see FS::cust_svc) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+Currently available options are: I<depend_jobnum>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+=cut
+
+# Standard FS::svc_Common::insert
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# Standard FS::svc_Common::delete
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# Standard FS::svc_Common::replace
+
+=item suspend
+
+Called by the suspend method of FS::cust_pkg (see FS::cust_pkg).
+
+=item unsuspend
+
+Called by the unsuspend method of FS::cust_pkg (see FS::cust_pkg).
+
+=item cancel
+
+Called by the cancel method of FS::cust_pkg (see FS::cust_pkg).
+
+=item check
+
+Checks all fields to make sure this is a valid broadband service. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+ my $x = $self->setfixed;
+
+ return $x unless ref($x);
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ || $self->ut_numbern('blocknum')
+ || $self->ut_textn('description')
+ || $self->ut_number('speed_up')
+ || $self->ut_number('speed_down')
+ || $self->ut_ipn('ip_addr')
+ || $self->ut_hexn('mac_addr')
+ || $self->ut_hexn('auth_key')
+ || $self->ut_coordn('latitude', -90, 90)
+ || $self->ut_coordn('longitude', -180, 180)
+ || $self->ut_sfloatn('altitude')
+ || $self->ut_textn('vlan_profile')
+ ;
+ return $error if $error;
+
+ if($self->speed_up < 0) { return 'speed_up must be positive'; }
+ if($self->speed_down < 0) { return 'speed_down must be positive'; }
+
+ my $cust_svc = $self->svcnum
+ ? qsearchs('cust_svc', { 'svcnum' => $self->svcnum } )
+ : '';
+ my $cust_pkg;
+ if ($cust_svc) {
+ $cust_pkg = $cust_svc->cust_pkg;
+ }else{
+ $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $self->pkgnum } );
+ return "Invalid pkgnum" unless $cust_pkg;
+ }
+
+ if ($self->blocknum) {
+ $error = $self->ut_foreign_key('blocknum', 'addr_block', 'blocknum');
+ return $error if $error;
+ }
+
+ if ($cust_pkg && $self->blocknum) {
+ my $addr_agentnum = $self->addr_block->agentnum;
+ if ($addr_agentnum && $addr_agentnum != $cust_pkg->cust_main->agentnum) {
+ return "Address block does not service this customer";
+ }
+ }
+
+ if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
+ return "Must supply either address or block"
+ unless $self->blocknum;
+ my $next_addr = $self->addr_block->next_free_addr;
+ if ($next_addr) {
+ $self->ip_addr($next_addr->addr);
+ } else {
+ return "No free addresses in addr_block (blocknum: ".$self->blocknum.")";
+ }
+ }
+
+ if (not($self->blocknum)) {
+ return "Must supply either address or block"
+ unless ($self->ip_addr and $self->ip_addr ne '0.0.0.0');
+ my @block = grep { $_->NetAddr->contains($self->NetAddr) }
+ map { $_->addr_block }
+ $self->allowed_routers;
+ if (scalar(@block)) {
+ $self->blocknum($block[0]->blocknum);
+ }else{
+ return "Address not with available block.";
+ }
+ }
+
+ # This should catch errors in the ip_addr. If it doesn't,
+ # they'll almost certainly not map into the block anyway.
+ my $self_addr = $self->NetAddr; #netmask is /32
+ return ('Cannot parse address: ' . $self->ip_addr) unless $self_addr;
+
+ my $block_addr = $self->addr_block->NetAddr;
+ unless ($block_addr->contains($self_addr)) {
+ return 'blocknum '.$self->blocknum.' does not contain address '.$self->ip_addr;
+ }
+
+ my $router = $self->addr_block->router
+ or return 'Cannot assign address from unallocated block:'.$self->addr_block->blocknum;
+ if(grep { $_->routernum == $router->routernum} $self->allowed_routers) {
+ } # do nothing
+ else {
+ return 'Router '.$router->routernum.' cannot provide svcpart '.$self->svcpart;
+ }
+
+ $self->SUPER::check;
+}
+
+=item NetAddr
+
+Returns a NetAddr::IP object containing the IP address of this service. The netmask
+is /32.
+
+=cut
+
+sub NetAddr {
+ my $self = shift;
+ new NetAddr::IP ($self->ip_addr);
+}
+
+=item addr_block
+
+Returns the FS::addr_block record (i.e. the address block) for this broadband service.
+
+=cut
+
+sub addr_block {
+ my $self = shift;
+ qsearchs('addr_block', { blocknum => $self->blocknum });
+}
+
+=back
+
+=item allowed_routers
+
+Returns a list of allowed FS::router objects.
+
+=cut
+
+sub allowed_routers {
+ my $self = shift;
+ map { $_->router } qsearch('part_svc_router', { svcpart => $self->svcpart });
+}
+
+=head1 BUGS
+
+The business with sb_field has been 'fixed', in a manner of speaking.
+
+allowed_routers isn't agent virtualized because part_svc isn't agent
+virtualized
+
+=head1 SEE ALSO
+
+FS::svc_Common, FS::Record, FS::addr_block,
+FS::part_svc, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_domain.pm b/FS/FS/svc_domain.pm
new file mode 100644
index 0000000..47aa8f3
--- /dev/null
+++ b/FS/FS/svc_domain.pm
@@ -0,0 +1,480 @@
+package FS::svc_domain;
+
+use strict;
+use vars qw( @ISA $whois_hack $conf
+ @defaultrecords $soadefaultttl $soaemail $soaexpire $soamachine
+ $soarefresh $soaretry
+);
+use Carp;
+use Scalar::Util qw( blessed );
+use Date::Format;
+#use Net::Whois::Raw;
+use Net::Domain::TLD qw(tld_exists);
+use FS::Record qw(fields qsearch qsearchs dbh);
+use FS::Conf;
+use FS::svc_Common;
+use FS::svc_Parent_Mixin;
+use FS::cust_svc;
+use FS::svc_acct;
+use FS::cust_pkg;
+use FS::cust_main;
+use FS::domain_record;
+use FS::queue;
+
+@ISA = qw( FS::svc_Parent_Mixin FS::svc_Common );
+
+#ask FS::UID to run this stuff for us later
+$FS::UID::callback{'FS::domain'} = sub {
+ $conf = new FS::Conf;
+
+ @defaultrecords = $conf->config('defaultrecords');
+ $soadefaultttl = $conf->config('soadefaultttl');
+ $soaemail = $conf->config('soaemail');
+ $soaexpire = $conf->config('soaexpire');
+ $soamachine = $conf->config('soamachine');
+ $soarefresh = $conf->config('soarefresh');
+ $soaretry = $conf->config('soaretry');
+
+};
+
+=head1 NAME
+
+FS::svc_domain - Object methods for svc_domain records
+
+=head1 SYNOPSIS
+
+ use FS::svc_domain;
+
+ $record = new FS::svc_domain \%hash;
+ $record = new FS::svc_domain { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+An FS::svc_domain object represents a domain. FS::svc_domain inherits from
+FS::svc_Common. The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key (assigned automatically for new accounts)
+
+=item domain
+
+=item catchall - optional svcnum of an svc_acct record, designating an email catchall account.
+
+=item suffix -
+
+=item parent_svcnum -
+
+=item registrarnum - Registrar (see L<FS::registrar>)
+
+=item registrarkey - Registrar key or password for this domain
+
+=item setup_date - UNIX timestamp
+
+=item renewal_interval - Number of days before expiration date to start renewal
+
+=item expiration_date - UNIX timestamp
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new domain. To add the domain to the database, see L<"insert">.
+
+=cut
+
+sub table_info {
+ {
+ 'name' => 'Domain',
+ 'sorts' => 'domain',
+ 'display_weight' => 20,
+ 'cancel_weight' => 60,
+ 'fields' => {
+ 'domain' => 'Domain',
+ },
+ };
+}
+
+sub table { 'svc_domain'; }
+
+sub search_sql {
+ my($class, $string) = @_;
+ $class->search_sql_field('domain', $string);
+}
+
+
+=item label
+
+Returns the domain.
+
+=cut
+
+sub label {
+ my $self = shift;
+ $self->domain;
+}
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this domain to the database. If there is an error, returns the error,
+otherwise returns false.
+
+The additional fields I<pkgnum> and I<svcpart> (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+The additional field I<action> should be set to I<N> for new domains or I<M>
+for transfers.
+
+A registration or transfer email will be submitted unless
+$FS::svc_domain::whois_hack is true.
+
+The additional field I<email> can be used to manually set the admin contact
+email address on this email. Otherwise, the svc_acct records for this package
+(see L<FS::cust_pkg>) are searched. If there is exactly one svc_acct record
+in the same package, it is automatically used. Otherwise an error is returned.
+
+If any I<soamachine> configuration file exists, an SOA record is added to
+the domain_record table (see <FS::domain_record>).
+
+If any records are defined in the I<defaultrecords> configuration file,
+appropriate records are added to the domain_record table (see
+L<FS::domain_record>).
+
+Currently available options are: I<depend_jobnum>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $error = $self->check;
+ return $error if $error;
+
+ return "Domain in use (here)"
+ if qsearchs( 'svc_domain', { 'domain' => $self->domain } );
+
+
+ $error = $self->SUPER::insert(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $soamachine ) {
+ my $soa = new FS::domain_record {
+ 'svcnum' => $self->svcnum,
+ 'reczone' => '@',
+ 'recaf' => 'IN',
+ 'rectype' => 'SOA',
+ 'recdata' => "$soamachine $soaemail ( ". time2str("%Y%m%d", time). "00 ".
+ "$soarefresh $soaretry $soaexpire $soadefaultttl )"
+ };
+ $error = $soa->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "couldn't insert SOA record for new domain: $error";
+ }
+
+ foreach my $record ( @defaultrecords ) {
+ my($zone,$af,$type,$data) = split(/\s+/,$record,4);
+ my $domain_record = new FS::domain_record {
+ 'svcnum' => $self->svcnum,
+ 'reczone' => $zone,
+ 'recaf' => $af,
+ 'rectype' => $type,
+ 'recdata' => $data,
+ };
+ my $error = $domain_record->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "couldn't insert record for new domain: $error";
+ }
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ ''; #no error
+}
+
+=item delete
+
+Deletes this domain from the database. If there is an error, returns the
+error, otherwise returns false.
+
+The corresponding FS::cust_svc record will be deleted as well.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete a domain which has accounts!"
+ if qsearch( 'svc_acct', { 'domsvc' => $self->svcnum } );
+
+ #return "Can't delete a domain with (domain_record) zone entries!"
+ # if qsearch('domain_record', { 'svcnum' => $self->svcnum } );
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $domain_record ( reverse $self->domain_record ) {
+ my $error = $domain_record->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't delete DNS entry: ".
+ join(' ', map $domain_record->$_(),
+ qw( reczone recaf rectype recdata )
+ ).
+ ":$error";
+ }
+ }
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+}
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my $new = shift;
+
+ my $old = ( blessed($_[0]) && $_[0]->isa('FS::Record') )
+ ? shift
+ : $new->replace_old;
+
+ return "Can't change domain - reorder."
+ if $old->getfield('domain') ne $new->getfield('domain');
+
+ # Better to do it here than to force the caller to remember that svc_domain is weird.
+ $new->setfield(action => 'M');
+ my $error = $new->SUPER::replace($old, @_);
+ return $error if $error;
+}
+
+=item suspend
+
+Just returns false (no error) for now.
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Just returns false (no error) for now.
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Just returns false (no error) for now.
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid domain. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+Sets any fixed values; see L<FS::part_svc>.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $x = $self->setfixed;
+ return $x unless ref($x);
+ #my $part_svc = $x;
+
+ my $error = $self->ut_numbern('svcnum')
+ || $self->ut_numbern('catchall')
+ ;
+ return $error if $error;
+
+ #hmm
+ my $pkgnum;
+ if ( $self->svcnum ) {
+ my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $self->svcnum } );
+ $pkgnum = $cust_svc->pkgnum;
+ } else {
+ $pkgnum = $self->pkgnum;
+ }
+
+ my($recref) = $self->hashref;
+
+ #if ( $recref->{domain} =~ /^([\w\-\.]{1,22})\.(com|net|org|edu)$/ ) {
+ if ( $recref->{domain} =~ /^([\w\-]{1,63})\.(com|net|org|edu|tv|info|biz)$/ ) {
+ $recref->{domain} = "$1.$2";
+ $recref->{suffix} ||= $2;
+ # hmmmmmmmm.
+ } elsif ( $whois_hack && $recref->{domain} =~ /^([\w\-\.]+)\.(\w+)$/ ) {
+ $recref->{domain} = "$1.$2";
+ # need to match a list of suffixes - no guarantee they're top-level..
+ # http://wiki.mozilla.org/TLD_List
+ # but this will have to do for now...
+ $recref->{suffix} ||= $2;
+ } else {
+ return "Illegal domain ". $recref->{domain}.
+ " (or unknown registry - try \$whois_hack)";
+ }
+
+ $self->suffix =~ /(^|\.)(\w+)$/
+ or return "can't parse suffix for TLD: ". $self->suffix;
+ my $tld = $2;
+ return "No such TLD: .$tld" unless tld_exists($tld);
+
+ if ( $recref->{catchall} ne '' ) {
+ my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $recref->{catchall} } );
+ return "Unknown catchall" unless $svc_acct;
+ }
+
+ $self->ut_alphan('suffix')
+ or $self->ut_foreign_keyn('registrarnum', 'registrar', 'registrarnum')
+ or $self->ut_textn('registrarkey')
+ or $self->ut_numbern('setup_date')
+ or $self->ut_numbern('renewal_interval')
+ or $self->ut_numbern('expiration_date')
+ or $self->ut_textn('purpose')
+ or $self->SUPER::check;
+
+}
+
+=item domain_record
+
+=cut
+
+sub domain_record {
+ my $self = shift;
+
+ my %order = (
+ 'SOA' => 1,
+ 'NS' => 2,
+ 'MX' => 3,
+ 'CNAME' => 4,
+ 'A' => 5,
+ 'TXT' => 6,
+ 'PTR' => 7,
+ );
+
+ my %sort = (
+ #'SOA' => sub { $_[0]->recdata cmp $_[1]->recdata }, #sure hope not though
+# 'SOA' => sub { 0; },
+# 'NS' => sub { 0; },
+ 'MX' => sub { my( $a_weight, $a_name ) = split(/\s+/, $_[0]->recdata);
+ my( $b_weight, $b_name ) = split(/\s+/, $_[1]->recdata);
+ $a_weight <=> $b_weight or $a_name cmp $b_name;
+ },
+ 'CNAME' => sub { $_[0]->reczone cmp $_[1]->reczone },
+ 'A' => sub { $_[0]->reczone cmp $_[1]->reczone },
+
+# 'TXT' => sub { 0; },
+ 'PTR' => sub { $_[0]->reczone <=> $_[1]->reczone },
+ );
+
+ sort { $order{$a->rectype} <=> $order{$b->rectype}
+ or &{ $sort{$a->rectype} || sub { 0; } }($a, $b)
+ }
+ qsearch('domain_record', { svcnum => $self->svcnum } );
+
+}
+
+sub catchall_svc_acct {
+ my $self = shift;
+ if ( $self->catchall ) {
+ qsearchs( 'svc_acct', { 'svcnum' => $self->catchall } );
+ } else {
+ '';
+ }
+}
+
+=item whois
+
+# Returns the Net::Whois::Domain object (see L<Net::Whois>) for this domain, or
+# undef if the domain is not found in whois.
+
+(If $FS::svc_domain::whois_hack is true, returns that in all cases instead.)
+
+=cut
+
+sub whois {
+ #$whois_hack or new Net::Whois::Domain $_[0]->domain;
+ #$whois_hack or die "whois_hack not set...\n";
+}
+
+=back
+
+=head1 BUGS
+
+Delete doesn't send a registration template.
+
+All registries should be supported.
+
+Should change action to a real field.
+
+The $recref stuff in sub check should be cleaned up.
+
+=head1 SEE ALSO
+
+L<FS::svc_Common>, L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>,
+L<FS::part_svc>, L<FS::cust_pkg>, L<Net::Whois>, schema.html from the base
+documentation, config.html from the base documentation.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/svc_external.pm b/FS/FS/svc_external.pm
new file mode 100644
index 0000000..0fb391f
--- /dev/null
+++ b/FS/FS/svc_external.pm
@@ -0,0 +1,204 @@
+package FS::svc_external;
+
+use strict;
+use vars qw(@ISA);
+use FS::Conf;
+use FS::svc_External_Common;
+
+@ISA = qw( FS::svc_External_Common );
+
+=head1 NAME
+
+FS::svc_external - Object methods for svc_external records
+
+=head1 SYNOPSIS
+
+ use FS::svc_external;
+
+ $record = new FS::svc_external \%hash;
+ $record = new FS::svc_external { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+An FS::svc_external object represents a generic externally tracked service.
+FS::svc_external inherits from FS::svc_External_Common (and FS::svc_Common).
+The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key
+
+=item id - unique number of external record
+
+=item title - for invoice line items
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new external service. To add the external service to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table_info {
+ {
+ 'name' => 'External service',
+ 'sorts' => 'id',
+ 'display_weight' => 90,
+ 'cancel_weight' => 10,
+ 'fields' => {
+ 'id' => { label => 'Unique number of external record',
+ type => 'text',
+ disable_default => 1,
+ disable_fixed => 1,
+ },
+ 'title' => { label => 'Printed on invoice line items',
+ type => 'text',
+ disable_inventory => 1,
+ },
+ },
+ };
+}
+
+sub table { 'svc_external'; }
+
+# oh! this should be moved to svc_artera_turbo or something now
+sub label {
+ my $self = shift;
+ my $conf = new FS::Conf;
+ if ( $conf->exists('svc_external-display_type')
+ && $conf->config('svc_external-display_type') eq 'artera_turbo' )
+ {
+ sprintf('%010d', $self->id). '-'.
+ substr('0000000000'.uc($self->title), -10);
+ } else {
+ #$self->SUPER::label;
+ $self->id. ' - '. $self->title;
+ }
+}
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this external service to the database. If there is an error, returns the
+error, otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+Currently available options are: I<depend_jobnum>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+=cut
+
+#sub insert {
+# my $self = shift;
+# my $error;
+#
+# $error = $self->SUPER::insert(@_);
+# return $error if $error;
+#
+# '';
+#}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+#sub delete {
+# my $self = shift;
+# my $error;
+#
+# $error = $self->SUPER::delete;
+# return $error if $error;
+#
+# '';
+#}
+
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+#sub replace {
+# my ( $new, $old ) = ( shift, shift );
+# my $error;
+#
+# $error = $new->SUPER::replace($old);
+# return $error if $error;
+#
+# '';
+#}
+
+=item suspend
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid external service. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+#sub check {
+# my $self = shift;
+# my $error;
+#
+# $error = $self->SUPER::delete;
+# return $error if $error;
+#
+# '';
+#}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_External_Common>, L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>,
+L<FS::part_svc>, L<FS::cust_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_forward.pm b/FS/FS/svc_forward.pm
new file mode 100644
index 0000000..3250f8a
--- /dev/null
+++ b/FS/FS/svc_forward.pm
@@ -0,0 +1,371 @@
+package FS::svc_forward;
+
+use strict;
+use vars qw( @ISA );
+use FS::Conf;
+use FS::Record qw( fields qsearch qsearchs dbh );
+use FS::svc_Common;
+use FS::cust_svc;
+use FS::svc_acct;
+use FS::svc_domain;
+
+@ISA = qw( FS::svc_Common );
+
+=head1 NAME
+
+FS::svc_forward - Object methods for svc_forward records
+
+=head1 SYNOPSIS
+
+ use FS::svc_forward;
+
+ $record = new FS::svc_forward \%hash;
+ $record = new FS::svc_forward { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+An FS::svc_forward object represents a mail forwarding alias. FS::svc_forward
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key (assigned automatcially for new accounts)
+
+=item srcsvc - svcnum of the source of the forward (see L<FS::svc_acct>)
+
+=item src - literal source (username or full email address)
+
+=item dstsvc - svcnum of the destination of the forward (see L<FS::svc_acct>)
+
+=item dst - literal destination (username or full email address)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new mail forwarding alias. To add the mail forwarding alias to the
+database, see L<"insert">.
+
+=cut
+
+
+sub table_info {
+ {
+ 'name' => 'Forward',
+ 'name_plural' => 'Mail forwards',
+ 'display_weight' => 30,
+ 'cancel_weight' => 30,
+ 'fields' => {
+ 'srcsvc' => 'service from which mail is to be forwarded',
+ 'dstsvc' => 'service to which mail is to be forwarded',
+ 'dst' => 'someone@another.domain.com to use when dstsvc is 0',
+ },
+ };
+}
+
+sub table { 'svc_forward'; }
+
+=item search_sql STRING
+
+Class method which returns an SQL fragment to search for the given string.
+
+=cut
+
+sub search_sql {
+ my( $class, $string ) = @_;
+ $class->search_sql_field('src', $string);
+}
+
+=item label [ END_TIMESTAMP [ START_TIMESTAMP ] ]
+
+Returns a text string representing this forward.
+
+END_TIMESTAMP and START_TIMESTAMP can optionally be passed when dealing with
+history records.
+
+=cut
+
+sub label {
+ my $self = shift;
+ my $tag = '';
+
+ if ( $self->srcsvc ) {
+ my $svc_acct = $self->srcsvc_acct(@_);
+ $tag = $svc_acct->email(@_);
+ } else {
+ $tag = $self->src;
+ }
+
+ $tag .= ' -> ';
+
+ if ( $self->dstsvc ) {
+ my $svc_acct = $self->dstsvc_acct(@_);
+ $tag .= $svc_acct->email(@_);
+ } else {
+ $tag .= $self->dst;
+ }
+
+ $tag;
+}
+
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this mail forwarding alias to the database. If there is an error, returns
+the error, otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+Currently available options are: I<depend_jobnum>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $error = $self->check;
+ return $error if $error;
+
+ $error = $self->SUPER::insert(@_);
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ ''; #no error
+
+}
+
+=item delete
+
+Deletes this mail forwarding alias from the database. If there is an error,
+returns the error, otherwise returns false.
+
+The corresponding FS::cust_svc record will be deleted as well.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::Autocommit = 0;
+ my $dbh = dbh;
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my ( $new, $old ) = ( shift, shift );
+
+ if ( $new->srcsvc != $old->srcsvc
+ && ( $new->dstsvc != $old->dstsvc
+ || ! $new->dstsvc && $new->dst ne $old->dst
+ )
+ ) {
+ return "Can't change both source and destination of a mail forward!"
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $error = $new->SUPER::replace($old, @_);
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item suspend
+
+Just returns false (no error) for now.
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Just returns false (no error) for now.
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Just returns false (no error) for now.
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid mail forwarding alias. If there
+is an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+Sets any fixed values; see L<FS::part_svc>.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $x = $self->setfixed;
+ return $x unless ref($x);
+ #my $part_svc = $x;
+
+ my $error = $self->ut_numbern('svcnum')
+ || $self->ut_numbern('srcsvc')
+ || $self->ut_numbern('dstsvc')
+ ;
+ return $error if $error;
+
+ return "Both srcsvc and src were defined; only one can be specified"
+ if $self->srcsvc && $self->src;
+
+ return "one of srcsvc or src is required"
+ unless $self->srcsvc || $self->src;
+
+ return "Unknown srcsvc: ". $self->srcsvc
+ unless ! $self->srcsvc || $self->srcsvc_acct;
+
+ return "Both dstsvc and dst were defined; only one can be specified"
+ if $self->dstsvc && $self->dst;
+
+ return "one of dstsvc or dst is required"
+ unless $self->dstsvc || $self->dst;
+
+ return "Unknown dstsvc: ". $self->dstsvc
+ unless ! $self->dstsvc || $self->dstsvc_acct;
+ #return "Unknown dstsvc"
+ # unless qsearchs('svc_acct', { 'svcnum' => $self->dstsvc } )
+ # || ! $self->dstsvc;
+
+ if ( $self->src ) {
+ $self->src =~ /^([\w\.\-\&]*)(\@([\w\-]+\.)+\w+)$/
+ or return "Illegal src: ". $self->src;
+ $self->src("$1$2");
+ } else {
+ $self->src('');
+ }
+
+ if ( $self->dst ) {
+ my $conf = new FS::Conf;
+ if ( $conf->exists('svc_forward-arbitrary_dst') ) {
+ my $error = $self->ut_textn('dst');
+ return $error if $error;
+ } else {
+ $self->dst =~ /^([\w\.\-\&]*)(\@([\w\-]+\.)+\w+)$/
+ or return "Illegal dst: ". $self->dst;
+ $self->dst("$1$2");
+ }
+ } else {
+ $self->dst('');
+ }
+
+ $self->SUPER::check;
+}
+
+=item srcsvc_acct
+
+Returns the FS::svc_acct object referenced by the srcsvc column, or false for
+literally specified forwards.
+
+=cut
+
+sub srcsvc_acct {
+ my $self = shift;
+ qsearchs('svc_acct', { 'svcnum' => $self->srcsvc } );
+}
+
+=item dstsvc_acct
+
+Returns the FS::svc_acct object referenced by the srcsvc column, or false for
+literally specified forwards.
+
+=cut
+
+sub dstsvc_acct {
+ my $self = shift;
+ qsearchs('svc_acct', { 'svcnum' => $self->dstsvc } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>,
+L<FS::svc_acct>, L<FS::svc_domain>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_phone.pm b/FS/FS/svc_phone.pm
new file mode 100644
index 0000000..ce767d5
--- /dev/null
+++ b/FS/FS/svc_phone.pm
@@ -0,0 +1,341 @@
+package FS::svc_phone;
+
+use strict;
+use vars qw( @ISA @pw_set $conf );
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs );
+use FS::Msgcat qw(gettext);
+use FS::svc_Common;
+use FS::part_svc;
+
+@ISA = qw( FS::svc_Common );
+
+#avoid l 1 and o O 0
+@pw_set = ( 'a'..'k', 'm','n', 'p-z', 'A'..'N', 'P'..'Z' , '2'..'9' );
+
+#ask FS::UID to run this stuff for us later
+$FS::UID::callback{'FS::svc_acct'} = sub {
+ $conf = new FS::Conf;
+};
+
+=head1 NAME
+
+FS::svc_phone - Object methods for svc_phone records
+
+=head1 SYNOPSIS
+
+ use FS::svc_phone;
+
+ $record = new FS::svc_phone \%hash;
+ $record = new FS::svc_phone { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+An FS::svc_phone object represents a phone number. FS::svc_phone inherits
+from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item svcnum
+
+primary key
+
+=item countrycode
+
+=item phonenum
+
+=item sip_password
+
+=item pin
+
+Voicemail PIN
+
+=item phone_name
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new phone number. To add the number to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+#
+sub table_info {
+ {
+ 'name' => 'Phone number',
+ 'sorts' => 'phonenum',
+ 'display_weight' => 60,
+ 'cancel_weight' => 80,
+ 'fields' => {
+ 'countrycode' => { label => 'Country code',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ },
+ 'phonenum' => 'Phone number',
+ 'pin' => { label => 'Personal Identification Number',
+ type => 'text',
+ disable_inventory => 1,
+ disable_select => 1,
+ },
+ 'sip_password' => 'SIP password',
+ 'name' => 'Name',
+ },
+ };
+}
+
+sub table { 'svc_phone'; }
+
+sub table_dupcheck_fields { ( 'countrycode', 'phonenum' ); }
+
+=item search_sql STRING
+
+Class method which returns an SQL fragment to search for the given string.
+
+=cut
+
+sub search_sql {
+ my( $class, $string ) = @_;
+ $class->search_sql_field('phonenum', $string );
+}
+
+=item label
+
+Returns the phone number.
+
+=cut
+
+sub label {
+ my $self = shift;
+ my $phonenum = $self->phonenum; #XXX format it better
+ my $label = $phonenum;
+ $label .= ' ('.$self->phone_name.')' if $self->phone_name;
+ $label;
+}
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item suspend
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid phone number. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $conf = new FS::Conf;
+
+ my $phonenum = $self->phonenum;
+ my $phonenum_check_method;
+ if ( $conf->exists('svc_phone-allow_alpha_phonenum') ) {
+ $phonenum =~ s/\W//g;
+ $phonenum_check_method = 'ut_alpha';
+ } else {
+ $phonenum =~ s/\D//g;
+ $phonenum_check_method = 'ut_number';
+ }
+ $self->phonenum($phonenum);
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ || $self->ut_numbern('countrycode')
+ || $self->$phonenum_check_method('phonenum')
+ || $self->ut_anything('sip_password')
+ || $self->ut_numbern('pin')
+ || $self->ut_textn('phone_name')
+ ;
+ return $error if $error;
+
+ $self->countrycode(1) unless $self->countrycode;
+
+ unless ( length($self->sip_password) ) {
+
+ $self->sip_password(
+ join('', map $pw_set[ int(rand $#pw_set) ], (0..16) )
+ );
+
+ }
+
+ $self->SUPER::check;
+}
+
+=item _check duplicate
+
+Internal method to check for duplicate phone numers.
+
+=cut
+
+#false laziness w/svc_acct.pm's _check_duplicate.
+sub _check_duplicate {
+ my $self = shift;
+
+ my $global_unique = $conf->config('global_unique-phonenum') || 'none';
+ return '' if $global_unique eq 'disabled';
+
+ $self->lock_table;
+
+ my @dup_ccphonenum =
+ grep { !$self->svcnum || $_->svcnum != $self->svcnum }
+ qsearch( 'svc_phone', {
+ 'countrycode' => $self->countrycode,
+ 'phonenum' => $self->phonenum,
+ });
+
+ return gettext('phonenum_in_use')
+ if $global_unique eq 'countrycode+phonenum' && @dup_ccphonenum;
+
+ my $part_svc = qsearchs('part_svc', { 'svcpart' => $self->svcpart } );
+ unless ( $part_svc ) {
+ return 'unknown svcpart '. $self->svcpart;
+ }
+
+ if ( @dup_ccphonenum ) {
+
+ my $exports = FS::part_export::export_info('svc_phone');
+ my %conflict_ccphonenum_svcpart = ( $self->svcpart => 'SELF', );
+
+ foreach my $part_export ( $part_svc->part_export ) {
+
+ #this will catch to the same exact export
+ my @svcparts = map { $_->svcpart } $part_export->export_svc;
+
+ $conflict_ccphonenum_svcpart{$_} = $part_export->exportnum
+ foreach @svcparts;
+
+ }
+
+ foreach my $dup_ccphonenum ( @dup_ccphonenum ) {
+ my $dup_svcpart = $dup_ccphonenum->cust_svc->svcpart;
+ if ( exists($conflict_ccphonenum_svcpart{$dup_svcpart}) ) {
+ return "duplicate phone number ".
+ $self->countrycode. ' '. $self->phonenum.
+ ": conflicts with svcnum ". $dup_ccphonenum->svcnum.
+ " via exportnum ". $conflict_ccphonenum_svcpart{$dup_svcpart};
+ }
+ }
+
+ }
+
+ return '';
+
+}
+
+=item check_pin
+
+Checks the supplied PIN against the PIN in the database. Returns true for a
+sucessful authentication, false if no match.
+
+=cut
+
+sub check_pin {
+ my($self, $check_pin) = @_;
+ length($self->pin) && $check_pin eq $self->pin;
+}
+
+=item radius_reply
+
+=cut
+
+sub radius_reply {
+ my $self = shift;
+ #XXX Session-Timeout! holy shit, need rlm_perl to ask for this in realtime
+ ();
+}
+
+=item radius_check
+
+=cut
+
+sub radius_check {
+ my $self = shift;
+ my %check = ();
+
+ my $conf = new FS::Conf;
+
+ $check{'User-Password'} = $conf->config('svc_phone-radius-default_password');
+
+ %check;
+}
+
+sub radius_groups {
+ ();
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
+L<FS::cust_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_www.pm b/FS/FS/svc_www.pm
new file mode 100644
index 0000000..53225bb
--- /dev/null
+++ b/FS/FS/svc_www.pm
@@ -0,0 +1,312 @@
+package FS::svc_www;
+
+use strict;
+use vars qw(@ISA $conf $apacheip);
+#use FS::Record qw( qsearch qsearchs );
+use FS::Record qw( qsearchs dbh );
+use FS::svc_Common;
+use FS::cust_svc;
+use FS::domain_record;
+use FS::svc_acct;
+use FS::svc_domain;
+
+@ISA = qw( FS::svc_Common );
+
+#ask FS::UID to run this stuff for us later
+$FS::UID::callback{'FS::svc_www'} = sub {
+ $conf = new FS::Conf;
+ $apacheip = $conf->config('apacheip');
+};
+
+=head1 NAME
+
+FS::svc_www - Object methods for svc_www records
+
+=head1 SYNOPSIS
+
+ use FS::svc_www;
+
+ $record = new FS::svc_www \%hash;
+ $record = new FS::svc_www { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+An FS::svc_www object represents an web virtual host. FS::svc_www inherits
+from FS::svc_Common. The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key
+
+=item recnum - DNS `A' record corresponding to this web virtual host. (see L<FS::domain_record>)
+
+=item usersvc - account (see L<FS::svc_acct>) corresponding to this web virtual host.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new web virtual host. To add the record to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table_info {
+ {
+ 'name' => 'Hosting',
+ 'name_plural' => 'Virtual hosting services',
+ 'display_weight' => 40,
+ 'cancel_weight' => 20,
+ 'fields' => {
+ },
+ };
+};
+
+sub table { 'svc_www'; }
+
+=item label [ END_TIMESTAMP [ START_TIMESTAMP ] ]
+
+Returns the zone name for this virtual host.
+
+END_TIMESTAMP and START_TIMESTAMP can optionally be passed when dealing with
+history records.
+
+=cut
+
+sub label {
+ my $self = shift;
+ $self->domain_record(@_)->zone;
+}
+
+=item insert [ , OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+Currently available options are: I<depend_jobnum>
+
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
+
+
+=cut
+
+sub insert {
+ my $self = shift;
+
+ my $error = $self->check;
+ return $error if $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #if ( $self->recnum =~ /^([\w\-]+|\@)\.(([\w\.\-]+\.)+\w+)$/ ) {
+ if ( $self->recnum =~ /^([\w\-]+|\@)\.(\d+)$/ ) {
+ my( $reczone, $domain_svcnum ) = ( $1, $2 );
+ unless ( $apacheip ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Configuration option apacheip not set; can't autocreate A record";
+ #"for $reczone". $svc_domain->domain;
+ }
+ my $domain_record = new FS::domain_record {
+ 'svcnum' => $domain_svcnum,
+ 'reczone' => $reczone,
+ 'recaf' => 'IN',
+ 'rectype' => 'A',
+ 'recdata' => $apacheip,
+ };
+ $error = $domain_record->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $self->recnum($domain_record->recnum);
+ }
+
+ $error = $self->SUPER::insert(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ my $error;
+
+ $error = $self->SUPER::delete(@_);
+ return $error if $error;
+
+ '';
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my ( $new, $old ) = ( shift, shift );
+ my $error;
+
+ $error = $new->SUPER::replace($old, @_);
+ return $error if $error;
+
+ '';
+}
+
+=item suspend
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid web virtual host. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $x = $self->setfixed;
+ return $x unless ref($x);
+ #my $part_svc = $x;
+
+ my $error =
+ $self->ut_numbern('svcnum')
+# || $self->ut_number('recnum')
+ || $self->ut_numbern('usersvc')
+ || $self->ut_anything('config')
+ ;
+ return $error if $error;
+
+ if ( $self->recnum =~ /^(\d+)$/ ) {
+
+ $self->recnum($1);
+ return "Unknown recnum: ". $self->recnum
+ unless qsearchs('domain_record', { 'recnum' => $self->recnum } );
+
+ } elsif ( $self->recnum =~ /^([\w\-]+|\@)\.(([\w\.\-]+\.)+\w+)$/ ) {
+
+ my( $reczone, $domain ) = ( $1, $2 );
+
+ my $svc_domain = qsearchs( 'svc_domain', { 'domain' => $domain } )
+ or return "unknown domain $domain (recnum $1.$2)";
+
+ my $domain_record = qsearchs( 'domain_record', {
+ 'reczone' => $reczone,
+ 'svcnum' => $svc_domain->svcnum,
+ });
+
+ if ( $domain_record ) {
+ $self->recnum($domain_record->recnum);
+ } else {
+ #insert will create it
+ #$self->recnum("$reczone.$domain");
+ $self->recnum("$reczone.". $svc_domain->svcnum);
+ }
+
+ } else {
+ return "Illegal recnum: ". $self->recnum;
+ }
+
+ if ( $self->usersvc ) {
+ return "Unknown usersvc0 (svc_acct.svcnum): ". $self->usersvc
+ unless qsearchs('svc_acct', { 'svcnum' => $self->usersvc } );
+ }
+
+ $self->SUPER::check;
+
+}
+
+=item domain_record
+
+Returns the FS::domain_record record for this web virtual host's zone (see
+L<FS::domain_record>).
+
+=cut
+
+sub domain_record {
+ my $self = shift;
+ qsearchs('domain_record', { 'recnum' => $self->recnum } );
+}
+
+=item svc_acct
+
+Returns the FS::svc_acct record for this web virtual host's owner (see
+L<FS::svc_acct>).
+
+=cut
+
+sub svc_acct {
+ my $self = shift;
+ qsearchs('svc_acct', { 'svcnum' => $self->usersvc } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_Common>, L<FS::Record>, L<FS::domain_record>, L<FS::cust_svc>,
+L<FS::part_svc>, L<FS::cust_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/tax_class.pm b/FS/FS/tax_class.pm
new file mode 100644
index 0000000..480fa10
--- /dev/null
+++ b/FS/FS/tax_class.pm
@@ -0,0 +1,392 @@
+package FS::tax_class;
+
+use strict;
+use vars qw( @ISA );
+use FS::UID qw(dbh);
+use FS::Record qw( qsearch qsearchs );
+use FS::Misc qw( csv_from_fixed );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::tax_class - Object methods for tax_class records
+
+=head1 SYNOPSIS
+
+ use FS::tax_class;
+
+ $record = new FS::tax_class \%hash;
+ $record = new FS::tax_class { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::tax_class object represents a tax class. FS::tax_class
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item taxclassnum
+
+Primary key
+
+=item data_vendor
+
+Vendor of the tax data
+
+=item taxclass
+
+Tax class
+
+=item description
+
+Human readable description of the tax class
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new tax class. To add the tax class to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'tax_class'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+
+ return "Can't delete a tax class which has tax rates!"
+ if qsearch( 'tax_rate', { 'taxclassnum' => $self->taxclassnum } );
+
+ return "Can't delete a tax class which has package tax rates!"
+ if qsearch( 'part_pkg_taxrate', { 'taxclassnum' => $self->taxclassnum } );
+
+ return "Can't delete a tax class which has package tax rates!"
+ if qsearch( 'part_pkg_taxrate', { 'taxclassnumtaxed' => $self->taxclassnum } );
+
+ return "Can't delete a tax class which has package tax overrides!"
+ if qsearch( 'part_pkg_taxoverride', { 'taxclassnum' => $self->taxclassnum } );
+
+ $self->SUPER::delete(@_);
+
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid tax class. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('taxclassnum')
+ || $self->ut_text('taxclass')
+ || $self->ut_textn('data_vendor')
+ || $self->ut_textn('description')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item batch_import
+
+Loads part_pkg_taxrate records from an external CSV file. If there is
+an error, returns the error, otherwise returns false.
+
+=cut
+
+sub batch_import {
+ my ($param, $job) = @_;
+
+ my $fh = $param->{filehandle};
+ my $format = $param->{'format'};
+
+ my @fields;
+ my $hook;
+ my $endhook;
+ my $data = {};
+ my $imported = 0;
+ my $dbh = dbh;
+
+ my @column_lengths = ();
+ my @column_callbacks = ();
+ if ( $format eq 'cch-fixed' || $format eq 'cch-fixed-update' ) {
+ $format =~ s/-fixed//;
+ push @column_lengths, qw( 8 10 3 2 2 10 100 );
+ push @column_lengths, 1 if $format eq 'cch-update';
+ }
+
+ my $line;
+ my ( $count, $last, $min_sec ) = (0, time, 5); #progressbar
+ if ( $job || scalar(@column_lengths) ) {
+ my $error = csv_from_fixed(\$fh, \$count, \@column_lengths);
+ return $error if $error;
+ }
+
+ if ( $format eq 'cch' || $format eq 'cch-update' ) {
+ @fields = qw( table name pos length number value description );
+ push @fields, 'actionflag' if $format eq 'cch-update';
+
+ $hook = sub {
+ my $hash = shift;
+
+ if ($hash->{'table'} eq 'DETAIL') {
+ push @{$data->{'taxcat'}}, [ $hash->{'value'}, $hash->{'description'} ]
+ if ($hash->{'name'} eq 'TAXCAT' &&
+ (!exists($hash->{actionflag}) || $hash->{actionflag} eq 'I') );
+
+ push @{$data->{'taxtype'}}, [ $hash->{'value'}, $hash->{'description'} ]
+ if ($hash->{'name'} eq 'TAXTYPE' &&
+ (!exists($hash->{actionflag}) || $hash->{actionflag} eq 'I') );
+
+ if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
+ my $name = $hash->{'name'};
+ my $value = $hash->{'value'};
+ return "Bad value for $name: $value"
+ unless $value =~ /^\d+$/;
+
+ if ($name eq 'TAXCAT' || $name eq 'TAXTYPE') {
+ my @tax_class = qsearch( 'tax_class',
+ { 'data_vendor' => 'cch' },
+ '',
+ "AND taxclass LIKE '".
+ ($name eq 'TAXTYPE' ? $value : '%').":".
+ ($name eq 'TAXCAT' ? $value : '%')."'",
+ );
+ foreach (@tax_class) {
+ my $error = $_->delete;
+ return $error if $error;
+ }
+ }
+ }
+
+ }
+
+ delete($hash->{$_})
+ for qw( data_vendor table name pos length number value description );
+ delete($hash->{actionflag}) if exists($hash->{actionflag});
+
+ '';
+
+ };
+
+ $endhook = sub {
+
+ my $sql = "SELECT DISTINCT ".
+ "substring(taxclass from 1 for position(':' in taxclass)-1),".
+ "substring(description from 1 for position(':' in description)-1) ".
+ "FROM tax_class WHERE data_vendor='cch'";
+
+ my $sth = $dbh->prepare($sql) or die $dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my @old_types = @{$sth->fetchall_arrayref};
+
+ $sql = "SELECT DISTINCT ".
+ "substring(taxclass from position(':' in taxclass)+1),".
+ "substring(description from position(':' in description)+1) ".
+ "FROM tax_class WHERE data_vendor='cch'";
+
+ $sth = $dbh->prepare($sql) or die $dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my @old_cats = @{$sth->fetchall_arrayref};
+
+ my $catcount = exists($data->{'taxcat'}) ? scalar(@{$data->{'taxcat'}})
+ : 0;
+ my $typecount = exists($data->{'taxtype'}) ? scalar(@{$data->{'taxtype'}})
+ : 0;
+
+ my $count = scalar(@old_types) * $catcount
+ + $typecount * (scalar(@old_cats) + $catcount);
+
+ $imported = 1 if $format eq 'cch-update'; #empty file ok
+
+ foreach my $type (@old_types) {
+ foreach my $cat (@{$data->{'taxcat'}}) {
+
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my $tax_class =
+ new FS::tax_class( { 'data_vendor' => 'cch',
+ 'taxclass' => $type->[0].':'.$cat->[0],
+ 'description' => $type->[1].':'.$cat->[1],
+ } );
+ my $error = $tax_class->insert;
+ return $error if $error;
+ $imported++;
+ }
+ }
+
+ foreach my $type (@{$data->{'taxtype'}}) {
+ foreach my $cat (@old_cats, @{$data->{'taxcat'}}) {
+
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my $tax_class =
+ new FS::tax_class( { 'data_vendor' => 'cch',
+ 'taxclass' => $type->[0].':'.$cat->[0],
+ 'description' => $type->[1].':'.$cat->[1],
+ } );
+ my $error = $tax_class->insert;
+ return $error if $error;
+ $imported++;
+ }
+ }
+
+ '';
+ };
+
+ } elsif ( $format eq 'extended' ) {
+ die "unimplemented\n";
+ @fields = qw( );
+ $hook = sub {};
+ } else {
+ die "unknown format $format";
+ }
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+
+ my $csv = new Text::CSV_XS;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+
+ while ( defined($line=<$fh>) ) {
+
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+
+ my @columns = $csv->fields();
+
+ my %tax_class = ( 'data_vendor' => $format );
+ foreach my $field ( @fields ) {
+ $tax_class{$field} = shift @columns;
+ }
+ if ( scalar( @columns ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unexpected trailing columns in line (wrong format?): $line";
+ }
+
+ my $error = &{$hook}(\%tax_class);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ next unless scalar(keys %tax_class);
+
+ my $tax_class = new FS::tax_class( \%tax_class );
+ $error = $tax_class->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert tax_class for $line: $error";
+ }
+
+ $imported++;
+ }
+
+ my $error = &{$endhook}();
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert tax_class for $line: $error";
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return "Empty File!" unless ($imported || $format eq 'cch-update');
+
+ ''; #no error
+
+}
+
+=back
+
+=head1 BUGS
+
+ batch_import does not handle mixed I and D records in the same file for
+ format cch-update
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
+
diff --git a/FS/FS/tax_rate.pm b/FS/FS/tax_rate.pm
new file mode 100644
index 0000000..0d9156b
--- /dev/null
+++ b/FS/FS/tax_rate.pm
@@ -0,0 +1,1080 @@
+package FS::tax_rate;
+
+use strict;
+use vars qw( @ISA $DEBUG $me
+ %tax_unittypes %tax_maxtypes %tax_basetypes %tax_authorities
+ %tax_passtypes );
+use Date::Parse;
+use Storable qw( thaw );
+use MIME::Base64;
+use FS::Record qw( qsearch qsearchs dbh );
+use FS::tax_class;
+use FS::cust_bill_pkg;
+use FS::cust_tax_location;
+use FS::part_pkg_taxrate;
+use FS::cust_main;
+use FS::Misc qw( csv_from_fixed );
+
+@ISA = qw( FS::Record );
+
+$DEBUG = 0;
+$me = '[FS::tax_rate]';
+
+=head1 NAME
+
+FS::tax_rate - Object methods for tax_rate objects
+
+=head1 SYNOPSIS
+
+ use FS::tax_rate;
+
+ $record = new FS::tax_rate \%hash;
+ $record = new FS::tax_rate { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::tax_rate object represents a tax rate, defined by locale.
+FS::tax_rate inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item taxnum
+
+primary key (assigned automatically for new tax rates)
+
+=item geocode
+
+a geographic location code provided by a tax data vendor
+
+=item data_vendor
+
+the tax data vendor
+
+=item location
+
+a location code provided by a tax authority
+
+=item taxclassnum
+
+a foreign key into FS::tax_class - the type of tax
+referenced but FS::part_pkg_taxrate
+eitem effective_date
+
+the time after which the tax applies
+
+=item tax
+
+percentage
+
+=item excessrate
+
+second bracket percentage
+
+=item taxbase
+
+the amount to which the tax applies (first bracket)
+
+=item taxmax
+
+a cap on the amount of tax if a cap exists
+
+=item usetax
+
+percentage on out of jurisdiction purchases
+
+=item useexcessrate
+
+second bracket percentage on out of jurisdiction purchases
+
+=item unittype
+
+one of the values in %tax_unittypes
+
+=item fee
+
+amount of tax per unit
+
+=item excessfee
+
+second bracket amount of tax per unit
+
+=item feebase
+
+the number of units to which the fee applies (first bracket)
+
+=item feemax
+
+the most units to which fees apply (first and second brackets)
+
+=item maxtype
+
+a value from %tax_maxtypes indicating how brackets accumulate (i.e. monthly, per invoice, etc)
+
+=item taxname
+
+if defined, printed on invoices instead of "Tax"
+
+=item taxauth
+
+a value from %tax_authorities
+
+=item basetype
+
+a value from %tax_basetypes indicating the tax basis
+
+=item passtype
+
+a value from %tax_passtypes indicating how the tax should displayed to the customer
+
+=item passflag
+
+'Y', 'N', or blank indicating the tax can be passed to the customer
+
+=item setuptax
+
+if 'Y', this tax does not apply to setup fees
+
+=item recurtax
+
+if 'Y', this tax does not apply to recurring fees
+
+=item manual
+
+if 'Y', has been manually edited
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new tax rate. To add the tax rate to the database, see L<"insert">.
+
+=cut
+
+sub table { 'tax_rate'; }
+
+=item insert
+
+Adds this tax rate to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this tax rate from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid tax rate. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ foreach (qw( taxbase taxmax )) {
+ $self->$_(0) unless $self->$_;
+ }
+
+ $self->ut_numbern('taxnum')
+ || $self->ut_text('geocode')
+ || $self->ut_textn('data_vendor')
+ || $self->ut_textn('location')
+ || $self->ut_foreign_key('taxclassnum', 'tax_class', 'taxclassnum')
+ || $self->ut_snumbern('effective_date')
+ || $self->ut_float('tax')
+ || $self->ut_floatn('excessrate')
+ || $self->ut_money('taxbase')
+ || $self->ut_money('taxmax')
+ || $self->ut_floatn('usetax')
+ || $self->ut_floatn('useexcessrate')
+ || $self->ut_numbern('unittype')
+ || $self->ut_floatn('fee')
+ || $self->ut_floatn('excessfee')
+ || $self->ut_floatn('feemax')
+ || $self->ut_numbern('maxtype')
+ || $self->ut_textn('taxname')
+ || $self->ut_numbern('taxauth')
+ || $self->ut_numbern('basetype')
+ || $self->ut_numbern('passtype')
+ || $self->ut_enum('passflag', [ '', 'Y', 'N' ])
+ || $self->ut_enum('setuptax', [ '', 'Y' ] )
+ || $self->ut_enum('recurtax', [ '', 'Y' ] )
+ || $self->ut_enum('manual', [ '', 'Y' ] )
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ || $self->SUPER::check
+ ;
+
+}
+
+=item taxclass_description
+
+Returns the human understandable value associated with the related
+FS::tax_class.
+
+=cut
+
+sub taxclass_description {
+ my $self = shift;
+ my $tax_class = qsearchs('tax_class', {'taxclassnum' => $self->taxclassnum });
+ $tax_class ? $tax_class->description : '';
+}
+
+=item unittype_name
+
+Returns the human understandable value associated with the unittype column
+
+=cut
+
+%tax_unittypes = ( '0' => 'access line',
+ '1' => 'minute',
+ '2' => 'account',
+);
+
+sub unittype_name {
+ my $self = shift;
+ $tax_unittypes{$self->unittype};
+}
+
+=item maxtype_name
+
+Returns the human understandable value associated with the maxtype column
+
+=cut
+
+%tax_maxtypes = ( '0' => 'receipts per invoice',
+ '1' => 'receipts per item',
+ '2' => 'total utility charges per utility tax year',
+ '3' => 'total charges per utility tax year',
+ '4' => 'receipts per access line',
+ '9' => 'monthly receipts per location',
+);
+
+sub maxtype_name {
+ my $self = shift;
+ $tax_maxtypes{$self->maxtype};
+}
+
+=item basetype_name
+
+Returns the human understandable value associated with the basetype column
+
+=cut
+
+%tax_basetypes = ( '0' => 'sale price',
+ '1' => 'gross receipts',
+ '2' => 'sales taxable telecom revenue',
+ '3' => 'minutes carried',
+ '4' => 'minutes billed',
+ '5' => 'gross operating revenue',
+ '6' => 'access line',
+ '7' => 'account',
+ '8' => 'gross revenue',
+ '9' => 'portion gross receipts attributable to interstate service',
+ '10' => 'access line',
+ '11' => 'gross profits',
+ '12' => 'tariff rate',
+ '14' => 'account',
+ '15' => 'prior year gross receipts',
+);
+
+sub basetype_name {
+ my $self = shift;
+ $tax_basetypes{$self->basetype};
+}
+
+=item taxauth_name
+
+Returns the human understandable value associated with the taxauth column
+
+=cut
+
+%tax_authorities = ( '0' => 'federal',
+ '1' => 'state',
+ '2' => 'county',
+ '3' => 'city',
+ '4' => 'local',
+ '5' => 'county administered by state',
+ '6' => 'city administered by state',
+ '7' => 'city administered by county',
+ '8' => 'local administered by state',
+ '9' => 'local administered by county',
+);
+
+sub taxauth_name {
+ my $self = shift;
+ $tax_authorities{$self->taxauth};
+}
+
+=item passtype_name
+
+Returns the human understandable value associated with the passtype column
+
+=cut
+
+%tax_passtypes = ( '0' => 'separate tax line',
+ '1' => 'separate surcharge line',
+ '2' => 'surcharge not separated',
+ '3' => 'included in base rate',
+);
+
+sub passtype_name {
+ my $self = shift;
+ $tax_passtypes{$self->passtype};
+}
+
+=item taxline TAXABLES, [ OPTIONSHASH ]
+
+Returns a listref of a name and an amount of tax calculated for the list
+of packages/amounts referenced by TAXABLES. If an error occurs, a message
+is returned as a scalar.
+
+=cut
+
+sub taxline {
+ my $self = shift;
+
+ my $taxables;
+ my %opt = ();
+
+ if (ref($_[0]) eq 'ARRAY') {
+ $taxables = shift;
+ %opt = @_;
+ }else{
+ $taxables = [ @_ ];
+ #exemptions would be broken in this case
+ }
+
+ my $name = $self->taxname;
+ $name = 'Other surcharges'
+ if ($self->passtype == 2);
+ my $amount = 0;
+
+ if ( $self->disabled ) { # we always know how to handle disabled taxes
+ return {
+ 'name' => $name,
+ 'amount' => $amount,
+ };
+ }
+
+ my $taxable_charged = 0;
+ my @cust_bill_pkg = grep { $taxable_charged += $_ unless ref; ref; }
+ @$taxables;
+
+ warn "calculating taxes for ". $self->taxnum. " on ".
+ join (",", map { $_->pkgnum } @cust_bill_pkg)
+ if $DEBUG;
+
+ if ($self->passflag eq 'N') {
+ # return "fatal: can't (yet) handle taxes not passed to the customer";
+ # until someone needs to track these in freeside
+ return {
+ 'name' => $name,
+ 'amount' => 0,
+ };
+ }
+
+ if ($self->maxtype != 0 && $self->maxtype != 9) {
+ return $self->_fatal_or_null( 'tax with "'.
+ $self->maxtype_name. '" threshold'
+ );
+ }
+
+ if ($self->maxtype == 9) {
+ return
+ $self->_fatal_or_null( 'tax with "'. $self->maxtype_name. '" threshold' );
+ # "texas" tax
+ }
+
+ # we treat gross revenue as gross receipts and expect the tax data
+ # to DTRT (i.e. tax on tax rules)
+ if ($self->basetype != 0 && $self->basetype != 1 &&
+ $self->basetype != 5 && $self->basetype != 6 &&
+ $self->basetype != 7 && $self->basetype != 8 &&
+ $self->basetype != 14
+ ) {
+ return
+ $self->_fatal_or_null( 'tax with "'. $self->basetype_name. '" basis' );
+ }
+
+ unless ($self->setuptax =~ /^Y$/i) {
+ $taxable_charged += $_->setup foreach @cust_bill_pkg;
+ }
+ unless ($self->recurtax =~ /^Y$/i) {
+ $taxable_charged += $_->recur foreach @cust_bill_pkg;
+ }
+
+ my $taxable_units = 0;
+ unless ($self->recurtax =~ /^Y$/i) {
+ if ($self->unittype == 0) {
+ my %seen = ();
+ foreach (@cust_bill_pkg) {
+ $taxable_units += $_->units
+ unless $seen{$_->pkgnum};
+ $seen{$_->pkgnum}++;
+ }
+ }elsif ($self->unittype == 1) {
+ return $self->_fatal_or_null( 'fee with minute unit type' );
+ }elsif ($self->unittype == 2) {
+ $taxable_units = 1;
+ }else {
+ return $self->_fatal_or_null( 'unknown unit type in tax'. $self->taxnum );
+ }
+ }
+
+ #
+ # XXX insert exemption handling here
+ #
+ # the tax or fee is applied to taxbase or feebase and then
+ # the excessrate or excess fee is applied to taxmax or feemax
+ #
+
+ $amount += $taxable_charged * $self->tax;
+ $amount += $taxable_units * $self->fee;
+
+ warn "calculated taxes as [ $name, $amount ]\n"
+ if $DEBUG;
+
+ return {
+ 'name' => $name,
+ 'amount' => $amount,
+ };
+
+}
+
+sub _fatal_or_null {
+ my ($self, $error) = @_;
+
+ my $conf = new FS::Conf;
+
+ $error = "fatal: can't yet handle ". $error;
+ my $name = $self->taxname;
+ $name = 'Other surcharges'
+ if ($self->passtype == 2);
+
+ if ($conf->exists('ignore_incalculable_taxes')) {
+ warn $error;
+ return { name => $name, amount => 0 };
+ } else {
+ return $error;
+ }
+}
+
+=item tax_on_tax CUST_MAIN
+
+Returns a list of taxes which are candidates for taxing taxes for the
+given customer (see L<FS::cust_main>)
+
+=cut
+
+sub tax_on_tax {
+ my $self = shift;
+ my $cust_main = shift;
+
+ warn "looking up taxes on tax ". $self->taxnum. " for customer ".
+ $cust_main->custnum
+ if $DEBUG;
+
+ my $geocode = $cust_main->geocode($self->data_vendor);
+
+ # CCH oddness in m2m
+ my $dbh = dbh;
+ my $extra_sql = ' AND ('.
+ join(' OR ', map{ 'geocode = '. $dbh->quote(substr($geocode, 0, $_)) }
+ qw(10 5 2)
+ ).
+ ')';
+
+ my $order_by = 'ORDER BY taxclassnum, length(geocode) desc';
+ my $select = 'DISTINCT ON(taxclassnum) *';
+
+ # should qsearch preface columns with the table to facilitate joins?
+ my @taxclassnums = map { $_->taxclassnum }
+ qsearch( { 'table' => 'part_pkg_taxrate',
+ 'select' => $select,
+ 'hashref' => { 'data_vendor' => $self->data_vendor,
+ 'taxclassnumtaxed' => $self->taxclassnum,
+ },
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $order_by,
+ } );
+
+ return () unless @taxclassnums;
+
+ $extra_sql =
+ "AND (". join(' OR ', map { "taxclassnum = $_" } @taxclassnums ). ")";
+
+ qsearch({ 'table' => 'tax_rate',
+ 'hashref' => { 'geocode' => $geocode, },
+ 'extra_sql' => $extra_sql,
+ })
+
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item batch_import
+
+=cut
+
+sub batch_import {
+ my ($param, $job) = @_;
+
+ my $fh = $param->{filehandle};
+ my $format = $param->{'format'};
+
+ my %insert = ();
+ my %delete = ();
+
+ my @fields;
+ my $hook;
+
+ my @column_lengths = ();
+ my @column_callbacks = ();
+ if ( $format eq 'cch-fixed' || $format eq 'cch-fixed-update' ) {
+ $format =~ s/-fixed//;
+ my $date_format = sub { my $r='';
+ /^(\d{4})(\d{2})(\d{2})$/ && ($r="$1/$2/$3");
+ $r;
+ };
+ my $trim = sub { my $r = shift; $r =~ s/^\s*//; $r =~ s/\s*$//; $r };
+ push @column_lengths, qw( 10 1 1 8 8 5 8 8 8 1 2 2 30 8 8 10 2 8 2 1 2 2 );
+ push @column_lengths, 1 if $format eq 'cch-update';
+ push @column_callbacks, $trim foreach (@column_lengths); # 5, 6, 15, 17 esp
+ $column_callbacks[8] = $date_format;
+ }
+
+ my $line;
+ my ( $count, $last, $min_sec ) = (0, time, 5); #progressbar
+ if ( $job || scalar(@column_callbacks) ) {
+ my $error =
+ csv_from_fixed(\$fh, \$count, \@column_lengths, \@column_callbacks);
+ return $error if $error;
+ }
+ $count *=2;
+
+ if ( $format eq 'cch' || $format eq 'cch-update' ) {
+ @fields = qw( geocode inoutcity inoutlocal tax location taxbase taxmax
+ excessrate effective_date taxauth taxtype taxcat taxname
+ usetax useexcessrate fee unittype feemax maxtype passflag
+ passtype basetype );
+ push @fields, 'actionflag' if $format eq 'cch-update';
+
+ $hook = sub {
+ my $hash = shift;
+
+ $hash->{'actionflag'} ='I' if ($hash->{'data_vendor'} eq 'cch');
+ $hash->{'data_vendor'} ='cch';
+ $hash->{'effective_date'} = str2time($hash->{'effective_date'});
+
+ my $taxclassid =
+ join(':', map{ $hash->{$_} } qw(taxtype taxcat) );
+
+ my %tax_class = ( 'data_vendor' => 'cch',
+ 'taxclass' => $taxclassid,
+ );
+
+ my $tax_class = qsearchs( 'tax_class', \%tax_class );
+ return "Error updating tax rate: no tax class $taxclassid"
+ unless $tax_class;
+
+ $hash->{'taxclassnum'} = $tax_class->taxclassnum;
+
+ foreach (qw( inoutcity inoutlocal taxtype taxcat )) {
+ delete($hash->{$_});
+ }
+
+ my %passflagmap = ( '0' => '',
+ '1' => 'Y',
+ '2' => 'N',
+ );
+ $hash->{'passflag'} = $passflagmap{$hash->{'passflag'}}
+ if exists $passflagmap{$hash->{'passflag'}};
+
+ foreach (keys %$hash) {
+ $hash->{$_} = substr($hash->{$_}, 0, 80)
+ if length($hash->{$_}) > 80;
+ }
+
+ my $actionflag = delete($hash->{'actionflag'});
+
+ $hash->{'taxname'} =~ s/`/'/g;
+ $hash->{'taxname'} =~ s|\\|/|g;
+
+ return '' if $format eq 'cch'; # but not cch-update
+
+ if ($actionflag eq 'I') {
+ $insert{ $hash->{'geocode'}. ':'. $hash->{'taxclassnum'} } = { %$hash };
+ }elsif ($actionflag eq 'D') {
+ $delete{ $hash->{'geocode'}. ':'. $hash->{'taxclassnum'} } = { %$hash };
+ }else{
+ return "Unexpected action flag: ". $hash->{'actionflag'};
+ }
+
+ delete($hash->{$_}) for keys %$hash;
+
+ '';
+
+ };
+
+ } elsif ( $format eq 'extended' ) {
+ die "unimplemented\n";
+ @fields = qw( );
+ $hook = sub {};
+ } else {
+ die "unknown format $format";
+ }
+
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+
+ my $csv = new Text::CSV_XS;
+
+ my $imported = 0;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ while ( defined($line=<$fh>) ) {
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my @columns = $csv->fields();
+
+ my %tax_rate = ( 'data_vendor' => $format );
+ foreach my $field ( @fields ) {
+ $tax_rate{$field} = shift @columns;
+ }
+ if ( scalar( @columns ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Unexpected trailing columns in line (wrong format?): $line";
+ }
+
+ my $error = &{$hook}(\%tax_rate);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if (scalar(keys %tax_rate)) { #inserts only, not updates for cch
+
+ my $tax_rate = new FS::tax_rate( \%tax_rate );
+ $error = $tax_rate->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't insert tax_rate for $line: $error";
+ }
+
+ }
+
+ $imported++;
+
+ }
+
+ for (grep { !exists($delete{$_}) } keys %insert) {
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my $tax_rate = new FS::tax_rate( $insert{$_} );
+ my $error = $tax_rate->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ my $hashref = $insert{$_};
+ $line = join(", ", map { "$_ => ". $hashref->{$_} } keys(%$hashref) );
+ return "can't insert tax_rate for $line: $error";
+ }
+
+ $imported++;
+ }
+
+ for (grep { exists($delete{$_}) } keys %insert) {
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my $old = qsearchs( 'tax_rate', $delete{$_} );
+ unless ($old) {
+ $dbh->rollback if $oldAutoCommit;
+ $old = $delete{$_};
+ return "can't find tax_rate to replace for: ".
+ #join(" ", map { "$_ => ". $old->{$_} } @fields);
+ join(" ", map { "$_ => ". $old->{$_} } keys(%$old) );
+ }
+ my $new = new FS::tax_rate({ $old->hash, %{$insert{$_}}, 'manual' => '' });
+ $new->taxnum($old->taxnum);
+ my $error = $new->replace($old);
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ my $hashref = $insert{$_};
+ $line = join(", ", map { "$_ => ". $hashref->{$_} } keys(%$hashref) );
+ return "can't replace tax_rate for $line: $error";
+ }
+
+ $imported++;
+ $imported++;
+ }
+
+ for (grep { !exists($insert{$_}) } keys %delete) {
+ if ( $job ) { # progress bar
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $imported / $count )
+ );
+ die $error if $error;
+ $last = time;
+ }
+ }
+
+ my $tax_rate = qsearchs( 'tax_rate', $delete{$_} );
+ unless ($tax_rate) {
+ $dbh->rollback if $oldAutoCommit;
+ $tax_rate = $delete{$_};
+ return "can't find tax_rate to delete for: ".
+ #join(" ", map { "$_ => ". $tax_rate->{$_} } @fields);
+ join(" ", map { "$_ => ". $tax_rate->{$_} } keys(%$tax_rate) );
+ }
+ my $error = $tax_rate->delete;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ my $hashref = $delete{$_};
+ $line = join(", ", map { "$_ => ". $hashref->{$_} } keys(%$hashref) );
+ return "can't delete tax_rate for $line: $error";
+ }
+
+ $imported++;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ return "Empty file!" unless ($imported || $format eq 'cch-update');
+
+ ''; #no error
+
+}
+
+=item process_batch_import
+
+Load a batch import as a queued JSRPC job
+
+=cut
+
+sub process_batch_import {
+ my $job = shift;
+
+ my $param = thaw(decode_base64(shift));
+ my $format = $param->{'format'}; #well... this is all cch specific
+
+ my $files = $param->{'uploaded_files'}
+ or die "No files provided.";
+
+ my (%files) = map { /^(\w+):([\.\w]+)$/ ? ($1,$2):() } split /,/, $files;
+
+ if ($format eq 'cch' || $format eq 'cch-fixed') {
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+ my $error = '';
+ my $have_location = 0;
+
+ my @list = ( 'CODE', 'codefile', \&FS::tax_class::batch_import,
+ 'PLUS4', 'plus4file', \&FS::cust_tax_location::batch_import,
+ 'ZIP', 'zipfile', \&FS::cust_tax_location::batch_import,
+ 'TXMATRIX', 'txmatrix', \&FS::part_pkg_taxrate::batch_import,
+ 'DETAIL', 'detail', \&FS::tax_rate::batch_import,
+ );
+ while( scalar(@list) ) {
+ my ($name, $file, $import_sub) = (shift @list, shift @list, shift @list);
+ unless ($files{$file}) {
+ next if $name eq 'PLUS4';
+ $error = "No $name supplied";
+ $error = "Neither PLUS4 nor ZIP supplied"
+ if ($name eq 'ZIP' && !$have_location);
+ next;
+ }
+ $have_location = 1 if $name eq 'PLUS4';
+ my $fmt = $format. ( $name eq 'ZIP' ? '-zip' : '' );
+ my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc;
+ my $filename = "$dir/". $files{$file};
+ open my $fh, "< $filename" or $error ||= "Can't open $name file: $!";
+
+ $error ||= &{$import_sub}({ 'filehandle' => $fh, 'format' => $fmt }, $job);
+ close $fh;
+ unlink $filename or warn "Can't delete $filename: $!";
+ }
+
+ if ($error) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ die $error;
+ }else{
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ }
+
+ }elsif ($format eq 'cch-update' || $format eq 'cch-fixed-update') {
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+ my $error = '';
+ my @insert_list = ();
+ my @delete_list = ();
+
+ my @list = ( 'CODE', 'codefile', \&FS::tax_class::batch_import,
+ 'PLUS4', 'plus4file', \&FS::cust_tax_location::batch_import,
+ 'ZIP', 'zipfile', \&FS::cust_tax_location::batch_import,
+ 'TXMATRIX', 'txmatrix', \&FS::part_pkg_taxrate::batch_import,
+ );
+ my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc;
+ while( scalar(@list) ) {
+ my ($name, $file, $import_sub) = (shift @list, shift @list, shift @list);
+ unless ($files{$file}) {
+ my $vendor = $name eq 'ZIP' ? 'cch' : 'cch-zip';
+ next # update expected only for previously installed location data
+ if ( ($name eq 'PLUS4' || $name eq 'ZIP')
+ && !scalar( qsearch( { table => 'cust_tax_location',
+ hashref => { data_vendor => $vendor },
+ select => 'DISTINCT data_vendor',
+ } )
+ )
+ );
+
+ $error = "No $name supplied";
+ next;
+ }
+ my $filename = "$dir/". $files{$file};
+ open my $fh, "< $filename" or $error ||= "Can't open $name file $filename: $!";
+ unlink $filename or warn "Can't delete $filename: $!";
+
+ my $ifh = new File::Temp( TEMPLATE => "$name.insert.XXXXXXXX",
+ DIR => $dir,
+ UNLINK => 0, #meh
+ ) or die "can't open temp file: $!\n";
+
+ my $dfh = new File::Temp( TEMPLATE => "$name.delete.XXXXXXXX",
+ DIR => $dir,
+ UNLINK => 0, #meh
+ ) or die "can't open temp file: $!\n";
+
+ my $insert_pattern = ($format eq 'cch-update') ? qr/"I"\s*$/ : qr/I\s*$/;
+ my $delete_pattern = ($format eq 'cch-update') ? qr/"D"\s*$/ : qr/D\s*$/;
+ while(<$fh>) {
+ my $handle = '';
+ $handle = $ifh if $_ =~ /$insert_pattern/;
+ $handle = $dfh if $_ =~ /$delete_pattern/;
+ unless ($handle) {
+ $error = "bad input line: $_" unless $handle;
+ last;
+ }
+ print $handle $_;
+ }
+ close $fh;
+ close $ifh;
+ close $dfh;
+
+ push @insert_list, $name, $ifh->filename, $import_sub;
+ unshift @delete_list, $name, $dfh->filename, $import_sub;
+
+ }
+ while( scalar(@insert_list) ) {
+ my ($name, $file, $import_sub) =
+ (shift @insert_list, shift @insert_list, shift @insert_list);
+
+ my $fmt = $format. ( $name eq 'ZIP' ? '-zip' : '' );
+ open my $fh, "< $file" or $error ||= "Can't open $name file $file: $!";
+ $error ||=
+ &{$import_sub}({ 'filehandle' => $fh, 'format' => $fmt }, $job);
+ close $fh;
+ unlink $file or warn "Can't delete $file: $!";
+ }
+
+ $error ||= "No DETAIL supplied"
+ unless ($files{detail});
+ open my $fh, "< $dir/". $files{detail}
+ or $error ||= "Can't open DETAIL file: $!";
+ $error ||=
+ &FS::tax_rate::batch_import({ 'filehandle' => $fh, 'format' => $format },
+ $job);
+ close $fh;
+ unlink "$dir/". $files{detail} or warn "Can't delete $files{detail}: $!"
+ if $files{detail};
+
+ while( scalar(@delete_list) ) {
+ my ($name, $file, $import_sub) =
+ (shift @delete_list, shift @delete_list, shift @delete_list);
+
+ my $fmt = $format. ( $name eq 'ZIP' ? '-zip' : '' );
+ open my $fh, "< $file" or $error ||= "Can't open $name file $file: $!";
+ $error ||=
+ &{$import_sub}({ 'filehandle' => $fh, 'format' => $fmt }, $job);
+ close $fh;
+ unlink $file or warn "Can't delete $file: $!";
+ }
+
+ if ($error) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ die $error;
+ }else{
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ }
+
+ }else{
+ die "Unknown format: $format";
+ }
+
+}
+
+=item browse_queries PARAMS
+
+Returns a list consisting of a hashref suited for use as the argument
+to qsearch, and sql query string. Each is based on the PARAMS hashref
+of keys and values which frequently would be passed as C<scalar($cgi->Vars)>
+from a form. This conveniently creates the query hashref and count_query
+string required by the browse and search elements. As a side effect,
+the PARAMS hashref is untainted and keys with unexpected values are removed.
+
+=cut
+
+sub browse_queries {
+ my $params = shift;
+
+ my $query = {
+ 'table' => 'tax_rate',
+ 'hashref' => {},
+ 'order_by' => 'ORDER BY geocode, taxclassnum',
+ },
+
+ my $extra_sql = '';
+
+ if ( $params->{data_vendor} =~ /^(\w+)$/ ) {
+ $extra_sql .= ' WHERE data_vendor = '. dbh->quote($1);
+ } else {
+ delete $params->{data_vendor};
+ }
+
+ if ( $params->{geocode} =~ /^(\w+)$/ ) {
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ 'geocode LIKE '. dbh->quote($1.'%');
+ } else {
+ delete $params->{geocode};
+ }
+
+ if ( $params->{taxclassnum} =~ /^(\d+)$/ &&
+ qsearchs( 'tax_class', {'taxclassnum' => $1} )
+ )
+ {
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ ' taxclassnum = '. dbh->quote($1)
+ } else {
+ delete $params->{taxclassnun};
+ }
+
+ my $tax_type = $1
+ if ( $params->{tax_type} =~ /^(\d+)$/ );
+ delete $params->{tax_type}
+ unless $tax_type;
+
+ my $tax_cat = $1
+ if ( $params->{tax_cat} =~ /^(\d+)$/ );
+ delete $params->{tax_cat}
+ unless $tax_cat;
+
+ my @taxclassnum = ();
+ if ($tax_type || $tax_cat ) {
+ my $compare = "LIKE '". ( $tax_type || "%" ). ":". ( $tax_cat || "%" ). "'";
+ $compare = "= '$tax_type:$tax_cat'" if ($tax_type && $tax_cat);
+ @taxclassnum = map { $_->taxclassnum }
+ qsearch({ 'table' => 'tax_class',
+ 'hashref' => {},
+ 'extra_sql' => "WHERE taxclass $compare",
+ });
+ }
+
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ). '( '.
+ join(' OR ', map { " taxclassnum = $_ " } @taxclassnum ). ' )'
+ if ( @taxclassnum );
+
+ unless ($params->{'showdisabled'}) {
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ "( disabled = '' OR disabled IS NULL )";
+ }
+
+ $query->{extra_sql} = $extra_sql;
+
+ return ($query, "SELECT COUNT(*) FROM tax_rate $extra_sql");
+}
+
+=back
+
+=head1 BUGS
+
+ Mixing automatic and manual editing works poorly at present.
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::cust_main>, L<FS::cust_bill>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/type_pkgs.pm b/FS/FS/type_pkgs.pm
new file mode 100644
index 0000000..6503755
--- /dev/null
+++ b/FS/FS/type_pkgs.pm
@@ -0,0 +1,130 @@
+package FS::type_pkgs;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearchs );
+use FS::agent_type;
+use FS::part_pkg;
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::type_pkgs - Object methods for type_pkgs records
+
+=head1 SYNOPSIS
+
+ use FS::type_pkgs;
+
+ $record = new FS::type_pkgs \%hash;
+ $record = new FS::type_pkgs { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::type_pkgs record links an agent type (see L<FS::agent_type>) to a
+billing item definition (see L<FS::part_pkg>). FS::type_pkgs inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item typepkgnum - primary key
+
+=item typenum - Agent type, see L<FS::agent_type>
+
+=item pkgpart - Billing item definition, see L<FS::part_pkg>
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Create a new record. To add the record to the database, see L<"insert">.
+
+=cut
+
+sub table { 'type_pkgs'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is an error,
+returns the error, otherwise returns false. Called by the insert and replace
+methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('typepkgnum')
+ || $self->ut_foreign_key('typenum', 'agent_type', 'typenum' )
+ || $self->ut_foreign_key('pkgpart', 'part_pkg', 'pkgpart' )
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item part_pkg
+
+Returns the FS::part_pkg object associated with this record.
+
+=cut
+
+sub part_pkg {
+ my $self = shift;
+ qsearchs( 'part_pkg', { 'pkgpart' => $self->pkgpart } );
+}
+
+=item agent_type
+
+Returns the FS::agent_type object associated with this record.
+
+=cut
+
+sub agent_type {
+ my $self = shift;
+ qsearchs( 'agent_type', { 'typenum' => $self->typenum } );
+}
+
+=cut
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::agent_type>, L<FS::part_pkgs>, schema.html from the base
+documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/usage_class.pm b/FS/FS/usage_class.pm
new file mode 100644
index 0000000..93a32df
--- /dev/null
+++ b/FS/FS/usage_class.pm
@@ -0,0 +1,143 @@
+package FS::usage_class;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::usage_class - Object methods for usage_class records
+
+=head1 SYNOPSIS
+
+ use FS::usage_class;
+
+ $record = new FS::usage_class \%hash;
+ $record = new FS::usage_class { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::usage_class object represents a usage class. Every rate detail
+(see L<FS::rate_detail>) has, optionally, a usage class. FS::usage_class
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item classnum
+
+Primary key (assigned automatically for new usage classes)
+
+=item classname
+
+Text name of this usage class
+
+=item disabled
+
+Disabled flag, empty or 'Y'
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new usage class. To add the usage class to the database,
+see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'usage_class'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid usage class. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('classnum')
+ || $self->ut_text('classname')
+ || $self->ut_enum('disabled', [ '', 'Y' ])
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+sub _populate_initial_data {
+ my ($class, %opts) = @_;
+
+ foreach ("Intrastate", "Interstate", "International") {
+ my $object = $class->new( { 'classname' => $_ } );
+ my $error = $object->insert;
+ die "error inserting $class into database: $error\n"
+ if $error;
+ }
+
+ '';
+
+}
+
+sub _upgrade_data {
+ my $class = shift;
+
+ return $class->_populate_initial_data(@_)
+ unless scalar( qsearch( 'usage_class', {} ) );
+
+ '';
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/MANIFEST b/FS/MANIFEST
new file mode 100644
index 0000000..4b9fd91
--- /dev/null
+++ b/FS/MANIFEST
@@ -0,0 +1,436 @@
+Changes
+MANIFEST
+MANIFEST.SKIP
+Makefile.PL
+bin/freeside-addoutsource
+bin/freeside-addoutsourceuser
+bin/freeside-addgroup
+bin/freeside-adduser
+bin/freeside-apply-credits
+bin/freeside-count-active-customers
+bin/freeside-daily
+bin/freeside-deloutsource
+bin/freeside-deloutsourceuser
+bin/freeside-deluser
+bin/freeside-email
+bin/freeside-expiration-alerter
+bin/freeside-queued
+bin/freeside-radgroup
+bin/freeside-reexport
+bin/freeside-selfservice-server
+bin/freeside-setinvoice
+bin/freeside-setup
+bin/freeside-sqlradius-radacctd
+bin/freeside-sqlradius-reset
+bin/freeside-sqlradius-seconds
+FS.pm
+FS/AccessRight.pm
+FS/CGI.pm
+FS/InitHandler.pm
+FS/ClientAPI.pm
+FS/ClientAPI_SessionCache.pm
+FS/ClientAPI/passwd.pm
+FS/ClientAPI/MyAccount.pm
+FS/Conf.pm
+FS/ConfItem.pm
+FS/Cron/backup.pm
+FS/Cron/bill.pm
+FS/Cron/vacuum.pm
+FS/Daemon.pm
+FS/Misc.pm
+FS/Record.pm
+FS/Report.pm
+FS/Report/Table.pm
+FS/Report/Table/Monthly.pm
+FS/SearchCache.pm
+FS/UI/Web.pm
+FS/UID.pm
+FS/Mason.pm
+FS/Mason/Request.pm
+FS/Msgcat.pm
+FS/Pony.pm
+FS/acct_snarf.pm
+FS/addr_block.pm
+FS/agent.pm
+FS/agent_type.pm
+FS/cust_bill.pm
+FS/cust_bill_pkg.pm
+FS/cust_bill_pkg_detail.pm
+FS/cust_credit.pm
+FS/cust_credit_bill.pm
+FS/cust_main.pm
+FS/cust_main/Import.pm
+FS/cust_main_Mixin.pm
+FS/cust_main_county.pm
+FS/cust_main_invoice.pm
+FS/cust_pay.pm
+FS/cust_bill_event.pm
+FS/cust_bill_pay.pm
+FS/cust_pay_batch.pm
+FS/cust_pay_refund.pm
+FS/cust_pkg.pm
+FS/cust_refund.pm
+FS/cust_credit_refund.pm
+FS/cust_svc.pm
+FS/h_Common.pm
+FS/h_cust_bill.pm
+FS/h_cust_pkg.pm
+FS/h_cust_pkg_reason.pm
+FS/h_cust_svc.pm
+FS/h_cust_tax_exempt.pm
+FS/h_domain_record.pm
+FS/h_svc_acct.pm
+FS/h_svc_broadband.pm
+FS/h_svc_domain.pm
+FS/h_svc_external.pm
+FS/h_svc_forward.pm
+FS/h_svc_www.pm
+FS/part_bill_event.pm
+FS/payinfo_Mixin.pm
+FS/export_svc.pm
+FS/part_export.pm
+FS/part_export_option.pm
+FS/part_export/acct_sql.pm
+FS/part_export/apache.pm
+FS/part_export/bind.pm
+FS/part_export/bind_slave.pm
+FS/part_export/bsdshell.pm
+FS/part_export/communigate_pro.pm
+FS/part_export/communigate_pro_singledomain.pm
+FS/part_export/cp.pm
+FS/part_export/cyrus.pm
+FS/part_export/domain_shellcommands.pm
+FS/part_export/forward_shellcommands.pm
+FS/part_export/http.pm
+FS/part_export/infostreet.pm
+FS/part_export/ldap.pm
+FS/part_export/null.pm
+FS/part_export/radiator.pm
+FS/part_export/router.pm
+FS/part_export/shellcommands.pm
+FS/part_export/shellcommands_withdomain.pm
+FS/part_export/sqlmail.pm
+FS/part_export/sqlradius.pm
+FS/part_export/sysvshell.pm
+FS/part_export/textradius.pm
+FS/part_export/vpopmail.pm
+FS/part_export/www_shellcommands.pm
+FS/part_pkg.pm
+FS/part_pkg_option.pm
+FS/part_pkg/flat.pm
+FS/part_pkg/flat_comission.pm
+FS/part_pkg/flat_comission_cust.pm
+FS/part_pkg/flat_comission_pkg.pm
+FS/part_pkg/flat_delayed.pm
+FS/part_pkg/prorate.pm
+FS/part_pkg/sesmon_hour.pm
+FS/part_pkg/sesmon_minute.pm
+FS/part_pkg/sql_external.pm
+FS/part_pkg/sql_generic.pm
+FS/part_pkg/sqlradacct_hour.pm
+FS/part_pkg/subscription.pm
+FS/part_pkg/voip_sqlradacct.pm
+FS/part_pkg/voip_cdr.pm
+FS/part_pkg/base_rate.pm
+FS/part_pkg/base_delayed.pm
+FS/part_pop_local.pm
+FS/part_referral.pm
+FS/part_svc.pm
+FS/part_svc_column.pm
+FS/part_svc_router.pm
+FS/part_virtual_field.pm
+FS/payby.pm
+FS/pkg_class.pm
+FS/pkg_svc.pm
+FS/rate.pm
+FS/rate_detail.pm
+FS/rate_region.pm
+FS/rate_prefix.pm
+FS/reg_code.pm
+FS/reg_code_pkg.pm
+FS/svc_Common.pm
+FS/svc_acct.pm
+FS/svc_acct_pop.pm
+FS/svc_broadband.pm
+FS/svc_domain.pm
+FS/svc_external.pm
+FS/router.pm
+FS/type_pkgs.pm
+FS/nas.pm
+FS/port.pm
+FS/session.pm
+FS/domain_record.pm
+FS/prepay_credit.pm
+FS/svc_www.pm
+FS/svc_forward.pm
+FS/raddb.pm
+FS/radius_usergroup.pm
+FS/queue.pm
+FS/queue_arg.pm
+FS/queue_depend.pm
+FS/msgcat.pm
+FS/cust_tax_exempt.pm
+FS/cust_tax_exempt_pkg.pm
+FS/clientapi_session.pm
+FS/clientapi_session_field.pm
+t/addr_block.t
+t/agent.t
+t/agent_type.t
+t/AccessRight.t
+t/CGI.t
+t/InitHandler.t
+t/ClientAPI.t
+t/ClientAPI_SessionCache.t
+t/Conf.t
+t/ConfItem.t
+t/Cron-backup.t
+t/Cron-bill.t
+t/Cron-vacuum.t
+t/Daemon.t
+t/Misc.t
+t/Record.t
+t/Report.t
+t/Report-Table.t
+t/Report-Table-Monthly.t
+t/UID.t
+t/Msgcat.t
+t/SearchCache.t
+t/cust_bill.t
+t/cust_bill_event.t
+t/cust_bill_pay.t
+t/cust_bill_pkg.t
+t/cust_bill_pkg_detail.t
+t/cust_credit.t
+t/cust_credit_bill.t
+t/cust_credit_refund.t
+t/cust_main.t
+t/cust_main_Mixin.t
+t/cust_main_county.t
+t/cust_main_invoice.t
+t/cust_pay.t
+t/cust_pay_batch.t
+t/cust_pay_refund.t
+t/cust_pkg.t
+t/cust_refund.t
+t/cust_svc.t
+t/h_cust_bill.t
+t/h_cust_pkg.t
+t/h_cust_pkg_reason.t
+t/h_cust_svc.t
+t/h_cust_tax_exempt.t
+t/h_Common.t
+t/h_domain_record.t
+t/h_svc_acct.t
+t/h_svc_broadband.t
+t/h_svc_domain.t
+t/h_svc_external.t
+t/h_svc_forward.t
+t/h_svc_www.t
+t/cust_tax_exempt.t
+t/cust_tax_exempt_pkg.t
+t/domain_record.t
+t/nas.t
+t/part_bill_event.t
+t/export_svc.t
+t/part_export.t
+t/part_export_option.t
+t/part_export-acct_sql.t
+t/part_export-apache.t
+t/part_export-bind.t
+t/part_export-bind_slave.t
+t/part_export-bsdshell.t
+t/part_export-communigate_pro.t
+t/part_export-communigate_pro_singledomain.t
+t/part_export-cp.t
+t/part_export-cyrus.t
+t/part_export-domain_shellcommands.t
+t/part_export-forward_shellcommands.t
+t/part_export-http.t
+t/part_export-infostreet.t
+t/part_export-ldap.t
+t/part_export-null.t
+t/part_export-passwdfile.t
+t/part_export-postfix.t
+t/part_export-radiator.t
+t/part_export-router.t
+t/part_export-shellcommands.t
+t/part_export-shellcommands_withdomain.t
+t/part_export-sqlmail.t
+t/part_export-sqlradius.t
+t/part_export-sysvshell.t
+t/part_export-textradius.t
+t/part_export-vpopmail.t
+t/part_export-www_shellcommands.t
+t/part_pkg.t
+t/part_pkg_option.t
+t/part_pkg-flat.t
+t/part_pkg-flat_comission.t
+t/part_pkg-flat_comission_cust.t
+t/part_pkg-flat_comission_pkg.t
+t/part_pkg-flat_delayed.t
+t/part_pkg-prorate.t
+t/part_pkg-sesmon_hour.t
+t/part_pkg-sesmon_minute.t
+t/part_pkg-sql_external.t
+t/part_pkg-sql_generic.t
+t/part_pkg-sqlradacct_hour.t
+t/part_pkg-subscription.t
+t/part_pkg-voip_sqlradacct.t
+t/part_pkg-voip_cdr.t
+t/part_pop_local.t
+t/part_referral.t
+t/part_svc.t
+t/part_svc_column.t
+t/payby.t
+t/payinfo_Mixin.t
+t/pkg_class.t
+t/pkg_svc.t
+t/port.t
+t/prepay_credit.t
+t/rate.t
+t/rate_detail.t
+t/rate_region.t
+t/rate_prefix.t
+t/radius_usergroup.t
+t/reg_code.t
+t/reg_code_pkg.t
+t/router.t
+t/session.t
+t/svc_acct.t
+t/svc_acct_pop.t
+t/svc_broadband.t
+t/svc_Common.t
+t/svc_domain.t
+t/svc_external.t
+t/svc_forward.t
+t/svc_www.t
+t/type_pkgs.t
+t/queue.t
+t/queue_arg.t
+t/queue_depend.t
+t/msgcat.t
+t/raddb.t
+t/clientapi_session.t
+t/clientapi_session_field.t
+FS/payment_gateway.pm
+t/payment_gateway.t
+FS/payment_gateway_option.pm
+t/payment_gateway_option.t
+FS/option_Common.pm
+t/option_Common.t
+FS/agent_payment_gateway.pm
+t/agent_payment_gateway.t
+FS/banned_pay.pm
+t/banned_pay.t
+bin/freeside-prepaidd
+FS/cdr.pm
+t/cdr.t
+FS/cdr_calltype.pm
+t/cdr_calltype.t
+FS/cdr_type.pm
+t/cdr_type.t
+FS/cdr_carrier.pm
+t/cdr_carrier.t
+FS/inventory_class.pm
+t/inventory_class.t
+FS/inventory_item.pm
+t/inventory_item.t
+FS/cdr_upstream_rate.pm
+t/cdr_upstream_rate.t
+FS/access_user.pm
+t/access_user.t
+FS/access_user_pref.pm
+t/access_user_pref.t
+FS/access_group.pm
+t/access_group.t
+FS/access_usergroup.pm
+t/access_usergroup.t
+FS/access_groupagent.pm
+t/access_groupagent.t
+FS/access_right.pm
+t/access_right.t
+FS/m2m_Common.pm
+FS/pay_batch.pm
+t/pay_batch.t
+FS/ConfDefaults.pm
+t/ConfDefaults.t
+FS/m2name_Common.pm
+FS/CurrentUser.pm
+FS/svc_phone.pm
+t/svc_phone.t
+FS/h_svc_phone.pm
+FS/cust_bill_pay_batch.pm
+t/cust_bill_pay_batch.t
+FS/cust_bill_pay_pkg.pm
+t/cust_bill_pay_pkg.t
+FS/cust_credit_bill_pkg.pm
+t/cust_credit_bill_pkg.t
+FS/registrar.pm
+t/registrar.t
+FS/svc_External_Common.pm
+t/svc_External_Common.t
+FS/svc_Parent_Mixin.pm
+t/svc_Parent_Mixin.t
+FS/cust_main_note.pm
+t/cust_main_note.t
+FS/cust_pkg_reason.pm
+t/cust_pkg_reason.t
+FS/reason.pm
+t/reason.t
+FS/reason_type.pm
+t/reason_type.t
+FS/pkg_referral.pm
+t/pkg_referral.t
+FS/part_event_option.pm
+t/part_event_option.t
+FS/part_event_condition.pm
+t/part_event_condition.t
+FS/part_event_condition_option.pm
+t/part_event_condition_option.t
+FS/part_event.pm
+t/part_event.t
+FS/cust_event.pm
+t/cust_event.t
+FS/part_event_condition_option_option.pm
+t/part_event_condition_option_option.t
+FS/cust_pkg_option.pm
+t/cust_pkg_option.t
+FS/conf.pm
+t/conf.t
+FS/acct_rt_transaction.pm
+t/acct_rt_transaction.t
+FS/cust_pay_pending.pm
+t/cust_pay_pending.t
+FS/part_pkg_taxclass.pm
+t/part_pkg_taxclass.t
+FS/tax_rate.pm
+t/tax_rate.t
+FS/tax_class.pm
+t/tax_class.t
+FS/cust_tax_location.pm
+t/cust_tax_location.t
+FS/part_pkg_taxproduct.pm
+t/part_pkg_taxproduct.t
+FS/part_pkg_taxoverride.pm
+t/part_pkg_taxoverride.t
+FS/part_pkg_taxrate.pm
+t/part_pkg_taxrate.t
+FS/part_pkg_link.pm
+t/part_pkg_link.t
+FS/pkg_category.pm
+t/pkg_category.t
+FS/phone_avail.pm
+t/phone_avail.t
+FS/Yori.pm
+FS/cust_svc_option.pm
+t/cust_svc_option.t
+FS/usage_class.pm
+t/usage_class.t
+FS/cust_bill_pkg_display.pm
+t/cust_bill_pkg_display.t
+FS/cust_pkg_detail.pm
+t/cust_pkg_detail.t
+FS/cust_location.pm
+t/cust_location.t
+FS/cust_bill_pkg_tax_location.pm
+t/cust_bill_pkg_tax_location.t
diff --git a/FS/MANIFEST.SKIP b/FS/MANIFEST.SKIP
new file mode 100644
index 0000000..ae335e7
--- /dev/null
+++ b/FS/MANIFEST.SKIP
@@ -0,0 +1 @@
+CVS/
diff --git a/FS/Makefile.PL b/FS/Makefile.PL
new file mode 100644
index 0000000..1647f8e
--- /dev/null
+++ b/FS/Makefile.PL
@@ -0,0 +1,10 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ 'NAME' => 'FS',
+ 'VERSION_FROM' => 'FS.pm', # finds $VERSION
+ 'EXE_FILES' => [ glob 'bin/*' ],
+ 'INSTALLSCRIPT' => '/usr/local/bin',
+ 'INSTALLSITEBIN' => '/usr/local/bin',
+);
diff --git a/FS/bin/freeside-addgroup b/FS/bin/freeside-addgroup
new file mode 100755
index 0000000..7b30f7d
--- /dev/null
+++ b/FS/bin/freeside-addgroup
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+
+use strict;
+use vars qw($opt_s);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::CurrentUser;
+use FS::AccessRight;
+use FS::access_group;
+use FS::access_right;
+use FS::access_groupagent;
+
+getopts("s");
+my $user = shift or die &usage; #just for adminsuidsetup
+my $group = shift or die &usage;
+
+$FS::CurrentUser::upgrade_hack = 1;
+#adminsuidsetup $rootuser;
+adminsuidsetup $user;
+
+my $access_group = new FS::access_group { 'groupname' => $group };
+my $error = $access_group->insert;
+die $error if $error;
+
+if ( $opt_s ) {
+ foreach my $rightname ( FS::AccessRight->rights ) {
+ my $access_right = new FS::access_right {
+ 'righttype' => 'FS::access_group',
+ 'rightobjnum' => $access_group->groupnum,
+ 'rightname' => $rightname,
+ };
+ my $ar_error = $access_right->insert;
+ die $ar_error if $ar_error;
+ }
+
+ foreach my $agent ( qsearch('agent', {} ) ) {
+ my $access_groupagent = new FS::access_groupagent {
+ 'groupnum' => $access_group->groupnum,
+ 'agentnum' => $agent->agentnum,
+ };
+ my $aga_error = $access_groupagent->insert;
+ die $aga_error if $aga_error;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-addgroup [ -s ] username groupname"
+}
+
diff --git a/FS/bin/freeside-addoutsource b/FS/bin/freeside-addoutsource
new file mode 100644
index 0000000..9cb1219
--- /dev/null
+++ b/FS/bin/freeside-addoutsource
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+domain=$1
+
+FREESIDE_CONF=%%%FREESIDE_CONF%%%
+FREESIDE_CACHE=%%%FREESIDE_CACHE%%%
+FREESIDE_EXPORT=%%%FREESIDE_EXPORT%%%
+
+#without this, [a-z]* matches CVS/, the copy doesn't return a sucessful error
+# status, and the rest of the commands aren't run
+export LANG=C
+
+createdb $domain && \
+\
+mkdir $FREESIDE_CONF/conf.DBI:Pg:dbname=$domain && \
+\
+chown freeside $FREESIDE_CONF/conf.DBI:Pg:dbname=$domain && \
+\
+cp /home/ivan/freeside/conf/[a-z]* $FREESIDE_CONF/conf.DBI:Pg:dbname=$domain && \
+\
+touch $FREESIDE_CONF/conf.DBI:Pg:dbname=$domain/secrets && \
+\
+chown freeside $FREESIDE_CONF/conf.DBI:Pg:dbname=$domain/secrets && \
+\
+chmod 600 $FREESIDE_CONF/conf.DBI:Pg:dbname=$domain/secrets && \
+\
+echo -e "DBI:Pg:dbname=$domain\nfreeside\n" >$FREESIDE_CONF/conf.DBI:Pg:dbname=$domain/secrets && \
+\
+mkdir $FREESIDE_CACHE/counters.DBI:Pg:dbname=$domain && \
+mkdir $FREESIDE_CACHE/cache.DBI:Pg:dbname=$domain && \
+mkdir $FREESIDE_EXPORT/export.DBI:Pg:dbname=$domain
+
diff --git a/FS/bin/freeside-addoutsourceuser b/FS/bin/freeside-addoutsourceuser
new file mode 100644
index 0000000..cbe792a
--- /dev/null
+++ b/FS/bin/freeside-addoutsourceuser
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+username=$1
+domain=$2
+password=$3
+realdomain=$4
+FREESIDE_CONF=%%%FREESIDE_CONF%%%
+
+freeside-adduser -s conf.DBI:Pg:dbname=$domain/secrets \
+ -n \
+ $username #2>/dev/null
+
+[ -e $FREESIDE_CONF/dbdef.DBI:Pg:dbname=$domain ] \
+ || ( freeside-setup -d $realdomain -u $username )
+
+freeside-adduser -g 1 $username
+
+htpasswd -b $FREESIDE_CONF/htpasswd $username $password
diff --git a/FS/bin/freeside-adduser b/FS/bin/freeside-adduser
new file mode 100644
index 0000000..5304813
--- /dev/null
+++ b/FS/bin/freeside-adduser
@@ -0,0 +1,119 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_s $opt_g $opt_n);
+use Fcntl qw(:flock);
+use Getopt::Std;
+
+my $FREESIDE_CONF = "%%%FREESIDE_CONF%%%";
+
+getopts("s:g:n");
+my $user = shift or die &usage;
+
+if ( $opt_s ) {
+
+ #if ( -e "$FREESIDE_CONF/mapsecrets" ) {
+ # open(MAPSECRETS,"<$FREESIDE_CONF/mapsecrets")
+ # or die "can't open $FREESIDE_CONF/mapsecrets: $!";
+ # while (<MAPSECRETS>) {
+ # /^(\S+) / or die "unparsable line in mapsecrets: $_";
+ # die "user $user already exists\n" if $user eq $1;
+ # }
+ # close MAPSECRETS;
+ #}
+
+ #insert new entry before a wildcard...
+ open(MAPSECRETS,"<$FREESIDE_CONF/mapsecrets")
+ and flock(MAPSECRETS,LOCK_EX)
+ or die "can't open $FREESIDE_CONF/mapsecrets: $!";
+ open(NEW,">$FREESIDE_CONF/mapsecrets.new")
+ or die "can't open $FREESIDE_CONF/mapsecrets.new: $!";
+ while(<MAPSECRETS>) {
+ if ( /^\*\s/ ) {
+ print NEW "$user $opt_s\n";
+ }
+ print NEW $_;
+ }
+ close MAPSECRETS or die "can't close $FREESIDE_CONF/mapsecrets: $!";
+ close NEW or die "can't close $FREESIDE_CONF/mapsecrets.new: $!";
+ rename("$FREESIDE_CONF/mapsecrets.new", "$FREESIDE_CONF/mapsecrets")
+ or die "can't move mapsecrets.new into place: $!";
+
+}
+
+###
+
+exit if $opt_n;
+
+###
+
+use FS::UID qw(adminsuidsetup);
+use FS::CurrentUser;
+use FS::access_user;
+use FS::access_usergroup;
+
+$FS::CurrentUser::upgrade_hack = 1;
+#adminsuidsetup $rootuser;
+adminsuidsetup $user;
+
+my $access_user = new FS::access_user {
+ 'username' => $user,
+ '_password' => 'notyet',
+ 'first' => 'Firstname', # $opt_f ||
+ 'last' => 'Lastname', # $opt_l ||
+};
+my $au_error = $access_user->insert;
+die $au_error if $au_error;
+
+if ( $opt_g ) {
+
+ my $access_usergroup = new FS::access_usergroup {
+ 'usernum' => $access_user->usernum,
+ 'groupnum' => $opt_g,
+ };
+ my $aug_error = $access_usergroup->insert;
+ die $aug_error if $aug_error;
+
+}
+
+###
+
+sub usage {
+ die "Usage:\n\n freeside-adduser [ -n ] [ -s ] [ -g groupnum ] username [ password ]"
+}
+
+=head1 NAME
+
+freeside-adduser - Command line interface to add (freeside) users.
+
+=head1 SYNOPSIS
+
+ freeside-adduser [ -n ] [ -s ] [ -g groupnum ] username [ password ]
+
+=head1 DESCRIPTION
+
+Adds a user to the Freeside billing system. This is for adding users (internal
+sales/tech folks) to the web interface, not for adding customer accounts.
+
+This functionality is now available in the web interface as well, under
+B<Configuration | Employees | View/Edit employees>.
+
+ -g: initial groupnum
+
+ Development/multi-DB options:
+
+ -s: alternate secrets file
+
+ -n: no ACL added, for bootstrapping
+
+=head1 NOTE
+
+No explicit htpasswd options are available in 1.7 - passwords are now
+maintained automatically.
+
+=head1 SEE ALSO
+
+Base Freeside documentation
+
+=cut
+
diff --git a/FS/bin/freeside-apply-credits b/FS/bin/freeside-apply-credits
new file mode 100755
index 0000000..ea6a7bd
--- /dev/null
+++ b/FS/bin/freeside-apply-credits
@@ -0,0 +1,21 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw( $user $cust_main @customers );
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_main;
+
+$user = shift or die &usage;
+&adminsuidsetup( $user );
+
+my @customers = qsearch('cust_main', {} );
+die "No customers" unless (scalar(@customers) > 0);
+
+foreach $cust_main (@customers) {
+ print "Applying credits for customer #". $cust_main->custnum;
+ $cust_main->apply_credits;
+}
+
+
+
diff --git a/FS/bin/freeside-cdrd b/FS/bin/freeside-cdrd
new file mode 100644
index 0000000..2cf75f3
--- /dev/null
+++ b/FS/bin/freeside-cdrd
@@ -0,0 +1,160 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::Daemon ':all'; #daemonize1 drop_root daemonize2 myexit logfile sig*
+use FS::UID qw( adminsuidsetup );
+use FS::Record qw( qsearch ); #qsearchs);
+#use FS::cdr;
+use FS::cust_pkg;
+use FS::queue;
+
+my $user = shift or die &usage;
+
+#daemonize1('freeside-sprepaidd', $user); #keep unique pid files w/multi installs
+daemonize1('freeside-cdrd');
+
+drop_root();
+
+adminsuidsetup($user);
+
+logfile( "%%%FREESIDE_LOG%%%/cdrd-log.". $FS::UID::datasrc );
+
+daemonize2();
+
+die "not running; no voip_cdr package defs w/ bill_every_call and customer pkgs"
+ unless _shouldrun();
+
+#--
+
+my $addl_from =
+ 'LEFT JOIN part_pkg USING ( pkgpart ) '.
+ "LEFT JOIN part_pkg_option
+ ON ( cust_pkg.pkgpart = part_pkg_option.pkgpart
+ AND part_pkg_option.optionname = 'bill_every_call' )";
+
+#XXX should pay attention to disable_src for efficiency
+
+my $extra_sql =
+ "WHERE plan = 'voip_cdr' ".
+ " AND optionvalue = '1' ".
+ " AND ( susp IS NULL OR susp = 0)".
+ " AND ( cancel IS NULL OR cancel = 0)".
+ " AND 0 < (
+ SELECT COUNT(*) FROM svc_phone LEFT JOIN cust_svc USING (svcnum)
+ WHERE cust_pkg.pkgnum = cust_svc.pkgnum
+ AND 0 < ( SELECT COUNT(*) FROM cdr
+ WHERE ( freesidestatus IS NULL OR freesidestatus = '' )
+ AND ( charged_party = svc_phone.phonenum
+ OR charged_party = svc_phone.countrycode
+ || svc_phone.phonenum
+ OR src = svc_phone.phonenum
+ OR src = svc_phone.countrycode
+ || svc_phone.phonenum
+ )
+ )
+ )
+ AND 0 = (
+ SELECT COUNT(*) FROM queue
+ WHERE queue.job = 'FS::cust_main::queued_bill'
+ AND queue.custnum = cust_pkg.custnum
+ )
+
+ ";
+# don't repeatedly queue failures
+# AND status != 'failed'
+
+while (1) {
+
+ my $found = 0;
+ foreach my $cust_pkg (
+ qsearch( {
+ 'select' => 'cust_pkg.*, part_pkg.plan',
+ 'table' => 'cust_pkg',
+ 'addl_from' => $addl_from,
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ } )
+ ) {
+
+ $found = 1;
+
+ #my $work_cust_pkg = $cust_pkg;
+
+ #my $cust_main = $cust_pkg->cust_main;
+
+ my $time = time;
+
+ my $job = new FS::queue {
+ 'job' => 'FS::cust_main::queued_bill',
+ 'secure' => 'Y',
+ 'custnum' => $cust_pkg->custnum,
+ };
+ my $error = $job->insert(
+ 'custnum' => $cust_pkg->custnum,
+ 'time' => $time,
+ 'invoice_time' => $time,
+ 'actual_time' => $time,
+ 'check_freq' => '1d', #well
+ #'debug' => 1,
+ );
+
+ if ( $error ) {
+ #die "FATAL: error inserting billing job: $error\n";
+ warn "WARNING: error inserting billing job (will retry in 30 seconds):".
+ " $error\n";
+ sleep 30; #i dunno, wait and see if the database comes back?
+ }
+
+ }
+
+ myexit() if sigterm() || sigint();
+ sleep 1 unless $found;
+
+}
+
+#--
+
+sub _shouldrun {
+
+ my $extra_sql =
+ ' AND 0 < ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.pkgpart = part_pkg.pkgpart
+ AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ )
+ ';
+
+ my @part_pkg =
+ grep $_->option('bill_every_call', 'hush'),
+ qsearch({
+ 'table' => 'part_pkg',
+ 'hashref' => { 'plan' => 'voip_cdr' },
+ 'extra_sql' => $extra_sql,
+ })
+ ;
+
+ scalar(@part_pkg);
+
+}
+
+sub usage {
+ die "Usage:\n\n freeside-cdrd user\n";
+}
+
+=head1 NAME
+
+freeside-cdrd - Real-time daemon for CDRs
+
+=head1 SYNOPSIS
+
+ freeside-cdrd
+
+=head1 DESCRIPTION
+
+Runs continuously, searches for CDRs and bills customers who have VoIP
+price plands with the B<bill_every_call> option set.
+
+=head1 SEE ALSO
+
+=cut
+
+1;
diff --git a/FS/bin/freeside-cdrrewrited b/FS/bin/freeside-cdrrewrited
new file mode 100644
index 0000000..0b7f688
--- /dev/null
+++ b/FS/bin/freeside-cdrrewrited
@@ -0,0 +1,129 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( $conf );
+use FS::Daemon ':all'; #daemonize1 drop_root daemonize2 myexit logfile sig*
+use FS::UID qw( adminsuidsetup );
+use FS::Record qw( qsearch ); #qsearchs);
+#use FS::cdr;
+#use FS::cust_pkg;
+#use FS::queue;
+
+my $user = shift or die &usage;
+
+#daemonize1('freeside-sprepaidd', $user); #keep unique pid files w/multi installs
+daemonize1('freeside-cdrrewrited');
+
+drop_root();
+
+adminsuidsetup($user);
+
+logfile( "%%%FREESIDE_LOG%%%/cdrrewrited-log.". $FS::UID::datasrc );
+
+daemonize2();
+
+$conf = new FS::Conf;
+
+die "not running; cdr-asterisk_forward_rewrite and cdr-charged_party_rewrite ".
+ " conf options are both off\n"
+ unless _shouldrun();
+
+#--
+
+while (1) {
+
+ #hmm... don't want to do an expensive search with an ever-growing bunch
+ # of unprocessed CDRs during the month... better to mark them all as
+ # rewritten "skipped", i.e. why we're a daemon in the first place
+ # instead of just doing this search like normal CDRs
+
+ my $found = 0;
+ foreach my $cdr (
+ qsearch( {
+ 'table' => 'cdr',
+ 'extra_sql' => 'FOR UPDATE',
+ 'hashref' => {},
+ 'extra_sql' => 'WHERE freesidestatus IS NULL'.
+ ' AND freesiderewritestatus IS NULL'.
+ ' LIMIT 1024', #arbitrary, but don't eat too much memory
+ } )
+ ) {
+
+ $found = 1;
+ my @status = ();
+
+ if ( $conf->exists('cdr-asterisk_forward_rewrite')
+ && $cdr->dstchannel =~ /^Local\/(\d+)/i && $1 ne $cdr->dst
+ )
+ {
+
+ my $dst = $1;
+
+ warn "dst ". $cdr->dst. " does not match dstchannel $dst ".
+ "(". $cdr->dstchannel. "); rewriting CDR as a forwarded call";
+
+ $cdr->charged_party($cdr->dst);
+ $cdr->dst($dst);
+ $cdr->amaflags(2);
+
+ push @status, 'asterisk_forward';
+
+ }
+
+ if ( $conf->exists('cdr-charged_party_rewrite') && ! $cdr->charged_party ) {
+
+ $cdr->set_charged_party;
+ push @status, 'charged_party';
+
+ }
+
+ $cdr->freesiderewritestatus(
+ scalar(@status) ? join('/', @status) : 'skipped'
+ );
+
+ my $error = $cdr->replace;
+
+ if ( $error ) {
+ warn "WARNING: error rewriting CDR (will retry in 30 seconds):".
+ " $error\n";
+ sleep 30; #i dunno, wait and see if the database comes back?
+ }
+
+ }
+
+ myexit() if sigterm() || sigint();
+ #sleep 1 unless $found;
+ sleep 5 unless $found;
+
+}
+
+#--
+
+sub _shouldrun {
+ $conf->exists('cdr-asterisk_forward_rewrite')
+ || $conf->exists('cdr-charged_party_rewrite');
+}
+
+sub usage {
+ die "Usage:\n\n freeside-cdrrewrited user\n";
+}
+
+=head1 NAME
+
+freeside-cdrrewrited - Real-time daemon for CDR rewriting
+
+=head1 SYNOPSIS
+
+ freeside-cdrrewrited
+
+=head1 DESCRIPTION
+
+Runs continuously, searches for CDRs and does forwarded-call rewriting if the
+"cdr-asterisk_forward_rewrite" or "cdr-charged_party_rewrite" config option is
+enabled.
+
+=head1 SEE ALSO
+
+=cut
+
+1;
diff --git a/FS/bin/freeside-count-active-customers b/FS/bin/freeside-count-active-customers
new file mode 100755
index 0000000..759085a
--- /dev/null
+++ b/FS/bin/freeside-count-active-customers
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+domain=$1
+
+echo "\t
+select count(*) from cust_main where
+ 0 < ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ AND ( cust_pkg.cancel IS NULL
+ OR cust_pkg.cancel = 0
+ )
+ )
+ OR 0 = ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ );
+" | psql -U freeside -q $domain | head -1
+
diff --git a/FS/bin/freeside-daily b/FS/bin/freeside-daily
new file mode 100755
index 0000000..13079b4
--- /dev/null
+++ b/FS/bin/freeside-daily
@@ -0,0 +1,104 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+
+&untaint_argv; #what it sounds like (eww)
+use vars qw(%opt);
+getopts("p:a:d:vl:sy:nm", \%opt);
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+use FS::Cron::bill qw(bill);
+bill(%opt);
+
+#what to do about the below when using -m? that is the question.
+
+use FS::Cron::notify qw(notify_flat_delay);
+notify_flat_delay(%opt);
+
+use FS::Cron::expire_user_pref qw(expire_user_pref);
+expire_user_pref();
+
+use FS::Cron::vacuum qw(vacuum);
+vacuum();
+
+use FS::Cron::backup qw(backup_scp);
+backup_scp();
+
+###
+# subroutines
+###
+
+sub untaint_argv {
+ foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
+ #$ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ # Date::Parse
+ $ARGV[$_] =~ /^(.*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ $ARGV[$_]=$1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-daily [ -d 'date' ] user [ custnum custnum ... ]\n";
+}
+
+###
+# documentation
+###
+
+=head1 NAME
+
+freeside-daily - Run daily billing and invoice collection events.
+
+=head1 SYNOPSIS
+
+ freeside-daily [ -d 'date' ] [ -y days ] [ -p 'payby' ] [ -a agentnum ] [ -s ] [ -v ] [ -l level ] [ -m ] user [ custnum custnum ... ]
+
+=head1 DESCRIPTION
+
+Bills customers and runs invoice collection events. Should be run from
+crontab daily.
+
+Bills customers. Searches for customers who are due for billing and calls
+the bill and collect methods of a cust_main object. See L<FS::cust_main>.
+
+ -d: Pretend it's 'date'. Date is in any format Date::Parse is happy with,
+ but be careful.
+
+ -y: In addition to -d, which specifies an absolute date, the -y switch
+ specifies an offset, in days. For example, "-y 15" would increment the
+ "pretend date" 15 days from whatever was specified by the -d switch
+ (or now, if no -d switch was given).
+
+ -n: When used with "-d" and/or "-y", specifies that invoices should be dated
+ with today's date, irregardless of the pretend date used to pre-generate
+ the invoices.
+
+ -p: Only process customers with the specified payby (I<CARD>, I<DCRD>, I<CHEK>, I<DCHK>, I<BILL>, I<COMP>, I<LECB>)
+
+ -a: Only process customers with the specified agentnum
+
+ -s: re-charge setup fees
+
+ -v: enable debugging
+
+ -l: debugging level
+
+ -m: Experimental multi-process mode uses the job queue for multi-process and/or multi-machine billing.
+
+user: From the mapsecrets file - see config.html from the base documentation
+
+custnum: if one or more customer numbers are specified, only bills those
+customers. Otherwise, bills all customers.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::cust_main>, config.html from the base documentation
+
+=cut
+
diff --git a/FS/bin/freeside-dbdef-create b/FS/bin/freeside-dbdef-create
new file mode 100755
index 0000000..a04f425
--- /dev/null
+++ b/FS/bin/freeside-dbdef-create
@@ -0,0 +1,47 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use DBI;
+use DBIx::DBSchema 0.26;
+use FS::UID qw(adminsuidsetup datasrc driver_name);
+use FS::Schema;
+
+my $user = shift or die &usage;
+
+$FS::Schema::setup_hack = 1;
+$FS::CurrentUser::upgrade_hack = 1;
+my($dbh)=adminsuidsetup $user;
+
+#needs to match FS::Record
+my($dbdef_file) = "%%%FREESIDE_CONF%%%/dbdef.". datasrc;
+
+my $dbdef = new_native DBIx::DBSchema $dbh;
+
+#print $dbdef->pretty_print;
+
+#important
+$dbdef->save($dbdef_file);
+
+sub usage {
+ die "Usage:\n dbdef-create user\n";
+}
+
+=head1 NAME
+
+freeside-dbdef-create - Recreate database schema cache
+
+=head1 SYNOPSIS
+
+ freeside-dbdef-create user
+
+=head1 DESCRIPTION
+
+Reverse engineers the database schema and recreates the dbdef cache file.
+
+=head1 SEE ALSO
+
+L<DBIx::DBSchema>
+
+=cut
+
+1;
diff --git a/FS/bin/freeside-dedup-cust_bill_pkg_detail-header b/FS/bin/freeside-dedup-cust_bill_pkg_detail-header
new file mode 100755
index 0000000..d887f21
--- /dev/null
+++ b/FS/bin/freeside-dedup-cust_bill_pkg_detail-header
@@ -0,0 +1,57 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( %seen $opt_d );
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_bill_pkg_detail;
+
+getopts('d');
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $extra_sql = "AND detail LIKE 'Date,Time%'";
+my @cust_bill_pkg_detail = qsearch( { 'table' => 'cust_bill_pkg_detail',
+ 'hashref' => {format => 'C'},
+ 'extra_sql' => $extra_sql,
+ } );
+for my $detail (@cust_bill_pkg_detail) {
+ if ( $seen{$detail->billpkgnum} ) {
+ if ($opt_d) { # dry run
+ print "DELETE cust_bill_pkg_detail WHERE detailnum=". $detail->detailnum.
+ "\n";
+ } else {
+ $detail->delete;
+ }
+ } else {
+ $seen{$detail->billpkgnum} = 1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-sqlradius-dedup-group [-d] user\n";
+}
+
+=head1 NAME
+
+freeside-dedup-cust_bill_pkg_detail-header - Command line tool to eliminate duplicate headers from cdr details on invoices
+
+=head1 SYNOPSIS
+
+ freeside-dedup-cust_bill_pkg_detail-header user
+
+=head1 DESCRIPTION
+
+ Removes all but one header when duplicate entries exist on invoice
+ cdr details.
+
+ -d: dry run
+
+=head1 SEE ALSO
+
+L<FS::part_pkg::voip_cdr>
+
+=cut
+
diff --git a/FS/bin/freeside-delete-addr_blocks b/FS/bin/freeside-delete-addr_blocks
new file mode 100755
index 0000000..a7e9976
--- /dev/null
+++ b/FS/bin/freeside-delete-addr_blocks
@@ -0,0 +1,31 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw( $user $block @blocks );
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::addr_block;
+use FS::svc_broadband;
+
+$user = shift or die &usage;
+&adminsuidsetup( $user );
+
+@blocks = qsearch('addr_block', {} );
+die "No address blocks" unless (scalar(@blocks) > 0);
+
+foreach $block (@blocks) {
+ my @devices = qsearch('svc_broadband', { 'blocknum' => $block->blocknum } );
+ if (@devices) {
+ print "Skipping block " . $block->ip_gateway . " / " . $block->ip_netmask;
+ print "\n";
+ }else{
+ print "Deleting block " . $block->ip_gateway . " / " . $block->ip_netmask;
+ print "\n";
+ $block->delete;
+ }
+}
+
+
+sub usage {
+ "Usage:\n freeside-delete-addr_blocks user \n";
+}
diff --git a/FS/bin/freeside-deloutsource b/FS/bin/freeside-deloutsource
new file mode 100644
index 0000000..afc3a01
--- /dev/null
+++ b/FS/bin/freeside-deloutsource
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+domain=$1
+FREESIDE_CONF=%%%FREESIDE_CONF%%%
+FREESIDE_CACHE=%%%FREESIDE_CACHE%%%
+FREESIDE_EXPORT=%%%FREESIDE_EXPORT%%%
+
+dropdb $domain && \
+rm -rf $FREESIDE_CONF/conf.DBI:Pg:host=localhost\;dbname=$domain && \
+rm -rf $FREESIDE_CACHE/counters.DBI:Pg:host=localhost\;dbname=$domain && \
+rm -rf $FREESIDE_CACHE/cache.DBI:Pg:host=localhost\;dbname=$domain && \
+rm -rf $FREESIDE_EXPORT/export.DBI:Pg:host=localhost\;dbname=$domain && \
+rm $FREESIDE_CONF/dbdef.DBI:Pg:host=localhost\;dbname=$domain
+
diff --git a/FS/bin/freeside-deloutsourceuser b/FS/bin/freeside-deloutsourceuser
new file mode 100644
index 0000000..dc4ff9c
--- /dev/null
+++ b/FS/bin/freeside-deloutsourceuser
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+username=$1
+
+freeside-deluser -h %%%FREESIDE_CONF%%%/htpasswd $username 2>/dev/null
+
diff --git a/FS/bin/freeside-deluser b/FS/bin/freeside-deluser
new file mode 100644
index 0000000..a2a361a
--- /dev/null
+++ b/FS/bin/freeside-deluser
@@ -0,0 +1,64 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_h);
+use Fcntl qw(:flock);
+use Getopt::Std;
+
+my $FREESIDE_CONF = "%%%FREESIDE_CONF%%%";
+
+getopts("h:");
+my $user = shift or die &usage;
+
+if ( $opt_h ) {
+ open(HTPASSWD,"<$opt_h")
+ and flock(HTPASSWD,LOCK_EX)
+ or die "can't open $opt_h: $!";
+ open(HTPASSWD_TMP,">$opt_h.tmp") or die "can't open $opt_h.tmp: $!";
+ while (<HTPASSWD>) {
+ print HTPASSWD_TMP $_ unless /^$user:/;
+ }
+ close HTPASSWD_TMP;
+ rename "$opt_h.tmp", "$opt_h" or die $!;
+ flock(HTPASSWD,LOCK_UN);
+ close HTPASSWD;
+}
+
+open(MAPSECRETS,"<$FREESIDE_CONF/mapsecrets")
+ and flock(MAPSECRETS,LOCK_EX)
+ or die "can't open $FREESIDE_CONF/mapsecrets: $!";
+open(MAPSECRETS_TMP,">>$FREESIDE_CONF/mapsecrets.tmp")
+ or die "can't open $FREESIDE_CONF/mapsecrets.tmp: $!";
+while (<MAPSECRETS>) {
+ print MAPSECRETS_TMP $_ unless /^$user\s/;
+}
+close MAPSECRETS_TMP;
+rename "$FREESIDE_CONF/mapsecrets.tmp", "$FREESIDE_CONF/mapsecrets" or die $!;
+flock(MAPSECRETS,LOCK_UN);
+close MAPSECRETS;
+
+sub usage {
+ die "Usage:\n\n freeside-deluser [ -h htpasswd_file ] username"
+}
+
+=head1 NAME
+
+freeside-deluser - Command line interface to add (freeside) users.
+
+=head1 SYNOPSIS
+
+ freeside-deluser [ -h htpasswd_file ] username
+
+=head1 DESCRIPTION
+
+Adds a user to the Freeside billing system. This is for adding users (internal
+sales/tech folks) to the web interface, not for adding customer accounts.
+
+ -h: Also delete from the given htpasswd filename
+
+=head1 SEE ALSO
+
+L<freeside-adduser>, L<htpasswd>(1), base Freeside documentation
+
+=cut
+
diff --git a/FS/bin/freeside-disable-reasons b/FS/bin/freeside-disable-reasons
new file mode 100755
index 0000000..0af4609
--- /dev/null
+++ b/FS/bin/freeside-disable-reasons
@@ -0,0 +1,64 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_t $opt_e);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::reason_type;
+use FS::reason;
+
+getopts('t:e');
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+die &usage
+ unless ($opt_t);
+
+$FS::Record::nowarn_identical = 1;
+
+my @reason = ();
+if ( $opt_t ) {
+ $opt_t =~ /^(\d+)$/ or die "invalid reason_type";
+ @reason = qsearch('reason', { reason_type => $1 } );
+ die "no reasons found\n" unless @reason;
+} else {
+ die "no reason_type specified\n";
+}
+
+foreach my $reason ( @reason ) {
+ if ( $opt_e ) {
+ $reason->disabled('');
+ }else{
+ $reason->disabled('Y');
+ }
+ my $error = $reason->replace
+ if $reason->modified;
+ die $error if $error;
+}
+
+
+sub usage {
+ die "Usage:\n\n freeside-disable-reasons -t reason_type [ -e ] user\n";
+}
+
+=head1 NAME
+
+freeside-disable-reasons - Command line tool to set the disabled column for reasons
+
+=head1 SYNOPSIS
+
+ freeside-disable-reasons -t reason_type [ -e ] user
+
+=head1 DESCRIPTION
+
+ Disables the reasons of the specified reason type.
+ Enables instead if -e is specified.
+
+=head1 SEE ALSO
+
+L<FS::reason>, L<FS::reason_type>
+
+=cut
+
diff --git a/FS/bin/freeside-email b/FS/bin/freeside-email
new file mode 100755
index 0000000..7a93f78
--- /dev/null
+++ b/FS/bin/freeside-email
@@ -0,0 +1,55 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::Record qw(qsearch);
+use FS::svc_acct;
+
+&untaint_argv; #what it sounds like (eww)
+my $user = shift or die &usage;
+
+adminsuidsetup $user;
+
+my $conf = new FS::Conf;
+
+my @svc_acct = qsearch('svc_acct', {});
+my @emails = map $_->email, @svc_acct;
+
+print join("\n", @emails), "\n";
+
+# subroutines
+
+sub untaint_argv {
+ foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
+ #$ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ # Date::Parse
+ $ARGV[$_] =~ /^(.*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ $ARGV[$_]=$1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-email user\n";
+}
+
+=head1 NAME
+
+freeside-email - Prints email addresses of all users on STDOUT
+
+=head1 SYNOPSIS
+
+ freeside-email user
+
+=head1 DESCRIPTION
+
+Prints the email addresses of all customers on STDOUT, separated by newlines.
+
+user: From the mapsecrets file - see config.html from the base documentation
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+=cut
+
diff --git a/FS/bin/freeside-expiration-alerter b/FS/bin/freeside-expiration-alerter
new file mode 100755
index 0000000..0bb61db
--- /dev/null
+++ b/FS/bin/freeside-expiration-alerter
@@ -0,0 +1,241 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use Date::Format;
+use Time::Local;
+use Text::Template;
+use Getopt::Std;
+use Net::SMTP;
+use Mail::Header;
+use Mail::Internet;
+use FS::Conf;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_main;
+
+use vars qw($smtpmachine %agent_failure_body);
+
+#hush, perl!
+$FS::alerter::_template::first = "";
+$FS::alerter::_template::last = "";
+$FS::alerter::_template::company = "";
+$FS::alerter::_template::payby = "";
+$FS::alerter::_template::expdate = "";
+
+# Set the mail program and other variables
+my $mail_sender = "billing\@mydomain.tld"; # or invoice_from if available
+my $failure_recipient = "postmaster"; # or invoice_from if available
+my $warning_time = 30 * 24 * 60 * 60;
+my $urgent_time = 15 * 24 * 60 * 60;
+my $panic_time = 5 * 24 * 60 * 60;
+my $window_time = 24 * 60 * 60;
+
+&untaint_argv; #what it sounds like (eww)
+
+#we're at now now (and later).
+my($_date)= $^T;
+
+# Get the current month
+my ($sec,$min,$hour,$mday,$mon,$year) =
+ (localtime($_date) )[0,1,2,3,4,5];
+$mon++;
+
+# Login to the database
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+# Get the needed configuration files
+my $conf = new FS::Conf;
+$smtpmachine = $conf->config('smtpmachine');
+
+my(@customers)=qsearch('cust_main',{});
+if (scalar(@customers) == 0)
+{
+ exit 1;
+}
+
+# Now I can start looping
+foreach my $customer (@customers)
+{
+ my $paydate = $customer->getfield('paydate');
+ next if $paydate =~ /^\s*$/; #skip empty expiration dates
+
+ my $custnum = $customer->getfield('custnum');
+ my $first = $customer->getfield('first');
+ my $last = $customer->getfield('last');
+ my $company = $customer->getfield('company');
+ my $payby = $customer->getfield('payby');
+ my $payinfo = $customer->getfield('payinfo');
+ my $daytime = $customer->getfield('daytime');
+ my $night = $customer->getfield('night');
+
+ my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
+
+ my $expire_time = timelocal(0,0,0,$payday,--$paymonth,$payyear);
+
+ #credit cards expire at the end of the month/year of their exp date
+ if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ ($paymonth < 11) ? $paymonth++ : ($paymonth=0, $payyear++);
+ $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
+ $expire_time--;
+ }
+
+ if (($expire_time < $_date + $warning_time &&
+ $expire_time > $_date + $warning_time - $window_time) ||
+ ($expire_time < $_date + $urgent_time &&
+ $expire_time > $_date + $urgent_time - $window_time) ||
+ ($expire_time < $_date + $panic_time &&
+ $expire_time > $_date + $panic_time - $window_time)) {
+
+ # Prepare for sending email, now inside the customer loop so i can be agent
+ # virtualized
+
+ my $agentnum = $customer->agentnum;
+
+ $mail_sender = $conf->config('invoice_from', $agentnum )
+ if $conf->exists('invoice_from', $agentnum);
+ $failure_recipient = $conf->config('invoice_from', $agentnum)
+ if $conf->exists('invoice_from', $agentnum);
+
+ $ENV{MAILADDRESS} = $mail_sender;
+
+ my @alerter_template = $conf->config('alerter_template', $agentnum)
+ or die "cannot load config file alerter_template";
+
+ my $alerter = new Text::Template TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", @alerter_template ]
+ or die "can't create new Text::Template object: $Text::Template::ERROR";
+
+ $alerter->compile() or die "can't compile template: $Text::Template::ERROR";
+
+ my @packages = $customer->ncancelled_pkgs;
+ if (scalar(@packages) != 0) {
+ my @invoicing_list = $customer->invoicing_list;
+ if ( grep { $_ ne 'POST' } @invoicing_list ) {
+ my $header = new Mail::Header ( [
+ "From: $mail_sender",
+ "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
+ "Sender: $mail_sender",
+ "Reply-To: $mail_sender",
+ "Date: ". time2str("%a, %d %b %Y %X %z", time),
+ "Subject: Billing Arrangement Expiration",
+ ] );
+ $FS::alerter::_template::first = $first;
+ $FS::alerter::_template::last = $last;
+ $FS::alerter::_template::company = $company;
+ if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ $FS::alerter::_template::payby = "credit card (" .
+ substr($payinfo, 0, 2) . "xxxxxxxxxx" .
+ substr($payinfo, -4) . ")";
+ }elsif ($payby eq 'COMP') {
+ $FS::alerter::_template::payby = "complimentary account";
+ }else{
+ $FS::alerter::_template::payby = "current method";
+ }
+ $FS::alerter::_template::expdate = $expire_time;
+
+ $FS::alerter::_template::company_name =
+ $conf->config('company_name', $agentnum);
+ $FS::alerter::_template::company_address =
+ join("\n", $conf->config('company_address', $agentnum) ). "\n";
+
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [ $alerter->fill_in( PACKAGE => 'FS::alerter::_template' ) ],
+ );
+ $!=0;
+ $message->smtpsend( Host => $smtpmachine )
+ or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
+ or die "Can't send expiration email: $!";
+
+ } elsif ( ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list ) {
+ push @{$agent_failure_body{$customer->agentnum}},
+ sprintf(qq{%5d %-32.32s %4s %10s %12s %12s},
+ $custnum,
+ $first . " " . $last . " " . $company,
+ $payby,
+ $paydate,
+ $daytime,
+ $night
+ );
+ }
+ }
+ }
+}
+
+# Now I need to send failure EMAIL
+
+foreach my $agentnum ( keys %agent_failure_body ) {
+
+ $mail_sender = $conf->config('invoice_from', $agentnum )
+ if $conf->exists('invoice_from', $agentnum);
+ $failure_recipient = $conf->config('invoice_from', $agentnum)
+ if $conf->exists('invoice_from', $agentnum);
+
+ $ENV{MAILADDRESS} = $mail_sender;
+ my $header = new Mail::Header ( [
+ "From: Account Processor",
+ "To: $failure_recipient",
+ "Sender: $mail_sender",
+ "Reply-To: $mail_sender",
+ "Subject: Unnotified Billing Arrangement Expirations",
+ ] );
+
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [ @{$agent_failure_body{$agentnum}} ],
+ );
+ $!=0;
+ $message->smtpsend( Host => $smtpmachine )
+ or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
+ or die "can't send alerter failure email to $failure_recipient".
+ " via server $smtpmachine with SMTP: $!";
+}
+
+# subroutines
+sub untaint_argv {
+ foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
+ $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal argument \"$ARGV[$_]\"";
+ $ARGV[$_]=$1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-expiration-alerter user\n";
+}
+
+=head1 NAME
+
+freeside-expiration-alerter - Emails notifications of credit card expirations.
+
+=head1 SYNOPSIS
+
+ freeside-expiration-alerter user
+
+=head1 DESCRIPTION
+
+Emails customers notice that their credit card or other billing arrangement
+is about to expire. Usually run as a cron job.
+
+user: From the mapsecrets file - see config.html from the base documentation
+
+=head1 BUGS
+
+Yes..... Use at your own risk. No guarantees or warrantees of any
+kind apply to this program. Parts of this program are hacked from
+other GNU licensed software created mainly by Ivan Kohler.
+
+This is released under the GNU Public License. See www.gnu.org
+for more information regarding this license.
+
+=head1 SEE ALSO
+
+L<FS::cust_main>, config.html from the base documentation
+
+=head1 AUTHOR
+
+Jeff Finucane <jeff@cmh.net>
+
+=cut
+
+
diff --git a/FS/bin/freeside-fetch b/FS/bin/freeside-fetch
new file mode 100755
index 0000000..7b674ed
--- /dev/null
+++ b/FS/bin/freeside-fetch
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use LWP::UserAgent;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearchs);
+use FS::Misc qw(send_email);
+
+my $user = shift or die &usage;
+my $employeelist = shift or die &usage;
+my $url = shift or die &usage;
+adminsuidsetup $user;
+
+my @employees = split ',', $employeelist;
+
+foreach my $employee (@employees) {
+
+ $employee =~ /^(\w+)$/;
+
+ my $access_user = qsearchs( 'access_user', { 'username' => $1 } );
+ unless ($access_user) {
+ warn "Can't find employee $employee... skipping";
+ next;
+ }
+
+ my $email_address = $access_user->option('email_address');
+ unless ($email_address) {
+ warn "No email address for $employee... skipping";
+ next;
+ }
+
+ no warnings 'redefine';
+ local *LWP::UserAgent::get_basic_credentials = sub {
+ return ($access_user->username, $access_user->_password);
+ };
+
+ my $ua = new LWP::UserAgent;
+ $ua->timeout(1800); #30m, some reports can take a while
+ $ua->agent("FreesideFetcher/0.1 " . $ua->agent);
+
+ my $req = new HTTP::Request GET => $url;
+ my $res = $ua->request($req);
+
+ my $conf = new FS::Conf;
+ my $subject = $conf->config('email_report-subject') || 'Freeside report';
+
+ my %options = ( 'from' => $email_address,
+ 'to' => $email_address,
+ 'subject' => $subject,
+ 'body' => $res->content,
+ );
+
+ $options{'content-type'} = $res->content_type
+ if $res->content_type;
+ $options{'content-encoding'} = $res->content_encoding
+ if $res->content_encoding;
+
+ if ($res->is_success) {
+ send_email %options;
+ }else{
+ warn "fetching $url failed for $employee: " . $res->status_line;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-fetch user employee[,employee ...] url\n\n";
+}
+
+=head1 NAME
+
+freeside-fetch - Send a freeside page to a list of employees.
+
+=head1 SYNOPSIS
+
+ freeside-fetch user employee[,employee ...] url
+
+=head1 DESCRIPTION
+
+ Fetches a web page specified by url as if employee and emails it to
+ employee. Useful when run out of cron to send freeside web pages.
+
+ user: From the mapsecrets file - a user with access to the freeside database
+
+ employee: the username of an employee to receive the emailed page. May be a comma separated list
+
+ url: the web page to be received
+
+=head1 BUGS
+
+ Can leak employee usernames and passwords if requested to access inappropriate urls.
+
+=cut
+
diff --git a/FS/bin/freeside-history-requeue b/FS/bin/freeside-history-requeue
new file mode 100755
index 0000000..77a4332
--- /dev/null
+++ b/FS/bin/freeside-history-requeue
@@ -0,0 +1,100 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_j $opt_d);
+use Getopt::Std;
+use Date::Parse;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::queue;
+
+getopts('j:d');
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $start = shift or die &usage;
+my $end = shift or die &usage;
+
+$start = str2time($start) unless $start =~ /^(\d+)$/;
+$end = str2time($end) unless $end =~ /^(\d+)$/;
+
+my $extra_sql = "AND history_date >= $start AND history_date <= $end";
+
+my $hashref = { 'history_action' => 'insert' };
+
+$hashref->{'job'} = $opt_j if $opt_j;
+
+my @h_queue = qsearch({
+ 'table' => 'h_queue',
+ 'hashref' => $hashref,
+ 'extra_sql' => $extra_sql,
+});
+
+my $num = 0;
+
+foreach my $h_queue (@h_queue) {
+
+ my @queue_args = qsearch({
+ 'table' => 'h_queue_arg',
+ 'hashref' => { 'history_action' => 'insert',
+ 'jobnum' => $h_queue->jobnum,
+ },
+ 'order_by' => 'argnum',
+ });
+
+ my @args = map {
+ my $arg = $_->arg;
+ $arg =~ s/^db\.suicidegirls\.com$/sg-account/;
+ $arg;
+ } @queue_args;
+
+ my $queue = new FS::queue {
+ map { $_ => $h_queue->$_() }
+ qw( job _date status statustext svcnum )
+ };
+
+ if ( $opt_d ) { #dry run
+ print "requeueing job: ". join(' ', @args). "\n";
+ my $error = $queue->check;
+ die "error requeueing job ". $h_queue->jobnum. ": $error" if $error;
+ } else {
+ print "requeueing job: ". join(' ', @args). "\n";
+ my $error = $queue->insert(@args);
+ #warn "error requeueing job ". $h_queue->jobnum. ": $error\n" if $error;
+ print "error requeueing job ". $h_queue->jobnum. ": $error\n" if $error;
+ }
+
+ $num++;
+
+}
+
+print "requeued $num jobs\n";
+
+sub usage {
+ die "Usage:\n\n freeside-history-requeue user start_timestamp end_timestamp\n";
+}
+
+=head1 NAME
+
+freeside-history-requeue - Command line tool to re-trigger export jobs for existing services
+
+=head1 SYNOPSIS
+
+ freeside-history-requeue [ -j job ] [ -d ] user start_timestamp end_timestamp
+
+=head1 DESCRIPTION
+
+ Re-queues all queued jobs for the specified time period.
+
+ -j: specifies that only jobs with this job string are re-queued.
+
+ -d: dry run
+
+=head1 SEE ALSO
+
+L<freeside-reexport>, L<freeside-sqlradius-reset>, L<FS::part_export>
+
+=cut
+
+1;
diff --git a/FS/bin/freeside-init-config b/FS/bin/freeside-init-config
new file mode 100755
index 0000000..fe4729c
--- /dev/null
+++ b/FS/bin/freeside-init-config
@@ -0,0 +1,45 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw($opt_u $opt_f $opt_v);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup checkeuid dbh);
+use FS::CurrentUser;
+use FS::Record qw(qsearch);
+use FS::Conf;
+
+
+die "Not running uid freeside!" unless checkeuid();
+
+getopts("u:vf");
+my $dir = shift or die &usage;
+
+$FS::CurrentUser::upgrade_hack = 1;
+$FS::UID::AutoCommit = 0;
+$FS::UID::callback_hack = 1;
+adminsuidsetup $opt_u; #$user;
+
+$|=1;
+
+if (!scalar(qsearch('conf', {})) || $opt_f) {
+ my $error = FS::Conf::init_config($dir);
+ if ($error) {
+ warn "CONFIGURATION INITIALIZATION FAILED\n";
+ dbh->rollback or die dbh->errstr;
+ die $error if $error;
+ }
+}
+
+warn "Freeside database initialized - committing transaction\n" if $opt_v;
+
+dbh->commit or die dbh->errstr;
+dbh->disconnect or die dbh->errstr;
+
+warn "Configuration initialization committed successfully\n" if $opt_v;
+
+sub usage {
+ die "Usage:\n freeside-init-config [ -v ] [ -f ] directory\n"
+ # [ -u user ] for devel/multi-db installs
+}
+
+1;
diff --git a/FS/bin/freeside-monthly b/FS/bin/freeside-monthly
new file mode 100755
index 0000000..1e41b78
--- /dev/null
+++ b/FS/bin/freeside-monthly
@@ -0,0 +1,91 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+
+&untaint_argv; #what it sounds like (eww)
+#use vars qw($opt_d $opt_v $opt_p $opt_a $opt_s $opt_y);
+use vars qw(%opt);
+getopts("p:a:d:vsy:", \%opt);
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+use FS::Cron::bill qw(bill);
+bill(%opt, 'check_freq'=>'1m' );
+
+###
+# subroutines
+###
+
+sub untaint_argv {
+ foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
+ #$ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ # Date::Parse
+ $ARGV[$_] =~ /^(.*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ $ARGV[$_]=$1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-monthly [ -d 'date' ] user [ custnum custnum ... ]\n";
+}
+
+###
+# documentation
+###
+
+=head1 NAME
+
+freeside-monthly - Run monthly billing and invoice collection events.
+
+=head1 SYNOPSIS
+
+ freeside-monthly [ -d 'date' ] [ -y days ] [ -p 'payby' ] [ -a agentnum ] [ -s ] [ -v ] user [ custnum custnum ... ]
+
+=head1 DESCRIPTION
+
+Bills customers and runs invoice collection events, for the alternate monthly
+event chain. If you have defined monthly event checks, should be run from
+crontab monthly.
+
+Bills customers. Searches for customers who are due for billing and calls
+the bill and collect methods of a cust_main object. See L<FS::cust_main>.
+
+ -d: Pretend it's 'date'. Date is in any format Date::Parse is happy with,
+ but be careful.
+
+ -y: In addition to -d, which specifies an absolute date, the -y switch
+ specifies an offset, in days. For example, "-y 15" would increment the
+ "pretend date" 15 days from whatever was specified by the -d switch
+ (or now, if no -d switch was given).
+
+ -p: Only process customers with the specified payby (I<CARD>, I<DCRD>, I<CHEK>, I<DCHK>, I<BILL>, I<COMP>, I<LECB>)
+
+ -a: Only process customers with the specified agentnum
+
+ -s: re-charge setup fees
+
+ -v: enable debugging
+
+user: From the mapsecrets file - see config.html from the base documentation
+
+custnum: if one or more customer numbers are specified, only bills those
+customers. Otherwise, bills all customers.
+
+=head1 NOTE
+
+In most cases, you would use freeside-daily only and not freeside-monthly.
+freeside-monthly would only be used in cases where you have events that can
+only be run once each month, for example, batching invoices to a third-party
+print/mail provider.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<freeside-daily>, L<FS::cust_main>, config.html from the base documentation
+
+=cut
+
diff --git a/FS/bin/freeside-prepaidd b/FS/bin/freeside-prepaidd
new file mode 100644
index 0000000..86bfe87
--- /dev/null
+++ b/FS/bin/freeside-prepaidd
@@ -0,0 +1,106 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm);
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::cust_pkg;
+
+my $user = shift or die &usage;
+
+#daemonize1('freeside-sprepaidd', $user); #keep unique pid files w/multi installs
+daemonize1('freeside-prepaidd');
+
+drop_root();
+
+adminsuidsetup($user);
+
+logfile( "%%%FREESIDE_LOG%%%/prepaidd-log.". $FS::UID::datasrc );
+
+daemonize2();
+
+#--
+
+while (1) {
+
+ foreach my $cust_pkg (
+ qsearch( {
+ 'select' => 'cust_pkg.*, part_pkg.plan',
+ 'table' => 'cust_pkg',
+ 'addl_from' => 'LEFT JOIN part_pkg USING ( pkgpart )',
+ #'hashref' => { 'plan' => 'prepaid' },#should check part_pkg::is_prepaid
+ #'extra_sql' => "AND bill < ". time.
+ 'hashref' => {},
+ 'extra_sql' => "WHERE plan = 'prepaid' AND bill < ". time.
+ " AND bill IS NOT NULL".
+ " AND ( susp IS NULL OR susp = 0)".
+ " AND ( cancel IS NULL OR cancel = 0)"
+ } )
+ ) {
+
+ my $work_cust_pkg = $cust_pkg;
+
+ my $cust_main = $cust_pkg->cust_main;
+ if ( $cust_main->total_unapplied_payments > 0
+ or $cust_main->total_credited > 0
+ )
+ {
+ #this needs a flag to say only do the prepaid packages...
+ # and only try em if the renewal price matches.. but this will do for now
+ my $b_error = $cust_main->bill;
+ if ( $b_error ) {
+ warn "Error billing customer #". $cust_main->custnum;
+ next;
+ }
+ $b_error = $cust_main->apply_payments_and_credits;
+ if ( $b_error ) {
+ warn "Error applying payments&credits, customer #". $cust_main->custnum;
+ next;
+ }
+
+ $work_cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $work_cust_pkg->pkgnum } );
+
+ next
+ if $cust_main->balance <= 0
+ and $work_cust_pkg->bill >= time;
+ }
+
+ my $action = $work_cust_pkg->part_pkg->option('recur_action') || 'suspend';
+
+ my $error = $work_cust_pkg->$action();
+
+ warn "Error ${action}ing package ". $work_cust_pkg->pkgnum.
+ " for custnum ". $work_cust_pkg->custnum.
+ ": $error\n"
+ if $error;
+ }
+
+ die "exiting" if sigterm() || sigint();
+ sleep 5;
+
+}
+
+#--
+
+sub usage {
+ die "Usage:\n\n freeside-prepaidd user\n";
+}
+
+=head1 NAME
+
+freeside-prepaidd - Real-time daemon for prepaid packages
+
+=head1 SYNOPSIS
+
+ freeside-prepaidd
+
+=head1 DESCRIPTION
+
+Runs continuously and suspends or cancels any prepaid customer packages which
+have passed their renewal date (next bill date).
+
+=head1 SEE ALSO
+
+=cut
+
+1;
diff --git a/FS/bin/freeside-prune-applications b/FS/bin/freeside-prune-applications
new file mode 100755
index 0000000..d2b6efe
--- /dev/null
+++ b/FS/bin/freeside-prune-applications
@@ -0,0 +1,63 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_d $opt_q $opt_v); # $opt_n instead of $opt_d?
+use vars qw($DEBUG $DRY_RUN);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup checkeuid);
+use FS::Misc::prune qw(prune_applications);
+
+die "Not running uid freeside!" unless checkeuid();
+
+getopts("dq");
+
+$DEBUG = !$opt_q;
+#$DEBUG = $opt_v;
+
+$DRY_RUN = $opt_d;
+
+my $user = shift or die &usage;
+my $dbh = adminsuidsetup($user);
+
+my $hashref = {};
+
+$hashref->{dry_run} = 1 if $DRY_RUN;
+$hashref->{debug} = 1 if $DEBUG;
+
+print join "\n", prune_applications($hashref);
+print "\n" if $DRY_RUN;
+
+$dbh->commit or die $dbh->errstr;
+
+###
+
+sub usage {
+ die "Usage:\n freeside-prune-applications [ -d ] [ -q | -v ] user\n";
+}
+
+=head1 NAME
+
+freeside-prune-applications - Removes stray applications of credit, payment to
+ bills, refunds, etc.
+
+=head1 SYNOPSIS
+
+ freeside-prune-applications [ -d ] [ -q | -v ]
+
+=head1 DESCRIPTION
+
+Reads your existing database schema and updates it to match the current schema,
+adding any columns or tables necessary.
+
+ [ -d ]: Dry run; display affected records (to STDOUT) only, but do not
+ remove them.
+
+ [ -q ]: Run quietly. This may become the default at some point.
+
+ [ -v ]: Run verbosely, sending debugging information to STDERR. This is the
+ current default.
+
+=head1 SEE ALSO
+
+=cut
+
diff --git a/FS/bin/freeside-queued b/FS/bin/freeside-queued
new file mode 100644
index 0000000..d4f09c1
--- /dev/null
+++ b/FS/bin/freeside-queued
@@ -0,0 +1,239 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( $DEBUG $kids $max_kids %kids );
+use POSIX qw(:sys_wait_h);
+use IO::File;
+use FS::UID qw(adminsuidsetup forksuidsetup driver_name dbh myconnect);
+use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm);
+use FS::Record qw(qsearch qsearchs);
+use FS::queue;
+use FS::queue_depend;
+
+# no autoloading for non-FS classes...
+use Net::SSH 0.07;
+
+$DEBUG = 0;
+
+$max_kids = '10'; #guess it should be a config file...
+$kids = 0;
+
+my $user = shift or die &usage;
+
+warn "starting daemonization (forking)\n" if $DEBUG;
+#daemonize1('freeside-queued',$user); #to keep pid files unique w/multi installs
+daemonize1('freeside-queued');
+
+warn "dropping privledges\n" if $DEBUG;
+drop_root();
+
+
+$ENV{HOME} = (getpwuid($>))[7]; #for ssh
+
+warn "connecting to database\n" if $DEBUG;
+$@ = 'not connected';
+while ( $@ ) {
+ eval { adminsuidsetup $user; };
+ if ( $@ ) {
+ warn $@;
+ warn "sleeping for reconnect...\n";
+ sleep 5;
+ }
+}
+
+logfile( "%%%FREESIDE_LOG%%%/queuelog.". $FS::UID::datasrc );
+
+warn "completing daemonization (detaching))\n" if $DEBUG;
+daemonize2();
+
+#--
+
+my $warnkids=0;
+while (1) {
+
+ &reap_kids;
+ #prevent runaway forking
+ if ( $kids >= $max_kids ) {
+ warn "WARNING: maximum $kids children reached\n" unless $warnkids++;
+ &reap_kids;
+ sleep 1; #waiting for signals is cheap
+ next;
+ }
+ $warnkids=0;
+
+ unless ( dbh && dbh->ping ) {
+ warn "WARNING: connection to database lost, reconnecting...\n";
+
+ eval { $FS::UID::dbh = myconnect; };
+
+ unless ( !$@ && dbh && dbh->ping ) {
+ warn "WARNING: still no connection to database, sleeping for retry...\n";
+ sleep 10;
+ next;
+ } else {
+ warn "WARNING: reconnected to database\n";
+ }
+ }
+
+ #my($job, $ljob);
+ #{
+ # my $oldAutoCommit = $FS::UID::AutoCommit;
+ # local $FS::UID::AutoCommit = 0;
+ $FS::UID::AutoCommit = 0;
+
+ my $nodepend = 'AND 0 = ( SELECT COUNT(*) FROM queue_depend'.
+ ' WHERE queue_depend.jobnum = queue.jobnum )';
+
+ my $order_by = "ORDER BY jobnum ". ( driver_name eq 'mysql'
+ ? 'LIMIT 1 FOR UPDATE'
+ : 'FOR UPDATE LIMIT 1' );
+
+ my $job = qsearchs({
+ 'table' => 'queue',
+ 'hashref' => { 'status' => 'new' },
+ 'extra_sql' => $nodepend,
+ 'order_by' => $order_by,
+ }) or do {
+ # if $oldAutoCommit {
+ dbh->commit or do {
+ warn "WARNING: database error, closing connection: ". dbh->errstr;
+ undef $FS::UID::dbh;
+ next;
+ };
+ # }
+ sleep 1;
+ next;
+ };
+
+ my %hash = $job->hash;
+ $hash{'status'} = 'locked';
+ my $ljob = new FS::queue ( \%hash );
+ my $error = $ljob->replace($job);
+ if ( $error ) {
+ warn "WARNING: database error locking job, closing connection: ".
+ dbh->errstr;
+ undef $FS::UID::dbh;
+ next;
+ }
+
+ # if $oldAutoCommit {
+ dbh->commit or do {
+ warn "WARNING: database error, closing connection: ". dbh->errstr;
+ undef $FS::UID::dbh;
+ next;
+ };
+ # }
+
+ $FS::UID::AutoCommit = 1;
+ #}
+
+ my @args = $ljob->args;
+ splice @args, 0, 1, $ljob if $args[0] eq '_JOB';
+
+ defined( my $pid = fork ) or do {
+ warn "WARNING: can't fork: $!\n";
+ my %hash = $job->hash;
+ $hash{'status'} = 'failed';
+ $hash{'statustext'} = "[freeside-queued] can't fork: $!";
+ my $ljob = new FS::queue ( \%hash );
+ my $error = $ljob->replace($job);
+ die $error if $error;
+ next; #don't increment the kid counter
+ };
+
+ if ( $pid ) {
+ $kids++;
+ $kids{$pid} = 1;
+ } else { #kid time
+
+ #get new db handle
+ $FS::UID::dbh->{InactiveDestroy} = 1;
+
+ forksuidsetup($user);
+
+ #auto-use classes...
+ #if ( $ljob->job =~ /(FS::part_export::\w+)::/ ) {
+ if ( $ljob->job =~ /(FS::(part_export|cust_main)::\w+)::/
+ || $ljob->job =~ /(FS::\w+)::/
+ )
+ {
+ my $class = $1;
+ eval "use $class;";
+ if ( $@ ) {
+ warn "job use $class failed";
+ my %hash = $ljob->hash;
+ $hash{'status'} = 'failed';
+ $hash{'statustext'} = $@;
+ my $fjob = new FS::queue( \%hash );
+ my $error = $fjob->replace($ljob);
+ die $error if $error;
+ exit; #end-of-kid
+ };
+ }
+
+ my $eval = "&". $ljob->job. '(@args);';
+ warn 'running "&'. $ljob->job. '('. join(', ', @args). ")\n" if $DEBUG;
+ eval $eval; #throw away return value? suppose so
+ if ( $@ ) {
+ warn "job $eval failed";
+ my %hash = $ljob->hash;
+ $hash{'status'} = 'failed';
+ $hash{'statustext'} = $@;
+ my $fjob = new FS::queue( \%hash );
+ my $error = $fjob->replace($ljob);
+ die $error if $error;
+ } else {
+ $ljob->delete;
+ }
+
+ exit;
+ #end-of-kid
+ }
+
+} continue {
+ if ( sigterm() ) {
+ warn "received TERM signal; exiting\n";
+ exit;
+ }
+ if ( sigint() ) {
+ warn "received INT signal; exiting\n";
+ exit;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-queued user\n";
+}
+
+sub reap_kids {
+ foreach my $pid ( keys %kids ) {
+ my $kid = waitpid($pid, WNOHANG);
+ if ( $kid > 0 ) {
+ $kids--;
+ delete $kids{$kid};
+ }
+ }
+}
+
+=head1 NAME
+
+freeside-queued - Job queue daemon
+
+=head1 SYNOPSIS
+
+ freeside-queued user
+
+=head1 DESCRIPTION
+
+Job queue daemon. Should be running at all times.
+
+user: from the mapsecrets file - see config.html from the base documentation
+
+=head1 VERSION
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+=cut
+
diff --git a/FS/bin/freeside-radgroup b/FS/bin/freeside-radgroup
new file mode 100644
index 0000000..ed85626
--- /dev/null
+++ b/FS/bin/freeside-radgroup
@@ -0,0 +1,76 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_svc;
+use FS::svc_acct;
+
+&untaint_argv; #what it sounds like (eww)
+
+my($user, $action, $groupname, $svcpart) = @ARGV;
+
+adminsuidsetup $user;
+
+my @svc_acct = map { $_->svc_x } qsearch('cust_svc', { svcpart => $svcpart } );
+
+if ( lc($action) eq 'add' ) {
+ foreach my $svc_acct ( @svc_acct ) {
+ my @groups = $svc_acct->radius_groups;
+ next if grep { $_ eq $groupname } @groups;
+ push @groups, $groupname;
+ my %hash = $svc_acct->hash;
+ $hash{usergroup} = \@groups;
+ my $new = new FS::svc_acct \%hash;
+ my $error = $new->replace($svc_acct);
+ die $error if $error;
+ }
+} else {
+ die &usage;
+}
+
+# subroutines
+
+sub untaint_argv {
+ foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
+ $ARGV[$_] =~ /^(.*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ $ARGV[$_]=$1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-radgroup user action groupname svcpart\n";
+}
+
+=head1 NAME
+
+freeside-radgroup - Command line utility to manipulate radius groups
+
+=head1 SYNOPSIS
+
+ freeside-addgroup user action groupname svcpart
+
+=head1 DESCRIPTION
+
+ B<user> is a freeside user as added with freeside-adduser.
+
+ B<command> is the action to take. Available actions are: I<add>
+
+ B<groupname> is the group to add (or remove, etc.)
+
+ B<svcpart> specifies which accounts will be updated.
+
+=head1 EXAMPLES
+
+freeside-radgroup freesideuser add groupname 3
+
+Adds I<groupname> to all accounts with service definition 3.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<freeside-adduser>, L<FS::svc_acct>, L<FS::part_svc>
+
+=cut
+
diff --git a/FS/bin/freeside-reexport b/FS/bin/freeside-reexport
new file mode 100644
index 0000000..54af9dd
--- /dev/null
+++ b/FS/bin/freeside-reexport
@@ -0,0 +1,71 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_s $opt_u $opt_p);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+use FS::svc_acct;
+use FS::cust_svc;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $export_x = shift or die &usage;
+my @part_export;
+if ( $export_x =~ /^(\d+)$/ ) {
+ @part_export = qsearchs('part_export', { exportnum=>$1 } )
+ or die "exportnum $export_x not found\n";
+} else {
+ @part_export = qsearch('part_export', { exporttype=>$export_x } )
+ or die "no exports of type $export_x found\n";
+}
+
+getopts('s:u:p:');
+
+my @svc_x = ();
+if ( $opt_s ) {
+ my $cust_svc = qsearchs('cust_svc', { svcnum=>$opt_s } )
+ or die "svcnum $opt_s not found\n";
+ push @svc_x, $cust_svc->svc_x;
+} elsif ( $opt_u ) {
+ my $svc_x = qsearchs('svc_acct', { username=>$opt_u } )
+ or die "username $opt_u not found\n";
+ push @svc_x, $svc_x;
+} elsif ( $opt_p ) {
+ push @svc_x, map { $_->svc_x } qsearch('cust_svc', { svcpart=>$opt_p } );
+ die "no services with svcpart $opt_p found\n" unless @svc_x;
+}
+
+foreach my $part_export ( @part_export ) {
+ foreach my $svc_x ( @svc_x ) {
+ my $error = $part_export->export_insert($svc_x);
+ die $error if $error;
+ }
+}
+
+
+sub usage {
+ die "Usage:\n\n freeside-reexport user exportnum|exporttype [ -s svcnum | -u username | -p svcpart ]\n";
+}
+
+=head1 NAME
+
+freeside-reexport - Command line tool to re-trigger export jobs for existing services
+
+=head1 SYNOPSIS
+
+ freeside-reexport user exportnum|exporttype [ -s svcnum | -u username | -p svcpart ]
+
+=head1 DESCRIPTION
+
+ Re-queues the export job for the specified exportnum or exporttype(s) and
+ specified service (selected by svcnum or username).
+
+=head1 SEE ALSO
+
+L<freeside-sqlradius-reset>, L<FS::part_export>
+
+=cut
+
diff --git a/FS/bin/freeside-reset-fixed b/FS/bin/freeside-reset-fixed
new file mode 100755
index 0000000..5829d44
--- /dev/null
+++ b/FS/bin/freeside-reset-fixed
@@ -0,0 +1,69 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_p $opt_s $opt_r);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::cust_svc;
+use FS::svc_Common;
+
+getopts('p:s:r');
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+die &usage
+ if ($opt_p && $opt_s);
+
+$FS::Record::nowarn_identical = 1;
+$FS::svc_Common::noexport_hack = 1
+ unless $opt_r;
+
+my @svc_x = ();
+if ( $opt_s ) {
+ $opt_s =~ /^(\d+)$/ or die "invalid svcnum";
+ my $cust_svc = qsearchs('cust_svc', { svcnum => $1 } )
+ or die "svcnum $opt_s not found\n";
+ push @svc_x, $cust_svc->svc_x;
+} elsif ( $opt_p ) {
+ $opt_p =~ /^(\d+)$/ or die "invalid svcpart";
+ push @svc_x, map { $_->svc_x } qsearch('cust_svc', { svcpart => $1 } );
+ die "no services with svcpart $opt_p found\n" unless @svc_x;
+} else {
+ push @svc_x, map { $_->svc_x } qsearch('cust_svc', {} );
+ die "no services found\n" unless @svc_x;
+}
+
+foreach my $svc_x ( @svc_x ) {
+ my $result = $svc_x->setfixed;
+ die $result unless ref($result);
+ my $error = $svc_x->replace
+ if $svc_x->modified;
+ die $error if $error;
+}
+
+
+sub usage {
+ die "Usage:\n\n freeside-reset-fixed user [ -s svcnum | -p svcpart ] [ -r ]\n";
+}
+
+=head1 NAME
+
+freeside-reset-fixed - Command line tool to set the fixed columns for existing services
+
+=head1 SYNOPSIS
+
+ freeside-reset-fixed user [ -s svcnum | -p svcpart ] [ -r ]
+
+=head1 DESCRIPTION
+
+ Resets the fixed columns for the specified service part or service number.
+ Re-exports the service if -r is specified.
+
+=head1 SEE ALSO
+
+L<freeside-reexport>, L<FS::part_svc>
+
+=cut
+
diff --git a/FS/bin/freeside-selfservice-server b/FS/bin/freeside-selfservice-server
new file mode 100644
index 0000000..2087e71
--- /dev/null
+++ b/FS/bin/freeside-selfservice-server
@@ -0,0 +1,240 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( $FREESIDE_LOG $FREESIDE_LOCK );
+use vars qw( $Debug %kids $kids $max_kids $ssh_pid %old_ssh_pid $keepalives );
+use subs qw( lock_write unlock_write myshutdown usage );
+use Fcntl qw(:flock);
+use POSIX qw(:sys_wait_h);
+use IO::Handle;
+use IO::Select;
+use IO::File;
+use Storable 2.09 qw(nstore_fd fd_retrieve);
+use Net::SSH qw(sshopen2);
+use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm);
+use FS::UID qw(adminsuidsetup forksuidsetup);
+use FS::ClientAPI;
+use FS::ClientAPI_SessionCache;
+
+use FS::Conf;
+use FS::cust_svc;
+
+$FREESIDE_LOG = "%%%FREESIDE_LOG%%%";
+$FREESIDE_LOCK = "%%%FREESIDE_LOCK%%%";
+
+$Debug = 1; # 2 will turn on more logging
+ # 3 will log packet contents, including passwords
+
+$max_kids = '10'; #?
+$keepalives = 0; #let clientd turn it on, so we don't barf on old ones
+$kids = 0;
+
+my $user = shift or die &usage;
+my $machine = shift or die &usage;
+my $tag = scalar(@ARGV) ? shift : '';
+
+my $lock_file = "$FREESIDE_LOCK/selfservice.$machine.writelock";
+
+# to keep pid files unique w/multi machines (and installs!)
+# $FS::UID::datasrc not posible
+daemonize1("freeside-selfservice-server","$user.$machine");
+
+#false laziness w/Daemon::drop_root
+my $freeside_gid = scalar(getgrnam('freeside'))
+ or die "can't find freeside group\n";
+
+open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
+chown $FS::UID::freeside_uid, $freeside_gid, $lock_file;
+
+drop_root();
+
+$ENV{HOME} = (getpwuid($>))[7]; #for ssh
+
+adminsuidsetup $user;
+
+#logfile("/usr/local/etc/freeside/selfservice.". $FS::UID::datasrc); #MACHINE
+logfile("$FREESIDE_LOG/selfservice.$machine.log");
+
+daemonize2();
+
+my $conf = new FS::Conf;
+if ( $conf->exists('selfservice-ignore_quantity') ) {
+ $FS::cust_svc::ignore_quantity = 1;
+ $FS::cust_svc::ignore_quantity = 1; #now it is used twice.
+}
+
+#clear the signup info cache so an "/etc/init.d/freeside restart" will pick
+#up new info... (better as a callback in Signup.pm?)
+my $cache = new FS::ClientAPI_SessionCache( {
+ 'namespace' => 'FS::ClientAPI::Signup',
+} );
+$cache->remove('signup_info_cache');
+
+my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name?
+
+my $warnkids=0;
+while (1) {
+ my($writer,$reader,$error) = (new IO::Handle, new IO::Handle, new IO::Handle);
+ warn "connecting to $machine\n" if $Debug;
+
+ $ssh_pid = sshopen2($machine,$reader,$writer,$clientd,$tag);
+
+# nstore_fd(\*writer, {'hi'=>'there'});
+
+ warn "entering main loop\n" if $Debug;
+ my $undisp = 0;
+ my $keepalive_count = 0;
+ my $s = IO::Select->new( $reader );
+ while (1) {
+
+ &reap_kids;
+
+ warn "waiting for packet from client\n" if $Debug && !$undisp;
+ $undisp = 1;
+ my @handles = $s->can_read(5);
+ unless ( @handles ) {
+ myshutdown() if sigint() || sigterm();
+ if ( $keepalives && $keepalive_count++ > 10 ) {
+ $keepalive_count = 0;
+ lock_write;
+ nstore_fd( { _token => '_keepalive' }, $writer );
+ unlock_write;
+ }
+ next;
+ }
+
+ $undisp = 0;
+
+ warn "receiving packet from client\n" if $Debug;
+
+ my $packet = eval { fd_retrieve($reader); };
+ if ( $@ ) {
+ warn "Storable error receiving packet from client".
+ " (assuming lost connection): $@\n"
+ if $Debug;
+ if ( $ssh_pid ) {
+ warn "sending TERM signal to ssh process $ssh_pid\n" if $Debug;
+ kill 'TERM', $ssh_pid;
+ $old_ssh_pid{$ssh_pid} = 1;
+ $ssh_pid = 0;
+ }
+ last;
+ }
+ warn "packet received\n".
+ join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
+ if $Debug > 2;
+
+ if ( $packet->{_packet} eq '_enable_keepalive' ) {
+ warn "enabling keep alives\n" if $Debug;
+ $keepalives=1;
+ next;
+ }
+
+ #prevent runaway forking
+ my $warnkids = 0;
+ while ( $kids >= $max_kids ) {
+ warn "WARNING: maximum $kids children reached\n" unless $warnkids++;
+ &reap_kids;
+ sleep 1;
+ }
+
+ warn "forking child\n" if $Debug;
+ defined( my $pid = fork ) or die "can't fork: $!";
+ if ( $pid ) {
+ $kids++;
+ $kids{$pid} = 1;
+ warn "child $pid spawned\n" if $Debug;
+ } else { #kid time
+
+ ##get new db handle
+ $FS::UID::dbh->{InactiveDestroy} = 1;
+ forksuidsetup($user);
+
+ #get db handle
+ #adminsuidsetup($user);
+
+ my $type = $packet->{_packet};
+ warn "calling $type handler\n" if $Debug;
+ my $rv = eval { FS::ClientAPI->dispatch($type, $packet); };
+ if ( $@ ) {
+ warn my $error = "WARNING: error dispatching $type: $@";
+ $rv = { _error => $error };
+ }
+ $rv->{_token} = $packet->{_token}; #identifier
+
+ open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
+ lock_write;
+ warn "sending response\n" if $Debug;
+ nstore_fd($rv, $writer) or die "FATAL: can't send response: $!";
+ $writer->flush or die "FATAL: can't flush: $!";
+ unlock_write;
+
+ warn "child exiting\n" if $Debug;
+ exit; #end-of-kid
+ }
+
+ }
+
+ myshutdown if sigint() || sigterm();
+ warn "connection lost, reconnecting\n" if $Debug;
+ sleep 3;
+
+}
+
+###
+# utility subroutines
+###
+
+sub reap_kids {
+ #warn "reaping kids\n";
+ foreach my $pid ( keys %kids ) {
+ my $kid = waitpid($pid, WNOHANG);
+ if ( $kid > 0 ) {
+ $kids--;
+ delete $kids{$kid};
+ }
+ }
+
+ foreach my $pid ( keys %old_ssh_pid ) {
+ waitpid($pid, WNOHANG) and delete $old_ssh_pid{$pid};
+ }
+ #warn "done reaping\n";
+}
+
+sub myshutdown {
+ &reap_kids;
+ my $wait = 12; #wait up to 1 minute
+ while ( $kids > 0 && $wait-- ) {
+ warn "waiting for $kids children to terminate";
+ sleep 5;
+ &reap_kids;
+ }
+ warn "abandoning $kids children" if $kids;
+ kill 'TERM', $ssh_pid if $ssh_pid;
+ die "exiting";
+}
+
+sub lock_write {
+ warn "locking $lock_file mutex for write to write stream\n" if $Debug > 1;
+
+ #broken on freebsd?
+ #flock($writer, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+
+ flock(LOCKFILE, LOCK_EX) or die "FATAL: can't lock $lock_file: $!";
+
+}
+
+sub unlock_write {
+ warn "unlocking $lock_file mutex\n" if $Debug > 1;
+
+ #broken on freebsd?
+ #flock($writer, LOCK_UN) or die "WARNING: can't release write lock: $!";
+
+ flock(LOCKFILE, LOCK_UN) or die "FATAL: can't unlock $lock_file: $!";
+
+}
+
+sub usage {
+ die "Usage:\n\n freeside-selfservice-server user machine\n";
+}
+
diff --git a/FS/bin/freeside-setinvoice b/FS/bin/freeside-setinvoice
new file mode 100644
index 0000000..708e2fa
--- /dev/null
+++ b/FS/bin/freeside-setinvoice
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::Record qw(qsearch qsearchs);
+use FS::cust_main;
+use FS::svc_acct;
+
+&untaint_argv; #what it sounds like (eww)
+my $user = shift or die &usage;
+
+adminsuidsetup $user;
+
+foreach my $cust_main (
+ grep { ! scalar($_->invoicing_list) }
+ qsearch( 'cust_main', {} )
+) {
+ my @dest;
+ my @cust_pkg = $cust_main->ncancelled_pkgs;
+ foreach my $cust_pkg ( @cust_pkg ) {
+ foreach my $cust_svc ( $cust_pkg->cust_svc ) {
+ my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $cust_svc->svcnum } );
+ push @dest, $svc_acct->svcnum if $svc_acct;
+ }
+ }
+ push @dest, 'POST' unless @dest;
+ $cust_main->invoicing_list(\@dest);
+}
+
+sub untaint_argv {
+ foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
+ $ARGV[$_] =~ /^(.*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ $ARGV[$_]=$1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-setinvoice user\n";
+}
+
+
diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup
new file mode 100755
index 0000000..ddff81e
--- /dev/null
+++ b/FS/bin/freeside-setup
@@ -0,0 +1,165 @@
+#!/usr/bin/perl -Tw
+
+#to delay loading dbdef until we're ready
+BEGIN { $FS::Schema::setup_hack = 1; }
+
+#to allow initial insert
+use FS::part_pkg;
+$FS::part_pkg::setup_hack = 1;
+$FS::part_pkg::setup_hack = 1;
+
+use strict;
+use vars qw($opt_u $opt_d $opt_v);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup datasrc checkeuid getsecrets);
+use FS::CurrentUser;
+use FS::Schema qw( dbdef_dist reload_dbdef );
+use FS::Record qw( qsearch );
+#use FS::raddb;
+use FS::Setup qw(create_initial_data);
+use FS::Conf;
+
+die "Not running uid freeside!" unless checkeuid();
+
+#my %attrib2db =
+# map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib;
+
+getopts("u:vd:");
+my $config_dir = shift || '%%%DIST_CONF%%%' ;
+$config_dir =~ /^([\w.:=\/]+)$/
+ or die "unacceptable configuration directory name";
+$config_dir = $1;
+
+getsecrets($opt_u);
+
+#needs to match FS::Record
+my($dbdef_file) = "%%%FREESIDE_CONF%%%/dbdef.". datasrc;
+
+###
+
+my $username_len = 32;
+
+#print "\n\n", <<END, ":";
+#Freeside tracks the RADIUS User-Name, check attribute Password and
+#reply attribute Framed-IP-Address for each user. You can specify additional
+#check and reply attributes (or you can add them later with the
+#fs-radius-add-check and fs-radius-add-reply programs).
+#
+#First enter any additional RADIUS check attributes you need to track for each
+#user, separated by whitespace.
+#END
+#my @check_attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
+# split(" ",&getvalue);
+#
+#print "\n\n", <<END, ":";
+#Now enter any additional reply attributes you need to track for each user,
+#separated by whitespace.
+#END
+#my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
+# split(" ",&getvalue);
+#
+#print "\n\n", <<END, ":";
+#Do you wish to enable the tracking of a second, separate shipping/service
+#address?
+#END
+#my $ship = &_yesno;
+#
+#sub getvalue {
+# my($x)=scalar(<STDIN>);
+# chop $x;
+# $x;
+#}
+#
+#sub _yesno {
+# print " [y/N]:";
+# my $x = scalar(<STDIN>);
+# $x =~ /^y/i;
+#}
+
+#my @check_attributes = (); #add later
+#my @attributes = (); #add later
+#my $ship = $opt_s;
+
+###
+# create a dbdef object from the old data structure
+###
+
+my $dbdef = dbdef_dist(datasrc);
+
+#important
+$dbdef->save($dbdef_file);
+&FS::Schema::reload_dbdef($dbdef_file);
+
+###
+# create 'em
+###
+
+$FS::CurrentUser::upgrade_hack = 1;
+$FS::UID::callback_hack = 1;
+my $dbh = adminsuidsetup $opt_u; #$user;
+$FS::UID::callback_hack = 0;
+
+#create tables
+$|=1;
+
+foreach my $statement ( $dbdef->sql($dbh) ) {
+ $dbh->do( $statement )
+ or die "CREATE error: ". $dbh->errstr. "\ndoing statement: $statement";
+}
+
+#now go back and reverse engineer the db
+#so we pick up the correct column DEFAULTs for #oidless inserts
+dbdef_create($dbh, $dbdef_file);
+delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
+reload_dbdef($dbdef_file);
+
+warn "Freeside schema initialized - commiting transaction\n" if $opt_v;
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+warn "Database schema committed successfully\n" if $opt_v;
+
+warn "Initializing freeside configuration\n" if $opt_v;
+$FS::UID::callback_hack = 1;
+$dbh = adminsuidsetup $opt_u;
+$FS::UID::callback_hack = 0;
+if (!scalar(qsearch('conf', {}))) {
+ my $error = FS::Conf::init_config($config_dir);
+ if ($error) {
+ $dbh->rollback or die $dbh->errstr;
+ die $error;
+ }
+}
+
+warn "Freeside configuration initialized - commiting transaction\n" if $opt_v;
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+warn "Freeside configuration committed successfully\n" if $opt_v;
+
+$dbh = adminsuidsetup $opt_u;
+create_initial_data('domain' => $opt_d);
+
+warn "Freeside database initialized - commiting transaction\n" if $opt_v;
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+warn "Database initialization committed successfully\n" if $opt_v;
+
+sub dbdef_create { # reverse engineer the schema from the DB and save to file
+ my( $dbh, $file ) = @_;
+ my $dbdef = new_native DBIx::DBSchema $dbh;
+ $dbdef->save($file);
+}
+
+sub usage {
+ die "Usage:\n freeside-setup -d domain.name [ -v ] [ config/dir ]\n"
+ # [ -u user ] for devel/multi-db installs
+}
+
+1;
+
+
diff --git a/FS/bin/freeside-sqlradius-dedup-group b/FS/bin/freeside-sqlradius-dedup-group
new file mode 100755
index 0000000..441d50f
--- /dev/null
+++ b/FS/bin/freeside-sqlradius-dedup-group
@@ -0,0 +1,82 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( %seen @dups );
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+
+my %allowed_types = map { $_ => 1 } qw ( sqlradius sqlradius_withdomain );
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $export_x = shift;
+my @part_export;
+if ( !defined($export_x) ) {
+ @part_export = qsearch('part_export', {} );
+} elsif ( $export_x =~ /^(\d+)$/ ) {
+ @part_export = qsearchs('part_export', { exportnum=>$1 } )
+ or die "exportnum $export_x not found\n";
+} else {
+ @part_export = qsearch('part_export', { exporttype=>$export_x } )
+ or die "no exports of type $export_x found\n";
+}
+
+@part_export = grep { $allowed_types{$_->exporttype} } @part_export
+ or die "No sqlradius exports specified.";
+
+foreach my $part_export ( @part_export ) {
+ my $dbh = DBI->connect( map $part_export->option($_),
+ qw ( datasrc username password ) );
+
+ my $sth = $dbh->prepare("SELECT id,username,groupname
+ FROM usergroup ORDER By username,groupname,id")
+ or die $dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+
+ @dups = (); %seen = ();
+ while (my $row = $sth->fetchrow_arrayref ) {
+ my ($userid, $username, $groupname) = @$row;
+ unless ( exists($seen{$username}{$groupname}) ) {
+ $seen{$username}{$groupname} = $userid;
+ next;
+ }
+ push @dups, $userid;
+ }
+
+ $sth = $dbh->prepare("DELETE FROM usergroup WHERE id = ?")
+ or die $dbh->errstr;
+
+ foreach (@dups) {
+ $sth->execute($_) or die $sth->errstr;
+ }
+
+}
+
+
+sub usage {
+ die "Usage:\n\n freeside-sqlradius-dedup-group user [ exportnum|exporttype ]\n";
+}
+
+=head1 NAME
+
+freeside-sqlradius-dedup-group - Command line tool to eliminate duplicate usergroup entries from radius tables
+
+=head1 SYNOPSIS
+
+ freeside-sqlradius-dedup-group user [ exportnum|exporttype ]
+
+=head1 DESCRIPTION
+
+ Removes all but one username groupname pair when duplicate entries exist
+ for the specified export (selected by exportnum or exporttype) or all
+ exports if none are specified.
+
+=head1 SEE ALSO
+
+L<freeside-reexport>, L<freeside-sqlradius-reset>, L<FS::part_export>
+
+=cut
+
diff --git a/FS/bin/freeside-sqlradius-radacctd b/FS/bin/freeside-sqlradius-radacctd
new file mode 100644
index 0000000..7b2d04d
--- /dev/null
+++ b/FS/bin/freeside-sqlradius-radacctd
@@ -0,0 +1,145 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( @part_export );
+use subs qw(myshutdown);
+use POSIX qw(:sys_wait_h);
+#use IO::File;
+use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm);
+use FS::UID qw(adminsuidsetup); #forksuidsetup driver_name dbh myconnect);
+use FS::Record qw(qsearch); # qsearchs);
+use FS::part_export;
+use FS::part_export::sqlradius;
+#use FS::svc_acct;
+#use FS::cust_svc;
+
+my $user = shift or die &usage;
+
+#daemonize1('freeside-sqlradius-radacctd', $user); #keep unique pid files w/multi installs
+daemonize1('freeside-sqlradius-radacctd');
+
+drop_root();
+
+#$ENV{HOME} = (getpwuid($>))[7]; #for ssh
+
+adminsuidsetup $user;
+
+logfile( "%%%FREESIDE_LOG%%%/sqlradius-radacctd-log.". $FS::UID::datasrc );
+
+daemonize2();
+
+#--
+
+my @part_export = FS::part_export::sqlradius->all_sqlradius_withaccounting();
+
+die "no sqlradius, sqlradius_withdomain, radiator or phone_sqlradius exports".
+ " without ignore_accounting"
+ unless @part_export;
+
+while (1) {
+
+ #fork off one kid per export (machine)
+ # _>{'_radacct_kid'} is an evil kludge
+ foreach my $part_export ( grep ! $_->{'_radacct_kid'}, @part_export ) {
+
+ defined( my $pid = fork ) or do {
+ warn "WARNING: can't fork to spawn child for ". $part_export->machine;
+ next;
+ };
+
+ if ( $pid ) {
+ $part_export->{'_radacct_kid'} = $pid;
+ warn "child $pid spawned for ". $part_export->machine;
+ } else { #kid time
+
+ adminsuidsetup($user); #get our own db handle
+
+ until ( sigint || sigterm ) {
+ $part_export->update_svc();
+ sleep 1;
+ }
+
+ warn "child for ". $part_export->machine. " done";
+ exit;
+
+ } #eo kid
+
+ }
+
+ #reap up any kids that died...
+ &reap_kids;
+
+ myshutdown() if sigterm() || sigint();
+
+ sleep 5;
+}
+
+#--
+
+sub myshutdown {
+ &reap_kids;
+
+ #kill all the kids
+ kill 'TERM', $_ foreach grep $_, map $_->{'_radacct_kid'}, @part_export;
+
+ my $wait = 12; #wait up to 1 minute
+ while ( ( grep $_->{'_radacct_kid'}, @part_export ) && $wait-- ) {
+ warn "waiting for children to terminate";
+ sleep 5;
+ &reap_kids;
+ }
+ warn "abandoning children" if grep $_->{'_radacct_kid'}, @part_export;
+ die "exiting";
+}
+
+sub reap_kids {
+ #warn "reaping kids\n";
+ foreach my $part_export ( grep $_->{'_radacct_kid'}, @part_export ) {
+ my $pid = $part_export->{'_radacct_kid'};
+ my $kid = waitpid($pid, WNOHANG);
+ if ( $kid > 0 ) {
+ $part_export->{'_radacct_kid'} = '';
+ }
+ }
+ #warn "done reaping\n";
+}
+
+sub usage {
+ die "Usage:\n\n freeside-sqlradius-radacctd user\n";
+}
+
+=head1 NAME
+
+freeside-sqlradius-radacctd - Real-time radacct import daemon
+
+=head1 SYNOPSIS
+
+ freeside-sqlradius-radacctd username
+
+=head1 DESCRIPTION
+
+Imports records from an the SQL radacct tables of all sqlradius,
+sqlradius_withdomain and radiator exports (except those with the
+ignore_accounting flag) and updates the following fields in svc_acct (see
+L<FS::svc_acct>) for each account: last_login, last_logout, seconds,
+upbytes, downbytes, totalbytes. Runs as a daemon and updates the database
+in real-time.
+
+B<username> is a username added by freeside-adduser.
+
+=head1 RADIUS DATABASE CHANGES
+
+In 1.7.4+, freeside-upgrade should have taken care of these changes already.
+
+ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL;
+
+If you want to ignore the existing accountg records, also do:
+
+UPDATE radacct SET FreesideStatus = 'done' WHERE FreesideStatus IS NULL;
+
+=head1 SEE ALSO
+
+=cut
+
+1;
+
diff --git a/FS/bin/freeside-sqlradius-reset b/FS/bin/freeside-sqlradius-reset
new file mode 100755
index 0000000..7d1d343
--- /dev/null
+++ b/FS/bin/freeside-sqlradius-reset
@@ -0,0 +1,103 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( $opt_n );
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+#use FS::svc_acct;
+use FS::cust_svc;
+
+getopts("n");
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#my $machine = shift or die &usage;
+
+my @exports = ();
+if ( @ARGV ) {
+ foreach my $exportnum ( @ARGV ) {
+ foreach my $exporttype (qw( sqlradius sqlradius_withdomain phone_sqlradius )) {
+ push @exports, qsearch('part_export', { exportnum => $exportnum,
+ exporttype => $exporttype, } );
+ }
+ }
+ } else {
+ @exports = qsearch('part_export', { exporttype=>'sqlradius' } );
+ push @exports, qsearch('part_export', { exporttype=>'sqlradius_withdomain' } );
+}
+
+unless ( $opt_n ) {
+ foreach my $export ( @exports ) {
+ my $icradius_dbh = DBI->connect(
+ map { $export->option($_) } qw( datasrc username password )
+ ) or die $DBI::errstr;
+ for my $table (qw( radcheck radreply usergroup )) {
+ my $sth = $icradius_dbh->prepare("DELETE FROM $table");
+ $sth->execute or die "Can't reset $table table: ". $sth->errstr;
+ }
+ $icradius_dbh->disconnect;
+ }
+}
+
+foreach my $export ( @exports ) {
+
+ #my @svcparts = map { $_->svcpart } $export->export_svc;
+ my $overlimit_groups = $export->option('overlimit_groups');
+
+ my @svc_x =
+ map { $_->svc_x }
+ map { qsearch('cust_svc', { 'svcpart' => $_->svcpart } ) }
+ grep { qsearch('cust_svc', { 'svcpart' => $_->svcpart } ) }
+ $export->export_svc;
+
+ foreach my $svc_x ( @svc_x ) {
+
+ $svc_x->check; #set any fixed usergroup so it'll export even if all
+ #svc_acct records don't have the group yet
+
+ if ($overlimit_groups && $svc_x->overlimit) {
+ $svc_x->usergroup( &{ $svc_x->_fieldhandlers->{'usergroup'} }
+ ($svc_x, $overlimit_groups)
+ );
+ }
+
+ #false laziness with FS::svc_acct::insert (like it matters)
+ my $error = $export->export_insert($svc_x);
+ die $error if $error;
+
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-sqlradius-reset user [ exportnum, ... ]\n";
+}
+
+=head1 NAME
+
+freeside-sqlradius-reset - Command line interface to reset and recreate RADIUS SQL tables
+
+=head1 SYNOPSIS
+
+ freeside-sqlradius-reset [ -n ] username [ EXPORTNUM, ... ]
+
+=head1 DESCRIPTION
+
+Deletes the radcheck, radreply and usergroup tables and repopulates them from
+the Freeside database, for the specified exports, or, if no exports are
+specified, for all sqlradius and sqlradius_withdomain exports.
+
+B<username> is a username added by freeside-adduser.
+
+The B<-n> option, if supplied, supresses the deletion of the existing data in
+the tables.
+
+=head1 SEE ALSO
+
+L<freeside-reexport>, L<FS::part_export>, L<FS::part_export::sqlradius>
+
+=cut
+
+1;
diff --git a/FS/bin/freeside-sqlradius-seconds b/FS/bin/freeside-sqlradius-seconds
new file mode 100644
index 0000000..9999cbb
--- /dev/null
+++ b/FS/bin/freeside-sqlradius-seconds
@@ -0,0 +1,58 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use Date::Parse;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearchs);
+use FS::svc_acct;
+
+my $fs_user = shift or die &usage;
+adminsuidsetup( $fs_user );
+
+my $target_user = shift or die &usage;
+my $start = shift or die &usage;
+$start = str2time($start);
+my $stop = scalar(@ARGV) ? str2time(shift) : time;
+
+my $svc_acct = qsearchs( 'svc_acct', { 'username' => $target_user } );
+die "username $target_user not found\n" unless $svc_acct;
+
+print $svc_acct->seconds_since_sqlradacct( $start, $stop ). "\n";
+
+sub usage {
+ die "Usage:\n\n freeside-sqlradius-seconds freeside_username target_username start_date stop_date\n";
+}
+
+
+=head1 NAME
+
+freeside-sqlradius-seconds - Command line time-online tool
+
+=head1 SYNOPSIS
+
+ freeside-sqlradius-seconds freeside_username target_username start_date [ stop_date ]
+
+=head1 DESCRIPTION
+
+Returns the number of seconds the specified username has been online between
+start_date (inclusive) and stop_date (exclusive).
+See L<FS::svc_acct/seconds_since_sqlradacct>
+
+B<freeside_username> is a username added by freeside-adduser.
+B<target_username> is the username of the user account to query.
+B<start_date> and B<stop_date> are in any format Date::Parse is happy with.
+B<stop_date> defaults to now if not specified.
+
+=head1 BUGS
+
+Selection of the account in question is rather simplistic in that
+B<target_username> doesn't necessarily identify a unique account (and wouldn't
+even if a domain was specified), and no sqlradius export is checked for.
+
+=head1 SEE ALSO
+
+L<FS::svc_acct/seconds_since_sqlradacct>
+
+=cut
+
+1;
diff --git a/FS/bin/freeside-sqlradius-set-lastlog b/FS/bin/freeside-sqlradius-set-lastlog
new file mode 100755
index 0000000..ad85630
--- /dev/null
+++ b/FS/bin/freeside-sqlradius-set-lastlog
@@ -0,0 +1,102 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs str2time_sql);
+use FS::Conf;
+use FS::part_export;
+use FS::svc_acct;
+
+my %allowed_types = map { $_ => 1 } qw ( sqlradius sqlradius_withdomain );
+my $conf = new FS::Conf;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $export_x = shift;
+my @part_export;
+if ( !defined($export_x) ) {
+ @part_export = qsearch('part_export', {} );
+} elsif ( $export_x =~ /^(\d+)$/ ) {
+ @part_export = qsearchs('part_export', { exportnum=>$1 } )
+ or die "exportnum $export_x not found\n";
+} else {
+ @part_export = qsearch('part_export', { exporttype=>$export_x } )
+ or die "no exports of type $export_x found\n";
+}
+
+# gross almost false laziness with FS::part_export::sqlradius::update_svc_acct
+@part_export = grep { ! $_->option('ignore_accounting') }
+ grep { $allowed_types{$_->exporttype} }
+ @part_export
+ or die "No sqlradius exports specified.";
+
+
+foreach my $part_export ( @part_export ) {
+ my $dbh = DBI->connect( map $part_export->option($_),
+ qw ( datasrc username password ) );
+
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+ my $group = "UserName";
+ $group .= ",Realm"
+ if ( ref($part_export) =~ /withdomain/ );
+
+ my $sth = $dbh->prepare("SELECT UserName, Realm,
+ $str2time max(AcctStartTime)),
+ $str2time max(AcctStopTime))
+ FROM radacct
+ WHERE AcctStartTime != 0 AND AcctStopTime != 0
+ GROUP BY $group")
+ or die $dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+
+ while (my $row = $sth->fetchrow_arrayref ) {
+ my ($username, $realm, $start, $stop) = @$row;
+
+ $username = lc($username) unless $conf->exists('username-uppercase');
+ my $extra_sql = '';
+ if ( ref($part_export) =~ /withdomain/ ) {
+ $extra_sql = " And '$realm' = ( SELECT domain FROM svc_domain
+ WHERE svc_domain.svcnum = svc_acct.domsvc ) ";
+ }
+
+ my $svc_acct = qsearchs( 'svc_acct',
+ { 'username' => $username },
+ '',
+ $extra_sql,
+ );
+ if ($svc_acct) {
+ $svc_acct->last_login($start)
+ if $start && (!$svc_acct->last_login || $start > $svc_acct->last_login);
+ $svc_acct->last_logout($stop)
+ if $stop && (!$svc_acct->last_logout || $stop > $svc_acct->last_logout);
+ }
+ }
+}
+
+
+sub usage {
+ die "Usage:\n\n freeside-sqlradius-set_lastlog user [ exportnum|exporttype ]\n";
+}
+
+=head1 NAME
+
+freeside-sqlradius-set-lastlog - Command line tool to set last_login and last_logout values from radius tables
+
+=head1 SYNOPSIS
+
+ freeside-sqlradius-set-lastlog user [ exportnum|exporttype ]
+
+=head1 DESCRIPTION
+
+ Sets the last_login and last_logout columns of each svc_acct based on
+ data in the radacct table for the specified export (selected by exportnum
+ or exporttype) or all exports if none are specified.
+
+=head1 SEE ALSO
+
+L<freeside-sqlradius-radacctd>, L<FS::part_export>
+
+=cut
+
diff --git a/FS/bin/freeside-upgrade b/FS/bin/freeside-upgrade
new file mode 100755
index 0000000..c988e13
--- /dev/null
+++ b/FS/bin/freeside-upgrade
@@ -0,0 +1,215 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_d $opt_s $opt_q $opt_v);
+use vars qw($DEBUG $DRY_RUN);
+use Getopt::Std;
+use DBIx::DBSchema 0.31;
+use FS::UID qw(adminsuidsetup checkeuid datasrc ); #getsecrets);
+use FS::CurrentUser;
+use FS::Schema qw( dbdef dbdef_dist reload_dbdef );
+use FS::Misc::prune qw(prune_applications);
+use FS::Conf;
+use FS::Record qw(qsearch);
+use FS::Upgrade qw(upgrade upgrade_sqlradius);
+
+my $start = time;
+
+die "Not running uid freeside!" unless checkeuid();
+
+getopts("dqs");
+
+$DEBUG = !$opt_q;
+#$DEBUG = $opt_v;
+
+$DRY_RUN = $opt_d;
+
+my $user = shift or die &usage;
+$FS::CurrentUser::upgrade_hack = 1;
+$FS::UID::callback_hack = 1;
+my $dbh = adminsuidsetup($user);
+$FS::UID::callback_hack = 0;
+
+#needs to match FS::Schema...
+my $dbdef_file = "%%%FREESIDE_CONF%%%/dbdef.". datasrc;
+
+dbdef_create($dbh, $dbdef_file);
+
+delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
+reload_dbdef($dbdef_file);
+
+warn "Upgrade startup completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+$start = time;
+
+$DBIx::DBSchema::DEBUG = $DEBUG;
+$DBIx::DBSchema::Table::DEBUG = $DEBUG;
+$DBIx::DBSchema::Index::DEBUG = $DEBUG;
+
+my @bugfix = ();
+
+if (dbdef->table('cust_main')->column('agent_custid') && ! $opt_s) {
+ push @bugfix,
+ "UPDATE cust_main SET agent_custid = NULL where agent_custid = ''";
+
+ push @bugfix,
+ "UPDATE h_cust_main SET agent_custid = NULL where agent_custid = ''"
+ if (dbdef->table('h_cust_main'));
+}
+
+#you should have run fs-migrate-part_svc ages ago, when you upgraded
+#from 1.3 to 1.4... if not, it needs to be hooked into -upgrade here or
+#you'll lose all the part_svc settings it migrates to part_svc_column
+
+if ( $DRY_RUN ) {
+ print
+ join(";\n", @bugfix, dbdef->sql_update_schema( dbdef_dist(datasrc), $dbh ) ). ";\n";
+ exit;
+} else {
+ foreach my $statement ( @bugfix ) {
+ $dbh->do( $statement )
+ or die "Error: ". $dbh->errstr. "\n executing: $statement";
+ }
+
+ warn "Pre-schema change upgrades completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+ $start = time;
+
+ dbdef->update_schema( dbdef_dist(datasrc), $dbh );
+}
+
+warn "Schema upgrade completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+$start = time;
+
+my $hashref = {};
+$hashref->{dry_run} = 1 if $DRY_RUN;
+$hashref->{debug} = 1 if $DEBUG && $DRY_RUN;
+prune_applications($hashref) unless $opt_s;
+
+warn "Application pruning completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+$start = time;
+
+print "\n" if $DRY_RUN;
+
+if ( $dbh->{Driver}->{Name} =~ /^mysql/i && ! $opt_s ) {
+
+ foreach my $table (qw( svc_acct svc_phone )) {
+
+ my $sth = $dbh->prepare(
+ "SELECT COUNT(*) FROM duplicate_lock WHERE lockname = '$table'"
+ ) or die $dbh->errstr;
+
+ $sth->execute or die $sth->errstr;
+
+ unless ( $sth->fetchrow_arrayref->[0] ) {
+
+ $sth = $dbh->prepare(
+ "INSERT INTO duplicate_lock ( lockname ) VALUES ( '$table' )"
+ ) or die $dbh->errstr;
+
+ $sth->execute or die $sth->errstr;
+
+ }
+
+ }
+
+ warn "Duplication lock creation completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+ $start = time;
+
+}
+
+$dbh->commit or die $dbh->errstr;
+
+dbdef_create($dbh, $dbdef_file);
+
+$dbh->disconnect or die $dbh->errstr;
+
+delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
+$FS::UID::AutoCommit = 0;
+$FS::UID::callback_hack = 1;
+$dbh = adminsuidsetup($user);
+$FS::UID::callback_hack = 0;
+unless ( $DRY_RUN ) {
+ my $dir = "%%%FREESIDE_CONF%%%/conf.". datasrc;
+ if (!scalar(qsearch('conf', {}))) {
+ my $error = FS::Conf::init_config($dir);
+ if ($error) {
+ warn "CONFIGURATION UPGRADE FAILED\n";
+ $dbh->rollback or die $dbh->errstr;
+ die $error;
+ }
+ }
+}
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+$dbh = adminsuidsetup($user);
+
+warn "Re-initialization with updated schema completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+$start = time;
+
+upgrade()
+ unless $DRY_RUN || $opt_s;
+
+warn "Table updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+$start = time;
+
+upgrade_sqlradius()
+ unless $DRY_RUN || $opt_s;
+
+warn "SQL RADIUS updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+$start = time;
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+warn "Commit and disconnection completed in ". (time-$start). " seconds; upgrade done!\n"; # if $DEBUG;
+
+###
+
+sub dbdef_create { # reverse engineer the schema from the DB and save to file
+ my( $dbh, $file ) = @_;
+ my $dbdef = new_native DBIx::DBSchema $dbh;
+ $dbdef->save($file);
+}
+
+sub usage {
+ die "Usage:\n freeside-upgrade [ -d ] [ -s ] [ -q | -v ] user\n";
+}
+
+=head1 NAME
+
+freeside-upgrade - Upgrades database schema for new freeside verisons.
+
+=head1 SYNOPSIS
+
+ freeside-upgrade [ -d ] [ -s ] [ -q | -v ]
+
+=head1 DESCRIPTION
+
+Reads your existing database schema and updates it to match the current schema,
+adding any columns or tables necessary.
+
+Also performs other upgrade functions:
+
+=over 4
+
+=item Calls FS:: Misc::prune::prune_applications (probably unnecessary every upgrade, but simply won't find any records to change)
+
+=item If necessary, moves your configuration information from the filesystem in /usr/local/etc/freeside/conf.<datasrc> to the database.
+
+=back
+
+ [ -d ]: Dry run; output SQL statements (to STDOUT) only, but do not execute
+ them.
+
+ [ -q ]: Run quietly. This may become the default at some point.
+
+ [ -v ]: Run verbosely, sending debugging information to STDERR. This is the
+ current default.
+
+ [ -s ]: Schema changes only. Useful for Pg/slony slaves where the data
+ changes will be replicated from the Pg/slony master.
+
+=head1 SEE ALSO
+
+=cut
+
diff --git a/FS/bin/freeside-yori b/FS/bin/freeside-yori
new file mode 100644
index 0000000..d113799
--- /dev/null
+++ b/FS/bin/freeside-yori
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use FS::Yori qw(reports report);
+
+if ( @ARGV ) {
+ while ( my $report = shift ) {
+ print report($report). "\n";
+ }
+} else {
+ print join("\n", reports() ). "\n";
+}
+
+
+1;
diff --git a/FS/t/AccessRight.t b/FS/t/AccessRight.t
new file mode 100644
index 0000000..a966842
--- /dev/null
+++ b/FS/t/AccessRight.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::AccessRight;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/CGI.t b/FS/t/CGI.t
new file mode 100644
index 0000000..1b4e238
--- /dev/null
+++ b/FS/t/CGI.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::CGI;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/ClientAPI.t b/FS/t/ClientAPI.t
new file mode 100644
index 0000000..973d8da
--- /dev/null
+++ b/FS/t/ClientAPI.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::ClientAPI;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/ClientAPI_SessionCache.t b/FS/t/ClientAPI_SessionCache.t
new file mode 100644
index 0000000..605803e
--- /dev/null
+++ b/FS/t/ClientAPI_SessionCache.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::ClientAPI_SessionCache;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Conf.t b/FS/t/Conf.t
new file mode 100644
index 0000000..a9f7653
--- /dev/null
+++ b/FS/t/Conf.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Conf;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/ConfDefaults.t b/FS/t/ConfDefaults.t
new file mode 100644
index 0000000..433555a
--- /dev/null
+++ b/FS/t/ConfDefaults.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::ConfDefaults;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/ConfItem.t b/FS/t/ConfItem.t
new file mode 100644
index 0000000..c7932d7
--- /dev/null
+++ b/FS/t/ConfItem.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::ConfItem;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Cron-backup.t b/FS/t/Cron-backup.t
new file mode 100644
index 0000000..847d41a
--- /dev/null
+++ b/FS/t/Cron-backup.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Cron::backup;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Cron-bill.t b/FS/t/Cron-bill.t
new file mode 100644
index 0000000..42c7b4f
--- /dev/null
+++ b/FS/t/Cron-bill.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Cron::bill;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Cron-vacuum.t b/FS/t/Cron-vacuum.t
new file mode 100644
index 0000000..eaa6b76
--- /dev/null
+++ b/FS/t/Cron-vacuum.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Cron::vacuum;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Daemon.t b/FS/t/Daemon.t
new file mode 100644
index 0000000..24893fd
--- /dev/null
+++ b/FS/t/Daemon.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Daemon;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/InitHandler.t b/FS/t/InitHandler.t
new file mode 100644
index 0000000..0ce60c8
--- /dev/null
+++ b/FS/t/InitHandler.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::InitHandler;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Misc.t b/FS/t/Misc.t
new file mode 100644
index 0000000..cc7751a
--- /dev/null
+++ b/FS/t/Misc.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Misc;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Msgcat.t b/FS/t/Msgcat.t
new file mode 100644
index 0000000..29e71b3
--- /dev/null
+++ b/FS/t/Msgcat.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Msgcat;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Record.t b/FS/t/Record.t
new file mode 100644
index 0000000..00de1ed
--- /dev/null
+++ b/FS/t/Record.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Record;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Report-Table-Monthly.t b/FS/t/Report-Table-Monthly.t
new file mode 100644
index 0000000..6ff365d
--- /dev/null
+++ b/FS/t/Report-Table-Monthly.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Report::Table::Monthly;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Report-Table.t b/FS/t/Report-Table.t
new file mode 100644
index 0000000..866d498
--- /dev/null
+++ b/FS/t/Report-Table.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Report::Table;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/Report.t b/FS/t/Report.t
new file mode 100644
index 0000000..76d6ea4
--- /dev/null
+++ b/FS/t/Report.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Report;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/SearchCache.t b/FS/t/SearchCache.t
new file mode 100644
index 0000000..3c26f35
--- /dev/null
+++ b/FS/t/SearchCache.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::SearchCache;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/UID.t b/FS/t/UID.t
new file mode 100644
index 0000000..9f7da4e
--- /dev/null
+++ b/FS/t/UID.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::UID;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_group.t b/FS/t/access_group.t
new file mode 100644
index 0000000..be14109
--- /dev/null
+++ b/FS/t/access_group.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_group;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_groupagent.t b/FS/t/access_groupagent.t
new file mode 100644
index 0000000..aff1f25
--- /dev/null
+++ b/FS/t/access_groupagent.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_groupagent;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_right.t b/FS/t/access_right.t
new file mode 100644
index 0000000..66cd362
--- /dev/null
+++ b/FS/t/access_right.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_right;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_user.t b/FS/t/access_user.t
new file mode 100644
index 0000000..cab679d
--- /dev/null
+++ b/FS/t/access_user.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_user;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_user_pref.t b/FS/t/access_user_pref.t
new file mode 100644
index 0000000..2822098
--- /dev/null
+++ b/FS/t/access_user_pref.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_user_pref;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/access_usergroup.t b/FS/t/access_usergroup.t
new file mode 100644
index 0000000..383a7cf
--- /dev/null
+++ b/FS/t/access_usergroup.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::access_usergroup;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/acct_rt_transaction.t b/FS/t/acct_rt_transaction.t
new file mode 100644
index 0000000..552bdc8
--- /dev/null
+++ b/FS/t/acct_rt_transaction.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::acct_rt_transaction;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/acct_snarf.t b/FS/t/acct_snarf.t
new file mode 100644
index 0000000..642760f
--- /dev/null
+++ b/FS/t/acct_snarf.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::acct_snarf;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/addr_block.t b/FS/t/addr_block.t
new file mode 100644
index 0000000..4f49a44
--- /dev/null
+++ b/FS/t/addr_block.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::addr_block;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/agent.t b/FS/t/agent.t
new file mode 100644
index 0000000..769cce2
--- /dev/null
+++ b/FS/t/agent.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::agent;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/agent_payment_gateway.t b/FS/t/agent_payment_gateway.t
new file mode 100644
index 0000000..af78a9a
--- /dev/null
+++ b/FS/t/agent_payment_gateway.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::agent_payment_gateway;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/agent_type.t b/FS/t/agent_type.t
new file mode 100644
index 0000000..99c66a1
--- /dev/null
+++ b/FS/t/agent_type.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::agent_type;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/banned_pay.t b/FS/t/banned_pay.t
new file mode 100644
index 0000000..bef1ff2
--- /dev/null
+++ b/FS/t/banned_pay.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::banned_pay;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cdr.t b/FS/t/cdr.t
new file mode 100644
index 0000000..1d1f3eb
--- /dev/null
+++ b/FS/t/cdr.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cdr;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cdr_calltype.t b/FS/t/cdr_calltype.t
new file mode 100644
index 0000000..d4e1394
--- /dev/null
+++ b/FS/t/cdr_calltype.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cdr_calltype;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cdr_carrier.t b/FS/t/cdr_carrier.t
new file mode 100644
index 0000000..1e21615
--- /dev/null
+++ b/FS/t/cdr_carrier.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cdr_carrier;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cdr_type.t b/FS/t/cdr_type.t
new file mode 100644
index 0000000..9dff15a
--- /dev/null
+++ b/FS/t/cdr_type.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cdr_type;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cdr_upstream_rate.t b/FS/t/cdr_upstream_rate.t
new file mode 100644
index 0000000..f9458c5
--- /dev/null
+++ b/FS/t/cdr_upstream_rate.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cdr_upstream_rate;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/clientapi_session.t b/FS/t/clientapi_session.t
new file mode 100644
index 0000000..a6414c3
--- /dev/null
+++ b/FS/t/clientapi_session.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::clientapi_session;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/clientapi_session_field.t b/FS/t/clientapi_session_field.t
new file mode 100644
index 0000000..a9d4fa9
--- /dev/null
+++ b/FS/t/clientapi_session_field.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::clientapi_session_field;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/conf.t b/FS/t/conf.t
new file mode 100644
index 0000000..5e52079
--- /dev/null
+++ b/FS/t/conf.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::conf;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill.t b/FS/t/cust_bill.t
new file mode 100644
index 0000000..b43f08e
--- /dev/null
+++ b/FS/t/cust_bill.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_ApplicationCommon.t b/FS/t/cust_bill_ApplicationCommon.t
new file mode 100644
index 0000000..fa03d34
--- /dev/null
+++ b/FS/t/cust_bill_ApplicationCommon.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_ApplicationCommon;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_event.t b/FS/t/cust_bill_event.t
new file mode 100644
index 0000000..0e2ca3e
--- /dev/null
+++ b/FS/t/cust_bill_event.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_event;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pay.t b/FS/t/cust_bill_pay.t
new file mode 100644
index 0000000..001eed0
--- /dev/null
+++ b/FS/t/cust_bill_pay.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pay;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pay_batch.t b/FS/t/cust_bill_pay_batch.t
new file mode 100644
index 0000000..bc3a827
--- /dev/null
+++ b/FS/t/cust_bill_pay_batch.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pay_batch;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pay_pkg.t b/FS/t/cust_bill_pay_pkg.t
new file mode 100644
index 0000000..b8fcddb
--- /dev/null
+++ b/FS/t/cust_bill_pay_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pay_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pkg.t b/FS/t/cust_bill_pkg.t
new file mode 100644
index 0000000..0e45bdb
--- /dev/null
+++ b/FS/t/cust_bill_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pkg_detail.t b/FS/t/cust_bill_pkg_detail.t
new file mode 100644
index 0000000..ea6e3d1
--- /dev/null
+++ b/FS/t/cust_bill_pkg_detail.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pkg_detail;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pkg_display.t b/FS/t/cust_bill_pkg_display.t
new file mode 100644
index 0000000..d84dbdf
--- /dev/null
+++ b/FS/t/cust_bill_pkg_display.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pkg_display;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_bill_pkg_tax_location.t b/FS/t/cust_bill_pkg_tax_location.t
new file mode 100644
index 0000000..087b59a
--- /dev/null
+++ b/FS/t/cust_bill_pkg_tax_location.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pkg_tax_location;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_credit.t b/FS/t/cust_credit.t
new file mode 100644
index 0000000..cddf75c
--- /dev/null
+++ b/FS/t/cust_credit.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_credit;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_credit_bill.t b/FS/t/cust_credit_bill.t
new file mode 100644
index 0000000..0ef54c3
--- /dev/null
+++ b/FS/t/cust_credit_bill.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_credit_bill;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_credit_bill_pkg.t b/FS/t/cust_credit_bill_pkg.t
new file mode 100644
index 0000000..4eb84c3
--- /dev/null
+++ b/FS/t/cust_credit_bill_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_credit_bill_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_credit_refund.t b/FS/t/cust_credit_refund.t
new file mode 100644
index 0000000..6b2b599
--- /dev/null
+++ b/FS/t/cust_credit_refund.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_credit_refund;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_event.t b/FS/t/cust_event.t
new file mode 100644
index 0000000..7812c5b
--- /dev/null
+++ b/FS/t/cust_event.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_event;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_location.t b/FS/t/cust_location.t
new file mode 100644
index 0000000..e98372d
--- /dev/null
+++ b/FS/t/cust_location.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_location;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_main.t b/FS/t/cust_main.t
new file mode 100644
index 0000000..b0ffbdb
--- /dev/null
+++ b/FS/t/cust_main.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_main;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_main_Mixin.t b/FS/t/cust_main_Mixin.t
new file mode 100644
index 0000000..c8b9291
--- /dev/null
+++ b/FS/t/cust_main_Mixin.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_main_Mixin;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_main_county.t b/FS/t/cust_main_county.t
new file mode 100644
index 0000000..dd61199
--- /dev/null
+++ b/FS/t/cust_main_county.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_main_county;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_main_invoice.t b/FS/t/cust_main_invoice.t
new file mode 100644
index 0000000..9661620
--- /dev/null
+++ b/FS/t/cust_main_invoice.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_main_invoice;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_main_note.t b/FS/t/cust_main_note.t
new file mode 100644
index 0000000..41a7bac
--- /dev/null
+++ b/FS/t/cust_main_note.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_main_note;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pay.t b/FS/t/cust_pay.t
new file mode 100644
index 0000000..f6d0b75
--- /dev/null
+++ b/FS/t/cust_pay.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pay;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pay_batch.t b/FS/t/cust_pay_batch.t
new file mode 100644
index 0000000..02b572c
--- /dev/null
+++ b/FS/t/cust_pay_batch.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pay_batch;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pay_pending.t b/FS/t/cust_pay_pending.t
new file mode 100644
index 0000000..9ab2b5e
--- /dev/null
+++ b/FS/t/cust_pay_pending.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pay_pending;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pay_refund.t b/FS/t/cust_pay_refund.t
new file mode 100644
index 0000000..85d6c23
--- /dev/null
+++ b/FS/t/cust_pay_refund.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pay_refund;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pay_void.t b/FS/t/cust_pay_void.t
new file mode 100644
index 0000000..dca9bec
--- /dev/null
+++ b/FS/t/cust_pay_void.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pay_void;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pkg.t b/FS/t/cust_pkg.t
new file mode 100644
index 0000000..c6a6860
--- /dev/null
+++ b/FS/t/cust_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pkg_detail.t b/FS/t/cust_pkg_detail.t
new file mode 100644
index 0000000..15dec00
--- /dev/null
+++ b/FS/t/cust_pkg_detail.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pkg_detail;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pkg_option.t b/FS/t/cust_pkg_option.t
new file mode 100644
index 0000000..12314bf
--- /dev/null
+++ b/FS/t/cust_pkg_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pkg_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_pkg_reason.t b/FS/t/cust_pkg_reason.t
new file mode 100644
index 0000000..2f0a4fa
--- /dev/null
+++ b/FS/t/cust_pkg_reason.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_pkg_reason;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_refund.t b/FS/t/cust_refund.t
new file mode 100644
index 0000000..91583da
--- /dev/null
+++ b/FS/t/cust_refund.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_refund;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_svc.t b/FS/t/cust_svc.t
new file mode 100644
index 0000000..267d731
--- /dev/null
+++ b/FS/t/cust_svc.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_svc;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_svc_option.t b/FS/t/cust_svc_option.t
new file mode 100644
index 0000000..eeaa170
--- /dev/null
+++ b/FS/t/cust_svc_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_svc_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_tax_exempt.t b/FS/t/cust_tax_exempt.t
new file mode 100644
index 0000000..8af13e3
--- /dev/null
+++ b/FS/t/cust_tax_exempt.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_tax_exempt;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_tax_exempt_pkg.t b/FS/t/cust_tax_exempt_pkg.t
new file mode 100644
index 0000000..099a0ce
--- /dev/null
+++ b/FS/t/cust_tax_exempt_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_tax_exempt_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/cust_tax_location.t b/FS/t/cust_tax_location.t
new file mode 100644
index 0000000..83a1362
--- /dev/null
+++ b/FS/t/cust_tax_location.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_tax_location;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/domain_record.t b/FS/t/domain_record.t
new file mode 100644
index 0000000..794518c
--- /dev/null
+++ b/FS/t/domain_record.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::domain_record;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/export_svc.t b/FS/t/export_svc.t
new file mode 100644
index 0000000..773c5de
--- /dev/null
+++ b/FS/t/export_svc.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::export_svc;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_Common.t b/FS/t/h_Common.t
new file mode 100644
index 0000000..174bb99
--- /dev/null
+++ b/FS/t/h_Common.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_Common;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_cust_bill.t b/FS/t/h_cust_bill.t
new file mode 100644
index 0000000..ceccb2a
--- /dev/null
+++ b/FS/t/h_cust_bill.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_cust_bill;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_cust_credit.t b/FS/t/h_cust_credit.t
new file mode 100644
index 0000000..e20f476
--- /dev/null
+++ b/FS/t/h_cust_credit.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_cust_credit;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_cust_pay.t b/FS/t/h_cust_pay.t
new file mode 100644
index 0000000..6a3fe95
--- /dev/null
+++ b/FS/t/h_cust_pay.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_cust_pay;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_cust_pkg.t b/FS/t/h_cust_pkg.t
new file mode 100644
index 0000000..16a8a5d
--- /dev/null
+++ b/FS/t/h_cust_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_cust_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_cust_pkg_reason.t b/FS/t/h_cust_pkg_reason.t
new file mode 100644
index 0000000..b8dae92
--- /dev/null
+++ b/FS/t/h_cust_pkg_reason.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_cust_pkg_reason;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_cust_svc.t b/FS/t/h_cust_svc.t
new file mode 100644
index 0000000..a7dabbe
--- /dev/null
+++ b/FS/t/h_cust_svc.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_cust_svc;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_cust_tax_exempt.t b/FS/t/h_cust_tax_exempt.t
new file mode 100644
index 0000000..432238a
--- /dev/null
+++ b/FS/t/h_cust_tax_exempt.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_cust_tax_exempt;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_domain_record.t b/FS/t/h_domain_record.t
new file mode 100644
index 0000000..f48e72e
--- /dev/null
+++ b/FS/t/h_domain_record.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_domain_record;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_svc_acct.t b/FS/t/h_svc_acct.t
new file mode 100644
index 0000000..9c94d08
--- /dev/null
+++ b/FS/t/h_svc_acct.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_svc_acct;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_svc_broadband.t b/FS/t/h_svc_broadband.t
new file mode 100644
index 0000000..b8e5c7c
--- /dev/null
+++ b/FS/t/h_svc_broadband.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_svc_broadband;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_svc_domain.t b/FS/t/h_svc_domain.t
new file mode 100644
index 0000000..87d2a09
--- /dev/null
+++ b/FS/t/h_svc_domain.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_svc_domain;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_svc_external.t b/FS/t/h_svc_external.t
new file mode 100644
index 0000000..5248f87
--- /dev/null
+++ b/FS/t/h_svc_external.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_svc_external;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_svc_forward.t b/FS/t/h_svc_forward.t
new file mode 100644
index 0000000..64731d5
--- /dev/null
+++ b/FS/t/h_svc_forward.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_svc_forward;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/h_svc_www.t b/FS/t/h_svc_www.t
new file mode 100644
index 0000000..07558ce
--- /dev/null
+++ b/FS/t/h_svc_www.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::h_svc_www;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/inventory_class.t b/FS/t/inventory_class.t
new file mode 100644
index 0000000..80b2fa2
--- /dev/null
+++ b/FS/t/inventory_class.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::inventory_class;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/inventory_item.t b/FS/t/inventory_item.t
new file mode 100644
index 0000000..8ce9d67
--- /dev/null
+++ b/FS/t/inventory_item.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::inventory_item;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/msgcat.t b/FS/t/msgcat.t
new file mode 100644
index 0000000..c38c639
--- /dev/null
+++ b/FS/t/msgcat.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::msgcat;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/nas.t b/FS/t/nas.t
new file mode 100644
index 0000000..6f8ae36
--- /dev/null
+++ b/FS/t/nas.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::nas;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/option_Common.t b/FS/t/option_Common.t
new file mode 100644
index 0000000..ad26141
--- /dev/null
+++ b/FS/t/option_Common.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::option_Common;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_bill_event.t b/FS/t/part_bill_event.t
new file mode 100644
index 0000000..5626a9f
--- /dev/null
+++ b/FS/t/part_bill_event.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_bill_event;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_event-Action.t b/FS/t/part_event-Action.t
new file mode 100644
index 0000000..a665277
--- /dev/null
+++ b/FS/t/part_event-Action.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_event::Action;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_event-Condition.t b/FS/t/part_event-Condition.t
new file mode 100644
index 0000000..c44a438
--- /dev/null
+++ b/FS/t/part_event-Condition.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_event::Condition;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_event.t b/FS/t/part_event.t
new file mode 100644
index 0000000..027b20c
--- /dev/null
+++ b/FS/t/part_event.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_event;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_event_condition.t b/FS/t/part_event_condition.t
new file mode 100644
index 0000000..fa5a05c
--- /dev/null
+++ b/FS/t/part_event_condition.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_event_condition;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_event_condition_option.t b/FS/t/part_event_condition_option.t
new file mode 100644
index 0000000..492fc82
--- /dev/null
+++ b/FS/t/part_event_condition_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_event_condition_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_event_condition_option_option.t b/FS/t/part_event_condition_option_option.t
new file mode 100644
index 0000000..f714011
--- /dev/null
+++ b/FS/t/part_event_condition_option_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_event_condition_option_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_event_option.t b/FS/t/part_event_option.t
new file mode 100644
index 0000000..546a78f
--- /dev/null
+++ b/FS/t/part_event_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_event_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-acct_sql.t b/FS/t/part_export-acct_sql.t
new file mode 100644
index 0000000..9eed472
--- /dev/null
+++ b/FS/t/part_export-acct_sql.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::acct_sql;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-apache.t b/FS/t/part_export-apache.t
new file mode 100644
index 0000000..b999508
--- /dev/null
+++ b/FS/t/part_export-apache.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::apache;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-bind.t b/FS/t/part_export-bind.t
new file mode 100644
index 0000000..d0c96be
--- /dev/null
+++ b/FS/t/part_export-bind.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::bind;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-bind_slave.t b/FS/t/part_export-bind_slave.t
new file mode 100644
index 0000000..c6a0386
--- /dev/null
+++ b/FS/t/part_export-bind_slave.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::bind_slave;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-bsdshell.t b/FS/t/part_export-bsdshell.t
new file mode 100644
index 0000000..eaf417a
--- /dev/null
+++ b/FS/t/part_export-bsdshell.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::bsdshell;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-communigate_pro.t b/FS/t/part_export-communigate_pro.t
new file mode 100644
index 0000000..88b8b64
--- /dev/null
+++ b/FS/t/part_export-communigate_pro.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::communigate_pro;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-communigate_pro_singledomain.t b/FS/t/part_export-communigate_pro_singledomain.t
new file mode 100644
index 0000000..6f8a64e
--- /dev/null
+++ b/FS/t/part_export-communigate_pro_singledomain.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::communigate_pro_singledomain;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-cp.t b/FS/t/part_export-cp.t
new file mode 100644
index 0000000..bbefa6c
--- /dev/null
+++ b/FS/t/part_export-cp.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::cp;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-cyrus.t b/FS/t/part_export-cyrus.t
new file mode 100644
index 0000000..e0b3f35
--- /dev/null
+++ b/FS/t/part_export-cyrus.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::cyrus;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-domain_shellcommands.t b/FS/t/part_export-domain_shellcommands.t
new file mode 100644
index 0000000..a2a44fb
--- /dev/null
+++ b/FS/t/part_export-domain_shellcommands.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::domain_shellcommands;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-forward_shellcommands.t b/FS/t/part_export-forward_shellcommands.t
new file mode 100644
index 0000000..78ca68d
--- /dev/null
+++ b/FS/t/part_export-forward_shellcommands.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::forward_shellcommands;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-http.t b/FS/t/part_export-http.t
new file mode 100644
index 0000000..ea41b93
--- /dev/null
+++ b/FS/t/part_export-http.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::http;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-infostreet.t b/FS/t/part_export-infostreet.t
new file mode 100644
index 0000000..1b33418
--- /dev/null
+++ b/FS/t/part_export-infostreet.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::infostreet;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-ldap.t b/FS/t/part_export-ldap.t
new file mode 100644
index 0000000..826c341
--- /dev/null
+++ b/FS/t/part_export-ldap.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::ldap;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-null.t b/FS/t/part_export-null.t
new file mode 100644
index 0000000..055cdce
--- /dev/null
+++ b/FS/t/part_export-null.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::null;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-passwdfile.t b/FS/t/part_export-passwdfile.t
new file mode 100644
index 0000000..0f18f30
--- /dev/null
+++ b/FS/t/part_export-passwdfile.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::passwdfile;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-postfix.t b/FS/t/part_export-postfix.t
new file mode 100644
index 0000000..9518caa
--- /dev/null
+++ b/FS/t/part_export-postfix.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::postfix;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-radiator.t b/FS/t/part_export-radiator.t
new file mode 100644
index 0000000..546e9de
--- /dev/null
+++ b/FS/t/part_export-radiator.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::radiator;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-router.t b/FS/t/part_export-router.t
new file mode 100644
index 0000000..54e4b63
--- /dev/null
+++ b/FS/t/part_export-router.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::router;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-shellcommands.t b/FS/t/part_export-shellcommands.t
new file mode 100644
index 0000000..7bb47d3
--- /dev/null
+++ b/FS/t/part_export-shellcommands.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::shellcommands;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-shellcommands_withdomain.t b/FS/t/part_export-shellcommands_withdomain.t
new file mode 100644
index 0000000..c0bd1bb
--- /dev/null
+++ b/FS/t/part_export-shellcommands_withdomain.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::shellcommands_withdomain;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-sqlmail.t b/FS/t/part_export-sqlmail.t
new file mode 100644
index 0000000..b048a75
--- /dev/null
+++ b/FS/t/part_export-sqlmail.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::sqlmail;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-sqlradius.t b/FS/t/part_export-sqlradius.t
new file mode 100644
index 0000000..5fb23a5
--- /dev/null
+++ b/FS/t/part_export-sqlradius.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::sqlradius;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-sqlradius_withdomain.t b/FS/t/part_export-sqlradius_withdomain.t
new file mode 100644
index 0000000..504bf67
--- /dev/null
+++ b/FS/t/part_export-sqlradius_withdomain.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::sqlradius_withdomain;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-sysvshell.t b/FS/t/part_export-sysvshell.t
new file mode 100644
index 0000000..7fc24ac
--- /dev/null
+++ b/FS/t/part_export-sysvshell.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::sysvshell;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-textradius.t b/FS/t/part_export-textradius.t
new file mode 100644
index 0000000..d8a48a0
--- /dev/null
+++ b/FS/t/part_export-textradius.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::textradius;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-vpopmail.t b/FS/t/part_export-vpopmail.t
new file mode 100644
index 0000000..2e37114
--- /dev/null
+++ b/FS/t/part_export-vpopmail.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::vpopmail;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export-www_shellcommands.t b/FS/t/part_export-www_shellcommands.t
new file mode 100644
index 0000000..2ea79cf
--- /dev/null
+++ b/FS/t/part_export-www_shellcommands.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::www_shellcommands;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export.t b/FS/t/part_export.t
new file mode 100644
index 0000000..26b3987
--- /dev/null
+++ b/FS/t/part_export.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_export_option.t b/FS/t/part_export_option.t
new file mode 100644
index 0000000..13200c2
--- /dev/null
+++ b/FS/t/part_export_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-flat.t b/FS/t/part_pkg-flat.t
new file mode 100644
index 0000000..3eee7a7
--- /dev/null
+++ b/FS/t/part_pkg-flat.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::flat;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-flat_comission.t b/FS/t/part_pkg-flat_comission.t
new file mode 100644
index 0000000..fefa57e
--- /dev/null
+++ b/FS/t/part_pkg-flat_comission.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::flat_comission;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-flat_comission_cust.t b/FS/t/part_pkg-flat_comission_cust.t
new file mode 100644
index 0000000..05d3ac4
--- /dev/null
+++ b/FS/t/part_pkg-flat_comission_cust.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::flat_comission_cust;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-flat_comission_pkg.t b/FS/t/part_pkg-flat_comission_pkg.t
new file mode 100644
index 0000000..851b58d
--- /dev/null
+++ b/FS/t/part_pkg-flat_comission_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::flat_comission_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-flat_delayed.t b/FS/t/part_pkg-flat_delayed.t
new file mode 100644
index 0000000..ed63846
--- /dev/null
+++ b/FS/t/part_pkg-flat_delayed.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::flat_delayed;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-prorate.t b/FS/t/part_pkg-prorate.t
new file mode 100644
index 0000000..d32b1c0
--- /dev/null
+++ b/FS/t/part_pkg-prorate.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::prorate;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-sesmon_hour.t b/FS/t/part_pkg-sesmon_hour.t
new file mode 100644
index 0000000..4f02cfc
--- /dev/null
+++ b/FS/t/part_pkg-sesmon_hour.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::sesmon_hour;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-sesmon_minute.t b/FS/t/part_pkg-sesmon_minute.t
new file mode 100644
index 0000000..6ceaa3c
--- /dev/null
+++ b/FS/t/part_pkg-sesmon_minute.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::sesmon_minute;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-sql_external.t b/FS/t/part_pkg-sql_external.t
new file mode 100644
index 0000000..366ed01
--- /dev/null
+++ b/FS/t/part_pkg-sql_external.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::sql_external;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-sql_generic.t b/FS/t/part_pkg-sql_generic.t
new file mode 100644
index 0000000..299a7c6
--- /dev/null
+++ b/FS/t/part_pkg-sql_generic.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::sql_generic;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-sqlradacct_hour.t b/FS/t/part_pkg-sqlradacct_hour.t
new file mode 100644
index 0000000..2a4ed79
--- /dev/null
+++ b/FS/t/part_pkg-sqlradacct_hour.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::sqlradacct_hour;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-subscription.t b/FS/t/part_pkg-subscription.t
new file mode 100644
index 0000000..10b4479
--- /dev/null
+++ b/FS/t/part_pkg-subscription.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::subscription;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-voip_cdr.t b/FS/t/part_pkg-voip_cdr.t
new file mode 100644
index 0000000..2d988a3
--- /dev/null
+++ b/FS/t/part_pkg-voip_cdr.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::voip_cdr;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg-voip_sqlradacct.t b/FS/t/part_pkg-voip_sqlradacct.t
new file mode 100644
index 0000000..8d54204
--- /dev/null
+++ b/FS/t/part_pkg-voip_sqlradacct.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg::voip_sqlradacct;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg.t b/FS/t/part_pkg.t
new file mode 100644
index 0000000..fd96073
--- /dev/null
+++ b/FS/t/part_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg_link.t b/FS/t/part_pkg_link.t
new file mode 100644
index 0000000..f5ada88
--- /dev/null
+++ b/FS/t/part_pkg_link.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg_link;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg_option.t b/FS/t/part_pkg_option.t
new file mode 100644
index 0000000..6239b2d
--- /dev/null
+++ b/FS/t/part_pkg_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg_taxclass.t b/FS/t/part_pkg_taxclass.t
new file mode 100644
index 0000000..bbe4073
--- /dev/null
+++ b/FS/t/part_pkg_taxclass.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg_taxclass;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg_taxoverride.t b/FS/t/part_pkg_taxoverride.t
new file mode 100644
index 0000000..d3b385d
--- /dev/null
+++ b/FS/t/part_pkg_taxoverride.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg_taxoverride;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg_taxproduct.t b/FS/t/part_pkg_taxproduct.t
new file mode 100644
index 0000000..a0aaa1d
--- /dev/null
+++ b/FS/t/part_pkg_taxproduct.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg_taxproduct;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pkg_taxrate.t b/FS/t/part_pkg_taxrate.t
new file mode 100644
index 0000000..6e5bee0
--- /dev/null
+++ b/FS/t/part_pkg_taxrate.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg_taxrate;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_pop_local.t b/FS/t/part_pop_local.t
new file mode 100644
index 0000000..4e4ad17
--- /dev/null
+++ b/FS/t/part_pop_local.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pop_local;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_referral.t b/FS/t/part_referral.t
new file mode 100644
index 0000000..d20b979
--- /dev/null
+++ b/FS/t/part_referral.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_referral;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_svc.t b/FS/t/part_svc.t
new file mode 100644
index 0000000..bdb2a7a
--- /dev/null
+++ b/FS/t/part_svc.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_svc;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/part_svc_column.t b/FS/t/part_svc_column.t
new file mode 100644
index 0000000..467025c
--- /dev/null
+++ b/FS/t/part_svc_column.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_svc_column;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/pay_batch.t b/FS/t/pay_batch.t
new file mode 100644
index 0000000..c43133d
--- /dev/null
+++ b/FS/t/pay_batch.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::pay_batch;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/payby.t b/FS/t/payby.t
new file mode 100644
index 0000000..7430bc8
--- /dev/null
+++ b/FS/t/payby.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::payby;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/payinfo_Mixin.t b/FS/t/payinfo_Mixin.t
new file mode 100644
index 0000000..3567c8e
--- /dev/null
+++ b/FS/t/payinfo_Mixin.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::payinfo_Mixin;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/payment_gateway.t b/FS/t/payment_gateway.t
new file mode 100644
index 0000000..4bcc781
--- /dev/null
+++ b/FS/t/payment_gateway.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::payment_gateway;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/payment_gateway_option.t b/FS/t/payment_gateway_option.t
new file mode 100644
index 0000000..19e6451
--- /dev/null
+++ b/FS/t/payment_gateway_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::payment_gateway_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/phone_avail.t b/FS/t/phone_avail.t
new file mode 100644
index 0000000..67f7e9a
--- /dev/null
+++ b/FS/t/phone_avail.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::phone_avail;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/pkg_category.t b/FS/t/pkg_category.t
new file mode 100644
index 0000000..ee256d5
--- /dev/null
+++ b/FS/t/pkg_category.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::pkg_category;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/pkg_class.t b/FS/t/pkg_class.t
new file mode 100644
index 0000000..fb3774f
--- /dev/null
+++ b/FS/t/pkg_class.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::pkg_class;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/pkg_referral.t b/FS/t/pkg_referral.t
new file mode 100644
index 0000000..ff047ba
--- /dev/null
+++ b/FS/t/pkg_referral.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::pkg_referral;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/pkg_svc.t b/FS/t/pkg_svc.t
new file mode 100644
index 0000000..77d3429
--- /dev/null
+++ b/FS/t/pkg_svc.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::pkg_svc;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/port.t b/FS/t/port.t
new file mode 100644
index 0000000..46377aa
--- /dev/null
+++ b/FS/t/port.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::port;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/prepay_credit.t b/FS/t/prepay_credit.t
new file mode 100644
index 0000000..e7626bd
--- /dev/null
+++ b/FS/t/prepay_credit.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::prepay_credit;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/queue.t b/FS/t/queue.t
new file mode 100644
index 0000000..43e3373
--- /dev/null
+++ b/FS/t/queue.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::queue;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/queue_arg.t b/FS/t/queue_arg.t
new file mode 100644
index 0000000..cf3f91d
--- /dev/null
+++ b/FS/t/queue_arg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::queue_arg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/queue_depend.t b/FS/t/queue_depend.t
new file mode 100644
index 0000000..8eaa2cd
--- /dev/null
+++ b/FS/t/queue_depend.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::queue_depend;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/raddb.t b/FS/t/raddb.t
new file mode 100644
index 0000000..ac28d07
--- /dev/null
+++ b/FS/t/raddb.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::raddb;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/radius_usergroup.t b/FS/t/radius_usergroup.t
new file mode 100644
index 0000000..325742c
--- /dev/null
+++ b/FS/t/radius_usergroup.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::radius_usergroup;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/rate.t b/FS/t/rate.t
new file mode 100644
index 0000000..ae9c8bb
--- /dev/null
+++ b/FS/t/rate.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::rate;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/rate_detail.t b/FS/t/rate_detail.t
new file mode 100644
index 0000000..163972e
--- /dev/null
+++ b/FS/t/rate_detail.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::rate_detail;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/rate_prefix.t b/FS/t/rate_prefix.t
new file mode 100644
index 0000000..d4bd513
--- /dev/null
+++ b/FS/t/rate_prefix.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::rate_prefix;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/rate_region.t b/FS/t/rate_region.t
new file mode 100644
index 0000000..6e0db8f
--- /dev/null
+++ b/FS/t/rate_region.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::rate_region;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/reason.t b/FS/t/reason.t
new file mode 100644
index 0000000..d5e4dc9
--- /dev/null
+++ b/FS/t/reason.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::reason;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/reason_type.t b/FS/t/reason_type.t
new file mode 100644
index 0000000..279d5b9
--- /dev/null
+++ b/FS/t/reason_type.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::reason_type;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/reg_code.t b/FS/t/reg_code.t
new file mode 100644
index 0000000..4b95990
--- /dev/null
+++ b/FS/t/reg_code.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::reg_code;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/reg_code_pkg.t b/FS/t/reg_code_pkg.t
new file mode 100644
index 0000000..7f89ffa
--- /dev/null
+++ b/FS/t/reg_code_pkg.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::reg_code_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/registrar.t b/FS/t/registrar.t
new file mode 100644
index 0000000..a6ba134
--- /dev/null
+++ b/FS/t/registrar.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::registrar;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/router.t b/FS/t/router.t
new file mode 100644
index 0000000..fe171b3
--- /dev/null
+++ b/FS/t/router.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::router;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/session.t b/FS/t/session.t
new file mode 100644
index 0000000..c4b714e
--- /dev/null
+++ b/FS/t/session.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::session;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_Common.t b/FS/t/svc_Common.t
new file mode 100644
index 0000000..ed49e1e
--- /dev/null
+++ b/FS/t/svc_Common.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_Common;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_External_Common.t b/FS/t/svc_External_Common.t
new file mode 100644
index 0000000..a0b2ea2
--- /dev/null
+++ b/FS/t/svc_External_Common.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_External_Common;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_Parent_Mixin.t b/FS/t/svc_Parent_Mixin.t
new file mode 100644
index 0000000..ed9923f
--- /dev/null
+++ b/FS/t/svc_Parent_Mixin.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_Parent_Mixin;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_acct.t b/FS/t/svc_acct.t
new file mode 100644
index 0000000..9ca78c9
--- /dev/null
+++ b/FS/t/svc_acct.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_acct;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_acct_pop.t b/FS/t/svc_acct_pop.t
new file mode 100644
index 0000000..e612c40
--- /dev/null
+++ b/FS/t/svc_acct_pop.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_acct_pop;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_broadband.t b/FS/t/svc_broadband.t
new file mode 100644
index 0000000..02dc112
--- /dev/null
+++ b/FS/t/svc_broadband.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_broadband;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_domain.t b/FS/t/svc_domain.t
new file mode 100644
index 0000000..4d91898
--- /dev/null
+++ b/FS/t/svc_domain.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_domain;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_external.t b/FS/t/svc_external.t
new file mode 100644
index 0000000..20a6767
--- /dev/null
+++ b/FS/t/svc_external.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_external;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_forward.t b/FS/t/svc_forward.t
new file mode 100644
index 0000000..d653d34
--- /dev/null
+++ b/FS/t/svc_forward.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_forward;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_phone.t b/FS/t/svc_phone.t
new file mode 100644
index 0000000..15b9ca2
--- /dev/null
+++ b/FS/t/svc_phone.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_phone;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/svc_www.t b/FS/t/svc_www.t
new file mode 100644
index 0000000..eb4e83f
--- /dev/null
+++ b/FS/t/svc_www.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_www;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/tax_class.t b/FS/t/tax_class.t
new file mode 100644
index 0000000..ddd8d9f
--- /dev/null
+++ b/FS/t/tax_class.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::tax_class;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/tax_rate.t b/FS/t/tax_rate.t
new file mode 100644
index 0000000..d498812
--- /dev/null
+++ b/FS/t/tax_rate.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::tax_rate;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/type_pkgs.t b/FS/t/type_pkgs.t
new file mode 100644
index 0000000..9840180
--- /dev/null
+++ b/FS/t/type_pkgs.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::type_pkgs;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/usage_class.t b/FS/t/usage_class.t
new file mode 100644
index 0000000..64fe98e
--- /dev/null
+++ b/FS/t/usage_class.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::usage_class;
+$loaded=1;
+print "ok 1\n";
diff --git a/INSTALL b/INSTALL
index ff2e43f..4ea1678 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1 +1,3 @@
-See htdocs/docs/index.html
+See:
+
+http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation#Installation_and_upgrades
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8df079c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,426 @@
+#!/usr/bin/make
+
+#solaris and perhaps other very weirdass /bin/sh
+#SHELL="/bin/ksh"
+
+DB_TYPE = Pg
+#DB_TYPE = mysql
+
+DB_USER = freeside
+DB_PASSWORD=
+
+DATASOURCE = DBI:${DB_TYPE}:dbname=freeside
+
+#changable now (some things which should go to the others still go to CONF)
+FREESIDE_CONF = /usr/local/etc/freeside
+FREESIDE_LOG = /usr/local/etc/freeside
+FREESIDE_LOCK = /usr/local/etc/freeside
+FREESIDE_CACHE = /usr/local/etc/freeside
+FREESIDE_EXPORT = /usr/local/etc/freeside
+
+MASON_HANDLER = ${FREESIDE_CONF}/handler.pl
+MASONDATA = ${FREESIDE_CACHE}/masondata
+
+#where to put the default configuraiton used by freeside-setup to initialize
+#a new database (not used after that). primarily of interest to distro
+#package maintainers
+DIST_CONF = ${FREESIDE_CONF}/default_conf
+
+#mod_perl v1
+#APACHE_VERSION = 1
+#mod_perl v2 prereleases up to and including 1.999_21
+#APACHE_VERSON = 1.99
+#mod_perl v2 proper and prereleases 1.999_22 and after
+APACHE_VERSION = 2
+
+#deb
+FREESIDE_DOCUMENT_ROOT = /var/www/freeside
+#redhat, fedora, mandrake
+#FREESIDE_DOCUMENT_ROOT = /var/www/html/freeside
+#freebsd
+#FREESIDE_DOCUMENT_ROOT = /usr/local/www/data/freeside
+#openbsd
+#FREESIDE_DOCUMENT_ROOT = /var/www/htdocs/freeside
+#suse
+#FREESIDE_DOCUMENT_ROOT = /srv/www/htdocs/freeside
+#apache
+#FREESIDE_DOCUMENT_ROOT = /usr/local/apache/htdocs/freeside
+
+#deb, redhat, fedora, mandrake, suse, others?
+INIT_FILE = /etc/init.d/freeside
+#freebsd
+#INIT_FILE = /usr/local/etc/rc.d/011.freeside.sh
+
+#deb
+INIT_INSTALL = /usr/sbin/update-rc.d freeside defaults 21 20
+#redhat, fedora
+#INIT_INSTALL = /sbin/chkconfig freeside on
+#not necessary (freebsd)
+#INIT_INSTALL = /usr/bin/true
+
+#deb, suse
+#HTTPD_RESTART = /etc/init.d/apache restart
+#deb w/apache2
+HTTPD_RESTART = /etc/init.d/apache2 restart
+#redhat, fedora, mandrake
+#HTTPD_RESTART = /etc/init.d/httpd restart
+#freebsd
+#HTTPD_RESTART = /usr/local/etc/rc.d/apache.sh stop || true; sleep 10; /usr/local/etc/rc.d/apache.sh start
+#openbsd
+#HTTPD_RESTART = kill -TERM `cat /var/www/logs/httpd.pid`; sleep 10; /usr/sbin/httpd -u -DSSL
+#apache
+#HTTPD_RESTART = /usr/local/apache/bin/apachectl stop; sleep 10; /usr/local/apache/bin/apachectl startssl
+
+#(an include directory, not a file, "Include /etc/apache/conf.d" in httpd.conf)
+#deb (3.1+), apache2
+APACHE_CONF = /etc/apache2/conf.d
+
+FREESIDE_RESTART = ${INIT_FILE} restart
+
+#deb, redhat, fedora, mandrake, suse, others?
+INSTALLGROUP = root
+#freebsd, openbsd
+#INSTALLGROUP = wheel
+
+#edit the stuff below to have the daemons start
+
+QUEUED_USER=fs_queue
+
+SELFSERVICE_USER = fs_selfservice
+#never run on the same machine in production!!!
+SELFSERVICE_MACHINES =
+# SELFSERVICE_MACHINES = www.example.com
+# SELFSERVICE_MACHINES = web1.example.com web2.example.com
+
+#user with sudo access on SELFSERVICE_MACHINES for automated self-service
+#installation.
+SELFSERVICE_INSTALL_USER = ivan
+SELFSERVICE_INSTALL_USERADD = /usr/sbin/useradd
+#SELFSERVICE_INSTALL_USERADD = "/usr/sbin/pw useradd"
+
+#RT_ENABLED = 0
+RT_ENABLED = 1
+RT_DOMAIN = example.com
+RT_TIMEZONE = US/Pacific
+#RT_TIMEZONE = US/Eastern
+FREESIDE_URL = "http://localhost/freeside/"
+
+#for now, same db as specified in DATASOURCE... eventually, otherwise?
+RT_DB_DATABASE = freeside
+
+# for cvs-upgrade-deploy target, the username who checked out the CVS copy.
+CVS_USER = ivan
+
+# for auto-version updates, so we can "make release" more things automatically
+RPM_SPECFILE = rpm/freeside.spec
+
+#---
+
+#rt/config.layout.in
+RT_PATH = /opt/rt3
+
+#only used for dev kludge now, not a big deal
+FREESIDE_PATH = `pwd`
+PERL_INC_DEV_KLUDGE = /usr/local/share/perl/5.8.8/
+
+VERSION=1.9.0cvs
+TAG=freeside_1_9_0
+
+DEBVERSION = `echo ${VERSION} | perl -pe 's/(\d)([a-z])/\1~\2/'`-1
+
+TEXMFHOME := "\$$TEXMFHOME"
+
+help:
+ @echo "supported targets:"
+ @echo " create-database create-config"
+ @echo " install deploy"
+ @echo " cvs-upgrade-deploy"
+ @echo " configure-rt create-rt"
+ @echo " clean help"
+ @echo
+ @echo " install-docs install-perl-modules"
+ @echo " install-init install-apache"
+ @echo " install-rt"
+ @echo " install-selfservice update-selfservice"
+ @echo
+ @echo " dev dev-docs dev-perl-modules"
+ @echo
+ @echo " masondocs alldocs docs"
+ @echo " wikiman"
+ @echo " perl-modules"
+ #@echo
+ #@echo " upload-docs release update-webdemo"
+
+
+masondocs: httemplate/* httemplate/*/* httemplate/*/*/* httemplate/*/*/*/*
+ rm -rf masondocs
+ cp -pr httemplate masondocs
+ touch masondocs
+
+alldocs: masondocs
+
+docs:
+ make masondocs
+
+wikiman:
+ chmod a+rx ./bin/pod2x
+ ./bin/pod2x
+
+install-docs: docs
+ [ -e ${FREESIDE_DOCUMENT_ROOT} ] && mv ${FREESIDE_DOCUMENT_ROOT} ${FREESIDE_DOCUMENT_ROOT}.`date +%Y%m%d%H%M%S` || true
+ cp -r masondocs ${FREESIDE_DOCUMENT_ROOT}
+ chown -R freeside:freeside ${FREESIDE_DOCUMENT_ROOT}
+ cp htetc/handler.pl ${MASON_HANDLER}
+ [ ! -e ${MASONDATA} ] && mkdir ${MASONDATA} || true
+ chown -R freeside ${MASONDATA}
+
+dev-docs:
+ [ -e ${FREESIDE_DOCUMENT_ROOT} ] && mv ${FREESIDE_DOCUMENT_ROOT} ${FREESIDE_DOCUMENT_ROOT}.`date +%Y%m%d%H%M%S` || true
+ ln -s ${FREESIDE_PATH}/httemplate ${FREESIDE_DOCUMENT_ROOT}
+ cp htetc/handler.pl ${MASON_HANDLER}
+ perl -p -i -e "\
+ s'###use Module::Refresh;###'use Module::Refresh;'; \
+ s'###Module::Refresh->refresh;###'Module::Refresh->refresh;'; \
+ " ${MASON_HANDLER} || true
+
+perl-modules:
+ cd FS; \
+ [ -e Makefile ] || perl Makefile.PL; \
+ make; \
+ perl -p -i -e "\
+ s/%%%VERSION%%%/${VERSION}/g;\
+ " blib/lib/FS.pm;\
+ perl -p -i -e "\
+ s|%%%FREESIDE_CONF%%%|${FREESIDE_CONF}|g;\
+ s|%%%FREESIDE_CACHE%%%|${FREESIDE_CACHE}|g;\
+ s'%%%FREESIDE_DOCUMENT_ROOT%%%'${FREESIDE_DOCUMENT_ROOT}'g; \
+ s'%%%RT_ENABLED%%%'${RT_ENABLED}'g; \
+ s'%%%MASONDATA%%%'${MASONDATA}'g;\
+ " blib/lib/FS/*.pm;\
+ perl -p -i -e "\
+ s|%%%FREESIDE_EXPORT%%%|${FREESIDE_EXPORT}|g;\
+ " blib/lib/FS/part_export/*.pm;\
+ perl -p -i -e "\
+ s|%%%FREESIDE_CACHE%%%|${FREESIDE_CACHE}|g;\
+ " blib/lib/FS/cust_main/*.pm;\
+ perl -p -i -e "\
+ s|%%%FREESIDE_CONF%%%|${FREESIDE_CONF}|g;\
+ s|%%%FREESIDE_LOG%%%|${FREESIDE_LOG}|g;\
+ s|%%%FREESIDE_LOCK%%%|${FREESIDE_LOCK}|g;\
+ s|%%%FREESIDE_CACHE%%%|${FREESIDE_CACHE}|g;\
+ s|%%%FREESIDE_EXPORT%%%|${FREESIDE_EXPORT}|g;\
+ s|%%%DIST_CONF%%%|${DIST_CONF}|g;\
+ " blib/script/*
+
+install-perl-modules: perl-modules
+ [ -L ${PERL_INC_DEV_KLUDGE}/FS ] \
+ && rm ${PERL_INC_DEV_KLUDGE}/FS \
+ && mv ${PERL_INC_DEV_KLUDGE}/FS.old ${PERL_INC_DEV_KLUDGE}/FS \
+ || true
+ cd FS; \
+ make install UNINST=1
+ #install this for freeside-setup
+ install -d $(DIST_CONF)
+ #install conf/[a-z]* $(DEFAULT_CONF)
+ #CVS is not [a-z]
+ install `ls -d conf/[a-z]* | grep -v CVS | grep -v '^conf/registries'` $(DIST_CONF)
+
+dev-perl-modules: perl-modules
+ [ -d ${PERL_INC_DEV_KLUDGE}/FS -a ! -L ${PERL_INC_DEV_KLUDGE}/FS ] \
+ && mv ${PERL_INC_DEV_KLUDGE}/FS ${PERL_INC_DEV_KLUDGE}/FS.old \
+ || true
+
+ rm -rf ${PERL_INC_DEV_KLUDGE}/FS
+ ln -sf ${FREESIDE_PATH}/FS/blib/lib/FS ${PERL_INC_DEV_KLUDGE}/FS
+
+install-texmf:
+ install -D -o freeside -m 444 etc/fslongtable.sty \
+ `kpsewhich -expand-var \\\$$TEXMFLOCAL`/tex/generic/fslongtable.sty
+ texhash `kpsewhich -expand-var \\\$$TEXMFLOCAL`
+
+install-init:
+ #[ -e ${INIT_FILE} ] || install -o root -g ${INSTALLGROUP} -m 711 init.d/freeside-init ${INIT_FILE}
+ install -o root -g ${INSTALLGROUP} -m 711 init.d/freeside-init ${INIT_FILE}
+ perl -p -i -e "\
+ s/%%%QUEUED_USER%%%/${QUEUED_USER}/g;\
+ s/%%%SELFSERVICE_USER%%%/${SELFSERVICE_USER}/g;\
+ s/%%%SELFSERVICE_MACHINES%%%/${SELFSERVICE_MACHINES}/g;\
+ " ${INIT_FILE}
+ ${INIT_INSTALL}
+
+install-apache:
+ [ -e ${APACHE_CONF}/freeside-base.conf ] && rm ${APACHE_CONF}/freeside-base.conf || true
+ [ -d ${APACHE_CONF} ] && \
+ ( install -o root -m 755 htetc/freeside-base${APACHE_VERSION}.conf ${APACHE_CONF} && \
+ ( [ ${RT_ENABLED} -eq 1 ] && install -o root -m 755 htetc/freeside-rt.conf ${APACHE_CONF} || true ) && \
+ perl -p -i -e "\
+ s'%%%FREESIDE_DOCUMENT_ROOT%%%'${FREESIDE_DOCUMENT_ROOT}'g; \
+ s'%%%FREESIDE_CONF%%%'${FREESIDE_CONF}'g; \
+ s'%%%MASON_HANDLER%%%'${MASON_HANDLER}'g; \
+ " ${APACHE_CONF}/freeside-*.conf \
+ ) || true
+
+install-selfservice:
+ [ -e ~freeside ] || cp -pr /etc/skel ~freeside && chown -R freeside ~freeside
+ [ -e ~freeside/.ssh/id_dsa.pub ] || [ -e ~freeside/.ssh/id_rsa.pub ] || su - freeside -c 'ssh-keygen -t dsa'
+ for MACHINE in ${SELFSERVICE_MACHINES}; do \
+ scp -r fs_selfservice/FS-SelfService ${SELFSERVICE_INSTALL_USER}@$$MACHINE:. ;\
+ ssh ${SELFSERVICE_INSTALL_USER}@$$MACHINE "cd FS-SelfService; perl Makefile.PL && make" ;\
+ ssh ${SELFSERVICE_INSTALL_USER}@$$MACHINE "cd FS-SelfService; sudo make install" ;\
+ scp ~freeside/.ssh/id_dsa.pub ${SELFSERVICE_INSTALL_USER}@$$MACHINE:. ;\
+ ssh ${SELFSERVICE_INSTALL_USER}@$$MACHINE "sudo ${SELFSERVICE_INSTALL_USERADD} freeside; sudo install -d -o freeside -m 600 ~freeside/.ssh/; sudo install -o freeside -m 600 ./id_dsa.pub ~freeside/.ssh/authorized_keys" ;\
+ ssh ${SELFSERVICE_INSTALL_USER}@$$MACHINE "sudo install -o freeside -d /usr/local/freeside" ;\
+ done
+
+update-selfservice:
+ for MACHINE in ${SELFSERVICE_MACHINES}; do \
+ RSYNC_RSH=ssh rsync -rlptz fs_selfservice/FS-SelfService/ ${SELFSERVICE_INSTALL_USER}@$$MACHINE:FS-SelfService ;\
+ ssh ${SELFSERVICE_INSTALL_USER}@$$MACHINE "cd FS-SelfService; make clean; perl Makefile.PL && make" ;\
+ ssh ${SELFSERVICE_INSTALL_USER}@$$MACHINE "cd FS-SelfService; sudo make install" ;\
+ done
+
+install: install-perl-modules install-docs install-init install-apache install-rt install-texmf
+
+deploy: install
+ ${HTTPD_RESTART}
+ ${FREESIDE_RESTART}
+
+cvs-upgrade-deploy:
+ su ${CVS_USER} -c 'cvs update -d -P'
+ make install-perl-modules
+ su freeside -c "freeside-upgrade ${CVS_USER}" #not really the same user
+ make deploy
+
+dev: dev-perl-modules dev-docs
+
+create-database:
+ perl -e 'use DBIx::DataSource qw( create_database ); create_database( "${DATASOURCE}", "${DB_USER}", "${DB_PASSWORD}" ) or die $$DBIx::DataSource::errstr;'
+
+create-config: install-perl-modules
+ [ -e ${FREESIDE_CONF} ] && mv ${FREESIDE_CONF} ${FREESIDE_CONF}.`date +%Y%m%d%H%M%S` || true
+ install -d -o freeside ${FREESIDE_CONF}
+
+ touch ${FREESIDE_CONF}/secrets
+ chown freeside ${FREESIDE_CONF}/secrets
+ chmod 600 ${FREESIDE_CONF}/secrets
+
+ echo -e "${DATASOURCE}\n${DB_USER}\n${DB_PASSWORD}" >${FREESIDE_CONF}/secrets
+ chmod 600 ${FREESIDE_CONF}/secrets
+ chown freeside ${FREESIDE_CONF}/secrets
+
+ mkdir "${FREESIDE_CONF}/conf.${DATASOURCE}"
+ rm -rf conf/registries #old dirs just won't go away
+ #cp conf/[a-z]* "${FREESIDE_CONF}/conf.${DATASOURCE}"
+ cp `ls -d conf/[a-z]* | grep -v CVS` "${FREESIDE_CONF}/conf.${DATASOURCE}"
+ chown -R freeside "${FREESIDE_CONF}/conf.${DATASOURCE}"
+
+ mkdir "${FREESIDE_CACHE}/counters.${DATASOURCE}"
+ chown freeside "${FREESIDE_CACHE}/counters.${DATASOURCE}"
+
+ mkdir "${FREESIDE_CACHE}/cache.${DATASOURCE}"
+ chown freeside "${FREESIDE_CACHE}/cache.${DATASOURCE}"
+
+ mkdir "${FREESIDE_EXPORT}/export.${DATASOURCE}"
+ chown freeside "${FREESIDE_EXPORT}/export.${DATASOURCE}"
+
+ #install this for freeside-setup
+ install -d $(DIST_CONF)
+ #install conf/[a-z]* $(DEFAULT_CONF)
+ #CVS is not [a-z]
+ install `ls -d conf/[a-z]* | grep -v CVS` $(DIST_CONF)
+
+configure-rt:
+ cd rt; \
+ cp config.layout.in config.layout; \
+ perl -p -i -e "\
+ s'%%%FREESIDE_DOCUMENT_ROOT%%%'${FREESIDE_DOCUMENT_ROOT}'g;\
+ s'%%%MASONDATA%%%'${MASONDATA}'g;\
+ " config.layout; \
+ ./configure --enable-layout=Freeside\
+ --with-db-type=${DB_TYPE} \
+ --with-db-dba=${DB_USER} \
+ --with-db-database=${RT_DB_DATABASE} \
+ --with-db-rt-user=${DB_USER} \
+ --with-db-rt-pass=${DB_PASSWORD} \
+ --with-web-user=freeside \
+ --with-web-group=freeside \
+ --with-rt-group=freeside
+
+create-rt: configure-rt
+ [ -d /opt ] || mkdir /opt #doh
+ [ -d /opt/rt3 ] || mkdir /opt/rt3 #
+ [ -d /opt/rt3/share ] || mkdir /opt/rt3/share #
+ cd rt; make install
+ rt/sbin/rt-setup-database --dba '${DB_USER}' \
+ -dba-password '${DB_PASSWORD}' \
+ -action schema \
+ || true
+ rt/sbin/rt-setup-database --action insert_initial \
+ && rt/sbin/rt-setup-database --action insert --datafile ${RT_PATH}/etc/initialdata \
+ || true
+
+install-rt:
+ perl -p -i -e "\
+ s'%%%RT_DOMAIN%%%'${RT_DOMAIN}'g;\
+ s'%%%RT_TIMEZONE%%%'${RT_TIMEZONE}'g;\
+ s'%%%FREESIDE_URL%%%'${FREESIDE_URL}'g;\
+ " ${RT_PATH}/etc/RT_SiteConfig.pm
+ [ ${RT_ENABLED} -eq 1 ] && ( cd rt; make install ) || true
+
+clean:
+ rm -rf masondocs
+ rm -rf httemplate/docs/man
+ rm -rf pod2htmi.tmp
+ rm -rf pod2htmd.tmp
+ -cd FS; \
+ make clean
+ -cd fs_selfservice/FS-SelfService; \
+ make clean
+
+#these are probably only useful if you're me...
+
+#release: upload-docs
+.PHONY: release
+release:
+ # Update the changelog
+ ./CVS2CL
+ cvs commit -m "Updated for ${VERSION}" ChangeLog
+
+ # Update the RPM specfile
+ cvs edit ${RPM_SPECFILE}
+ perl -p -i -e "s/\d+[^\}]+/${VERSION}/ if /%define\s+version\s+(\d+[^\}]+)\}/;" ${RPM_SPECFILE}
+ cvs commit -m "Updated for ${VERSION}" ${RPM_SPECFILE}
+
+ # Update the Debian changelog
+ cvs edit debian/changelog
+ dch -v ${DEBVERSION} -p "New upstream release"
+ cvs commit -m "Updated for ${VERSION}" debian/changelog
+
+ #cvs tag ${TAG}
+ cvs tag -F ${TAG}
+
+ #cd /home/ivan
+ cvs export -r ${TAG} -d freeside-${VERSION} freeside
+ tar czvf freeside-${VERSION}.tar.gz freeside-${VERSION}
+
+ scp freeside-${VERSION}.tar.gz ivan@420.am:/var/www/www.sisd.com/freeside/
+ mv freeside-${VERSION} freeside-${VERSION}.tar.gz ..
+
+ #these things failing should not make release target fail, so: "|| true"
+
+ #kick off vmware update
+ #./BUILD_VMWARE_APPLIANCE ${$TAG} || true
+
+ #kick off deb package update
+
+ #kick off rpm package update too?
+
+ #update web demo?
+
+ #update web demo self-service?
+
+update-webdemo:
+ ssh ivan@420.am '( cd freeside; cvs update -d -P )'
+ #ssh root@420.am '( cd /home/ivan/freeside; make clean; make deploy )'
+ ssh root@420.am '( cd /home/ivan/freeside; make deploy )'
+
diff --git a/README b/README
index 14234df..41ae52d 100644
--- a/README
+++ b/README
@@ -1,43 +1,36 @@
-Freeside, (pre) 1.1.4
+Freeside is a billing and administration package for Internet Service
+Providers, VoIP providers and other online businesses.
-Copyright (C) 1998 Silicon Interactive Software Design. All rights reserved.
+Copyright (C) 2005-2008 Freeside Internet Services, Inc.
+Copyright (C) 2000-2005 Ivan Kohler
+Copyright (C) 1999 Silicon Interactive Software Design
+Additional copyright holders may be found in the docs/license.html file.
+All rights reserved
This program is free software; you can redistribute it and/or modify
- it under the terms of either:
-
- a) the GNU General Public License as published by the Free
- Software Foundation; either version 2, or (at your option) any
- later version, or
-
- b) the "Artistic License"
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or (at
+ your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either the
- GNU General Public License or the Artistic License for more details.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
- You should have received a copy of the GNU General Public
- License along with this program, in the file `GPL'; if not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite
- 330, Boston, MA 02111-1307, USA.
-
- You should have received a copy of the Artistic License along with
- this program, in the file `Artistic'; if not, download it from
- http://www.perl.com/CPAN/doc/misc/license/Artistic
-
-Freeside is a billing and administration package for Internet Service
-Providers.
-
-The Freeside home page is at `http://www.sisd.com/freeside'.
+ You should have received a copy of the GNU Affero General Public
+ License along with this program, in the file `AGPL'; if not,
+ see <http://www.fsf.org/licensing/licenses/agpl-3.0.html>.
-The documentation is in `htdocs/docs'.
+The Freeside home page is at http://www.freeside.biz/freeside
-A mailing list for users and developers is available. Send a blank message to
-<ivan-freeside-subscribe@sisd.com> to subscribe.
+The documentation is at http://www.freeside.biz/mediawiki
-Commercial support is available from Ivan Kohler <ivan@sisd.com>. Please
-subscribe to the the mailing list to request free support!
+Community support resources are located at
+http://www.freeside.biz/freeside/developers.html
-Ivan Kohler
-ivan@sisd.com
+Preconfigured appliances, installation, customization, training and support
+services are available from Freeside Internet Services, Inc.
+Products: http://www.freeside.biz/freeside/products.html
+Services: http://www.freeside.biz/freeside/services.html
+Contact: http://www.freeside.biz/freeside/contact.html
diff --git a/TODO b/TODO
deleted file mode 100644
index 0171c32..0000000
--- a/TODO
+++ /dev/null
@@ -1,530 +0,0 @@
-If you are interested in helping with any of these, please join the mailing
-list (send a blank message to ivan-freeside-subscribe@sisd.com) to avoid
-duplication of effort.
-
--- 1.1.x --
-
-postgres can't deal with NULL!
-
-svc_acct.import should recognize "UNIX" in the RADIUS password file as null.
-
-radius logfile parsing and perl expression check.
-
-mailing list archive, faq, cvs
-
-(test cust_main.pm with cybercash v2 and v3)
-
-Fix in cust_bill BUGS:
-There is an off-by-one error in print_text which causes a visual error (Page 1
-of 2 printed on some single-page invoices).
-
-FIX It doesn't properly inherit/override FS::Record yet, so no more replace vs
-rep silliness!
-
-fields should be a method against a FS::Record or derived object, as well as
-being something you can call as FS::Record::fields('tablename'). Might
-even be able to handle both in the same routine (that would be neato).
-Get rid of hfields and other assorted silliness.
-Clean up hfields/sfields/fields crap. yuck.
-
-$lpr in cust_main.pm (from Bill.pm) should become /var/spool/freeside/conf/lpr
-
-Override FS::Record new, add, rep and del (create, insert, replace and
-delete) in all derived classes.
-IE create, insert, delete and replace from derived classes should override new,
-add, del and rep (respectively) from FS::Record. Depriciate old names.
-
-Allow a cancelled/suspended/active status from packages to bubble up to
-the customer lists. Put active, then suspended, then cancelled accounts.
-Similar ordering on the package listing inside a single customer.
-
-Add the ability for services to filter information up to the package level
-for invoices and web screens, so you can select a particlar package based
-on username or domain name, etc.
-
-You can't delete the stuff under administration yet. Add this,
-_including_ making sure the thing you are deleting is not in use!
-
-Immediate removal of incorrectly entered check payments (can't take too
-long to do this, or accounting is fubared).
-
-Add code to move from one service to another (POP to SLIP/PPP, etc.).
-This _should_ be possible by working off the rules in part_svc rather than
-hardcoding anything in. The rules in part_svc may need some elaboration,
-perhaps.
-
-Use ut_ FS::Record methods in all derived classes (possibly some from dbdef?... eventually all from dbdef??? - but then `dbdef-create' would be impossible as there would be metadata we couldn't ask the backend for. hmm.)
-
-(bring back from fsold, ) Generalize config-sending stuff and make more configurable.
-Expand the HylaFAX interface (also possibly generalize for other fax
-softwar ie .comfaxe); allow things like arbitrary faxes of sales
-literature, specific troubleshooting documents and so on. Maybe even
-allow users to do this (though that might not belong in Freeside).
-misc/sendconfig.cgi
-misc/process/sendconfig.cgi
-Configure fax recipients via a separate box rather than using the finger
-name or first+last from cust_main.
-
-move all phone number logic out of Freeside - let HylaFAX or whatever
-handle it.
-
-soundex searches for customer name and company? where are free soundex tools? (standard Text::Soundex duh)
-
-should be able to link on (username, domain name, some field in email alias) instead of svcnum only. (username done, what else?)
-
-(done but clean up) change svc_domain.pm mail sending from a pipe to "/usr/lib/sendmail" to Mail::Mailer or Net::SMTP or something. also is the complete text of the registration agreement needed in there (it used to be)?
-
-generalize and make configurable new invoice printing scheme in FS::Bill::collect (past due)
-
-deleting an svc_domain should delete all associated svc_acct_sm records.
-same with a svc_acct.
-
-periodic password encrypter
-
-Automated, configurable notification, suspension and cancellation of
-defunct accounts.
-...
-expire cron job
-...
-Allow for a future setup date on accounts.
-
-one-time/per-customer/? changes in rates and descriptions ('remembered
-invoices'): implement by creating a new package on the fly... but it isn't
-associated with any agent types so it won't show up for other customers to buy.
-
-if CGI::Base will not have redirect fixed (cgifix.html), should migrate to
-CGI.pm insetead? It is >1 year newer.
-
-library repetitve stuff from Bill.pm Invoice.pm and friends (calculating
-previous balances etc etc)
-
-
-sub AUTOLOAD in FS::Record should warn? die? if used with a non-existant column
-name?
-
-edit (not just import, export and allow default/fixed) arbitrary radius stuff
-in svc_acct
-
-edit/svc_acct.cgi and edit/process/svc_acct.cgi should deal with arbitrary radius stuff
-
-radius import should take DEFAULT entry and put it in /var/spool/freeside/conf/radius-default ; svc_acct.export should use it (and doc)
-
-FS::Invoice and FS::Bill should merge with the classes they're derived from
-
-in UI, s/State/State\/Provence/go and s/County/County\/Locality/go
-
-.us domains and others!
-
-what else (besides l10n) for i18n?
-
-audit htdocs/* for things that should be libraried and things that should be
-new methods on the objects (need to do this before implementing a new UI)
-all the big things are done
-
-some places we die() where we should &FS::CGI::idiot (and perhaps vice-versa).
-Decide based on whether or not the "error" should show up in logs.
-
-all .cgi's should use standard header/footer and idiot() subroutines. maybe HTML:: perl modules
-for HTML creation. CGI.pm instead.
-
-library the conf reading stuff; bin/svc_acct.export version with missing-filename checking is good
-library conf stuff -> check all the conf stuff to make sure they close filehandles.
-
-When running bin/bill, Fix this (Annoying but harmless):
-Use of uninitialized value at /usr/local/lib/site_perl/FS/cust_pkg.pm line 99, <ADDRESS> chunk 4.
-Use of uninitialized value at /usr/local/lib/site_perl/FS/cust_pkg.pm line 102, <ADDRESS> chunk 4.
-Use of uninitialized value at /usr/local/lib/site_perl/FS/cust_pkg.pm line 105, <ADDRESS> chunk 4.
-
-all cgi (but internal to the isp) places where package names are listed should also have
-comment (like agent_type)
-
-clean up $recref and other silliness and use -> calls where possible, or
-one other alternative. clean up everything else.
-should FS::Record use Tie::Hash? That would be very clean, but where do we
-store the other information? Maybe you could ask any FS::Record object for a
-tied hash?
-
-change all htdocs/edit/process/* loops to look like: (library this sort of thing!!!!)
-
-my($new) = create FS::svc_acct_sm ( {
- map {
- ($_, scalar($req->param($_)));
- } qw(svcnum pkgnum svcpart domuser domuid domsvc)
-} );
-
-to avoid form errors causing too much silliness
-
-add this code to all svc_*.pm (already in acct and acct_sm and domain): (library!)
-
- #get part_svc
- my($svcpart);
- my($svcnum)=$self->getfield('svcnum');
- if ($svcnum) {
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
- return "Unknown svcnum" unless $cust_svc;
- $svcpart=$cust_svc->svcpart;
- } else {
- $svcpart=$self->getfield('svcpart');
- }
- my($part_svc)=qsearchs('part_svc',{'svcpart'=>$svcpart});
- return "Unkonwn svcpart" unless $part_svc;
-
- #set fixed fields from part_svc
- my($field);
- foreach $field ( fields('svc_acct') ) {
- if ( $part_svc->getfield('svc_acct__'. $field. '_flag') eq 'F' ) {
- $self->setfield($field,$part_svc->getfield('svc_acct__'. $field) );
- }
- }
-
-change all file access from regular open(FILE,) stuff to OO, because of
-problems scoping and passing filehandles like that.
-
-svc_domain.pm mail sending uses Date::Format which doesn't seem to pick up
-correct timezone.
-
-view/svc_domain.cgi needs to know the domain might be unaudited (cosmetic)
-
-Check everything into CVS.
-
---- 1.1.x or 1.2 or later
-
-the web interface should create a new object and use it instead of a blank
-form for new records. the create method of svc_ objects should set defaults
-(from part_svc).
-
-sub check in man FS::table_name should be rewriteen. Get rid of $recref
-stuff. Make sure all fields that refer to other database are checked.
-
-Integration with signup disks (are there any free ones? Netscape?).
-
-One-button cancel (+refund) for lusers who can't get online.
-
-Keep information on virtual web servers (hostname, IP, host machine,
-directory, etc.) and export this information for importation into the ISPs
-web farm.
-
-Remove requirement that the first mail alias be the catchall? Still make
-sure only one catchall per domain is defined in any case, of course.
-
-Ability to move cust_pkg records from one customer to another? (proably
-will need to cancel the old and create a new like when we move services
-between packages).
-
-Auto-increment expired cards one year, and try again?
-
-Lay out the forms a bit better.
-
-More non-US stuff - zip codes, country codes, foreign currencies, etc.
-
-cust_refund.{cgi.pm} need to do cards xaxtions. (now we only have cust_credit)
-
-Nicer set of integrated reporting possibilities, like weekly sales totals
-by customer, package, agent, referral, etc., aging reports sorted by lots
-of different things, and so on.
-
-Client/server setup for users to modify their own passwords, shells, etc,
-via passwd or secure web interface (prelminary passwd/chfn/chsh
-replacement done). Complicated by the fact that we don't want to allow
-incoming connections to the machine running Freeside, so we probably need
-to have a daemon on each external shell or web machine that is contacted
-by the Freeside machine. Be very very careful for both traditional
-security issues and DoS problems.
-
-An extension of the above to allow users to modify selected parts of their
-own information, order and cancel services. A web interface for new
-customers.
-
-Expand domain name stuff to house all domain information. Export
-named.boot/named.conf (primary and secondary) and named.{domain} files.
-Add more registries (not just InterNIC's com org net edu)
-
-Nice postscript paper invoices, rather than current ASCII invoices.
-
-
-think about race-condititions in FS::Record and derived ->check ->insert
-and so on, uid and username checks in svc_acct, etc.
-
-Move to rsync over ssh file exportation rather than scp.
-
-check 'n fix the proactive password checker. (cracklib?)
-
-refunds of "BILL" payments: generate pseudo-check.
-
-write batch senders and batch parsers for the different credit card processors
-people use/
-More CC processors/methods.
-
-In FS::Record, the counter dir should have .datasrc appended to it like the
-dbdef does, which should place all the (most of) the DB metadata in unique
-files and let me run concurrent .datasrc's. Maybe do something similar for
-user, password and datasrc itself? (or something to get the out of the source
-files) and then we're set. (secrets file also needs .datasrc appended, or maybe
-"/var/spool/freeside".datasrc
-
-you should be able to fiddle the setup date in cust_pkg. (at least initially)
-
-cych v3 and v2 support
-
-delete options in administration section
-
-write a generic batch senders and batch parsers.
-
-need a way to override svc_acct export on a per-machine basis; just use config files based on machine name i suppose; document that.
-
-you should be able to get column types as a method against an FS::Record object
-as well as dbdef->table($table)->column($column)->type
-
-move to perl module for fuzzy and soundex searching.
-
-make fs-setup option to add sample data so you can click on "New Customer" right away? so people understand what this stuff is?
-
-package view needs to list extraneous services; we need to prevent the
-creation of them so this never happens (and mark it as such in the source)
-(the creation problem should be fixed - though they will still happen if people
-fsck around in the data manually, so list them anyway)
-
-add attribute dictionary to fs-setup as a menu, plus analyze users file to
-decide automatically
-
-Check for and report on duplicate billing accounts (cust_main, though many
-will have a need for these so probably don't disable them outright.)
-
-create a ->warn as well as a ->check method for all FS::table classes?
-(see above)
-
-something to automate making a release and updating the web demo
-
-export a debian-style (also redhat and?) /etc/group file aswell!
-
-seems to be an off-by-one error in the ascii invoice formatting which is saying
-"1 of 2" pages when there is only one.
-
-get rid of agrep? needs the (non-free) glimpse distribution. agrep used to
-be free? what else can do fuzzy searching?
-
-site_perl/svc_domain.cgi (hmm... or maybe should have a button? or maybe svc_domain.pm should handle this) should set $whois_hack for non-internic domains, so you can add them...
-
-svc_acct_sm.import qmail import should pull in recipientmap people too.
-
-.pm's like svc_acct.pm which need to do time-consuming things like ssh remotely
-should fork and do them in a child.
-
-i18n/l10n: take ALL messages and catalog them in english.txt or in database or something, so we can eventually go int'l. int'l currency support would be a help aswell.
-
-get some of { city, county, state, zip } from the missing bits if
-possible (where can i get the data to do this? usps.gov?)
-
-additional interfaces (perltk? java?)
-
-Put the GPL notice in all files.
-
--- 1.2 or later --
-
-$cust_bill->owed database field to be eliminated, replaced by a method call
-that calculates on the fly. make sure to grep for ->(get|set)field('owed')
-same for cust_credit->credited
-
-Export quota information.
-
-move all configuration to a central place. maybe in blob's in the
-database. maybe even things like the code to execute when a username is
-changed can be in there, so less of the distributed scripts change between
-different sites.
-
-Implement setup and recurring fees as Safe perl expressions rather than
-numbers, to allow for variable-rate services. Backwards compatibility is
-obtained because { 43 } in perl is still 43. :) Define API to pass
-starting and ending dates and any other necessary data to expression
-(fees are currently evaluated as Safe expressions but more work needs to
-be done to define an opmask for various needs, write examples
-(usage-based billing, etc.) and so on).
-...
-Add the ability to modify the next billing date in cust_pkg, and take
-appropriate action. This will allow the implementation of pro-rate/1st of
-the month billing as well as the ability to manually fiddle with
-anniversary dates in cust_pkg, so you can sync a customer's anniversary
-date even if you're using anniversary billing (manually or automatically).
-(now with above, we need to have a way to automatically pro-rate /^(\d+)$/
-charges - anything more complicated should figure it out itself given
-starting and ending dates [document that!])
-...
-Daily Radius log parsing into database; other logfile formats?
-...
-Callbacks to enforce hourly limits on accounts (suspend until the end of
-the billing period?), for those who limit customers rather than tack on
-extra charges.
-
-Flag packages (part_pkg) as taxable or non/taxable as some ISPs (for
-example) need to charge tax on equipment but not service (separate flags
-for setup and recurring fee... or perhaps a setup_tax, setup_notax,
-recur_tax and recur_notax fees, and possibly something more flexible if
-there is need).
-
-Allow for a variable number of invoices for customers who need multiple
-copies.
-
-Add a mail alias service with table svc_acct (not domain mail aliasing
-which is domain with svc_acct_sm)
-
-(bring back from fsold) Change customer comment field from its current kludge to something more
-workable.
-
-Better work orders with more information. Should eventually open a ticket
-when we have such a thing.
-edit/svc_wo.cgi
-edit/process/svc_wo.cgi
-Call tracking and trouble tickets.
-
-use mod_perl and Apache::AuthDBI instead of mod_auth_mysql when we do local
-users
-More accoutability for complimentary accounts: approval, expiration, term
-(no more than x months in advance) and notification.
-Flag particular users (or all users, for that matter) as having their
-passwords hidden and/or locked from users of Freeside (maybe need Freeside
-security levels first?).
-...
-Better Freeside-level configurable access, for those ISP's who have
-employees they can't trust. Right now you're "stuck" with setting up
-.htaccess stuff yourself. This should really just be integrated.
-
-update site_perl/table_template* (pry out of date)
-
-/var/spool/freeside/conf (and whatever else /var/spool/freeside we can)
-in database (except secrets), then web interface,
-make /var/spool/freeside a configurable directory (probably as part of
-some automated installation process?)
-
-add a table with column of export services (passwd, shadow, master.passwd, .qmail file update, dns update, etc.) and rows machine groups and whether or not to export that (and any necessary parameters). wasn't matt (vunderkid, not matt@michweb) working on this? find him? each machine goes in a group of its own as well as a group based on function. add a table with only svcpart and machine group. now, when you import from each machine, it can get its own accounts with one svcpart and universal accounts with another svcpart. (though that does make the username duplicate checking more interesting)
-
-password and slipip stuff in svc_acct.pm store need to be split into two fields or something, so the silliness in svc_acct.pm and svc_acct.export with looking at the data to decide what to do with it can be fixed (1.2)
-
-This requires some serious magic in FS::Record:
-ok, if date_type in fs-setup is to be something besides int,
-now we need to create wrappers
-for them so they behave identically across RDBMS's, ie date pops out as as
-UNIX timestamp (or an object of some sort? maybe even a blessed $obj which
-is a string not a hashref for backwards compatibility?) and so on. (remember
-to treat '0' as Not a Date instead of 1/1/70.
-
-Add Freeside-level transactions for RDBMS's which don't support
-transcations? (Currently we assume a minimal RDBMS which has no rollback,
-transactions or atomic updates). Or just require a RDBMS that supports
-rollback and/or atomic updates and get rid of the work-arounds? The /rdb
-interface had this kludge on top of it but is a technical dead-end in most
-other ways, unless it can gain an SQL parser and DBD interface.
-
-Better automated comparison of our CC records with processors (CyberCash,
-at least, has not always had 100% accuracy, though recent versions are
-much better)
-
-Expect or other pty based login check, where we actually connect to a
-terminal server or shell machine and test logging in as the user (if we
-are keeping a cleartext password for that user) (This is something tech
-support often needs for new customers)
-
-Use cust_main table for pre-sales tracking as well?
-
-Automatic commision report and check generation via freq and prog (to
-become a Safe perl expression) fields in agent table, and possibly others.
-
-Database and add a mailed-out date and method for disk/CD mailing, so a
-customer can call and you can say, "sent on xx/xx/xx via {US Mail, Fedex,
-UPS, etc}"
-
-Inventory tracking for physical items such as routers (for sale or
-lease... probably doesn't make a difference in the ordering... but if you
-cancel a router lease the inventory should come back. hmm.)
-
--- Matt's wishlist ---
-
-From matt@michweb.net Fri Feb 20 16:39:53 1998
-Date: Thu, 19 Feb 1998 23:20:11 -0500
-From: Matt Simerson <matt@michweb.net>
-Reply-To: quadran-developer@netgoth.com
-To: quadran-developer@netgoth.com
-Subject: Re: Welcome to quadran-developer
-
->Whats it based on and what is it supposed to do? I'm interested, but
->unfortunatly, I don't have that much time to help on the project (I'm busily
->working on one of my own based around MySQL and Qt right now -- don't know
->if it will be GPL'ed or not yet -- we'll probably just use it in house since
->it is designed around our system)...
-
-That's what I set out to find, but didn't find anything on the web site.
-I'm looking for something that will do the following:
-
-Single point of entry for users on a secure system:
- Creates account on user (public) systems
- update /etc/passwd/master.passwd file
- update radius database (if necessary)
- Set up up disk quotas (although I hacked adduser to do this)
- Option for adding user to a mailing list(s)
- Export of new user info to customizable report (for automated entry
-into
- accounting software, etc...)
-
-Automated billing:
- Export credit card info for batch processing and have hooks built
- in for other forms of electronic processing.
- Batch-Payment (apply payments from formatted text file).
- Customizable reports for manual entry/importing into Accounting
-software
- Email or laser print invoices
- Sanity checks credit card numbers before processing (code available)
-
-Simple method for disabling an account.
- Arbitrary Expiration Dates (on a given day, in x days)
- Remove from radius.
- Changing password to '*'
- Virtual customers disabling dns, http server, log processing, etc..
-
-Billing for different account types:
- Dialup monthly flat rate. Prorates for partial months.
- Dialup monthly flat rate for x hours + hourly usage.
- Dialup email only
- Email only accounts
- Virtual Web accounts - w/multiple mailboxes
- Leased line accounts
- Disk space used over quota.
- Tech support minimum + hourly charges
- Other for misc stuff (modem, RAM, etc...)
-
-Per user definable RADIUS attributes (ties in with above)
- Fixed IP
- Simultaneous Use
- IP filters (for dialup email only)
-
-Keep logs of modem usage generated daily from radius accounting logs stored
-on multiple radius servers.
-
-Keep logs of disk usage generated from quota.
-
-Method of adding virtual domains to your system:
- Automatically grabs an IP address from a preassigned pool.
- Creates a domain.com database file from database fields
- Updates /etc/named.conf or /etc/named.boot and reloads named.
- Add's virtual.com to /etc/sendmail.cw or qmail control files.
- Edits your web servers httpd.conf file and restarts http server.
- An optional section for adding vif's can be added if the users OS
- supports adding them on the fly. Otherwise it's up to the end
- user. Make a hook that can run a custom script that the user
- tweaks for his system.
- Update or create the config file your web stats analyzer needs. I've
- done this for analog (free) and http-analyze. Probably
- should only officially support analog and let users hack
- it to their hearts desire.
-I've already written scripts that do most of the virtual web stuff on my
-system...in bash. Shouldn't be hard for a perlmeister to convert. In fact,
-as long as all the info was stored in the database (username, domain name,
-and ip pool) this could easily just be run as an external script that the
-user tweaks to match his system.
-
-We use a great accounting software (M.Y.O.B) that does all the AP, AR,
-Payroll, Tax stuff, and most everything else we could need. It's already
-set up for the type of checks we have, etc, etc... I just need something to
-do the billing part. I can import/export sales and payments directly once
-the billing part is done. You can't write accounting software as good as
-M.Y.O.B. for $120.
-
-
diff --git a/bin/add-history-records.pl b/bin/add-history-records.pl
new file mode 100755
index 0000000..fbf9d09
--- /dev/null
+++ b/bin/add-history-records.pl
@@ -0,0 +1,139 @@
+#!/usr/bin/perl
+
+die "This is broken. Don't use it!\n";
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearchs qsearch);
+
+use Data::Dumper;
+
+my @tables = qw(svc_acct svc_broadband svc_domain svc_external svc_forward svc_www cust_svc domain_record);
+#my @tables = qw(svc_www);
+
+my $user = shift or die &usage;
+my $dbh = adminsuidsetup($user);
+
+my $dbdef = FS::Record::dbdef;
+
+foreach my $table (@tables) {
+
+ my $h_table = 'h_' . $table;
+ my $cnt = 0;
+ my $t_cnt = 0;
+
+ eval "use FS::${table}";
+ die $@ if $@;
+ eval "use FS::${h_table}";
+ die $@ if $@;
+
+ print "Adding history records for ${table}...\n";
+
+ my $dbdef_table = $dbdef->table($table);
+ my $pkey = $dbdef_table->primary_key;
+
+ foreach my $rec (qsearch($table, {})) {
+
+ #my $h_rec = qsearchs(
+ # $h_table,
+ # { $pkey => $rec->getfield($pkey) },
+ # eval "FS::${h_table}->sql_h_searchs(time)",
+ #);
+
+ my $h_rec = qsearchs(
+ $h_table,
+ { $pkey => $rec->getfield($pkey) },
+ "DISTINCT ON ( $pkey ) *",
+ "AND history_action = 'insert' ORDER BY $pkey ASC, history_date DESC",
+ '',
+ 'AS maintable',
+ );
+
+ unless ($h_rec) {
+ my $h_insert_rec = $rec->_h_statement('insert', 1);
+ #print $h_insert_rec . "\n";
+ $dbh->do($h_insert_rec);
+ die $dbh->errstr if $dbh->err;
+ $dbh->commit or die $dbh->errstr;
+ $cnt++;
+ }
+
+
+ $t_cnt++;
+
+ }
+
+ print "History records inserted into $h_table: $cnt\n";
+ print " Total records in $table: $t_cnt\n";
+
+ print "\n";
+
+}
+
+foreach my $table (@tables) {
+
+ my $h_table = 'h_' . $table;
+ my $cnt = 0;
+
+ eval "use FS::${table}";
+ die $@ if $@;
+ eval "use FS::${h_table}";
+ die $@ if $@;
+
+ print "Adding insert records for unmatched delete records on ${table}...\n";
+
+ my $dbdef_table = $dbdef->table($table);
+ my $pkey = $dbdef_table->primary_key;
+
+ #SELECT * FROM h_svc_www
+ #DISTINCT ON ( $pkey ) ?
+ my $where = "
+ WHERE ${pkey} in (
+ SELECT ${h_table}1.${pkey}
+ FROM ${h_table} as ${h_table}1
+ WHERE (
+ SELECT count(${h_table}2.${pkey})
+ FROM ${h_table} as ${h_table}2
+ WHERE ${h_table}2.${pkey} = ${h_table}1.${pkey}
+ AND ${h_table}2.history_action = 'delete'
+ ) > 0
+ AND (
+ SELECT count(${h_table}3.${pkey})
+ FROM ${h_table} as ${h_table}3
+ WHERE ${h_table}3.${pkey} = ${h_table}1.${pkey}
+ AND ( ${h_table}3.history_action = 'insert'
+ OR ${h_table}3.history_action = 'replace_new' )
+ ) = 0
+ GROUP BY ${h_table}1.${pkey})";
+
+
+ my @h_recs = qsearch(
+ $h_table, { },
+ "DISTINCT ON ( $pkey ) *",
+ $where,
+ '',
+ ''
+ );
+
+ foreach my $h_rec (@h_recs) {
+ #print "Adding insert record for deleted record with pkey='" . $h_rec->getfield($pkey) . "'...\n";
+ my $class = 'FS::' . $table;
+ my $rec = $class->new({ $h_rec->hash });
+ my $h_insert_rec = $rec->_h_statement('insert', 1);
+ #print $h_insert_rec . "\n";
+ $dbh->do($h_insert_rec);
+ die $dbh->errstr if $dbh->err;
+ $dbh->commit or die $dbh->errstr;
+ $cnt++;
+ }
+
+ print "History records inserted into $h_table: $cnt\n";
+
+}
+
+
+
+sub usage {
+ die "Usage:\n add-history-records.pl user\n";
+}
+
diff --git a/bin/all-postal-no-email b/bin/all-postal-no-email
new file mode 100755
index 0000000..ef5dff6
--- /dev/null
+++ b/bin/all-postal-no-email
@@ -0,0 +1,22 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_main;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+foreach my $cust_main ( qsearch( 'cust_main', {} ) ) {
+
+ print $cust_main->custnum. "\n";
+
+ $cust_main->invoicing_list( [ 'POST' ] );
+
+}
+
+sub usage {
+ die "Usage:\n\n all-postal-no-email user\n";
+}
+
diff --git a/bin/apache.export b/bin/apache.export
new file mode 100755
index 0000000..82eb6d6
--- /dev/null
+++ b/bin/apache.export
@@ -0,0 +1,94 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Std;
+#use File::Path;
+use File::Rsync;
+use Net::SSH qw(ssh);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+use FS::cust_svc;
+use FS::svc_www;
+
+use vars qw(%opt);
+getopts("d", \%opt);
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#needs the export number in there somewhere too...?
+my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/apache";
+mkdir $spooldir, 0700 unless -d $spooldir;
+
+my @exports = qsearch('part_export', { 'exporttype' => 'apache' } );
+
+my $rsync = File::Rsync->new({
+ rsh => 'ssh',
+# dry_run => 1,
+});
+
+foreach my $export ( @exports ) {
+
+ my $machine = $export->machine;
+ my $exportnum = $export->exportnum;
+ my $file = "$spooldir/$machine.exportnum$exportnum.conf";
+
+ warn "exporting apache configuration for $machine to $file\n"
+ if $opt{d};
+
+ open(HTTPD_CONF,">$file") or die "can't open $file: $!";
+
+ my $template = $export->option('template');
+
+ my @svc_www = $export->svc_x;
+
+ foreach my $svc_www ( @svc_www ) {
+ use vars qw($zone $username $dir $email $config);
+ $zone = $svc_www->domain_record->zone;
+ $config = $svc_www->config;
+ if ( $svc_www->svc_acct ) {
+ $username = $svc_www->svc_acct->username;
+ $dir = $svc_www->svc_acct->dir;
+ $email = $svc_www->svc_acct->email;
+ } else {
+ $username = '';
+ $dir = '';
+ $email = '';
+ }
+
+ warn " adding configuration section for $zone\n"
+ if $opt{d};
+
+ print HTTPD_CONF eval(qq("$template")). "\n\n";
+ }
+
+ my $user = $export->option('user');
+ my $httpd_conf = $export->option('httpd_conf');
+
+ warn "syncing $file to $httpd_conf on $machine\n"
+ if $opt{d};
+
+ $rsync->exec( {
+ src => $file,
+ dest => "$user\@$machine:$httpd_conf",
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+ # warn $rsync->out;
+
+ my $restart = $export->option('restart') || 'apachectl graceful';
+
+ warn "running restart command $restart on $machine\n"
+ if $opt{d};
+
+ ssh("root\@$machine", $restart);
+
+}
+
+close HTTPD_CONF;
+
+# -----
+
+sub usage {
+ die "Usage:\n apache.export [ -d ] user\n";
+}
+
diff --git a/bin/artera.import b/bin/artera.import
new file mode 100644
index 0000000..716ddda
--- /dev/null
+++ b/bin/artera.import
@@ -0,0 +1,75 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use Text::CSV_XS;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearchs);
+use FS::svc_external;
+use FS::svc_domain;
+use FS::svc_acct;
+
+$FS::svc_Common::noexport_hack = 1;
+
+my $svcpart = 30;
+
+my $user = shift
+ or die 'Usage:\n\n artera.import user <artera_active_orders.csv';
+adminsuidsetup $user;
+
+##
+
+my $csv = new Text::CSV_XS;
+
+my $header = scalar(<>);
+
+my( $num, $linked ) = ( 0, 0 );
+
+while (<>) {
+ my $status = $csv->parse($_)
+ or die $csv->error_input;
+ my($serial, $keycode, $name, $ordernum, $email) = $csv->fields();
+ #warn join(" - ", $serial, $keycode, $name, $ordernum, $email ). "\n";
+
+ $email =~ /^([^@]+)\@([^@]+)$/
+ or die $email;
+ my($username, $domain) = ( $1, $2 );
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } );
+ my $cust_svc = '';
+ if ( $svc_domain ) {
+ my $svc_acct = qsearchs('svc_acct', {
+ 'username' => $username,
+ 'domsvc' => $svc_domain->svcnum,
+ } );
+ $cust_svc = $svc_acct->cust_svc
+ if $svc_acct;
+ #} else {
+ # warn "can't find domain $domain\n";
+ }
+
+ my $exist = qsearchs('svc_external', { 'id' => $serial } );
+ next if $exist;
+
+ my $svc_external = new FS::svc_external {
+ 'svcpart' => $svcpart,
+ 'pkgnum' => ( $cust_svc ? $cust_svc->pkgnum : '' ),
+ 'id' => $serial,
+ 'title' => $keycode,
+ };
+ #my $error = $svc_external->check;
+ my $error = $svc_external->insert;
+ if ( $cust_svc && $error =~ /^Already/ ) {
+ warn $error;
+ $svc_external->pkgnum('');
+ $error = $svc_external->insert;
+ }
+ warn $error if $error;
+
+ $num++;
+ $linked++ if $cust_svc;
+ #print "$num imported, $linked linked\n";
+
+}
+
+print "$num imported, $linked linked\n";
+
diff --git a/bin/backup-dvd b/bin/backup-dvd
new file mode 100644
index 0000000..d0314b4
--- /dev/null
+++ b/bin/backup-dvd
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+database="freeside"
+DEVICE="/dev/hda"
+
+su freeside -c "pg_dump $database" >/var/backups/$database.sql
+
+DATE=$(date +%Y-%m-%d)
+
+#NOTE: These two paths must end in a / in
+#order to correctly build up the other paths
+#BACKUP_DIR="/backup/directory/"
+BACKUP_DIR="/backup/"
+ #TEMP_BACKUP_FILES_DIR="/backup/temp/"
+
+BACKUP_FILE=$BACKUP_DIR"backup-"$DATE".tar.bz2"
+ #DATABASE_FILE=$TEMP_BACKUP_FILES_DIR"foo-"$DATE".sql"
+
+ #These directories shouldn't end in a / although
+ #I don't think it will cause any problems if
+ #they do. There should be a space at the end though
+ #to ensure the database file gets concatenated correctly.
+ #SOURCE="/a/location /other/locations " $DATABASE_FILE
+
+#echo Removing old backup directories
+rm -rf $BACKUP_DIR
+ #rm -rf $TEMP_BACKUP_FILES_DIR
+
+#echo Creating new backup directories
+mkdir $BACKUP_DIR
+ #mkdir $TEMP_BACKUP_FILES_DIR
+
+ #echo Creating database backup
+ #pg_dump -U username -f $DATABASE_FILE databaseName
+
+#echo Backing up $SOURCE to file $BACKUP_FILE
+#tar -cvpl -f $BACKUP_FILE --anchored --exclude /backup /
+tar -cjpl -f $BACKUP_FILE --anchored --exclude /backup /
+
+ ##This is not necessary and possibly harmful for DVD+RW media
+ #echo Quick blanking media
+ #dvd+rw-format -blank /dev/hdc
+
+#echo Burning backup
+growisofs -dvd-compat -Z $DEVICE -quiet -r -J $BACKUP_FILE
diff --git a/bin/bill b/bin/bill
deleted file mode 100755
index 5c5be70..0000000
--- a/bin/bill
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/usr/local/bin/perl -Tw
-#
-# bill: Bill customer(s)
-#
-# Usage: bill [ -c [ i ] ] [ -d 'date' ] [ -b ]
-#
-# Bills all customers.
-#
-# Adds record to /dbin/cust_bill and /dbin/cust_pay (if payment made -
-# CARD & COMP), prints invoice / charges card etc.
-#
-# -c: Turn on collecting (you probably want this).
-#
-# -i: real-time billing (as opposed to batch billing). only relevant
-# for credit cards.
-#
-# -d: Pretent it's 'date'. Date is in any format Date::Parse is happy with,
-# but be careful.
-#
-# ## n/a ## -b: send batch when done billing
-#
-# ivan@voicenet.com sep/oct 96
-#
-# separated billing and collections, cleaned up code.
-# ivan@voicenet.com 96-nov-11
-#
-# added -d option
-# ivan@voicenet.com 96-nov-13
-#
-# added -v option and started to implement it, added 'd:' to getopts call
-# (oops!)
-# ivan@voicenet.com 97-jan-2
-#
-# added more debug messages, moved some searches to fssearch.pl library (for
-# speed)
-# rewrote "all customer" finder to know about bill dates, for speed.
-# ivan@voicenet.com 97-jan-8
-#
-# thought about it a while, and removed passing of the -d option to collect...?
-# ivan@voicenet.com 97-jan-14
-#
-# make all -v stuff STDERR
-# ivan@voicenet.com 97-feb-4
-#
-# added pkgnum as argument to program from /db/part_pkg, with kludge for the
-# "/bin/echo XX" 's already there.
-# ivan@voicenet.com 97-feb-23
-#
-# - general cleanup
-# - customers who are suspended can still be billed for the setup fee
-# - cust_pkg record is re-read after the package setup fee program is run.
-# this way,
-# that program can modify the record (for example, to start accounts off
-# suspended)
-# (best to think four or five times before modifying anything else!)
-# ivan@voicenet.com 97-feb-26
-#
-# don't bill recurring fee if its not time! (was removed)
-# ivan@voicenet.com 97-mar-6
-#
-# added -b option, send batch when done billing.
-# ivan@voicenet.com 97-apr-4
-#
-#insecure dependency on line 179ish below needs to be fixed before bill is
-#used setuid
-# ivan@voicenet.com 97-jun-2
-#
-# removed running of setup program (depriciated)
-# ivan@voicenet.com 97-jul-21
-#
-# rewrote for new API, removed option to specify custnums (use FS::Bill
-# instead), removed -v option (?)
-# ivan@voicenet.com 97-jul-22 - 23 - 25 -28
-# (need to add back in email stuff, look in /home/ivan/old/dbin/collect)
-#
-# s/suidsetup/adminsuidsetup/, s/FS::Search/FS::Record/, added some batch
-# exporting stuff (which still needs to be generalized) and removed &idiot
-# ivan@sisd.com 98-may-27
-
-# setup
-
-use strict;
-use Fcntl qw(:flock);
-use Date::Parse;
-use Getopt::Std;
-use FS::UID qw(adminsuidsetup swapuid);
-use FS::Record qw(qsearch qsearchs);
-use FS::Bill;
-
-my($batchfile)="/var/spool/freeside/batch";
-my($batchlock)="/var/spool/freeside/batch.lock";
-
-adminsuidsetup;
-
-&untaint_argv; #what it sounds like (eww)
-use vars qw($opt_b $opt_c $opt_i $opt_d);
-getopts("bcid:"); #switches
-
-#we're at now now (and later).
-my($time)= $main::opt_d ? str2time($main::opt_d) : $^T;
-
-# find packages w/ bill < time && cancel != '', and create corresponding
-# customer objects
-
-my($cust_main,%saw);
-foreach $cust_main (
- map {
- if ( ( $_->getfield('bill') || 0 ) <= $time &&
- !$saw{ $_->getfield('custnum') }++ ) {
- qsearchs('cust_main',{'custnum'=> $_->getfield('custnum') } );
- } else {
- ();
- }
- } qsearch('cust_pkg',{'cancel'=>''})
-) {
-
- # and bill them
-
- print "Billing customer #" . $cust_main->getfield('custnum') . "\n";
-
- bless($cust_main,"FS::Bill");
-
- my($error);
-
- $error=$cust_main->bill('time'=>$time);
- warn "Error billing, customer #" . $cust_main->getfield('custnum') .
- ":" . $error if $error;
-
- if ($main::opt_c) {
- $error=$cust_main->collect('invoice_time'=>$time,
- 'batch_card' => $main::opt_i ? 'no' : 'yes',
- );
- warn "Error collecting customer #" . $cust_main->getfield('custnum') .
- ":" . $error if $error;
-
- #sleep 1;
-
- }
-
-}
-
-#if ($main::opt_b) {
-#
-# die "Batch still waiting for reply? ($batchlock exists)\n" if -e $batchlock;
-# open(BATCHLOCK,"+>>$batchlock") or die "Can't open $batchlock: $!";
-# select(BATCHLOCK); $|=1; select(STDOUT);
-# unless ( flock(BATCHLOCK,,LOCK_EX|LOCK_NB) ) {
-# seek(BATCHLOCK,0,0);
-# my($pid)=<BATCHLOCK>;
-# chop($pid);
-# die "Is a batch running? (pid $pid)\n";
-# }
-# seek(BATCHLOCK,0,0);
-# print BATCHLOCK $$,"\n";
-#
-# ( open(BATCH,">$batchfile")
-# and flock(BATCH,LOCK_EX|LOCK_NB)
-# ) or die "Can't open $batchfile: $!";
-#
-# my($cust_pay_batch);
-# foreach $cust_pay_batch (qsearch('cust_pay_batch',{})) {
-# print BATCH join(':',
-# $_->getfield('cardnum'),
-# $_->getfield('exp'),
-# $_->getfield('amount'),
-# $_->getfield('payname')
-# || $_->getfield('first'). ' '. $_->getfield('last'),
-# "Description",
-# $_->getfield('zip'),
-# ),"\n";
-# }
-#
-# flock(BATCH,LOCK_UN);
-# close BATCH;
-#
-# flock(BATCHLOCK,LOCK_UN);
-# close BATCHLOCK;
-#}
-
-# subroutines
-
-sub untaint_argv {
- foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
- $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
- $ARGV[$_]=$1;
- }
-}
-
diff --git a/bin/bill-as-nextmonth b/bin/bill-as-nextmonth
new file mode 100755
index 0000000..813e841
--- /dev/null
+++ b/bin/bill-as-nextmonth
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+month=`date +%m`
+nextmonth=`expr $month + 1`
+/usr/local/bin/freeside-daily -d $nextmonth/1/`date +%Y` fs_daily
diff --git a/bin/bill-as-nextmonth-BILL b/bin/bill-as-nextmonth-BILL
new file mode 100755
index 0000000..91e9431
--- /dev/null
+++ b/bin/bill-as-nextmonth-BILL
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+month=`date +%m`
+nextmonth=`expr $month + 1`
+/usr/local/bin/freeside-daily -d $nextmonth/1/`date +%Y` -p BILL fs_daily
diff --git a/bin/bill-as-nextyear b/bin/bill-as-nextyear
new file mode 100755
index 0000000..63c4ad2
--- /dev/null
+++ b/bin/bill-as-nextyear
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+year=`date +%Y`
+nextyear=`expr $year + 1`
+/usr/local/bin/freeside-daily -d 1/1/$nextyear fs_daily
diff --git a/bin/bill-as-nextyear-BILL b/bin/bill-as-nextyear-BILL
new file mode 100755
index 0000000..0d77dd0
--- /dev/null
+++ b/bin/bill-as-nextyear-BILL
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+year=`date +%Y`
+nextyear=`expr $year + 1`
+/usr/local/bin/freeside-daily -d 1/1/$nextyear -p BILL fs_daily
diff --git a/bin/bill-for-nextmonth b/bin/bill-for-nextmonth
new file mode 100755
index 0000000..e1a3376
--- /dev/null
+++ b/bin/bill-for-nextmonth
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+month=`date +%m`
+nextmonth=`expr $month + 1`
+/usr/local/bin/freeside-daily -d $nextmonth/1/`date +%Y` -n fs_daily
diff --git a/bin/bill-for-nextyear b/bin/bill-for-nextyear
new file mode 100755
index 0000000..1430a58
--- /dev/null
+++ b/bin/bill-for-nextyear
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+year=`date +%Y`
+nextyear=`expr $year + 1`
+/usr/local/bin/freeside-daily -d 1/1/$nextyear -n fs_daily
diff --git a/bin/bill-nextmonth b/bin/bill-nextmonth
new file mode 100755
index 0000000..813e841
--- /dev/null
+++ b/bin/bill-nextmonth
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+month=`date +%m`
+nextmonth=`expr $month + 1`
+/usr/local/bin/freeside-daily -d $nextmonth/1/`date +%Y` fs_daily
diff --git a/bin/bill-nextyear b/bin/bill-nextyear
new file mode 100755
index 0000000..63c4ad2
--- /dev/null
+++ b/bin/bill-nextyear
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+year=`date +%Y`
+nextyear=`expr $year + 1`
+/usr/local/bin/freeside-daily -d 1/1/$nextyear fs_daily
diff --git a/bin/billco-upload b/bin/billco-upload
new file mode 100644
index 0000000..ce4a43d
--- /dev/null
+++ b/bin/billco-upload
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+AGENTNUMS="1 2 3"
+
+date=`date +"%Y%m%d"`
+dir="/usr/local/etc/freeside/export.DBI:Pg:dbname=freeside/cust_bill"
+cd "$dir"
+
+for AGENTNUM in $AGENTNUMS; do
+
+ for a in header detail; do
+ mv agentnum$AGENTNUM-$a.csv agentnum$AGENTNUM-$date-$a.csv
+ done
+
+ zip agentnum$AGENTNUM-$date.zip agentnum$AGENTNUM-$date-header.csv agentnum$AGENTNUM-$date-detail.csv
+
+ echo $dir/agentnum$AGENTNUM-$date.zip
+
+done
+
diff --git a/bin/bind.export b/bin/bind.export
new file mode 100755
index 0000000..286e43a
--- /dev/null
+++ b/bin/bind.export
@@ -0,0 +1,195 @@
+#!/usr/bin/perl -w
+
+use strict;
+use File::Path;
+use File::Rsync;
+use Net::SSH qw(ssh);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+use FS::cust_pkg;
+use FS::cust_svc;
+use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/bind";
+mkdir $spooldir, 0700 unless -d $spooldir;
+
+my @exports = qsearch('part_export', { 'exporttype' => 'bind' } );
+my @sexports = qsearch('part_export', { 'exporttype' => 'bind_slave' } );
+
+my $rsync = File::Rsync->new({
+ rsh => 'ssh',
+# dry_run => 1,
+});
+
+foreach my $export ( @exports ) {
+
+ my $machine = $export->machine;
+ my $prefix = "$spooldir/$machine";
+
+ my $bind_rel = $export->option('bind_release');
+ my $ndc_cmd = $export->option('reload')
+ || ( ($bind_rel eq 'BIND9') ? 'rndc' : 'ndc' );
+ my $minttl = $export->option('bind9_minttl');
+
+ #prevent old domain files from piling up
+ #rmtree "$prefix" or die "can't rmtree $prefix.db: $!";
+
+ mkdir $prefix, 0700 unless -d $prefix;
+
+ open(NAMED_CONF,">$prefix/named.conf")
+ or die "can't open $prefix/named.conf: $!";
+
+ if ( -e "$prefix/named.conf.HEADER" ) {
+ open(CONF_HEADER,"<$prefix/named.conf.HEADER")
+ or die "can't open $prefix/named.conf.HEADER: $!";
+ while (<CONF_HEADER>) { print NAMED_CONF $_; }
+ close CONF_HEADER;
+ }
+
+ my $zonepath = $export->option('zonepath');
+ $zonepath =~ s/\/$//;
+
+ my @svc_domain = $export->svc_x;
+
+ foreach my $svc_domain ( @svc_domain ) {
+ my $domain = $svc_domain->domain;
+ my @masters = qsearch('domain_record', {
+ 'svcnum' => $svc_domain->svcnum,
+ 'rectype' => '_mstr',
+ } );
+ if ( @masters ) {
+ my $masters = join('; ', map { $_->recdata } @masters );
+
+ print NAMED_CONF <<END;
+zone "$domain" {
+ type slave;
+ file "db.$domain";
+ masters { $masters; };
+};
+
+END
+
+ } else {
+
+ print NAMED_CONF <<END;
+zone "$domain" {
+ type master;
+ file "$zonepath/db.$domain";
+};
+
+END
+
+ open (DB_MASTER,">$prefix/db.$domain")
+ or die "can't open $prefix/db.$domain: $!";
+
+ if ($bind_rel eq 'BIND9') {
+ print DB_MASTER "\$TTL $minttl\n\$ORIGIN $domain.\n";
+ }
+
+ my @domain_records =
+ qsearch('domain_record', { 'svcnum' => $svc_domain->svcnum } );
+ foreach my $domain_record (
+ sort { $b->rectype cmp $a->rectype } @domain_records
+ ) {
+ #if ( $domain_record->rectype eq 'SOA' ) {
+ # print DB_MASTER join("\t", $domain_record-> reczone
+ #} else {
+ print DB_MASTER join("\t",
+ map { $domain_record->getfield($_) }
+ qw( reczone recaf rectype recdata )
+ ), "\n";
+ #}
+ }
+
+ close DB_MASTER;
+
+ }
+
+ }
+
+ $rsync->exec( {
+ src => "$prefix/",
+ recursive => 1,
+ dest => "root\@$machine:$zonepath/",
+ exclude => [qw( *.import named.conf.HEADER named.conf )],
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+ # warn $rsync->out;
+
+ $rsync->exec( {
+ src => "$prefix/named.conf",
+ dest => "root\@$machine:". $export->option('named_conf'),
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+# warn $rsync->out;
+
+ ssh("root\@$machine", "$ndc_cmd reload");
+
+}
+
+close NAMED_CONF;
+
+foreach my $sexport ( @sexports ) { #false laziness with above
+
+ my $machine = $sexport->machine;
+ my $prefix = "$spooldir/$machine";
+
+ my $bind_rel = $sexport->option('bind_release');
+ my $ndc_cmd = ($bind_rel eq 'BIND9') ? 'rndc' : 'ndc';
+
+ #prevent old domain files from piling up
+ #rmtree "$prefix" or die "can't rmtree $prefix.db: $!";
+
+ mkdir $prefix, 0700 unless -d $prefix;
+
+ open(NAMED_CONF,">$prefix/named.conf")
+ or die "can't open $prefix/named.conf: $!";
+
+ if ( -e "$prefix/named.conf.HEADER" ) {
+ open(CONF_HEADER,"<$prefix/named.conf.HEADER")
+ or die "can't open $prefix/named.conf.HEADER: $!";
+ while (<CONF_HEADER>) { print NAMED_CONF $_; }
+ close CONF_HEADER;
+ }
+
+ my $masters = $sexport->option('master');
+
+ #false laziness with freeside-sqlradius-reset
+ my @svc_domain =
+ map { qsearchs('svc_domain', { 'svcnum' => $_->svcnum } ) }
+ map { qsearch('cust_svc', { 'svcpart' => $_->svcpart } ) }
+ grep { qsearch('cust_svc', { 'svcpart' => $_->svcpart } ) }
+ $sexport->export_svc;
+
+ foreach my $svc_domain ( @svc_domain ) {
+ my $domain = $svc_domain->domain;
+ print NAMED_CONF <<END;
+zone "$domain" {
+ type slave;
+ file "db.$domain";
+ masters { $masters; };
+};
+
+END
+
+ }
+
+ $rsync->exec( {
+ src => "$prefix/named.conf",
+ dest => "root\@$machine:". $sexport->option('named_conf'),
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+# warn $rsync->out;
+
+ ssh("root\@$machine", "$ndc_cmd reload");
+
+}
+close NAMED_CONF;
+
+# -----
+
+sub usage {
+ die "Usage:\n bind.export user\n";
+}
+
diff --git a/bin/bind.import b/bin/bind.import
new file mode 100755
index 0000000..45db2e2
--- /dev/null
+++ b/bin/bind.import
@@ -0,0 +1,235 @@
+#!/usr/bin/perl -w
+#
+# REQUIRED:
+# -p: part number for domains
+#
+# -n: named.conf file (or an include file with zones you want to import),
+# for example root@ns.isp.com:/var/named/named.conf
+#
+# OPTIONAL:
+# -d: dry-run, debug: don't insert any records, just dump debugging output
+# -e: use existing domains records in Freeside
+# -s: import slave zones as master. useful if you need to recreate your
+# primary nameserver from a secondary
+# -c dir: override patch for downloading zone files (for example, when
+# downloading zone files from chrooted bind)
+#
+# need to manually put header in
+# /usr/local/etc/freeside/export.<datasrc./bind/<machine>/named.conf.HEADER
+# (or, nowadays, better just to include the file freeside exports)
+
+use strict;
+
+use vars qw($domain_svcpart);
+
+use Getopt::Std;
+use Data::Dumper;
+#use BIND::Conf_Parser;
+#use DNS::ZoneParse 0.81;
+
+use Net::SCP qw(scp iscp);
+
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch); #qsearchs);
+#use FS::svc_acct_sm;
+use FS::svc_domain;
+use FS::domain_record;
+#use FS::svc_acct;
+#use FS::part_svc;
+
+use vars qw($opt_p $opt_n $opt_s $opt_c $opt_d $opt_e);
+getopts("p:n:sc:de");
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+$FS::svc_Common::noexport_hack = 1;
+$FS::domain_record::noserial_hack = 1;
+
+use vars qw($spooldir);
+$spooldir = "/usr/local/etc/freeside/export.". datasrc. "/bind";
+mkdir $spooldir unless -d $spooldir;
+
+$domain_svcpart = $opt_p;
+
+my $named_conf = $opt_n;
+
+use vars qw($named_machine $prefix);
+$named_machine = (split(/:/, $named_conf))[0];
+my $pnamed_machine = $named_machine;
+$pnamed_machine =~ s/^[\w\-]+\@//;
+$prefix = "$spooldir/$pnamed_machine";
+mkdir $prefix unless -d $prefix;
+
+#iscp("$named_conf","$prefix/named.conf.import");
+scp("$named_conf","$prefix/named.conf.import");
+
+##
+
+$FS::svc_domain::whois_hack=1;
+
+my $p = Parser->new;
+$p->parse_file("$prefix/named.conf.import");
+
+print "\nBIND import completed.\n";
+
+##
+
+sub usage {
+ die "Usage:\n\n bind.import -p partnum -n \"user\@machine:/path/to/named.conf\" [ -s ] [ -c chroot_dir ] [ -d ] [ -e ] user\n";
+}
+
+########
+BEGIN {
+
+ package Parser;
+ use BIND::Conf_Parser;
+ use vars qw(@ISA $named_dir);
+ @ISA = qw(BIND::Conf_Parser);
+
+ $named_dir = 'COULD_NOT_FIND_NAMED_DIRECTORY_TRY_SETTING_-C_OPTION';
+ sub handle_option {
+ my($self, $option, $argument) = @_;
+ return unless $option eq "directory";
+ $named_dir = $argument;
+ #warn "found named dir: $named_dir\n";
+ }
+
+ sub handle_zone {
+ my($self, $name, $class, $type, $options) = @_;
+ return unless $class eq 'in';
+ return if grep { $name eq $_ } (qw(
+ . localhost 127.in-addr.arpa 0.in-addr.arpa 255.in-addr.arpa
+ 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa
+ 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.int
+ ));
+
+ use FS::Record qw(qsearchs);
+ use FS::svc_domain;
+
+ my $domain =
+ qsearchs('svc_domain', { 'domain' => $name } )
+ || new FS::svc_domain( {
+ svcpart => $main::domain_svcpart,
+ domain => $name,
+ action => 'N',
+ } );
+ unless ( $domain->svcnum ) {
+ my $error = $domain->insert;
+ die $error if $error;
+ }
+
+ if ( $type eq 'slave' && !$main::opt_s ) {
+
+ if ( $main::opt_d ) {
+
+ use Data::Dumper;
+ print "$name: ". Dumper($options);
+
+ } else {
+
+ foreach my $master ( @{ $options->{masters} } ) {
+ my $domain_record = new FS::domain_record( {
+ 'svcnum' => $domain->svcnum,
+ 'reczone' => '@',
+ 'recaf' => 'IN',
+ 'rectype' => '_mstr',
+ 'recdata' => $master,
+ } );
+ my $error = $domain_record->insert;
+ die $error if $error;
+ }
+
+ }
+
+ } elsif ( $type eq 'master' || ( $type eq 'slave' && $main::opt_s ) ) {
+
+ my $file = $options->{file};
+
+ use File::Basename;
+ my $basefile = basename($file);
+ my $sourcefile = $file;
+ if ( $main::opt_c ) {
+ $sourcefile = "$main::opt_c/$sourcefile" if $main::opt_c;
+ } else {
+ $sourcefile = "$named_dir/$sourcefile" unless $file =~ /^\//;
+ }
+
+ use Net::SCP qw(iscp scp);
+ #iscp("$main::named_machine:$sourcefile",
+ # "$main::prefix/$basefile.import");
+ scp("$main::named_machine:$sourcefile",
+ "$main::prefix/$basefile.import");
+
+ use DNS::ZoneParse 0.84;
+ my $zone = DNS::ZoneParse->new("$main::prefix/$basefile.import");
+
+ my $dump = $zone->dump;
+
+ if ( $main::opt_d ) {
+
+ use Data::Dumper;
+ print "$name: ". Dumper($dump);
+
+ } else {
+
+ foreach my $rectype ( keys %$dump ) {
+ if ( $rectype =~ /^SOA$/i ) {
+ my $rec = $dump->{$rectype};
+ $rec->{email} =~ s/\@/\./;
+ my $domain_record = new FS::domain_record( {
+ 'svcnum' => $domain->svcnum,
+ 'reczone' => $rec->{origin},
+ 'recaf' => 'IN',
+ 'rectype' => $rectype,
+ 'recdata' =>
+ $rec->{primary}. ' '. $rec->{email}. ' ( '.
+ join(' ', map $rec->{$_},
+ qw( serial refresh retry expire minimumTTL ) ).
+ ' )',
+ } );
+ my $error = $domain_record->insert;
+ die $error if $error;
+ } else {
+ #die $dump->{$rectype};
+
+ my $datasub;
+ if ( $rectype =~ /^MX$/i ) {
+ $datasub = sub { $_[0]->{priority}. ' '. $_[0]->{host}; };
+ } elsif ( $rectype =~ /^TXT$/i ) {
+ $datasub = sub { $_[0]->{text}; };
+ } else {
+ $datasub = sub { $_[0]->{host}; };
+ }
+
+ foreach my $rec ( @{ $dump->{$rectype} } ) {
+ my $domain_record = new FS::domain_record( {
+ 'svcnum' => $domain->svcnum,
+ 'reczone' => $rec->{name},
+ 'recaf' => $rec->{class} || 'IN',
+ 'rectype' => $rectype,
+ 'recdata' => &{$datasub}($rec),
+ } );
+ my $error = $domain_record->insert;
+ if ( $error ) {
+ warn "$error inserting ".
+ $rec->{name}. ' . '. $domain->domain. "\n";
+ warn Dumper($rec);
+ #system('cat',"$main::prefix/$basefile.import");
+ die;
+ }
+ }
+ }
+ }
+
+ }
+
+ #} else {
+ # die "unrecognized type $type\n";
+ }
+
+ }
+
+}
+#########
+
diff --git a/bin/breakdown-bill-applications b/bin/breakdown-bill-applications
new file mode 100644
index 0000000..44c3e36
--- /dev/null
+++ b/bin/breakdown-bill-applications
@@ -0,0 +1,25 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::UID qw(adminsuidsetup dbh);
+use FS::Record qw( qsearch );
+use FS::cust_bill_pay;
+use FS::cust_credit_bill;
+
+$FS::CurrentUser::upgrade_hack = 1;
+adminsuidsetup(shift) or die "Usage: breakdown-bill-applications username\n";
+
+#quick and dirty conversion script if you have enough memory to throw at it
+
+my @tables = qw( cust_bill_pay cust_credit_bill );
+
+my @apps = ();
+foreach my $table {
+ push @apps, qsearch($table,
+
+
+) {
+
+}
+
+foreach my $cust_bill_
diff --git a/bin/bsdshell.export b/bin/bsdshell.export
new file mode 100755
index 0000000..6e0d103
--- /dev/null
+++ b/bin/bsdshell.export
@@ -0,0 +1,114 @@
+#!/usr/bin/perl -w
+
+# bsdshell export
+
+use strict;
+use File::Rsync;
+use Net::SSH qw(ssh);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+use FS::cust_svc;
+use FS::svc_acct;
+
+my @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $spooldir = "/usr/local/etc/freeside/export.". datasrc;
+#my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/shell";
+
+my @bsd_exports = qsearch('part_export', { 'exporttype' => 'bsdshell' } );
+
+my $rsync = File::Rsync->new({
+ rsh => 'ssh',
+# dry_run => 1,
+});
+
+foreach my $export ( @bsd_exports ) {
+ my $machine = $export->machine;
+ my $prefix = "$spooldir/$machine";
+ mkdir $prefix, 0700 unless -d $prefix;
+
+ #LOCKING!!!
+
+ ( open(MASTER,">$prefix/master.passwd")
+ #!!! and flock(MASTER,LOCK_EX|LOCK_NB)
+ ) or die "Can't open $prefix/master.passwd: $!";
+ ( open(PASSWD,">$prefix/passwd")
+ #!!! and flock(PASSWD,LOCK_EX|LOCK_NB)
+ ) or die "Can't open $prefix/passwd: $!";
+
+ chmod 0644, "$prefix/passwd";
+ chmod 0600, "$prefix/master.passwd";
+
+ my @svc_acct = $export->svc_x;
+
+ next unless @svc_acct;
+
+ foreach my $svc_acct ( sort { $a->uid <=> $b->uid } @svc_acct ) {
+
+ my $password = $svc_acct->_password;
+ my $cpassword;
+ #if ( ( length($password) <= 8 )
+ if ( ( length($password) <= 12 )
+ && ( $password ne '*' )
+ && ( $password ne '!!' )
+ && ( $password ne '' )
+ ) {
+ $cpassword=crypt($password,
+ $saltset[int(rand(64))].$saltset[int(rand(64))]
+ );
+ # MD5 !!!!
+ } else {
+ $cpassword=$password;
+ }
+
+ ###
+ # FORMAT OF THE PASSWD FILE HERE
+ print PASSWD join(":",
+ $svc_acct->username,
+ 'x', # "##". $username,
+ $svc_acct->uid,
+ $svc_acct->gid,
+ $svc_acct->finger,
+ $svc_acct->dir,
+ $svc_acct->shell,
+ ), "\n";
+
+ ###
+ # FORMAT OF FreeBSD MASTER PASSWD FILE HERE
+ print MASTER join(":",
+ $svc_acct->username, # User name
+ $cpassword, # Encrypted password
+ $svc_acct->uid, # User ID
+ $svc_acct->gid, # Group ID
+ "", # Login Class
+ "0", # Password Change Time
+ "0", # Password Expiration Time
+ $svc_acct->finger, # Users name
+ $svc_acct->dir, # Users home directory
+ $svc_acct->shell, # shell
+ ), "\n" ;
+
+ }
+
+ #!!! flock(MASTER,LOCK_UN);
+ #!!! flock(PASSWD,LOCK_UN);
+ close MASTER;
+ close PASSWD;
+
+ $rsync->exec( {
+ src => "$prefix/passwd",
+ dest => "root\@$machine:/etc/passwd"
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+
+ $rsync->exec( {
+ src => "$prefix/master.passwd",
+ dest => "root\@$machine:/etc/master.passwd.new"
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+ ssh("root\@$machine", "pwd_mkdb /etc/master.passwd.new");
+
+ # UNLOCK!!
+}
diff --git a/bin/cch_tax_tool b/bin/cch_tax_tool
new file mode 100755
index 0000000..6261363
--- /dev/null
+++ b/bin/cch_tax_tool
@@ -0,0 +1,59 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+# this tool manipulates fixed length cch tax files by comparing the
+# update files in the $update_dir to the initial install files
+# in the $init_dir
+#
+# it produces .DOIT files in $update_dir which are suitable for
+# syncing a database initialzed with the files in $init_dir to
+# the state represented by the files in $update_dir
+#
+# how one acquires update files from cch that overlap with initial
+# full install remains a mystery
+
+my $init_dir = "cchinit/";
+my $update_dir = "cchupdate/";
+
+foreach my $file (qw (CODE DETAIL PLUS4 GEOCODE TXMATRIX ZIP)) {
+ my $tfile = $update_dir. $file. "T";
+ $tfile = $update_dir. "TXMATRIT" if $tfile =~ /TXMATRIXT$/;
+ open FILE, "$tfile.TXT" or die "Can't open $tfile.TXT\n";
+ open INSERT, ">$tfile.INS" or die "Can't open $tfile.INS\n";
+ open DELETE, ">$tfile.DEL" or die "Can't open $tfile.DEL\n";
+ while(<FILE>){
+ chomp;
+ print INSERT "$_\n" if s/I$//;
+ print DELETE "$_\n" if s/D$//;
+ }
+ close FILE;
+ close INSERT;
+ close DELETE;
+ system "sort $tfile.INS > $tfile.INSSORT";
+ system "sort $tfile.DEL > $tfile.DELSORT";
+ system "sort $init_dir$file.txt > $tfile.ORGINSSORT";
+ system "comm -12 $tfile.INSSORT $tfile.ORGINSSORT > $tfile.PREINS";
+ system "comm -23 $tfile.INSSORT $tfile.ORGINSSORT > $tfile.2BEINS";
+ system "comm -23 $tfile.DELSORT $tfile.ORGINSSORT > $tfile.PREDEL";
+ system "comm -12 $tfile.DELSORT $tfile.ORGINSSORT > $tfile.2BEDEL";
+}
+
+foreach my $file (qw (CODET DETAILT PLUS4T GEOCODET TXMATRIT ZIPT)) {
+ my $tfile = $update_dir. $file;
+ $tfile = "TXMATRIT" if $tfile eq "TXMATRIXT";
+ open INSERT, "$tfile.2BEINS" or die "Can't open $tfile.2BEINS\n";
+ open DELETE, "$tfile.2BEDEL" or die "Can't open $tfile.2BEDEL\n";
+ open FILE, ">$tfile.DOIT" or die "Can't open $tfile.DOIT\n";
+ while(<INSERT>){
+ chomp;
+ print FILE $_, "I\n";
+ }
+ while(<DELETE>){
+ chomp;
+ print FILE $_, "D\n";
+ }
+ close FILE;
+ close INSERT;
+ close DELETE;
+}
diff --git a/bin/cdr.http_and_import b/bin/cdr.http_and_import
new file mode 100755
index 0000000..5637fa5
--- /dev/null
+++ b/bin/cdr.http_and_import
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+#
+# Usage:
+# cdr.http_and_import [ -p prefix ] [ -e extension ] [ -v ] user format URL
+#
+# -e: file extension, defaults to .csv
+# -d: if specified, moves files to the specified folder when done
+
+use strict;
+use Getopt::Std;
+use WWW::IndexParser;
+#use LWP::UserAgent;
+use FS::UID qw(adminsuidsetup datasrc dbh);
+use FS::cdr;
+
+###
+# parse command line
+###
+
+use vars qw( $opt_p $opt_e $opt_v );
+getopts('p:e:v');
+
+$opt_e ||= 'csv';
+#$opt_e = ".$opt_e" unless $opt_e =~ /^\./;
+$opt_e =~ s/^\.//;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+# %%%FREESIDE_CACHE%%%
+my $cachedir = '/usr/local/etc/freeside/cache.'. datasrc. '/cdrs';
+mkdir $cachedir unless -d $cachedir;
+
+my $format = shift or die &usage;
+
+use vars qw( $URL );
+$URL = shift or die &usage;
+
+###
+# get the file list
+###
+
+warn "Retreiving directory listing\n" if $opt_v;
+
+my @files = WWW::IndexParser->new(url => $URL);
+
+###
+# import each file
+###
+
+foreach my $file ( @files ) {
+
+ my $filename = $file->{filename};
+
+ if ( $opt_p ) { next unless $filename =~ /^$opt_p/ };
+ if ( $opt_e ) { next unless $filename =~ /\.$opt_e$/i };
+
+ #check and see if we've gotten this file already!!!
+ #just going to cheat with filenames in the cache for now
+ if ( -e "$cachedir/$filename" ) {
+ warn "Already have unprocessed $cachedir/$filename; skipping\n"; # if $opt_v;
+ next;
+ }
+ if ( -e "$cachedir/$filename.DONE" ) {
+ warn "Already processed $cachedir/$filename; skipping\n" if $opt_v;
+ next;
+ }
+
+ warn "Downloading $filename\n" if $opt_v;
+
+ #get the file
+
+ my $ua = LWP::UserAgent->new;
+ my $response = $ua->get("$URL/$filename");
+
+ unless ( $response->is_success ) {
+ die "Error retreiving $URL/$filename: ". $response->status_line;
+ }
+
+ open(FILE, ">$cachedir/$filename")
+ or die "can't open $cachedir/$filename: $!";
+ print FILE $response->content;
+ close FILE or die "can't close $cachedir/$filename: $!";
+
+ warn "Processing $filename\n" if $opt_v;
+
+ my $error = FS::cdr::batch_import( {
+ 'file' => "$cachedir/$filename",
+ 'format' => $format,
+ 'params' => { 'cdrbatch' => $filename },
+ 'empty_ok' => 1,
+ } );
+ die $error if $error;
+
+ close FILE;
+
+ rename("$cachedir/$filename", "$cachedir/$filename.DONE");
+
+}
+
+###
+# sub
+###
+
+sub usage {
+ "Usage: \n cdr.http_and_import [ -p prefix ] [ -e extension ] [ -v ] user format URL\n";
+}
+
diff --git a/bin/cdr.import b/bin/cdr.import
new file mode 100644
index 0000000..a17417b
--- /dev/null
+++ b/bin/cdr.import
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+#
+# Usage:
+# cdr.import user format filename
+#
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::cdr;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $format = shift or die &usage;
+
+my $file = shift;
+
+my $error = FS::cdr::batch_import( {
+ 'file' => $file,
+ 'format' => $format,
+ 'params' => { 'cdrbatch' => $file },
+} );
+die $error if $error;
+
+sub usage {
+ "Usage: \n cdr.import user format filename\n";
+}
+
diff --git a/bin/cdr.sftp_and_import b/bin/cdr.sftp_and_import
new file mode 100755
index 0000000..79e743f
--- /dev/null
+++ b/bin/cdr.sftp_and_import
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+#
+# Usage:
+# cdr.sftp_and_import [ -e extension ] [ -d donefolder ] [ -v ] user format [sftpuser@]servername
+#
+# -e: file extension, defaults to .csv
+# -d: if specified, moves files to the specified folder when done
+
+use strict;
+use Getopt::Std;
+use Net::SFTP::Foreign;
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::cdr;
+
+###
+# parse command line
+###
+
+use vars qw( $opt_e $opt_d $opt_v );
+getopts('e:d:v');
+
+$opt_e ||= 'csv';
+#$opt_e = ".$opt_e" unless $opt_e =~ /^\./;
+$opt_e =~ s/^\.//;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+# %%%FREESIDE_CACHE%%%
+my $cachedir = '/usr/local/etc/freeside/cache.'. datasrc. '/cdrs';
+mkdir $cachedir unless -d $cachedir;
+
+my $format = shift or die &usage;
+
+use vars qw( $servername );
+$servername = shift or die &usage;
+
+###
+# get the file list
+###
+
+warn "Retreiving directory listing\n" if $opt_v;
+
+my $ls_sftp = sftp();
+
+my $ls = $ls_sftp->ls('.', wanted => qr/\.*$opt_e$/i );
+
+###
+# import each file
+###
+
+foreach my $file ( @$ls ) {
+
+ my $filename = $file->{filename};
+ warn "Downloading $filename\n" if $opt_v;
+
+ #get the file
+ my $get_sftp = sftp();
+ $get_sftp->get($filename, "$cachedir/$filename")
+ or die "Can't get $filename: ". $get_sftp->error;
+
+ warn "Processing $filename\n" if $opt_v;
+
+ my $error = FS::cdr::batch_import( {
+ 'file' => "$cachedir/$filename"
+ 'format' => $format,
+ 'params' => { 'cdrbatch' => $filename, },
+ 'empty_ok' => 1,
+ } );
+ die $error if $error;
+
+ close FILE;
+
+ if ( $opt_d ) {
+ my $mv_sftp = sftp();
+ $mv_sftp->rename($filename, "$opt_d/$filename")
+ or die "can't move $filename to $opt_d: ". $mv_sftp->error;
+ }
+
+ unlink "$cachedir/$filename";
+
+}
+
+1;
+
+###
+# sub
+###
+
+sub usage {
+ "Usage: \n cdr.import user format servername\n";
+}
+
+use vars qw( $sftp );
+
+sub sftp {
+
+ #reuse connections
+ return $sftp if $sftp && $sftp->cwd;
+
+ my %sftp = ( host => $servername );
+
+ #XXX remove these
+ $sftp{port} = 10022;
+ #$sftp{more} = '-v';
+
+ $sftp = Net::SFTP::Foreign->new(%sftp);
+ $sftp->error and die "SFTP connection failed: ". $sftp->error;
+
+ $sftp;
+}
+
diff --git a/bin/cdr_calltype.import b/bin/cdr_calltype.import
new file mode 100755
index 0000000..a998284
--- /dev/null
+++ b/bin/cdr_calltype.import
@@ -0,0 +1,41 @@
+#!/usr/bin/perl -w
+#
+# bin/cdr_calltype.import ivan ~ivan/convergent/newspecs/fixed_inbound/calltypes.csv
+
+use strict;
+use FS::UID qw(dbh adminsuidsetup);
+use FS::cdr_calltype;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+while (<>) {
+
+ chomp;
+ my $line = $_;
+
+ #$line =~ /^(\d+),"([^"]+)"$/ or do {
+ $line =~ /^(\d+),"([^"]+)"/ or do {
+ warn "unparsable line: $line\n";
+ next;
+ };
+
+ my $cdr_calltype = new FS::cdr_calltype {
+ 'calltypenum' => $1,
+ 'calltypename' => $2,
+ };
+
+ #my $error = $cdr_calltype->check;
+ my $error = $cdr_calltype->insert;
+ if ( $error ) {
+ warn "********** $error FOR LINE: $line\n";
+ dbh->commit;
+ #my $wait = scalar(<STDIN>);
+ }
+
+}
+
+sub usage {
+ "Usage:\n\ncdr_calltype.import username filename ...\n";
+}
+
diff --git a/bin/cdr_upstream_rate.import b/bin/cdr_upstream_rate.import
new file mode 100755
index 0000000..fda3883
--- /dev/null
+++ b/bin/cdr_upstream_rate.import
@@ -0,0 +1,142 @@
+#!/usr/bin/perl -w
+#
+# Usage: bin/cdr_upstream_rate.import username ratenum filename
+#
+# records will be imported into cdr_upstream_rate, rate_detail and rate_region
+#
+# Example: bin/cdr_upstream_rate.import ivan 1 ~ivan/convergent/sample_rate_table.csv
+#
+# username: a freeside login (from /usr/local/etc/freeside/mapsecrets)
+# ratenum: rate plan (FS::rate) created with the web UI
+# filename: CSV file
+#
+# the following fields are currently used:
+# - Class Code => cdr_upstream_rate.rateid
+# - Description => rate_region.regionname
+# (rate_detail->dest_region)
+# - 1_rate => ( * 60 / 1_rate_seconds ) => rate_detail.min_charge
+# - 1_rate_seconds => (used above)
+# - 1_second_increment => rate_detail.sec_granularity
+#
+# the following fields are not (yet) used:
+# - Flagfall => what's this for?
+#
+# - 1_cap_time => freeside doesn't have voip time caps yet...
+# - 1_cap_cost => freeside doesn't have voip cost caps yet...
+# - 1_repeat => not sure what this is for, sample data is all 0
+#
+# - 2_rate => \
+# - 2_rate_seconds => |
+# - 2_second_increment => | not sure what the second set of rate data
+# - 2_cap_time => | is supposed to be for...
+# - 2_cap_cost => |
+# - 2_repeat => /
+#
+# - Carrier => probably not needed?
+# - Start Date => not necessary?
+
+use strict;
+use vars qw( $DEBUG );
+use Text::CSV_XS;
+use FS::UID qw(dbh adminsuidsetup);
+use FS::Record qw(qsearchs);
+use FS::rate;
+use FS::cdr_upstream_rate;
+use FS::rate_detail;
+use FS::rate_region;
+
+$DEBUG = 1;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $ratenum = shift or die &usage;
+
+my $rate = qsearchs( 'rate', { 'ratenum' => $ratenum } );
+die "rate plan $ratenum not found in rate table\n"
+ unless $rate;
+
+my $csv = new Text::CSV_XS;
+my $hline = scalar(<>);
+chomp($hline);
+$csv->parse($hline) or die "can't parse header: $hline\n";
+my @header = $csv->fields();
+
+$FS::UID::AutoCommit = 0;
+
+while (<>) {
+
+ chomp;
+ my $line = $_;
+
+# #$line =~ /^(\d+),"([^"]+)"$/ or do {
+# #}
+# $line =~ /^(\d+),"([^"]+)"/ or do {
+# warn "unparsable line: $line\n";
+# next;
+# };
+
+ $csv->parse($line) or die "can't parse line: $line\n";
+ my @line = $csv->fields();
+
+ my %hash = map { $_ => shift(@line) } @header;
+
+ warn join('', map { "$_ => $hash{$_}\n" } keys %hash )
+ if $DEBUG > 1;
+
+ my $rate_region = new FS::rate_region {
+ 'regionname' => $hash{'Description'}
+ };
+
+ my $error = $rate_region->insert;
+ if ( $error ) {
+ dbh->rollback;
+ die "error inserting into rate_region: $error\n";
+ }
+ my $dest_regionnum = $rate_region->regionnum;
+ warn "rate_region $dest_regionnum inserted\n"
+ if $DEBUG;
+
+ my $rate_detail = new FS::rate_detail {
+ 'ratenum' => $ratenum,
+ 'dest_regionnum' => $dest_regionnum,
+ 'min_included' => 0,
+ #'min_charge', => sprintf('%.5f', 60 * $hash{'1_rate'} / $hash{'1_rate_seconds'} ),
+ 'min_charge', => sprintf('%.5f', $hash{'1_rate'} /
+ ( $hash{'1_rate_seconds'} / 60 )
+ ),
+ 'sec_granularity' => $hash{'1_second_increment'},
+ };
+ $error = $rate_detail->insert;
+ if ( $error ) {
+ dbh->rollback;
+ die "error inserting into rate_detail: $error\n";
+ }
+ my $ratedetailnum = $rate_detail->ratedetailnum;
+ warn "rate_detail $ratedetailnum inserted\n"
+ if $DEBUG;
+
+ my $cdr_upstream_rate = new FS::cdr_upstream_rate {
+ 'upstream_rateid' => $hash{'Class Code'},
+ 'ratedetailnum' => $rate_detail->ratedetailnum,
+ };
+ $error = $cdr_upstream_rate->insert;
+ if ( $error ) {
+ dbh->rollback;
+ die "error inserting into cdr_upstream_rate: $error\n";
+ }
+ warn "cdr_upstream_rate ". $cdr_upstream_rate->upstreamratenum. " inserted\n"
+ if $DEBUG;
+
+ dbh->commit or die "can't commit: ". dbh->errstr;
+
+ warn "\n" if $DEBUG;
+
+}
+
+dbh->commit or die "can't commit: ". dbh->errstr;
+
+sub usage {
+ "Usage:\n\ncdr_upstream_rate.import username ratenum filename\n";
+}
+
diff --git a/bin/create-fetchmailrc b/bin/create-fetchmailrc
new file mode 100644
index 0000000..11bde0c
--- /dev/null
+++ b/bin/create-fetchmailrc
@@ -0,0 +1,47 @@
+#!/usr/bin/perl -w
+# this quick hack helps you generate/maintain .fetchmailrc files from
+# FS::acct_snarf data. it is run from a shellcommands export as:
+# create-fetchmailrc $username $dir $snarf_machine1 $snarf_username1 $snarf__password1 $snarf_machine2 $snarf_username2 $snarf__password2 ...
+
+use strict;
+use POSIX qw( setuid setgid );
+
+my $header = <<END;
+# Configuration created by create-fetchmailrc
+set postmaster "postmaster"
+set bouncemail
+set no spambounce
+set properties ""
+set daemon 240
+END
+
+my $username = shift @ARGV or die "no username specified\n";
+my $homedir = shift @ARGV or die "no homedir specified\n";
+my $filename = "$homedir/.fetchmailrc";
+
+my $gid = scalar(getgrnam($username)) or die "can't find $username's gid\n";
+my $uid = scalar(getpwnam($username)) or die "can't find $username's uid\n";
+
+exit unless $ARGV[0];
+
+open(FETCHMAILRC, ">$filename") or die "can't open $filename: $!\n";
+chown $uid, $gid, $filename or die "can't chown $uid.$gid $filename: $!\n";
+chmod 0600, $filename or die "can't chmod 600 $filename: $!\n";
+print FETCHMAILRC $header;
+
+while ($ARGV[0]) {
+ my( $s_machine, $s_username, $s_password ) = splice( @ARGV, 0, 3 );
+ print FETCHMAILRC <<END;
+poll $s_machine
+ user '$s_username' there with password '$s_password' is '$username' here
+END
+}
+
+close FETCHMAILRC;
+
+setgid($gid) or die "can't setgid $gid\n";
+setuid($uid) or die "can't setuid $uid\n";
+$ENV{HOME} = $homedir;
+
+system(qq(fetchmail -a -K --antispam "550,451" -d 180 -f $filename));
+
diff --git a/bin/customer-faker b/bin/customer-faker
new file mode 100755
index 0000000..236a412
--- /dev/null
+++ b/bin/customer-faker
@@ -0,0 +1,124 @@
+#!/usr/bin/perl
+
+use strict;
+use Getopt::Std;
+use Data::Faker;
+use Business::CreditCard;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::svc_acct;
+
+my $refnum = 1;
+
+#my @pkgs = ( 4, 5, 6 );
+my $svcpart = 2;
+
+use vars qw( $opt_p $opt_a $opt_k );
+getopts('p:a:k:');
+
+my $agentnum = $opt_a || 1;
+
+my @pkgs = $opt_k ? split(/,\s*/, $opt_k) : ( 2, 3, 4 );
+
+my $user = shift or die &usage;
+my $num = shift or die &usage;
+adminsuidsetup($user);
+
+my $onum = $num;
+my $start = time;
+
+my @states = qw( AL AK AS AZ AR CA CO CT DE DC FL GA GU HI ID IL IN IA KS KY LA ME MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND MP OH OK OR PA PR RI SC SD TN TX UT VT VI VA WA WV WI WY );
+#FM MH
+
+until ( $num-- <= 0 ) {
+
+ my $faker = new Data::Faker;
+
+ my $cust_main = new FS::cust_main {
+ 'agentnum' => $agentnum,
+ 'refnum' => $refnum,
+ 'first' => $faker->first_name,
+ 'last' => $faker->last_name,
+ 'company' => ( $num % 2 ? $faker->company. ', '. $faker->company_suffix : '' ), #half with companies..
+ 'address1' => $faker->street_address,
+ 'city' => 'Tofutown', #missing, so everyone is from tofutown# $faker->city,
+ #'state' => $faker->us_state_abbr,
+ 'state' => $states[ int(rand($#states)) ],
+ 'zip' => $faker->us_zip_code,
+ 'country' => 'US',
+ 'daytime' => $faker->phone_number,
+ 'night' => $faker->phone_number,
+ #forget it, these can have extensions# 'fax' => ( $num % 2 ? $faker->phone_number : '' ), #ditto
+ #bah, forget shipping addresses
+ 'payby' => 'BILL',
+ 'payip' => $faker->ip_address,
+ };
+
+ if ( $opt_p eq 'CARD' || ( !$opt_p && rand() > .33 ) ) {
+ $cust_main->payby('CARD');
+ my $cardnum = '4123'. sprintf('%011u', int(rand(100000000000)) );
+ $cust_main->payinfo( $cardnum. generate_last_digit($cardnum) );
+ $cust_main->paydate( '2009-05-01' );
+ } elsif ( $opt_p eq 'CHEK' || ( !$opt_p && rand() > .66 ) ) {
+ $cust_main->payby('CHEK');
+ my $payinfo = sprintf('%7u@%09u', int(rand(10000000)), int(rand(1000000000)) );
+ $cust_main->payinfo($payinfo);
+ $cust_main->payname( 'First International Bank of Testing' );
+ }
+
+ # could insert invoicing_list and other stuff too.. hell, could insert
+ # packages, services, more
+ # but i just wanted 10k customers to test the pager and this was good enough
+ # not anymore, here's some services and packages
+
+ my $now = time;
+ my $year = 31556736; #60*60*24*365.24
+ my $setup = $now - int(rand($year));
+
+ my $cust_pkg = new FS::cust_pkg {
+ 'pkgpart' => $pkgs[ int(rand(scalar(@pkgs))) ],
+
+ #some dates in here would be nice
+ 'setup' => $setup,
+ #'last_bill'
+ #'bill'
+ #'susp'
+ #'expire'
+ #'cancel'
+ };
+
+ my $svc_acct = new FS::svc_acct {
+ 'svcpart' => $svcpart,
+ 'username' => $faker->username,
+ };
+
+ while ( qsearch( 'svc_acct', { 'username' => $svc_acct->username } ) ) {
+ my $username = $svc_acct->username;
+ $username++;
+ $svc_acct->username($username);
+ }
+
+ use Tie::RefHash;
+ tie my %hash, 'Tie::RefHash',
+ $cust_pkg => [ $svc_acct ],
+ ;
+
+ my $error = $cust_main->insert( \%hash );
+ die $error if $error;
+
+}
+
+my $end = time;
+
+my $sec = $end-$start;
+$sec=1 if $sec==0;
+my $persec = $onum / $sec;
+print "$onum customers inserted in $sec seconds ($persec customers/sec)\n";
+
+#---
+
+sub usage {
+ die "Usage:\n\n customer-faker [ -p payby ] [ -a agentnum ] [ -k pkgpart,pkgpart,pkgpart... ] user num_fakes\n";
+}
diff --git a/bin/dbdef-create b/bin/dbdef-create
deleted file mode 100755
index eb62c77..0000000
--- a/bin/dbdef-create
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# create dbdef file for existing mySQL database (needs SHOW|DESCRIBE command
-# not in Pg) based on fs-setup
-#
-# ivan@sisd.com 98-jun-2
-
-use strict;
-use DBI;
-use FS::dbdef;
-use FS::UID qw(adminsuidsetup datasrc);
-
-#needs to match FS::Record
-my($dbdef_file) = "/var/spool/freeside/dbdef.". datasrc;
-
-my($dbh)=adminsuidsetup;
-
-my($tables_sth)=$dbh->prepare("SHOW TABLES");
-my($tables_rv)=$tables_sth->execute;
-
-my(@tables);
-foreach ( @{$tables_sth->fetchall_arrayref} ) {
- my($table)=${$_}[0];
- #print "TABLE\t$table\n";
-
- my($index_sth)=$dbh->prepare("SHOW INDEX FROM $table");
- my($primary_key)='';
- my(%index,%unique);
- for ( 1 .. $index_sth->execute ) {
- my($row)=$index_sth->fetchrow_hashref;
- if ( ${$row}{'Key_name'} eq "PRIMARY" ) {
- $primary_key=${$row}{'Column_name'};
- next;
- }
- if ( ${$row}{'Non_unique'} ) { #index
- push @{$index{${$row}{'Key_name'}}}, ${$row}{'Column_name'};
- } else { #unique
- push @{$unique{${$row}{'Key_name'}}}, ${$row}{'Column_name'};
- }
- }
-
- my(@index)=values %index;
- my(@unique)=values %unique;
- #print "\tPRIMARY KEY $primary_key\n";
- foreach (@index) {
- #print "\tINDEX\t", join(', ', @{$_}), "\n";
- }
- foreach (@unique) {
- #print "\tUNIQUE\t", join(', ', @{$_}), "\n";
- }
-
- my($columns_sth)=$dbh->prepare("SHOW COLUMNS FROM $table");
- my(@columns);
- for ( 1 .. $columns_sth->execute ) {
- my($row)=$columns_sth->fetchrow_hashref;
- #print "\t", ${$row}{'Field'}, "\n";
- ${$row}{'Type'} =~ /^(\w+)\(?([\d\,]+)?\)?( unsigned)?$/
- or die "Illegal type ${$row}{'Type'}\n";
- my($type,$length)=($1,$2);
- my($null)=${$row}{'Null'};
- $null =~ s/YES/NULL/;
- push @columns, new FS::dbdef_column (
- ${$row}{'Field'},
- $type,
- $null,
- $length,
- );
- }
-
- #print "\n";
- push @tables, new FS::dbdef_table (
- $table,
- $primary_key,
- new FS::dbdef_unique (\@unique),
- new FS::dbdef_index (\@index),
- @columns,
- );
-
-}
-
-my($dbdef) = new FS::dbdef ( @tables );
-
-#important
-$dbdef->save($dbdef_file);
-
diff --git a/bin/expand-country b/bin/expand-country
new file mode 100755
index 0000000..c6f2a1f
--- /dev/null
+++ b/bin/expand-country
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Locale::SubCountry;
+use FS::UID qw(adminsuidsetup);
+use FS::Setup;
+use FS::Record qw(qsearch);
+use FS::cust_main_county;
+
+my $user = shift or die &usage;
+my $country = shift or die &usage;
+
+adminsuidsetup($user);
+
+my @country = qsearch('cust_main_county', { 'country' => $country } );
+die "unknown country $country" unless (@country);
+#die "$country already expanded" if scalar(@country) > 1;
+
+foreach my $cust_main_county ( @country ) {
+ my $error = $cust_main_county->delete;
+ die $error if $error;
+}
+
+FS::Setup::_add_country($country);
+
+sub usage {
+ die "Usage:\n\n expand-country user countrycode\n";
+}
+
diff --git a/bin/explain-ar-total.sql b/bin/explain-ar-total.sql
new file mode 100644
index 0000000..f154430
--- /dev/null
+++ b/bin/explain-ar-total.sql
@@ -0,0 +1,976 @@
+EXPLAIN SELECT ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill LEFT JOIN cust_main USING ( custnum ) WHERE cust_bill._date > ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund LEFT JOIN cust_main USING ( custnum ) WHERE cust_refund._date > ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit LEFT JOIN cust_main USING ( custnum ) WHERE cust_credit._date > ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay LEFT JOIN cust_main USING ( custnum ) WHERE cust_pay._date > ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ AS balance_0_30, ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill LEFT JOIN cust_main USING ( custnum ) WHERE cust_bill._date <= ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND cust_bill._date > ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund LEFT JOIN cust_main USING ( custnum ) WHERE cust_refund._date <= ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND cust_refund._date > ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit LEFT JOIN cust_main USING ( custnum ) WHERE cust_credit._date <= ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND cust_credit._date > ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay LEFT JOIN cust_main USING ( custnum ) WHERE cust_pay._date <= ( EXTRACT( EPOCH FROM now() ) - 2592000 ) AND cust_pay._date > ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ AS balance_30_60, ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill LEFT JOIN cust_main USING ( custnum ) WHERE cust_bill._date <= ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND cust_bill._date > ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund LEFT JOIN cust_main USING ( custnum ) WHERE cust_refund._date <= ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND cust_refund._date > ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit LEFT JOIN cust_main USING ( custnum ) WHERE cust_credit._date <= ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND cust_credit._date > ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay LEFT JOIN cust_main USING ( custnum ) WHERE cust_pay._date <= ( EXTRACT( EPOCH FROM now() ) - 5184000 ) AND cust_pay._date > ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ AS balance_60_90, ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill LEFT JOIN cust_main USING ( custnum ) WHERE cust_bill._date <= ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund LEFT JOIN cust_main USING ( custnum ) WHERE cust_refund._date <= ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit LEFT JOIN cust_main USING ( custnum ) WHERE cust_credit._date <= ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay LEFT JOIN cust_main USING ( custnum ) WHERE cust_pay._date <= ( EXTRACT( EPOCH FROM now() ) - 7776000 ) AND ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ AS balance_90_0, ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill LEFT JOIN cust_main USING ( custnum ) WHERE ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund LEFT JOIN cust_main USING ( custnum ) WHERE ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit LEFT JOIN cust_main USING ( custnum ) WHERE ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay LEFT JOIN cust_main USING ( custnum ) WHERE ( SELECT COALESCE(SUM(charged - ( SELECT COALESCE(SUM(amount),0) FROM cust_bill_pay
+ WHERE cust_bill.invnum = cust_bill_pay.invnum ) - ( SELECT COALESCE(SUM(amount),0) FROM cust_credit_bill
+ WHERE cust_bill.invnum = cust_credit_bill.invnum )), 0) FROM cust_bill WHERE cust_main.custnum = cust_bill.custnum )
+ + ( SELECT COALESCE(SUM(refund
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_refund.refundnum = cust_credit_refund.refundnum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_refund.refundnum = cust_pay_refund.refundnum )
+ ,0
+ )
+ ), 0) FROM cust_refund WHERE cust_main.custnum = cust_refund.custnum )
+ - ( SELECT COALESCE(SUM(amount
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_refund
+ WHERE cust_credit.crednum = cust_credit_refund.crednum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_credit_bill
+ WHERE cust_credit.crednum = cust_credit_bill.crednum )
+ ,0
+ )
+ ), 0) FROM cust_credit WHERE cust_main.custnum = cust_credit.custnum )
+ - ( SELECT COALESCE(SUM(paid
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_bill_pay
+ WHERE cust_pay.paynum = cust_bill_pay.paynum )
+ ,0
+ )
+ - COALESCE(
+ ( SELECT SUM(amount) FROM cust_pay_refund
+ WHERE cust_pay.paynum = cust_pay_refund.paynum )
+ ,0
+ )
+ ), 0) FROM cust_pay WHERE cust_main.custnum = cust_pay.custnum )
+ > 0 AND ( agentnum = 1 OR agentnum = 2 OR agentnum = 3 OR agentnum = 4 OR agentnum IS NULL ) )
+ AS balance_0_0
diff --git a/bin/find-overapplied b/bin/find-overapplied
new file mode 100644
index 0000000..7973cef
--- /dev/null
+++ b/bin/find-overapplied
@@ -0,0 +1,27 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Data::Dumper;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_credit;
+use FS::cust_pay;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my @credits = grep { $_->credited < 0 } qsearch('cust_credit', {});
+my @payments = grep { $_->unapplied < 0 } qsearch('cust_pay', {});
+
+if ( @credits ) {
+ print scalar(@credits). " overapplied credits:\n". Dumper(@credits). "\n";
+}
+
+if ( @payments ) {
+ print scalar(@payments). " overapplied payments:\n". Dumper(@payments). "\n";
+}
+
+sub usage {
+ die "Usage:\n\n find-overapplied user\n";
+}
+
diff --git a/bin/fix-sequences b/bin/fix-sequences
new file mode 100755
index 0000000..dc4abd7
--- /dev/null
+++ b/bin/fix-sequences
@@ -0,0 +1,69 @@
+#!/usr/bin/perl -Tw
+
+# run dbdef-create first!
+
+use strict;
+use DBI;
+use DBIx::DBSchema 0.26;
+use DBIx::DBSchema::Table;
+use DBIx::DBSchema::Column;
+use DBIx::DBSchema::ColGroup::Unique;
+use DBIx::DBSchema::ColGroup::Index;
+use FS::UID qw(adminsuidsetup driver_name);
+use FS::Record qw(dbdef);
+
+my $user = shift or die &usage;
+my $dbh = adminsuidsetup $user;
+
+my $schema = dbdef();
+
+#false laziness w/fs-setup
+my @tables = scalar(@ARGV)
+ ? @ARGV
+ : grep { ! /^h_/ } $schema->tables;
+foreach my $table ( @tables ) {
+ my $tableobj = $schema->table($table)
+ or die "unknown table $table (did you run dbdef-create?)\n";
+
+ my $primary_key = $tableobj->primary_key;
+ next unless $primary_key;
+
+ my $col = $tableobj->column($primary_key);
+
+
+ next unless uc($col->type) eq 'SERIAL'
+ || ( driver_name eq 'Pg'
+ && defined($col->default)
+ && $col->default =~ /^nextval\(/i
+ )
+ || ( driver_name eq 'mysql'
+ && defined($col->local)
+ && $col->local =~ /AUTO_INCREMENT/i
+ );
+
+ my $seq = "${table}_${primary_key}_seq";
+ if ( driver_name eq 'Pg'
+ && defined($col->default)
+ && $col->default =~ /^nextval\('"(public\.)?(\w+_seq)"'::text\)$/
+ ) {
+ $seq = $2;
+ }
+
+ warn "fixing sequence for $table\n";
+
+
+ my $sql = "SELECT setval( '$seq',
+ ( SELECT max($primary_key) FROM $table ) );";
+
+ #warn $col->default. " $seq\n$sql\n";
+ $dbh->do( $sql ) or die $dbh->errstr;
+
+}
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+sub usage {
+ die "Usage:\n fix-sequences user [ table table ... ] \n";
+}
+
diff --git a/bin/follow-tax-rename b/bin/follow-tax-rename
new file mode 100644
index 0000000..b7536e8
--- /dev/null
+++ b/bin/follow-tax-rename
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+
+use FS::UID qw( adminsuidsetup );
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_bill_pkg;
+
+$FS::Record::nowarn_classload = 1;
+$FS::Record::nowarn_classload = 1;
+
+adminsuidsetup shift;
+
+my $begin = 1231876106;
+
+my @old = qsearch('h_cust_main_county', {
+ 'history_action' => 'replace_old',
+ 'history_date' => { op=>'>=', value=>$begin, },
+} );
+
+foreach my $old (@old) {
+
+ my $new = qsearchs('h_cust_main_county', {
+ 'history_action' => 'replace_new',
+ 'history_date' => $old->history_date,
+ });
+
+ unless ( $new ) {
+ warn "huh? no corresponding new record found?";
+ next;
+ }
+
+ my $old_taxname = $old->taxname;
+ my $new_taxname = $new->taxname;
+
+ my @cust_bill_pkg = qsearch('cust_bill_pkg', {
+ 'pkgnum' => 0,
+ 'itemdesc' => $old->taxname,
+ });
+
+ next unless @cust_bill_pkg;
+
+ warn 'fixing '. scalar(@cust_bill_pkg).
+ " dangling line items for rename $old_taxname -> $new_taxname\n";
+
+ foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
+
+ $cust_bill_pkg->itemdesc( $new->taxname );
+ my $error = $cust_bill_pkg->replace;
+ die $error if $error;
+
+ }
+
+}
diff --git a/bin/freeside-create-initial-data b/bin/freeside-create-initial-data
new file mode 100755
index 0000000..4102089
--- /dev/null
+++ b/bin/freeside-create-initial-data
@@ -0,0 +1,31 @@
+#!/usr/bin/perl -Tw
+
+#to allow initial insert
+use FS::part_pkg;
+$FS::part_pkg::setup_hack = 1;
+$FS::part_pkg::setup_hack = 1;
+
+use strict;
+use vars qw($opt_d $opt_v);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Setup qw(create_initial_data);
+
+getopts("d:");
+
+my $dbh = adminsuidsetup shift;
+create_initial_data('domain' => $opt_d);
+
+warn "Freeside initial data inserted - commiting transaction\n" if $opt_v;
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+warn "Database initialization committed successfully\n" if $opt_v;
+
+sub usage {
+ die "Usage:\n freeside-create-initial-data -d domain.name [ -v ] user\n"
+}
+
+1;
+
diff --git a/bin/freeside-init b/bin/freeside-init
new file mode 100755
index 0000000..fe12931
--- /dev/null
+++ b/bin/freeside-init
@@ -0,0 +1,60 @@
+#! /bin/sh
+#
+# start the freeside job queue daemon
+
+#PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/local/bin/freeside-queued
+NAME=freeside-queued
+DESC="freeside job queue daemon"
+USER="ivan"
+
+test -f $DAEMON || exit 0
+
+set -e
+
+case "$1" in
+ start)
+ echo -n "Starting $DESC: "
+# start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid -b -m\
+# --exec $DAEMON
+ $DAEMON $USER &
+ echo "$NAME."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
+ --exec $DAEMON
+ echo "$NAME."
+ rm /var/run/$NAME.pid
+ ;;
+ #reload)
+ #
+ # If the daemon can reload its config files on the fly
+ # for example by sending it SIGHUP, do it here.
+ #
+ # If the daemon responds to changes in its config file
+ # directly anyway, make this a do-nothing entry.
+ #
+ # echo "Reloading $DESC configuration files."
+ # start-stop-daemon --stop --signal 1 --quiet --pidfile \
+ # /var/run/$NAME.pid --exec $DAEMON
+ #;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented, move the "force-reload"
+ # option to the "reload" entry above. If not, "force-reload" is
+ # just the same as "restart".
+ #
+ $0 stop
+ sleep 1
+ $0 start
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/bin/freeside-migrate-events b/bin/freeside-migrate-events
new file mode 100644
index 0000000..76643b8
--- /dev/null
+++ b/bin/freeside-migrate-events
@@ -0,0 +1,229 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw( qsearch );
+use FS::part_bill_event;
+use FS::part_event;
+use FS::cust_bill_event;
+use FS::cust_event;
+
+my $user = shift or die &usage;
+adminsuidsetup($user);
+
+my %plan2action = (
+ 'fee' => 'fee',
+ 'fee_percent' => 'NOTYET', #XXX need fee_percent action
+ 'suspend' => 'suspend',
+ 'suspend-if-balance' => 'NOTYET', #XXX "if balance" becomes a balance condition
+ 'suspend-if-pkgpart' => 'suspend_if_pkgpart',
+ 'suspend-unless-pkgpart' => 'suspend_unless_pkgpart',
+ 'cancel' => 'cancel',
+ 'addpost' => 'addpost',
+ 'comp' => 'NOTYET', #XXX or N/A or something
+ 'credit' => 'NOTYET',
+ 'realtime-card' => 'cust_bill_realtime_card',
+ 'realtime-check' => 'cust_bill_realtime_check',
+ 'realtime-lec' => 'cust_bill_realtime_lec',
+ 'batch-card' => 'cust_bill_batch',
+ #?'retriable' =>
+ 'send' => 'cust_bill_send',
+ 'send_email' => 'NOTYET',
+ 'send_alternate' => 'cust_bill_send_alternate',
+ 'send_if_newest' => 'cust_bill_send_if_newest',
+ 'send_agent' => 'cust_bill_send_agent',
+ 'send_csv_ftp' => 'cust_bill_send_csv_ftp',
+ 'spool_csv', => 'cust_bill_spool_csv',
+ 'bill' => 'bill',
+ 'apply' => 'apply',
+ 'collect' => 'collect',
+);
+
+
+foreach my $part_bill_event (
+ qsearch({
+ 'table' => 'part_bill_event',
+ })
+) {
+
+ print $part_bill_event->event;
+
+ my $action = $plan2action{ $part_bill_event->plan };
+
+ if ( $action eq 'NOTYET' ) {
+ warn "not migrating part_bill_event.eventpart ".$part_bill_event->eventpart.
+ "; ". $part_bill_event->plan. " plan not (yet) handled";
+ next;
+ } elsif ( ! $action ) {
+ warn "not migrating part_bill_event.eventpart ".$part_bill_event->eventpart.
+ "; unknown plan ". $part_bill_event->plan;
+ next;
+ }
+
+ my %plandata = map { /^(\w+) (.*)$/; ($1, $2); }
+ split(/\n/, $part_bill_event->plandata);
+
+ #XXX may need to fudge some plandata2option names!!!
+
+ my $part_event = new FS::part_event {
+ 'event' => $part_bill_event->event,
+ 'eventtable' => 'cust_bill',
+ 'check_freq' => $part_bill_event->freq || '1d',
+ 'weight' => $part_bill_event->weight,
+ 'action' => $action,
+ 'disabled' => $part_bill_event->disabled,
+ };
+
+ my $error = $part_event->insert(\%plandata);
+ die "error inserting part_event: $error\n" if $error;
+
+ print ' '. $part_event->eventpart;
+
+ my $once = new FS::part_event_condition {
+ 'eventpart' => $part_event->eventpart,
+ 'conditionname' => 'once'
+ };
+ $error = $once->insert;
+ die $error if $error;
+
+ my $balance = new FS::part_event_condition {
+ 'eventpart' => $part_event->eventpart,
+ 'conditionname' => 'balance'
+ };
+ $error = $balance->insert( 'balance' => 0 );
+ die $error if $error;
+
+ my $cust_bill_owed = new FS::part_event_condition {
+ 'eventpart' => $part_event->eventpart,
+ 'conditionname' => 'cust_bill_owed'
+ };
+ $error = $cust_bill_owed->insert( 'owed' => 0 );
+ die $error if $error;
+
+ my $payby = new FS::part_event_condition {
+ 'eventpart' => $part_event->eventpart,
+ 'conditionname' => 'payby'
+ };
+ $error = $payby->insert( 'payby' => { $part_bill_event->payby => 1 } );
+ die $error if $error;
+
+ if ( $part_bill_event->seconds ) {
+
+ my $age = new FS::part_event_condition {
+ 'eventpart' => $part_event->eventpart,
+ 'conditionname' => 'cust_bill_age'
+ };
+ $error = $age->insert( 'age' => ($part_bill_event->seconds/86400 ).'d' );
+ die $error if $error;
+
+ }
+
+ #my $derror = $part_bill_event->delete;
+ #die "error removing part_bill_event: $derror\n" if $derror;
+
+ foreach my $cust_bill_event (
+ qsearch({
+ 'table' => 'cust_bill_event',
+ 'hashref' => { 'eventpart' => $part_bill_event->eventpart, },
+ })
+ ) {
+
+ my $cust_event = new FS::cust_event {
+ 'eventpart' => $part_event->eventpart,
+ 'tablenum' => $cust_bill_event->invnum,
+ '_date' => $cust_bill_event->_date,
+ 'status' => $cust_bill_event->status,
+ 'statustext' => $cust_bill_event->statustext,
+ };
+
+ my $cerror = $cust_event->insert;
+ #die "error inserting cust_event: $cerror\n" if $cerror;
+ warn "error inserting cust_event: $cerror\n" if $cerror;
+
+ #my $dcerror = $cust_bill_event->delete;
+ #die "error removing cust_bill_event: $dcerror\n" if $dcerror;
+
+ print ".";
+
+ }
+
+ print "\n";
+
+}
+
+sub usage {
+ die "Usage:\n freeside-migrate-events user\n";
+}
+
+=head1 NAME
+
+freeside-migrate-events - Migrates 1.7/1.8-style invoice events to
+ 1.9/2.0-style billing events
+
+=head1 SYNOPSIS
+
+ freeside-migrate-events
+
+=head1 DESCRIPTION
+
+Migrates events from L<FS::part_bill_event> to L<FS::part_event> and friends,
+and from L<FS::cust_bill_event> records to L<FS::cust_event>
+
+=head1 BUGS
+
+Doesn't migrate any action options yet.
+
+Doesn't translate option names that changed.
+
+Doesn't migrate reasons.
+
+Doesn't delete the old events (which is not a big deal, since the new code
+won't run them...)
+
+=head1 SEE ALSO
+
+=cut
+
+1;
+
+__END__
+
+#part_bill_event part_event
+#
+#eventpart n/a
+#event event
+#freq check_freq
+#payby part_event_condition.conditionname = payby
+#eventcode PARSE_WITH_REGEX (probably can just get from plandata)
+#seconds part_event_condition.conditionname = cust_bill_age
+#plandata PARSE_WITH_REGEX (along with eventcode, yuck)
+#reason part_event_option.optionname = reason
+#disabled disabled
+#
+
+ #these might help parse existing eventcode
+
+ $c =~ /^\s*\$cust_main\->(suspend|cancel|invoicing_list_addpost|bill|collect)\(\);\s*("";)?\s*$/
+
+ or $c =~ /^\s*\$cust_bill\->(comp|realtime_(card|ach|lec)|batch_card|send)\((%options)*\);\s*$/
+
+ or $c =~ /^\s*\$cust_bill\->send(_if_newest)?\(\'[\w\-\s]+\'\s*(,\s*(\d+|\[\s*\d+(,\s*\d+)*\s*\])\s*,\s*'[\w\@\.\-\+]*'\s*)?\);\s*$/
+
+# or $c =~ /^\s*\$cust_main\->apply_payments; \$cust_main->apply_credits; "";\s*$/
+ or $c =~ /^\s*\$cust_main\->apply_payments_and_credits; "";\s*$/
+
+ or $c =~ /^\s*\$cust_main\->charge\( \s*\d*\.?\d*\s*,\s*\'[\w \!\@\#\$\%\&\(\)\-\+\;\:\"\,\.\?\/]*\'\s*\);\s*$/
+
+ or $c =~ /^\s*\$cust_main\->suspend_(if|unless)_pkgpart\([\d\,\s]*\);\s*$/
+
+ or $c =~ /^\s*\$cust_bill\->cust_suspend_if_balance_over\([\d\.\s]*\);\s*$/
+
+ or do {
+ #log
+ return "illegal eventcode: $c";
+ };
+
+ }
+
+
diff --git a/bin/freeside-session-kill b/bin/freeside-session-kill
new file mode 100755
index 0000000..d5fd703
--- /dev/null
+++ b/bin/freeside-session-kill
@@ -0,0 +1,103 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($conf);
+use Fcntl qw(:flock);
+use FS::UID qw(adminsuidsetup datasrc dbh);
+use FS::Record qw(dbdef qsearch fields);
+use FS::session;
+use FS::svc_acct;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $sessionlock = "/usr/local/etc/freeside/session-kill.lock.". datasrc;
+
+open(LOCK,"+>>$sessionlock") or die "Can't open $sessionlock: $!";
+select(LOCK); $|=1; select(STDOUT);
+unless ( flock(LOCK,LOCK_EX|LOCK_NB) ) {
+ seek(LOCK,0,0);
+ my($pid)=<LOCK>;
+ chop($pid);
+ #no reason to start loct of blocking processes
+ die "Is another session kill process running under pid $pid?\n";
+}
+seek(LOCK,0,0);
+print LOCK $$,"\n";
+
+$FS::UID::AutoCommit = 0;
+
+my $now = time;
+
+#uhhhhh
+
+use DBIx::DBSchema;
+use DBIx::DBSchema::Table; #down this path lies madness
+use DBIx::DBSchema::Column;
+
+my $dbdef = dbdef or die;
+#warn $dbdef;
+#warn $dbdef->{'tables'};
+#warn keys %{$dbdef->{'tables'}};
+my $session_table = $dbdef->table('session') or die;
+my $svc_acct_table = $dbdef->table('svc_acct') or die;
+
+my $session_svc_acct = new DBIx::DBSchema::Table ( 'session,svc_acct', '', '', '',
+ map( DBIx::DBSchema::Column->new( "session.$_",
+ $session_table->column($_)->type,
+ $session_table->column($_)->null,
+ $session_table->column($_)->length,
+ ), $session_table->columns() ),
+ map( DBIx::DBSchema::Column->new( "svc_acct.$_",
+ $svc_acct_table->column($_)->type,
+ $svc_acct_table->column($_)->null,
+ $svc_acct_table->column($_)->length,
+ ), $svc_acct_table->columns ),
+# map("svc_acct.$_", $svc_acct_table->columns),
+);
+
+$dbdef->addtable($session_svc_acct); #madness, i tell you
+
+$FS::Record::DEBUG = 1;
+my @session = qsearch('session,svc_acct', {}, '', ' WHERE '. join(' AND ',
+ 'svc_acct.svcnum = session.svcnum',
+ '( session.logout IS NULL OR session.logout = 0 )',
+ "( $now - session.login ) >= svc_acct.seconds"
+). " FOR UPDATE" );
+
+my $dbh = dbh;
+
+foreach my $join ( @session ) {
+
+ my $session = new FS::session ( {
+ map { $_ => $join->{'Hash'}{"session.$_"} } fields('session')
+ } ); #see no evil
+
+ my $svc_acct = new FS::svc_acct ( {
+ map { $_ => $join->{'Hash'}{"svc_acct.$_"} } fields('svc_acct')
+ } );
+
+ #false laziness w/ fs_session_server
+ my $nsession = new FS::session ( { $session->hash } );
+ my $error = $nsession->replace($session);
+ if ( $error ) {
+ $dbh->rollback;
+ die $error;
+ }
+ my $time = $nsession->logout - $nsession->login;
+ my $new_svc_acct = new FS::svc_acct ( { $svc_acct->hash } );
+ my $seconds = $new_svc_acct->seconds;
+ $seconds -= $time;
+ $seconds = 0 if $seconds < 0;
+ $new_svc_acct->seconds( $seconds );
+ $error = $new_svc_acct->replace( $svc_acct );
+ warn "can't debit time from ". $svc_acct->username. ": $error\n"; #don't want to rollback, though
+ #ssenizal eslaf
+
+}
+
+$dbh->commit or die $dbh->errstr;
+
+sub usage {
+ die "Usage:\n\n freeside-session-kill user\n";
+}
diff --git a/bin/freeside-upgrade-unicode b/bin/freeside-upgrade-unicode
new file mode 100755
index 0000000..c603365
--- /dev/null
+++ b/bin/freeside-upgrade-unicode
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+# based on example code from
+# http://blog.larik.nl:80/articles/2006/03/13/upgrade-your-postgresql-databases-to-unicode
+# by frodo larik / blog.larik.nl
+
+db=freeside
+
+# This script updates all dbs to use unicode
+
+dbhost='localhost'
+username='freeside'
+#odir=${HOME}/freeside_unicode_upgrade
+odir=/home/ivan/FREESIDE_unicode_upgrade
+
+if [ "${db}X" == "X" ]
+then
+ echo "I need a db for host ${dbhost} and username ${username} $db"
+ exit
+fi
+
+if [ ! -d $odir ]
+then
+ mkdir $odir || exit "Exit at mkdir"
+fi
+
+#echo -n "Enter a comma-separated list of country codes to keep [US,CA]:"
+#countries=`line`
+#if [ "${countries}X" == "X" ]
+#then
+# countries='US,CA'
+#fi
+
+echo "delete from cust_main_county where 0 = ( select count(*) from cust_main where cust_main_county.country = cust_main.country );" | su freeside -c 'psql freeside'
+
+dump_sql=${odir}/${db}_out.sql
+conv_sql=${odir}/${db}_conv.sql
+result_sql=${odir}/${db}_result.txt
+sql_diff=${odir}/${db}.diff
+
+# 0. stop
+
+/etc/init.d/freeside stop || die "can't stop freeside"
+/etc/init.d/apache stop || die "can't stop apache"
+/etc/init.d/apache2 stop || die "can't stop apache"
+
+echo "Dumping $db database to $dump_sql"
+
+su $username -c "pg_dump --host=$dbhost --username=$username -D --format=p $db" >$dump_sql || exit "exit at pg_dump"
+
+echo "Removing invalid characters from the dump"
+
+iconv -c -f UTF-8 -t UTF-8 ${dump_sql} > ${conv_sql} || exit "exit at iconv"
+
+echo "*** Making a diff from the dump: check $sql_diff ***"
+
+diff $dump_sql $conv_sql > $sql_diff
+
+echo "Removing current database"
+
+su $freeside -c "dropdb --host=$dbhost --username=$username $db" || exit "exit at dropdb"
+
+echo "Creating a new databse"
+
+su freeside -c "createdb --encoding='unicode' --host=$dbhost --username=$username $db" || exit "exit at createdb"
+
+echo "Loading data into new database"
+su freeside -c "psql -f $conv_sql -o $result_sql -h $dbhost -U $username $db" || exit "exit at psql ${extra_string}"
+
+# 99.
+/etc/init.d/freeside start || die "oh no, can't start freeside"
+/etc/init.d/apache start || die "oh no, can't start apache"
diff --git a/bin/freeside.import b/bin/freeside.import
new file mode 100644
index 0000000..fdfcc08
--- /dev/null
+++ b/bin/freeside.import
@@ -0,0 +1,146 @@
+#!/usr/bin/perl -w
+
+use strict;
+use DBI;
+
+my $s_datasrc = 'DBI:mysql:host=ns1.enetonline.net;port=3307;user=ivan;dbname=freeside';
+my $s_dbuser = 'ivan';
+my $s_dbpass = '';
+
+my $d_datasrc = 'DBI:Pg:dbname=freeside';
+my $d_dbuser = 'freeside';
+my $d_dbpass = '';
+
+#my @tables = qw(
+#addr_block
+#agent
+#agent_type
+#cust_bill
+#cust_bill_event
+#cust_bill_pay
+#cust_bill_pkg
+#cust_bill_pkg_detail
+#cust_credit
+#cust_credit_bill
+#cust_credit_refund
+#cust_main
+#cust_main_county
+#cust_main_invoice
+#cust_pay
+#cust_pay_batch
+#cust_pkg
+#cust_refund
+#cust_svc
+#cust_tax_exempt
+#domain_record
+#export_svc
+#h_addr_block
+#h_agent
+#h_agent_type
+#h_cust_bill
+#h_cust_bill_event
+#h_cust_bill_pay
+#h_cust_bill_pkg
+#h_cust_bill_pkg_detail
+#h_cust_credit
+#h_cust_credit_bill
+#h_cust_credit_refund
+#h_cust_main
+#h_cust_main_county
+#h_cust_main_invoice
+#h_cust_pay
+#h_cust_pay_batch
+#h_cust_pkg
+#h_cust_refund
+#h_cust_svc
+#h_cust_tax_exempt
+#h_domain_record
+#h_export_svc
+#h_msgcat
+#h_nas
+#h_part_bill_event
+#h_part_export
+#h_part_export_option
+#h_part_pkg
+#h_part_pop_local
+#h_part_referral
+#h_part_svc
+#h_part_svc_column
+#h_part_svc_router
+#h_pkg_svc
+#h_port
+#h_prepay_credit
+#h_queue
+#h_queue_arg
+#h_queue_depend
+#h_radius_usergroup
+#h_router
+#h_router_field
+#h_sb_field
+#h_session
+#h_svc_acct
+#h_svc_acct_pop
+#h_svc_broadband
+#h_svc_domain
+#h_svc_forward
+#h_svc_www
+#h_type_pkgs
+#msgcat
+#nas
+#part_bill_event
+#part_export
+#part_export_option
+#part_pkg
+
+my @tables = qw(
+part_pop_local
+part_referral
+part_router_field
+part_sb_field
+part_svc
+part_svc_column
+part_svc_router
+pkg_svc
+port
+prepay_credit
+queue
+queue_arg
+queue_depend
+radius_usergroup
+router
+router_field
+sb_field
+session
+svc_acct
+svc_acct_pop
+svc_broadband
+svc_domain
+svc_forward
+svc_www
+type_pkgs
+);
+
+my $s_dbh = DBI->connect($s_datasrc, $s_dbuser, $s_dbpass) or die $DBI::errstr;
+my $d_dbh = DBI->connect($d_datasrc, $d_dbuser, $d_dbpass) or die $DBI::errstr;
+
+foreach my $table ( @tables ) {
+ $d_dbh->do("delete from $table");
+
+ my $s_sth = $s_dbh->prepare("select * from $table");
+ $s_sth->execute or die $s_sth->errstr;
+
+ my $row;
+ while ( $row = $s_sth->fetchrow_arrayref ) {
+ my $d_sth = $d_dbh->prepare(
+ "insert into $table ( ".
+ join(', ', @{$s_sth->{NAME}} ).
+ ' ) VALUES ( '.
+ join(', ', map { '?' } @{$s_sth->{NAME}} ).
+ ' )'
+ ) or die $d_dbh->errstr;
+
+ $d_sth->execute(@$row) or die $d_sth->errstr;
+
+ }
+}
+
diff --git a/bin/fs-migrate-cust_tax_exempt b/bin/fs-migrate-cust_tax_exempt
new file mode 100755
index 0000000..ede80b0
--- /dev/null
+++ b/bin/fs-migrate-cust_tax_exempt
@@ -0,0 +1,323 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Time::Local;
+use Date::Format;
+use Time::Duration;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw( qsearch dbh );
+use FS::cust_tax_exempt;
+#use FS::cust_bill;
+use FS::h_cust_bill;
+use FS::h_cust_tax_exempt;
+use FS::cust_bill_pkg;
+use FS::cust_tax_exempt_pkg;
+#use Data::Dumper;
+
+my $start = time;
+
+adminsuidsetup shift;
+
+my $fuz = 7; #seconds
+
+ #site-specific rewrites
+my %rewrite = (
+ #cust_tax_exempt.exemptnum => { 'field' => 'newvalue', ... },
+ '23' => { month=>10, year=>2005, invnum=>1640 },
+
+ #etc.
+);
+
+my @cust_tax_exempt = qsearch('cust_tax_exempt', {} );
+my $num_cust_tax_exempt = scalar(@cust_tax_exempt);
+my $num_cust_tax_exempt_migrated = 0;
+my $total_cust_tax_exempt_migrated = 0;
+my $num_cust_tax_exempt_pkg_migrated = 0;
+my $total_cust_tax_exempt_pkg_migrated = 0;
+
+$FS::UID::AutoCommit = 0;
+
+foreach my $cust_tax_exempt ( @cust_tax_exempt ) {
+
+ if ( exists $rewrite{ $cust_tax_exempt->exemptnum } ) {
+ my $hashref = $rewrite{ $cust_tax_exempt->exemptnum };
+ $cust_tax_exempt->setfield($_, $hashref->{$_})
+ foreach keys %$hashref;
+ }
+
+ if ( $cust_tax_exempt->year < 1990 ) {
+ warn "exemption year is ". $cust_tax_exempt->year.
+ "; not migrating exemption ". $cust_tax_exempt->exemptnum.
+ ' for custnum '. $cust_tax_exempt->custnum. "\n\n";
+ next;
+ }
+
+ # also make sure cust_bill_pkg record dates contain the month/year
+# my $mon = $cust_tax_exempt->month;
+# my $year = $cust_tax_exempt->year;
+# $mon--;
+# my $edate_after = timelocal(0,0,0,1,$mon,$year);
+# $mon++;
+# if ( $mon >= 12 ) { $mon-=12; $year++ };
+# my $sdate_before = timelocal(0,0,0,1,$mon,$year);
+
+ my $mon = $cust_tax_exempt->month;
+ my $year = $cust_tax_exempt->year;
+ if ( $mon >= 12 ) { $mon-=12; $year++ };
+ my $sdate_before = timelocal(0,0,0,1,$mon,$year);
+ #$mon++;
+ #if ( $mon >= 12 ) { $mon-=12; $year++ };
+ my $edate_after = timelocal(0,0,0,1,$mon,$year);
+
+ # !! start a transaction? (yes, its started)
+
+ my @h_cust_tax_exempt = qsearch({
+ 'table' => 'h_cust_tax_exempt',
+ 'hashref' => { 'exemptnum' => $cust_tax_exempt->exemptnum },
+ 'extra_sql' => " AND ( history_action = 'insert'
+ OR history_action = 'replace_new' )
+ ORDER BY history_date ASC
+ ",
+ });
+
+ my $amount_so_far = 0;
+ my $num_cust_tax_exempt_pkg = 0;
+ my $total_cust_tax_exempt_pkg = 0;
+ H_CUST_TAX_EXEMPT: foreach my $h_cust_tax_exempt ( @h_cust_tax_exempt ) {
+
+ my $amount = sprintf('%.2f', $h_cust_tax_exempt->amount - $amount_so_far );
+ $amount_so_far += $amount;
+
+# print Dumper($h_cust_tax_exempt), "\n";
+
+ #find a matching cust_bill record
+ # (print time differences and choose a meaningful threshold, should work)
+
+ my @h_cust_bill = ();
+ if ( $cust_tax_exempt->invnum ) {
+ #warn "following invnum ". $cust_tax_exempt->invnum.
+ # " kludge for cust_tax_exempt ". $cust_tax_exempt->exemptnum. "\n";
+
+ @h_cust_bill = qsearch({
+ #'table' => 'cust_bill',
+ 'table' => 'h_cust_bill',
+ 'hashref' => { 'custnum' => $h_cust_tax_exempt->custnum,
+ 'invnum' => $cust_tax_exempt->invnum,
+ 'history_action' => 'insert',
+ },
+ #'extra_sql' =>
+ # ' AND history_date <= '. ( $h_cust_tax_exempt->history_date + $fuz ).
+ # ' AND history_date > '. ( $h_cust_tax_exempt->history_date - $fuz ),
+ });
+
+ } else {
+
+ @h_cust_bill = qsearch({
+ #'table' => 'cust_bill',
+ 'table' => 'h_cust_bill',
+ 'hashref' => { 'custnum' => $h_cust_tax_exempt->custnum,
+ 'history_action' => 'insert',
+ },
+ 'extra_sql' =>
+ ' AND history_date <= '. ( $h_cust_tax_exempt->history_date + $fuz ).
+ ' AND history_date > '. ( $h_cust_tax_exempt->history_date - $fuz ),
+ });
+
+ }
+
+ if ( scalar(@h_cust_bill) != 1 ) {
+ warn ' '. scalar(@h_cust_bill). ' h_cust_bill records matching '.
+ 'h_cust_tax_exempt.historynum '. $h_cust_tax_exempt->historynum.
+ "; not migrating (adjust fuz factor?)\n";
+ next;
+ }
+
+ my $h_cust_bill = $h_cust_bill[0];
+
+# print Dumper(@cust_bill), "\n\n";
+
+ # then find a matching cust_bill_pkg record with part_pkg.taxclass record
+ # that matches the one pointed to by cust_tax_exempt.taxnum
+ # (hopefully just one, see how many we can match automatically)
+
+ my $cust_main_county = $cust_tax_exempt->cust_main_county;
+ my $taxclass = $cust_main_county->taxclass;
+
+ my $hashref = {
+ 'custnum' => $cust_tax_exempt->custnum,
+ 'invnum' => $h_cust_bill->invnum,
+ 'pkgnum' => { op=>'>', value=>0, },
+ };
+ unless ( $cust_tax_exempt->invnum ) {
+ # also make sure cust_bill_pkg record dates contain the month/year
+
+ #$hashref->{'sdate'} = { op=>'<', value=>$sdate_before };
+ $hashref->{'sdate'} = { op=>'<=', value=>$sdate_before };
+
+ #$hashref->{'edate'} = { op=>'>', value=>$edate_after };
+ $hashref->{'edate'} = { op=>'>=', value=>$edate_after };
+ }
+
+ if ( $cust_tax_exempt->billpkgnum ) {
+ $hashref->{'billpkgnum'} = $cust_tax_exempt->billpkgnum;
+ }
+
+ my $extra_sql = 'ORDER BY billpkgnum';
+
+ $extra_sql = "AND taxclass = '$taxclass' $extra_sql"
+ unless $cust_tax_exempt->ignore_current_taxclass;
+
+ my @cust_bill_pkg = qsearch({
+ 'select' => 'cust_bill_pkg.*, part_pkg.freq',
+ 'table' => 'cust_bill_pkg',
+ 'addl_from' => 'LEFT JOIN cust_pkg using ( pkgnum ) '.
+ 'LEFT JOIN part_pkg using ( pkgpart ) ',
+ 'hashref' => $hashref,
+ 'extra_sql' => $extra_sql,
+ });
+
+ foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
+ $cust_bill_pkg->exemptable_per_month(
+ sprintf('%.2f',
+ ( $cust_bill_pkg->setup + $cust_bill_pkg->recur )
+ /
+ ( $cust_bill_pkg[0]->freq || 1 )
+ )
+ );
+ }
+
+ my(@cust_tax_exempt_pkg) = ();
+ if ( scalar(@cust_bill_pkg) == 1
+ && $cust_bill_pkg[0]->exemptable_per_month >= $amount
+ )
+ {
+
+ my $cust_bill_pkg = $cust_bill_pkg[0];
+
+ # finally, create an appropriate cust_tax_exempt_pkg record
+
+ push @cust_tax_exempt_pkg, new FS::cust_tax_exempt_pkg {
+ 'billpkgnum' => $cust_bill_pkg->billpkgnum,
+ 'taxnum' => $cust_tax_exempt->taxnum,
+ 'year' => $cust_tax_exempt->year,
+ 'month' => $cust_tax_exempt->month,
+ 'amount' => $amount,
+ };
+
+ } else {
+
+# warn ' '. scalar(@cust_bill_pkg). ' cust_bill_pkg records for invoice '.
+# $h_cust_bill->invnum.
+# "; not migrating h_cust_tax_exempt historynum ".
+# $h_cust_tax_exempt->historynum. " for \$$amount\n";
+# warn " *** DIFFERENT DATES ***\n"
+# if grep { $_->sdate != $cust_bill_pkg[0]->sdate
+# || $_->edate != $cust_bill_pkg[0]->edate
+# } @cust_bill_pkg;
+# foreach ( @cust_bill_pkg ) {
+# warn ' '. $_->billpkgnum. ': '. $_->setup. 's/'. $_->recur.'r'.
+# ' '. time2str('%D', $_->sdate). '-'. time2str('%D', $_->edate).
+# "\n";
+# }
+#
+# next;
+
+ my $remaining = $amount;
+ foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
+ last unless $remaining;
+ my $this_amount =sprintf('%.2f',
+ $remaining <= $cust_bill_pkg->exemptable_per_month
+ ? $remaining
+ : $cust_bill_pkg->exemptable_per_month
+ );;
+
+ push @cust_tax_exempt_pkg, new FS::cust_tax_exempt_pkg {
+ 'billpkgnum' => $cust_bill_pkg->billpkgnum,
+ 'taxnum' => $cust_tax_exempt->taxnum,
+ 'year' => $cust_tax_exempt->year,
+ 'month' => $cust_tax_exempt->month,
+ 'amount' => $this_amount,
+ };
+
+ $remaining -= $this_amount;
+
+ }
+
+ }
+
+ foreach my $cust_tax_exempt_pkg ( @cust_tax_exempt_pkg ) {
+ my $error = $cust_tax_exempt_pkg->insert;
+ #my $error = $cust_tax_exempt_pkg->check;
+ if ( $error ) {
+ warn "*** error inserting cust_tax_exempt_pkg record: $error\n";
+ next; #not necessary.. H_CUST_TAX_EXEMPT;
+
+ #not necessary, incorrect $total_cust_tax_exempt_pkg will error it out
+ # roll back at least the entire cust_tax_exempt transaction
+ # next CUST_TAX_EXEMPT;
+ }
+
+ $num_cust_tax_exempt_pkg++;
+
+ $total_cust_tax_exempt_pkg += $cust_tax_exempt_pkg->amount;
+
+ }
+
+ }
+
+ $total_cust_tax_exempt_pkg = sprintf('%.2f', $total_cust_tax_exempt_pkg );
+
+ unless ( $total_cust_tax_exempt_pkg == $cust_tax_exempt->amount ) {
+ warn "total h_ amount $total_cust_tax_exempt_pkg != cust_tax_exempt.amount ".
+ $cust_tax_exempt->amount.
+ ";\n not migrating exemption ". $cust_tax_exempt->exemptnum. " for ".
+ $cust_tax_exempt->month. '/'. $cust_tax_exempt->year.
+ ' (custnum '. $cust_tax_exempt->custnum. ") ".
+ #"\n (sdate < ". time2str('%D', $sdate_before ).
+ "\n (sdate <= ". time2str('%D', $sdate_before ). " [$sdate_before]".
+ #' / edate > '. time2str('%D', $edate_after ). ')'.
+ ' / edate >= '. time2str('%D', $edate_after ). " [$edate_after])".
+ "\n\n";
+
+ # roll back at least the entire cust_tax_exempt transaction
+ dbh->rollback;
+
+ # next CUST_TAX_EXEMPT;
+ next;
+ }
+
+ # remove the cust_tax_exempt record
+ my $error = $cust_tax_exempt->delete;
+ if ( $error ) {
+ #roll back at least the entire cust_tax_exempt transaction
+ dbh->rollback;
+
+ #next CUST_TAX_EXEMPT;
+ next;
+ }
+
+ $num_cust_tax_exempt_migrated++;
+ $total_cust_tax_exempt_migrated += $cust_tax_exempt->amount;
+
+ $num_cust_tax_exempt_pkg_migrated += $num_cust_tax_exempt_pkg;
+ $total_cust_tax_exempt_pkg_migrated += $total_cust_tax_exempt_pkg;
+
+ # commit the transaction
+ dbh->commit;
+
+}
+
+$total_cust_tax_exempt_migrated =
+ sprintf('%.2f', $total_cust_tax_exempt_migrated );
+$total_cust_tax_exempt_pkg_migrated =
+ sprintf('%.2f', $total_cust_tax_exempt_pkg_migrated );
+
+warn
+ "$num_cust_tax_exempt_migrated / $num_cust_tax_exempt (".
+ sprintf('%.2f', 100 * $num_cust_tax_exempt_migrated / $num_cust_tax_exempt).
+ '%) cust_tax_exempt records migrated ($'. $total_cust_tax_exempt_migrated.
+ ")\n to $num_cust_tax_exempt_pkg_migrated cust_tax_exempt_pkg records".
+ ' ($'. $total_cust_tax_exempt_pkg_migrated. ')'.
+ "\n in ". duration(time-$start). "\n"
+;
+
diff --git a/bin/fs-migrate-part_svc b/bin/fs-migrate-part_svc
new file mode 100755
index 0000000..b0f3ac5
--- /dev/null
+++ b/bin/fs-migrate-part_svc
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch fields);
+use FS::part_svc;
+
+my $user = shift or die &usage;
+my $dbh = adminsuidsetup $user;
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+
+foreach my $part_svc ( qsearch('part_svc', {} ) ) {
+ foreach my $field (
+ grep { defined($part_svc->getfield($part_svc->svcdb.'__'.$_.'_flag') ) }
+ fields($part_svc->svcdb)
+ ) {
+ my $flag = $part_svc->getfield($part_svc->svcdb.'__'.$field.'_flag');
+ if ( uc($flag) =~ /^([DF])$/ ) {
+ my $part_svc_column = new FS::part_svc_column {
+ 'svcpart' => $part_svc->svcpart,
+ 'columnname' => $field,
+ 'columnflag' => $1,
+ 'columnvalue' => $part_svc->getfield($part_svc->svcdb.'__'.$field),
+ };
+ my $error = $part_svc_column->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ die $error;
+ }
+ }
+ }
+}
+
+$dbh->commit or die $dbh->errstr;
+
+sub usage {
+ die "Usage:\n fs-migrate-part_svc user\n";
+}
+
diff --git a/bin/fs-migrate-payref b/bin/fs-migrate-payref
new file mode 100755
index 0000000..1584197
--- /dev/null
+++ b/bin/fs-migrate-payref
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_pay;
+use FS::cust_refund;
+
+my $user = shift or die &usage;
+my $dbh = adminsuidsetup $user;
+
+# apply payments to invoices
+
+foreach my $cust_pay ( qsearch('cust_pay', {} ) ) {
+ my $error = $cust_pay->upgrade_replace;
+ warn $error if $error;
+}
+
+# apply refunds to credits
+
+foreach my $cust_refund ( qsearch('cust_refund') ) {
+ my $error = $cust_refund->upgrade_replace;
+ warn $error if $error;
+}
+
+# ? apply credits to invoices
+
+sub usage {
+ die "Usage:\n fs-migrate-payref user\n";
+}
+
diff --git a/bin/fs-migrate-svc_acct_sm b/bin/fs-migrate-svc_acct_sm
new file mode 100755
index 0000000..07f7b61
--- /dev/null
+++ b/bin/fs-migrate-svc_acct_sm
@@ -0,0 +1,227 @@
+#!/usr/bin/perl -Tw
+#
+# jeff@cmh.net 01-Jul-20
+
+#to delay loading dbdef until we're ready
+#BEGIN { $FS::Record::setup_hack = 1; }
+
+use strict;
+use Term::Query qw(query);
+#use DBI;
+#use DBIx::DBSchema;
+#use DBIx::DBSchema::Table;
+#use DBIx::DBSchema::Column;
+#use DBIx::DBSchema::ColGroup::Unique;
+#use DBIx::DBSchema::ColGroup::Index;
+use FS::Conf;
+use FS::UID qw(adminsuidsetup datasrc checkeuid getsecrets);
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_domain;
+use FS::svc_forward;
+use vars qw( $conf $old_default_domain %part_domain_svc %part_acct_svc %part_forward_svc $svc_acct $svc_acct_sm $error);
+
+die "Not running uid freeside!" unless checkeuid();
+
+my $user = shift or die &usage;
+getsecrets($user);
+
+$conf = new FS::Conf;
+$old_default_domain = $conf->config('domain');
+
+#needs to match FS::Record
+#my($dbdef_file) = "/usr/local/etc/freeside/dbdef.". datasrc;
+
+###
+# This section would be the appropriate place to manipulate
+# the schema & tables.
+###
+
+## we need to add the domsvc to svc_acct
+## we must add a svc_forward record....
+## I am thinking that the fields svcnum (int), destsvc (int), and
+## dest (varchar (80)) are appropriate, with destsvc/dest an either/or
+## much in the spirit of cust_main_invoice
+
+###
+# massage the data
+###
+
+my($dbh)=adminsuidsetup $user;
+
+$|=1;
+
+$FS::svc_Common::noexport_hack = 1;
+$FS::svc_domain::whois_hack = 1;
+
+%part_domain_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_domain'});
+%part_acct_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+%part_forward_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_forward'});
+
+die "No services with svcdb svc_domain!\n" unless %part_domain_svc;
+die "No services with svcdb svc_acct!\n" unless %part_acct_svc;
+die "No services with svcdb svc_forward!\n" unless %part_forward_svc;
+
+my($svc_domain) = qsearchs('svc_domain', { 'domain' => $old_default_domain });
+if (! $svc_domain || $svc_domain->domain != $old_default_domain) {
+ print <<EOF;
+
+Your database currently does not contain a svc_domain record for the
+domain $old_default_domain. Would you like me to add one for you?
+EOF
+
+ my($response)=scalar(<STDIN>);
+ chop $response;
+ if ($response =~ /^[yY]/) {
+ print "\n\n", &menu_domain_svc, "\n", <<END;
+I need to create new domain accounts. Which service shall I use for that?
+END
+ my($domain_svcpart)=&getdomainpart;
+
+ $svc_domain = new FS::svc_domain {
+ 'domain' => $old_default_domain,
+ 'svcpart' => $domain_svcpart,
+ 'action' => 'M',
+ };
+# $error=$svc_domain->insert && die "Error adding domain $old_default_domain: $error";
+ $error=$svc_domain->insert;
+ die "Error adding domain $old_default_domain: $error" if $error;
+ }else{
+ print <<EOF;
+
+ This program cannot function properly until a svc_domain record matching
+your conf_dir/domain file exists.
+EOF
+
+ exit 1;
+ }
+}
+
+print "\n\n", &menu_acct_svc, "\n", <<END;
+I may need to create some new pop accounts and set up forwarding to them
+for some users. Which service shall I use for that?
+END
+my($pop_svcpart)=&getacctpart;
+
+print "\n\n", &menu_forward_svc, "\n", <<END;
+I may need to create some new forwarding for some users. Which service
+shall I use for that?
+END
+my($forward_svcpart)=&getforwardpart;
+
+sub menu_domain_svc {
+ ( join "\n", map "$_: ".$part_domain_svc{$_}->svc, sort keys %part_domain_svc ). "\n";
+}
+sub menu_acct_svc {
+ ( join "\n", map "$_: ".$part_acct_svc{$_}->svc, sort keys %part_acct_svc ). "\n";
+}
+sub menu_forward_svc {
+ ( join "\n", map "$_: ".$part_forward_svc{$_}->svc, sort keys %part_forward_svc ). "\n";
+}
+sub getdomainpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_domain_svc ];
+ $^W=1;
+ $return;
+}
+sub getacctpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_acct_svc ];
+ $^W=1;
+ $return;
+}
+sub getforwardpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_forward_svc ];
+ $^W=1;
+ $return;
+}
+
+
+#migrate data
+
+my(@svc_accts) = qsearch('svc_acct', {});
+foreach $svc_acct (@svc_accts) {
+ my(@svc_acct_sms) = qsearch('svc_acct_sm', {
+ domuid => $svc_acct->getfield('uid'),
+ }
+ );
+
+ # Ok.. we've got the svc_acct record, and an array of svc_acct_sm's
+ # What do we do from here?
+
+ # The intuitive:
+ # plop the svc_acct into the 'default domain'
+ # and then represent the svc_acct_sm's with svc_forwards
+ # they can be gussied up manually, later
+ #
+ # Perhaps better:
+ # when no svc_acct_sm exists, place svc_acct in 'default domain'
+ # when one svc_acct_sm exists, place svc_acct in corresponding
+ # domain & possibly create a svc_forward in 'default domain'
+ # when multiple svc_acct_sm's exists (in different domains) we'd
+ # better use the 'intuitive' approach.
+ #
+ # Specific way:
+ # as 'perhaps better,' but we may be able to guess which domain
+ # is correct by comparing the svcnum of domains to the username
+ # of the svc_acct
+ #
+
+ # The intuitive way:
+
+ my $def_acct = new FS::svc_acct ( { $svc_acct->hash } );
+ $def_acct->setfield('domsvc' => $svc_domain->getfield('svcnum'));
+ $error = $def_acct->replace($svc_acct);
+ die "Error replacing svc_acct for " . $def_acct->username . " : $error" if $error;
+
+ foreach $svc_acct_sm (@svc_acct_sms) {
+
+ my($domrec)=qsearchs('svc_domain', {
+ svcnum => $svc_acct_sm->getfield('domsvc'),
+ }) || die "svc_acct_sm references invalid domsvc $svc_acct_sm->getfield('domsvc')\n";
+
+ if ($svc_acct_sm->getfield('domuser') =~ /^\*$/) {
+
+ my($newdom) = new FS::svc_domain ( { $domrec->hash } );
+ $newdom->setfield('catchall', $svc_acct->svcnum);
+ $newdom->setfield('action', "M");
+ $error = $newdom->replace($domrec);
+ die "Error replacing svc_domain for (anything)@" . $domrec->domain . " : $error" if $error;
+
+ } else {
+
+ my($newacct) = new FS::svc_acct {
+ 'svcpart' => $pop_svcpart,
+ 'username' => $svc_acct_sm->getfield('domuser'),
+ 'domsvc' => $svc_acct_sm->getfield('domsvc'),
+ 'dir' => '/dev/null',
+ };
+ $error = $newacct->insert;
+ die "Error adding svc_acct for " . $newacct->username . " : $error" if $error;
+
+ my($newforward) = new FS::svc_forward {
+ 'svcpart' => $forward_svcpart,
+ 'srcsvc' => $newacct->getfield('svcnum'),
+ 'dstsvc' => $def_acct->getfield('svcnum'),
+ };
+ $error = $newforward->insert;
+ die "Error adding svc_forward for " . $newacct->username ." : $error" if $error;
+ }
+
+ $error = $svc_acct_sm->delete;
+ die "Error deleting svc_acct_sm for " . $svc_acct_sm->domuser ." : $error" if $error;
+
+ };
+
+};
+
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+print "svc_acct_sm records sucessfully migrated\n";
+
+sub usage {
+ die "Usage:\n fs-migrate-svc_acct_sm user\n";
+}
+
diff --git a/bin/fs-radius-add-check b/bin/fs-radius-add-check
new file mode 100755
index 0000000..4e4769e
--- /dev/null
+++ b/bin/fs-radius-add-check
@@ -0,0 +1,68 @@
+#!/usr/bin/perl -Tw
+
+# quick'n'dirty hack of fs-setup to add radius attributes
+
+use strict;
+use DBI;
+use FS::UID qw(adminsuidsetup checkeuid getsecrets);
+use FS::raddb;
+
+die "Not running uid freeside!" unless checkeuid();
+
+my %attrib2db =
+ map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib;
+
+my $user = shift or die &usage;
+getsecrets($user);
+
+my $dbh = adminsuidsetup $user;
+
+###
+
+print "\n\n", <<END, ":";
+Enter the additional RADIUS check attributes you need to track for
+each user, separated by whitespace.
+END
+my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
+ split(" ",&getvalue);
+
+sub getvalue {
+ my($x)=scalar(<STDIN>);
+ chop $x;
+ $x;
+}
+
+###
+
+my($char_d) = 80; #default maxlength for text fields
+
+###
+
+foreach my $attribute ( @attributes ) {
+
+ my $statement =
+ "ALTER TABLE svc_acct ADD COLUMN rc_$attribute varchar($char_d) NULL";
+ my $sth = $dbh->prepare( $statement )
+ or warn "Error preparing $statement: ". $dbh->errstr;
+ my $rc = $sth->execute
+ or warn "Error executing $statement: ". $sth->errstr;
+
+ $statement =
+ "ALTER TABLE h_svc_acct ADD COLUMN rc_$attribute varchar($char_d) NULL";
+ $sth = $dbh->prepare( $statement )
+ or warn "Error preparing $statement: ". $dbh->errstr;
+ $rc = $sth->execute
+ or warn "Error executing $statement: ". $sth->errstr;
+
+}
+
+$dbh->commit or die $dbh->errstr;
+
+$dbh->disconnect or die $dbh->errstr;
+
+print "\n\n", "Now you must run dbdef-create.\n\n";
+
+sub usage {
+ die "Usage:\n fs-radius-add-check user\n";
+}
+
diff --git a/bin/fs-radius-add-reply b/bin/fs-radius-add-reply
new file mode 100755
index 0000000..3de0137
--- /dev/null
+++ b/bin/fs-radius-add-reply
@@ -0,0 +1,69 @@
+#!/usr/bin/perl -Tw
+
+# quick'n'dirty hack of fs-setup to add radius attributes
+
+use strict;
+use DBI;
+use FS::UID qw(adminsuidsetup checkeuid getsecrets);
+use FS::raddb;
+
+die "Not running uid freeside!" unless checkeuid();
+
+my %attrib2db =
+ map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib;
+
+my $user = shift or die &usage;
+getsecrets($user);
+
+my $dbh = adminsuidsetup $user;
+
+###
+
+print "\n\n", <<END, ":";
+Enter the additional RADIUS reply attributes you need to track for
+each user, separated by whitespace.
+END
+my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
+ split(" ",&getvalue);
+
+sub getvalue {
+ my($x)=scalar(<STDIN>);
+ chop $x;
+ $x;
+}
+
+###
+
+my($char_d) = 80; #default maxlength for text fields
+
+###
+
+foreach my $attribute ( @attributes ) {
+
+ my $statement =
+ "ALTER TABLE svc_acct ADD COLUMN radius_$attribute varchar($char_d) NULL";
+ my $sth = $dbh->prepare( $statement )
+ or warn "Error preparing $statement: ". $dbh->errstr;
+ my $rc = $sth->execute
+ or warn "Error executing $statement: ". $sth->errstr;
+
+ $statement =
+ "ALTER TABLE h_svc_acct ADD COLUMN radius_$attribute varchar($char_d) NULL";
+ $sth = $dbh->prepare( $statement )
+ or warn "Error preparing $statement: ". $dbh->errstr;
+ $rc = $sth->execute
+ or warn "Error executing $statement: ". $sth->errstr;
+
+}
+
+$dbh->commit or die $dbh->errstr;
+
+$dbh->disconnect or die $dbh->errstr;
+
+print "\n\n", "Now you must run dbdef-create.\n\n";
+
+sub usage {
+ die "Usage:\n fs-radius-add-reply user\n";
+}
+
+
diff --git a/bin/fs-setup b/bin/fs-setup
deleted file mode 100755
index 45332d8..0000000
--- a/bin/fs-setup
+++ /dev/null
@@ -1,542 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# create database and necessary tables, etc. DBI version.
-#
-# ivan@sisd.com 97-nov-8,9
-#
-# agent_type and type_pkgs added.
-# (index need to be declared, & primary keys shoudln't have mysql syntax)
-# ivan@sisd.com 97-nov-13
-#
-# pulled modified version back out of register.cgi ivan@sisd.com 98-feb-21
-#
-# removed extraneous sample data ivan@sisd.com 98-mar-23
-#
-# gained the big hash from dbdef.pm, dbdef.pm usage rewrite ivan@sisd.com
-# 98-apr-19 - 98-may-11 plus
-#
-# finished up ivan@sisd.com 98-jun-1
-#
-# part_svc fields are all forced NULL, not the opposite
-# hmm: also are forced varchar($char_d) as fixed '0' for things like
-# uid is Not Good. will this break anything else?
-# ivan@sisd.com 98-jun-29
-#
-# ss is 11 chars ivan@sisd.com 98-jul-20
-#
-# setup of arbitrary radius fields ivan@sisd.com 98-aug-9
-#
-# ouch, removed index on company name that wasn't supposed to be there
-# ivan@sisd.com 98-sep-4
-#
-# fix radius attributes ivan@sisd.com 98-sep-27
-
-#to delay loading dbdef until we're ready
-BEGIN { $FS::Record::setup_hack = 1; }
-
-use strict;
-use DBI;
-use FS::dbdef;
-use FS::UID qw(adminsuidsetup datasrc);
-use FS::Record;
-use FS::cust_main_county;
-
-#needs to match FS::Record
-my($dbdef_file) = "/var/spool/freeside/dbdef.". datasrc;
-
-###
-
-print "\nEnter the maximum username length: ";
-my($username_len)=&getvalue;
-
-print "\n\n", <<END, ":";
-Freeside tracks the RADIUS attributes User-Name, Password and Framed-IP-Address
-for each user. Enter any additional RADIUS attributes you need to track for
-each user, separated by whitespace.
-END
-my @attributes = map { s/\-/_/g; $_; } split(" ",&getvalue);
-
-sub getvalue {
- my($x)=scalar(<STDIN>);
- chop $x;
- $x;
-}
-
-###
-
-my($char_d) = 80; #default maxlength for text fields
-
-#my(@date_type) = ( 'timestamp', '', '' );
-my(@date_type) = ( 'int', 'NULL', '' );
-my(@perl_type) = ( 'long varchar', 'NULL', '' );
-my(@money_type);
-if (datasrc =~ m/Pg/) { #Pg can't do decimal(10,2)
- @money_type = ( 'money', '', '' );
-} else {
- @money_type = ( 'decimal', '', '10,2' );
-}
-
-###
-# create a dbdef object from the old data structure
-###
-
-my(%tables)=&tables_hash_hack;
-
-#turn it into objects
-my($dbdef) = new FS::dbdef ( map {
- my(@columns);
- while (@{$tables{$_}{'columns'}}) {
- my($name,$type,$null,$length)=splice @{$tables{$_}{'columns'}}, 0, 4;
- push @columns, new FS::dbdef_column ( $name,$type,$null,$length );
- }
- FS::dbdef_table->new(
- $_,
- $tables{$_}{'primary_key'},
- #FS::dbdef_unique->new(@{$tables{$_}{'unique'}}),
- #FS::dbdef_index->new(@{$tables{$_}{'index'}}),
- FS::dbdef_unique->new($tables{$_}{'unique'}),
- FS::dbdef_index->new($tables{$_}{'index'}),
- @columns,
- );
-} (keys %tables) );
-
-#add radius attributes to svc_acct
-
-my($svc_acct)=$dbdef->table('svc_acct');
-
-my($attribute);
-foreach $attribute (@attributes) {
- $svc_acct->addcolumn ( new FS::dbdef_column (
- 'radius_'. $attribute,
- 'varchar',
- 'NULL',
- $char_d,
- ));
-}
-
-#make part_svc table (but now as object)
-
-my($part_svc)=$dbdef->table('part_svc');
-
-#because of svc_acct_pop
-#foreach (grep /^svc_/, $dbdef->tables) {
-#foreach (qw(svc_acct svc_acct_sm svc_charge svc_domain svc_wo)) {
-foreach (qw(svc_acct svc_acct_sm svc_domain)) {
- my($table)=$dbdef->table($_);
- my($col);
- foreach $col ( $table->columns ) {
- next if $col =~ /^svcnum$/;
- $part_svc->addcolumn( new FS::dbdef_column (
- $table->name. '__' . $table->column($col)->name,
- 'varchar', #$table->column($col)->type,
- 'NULL',
- $char_d, #$table->column($col)->length,
- ));
- $part_svc->addcolumn ( new FS::dbdef_column (
- $table->name. '__'. $table->column($col)->name . "_flag",
- 'char',
- 'NULL',
- 1,
- ));
- }
-}
-
-#important
-$dbdef->save($dbdef_file);
-FS::Record::reload_dbdef;
-
-###
-# create 'em
-###
-
-my($dbh)=adminsuidsetup;
-
-#create tables
-$|=1;
-
-my($table);
-foreach ($dbdef->tables) {
- my($table)=$dbdef->table($_);
- print "Creating $_...";
-
- my($statement);
-
- #create table
- foreach $statement ($table->sql_create_table(datasrc)) {
- #print $statement, "\n";
- $dbh->do( $statement )
- or die "CREATE error: ",$dbh->errstr, "\ndoing statement: $statement";
- }
-
- print "\n";
-}
-
-#not really sample data (and shouldn't default to US)
-
-#cust_main_county
-foreach ( qw(
-AL AK AS AZ AR CA CO CT DC DE FM FL GA GU HI ID IL IN IA KS KY LA
-ME MH MD MA MI MN MS MO MT NC ND NE NH NJ NM NV NY MP OH OK OR PA PW PR RI
-SC SD TN TX TT UT VT VI VA WA WV WI WY AE AA AP
-) ) {
- my($cust_main_county)=create FS::cust_main_county({
- 'state' => $_,
- 'tax' => 0,
- });
- my($error);
- $error=$cust_main_county->insert;
- die $error if $error;
-}
-
-$dbh->disconnect or die $dbh->errstr;
-
-###
-# Now it becomes an object. much better.
-###
-sub tables_hash_hack {
-
- #note that s/(date|change)/_$1/; to avoid keyword conflict.
- #put a kludge in FS::Record to catch this or? (pry need some date-handling
- #stuff anyway also)
-
- my(%tables)=( #yech.}
-
- 'agent' => {
- 'columns' => [
- 'agentnum', 'int', '', '',
- 'agent', 'varchar', '', $char_d,
- 'typenum', 'int', '', '',
- 'freq', 'smallint', 'NULL', '',
- 'prog', @perl_type,
- ],
- 'primary_key' => 'agentnum',
- 'unique' => [ [] ],
- 'index' => [ ['typenum'] ],
- },
-
- 'agent_type' => {
- 'columns' => [
- 'typenum', 'int', '', '',
- 'atype', 'varchar', '', $char_d,
- ],
- 'primary_key' => 'typenum',
- 'unique' => [ [] ],
- 'index' => [ [] ],
- },
-
- 'type_pkgs' => {
- 'columns' => [
- 'typenum', 'int', '', '',
- 'pkgpart', 'int', '', '',
- ],
- 'primary_key' => '',
- 'unique' => [ ['typenum', 'pkgpart'] ],
- 'index' => [ ['typenum'] ],
- },
-
- 'cust_bill' => {
- 'columns' => [
- 'invnum', 'int', '', '',
- 'custnum', 'int', '', '',
- '_date', @date_type,
- 'charged', @money_type,
- 'owed', @money_type,
- 'printed', 'int', '', '',
- ],
- 'primary_key' => 'invnum',
- 'unique' => [ [] ],
- 'index' => [ ['custnum'] ],
- },
-
- 'cust_bill_pkg' => {
- 'columns' => [
- 'pkgnum', 'int', '', '',
- 'invnum', 'int', '', '',
- 'setup', @money_type,
- 'recur', @money_type,
- 'sdate', @date_type,
- 'edate', @date_type,
- ],
- 'primary_key' => '',
- 'unique' => [ ['pkgnum', 'invnum'] ],
- 'index' => [ ['invnum'] ],
- },
-
- 'cust_credit' => {
- 'columns' => [
- 'crednum', 'int', '', '',
- 'custnum', 'int', '', '',
- '_date', @date_type,
- 'amount', @money_type,
- 'credited', @money_type,
- 'otaker', 'varchar', '', 8,
- 'reason', 'varchar', '', 255,
- ],
- 'primary_key' => 'crednum',
- 'unique' => [ [] ],
- 'index' => [ ['custnum'] ],
- },
-
- 'cust_main' => {
- 'columns' => [
- 'custnum', 'int', '', '',
- 'agentnum', 'int', '', '',
- 'last', 'varchar', '', $char_d,
- 'first', 'varchar', '', $char_d,
- 'ss', 'char', 'NULL', 11,
- 'company', 'varchar', 'NULL', $char_d,
- 'address1', 'varchar', '', $char_d,
- 'address2', 'varchar', 'NULL', $char_d,
- 'city', 'varchar', '', $char_d,
- 'county', 'varchar', 'NULL', $char_d,
- 'state', 'char', '', 2,
- 'zip', 'varchar', '', 10,
- 'country', 'char', '', 2,
- 'daytime', 'varchar', 'NULL', 20,
- 'night', 'varchar', 'NULL', 20,
- 'fax', 'varchar', 'NULL', 12,
- 'payby', 'char', '', 4,
- 'payinfo', 'varchar', 'NULL', 16,
- 'paydate', @date_type,
- 'payname', 'varchar', 'NULL', $char_d,
- 'tax', 'char', 'NULL', 1,
- 'otaker', 'varchar', '', 8,
- 'refnum', 'int', '', '',
- ],
- 'primary_key' => 'custnum',
- 'unique' => [ [] ],
- #'index' => [ ['last'], ['company'] ],
- 'index' => [ ['last'], ],
- },
-
- 'cust_main_county' => { #county+state are checked off the cust_main_county
- #table for validation and to provide a tax rate.
- #add country?
- 'columns' => [
- 'taxnum', 'int', '', '',
- 'state', 'char', '', 2, #two letters max in US... elsewhere?
- 'county', 'varchar', '', $char_d,
- 'tax', 'real', '', '', #tax %
- ],
- 'primary_key' => 'taxnum',
- 'unique' => [ [] ],
- # 'unique' => [ ['taxnum'], ['state', 'county'] ],
- 'index' => [ [] ],
- },
-
- 'cust_pay' => {
- 'columns' => [
- 'paynum', 'int', '', '',
- 'invnum', 'int', '', '',
- 'paid', @money_type,
- '_date', @date_type,
- 'payby', 'char', '', 4, # CARD/BILL/COMP, should be index into
- # payment type table.
- 'payinfo', 'varchar', 'NULL', 16, #see cust_main above
- 'paybatch', 'varchar', 'NULL', $char_d, #for auditing purposes.
- ],
- 'primary_key' => 'paynum',
- 'unique' => [ [] ],
- 'index' => [ ['invnum'] ],
- },
-
- 'cust_pay_batch' => { #what's this used for again? list of customers
- #in current CARD batch? (necessarily CARD?)
- 'columns' => [
- 'invnum', 'int', '', '',
- 'custnum', 'int', '', '',
- 'last', 'varchar', '', $char_d,
- 'first', 'varchar', '', $char_d,
- 'address1', 'varchar', '', $char_d,
- 'address2', 'varchar', 'NULL', $char_d,
- 'city', 'varchar', '', $char_d,
- 'state', 'char', '', 2,
- 'zip', 'varchar', '', 10,
- 'country', 'char', '', 2,
- 'trancode', 'TINYINT', '', '',
- 'cardnum', 'varchar', '', 16,
- 'exp', @date_type,
- 'payname', 'varchar', 'NULL', $char_d,
- 'amount', @money_type,
- ],
- 'primary_key' => '',
- 'unique' => [ [] ],
- 'index' => [ ['invnum'], ['custnum'] ],
- },
-
- 'cust_pkg' => {
- 'columns' => [
- 'pkgnum', 'int', '', '',
- 'custnum', 'int', '', '',
- 'pkgpart', 'int', '', '',
- 'otaker', 'varchar', '', 8,
- 'setup', @date_type,
- 'bill', @date_type,
- 'susp', @date_type,
- 'cancel', @date_type,
- 'expire', @date_type,
- ],
- 'primary_key' => 'pkgnum',
- 'unique' => [ [] ],
- 'index' => [ ['custnum'] ],
- },
-
- 'cust_refund' => {
- 'columns' => [
- 'refundnum', 'int', '', '',
- 'crednum', 'int', '', '',
- '_date', @date_type,
- 'refund', @money_type,
- 'otaker', 'varchar', '', 8,
- 'reason', 'varchar', '', $char_d,
- 'payby', 'char', '', 4, # CARD/BILL/COMP, should be index
- # into payment type table.
- 'payinfo', 'varchar', 'NULL', 16, #see cust_main above
- ],
- 'primary_key' => 'refundnum',
- 'unique' => [ [] ],
- 'index' => [ ['crednum'] ],
- },
-
- 'cust_svc' => {
- 'columns' => [
- 'svcnum', 'int', '', '',
- 'pkgnum', 'int', '', '',
- 'svcpart', 'int', '', '',
- ],
- 'primary_key' => 'svcnum',
- 'unique' => [ [] ],
- 'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
- },
-
- 'part_pkg' => {
- 'columns' => [
- 'pkgpart', 'int', '', '',
- 'pkg', 'varchar', '', $char_d,
- 'comment', 'varchar', '', $char_d,
- 'setup', @perl_type,
- 'freq', 'smallint', '', '', #billing frequency (months)
- 'recur', @perl_type,
- ],
- 'primary_key' => 'pkgpart',
- 'unique' => [ [] ],
- 'index' => [ [] ],
- },
-
- 'pkg_svc' => {
- 'columns' => [
- 'pkgpart', 'int', '', '',
- 'svcpart', 'int', '', '',
- 'quantity', 'int', '', '',
- ],
- 'primary_key' => '',
- 'unique' => [ ['pkgpart', 'svcpart'] ],
- 'index' => [ ['pkgpart'] ],
- },
-
- 'part_referral' => {
- 'columns' => [
- 'refnum', 'int', '', '',
- 'referral', 'varchar', '', $char_d,
- ],
- 'primary_key' => 'refnum',
- 'unique' => [ [] ],
- 'index' => [ [] ],
- },
-
- 'part_svc' => {
- 'columns' => [
- 'svcpart', 'int', '', '',
- 'svc', 'varchar', '', $char_d,
- 'svcdb', 'varchar', '', $char_d,
- ],
- 'primary_key' => 'svcpart',
- 'unique' => [ [] ],
- 'index' => [ [] ],
- },
-
- #(this should be renamed to part_pop)
- 'svc_acct_pop' => {
- 'columns' => [
- 'popnum', 'int', '', '',
- 'city', 'varchar', '', $char_d,
- 'state', 'char', '', 2,
- 'ac', 'char', '', 3,
- 'exch', 'char', '', 3,
- #rest o' number?
- ],
- 'primary_key' => 'popnum',
- 'unique' => [ [] ],
- 'index' => [ [] ],
- },
-
- 'svc_acct' => {
- 'columns' => [
- 'svcnum', 'int', '', '',
- 'username', 'varchar', '', $username_len, #unique (& remove dup code)
- '_password', 'varchar', '', 25, #13 for encryped pw's plus ' *SUSPENDED*
- 'popnum', 'int', 'NULL', '',
- 'uid', 'bigint', 'NULL', '',
- 'gid', 'bigint', 'NULL', '',
- 'finger', 'varchar', 'NULL', $char_d,
- 'dir', 'varchar', 'NULL', $char_d,
- 'shell', 'varchar', 'NULL', $char_d,
- 'quota', 'varchar', 'NULL', $char_d,
- 'slipip', 'varchar', 'NULL', 15, #four TINYINTs, bah.
- ],
- 'primary_key' => 'svcnum',
- 'unique' => [ [] ],
- 'index' => [ ['username'] ],
- },
-
- 'svc_acct_sm' => {
- 'columns' => [
- 'svcnum', 'int', '', '',
- 'domsvc', 'int', '', '',
- 'domuid', 'bigint', '', '',
- 'domuser', 'varchar', '', $char_d,
- ],
- 'primary_key' => 'svcnum',
- 'unique' => [ [] ],
- 'index' => [ ['domsvc'], ['domuid'] ],
- },
-
- #'svc_charge' => {
- # 'columns' => [
- # 'svcnum', 'int', '', '',
- # 'amount', @money_type,
- # ],
- # 'primary_key' => 'svcnum',
- # 'unique' => [ [] ],
- # 'index' => [ [] ],
- #},
-
- 'svc_domain' => {
- 'columns' => [
- 'svcnum', 'int', '', '',
- 'domain', 'varchar', '', $char_d,
- ],
- 'primary_key' => 'svcnum',
- 'unique' => [ ['domain'] ],
- 'index' => [ [] ],
- },
-
- #'svc_wo' => {
- # 'columns' => [
- # 'svcnum', 'int', '', '',
- # 'svcnum', 'int', '', '',
- # 'svcnum', 'int', '', '',
- # 'worker', 'varchar', '', $char_d,
- # '_date', @date_type,
- # ],
- # 'primary_key' => 'svcnum',
- # 'unique' => [ [] ],
- # 'index' => [ [] ],
- #},
-
- );
-
- %tables;
-
-}
-
diff --git a/bin/generate-prepay b/bin/generate-prepay
new file mode 100755
index 0000000..cb4ba7f
--- /dev/null
+++ b/bin/generate-prepay
@@ -0,0 +1,35 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::prepay_credit;
+
+require 5.004; #srand(time|$$);
+
+my $user = shift or die &usage;
+&adminsuidsetup( $user );
+
+my $amount = shift or die &usage;
+
+my $seconds = shift or die &usage;
+
+my $num_digits = shift or die &usage;
+
+my $num_entries = shift or die &usage;
+
+for ( 1 .. $num_entries ) {
+ my $identifier = join( '', map int(rand(10)), ( 1 .. $num_digits ) );
+ my $prepay_credit = new FS::prepay_credit {
+ 'identifier' => $identifier,
+ 'amount' => $amount,
+ 'seconds' => $seconds,
+ };
+ my $error = $prepay_credit->insert;
+ die $error if $error;
+ print "$identifier\n";
+}
+
+sub usage {
+ die "Usage:\n\n generate-prepay user amount seconds num_digits num_entries";
+}
+
diff --git a/bin/generate-raddb b/bin/generate-raddb
new file mode 100755
index 0000000..af21c05
--- /dev/null
+++ b/bin/generate-raddb
@@ -0,0 +1,53 @@
+#!/usr/bin/perl
+
+# usage: generate-raddb radius-server/raddb/dictionary* >raddb.pm
+# i.e.: generate-raddb ~/freeradius/freeradius-1.0.5/share/dictionary* ~/wirelessoceans/dictionary.ip3networks ~/wtxs/dictionary.mot.canopy >raddb.pm.new
+print <<END;
+package FS::raddb;
+use vars qw(%attrib);
+
+%attrib = (
+END
+
+while (<>) {
+ next if /^(#|\s*$|\$INCLUDE\s+)/;
+ next if /^(VALUE|VENDOR|BEGIN\-VENDOR|END\-VENDOR)\s+/;
+ /^(ATTRIBUTE|ATTRIB_NMC)\s+([\w\-\/]+)\s+/ or die $_;
+ $attrib = $2;
+ $dbname = lc($2);
+ $dbname =~ s/[\-\/]/_/g;
+ $dbname = substr($dbname,0,24);
+ while ( exists $hash{$dbname} ) {
+ #warn $dbname;
+ $dbname =~ s/(.)$//;
+ my $w = $1;
+ $w =~ tr/_a-z0-9/a-z0-9_/;
+ $dbname = "$dbname$w";
+ }
+ $hash{$dbname} = $attrib;
+ #print "$2\n";
+}
+
+foreach ( sort keys %hash ) {
+# print "$_\n" if length($_)>24;
+# print substr($_,0,24),"\n" if length($_)>24;
+# $max = length($_) if length($_)>$max;
+# have to fudge things since everything >24 is *not* unique
+
+ #print " '". substr($_,0,24). "' => '$hash{$_}',\n";
+ print " '$_' ". ( " " x (24-length($_) ) ). "=> '$hash{$_}',\n";
+}
+
+print <<END;
+
+ #NETC.NET.AU (RADIATOR?)
+ 'authentication_type' => 'Authentication-Type',
+
+ #wtxs (dunno)
+ #'radius_operator' => 'Radius-Operator',
+
+);
+
+1;
+END
+
diff --git a/bin/generate-table-module b/bin/generate-table-module
new file mode 100755
index 0000000..509feed
--- /dev/null
+++ b/bin/generate-table-module
@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+
+use FS::Schema qw( dbdef_dist );
+
+my $table = shift;
+
+###
+# add a new FS/FS/table.pm
+###
+
+my %ut = ( #just guesses
+ 'int' => 'number',
+ 'number' => 'float',
+ 'varchar' => 'text',
+ 'text' => 'text',
+ 'serial' => 'number',
+);
+
+my $dbdef_table = dbdef_dist->table($table)
+ or die "define table in Schema.pm first";
+my $primary_key = $dbdef_table->primary_key;
+
+open(SRC,"<eg/table_template.pm") or die $!;
+-e "FS/FS/$table.pm" and die "FS/FS/$table.pm already exists!";
+open(DEST,">FS/FS/$table.pm") or die $!;
+
+while (my $line = <SRC>) {
+
+ $line =~ s/table_name/$table/g;
+
+ if ( $line =~ /^=item\s+field\s+-\s+description\s*$/ ) {
+
+ foreach my $column ( $dbdef_table->columns ) {
+ print DEST "=item $column\n\n";
+ if ( $column eq $primary_key ) {
+ print DEST "primary key\n\n";
+ } else {
+ print DEST "$column\n\n";
+ }
+ }
+ next;
+
+ } elsif ( $line=~ /^(\s*)\$self->ut_numbern\('primary_key'\)\s*/ ) {
+
+ print DEST "$1\$self->ut_numbern('$primary_key')\n"
+ if $primary_key;
+ next;
+
+ } elsif (
+ $line =~ /^(\s*)\|\|\s+\$self->ut_number\('validate_other_fields'\)\s*/
+ ) {
+
+ foreach my $column ( grep { $_ ne $primary_key } $dbdef_table->columns ) {
+ my $ut = $ut{$dbdef_table->column($column)->type};
+ $ut .= 'n' if $dbdef_table->column($column)->null;
+ print DEST "$1|| \$self->ut_$ut('$column')\n";
+ }
+ next;
+
+ }
+
+ print DEST $line;
+}
+
+close SRC;
+close DEST;
+
+###
+# add FS/t/table.t
+###
+
+open(TEST,">FS/t/$table.t") or die $!;
+print TEST <<ENDTEST;
+BEGIN { \$| = 1; print "1..1\\n" }
+END {print "not ok 1\\n" unless \$loaded;}
+use FS::$table;
+\$loaded=1;
+print "ok 1\\n";
+ENDTEST
+close TEST;
+
+###
+# add them to MANIFEST
+###
+
+system('cvs edit FS/MANIFEST');
+
+open(MANIFEST,">>FS/MANIFEST") or die $!;
+print MANIFEST "FS/$table.pm\n",
+ "t/$table.t\n";
+close MANIFEST;
+
diff --git a/bin/generate-tests b/bin/generate-tests
new file mode 100755
index 0000000..73fd29e
--- /dev/null
+++ b/bin/generate-tests
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+@files = glob('FS/*.pm');
+foreach (@files) {
+# warn $_;
+ chomp;
+ s/^FS\///;
+ $f=$_;
+ $f=~s/pm$/t/;
+ $m=$_;
+ $m=~s/\.pm$//;
+ open(TEST,">t/$f");
+ print "t/$f\n";
+ print TEST
+ 'BEGIN { $| = 1; print "1..1\n" }'. "\n".
+ 'END {print "not ok 1\n" unless $loaded;}'. "\n".
+ "use FS::$m;\n".
+ '$loaded=1;'. "\n".
+ 'print "ok 1\n";'. "\n"
+ ;
+ close TEST;
+}
diff --git a/bin/import-county-tax-rates b/bin/import-county-tax-rates
new file mode 100755
index 0000000..05798c9
--- /dev/null
+++ b/bin/import-county-tax-rates
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+#
+# import-county-tax-rates username state country <filename.csv
+# example: import-county-tax-rates ivan CA US <taxes.csv
+#
+# rates.csv: taxrate,county
+
+use FS::UID qw(adminsuidsetup);
+use FS::cust_main_county;
+
+my $user = shift;
+adminsuidsetup $user;
+
+my($state, $country) = (shift, shift);
+
+while (<>) {
+ my($tax, $county) = split(','); #half-ass CSV parser
+
+ my $cust_main_county = new FS::cust_main_county {
+ 'county' => $county,
+ 'state' => $state,
+ 'country' => $country,
+ 'tax' => $tax,
+ };
+
+ my $error = $cust_main_county->insert;
+ #my $error = $cust_main_county->check;
+ die $error if $error;
+
+}
diff --git a/bin/import-optigold.pl b/bin/import-optigold.pl
new file mode 100755
index 0000000..d32a2a1
--- /dev/null
+++ b/bin/import-optigold.pl
@@ -0,0 +1,1077 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use DBI;
+use HTML::TableParser;
+use Date::Parse;
+use Text::CSV_XS;
+use FS::Record qw(qsearch qsearchs);
+use FS::cust_credit;
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::cust_svc;
+use FS::svc_acct;
+use FS::part_referral;
+use FS::part_pkg;
+use FS::UID qw(adminsuidsetup);
+
+my $DEBUG = 0;
+
+my $dry_run = '0';
+
+my $s_dbname = 'DBI:Pg:dbname=optigoldimport';
+my $s_dbuser = 'freeside';
+my $s_dbpass = '';
+my $extension = '.htm';
+
+#my $d_dbuser = 'freeside';
+my $d_dbuser = 'enet';
+#my $d_dbuser = 'ivan';
+#my $d_dbuser = 'freesideimport';
+
+my $radius_file = 'radius.csv';
+my $email_file = 'email.csv';
+
+#my $agentnum = 1;
+my $agentnum = 13;
+my $legacy_domain_svcnum = 1;
+my $legacy_ppp_svcpart = 2;
+my $legacy_email_svcpart = 3;
+#my $legacy_broadband_svcpart = 4;
+#my $legacy_broadband_svcpart = 14;
+#my $previous_credit_reasonnum = 1;
+my $previous_credit_reasonnum = 1220;
+
+
+my $state = ''; #statemachine-ish
+my $sourcefile;
+my $s_dbh;
+my $columncount;
+my $rowcount;
+
+my @args = (
+ {
+ id => 1,
+ hdr => \&header,
+ row => \&row,
+ start => \&start,
+ end => \&end,
+ },
+ );
+
+
+$s_dbh = DBI->connect($s_dbname, $s_dbuser, $s_dbpass,
+ { 'AutoCommit' => 0,
+ 'ChopBlanks' => 1,
+ 'ShowErrorStatement' => 1
+ }
+ );
+
+foreach ( qw ( billcycle cust email product ) ) {
+ $sourcefile = $_;
+
+ print "parsing $sourcefile\n";
+
+ die "bad file name" unless $sourcefile =~ /^\w+$/;
+
+ $columncount = 0;
+ $rowcount = 0;
+
+ my $c_sth = '';
+ if ( $c_sth = $s_dbh->prepare("SELECT COUNT(*) FROM $sourcefile") ) {
+ if ( $c_sth->execute ) {
+ if ( $c_sth->fetchrow_arrayref->[0] ) {
+ warn "already have data in $sourcefile table; skipping";
+ next;
+ }
+ }
+ }
+
+ my $tp = new HTML::TableParser( \@args, { Decode => 1, Trim => 1, Chomp => 1 });
+ $tp->parse_file($sourcefile.$extension) or die "failed";
+ $s_dbh->commit or die $s_dbh->errstr;
+# $s_dbh->disconnect;
+}
+
+
+sub start {
+ warn "start\n" if $DEBUG;
+ my $table_id = shift;
+ die "unexpected state change" unless $state eq '';
+ die "unexpected table" unless $table_id eq '1';
+ $state = 'table';
+}
+
+sub end {
+ warn "end\n" if $DEBUG;
+ my ($tbl_id, $line, $udata) = @_;
+ die "unexpected state change in header" unless $state eq 'rows';
+ die "unexpected table" unless $tbl_id eq '1';
+ $state = '';
+}
+
+sub header {
+ warn "header\n" if $DEBUG;
+ my ($tbl_id, $line, $cols, $udata) = @_;
+ die "unexpected state change in header" unless $state eq 'table';
+ die "unexpected table" unless $tbl_id eq '1';
+ $state = 'rows';
+
+ die "invalid column ". join (', ', grep { !/^[ \w\r]+$/ } @$cols)
+ if scalar(grep { !/^[ \w\r]+$/ } @$cols);
+
+ my $sql = "CREATE TABLE $sourcefile ( ".
+ join(', ', map { s/[ \r]/_/g; "$_ varchar NULL" } @$cols). " )";
+ $s_dbh->do($sql) or die "create table failed: ". $s_dbh->errstr;
+ $columncount = scalar( @$cols );
+}
+
+sub row {
+ warn "row\n" if $DEBUG;
+ my ($tbl_id, $line, $cols, $udata) = @_;
+ die "unexpected state change in row" unless $state eq 'rows';
+ die "unexpected table" unless $tbl_id eq '1';
+
+ die "invalid number of columns: ". join(', ', @$cols)
+ unless (scalar(@$cols) == $columncount);
+
+ my $sql = "INSERT INTO $sourcefile VALUES(".
+ join(', ', map { s/\s*(\S[\S ]*?)\s*$/$1/; $s_dbh->quote($_) } @$cols). ")";
+ $s_dbh->do($sql) or die "insert failed: ". $s_dbh->errstr;
+ $rowcount++;
+ warn "row $rowcount\n" unless ($rowcount % 1000);
+}
+
+## now svc_acct from CSV files
+
+$FS::cust_main::import=1;
+$FS::cust_pkg::disable_agentcheck = 1;
+$FS::cust_svc::ignore_quantity = 1;
+
+my (%master_map) = ();
+my (%referrals) = ();
+my (%custid) = ();
+my (%cancel) = ();
+my (%susp) = ();
+my (%adjo) = ();
+my (%bill) = ();
+my (%cust_pkg_map) = ();
+my (%object_map) = ();
+my (%package_cache) = ();
+my $count = 0;
+
+my $d_dbh = adminsuidsetup $d_dbuser;
+local $FS::UID::AutoCommit = 0;
+
+my @import = ( { 'file' => $radius_file,
+ 'sep_char' => ';',
+ 'fields' => [ qw( garbage1 username garbage2 garbage3 _password ) ],
+ 'fixup' => sub {
+ my $hash = shift;
+ delete $hash->{$_}
+ foreach qw (garbage1 garbage2 garbage3);
+ $hash->{'svcpart'} = $legacy_ppp_svcpart;
+ $hash->{'domsvc'} = $legacy_domain_svcnum;
+ '';
+ },
+ 'mapkey' => 'legacy_ppp',
+ 'skey' => 'username',
+ },
+ { 'file' => $email_file,
+ 'sep_char' => ';',
+ 'fields' => [ qw( username null finger _password status garbage ) ],
+ 'fixup' => sub {
+ my $hash = shift;
+ #return 1
+ # if $object_map{'legacy_ppp'}{$hash->{'username'}};
+ delete $hash->{$_}
+ foreach qw (null status garbage);
+ $hash->{'svcpart'} = $legacy_email_svcpart;
+ $hash->{'domsvc'} = $legacy_domain_svcnum;
+ '';
+ },
+ 'mapkey' => 'legacy_email',
+ 'skey' => 'username',
+ },
+);
+
+while ( @import ) {
+ my $href = shift @import;
+ my $file = $href->{'file'} or die "No file specified";
+ my (@fields) = @{$href->{'fields'}};
+ my ($sep_char) = $href->{'sep_char'} || ';';
+ my ($fixup) = $href->{'fixup'};
+ my ($mapkey) = $href->{'mapkey'};
+ my ($skey) = $href->{'skey'};
+ my $line;
+
+ my $csv = new Text::CSV_XS({'sep_char' => $sep_char});
+ open(FH, $file) or die "cannot open $file: $!";
+ $count = 0;
+
+ while ( defined($line=<FH>) ) {
+ chomp $line;
+
+ $line &= "\177" x length($line); # i hope this isn't really necessary
+ $csv->parse($line)
+ or die "cannot parse: " . $csv->error_input();
+
+ my @values = $csv->fields();
+ my %hash;
+ foreach my $field (@fields) {
+ $hash{$field} = shift @values;
+ }
+
+ if (@values) {
+ warn "skipping malformed line: $line\n";
+ next;
+ }
+
+ my $skip = &{$fixup}(\%hash)
+ if $fixup;
+
+ unless ($skip) {
+ my $svc_acct = new FS::svc_acct { %hash };
+ my $error = $svc_acct->insert;
+ if ($error) {
+ warn $error;
+ next;
+ }
+
+ if ($skey && $mapkey) {
+ my $key = (ref($skey) eq 'CODE') ? &{$skey}($svc_acct) : $hash{$skey};
+ $object_map{$mapkey}{$key} = $svc_acct->svcnum;
+ }
+
+ $count++
+ }
+ }
+ print "Imported $count service records\n";
+
+}
+
+
+
+sub pkg_freq {
+ my ( $href ) = ( shift );
+ my $once;
+ $href->{'one_time_list'} =~ /^\s*(\S[\S ]*?)\s*$/ && ($once = $1);
+ $once
+ ? 0
+ : int(eval "$href->{'months_credit'} + 0");
+# int(eval "$href->{'month_credit'} + 0");
+}
+
+sub account_id {
+ my $href = shift;
+ if ($href->{'slave_account_id'} =~ /^\s*(\S[\S ]*?)\s*$/) {
+ "slave:$1";
+ }else{
+ my $l = $href->{cbilling_cycle_login};
+ $l =~ /^\s*(\S[\S ]*?)\s*$/ && ($l = $1);
+ $l;
+ }
+}
+
+sub b_or {
+ my ( $field, $hash ) = ( shift, shift );
+ $field = 'billing_'. $field
+ if $hash->{'billing_use'} eq 'Billing Address';
+ $hash->{$field};
+}
+
+sub p_or {
+ my ( $field, $hash ) = ( shift, shift );
+ $field = 'billing_'. $field
+ if $hash->{'billing_use'} eq 'Billing Address';
+ my $ac = ( $hash->{$field. '_area_code'}
+ && $hash->{$field. '_area_code'} =~ /^\d{3}$/ )
+ ? $hash->{$field. '_area_code'}. '-'
+ : '903-' # wtf?
+ ;
+ ( $hash->{$field} && $hash->{$field} =~ /^\d{3}-\d{4}$/)
+ ? $ac. $hash->{$field}
+ : '';
+}
+
+sub or_b {
+ my ( $field, $hash ) = ( shift, shift );
+ $hash->{'billing_use'} eq 'Billing Address' ? $hash->{$field} : '';
+}
+
+sub or_p {
+ my ( $field, $hash ) = ( shift, shift );
+ $hash->{'billing_use'} eq 'Billing Address' && $hash->{$field} =~ /^\d{3}-\d{4}$/
+ ? ( $hash->{$field. '_area_code'} =~ /^\d{3}$/
+ ? $hash->{$field. '_area_code'}. '-'
+ : '903-' # wtf?
+ ). $hash->{$field}
+ : '';
+}
+
+my %payby_map = ( '' => 'BILL',
+ 'None' => 'BILL',
+ 'Credit Card' => 'CARD',
+ 'Bank Debit' => 'CHEK',
+ 'Virtual Check' => 'CHEK',
+);
+sub payby {
+ $payby_map{ shift->{billing_type} };
+}
+
+sub payinfo {
+ my $hash = shift;
+ my $payby = payby($hash);
+ my $info;
+ my $cc =
+ $hash->{'credit_card_number_1'}.
+ $hash->{'credit_card_number_2'}.
+ $hash->{'credit_card_number_3'}.
+ $hash->{'credit_card_number_4'};
+ my $bank =
+ $hash->{'bank_account_number'}.
+ '@'.
+ $hash->{'bank_transit_number'};
+ if ($payby eq 'CARD') {
+ $info = $cc;
+ }elsif ($payby eq 'CHEK') {
+ $info = $bank;
+ }elsif ($payby eq 'BILL') {
+ $info = $hash->{'blanket_purchase_order_number'};
+ $bank =~ s/[^\d\@]//g;
+ $cc =~ s/\D//g;
+ if ( $bank =~ /^\d+\@\d{9}/) {
+ $info = $bank;
+ $payby = 'DCHK';
+ }
+ if ( $cc =~ /^\d{13,16}/ ) {
+ $info = $cc;
+ $payby = 'DCRD';
+ }
+ }else{
+ die "unexpected payby";
+ }
+ ($info, $payby);
+}
+
+sub ut_name_fixup {
+ my ($object, $field) = (shift, shift);
+ my $value = $object->getfield($field);
+ $value =~ s/[^\w \,\.\-\']/ /g;
+ $object->setfield($field, $value);
+}
+
+sub ut_text_fixup {
+ my ($object, $field) = (shift, shift);
+ my $value = $object->getfield($field);
+ $value =~ s/[^\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]/ /g;
+ $object->setfield($field, $value);
+}
+
+sub ut_state_fixup {
+ my ($object, $field) = (shift, shift);
+ my $value = $object->getfield($field);
+ $value = 'TX' if $value eq 'TTX';
+ $object->setfield($field, $value);
+}
+
+sub ut_zip_fixup {
+ my ($object, $field) = (shift, shift);
+ my $value = $object->getfield($field);
+ $value =~ s/[^-\d]//g;
+ $object->setfield($field, $value);
+}
+
+my @tables = (
+part_pkg => { 'stable' => 'product',
+#part_pkg => { 'stable' => 'billcycle',
+ 'mapping' =>
+ { 'pkg' => sub { my $href = shift;
+ $href->{'description'}
+ ? $href->{'description'}
+ : $href->{'product_id'};
+ },
+ 'comment' => 'product_id',
+ 'freq' => sub { pkg_freq(shift) },
+ 'recur_fee'=> sub { my $href = shift;
+ my $price = ( pkg_freq($href)
+ ? $href->{'unit_price'}
+ : 0
+ );
+ $price =~ s/[^\d.]//g;
+ $price = 0 unless $price;
+ sprintf("%.2f", $price);
+ },
+ 'setuptax' => sub { my $href = shift;
+ $href->{'taxable'} ? '' : 'Y';
+ },
+ 'recurtax' => sub { my $href = shift;
+ $href->{'taxable'} ? '' : 'Y';
+ },
+ 'plan' => sub { 'flat' },
+ 'disabled' => sub { 'Y' },
+ 'pkg_svc' => sub { my $href = shift;
+ my $result = {};
+ if (pkg_freq($href)){
+ $result->{$legacy_ppp_svcpart} = 1;
+ $result->{$legacy_email_svcpart} =
+ $href->{emails_allowed}
+ if $href->{emails_allowed};
+ }
+ },
+ 'primary_svc'=> sub { pkg_freq(shift)
+ ? $legacy_ppp_svcpart
+ : ''
+ ;
+ },
+ },
+ 'fixup' => sub { my $part_pkg = shift;
+ my $row = shift;
+ unless ($part_pkg->pkg =~ /^\s*(\S[\S ]*?)\s*$/) {
+ warn "no pkg: ". $part_pkg->pkg. " for ". $row->{product_id};
+ return 1;
+ }
+
+ unless ($part_pkg->comment =~ /^\s*(\S[\S ]*?)\s*$/) {
+ warn "no comment: ". $part_pkg->comment. " for ". $row->{product_id};
+ return 1;
+ }
+
+ return 1 if exists($package_cache{$1});
+ $package_cache{$1} = $part_pkg;
+ 1;
+ },
+ 'wrapup' => sub { foreach (keys %package_cache) {
+ my $part_pkg = $package_cache{$_};
+ my $options =
+ { map { my $v = $part_pkg->$_;
+ $part_pkg->$_('');
+ ($_ => $v);
+ }
+ qw (setup_fee recur_fee)
+ };
+ my $error =
+ $part_pkg->insert(options=>$options);
+ die "Error inserting package: $error"
+ if $error;
+ $count++ unless $error;
+ }
+ },
+ },
+part_referral => { 'stable' => 'cust',
+ 'mapping' =>
+ { 'agentnum' => sub { $agentnum },
+ 'referral' => sub { my $r = shift->{'referred_from'};
+ $referrals{$r} = 1;
+ },
+ },
+ 'fixup' => sub { 1 },
+ 'wrapup' => sub { foreach (keys %referrals) {
+ my $part_referral =
+ new FS::part_referral( {
+ 'agentnum' => $agentnum,
+ 'referral' => $referrals{$_},
+ } );
+ my $error = $part_referral->insert;
+ die "Error inserting referral: $error"
+ if $error;
+ $count++ unless $error;
+ $referrals{$_} = $part_referral->refnum;
+ }
+ },
+ },
+#svc_acct => { 'stable' => 'cust',
+# 'mapping' =>
+# { 'username' => 'login',
+# '_password' => 'password',
+# 'svcpart' => sub{ $legacy_ppp_svcpart },
+# 'domsvc' => sub{ $legacy_domain_svcnum },
+# 'status' => 'status',
+# },
+# 'fixup' => sub { my $svc_acct = shift;
+# my $row = shift;
+# my $id = $row->{'master_account'}
+# ? 'slave:'. $row->{'customer_id'}
+# : $row->{'login'};
+# my $status = $svc_acct->status;
+# if ( $status ne 'Current'
+# && $status ne 'On Hold' )
+# {
+# $cancel{$id} =
+# str2time($row->{termination_date});
+# warn "not creating (cancelled) svc_acct for " .
+# $svc_acct->username. "\n";
+# return 1
+# }
+# $susp{$id} = str2time($row->{hold_date})
+# if $status eq 'On Hold';
+# $adjo{$id} = str2time($row->{hold_date})
+# if ( $status eq 'Current' &&
+# $row->{hold_date} );
+# $bill{$id} =
+# str2time($row->{expiration_date});
+# '';
+# },
+# 'skey' => sub { my $svc_acct = shift;
+# my $row = shift;
+# my $id = $row->{'master_account'}
+# ? 'slave:'. $row->{'customer_id'}
+# : $row->{'login'};
+# },
+# },
+cust_main => { 'stable' => 'cust',
+ 'mapping' =>
+ { 'agentnum' => sub { $agentnum },
+ 'agent_custid' => sub { my $id = shift->{'customer_number'};
+ if (exists($custid{$id})) {
+ $custid{$id}++;
+ $id. chr(64 + $custid{$id});
+ }else{
+ $custid{$id} = 0;
+ $id;
+ }
+ },
+ 'last' => sub { b_or('last_name', shift) || ' ' },
+ 'first' => sub { b_or('first_name', shift) || ' ' },
+ 'stateid' => 'drivers_license_number',
+ 'signupdate' => sub { str2time(shift->{'creation_date'}) },
+ 'company' => sub { b_or('company_name', shift) },
+ 'address1' => sub { b_or('address', shift) || ' ' },
+ 'city' => sub { b_or('city', shift) || 'Paris' },
+ 'state' => sub { uc(b_or('state', shift)) || 'TX' },
+ 'zip' => sub { b_or('zip_code', shift) || '75460' },
+ 'country' => sub { 'US' },
+ 'daytime' => sub { p_or('phone', shift) },
+ 'night' => sub { p_or('phone_alternate_1', shift) },
+ 'fax' => sub { p_or('fax', shift) },
+ 'ship_last' => sub { or_b('last_name', shift) },
+ 'ship_first' => sub { or_b('first_name', shift) },
+ 'ship_company' => sub { or_b('company_name', shift) },
+ 'ship_address1'=> sub { or_b('address', shift) },
+ 'ship_city' => sub { or_b('city', shift) },
+ 'ship_state' => sub { uc(or_b('state', shift)) },
+ 'ship_zip' => sub { or_b('zip_code', shift) },
+ 'ship_daytime' => sub { or_p('phone', shift) },
+ 'ship_fax' => sub { or_p('fax', shift) },
+ 'tax' => sub { shift->{taxable} eq '' ? 'Y' : '' },
+ 'refnum' => sub { $referrals{shift->{'referred_from'}}
+ || 1
+ },
+ },
+ 'fixup' => sub { my $cust_main = shift;
+ my $row = shift;
+
+ my ($master_account, $customer_id, $login) =
+ ('', '', '');
+ $row->{'master_account'} =~ /^\s*(\S[\S ]*?)\s*$/
+ && ($master_account = $1);
+ $row->{'customer_id'} =~ /^\s*(\S[\S ]*?)\s*$/
+ && ($customer_id = $1);
+ $row->{'login'} =~ /^\s*(\S[\S ]*?)\s*$/
+ && ($login = $1);
+
+ my ($first, $last, $company) =
+ ('', '', '');
+ $cust_main->first =~ /^\s*(\S[\S ]*?)\s*$/
+ && ($first = $1);
+ $cust_main->last =~ /^\s*(\S[\S ]*?)\s*$/
+ && ($last = $1);
+ $cust_main->company =~ /^\s*(\S[\S ]*?)\s*$/
+ && ($company = $1);
+
+ unless ($first || $last || $company) {
+ warn "bogus entry: ". $row->{'login'};
+ return 1;
+ }
+
+ my $id = $master_account
+ ? 'slave:'. $customer_id
+ : $login;
+ #my $id = $login;
+ my $status = $row->{status};
+
+ my $cancelled = 0;
+ if ( $status ne 'Current'
+ && $status ne 'current'
+ && $status ne 'On Hold' )
+ {
+ $cancelled = 1;
+ $cancel{$login} =
+ str2time($row->{termination_date});
+ }
+ $susp{$id} = str2time($row->{hold_date})
+ if ($status eq 'On Hold' && !$cancelled);
+ $adjo{$id} = str2time($row->{hold_date})
+ if ( $status eq 'Current' && !$cancelled &&
+ $row->{hold_date} );
+ $bill{$id} =
+ str2time($row->{expiration_date})
+ if (!$cancelled);
+
+ my $svcnum =
+ $object_map{legacy_ppp}{$row->{'login'} };
+ unless( $cancelled || $svcnum || $status eq 'Pn Hold' ) {
+ warn "can't find svc_acct for legacy ppp ".
+ $row->{'login'}, "\n";
+ }
+
+ $object_map{svc_acct}{$id} = $svcnum
+ unless $cancelled;
+
+ $master_map{$login} = $master_account
+ if $master_account;
+ return 1 if $master_account;
+ $cust_main->ship_country('US')
+ if $cust_main->has_ship_address;
+ ut_name_fixup($cust_main, 'first');
+ ut_name_fixup($cust_main, 'company');
+ ut_name_fixup($cust_main, 'last');
+
+ my ($info, $payby) = payinfo($row);
+ $cust_main->payby($payby);
+ $cust_main->payinfo($info);
+
+ $cust_main->paycvv(
+ $row->{'credit_card_cvv_number'}
+ )
+ if ($payby eq 'CARD' or $payby eq 'DCRD');
+
+ $cust_main->paydate('20'.
+ $row->{'credit_card_exp_date_2'}. '-'.
+ substr(
+ $row->{'credit_card_exp_date_1'},
+ 0,
+ 2,
+ ).
+ '-01'
+ )
+ if ($payby eq 'CARD' or $payby eq 'DCRD');
+
+ my $payname = '';
+ $payname = $row->{'credit_card_name'}
+ if ($payby eq 'CARD' or $payby eq 'DCRD');
+ $payname = $row->{'bank_name'}
+ if ($payby eq 'CHEK' or $payby eq 'DCHK');
+ $cust_main->payname($payname);
+
+ $cust_main->paytype(
+ $row->{'bank_account_to_debit'}
+ ? 'Personal '.
+ $row->{bank_account_to_debit}
+ : ''
+ )
+ if ($payby eq 'CHEK' or $payby eq 'DCHK');
+
+ $cust_main->payby('BILL')
+ if ($cust_main->payby eq 'CHEK' &&
+ $cust_main->payinfo !~ /^\d+\@\d{9}$/);
+ $cust_main->payby('BILL')
+ if ($cust_main->payby eq 'CARD' &&
+ $cust_main->payinfo =~ /^\s*$/);
+ $cust_main->paydate('2037-12-01')
+ if ($cust_main->payby eq 'BILL');
+ ut_text_fixup($cust_main, 'address1');
+ ut_state_fixup($cust_main, 'state');
+ ut_zip_fixup($cust_main, 'zip');
+
+
+ '';
+ },
+ 'skey' => sub { my $object = shift;
+ my $href = shift;
+ my $balance = sprintf("%.2f",
+ $href->{balance_due});
+ if ($balance < 0) {
+ my $cust_credit = new FS::cust_credit({
+ 'custnum' => $object->custnum,
+ 'amount' => sprintf("%.2f", -$balance),
+ 'reasonnum' => $previous_credit_reasonnum,
+ });
+ my $error = $cust_credit->insert;
+ warn "Error inserting credit for ",
+ $href->{'login'}, " : $error\n"
+ if $error;
+
+ }elsif($balance > 0) {
+ my $error = $object->charge(
+ $balance, "Prior balance",
+ );
+ warn "Error inserting balance charge for ",
+ $href->{'login'}, " : $error\n"
+ if $error;
+
+ }
+ $href->{'login'};
+ },
+ },
+#cust_main => { 'stable' => 'cust',
+# 'mapping' =>
+# { 'referred_by' => sub { my $href = shift;
+# my $u = shift->{'login'};
+# my $cn = $href->{'customer_number'};
+#
+# my $c = qsearch( 'cust_main',
+# { 'custnum' => $cn }
+# ) or die "can't fine customer $cn";
+#
+# my $s = qsearch( 'svc_acct',
+# { 'username' => $u }
+# ) or return '';
+#
+# my $n = $s->cust_svc
+# ->cust_pkg
+# ->cust_main
+# ->custnum;
+#
+# $c->referral_custnum($n);
+# my $error = $c->replace;
+# die "error setting referral: $error"
+# if $error;
+# '';
+# },
+# };
+# 'fixup' => sub { 1 },
+# },
+cust_pkg => { 'stable' => 'billcycle',
+ 'mapping' =>
+ { 'custnum' => sub { my $l = shift->{cbilling_cycle_login};
+ $l =~ /^\s*(\S[\S ]*?)\s*$/ && ($l = $1);
+ my $r = $object_map{'cust_main'}{$l};
+ unless ($r) {
+ my $m = $master_map{$l};
+ $r = $object_map{'cust_main'}{$m}
+ if $m;
+ }
+ $r;
+ },
+ 'pkgpart' => sub { my $href = shift;
+ my $p = $href->{product_id};
+ $p =~ /^\s*(\S[\S ]*?)\s*$/ && ($p = $1);
+ my $pkg = $package_cache{$p}
+ if $package_cache{$p};
+
+ my $month = '';
+ $href->{month_credit} =~ /\s*(\S[\S ]*?)\s*$/ && ($month = $1);
+ $month = int(eval "$month + 0");
+
+ my $price = 0;
+ $href->{unit_price} =~ /\s*(\S[\S ]*?)\s*$/ && ($price = $1);
+ $price = eval "$price + 0";
+
+ if ($pkg) {
+ $pkg = ''
+ unless $pkg->freq + 0 == $month;
+
+ if ($pkg && ($pkg->freq + 0)) {
+ my $recur = 0;
+ $pkg->recur_fee =~ /\s*(\S[\S ]*?)\s*$/ && ($recur = $1);
+ $recur = eval "$recur + 0";
+ $pkg = ''
+ unless $recur == $price;
+ }
+
+ if ($pkg) {
+ $pkg = ''
+ unless $pkg->setuptax
+ eq ($href->{taxable} ? '' : 'Y');
+ }
+
+ }
+
+ unless ($pkg) {
+ my $pkghref = { 'pkg' => ($href->{description} ? $href->{description} : $href->{product_id} ),
+ 'comment' => $href->{product_id},
+ 'freq' => $month,
+ 'setuptax' => ($href->{'taxable'} ? '' : 'Y'),
+ 'recurtax' => ($href->{'taxable'} ? '' : 'Y'),
+ 'plan' => 'flat',
+ 'disabled' => 'Y',
+ };
+
+ my @pkgs = qsearch('part_pkg', $pkghref);
+ my $recur = sprintf("%.2f", ($month ? $price : 0));
+ for (@pkgs) {
+ my %options = $_->options;
+ if ($options{recur_fee} eq $recur) {
+ $pkg = $_;
+ last;
+ }
+ }
+
+ $pkghref->{recur_fee} = $recur
+ unless $pkg;
+
+ my $pkg_svc = {};
+
+ if ($month){
+ $pkg_svc->{$legacy_ppp_svcpart} = 1;
+ $pkg_svc->{$legacy_email_svcpart} =
+ $href->{emails_allowed}
+ if $href->{emails_allowed};
+ }
+ $pkghref->{pkg_svc} = $pkg_svc;
+ $pkghref->{primary_svc}
+ = ( $month
+ ? $legacy_ppp_svcpart
+ : '');
+ unless ($pkg) {
+ $pkg = new FS::part_pkg $pkghref;
+ my $options =
+ { map { my $v = $pkg->$_;
+ $pkg->$_('');
+ ($_ => $v);
+ }
+ qw (setup_fee recur_fee)
+ };
+ my $error =
+ $pkg->insert(options=>$options);
+ if ($error) {
+ warn "Error inserting pkg ".
+ join(", ", map{"$_ => ". $pkg->get($_)} fields $pkg).
+ ": $error\n";
+ $pkg = '';
+ }
+ }
+ }
+ $pkg ? $pkg->pkgpart : '';
+ },
+ 'setup' => sub { str2time(shift->{creation_date}) },
+ 'bill' => sub { $bill{account_id(shift)}
+ #$bill{$href->{cbilling_cycle_login}};
+ },
+ 'susp' => sub { $susp{account_id(shift)}
+ #$susp{$href->{cbilling_cycle_login}};
+ },
+ 'adjo' => sub { $adjo{account_id(shift)}
+ #$adjo{$href->{cbilling_cycle_login}};
+ },
+ 'cancel' => sub { $cancel{account_id(shift)}
+ #$cancel{$href->{cbilling_cycle_login}};
+ },
+ },
+ 'fixup' => sub { my ($object, $row) = (shift,shift);
+ unless ($object->custnum) {
+ warn "can't find customer for ".
+ $row->{cbilling_cycle_login}. "\n";
+ return 1;
+ }
+ unless ($object->pkgpart) {
+ warn "can't find package for ".
+ $row->{product_id}. "\n";
+ return 1;
+ }
+ '';
+ },
+ 'skey' => sub { my $object = shift;
+ my $href = shift;
+ my $id = $href->{'billing_cycle_item_id'};
+ $id =~ /^\s*(\S[\S ]*?)\s*$/ && ($id = $1);
+ $cust_pkg_map{$id} = $object->pkgnum;
+ account_id($href);
+ },
+ 'wrapup' => sub { for my $id (keys %{$object_map{'cust_pkg'}}){
+ my $cust_svc =
+ qsearchs( 'cust_svc', { 'svcnum' =>
+ $object_map{'svc_acct'}{$id} }
+ );
+ unless ($cust_svc) {
+ warn "can't find legacy ppp $id\n";
+ next;
+ }
+ $cust_svc->
+ pkgnum($object_map{'cust_pkg'}{$id});
+ my $error = $cust_svc->replace;
+ warn "error linking legacy ppp $id: $error\n"
+ if $error;
+ }
+ },
+ },
+svc_acct => { 'stable' => 'email',
+ 'mapping' =>
+ { 'username' => 'email_name',
+ '_password' => 'password',
+ 'svcpart' => sub{ $legacy_email_svcpart },
+ 'domsvc' => sub{ $legacy_domain_svcnum },
+ },
+# 'fixup' => sub { my ($object, $row) = (shift,shift);
+# my ($sd,$sm,$sy) = split '/',
+# $row->{shut_off_date}
+# if $row->{shut_off_date};
+# if ($sd && $sm && $sy) {
+# my ($cd, $cm, $cy) = (localtime)[3,4,5];
+# $cy += 1900; $cm++;
+# return 1 if $sy < $cy;
+# return 1 if ($sy == $cy && $sm < $cm);
+# return 1 if ($sy == $cy && $sm == $cm && $sd <= $cd);
+# }
+# return 1 if $object_map{'cust_main'}{$object->username};
+# '';
+# },
+ 'fixup' => sub { my ($object, $row) = (shift,shift);
+ my ($sd,$sm,$sy) = split '/',
+ $row->{shut_off_date}
+ if $row->{shut_off_date};
+ if ($sd && $sm && $sy) {
+ my ($cd, $cm, $cy) = (localtime)[3,4,5];
+ $cy += 1900; $cm++;
+ return 1 if $sy < $cy;
+ return 1 if ($sy == $cy && $sm < $cm);
+ return 1 if ($sy == $cy && $sm == $cm && $sd <= $cd);
+ }
+ #return 1 if $object_map{'cust_main'}{$object->username};
+
+ my $email_name;
+ $row->{email_name} =~ /^\s*(\S[\S ]*?)\s*$/
+ && ($email_name = $1);
+
+ my $svcnum =
+ $object_map{legacy_email}{$email_name}
+ if $email_name;
+ unless( $svcnum ) {
+ warn "can't find svc_acct for legacy email ".
+ $row->{'email_name'}, "\n";
+ return 1;
+ }
+
+ $object_map{svc_acct}{'email:'.$row->{'email_customer_id'}} = $svcnum;
+ return 1;
+ },
+# 'skey' => sub { my $object = shift;
+# my $href = shift;
+# 'email:'. $href->{'email_customer_id'};
+# },
+ 'wrapup' => sub { for my $id (keys %{$object_map{'svc_acct'}}){
+ next unless $id =~ /^email:(\d+)/;
+ my $custid = $1;
+ my $cust_svc =
+ qsearchs( 'cust_svc', { 'svcnum' =>
+ $object_map{'svc_acct'}{$id} }
+ );
+ unless ($cust_svc) {
+ warn "can't find legacy email $id\n";
+ next;
+ }
+
+ if ($cust_svc->pkgnum) {
+ warn "service already linked for $id\n";
+ next;
+ }
+
+ $cust_svc->
+ pkgnum($cust_pkg_map{$custid});
+ if ($cust_svc->pkgnum){
+ my $error = $cust_svc->replace;
+ warn "error linking legacy email $id: $error\n"
+ if $error;
+ }else{
+ warn "can't find package for $id\n"
+ }
+ }
+ },
+ },
+);
+
+#my $s_dbh = DBI->connect($s_datasrc, $s_dbuser, $s_dbpass) or die $DBI::errstr;
+
+while ( @tables ) {
+ my ($table, $href) = (shift @tables, shift @tables);
+ my $stable = $href->{'stable'} or die "No source table"; # good enough for now
+ my (%mapping) = %{$href->{'mapping'}};
+ my ($fixup) = $href->{'fixup'};
+ my ($wrapup) = $href->{'wrapup'};
+ my ($id) = $href->{'id'};
+ my ($skey) = $href->{'skey'};
+
+ #$d_dbh->do("delete from $table");
+
+ my $s_sth = $s_dbh->prepare("select count(*) from $stable");
+ $s_sth->execute or die $s_sth->errstr;
+ my $rowcount = $s_sth->fetchrow_arrayref->[0];
+
+ $s_sth = $s_dbh->prepare("select * from $stable");
+ $s_sth->execute or die $s_sth->errstr;
+
+ my $row;
+ $count = 0;
+ while ( $row = $s_sth->fetchrow_hashref ) {
+ my $class = "FS::$table";
+
+ warn sprintf("%.2f", 100*$count/$rowcount). "% of $table processed\n"
+ unless( !$count || $count % 100 );
+
+ my $object = new $class ( {
+ map { $_ => ( ref($mapping{$_}) eq 'CODE'
+ ? &{$mapping{$_}}($row)
+ : $row->{$mapping{$_}}
+ )
+ }
+ keys(%mapping)
+ } );
+ my $skip = &{$fixup}($object, $row)
+ if $fixup;
+
+ unless ($skip) {
+ my $error = $object->insert;
+ if ($error) {
+ warn "Error inserting $table ".
+ join(", ", map{"$_ => ". $object->get($_)} fields $object).
+ ": $error\n";
+ next;
+ }
+ if ($skey) {
+ my $key = (ref($skey) eq 'CODE') ? &{$skey}($object, $row)
+ : $row->{$skey};
+ $object_map{$table}{$key} = $object->get($object->primary_key)
+ }
+ $count++;
+ }
+ }
+
+ &{$wrapup}()
+ if $wrapup;
+
+ print "$count/$rowcount of $table SUCCESSFULLY processed\n";
+
+}
+
+# link to any uncancelled package on customer
+foreach my $username ( keys %{$object_map{'legacy_email'}} ) {
+ my $cust_svc = qsearchs( 'cust_svc',
+ { 'svcnum' => $object_map{legacy_email}{$username} }
+ );
+ next unless $cust_svc;
+ next if $cust_svc->pkgnum;
+
+ my $custnum = $object_map{cust_main}{$username};
+ unless ($custnum) {
+ my $master = $master_map{$username};
+ $custnum = $object_map{'cust_main'}{$master}
+ if $master;
+ next unless $custnum;
+ }
+
+ #my $extra_sql = ' AND 0 != (select freq from part_pkg where '.
+ # 'cust_pkg.pkgpart = part_pkg.pkgpart )';
+ my $extra_sql = " AND 'Prior balance' != (select pkg from part_pkg where ".
+ "cust_pkg.pkgpart = part_pkg.pkgpart )";
+
+ my @cust_pkg = qsearch( {
+ 'table' => 'cust_pkg',
+ 'hashref' => { 'custnum' => $custnum,
+ 'cancel' => '',
+ },
+ 'extra_sql' => $extra_sql,
+ } );
+ next unless scalar(@cust_pkg);
+
+ $cust_svc->pkgnum($cust_pkg[0]->pkgnum);
+ $cust_svc->replace;
+}
+
+
+if ($dry_run) {
+ $d_dbh->rollback;
+}else{
+ $d_dbh->commit or die $d_dbh->errstr;
+}
+
diff --git a/bin/import-tax-rates b/bin/import-tax-rates
new file mode 100755
index 0000000..1cb76e0
--- /dev/null
+++ b/bin/import-tax-rates
@@ -0,0 +1,56 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw($opt_c $opt_p $opt_t $opt_d $opt_z $opt_f);
+use vars qw($DEBUG);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::tax_rate;
+use FS::cust_tax_location;
+
+getopts('c:p:t:d:z:f:');
+
+my $user = shift or die &usage;
+my $dbh = adminsuidsetup $user;
+
+my ($format) = $opt_f =~ /^([-\w]+)$/;
+
+my @list = (
+ 'CODE', $opt_c, \&FS::tax_class::batch_import,
+ 'PLUS4', $opt_p, \&FS::cust_tax_location::batch_import,
+ 'ZIP', $opt_z, \&FS::cust_tax_location::batch_import,
+ 'TXMATRIX', $opt_t, \&FS::part_pkg_taxrate::batch_import,
+ 'DETAIL', $opt_d, \&FS::tax_rate::batch_import,
+);
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+
+my $error = '';
+
+while(@list) {
+ my ($name, $file, $method) = splice(@list, 0, 3);
+
+ my $fh;
+
+ $file =~ /^([\s\d\w.]+)$/ or die "Illegal filename: $file\n";
+ $file = $1;
+
+ my $f = $format;
+ $f .= '-zip' if $name eq 'ZIP';
+
+ open $fh, '<', $file or die "can't open $name file: $!\n";
+ $error ||= &{$method}( { filehandle => $fh, 'format' => $f, } );
+
+ die "error while processing $file: $error" if $error;
+ close $fh;
+}
+
+if ($error) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+}else{
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+}
+
+sub usage { die "Usage:\nimport-tax-rates f FORMAT -c CODEFILE -p PLUS4FILE -z ZIPFILE -t TXMATRIXFILE -d DETAILFILE user\n\n"; }
diff --git a/bin/ispman.ldap.import b/bin/ispman.ldap.import
new file mode 100755
index 0000000..7495f47
--- /dev/null
+++ b/bin/ispman.ldap.import
@@ -0,0 +1,114 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Net::LDAP::LDIF;
+
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearchs);
+use FS::svc_domain;
+use FS::svc_acct;
+
+my $user = shift or die;
+adminsuidsetup($user);
+
+$FS::svc_Common::noexport_hack = 1;
+$FS::svc_domain::whois_hack = 1;
+
+my $domain_svcpart = 1;
+my $account_svcpart = 2;
+my $mailbox_svcpart = 3;
+my $fedweeknet_svcpart = 4;
+
+#my $ldif =
+# Net::LDAP::LDIF->new( "ispman-06-23-04.ldif", "r", onerror => 'undef' );
+my $ldif =
+ Net::LDAP::LDIF->new( "ispman-06-23-04.ldif", "r", onerror => 'warn' );
+
+#my %objectclass;
+
+my $acct = 0;
+my $imported = 0;
+
+my $entry;
+while ( $entry = $ldif->read_entry ) {
+ #warn "$entry\n";
+ my %attributes = map { $_ => [ $entry->get_value( $_ ) ] } $entry->attributes;
+
+ my $objectclass = join('/', @{$attributes{'objectclass'}} );
+
+ next unless $objectclass eq 'posixAccount/ispmanDomainUser/radiusprofile';
+
+ foreach my $attr ( keys %attributes ) {
+ print join( " => ", substr($attr.' 'x30,0,30), @{$attributes{ $attr }} ), "\n";
+ #if ( $attr eq 'objectclass' ) {
+ # $objectclass{ join('/', @{$attributes{$attr}} ) }++;
+ #}
+ }
+ print "\n";
+
+ $acct++;
+
+ my $email = $attributes{'maillocaladdress'}->[0];
+ $email =~ /^(\w+)\@([\w\.\-]+)$/ or die $email;
+ die "$1 ne ". $attributes{'ispmanuserid'}->[0]. "\n"
+ unless lc($1) eq $attributes{'ispmanuserid'}->[0];
+ my $username = lc($1);
+ my $domain = lc($2);
+
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
+ || new FS::svc_domain { 'svcpart' => $domain_svcpart,
+ 'domain' => $domain,
+ 'action' => 'N',
+ };
+
+ unless ( $svc_domain->svcnum ) {
+ my $error = $svc_domain->insert;
+ if ( $error ) {
+ die "inserting domain: $error\n";
+ }
+ }
+
+ ( my $password = $attributes{'userpassword'}->[0] ) =~ s/^\{crypt\}//;
+
+ # pick svcpart
+ my $svcpart = $account_svcpart;
+ if ( $domain eq 'fedweeknet.com' ) {
+ $svcpart = $fedweeknet_svcpart;
+ } elsif ( $attributes{'dialupaccess'}->[0] =~ /(false|no)/i ) {
+ $svcpart = $mailbox_svcpart;
+ }
+
+ my $dir = $attributes{'homedirectory'}->[0];
+ $dir =~ s/\s+//g;
+ $dir =~ s/\@/_/;
+
+ my $svc_acct = new FS::svc_acct {
+ 'svcpart' => $svcpart,
+ 'username' => $username,
+ '_password' => $password,
+ 'finger' => $attributes{'cn'}->[0],
+ 'domsvc' => $svc_domain->svcnum,
+ 'shell' => $attributes{'loginshell'}->[0],
+ 'uid' => $attributes{'uidnumber'}->[0],
+ 'gid' => $attributes{'gidnumber'}->[0],
+ 'dir' => $dir,
+ 'quota' => $attributes{'mailquota'}->[0],
+ };
+ my $error = $svc_acct->insert;
+ #my $error = $svc_acct->check;
+
+ if ( $error ) {
+ warn "$error\n";
+ } else {
+ $imported++;
+ }
+
+}
+
+print "$imported of $acct imported\n";
+
+#print "\n\n";
+
+#foreach ( sort { $objectclass{$b} <=> $objectclass{$a} } keys %objectclass ) {
+# print "$objectclass{$_}: $_\n";
+#}
diff --git a/bin/japan.pl b/bin/japan.pl
new file mode 100755
index 0000000..14e44e4
--- /dev/null
+++ b/bin/japan.pl
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+use FS::UID qw( adminsuidsetup );
+use FS::Record qw( qsearch );
+use FS::cust_main_county;
+
+adminsuidsetup shift;
+
+my $country = 'JP';
+
+foreach my $cust_main_county (
+ qsearch('cust_main_county', { 'country' => $country } )
+) {
+
+ if ( $cust_main_county->state =~ /\[([\w ]+)\]\s*$/ ) {
+ $cust_main_county->state($1);
+ my $error = $cust_main_county->replace;
+ die $error if $error;
+ }
+
+}
+
+
+#use Locale::SubCountry;
+#
+##my $state = 'Tôkyô [Tokyo]';
+#my $state = 'Tottori';
+#
+#my $lsc = new Locale::SubCountry 'JP';
+#
+#print $lsc->code($state)."\n";
+
diff --git a/bin/mapsecrets2access_user b/bin/mapsecrets2access_user
new file mode 100755
index 0000000..945f130
--- /dev/null
+++ b/bin/mapsecrets2access_user
@@ -0,0 +1,87 @@
+#!/usr/bin/perl -w
+
+use strict;
+use File::Copy "cp";
+use FS::UID qw(adminsuidsetup);
+use FS::CurrentUser;
+use FS::AccessRight;
+use FS::Record qw(qsearchs qsearch);
+use FS::access_group;
+use FS::access_user;
+use FS::access_usergroup;
+use FS::access_right;
+use FS::access_groupagent;
+use FS::agent;
+
+$FS::CurrentUser::upgrade_hack = 1;
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $supergroup = qsearchs('access_group', { 'groupname' => 'Superuser' } );
+unless ( $supergroup ) {
+
+ $supergroup = new FS::access_group { 'groupname' => 'Superuser' };
+ my $error = $supergroup->insert;
+ die $error if $error;
+
+ foreach my $rightname ( FS::AccessRight->rights ) {
+ my $access_right = new FS::access_right {
+ 'righttype' => 'FS::access_group',
+ 'rightobjnum' => $supergroup->groupnum,
+ 'rightname' => $rightname,
+ };
+ my $ar_error = $access_right->insert;
+ die $ar_error if $ar_error;
+ }
+
+ foreach my $agent ( qsearch('agent', {} ) ) {
+ my $access_groupagent = new FS::access_groupagent {
+ 'groupnum' => $supergroup->groupnum,
+ 'agentnum' => $agent->agentnum,
+ };
+ my $aga_error = $access_groupagent->insert;
+ die $aga_error if $aga_error;
+ }
+
+}
+my $supergroupnum = $supergroup->groupnum;
+
+my $conf = new FS::Conf;
+my $dir = $conf->base_dir;
+my $mapsecrets = "$dir/mapsecrets";
+open(MAPSECRETS, "<$mapsecrets") or die "Can't open $mapsecrets: $!";
+while (<MAPSECRETS>) {
+ /([\w]+)\s+secrets\s*$/ or die "unparsable line in mapsecrets: $_";
+ my $username = $1;
+
+ next if qsearchs('access_user', { 'username' => $username } );
+
+ my $access_user = new FS::access_user {
+ 'username' => $username,
+ '_password' => 'notyet',
+ 'first' => 'Legacy',
+ 'last' => 'User',
+ };
+ my $au_error = $access_user->insert;
+ die $au_error if $au_error;
+
+ my $access_usergroup = new FS::access_usergroup {
+ 'usernum' => $access_user->usernum,
+ 'groupnum' => $supergroupnum,
+ };
+ my $aug_error = $access_usergroup->insert;
+ die $aug_error if $aug_error;
+
+}
+close MAPSECRETS;
+
+# okay to clobber mapsecrets now i guess
+cp $mapsecrets, "$mapsecrets.bak$$";
+open(MAPSECRETS, ">$mapsecrets") or die $!;
+print MAPSECRETS '* secrets'. "\n";
+close MAPSECRETS or die $!;
+
+sub usage {
+ die "Usage:\n mapsecrets2access_user user\n";
+}
+
diff --git a/bin/masonize b/bin/masonize
new file mode 100755
index 0000000..509ef3e
--- /dev/null
+++ b/bin/masonize
@@ -0,0 +1,80 @@
+#!/usr/bin/perl
+
+foreach $file ( split(/\n/, `find . -depth -print`) ) {
+ next unless $file =~ /(cgi|html)$/;
+ open(F,$file) or die "can't open $file for reading: $!";
+ @file = <F>;
+ #print "$file ". scalar(@file). "\n";
+ close $file;
+ $newline = ''; #avoid prepending extraneous newlines
+ $all = join('',@file);
+
+ $w = '';
+
+ $mode = 'html';
+ while ( length($all) ) {
+
+ if ( $mode eq 'html' ) {
+
+ if ( $all =~ /^(.+?)(<%=?.*)$/s && $1 !~ /<%/s ) {
+ $w .= $1;
+ $all = $2;
+ next;
+ } elsif ( $all =~ /^<%=(.*)$/s ) {
+ $w .= '<%';
+ $all = $1;
+ $mode = 'perlv';
+ #die;
+ next;
+ } elsif ( $all =~ /^<%(.*)$/s ) {
+ $w .= $newline; $newline = "\n";
+ $all = $1;
+ $mode = 'perlc';
+
+ #avoid newline prepend fix from borking indented first <%
+ $w =~ s/\n\s+\z/\n/;
+ $w .= "\n" if $w =~ /.+\z/;
+
+ next;
+ } elsif ( $all !~ /<%/s ) {
+ $w .= $all;
+ last;
+ } else {
+ warn length($all); die;
+ }
+ die;
+
+ } elsif ( $mode eq 'perlv' ) {
+
+ if ( $all =~ /^(.*?%>)(.*)$/s ) {
+ $w .= $1;
+ $all=$2;
+ $mode = 'html';
+ next;
+ }
+ die "unterminated <%= ??? (in $file):";
+
+ } elsif ( $mode eq 'perlc' ) {
+
+ if ( $all =~ /^([^\n]*?)%>(.*)$/s ) {
+ $w .= "%$1\n";
+ $all=$2;
+ $mode='html';
+ next;
+ }
+ if ( $all =~ /^([^\n]*)\n(.*)$/s ) {
+ $w .= "%$1\n";
+ $all=$2;
+ next;
+ }
+
+ } else { die };
+
+ }
+
+ system("chmod u+w $file");
+ select W; $| = 1; select STDOUT;
+ open(W,">$file") or die "can't open $file for writing: $!";
+ print W $w;
+ close W;
+}
diff --git a/bin/passwd.import b/bin/passwd.import
new file mode 100755
index 0000000..8ab9e2a
--- /dev/null
+++ b/bin/passwd.import
@@ -0,0 +1,121 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw(%part_svc);
+use Date::Parse;
+use Term::Query qw(query);
+use Net::SCP qw(iscp);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_acct;
+use FS::part_svc;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+push @FS::svc_acct::shells, qw(/bin/sync /sbin/shutdown /bin/halt /sbin/halt); #others?
+
+my($spooldir)="/usr/local/etc/freeside/export.". datasrc;
+
+#$FS::svc_acct::nossh_hack = 1;
+$FS::svc_Common::noexport_hack = 1;
+
+###
+
+%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
+print "\n\n", &menu_svc, "\n", <<END;
+Enter part number to import.
+END
+my($shell_svcpart)=&getpart;
+
+print "\n\n", <<END;
+Enter the location and name of your _user_ passwd file, for example
+"mail.isp.com:/etc/passwd" or "nis.isp.com:/etc/global/passwd"
+END
+my($loc_passwd)=&getvalue(":");
+iscp("root\@$loc_passwd", "$spooldir/passwd.import");
+
+print "\n\n", <<END;
+Enter the location and name of your _user_ shadow file, for example
+"mail.isp.com:/etc/shadow" or "bsd.isp.com:/etc/master.passwd"
+END
+my($loc_shadow)=&getvalue(":");
+iscp("root\@$loc_shadow", "$spooldir/shadow.import");
+
+sub menu_svc {
+ ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
+}
+sub getpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
+}
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+###
+
+open(PASSWD,"<$spooldir/passwd.import");
+open(SHADOW,"<$spooldir/shadow.import");
+
+my(%password);
+while (<SHADOW>) {
+ chop;
+ my($username,$password)=split(/:/);
+ #$password =~ s/^\!$/\*/;
+ #$password =~ s/\!+/\*SUSPENDED\* /;
+ $password =~ s/^NP$/\*/;
+ $password =~ s/^\*LK\*$/\*/;
+ $password{$username}=$password;
+}
+
+while (<PASSWD>) {
+ chop;
+ my($username,$x,$uid,$gid,$finger,$dir,$shell) = split(/:/);
+ my $password = $password{$username};
+
+ my $svcpart = $shell_svcpart;
+
+ #if ( qsearchs('svc_acct', { 'username' => $username } ) ) {
+ # warn "warning: $username already exists; skipping\n";
+ # next;
+ #}
+
+ my($svc_acct) = new FS::svc_acct ({
+ 'svcpart' => $svcpart,
+ 'username' => $username,
+ '_password' => $password,
+ 'uid' => $uid,
+ 'gid' => $gid,
+ 'finger' => $finger,
+ 'dir' => $dir,
+ 'shell' => $shell,
+ #%{$allparam{$username}},
+ });
+ my($error);
+ $error=$svc_acct->insert;
+ if ( $error ) {
+ if ( $error =~ /duplicate/i ) {
+ warn "$username: $error";
+ } else {
+ die "$username: $error";
+ }
+ }
+
+}
+
+sub usage {
+ die "Usage:\n\n passwd.import user\n";
+}
+
diff --git a/bin/payment-faker b/bin/payment-faker
new file mode 100755
index 0000000..03316e1
--- /dev/null
+++ b/bin/payment-faker
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+
+use Date::Parse;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+use FS::cust_pay;
+use FS::cust_credit;
+
+my $user;
+$user = shift or die "usage: payment-faker $user";
+adminsuidsetup($user);
+
+for $month ( 1 .. 11 ) {
+
+ print "month $month\n";
+
+ system(qq!freeside-daily -d "$month/1/2006" $user!);
+
+ foreach my $cust_main ( qsearch('cust_main', {} ) ) {
+ next unless $cust_main->balance > 0;
+ my $item = '';
+ if ( rand() > .95 ) {
+ $item = new FS::cust_credit {
+ 'amount' => $cust_main->balance,
+ '_date' => str2time("$month/1/2006"),
+ 'reason' => 'testing',
+ };
+ } else {
+
+ if ( rand() > .5 ) {
+ $payby = 'BILL';
+ $payinfo = int(rand(10000));
+ } else {
+ $payby = 'CARD';
+ $payinfo = '4111111111111111';
+ }
+
+ $item = new FS::cust_pay {
+ 'paid' => $cust_main->balance,
+ '_date' => str2time("$month/1/2006"),
+ 'payby' => $payby,
+ 'payinfo' => $payinfo,
+ };
+ }
+
+ $item->custnum($cust_main->custnum);
+ my $error = $item->insert;
+ die $error if $error;
+ $cust_main->apply_payments;
+ $cust_main->apply_credits;
+
+ }
+
+}
diff --git a/bin/pg-readonly b/bin/pg-readonly
new file mode 100644
index 0000000..ad69fbd
--- /dev/null
+++ b/bin/pg-readonly
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+#
+# hack to update/add read-only permissions for a user on the db
+#
+# usage: pg-readonly freesideuser readonlyuser
+
+use strict;
+use DBI;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(dbdef);
+
+my $user = shift or die &usage;
+my $rouser = shift or die &usage;
+
+my $dbh = adminsuidsetup $user;
+
+foreach my $table ( dbdef->tables ) {
+ $dbh->do("GRANT SELECT ON $table TO $rouser");
+ $dbh->commit();
+ if ( my $pkey = dbdef->table($table)->primary_key ) {
+ $dbh->do("GRANT SELECT ON ${table}_${pkey}_seq TO $rouser");
+ $dbh->commit();
+ }
+}
diff --git a/bin/pg-version b/bin/pg-version
new file mode 100755
index 0000000..b6cddb6
--- /dev/null
+++ b/bin/pg-version
@@ -0,0 +1,13 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::UID qw(adminsuidsetup dbh);
+
+my $user = shift or die &usage;
+adminsuidsetup($user);
+
+print "pg_server_version: ". dbh->{'pg_server_version'}. "\n";
+
+sub usage {
+ "\n\nUsage: pg-version username\n";
+};
diff --git a/bin/pod2x b/bin/pod2x
index 1edb1c4..ecb7f91 100755
--- a/bin/pod2x
+++ b/bin/pod2x
@@ -1,23 +1,145 @@
-#!/usr/bin/perl
+#!/usr/bin/perl -w
-#use Pod::Text;
-#$Pod::Text::termcap=1;
+use strict;
-my $site_perl = "./site_perl";
-#my $catman = "./catman";
-my $catman = "./htdocs/docs/man";
-#my $html = "./htdocs/docs/man";
+my $mw_username = 'ivan';
+chomp( my $mw_password = `cat .mw-password` );
+
+my $site_perl = "./FS";
+#my $html = "Freeside:1.7:Documentation:Developer";
+my $html = "Freeside:1.9:Documentation:Developer";
+
+foreach my $dir (
+ $html,
+ map "$html/$_", qw( bin FS FS/UI FS/part_export FS/part_pkg
+ FS/part_event FS/part_event/Condition FS/part_event/Action
+ FS/ClientAPI FS/Cron FS/Misc FS/Report FS/Report/Table
+ FS/TicketSystem FS/UI
+ FS/SelfService
+ )
+) {
+ -d $dir or mkdir $dir;
+}
$|=1;
-die "Can't find $site_perl and $catman"
- unless [ -d $site_perl ] && [ -d $catman ] && [ -d $html ];
+die "Can't find $site_perl" unless -d $site_perl;
+#die "Can't find $catman" unless -d $catman;
+-d $html or mkdir $html;
+
+my $count = 0;
-foreach my $file (glob("$site_perl/*.pm")) {
- $file =~ /\/([\w\-]+)\.pm$/ or die "oops file $file";
- my $name = $1;
- print "$name\n";
- system "pod2text $file >$catman/$name.txt";
-# system "pod2html --podpath=$site_perl $file >$html/$name.html";
-# system "pod2html $file >$html/$name.html";
+#make some useless links
+foreach my $file (
+ glob("$site_perl/bin/freeside-*"),
+) {
+ next if $file =~ /\.pod$/;
+ #symlink $file, "$file.pod"; # or die "link $file to $file.pod: $!";
+ #system("cp $file $file.pod");
+ -e "$file.pod" or system("cp $file $file.pod");
}
+
+#just for filename_to_pagename for now
+use WWW::Mediawiki::Client;
+my $mvs = WWW::Mediawiki::Client->new(
+ 'host' => 'www.freeside.biz',
+ 'wiki_path' => 'mediawiki/index.php',
+ 'username' => $mw_username,
+ 'password' => $mw_password,
+ #'commit_message' => 'import from POD'
+ );
+#$mvs->do_login;
+
+use MediaWiki;
+
+my $c = MediaWiki->new;
+# $is_ok = $c->setup("config.ini");
+$c->setup({
+ 'bot' => { 'user' => $mw_username, 'pass' => $mw_password },
+ 'wiki' => {
+ 'host' => 'www.freeside.biz',
+ 'path' => 'mediawiki',
+ #'has_query' => 1,
+
+ }
+}) or die "Mediawiki->setup failed";
+
+my @files;
+if ( @ARGV ) {
+ @files = @ARGV;
+} else {
+ @files = (
+ glob("$site_perl/*.pm"),
+ glob("$site_perl/*/*.pm"),
+ glob("$site_perl/*/*/*.pm"),
+ glob("$site_perl/*/*/*/*.pm"),
+ glob("$site_perl/bin/*.pod"),
+ glob("./fs_selfservice/FS-SelfService/*.pm"),
+ glob("./fs_selfservice/FS-SelfService/*/*.pm"),
+ );
+
+}
+
+foreach my $file (@files) {
+ next if $file =~ /(^|\/)blib\//;
+ next if $file =~ /(^|\/)CVS\//;
+ #$file =~ /\/([\w\-]+)\.pm$/ or die "oops file $file";
+ my $name;
+ if ( $file =~ /fs_\w+\/FS\-\w+\/(.*)\.pm$/ ) {
+ $name = "FS/$1";
+ } elsif ( $file =~ /$site_perl\/(.*)\.(pm|pod)$/ ) {
+ $name = $1;
+ } else {
+ die "oops file $file";
+ }
+
+ #exit if $count++ == 10;
+
+ my $htmlroot = join('/', map '..',1..(scalar($file =~ tr/\///)-2)) || '.';
+
+ system "pod2wiki --style mediawiki $file >$html/$name.rawwiki";
+
+ if ( -e "$html/$name.rawwiki" ) {
+ print "processing $name\n";
+ } else {
+ print "skipping $name\n";
+ next;
+ };
+
+# $mvs->do_update("$html/$name.wiki");
+
+
+ my $text = '';
+ open(RAW, "<$html/$name.rawwiki") or die $!;
+ while (<RAW>) {
+ s/\[\[([^#p][^\]]*)\]\]/"[[$html\/". w_e($1). "|$1]]"/ge;
+ $text .= $_;
+ }
+ close RAW;
+
+ my $pagename = $mvs->filename_to_pagename("$html/$name.wiki");
+ #print " uploading to $pagename\n";
+
+ $c->text( $pagename, $text );
+
+}
+
+sub w_e {
+ my $s = shift;
+ $s =~ s/_/ /g;
+ $s =~ s/::/\//g;
+ $s =~ s/^freeside-/bin\/freeside-/g;
+ $s;
+}
+
+
+## system "pod2text $file >$catman/$name.txt";
+##
+# system "pod2html --podroot=$site_perl --podpath=./FS:./FS/UI:.:./bin --norecurse --htmlroot=$htmlroot $file >$html/$name.html";
+# #system "pod2html --podroot=$site_perl --htmlroot=$htmlroot $file >$html/$name.html";
+## system "pod2html $file >$html/$name.html";
+##
+
+#remove the useless links
+unlink glob("$site_perl/bin/*.pod");
+
diff --git a/bin/postfix.export b/bin/postfix.export
new file mode 100755
index 0000000..61380da
--- /dev/null
+++ b/bin/postfix.export
@@ -0,0 +1,122 @@
+#!/usr/bin/perl -w
+
+use strict;
+#use File::Path;
+use File::Rsync;
+use Net::SSH qw(ssh);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch); # qsearchs);
+use FS::part_export;
+#use FS::cust_pkg;
+use FS::cust_svc;
+#use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/postfix";
+mkdir $spooldir, 0700 unless -d $spooldir;
+
+my @exports = qsearch('part_export', { 'exporttype' => 'postfix' } );
+
+my $rsync = File::Rsync->new({
+ rsh => 'ssh',
+# dry_run => 1,
+});
+
+foreach my $export ( @exports ) {
+
+ my $machine = $export->machine;
+ my $prefix = "$spooldir/$machine";
+ mkdir $prefix, 0700 unless -d $prefix;
+
+ #construct %domain hash
+
+ my $mydomain = $export->option('mydomain');
+ my %domain;
+ foreach my $svc_forward ( $export->svc_x ) {
+
+ my( $username, $domain );
+ my $srcsvc_acct = $svc_forward->srcsvc_acct;
+ if ( $srcsvc_acct ) {
+ ( $username, $domain ) = ( $srcsvc_acct->username, $srcsvc_acct->domain );
+ } elsif ( $svc_forward->src =~ /^([^@]*)\@([^@]+)$/ ) {
+ ( $username, $domain ) = ( $1, $2 );
+ } else {
+ die "bad svc_forward record? svcnum ". $svc_forward->svcnum. "\n";
+ }
+
+ my( $dusername, $ddomain );
+ my $dstsvc_acct = $svc_forward->dstsvc_acct;
+ if ( $dstsvc_acct ) {
+ $dusername = $dstsvc_acct->username;
+ $ddomain = $dstsvc_acct->domain;
+ } elsif ( $svc_forward->dst =~ /([^@]+)\@([^@]+)$/ ) {
+ ( $dusername, $ddomain ) = ( $1, $2 );
+ } else {
+ die "bad svc_forward record? svcnum ". $svc_forward->svcnum. "\n";
+ }
+ my $dest;
+ if ( $ddomain eq $mydomain ) {
+ $dest = $dusername;
+ } else {
+ $dest = "$dusername\@$ddomain";
+ }
+
+ push @{$domain{$domain}{$username}}, $dest;
+
+ }
+
+ #write aliases
+
+ my $aliases = delete $domain{$mydomain};
+ open(ALIASES, ">$prefix/aliases") or die "can't open $prefix/aliases: $!";
+ foreach my $alias ( keys %$aliases ) {
+ print ALIASES "$alias: ". join(',', @{ $aliases->{$alias} } ). "\n";
+ }
+ close ALIASES;
+
+ #write virtual
+
+ open(VIRTUAL, ">$prefix/virtual") or die "can't open $prefix/virtual: $!";
+ foreach my $domain ( keys %domain ) {
+ print VIRTUAL "$domain DOMAIN\n";
+ #foreach my $virtual ( sort { $a ne '' <=> $b ne '' } keys %{$domain{$domain}} ) {
+ foreach my $virtual ( sort { ( ($b ne '') <=> ($a ne '') ) || $a cmp $b } keys %{$domain{$domain}} ) {
+ print VIRTUAL "$virtual\@$domain ".
+ join(',', @{ $domain{$domain}{$virtual} } ). "\n";
+ }
+ print VIRTUAL "\n";
+ }
+ close VIRTUAL;
+
+ #rsync
+
+ my $user = $export->option('user');
+ $rsync->exec( {
+ src => "$prefix/aliases",
+ dest => "$user\@$machine:". $export->option('aliases'),
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+# warn $rsync->out;
+
+ ssh("$user\@$machine", $export->option('newaliases') || 'newaliases');
+# ssh("$user\@$machine", "postfix reload");
+
+ $rsync->exec( {
+ src => "$prefix/virtual",
+ dest => "$user\@$machine:". $export->option('virtual'),
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+# warn $rsync->out;
+ ssh("$user\@$machine", $export->option('postmap')
+ || 'postmap hash:/etc/postfix/virtual');
+ ssh("$user\@$machine", $export->option('reload') || 'postfix reload');
+
+}
+
+# -----
+
+sub usage {
+ die "Usage:\n postfix.export user\n";
+}
+
+
diff --git a/bin/postfix_courierimap.import b/bin/postfix_courierimap.import
new file mode 100755
index 0000000..12c138b
--- /dev/null
+++ b/bin/postfix_courierimap.import
@@ -0,0 +1,137 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw(%part_svc %domain_part_svc);
+#use Date::Parse;
+use DBI;
+use Term::Query qw(query);
+use FS::UID qw(adminsuidsetup); #datasrc
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_acct;
+use FS::part_svc;
+use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#push @FS::svc_acct::shells, qw(/bin/sync /sbin/shutdown /bin/halt /sbin/halt); #others?
+
+$FS::svc_Common::noexport_hack = 1;
+$FS::svc_domain::whois_hack = 1;
+
+###
+
+%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
+print "\n\n", &menu_svc, "\n", <<END;
+Enter part number to import.
+END
+my $mailbox_svcpart = &getpart;
+
+%domain_part_svc = map { $_->svcpart, $_ }
+ qsearch('part_svc', { 'svcdb' => 'svc_domain'} );
+
+die "No services with svcdb svc_domain!\n" unless %domain_part_svc;
+
+print "\n\n", &menu_domain_svc, "\n", <<END;
+Enter part number for domains.
+END
+my $domain_svcpart = &getdomainpart;
+
+my $datasrc = &getvalue("\n\nEnter the DBI datasource:");
+my $db_user = &getvalue("\n\nEnter the database user:");
+my $db_pass = &getvalue("\n\nEnter the database password:");
+
+sub menu_svc {
+ ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
+}
+sub menu_domain_svc {
+ ( join "\n", map "$_: ".$domain_part_svc{$_}->svc, sort keys %domain_part_svc ). "\n";
+}
+sub getpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
+}
+sub getdomainpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %domain_part_svc ];
+ $^W=1;
+ $return;
+}
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+###
+
+my $dbh = DBI->connect( $datasrc, $db_user, $db_pass )
+ or die $DBI::errstr;
+
+my $sth = $dbh->prepare('SELECT username, password, crypt, name, domain FROM mailbox')
+ or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+
+my $row;
+while ( defined ( $row = $sth->fetchrow_arrayref ) ) {
+ my( $r_username, $password, $crypt, $finger, $r_domain ) = @$row;
+
+ my( $username, $domain );
+ if ( $r_username =~ /^([^@]+)\@([^@]+)$/ ) {
+ $username = $1;
+ $domain = $2;
+ } else {
+ $username = $r_username;
+ $domain = $r_domain;
+ }
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
+ || new FS::svc_domain {
+ 'domain' => $domain,
+ 'svcpart' => $domain_svcpart,
+ 'action' => 'N',
+ };
+ unless ( $svc_domain->svcnum ) {
+ my $error = $svc_domain->insert;
+ if ( $error ) {
+ die "can't insert domain $domain: $error\n";
+ }
+ }
+
+ $password = $crypt if $password eq '*CRYPTED*';
+
+ $finger =~ s/Outdoor Power.*$/Outdoor Power/;
+
+ my $svc_acct = new FS::svc_acct {
+ 'svcpart' => $mailbox_svcpart,
+ 'username' => $username,
+ 'domsvc' => $svc_domain->svcnum,
+ '_password' => $password,
+ 'finger' => $finger,
+ };
+
+ my $error = $svc_acct->insert;
+ #my $error = $svc_acct->check;
+ if ( $error ) {
+ if ( $error =~ /duplicate/i ) {
+ warn "$r_username / $r_domain: $error";
+ } else {
+ die "$r_username / $r_domain: $error";
+ }
+ }
+
+}
+
+sub usage {
+ die "Usage:\n\n postfix_courierimap.import user\n";
+}
+
+
diff --git a/bin/print-schema b/bin/print-schema
new file mode 100755
index 0000000..886e325
--- /dev/null
+++ b/bin/print-schema
@@ -0,0 +1,7 @@
+#!/usr/bin/perl
+
+use DBIx::DBSchema;
+
+$l = load DBIx::DBSchema "/usr/local/etc/freeside/dbdef.DBI:Pg:dbname=freeside";
+
+print $l->pretty_print, "\n";
diff --git a/bin/rate-us.import b/bin/rate-us.import
new file mode 100755
index 0000000..66ac5de
--- /dev/null
+++ b/bin/rate-us.import
@@ -0,0 +1,109 @@
+#!/usr/bin/perl -w
+
+use strict;
+#use Spreadsheet::ParseExcel;
+use DBI;
+use FS::UID qw(adminsuidsetup);
+use FS::rate_region;
+use FS::rate_prefix;
+use FS::rate_region;
+
+my $ratenum = 1;
+
+my $user = shift or usage();
+adminsuidsetup $user;
+
+sub usage {
+ #die "Usage:\n\n rate.import user rates.xls worksheet_name";
+ die "Usage:\n\n rate.import user";
+}
+
+my %rate_region;
+
+foreach my $file ( 'areas and rates US.xls',
+ 'areas and rates US2.xls',
+ 'areas and rates US3.xls',
+ )
+{
+
+ my $dbh = DBI->connect("DBI:Excel:file=$file")
+ or die "can't connect: $DBI::errstr";
+
+ #my $table = shift or usage();
+ my $table = 'Sheet1';
+ my $sth = $dbh->prepare("select * from $table")
+ or die "can't prepare: ". $dbh->errstr;
+ $sth->execute
+ or die "can't execute: ". $sth->errstr;
+
+ while ( my $row = $sth->fetchrow_hashref ) {
+
+ #print join(' - ', map $row->{$_}, qw( rate_center Code Area_Prefix Rate ) ). "\n";
+
+ my $regionname = $row->{'rate_center'};
+ $regionname =~ s/\xA0//g;
+ #$regionname =~ s/\xE9/e/g; #e with accent aigu
+ $regionname =~ s/(^\s+|\s+$)//;
+ $regionname .= ', USA';
+
+ my $prefix = $row->{'area_prefix'};
+ $prefix =~ s/\xA0//g;
+ $prefix =~ s/\s$//;
+ #my $prefixprefix = '';
+ #if ( $prefix =~ /^\s*(\d+)\s*\((.*)\)\s*$/ ) {
+ # $prefixprefix = $1;
+ # $prefix = $2;
+ #} elsif ( $prefix =~ /^\s*\((\d{3})\)\s*(.*)$/ ) {
+ # $prefixprefix = $1;
+ # $prefix = $2;
+ #}
+
+ my @rate_prefix = map {
+ #warn $row->{'rate_center'}. ": $prefixprefix$_\n";
+ new FS::rate_prefix {
+ 'countrycode' => '1', # $row->{'Country'}
+ #'npa' => $prefixprefix.$_,
+ 'npa' => $_,
+ };
+ }
+ split(/\s*[;,]\s*/, $prefix);
+
+
+ my $dest_detail = new FS::rate_detail {
+ 'ratenum' => $ratenum,
+ 'min_included' => 0,
+ 'min_charge' =>
+ sprintf('%.2f', $row->{'rate'} ),
+ 'sec_granularity' => 60,
+ };
+
+ unless ( exists $rate_region{$regionname} ) {
+
+ my $rate_region = new FS::rate_region {
+ 'regionname' => $regionname,
+ };
+
+ my $error = $rate_region->insert( 'rate_prefix' => \@rate_prefix,
+ 'dest_detail' => [ $dest_detail ],
+ );
+ die $error if $error;
+
+ $rate_region{$regionname} = $rate_region->regionnum;
+
+ } else {
+
+ foreach my $rate_prefix ( @rate_prefix ) {
+ $rate_prefix->regionnum($rate_region{$regionname});
+ my $error = $rate_prefix->insert;
+ die $error if $error;
+ }
+
+ #$rate_detail->dest_regionnum($rate_region{$regionname});
+ #$error = $rate_detail->insert;
+ #die $error if $error;
+
+ }
+
+ }
+
+}
diff --git a/bin/rate.delete b/bin/rate.delete
new file mode 100644
index 0000000..7b7e4bc
--- /dev/null
+++ b/bin/rate.delete
@@ -0,0 +1,3 @@
+#delete from rate_detail where ratenum = 18;
+#delete from rate_region where 0 = ( select count(*) from rate_detail where rate_region.regionnum = rate_detail.dest_regionnum );
+#delete from rate_prefix where 0 = ( select count(*) from rate_region where rate_prefix.regionnum = rate_region.regionnum );
diff --git a/bin/rate.import b/bin/rate.import
new file mode 100755
index 0000000..fdd756d
--- /dev/null
+++ b/bin/rate.import
@@ -0,0 +1,95 @@
+#!/usr/bin/perl
+
+use strict;
+#use Spreadsheet::ParseExcel;
+use DBI;
+use FS::UID qw(adminsuidsetup);
+use FS::rate_region;
+use FS::rate_prefix;
+use FS::rate_region;
+
+my $ratenum = 1;
+
+my $user = shift or usage();
+adminsuidsetup $user;
+
+#my $file = shift or usage();
+my $file = 'areas and rates.xls';
+my $dbh = DBI->connect("DBI:Excel:file=$file")
+ or die "can't connect: $DBI::errstr";
+
+#my $table = shift or usage();
+my $table = 'areas_and_rates';
+my $sth = $dbh->prepare("select * from $table")
+ or die "can't prepare: ". $dbh->errstr;
+$sth->execute
+ or die "can't execute: ". $sth->errstr;
+
+sub usage {
+ #die "Usage:\n\n rate.import user rates.xls worksheet_name";
+ die "Usage:\n\n rate.import user";
+}
+
+##
+
+while ( my $row = $sth->fetchrow_hashref ) {
+
+ #print join(' - ', map $row->{$_}, qw( Country Code Area_Prefix Rate ) ). "\n";
+
+ my $regionname = $row->{'Country'};
+ $regionname =~ s/\xA0//g;
+ $regionname =~ s/\xE9/e/g; #e with accent aigu
+ $regionname =~ s/(^\s+|\s+$)//;
+
+ #next if $regionname =~ /Sweden Telia Mobile/;
+
+ my $rate_region = new FS::rate_region {
+ 'regionname' => $regionname,
+ };
+
+ my $prefix = $row->{'Area_Prefix'};
+ $prefix =~ s/\xA0//g;
+ $prefix =~ s/\s$//;
+ my $prefixprefix = '';
+ if ( $prefix =~ /^\s*(\d+)\s*\((.*)\)\s*$/ ) {
+ $prefixprefix = $1;
+ $prefix = $2;
+ } elsif ( $prefix =~ /^\s*\((\d{3})\)\s*(.*)$/ ) {
+ $prefixprefix = $1;
+ $prefix = $2;
+ }
+
+ my @rate_prefix = ();
+ if ( $prefix =~ /\d/ ) {
+
+ @rate_prefix = map {
+ #warn $row->{'Country'}. ": $prefixprefix$_\n";
+ new FS::rate_prefix {
+ 'countrycode' => $row->{'Code'},
+ 'npa' => $prefixprefix.$_,
+ };
+ }
+ split(/\s*[;,]\s*/, $prefix);
+
+ } else {
+ @rate_prefix = ( new FS::rate_prefix {
+ 'countycode' => $row->{'Code'},
+ 'npa' => '',
+ };
+ );
+ }
+
+ my $dest_detail = new FS::rate_detail {
+ 'ratenum' => $ratenum,
+ 'min_included' => 0,
+ 'min_charge' =>
+ sprintf('%.2f', $row->{'Rate'} ),
+ 'sec_granularity' => 60,
+ };
+
+ my $error = $rate_region->insert( 'rate_prefix' => \@rate_prefix,
+ 'dest_detail' => [ $dest_detail ],
+ );
+ die $error if $error;
+
+}
diff --git a/bin/reset-cust_credit-otaker b/bin/reset-cust_credit-otaker
new file mode 100755
index 0000000..93002d0
--- /dev/null
+++ b/bin/reset-cust_credit-otaker
@@ -0,0 +1,88 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw($opt_d);
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::cust_credit;
+use FS::h_cust_credit;
+
+getopts('d:');
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+die &usage
+ unless ($opt_d);
+
+$FS::Record::nowarn_identical = 1;
+
+if ( $opt_d ) {
+ $opt_d =~ /^(\d+)$/ or die "invalid date";
+} else {
+ die "no date specified\n";
+}
+
+my @cust_credit = qsearch('cust_credit', { otaker => $user } );
+die "no credits found\n" unless @cust_credit;
+
+my $cust_credit = new FS::cust_credit;
+my @fields = grep { $_ !~ /^otaker|reason|reasonnum$/ } $cust_credit->fields;
+
+foreach my $cust_credit ( @cust_credit ) {
+ my %hash = $cust_credit->hash;
+ foreach (qw(otaker reason reasonnum)) {
+ delete $hash{$_};
+ }
+ $hash{'history_action'} = 'replace_old';
+ my $h_cust_credit =
+ qsearchs({ 'table' => 'h_cust_credit',
+ 'hashref' => \%hash,
+ 'select' => '*',
+ 'extra_sql' => " AND history_date <= $opt_d",
+ 'order_by' => 'ORDER BY history_date DESC LIMIT 1',
+ });
+ if ($h_cust_credit) {
+ $cust_credit->otaker($h_cust_credit->otaker);
+ my $reason = $h_cust_credit->getfield('reason');
+ if ($reason =~ /^\s*$/) {
+ $reason = '(none)';
+ }
+ $cust_credit->otaker($h_cust_credit->otaker);
+ $cust_credit->reason($reason);
+ my $error = $cust_credit->replace
+ if $cust_credit->modified;
+ die "error replacing cust_credit: $error\n"
+ if $error;
+ }else{
+ warn "Skipping credit.crednum ". $cust_credit->crednum;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n reset-cust_credit-otaker -d epoch_date user\n";
+}
+
+=head1 NAME
+
+reset-cust_credit-otaker - Command line tool to reset the otaker column for cust_credits to a previous value
+
+=head1 SYNOPSIS
+
+ reset-cust_credit-otaker -d epoch_date user
+
+=head1 DESCRIPTION
+
+ Sets the otaker column of the cust_credit records specified by user and
+ datespec to the value just prior to datespec.
+
+ The reasonnum of the cust_credit record is also set to reason record
+ which matches the reason specified in the history.
+
+=head1 SEE ALSO
+
+L<FS::cust_credit>, L<FS::h_cust_credit>;
+
+=cut
+
diff --git a/bin/rollback b/bin/rollback
new file mode 100755
index 0000000..7f83ef4
--- /dev/null
+++ b/bin/rollback
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs fields);
+
+use FS::svc_acct;
+
+#cust_pkg pkgnum 240133 241206 replace_old
+#cust_svc svcnum 31102 32083 delete
+#svc_acct svcnum 37162 37652 delete
+my($user, $table, $pkey, $start, $end, $action) = @ARGV;
+
+adminsuidsetup $user or die;
+
+#eval "use FS::h_$table;";
+#die $@ if $@;
+eval "use FS::$table;";
+die $@ if $@;
+
+my @history = grep { $_->historynum <= $end } qsearch("h_$table", { 'historynum' => { op=>'>=', value=>$start }, history_action => $action } );
+
+my %seen;
+foreach my $h (@history) {
+ my $error;
+ if ( $action eq 'replace_old' ) {
+ my $old = qsearchs($table, { $pkey => $h->get($pkey) } );
+ unless ( $old ) { die "can't find $table $pkey ". $h->get($pkey). "\n"; }
+ my $new = "FS::$table"->new( { map { $_ => $h->get($_) } fields($table) } );
+ $error = $new->replace($old);
+ } elsif ( $action eq 'delete' ) {
+ next if $seen{$h->get($pkey)}++;
+ my $new = "FS::$table"->new( { map { $_ => $h->get($_) } fields($table) } );
+ $error = $new->insert;
+ } else {
+ die "unknown action $action\n";
+ }
+ die $error if $error;
+}
diff --git a/bin/rotate-cdrs b/bin/rotate-cdrs
new file mode 100755
index 0000000..7bef0bb
--- /dev/null
+++ b/bin/rotate-cdrs
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Fcntl qw(:flock);
+use IO::File;
+
+my $dir = '/usr/local/etc/freeside/export/cdr';
+#chdir $dir;
+
+#XXX glob might not handle lots of args at some point...
+foreach my $file ( glob("$dir/*/CDR*-spool.CSV") ) {
+
+ $file =~ m{(\d+)/CDR(\d+)-spool.CSV$}
+ or die "guru meditation #54: can't parse filename: $file\n";
+ my($custnum, $date) = ($1, $2);
+
+
+ my $alpha = 'A';
+ while ( -e "$dir/$custnum/CDR$date$alpha.CSV" ) {
+ $alpha++; # A -> Z -> AA etc.
+ }
+ my $newfile = "$dir/$custnum/CDR$date$alpha.CSV";
+
+ rename $file, $newfile
+ or die "$! moving $file to $newfile\n";
+
+ use IO::File;
+ my $lock = new IO::File ">>$newfile"
+ or die "can't open $newfile: $!\n";
+ sleep 1; #just in case. i guess there's still a *remotely* possible
+ #race condition, but i'm not losing any sleep over it... (rimshot)
+ flock($lock, LOCK_EX)
+ or die "can't lock $newfile: $!\n";
+ #okay we've got the lock, any pending write should be done...
+
+ print "$custnum: $newfile\n";
+
+}
diff --git a/bin/rt-drop-tables b/bin/rt-drop-tables
new file mode 100755
index 0000000..b027542
--- /dev/null
+++ b/bin/rt-drop-tables
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+my @tables = qw(
+Attachments
+Queues
+Links
+Principals
+Groups
+ScripConditions
+Transactions
+Scrips
+ACL
+GroupMembers
+CachedGroupMembers
+Users
+Tickets
+ScripActions
+Templates
+TicketCustomFieldValues
+CustomFields
+CustomFieldValues
+sessions
+);
+
+foreach my $table ( @tables ) {
+ print "drop table $table;\n";
+ print "drop sequence ${table}_id_seq;\n";
+}
+
diff --git a/bin/rt-update-links b/bin/rt-update-links
new file mode 100644
index 0000000..75d554f
--- /dev/null
+++ b/bin/rt-update-links
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+
+use FS::UID qw(adminsuidsetup);
+
+my( $olddb, $newdb ) = ( shift, shift );
+
+$FS::CurrentUser::upgrade_hack = 1;
+my $dbh = adminsuidsetup;
+
+my $statement = "select * from links where base like 'fsck.com-rt://$olddb/%' OR target like 'fsck.com-rt://$olddb/%'";
+
+my $sth = $dbh->prepare($statement) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+
+while ( my $row = $sth->fetchrow_hashref ) {
+
+ ( my $base = $row->{'base'} )
+ =~ s(^fsck\.com-rt://$olddb/)(fsck.com-rt://$newdb/);
+
+ ( my $target = $row->{'target'} )
+ =~ s(^fsck\.com-rt://$olddb/)(fsck.com-rt://$newdb/);
+
+ if ( $row->{'base'} ne $base || $row->{'target'} ne $target ) {
+
+ my $update = 'UPDATE links SET base = ?, target = ? where id = ?';
+ my @param = ( $base, $target, $row->{'id'} );
+
+ warn "$update : ". join(', ', @param). "\n";
+ $dbh->do($update, {}, @param );
+
+ }
+
+}
+
+$dbh->commit;
+
diff --git a/bin/sendmail.import b/bin/sendmail.import
new file mode 100644
index 0000000..ef745fc
--- /dev/null
+++ b/bin/sendmail.import
@@ -0,0 +1,178 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Term::Query qw(query);
+use Net::SCP qw(iscp);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+##use FS::svc_acct_sm;
+#use FS::svc_domain;
+#use FS::domain_record;
+use FS::svc_acct;
+##use FS::part_svc;
+use FS::svc_forward;
+use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#$FS::svc_Common::noexport_hack = 1;
+#$FS::domain_record::noserial_hack = 1;
+
+use vars qw($defaultdomain);
+$defaultdomain = '295.ca';
+
+use vars qw(@svcpart $forward_svcpart);
+@svcpart = qw( 2 4 );
+$forward_svcpart = 7;
+
+use vars qw($spooldir);
+$spooldir = "/usr/local/etc/freeside/export.". datasrc. "/sendmail";
+mkdir($spooldir, 0755) unless -d $spooldir;
+
+print "\n\n", <<END;
+Enter the location and name of your Sendmail aliases file, for example
+"mail.isp.com:/etc/mail/aliases"
+END
+my($aliases)=&getvalue(":");
+
+use vars qw($aliases_machine $aliases_prefix);
+$aliases_machine = (split(/:/, $aliases))[0];
+$aliases_prefix = "$spooldir/$aliases_machine";
+mkdir($aliases_prefix, 0755) unless -d $aliases_prefix;
+
+#iscp("root\@$aliases","$aliases_prefix/aliases.import");
+iscp("ivan\@$aliases","$aliases_prefix/aliases.import");
+
+print "\n\n", <<END;
+Enter the location and name of your Sendmail virtusertable directory, for example
+"mail.isp.com:/etc/mail/virtusertable"
+END
+my($virtusertable)=&getvalue(":");
+
+use vars qw($virtusertable_machine $virtusertable_prefix);
+$virtusertable_machine = (split(/:/, $virtusertable))[0];
+$virtusertable_prefix = "$spooldir/$virtusertable_machine";
+mkdir($virtusertable_prefix, 0755) unless -d $virtusertable_prefix;
+mkdir("$virtusertable_prefix/virtusertable.import", 0755)
+ unless -d "$virtusertable_prefix/virtusertable.import";
+
+#iscp("root\@$virtusertable/*","$aliases_prefix/virtusertable.import/");
+iscp("ivan\@$virtusertable/*","$aliases_prefix/virtusertable.import/");
+
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+##
+
+foreach my $file (
+ "$aliases_prefix/aliases.import",
+ glob("$aliases_prefix/virtusertable.import/*"),
+) {
+
+ warn "importing $file\n";
+
+ open(FILE,"<$file") or die $!;
+ while (<FILE>) {
+ next if /^\s*#/ || /^\s*$/; #skip comments & blank lines
+
+ unless ( /^([\w\@\.\-]+)[:\s]\s*(.*\S)\s*$/ ) {
+ warn "Unparsable line: $_";
+ next;
+ }
+ my($rawusername, $rawdest) = ($1, $2);
+
+ my($username, $domain);
+ if ( $rawusername =~ /^([\w\-\.\&]*)\@([\w\.\-]+)$/ ) {
+ $username = $1;
+ $domain = $2;
+ } elsif ( $rawusername =~ /\@/ ) {
+ die "Unparsable username: $rawusername\n";
+ } else {
+ $username = $rawusername;
+ $domain = $defaultdomain;
+ }
+
+ #find svc_acct record or set $src
+ my($srcsvc, $src) = &svcnum_or_literal($username, $domain);
+
+ foreach my $dest ( split(/,/, $rawdest) ) {
+
+ my($dusername, $ddomain);
+ if ( $dest =~ /^([\w\-\.\&]+)\@([\w\.\-]+)$/ ) {
+ $dusername = $1;
+ $ddomain = $2;
+ } elsif ( $dest =~ /\@/ ) {
+ die "Unparsable username: $dest\n";
+ } else {
+ $dusername = $dest;
+ $ddomain = $defaultdomain;
+ }
+ my($dstsvc, $dst) = &svcnum_or_literal($dusername, $ddomain);
+
+ my $svc_forward = new FS::svc_forward ({
+ svcpart => $forward_svcpart,
+ srcsvc => $srcsvc,
+ src => $src,
+ dstsvc => $dstsvc,
+ dst => $dst,
+ });
+ my $error = $svc_forward->insert;
+ #my $error = $svc_forward->check;
+ if ( $error ) {
+ die "$rawusername: $rawdest: $error\n";
+ }
+ }
+
+
+ } #next entry
+
+} #next file
+
+##
+
+sub svcnum_or_literal {
+ my($username, $domain) = @_;
+
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } );
+ my $domsvc = $svc_domain ? $svc_domain->svcnum : '';
+
+ my @svc_acct = grep { my $svc_acct = $_;
+ grep { $svc_acct->cust_svc->svcpart == $_ } @svcpart
+ }
+ qsearch('svc_acct', {
+ 'username' => $username,
+ 'domsvc' => $domsvc,
+ });
+
+ if ( scalar(@svc_acct) > 1 ) {
+ die "multiple sources found for $username\@$domain !\n";
+ }
+
+ my( $svcnum, $literal ) = ('', '');
+ if ( @svc_acct ) {
+ my $svc_acct = $svc_acct[0];
+ $svcnum = $svc_acct->svcnum;
+ } else {
+ $literal = "$username\@$domain";
+ }
+
+ return( $svcnum, $literal );
+
+}
+
+sub usage {
+ die "Usage:\n\n sendmail.import user\n";
+}
+
+
+
+
+
diff --git a/bin/sequences.reset b/bin/sequences.reset
new file mode 100644
index 0000000..2dc1d3b
--- /dev/null
+++ b/bin/sequences.reset
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(dbdef dbh);
+
+my $user = shift;
+adminsuidsetup $user or die;
+
+foreach my $table ( dbdef->tables ) {
+ my $primary_key = dbdef->table($table)->primary_key;
+ next unless $primary_key;
+ #my $local = dbdef->table($table)->column($primary_key)->local;
+ ##next unless $default =~ /nextval/;
+ #print "$local\n";
+
+ my $statement = "select setval('${table}_${primary_key}_seq', ( select max($primary_key) from $table ) )";
+
+ print "$statement;\n";
+ next;
+
+ my $sth = dbh->prepare($statement) or do {
+ warn dbh->errstr. " preparing $statement\n";
+ next;
+ };
+ $sth->execute or do {
+ warn dbh->errstr. " executing $statement\n";
+ dbh->commit;
+ next;
+ }
+
+}
+
diff --git a/bin/shadow.reimport b/bin/shadow.reimport
new file mode 100755
index 0000000..7957011
--- /dev/null
+++ b/bin/shadow.reimport
@@ -0,0 +1,125 @@
+#!/usr/bin/perl -w
+#
+# -d: dry-run: make no changes
+# -r: replace: overwrite existing passwords (otherwise only "*" passwords will
+# be changed)
+# -b: blowfish replace: overwrite existing passwords only if they are
+# blowfish-encrypted
+
+use strict;
+use vars qw(%part_svc);
+use Getopt::Std;
+use Term::Query qw(query);
+use Net::SCP qw(iscp);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_acct;
+use FS::part_svc;
+
+use vars qw($opt_d $opt_r $opt_b);
+getopts("drb");
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+push @FS::svc_acct::shells, qw(/bin/sync /sbin/shutdown /bin/halt /sbin/halt); #others?
+
+my($spooldir)="/usr/local/etc/freeside/export.". datasrc;
+
+#$FS::svc_acct::nossh_hack = 1;
+$FS::svc_Common::noexport_hack = 1;
+
+###
+
+%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
+print "\n\n", &menu_svc, "\n", <<END;
+Enter part number or part numbers to import.
+END
+my($shell_svcpart)=&getvalue;
+my @shell_svcpart = split(/[,\s]+/, $shell_svcpart);
+
+print "\n\n", <<END;
+Enter the location and name of your _user_ shadow file, for example
+"mail.isp.com:/etc/shadow" or "bsd.isp.com:/etc/master.passwd"
+END
+my($loc_shadow)=&getvalue(":");
+iscp("root\@$loc_shadow", "$spooldir/shadow.import");
+
+sub menu_svc {
+ ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
+}
+sub getpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
+}
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+###
+
+open(SHADOW,"<$spooldir/shadow.import");
+
+my($line, $updated);
+while (<SHADOW>) {
+ $line++;
+ chop;
+ my($username,$password)=split(/:/);
+
+# my @svc_acct = grep { $_->cust_svc->svcpart == $shell_svcpart }
+# qsearch('svc_acct', { 'username' => $username } );
+ my @svc_acct = grep {
+ my $svcpart = $_->cust_svc->svcpart;
+ grep { $_ == $svcpart } @shell_svcpart;
+ } qsearch('svc_acct', { 'username' => $username } );
+
+ next unless @svc_acct;
+
+ if ( scalar(@svc_acct) > 1 ) {
+ die "more than one $username found!\n";
+ next;
+ }
+
+ my $svc_acct = shift @svc_acct;
+
+ next unless $svc_acct->_password eq '*'
+ || $opt_r
+ || ( $opt_b && $svc_acct->_password =~ /^\$2a?\$/ );
+
+ next if $svc_acct->username eq 'root';
+
+ next if $password eq 'NP' || $password eq '*LK*';
+
+ next if $svc_acct->_password eq $password;
+ next if $svc_acct->_password =~ /^\*SUSPENDED\*/;
+
+ my $new_svc_acct = new FS::svc_acct( { $svc_acct->hash } );
+ $new_svc_acct->_password($password);
+ #warn "$username: ". $svc_acct->_password. " -> $password\n";
+ warn "changing password for $username\n";
+ unless ( $opt_d ) {
+ my $error = $new_svc_acct->replace($svc_acct);
+ die "$username: $error" if $error;
+ }
+
+ $updated++;
+
+}
+
+warn "$updated of $line passwords changed\n";
+
+sub usage {
+ die "Usage:\n\n shadow.reimport [ -d ] [ -r ] user\n";
+}
+
diff --git a/bin/slony-setup b/bin/slony-setup
new file mode 100755
index 0000000..0798c1a
--- /dev/null
+++ b/bin/slony-setup
@@ -0,0 +1,109 @@
+#!/usr/bin/perl
+#
+# slony replication setup
+#
+# usage: slony-setup freesideuser
+
+use strict;
+use DBI;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(dbdef);
+
+my $user = shift or die "usage: slony-setup username\n";
+adminsuidsetup($user);
+
+#---
+
+my $MASTERHOST = '192.168.20.10';
+my $SLAVEHOST = '192.168.20.50';
+#my $REPLICATIONUSER='pgsql';
+my $REPLICATIONUSER='postgres';
+
+#--------
+
+print <<END;
+
+#on slave:
+useradd freeside
+cp -pr /etc/skel /home/freeside
+chown -R freeside /home/freeside
+
+su postgres -c 'createuser freeside' #n y n
+su freeside -c 'createdb freeside'
+
+#on master:
+su postgres -c 'createlang plpgsql freeside'
+
+pg_dump -s -U $REPLICATIONUSER -h $MASTERHOST freeside | psql -U $REPLICATIONUSER -h $SLAVEHOST freeside
+
+END
+
+#--------
+
+#drop set ( id = 1, origin = 1);
+
+print <<END;
+#on master:
+slonik <<_EOF_
+
+cluster name = freeside;
+node 1 admin conninfo = 'dbname=freeside host=$MASTERHOST user=$REPLICATIONUSER';
+node 2 admin conninfo = 'dbname=freeside host=$SLAVEHOST user=$REPLICATIONUSER';
+init cluster ( id=1, comment = 'Master Node');
+
+create set (id=1, origin=1, comment='All freeside tables');
+
+END
+
+my $id = 1;
+
+foreach my $table ( dbdef->tables ) {
+ #next if $table =~ /^sql_/i;
+ print "set add table (set id=1, origin=1, id=". $id++. ", fully qualified name = 'public.$table' );\n";
+
+}
+
+print <<END;
+
+store node (id=2, comment = 'Slave node');
+store path (server = 1, client = 2, conninfo='dbname=freeside host=$MASTERHOST user=$REPLICATIONUSER');
+store path (server = 2, client = 1, conninfo='dbname=freeside host=$SLAVEHOST user=$REPLICATIONUSER');
+store listen (origin=1, provider = 1, receiver =2);
+store listen (origin=2, provider = 2, receiver =1);
+
+_EOF_
+END
+
+print <<END;
+
+### start slon processes (both machines) (this is debian-specific)
+mkdir /etc/slony1/freeside
+
+cat >/etc/slony1/freeside/slon.conf <<_EOF_
+# Set the cluster name that this instance of slon is running against
+# default is to read it off the command line
+cluster_name='freeside'
+
+# Set slon's connection info, default is to read it off the command line
+conn_info='host=localhost port=5432 dbname=freeside user=postgres'
+_EOF_
+
+/etc/init.d/slony1 start
+
+END
+
+
+print <<END;
+#on master:
+slonik <<_EOF_
+
+cluster name = freeside;
+
+node 1 admin conninfo = 'dbname=freeside host=$MASTERHOST user=$REPLICATIONUSER';
+node 2 admin conninfo = 'dbname=freeside host=$SLAVEHOST user=$REPLICATIONUSER';
+
+subscribe set ( id = 1, provider = 1, receiver = 2, forward = no);
+
+_EOF_
+END
+
diff --git a/bin/sqlradius-norealm.reimport b/bin/sqlradius-norealm.reimport
new file mode 100755
index 0000000..b7d0166
--- /dev/null
+++ b/bin/sqlradius-norealm.reimport
@@ -0,0 +1,113 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw(%part_svc);
+#use Date::Parse;
+use DBI;
+use Term::Query qw(query);
+use FS::UID qw(adminsuidsetup); #datasrc
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_acct;
+use FS::part_svc;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#push @FS::svc_acct::shells, qw(/bin/sync /sbin/shutdown /bin/halt /sbin/halt); #others?
+
+$FS::svc_Common::noexport_hack = 1;
+
+###
+
+%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
+print "\n\n", &menu_svc, "\n", <<END;
+Enter part number to import.
+END
+my $sqlradius_svcpart = &getpart;
+
+my $datasrc = &getvalue("\n\nEnter the DBI datasource:");
+my $db_user = &getvalue("\n\nEnter the database user:");
+my $db_pass = &getvalue("\n\nEnter the database password:");
+
+sub menu_svc {
+ ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
+}
+sub getpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
+}
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+###
+
+my $dbh = DBI->connect( $datasrc, $db_user, $db_pass )
+ or die $DBI::errstr;
+
+my $sth = $dbh->prepare('SELECT DISTINCT UserName FROM radcheck')
+ or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+
+my $row;
+while ( defined ( $row = $sth->fetchrow_arrayref ) ) {
+ my( $username ) = @$row;
+
+ my( $password, $group ) = ( '', '', '' );
+
+ my $rc_sth = $dbh->prepare(
+ 'SELECT Attribute, Value'.
+ ' FROM radcheck'.
+ ' WHERE UserName = ?'
+ ) or die $dbh->errstr;
+ $rc_sth->execute($username) or die $rc_sth->errstr;
+
+ foreach my $rc_row ( @{$rc_sth->fetchall_arrayref} ) {
+ my($attribute, $value) = @$rc_row;
+ if ( $attribute =~ /^((Crypt|User)-)?Password$/ ) {
+ $password = $value unless $password && !$1;
+ } else {
+ #handle other params!
+ }
+ }
+
+ my @svc_acct = grep { $_->cust_svc->svcpart == $sqlradius_svcpart }
+ qsearch('svc_acct', { 'username' => $username, } );
+
+ #print "$r_username / $realm: $password / $finger: ";
+ print "$username: $password: ";
+ if ( scalar(@svc_acct) == 0 ) {
+ print "not found\n";
+ next;
+ } elsif ( scalar(@svc_acct) > 1 ) {
+ print "multiple matches found?!?!\n";
+ next;
+ } else {
+ #print "correcting password and name\n";
+ print "correcting password\n";
+ }
+
+ my $svc_acct = $svc_acct[0];
+ #my $new = new FS::svc_acct { $svc_acct->hash, '_password' => $password, 'finger' => $finger };
+ my $new = new FS::svc_acct { $svc_acct->hash, '_password' => $password };
+ my $error = $new->replace($svc_acct);
+ #my $error = $new->check;
+ die "$username: $error" if $error;
+
+}
+
+sub usage {
+ die "Usage:\n\n sqlradius-norealm.reimport user\n";
+}
+
diff --git a/bin/sqlradius.import b/bin/sqlradius.import
new file mode 100644
index 0000000..e75f65b
--- /dev/null
+++ b/bin/sqlradius.import
@@ -0,0 +1,152 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw(%part_svc %domain_part_svc);
+#use Date::Parse;
+use DBI;
+use Term::Query qw(query);
+use FS::UID qw(adminsuidsetup); #datasrc
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_acct;
+use FS::part_svc;
+use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#push @FS::svc_acct::shells, qw(/bin/sync /sbin/shutdown /bin/halt /sbin/halt); #others?
+
+$FS::svc_Common::noexport_hack = 1;
+$FS::svc_domain::whois_hack = 1;
+
+###
+
+%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
+print "\n\n", &menu_svc, "\n", <<END;
+Enter part number to import.
+END
+my $sqlradius_svcpart = &getpart;
+
+%domain_part_svc = map { $_->svcpart, $_ }
+ qsearch('part_svc', { 'svcdb' => 'svc_domain'} );
+
+die "No services with svcdb svc_domain!\n" unless %domain_part_svc;
+
+print "\n\n", &menu_domain_svc, "\n", <<END;
+Enter part number for domains.
+END
+my $domain_svcpart = &getdomainpart;
+
+my $datasrc = &getvalue("\n\nEnter the DBI datasource:");
+my $db_user = &getvalue("\n\nEnter the database user:");
+my $db_pass = &getvalue("\n\nEnter the database password:");
+
+sub menu_svc {
+ ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
+}
+sub menu_domain_svc {
+ ( join "\n", map "$_: ".$domain_part_svc{$_}->svc, sort keys %domain_part_svc ). "\n";
+}
+sub getpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
+}
+sub getdomainpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %domain_part_svc ];
+ $^W=1;
+ $return;
+}
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+###
+
+my $dbh = DBI->connect( $datasrc, $db_user, $db_pass )
+ or die $DBI::errstr;
+
+my $sth = $dbh->prepare('SELECT DISTINCT UserName, Realm FROM radcheck')
+ or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+
+my $row;
+while ( defined ( $row = $sth->fetchrow_arrayref ) ) {
+ my( $r_username, $realm ) = @$row;
+
+ my( $username, $domain );
+ if ( $r_username =~ /^([^@]+)\@([^@]+)$/ ) {
+ $username = $1;
+ $domain = $2;
+ } else {
+ $username = $r_username;
+ $domain = $realm;
+ }
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
+ || new FS::svc_domain {
+ 'domain' => $domain,
+ 'svcpart' => $domain_svcpart,
+ 'action' => 'N',
+ };
+ unless ( $svc_domain->svcnum ) {
+ my $error = $svc_domain->insert;
+ if ( $error ) {
+ die "can't insert domain $domain: $error\n";
+ }
+ }
+
+ my( $password, $finger, $group ) = ( '', '', '' );
+
+ my $rc_sth = $dbh->prepare(
+ 'SELECT Attribute, Value, Name, GroupName'.
+ ' FROM radcheck'.
+ ' WHERE UserName = ? and Realm = ?'
+ ) or die $dbh->errstr;
+ $rc_sth->execute($r_username, $realm) or die $rc_sth->errstr;
+
+ foreach my $rc_row ( @{$rc_sth->fetchall_arrayref} ) {
+ my($attribute, $value, $name, $groupname) = @$rc_row;
+ if ( $attribute =~ /^((User|Crypt)-)?Password$/ ) {
+ $password = $value;
+ $finger = $name;
+ $group = $groupname;
+ } else {
+ #handle other params!
+ }
+ }
+
+ my $svc_acct = new FS::svc_acct {
+ 'svcpart' => $sqlradius_svcpart,
+ 'username' => $username,
+ 'domsvc' => $svc_domain->svcnum,
+ '_password' => $password,
+ 'finger' => $finger,
+ };
+
+ my $error = $svc_acct->insert;
+ #my $error = $svc_acct->check;
+ if ( $error ) {
+ if ( $error =~ /duplicate/i ) {
+ warn "$r_username / $realm: $error";
+ } else {
+ die "$r_username / $realm: $error";
+ }
+ }
+
+}
+
+sub usage {
+ die "Usage:\n\n sqlradius.import user\n";
+}
+
diff --git a/bin/sqlradius.reimport b/bin/sqlradius.reimport
new file mode 100755
index 0000000..2218a3f
--- /dev/null
+++ b/bin/sqlradius.reimport
@@ -0,0 +1,160 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw(%part_svc %domain_part_svc);
+#use Date::Parse;
+use DBI;
+use Term::Query qw(query);
+use FS::UID qw(adminsuidsetup); #datasrc
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_acct;
+use FS::part_svc;
+use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#push @FS::svc_acct::shells, qw(/bin/sync /sbin/shutdown /bin/halt /sbin/halt); #others?
+
+$FS::svc_Common::noexport_hack = 1;
+$FS::svc_domain::whois_hack = 1;
+
+###
+
+%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
+print "\n\n", &menu_svc, "\n", <<END;
+Enter part number to import.
+END
+my $sqlradius_svcpart = &getpart;
+
+%domain_part_svc = map { $_->svcpart, $_ }
+ qsearch('part_svc', { 'svcdb' => 'svc_domain'} );
+
+die "No services with svcdb svc_domain!\n" unless %domain_part_svc;
+
+print "\n\n", &menu_domain_svc, "\n", <<END;
+Enter part number for domains.
+END
+my $domain_svcpart = &getdomainpart;
+
+my $datasrc = &getvalue("\n\nEnter the DBI datasource:");
+my $db_user = &getvalue("\n\nEnter the database user:");
+my $db_pass = &getvalue("\n\nEnter the database password:");
+
+sub menu_svc {
+ ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
+}
+sub menu_domain_svc {
+ ( join "\n", map "$_: ".$domain_part_svc{$_}->svc, sort keys %domain_part_svc ). "\n";
+}
+sub getpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
+}
+sub getdomainpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %domain_part_svc ];
+ $^W=1;
+ $return;
+}
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+###
+
+my $dbh = DBI->connect( $datasrc, $db_user, $db_pass )
+ or die $DBI::errstr;
+
+my $sth = $dbh->prepare('SELECT DISTINCT UserName, Realm FROM radcheck')
+ or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+
+my $row;
+while ( defined ( $row = $sth->fetchrow_arrayref ) ) {
+ my( $r_username, $realm ) = @$row;
+
+ my( $username, $domain );
+ if ( $r_username =~ /^([^@]+)\@([^@]+)$/ ) {
+ $username = $1;
+ $domain = $2;
+ } else {
+ $username = $r_username;
+ $domain = $realm;
+ }
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
+ || new FS::svc_domain {
+ 'domain' => $domain,
+ 'svcpart' => $domain_svcpart,
+ 'action' => 'N',
+ };
+ unless ( $svc_domain->svcnum ) {
+ die "new domain? wtf";
+ my $error = $svc_domain->insert;
+ if ( $error ) {
+ die "can't insert domain $domain: $error\n";
+ }
+ }
+
+ #my( $password, $finger, $group ) = ( '', '', '' );
+ my( $password, $group ) = ( '', '', '' );
+
+ my $rc_sth = $dbh->prepare(
+ 'SELECT Attribute, Value, Name, GroupName'.
+ ' FROM radcheck'.
+ ' WHERE UserName = ? and Realm = ?'
+ ) or die $dbh->errstr;
+ $rc_sth->execute($r_username, $realm) or die $rc_sth->errstr;
+
+ foreach my $rc_row ( @{$rc_sth->fetchall_arrayref} ) {
+ my($attribute, $value, $name, $groupname) = @$rc_row;
+ if ( $attribute =~ /^((Crypt|User)-)?Password$/ ) {
+ $password = $value;
+ #$finger = $name;
+ $group = $groupname;
+ } else {
+ #handle other params!
+ }
+ }
+
+ my @svc_acct = grep { $_->cust_svc->svcpart == $sqlradius_svcpart }
+ qsearch('svc_acct', { 'username' => $username,
+ 'domsvc' => $svc_domain->svcnum, } );
+
+ #print "$r_username / $realm: $password / $finger: ";
+ print "$r_username / $realm: $password: ";
+ if ( scalar(@svc_acct) == 0 ) {
+ print "not found\n";
+ next;
+ } elsif ( scalar(@svc_acct) > 1 ) {
+ print "multiple matches found?!?!\n";
+ next;
+ } else {
+ #print "correcting password and name\n";
+ print "correcting password\n";
+ }
+
+ my $svc_acct = $svc_acct[0];
+ #my $new = new FS::svc_acct { $svc_acct->hash, '_password' => $password, 'finger' => $finger };
+ my $new = new FS::svc_acct { $svc_acct->hash, '_password' => $password };
+ my $error = $new->replace($svc_acct);
+ #my $error = $new->check;
+ die "$r_username / $realm: $error" if $error;
+
+}
+
+sub usage {
+ die "Usage:\n\n sqlradius.reimport user\n";
+}
+
diff --git a/bin/strip-eps b/bin/strip-eps
new file mode 100755
index 0000000..2c2d124
--- /dev/null
+++ b/bin/strip-eps
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+
+# Author: Andy Turner <andrew.turner@acadia.net>
+
+use strict;
+
+# The first line has some binary magic for file identification
+# purposes. GhostScript doesn't like it. Strip it.
+scalar <>;
+
+# Add a header so that we can use magic to determine the file type.
+print "%!PS-Adobe-3.0 EPSF-3.0\n";
+
+while (<>) {
+ print;
+
+ # Illustrator Version 7 format EPS files have a bunch of binary gook
+ # after the "%%EOF" line. (% is a comment in PostScript, right?)
+ last if /^%%EOF/;
+}
diff --git a/bin/svc_acct.export b/bin/svc_acct.export
deleted file mode 100755
index 3f65a08..0000000
--- a/bin/svc_acct.export
+++ /dev/null
@@ -1,351 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# Create and export password files: passwd, passwd.adjunct, shadow,
-# acp_passwd, acp_userinfo, acp_dialup, users
-#
-# ivan@voicenet.com late august/september 96
-# (the password encryption bits were from melody)
-#
-# use a temporary copy of svc_acct to minimize lock time on the real file,
-# and skip blank entries.
-#
-# ivan@voicenet.com 96-Oct-6
-#
-# change users / acp_dialup file formats
-# ivan@voicenet.com 97-jan-28-31
-#
-# change priority (after copies) to 19, not 10
-# ivan@voicenet.com 97-feb-5
-#
-# added exit if stuff is already locked 97-apr-15
-#
-# rewrite ivan@sisd.com 98-mar-9
-#
-# Changed 'password' to '_password' because Pg6.3 reserves this word
-# Added code to create a FreeBSD style master.passwd file
-# bmccane@maxbaud.net 98-Apr-3
-#
-# don't export non-root 0 UID's, even if they get put in the database
-# ivan@sisd.com 98-jul-14
-#
-# Uses Idle_Timeout, Port_Limit, Framed_Netmask and Framed_Route if they
-# exist; need some way to support arbitrary radius fields. also
-# /var/spool/freeside/conf/ ivan@sisd.com 98-jul-26, aug-9
-#
-# OOPS! added arbitrary radius fields (pry 98-aug-16) but forgot to say so.
-# ivan@sisd.com 98-sep-18
-
-use strict;
-use Fcntl qw(:flock);
-use FS::SSH qw(scp ssh);
-use FS::UID qw(adminsuidsetup);
-use FS::Record qw(qsearch fields);
-
-my($fshellmachines)="/var/spool/freeside/conf/shellmachines";
-my(@shellmachines);
-if ( -e $fshellmachines ) {
- open(SHELLMACHINES,$fshellmachines);
- @shellmachines=map {
- /^(.*)$/ or die "Illegal line in conf/shellmachines"; #we trust the file
- $1;
- } grep $_ !~ /^(#|$)/, <SHELLMACHINES>;
- close SHELLMACHINES;
-}
-
-my($fbsdshellmachines)="/var/spool/freeside/conf/bsdshellmachines";
-my(@bsdshellmachines);
-if ( -e $fbsdshellmachines ) {
- open(BSDSHELLMACHINES,$fbsdshellmachines);
- @bsdshellmachines=map {
- /^(.*)$/ or die "Illegal line in conf/bsdshellmachines"; #we trust the file
- $1;
- } grep $_ !~ /^(#|$)/, <BSDSHELLMACHINES>;
- close BSDSHELLMACHINES;
-}
-
-my($fnismachines)="/var/spool/freeside/conf/nismachines";
-my(@nismachines);
-if ( -e $fnismachines ) {
- open(NISMACHINES,$fnismachines);
- @nismachines=map {
- /^(.*)$/ or die "Illegal line in conf/nismachines"; #we trust the file
- $1;
- } grep $_ !~ /^(#|$)/, <NISMACHINES>;
- close NISMACHINES;
-}
-
-my($ferpcdmachines)="/var/spool/freeside/conf/erpcdmachines";
-my(@erpcdmachines);
-if ( -e $ferpcdmachines ) {
- open(ERPCDMACHINES,$ferpcdmachines);
- @erpcdmachines=map {
- /^(.*)$/ or die "Illegal line in conf/erpcdmachines"; #we trust the file
- $1;
- } grep $_ !~ /^(#|$)/, <ERPCDMACHINES>;
- close ERPCDMACHINES;
-}
-
-my($fradiusmachines)="/var/spool/freeside/conf/radiusmachines";
-my(@radiusmachines);
-if ( -e $fradiusmachines ) {
- open(RADIUSMACHINES,$fradiusmachines);
- @radiusmachines=map {
- /^(.*)$/ or die "Illegal line in conf/radiusmachines"; #we trust the file
- $1;
- } grep $_ !~ /^(#|$)/, <RADIUSMACHINES>;
- close RADIUSMACHINES;
-}
-
-my($spooldir)="/var/spool/freeside/export";
-my($spoollock)="/var/spool/freeside/svc_acct.export.lock";
-
-adminsuidsetup;
-
-my(@saltset)= ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
-srand(time|$$);
-
-open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!";
-select(EXPORT); $|=1; select(STDOUT);
-unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) {
- seek(EXPORT,0,0);
- my($pid)=<EXPORT>;
- chop($pid);
- #no reason to start loct of blocking processes
- die "Is another export process running under pid $pid?\n";
-}
-seek(EXPORT,0,0);
-print EXPORT $$,"\n";
-
-my(@svc_acct)=qsearch('svc_acct',{});
-
-( open(MASTER,">$spooldir/master.passwd")
- and flock(MASTER,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/master.passwd: $!";
-( open(PASSWD,">$spooldir/passwd")
- and flock(PASSWD,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/passwd: $!";
-( open(SHADOW,">$spooldir/shadow")
- and flock(SHADOW,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/shadow: $!";
-( open(ACP_PASSWD,">$spooldir/acp_passwd")
- and flock (ACP_PASSWD,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/acp_passwd: $!";
-( open (ACP_DIALUP,">$spooldir/acp_dialup")
- and flock(ACP_DIALUP,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/acp_dialup: $!";
-( open (USERS,">$spooldir/users")
- and flock(USERS,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/users: $!";
-
-chmod 0644, "$spooldir/passwd",
- "$spooldir/acp_dialup",
-;
-chmod 0600, "$spooldir/master.passwd",
- "$spooldir/acp_passwd",
- "$spooldir/shadow",
- "$spooldir/users",
-;
-
-setpriority(0,0,10);
-
-my($svc_acct);
-foreach $svc_acct (@svc_acct) {
-
- my($password)=$svc_acct->getfield('_password');
- my($cpassword,$rpassword);
- if ( ( length($password) <= 8 )
- && ( $password ne '*' )
- && ( $password ne '' )
- ) {
- $cpassword=crypt($password,
- $saltset[int(rand(64))].$saltset[int(rand(64))]
- );
- $rpassword=$password;
- } else {
- $cpassword=$password;
- $rpassword='UNIX';
- }
-
- if ( $svc_acct->uid =~ /^(\d+)$/ ) {
-
- die "Non-root user ". $svc_acct->username. " has 0 UID!"
- if $svc_acct->uid == 0 && $svc_acct->username ne 'root';
-
- ###
- # FORMAT OF FreeBSD MASTER PASSWD FILE HERE
- print MASTER join(":",
- $svc_acct->username, # User name
- $cpassword, # Encrypted password
- $svc_acct->uid, # User ID
- $svc_acct->gid, # Group ID
- "", # Login Class
- "0", # Password Change Time
- "0", # Password Expiration Time
- $svc_acct->finger, # Users name
- $svc_acct->dir, # Users home directory
- $svc_acct->shell, # shell
- ), "\n" ;
-
- ###
- # FORMAT OF THE PASSWD FILE HERE
- print PASSWD join(":",
- $svc_acct->username,
- 'x', # "##". $svc_acct->$username,
- $svc_acct->uid,
- $svc_acct->gid,
- $svc_acct->finger,
- $svc_acct->dir,
- $svc_acct->shell,
- ), "\n";
-
- ###
- # FORMAT OF THE SHADOW FILE HERE
- print SHADOW join(":",
- $svc_acct->username,
- $cpassword,
- '',
- '',
- '',
- '',
- '',
- '',
- '',
- ), "\n";
-
- }
-
- if ( $svc_acct->slipip ne '' ) {
-
- ###
- # FORMAT OF THE ACP_* FILES HERE
- print ACP_PASSWD join(":",
- $svc_acct->username,
- $cpassword,
- "0",
- "0",
- "",
- "",
- "",
- ), "\n";
-
- my($ip)=$svc_acct->slipip;
-
- unless ( $ip eq '0.0.0.0' || $svc_acct->slipip eq '0e0' ) {
- print ACP_DIALUP $svc_acct->username, "\t*\t", $svc_acct->slipip, "\n";
- }
-
- ###
- # FORMAT OF THE USERS FILE HERE
- print USERS
- $svc_acct->username, qq(\tPassword = "$rpassword"\n\t),
-
- join ",\n\t",
- map {
- /^(radius_(.*))$/;
- my($field,$attrib)=($1,$2);
- $attrib =~ s/_/\-/g;
- "$attrib = \"". $svc_acct->getfield($field). "\"";
- } grep /^radius_/ && $svc_acct->getfield($_), fields('svc_acct')
- ;
- if ( $ip && $ip ne '0e0' ) {
- print USERS qq(,\n\tFramed-Address = "$ip"\n\n);
- } else {
- print USERS qq(\n\n);
- }
-
- }
-
-}
-
-flock(MASTER,LOCK_UN);
-flock(PASSWD,LOCK_UN);
-flock(SHADOW,LOCK_UN);
-flock(ACP_DIALUP,LOCK_UN);
-flock(ACP_PASSWD,LOCK_UN);
-flock(USERS,LOCK_UN);
-
-close MASTER;
-close PASSWD;
-close SHADOW;
-close ACP_DIALUP;
-close ACP_PASSWD;
-close USERS;
-
-###
-# export stuff
-#
-
-my($shellmachine);
-foreach $shellmachine (@shellmachines) {
- scp("$spooldir/passwd","root\@$shellmachine:/etc/passwd.new")
- == 0 or die "scp error: $!";
- scp("$spooldir/shadow","root\@$shellmachine:/etc/shadow.new")
- == 0 or die "scp error: $!";
- ssh("root\@$shellmachine",
- "( ".
- "mv /etc/passwd.new /etc/passwd; ".
- "mv /etc/shadow.new /etc/shadow; ".
- " )"
- )
- == 0 or die "ssh error: $!";
-}
-
-my($bsdshellmachine);
-foreach $bsdshellmachine (@bsdshellmachines) {
- scp("$spooldir/passwd","root\@$bsdshellmachine:/etc/passwd.new")
- == 0 or die "scp error: $!";
- scp("$spooldir/master.passwd","root\@$bsdshellmachine:/etc/master.passwd.new")
- == 0 or die "scp error: $!";
- ssh("root\@$bsdshellmachine",
- "( ".
- "mv /etc/passwd.new /etc/passwd; ".
- "mv /etc/master.passwd.new /etc/master.passwd; ".
- " )"
- )
- == 0 or die "ssh error: $!";
-}
-
-my($nismachine);
-foreach $nismachine (@nismachines) {
- scp("$spooldir/passwd","root\@$nismachine:/etc/global/passwd")
- == 0 or die "scp error: $!";
- scp("$spooldir/shadow","root\@$nismachine:/etc/global/shadow")
- == 0 or die "scp error: $!";
- ssh("root\@$nismachine",
- "( ".
- "cd /var/yp; make; ".
- " )"
- )
- == 0 or die "ssh error: $!";
-}
-
-my($erpcdmachine);
-foreach $erpcdmachine (@erpcdmachines) {
- scp("$spooldir/acp_passwd","root\@$erpcdmachine:/usr/annex/acp_passwd")
- == 0 or die "scp error: $!";
- scp("$spooldir/acp_dialup","root\@$erpcdmachine:/usr/annex/acp_dialup")
- == 0 or die "scp error: $!";
- ssh("root\@$erpcdmachine",
- "( ".
- "kill -USR1 \`cat /usr/annex/erpcd.pid\'".
- " )"
- )
- == 0 or die "ssh error: $!";
-}
-
-my($radiusmachine);
-foreach $radiusmachine (@radiusmachines) {
- scp("$spooldir/users","root\@$radiusmachine:/etc/raddb/users")
- == 0 or die "scp error: $!";
- ssh("root\@$erpcdmachine",
- "( ".
- "builddbm".
- " )"
- )
- == 0 or die "ssh error: $!";
-}
-
-unlink $spoollock;
-flock(EXPORT,LOCK_UN);
-close EXPORT;
-
diff --git a/bin/svc_acct.import b/bin/svc_acct.import
index c4b8c5e..aff26b9 100755
--- a/bin/svc_acct.import
+++ b/bin/svc_acct.import
@@ -1,31 +1,21 @@
#!/usr/bin/perl -Tw
-#
-# ivan@sisd.com 98-mar-9
-#
-# changed 'password' field to '_password' because PgSQL 6.3 reserves this word
-# bmccane@maxbaud.net 98-Apr-3
-#
-# generalized svcparts (still needs radius import) ivan@sisd.com 98-mar-23
-#
-# radius import, now an interactive script. still needs erpcd import?
-# ivan@sisd.com 98-jun-24
-#
-# arbitrary radius attributes ivan@sisd.com 98-aug-9
-#
-# don't import /var/spool/freeside/conf/shells! ivan@sisd.com 98-aug-13
use strict;
use vars qw(%part_svc);
use Date::Parse;
-use FS::SSH qw(iscp);
-use FS::UID qw(adminsuidsetup);
+use Term::Query qw(query);
+use Net::SCP qw(iscp);
+use FS::UID qw(adminsuidsetup datasrc);
use FS::Record qw(qsearch);
use FS::svc_acct;
+use FS::part_svc;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
-adminsuidsetup;
+push @FS::svc_acct::shells, qw(/bin/sync /sbin/shuddown /bin/halt); #others?
-#my($spooldir)="/var/spool/freeside/export";
-my($spooldir)="unix/";
+my($spooldir)="/usr/local/etc/freeside/export.". datasrc;
$FS::svc_acct::nossh_hack = 1;
@@ -33,6 +23,8 @@ $FS::svc_acct::nossh_hack = 1;
%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
print "\n\n", &menu_svc, "\n", <<END;
Most accounts probably have entries in passwd and users (with Port-Limit
nonexistant or 1).
@@ -58,8 +50,7 @@ my($oisdn_svcpart)=&getpart;
print "\n\n", &menu_svc, "\n", <<END;
POP mail accounts have entries in passwd only, and have a particular shell.
END
-print "Enter that shell: ";
-my($pop_shell)=&getvalue;
+my($pop_shell)=&getvalue("Enter that shell:");
my($popmail_svcpart)=&getpart;
print "\n\n", &menu_svc, "\n", <<END;
@@ -71,37 +62,38 @@ print "\n\n", <<END;
Enter the location and name of your _user_ passwd file, for example
"mail.isp.com:/etc/passwd" or "nis.isp.com:/etc/global/passwd"
END
-print ":";
-my($loc_passwd)=&getvalue;
+my($loc_passwd)=&getvalue(":");
iscp("root\@$loc_passwd", "$spooldir/passwd.import");
print "\n\n", <<END;
Enter the location and name of your _user_ shadow file, for example
"mail.isp.com:/etc/shadow" or "bsd.isp.com:/etc/master.passwd"
END
-print ":";
-my($loc_shadow)=&getvalue;
+my($loc_shadow)=&getvalue(":");
iscp("root\@$loc_shadow", "$spooldir/shadow.import");
print "\n\n", <<END;
Enter the location and name of your radius "users" file, for example
"radius.isp.com:/etc/raddb/users"
END
-print ":";
-my($loc_users)=&getvalue;
+my($loc_users)=&getvalue(":");
iscp("root\@$loc_users", "$spooldir/users.import");
sub menu_svc {
( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
}
sub getpart {
- print "Enter part number, or 0 for none: ";
- &getvalue;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
}
sub getvalue {
- my($x)=scalar(<STDIN>);
- chop $x;
- $x;
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
}
print "\n\n";
@@ -116,12 +108,14 @@ my(%upassword,%ip,%allparam);
my(%param,$username);
while (<USERS>) {
chop;
- next if /^$/;
+ next if /^\s*$/;
+ next if /^\s*#/;
if ( /^\S/ ) {
- /^(\w+)\s+Password\s+=\s+"([^"]+)"(,\s+Expiration\s+=\s+"([^"]*")\s*)?$/
+ /^(\w+)\s+(Auth-Type\s+=\s+Local,\s+)?Password\s+=\s+"([^"]+)"(,\s+Expiration\s+=\s+"([^"]*")\s*)?$/
or die "1Unexpected line in users.import: $_";
my($password,$expiration);
- ($username,$password,$expiration)=(lc($1),$2,$4);
+ ($username,$password,$expiration)=(lc($1),$3,$5);
+ $password = '' if $password eq 'UNIX';
$upassword{$username}=$password;
undef %param;
} else {
@@ -130,8 +124,12 @@ while (<USERS>) {
while (<USERS>) {
chop;
if ( /^\s*$/ ) {
- $ip{$username}=$param{'radius_Framed_IP_Address'}||'0e0';
- delete $param{'radius_Framed_IP_Address'};
+ if ( defined $param{'radius_Framed_IP_Address'} ) {
+ $ip{$username} = $param{'radius_Framed_IP_Address'};
+ delete $param{'radius_Framed_IP_Address'};
+ } else {
+ $ip{$username} = '0e0';
+ }
$allparam{$username}={ %param };
last;
} elsif ( /^\s+([\w\-]+)\s=\s"?([\w\.\-\s]+)"?,?\s*$/ ) {
@@ -144,14 +142,20 @@ while (<USERS>) {
}
}
#? incase there isn't a terminating blank line ?
-$ip{$username}=$param{'radius_Framed_IP_Address'}||'0e0';
-delete $param{'radius_Framed_IP_Address'};
+if ( defined $param{'radius_Framed_IP_Address'} ) {
+ $ip{$username} = $param{'radius_Framed_IP_Address'};
+ delete $param{'radius_Framed_IP_Address'};
+} else {
+ $ip{$username} = '0e0';
+}
$allparam{$username}={ %param };
my(%password);
while (<SHADOW>) {
chop;
my($username,$password)=split(/:/);
+ #$password =~ s/^\!$/\*/;
+ #$password =~ s/\!+/\*SUSPENDED\* /;
$password{$username}=$password;
}
@@ -176,16 +180,16 @@ while (<PASSWD>) {
$svcpart = $shell_svcpart;
}
- my($svc_acct) = create FS::svc_acct ({
- 'svcpart' => $svcpart,
- 'username' => $username,
- 'password' => $password,
- 'uid' => $uid,
- 'gid' => $gid,
- 'finger' => $finger,
- 'dir' => $dir,
- 'shell' => $shell,
- 'slipip' => $ip{$username},
+ my($svc_acct) = new FS::svc_acct ({
+ 'svcpart' => $svcpart,
+ 'username' => $username,
+ '_password' => $password,
+ 'uid' => $uid,
+ 'gid' => $gid,
+ 'finger' => $finger,
+ 'dir' => $dir,
+ 'shell' => $shell,
+ 'slipip' => $ip{$username},
%{$allparam{$username}},
});
my($error);
@@ -210,11 +214,11 @@ foreach $username ( keys %upassword ) {
die "Illegal Port-Limit in users!\n";
}
- my($svc_acct) = create FS::svc_acct ({
- 'svcpart' => $svcpart,
- 'username' => $username,
- 'password' => $password,
- 'slipip' => $ip{$username},
+ my($svc_acct) = new FS::svc_acct ({
+ 'svcpart' => $svcpart,
+ 'username' => $username,
+ '_password' => $password,
+ 'slipip' => $ip{$username},
%{$allparam{$username}},
});
my($error);
@@ -225,3 +229,9 @@ foreach $username ( keys %upassword ) {
delete $upassword{$username};
}
+#
+
+sub usage {
+ die "Usage:\n\n svc_acct.import user\n";
+}
+
diff --git a/bin/svc_acct_pop.import b/bin/svc_acct_pop.import
new file mode 100755
index 0000000..9e3d38b
--- /dev/null
+++ b/bin/svc_acct_pop.import
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+use strict;
+use Text::CSV_XS;
+use FS::UID qw(adminsuidsetup);
+use FS::svc_acct_pop;
+
+my @fields = qw( ac loc state city exch );
+my $fixup = sub {
+ my $hash = shift;
+ $hash->{ac} =~ /^\s*(\d{3})\s*$/;
+ $hash->{ac} = $1;
+ $hash->{loc} =~ /^\s*(\d{3})(\d{4})\s*$/;
+ $hash->{exch} = $1;
+ $hash->{loc} = $2;
+ $hash->{state} =~ /^\s*(\S{0,2})\s*$/;
+ $hash->{state} = $1;
+ $hash->{city} =~ /^\s*(.*?)\s*$/;
+ $hash->{city} = $1;
+
+ };
+
+my $user = shift or usage();
+adminsuidsetup $user;
+
+my $file = shift or usage();
+my $csv = new Text::CSV_XS;
+
+open(FH, $file) or die "cannot open $file: $!";
+
+sub usage {
+ die "Usage:\n\n svc_acct_pop.import user popfile.csv\n\n";
+}
+
+###
+
+my $line;
+while ( defined($line=<FH>) ) {
+ chomp $line;
+
+ $line &= "\177" x length($line); # i hope this isn't really necessary
+ $csv->parse($line)
+ or die "cannot parse: " . $csv->error_input();
+
+ my @values = $csv->fields();
+ my %hash;
+ foreach my $field (@fields) {
+ $hash{$field} = shift @values;
+ }
+
+ &{$fixup}(\%hash);
+
+ my $svc_acct_pop = new FS::svc_acct_pop { %hash };
+
+ #my $error = $svc_acct_pop->check;
+ my $error = $svc_acct_pop->insert;
+ die $error if $error;
+
+}
diff --git a/bin/svc_acct_sm.export b/bin/svc_acct_sm.export
deleted file mode 100755
index c2ec1e5..0000000
--- a/bin/svc_acct_sm.export
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# Create and export VoiceNet_quasar.m4
-#
-# ivan@voicenet.com late oct 96
-#
-# change priority (after copies) to 19, not 10
-# ivan@voicenet.com 97-feb-5
-#
-# put file in different place and run different script, as per matt and
-# mohamed
-# ivan@voicenet.com 97-mar-10
-#
-# added exit if stuff is already locked ivan@voicenet.com 97-apr-15
-#
-# removed mail2
-# ivan@voicenet.com 97-jul-10
-#
-# rewrote lots of the bits, now exports qmail "virtualdomain",
-# "recipientmap" and "rcpthosts" files as well
-#
-# ivan@voicenet.com 97-sep-4
-#
-# adds ".extra" files
-#
-# ivan@voicenet.com 97-sep-29
-#
-# added ".pp" files, ugh.
-#
-# ivan@voicenet.com 97-oct-1
-#
-# rewrite ivan@sisd.com 98-mar-9
-#
-# now can create .qmail-default files ivan@sisd.com 98-mar-10
-#
-# put example $my_domain declaration in ivan@sisd.com 98-mar-23
-#
-# /var/spool/freeside/conf and sendmail updates ivan@sisd.com 98-aug-14
-
-use strict;
-use Fcntl qw(:flock);
-use FS::SSH qw(ssh scp);
-use FS::UID qw(adminsuidsetup);
-use FS::Record qw(qsearch qsearchs);
-
-my($conf_shellm)="/var/spool/freeside/conf/shellmachine";
-my($fqmailmachines)="/var/spool/freeside/conf/qmailmachines";
-my($shellmachine);
-my(@qmailmachines);
-if ( -e $fqmailmachines ) {
- open(SHELLMACHINE,$conf_shellm) or die "Can't open $conf_shellm: $!";
- <SHELLMACHINE> =~ /^([\w\.\-]+)$/ or die "Illegal $conf_shellm";
- $shellmachine = $1;
- close SHELLMACHINE;
- open(QMAILMACHINES,$fqmailmachines);
- @qmailmachines=map {
- /^(.*)$/ or die "Illegal line in conf/qmailmachines"; #we trust the file
- $1;
- } grep $_ !~ /^(#|$)/, <QMAILMACHINES>;
- close QMAILMACHINES;
-}
-
-my($fsendmailmachines)="/var/spool/freeside/conf/sendmailmachines";
-my(@sendmailmachines);
-if ( -e $fsendmailmachines ) {
- open(SENDMAILMACHINES,$fsendmailmachines);
- @sendmailmachines=map {
- /^(.*)$/ or die "Illegal line in conf/sendmailmachines"; #we trust the file
- $1;
- } grep $_ !~ /^(#|$)/, <SENDMAILMACHINES>;
- close SENDMAILMACHINES;
-}
-
-my($conf_domain)="/var/spool/freeside/conf/domain";
-open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!";
-my($mydomain)=map {
- /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file
- $1
-} grep $_ !~ /^(#|$)/, <DOMAIN>;
-close DOMAIN;
-
-my($spooldir)="/var/spool/freeside/export";
-my($spoollock)="/var/spool/freeside/svc_acct_sm.export.lock";
-
-adminsuidsetup;
-umask 066;
-
-open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!";
-select(EXPORT); $|=1; select(STDOUT);
-unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) {
- seek(EXPORT,0,0);
- my($pid)=<EXPORT>;
- chop($pid);
- #no reason to start locks of blocking processes
- die "Is another export process running under pid $pid?\n";
-}
-seek(EXPORT,0,0);
-print EXPORT $$,"\n";
-
-my(@svc_acct_sm)=qsearch('svc_acct_sm',{});
-
-( open(RCPTHOSTS,">$spooldir/rcpthosts")
- and flock(RCPTHOSTS,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/rcpthosts: $!";
-( open(RECIPIENTMAP,">$spooldir/recipientmap")
- and flock(RECIPIENTMAP,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/recipientmap: $!";
-( open(VIRTUALDOMAINS,">$spooldir/virtualdomains")
- and flock(VIRTUALDOMAINS,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/virtualdomains: $!";
-( open(VIRTUSERTABLE,">$spooldir/virtusertable")
- and flock(VIRTUSERTABLE,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/virtusertable: $!";
-( open(SENDMAIL_CW,">$spooldir/sendmail.cw")
- and flock(SENDMAIL_CW,LOCK_EX|LOCK_NB)
-) or die "Can't open $spooldir/sendmail.cw: $!";
-
-setpriority(0,0,10);
-
-my($svc_domain,%domain);
-foreach $svc_domain ( qsearch('svc_domain',{}) ) {
- my($domain)=$svc_domain->domain;
- $domain{$svc_domain->svcnum}=$domain;
- print RCPTHOSTS "$domain\n.$domain\n";
- print SENDMAIL_CW "$domain\n";
-}
-
-my(@sendmail);
-
-my($svc_acct_sm);
-foreach $svc_acct_sm ( qsearch('svc_acct_sm') ) {
- my($domsvc,$domuid,$domuser)=(
- $svc_acct_sm->domsvc,
- $svc_acct_sm->domuid,
- $svc_acct_sm->domuser,
- );
- my($domain)=$domain{$domsvc};
- my($svc_acct)=qsearchs('svc_acct',{'uid'=>$domuid});
- my($username,$dir,$uid,$gid)=(
- $svc_acct->username,
- $svc_acct->dir,
- $svc_acct->uid,
- $svc_acct->gid,
- );
- next unless $username && $domain && $domuser;
-
- if ($domuser eq '*') {
- push @sendmail, "\@$domain\t$username\n";
- print VIRTUALDOMAINS "$domain:$username-$domain\n",
- ".$domain:$username-$domain\n",
- ;
- ###
- # qmail
- ssh("root\@$shellmachine",
- "[ -e $dir/.qmail-default ] || { touch $dir/.qmail-default; chown $uid:$gid $dir/.qmail-default; }"
- ) if ( $shellmachine && $dir && $uid );
-
- } else {
- print VIRTUSERTABLE "$domuser\@$domain\t$username\n";
- print RECIPIENTMAP "$domuser\@$domain:$username\@$mydomain\n";
- }
-
- print VIRTUSERTABLE @sendmail;
-
-}
-
-chmod 0644, "$spooldir/sendmail.cw",
- "$spooldir/virtusertable",
- "$spooldir/rcpthosts",
- "$spooldir/recipientmap",
- "$spooldir/virtualdomains",
-;
-
-flock(SENDMAIL_CW,LOCK_UN);
-flock(VIRTUSERTABLE,LOCK_UN);
-flock(RCPTHOSTS,LOCK_UN);
-flock(RECIPIENTMAP,LOCK_UN);
-flock(VIRTUALDOMAINS,LOCK_UN);
-
-close SENDMAIL_CW;
-close VIRTUSERTABLE;
-close RCPTHOSTS;
-close RECIPIENTMAP;
-close VIRTUALDOMAINS;
-
-###
-# export stuff
-#
-
-my($sendmailmachine);
-foreach $sendmailmachine (@sendmailmachines) {
- scp("$spooldir/sendmail.cw","root\@$sendmailmachine:/etc/sendmail.cw.new")
- == 0 or die "scp error: $!";
- scp("$spooldir/virtusertable","root\@$sendmailmachine:/etc/virtusertable.new")
- == 0 or die "scp error: $!";
- ssh("root\@$sendmailmachine",
- "( ".
- "mv /etc/sendmail.cw.new /etc/sendmail.cw; ".
- "mv /etc/virtusertable.new /etc/virtusertable; ".
- #"/etc/init.d/sendmail restart; ".
- " )"
- )
- == 0 or die "ssh error: $!";
-}
-
-my($qmailmachine);
-foreach $qmailmachine (@qmailmachines) {
- scp("$spooldir/recipientmap","root\@$qmailmachine:/var/qmail/control/recipientmap")
- == 0 or die "scp error: $!";
- scp("$spooldir/virtualdomains","root\@$qmailmachine:/var/qmail/control/virtualdomains")
- == 0 or die "scp error: $!";
- scp("$spooldir/rcpthosts","root\@$qmailmachine:/var/qmail/control/rcpthosts")
- == 0 or die "scp error: $!";
- #ssh("root\@$qmailmachine","/etc/init.d/qmail restart")
- # == 0 or die "ssh error: $!";
-}
-
-unlink $spoollock;
-flock(EXPORT,LOCK_UN);
-close EXPORT;
-
diff --git a/bin/svc_acct_sm.import b/bin/svc_acct_sm.import
deleted file mode 100755
index 10d7e4c..0000000
--- a/bin/svc_acct_sm.import
+++ /dev/null
@@ -1,252 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# ivan@sisd.com 98-mar-9
-#
-# generalized svcparts ivan@sisd.com 98-mar-23
-
-# You really need to enable ssh into a shell machine as this needs to rename
-# .qmail-extension files.
-#
-# now an interactive script ivan@sisd.com 98-jun-30
-#
-# has an (untested) section for sendmail, s/warn/die/g and generates a program
-# to run on your mail machine _later_ instead of ssh'ing for each user
-# ivan@sisd.com 98-jul-13
-
-use strict;
-use vars qw(%d_part_svc %m_part_svc);
-use FS::SSH qw(iscp);
-use FS::UID qw(adminsuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::svc_acct_sm;
-use FS::svc_domain;
-
-adminsuidsetup;
-
-#my($spooldir)="/var/spool/freeside/export";
-my($spooldir)="unix";
-
-my(%mta) = (
- 1 => "qmail",
- 2 => "sendmail",
-);
-
-###
-
-%d_part_svc =
- map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_domain'});
-%m_part_svc =
- map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct_sm'});
-
-print "\n\n",
- ( join "\n", map "$_: ".$d_part_svc{$_}->svc, sort keys %d_part_svc ),
- "\n\nEnter part number for domains: ";
-my($domain_svcpart)=&getvalue;
-
-print "\n\n",
- ( join "\n", map "$_: ".$m_part_svc{$_}->svc, sort keys %m_part_svc ),
- "\n\nEnter part number for mail aliases: ";
-my($mailalias_svcpart)=&getvalue;
-
-print "\n\n", <<END;
-Select your MTA from the following list.
-END
-print join "\n", map "$_: $mta{$_}", sort keys %mta;
-print "\n\n:";
-my($mta)=&getvalue;
-
-if ( $mta{$mta} eq "qmail" ) {
-
- print "\n\n", <<END;
-Enter the location and name of your qmail control directory, for example
-"mail.isp.com:/var/qmail/control"
-END
- print ":";
- my($control)=&getvalue;
- iscp("root\@$control/rcpthosts","$spooldir/rcpthosts.import");
-# iscp("root\@$control/recipientmap","$spooldir/recipientmap.import");
- iscp("root\@$control/virtualdomains","$spooldir/virtualdomains.import");
-
-# print "\n\n", <<END;
-#Enter the name of the machine with your user .qmail files, for example
-#"mail.isp.com"
-#END
-# print ":";
-# my($shellmachine)=&getvalue;
-
-} elsif ( $mta{$mta} eq "sendmail" ) {
-
- print "\n\n", <<END;
-Enter the location and name of your sendmail virtual user table, for example
-"mail.isp.com:/etc/virtusertable"
-END
- print ":";
- my($virtusertable)=&getvalue;
- iscp("root\@$virtusertable","$spooldir/virtusertable.import");
-
- print "\n\n", <<END;
-Enter the location and name of your sendmail.cw file, for example
-"mail.isp.com:/etc/sendmail.cw"
-END
- print ":";
- my($sendmail_cw)=&getvalue;
- iscp("root\@$sendmail_cw","$spooldir/sendmail.cw.import");
-
-} else {
- die "Unknown MTA!\n";
-}
-
-sub getvalue {
- my($x)=scalar(<STDIN>);
- chop $x;
- $x;
-}
-
-print "\n\n";
-
-###
-
-$FS::svc_domain::whois_hack=1;
-$FS::svc_acct_sm::nossh_hack=1;
-
-if ( $mta{$mta} eq "qmail" ) {
- open(RCPTHOSTS,"<$spooldir/rcpthosts.import")
- or die "Can't open $spooldir/rcpthosts.import: $!";
-} elsif ( $mta{$mta} eq "sendmail" ) {
- open(RCPTHOSTS,"<$spooldir/sendmail.cw.import")
- or die "Can't open $spooldir/sendmail.cw.import: $!";
-} else {
- die "Unknown MTA!\n";
-}
-
-my(%svcnum);
-
-while (<RCPTHOSTS>) {
- next if /^(#|$)/;
- /^\.?([\w\-\.]+)$/
- #or do { warn "Strange rcpthosts/sendmail.cw line: $_"; next; };
- or die "Strange rcpthosts/sendmail.cw line: $_";
- my $domain = $1;
- my($svc_domain);
- unless ( $svc_domain = qsearchs('svc_domain', {'domain'=>$domain} ) ) {
- $svc_domain = create FS::svc_domain ({
- 'domain' => $domain,
- 'svcpart' => $domain_svcpart,
- 'action' => 'N',
- });
- my $error = $svc_domain->insert;
- #warn $error if $error;
- die $error if $error;
- }
- $svcnum{$domain}=$svc_domain->svcnum;
-}
-close RCPTHOSTS;
-
-#these two loops have enough similar parts they should probably be merged
-if ( $mta{$mta} eq "qmail" ) {
-
- open(VD_FIX,">$spooldir/virtualdomains.FIX");
- print VD_FIX "#!/usr/bin/perl\n";
-
- open(VIRTUALDOMAINS,"<$spooldir/virtualdomains.import")
- or die "Can't open $spooldir/virtualdomains.import: $!";
- while (<VIRTUALDOMAINS>) {
- next if /^#/;
- /^\.?([\w\-\.]+):(\w+)(\-([\w\-\.]+))?$/
- #or do { warn "Strange virtualdomains line: $_"; next; };
- or die "Strange virtualdomains line: $_";
- my($domain,$username,$dash_ext,$extension)=($1,$2,$3,$4);
- $dash_ext ||= '';
- $extension ||= '';
- my($svc_acct)=qsearchs('svc_acct',{'username'=>$username});
- unless ( $svc_acct ) {
- #warn "Unknown user $username in virtualdomains; skipping\n";
- #die "Unknown user $username in virtualdomains; skipping\n";
- next;
- }
- if ( $domain ne $extension ) {
- #warn "virtualdomains line $domain:$username$dash_ext changed to $domain:$username-$domain\n";
- my($dir)=$svc_acct->dir;
- my($qdomain)=$domain;
- $qdomain =~ s/\./:/g; #see manpage for 'dot-qmail': EXTENSION ADDRESSES
- #example to move .qmail files for virtual domains to their new location
- #dry run
- #issh("root\@$shellmachine",'perl -e \'foreach $a (<'. $dir. '/.qmail'. $dash_ext. '-*>) { $old=$a; $a =~ s/\\.qmail'. $dash_ext. '\\-/\\.qmail\\-'. $qdomain. '\\-/; print " $old -> $a\n"; }\'');
- #the real thing
- #issh("root\@$shellmachine",'perl -e \'foreach $a (<'. $dir. '/.qmail'. $dash_ext. '-*>) { $old=$a; $a =~ s/\\.qmail'. $dash_ext. '\\-/\\.qmail\\-'. $qdomain. '\\-/; rename $old, $a; }\'');
- print VD_FIX <<END;
-foreach \$file (<$dir/.qmail$dash_ext-*>) {
- \$old = \$file;
- \$file =~ s/\.qmail$dash_ext\-/\.qmail\-$qdomain\-/;
- rename \$old, \$file;
-}
-END
- }
-
- unless ( exists $svcnum{$domain} ) {
- my($svc_domain) = create FS::svc_domain ({
- 'domain' => $domain,
- 'svcpart' => $domain_svcpart,
- 'action' => 'N',
- });
- my $error = $svc_domain->insert;
- #warn $error if $error;
- die $error if $error;
- $svcnum{$domain}=$svc_domain->svcnum;
- }
-
- my($svc_acct_sm)=create FS::svc_acct_sm ({
- 'domsvc' => $svcnum{$domain},
- 'domuid' => $svc_acct->uid,
- 'domuser' => '*',
- 'svcpart' => $mailalias_svcpart,
- });
- my($error)='';
- $error=$svc_acct_sm->insert;
- #warn $error if $error;
- die $error, ", domain $domain" if $error;
- }
- close VIRTUALDOMAINS;
- close VD_FIX;
-
-} elsif ( $mta{$mta} eq "sendmail" ) {
-
- open(VIRTUSERTABLE,"<$spooldir/virtusertable.import")
- or die "Can't open $spooldir/virtusertable.import: $!";
- while (<VIRTUSERTABLE>) {
- next if /^#/; #comments?
- /^([\w\-\.]+)?\@([\w\-\.]+)\t([\w\-\.]+)$/
- #or do { warn "Strange virtusertable line: $_"; next; };
- or die "Strange virtusertable line: $_";
- my($domuser,$domain,$username)=($1,$2,$3);
- my($svc_acct)=qsearchs('svc_acct',{'username'=>$username});
- unless ( $svc_acct ) {
- #warn "Unknown user $username in virtusertable";
- die "Unknown user $username in virtusertable";
- next;
- }
- my($svc_acct_sm)=create FS::svc_acct_sm ({
- 'domsvc' => $svcnum{$domain},
- 'domuid' => $svc_acct->uid,
- 'domuser' => $domuser || '*',
- 'svcpart' => $mailalias_svcpart,
- });
- my($error)='';
- $error=$svc_acct_sm->insert;
- #warn $error if $error;
- die $error if $error;
- }
- close VIRTUSERTABLE;
-
-} else {
- die "Unknown MTA!\n";
-}
-
-#open(RECIPIENTMAP,"<$spooldir/recipientmap.import");
-#close RECIPIENTMAP;
-
-print "\n\n", <<END if $mta{$mta} eq "qmail";
-Don\'t forget to run $spooldir/virtualdomains.FIX before using
-$spooldir/virtualdomains !
-END
-
diff --git a/bin/svc_broadband.renumber b/bin/svc_broadband.renumber
new file mode 100755
index 0000000..980fa00
--- /dev/null
+++ b/bin/svc_broadband.renumber
@@ -0,0 +1,84 @@
+#!/usr/bin/perl
+
+use strict;
+
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_Common;
+use FS::part_svc_router;
+use FS::svc_broadband;
+use FS::router;
+use FS::addr_block;
+
+$FS::svc_Common::noexport_hack = 1; #Disable exports!
+
+my $user = shift if $ARGV[0] or die &usage;
+adminsuidsetup($user);
+
+my $remapfile = shift if $ARGV[0] or die &usage;
+my $old_blocknum = shift if $ARGV[0] or die &usage;
+my $new_blocknum = shift if $ARGV[0] or die &usage;
+my $old_svcnum = shift if $ARGV[0];
+
+my %ipmap;
+
+open(REMAP, "<$remapfile") or die $!;
+while (<REMAP>) {
+ next unless (/^([0-9\.]+)\s+([0-9\.]+)$/);
+ my ($old_ip, $new_ip) = ($1, $2);
+ $ipmap{$old_ip} = $new_ip;
+}
+close(REMAP);
+
+my @svcs;
+if ($old_svcnum) {
+ @svcs = ( qsearchs('svc_broadband', { svcnum => $old_svcnum,
+ blocknum => $old_blocknum }) );
+} else {
+ @svcs = qsearch('svc_broadband', { blocknum => $old_blocknum });
+}
+
+foreach my $old_sb (@svcs) {
+
+ my $old_ip = $old_sb->ip_addr;
+ my $new_ip = $ipmap{$old_ip};
+ print "Renumbering ${old_ip} (${old_blocknum}) => ${new_ip} (${new_blocknum})...\n";
+
+
+ my $new_sb = new FS::svc_broadband
+ { $old_sb->hash,
+ ip_addr => $new_ip,
+ blocknum => $new_blocknum,
+ svcpart => $old_sb->cust_svc->svcpart,
+ };
+
+ my $error = $new_sb->replace($old_sb);
+ die $error if $error;
+
+}
+
+
+
+exit(0);
+
+sub usage {
+
+ my $usage = <<EOT;
+Usage:
+ svc_broadband.renumber user remapfile old_blocknum new_blocknum [ svcnum ]
+
+remapfile format:
+old_ip_address new_ip_address
+...
+
+Example remapfile:
+10.0.0.5 192.168.0.5
+10.0.0.20 192.168.0.20
+10.0.0.32 192.168.0.3
+
+Warning: This assumes your routers have already been reconfigured with the
+ new addresses. Exports will not be run!
+
+EOT
+
+}
diff --git a/bin/svc_domain.erase b/bin/svc_domain.erase
new file mode 100755
index 0000000..435dd5f
--- /dev/null
+++ b/bin/svc_domain.erase
@@ -0,0 +1,15 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch);
+
+use FS::domain_record;
+use FS::svc_domain;
+
+adminsuidsetup(shift @ARGV) or die "Usage: svc_domain.erase user\n";
+
+foreach my $record ( qsearch('domain_record',{}), qsearch('svc_domain', {} ) ) {
+ my $error = $record->delete;
+ die $error if $error;
+}
diff --git a/bin/sysvshell.export b/bin/sysvshell.export
new file mode 100755
index 0000000..c13912c
--- /dev/null
+++ b/bin/sysvshell.export
@@ -0,0 +1,112 @@
+#!/usr/bin/perl -w
+
+# sysvshell export
+
+use strict;
+use File::Rsync;
+use Net::SSH qw(ssh);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::part_export;
+use FS::cust_svc;
+use FS::svc_acct;
+
+my @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $spooldir = "/usr/local/etc/freeside/export.". datasrc;
+#my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/shell";
+
+my @sysv_exports = qsearch('part_export', { 'exporttype' => 'sysvshell' } );
+
+my $rsync = File::Rsync->new({
+ rsh => 'ssh',
+# dry_run => 1,
+});
+
+foreach my $export ( @sysv_exports ) {
+ my $machine = $export->machine;
+ my $prefix = "$spooldir/$machine";
+ mkdir $prefix, 0700 unless -d $prefix;
+
+ #LOCKING!!!
+
+ ( open(SHADOW,">$prefix/shadow")
+ #!!! and flock(SHADOW,LOCK_EX|LOCK_NB)
+ ) or die "Can't open $prefix/shadow: $!";
+ ( open(PASSWD,">$prefix/passwd")
+ #!!! and flock(PASSWD,LOCK_EX|LOCK_NB)
+ ) or die "Can't open $prefix/passwd: $!";
+
+ chmod 0644, "$prefix/passwd";
+ chmod 0600, "$prefix/shadow";
+
+ my @svc_acct = $export->svc_x;
+
+ next unless @svc_acct;
+
+ foreach my $svc_acct ( sort { $a->uid <=> $b->uid } @svc_acct ) {
+
+ my $password = $svc_acct->_password;
+ my $cpassword;
+ #if ( ( length($password) <= 8 )
+ if ( ( length($password) <= 12 )
+ && ( $password ne '*' )
+ && ( $password ne '!!' )
+ && ( $password ne '' )
+ ) {
+ $cpassword=crypt($password,
+ $saltset[int(rand(64))].$saltset[int(rand(64))]
+ );
+ # MD5 !!!!
+ } else {
+ $cpassword=$password;
+ }
+
+ ###
+ # FORMAT OF THE PASSWD FILE HERE
+ print PASSWD join(":",
+ $svc_acct->username,
+ 'x', # "##". $username,
+ $svc_acct->uid,
+ $svc_acct->gid,
+ $svc_acct->finger,
+ $svc_acct->dir,
+ $svc_acct->shell,
+ ), "\n";
+
+ ###
+ # FORMAT OF THE SHADOW FILE HERE
+ print SHADOW join(":",
+ $svc_acct->username,
+ $cpassword,
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ ), "\n";
+
+ }
+
+ #!!! flock(SHADOW,LOCK_UN);
+ #!!! flock(PASSWD,LOCK_UN);
+ close SHADOW;
+ close PASSWD;
+
+ $rsync->exec( {
+ src => "$prefix/shadow",
+ dest => "root\@$machine:/etc/shadow"
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+
+ $rsync->exec( {
+ src => "$prefix/passwd",
+ dest => "root\@$machine:/etc/passwd"
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+
+ # UNLOCK!!
+}
diff --git a/bin/test_scrub b/bin/test_scrub
new file mode 100644
index 0000000..5766925
--- /dev/null
+++ b/bin/test_scrub
@@ -0,0 +1,48 @@
+#!/usr/bin/perl -w
+
+#This drops anything from the database that could cause live things to happen.
+#You'd want to do this on a test copy of your live database but NEVER on the
+#live database itself.
+
+#-all exports (all records in part_export, part_export_option export_svc)
+#-all non-POST invoice destinations (cust_main_invoice)
+#-all payment gateways and agent payment gw overrides (payment_gateway,
+# payment_gateway_option, agent_payment_gateway)
+#-everything in the job queue (queue and queue_arg)
+#-business-onlinepayment and business-onlinepayment-ach config
+
+use strict;
+use FS::UID qw(adminsuidsetup dbh);
+use FS::Conf;
+
+adminsuidsetup shift;
+
+foreach my $table (qw(
+ part_export
+ part_export_option
+ export_svc
+ payment_gateway
+ payment_gateway_option
+ agent_payment_gateway
+ queue
+ queue_arg
+)) {
+
+ my $sth = dbh->prepare("DELETE FROM $table") or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+
+}
+
+my $dsth = dbh->prepare("DELETE FROM cust_main_invoice WHERE dest != 'POST'")
+ or die dbh->errstr;
+$dsth->execute or die $dsth->errstr;
+
+my $conf = new FS::Conf;
+foreach my $item (qw(
+ business-onlinepayment
+ business-onlinepayment-ach
+)) {
+ $conf->delete($item);
+}
+
+dbh->commit or die dbh->errstr;
diff --git a/bin/tron-scan b/bin/tron-scan
new file mode 100755
index 0000000..914d6d4
--- /dev/null
+++ b/bin/tron-scan
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::Record qw(qsearch);
+use FS::Tron qw(tron_scan tron_lint);
+use FS::cust_svc;
+
+adminsuidsetup shift;
+
+my $conf = new FS::Conf;
+my $mcp_svcpart = $conf->config('mcp_svcpart') or die "no mcp_svcpart";
+
+#tron_scan($_) foreach qsearch('cust_svc', { 'svcpart' => $mcp_svcpart } );
+foreach my $svc ( qsearch('cust_svc', { 'svcpart' => $mcp_svcpart } ) ) {
+ my $error = tron_scan($svc);
+ warn $error if $error;
+
+ my @lint = tron_lint($svc);
+ print $svc->svc_x->title. "\n". join('', map " $_\n", @lint )
+ if @lint;
+}
+
+1;
diff --git a/conf/address b/conf/address
deleted file mode 100644
index b8b6610..0000000
--- a/conf/address
+++ /dev/null
@@ -1,4 +0,0 @@
-Silicon Interactive Software Design
-119 Signal Hill Road
-Holland, PA 18966-2924
-
diff --git a/conf/agent_defaultpkg b/conf/agent_defaultpkg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/conf/agent_defaultpkg
diff --git a/conf/alerter_template b/conf/alerter_template
new file mode 100644
index 0000000..6fb66b7
--- /dev/null
+++ b/conf/alerter_template
@@ -0,0 +1,18 @@
+
+
+{ $company_name; }
+{ $company_address; }
+
+
+{ $first; } { $last; }:
+
+ We thank you for your continuing patronage. This notice is to remind you
+that your { $payby } used to pay { $company_name; } for Internet
+service will expire on { use Date::Format; time2str("%B %o, %Y", $expdate); }. Please provide us with new
+billing information so that we may continue your service uninterrupted.
+
+Very Truly Yours,
+
+ { $company_name; } Service Team
+
+
diff --git a/conf/blank_logo.eps b/conf/blank_logo.eps
new file mode 100644
index 0000000..e7e3bab
--- /dev/null
+++ b/conf/blank_logo.eps
@@ -0,0 +1,22 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%BoundingBox: 0 0 1 1
+%%HiResBoundingBox: 0 0 0 0
+%%Creator: Karbon14 EPS Exportfilter 0.5
+%%CreationDate: (01/03/2007 11:23:26 PM)
+%%For: (ivan) ()
+%%Title: ()
+
+/N {newpath} def
+/C {closepath} def
+/m {moveto} def
+/c {curveto} def
+/l {lineto} def
+/s {stroke} def
+/f {fill} def
+/w {setlinewidth} def
+/d {setdash} def
+/r {setrgbcolor} def
+/S {gsave} def
+/R {grestore} def
+
+%%EOF
diff --git a/conf/company_address b/conf/company_address
new file mode 100644
index 0000000..3824862
--- /dev/null
+++ b/conf/company_address
@@ -0,0 +1,2 @@
+1234 Example Lane
+Exampleton, CA 54321
diff --git a/conf/company_name b/conf/company_name
new file mode 100644
index 0000000..2cd5323
--- /dev/null
+++ b/conf/company_name
@@ -0,0 +1 @@
+ExampleCo
diff --git a/conf/cust_pkg-change_svcpart b/conf/cust_pkg-change_svcpart
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/conf/cust_pkg-change_svcpart
diff --git a/conf/declinetemplate b/conf/declinetemplate
new file mode 100644
index 0000000..14b8c60
--- /dev/null
+++ b/conf/declinetemplate
@@ -0,0 +1,10 @@
+Hi,
+
+Your credit card could not be processed for the following reason:
+ { $error }
+
+Please provide us with new billing information so that we may continue your
+service uninterrupted.
+
+Thanks.
+
diff --git a/conf/domain b/conf/domain
deleted file mode 100644
index b3cefaf..0000000
--- a/conf/domain
+++ /dev/null
@@ -1 +0,0 @@
-domain.tld
diff --git a/conf/impending_recur_template b/conf/impending_recur_template
new file mode 100644
index 0000000..deb396a
--- /dev/null
+++ b/conf/impending_recur_template
@@ -0,0 +1,20 @@
+
+
+{ $company_name; }
+{ $company_address; }
+
+
+{ $first; } { $last; }:
+
+ We thank you for your continuing patronage. This notice is to remind you
+that your { $packages->[0] } Internet service
+will expire on { use Date::Format; time2str("%B %o, %Y", $recurdates->[0]); }.
+At that time we will begin charging you on a recurring basis so that we may
+continue your service uninterrupted.
+
+Very Truly Yours,
+
+ { $company_name; } Service Team
+
+
+
diff --git a/conf/invoice_from b/conf/invoice_from
new file mode 100644
index 0000000..110ec8f
--- /dev/null
+++ b/conf/invoice_from
@@ -0,0 +1 @@
+ivan-unconfigured-freeside-installation@420.am
diff --git a/conf/invoice_html b/conf/invoice_html
new file mode 100644
index 0000000..7a43aa6
--- /dev/null
+++ b/conf/invoice_html
@@ -0,0 +1,226 @@
+<STYLE TYPE="text/css">
+.invoice { font-family: sans-serif; font-size: 10pt }
+.invoice_header { font-size: 10pt }
+.invoice_headerright TH { border-top: 2px solid #000000; border-bottom: 2px solid #000000 }
+.invoice_headerright TD { font-size: 10pt; empty-cells: show }
+.invoice_longtable table { cellspacing: none }
+.invoice_longtable TH { border-top: 2px solid #000000; border-bottom: 1px solid #000000; padding-left: none; padding-right: none; font-size: 10pt }
+.invoice_desc TD { border-top: 2px solid #000000; font-weight: bold; font-size: 10pt }
+.invoice_desc_more TD { font-weight: bold; font-size: 10pt }
+.invoice_extdesc TD { font-size: 8pt }
+.invoice_totaldesc TD { font-size: 10pt; empty-cells: show }
+</STYLE>
+
+<table class="invoice" bgcolor="#ffffff" WIDTH=768 CELLSPACING=8><tr><td>
+
+ <table class="invoice_header" width="100%">
+ <tr>
+ <td><img src="<%= $cid ? "cid:$cid" : "cust_bill-logo.cgi?invnum=$invnum;template=$template" %>"></td>
+ <td align="left"><%= $returnaddress %></td>
+ <td align="right">
+ <table CLASS="invoice_headerright" cellspacing=0>
+ <tr>
+ <td align="center">
+ Invoice&nbsp;date<BR>
+ <B><%= $date %></B>
+ </td>
+ <td>
+ </td>
+ <td align="center">
+ Invoice&nbsp;#<BR>
+ <B><%= $invnum %></B>
+ </td>
+ <td>
+ </td>
+ <td align="center">
+ Customer #<BR>
+ <B><%= $custnum %></B>
+ </td>
+ </tr>
+ <tr>
+ <th>&nbsp;</th>
+ <th colspan=3 align="center">
+ <FONT SIZE="+3">I</FONT><FONT SIZE="+2">NVOICE</FONT>
+ </th>
+ <th>&nbsp;</th>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ </td>
+ <td align="left">
+ <b><%= $payname %></b><BR>
+ <%= join('<BR>', grep length($_), $company,
+ $address1,
+ $address2,
+ "$city,&nbsp;$state&nbsp;&nbsp;$zip",
+ $country,
+ )
+ %>
+ </td>
+ <%= $ship_enable ? ('<td align="left">'.
+ join('<BR>',grep length($_), '<b>Service Address</b>',
+ $ship_company,
+ $ship_address1,
+ $ship_address2,
+ "$ship_city,&nbsp;$ship_state&nbsp;$ship_zip",
+ $ship_country,
+ ' ',
+ ' ',
+ ).
+ '</td><tr><td></td><td></td>'
+ )
+ : ''
+ %>
+ <td align="right">
+ Terms: <%= $terms %><BR>
+ <%= $po_line %>
+ </td>
+ </tr>
+
+ </table>
+
+ <%=
+ foreach my $section ( @sections ) {
+ if ($section->{'pretotal'}) {
+ $OUT .=
+ '<table width="100%"><tr><td>'.
+ '<p align="right"><b><font size="+1">'.
+ uc(substr($section->{'pretotal'},0,1)).
+ '</font><font size="+0">'. uc(substr($section->{'pretotal'},1)).
+ '</font></b>'.
+ '<p>'.
+ '</td></tr></table>';
+ }
+ $OUT .= '<table><tr><td>';
+ if ($section->{'description'}) {
+ $OUT .=
+ '<p><b><font size="+1">'. uc(substr($section->{'description'},0,1)).
+ '</font><font size="+0">'. uc(substr($section->{'description'},1)).
+ '</font></b>'.
+ '<p>';
+ }else{
+ $OUT .=
+ '<p><b><font size="+1">C</font><font size="+0">HARGES</font></b>'.
+ '<p>';
+ }
+ $OUT .= '</td></tr></table>';
+
+ $OUT .=
+ '<table class="invoice_longtable" CELLSPACING=0 WIDTH="100%">'.
+ '<tr>'.
+ '<th align="center">Ref</th>'.
+ '<th align="left">Description</th>'.
+ ( $unitprices
+ ? '<th align="left">Unit Price</th>'.
+ '<th align="left">Quantity</th>'
+ : ''
+ ).
+ '<th align="right">Amount</th>'.
+ '</tr>';
+
+ my $lastref = 0;
+ foreach my $line (
+ grep { ( scalar(@sections) > 1
+ ? $section->{'description'} eq $_->{'section'}->{'description'}
+ : 1
+ ) }
+ @detail_items )
+ {
+ $OUT .=
+ '<tr class="invoice_desc'.
+ ( ($line->{'ref'} && $line->{'ref'} ne $lastref) ? '' : '_more' ).
+ '">'.
+ '<td align="center">'.
+ ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ). '</td>'.
+ '<td align="left">'. $line->{'description'}. '</td>'.
+ ( $unitprices
+ ? '<td align="left">'. $line->{'unit_amount'}. '</td>'.
+ '<td align="left">'. $line->{'quantity'}. '</td>'
+ : ''
+ ).
+
+ '<td align="right">'. $line->{'amount'}. '</td>'.
+ '</tr>'
+ ;
+ $lastref = $line->{'ref'};
+ if ( @{$line->{'ext_description'} } ) {
+ $OUT .= '<tr class="invoice_extdesc"><td></td><td';
+ $OUT .= $unitprices ? ' colspan=3>' : '>';
+ $OUT .= '<table width="100%">';
+ foreach my $ext_desc ( @{$line->{'ext_description'} } ) {
+ $OUT .=
+ '<tr class="invoice_extdesc">'.
+ '<td align="left" '.
+ ( $ext_desc =~ /<\/?TD>/i ? '' : 'colspan=99' ). '>'.
+ '&nbsp;&nbsp;'. $ext_desc.
+ '</td>'.
+ '</tr>'
+ }
+ $OUT .= '</table></td><td></td></tr>';
+ }
+ }
+
+
+ if (scalar(@sections) > 1) {
+ my $style = 'border-top: 3px solid #000000;'.
+ 'border-bottom: 3px solid #000000;';
+ $OUT .=
+ '<tr class="invoice_totaldesc">'.
+ qq(<td style="$style">&nbsp;</td>).
+ qq(<td align="left" style="$style").
+ ( $unitprices ? ' colspan=3>' : '>' ).
+ $section->{'description'}. ' Total </td>'.
+ qq(<td align="right" style="$style">).
+ $section->{'subtotal'}. '</td>'.
+ '</tr>'
+ ;
+ }
+
+ if ($section->{'posttotal'}) {
+ $OUT .= '<tr><td align="right" colspan=5>';
+ $OUT .=
+ '<p><font size="+1">'. $section->{'posttotal'}.
+ '</font>'.
+ '<p>';
+ $OUT .= '</td></tr>';
+ }
+
+ }
+
+ my $style = 'border-top: 3px solid #000000;';
+ my $linenum = 0;
+
+ foreach my $line ( @total_items ) {
+
+ $style .= 'border-bottom: 3px solid #000000;'
+ if ++$linenum == scalar(@total_items);
+
+ $OUT .=
+ '<tr class="invoice_totaldesc">'.
+ qq(<td style="$style">&nbsp;</td>).
+ qq(<td align="left" style="$style").
+ ( $unitprices ? ' colspan=3>' : '>' ).
+ $line->{'total_item'}. '</td>'.
+ qq(<td align="right" style="$style">).
+ $line->{'total_amount'}. '</td>'.
+ '</tr>'
+ ;
+
+ $style='';
+
+ }
+
+ %>
+ </table>
+ <br><br>
+
+<%= $notes %>
+
+ <hr NOSHADE SIZE=2 COLOR="#000000">
+ <p align="center"><%= $footer %>
+
+</td></tr></table>
diff --git a/conf/invoice_html_statement b/conf/invoice_html_statement
new file mode 100644
index 0000000..0595602
--- /dev/null
+++ b/conf/invoice_html_statement
@@ -0,0 +1,124 @@
+<STYLE TYPE="text/css">
+.invoice { font-family: sans-serif; font-size: 10pt }
+.invoice_header { font-size: 10pt }
+.invoice_headerright TH { border-top: 2px solid #000000; border-bottom: 2px solid #000000 }
+.invoice_headerright TD { font-size: 10pt; empty-cells: show }
+.invoice_longtable table { cellspacing: none }
+.invoice_longtable TH { border-top: 2px solid #000000; border-bottom: 1px solid #000000; padding-left: none; padding-right: none; font-size: 10pt }
+.invoice_desc TD { border-top: 2px solid #000000; font-weight: bold; font-size: 10pt }
+.invoice_extdesc TD { font-size: 8pt }
+.invoice_totaldesc TD { font-size: 10pt; empty-cells: show }
+</STYLE>
+
+<table class="invoice" bgcolor="#ffffff" WIDTH=768 CELLSPACING=8><tr><td>
+
+ <table class="invoice_header" width="100%">
+ <tr>
+ <td><img src="<%= $cid ? "cid:$cid" : "cust_bill-logo.cgi?invnum=$invnum;template=$template" %>"></td>
+ <td align="left"><%= $returnaddress %></td>
+ <td align="right">
+ <table CLASS="invoice_headerright" cellspacing=0>
+ <tr>
+ <td align="right">
+ Invoice&nbsp;date<BR>
+ <B><%= $date %></B>
+ </td>
+ <td>
+ </td>
+ <td align="left">
+ Invoice&nbsp;number<BR>
+ <B><%= $invnum %></B>
+ </td>
+ </tr>
+ <tr>
+ <th>&nbsp;</th>
+ <th colspan=1 align="center">
+ <FONT SIZE="+3">S</FONT><FONT SIZE="+2">TATEMENT</FONT>
+ </th>
+ <th>&nbsp;</th>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ </td>
+ <td align="left">
+ <b><%= $payname %></b><BR>
+ <%= join('<BR>', grep length($_), $company,
+ $address1,
+ $address2,
+ "$city,&nbsp;$state&nbsp;&nbsp;$zip",
+ $country,
+ )
+ %>
+ </td>
+ <td align="right">
+ Terms: <%= $terms %><BR>
+ <%= $po_line %>
+ </td>
+ </tr>
+
+ </table>
+
+ <p><b><font size="+1">C</font><font size="+0">HARGES</font></b>
+ <p>
+ <table class="invoice_longtable" CELLSPACING=0 WIDTH="100%">
+ <tr>
+ <th align="center">Ref</th>
+ <th align="left">Description</th>
+ <th align="right">Amount</th>
+ </tr>
+ <%=
+
+ foreach my $line ( @detail_items ) {
+ $OUT .=
+ '<tr class="invoice_desc">'.
+ '<td align="center">'. $line->{'ref'}. '</td>'.
+ '<td align="left">'. $line->{'description'}. '</td>'.
+ '<td align="right">'. $line->{'amount'}. '</td>'.
+ '</tr>'
+ ;
+ foreach my $ext_desc ( @{$line->{'ext_description'} } ) {
+ $OUT .=
+ '<tr class="invoice_extdesc">'.
+ '<td></td>'.
+ '<td align="left">-&nbsp;'. $ext_desc. '</td>'.
+ '<td></td>'.
+ '</tr>'
+ }
+ }
+
+ my $style = 'border-top: 3px solid #000000;';
+ my $linenum = 0;
+
+ foreach my $line ( @total_items ) {
+
+ $style .= 'border-bottom: 3px solid #000000;'
+ if ++$linenum == scalar(@total_items);
+
+ $OUT .=
+ '<tr class="invoice_totaldesc">'.
+ qq(<td style="$style">&nbsp;</td>).
+ qq(<td align="left" style="$style">).
+ $line->{'total_item'}. '</td>'.
+ qq(<td align="right" style="$style">).
+ $line->{'total_amount'}. '</td>'.
+ '</tr>'
+ ;
+
+ $style='';
+
+ }
+
+ %>
+ </table>
+ <br><br>
+
+<%= $notes %>
+
+ <hr NOSHADE SIZE=2 COLOR="#000000">
+ <p align="center"><%= $footer %>
+
+</td></tr></table>
diff --git a/conf/invoice_latex b/conf/invoice_latex
new file mode 100644
index 0000000..aaec6be
--- /dev/null
+++ b/conf/invoice_latex
@@ -0,0 +1,334 @@
+%% file: Standard Multipage.tex
+%% Purpose: Multipage bill template for e-Bills
+%%
+%% Created by Mark Asplen-Taylor
+%% Asplen Management Ltd
+%% www.asplen.co.uk
+%%
+%% Modified for Freeside by Kristian Hoffman
+%%
+%% Changes
+%% 0.1 4/12/00 Created
+%% 0.2 18/10/01 More fields added
+%% 1.0 16/11/01 RELEASED
+%% 1.2 16/10/02 Invoice number added
+%% 1.3 2/12/02 Logo graphic added
+%% 1.4 7/2/03 Multipage headers/footers added
+%% n/a forked for Freeside; checked into CVS
+%%
+
+\documentclass[letterpaper]{article}
+
+\usepackage{fancyhdr,lastpage,ifthen,fslongtable,afterpage,caption,multirow,bigstrut}
+\usepackage{graphicx} % required for logo graphic
+
+\addtolength{\voffset}{-0.0cm} % top margin to top of header
+\addtolength{\hoffset}{-0.6cm} % left margin on page
+\addtolength{\topmargin}{-1.25cm} % top margin to top of header
+\setlength{\headheight}{2.0cm} % height of header
+\setlength{\headsep}{1.0cm} % between header and text
+\setlength{\footskip}{1.0cm} % bottom of footer from bottom of text
+
+%\addtolength{\textwidth}{2.1in} % width of text
+\setlength{\textwidth}{19.5cm}
+\setlength{\textheight}{19.5cm}
+\setlength{\oddsidemargin}{-0.9cm} % odd page left margin
+\setlength{\evensidemargin}{-0.9cm} % even page left margin
+
+\LTchunksize=40
+
+\renewcommand{\headrulewidth}{0pt}
+\renewcommand{\footrulewidth}{1pt}
+
+\renewcommand{\footrule}{
+[@--
+ $coupon ? '\ifthenelse{\equal{\thepage}{1}}' : '';
+--@]
+ {
+ }
+ {
+ \vbox to 0pt{\rule{\headwidth}{\footrulewidth}\vss}
+ }
+}
+
+\newcommand{\extracouponspace}{3.6cm}
+
+% Adjust the inset of the mailing address
+\newcommand{\addressinset}[1][]{\hspace{1.0cm}}
+
+% Adjust the inset of the return address and logo
+\newcommand{\returninset}[1][]{\hspace{-0.25cm}}
+
+% New command for address lines i.e. skip them if blank
+\newcommand{\addressline}[1]{\ifthenelse{\equal{#1}{}}{}{#1\\}}
+
+% Inserts dollar symbol
+\newcommand{\dollar}[1][]{\symbol{36}}
+
+% Remove plain style header/footer
+\fancypagestyle{plain}{
+ \fancyhead{}
+}
+\fancyhf{}
+
+% Define fancy header/footer for first and subsequent pages
+\fancyfoot[C]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+[@--
+ if ($coupon) {
+ $OUT .= '\vspace{-\extracouponspace}';
+ $OUT .= '\rule[0.5em]{\textwidth}{\footrulewidth}\\\\';
+ $OUT .= $coupon;
+ }
+ '';
+--@] \small{
+[@-- $footer --@]
+ }[@-- $coupon ? '\vspace{\extracouponspace}' : '' --@]
+ }
+ { % ... pages
+ \small{
+[@-- $smallfooter --@]
+ }
+ }
+}
+
+\fancyfoot[R]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ }
+ { % ... pages
+ \small{\thepage\ of \pageref{LastPage}}
+ }
+}
+
+\fancyhead[L]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ \returninset
+ \makebox{
+ \begin{tabular}{ll}
+ \includegraphics{[@-- $logo_file --@]} &
+ \begin{minipage}[b]{5.5cm}
+[@-- $returnaddress --@]
+ \end{minipage}
+ \end{tabular}
+ }
+ }
+ { % ... pages
+ %\includegraphics{[@-- $logo_file --@]} % Uncomment if you want the logo on all pages.
+ }
+}
+
+\fancyhead[R]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ \begin{tabular}{ccc}
+ Invoice date & Invoice \#& Customer\#\\
+ \vspace{0.2cm}
+ \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]} \\\hline
+ \rule{0pt}{5ex} &~~ \huge{\textsc{Invoice}} & \\
+ \vspace{-0.2cm}
+ & & \\\hline
+ \end{tabular}
+ }
+ { % ... pages
+ \small{
+ \begin{tabular}{lll}
+ Invoice date & Invoice \#& Customer\#\\
+ \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]}\\
+ \end{tabular}
+ }
+ }
+}
+
+\pagestyle{fancy}
+
+
+%% Font options are:
+%% bch Bitsream Charter
+%% put Utopia
+%% phv Adobe Helvetica
+%% pnc New Century Schoolbook
+%% ptm Times
+%% pcr Courier
+
+\renewcommand{\familydefault}{phv}
+
+
+% Commands for freeside table header...
+
+\newcommand{\FSdescriptionlength} { [@-- $unitprices ? '8.2cm' : '12.8cm' --@] }
+\newcommand{\FSdescriptioncolumncount} { [@-- $unitprices ? '4' : '6' --@] }
+\newcommand{\FSunitcolumns}{ [@-- $unitprices ? '\makebox[2.5cm][l]{\textbf{~~Unit Price}}&\makebox[1.4cm]{\textbf{~Quantity}}&' : '' --@] }
+
+\newcommand{\FShead}{
+ \hline
+ \rule{0pt}{2.5ex}
+ \makebox[1.4cm]{\textbf{Ref}} &
+% \makebox[2.9cm][l]{\textbf{Description}}&
+% \makebox[1.4cm][l]{}&
+% \makebox[1.4cm][l]{}&
+% \makebox[2.5cm][l]{}&
+ \multicolumn{\FSdescriptioncolumncount}{l}{\makebox[\FSdescriptionlength][l]{\textbf{Description}}}&
+ \FSunitcolumns
+ \makebox[1.6cm][r]{\textbf{Amount}} \\
+ \hline
+}
+
+% ...description...
+\newcommand{\FSdesc}[5]{
+ \multicolumn{1}{c}{\rule{0pt}{2.5ex}\textbf{#1}} &
+ \multicolumn{4}{l}{\textbf{#2}} &
+ \multicolumn{1}{l}{\textbf{#3}} &
+ \multicolumn{1}{r}{\textbf{#4}} &
+ \multicolumn{1}{r}{\textbf{\dollar #5}}\\
+}
+% ...extended description...
+\newcommand{\FSextdesc}[1]{
+ \multicolumn{1}{l}{\rule{0pt}{1.0ex}} &
+%% \multicolumn{2}{l}{\small{~-~#1}}\\
+#1\\
+}
+% ...and total line items.
+\newcommand{\FStotaldesc}[2]{
+ & \multicolumn{6}{l}{#1} & #2\\
+}
+
+
+\begin{document}
+%
+%% Headers and footers defined for the first page
+%
+%% The LH Heading comprising logo
+%% UNCOMMENT the following FOUR lines and change the path if necssary to provide a logo
+%
+%% The Heading comprising isue date, customer ref & INVOICE name
+%
+%% Header & footer changes for subsequent pages
+%
+%
+%
+[@-- $coupon ? '\enlargethispage{-\extracouponspace}' : '' --@]
+\addressinset \rule{0.5cm}{0cm}
+\makebox{
+\begin{minipage}[t]{7.0cm}
+\vspace{0.25cm}
+\textbf{[@-- $payname --@]}\\
+\addressline{[@-- $company --@]}
+\addressline{[@-- $address1 --@]}
+\addressline{[@-- $address2 --@]}
+\addressline{[@-- $city --@], [@-- $state --@]~~[@-- $zip --@]}
+\addressline{[@-- $country --@]}
+\end{minipage}}
+\hfill
+\makebox{
+\begin{minipage}[t]{6.4cm}
+[@--
+ if ($ship_enable) {
+ $OUT .= '\textbf{Service Address}\\\\';
+ $OUT .= "\\addressline{$ship_company}";
+ $OUT .= "\\addressline{$ship_address1}";
+ $OUT .= "\\addressline{$ship_address2}";
+ $OUT .= "\\addressline{$ship_city, $ship_state~~$ship_zip}";
+ $OUT .= "\\addressline{$ship_country}";
+ $OUT .= '~\\\\';
+ }else{
+ $OUT .= '';
+ }
+--@]
+\begin{flushright}
+Terms: [@-- $terms --@]\\
+[@-- $po_line --@]\\
+\end{flushright}
+\end{minipage}}
+\vspace{1.5cm}
+%
+\section*{}
+[@--
+ foreach my $section ( @sections ) {
+ if ($section->{'pretotal'}) {
+ $OUT .= '\begin{flushright}';
+ $OUT .= '\large\textsc{'. $section->{'pretotal'}. '}\\\\';
+ $OUT .= '\\end{flushright}';
+ }
+ $OUT .= '\pagebreak' if $section{'post_total'};
+ $OUT .= '\captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}';
+ $OUT .= '\ifthenelse{\equal{\thepage}{1}}{\setlength{\LTextracouponspace}{\extracouponspace}}{\setlength{\LTextracouponspace}{0pt}}'
+ if $coupon;
+ $OUT .= '\begin{longtable}{cllllllr}';
+ $OUT .= '\caption*{ ';
+ $OUT .= ($section->{'description'}) ? $section->{'description'}: 'Charges';
+ $OUT .= '}\\\\';
+ $OUT .= '\FShead';
+ $OUT .= '\endfirsthead';
+ $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\\\';
+ $OUT .= '\FShead';
+ $OUT .= '\endhead';
+ $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued on next page...}\\\\';
+ $OUT .= '\endfoot';
+ $OUT .= '\hline';
+
+ if (scalar(@sections) > 1) {
+ $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' .
+ '{' . $section->{'subtotal'} . '}' . "\n";
+ }
+
+ #if ($section == $sections[$#sections]) {
+ foreach my $line (grep {$_->{section}->{description} eq $section->{description}} @total_items) {
+ $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' .
+ '{' . $line->{'total_amount'} . '}' . "\n";
+ }
+ #}
+
+ $OUT .= '\hline';
+ $OUT .= '\endlastfoot';
+
+ my $lastref = 0;
+ foreach my $line (
+ grep { ( scalar( @sections ) > 1
+ ? $section->{'description'} eq $_->{'section'}->{'description'}
+ : 1
+ ) }
+ @detail_items )
+ {
+ my $ext_description = $line->{'ext_description'};
+
+ # Don't break-up small packages.
+ my $rowbreak = @$ext_description < 5 ? '*' : '';
+
+ $OUT .= "\\hline\n" if ($line->{'ref'} && $line->{'ref'} ne $lastref);
+ $OUT .= '\FSdesc'.
+ '{' . ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ) . '}'.
+ '{' . $line->{'description'} . '}' .
+ '{' . ( $unitprices ? $line->{'unit_amount'} : '' ) . '}'.
+ '{' . ( $unitprices ? $line->{'quantity'} : '' ) . '}' .
+ '{' . $line->{'amount'} . "}${rowbreak}\n";
+ $lastref = $line->{'ref'};
+
+ foreach my $ext_desc (@$ext_description) {
+ if ( $ext_desc !~ /[^\\]&/ ) {
+ $ext_desc = substr($ext_desc, 0, 80) . '...'
+ if (length($ext_desc) > 80);
+ $ext_desc = '\multicolumn{6}{l}{\small{~~~'. $ext_desc. '}}';
+ }else{
+ $ext_desc = "~~~$ext_desc";
+ }
+ $OUT .= '\FSextdesc{' . $ext_desc . '}' . "${rowbreak}\n";
+ }
+
+ }
+
+ $OUT .= '\end{longtable}';
+
+ if ($section->{'posttotal'}) {
+ $OUT .= '\begin{flushright}';
+ $OUT .= '\normalfont\large\bfseries\textsc{'. $section->{'posttotal'}. '}\\\\';
+ $OUT .= '\\end{flushright}';
+ }
+ }
+
+--@]
+\vfill
+[@-- $notes --@]
+\end{document}
diff --git a/conf/invoice_latex.diff b/conf/invoice_latex.diff
new file mode 100644
index 0000000..b66a522
--- /dev/null
+++ b/conf/invoice_latex.diff
@@ -0,0 +1,138 @@
+--- invoice_latex.old 2005-04-14 01:52:02.000000000 -0700
++++ invoice_latex 2005-04-14 02:33:26.000000000 -0700
+@@ -5,7 +5,7 @@
+ %% Asplen Management Ltd
+ %% www.asplen.co.uk
+ %%
+-%% Modified for Freeside by Ivan Kohler
++%% Modified for Freeside by Ivan Kohler and Kristian Hoffman
+ %%
+ %% Changes
+ %% 0.1 4/12/00 Created
+@@ -61,7 +61,7 @@
+ %% Headers and footers defined for the first page
+ \fancyfoot[CO,CE]{\small{
+ \begin{tabular}{c}
+-$footer
++[@-- $footer --@]
+ \end{tabular}}}
+ %
+ %% The LH Heading comprising logo
+@@ -76,7 +76,7 @@
+ \begin{tabular}{rcl}
+ Invoice date & & Invoice number \\
+ \vspace{0.2cm}
+-\textbf{$date} & & \textbf{$invnum} \\\hline
++\textbf{[@-- $date --@]} & & \textbf{[@-- $invnum --@]} \\\hline
+ \rule{0pt}{5ex} &~~ \huge{\textsc{Invoice}}& \\
+ \vspace{-0.2cm}
+ & & \\\hline
+@@ -85,71 +85,76 @@
+ %% Header & footer changes for subsequent pages
+ %
+ \afterpage{ \fancyfoot[RO,RE]{\small{\thepage\ of \pageref{LastPage}}} }
+-\afterpage{ \fancyfoot[CO,CE]{\small{$smallfooter}} }
++\afterpage{ \fancyfoot[CO,CE]{\small{[@-- $smallfooter --@]}} }
+ \afterpage{ \fancyhead[LO,LE]{\small{}} }
+ \afterpage{ \fancyhead[RO,RE]{\small{
+ \begin{tabular}{ll}
+ Invoice date & Invoice number\\
+-\textbf{$date} & \textbf{$invnum}\\
++\textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]}\\
+ \end{tabular}}} }
+ %
+ %
+ \makebox{
+ \begin{minipage}[t]{2.9in}
+ \vspace{0.20in}
+-\textbf{$payname}\\
+-\addressline{$company}
+-\addressline{$address1}
+-\addressline{$address2}
+-\addressline{$city, $state $zip}
+-\addressline{$country}
++\textbf{[@-- $payname --@]}\\
++\addressline{[@-- $company --@]}
++\addressline{[@-- $address1 --@]}
++\addressline{[@-- $address2 --@]}
++\addressline{[@-- $city --@], [@-- $state --@] [@-- $zip --@]}
++\addressline{[@-- $country --@]}
+ \end{minipage}}
+ \hfill
+ \makebox{
+ \begin{minipage}[t]{2.5in}
+ \begin{flushright}
+-Terms: $terms\\
+-$po_line\\
++Terms: [@-- $terms --@]\\
++[@-- $po_line --@]\\
+ \end{flushright}
+ \end{minipage}}
+ \vspace{0.5cm}
+ %
+ \section*{\textsc{Charges}}
+-\begin{longtable}{|c|l|c|r|r|}
++\begin{longtable}{|c|l|r|}
+ \hline
+ \rule{0pt}{2.5ex}
+ \makebox[1.4cm]{\textbf{Ref}} &
+-\makebox[7.9cm][l]{\textbf{Description}} &
+-\makebox[1.3cm][c]{\textbf{Quantity}} &
+-\makebox[2.5cm][r]{\textbf{Unit Price}} &
+-\makebox[2.5cm][r]{\textbf{Amount}} \\
++\makebox[13cm][l]{\textbf{Description}} &
++\makebox[2cm][r]{\textbf{Amount}} \\
+ \hline
+ \endfirsthead
+-\multicolumn{5}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\
++\multicolumn{3}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\
+ \hline
+ \rule{0pt}{2.5ex}
+ \makebox[1.4cm]{\textbf{Ref}} &
+-\makebox[7.9cm][l]{\textbf{Description}} &
+-\makebox[1.3cm][c]{\textbf{Quantity}} &
+-\makebox[2.5cm][r]{\textbf{Unit Price}} &
+-\makebox[2.5cm][r]{\textbf{Amount}} \\
++\makebox[13cm][l]{\textbf{Description}} &
++\makebox[2cm][r]{\textbf{Amount}} \\
+ \hline
+ \endhead
+-\multicolumn{5}{r}{\rule{0pt}{2.5ex}/cont...}\\
++\multicolumn{3}{r}{\rule{0pt}{2.5ex}/cont...}\\
+ \endfoot
+-%%TotalDetails
+- & \multicolumn{3}{l}{$total_item} & $total_amount\\
+-%%EndTotalDetails
++[@--
++
++ foreach my $line (@total_items) {
++ $OUT .= ' & \multicolumn{1}{l}{' . $line->{'total_item'} . '} & ' .
++ $line->{'total_amount'} . '\\\\' . "\n";
++ }
++
++--@]
+ \hline
+ \endlastfoot
+-%%Detail
+-\rule{0pt}{2.5ex}$ref &
+-\begin{tabular}{l}
+-$description\tabularnewline
+-\end{tabular}
+-& $quantity & \dollar $amount & \dollar $amount\\\hline
+-%%EndDetail
++[@--
++
++ foreach my $line (@detail_items) {
++ $OUT .= '\rule{0pt}{2.5ex}' . $line->{'ref'} . ' &' . "\n".
++ '\begin{tabular}{l}' . "\n".
++ $line->{'description'} . '\tabularnewline' . "\n".
++ '\end{tabular}' . "\n".
++ '& \dollar ' . $line->{'amount'} . '\\\\\\hline' . "\n";
++ }
++
++--@]
+ \end{longtable}
+ \vfill
+-$notes
++[@-- $notes --@]
+ \end{document}
diff --git a/conf/invoice_latex_statement b/conf/invoice_latex_statement
new file mode 100644
index 0000000..302306a
--- /dev/null
+++ b/conf/invoice_latex_statement
@@ -0,0 +1,244 @@
+%% file: Standard Multipage.tex
+%% Purpose: Multipage bill template for e-Bills
+%%
+%% Created by Mark Asplen-Taylor
+%% Asplen Management Ltd
+%% www.asplen.co.uk
+%%
+%% Modified for Freeside by Kristian Hoffman
+%%
+%% Changes
+%% 0.1 4/12/00 Created
+%% 0.2 18/10/01 More fields added
+%% 1.0 16/11/01 RELEASED
+%% 1.2 16/10/02 Invoice number added
+%% 1.3 2/12/02 Logo graphic added
+%% 1.4 7/2/03 Multipage headers/footers added
+%% n/a forked for Freeside; checked into CVS
+%%
+
+\documentclass[letterpaper]{article}
+
+\usepackage{fancyhdr,lastpage,ifthen,longtable,afterpage}
+\usepackage{graphicx} % required for logo graphic
+
+\addtolength{\voffset}{-0.0cm} % top margin to top of header
+\addtolength{\hoffset}{-0.6cm} % left margin on page
+\addtolength{\topmargin}{-1.25cm} % top margin to top of header
+\setlength{\headheight}{2.0cm} % height of header
+\setlength{\headsep}{1.0cm} % between header and text
+\setlength{\footskip}{1.0cm} % bottom of footer from bottom of text
+
+%\addtolength{\textwidth}{2.1in} % width of text
+\setlength{\textwidth}{19.5cm}
+\setlength{\textheight}{19.5cm}
+\setlength{\oddsidemargin}{-0.9cm} % odd page left margin
+\setlength{\evensidemargin}{-0.9cm} % even page left margin
+
+\renewcommand{\headrulewidth}{0pt}
+\renewcommand{\footrulewidth}{1pt}
+
+% Adjust the inset of the mailing address
+\newcommand{\addressinset}[1][]{\hspace{1.0cm}}
+
+% Adjust the inset of the return address and logo
+\newcommand{\returninset}[1][]{\hspace{-0.25cm}}
+
+% New command for address lines i.e. skip them if blank
+\newcommand{\addressline}[1]{\ifthenelse{\equal{#1}{}}{}{#1\newline}}
+
+% Inserts dollar symbol
+\newcommand{\dollar}[1][]{\symbol{36}}
+
+% Remove plain style header/footer
+\fancypagestyle{plain}{
+ \fancyhead{}
+}
+\fancyhf{}
+
+% Define fancy header/footer for first and subsequent pages
+\fancyfoot[C]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ \small{
+[@-- $footer --@]
+ }
+ }
+ { % ... pages
+ \small{
+[@-- $smallfooter --@]
+ }
+ }
+}
+
+\fancyfoot[R]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ }
+ { % ... pages
+ \small{\thepage\ of \pageref{LastPage}}
+ }
+}
+
+\fancyhead[L]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ \returninset
+ \makebox{
+ \begin{tabular}{ll}
+ \includegraphics{[@-- $conf_dir --@]/logo.eps} &
+ \begin{minipage}[b]{5.5cm}
+[@-- $returnaddress --@]
+ \end{minipage}
+ \end{tabular}
+ }
+ }
+ { % ... pages
+ %\includegraphics{[@-- $conf_dir --@]/logo.eps} % Uncomment if you want the logo on all pages.
+ }
+}
+
+\fancyhead[R]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ \begin{tabular}{rcl}
+ Invoice date & & Invoice number \\
+ \vspace{0.2cm}
+ \textbf{[@-- $date --@]} & & \textbf{[@-- $invnum --@]} \\\hline
+ \rule{0pt}{5ex} &~~ \huge{\textsc{Statement}} & \\
+ \vspace{-0.2cm}
+ & & \\\hline
+ \end{tabular}
+ }
+ { % ... pages
+ \small{
+ \begin{tabular}{ll}
+ Invoice date & Invoice number\\
+ \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]}\\
+ \end{tabular}
+ }
+ }
+}
+
+\pagestyle{fancy}
+
+
+%% Font options are:
+%% bch Bitsream Charter
+%% put Utopia
+%% phv Adobe Helvetica
+%% pnc New Century Schoolbook
+%% ptm Times
+%% pcr Courier
+
+\renewcommand{\familydefault}{phv}
+
+
+% Commands for freeside description...
+\newcommand{\FSdesc}[3]{
+ \multicolumn{1}{c}{\rule{0pt}{2.5ex}\textbf{#1}} &
+ \textbf{#2} &
+ \multicolumn{1}{r}{\textbf{\dollar #3}}\\
+}
+% ...extended description...
+\newcommand{\FSextdesc}[1]{
+ \multicolumn{1}{l}{\rule{0pt}{1.0ex}} &
+ \multicolumn{2}{l}{\small{~-~#1}}\\
+}
+% ...and total line items.
+\newcommand{\FStotaldesc}[2]{
+ & \multicolumn{1}{l}{#1} & #2\\
+}
+
+
+\begin{document}
+%
+%% Headers and footers defined for the first page
+%
+%% The LH Heading comprising logo
+%% UNCOMMENT the following FOUR lines and change the path if necssary to provide a logo
+%
+%% The Heading comprising isue date, customer ref & INVOICE name
+%
+%% Header & footer changes for subsequent pages
+%
+%
+%
+\begin{tabular}{ll}
+\addressinset \rule{0cm}{0cm} &
+\makebox{
+\begin{minipage}[t]{5.0cm}
+\vspace{0.25cm}
+\textbf{[@-- $payname --@]}\\
+\addressline{[@-- $company --@]}
+\addressline{[@-- $address1 --@]}
+\addressline{[@-- $address2 --@]}
+\addressline{[@-- $city --@], [@-- $state --@]~~[@-- $zip --@]}
+\addressline{[@-- $country --@]}
+\end{minipage}}
+\end{tabular}
+\hfill
+\makebox{
+\begin{minipage}[t]{6.4cm}
+\begin{flushright}
+Terms: [@-- $terms --@]\\
+[@-- $po_line --@]\\
+\end{flushright}
+\end{minipage}}
+\vspace{1.5cm}
+%
+\section*{\textsc{Charges}}
+\begin{longtable}{clr}
+\hline
+\rule{0pt}{2.5ex}
+\makebox[1.4cm]{\textbf{Ref}} &
+\makebox[12.8cm][l]{\textbf{Description}} &
+\makebox[2.5cm][r]{\textbf{Amount}} \\
+\hline
+\endfirsthead
+\multicolumn{3}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\
+\hline
+\rule{0pt}{2.5ex}
+\makebox[1.4cm]{\textbf{Ref}} &
+\makebox[12.8cm][l]{\textbf{Description}} &
+\makebox[2.5cm][r]{\textbf{Amount}} \\
+\hline
+\endhead
+\multicolumn{3}{r}{\rule{0pt}{2.5ex}Continued on next page...}\\
+\endfoot
+\hline
+[@--
+
+ foreach my $line (@total_items) {
+ $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' .
+ '{' . $line->{'total_amount'} . '}' . "\n";
+ }
+
+--@]
+\hline
+\endlastfoot
+[@--
+
+ foreach my $line (@detail_items) {
+ my $ext_description = $line->{'ext_description'};
+
+ # Don't break-up small packages.
+ my $rowbreak = @$ext_description < 5 ? '*' : '';
+
+ $OUT .= "\\hline\n";
+ $OUT .= '\FSdesc{' . $line->{'ref'} . '}{' . $line->{'description'} . '}' .
+ '{' . $line->{'amount'} . "}${rowbreak}\n";
+
+ foreach my $ext_desc (@$ext_description) {
+ $ext_desc = substr($ext_desc, 0, 80) . '...'
+ if (length($ext_desc) > 80);
+ $OUT .= '\FSextdesc{' . $ext_desc . '}' . "${rowbreak}\n";
+ }
+
+ }
+
+--@]
+\end{longtable}
+\vfill
+[@-- $notes --@]
+\end{document}
diff --git a/conf/invoice_latexcoupon b/conf/invoice_latexcoupon
new file mode 100644
index 0000000..327c121
--- /dev/null
+++ b/conf/invoice_latexcoupon
@@ -0,0 +1,36 @@
+Detach and return this remittance form with your your payment.\\
+\begin{tabular}{ll}
+\begin{tabular}{ll}
+\returninset
+\begin{tabular}{ll}
+ \makebox{ \includegraphics{[@-- $logo_file --@]}} &
+ \begin{minipage}[b]{5.5cm}
+[@-- $returnaddress --@]
+ \end{minipage}
+\end{tabular}&
+\begin{tabular}{r@{: }lr}
+Invoice date & \textbf{[@-- $date --@]} & \multirow{4}*{
+\makebox{
+\begin{minipage}[t]{7.0cm}
+\textbf{[@-- $payname --@]}\\
+\addressline{[@-- $company --@]}
+\addressline{[@-- $address1 --@]}
+\addressline{[@-- $address2 --@]}
+\addressline{[@-- $city --@], [@-- $state --@]~~[@-- $zip --@]}
+\addressline{[@-- $country --@]}
+\end{minipage}}}\\
+Customer\#& \textbf{[@-- $custnum --@]} & \\
+Total Due & \textbf{[@-- $balance --@]} & \\
+\rule{0pt}{2.25em}Amount Enclosed & \rule{2cm}{1pt}& \\
+\end{tabular}\\
+\rule{0pt}{1cm} &\\
+\end{tabular}\\
+\begin{tabular}{ll}
+\addressinset \rule{0.5cm}{0cm} &
+\makebox{
+\begin{minipage}[t]{7.0cm}
+[@-- $returnaddress --@]
+\end{minipage}}
+\hfill
+\end{tabular}\\
+\end{tabular}\\
diff --git a/conf/invoice_latexfooter b/conf/invoice_latexfooter
new file mode 100644
index 0000000..2e32123
--- /dev/null
+++ b/conf/invoice_latexfooter
@@ -0,0 +1 @@
+[@-- $company_name --@]
diff --git a/conf/invoice_latexnotes b/conf/invoice_latexnotes
new file mode 100644
index 0000000..5303d3c
--- /dev/null
+++ b/conf/invoice_latexnotes
@@ -0,0 +1,8 @@
+%%
+%% Add any customer specific notes in here
+%%
+\section*{\textsc{Notes}}
+\begin{enumerate}
+\item Please make your check payable to \textbf{[@-- $company_name --@]}.
+\item If you have any questions please email or telephone.
+\end{enumerate}
diff --git a/conf/invoice_latexnotes_statement b/conf/invoice_latexnotes_statement
new file mode 100644
index 0000000..0836d27
--- /dev/null
+++ b/conf/invoice_latexnotes_statement
@@ -0,0 +1,8 @@
+%%
+%% Add any customer specific notes in here
+%%
+\section*{\textsc{Notes}}
+\begin{enumerate}
+\item This statement reflects current charges and payments.
+\item If you have any questions please email or telephone.
+\end{enumerate}
diff --git a/conf/invoice_latexsmallfooter b/conf/invoice_latexsmallfooter
new file mode 100644
index 0000000..2e32123
--- /dev/null
+++ b/conf/invoice_latexsmallfooter
@@ -0,0 +1 @@
+[@-- $company_name --@]
diff --git a/conf/invoice_template b/conf/invoice_template
new file mode 100644
index 0000000..b33c4dd
--- /dev/null
+++ b/conf/invoice_template
@@ -0,0 +1,26 @@
+
+ Invoice
+ { substr("Page $page of $total_pages ", 0, 19); } { use Date::Format; time2str("%x", $date); } Invoice #{ $invnum; }
+
+
+{ $company_name; }
+{ $company_address; }
+
+
+{ $address[0]; }
+{ $address[1]; }
+{ $address[2]; }
+{ $address[3]; }
+{ $address[4]; }
+{ $address[5]; }
+
+{
+ join("\n",
+ map {
+ my ( $desc, $price ) = @{$_};
+ " ". substr( $desc. " "x65, 0, 65). " ". substr( $price. " "x11, 0, 11);
+ } invoice_lines(31)
+ );
+}
+
+ -=> { $company_name; } <=-
diff --git a/conf/invoice_template_statement b/conf/invoice_template_statement
new file mode 100644
index 0000000..db02915
--- /dev/null
+++ b/conf/invoice_template_statement
@@ -0,0 +1,26 @@
+
+ Statement
+ { substr("Page $page of $total_pages ", 0, 19); } { use Date::Format; time2str("%x", $date); } Invoice #{ $invnum; }
+
+
+{ $company_name; }
+{ $company_address; }
+
+
+{ $address[0]; }
+{ $address[1]; }
+{ $address[2]; }
+{ $address[3]; }
+{ $address[4]; }
+{ $address[5]; }
+
+{
+ join("\n",
+ map {
+ my ( $desc, $price ) = @{$_};
+ " ". substr( $desc. " "x65, 0, 65). " ". substr( $price. " "x11, 0, 11);
+ } invoice_lines(31)
+ );
+}
+
+ -=> { $company_name; } <=-
diff --git a/conf/locale b/conf/locale
new file mode 100644
index 0000000..7741b83
--- /dev/null
+++ b/conf/locale
@@ -0,0 +1 @@
+en_US
diff --git a/conf/logo.eps b/conf/logo.eps
new file mode 100644
index 0000000..ff25dd4
--- /dev/null
+++ b/conf/logo.eps
@@ -0,0 +1,13510 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%HiResBoundingBox: 261.500000 345.500000 418.500000 446.500000
+%%Creator: xpdf/pdftops 3.00
+%%LanguageLevel: 2
+%%DocumentMedia: plain 612 792 0 () ()
+%%BoundingBox: 19 0 70 33
+%%EndComments
+%%BeginProcSet: epsffit 1 0
+gsave
+-65.000 -111.618 translate
+0.324 0.324 scale
+%%EndProcSet
+
+% EPSF created by ps2eps 1.54
+%%BeginProlog
+save
+countdictstack
+mark
+newpath
+/showpage {} def
+/setpagedevice {pop} def
+%%EndProlog
+%%Page 1 1
+/xpdf 75 dict def xpdf begin
+% PDF special state
+/pdfDictSize 15 def
+/pdfSetup {
+ 3 1 roll 2 array astore
+ /setpagedevice where {
+ pop 3 dict begin
+ /PageSize exch def
+ /ImagingBBox null def
+ /Policies 1 dict dup begin /PageSize 3 def end def
+ { /Duplex true def } if
+ currentdict end setpagedevice
+ } {
+ pop pop
+ } ifelse
+} def
+/pdfStartPage {
+ pdfDictSize dict begin
+ /pdfFill [0] def
+ /pdfStroke [0] def
+ /pdfLastFill false def
+ /pdfLastStroke false def
+ /pdfTextMat [1 0 0 1 0 0] def
+ /pdfFontSize 0 def
+ /pdfCharSpacing 0 def
+ /pdfTextRender 0 def
+ /pdfTextRise 0 def
+ /pdfWordSpacing 0 def
+ /pdfHorizScaling 1 def
+ /pdfTextClipPath [] def
+} def
+/pdfEndPage { end } def
+% separation convention operators
+/findcmykcustomcolor where {
+ pop
+}{
+ /findcmykcustomcolor { 5 array astore } def
+} ifelse
+/setcustomcolor where {
+ pop
+}{
+ /setcustomcolor {
+ exch
+ [ exch /Separation exch dup 4 get exch /DeviceCMYK exch
+ 0 4 getinterval cvx
+ [ exch /dup load exch { mul exch dup } /forall load
+ /pop load dup ] cvx
+ ] setcolorspace setcolor
+ } def
+} ifelse
+/customcolorimage where {
+ pop
+}{
+ /customcolorimage {
+ gsave
+ [ exch /Separation exch dup 4 get exch /DeviceCMYK exch
+ 0 4 getinterval
+ [ exch /dup load exch { mul exch dup } /forall load
+ /pop load dup ] cvx
+ ] setcolorspace
+ 10 dict begin
+ /ImageType 1 def
+ /DataSource exch def
+ /ImageMatrix exch def
+ /BitsPerComponent exch def
+ /Height exch def
+ /Width exch def
+ /Decode [1 0] def
+ currentdict end
+ image
+ grestore
+ } def
+} ifelse
+% PDF color state
+/sCol {
+ pdfLastStroke not {
+ pdfStroke aload length
+ dup 1 eq {
+ pop setgray
+ }{
+ dup 3 eq {
+ pop setrgbcolor
+ }{
+ 4 eq {
+ setcmykcolor
+ }{
+ findcmykcustomcolor exch setcustomcolor
+ } ifelse
+ } ifelse
+ } ifelse
+ /pdfLastStroke true def /pdfLastFill false def
+ } if
+} def
+/fCol {
+ pdfLastFill not {
+ pdfFill aload length
+ dup 1 eq {
+ pop setgray
+ }{
+ dup 3 eq {
+ pop setrgbcolor
+ }{
+ 4 eq {
+ setcmykcolor
+ }{
+ findcmykcustomcolor exch setcustomcolor
+ } ifelse
+ } ifelse
+ } ifelse
+ /pdfLastFill true def /pdfLastStroke false def
+ } if
+} def
+% build a font
+/pdfMakeFont {
+ 4 3 roll findfont
+ 4 2 roll matrix scale makefont
+ dup length dict begin
+ { 1 index /FID ne { def } { pop pop } ifelse } forall
+ /Encoding exch def
+ currentdict
+ end
+ definefont pop
+} def
+/pdfMakeFont16 {
+ exch findfont
+ dup length dict begin
+ { 1 index /FID ne { def } { pop pop } ifelse } forall
+ /WMode exch def
+ currentdict
+ end
+ definefont pop
+} def
+/pdfMakeFont16L3 {
+ 1 index /CIDFont resourcestatus {
+ pop pop 1 index /CIDFont findresource /CIDFontType known
+ } {
+ false
+ } ifelse
+ {
+ 0 eq { /Identity-H } { /Identity-V } ifelse
+ exch 1 array astore composefont pop
+ } {
+ pdfMakeFont16
+ } ifelse
+} def
+% graphics state operators
+/q { gsave pdfDictSize dict begin } def
+/Q { end grestore } def
+/cm { concat } def
+/d { setdash } def
+/i { setflat } def
+/j { setlinejoin } def
+/J { setlinecap } def
+/M { setmiterlimit } def
+/w { setlinewidth } def
+% color operators
+/g { dup 1 array astore /pdfFill exch def setgray
+ /pdfLastFill true def /pdfLastStroke false def } def
+/G { dup 1 array astore /pdfStroke exch def setgray
+ /pdfLastStroke true def /pdfLastFill false def } def
+/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor
+ /pdfLastFill true def /pdfLastStroke false def } def
+/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor
+ /pdfLastStroke true def /pdfLastFill false def } def
+/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor
+ /pdfLastFill true def /pdfLastStroke false def } def
+/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor
+ /pdfLastStroke true def /pdfLastFill false def } def
+/ck { 6 copy 6 array astore /pdfFill exch def
+ findcmykcustomcolor exch setcustomcolor
+ /pdfLastFill true def /pdfLastStroke false def } def
+/CK { 6 copy 6 array astore /pdfStroke exch def
+ findcmykcustomcolor exch setcustomcolor
+ /pdfLastStroke true def /pdfLastFill false def } def
+% path segment operators
+/m { moveto } def
+/l { lineto } def
+/c { curveto } def
+/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
+ neg 0 rlineto closepath } def
+/h { closepath } def
+% path painting operators
+/S { sCol stroke } def
+/Sf { fCol stroke } def
+/f { fCol fill } def
+/f* { fCol eofill } def
+% clipping operators
+/W { clip newpath } def
+/W* { eoclip newpath } def
+% text state operators
+/Tc { /pdfCharSpacing exch def } def
+/Tf { dup /pdfFontSize exch def
+ dup pdfHorizScaling mul exch matrix scale
+ pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put
+ exch findfont exch makefont setfont } def
+/Tr { /pdfTextRender exch def } def
+/Ts { /pdfTextRise exch def } def
+/Tw { /pdfWordSpacing exch def } def
+/Tz { /pdfHorizScaling exch def } def
+% text positioning operators
+/Td { pdfTextMat transform moveto } def
+/Tm { /pdfTextMat exch def } def
+% text string operators
+/cshow where {
+ pop
+ /cshow2 {
+ dup {
+ pop pop
+ 1 string dup 0 3 index put 3 index exec
+ } exch cshow
+ pop pop
+ } def
+}{
+ /cshow2 {
+ currentfont /FontType get 0 eq {
+ 0 2 2 index length 1 sub {
+ 2 copy get exch 1 add 2 index exch get
+ 2 copy exch 256 mul add
+ 2 string dup 0 6 5 roll put dup 1 5 4 roll put
+ 3 index exec
+ } for
+ } {
+ dup {
+ 1 string dup 0 3 index put 3 index exec
+ } forall
+ } ifelse
+ pop pop
+ } def
+} ifelse
+/awcp {
+ exch {
+ false charpath
+ 5 index 5 index rmoveto
+ 6 index eq { 7 index 7 index rmoveto } if
+ } exch cshow2
+ 6 {pop} repeat
+} def
+/Tj {
+ fCol
+ 1 index stringwidth pdfTextMat idtransform pop
+ sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse
+ pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32
+ 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0
+ pdfTextMat dtransform
+ 6 5 roll Tj1
+} def
+/Tj16 {
+ fCol
+ 2 index stringwidth pdfTextMat idtransform pop
+ sub exch div
+ pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32
+ 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0
+ pdfTextMat dtransform
+ 6 5 roll Tj1
+} def
+/Tj16V {
+ fCol
+ 2 index stringwidth pdfTextMat idtransform exch pop
+ sub exch div
+ 0 pdfWordSpacing pdfTextMat dtransform 32
+ 4 3 roll pdfCharSpacing add 0 exch
+ pdfTextMat dtransform
+ 6 5 roll Tj1
+} def
+/Tj1 {
+ 0 pdfTextRise pdfTextMat dtransform rmoveto
+ currentpoint 8 2 roll
+ pdfTextRender 1 and 0 eq {
+ 6 copy awidthshow
+ } if
+ pdfTextRender 3 and dup 1 eq exch 2 eq or {
+ 7 index 7 index moveto
+ 6 copy
+ currentfont /FontType get 3 eq { fCol } { sCol } ifelse
+ false awcp currentpoint stroke moveto
+ } if
+ pdfTextRender 4 and 0 ne {
+ 8 6 roll moveto
+ false awcp
+ /pdfTextClipPath [ pdfTextClipPath aload pop
+ {/moveto cvx}
+ {/lineto cvx}
+ {/curveto cvx}
+ {/closepath cvx}
+ pathforall ] def
+ currentpoint newpath moveto
+ } {
+ 8 {pop} repeat
+ } ifelse
+ 0 pdfTextRise neg pdfTextMat dtransform rmoveto
+} def
+/TJm { pdfFontSize 0.001 mul mul neg 0
+ pdfTextMat dtransform rmoveto } def
+/TJmV { pdfFontSize 0.001 mul mul neg 0 exch
+ pdfTextMat dtransform rmoveto } def
+/Tclip { pdfTextClipPath cvx exec clip newpath
+ /pdfTextClipPath [] def } def
+% Level 2 image operators
+/pdfImBuf 100 string def
+/pdfIm {
+ image
+ { currentfile pdfImBuf readline
+ not { pop exit } if
+ (%-EOD-) eq { exit } if } loop
+} def
+/pdfImSep {
+ findcmykcustomcolor exch
+ dup /Width get /pdfImBuf1 exch string def
+ dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def
+ /pdfImDecodeLow exch def
+ begin Width Height BitsPerComponent ImageMatrix DataSource end
+ /pdfImData exch def
+ { pdfImData pdfImBuf1 readstring pop
+ 0 1 2 index length 1 sub {
+ 1 index exch 2 copy get
+ pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi
+ 255 exch sub put
+ } for }
+ 6 5 roll customcolorimage
+ { currentfile pdfImBuf readline
+ not { pop exit } if
+ (%-EOD-) eq { exit } if } loop
+} def
+/pdfImM {
+ fCol imagemask
+ { currentfile pdfImBuf readline
+ not { pop exit } if
+ (%-EOD-) eq { exit } if } loop
+} def
+end
+xpdf begin
+/F2_0 /Helvetica 1 1
+[ /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+ /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+ /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+ /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+ /space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle
+ /parenleft/parenright/asterisk/plus/comma/hyphen/period/slash
+ /zero/one/two/three/four/five/six/seven
+ /eight/nine/colon/semicolon/less/equal/greater/question
+ /at/A/B/C/D/E/F/G
+ /H/I/J/K/L/M/N/O
+ /P/Q/R/S/T/U/V/W
+ /X/Y/Z/bracketleft/backslash/bracketright/asciicircum/underscore
+ /grave/a/b/c/d/e/f/g
+ /h/i/j/k/l/m/n/o
+ /p/q/r/s/t/u/v/w
+ /x/y/z/braceleft/bar/braceright/asciitilde/bullet
+ /Euro/bullet/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl
+ /circumflex/perthousand/Scaron/guilsinglleft/OE/bullet/Zcaron/bullet
+ /bullet/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash/emdash
+ /tilde/trademark/scaron/guilsinglright/oe/bullet/zcaron/Ydieresis
+ /space/exclamdown/cent/sterling/currency/yen/brokenbar/section
+ /dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron
+ /degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered
+ /cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown
+ /Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla
+ /Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex/Idieresis
+ /Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis/multiply
+ /Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
+ /agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla
+ /egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis
+ /eth/ntilde/ograve/oacute/ocircumflex/otilde/odieresis/divide
+ /oslash/ugrave/uacute/ucircumflex/udieresis/yacute/thorn/ydieresis]
+pdfMakeFont
+612 792 false pdfSetup
+pdfStartPage
+26.1663 -1.02141e-14 translate
+0.9406 0.9406 scale
+[] 0 d
+1 i
+0 j
+0 J
+10 M
+1 w
+0 g
+0 G
+q
+[1 0 0 1 0 0] cm
+[1 0 0 1 0 0] Tm
+0 0 Td
+0 g
+328.715 366.945 10.4374 0.2006 re
+f*
+0 g
+324.902 367.146 18.0648 0.2005 re
+f*
+0 g
+322.292 367.346 23.2834 0.2006 re
+f*
+0 g
+320.285 367.547 27.2978 0.2005 re
+f*
+0 g
+318.278 367.747 31.3122 0.2006 re
+f*
+0 g
+316.672 367.948 34.323 0.2006 re
+f*
+0 g
+315.267 368.148 37.3338 0.2005 re
+f*
+0 g
+313.862 368.349 39.9433 0.2005 re
+f*
+0 g
+312.658 368.549 42.5525 0.2006 re
+f*
+0 g
+311.453 368.75 44.9612 0.2006 re
+f*
+0 g
+310.249 368.951 47.1691 0.2006 re
+f*
+0 g
+309.245 369.151 49.377 0.2005 re
+f*
+0 g
+308.242 369.352 50.5813 0.2005 re
+f*
+0 g
+307.238 369.552 49.377 0.2006 re
+f*
+0 g
+306.435 369.753 47.9719 0.2006 re
+f*
+0 g
+305.432 369.953 47.3698 0.2006 re
+f*
+0 g
+304.629 370.154 46.5669 0.2005 re
+f*
+0 g
+303.826 370.355 46.1654 0.2006 re
+f*
+0 g
+303.023 370.555 45.7641 0.2005 re
+f*
+1 g
+348.787 370.555 13.8496 0.2005 re
+f*
+0.498 0 0.482 rg
+362.637 370.555 2.2079 0.2005 re
+f*
+0 g
+302.22 370.756 45.3626 0.2006 re
+f*
+1 g
+347.583 370.756 13.8497 0.2006 re
+f*
+0.498 0 0.482 rg
+361.433 370.756 4.2151 0.2006 re
+f*
+0 g
+301.417 370.956 45.1618 0.2005 re
+f*
+1 g
+346.579 370.956 13.6489 0.2005 re
+f*
+0.498 0 0.482 rg
+360.228 370.956 6.2224 0.2005 re
+f*
+0 g
+300.615 371.157 45.1619 0.2006 re
+f*
+1 g
+345.776 371.157 13.4481 0.2006 re
+f*
+0.498 0 0.482 rg
+359.225 371.157 7.8281 0.2006 re
+f*
+0 g
+300.012 371.357 44.7605 0.2005 re
+f*
+1 g
+344.773 371.357 13.4481 0.2005 re
+f*
+0.498 0 0.482 rg
+358.221 371.357 9.6346 0.2005 re
+f*
+0 g
+299.209 371.558 44.7604 0.2006 re
+f*
+1 g
+343.97 371.558 13.2475 0.2006 re
+f*
+0.498 0 0.482 rg
+357.217 371.558 11.2403 0.2006 re
+f*
+0 g
+298.607 371.758 44.5597 0.2006 re
+f*
+1 g
+343.167 371.758 13.0467 0.2006 re
+f*
+0.498 0 0.482 rg
+356.214 371.758 13.0468 0.2006 re
+f*
+0 g
+298.005 371.959 44.5597 0.2005 re
+f*
+1 g
+342.565 371.959 12.8461 0.2005 re
+f*
+0.498 0 0.482 rg
+355.411 371.959 14.4518 0.2005 re
+f*
+0 g
+297.202 372.16 44.5597 0.2005 re
+f*
+1 g
+341.762 372.16 12.846 0.2005 re
+f*
+0.498 0 0.482 rg
+354.608 372.16 16.0576 0.2005 re
+f*
+0 g
+296.6 372.36 44.5597 0.2006 re
+f*
+1 g
+341.16 372.36 12.6454 0.2006 re
+f*
+0.498 0 0.482 rg
+353.805 372.36 17.4625 0.2006 re
+f*
+0 g
+295.998 372.561 44.359 0.2006 re
+f*
+1 g
+340.357 372.561 12.6453 0.2006 re
+f*
+0.498 0 0.482 rg
+353.002 372.561 18.8677 0.2006 re
+f*
+0 g
+295.396 372.761 44.359 0.2006 re
+f*
+1 g
+339.755 372.761 12.4446 0.2006 re
+f*
+0.498 0 0.482 rg
+352.2 372.761 20.2726 0.2006 re
+f*
+0 g
+294.794 372.962 44.359 0.2006 re
+f*
+1 g
+339.153 372.962 12.2439 0.2006 re
+f*
+0.498 0 0.482 rg
+351.397 372.962 21.6777 0.2006 re
+f*
+0 g
+294.192 373.162 44.359 0.2005 re
+f*
+1 g
+338.551 373.162 12.2439 0.2005 re
+f*
+0.498 0 0.482 rg
+350.794 373.162 22.882 0.2005 re
+f*
+0 g
+293.589 373.363 44.5597 0.2005 re
+f*
+1 g
+338.149 373.363 11.8424 0.2005 re
+f*
+0.498 0 0.482 rg
+349.991 373.363 24.2871 0.2005 re
+f*
+0 g
+292.987 373.563 44.5598 0.2006 re
+f*
+1 g
+337.547 373.563 11.8424 0.2006 re
+f*
+0.498 0 0.482 rg
+349.389 373.563 25.4914 0.2006 re
+f*
+0 g
+292.385 373.764 44.5597 0.2006 re
+f*
+1 g
+336.945 373.764 11.8425 0.2006 re
+f*
+0.498 0 0.482 rg
+348.787 373.764 26.6956 0.2006 re
+f*
+0 g
+291.783 373.965 44.7605 0.2005 re
+f*
+1 g
+336.543 373.965 11.6417 0.2005 re
+f*
+0.498 0 0.482 rg
+348.185 373.965 27.6993 0.2005 re
+f*
+0 g
+291.381 374.165 44.5597 0.2005 re
+f*
+1 g
+335.941 374.165 11.6417 0.2005 re
+f*
+0.498 0 0.482 rg
+347.583 374.165 28.9036 0.2005 re
+f*
+0 g
+290.779 374.366 44.7605 0.2006 re
+f*
+1 g
+335.54 374.366 11.4409 0.2006 re
+f*
+0.498 0 0.482 rg
+346.981 374.366 30.108 0.2006 re
+f*
+0 g
+290.378 374.566 44.5597 0.2006 re
+f*
+1 g
+334.938 374.566 11.441 0.2006 re
+f*
+0.498 0 0.482 rg
+346.379 374.566 31.1115 0.2006 re
+f*
+0 g
+289.776 374.767 44.7605 0.2005 re
+f*
+1 g
+334.536 374.767 11.4409 0.2005 re
+f*
+0.498 0 0.482 rg
+345.977 374.767 32.1152 0.2005 re
+f*
+0 g
+289.174 374.967 44.9611 0.2006 re
+f*
+1 g
+334.135 374.967 11.2403 0.2006 re
+f*
+0.498 0 0.482 rg
+345.375 374.967 33.1187 0.2006 re
+f*
+0 g
+288.772 375.168 44.9612 0.2005 re
+f*
+1 g
+333.733 375.168 11.0396 0.2005 re
+f*
+0.498 0 0.482 rg
+344.773 375.168 34.323 0.2005 re
+f*
+0 g
+288.371 375.368 44.9611 0.2006 re
+f*
+1 g
+333.332 375.368 11.0396 0.2006 re
+f*
+0.498 0 0.482 rg
+344.371 375.368 35.1259 0.2006 re
+f*
+0 g
+287.768 375.569 45.1619 0.2006 re
+f*
+1 g
+332.93 375.569 10.8389 0.2006 re
+f*
+0.498 0 0.482 rg
+343.769 375.569 36.3302 0.2006 re
+f*
+0 g
+287.367 375.77 45.1619 0.2006 re
+f*
+1 g
+332.529 375.77 10.8388 0.2006 re
+f*
+0.498 0 0.482 rg
+343.368 375.77 37.1331 0.2006 re
+f*
+0 g
+286.765 375.97 45.3626 0.2005 re
+f*
+1 g
+332.127 375.97 10.6382 0.2005 re
+f*
+0.498 0 0.482 rg
+342.766 375.97 38.1367 0.2005 re
+f*
+0 g
+286.363 376.171 45.3626 0.2005 re
+f*
+1 g
+331.726 376.171 10.6381 0.2005 re
+f*
+0.498 0 0.482 rg
+342.364 376.171 39.1403 0.2005 re
+f*
+0 g
+285.962 376.371 45.3625 0.2006 re
+f*
+1 g
+331.325 376.371 10.4375 0.2006 re
+f*
+0.498 0 0.482 rg
+341.762 376.371 40.1439 0.2006 re
+f*
+0 g
+285.561 376.572 45.3626 0.2006 re
+f*
+1 g
+330.923 376.572 10.4374 0.2006 re
+f*
+0.498 0 0.482 rg
+341.361 376.572 40.9468 0.2006 re
+f*
+0 g
+284.958 376.772 45.5633 0.2005 re
+f*
+1 g
+330.522 376.772 10.4374 0.2005 re
+f*
+0.498 0 0.482 rg
+340.959 376.772 41.7496 0.2005 re
+f*
+0 g
+284.557 376.973 45.7639 0.2006 re
+f*
+1 g
+330.321 376.973 10.2368 0.2006 re
+f*
+0.498 0 0.482 rg
+340.558 376.973 42.7532 0.2006 re
+f*
+0 g
+284.156 377.173 45.764 0.2005 re
+f*
+1 g
+329.92 377.173 10.2367 0.2005 re
+f*
+0.498 0 0.482 rg
+340.156 377.173 43.5561 0.2005 re
+f*
+0 g
+283.754 377.374 45.7641 0.2006 re
+f*
+1 g
+329.518 377.374 10.2367 0.2006 re
+f*
+0.498 0 0.482 rg
+339.755 377.374 44.3589 0.2006 re
+f*
+0 g
+283.353 377.575 45.9648 0.2006 re
+f*
+1 g
+329.317 377.575 10.0359 0.2006 re
+f*
+0.498 0 0.482 rg
+339.353 377.575 45.1619 0.2006 re
+f*
+0 g
+282.951 377.775 45.9647 0.2005 re
+f*
+1 g
+328.916 377.775 10.036 0.2005 re
+f*
+0.498 0 0.482 rg
+338.952 377.775 45.9647 0.2005 re
+f*
+0 g
+282.55 377.976 45.9647 0.2006 re
+f*
+1 g
+328.515 377.976 10.036 0.2006 re
+f*
+0.498 0 0.482 rg
+338.551 377.976 46.7676 0.2006 re
+f*
+0 g
+282.148 378.176 46.1655 0.2005 re
+f*
+1 g
+328.314 378.176 9.8352 0.2005 re
+f*
+0.498 0 0.482 rg
+338.149 378.176 47.5705 0.2005 re
+f*
+0 g
+281.747 378.377 46.1655 0.2006 re
+f*
+1 g
+327.912 378.377 9.8353 0.2006 re
+f*
+0.498 0 0.482 rg
+337.748 378.377 48.3733 0.2006 re
+f*
+0 g
+281.346 378.577 46.3662 0.2006 re
+f*
+1 g
+327.712 378.577 9.6346 0.2006 re
+f*
+0.498 0 0.482 rg
+337.346 378.577 49.1762 0.2006 re
+f*
+0 g
+280.944 378.778 46.3662 0.2005 re
+f*
+1 g
+327.31 378.778 9.6345 0.2005 re
+f*
+0.498 0 0.482 rg
+336.945 378.778 49.9792 0.2005 re
+f*
+0 g
+280.543 378.978 46.5668 0.2006 re
+f*
+1 g
+327.11 378.978 9.4339 0.2006 re
+f*
+0.498 0 0.482 rg
+336.543 378.978 50.7819 0.2006 re
+f*
+0 g
+280.141 379.179 46.7676 0.2005 re
+f*
+1 g
+326.909 379.179 9.4339 0.2005 re
+f*
+0.498 0 0.482 rg
+336.343 379.179 51.3841 0.2005 re
+f*
+0 g
+279.74 379.38 46.7677 0.2006 re
+f*
+1 g
+326.507 379.38 9.4338 0.2006 re
+f*
+0.498 0 0.482 rg
+335.941 379.38 52.187 0.2006 re
+f*
+0 g
+279.338 379.58 46.9684 0.2006 re
+f*
+1 g
+326.307 379.58 9.2331 0.2006 re
+f*
+0.498 0 0.482 rg
+335.54 379.58 52.9899 0.2006 re
+f*
+0 g
+278.937 379.781 46.9683 0.2005 re
+f*
+1 g
+325.905 379.781 9.2331 0.2005 re
+f*
+0.498 0 0.482 rg
+335.138 379.781 53.5921 0.2005 re
+f*
+0 g
+278.736 379.981 46.9684 0.2006 re
+f*
+1 g
+325.704 379.981 9.2331 0.2006 re
+f*
+0.498 0 0.482 rg
+334.938 379.981 54.1942 0.2006 re
+f*
+0 g
+278.335 380.182 47.1691 0.2005 re
+f*
+1 g
+325.504 380.182 9.0324 0.2005 re
+f*
+0.498 0 0.482 rg
+334.536 380.182 54.9971 0.2005 re
+f*
+0 g
+277.933 380.382 47.3698 0.2006 re
+f*
+1 g
+325.303 380.382 9.0323 0.2006 re
+f*
+0.498 0 0.482 rg
+334.335 380.382 55.5994 0.2006 re
+f*
+0 g
+277.532 380.583 47.3697 0.2005 re
+f*
+1 g
+324.902 380.583 9.0324 0.2005 re
+f*
+0.498 0 0.482 rg
+333.934 380.583 56.4022 0.2005 re
+f*
+0 g
+277.331 380.783 47.3698 0.2006 re
+f*
+1 g
+324.701 380.783 9.0324 0.2006 re
+f*
+0.498 0 0.482 rg
+333.733 380.783 56.8036 0.2006 re
+f*
+0 g
+287.367 380.984 37.1331 0.2006 re
+f*
+1 g
+324.5 380.984 8.8316 0.2006 re
+f*
+0.498 0 0.482 rg
+333.332 380.984 57.6066 0.2006 re
+f*
+0 g
+287.367 381.185 36.9324 0.2005 re
+f*
+1 g
+324.299 381.185 8.8316 0.2005 re
+f*
+0.498 0 0.482 rg
+333.131 381.185 58.2087 0.2005 re
+f*
+0 g
+287.367 381.385 36.5309 0.2006 re
+f*
+1 g
+323.898 381.385 8.8317 0.2006 re
+f*
+0.498 0 0.482 rg
+332.73 381.385 58.8108 0.2006 re
+f*
+0 g
+287.367 381.586 36.3302 0.2005 re
+f*
+1 g
+323.697 381.586 8.8317 0.2005 re
+f*
+0.498 0 0.482 rg
+332.529 381.586 59.413 0.2005 re
+f*
+0 g
+287.367 381.786 36.1295 0.2006 re
+f*
+1 g
+323.497 381.786 8.6309 0.2006 re
+f*
+0.498 0 0.482 rg
+332.127 381.786 60.2159 0.2006 re
+f*
+0 g
+287.367 381.987 35.9288 0.2006 re
+f*
+1 g
+323.296 381.987 8.6309 0.2006 re
+f*
+0.498 0 0.482 rg
+331.927 381.987 60.6173 0.2006 re
+f*
+0 g
+278.937 382.188 0.2007 0.2005 re
+f*
+1 g
+279.138 382.188 8.2295 0.2005 re
+f*
+0 g
+287.367 382.188 35.7281 0.2005 re
+f*
+1 g
+323.095 382.188 8.4302 0.2005 re
+f*
+0.498 0 0.482 rg
+331.525 382.188 61.4201 0.2005 re
+f*
+0 g
+278.937 382.388 43.9575 0.2006 re
+f*
+1 g
+322.894 382.388 8.4302 0.2006 re
+f*
+0.498 0 0.482 rg
+331.325 382.388 61.8216 0.2006 re
+f*
+0 g
+278.937 382.589 43.7569 0.2005 re
+f*
+1 g
+322.694 382.589 8.4301 0.2005 re
+f*
+0.498 0 0.482 rg
+331.124 382.589 62.4238 0.2005 re
+f*
+0 g
+278.937 382.789 43.5561 0.2006 re
+f*
+1 g
+322.493 382.789 8.2295 0.2006 re
+f*
+0.498 0 0.482 rg
+330.722 382.789 63.2266 0.2006 re
+f*
+0 g
+278.937 382.99 43.3554 0.2006 re
+f*
+1 g
+322.292 382.99 8.2295 0.2006 re
+f*
+0.498 0 0.482 rg
+330.522 382.99 63.628 0.2006 re
+f*
+0 g
+278.937 383.19 43.1547 0.2005 re
+f*
+1 g
+322.092 383.19 8.2294 0.2005 re
+f*
+0.498 0 0.482 rg
+330.321 383.19 64.2303 0.2005 re
+f*
+0 g
+278.937 383.391 42.9539 0.2006 re
+f*
+1 g
+321.891 383.391 8.2295 0.2006 re
+f*
+0.498 0 0.482 rg
+330.12 383.391 64.6317 0.2006 re
+f*
+0 g
+278.937 383.591 42.7533 0.2005 re
+f*
+1 g
+321.69 383.591 8.0287 0.2005 re
+f*
+0.498 0 0.482 rg
+329.719 383.591 65.4345 0.2005 re
+f*
+0 g
+278.937 383.792 42.5525 0.2006 re
+f*
+1 g
+321.489 383.792 8.0288 0.2006 re
+f*
+0.498 0 0.482 rg
+329.518 383.792 65.8359 0.2006 re
+f*
+0 g
+278.937 383.992 42.3518 0.2006 re
+f*
+1 g
+321.289 383.992 8.0288 0.2006 re
+f*
+0.498 0 0.482 rg
+329.317 383.992 66.4381 0.2006 re
+f*
+0 g
+278.937 384.193 42.1511 0.2005 re
+f*
+1 g
+321.088 384.193 8.0287 0.2005 re
+f*
+0.498 0 0.482 rg
+329.117 384.193 66.8396 0.2005 re
+f*
+0 g
+278.937 384.394 41.9503 0.2005 re
+f*
+1 g
+320.887 384.394 8.0288 0.2005 re
+f*
+0.498 0 0.482 rg
+328.916 384.394 67.241 0.2005 re
+f*
+0 g
+278.937 384.594 41.7497 0.2006 re
+f*
+1 g
+320.687 384.594 7.828 0.2006 re
+f*
+0.498 0 0.482 rg
+328.515 384.594 68.0439 0.2006 re
+f*
+0 g
+271.109 384.795 0.2008 0.2006 re
+f*
+1 g
+271.31 384.795 7.6273 0.2006 re
+f*
+0 g
+278.937 384.795 41.5489 0.2006 re
+f*
+1 g
+320.486 384.795 7.8281 0.2006 re
+f*
+0.498 0 0.482 rg
+328.314 384.795 68.4453 0.2006 re
+f*
+0 g
+270.707 384.995 0.6022 0.2006 re
+f*
+1 g
+271.31 384.995 7.6273 0.2006 re
+f*
+0 g
+278.937 384.995 41.3482 0.2006 re
+f*
+1 g
+320.285 384.995 7.828 0.2006 re
+f*
+0.498 0 0.482 rg
+328.113 384.995 69.0475 0.2006 re
+f*
+0 g
+270.507 385.196 0.8029 0.2005 re
+f*
+1 g
+271.31 385.196 7.6273 0.2005 re
+f*
+0 g
+278.937 385.196 41.1475 0.2005 re
+f*
+1 g
+320.084 385.196 7.828 0.2005 re
+f*
+0.498 0 0.482 rg
+327.912 385.196 69.4489 0.2005 re
+f*
+0 g
+270.306 385.396 1.0036 0.2006 re
+f*
+1 g
+271.31 385.396 7.6273 0.2006 re
+f*
+0 g
+278.937 385.396 40.9467 0.2006 re
+f*
+1 g
+319.884 385.396 7.8281 0.2006 re
+f*
+0.498 0 0.482 rg
+327.712 385.396 69.8504 0.2006 re
+f*
+0 g
+269.904 385.597 1.4051 0.2005 re
+f*
+1 g
+271.31 385.597 7.6273 0.2005 re
+f*
+0 g
+278.937 385.597 40.7461 0.2005 re
+f*
+1 g
+319.683 385.597 7.828 0.2005 re
+f*
+0.498 0 0.482 rg
+327.511 385.597 70.4525 0.2005 re
+f*
+0 g
+269.704 385.797 1.6058 0.2006 re
+f*
+1 g
+271.31 385.797 7.6273 0.2006 re
+f*
+0 g
+278.937 385.797 40.7461 0.2006 re
+f*
+1 g
+319.683 385.797 7.6273 0.2006 re
+f*
+0.498 0 0.482 rg
+327.31 385.797 70.8539 0.2006 re
+f*
+0 g
+269.503 385.998 1.8065 0.2005 re
+f*
+1 g
+271.31 385.998 7.6273 0.2005 re
+f*
+0 g
+278.937 385.998 40.5453 0.2005 re
+f*
+1 g
+319.482 385.998 7.6273 0.2005 re
+f*
+0.498 0 0.482 rg
+327.11 385.998 71.2554 0.2005 re
+f*
+0 g
+269.102 386.199 2.208 0.2006 re
+f*
+1 g
+271.31 386.199 7.6273 0.2006 re
+f*
+0 g
+278.937 386.199 40.3446 0.2006 re
+f*
+1 g
+319.281 386.199 7.6273 0.2006 re
+f*
+0.498 0 0.482 rg
+326.909 386.199 71.8576 0.2006 re
+f*
+0 g
+268.901 386.399 2.4087 0.2005 re
+f*
+1 g
+271.31 386.399 7.6273 0.2005 re
+f*
+0 g
+278.937 386.399 40.1438 0.2005 re
+f*
+1 g
+319.081 386.399 7.6274 0.2005 re
+f*
+0.498 0 0.482 rg
+326.708 386.399 72.259 0.2005 re
+f*
+0 g
+268.7 386.6 2.6094 0.2006 re
+f*
+1 g
+271.31 386.6 7.6273 0.2006 re
+f*
+0 g
+278.937 386.6 39.9431 0.2006 re
+f*
+1 g
+318.88 386.6 7.6274 0.2006 re
+f*
+0.498 0 0.482 rg
+326.507 386.6 72.6604 0.2006 re
+f*
+0 g
+268.299 386.8 3.0108 0.2006 re
+f*
+1 g
+271.31 386.8 7.6273 0.2006 re
+f*
+0 g
+278.937 386.8 39.9431 0.2006 re
+f*
+1 g
+318.88 386.8 7.4267 0.2006 re
+f*
+0.498 0 0.482 rg
+326.307 386.8 73.0618 0.2006 re
+f*
+0 g
+268.098 387.001 3.2115 0.2005 re
+f*
+1 g
+271.31 387.001 7.6273 0.2005 re
+f*
+0 g
+278.937 387.001 39.7425 0.2005 re
+f*
+1 g
+318.679 387.001 7.4265 0.2005 re
+f*
+0.498 0 0.482 rg
+326.106 387.001 73.6641 0.2005 re
+f*
+0 g
+267.897 387.201 3.4123 0.2005 re
+f*
+1 g
+271.31 387.201 7.6273 0.2005 re
+f*
+0 g
+278.937 387.201 39.5417 0.2005 re
+f*
+1 g
+318.479 387.201 7.4266 0.2005 re
+f*
+0.498 0 0.482 rg
+325.905 387.201 74.0655 0.2005 re
+f*
+0 g
+267.697 387.402 3.613 0.2006 re
+f*
+1 g
+271.31 387.402 7.6273 0.2006 re
+f*
+0 g
+278.937 387.402 39.341 0.2006 re
+f*
+1 g
+318.278 387.402 7.4266 0.2006 re
+f*
+0.498 0 0.482 rg
+325.704 387.402 74.4669 0.2006 re
+f*
+0 g
+267.295 387.603 4.0144 0.2006 re
+f*
+1 g
+271.31 387.603 7.6273 0.2006 re
+f*
+0 g
+278.937 387.603 39.341 0.2006 re
+f*
+1 g
+318.278 387.603 7.2259 0.2006 re
+f*
+0.498 0 0.482 rg
+325.504 387.603 74.8683 0.2006 re
+f*
+0 g
+267.094 387.803 4.2151 0.2006 re
+f*
+1 g
+271.31 387.803 7.6273 0.2006 re
+f*
+0 g
+278.937 387.803 39.1402 0.2006 re
+f*
+1 g
+318.077 387.803 7.226 0.2006 re
+f*
+0.498 0 0.482 rg
+325.303 387.803 75.4705 0.2006 re
+f*
+0 g
+266.894 388.004 4.4159 0.2006 re
+f*
+1 g
+271.31 388.004 7.6273 0.2006 re
+f*
+0 g
+278.937 388.004 38.9395 0.2006 re
+f*
+1 g
+317.876 388.004 7.226 0.2006 re
+f*
+0.498 0 0.482 rg
+325.102 388.004 75.8719 0.2006 re
+f*
+0 g
+266.693 388.204 4.6166 0.2005 re
+f*
+1 g
+271.31 388.204 7.6273 0.2005 re
+f*
+0 g
+278.937 388.204 38.7389 0.2005 re
+f*
+1 g
+317.676 388.204 7.2258 0.2005 re
+f*
+0.498 0 0.482 rg
+324.902 388.204 76.2734 0.2005 re
+f*
+0 g
+266.492 388.405 4.8173 0.2005 re
+f*
+1 g
+271.31 388.405 7.6273 0.2005 re
+f*
+0 g
+278.937 388.405 38.7389 0.2005 re
+f*
+1 g
+317.676 388.405 7.0251 0.2005 re
+f*
+0.498 0 0.482 rg
+324.701 388.405 76.6748 0.2005 re
+f*
+0 g
+266.292 388.605 5.018 0.2006 re
+f*
+1 g
+271.31 388.605 7.6273 0.2006 re
+f*
+0 g
+278.937 388.605 38.5381 0.2006 re
+f*
+1 g
+317.475 388.605 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+324.5 388.605 77.0762 0.2006 re
+f*
+0 g
+265.89 388.806 5.4195 0.2006 re
+f*
+1 g
+271.31 388.806 7.6273 0.2006 re
+f*
+0 g
+278.937 388.806 38.3374 0.2006 re
+f*
+1 g
+317.274 388.806 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+324.299 388.806 77.4777 0.2006 re
+f*
+0 g
+265.689 389.006 5.6202 0.2005 re
+f*
+1 g
+271.31 389.006 7.6273 0.2005 re
+f*
+0 g
+278.937 389.006 38.3374 0.2005 re
+f*
+1 g
+317.274 389.006 7.0252 0.2005 re
+f*
+0.498 0 0.482 rg
+324.299 389.006 77.8791 0.2005 re
+f*
+0 g
+265.489 389.207 5.8209 0.2005 re
+f*
+1 g
+271.31 389.207 7.6273 0.2005 re
+f*
+0 g
+278.937 389.207 38.1367 0.2005 re
+f*
+1 g
+317.074 389.207 7.0252 0.2005 re
+f*
+0.498 0 0.482 rg
+324.099 389.207 78.2805 0.2005 re
+f*
+0 g
+265.288 389.407 6.0216 0.2006 re
+f*
+1 g
+271.31 389.407 7.6273 0.2006 re
+f*
+0 g
+278.937 389.407 37.9359 0.2006 re
+f*
+1 g
+316.873 389.407 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+323.898 389.407 78.682 0.2006 re
+f*
+0 g
+265.087 389.608 6.2224 0.2006 re
+f*
+1 g
+271.31 389.608 7.6273 0.2006 re
+f*
+0 g
+278.937 389.608 37.9359 0.2006 re
+f*
+1 g
+316.873 389.608 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+323.697 389.608 79.0835 0.2006 re
+f*
+0 g
+264.886 389.809 6.4231 0.2005 re
+f*
+1 g
+271.31 389.809 7.6273 0.2005 re
+f*
+0 g
+278.937 389.809 37.7352 0.2005 re
+f*
+1 g
+316.672 389.809 6.8245 0.2005 re
+f*
+0.498 0 0.482 rg
+323.497 389.809 79.4849 0.2005 re
+f*
+0 g
+264.686 390.009 6.6238 0.2006 re
+f*
+1 g
+271.31 390.009 7.6273 0.2006 re
+f*
+0 g
+278.937 390.009 37.5345 0.2006 re
+f*
+1 g
+316.471 390.009 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+323.497 390.009 79.6856 0.2006 re
+f*
+0 g
+264.485 390.21 6.8245 0.2005 re
+f*
+1 g
+271.31 390.21 7.6273 0.2005 re
+f*
+0 g
+278.937 390.21 37.5345 0.2005 re
+f*
+1 g
+316.471 390.21 6.8245 0.2005 re
+f*
+0.498 0 0.482 rg
+323.296 390.21 80.087 0.2005 re
+f*
+0 g
+264.284 390.41 7.0252 0.2006 re
+f*
+1 g
+271.31 390.41 7.6273 0.2006 re
+f*
+0 g
+278.937 390.41 37.3338 0.2006 re
+f*
+1 g
+316.271 390.41 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+323.095 390.41 80.4884 0.2006 re
+f*
+0 g
+264.084 390.611 7.226 0.2006 re
+f*
+1 g
+271.31 390.611 7.6273 0.2006 re
+f*
+0 g
+278.937 390.611 37.1331 0.2006 re
+f*
+1 g
+316.07 390.611 6.8244 0.2006 re
+f*
+0.498 0 0.482 rg
+322.894 390.611 80.89 0.2006 re
+f*
+0 g
+263.883 390.811 7.4267 0.2006 re
+f*
+1 g
+271.31 390.811 7.6273 0.2006 re
+f*
+0 g
+278.937 390.811 37.1331 0.2006 re
+f*
+1 g
+316.07 390.811 6.8244 0.2006 re
+f*
+0.498 0 0.482 rg
+322.894 390.811 81.0907 0.2006 re
+f*
+0 g
+263.682 391.012 7.6274 0.2005 re
+f*
+1 g
+271.31 391.012 7.6273 0.2005 re
+f*
+0 g
+278.937 391.012 36.9323 0.2005 re
+f*
+1 g
+315.869 391.012 6.8246 0.2005 re
+f*
+0.498 0 0.482 rg
+322.694 391.012 81.492 0.2005 re
+f*
+0 g
+263.281 391.213 8.0288 0.2005 re
+f*
+1 g
+271.31 391.213 7.6273 0.2005 re
+f*
+0 g
+278.937 391.213 36.9323 0.2005 re
+f*
+1 g
+315.869 391.213 6.6238 0.2005 re
+f*
+0.498 0 0.482 rg
+322.493 391.213 81.8935 0.2005 re
+f*
+0 g
+263.08 391.413 8.2296 0.2006 re
+f*
+1 g
+271.31 391.413 7.6273 0.2006 re
+f*
+0 g
+278.937 391.413 36.7317 0.2006 re
+f*
+1 g
+315.669 391.413 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+322.292 391.413 82.2949 0.2006 re
+f*
+0 g
+262.879 391.614 8.4303 0.2006 re
+f*
+1 g
+271.31 391.614 7.6273 0.2006 re
+f*
+0 g
+278.937 391.614 36.5309 0.2006 re
+f*
+1 g
+315.468 391.614 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+322.292 391.614 82.4957 0.2006 re
+f*
+0 g
+262.679 391.814 8.631 0.2005 re
+f*
+1 g
+271.31 391.814 7.6273 0.2005 re
+f*
+0 g
+278.937 391.814 36.5309 0.2005 re
+f*
+1 g
+315.468 391.814 6.6238 0.2005 re
+f*
+0.498 0 0.482 rg
+322.092 391.814 82.8971 0.2005 re
+f*
+0 g
+262.478 392.015 8.8317 0.2006 re
+f*
+1 g
+271.31 392.015 7.6273 0.2006 re
+f*
+0 g
+278.937 392.015 36.3302 0.2006 re
+f*
+1 g
+315.267 392.015 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+321.891 392.015 83.2986 0.2006 re
+f*
+0 g
+262.277 392.215 9.0324 0.2005 re
+f*
+1 g
+271.31 392.215 7.6273 0.2005 re
+f*
+0 g
+278.937 392.215 36.3302 0.2005 re
+f*
+1 g
+315.267 392.215 6.6237 0.2005 re
+f*
+0.498 0 0.482 rg
+321.891 392.215 83.4993 0.2005 re
+f*
+0 g
+262.277 392.416 9.0324 0.2006 re
+f*
+1 g
+271.31 392.416 7.6273 0.2006 re
+f*
+0 g
+278.937 392.416 36.1295 0.2006 re
+f*
+1 g
+315.066 392.416 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+321.69 392.416 83.9007 0.2006 re
+f*
+0 g
+262.076 392.616 9.2332 0.2006 re
+f*
+1 g
+271.31 392.616 7.6273 0.2006 re
+f*
+0 g
+278.937 392.616 36.1295 0.2006 re
+f*
+1 g
+315.066 392.616 6.423 0.2006 re
+f*
+0.498 0 0.482 rg
+321.489 392.616 84.3022 0.2006 re
+f*
+0 g
+261.876 392.817 9.4339 0.2005 re
+f*
+1 g
+271.31 392.817 7.6273 0.2005 re
+f*
+0 g
+278.937 392.817 35.9287 0.2005 re
+f*
+1 g
+314.866 392.817 6.6238 0.2005 re
+f*
+0.498 0 0.482 rg
+321.489 392.817 84.5029 0.2005 re
+f*
+0 g
+261.675 393.018 9.6346 0.2006 re
+f*
+1 g
+271.31 393.018 7.6273 0.2006 re
+f*
+0 g
+278.937 393.018 35.9287 0.2006 re
+f*
+1 g
+314.866 393.018 6.4231 0.2006 re
+f*
+0.498 0 0.482 rg
+321.289 393.018 84.9043 0.2006 re
+f*
+0 g
+261.474 393.218 9.8353 0.2005 re
+f*
+1 g
+271.31 393.218 7.6273 0.2005 re
+f*
+0 g
+278.937 393.218 35.7281 0.2005 re
+f*
+1 g
+314.665 393.218 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+321.088 393.218 85.3057 0.2005 re
+f*
+0 g
+261.274 393.419 10.036 0.2006 re
+f*
+1 g
+271.31 393.419 7.6273 0.2006 re
+f*
+0 g
+278.937 393.419 35.7281 0.2006 re
+f*
+1 g
+314.665 393.419 6.423 0.2006 re
+f*
+0.498 0 0.482 rg
+321.088 393.419 85.5064 0.2006 re
+f*
+0 g
+261.073 393.619 10.2368 0.2006 re
+f*
+1 g
+271.31 393.619 7.6273 0.2006 re
+f*
+0 g
+278.937 393.619 35.5273 0.2006 re
+f*
+1 g
+314.464 393.619 6.423 0.2006 re
+f*
+0.498 0 0.482 rg
+320.887 393.619 85.908 0.2006 re
+f*
+0 g
+260.872 393.82 10.4375 0.2005 re
+f*
+1 g
+271.31 393.82 7.6273 0.2005 re
+f*
+0 g
+278.937 393.82 35.5273 0.2005 re
+f*
+1 g
+314.464 393.82 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+320.887 393.82 86.1087 0.2005 re
+f*
+0 g
+260.671 394.02 10.6382 0.2006 re
+f*
+1 g
+271.31 394.02 7.6273 0.2006 re
+f*
+0 g
+278.937 394.02 35.3266 0.2006 re
+f*
+1 g
+314.263 394.02 6.4231 0.2006 re
+f*
+0.498 0 0.482 rg
+320.687 394.02 86.51 0.2006 re
+f*
+0 g
+260.471 394.221 10.8389 0.2005 re
+f*
+1 g
+271.31 394.221 7.6273 0.2005 re
+f*
+0 g
+278.937 394.221 35.3266 0.2005 re
+f*
+1 g
+314.263 394.221 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+320.486 394.221 86.9115 0.2005 re
+f*
+0 g
+260.27 394.421 11.0396 0.2006 re
+f*
+1 g
+271.31 394.421 7.6273 0.2006 re
+f*
+0 g
+278.937 394.421 35.1259 0.2006 re
+f*
+1 g
+314.063 394.421 6.423 0.2006 re
+f*
+0.498 0 0.482 rg
+320.486 394.421 86.9115 0.2006 re
+f*
+0 g
+260.27 394.622 11.0396 0.2006 re
+f*
+1 g
+271.31 394.622 7.6273 0.2006 re
+f*
+0 g
+278.937 394.622 35.1259 0.2006 re
+f*
+1 g
+314.063 394.622 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+320.285 394.622 87.3129 0.2006 re
+f*
+0 g
+260.069 394.823 11.2403 0.2005 re
+f*
+1 g
+271.31 394.823 7.6273 0.2005 re
+f*
+0 g
+278.937 394.823 34.9251 0.2005 re
+f*
+1 g
+313.862 394.823 6.4231 0.2005 re
+f*
+0.498 0 0.482 rg
+320.285 394.823 87.5137 0.2005 re
+f*
+0 g
+259.868 395.023 11.4411 0.2006 re
+f*
+1 g
+271.31 395.023 7.6273 0.2006 re
+f*
+0 g
+278.937 395.023 34.9251 0.2006 re
+f*
+1 g
+313.862 395.023 6.2224 0.2006 re
+f*
+0.498 0 0.482 rg
+320.084 395.023 87.9151 0.2006 re
+f*
+0 g
+259.668 395.224 11.6418 0.2005 re
+f*
+1 g
+271.31 395.224 7.6273 0.2005 re
+f*
+0 g
+278.937 395.224 34.7245 0.2005 re
+f*
+1 g
+313.661 395.224 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+320.084 395.224 88.1158 0.2005 re
+f*
+0 g
+259.467 395.424 11.8425 0.2006 re
+f*
+1 g
+271.31 395.424 7.6273 0.2006 re
+f*
+0 g
+278.937 395.424 34.7245 0.2006 re
+f*
+1 g
+313.661 395.424 6.2222 0.2006 re
+f*
+0.498 0 0.482 rg
+319.884 395.424 88.5173 0.2006 re
+f*
+0 g
+259.266 395.625 12.0432 0.2005 re
+f*
+1 g
+271.31 395.625 7.6273 0.2005 re
+f*
+0 g
+278.937 395.625 34.5237 0.2005 re
+f*
+1 g
+313.461 395.625 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+319.884 395.625 88.5173 0.2005 re
+f*
+0 g
+259.266 395.825 12.0432 0.2006 re
+f*
+1 g
+271.31 395.825 7.6273 0.2006 re
+f*
+0 g
+278.937 395.825 34.5237 0.2006 re
+f*
+1 g
+313.461 395.825 6.2224 0.2006 re
+f*
+0.498 0 0.482 rg
+319.683 395.825 88.9186 0.2006 re
+f*
+0 g
+259.066 396.026 12.2439 0.2006 re
+f*
+1 g
+271.31 396.026 7.6273 0.2006 re
+f*
+0 g
+278.937 396.026 34.5237 0.2006 re
+f*
+1 g
+313.461 396.026 6.2224 0.2006 re
+f*
+0.498 0 0.482 rg
+319.683 396.026 89.1194 0.2006 re
+f*
+0 g
+258.865 396.227 12.4447 0.2005 re
+f*
+1 g
+271.31 396.227 7.6273 0.2005 re
+f*
+0 g
+278.937 396.227 34.323 0.2005 re
+f*
+1 g
+313.26 396.227 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+319.482 396.227 89.5209 0.2005 re
+f*
+0 g
+258.664 396.427 12.6454 0.2006 re
+f*
+1 g
+271.31 396.427 7.6273 0.2006 re
+f*
+0 g
+278.937 396.427 34.323 0.2006 re
+f*
+1 g
+313.26 396.427 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+319.482 396.427 89.7216 0.2006 re
+f*
+0 g
+258.463 396.628 12.8461 0.2005 re
+f*
+1 g
+271.31 396.628 7.6273 0.2005 re
+f*
+0 g
+278.937 396.628 34.1223 0.2005 re
+f*
+1 g
+313.059 396.628 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+319.281 396.628 89.9223 0.2005 re
+f*
+0 g
+258.463 396.828 12.8461 0.2006 re
+f*
+1 g
+271.31 396.828 7.6273 0.2006 re
+f*
+0 g
+278.937 396.828 34.1223 0.2006 re
+f*
+1 g
+313.059 396.828 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+319.281 396.828 90.123 0.2006 re
+f*
+0 g
+258.263 397.029 13.0468 0.2006 re
+f*
+1 g
+271.31 397.029 7.6273 0.2006 re
+f*
+0 g
+278.937 397.029 34.1223 0.2006 re
+f*
+1 g
+313.059 397.029 6.0215 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 397.029 90.5245 0.2006 re
+f*
+0 g
+258.062 397.229 13.2475 0.2005 re
+f*
+1 g
+271.31 397.229 7.6273 0.2005 re
+f*
+0 g
+278.937 397.229 33.9216 0.2005 re
+f*
+1 g
+312.858 397.229 6.2222 0.2005 re
+f*
+0.498 0 0.482 rg
+319.081 397.229 90.7253 0.2005 re
+f*
+0 g
+258.062 397.43 13.2475 0.2006 re
+f*
+1 g
+271.31 397.43 7.6273 0.2006 re
+f*
+0 g
+278.937 397.43 33.9216 0.2006 re
+f*
+1 g
+312.858 397.43 6.0215 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 397.43 90.926 0.2006 re
+f*
+0 g
+257.861 397.63 13.4483 0.2005 re
+f*
+1 g
+271.31 397.63 7.6273 0.2005 re
+f*
+0 g
+278.937 397.63 33.7209 0.2005 re
+f*
+1 g
+312.658 397.63 6.2222 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 397.63 91.1267 0.2005 re
+f*
+0 g
+257.661 397.831 13.649 0.2006 re
+f*
+1 g
+271.31 397.831 7.6273 0.2006 re
+f*
+0 g
+278.937 397.831 33.7209 0.2006 re
+f*
+1 g
+312.658 397.831 6.2222 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 397.831 91.3274 0.2006 re
+f*
+0 g
+257.46 398.032 13.8497 0.2006 re
+f*
+1 g
+271.31 398.032 7.6273 0.2006 re
+f*
+0 g
+278.937 398.032 33.7209 0.2006 re
+f*
+1 g
+312.658 398.032 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+318.679 398.032 91.7287 0.2006 re
+f*
+0 g
+257.46 398.232 13.8497 0.2005 re
+f*
+1 g
+271.31 398.232 7.6273 0.2005 re
+f*
+0 g
+278.937 398.232 33.5201 0.2005 re
+f*
+1 g
+312.457 398.232 6.2224 0.2005 re
+f*
+0.498 0 0.482 rg
+318.679 398.232 91.7287 0.2005 re
+f*
+0 g
+257.259 398.433 14.0504 0.2006 re
+f*
+1 g
+271.31 398.433 7.6273 0.2006 re
+f*
+0 g
+278.937 398.433 33.5201 0.2006 re
+f*
+1 g
+312.457 398.433 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+318.479 398.433 92.1302 0.2006 re
+f*
+0 g
+257.058 398.633 14.2511 0.2005 re
+f*
+1 g
+271.31 398.633 7.6273 0.2005 re
+f*
+0 g
+278.937 398.633 33.5201 0.2005 re
+f*
+1 g
+312.457 398.633 6.0216 0.2005 re
+f*
+0.498 0 0.482 rg
+318.479 398.633 92.3309 0.2005 re
+f*
+0 g
+257.058 398.834 14.2511 0.2006 re
+f*
+1 g
+271.31 398.834 7.6273 0.2006 re
+f*
+0 g
+278.937 398.834 33.3194 0.2006 re
+f*
+1 g
+312.256 398.834 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+318.479 398.834 92.3309 0.2006 re
+f*
+0 g
+256.858 399.034 14.4519 0.2006 re
+f*
+1 g
+271.31 399.034 7.6273 0.2006 re
+f*
+0 g
+278.937 399.034 33.3194 0.2006 re
+f*
+1 g
+312.256 399.034 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+318.278 399.034 92.7324 0.2006 re
+f*
+0 g
+256.657 399.235 14.6526 0.2005 re
+f*
+1 g
+271.31 399.235 7.6273 0.2005 re
+f*
+0 g
+278.937 399.235 33.3194 0.2005 re
+f*
+1 g
+312.256 399.235 6.0216 0.2005 re
+f*
+0.498 0 0.482 rg
+318.278 399.235 92.9331 0.2005 re
+f*
+0 g
+256.657 399.435 14.6526 0.2005 re
+f*
+1 g
+271.31 399.435 7.6273 0.2005 re
+f*
+0 g
+278.937 399.435 33.1187 0.2005 re
+f*
+1 g
+312.056 399.435 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+318.278 399.435 92.9331 0.2005 re
+f*
+0 g
+256.456 399.636 14.8533 0.2006 re
+f*
+1 g
+271.31 399.636 7.6273 0.2006 re
+f*
+0 g
+278.937 399.636 33.1187 0.2006 re
+f*
+1 g
+312.056 399.636 6.0215 0.2006 re
+f*
+0.498 0 0.482 rg
+318.077 399.636 93.3346 0.2006 re
+f*
+0 g
+256.256 399.837 15.054 0.2006 re
+f*
+1 g
+271.31 399.837 7.6273 0.2006 re
+f*
+0 g
+278.937 399.837 33.1187 0.2006 re
+f*
+1 g
+312.056 399.837 6.0215 0.2006 re
+f*
+0.498 0 0.482 rg
+318.077 399.837 93.5353 0.2006 re
+f*
+0 g
+256.256 400.037 15.054 0.2006 re
+f*
+1 g
+271.31 400.037 7.6273 0.2006 re
+f*
+0 g
+278.937 400.037 32.918 0.2006 re
+f*
+1 g
+311.855 400.037 6.2222 0.2006 re
+f*
+0.498 0 0.482 rg
+318.077 400.037 93.5353 0.2006 re
+f*
+0 g
+256.055 400.238 15.2547 0.2005 re
+f*
+1 g
+271.31 400.238 7.6273 0.2005 re
+f*
+0 g
+278.937 400.238 32.918 0.2005 re
+f*
+1 g
+311.855 400.238 6.0215 0.2005 re
+f*
+0.498 0 0.482 rg
+317.876 400.238 93.9367 0.2005 re
+f*
+0 g
+255.854 400.438 15.4555 0.2006 re
+f*
+1 g
+271.31 400.438 7.6273 0.2006 re
+f*
+0 g
+278.937 400.438 32.918 0.2006 re
+f*
+1 g
+311.855 400.438 6.0215 0.2006 re
+f*
+0.498 0 0.482 rg
+317.876 400.438 93.9367 0.2006 re
+f*
+0 g
+255.854 400.639 15.4555 0.2005 re
+f*
+1 g
+271.31 400.639 7.6273 0.2005 re
+f*
+0 g
+278.937 400.639 32.7173 0.2005 re
+f*
+1 g
+311.654 400.639 6.2222 0.2005 re
+f*
+0.498 0 0.482 rg
+317.876 400.639 94.1375 0.2005 re
+f*
+0 g
+255.653 400.839 15.6562 0.2006 re
+f*
+1 g
+271.31 400.839 7.6273 0.2006 re
+f*
+0 g
+278.937 400.839 32.7173 0.2006 re
+f*
+1 g
+311.654 400.839 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+317.676 400.839 94.5388 0.2006 re
+f*
+0 g
+255.653 401.04 15.6562 0.2006 re
+f*
+1 g
+271.31 401.04 7.6273 0.2006 re
+f*
+0 g
+278.937 401.04 32.7173 0.2006 re
+f*
+1 g
+311.654 401.04 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+317.676 401.04 94.5388 0.2006 re
+f*
+0 g
+255.453 401.241 15.8569 0.2005 re
+f*
+1 g
+271.31 401.241 7.6273 0.2005 re
+f*
+0 g
+278.937 401.241 32.5165 0.2005 re
+f*
+1 g
+311.453 401.241 6.2224 0.2005 re
+f*
+0.498 0 0.482 rg
+317.676 401.241 94.7395 0.2005 re
+f*
+0 g
+255.252 401.441 16.0576 0.2005 re
+f*
+1 g
+271.31 401.441 7.6273 0.2005 re
+f*
+0 g
+278.937 401.441 32.5165 0.2005 re
+f*
+1 g
+311.453 401.441 6.2224 0.2005 re
+f*
+0.498 0 0.482 rg
+317.676 401.441 94.9402 0.2005 re
+f*
+0 g
+255.252 401.642 16.0576 0.2006 re
+f*
+1 g
+271.31 401.642 7.6273 0.2006 re
+f*
+0 g
+278.937 401.642 32.5165 0.2006 re
+f*
+1 g
+311.453 401.642 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+317.475 401.642 95.141 0.2006 re
+f*
+0 g
+255.051 401.842 16.2583 0.2006 re
+f*
+1 g
+271.31 401.842 7.6273 0.2006 re
+f*
+0 g
+278.937 401.842 32.5165 0.2006 re
+f*
+1 g
+311.453 401.842 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+317.475 401.842 95.3417 0.2006 re
+f*
+0 g
+255.051 402.043 16.2583 0.2005 re
+f*
+1 g
+271.31 402.043 7.6273 0.2005 re
+f*
+0 g
+278.937 402.043 32.3158 0.2005 re
+f*
+1 g
+311.253 402.043 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+317.475 402.043 95.3417 0.2005 re
+f*
+0 g
+254.851 402.243 16.4591 0.2005 re
+f*
+1 g
+271.31 402.243 7.6273 0.2005 re
+f*
+0 g
+278.937 402.243 32.3158 0.2005 re
+f*
+1 g
+311.253 402.243 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+317.475 402.243 95.5425 0.2005 re
+f*
+0 g
+254.851 402.444 16.4591 0.2006 re
+f*
+1 g
+271.31 402.444 7.6273 0.2006 re
+f*
+0 g
+278.937 402.444 32.3158 0.2006 re
+f*
+1 g
+311.253 402.444 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+317.274 402.444 95.7432 0.2006 re
+f*
+0 g
+254.65 402.644 16.6598 0.2006 re
+f*
+1 g
+271.31 402.644 7.6273 0.2006 re
+f*
+0 g
+278.937 402.644 32.1151 0.2006 re
+f*
+1 g
+311.052 402.644 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+317.274 402.644 95.9438 0.2006 re
+f*
+0 g
+254.449 402.845 16.8605 0.2006 re
+f*
+1 g
+271.31 402.845 7.6273 0.2006 re
+f*
+0 g
+278.937 402.845 32.1151 0.2006 re
+f*
+1 g
+311.052 402.845 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+317.274 402.845 95.9438 0.2006 re
+f*
+0 g
+254.449 403.046 16.8605 0.2006 re
+f*
+1 g
+271.31 403.046 7.6273 0.2006 re
+f*
+0 g
+278.937 403.046 32.1151 0.2006 re
+f*
+1 g
+311.052 403.046 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+317.274 403.046 96.1446 0.2006 re
+f*
+0 g
+254.248 403.246 17.0612 0.2005 re
+f*
+1 g
+271.31 403.246 7.6273 0.2005 re
+f*
+0 g
+278.937 403.246 32.1151 0.2005 re
+f*
+1 g
+311.052 403.246 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+317.274 403.246 96.1446 0.2005 re
+f*
+0 g
+254.248 403.447 17.0612 0.2005 re
+f*
+1 g
+271.31 403.447 7.6273 0.2005 re
+f*
+0 g
+278.937 403.447 31.9144 0.2005 re
+f*
+1 g
+310.851 403.447 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+317.274 403.447 96.3453 0.2005 re
+f*
+0 g
+254.048 403.647 17.2619 0.2006 re
+f*
+1 g
+271.31 403.647 7.6273 0.2006 re
+f*
+0 g
+278.937 403.647 31.9144 0.2006 re
+f*
+1 g
+310.851 403.647 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 403.647 96.546 0.2006 re
+f*
+0 g
+254.048 403.848 17.2619 0.2006 re
+f*
+1 g
+271.31 403.848 7.6273 0.2006 re
+f*
+0 g
+278.937 403.848 31.9144 0.2006 re
+f*
+1 g
+310.851 403.848 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 403.848 96.7467 0.2006 re
+f*
+0 g
+253.847 404.048 17.4626 0.2005 re
+f*
+1 g
+271.31 404.048 7.6273 0.2005 re
+f*
+0 g
+278.937 404.048 31.9144 0.2005 re
+f*
+1 g
+310.851 404.048 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+317.074 404.048 13.6489 0.2005 re
+f*
+1 g
+330.722 404.048 8.4302 0.2005 re
+f*
+0.498 0 0.482 rg
+339.153 404.048 74.6676 0.2005 re
+f*
+0 g
+253.847 404.249 17.4626 0.2005 re
+f*
+1 g
+271.31 404.249 7.6273 0.2005 re
+f*
+0 g
+278.937 404.249 31.7137 0.2005 re
+f*
+1 g
+310.651 404.249 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+317.074 404.249 12.2439 0.2005 re
+f*
+1 g
+329.317 404.249 11.2403 0.2005 re
+f*
+0.498 0 0.482 rg
+340.558 404.249 73.4633 0.2005 re
+f*
+0 g
+253.646 404.449 17.6633 0.2006 re
+f*
+1 g
+271.31 404.449 7.6273 0.2006 re
+f*
+0 g
+278.937 404.449 31.7137 0.2006 re
+f*
+1 g
+310.651 404.449 6.423 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 404.449 11.0395 0.2006 re
+f*
+1 g
+328.113 404.449 13.4483 0.2006 re
+f*
+0.498 0 0.482 rg
+341.561 404.449 72.4597 0.2006 re
+f*
+0 g
+253.646 404.65 17.6633 0.2006 re
+f*
+1 g
+271.31 404.65 7.6273 0.2006 re
+f*
+0 g
+278.937 404.65 31.7137 0.2006 re
+f*
+1 g
+310.651 404.65 6.423 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 404.65 9.8352 0.2006 re
+f*
+1 g
+326.909 404.65 15.6561 0.2006 re
+f*
+0.498 0 0.482 rg
+342.565 404.65 71.6568 0.2006 re
+f*
+0 g
+253.445 404.851 17.8641 0.2005 re
+f*
+1 g
+271.31 404.851 7.6273 0.2005 re
+f*
+0 g
+278.937 404.851 31.7137 0.2005 re
+f*
+1 g
+310.651 404.851 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+317.074 404.851 8.8316 0.2005 re
+f*
+1 g
+325.905 404.851 17.2619 0.2005 re
+f*
+0.498 0 0.482 rg
+343.167 404.851 71.0546 0.2005 re
+f*
+0 g
+253.445 405.051 17.8641 0.2006 re
+f*
+1 g
+271.31 405.051 7.6273 0.2006 re
+f*
+0 g
+278.937 405.051 31.7137 0.2006 re
+f*
+1 g
+310.651 405.051 6.423 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 405.051 8.0288 0.2006 re
+f*
+1 g
+325.102 405.051 7.0251 0.2006 re
+f*
+0 g
+332.127 405.051 6.2223 0.2006 re
+f*
+1 g
+338.35 405.051 5.6201 0.2006 re
+f*
+0.498 0 0.482 rg
+343.97 405.051 70.4526 0.2006 re
+f*
+0 g
+253.445 405.252 17.8641 0.2005 re
+f*
+1 g
+271.31 405.252 7.6273 0.2005 re
+f*
+0 g
+278.937 405.252 31.513 0.2005 re
+f*
+1 g
+310.45 405.252 6.4229 0.2005 re
+f*
+0.498 0 0.482 rg
+316.873 405.252 7.6274 0.2005 re
+f*
+1 g
+324.5 405.252 5.6201 0.2005 re
+f*
+0 g
+330.12 405.252 9.8353 0.2005 re
+f*
+1 g
+339.956 405.252 4.6165 0.2005 re
+f*
+0.498 0 0.482 rg
+344.572 405.252 69.8504 0.2005 re
+f*
+0 g
+253.245 405.452 18.0648 0.2006 re
+f*
+1 g
+271.31 405.452 7.6273 0.2006 re
+f*
+0 g
+278.937 405.452 31.513 0.2006 re
+f*
+1 g
+310.45 405.452 6.4229 0.2006 re
+f*
+0.498 0 0.482 rg
+316.873 405.452 7.0252 0.2006 re
+f*
+1 g
+323.898 405.452 5.2187 0.2006 re
+f*
+0 g
+329.117 405.452 12.0432 0.2006 re
+f*
+1 g
+341.16 405.452 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+344.973 405.452 69.6497 0.2006 re
+f*
+0 g
+253.245 405.653 18.0648 0.2006 re
+f*
+1 g
+271.31 405.653 7.6273 0.2006 re
+f*
+0 g
+278.937 405.653 31.513 0.2006 re
+f*
+1 g
+310.45 405.653 6.4229 0.2006 re
+f*
+0.498 0 0.482 rg
+316.873 405.653 6.4231 0.2006 re
+f*
+1 g
+323.296 405.653 4.8172 0.2006 re
+f*
+0 g
+328.113 405.653 13.8497 0.2006 re
+f*
+1 g
+341.963 405.653 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+345.576 405.653 69.0475 0.2006 re
+f*
+0 g
+253.044 405.853 18.2655 0.2006 re
+f*
+1 g
+271.31 405.853 7.6273 0.2006 re
+f*
+0 g
+278.937 405.853 31.513 0.2006 re
+f*
+1 g
+310.45 405.853 6.4229 0.2006 re
+f*
+0.498 0 0.482 rg
+316.873 405.853 5.821 0.2006 re
+f*
+1 g
+322.694 405.853 4.8172 0.2006 re
+f*
+0 g
+327.511 405.853 15.0539 0.2006 re
+f*
+1 g
+342.565 405.853 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+345.977 405.853 68.8468 0.2006 re
+f*
+0 g
+253.044 406.054 18.2655 0.2005 re
+f*
+1 g
+271.31 406.054 7.6273 0.2005 re
+f*
+0 g
+278.937 406.054 31.513 0.2005 re
+f*
+1 g
+310.45 406.054 6.4229 0.2005 re
+f*
+0.498 0 0.482 rg
+316.873 406.054 5.2188 0.2005 re
+f*
+1 g
+322.092 406.054 4.6165 0.2005 re
+f*
+0 g
+326.708 406.054 16.459 0.2005 re
+f*
+1 g
+343.167 406.054 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+346.379 406.054 68.4453 0.2005 re
+f*
+0 g
+252.843 406.255 18.4662 0.2005 re
+f*
+1 g
+271.31 406.255 7.6273 0.2005 re
+f*
+0 g
+278.937 406.255 31.3122 0.2005 re
+f*
+1 g
+310.249 406.255 6.6237 0.2005 re
+f*
+0.498 0 0.482 rg
+316.873 406.255 4.8174 0.2005 re
+f*
+1 g
+321.69 406.255 4.6165 0.2005 re
+f*
+0 g
+326.307 406.255 17.4626 0.2005 re
+f*
+1 g
+343.769 406.255 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+346.78 406.255 68.0438 0.2005 re
+f*
+0 g
+252.843 406.455 18.4662 0.2006 re
+f*
+1 g
+271.31 406.455 7.6273 0.2006 re
+f*
+0 g
+278.937 406.455 31.3122 0.2006 re
+f*
+1 g
+310.249 406.455 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+316.873 406.455 4.2152 0.2006 re
+f*
+1 g
+321.088 406.455 4.6165 0.2006 re
+f*
+0 g
+325.704 406.455 18.4662 0.2006 re
+f*
+1 g
+344.171 406.455 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+347.382 406.455 67.6425 0.2006 re
+f*
+0 g
+252.843 406.656 18.4662 0.2006 re
+f*
+1 g
+271.31 406.656 7.6273 0.2006 re
+f*
+0 g
+278.937 406.656 31.3122 0.2006 re
+f*
+1 g
+310.249 406.656 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+316.873 406.656 3.8138 0.2006 re
+f*
+1 g
+320.687 406.656 4.6165 0.2006 re
+f*
+0 g
+325.303 406.656 19.269 0.2006 re
+f*
+1 g
+344.572 406.656 3.2116 0.2006 re
+f*
+0.498 0 0.482 rg
+347.784 406.656 67.241 0.2006 re
+f*
+0 g
+252.643 406.856 18.6669 0.2005 re
+f*
+1 g
+271.31 406.856 7.6273 0.2005 re
+f*
+0 g
+278.937 406.856 31.3122 0.2005 re
+f*
+1 g
+310.249 406.856 6.8245 0.2005 re
+f*
+0.498 0 0.482 rg
+317.074 406.856 3.0108 0.2005 re
+f*
+1 g
+320.084 406.856 4.8172 0.2005 re
+f*
+0 g
+324.902 406.856 20.0719 0.2005 re
+f*
+1 g
+344.973 406.856 3.2116 0.2005 re
+f*
+0.498 0 0.482 rg
+348.185 406.856 67.0402 0.2005 re
+f*
+0 g
+252.643 407.057 18.6669 0.2006 re
+f*
+1 g
+271.31 407.057 7.6273 0.2006 re
+f*
+0 g
+278.937 407.057 31.3122 0.2006 re
+f*
+1 g
+310.249 407.057 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 407.057 2.4086 0.2006 re
+f*
+1 g
+319.482 407.057 4.8173 0.2006 re
+f*
+0 g
+324.299 407.057 21.0755 0.2006 re
+f*
+1 g
+345.375 407.057 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+348.386 407.057 66.8395 0.2006 re
+f*
+0 g
+252.442 407.257 18.8677 0.2005 re
+f*
+1 g
+271.31 407.257 7.6273 0.2005 re
+f*
+0 g
+278.937 407.257 31.1115 0.2005 re
+f*
+1 g
+310.048 407.257 7.0252 0.2005 re
+f*
+0.498 0 0.482 rg
+317.074 407.257 2.0071 0.2005 re
+f*
+1 g
+319.081 407.257 5.0181 0.2005 re
+f*
+0 g
+324.099 407.257 21.4769 0.2005 re
+f*
+1 g
+345.576 407.257 3.2116 0.2005 re
+f*
+0.498 0 0.482 rg
+348.787 407.257 66.438 0.2005 re
+f*
+0 g
+252.442 407.458 18.8677 0.2006 re
+f*
+1 g
+271.31 407.458 7.6273 0.2006 re
+f*
+0 g
+278.937 407.458 31.1115 0.2006 re
+f*
+1 g
+310.048 407.458 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 407.458 1.405 0.2006 re
+f*
+1 g
+318.479 407.458 5.2187 0.2006 re
+f*
+0 g
+323.697 407.458 22.2798 0.2006 re
+f*
+1 g
+345.977 407.458 3.2116 0.2006 re
+f*
+0.498 0 0.482 rg
+349.189 407.458 66.2374 0.2006 re
+f*
+0 g
+252.442 407.658 18.8677 0.2006 re
+f*
+1 g
+271.31 407.658 7.6273 0.2006 re
+f*
+0 g
+278.937 407.658 31.1115 0.2006 re
+f*
+1 g
+310.048 407.658 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+317.074 407.658 1.0035 0.2006 re
+f*
+1 g
+318.077 407.658 5.2188 0.2006 re
+f*
+0 g
+323.296 407.658 22.882 0.2006 re
+f*
+1 g
+346.178 407.658 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+349.389 407.658 66.0367 0.2006 re
+f*
+0 g
+252.241 407.859 19.0684 0.2005 re
+f*
+1 g
+271.31 407.859 7.6273 0.2005 re
+f*
+0 g
+278.937 407.859 31.1115 0.2005 re
+f*
+1 g
+310.048 407.859 7.0252 0.2005 re
+f*
+0.498 0 0.482 rg
+317.074 407.859 0.4014 0.2005 re
+f*
+1 g
+317.475 407.859 5.4194 0.2005 re
+f*
+0 g
+322.894 407.859 23.6849 0.2005 re
+f*
+1 g
+346.579 407.859 3.2116 0.2005 re
+f*
+0.498 0 0.482 rg
+349.791 407.859 65.8359 0.2005 re
+f*
+0 g
+252.241 408.06 19.0684 0.2006 re
+f*
+1 g
+271.31 408.06 7.6273 0.2006 re
+f*
+0 g
+278.937 408.06 31.1115 0.2006 re
+f*
+1 g
+310.048 408.06 12.6454 0.2006 re
+f*
+0 g
+322.694 408.06 24.0863 0.2006 re
+f*
+1 g
+346.78 408.06 3.2114 0.2006 re
+f*
+0.498 0 0.482 rg
+349.991 408.06 65.6353 0.2006 re
+f*
+0 g
+252.241 408.26 19.0684 0.2005 re
+f*
+1 g
+271.31 408.26 7.6273 0.2005 re
+f*
+0 g
+278.937 408.26 31.1115 0.2005 re
+f*
+1 g
+310.048 408.26 12.2439 0.2005 re
+f*
+0 g
+322.292 408.26 24.8892 0.2005 re
+f*
+1 g
+347.181 408.26 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+350.393 408.26 65.2338 0.2005 re
+f*
+0 g
+252.04 408.461 19.2691 0.2006 re
+f*
+1 g
+271.31 408.461 7.6273 0.2006 re
+f*
+0 g
+278.937 408.461 31.1115 0.2006 re
+f*
+1 g
+310.048 408.461 12.0432 0.2006 re
+f*
+0 g
+322.092 408.461 25.2906 0.2006 re
+f*
+1 g
+347.382 408.461 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+350.594 408.461 65.2338 0.2006 re
+f*
+0 g
+252.04 408.661 19.2691 0.2006 re
+f*
+1 g
+271.31 408.661 7.6273 0.2006 re
+f*
+0 g
+278.937 408.661 30.9108 0.2006 re
+f*
+1 g
+309.848 408.661 11.8425 0.2006 re
+f*
+0 g
+321.69 408.661 25.8927 0.2006 re
+f*
+1 g
+347.583 408.661 3.2116 0.2006 re
+f*
+0.498 0 0.482 rg
+350.794 408.661 65.033 0.2006 re
+f*
+0 g
+252.04 408.862 19.2691 0.2005 re
+f*
+1 g
+271.31 408.862 7.6273 0.2005 re
+f*
+0 g
+278.937 408.862 30.9108 0.2005 re
+f*
+1 g
+309.848 408.862 11.6417 0.2005 re
+f*
+0 g
+321.489 408.862 26.2943 0.2005 re
+f*
+1 g
+347.784 408.862 3.4122 0.2005 re
+f*
+0.498 0 0.482 rg
+351.196 408.862 64.6316 0.2005 re
+f*
+0 g
+251.84 409.062 19.4698 0.2006 re
+f*
+1 g
+271.31 409.062 7.6273 0.2006 re
+f*
+0 g
+278.937 409.062 30.9108 0.2006 re
+f*
+1 g
+309.848 409.062 11.2403 0.2006 re
+f*
+0 g
+321.088 409.062 26.8963 0.2006 re
+f*
+1 g
+347.984 409.062 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+351.397 409.062 64.6317 0.2006 re
+f*
+0 g
+251.84 409.263 19.4698 0.2005 re
+f*
+1 g
+271.31 409.263 7.6273 0.2005 re
+f*
+0 g
+278.937 409.263 30.9108 0.2005 re
+f*
+1 g
+309.848 409.263 11.0395 0.2005 re
+f*
+0 g
+320.887 409.263 27.4986 0.2005 re
+f*
+1 g
+348.386 409.263 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+351.597 409.263 64.431 0.2005 re
+f*
+0 g
+251.84 409.463 19.4698 0.2006 re
+f*
+1 g
+271.31 409.463 7.6273 0.2006 re
+f*
+0 g
+278.937 409.463 30.9108 0.2006 re
+f*
+1 g
+309.848 409.463 10.8389 0.2006 re
+f*
+0 g
+320.687 409.463 27.6992 0.2006 re
+f*
+1 g
+348.386 409.463 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+351.999 409.463 64.0296 0.2006 re
+f*
+0 g
+251.639 409.664 19.6705 0.2006 re
+f*
+1 g
+271.31 409.664 7.6273 0.2006 re
+f*
+0 g
+278.937 409.664 30.9108 0.2006 re
+f*
+1 g
+309.848 409.664 10.4374 0.2006 re
+f*
+0 g
+320.285 409.664 28.3014 0.2006 re
+f*
+1 g
+348.586 409.664 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+352.2 409.664 64.0294 0.2006 re
+f*
+0 g
+251.639 409.865 19.6705 0.2005 re
+f*
+1 g
+271.31 409.865 7.6273 0.2005 re
+f*
+0 g
+278.937 409.865 30.9108 0.2005 re
+f*
+1 g
+309.848 409.865 10.2367 0.2005 re
+f*
+0 g
+320.084 409.865 28.7029 0.2005 re
+f*
+1 g
+348.787 409.865 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+352.4 409.865 63.8287 0.2005 re
+f*
+0 g
+251.639 410.065 19.6705 0.2006 re
+f*
+1 g
+271.31 410.065 7.6273 0.2006 re
+f*
+0 g
+278.937 410.065 30.7101 0.2006 re
+f*
+1 g
+309.647 410.065 10.2366 0.2006 re
+f*
+0 g
+319.884 410.065 29.1043 0.2006 re
+f*
+1 g
+348.988 410.065 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+352.601 410.065 63.628 0.2006 re
+f*
+0 g
+251.438 410.266 19.8713 0.2005 re
+f*
+1 g
+271.31 410.266 7.6273 0.2005 re
+f*
+0 g
+278.937 410.266 30.7101 0.2005 re
+f*
+1 g
+309.647 410.266 10.036 0.2005 re
+f*
+0 g
+319.683 410.266 29.5057 0.2005 re
+f*
+1 g
+349.189 410.266 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+352.802 410.266 63.4272 0.2005 re
+f*
+0 g
+251.438 410.466 19.8713 0.2006 re
+f*
+1 g
+271.31 410.466 7.6273 0.2006 re
+f*
+0 g
+278.937 410.466 30.7101 0.2006 re
+f*
+1 g
+309.647 410.466 9.8352 0.2006 re
+f*
+0 g
+319.482 410.466 29.9072 0.2006 re
+f*
+1 g
+349.389 410.466 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+353.002 410.466 63.4274 0.2006 re
+f*
+0 g
+251.438 410.667 19.8713 0.2005 re
+f*
+1 g
+271.31 410.667 7.6273 0.2005 re
+f*
+0 g
+278.937 410.667 30.7101 0.2005 re
+f*
+1 g
+309.647 410.667 9.6345 0.2005 re
+f*
+0 g
+319.281 410.667 30.3086 0.2005 re
+f*
+1 g
+349.59 410.667 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 410.667 63.2266 0.2005 re
+f*
+0 g
+251.438 410.867 19.8713 0.2006 re
+f*
+1 g
+271.31 410.867 7.6273 0.2006 re
+f*
+0 g
+278.937 410.867 30.7101 0.2006 re
+f*
+1 g
+309.647 410.867 9.4337 0.2006 re
+f*
+0 g
+319.081 410.867 30.5094 0.2006 re
+f*
+1 g
+349.59 410.867 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+353.404 410.867 63.0259 0.2006 re
+f*
+0 g
+251.238 411.068 20.072 0.2006 re
+f*
+1 g
+271.31 411.068 7.6273 0.2006 re
+f*
+0 g
+278.937 411.068 30.7101 0.2006 re
+f*
+1 g
+309.647 411.068 9.233 0.2006 re
+f*
+0 g
+318.88 411.068 30.9109 0.2006 re
+f*
+1 g
+349.791 411.068 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+353.605 411.068 62.8252 0.2006 re
+f*
+0 g
+251.238 411.268 20.072 0.2005 re
+f*
+1 g
+271.31 411.268 7.6273 0.2005 re
+f*
+0 g
+278.937 411.268 30.7101 0.2005 re
+f*
+1 g
+309.647 411.268 9.0324 0.2005 re
+f*
+0 g
+318.679 411.268 31.3121 0.2005 re
+f*
+1 g
+349.991 411.268 3.8138 0.2005 re
+f*
+0.498 0 0.482 rg
+353.805 411.268 28.7028 0.2005 re
+f*
+1 g
+382.508 411.268 1.2043 0.2005 re
+f*
+0.498 0 0.482 rg
+383.712 411.268 23.2835 0.2005 re
+f*
+1 g
+406.996 411.268 1.6057 0.2005 re
+f*
+0.498 0 0.482 rg
+408.602 411.268 8.0288 0.2005 re
+f*
+0 g
+251.238 411.469 20.072 0.2006 re
+f*
+1 g
+271.31 411.469 7.6273 0.2006 re
+f*
+0 g
+278.937 411.469 30.7101 0.2006 re
+f*
+1 g
+309.647 411.469 8.8316 0.2006 re
+f*
+0 g
+318.479 411.469 31.5129 0.2006 re
+f*
+1 g
+349.991 411.469 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+354.006 411.469 27.0972 0.2006 re
+f*
+1 g
+381.103 411.469 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+384.917 411.469 20.4734 0.2006 re
+f*
+1 g
+405.39 411.469 4.6166 0.2006 re
+f*
+0.498 0 0.482 rg
+410.007 411.469 6.6237 0.2006 re
+f*
+0 g
+251.238 411.67 20.072 0.2005 re
+f*
+1 g
+271.31 411.67 7.6273 0.2005 re
+f*
+0 g
+278.937 411.67 30.5094 0.2005 re
+f*
+1 g
+309.446 411.67 8.8316 0.2005 re
+f*
+0 g
+318.278 411.67 31.9144 0.2005 re
+f*
+1 g
+350.192 411.67 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+354.207 411.67 26.2942 0.2005 re
+f*
+1 g
+380.501 411.67 4.8173 0.2005 re
+f*
+0.498 0 0.482 rg
+385.318 411.67 19.269 0.2005 re
+f*
+1 g
+404.587 411.67 6.0216 0.2005 re
+f*
+0.498 0 0.482 rg
+410.609 411.67 6.0216 0.2005 re
+f*
+0 g
+251.037 411.87 20.2727 0.2006 re
+f*
+1 g
+271.31 411.87 7.6273 0.2006 re
+f*
+0 g
+278.937 411.87 30.5094 0.2006 re
+f*
+1 g
+309.446 411.87 8.6308 0.2006 re
+f*
+0 g
+318.077 411.87 32.3159 0.2006 re
+f*
+1 g
+350.393 411.87 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+354.407 411.87 6.2223 0.2006 re
+f*
+1 g
+360.63 411.87 9.6345 0.2006 re
+f*
+0.498 0 0.482 rg
+370.264 411.87 9.6346 0.2006 re
+f*
+1 g
+379.899 411.87 5.8208 0.2006 re
+f*
+0.498 0 0.482 rg
+385.72 411.87 2.6093 0.2006 re
+f*
+1 g
+388.329 411.87 0.2008 0.2006 re
+f*
+0.498 0 0.482 rg
+388.53 411.87 6.2223 0.2006 re
+f*
+1 g
+394.752 411.87 0.2007 0.2006 re
+f*
+0.498 0 0.482 rg
+394.953 411.87 9.0324 0.2006 re
+f*
+1 g
+403.985 411.87 7.2259 0.2006 re
+f*
+0.498 0 0.482 rg
+411.211 411.87 5.4194 0.2006 re
+f*
+0 g
+251.037 412.071 20.2727 0.2006 re
+f*
+1 g
+271.31 412.071 7.6273 0.2006 re
+f*
+0 g
+278.937 412.071 30.5094 0.2006 re
+f*
+1 g
+309.446 412.071 8.4301 0.2006 re
+f*
+0 g
+317.876 412.071 32.5166 0.2006 re
+f*
+1 g
+350.393 412.071 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+354.407 412.071 6.2223 0.2006 re
+f*
+1 g
+360.63 412.071 9.6345 0.2006 re
+f*
+0.498 0 0.482 rg
+370.264 412.071 9.2331 0.2006 re
+f*
+1 g
+379.497 412.071 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+382.307 412.071 1.6058 0.2006 re
+f*
+1 g
+383.913 412.071 2.2078 0.2006 re
+f*
+0.498 0 0.482 rg
+386.121 412.071 2.2079 0.2006 re
+f*
+1 g
+388.329 412.071 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+394.953 412.071 8.6309 0.2006 re
+f*
+1 g
+403.584 412.071 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+406.996 412.071 1.8065 0.2006 re
+f*
+1 g
+408.802 412.071 2.6093 0.2006 re
+f*
+0.498 0 0.482 rg
+411.412 412.071 5.4194 0.2006 re
+f*
+0 g
+251.037 412.271 20.2727 0.2005 re
+f*
+1 g
+271.31 412.271 7.6273 0.2005 re
+f*
+0 g
+278.937 412.271 30.5094 0.2005 re
+f*
+1 g
+309.446 412.271 8.2295 0.2005 re
+f*
+0 g
+317.676 412.271 32.9179 0.2005 re
+f*
+1 g
+350.594 412.271 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+354.608 412.271 6.0216 0.2005 re
+f*
+1 g
+360.63 412.271 9.6345 0.2005 re
+f*
+0.498 0 0.482 rg
+370.264 412.271 8.8317 0.2005 re
+f*
+1 g
+379.096 412.271 2.2079 0.2005 re
+f*
+0.498 0 0.482 rg
+381.304 412.271 3.4122 0.2005 re
+f*
+1 g
+384.716 412.271 1.6057 0.2005 re
+f*
+0.498 0 0.482 rg
+386.322 412.271 2.0072 0.2005 re
+f*
+1 g
+388.329 412.271 6.6238 0.2005 re
+f*
+0.498 0 0.482 rg
+394.953 412.271 8.2295 0.2005 re
+f*
+1 g
+403.182 412.271 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+406.193 412.271 3.613 0.2005 re
+f*
+1 g
+409.806 412.271 2.0071 0.2005 re
+f*
+0.498 0 0.482 rg
+411.813 412.271 5.018 0.2005 re
+f*
+0 g
+251.037 412.472 20.2727 0.2006 re
+f*
+1 g
+271.31 412.472 7.6273 0.2006 re
+f*
+0 g
+278.937 412.472 30.5094 0.2006 re
+f*
+1 g
+309.446 412.472 8.0287 0.2006 re
+f*
+0 g
+317.475 412.472 21.0756 0.2006 re
+f*
+1 g
+338.551 412.472 2.81 0.2006 re
+f*
+0 g
+341.361 412.472 9.2331 0.2006 re
+f*
+1 g
+350.594 412.472 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+354.809 412.472 5.8209 0.2006 re
+f*
+1 g
+360.63 412.472 9.6345 0.2006 re
+f*
+0.498 0 0.482 rg
+370.264 412.472 8.4303 0.2006 re
+f*
+1 g
+378.695 412.472 2.2079 0.2006 re
+f*
+0.498 0 0.482 rg
+380.902 412.472 4.4158 0.2006 re
+f*
+1 g
+385.318 412.472 1.405 0.2006 re
+f*
+0.498 0 0.482 rg
+386.723 412.472 1.6057 0.2006 re
+f*
+1 g
+388.329 412.472 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+394.953 412.472 7.8281 0.2006 re
+f*
+1 g
+402.781 412.472 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+405.591 412.472 4.8172 0.2006 re
+f*
+1 g
+410.408 412.472 1.8065 0.2006 re
+f*
+0.498 0 0.482 rg
+412.215 412.472 4.6165 0.2006 re
+f*
+0 g
+250.836 412.672 20.4734 0.2005 re
+f*
+1 g
+271.31 412.672 7.6273 0.2005 re
+f*
+0 g
+278.937 412.672 30.5094 0.2005 re
+f*
+1 g
+309.446 412.672 8.0287 0.2005 re
+f*
+0 g
+317.475 412.672 19.8713 0.2005 re
+f*
+1 g
+337.346 412.672 5.0179 0.2005 re
+f*
+0 g
+342.364 412.672 8.4303 0.2005 re
+f*
+1 g
+350.794 412.672 4.215 0.2005 re
+f*
+0.498 0 0.482 rg
+355.01 412.672 8.631 0.2005 re
+f*
+1 g
+363.641 412.672 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 412.672 11.2403 0.2005 re
+f*
+1 g
+378.494 412.672 2.0072 0.2005 re
+f*
+0.498 0 0.482 rg
+380.501 412.672 5.018 0.2005 re
+f*
+1 g
+385.519 412.672 1.4051 0.2005 re
+f*
+0.498 0 0.482 rg
+386.924 412.672 1.4049 0.2005 re
+f*
+1 g
+388.329 412.672 3.4123 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 412.672 10.6381 0.2005 re
+f*
+1 g
+402.379 412.672 2.8101 0.2005 re
+f*
+0.498 0 0.482 rg
+405.189 412.672 5.6201 0.2005 re
+f*
+1 g
+410.81 412.672 1.6058 0.2005 re
+f*
+0.498 0 0.482 rg
+412.415 412.672 4.4158 0.2005 re
+f*
+0 g
+250.836 412.873 20.4734 0.2006 re
+f*
+1 g
+271.31 412.873 7.6273 0.2006 re
+f*
+0 g
+278.937 412.873 30.5094 0.2006 re
+f*
+1 g
+309.446 412.873 7.828 0.2006 re
+f*
+0 g
+317.274 412.873 19.4698 0.2006 re
+f*
+1 g
+336.744 412.873 6.2223 0.2006 re
+f*
+0 g
+342.966 412.873 8.0287 0.2006 re
+f*
+1 g
+350.995 412.873 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+355.21 412.873 8.4302 0.2006 re
+f*
+1 g
+363.641 412.873 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 412.873 10.8389 0.2006 re
+f*
+1 g
+378.092 412.873 2.2079 0.2006 re
+f*
+0.498 0 0.482 rg
+380.3 412.873 5.6201 0.2006 re
+f*
+1 g
+385.92 412.873 1.2043 0.2006 re
+f*
+0.498 0 0.482 rg
+387.125 412.873 1.2043 0.2006 re
+f*
+1 g
+388.329 412.873 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 412.873 10.4374 0.2006 re
+f*
+1 g
+402.179 412.873 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+404.989 412.873 6.2223 0.2006 re
+f*
+1 g
+411.211 412.873 1.405 0.2006 re
+f*
+0.498 0 0.482 rg
+412.616 412.873 4.4159 0.2006 re
+f*
+0 g
+250.836 413.073 20.4734 0.2006 re
+f*
+1 g
+271.31 413.073 7.6273 0.2006 re
+f*
+0 g
+278.937 413.073 13.0467 0.2006 re
+f*
+1 g
+291.984 413.073 10.036 0.2006 re
+f*
+0 g
+302.02 413.073 7.4267 0.2006 re
+f*
+1 g
+309.446 413.073 7.6273 0.2006 re
+f*
+0 g
+317.074 413.073 19.0683 0.2006 re
+f*
+1 g
+336.142 413.073 7.2259 0.2006 re
+f*
+0 g
+343.368 413.073 7.6273 0.2006 re
+f*
+1 g
+350.995 413.073 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+355.21 413.073 8.4302 0.2006 re
+f*
+1 g
+363.641 413.073 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 413.073 10.6382 0.2006 re
+f*
+1 g
+377.892 413.073 2.2079 0.2006 re
+f*
+0.498 0 0.482 rg
+380.099 413.073 6.0215 0.2006 re
+f*
+1 g
+386.121 413.073 1.2043 0.2006 re
+f*
+0.498 0 0.482 rg
+387.325 413.073 1.0036 0.2006 re
+f*
+1 g
+388.329 413.073 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 413.073 10.036 0.2006 re
+f*
+1 g
+401.777 413.073 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+404.788 413.073 6.8244 0.2006 re
+f*
+1 g
+411.612 413.073 1.2043 0.2006 re
+f*
+0.498 0 0.482 rg
+412.817 413.073 4.2152 0.2006 re
+f*
+0 g
+250.836 413.274 20.4734 0.2005 re
+f*
+1 g
+271.31 413.274 7.6273 0.2005 re
+f*
+0 g
+278.937 413.274 13.0467 0.2005 re
+f*
+1 g
+291.984 413.274 10.036 0.2005 re
+f*
+0 g
+302.02 413.274 7.4267 0.2005 re
+f*
+1 g
+309.446 413.274 7.4265 0.2005 re
+f*
+0 g
+316.873 413.274 18.667 0.2005 re
+f*
+1 g
+335.54 413.274 3.2115 0.2005 re
+f*
+0 g
+338.751 413.274 2.8101 0.2005 re
+f*
+1 g
+341.561 413.274 2.2079 0.2005 re
+f*
+0 g
+343.769 413.274 7.4266 0.2005 re
+f*
+1 g
+351.196 413.274 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+355.411 413.274 8.2295 0.2005 re
+f*
+1 g
+363.641 413.274 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 413.274 10.4375 0.2005 re
+f*
+1 g
+377.691 413.274 2.2079 0.2005 re
+f*
+0.498 0 0.482 rg
+379.899 413.274 6.4229 0.2005 re
+f*
+1 g
+386.322 413.274 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+387.325 413.274 1.0036 0.2005 re
+f*
+1 g
+388.329 413.274 3.4123 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 413.274 9.8352 0.2005 re
+f*
+1 g
+401.576 413.274 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+404.587 413.274 7.2259 0.2005 re
+f*
+1 g
+411.813 413.274 1.2044 0.2005 re
+f*
+0.498 0 0.482 rg
+413.017 413.274 4.0144 0.2005 re
+f*
+0 g
+250.836 413.475 20.4734 0.2006 re
+f*
+1 g
+271.31 413.475 7.6273 0.2006 re
+f*
+0 g
+278.937 413.475 13.0467 0.2006 re
+f*
+1 g
+291.984 413.475 10.036 0.2006 re
+f*
+0 g
+302.02 413.475 7.4267 0.2006 re
+f*
+1 g
+309.446 413.475 7.4265 0.2006 re
+f*
+0 g
+316.873 413.475 18.2655 0.2006 re
+f*
+1 g
+335.138 413.475 3.0108 0.2006 re
+f*
+0 g
+338.149 413.475 4.2151 0.2006 re
+f*
+1 g
+342.364 413.475 1.8065 0.2006 re
+f*
+0 g
+344.171 413.475 7.0252 0.2006 re
+f*
+1 g
+351.196 413.475 4.4158 0.2006 re
+f*
+0.498 0 0.482 rg
+355.612 413.475 8.0288 0.2006 re
+f*
+1 g
+363.641 413.475 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 413.475 10.2367 0.2006 re
+f*
+1 g
+377.49 413.475 2.208 0.2006 re
+f*
+0.498 0 0.482 rg
+379.698 413.475 6.8244 0.2006 re
+f*
+1 g
+386.522 413.475 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+387.526 413.475 0.8028 0.2006 re
+f*
+1 g
+388.329 413.475 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 413.475 9.6345 0.2006 re
+f*
+1 g
+401.376 413.475 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+404.386 413.475 7.6274 0.2006 re
+f*
+1 g
+412.014 413.475 1.2042 0.2006 re
+f*
+0.498 0 0.482 rg
+413.218 413.475 3.8138 0.2006 re
+f*
+0 g
+250.836 413.675 20.4734 0.2005 re
+f*
+1 g
+271.31 413.675 7.6273 0.2005 re
+f*
+0 g
+278.937 413.675 13.0467 0.2005 re
+f*
+1 g
+291.984 413.675 10.036 0.2005 re
+f*
+0 g
+302.02 413.675 7.4267 0.2005 re
+f*
+1 g
+309.446 413.675 7.2258 0.2005 re
+f*
+0 g
+316.672 413.675 18.2655 0.2005 re
+f*
+1 g
+334.938 413.675 2.8101 0.2005 re
+f*
+0 g
+337.748 413.675 5.018 0.2005 re
+f*
+1 g
+342.766 413.675 1.6057 0.2005 re
+f*
+0 g
+344.371 413.675 6.8245 0.2005 re
+f*
+1 g
+351.196 413.675 4.6165 0.2005 re
+f*
+0.498 0 0.482 rg
+355.812 413.675 7.8281 0.2005 re
+f*
+1 g
+363.641 413.675 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 413.675 10.036 0.2005 re
+f*
+1 g
+377.289 413.675 2.2079 0.2005 re
+f*
+0.498 0 0.482 rg
+379.497 413.675 7.2259 0.2005 re
+f*
+1 g
+386.723 413.675 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+387.727 413.675 0.6021 0.2005 re
+f*
+1 g
+388.329 413.675 3.4123 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 413.675 9.4338 0.2005 re
+f*
+1 g
+401.175 413.675 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+404.186 413.675 8.0288 0.2005 re
+f*
+1 g
+412.215 413.675 1.2043 0.2005 re
+f*
+0.498 0 0.482 rg
+413.419 413.675 3.613 0.2005 re
+f*
+0 g
+250.635 413.876 20.6741 0.2006 re
+f*
+1 g
+271.31 413.876 7.6273 0.2006 re
+f*
+0 g
+278.937 413.876 16.0575 0.2006 re
+f*
+1 g
+294.994 413.876 3.613 0.2006 re
+f*
+0 g
+298.607 413.876 10.8389 0.2006 re
+f*
+1 g
+309.446 413.876 7.0251 0.2006 re
+f*
+0 g
+316.471 413.876 18.0648 0.2006 re
+f*
+1 g
+334.536 413.876 2.8101 0.2006 re
+f*
+0 g
+337.346 413.876 5.8208 0.2006 re
+f*
+1 g
+343.167 413.876 1.6058 0.2006 re
+f*
+0 g
+344.773 413.876 6.6237 0.2006 re
+f*
+1 g
+351.397 413.876 4.4158 0.2006 re
+f*
+0.498 0 0.482 rg
+355.812 413.876 7.8281 0.2006 re
+f*
+1 g
+363.641 413.876 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 413.876 9.8353 0.2006 re
+f*
+1 g
+377.089 413.876 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+379.497 413.876 7.4267 0.2006 re
+f*
+1 g
+386.924 413.876 0.8028 0.2006 re
+f*
+0.498 0 0.482 rg
+387.727 413.876 0.6021 0.2006 re
+f*
+1 g
+388.329 413.876 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 413.876 9.2331 0.2006 re
+f*
+1 g
+400.974 413.876 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+403.985 413.876 8.4302 0.2006 re
+f*
+1 g
+412.415 413.876 1.2043 0.2006 re
+f*
+0.498 0 0.482 rg
+413.62 413.876 3.4123 0.2006 re
+f*
+0 g
+250.635 414.076 20.6741 0.2006 re
+f*
+1 g
+271.31 414.076 7.6273 0.2006 re
+f*
+0 g
+278.937 414.076 16.0575 0.2006 re
+f*
+1 g
+294.994 414.076 3.613 0.2006 re
+f*
+0 g
+298.607 414.076 10.6381 0.2006 re
+f*
+1 g
+309.245 414.076 7.2259 0.2006 re
+f*
+0 g
+316.471 414.076 17.6633 0.2006 re
+f*
+1 g
+334.135 414.076 3.0108 0.2006 re
+f*
+0 g
+337.146 414.076 6.423 0.2006 re
+f*
+1 g
+343.568 414.076 1.405 0.2006 re
+f*
+0 g
+344.973 414.076 6.4231 0.2006 re
+f*
+1 g
+351.397 414.076 4.6165 0.2006 re
+f*
+0.498 0 0.482 rg
+356.013 414.076 7.6274 0.2006 re
+f*
+1 g
+363.641 414.076 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 414.076 9.6346 0.2006 re
+f*
+1 g
+376.888 414.076 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+379.297 414.076 7.6274 0.2006 re
+f*
+1 g
+386.924 414.076 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+387.928 414.076 0.4013 0.2006 re
+f*
+1 g
+388.329 414.076 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 414.076 9.0324 0.2006 re
+f*
+1 g
+400.774 414.076 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+403.784 414.076 8.8316 0.2006 re
+f*
+1 g
+412.616 414.076 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+413.62 414.076 3.6129 0.2006 re
+f*
+0 g
+250.635 414.277 20.6741 0.2005 re
+f*
+1 g
+271.31 414.277 7.6273 0.2005 re
+f*
+0 g
+278.937 414.277 16.0575 0.2005 re
+f*
+1 g
+294.994 414.277 3.613 0.2005 re
+f*
+0 g
+298.607 414.277 10.6381 0.2005 re
+f*
+1 g
+309.245 414.277 7.0252 0.2005 re
+f*
+0 g
+316.271 414.277 17.6633 0.2005 re
+f*
+1 g
+333.934 414.277 3.0108 0.2005 re
+f*
+0 g
+336.945 414.277 6.8245 0.2005 re
+f*
+1 g
+343.769 414.277 1.405 0.2005 re
+f*
+0 g
+345.174 414.277 6.423 0.2005 re
+f*
+1 g
+351.597 414.277 4.6165 0.2005 re
+f*
+0.498 0 0.482 rg
+356.214 414.277 7.4267 0.2005 re
+f*
+1 g
+363.641 414.277 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 414.277 9.4339 0.2005 re
+f*
+1 g
+376.687 414.277 2.6093 0.2005 re
+f*
+0.498 0 0.482 rg
+379.297 414.277 7.828 0.2005 re
+f*
+1 g
+387.125 414.277 0.803 0.2005 re
+f*
+0.498 0 0.482 rg
+387.928 414.277 0.4013 0.2005 re
+f*
+1 g
+388.329 414.277 3.4123 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 414.277 8.8316 0.2005 re
+f*
+1 g
+400.573 414.277 3.2116 0.2005 re
+f*
+0.498 0 0.482 rg
+403.784 414.277 9.0323 0.2005 re
+f*
+1 g
+412.817 414.277 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+413.82 414.277 3.4122 0.2005 re
+f*
+0 g
+250.635 414.477 20.6741 0.2005 re
+f*
+1 g
+271.31 414.477 7.6273 0.2005 re
+f*
+0 g
+278.937 414.477 16.0575 0.2005 re
+f*
+1 g
+294.994 414.477 3.613 0.2005 re
+f*
+0 g
+298.607 414.477 10.6381 0.2005 re
+f*
+1 g
+309.245 414.477 6.8245 0.2005 re
+f*
+0 g
+316.07 414.477 17.6633 0.2005 re
+f*
+1 g
+333.733 414.477 3.0108 0.2005 re
+f*
+0 g
+336.744 414.477 7.4266 0.2005 re
+f*
+1 g
+344.171 414.477 1.2043 0.2005 re
+f*
+0 g
+345.375 414.477 6.2223 0.2005 re
+f*
+1 g
+351.597 414.477 4.6165 0.2005 re
+f*
+0.498 0 0.482 rg
+356.214 414.477 7.4267 0.2005 re
+f*
+1 g
+363.641 414.477 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 414.477 9.2331 0.2005 re
+f*
+1 g
+376.486 414.477 2.6094 0.2005 re
+f*
+0.498 0 0.482 rg
+379.096 414.477 8.0287 0.2005 re
+f*
+1 g
+387.125 414.477 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+388.128 414.477 0.2007 0.2005 re
+f*
+1 g
+388.329 414.477 3.4123 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 414.477 8.6309 0.2005 re
+f*
+1 g
+400.372 414.477 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+403.584 414.477 9.4339 0.2005 re
+f*
+1 g
+413.017 414.477 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+414.021 414.477 3.2114 0.2005 re
+f*
+0 g
+250.635 414.678 20.6741 0.2006 re
+f*
+1 g
+271.31 414.678 7.6273 0.2006 re
+f*
+0 g
+278.937 414.678 16.0575 0.2006 re
+f*
+1 g
+294.994 414.678 3.613 0.2006 re
+f*
+0 g
+298.607 414.678 10.6381 0.2006 re
+f*
+1 g
+309.245 414.678 6.8245 0.2006 re
+f*
+0 g
+316.07 414.678 17.4626 0.2006 re
+f*
+1 g
+333.533 414.678 3.0108 0.2006 re
+f*
+0 g
+336.543 414.678 7.828 0.2006 re
+f*
+1 g
+344.371 414.678 1.0036 0.2006 re
+f*
+0 g
+345.375 414.678 6.2223 0.2006 re
+f*
+1 g
+351.597 414.678 4.8173 0.2006 re
+f*
+0.498 0 0.482 rg
+356.415 414.678 7.2259 0.2006 re
+f*
+1 g
+363.641 414.678 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 414.678 9.0324 0.2006 re
+f*
+1 g
+376.286 414.678 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+379.096 414.678 8.2294 0.2006 re
+f*
+1 g
+387.325 414.678 0.8029 0.2006 re
+f*
+0.498 0 0.482 rg
+388.128 414.678 0.2007 0.2006 re
+f*
+1 g
+388.329 414.678 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 414.678 8.4302 0.2006 re
+f*
+1 g
+400.171 414.678 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+403.584 414.678 9.4339 0.2006 re
+f*
+1 g
+413.017 414.678 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+414.021 414.678 3.2114 0.2006 re
+f*
+0 g
+250.635 414.878 20.6741 0.2006 re
+f*
+1 g
+271.31 414.878 7.6273 0.2006 re
+f*
+0 g
+278.937 414.878 16.0575 0.2006 re
+f*
+1 g
+294.994 414.878 3.613 0.2006 re
+f*
+0 g
+298.607 414.878 10.6381 0.2006 re
+f*
+1 g
+309.245 414.878 6.6237 0.2006 re
+f*
+0 g
+315.869 414.878 17.4626 0.2006 re
+f*
+1 g
+333.332 414.878 3.0109 0.2006 re
+f*
+0 g
+336.343 414.878 8.2294 0.2006 re
+f*
+1 g
+344.572 414.878 1.0036 0.2006 re
+f*
+0 g
+345.576 414.878 6.2224 0.2006 re
+f*
+1 g
+351.798 414.878 4.6165 0.2006 re
+f*
+0.498 0 0.482 rg
+356.415 414.878 7.2259 0.2006 re
+f*
+1 g
+363.641 414.878 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 414.878 8.8317 0.2006 re
+f*
+1 g
+376.085 414.878 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+379.096 414.878 8.2294 0.2006 re
+f*
+1 g
+387.325 414.878 4.4159 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 414.878 8.4302 0.2006 re
+f*
+1 g
+400.171 414.878 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+403.383 414.878 9.8352 0.2006 re
+f*
+1 g
+413.218 414.878 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+414.222 414.878 3.0108 0.2006 re
+f*
+0 g
+250.435 415.079 20.8749 0.2006 re
+f*
+1 g
+271.31 415.079 7.6273 0.2006 re
+f*
+0 g
+278.937 415.079 16.0575 0.2006 re
+f*
+1 g
+294.994 415.079 3.613 0.2006 re
+f*
+0 g
+298.607 415.079 10.6381 0.2006 re
+f*
+1 g
+309.245 415.079 6.4231 0.2006 re
+f*
+0 g
+315.669 415.079 17.4625 0.2006 re
+f*
+1 g
+333.131 415.079 3.0108 0.2006 re
+f*
+0 g
+336.142 415.079 8.631 0.2006 re
+f*
+1 g
+344.773 415.079 0.8028 0.2006 re
+f*
+0 g
+345.576 415.079 6.2224 0.2006 re
+f*
+1 g
+351.798 415.079 4.8172 0.2006 re
+f*
+0.498 0 0.482 rg
+356.615 415.079 7.0252 0.2006 re
+f*
+1 g
+363.641 415.079 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 415.079 8.8317 0.2006 re
+f*
+1 g
+376.085 415.079 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+378.895 415.079 8.6309 0.2006 re
+f*
+1 g
+387.526 415.079 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 415.079 8.2295 0.2006 re
+f*
+1 g
+399.971 415.079 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+403.383 415.079 9.8352 0.2006 re
+f*
+1 g
+413.218 415.079 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+414.222 415.079 3.0108 0.2006 re
+f*
+0 g
+250.435 415.28 20.8749 0.2005 re
+f*
+1 g
+271.31 415.28 7.6273 0.2005 re
+f*
+0 g
+278.937 415.28 16.0575 0.2005 re
+f*
+1 g
+294.994 415.28 3.613 0.2005 re
+f*
+0 g
+298.607 415.28 10.6381 0.2005 re
+f*
+1 g
+309.245 415.28 6.4231 0.2005 re
+f*
+0 g
+315.669 415.28 17.2618 0.2005 re
+f*
+1 g
+332.93 415.28 3.0108 0.2005 re
+f*
+0 g
+335.941 415.28 15.8569 0.2005 re
+f*
+1 g
+351.798 415.28 5.0179 0.2005 re
+f*
+0.498 0 0.482 rg
+356.816 415.28 6.8245 0.2005 re
+f*
+1 g
+363.641 415.28 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 415.28 8.631 0.2005 re
+f*
+1 g
+375.884 415.28 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+378.895 415.28 8.6309 0.2005 re
+f*
+1 g
+387.526 415.28 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 415.28 8.0288 0.2005 re
+f*
+1 g
+399.77 415.28 3.4122 0.2005 re
+f*
+0.498 0 0.482 rg
+403.182 415.28 10.2367 0.2005 re
+f*
+1 g
+413.419 415.28 0.8028 0.2005 re
+f*
+0.498 0 0.482 rg
+414.222 415.28 3.2116 0.2005 re
+f*
+0 g
+250.435 415.48 20.8749 0.2006 re
+f*
+1 g
+271.31 415.48 7.6273 0.2006 re
+f*
+0 g
+278.937 415.48 16.0575 0.2006 re
+f*
+1 g
+294.994 415.48 3.613 0.2006 re
+f*
+0 g
+298.607 415.48 10.6381 0.2006 re
+f*
+1 g
+309.245 415.48 6.2223 0.2006 re
+f*
+0 g
+315.468 415.48 17.2619 0.2006 re
+f*
+1 g
+332.73 415.48 3.2115 0.2006 re
+f*
+0 g
+335.941 415.48 16.0575 0.2006 re
+f*
+1 g
+351.999 415.48 4.8173 0.2006 re
+f*
+0.498 0 0.482 rg
+356.816 415.48 6.8245 0.2006 re
+f*
+1 g
+363.641 415.48 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 415.48 8.4303 0.2006 re
+f*
+1 g
+375.684 415.48 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+378.895 415.48 8.8316 0.2006 re
+f*
+1 g
+387.727 415.48 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 415.48 8.0288 0.2006 re
+f*
+1 g
+399.77 415.48 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+403.182 415.48 10.2367 0.2006 re
+f*
+1 g
+413.419 415.48 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+414.422 415.48 3.0108 0.2006 re
+f*
+0 g
+250.435 415.681 20.8749 0.2005 re
+f*
+1 g
+271.31 415.681 7.6273 0.2005 re
+f*
+0 g
+278.937 415.681 16.0575 0.2005 re
+f*
+1 g
+294.994 415.681 3.613 0.2005 re
+f*
+0 g
+298.607 415.681 10.6381 0.2005 re
+f*
+1 g
+309.245 415.681 6.2223 0.2005 re
+f*
+0 g
+315.468 415.681 17.0612 0.2005 re
+f*
+1 g
+332.529 415.681 3.2115 0.2005 re
+f*
+0 g
+335.74 415.681 16.2582 0.2005 re
+f*
+1 g
+351.999 415.681 5.018 0.2005 re
+f*
+0.498 0 0.482 rg
+357.017 415.681 6.6238 0.2005 re
+f*
+1 g
+363.641 415.681 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 415.681 8.4303 0.2005 re
+f*
+1 g
+375.684 415.681 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+378.895 415.681 8.8316 0.2005 re
+f*
+1 g
+387.727 415.681 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 415.681 7.828 0.2005 re
+f*
+1 g
+399.569 415.681 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+403.182 415.681 10.4374 0.2005 re
+f*
+1 g
+413.62 415.681 0.8029 0.2005 re
+f*
+0.498 0 0.482 rg
+414.422 415.681 3.0108 0.2005 re
+f*
+0 g
+250.435 415.881 20.8749 0.2006 re
+f*
+1 g
+271.31 415.881 7.6273 0.2006 re
+f*
+0 g
+278.937 415.881 16.0575 0.2006 re
+f*
+1 g
+294.994 415.881 3.613 0.2006 re
+f*
+0 g
+298.607 415.881 10.6381 0.2006 re
+f*
+1 g
+309.245 415.881 6.2223 0.2006 re
+f*
+0 g
+315.468 415.881 16.8604 0.2006 re
+f*
+1 g
+332.328 415.881 3.4123 0.2006 re
+f*
+0 g
+335.74 415.881 16.2582 0.2006 re
+f*
+1 g
+351.999 415.881 5.018 0.2006 re
+f*
+0.498 0 0.482 rg
+357.017 415.881 6.6238 0.2006 re
+f*
+1 g
+363.641 415.881 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 415.881 8.2295 0.2006 re
+f*
+1 g
+375.483 415.881 3.2116 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 415.881 9.0323 0.2006 re
+f*
+1 g
+387.727 415.881 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 415.881 7.828 0.2006 re
+f*
+1 g
+399.569 415.881 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+403.182 415.881 10.4374 0.2006 re
+f*
+1 g
+413.62 415.881 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+414.623 415.881 2.8101 0.2006 re
+f*
+0 g
+250.435 416.082 20.8749 0.2006 re
+f*
+1 g
+271.31 416.082 7.6273 0.2006 re
+f*
+0 g
+278.937 416.082 16.0575 0.2006 re
+f*
+1 g
+294.994 416.082 3.613 0.2006 re
+f*
+0 g
+298.607 416.082 10.6381 0.2006 re
+f*
+1 g
+309.245 416.082 6.2223 0.2006 re
+f*
+0 g
+315.468 416.082 16.8604 0.2006 re
+f*
+1 g
+332.328 416.082 3.2116 0.2006 re
+f*
+0 g
+335.54 416.082 16.4589 0.2006 re
+f*
+1 g
+351.999 416.082 5.2187 0.2006 re
+f*
+0.498 0 0.482 rg
+357.217 416.082 6.4231 0.2006 re
+f*
+1 g
+363.641 416.082 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 416.082 8.2295 0.2006 re
+f*
+1 g
+375.483 416.082 3.2116 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 416.082 9.0323 0.2006 re
+f*
+1 g
+387.727 416.082 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 416.082 7.6273 0.2006 re
+f*
+1 g
+399.368 416.082 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+402.981 416.082 10.6381 0.2006 re
+f*
+1 g
+413.62 416.082 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+414.623 416.082 2.8101 0.2006 re
+f*
+0 g
+250.435 416.282 20.8749 0.2005 re
+f*
+1 g
+271.31 416.282 7.6273 0.2005 re
+f*
+0 g
+278.937 416.282 16.0575 0.2005 re
+f*
+1 g
+294.994 416.282 3.613 0.2005 re
+f*
+0 g
+298.607 416.282 10.6381 0.2005 re
+f*
+1 g
+309.245 416.282 6.0216 0.2005 re
+f*
+0 g
+315.267 416.282 16.8604 0.2005 re
+f*
+1 g
+332.127 416.282 3.4123 0.2005 re
+f*
+0 g
+335.54 416.282 16.6597 0.2005 re
+f*
+1 g
+352.2 416.282 5.0179 0.2005 re
+f*
+0.498 0 0.482 rg
+357.217 416.282 6.4231 0.2005 re
+f*
+1 g
+363.641 416.282 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 416.282 8.0288 0.2005 re
+f*
+1 g
+375.282 416.282 3.4123 0.2005 re
+f*
+0.498 0 0.482 rg
+378.695 416.282 9.2331 0.2005 re
+f*
+1 g
+387.928 416.282 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 416.282 7.6273 0.2005 re
+f*
+1 g
+399.368 416.282 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+402.981 416.282 10.8388 0.2005 re
+f*
+1 g
+413.82 416.282 0.8029 0.2005 re
+f*
+0.498 0 0.482 rg
+414.623 416.282 2.8101 0.2005 re
+f*
+0 g
+250.435 416.483 20.8749 0.2005 re
+f*
+1 g
+271.31 416.483 7.6273 0.2005 re
+f*
+0 g
+278.937 416.483 16.0575 0.2005 re
+f*
+1 g
+294.994 416.483 3.613 0.2005 re
+f*
+0 g
+298.607 416.483 10.6381 0.2005 re
+f*
+1 g
+309.245 416.483 6.0216 0.2005 re
+f*
+0 g
+315.267 416.483 16.6597 0.2005 re
+f*
+1 g
+331.927 416.483 3.613 0.2005 re
+f*
+0 g
+335.54 416.483 16.6597 0.2005 re
+f*
+1 g
+352.2 416.483 5.2187 0.2005 re
+f*
+0.498 0 0.482 rg
+357.418 416.483 6.2223 0.2005 re
+f*
+1 g
+363.641 416.483 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 416.483 8.0288 0.2005 re
+f*
+1 g
+375.282 416.483 3.4123 0.2005 re
+f*
+0.498 0 0.482 rg
+378.695 416.483 9.2331 0.2005 re
+f*
+1 g
+387.928 416.483 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 416.483 7.4266 0.2005 re
+f*
+1 g
+399.168 416.483 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+402.981 416.483 10.8388 0.2005 re
+f*
+1 g
+413.82 416.483 0.2008 0.2005 re
+f*
+0.498 0 0.482 rg
+414.021 416.483 3.4122 0.2005 re
+f*
+0 g
+250.435 416.683 20.8749 0.2006 re
+f*
+1 g
+271.31 416.683 7.6273 0.2006 re
+f*
+0 g
+278.937 416.683 16.0575 0.2006 re
+f*
+1 g
+294.994 416.683 3.613 0.2006 re
+f*
+0 g
+298.607 416.683 10.6381 0.2006 re
+f*
+1 g
+309.245 416.683 6.0216 0.2006 re
+f*
+0 g
+315.267 416.683 16.6597 0.2006 re
+f*
+1 g
+331.927 416.683 3.4123 0.2006 re
+f*
+0 g
+335.339 416.683 16.8604 0.2006 re
+f*
+1 g
+352.2 416.683 5.2187 0.2006 re
+f*
+0.498 0 0.482 rg
+357.418 416.683 6.2223 0.2006 re
+f*
+1 g
+363.641 416.683 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 416.683 7.8281 0.2006 re
+f*
+1 g
+375.081 416.683 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 416.683 9.2331 0.2006 re
+f*
+1 g
+387.928 416.683 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 416.683 7.4266 0.2006 re
+f*
+1 g
+399.168 416.683 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+402.981 416.683 14.4518 0.2006 re
+f*
+0 g
+250.234 416.884 21.0756 0.2006 re
+f*
+1 g
+271.31 416.884 7.6273 0.2006 re
+f*
+0 g
+278.937 416.884 16.0575 0.2006 re
+f*
+1 g
+294.994 416.884 3.613 0.2006 re
+f*
+0 g
+298.607 416.884 10.6381 0.2006 re
+f*
+1 g
+309.245 416.884 6.0216 0.2006 re
+f*
+0 g
+315.267 416.884 16.459 0.2006 re
+f*
+1 g
+331.726 416.884 3.613 0.2006 re
+f*
+0 g
+335.339 416.884 16.8604 0.2006 re
+f*
+1 g
+352.2 416.884 5.4194 0.2006 re
+f*
+0.498 0 0.482 rg
+357.619 416.884 6.0216 0.2006 re
+f*
+1 g
+363.641 416.884 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 416.884 7.8281 0.2006 re
+f*
+1 g
+375.081 416.884 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 416.884 9.2331 0.2006 re
+f*
+1 g
+387.928 416.884 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 416.884 7.4266 0.2006 re
+f*
+1 g
+399.168 416.884 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+402.981 416.884 14.4518 0.2006 re
+f*
+0 g
+250.234 417.085 14.2511 0.2005 re
+f*
+1 g
+264.485 417.085 25.8928 0.2005 re
+f*
+0 g
+290.378 417.085 4.6165 0.2005 re
+f*
+1 g
+294.994 417.085 3.613 0.2005 re
+f*
+0 g
+298.607 417.085 10.6381 0.2005 re
+f*
+1 g
+309.245 417.085 5.8209 0.2005 re
+f*
+0 g
+315.066 417.085 16.6597 0.2005 re
+f*
+1 g
+331.726 417.085 3.613 0.2005 re
+f*
+0 g
+335.339 417.085 17.0611 0.2005 re
+f*
+1 g
+352.4 417.085 5.2187 0.2005 re
+f*
+0.498 0 0.482 rg
+357.619 417.085 6.0216 0.2005 re
+f*
+1 g
+363.641 417.085 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 417.085 7.6274 0.2005 re
+f*
+1 g
+374.881 417.085 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 417.085 9.4339 0.2005 re
+f*
+1 g
+387.928 417.085 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 417.085 7.2259 0.2005 re
+f*
+1 g
+398.967 417.085 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 417.085 14.6525 0.2005 re
+f*
+0 g
+250.234 417.285 14.2511 0.2005 re
+f*
+1 g
+264.485 417.285 25.8928 0.2005 re
+f*
+0 g
+290.378 417.285 4.6165 0.2005 re
+f*
+1 g
+294.994 417.285 3.613 0.2005 re
+f*
+0 g
+298.607 417.285 10.6381 0.2005 re
+f*
+1 g
+309.245 417.285 5.8209 0.2005 re
+f*
+0 g
+315.066 417.285 16.459 0.2005 re
+f*
+1 g
+331.525 417.285 3.6129 0.2005 re
+f*
+0 g
+335.138 417.285 17.2619 0.2005 re
+f*
+1 g
+352.4 417.285 5.4194 0.2005 re
+f*
+0.498 0 0.482 rg
+357.82 417.285 5.8209 0.2005 re
+f*
+1 g
+363.641 417.285 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 417.285 7.6274 0.2005 re
+f*
+1 g
+374.881 417.285 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 417.285 9.6345 0.2005 re
+f*
+1 g
+388.128 417.285 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 417.285 7.2259 0.2005 re
+f*
+1 g
+398.967 417.285 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 417.285 14.8532 0.2005 re
+f*
+0 g
+250.234 417.486 14.2511 0.2006 re
+f*
+1 g
+264.485 417.486 25.8928 0.2006 re
+f*
+0 g
+290.378 417.486 4.6165 0.2006 re
+f*
+1 g
+294.994 417.486 3.613 0.2006 re
+f*
+0 g
+298.607 417.486 10.6381 0.2006 re
+f*
+1 g
+309.245 417.486 5.8209 0.2006 re
+f*
+0 g
+315.066 417.486 16.459 0.2006 re
+f*
+1 g
+331.525 417.486 3.6129 0.2006 re
+f*
+0 g
+335.138 417.486 17.2619 0.2006 re
+f*
+1 g
+352.4 417.486 5.4194 0.2006 re
+f*
+0.498 0 0.482 rg
+357.82 417.486 5.8209 0.2006 re
+f*
+1 g
+363.641 417.486 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 417.486 7.6274 0.2006 re
+f*
+1 g
+374.881 417.486 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 417.486 9.6345 0.2006 re
+f*
+1 g
+388.128 417.486 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 417.486 7.2259 0.2006 re
+f*
+1 g
+398.967 417.486 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 417.486 14.8532 0.2006 re
+f*
+0 g
+250.234 417.686 14.2511 0.2006 re
+f*
+1 g
+264.485 417.686 25.8928 0.2006 re
+f*
+0 g
+290.378 417.686 4.6165 0.2006 re
+f*
+1 g
+294.994 417.686 3.613 0.2006 re
+f*
+0 g
+298.607 417.686 10.6381 0.2006 re
+f*
+1 g
+309.245 417.686 5.8209 0.2006 re
+f*
+0 g
+315.066 417.686 16.2582 0.2006 re
+f*
+1 g
+331.325 417.686 3.8137 0.2006 re
+f*
+0 g
+335.138 417.686 17.2619 0.2006 re
+f*
+1 g
+352.4 417.686 5.6201 0.2006 re
+f*
+0.498 0 0.482 rg
+358.02 417.686 5.6202 0.2006 re
+f*
+1 g
+363.641 417.686 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 417.686 7.4267 0.2006 re
+f*
+1 g
+374.68 417.686 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 417.686 9.6345 0.2006 re
+f*
+1 g
+388.128 417.686 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 417.686 7.0252 0.2006 re
+f*
+1 g
+398.766 417.686 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 417.686 14.8532 0.2006 re
+f*
+0 g
+250.234 417.887 14.2511 0.2006 re
+f*
+1 g
+264.485 417.887 25.8928 0.2006 re
+f*
+0 g
+290.378 417.887 4.6165 0.2006 re
+f*
+1 g
+294.994 417.887 3.613 0.2006 re
+f*
+0 g
+298.607 417.887 10.6381 0.2006 re
+f*
+1 g
+309.245 417.887 5.6201 0.2006 re
+f*
+0 g
+314.866 417.887 16.459 0.2006 re
+f*
+1 g
+331.325 417.887 3.8137 0.2006 re
+f*
+0 g
+335.138 417.887 17.2619 0.2006 re
+f*
+1 g
+352.4 417.887 5.6201 0.2006 re
+f*
+0.498 0 0.482 rg
+358.02 417.887 5.6202 0.2006 re
+f*
+1 g
+363.641 417.887 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 417.887 7.4267 0.2006 re
+f*
+1 g
+374.68 417.887 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 417.887 9.6345 0.2006 re
+f*
+1 g
+388.128 417.887 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 417.887 7.0252 0.2006 re
+f*
+1 g
+398.766 417.887 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 417.887 14.8532 0.2006 re
+f*
+0 g
+250.234 418.087 14.2511 0.2006 re
+f*
+1 g
+264.485 418.087 25.8928 0.2006 re
+f*
+0 g
+290.378 418.087 4.6165 0.2006 re
+f*
+1 g
+294.994 418.087 3.613 0.2006 re
+f*
+0 g
+298.607 418.087 10.6381 0.2006 re
+f*
+1 g
+309.245 418.087 5.6201 0.2006 re
+f*
+0 g
+314.866 418.087 16.459 0.2006 re
+f*
+1 g
+331.325 418.087 3.8137 0.2006 re
+f*
+0 g
+335.138 418.087 17.4626 0.2006 re
+f*
+1 g
+352.601 418.087 5.6201 0.2006 re
+f*
+0.498 0 0.482 rg
+358.221 418.087 5.4195 0.2006 re
+f*
+1 g
+363.641 418.087 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 418.087 7.4267 0.2006 re
+f*
+1 g
+374.68 418.087 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 418.087 9.6345 0.2006 re
+f*
+1 g
+388.128 418.087 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 418.087 7.0252 0.2006 re
+f*
+1 g
+398.766 418.087 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 418.087 14.8532 0.2006 re
+f*
+0 g
+250.234 418.288 14.2511 0.2005 re
+f*
+1 g
+264.485 418.288 25.8928 0.2005 re
+f*
+0 g
+290.378 418.288 4.6165 0.2005 re
+f*
+1 g
+294.994 418.288 3.613 0.2005 re
+f*
+0 g
+298.607 418.288 10.6381 0.2005 re
+f*
+1 g
+309.245 418.288 5.6201 0.2005 re
+f*
+0 g
+314.866 418.288 16.2583 0.2005 re
+f*
+1 g
+331.124 418.288 4.0144 0.2005 re
+f*
+0 g
+335.138 418.288 17.4626 0.2005 re
+f*
+1 g
+352.601 418.288 5.6201 0.2005 re
+f*
+0.498 0 0.482 rg
+358.221 418.288 5.4195 0.2005 re
+f*
+1 g
+363.641 418.288 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 418.288 7.2259 0.2005 re
+f*
+1 g
+374.479 418.288 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 418.288 9.6345 0.2005 re
+f*
+1 g
+388.128 418.288 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 418.288 7.0252 0.2005 re
+f*
+1 g
+398.766 418.288 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 418.288 14.8532 0.2005 re
+f*
+0 g
+250.234 418.489 14.2511 0.2005 re
+f*
+1 g
+264.485 418.489 25.8928 0.2005 re
+f*
+0 g
+290.378 418.489 4.6165 0.2005 re
+f*
+1 g
+294.994 418.489 3.613 0.2005 re
+f*
+0 g
+298.607 418.489 10.8389 0.2005 re
+f*
+1 g
+309.446 418.489 5.4193 0.2005 re
+f*
+0 g
+314.866 418.489 16.2583 0.2005 re
+f*
+1 g
+331.124 418.489 3.8137 0.2005 re
+f*
+0 g
+334.938 418.489 17.6633 0.2005 re
+f*
+1 g
+352.601 418.489 5.6201 0.2005 re
+f*
+0.498 0 0.482 rg
+358.221 418.489 5.4195 0.2005 re
+f*
+1 g
+363.641 418.489 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 418.489 7.2259 0.2005 re
+f*
+1 g
+374.479 418.489 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 418.489 9.6345 0.2005 re
+f*
+1 g
+388.128 418.489 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 418.489 7.0252 0.2005 re
+f*
+1 g
+398.766 418.489 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 418.489 14.8532 0.2005 re
+f*
+0 g
+250.234 418.689 14.2511 0.2006 re
+f*
+1 g
+264.485 418.689 25.8928 0.2006 re
+f*
+0 g
+290.378 418.689 4.6165 0.2006 re
+f*
+1 g
+294.994 418.689 3.613 0.2006 re
+f*
+0 g
+298.607 418.689 10.8389 0.2006 re
+f*
+1 g
+309.446 418.689 5.4193 0.2006 re
+f*
+0 g
+314.866 418.689 16.2583 0.2006 re
+f*
+1 g
+331.124 418.689 3.8137 0.2006 re
+f*
+0 g
+334.938 418.689 17.6633 0.2006 re
+f*
+1 g
+352.601 418.689 5.8209 0.2006 re
+f*
+0.498 0 0.482 rg
+358.422 418.689 5.2187 0.2006 re
+f*
+1 g
+363.641 418.689 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 418.689 7.2259 0.2006 re
+f*
+1 g
+374.479 418.689 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 418.689 9.6345 0.2006 re
+f*
+1 g
+388.128 418.689 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 418.689 6.8244 0.2006 re
+f*
+1 g
+398.566 418.689 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 418.689 14.8532 0.2006 re
+f*
+0 g
+250.234 418.89 14.2511 0.2006 re
+f*
+1 g
+264.485 418.89 25.8928 0.2006 re
+f*
+0 g
+290.378 418.89 4.6165 0.2006 re
+f*
+1 g
+294.994 418.89 3.613 0.2006 re
+f*
+0 g
+298.607 418.89 10.8389 0.2006 re
+f*
+1 g
+309.446 418.89 5.4193 0.2006 re
+f*
+0 g
+314.866 418.89 16.0576 0.2006 re
+f*
+1 g
+330.923 418.89 4.0144 0.2006 re
+f*
+0 g
+334.938 418.89 17.6633 0.2006 re
+f*
+1 g
+352.601 418.89 5.8209 0.2006 re
+f*
+0.498 0 0.482 rg
+358.422 418.89 5.2187 0.2006 re
+f*
+1 g
+363.641 418.89 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 418.89 7.2259 0.2006 re
+f*
+1 g
+374.479 418.89 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 418.89 9.6345 0.2006 re
+f*
+1 g
+388.128 418.89 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 418.89 6.8244 0.2006 re
+f*
+1 g
+398.566 418.89 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 418.89 14.8532 0.2006 re
+f*
+0 g
+250.234 419.09 14.2511 0.2005 re
+f*
+1 g
+264.485 419.09 25.8928 0.2005 re
+f*
+0 g
+290.378 419.09 4.6165 0.2005 re
+f*
+1 g
+294.994 419.09 3.613 0.2005 re
+f*
+0 g
+298.607 419.09 10.8389 0.2005 re
+f*
+1 g
+309.446 419.09 5.4193 0.2005 re
+f*
+0 g
+314.866 419.09 16.0576 0.2005 re
+f*
+1 g
+330.923 419.09 4.0144 0.2005 re
+f*
+0 g
+334.938 419.09 17.6633 0.2005 re
+f*
+1 g
+352.601 419.09 5.8209 0.2005 re
+f*
+0.498 0 0.482 rg
+358.422 419.09 5.2187 0.2005 re
+f*
+1 g
+363.641 419.09 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 419.09 7.2259 0.2005 re
+f*
+1 g
+374.479 419.09 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 419.09 9.6345 0.2005 re
+f*
+1 g
+388.128 419.09 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 419.09 6.8244 0.2005 re
+f*
+1 g
+398.566 419.09 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 419.09 14.8532 0.2005 re
+f*
+0 g
+250.234 419.291 14.2511 0.2006 re
+f*
+1 g
+264.485 419.291 25.8928 0.2006 re
+f*
+0 g
+290.378 419.291 4.6165 0.2006 re
+f*
+1 g
+294.994 419.291 3.613 0.2006 re
+f*
+0 g
+298.607 419.291 10.8389 0.2006 re
+f*
+1 g
+309.446 419.291 5.4193 0.2006 re
+f*
+0 g
+314.866 419.291 16.0576 0.2006 re
+f*
+1 g
+330.923 419.291 4.0144 0.2006 re
+f*
+0 g
+334.938 419.291 17.6633 0.2006 re
+f*
+1 g
+352.601 419.291 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+358.623 419.291 5.018 0.2006 re
+f*
+1 g
+363.641 419.291 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 419.291 7.0252 0.2006 re
+f*
+1 g
+374.279 419.291 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 419.291 9.6345 0.2006 re
+f*
+1 g
+388.128 419.291 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 419.291 6.8244 0.2006 re
+f*
+1 g
+398.566 419.291 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 419.291 14.8532 0.2006 re
+f*
+0 g
+250.234 419.491 14.2511 0.2005 re
+f*
+1 g
+264.485 419.491 25.8928 0.2005 re
+f*
+0 g
+290.378 419.491 4.6165 0.2005 re
+f*
+1 g
+294.994 419.491 3.613 0.2005 re
+f*
+0 g
+298.607 419.491 10.8389 0.2005 re
+f*
+1 g
+309.446 419.491 5.2187 0.2005 re
+f*
+0 g
+314.665 419.491 16.2582 0.2005 re
+f*
+1 g
+330.923 419.491 4.0144 0.2005 re
+f*
+0 g
+334.938 419.491 17.6633 0.2005 re
+f*
+1 g
+352.601 419.491 6.0216 0.2005 re
+f*
+0.498 0 0.482 rg
+358.623 419.491 5.018 0.2005 re
+f*
+1 g
+363.641 419.491 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 419.491 7.0252 0.2005 re
+f*
+1 g
+374.279 419.491 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 419.491 9.6345 0.2005 re
+f*
+1 g
+388.128 419.491 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 419.491 6.8244 0.2005 re
+f*
+1 g
+398.566 419.491 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 419.491 14.8532 0.2005 re
+f*
+0 g
+250.234 419.692 14.2511 0.2006 re
+f*
+1 g
+264.485 419.692 25.8928 0.2006 re
+f*
+0 g
+290.378 419.692 4.6165 0.2006 re
+f*
+1 g
+294.994 419.692 3.613 0.2006 re
+f*
+0 g
+298.607 419.692 10.8389 0.2006 re
+f*
+1 g
+309.446 419.692 5.2187 0.2006 re
+f*
+0 g
+314.665 419.692 16.2582 0.2006 re
+f*
+1 g
+330.923 419.692 4.0144 0.2006 re
+f*
+0 g
+334.938 419.692 17.6633 0.2006 re
+f*
+1 g
+352.601 419.692 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+358.623 419.692 5.018 0.2006 re
+f*
+1 g
+363.641 419.692 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 419.692 7.0252 0.2006 re
+f*
+1 g
+374.279 419.692 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 419.692 9.6345 0.2006 re
+f*
+1 g
+388.128 419.692 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 419.692 6.8244 0.2006 re
+f*
+1 g
+398.566 419.692 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 419.692 14.8532 0.2006 re
+f*
+0 g
+250.234 419.892 14.2511 0.2005 re
+f*
+1 g
+264.485 419.892 6.6237 0.2005 re
+f*
+0 g
+271.109 419.892 0.2008 0.2005 re
+f*
+1 g
+271.31 419.892 7.6273 0.2005 re
+f*
+0 g
+278.937 419.892 0.2007 0.2005 re
+f*
+1 g
+279.138 419.892 11.2403 0.2005 re
+f*
+0 g
+290.378 419.892 4.6165 0.2005 re
+f*
+1 g
+294.994 419.892 3.613 0.2005 re
+f*
+0 g
+298.607 419.892 10.8389 0.2005 re
+f*
+1 g
+309.446 419.892 5.2187 0.2005 re
+f*
+0 g
+314.665 419.892 16.0575 0.2005 re
+f*
+1 g
+330.722 419.892 4.2151 0.2005 re
+f*
+0 g
+334.938 419.892 17.6633 0.2005 re
+f*
+1 g
+352.601 419.892 6.2223 0.2005 re
+f*
+0.498 0 0.482 rg
+358.823 419.892 4.8173 0.2005 re
+f*
+1 g
+363.641 419.892 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 419.892 7.0252 0.2005 re
+f*
+1 g
+374.279 419.892 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 419.892 9.6345 0.2005 re
+f*
+1 g
+388.128 419.892 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 419.892 6.8244 0.2005 re
+f*
+1 g
+398.566 419.892 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 419.892 14.8532 0.2005 re
+f*
+0 g
+250.234 420.093 21.0756 0.2006 re
+f*
+1 g
+271.31 420.093 7.6273 0.2006 re
+f*
+0 g
+278.937 420.093 16.0575 0.2006 re
+f*
+1 g
+294.994 420.093 3.613 0.2006 re
+f*
+0 g
+298.607 420.093 11.0396 0.2006 re
+f*
+1 g
+309.647 420.093 5.018 0.2006 re
+f*
+0 g
+314.665 420.093 16.0575 0.2006 re
+f*
+1 g
+330.722 420.093 4.2151 0.2006 re
+f*
+0 g
+334.938 420.093 17.6633 0.2006 re
+f*
+1 g
+352.601 420.093 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+358.823 420.093 4.8173 0.2006 re
+f*
+1 g
+363.641 420.093 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 420.093 7.0252 0.2006 re
+f*
+1 g
+374.279 420.093 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 420.093 9.6345 0.2006 re
+f*
+1 g
+388.128 420.093 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 420.093 6.8244 0.2006 re
+f*
+1 g
+398.566 420.093 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 420.093 14.8532 0.2006 re
+f*
+0 g
+250.234 420.294 21.0756 0.2005 re
+f*
+1 g
+271.31 420.294 7.6273 0.2005 re
+f*
+0 g
+278.937 420.294 16.0575 0.2005 re
+f*
+1 g
+294.994 420.294 3.613 0.2005 re
+f*
+0 g
+298.607 420.294 11.0396 0.2005 re
+f*
+1 g
+309.647 420.294 5.018 0.2005 re
+f*
+0 g
+314.665 420.294 16.0575 0.2005 re
+f*
+1 g
+330.722 420.294 4.2151 0.2005 re
+f*
+0 g
+334.938 420.294 17.8641 0.2005 re
+f*
+1 g
+352.802 420.294 6.0215 0.2005 re
+f*
+0.498 0 0.482 rg
+358.823 420.294 4.8173 0.2005 re
+f*
+1 g
+363.641 420.294 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 420.294 7.0252 0.2005 re
+f*
+1 g
+374.279 420.294 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 420.294 9.6345 0.2005 re
+f*
+1 g
+388.128 420.294 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 420.294 6.8244 0.2005 re
+f*
+1 g
+398.566 420.294 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 420.294 14.8532 0.2005 re
+f*
+0 g
+250.234 420.494 21.0756 0.2006 re
+f*
+1 g
+271.31 420.494 7.6273 0.2006 re
+f*
+0 g
+278.937 420.494 16.0575 0.2006 re
+f*
+1 g
+294.994 420.494 3.613 0.2006 re
+f*
+0 g
+298.607 420.494 11.0396 0.2006 re
+f*
+1 g
+309.647 420.494 5.018 0.2006 re
+f*
+0 g
+314.665 420.494 16.0575 0.2006 re
+f*
+1 g
+330.722 420.494 4.2151 0.2006 re
+f*
+0 g
+334.938 420.494 17.8641 0.2006 re
+f*
+1 g
+352.802 420.494 6.0215 0.2006 re
+f*
+0.498 0 0.482 rg
+358.823 420.494 4.8173 0.2006 re
+f*
+1 g
+363.641 420.494 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 420.494 7.0252 0.2006 re
+f*
+1 g
+374.279 420.494 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 420.494 9.6345 0.2006 re
+f*
+1 g
+388.128 420.494 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 420.494 6.8244 0.2006 re
+f*
+1 g
+398.566 420.494 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 420.494 14.8532 0.2006 re
+f*
+0 g
+250.234 420.695 21.0756 0.2006 re
+f*
+1 g
+271.31 420.695 7.6273 0.2006 re
+f*
+0 g
+278.937 420.695 16.0575 0.2006 re
+f*
+1 g
+294.994 420.695 3.613 0.2006 re
+f*
+0 g
+298.607 420.695 11.0396 0.2006 re
+f*
+1 g
+309.647 420.695 5.018 0.2006 re
+f*
+0 g
+314.665 420.695 16.0575 0.2006 re
+f*
+1 g
+330.722 420.695 4.2151 0.2006 re
+f*
+0 g
+334.938 420.695 17.8641 0.2006 re
+f*
+1 g
+352.802 420.695 6.2222 0.2006 re
+f*
+0.498 0 0.482 rg
+359.024 420.695 4.6166 0.2006 re
+f*
+1 g
+363.641 420.695 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 420.695 7.0252 0.2006 re
+f*
+1 g
+374.279 420.695 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 420.695 9.6345 0.2006 re
+f*
+1 g
+388.128 420.695 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 420.695 6.8244 0.2006 re
+f*
+1 g
+398.566 420.695 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 420.695 14.8532 0.2006 re
+f*
+0 g
+250.234 420.895 21.0756 0.2006 re
+f*
+1 g
+271.31 420.895 7.6273 0.2006 re
+f*
+0 g
+278.937 420.895 16.0575 0.2006 re
+f*
+1 g
+294.994 420.895 3.613 0.2006 re
+f*
+0 g
+298.607 420.895 11.0396 0.2006 re
+f*
+1 g
+309.647 420.895 5.018 0.2006 re
+f*
+0 g
+314.665 420.895 16.0575 0.2006 re
+f*
+1 g
+330.722 420.895 4.2151 0.2006 re
+f*
+0 g
+334.938 420.895 17.8641 0.2006 re
+f*
+1 g
+352.802 420.895 6.2222 0.2006 re
+f*
+0.498 0 0.482 rg
+359.024 420.895 4.6166 0.2006 re
+f*
+1 g
+363.641 420.895 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 420.895 7.0252 0.2006 re
+f*
+1 g
+374.279 420.895 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 420.895 9.6345 0.2006 re
+f*
+1 g
+388.128 420.895 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 420.895 6.8244 0.2006 re
+f*
+1 g
+398.566 420.895 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 420.895 14.8532 0.2006 re
+f*
+0 g
+250.234 421.096 21.0756 0.2005 re
+f*
+1 g
+271.31 421.096 7.6273 0.2005 re
+f*
+0 g
+278.937 421.096 16.0575 0.2005 re
+f*
+1 g
+294.994 421.096 3.613 0.2005 re
+f*
+0 g
+298.607 421.096 11.2403 0.2005 re
+f*
+1 g
+309.848 421.096 4.8173 0.2005 re
+f*
+0 g
+314.665 421.096 16.0575 0.2005 re
+f*
+1 g
+330.722 421.096 4.2151 0.2005 re
+f*
+0 g
+334.938 421.096 17.8641 0.2005 re
+f*
+1 g
+352.802 421.096 6.2222 0.2005 re
+f*
+0.498 0 0.482 rg
+359.024 421.096 4.6166 0.2005 re
+f*
+1 g
+363.641 421.096 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 421.096 7.0252 0.2005 re
+f*
+1 g
+374.279 421.096 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 421.096 9.6345 0.2005 re
+f*
+1 g
+388.128 421.096 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 421.096 6.8244 0.2005 re
+f*
+1 g
+398.566 421.096 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 421.096 14.8532 0.2005 re
+f*
+0 g
+250.234 421.296 21.0756 0.2005 re
+f*
+1 g
+271.31 421.296 7.6273 0.2005 re
+f*
+0 g
+278.937 421.296 16.0575 0.2005 re
+f*
+1 g
+294.994 421.296 3.613 0.2005 re
+f*
+0 g
+298.607 421.296 11.2403 0.2005 re
+f*
+1 g
+309.848 421.296 4.8173 0.2005 re
+f*
+0 g
+314.665 421.296 16.0575 0.2005 re
+f*
+1 g
+330.722 421.296 4.2151 0.2005 re
+f*
+0 g
+334.938 421.296 17.6633 0.2005 re
+f*
+1 g
+352.601 421.296 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+359.024 421.296 4.6166 0.2005 re
+f*
+1 g
+363.641 421.296 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 421.296 7.0252 0.2005 re
+f*
+1 g
+374.279 421.296 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 421.296 9.6345 0.2005 re
+f*
+1 g
+388.128 421.296 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 421.296 6.8244 0.2005 re
+f*
+1 g
+398.566 421.296 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 421.296 14.8532 0.2005 re
+f*
+0 g
+250.234 421.497 21.0756 0.2006 re
+f*
+1 g
+271.31 421.497 7.6273 0.2006 re
+f*
+0 g
+278.937 421.497 16.0575 0.2006 re
+f*
+1 g
+294.994 421.497 3.613 0.2006 re
+f*
+0 g
+298.607 421.497 11.2403 0.2006 re
+f*
+1 g
+309.848 421.497 4.8173 0.2006 re
+f*
+0 g
+314.665 421.497 16.0575 0.2006 re
+f*
+1 g
+330.722 421.497 4.2151 0.2006 re
+f*
+0 g
+334.938 421.497 17.6633 0.2006 re
+f*
+1 g
+352.601 421.497 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+359.225 421.497 4.4159 0.2006 re
+f*
+1 g
+363.641 421.497 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 421.497 7.0252 0.2006 re
+f*
+1 g
+374.279 421.497 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 421.497 9.6345 0.2006 re
+f*
+1 g
+388.128 421.497 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 421.497 6.8244 0.2006 re
+f*
+1 g
+398.566 421.497 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 421.497 14.8532 0.2006 re
+f*
+0 g
+250.234 421.697 21.0756 0.2006 re
+f*
+1 g
+271.31 421.697 7.6273 0.2006 re
+f*
+0 g
+278.937 421.697 16.0575 0.2006 re
+f*
+1 g
+294.994 421.697 3.613 0.2006 re
+f*
+0 g
+298.607 421.697 11.2403 0.2006 re
+f*
+1 g
+309.848 421.697 4.8173 0.2006 re
+f*
+0 g
+314.665 421.697 16.0575 0.2006 re
+f*
+1 g
+330.722 421.697 4.2151 0.2006 re
+f*
+0 g
+334.938 421.697 17.6633 0.2006 re
+f*
+1 g
+352.601 421.697 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+359.225 421.697 4.4159 0.2006 re
+f*
+1 g
+363.641 421.697 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 421.697 7.0252 0.2006 re
+f*
+1 g
+374.279 421.697 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 421.697 9.6345 0.2006 re
+f*
+1 g
+388.128 421.697 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 421.697 6.8244 0.2006 re
+f*
+1 g
+398.566 421.697 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 421.697 12.0431 0.2006 re
+f*
+1 g
+414.824 421.697 0.2008 0.2006 re
+f*
+0.498 0 0.482 rg
+415.025 421.697 2.6093 0.2006 re
+f*
+0 g
+250.234 421.898 21.0756 0.2005 re
+f*
+1 g
+271.31 421.898 7.6273 0.2005 re
+f*
+0 g
+278.937 421.898 16.0575 0.2005 re
+f*
+1 g
+294.994 421.898 3.613 0.2005 re
+f*
+0 g
+298.607 421.898 11.441 0.2005 re
+f*
+1 g
+310.048 421.898 4.6166 0.2005 re
+f*
+0 g
+314.665 421.898 16.0575 0.2005 re
+f*
+1 g
+330.722 421.898 4.2151 0.2005 re
+f*
+0 g
+334.938 421.898 17.6633 0.2005 re
+f*
+1 g
+352.601 421.898 6.6237 0.2005 re
+f*
+0.498 0 0.482 rg
+359.225 421.898 4.4159 0.2005 re
+f*
+1 g
+363.641 421.898 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 421.898 7.2259 0.2005 re
+f*
+1 g
+374.479 421.898 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 421.898 9.6345 0.2005 re
+f*
+1 g
+388.128 421.898 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 421.898 6.8244 0.2005 re
+f*
+1 g
+398.566 421.898 16.4591 0.2005 re
+f*
+0.498 0 0.482 rg
+415.025 421.898 2.6093 0.2005 re
+f*
+0 g
+250.234 422.099 21.0756 0.2006 re
+f*
+1 g
+271.31 422.099 7.6273 0.2006 re
+f*
+0 g
+278.937 422.099 16.0575 0.2006 re
+f*
+1 g
+294.994 422.099 3.613 0.2006 re
+f*
+0 g
+298.607 422.099 11.441 0.2006 re
+f*
+1 g
+310.048 422.099 4.6166 0.2006 re
+f*
+0 g
+314.665 422.099 16.0575 0.2006 re
+f*
+1 g
+330.722 422.099 4.2151 0.2006 re
+f*
+0 g
+334.938 422.099 17.6633 0.2006 re
+f*
+1 g
+352.601 422.099 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+359.225 422.099 4.4159 0.2006 re
+f*
+1 g
+363.641 422.099 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 422.099 7.2259 0.2006 re
+f*
+1 g
+374.479 422.099 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 422.099 9.6345 0.2006 re
+f*
+1 g
+388.128 422.099 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 422.099 7.0252 0.2006 re
+f*
+1 g
+398.766 422.099 16.2583 0.2006 re
+f*
+0.498 0 0.482 rg
+415.025 422.099 2.6093 0.2006 re
+f*
+0 g
+250.234 422.299 21.0756 0.2005 re
+f*
+1 g
+271.31 422.299 7.6273 0.2005 re
+f*
+0 g
+278.937 422.299 16.0575 0.2005 re
+f*
+1 g
+294.994 422.299 3.613 0.2005 re
+f*
+0 g
+298.607 422.299 11.441 0.2005 re
+f*
+1 g
+310.048 422.299 4.6166 0.2005 re
+f*
+0 g
+314.665 422.299 16.0575 0.2005 re
+f*
+1 g
+330.722 422.299 4.2151 0.2005 re
+f*
+0 g
+334.938 422.299 17.6633 0.2005 re
+f*
+1 g
+352.601 422.299 6.8245 0.2005 re
+f*
+0.498 0 0.482 rg
+359.425 422.299 4.2151 0.2005 re
+f*
+1 g
+363.641 422.299 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 422.299 7.2259 0.2005 re
+f*
+1 g
+374.479 422.299 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 422.299 9.6345 0.2005 re
+f*
+1 g
+388.128 422.299 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 422.299 7.0252 0.2005 re
+f*
+1 g
+398.766 422.299 16.2583 0.2005 re
+f*
+0.498 0 0.482 rg
+415.025 422.299 2.4086 0.2005 re
+f*
+0 g
+250.234 422.5 21.0756 0.2006 re
+f*
+1 g
+271.31 422.5 7.6273 0.2006 re
+f*
+0 g
+278.937 422.5 16.0575 0.2006 re
+f*
+1 g
+294.994 422.5 3.613 0.2006 re
+f*
+0 g
+298.607 422.5 11.441 0.2006 re
+f*
+1 g
+310.048 422.5 4.6166 0.2006 re
+f*
+0 g
+314.665 422.5 16.0575 0.2006 re
+f*
+1 g
+330.722 422.5 4.2151 0.2006 re
+f*
+0 g
+334.938 422.5 17.6633 0.2006 re
+f*
+1 g
+352.601 422.5 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+359.425 422.5 4.2151 0.2006 re
+f*
+1 g
+363.641 422.5 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 422.5 7.2259 0.2006 re
+f*
+1 g
+374.479 422.5 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 422.5 9.6345 0.2006 re
+f*
+1 g
+388.128 422.5 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 422.5 7.0252 0.2006 re
+f*
+1 g
+398.766 422.5 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 422.5 8.0287 0.2006 re
+f*
+1 g
+410.81 422.5 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+415.025 422.5 2.4086 0.2006 re
+f*
+0 g
+250.234 422.7 21.0756 0.2006 re
+f*
+1 g
+271.31 422.7 7.6273 0.2006 re
+f*
+0 g
+278.937 422.7 16.0575 0.2006 re
+f*
+1 g
+294.994 422.7 3.613 0.2006 re
+f*
+0 g
+298.607 422.7 11.6417 0.2006 re
+f*
+1 g
+310.249 422.7 4.4159 0.2006 re
+f*
+0 g
+314.665 422.7 16.0575 0.2006 re
+f*
+1 g
+330.722 422.7 4.2151 0.2006 re
+f*
+0 g
+334.938 422.7 17.6633 0.2006 re
+f*
+1 g
+352.601 422.7 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+359.425 422.7 4.2151 0.2006 re
+f*
+1 g
+363.641 422.7 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 422.7 7.2259 0.2006 re
+f*
+1 g
+374.479 422.7 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 422.7 9.6345 0.2006 re
+f*
+1 g
+388.128 422.7 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 422.7 7.0252 0.2006 re
+f*
+1 g
+398.766 422.7 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 422.7 8.0287 0.2006 re
+f*
+1 g
+410.81 422.7 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+415.025 422.7 2.4086 0.2006 re
+f*
+0 g
+250.435 422.901 20.8749 0.2005 re
+f*
+1 g
+271.31 422.901 7.6273 0.2005 re
+f*
+0 g
+278.937 422.901 16.0575 0.2005 re
+f*
+1 g
+294.994 422.901 3.613 0.2005 re
+f*
+0 g
+298.607 422.901 11.6417 0.2005 re
+f*
+1 g
+310.249 422.901 16.6597 0.2005 re
+f*
+0 g
+326.909 422.901 4.0144 0.2005 re
+f*
+1 g
+330.923 422.901 4.0144 0.2005 re
+f*
+0 g
+334.938 422.901 12.0431 0.2005 re
+f*
+1 g
+346.981 422.901 0.2008 0.2005 re
+f*
+0 g
+347.181 422.901 5.4194 0.2005 re
+f*
+1 g
+352.601 422.901 6.8245 0.2005 re
+f*
+0.498 0 0.482 rg
+359.425 422.901 4.2151 0.2005 re
+f*
+1 g
+363.641 422.901 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 422.901 7.4267 0.2005 re
+f*
+1 g
+374.68 422.901 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 422.901 9.6345 0.2005 re
+f*
+1 g
+388.128 422.901 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 422.901 7.2259 0.2005 re
+f*
+1 g
+398.967 422.901 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 422.901 8.0287 0.2005 re
+f*
+1 g
+410.81 422.901 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+415.025 422.901 2.4086 0.2005 re
+f*
+0 g
+250.435 423.101 20.8749 0.2006 re
+f*
+1 g
+271.31 423.101 7.6273 0.2006 re
+f*
+0 g
+278.937 423.101 16.0575 0.2006 re
+f*
+1 g
+294.994 423.101 3.613 0.2006 re
+f*
+0 g
+298.607 423.101 11.6417 0.2006 re
+f*
+1 g
+310.249 423.101 16.6597 0.2006 re
+f*
+0 g
+326.909 423.101 4.0144 0.2006 re
+f*
+1 g
+330.923 423.101 16.2583 0.2006 re
+f*
+0 g
+347.181 423.101 5.4194 0.2006 re
+f*
+1 g
+352.601 423.101 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+359.425 423.101 4.2151 0.2006 re
+f*
+1 g
+363.641 423.101 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 423.101 7.4267 0.2006 re
+f*
+1 g
+374.68 423.101 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 423.101 9.6345 0.2006 re
+f*
+1 g
+388.128 423.101 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 423.101 7.2259 0.2006 re
+f*
+1 g
+398.967 423.101 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 423.101 8.0287 0.2006 re
+f*
+1 g
+410.81 423.101 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+414.824 423.101 2.6094 0.2006 re
+f*
+0 g
+250.435 423.302 20.8749 0.2005 re
+f*
+1 g
+271.31 423.302 7.6273 0.2005 re
+f*
+0 g
+278.937 423.302 16.0575 0.2005 re
+f*
+1 g
+294.994 423.302 3.613 0.2005 re
+f*
+0 g
+298.607 423.302 11.8425 0.2005 re
+f*
+1 g
+310.45 423.302 4.2151 0.2005 re
+f*
+0 g
+314.665 423.302 8.4302 0.2005 re
+f*
+1 g
+323.095 423.302 3.8136 0.2005 re
+f*
+0 g
+326.909 423.302 4.0144 0.2005 re
+f*
+1 g
+330.923 423.302 16.2583 0.2005 re
+f*
+0 g
+347.181 423.302 5.4194 0.2005 re
+f*
+1 g
+352.601 423.302 6.8245 0.2005 re
+f*
+0.498 0 0.482 rg
+359.425 423.302 4.2151 0.2005 re
+f*
+1 g
+363.641 423.302 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 423.302 7.4267 0.2005 re
+f*
+1 g
+374.68 423.302 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+378.494 423.302 9.6345 0.2005 re
+f*
+1 g
+388.128 423.302 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 423.302 7.2259 0.2005 re
+f*
+1 g
+398.967 423.302 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+402.781 423.302 8.0287 0.2005 re
+f*
+1 g
+410.81 423.302 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+414.824 423.302 2.6094 0.2005 re
+f*
+0 g
+250.435 423.502 20.8749 0.2006 re
+f*
+1 g
+271.31 423.502 7.6273 0.2006 re
+f*
+0 g
+278.937 423.502 16.0575 0.2006 re
+f*
+1 g
+294.994 423.502 3.613 0.2006 re
+f*
+0 g
+298.607 423.502 11.8425 0.2006 re
+f*
+1 g
+310.45 423.502 4.4157 0.2006 re
+f*
+0 g
+314.866 423.502 8.2296 0.2006 re
+f*
+1 g
+323.095 423.502 3.8136 0.2006 re
+f*
+0 g
+326.909 423.502 4.0144 0.2006 re
+f*
+1 g
+330.923 423.502 16.2583 0.2006 re
+f*
+0 g
+347.181 423.502 5.2187 0.2006 re
+f*
+1 g
+352.4 423.502 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+359.425 423.502 4.2151 0.2006 re
+f*
+1 g
+363.641 423.502 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 423.502 7.4267 0.2006 re
+f*
+1 g
+374.68 423.502 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+378.494 423.502 9.6345 0.2006 re
+f*
+1 g
+388.128 423.502 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 423.502 7.4266 0.2006 re
+f*
+1 g
+399.168 423.502 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+402.781 423.502 8.0287 0.2006 re
+f*
+1 g
+410.81 423.502 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+414.824 423.502 2.6094 0.2006 re
+f*
+0 g
+250.435 423.703 20.8749 0.2006 re
+f*
+1 g
+271.31 423.703 7.6273 0.2006 re
+f*
+0 g
+278.937 423.703 16.0575 0.2006 re
+f*
+1 g
+294.994 423.703 3.613 0.2006 re
+f*
+0 g
+298.607 423.703 12.0432 0.2006 re
+f*
+1 g
+310.651 423.703 4.215 0.2006 re
+f*
+0 g
+314.866 423.703 8.2296 0.2006 re
+f*
+1 g
+323.095 423.703 3.8136 0.2006 re
+f*
+0 g
+326.909 423.703 4.0144 0.2006 re
+f*
+1 g
+330.923 423.703 4.0144 0.2006 re
+f*
+0 g
+334.938 423.703 8.0288 0.2006 re
+f*
+1 g
+342.966 423.703 4.2151 0.2006 re
+f*
+0 g
+347.181 423.703 5.2187 0.2006 re
+f*
+1 g
+352.4 423.703 7.2259 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 423.703 4.0144 0.2006 re
+f*
+1 g
+363.641 423.703 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 423.703 7.6274 0.2006 re
+f*
+1 g
+374.881 423.703 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 423.703 9.2331 0.2006 re
+f*
+1 g
+387.928 423.703 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 423.703 7.4266 0.2006 re
+f*
+1 g
+399.168 423.703 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+402.981 423.703 7.828 0.2006 re
+f*
+1 g
+410.81 423.703 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+414.824 423.703 2.6094 0.2006 re
+f*
+0 g
+250.435 423.904 20.8749 0.2005 re
+f*
+1 g
+271.31 423.904 7.6273 0.2005 re
+f*
+0 g
+278.937 423.904 16.0575 0.2005 re
+f*
+1 g
+294.994 423.904 3.8137 0.2005 re
+f*
+0 g
+298.808 423.904 11.8425 0.2005 re
+f*
+1 g
+310.651 423.904 4.215 0.2005 re
+f*
+0 g
+314.866 423.904 8.2296 0.2005 re
+f*
+1 g
+323.095 423.904 3.8136 0.2005 re
+f*
+0 g
+326.909 423.904 4.2151 0.2005 re
+f*
+1 g
+331.124 423.904 3.8137 0.2005 re
+f*
+0 g
+334.938 423.904 8.0288 0.2005 re
+f*
+1 g
+342.966 423.904 4.2151 0.2005 re
+f*
+0 g
+347.181 423.904 5.2187 0.2005 re
+f*
+1 g
+352.4 423.904 7.2259 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 423.904 4.0144 0.2005 re
+f*
+1 g
+363.641 423.904 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 423.904 7.6274 0.2005 re
+f*
+1 g
+374.881 423.904 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+378.695 423.904 9.2331 0.2005 re
+f*
+1 g
+387.928 423.904 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 423.904 7.4266 0.2005 re
+f*
+1 g
+399.168 423.904 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+402.981 423.904 7.828 0.2005 re
+f*
+1 g
+410.81 423.904 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+414.824 423.904 2.6094 0.2005 re
+f*
+0 g
+250.435 424.104 20.8749 0.2006 re
+f*
+1 g
+271.31 424.104 7.6273 0.2006 re
+f*
+0 g
+278.937 424.104 16.0575 0.2006 re
+f*
+1 g
+294.994 424.104 3.8137 0.2006 re
+f*
+0 g
+298.808 424.104 11.8425 0.2006 re
+f*
+1 g
+310.651 424.104 4.215 0.2006 re
+f*
+0 g
+314.866 424.104 8.4303 0.2006 re
+f*
+1 g
+323.296 424.104 3.6129 0.2006 re
+f*
+0 g
+326.909 424.104 4.2151 0.2006 re
+f*
+1 g
+331.124 424.104 3.8137 0.2006 re
+f*
+0 g
+334.938 424.104 8.0288 0.2006 re
+f*
+1 g
+342.966 424.104 4.2151 0.2006 re
+f*
+0 g
+347.181 424.104 5.2187 0.2006 re
+f*
+1 g
+352.4 424.104 7.2259 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 424.104 4.0144 0.2006 re
+f*
+1 g
+363.641 424.104 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 424.104 7.8281 0.2006 re
+f*
+1 g
+375.081 424.104 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 424.104 9.2331 0.2006 re
+f*
+1 g
+387.928 424.104 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 424.104 7.6273 0.2006 re
+f*
+1 g
+399.368 424.104 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+402.981 424.104 7.828 0.2006 re
+f*
+1 g
+410.81 424.104 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+414.623 424.104 2.8101 0.2006 re
+f*
+0 g
+250.435 424.305 20.8749 0.2005 re
+f*
+1 g
+271.31 424.305 7.6273 0.2005 re
+f*
+0 g
+278.937 424.305 16.0575 0.2005 re
+f*
+1 g
+294.994 424.305 3.8137 0.2005 re
+f*
+0 g
+298.808 424.305 12.0432 0.2005 re
+f*
+1 g
+310.851 424.305 4.0143 0.2005 re
+f*
+0 g
+314.866 424.305 8.4303 0.2005 re
+f*
+1 g
+323.296 424.305 3.6129 0.2005 re
+f*
+0 g
+326.909 424.305 4.2151 0.2005 re
+f*
+1 g
+331.124 424.305 4.0144 0.2005 re
+f*
+0 g
+335.138 424.305 7.8281 0.2005 re
+f*
+1 g
+342.966 424.305 4.2151 0.2005 re
+f*
+0 g
+347.181 424.305 5.2187 0.2005 re
+f*
+1 g
+352.4 424.305 7.2259 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 424.305 4.0144 0.2005 re
+f*
+1 g
+363.641 424.305 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 424.305 7.8281 0.2005 re
+f*
+1 g
+375.081 424.305 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+378.695 424.305 9.2331 0.2005 re
+f*
+1 g
+387.928 424.305 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 424.305 7.6273 0.2005 re
+f*
+1 g
+399.368 424.305 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+402.981 424.305 7.828 0.2005 re
+f*
+1 g
+410.81 424.305 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+414.623 424.305 2.6093 0.2005 re
+f*
+0 g
+250.435 424.505 20.8749 0.2006 re
+f*
+1 g
+271.31 424.505 7.6273 0.2006 re
+f*
+0 g
+278.937 424.505 16.0575 0.2006 re
+f*
+1 g
+294.994 424.505 3.8137 0.2006 re
+f*
+0 g
+298.808 424.505 12.0432 0.2006 re
+f*
+1 g
+310.851 424.505 4.0143 0.2006 re
+f*
+0 g
+314.866 424.505 8.4303 0.2006 re
+f*
+1 g
+323.296 424.505 3.6129 0.2006 re
+f*
+0 g
+326.909 424.505 4.4158 0.2006 re
+f*
+1 g
+331.325 424.505 3.8137 0.2006 re
+f*
+0 g
+335.138 424.505 7.8281 0.2006 re
+f*
+1 g
+342.966 424.505 4.0143 0.2006 re
+f*
+0 g
+346.981 424.505 5.2188 0.2006 re
+f*
+1 g
+352.2 424.505 7.4266 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 424.505 4.0144 0.2006 re
+f*
+1 g
+363.641 424.505 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 424.505 8.0288 0.2006 re
+f*
+1 g
+375.282 424.505 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 424.505 9.2331 0.2006 re
+f*
+1 g
+387.928 424.505 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 424.505 7.828 0.2006 re
+f*
+1 g
+399.569 424.505 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+402.981 424.505 7.828 0.2006 re
+f*
+1 g
+410.81 424.505 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+414.623 424.505 2.6093 0.2006 re
+f*
+0 g
+250.635 424.706 20.6741 0.2006 re
+f*
+1 g
+271.31 424.706 7.6273 0.2006 re
+f*
+0 g
+278.937 424.706 16.0575 0.2006 re
+f*
+1 g
+294.994 424.706 3.8137 0.2006 re
+f*
+0 g
+298.808 424.706 12.2439 0.2006 re
+f*
+1 g
+311.052 424.706 3.8136 0.2006 re
+f*
+0 g
+314.866 424.706 8.4303 0.2006 re
+f*
+1 g
+323.296 424.706 3.4122 0.2006 re
+f*
+0 g
+326.708 424.706 4.6165 0.2006 re
+f*
+1 g
+331.325 424.706 3.8137 0.2006 re
+f*
+0 g
+335.138 424.706 7.8281 0.2006 re
+f*
+1 g
+342.966 424.706 4.0143 0.2006 re
+f*
+0 g
+346.981 424.706 5.2188 0.2006 re
+f*
+1 g
+352.2 424.706 7.4266 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 424.706 4.0144 0.2006 re
+f*
+1 g
+363.641 424.706 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 424.706 8.0288 0.2006 re
+f*
+1 g
+375.282 424.706 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+378.695 424.706 9.2331 0.2006 re
+f*
+1 g
+387.928 424.706 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 424.706 7.828 0.2006 re
+f*
+1 g
+399.569 424.706 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+402.981 424.706 7.828 0.2006 re
+f*
+1 g
+410.81 424.706 3.8137 0.2006 re
+f*
+0.498 0 0.482 rg
+414.623 424.706 2.6093 0.2006 re
+f*
+0 g
+250.635 424.906 20.6741 0.2005 re
+f*
+1 g
+271.31 424.906 7.6273 0.2005 re
+f*
+0 g
+278.937 424.906 16.0575 0.2005 re
+f*
+1 g
+294.994 424.906 3.8137 0.2005 re
+f*
+0 g
+298.808 424.906 12.2439 0.2005 re
+f*
+1 g
+311.052 424.906 3.8136 0.2005 re
+f*
+0 g
+314.866 424.906 8.4303 0.2005 re
+f*
+1 g
+323.296 424.906 3.4122 0.2005 re
+f*
+0 g
+326.708 424.906 4.6165 0.2005 re
+f*
+1 g
+331.325 424.906 3.8137 0.2005 re
+f*
+0 g
+335.138 424.906 7.8281 0.2005 re
+f*
+1 g
+342.966 424.906 4.0143 0.2005 re
+f*
+0 g
+346.981 424.906 5.2188 0.2005 re
+f*
+1 g
+352.2 424.906 7.4266 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 424.906 4.0144 0.2005 re
+f*
+1 g
+363.641 424.906 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 424.906 8.2295 0.2005 re
+f*
+1 g
+375.483 424.906 3.2116 0.2005 re
+f*
+0.498 0 0.482 rg
+378.695 424.906 9.0323 0.2005 re
+f*
+1 g
+387.727 424.906 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 424.906 8.0288 0.2005 re
+f*
+1 g
+399.77 424.906 3.4122 0.2005 re
+f*
+0.498 0 0.482 rg
+403.182 424.906 7.6273 0.2005 re
+f*
+1 g
+410.81 424.906 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+414.422 424.906 2.81 0.2005 re
+f*
+0 g
+250.635 425.107 20.8748 0.2006 re
+f*
+1 g
+271.51 425.107 7.4266 0.2006 re
+f*
+0 g
+278.937 425.107 16.0575 0.2006 re
+f*
+1 g
+294.994 425.107 4.0144 0.2006 re
+f*
+0 g
+299.009 425.107 5.2187 0.2006 re
+f*
+1 g
+304.227 425.107 2.208 0.2006 re
+f*
+0 g
+306.435 425.107 4.8172 0.2006 re
+f*
+1 g
+311.253 425.107 3.6129 0.2006 re
+f*
+0 g
+314.866 425.107 8.4303 0.2006 re
+f*
+1 g
+323.296 425.107 3.4122 0.2006 re
+f*
+0 g
+326.708 425.107 4.8173 0.2006 re
+f*
+1 g
+331.525 425.107 3.6129 0.2006 re
+f*
+0 g
+335.138 425.107 7.8281 0.2006 re
+f*
+1 g
+342.966 425.107 4.0143 0.2006 re
+f*
+0 g
+346.981 425.107 5.018 0.2006 re
+f*
+1 g
+351.999 425.107 7.6274 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 425.107 4.0144 0.2006 re
+f*
+1 g
+363.641 425.107 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 425.107 8.2295 0.2006 re
+f*
+1 g
+375.483 425.107 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+378.895 425.107 8.8316 0.2006 re
+f*
+1 g
+387.727 425.107 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 425.107 8.2295 0.2006 re
+f*
+1 g
+399.971 425.107 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+403.182 425.107 7.6273 0.2006 re
+f*
+1 g
+410.81 425.107 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+414.422 425.107 2.81 0.2006 re
+f*
+0 g
+250.635 425.308 20.8748 0.2005 re
+f*
+1 g
+271.51 425.308 7.4266 0.2005 re
+f*
+0 g
+278.937 425.308 16.0575 0.2005 re
+f*
+1 g
+294.994 425.308 4.0144 0.2005 re
+f*
+0 g
+299.009 425.308 4.8173 0.2005 re
+f*
+1 g
+303.826 425.308 3.0108 0.2005 re
+f*
+0 g
+306.837 425.308 4.4158 0.2005 re
+f*
+1 g
+311.253 425.308 3.8137 0.2005 re
+f*
+0 g
+315.066 425.308 8.2295 0.2005 re
+f*
+1 g
+323.296 425.308 3.4122 0.2005 re
+f*
+0 g
+326.708 425.308 4.8173 0.2005 re
+f*
+1 g
+331.525 425.308 3.6129 0.2005 re
+f*
+0 g
+335.138 425.308 7.8281 0.2005 re
+f*
+1 g
+342.966 425.308 4.0143 0.2005 re
+f*
+0 g
+346.981 425.308 5.018 0.2005 re
+f*
+1 g
+351.999 425.308 7.6274 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 425.308 4.0144 0.2005 re
+f*
+1 g
+363.641 425.308 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 425.308 8.4303 0.2005 re
+f*
+1 g
+375.684 425.308 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+378.895 425.308 8.8316 0.2005 re
+f*
+1 g
+387.727 425.308 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 425.308 8.2295 0.2005 re
+f*
+1 g
+399.971 425.308 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+403.182 425.308 7.6273 0.2005 re
+f*
+1 g
+410.81 425.308 3.4122 0.2005 re
+f*
+0.498 0 0.482 rg
+414.222 425.308 3.0108 0.2005 re
+f*
+0 g
+250.635 425.508 20.8748 0.2006 re
+f*
+1 g
+271.51 425.508 7.4266 0.2006 re
+f*
+0 g
+278.937 425.508 16.0575 0.2006 re
+f*
+1 g
+294.994 425.508 4.0144 0.2006 re
+f*
+0 g
+299.009 425.508 4.6166 0.2006 re
+f*
+1 g
+303.625 425.508 3.4122 0.2006 re
+f*
+0 g
+307.038 425.508 4.4158 0.2006 re
+f*
+1 g
+311.453 425.508 3.613 0.2006 re
+f*
+0 g
+315.066 425.508 8.2295 0.2006 re
+f*
+1 g
+323.296 425.508 3.2115 0.2006 re
+f*
+0 g
+326.507 425.508 5.2187 0.2006 re
+f*
+1 g
+331.726 425.508 3.4122 0.2006 re
+f*
+0 g
+335.138 425.508 7.8281 0.2006 re
+f*
+1 g
+342.966 425.508 3.8137 0.2006 re
+f*
+0 g
+346.78 425.508 5.2186 0.2006 re
+f*
+1 g
+351.999 425.508 7.6274 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 425.508 4.0144 0.2006 re
+f*
+1 g
+363.641 425.508 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 425.508 8.4303 0.2006 re
+f*
+1 g
+375.684 425.508 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+378.895 425.508 8.6309 0.2006 re
+f*
+1 g
+387.526 425.508 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 425.508 8.4302 0.2006 re
+f*
+1 g
+400.171 425.508 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+403.383 425.508 7.4266 0.2006 re
+f*
+1 g
+410.81 425.508 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+414.222 425.508 2.8102 0.2006 re
+f*
+0 g
+250.635 425.709 20.8748 0.2005 re
+f*
+1 g
+271.51 425.709 7.4266 0.2005 re
+f*
+0 g
+278.937 425.709 16.0575 0.2005 re
+f*
+1 g
+294.994 425.709 4.2151 0.2005 re
+f*
+0 g
+299.209 425.709 4.4159 0.2005 re
+f*
+1 g
+303.625 425.709 3.6129 0.2005 re
+f*
+0 g
+307.238 425.709 4.4159 0.2005 re
+f*
+1 g
+311.654 425.709 3.4122 0.2005 re
+f*
+0 g
+315.066 425.709 8.0288 0.2005 re
+f*
+1 g
+323.095 425.709 3.4122 0.2005 re
+f*
+0 g
+326.507 425.709 5.2187 0.2005 re
+f*
+1 g
+331.726 425.709 3.613 0.2005 re
+f*
+0 g
+335.339 425.709 7.6273 0.2005 re
+f*
+1 g
+342.966 425.709 3.8137 0.2005 re
+f*
+0 g
+346.78 425.709 5.018 0.2005 re
+f*
+1 g
+351.798 425.709 7.828 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 425.709 4.0144 0.2005 re
+f*
+1 g
+363.641 425.709 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 425.709 8.631 0.2005 re
+f*
+1 g
+375.884 425.709 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+378.895 425.709 8.6309 0.2005 re
+f*
+1 g
+387.526 425.709 4.2151 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 425.709 8.6309 0.2005 re
+f*
+1 g
+400.372 425.709 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+403.383 425.709 7.4266 0.2005 re
+f*
+1 g
+410.81 425.709 3.4122 0.2005 re
+f*
+0.498 0 0.482 rg
+414.222 425.709 2.8102 0.2005 re
+f*
+0 g
+250.836 425.909 20.6741 0.2006 re
+f*
+1 g
+271.51 425.909 7.4266 0.2006 re
+f*
+0 g
+278.937 425.909 16.0575 0.2006 re
+f*
+1 g
+294.994 425.909 4.2151 0.2006 re
+f*
+0 g
+299.209 425.909 4.4159 0.2006 re
+f*
+1 g
+303.625 425.909 3.6129 0.2006 re
+f*
+0 g
+307.238 425.909 4.4159 0.2006 re
+f*
+1 g
+311.654 425.909 3.4122 0.2006 re
+f*
+0 g
+315.066 425.909 8.0288 0.2006 re
+f*
+1 g
+323.095 425.909 3.2115 0.2006 re
+f*
+0 g
+326.307 425.909 5.6201 0.2006 re
+f*
+1 g
+331.927 425.909 3.4123 0.2006 re
+f*
+0 g
+335.339 425.909 7.6273 0.2006 re
+f*
+1 g
+342.966 425.909 3.8137 0.2006 re
+f*
+0 g
+346.78 425.909 5.018 0.2006 re
+f*
+1 g
+351.798 425.909 7.828 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 425.909 4.0144 0.2006 re
+f*
+1 g
+363.641 425.909 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 425.909 8.8317 0.2006 re
+f*
+1 g
+376.085 425.909 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+379.096 425.909 8.4302 0.2006 re
+f*
+1 g
+387.526 425.909 4.2151 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 425.909 8.6309 0.2006 re
+f*
+1 g
+400.372 425.909 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+403.383 425.909 7.2259 0.2006 re
+f*
+1 g
+410.609 425.909 3.4123 0.2006 re
+f*
+0.498 0 0.482 rg
+414.021 425.909 3.0108 0.2006 re
+f*
+0 g
+250.836 426.11 20.6741 0.2006 re
+f*
+1 g
+271.51 426.11 7.4266 0.2006 re
+f*
+0 g
+278.937 426.11 16.0575 0.2006 re
+f*
+1 g
+294.994 426.11 4.2151 0.2006 re
+f*
+0 g
+299.209 426.11 4.4159 0.2006 re
+f*
+1 g
+303.625 426.11 3.6129 0.2006 re
+f*
+0 g
+307.238 426.11 4.6166 0.2006 re
+f*
+1 g
+311.855 426.11 3.2115 0.2006 re
+f*
+0 g
+315.066 426.11 8.0288 0.2006 re
+f*
+1 g
+323.095 426.11 3.2115 0.2006 re
+f*
+0 g
+326.307 426.11 5.6201 0.2006 re
+f*
+1 g
+331.927 426.11 3.4123 0.2006 re
+f*
+0 g
+335.339 426.11 7.6273 0.2006 re
+f*
+1 g
+342.966 426.11 3.6129 0.2006 re
+f*
+0 g
+346.579 426.11 5.2188 0.2006 re
+f*
+1 g
+351.798 426.11 8.0287 0.2006 re
+f*
+0.498 0 0.482 rg
+359.827 426.11 3.8137 0.2006 re
+f*
+1 g
+363.641 426.11 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 426.11 8.8317 0.2006 re
+f*
+1 g
+376.085 426.11 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+379.096 426.11 8.2294 0.2006 re
+f*
+1 g
+387.325 426.11 4.4159 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 426.11 8.8316 0.2006 re
+f*
+1 g
+400.573 426.11 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+403.584 426.11 7.0252 0.2006 re
+f*
+1 g
+410.609 426.11 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+413.82 426.11 3.2116 0.2006 re
+f*
+0 g
+250.836 426.31 20.6741 0.2005 re
+f*
+1 g
+271.51 426.31 7.4266 0.2005 re
+f*
+0 g
+278.937 426.31 16.0575 0.2005 re
+f*
+1 g
+294.994 426.31 4.4159 0.2005 re
+f*
+0 g
+299.41 426.31 4.2151 0.2005 re
+f*
+1 g
+303.625 426.31 3.8137 0.2005 re
+f*
+0 g
+307.439 426.31 4.4158 0.2005 re
+f*
+1 g
+311.855 426.31 3.4122 0.2005 re
+f*
+0 g
+315.267 426.31 7.8281 0.2005 re
+f*
+1 g
+323.095 426.31 3.2115 0.2005 re
+f*
+0 g
+326.307 426.31 5.8208 0.2005 re
+f*
+1 g
+332.127 426.31 3.2116 0.2005 re
+f*
+0 g
+335.339 426.31 7.6273 0.2005 re
+f*
+1 g
+342.966 426.31 3.6129 0.2005 re
+f*
+0 g
+346.579 426.31 5.018 0.2005 re
+f*
+1 g
+351.597 426.31 8.2295 0.2005 re
+f*
+0.498 0 0.482 rg
+359.827 426.31 3.8137 0.2005 re
+f*
+1 g
+363.641 426.31 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 426.31 9.0324 0.2005 re
+f*
+1 g
+376.286 426.31 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+379.297 426.31 8.0287 0.2005 re
+f*
+1 g
+387.325 426.31 4.4159 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 426.31 9.0324 0.2005 re
+f*
+1 g
+400.774 426.31 2.81 0.2005 re
+f*
+0.498 0 0.482 rg
+403.584 426.31 7.0252 0.2005 re
+f*
+1 g
+410.609 426.31 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+413.82 426.31 3.2116 0.2005 re
+f*
+0 g
+250.836 426.511 20.6741 0.2006 re
+f*
+1 g
+271.51 426.511 7.4266 0.2006 re
+f*
+0 g
+278.937 426.511 16.0575 0.2006 re
+f*
+1 g
+294.994 426.511 4.4159 0.2006 re
+f*
+0 g
+299.41 426.511 4.2151 0.2006 re
+f*
+1 g
+303.625 426.511 3.8137 0.2006 re
+f*
+0 g
+307.439 426.511 4.6165 0.2006 re
+f*
+1 g
+312.056 426.511 3.2115 0.2006 re
+f*
+0 g
+315.267 426.511 7.8281 0.2006 re
+f*
+1 g
+323.095 426.511 3.0107 0.2006 re
+f*
+0 g
+326.106 426.511 6.2223 0.2006 re
+f*
+1 g
+332.328 426.511 3.2116 0.2006 re
+f*
+0 g
+335.54 426.511 7.4266 0.2006 re
+f*
+1 g
+342.966 426.511 3.4122 0.2006 re
+f*
+0 g
+346.379 426.511 5.2187 0.2006 re
+f*
+1 g
+351.597 426.511 8.2295 0.2006 re
+f*
+0.498 0 0.482 rg
+359.827 426.511 3.8137 0.2006 re
+f*
+1 g
+363.641 426.511 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 426.511 9.2331 0.2006 re
+f*
+1 g
+376.486 426.511 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+379.297 426.511 7.828 0.2006 re
+f*
+1 g
+387.125 426.511 4.6166 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 426.511 9.2331 0.2006 re
+f*
+1 g
+400.974 426.511 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+403.784 426.511 6.8244 0.2006 re
+f*
+1 g
+410.609 426.511 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+413.62 426.511 3.4123 0.2006 re
+f*
+0 g
+250.836 426.711 20.6741 0.2005 re
+f*
+1 g
+271.51 426.711 7.4266 0.2005 re
+f*
+0 g
+278.937 426.711 16.0575 0.2005 re
+f*
+1 g
+294.994 426.711 4.6166 0.2005 re
+f*
+0 g
+299.611 426.711 4.0144 0.2005 re
+f*
+1 g
+303.625 426.711 3.8137 0.2005 re
+f*
+0 g
+307.439 426.711 4.8172 0.2005 re
+f*
+1 g
+312.256 426.711 3.0108 0.2005 re
+f*
+0 g
+315.267 426.711 7.8281 0.2005 re
+f*
+1 g
+323.095 426.711 3.0107 0.2005 re
+f*
+0 g
+326.106 426.711 6.2223 0.2005 re
+f*
+1 g
+332.328 426.711 3.2116 0.2005 re
+f*
+0 g
+335.54 426.711 7.4266 0.2005 re
+f*
+1 g
+342.966 426.711 3.4122 0.2005 re
+f*
+0 g
+346.379 426.711 5.018 0.2005 re
+f*
+1 g
+351.397 426.711 8.4302 0.2005 re
+f*
+0.498 0 0.482 rg
+359.827 426.711 3.8137 0.2005 re
+f*
+1 g
+363.641 426.711 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 426.711 9.4339 0.2005 re
+f*
+1 g
+376.687 426.711 2.6093 0.2005 re
+f*
+0.498 0 0.482 rg
+379.297 426.711 7.6274 0.2005 re
+f*
+1 g
+386.924 426.711 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+387.928 426.711 0.2006 0.2005 re
+f*
+1 g
+388.128 426.711 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 426.711 9.4338 0.2005 re
+f*
+1 g
+401.175 426.711 2.6094 0.2005 re
+f*
+0.498 0 0.482 rg
+403.784 426.711 6.6237 0.2005 re
+f*
+1 g
+410.408 426.711 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+413.419 426.711 3.4122 0.2005 re
+f*
+0 g
+251.037 426.912 20.4734 0.2006 re
+f*
+1 g
+271.51 426.912 7.4266 0.2006 re
+f*
+0 g
+278.937 426.912 16.0575 0.2006 re
+f*
+1 g
+294.994 426.912 4.6166 0.2006 re
+f*
+0 g
+299.611 426.912 4.0144 0.2006 re
+f*
+1 g
+303.625 426.912 3.8137 0.2006 re
+f*
+0 g
+307.439 426.912 5.0179 0.2006 re
+f*
+1 g
+312.457 426.912 3.0108 0.2006 re
+f*
+0 g
+315.468 426.912 7.6274 0.2006 re
+f*
+1 g
+323.095 426.912 2.81 0.2006 re
+f*
+0 g
+325.905 426.912 6.6238 0.2006 re
+f*
+1 g
+332.529 426.912 3.0108 0.2006 re
+f*
+0 g
+335.54 426.912 7.4266 0.2006 re
+f*
+1 g
+342.966 426.912 3.2115 0.2006 re
+f*
+0 g
+346.178 426.912 5.2187 0.2006 re
+f*
+1 g
+351.397 426.912 8.4302 0.2006 re
+f*
+0.498 0 0.482 rg
+359.827 426.912 3.8137 0.2006 re
+f*
+1 g
+363.641 426.912 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 426.912 9.6346 0.2006 re
+f*
+1 g
+376.888 426.912 2.6093 0.2006 re
+f*
+0.498 0 0.482 rg
+379.497 426.912 7.4267 0.2006 re
+f*
+1 g
+386.924 426.912 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+387.928 426.912 0.2006 0.2006 re
+f*
+1 g
+388.128 426.912 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 426.912 9.6345 0.2006 re
+f*
+1 g
+401.376 426.912 2.6094 0.2006 re
+f*
+0.498 0 0.482 rg
+403.985 426.912 6.423 0.2006 re
+f*
+1 g
+410.408 426.912 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+413.419 426.912 3.4122 0.2006 re
+f*
+0 g
+251.037 427.113 20.4734 0.2006 re
+f*
+1 g
+271.51 427.113 7.4266 0.2006 re
+f*
+0 g
+278.937 427.113 16.0575 0.2006 re
+f*
+1 g
+294.994 427.113 3.613 0.2006 re
+f*
+0 g
+298.607 427.113 0.2007 0.2006 re
+f*
+1 g
+298.808 427.113 1.0036 0.2006 re
+f*
+0 g
+299.812 427.113 4.0144 0.2006 re
+f*
+1 g
+303.826 427.113 3.613 0.2006 re
+f*
+0 g
+307.439 427.113 5.0179 0.2006 re
+f*
+1 g
+312.457 427.113 3.0108 0.2006 re
+f*
+0 g
+315.468 427.113 7.6274 0.2006 re
+f*
+1 g
+323.095 427.113 2.6093 0.2006 re
+f*
+0 g
+325.704 427.113 7.0252 0.2006 re
+f*
+1 g
+332.73 427.113 3.0108 0.2006 re
+f*
+0 g
+335.74 427.113 7.0252 0.2006 re
+f*
+1 g
+342.766 427.113 3.4122 0.2006 re
+f*
+0 g
+346.178 427.113 5.018 0.2006 re
+f*
+1 g
+351.196 427.113 8.6309 0.2006 re
+f*
+0.498 0 0.482 rg
+359.827 427.113 3.8137 0.2006 re
+f*
+1 g
+363.641 427.113 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 427.113 9.8353 0.2006 re
+f*
+1 g
+377.089 427.113 2.6094 0.2006 re
+f*
+0.498 0 0.482 rg
+379.698 427.113 7.0251 0.2006 re
+f*
+1 g
+386.723 427.113 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+387.727 427.113 0.4014 0.2006 re
+f*
+1 g
+388.128 427.113 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 427.113 9.8352 0.2006 re
+f*
+1 g
+401.576 427.113 2.4087 0.2006 re
+f*
+0.498 0 0.482 rg
+403.985 427.113 6.2223 0.2006 re
+f*
+1 g
+410.207 427.113 3.0107 0.2006 re
+f*
+0.498 0 0.482 rg
+413.218 427.113 3.613 0.2006 re
+f*
+0 g
+251.037 427.313 20.4734 0.2005 re
+f*
+1 g
+271.51 427.313 7.4266 0.2005 re
+f*
+0 g
+278.937 427.313 16.0575 0.2005 re
+f*
+1 g
+294.994 427.313 3.613 0.2005 re
+f*
+0 g
+298.607 427.313 0.2007 0.2005 re
+f*
+1 g
+298.808 427.313 1.0036 0.2005 re
+f*
+0 g
+299.812 427.313 4.0144 0.2005 re
+f*
+1 g
+303.826 427.313 3.613 0.2005 re
+f*
+0 g
+307.439 427.313 5.2187 0.2005 re
+f*
+1 g
+312.658 427.313 2.81 0.2005 re
+f*
+0 g
+315.468 427.313 7.6274 0.2005 re
+f*
+1 g
+323.095 427.313 2.6093 0.2005 re
+f*
+0 g
+325.704 427.313 7.2259 0.2005 re
+f*
+1 g
+332.93 427.313 2.8101 0.2005 re
+f*
+0 g
+335.74 427.313 7.0252 0.2005 re
+f*
+1 g
+342.766 427.313 3.2114 0.2005 re
+f*
+0 g
+345.977 427.313 5.2188 0.2005 re
+f*
+1 g
+351.196 427.313 8.6309 0.2005 re
+f*
+0.498 0 0.482 rg
+359.827 427.313 3.8137 0.2005 re
+f*
+1 g
+363.641 427.313 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 427.313 10.036 0.2005 re
+f*
+1 g
+377.289 427.313 2.4087 0.2005 re
+f*
+0.498 0 0.482 rg
+379.698 427.313 6.8244 0.2005 re
+f*
+1 g
+386.522 427.313 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+387.526 427.313 0.6021 0.2005 re
+f*
+1 g
+388.128 427.313 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 427.313 10.036 0.2005 re
+f*
+1 g
+401.777 427.313 2.4086 0.2005 re
+f*
+0.498 0 0.482 rg
+404.186 427.313 6.0216 0.2005 re
+f*
+1 g
+410.207 427.313 2.8101 0.2005 re
+f*
+0.498 0 0.482 rg
+413.017 427.313 3.8136 0.2005 re
+f*
+0 g
+251.037 427.514 20.4734 0.2006 re
+f*
+1 g
+271.51 427.514 7.4266 0.2006 re
+f*
+0 g
+278.937 427.514 16.0575 0.2006 re
+f*
+1 g
+294.994 427.514 3.613 0.2006 re
+f*
+0 g
+298.607 427.514 0.4014 0.2006 re
+f*
+1 g
+299.009 427.514 1.0036 0.2006 re
+f*
+0 g
+300.012 427.514 3.8137 0.2006 re
+f*
+1 g
+303.826 427.514 3.613 0.2006 re
+f*
+0 g
+307.439 427.514 5.4194 0.2006 re
+f*
+1 g
+312.858 427.514 2.8101 0.2006 re
+f*
+0 g
+315.669 427.514 7.2258 0.2006 re
+f*
+1 g
+322.894 427.514 2.6094 0.2006 re
+f*
+0 g
+325.504 427.514 7.4266 0.2006 re
+f*
+1 g
+332.93 427.514 3.0108 0.2006 re
+f*
+0 g
+335.941 427.514 6.8245 0.2006 re
+f*
+1 g
+342.766 427.514 3.2114 0.2006 re
+f*
+0 g
+345.977 427.514 5.018 0.2006 re
+f*
+1 g
+350.995 427.514 8.8317 0.2006 re
+f*
+0.498 0 0.482 rg
+359.827 427.514 3.8137 0.2006 re
+f*
+1 g
+363.641 427.514 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 427.514 10.2367 0.2006 re
+f*
+1 g
+377.49 427.514 2.4087 0.2006 re
+f*
+0.498 0 0.482 rg
+379.899 427.514 6.4229 0.2006 re
+f*
+1 g
+386.322 427.514 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+387.325 427.514 0.8029 0.2006 re
+f*
+1 g
+388.128 427.514 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 427.514 10.2367 0.2006 re
+f*
+1 g
+401.978 427.514 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+404.386 427.514 5.6202 0.2006 re
+f*
+1 g
+410.007 427.514 2.81 0.2006 re
+f*
+0.498 0 0.482 rg
+412.817 427.514 3.8137 0.2006 re
+f*
+0 g
+251.238 427.714 20.2727 0.2005 re
+f*
+1 g
+271.51 427.714 7.4266 0.2005 re
+f*
+0 g
+278.937 427.714 16.0575 0.2005 re
+f*
+1 g
+294.994 427.714 3.613 0.2005 re
+f*
+0 g
+298.607 427.714 0.4014 0.2005 re
+f*
+1 g
+299.009 427.714 1.0036 0.2005 re
+f*
+0 g
+300.012 427.714 4.0144 0.2005 re
+f*
+1 g
+304.027 427.714 3.4123 0.2005 re
+f*
+0 g
+307.439 427.714 5.6201 0.2005 re
+f*
+1 g
+313.059 427.714 2.6094 0.2005 re
+f*
+0 g
+315.669 427.714 7.2258 0.2005 re
+f*
+1 g
+322.894 427.714 2.4087 0.2005 re
+f*
+0 g
+325.303 427.714 7.828 0.2005 re
+f*
+1 g
+333.131 427.714 2.8101 0.2005 re
+f*
+0 g
+335.941 427.714 6.8245 0.2005 re
+f*
+1 g
+342.766 427.714 3.0108 0.2005 re
+f*
+0 g
+345.776 427.714 5.018 0.2005 re
+f*
+1 g
+350.794 427.714 9.0323 0.2005 re
+f*
+0.498 0 0.482 rg
+359.827 427.714 3.8137 0.2005 re
+f*
+1 g
+363.641 427.714 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 427.714 10.4375 0.2005 re
+f*
+1 g
+377.691 427.714 2.4086 0.2005 re
+f*
+0.498 0 0.482 rg
+380.099 427.714 6.0215 0.2005 re
+f*
+1 g
+386.121 427.714 1.2043 0.2005 re
+f*
+0.498 0 0.482 rg
+387.325 427.714 0.8029 0.2005 re
+f*
+1 g
+388.128 427.714 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 427.714 10.4374 0.2005 re
+f*
+1 g
+402.179 427.714 2.4086 0.2005 re
+f*
+0.498 0 0.482 rg
+404.587 427.714 5.4195 0.2005 re
+f*
+1 g
+410.007 427.714 2.6093 0.2005 re
+f*
+0.498 0 0.482 rg
+412.616 427.714 4.0144 0.2005 re
+f*
+0 g
+251.238 427.915 20.2727 0.2006 re
+f*
+1 g
+271.51 427.915 7.4266 0.2006 re
+f*
+0 g
+278.937 427.915 16.0575 0.2006 re
+f*
+1 g
+294.994 427.915 3.613 0.2006 re
+f*
+0 g
+298.607 427.915 0.6021 0.2006 re
+f*
+1 g
+299.209 427.915 1.0036 0.2006 re
+f*
+0 g
+300.213 427.915 3.8137 0.2006 re
+f*
+1 g
+304.027 427.915 3.4123 0.2006 re
+f*
+0 g
+307.439 427.915 5.8208 0.2006 re
+f*
+1 g
+313.26 427.915 2.6093 0.2006 re
+f*
+0 g
+315.869 427.915 7.0252 0.2006 re
+f*
+1 g
+322.894 427.915 2.208 0.2006 re
+f*
+0 g
+325.102 427.915 8.2294 0.2006 re
+f*
+1 g
+333.332 427.915 2.8101 0.2006 re
+f*
+0 g
+336.142 427.915 6.423 0.2006 re
+f*
+1 g
+342.565 427.915 3.0108 0.2006 re
+f*
+0 g
+345.576 427.915 5.2188 0.2006 re
+f*
+1 g
+350.794 427.915 8.8316 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 427.915 4.0144 0.2006 re
+f*
+1 g
+363.641 427.915 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 427.915 10.6382 0.2006 re
+f*
+1 g
+377.892 427.915 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+380.3 427.915 5.6201 0.2006 re
+f*
+1 g
+385.92 427.915 1.2043 0.2006 re
+f*
+0.498 0 0.482 rg
+387.125 427.915 1.0036 0.2006 re
+f*
+1 g
+388.128 427.915 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 427.915 10.8388 0.2006 re
+f*
+1 g
+402.58 427.915 2.208 0.2006 re
+f*
+0.498 0 0.482 rg
+404.788 427.915 5.018 0.2006 re
+f*
+1 g
+409.806 427.915 2.6093 0.2006 re
+f*
+0.498 0 0.482 rg
+412.415 427.915 4.2151 0.2006 re
+f*
+0 g
+251.238 428.115 20.2727 0.2006 re
+f*
+1 g
+271.51 428.115 7.4266 0.2006 re
+f*
+0 g
+278.937 428.115 16.0575 0.2006 re
+f*
+1 g
+294.994 428.115 3.613 0.2006 re
+f*
+0 g
+298.607 428.115 0.8029 0.2006 re
+f*
+1 g
+299.41 428.115 1.0036 0.2006 re
+f*
+0 g
+300.414 428.115 3.6129 0.2006 re
+f*
+1 g
+304.027 428.115 3.4123 0.2006 re
+f*
+0 g
+307.439 428.115 6.0215 0.2006 re
+f*
+1 g
+313.461 428.115 2.4086 0.2006 re
+f*
+0 g
+315.869 428.115 6.8246 0.2006 re
+f*
+1 g
+322.694 428.115 2.2078 0.2006 re
+f*
+0 g
+324.902 428.115 8.631 0.2006 re
+f*
+1 g
+333.533 428.115 2.6093 0.2006 re
+f*
+0 g
+336.142 428.115 6.423 0.2006 re
+f*
+1 g
+342.565 428.115 2.8101 0.2006 re
+f*
+0 g
+345.375 428.115 5.2187 0.2006 re
+f*
+1 g
+350.594 428.115 9.0324 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 428.115 4.0144 0.2006 re
+f*
+1 g
+363.641 428.115 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 428.115 11.0396 0.2006 re
+f*
+1 g
+378.293 428.115 2.2079 0.2006 re
+f*
+0.498 0 0.482 rg
+380.501 428.115 5.018 0.2006 re
+f*
+1 g
+385.519 428.115 1.4051 0.2006 re
+f*
+0.498 0 0.482 rg
+386.924 428.115 1.2042 0.2006 re
+f*
+1 g
+388.128 428.115 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 428.115 11.0396 0.2006 re
+f*
+1 g
+402.781 428.115 2.2079 0.2006 re
+f*
+0.498 0 0.482 rg
+404.989 428.115 4.6165 0.2006 re
+f*
+1 g
+409.605 428.115 2.4087 0.2006 re
+f*
+0.498 0 0.482 rg
+412.014 428.115 4.6165 0.2006 re
+f*
+0 g
+251.238 428.316 20.2727 0.2005 re
+f*
+1 g
+271.51 428.316 7.4266 0.2005 re
+f*
+0 g
+278.937 428.316 16.0575 0.2005 re
+f*
+1 g
+294.994 428.316 3.613 0.2005 re
+f*
+0 g
+298.607 428.316 0.8029 0.2005 re
+f*
+1 g
+299.41 428.316 1.2043 0.2005 re
+f*
+0 g
+300.615 428.316 3.4122 0.2005 re
+f*
+1 g
+304.027 428.316 3.2115 0.2005 re
+f*
+0 g
+307.238 428.316 6.4231 0.2005 re
+f*
+1 g
+313.661 428.316 2.4086 0.2005 re
+f*
+0 g
+316.07 428.316 6.6238 0.2005 re
+f*
+1 g
+322.694 428.316 2.0071 0.2005 re
+f*
+0 g
+324.701 428.316 9.0324 0.2005 re
+f*
+1 g
+333.733 428.316 2.6094 0.2005 re
+f*
+0 g
+336.343 428.316 6.0215 0.2005 re
+f*
+1 g
+342.364 428.316 3.0108 0.2005 re
+f*
+0 g
+345.375 428.316 5.018 0.2005 re
+f*
+1 g
+350.393 428.316 9.2331 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 428.316 1.0036 0.2005 re
+f*
+1 g
+360.63 428.316 0.2007 0.2005 re
+f*
+0.498 0 0.482 rg
+360.83 428.316 2.8101 0.2005 re
+f*
+1 g
+363.641 428.316 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 428.316 11.2403 0.2005 re
+f*
+1 g
+378.494 428.316 2.4087 0.2005 re
+f*
+0.498 0 0.482 rg
+380.902 428.316 4.215 0.2005 re
+f*
+1 g
+385.117 428.316 1.6058 0.2005 re
+f*
+0.498 0 0.482 rg
+386.723 428.316 1.405 0.2005 re
+f*
+1 g
+388.128 428.316 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 428.316 11.441 0.2005 re
+f*
+1 g
+403.182 428.316 2.2079 0.2005 re
+f*
+0.498 0 0.482 rg
+405.39 428.316 3.8137 0.2005 re
+f*
+1 g
+409.204 428.316 2.6093 0.2005 re
+f*
+0.498 0 0.482 rg
+411.813 428.316 4.8173 0.2005 re
+f*
+0 g
+251.438 428.516 20.072 0.2006 re
+f*
+1 g
+271.51 428.516 7.4266 0.2006 re
+f*
+0 g
+278.937 428.516 16.0575 0.2006 re
+f*
+1 g
+294.994 428.516 3.613 0.2006 re
+f*
+0 g
+298.607 428.516 1.0036 0.2006 re
+f*
+1 g
+299.611 428.516 1.2043 0.2006 re
+f*
+0 g
+300.815 428.516 3.2115 0.2006 re
+f*
+1 g
+304.027 428.516 3.2115 0.2006 re
+f*
+0 g
+307.238 428.516 6.8245 0.2006 re
+f*
+1 g
+314.063 428.516 2.2079 0.2006 re
+f*
+0 g
+316.271 428.516 6.2223 0.2006 re
+f*
+1 g
+322.493 428.516 2.0072 0.2006 re
+f*
+0 g
+324.5 428.516 9.4338 0.2006 re
+f*
+1 g
+333.934 428.516 2.6094 0.2006 re
+f*
+0 g
+336.543 428.516 5.8208 0.2006 re
+f*
+1 g
+342.364 428.516 2.8101 0.2006 re
+f*
+0 g
+345.174 428.516 5.2187 0.2006 re
+f*
+1 g
+350.393 428.516 9.2331 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 428.516 1.0036 0.2006 re
+f*
+1 g
+360.63 428.516 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 428.516 11.6418 0.2006 re
+f*
+1 g
+378.895 428.516 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+381.304 428.516 3.4122 0.2006 re
+f*
+1 g
+384.716 428.516 1.6057 0.2006 re
+f*
+0.498 0 0.482 rg
+386.322 428.516 1.8065 0.2006 re
+f*
+1 g
+388.128 428.516 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 428.516 11.6417 0.2006 re
+f*
+1 g
+403.383 428.516 2.4087 0.2006 re
+f*
+0.498 0 0.482 rg
+405.792 428.516 3.0108 0.2006 re
+f*
+1 g
+408.802 428.516 2.6093 0.2006 re
+f*
+0.498 0 0.482 rg
+411.412 428.516 5.018 0.2006 re
+f*
+0 g
+251.438 428.717 20.072 0.2005 re
+f*
+1 g
+271.51 428.717 7.4266 0.2005 re
+f*
+0 g
+278.937 428.717 16.0575 0.2005 re
+f*
+1 g
+294.994 428.717 3.613 0.2005 re
+f*
+0 g
+298.607 428.717 1.2043 0.2005 re
+f*
+1 g
+299.812 428.717 1.2043 0.2005 re
+f*
+0 g
+301.016 428.717 3.0108 0.2005 re
+f*
+1 g
+304.027 428.717 3.2115 0.2005 re
+f*
+0 g
+307.238 428.717 7.0252 0.2005 re
+f*
+1 g
+314.263 428.717 2.2079 0.2005 re
+f*
+0 g
+316.471 428.717 6.0216 0.2005 re
+f*
+1 g
+322.493 428.717 1.8065 0.2005 re
+f*
+0 g
+324.299 428.717 10.0359 0.2005 re
+f*
+1 g
+334.335 428.717 2.4087 0.2005 re
+f*
+0 g
+336.744 428.717 5.4194 0.2005 re
+f*
+1 g
+342.163 428.717 2.81 0.2005 re
+f*
+0 g
+344.973 428.717 5.2188 0.2005 re
+f*
+1 g
+350.192 428.717 9.4338 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 428.717 1.0036 0.2005 re
+f*
+1 g
+360.63 428.717 6.6237 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 428.717 12.0432 0.2005 re
+f*
+1 g
+379.297 428.717 2.81 0.2005 re
+f*
+0.498 0 0.482 rg
+382.107 428.717 1.8066 0.2005 re
+f*
+1 g
+383.913 428.717 2.2078 0.2005 re
+f*
+0.498 0 0.482 rg
+386.121 428.717 2.0072 0.2005 re
+f*
+1 g
+388.128 428.717 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 428.717 12.0432 0.2005 re
+f*
+1 g
+403.784 428.717 2.81 0.2005 re
+f*
+0.498 0 0.482 rg
+406.594 428.717 1.6058 0.2005 re
+f*
+1 g
+408.2 428.717 2.8101 0.2005 re
+f*
+0.498 0 0.482 rg
+411.01 428.717 5.4194 0.2005 re
+f*
+0 g
+251.438 428.918 20.072 0.2006 re
+f*
+1 g
+271.51 428.918 7.4266 0.2006 re
+f*
+0 g
+278.937 428.918 16.0575 0.2006 re
+f*
+1 g
+294.994 428.918 3.613 0.2006 re
+f*
+0 g
+298.607 428.918 1.405 0.2006 re
+f*
+1 g
+300.012 428.918 1.2043 0.2006 re
+f*
+0 g
+301.217 428.918 2.8101 0.2006 re
+f*
+1 g
+304.027 428.918 3.0108 0.2006 re
+f*
+0 g
+307.038 428.918 7.4266 0.2006 re
+f*
+1 g
+314.464 428.918 2.2079 0.2006 re
+f*
+0 g
+316.672 428.918 5.6202 0.2006 re
+f*
+1 g
+322.292 428.918 1.8065 0.2006 re
+f*
+0 g
+324.099 428.918 10.4374 0.2006 re
+f*
+1 g
+334.536 428.918 2.4086 0.2006 re
+f*
+0 g
+336.945 428.918 5.018 0.2006 re
+f*
+1 g
+341.963 428.918 2.8101 0.2006 re
+f*
+0 g
+344.773 428.918 5.2186 0.2006 re
+f*
+1 g
+349.991 428.918 9.6346 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 428.918 1.0036 0.2006 re
+f*
+1 g
+360.63 428.918 6.6237 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 428.918 12.4447 0.2006 re
+f*
+1 g
+379.698 428.918 6.0215 0.2006 re
+f*
+0.498 0 0.482 rg
+385.72 428.918 2.4086 0.2006 re
+f*
+1 g
+388.128 428.918 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 428.918 12.6453 0.2006 re
+f*
+1 g
+404.386 428.918 6.2223 0.2006 re
+f*
+0.498 0 0.482 rg
+410.609 428.918 5.8209 0.2006 re
+f*
+0 g
+251.438 429.118 20.2727 0.2006 re
+f*
+1 g
+271.711 429.118 7.2259 0.2006 re
+f*
+0 g
+278.937 429.118 16.0575 0.2006 re
+f*
+1 g
+294.994 429.118 3.613 0.2006 re
+f*
+0 g
+298.607 429.118 1.6057 0.2006 re
+f*
+1 g
+300.213 429.118 1.2044 0.2006 re
+f*
+0 g
+301.417 429.118 2.4086 0.2006 re
+f*
+1 g
+303.826 429.118 3.2115 0.2006 re
+f*
+0 g
+307.038 429.118 7.828 0.2006 re
+f*
+1 g
+314.866 429.118 2.0072 0.2006 re
+f*
+0 g
+316.873 429.118 5.2188 0.2006 re
+f*
+1 g
+322.092 429.118 1.8064 0.2006 re
+f*
+0 g
+323.898 429.118 10.8389 0.2006 re
+f*
+1 g
+334.737 429.118 2.4086 0.2006 re
+f*
+0 g
+337.146 429.118 4.6166 0.2006 re
+f*
+1 g
+341.762 429.118 2.6093 0.2006 re
+f*
+0 g
+344.371 429.118 5.4195 0.2006 re
+f*
+1 g
+349.791 429.118 9.8352 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 429.118 20.4734 0.2006 re
+f*
+1 g
+380.099 429.118 5.2187 0.2006 re
+f*
+0.498 0 0.482 rg
+385.318 429.118 2.81 0.2006 re
+f*
+1 g
+388.128 429.118 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 429.118 13.0468 0.2006 re
+f*
+1 g
+404.788 429.118 5.4194 0.2006 re
+f*
+0.498 0 0.482 rg
+410.207 429.118 6.2223 0.2006 re
+f*
+0 g
+251.639 429.319 20.0719 0.2005 re
+f*
+1 g
+271.711 429.319 7.2259 0.2005 re
+f*
+0 g
+278.937 429.319 16.0575 0.2005 re
+f*
+1 g
+294.994 429.319 3.613 0.2005 re
+f*
+0 g
+298.607 429.319 1.8065 0.2005 re
+f*
+1 g
+300.414 429.319 1.405 0.2005 re
+f*
+0 g
+301.819 429.319 1.8065 0.2005 re
+f*
+1 g
+303.625 429.319 3.2115 0.2005 re
+f*
+0 g
+306.837 429.319 8.4302 0.2005 re
+f*
+1 g
+315.267 429.319 1.8065 0.2005 re
+f*
+0 g
+317.074 429.319 4.8172 0.2005 re
+f*
+1 g
+321.891 429.319 1.6058 0.2005 re
+f*
+0 g
+323.497 429.319 11.6417 0.2005 re
+f*
+1 g
+335.138 429.319 2.208 0.2005 re
+f*
+0 g
+337.346 429.319 4.2151 0.2005 re
+f*
+1 g
+341.561 429.319 2.6093 0.2005 re
+f*
+0 g
+344.171 429.319 5.6202 0.2005 re
+f*
+1 g
+349.791 429.319 9.8352 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 429.319 21.2763 0.2005 re
+f*
+1 g
+380.902 429.319 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+384.716 429.319 3.4122 0.2005 re
+f*
+1 g
+388.128 429.319 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 429.319 13.8497 0.2005 re
+f*
+1 g
+405.591 429.319 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+409.404 429.319 6.8244 0.2005 re
+f*
+0 g
+251.639 429.519 20.0719 0.2005 re
+f*
+1 g
+271.711 429.519 7.2259 0.2005 re
+f*
+0 g
+278.937 429.519 13.0467 0.2005 re
+f*
+1 g
+291.984 429.519 0.2008 0.2005 re
+f*
+0 g
+292.184 429.519 2.81 0.2005 re
+f*
+1 g
+294.994 429.519 3.613 0.2005 re
+f*
+0 g
+298.607 429.519 2.0072 0.2005 re
+f*
+1 g
+300.615 429.519 1.6057 0.2005 re
+f*
+0 g
+302.22 429.519 1.2044 0.2005 re
+f*
+1 g
+303.425 429.519 3.2115 0.2005 re
+f*
+0 g
+306.636 429.519 9.0324 0.2005 re
+f*
+1 g
+315.669 429.519 1.8064 0.2005 re
+f*
+0 g
+317.475 429.519 4.2152 0.2005 re
+f*
+1 g
+321.69 429.519 1.405 0.2005 re
+f*
+0 g
+323.095 429.519 12.2439 0.2005 re
+f*
+1 g
+335.339 429.519 2.4086 0.2005 re
+f*
+0 g
+337.748 429.519 3.6129 0.2005 re
+f*
+1 g
+341.361 429.519 2.4087 0.2005 re
+f*
+0 g
+343.769 429.519 5.8208 0.2005 re
+f*
+1 g
+349.59 429.519 10.036 0.2005 re
+f*
+0.498 0 0.482 rg
+359.626 429.519 22.4805 0.2005 re
+f*
+1 g
+382.107 429.519 1.4051 0.2005 re
+f*
+0.498 0 0.482 rg
+383.512 429.519 4.6165 0.2005 re
+f*
+1 g
+388.128 429.519 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 429.519 15.054 0.2005 re
+f*
+1 g
+406.795 429.519 1.6057 0.2005 re
+f*
+0.498 0 0.482 rg
+408.401 429.519 7.828 0.2005 re
+f*
+0 g
+251.639 429.72 20.0719 0.2006 re
+f*
+1 g
+271.711 429.72 7.2259 0.2006 re
+f*
+0 g
+278.937 429.72 13.0467 0.2006 re
+f*
+1 g
+291.984 429.72 6.6238 0.2006 re
+f*
+0 g
+298.607 429.72 2.2079 0.2006 re
+f*
+1 g
+300.815 429.72 5.6202 0.2006 re
+f*
+0 g
+306.435 429.72 9.6345 0.2006 re
+f*
+1 g
+316.07 429.72 1.8064 0.2006 re
+f*
+0 g
+317.876 429.72 3.4123 0.2006 re
+f*
+1 g
+321.289 429.72 1.4051 0.2006 re
+f*
+0 g
+322.694 429.72 13.0467 0.2006 re
+f*
+1 g
+335.74 429.72 2.6093 0.2006 re
+f*
+0 g
+338.35 429.72 2.4087 0.2006 re
+f*
+1 g
+340.758 429.72 2.81 0.2006 re
+f*
+0 g
+343.568 429.72 5.8209 0.2006 re
+f*
+1 g
+349.389 429.72 10.2367 0.2006 re
+f*
+0.498 0 0.482 rg
+359.626 429.72 28.5021 0.2006 re
+f*
+1 g
+388.128 429.72 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 429.72 24.4877 0.2006 re
+f*
+0 g
+251.639 429.92 20.0719 0.2006 re
+f*
+1 g
+271.711 429.92 7.2259 0.2006 re
+f*
+0 g
+278.937 429.92 13.0467 0.2006 re
+f*
+1 g
+291.984 429.92 6.6238 0.2006 re
+f*
+0 g
+298.607 429.92 2.6093 0.2006 re
+f*
+1 g
+301.217 429.92 5.018 0.2006 re
+f*
+0 g
+306.235 429.92 10.4374 0.2006 re
+f*
+1 g
+316.672 429.92 2.0073 0.2006 re
+f*
+0 g
+318.679 429.92 1.8064 0.2006 re
+f*
+1 g
+320.486 429.92 1.6058 0.2006 re
+f*
+0 g
+322.092 429.92 14.0503 0.2006 re
+f*
+1 g
+336.142 429.92 7.0252 0.2006 re
+f*
+0 g
+343.167 429.92 6.0216 0.2006 re
+f*
+1 g
+349.189 429.92 10.2367 0.2006 re
+f*
+0.498 0 0.482 rg
+359.425 429.92 28.7028 0.2006 re
+f*
+1 g
+388.128 429.92 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 429.92 24.2871 0.2006 re
+f*
+0 g
+251.84 430.121 19.8712 0.2006 re
+f*
+1 g
+271.711 430.121 7.2259 0.2006 re
+f*
+0 g
+278.937 430.121 13.0467 0.2006 re
+f*
+1 g
+291.984 430.121 6.6238 0.2006 re
+f*
+0 g
+298.607 430.121 2.8101 0.2006 re
+f*
+1 g
+301.417 430.121 4.4158 0.2006 re
+f*
+0 g
+305.833 430.121 11.6417 0.2006 re
+f*
+1 g
+317.475 430.121 4.0144 0.2006 re
+f*
+0 g
+321.489 430.121 15.2547 0.2006 re
+f*
+1 g
+336.744 430.121 6.0216 0.2006 re
+f*
+0 g
+342.766 430.121 6.2222 0.2006 re
+f*
+1 g
+348.988 430.121 10.4375 0.2006 re
+f*
+0.498 0 0.482 rg
+359.425 430.121 28.7028 0.2006 re
+f*
+1 g
+388.128 430.121 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 430.121 24.2871 0.2006 re
+f*
+0 g
+251.84 430.322 19.8712 0.2005 re
+f*
+1 g
+271.711 430.322 7.2259 0.2005 re
+f*
+0 g
+278.937 430.322 23.0827 0.2005 re
+f*
+1 g
+302.02 430.322 3.4123 0.2005 re
+f*
+0 g
+305.432 430.322 13.4481 0.2005 re
+f*
+1 g
+318.88 430.322 1.6058 0.2005 re
+f*
+0 g
+320.486 430.322 16.8605 0.2005 re
+f*
+1 g
+337.346 430.322 4.8172 0.2005 re
+f*
+0 g
+342.163 430.322 6.6238 0.2005 re
+f*
+1 g
+348.787 430.322 10.6381 0.2005 re
+f*
+0.498 0 0.482 rg
+359.425 430.322 28.7028 0.2005 re
+f*
+1 g
+388.128 430.322 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 430.322 24.2871 0.2005 re
+f*
+0 g
+251.84 430.522 19.8712 0.2006 re
+f*
+1 g
+271.711 430.522 7.2259 0.2006 re
+f*
+0 g
+278.937 430.522 23.6849 0.2006 re
+f*
+1 g
+302.622 430.522 2.2079 0.2006 re
+f*
+0 g
+304.83 430.522 33.3194 0.2006 re
+f*
+1 g
+338.149 430.522 3.2115 0.2006 re
+f*
+0 g
+341.361 430.522 7.2259 0.2006 re
+f*
+1 g
+348.586 430.522 10.8389 0.2006 re
+f*
+0.498 0 0.482 rg
+359.425 430.522 28.7028 0.2006 re
+f*
+1 g
+388.128 430.522 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 430.522 24.0863 0.2006 re
+f*
+0 g
+252.04 430.723 19.8712 0.2005 re
+f*
+1 g
+271.912 430.723 7.0252 0.2005 re
+f*
+0 g
+278.937 430.723 69.4489 0.2005 re
+f*
+1 g
+348.386 430.723 11.0396 0.2005 re
+f*
+0.498 0 0.482 rg
+359.425 430.723 28.7028 0.2005 re
+f*
+1 g
+388.128 430.723 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 430.723 24.0863 0.2005 re
+f*
+0 g
+252.04 430.923 19.8712 0.2006 re
+f*
+1 g
+271.912 430.923 7.0252 0.2006 re
+f*
+0 g
+278.937 430.923 69.2482 0.2006 re
+f*
+1 g
+348.185 430.923 11.0395 0.2006 re
+f*
+0.498 0 0.482 rg
+359.225 430.923 28.9036 0.2006 re
+f*
+1 g
+388.128 430.923 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 430.923 24.0863 0.2006 re
+f*
+0 g
+252.04 431.124 19.8712 0.2006 re
+f*
+1 g
+271.912 431.124 7.0252 0.2006 re
+f*
+0 g
+278.937 431.124 69.0474 0.2006 re
+f*
+1 g
+347.984 431.124 11.2403 0.2006 re
+f*
+0.498 0 0.482 rg
+359.225 431.124 28.9036 0.2006 re
+f*
+1 g
+388.128 431.124 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 431.124 23.8856 0.2006 re
+f*
+0 g
+252.241 431.324 19.6705 0.2005 re
+f*
+1 g
+271.912 431.324 7.0252 0.2005 re
+f*
+0 g
+278.937 431.324 68.8468 0.2005 re
+f*
+1 g
+347.784 431.324 11.4409 0.2005 re
+f*
+0.498 0 0.482 rg
+359.225 431.324 28.9036 0.2005 re
+f*
+1 g
+388.128 431.324 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 431.324 23.8856 0.2005 re
+f*
+0 g
+252.241 431.525 19.6705 0.2005 re
+f*
+1 g
+271.912 431.525 7.0252 0.2005 re
+f*
+0 g
+278.937 431.525 68.4453 0.2005 re
+f*
+1 g
+347.382 431.525 11.8424 0.2005 re
+f*
+0.498 0 0.482 rg
+359.225 431.525 28.9036 0.2005 re
+f*
+1 g
+388.128 431.525 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 431.525 23.8856 0.2005 re
+f*
+0 g
+252.241 431.725 19.6705 0.2006 re
+f*
+1 g
+271.912 431.725 7.0252 0.2006 re
+f*
+0 g
+278.937 431.725 68.2446 0.2006 re
+f*
+1 g
+347.181 431.725 11.8424 0.2006 re
+f*
+0.498 0 0.482 rg
+359.024 431.725 29.1043 0.2006 re
+f*
+1 g
+388.128 431.725 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 431.725 23.6849 0.2006 re
+f*
+0 g
+252.442 431.926 19.6705 0.2006 re
+f*
+1 g
+272.112 431.926 6.8245 0.2006 re
+f*
+0 g
+278.937 431.926 68.0438 0.2006 re
+f*
+1 g
+346.981 431.926 12.0432 0.2006 re
+f*
+0.498 0 0.482 rg
+359.024 431.926 29.1043 0.2006 re
+f*
+1 g
+388.128 431.926 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 431.926 23.6849 0.2006 re
+f*
+0 g
+252.442 432.127 19.6705 0.2005 re
+f*
+1 g
+272.112 432.127 6.8245 0.2005 re
+f*
+0 g
+278.937 432.127 67.6424 0.2005 re
+f*
+1 g
+346.579 432.127 12.4446 0.2005 re
+f*
+0.498 0 0.482 rg
+359.024 432.127 29.1043 0.2005 re
+f*
+1 g
+388.128 432.127 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 432.127 23.4841 0.2005 re
+f*
+0 g
+252.643 432.327 19.4697 0.2005 re
+f*
+1 g
+272.112 432.327 6.8245 0.2005 re
+f*
+0 g
+278.937 432.327 67.4417 0.2005 re
+f*
+1 g
+346.379 432.327 12.6453 0.2005 re
+f*
+0.498 0 0.482 rg
+359.024 432.327 29.1043 0.2005 re
+f*
+1 g
+388.128 432.327 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 432.327 23.4841 0.2005 re
+f*
+0 g
+252.643 432.528 19.4697 0.2006 re
+f*
+1 g
+272.112 432.528 6.8245 0.2006 re
+f*
+0 g
+278.937 432.528 67.0402 0.2006 re
+f*
+1 g
+345.977 432.528 12.8461 0.2006 re
+f*
+0.498 0 0.482 rg
+358.823 432.528 29.305 0.2006 re
+f*
+1 g
+388.128 432.528 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 432.528 23.4841 0.2006 re
+f*
+0 g
+252.643 432.728 19.6705 0.2006 re
+f*
+1 g
+272.313 432.728 6.6237 0.2006 re
+f*
+0 g
+278.937 432.728 66.8396 0.2006 re
+f*
+1 g
+345.776 432.728 13.0467 0.2006 re
+f*
+0.498 0 0.482 rg
+358.823 432.728 29.305 0.2006 re
+f*
+1 g
+388.128 432.728 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 432.728 23.2835 0.2006 re
+f*
+0 g
+252.843 432.929 19.4698 0.2006 re
+f*
+1 g
+272.313 432.929 6.6237 0.2006 re
+f*
+0 g
+278.937 432.929 66.4381 0.2006 re
+f*
+1 g
+345.375 432.929 13.4482 0.2006 re
+f*
+0.498 0 0.482 rg
+358.823 432.929 29.305 0.2006 re
+f*
+1 g
+388.128 432.929 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 432.929 23.2835 0.2006 re
+f*
+0 g
+252.843 433.129 19.4698 0.2006 re
+f*
+1 g
+272.313 433.129 6.6237 0.2006 re
+f*
+0 g
+278.937 433.129 66.0366 0.2006 re
+f*
+1 g
+344.973 433.129 13.649 0.2006 re
+f*
+0.498 0 0.482 rg
+358.623 433.129 29.5057 0.2006 re
+f*
+1 g
+388.128 433.129 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 433.129 23.2835 0.2006 re
+f*
+0 g
+253.044 433.33 19.2691 0.2005 re
+f*
+1 g
+272.313 433.33 6.8244 0.2005 re
+f*
+0 g
+279.138 433.33 65.4345 0.2005 re
+f*
+1 g
+344.572 433.33 14.0504 0.2005 re
+f*
+0.498 0 0.482 rg
+358.623 433.33 29.5057 0.2005 re
+f*
+1 g
+388.128 433.33 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 433.33 23.0827 0.2005 re
+f*
+0 g
+253.044 433.53 19.4698 0.2005 re
+f*
+1 g
+272.514 433.53 6.6237 0.2005 re
+f*
+0 g
+279.138 433.53 65.0331 0.2005 re
+f*
+1 g
+344.171 433.53 14.2511 0.2005 re
+f*
+0.498 0 0.482 rg
+358.422 433.53 29.7064 0.2005 re
+f*
+1 g
+388.128 433.53 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 433.53 23.0827 0.2005 re
+f*
+0 g
+253.044 433.731 19.4698 0.2006 re
+f*
+1 g
+272.514 433.731 6.6237 0.2006 re
+f*
+0 g
+279.138 433.731 64.6317 0.2006 re
+f*
+1 g
+343.769 433.731 14.6525 0.2006 re
+f*
+0.498 0 0.482 rg
+358.422 433.731 29.7064 0.2006 re
+f*
+1 g
+388.128 433.731 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 433.731 22.882 0.2006 re
+f*
+0 g
+253.245 433.932 19.2691 0.2006 re
+f*
+1 g
+272.514 433.932 6.6237 0.2006 re
+f*
+0 g
+279.138 433.932 64.2302 0.2006 re
+f*
+1 g
+343.368 433.932 14.8532 0.2006 re
+f*
+0.498 0 0.482 rg
+358.221 433.932 29.9072 0.2006 re
+f*
+1 g
+388.128 433.932 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 433.932 22.882 0.2006 re
+f*
+0 g
+253.245 434.132 19.2691 0.2005 re
+f*
+1 g
+272.514 434.132 6.6237 0.2005 re
+f*
+0 g
+279.138 434.132 63.8288 0.2005 re
+f*
+1 g
+342.966 434.132 15.2546 0.2005 re
+f*
+0.498 0 0.482 rg
+358.221 434.132 29.9072 0.2005 re
+f*
+1 g
+388.128 434.132 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 434.132 22.6813 0.2005 re
+f*
+0 g
+253.445 434.333 19.2691 0.2006 re
+f*
+1 g
+272.715 434.333 6.423 0.2006 re
+f*
+0 g
+279.138 434.333 15.4554 0.2006 re
+f*
+1 g
+294.593 434.333 2.4086 0.2006 re
+f*
+0 g
+297.002 434.333 45.3626 0.2006 re
+f*
+1 g
+342.364 434.333 15.6561 0.2006 re
+f*
+0.498 0 0.482 rg
+358.02 434.333 30.1079 0.2006 re
+f*
+1 g
+388.128 434.333 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 434.333 22.6813 0.2006 re
+f*
+0 g
+253.445 434.533 19.2691 0.2005 re
+f*
+1 g
+272.715 434.533 6.423 0.2005 re
+f*
+0 g
+279.138 434.533 14.6525 0.2005 re
+f*
+1 g
+293.79 434.533 3.8137 0.2005 re
+f*
+0 g
+297.604 434.533 44.359 0.2005 re
+f*
+1 g
+341.963 434.533 16.0575 0.2005 re
+f*
+0.498 0 0.482 rg
+358.02 434.533 30.1079 0.2005 re
+f*
+1 g
+388.128 434.533 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 434.533 22.4805 0.2005 re
+f*
+0 g
+253.646 434.734 19.0683 0.2006 re
+f*
+1 g
+272.715 434.734 6.423 0.2006 re
+f*
+0 g
+279.138 434.734 14.0504 0.2006 re
+f*
+1 g
+293.188 434.734 4.8172 0.2006 re
+f*
+0 g
+298.005 434.734 43.3554 0.2006 re
+f*
+1 g
+341.361 434.734 16.459 0.2006 re
+f*
+0.498 0 0.482 rg
+357.82 434.734 30.3086 0.2006 re
+f*
+1 g
+388.128 434.734 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 434.734 22.4805 0.2006 re
+f*
+0 g
+253.646 434.934 19.269 0.2005 re
+f*
+1 g
+272.915 434.934 6.2223 0.2005 re
+f*
+0 g
+279.138 434.934 13.6489 0.2005 re
+f*
+1 g
+292.786 434.934 5.6202 0.2005 re
+f*
+0 g
+298.407 434.934 42.3518 0.2005 re
+f*
+1 g
+340.758 434.934 17.0611 0.2005 re
+f*
+0.498 0 0.482 rg
+357.82 434.934 30.3086 0.2005 re
+f*
+1 g
+388.128 434.934 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 434.934 22.4805 0.2005 re
+f*
+0 g
+253.847 435.135 19.0683 0.2006 re
+f*
+1 g
+272.915 435.135 6.2223 0.2006 re
+f*
+0 g
+279.138 435.135 13.2475 0.2006 re
+f*
+1 g
+292.385 435.135 6.2223 0.2006 re
+f*
+0 g
+298.607 435.135 41.5489 0.2006 re
+f*
+1 g
+340.156 435.135 17.4626 0.2006 re
+f*
+0.498 0 0.482 rg
+357.619 435.135 30.5093 0.2006 re
+f*
+1 g
+388.128 435.135 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 435.135 22.2799 0.2006 re
+f*
+0 g
+253.847 435.335 19.0683 0.2005 re
+f*
+1 g
+272.915 435.335 6.2223 0.2005 re
+f*
+0 g
+279.138 435.335 13.0468 0.2005 re
+f*
+1 g
+292.184 435.335 6.6237 0.2005 re
+f*
+0 g
+298.808 435.335 40.7461 0.2005 re
+f*
+1 g
+339.554 435.335 17.864 0.2005 re
+f*
+0.498 0 0.482 rg
+357.418 435.335 30.71 0.2005 re
+f*
+1 g
+388.128 435.335 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 435.335 22.2799 0.2005 re
+f*
+0 g
+254.048 435.536 19.0683 0.2006 re
+f*
+1 g
+273.116 435.536 6.0216 0.2006 re
+f*
+0 g
+279.138 435.536 13.0468 0.2006 re
+f*
+1 g
+292.184 435.536 6.8244 0.2006 re
+f*
+0 g
+299.009 435.536 39.7425 0.2006 re
+f*
+1 g
+338.751 435.536 18.6669 0.2006 re
+f*
+0.498 0 0.482 rg
+357.418 435.536 30.71 0.2006 re
+f*
+1 g
+388.128 435.536 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 435.536 22.0791 0.2006 re
+f*
+0 g
+254.048 435.737 19.0683 0.2006 re
+f*
+1 g
+273.116 435.737 6.0216 0.2006 re
+f*
+0 g
+279.138 435.737 12.846 0.2006 re
+f*
+1 g
+291.984 435.737 7.0252 0.2006 re
+f*
+0 g
+299.009 435.737 39.1403 0.2006 re
+f*
+1 g
+338.149 435.737 19.0683 0.2006 re
+f*
+0.498 0 0.482 rg
+357.217 435.737 30.9108 0.2006 re
+f*
+1 g
+388.128 435.737 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 435.737 22.0791 0.2006 re
+f*
+0 g
+254.248 435.937 19.0683 0.2006 re
+f*
+1 g
+273.317 435.937 5.8209 0.2006 re
+f*
+0 g
+279.138 435.937 12.6453 0.2006 re
+f*
+1 g
+291.783 435.937 7.4266 0.2006 re
+f*
+0 g
+299.209 435.937 38.3375 0.2006 re
+f*
+1 g
+337.547 435.937 19.4697 0.2006 re
+f*
+0.498 0 0.482 rg
+357.017 435.937 31.1115 0.2006 re
+f*
+1 g
+388.128 435.937 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 435.937 21.8784 0.2006 re
+f*
+0 g
+254.248 436.138 19.0683 0.2005 re
+f*
+1 g
+273.317 436.138 6.0216 0.2005 re
+f*
+0 g
+279.338 436.138 12.4446 0.2005 re
+f*
+1 g
+291.783 436.138 7.4266 0.2005 re
+f*
+0 g
+299.209 436.138 37.7353 0.2005 re
+f*
+1 g
+336.945 436.138 20.0719 0.2005 re
+f*
+0.498 0 0.482 rg
+357.017 436.138 7.4267 0.2005 re
+f*
+1 g
+364.443 436.138 1.8064 0.2005 re
+f*
+0.498 0 0.482 rg
+366.25 436.138 21.8784 0.2005 re
+f*
+1 g
+388.128 436.138 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 436.138 21.8784 0.2005 re
+f*
+0 g
+254.449 436.338 19.0684 0.2005 re
+f*
+1 g
+273.517 436.338 5.8208 0.2005 re
+f*
+0 g
+279.338 436.338 12.4446 0.2005 re
+f*
+1 g
+291.783 436.338 7.6274 0.2005 re
+f*
+0 g
+299.41 436.338 36.9324 0.2005 re
+f*
+1 g
+336.343 436.338 20.4733 0.2005 re
+f*
+0.498 0 0.482 rg
+356.816 436.338 7.2259 0.2005 re
+f*
+1 g
+364.042 436.338 2.6094 0.2005 re
+f*
+0.498 0 0.482 rg
+366.651 436.338 21.4769 0.2005 re
+f*
+1 g
+388.128 436.338 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 436.338 21.6777 0.2005 re
+f*
+0 g
+254.449 436.539 19.0684 0.2006 re
+f*
+1 g
+273.517 436.539 5.8208 0.2006 re
+f*
+0 g
+279.338 436.539 12.4446 0.2006 re
+f*
+1 g
+291.783 436.539 7.8281 0.2006 re
+f*
+0 g
+299.611 436.539 35.9288 0.2006 re
+f*
+1 g
+335.54 436.539 21.0755 0.2006 re
+f*
+0.498 0 0.482 rg
+356.615 436.539 7.2259 0.2006 re
+f*
+1 g
+363.841 436.539 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+366.852 436.539 21.2762 0.2006 re
+f*
+1 g
+388.128 436.539 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 436.539 21.6777 0.2006 re
+f*
+0 g
+254.65 436.739 19.0684 0.2006 re
+f*
+1 g
+273.718 436.739 5.6201 0.2006 re
+f*
+0 g
+279.338 436.739 12.4446 0.2006 re
+f*
+1 g
+291.783 436.739 7.8281 0.2006 re
+f*
+0 g
+299.611 436.739 35.3266 0.2006 re
+f*
+1 g
+334.938 436.739 21.477 0.2006 re
+f*
+0.498 0 0.482 rg
+356.415 436.739 7.2259 0.2006 re
+f*
+1 g
+363.641 436.739 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+367.053 436.739 21.0755 0.2006 re
+f*
+1 g
+388.128 436.739 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 436.739 21.4769 0.2006 re
+f*
+0 g
+254.65 436.94 19.0684 0.2006 re
+f*
+1 g
+273.718 436.94 5.6201 0.2006 re
+f*
+0 g
+279.338 436.94 12.4446 0.2006 re
+f*
+1 g
+291.783 436.94 7.8281 0.2006 re
+f*
+0 g
+299.611 436.94 34.7244 0.2006 re
+f*
+1 g
+334.335 436.94 22.0792 0.2006 re
+f*
+0.498 0 0.482 rg
+356.415 436.94 7.0252 0.2006 re
+f*
+1 g
+363.44 436.94 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+367.253 436.94 20.8748 0.2006 re
+f*
+1 g
+388.128 436.94 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 436.94 21.2763 0.2006 re
+f*
+0 g
+254.851 437.141 19.0684 0.2005 re
+f*
+1 g
+273.919 437.141 5.4194 0.2005 re
+f*
+0 g
+279.338 437.141 12.4446 0.2005 re
+f*
+1 g
+291.783 437.141 8.0288 0.2005 re
+f*
+0 g
+299.812 437.141 33.9216 0.2005 re
+f*
+1 g
+333.733 437.141 22.4805 0.2005 re
+f*
+0.498 0 0.482 rg
+356.214 437.141 7.226 0.2005 re
+f*
+1 g
+363.44 437.141 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 437.141 20.8748 0.2005 re
+f*
+1 g
+388.128 437.141 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 437.141 21.2763 0.2005 re
+f*
+0 g
+254.851 437.341 19.0684 0.2005 re
+f*
+1 g
+273.919 437.341 5.4194 0.2005 re
+f*
+0 g
+279.338 437.341 12.4446 0.2005 re
+f*
+1 g
+291.783 437.341 8.0288 0.2005 re
+f*
+0 g
+299.812 437.341 33.3194 0.2005 re
+f*
+1 g
+333.131 437.341 22.882 0.2005 re
+f*
+0.498 0 0.482 rg
+356.013 437.341 7.2259 0.2005 re
+f*
+1 g
+363.239 437.341 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+367.454 437.341 20.674 0.2005 re
+f*
+1 g
+388.128 437.341 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 437.341 21.0755 0.2005 re
+f*
+0 g
+255.051 437.542 19.0683 0.2006 re
+f*
+1 g
+274.12 437.542 5.4195 0.2006 re
+f*
+0 g
+279.539 437.542 12.2438 0.2006 re
+f*
+1 g
+291.783 437.542 8.0288 0.2006 re
+f*
+0 g
+299.812 437.542 32.5165 0.2006 re
+f*
+1 g
+332.328 437.542 23.4842 0.2006 re
+f*
+0.498 0 0.482 rg
+355.812 437.542 7.4266 0.2006 re
+f*
+1 g
+363.239 437.542 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+367.454 437.542 20.674 0.2006 re
+f*
+1 g
+388.128 437.542 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 437.542 21.0755 0.2006 re
+f*
+0 g
+255.252 437.742 18.8676 0.2006 re
+f*
+1 g
+274.12 437.742 5.4195 0.2006 re
+f*
+0 g
+279.539 437.742 12.2438 0.2006 re
+f*
+1 g
+291.783 437.742 8.0288 0.2006 re
+f*
+0 g
+299.812 437.742 31.9144 0.2006 re
+f*
+1 g
+331.726 437.742 23.8856 0.2006 re
+f*
+0.498 0 0.482 rg
+355.612 437.742 7.6273 0.2006 re
+f*
+1 g
+363.239 437.742 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+367.454 437.742 20.674 0.2006 re
+f*
+1 g
+388.128 437.742 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 437.742 20.8748 0.2006 re
+f*
+0 g
+255.252 437.943 19.0683 0.2005 re
+f*
+1 g
+274.32 437.943 5.2188 0.2005 re
+f*
+0 g
+279.539 437.943 12.2438 0.2005 re
+f*
+1 g
+291.783 437.943 8.2295 0.2005 re
+f*
+0 g
+300.012 437.943 31.1115 0.2005 re
+f*
+1 g
+331.124 437.943 24.2871 0.2005 re
+f*
+0.498 0 0.482 rg
+355.411 437.943 7.828 0.2005 re
+f*
+1 g
+363.239 437.943 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+367.454 437.943 20.674 0.2005 re
+f*
+1 g
+388.128 437.943 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 437.943 20.8748 0.2005 re
+f*
+0 g
+255.453 438.143 19.0684 0.2006 re
+f*
+1 g
+274.521 438.143 5.018 0.2006 re
+f*
+0 g
+279.539 438.143 12.2438 0.2006 re
+f*
+1 g
+291.783 438.143 8.2295 0.2006 re
+f*
+0 g
+300.012 438.143 30.5094 0.2006 re
+f*
+1 g
+330.522 438.143 24.6885 0.2006 re
+f*
+0.498 0 0.482 rg
+355.21 438.143 8.0287 0.2006 re
+f*
+1 g
+363.239 438.143 4.4159 0.2006 re
+f*
+0.498 0 0.482 rg
+367.655 438.143 20.4733 0.2006 re
+f*
+1 g
+388.128 438.143 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 438.143 20.6741 0.2006 re
+f*
+0 g
+255.453 438.344 19.0684 0.2005 re
+f*
+1 g
+274.521 438.344 5.2186 0.2005 re
+f*
+0 g
+279.74 438.344 12.0432 0.2005 re
+f*
+1 g
+291.783 438.344 8.2295 0.2005 re
+f*
+0 g
+300.012 438.344 29.9072 0.2005 re
+f*
+1 g
+329.92 438.344 25.2907 0.2005 re
+f*
+0.498 0 0.482 rg
+355.21 438.344 8.0287 0.2005 re
+f*
+1 g
+363.239 438.344 4.4159 0.2005 re
+f*
+0.498 0 0.482 rg
+367.655 438.344 20.4733 0.2005 re
+f*
+1 g
+388.128 438.344 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 438.344 20.6741 0.2005 re
+f*
+0 g
+255.653 438.544 19.0683 0.2006 re
+f*
+1 g
+274.722 438.544 5.018 0.2006 re
+f*
+0 g
+279.74 438.544 12.2439 0.2006 re
+f*
+1 g
+291.984 438.544 8.0288 0.2006 re
+f*
+0 g
+300.012 438.544 29.3051 0.2006 re
+f*
+1 g
+329.317 438.544 25.692 0.2006 re
+f*
+0.498 0 0.482 rg
+355.01 438.544 8.2295 0.2006 re
+f*
+1 g
+363.239 438.544 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+367.454 438.544 20.674 0.2006 re
+f*
+1 g
+388.128 438.544 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 438.544 20.4734 0.2006 re
+f*
+0 g
+255.653 438.745 19.2691 0.2006 re
+f*
+1 g
+274.922 438.745 4.8172 0.2006 re
+f*
+0 g
+279.74 438.745 12.2439 0.2006 re
+f*
+1 g
+291.984 438.745 8.0288 0.2006 re
+f*
+0 g
+300.012 438.745 28.7029 0.2006 re
+f*
+1 g
+328.715 438.745 26.0935 0.2006 re
+f*
+0.498 0 0.482 rg
+354.809 438.745 8.4302 0.2006 re
+f*
+1 g
+363.239 438.745 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+367.454 438.745 20.674 0.2006 re
+f*
+1 g
+388.128 438.745 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 438.745 20.2727 0.2006 re
+f*
+0 g
+255.854 438.946 19.0684 0.2005 re
+f*
+1 g
+274.922 438.946 4.8172 0.2005 re
+f*
+0 g
+279.74 438.946 12.4447 0.2005 re
+f*
+1 g
+292.184 438.946 7.828 0.2005 re
+f*
+0 g
+300.012 438.946 28.1007 0.2005 re
+f*
+1 g
+328.113 438.946 26.2943 0.2005 re
+f*
+0.498 0 0.482 rg
+354.407 438.946 8.8316 0.2005 re
+f*
+1 g
+363.239 438.946 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+367.454 438.946 20.674 0.2005 re
+f*
+1 g
+388.128 438.946 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 438.946 20.2727 0.2005 re
+f*
+0 g
+256.055 439.146 19.0683 0.2006 re
+f*
+1 g
+275.123 439.146 4.8173 0.2006 re
+f*
+0 g
+279.94 439.146 12.2439 0.2006 re
+f*
+1 g
+292.184 439.146 7.828 0.2006 re
+f*
+0 g
+300.012 439.146 27.4986 0.2006 re
+f*
+1 g
+327.511 439.146 26.6957 0.2006 re
+f*
+0.498 0 0.482 rg
+354.207 439.146 9.0323 0.2006 re
+f*
+1 g
+363.239 439.146 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+367.454 439.146 20.674 0.2006 re
+f*
+1 g
+388.128 439.146 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 439.146 20.0719 0.2006 re
+f*
+0 g
+256.055 439.347 19.269 0.2005 re
+f*
+1 g
+275.324 439.347 4.6166 0.2005 re
+f*
+0 g
+279.94 439.347 12.4446 0.2005 re
+f*
+1 g
+292.385 439.347 7.6273 0.2005 re
+f*
+0 g
+300.012 439.347 26.8964 0.2005 re
+f*
+1 g
+326.909 439.347 27.0971 0.2005 re
+f*
+0.498 0 0.482 rg
+354.006 439.347 9.4339 0.2005 re
+f*
+1 g
+363.44 439.347 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+367.253 439.347 20.8748 0.2005 re
+f*
+1 g
+388.128 439.347 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 439.347 19.8712 0.2005 re
+f*
+0 g
+256.256 439.547 19.2691 0.2006 re
+f*
+1 g
+275.525 439.547 4.4158 0.2006 re
+f*
+0 g
+279.94 439.547 12.4446 0.2006 re
+f*
+1 g
+292.385 439.547 7.6273 0.2006 re
+f*
+0 g
+300.012 439.547 26.495 0.2006 re
+f*
+1 g
+326.507 439.547 27.2979 0.2006 re
+f*
+0.498 0 0.482 rg
+353.805 439.547 9.8352 0.2006 re
+f*
+1 g
+363.641 439.547 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+367.053 439.547 21.0755 0.2006 re
+f*
+1 g
+388.128 439.547 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 439.547 19.8712 0.2006 re
+f*
+0 g
+256.456 439.748 19.0684 0.2006 re
+f*
+1 g
+275.525 439.748 4.6165 0.2006 re
+f*
+0 g
+280.141 439.748 12.4446 0.2006 re
+f*
+1 g
+292.586 439.748 7.2259 0.2006 re
+f*
+0 g
+299.812 439.748 26.0935 0.2006 re
+f*
+1 g
+325.905 439.748 27.6993 0.2006 re
+f*
+0.498 0 0.482 rg
+353.605 439.748 10.2367 0.2006 re
+f*
+1 g
+363.841 439.748 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+367.053 439.748 21.0755 0.2006 re
+f*
+1 g
+388.128 439.748 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 439.748 19.6705 0.2006 re
+f*
+0 g
+256.456 439.948 19.269 0.2005 re
+f*
+1 g
+275.725 439.948 4.4159 0.2005 re
+f*
+0 g
+280.141 439.948 12.6453 0.2005 re
+f*
+1 g
+292.786 439.948 7.0252 0.2005 re
+f*
+0 g
+299.812 439.948 25.6921 0.2005 re
+f*
+1 g
+325.504 439.948 27.6993 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 439.948 10.8388 0.2005 re
+f*
+1 g
+364.042 439.948 2.6094 0.2005 re
+f*
+0.498 0 0.482 rg
+366.651 439.948 18.4661 0.2005 re
+f*
+1 g
+385.117 439.948 0.2008 0.2005 re
+f*
+0.498 0 0.482 rg
+385.318 439.948 2.81 0.2005 re
+f*
+1 g
+388.128 439.948 3.613 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 439.948 19.6705 0.2005 re
+f*
+0 g
+256.657 440.149 19.2691 0.2006 re
+f*
+1 g
+275.926 440.149 4.2151 0.2006 re
+f*
+0 g
+280.141 440.149 12.6453 0.2006 re
+f*
+1 g
+292.786 440.149 7.0252 0.2006 re
+f*
+0 g
+299.812 440.149 25.2907 0.2006 re
+f*
+1 g
+325.102 440.149 27.8999 0.2006 re
+f*
+0.498 0 0.482 rg
+353.002 440.149 11.2403 0.2006 re
+f*
+1 g
+364.243 440.149 2.208 0.2006 re
+f*
+0.498 0 0.482 rg
+366.451 440.149 18.6668 0.2006 re
+f*
+1 g
+385.117 440.149 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 440.149 19.4698 0.2006 re
+f*
+0 g
+256.858 440.35 19.2691 0.2005 re
+f*
+1 g
+276.127 440.35 4.2151 0.2005 re
+f*
+0 g
+280.342 440.35 12.6453 0.2005 re
+f*
+1 g
+292.987 440.35 6.8245 0.2005 re
+f*
+0 g
+299.812 440.35 24.6885 0.2005 re
+f*
+1 g
+324.5 440.35 28.1007 0.2005 re
+f*
+0.498 0 0.482 rg
+352.601 440.35 12.2439 0.2005 re
+f*
+1 g
+364.845 440.35 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+365.848 440.35 19.269 0.2005 re
+f*
+1 g
+385.117 440.35 6.6238 0.2005 re
+f*
+0.498 0 0.482 rg
+391.741 440.35 19.2691 0.2005 re
+f*
+0 g
+256.858 440.55 19.4698 0.2006 re
+f*
+1 g
+276.327 440.55 4.0144 0.2006 re
+f*
+0 g
+280.342 440.55 12.6453 0.2006 re
+f*
+1 g
+292.987 440.55 6.6238 0.2006 re
+f*
+0 g
+299.611 440.55 24.4878 0.2006 re
+f*
+1 g
+324.099 440.55 28.3014 0.2006 re
+f*
+0.498 0 0.482 rg
+352.4 440.55 32.7172 0.2006 re
+f*
+1 g
+385.117 440.55 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+391.741 440.55 19.2691 0.2006 re
+f*
+0 g
+257.058 440.751 19.4698 0.2005 re
+f*
+1 g
+276.528 440.751 4.0144 0.2005 re
+f*
+0 g
+280.543 440.751 12.6453 0.2005 re
+f*
+1 g
+293.188 440.751 6.423 0.2005 re
+f*
+0 g
+299.611 440.751 24.0863 0.2005 re
+f*
+1 g
+323.697 440.751 28.3014 0.2005 re
+f*
+0.498 0 0.482 rg
+351.999 440.751 58.8108 0.2005 re
+f*
+0 g
+257.259 440.951 19.4697 0.2006 re
+f*
+1 g
+276.729 440.951 3.8138 0.2006 re
+f*
+0 g
+280.543 440.951 12.6453 0.2006 re
+f*
+1 g
+293.188 440.951 6.423 0.2006 re
+f*
+0 g
+299.611 440.951 23.6849 0.2006 re
+f*
+1 g
+323.296 440.951 28.5022 0.2006 re
+f*
+0.498 0 0.482 rg
+351.798 440.951 58.8107 0.2006 re
+f*
+0 g
+257.259 441.152 19.6705 0.2006 re
+f*
+1 g
+276.93 441.152 3.8136 0.2006 re
+f*
+0 g
+280.743 441.152 12.4447 0.2006 re
+f*
+1 g
+293.188 441.152 6.2223 0.2006 re
+f*
+0 g
+299.41 441.152 23.4841 0.2006 re
+f*
+1 g
+322.894 441.152 28.5022 0.2006 re
+f*
+0.498 0 0.482 rg
+351.397 441.152 59.0115 0.2006 re
+f*
+0 g
+257.46 441.352 19.6705 0.2005 re
+f*
+1 g
+277.13 441.352 3.6129 0.2005 re
+f*
+0 g
+280.743 441.352 12.4447 0.2005 re
+f*
+1 g
+293.188 441.352 6.2223 0.2005 re
+f*
+0 g
+299.41 441.352 23.0827 0.2005 re
+f*
+1 g
+322.493 441.352 28.5021 0.2005 re
+f*
+0.498 0 0.482 rg
+350.995 441.352 59.413 0.2005 re
+f*
+0 g
+257.661 441.553 19.6705 0.2006 re
+f*
+1 g
+277.331 441.553 3.613 0.2006 re
+f*
+0 g
+280.944 441.553 12.4446 0.2006 re
+f*
+1 g
+293.389 441.553 5.8208 0.2006 re
+f*
+0 g
+299.209 441.553 23.0828 0.2006 re
+f*
+1 g
+322.292 441.553 28.5022 0.2006 re
+f*
+0.498 0 0.482 rg
+350.794 441.553 59.4129 0.2006 re
+f*
+0 g
+257.661 441.753 19.8713 0.2005 re
+f*
+1 g
+277.532 441.753 3.4122 0.2005 re
+f*
+0 g
+280.944 441.753 12.4446 0.2005 re
+f*
+1 g
+293.389 441.753 5.8208 0.2005 re
+f*
+0 g
+299.209 441.753 22.6813 0.2005 re
+f*
+1 g
+321.891 441.753 28.5022 0.2005 re
+f*
+0.498 0 0.482 rg
+350.393 441.753 59.6137 0.2005 re
+f*
+0 g
+257.861 441.954 19.8712 0.2006 re
+f*
+1 g
+277.732 441.954 3.4123 0.2006 re
+f*
+0 g
+281.145 441.954 12.2439 0.2006 re
+f*
+1 g
+293.389 441.954 5.6201 0.2006 re
+f*
+0 g
+299.009 441.954 22.4806 0.2006 re
+f*
+1 g
+321.489 441.954 28.5021 0.2006 re
+f*
+0.498 0 0.482 rg
+349.991 441.954 60.0152 0.2006 re
+f*
+0 g
+258.062 442.155 19.8712 0.2006 re
+f*
+1 g
+277.933 442.155 3.4122 0.2006 re
+f*
+0 g
+281.346 442.155 12.0432 0.2006 re
+f*
+1 g
+293.389 442.155 5.6201 0.2006 re
+f*
+0 g
+299.009 442.155 22.2799 0.2006 re
+f*
+1 g
+321.289 442.155 28.1007 0.2006 re
+f*
+0.498 0 0.482 rg
+349.389 442.155 60.4166 0.2006 re
+f*
+0 g
+258.263 442.355 19.8712 0.2005 re
+f*
+1 g
+278.134 442.355 3.4123 0.2005 re
+f*
+0 g
+281.546 442.355 11.8424 0.2005 re
+f*
+1 g
+293.389 442.355 5.4194 0.2005 re
+f*
+0 g
+298.808 442.355 22.0791 0.2005 re
+f*
+1 g
+320.887 442.355 28.1007 0.2005 re
+f*
+0.498 0 0.482 rg
+348.988 442.355 60.6173 0.2005 re
+f*
+0 g
+258.263 442.556 20.0719 0.2006 re
+f*
+1 g
+278.335 442.556 3.2116 0.2006 re
+f*
+0 g
+281.546 442.556 11.8424 0.2006 re
+f*
+1 g
+293.389 442.556 5.2187 0.2006 re
+f*
+0 g
+298.607 442.556 22.0792 0.2006 re
+f*
+1 g
+320.687 442.556 27.6992 0.2006 re
+f*
+0.498 0 0.482 rg
+348.386 442.556 61.0187 0.2006 re
+f*
+0 g
+258.463 442.756 20.072 0.2005 re
+f*
+1 g
+278.535 442.756 3.2114 0.2005 re
+f*
+0 g
+281.747 442.756 11.6418 0.2005 re
+f*
+1 g
+293.389 442.756 5.2187 0.2005 re
+f*
+0 g
+298.607 442.756 21.6777 0.2005 re
+f*
+1 g
+320.285 442.756 27.6992 0.2005 re
+f*
+0.498 0 0.482 rg
+347.984 442.756 61.4202 0.2005 re
+f*
+0 g
+258.664 442.957 20.0719 0.2006 re
+f*
+1 g
+278.736 442.957 3.2116 0.2006 re
+f*
+0 g
+281.948 442.957 11.441 0.2006 re
+f*
+1 g
+293.389 442.957 5.018 0.2006 re
+f*
+0 g
+298.407 442.957 21.6777 0.2006 re
+f*
+1 g
+320.084 442.957 27.2978 0.2006 re
+f*
+0.498 0 0.482 rg
+347.382 442.957 61.8216 0.2006 re
+f*
+0 g
+258.865 443.157 20.2727 0.2006 re
+f*
+1 g
+279.138 443.157 3.0108 0.2006 re
+f*
+0 g
+282.148 443.157 11.2403 0.2006 re
+f*
+1 g
+293.389 443.157 4.8173 0.2006 re
+f*
+0 g
+298.206 443.157 21.6776 0.2006 re
+f*
+1 g
+319.884 443.157 26.8965 0.2006 re
+f*
+0.498 0 0.482 rg
+346.78 443.157 62.223 0.2006 re
+f*
+0 g
+258.865 443.358 20.4734 0.2005 re
+f*
+1 g
+279.338 443.358 3.0108 0.2005 re
+f*
+0 g
+282.349 443.358 11.0396 0.2005 re
+f*
+1 g
+293.389 443.358 4.6165 0.2005 re
+f*
+0 g
+298.005 443.358 21.477 0.2005 re
+f*
+1 g
+319.482 443.358 26.6957 0.2005 re
+f*
+0.498 0 0.482 rg
+346.178 443.358 62.6245 0.2005 re
+f*
+0 g
+259.066 443.558 20.4734 0.2006 re
+f*
+1 g
+279.539 443.558 3.0108 0.2006 re
+f*
+0 g
+282.55 443.558 10.8388 0.2006 re
+f*
+1 g
+293.389 443.558 4.4158 0.2006 re
+f*
+0 g
+297.805 443.558 21.477 0.2006 re
+f*
+1 g
+319.281 443.558 26.2942 0.2006 re
+f*
+0.498 0 0.482 rg
+345.576 443.558 63.2267 0.2006 re
+f*
+0 g
+259.266 443.759 20.4733 0.2005 re
+f*
+1 g
+279.74 443.759 3.0108 0.2005 re
+f*
+0 g
+282.75 443.759 10.4375 0.2005 re
+f*
+1 g
+293.188 443.759 4.4158 0.2005 re
+f*
+0 g
+297.604 443.759 21.4769 0.2005 re
+f*
+1 g
+319.081 443.759 25.8928 0.2005 re
+f*
+0.498 0 0.482 rg
+344.973 443.759 63.6281 0.2005 re
+f*
+0 g
+259.467 443.959 20.6741 0.2006 re
+f*
+1 g
+280.141 443.959 2.8101 0.2006 re
+f*
+0 g
+282.951 443.959 10.2367 0.2006 re
+f*
+1 g
+293.188 443.959 4.2151 0.2006 re
+f*
+0 g
+297.403 443.959 21.4769 0.2006 re
+f*
+1 g
+318.88 443.959 25.6921 0.2006 re
+f*
+0.498 0 0.482 rg
+344.572 443.959 63.8288 0.2006 re
+f*
+0 g
+259.668 444.16 20.6741 0.2006 re
+f*
+1 g
+280.342 444.16 2.8101 0.2006 re
+f*
+0 g
+283.152 444.16 9.8352 0.2006 re
+f*
+1 g
+292.987 444.16 4.2152 0.2006 re
+f*
+0 g
+297.202 444.16 21.477 0.2006 re
+f*
+1 g
+318.679 444.16 25.2905 0.2006 re
+f*
+0.498 0 0.482 rg
+343.97 444.16 64.2303 0.2006 re
+f*
+0 g
+259.868 444.361 20.8748 0.2005 re
+f*
+1 g
+280.743 444.361 2.6094 0.2005 re
+f*
+0 g
+283.353 444.361 9.6345 0.2005 re
+f*
+1 g
+292.987 444.361 4.0144 0.2005 re
+f*
+0 g
+297.002 444.361 21.477 0.2005 re
+f*
+1 g
+318.479 444.361 24.8892 0.2005 re
+f*
+0.498 0 0.482 rg
+343.368 444.361 64.6317 0.2005 re
+f*
+0 g
+259.868 444.561 21.0756 0.2005 re
+f*
+1 g
+280.944 444.561 2.81 0.2005 re
+f*
+0 g
+283.754 444.561 9.0324 0.2005 re
+f*
+1 g
+292.786 444.561 4.0144 0.2005 re
+f*
+0 g
+296.801 444.561 21.477 0.2005 re
+f*
+1 g
+318.278 444.561 24.287 0.2005 re
+f*
+0.498 0 0.482 rg
+342.565 444.561 65.2339 0.2005 re
+f*
+0 g
+260.069 444.762 21.2762 0.2006 re
+f*
+1 g
+281.346 444.762 2.6094 0.2006 re
+f*
+0 g
+283.955 444.762 8.6309 0.2006 re
+f*
+1 g
+292.586 444.762 4.0144 0.2006 re
+f*
+0 g
+296.6 444.762 21.4769 0.2006 re
+f*
+1 g
+318.077 444.762 23.8857 0.2006 re
+f*
+0.498 0 0.482 rg
+341.963 444.762 65.836 0.2006 re
+f*
+0 g
+260.27 444.962 21.2763 0.2006 re
+f*
+1 g
+281.546 444.962 2.81 0.2006 re
+f*
+0 g
+284.356 444.962 8.0288 0.2006 re
+f*
+1 g
+292.385 444.962 3.8137 0.2006 re
+f*
+0 g
+296.199 444.962 21.6776 0.2006 re
+f*
+1 g
+317.876 444.962 23.4842 0.2006 re
+f*
+0.498 0 0.482 rg
+341.361 444.962 66.2374 0.2006 re
+f*
+0 g
+260.471 445.163 21.477 0.2006 re
+f*
+1 g
+281.948 445.163 2.6094 0.2006 re
+f*
+0 g
+284.557 445.163 7.6273 0.2006 re
+f*
+1 g
+292.184 445.163 3.8136 0.2006 re
+f*
+0 g
+295.998 445.163 21.6778 0.2006 re
+f*
+1 g
+317.676 445.163 23.0827 0.2006 re
+f*
+0.498 0 0.482 rg
+340.758 445.163 66.6388 0.2006 re
+f*
+0 g
+260.671 445.363 21.6777 0.2005 re
+f*
+1 g
+282.349 445.363 2.8101 0.2005 re
+f*
+0 g
+285.159 445.363 6.6237 0.2005 re
+f*
+1 g
+291.783 445.363 3.8137 0.2005 re
+f*
+0 g
+295.597 445.363 21.8784 0.2005 re
+f*
+1 g
+317.475 445.363 22.6813 0.2005 re
+f*
+0.498 0 0.482 rg
+340.156 445.363 67.0403 0.2005 re
+f*
+0 g
+260.872 445.564 21.8784 0.2006 re
+f*
+1 g
+282.75 445.564 2.8101 0.2006 re
+f*
+0 g
+285.561 445.564 5.6202 0.2006 re
+f*
+1 g
+291.181 445.564 4.0144 0.2006 re
+f*
+0 g
+295.195 445.564 22.0791 0.2006 re
+f*
+1 g
+317.274 445.564 22.2799 0.2006 re
+f*
+0.498 0 0.482 rg
+339.554 445.564 67.4417 0.2006 re
+f*
+0 g
+261.073 445.765 22.0792 0.2005 re
+f*
+1 g
+283.152 445.765 3.0108 0.2005 re
+f*
+0 g
+286.163 445.765 4.2151 0.2005 re
+f*
+1 g
+290.378 445.765 4.4158 0.2005 re
+f*
+0 g
+294.794 445.765 22.2799 0.2005 re
+f*
+1 g
+317.074 445.765 21.8784 0.2005 re
+f*
+0.498 0 0.482 rg
+338.952 445.765 67.8432 0.2005 re
+f*
+0 g
+261.073 445.965 22.4807 0.2006 re
+f*
+1 g
+283.553 445.965 3.6129 0.2006 re
+f*
+0 g
+287.166 445.965 2.2079 0.2006 re
+f*
+1 g
+289.374 445.965 5.018 0.2006 re
+f*
+0 g
+294.392 445.965 22.4805 0.2006 re
+f*
+1 g
+316.873 445.965 21.477 0.2006 re
+f*
+0.498 0 0.482 rg
+338.35 445.965 68.2446 0.2006 re
+f*
+0 g
+261.274 446.166 22.882 0.2006 re
+f*
+1 g
+284.156 446.166 9.8352 0.2006 re
+f*
+0 g
+293.991 446.166 22.6813 0.2006 re
+f*
+1 g
+316.672 446.166 21.0756 0.2006 re
+f*
+0.498 0 0.482 rg
+337.748 446.166 68.646 0.2006 re
+f*
+0 g
+261.474 446.366 23.2834 0.2005 re
+f*
+1 g
+284.758 446.366 8.631 0.2005 re
+f*
+0 g
+293.389 446.366 23.2834 0.2005 re
+f*
+1 g
+316.672 446.366 20.4734 0.2005 re
+f*
+0.498 0 0.482 rg
+337.146 446.366 69.0475 0.2005 re
+f*
+0 g
+261.675 446.567 23.6849 0.2005 re
+f*
+1 g
+285.36 446.567 7.4266 0.2005 re
+f*
+0 g
+292.786 446.567 23.6849 0.2005 re
+f*
+1 g
+316.471 446.567 19.8713 0.2005 re
+f*
+0.498 0 0.482 rg
+336.343 446.567 69.8503 0.2005 re
+f*
+0 g
+261.876 446.767 24.2871 0.2006 re
+f*
+1 g
+286.163 446.767 5.6201 0.2006 re
+f*
+0 g
+291.783 446.767 24.4878 0.2006 re
+f*
+1 g
+316.271 446.767 19.4698 0.2006 re
+f*
+0.498 0 0.482 rg
+335.74 446.767 70.2518 0.2006 re
+f*
+0 g
+262.076 446.968 25.2907 0.2006 re
+f*
+1 g
+287.367 446.968 3.2115 0.2006 re
+f*
+0 g
+290.579 446.968 25.6921 0.2006 re
+f*
+1 g
+316.271 446.968 18.8676 0.2006 re
+f*
+0.498 0 0.482 rg
+335.138 446.968 70.6533 0.2006 re
+f*
+0 g
+262.277 447.168 53.7928 0.2005 re
+f*
+1 g
+316.07 447.168 18.4662 0.2005 re
+f*
+0.498 0 0.482 rg
+334.536 447.168 71.0547 0.2005 re
+f*
+0 g
+262.478 447.369 53.3913 0.2006 re
+f*
+1 g
+315.869 447.369 18.0648 0.2006 re
+f*
+0.498 0 0.482 rg
+333.934 447.369 71.4561 0.2006 re
+f*
+0 g
+262.679 447.57 53.1906 0.2005 re
+f*
+1 g
+315.869 447.57 17.4626 0.2005 re
+f*
+0.498 0 0.482 rg
+333.332 447.57 71.8576 0.2005 re
+f*
+0 g
+262.879 447.77 52.7893 0.2006 re
+f*
+1 g
+315.669 447.77 17.0611 0.2006 re
+f*
+0.498 0 0.482 rg
+332.73 447.77 72.259 0.2006 re
+f*
+0 g
+263.08 447.971 52.5886 0.2006 re
+f*
+1 g
+315.669 447.971 16.4589 0.2006 re
+f*
+0.498 0 0.482 rg
+332.127 447.971 72.6605 0.2006 re
+f*
+0 g
+263.281 448.171 52.187 0.2006 re
+f*
+1 g
+315.468 448.171 16.0576 0.2006 re
+f*
+0.498 0 0.482 rg
+331.525 448.171 73.0618 0.2006 re
+f*
+0 g
+263.481 448.372 51.9863 0.2005 re
+f*
+1 g
+315.468 448.372 15.4554 0.2005 re
+f*
+0.498 0 0.482 rg
+330.923 448.372 73.4633 0.2005 re
+f*
+0 g
+263.682 448.572 51.5849 0.2005 re
+f*
+1 g
+315.267 448.572 15.0539 0.2005 re
+f*
+0.498 0 0.482 rg
+330.321 448.572 73.8648 0.2005 re
+f*
+0 g
+263.883 448.773 51.3842 0.2006 re
+f*
+1 g
+315.267 448.773 14.4518 0.2006 re
+f*
+0.498 0 0.482 rg
+329.719 448.773 74.2662 0.2006 re
+f*
+0 g
+264.084 448.973 50.9828 0.2006 re
+f*
+1 g
+315.066 448.973 14.2511 0.2006 re
+f*
+0.498 0 0.482 rg
+329.317 448.973 74.4669 0.2006 re
+f*
+0 g
+264.284 449.174 50.782 0.2005 re
+f*
+1 g
+315.066 449.174 13.6489 0.2005 re
+f*
+0.498 0 0.482 rg
+328.715 449.174 74.8683 0.2005 re
+f*
+0 g
+264.485 449.375 50.5813 0.2006 re
+f*
+1 g
+315.066 449.375 13.2475 0.2006 re
+f*
+0.498 0 0.482 rg
+328.314 449.375 75.069 0.2006 re
+f*
+0 g
+264.686 449.575 50.1798 0.2005 re
+f*
+1 g
+314.866 449.575 12.8461 0.2005 re
+f*
+0.498 0 0.482 rg
+327.712 449.575 75.2698 0.2005 re
+f*
+0 g
+264.886 449.776 49.9791 0.2006 re
+f*
+1 g
+314.866 449.776 12.4447 0.2006 re
+f*
+0.498 0 0.482 rg
+327.31 449.776 75.4705 0.2006 re
+f*
+0 g
+265.288 449.976 49.5776 0.2006 re
+f*
+1 g
+314.866 449.976 12.0432 0.2006 re
+f*
+0.498 0 0.482 rg
+326.909 449.976 75.6712 0.2006 re
+f*
+0 g
+265.489 450.177 49.1763 0.2005 re
+f*
+1 g
+314.665 450.177 11.8424 0.2005 re
+f*
+0.498 0 0.482 rg
+326.507 450.177 75.8719 0.2005 re
+f*
+0 g
+265.689 450.377 48.9756 0.2005 re
+f*
+1 g
+314.665 450.377 11.4409 0.2005 re
+f*
+0.498 0 0.482 rg
+326.106 450.377 76.0727 0.2005 re
+f*
+0 g
+265.89 450.578 48.7749 0.2006 re
+f*
+1 g
+314.665 450.578 11.0395 0.2006 re
+f*
+0.498 0 0.482 rg
+325.704 450.578 76.2734 0.2006 re
+f*
+0 g
+266.091 450.778 48.3733 0.2006 re
+f*
+1 g
+314.464 450.778 10.8389 0.2006 re
+f*
+0.498 0 0.482 rg
+325.303 450.778 76.4741 0.2006 re
+f*
+0 g
+266.292 450.979 48.1726 0.2006 re
+f*
+1 g
+314.464 450.979 10.4374 0.2006 re
+f*
+0.498 0 0.482 rg
+324.902 450.979 76.6748 0.2006 re
+f*
+0 g
+266.492 451.18 47.9719 0.2005 re
+f*
+1 g
+314.464 451.18 10.2367 0.2005 re
+f*
+0.498 0 0.482 rg
+324.701 451.18 76.6748 0.2005 re
+f*
+0 g
+266.693 451.38 47.7712 0.2005 re
+f*
+1 g
+314.464 451.38 9.8353 0.2005 re
+f*
+0.498 0 0.482 rg
+324.299 451.38 76.6748 0.2005 re
+f*
+0 g
+267.094 451.581 47.169 0.2006 re
+f*
+1 g
+314.263 451.581 9.8353 0.2006 re
+f*
+0.498 0 0.482 rg
+324.099 451.581 76.6748 0.2006 re
+f*
+0 g
+267.295 451.781 46.9683 0.2006 re
+f*
+1 g
+314.263 451.781 9.4338 0.2006 re
+f*
+0.498 0 0.482 rg
+323.697 451.781 76.8755 0.2006 re
+f*
+0 g
+267.496 451.982 46.7676 0.2006 re
+f*
+1 g
+314.263 451.982 9.2331 0.2006 re
+f*
+0.498 0 0.482 rg
+323.497 451.982 76.8755 0.2006 re
+f*
+0 g
+267.697 452.183 46.5669 0.2005 re
+f*
+1 g
+314.263 452.183 9.0324 0.2005 re
+f*
+0.498 0 0.482 rg
+323.296 452.183 76.6748 0.2005 re
+f*
+0 g
+268.098 452.383 46.1654 0.2005 re
+f*
+1 g
+314.263 452.383 8.6309 0.2005 re
+f*
+0.498 0 0.482 rg
+322.894 452.383 76.8756 0.2005 re
+f*
+0 g
+268.299 452.583 45.764 0.2006 re
+f*
+1 g
+314.063 452.583 8.631 0.2006 re
+f*
+0.498 0 0.482 rg
+322.694 452.583 76.8754 0.2006 re
+f*
+0 g
+268.499 452.784 45.5633 0.2006 re
+f*
+1 g
+314.063 452.784 8.4302 0.2006 re
+f*
+0.498 0 0.482 rg
+322.493 452.784 76.8755 0.2006 re
+f*
+0 g
+268.7 452.985 45.3626 0.2005 re
+f*
+1 g
+314.063 452.985 8.2295 0.2005 re
+f*
+0.498 0 0.482 rg
+322.292 452.985 76.6748 0.2005 re
+f*
+0 g
+269.102 453.185 44.9612 0.2006 re
+f*
+1 g
+314.063 453.185 8.0288 0.2006 re
+f*
+0.498 0 0.482 rg
+322.092 453.185 76.6748 0.2006 re
+f*
+0 g
+269.302 453.386 44.7604 0.2005 re
+f*
+1 g
+314.063 453.386 7.828 0.2005 re
+f*
+0.498 0 0.482 rg
+321.891 453.386 76.6748 0.2005 re
+f*
+0 g
+269.503 453.586 44.5597 0.2006 re
+f*
+1 g
+314.063 453.586 7.6274 0.2006 re
+f*
+0.498 0 0.482 rg
+321.69 453.586 76.6747 0.2006 re
+f*
+0 g
+269.904 453.787 44.1583 0.2006 re
+f*
+1 g
+314.063 453.787 7.4266 0.2006 re
+f*
+0.498 0 0.482 rg
+321.489 453.787 76.4741 0.2006 re
+f*
+0 g
+270.105 453.987 43.9576 0.2005 re
+f*
+1 g
+314.063 453.987 7.2259 0.2005 re
+f*
+0.498 0 0.482 rg
+321.289 453.987 76.4741 0.2005 re
+f*
+0 g
+270.306 454.188 43.7568 0.2006 re
+f*
+1 g
+314.063 454.188 7.2259 0.2006 re
+f*
+0.498 0 0.482 rg
+321.289 454.188 76.2734 0.2006 re
+f*
+0 g
+270.707 454.389 43.3554 0.2005 re
+f*
+1 g
+314.063 454.389 7.0252 0.2005 re
+f*
+0.498 0 0.482 rg
+321.088 454.389 76.0726 0.2005 re
+f*
+0 g
+270.908 454.589 43.1547 0.2006 re
+f*
+1 g
+314.063 454.589 6.8244 0.2006 re
+f*
+0.498 0 0.482 rg
+320.887 454.589 76.0727 0.2006 re
+f*
+0 g
+271.109 454.79 42.954 0.2006 re
+f*
+1 g
+314.063 454.79 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+320.687 454.79 75.8719 0.2006 re
+f*
+0 g
+271.51 454.99 42.3517 0.2005 re
+f*
+1 g
+313.862 454.99 6.8246 0.2005 re
+f*
+0.498 0 0.482 rg
+320.687 454.99 75.6711 0.2005 re
+f*
+0 g
+271.711 455.191 42.151 0.2006 re
+f*
+1 g
+313.862 455.191 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+320.486 455.191 75.6712 0.2006 re
+f*
+0 g
+272.112 455.391 41.7496 0.2005 re
+f*
+1 g
+313.862 455.391 6.4231 0.2005 re
+f*
+0.498 0 0.482 rg
+320.285 455.391 75.4705 0.2005 re
+f*
+0 g
+272.313 455.592 41.5488 0.2006 re
+f*
+1 g
+313.862 455.592 6.4231 0.2006 re
+f*
+0.498 0 0.482 rg
+320.285 455.592 75.2698 0.2006 re
+f*
+0 g
+272.715 455.792 41.1474 0.2005 re
+f*
+1 g
+313.862 455.792 6.2224 0.2005 re
+f*
+0.498 0 0.482 rg
+320.084 455.792 75.069 0.2005 re
+f*
+0 g
+272.915 455.993 41.1475 0.2006 re
+f*
+1 g
+314.063 455.993 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+320.084 455.993 74.8683 0.2006 re
+f*
+0 g
+273.116 456.194 40.9468 0.2006 re
+f*
+1 g
+314.063 456.194 5.8208 0.2006 re
+f*
+0.498 0 0.482 rg
+319.884 456.194 74.6677 0.2006 re
+f*
+0 g
+273.517 456.394 40.5453 0.2005 re
+f*
+1 g
+314.063 456.394 5.8208 0.2005 re
+f*
+0.498 0 0.482 rg
+319.884 456.394 74.4669 0.2005 re
+f*
+0 g
+273.919 456.595 40.1439 0.2006 re
+f*
+1 g
+314.063 456.595 5.6202 0.2006 re
+f*
+0.498 0 0.482 rg
+319.683 456.595 74.2661 0.2006 re
+f*
+0 g
+274.12 456.795 39.9432 0.2005 re
+f*
+1 g
+314.063 456.795 5.6202 0.2005 re
+f*
+0.498 0 0.482 rg
+319.683 456.795 32.5165 0.2005 re
+f*
+1 g
+352.2 456.795 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 456.795 40.5453 0.2005 re
+f*
+0 g
+274.521 456.996 39.5417 0.2006 re
+f*
+1 g
+314.063 456.996 5.4194 0.2006 re
+f*
+0.498 0 0.482 rg
+319.482 456.996 32.7173 0.2006 re
+f*
+1 g
+352.2 456.996 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 456.996 40.1439 0.2006 re
+f*
+0 g
+274.722 457.196 39.3411 0.2006 re
+f*
+1 g
+314.063 457.196 5.4194 0.2006 re
+f*
+0.498 0 0.482 rg
+319.482 457.196 32.7173 0.2006 re
+f*
+1 g
+352.2 457.196 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 457.196 39.9431 0.2006 re
+f*
+0 g
+275.123 457.397 38.9396 0.2005 re
+f*
+1 g
+314.063 457.397 5.4194 0.2005 re
+f*
+0.498 0 0.482 rg
+319.482 457.397 32.7173 0.2005 re
+f*
+1 g
+352.2 457.397 1.0036 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 457.397 39.5417 0.2005 re
+f*
+0 g
+275.324 457.597 38.7389 0.2006 re
+f*
+1 g
+314.063 457.597 5.2187 0.2006 re
+f*
+0.498 0 0.482 rg
+319.281 457.597 32.918 0.2006 re
+f*
+1 g
+352.2 457.597 1.0036 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 457.597 39.1403 0.2006 re
+f*
+0 g
+275.725 457.798 38.3375 0.2005 re
+f*
+1 g
+314.063 457.798 5.2187 0.2005 re
+f*
+0.498 0 0.482 rg
+319.281 457.798 32.7172 0.2005 re
+f*
+1 g
+351.999 457.798 1.2044 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 457.798 38.9395 0.2005 re
+f*
+0 g
+276.127 457.999 37.936 0.2006 re
+f*
+1 g
+314.063 457.999 5.2187 0.2006 re
+f*
+0.498 0 0.482 rg
+319.281 457.999 32.7172 0.2006 re
+f*
+1 g
+351.999 457.999 1.2044 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 457.999 38.5381 0.2006 re
+f*
+0 g
+276.327 458.199 37.7353 0.2006 re
+f*
+1 g
+314.063 458.199 5.0179 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 458.199 32.918 0.2006 re
+f*
+1 g
+351.999 458.199 1.2044 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 458.199 38.1367 0.2006 re
+f*
+0 g
+276.729 458.4 37.3339 0.2005 re
+f*
+1 g
+314.063 458.4 5.0179 0.2005 re
+f*
+0.498 0 0.482 rg
+319.081 458.4 32.918 0.2005 re
+f*
+1 g
+351.999 458.4 1.2044 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 458.4 37.9359 0.2005 re
+f*
+0 g
+277.13 458.6 37.1331 0.2006 re
+f*
+1 g
+314.263 458.6 4.8172 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 458.6 32.7174 0.2006 re
+f*
+1 g
+351.798 458.6 1.405 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 458.6 37.5345 0.2006 re
+f*
+0 g
+277.532 458.801 36.7316 0.2005 re
+f*
+1 g
+314.263 458.801 4.8172 0.2005 re
+f*
+0.498 0 0.482 rg
+319.081 458.801 32.7174 0.2005 re
+f*
+1 g
+351.798 458.801 1.405 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 458.801 37.1331 0.2005 re
+f*
+0 g
+277.732 459.001 36.531 0.2006 re
+f*
+1 g
+314.263 459.001 4.8172 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 459.001 32.7174 0.2006 re
+f*
+1 g
+351.798 459.001 1.405 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 459.001 36.9323 0.2006 re
+f*
+0 g
+278.134 459.202 36.1295 0.2006 re
+f*
+1 g
+314.263 459.202 4.8172 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 459.202 32.7174 0.2006 re
+f*
+1 g
+351.798 459.202 1.405 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 459.202 36.5309 0.2006 re
+f*
+0 g
+278.535 459.403 35.728 0.2005 re
+f*
+1 g
+314.263 459.403 4.6165 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 459.403 32.7173 0.2005 re
+f*
+1 g
+351.597 459.403 1.6058 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 459.403 36.1295 0.2005 re
+f*
+0 g
+278.736 459.603 35.5274 0.2005 re
+f*
+1 g
+314.263 459.603 4.6165 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 459.603 32.7173 0.2005 re
+f*
+1 g
+351.597 459.603 1.6058 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 459.603 35.7281 0.2005 re
+f*
+0 g
+279.138 459.804 35.3266 0.2006 re
+f*
+1 g
+314.464 459.804 4.4158 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 459.804 32.7173 0.2006 re
+f*
+1 g
+351.597 459.804 1.6058 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 459.804 35.3266 0.2006 re
+f*
+0 g
+279.539 460.004 34.9251 0.2006 re
+f*
+1 g
+314.464 460.004 4.4158 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 460.004 32.7173 0.2006 re
+f*
+1 g
+351.597 460.004 1.6058 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 460.004 34.9251 0.2006 re
+f*
+0 g
+279.94 460.205 34.5237 0.2006 re
+f*
+1 g
+314.464 460.205 4.4158 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 460.205 32.5166 0.2006 re
+f*
+1 g
+351.397 460.205 1.8065 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 460.205 34.7245 0.2006 re
+f*
+0 g
+280.342 460.405 34.1223 0.2005 re
+f*
+1 g
+314.464 460.405 4.4158 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 460.405 32.5166 0.2005 re
+f*
+1 g
+351.397 460.405 1.8065 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 460.405 34.323 0.2005 re
+f*
+0 g
+280.743 460.606 33.9217 0.2006 re
+f*
+1 g
+314.665 460.606 4.215 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 460.606 32.5166 0.2006 re
+f*
+1 g
+351.397 460.606 1.8065 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 460.606 33.9215 0.2006 re
+f*
+0 g
+281.145 460.806 33.5202 0.2005 re
+f*
+1 g
+314.665 460.806 4.215 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 460.806 32.3159 0.2005 re
+f*
+1 g
+351.196 460.806 2.0072 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 460.806 33.5201 0.2005 re
+f*
+0 g
+281.546 461.007 33.1187 0.2006 re
+f*
+1 g
+314.665 461.007 4.215 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 461.007 32.3159 0.2006 re
+f*
+1 g
+351.196 461.007 2.0072 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 461.007 33.1186 0.2006 re
+f*
+0 g
+281.948 461.208 32.7173 0.2006 re
+f*
+1 g
+314.665 461.208 4.215 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 461.208 32.3159 0.2006 re
+f*
+1 g
+351.196 461.208 2.0072 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 461.208 32.7172 0.2006 re
+f*
+0 g
+282.349 461.408 32.3159 0.2005 re
+f*
+1 g
+314.665 461.408 4.215 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 461.408 32.1151 0.2005 re
+f*
+1 g
+350.995 461.408 2.208 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 461.408 32.3158 0.2005 re
+f*
+0 g
+282.75 461.609 32.1151 0.2005 re
+f*
+1 g
+314.866 461.609 4.0144 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 461.609 32.1151 0.2005 re
+f*
+1 g
+350.995 461.609 2.208 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 461.609 31.9143 0.2005 re
+f*
+0 g
+283.152 461.809 31.7136 0.2006 re
+f*
+1 g
+314.866 461.809 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 461.809 32.1151 0.2006 re
+f*
+1 g
+350.995 461.809 2.208 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 461.809 31.5129 0.2006 re
+f*
+0 g
+283.553 462.01 31.3121 0.2006 re
+f*
+1 g
+314.866 462.01 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 462.01 31.9145 0.2006 re
+f*
+1 g
+350.794 462.01 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 462.01 31.1115 0.2006 re
+f*
+0 g
+283.955 462.21 31.1115 0.2005 re
+f*
+1 g
+315.066 462.21 3.8136 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 462.21 31.9145 0.2005 re
+f*
+1 g
+350.794 462.21 2.4086 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 462.21 30.7101 0.2005 re
+f*
+0 g
+284.356 462.411 30.7101 0.2006 re
+f*
+1 g
+315.066 462.411 3.8136 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 462.411 31.9145 0.2006 re
+f*
+1 g
+350.794 462.411 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 462.411 30.1079 0.2006 re
+f*
+0 g
+284.758 462.611 30.5094 0.2005 re
+f*
+1 g
+315.267 462.611 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+318.88 462.611 31.7137 0.2005 re
+f*
+1 g
+350.594 462.611 2.6094 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 462.611 29.7065 0.2005 re
+f*
+0 g
+285.36 462.812 29.9072 0.2006 re
+f*
+1 g
+315.267 462.812 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 462.812 31.7137 0.2006 re
+f*
+1 g
+350.594 462.812 2.6094 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 462.812 29.305 0.2006 re
+f*
+0 g
+285.761 463.013 29.5058 0.2006 re
+f*
+1 g
+315.267 463.013 3.6129 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 463.013 31.7137 0.2006 re
+f*
+1 g
+350.594 463.013 2.6094 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 463.013 28.9035 0.2006 re
+f*
+0 g
+286.163 463.213 29.305 0.2006 re
+f*
+1 g
+315.468 463.213 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+318.88 463.213 31.513 0.2006 re
+f*
+1 g
+350.393 463.213 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 463.213 28.5021 0.2006 re
+f*
+0 g
+286.564 463.414 28.9036 0.2005 re
+f*
+1 g
+315.468 463.414 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+319.081 463.414 31.3123 0.2005 re
+f*
+1 g
+350.393 463.414 2.8101 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 463.414 27.9 0.2005 re
+f*
+0 g
+287.166 463.614 28.3014 0.2005 re
+f*
+1 g
+315.468 463.614 3.6129 0.2005 re
+f*
+0.498 0 0.482 rg
+319.081 463.614 31.1116 0.2005 re
+f*
+1 g
+350.192 463.614 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 463.614 27.4985 0.2005 re
+f*
+0 g
+287.568 463.815 28.1008 0.2006 re
+f*
+1 g
+315.669 463.815 3.4121 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 463.815 31.1116 0.2006 re
+f*
+1 g
+350.192 463.815 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 463.815 27.0971 0.2006 re
+f*
+0 g
+287.969 464.015 27.6994 0.2006 re
+f*
+1 g
+315.669 464.015 3.4121 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 464.015 30.9108 0.2006 re
+f*
+1 g
+349.991 464.015 3.2116 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 464.015 26.495 0.2006 re
+f*
+0 g
+288.571 464.216 27.2978 0.2005 re
+f*
+1 g
+315.869 464.216 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+319.081 464.216 30.9108 0.2005 re
+f*
+1 g
+349.991 464.216 3.2116 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 464.216 26.0935 0.2005 re
+f*
+0 g
+288.973 464.416 26.8964 0.2006 re
+f*
+1 g
+315.869 464.416 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+319.081 464.416 30.7102 0.2006 re
+f*
+1 g
+349.791 464.416 3.4122 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 464.416 25.4914 0.2006 re
+f*
+0 g
+289.575 464.617 26.495 0.2005 re
+f*
+1 g
+316.07 464.617 3.2115 0.2005 re
+f*
+0.498 0 0.482 rg
+319.281 464.617 30.5094 0.2005 re
+f*
+1 g
+349.791 464.617 3.4122 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 464.617 25.0899 0.2005 re
+f*
+0 g
+289.976 464.818 26.0936 0.2006 re
+f*
+1 g
+316.07 464.818 3.2115 0.2006 re
+f*
+0.498 0 0.482 rg
+319.281 464.818 30.3086 0.2006 re
+f*
+1 g
+349.59 464.818 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 464.818 24.4878 0.2006 re
+f*
+0 g
+290.579 465.018 25.6921 0.2006 re
+f*
+1 g
+316.271 465.018 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+319.281 465.018 30.3086 0.2006 re
+f*
+1 g
+349.59 465.018 3.613 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 465.018 24.0863 0.2006 re
+f*
+0 g
+291.181 465.219 25.0899 0.2005 re
+f*
+1 g
+316.271 465.219 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+319.281 465.219 30.1079 0.2005 re
+f*
+1 g
+349.389 465.219 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 465.219 23.4842 0.2005 re
+f*
+0 g
+291.582 465.419 24.8892 0.2005 re
+f*
+1 g
+316.471 465.419 3.0108 0.2005 re
+f*
+0.498 0 0.482 rg
+319.482 465.419 29.9072 0.2005 re
+f*
+1 g
+349.389 465.419 3.8137 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 465.419 22.882 0.2005 re
+f*
+0 g
+292.184 465.62 24.287 0.2006 re
+f*
+1 g
+316.471 465.62 3.0108 0.2006 re
+f*
+0.498 0 0.482 rg
+319.482 465.62 29.7065 0.2006 re
+f*
+1 g
+349.189 465.62 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 465.62 22.4806 0.2006 re
+f*
+0 g
+292.786 465.82 23.8856 0.2006 re
+f*
+1 g
+316.672 465.82 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+319.482 465.82 29.7065 0.2006 re
+f*
+1 g
+349.189 465.82 4.0144 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 465.82 21.8784 0.2006 re
+f*
+0 g
+293.389 466.021 23.2834 0.2006 re
+f*
+1 g
+316.672 466.021 3.0109 0.2006 re
+f*
+0.498 0 0.482 rg
+319.683 466.021 29.3049 0.2006 re
+f*
+1 g
+348.988 466.021 4.2152 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 466.021 21.2762 0.2006 re
+f*
+0 g
+293.79 466.222 23.0827 0.2005 re
+f*
+1 g
+316.873 466.222 2.8102 0.2005 re
+f*
+0.498 0 0.482 rg
+319.683 466.222 29.3049 0.2005 re
+f*
+1 g
+348.988 466.222 4.2152 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 466.222 20.6741 0.2005 re
+f*
+0 g
+294.392 466.422 22.6813 0.2006 re
+f*
+1 g
+317.074 466.422 2.6094 0.2006 re
+f*
+0.498 0 0.482 rg
+319.683 466.422 29.1043 0.2006 re
+f*
+1 g
+348.787 466.422 4.4158 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 466.422 20.0719 0.2006 re
+f*
+0 g
+295.195 466.623 21.8784 0.2005 re
+f*
+1 g
+317.074 466.623 2.81 0.2005 re
+f*
+0.498 0 0.482 rg
+319.884 466.623 28.7029 0.2005 re
+f*
+1 g
+348.586 466.623 4.6166 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 466.623 19.4698 0.2005 re
+f*
+0 g
+295.797 466.823 21.477 0.2006 re
+f*
+1 g
+317.274 466.823 2.6093 0.2006 re
+f*
+0.498 0 0.482 rg
+319.884 466.823 28.7029 0.2006 re
+f*
+1 g
+348.586 466.823 4.6166 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 466.823 18.8676 0.2006 re
+f*
+0 g
+296.399 467.024 20.8748 0.2006 re
+f*
+1 g
+317.274 467.024 2.8101 0.2006 re
+f*
+0.498 0 0.482 rg
+320.084 467.024 28.3014 0.2006 re
+f*
+1 g
+348.386 467.024 4.8173 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 467.024 18.2654 0.2006 re
+f*
+0 g
+297.002 467.224 20.4734 0.2005 re
+f*
+1 g
+317.475 467.224 2.6094 0.2005 re
+f*
+0.498 0 0.482 rg
+320.084 467.224 28.1007 0.2005 re
+f*
+1 g
+348.185 467.224 5.018 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 467.224 17.6633 0.2005 re
+f*
+0 g
+297.604 467.425 20.072 0.2005 re
+f*
+1 g
+317.676 467.425 2.6093 0.2005 re
+f*
+0.498 0 0.482 rg
+320.285 467.425 27.9 0.2005 re
+f*
+1 g
+348.185 467.425 5.018 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 467.425 17.0611 0.2005 re
+f*
+0 g
+298.206 467.625 19.6704 0.2006 re
+f*
+1 g
+317.876 467.625 2.4087 0.2006 re
+f*
+0.498 0 0.482 rg
+320.285 467.625 27.6992 0.2006 re
+f*
+1 g
+347.984 467.625 5.2188 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 467.625 16.2582 0.2006 re
+f*
+0 g
+299.009 467.826 18.8676 0.2006 re
+f*
+1 g
+317.876 467.826 2.6094 0.2006 re
+f*
+0.498 0 0.482 rg
+320.486 467.826 27.2979 0.2006 re
+f*
+1 g
+347.784 467.826 5.4194 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 467.826 15.6561 0.2006 re
+f*
+0 g
+299.611 468.027 18.4661 0.2005 re
+f*
+1 g
+318.077 468.027 2.6095 0.2005 re
+f*
+0.498 0 0.482 rg
+320.687 468.027 26.8963 0.2005 re
+f*
+1 g
+347.583 468.027 5.6202 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 468.027 14.8532 0.2005 re
+f*
+0 g
+300.414 468.227 17.864 0.2006 re
+f*
+1 g
+318.278 468.227 2.4087 0.2006 re
+f*
+0.498 0 0.482 rg
+320.687 468.227 26.8963 0.2006 re
+f*
+1 g
+347.583 468.227 5.6202 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 468.227 14.2511 0.2006 re
+f*
+0 g
+301.217 468.428 17.2619 0.2005 re
+f*
+1 g
+318.479 468.428 2.4086 0.2005 re
+f*
+0.498 0 0.482 rg
+320.887 468.428 26.495 0.2005 re
+f*
+1 g
+347.382 468.428 5.8209 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 468.428 13.4482 0.2005 re
+f*
+0 g
+301.819 468.628 16.6597 0.2006 re
+f*
+1 g
+318.479 468.628 2.6094 0.2006 re
+f*
+0.498 0 0.482 rg
+321.088 468.628 26.0935 0.2006 re
+f*
+1 g
+347.181 468.628 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 468.628 12.6453 0.2006 re
+f*
+0 g
+302.622 468.829 16.0576 0.2006 re
+f*
+1 g
+318.679 468.829 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+321.088 468.829 25.8927 0.2006 re
+f*
+1 g
+346.981 468.829 6.2224 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 468.829 11.8424 0.2006 re
+f*
+0 g
+303.425 469.029 15.4553 0.2005 re
+f*
+1 g
+318.88 469.029 2.4087 0.2005 re
+f*
+0.498 0 0.482 rg
+321.289 469.029 25.4914 0.2005 re
+f*
+1 g
+346.78 469.029 6.423 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 469.029 11.2403 0.2005 re
+f*
+0 g
+304.227 469.23 14.8532 0.2006 re
+f*
+1 g
+319.081 469.23 2.4087 0.2006 re
+f*
+0.498 0 0.482 rg
+321.489 469.23 25.0899 0.2006 re
+f*
+1 g
+346.579 469.23 6.6238 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 469.23 10.4374 0.2006 re
+f*
+0 g
+305.231 469.43 14.0504 0.2005 re
+f*
+1 g
+319.281 469.43 2.4087 0.2005 re
+f*
+0.498 0 0.482 rg
+321.69 469.43 24.8891 0.2005 re
+f*
+1 g
+346.579 469.43 6.6238 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 469.43 9.4338 0.2005 re
+f*
+0 g
+306.034 469.631 13.4482 0.2006 re
+f*
+1 g
+319.482 469.631 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+321.891 469.631 24.4878 0.2006 re
+f*
+1 g
+346.379 469.631 6.8245 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 469.631 8.6309 0.2006 re
+f*
+0 g
+306.837 469.832 12.8461 0.2006 re
+f*
+1 g
+319.683 469.832 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+322.092 469.832 24.0863 0.2006 re
+f*
+1 g
+346.178 469.832 7.0252 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 469.832 7.6273 0.2006 re
+f*
+0 g
+307.841 470.032 12.0431 0.2005 re
+f*
+1 g
+319.884 470.032 2.4087 0.2005 re
+f*
+0.498 0 0.482 rg
+322.292 470.032 23.6848 0.2005 re
+f*
+1 g
+345.977 470.032 7.226 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 470.032 6.8244 0.2005 re
+f*
+0 g
+308.844 470.233 11.2403 0.2006 re
+f*
+1 g
+320.084 470.233 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+322.493 470.233 23.2835 0.2006 re
+f*
+1 g
+345.776 470.233 7.4266 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 470.233 5.6201 0.2006 re
+f*
+0 g
+309.848 470.433 10.4374 0.2005 re
+f*
+1 g
+320.285 470.433 2.4087 0.2005 re
+f*
+0.498 0 0.482 rg
+322.694 470.433 22.6812 0.2005 re
+f*
+1 g
+345.375 470.433 7.8281 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 470.433 4.6165 0.2005 re
+f*
+0 g
+311.052 470.634 9.4338 0.2006 re
+f*
+1 g
+320.486 470.634 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+322.894 470.634 22.2799 0.2006 re
+f*
+1 g
+345.174 470.634 8.0288 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 470.634 3.6129 0.2006 re
+f*
+0 g
+312.256 470.834 8.4303 0.2005 re
+f*
+1 g
+320.687 470.834 2.4086 0.2005 re
+f*
+0.498 0 0.482 rg
+323.095 470.834 21.8783 0.2005 re
+f*
+1 g
+344.973 470.834 8.2296 0.2005 re
+f*
+0.498 0 0.482 rg
+353.203 470.834 2.4086 0.2005 re
+f*
+0 g
+313.461 471.035 7.6274 0.2006 re
+f*
+1 g
+321.088 471.035 2.2079 0.2006 re
+f*
+0.498 0 0.482 rg
+323.296 471.035 21.477 0.2006 re
+f*
+1 g
+344.773 471.035 8.4302 0.2006 re
+f*
+0.498 0 0.482 rg
+353.203 471.035 1.2043 0.2006 re
+f*
+0 g
+314.665 471.235 6.6237 0.2006 re
+f*
+1 g
+321.289 471.235 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+323.697 471.235 20.8748 0.2006 re
+f*
+0 g
+316.271 471.436 5.2187 0.2005 re
+f*
+1 g
+321.489 471.436 2.4086 0.2005 re
+f*
+0.498 0 0.482 rg
+323.898 471.436 20.2727 0.2005 re
+f*
+0 g
+317.676 471.637 4.215 0.2006 re
+f*
+1 g
+321.891 471.637 2.208 0.2006 re
+f*
+0.498 0 0.482 rg
+324.099 471.637 19.8711 0.2006 re
+f*
+0 g
+319.482 471.837 2.6094 0.2006 re
+f*
+1 g
+322.092 471.837 2.4086 0.2006 re
+f*
+0.498 0 0.482 rg
+324.5 471.837 19.0683 0.2006 re
+f*
+0 g
+321.289 472.038 0.8029 0.2005 re
+f*
+1 g
+322.092 472.038 2.6093 0.2005 re
+f*
+0.498 0 0.482 rg
+324.701 472.038 18.6669 0.2005 re
+f*
+0.498 0 0.482 rg
+325.102 472.238 17.864 0.2006 re
+f*
+0.498 0 0.482 rg
+325.504 472.439 17.2619 0.2005 re
+f*
+0.498 0 0.482 rg
+325.905 472.639 16.459 0.2006 re
+f*
+0.498 0 0.482 rg
+326.307 472.84 15.6561 0.2006 re
+f*
+0.498 0 0.482 rg
+326.909 473.041 14.6526 0.2005 re
+f*
+0.498 0 0.482 rg
+327.511 473.241 13.4482 0.2006 re
+f*
+0.498 0 0.482 rg
+328.113 473.442 12.4447 0.2005 re
+f*
+0.498 0 0.482 rg
+328.715 473.642 11.2403 0.2006 re
+f*
+0.498 0 0.482 rg
+329.518 473.843 9.8352 0.2005 re
+f*
+0.498 0 0.482 rg
+330.321 474.043 8.2296 0.2006 re
+f*
+0.498 0 0.482 rg
+331.525 474.244 6.0216 0.2006 re
+f*
+0.498 0 0.482 rg
+333.533 474.445 2.2079 0.2005 re
+f*
+Q
+showpage
+pdfEndPage
+end
+%%Trailer
+cleartomark
+countdictstack
+exch sub { end } repeat
+restore
+%%EOF
+grestore
diff --git a/conf/logo.png b/conf/logo.png
new file mode 100644
index 0000000..1e415e6
--- /dev/null
+++ b/conf/logo.png
Binary files differ
diff --git a/conf/maxsearchrecordsperpage b/conf/maxsearchrecordsperpage
new file mode 100644
index 0000000..29d6383
--- /dev/null
+++ b/conf/maxsearchrecordsperpage
@@ -0,0 +1 @@
+100
diff --git a/conf/payment_receipt_email b/conf/payment_receipt_email
new file mode 100644
index 0000000..1a0a758
--- /dev/null
+++ b/conf/payment_receipt_email
@@ -0,0 +1,26 @@
+
+{ $date }
+
+Dear { $name },
+
+This message is to inform you that your payment of ${ $paid } has been
+received.
+
+Payment ID: { $paynum }
+Date: { $date }
+Amount: { $paid }
+Type: { $payby } # { $payinfo }
+
+{
+ if ( $balance > 0 ) {
+ $OUT .= "Your current balance is now \$$balance.\n\n";
+ } elsif ( $balance < 0 ) {
+ $OUT .= 'You have a credit balance of $'. sprintf("%.2f",0-$balance).
+ ".\n".
+ "Future charges will be deducted from this balance before billing ".
+ "you again.\n\n";
+
+ }
+}
+Thank you for your business.
+
diff --git a/conf/registries/internic/from b/conf/registries/internic/from
deleted file mode 100644
index dc36ae7..0000000
--- a/conf/registries/internic/from
+++ /dev/null
@@ -1 +0,0 @@
-domreg@domain.tld
diff --git a/conf/registries/internic/nameservers b/conf/registries/internic/nameservers
deleted file mode 100644
index e1aa999..0000000
--- a/conf/registries/internic/nameservers
+++ /dev/null
@@ -1,3 +0,0 @@
-192.168.1.1 ns1.domain.tld
-192.168.1.2 ns2.domain.tld
-192.168.1.3 ns3.domain.tld
diff --git a/conf/registries/internic/tech_contact b/conf/registries/internic/tech_contact
deleted file mode 100644
index 1e6fea0..0000000
--- a/conf/registries/internic/tech_contact
+++ /dev/null
@@ -1 +0,0 @@
-A1
diff --git a/conf/registries/internic/template b/conf/registries/internic/template
deleted file mode 100644
index 8e4983c..0000000
--- a/conf/registries/internic/template
+++ /dev/null
@@ -1,231 +0,0 @@
-[ URL ftp://rs.internic.net/templates/domain-template.txt ] [ 03/98 ]
-
-******* Please DO NOT REMOVE Version Number or Sections A-Q ********
-
-Domain Version Number: 4.0
-
-******* Email completed agreement to hostmaster@internic.net *******
-
- NETWORK SOLUTIONS, INC.
-
- DOMAIN NAME REGISTRATION AGREEMENT
-
-
-A. Introduction. This domain name registration agreement
-("Registration Agreement") is submitted to NETWORK SOLUTIONS, INC.
-("NSI") for the purpose of applying for and registering a domain name
-on the Internet. If this Registration Agreement is accepted by NSI,
-and a domain name is registered in NSI's domain name database and
-assigned to the Registrant, Registrant ("Registrant") agrees to be
-bound by the terms of this Registration Agreement and the terms of
-NSI's Domain Name Dispute Policy ("Dispute Policy") which is
-incorporated herein by reference and made a part of this Registration
-Agreement. This Registration Agreement shall be accepted at the
-offices of NSI.
-
-B. Fees and Payments.
-
-1) Registration or renewal (re-registration) date through March 31, 1998:
-Registrant agrees to pay a registration fee of One Hundred United States
-Dollars (US$100) as consideration for the registration of each new domain
-name or Fifty United States Dollars (US$50) to renew (re-register) an
-existing registration.
-2) Registration or renewal date on and after April 1, 1998: Registrant
-agrees to pay a registration fee of Seventy United States Dollars (US$70)
-as consideration for the registration of each new domain name or the
-applicable renewal (re-registration) fee (currently Thirty-Five United
-States Dollars (US$35)) at the time of renewal (re-registration).
-3) Period of Service: The non-refundable fee covers a period of two (2)
-years for each new registration, and one (1) year for each renewal,
-and includes any permitted modification(s) to the domain name record
-during the covered period.
-4) Payment: Payment is due to Network Solutions within thirty (30)
-days from the date of the invoice.
-
-C. Dispute Policy. Registrant agrees, as a condition to
-submitting this Registration Agreement, and if the Registration
-Agreement is accepted by NSI, that the Registrant shall be bound by
-NSI's current Dispute Policy. The current version of the Dispute
-Policy may be found at the InterNIC Registration Services web site:
-"http://www.netsol.com/rs/dispute-policy.html".
-
-D. Dispute Policy Changes or Modifications. Registrant agrees
-that NSI, in its sole discretion, may change or modify the Dispute
-Policy, incorporated by reference herein, at any time. Registrant
-agrees that Registrant's maintaining the registration of a domain name
-after changes or modifications to the Dispute Policy become effective
-constitutes Registrant's continued acceptance of these changes or
-modifications. Registrant agrees that if Registrant considers any such
-changes or modifications to be unacceptable, Registrant may request
-that the domain name be deleted from the domain name database.
-
-E. Disputes. Registrant agrees that, if the registration of its
-domain name is challenged by any third party, the Registrant will be
-subject to the provisions specified in the Dispute Policy.
-
-F. Agents. Registrant agrees that if this Registration Agreement
-is completed by an agent for the Registrant, such as an ISP or
-Administrative Contact/Agent, the Registrant is nonetheless bound as a
-principal by all terms and conditions herein, including the Dispute
-Policy.
-
-G. Limitation of Liability. Registrant agrees that NSI shall have
-no liability to the Registrant for any loss Registrant may incur in
-connection with NSI's processing of this Registration Agreement, in
-connection with NSI's processing of any authorized modification to the
-domain name's record during the covered period, as a result of the
-Registrant's ISP's failure to pay either the initial registration fee
-or renewal fee, or as a result of the application of the provisions of
-the Dispute Policy. Registrant agrees that in no event shall the
-maximum liability of NSI under this Agreement for any matter exceed
-Five Hundred United States Dollars (US$500).
-
-H. Indemnity. Registrant agrees, in the event the Registration
-Agreement is accepted by NSI and a subsequent dispute arises with any
-third party, to indemnify and hold NSI harmless pursuant to the terms
-and conditions contained in the Dispute Policy.
-
-I. Breach. Registrant agrees that failure to abide by any
-provision of this Registration Agreement or the Dispute Policy may be
-considered by NSI to be a material breach and that NSI may provide a
-written notice, describing the breach, to the Registrant. If, within
-thirty (30) days of the date of mailing such notice, the Registrant
-fails to provide evidence, which is reasonably satisfactory to NSI,
-that it has not breached its obligations, then NSI may delete
-Registrant's registration of the domain name. Any such breach by a
-Registrant shall not be deemed to be excused simply because NSI did
-not act earlier in response to that, or any other, breach by the
-Registrant.
-
-J. No Guaranty. Registrant agrees that, by registration of a
-domain name, such registration does not confer immunity from objection
-to either the registration or use of the domain name.
-
-K. Warranty. Registrant warrants by submitting this Registration
-Agreement that, to the best of Registrant's knowledge and belief, the
-information submitted herein is true and correct, and that any future
-changes to this information will be provided to NSI in a timely manner
-according to the domain name modification procedures in place at that
-time. Breach of this warranty will constitute a material breach.
-
-L. Revocation. Registrant agrees that NSI may delete a
-Registrant's domain name if this Registration Agreement, or subsequent
-modification(s) thereto, contains false or misleading information, or
-conceals or omits any information NSI would likely consider material
-to its decision to approve this Registration Agreement.
-
-M. Right of Refusal. NSI, in its sole discretion, reserves the
-right to refuse to approve the Registration Agreement for any
-Registrant. Registrant agrees that the submission of this Registration
-Agreement does not obligate NSI to accept this Registration Agreement.
-Registrant agrees that NSI shall not be liable for loss or damages
-that may result from NSI's refusal to accept this Registration
-Agreement.
-
-N. Severability. Registrant agrees that the terms of this
-Registration Agreement are severable. If any term or provision is
-declared invalid, it shall not affect the remaining terms or
-provisions which shall continue to be binding.
-
-O. Entirety. Registrant agrees that this Registration Agreement
-and the Dispute Policy is the complete and exclusive agreement between
-Registrant and NSI regarding the registration of Registrant's domain
-name. This Registration Agreement and the Dispute Policy supersede all
-prior agreements and understandings, whether established by custom,
-practice, policy, or precedent.
-
-P. Governing Law. Registrant agrees that this Registration
-Agreement shall be governed in all respects by and construed in
-accordance with the laws of the Commonwealth of Virginia, United
-States of America. By submitting this Registration Agreement,
-Registrant consents to the exclusive jurisdiction and venue of the
-United States District Court for the Eastern District of Virginia,
-Alexandria Division. If there is no jurisdiction in the United States
-District Court for the Eastern District of Virginia, Alexandria
-Division, then jurisdiction shall be in the Circuit Court of Fairfax
-County, Fairfax, Virginia.
-
-Q. This is Domain Name Registration Agreement Version
-Number 4.0. This Registration Agreement is only for registrations
-under top-level domains: COM, ORG, NET, and EDU. By completing
-and submitting this Registration Agreement for consideration and
-acceptance by NSI, the Registrant agrees that he/she has read and
-agrees to be bound by A through P above.
-
-
-Authorization
-0a. (N)ew (M)odify (D)elete....:###action###
-0b. Auth Scheme................:
-0c. Auth Info..................:
-
-1. Comments...................:###purpose###
-
-2. Complete Domain Name.......:###domain###
-
-Organization Using Domain Name
-
-3a. Organization Name..........:###company###
-###LOOP###
-3b. Street Address.............:###address###
-###ENDLOOP###
-3c. City.......................:###city###
-3d. State......................:###state###
-3e. Postal Code................:###zip###
-3f. Country....................:###country###
-
-Administrative Contact
-4a. NIC Handle (if known)......:
-4b. (I)ndividual (R)ole........:I
-4c. Name (Last, First).........:###last###, ###first###
-4d. Organization Name..........:###company###
-###LOOP###
-4e. Street Address.............:###address###
-###ENDLOOP###
-4f. City.......................:###city###
-4g. State......................:###state###
-4h. Postal Code................:###zip###
-4i. Country....................:###country###
-4j. Phone Number...............:###daytime###
-4k. Fax Number.................:###fax###
-4l. E-Mailbox..................:###email###
-
-Technical Contact
-5a. NIC Handle (if known)......:###tech_contact###
-5b. (I)ndividual (R)ole........:
-5c. Name (Last, First).........:
-5d. Organization Name..........:
-5e. Street Address.............:
-5f. City.......................:
-5g. State......................:
-5h. Postal Code................:
-5i. Country....................:
-5j. Phone Number...............:
-5k. Fax Number.................:
-5l. E-Mailbox..................:
-
-Billing Contact
-6a. NIC Handle (if known)......:
-6b. (I)ndividual (R)ole........:
-6c. Name (Last, First).........:
-6d. Organization Name..........:
-6e. Street Address.............:
-6f. City.......................:
-6g. State......................:
-6h. Postal Code................:
-6i. Country....................:
-6j. Phone Number...............:
-6k. Fax Number.................:
-6l. E-Mailbox..................:
-
-Prime Name Server
-7a. Primary Server Hostname....:###primary###
-7b. Primary Server Netaddress..:###primary_ip###
-
-Secondary Name Server(s)
-###LOOP###
-8a. Secondary Server Hostname..:###secondary###
-8b. Secondary Server Netaddress:###secondary_ip###
-###ENDLOOP###
-
-END OF AGREEMENT
-
diff --git a/conf/registries/internic/to b/conf/registries/internic/to
deleted file mode 100644
index c80f93c..0000000
--- a/conf/registries/internic/to
+++ /dev/null
@@ -1 +0,0 @@
-hostmaster@internic.net
diff --git a/conf/secrets b/conf/secrets
deleted file mode 100644
index 5843943..0000000
--- a/conf/secrets
+++ /dev/null
@@ -1,3 +0,0 @@
-DBI:mysql:freeside
-freeside
-put_your_password_here
diff --git a/conf/shells b/conf/shells
index 02d74f7..a41fc62 100644
--- a/conf/shells
+++ b/conf/shells
@@ -1,2 +1,5 @@
-/bin/csh
+
/bin/sh
+/bin/csh
+/bin/bash
+/bin/false
diff --git a/conf/show-msgcat-codes b/conf/show-msgcat-codes
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/conf/show-msgcat-codes
diff --git a/conf/smtpmachine b/conf/smtpmachine
index fa7963c..2fbb50c 100644
--- a/conf/smtpmachine
+++ b/conf/smtpmachine
@@ -1 +1 @@
-mail
+localhost
diff --git a/conf/soadefaultttl b/conf/soadefaultttl
new file mode 100644
index 0000000..92f616f
--- /dev/null
+++ b/conf/soadefaultttl
@@ -0,0 +1 @@
+259200
diff --git a/conf/soaexpire b/conf/soaexpire
new file mode 100644
index 0000000..d235b91
--- /dev/null
+++ b/conf/soaexpire
@@ -0,0 +1 @@
+3600000
diff --git a/conf/soarefresh b/conf/soarefresh
new file mode 100644
index 0000000..9f35f8e
--- /dev/null
+++ b/conf/soarefresh
@@ -0,0 +1 @@
+10800
diff --git a/conf/soaretry b/conf/soaretry
new file mode 100644
index 0000000..bb08106
--- /dev/null
+++ b/conf/soaretry
@@ -0,0 +1 @@
+1800
diff --git a/conf/ticket_system b/conf/ticket_system
new file mode 100644
index 0000000..631f98a
--- /dev/null
+++ b/conf/ticket_system
@@ -0,0 +1 @@
+RT_Internal
diff --git a/conf/welcome_letter b/conf/welcome_letter
new file mode 100644
index 0000000..be7b484
--- /dev/null
+++ b/conf/welcome_letter
@@ -0,0 +1,121 @@
+%% file: random_latex
+%% Purpose: Multipage template for welcome letters
+%%
+%% Based on work by
+%%
+%% Mark Asplen-Taylor
+%% Asplen Management Ltd
+%% www.asplen.co.uk
+%%
+%% Kristian Hoffman
+%%
+%% Changes
+%% 0.1 6/19/07 Created
+
+\documentclass[letterpaper]{article}
+
+\hyphenpenalty=5000
+\usepackage{fancyhdr,lastpage,ifthen,afterpage}
+\usepackage{graphicx} % required for logo graphic
+
+\addtolength{\voffset}{-0.0cm} % top margin to top of header
+\addtolength{\hoffset}{-0.6cm} % left margin on page
+\addtolength{\topmargin}{-1.25cm} % top margin to top of header
+\setlength{\headheight}{2.0cm} % height of header
+\setlength{\headsep}{1.0cm} % between header and text
+\setlength{\footskip}{1.0cm} % bottom of footer from bottom of text
+
+%\addtolength{\textwidth}{2.1in} % width of text
+\setlength{\textwidth}{19.5cm}
+\setlength{\textheight}{19.5cm}
+\setlength{\oddsidemargin}{-0.9cm} % odd page left margin
+\setlength{\evensidemargin}{-0.9cm} % even page left margin
+
+\renewcommand{\headrulewidth}{0pt}
+\renewcommand{\footrulewidth}{0pt}
+
+% Adjust the inset of the mailing address
+\newcommand{\addressinset}[1][]{\hspace{1.0cm}}
+
+% Adjust the inset of the return address and logo
+\newcommand{\returninset}[1][]{\hspace{-0.25cm}}
+
+% New command for address lines i.e. skip them if blank
+\newcommand{\addressline}[1]{\ifthenelse{\equal{#1}{}}{}{#1\newline}}
+
+% Remove plain style header/footer
+\fancypagestyle{plain}{
+ \fancyhead{}
+}
+\fancyhf{}
+
+% Define fancy header/footer for first and subsequent pages
+
+\fancyfoot[R]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ ~
+ }
+ { % ... pages
+ \small{\thepage\ of \pageref{LastPage}}
+ }
+}
+
+\fancyhead[L]{
+ \ifthenelse{\equal{\thepage}{1}}
+ { % First page
+ \returninset
+ \makebox{
+ \begin{tabular}{ll}
+ \includegraphics{[@-- $conf_dir --@]/logo.eps} &
+ \begin{minipage}[b]{5.5cm}
+[@-- $returnaddress --@]
+ \end{minipage}
+ \end{tabular}
+ }
+ }
+ { % ... pages
+ %\includegraphics{[@-- $conf_dir --@]/logo.eps} % Uncomment if you want the logo on all pages.
+ }
+}
+
+\pagestyle{fancy}
+
+
+%% Font options are:
+%% bch Bitsream Charter
+%% put Utopia
+%% phv Adobe Helvetica
+%% pnc New Century Schoolbook
+%% ptm Times
+%% pcr Courier
+
+\renewcommand{\familydefault}{phv}
+
+
+\begin{document}
+%
+\begin{tabular}{ll}
+\addressinset \rule{0cm}{0cm} &
+\makebox{
+\begin{minipage}[t]{5.0cm}
+\vspace{0.25cm}
+\textbf{[@-- $payname --@]}\\
+\addressline{[@-- $company --@]}
+\addressline{[@-- $address1 --@]}
+\addressline{[@-- $address2 --@]}
+\addressline{[@-- $city --@], [@-- $state --@]~~[@-- $zip --@]}
+\addressline{[@-- $country --@]}
+\end{minipage}}
+\end{tabular}
+\vspace{1.5cm}
+\\
+% Your content goes here
+Dear [@-- $first --@] [@-- $last --@]:\\
+\\
+ Thank you for choosing [@-- $company_name --@]. We aim to meet all of your
+ needs. Please do not hesitate to contact us for any additional
+ services or assistance.\\
+
+\end{document}
+
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..829b543
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,25 @@
+Freeside for Debian
+-------------------
+
+1.
+Edit /etc/apache2/envvars or /etc/apache2/apache2.conf and set User and Group
+to freeside
+
+2.
+/etc/init.d/apache2 restart
+
+3.
+Create one or more Freeside users (your internal sales/tech folks, not customer accounts):
+$ su
+# su freeside
+$ freeside-adduser -g 1 desired_username
+$ htpasswd /etc/freeside/htpasswd username
+(enter password)
+
+4.
+Go to http://your.host.name/freeside and log in.
+
+Optional but recommended.
+(Hopefully) get an SSL certificate setup and change that to https://
+
+ -- Ivan Kohler <ivan-debian@420.am> Wed, 02 Apr 2008 01:46:20 -0700
diff --git a/debian/TODO b/debian/TODO
new file mode 100644
index 0000000..15fed69
--- /dev/null
+++ b/debian/TODO
@@ -0,0 +1,38 @@
+
+test) freeside-webui /etc/apache/conf.d/freeside.conf
+ AuthUserFile is wrong (just fucked)
+
+test its working) somes sort of Alias /freeside /usr/share/freeside/www is needed
+
+test in postinst) freeside package var/cache/freeside/cache.<datasrc is missing>
+
+test RT is missing. doh. get it working.
+
+test actually installing!
+
+--- rc2... right? ---
+
+freeside-selfservice-client doesn't install at all
+
+start freeside-sqlradius-radacctd from /etc/default/freeside too
+
+Added to README.Debian... do something else?
+Ensure apache is set to run as User freeside.
+
+init script doesn't need to add /usr/local/bin. could start over from
+init.d.ex or init.d.lsb.ex
+
+finish
+
+RT install locations (or for now: disable for unstable, enable for
+experiemental. but try to get it finished off in time for lenny)
+
+debian/copyright administrivia
+
+AGPL drama
+
+upload
+
+AGPL drama or silent waiting for days or years
+
+profit! err
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..d53e90e
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,6 @@
+freeside (1.9.0~cvs0-1) unstable; urgency=low
+
+ * Initial release
+
+ -- Ivan Kohler <ivan-debian@420.am> Wed, 02 Apr 2008 01:46:20 -0700
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/debian/config b/debian/config
new file mode 100644
index 0000000..4ffa236
--- /dev/null
+++ b/debian/config
@@ -0,0 +1,19 @@
+#!/bin/sh
+# config script for freeside
+
+set -e
+
+# source debconf stuff
+. /usr/share/debconf/confmodule
+
+# source dbconfig-common shell library, and call the hook function
+if [ -f /usr/share/dbconfig-common/dpkg/config ]; then
+ # we support mysql and pgsql
+ dbc_dbtypes="pgsql, mysql"
+
+ # source dbconfig-common stuff
+ . /usr/share/dbconfig-common/dpkg/config
+ dbc_go freeside $@
+fi
+
+# ... rest of your code ...
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..75869a0
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,59 @@
+Source: freeside
+Section: misc
+Priority: extra
+Maintainer: Ivan Kohler <ivan-debian@420.am>
+Build-Depends: debhelper (>= 5), perl (>= 5.8)
+Standards-Version: 3.7.2
+Homepage: http://www.freeside.biz/freeside
+Vcs-Browser: http://www.freeside.biz/cgi-bin/viewvc.cgi/freeside/
+Vcs-Cvs: :pserver:anonymous:anonymous@cvs.420.am:/home/cvs/cvsroot freeside
+
+Package: freeside
+Architecture: all
+Pre-Depends: freeside-lib, dbconfig-common
+Depends: ${perl:Depends}, ${shlibs:Depends}, ${misc:Depends}, freeside-webui, debconf, adduser (>= 3.11)
+Recommends: cron
+Suggests: gnupg
+Description: Billing and trouble ticketing for service providers
+ Freeside is a web-based billing and trouble ticketing application. It
+ includes features for ISPs, hosting providers, and VoIP providers, but can
+ also be used as a generic customer database, invoicing and membership
+ application. If you like buzzwords, call it an "BSS/OSS and CRM solution".
+
+Package: freeside-lib
+Architecture: all
+Depends: ghostscript | gs-gpl, gsfonts, tetex-base, tetex-bin, libauthen-passphrase-perl, libbusiness-creditcard-perl, libcache-cache-perl, libcache-simple-timedexpiry-perl, libclass-returnvalue-perl, libcrypt-passwdmd5-perl, libdate-manip-perl, libdbd-pg-perl | libdbd-mysql-perl, libdbi-perl, libdbix-dbschema-perl (>= 0.35), libdbix-searchbuilder-perl, libdigest-sha1-perl, libfile-counterfile-perl, libfile-rsync-perl, libfrontier-rpc-perl, libhtml-format-perl, libhtml-tree-perl, libipc-run3-perl, libipc-sharelite-perl, liblingua-en-nameparse-perl, liblocale-maketext-fuzzy-perl, liblocale-maketext-lexicon-perl, liblocale-subcountry-perl, liblog-dispatch-perl, libmailtools-perl (>= 2), libmime-perl (>= 5.424) | libmime-perl (< 5.421), libnet-domain-tld-perl, libnet-scp-perl, libnet-ssh-perl, libnet-whois-raw-perl, libnetaddr-ip-perl, libnumber-format-perl, libregexp-common-perl, libstring-approx-perl, libstring-shellquote-perl, libterm-readkey-perl, libtest-inline-perl, libtext-autoformat-perl, libtext-csv-perl, libtext-template-perl, libtext-wrapper-perl, libtie-ixhash-perl, libtime-duration-perl, libtime-modules-perl, libtimedate-perl, libuniversal-require-perl, liburi-perl, libwant-perl, libwww-perl
+Recommends: libdbd-pg-perl, libdbd-mysql-perl, rsync
+Suggests: libbusiness-onlinepayment-perl
+Description: Libraries for Freeside billing and trouble ticketing
+ Freeside is a web-based billing and trouble ticketing application.
+ .
+ This package provides the perl libraries and command line utilities.
+
+#Package: freeside-bin
+#Architecture: all
+#Depends: freeside-lib
+#Description: Command line tools for Freeside billing and trouble ticketing
+# Freeside is a web-based billing and trouble ticketing application.
+# .
+# This package provides the command-line utilities.
+
+Package: freeside-webui
+Architecture: all
+Depends: freeside-lib, apache2, libapache2-mod-perl2, libapache2-request-perl, libapache-session-perl, libchart-perl, libcolor-scheme-perl, libdatetime-perl, libdatetime-format-strptime-perl, libgd-gd2-noxpm-perl | libgd-gd2-perl, libgd-graph-perl, libhtml-mason-perl, libhtml-scrubber-perl, libhtml-widgets-selectlayers-perl, libio-stringy-perl, libjson-perl, liblingua-en-inflect-perl, libmodule-versions-report-perl, libspreadsheet-writeexcel-perl, libtree-simple-perl, libyaml-perl
+Recommends: libapache-dbi-perl
+Description: Web interface for Freeside billing and trouble ticketing
+ Freeside is a web-based billing and trouble ticketing application.
+ .
+ This package provides the web interface for employees.
+
+#Package: freeside-selfservice-client
+#Architecture: all
+#Description: End-customer interface to Freeside billing and trouble ticketing
+# Freeside is a web-based billing and trouble ticketing application.
+# .
+# This package provides customer signup and self-service web interfaces and
+# XML-RPC, PHP and Perl APIs.
+# .
+# In production use, this package is typically installed on a public web server,
+# separate from the rest of the freeside-* packages.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..c409cb9
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,45 @@
+This package was debianized by Ivan Kohler <ivan-debian@420.am> on
+Wed, 02 Apr 2008 01:46:20 -0700.
+
+It was downloaded from <http://www.freeside.biz/freeside>
+
+Upstream Author(s):
+
+ Freeside Internet Services, Inc.
+
+Copyright:
+
+Copyright (C) 2005-2008 Freeside Internet Services, Inc.
+Copyright (C) 2000-2005 Ivan Kohler
+Copyright (C) 1999 Silicon Interactive Software Design
+All rights reserved
+
+before uploading to debian proper: <likewise for all other copyrights from httemplate/docs/license.html>
+
+License:
+
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This package 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+On Debian systems, the complete text of the GNU Affero General
+Public License may someday be found in `/usr/share/common-licenses/AGPL'.
+Until then, you can find it in `/usr/share/doc/freeside/AGPL'.
+
+The Debian packaging is (C) 2008, Ivan Kohler <ivan-debian@420.am> and
+is licensed under the AGPL, see above.
+
+before uploading to debian proper, from httemplate/docs/license.html:
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
+
diff --git a/debian/cron.d b/debian/cron.d
new file mode 100644
index 0000000..f86db1b
--- /dev/null
+++ b/debian/cron.d
@@ -0,0 +1,4 @@
+#
+# Regular cron jobs for the freeside package
+#
+0 0 * * * freeside /usr/bin/freeside-daily fs_daily
diff --git a/debian/dbconfig-common.install b/debian/dbconfig-common.install
new file mode 100644
index 0000000..31b5d14
--- /dev/null
+++ b/debian/dbconfig-common.install
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+. /etc/dbconfig-common/freeside.conf
+
+DB_USER=$dbc_dbuser
+DB_PASSWORD=$dbc_dbpass
+
+# -- can't find a better place to hook this in. dammit.
+
+[ "$dbc_dbtype" = "pgsql" ] && DB_TYPE=Pg
+[ "$dbc_dbtype" = "mysql" ] && DB_TYPE=mysql
+#XXX ask dbc about a remote database etc.
+DATASOURCE=DBI:${DB_TYPE}:dbname=${dbc_dbname}
+
+#debian/rules
+FREESIDE_CONF=/etc/freeside
+FREESIDE_CACHE=/var/cache/freeside
+#XXX huh?
+FREESIDE_EXPORT=/var/spool/freeside
+DEFAULT_CONF=/usr/share/freeside/default_conf
+
+#XXX this rather seriously needs proper debian-style config file handling.
+
+#shamelessly lifted from Makefile create-config target
+[ -e ${FREESIDE_CONF} ] || install -d -o freeside ${FREESIDE_CONF}
+
+touch ${FREESIDE_CONF}/secrets
+chown freeside ${FREESIDE_CONF}/secrets
+chmod 600 ${FREESIDE_CONF}/secrets
+
+[ -s ${FREESIDE_CONF}/secrets ] || echo -e "${DATASOURCE}\n${DB_USER}\n${DB_PASSWORD}" >${FREESIDE_CONF}/secrets
+chmod 600 ${FREESIDE_CONF}/secrets
+chown freeside ${FREESIDE_CONF}/secrets
+
+#XXX yuck! this too!
+[ -e /var/opt/freeside/rt/etc/RT_Config.pm.dbc ] || cp /var/opt/freeside/rt/etc/RT_Config.pm.dbc.generic /var/opt/freeside/rt/etc/RT_Config.pm.dbc
+perl -pi.generic -e "s/^\\s*Set\\s*\\(\s*\\\$DatabaseType.*\$/Set(\\\$DatabaseType, '$DB_TYPE');/" /var/opt/freeside/rt/etc/RT_Config.pm.dbc
+mv /var/opt/freeside/rt/etc/RT_Config.pm.dbc /var/opt/freeside/rt/etc/RT_Config.pm
+perl -pi -e "\
+ s'_DBC_DBUSER_'${dbc_dbuser}'g;\
+ s'_DBC_DBPASS_'${dbc_dbpass}'g;\
+ s'_DBC_DBNAME_'${dbc_dbname}'g;\
+" /var/opt/freeside/rt/etc/RT_Config.pm
+
+#dunno how to hook this in where i need it...
+#dbc_generate_include="template:/var/opt/freeside/rt/etc/RT_Config.pm"
+#dbc_generate_include_args="-o template_infile=/var/opt/freeside/rt/etc/RT_Config.pm.dbc"
+
+install -o freeside -d "${FREESIDE_CACHE}/counters.${DATASOURCE}"
+install -o freeside -d "${FREESIDE_CACHE}/cache.${DATASOURCE}"
+install -o freeside -d "${FREESIDE_EXPORT}/export.${DATASOURCE}"
+
+if [ ! -d "${FREESIDE_CONF}/conf.${DATASOURCE}" ] ; then #don't clobber conf
+install -o freeside -d "${FREESIDE_CONF}/conf.${DATASOURCE}"
+#cp conf/[a-z]* "${FREESIDE_CONF}/conf.${DATASOURCE}"
+cp -i `ls -d ${DEFAULT_CONF}/[a-z]* | grep -v CVS` "${FREESIDE_CONF}/conf.${DATASOURCE}" #-i just in case
+chown -R freeside "${FREESIDE_CONF}/conf.${DATASOURCE}"
+fi
+
+# -- back to your regularly schedule program... go ahead, create the db
+
+DOMAIN=`dnsdomainname`
+if [ "$DOMAIN" = "localdomain" ]; then #freeside needs a valid domain
+ DOMAIN='example.com'
+fi
+
+# XXX this should probably be handled by the _install_...
+# dpkg-statoverride or something
+chown freeside /etc/freeside
+
+su freeside -c "/usr/bin/freeside-setup -d $DOMAIN"
+su freeside -c '/usr/bin/freeside-adduser -g 1 fs_queue'
+su freeside -c '/usr/bin/freeside-adduser -g 1 fs_daily'
+su freeside -c '/usr/bin/freeside-adduser -g 1 fs_selfservice'
+su freeside -c '/usr/bin/freeside-adduser -g 1 fs_upgrade'
+
+#RT paths are bunk for deb proper
+
+chown freeside /var/opt/freeside/rt/etc/RT_Config.pm
+
+su freeside -c "/var/opt/freeside/rt/sbin/rt-setup-database --dba '$DB_USER' --dba-password '$DB_PASSWORD' --action schema"
+
+su freeside -c '/var/opt/freeside/rt/sbin/rt-setup-database --action insert_initial'
+
+su freeside -c '/var/opt/freeside/rt/sbin/rt-setup-database --action insert --datafile /var/opt/freeside/rt/etc/initialdata'
+
+#XXX this totally doesn't belong here, but what the hey
+chown -R freeside /var/cache/freeside/masondata
+
+exit 0
diff --git a/debian/dbconfig-common.upgrade b/debian/dbconfig-common.upgrade
new file mode 100644
index 0000000..cae9adb
--- /dev/null
+++ b/debian/dbconfig-common.upgrade
@@ -0,0 +1,3 @@
+#!/bin/sh
+su freeside -c '/usr/bin/freeside-upgrade fs_upgrade'
+#RT upgrade
diff --git a/debian/freeside-webui.links b/debian/freeside-webui.links
new file mode 100644
index 0000000..7ca4030
--- /dev/null
+++ b/debian/freeside-webui.links
@@ -0,0 +1,4 @@
+etc/freeside/apache2/freeside-alias.conf etc/apache2/conf.d/freeside-alias.conf
+etc/freeside/apache2/freeside-base2.conf etc/apache2/conf.d/freeside-base2.conf
+etc/freeside/apache2/freeside-rt.conf etc/apache2/conf.d/freeside-rt.conf
+
diff --git a/debian/freeside.apache-alias.conf b/debian/freeside.apache-alias.conf
new file mode 100644
index 0000000..fdd4340
--- /dev/null
+++ b/debian/freeside.apache-alias.conf
@@ -0,0 +1 @@
+Alias /freeside/ /usr/share/freeside/www/
diff --git a/debian/freeside.default b/debian/freeside.default
new file mode 100644
index 0000000..eca0306
--- /dev/null
+++ b/debian/freeside.default
@@ -0,0 +1,12 @@
+# Defaults for freeside initscript
+# sourced by /etc/init.d/freeside
+# installed at /etc/default/freeside by the maintainer scripts
+
+#
+# This is a POSIX shell fragment
+#
+
+# Additional options that are passed to the Freeside startup scripts.
+SELFSERVICE_MACHINES=""
+
+#start freeside-sqlradius-radacctd from here too, etc.
diff --git a/debian/freeside.docs b/debian/freeside.docs
new file mode 100644
index 0000000..e845566
--- /dev/null
+++ b/debian/freeside.docs
@@ -0,0 +1 @@
+README
diff --git a/debian/init.d.ex b/debian/init.d.ex
new file mode 100644
index 0000000..2480f51
--- /dev/null
+++ b/debian/init.d.ex
@@ -0,0 +1,157 @@
+#! /bin/sh
+#
+# skeleton example file to build /etc/init.d/ scripts.
+# This file should be used to construct scripts for /etc/init.d.
+#
+# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
+# Modified for Debian
+# by Ian Murdock <imurdock@gnu.ai.mit.edu>.
+# Further changes by Javier Fernandez-Sanguino <jfs@debian.org>
+#
+# Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/freeside
+NAME=freeside
+DESC=freeside
+
+test -x $DAEMON || exit 0
+
+LOGDIR=/var/log/freeside
+PIDFILE=/var/run/$NAME.pid
+DODTIME=1 # Time to wait for the server to die, in seconds
+ # If this value is set too low you might not
+ # let some servers to die gracefully and
+ # 'restart' will not work
+
+# Include freeside defaults if available
+if [ -f /etc/default/freeside ] ; then
+ . /etc/default/freeside
+fi
+
+set -e
+
+running_pid()
+{
+ # Check if a given process pid's cmdline matches a given name
+ pid=$1
+ name=$2
+ [ -z "$pid" ] && return 1
+ [ ! -d /proc/$pid ] && return 1
+ cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
+ # Is this the expected child?
+ [ "$cmd" != "$name" ] && return 1
+ return 0
+}
+
+running()
+{
+# Check if the process is running looking at /proc
+# (works for all users)
+
+ # No pidfile, probably no daemon present
+ [ ! -f "$PIDFILE" ] && return 1
+ # Obtain the pid and check it against the binary name
+ pid=`cat $PIDFILE`
+ running_pid $pid $NAME || return 1
+ return 0
+}
+
+force_stop() {
+# Forcefully kill the process
+ [ ! -f "$PIDFILE" ] && return
+ if running ; then
+ kill -15 $pid
+ # Is it really dead?
+ [ -n "$DODTIME" ] && sleep "$DODTIME"s
+ if running ; then
+ kill -9 $pid
+ [ -n "$DODTIME" ] && sleep "$DODTIME"s
+ if running ; then
+ echo "Cannot kill $LABEL (pid=$pid)!"
+ exit 1
+ fi
+ fi
+ fi
+ rm -f $PIDFILE
+ return 0
+}
+
+case "$1" in
+ start)
+ echo -n "Starting $DESC: "
+ start-stop-daemon --start --quiet --pidfile $PIDFILE \
+ --exec $DAEMON -- $DAEMON_OPTS
+ if running then
+ echo "$NAME."
+ else
+ echo " ERROR."
+ fi
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ start-stop-daemon --stop --quiet --pidfile $PIDFILE \
+ --exec $DAEMON
+ echo "$NAME."
+ ;;
+ force-stop)
+ echo -n "Forcefully stopping $DESC: "
+ force_stop
+ if ! running then
+ echo "$NAME."
+ else
+ echo " ERROR."
+ fi
+ ;;
+ #reload)
+ #
+ # If the daemon can reload its config files on the fly
+ # for example by sending it SIGHUP, do it here.
+ #
+ # If the daemon responds to changes in its config file
+ # directly anyway, make this a do-nothing entry.
+ #
+ # echo "Reloading $DESC configuration files."
+ # start-stop-daemon --stop --signal 1 --quiet --pidfile \
+ # /var/run/$NAME.pid --exec $DAEMON
+ #;;
+ force-reload)
+ #
+ # If the "reload" option is implemented, move the "force-reload"
+ # option to the "reload" entry above. If not, "force-reload" is
+ # just the same as "restart" except that it does nothing if the
+ # daemon isn't already running.
+ # check wether $DAEMON is running. If so, restart
+ start-stop-daemon --stop --test --quiet --pidfile \
+ /var/run/$NAME.pid --exec $DAEMON \
+ && $0 restart \
+ || exit 0
+ ;;
+ restart)
+ echo -n "Restarting $DESC: "
+ start-stop-daemon --stop --quiet --pidfile \
+ /var/run/$NAME.pid --exec $DAEMON
+ [ -n "$DODTIME" ] && sleep $DODTIME
+ start-stop-daemon --start --quiet --pidfile \
+ /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS
+ echo "$NAME."
+ ;;
+ status)
+ echo -n "$LABEL is "
+ if running ; then
+ echo "running"
+ else
+ echo " not running."
+ exit 1
+ fi
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $N {start|stop|restart|force-reload|status|force-stop}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/debian/init.d.lsb.ex b/debian/init.d.lsb.ex
new file mode 100644
index 0000000..1223129
--- /dev/null
+++ b/debian/init.d.lsb.ex
@@ -0,0 +1,281 @@
+#!/bin/sh
+#
+# Example init.d script with LSB support.
+#
+# Please read this init.d carefully and modify the sections to
+# adjust it to the program you want to run.
+#
+# Copyright (c) 2007 Javier Fernandez-Sanguino <jfs@debian.org>
+#
+# This is free software; you may redistribute it and/or modify
+# it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2,
+# or (at your option) any later version.
+#
+# This 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 with
+# the Debian operating system, in /usr/share/common-licenses/GPL; if
+# not, write to the Free Software Foundation, Inc., 59 Temple Place,
+# Suite 330, Boston, MA 02111-1307 USA
+#
+### BEGIN INIT INFO
+# Provides: freeside
+# Required-Start: $network $local_fs
+# Required-Stop:
+# Should-Start: $named
+# Should-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: <Enter a short description of the sortware>
+# Description: <Enter a long description of the software>
+# <...>
+# <...>
+### END INIT INFO
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+
+DAEMON=/usr/sbin/freeside # Introduce the server's location here
+NAME=#PACKAGE # Introduce the short server's name here
+DESC=#PACKAGE # Introduce a short description here
+LOGDIR=/var/log/freeside # Log directory to use
+
+PIDFILE=/var/run/$NAME.pid
+
+test -x $DAEMON || exit 0
+test -x $DAEMON_WRAPPER || exit 0
+
+. /lib/lsb/init-functions
+
+# Default options, these can be overriden by the information
+# at /etc/default/$NAME
+DAEMON_OPTS="" # Additional options given to the server
+
+DODTIME=10 # Time to wait for the server to die, in seconds
+ # If this value is set too low you might not
+ # let some servers to die gracefully and
+ # 'restart' will not work
+
+LOGFILE=$LOGDIR/$NAME.log # Server logfile
+#DAEMONUSER=freeside # Users to run the daemons as. If this value
+ # is set start-stop-daemon will chuid the server
+
+# Include defaults if available
+if [ -f /etc/default/$NAME ] ; then
+ . /etc/default/$NAME
+fi
+
+# Use this if you want the user to explicitly set 'RUN' in
+# /etc/default/
+#if [ "x$RUN" != "xyes" ] ; then
+# log_failure_msg "$NAME disabled, please adjust the configuration to your needs "
+# log_failure_msg "and then set RUN to 'yes' in /etc/default/$NAME to enable it."
+# exit 1
+#fi
+
+# Check that the user exists (if we set a user)
+# Does the user exist?
+if [ -n "$DAEMONUSER" ] ; then
+ if getent passwd | grep -q "^$DAEMONUSER:"; then
+ # Obtain the uid and gid
+ DAEMONUID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $3}'`
+ DAEMONGID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $4}'`
+ else
+ log_failure_msg "The user $DAEMONUSER, required to run $NAME does not exist."
+ exit 1
+ fi
+fi
+
+
+set -e
+
+running_pid() {
+# Check if a given process pid's cmdline matches a given name
+ pid=$1
+ name=$2
+ [ -z "$pid" ] && return 1
+ [ ! -d /proc/$pid ] && return 1
+ cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
+ # Is this the expected server
+ [ "$cmd" != "$name" ] && return 1
+ return 0
+}
+
+running() {
+# Check if the process is running looking at /proc
+# (works for all users)
+
+ # No pidfile, probably no daemon present
+ [ ! -f "$PIDFILE" ] && return 1
+ pid=`cat $PIDFILE`
+ running_pid $pid $DAEMON_WRAPPER || return 1
+ return 0
+}
+
+start_server() {
+# Start the process using the wrapper
+ if [ -z "$DAEMONUSER" ] ; then
+ start-stop-daemon --start --quiet --pidfile $PIDFILE \
+ --exec $DAEMON -- $DAEMON_OPTS
+ errcode=$?
+ else
+# if we are using a daemonuser then change the user id
+ start-stop-daemon --start --quiet --pidfile $PIDFILE \
+ --chuid $DAEMONUSER \
+ --exec $DAEMON -- $DAEMON_OPTS
+ errcode=$?
+ fi
+ return $errcode
+}
+
+stop_server() {
+# Stop the process using the wrapper
+ if [ -z "$DAEMONUSER" ] ; then
+ start-stop-daemon --stop --quiet --pidfile $PIDFILE \
+ --exec $DAEMON
+ errcode=$
+ else
+# if we are using a daemonuser then look for process that match
+ start-stop-daemon --stop --quiet --pidfile $PIDFILE \
+ --user $DAEMONUSER \
+ --exec $DAEMON
+ errcode=$
+ fi
+
+ return $errcode
+}
+
+reload_server() {
+ [ ! -f "$PIDFILE" ] && return 1
+ pid=`cat $PIDFILE` # This is the daemon's pid
+ # Send a SIGHUP
+ kill -1 $pid
+ return $?
+}
+
+force_stop() {
+# Force the process to die killing it manually
+ [ ! -e "$PIDFILE" ] && return
+ if running ; then
+ kill -15 $pid
+ # Is it really dead?
+ sleep "$DIETIME"s
+ if running ; then
+ kill -9 $pid
+ sleep "$DIETIME"s
+ if running ; then
+ echo "Cannot kill $NAME (pid=$pid)!"
+ exit 1
+ fi
+ fi
+ fi
+ rm -f $PIDFILE
+}
+
+
+case "$1" in
+ start)
+ log_daemon_msg "Starting $DESC " "$NAME"
+ # Check if it's running first
+ if running ; then
+ log_progress_msg "apparently already running"
+ log_end_msg 0
+ exit 0
+ fi
+ if start_server && running ; then
+ # It's ok, the server started and is running
+ log_end_msg 0
+ else
+ # Either we could not start it or it is not running
+ # after we did
+ # NOTE: Some servers might die some time after they start,
+ # this code does not try to detect this and might give
+ # a false positive (use 'status' for that)
+ log_end_msg 1
+ fi
+ ;;
+ stop)
+ log_daemon_msg "Stopping $DESC" "$NAME"
+ if running ; then
+ # Only stop the server if we see it running
+ stop_server
+ log_end_msg $?
+ else
+ # If it's not running don't do anything
+ log_progress_msg "apparently not running"
+ log_end_msg 0
+ exit 0
+ fi
+ ;;
+ force-stop)
+ # First try to stop gracefully the program
+ $0 stop
+ if running; then
+ # If it's still running try to kill it more forcefully
+ log_daemon_msg "Stopping (force) $DESC" "$NAME"
+ force_stop
+ log_end_msg $?
+ fi
+ ;;
+ restart|force-reload)
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ stop_server
+ # Wait some sensible amount, some server need this
+ [ -n "$DIETIME" ] && sleep $DIETIME
+ start_server
+ running
+ log_end_msg $?
+ ;;
+ status)
+
+ log_daemon_msg "Checking status of $DESC" "$NAME"
+ if running ; then
+ log_progress_msg "running"
+ log_end_msg 0
+ else
+ log_progress_msg "apparently not running"
+ log_end_msg 1
+ exit 1
+ fi
+ ;;
+ # Use this if the daemon cannot reload
+ reload)
+ log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon"
+ log_warning_msg "cannot re-read the config file (use restart)."
+ ;;
+ # And this if it cann
+ #reload)
+ #
+ # If the daemon can reload its config files on the fly
+ # for example by sending it SIGHUP, do it here.
+ #
+ # If the daemon responds to changes in its config file
+ # directly anyway, make this a do-nothing entry.
+ #
+ # log_daemon_msg "Reloading $DESC configuration files" "$NAME"
+ # if running ; then
+ # reload_server
+ # if ! running ; then
+ # Process died after we tried to reload
+ # log_progress_msg "died on reload"
+ # log_end_msg 1
+ # exit 1
+ # fi
+ # else
+ # log_progress_msg "server is not running"
+ # log_end_msg 1
+ # exit 1
+ # fi
+ #;;
+
+ *)
+ N=/etc/init.d/$NAME
+ echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/debian/postinst b/debian/postinst
new file mode 100644
index 0000000..5d04550
--- /dev/null
+++ b/debian/postinst
@@ -0,0 +1,54 @@
+#!/bin/sh
+# postinst script for freeside
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# source debconf stuff
+. /usr/share/debconf/confmodule
+
+# source dbconfig-common stuff
+. /usr/share/dbconfig-common/dpkg/postinst
+
+dbc_pgsql_createdb_encoding='sql_ascii'
+
+#echo "i should create the db here"
+dbc_go freeside $@
+#echo "db should be craeted now"
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ configure)
+
+ a2enmod perl
+
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
diff --git a/debian/postrm b/debian/postrm
new file mode 100644
index 0000000..c008445
--- /dev/null
+++ b/debian/postrm
@@ -0,0 +1,48 @@
+#!/bin/sh
+# postrm script for freeside
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# source debconf stuff
+. /usr/share/debconf/confmodule
+
+# source dbconfig-common stuff
+if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then
+ . /usr/share/dbconfig-common/dpkg/postrm
+ dbc_go freeside $@
+fi
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/preinst b/debian/preinst
new file mode 100644
index 0000000..50c89e1
--- /dev/null
+++ b/debian/preinst
@@ -0,0 +1,100 @@
+#!/bin/sh
+# preinst script for freeside
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade)
+
+ # If the package has default file it could be sourced, so that
+ # the local admin can overwrite the defaults
+
+ [ -f "/etc/default/freeside" ] && . /etc/default/freeside
+
+ # Sane defaults:
+
+ [ -z "$FREESIDE_HOME" ] && FREESIDE_HOME=/home/freeside
+ [ -z "$FREESIDE_USER" ] && FREESIDE_USER=freeside
+ [ -z "$FREESIDE_NAME" ] && FREESIDE_NAME="Freeside"
+ [ -z "$FREESIDE_GROUP" ] && FREESIDE_GROUP=freeside
+
+ [ -z "$RT_GROUP" ] && RT_GROUP=rt
+
+ # Groups that the user will be added to, if undefined, then none.
+ ADDGROUP="rt"
+
+ # create user to avoid running server as root
+ # 1. create group if not existing
+ if ! getent group | grep -q "^$FREESIDE_GROUP:" -; then
+ echo -n "Adding group $FREESIDE_GROUP.."
+ addgroup --quiet --system $FREESIDE_GROUP 2>/dev/null ||true
+ echo "..done"
+ fi
+ if ! getent group | grep -q "^$RT_GROUP:" -; then
+ echo -n "Adding group $RT_GROUP.."
+ addgroup --quiet --system $RT_GROUP 2>/dev/null ||true
+ echo "..done"
+ fi
+ # 2. create homedir if not existing
+ test -d $FREESIDE_HOME || mkdir $FREESIDE_HOME
+ # 3. create user if not existing
+ if ! getent passwd | grep -q "^$FREESIDE_USER:" -; then
+ echo -n "Adding system user $FREESIDE_USER.."
+ adduser --quiet \
+ --system \
+ --ingroup $FREESIDE_GROUP \
+ --shell /bin/sh \
+ --no-create-home \
+ --disabled-password \
+ $FREESIDE_USER 2>/dev/null || true
+ echo "..done"
+ fi
+ # 4. adjust passwd entry
+ usermod -c "$FREESIDE_NAME" \
+ -d $FREESIDE_HOME \
+ -g $FREESIDE_GROUP \
+ $FREESIDE_USER
+ # 5. adjust file and directory permissions
+ if ! dpkg-statoverride --list $FREESIDE_HOME >/dev/null
+ then
+ chown -R $FREESIDE_USER:adm $FREESIDE_HOME
+ chmod u=rwx,g=rxs,o= $FREESIDE_HOME
+ fi
+ # 6. Add the user to the ADDGROUP group
+ if test -n $ADDGROUP
+ then
+ if ! groups $FREESIDE_USER | cut -d: -f2 | \
+ grep -qw $ADDGROUP -; then
+ adduser $FREESIDE_USER $ADDGROUP
+ fi
+ fi
+ ;;
+
+ abort-upgrade)
+ ;;
+
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/prerm b/debian/prerm
new file mode 100644
index 0000000..4c17489
--- /dev/null
+++ b/debian/prerm
@@ -0,0 +1,46 @@
+#!/bin/sh
+# prerm script for freeside
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# source debconf stuff
+. /usr/share/debconf/confmodule
+# source dbconfig-common stuff
+. /usr/share/dbconfig-common/dpkg/prerm
+dbc_go freeside $@
+
+# summary of how this script can be called:
+# * <prerm> `remove'
+# * <old-prerm> `upgrade' <new-version>
+# * <new-prerm> `failed-upgrade' <old-version>
+# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+# * <deconfigured's-prerm> `deconfigure' `in-favour'
+# <package-being-installed> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ remove|upgrade|deconfigure)
+ ;;
+
+ failed-upgrade)
+ ;;
+
+ *)
+ echo "prerm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..d37dfd1
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,230 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# If set to a true value then MakeMaker's prompt function will
+# always return the default without waiting for user input.
+#export PERL_MM_USE_DEFAULT=1
+
+PERL ?= /usr/bin/perl
+#PACKAGE = $(shell dh_listpackages)
+PACKAGE = freeside
+TMP = $(CURDIR)/debian/$(PACKAGE)
+DBC_SCRIPTS = $(TMP)/usr/share/dbconfig-common/scripts/freeside
+
+#this is gotten from dbconfig-common
+DB_TYPE = db_type_is_configured_during_pkg_install_by_dbconfig-common_not_at_build_time
+
+#no chance, it doesn't get backslash-interpolted now...
+#DEBVERSION = `head -1 debian/changelog | cut -d')' -f1 | cut -c11-`
+DEBVERSION = 1.7.3~rc2-1
+export VERSION = $(DEBVERSION) (Debian)
+
+export FREESIDE_CONF = /etc/freeside
+export FREESIDE_LOG = /var/log/freeside
+export FREESIDE_LOCK = /var/lock/freeside
+export FREESIDE_CACHE = $(TMP)/var/cache/freeside
+FREESIDE_CACHE = $(TMP)/var/cache/freeside
+
+#XXX huh?
+export FREESIDE_EXPORT = /var/spool/freeside
+
+#XXX own subdir?
+export MASON_HANDLER = $(TMP)-webui/usr/share/freeside/handler.pl
+
+export APACHE_VERSION = 2
+export FREESIDE_DOCUMENT_ROOT = $(TMP)-webui/usr/share/freeside/www
+export INIT_FILE = $(TMP).init
+export INIT_INSTALL = /bin/true
+export HTTPD_RESTART = /bin/true
+#export APACHE_CONF = $(TMP)-webui/etc/apache2/conf.d
+export APACHE_CONF = $(TMP)-webui/etc/freeside/apache2
+export FREESIDE_RESTART = /bin/true
+
+#XXX root?
+export INSTALLGROUP = adm
+
+export SELFSERVICE_MACHINES =
+
+#prompt ? XXX these are runtime, not buildtime :/
+export RT_DOMAIN = `dnsdomainname`
+export RT_TIMEZONE = `cat /etc/timezone`
+
+export HOSTNAME = `hostname -f`
+export FREESIDE_URL = http://$(HOSTNAME)/freeside/
+
+#specific to deb pkg, for purposes of saving off a permanent copy of default
+#config for postinst and that sort of thing
+export DIST_CONF = $(TMP)/usr/share/freeside/default_conf
+
+#XXX yuck. proper RT layout is entirely necessary
+#this seems to infect way to much of RT with the build location, requiring
+# a kludge to hack it out afterwords. look into using fakeroot (didn't
+# realize it would need to be explicit argh)
+# (but leaving it for now, otherwise can't get RT to put files where we need em)
+export RT_PATH = $(TMP)/var/opt/freeside/rt
+
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+configure: configure-stamp
+configure-stamp:
+ dh_testdir
+ # Add here commands to configure the package.
+
+ touch configure-stamp
+
+
+build: build-stamp
+build-stamp:
+ dh_testdir
+ # Add commands to compile the package here
+
+ ( cd FS/ && $(PERL) Makefile.PL INSTALLDIRS=vendor )
+
+ $(MAKE) -e perl-modules
+
+ #TEST#
+
+ touch $@
+
+clean:
+ dh_testdir
+ dh_testroot
+ dh_clean build-stamp install-stamp
+
+ # Add here commands to clean up after the build process.
+ $(MAKE) -e clean
+ #|| true #XXX freeside clean target fucked
+
+ dh_clean
+
+install: install-stamp
+install-stamp: build-stamp
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ # Add here commands to install package into
+ # debian/<package>-whatever.
+ ( cd FS/ && $(MAKE) -e DESTDIR=$(TMP)-lib install )
+
+ #false laziness w/install-perl-modules now
+ #install this for postinst later (no create-config)
+ install -d $(DIST_CONF)
+ #install conf/[a-z]* $(DEFAULT_CONF)
+ #CVS is not [a-z]
+ install `ls -d conf/[a-z]* | grep -v CVS` $(DIST_CONF)
+
+ install -d $(FREESIDE_DOCUMENT_ROOT)
+ install -d $(FREESIDE_CACHE)/masondata #MASONDATA
+ $(MAKE) -e install-docs
+
+ #hack the build dir out of Freeside too. oh yeah, sucky.
+ perl -p -i -e "\
+ s'${TMP}(-webui)?''g;\
+ " ${TMP}-webui/usr/share/freeside/handler.pl \
+ ${TMP}/usr/share/perl5/FS/* \
+ ${TMP}/usr/share/perl5/FS/*/* \
+ ${TMP}/usr/bin/*
+
+ rm -r $(FREESIDE_DOCUMENT_ROOT).*
+
+ install -d $(APACHE_CONF)
+ install debian/freeside.apache-alias.conf $(APACHE_CONF)/freeside-alias.conf
+ FREESIDE_DOCUMENT_ROOT=/usr/share/freeside/www MASON_HANDLER=/usr/share/freeside/handler.pl FREESIDE_CONF=/etc/freeside $(MAKE) -e install-apache
+
+ $(MAKE) -e install-init
+
+ #RT
+ #(configure-rt)
+
+ # XXX need to adjust db-type, db-database, db-rt-user, db-rt-pass
+ # based on info from dbc
+ ( cd rt; \
+ cp config.layout.in config.layout; \
+ perl -p -i -e "\
+ s'%%%FREESIDE_DOCUMENT_ROOT%%%'${FREESIDE_DOCUMENT_ROOT}'g;\
+ s'%%%MASONDATA%%%'${FREESIDE_CACHE}/masondata'g;\
+ " config.layout; \
+ ./configure --prefix=${RT_PATH} \
+ --enable-layout=Freeside \
+ --with-db-type=Pg \
+ --with-db-dba=freeside \
+ --with-db-database=_DBC_DBNAME_ \
+ --with-db-rt-user=_DBC_DBUSER_ \
+ --with-db-rt-pass=_DBC_DBPASS_ \
+ --with-web-user=freeside \
+ --with-web-group=freeside \
+ --with-rt-group=freeside \
+ )
+
+ #(create-rt)
+ install -d $(RT_PATH)
+ ( cd rt; make install )
+ #hack the build dir out of RT. yeah, sucky.
+ perl -p -i -e "\
+ s'${TMP}''g;\
+ " ${RT_PATH}/etc/RT_Config.pm \
+ ${RT_PATH}/lib/RT.pm \
+ ${RT_PATH}/bin/mason_handler.fcgi \
+ ${RT_PATH}/bin/mason_handler.scgi \
+ ${RT_PATH}/bin/standalone_httpd \
+ ${RT_PATH}/bin/webmux.pl \
+ ${RT_PATH}/bin/rt-crontool \
+ ${RT_PATH}/sbin/rt-dump-database \
+ ${RT_PATH}/sbin/rt-setup-database
+
+ #hack @INC dir out of RT (well, handler.pl) too.
+ perl -p -i -e "\
+ s'/opt/rt3/'/var/opt/freeside/rt/'g;\
+ " ${TMP}-webui/usr/share/freeside/handler.pl
+
+ mv ${RT_PATH}/etc/RT_Config.pm ${RT_PATH}/etc/RT_Config.pm.dbc
+
+ perl -p -i -e "\
+ s'%%%RT_DOMAIN%%%'${RT_DOMAIN}'g;\
+ s'%%%RT_TIMEZONE%%%'${RT_TIMEZONE}'g;\
+ s'%%%FREESIDE_URL%%%'${FREESIDE_URL}'g;\
+ " ${RT_PATH}/etc/RT_SiteConfig.pm
+
+ install -D debian/dbconfig-common.install $(DBC_SCRIPTS)/install/pgsql
+ install -D debian/dbconfig-common.install $(DBC_SCRIPTS)/install/mysql
+
+ install -D debian/dbconfig-common.upgrade $(DBC_SCRIPTS)/upgrade/pgsql/$(DEBVERSION)
+ install -D debian/dbconfig-common.upgrade $(DBC_SCRIPTS)/upgrade/mysql/$(DEBVERSION)
+
+ dh_install
+
+ touch $@
+
+binary-arch:
+# We have nothing to do here for an architecture-independent package
+
+binary-indep: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs ChangeLog
+ dh_installdocs #freeside.docs README AGPL
+ dh_installexamples eg/*
+# dh_installmenu
+ dh_installdebconf
+# dh_installlogrotate
+ dh_installinit
+ dh_installcron
+# dh_installinfo
+ dh_installman
+ dh_perl
+ dh_link
+ dh_compress
+ dh_fixperms
+ dh_installdeb
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/debian/templates b/debian/templates
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/debian/templates
diff --git a/eg/TEMPLATE_cust_main.import b/eg/TEMPLATE_cust_main.import
index 39a5785..f6d88c7 100755
--- a/eg/TEMPLATE_cust_main.import
+++ b/eg/TEMPLATE_cust_main.import
@@ -1,17 +1,19 @@
#!/usr/bin/perl -w
-
-# Template for importing legacy customer data
#
-# ivan@sisd.com 98-aug-17 - 20
+# Template for importing legacy customer data
use strict;
+use Date::Parse;
use FS::UID qw(adminsuidsetup datasrc);
use FS::Record qw(fields qsearch qsearchs);
use FS::cust_main;
use FS::cust_pkg;
-use Date::Parse;
+use FS::cust_svc;
+use FS::svc_acct;
+use FS::pkg_svc;
-adminsuidsetup;
+my $user = shift or die &usage;
+adminsuidsetup $user;
# use these for the imported cust_main records (unless you have these in legacy
# data)
@@ -90,7 +92,7 @@ while (<CLIENT>) {
$svc{'First'} =~ s/&/and/go;
$svc{'Zip'} =~ s/\s+$//go;
- my($cust_main) = create FS::cust_main ( {
+ my($cust_main) = new FS::cust_main ( {
'custnum' => $svc{'custnum'},
'agentnum' => $agentnum,
'last' => $svc{'last'},
@@ -121,7 +123,7 @@ while (<CLIENT>) {
die $error;
}
- my($cust_pkg)=create FS::cust_pkg ( {
+ my($cust_pkg)=new FS::cust_pkg ( {
'custnum' => $svc{'custnum'},
'pkgpart' => $pkgpart{$svc{'LegacyBillingData'}},
'setup' => '',
@@ -168,7 +170,7 @@ while (<CLIENT>) {
} else {
#create new cust_svc record linked to cust_pkg record
- my($n_cust_svc) = create FS::cust_svc ({
+ my($n_cust_svc) = new FS::cust_svc ({
'svcnum' => $o_cust_svc->svcnum,
'pkgnum' => $cust_pkg->pkgnum,
'svcpart' => $pkg_svc->svcpart,
@@ -187,3 +189,8 @@ while (<CLIENT>) {
warn "\n$link of $line lines linked\n";
+# ---
+
+sub usage {
+ die "Usage:\n\n cust_main.import user\n";
+}
diff --git a/eg/cdr_template.pm b/eg/cdr_template.pm
new file mode 100644
index 0000000..5499d22
--- /dev/null
+++ b/eg/cdr_template.pm
@@ -0,0 +1,99 @@
+package FS::cdr::cdr_template;
+
+use strict;
+use base qw( FS::cdr );
+use vars qw( %info );
+use FS::cdr qw( _cdr_date_parser_maker _cdr_min_parser_maker );
+
+%info = (
+ 'name' => 'Example CDR format',
+ 'weight' => 500,
+ 'header' => 0, #0 default, set to 1 to ignore the first line, or
+ # to higher numbers to ignore that number of lines
+ 'type' => 'csv', #csv (default), fixedlength or xls
+ 'sep_char' => ',', #for csv, defaults to ,
+ 'disabled' => 0, #0 default, set to 1 to disable
+
+ #listref of what to do with each field from the CDR, in order
+ 'import_fields' => [
+
+ #place data directly in the specified field
+ 'freeside_cdr_fieldname',
+
+ #subroutine reference
+ sub { my($cdr, $field_data) = @_;
+ #do something to $field_data
+ $cdr->fieldname($field_data);
+ },
+
+ #premade subref factory for date+time parsing, understands dates like:
+ # 10/31/2007 08:57:24
+ # 2007-10-31 08:57:24.113000000
+ _cdr_date_parser_maker('startddate'), #for example
+
+ #premade subref factory for decimal minute parsing
+ _cdr_min_parser_maker, #defaults to billsec and duration
+ _cdr_min_parser_maker('fieldname'), #one field
+ _cdr_min_parser_maker(['billsec', 'duration']), #listref for multiple fields
+
+ ],
+
+ #Parse::FixedLength field descriptions & lengths, for type=>'fixedlength' only
+ 'fixedlength_format' => [qw(
+ Type:2:1:2
+ Sequence:4:3:6
+ )],
+
+);
+
+1;
+
+__END__
+
+list of freeside CDR fields, useful ones marked with *
+
+ acctid - primary key
+*[1] calldate - Call timestamp (SQL timestamp)
+ clid - Caller*ID with text
+* src - Caller*ID number / Source number
+* dst - Destination extension
+ dcontext - Destination context
+ channel - Channel used
+ dstchannel - Destination channel if appropriate
+ lastapp - Last application if appropriate
+ lastdata - Last application data
+* startdate - Start of call (UNIX-style integer timestamp)
+ answerdate - Answer time of call (UNIX-style integer timestamp)
+* enddate - End time of call (UNIX-style integer timestamp)
+* duration - Total time in system, in seconds
+* billsec - Total time call is up, in seconds
+*[2] disposition - What happened to the call: ANSWERED, NO ANSWER, BUSY
+ amaflags - What flags to use: BILL, IGNORE etc, specified on a per
+ channel basis like accountcode.
+*[3] accountcode - CDR account number to use: account
+ uniqueid - Unique channel identifier (Unitel/RSLCOM Event ID)
+ userfield - CDR user-defined field
+ cdr_type - CDR type - see FS::cdr_type (Usage = 1, S&E = 7, OC&C = 8)
+*[4] charged_party - Service number to be billed
+ upstream_currency - Wholesale currency from upstream
+*[5] upstream_price - Wholesale price from upstream
+ upstream_rateplanid - Upstream rate plan ID
+ rated_price - Rated (or re-rated) price
+ distance - km (need units field?)
+ islocal - Local - 1, Non Local = 0
+*[6] calltypenum - Type of call - see FS::cdr_calltype
+ description - Description (cdr_type 7&8 only) (used for
+ cust_bill_pkg.itemdesc)
+ quantity - Number of items (cdr_type 7&8 only)
+ carrierid - Upstream Carrier ID (see FS::cdr_carrier)
+ upstream_rateid - Upstream Rate ID
+ svcnum - Link to customer service (see FS::cust_svc)
+ freesidestatus - NULL, done (or something)
+
+[1] Auto-populated from startdate if not present
+[2] Package options available to ignore calls without a specific disposition
+[3] When using 'cdr-charged_party-accountcode' config
+[4] Auto-populated from src (normal calls) or dst (toll free calls) if not present
+[5] When using 'upstream_simple' rating method.
+[6] Set to usage class classnum when using pre-rated CDRs and usage class-based
+ taxation (local/intrastate/interstate/international)
diff --git a/eg/export_template.pm b/eg/export_template.pm
new file mode 100644
index 0000000..22eb36a
--- /dev/null
+++ b/eg/export_template.pm
@@ -0,0 +1,113 @@
+package FS::part_export::myexport;
+
+use vars qw(@ISA %info);
+use Tie::IxHash;
+use FS::part_export;
+
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
+ 'regular_option' => { label => 'Option description', default => 'value' },
+ 'select_option' => { label => 'Select option description',
+ type => 'select', options=>[qw(chocolate vanilla)],
+ default => 'vanilla',
+ },
+ 'textarea_option' => { label => 'Textarea option description',
+ type => 'textarea',
+ default => 'Default text.',
+ },
+ 'checkbox_option' => { label => 'Checkbox label', type => 'checkbox' },
+;
+
+%info = (
+ 'svc' => 'svc_acct',
+ #'svc' => [qw( svc_acct svc_forward )],
+ 'desc' =>
+ 'Export short description',
+ 'options' => \%options,
+ 'nodomain' => 'Y',
+ 'notes' => <<'END'
+HTML notes about this export.
+END
+
+sub rebless { shift; }
+
+sub _export_insert {
+ my($self, $svc_something) = (shift, shift);
+ $err_or_queue = $self->myexport_queue( $svc_something->svcnum, 'insert',
+ $svc_something->username, $svc_something->_password );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ #return "can't change username with myexport"
+ # if $old->username ne $new->username;
+ #return '' unless $old->_password ne $new->_password;
+ $err_or_queue = $self->myexport_queue( $new->svcnum,
+ 'replace', $new->username, $new->_password );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_delete {
+ my( $self, $svc_something ) = (shift, shift);
+ $err_or_queue = $self->myexport_queue( $svc_something->svcnum,
+ 'delete', $svc_something->username );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+#these three are optional
+# fallback for svc_acct will change and restore password
+sub _export_suspend {
+ my( $self, $svc_something ) = (shift, shift);
+ $err_or_queue = $self->myexport_queue( $svc_something->svcnum,
+ 'suspend', $svc_something->username );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_something ) = (shift, shift);
+ $err_or_queue = $self->myexport_queue( $svc_something->svcnum,
+ 'unsuspend', $svc_something->username );
+ ref($err_or_queue) ? '' : $err_or_queue;
+}
+
+sub export_links {
+ my($self, $svc_something, $arrayref) = (shift, shift, shift);
+ #push @$arrayref, qq!<A HREF="http://example.com/~!. $svc_something->username.
+ # qq!">!. $svc_something->username. qq!</A>!;
+ '';
+}
+
+###
+
+#a good idea to queue anything that could fail or take any time
+sub myexport_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::myexport::myexport_$method",
+ };
+ $queue->insert( @_ ) or $queue;
+}
+
+sub myexport_insert { #subroutine, not method
+ my( $username, $password ) = @_;
+ #do things with $username and $password
+}
+
+sub myexport_replace { #subroutine, not method
+}
+
+sub myexport_delete { #subroutine, not method
+ my( $username ) = @_;
+ #do things with $username
+}
+
+sub myexport_suspend { #subroutine, not method
+}
+
+sub myexport_unsuspend { #subroutine, not method
+}
+
+
diff --git a/eg/part_event-Action-template.pm b/eg/part_event-Action-template.pm
new file mode 100644
index 0000000..c2f5ba5
--- /dev/null
+++ b/eg/part_event-Action-template.pm
@@ -0,0 +1,55 @@
+package FS::part_event::Action::myaction;
+
+use strict;
+
+use base qw( FS::part_event::Action );
+
+# see the FS::part_event::Action manpage for full documentation on each
+# of the required and optional methods.
+
+sub description {
+ 'New action (the author forgot to change this description)';
+}
+
+#sub eventtable_hashref {
+# { 'cust_main' => 1,
+# 'cust_bill' => 1,
+# 'cust_pkg' => 1,
+# };
+#}
+
+#sub option_fields {
+# (
+# 'field' => 'description',
+#
+# 'another_field' => { 'label'=>'Amount', 'type'=>'money', },
+#
+# 'third_field' => { 'label' => 'Types',
+# 'type' => 'select',
+# 'options' => [ 'h', 's' ],
+# 'option_labels' => { 'h' => 'Happy',
+# 's' => 'Sad',
+# },
+# );
+#}
+
+#sub default_weight {
+# 100;
+#}
+
+
+sub do_action {
+ my( $self, $object ) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $value_of_field = $self->option('field');
+
+ #do your action
+
+ #die "Error: $error";
+ return 'Null example action completed sucessfully.';
+
+}
+
+1;
diff --git a/eg/part_event-Condition-template.pm b/eg/part_event-Condition-template.pm
new file mode 100644
index 0000000..cc05843
--- /dev/null
+++ b/eg/part_event-Condition-template.pm
@@ -0,0 +1,57 @@
+package FS::part_event::Condition::mycondition;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+
+# see the FS::part_event::Condition manpage for full documentation on each
+# of the required and optional methods.
+
+sub description {
+ 'New condition (the author forgot to change this description)';
+}
+
+#sub eventtable_hashref {
+# { 'cust_main' => 1,
+# 'cust_bill' => 1,
+# 'cust_pkg' => 1,
+# 'cust_pay_batch' => 1,
+# };
+#}
+
+#sub option_fields {
+# (
+# 'field' => 'description',
+#
+# 'another_field' => { 'label'=>'Amount', 'type'=>'money', },
+#
+# 'third_field' => { 'label' => 'Types',
+# 'type' => 'checkbox-multiple',
+# 'options' => [ 'h', 's' ],
+# 'option_labels' => { 'h' => 'Happy',
+# 's' => 'Sad',
+# },
+# );
+#}
+
+sub condition {
+ my($self, $object, %opt) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $value_of_field = $self->option('field');
+
+ my $time = $opt{'time'}; #use this instead of time or $^T
+
+ #test your condition
+ 1;
+
+}
+
+#sub condition_sql {
+# my( $class, $table ) = @_;
+# #...
+# 'true';
+#}
+
+1;
diff --git a/eg/table_template-svc.pm b/eg/table_template-svc.pm
new file mode 100644
index 0000000..7e10275
--- /dev/null
+++ b/eg/table_template-svc.pm
@@ -0,0 +1,212 @@
+package FS::svc_table;
+
+use strict;
+use base qw( FS::svc_Common );
+#use FS::Record qw( qsearch qsearchs );
+use FS::cust_svc;
+
+=head1 NAME
+
+FS::table_name - Object methods for table_name records
+
+=head1 SYNOPSIS
+
+ use FS::table_name;
+
+ $record = new FS::table_name \%hash;
+ $record = new FS::table_name { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 DESCRIPTION
+
+An FS::table_name object represents an example. FS::table_name inherits from
+FS::svc_Common. The following fields are currently supported:
+
+=over 4
+
+=item field - description
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'table_name'; }
+
+sub table_info {
+ {
+ 'name' => 'Example',
+ 'name_plural' => 'Example services', #optional,
+ 'longname_plural' => 'Example services', #optional
+ 'sorts' => 'svcnum', # optional sort field (or arrayref of sort fields, main first)
+ 'display_weight' => 100,
+ 'cancel_weight' => 100,
+ 'fields' => {
+ 'field' => 'Description',
+ 'another_field' => {
+ 'label' => 'Description',
+ 'def_label' => 'Description for service definitions',
+ 'type' => 'text',
+ 'disable_default' => 1, #disable switches
+ 'disable_fixed' => 1, #
+ 'disable_inventory' => 1, #
+ },
+ 'foreign_key' => {
+ 'label' => 'Description',
+ 'def_label' => 'Description for service defs',
+ 'type' => 'select',
+ 'select_table' => 'foreign_table',
+ 'select_key' => 'key_field_in_table',
+ 'select_label' => 'label_field_in_table',
+ },
+
+ },
+ };
+}
+
+=item search_sql STRING
+
+Class method which returns an SQL fragment to search for the given string.
+
+=cut
+
+#or something more complicated if necessary
+sub search_sql {
+ my($class, $string) = @_;
+ $class->search_sql_field('search_field', $string);
+}
+
+=item label
+
+Returns a meaningful identifier for this example
+
+=cut
+
+sub label {
+ my $self = shift;
+ $self->label_field; #or something more complicated if necessary
+}
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my $error;
+
+ $error = $self->SUPER::insert;
+ return $error if $error;
+
+ '';
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ my $self = shift;
+ my $error;
+
+ $error = $self->SUPER::delete;
+ return $error if $error;
+
+ '';
+}
+
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my ( $new, $old ) = ( shift, shift );
+ my $error;
+
+ $error = $new->SUPER::replace($old);
+ return $error if $error;
+
+ '';
+}
+
+=item suspend
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and repalce methods.
+
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $x = $self->setfixed;
+ return $x unless ref($x);
+ my $part_svc = $x;
+
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
+L<FS::cust_pkg>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/eg/table_template.pm b/eg/table_template.pm
new file mode 100644
index 0000000..9c71b3a
--- /dev/null
+++ b/eg/table_template.pm
@@ -0,0 +1,116 @@
+package FS::table_name;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::table_name - Object methods for table_name records
+
+=head1 SYNOPSIS
+
+ use FS::table_name;
+
+ $record = new FS::table_name \%hash;
+ $record = new FS::table_name { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::table_name object represents an example. FS::table_name inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item field - description
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new example. To add the example to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'table_name'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('primary_key')
+ || $self->ut_number('validate_other_fields')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+The author forgot to customize this manpage.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/eg/xmlrpc-example.pl b/eg/xmlrpc-example.pl
new file mode 100755
index 0000000..7a2a0a6
--- /dev/null
+++ b/eg/xmlrpc-example.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $server = new Frontier::Client (
+ url => 'http://user:pass@freesidehost/misc/xmlrpc.cgi',
+);
+
+#my $method = 'cust_main.smart_search';
+#my @args = (search => '1');
+
+my $method = 'Record.qsearch';
+my @args = (cust_main => { });
+
+my $result = $server->call($method, @args);
+
+if (ref($result) eq 'ARRAY') {
+ print "Result:\n";
+ print Dumper(@$result);
+}
+
diff --git a/etc/abbr_state.txt b/etc/abbr_state.txt
new file mode 100644
index 0000000..7e4f57f
--- /dev/null
+++ b/etc/abbr_state.txt
@@ -0,0 +1,72 @@
+State/Possession Abbreviation
+
+ALABAMA AL
+ALASKA AK
+AMERICAN SAMOA AS
+ARIZONA AZ
+ARKANSAS AR
+CALIFORNIA CA
+COLORADO CO
+CONNECTICUT CT
+DELAWARE DE
+DISTRICT OF COLUMBIA DC
+FEDERATED STATES OF MICRONESIA FM
+FLORIDA FL
+GEORGIA GA
+GUAM GU
+HAWAII HI
+IDAHO ID
+ILLINOIS IL
+INDIANA IN
+IOWA IA
+KANSAS KS
+KENTUCKY KY
+LOUISIANA LA
+MAINE ME
+MARSHALL ISLANDS MH
+MARYLAND MD
+MASSACHUSETTS MA
+MICHIGAN MI
+MINNESOTA MN
+MISSISSIPPI MS
+MISSOURI MO
+MONTANA MT
+NEBRASKA NE
+NEVADA NV
+NEW HAMPSHIRE NH
+NEW JERSEY NJ
+NEW MEXICO NM
+NEW YORK NY
+NORTH CAROLINA NC
+NORTH DAKOTA ND
+NORTHERN MARIANA ISLANDS MP
+OHIO OH
+OKLAHOMA OK
+OREGON OR
+PALAU PW
+PENNSYLVANIA PA
+PUERTO RICO PR
+RHODE ISLAND RI
+SOUTH CAROLINA SC
+SOUTH DAKOTA SD
+TENNESSEE TN
+TEXAS TX
+UTAH UT
+VERMONT VT
+VIRGIN ISLANDS VI
+VIRGINIA VA
+WASHINGTON WA
+WEST VIRGINIA WV
+WISCONSIN WI
+WYOMING WY
+
+
+Military "State" Abbreviation
+
+Armed Forces Africa AE
+Armed Forces Americas AA
+(except Canada)
+Armed Forces Canada AE
+Armed Forces Europe AE
+Armed Forces Middle East AE
+Armed Forces Pacific AP
diff --git a/etc/acp_logfile-parse b/etc/acp_logfile-parse
deleted file mode 100755
index 5e25899..0000000
--- a/etc/acp_logfile-parse
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/usr/bin/perl
-
-###
-# WHO WROTE THIS???
-###
-
-#require "perldb.pl";
-
-# Compute SLIP/PPP log times
-# Arguments -a Process entire file with totals
-# -t Process only totals
-# -f File to be processed if not current
-# -d processing start date (default is entire file)
-# -l to return all totals for dayuse
-# -w name of tmp work file for dayuse
-# user names
-
-require "time.pl";
-
-$space=' ';
-
-unless (@ARGV[0]) {
- print "Missing Arguments\n";
- print "-a - entire file\n";
- print "-t - totals only\n";
- print "-f - file name to be processed\n";
- print "-d - processing start date (yymmdd)\n";
- print "-l - return totals for dayuse\n";
- print "-w - tmp work file for dayuse\n";
- exit;
-} # end if test for missing arguments
-
-$infile = "/usr/annex/acp_logfile";
-$tmpfile = "/tmp/ppp";
-$n = $#ARGV;
-$start_yymmdd = "";
-for ($i = 0; $i <= $n; $i++) {
- if ($ARGV[$i] eq "-a") {
- $allflag = "true";
- }
- elsif ($ARGV[$i] eq "-t") {
- $totalflag = "true";
- }
- elsif ($ARGV[$i] eq "-f") {
- $i++;
- $infile = $ARGV[$i];
- }
- elsif ($ARGV[$i] eq "-d") {
- $i++;
- $start_yymmdd = $ARGV[$i];
- } #end start yymmdd
- elsif ($ARGV[$i] eq "-l") {
- $logflag = "true";
- $totalflag = "true";
- } # end log
- elsif ($ARGV[$i] eq "-w") {
- $i++;
- $tmpfile = $ARGV[$i];
- } # end tmp file
- else {
- ($arg_user,$arg_yymmdd) = split (/:/, $ARGV[$i]);
- $ip_user_date {$arg_user} = $ARGV[$i];
- $userflag = "true";
- } # end else
- } # end for 1 = 1 to n
-
-open (IN,$infile)
- || die "Can't open acp_logfile";
-
-NEXTUSER: while (<IN>) {
- chop;
- ($add,$ether,$port,$date,$time,$type,$action,$user) = split(/:/);
-
- if ($logflag) {
- $start_yymmdd = '';
- if ($ip_user_date{$user}) {
- ($ip_user, $start_yymmdd) =
- split (/:/, $ip_user_date{$user});
- } # end get date
- } # end log flag
- if ($start_yymmdd) {
- if ($date < $start_yymmdd) {
- next NEXTUSER;
- } #end date compare
- } #end if date
- if ($userflag){
- if (!$ip_user_date{$user}) {
- next NEXTUSER;
- } # end user test
- } # end by user or all
- if (($totalflag) ||
- ($allflag) ||
- ($ip_user_date{$user})) {
- if (($type eq 'ppp') || ($type eq 'slip')) {
-
- if ($action eq 'login') {
- $login{$user} = "$time:$date";
-
- }
- elsif ($action eq 'logout') {
- if (!$login{$user}) {
- $login{$user} = "010101:$date";
- } #end pad user if carry over
- ($stime,$sdate) = split(':',$login{$user});
- $start = &annex2sec($stime);
- $end = &annex2sec($time);
-
- #If we went through midnight, add a day;
- if ($end < $start) {$end += 86400;}
- $timeon = $end - $start;
-
- $elapsed{$user} += $timeon;
-
- if (!$totalflag) {
- print (&fmt_user($user),
- ' ', &fmt_date($sdate), ' In: ',
- &fmt_time($stime),' Out: ',
- &fmt_time($time),
- ' Elapsed: ', &fmt_sec($timeon), "\n");
- } # end total test
- } #end elsif action
- } # type = ppp of slip
- } # check arguments
-}
-close IN;
-
-if ($logflag) {
- open (TMPPPP, ">$tmpfile")
- || die "Can't open ppp tmp file";
- foreach $user ( sort((keys(%elapsed))) ) {
- $log_time = &fmt_sec($elapsed{$user});
- $tmp = join (':',
- $user,
- $log_time);
- print (TMPPPP "$tmp\n");
- }
- close (TMPPPP);
-}
- else {
- print "\n\nTotal Time On For Period:\n";
- print "-------------------------\n";
-
- foreach $user ( sort((keys(%elapsed))) ) {
- print (&fmt_user($user), " ",&fmt_sec($elapsed{$user}), "\n");
- }
- }
-exit(0);
-
-#-------------------------------------------------------
-#--------------- Subroutines Start Here ----------------
-#-------------------------------------------------------
-
-sub annex2sec {
- local($time) = @_;
- return( &time2sec( &break_annex($time) ) );
-}
-
-sub fmt_date {
- local($date) = @_;
-
- return( substr($date,2,2).'/'.substr($date,4,2).'/'.substr($date,0,2) );
-}
-
-sub fmt_time {
- local($time) = @_;
- local($s,$m,$h) = &break_annex($time);
- return ("$h:$m:$s");
-}
-
-
-sub break_annex {
- local($time) = @_;
- local($h,$m,$s);
-
- $h=substr($time,0,2);
- $m=substr($time,2,2);
- $s=substr($time,4,2);
-
- return ($s,$m,$h);
-}
-
-sub fmt_sec {
- local(@t) = &sec2time(@_);
- @t[2] += (@t[3]*24);
-
- foreach $a (@t) {
- if ($a < 10) {$a = "0$a";}
- }
-
- return ("@t[2]:@t[1]:@t[0]");
-}
-
-sub fmt_user {
- local($user) = @_;
- return( $user.substr($space,0,8 - length($user) ).' ' );
-}
-
diff --git a/etc/example-direct-cardin b/etc/example-direct-cardin
deleted file mode 100755
index 1a40972..0000000
--- a/etc/example-direct-cardin
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/local/bin/perl
-
-###
-# THIS IS FROM CYBERCASH (is there a newer version?)
-###
-
-$paymentserverhost = 'localhost';
-$paymentserverport = 8000;
-$paymentserversecret = 'two-turntables';
-use CCLib qw(sendmserver);
-
-# first lets fake up some data
-# use time of day and pid to give me my pretend
-# order number
-# you obviously need to get real data from somewhere...
-
-$oid = "test$$"; #fake order number.
-$amount = 'usd 42.42';
-$ramount = 'usd 24.24';
-$pan = '4111111111111111';
-$name = 'John Q. Doe';
-$addr = '17 Richard Rd.';
-$city = 'Ivyland';
-$state = 'PA';
-$zip = '18974';
-$country = 'USA';
-$exp = '7/97';
-
-
-%result = &sendmserver('mauthcapture',
- 'Order-ID', $oid,
- 'Amount', $amount,
- 'Card-Number', $pan,
- 'Card-Name', $name,
- 'Card-Address', $addr,
- 'Card-City', $city,
- 'Card-State', $state,
- 'Card-Zip', $zip,
- 'Card-Country', $country,
- 'Card-Exp', $exp);
-
-#
-# just dump results to stdout.
-# you should process them...
-# to allow results to affect operation of your fulfillment...
-#
-foreach (keys(%result)) {
- print " $_ ==> $result{$_}\n";
-}
-
-print "\n";
-
-exit;
-
-$trans=$result{'MTransactionNumber'};
-$code=$result{'MRetrievalCode'};
-
-%result = &sendmserver('return',
- 'Order-ID', $oid,
- 'Return-Amount',$ramount,
- 'Amount',$amount,
- );
-
-foreach (keys(%result)) {
- print " $_ ==> $result{$_}\n";
-}
-
diff --git a/etc/fslongtable.sty b/etc/fslongtable.sty
new file mode 100644
index 0000000..fc936a1
--- /dev/null
+++ b/etc/fslongtable.sty
@@ -0,0 +1,439 @@
+%%
+%% This is file `fslongtable.sty',
+%%
+%% Copyright 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003
+%% The LaTeX3 Project and any individual authors listed elsewhere
+%% in this file.
+%%
+%% This file was forked from file(s) of the Standard LaTeX `Tools Bundle'.
+%% This file includes a new length LTextracouponspace which modifies
+%% the behavior of the package at the end of a page. This feature
+%% and package is not supported or acknowledged by Dave Carlisle.
+%% Do not contact him for such support.
+%% --------------------------------------------------------------------------
+%%
+%% It may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either version 1.3
+%% of this license or (at your option) any later version.
+%% The latest version of this license is in
+%% http://www.latex-project.org/lppl.txt
+%% and version 1.3 or later is part of all distributions of LaTeX
+%% version 2003/12/01 or later.
+%%
+%% File: longtable.dtx Copyright (C) 1990-2001 David Carlisle
+%% File: fslongtable.sty Copyright (C) 2008 Jeff Finucane
+\NeedsTeXFormat{LaTeX2e}[1995/06/01]
+\ProvidesPackage{longtable}
+ [2004/02/01 v4.11 Multi-page Table package (DPC)]
+\def\LT@err{\PackageError{longtable}}
+\def\LT@warn{\PackageWarning{longtable}}
+\def\LT@final@warn{%
+ \AtEndDocument{%
+ \LT@warn{Table \@width s have changed. Rerun LaTeX.\@gobbletwo}}%
+ \global\let\LT@final@warn\relax}
+\DeclareOption{errorshow}{%
+ \def\LT@warn{\PackageInfo{longtable}}}
+\DeclareOption{pausing}{%
+ \def\LT@warn#1{%
+ \LT@err{#1}{This is not really an error}}}
+\DeclareOption{set}{}
+\DeclareOption{final}{}
+\ProcessOptions
+\newskip\LTleft \LTleft=\fill
+\newskip\LTright \LTright=\fill
+\newskip\LTpre \LTpre=\bigskipamount
+\newskip\LTpost \LTpost=\bigskipamount
+\newcount\LTchunksize \LTchunksize=20
+\let\c@LTchunksize\LTchunksize
+\newdimen\LTcapwidth \LTcapwidth=4in
+\newlength\LTextracouponspace
+\newbox\LT@head
+\newbox\LT@firsthead
+\newbox\LT@foot
+\newbox\LT@lastfoot
+\newcount\LT@cols
+\newcount\LT@rows
+\newcounter{LT@tables}
+\newcounter{LT@chunks}[LT@tables]
+\ifx\c@table\undefined
+ \newcounter{table}
+ \def\fnum@table{\tablename~\thetable}
+\fi
+\ifx\tablename\undefined
+ \def\tablename{Table}
+\fi
+\newtoks\LT@p@ftn
+\mathchardef\LT@end@pen=30000
+\def\longtable{%
+ \par
+ \ifx\multicols\@undefined
+ \else
+ \ifnum\col@number>\@ne
+ \@twocolumntrue
+ \fi
+ \fi
+ \if@twocolumn
+ \LT@err{longtable not in 1-column mode}\@ehc
+ \fi
+ \begingroup
+ \@ifnextchar[\LT@array{\LT@array[x]}}
+\def\LT@array[#1]#2{%
+ \refstepcounter{table}\stepcounter{LT@tables}%
+ \if l#1%
+ \LTleft\z@ \LTright\fill
+ \else\if r#1%
+ \LTleft\fill \LTright\z@
+ \else\if c#1%
+ \LTleft\fill \LTright\fill
+ \fi\fi\fi
+ \let\LT@mcol\multicolumn
+ \let\LT@@tabarray\@tabarray
+ \let\LT@@hl\hline
+ \def\@tabarray{%
+ \let\hline\LT@@hl
+ \LT@@tabarray}%
+ \let\\\LT@tabularcr\let\tabularnewline\\%
+ \def\newpage{\noalign{\break}}%
+ \def\pagebreak{\noalign{\ifnum`}=0\fi\@testopt{\LT@no@pgbk-}4}%
+ \def\nopagebreak{\noalign{\ifnum`}=0\fi\@testopt\LT@no@pgbk4}%
+ \let\hline\LT@hline \let\kill\LT@kill\let\caption\LT@caption
+ \@tempdima\ht\strutbox
+ \let\@endpbox\LT@endpbox
+ \ifx\extrarowheight\@undefined
+ \let\@acol\@tabacol
+ \let\@classz\@tabclassz \let\@classiv\@tabclassiv
+ \def\@startpbox{\vtop\LT@startpbox}%
+ \let\@@startpbox\@startpbox
+ \let\@@endpbox\@endpbox
+ \let\LT@LL@FM@cr\@tabularcr
+ \else
+ \advance\@tempdima\extrarowheight
+ \col@sep\tabcolsep
+ \let\@startpbox\LT@startpbox\let\LT@LL@FM@cr\@arraycr
+ \fi
+ \setbox\@arstrutbox\hbox{\vrule
+ \@height \arraystretch \@tempdima
+ \@depth \arraystretch \dp \strutbox
+ \@width \z@}%
+ \let\@sharp##\let\protect\relax
+ \begingroup
+ \@mkpream{#2}%
+ \xdef\LT@bchunk{%
+ \global\advance\c@LT@chunks\@ne
+ \global\LT@rows\z@\setbox\z@\vbox\bgroup
+ \LT@setprevdepth
+ \tabskip\LTleft \noexpand\halign to\hsize\bgroup
+ \tabskip\z@ \@arstrut \@preamble \tabskip\LTright \cr}%
+ \endgroup
+ \expandafter\LT@nofcols\LT@bchunk&\LT@nofcols
+ \LT@make@row
+ \m@th\let\par\@empty
+ \everycr{}\lineskip\z@\baselineskip\z@
+ \LT@bchunk}
+\def\LT@no@pgbk#1[#2]{\penalty #1\@getpen{#2}\ifnum`{=0\fi}}
+\def\LT@start{%
+ \let\LT@start\endgraf
+ \endgraf\penalty\z@\vskip\LTpre
+ \dimen@\pagetotal
+ \advance\dimen@ \ht\ifvoid\LT@firsthead\LT@head\else\LT@firsthead\fi
+ \advance\dimen@ \dp\ifvoid\LT@firsthead\LT@head\else\LT@firsthead\fi
+ \advance\dimen@ \ht\LT@foot
+ \advance\dimen@ \LTextracouponspace
+ \dimen@ii\vfuzz
+ \vfuzz\maxdimen
+ \setbox\tw@\copy\z@
+ \setbox\tw@\vsplit\tw@ to \ht\@arstrutbox
+ \setbox\tw@\vbox{\unvbox\tw@}%
+ \vfuzz\dimen@ii
+ \advance\dimen@ \ht
+ \ifdim\ht\@arstrutbox>\ht\tw@\@arstrutbox\else\tw@\fi
+ \advance\dimen@\dp
+ \ifdim\dp\@arstrutbox>\dp\tw@\@arstrutbox\else\tw@\fi
+ \advance\dimen@ -\pagegoal
+ \ifdim \dimen@>\z@\vfil\break\fi
+ \global\@colroom\@colht
+ \ifnum\thepage=1
+ \advance\vsize-\LTextracouponspace
+ \dimen@\pagegoal\advance\dimen@-\LTextracouponspace\pagegoal\dimen@
+ \fi
+ \ifvoid\LT@foot\else
+ \advance\vsize-\ht\LT@foot
+ \global\advance\@colroom-\ht\LT@foot
+ \dimen@\pagegoal\advance\dimen@-\ht\LT@foot\pagegoal\dimen@
+ \maxdepth\z@
+ \fi
+ \ifvoid\LT@firsthead\copy\LT@head\else\box\LT@firsthead\fi\nobreak
+ \output{\LT@output}}
+\def\endlongtable{%
+ \crcr
+ \noalign{%
+ \let\LT@entry\LT@entry@chop
+ \xdef\LT@save@row{\LT@save@row}}%
+ \LT@echunk
+ \LT@start
+ \unvbox\z@
+ \LT@get@widths
+ \if@filesw
+ {\let\LT@entry\LT@entry@write\immediate\write\@auxout{%
+ \gdef\expandafter\noexpand
+ \csname LT@\romannumeral\c@LT@tables\endcsname
+ {\LT@save@row}}}%
+ \fi
+ \ifx\LT@save@row\LT@@save@row
+ \else
+ \LT@warn{Column \@width s have changed\MessageBreak
+ in table \thetable}%
+ \LT@final@warn
+ \fi
+ \endgraf\penalty -\LT@end@pen
+ \endgroup
+ \global\@mparbottom\z@
+ \pagegoal\vsize
+ \endgraf\penalty\z@\addvspace\LTpost
+ \ifvoid\footins\else\insert\footins{}\fi}
+\def\LT@nofcols#1&{%
+ \futurelet\@let@token\LT@n@fcols}
+\def\LT@n@fcols{%
+ \advance\LT@cols\@ne
+ \ifx\@let@token\LT@nofcols
+ \expandafter\@gobble
+ \else
+ \expandafter\LT@nofcols
+ \fi}
+\def\LT@tabularcr{%
+ \relax\iffalse{\fi\ifnum0=`}\fi
+ \@ifstar
+ {\def\crcr{\LT@crcr\noalign{\nobreak}}\let\cr\crcr
+ \LT@t@bularcr}%
+ {\LT@t@bularcr}}
+\let\LT@crcr\crcr
+\let\LT@setprevdepth\relax
+\def\LT@t@bularcr{%
+ \global\advance\LT@rows\@ne
+ \ifnum\LT@rows=\LTchunksize
+ \gdef\LT@setprevdepth{%
+ \prevdepth\z@\global
+ \global\let\LT@setprevdepth\relax}%
+ \expandafter\LT@xtabularcr
+ \else
+ \ifnum0=`{}\fi
+ \expandafter\LT@LL@FM@cr
+ \fi}
+\def\LT@xtabularcr{%
+ \@ifnextchar[\LT@argtabularcr\LT@ntabularcr}
+\def\LT@ntabularcr{%
+ \ifnum0=`{}\fi
+ \LT@echunk
+ \LT@start
+ \unvbox\z@
+ \LT@get@widths
+ \LT@bchunk}
+\def\LT@argtabularcr[#1]{%
+ \ifnum0=`{}\fi
+ \ifdim #1>\z@
+ \unskip\@xargarraycr{#1}%
+ \else
+ \@yargarraycr{#1}%
+ \fi
+ \LT@echunk
+ \LT@start
+ \unvbox\z@
+ \LT@get@widths
+ \LT@bchunk}
+\def\LT@echunk{%
+ \crcr\LT@save@row\cr\egroup
+ \global\setbox\@ne\lastbox
+ \unskip
+ \egroup}
+\def\LT@entry#1#2{%
+ \ifhmode\@firstofone{&}\fi\omit
+ \ifnum#1=\c@LT@chunks
+ \else
+ \kern#2\relax
+ \fi}
+\def\LT@entry@chop#1#2{%
+ \noexpand\LT@entry
+ {\ifnum#1>\c@LT@chunks
+ 1}{0pt%
+ \else
+ #1}{#2%
+ \fi}}
+\def\LT@entry@write{%
+ \noexpand\LT@entry^^J%
+ \@spaces}
+\def\LT@kill{%
+ \LT@echunk
+ \LT@get@widths
+ \expandafter\LT@rebox\LT@bchunk}
+\def\LT@rebox#1\bgroup{%
+ #1\bgroup
+ \unvbox\z@
+ \unskip
+ \setbox\z@\lastbox}
+\def\LT@blank@row{%
+ \xdef\LT@save@row{\expandafter\LT@build@blank
+ \romannumeral\number\LT@cols 001 }}
+\def\LT@build@blank#1{%
+ \if#1m%
+ \noexpand\LT@entry{1}{0pt}%
+ \expandafter\LT@build@blank
+ \fi}
+\def\LT@make@row{%
+ \global\expandafter\let\expandafter\LT@save@row
+ \csname LT@\romannumeral\c@LT@tables\endcsname
+ \ifx\LT@save@row\relax
+ \LT@blank@row
+ \else
+ {\let\LT@entry\or
+ \if!%
+ \ifcase\expandafter\expandafter\expandafter\LT@cols
+ \expandafter\@gobble\LT@save@row
+ \or
+ \else
+ \relax
+ \fi
+ !%
+ \else
+ \aftergroup\LT@blank@row
+ \fi}%
+ \fi}
+\let\setlongtables\relax
+\def\LT@get@widths{%
+ \setbox\tw@\hbox{%
+ \unhbox\@ne
+ \let\LT@old@row\LT@save@row
+ \global\let\LT@save@row\@empty
+ \count@\LT@cols
+ \loop
+ \unskip
+ \setbox\tw@\lastbox
+ \ifhbox\tw@
+ \LT@def@row
+ \advance\count@\m@ne
+ \repeat}%
+ \ifx\LT@@save@row\@undefined
+ \let\LT@@save@row\LT@save@row
+ \fi}
+\def\LT@def@row{%
+ \let\LT@entry\or
+ \edef\@tempa{%
+ \ifcase\expandafter\count@\LT@old@row
+ \else
+ {1}{0pt}%
+ \fi}%
+ \let\LT@entry\relax
+ \xdef\LT@save@row{%
+ \LT@entry
+ \expandafter\LT@max@sel\@tempa
+ \LT@save@row}}
+\def\LT@max@sel#1#2{%
+ {\ifdim#2=\wd\tw@
+ #1%
+ \else
+ \number\c@LT@chunks
+ \fi}%
+ {\the\wd\tw@}}
+\def\LT@hline{%
+ \noalign{\ifnum0=`}\fi
+ \penalty\@M
+ \futurelet\@let@token\LT@@hline}
+\def\LT@@hline{%
+ \ifx\@let@token\hline
+ \global\let\@gtempa\@gobble
+ \gdef\LT@sep{\penalty-\@medpenalty\vskip\doublerulesep}%
+ \else
+ \global\let\@gtempa\@empty
+ \gdef\LT@sep{\penalty-\@lowpenalty\vskip-\arrayrulewidth}%
+ \fi
+ \ifnum0=`{\fi}%
+ \multispan\LT@cols
+ \unskip\leaders\hrule\@height\arrayrulewidth\hfill\cr
+ \noalign{\LT@sep}%
+ \multispan\LT@cols
+ \unskip\leaders\hrule\@height\arrayrulewidth\hfill\cr
+ \noalign{\penalty\@M}%
+ \@gtempa}
+\def\LT@caption{%
+ \noalign\bgroup
+ \@ifnextchar[{\egroup\LT@c@ption\@firstofone}\LT@capti@n}
+\def\LT@c@ption#1[#2]#3{%
+ \LT@makecaption#1\fnum@table{#3}%
+ \def\@tempa{#2}%
+ \ifx\@tempa\@empty\else
+ {\let\\\space
+ \addcontentsline{lot}{table}{\protect\numberline{\thetable}{#2}}}%
+ \fi}
+\def\LT@capti@n{%
+ \@ifstar
+ {\egroup\LT@c@ption\@gobble[]}%
+ {\egroup\@xdblarg{\LT@c@ption\@firstofone}}}
+\def\LT@makecaption#1#2#3{%
+ \LT@mcol\LT@cols c{\hbox to\z@{\hss\parbox[t]\LTcapwidth{%
+ \sbox\@tempboxa{#1{#2: }#3}%
+ \ifdim\wd\@tempboxa>\hsize
+ #1{#2: }#3%
+ \else
+ \hbox to\hsize{\hfil\box\@tempboxa\hfil}%
+ \fi
+ \endgraf\vskip\baselineskip}%
+ \hss}}}
+\def\LT@output{%
+ \ifnum\outputpenalty <-\@Mi
+ \ifnum\outputpenalty > -\LT@end@pen
+ \LT@err{floats and marginpars not allowed in a longtable}\@ehc
+ \else
+ \setbox\z@\vbox{\unvbox\@cclv}%
+ \ifdim \ht\LT@lastfoot>\ht\LT@foot
+ \dimen@\pagegoal
+ \advance\dimen@-\ht\LT@lastfoot
+ \ifdim\dimen@<\ht\z@
+ \setbox\@cclv\vbox{\unvbox\z@\copy\LT@foot\vss}%
+ \@makecol
+ \@outputpage
+ \setbox\z@\vbox{\box\LT@head}%
+ \fi
+ \fi
+ \global\@colroom\@colht
+ \global\vsize\@colht
+ \vbox
+ {\unvbox\z@\box\ifvoid\LT@lastfoot\LT@foot\else\LT@lastfoot\fi}%
+ \fi
+ \else
+ \setbox\@cclv\vbox{\unvbox\@cclv\copy\LT@foot\vss}%
+ \@makecol
+ \@outputpage
+ \global\vsize\@colroom
+ \copy\LT@head\nobreak
+ \fi}
+\def\LT@end@hd@ft#1{%
+ \LT@echunk
+ \ifx\LT@start\endgraf
+ \LT@err
+ {Longtable head or foot not at start of table}%
+ {Increase LTchunksize}%
+ \fi
+ \setbox#1\box\z@
+ \LT@get@widths
+ \LT@bchunk}
+\def\endfirsthead{\LT@end@hd@ft\LT@firsthead}
+\def\endhead{\LT@end@hd@ft\LT@head}
+\def\endfoot{\LT@end@hd@ft\LT@foot}
+\def\endlastfoot{\LT@end@hd@ft\LT@lastfoot}
+\def\LT@startpbox#1{%
+ \bgroup
+ \let\@footnotetext\LT@p@ftntext
+ \setlength\hsize{#1}%
+ \@arrayparboxrestore
+ \vrule \@height \ht\@arstrutbox \@width \z@}
+\def\LT@endpbox{%
+ \@finalstrut\@arstrutbox
+ \egroup
+ \the\LT@p@ftn
+ \global\LT@p@ftn{}%
+ \hfil}
+\def\LT@p@ftntext#1{%
+ \edef\@tempa{\the\LT@p@ftn\noexpand\footnotetext[\the\c@footnote]}%
+ \global\LT@p@ftn\expandafter{\@tempa{#1}}}%
+\endinput
+%%
+%% End of file `longtable.sty'.
diff --git a/etc/megapop.pl b/etc/megapop.pl
new file mode 100755
index 0000000..e2930fb
--- /dev/null
+++ b/etc/megapop.pl
@@ -0,0 +1,114 @@
+#!/usr/bin/perl -Tw
+#
+# this will break when megapop changes the URL or format of their listing page.
+# that's stupid. perhaps they can provide a machine-readable listing?
+
+use strict;
+use LWP::UserAgent;
+use FS::UID qw(adminsuidsetup);
+use FS::svc_acct_pop;
+
+my $url = "http://www.megapop.com/location.htm";
+
+my $user = shift or die &usage;
+adminsuidsetup($user);
+
+my %state2usps = &state2usps;
+$state2usps{'WASHINGTON STATE'} = 'WA'; #megapop's on crack
+$state2usps{'CANADA'} = 'CANADA'; #freeside's on crack
+
+my $ua = new LWP::UserAgent;
+my $request = new HTTP::Request('GET', $url);
+my $response = $ua->request($request);
+die $response->error_as_HTML unless $response->is_success;
+my $line;
+my $usps = '';
+foreach $line ( split("\n", $response->content) ) {
+ if ( $line =~ /\W(\w[\w\s]*\w)\s+LOCATIONS/i ) {
+ $usps = $state2usps{uc($1)}
+ or warn "warning: unknown state $1\n";
+ } elsif ( $line =~ /(\d{3})\-(\d{3})\-(\d{4})\s+(\w[\w\s]*\w)/ ) {
+ print "$1 $2 $3 $4 $usps\n";
+ my $svc_acct_pop = new FS::svc_acct_pop ( {
+ 'city' => $4,
+ 'state' => $usps,
+ 'ac' => $1,
+ 'exch' => $2,
+ } );
+ my $error = $svc_acct_pop->insert;
+ die $error if $error;
+ }
+}
+
+sub usage {
+ die "Usage:\n $0 user\n";
+}
+
+sub state2usps{ (
+ 'ALABAMA' => 'AL',
+ 'ALASKA' => 'AK',
+ 'AMERICAN SAMOA' => 'AS',
+ 'ARIZONA' => 'AZ',
+ 'ARKANSAS' => 'AR',
+ 'CALIFORNIA' => 'CA',
+ 'COLORADO' => 'CO',
+ 'CONNECTICUT' => 'CT',
+ 'DELAWARE' => 'DE',
+ 'DISTRICT OF COLUMBIA' => 'DC',
+ 'FEDERATED STATES OF MICRONESIA' => 'FM',
+ 'FLORIDA' => 'FL',
+ 'GEORGIA' => 'GA',
+ 'GUAM' => 'GU',
+ 'HAWAII' => 'HI',
+ 'IDAHO' => 'ID',
+ 'ILLINOIS' => 'IL',
+ 'INDIANA' => 'IN',
+ 'IOWA' => 'IA',
+ 'KANSAS' => 'KS',
+ 'KENTUCKY' => 'KY',
+ 'LOUISIANA' => 'LA',
+ 'MAINE' => 'ME',
+ 'MARSHALL ISLANDS' => 'MH',
+ 'MARYLAND' => 'MD',
+ 'MASSACHUSETTS' => 'MA',
+ 'MICHIGAN' => 'MI',
+ 'MINNESOTA' => 'MN',
+ 'MISSISSIPPI' => 'MS',
+ 'MISSOURI' => 'MO',
+ 'MONTANA' => 'MT',
+ 'NEBRASKA' => 'NE',
+ 'NEVADA' => 'NV',
+ 'NEW HAMPSHIRE' => 'NH',
+ 'NEW JERSEY' => 'NJ',
+ 'NEW MEXICO' => 'NM',
+ 'NEW YORK' => 'NY',
+ 'NORTH CAROLINA' => 'NC',
+ 'NORTH DAKOTA' => 'ND',
+ 'NORTHERN MARIANA ISLANDS' => 'MP',
+ 'OHIO' => 'OH',
+ 'OKLAHOMA' => 'OK',
+ 'OREGON' => 'OR',
+ 'PALAU' => 'PW',
+ 'PENNSYLVANIA' => 'PA',
+ 'PUERTO RICO' => 'PR',
+ 'RHODE ISLAND' => 'RI',
+ 'SOUTH CAROLINA' => 'SC',
+ 'SOUTH DAKOTA' => 'SD',
+ 'TENNESSEE' => 'TN',
+ 'TEXAS' => 'TX',
+ 'UTAH' => 'UT',
+ 'VERMONT' => 'VT',
+ 'VIRGIN ISLANDS' => 'VI',
+ 'VIRGINIA' => 'VA',
+ 'WASHINGTON' => 'WA',
+ 'WEST VIRGINIA' => 'WV',
+ 'WISCONSIN' => 'WI',
+ 'WYOMING' => 'WY',
+ 'ARMED FORCES AFRICA' => 'AE',
+ 'ARMED FORCES AMERICAS' => 'AA',
+ 'ARMED FORCES CANADA' => 'AE',
+ 'ARMED FORCES EUROPE' => 'AE',
+ 'ARMED FORCES MIDDLE EAST' => 'AE',
+ 'ARMED FORCES PACIFIC' => 'AP',
+) }
+
diff --git a/etc/sql-reserved-words.txt b/etc/sql-reserved-words.txt
new file mode 100644
index 0000000..dc507ce
--- /dev/null
+++ b/etc/sql-reserved-words.txt
@@ -0,0 +1,103 @@
+From http://epoch.cs.berkeley.edu:8000/sequoia/dba/montage/FAQ/SQL.html
+ by Jean Anderson (jta@postgres.berkeley.edu)
+
+What are the SQL reserved words?
+
+I grep'd the following list out of the sql docs available via anonymous ftp to speckle.ncsl.nist.gov:/isowg3.
+SQL3 words are not set in stone, but you'd do well to avoid them.
+
+ From sql1992.txt:
+
+ AFTER, ALIAS, ASYNC, BEFORE, BOOLEAN, BREADTH,
+ COMPLETION, CALL, CYCLE, DATA, DEPTH, DICTIONARY, EACH, ELSEIF,
+ EQUALS, GENERAL, IF, IGNORE, LEAVE, LESS, LIMIT, LOOP, MODIFY,
+ NEW, NONE, OBJECT, OFF, OID, OLD, OPERATION, OPERATORS, OTHERS,
+ PARAMETERS, PENDANT, PREORDER, PRIVATE, PROTECTED, RECURSIVE, REF,
+ REFERENCING, REPLACE, RESIGNAL, RETURN, RETURNS, ROLE, ROUTINE,
+ ROW, SAVEPOINT, SEARCH, SENSITIVE, SEQUENCE, SIGNAL, SIMILAR,
+ SQLEXCEPTION, SQLWARNING, STRUCTURE, TEST, THERE, TRIGGER, TYPE,
+ UNDER, VARIABLE, VIRTUAL, VISIBLE, WAIT, WHILE, WITHOUT
+
+ From sql1992.txt (Annex E):
+
+ ABSOLUTE, ACTION, ADD, ALLOCATE, ALTER, ARE, ASSERTION, AT, BETWEEN,
+ BIT, BIT
+
+What are the SQL reserved words?
+
+I grep'd the following list out of the sql docs available via anonymous ftp to speckle.ncsl.nist.gov:/isowg3.
+SQL3 words are not set in stone, but you'd do well to avoid them.
+
+ From sql1992.txt:
+
+ AFTER, ALIAS, ASYNC, BEFORE, BOOLEAN, BREADTH,
+ COMPLETION, CALL, CYCLE, DATA, DEPTH, DICTIONARY, EACH, ELSEIF,
+ EQUALS, GENERAL, IF, IGNORE, LEAVE, LESS, LIMIT, LOOP, MODIFY,
+ NEW, NONE, OBJECT, OFF, OID, OLD, OPERATION, OPERATORS, OTHERS,
+ PARAMETERS, PENDANT, PREORDER, PRIVATE, PROTECTED, RECURSIVE, REF,
+ REFERENCING, REPLACE, RESIGNAL, RETURN, RETURNS, ROLE, ROUTINE,
+ ROW, SAVEPOINT, SEARCH, SENSITIVE, SEQUENCE, SIGNAL, SIMILAR,
+ SQLEXCEPTION, SQLWARNING, STRUCTURE, TEST, THERE, TRIGGER, TYPE,
+ UNDER, VARIABLE, VIRTUAL, VISIBLE, WAIT, WHILE, WITHOUT
+
+ From sql1992.txt (Annex E):
+
+ ABSOLUTE, ACTION, ADD, ALLOCATE, ALTER, ARE, ASSERTION, AT, BETWEEN,
+ BIT, BIT
+
+What are the SQL reserved words?
+
+I grep'd the following list out of the sql docs available via anonymous ftp to speckle.ncsl.nist.gov:/isowg3.
+SQL3 words are not set in stone, but you'd do well to avoid them.
+
+ From sql1992.txt:
+
+ AFTER, ALIAS, ASYNC, BEFORE, BOOLEAN, BREADTH,
+ COMPLETION, CALL, CYCLE, DATA, DEPTH, DICTIONARY, EACH, ELSEIF,
+ EQUALS, GENERAL, IF, IGNORE, LEAVE, LESS, LIMIT, LOOP, MODIFY,
+ NEW, NONE, OBJECT, OFF, OID, OLD, OPERATION, OPERATORS, OTHERS,
+ PARAMETERS, PENDANT, PREORDER, PRIVATE, PROTECTED, RECURSIVE, REF,
+ REFERENCING, REPLACE, RESIGNAL, RETURN, RETURNS, ROLE, ROUTINE,
+ ROW, SAVEPOINT, SEARCH, SENSITIVE, SEQUENCE, SIGNAL, SIMILAR,
+ SQLEXCEPTION, SQLWARNING, STRUCTURE, TEST, THERE, TRIGGER, TYPE,
+ UNDER, VARIABLE, VIRTUAL, VISIBLE, WAIT, WHILE, WITHOUT
+
+ From sql1992.txt (Annex E):
+
+ ABSOLUTE, ACTION, ADD, ALLOCATE, ALTER, ARE, ASSERTION, AT, BETWEEN,
+ BIT, BIT_LENGTH, BOTH, CASCADE, CASCADED, CASE, CAST, CATALOG,
+ CHAR_LENGTH, CHARACTER_LENGTH, COALESCE, COLLATE, COLLATION, COLUMN,
+ CONNECT, CONNECTION, CONSTRAINT, CONSTRAINTS, CONVERT, CORRESPONDING,
+ CROSS, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER,
+ DATE, DAY, DEALLOCATE, DEFERRABLE, DEFERRED, DESCRIBE, DESCRIPTOR,
+ DIAGNOSTICS, DISCONNECT, DOMAIN, DROP, ELSE, END-EXEC, EXCEPT,
+ EXCEPTION, EXECUTE, EXTERNAL, EXTRACT, FALSE, FIRST, FULL, GET,
+ GLOBAL, HOUR, IDENTITY, IMMEDIATE, INITIALLY, INNER, INPUT,
+ INSENSITIVE, INTERSECT, INTERVAL, ISOLATION, JOIN, LAST, LEADING,
+ LEFT, LEVEL, LOCAL, LOWER, MATCH, MINUTE, MONTH, NAMES, NATIONAL,
+ NATURAL, NCHAR, NEXT, NO, NULLIF, OCTET_LENGTH, ONLY, OUTER, OUTPUT,
+ OVERLAPS, PAD, PARTIAL, POSITION, PREPARE, PRESERVE, PRIOR, READ,
+ RELATIVE, RESTRICT, REVOKE, RIGHT, ROWS, SCROLL, SECOND, SESSION,
+ SESSION_USER, SIZE, SPACE, SQLSTATE, SUBSTRING, SYSTEM_USER,
+ TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE,
+ TRAILING, TRANSACTION, TRANSLATE, TRANSLATION, TRIM, TRUE, UNKNOWN,
+ UPPER, USAGE, USING, VALUE, VARCHAR, VARYING, WHEN, WRITE, YEAR, ZONE
+
+ From sql3part2.txt (Annex E)
+
+ ACTION, ACTOR, AFTER, ALIAS, ASYNC, ATTRIBUTES, BEFORE, BOOLEAN,
+ BREADTH, COMPLETION, CURRENT_PATH, CYCLE, DATA, DEPTH, DESTROY,
+ DICTIONARY, EACH, ELEMENT, ELSEIF, EQUALS, FACTOR, GENERAL, HOLD,
+ IGNORE, INSTEAD, LESS, LIMIT, LIST, MODIFY, NEW, NEW_TABLE, NO,
+ NONE, OFF, OID, OLD, OLD_TABLE, OPERATION, OPERATOR, OPERATORS,
+ PARAMETERS, PATH, PENDANT, POSTFIX, PREFIX, PREORDER, PRIVATE,
+ PROTECTED, RECURSIVE, REFERENCING, REPLACE, ROLE, ROUTINE, ROW,
+ SAVEPOINT, SEARCH, SENSITIVE, SEQUENCE, SESSION, SIMILAR, SPACE,
+ SQLEXCEPTION, SQLWARNING, START, STATE, STRUCTURE, SYMBOL, TERM,
+ TEST, THERE, TRIGGER, TYPE, UNDER, VARIABLE, VIRTUAL, VISIBLE,
+ WAIT, WITHOUT
+
+ sql3part4.txt (ANNEX E):
+
+ CALL, DO, ELSEIF, EXCEPTION, IF, LEAVE, LOOP, OTHERS, RESIGNAL,
+ RETURN, RETURNS, SIGNAL, TUPLE, WHILE
diff --git a/fs_passwd/fs_passwd b/fs_passwd/fs_passwd
index bcf09f1..feddb46 100755
--- a/fs_passwd/fs_passwd
+++ b/fs_passwd/fs_passwd
@@ -10,17 +10,17 @@
#
# password lengths 0,255 instead of 6,8 - we'll let the server process
# check the data ivan@sisd.com 98-jul-17
+#
+# updated for the exciting new world of self-service 2004-mar-10
use strict;
use Getopt::Std;
-use Socket;
-use IO::Handle;
+use FS::SelfService qw(passwd);
use vars qw($opt_f $opt_s);
-my($fs_passwdd_socket)="/usr/local/freeside/fs_passwdd_socket";
my($freeside_uid)=scalar(getpwnam('freeside'));
-$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
+$ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin';
$ENV{'SHELL'} = '/bin/sh';
$ENV{'IFS'} = " \t\n";
$ENV{'CDPATH'} = '';
@@ -114,13 +114,15 @@ print "\n";
system '/bin/stty', 'echo';
-socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-connect(SOCK, sockaddr_un($fs_passwdd_socket)) or die "connect: $!";
-print SOCK join("\n",$me,$old_password,$new_password,$new_gecos,$new_shell),"\n";
-SOCK->flush;
-my($error);
-$error = <SOCK>;
-chop $error;
+my $rv = passwd(
+ 'username' => $me,
+ 'old_password' => $old_password,
+ 'new_password' => $new_password,
+ 'new_gecos' => $new_gecos,
+ 'new_shell' => $new_shell,
+);
+
+my $error = $rv->{error};
if ($error) {
print "\nUpdate error: $error\n";
diff --git a/fs_passwd/fs_passwd_server b/fs_passwd/fs_passwd_server
deleted file mode 100755
index 99e7c43..0000000
--- a/fs_passwd/fs_passwd_server
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# fs_passwd_server
-#
-# portions of this script are copied from the `passwd' script in the original
-# (perl 4) camel book, now archived at
-# http://www.perl.com/CPAN/scripts/nutshell/ch6/passwd
-#
-# ivan@sisd.com 98-mar-9
-#
-# crypt-aware, s/password/_password/; ivan@sisd.com 98-aug-23
-
-use strict;
-use IO::Handle;
-use FS::SSH qw(sshopen2);
-use FS::UID qw(adminsuidsetup);
-use FS::Record qw(qsearchs);
-use FS::svc_acct;
-
-$SIG{CHLD} = sub { wait() };
-
-&adminsuidsetup;
-
-my($fs_passwdd)="/usr/local/sbin/fs_passwdd";
-
-my($shellmachine)=shift;
-die "Usage: fs_passwd_server shellmachine\n" unless $shellmachine;
-
-while (1) {
- my($reader,$writer)=(new IO::Handle, new IO::Handle);
- $writer->autoflush(1);
- sshopen2($shellmachine,$reader,$writer,$fs_passwdd);
- while (1) {
- my($username,$old_password,$new_password,$new_gecos,$new_shell);
- defined($username=<$reader>) or last;
- defined($old_password=<$reader>) or last;
- defined($new_password=<$reader>) or last;
- defined($new_gecos=<$reader>) or last;
- defined($new_shell=<$reader>) or last;
- chop($username);
- chop($old_password);
- chop($new_password);
- chop($new_gecos);
- chop($new_shell);
- my($svc_acct);
-
- #need to try both $old_password and encrypted $old_password
- #maybe the crypt function in svc_acct.export needs to be a library?
- my $salt = substr($old_password,0,2);
- my $cold_password = crypt($old_password,$salt);
- $svc_acct=qsearchs('svc_acct',{'username'=>$username,
- '_password'=>$old_password,
- } )
- || qsearchs('svc_acct',{'username'=>$username,
- '_password'=>$cold_password,
- } );
- unless ( $svc_acct ) { print $writer "Incorrect password.\n"; next; }
-
- my(%hash)=$svc_acct->hash;
- my($new_svc_acct) = create FS::svc_acct ( \%hash );
- $new_svc_acct->setfield('_password',$new_password)
- if $new_password && $new_password ne $old_password;
- $new_svc_acct->setfield('finger',$new_gecos) if $new_gecos;
- $new_svc_acct->setfield('shell',$new_shell) if $new_shell;
- my($error)=$new_svc_acct->replace($svc_acct);
- print $writer $error,"\n";
- }
- close $writer;
- close $reader;
- sleep 60;
- warn "Connection to $shellmachine lost! Reconnecting...\n";
-}
-
diff --git a/fs_passwd/fs_passwdd b/fs_passwd/fs_passwdd
deleted file mode 100755
index 582e13c..0000000
--- a/fs_passwd/fs_passwdd
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# fs_passwdd
-#
-# This is run REMOTELY over ssh by fs_passwd_server.
-#
-# ivan@sisd.com 98-mar-9
-
-use strict;
-use Socket;
-
-my($fs_passwdd_socket)="/usr/local/freeside/fs_passwdd_socket";
-
-$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
-$ENV{'SHELL'} = '/bin/sh';
-$ENV{'IFS'} = " \t\n";
-$ENV{'CDPATH'} = '';
-$ENV{'ENV'} = '';
-$ENV{'BASH_ENV'} = '';
-
-$|=1;
-
-my $uaddr = sockaddr_un($fs_passwdd_socket);
-my $proto = getprotobyname('tcp');
-
-socket(Server,PF_UNIX,SOCK_STREAM,0) or die "socket: $!";
-unlink($fs_passwdd_socket);
-bind(Server, $uaddr) or die "bind: $!";
-listen(Server,SOMAXCONN) or die "listen: $!";
-
-my($paddr);
-for ( ; $paddr = accept(Client,Server); close Client) {
- my($me,$old_password,$new_password,$new_gecos,$new_shell);
-
- $me=<Client>;
- $old_password=<Client>;
- $new_password=<Client>;
- $new_gecos=<Client>;
- $new_shell=<Client>;
-
- print $me,$old_password,$new_password,$new_gecos,$new_shell;
- my($error);
-
- $error=<STDIN>;
-
- print Client $error;
- close Client;
-}
-
diff --git a/fs_radlog/fs_radlogd b/fs_radlog/fs_radlogd
deleted file mode 100755
index 74c2af3..0000000
--- a/fs_radlog/fs_radlogd
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# ivan@sisd.com 98-mar-23
-
-use strict;
-use Date::Parse; #but hopefully not
-
-$|=1;
-
-my($file,$pos)=@_;
-open(FILE,"<$file") or die "Can't open $file: $!";
-seek(FILE,$pos,0) or die "Can't seek: $!";
-
-my($datestr);
-my(%param);
-
-$SIG{'HUP'} = sub { print "EOF\n"; exit; };
-
-while (1) {
-
- while (<FILE>) {
- next if /^$/;
- if ( /^\S/ ) {
- chop($datestr=$_);
- undef %param;
- } else {
- warn "Unexpected line: $_";
- }
- while (<FILE>) {
- if ( /^$/ ) {
- #if ( $param{'Acct-Status-Type'} eq 'Stop' ) {
- print join("\t",
- tell FILE,
- %param,
- ),"\n";
- #}
- last;
- } elsif ( /^\s+([\w\-]+)\s\=\s\"?([\w\.\-]+)\"?\s*$/ ) {
- $param{$1}=$2;
- } else {
- warn "Unexpected line: $_";
- }
-
- }
-
- }
- sleep 1;
- seek(FILE,0,1);
-}
-
-
diff --git a/fs_selfservice/DEPLOY b/fs_selfservice/DEPLOY
new file mode 100755
index 0000000..e73012f
--- /dev/null
+++ b/fs_selfservice/DEPLOY
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+#this is a quick hack for my dev machine. do not use it.
+# see the "make install-selfservice" and "make update-selfservice" makefile
+# targets to properly install this stuff.
+
+#kill `cat /var/run/freeside-selfservice-server.fs_selfservice.pid`
+
+cd FS-SelfService
+perl Makefile.PL && make && make install
+cd ..
+
+#( cd ..; make deploy; cd fs_selfservice )
+( cd ..; make clean; make install-perl-modules; /etc/init.d/freeside restart; cd fs_selfservice )
+
+#cp /home/ivan/freeside/fs_selfservice/FS-SelfService/cgi/* /var/www/MyAccount
+#chown freeside /var/www/MyAccount/*.cgi
+#chmod 755 /var/www/MyAccount/*.cgi
+#ln -s /var/www/MyAccount/selfservice.cgi /var/www/MyAccount/index.cgi || true
+
+ #cp /home/ivan/freeside/fs_signup/FS-SignupClient/cgi/* /var/www/signup/
+ ##mv /var/www/signup/signup-snarf.html /var/www/signup/signup.html #!!!!!
+ ##mv /var/www/signup/signup-billaddress.html /var/www/signup/signup.html #!!!!!
+ ##mv /var/www/signup/signup-freeoption.html /var/www/signup/signup.html #!!!!!
+ #chown freeside /var/www/signup/signup.cgi
+ #chmod 755 /var/www/signup/signup.cgi
+ #ln -s /var/www/signup/signup.cgi /var/www/signup/index.cgi || true
+
+
+chmod 755 /var/www/selfservice/*.cgi
diff --git a/fs_selfservice/FS-SelfService/Changes b/fs_selfservice/FS-SelfService/Changes
new file mode 100644
index 0000000..b9e26b7
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/Changes
@@ -0,0 +1,6 @@
+Revision history for Perl extension FS::SelfService.
+
+0.01 Tue May 28 16:49:41 2002
+ - original version; created by h2xs 1.21 with options
+ -A -X -n FS::SelfService
+
diff --git a/fs_selfservice/FS-SelfService/MANIFEST b/fs_selfservice/FS-SelfService/MANIFEST
new file mode 100644
index 0000000..a619b2b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/MANIFEST
@@ -0,0 +1,8 @@
+Changes
+Makefile.PL
+MANIFEST
+SelfService.pm
+SelfService/XMLRPC.pm
+test.pl
+freeside-selfservice-clientd
+freeside-selfservice-xmlrpc-server
diff --git a/fs_selfservice/FS-SelfService/Makefile.PL b/fs_selfservice/FS-SelfService/Makefile.PL
new file mode 100644
index 0000000..c078f08
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/Makefile.PL
@@ -0,0 +1,20 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ 'NAME' => 'FS::SelfService',
+ 'VERSION_FROM' => 'SelfService.pm', # finds $VERSION
+ 'EXE_FILES' => [ 'freeside-selfservice-clientd',
+ 'freeside-selfservice-xmlrpc-server',
+ ],
+ 'INSTALLSCRIPT' => '/usr/local/sbin',
+ 'INSTALLSITEBIN' => '/usr/local/sbin',
+ 'INSTALLSITESCRIPT' => '/usr/local/sbin', #recent deb users this...
+ 'PERM_RWX' => '750',
+ 'PREREQ_PM' => {
+ 'Storable' => 2.09,
+ }, # e.g., Module::Name => 1.1
+ ($] >= 5.005 ? ## Add these new keywords supported since 5.005
+ (ABSTRACT_FROM => 'SelfService.pm', # retrieve abstract from module
+ AUTHOR => 'Ivan Kohler <ivan-freeside-selfservice@420.am>') : ()),
+);
diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm
new file mode 100644
index 0000000..580ca73
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/SelfService.pm
@@ -0,0 +1,1707 @@
+package FS::SelfService;
+
+use strict;
+use vars qw( $VERSION @ISA @EXPORT_OK $DEBUG
+ $skip_uid_check $dir $socket %autoload $tag );
+use Exporter;
+use Socket;
+use FileHandle;
+#use IO::Handle;
+use IO::Select;
+use Storable 2.09 qw(nstore_fd fd_retrieve);
+
+$VERSION = '0.03';
+
+@ISA = qw( Exporter );
+
+$DEBUG = 0;
+
+$dir = "/usr/local/freeside";
+$socket = "$dir/selfservice_socket";
+$socket .= '.'.$tag if defined $tag && length($tag);
+
+#maybe should ask ClientAPI for this list
+%autoload = (
+ 'passwd' => 'passwd/passwd',
+ 'chfn' => 'passwd/passwd',
+ 'chsh' => 'passwd/passwd',
+ 'login_info' => 'MyAccount/login_info',
+ 'login' => 'MyAccount/login',
+ 'logout' => 'MyAccount/logout',
+ 'customer_info' => 'MyAccount/customer_info',
+ 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
+ 'invoice' => 'MyAccount/invoice',
+ 'invoice_logo' => 'MyAccount/invoice_logo',
+ 'list_invoices' => 'MyAccount/list_invoices', #?
+ 'cancel' => 'MyAccount/cancel', #add to ss cgi!
+ 'payment_info' => 'MyAccount/payment_info',
+ 'process_payment' => 'MyAccount/process_payment',
+ 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
+ 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
+ 'process_prepay' => 'MyAccount/process_prepay',
+ 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
+ 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
+ 'list_svc_usage' => 'MyAccount/list_svc_usage',
+ 'list_support_usage' => 'MyAccount/list_support_usage',
+ 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
+ 'change_pkg' => 'MyAccount/change_pkg',
+ 'order_recharge' => 'MyAccount/order_recharge',
+ 'renew_info' => 'MyAccount/renew_info',
+ 'order_renew' => 'MyAccount/order_renew',
+ 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
+ 'charge' => 'MyAccount/charge', #?
+ 'part_svc_info' => 'MyAccount/part_svc_info',
+ 'provision_acct' => 'MyAccount/provision_acct',
+ 'provision_external' => 'MyAccount/provision_external',
+ 'unprovision_svc' => 'MyAccount/unprovision_svc',
+ 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
+ 'signup_info' => 'Signup/signup_info',
+ 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
+ 'new_customer' => 'Signup/new_customer',
+ 'agent_login' => 'Agent/agent_login',
+ 'agent_logout' => 'Agent/agent_logout',
+ 'agent_info' => 'Agent/agent_info',
+ 'agent_list_customers' => 'Agent/agent_list_customers',
+ 'mason_comp' => 'MasonComponent/mason_comp',
+ 'call_time' => 'PrepaidPhone/call_time',
+ 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
+ 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
+);
+@EXPORT_OK = (
+ keys(%autoload),
+ qw( regionselector regionselector_hashref
+ expselect popselector domainselector didselector )
+);
+
+$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
+$ENV{'SHELL'} = '/bin/sh';
+$ENV{'IFS'} = " \t\n";
+$ENV{'CDPATH'} = '';
+$ENV{'ENV'} = '';
+$ENV{'BASH_ENV'} = '';
+
+#you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
+#if you grant appropriate permissions to whatever user
+my $freeside_uid = scalar(getpwnam('freeside'));
+die "not running as the freeside user\n"
+ if $> != $freeside_uid && ! $skip_uid_check;
+
+-e $dir or die "FATAL: $dir doesn't exist!";
+-d $dir or die "FATAL: $dir isn't a directory!";
+-r $dir or die "FATAL: Can't read $dir as freeside user!";
+-x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
+
+foreach my $autoload ( keys %autoload ) {
+
+ my $eval =
+ "sub $autoload { ". '
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ #warn scalar(@_). ": ". join(" / ", @_);
+ $param = { @_ };
+ }
+
+ $param->{_packet} = \''. $autoload{$autoload}. '\';
+
+ simple_packet($param);
+ }';
+
+ eval $eval;
+ die $@ if $@;
+
+}
+
+sub simple_packet {
+ my $packet = shift;
+ warn "sending ". $packet->{_packet}. " to server"
+ if $DEBUG;
+ socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
+ connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
+ nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
+ SOCK->flush;
+
+ #shoudl trap: Magic number checking on storable file failed at blib/lib/Storable.pm (autosplit into blib/lib/auto/Storable/fd_retrieve.al) line 337, at /usr/local/share/perl/5.6.1/FS/SelfService.pm line 71
+
+ #block until there is a message on socket
+# my $w = new IO::Select;
+# $w->add(\*SOCK);
+# my @wait = $w->can_read;
+
+ warn "reading message from server"
+ if $DEBUG;
+
+ my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
+ die $return->{'_error'} if defined $return->{_error} && $return->{_error};
+
+ warn "returning message to client"
+ if $DEBUG;
+
+ $return;
+}
+
+=head1 NAME
+
+FS::SelfService - Freeside self-service API
+
+=head1 SYNOPSIS
+
+ # password and shell account changes
+ use FS::SelfService qw(passwd chfn chsh);
+
+ # "my account" functionality
+ use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
+
+ my $rv = login( { 'username' => $username,
+ 'domain' => $domain,
+ 'password' => $password,
+ }
+ );
+
+ if ( $rv->{'error'} ) {
+ #handle login error...
+ } else {
+ #successful login
+ my $session_id = $rv->{'session_id'};
+ }
+
+ my $customer_info = customer_info( { 'session_id' => $session_id } );
+
+ #payment_info and process_payment are available in 1.5+ only
+ my $payment_info = payment_info( { 'session_id' => $session_id } );
+
+ #!!! process_payment example
+
+ #!!! list_pkgs example
+
+ #!!! order_pkg example
+
+ #!!! cancel_pkg example
+
+ # signup functionality
+ use FS::SelfService qw( signup_info new_customer );
+
+ my $signup_info = signup_info;
+
+ $rv = new_customer( {
+ 'first' => $first,
+ 'last' => $last,
+ 'company' => $company,
+ 'address1' => $address1,
+ 'address2' => $address2,
+ 'city' => $city,
+ 'state' => $state,
+ 'zip' => $zip,
+ 'country' => $country,
+ 'daytime' => $daytime,
+ 'night' => $night,
+ 'fax' => $fax,
+ 'payby' => $payby,
+ 'payinfo' => $payinfo,
+ 'paycvv' => $paycvv,
+ 'paystart_month' => $paystart_month
+ 'paystart_year' => $paystart_year,
+ 'payissue' => $payissue,
+ 'payip' => $payip
+ 'paydate' => $paydate,
+ 'payname' => $payname,
+ 'invoicing_list' => $invoicing_list,
+ 'referral_custnum' => $referral_custnum,
+ 'agentnum' => $agentnum,
+ 'pkgpart' => $pkgpart,
+
+ 'username' => $username,
+ '_password' => $password,
+ 'popnum' => $popnum,
+ #OR
+ 'countrycode' => 1,
+ 'phonenum' => $phonenum,
+ 'pin' => $pin,
+ }
+ );
+
+ my $error = $rv->{'error'};
+ if ( $error eq '_decline' ) {
+ print_decline();
+ } elsif ( $error ) {
+ reprint_signup();
+ } else {
+ print_success();
+ }
+
+=head1 DESCRIPTION
+
+Use this API to implement your own client "self-service" module.
+
+If you just want to customize the look of the existing "self-service" module,
+see XXXX instead.
+
+=head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
+
+=over 4
+
+=item passwd
+
+=item chfn
+
+=item chsh
+
+=back
+
+=head1 "MY ACCOUNT" FUNCTIONS
+
+=over 4
+
+=item login HASHREF
+
+Creates a user session. Takes a hash reference as parameter with the
+following keys:
+
+=over 4
+
+=item username
+
+Username
+
+=item domain
+
+Domain
+
+=item password
+
+Password
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors.
+
+=item session_id
+
+Session identifier for successful logins
+
+=back
+
+=item customer_info HASHREF
+
+Returns general customer information.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item name
+
+Customer name
+
+=item balance
+
+Balance owed
+
+=item open
+
+Array reference of hash references of open inoices. Each hash reference has
+the following keys: invnum, date, owed
+
+=item small_custview
+
+An HTML fragment containing shipping and billing addresses.
+
+=item The following fields are also returned
+
+first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo payname month year invoicing_list postal_invoicing
+
+=back
+
+=item edit_info HASHREF
+
+Takes a hash reference as parameter with any of the following keys:
+
+first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo paycvv payname month year invoicing_list postal_invoicing
+
+If a field exists, the customer record is updated with the new value of that
+field. If a field does not exist, that field is not changed on the customer
+record.
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors
+
+=item invoice HASHREF
+
+Returns an invoice. Takes a hash reference as parameter with two keys:
+session_id and invnum
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item invnum
+
+Invoice number
+
+=item invoice_text
+
+Invoice text
+
+=back
+
+=item list_invoices HASHREF
+
+Returns a list of all customer invoices. Takes a hash references with a single
+key, session_id.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item invoices
+
+Reference to array of hash references with the following keys:
+
+=over 4
+
+=item invnum
+
+Invoice ID
+
+=item _date
+
+Invoice date, in UNIX epoch time
+
+=back
+
+=back
+
+=item cancel HASHREF
+
+Cancels this customer.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference with a single key, B<error>, which is empty on
+success or an error message on errors.
+
+=item payment_info HASHREF
+
+Returns information that may be useful in displaying a payment page.
+
+Takes a hash reference as parameter with a single key: B<session_id>.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item balance
+
+Balance owed
+
+=item payname
+
+Exact name on credit card (CARD/DCRD)
+
+=item address1
+
+Address line one
+
+=item address2
+
+Address line two
+
+=item city
+
+City
+
+=item state
+
+State
+
+=item zip
+
+Zip or postal code
+
+=item payby
+
+Customer's current default payment type.
+
+=item card_type
+
+For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
+
+=item payinfo
+
+For CARD/DCRD payment types, the card number
+
+=item month
+
+For CARD/DCRD payment types, expiration month
+
+=item year
+
+For CARD/DCRD payment types, expiration year
+
+=item cust_main_county
+
+County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>). Note these are not FS::cust_main_county objects, but hash references of columns and values.
+
+=item states
+
+Array reference of all states in the current default country.
+
+=item card_types
+
+Hash reference of card types; keys are card types, values are the exact strings
+passed to the process_payment function
+
+=cut
+
+#this doesn't actually work yet
+#
+#=item paybatch
+#
+#Unique transaction identifier (prevents multiple charges), passed to the
+#process_payment function
+
+=back
+
+=item process_payment HASHREF
+
+Processes a payment and possible change of address or payment type. Takes a
+hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item amount
+
+Amount
+
+=item save
+
+If true, address and card information entered will be saved for subsequent
+transactions.
+
+=item auto
+
+If true, future credit card payments will be done automatically (sets payby to
+CARD). If false, future credit card payments will be done on-demand (sets
+payby to DCRD). This option only has meaning if B<save> is set true.
+
+=item payname
+
+Name on card
+
+=item address1
+
+Address line one
+
+=item address2
+
+Address line two
+
+=item city
+
+City
+
+=item state
+
+State
+
+=item zip
+
+Zip or postal code
+
+=item payinfo
+
+Card number
+
+=item month
+
+Card expiration month
+
+=item year
+
+Card expiration year
+
+=cut
+
+#this doesn't actually work yet
+#
+#=item paybatch
+#
+#Unique transaction identifier, returned from the payment_info function.
+#Prevents multiple charges.
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=item process_payment_order_pkg
+
+Combines the B<process_payment> and B<order_pkg> functions in one step. If the
+payment processes sucessfully, the package is ordered. Takes a hash reference
+as parameter with the keys of both methods.
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=item process_payment_order_renew
+
+Combines the B<process_payment> and B<order_renew> functions in one step. If
+the payment processes sucessfully, the renewal is processed. Takes a hash
+reference as parameter with the keys of both methods.
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=item list_pkgs
+
+Returns package information for this customer. For more detail on services,
+see L</list_svcs>.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference containing customer package information. The hash reference contains the following keys:
+
+=over 4
+
+=item custnum
+
+Customer number
+
+=item error
+
+Empty on success, or an error message on errors.
+
+=item cust_pkg HASHREF
+
+Array reference of hash references, each of which has the fields of a cust_pkg
+record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
+the internal FS:: objects, but hash references of columns and values.
+
+=over 4
+
+=item part_pkg fields
+
+All fields of part_pkg for this specific cust_pkg (be careful with this
+information - it may reveal more about your available packages than you would
+like users to know in aggregate)
+
+=cut
+
+#XXX pare part_pkg fields down to a more secure subset
+
+=item part_svc
+
+An array of hash references indicating information on unprovisioned services
+available for provisioning for this specific cust_pkg. Each has the following
+keys:
+
+=over 4
+
+=item part_svc fields
+
+All fields of part_svc (be careful with this information - it may reveal more
+about your available packages than you would like users to know in aggregate)
+
+=cut
+
+#XXX pare part_svc fields down to a more secure subset
+
+=back
+
+=item cust_svc
+
+An array of hash references indicating information on the customer services
+already provisioned for this specific cust_pkg. Each has the following keys:
+
+=over 4
+
+=item label
+
+Array reference with three elements: The first element is the name of this service. The second element is a meaningful user-specific identifier for the service (i.e. username, domain or mail alias). The last element is the table name of this service.
+
+=back
+
+=item svcnum
+
+Primary key for this service
+
+=item svcpart
+
+Service definition (see L<FS::part_svc>)
+
+=item pkgnum
+
+Customer package (see L<FS::cust_pkg>)
+
+=item overlimit
+
+Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
+
+=back
+
+=back
+
+=item list_svcs
+
+Returns service information for this customer.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference containing customer package information. The hash reference contains the following keys:
+
+=over 4
+
+=item custnum
+
+Customer number
+
+=item svcs
+
+An array of hash references indicating information on all of this customer's
+services. Each has the following keys:
+
+=over 4
+
+=item svcnum
+
+Primary key for this service
+
+=item label
+
+Name of this service
+
+=item value
+
+Meaningful user-specific identifier for the service (i.e. username, domain, or
+mail alias).
+
+=back
+
+Account (svc_acct) services also have the following keys:
+
+=over 4
+
+=item username
+
+Username
+
+=item email
+
+username@domain
+
+=item seconds
+
+Seconds remaining
+
+=item upbytes
+
+Upload bytes remaining
+
+=item downbytes
+
+Download bytes remaining
+
+=item totalbytes
+
+Total bytes remaining
+
+=item recharge_amount
+
+Cost of a recharge
+
+=item recharge_seconds
+
+Number of seconds gained by recharge
+
+=item recharge_upbytes
+
+Number of upload bytes gained by recharge
+
+=item recharge_downbytes
+
+Number of download bytes gained by recharge
+
+=item recharge_totalbytes
+
+Number of total bytes gained by recharge
+
+=back
+
+=back
+
+=item order_pkg
+
+Orders a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgpart
+
+Package to order (see L<FS::part_pkg>).
+
+=item svcpart
+
+Service to order (see L<FS::part_svc>).
+
+Normally optional; required only to provision a non-svc_acct service, or if the
+package definition does not contain one svc_acct service definition with
+quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
+also be specified to indicate that no initial service should be provisioned.
+
+=back
+
+Fields used when provisioning an svc_acct service:
+
+=over 4
+
+=item username
+
+Username
+
+=item _password
+
+Password
+
+=item sec_phrase
+
+Optional security phrase
+
+=item popnum
+
+Optional Access number number
+
+=back
+
+Fields used when provisioning an svc_domain service:
+
+=over 4
+
+=item domain
+
+Domain
+
+=back
+
+Fields used when provisioning an svc_phone service:
+
+=over 4
+
+=item phonenum
+
+Phone number
+
+=item pin
+
+Voicemail PIN
+
+=item sip_password
+
+SIP password
+
+=back
+
+Fields used when provisioning an svc_external service:
+
+=over 4
+
+=item id
+
+External numeric ID.
+
+=item title
+
+External text title.
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors. The special error '_decline' is returned for
+declined transactions.
+
+=item renew_info
+
+Provides useful info for early renewals.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=back
+
+Returns a hash reference. On errors, it contains a single key, B<error>, with
+the error message. Otherwise, contains a single key, B<dates>, pointing to
+an array refernce of hash references. Each hash reference contains the
+following keys:
+
+=over 4
+
+=item bill_date
+
+(Future) Bill date. Indicates a future date for which billing could be run.
+Specified as a integer UNIX timestamp. Pass this value to the B<order_renew>
+function.
+
+=item bill_date_pretty
+
+(Future) Bill date as a human-readable string. (Convenience for display;
+subject to change, so best not to parse for the date.)
+
+=item amount
+
+Base amount which will be charged if renewed early as of this date.
+
+=item renew_date
+
+Renewal date; i.e. even-futher future date at which the customer will be paid
+through if the early renewal is completed with the given B<bill-date>.
+Specified as a integer UNIX timestamp.
+
+=item renew_date_pretty
+
+Renewal date as a human-readable string. (Convenience for display;
+subject to change, so best not to parse for the date.)
+
+=back
+
+=item order_renew
+
+Renews this customer early; i.e. runs billing for this customer in advance.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item date
+
+Integer date as returned by the B<renew_info> function, indicating the advance
+date for which to run billing.
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=item cancel_pkg
+
+Cancels a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgpart
+
+pkgpart of package to cancel
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=back
+
+=head1 SIGNUP FUNCTIONS
+
+=over 4
+
+=item signup_info HASHREF
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id - Optional agent/reseller interface session
+
+=back
+
+Returns a hash reference containing information that may be useful in
+displaying a signup page. The hash reference contains the following keys:
+
+=over 4
+
+=item cust_main_county
+
+County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>). Note these are not FS::cust_main_county objects, but hash references of columns and values.
+
+=item part_pkg
+
+Available packages - array reference of hash references, each of which has the fields of a part_pkg record (see L<FS::part_pkg>). Each hash reference also has an additional 'payby' field containing an array reference of acceptable payment types specific to this package (see below and L<FS::part_pkg/payby>). Note these are not FS::part_pkg objects, but hash references of columns and values. Requires the 'signup_server-default_agentnum' configuration value to be set, or
+an agentnum specified explicitly via reseller interface session_id in the
+options.
+
+=item agent
+
+Array reference of hash references, each of which has the fields of an agent record (see L<FS::agent>). Note these are not FS::agent objects, but hash references of columns and values.
+
+=item agentnum2part_pkg
+
+Hash reference; keys are agentnums, values are array references of available packages for that agent, in the same format as the part_pkg arrayref above.
+
+=item svc_acct_pop
+
+Access numbers - array reference of hash references, each of which has the fields of an svc_acct_pop record (see L<FS::svc_acct_pop>). Note these are not FS::svc_acct_pop objects, but hash references of columns and values.
+
+=item security_phrase
+
+True if the "security_phrase" feature is enabled
+
+=item payby
+
+Array reference of acceptable payment types for signup
+
+=over 4
+
+=item CARD
+
+credit card - automatic
+
+=item DCRD
+
+credit card - on-demand - version 1.5+ only
+
+=item CHEK
+
+electronic check - automatic
+
+=item DCHK
+
+electronic check - on-demand - version 1.5+ only
+
+=item LECB
+
+Phone bill billing
+
+=item BILL
+
+billing, not recommended for signups
+
+=item COMP
+
+free, definitely not recommended for signups
+
+=item PREPAY
+
+special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
+
+=back
+
+=item cvv_enabled
+
+True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
+
+=item msgcat
+
+Hash reference of message catalog values, to support error message customization. Currently available keys are: passwords_dont_match, invalid_card, unknown_card_type, and not_a (as in "Not a Discover card"). Values are configured in the web interface under "View/Edit message catalog".
+
+=item statedefault
+
+Default state
+
+=item countrydefault
+
+Default country
+
+=back
+
+=item new_customer HASHREF
+
+Creates a new customer. Takes a hash reference as parameter with the
+following keys:
+
+=over 4
+
+=item first
+
+first name (required)
+
+=item last
+
+last name (required)
+
+=item ss
+
+(not typically collected; mostly used for ACH transactions)
+
+=item company
+
+Company name
+
+=item address1 (required)
+
+Address line one
+
+=item address2
+
+Address line two
+
+=item city (required)
+
+City
+
+=item county
+
+County
+
+=item state (required)
+
+State
+
+=item zip (required)
+
+Zip or postal code
+
+=item daytime
+
+Daytime phone number
+
+=item night
+
+Evening phone number
+
+=item fax
+
+Fax number
+
+=item payby
+
+CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
+
+=item payinfo
+
+Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
+
+=item paycvv
+
+Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
+
+=item paydate
+
+Expiration date for CARD/DCRD
+
+=item payname
+
+Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
+
+=item invoicing_list
+
+comma-separated list of email addresses for email invoices. The special value 'POST' is used to designate postal invoicing (it may be specified alone or in addition to email addresses),
+
+=item referral_custnum
+
+referring customer number
+
+=item agentnum
+
+Agent number
+
+=item pkgpart
+
+pkgpart of initial package
+
+=item username
+
+Username
+
+=item _password
+
+Password
+
+=item sec_phrase
+
+Security phrase
+
+=item popnum
+
+Access number (index, not the literal number)
+
+=item countrycode
+
+Country code (to be provisioned as a service)
+
+=item phonenum
+
+Phone number (to be provisioned as a service)
+
+=item pin
+
+Voicemail PIN
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors. The special error '_decline' is returned for declined transactions; other error messages should be suitable for display to the user (and are customizable in under Configuration | View/Edit message catalog)
+
+=back
+
+=item regionselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item selected_county
+
+Currently selected county
+
+=item selected_state
+
+Currently selected state
+
+=item selected_country
+
+Currently selected country
+
+=item prefix
+
+Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
+
+=item onchange
+
+Specify a javascript subroutine to call on changes
+
+=item default_state
+
+Default state
+
+=item default_country
+
+Default country
+
+=item locales
+
+An arrayref of hash references specifying regions. Normally you can just pass the value of the I<cust_main_county> field returned by B<signup_info>.
+
+=back
+
+Returns a list consisting of three HTML fragments for county selection,
+state selection and country selection, respectively.
+
+=cut
+
+#false laziness w/FS::cust_main_county (this is currently the "newest" version)
+sub regionselector {
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ $param = { @_ };
+ }
+ $param->{'selected_country'} ||= $param->{'default_country'};
+ $param->{'selected_state'} ||= $param->{'default_state'};
+
+ my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
+
+ my $countyflag = 0;
+
+ my %cust_main_county;
+
+# unless ( @cust_main_county ) { #cache
+ #@cust_main_county = qsearch('cust_main_county', {} );
+ #foreach my $c ( @cust_main_county ) {
+ foreach my $c ( @{ $param->{'locales'} } ) {
+ #$countyflag=1 if $c->county;
+ $countyflag=1 if $c->{county};
+ #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
+ #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
+ $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
+ }
+# }
+ $countyflag=1 if $param->{selected_county};
+
+ my $script_html = <<END;
+ <SCRIPT>
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+ function ${prefix}country_changed(what) {
+ country = what.options[what.selectedIndex].text;
+ for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
+ what.form.${prefix}state.options[i] = null;
+END
+ #what.form.${prefix}state.options[0] = new Option('', '', false, true);
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ my $text = $state || '(n/a)';
+ $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
+ }
+ $script_html .= "}\n";
+ }
+
+ $script_html .= <<END;
+ }
+ function ${prefix}state_changed(what) {
+END
+
+ if ( $countyflag ) {
+ $script_html .= <<END;
+ state = what.options[what.selectedIndex].text;
+ country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
+ for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
+ what.form.${prefix}county.options[i] = null;
+END
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ $script_html .= "\nif ( state == \"$state\" ) {\n";
+ #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
+ foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
+ my $text = $county || '(n/a)';
+ $script_html .=
+ qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
+ }
+ $script_html .= "}\n";
+ }
+ $script_html .= "}\n";
+ }
+ }
+
+ $script_html .= <<END;
+ }
+ </SCRIPT>
+END
+
+ my $county_html = $script_html;
+ if ( $countyflag ) {
+ $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
+ $county_html .= '</SELECT>';
+ } else {
+ $county_html .=
+ qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
+ }
+
+ my $state_html = qq!<SELECT NAME="${prefix}state" !.
+ qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
+ foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
+ my $text = $state || '(n/a)';
+ my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
+ $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
+ }
+ $state_html .= '</SELECT>';
+
+ my $country_html = '';
+ if ( scalar( keys %cust_main_county ) > 1 ) {
+
+ $country_html = qq(<SELECT NAME="${prefix}country" ).
+ qq(onChange="${prefix}country_changed(this); ).
+ $param->{'onchange'}.
+ '"'.
+ '>';
+ my $countrydefault = $param->{default_country} || 'US';
+ foreach my $country (
+ sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
+ keys %cust_main_county
+ ) {
+ my $selected = $country eq $param->{'selected_country'}
+ ? ' SELECTED'
+ : '';
+ $country_html .= "\n<OPTION$selected>$country</OPTION>"
+ }
+ $country_html .= '</SELECT>';
+ } else {
+
+ $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
+ ' VALUE="'. (keys %cust_main_county )[0]. '">';
+
+ }
+
+ ($county_html, $state_html, $country_html);
+
+}
+
+sub regionselector_hashref {
+ my ($county_html, $state_html, $country_html) = regionselector(@_);
+ {
+ 'county_html' => $county_html,
+ 'state_html' => $state_html,
+ 'country_html' => $country_html,
+ };
+}
+
+#=item expselect HASHREF | LIST
+#
+#Takes as input a hashref or list of key/value pairs with the following keys:
+#
+#=over 4
+#
+#=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
+#
+#=item date - current date, in yyyy-mm-dd or m-d-yyyy format
+#
+#=back
+
+=item expselect PREFIX [ DATE ]
+
+Takes as input a unique prefix string and the current expiration date, in
+yyyy-mm-dd or m-d-yyyy format
+
+Returns an HTML fragments for expiration date selection.
+
+=cut
+
+sub expselect {
+ #my $param;
+ #if ( ref($_[0]) ) {
+ # $param = shift;
+ #} else {
+ # $param = { @_ };
+ #my $prefix = $param->{'prefix'};
+ #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
+ #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
+ my $prefix = shift;
+ my $date = scalar(@_) ? shift : '';
+
+ my( $m, $y ) = ( 0, 0 );
+ if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
+ ( $m, $y ) = ( $2, $1 );
+ } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+ ( $m, $y ) = ( $1, $3 );
+ }
+ my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
+ for ( 1 .. 12 ) {
+ $return .= qq!<OPTION VALUE="$_"!;
+ $return .= " SELECTED" if $_ == $m;
+ $return .= ">$_";
+ }
+ $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
+ my @t = localtime;
+ my $thisYear = $t[5] + 1900;
+ for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
+ $return .= qq!<OPTION VALUE="$_"!;
+ $return .= " SELECTED" if $_ == $y;
+ $return .= ">$_";
+ }
+ $return .= "</SELECT>";
+
+ $return;
+}
+
+=item popselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item popnum
+
+Access number number
+
+=item pops
+
+An arrayref of hash references specifying access numbers. Normally you can just pass the value of the I<svc_acct_pop> field returned by B<signup_info>.
+
+=back
+
+Returns an HTML fragment for access number selection.
+
+=cut
+
+#horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
+sub popselector {
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ $param = { @_ };
+ }
+ my $popnum = $param->{'popnum'};
+ my $pops = $param->{'pops'};
+
+ return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
+ return $pops->[0]{city}. ', '. $pops->[0]{state}.
+ ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
+ '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
+ if scalar(@$pops) == 1;
+
+ my %pop = ();
+ my %popnum2pop = ();
+ foreach (@$pops) {
+ push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
+ $popnum2pop{$_->{popnum}} = $_;
+ }
+
+ my $text = <<END;
+ <SCRIPT>
+ function opt(what,href,text) {
+ var optionName = new Option(text, href, false, false)
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+END
+
+ my $init_popstate = $param->{'init_popstate'};
+ if ( $init_popstate ) {
+ $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
+ $init_popstate. '">';
+ } else {
+ $text .= <<END;
+ function acstate_changed(what) {
+ state = what.options[what.selectedIndex].text;
+ what.form.popac.options.length = 0
+ what.form.popac.options[0] = new Option("Area code", "-1", false, true);
+END
+ }
+
+ my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
+ foreach my $state ( sort { $a cmp $b } @states ) {
+ $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
+
+ foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
+ $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
+ if ($ac eq $param->{'popac'}) {
+ $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
+ }
+ }
+ $text .= "}\n" unless $init_popstate;
+ }
+ $text .= "popac_changed(what.form.popac)}\n";
+
+ $text .= <<END;
+ function popac_changed(what) {
+ ac = what.options[what.selectedIndex].text;
+ what.form.popnum.options.length = 0;
+ what.form.popnum.options[0] = new Option("City", "-1", false, true);
+
+END
+
+ foreach my $state ( @states ) {
+ foreach my $popac ( keys %{ $pop{$state} } ) {
+ $text .= "\nif ( ac == \"$popac\" ) {\n";
+
+ foreach my $pop ( @{$pop{$state}->{$popac}}) {
+ my $o_popnum = $pop->{popnum};
+ my $poptext = $pop->{city}. ', '. $pop->{state}.
+ ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
+
+ $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
+ if ($popnum == $o_popnum) {
+ $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
+ }
+ }
+ $text .= "}\n";
+ }
+ }
+
+
+ $text .= "}\n</SCRIPT>\n";
+
+ $text .=
+ qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
+ qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
+ $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
+ ">$_" foreach sort { $a cmp $b } @states;
+ $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
+
+ $text .=
+ qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
+ qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
+
+ $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
+
+
+ #comment this block to disable initial list polulation
+ my @initial_select = ();
+ if ( scalar( @$pops ) > 100 ) {
+ push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
+ } else {
+ @initial_select = @$pops;
+ }
+ foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
+ $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
+ ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
+ $pop->{city}. ', '. $pop->{state}.
+ ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
+ }
+
+ $text .= qq!</SELECT></TD></TR></TABLE>!;
+
+ $text;
+
+}
+
+=item domainselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item pkgnum
+
+Package number
+
+=item domsvc
+
+Service number of the selected item.
+
+=back
+
+Returns an HTML fragment for domain selection.
+
+=cut
+
+sub domainselector {
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ $param = { @_ };
+ }
+ my $domsvc= $param->{'domsvc'};
+ my $rv =
+ domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
+ my $domains = $rv->{'domains'};
+ $domsvc = $rv->{'domsvc'} unless $domsvc;
+
+ return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
+ unless scalar(keys %$domains);
+
+ if (scalar(keys %$domains) == 1) {
+ my $key;
+ foreach(keys %$domains) {
+ $key = $_;
+ }
+ return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
+ '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
+ }
+
+ my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em"><OPTION>(Choose Domain)!;
+
+
+ foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
+ $text .= qq!<OPTION VALUE="!. $domain. '"'.
+ ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
+ $domains->{$domain};
+ }
+
+ $text .= qq!</SELECT></TD></TR>!;
+
+ $text;
+
+}
+
+=item didselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item field
+
+Field name for the returned HTML fragment.
+
+=item svcpart
+
+Service definition (see L<FS::part_svc>)
+
+=back
+
+Returns an HTML fragment for DID selection.
+
+=cut
+
+sub didselector {
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ $param = { @_ };
+ }
+
+ my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
+ 'args'=>[ %$param ],
+ );
+
+ #hmm.
+ $rv->{'error'} || $rv->{'output'};
+
+}
+
+=back
+
+=head1 RESELLER FUNCTIONS
+
+Note: Resellers can also use the B<signup_info> and B<new_customer> functions
+with their active session, and the B<customer_info> and B<order_pkg> functions
+with their active session and an additional I<custnum> parameter.
+
+For the most part, development of the reseller web interface has been
+superceded by agent-virtualized access to the backend.
+
+=over 4
+
+=item agent_login
+
+Agent login
+
+=item agent_info
+
+Agent info
+
+=item agent_list_customers
+
+List agent's customers.
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
+
+=cut
+
+1;
+
diff --git a/fs_selfservice/FS-SelfService/SelfService/FreeRadiusVoip.pm b/fs_selfservice/FS-SelfService/SelfService/FreeRadiusVoip.pm
new file mode 100644
index 0000000..0df24f7
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/SelfService/FreeRadiusVoip.pm
@@ -0,0 +1,61 @@
+#Add this to the modules section of radiusd.conf
+# perl {
+# #path to this module
+# module=/usr/local/share/perl/5.8.8/FS/SelfService/FreeRadiusVoip.pm
+# func_authorize = authorize;
+# }
+#
+#In the Authorize section
+#Make sure that you have 'files' uncommented. Then add a line containing 'perl'
+# after it.
+#
+# #N/A# Add a line containing 'perl' to the Accounting section.
+#
+# and on debian systems, add this to /etc/init.d/freeradius, with the
+# correct path (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=416266)
+# LD_PRELOAD=/usr/lib/libperl.so.5.8.8
+# export LD_PRELOAD
+
+BEGIN { $FS::SelfService::skip_uid_check = 1; }
+
+use strict;
+use vars qw(%RAD_REQUEST %RAD_REPLY %RAD_CHECK);
+#use Data::Dumper;
+use FS::SelfService qw(call_time);
+
+use constant RLM_MODULE_REJECT=> 0; #immediately reject the request
+use constant RLM_MODULE_FAIL=> 1; #module failed, don't reply
+use constant RLM_MODULE_OK=> 2; #the module is OK, continue
+use constant RLM_MODULE_HANDLED=> 3; #the module handled the request, so stop
+use constant RLM_MODULE_INVALID=> 4; #the module considers the request invalid
+use constant RLM_MODULE_USERLOCK=> 5; #reject the request (user is locked out)
+use constant RLM_MODULE_NOTFOUND=> 6; #user not found
+use constant RLM_MODULE_NOOP=> 7; #module succeeded without doing anything
+use constant RLM_MODULE_UPDATED=> 8; #OK (pairs modified)
+use constant RLM_MODULE_NUMCODES=> 9; #How many return codes there are
+
+sub authorize {
+
+ #&log_request_attributes();
+
+ my $response = call_time( 'src' => $RAD_REQUEST{'Calling-Station-Id'},
+ 'dst' => $RAD_REQUEST{'Called-Station-Id'}, );
+
+ if ( $response->{'error'} ) {
+ $RAD_REPLY{'Reply-Message'} = $response->{'error'};
+ return RLM_MODULE_REJECT;
+ } else {
+ $RAD_REPLY{'Session-Timeout'} = $response->{'seconds'};
+ return RLM_MODULE_OK;
+ }
+
+}
+
+sub log_request_attributes {
+ # This shouldn't be done in production environments!
+ # This is only meant for debugging!
+ for (keys %RAD_REQUEST) {
+ &radiusd::radlog(1, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
+ }
+}
+
diff --git a/fs_selfservice/FS-SelfService/SelfService/XMLRPC.pm b/fs_selfservice/FS-SelfService/SelfService/XMLRPC.pm
new file mode 100644
index 0000000..4e0d3e9
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/SelfService/XMLRPC.pm
@@ -0,0 +1,88 @@
+package FS::SelfService::XMLRPC;
+
+=head1 NAME
+
+FS::SelfService::XMLRPC - Freeside XMLRPC accessible self-service API
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Use this API to implement your own client "self-service" module vi XMLRPC.
+
+Each routine described in L<FS::SelfService> is available vi XMLRPC as the
+method FS.SelfService.XMLRPC.B<method>. All values are passed to the
+selfservice-server in a struct of strings. The return values are in a
+struct as strings, arrays, or structs as appropriate for the values
+described in L<FS::SelfService>.
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<freeside-selfservice-clientd>, L<freeside-selfservice-server>,L<FS::SelfService>
+
+=cut
+
+use strict;
+use vars qw($DEBUG $AUTOLOAD);
+use FS::SelfService;
+
+$DEBUG = 0;
+$FS::SelfService::DEBUG = $DEBUG;
+
+sub AUTOLOAD {
+ my $call = $AUTOLOAD;
+ $call =~ s/^FS::SelfService::XMLRPC:://;
+ if (exists($FS::SelfService::autoload{$call})) {
+ shift; #discard package name;
+ $call = "FS::SelfService::$call";
+ no strict 'refs';
+ &{$call}(@_);
+ }else{
+ die "No such procedure: $call";
+ }
+}
+
+package SOAP::Transport::HTTP::Daemon; # yuck
+
+use POSIX qw(:sys_wait_h);
+
+no warnings 'redefine';
+
+sub handle {
+ my $self = shift->new;
+
+ local $SIG{CHLD} = 'IGNORE';
+
+ACCEPT:
+ while (my $c = $self->accept) {
+
+ my $kid = 0;
+ do {
+ $kid = waitpid(-1, WNOHANG);
+ warn "found kid $kid";
+ } while $kid > 0;
+
+ my $pid = fork;
+ next ACCEPT if $pid;
+
+ if ( not defined $pid ) {
+ warn "fork() failed: $!";
+ $c = undef;
+ } else {
+ while (my $r = $c->get_request) {
+ $self->request($r);
+ $self->SUPER::handle;
+ $c->send_response($self->response);
+ }
+ # replaced ->close, thanks to Sean Meisner <Sean.Meisner@VerizonWireless.com>
+ # shutdown() doesn't work on AIX. close() is used in this case. Thanks to Jos Clijmans <jos.clijmans@recyfin.be>
+ UNIVERSAL::isa($c, 'shutdown') ? $c->shutdown(2) : $c->close();
+ $c->close;
+ }
+ exit;
+ }
+}
+
+1;
diff --git a/fs_selfservice/FS-SelfService/cgi/ach_payment_results.html b/fs_selfservice/FS-SelfService/cgi/ach_payment_results.html
new file mode 100644
index 0000000..62419d1
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/ach_payment_results.html
@@ -0,0 +1,13 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Payment results</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error processing your payment: $error</FONT>!;
+} else {
+ $OUT .= 'Your payment was processed successfully. Thank you.';
+} %>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent.cgi b/fs_selfservice/FS-SelfService/cgi/agent.cgi
new file mode 100644
index 0000000..6e8de61
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent.cgi
@@ -0,0 +1,458 @@
+#!/usr/bin/perl -T
+#!/usr/bin/perl -Tw
+
+#some false laziness w/selfservice.cgi
+
+use strict;
+use vars qw($DEBUG $me $cgi $session_id $form_max $template_dir);
+use subs qw(do_template);
+use CGI;
+use CGI::Carp qw(fatalsToBrowser);
+use Business::CreditCard;
+use Text::Template;
+#use HTML::Entities;
+use FS::SelfService qw( agent_login agent_logout agent_info
+ agent_list_customers
+ signup_info new_customer
+ customer_info list_pkgs order_pkg
+ part_svc_info provision_acct provision_external
+ unprovision_svc
+ );
+
+$DEBUG = 0;
+$me = 'agent.cgi:';
+
+$template_dir = '.';
+
+$form_max = 255;
+
+warn "$me starting\n" if $DEBUG;
+
+warn "$me initializing CGI\n" if $DEBUG;
+$cgi = new CGI;
+
+unless ( defined $cgi->param('session') ) {
+ warn "$me no session defined, sending login page\n" if $DEBUG;
+ do_template('agent_login',{});
+ exit;
+}
+
+if ( $cgi->param('session') eq 'login' ) {
+
+ warn "$me processing login\n" if $DEBUG;
+
+ $cgi->param('username') =~ /^\s*([a-z0-9_\-\.\&]{0,$form_max})\s*$/i
+ or die "illegal username";
+ my $username = $1;
+
+ $cgi->param('password') =~ /^(.{0,$form_max})$/
+ or die "illegal password";
+ my $password = $1;
+
+ my $rv = agent_login(
+ 'username' => $username,
+ 'password' => $password,
+ );
+ if ( $rv->{error} ) {
+ do_template('agent_login', {
+ 'error' => $rv->{error},
+ 'username' => $username,
+ } );
+ exit;
+ } else {
+ $cgi->param('session' => $rv->{session_id} );
+ $cgi->param('action' => 'agent_main' );
+ }
+}
+
+$session_id = $cgi->param('session');
+
+warn "$me checking action\n" if $DEBUG;
+$cgi->param('action') =~
+ /^(agent_main|signup|process_signup|list_customers|view_customer|agent_provision|provision_svc|process_svc_acct|process_svc_external|delete_svc|agent_order_pkg|process_order_pkg|logout)$/
+ or die "unknown action ". $cgi->param('action');
+my $action = $1;
+
+warn "$me running $action\n" if $DEBUG;
+my $result = eval "&$action();";
+die $@ if $@;
+
+if ( $result->{error} eq "Can't resume session" ) { #ick
+ do_template('agent_login',{});
+ exit;
+}
+
+warn "$me processing template $action\n" if $DEBUG;
+do_template($action, {
+ 'session_id' => $session_id,
+ %{$result}
+});
+warn "$me done processing template $action\n" if $DEBUG;
+
+#--
+
+sub logout {
+ $action = 'agent_logout';
+ agent_logout( 'session_id' => $session_id );
+}
+
+sub agent_main { agent_info( 'session_id' => $session_id ); }
+
+sub signup { signup_info( 'session_id' => $session_id ); }
+
+sub process_signup {
+
+ my $init_data = signup_info( 'session_id' => $session_id );
+ if ( $init_data->{'error'} ) {
+ if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+ do_template('agent_login',{});
+ exit;
+ } else { #?
+ die $init_data->{'error'};
+ }
+ }
+
+ my $error = '';
+
+ #false laziness w/signup.cgi, identical except for agentnum vs session_id
+ my $payby = $cgi->param('payby');
+ if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+ #$payinfo = join('@', map { $cgi->param( $payby. "_payinfo$_" ) } (1,2) );
+ $cgi->param('payinfo' => $cgi->param($payby. '_payinfo1'). '@'.
+ $cgi->param($payby. '_payinfo2')
+ );
+ } else {
+ $cgi->param('payinfo' => $cgi->param( $payby. '_payinfo' ) );
+ }
+ $cgi->param('paydate' => $cgi->param( $payby. '_month' ). '-'.
+ $cgi->param( $payby. '_year' )
+ );
+ $cgi->param('payname' => $cgi->param( $payby. '_payname' ) );
+ $cgi->param('paycvv' => defined $cgi->param( $payby. '_paycvv' )
+ ? $cgi->param( $payby. '_paycvv' )
+ : ''
+ );
+
+ if ( $cgi->param('invoicing_list') ) {
+ $cgi->param('invoicing_list' => $cgi->param('invoicing_list'). ', POST')
+ if $cgi->param('invoicing_list_POST');
+ } else {
+ $cgi->param('invoicing_list' => 'POST' );
+ }
+
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ $error = $init_data->{msgcat}{passwords_dont_match}; #msgcat
+ $cgi->param('_password', '');
+ $cgi->param('_password2', '');
+ }
+
+ if ( $payby =~ /^(CARD|DCRD)$/ && $cgi->param('CARD_type') ) {
+ my $payinfo = $cgi->param('payinfo');
+ $payinfo =~ s/\D//g;
+
+ $payinfo =~ /^(\d{13,16})$/
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ $payinfo = $1;
+ validate($payinfo)
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ cardtype($payinfo) eq $cgi->param('CARD_type')
+ or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
+ }
+
+ unless ( $error ) {
+ my $rv = new_customer ( {
+ 'session_id' => $session_id,
+ map { $_ => scalar($cgi->param($_)) }
+ qw( last first ss company
+ address1 address2 city county state zip country
+ daytime night fax
+
+ ship_last ship_first ship_company
+ ship_address1 ship_address2 ship_city ship_county ship_state
+ ship_zip ship_country
+ ship_daytime ship_night ship_fax
+
+ payby payinfo paycvv paydate payname invoicing_list
+ referral_custnum promo_code reg_code
+ pkgpart username sec_phrase _password popnum refnum
+ ),
+ grep { /^snarf_/ } $cgi->param
+ } );
+ $error = $rv->{'error'};
+ }
+ #eslaf
+
+ if ( $error ) {
+ $action = 'signup';
+ my $r = {
+ $cgi->Vars,
+ %{$init_data},
+ 'error' => $error,
+ };
+ #warn join('\n', map "$_ => $r->{$_}", keys %$r )."\n";
+ $r;
+ } else {
+ $action = 'agent_main';
+ my $agent_info = agent_info( 'session_id' => $session_id );
+ $agent_info->{'message'} = 'Signup successful';
+ $agent_info;
+ }
+
+}
+
+sub list_customers {
+
+ my $results =
+ agent_list_customers( 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) }
+ grep defined($cgi->param($_)),
+ qw(prospect active susp cancel),
+ 'search',
+ );
+
+ if ( scalar( @{$results->{'customers'}} ) == 1 ) {
+ $action = 'view_customer';
+ customer_info (
+ 'agent_session_id' => $session_id,
+ 'custnum' => $results->{'customers'}[0]{'custnum'},
+ );
+ } else {
+ $results;
+ }
+
+}
+
+sub view_customer {
+
+ #my $init_data = signup_info( 'session_id' => $session_id );
+ #if ( $init_data->{'error'} ) {
+ # if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+ # do_template('agent_login',{});
+ # exit;
+ # } else { #?
+ # die $init_data->{'error'};
+ # }
+ #}
+ #
+ #my $customer_info =
+ customer_info (
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ );
+ #
+ #return {
+ # ( map { $_ => $init_data->{$_} }
+ # qw( part_pkg security_phrase svc_acct_pop ),
+ # ),
+ # %$customer_info,
+ #};
+}
+
+sub agent_order_pkg {
+
+ my $init_data = signup_info( 'session_id' => $session_id );
+ if ( $init_data->{'error'} ) {
+ if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+ do_template('agent_login',{});
+ exit;
+ } else { #?
+ die $init_data->{'error'};
+ }
+ }
+
+ my $customer_info = customer_info (
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ );
+
+ return {
+ ( map { $_ => $init_data->{$_} }
+ qw( part_pkg security_phrase svc_acct_pop ),
+ ),
+ %$customer_info,
+ };
+
+}
+
+sub agent_provision {
+ my $result = list_pkgs(
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+ $result;
+}
+
+sub provision_svc {
+
+ my $result = part_svc_info(
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart custnum ),
+ );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+
+ $result->{'svcdb'} =~ /^svc_(.*)$/
+ #or return { 'error' => 'Unknown svcdb '. $result->{'svcdb'} };
+ or die 'Unknown svcdb '. $result->{'svcdb'};
+ $action .= "_$1";
+ $action = "agent_$action";
+
+ $result;
+}
+
+sub process_svc_acct {
+
+ my $result = provision_acct (
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw(
+ custnum pkgnum svcpart username _password _password2 sec_phrase popnum )
+ );
+
+ if ( exists $result->{'error'} && $result->{'error'} ) {
+ #warn "$result $result->{'error'}";
+ $action = 'provision_svc_acct';
+ $action = "agent_$action";
+ return {
+ $cgi->Vars,
+ %{ part_svc_info( 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw(pkgnum svcpart custnum)
+ )
+ },
+ 'error' => $result->{'error'},
+ };
+ } else {
+ #warn "$result $result->{'error'}";
+ $action = 'agent_provision';
+ return {
+ %{agent_provision()},
+ 'message' => $result->{'svc'}. ' setup successfully.',
+ };
+ }
+
+}
+
+sub process_svc_external {
+
+ my $result = provision_external (
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( custnum pkgnum svcpart )
+ );
+
+ #warn "$result $result->{'error'}";
+ $action = 'agent_provision';
+ return {
+ %{agent_provision()},
+ 'message' => $result->{'error'}
+ ? '<FONT COLOR="#FF0000">'. $result->{'error'}. '</FONT>'
+ : $result->{'svc'}. ' setup successfully'.
+ ': serial number '.
+ sprintf('%010d', $result->{'id'}). '-'. $result->{'title'}
+ };
+
+}
+
+sub delete_svc {
+ my $result = unprovision_svc(
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ 'svcnum' => $cgi->param('svcnum'),
+ );
+
+ $action = 'agent_provision';
+
+ return {
+ %{agent_provision()},
+ 'message' => $result->{'error'}
+ ? '<FONT COLOR="#FF0000">'. $result->{'error'}. '</FONT>'
+ : $result->{'svc'}. ' removed.'
+ };
+
+}
+
+sub process_order_pkg {
+
+ my $results = '';
+
+ unless ( length($cgi->param('_password')) ) {
+ my $init_data = signup_info( 'session_id' => $session_id );
+ #die $init_data->{'error'} if $init_data->{'error'};
+ $results = { 'error' => $init_data->{msgcat}{empty_password} };
+ }
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ my $init_data = signup_info( 'session_id' => $session_id );
+ $results = { 'error' => $init_data->{msgcat}{passwords_dont_match} };
+ $cgi->param('_password', '');
+ $cgi->param('_password2', '');
+ }
+
+ $results ||= order_pkg (
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) }
+ qw( custnum pkgpart username _password _password2 sec_phrase popnum )
+ );
+
+ if ( $results->{'error'} ) {
+ $action = 'agent_order_pkg';
+ return {
+ $cgi->Vars,
+ %{agent_order_pkg()},
+ #'message' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ 'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ };
+ } else {
+ $action = 'view_customer';
+ #$cgi->delete( grep { $_ ne 'custnum' } $cgi->param );
+ return {
+ %{view_customer()},
+ 'message' => 'Package order successful.',
+ };
+ }
+
+}
+
+#--
+
+sub do_template {
+ my $name = shift;
+ my $fill_in = shift;
+ #warn join(' / ', map { "$_=>".$fill_in->{$_} } keys %$fill_in). "\n";
+
+ $cgi->delete_all();
+ $fill_in->{'selfurl'} = $cgi->self_url; #OLD
+ $fill_in->{'self_url'} = $cgi->self_url;
+ $fill_in->{'cgi'} = \$cgi;
+
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => "$template_dir/$name.html",
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1, )
+ or die $Text::Template::ERROR;
+
+ local $^W = 0;
+ print $cgi->header( '-expires' => 'now' ),
+ $template->fill_in( PACKAGE => 'FS::SelfService::_agentcgi',
+ HASH => $fill_in
+ );
+}
+
+package FS::SelfService::_agentcgi;
+
+use HTML::Entities;
+use FS::SelfService qw(regionselector expselect popselector);
+
+#false laziness w/selfservice.cgi
+sub include {
+ my $name = shift;
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => "$main::template_dir/$name.html",
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1,
+ )
+ or die $Text::Template::ERROR;
+
+ $template->fill_in( PACKAGE => 'FS::SelfService::_agentcgi',
+ #HASH => $fill_in
+ );
+
+}
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html b/fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html
new file mode 100644
index 0000000..603fc0b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html
@@ -0,0 +1,7 @@
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+<A HREF="<%= $url %>agent_provision">Setup services</A><BR><BR>
+<A HREF="<%= $url %>agent_order_pkg">Purchase additional package</A><BR><BR>
+
+</TD>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html b/fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html
new file mode 100644
index 0000000..63fa127
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html
@@ -0,0 +1,17 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= $small_custview %>
+<BR>
+<%= if ( $error ) {
+
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT>!;
+} else {
+ $OUT .= "<FONT SIZE=4>$svc removed.</FONT>";
+} %>
+
+</TD></TR></TABLE>
+
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_login.html b/fs_selfservice/FS-SelfService/cgi/agent_login.html
new file mode 100644
index 0000000..4b0778e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_login.html
@@ -0,0 +1,22 @@
+<HTML><HEAD><TITLE>Reseller Login</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=5>Reseller Login</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="login">
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+<TR>
+ <TH ALIGN="right">Username </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>">
+ </TD>
+</TR>
+<TR>
+ <TH ALIGN="right">Password </TH>
+ <TD>
+ <INPUT TYPE="password" NAME="password">
+ </TD>
+</TR>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" VALUE="Login">
+</FORM></BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_logout.html b/fs_selfservice/FS-SelfService/cgi/agent_logout.html
new file mode 100644
index 0000000..9809467
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_logout.html
@@ -0,0 +1,5 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+You have been logged out.
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_main.html b/fs_selfservice/FS-SelfService/cgi/agent_main.html
new file mode 100644
index 0000000..3aefd61
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_main.html
@@ -0,0 +1,33 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%= $message
+ ? "<FONT SIZE=\"+2\"><B>$message</B></FONT>"
+ : "Hello $agent!"
+%><BR><BR>
+
+<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">
+<TR><TH BGCOLOR="#cccccc">Customer summary</TH></TR>
+<TR><TD BGCOLOR="#dddddd">
+
+ <B><%= $num_prospect %></B>
+ <%= $num_prospect ? qq!<A HREF="${url}list_customers;prospect=1">! : '' %>prospects</A>
+
+ <BR><FONT COLOR="#00CC00"><B><%= $num_active %></B></FONT>
+ <%= $num_active ? qq!<A HREF="${url}list_customers;active=1">! : '' %>active</A>
+
+ <BR><FONT COLOR="#FF9900"><B><%= $num_susp %></B></FONT>
+ <%= $num_susp ? qq!<A HREF="${url}list_customers;susp=1">! : '' %>suspended</A>
+
+ <BR><FONT COLOR="#FF0000"><B><%= $num_cancel %></B></FONT>
+ <%= $num_cancel ? qq!<A HREF="${url}list_customers;cancel=1">! : '' %>cancelled</A>
+
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_menu.html b/fs_selfservice/FS-SelfService/cgi/agent_menu.html
new file mode 100644
index 0000000..84a2953
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_menu.html
@@ -0,0 +1,15 @@
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+
+<A HREF="<%= $url %>agent_main">Overview</A><BR><BR>
+<A HREF="<%= $url %>signup">New customer<!--/prospect--></A><BR><BR>
+<FORM ACTION="<%= $selfurl %>">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="list_customers">
+<INPUT TYPE="text" NAME="search" SIZE=20><BR>
+<SMALL><I>cust&nbsp;#,&nbsp;last&nbsp;name,&nbsp;or&nbsp;company</I></SMALL><BR>
+<INPUT TYPE="submit" VALUE="Search customers"><BR>
+</FORM>
+<A HREF="<%= $url %>logout">Logout</A><BR><BR>
+
+</TD>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html b/fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html
new file mode 100644
index 0000000..18a37e8
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html
@@ -0,0 +1,18 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+<%= $small_custview %>
+<BR>
+
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+<%= include('order_pkg') %>
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_provision.html b/fs_selfservice/FS-SelfService/cgi/agent_provision.html
new file mode 100644
index 0000000..f7f39b5
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_provision.html
@@ -0,0 +1,23 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%= $message
+ ? "<FONT SIZE=\"+2\"><B>$message</B></FONT><BR><BR>"
+ : ''
+%>
+
+<%= $small_custview %>
+<BR>
+
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+<%= include('provision_list') %>
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html b/fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html
new file mode 100644
index 0000000..a867edb
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html
@@ -0,0 +1,16 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+<%= $small_custview %>
+<BR>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+<%= include('svc_acct') %>
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/bill.html b/fs_selfservice/FS-SelfService/cgi/bill.html
new file mode 100644
index 0000000..1b59027
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/bill.html
@@ -0,0 +1,15 @@
+<TR>
+ <TD ALIGN="right">P.O.&nbsp;number</TD>
+ <TD><INPUT TYPE="text" NAME="payinfo" SIZE=10 MAXLENGTH=20 VALUE="<%=$payinfo%>"></TD>
+</TR><TR>
+ <TD ALIGN="right">Attention</TD>
+ <TD><INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="payname" VALUE="<%=$payname%>"></TD>
+</TR><TR>
+ <TD><INPUT TYPE="checkbox" NAME="postal_invoicing" VALUE="POST" <%=
+ $postal_invoicing ? 'CHECKED' : ''
+ %>></TD>
+ <TD>Postal mail invoice</TD>
+</TR><TR>
+ <TD>Email address(es)</TD>
+ <TD><INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(',', $invoicing_list ) %>"></TD>
+</TR>
diff --git a/fs_selfservice/FS-SelfService/cgi/card.html b/fs_selfservice/FS-SelfService/cgi/card.html
new file mode 100644
index 0000000..cf6d20d
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/card.html
@@ -0,0 +1,73 @@
+<TR>
+ <TD ALIGN="right">Card&nbsp;number</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="payinfo" SIZE=20 MAXLENGTH=19 VALUE="<%=$payinfo%>"> </TD>
+ <TD>Exp.</TD>
+ <TD>
+ <SELECT NAME="month">
+ <%= for ( ( map "0$_", 1 .. 9 ), 10 .. 12 ) {
+ $OUT .= '<OPTION'. ($_ == $month ? ' SELECTED' : ''). ">$_\n";
+ } %>
+ </SELECT>
+ </TD>
+ <TD> / </TD>
+ <TD>
+ <SELECT NAME="year">
+ <%= my @a = localtime; for ( $a[5]+1900 .. $a[5]+1915 ) {
+ $OUT .= '<OPTION'. ($_ == $year ? ' SELECTED' : ''). ">$_\n";
+ } %>
+ </SELECT>
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+</TR>
+<%=
+ if ( $withcvv ) {
+ $OUT .= qq!<TR>!;
+ $OUT .= qq!<TD ALIGN="right">CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)</TD>!;
+ $OUT .= qq!<TD><INPUT TYPE="text" NAME="paycvv" VALUE="" SIZE=4 MAXLENGTH=4></TD>!;
+ $OUT .= qq!</TR>!;
+ }
+ '';
+%>
+<TR>
+ <TD ALIGN="right">Exact&nbsp;name&nbsp;on&nbsp;card</TD>
+ <TD><INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="payname" VALUE="<%=$payname%>"></TD>
+</TR><TR>
+ <TD ALIGN="right">Card&nbsp;billing&nbsp;address</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address1" VALUE="<%=$address1%>">
+ </TD>
+</TR><TR>
+ <TD ALIGN="right">Address&nbsp;line&nbsp;2</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address2" VALUE="<%=$address2%>">
+ </TD>
+</TR><TR>
+ <TD ALIGN="right">City</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="city" SIZE="12" MAXLENGTH=80 VALUE="<%=$city%>">
+ </TD>
+ <TD>State</TD>
+ <TD>
+ <SELECT NAME="state">
+ <%= for ( @states ) {
+ $OUT .= '<OPTION'. ($_ eq $state ? ' SELECTED' : '' ). ">$_\n";
+ } %>
+ </SELECT>
+ </TD>
+ <TD>Zip</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="zip" SIZE=11 MAXLENGTH=10 VALUE="<%=$zip%>">
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+</TR>
diff --git a/fs_selfservice/FS-SelfService/cgi/change_bill.html b/fs_selfservice/FS-SelfService/cgi/change_bill.html
new file mode 100755
index 0000000..f186c9b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/change_bill.html
@@ -0,0 +1,23 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee">
+<FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Edit billing address</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT><BR><BR>!;
+} ''; %>
+
+<FORM NAME="ChangeBillForm" ACTION="<%= $selfurl %>" METHOD=POST onSubmit="document.bottomform.submit.disabled=true;">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_change_bill">
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<%= $r=qq!<font color="#ff0000">*</font>&nbsp;!; include('contact') %>
+
+<INPUT TYPE="submit" NAME="submit" VALUE="<%= $custnum ? "Apply Changes" : "Add Customer" %>">
+<BR>
+</FORM>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/change_password.html b/fs_selfservice/FS-SelfService/cgi/change_password.html
new file mode 100644
index 0000000..dcfce31
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/change_password.html
@@ -0,0 +1,51 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Change password</FONT><BR><BR>
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+
+<FORM ACTION="<%= $selfurl %>" METHOD="POST">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_change_password">
+
+<TABLE BGCOLOR="#cccccc">
+
+ <TR>
+ <TH ALIGN="right">Change password for account: </TH>
+ <TD>
+ <SELECT NAME="svcnum">
+ <%= foreach my $svc ( @svcs ) {
+ $OUT .= '<OPTION VALUE="'. $svc->{'svcnum'}. '"'.
+ ( $svc->{'svcnum'} eq $svcnum ? ' SELECTED' : '' ). '>'.
+ $svc->{'label'}. ': '. $svc->{'value'}. "\n";
+ }
+ %>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">New password: </TH>
+ <TD><INPUT TYPE="password" NAME="new_password" SIZE="18"></TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">Re-enter new password: </TH>
+ <TD><INPUT TYPE="password" NAME="new_password2" SIZE="18"></TD>
+ </TR>
+
+</TABLE>
+<BR>
+
+<INPUT TYPE="submit" VALUE="Change password">
+
+</FORM>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/change_pay.html b/fs_selfservice/FS-SelfService/cgi/change_pay.html
new file mode 100644
index 0000000..2bea955
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/change_pay.html
@@ -0,0 +1,73 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Change payment information</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT><BR><BR>!;
+ } ''; %>
+
+<FORM NAME="OneTrueForm" METHOD="POST" ACTION="<%=$selfurl%>" onSubmit="document.OneTrueForm.process.disabled=true">
+<%=
+ use Tie::IxHash;
+ use HTML::Widgets::SelectLayers;
+
+ my $preauto = '<TR><TD COLSPAN=3><INPUT TYPE="checkbox" NAME="auto" VALUE="1"';
+ my $postauto = '>Charge future payments to this card automatically</TD></TR>';
+
+ my $tail = qq(</TABLE><INPUT TYPE="hidden" NAME="session" VALUE="$session_id">).
+ qq(<INPUT TYPE="hidden" NAME="action" VALUE="process_change_pay">).
+ qq(<BR>).
+ qq(<INPUT TYPE="submit" NAME="process" ).
+ qq(VALUE="Save payment information"> ).
+ qq(<!-- onClick="this.disabled=true"> -->);
+
+
+ my %paybychecked = (
+ 'BILL' => include('bill'),
+ 'CARD' => include('card')."$preauto CHECKED $postauto",
+ 'DCRD' => include('card')."$preauto $postauto",
+ 'CHEK' => include('check')."$preauto CHECKED $postauto",
+ 'DCHK' => include('check')."$preauto $postauto",
+ );
+ my %payby_index = ( 'CARD' => qq/Credit Card/,
+ 'DCRD' => qq/Credit Card/,
+ 'CHEK' => qq/Check/,
+ 'DCHK' => qq/Check/,
+ 'LECB' => qq/Phone Bill Billing/,
+ 'BILL' => qq/Billing/,
+ 'COMP' => qq/Complimentary/,
+ 'PREPAY' => qq/Prepaid Card/,
+ );
+ tie my %options, 'Tie::IxHash', ();
+ foreach my $payby_option ( @paybys ) {
+ $options{$payby_option} = $payby_index{$payby_option};
+ }
+ $options{$payby} = $payby_index{$payby}
+ unless exists($options{$payby});
+
+ HTML::Widgets::SelectLayers->new(
+ options => \%options,
+ selected_layer => $payby,
+# form_name => 'dummy',
+# form_action => 'dummy.cgi',
+ layer_callback => sub { my $layer = shift; return '<TABLE BGCOLOR="#cccccc">'.$paybychecked{$layer}.qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$layer">$tail!; },
+ )->html;
+
+%>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/change_pkg.html b/fs_selfservice/FS-SelfService/cgi/change_pkg.html
new file mode 100644
index 0000000..a841308
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/change_pkg.html
@@ -0,0 +1,37 @@
+<SCRIPT TYPE="text/javascript">
+function enable_change_pkg () {
+ if ( document.ChangePkgForm.pkgpart.selectedIndex > 0 ) {
+ document.ChangePkgForm.submit.disabled = false;
+ } else {
+ document.ChangePkgForm.submit.disabled = true;
+ }
+}
+</SCRIPT>
+<FONT SIZE=4>Purchase replacement package for "<%= $pkg; %>"</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+<FORM NAME="ChangePkgForm" ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_change_pkg">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>">
+<INPUT TYPE="hidden" NAME="pkg" VALUE="<%= $pkg %>">
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart" onChange="enable_change_pkg()">
+ <OPTION VALUE="">
+
+ <%=
+ foreach my $part_pkg ( @part_pkg ) {
+ $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $part_pkg->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+</TABLE>
+<INPUT NAME="submit" TYPE="submit" VALUE="Purchase" disabled>
+</FORM>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/change_ship.html b/fs_selfservice/FS-SelfService/cgi/change_ship.html
new file mode 100755
index 0000000..28ee94e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/change_ship.html
@@ -0,0 +1,102 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee">
+<FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Edit service address</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT><BR><BR>!;
+} ''; %>
+
+<FORM NAME="OneTrueForm" ACTION="<%= $selfurl %>" METHOD=POST onSubmit="document.bottomform.submit.disabled=true;">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_change_ship">
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<%=
+ foreach (
+ qw( last first company address1 address2 city county state zip country
+ daytime night fax )
+ ) {
+ $OUT .= qq!<INPUT TYPE="hidden" NAME="$_" VALUE="${$_}">!;
+ };
+ '';
+%>
+<SCRIPT>
+function bill_changed(what) {
+ if ( what.form.same.checked ) {
+<%=
+ for (qw( last first company address1 address2 city zip daytime night fax )) {
+ $OUT .= "what.form.ship_$_.value = what.form.$_.value;";
+ }
+ '';
+%>
+ what.form.ship_country.selectedIndex = what.form.country.selectedIndex;
+
+ function fix_ship_county() {
+ what.form.ship_county.selectedIndex = what.form.county.selectedIndex;
+ }
+
+ function fix_ship_state() {
+ what.form.ship_state.selectedIndex = what.form.state.selectedIndex;
+ ship_state_changed(what.form.ship_state, fix_ship_county );
+ }
+
+ ship_country_changed(what.form.ship_country, fix_ship_state );
+
+ }
+}
+function samechanged(what) {
+ if ( what.checked ) {
+ bill_changed(what);
+
+<%=
+ for (qw( last first company address1 address2 city county state zip country daytime night fax )) {
+ $OUT .= "what.form.ship_$_.disabled = true;";
+ $OUT .= "what.form.ship_$_.style.backgroundColor = '#dddddd';";
+ }
+ if ( $require_address2 ) {
+ $OUT .= "document.getElementById('ship_address2_required').style.visibility = 'hidden';";
+ $OUT .= "document.getElementById('ship_address2_label').style.visibility = 'hidden';";
+ }
+%>
+
+ } else {
+
+<%=
+ for (qw( last first company address1 address2 city county state zip country daytime night fax )) {
+ $OUT .= "what.form.ship_$_.disabled = false;";
+ $OUT .= "what.form.ship_$_.style.backgroundColor = '#ffffff';";
+ }
+ if ( $require_address2 ) {
+ $OUT .= "document.getElementById('ship_address2_required').style.visibility = '';";
+ $OUT .= "document.getElementById('ship_address2_label').style.visibility = '';";
+ }
+%>
+ }
+}
+</SCRIPT>
+(<INPUT TYPE="checkbox" NAME="same" VALUE="Y" onClick="samechanged(this)"
+ <%= (!$ship_last || $cgi->param('same') eq 'Y') ? 'CHECKED' : '' %>
+ >same as billing address)
+<%= $r=qq!<font color="#ff0000">*</font>&nbsp;!;
+ if (!$ship_last || $cgi->param('same') eq 'Y') {
+ $disabled = 'DISABLED STYLE="background-color: #dddddd"';
+ foreach ( qw( last first company address1 address2 city county state
+ zip country daytime night fax )
+ ) {
+ ${"ship_$_"} = ${$_};
+ }
+ }else{
+ $disabled = '';
+ }
+ $pre = 'ship_';
+ include('contact');
+%>
+
+<INPUT TYPE="submit" NAME="submit" VALUE="<%= $custnum ? "Apply Changes" : "Add Customer" %>">
+<BR>
+</FORM>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/check.html b/fs_selfservice/FS-SelfService/cgi/check.html
new file mode 100644
index 0000000..68753fe
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/check.html
@@ -0,0 +1,54 @@
+<TR>
+ <TD ALIGN="right">Account&nbsp;type</TD>
+ <TD>
+ <SELECT NAME="paytype">
+ <%= foreach ( @paytypes ) {
+ $selected = $paytype eq $_ ? ' SELECTED' : '';
+ $OUT .= qq(<OPTION$selected VALUE="$_">$_\n);
+ } %>
+ </SELECT>
+ </TD>
+</TD><TR>
+ <TD ALIGN="right">Account&nbsp;number</TD>
+ <TD><INPUT TYPE="text" NAME="payinfo1" SIZE=10 MAXLENGTH=20 VALUE="<%=$payinfo1%>"></TD>
+</TD><TR>
+ <TD ALIGN="right">ABA/Routing&nbsp;number</TD>
+ <TD><INPUT TYPE="text" NAME="payinfo2" SIZE=10 MAXLENGTH=9 VALUE="<%=$payinfo2%>"></TD>
+</TR><TR>
+ <TD ALIGN="right">Bank&nbsp;name</TD>
+ <TD><INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="payname" VALUE="<%=$payname%>"></TD>
+</TR><TR>
+ <%=
+ $OUT = '';
+ if ($show_paystate) {
+ $OUT .= qq!<TD ALIGN="right">Bank state</TD><TD><SELECT NAME="paystate">!;
+ for ( @states ) {
+ $OUT .= '<OPTION'. ($_ eq $paystate ? ' SELECTED' : '' ). ">$_\n";
+ }
+ $OUT .= '</SELECT></TD></TR><TR>';
+ }
+ %>
+ <%=
+ $OUT = '';
+ if ($show_ss) {
+ $OUT .= '<TD ALIGN="right">Account&nbsp;holder<BR>Social&nbsp;';
+ $OUT .= 'security&nbsp;or&nbsp;tax&nbsp;ID&nbsp;#</TD><TD>';
+ $OUT .= qq!<INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="ss" VALUE="$ss">!;
+ $OUT .= '</TD></TR><TR>';
+ }
+ %>
+ <%=
+ $OUT = '';
+ if ($show_stateid) {
+ $OUT .= '<TD ALIGN="right">';
+ $OUT .= qq!Account&nbsp;holder<BR>$stateid_label</TD><TD>!;
+ $OUT .= qq!<INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="stateid" VALUE="$stateid"></TD>!;
+ $OUT .= qq!<TD ALIGN="right">$stateid_state_label</TD>!;
+ $OUT .= '<TD><SELECT NAME="stateid_state">';
+ for ( @states ) {
+ $OUT .= '<OPTION'. ($_ eq $stateid_state ? ' SELECTED' : '' ). ">$_\n";
+ }
+ $OUT .='</SELECT></TD></TR><TR>';
+ }
+ %>
+</TR>
diff --git a/fs_selfservice/FS-SelfService/cgi/contact.html b/fs_selfservice/FS-SelfService/cgi/contact.html
new file mode 100644
index 0000000..20c15df
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/contact.html
@@ -0,0 +1,135 @@
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<TR>
+ <TH ALIGN="right"><%=$r%>Contact&nbsp;name<BR>(last,&nbsp;first)</TH>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%=$pre%>last" VALUE="<%= ${$pre.'last'} %>" onChange="<%= $onchange %>" <%=$disabled%>> ,
+ <INPUT TYPE="text" NAME="<%=$pre%>first" VALUE="<%= ${$pre.'first'} %>" onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=7>
+ <INPUT TYPE="text" NAME="<%=$pre%>company" VALUE="<%= ${$pre.'company'} %>" SIZE=70 onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right"><%=$r%>Address</TH>
+ <TD COLSPAN=7>
+ <INPUT TYPE="text" NAME="<%=$pre%>address1" VALUE="<%= ${$pre.'address1'} %>" SIZE=70 onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">
+ <%=
+ my $style =
+ ( $disabled
+ || !$require_address2
+ || ( !$pre && $ship_last )
+ )
+ ? 'visibility:hidden'
+ : '';
+
+ $OUT .= qq!<FONT ID="${pre}address2_required" color="#ff0000" STYLE="$style">*</FONT>&nbsp;<FONT ID="${pre}address2_label" STYLE="$style"><B>Unit&nbsp;#</B></FONT>!;
+ %>
+ </TD>
+ <TD COLSPAN=7>
+ <INPUT TYPE="text" NAME="<%=$pre%>address2" VALUE="<%= ${$pre.'address2'} %>" SIZE=70 onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right"><%=$r%>City</TH>
+ <TD>
+ <INPUT TYPE="text" ID="<%=$pre%>city" NAME="<%=$pre%>city" VALUE="<%= ${$pre.'city'} %>" onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ FS::SelfService::regionselector( {
+ prefix => $pre,
+ selected_county => ${$pre.'county'},
+ selected_state => ${$pre.'state'},
+ selected_country => ${$pre.'country'},
+ default_state => $statedefault,
+ default_country => $countrydefault,
+ locales => \@cust_main_county,
+ } );
+
+ $OUT .= qq!<TH ALIGN="right">${r}State/County</TH>!;
+ $OUT .= qq!<TD>$county_html $state_html</TD>!;
+ $OUT .= qq!<TH>${r}Zip</TH>!;
+ $OUT .= qq!<TD><INPUT TYPE="text" NAME="${pre}zip" VALUE="${$pre.'zip'}" SIZE=10 onChange="$onchange" $disabled></TD>!;
+ $OUT .= qq!</TR>!;
+ $OUT .= qq!<TR>!;
+ $OUT .= qq!<TH ALIGN="right">${r}Country</TH>!;
+ $OUT .= qq!<TD COLSPAN=5>$country_html</TD>!;
+ %>
+</TR>
+
+<SCRIPT>
+ <%=
+ if ( $disabled ) {
+ $OUT .= qq!var what = document.getElementById("${pre}city");!;
+ for (qw( county state country ) ) {
+ $OUT .= "what.form.$pre$_.disabled = true;";
+ $OUT .= "what.form.$pre$_.style.backgroundColor = '#dddddd';";
+ }
+ }else{
+ '';
+ }
+ %>
+</SCRIPT>
+
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%=$pre%>daytime" VALUE="<%= ${$pre.'daytime'} %>" SIZE=18 onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%=$pre%>night" VALUE="<%= ${$pre.'night'} %>" SIZE=18 onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%=$pre%>fax" VALUE="<%= ${$pre.'fax'} %>" SIZE=12 onChange="<%= $onchange %>" <%=$disabled%>>
+ </TD>
+</TR>
+
+</TABLE>
+<%=$r%>required fields<BR>
+
+<!--
+#my($county_html, $state_html, $country_html) =
+# FS::cust_main_county::regionselector( $cust_main->get($pre.'county'),
+# $cust_main->get($pre.'state'),
+# $cust_main->get($pre.'country'),
+# $pre,
+# $onchange,
+# $disabled,
+# );
+
+my %select_hash = (
+ 'county' => ${$pre.'county'},
+ 'state' => ${$pre.'state'},
+ 'country' => ${$pre.'country'},
+ 'prefix' => $pre,
+ 'onchange' => $onchange,
+ 'disabled' => $disabled,
+);
+
+my @counties = counties( ${$pre.'state'},
+ ${$pre.'country'},
+ );
+my $county_style = scalar(@counties) > 1 ? '' : 'STYLE="visibility:hidden"';
+
+my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
+-->
diff --git a/fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi b/fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi
new file mode 100644
index 0000000..5f344a3
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi
@@ -0,0 +1,19 @@
+#!/usr/bin/perl -T
+#!/usr/bin/perl -Tw
+
+use strict;
+use CGI;
+use FS::SelfService qw( invoice_logo );
+
+my $cgi = new CGI;
+
+my($query) = $cgi->keywords;
+$query =~ /^([^\.\/]*)$/ or '' =~ /^()$/;
+my $templatename = $1;
+my $hashref = invoice_logo('templatename' => $templatename);
+
+print $cgi->header( '-type' => $hashref->{'content_type'},
+ '-expires' => 'now',
+ ).
+ $hashref->{'logo'};
+
diff --git a/fs_selfservice/FS-SelfService/cgi/customer_change_pkg.html b/fs_selfservice/FS-SelfService/cgi/customer_change_pkg.html
new file mode 100644
index 0000000..46d3faf
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/customer_change_pkg.html
@@ -0,0 +1,8 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= include('change_pkg') %>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/customer_order_pkg.html b/fs_selfservice/FS-SelfService/cgi/customer_order_pkg.html
new file mode 100755
index 0000000..78cc16c
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/customer_order_pkg.html
@@ -0,0 +1,8 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= include('order_pkg') %>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2.html b/fs_selfservice/FS-SelfService/cgi/cvv2.html
new file mode 100644
index 0000000..b178c85
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/cvv2.html
@@ -0,0 +1,25 @@
+<HTML>
+ <HEAD>
+ <TITLE>
+ CVV2 information
+ </TITLE>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+ security code used to reduce credit card fraud.<BR><BR>
+ <TABLE BORDER=0 CELLSPACING=4>
+ <TR>
+ <TH>Visa / MasterCard / Discover</TH>
+ <TH>American Express</TH>
+ </TR>
+ <TR>
+ <TD>
+ <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="cvv2.png">
+ </TD>
+ <TD>
+ <IMG BORDER=0 ALT="American Express" SRC="cvv2_amex.png">
+ </TD>
+ </TABLE>
+ <CENTER><A HREF="javascript:close()">(close window)</A></CENTER>
+ </BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2.png b/fs_selfservice/FS-SelfService/cgi/cvv2.png
new file mode 100644
index 0000000..4610dcb
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/cvv2.png
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png b/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png
new file mode 100644
index 0000000..21c36a0
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/decline.html b/fs_selfservice/FS-SelfService/cgi/decline.html
new file mode 100644
index 0000000..a37ba3a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/decline.html
@@ -0,0 +1,5 @@
+<HTML><HEAD><TITLE>Processing error</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Processing error</FONT><BR><BR>
+There has been an error processing your account. Please contact customer
+support.
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/delete_svc.html b/fs_selfservice/FS-SelfService/cgi/delete_svc.html
new file mode 100644
index 0000000..4155d09
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/delete_svc.html
@@ -0,0 +1,14 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT>!;
+} else {
+ $OUT .= "<FONT SIZE=4>$svc removed.</FONT>";
+} %>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/footer.html b/fs_selfservice/FS-SelfService/cgi/footer.html
new file mode 100644
index 0000000..98cc79b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/footer.html
@@ -0,0 +1,3 @@
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.freeside.biz/freeside">freeside</a></FONT>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/images/cross.png b/fs_selfservice/FS-SelfService/cgi/images/cross.png
new file mode 100644
index 0000000..1514d51
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/images/cross.png
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/images/wait-orange.gif b/fs_selfservice/FS-SelfService/cgi/images/wait-orange.gif
new file mode 100644
index 0000000..92c7f34
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/images/wait-orange.gif
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/list_customers.html b/fs_selfservice/FS-SelfService/cgi/list_customers.html
new file mode 100644
index 0000000..7fe7fa4
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/list_customers.html
@@ -0,0 +1,36 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%=
+ if ( @customers ) {
+ $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#cccccc" COLSPAN=3>Customers</TH><TD>';
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $customer ( @customers ) {
+ my $td = qq!<TD BGCOLOR="#$col">!;
+ my $a = qq!<A HREF="${url}view_customer;custnum=!.
+ $customer->{'custnum'}. '">';
+ $OUT .=
+ '<TR>'.
+ "$td<FONT COLOR=\"". $customer->{'statuscolor'}. '">'.
+ ucfirst($customer->{'status'}). "</TD>". "$td</TD>".
+ "$td$a". $customer->{'name'}. "</A></TD>".
+ '</TR>';
+ #"$td</TD>".
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+ $OUT .= '</TABLE>';
+ } else {
+ $OUT .= 'No customers.<BR><BR>';
+ }
+%>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/login.html b/fs_selfservice/FS-SelfService/cgi/login.html
new file mode 100644
index 0000000..e5daec8
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/login.html
@@ -0,0 +1,85 @@
+<HTML><HEAD><TITLE>Login</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=5>Login</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="login">
+
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+
+<TR>
+ <TH ALIGN="right">Username </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"><%= $single_domain ? '@'.$single_domain : '' %>
+ </TD>
+</TR>
+
+<%=
+if ( $single_domain ) {
+
+ $OUT .= qq(<INPUT TYPE="hidden" NAME="domain" VALUE="$single_domain">);
+
+} else {
+
+ $OUT .= qq(
+ <TR>
+ <TH ALIGN="right">Domain </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="domain" VALUE="$domain">
+ </TD>
+ </TR>
+ );
+
+}
+
+%>
+
+<TR>
+ <TH ALIGN="right">Password </TH>
+ <TD>
+ <INPUT TYPE="password" NAME="password">
+ </TD>
+</TR>
+<TR>
+ <TD COLSPAN=2 ALIGN="center"><INPUT TYPE="submit" VALUE="Login"></TD>
+</TR>
+</TABLE>
+</FORM>
+
+<%=
+
+if ( $phone_login ) {
+
+ $OUT .= qq(
+
+ <B>OR</B><BR><BR>
+
+ <FORM ACTION="$self_url" METHOD=POST>
+ <INPUT TYPE="hidden" NAME="session" VALUE="login">
+ <TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+ <TR>
+ <TH ALIGN="right">Phone number </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="$username">
+ </TD>
+ </TR>
+ <INPUT TYPE="hidden" NAME="domain" VALUE="svc_phone">
+ <TR>
+ <TH ALIGN="right">PIN </TH>
+ <TD>
+ <INPUT TYPE="password" NAME="password">
+ </TD>
+ </TR>
+ <TR>
+ <TD COLSPAN=2 ALIGN="center"><INPUT TYPE="submit" VALUE="Login"></TD>
+ </TR>
+ </TABLE>
+ </FORM>
+
+ );
+
+}
+
+%>
+
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/logout.html b/fs_selfservice/FS-SelfService/cgi/logout.html
new file mode 100644
index 0000000..0e774e9
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/logout.html
@@ -0,0 +1,5 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+You have been logged out.
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/make_ach_payment.html b/fs_selfservice/FS-SelfService/cgi/make_ach_payment.html
new file mode 100644
index 0000000..2394c10
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/make_ach_payment.html
@@ -0,0 +1,58 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Make a payment</FONT><BR><BR>
+<FORM NAME="OneTrueForm" METHOD="POST" ACTION="<%=$selfurl%>" onSubmit="document.OneTrueForm.process.disabled=true">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%=$session_id%>">
+<INPUT TYPE="hidden" NAME="action" VALUE="ach_payment_results">
+<TABLE BGCOLOR="#cccccc">
+<TR>
+ <TD ALIGN="right">Amount&nbsp;Due</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<%=sprintf("%.2f",$balance)%>
+ </TD></TR></TABLE>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Payment&nbsp;amount</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<INPUT TYPE="text" NAME="amount" SIZE=8 VALUE="<%=sprintf("%.2f",$balance)%>">
+ </TD></TR></TABLE>
+ </TD>
+</TR>
+<%= include('check') %>
+<TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox" CHECKED NAME="save" VALUE="1">
+ Remember this information
+ </TD>
+</TR><TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox"<%= $payby eq 'CHEK' ? ' CHECKED' : '' %> NAME="auto" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.save.checked=true; }">
+ Charge future payments to this account automatically
+ </TD>
+</TR>
+</TABLE>
+<BR>
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="<%=$paybatch%>">
+<INPUT TYPE="submit" NAME="process" VALUE="Process payment"> <!-- onClick="this.disabled=true"> -->
+</FORM>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/make_payment.html b/fs_selfservice/FS-SelfService/cgi/make_payment.html
new file mode 100644
index 0000000..a468d99
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/make_payment.html
@@ -0,0 +1,68 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Make a payment</FONT><BR><BR>
+<FORM NAME="OneTrueForm" METHOD="POST" ACTION="<%=$selfurl%>" onSubmit="document.OneTrueForm.process.disabled=true">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%=$session_id%>">
+<INPUT TYPE="hidden" NAME="action" VALUE="payment_results">
+<TABLE BGCOLOR="#cccccc">
+<TR>
+ <TD ALIGN="right">Amount&nbsp;Due</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<%=sprintf("%.2f",$balance)%>
+ </TD></TR></TABLE>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Payment&nbsp;amount</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<INPUT TYPE="text" NAME="amount" SIZE=8 VALUE="<%=sprintf("%.2f",$balance)%>">
+ </TD></TR></TABLE>
+ </TD>
+</TR><TR>
+ <TD ALIGN="right">Card&nbsp;type</TD>
+ <TD>
+ <SELECT NAME="card_type"><OPTION></OPTION>
+ <%= foreach ( keys %card_types ) {
+ $selected = $card_type eq $card_types{$_} ? ' SELECTED' : '';
+ $OUT .= qq(<OPTION$selected VALUE="). $card_types{$_}. qq(">$_\n);
+ } %>
+ </SELECT>
+ </TD>
+</TR>
+<%= include('card') %>
+<TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox" CHECKED NAME="save" VALUE="1">
+ Remember this information
+ </TD>
+</TR><TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox"<%= $payby eq 'CARD' ? ' CHECKED' : '' %> NAME="auto" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.save.checked=true; }">
+ Charge future payments to this card automatically
+ </TD>
+</TR>
+</TABLE>
+<BR>
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="<%=$paybatch%>">
+<INPUT TYPE="submit" NAME="process" VALUE="Process payment"> <!-- onClick="this.disabled=true"> -->
+</FORM>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/map.gif b/fs_selfservice/FS-SelfService/cgi/map.gif
new file mode 100644
index 0000000..ef884d8
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/map.gif
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/misc/areacodes.cgi b/fs_selfservice/FS-SelfService/cgi/misc/areacodes.cgi
new file mode 100755
index 0000000..b33e58c
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/misc/areacodes.cgi
@@ -0,0 +1,18 @@
+#!/usr/bin/perl -w
+
+use strict;
+use CGI;
+use FS::SelfService qw( mason_comp );
+
+my $cgi = new CGI;
+
+my $rv = mason_comp( 'comp' => '/misc/areacodes.cgi',
+ 'query_string' => $cgi->query_string, #pass CGI params...
+ );
+
+#hmm.
+my $output = $rv->{'error'} || $rv->{'output'};
+
+print $cgi->header( '-expires' => 'now' ).
+ $output;
+
diff --git a/fs_selfservice/FS-SelfService/cgi/misc/exchanges.cgi b/fs_selfservice/FS-SelfService/cgi/misc/exchanges.cgi
new file mode 100755
index 0000000..d8df970
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/misc/exchanges.cgi
@@ -0,0 +1,18 @@
+#!/usr/bin/perl -w
+
+use strict;
+use CGI;
+use FS::SelfService qw( mason_comp );
+
+my $cgi = new CGI;
+
+my $rv = mason_comp( 'comp' => '/misc/exchanges.cgi',
+ 'query_string' => $cgi->query_string, #pass CGI params...
+ );
+
+#hmm.
+my $output = $rv->{'error'} || $rv->{'output'};
+
+print $cgi->header( '-expires' => 'now' ).
+ $output;
+
diff --git a/fs_selfservice/FS-SelfService/cgi/misc/phonenums.cgi b/fs_selfservice/FS-SelfService/cgi/misc/phonenums.cgi
new file mode 100755
index 0000000..e7d695d
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/misc/phonenums.cgi
@@ -0,0 +1,18 @@
+#!/usr/bin/perl -w
+
+use strict;
+use CGI;
+use FS::SelfService qw( mason_comp );
+
+my $cgi = new CGI;
+
+my $rv = mason_comp( 'comp' => '/misc/phonenums.cgi',
+ 'query_string' => $cgi->query_string, #pass CGI params...
+ );
+
+#hmm.
+my $output = $rv->{'error'} || $rv->{'output'};
+
+print $cgi->header( '-expires' => 'now' ).
+ $output;
+
diff --git a/fs_selfservice/FS-SelfService/cgi/myaccount.html b/fs_selfservice/FS-SelfService/cgi/myaccount.html
new file mode 100644
index 0000000..cb5ed35
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/myaccount.html
@@ -0,0 +1,94 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+Hello <%= $name %>!<BR><BR>
+<%= $small_custview %>
+<BR>
+<%= if ( $balance > 0 ) {
+ $OUT .= qq! <B><A HREF="${url}make_payment">Make a payment</A></B><BR><BR>!;
+} %>
+<%=
+ if ( @open_invoices ) {
+ $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#ff6666" COLSPAN=5>Open Invoices</TH></TR>';
+ my $link = qq!<A HREF="<%= $url %>myaccount!;
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $invoice ( @open_invoices ) {
+ my $td = qq!<TD BGCOLOR="#$col">!;
+ my $a=qq!<A HREF="${url}view_invoice;invnum=!. $invoice->{'invnum'}. '">';
+ $OUT .=
+ "<TR>$td${a}Invoice #". $invoice->{'invnum'}. "</A></TD>$td</TD>".
+ "$td$a". $invoice->{'date'}. "</A></TD>$td</TD>".
+ qq!<TD BGCOLOR="#$col" ALIGN="right">$a\$!. $invoice->{'owed'}.
+ '</A></TD>'.
+ '</TR>';
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+ $OUT .= '</TABLE><BR>';
+ } else {
+ $OUT .= 'You have no outstanding invoices.<BR><BR>';
+ }
+%>
+
+<%=
+ if ( @support_services ) {
+ $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#ff6666" COLSPAN="3">Support Time Remaining</TH>'.
+ '</TR><TR><TH ALIGN="left">#</TH><TH>Package</TH>'.
+ '<TH>Time Remaining</TH></TR>';
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $support ( @support_services ) {
+ my $td = qq!<TD BGCOLOR="#$col">!;
+ my $a = qq!<A HREF="${url}view_support_details;svcnum=!.
+ $support->{'svcnum'}. '">';
+ $OUT .=
+ "<TR>$td$a". $support->{'pkgnum'}. "</A></TD>".
+ $td.$a. $support->{'pkg'}. "</A></TD>".
+ $td.$a. $support->{'time'}. "</A></TD>".
+ '</TR>';
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+ $OUT .= '</TABLE><BR>';
+ } else {
+ $OUT .= '';
+ }
+%>
+
+<%=
+ if ( @tickets ) {
+ $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#ff6666" COLSPAN=5>Open Tickets</TH></TR>'.
+ '<TR><TH>#</TH><TH>Subject</TH><TH>Priority</TH><TH>Queue</TH>'.
+ '<TH>Status</TH></TR>';
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $ticket ( @tickets ) {
+ my $td = qq!<TD BGCOLOR="#$col">!;
+ $OUT .=
+ "<TR>$td". $ticket->{'id'}. "</TD>".
+ $td. $ticket->{'subject'}. "</TD>".
+ $td. ($ticket->{'content'} || $ticket->{'priority'}). "</TD>".
+ $td. $ticket->{'queue'}. "</TD>".
+ $td. $ticket->{'status'}. "</TD>".
+ '</TR>';
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+ $OUT .= '</TABLE>';
+ } else {
+ $OUT .= '';
+ }
+%>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/myaccount_menu.html b/fs_selfservice/FS-SelfService/cgi/myaccount_menu.html
new file mode 100644
index 0000000..ec5a8fa
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/myaccount_menu.html
@@ -0,0 +1,94 @@
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0><TR>
+<TD VALIGN="top" HEIGHT="100%" BGCOLOR="#dddddd">
+
+<TABLE CELLSPACING=0 BORDER=0 HEIGHT="100%">
+
+<%=
+
+my @menu = (
+{ title=>' ' },
+{ title=>'Overview', url=>'myaccount', size=>'+1', },
+{ title=>' ' },
+
+{ title=>'Purchase', size=>'+1', },
+ { title=>'Purchase additional package',
+ url=>'customer_order_pkg', 'indent'=>2 },
+);
+
+if ( 1 ) { #XXXFIXME "enable selfservice prepay features" flag or something, eventually per-pkg or something really fancy
+
+ push @menu, (
+ { title=>'Recharge my account with a credit card',
+ url=>'make_payment', indent=>2 },
+ { title=>'Recharge my account with a check',
+ url=>'make_ach_payment', indent=>2 },
+ { title=>'Recharge my account with a prepaid card',
+ url=>'recharge_prepay', indent=>2 },
+ );
+
+}
+
+push @menu, (
+
+{ title=>' ' },
+
+{ title=>'View my usage', url=>'view_usage', size=>'+1', },
+{ title=>'Setup my services', url=>'provision', size=>'+1', },
+
+{ title=>' ' },
+
+{ title=>'Change my information', size=>'+1', },
+ { title=>'Change billing address', url=>'change_bill', indent=>2 },
+ { title=>'Change service address', url=>'change_ship', indent=>2 },
+ { title=>'Change payment information', url=>'change_pay', indent=>2 },
+ { title=>'Change password(s)', url=>'change_password', indent=>2 },
+
+{ title=>' ' },
+
+{ title=>'Logout', url=>'logout', size=>'+1', },
+
+);
+
+foreach my $item ( @menu ) {
+
+ $OUT .= '<TR><TD';
+ if ( exists $item->{'url'} && $action eq $item->{'url'} ) {
+ $OUT .= ' BGCOLOR="#eeeeee" '.
+ ' STYLE="border-top: 1px solid black;'.
+ ' border-left: 1px solid black;'.
+ ' border-bottom: 1px solid black"';
+ } else {
+ $OUT .= ' STYLE="border-right: 1px solid black"';
+ }
+ $OUT.='>';
+
+ $OUT .= '<FONT SIZE="'. $item->{'size'}. '">'
+ if exists $item->{'size'};
+
+ $OUT .= '&nbsp;' x $item->{'indent'}
+ if exists $item->{'indent'};
+
+ $OUT .= '<A HREF="'. $url. $item->{'url'}. '">'
+ if exists $item->{'url'} && $action ne $item->{'url'};
+
+ $item->{'title'} =~ s/ /&nbsp;/g;
+ $OUT .= $item->{'title'};
+
+ $OUT .= '</FONT>'
+ if exists $item->{'size'};
+
+ $OUT .= '</A>'
+ if exists $item->{'url'} && $action ne $item->{'url'};
+
+ $OUT .= '</TD></TR>';
+
+}
+
+%>
+
+<TR><TD STYLE="border-right: 1px solid black" HEIGHT="100%"><BR><BR><BR><BR></TD></TR>
+
+</TABLE>
+
+</TD>
diff --git a/fs_selfservice/FS-SelfService/cgi/order_pkg.html b/fs_selfservice/FS-SelfService/cgi/order_pkg.html
new file mode 100644
index 0000000..9cdd4cd
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/order_pkg.html
@@ -0,0 +1,75 @@
+<SCRIPT TYPE="text/javascript">
+function enable_order_pkg () {
+ if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) {
+ document.OrderPkgForm.submit.disabled = false;
+ } else {
+ document.OrderPkgForm.submit.disabled = true;
+ }
+}
+</SCRIPT>
+<FONT SIZE=4>Purchase additional package</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+<FORM NAME="OrderPkgForm" ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_order_pkg">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart" onChange="enable_order_pkg()">
+ <OPTION VALUE="">
+
+ <%=
+ foreach my $part_pkg ( @part_pkg ) {
+ $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $part_pkg->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $_password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $_password2 %>"></TD>
+</TR>
+<%=
+ if ( $security_phrase ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( @svc_acct_pop ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector( 'popnum' => $popnum,
+ 'pops' => \@svc_acct_pop,
+ 'init_popstate' => $init_popstate,
+ 'popac' => $popac,
+ 'acstate' => $acstate,
+ ).
+ '</TD></TR>';
+ } else {
+ $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+ }
+%>
+</TABLE>
+<INPUT NAME="submit" TYPE="submit" VALUE="Purchase" disabled>
+</FORM>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/passwd.cgi b/fs_selfservice/FS-SelfService/cgi/passwd.cgi
new file mode 100755
index 0000000..87e5e68
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/passwd.cgi
@@ -0,0 +1,61 @@
+#!/usr/bin/perl -T
+#!/usr/bin/perl -Tw
+
+use strict;
+use Getopt::Std;
+use FS::SelfService qw(passwd);
+use CGI;
+use CGI::Carp qw(fatalsToBrowser);
+
+my $freeside_uid = scalar(getpwnam('freeside'));
+
+$ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin';
+$ENV{'SHELL'} = '/bin/sh';
+$ENV{'IFS'} = " \t\n";
+$ENV{'CDPATH'} = '';
+$ENV{'ENV'} = '';
+$ENV{'BASH_ENV'} = '';
+
+die "passwd.cgi isn't running as freeside user\n" if $> != $freeside_uid;
+
+my $cgi = new CGI;
+
+$cgi->param('username') =~ /^([^\n]{0,255}$)/ or die "Illegal username";
+my $me = $1;
+
+$cgi->param('domain') =~ /^([^\n]{0,255}$)/ or die "Illegal domain";
+my $domain = $1;
+
+$cgi->param('old_password') =~ /^([^\n]{0,255}$)/ or die "Illegal old_password";
+my $old_password = $1;
+
+$cgi->param('new_password') =~ /^([^\n]{0,255}$)/ or die "Illegal new_password";
+my $new_password = $1;
+
+die "New passwords don't match"
+ unless $new_password eq $cgi->param('new_password2');
+
+my $rv = passwd(
+ 'username' => $me,
+ 'domain' => $domain,
+ 'old_password' => $old_password,
+ 'new_password' => $new_password,
+);
+
+my $error = $rv->{error};
+
+if ($error) {
+ die $error;
+} else {
+ print $cgi->header(), <<END;
+<html>
+ <head>
+ <title>Password changed</title>
+ </head>
+ <body bgcolor="#e8e8e8">
+ <h3>Password changed</h3>
+<br>Your password has been changed.
+ </body>
+</html>
+END
+}
diff --git a/fs_selfservice/FS-SelfService/cgi/passwd.html b/fs_selfservice/FS-SelfService/cgi/passwd.html
new file mode 100644
index 0000000..459c96a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/passwd.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <title>Change password</title>
+ </head>
+ <body bgcolor="#e8e8e8">
+ <h3>Change password</h3>
+ <form action="passwd.cgi" method="post">
+ <table bgcolor="#cccccc" border=0 cellspacing=2>
+ <tr><th align="right">Username</th>
+ <td><input type="text" name="username" size="18"></td>
+ </tr>
+ <tr><th align="right">Domain</th>
+ <td><input type="text" name="domain" size="18"></td>
+ </tr>
+ <tr><th align="right">Current password</th>
+ <td><input type="password" name="old_password" size="18"></td>
+ </tr>
+ <tr><th align="right">New password</th>
+ <td><input type="password" name="new_password" size="18"></td>
+ </tr>
+ <tr><th align="right">Re-enter new password</th>
+ <td><input type="password" name="new_password2" size="18"></td>
+ </tr>
+ </table>
+ <br><input type="submit" value="Change password">
+ </body>
+</html>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/payment_results.html b/fs_selfservice/FS-SelfService/cgi/payment_results.html
new file mode 100644
index 0000000..62419d1
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/payment_results.html
@@ -0,0 +1,13 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Payment results</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error processing your payment: $error</FONT>!;
+} else {
+ $OUT .= 'Your payment was processed successfully. Thank you.';
+} %>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_change_bill.html b/fs_selfservice/FS-SelfService/cgi/process_change_bill.html
new file mode 100644
index 0000000..93e05cf
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_change_bill.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Information updated successfully.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_change_password.html b/fs_selfservice/FS-SelfService/cgi/process_change_password.html
new file mode 100644
index 0000000..bfd2312
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_change_password.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Password changed for <%= $value %> <%= $label %>.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_change_pay.html b/fs_selfservice/FS-SelfService/cgi/process_change_pay.html
new file mode 100644
index 0000000..93e05cf
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_change_pay.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Information updated successfully.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_change_pkg.html b/fs_selfservice/FS-SelfService/cgi/process_change_pkg.html
new file mode 100644
index 0000000..7c0f0a6
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_change_pkg.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Package change successful.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_change_ship.html b/fs_selfservice/FS-SelfService/cgi/process_change_ship.html
new file mode 100644
index 0000000..93e05cf
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_change_ship.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Information updated successfully.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_order_pkg.html b/fs_selfservice/FS-SelfService/cgi/process_order_pkg.html
new file mode 100755
index 0000000..3e4471d
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_order_pkg.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Package order successful.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_order_recharge.html b/fs_selfservice/FS-SelfService/cgi/process_order_recharge.html
new file mode 100644
index 0000000..ef0516a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_order_recharge.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4><%= $svc %> recharged successfully.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_svc_acct.html b/fs_selfservice/FS-SelfService/cgi/process_svc_acct.html
new file mode 100644
index 0000000..813521f
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_svc_acct.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4><%= $svc %> setup successfully.</FONT>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/process_svc_external.html b/fs_selfservice/FS-SelfService/cgi/process_svc_external.html
new file mode 100644
index 0000000..1d2937b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_svc_external.html
@@ -0,0 +1,12 @@
+<HTML><HEAD><TITLE><%= $error ? 'MyAccount' : sprintf("Your serial number is %010d-$title", $id) %></TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4><%= $svc %> setup successfully.</FONT>
+
+<BR><BR>Your serial number is <%= sprintf("%010d-$title", $id) %>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/promocode.html b/fs_selfservice/FS-SelfService/cgi/promocode.html
new file mode 100644
index 0000000..f8ee7f6
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/promocode.html
@@ -0,0 +1,14 @@
+<HTML><HEAD><TITLE>ISP Signup</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup - promotional code</FONT><BR><BR>
+<SCRIPT>
+function gotoURL(object) {
+ window.location.href = 'signup.cgi?promo_code=' + object.promo_code.value;
+}
+</SCRIPT>
+<FORM>
+Enter promotional code <INPUT TYPE="text" NAME="promo_code">
+<INPUT type="submit" VALUE="Signup" onClick="gotoURL(this.form)">
+
+</FORM>
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/provision.html b/fs_selfservice/FS-SelfService/cgi/provision.html
new file mode 100644
index 0000000..5ae7b42
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/provision.html
@@ -0,0 +1,8 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= include('provision_list') %>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/provision_list.html b/fs_selfservice/FS-SelfService/cgi/provision_list.html
new file mode 100644
index 0000000..88d1c84
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/provision_list.html
@@ -0,0 +1,92 @@
+<FONT SIZE=4>Setup services</FONT><BR><BR>
+
+<SCRIPT>
+function areyousure(href, message) {
+ if (confirm(message) == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
+<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#ffffff">
+
+<%= foreach my $pkg (
+ grep { scalar(@{$_->{part_svc}})
+ || scalar(@{$_->{cust_svc}})
+ } @cust_pkg
+ ) {
+
+ $OUT .= #'<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#ffffff">'.
+ '<TR><TH BGCOLOR="#6666ff" COLSPAN=2>'.
+ $pkg->{'pkg'}. '</TH><TH BGCOLOR="#6666ff" >' .
+ qq!(<A style="font-size: smaller;color: #000000" HREF="! .
+ qq!${url}customer_change_pkg;pkgnum=$pkg->{'pkgnum'};pkg=$pkg->{'pkg'}">! .
+ 'change</A>)</TH></TR>';
+
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $cust_svc ( @{ $pkg->{cust_svc} } ) {
+ my $td = qq!<TD BGCOLOR="#$col"!;
+
+ $OUT .= '<TR>'.
+ "$td ALIGN=right>". $cust_svc->{label}[0]. ': </TD>'.
+ "$td><B>". $cust_svc->{label}[1]. '</B>';
+ $OUT .= '<BR><I>password: '. encode_entities($cust_svc->{_password}). '</I>'
+ if exists($cust_svc->{_password});
+ $OUT .= '</TD>'.
+ "$td><FONT SIZE=-1>";
+
+ #if ( $cust_svc->{label}[2] eq 'svc_acct' ) {
+ # $OUT .= qq!(<A HREF="${url}changepw;svcnum=$cust_svc->{'svcnum'}">!.
+ # 'change&nbsp;pw) ';
+ #}
+
+ unless ( $cust_svc->{'svcnum'} == $svcnum ) {
+ $OUT .= qq!(<A HREF="javascript:areyousure('${url}delete_svc;svcnum=$cust_svc->{svcnum}', 'This will permanently delete the $cust_svc->{label}[1] $cust_svc->{label}[0]. Are you sure?')">!.
+ 'delete</A>)';
+
+ }
+ $OUT .= '</FONT></TD></TR>';
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+
+ $OUT .= '<TR><TD COLSPAN=3 BGCOLOR="#000000"></TD></TR>'
+ if scalar(@{$pkg->{part_svc}}) && scalar(@{$pkg->{cust_svc}});
+
+ $col = $col1;
+
+ foreach my $part_svc ( @{ $pkg->{part_svc} } ) {
+
+ my $td = qq!<TD BGCOLOR="#$col"!;
+
+ my $link;
+
+ if ( $part_svc->{'svcdb'} eq 'svc_external'
+ #&& $conf->exists('svc_external-skip_manual')
+ ) {
+ $link = "${url}process_svc_external;".
+ "pkgnum=$pkg->{'pkgnum'};".
+ "svcpart=$part_svc->{'svcpart'}";
+ } else {
+ $link = "${url}provision_svc;".
+ "pkgnum=$pkg->{'pkgnum'};".
+ "svcpart=$part_svc->{'svcpart'}";
+ }
+
+ $OUT .= "<TR>$td COLSPAN=3 ALIGN=center>".
+ qq!<A HREF="$link">!. 'Setup '. $part_svc->{'svc'}. '</A> '.
+ '('. $part_svc->{'num_avail'}. ' available)'.
+ '</TD></TR>'
+ #self-service only supports these services so far
+ if grep { $part_svc->{'svcdb'} eq $_ } qw( svc_acct svc_external );
+
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+
+ #$OUT .= '</TABLE><BR>';
+ $OUT .= '<TR><TD BGCOLOR="#eeeeee" COLSPAN=3>&nbsp;</TD></TR>';
+
+} %>
+
+</TABLE>
diff --git a/fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html b/fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html
new file mode 100644
index 0000000..550493b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html
@@ -0,0 +1,8 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= include('svc_acct') %>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/recharge_prepay.html b/fs_selfservice/FS-SelfService/cgi/recharge_prepay.html
new file mode 100644
index 0000000..3de4c87
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/recharge_prepay.html
@@ -0,0 +1,33 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Recharge with prepaid card</FONT><BR><BR>
+<FORM NAME="OneTrueForm" METHOD="POST" ACTION="<%=$selfurl%>" onSubmit="document.OneTrueForm.process.disabled=true">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%=$session_id%>">
+<INPUT TYPE="hidden" NAME="action" VALUE="recharge_results">
+<TABLE BGCOLOR="#cccccc">
+<!--
+<TR>
+ <TD ALIGN="right">Amount&nbsp;Due</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<%=sprintf("%.2f",$balance)%>
+ </TD></TR></TABLE>
+ </TD>
+</TR>
+-->
+<TR>
+ <TD ALIGN="right">Prepaid&nbsp;card&nbsp;number</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="prepaid_cardnum" SIZE=20 MAXLENGTH=19 VALUE="<%=$prepaid_cardnum%>">
+ </TD>
+</TR>
+</TABLE>
+<BR>
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="<%=$paybatch%>">
+<INPUT TYPE="submit" NAME="process" VALUE="Recharge"> <!-- onClick="this.disabled=true"> -->
+</FORM>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/recharge_results.html b/fs_selfservice/FS-SelfService/cgi/recharge_results.html
new file mode 100644
index 0000000..6d928e3
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/recharge_results.html
@@ -0,0 +1,21 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Recharge results</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error processing your prepaid card: $error</FONT>!;
+} else {
+ $OUT .= 'Prepaid card recharge successful!<BR><BR>';
+
+ $OUT .= '$'. sprintf('%.2f', $amount). ' added to your account.<BR><BR>'
+ if $amount;
+
+ $OUT .= $duration. ' added to your account.<BR><BR>'
+ if $seconds;
+
+ $OUT .= 'Thank you.';
+} %>
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/regcode.html b/fs_selfservice/FS-SelfService/cgi/regcode.html
new file mode 100644
index 0000000..e639b9b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/regcode.html
@@ -0,0 +1,14 @@
+<HTML><HEAD><TITLE>ISP Signup</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup - registration code</FONT><BR><BR>
+<SCRIPT>
+function gotoURL(object) {
+ window.location.href = 'signup.cgi?reg_code=' + object.reg_code.value;
+}
+</SCRIPT>
+<FORM>
+Enter registration code <INPUT TYPE="text" NAME="reg_code">
+<INPUT type="submit" VALUE="Signup" onClick="gotoURL(this.form)">
+
+</FORM>
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi
new file mode 100644
index 0000000..865b5ce
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi
@@ -0,0 +1,667 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw($DEBUG $cgi $session_id $form_max $template_dir);
+use subs qw(do_template);
+use CGI;
+use CGI::Carp qw(fatalsToBrowser);
+use Text::Template;
+use HTML::Entities;
+use Date::Format;
+use Number::Format 1.50;
+use FS::SelfService qw( login_info login customer_info edit_info invoice
+ payment_info process_payment
+ process_prepay
+ list_pkgs order_pkg signup_info order_recharge
+ part_svc_info provision_acct provision_external
+ unprovision_svc change_pkg domainselector
+ list_svcs list_svc_usage list_support_usage
+ myaccount_passwd
+ );
+
+$template_dir = '.';
+
+$DEBUG = 1;
+
+$form_max = 255;
+
+$cgi = new CGI;
+
+unless ( defined $cgi->param('session') ) {
+ my $login_info = login_info();
+
+ do_template('login', $login_info );
+ exit;
+}
+
+if ( $cgi->param('session') eq 'login' ) {
+
+ $cgi->param('username') =~ /^\s*([a-z0-9_\-\.\&]{0,$form_max})\s*$/i
+ or die "illegal username";
+ my $username = $1;
+
+ $cgi->param('domain') =~ /^\s*([\w\-\.]{0,$form_max})\s*$/
+ or die "illegal domain";
+ my $domain = $1;
+
+ $cgi->param('password') =~ /^(.{0,$form_max})$/
+ or die "illegal password";
+ my $password = $1;
+
+ my $rv = login(
+ 'username' => $username,
+ 'domain' => $domain,
+ 'password' => $password,
+ );
+ if ( $rv->{error} ) {
+ my $login_info = login_info();
+ do_template('login', {
+ 'error' => $rv->{error},
+ 'username' => $username,
+ 'domain' => $domain,
+ %$login_info,
+ } );
+ exit;
+ } else {
+ $cgi->param('session' => $rv->{session_id} );
+ $cgi->param('action' => 'myaccount' );
+ }
+}
+
+$session_id = $cgi->param('session');
+
+#order|pw_list XXX ???
+$cgi->param('action') =~
+ /^(myaccount|view_invoice|make_payment|make_ach_payment|payment_results|ach_payment_results|recharge_prepay|recharge_results|logout|change_bill|change_ship|change_pay|process_change_bill|process_change_ship|process_change_pay|customer_order_pkg|process_order_pkg|customer_change_pkg|process_change_pkg|process_order_recharge|provision|provision_svc|process_svc_acct|process_svc_external|delete_svc|view_usage|view_usage_details|view_support_details|change_password|process_change_password)$/
+ or die "unknown action ". $cgi->param('action');
+my $action = $1;
+
+warn "calling $action sub\n"
+ if $DEBUG;
+$FS::SelfService::DEBUG = $DEBUG;
+my $result = eval "&$action();";
+die $@ if $@;
+
+if ( $result->{error} eq "Can't resume session"
+ || $result->{error} eq "Expired session" ) { #ick
+
+ my $login_info = login_info();
+ do_template('login', $login_info);
+ exit;
+}
+
+#warn $result->{'open_invoices'};
+#warn scalar(@{$result->{'open_invoices'}});
+
+warn "processing template $action\n"
+ if $DEBUG;
+do_template($action, {
+ 'session_id' => $session_id,
+ 'action' => $action, #so the menu knows what tab we're on...
+ %{$result}
+});
+
+#--
+
+sub myaccount { customer_info( 'session_id' => $session_id ); }
+
+sub change_bill { my $payment_info =
+ payment_info( 'session_id' => $session_id );
+ return $payment_info if ( $payment_info->{'error'} );
+ my $customer_info =
+ customer_info( 'session_id' => $session_id );
+ return {
+ %$payment_info,
+ %$customer_info,
+ };
+ }
+sub change_ship { change_bill(@_); }
+sub change_pay { change_bill(@_); }
+
+sub _process_change_info {
+ my ($erroraction, @fields) = @_;
+
+ my $results = '';
+
+ $results ||= edit_info (
+ 'session_id' => $session_id,
+ map { ($_ => $cgi->param($_)) } grep { defined($cgi->param($_)) } @fields,
+ );
+
+
+ if ( $results->{'error'} ) {
+ no strict 'refs';
+ $action = $erroraction;
+ return {
+ $cgi->Vars,
+ %{&$action()},
+ 'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ };
+ } else {
+ return $results;
+ }
+}
+
+sub process_change_bill {
+ _process_change_info( 'change_bill',
+ qw( first last company address1 address2 city state
+ county zip country daytime night fax )
+ );
+}
+
+sub process_change_ship {
+ my @list = map { "ship_$_" }
+ qw( first last company address1 address2 city state
+ county zip country daytime night fax
+ );
+ if ($cgi->param('same') eq 'Y') {
+ foreach (@list) { $cgi->param($_, '') }
+ }
+
+ _process_change_info( 'change_ship', @list );
+}
+
+sub process_change_pay {
+ my $postal = $cgi->param( 'postal_invoicing' );
+ my @list =
+ qw( payby payinfo payinfo1 payinfo2 month year payname
+ address1 address2 city county state zip country auto paytype
+ paystate ss stateid stateid_state invoicing_list
+ );
+ push @list, 'postal_invoicing' if $postal;
+ unless ( $postal || $cgi->param( 'invoicing_list' ) ) {
+ $action = 'change_pay';
+ return {
+ %{&change_pay()},
+ $cgi->Vars,
+ 'error' => '<FONT COLOR="#FF0000">Postal or email required.</FONT>',
+ };
+ }
+ _process_change_info( 'change_pay', @list );
+}
+
+sub view_invoice {
+
+ $cgi->param('invnum') =~ /^(\d+)$/ or die "illegal invnum";
+ my $invnum = $1;
+
+ invoice( 'session_id' => $session_id,
+ 'invnum' => $invnum,
+ );
+
+}
+
+sub customer_order_pkg {
+ my $init_data = signup_info( 'customer_session_id' => $session_id );
+ return $init_data if ( $init_data->{'error'} );
+
+ my $customer_info = customer_info( 'session_id' => $session_id );
+ return $customer_info if ( $customer_info->{'error'} );
+
+ return {
+ ( map { $_ => $init_data->{$_} }
+ qw( part_pkg security_phrase svc_acct_pop ),
+ ),
+ %$customer_info,
+ };
+}
+
+sub customer_change_pkg {
+ my $init_data = signup_info( 'customer_session_id' => $session_id );
+ return $init_data if ( $init_data->{'error'} );
+
+ my $customer_info = customer_info( 'session_id' => $session_id );
+ return $customer_info if ( $customer_info->{'error'} );
+
+ return {
+ ( map { $_ => $init_data->{$_} }
+ qw( part_pkg security_phrase svc_acct_pop ),
+ ),
+ ( map { $_ => $cgi->param($_) }
+ qw( pkgnum pkg )
+ ),
+ %$customer_info,
+ };
+}
+
+sub process_order_pkg {
+
+ my $results = '';
+
+ unless ( length($cgi->param('_password')) ) {
+ my $init_data = signup_info( 'customer_session_id' => $session_id );
+ $results = { 'error' => $init_data->{msgcat}{empty_password} };
+ $results = { 'error' => $init_data->{error} } if($init_data->{error});
+ }
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ my $init_data = signup_info( 'customer_session_id' => $session_id );
+ $results = { 'error' => $init_data->{msgcat}{passwords_dont_match} };
+ $results = { 'error' => $init_data->{error} } if($init_data->{error});
+ $cgi->param('_password', '');
+ $cgi->param('_password2', '');
+ }
+
+ $results ||= order_pkg (
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) }
+ qw( custnum pkgpart username _password _password2 sec_phrase popnum )
+ );
+
+
+ if ( $results->{'error'} ) {
+ $action = 'customer_order_pkg';
+ return {
+ $cgi->Vars,
+ %{customer_order_pkg()},
+ 'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ };
+ } else {
+ return $results;
+ }
+
+}
+
+sub process_change_pkg {
+
+ my $results = '';
+
+ $results ||= change_pkg (
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) }
+ qw( pkgpart pkgnum )
+ );
+
+
+ if ( $results->{'error'} ) {
+ $action = 'customer_change_pkg';
+ return {
+ $cgi->Vars,
+ %{customer_change_pkg()},
+ 'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ };
+ } else {
+ return $results;
+ }
+
+}
+
+sub process_order_recharge {
+
+ my $results = '';
+
+ $results ||= order_recharge (
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) }
+ qw( svcnum )
+ );
+
+
+ if ( $results->{'error'} ) {
+ $action = 'view_usage';
+ if ($results->{'error'} eq '_decline') {
+ $results->{'error'} = "There has been an error processing your account. Please contact customer support."
+ }
+ return {
+ $cgi->Vars,
+ %{view_usage()},
+ 'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ };
+ } else {
+ return $results;
+ }
+
+}
+
+sub make_payment {
+ payment_info( 'session_id' => $session_id );
+}
+
+sub payment_results {
+
+ use Business::CreditCard;
+
+ #we should only do basic checking here for DoS attacks and things
+ #that couldn't be constructed by the web form... let process_payment() do
+ #the rest, it gives better error messages
+
+ $cgi->param('amount') =~ /^\s*(\d+(\.\d{2})?)\s*$/
+ or die "Illegal amount: ". $cgi->param('amount'); #!!!
+ my $amount = $1;
+
+ my $payinfo = $cgi->param('payinfo');
+ $payinfo =~ s/\D//g;
+ $payinfo =~ /^(\d{13,16})$/
+ #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ or die "illegal card"; #!!!
+ $payinfo = $1;
+ validate($payinfo)
+ #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ or die "invalid card"; #!!!
+
+ if ( $cgi->param('card_type') ) {
+ cardtype($payinfo) eq $cgi->param('card_type')
+ #or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
+ or die "not a ". $cgi->param('card_type');
+ }
+
+ $cgi->param('paycvv') =~ /^\s*(.{0,4})\s*$/ or die "illegal CVV2";
+ my $paycvv = $1;
+
+ $cgi->param('month') =~ /^(\d{2})$/ or die "illegal month";
+ my $month = $1;
+ $cgi->param('year') =~ /^(\d{4})$/ or die "illegal year";
+ my $year = $1;
+
+ $cgi->param('payname') =~ /^(.{0,80})$/ or die "illegal payname";
+ my $payname = $1;
+
+ $cgi->param('address1') =~ /^(.{0,80})$/ or die "illegal address1";
+ my $address1 = $1;
+
+ $cgi->param('address2') =~ /^(.{0,80})$/ or die "illegal address2";
+ my $address2 = $1;
+
+ $cgi->param('city') =~ /^(.{0,80})$/ or die "illegal city";
+ my $city = $1;
+
+ $cgi->param('state') =~ /^(.{2})$/ or die "illegal state";
+ my $state = $1;
+
+ $cgi->param('zip') =~ /^(.{0,10})$/ or die "illegal zip";
+ my $zip = $1;
+
+ my $save = 0;
+ $save = 1 if $cgi->param('save');
+
+ my $auto = 0;
+ $auto = 1 if $cgi->param('auto');
+
+ $cgi->param('paybatch') =~ /^([\w\-\.]+)$/ or die "illegal paybatch";
+ my $paybatch = $1;
+
+ process_payment(
+ 'session_id' => $session_id,
+ 'payby' => 'CARD',
+ 'amount' => $amount,
+ 'payinfo' => $payinfo,
+ 'paycvv' => $paycvv,
+ 'month' => $month,
+ 'year' => $year,
+ 'payname' => $payname,
+ 'address1' => $address1,
+ 'address2' => $address2,
+ 'city' => $city,
+ 'state' => $state,
+ 'zip' => $zip,
+ 'save' => $save,
+ 'auto' => $auto,
+ 'paybatch' => $paybatch,
+ );
+
+}
+
+sub make_ach_payment {
+ payment_info( 'session_id' => $session_id );
+}
+
+sub ach_payment_results {
+
+ #we should only do basic checking here for DoS attacks and things
+ #that couldn't be constructed by the web form... let process_payment() do
+ #the rest, it gives better error messages
+
+ $cgi->param('amount') =~ /^\s*(\d+(\.\d{2})?)\s*$/
+ or die "illegal amount"; #!!!
+ my $amount = $1;
+
+ my $payinfo1 = $cgi->param('payinfo1');
+ $payinfo1=~ /^(\d+)$/
+ or die "illegal account"; #!!!
+ $payinfo1= $1;
+
+ my $payinfo2 = $cgi->param('payinfo2');
+ $payinfo2=~ /^(\d+)$/
+ or die "illegal ABA/routing code"; #!!!
+ $payinfo2= $1;
+
+ $cgi->param('payname') =~ /^(.{0,80})$/ or die "illegal payname";
+ my $payname = $1;
+
+ $cgi->param('paystate') =~ /^(.{0,2})$/ or die "illegal paystate";
+ my $paystate = $1;
+
+ $cgi->param('paytype') =~ /^(.{0,80})$/ or die "illegal paytype";
+ my $paytype = $1;
+
+ $cgi->param('ss') =~ /^(.{0,80})$/ or die "illegal ss";
+ my $ss = $1;
+
+ $cgi->param('stateid') =~ /^(.{0,80})$/ or die "illegal stateid";
+ my $stateid = $1;
+
+ $cgi->param('stateid_state') =~ /^(.{0,2})$/ or die "illegal stateid_state";
+ my $stateid_state = $1;
+
+ my $save = 0;
+ $save = 1 if $cgi->param('save');
+
+ my $auto = 0;
+ $auto = 1 if $cgi->param('auto');
+
+ $cgi->param('paybatch') =~ /^([\w\-\.]+)$/ or die "illegal paybatch";
+ my $paybatch = $1;
+
+ process_payment(
+ 'session_id' => $session_id,
+ 'payby' => 'CHEK',
+ 'amount' => $amount,
+ 'payinfo1' => $payinfo1,
+ 'payinfo2' => $payinfo2,
+ 'month' => '12',
+ 'year' => '2037',
+ 'payname' => $payname,
+ 'paytype' => $paytype,
+ 'paystate' => $paystate,
+ 'ss' => $ss,
+ 'stateid' => $stateid,
+ 'stateid_state' => $stateid_state,
+ 'save' => $save,
+ 'auto' => $auto,
+ 'paybatch' => $paybatch,
+ );
+
+}
+
+sub recharge_prepay {
+ customer_info( 'session_id' => $session_id );
+}
+
+sub recharge_results {
+
+ my $prepaid_cardnum = $cgi->param('prepaid_cardnum');
+ $prepaid_cardnum =~ s/\W//g;
+ $prepaid_cardnum =~ /^(\w*)$/ or die "illegal prepaid card number";
+ $prepaid_cardnum = $1;
+
+ process_prepay ( 'session_id' => $session_id,
+ 'prepaid_cardnum' => $prepaid_cardnum,
+ );
+}
+
+sub logout {
+ FS::SelfService::logout( 'session_id' => $session_id );
+}
+
+sub provision {
+ my $result = list_pkgs( 'session_id' => $session_id );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+ $result;
+}
+
+sub provision_svc {
+
+ my $result = part_svc_info(
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart ),
+ );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+
+ $result->{'svcdb'} =~ /^svc_(.*)$/
+ #or return { 'error' => 'Unknown svcdb '. $result->{'svcdb'} };
+ or die 'Unknown svcdb '. $result->{'svcdb'};
+ $action .= "_$1";
+
+ $result;
+}
+
+sub process_svc_acct {
+
+ my $result = provision_acct (
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw(
+ pkgnum svcpart username domsvc _password _password2 sec_phrase popnum )
+ );
+
+ if ( exists $result->{'error'} && $result->{'error'} ) {
+ #warn "$result $result->{'error'}";
+ $action = 'provision_svc_acct';
+ return {
+ $cgi->Vars,
+ %{ part_svc_info( 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
+ )
+ },
+ 'error' => $result->{'error'},
+ };
+ } else {
+ #warn "$result $result->{'error'}";
+ return $result;
+ }
+
+}
+
+sub process_svc_external {
+ provision_external (
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
+ );
+}
+
+sub delete_svc {
+ unprovision_svc(
+ 'session_id' => $session_id,
+ 'svcnum' => $cgi->param('svcnum'),
+ );
+}
+
+sub view_usage {
+ list_svcs(
+ 'session_id' => $session_id,
+ 'svcdb' => 'svc_acct',
+ 'ncancelled' => 1,
+ );
+}
+
+sub view_usage_details {
+ list_svc_usage(
+ 'session_id' => $session_id,
+ 'svcnum' => $cgi->param('svcnum'),
+ 'beginning' => $cgi->param('beginning') || '',
+ 'ending' => $cgi->param('ending') || '',
+ );
+}
+
+sub view_support_details {
+ list_support_usage(
+ 'session_id' => $session_id,
+ 'svcnum' => $cgi->param('svcnum'),
+ 'beginning' => $cgi->param('beginning') || '',
+ 'ending' => $cgi->param('ending') || '',
+ );
+}
+
+sub change_password {
+ list_svcs(
+ 'session_id' => $session_id,
+ 'svcdb' => 'svc_acct',
+ );
+};
+
+sub process_change_password {
+
+ my $result = myaccount_passwd(
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( svcnum new_password new_password2 )
+ );
+
+ if ( exists $result->{'error'} && $result->{'error'} ) {
+
+ $action = 'change_password';
+ return {
+ $cgi->Vars,
+ %{ list_svcs( 'session_id' => $session_id,
+ 'svcdb' => 'svc_acct',
+ )
+ },
+ #'svcnum' => $cgi->param('svcnum'),
+ 'error' => $result->{'error'}
+ };
+
+ } else {
+
+ return $result;
+
+ }
+
+}
+
+#--
+
+sub do_template {
+ my $name = shift;
+ my $fill_in = shift;
+
+ $cgi->delete_all();
+ $fill_in->{'selfurl'} = $cgi->self_url;
+ $fill_in->{'cgi'} = \$cgi;
+
+ my $source = "$template_dir/$name.html";
+ #warn "creating template for $source\n";
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => $source,
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1,
+ )
+ or die $Text::Template::ERROR;
+
+ #warn "filling in $template with $fill_in\n";
+ print $cgi->header( '-expires' => 'now' ),
+ $template->fill_in( PACKAGE => 'FS::SelfService::_selfservicecgi',
+ HASH => $fill_in
+ );
+}
+
+#*FS::SelfService::_selfservicecgi::include = \&Text::Template::fill_in_file;
+
+package FS::SelfService::_selfservicecgi;
+
+#use FS::SelfService qw(regionselector expselect popselector);
+use HTML::Entities;
+use FS::SelfService qw(regionselector popselector domainselector);
+
+#false laziness w/agent.cgi
+sub include {
+ my $name = shift;
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => "$main::template_dir/$name.html",
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1,
+ )
+ or die $Text::Template::ERROR;
+
+ $template->fill_in( PACKAGE => 'FS::SelfService::_selfservicecgi',
+ #HASH => $fill_in
+ );
+
+}
+
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-agentselect.html b/fs_selfservice/FS-SelfService/cgi/signup-agentselect.html
new file mode 100755
index 0000000..7851c56
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-agentselect.html
@@ -0,0 +1,195 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Agent <SELECT NAME="agentnum">
+<%=
+ warn $init_data;
+ warn $init_data->{'agent'};
+ foreach my $agent ( @{$init_data->{'agent'}} ) {
+ $OUT .= '<OPTION VALUE="'. $agent->{'agentnum'}. '"';
+ $OUT .= ' SELECTED' if $agent->{'agentnum'} eq $agentnum;
+ $OUT .= '>'. $agent->{'agent'};
+ }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">(none)
+
+ <%=
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $package->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup" >
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-alternate.html b/fs_selfservice/FS-SelfService/cgi/signup-alternate.html
new file mode 100755
index 0000000..490cefa
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-alternate.html
@@ -0,0 +1,218 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="dummy">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="3">
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD><SELECT NAME="state" SIZE="1">
+
+ <%=
+ foreach ( @{$locales} ) {
+ my $value = $_->{'state'};
+ $value .= ' ('. $_->{'county'}. ')' if $_->{'county'};
+ $value .= ' / '. $_->{'country'};
+
+ $OUT .= qq(<OPTION VALUE="$value");
+ $OUT .= ' SELECTED' if ( $state eq $_->{'state'}
+ && $county eq $_->{'county'}
+ && $country eq $_->{'country'}
+ );
+ $OUT .= ">$value</OPTION>";
+ }
+ %>
+
+ </SELECT></TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+
+<BR><BR>
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Username</TH>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Password</TH>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Re-enter Password</TH>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+
+<%= if ( $init_data->{'security_phrase'} ) {
+ <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+
+<%= if ( scalar(@$pops) ) {
+ '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ popselector($popnum);
+ }
+%>
+
+</TABLE><font color="#ff0000">*</font> required fields
+
+<BR><BR>First package
+
+ <%= use Tie::IxHash;
+ my %pkgpart2payby = map { $_->{pkgpart} => $_->{payby}[0] } @{$packages};
+ tie my %options, 'Tie::IxHash',
+ '' => '(none)',
+ map { $_->{pkgpart} => $_->{pkg} }
+ sort { $a->{recur} <=> $b->{recur} }
+ @{$packages}
+ ;
+
+ use HTML::Widgets::SelectLayers 0.02;
+ my @form_text = qw( magic ref ss agentnum
+ last first company address1 address2
+ city zip daytime night fax
+ username _password _password2 sec_phrase );
+ my @form_select = qw( state ); #county country
+ if ( scalar(@$pops) == 0 or scalar(@$pops) == 1 ) {
+ push @form_text, 'popnum',
+ } else {
+ push @form_select, 'popnum',
+ }
+ my $widget = new HTML::Widgets::SelectLayers(
+ options => \%options,
+ selected_layer => $pkgpart,
+ form_name => 'dummy',
+ form_action => $self_url,
+ form_text => \@form_text,
+ form_select => \@form_select,
+ layer_callback => sub {
+ my $layer = shift;
+ my $html = qq( <INPUT TYPE="hidden" NAME="pkgpart" VALUE="$layer">);
+
+ if ( $pkgpart2payby{$layer} eq 'BILL' ) {
+ $html .= <<ENDOUT;
+<INPUT TYPE="hidden" NAME="payby" VALUE="BILL">
+<INPUT TYPE="hidden" NAME="invoicing_list_POST" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_payinfo" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_month" VALUE="12">
+<INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">
+<INPUT TYPE="hidden" NAME="BILL_payname" VALUE="">
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+ENDOUT
+ } elsif ( $pkgpart2payby{$layer} eq 'CARD' ) {
+ my $postal_checked = '';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $postal_checked = 'CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+
+ $invoicing_list= join(', ', grep { $_ ne 'POST' } @invoicing_list );
+
+ my $expselect = expselect("CARD", $paydate);
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected =
+ $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .=
+ qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ $html .= <<ENDOUT;
+<INPUT TYPE="hidden" NAME="payby" VALUE="CARD">
+<BR><BR>Billing information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0>
+<INPUT TYPE="hidden" NAME="invoicing_list_POST" VALUE="">
+<TR>
+ <TD ALIGN="right">Email statement to </TD>
+ <TD><INPUT TYPE="text" NAME="invoicing_list" VALUE="$invoicing_list"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Credit card type</TH>
+ <TD>$cardselect</TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Card number</TH>
+ <TD><INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>*</font>Exp</TH>
+ <TD>$expselect</TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Name on card</TH>
+ <TD><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname"></TD>
+</TR>
+</TABLE>
+<font color="#ff0000">*</font> required fields
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+ENDOUT
+ } else {
+ $html = <<ENDOUT;
+<BR>Please select a package.<BR>
+ENDOUT
+
+ }
+
+ $html;
+
+ },
+ );
+
+ $widget->html;
+
+
+ %>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-billaddress.html b/fs_selfservice/FS-SelfService/cgi/signup-billaddress.html
new file mode 100755
index 0000000..3cf9d25
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-billaddress.html
@@ -0,0 +1,307 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+ $OUT .= '<OPTION VALUE="">' unless $refnum;
+ foreach my $part_referral ( @{$init_data->{'part_referral'}} ) {
+ $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+ $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+ $OUT .= '>'. $part_referral->{'referral'};
+ }
+%>
+</SELECT><BR><BR>
+Billing Address (where credit card statement is sent)
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Exact name on card<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>" onChange="changed(this)">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>" onChange="changed(this)"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country, '', 'changed(this)' );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12 onChange="changed(this)"></TD>
+</TR>
+</TABLE>
+
+<SCRIPT>
+function changed(what) {
+ what.form.same.checked = false;
+}
+function samechanged(what) {
+ if ( what.checked ) {
+
+ <%= foreach (qw(
+ last first company address1 address2 city zip daytime night fax
+ )) {
+ $OUT .= "what.form.ship_$_.value = what.form.$_.value;\n";
+ }
+ %>
+
+ what.form.ship_country.selectedIndex = what.form.country.selectedIndex;
+ ship_country_changed(what.form.ship_country);
+ what.form.ship_state.selectedIndex = what.form.state.selectedIndex;
+ ship_state_changed(what.form.ship_state);
+ what.form.ship_county.selectedIndex = what.form.county.selectedIndex;
+ }
+}
+</SCRIPT>
+
+<BR><BR>
+Service Address
+(<INPUT TYPE="checkbox" NAME="same" VALUE="Y" onClick="samechanged(this)" <%= $same eq 'Y' ? 'CHECKED' : '' %>>same as billing address)<BR>
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_last" VALUE="<%= $ship_last %>" onChange="changed(this)">,
+ <INPUT TYPE="text" NAME="ship_first" VALUE="<%= $ship_first %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_company" SIZE=70 VALUE="<%= $ship_company %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_address1" SIZE=70 VALUE="<%= $ship_address1 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_address2" SIZE=70 VALUE="<%= $ship_address2 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="ship_city" VALUE="<%= $ship_city %>" onChange="changed(this)"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($ship_county_html, $ship_state_html, $ship_country_html) =
+ regionselector( $ship_county,
+ $ship_state,
+ $ship_country,
+ 'ship_',
+ 'changed(this)',
+ );
+
+ "$ship_county_html $ship_state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="ship_zip" SIZE=10 VALUE="<%= $ship_zip %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $ship_country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_daytime" VALUE="<%= $ship_daytime %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_night" VALUE="<%= $ship_night %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_fax" VALUE="<%= $ship_fax %>" SIZE=12 onChange="changed(this)"></TD>
+</TR>
+</TABLE>
+
+<font color="#ff0000">*</font> required fields<BR>
+
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<INPUT TYPE="hidden" NAME="promo_code" VALUE="<%= $cgi->param('promo_code') %>">
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart">
+
+ <%=
+ $OUT .= '<OPTION VALUE="">(none)' unless scalar(@$packages) == 1;
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED'
+ if ( $pkgpart && $package->{'pkgpart'} == $pkgpart )
+ || scalar(@$packages) == 1;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-freeoption.html b/fs_selfservice/FS-SelfService/cgi/signup-freeoption.html
new file mode 100755
index 0000000..40ad03c
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-freeoption.html
@@ -0,0 +1,262 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+ $OUT .= '<OPTION VALUE="">' unless $refnum;
+ foreach my $part_referral ( @{$init_data->{'part_referral'}} ) {
+ $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+ $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+ $OUT .= '>'. $part_referral->{'referral'};
+ }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>
+<%=
+ my $first_payby = $packages->[0]{'payby'}[0];
+ unless ( grep { scalar( @{$_->{'payby'}} ) > 1
+ || $_->{'payby'}->[0] ne $first_payby
+ } @$packages
+ ) {
+ @payby = ( $first_payby );
+ }
+
+ unless ( scalar(@payby) == 1 && $payby[0] eq 'BILL' ) {
+
+ $OUT .= ' Billing information
+ <TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+ <TR><TD>
+ <INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+
+ my @invoicing_list = split(', ', $invoicing_list );
+
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+
+ $OUT .= '> Postal mail invoice
+ </TD></TR>
+ <TR><TD>Email invoice
+ <INPUT TYPE="text" NAME="invoicing_list" VALUE="'
+ .join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ).
+ '"></TD></TR>';
+
+ $OUT .= '<TR><TD>Billing type</TD></TR>'
+ if scalar(@payby) > 1;
+
+ $OUT .= '</TABLE>';
+
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="invoicing_list" VALUE="">
+ <INPUT TYPE="hidden" NAME="invoicing_list_POST" VALUE="">';
+ }
+
+%>
+
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => <<'END',
+<INPUT TYPE="hidden" NAME="BILL_payinfo" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_month" VALUE="12">
+<INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">
+<INPUT TYPE="hidden" NAME="BILL_payname" VALUE="">
+END
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => <<'END',
+<INPUT TYPE="hidden" NAME="BILL_payinfo" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_month" VALUE="12">
+<INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">
+<INPUT TYPE="hidden" NAME="BILL_payname" VALUE="">
+END
+
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE>
+<%= unless ( scalar(@payby) == 1 && $payby[0] eq 'BILL' ) {
+ $OUT .= '<font color="#ff0000">*</font> required fields for each billing type';
+ }
+ '';
+%>
+<BR><BR>First package
+<INPUT TYPE="hidden" NAME="promo_code" VALUE="<%= $cgi->param('promo_code') %>"><TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart">
+
+ <%=
+ $OUT .= '<OPTION VALUE="">(none)' unless scalar(@$packages) == 1;
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED'
+ if ( $pkgpart && $package->{'pkgpart'} == $pkgpart )
+ || scalar(@$packages) == 1;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-snarf.html b/fs_selfservice/FS-SelfService/cgi/signup-snarf.html
new file mode 100755
index 0000000..d167efb
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-snarf.html
@@ -0,0 +1,228 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">(none)
+
+ <%=
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $package->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR>Enter up to ten external accounts from which to retrieve email
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="left">Mail server</TH>
+ <TH ALIGN="left">Username</TH>
+ <TH ALIGN="left">Password</TH>
+</TR>
+<%=
+ for my $num ( 1..10 ) {
+ no strict 'vars';
+ $OUT .= qq!<TR><TD><INPUT TYPE="text" NAME="snarf_machine$num" VALUE="${"snarf_machine$num"}"></TD>!.
+ qq!<INPUT TYPE="hidden" NAME="snarf_protocol$num" VALUE="pop3">!.
+ qq!<TD><INPUT TYPE="text" NAME="snarf_username$num" VALUE="${"snarf_username$num"}"></TD>!.
+ qq!<TD><INPUT TYPE="password" NAME="snarf_password$num" VALUE="${"snarf_password$num"}"></TD>!.
+ qq!</TR>!;
+ }
+%>
+</TABLE>
+
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup.cgi b/fs_selfservice/FS-SelfService/cgi/signup.cgi
new file mode 100755
index 0000000..47857f0
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup.cgi
@@ -0,0 +1,387 @@
+#!/usr/bin/perl -T
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw( @payby $cgi $init_data
+ $self_url $error $agentnum
+
+ $ieak_file $ieak_template
+ $signup_html $signup_template
+ $success_html $success_template
+ $decline_html $decline_template
+ );
+
+use subs qw( print_form print_okay print_decline
+ success_default decline_default
+ );
+use CGI;
+#use CGI::Carp qw(fatalsToBrowser);
+use Text::Template;
+use Business::CreditCard;
+use HTTP::BrowserDetect;
+use FS::SelfService qw( signup_info new_customer );
+
+#acceptable payment methods
+#
+#@payby = qw( CARD BILL COMP );
+#@payby = qw( CARD BILL );
+#@payby = qw( CARD );
+@payby = qw( CARD PREPAY );
+
+$ieak_file = '/usr/local/freeside/ieak.template';
+$signup_html = -e 'signup.html'
+ ? 'signup.html'
+ : '/usr/local/freeside/signup.html';
+$success_html = -e 'success.html'
+ ? 'success.html'
+ : '/usr/local/freeside/success.html';
+$decline_html = -e 'decline.html'
+ ? 'decline.html'
+ : '/usr/local/freeside/decline.html';
+
+
+if ( -e $ieak_file ) {
+ my $ieak_txt = Text::Template::_load_text($ieak_file)
+ or die $Text::Template::ERROR;
+ $ieak_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $ieak_txt = $1;
+ $ieak_txt =~ s/\r//g; # don't double \r on old templates
+ $ieak_txt =~ s/\n/\r\n/g;
+ $ieak_template = new Text::Template ( TYPE => 'STRING', SOURCE => $ieak_txt )
+ or die $Text::Template::ERROR;
+} else {
+ $ieak_template = '';
+}
+
+$agentnum = '';
+if ( -e $signup_html ) {
+ my $signup_txt = Text::Template::_load_text($signup_html)
+ or die $Text::Template::ERROR;
+ $signup_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $signup_txt = $1;
+ $signup_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => $signup_txt,
+ DELIMITERS => [ '<%=', '%>' ]
+ )
+ or die $Text::Template::ERROR;
+ if ( $signup_txt =~
+ /<\s*INPUT TYPE="?hidden"?\s+NAME="?agentnum"?\s+VALUE="?(\d+)"?\s*\/?\s*>/si
+ ) {
+ $agentnum = $1;
+ }
+} else {
+ #too much maintenance hassle to keep in this file
+ die "can't find ./signup.html or /usr/local/freeside/signup.html";
+ #$signup_template = new Text::Template ( TYPE => 'STRING',
+ # SOURCE => &signup_default,
+ # DELIMITERS => [ '<%=', '%>' ]
+ # )
+ # or die $Text::Template::ERROR;
+}
+
+if ( -e $success_html ) {
+ my $success_txt = Text::Template::_load_text($success_html)
+ or die $Text::Template::ERROR;
+ $success_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $success_txt = $1;
+ $success_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => $success_txt,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+} else {
+ $success_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => &success_default,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+}
+
+if ( -e $decline_html ) {
+ my $decline_txt = Text::Template::_load_text($decline_html)
+ or die $Text::Template::ERROR;
+ $decline_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $decline_txt = $1;
+ $decline_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => $decline_txt,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+} else {
+ $decline_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => &decline_default,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+}
+
+$cgi = new CGI;
+
+$init_data = signup_info( 'agentnum' => $agentnum,
+ 'promo_code' => scalar($cgi->param('promo_code')),
+ 'reg_code' => uc(scalar($cgi->param('reg_code'))),
+ );
+
+if ( ( defined($cgi->param('magic')) && $cgi->param('magic') eq 'process' )
+ || ( defined($cgi->param('action')) && $cgi->param('action') eq 'process_signup' )
+ ) {
+
+ $error = '';
+
+ $cgi->param('agentnum', $agentnum) if $agentnum;
+ $cgi->param('reg_code', uc(scalar($cgi->param('reg_code'))) );
+
+ #false laziness w/agent.cgi, identical except for agentnum
+ my $payby = $cgi->param('payby');
+ if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+ #$payinfo = join('@', map { $cgi->param( $payby. "_payinfo$_" ) } (1,2) );
+ $cgi->param('payinfo' => $cgi->param($payby. '_payinfo1'). '@'.
+ $cgi->param($payby. '_payinfo2')
+ );
+ } else {
+ $cgi->param('payinfo' => $cgi->param( $payby. '_payinfo' ) );
+ }
+ $cgi->param('paydate' => $cgi->param( $payby. '_month' ). '-'.
+ $cgi->param( $payby. '_year' )
+ );
+ $cgi->param('payname' => $cgi->param( $payby. '_payname' ) );
+ $cgi->param('paycvv' => defined $cgi->param( $payby. '_paycvv' )
+ ? $cgi->param( $payby. '_paycvv' )
+ : ''
+ );
+ $cgi->param('paytype' => defined $cgi->param( $payby. '_paytype' )
+ ? $cgi->param( $payby. '_paytype' )
+ : ''
+ );
+ $cgi->param('paystate' => defined $cgi->param( $payby. '_paystate' )
+ ? $cgi->param( $payby. '_paystate' )
+ : ''
+ );
+
+ if ( $cgi->param('invoicing_list') ) {
+ $cgi->param('invoicing_list' => $cgi->param('invoicing_list'). ', POST')
+ if $cgi->param('invoicing_list_POST');
+ } else {
+ $cgi->param('invoicing_list' => 'POST' );
+ }
+
+ #if ( $svc_x eq 'svc_acct' ) {
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ $error = $init_data->{msgcat}{passwords_dont_match}; #msgcat
+ $cgi->param('_password', '');
+ $cgi->param('_password2', '');
+ }
+
+ if ( $payby =~ /^(CARD|DCRD)$/ && $cgi->param('CARD_type') ) {
+ my $payinfo = $cgi->param('payinfo');
+ $payinfo =~ s/\D//g;
+
+ $payinfo =~ /^(\d{13,16})$/
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ $payinfo = $1;
+ validate($payinfo)
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ cardtype($payinfo) eq $cgi->param('CARD_type')
+ or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
+ }
+
+ if ($init_data->{emailinvoiceonly} && (length $cgi->param('invoicing_list') < 1)) {
+ $error ||= $init_data->{msgcat}{illegal_or_empty_text};
+ }
+
+ my $rv = '';
+ unless ( $error ) {
+ $rv = new_customer( {
+ ( map { $_ => scalar($cgi->param($_)) }
+ qw( last first ss company
+ address1 address2 city county state zip country
+ daytime night fax stateid stateid_state
+
+ ship_last ship_first ship_company
+ ship_address1 ship_address2 ship_city ship_county ship_state
+ ship_zip ship_country
+ ship_daytime ship_night ship_fax
+
+ payby payinfo paycvv paydate payname paystate paytype
+ invoicing_list referral_custnum promo_code reg_code
+ pkgpart refnum agentnum
+ username sec_phrase _password popnum
+ countrycode phonenum sip_password pin
+ ),
+ grep { /^snarf_/ } $cgi->param
+ ),
+ 'payip' => $cgi->remote_host(),
+ } );
+ $error = $rv->{'error'};
+ }
+ #eslaf
+
+ if ( $error eq '_decline' ) {
+ print_decline();
+ } elsif ( $error ) {
+ #fudge the snarf info
+ no strict 'refs';
+ ${$_} = $cgi->param($_) foreach grep { /^snarf_/ } $cgi->param;
+ print_form();
+ } else {
+ print_okay(
+ 'pkgpart' => scalar($cgi->param('pkgpart')),
+ %$rv,
+ );
+ }
+
+} else {
+ $error = '';
+ print_form;
+}
+
+sub print_form {
+
+ $error = "Error: $error" if $error;
+
+ my $r = {
+ $cgi->Vars,
+ %{$init_data},
+ 'error' => $error,
+ };
+
+ $r->{pkgpart} ||= $r->{default_pkgpart};
+
+ $r->{referral_custnum} = $r->{'ref'};
+ #$cgi->delete('ref');
+ #$cgi->delete('init_popstate');
+ $r->{self_url} = $cgi->self_url;
+
+ print $cgi->header( '-expires' => 'now' ),
+ $signup_template->fill_in( PACKAGE => 'FS::SelfService::_signupcgi',
+ HASH => $r
+ );
+}
+
+sub print_decline {
+ print $cgi->header( '-expires' => 'now' ),
+ $decline_template->fill_in();
+}
+
+sub print_okay {
+ my %param = @_;
+ my $user_agent = new HTTP::BrowserDetect $ENV{HTTP_USER_AGENT};
+
+ my( $username, $password ) = ( '', '' );
+ my( $countrycode, $phonenum, $sip_password, $pin ) = ( '', '', '', '' );
+
+ my $svc_x = $param{signup_service} || 'svc_acct'; #just in case
+ if ( $svc_x eq 'svc_acct' ) {
+
+ $cgi->param('username') =~ /^(.+)$/
+ or die "fatal: invalid username got past FS::SelfService::new_customer";
+ $username = $1;
+ $cgi->param('_password') =~ /^(.+)$/
+ or die "fatal: invalid password got past FS::SelfService::new_customer";
+ $password = $1;
+
+ } elsif ( $svc_x eq 'svc_phone' ) {
+
+ $countrycode = $param{countrycode};
+ $phonenum = $param{phonenum};
+ $sip_password = $param{sip_password};
+ $pin = $param{pin};
+
+ } else {
+ die "unknown signup service $svc_x";
+ }
+
+ ( $cgi->param('first'). ' '. $cgi->param('last') ) =~ /^(.*)$/
+ or die "fatal: invalid email_name got past FS::SelfService::new_customer";
+ my $email_name = $1; #global for template
+
+ #my %pop = ();
+ my %popnum2pop = ();
+ foreach ( @{ $init_data->{'svc_acct_pop'} } ) {
+ #push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
+ $popnum2pop{$_->{popnum}} = $_;
+ }
+
+ my( $ac, $exch, $loc);
+ my $pop = $popnum2pop{$cgi->param('popnum')};
+ #or die "fatal: invalid popnum got past FS::SelfService::new_customer";
+ if ( $pop ) {
+ ( $ac, $exch, $loc ) = ( $pop->{'ac'}, $pop->{'exch'}, $pop->{'loc'} );
+ } else {
+ ( $ac, $exch, $loc ) = ( '', '', ''); #presumably you're not using them.
+ }
+
+ #global for template
+ my $part_pkg = ( grep { $_->{'pkgpart'} eq $param{'pkgpart'} }
+ @{ $init_data->{'part_pkg'} }
+ )[0];
+ my $pkg = $part_pkg->{'pkg'};
+
+ if ( $ieak_template && $user_agent->windows && $user_agent->ie ) {
+
+ #send an IEAK config
+ print $cgi->header('application/x-Internet-signup'),
+ $ieak_template->fill_in();
+
+ } else { #send a simple confirmation
+
+ print $cgi->header( '-expires' => 'now' ),
+ $success_template->fill_in( HASH => {
+
+ email_name => $email_name,
+ pkg => $pkg,
+ part_pkg => \$part_pkg,
+
+ signup_service => $svc_x,
+
+ #for svc_acct
+ username => $username,
+ password => $password,
+ _password => $password,
+ ac => $ac, #for dialup POP
+ exch => $exch, #
+ loc => $loc, #
+
+ #for svc_phone
+ countrycode => $countrycode,
+ phonenum => $phonenum,
+ sip_password => $sip_password,
+ pin => $pin,
+
+ });
+ }
+
+}
+
+sub success_default { #html to use if you don't specify a success file
+ <<'END';
+<HTML><HEAD><TITLE>Signup successful</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Signup successful</FONT><BR><BR>
+Thanks for signing up!
+<BR><BR>
+Signup information for <%= $email_name %>:
+<BR><BR>
+Username: <%= $username %><BR>
+Password: <%= $password %><BR>
+Access number: (<%= $ac %>) / <%= $exch %> - <%= $local %><BR>
+Package: <%= $pkg %><BR>
+</BODY></HTML>
+END
+}
+
+sub decline_default { #html to use if there is a decline
+ <<'END';
+<HTML><HEAD><TITLE>Processing error</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Processing error</FONT><BR><BR>
+There has been an error processing your account. Please contact customer
+support.
+</BODY></HTML>
+END
+}
+
+# subs for the templates...
+
+package FS::SelfService::_signupcgi;
+use HTML::Entities;
+use FS::SelfService qw(regionselector expselect popselector didselector);
+
diff --git a/fs_selfservice/FS-SelfService/cgi/signup.html b/fs_selfservice/FS-SelfService/cgi/signup.html
new file mode 100755
index 0000000..1b97121
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup.html
@@ -0,0 +1,424 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <TITLE><%= $agent || ( $signup_service eq 'svc_phone' ? 'ITSP' : 'ISP' ) %> Signup form</TITLE>
+ <%= $head %>
+</HEAD>
+<BODY BGCOLOR="<%= $body_bgcolor || '#e8e8e8' %>" onUnload="myclose()">
+
+<script type="text/javascript">
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+</script>
+
+<%= $OUT .= $body_header
+ || '<FONT SIZE=7>'.
+ ( $agent || ( $signup_service eq 'svc_phone' ? 'ITSP' : 'ISP' ) ).
+ ' Signup form</FONT><BR><BR>';
+%>
+
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_signup">
+<INPUT TYPE="hidden" NAME="referral_custnum" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+<input type="hidden" name="payby">
+<%=
+ $OUT = join("\n",map { my $method = $_ ; map { qq|<input type="hidden" name="${method}_$_" />| } qw / payinfo payinfo1 payinfo2 payname paystate paytype paycvv month year type / } @payby);
+%>
+
+<%=
+ $OUT = join("\n", map { qq|<input type="hidden" name="$_" />| } qw / promo_code reg_code pkgpart username _password _password2 sec_phrase popnum countrycode phonenum sip_password pin / );
+%>
+
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+ $OUT .= '<OPTION VALUE="">' unless $refnum;
+ foreach my $part_referral ( @part_referral ) {
+ $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+ $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+ $OUT .= '>'. $part_referral->{'referral'};
+ }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="<%= $box_bgcolor || '#c0c0c0' %>" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( {
+ selected_county => $county,
+ selected_state => $state,
+ selected_country => $country,
+ default_state => $statedefault,
+ default_country => $countrydefault,
+ locales => \@cust_main_county,
+ } );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+<%=
+ $OUT = '';
+ if ( $stateid_enabled ) {
+ my ($county_html, $state_html, $country_html) =
+ regionselector( {
+ prefix => 'stateid_',
+ default_state => $statedefault,
+ default_country => $countrydefault,
+ locales => \@cust_main_county,
+ } );
+ $OUT .= qq!<TR><TD ALIGN="right">!. $label{stateid}.'</TD>';
+ $OUT .= qq!<TD><INPUT TYPE="text" NAME="stateid" VALUE="$stateid" SIZE=12></TD>!;
+ $OUT .= qq!<TD ALIGN="right">!. $label{stateid_state} .'</TD>';
+ $OUT .="<TD COLSPAN=3>$county_html $state_html</TD></TR>";
+ }
+%>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="<%= $box_bgcolor || '#c0c0c0' %>" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT ='';
+ unless ( $emailinvoiceonly ) {
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '> Postal mail invoice'; }
+ %>
+
+
+</TD></TR>
+<TR><TD><%= $OUT = ( $emailinvoiceonly ? q|<font color="#ff0000">*</font>| : q|| ) %> Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= ( scalar(@payby) > 1 or 1 ) ? '<TR><TD>Billing type ' : '' %>
+<!--</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>-->
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ foreach ( keys %card_types ) {
+ $selected = $CARD_type eq $card_types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$card_types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9> Type <SELECT NAME="CHEK_paytype">!. join('', map {qq!<OPTION VALUE="$_">$_</OPTION>!} @paytypes). qq!</SELECT><BR>{$r}Bank State <INPUT TYPE="text" NAME="CHEK_paystate" VALUE="" SIZE=5 MAXLENGTH=4><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10> Type <SELECT NAME="DCHK_paytype">!. join('', map {qq!<OPTION VALUE="$_">$_</OPTION>!} @paytypes). qq!</SELECT><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><BR>{$r}Bank State <INPUT TYPE="text" NAME="DCHK_paystate" VALUE="" SIZE=5 MAXLENGTH=4><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" NAME="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><INPUT TYPE="hidden" NAME="BILL_month" VALUE="12"><INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">Attention<INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $cvv_enabled ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<TR><TD ALIGN="right">CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)</TD><TD><INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4></TD></TR>!;
+ }
+ }
+ if ( $paystate_enabled ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CHEK DCHK) ) {
+ my ($county_html, $state_html, $country_html) =
+ regionselector( {
+ prefix => "${payby}_pay",
+ default_state => $statedefault,
+ default_country => $countrydefault,
+ locales => \@cust_main_county,
+ } );
+ $payby{$payby} .= "<BR>${r}Bank state $county_html $state_html";
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => '<TABLE BGCOLOR="'. ( $box_bgcolor || '#c0c0c0' ). qq!" BORDER=0 CELLSPACING=0 WIDTH="100%"><TR><TD ALIGN="right"><font color="#ff0000">*</font> Card type</TD><TD>$cardselect</TD></TR><TR><TD ALIGN="right"><font color="#ff0000">*</font> Card number</TD><TD><INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19></TD></TR><TR><TD ALIGN="right"><font color="#ff0000">*</font> Expration</TD><TD>!. expselect("CARD", $paydate). qq!</TD></TR><TR><TD ALIGN="right"><font color="#ff0000">*</font> Name on card</TD><TD><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname"></TD></TR>!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10> Type <SELECT NAME="CHEK_paytype">!. join('', map {qq!<OPTION VALUE="$_"!.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>"} @paytypes). qq!</SELECT><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10> Type <SELECT NAME="DCHK_paytype">!. join('', map {qq!<OPTION VALUE="$_"!.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>"} @paytypes). qq!</SELECT><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><INPUT TYPE="hidden" NAME="BILL_month" VALUE="12"><INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">Attention<INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $cvv_enabled ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<TR><TD ALIGN="right">CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)</TD><TD><INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4></TD></TR>!;
+ }
+ }
+ if ( $paystate_enabled ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CHEK DCHK) ) {
+ my ($county_html, $state_html, $country_html) =
+ regionselector( {
+ prefix => "${payby}_pay",
+ selected_county => $county,
+ selected_state => $state,
+ selected_country => $country,
+ default_state => $statedefault,
+ default_country => $countrydefault,
+ locales => \@cust_main_county,
+ } );
+ $paybychecked{$payby} .= "<BR>${r}Bank state $county_html $state_html";
+ }
+ }
+
+use Tie::IxHash;
+use HTML::Widgets::SelectLayers;
+
+ my %payby_index = ( 'CARD' => qq/Credit Card/,
+ 'DCRD' => qq/Credit Card/,
+ 'CHEK' => qq/Check/,
+ 'DCHK' => qq/Check/,
+ 'LECB' => qq/Phone Bill Billing/,
+ 'BILL' => qq/Billing/,
+ 'COMP' => qq/Complimentary/,
+ 'PREPAY' => qq/Prepaid Card/,
+ );
+
+
+tie my %options, 'Tie::IxHash', ();
+
+foreach my $payby_option ( @payby ) {
+ $options{$payby_option} = $payby_index{$payby_option};
+}
+
+my $selected_layer = ( grep { $_ eq 'CARD' } @payby ) ? 'CARD' : $payby[0];
+
+HTML::Widgets::SelectLayers->new(
+ options => \%options,
+ selected_layer => $selected_layer,
+ form_name => 'dummy',
+ html_between => '</td></tr></table>',
+ form_action => 'dummy.cgi',
+ layer_callback => sub { my $layer = shift; return $paybychecked{$layer}. '</TABLE>'; },
+)->html;
+
+
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields
+<FORM name="signup_form" action="<%= $self_url %>" METHOD="POST" onsubmit="return fixup_form();"><BR><BR>First package
+<INPUT TYPE="hidden" NAME="promo_code" VALUE="<%= $promo_code %>">
+<INPUT TYPE="hidden" NAME="reg_code" VALUE="<%= $reg_code %>">
+<TABLE BGCOLOR="<%= $box_bgcolor || '#c0c0c0' %>" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart">
+
+ <%=
+ $OUT .= '<OPTION VALUE="">(none)'
+ unless scalar(@part_pkg) == 1 or $default_pkgpart;
+ foreach my $part_pkg ( @part_pkg ) {
+ $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $part_pkg->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<%=
+ if ( $signup_service eq 'svc_phone' ) {
+
+ $OUT .= '<TR><TD ALIGN="right">Phone number</TD><TD>'.
+ didselector( 'field' => 'phonenum',
+ 'svcpart' => $default_svcpart,
+ ).
+ '</TD></TR>';
+
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Voicemail PIN</TD>
+ <TD><INPUT TYPE="pin" NAME="pin" VALUE="$pin"></TD>
+</TR>
+ENDOUT
+
+ } else {
+
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="$username"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="$_password"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="$_password2"></TD>
+</TR>
+ENDOUT
+
+ if ( $security_phrase ) {
+ $OUT .= <<SECPHRASE;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+SECPHRASE
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+
+ }
+
+ if ( @svc_acct_pop ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector( 'popnum' => $popnum,
+ 'pops' => \@svc_acct_pop,
+ 'init_popstate' => $init_popstate,
+ 'popac' => $popac,
+ 'acstate' => $acstate,
+ ).
+ '</TD></TR>';
+ } else {
+ $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+ }
+
+%>
+
+</TABLE>
+
+<%=
+if ( @optional_packages ) {
+ my @html;
+ foreach my $ii ( 0 .. $#optional_packages) {
+ my $friendly_index = $ii + 1;
+ if ($optional_packages[$ii]) {
+ push @html, qq|<BR>Optional Package #$friendly_index <br />|,'<table bgcolor="#c0c0c0"><tr><td>';
+
+ push @html, qq|<select name="optional_package${ii}">|;
+ push @html, qq|<option value="none"></option>|;
+ push @html, map { qq|<option value="$_->{pkgpart}">$_->{pkg}</option>| } @{$optional_packages[$ii]};
+ push @html, q|</select>|;
+
+ push @html, '</td></tr></table>';
+ }
+ $OUT = join("\n", @html);
+ }
+} else {
+$OUT = ''
+}
+%>
+
+<BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup">
+<script language="JavaScript">
+
+function fixup_form() {
+
+ // copy payment method data up to OneTrueForm
+
+ var payment_method_elements = new Array( 'payinfo', 'payinfo1', 'payinfo2', 'payname', 'paycvv' , 'paystate', 'paytype', 'month', 'year','type' );
+ var payment_method_form_name = document.OneTrueForm.select.options[document.OneTrueForm.select.selectedIndex].value;
+ document.OneTrueForm.elements['payby'].value = payment_method_form_name;
+ var payment_method_form = document.forms[payment_method_form_name];
+
+ for ( ii = 0 ; ii < payment_method_elements.length ; ii++ ) {
+ var true_element_name = payment_method_form_name + '_' + payment_method_elements[ii];
+ copyelement ( payment_method_form.elements[true_element_name],
+ document.OneTrueForm.elements[true_element_name] );
+ }
+
+ // Copy signup details to OneTrueForm
+
+ var signup_elements = new Array (
+ 'promo_code', 'reg_code', 'pkgpart',
+ 'username', '_password', '_password2', 'sec_phrase', 'popnum',
+ 'countrycode', 'phonenum', 'sip_password', 'pin'
+ );
+
+ for ( ii = 0 ; ii < signup_elements.length ; ii ++ ) {
+ copyelement ( document.signup_form.elements[signup_elements[ii]],
+ document.OneTrueForm.elements[signup_elements[ii]]);
+ }
+
+ document.OneTrueForm.submit();
+ return false;
+}
+
+function copyelement(from, to) {
+// alert ( from + ' ' + to );
+
+ if ( from == undefined ) {
+ to.value = '';
+ } else {
+ if ( from.type == 'select-one' ) {
+ to.value = from.options[from.selectedIndex].value;
+ } else if ( from.type == 'checkbox' ) {
+ if ( from.checked ) {
+ to.value = from.value;
+ } else {
+ to.value = '';
+ }
+ } else {
+ if ( from.value == undefined ) {
+ to.value = '';
+ } else {
+ to.value = from.value;
+ }
+ }
+// alert(from.name + " (" + from.type + "): " + to.name + " => " + to.value);
+ }
+}
+
+</script>
+</FORM>
+<%= $OUT .= $body_footer %>
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/stateselect.html b/fs_selfservice/FS-SelfService/cgi/stateselect.html
new file mode 100644
index 0000000..ba55bff
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/stateselect.html
@@ -0,0 +1,134 @@
+<HTML><HEAD><TITLE>ISP Signup</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup - state selection</FONT><BR><BR>
+<SCRIPT>
+function gotoURL(object) {
+ window.location.href = object.options[object.selectedIndex].value;
+}
+</SCRIPT>
+<FORM>
+Select your state from the map or dropdown:
+<MAP NAME=usmap>
+<area shape=poly COORDS="264,157,286,155,292,193,276,195,270,199,264,157" href="signup.cgi?init_popstate=AL">
+<area shape=poly COORDS="28,197,46,185,72,199,72,241,88,243,102,261,92,263,70,241,42,243,28,257,12,259,34,243,20,233,16,223,34,215,22,207,30,205,28,197" href="../states/Alaska.html">
+<area shape=poly COORDS="70,137,106,137,100,189,84,187,60,173,70,133,70,137,70,137" href="signup.cgi?init_popstate=AZ">
+<area shape=poly COORDS="250,153,242,179,220,177,218,171,216,145,252,143,250,155,250,153" href="signup.cgi?init_popstate=AR">
+<area shape=poly COORDS="10,79,38,81,30,109,62,151,56,173,40,169,20,145,4,101,10,75,26,79,10,79,10,79" href="signup.cgi?init_popstate=CA">
+<area shape=poly COORDS="108,103,158,107,154,141,104,137,110,101,128,103,108,103" href="signup.cgi?init_popstate=CO">
+<area shape=poly COORDS="374,107,405,105,405,123,372,125,374,107" href="signup.cgi?init_popstate=CT">
+<area shape=poly COORDS="370,143,402,145,405,157,362,157,370,143" href="signup.cgi?init_popstate=DE">
+<area shape=poly COORDS="275,193,325,187,327,197,341,219,341,233,335,237,317,215,315,205,307,195,293,203,275,193" href="signup.cgi?init_popstate=FL">
+<area shape=poly COORDS="297,153,283,155,297,191,321,189,321,169,297,153" href="signup.cgi?init_popstate=GA">
+<area shape=poly COORDS="98,233,142,263,156,251,162,239,164,229,136,231,94,221,100,235,98,233" href="signup.cgi?init_popstate=HI">
+<area shape=poly COORDS="68,21,76,21,72,35,80,47,80,55,84,65,100,69,94,93,56,83,66,51,70,19,68,21" href="signup.cgi?init_popstate=ID">
+<area shape=poly COORDS="242,91,258,89,266,123,256,139,234,109,248,87,242,91" href="signup.cgi?init_popstate=IL">
+<area shape=poly COORDS="261,95,265,123,265,131,285,117,277,91,261,95" href="signup.cgi?init_popstate=IN">
+<area shape=poly COORDS="198,87,206,111,232,109,240,99,240,91,232,79,198,87" href="signup.cgi?init_popstate=IA">
+<area shape=poly COORDS="158,111,158,135,214,139,214,127,208,113,158,111" href="signup.cgi?init_popstate=KS">
+<area shape=poly COORDS="263,133,275,129,289,115,303,121,307,129,299,135,251,141,269,131,263,133" href="signup.cgi?init_popstate=KY">
+<area shape=poly COORDS="222,179,246,179,244,197,258,193,262,213,226,209,224,177,222,179" href="signup.cgi?init_popstate=LA">
+<area shape=poly COORDS="363,37,373,59,373,47,387,31,377,9,365,15,363,37" href="signup.cgi?init_popstate=ME">
+<area shape=poly COORDS="376,159,405,159,405,175,374,177,376,159" href="signup.cgi?init_popstate=MD">
+<area shape=poly COORDS="378,74,380,88,404,88,404,72,378,74" href="signup.cgi?init_popstate=MA">
+<area shape=poly COORDS="265,73,269,83,265,93,293,91,295,71,281,53,271,53,267,69,265,73,265,73" href="signup.cgi?init_popstate=MI">
+<area shape=poly COORDS="194,31,222,33,242,35,224,51,222,63,222,73,234,79,196,85,194,31" href="signup.cgi?init_popstate=MN">
+<area shape=poly COORDS="265,159,271,199,257,201,259,195,241,197,251,155,265,159" href="signup.cgi?init_popstate=MS">
+<area shape=poly COORDS="206,113,234,111,256,139,248,147,214,145,208,111,206,113" href="signup.cgi?init_popstate=MO">
+<area shape=poly COORDS="78,23,148,31,146,67,84,63,78,35,80,19,78,23" href="signup.cgi?init_popstate=MT">
+<area shape=poly COORDS="146,85,148,103,158,105,164,109,206,109,198,85,144,87,146,85" href="signup.cgi?init_popstate=NE">
+<area shape=poly COORDS="40,83,76,87,64,151,32,109,40,83,40,83" href="signup.cgi?init_popstate=NV">
+<area shape=poly COORDS="298,11,330,9,330,25,298,25,298,11" href="signup.cgi?init_popstate=NH">
+<area shape=poly COORDS="372,127,404,125,405,141,368,139,376,125,372,127" href="signup.cgi?init_popstate=NJ">
+<area shape=poly COORDS="106,137,100,191,122,187,148,187,150,139,106,137,106,137" href="signup.cgi?init_popstate=NM">
+<area shape=poly COORDS="313,79,331,63,337,45,349,45,359,65,357,79,345,65,315,77,313,79,313,79" href="signup.cgi?init_popstate=NY">
+<area shape=poly COORDS="309,137,295,151,319,149,337,153,357,131,351,129,309,137,309,137" href="signup.cgi?init_popstate=NC">
+<area shape=poly COORDS="146,31,148,57,198,57,190,31,146,31,146,31" href="signup.cgi?init_popstate=ND">
+<area shape=poly COORDS="281,93,285,113,299,121,311,101,309,85,299,93,281,93,281,93" href="signup.cgi?init_popstate=OH">
+<area shape=poly COORDS="148,145,174,145,174,163,218,171,216,143,150,139,150,145,156,143,148,145,148,145" href="signup.cgi?init_popstate=OK">
+<area shape=poly COORDS="20,41,8,73,16,77,22,77,28,77,36,79,42,81,48,83,56,83,66,49,20,41,20,41" href="signup.cgi?init_popstate=OR">
+<area shape=poly COORDS="309,83,345,71,351,93,313,105,309,83,309,83" href="signup.cgi?init_popstate=PA">
+<area shape=poly COORDS="376,93,405,93,405,107,376,105,376,93" href="signup.cgi?init_popstate=RI">
+<area shape=poly COORDS="301,155,321,149,337,155,325,175,301,157,301,155,301,155" href="signup.cgi?init_popstate=SC">
+<area shape=poly COORDS="146,59,198,61,198,83,146,83,148,57,146,59,146,59" href="signup.cgi?init_popstate=SD">
+<area shape=poly COORDS="255,145,251,157,297,153,311,133,255,145,255,145" href="signup.cgi?init_popstate=TN">
+<area shape=poly COORDS="150,145,172,145,174,167,198,173,218,173,228,207,204,221,198,231,202,247,180,241,154,207,146,219,120,189,154,189,152,145,150,145,150,145" href="signup.cgi?init_popstate=TX">
+<area shape=poly COORDS="78,89,96,91,96,103,110,103,106,135,70,133,78,89,78,89" href="signup.cgi?init_popstate=UT">
+<area shape=poly COORDS="298,29,332,29,332,47,294,45,298,29" href="signup.cgi?init_popstate=VT">
+<area shape=poly COORDS="307,127,297,137,351,127,349,113,341,111,341,105,329,107,315,131,307,127,307,127" href="signup.cgi?init_popstate=VA">
+<area shape=poly COORDS="32,13,68,19,64,47,20,39,20,13,30,19,32,13,32,13" href="signup.cgi?init_popstate=WA">
+<area shape=poly COORDS="303,119,313,129,329,103,311,105,299,121,313,127,303,119,303,119" href="signup.cgi?init_popstate=WV">
+<area shape=poly COORDS="228,51,256,55,254,89,238,89,234,77,224,71,230,49,236,53,228,51,228,51" href="signup.cgi?init_popstate=WI">
+<area shape=poly COORDS="146,71,144,103,96,99,102,63,148,69,146,71,146,71" href="signup.cgi?init_popstate=WY">
+</MAP>
+<IMG SRC="map.gif" usemap=#usmap WIDTH=405 HEIGHT=270 border=0><BR>
+<SELECT NAME="init_popstate" onChange="gotoURL(this.form.init_popstate)">
+<OPTION VALUE="stateselect.html"></OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AL">Alabama</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AK">Alaska</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=AS">American Samoa</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=AZ">Arizona</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AR">Arkansas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=CA">California</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=CO">Colorado</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=CT">Connecticut</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=DE">Delaware</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=DC">District of Columbia</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=FM">Federated States of Micronesia</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=FL">Florida</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=GA">Georgia</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=GU">Guam</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=HI">Hawaii</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=ID">Idaho</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=IL">Illinois</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=IN">Indiana</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=IA">Iowa</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=KS">Kansas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=KY">Kentucky</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=LA">Louisiana</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=ME">Maine</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=MH">Marshall Islands</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=MD">Maryland</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MA">Massachusetts</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MI">Michigan</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MN">Minnesota</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MS">Mississippi</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MO">Missouri</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MT">Montana</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NE">Nebraska</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NV">Nevada</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NH">New Hampshire</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NJ">New Jersey</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NM">New Mexico</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NY">New York</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NC">North Carolina</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=ND">North Dakota</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=MP">Northern Mariana Islands</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=OH">Ohio</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=OK">Oklahoma</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=OR">Oregon</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=PW">Palau</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=PA">Pennsylvania</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=PR">Puerto Rico</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=RI">Rhode Island</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=SC">South Carolina</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=SD">South Dakota</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=TN">Tennessee</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=TX">Texas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=UT">Utah</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=VT">Vermont</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=VI">Virgin Islands</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=VA">Virginia</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WA">Washington</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WV">West Virginia</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WI">Wisconsin</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WY">Wyoming</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Africa</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AA">Armed Forces Americas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Canada</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Europe</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Middle East</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AP">Armed Forces Pacific</OPTION>
+-->
+</SELECT>
+</FORM>
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/success-delayed.html b/fs_selfservice/FS-SelfService/cgi/success-delayed.html
new file mode 100644
index 0000000..5eeed59
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/success-delayed.html
@@ -0,0 +1,16 @@
+<HTML><HEAD><TITLE>Signup successful</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Signup successful</FONT><BR><BR>
+Thanks for signing up!
+<BR><BR>
+Signup information for <%= $email_name %>:
+<BR><BR>
+Username: <%= $username %><BR>
+Password: <%= $password %><BR>
+Access number: (<%= $ac %>) / <%= $exch %> - <%= $local %><BR>
+Package: <%= $pkg %><BR>
+Charge: <%= sprintf('$%.2f', $part_pkg->{'options'}->{'setup_fee'}) %><BR>
+In <%= $part_pkg->{'options'}->{'free_days'} %> days you will be charged
+ <%= sprintf('$%.2f', $part_pkg->{'options'}->{'recur_fee'}) %>
+and <%= $part_pkg->{'freq_pretty'} %> thereafter.<BR>
+
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/success.html b/fs_selfservice/FS-SelfService/cgi/success.html
new file mode 100644
index 0000000..92185c3
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/success.html
@@ -0,0 +1,41 @@
+<HTML>
+
+<HEAD>
+ <TITLE>Signup successful</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="#e8e8e8">
+
+<FONT SIZE=7>Signup successful</FONT><BR><BR>
+
+Thanks for signing up! Save this information for future reference.
+<BR><BR>
+
+Signup information for <%= $email_name %>:
+<BR><BR>
+
+<%=
+ if ($signup_service eq 'svc_acct' || !$signup_service ) { #just in case
+ $OUT .= <<END
+ Username: $username<BR>
+ Password: $password><BR>
+ Access number: ($ac) / $exch - $local <BR>
+END
+ } elsif ( $signup_service eq 'svc_phone' ) {
+ $OUT .= <<END
+ <!-- Countrycode: $countrycode <BR>-->
+ Phone number: $phonenum<BR>
+ SIP Server: itsp.sip.server.name<BR>
+ SIP Login: $phonenum<BR>
+ SIP Password: $sip_password<BR>
+ Voicemail PIN: $pin<BR>
+END
+ } else {
+ die "unknown signup service $signup_service";
+ }
+%>
+
+ Package: <%= $pkg %><BR>
+
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/svc_acct.html b/fs_selfservice/FS-SelfService/cgi/svc_acct.html
new file mode 100644
index 0000000..0024438
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/svc_acct.html
@@ -0,0 +1,58 @@
+<FONT SIZE=4>Setup <%= $svc %></FONT><BR><BR>
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error setting up $svc: $error!.
+ '</FONT><BR><BR>';
+} ''; %>
+<FORM ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_svc_acct">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $svcpart %>">
+<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#cccccc">
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<%=
+ $OUT .= domainselector(pkgnum=>$pkgnum, svcpart=>$svcpart);
+%>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $_password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $_password2 %>"></TD>
+</TR>
+<%=
+ if ( $security_phrase ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( @svc_acct_pop ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector( 'popnum' => $popnum,
+ 'pops' => \@svc_acct_pop,
+ 'init_popstate' => $init_popstate,
+ 'popac' => $popac,
+ 'acstate' => $acstate,
+ ).
+ '</TD></TR>';
+ } else {
+ $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+ }
+%>
+</TABLE>
+<INPUT TYPE="submit" VALUE="Setup">
+</FORM>
diff --git a/fs_selfservice/FS-SelfService/cgi/view_customer.html b/fs_selfservice/FS-SelfService/cgi/view_customer.html
new file mode 100644
index 0000000..5bfb9b6
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/view_customer.html
@@ -0,0 +1,24 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%= $message
+ ? "<FONT SIZE=\"+2\"><B>$message</B></FONT><BR><BR>"
+ : ''
+%>
+
+<%= $small_custview %>
+
+<BR>
+
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/view_invoice.html b/fs_selfservice/FS-SelfService/cgi/view_invoice.html
new file mode 100644
index 0000000..8fa5fb7
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/view_invoice.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<%= $invoice_html %>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/view_support_details.html b/fs_selfservice/FS-SelfService/cgi/view_support_details.html
new file mode 100644
index 0000000..ea21874
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/view_support_details.html
@@ -0,0 +1,78 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Support usage details for
+<%= Date::Format::time2str('%b&nbsp;%o&nbsp;%Y', $beginning) %> -
+<%= Date::Format::time2str('%b&nbsp;%o&nbsp;%Y', $ending) %>
+</FONT><BR><BR>
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+
+<TABLE WIDTH="100%">
+ <TR>
+ <TD WIDTH="50%">
+<%= if ($previous < $beginning) {
+ $OUT .= qq!<A HREF="${url}view_support_details;svcnum=$svcnum;beginning=!;
+ $OUT .= qq!$previous;ending=$beginning">Previous period</A>!;
+ }else{
+ '';
+ } %>
+ </TD>
+ <TD WIDTH="50%" ALIGN="right">
+<%= if ($next > $ending) {
+ $OUT .= qq!<A HREF="${url}view_support_details;svcnum=$svcnum;beginning=!;
+ $OUT .= qq!$ending;ending=$next">Next period</A>!;
+ }else{
+ '';
+ }%>
+ </TD>
+ </TR>
+</TABLE>
+<TABLE BGCOLOR="#cccccc">
+ <TR>
+ <TH ALIGN="left">Ticket</TH>
+ <TH ALIGN="center">Subject</TH>
+ <TH ALIGN="center">Staff</TH>
+ <TH ALIGN="center">Date</TH>
+ <TH ALIGN="center">Status</TH>
+ <TH ALIGN="right">Time</TH>
+ </TR>
+<%= my $total = 0;
+ foreach my $usage ( @usage ) {
+ $OUT .= '<TR><TD ALIGN="left">';
+ $OUT .= $usage->{'ticketid'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $usage->{'subject'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $usage->{'creator'};
+ $OUT .= '</TD><TD ALIGN="left">';
+ $OUT .= Date::Format::time2str('%T%P %a&nbsp;%b&nbsp;%o&nbsp;%Y', $usage->{'_date'});
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $usage->{'status'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ my $duration = $usage->{'support'};
+ $total += $usage->{'support'};
+ my $h = int($duration/3600);
+ my $m = sprintf("%02d", int(($duration % 3600) / 60));
+ my $s = sprintf("%02d", $duration % 60);
+ $OUT .= $usage->{'support'} < 0 ? '-' : '';
+ $OUT .= "$h:$m:$s";
+ $OUT .= '</TD></TR>';
+ }
+ my $h = int($total/3600);
+ my $m = sprintf("%02d", int(($total % 3600) / 60));
+ my $s = sprintf("%02d", $total % 60);
+ $OUT .= qq!<TR><TD COLSPAN="5"></TD><TD ALIGN="right"><HR></TD></TR>!;
+ $OUT .= qq!<TR><TD COLSPAN="5"></TD><TD ALIGN="right">$h:$m:$s</TD></TR>!;
+ %>
+
+</TABLE>
+<BR>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/view_usage.html b/fs_selfservice/FS-SelfService/cgi/view_usage.html
new file mode 100644
index 0000000..b78f997
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/view_usage.html
@@ -0,0 +1,58 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Service usage</FONT><BR><BR>
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+
+<TABLE BGCOLOR="#cccccc">
+ <TR>
+ <TH ALIGN="left">Account</TH>
+ <TH ALIGN="right">Time remaining</TH>
+ <TH ALIGN="right">Upload remaining</TH>
+ <TH ALIGN="right">Download remaining</TH>
+ <TH ALIGN="right">Total remaining</TH>
+ </TR>
+<%= foreach my $svc ( @svcs ) {
+ my $link = "${url}view_usage_details;".
+ "svcnum=$svc->{'svcnum'};beginning=0;ending=0";
+ $OUT .= '<TR><TD>';
+ $OUT .= qq!<A HREF="$link">!. $svc->{'label'}. ': '. $svc->{'value'}.'</A>';
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'seconds'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'upbytes'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'downbytes'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'totalbytes'};
+ $OUT .= '</TD></TR>';
+ if ( $svc->{'recharge_amount'} ) {
+ my $link = "${url}process_order_recharge;".
+ "svcnum=$svc->{'svcnum'}";
+ $OUT .= '<TR><TD ALIGN="right">';
+ $OUT .= qq!<A HREF="$link">!.'Recharge for $';
+ $OUT .= $svc->{'recharge_amount'} . '</A> with';
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'recharge_seconds'} if $svc->{'recharge_seconds'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'recharge_upbytes'} if $svc->{'recharge_upbytes'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'recharge_downbytes'} if $svc->{'recharge_downbytes'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= $svc->{'recharge_totalbytes'} if $svc->{'recharge_totalbytes'};
+ $OUT .= '</TD></TR>';
+ }
+ } %>
+
+</TABLE>
+<BR>
+
+</TD></TR></TABLE>
+
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/view_usage_details.html b/fs_selfservice/FS-SelfService/cgi/view_usage_details.html
new file mode 100644
index 0000000..6bac748
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/view_usage_details.html
@@ -0,0 +1,84 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4>Service usage details for
+<%= Date::Format::time2str('%b&nbsp;%o&nbsp;%Y', $beginning) %> -
+<%= Date::Format::time2str('%b&nbsp;%o&nbsp;%Y', $ending) %>
+</FONT><BR><BR>
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+
+<TABLE WIDTH="100%">
+ <TR>
+ <TD WIDTH="50%">
+<%= if ($previous < $beginning) {
+ $OUT .= qq!<A HREF="${url}view_usage_details;svcnum=$svcnum;beginning=!;
+ $OUT .= qq!$previous;ending=$beginning">Previous period</A>!;
+ }else{
+ '';
+ } %>
+ </TD>
+ <TD WIDTH="50%" ALIGN="right">
+<%= if ($next > $ending) {
+ $OUT .= qq!<A HREF="${url}view_usage_details;svcnum=$svcnum;beginning=!;
+ $OUT .= qq!$ending;ending=$next">Next period</A>!;
+ }else{
+ '';
+ }%>
+ </TD>
+ </TR>
+</TABLE>
+<TABLE BGCOLOR="#cccccc">
+ <TR>
+ <TH ALIGN="left">Account</TH>
+ <TH ALIGN="right">Start Time</TH>
+ <TH ALIGN="right">Duration</TH>
+ <TH ALIGN="right">Upload</TH>
+ <TH ALIGN="right">Download</TH>
+ </TR>
+<%= my $total = 0;
+ my $utotal = 0;
+ my $dtotal = 0;
+ foreach my $usage ( @usage ) {
+ $OUT .= '<TR><TD>';
+ $OUT .= $usage->{'username'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= Date::Format::time2str('%T%P %a&nbsp;%b&nbsp;%o&nbsp;%Y', $usage->{'acctstarttime'});
+ $OUT .= '</TD><TD ALIGN="right">';
+ my $duration = $usage->{'acctstoptime'} - $usage->{'acctstarttime'};
+ $total += $duration;
+ my $h = int($duration/3600);
+ my $m = sprintf("%02d", int(($duration % 3600) / 60));
+ my $s = sprintf("%02d", $duration % 60);
+ $OUT .= "$h:$m:$s";
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= Number::Format::format_bytes($usage->{'acctinputoctets'}, precision => 2);
+ $utotal += $usage->{'acctinputoctets'};
+ $OUT .= '</TD><TD ALIGN="right">';
+ $OUT .= Number::Format::format_bytes($usage->{'acctoutputoctets'}, precision => 2);
+ $dtotal += $usage->{'acctoutputoctets'};
+ $OUT .= '</TD></TR>';
+ }
+ my $h = int($total/3600);
+ my $m = sprintf("%02d", int(($total % 3600) / 60));
+ my $s = sprintf("%02d", $total % 60);
+ $OUT .= qq!<TR><TD></TD><TD></TD>!;
+ $OUT .= qq!<TD ALIGN="right"><HR></TD>! x 3;
+ $OUT .= qq!</TR>!;
+ $OUT .= qq!<TR><TD></TD><TD></TD><TD ALIGN="right">$h:$m:$s</TD>!;
+ $OUT .= qq!<TD ALIGN="right">!;
+ $OUT .= Number::Format::format_bytes($utotal, precision => 2). qq!</TD>!;
+ $OUT .= qq!<TD ALIGN="right">!;
+ $OUT .= Number::Format::format_bytes($dtotal, precision => 2). qq!</TD>!;
+ $OUT .= qq!</TR>!; %>
+
+</TABLE>
+<BR>
+
+</TD></TR></TABLE>
+<%= include('footer') %>
diff --git a/fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi b/fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi
new file mode 100644
index 0000000..559ae04
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi
@@ -0,0 +1,18 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use XMLRPC::Transport::HTTP;
+use XMLRPC::Lite; # for XMLRPC::Serializer
+use FS::SelfService::XMLRPC;
+
+my %typelookup = (
+ base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'],
+ dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'],
+ string => [40, sub {1}, 'as_string'],
+);
+my $serializer = new XMLRPC::Serializer(typelookup => \%typelookup);
+
+XMLRPC::Transport::HTTP::CGI->dispatch_to('FS::SelfService::XMLRPC')
+ ->serializer($serializer)
+ ->handle;
+
diff --git a/fs_selfservice/FS-SelfService/freeside-selfservice-clientd b/fs_selfservice/FS-SelfService/freeside-selfservice-clientd
new file mode 100644
index 0000000..bdc8e15
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/freeside-selfservice-clientd
@@ -0,0 +1,272 @@
+#!/usr/bin/perl -w
+#
+# freeside-selfservice-clientd
+#
+# This is run REMOTELY over ssh by freeside-selfservice-server
+
+use strict;
+use subs qw(spawn logmsg lock_write unlock_write);
+use Fcntl qw(:flock);
+use POSIX qw(:sys_wait_h);
+use Socket;
+use Storable 2.09 qw(nstore_fd fd_retrieve);
+use IO::Handle qw(_IONBF);
+use IO::Select;
+use IO::File;
+
+#STDOUT->setbuf('');
+
+my $tag = scalar(@ARGV) ? '.'.shift : '';
+
+use vars qw( $Debug );
+$Debug = 2; #2 will turn on child logging
+ #3 will log packet contents,#including passwords
+ #4 will log receipts of all packets from server including
+ # keepalives (big!)
+
+my $socket = "/usr/local/freeside/selfservice_socket$tag";
+my $pid_file = "$socket.pid";
+
+my $log_file = "/usr/local/freeside/selfservice$tag.log";
+
+my $lock_file = "/usr/local/freeside/selfservice$tag.writelock";
+
+#my $me = '[client]';
+
+$|=1;
+
+$SIG{__WARN__} = \&_logmsg;
+
+#read data to be cached or something
+#warn "$me Reading init data\n" if $Debug;
+#my $signup_init =
+
+warn "Creating $lock_file\n" if $Debug;
+open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
+close LOCKFILE;
+
+warn "Creating $socket\n" if $Debug;
+my $uaddr = sockaddr_un($socket);
+my $proto = getprotobyname('tcp');
+socket(Server,PF_UNIX,SOCK_STREAM,0) or die "socket: $!";
+unlink($socket);
+bind(Server, $uaddr) or die "bind: $!";
+listen(Server,SOMAXCONN) or die "listen: $!";
+
+if ( -e $pid_file ) {
+ open(PIDFILE,"<$pid_file");
+ my $old_pid = <PIDFILE>;
+ close PIDFILE;
+ if ( $old_pid =~ /^(\d+)$/ ) {
+ kill 'TERM', $1;
+ }
+}
+open(PIDFILE,">$pid_file");
+print PIDFILE "$$\n";
+close PIDFILE;
+
+#my $waitedpid;
+#sub REAPER { $waitedpid = wait; $SIG{CHLD} = \&REAPER; }
+#$SIG{CHLD} = \&REAPER;
+
+warn "enabling keep alives\n" if $Debug;
+nstore_fd( { _packet => '_enable_keepalive' } , \*STDOUT );
+
+warn "entering main loop\n" if $Debug;
+
+my %kids;
+
+my $s = new IO::Select;
+$s->add(\*STDIN);
+$s->add(\*Server);
+
+#for ( $waitedpid = 0;
+# accept(Client,Server) || $waitedpid;
+# $waitedpid = 0, close Client)
+#{
+# next if $waitedpid;
+
+#$SIG{PIPE} = sub { warn "SIGPIPE received" };
+#$SIG{CHLD} = sub { warn "SIGCHLD received" };
+
+#sub REAPER { warn "SIGCHLD received"; my $pid = wait; $SIG{CHLD} = \&REAPER; }
+#sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; }
+#sub REAPER { my $pid = wait; delete $kids{$pid}; $SIG{CHLD} = \&REAPER; }
+#$SIG{CHLD} = \&REAPER;
+
+my $undisp = 0;
+while (1) {
+
+ &reap_kids;
+
+ warn "waiting for connection\n" if $Debug && !$undisp;
+
+ #my @handles = $s->can_read();
+ my @handles = $s->can_read(5);
+ $undisp = !scalar(@handles);
+ foreach my $handle ( @handles ) {
+
+ if ( $handle == \*STDIN ) {
+
+ warn "receiving packet from server\n" if $Debug > 3;
+
+ my $packet = fd_retrieve(\*STDIN);
+ my $token = $packet->{'_token'};
+
+ if ( $token eq '_keepalive' ) {
+ $undisp = 1;
+ next;
+ }
+
+ warn "received packet from server with token $token\n".
+ ( $Debug > 2
+ ? join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
+ : '' )
+ if $Debug;
+
+ if ( exists($kids{$token}) ) {
+ warn "sending return packet to $token via $kids{$token}\n"
+ if $Debug;
+ nstore_fd($packet, $kids{$token});
+ warn "flushing to $token\n" if $Debug;
+ until ( $kids{$token}->flush ) {
+ warn "WARNING: error flushing: $!";
+ sleep 1;
+ }
+ #no close or delete here - will block waiting for child
+ warn "done with $token\n" if $Debug;
+ } else {
+ warn "WARNING: unknown token $token, discarding message";
+ }
+
+ } elsif ( $handle == \*Server ) {
+
+ until ( accept(Client, Server) ) {
+ warn "WARNING: accept failed: $!";
+ next;
+ }
+
+ warn "received local connection; forking\n" if $Debug;
+
+ spawn sub { #child
+ warn "[child-$$] reading packet from local client" if $Debug > 1;
+ my $packet = fd_retrieve(\*Client);
+ warn "[child-$$] packet received:\n".
+ join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
+ if $Debug > 2;
+ my $command = $packet->{'command'};
+ #handle some commands weirdly?
+ $packet->{_token}=$$;
+
+ warn "[child-$$] locking write stream\n" if $Debug > 1;
+ lock_write;
+
+ warn "[child-$$] sending packet to remote server\n" if $Debug > 1;
+ nstore_fd($packet, \*STDOUT) or die "FATAL: can't send response: $!";
+
+ warn "[child-$$] flushing write stream\n" if $Debug > 1;
+ STDOUT->flush or die "FATAL: can't flush: $!";
+
+ warn "[child-$$] releasing write lock\n" if $Debug > 1;
+ unlock_write;
+
+ warn "[child-$$] closing write stream\n" if $Debug > 1;
+ close STDOUT or die "FATAL: can't close write stream: $!"; #??!
+
+ warn "[child-$$] waiting for response from parent\n" if $Debug > 1;
+ my $w = new IO::Select;
+ $w->add(\*STDIN);
+ until ( $w->can_read ) {
+ warn "[child-$$] WARNING: interrupted select: $!\n";
+ }
+ my $rv = fd_retrieve(\*STDIN);
+
+ #close STDIN;
+
+ warn "[child-$$] sending response to local client" if $Debug > 1;
+ nstore_fd($rv, \*Client);
+ Client->flush or die "FATAL: can't flush to local client: $!";
+ close Client or die "FATAL: can't close connection to local client: $!";
+
+ warn "[child-$$] child exiting" if $Debug > 1;
+ exit;
+
+ }; #eo child
+
+ #close Client;
+
+ } else {
+ die "wtf? $handle";
+ }
+
+ }
+
+}
+
+sub reap_kids {
+ #warn "reaping kids\n";
+ foreach my $pid ( keys %kids ) {
+ my $kid = waitpid($pid, WNOHANG);
+ if ( $kid > 0 ) {
+ close $kids{$kid};
+ delete $kids{$kid};
+ }
+ }
+ #warn "done reaping\n";
+}
+
+sub spawn {
+ my $coderef = shift;
+
+ unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') {
+ use Carp;
+ confess "usage: spawn CODEREF";
+ }
+
+ my $pid;
+ #if (!defined($pid = fork)) {
+ my $kid = new IO::Handle;
+ if (!defined($pid = open($kid, '|-'))) {
+ warn "WARNING: cannot fork: $!";
+ return;
+ } elsif ($pid) {
+ warn "begat $pid" if $Debug;
+ $kids{$pid} = $kid;
+ #$kids{$pid}->autoflush;
+ return; # I'm the parent
+ }
+ # else I'm the child -- go spawn
+
+# open(STDIN, "<&Client") || die "can't dup client to stdin";
+# open(STDOUT, ">&Client") || die "can't dup client to stdout";
+# open(STDERR, ">&STDOUT") || die "can't dup stdout to stderr";
+ exit &$coderef();
+}
+
+sub _logmsg {
+ chomp( my $msg = shift );
+ my $log = new IO::File ">>$log_file";
+ die "can't open $log_file: $!" unless defined($log);
+ flock($log, LOCK_EX);
+ seek($log, 0, 2);
+ print $log "[client] [". scalar(localtime). "] [$$] $msg\n";
+ flock($log, LOCK_UN);
+ close $log;
+}
+
+sub lock_write {
+ #broken on freebsd?
+ #flock(STDOUT, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+
+ #open a new one for each kid to get a unique lock
+ open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
+
+ flock(LOCKFILE, LOCK_EX) or die "FATAL: can't lock $lock_file: $!";
+}
+
+sub unlock_write {
+ #broken on freebsd?
+ #flock(STDOUT, LOCK_UN) or die "FATAL: can't release write lock: $!";
+
+ flock(LOCKFILE, LOCK_UN) or die "FATAL: can't unlock $lock_file: $!";
+}
diff --git a/fs_selfservice/FS-SelfService/freeside-selfservice-xmlrpc-server b/fs_selfservice/FS-SelfService/freeside-selfservice-xmlrpc-server
new file mode 100644
index 0000000..bd4f83b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/freeside-selfservice-xmlrpc-server
@@ -0,0 +1,59 @@
+#!/usr/bin/perl -w
+#
+# freeside-selfservice-xmlrpc-server
+#
+
+use strict;
+use Fcntl qw(:flock);
+use POSIX;
+use Getopt::Std;
+use XMLRPC::Transport::HTTP;
+use XMLRPC::Lite; # for XMLRPC::Serializer;
+use FS::SelfService::XMLRPC;
+
+use vars qw( $opt_p $opt_d );
+use vars qw( $DEBUG );
+
+getopts("p:d");
+$DEBUG = $opt_d;
+my $tag = $opt_p ? ':'.$opt_p : '';
+
+my %typelookup = (
+ base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'],
+ dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'],
+ string => [40, sub {1}, 'as_string'],
+);
+my $serializer = new XMLRPC::Serializer(typelookup => \%typelookup);
+
+my $log_file = "/usr/local/freeside/selfservice.xmlrpc$tag.log";
+
+my $pid = fork;
+defined($pid) or die "Can't fork to start: $!";
+print "Started daemon with pid $pid\n" if $pid;
+exit if $pid;
+
+POSIX::setsid();
+open STDIN, "/dev/null" or die "Can't get rid of STDIN";
+open STDOUT, ">/dev/null" or die "Can't get rid of STDOUT";
+open STDERR, ">&STDOUT" or die "Can't get rid of STDERR";
+
+$SIG{__WARN__} = \&_logmsg;
+$SIG{__DIE__} = sub { &_logmsg(@_); exit };
+
+my $daemon = XMLRPC::Transport::HTTP::Daemon
+ ->new(LocalPort => $opt_p ? $opt_p : 8080)
+ ->dispatch_to('FS::SelfService::XMLRPC')
+ ->serializer($serializer);
+
+warn "Handling request at ", $daemon->url, "\n";
+$daemon->handle;
+
+sub _logmsg {
+ chomp( my $msg = shift );
+ my $log = new IO::File ">>$log_file";
+ flock($log, LOCK_EX);
+ seek($log, 0, 2);
+ print $log "[". scalar(localtime). "] [$$] $msg\n";
+ flock($log, LOCK_UN);
+ close $log;
+}
diff --git a/fs_selfservice/FS-SelfService/ieak.template b/fs_selfservice/FS-SelfService/ieak.template
new file mode 100755
index 0000000..52edaa9
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/ieak.template
@@ -0,0 +1,40 @@
+[Entry]
+Entry_Name = The Internet
+[Phone]
+Dial_As_Is=no
+Phone_Number = { $exch. $loc }
+Area_Code = { $ac }
+Country_Code = 1
+Country_Id = 1
+[Server]
+Type = PPP
+SW_Compress = Yes
+PW_Encrypt = Yes
+Negotiate_TCP/IP = Yes
+Disable_LCP = No
+[TCP/IP]
+Specify_IP_Address = No
+Specity_Server_Address = No
+IP_Header_Compress = Yes
+Gateway_On_Remote = Yes
+[User]
+Name = { $username }
+Password = { $password }
+Display_Password = Yes
+[Internet_Mail]
+Email_Name = { $email_name }
+Email_Address = { $username }\@domain.tld
+POP_Server = mail.domain.tld
+POP_Server_Port_Number = 110
+POP_Login_Name = { $username }
+POP_Login_Password = { $password }
+SMTP_Server = mail.domain.tld
+SMTP_Server_Port_Number = 25
+Install_Mail = 1
+[Internet_News]
+NNTP_Server = news.domain.tld
+NNTP_Server_Port_Number = 119
+Logon_Required = No
+Install_News = 1
+[Branding]
+Window_Title = The Internet
diff --git a/fs_selfservice/FS-SelfService/test.pl b/fs_selfservice/FS-SelfService/test.pl
new file mode 100644
index 0000000..7468ea4
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/test.pl
@@ -0,0 +1,17 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test;
+BEGIN { plan tests => 1 };
+use FS::SelfService;
+ok(1); # If we made it this far, we're ok.
+
+#########################
+
+# Insert your test code below, the Test module is use()ed here so read
+# its man page ( perldoc Test ) for help writing this test script.
+
diff --git a/fs_selfservice/fri/CHANGE.log b/fs_selfservice/fri/CHANGE.log
new file mode 100644
index 0000000..f25712b
--- /dev/null
+++ b/fs_selfservice/fri/CHANGE.log
@@ -0,0 +1,271 @@
+
+
+Change log - 05/02/2006
+
+ * update of french translation (submitted by Xavier Ourcière)
+
+Change log - 04/28/2006
+
+ * changed PEAR portability flags to try and fix a bug a user is having (maybe a buggy or old version of PEAR on users machine)
+ * fixed no voicemail message to be more intuitive
+ * fixed ajax bug
+ * fixed German i18n translation bug (requested by Wanninger)
+ * fixed settings recording format bug
+ * fixed settings call forward bug
+
+Change log - 04/10/2006
+
+ * added autoplay of recordings (requested by Robert LaPoint)
+ * refactored the response from the asterisk manager interface so do not always have to strip off "value:" from the response
+
+Change log - 04/04/2006
+
+ * abstracted the doc_root (PHP_SELF) to a variable to handle cases where it is not set properly (requested by Diego Iastrubni)
+ * removed error message about user voicemail directory (submitted by Diego Iastrubni)
+ * added feature to login to allow voicemail include files with wildcards (submitted by Diego Iastrubni)
+ * made voicemail password length message more accurate and descriptive on settings page (submitte by Robert Colbert)
+ * added outbound caller id record matching for call monitor page for results returned to individual users (requested by Robert LaPoint)
+ * fixed AJAX bug that kept giving javascript errors. Now form, pass, and parse a full xml doc
+ * fixed bug in description of dial code in help settings page (submitte by Robert Colbert)
+ * fixed bug to disable AJAX if using a browser that does not support AJAX
+ * updated Italian Translation (contributed by Francesco Romano: alteclab.it)
+
+Change log - 03/31/2006
+
+ * updated Spanish Translation (contributed by Antonio Cano damas: igestec.com)
+
+Change log - 03/29/2006
+
+ * added support for voicemail.conf include files (requested by Diego Iastrubni)
+ * updated database connection to support sqlite (and other databases using a connect file) (requested by Diego Iastrubni)
+
+Change log - 03/28/2006
+
+ * updated for PHP5 support
+ * fixed bug in AJAX javascript (fix submitted by Mahmud Fatafta - voicemetro.com)
+
+Change log - 03/23/2006
+
+ * remove variable references in function calls for PHP5 support (PHP4 supports, PHP5 does not, go figure)
+
+Change log - 03/18/2006
+
+ * fixed setting page voicemail options bug (submitted by Dave Vaughn: techcompinc.com)
+ * fixed settings page record settings FreePBX version bug (submitted by Luca Pandolfini)
+
+Change log - 03/13/2006
+
+ * added navigation menus to ajax update
+ * changed voicemail password on settings page so it can be variable length (submitted by vgster)
+ * fixed bug with settings page check boxes
+
+Change log - 03/09/2006
+
+ * fixed bug in error reporting for asterisk config files or recording file directories missing
+ * fixed bug for voicemail message move to perserve permissions, group, and user
+ * fixed bug in .inc and .conf file security (submitted by Diego Iastrubni, François Harvey: securiweb.net, and Adam Gray: novacoast.com)
+
+Change log - 03/07/2006
+
+ * added ajax seemless page refresh to callmonitor and voicemail
+ * added recording playback encryption (requested by François Harvey: securiweb.net)
+ * added ajax page refresh for voicemail and callmonitor (will seemlessly update page realtime)
+ * fixed bug in file permissions when a voicemail was moved (submitted by ?)
+
+Change log - 02/22/2006
+
+ * added filter to not load code not needed if a module is not loaded (submitted by Diego Iastrubni)
+ * refactored asterisk manager interface class to not require password lookup in common and asi files
+ * fixed module admin bug (submitted by serger)
+
+Change log - 02/14/2006
+
+ * added callmonitor duration filter to filter out short length calls (sponsored by John Cardner, Phonoscope, Inc)
+
+Change log - 02/09/2006
+
+ * added voicemail email and pager settings
+ * more rework of callmonitor recording match to handle large volumes of recordings (sponsored by John Cardner, Phonoscope, Inc)
+
+Change log - 02/07/2006
+
+ * added check for PHP PEAR installation
+ * added check for proper communication with the Asterisk Manager
+ * fixed class coding standard (ie ClassName)
+ * fixed method coding standard (ie methodName)
+ * fixed variable coding standard (ie variable_name)
+ * fixed constant coding standard (ie CONSTANT_NAME)
+ * added config option for voicemail password length (submitted by Chuck Bunn)
+ - set with $SETTINGS_VOICEMAIL_PASSWORD_LENGTH in /includes/main.conf
+ * added voicemail audio format admin option in settings page (submitted by Chuck Bunn)
+ - set with $ARI_VOICEMAIL_AUDIO_FORMAT_DEFAULT in /includes/main.conf
+ * fixed bug to separate voicemail password set in settings page (submitted by Chuck Bunn)
+
+Change log - 02/05/2006
+
+ * added call forward setting
+ * added Hebrew Translation (submitted by Diego Iastrubni)
+ * fixed i18n translation best practices and bugs (submitted by Diego Iastrubni)
+ * fixed voicemail message move bug (submitted by Steve Davies)
+ * fixed voicemail folder creation permissions issue (submitted by Steve Davies)
+
+Change log - 01/31/2006
+
+ * added help page
+ * added file lookup limiting code to prevent hanging when extremely large numbers of files are found in a directory
+ * added database type global variable
+
+Change log - 01/26/2006
+
+ * added php 4 or later version checking
+ * fixed php pre 4.3 version compatability
+ * fixed buy in call manager file matching recursively searching directories (submitted by Adrian Carter)
+
+Change log - 01/20/2006
+
+ * added call monitor aggressive matching option
+
+Change log - 01/18/2006
+
+ * added Hungarian Translation (submitted by Diego Imre Csaba Varasdy)
+ * fixed bug for Asterisk Manager change in Asterisk 1.2
+
+Change log - 01/12/2006
+
+ * added column sort to voicemail page (requested by Diego Elias Sofronas)
+ * added column sort to call monitor page (requested by Elias Sofronas)
+ * added i18n lang select to login page (requested by Diego Iastrubni)
+
+Change log - 12/09/2005
+
+ * another fix to the on-demand call monitor recordings (submitted by Blake Krone)
+
+Change log - 12/09/2005
+
+ * fix to recognize on-demand call monitor recordings (identified as auto-...) (submitted by Francesco Romano, Antonio Cano Damas, and Jason P. Meyer)
+ * added German Translation (submitted by Till Stoermer)
+
+Change log - 12/07/2005
+
+ * fixed search bug (submitted by Francesco Romano)
+ * fixed formating bugs
+
+Change log - 12/01/2005
+
+ * fix delete, move_to, and forward_to voicemail buttons for i18n translations
+ * fix delete call monitor button for i18n translations
+ * fix call monitor file matching problem if call time is a second or two later than time recorded in database log (submitted by Will Prater, Steve D, and others)
+ * changed to get call recording settings from asterisk and not the mysql database to support ARI standalone
+ * fix i18n for recording popup (submitted by Antonio Cano Damas)
+ * added search for voicemail
+ * added class to handle Asterisk Manager Interface (phpagi-asmanager.php would need error handling added)
+ * moved i18n language functions to own file so can support i18n in recording popup
+ * added Italian (submitted by Francesco Romano)
+ * updated Spanish translation (submitted by Antonio Cano Damas)
+ * fixed bugs in standalone code (sponsored by Hugh Buitano and also submitted by John Biundo)
+ * fixed logo (submitted by John Biundo)
+ * cleaned up css for misc/audio.php
+
+Change log - 11/17/2005
+
+ * added protocol multi-config_file (iax,sip,zap) support (sponsored by Hugh Buitano, Infosecure Systems)
+ * add global variables for asterisk and asteriskcdr database hosts and names (sponsored by Hugh Buitano, Infosecure Systems)
+ * added French translation (submitted by Joachim Buron-Pilatre, Phileas Com)
+ * fixed bug (submitted by Joachim Buron-Pilatre, Phileas Com)
+
+Change log - 11/13/2005
+
+ * refactored login context support
+ * added voicemail context support (submitted by Todd Courtnage)
+ * fixed voicemail sub nav folders to allow i18n translation (submitted by Elias Sofronas)
+ * fixed voicemail finding messages in different contexts (sponsored by Brian Connelly, Connelly Management)
+
+Change log - 11/09/2005
+
+ * fixed utf-8 translation in Greek (submitted by Elias Sofronas)
+ * added admin only access to specific modules (submitted by Julian J. M.)
+ * rework handler module code so that each module is only build one time
+ * added download message link on recording playback popup (sponsored by John Cardner, Phonoscope, Inc)
+ * converted i18n translation to utf-8 (submitted by Niklas Larsson and Elias Sofronas)
+ * fix more bugs in i18n translation (submitted by Niklas Larsson)
+ * fixed security bug that allowed access to all files (Edwin Eefting, syn-3.nl)
+
+Change log - 11/04/2005
+
+ * fixed bug to reload asterisk voicemail after voicemail password setting change (submitted by Jason Becker)
+
+Change log - 11/03/2005
+
+ * Highlight which voicemail sub-folder in use (submitted by Elias Sofronas)
+ * set default i18n page (suggested by Niklas Larsson)
+ * admin only account for call monitor (submitted by Julian J. M.)
+ * enhanced pattern matching call monitor unique id from database (submitted by Julian J. M.)
+ * updated Spanish translation (submitted by Diego Iastrubni)
+ * added Swedish translation (submitted by Niklas Larsson)
+ * added Greek translation (submitted by Elias Sofronas)
+ * fixed bug in call recording settings method (changed in AMP 1.10.009)
+ * fix bugs in i18n translation (submitted by Niklas Larsson)
+ - buttons, left menus, select all | none, Call Monitor (heading), Login page.
+
+Change log - 10/21/2005
+
+ * fixed bug in voicemail navigation (submitted by Elias Sofronas)
+ * added version cleanup
+ * added Spanish translation (submitted by Susana Castillo)
+ * added Portuguese translation (submitted by Alejandro Duplat)
+ * added admin setting for call recording
+
+Change log - 09/30/2005
+
+ * added i18n language support
+ * fixed bug if no folder or extension was selected and "move_to" or
+ "forward_to" clicked (bug submitted by Elias Sofronas)
+ * converted modules to a OO plugin architecture
+ * added version to footer
+ * add theme customization
+ * added recording type support (.WAV, .GSM) on settings page
+ * fixed bug to find call recording files better (patch submitted by Mark Voevodin)
+ * fixed bug for navigation and search controls to link to correct folder (bug submitted by Elias Sofronas)
+ * added voicemail password change to settings page
+ * added call monitor delete recording functionality (does not delete database entry)
+ * added call recording settings on settings page
+
+Change log - 09/15/2005
+
+ * added settings page
+ * added call monitor record options on settings page
+ * fixed bug to view src and dst calls in call monitor when restricted (submitted by Elias Sofronas and Thomas Stalder)
+
+Change log - 08/25/2005
+
+ * added SIP authentication login (this does not allow voicemail access)
+ * added persistent passwords (cookies)
+ * added encryption for cookies
+
+Change log - 08/23/2005
+
+ * Fixed $_SESSION['user'] bug conflict with AMP
+ -> changed to $_SESSION['ari_user']
+ * Fixed recording file lookup bug.
+
+Change log - 08/16/2005
+
+ * Fixed formating bug in css
+ * Added multipath to call monitor recordings
+ - set with $asterisk_callmonitor_path in /includes/main.conf
+ * added authentication
+ - use voicemail password
+ - access mailbox voicemail
+ - access call monitor for mailbox
+ - use AMP password
+ - access call monitor for all users
+ - config to allow voicemail to have call monitor access to all users
+ * voicemail access
+ - search of mailbox
+ - easy to delete voicemail interface
+ - move voicemail interface
+ - forward voicemail interface
+
+
+
+ \ No newline at end of file
diff --git a/fs_selfservice/fri/LICENSE.txt b/fs_selfservice/fri/LICENSE.txt
new file mode 100644
index 0000000..c09b19c
--- /dev/null
+++ b/fs_selfservice/fri/LICENSE.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/fs_selfservice/fri/README.txt b/fs_selfservice/fri/README.txt
new file mode 100644
index 0000000..2e3b908
--- /dev/null
+++ b/fs_selfservice/fri/README.txt
@@ -0,0 +1,123 @@
+Developed by Dan Littlejohn of Littlejohn Consulting.
+ www.littlejohnconsulting.com
+
+Released under the GPL.
+
+Send bug reports, requests to dan@littlejohnconsulting.com
+
++++
+
+Misc notes
+
+ARI Project Page
+ www.littlejohnconsulting.com?q=ari
+
+Coding standard
+ * class - CamelCase (ie ClassName)
+ * method camelCase (ie methodName)
+ * variable underscore (ie variable_name)
+ * constant UNDERSCORE (ie CONSTANT_NAME)
+
+Requirements
+ PHP4 (but PHP5 is not yet supported)
+ PHP PEAR
+ asterisk 1.2 or later
+ apache or apache2
+ asterisk manager - at a mininum need command access
+
+security
+ for security all the files in ./recordings/include should be locked down in the web browser
+ so they cannot be viewed.
+
+voicemail email links - For those who would like to include a link to ARI in the voicemail email and set the correct login (mailbox) you can do so as:
+
+ http://< ip address >/recordings/index.php?login=< login >
+
+ replace
+ < ip address > with the server dns or ip
+ < login > with the login or mailbox
+
++++
+
+Module API
+
+odules can be added or removed from ARI.
+
+API
+
+must include these methods.
+
+rank - weights were the module menu item will appear in the navigation window
+init - initialize the module. Database access should first appear here and not in the constructor
+navMenu - side navigation menu item
+display - main module page content
+
+example
+
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the help page
+ */
+
+/**
+ * Class for new_module
+ */
+class NewModule {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 50;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ // put if statement in return string, because do not know $logout until page is built
+ $ret .= "
+ <?php if ($logout !='') { ?>
+ <p><small><small><a href='" . $_SERVER['PHP_SELF'] . "?m=NewModule&f=display'>" . _("new_module") . "</a></small></small></p>
+ <?php } ?>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText("new_module");
+ $ret .= $display->displayLine();
+
+ return $ret;
+ }
+
+}
+
+
+?>
+
+
diff --git a/fs_selfservice/fri/includes/ajax.php b/fs_selfservice/fri/includes/ajax.php
new file mode 100644
index 0000000..fc7961b
--- /dev/null
+++ b/fs_selfservice/fri/includes/ajax.php
@@ -0,0 +1,132 @@
+<?php
+
+/*
+ * AJAX page update script
+ */
+function ajaxRefreshScript($args) {
+
+ global $AJAX_PAGE_REFRESH_TIME;
+
+ $url_args = "?ajax_refresh=1&";
+ foreach($args as $key => $value) {
+ $url_args .= $key . "=" . $value . "&";
+ }
+ $url_args = substr($url_args, 0,strlen($url_args)-1);
+
+ $ret = "
+ <script type='text/javascript' language='javascript'>
+
+ var http_request = false;
+
+ function makeRequest(url, parameters) {
+
+ http_request = false;
+
+ if (window.XMLHttpRequest) { // Mozilla, Safari,...
+ http_request = new XMLHttpRequest();
+ if (http_request.overrideMimeType) {
+ http_request.overrideMimeType('text/xml');
+ }
+ }
+ else if (window.ActiveXObject) { // IE
+ try {
+ http_request = new ActiveXObject('Msxml2.XMLHTTP');
+ }
+ catch (e) {
+ try {
+ http_request = new ActiveXObject('Microsoft.XMLHTTP');
+ }
+ catch (e) {}
+ }
+ }
+ if (!http_request) {
+ return false;
+ }
+ http_request.onreadystatechange = alertContents;
+ http_request.open('GET', url + parameters, true);
+ http_request.send(null);
+ }
+
+ function alertContents() {
+
+ if (!http_request) {
+ return;
+ }
+
+ if (http_request.readyState == 4) {
+ if (http_request.status == 200) {
+
+ var result = http_request.responseXML;
+ if (!result.documentElement && http_request.responseStream) {
+ result.load(http_request.responseStream);
+ }
+
+ var response = http_request.responseXML.documentElement;
+
+ var nav_menu = '';
+ if (response.getElementsByTagName('nav_menu')[0]) {
+ nav_menu = response.getElementsByTagName('nav_menu')[0].firstChild.data;
+ }
+ var nav_submenu = '';
+ if (response.getElementsByTagName('nav_submenu')[0]) {
+ nav_submenu = response.getElementsByTagName('nav_submenu')[0].firstChild.data;
+ }
+ var content = '';
+ if (response.getElementsByTagName('content')[0]) {
+ content = response.getElementsByTagName('content')[0].firstChild.data;
+ }
+
+ if (nav_menu) {
+ document.getElementById('nav_menu').innerHTML = '';
+ document.getElementById('nav_menu').innerHTML = nav_menu;
+ }
+ if (nav_submenu) {
+ document.getElementById('nav_submenu').innerHTML = '';
+ document.getElementById('nav_submenu').innerHTML = nav_submenu;
+ }
+ if (content) {
+ document.getElementById('content').innerHTML = '';
+ document.getElementById('content').innerHTML = content;
+ }
+ }
+ }
+ }
+
+ function updatePage() {
+ makeRequest('" . $_SESSION['ARI_ROOT'] . "', '" . $url_args . "');
+ }
+
+ // refresh time in 'minutes:seconds' (0 to inifinity) : (0 to 59)
+ var refresh_time='" . $AJAX_PAGE_REFRESH_TIME . "';
+
+ if (document.images){
+ var limit=refresh_time.split(\":\");
+ limit=limit[0]*60+limit[1]*1;
+ var current = limit;
+ }
+
+ function beginRefresh(){
+
+ if (!document.images) {
+ return;
+ }
+ if (current==1) {
+ updatePage();
+ current = limit;
+ }
+ else {
+ current-=1;
+ }
+
+ setTimeout(\"beginRefresh()\",1000);
+ }
+
+ window.onload=beginRefresh;
+
+ </script>";
+
+ return $ret;
+}
+
+
+?> \ No newline at end of file
diff --git a/fs_selfservice/fri/includes/asi.php b/fs_selfservice/fri/includes/asi.php
new file mode 100644
index 0000000..62f221e
--- /dev/null
+++ b/fs_selfservice/fri/includes/asi.php
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * @file
+ * Asterisk manager interface for access to asterisk api (astdb)
+ */
+
+/**
+ * Asterisk Manager Interface
+ */
+class AsteriskManagerInterface {
+
+ var $socket;
+
+ /**
+ * constructor
+ */
+ function AsteriskManagerInterface() {
+ }
+
+ /*
+ * Reloads Asterisk Configuration
+ *
+ * @param $username
+ * asterisk manager interface username
+ * @param $password
+ * asterisk manager interface password
+ */
+ function connect($host,$username,$password) {
+
+ // connect
+ $fp = fsockopen($host, 5038, $errno, $errstr, 10);
+ if (!$fp) {
+ return FALSE;
+ }
+ else {
+ $buffer='';
+ if(version_compare(phpversion(), '4.3', '>=')) {
+ stream_set_timeout($fp, 5);
+ }
+ else {
+ socket_set_timeout($fp, 5);
+ }
+ $buffer = fgets($fp);
+ if (!preg_match('/Asterisk Call Manager/i', $buffer)) {
+ $_SESSION['ari_error'] = _("Asterisk Call Manager not responding") . "<br />\n";
+ return FALSE;
+ }
+ else {
+ $out="Action: Login\r\nUsername: ".$username."\r\nSecret: ".$password."\r\n\r\n";
+ fwrite($fp,$out);
+ $buffer=fgets($fp);
+ if ($buffer!="Response: Success\r\n") {
+ $_SESSION['ari_error'] = _("Asterisk authentication failed:") . "<br />" . $buffer . "<br />\n";
+ return FALSE;
+ }
+ else {
+ $buffers=fgets($fp); // get rid of Message: Authentication accepted
+
+ // connected
+ $this->socket = $fp;
+ }
+ }
+ }
+ return TRUE;
+ }
+
+ /*
+ * Reloads Asterisk Configuration
+ */
+ function disconnect() {
+
+ if ($this->socket) {
+ fclose($this->socket);
+ }
+ }
+
+ /*
+ * Reloads Asterisk Configuration
+ *
+ * @param $command
+ * Command to be sent to the asterisk manager interface
+ * @return $ret
+ * response from asterisk manager interface
+ */
+ function command($command) {
+
+ $response = '';
+
+ fwrite($this->socket,$command);
+
+ $count = 0;
+ while (($buffer = fgets($this->socket)) && (!preg_match('/Response: Follows/i', $buffer))) {
+
+ if ($count>100) {
+ $_SESSION['ari_error'] = _("Asterisk command not understood") . "<br />" . $buffer . "<br />\n";
+ return FALSE;
+ }
+ $count++;
+ }
+
+ $count = 0;
+ while (($buffer = fgets($this->socket)) && (!preg_match('/END COMMAND/i', $buffer))) {
+
+ if (preg_match('/Value/',$buffer)) {
+ $parts = split(' ',trim($buffer));
+ $response = $parts[1];
+ }
+
+ if ($count>100) {
+ $_SESSION['ari_error'] = _("Asterisk command not understood") . "<br />" . $buffer . "<br />\n";
+ return;
+ }
+ $count++;
+ }
+
+ return $response;
+ }
+
+ function command2($command) {
+
+ $response = '';
+
+ fwrite($this->socket,$command);
+
+ $count = 0;
+ while (($buffer = fgets($this->socket)) && (!preg_match('/Response: Follows/i', $buffer))) {
+
+ if ($count>100) {
+ $_SESSION['ari_error'] = _("Asterisk command not understood") . "<br />" . $buffer . "<br />\n";
+ return FALSE;
+ }
+ $count++;
+ }
+
+ $count = 0;
+ while (($buffer = fgets($this->socket)) && (!preg_match('/END COMMAND/i', $buffer))) {
+
+ if (preg_match('/Value:/',$buffer)) {
+ $parts = split('Value:',trim($buffer));
+ $response = $parts[1];
+ }
+ if ($count>100) {
+ $_SESSION['ari_error'] = _("Asterisk command not understood") . "<br />" . $buffer . "<br />\n";
+ return;
+ }
+ $count++;
+ }
+
+ return $response;
+ }
+
+}
+
+
+?> \ No newline at end of file
diff --git a/fs_selfservice/fri/includes/bootstrap.php b/fs_selfservice/fri/includes/bootstrap.php
new file mode 100644
index 0000000..a01a2f5
--- /dev/null
+++ b/fs_selfservice/fri/includes/bootstrap.php
@@ -0,0 +1,315 @@
+<?php
+
+/**
+ * @file
+ * Functions that need to be loaded on every request.
+ */
+
+/**
+ * Sets doc root
+ */
+function setARIRoot() {
+
+ $found = 0;
+ if (isset($_SERVER['PHP_SELF'])) {
+ if ($_SERVER['PHP_SELF']!='') {
+ $_SESSION['ARI_ROOT'] = $_SERVER['PHP_SELF'];
+ }
+ }
+
+ if (!$found) {
+ $_SESSION['ARI_ROOT'] = "index.php";
+ }
+}
+
+/**
+ * Return a arguments.
+ *
+ * @param $args
+ * The name of the array being acted upon.
+ * @param $name
+ * The name of the variable to return.
+ * @return
+ * The value of the variable.
+ */
+function getArgument($args, $name) {
+
+ return isset($args[$name]) ? $args[$name] : '';
+}
+
+/*
+ * Gets top level directory names
+ *
+ * @param $path
+ * directory to search
+ * @param $filter
+ * string to use as a filter to match files to return
+ * @return $directories
+ * directories found
+ */
+function getDirectories($path,$filter) {
+
+ $directories = array();
+
+ if (is_dir($path)) {
+
+ $dh = opendir($path);
+ while (false!== ($item = readdir($dh))) {
+ if($item!="." && $item!="..") {
+
+ $path = fixPathSlash($path);
+ $directory = $path;
+ $directory = appendPath($directory,$item);
+
+ if (is_dir($directory)) {
+
+ $found = 0;
+ if ($filter) {
+ if (strpos($directory,$filter)) {
+ $found = 1;
+ }
+ } else {
+ $found = 1;
+ }
+ if ($found) {
+ $directories[count($directories) + 1] = $directory;
+ }
+ }
+ }
+ }
+ }
+
+ return $directories;
+}
+
+/*
+ * Gets file names recursively 6 folders deep
+ *
+ * @param $path
+ * directory to search
+ * @param $filter
+ * string to use as a filter to match files to return
+ * @param $recursive_max
+ * max number of sub folders to search
+ * @param $recursive_count
+ * current sub folder count
+ * @return $files
+ * files found
+ */
+function getFiles($path,$filter,$recursive_max,$recursive_count) {
+
+ $files = array();
+
+ if (@is_dir($path) && @is_readable($path)) {
+ $dh = opendir($path);
+ while (false!== ($item = readdir($dh))) {
+ if($item[0]!=".") {
+
+ $path = fixPathSlash($path);
+ $msg_path = appendPath($path,$item);
+
+ $fileCount++;
+ if ($fileCount>3000) {
+ $_SESSION['ari_error']
+ .= _("To many files in $msg_path Not all files processed") . "<br>";
+ return;
+ }
+
+ if ($recursive_count<$recursive_max && is_dir($msg_path)) {
+
+ $dirCount++;
+ if ($dirCount>10) {
+ $_SESSION['ari_error']
+ .= sprintf(_("To many directories in %s Not all files processed"),$msg_path) . "<br>";
+ return;
+ }
+
+ $count = $recursive_count + 1;
+ $path_files = getFiles($msg_path,$filter,$recursive_max,$count);
+ $files = array_merge($files,$path_files);
+ }
+ else {
+ $found = 0;
+ if ($filter) {
+ if (strpos($msg_path,$filter)) {
+ $found = 1;
+ }
+ } else {
+ $found = 1;
+ }
+ if ($found) {
+ $files[count($files) + 1] = $msg_path;
+ }
+ }
+ }
+ }
+ }
+
+ return $files;
+}
+
+/* Utilities */
+
+/**
+ * Fixes the path for a trailing slash
+ *
+ * @param $path
+ * path to append
+ * @return $ret
+ * path to returned
+ */
+function fixPathSlash($path) {
+
+ $ret = $path;
+
+ $slash = '';
+ if (!preg_match('/\/$/',$path)) {
+ $slash = '/';
+ }
+ $ret .= $slash;
+
+ return $ret;
+}
+
+/**
+ * Appends folder to end of path
+ *
+ * @param $path
+ * path to append
+ * @param $folder
+ * folder to append to path
+ * @return $ret
+ * path to returned
+ */
+function appendPath($path,$folder) {
+
+ $ret = $path;
+
+ $m = '';
+ if (!preg_match('/\/$/',$path)) {
+ $m = '/';
+ }
+ $ret .= $m . $folder;
+
+ return $ret;
+}
+
+/**
+ * Get Date format
+ *
+ * @param $timestamp
+ * timestamp to be converted
+ */
+function getDateFormat($timestamp) {
+ return date('Y-m-d', $timestamp);
+}
+
+/**
+ * Get time format
+ *
+ * @param $timestamp
+ * timestamp to be converted
+ */
+function getTimeFormat($timestamp) {
+ return date('G:i:s', $timestamp);
+}
+
+/* */
+
+/**
+ * Checks ARI dependencies
+ */
+function checkDependencies() {
+
+ // check for PHP
+ if (!version_compare(phpversion(), '4.3', '>=')) {
+ echo _("ARI requires a version of PHP 4.3 or later");
+ exit();
+ }
+
+ // check for PEAR
+ $include_path = ini_get('include_path');
+ $buf = split(':|,',$include_path);
+
+ $found = 0;
+ foreach ($buf as $path) {
+ $path = fixPathSlash($path);
+ $pear_check_path = $path . "DB.php";
+ if (is_file($pear_check_path)) {
+ $found = 1;
+ break;
+ }
+ }
+
+ if (!$found) {
+ echo _("PHP PEAR must be installed. Visit http://pear.php.net for help with installation.");
+ exit();
+ }
+}
+
+/**
+ * Starts the session
+ */
+function startARISession() {
+
+ if (!isset($_SESSION['ari_user']) ) {
+
+ // start a new session for the user
+ ini_set('session.name', 'ARI'); // prevent session name clashes
+ ini_set('session.gc_maxlifetime', '3900'); // make the session timeout a long time
+ set_time_limit(360);
+ session_start();
+ }
+}
+
+/**
+ * Bootstrap
+ *
+ * Loads critical variables needed for every page request
+ *
+ */
+function bootstrap() {
+
+ // set error reporting
+ error_reporting (E_ALL & ~ E_NOTICE);
+}
+
+/**
+ * Set HTTP headers in preparation for a page response.
+ *
+ * TODO: Figure out caching
+ */
+function ariPageHeader() {
+
+ bootstrap();
+}
+
+/**
+ * Perform end-of-request tasks.
+ *
+ * This function sets the page cache if appropriate, and allows modules to
+ * react to the closing of the page by calling hook_exit().
+ */
+function ariPageFooter() {
+
+}
+
+/**
+ * Includes and run functions
+ */
+
+include_once("./includes/lang.php");
+$language = new Language();
+$language->set();
+
+checkDependencies();
+startARISession();
+setARIRoot();
+
+include_once("./includes/main.conf.php");
+include_once("./version.php");
+include_once("./includes/crypt.php");
+include_once("./includes/login.php");
+
+
+?>
diff --git a/fs_selfservice/fri/includes/common.php b/fs_selfservice/fri/includes/common.php
new file mode 100644
index 0000000..87f2026
--- /dev/null
+++ b/fs_selfservice/fri/includes/common.php
@@ -0,0 +1,434 @@
+<?php
+
+/**
+ * @file
+ * common functions - core handler
+ */
+
+/*
+ * Checks if user is set and sets
+ */
+function checkErrorMessage() {
+
+ if ($_SESSION['ari_error']) {
+ $ret .= "<div class='error'>
+ " . $_SESSION['ari_error'] . "
+ </div>
+ <br>";
+ unset($_SESSION['ari_error']);
+ }
+
+ return $ret;
+}
+
+/*
+ * Checks modules directory, and configuration, and loaded modules
+ */
+function loadModules() {
+
+ global $ARI_ADMIN_MODULES;
+ global $ARI_DISABLED_MODULES;
+
+ global $loaded_modules;
+
+ $modules_path = "./modules";
+ if (is_dir($modules_path)) {
+
+ $filter = ".module";
+ $recursive_max = 1;
+ $recursive_count = 0;
+ $files = getFiles($modules_path,$filter,$recursive_max,$recursive_count);
+
+ foreach($files as $key => $path) {
+
+ // build module object
+ include_once($path);
+ $path_parts = pathinfo($path);
+ list($name,$ext) = split("\.",$path_parts['basename']);
+
+ // check for module and get rank
+ if (class_exists($name)) {
+
+ $module = new $name();
+
+ // check if admin module
+ $found = 0;
+ if ($ARI_ADMIN_MODULES) {
+ $admin_modules = split(',',$ARI_ADMIN_MODULES);
+ foreach ($admin_modules as $key => $value) {
+ if ($name==$value) {
+ $found = 1;
+ break;
+ }
+ }
+ }
+
+ // check if disabled module
+ $disabled = 0;
+ if ($ARI_DISABLED_MODULES) {
+ $disabled_modules = split(',',$ARI_DISABLED_MODULES);
+ foreach ($disabled_modules as $key => $value) {
+ if ($name==$value) {
+ $disabled = 1;
+ break;
+ }
+ }
+ }
+
+ // if not admin module or admin user add to module name to array
+ if (!$disabled && (!$found || $_SESSION['ari_user']['admin'])) {
+ $loaded_modules[$name] = $module;
+ }
+ }
+ }
+ }
+ else {
+ $_SESSION['ari_error'] = _("$path not a directory or not readable");
+ }
+}
+
+/**
+ * Builds database connections
+ */
+function databaseLogon() {
+
+ global $STANDALONE;
+
+ global $ASTERISKMGR_DBHOST;
+
+ global $AMP_FUNCTIONS_FILES;
+ global $AMPORTAL_CONF_FILE;
+
+ global $LEGACY_AMP_DBENGINE;
+ global $LEGACY_AMP_DBFILE;
+ global $LEGACY_AMP_DBHOST;
+ global $LEGACY_AMP_DBNAME;
+
+ global $ASTERISKCDR_DBENGINE;
+ global $ASTERISKCDR_DBFILE;
+ global $ASTERISKCDR_DBHOST;
+ global $ASTERISKCDR_DBNAME;
+
+ global $ARI_DISABLED_MODULES;
+
+ global $loaded_modules;
+
+ // This variable is a global in the FreePBX function.inc.php but needs to be
+ // declared here or the is not seen when parse_amprotaconf() is eventually called
+ // ?php bug?
+ //
+ global $amp_conf_defaults;
+
+ // get user
+ if ($STANDALONE['use']) {
+
+ $mgrhost = $ASTERISKMGR_DBHOST;
+ $mgruser = $STANDALONE['asterisk_mgruser'];
+ $mgrpass = $STANDALONE['asterisk_mgrpass'];
+
+ $asteriskcdr_dbengine = $ASTERISKCDR_DBENGINE;
+ $asteriskcdr_dbfile = $ASTERISKCDR_DBFILE;
+ $asteriskcdr_dbuser = $STANDALONE['asteriskcdr_dbuser'];
+ $asteriskcdr_dbpass = $STANDALONE['asteriskcdr_dbpass'];
+ $asteriskcdr_dbhost = $ASTERISKCDR_DBHOST;
+ $asteriskcdr_dbname = $ASTERISKCDR_DBNAME;
+ }
+ else {
+
+ $include = 0;
+ $files = split(';',$AMP_FUNCTIONS_FILES);
+ foreach ($files as $file) {
+ if (is_file($file)) {
+ include_once($file);
+ $include = 1;
+ }
+ }
+
+ if ($include) {
+ $amp_conf = parse_amportal_conf($AMPORTAL_CONF_FILE);
+
+ $mgrhost = $ASTERISKMGR_DBHOST;
+ $mgruser = $amp_conf['AMPMGRUSER'];
+ $mgrpass = $amp_conf['AMPMGRPASS'];
+
+ $amp_dbengine = isset($amp_conf["AMPDBENGINE"]) ? $amp_conf["AMPDBENGINE"] : $LEGACY_AMP_DBENGINE;
+ $amp_dbfile = isset($amp_conf["AMPDBFILE"]) ? $amp_conf["AMPDBFILE"] : $LEGACY_AMP_DBFILE;
+ $amp_dbuser = $amp_conf["AMPDBUSER"];
+ $amp_dbpass = $amp_conf["AMPDBPASS"];
+ $amp_dbhost = isset($amp_conf["AMPDBHOST"]) ? $amp_conf["AMPDBHOST"] : $LEGACY_AMP_DBHOST;
+ $amp_dbname = isset($amp_conf["AMPDBNAME"]) ? $amp_conf["AMPDBNAME"] : $LEGACY_AMP_DBNAME;
+
+ $asteriskcdr_dbengine = $ASTERISKCDR_DBENGINE;
+ $asteriskcdr_dbfile = $ASTERISKCDR_DBFILE;
+ $asteriskcdr_dbuser = $amp_conf["AMPDBUSER"];
+ $asteriskcdr_dbpass = $amp_conf["AMPDBPASS"];
+ $asteriskcdr_dbhost = $ASTERISKCDR_DBHOST;
+ $asteriskcdr_dbhost = isset($amp_conf["AMPDBHOST"]) ? $amp_conf["AMPDBHOST"] : $ASTERISKCDR_DBHOST;
+ $asteriskcdr_dbname = $ASTERISKCDR_DBNAME;
+
+ unset($amp_conf);
+ }
+ }
+
+ // asterisk manager interface (berkeley database I think)
+ global $asterisk_manager_interface;
+ $asterisk_manager_interface = new AsteriskManagerInterface();
+
+ $success = $asterisk_manager_interface->Connect($mgrhost,$mgruser,$mgrpass);
+ if (!$success) {
+ $_SESSION['ari_error'] =
+ _("ARI does not appear to have access to the Asterisk Manager.") . " ($errno)<br>" .
+ _("Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager Account.") . "<br>" .
+ _("Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account") . "<br>" .
+ _("make sure [general] enabled = yes and a 'permit=' line for localhost or the webserver.");
+ return FALSE;
+ }
+
+ // pear interface databases
+ $db = new Database();
+
+ // AMP asterisk database
+ if (!$STANDALONE['use']) {
+ $_SESSION['dbh_asterisk'] = $db->logon($amp_dbengine,
+ $amp_dbfile,
+ $amp_dbuser,
+ $amp_dbpass,
+ $amp_dbhost,
+ $amp_dbname);
+ if (!isset($_SESSION['dbh_asterisk'])) {
+ $_SESSION['ari_error'] .= _("Cannot connect to the $amp_dbname database") . "<br>" .
+ _("Check AMP installation, asterisk, and ARI main.conf");
+ return FALSE;
+ }
+ }
+
+ // cdr database
+ if (in_array('callmonitor',array_keys($loaded_modules))) {
+ $_SESSION['dbh_cdr'] = $db->logon($asteriskcdr_dbengine,
+ $asteriskcdr_dbfile,
+ $asteriskcdr_dbuser,
+ $asteriskcdr_dbpass,
+ $asteriskcdr_dbhost,
+ $asteriskcdr_dbname);
+ if (!isset($_SESSION['dbh_cdr'])) {
+ $_SESSION['ari_error'] .= sprintf(_("Cannot connect to the $asteriskcdr_dbname database"),$asteriskcdr_dbname) . "<br>" .
+ _("Check AMP installation, asterisk, and ARI main.conf");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Logout if needed for any databases
+ */
+function databaseLogoff() {
+
+ global $asterisk_manager_interface;
+
+ $asterisk_manager_interface->Disconnect();
+}
+
+/*
+ * Checks if user is set and sets
+ */
+function loginBlock() {
+
+ $login = new Login();
+
+ if (isset($_REQUEST['logout'])) {
+ $login->Unauth();
+ }
+
+ if (!isset($_SESSION['ari_user'])) {
+ $login->Auth();
+
+ }
+
+ if (!isset($_SESSION['ari_user'])) {
+
+ // login form
+ $ret .= $login->GetForm();
+
+ return $ret;
+ }
+}
+
+/*
+ * Main handler for website
+ */
+function handleBlock() {
+
+ global $ARI_NO_LOGIN;
+
+ global $loaded_modules;
+
+ // check errors here and in login block
+ $content .= checkErrorMessage();
+
+ // check logout
+ if ($_SESSION['ari_user'] && !$ARI_NO_LOGIN) {
+ $logout = 1;
+ }
+
+ // if nothing set goto user default page
+ if (!isset($_REQUEST['m'])) {
+ $_REQUEST['m'] = $_SESSION['ari_user']['default_page'];
+ }
+ // if not function specified then use display page function
+ if (!isset($_REQUEST['f'])) {
+ $_REQUEST['f'] = 'display';
+ }
+
+ $m = $_REQUEST['m']; // module
+ $f = $_REQUEST['f']; // function
+ $a = $_REQUEST['a']; // action
+
+ // set arguments
+ $args = array();
+ foreach($_REQUEST as $key => $value) {
+ $args[$key] = $value;
+ }
+
+ // set rank
+ $ranked_modules = array();
+ foreach ($loaded_modules as $module) {
+
+ $module_methods = get_class_methods($module); // note that PHP4 returns all lowercase
+ while (list($index, $value) = each($module_methods)) {
+ $module_methods[strtolower($index)] = strtolower($value);
+ }
+ reset($module_methods);
+
+ $rank = 99999;
+ $rank_function = "rank";
+ if (in_array(strtolower($rank_function), $module_methods)) {
+ $rank = $module->$rank_function();
+ }
+
+ $ranked_modules[$rank] = $module;
+ }
+ ksort($ranked_modules);
+
+ // process modules
+ foreach ($ranked_modules as $module) {
+
+ // process module
+ $name = get_class($module); // note PHP4 returns all lowercase
+ $module_methods = get_class_methods($module); // note PHP4 returns all lowercase
+ while (list($index, $value) = each($module_methods)) {
+ $module_methods[strtolower($index)] = strtolower($value);
+ }
+ reset($module_methods);
+
+ // init module
+ $module->init();
+
+ // add nav menu items
+ $nav_menu_function = "navMenu";
+ if (in_array(strtolower($nav_menu_function), $module_methods)) {
+ $nav_menu .= $module->$nav_menu_function($args);
+ }
+
+ if (strtolower($m)==strtolower($name)) {
+
+ // build sub menu
+ $subnav_menu_function = "navSubMenu";
+ if (in_array(strtolower($subnav_menu_function), $module_methods)) {
+ $subnav_menu .= $module->$subnav_menu_function($args);
+ }
+
+ // execute function (usually to build content)
+ if (in_array(strtolower($f), $module_methods)) {
+ $content .= $module->$f($args);
+ }
+ }
+ }
+
+ // add logout link
+ if ($logout != '') {
+ $nav_menu .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?logout=1'>" . _("Logout") . "</a></small></small></p>";
+ }
+
+ // error message if no content
+ if (!$content) {
+ $content .= _("Page Not Found.");
+ }
+
+ return array($nav_menu,$subnav_menu,$content);
+}
+
+/*
+ * Main handler for website
+ */
+function handler() {
+
+ global $ARI_VERSION;
+
+ // version
+ $ari_version = $ARI_VERSION;
+
+ // check error
+ $error = $_SESSION['ari_error'];
+
+ // load modules
+ loadModules();
+
+ // login to database
+ $success = databaseLogon();
+ if ($success) {
+
+ // check if login is needed
+ $content = loginBlock();
+ if (!isset($content)) {
+ list($nav_menu,$subnav_menu,$content) = handleBlock();
+ }
+ }
+ else {
+
+ $display = new Display();
+
+ $content .= $display->displayHeaderText("ARI");
+ $content .= $display->displayLine();
+ $content .= checkErrorMessage();
+ }
+
+ // log off any databases needed
+ databaseLogoff();
+
+ // check for ajax request and refresh or if not build the page
+ if (isset($_REQUEST['ajax_refresh']) ) {
+
+ echo "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+ <response>
+ <nav_menu><![CDATA[" . $nav_menu . "]]></nav_menu>
+ <subnav_menu><![CDATA[" . $subnav_menu . "]]></subnav_menu>
+ <content><![CDATA[" . $content . "]]></content>
+ </response>";
+ }
+ else {
+
+ // build the page
+ include_once("./theme/page.tpl.php");
+ }
+}
+
+/**
+ * Includes and run functions
+ */
+
+// create asterisk manager interface singleton
+$asterisk_manager_interface = '';
+
+// array to keep track of loaded modules
+$loaded_modules = array();
+
+include_once("./includes/asi.php");
+include_once("./includes/database.php");
+include_once("./includes/display.php");
+include_once("./includes/ajax.php");
+
+include_once("./includes/freeside.class.php");
+
+?>
diff --git a/fs_selfservice/fri/includes/crypt.php b/fs_selfservice/fri/includes/crypt.php
new file mode 100644
index 0000000..301d8a8
--- /dev/null
+++ b/fs_selfservice/fri/includes/crypt.php
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * Allows encrypt and decrypt
+ */
+class Crypt {
+
+ /**
+ * Gets a random value for encryption
+ * - From php.net docs
+ *
+ * @param $iv_len
+ * length of random variable
+ */
+ function getRndIV($iv_len) {
+
+ $iv = '';
+ while ($iv_len-- > 0) {
+ $iv .= chr(mt_rand() & 0xff);
+ }
+ return $iv;
+ }
+
+ /**
+ * Encrypts string
+ * - From php.net docs
+ *
+ * @param $str
+ * string to encrypt
+ * @param $salt
+ * password to use for encryption
+ * @param $iv_len
+ * length of random number
+ */
+ function encrypt($str, $salt, $iv_len = 16) {
+
+ $str .= "\x13";
+ $n = strlen($str);
+ if ($n % 16) $str .= str_repeat("\0", 16 - ($n % 16));
+ $i = 0;
+ $enc_text = $this->getRndIV($iv_len);
+ $iv = substr($salt ^ $enc_text, 0, 512);
+ while ($i < $n) {
+ $block = substr($str, $i, 16) ^ pack('H*', md5($iv));
+ $enc_text .= $block;
+ $iv = substr($block . $iv, 0, 512) ^ $salt;
+ $i += 16;
+ }
+ return urlencode(base64_encode($enc_text));
+ }
+
+ /**
+ * Decrypts string
+ * - From php.net docs
+ *
+ * @param $enc
+ * encrypted string to decrypt
+ * @param $salt
+ * password to use for encryption
+ * @param $iv_len
+ * length of random number
+ */
+ function decrypt($enc, $salt, $iv_len = 16) {
+
+ $enc = urldecode(base64_decode($enc));
+ $n = strlen($enc);
+ $i = $iv_len;
+ $str = '';
+ $iv = substr($salt ^ substr($enc, 0, $iv_len), 0, 512);
+ while ($i < $n) {
+ $block = substr($enc, $i, 16);
+ $str .= $block ^ pack('H*', md5($iv));
+ $iv = substr($block . $iv, 0, 512) ^ $salt;
+ $i += 16;
+ }
+ return preg_replace('/\\x13\\x00*$/', '', $str);
+ }
+}
+
+
+?>
diff --git a/fs_selfservice/fri/includes/database.php b/fs_selfservice/fri/includes/database.php
new file mode 100644
index 0000000..ff3d199
--- /dev/null
+++ b/fs_selfservice/fri/includes/database.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Functions for the database
+ */
+
+/*
+ * Database Class
+ */
+class Database {
+
+ /*
+ * Constructor
+ */
+ function Database() {
+
+ // PEAR must be installed
+ require_once('DB.php');
+ }
+
+ /*
+ * Logs into database and returns database handle
+ *
+
+ * @param $engine
+ * database engine
+ * @param $dbfile
+ * database file
+ * @param $username
+ * username for database
+ * @param $password
+ * password for database
+ * @param $host
+ * database host
+ * @param $name
+ * database name
+ * @return $dbh
+ * variable to hold the returned database handle
+ */
+ function logon($engine,$dbfile,$username,$password,$host,$name) {
+
+ // connect string
+ if ($dbfile) {
+ // datasource mostly to support sqlite: dbengine://dbfile?mode=xxxx
+ $dsn = $engine . '://' . $dbfile . '?mode=0666';
+ }
+ else {
+ // datasource in in this style: dbengine://username:password@host/database
+ $datasource = $engine . '://' . $username . ':' . $password . '@' . $host . '/' . $name;
+ }
+
+ // options
+ $options = array(
+ 'debug' => 2,
+ 'portability' => DB_PORTABILITY_LOWERCASE|DB_PORTABILITY_RTRIM|DB_PORTABILITY_DELETE_COUNT|DB_PORTABILITY_NUMROWS|DB_PORTABILITY_ERRORS|DB_PORTABILITY_NULL_TO_EMPTY,
+ );
+
+ // attempt connection
+ $dbh = DB::connect($datasource,$options);
+
+ // if connection failed show error
+ if(DB::isError($dbh)) {
+ $_SESSION['ari_error'] .= $dbh->getMessage() . "<br><br>";
+ return;
+ }
+ return $dbh;
+ }
+}
+
+
+?> \ No newline at end of file
diff --git a/fs_selfservice/fri/includes/display.php b/fs_selfservice/fri/includes/display.php
new file mode 100644
index 0000000..41d8dc5
--- /dev/null
+++ b/fs_selfservice/fri/includes/display.php
@@ -0,0 +1,222 @@
+<?php
+
+/**
+ * @file
+ * Functions common to display
+ */
+
+/**
+ * Display
+ */
+class Display {
+
+ /**
+ * display constructor
+ */
+ function Display() {
+ }
+
+ /**
+ * display text header
+ *
+ * @param $text
+ * Header text to display
+ */
+ function displayHeaderText($text) {
+
+ $ret = "<h2>" . $text . "</h2>
+ <br>";
+
+ return $ret;
+ }
+
+ /**
+ * displays header line
+ */
+ function displayLine() {
+
+ $ret = "
+ <div id='line'>
+ <div class='spacer'></div>
+ <div class='spacer'></div>
+ </div>
+ <br>";
+
+ return $ret;
+ }
+}
+
+/**
+ * DisplaySearch
+ */
+class DisplaySearch extends Display {
+
+ /**
+ * Constructor
+ */
+ function DisplaySearch() {
+ }
+
+ /**
+ * displays search controls
+ *
+ * @param $align
+ * where to align the control
+ * @param $q
+ * search query
+ * @param $focus
+ * whether to focus control on this block
+ */
+ function displaySearchBlock($align,$m,$q,$url_opts,$focus) {
+
+ // align
+ if ($align=='center') {
+ $alignText = "class='bar_center'";
+ }
+ else {
+ $alignText = "class='bar_left'";
+ }
+
+ // url options
+ foreach ($url_opts as $key => $value) {
+ $option_text .= "<input type=hidden name=" . $key . " value=" . $value . ">";
+ }
+
+ // build
+ $ret .= "<div " . $alignText . ">
+ <form class='bar' action='" . $_SESSION['ARI_ROOT'] . "' method='GET' name='search'>
+ <input type=hidden name=m value=" . $m . ">
+ <input type=text name=q size=40 value='" . $q . "' maxlength=256>
+ " . $option_text . "
+ <input type=hidden name=start value=0>
+ <input type=submit name=btnS value='" . _("Search") . "'>
+ </form>
+ </div>";
+
+ if ($focus=="true") { // search block loaded twice usually so only allow javascript to be loaded on the top block
+ $ret .= "<script type='text/javascript'>
+ <!--
+ if (document.search) {
+ document.search.q.focus();
+ }
+ // -->
+ </script>";
+ }
+
+ return $ret;
+ }
+
+ /**
+ * displays info bar
+ *
+ * @param $controls
+ * controls for the page on the bar
+ * @param $q
+ * search query
+ * @param $start
+ * start number of current page
+ * @param $span
+ * number of items on current page
+ * @param $total
+ * total number of records found by current search
+ */
+ function displayInfoBarBlock($controls,$q,$start,$span,$total) {
+
+ if ($total<$span) {
+ $span = $total;
+ }
+ $start_count = ($total>0)?$start+1:$start;
+ $span_count = ($start+$span>$total)?$total:$start+$span;
+
+ if ($controls) {
+ $left_text = $controls;
+ }
+ elseif ($q != NULL) {
+ $left_text = "<small><small>" . _("Searched for") . " <u>" . $q . "</u></small></small>";
+ }
+
+ if ($span<$total) {
+ $right_text = "<small><small>" . sprintf(_("Results %d - %d of %d"),$start_count,$span_count,$total) . "</small></small>";
+ } else {
+ $right_text = "<small><small>" . sprintf(_("Results %d"),$total) . "</small></small>";
+ }
+
+ $ret .= "
+ <table id='navbar' width='100%'>
+ <tr>
+ <td>
+ " . $left_text . "
+ </td>
+ <td align='right'>
+ " . $right_text ."
+ </td>
+ </tr>
+ </table>";
+
+ return $ret;
+ }
+
+ /**
+ * displays navigation bar
+ *
+ * @param $q
+ * search query
+ * @param $start
+ * start number of current page
+ * @param $span
+ * number of items on current page
+ * @param $total
+ * total number of records found by current search
+ */
+ function displayNavigationBlock($m,$q,$url_opts,$start,$span,$total) {
+
+ $start = $start=='' ? 0 : $start ;
+ $span = $span=='' ? 15 : $span ;
+
+ $total_pages = ceil($total/$span);
+ $start_page = floor($start/$span);
+
+ // if more than ten pages start at this page minus ten otherwise start at zero
+ $begin = ($start_page>10)?($start_page-10):0;
+ // if more than ten pages then stop at this page plus ten otherwise stop at last page
+ $end = ($start_page>8)?($start_page+10):10;
+
+ // url
+ $unicode_q = urlencode($q); // encode search string
+
+ foreach ($url_opts as $key => $value) {
+ $option_text .= "&" . $key . "=" . $value;
+ }
+
+ $url = $_SESSION['ARI_ROOT'] . "?m=" . $m . "&q=" . $unicode_q . $option_text;
+
+ // build
+ if ($start_page!=0) {
+ $start_page_text = "<a href='" . $url . "&start=0'><small>" . _("First") . "</a>&nbsp;</small>
+ <a href=" . $url . "&start=" . ($start-$span) . "><small><</a>&nbsp;</small>";
+ }
+
+ for($next_page=$begin;($next_page<$total_pages)&&($next_page<$end);$next_page++) {
+ if ($next_page == $start_page) {
+ $middle_page_text .= "<small>" . ($next_page+1) . "&nbsp;</small>";
+ } else {
+ $middle_page_text .= "<a href='" . $url . "&start=" . ($next_page*$span) . "'><small>" . ($next_page+1) . "</a>&nbsp;</small>";
+ }
+ }
+ if ( ($start_page != $total_pages-1) && ($total != 0) ) {
+ $end_page_text = "<a href='" . $url . "&start=" . ($start+$span) . "'><small>></a>&nbsp;</small>
+ <a href='" . $url . "&start=" . ($total_pages-1)*$span . "'><small>" . _("Last") . "</a>&nbsp;</small>";
+ }
+
+ $ret .= "<div class='bar_center'>
+ " . $start_page_text . "
+ " . $middle_page_text . "
+ " . $end_page_text . "
+ </div>";
+
+ return $ret;
+ }
+}
+
+
+?> \ No newline at end of file
diff --git a/fs_selfservice/fri/includes/freeside.class.php b/fs_selfservice/fri/includes/freeside.class.php
new file mode 100644
index 0000000..a441398
--- /dev/null
+++ b/fs_selfservice/fri/includes/freeside.class.php
@@ -0,0 +1,38 @@
+<?php
+class FreesideSelfService {
+
+ //Change this to match the location of your selfservice xmlrpc.cgi or daemon
+ //var $URL = 'https://www.example.com/selfservice/xmlrpc.cgi';
+ var $URL = 'http://localhost/selfservice/xmlrpc.cgi';
+
+ function FreesideSelfService() {
+ $this;
+ }
+
+ public function __call($name, $arguments) {
+
+ error_log("[FreesideSelfService] $name called, sending to ". $this->URL);
+
+ $request = xmlrpc_encode_request("FS.SelfService.XMLRPC.$name", $arguments);
+ $context = stream_context_create( array( 'http' => array(
+ 'method' => "POST",
+ 'header' => "Content-Type: text/xml",
+ 'content' => $request
+ )));
+ $file = file_get_contents($this->URL, false, $context);
+ if (!$file) {
+ trigger_error("[FreesideSelfService] XML-RPC communication error: file_get_contents did not return");
+ } else {
+ $response = xmlrpc_decode($file);
+ if (xmlrpc_is_fault($response)) {
+ trigger_error("[FreesideSelfService] XML-RPC communication error: $response[faultString] ($response[faultCode])");
+ } else {
+ //error_log("[FreesideSelfService] $response");
+ return $response;
+ }
+ }
+ }
+
+}
+
+?>
diff --git a/fs_selfservice/fri/includes/lang.php b/fs_selfservice/fri/includes/lang.php
new file mode 100644
index 0000000..b27b8e3
--- /dev/null
+++ b/fs_selfservice/fri/includes/lang.php
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * @file
+ * i18n language functions
+ */
+
+/**
+ * Class for login
+ */
+class Language {
+
+ var $error;
+
+ /**
+ * Sets i18n locale language
+ *
+ * sets the language for i18n php gettext module
+ * (gettext has to be enabled in the php.ini)
+ *
+ */
+ function set() {
+
+ if (extension_loaded('gettext')) {
+
+ // try and find the default locale
+ $default_lang = preg_replace('/-/','_',$_SERVER['HTTP_ACCEPT_LANGUAGE']);
+
+ $locale = 'en_US';
+ $locale_dir = "./locale";
+ $directories = getdirectories($locale_dir,"");
+ foreach($directories as $directory) {
+ $buf = substr($directory,strlen($locale_dir)+1,strlen($directory) - strlen($locale_dir));
+ if (preg_match("/" . $buf . "/i",$default_lang)) {
+ $locale = $buf;
+ break;
+ }
+ }
+
+ // set locale
+ $language = isset($_COOKIE['ari_lang']) ? $_COOKIE['ari_lang'] : $locale;
+ putenv("LANG=$language");
+ putenv("LANGUAGE=$language");
+ setlocale(LC_MESSAGES,$language);
+ bindtextdomain('ari','./locale');
+ bind_textdomain_codeset('ari', 'UTF-8');
+ textdomain('ari');
+
+ } else {
+ function _($str) {
+ return $str;
+ }
+ }
+ }
+
+ /**
+ * Sets the i18n language in a cookie
+ *
+ * @param $lang_code
+ * length of random number
+ */
+ function setCookie($lang_code) {
+
+ if (extension_loaded('gettext')) {
+ setcookie("ari_lang", $lang_code, time()+365*24*60*60);
+ }
+ }
+
+ /**
+ * Sets the i18n language in a cookie
+ *
+ * @param $lang_code
+ * length of random number
+ */
+ function getForm() {
+
+ // lang setting options
+ if (extension_loaded('gettext')) {
+
+ $langOptions = "
+ <script>
+ function setCookie(name,value) {
+ var t = new Date();
+ var e = new Date();
+ e.setTime(t.getTime() + 365*24*60*60);
+ document.cookie = name+\"=\"+escape(value) + \";expires=\"+e.toGMTString();
+ }
+ </script>
+ <form class='lang' name='lang' action=" . $_SESSION['ARI_ROOT'] . " method='POST'>
+ <select class='lang_code' name='lang_code' onChange=\"setCookie('ari_lang',document.lang.lang_code.value); window.location.reload();\">
+ <option value='en_US' " . ($_COOKIE['ari_lang']=='en_US' ? 'selected' : '') . ">English</option>
+ <option value='es_ES' " . ($_COOKIE['ari_lang']=='es_ES' ? 'selected' : '') . ">Espa&ntilde;ol</option>
+ <option value='fr_FR' " . ($_COOKIE['ari_lang']=='fr_FR' ? 'selected' : '') . ">French</option>
+ <option value='de_DE' " . ($_COOKIE['ari_lang']=='de_DE' ? 'selected' : '') . ">German</option>
+ <option value='el_GR' " . ($_COOKIE['ari_lang']=='el_GR' ? 'selected' : '') . ">Greek</option>
+ <option value='he_IL' " . ($_COOKIE['ari_lang']=='he_IL' ? 'selected' : '') . ">Hebrew</option>
+ <option value='hu_HU' " . ($_COOKIE['ari_lang']=='hu_HU' ? 'selected' : '') . ">Hungarian</option>
+ <option value='it_IT' " . ($_COOKIE['ari_lang']=='it_IT' ? 'selected' : '') . ">Italian</option>
+ <option value='pt_BR' " . ($_COOKIE['ari_lang']=='pt_BR' ? 'selected' : '') . ">Portuguese</option>
+ <option value='sv_SE' " . ($_COOKIE['ari_lang']=='sv_SE' ? 'selected' : '') . ">Swedish</option>
+ </select>
+ </form>";
+ }
+
+ return $langOptions;
+ }
+
+
+}
+
+
+?> \ No newline at end of file
diff --git a/fs_selfservice/fri/includes/login.php b/fs_selfservice/fri/includes/login.php
new file mode 100644
index 0000000..41bb7a6
--- /dev/null
+++ b/fs_selfservice/fri/includes/login.php
@@ -0,0 +1,515 @@
+<?php
+
+/**
+ * @file
+ * login functions
+ */
+
+/**
+ * Class for login
+ */
+class Login {
+
+ var $error;
+
+ /**
+ * Authenticate user and register user information into a session
+ */
+ function Auth() {
+
+ global $ARI_ADMIN_USERNAME;
+ global $ARI_ADMIN_PASSWORD;
+ global $ARI_ADMIN_EXTENSIONS;
+ global $ARI_CRYPT_PASSWORD;
+ global $ASTERISK_VOICEMAIL_CONF;
+ global $ASTERISK_VOICEMAIL_CONTEXT;
+ global $ASTERISK_VOICEMAIL_PATH;
+ global $ASTERISK_PROTOCOLS;
+ global $CALLMONITOR_ADMIN_EXTENSIONS;
+ global $ARI_NO_LOGIN;
+ global $ARI_DEFAULT_ADMIN_PAGE;
+ global $ARI_DEFAULT_USER_PAGE;
+
+ $crypt = new Crypt();
+
+ // init variables
+ $extension = '';
+ $displayname = '';
+ $vm_password = '';
+ $category = '';
+ $context = '';
+ $voicemail_enabled = '';
+ $voicemail_email_address = '';
+ $voicemail_pager_address = '';
+ $voicemail_email_enable = '';
+ $admin = '';
+ $admin_callmonitor = '';
+ $default_page = '';
+
+ $username = '';
+ $password = '';
+
+ // get the ari authentication cookie
+ $data = '';
+ $chksum = '';
+ if (isset($_COOKIE['ari_auth'])) {
+ $buf = unserialize($_COOKIE['ari_auth']);
+ list($data,$chksum) = $buf;
+ }
+ if (md5($data) == $chksum) {
+ $data = unserialize($crypt->decrypt($data,$ARI_CRYPT_PASSWORD));
+ $username = $data['username'];
+ $password = $data['password'];
+ }
+
+ if (isset($_POST['username']) &&
+ isset($_POST['password'])) {
+ $username = $_POST['username'];
+ $password = $_POST['password'];
+ }
+
+ // init email options array
+ $voicemail_email = array();
+
+ // when login, make a new session
+ if ($username && !$ARI_NO_LOGIN) {
+
+ $auth = false;
+
+ // check admin
+ if (!$auth) {
+ if ($username==$ARI_ADMIN_USERNAME &&
+ $password==$ARI_ADMIN_PASSWORD) {
+
+ // authenticated
+ $auth = true;
+
+ $extension = 'admin';
+ $name = 'Administrator';
+ $admin = 1;
+ $admin_callmonitor = 1;
+
+ $default_page = $ARI_DEFAULT_ADMIN_PAGE;
+ }
+ }
+
+ // check voicemail login
+ if (!$auth) {
+
+ if (is_readable($ASTERISK_VOICEMAIL_CONF)) {
+
+ $lines = file($ASTERISK_VOICEMAIL_CONF);
+
+ // look for include files and tack their lines to end of array
+ foreach ($lines as $key => $line) {
+
+ if (preg_match("/include/i",$line)) {
+
+ $include_filename = '';
+ $parts = split(' ',$line);
+ if (isset($parts[1])) {
+ $include_filename = trim($parts[1]);
+ }
+
+ if ($include_filename) {
+ $path_parts = pathinfo($ASTERISK_VOICEMAIL_CONF);
+ $include_path = fixPathSlash($path_parts['dirname']) . $include_filename;
+ foreach (glob($include_path) as $include_file) {
+ $include_lines = file($include_file);
+ $lines = array_merge($include_lines,$lines);
+ }
+ }
+ }
+ }
+
+ // process
+ foreach ($lines as $key => $line) {
+
+ // check for current context and process
+ if (preg_match("/\[.*\]/i",$line)) {
+ $currentContext = trim(preg_replace('/\[|\]/', '', $line));
+ }
+ if ($ASTERISK_VOICEMAIL_CONTEXT &&
+ $currentContext!=$ASTERISK_VOICEMAIL_CONTEXT) {
+ continue;
+ }
+
+ // check for user and process
+ unset($value);
+ $parts = split('=>',$line);
+ if (isset($parts[0])) {
+ $var = $parts[0];
+ }
+ if (isset($parts[1])) {
+ $value = $parts[1];
+ }
+ $var = trim($var);
+ if ($var==$username && $value) {
+ $buf = split(',',$value);
+ if ($buf[0]==$password) {
+
+ // authenticated
+ $auth = true;
+ $extension = $username;
+ $displayname = $buf[1];
+ $vm_password = $buf[0];
+ $default_page = $ARI_DEFAULT_USER_PAGE;
+ $context = $currentContext;
+ $voicemail_enabled = 1;
+ $voicemail_email_address = $buf[2];
+ $voicemail_pager_address = $buf[3];
+
+ if ($voicemail_email_address || $voicemail_pager_address) {
+ $voicemail_email_enable = 1;
+ }
+
+ $options = split('\|',$buf[4]);
+ foreach ($options as $option) {
+ $opt_buf = split('=',$option);
+ $voicemail_email[$opt_buf[0]] = trim($opt_buf[1]);
+ }
+
+ $admin = 0;
+ if ($ARI_ADMIN_EXTENSIONS) {
+ $extensions = split(',',$ARI_ADMIN_EXTENSIONS);
+ foreach ($extensions as $key => $value) {
+ if ($extension==$value) {
+ $admin = 1;
+ break 2;
+ }
+ }
+ }
+
+ $admin_callmonitor = 0;
+ if ($CALLMONITOR_ADMIN_EXTENSIONS) {
+ $extensions = split(',',$CALLMONITOR_ADMIN_EXTENSIONS);
+ foreach ($extensions as $key => $value) {
+ if ($value=='all' || $extension==$value) {
+ $admin_callmonitor = 1;
+ break 2;
+ }
+ }
+ }
+ }
+ else {
+ $_SESSION['ari_error'] = "Incorrect Password";
+ return;
+ }
+ }
+ }
+ }
+ else {
+ $_SESSION['ari_error'] = "File not readable: " . $ASTERISK_VOICEMAIL_CONF;
+ return;
+ }
+ }
+
+ // check sip login
+ if (!$auth) {
+
+ foreach($ASTERISK_PROTOCOLS as $protocol => $value) {
+
+ $config_files = split(';',$value['config_files']);
+ foreach ($config_files as $config_file) {
+
+ if (is_readable($config_file)) {
+
+ $lines = file($config_file);
+ foreach ($lines as $key => $line) {
+
+ unset($value);
+ $parts = split('=',$line);
+ if (isset($parts[0])) {
+ $var = trim($parts[0]);
+ }
+ if (isset($parts[1])) {
+ $value = trim($parts[1]);
+ }
+ if ($var=="username") {
+ $protocol_username = $value;
+ }
+ if ($var=="secret") {
+
+ $protocol_password = $value;
+ if ($protocol_username==$username &&
+ $protocol_password==$password) {
+
+ // authenticated
+ $auth = true;
+ $extension = $username ;
+ $displayname = $username;
+ $default_page = $ARI_DEFAULT_ADMIN_PAGE;
+
+ $admin = 0;
+ if ($ARI_ADMIN_EXTENSIONS) {
+ $extensions = split(',',$ARI_ADMIN_EXTENSIONS);
+ foreach ($extensions as $key => $value) {
+ if ($extension==$value) {
+ $admin = 1;
+ break 2;
+ }
+ }
+ }
+
+ $admin_callmonitor = 0;
+ if ($CALLMONITOR_ADMIN_EXTENSIONS) {
+ $extensions = split(',',$CALLMONITOR_ADMIN_EXTENSIONS);
+ foreach ($extensions as $key => $value) {
+ if ($value=='all' || $extension==$value) {
+ $admin_callmonitor = 1;
+ break 2;
+ }
+ }
+ }
+ }
+ else if ($protocol_username==$username &&
+ $protocol_password!=$password) {
+ $_SESSION['ari_error'] = _("Incorrect Password");
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // let user know bad login
+ if (!$auth) {
+ $_SESSION['ari_error'] = _("Incorrect Username or Password");
+ }
+
+ // freeside login
+ $freeside = new FreesideSelfService();
+ $domain = 'svc_phone';
+ $response = $freeside->login( array(
+ 'username' => strtolower($username),
+ 'domain' => $domain,
+ 'password' => strtolower($password),
+ ) );
+ error_log("[login] received response from freeside: $response");
+ $error = $response['error'];
+
+ if ( ! $error && $response['session_id'] ) {
+
+ // sucessful freeside login
+ error_log("[login] logged into freeside with session_id=$session_id");
+
+ // store session id in your session store, to be used for other calls
+ //$fs_session_id = $response['session_id'];
+ $_SESSION['freeside_session_id'] = $response['session_id'];
+
+ $customer_info = $freeside->customer_info( array(
+ 'session_id' => $_SESSION['freeside_session_id'] ,
+ ) );
+ //XXX error checking here too
+ $displayname = $customer_info['name'];
+
+ } else {
+
+ // unsucessful login
+ error_log("[login] error logging into freeside: $error");
+ $auth = false;
+ $extension = '';
+
+ // display error message to user
+ $_SESSION['ari_error'] = _("Incorrect Username or Password");
+
+ }
+
+ // if authenticated and user wants to be remembered, set cookie
+ $remember = '';
+ if (isset($_POST['remember'])) {
+ $remember = $_POST['remember'];
+ }
+ if ($auth && $remember) {
+
+ $data = array('username' => $username, 'password' => $password);
+ $data = $crypt->encrypt(serialize($data),$ARI_CRYPT_PASSWORD);
+
+ $chksum = md5($data);
+
+ $buf = serialize(array($data,$chksum));
+ setcookie('ari_auth',$buf,time()+365*24*60*60,'/');
+ }
+
+ // set category
+ if (!$category) {
+ $category = "general";
+ }
+
+ // set context
+ if (!$context) {
+ $context = "default";
+ }
+
+ // no login user
+ if ($ARI_NO_LOGIN) {
+ $extension = 'admin';
+ $name = 'Administrator';
+ $admin_callmonitor = 1;
+ $default_page = $ARI_DEFAULT_ADMIN_PAGE;
+ }
+
+ // get outboundCID if it exists
+ $outboundCID = $this->getOutboundCID($extension);
+
+ // set
+ if ($extension) {
+ $_SESSION['ari_user']['extension'] = $extension;
+ $_SESSION['ari_user']['outboundCID'] = $outboundCID;
+ $_SESSION['ari_user']['displayname'] = $displayname;
+ $_SESSION['ari_user']['voicemail_password'] = $vm_password;
+ $_SESSION['ari_user']['category'] = $category;
+ $_SESSION['ari_user']['context'] = $context;
+ $_SESSION['ari_user']['voicemail_enabled'] = $voicemail_enabled;
+ $_SESSION['ari_user']['voicemail_email_address'] = $voicemail_email_address;
+ $_SESSION['ari_user']['voicemail_pager_address'] = $voicemail_pager_address;
+ $_SESSION['ari_user']['voicemail_email_enable'] = $voicemail_email_enable;
+ foreach ($voicemail_email as $key => $value) {
+ $_SESSION['ari_user']['voicemail_email'][$key] = $value;
+ }
+ $_SESSION['ari_user']['admin'] = $admin;
+ $_SESSION['ari_user']['admin_callmonitor'] = $admin_callmonitor;
+ $_SESSION['ari_user']['default_page'] = $default_page;
+
+ // force the session data saved
+ session_write_close();
+ }
+ }
+ }
+
+ /*
+ * Gets user outbound caller id
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $ret
+ * outbound caller id
+ */
+ function getOutboundCID($extension) {
+
+ global $asterisk_manager_interface;
+
+ $ret = '';
+ $response = $asterisk_manager_interface->Command2("Action: Command\r\nCommand: database get AMPUSER $extension/outboundcid\r\n\r\n");
+ if ($response) {
+
+ $posLeft = strpos( $response, "<")+strlen("<");
+ $posRight = strpos( $response, ">", $posLeft);
+ $ret = substr( $response,$posLeft,$posRight-$posLeft);
+ }
+ return $ret;
+ }
+
+ /**
+ * logout
+ */
+ function Unauth() {
+ unset($_COOKIE["ari_auth"]);
+ setcookie('ari_auth',"",time(),'/');
+ unset($_SESSION['ari_user']);
+ }
+
+ /**
+ * Provide a login form for user
+ *
+ * @param $request
+ * Variable to hold data entered into form
+ */
+ function GetForm() {
+
+ global $ARI_NO_LOGIN;
+
+ if ($ARI_NO_LOGIN) {
+ $ret = '';
+ return;
+ }
+
+ if (isset($_GET['login'])) {
+ $login = $_GET['login'];
+ }
+
+ // if user name and password were given, but there was a problem report the error
+ if ($this->error!='') {
+ $ret = $this->error;
+ }
+
+ $language = new Language();
+ $display = new Display(NULL);
+
+ // new header
+ $ret .= $display->DisplayHeaderText(_("Login"));
+ $ret .= $display->DisplayLine();
+ $ret .= checkErrorMessage();
+
+ $ret .= "
+ <table id='login'>
+ <form id='login' name='login' action=" . $_SESSION['ARI_ROOT'] . " method='POST'>
+ <tr>
+ <td class='right'>
+ <small><small>" . _("Login") . ":&nbsp;&nbsp;</small></small>
+ </td>
+ <td>
+ <input type='text' name='username' value='" . $login . "' maxlength=20 tabindex=1>
+ </td>
+ </tr>
+ <tr>
+ <td class='right'>
+ <small><small>" . _("Password") . ":&nbsp;&nbsp;</small></small>
+ </td>
+ <td colspan=1>
+ <input type='password' name='password' maxlength=20 tabindex=2>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>
+ <input type='submit' name='btnSubmit' value='" . _("Submit") . "' tabindex=3></small></small></p>
+ </td>
+ </tr>
+ <tr>
+ <td class='right'>
+ <input type='checkbox' name='remember'>
+ </td>
+ <td class='left'>
+ <p class='small'>" . _("Remember Password") . "</p>
+ </td>
+ </tr>
+ </form>
+ <tr>
+ <td></td>
+ <td>
+ " . $language->getForm() . "
+ </td>
+ </tr>
+ <tr><td>&nbsp;</td></tr>
+ </table>
+ <table id='login_text'>
+ <tr>
+ <td>" .
+ _("Use your <b>Voicemail Mailbox and Password</b>") . "<br>" .
+ _("This is the same password used for the phone") . "<br>" .
+ "<br>" .
+ _("For password maintenance or assistance, contact your Phone System Administrator.") . "<br>" . "
+ </td>
+ </tr>
+ </table>";
+
+ $ret .= "
+ <script type='text/javascript'>
+ <!--
+ if (document.login) {
+ document.login.username.focus();
+ }
+ // -->
+ </script>";
+
+ return $ret;
+ }
+
+
+}
+
+
+?>
diff --git a/fs_selfservice/fri/includes/main.conf.php b/fs_selfservice/fri/includes/main.conf.php
new file mode 100644
index 0000000..cedf60c
--- /dev/null
+++ b/fs_selfservice/fri/includes/main.conf.php
@@ -0,0 +1,331 @@
+<?php
+
+/**
+ * @file
+ * site-specific configuration file.
+ */
+
+###############################
+# AMP or standalone settings
+###############################
+#
+# From AMP. Used for logon to database.
+#
+$AMP_FUNCTIONS_FILES = "../admin/functions.php;../admin/functions.inc.php";
+$AMPORTAL_CONF_FILE = "/etc/amportal.conf";
+
+#
+# Host for Asterisk Manager Interface
+#
+$ASTERISKMGR_DBHOST = "localhost";
+
+#
+# Database options for older legacy AMP installations (pre-FreePBX)
+# - $LEGACY_AMP_DBFILE only needs to be set if using a database like sqlite
+#
+$LEGACY_AMP_DBHOST = "localhost";
+$LEGACY_AMP_DBENGINE = "mysql";
+$LEGACY_AMP_DBFILE = "";
+$LEGACY_AMP_DBNAME = "asterisk";
+
+#
+# Database cdr settings
+# - Only need to update these settings if standalone or an older AMP version (pre-FreePBX) is used
+# - $ASTERISKCDR_DBFILE only needs to be set if using a database like sqlite
+# Options: supported database types (others are supported, but not listed)
+# 'mysql' - MySQL
+# 'pgsql' - PostgreSQL
+# 'oci8' - Oracle
+# 'odbc' - ODBC
+#
+$ASTERISKCDR_DBHOST = "localhost";
+$ASTERISKCDR_DBENGINE = "mysql";
+$ASTERISKCDR_DBFILE = "";
+$ASTERISKCDR_DBNAME = "asteriskcdrdb";
+$ASTERISKCDR_DBTABLE = "cdr";
+
+#
+# Standalone, for use without AMP
+# set use = true;
+# set asterisk_mgruser to Asterisk Call Manager username
+# set asterisk_mgrpass to Asterisk Call Manager password
+#
+$STANDALONE['use'] = false;
+$STANDALONE['asterisk_mgruser'] = "";
+$STANDALONE['asterisk_mgrpass'] = "";
+$STANDALONE['asteriskcdr_dbuser'] = "";
+$STANDALONE['asteriskcdr_dbpass'] = "";
+
+###############################
+# authentication settings
+###############################
+#
+# For using the Call Monitor only
+# option: 0 - use Authentication, Voicemail, and Call Monitor
+# 1 - use only the Call Monitor
+#
+$ARI_NO_LOGIN = 0;
+
+#
+# Admin only account
+#
+$ARI_ADMIN_USERNAME = "admin";
+$ARI_ADMIN_PASSWORD ="ari_password";
+#
+# Admin extensions
+# option: Comma delimited list of extensions
+#
+$ARI_ADMIN_EXTENSIONS = "";
+
+#
+# Authentication password to unlock cookie password
+# This must be all continuous and only letters and numbers
+#
+$ARI_CRYPT_PASSWORD = "z1Mc6KRxA7Nw90dGjY5qLXhtrPgJOfeCaUmHvQT3yW8nDsI2VkEpiS4blFoBuZ";
+
+###############################
+# modules settings
+###############################
+#
+# modules with admin only status (they will not be displayed for regular users)
+# option: Comma delimited list of module names (ie voicemail,callmonitor,help,settings)
+#
+$ARI_ADMIN_MODULES = "";
+
+#
+# disable modules (you can also just delete them from /recordings/modules without problems)
+# option: Comma delimited list of module names (ie voicemail,callmonitor,help,settings)
+#
+$ARI_DISABLED_MODULES = "";
+
+#
+# sets the default admin page
+# option: Comma delimited list of module names (ie voicemail,callmonitor,help,settings)
+#
+$ARI_DEFAULT_ADMIN_PAGE = "callmonitor";
+
+#
+# sets the default user page
+# option: Comma delimited list of module names (ie voicemail,callmonitor,help,settings)
+#
+#$ARI_DEFAULT_USER_PAGE = "voicemail";
+$ARI_DEFAULT_USER_PAGE = "dashboard";
+
+#
+# enables ajax page refresh
+# option: 0 - disable ajax page refresh
+# 1 - enable ajax page refresh
+#
+$AJAX_PAGE_REFRESH_ENABLE = 1;
+
+#
+# sets the default user page
+# option: refresh time in 'minutes:seconds' (0 to inifinity) : (0 to 59)
+#
+$AJAX_PAGE_REFRESH_TIME ="01:00";
+###############################
+# voicemail settings
+###############################
+#
+# voicemail config.
+#
+$ASTERISK_VOICEMAIL_CONF = "/etc/asterisk/voicemail.conf";
+
+#
+# To set to a specific context.
+# If using default or more than one context then leave blank
+#
+$ASTERISK_VOICEMAIL_CONTEXT = "";
+
+#
+# Location of asterisk voicemail recordings on server
+# Use semi-colon for multiple paths
+#
+$ASTERISK_VOICEMAIL_PATH = "/var/spool/asterisk/voicemail";
+
+#
+# valid mailbox folders
+#
+$ASTERISK_VOICEMAIL_FOLDERS = array();
+$ASTERISK_VOICEMAIL_FOLDERS[0]['folder'] = "INBOX";
+$ASTERISK_VOICEMAIL_FOLDERS[0]['name'] = _("INBOX");
+$ASTERISK_VOICEMAIL_FOLDERS[1]['folder'] = "Family";
+$ASTERISK_VOICEMAIL_FOLDERS[1]['name'] = _("Family");
+$ASTERISK_VOICEMAIL_FOLDERS[2]['folder'] = "Friends";
+$ASTERISK_VOICEMAIL_FOLDERS[2]['name'] = _("Friends");
+$ASTERISK_VOICEMAIL_FOLDERS[3]['folder'] = "Old";
+$ASTERISK_VOICEMAIL_FOLDERS[3]['name'] = _("Old");
+$ASTERISK_VOICEMAIL_FOLDERS[4]['folder'] = "Work";
+$ASTERISK_VOICEMAIL_FOLDERS[4]['name'] = _("Work");
+
+###############################
+# call monitor settings
+###############################
+#
+# Location of asterisk call monitor recordings on server
+#
+$ASTERISK_CALLMONITOR_PATH = "/var/spool/asterisk/monitor";
+
+#
+# Extensions with access to all call monitor recordings
+# option: Comma delimited list of extensions or "all"
+#
+$CALLMONITOR_ADMIN_EXTENSIONS ="";
+#
+# Allow call monitor users to delete monitored calls
+# option: 0 - do not show controls
+# 1 - show controls
+#
+$CALLMONITOR_ALLOW_DELETE = 1;
+
+#
+# Allow for aggressive matching of recording files to database records
+# will match recordings that are marked several seconds off
+# option: 0 - do not aggressively match
+# 1 - aggressively match
+#
+$CALLMONITOR_AGGRESSIVE_MATCHING = 1;
+
+#
+# Limits log/recording file matching to exact matching
+# will not try to look through all the recordings and make a best match
+# even if there is not uniqueid
+# requires that the MYSQL_UNIQUEID flag be compiled in asterisk-addons
+# (in the asterisk-addon Makefile add the following "CFLAGS+=-DMYSQL_LOGUNIQUEID")
+#
+# * use if there are or will be more than 2500 recording files
+#
+# option: 0 - do not exact match
+# 1 - only exact match
+#
+$CALLMONITOR_ONLY_EXACT_MATCHING = 0;
+
+###############################
+# conference page settings
+###############################
+#
+# Meetme extension prefix
+# for this module to function, the user has to have
+# a meetme conference room {prefix}{extension}
+#
+$CONFERENCE_WEBMEETME_PREFIX = "";
+
+#
+# url to web meetme conference room
+# example: "http://example.mycompany.com/webmeetme"
+#
+$CONFERENCE_WEBMEETME_URL = "";
+
+###############################
+# help page settings
+###############################
+#
+# help feature codes
+# list of handset options and their function
+#
+$ARI_HELP_FEATURE_CODES = array();
+//$ARI_HELP_FEATURE_CODES['*411'] = _("Directory");
+//$ARI_HELP_FEATURE_CODES['*43'] = _("Echo Test");
+//$ARI_HELP_FEATURE_CODES['*60'] = _("Time");
+//$ARI_HELP_FEATURE_CODES['*61'] = _("Weather");
+//$ARI_HELP_FEATURE_CODES['*62'] = _("Schedule wakeup call");
+//$ARI_HELP_FEATURE_CODES['*65'] = _("festival test (your extension is XXX)");
+//$ARI_HELP_FEATURE_CODES['*77'] = _("IVR Recording");
+//$ARI_HELP_FEATURE_CODES['*99'] = _("Playback IVR Recording");
+//$ARI_HELP_FEATURE_CODES['666'] = _("Test Fax");
+//$ARI_HELP_FEATURE_CODES['7777'] = _("Simulate incoming call");
+
+$ARI_HELP_FEATURE_CODES['*72'] = _("Call Forward All Activate");
+$ARI_HELP_FEATURE_CODES['*73'] = _("Call Forward All Deactivate");
+$ARI_HELP_FEATURE_CODES['*74'] = _("Call Forward All Prompting Deactivate");
+$ARI_HELP_FEATURE_CODES['*90'] = _("Call Forward Busy Activate");
+$ARI_HELP_FEATURE_CODES['*91'] = _("Call Forward Busy Deactivate");
+$ARI_HELP_FEATURE_CODES['*92'] = _("Call Forward Busy Prompting Deactivate");
+$ARI_HELP_FEATURE_CODES['*52'] = _("Call Forward No Answer/Unavailable Activate");
+$ARI_HELP_FEATURE_CODES['*53'] = _("Call Forward No Answer/Unavailable Deactivate");
+$ARI_HELP_FEATURE_CODES['*70'] = _("Call Waiting - Activate");
+$ARI_HELP_FEATURE_CODES['*71'] = _("Call Waiting - Deactivate");
+$ARI_HELP_FEATURE_CODES['*78'] = _("Do-Not-Disturb Activate");
+$ARI_HELP_FEATURE_CODES['*79'] = _("Do-Not-Disturb Deactivate");
+$ARI_HELP_FEATURE_CODES['*97'] = _("My Voicemail");
+$ARI_HELP_FEATURE_CODES['*98'] = _("Dial Voicemail");
+
+###############################
+# settings page settings
+###############################
+#
+# protocol config.
+# config_file options: semi-colon delimited list of extensions
+#
+$ASTERISK_PROTOCOLS = array();
+$ASTERISK_PROTOCOLS['iax']['table'] = "iax";
+$ASTERISK_PROTOCOLS['iax']['config_files'] = "/etc/asterisk/iax.conf;/etc/asterisk/iax_additional.conf";
+$ASTERISK_PROTOCOLS['sip']['table'] = "sip";
+$ASTERISK_PROTOCOLS['sip']['config_files'] = "/etc/asterisk/sip.conf;/etc/asterisk/sip_additional.conf";
+$ASTERISK_PROTOCOLS['zap']['table'] = "zap";
+$ASTERISK_PROTOCOLS['zap']['config_files'] = "/etc/asterisk/zapata.conf;/etc/asterisk/zapata_additional.conf";
+
+# Settings for Follow-Me Select Boxes in seconds
+#
+
+$SETTINGS_PRERING_LOW = 4;
+$SETTINGS_PRERING_HIGH = 30;
+$SETTINGS_LISTRING_LOW = 6;
+$SETTINGS_LISTRING_HIGH = 60;
+
+$SETTINGS_FOLLOW_ME_LIST_MAX = 5;
+$SETTINGS_ALLOW_VMX_SETTINGS = true;
+#
+# For setting
+# option: 0 - do not show controls
+# 1 - show controls
+#
+$SETTINGS_ALLOW_CALLFORWARD_SETTINGS = 1;
+$SETTINGS_ALLOW_VOICEMAIL_SETTINGS = 1;
+$SETTINGS_ALLOW_VOICEMAIL_PASSWORD_SET = 1;
+
+#
+# password length
+# setting: number of characters required for changing voicemail password
+#
+$SETTINGS_VOICEMAIL_PASSWORD_LENGTH = 3;
+
+#
+# password exact length
+# option: 0 - do not require exact length when setting the password
+# 1 - require exact length when setting the password
+#
+$SETTINGS_VOICEMAIL_PASSWORD_EXACT = 0;
+
+#
+# voicemail email option descriptions
+#
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS = array();
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS['attach'] = _("Email voicemail as attachment");
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS['saycid'] = _("Say caller id in recording emailed");
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS['envelope'] = _("Say envelop (date/time) in recording emailed");
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS['delete'] = _("Delete voicemail when emailed");
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS['nextaftercmd'] = _("Play next message after deleting current message");
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS['review'] = _("Ask caller to review their voicemail before sending");
+$SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS['maxmessage'] = _("Maximum time in seconds a voicemail will record");
+
+#
+# Default
+# option: ".wav" - wav format
+# ".gsm" - gsm format
+#
+$ARI_VOICEMAIL_AUDIO_FORMAT_DEFAULT = ".wav";
+
+#
+# For setting
+# option: 0 - do not show controls
+# 1 - show controls
+#
+$SETTINGS_ALLOW_CALL_RECORDING_SET = 1;
+
+
+$SETTINGS_ALLOW_PHONE_SETTINGS = 1;
+
+
+
+?>
diff --git a/fs_selfservice/fri/index.php b/fs_selfservice/fri/index.php
new file mode 100644
index 0000000..0fe6149
--- /dev/null
+++ b/fs_selfservice/fri/index.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * main
+ */
+
+include_once("includes/bootstrap.php");
+ariPageHeader();
+include_once("includes/common.php");
+
+handler();
+
+ariPageFooter();
+
+
+?>
+
+
+
diff --git a/fs_selfservice/fri/locale/ari.po b/fs_selfservice/fri/locale/ari.po
new file mode 100644
index 0000000..4e3493e
--- /dev/null
+++ b/fs_selfservice/fri/locale/ari.po
@@ -0,0 +1,590 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr ""
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr ""
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+msgid "Asterisk command not understood"
+msgstr ""
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:226
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:173
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr ""
+
+#: ../includes/common.php:174
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr ""
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr ""
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr ""
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr ""
+
+#: ../includes/display.php:139
+#, php-format
+msgid "Results %d - %d of %d"
+msgstr ""
+
+#: ../includes/display.php:141
+#, php-format
+msgid "Results %d"
+msgstr ""
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr ""
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr ""
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr ""
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr ""
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr ""
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr ""
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr ""
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr ""
+
+#: ../includes/login.php:451
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr ""
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr ""
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr ""
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr ""
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr ""
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr ""
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr ""
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr ""
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr ""
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr ""
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr ""
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr ""
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr ""
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:239
+msgid "IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:244
+msgid "Message Center (does not ask for extension)"
+msgstr ""
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr ""
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr ""
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr ""
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr ""
+
+#: ../modules/callmonitor.module:147
+msgid "duration"
+msgstr ""
+
+#: ../modules/callmonitor.module:150
+msgid "ignore"
+msgstr ""
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr ""
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr ""
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr ""
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr ""
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr ""
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr ""
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr ""
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr ""
+
+#: ../modules/callmonitor.module:259
+#, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Call Monitor for %s (%s)"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr ""
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr ""
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr ""
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr ""
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, php-format
+msgid "Conference for %s (%s%s)"
+msgstr ""
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr ""
+
+#: ../modules/help.module:70
+#, php-format
+msgid "Help for %s (%s)"
+msgstr ""
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr ""
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr ""
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr ""
+
+#: ../modules/settings.module:157
+#, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr ""
+
+#: ../modules/settings.module:162
+#, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr ""
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr ""
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, php-format
+msgid "%s does not exist or is not writable"
+msgstr ""
+
+#: ../modules/settings.module:223
+msgid "Voicemail email and pager address not changed"
+msgstr ""
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+msgid "Voicemail email settings not changed"
+msgstr ""
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr ""
+
+#: ../modules/settings.module:408
+msgid "Call Routing"
+msgstr ""
+
+#: ../modules/settings.module:411
+msgid "Call Forwarding:"
+msgstr ""
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+msgid "Enable"
+msgstr ""
+
+#: ../modules/settings.module:431
+#, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr ""
+
+#: ../modules/settings.module:434
+#, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr ""
+
+#: ../modules/settings.module:439
+msgid "Voicemail Password:"
+msgstr ""
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr ""
+
+#: ../modules/settings.module:492
+msgid "Email Voicemail To:"
+msgstr ""
+
+#: ../modules/settings.module:498
+msgid "Pager Voicemail To:"
+msgstr ""
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr ""
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr ""
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr ""
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr ""
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr "Call Monitor Settings"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr ""
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr ""
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr ""
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr ""
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr ""
+
+#: ../modules/settings.module:669
+#, php-format
+msgid "Settings for %s (%s)"
+msgstr ""
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr ""
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr ""
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr ""
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr ""
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr ""
+
+#: ../modules/voicemail.module:307
+msgid "Folder"
+msgstr ""
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr ""
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr ""
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr ""
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr ""
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+msgid "Voicemail Login not found."
+msgstr ""
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr ""
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr ""
+
+#: ../modules/voicemail.module:428
+#, php-format
+msgid "Voicemail for %s (%s)"
+msgstr ""
+
+#: ../modules/voicemail.module:678
+#, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr ""
+
+#: ../modules/voicemail.module:718
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr ""
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr ""
diff --git a/fs_selfservice/fri/locale/ari.utf-8.po b/fs_selfservice/fri/locale/ari.utf-8.po
new file mode 100644
index 0000000..aff5a75
--- /dev/null
+++ b/fs_selfservice/fri/locale/ari.utf-8.po
@@ -0,0 +1,590 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr ""
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr ""
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+msgid "Asterisk command not understood"
+msgstr ""
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:226
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:173
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr ""
+
+#: ../includes/common.php:174
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr ""
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr ""
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr ""
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr ""
+
+#: ../includes/display.php:139
+#, php-format
+msgid "Results %d - %d of %d"
+msgstr ""
+
+#: ../includes/display.php:141
+#, php-format
+msgid "Results %d"
+msgstr ""
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr ""
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr ""
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr ""
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr ""
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr ""
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr ""
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr ""
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr ""
+
+#: ../includes/login.php:451
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr ""
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr ""
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr ""
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr ""
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr ""
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr ""
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr ""
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr ""
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr ""
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr ""
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr ""
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr ""
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr ""
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:239
+msgid "IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:244
+msgid "Message Center (does not ask for extension)"
+msgstr ""
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr ""
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr ""
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr ""
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr ""
+
+#: ../modules/callmonitor.module:147
+msgid "duration"
+msgstr ""
+
+#: ../modules/callmonitor.module:150
+msgid "ignore"
+msgstr ""
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr ""
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr ""
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr ""
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr ""
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr ""
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr ""
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr ""
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr ""
+
+#: ../modules/callmonitor.module:259
+#, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr ""
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr ""
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr ""
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr ""
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr ""
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, php-format
+msgid "Conference for %s (%s%s)"
+msgstr ""
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr ""
+
+#: ../modules/help.module:70
+#, php-format
+msgid "Help for %s (%s)"
+msgstr ""
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr ""
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr ""
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr ""
+
+#: ../modules/settings.module:157
+#, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr ""
+
+#: ../modules/settings.module:162
+#, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr ""
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr ""
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, php-format
+msgid "%s does not exist or is not writable"
+msgstr ""
+
+#: ../modules/settings.module:223
+msgid "Voicemail email and pager address not changed"
+msgstr ""
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+msgid "Voicemail email settings not changed"
+msgstr ""
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr ""
+
+#: ../modules/settings.module:408
+msgid "Call Routing"
+msgstr ""
+
+#: ../modules/settings.module:411
+msgid "Call Forwarding:"
+msgstr ""
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+msgid "Enable"
+msgstr ""
+
+#: ../modules/settings.module:431
+#, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr ""
+
+#: ../modules/settings.module:434
+#, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr ""
+
+#: ../modules/settings.module:439
+msgid "Voicemail Password:"
+msgstr ""
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr ""
+
+#: ../modules/settings.module:492
+msgid "Email Voicemail To:"
+msgstr ""
+
+#: ../modules/settings.module:498
+msgid "Pager Voicemail To:"
+msgstr ""
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr ""
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr ""
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr ""
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr ""
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr ""
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr ""
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr ""
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr ""
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr ""
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr ""
+
+#: ../modules/settings.module:669
+#, php-format
+msgid "Settings for %s (%s)"
+msgstr ""
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr ""
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr ""
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr ""
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr ""
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr ""
+
+#: ../modules/voicemail.module:307
+msgid "Folder"
+msgstr ""
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr ""
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr ""
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr ""
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr ""
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+msgid "Voicemail Login not found."
+msgstr ""
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr ""
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr ""
+
+#: ../modules/voicemail.module:428
+#, php-format
+msgid "Voicemail for %s (%s)"
+msgstr ""
+
+#: ../modules/voicemail.module:678
+#, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr ""
+
+#: ../modules/voicemail.module:718
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr ""
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr ""
diff --git a/fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..b94eba2
--- /dev/null
+++ b/fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..b89b612
--- /dev/null
+++ b/fs_selfservice/fri/locale/de_DE/LC_MESSAGES/ari.po
@@ -0,0 +1,631 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2005 AsteriskPBX.de
+# This file is distributed under the same license as the PACKAGE package.
+# Till Stoemer <ts@AsteriskPBX.de>, 2005.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: ari-de\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-04-03 08:26-0400\n"
+"PO-Revision-Date: 2005-12-10 19:50+0100\n"
+"Last-Translator: Till Stoermer <ts@AsteriskPBX.de>\n"
+"Language-Team: German <ts@AsteriskPBX.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:48
+msgid "Asterisk Call Manager not responding"
+msgstr "Der Asterisk Call-Manager reagiert nicht."
+
+#: ../includes/asi.php:56
+msgid "Asterisk authentication failed:"
+msgstr "Anmeldung am Asterisk gescheitert."
+
+#: ../includes/asi.php:98 ../includes/asi.php:112
+#, fuzzy
+msgid "Asterisk command not understood"
+msgstr "Asterisk reload command not understood"
+
+#: ../includes/bootstrap.php:106
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:209
+msgid "ARI requires a version of PHP 4.0 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:228
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:167
+#, fuzzy
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "Kann nicht zum Asterisk-Manager verbinden"
+
+#: ../includes/common.php:168
+msgid ""
+"Check the ARI 'main.conf' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:169
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:170
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:187 ../includes/common.php:202
+#, fuzzy
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+"&Uuml;berpr&uuml;fe die AMP-Installation, Asterisk-Datenbank, oder die ARI "
+"main.conf"
+
+#: ../includes/common.php:332
+msgid "Logout"
+msgstr "Abmelden"
+
+#: ../includes/common.php:337
+msgid "Page Not Found."
+msgstr "Seite nicht gefunden"
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "Suchen"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "Suchen nach"
+
+#: ../includes/display.php:139
+#, fuzzy, php-format
+msgid "Results %d of %d"
+msgstr "Ergebnis"
+
+#: ../includes/display.php:141
+#, fuzzy, php-format
+msgid "Results %d"
+msgstr "Ergebnis"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "Erste"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "Letzte"
+
+#: ../includes/login.php:239
+#, fuzzy
+msgid "Voicemail Login not found."
+msgstr "Vicemail-Login nicht gefunden"
+
+#: ../includes/login.php:240
+msgid "No access to voicemail"
+msgstr "Kein Zugriff auf Voicemail"
+
+#: ../includes/login.php:266
+msgid "Incorrect Password"
+msgstr "Falsches Passwort"
+
+#: ../includes/login.php:278
+msgid "Incorrect Username or Password"
+msgstr "Falscher Benutzer oder Passwort"
+
+#: ../includes/login.php:381 ../includes/login.php:391
+msgid "Login"
+msgstr "Anmeldung"
+
+#: ../includes/login.php:399
+msgid "Password"
+msgstr "Passwort"
+
+#: ../includes/login.php:408
+msgid "Submit"
+msgstr "Anmelden"
+
+#: ../includes/login.php:416
+msgid "Remember Password"
+msgstr "Passwort merken"
+
+#: ../includes/login.php:431
+#, fuzzy
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr "Voicemail Mailbox und Password"
+
+#: ../includes/login.php:432
+msgid "This is the same password used for the phone"
+msgstr "Dieses ist das selbe Passwort, das beim Telefon genutzt wird."
+
+#: ../includes/login.php:434
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+"F&uuml;r Passwort-&Auml;nderungen, kontaktieren Sie Ihren Voicemail-Admin"
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr "Eingang"
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr "Familie"
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr "Freunde"
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr "Alt"
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr "Arbeit"
+
+#: ../includes/main.conf.php:213
+msgid "Directory"
+msgstr ""
+
+#: ../includes/main.conf.php:214
+msgid "Echo Test"
+msgstr ""
+
+#: ../includes/main.conf.php:215 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:326
+msgid "Time"
+msgstr "Uhrzeit"
+
+#: ../includes/main.conf.php:216
+msgid "Weather"
+msgstr ""
+
+#: ../includes/main.conf.php:217
+msgid "Schedule wakeup call"
+msgstr ""
+
+#: ../includes/main.conf.php:218
+msgid "festival test (your extension is XXX)"
+msgstr ""
+
+#: ../includes/main.conf.php:219
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:220
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:221
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:222
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:223
+#, fuzzy
+msgid "IVR Recording"
+msgstr "Aufnahmen"
+
+#: ../includes/main.conf.php:224
+msgid "Enable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:225
+msgid "Disable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:226
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:227
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:228
+msgid "Message Center (does no ask for extension)"
+msgstr ""
+
+#: ../includes/main.conf.php:229
+msgid "Enter Message Center"
+msgstr ""
+
+#: ../includes/main.conf.php:230
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:231
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:232
+msgid "Simulate incoming call"
+msgstr ""
+
+#: ../includes/main.conf.php:273
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:274
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:275
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:276
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:277
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:278
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:279
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr "Anrufliste"
+
+#: ../modules/callmonitor.module:132 ../modules/voicemail.module:117
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:303
+#, fuzzy
+msgid "delete"
+msgstr "Ausw&auml;hlen"
+
+#: ../modules/callmonitor.module:147
+#, fuzzy
+msgid "duration"
+msgstr "Dauer"
+
+#: ../modules/callmonitor.module:150
+#, fuzzy
+msgid "ignore"
+msgstr "Keine"
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:324
+msgid "Date"
+msgstr "Datum"
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:328
+msgid "Caller ID"
+msgstr "Anrufer-Nummer"
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr "Anrufer"
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr "Angerufener"
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr "Kontext"
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:334
+msgid "Duration"
+msgstr "Dauer"
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr "Monitor"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:375
+msgid "play"
+msgstr "Abspielen"
+
+#: ../modules/callmonitor.module:259
+#, fuzzy, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Anrufliste"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:459
+msgid "select"
+msgstr "Ausw&auml;hlen"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:460
+msgid "all"
+msgstr "Alle"
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:461
+msgid "none"
+msgstr "Keine"
+
+#: ../modules/callmonitor.module:543
+msgid "Only deletes recording files, not cdr log"
+msgstr "Nur die Aufnahme-Datei wird gel&ouml;scht (In der CDR nicht)"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr ""
+
+#: ../modules/help.module:70
+#, php-format
+msgid "Help for %s (%s)"
+msgstr ""
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:647
+msgid "Settings"
+msgstr "Einstellungen"
+
+#: ../modules/settings.module:122
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:123
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:143 ../modules/settings.module:148
+#: ../modules/settings.module:153 ../modules/settings.module:158
+#: ../modules/settings.module:168 ../modules/settings.module:173
+msgid "Voicemail password not changed"
+msgstr "Voicemail-Passwort nicht ge&auml;ndert"
+
+#: ../modules/settings.module:144
+msgid "Password and password confirm must not be blank"
+msgstr "Passwort und Passwort-Wiederholen-Feld darf nicht leer sein"
+
+#: ../modules/settings.module:149
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "Das Passwort muss aus mindestens 4 Ziffern bestehen."
+
+#: ../modules/settings.module:154
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "Das Passwort muss aus mindestens 4 Ziffern bestehen."
+
+#: ../modules/settings.module:159
+msgid "Password and password confirm do not match"
+msgstr "Die Passwort stimmen nicht &uuml;berein."
+
+#: ../modules/settings.module:169 ../modules/settings.module:174
+#: ../modules/settings.module:226 ../modules/settings.module:231
+#, fuzzy, php-format
+msgid "%s does not exist or is not writable"
+msgstr "existiert nicht, oder ist nicht lesbar."
+
+#: ../modules/settings.module:215
+#, fuzzy
+msgid "Voicemail email and pager address not changed"
+msgstr "Voicemail-Passwort nicht ge&auml;ndert"
+
+#: ../modules/settings.module:225 ../modules/settings.module:230
+#, fuzzy
+msgid "Voicemail email settings not changed"
+msgstr "Voicemail-Passwort nicht ge&auml;ndert"
+
+#: ../modules/settings.module:375
+msgid "Language:"
+msgstr "Sprache"
+
+#: ../modules/settings.module:396
+#, fuzzy
+msgid "Call Routing"
+msgstr "Call Monitor Einstellungen"
+
+#: ../modules/settings.module:399
+msgid "Call Forwarding:"
+msgstr ""
+
+#: ../modules/settings.module:407 ../modules/settings.module:486
+#, fuzzy
+msgid "Enable"
+msgstr "in Tabelle"
+
+#: ../modules/settings.module:418
+msgid "Voicemail Password:"
+msgstr "Voicemail-Passwort"
+
+#: ../modules/settings.module:424
+msgid "Enter again to confirm:"
+msgstr "Erneute Eingabe zum best&auml;tigen"
+
+#: ../modules/settings.module:430
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "Das Passwort muss aus mindestens 4 Ziffern bestehen."
+
+#: ../modules/settings.module:471
+#, fuzzy
+msgid "Email Voicemail To:"
+msgstr "Voicemail"
+
+#: ../modules/settings.module:477
+#, fuzzy
+msgid "Pager Voicemail To:"
+msgstr "Voicemail"
+
+#: ../modules/settings.module:539
+msgid "Audio Format:"
+msgstr "Audio-Format"
+
+#: ../modules/settings.module:542
+msgid "Best Quality"
+msgstr "Beste Qualit&auml;t"
+
+#: ../modules/settings.module:543
+msgid "Smallest Download"
+msgstr "F&uuml;r geringen Download"
+
+#: ../modules/settings.module:551
+msgid "Voicemail Settings"
+msgstr "Voicemail-Einstellungen"
+
+#: ../modules/settings.module:591
+msgid "Call Monitor Settings"
+msgstr "Call Monitor Einstellungen"
+
+#: ../modules/settings.module:594
+msgid "Record INCOMING:"
+msgstr "Aufnahme eingehender Telefonate:"
+
+#: ../modules/settings.module:596 ../modules/settings.module:604
+msgid "Always"
+msgstr "Immer"
+
+#: ../modules/settings.module:597 ../modules/settings.module:605
+msgid "Never"
+msgstr "Nie"
+
+#: ../modules/settings.module:598 ../modules/settings.module:606
+msgid "On-Demand"
+msgstr "Bei Bedarf"
+
+#: ../modules/settings.module:602
+msgid "Record OUTGOING:"
+msgstr "Aufnahme abgehende Telefonate"
+
+#: ../modules/settings.module:649
+#, fuzzy, php-format
+msgid "Settings for %s (%s)"
+msgstr "Einstellungen f&uuml;r"
+
+#: ../modules/settings.module:685
+msgid "Update"
+msgstr "Erneuern"
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr "Voicemail"
+
+#: ../modules/voicemail.module:161
+msgid "A folder must be selected before the message can be moved."
+msgstr ""
+"Ein Ordner muss gew&auml;hlt werden, bevor die Nachricht verschoben werden "
+"kann."
+
+#: ../modules/voicemail.module:175
+msgid "An extension must be selected before the message can be forwarded."
+msgstr ""
+"Ein Anschluss muss gew&auml;hlt werden, bevor die Nachricht weitergeleitet "
+"werden kann."
+
+#: ../modules/voicemail.module:239
+msgid "No Voicemail Recordings for Admin"
+msgstr "No Voicemail Recordings for Admin"
+
+#: ../modules/voicemail.module:306
+msgid "move_to"
+msgstr ""
+
+#: ../modules/voicemail.module:309
+msgid "Folder"
+msgstr "Ordner"
+
+#: ../modules/voicemail.module:313
+msgid "forward_to"
+msgstr ""
+
+#: ../modules/voicemail.module:330
+msgid "Priority"
+msgstr "Prirorit&auml;t"
+
+#: ../modules/voicemail.module:332
+msgid "Orig Mailbox"
+msgstr "Orig Mailbox"
+
+#: ../modules/voicemail.module:364
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:379
+msgid "Voicemail recording(s) was not found."
+msgstr "Sprachnachricht(en) nicht gefunden"
+
+#: ../modules/voicemail.module:380
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:412
+#, fuzzy, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "Voicemail"
+
+#: ../modules/voicemail.module:662
+#, fuzzy, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "Konnte Mailbox-Ordner nicht erstellen"
+
+#: ../modules/voicemail.module:702
+#, fuzzy, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr "Zugriff verweigert auf Ordner"
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr "Download"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "Kein Verzeichnis, oder nicht lesbar"
+
+#~ msgid "No database connection"
+#~ msgstr "Keine Verbindung zur Datenbank"
+
+#~ msgid "of"
+#~ msgstr "von"
+
+#~ msgid "Login used"
+#~ msgstr "Login genutzt"
+
+#~ msgid "Use your"
+#~ msgstr "Nutze Deine"
+
+#~ msgid "for"
+#~ msgstr "f&uuml;r"
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr "Das Passwort muss aus mindestens 4 Ziffern bestehen."
+
+#~ msgid "Check voicemail audio format on settings page to change from"
+#~ msgstr "Check voicemail audio format on settings page to change from"
+
+#~ msgid "Searching of voicemail is not yet implemented"
+#~ msgstr "Searching of voicemail is not yet implemented"
+
+#~ msgid "on the server"
+#~ msgstr "auf dem Server"
+
+#~ msgid "Folders"
+#~ msgstr "Ordner"
diff --git a/fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..6b00b14
--- /dev/null
+++ b/fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..2566494
--- /dev/null
+++ b/fs_selfservice/fri/locale/el_GR/LC_MESSAGES/ari.po
@@ -0,0 +1,648 @@
+# Copyright (C) 2005 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Elias Sofronas <esofronas@gmail.com>, 2005.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: 2005-11-14 10:06+0200\n"
+"Last-Translator: Elias Sofronas <esofronas@gmail.com>\n"
+"Language-Team: English <en@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "Ο διαχειÏιστής κλήσεων Asterisk δεν αποκÏίνεται"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "Η πιστοποίηση στο Asterisk απέτυχε:"
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+#, fuzzy
+msgid "Asterisk command not understood"
+msgstr "Η εντολή Asterisk επαναφόÏτωσης δεν αναγνωÏίστηκε"
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:226
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:173
+#, fuzzy
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "ΑδÏνατη η σÏνδεση στον Asterisk Manager"
+
+#: ../includes/common.php:174
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+#, fuzzy
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+"Ελέγχτε την εγκατάσταση του AMP, την βάση δεδομένων του asterisk, ή το ARI "
+"main.conf"
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr "ΑποσÏνδεση"
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr "Η σελίδα δεν βÏέθηκε"
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "ΕÏÏεση"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "ΕÏÏεση για"
+
+#: ../includes/display.php:139
+#, fuzzy, php-format
+msgid "Results %d - %d of %d"
+msgstr "Αποτελέσματα"
+
+#: ../includes/display.php:141
+#, fuzzy, php-format
+msgid "Results %d"
+msgstr "Αποτελέσματα"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "ΠÏώτο"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "Τελευταίο"
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "Λάθος Κωδικός"
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr "Λάθος όνομα χÏήστη ή κωδικός"
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr "ΘυÏίδα"
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr "Κωδικός"
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr "Είσοδος"
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr "Απομνημόνευση ΚωδικοÏ"
+
+#: ../includes/login.php:451
+#, fuzzy
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr "ΘυÏίδα Τηλεφωνητή και Κωδικό"
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr "Αυτό είναι ο ίδιος κωδικός που χÏησιμοποιήθηκε για το τηλέφωνο"
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+"Για αλλαγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï Î® υποστήÏιξη, επικοινωνήστε με τον ΔιαχειÏιστή του "
+"συστήματος"
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr "ΕΣΕΡΧΟΜΕÎΑ"
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr "ΟΙΚΟΓΕÎΕΙΑ"
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr "ΦΙΛΟΙ"
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr "ΠΑΛΙΑ"
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr "ΔΟΥΛΕΙΑ"
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr ""
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr ""
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr "ÎÏα"
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr ""
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr ""
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr ""
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:239
+#, fuzzy
+msgid "IVR Recording"
+msgstr "Μυνήματα ΘυÏίδας"
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:244
+msgid "Message Center (does not ask for extension)"
+msgstr ""
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr ""
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr ""
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr "ΠαÏακολοÏθηση Κλήσεων"
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr "διαγÏαφή"
+
+#: ../modules/callmonitor.module:147
+#, fuzzy
+msgid "duration"
+msgstr "ΔιάÏκεια"
+
+#: ../modules/callmonitor.module:150
+#, fuzzy
+msgid "ignore"
+msgstr "κανένα"
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr "ΗμεÏομηνία"
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr "Ταυτότητα ΚαλοÏντος"
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr "Πηγή"
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr "ΠÏοοÏισμός"
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr "ΠεÏιεχόμενο"
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr "ΔιάÏκεια"
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr "ΠαÏακολοÏθηση"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr "άκουσε"
+
+#: ../modules/callmonitor.module:259
+#, fuzzy, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "ΠαÏακολοÏθηση Κλήσεων"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr "επιλογή"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr "όλα"
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr "κανένα"
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr ""
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, fuzzy, php-format
+msgid "Conference for %s (%s%s)"
+msgstr "Τηλεφωνητής"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr ""
+
+#: ../modules/help.module:70
+#, fuzzy, php-format
+msgid "Help for %s (%s)"
+msgstr "Ρυθμίσεις για"
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr "Ρυθμίσεις"
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr "Ο κωδικός του τηλεφωνητή δεν άλλαξε"
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr "Ο κωδικός και η επιβεβαίωση ÎºÏ‰Î´Î¹ÎºÎ¿Ï Î´ÎµÎ½ Ï€Ïέπει να είναι κενά"
+
+#: ../modules/settings.module:157
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "Οι κωδικοί Ï€Ïέπει να είναι μόνο 4 αÏιθμοί"
+
+#: ../modules/settings.module:162
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "Οι κωδικοί Ï€Ïέπει να είναι μόνο 4 αÏιθμοί"
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr "Ο κωδικός και η επιβεβαίωση ÎºÏ‰Î´Î¹ÎºÎ¿Ï Î´ÎµÎ½ συμφωνοÏν"
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, fuzzy, php-format
+msgid "%s does not exist or is not writable"
+msgstr "Δεν υπάÏχει ή δεν είναι εγγÏάψιμο"
+
+#: ../modules/settings.module:223
+#, fuzzy
+msgid "Voicemail email and pager address not changed"
+msgstr "Ο κωδικός του τηλεφωνητή δεν άλλαξε"
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+#, fuzzy
+msgid "Voicemail email settings not changed"
+msgstr "Ο κωδικός του τηλεφωνητή δεν άλλαξε"
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr "Γλώσσα:"
+
+#: ../modules/settings.module:408
+#, fuzzy
+msgid "Call Routing"
+msgstr "Ρυθμίσεις ΠαÏακολοÏθησης Κλήσεων"
+
+#: ../modules/settings.module:411
+#, fuzzy
+msgid "Call Forwarding:"
+msgstr "Ρυθμίσεις ΠαÏακολοÏθησης Κλήσεων"
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+#, fuzzy
+msgid "Enable"
+msgstr "στο πεδίο"
+
+#: ../modules/settings.module:431
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "Οι κωδικοί Ï€Ïέπει να είναι μόνο 4 αÏιθμοί"
+
+#: ../modules/settings.module:434
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr "Οι κωδικοί Ï€Ïέπει να είναι μόνο 4 αÏιθμοί"
+
+#: ../modules/settings.module:439
+#, fuzzy
+msgid "Voicemail Password:"
+msgstr "Κωδικός Τηλεφωνητή"
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr "Εισάγετε ξανά για επιβεβαίωση:"
+
+#: ../modules/settings.module:492
+#, fuzzy
+msgid "Email Voicemail To:"
+msgstr "Τηλεφωνητής"
+
+#: ../modules/settings.module:498
+#, fuzzy
+msgid "Pager Voicemail To:"
+msgstr "Τηλεφωνητής"
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr "Ποιότητα Ήχου:"
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr "Μέγιστη Ποιότητα"
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr "ΜικÏότεÏο Download"
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr "Ρυθμίσεις Τηλεφωνητή"
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr "Ρυθμίσεις ΠαÏακολοÏθησης Κλήσεων"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr "ΗχογÏάφηση ΕΙΣΕΡΧΟΜΕÎΟΥ:"
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr "Πάντα"
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr "Ποτέ"
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr "Επιτόπου"
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr "ΗχογÏάφηση ΕΞΕΡΧΟΜΕÎΟΥ:"
+
+#: ../modules/settings.module:669
+#, fuzzy, php-format
+msgid "Settings for %s (%s)"
+msgstr "Ρυθμίσεις για"
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr "Ανανέωση"
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr "Τηλεφωνητής"
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr "ΠÏέπει να επιλεχθεί ένας κατάλογος Ï€Ïίν μεταφεÏεθεί το μÏνημα."
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr "ΠÏέπει να επιλεχθεί ΘυÏίδα παÏαλήπτη Ï€Ïίν Ï€Ïοωθηθεί το μÏνημα."
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr "μετακίνηση"
+
+#: ../modules/voicemail.module:307
+#, fuzzy
+msgid "Folder"
+msgstr "Κατάλογοι"
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr "Ï€Ïοώθηση"
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr "ΠÏοτεÏαιότητα"
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr "ΑÏχικός Κατάλογος Μυνημάτων"
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr "Δεν βÏέθηκαν εγγÏαφή(ές) στον τηλεφωνητή."
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+#, fuzzy
+msgid "Voicemail Login not found."
+msgstr "Δεν βÏέθηκε Ï€Ïόσβαση για θυÏίδα μυνημάτων"
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr "Καμία Ï€Ïόσβαση στον τηλεφωνητή"
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr "Δεν ΥπάÏχουν ΕγγÏαφές Μυνημάτων για τον ΔιαχειÏιστή"
+
+#: ../modules/voicemail.module:428
+#, fuzzy, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "Τηλεφωνητής"
+
+#: ../modules/voicemail.module:678
+#, fuzzy, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "ΑδÏνατη η δημιουÏγία καταλόγου μυνημάτων"
+
+#: ../modules/voicemail.module:718
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr ""
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr "κατέβασμα"
+
+#~ msgid "Passwords must be all numbers and only 4 digits"
+#~ msgstr "Οι κωδικοί Ï€Ïέπει να είναι μόνο 4 αÏιθμοί"
+
+#~ msgid "Folders"
+#~ msgstr "Κατάλογοι"
+
+#~ msgid "Login used"
+#~ msgstr "Όνομα χÏήστη που χÏησιμοποιήθηκε"
+
+#, fuzzy
+#~ msgid "No Asterisk Manager Interface connection"
+#~ msgstr "Ο διαχειÏιστής κλήσεων Asterisk δεν αποκÏίνεται"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "δεν είναι κατάλογος ή δεν είναι αναγνώσιμος"
+
+#~ msgid "of"
+#~ msgstr "από"
+
+#~ msgid "Use your"
+#~ msgstr "ΧÏησιμοποίησε την δικιά σου"
+
+#~ msgid "for"
+#~ msgstr "για"
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr "Ο κωδικός Ï€Ïέπει να έιναι 4 αÏιθμοί"
+
+#~ msgid "Check voicemail audio format on settings page to change from"
+#~ msgstr ""
+#~ "Ελέγχτε το audio format του μυνήματος στην σελίδα Ïυθμίσεων για αλλαγή"
+
+#~ msgid "on the server"
+#~ msgstr "στον server"
+
+#~ msgid "No database connection"
+#~ msgstr "Δεν υπάÏχει σÏνδεση με την βάση δεδομένων"
diff --git a/fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..e0fbdd9
--- /dev/null
+++ b/fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..0518573
--- /dev/null
+++ b/fs_selfservice/fri/locale/es_ES/LC_MESSAGES/ari.po
@@ -0,0 +1,616 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Grupo Ikusnet, Antonio F. Cano <antonio@igestec.com>, 2006.
+# Grupo Ikusnet, Agustin Vericat <agustin@igestec.com>, 2006.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: 2006-03-31 13:00\n"
+"Last-Translator: Antonio F. Cano <antonio@igestec.com>\n"
+"Language-Team: Espanol <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "La Centralita no responde"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "Fallo la Autenticacion con la Centralita"
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+msgid "Asterisk command not understood"
+msgstr "La recarga no funcino"
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr "Demasiados directorios en %s, notodos los archivos han sido procesados"
+
+#: ../includes/bootstrap.php:226
+#, fuzzy
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr "Necesita una versi&oacute; de PHP 4.0 o superior"
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+"PHP PEAR debe estar instalado. Visite http://pear.php.net para obtener ayuda"
+
+#: ../includes/common.php:173
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "No es posible conectar con la Centralita"
+
+#: ../includes/common.php:174
+#, fuzzy
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+"Compruebe el archivo 'main.conf' para configuar la conexi&oacute;n con "
+"Asterisk Manager"
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+"Compruebe /etc/asterisk/manager.conf para crear una cuenta Asterisk Manager"
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr "Compruebe la instalacion de FreePBX, DDBB o ARI en main.conf"
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr "Salir"
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr "Pagina No encontrada"
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "Buscar"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "Buscado para"
+
+#: ../includes/display.php:139
+#, fuzzy, php-format
+msgid "Results %d - %d of %d"
+msgstr "Resultados %d de %d"
+
+#: ../includes/display.php:141
+#, php-format
+msgid "Results %d"
+msgstr "Resultados %d"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "Primero"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "Ultimo"
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "Contrase&ntilde;a Incorrecta"
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr "Contrase&ntilde; Incorrecta"
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr "Usuario"
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr "Contrase&ntilde;a"
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr "Enviar"
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr "Recordar Contrase&ntilde;a"
+
+#: ../includes/login.php:451
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr "Use su Buz&oacute;n de Voz (Usuario) y contrase&ntilde;a"
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr "Esta es es la misma contrase&ntilde;a usada para el telefono"
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+"Para mantenimiento de contrase&ntilde;as o asistencia, pongase en contacto "
+"con el Administrador."
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr "Entrada"
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr "Familiares"
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr "Amigos"
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr "Antiguos"
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr "Trabajo"
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr "Directorio"
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr "Test Eco"
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr "Hora"
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr "Tiempo"
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr "Programar llamada despertador"
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr "Test festival tts (su extension es XXX)"
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr "Activar Llamada en Espera (Desactivada por defecto)"
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr "Desactivar Llamada en Espera"
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr "Desv&iacute;o de llamada"
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr "Desactivar el Desv&iacute;o de Llamada"
+
+#: ../includes/main.conf.php:239
+msgid "IVR Recording"
+msgstr "Grabaciones"
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr "Activar No-Molestar"
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr "Desactivar No-Molestar"
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr "Desv&iacute;o de llamada cuando est&eacute; Ocupado"
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr "Desactivar el Desv&iacute;o de llamada cuando est&eacute; Ocupado"
+
+#: ../includes/main.conf.php:244
+#, fuzzy
+msgid "Message Center (does not ask for extension)"
+msgstr "Centro de Mensajes (no pregunta la extens&iacute;n)"
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr "Entrar en el Centro de Mensajes"
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr "Escuchar la grabaci&oacute;n realizada"
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr "Probar Fax"
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr "Simular una llamada entrante"
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr "Adjuntar el mensaje de voz en el correo electr&oacute;nico"
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+"Indica el CallerID en la grabaci&oacute;n enviada por correo electr&oacute;"
+"nico"
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+"Indica la etiqueta (tiempo/hora) en la grabaci&oacute;n enviada por correo "
+"electr&oacute;nico"
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+"Eliminar el mensaje de voz una vez enviado por correo electr&oacute;nico"
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr "Reproducir el siguiente mensaje una vez eliminado el actual"
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr "Registro de Llamadas"
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr "La ruta no es un directorio: %s"
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr "Eliminar"
+
+#: ../modules/callmonitor.module:147
+msgid "duration"
+msgstr "Duraci&oacute;n"
+
+#: ../modules/callmonitor.module:150
+msgid "ignore"
+msgstr "ninguno"
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr "Fecha"
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr "Caller ID"
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr "Origen"
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr "Destino"
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr "Contexto"
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr "Duraci&oacute;n"
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr "Monitor para"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr "escuchar"
+
+#: ../modules/callmonitor.module:259
+#, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Registro de Llamadas de %s (%s)"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr "Selecionar"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr "todos"
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr "ninguno"
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr "Solo elimina los archivos grabados, no el log en el CDR"
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, fuzzy, php-format
+msgid "Conference for %s (%s%s)"
+msgstr "Buz&oacute;n de Voz de %s (%s)"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr "Ayuda"
+
+#: ../modules/help.module:70
+#, php-format
+msgid "Help for %s (%s)"
+msgstr "Ayuda para %s (%s)"
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr "Teclas de Marcaci&oacute;n"
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr "Acci&oacute;n"
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr "Opciones"
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr "El n&uacute;mero del desv&iacute;o no ha cambiado"
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+"El n&uacute;mero %s debe contener n&uacte;meros marcables (caracteres como "
+"'(', '-', y ')' son v&aacute;lidos)"
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr "La Contrase&ntilde;a del Buz&oacute;n de Voz no ha cambiado"
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr ""
+"Contrase&ntilde;a y la confirmacion de esta no deben de estar en blanco"
+
+#: ../modules/settings.module:157
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "Contrase&ntilde;a ha de ser numerica y de longitud %d digitos"
+
+#: ../modules/settings.module:162
+#, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "Contrase&ntilde;a ha de ser numerica y de longitud %d digitos"
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr "Contrase&ntilde;a y conformacion no corresponden"
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, php-format
+msgid "%s does not exist or is not writable"
+msgstr "%s No existe o no se puede escribir"
+
+#: ../modules/settings.module:223
+msgid "Voicemail email and pager address not changed"
+msgstr "La Contrase&ntilde;a del Buz&oacute;n de Voz no ha cambiado"
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+msgid "Voicemail email settings not changed"
+msgstr "La Contrase&ntilde;a del Buz&oacute;n de Voz no ha cambiado"
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr "Idioma:"
+
+#: ../modules/settings.module:408
+msgid "Call Routing"
+msgstr "Enrutado de llamadas"
+
+#: ../modules/settings.module:411
+msgid "Call Forwarding:"
+msgstr "Desviar llamadas a:"
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+msgid "Enable"
+msgstr "Activar"
+
+#: ../modules/settings.module:431
+#, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "Contrase&ntilde;a ha de ser numerica y de longitud %s digitos"
+
+#: ../modules/settings.module:434
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr "Contrase&ntilde;a ha de ser numerica y de longitud %s digitos"
+
+#: ../modules/settings.module:439
+msgid "Voicemail Password:"
+msgstr "Contrase&ntilde;a del Buz&oacute;n de Voz"
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr "Introduzca otra vez para confirmar:"
+
+#: ../modules/settings.module:492
+msgid "Email Voicemail To:"
+msgstr "Buz&oacute;n de Voz para"
+
+#: ../modules/settings.module:498
+msgid "Pager Voicemail To:"
+msgstr "Buz&oacute;n de Voz para"
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr "Formato del Audio:"
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr "Mejor Calidad"
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr "Descarga rapida"
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr "Propiedades del Buz&oacute;n de Voz"
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr "Propiedades del Registro de Llamadas"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr "Grabaciones Entrantes:"
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr "Siempre"
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr "Nunca"
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr "Bajo demanda"
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr "Grabaciones Salientes:"
+
+#: ../modules/settings.module:669
+#, php-format
+msgid "Settings for %s (%s)"
+msgstr "Ajustes para %s (%s)"
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr "Actualizar"
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr "Buz&oacute;n de Voz"
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr "Debe elegir primero una carpeta antes de mover el mensaje."
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr "Debe de seleccionar una extension antes de reenviar el mensaje"
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr "Mover a"
+
+#: ../modules/voicemail.module:307
+msgid "Folder"
+msgstr "Carpetas"
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr "Enviar a"
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr "Prioridad"
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr "Buz&oacute;n de Voz Orig"
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr "Mensaje"
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr "No se ha encontrado grabaciones en el Buz&oacute;n de Voz."
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+msgid "Voicemail Login not found."
+msgstr ""
+"No se encontro el usuario del Buz&oacute;n de Voz, se usa el usuario de la "
+"extension"
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr "No tiene permiso para acceder al Buz&oacute;n de Voz"
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr "No hay grabaciones en el Buz&oacute;n de Voz de Admin"
+
+#: ../modules/voicemail.module:428
+#, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "Buz&oacute;n de Voz de %s (%s)"
+
+#: ../modules/voicemail.module:678
+#, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "No puedo crear la carpeta %s en el buz&oacute;n de voz"
+
+#: ../modules/voicemail.module:718
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr "Permiso denegado en el directorio %s o %s"
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr "Descargar"
+
+#~ msgid "Settings for"
+#~ msgstr "Configuracion de"
+
+#~ msgid "Folders"
+#~ msgstr "Carpetas"
diff --git a/fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..78d4733
--- /dev/null
+++ b/fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..7f15c7a
--- /dev/null
+++ b/fs_selfservice/fri/locale/fr_FR/LC_MESSAGES/ari.po
@@ -0,0 +1,635 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <jbp@phileas-com.net>, 15/11/2005.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: 2006-04-29 11:30+0100\n"
+"Last-Translator: Xavier Ourcière <xourciere@propolys.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "Asterisk Call Manager ne répond pas"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "Authentification Asterisk échoue :"
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+msgid "Asterisk command not understood"
+msgstr "Asterisk: commande non comprise"
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:226
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:173
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "Connexion impossible à Asterisk Manager"
+
+#: ../includes/common.php:174
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+"Vérifiez l'installation d'AMP, de Asterisk, ou le fichier ARI main.conf"
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr "Déconexion"
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr "Fichier introuvable"
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "Rechercher"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "Rechercher pour"
+
+#: ../includes/display.php:139
+#, php-format
+msgid "Results %d - %d of %d"
+msgstr "Résultats %d à %s sur %d"
+
+#: ../includes/display.php:141
+#, php-format
+msgid "Results %d"
+msgstr "Résultats %d"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "Premier"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "Dernier"
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "Mot de Passe eronné"
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr "Login ou Mot de Passe erroné"
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr "Authentification"
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr "Mot de Passe"
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr "Valider"
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr "Se souvenir du mot de passe"
+
+#: ../includes/login.php:451
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr "Utilisez votre <b>numéro de la boîte vocale et votre mot de passe</b>"
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr "C'est le même Mot de Passe que sur le téléphone"
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr "Pour de l'assistance contactez votre administrateur de téléphonie."
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr "NOUVEAUX"
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr "Famille"
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr "Amis"
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr "Anciens"
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr "Travail"
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr "Annuaire local"
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr "Test d'echo"
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr "Heure"
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr "Météo"
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr "Programmation de réveil"
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr "test de festival (votre numéro de téléphone est le XXXX)"
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:239
+#, fuzzy
+msgid "IVR Recording"
+msgstr "Enregistrement"
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr "Active ne pas déranger"
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr "Désactive ne pas déranger"
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:244
+#, fuzzy
+msgid "Message Center (does not ask for extension)"
+msgstr "Boite vocale personnelle"
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr "Centre de messageries"
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr "Simulation d'appel entrant"
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr "Journal d'Appels"
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr "Supprimer"
+
+#: ../modules/callmonitor.module:147
+msgid "duration"
+msgstr "Durée supérieure à"
+
+#: ../modules/callmonitor.module:150
+msgid "ignore"
+msgstr "Filtrer"
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr "Date"
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr "ID Appelant"
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr ""
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr ""
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr "Contexte"
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr "Durée"
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr "Enregistrement"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr "Ecouter"
+
+#: ../modules/callmonitor.module:259
+#, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Journal d'Appels de %s (%s)"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr "Sélection"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr "Tous"
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr "Aucun"
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr "Supprime seulement les fichiers des enregistrements mais pas les CDRs"
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, fuzzy, php-format
+msgid "Conference for %s (%s%s)"
+msgstr "Boîte Vocale de %s (%s)"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr "Aide"
+
+#: ../modules/help.module:70
+#, php-format
+msgid "Help for %s (%s)"
+msgstr "Aide: %s (%s)"
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr "Paramètres"
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr "Mot de passe de boite vocale non changé"
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr "Le mot de passe et sa confirmation ne peuvent pas être vides"
+
+#: ../modules/settings.module:157
+#, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr ""
+"Le mot de passe doit comporter uniquement des chiffres et doit avoir une "
+"longueur supérieure à %d"
+
+#: ../modules/settings.module:162
+#, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr ""
+"Le mot de passe doit comporter uniquement des chiffres et doit avoir une "
+"longueur de %d"
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr ""
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, php-format
+msgid "%s does not exist or is not writable"
+msgstr "%s n'existe pas ou n'a pas l'autorisation en écriture"
+
+#: ../modules/settings.module:223
+msgid "Voicemail email and pager address not changed"
+msgstr "Email voicemail et adresse de pager inchangés"
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+msgid "Voicemail email settings not changed"
+msgstr "Paramètres de la boite vocale inchangés"
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr "Langue"
+
+#: ../modules/settings.module:408
+msgid "Call Routing"
+msgstr "Routage d'appels"
+
+#: ../modules/settings.module:411
+msgid "Call Forwarding:"
+msgstr "Transfert vers:"
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+msgid "Enable"
+msgstr "Activer"
+
+#: ../modules/settings.module:431
+#, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr ""
+"Le mot de passe doit comporter uniquement des chiffres et seulement 4 "
+"chiffres"
+
+#: ../modules/settings.module:434
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr ""
+"Le mot de passe doit comporter uniquement des chiffres et seulement 4 "
+"chiffres"
+
+#: ../modules/settings.module:439
+msgid "Voicemail Password:"
+msgstr "Mot de passe de la boîte vocale"
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr "Repetez le mot de passe:"
+
+#: ../modules/settings.module:492
+msgid "Email Voicemail To:"
+msgstr "Adresse émail pour le Voicemail:"
+
+#: ../modules/settings.module:498
+msgid "Pager Voicemail To:"
+msgstr ""
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr "Format audio:"
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr "Meilleure Qualité"
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr "Taille réduite"
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr "Paramètres boîte vocale"
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr "Enregistrements d'appels"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr "Enregistrements ENTRANTS"
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr "Toujours"
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr "Jamais"
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr "Sur demande"
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr "Enregistrements SORTANTS"
+
+#: ../modules/settings.module:669
+#, php-format
+msgid "Settings for %s (%s)"
+msgstr "Paramètres de %s (%s)"
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr "Mettre à jour"
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr "Boîte Vocale"
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr "Sélection un dossier avant de déplacer le message."
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr "Sélectionnez d'abord une extension pour le transfert du message."
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr "Déplacer vers"
+
+#: ../modules/voicemail.module:307
+msgid "Folder"
+msgstr "Dossier"
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr "Transmettre à"
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr "Priorité"
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr "Boîte Source"
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr "Enregistrement audio non trouvé"
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+#, fuzzy
+msgid "Voicemail Login not found."
+msgstr "Enregistrement audio non trouvé"
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr "Aucun accès à la boîte vocale"
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr "Pas d'enregistrement pour Admin"
+
+#: ../modules/voicemail.module:428
+#, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "Boîte Vocale de %s (%s)"
+
+#: ../modules/voicemail.module:678
+#, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "N'a pas pu créer le dossier %s"
+
+#: ../modules/voicemail.module:718
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr ""
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr ""
+
+#~ msgid "Passwords must be all numbers and only 4 digits"
+#~ msgstr ""
+#~ "Le mot de passe doit comporter que des chiffres et 4 chiffres maximum"
+
+#, fuzzy
+#~ msgid "No Asterisk Manager Interface connection"
+#~ msgstr "Asterisk Call Manager ne répond pas"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "pas un répertoire ou non lisible"
+
+#~ msgid "of"
+#~ msgstr "de"
+
+#~ msgid "Use your"
+#~ msgstr "Utilisez votre"
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr ""
+#~ "Le mot de passe doit comporter que des chiffres et 4 chiffres maximum"
+
+#~ msgid "Check voicemail audio format on settings page to change from"
+#~ msgstr "Vérifiez le format audio à la page paramètres"
+
+#~ msgid "on the server"
+#~ msgstr "sur le serveur"
+
+#~ msgid "No database connection"
+#~ msgstr "Pas de connexion à la base de données"
+
+#~ msgid "Format Audio:"
+#~ msgstr "Format Audio :"
diff --git a/fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..3b00bd1
--- /dev/null
+++ b/fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..7c9ae97
--- /dev/null
+++ b/fs_selfservice/fri/locale/he_IL/LC_MESSAGES/ari.po
@@ -0,0 +1,646 @@
+# translation of ari-he.po to Hebrew
+# This file is distributed under the same license as the PACKAGE package.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER.
+# Diego Iastrubni <diego.iastrubni@xorcom.com>, 2006.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ari-he\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: 2006-02-05 11:48+0200\n"
+"Last-Translator: Diego Iastrubni <diego.iastrubni@xorcom.com>\n"
+"Language-Team: Hebrew <xorcom-users@xorcom.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.9.1\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "מנהל השיחות של Asterisk ×œ× ×ž×’×™×‘"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "×”×ימות מול Asterisk נכשל:"
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+#, fuzzy
+msgid "Asterisk command not understood"
+msgstr "פקודת reload של Asterisk ×œ× ×ž×•×‘× ×ª"
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:226
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:173
+#, fuzzy
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "×ין ×פשרות להתחבר למנהל של Asterisk"
+
+#: ../includes/common.php:174
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+#, fuzzy
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+"בדוק ×ת ההתקנה של AMP, בסיס ×”× ×ª×•× ×™× ×©×œ asterisk ×ו הקובץ main.conf של ARI"
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr "יצי××”"
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr "דף ×œ× × ×ž×¦×"
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "חפש"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "חיפוש של"
+
+#: ../includes/display.php:139
+#, fuzzy, php-format
+msgid "Results %d - %d of %d"
+msgstr "תוצ×ות"
+
+#: ../includes/display.php:141
+#, fuzzy, php-format
+msgid "Results %d"
+msgstr "תוצ×ות"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "ר×שון"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "×חרון"
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "ססמה ×œ× × ×›×•× ×”"
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr "×©× ×ž×©×ª×ž×© ×œ× × ×›×•×Ÿ ×ו ססמה ×œ× × ×›×•× ×”"
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr "×©× ×ž×©×ª×ž×©"
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr "ססמה"
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr "שלח"
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr "זכור ססמה"
+
+#: ../includes/login.php:451
+#, fuzzy
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr "תיבה קולית וססמה"
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr "זוהי ×ותה ססמה שבשימוש בטלפון שלך"
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr "עבור ססמה התחזוקה, ×× × ×¤× ×” ×ל מנהל הטלפוניה שלך."
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr "נכנסות"
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr "משפחה"
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr "חברי×"
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr "ישני×"
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr "עבודה"
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr ""
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr ""
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr "שעה"
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr ""
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr ""
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr ""
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:239
+#, fuzzy
+msgid "IVR Recording"
+msgstr "הקלטות"
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:244
+msgid "Message Center (does not ask for extension)"
+msgstr ""
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr ""
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr ""
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr "צג שיחות"
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+#, fuzzy
+msgid "delete"
+msgstr "בחר"
+
+#: ../modules/callmonitor.module:147
+#, fuzzy
+msgid "duration"
+msgstr "משך"
+
+#: ../modules/callmonitor.module:150
+#, fuzzy
+msgid "ignore"
+msgstr "כלו×"
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr "ת×ריך"
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr "שיחה מזוהה"
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr "מקור"
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr "יעד"
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr "הקשר"
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr "משך"
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr "ניטור"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr "נגן"
+
+#: ../modules/callmonitor.module:259
+#, fuzzy, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "צג שיחות"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr "בחר"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr "הכל"
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr "כלו×"
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr "מחק הקלטות בלבד, ×œ× ×ת ×¨×™×©×•× ×”Ö¾cdr"
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, fuzzy, php-format
+msgid "Conference for %s (%s%s)"
+msgstr "תיבה קולית"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr ""
+
+#: ../modules/help.module:70
+#, fuzzy, php-format
+msgid "Help for %s (%s)"
+msgstr "הגדרות עבור"
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr "הגדרות"
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr "ססמת התיבה הקולית ×œ× ×©×•× ×ª×”"
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr "הסממה וה×ימות של הססמה ×œ× ×™×›×•×œ×™× ×œ×”×™×•×ª רקי×"
+
+#: ../modules/settings.module:157
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "הסממ×ות חייבת להכיל 4 ספרות בלבד"
+
+#: ../modules/settings.module:162
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "הסממ×ות חייבת להכיל 4 ספרות בלבד"
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr "הסממה וה×ימות של הססמה ×œ× ×ª×•×מי×"
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, fuzzy, php-format
+msgid "%s does not exist or is not writable"
+msgstr "×œ× ×§×™×™× ×ו ×ין ×פשרות לכתוב עליו"
+
+#: ../modules/settings.module:223
+#, fuzzy
+msgid "Voicemail email and pager address not changed"
+msgstr "ססמת התיבה הקולית ×œ× ×©×•× ×ª×”"
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+#, fuzzy
+msgid "Voicemail email settings not changed"
+msgstr "ססמת התיבה הקולית ×œ× ×©×•× ×ª×”"
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr "שפה:"
+
+#: ../modules/settings.module:408
+#, fuzzy
+msgid "Call Routing"
+msgstr "הגדרות ניתור שיחות"
+
+#: ../modules/settings.module:411
+#, fuzzy
+msgid "Call Forwarding:"
+msgstr "הגדרות ניתור שיחות"
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+#, fuzzy
+msgid "Enable"
+msgstr "בטבלה"
+
+#: ../modules/settings.module:431
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "הסממ×ות חייבת להכיל 4 ספרות בלבד"
+
+#: ../modules/settings.module:434
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr "הסממ×ות חייבת להכיל 4 ספרות בלבד"
+
+#: ../modules/settings.module:439
+#, fuzzy
+msgid "Voicemail Password:"
+msgstr "ססמת תיבה קולית:"
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr "הכנס שוב ל×ימות:"
+
+#: ../modules/settings.module:492
+#, fuzzy
+msgid "Email Voicemail To:"
+msgstr "תיבה קולית"
+
+#: ../modules/settings.module:498
+#, fuzzy
+msgid "Pager Voicemail To:"
+msgstr "תיבה קולית"
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr "תבנית שמע:"
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr "×יכות ×”×›×™ טובה"
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr "הורדה הכי קטנה"
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr "הגדרות תיבה קולית"
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr "הגדרות ניתור שיחות"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr "הקלטת שיחות נכנסות:"
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr "תמיד"
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr "××£ פע×"
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr "לפי דרישה"
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr "הקלטה שיחות יוצ×ות:"
+
+#: ../modules/settings.module:669
+#, fuzzy, php-format
+msgid "Settings for %s (%s)"
+msgstr "הגדרות עבור"
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr "עדכן"
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr "תיבה קולית"
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr "יש לבחור תיקייה לפני ש×פשר להעביר ×ת ההודעה."
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr "יש לבחור שלוחה לפני ש×פשר העביר ×ת השיחה הל××”."
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr ""
+
+#: ../modules/voicemail.module:307
+msgid "Folder"
+msgstr "תיקייה"
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr ""
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr "עדיפות"
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr "תיבת דו×ר מקורית"
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr "הקלטת תיבה קולית ×œ× × ×ž×¦××”."
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+#, fuzzy
+msgid "Voicemail Login not found."
+msgstr "×©× ×”×ž×©×ª×ž×© של תיבת הקול"
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr "×ין גישה לתיבת הקול"
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr "×ין הקלטות בתיבת הקול של המנהל"
+
+#: ../modules/voicemail.module:428
+#, fuzzy, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "תיבה קולית"
+
+#: ../modules/voicemail.module:678
+#, fuzzy, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "×ין ×פשרות ליצור ×ת תיקיית הדו×ר"
+
+#: ../modules/voicemail.module:718
+#, fuzzy, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr "הגישה נדחתה בתיקייה"
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr "הורדה"
+
+#~ msgid "Passwords must be all numbers and only 4 digits"
+#~ msgstr "הסממ×ות חייבת להכיל 4 ספרות בלבד"
+
+#~ msgid "Folders"
+#~ msgstr "תיקיות"
+
+#~ msgid "Login used"
+#~ msgstr "×©× ×”×©×ž×©×ª×©"
+
+#, fuzzy
+#~ msgid "No Asterisk Manager Interface connection"
+#~ msgstr "מנהל השיחות של Asterisk ×œ× ×ž×’×™×‘"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "×œ× ×¡×¤×¨×™×™×”, ×ו ×ין ×פשרות לקר×"
+
+#~ msgid "of"
+#~ msgstr "של "
+
+#~ msgid "Use your"
+#~ msgstr "השתמש בשלך"
+
+#~ msgid "for"
+#~ msgstr "עבור"
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr "הסממה חייבת להכיל 4 ספרות בלבד"
+
+#, fuzzy
+#~ msgid "Check voicemail audio format on settings page to change from"
+#~ msgstr "בחר ×ת תבנית השמע של התיבה הקולית בחלון ההגדרות "
+
+#~ msgid "on the server"
+#~ msgstr "ברשת"
+
+#~ msgid "No database connection"
+#~ msgstr "×ין חיבור לבסיס נתוני×"
diff --git a/fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..ff5a922
--- /dev/null
+++ b/fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..c9d9e44
--- /dev/null
+++ b/fs_selfservice/fri/locale/hu_HU/LC_MESSAGES/ari.po
@@ -0,0 +1,645 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Varasdy Imre <csvarasdy@softpbx.hu>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "Asterisk Call Manager nem válaszol"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "Asterisk bejelentkezés elutasítva:"
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+#, fuzzy
+msgid "Asterisk command not understood"
+msgstr "Asterisk frissítés parancs ismeretlen"
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:226
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:173
+#, fuzzy
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "Nem tudok csatlakozni az Asterisk Managerhez"
+
+#: ../includes/common.php:174
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+#, fuzzy
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+"Ellen&otilde;rizze az AMP telepítést, asterisk adatbázist, vagy az ARI main."
+"conf filet"
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr "Kilépés"
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr "Nincs ilyen oldal."
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "Keres"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "Keresés"
+
+#: ../includes/display.php:139
+#, fuzzy, php-format
+msgid "Results %d - %d of %d"
+msgstr "Eredmény"
+
+#: ../includes/display.php:141
+#, fuzzy, php-format
+msgid "Results %d"
+msgstr "Eredmény"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "Els&otilde;"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "Utolsó"
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "Hibás jelszó"
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr "Rossz Felhasználonév vagy jelszó"
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr "Azonosító"
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr "Jelszó"
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr "Rögzít"
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr "Jelszó megjegyzése"
+
+#: ../includes/login.php:451
+#, fuzzy
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr "Hangposta és Jelszó"
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr "A jelszó ugyanaz, mint a telefonhoz"
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+"Om du har problem med lösenord eller behöver hjälp ska du kontakta din vÃ"
+"¤xel ansvarig"
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr "Bejövõ"
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr "Család"
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr "Barátok"
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr "Régi"
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr "Munka"
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr ""
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr ""
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr "Idõ"
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr ""
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr ""
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr ""
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:239
+#, fuzzy
+msgid "IVR Recording"
+msgstr "Felvétel"
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:244
+msgid "Message Center (does not ask for extension)"
+msgstr ""
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr ""
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr ""
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr "Hangrögzítés"
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr "Töröl"
+
+#: ../modules/callmonitor.module:147
+#, fuzzy
+msgid "duration"
+msgstr "Hossz"
+
+#: ../modules/callmonitor.module:150
+#, fuzzy
+msgid "ignore"
+msgstr "semmi"
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr "Dátum"
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr "Hivószám"
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr "Hívó"
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr "Hívott"
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr "Csoport"
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr "Hossz"
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr "Rögzítés"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr "Lejátszás"
+
+#: ../modules/callmonitor.module:259
+#, fuzzy, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Hangrögzítés"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr "Választ"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr "Mind"
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr "semmi"
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr "Csak a hangfileokat törli, a CDR-t nem"
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, fuzzy, php-format
+msgid "Conference for %s (%s%s)"
+msgstr "Hangposta"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr ""
+
+#: ../modules/help.module:70
+#, fuzzy, php-format
+msgid "Help for %s (%s)"
+msgstr "Beállítások"
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr "Beállítások"
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr "A jelszót nem változtattam meg"
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr "A jelszavakat nem hagyhatja üresen"
+
+#: ../modules/settings.module:157
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "A jelszó csak számból állhat és csak 4 karakteres lehet"
+
+#: ../modules/settings.module:162
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "A jelszó csak számból állhat és csak 4 karakteres lehet"
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr "A két jelszó nem egyezik"
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, fuzzy, php-format
+msgid "%s does not exist or is not writable"
+msgstr "nem létezik vagy nem írható"
+
+#: ../modules/settings.module:223
+#, fuzzy
+msgid "Voicemail email and pager address not changed"
+msgstr "A jelszót nem változtattam meg"
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+#, fuzzy
+msgid "Voicemail email settings not changed"
+msgstr "A jelszót nem változtattam meg"
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr "Nyelv"
+
+#: ../modules/settings.module:408
+#, fuzzy
+msgid "Call Routing"
+msgstr "Hangrögzítés beállításai"
+
+#: ../modules/settings.module:411
+#, fuzzy
+msgid "Call Forwarding:"
+msgstr "Hangrögzítés beállításai"
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+#, fuzzy
+msgid "Enable"
+msgstr "táblában"
+
+#: ../modules/settings.module:431
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "A jelszó csak számból állhat és csak 4 karakteres lehet"
+
+#: ../modules/settings.module:434
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr "A jelszó csak számból állhat és csak 4 karakteres lehet"
+
+#: ../modules/settings.module:439
+#, fuzzy
+msgid "Voicemail Password:"
+msgstr "Hangposta jelszó:"
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr "Irja be újra:"
+
+#: ../modules/settings.module:492
+#, fuzzy
+msgid "Email Voicemail To:"
+msgstr "Hangposta"
+
+#: ../modules/settings.module:498
+#, fuzzy
+msgid "Pager Voicemail To:"
+msgstr "Hangposta"
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr "Hangformátum:"
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr "Legjobb minõség"
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr "Legkisebb méret"
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr "Hangposta beállítások"
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr "Hangrögzítés beállításai"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr "Hangrögzítés - Bejövõ:"
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr "Mindíg"
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr "Soha"
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr "Igény esetén"
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr "Hangrögzítés - Bejövõ:"
+
+#: ../modules/settings.module:669
+#, fuzzy, php-format
+msgid "Settings for %s (%s)"
+msgstr "Beállítások"
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr "Frissít"
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr "Hangposta"
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr "Ãthelyezés elõtt ki kell jelölni egy mappát."
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr "Üzenet áthelyezése elõtt ki kell jelölni egy melléket."
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr "Ãthelyez"
+
+#: ../modules/voicemail.module:307
+#, fuzzy
+msgid "Folder"
+msgstr "Mappa"
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr "Ãtirányít"
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr "Prioritás"
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr "Eredeti Postafiók"
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr "Nem találok rögzítés(eke)t."
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+#, fuzzy
+msgid "Voicemail Login not found."
+msgstr "Hangposta Azonosító nem található"
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr "Nincs hozzáférés a hangpostához"
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr "Nincs hangfelvétel az Admin részére"
+
+#: ../modules/voicemail.module:428
+#, fuzzy, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "Hangposta"
+
+#: ../modules/voicemail.module:678
+#, fuzzy, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "Nem tudom létrehozni a hangposta mappát"
+
+#: ../modules/voicemail.module:718
+#, fuzzy, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr "Hozzáférés elutasítva"
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr "letöltés"
+
+#~ msgid "Passwords must be all numbers and only 4 digits"
+#~ msgstr "A jelszó csak számból állhat és csak 4 karakteres lehet"
+
+#~ msgid "Folders"
+#~ msgstr "Mappák"
+
+#~ msgid "Login used"
+#~ msgstr "Azonosító használt"
+
+#, fuzzy
+#~ msgid "No Asterisk Manager Interface connection"
+#~ msgstr "Asterisk Call Manager nem válaszol"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "nem k&ouml;nyvtár vagy nem olvasható"
+
+#~ msgid "of"
+#~ msgstr "av"
+
+#~ msgid "Use your"
+#~ msgstr "Használja "
+
+#~ msgid "for"
+#~ msgstr " - "
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr "A jelszó csak számból állhat és 4 karakteres lehet"
+
+#~ msgid "on the server"
+#~ msgstr "a serveren"
+
+#~ msgid "No database connection"
+#~ msgstr "Nincs adatbázis kapcsolat"
diff --git a/fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..d5a7da8
--- /dev/null
+++ b/fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..db245f9
--- /dev/null
+++ b/fs_selfservice/fri/locale/it_IT/LC_MESSAGES/ari.po
@@ -0,0 +1,999 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: 1.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-08-24 12:33+0200\n"
+"PO-Revision-Date: 2007-08-25 22:41-0600\n"
+"Last-Translator: Francesco Romano\n"
+"Language-Team: Italian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "Il Call Manager di Asterisk non risponde"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "Autenticazione Asterisk fallita:"
+
+#
+#: ../includes/asi.php:96 ../includes/asi.php:111 ../includes/asi.php:130
+#: ../includes/asi.php:144
+msgid "Asterisk command not understood"
+msgstr "comando reload di Asterisk non eseguito"
+
+#
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr "Troppe directory in %s Non tutti i files sono stati processati"
+
+#: ../includes/bootstrap.php:226
+#, fuzzy
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr "ARI richiede PHP 4.0 o superiore"
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+"PHP PEAR deve essere installato. Visitare http://pear.php.net per aiuto "
+"nell'installazione."
+
+#
+#: ../includes/common.php:180
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "Impossibile connettersi all'Asterisk Manager"
+
+#: ../includes/common.php:181
+##, fuzzy
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+"Controllare il file di configurazione main.conf di ARI per l'impostazione "
+"sull'account dell'Asterisk Manager."
+
+#: ../includes/common.php:182
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+"Controllare /etc/asterisk/manager.conf per un valido account Asterisk Manager"
+
+#: ../includes/common.php:183
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+"assicurarsi che in [general] sia presente enable = yes e la riga 'permit=' "
+"con l'indirizzo localhost o il webserver."
+
+#
+#: ../includes/common.php:200 ../includes/common.php:215
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+"Controllare l'installazione di AMP, il database di asterisk o il file main."
+"conf di ARI"
+
+#: ../includes/common.php:351
+msgid "Logout"
+msgstr "Esci"
+
+#: ../includes/common.php:356
+msgid "Page Not Found."
+msgstr "Pagina Non Trovata"
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "Cerca"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "Ricerca per"
+
+#
+#: ../includes/display.php:139
+##, fuzzy, php-format
+msgid "Results %d - %d of %d"
+msgstr "Risultati %d - %d di %d"
+
+#
+#: ../includes/display.php:141
+#, php-format
+msgid "Results %d"
+msgstr "Risultati %d"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "Prima"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "Ultima"
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "Password sbagliata"
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr "Nome Utente o Password sbagliati"
+
+#: ../includes/login.php:404 ../includes/login.php:413
+msgid "Login"
+msgstr "Login"
+
+#: ../includes/login.php:421
+msgid "Password"
+msgstr "Password"
+
+#: ../includes/login.php:430
+msgid "Submit"
+msgstr "Invia"
+
+#: ../includes/login.php:438
+msgid "Remember Password"
+msgstr "Ricorda Password"
+
+#
+#: ../includes/login.php:453
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr ""
+"Utilizzare come login il numero della <b>Casella Vocale e relativa "
+"Password</b>"
+
+#: ../includes/login.php:454
+msgid "This is the same password used for the phone"
+msgstr "Sono gli stessi utilizzati dal proprio telefono"
+
+#: ../includes/login.php:456
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+"Per assistenza o manutenzione, contattare l'amministratore del Centralino."
+
+#: ../includes/main.conf.php:150
+msgid "INBOX"
+msgstr "NUOVI"
+
+#: ../includes/main.conf.php:152
+msgid "Family"
+msgstr "Personali"
+
+#: ../includes/main.conf.php:154
+msgid "Friends"
+msgstr "Amici"
+
+#: ../includes/main.conf.php:156
+msgid "Old"
+msgstr "Vecchi"
+
+#: ../includes/main.conf.php:158
+msgid "Work"
+msgstr "Lavoro"
+
+#: ../includes/main.conf.php:237
+msgid "Call Forward All Activate"
+msgstr "Attivazione Trasferimento di Chiamata Incondizionato"
+
+#: ../includes/main.conf.php:238
+msgid "Call Forward All Deactivate"
+msgstr "Disattivazione Trasferimento di Chiamata Incondizionato"
+
+#: ../includes/main.conf.php:239
+msgid "Call Forward All Prompting Deactivate"
+msgstr "Disattivazione Trasferimento di Chiamata Incondizionato (chiede dettagli)"
+
+#: ../includes/main.conf.php:240
+msgid "Call Forward Busy Activate"
+msgstr "Attivazione Trasferimento di Chiamata su Occupato"
+
+#: ../includes/main.conf.php:241
+msgid "Call Forward Busy Deactivate"
+msgstr "Disattivazione Trasferimento di Chiamata su Occupato"
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward Busy Prompting Deactivate"
+msgstr "Disattivazione Trasferimento di Chiamata su Occupato (chiede dettagli)"
+
+#: ../includes/main.conf.php:243
+msgid "Call Forward No Answer/Unavailable Activate"
+msgstr "Attivazione Trasferimento di Chiamata su nessuna risposta"
+
+#: ../includes/main.conf.php:244
+msgid "Call Forward No Answer/Unavailable Deactivate"
+msgstr "Disattivazione Trasferimento di Chiamata su nessuna risposta"
+
+#: ../includes/main.conf.php:245
+msgid "Call Waiting - Activate"
+msgstr "Attivazione Avviso di chiamata"
+
+#: ../includes/main.conf.php:247
+msgid "Do-Not-Disturb Activate"
+msgstr "Attivazione Non-Disturbare"
+
+#: ../includes/main.conf.php:248
+msgid "Do-Not-Disturb Deactivate"
+msgstr "Disattivazione Non-Disturbare"
+
+#: ../includes/main.conf.php:249
+msgid "My Voicemail"
+msgstr "Propria Casella Vocale"
+
+#: ../includes/main.conf.php:250
+msgid "Dial Voicemail"
+msgstr "Casella Vocale"
+
+#: ../includes/main.conf.php:303
+msgid "Email voicemail as attachment"
+msgstr "Invia messaggio vocale come allegato email"
+
+#: ../includes/main.conf.php:304
+msgid "Say caller id in recording emailed"
+msgstr "Riproduci ID chiamante nella registrazione inviata"
+
+#: ../includes/main.conf.php:305
+msgid "Say envelop (date/time) in recording emailed"
+msgstr "Riproduci data/ora nella registrazione inviata"
+
+#: ../includes/main.conf.php:306
+msgid "Delete voicemail when emailed"
+msgstr "Elimina messaggio vocale dopo aver spedito l'email"
+
+#: ../includes/main.conf.php:307
+msgid "Play next message after deleting current message"
+msgstr ""
+"Riproduci il messaggio seguente dopo aver eliminato il messaggio corrente"
+
+#: ../includes/main.conf.php:308
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:309
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/VmX.module:58
+msgid "VmX&#8482 Locator"
+msgstr "VmX&#8482 Locator"
+
+#: ../modules/VmX.module:115
+msgid ""
+"Your Premium VmX Locator service has been disabled, REFRESH your browser to "
+"remove this message"
+msgstr ""
+"Il proprio VmX Locator è stato disabilitato, AGGIORNARE la pagina per "
+"rimuovere questo messaggio"
+
+#: ../modules/VmX.module:116 ../modules/followme.module:101
+#, php-format
+msgid ""
+"Check with your Telephone System Administrator if you think there is a "
+"problem"
+msgstr ""
+"Contattare l'amministratore del Sistema Telefonico se ci sono dei problemi"
+
+#: ../modules/VmX.module:147
+msgid "Option 0 not changed"
+msgstr "Opzione 0 non cambiata"
+
+#
+#: ../modules/VmX.module:148 ../modules/VmX.module:181
+#: ../modules/VmX.module:201 ../modules/phonefeatures.module:302
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+"Il numero %s deve contenere cifre valide (vanno benne caratteri come '(', "
+"'-' e ')')"
+
+#: ../modules/VmX.module:180
+msgid "Option 1 not changed"
+msgstr "Opzione 1 non cambiata"
+
+#: ../modules/VmX.module:200
+msgid "Option 2 not changed"
+msgstr "opzione 2 non cambiata"
+
+#: ../modules/VmX.module:300
+msgid "Use When:"
+msgstr "Utilizzare quando:"
+
+#: ../modules/VmX.module:300
+msgid ""
+"Menu options below are available during your personal voicemail greeting "
+"playback. <br/><br/>Check both to use at all times."
+msgstr ""
+"Le opzioni del menu disponibili qui sotto sono proposte durante il messaggio "
+"di benvenuto della casella vocale. <br/><br/>"
+
+#: ../modules/VmX.module:302
+msgid "unavailable"
+msgstr "non disponibile"
+
+#: ../modules/VmX.module:306
+msgid "busy"
+msgstr "occupato"
+
+#: ../modules/VmX.module:310
+msgid "Voicemail Instructions:"
+msgstr "Istruzioni Casella Vocale:"
+
+#: ../modules/VmX.module:310
+msgid "Uncheck to play a beep after your personal voicemail greeting."
+msgstr "Deselezionare per riprodurre un tono dopo il messaggio di benvenuto."
+
+#: ../modules/VmX.module:313
+msgid "Standard voicemail prompts."
+msgstr "Messaggi standard Casella Vocale"
+
+#: ../modules/VmX.module:321
+msgid "Press 0:"
+msgstr "Premere 0:"
+
+#: ../modules/VmX.module:321
+msgid ""
+"Pressing 0 during your personal voicemail greeing goes to the Operator. \n"
+"\t\t\t\t\tUncheck to enter another destination here."
+msgstr ""
+"Premendo 0 durante il messaggio di benvenuto della Casella Vocale, la "
+"chiamata sarà reindirizzata all'operatore. \n"
+"\t\t\t\t\tDeselezionare per inserire un'altra destinazione."
+
+#: ../modules/VmX.module:329
+msgid "Go To Operator"
+msgstr "Per andare all'Operatore"
+
+#: ../modules/VmX.module:333
+msgid "Press 1:"
+msgstr "Premere 1:"
+
+#: ../modules/VmX.module:336
+msgid ""
+"The remaining options can have internal extensions, ringgroups, queues and "
+"external numbers that may be rung. It is often used to include your cell "
+"phone. You should run a test to make sure that the number is functional any "
+"time a change is made so you don't leave a caller stranded or receiving "
+"invalid number messages."
+msgstr ""
+
+#: ../modules/VmX.module:338
+msgid ""
+"Enter an alternate number here, then change your personal voicemail greeting "
+"to let callers know to press 1 to reach that number. <br/><br/>If you'd like "
+"to use your Follow Me List, check \"Send to Follow Me\" and disable Follow "
+"Me above."
+msgstr ""
+"Immettere una destinazione alternativa, dopo, cambiare il messaggio di "
+"benvenuto per permettere ai chiamanti di premere 1 per raggiungere quella "
+"numerazione. <br/><br/> Se si vuole utilizzare la Lista Seguimi, selezionare "
+"\"Invia al Seguimi\" e disattivare sopra il Seguimi."
+
+#: ../modules/VmX.module:351
+msgid "Send to Follow-Me"
+msgstr "Invia al Seguimi"
+
+#: ../modules/VmX.module:359
+msgid "Press 2:"
+msgstr "Premere 2:"
+
+#: ../modules/VmX.module:359
+msgid ""
+"Use any extensions, ringgroups, queues or external numbers. <br/><br/"
+">Remember to re-record your personal voicemail greeting and include "
+"instructions. Run a test to make sure that the number is functional."
+msgstr ""
+"Utilizzare qualsiasi interno, gruppo di chiamata, coda o numero esterno. <br/"
+"><br/>Ricordarsi di ri-registrare il proprio messaggio di benvenuto e "
+"includere delle istruzioni. Fare dei test per assicurarsi che tutto funzioni."
+
+#
+#: ../modules/VmX.module:373
+##, fuzzy, php-format
+msgid "VmX Locator&#8482; Settings for %s (%s)"
+msgstr "Impostazioni di %s (%s)"
+
+#: ../modules/VmX.module:415 ../modules/followme.module:384
+#: ../modules/phonefeatures.module:180 ../modules/settings.module:625
+msgid "Update"
+msgstr "Aggiorna"
+
+#: ../modules/callmonitor.module:36 ../modules/callmonitor.module:256
+msgid "Call Monitor"
+msgstr "Registrazioni Chiamate"
+
+#
+#: ../modules/callmonitor.module:131
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr "Il percorso non è una directory: %s"
+
+#: ../modules/callmonitor.module:140 ../modules/voicemail.module:318
+msgid "delete"
+msgstr "elimina"
+
+#
+#: ../modules/callmonitor.module:146
+msgid "duration"
+msgstr "durata"
+
+#
+#: ../modules/callmonitor.module:149
+msgid "ignore"
+msgstr "niente"
+
+#: ../modules/callmonitor.module:158 ../modules/voicemail.module:339
+msgid "Date"
+msgstr "Data"
+
+#: ../modules/callmonitor.module:160 ../modules/voicemail.module:341
+msgid "Time"
+msgstr "Ora"
+
+#: ../modules/callmonitor.module:162 ../modules/voicemail.module:343
+msgid "Caller ID"
+msgstr "ID Chiamante"
+
+#: ../modules/callmonitor.module:164
+msgid "Source"
+msgstr "Sorgente"
+
+#: ../modules/callmonitor.module:166
+msgid "Destination"
+msgstr "Destinazione"
+
+#: ../modules/callmonitor.module:168
+msgid "Context"
+msgstr "Contesto"
+
+#: ../modules/callmonitor.module:170 ../modules/voicemail.module:349
+msgid "Duration"
+msgstr "Durata"
+
+#: ../modules/callmonitor.module:201
+msgid "Monitor"
+msgstr "Registrazione"
+
+#: ../modules/callmonitor.module:221 ../modules/voicemail.module:390
+msgid "play"
+msgstr "riproduci"
+
+#
+#: ../modules/callmonitor.module:258
+#, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Registrazioni Chiamate di %s (%s)"
+
+#: ../modules/callmonitor.module:310 ../modules/voicemail.module:492
+msgid "select"
+msgstr "seleziona"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:493
+msgid "all"
+msgstr "tutto"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:494
+msgid "none"
+msgstr "niente"
+
+#: ../modules/callmonitor.module:532
+msgid "Only deletes recording files, not cdr log"
+msgstr "Eliminati solo i file di registrazione, non i log delle chiamate"
+
+#: ../modules/featurecodes.module:36 ../modules/featurecodes.module:63
+##, fuzzy
+msgid "Feature Codes"
+msgstr "Codici Servizi"
+
+#
+#: ../modules/featurecodes.module:65
+##, fuzzy, php-format
+msgid " for %s (%s)"
+msgstr " per %s (%s)"
+
+#: ../modules/featurecodes.module:72
+msgid "Handset Feature Code"
+msgstr "Codice"
+
+#: ../modules/featurecodes.module:75
+msgid "Action"
+msgstr "Azione"
+
+#: ../modules/followme.module:43
+msgid "Follow Me"
+msgstr "Seguimi"
+
+#: ../modules/followme.module:100
+msgid ""
+"Your Follow-Me has been disabled, REFRESH your browser to remove this message"
+msgstr ""
+"Il Seguimi è disattivato, AGGIORNA la pagina per rimuovere questo messaggio"
+
+#: ../modules/followme.module:118
+msgid "Follow-Me pre-ring time not changed"
+msgstr "Tempo di pre-squillo per il Seguimi non cambiato"
+
+#: ../modules/followme.module:119 ../modules/followme.module:142
+#, php-format
+msgid "Number %s must be an interger number of seconds"
+msgstr "Il numero %s deve contenere numeri interi"
+
+#: ../modules/followme.module:141
+msgid "Follow-Me list ring time not changed"
+msgstr "Tempo di squillo Lista Seguimi non cambiato"
+
+#: ../modules/followme.module:185
+msgid "Follow-Me list must contain at least one valid number"
+msgstr "Il Seguimi deve contenere almeno un numero valido"
+
+#: ../modules/followme.module:186
+#, php-format
+msgid "The following: %s is not valid"
+msgstr "Il seguente: %s non è valido"
+
+#
+#: ../modules/followme.module:291 ../modules/followme.module:344
+#: ../modules/phonefeatures.module:335 ../modules/settings.module:420
+msgid "Enable"
+msgstr "Attiva"
+
+#: ../modules/followme.module:292
+msgid ""
+"Dial-by-name Directory, IVR, and internal \n"
+"\t\t\t\t\t\t\t\t\t\t\t\t\tcalls will ring the numbers in the FollowMe \n"
+"\t\t\t\t\t\t\t\t\t\t\t\t\tList. Any FreePBX routes that directly \n"
+"\t\t\t\t\t\t\t\t\t\t\t\t\treference a FollowMe are unaffected by this \n"
+"\t\t\t\t\t\t\t\t\t\t\t\t\tenable/disable setting."
+msgstr "L'Elenco Telefonico, l'IVR e le chiamate interne chiameranno i numeri definiti nella Lista Seguimi. Qualsiasi rotta che ha come referenza un Seguimi non sarà affetto dall'attivazione o dalla disattivazione di questa impostazione."
+
+#: ../modules/followme.module:304
+msgid "Follow Me List:"
+msgstr "Lista Seguimi:"
+
+#: ../modules/followme.module:305
+#, php-format
+msgid "Extensions and outside numbers to ring next."
+msgstr "Interni e numeri esterni da chiamare dopo."
+
+#: ../modules/followme.module:306
+#, php-format
+msgid "Include %s to keep it ringing."
+msgstr "Immettere %s per lasciar squillare."
+
+#: ../modules/followme.module:312
+#, php-format
+msgid "Ring %s First For:"
+msgstr "Chiama prima %s per:"
+
+#: ../modules/followme.module:313
+#, php-format
+msgid "Time to ring extension %s before ringing the %s Follow Me List %s"
+msgstr ""
+"Il tempo di chiamata per l'interno %s prima di far squillare la %s Lista "
+"Seguimi %s"
+
+#: ../modules/followme.module:323 ../modules/followme.module:336
+msgid "seconds"
+msgstr "secondi"
+
+#: ../modules/followme.module:326
+msgid "Ring Followme List for:"
+msgstr "Chiama la Lista Seguimi per:"
+
+#: ../modules/followme.module:326
+msgid "Time to ring the Follow Me List."
+msgstr "Il tempo di chiamata per la Lista Seguimi."
+
+#: ../modules/followme.module:341
+msgid "Use Confirmation:"
+msgstr "Utilizza Conferma:"
+
+#: ../modules/followme.module:341
+msgid ""
+"Outside lines that are part of the Follow Me List will be called and offered "
+"a menu:<br/><br/> \"You have an incoming call. Press 1 to accept or 2 to "
+"decline.\"<br/><br/> This keeps calls from ending up in external voicemail. "
+"Make sure that the List Ring Time is long enough to allow for you to hear "
+"and react to this message."
+msgstr ""
+"Ai Numeri esterni che fanno parte della Lista Seguimi sarà proposto un menu:"
+"<br/><br/> \"Hai una chiamata in arrivo. Premere 1 per accettare o 2 per "
+"rifiutare.\" Questo evita alle chiamate esterne di finire in una segreteria. "
+"Assicurarsi che il tempo di chiamata sia abbastanza lungo per rispondere a "
+"questo messaggio."
+
+#: ../modules/followme.module:356
+##, fuzzy
+msgid "Followme Settings"
+msgstr "Impostazioni Seguimi"
+
+#
+#: ../modules/followme.module:358
+##, fuzzy, php-format
+msgid "Followme Settings for %s (%s)"
+msgstr "Impostazioni Seguimi per %s (%s)"
+
+#: ../modules/phonefeatures.module:25 ../modules/phonefeatures.module:96
+#: ../modules/phonefeatures.module:163
+msgid "Phone Features"
+msgstr "Servizi Telefonici"
+
+#
+#: ../modules/phonefeatures.module:149
+##, fuzzy
+msgid "Call Forwarding"
+msgstr "Trasferimento di Chiamata"
+
+#
+#: ../modules/phonefeatures.module:165
+##, fuzzy, php-format
+msgid "Features for %s (%s)"
+msgstr "Impostazioni per %s (%s)"
+
+#: ../modules/phonefeatures.module:301
+msgid "Call forward number not changed"
+msgstr "Numero per il trasferimento di chiamata non cambiato"
+
+#: ../modules/settings.module:56
+msgid "Settings"
+msgstr "Impostazioni"
+
+#: ../modules/settings.module:118 ../modules/settings.module:123
+#: ../modules/settings.module:128 ../modules/settings.module:133
+#: ../modules/settings.module:143 ../modules/settings.module:148
+msgid "Voicemail password not changed"
+msgstr "Password Casella Vocale non cambiata"
+
+#: ../modules/settings.module:119
+msgid "Password and password confirm must not be blank"
+msgstr "Password e conferma password non possono essere vuoti"
+
+#
+#: ../modules/settings.module:124
+##, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "La Password deve essere minimo di %d numeri"
+
+#
+#: ../modules/settings.module:129
+##, fuzzy, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "La Password deve essere di %d numeri"
+
+#: ../modules/settings.module:134
+msgid "Password and password confirm do not match"
+msgstr "Password e Conferma password non corrispondono"
+
+#
+#: ../modules/settings.module:144 ../modules/settings.module:149
+#: ../modules/settings.module:200 ../modules/settings.module:205
+##, fuzzy, php-format
+msgid "%s does not exist or is not writable"
+msgstr "%s non esiste o non è scrivile"
+
+#
+#: ../modules/settings.module:189
+msgid "Voicemail email and pager address not changed"
+msgstr "Password Casella Vocale non cambiata"
+
+#
+#: ../modules/settings.module:199 ../modules/settings.module:204
+msgid "Voicemail email settings not changed"
+msgstr "Password Casella Vocale non cambiata"
+
+#: ../modules/settings.module:347
+msgid "Language:"
+msgstr "Lingua:"
+
+#
+#: ../modules/settings.module:357
+#, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "La Password deve essere di solo numeri e %s cifre"
+
+#
+#: ../modules/settings.module:360
+##, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr "La Password deve essere di solo numeri e minimo %s cifre"
+
+#
+#: ../modules/settings.module:365
+msgid "Voicemail Password:"
+msgstr "Password Casella Vocale:"
+
+#: ../modules/settings.module:371
+msgid "Enter again to confirm:"
+msgstr "Conferma password:"
+
+#
+#: ../modules/settings.module:419
+msgid "Email Notification"
+msgstr "Notifica Email"
+
+#
+#: ../modules/settings.module:423
+msgid "Email Voicemail To:"
+msgstr "Notifica Email a:"
+
+#
+#: ../modules/settings.module:429
+msgid "Pager Email Notification To:"
+msgstr "Invia Notifica Email al Pager:"
+
+#: ../modules/settings.module:485
+msgid "Audio Format:"
+msgstr "Formato Audio:"
+
+#: ../modules/settings.module:488
+msgid "Best Quality"
+msgstr "Migliore Qualità"
+
+#: ../modules/settings.module:489
+msgid "Smallest Download"
+msgstr "Download Veloci"
+
+#: ../modules/settings.module:497
+msgid "Voicemail Settings"
+msgstr "Impostazioni Casella Vocale"
+
+#: ../modules/settings.module:538
+msgid "Call Monitor Settings"
+msgstr "Impostazioni Registrazioni Chiamate"
+
+#: ../modules/settings.module:541
+msgid "Record INCOMING:"
+msgstr "Registra ENTRANTI:"
+
+#: ../modules/settings.module:543 ../modules/settings.module:551
+msgid "Always"
+msgstr "Sempre"
+
+#: ../modules/settings.module:544 ../modules/settings.module:552
+msgid "Never"
+msgstr "Mai"
+
+#: ../modules/settings.module:545 ../modules/settings.module:553
+msgid "On-Demand"
+msgstr "Su richiesta"
+
+#: ../modules/settings.module:549
+msgid "Record OUTGOING:"
+msgstr "Registra USCENTI:"
+
+#
+#: ../modules/settings.module:592
+##, fuzzy, php-format
+msgid "Settings for %s (%s)"
+msgstr "Impostazioni per %s (%s)"
+
+#: ../modules/voicemail.module:44
+msgid "Voicemail"
+msgstr "Casella Vocale"
+
+#: ../modules/voicemail.module:163
+msgid "A folder must be selected before the message can be moved."
+msgstr ""
+"Prima di spostare un messaggio, selezionare una cartella di destinazione"
+
+#: ../modules/voicemail.module:177
+msgid "An extension must be selected before the message can be forwarded."
+msgstr "Prima di inoltrare un messaggio, selezionare l'interno di destinazione"
+
+#: ../modules/voicemail.module:321
+msgid "move_to"
+msgstr "sposta_verso"
+
+#: ../modules/voicemail.module:324
+msgid "Folder"
+msgstr "Cartella"
+
+#: ../modules/voicemail.module:328
+msgid "forward_to"
+msgstr "inoltra_a"
+
+#: ../modules/voicemail.module:345
+msgid "Priority"
+msgstr "Priorità"
+
+#: ../modules/voicemail.module:347
+msgid "Orig Mailbox"
+msgstr "Casella Orig"
+
+#: ../modules/voicemail.module:379
+msgid "Message"
+msgstr "Messaggio"
+
+#: ../modules/voicemail.module:394
+msgid "Voicemail recording(s) was not found."
+msgstr "Registrazioni Casella Vocale non trovate."
+
+#
+#: ../modules/voicemail.module:395
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+"Nella pagina delle impostazioni, cambiare il formato dei messaggi vocali. "
+"Adesso è impostato su %s"
+
+#
+#: ../modules/voicemail.module:422
+msgid "Voicemail Login not found."
+msgstr "Login Casella Vocale non trovato"
+
+#: ../modules/voicemail.module:423
+msgid "No access to voicemail"
+msgstr "Accesso alla Casella Vocale disabilitato"
+
+#: ../modules/voicemail.module:429
+msgid "No Voicemail Recordings for Admin"
+msgstr "Nessuna Casella Vocale per Admin"
+
+#
+#: ../modules/voicemail.module:445
+#, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "Casella Vocale di %s (%s)"
+
+#
+#: ../modules/voicemail.module:695
+##, fuzzy, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "Non posso creare la cartella %s sul server"
+
+#
+#: ../modules/voicemail.module:735
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr "Permessi negati nella cartella %s o %s"
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr "scarica"
+
+msgid "Unconditional:"
+msgstr "Incondizionato:"
+
+msgid "Unavailable:"
+msgstr "Non disponibile:"
+
+msgid "Busy:"
+msgstr "Occupato:"
+
+#
+##, fuzzy
+msgid "Call Waiting"
+msgstr "Avviso di Chiamata"
+
+##, fuzzy
+msgid "Do Not Disturb"
+msgstr " Non-Disturbare"
+
+#
+##, fuzzy
+msgid "Passwords must be all numbers and at least 3 digits"
+msgstr "La Password deve essere di solo numeri e minimo di 3 cifre"
+
+#~ msgid "Directory"
+#~ msgstr "Directory"
+
+#~ msgid "Echo Test"
+#~ msgstr "Test Echo"
+
+#~ msgid "Weather"
+#~ msgstr "Meteo"
+
+#~ msgid "Schedule wakeup call"
+#~ msgstr "Sveglia"
+
+#~ msgid "festival test (your extension is XXX)"
+#~ msgstr "Test Festival (il tuo interno è XXX)"
+
+#~ msgid "Deactivate Call Waiting"
+#~ msgstr "Disattiva Avviso di Chiamata"
+
+#~ msgid "Disable Call Forwarding"
+#~ msgstr "Disattiva Inoltro di Chiamata"
+
+#
+#~ msgid "IVR Recording"
+#~ msgstr "Registrazione IVR"
+
+#~ msgid "Disable Do-Not-Disturb"
+#~ msgstr "Disattiva Non-Disturbare"
+
+#~ msgid "Disable Call Forward on Busy"
+#~ msgstr "Disattiva Inoltro di Chiamata su Occupato"
+
+##, fuzzy
+#~ msgid "Message Center (does not ask for extension)"
+#~ msgstr "Centro Messaggi (non chiede l'interno)"
+
+#~ msgid "Enter Message Center"
+#~ msgstr "Centro Messaggi"
+
+#~ msgid "Playback IVR Recording"
+#~ msgstr "Riproduce Registrazione IVR"
+
+#~ msgid "Test Fax"
+#~ msgstr "Test Fax"
+
+#~ msgid "Simulate incoming call"
+#~ msgstr "Simula chiamata entrante"
+
+#
+##, fuzzy
+#~ msgid "Conference for %s (%s%s)"
+#~ msgstr "Conferenza per %s (%s%s)"
+
+#~ msgid "Help"
+#~ msgstr "Aiuto"
+
+#
+#~ msgid "Pager Voicemail To:"
+#~ msgstr "Casella Vocale"
+
+#~ msgid "Passwords must be all numbers and only 4 digits"
+#~ msgstr "La Password deve essere di solo numeri e 4 cifre"
+
+msgid "Folders"
+msgstr "Cartelle"
+
+#~ msgid "Login used"
+#~ msgstr "Login utilizzato"
+
+#~ msgid "No Asterisk Manager Interface connection"
+#~ msgstr "Impossibile connettersi all'Asterisk Manager Interface"
+
+#~ msgid "Cannot connect to the"
+#~ msgstr "Impossibile connettersi al"
+
+#~ msgid "database"
+#~ msgstr "database"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "non è una directory o non è leggibile"
+
+#~ msgid "of"
+#~ msgstr "di"
+
+#~ msgid "Use your"
+#~ msgstr "Utilizzare il "
+
+#~ msgid "for"
+#~ msgstr "di"
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr "La Password deve essere di 4 numeri"
+
+#~ msgid "Check voicemail audio format on settings page to change from"
+#~ msgstr "Controllare il formato audio nella pagina delle impostazioni"
+
+#~ msgid "on the server"
+#~ msgstr "nel server"
+
+#~ msgid "No database connection"
+#~ msgstr "Connessione al database fallita"
+
+msgid "Email a notification, including audio file if indicated below. "
+msgstr "Invia una notifica per posta elettronica, incluso il file audio se impostato sotto."
+
+msgid "Email a short notification "
+msgstr "Invia una breve notifica"
+
+msgid "Phone Features for %s (%s)"
+msgstr "Servizi Telefonici per %s (%s)"
+
+msgid "User Portal"
+msgstr "Portale Utente" \ No newline at end of file
diff --git a/fs_selfservice/fri/locale/locale.txt b/fs_selfservice/fri/locale/locale.txt
new file mode 100644
index 0000000..6b93e2e
--- /dev/null
+++ b/fs_selfservice/fri/locale/locale.txt
@@ -0,0 +1,37 @@
+// To create the .po (write your translations to this file):
+$ find *.php ../includes/* ../modules/*.module ../misc/*.php ../theme/* | xargs xgettext -L PHP -o ari.po --keyword=_ -
+
+// To create the utf-8 .po
+$ iconv -f iso-8859-1 -t utf-8 -o ari.utf-8.po ari.po
+
+// To create the .mo:
+$ msgfmt -v ari.utf-8.po -o ari.mo
+
+// To update (assume both files to be merged are utf-8)
+$ msgmerge es_ES/LC_MESSAGES/ari.po ari.utf-8.po --output-file=es_ES/LC_MESSAGES/ari.po
+$ msgfmt -v es_ES/LC_MESSAGES/ari.po -o es_ES/LC_MESSAGES/ari.mo
+
+
+// script
+// for this to work all translated files need to be converted to utf-8 (use iconv)
+//
+find ../*.php ../includes/* ../modules/*.module ../misc/*.php ../theme/*.css | xargs xgettext -L PHP -o ari.po --keyword=_ -
+iconv -f iso-8859-1 -t utf-8 -o ari.utf-8.po ari.po
+msgmerge el_GR/LC_MESSAGES/ari.po ari.utf-8.po --output-file=el_GR/LC_MESSAGES/ari.po
+msgfmt -v el_GR/LC_MESSAGES/ari.po -o el_GR/LC_MESSAGES/ari.mo
+msgmerge es_ES/LC_MESSAGES/ari.po ari.utf-8.po --output-file=es_ES/LC_MESSAGES/ari.po
+msgfmt -v es_ES/LC_MESSAGES/ari.po -o es_ES/LC_MESSAGES/ari.mo
+msgmerge fr_FR/LC_MESSAGES/ari.po ari.utf-8.po --output-file=fr_FR/LC_MESSAGES/ari.po
+msgfmt -v fr_FR/LC_MESSAGES/ari.po -o fr_FR/LC_MESSAGES/ari.mo
+msgmerge he_IL/LC_MESSAGES/ari.po ari.utf-8.po --output-file=he_IL/LC_MESSAGES/ari.po
+msgfmt -v he_IL/LC_MESSAGES/ari.po -o he_IL/LC_MESSAGES/ari.mo
+msgmerge hu_HU/LC_MESSAGES/ari.po ari.utf-8.po --output-file=hu_HU/LC_MESSAGES/ari.po
+msgfmt -v hu_HU/LC_MESSAGES/ari.po -o hu_HU/LC_MESSAGES/ari.mo
+msgmerge it_IT/LC_MESSAGES/ari.po ari.utf-8.po --output-file=it_IT/LC_MESSAGES/ari.po
+msgfmt -v ot_IT/LC_MESSAGES/ari.po -o it_IT/LC_MESSAGES/ari.mo
+msgmerge pt_BR/LC_MESSAGES/ari.po ari.utf-8.po --output-file=pt_BR/LC_MESSAGES/ari.po
+msgfmt -v pt_BR/LC_MESSAGES/ari.po -o pt_BR/LC_MESSAGES/ari.mo
+msgmerge sv_SE/LC_MESSAGES/ari.po ari.po --output-file=sv_SE/LC_MESSAGES/ari.po
+msgfmt -v sv_SE/LC_MESSAGES/ari.po -o sv_SE/LC_MESSAGES/ari.mo
+
+
diff --git a/fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..baa1a11
--- /dev/null
+++ b/fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..0ab45fa
--- /dev/null
+++ b/fs_selfservice/fri/locale/pt_BR/LC_MESSAGES/ari.po
@@ -0,0 +1,647 @@
+# Brazilian portuguese translation
+# Copyright (C) 2005 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Arnaldo M. Pereira <arnaldo@ansi-c.org>, 2005.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Arnaldo M. Pereira <arnaldo@ansi-c.org>\n"
+"Language-Team: Brazilian Portuguese <arnaldo@ansi-c.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "Asterisk Call Manager não responde"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "Autenticação no Asterisk falhou:"
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+#, fuzzy
+msgid "Asterisk command not understood"
+msgstr "Comando reload do Asterisk não compreendido"
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr ""
+
+#: ../includes/bootstrap.php:226
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr ""
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+
+#: ../includes/common.php:173
+#, fuzzy
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "Não foi possível conectar ao Asterisk Manager"
+
+#: ../includes/common.php:174
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+
+#: ../includes/common.php:175
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+
+#: ../includes/common.php:176
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+
+#: ../includes/common.php:193 ../includes/common.php:208
+#, fuzzy
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr ""
+"Verifique a instalação do AMP, do banco de dados do asterisk ou do main.conf "
+"do ARI"
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr ""
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr "Página não encontrada."
+
+#: ../includes/display.php:92
+#, fuzzy
+msgid "Search"
+msgstr "Procurado"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "Procurado"
+
+#: ../includes/display.php:139
+#, fuzzy, php-format
+msgid "Results %d - %d of %d"
+msgstr "Resultados"
+
+#: ../includes/display.php:141
+#, fuzzy, php-format
+msgid "Results %d"
+msgstr "Resultados"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "Primeiro"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr ""
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "Senha incorreta"
+
+#: ../includes/login.php:279
+#, fuzzy
+msgid "Incorrect Username or Password"
+msgstr "Senha incorreta"
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr ""
+
+#: ../includes/login.php:419
+#, fuzzy
+msgid "Password"
+msgstr "Senha incorreta"
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr ""
+
+#: ../includes/login.php:436
+#, fuzzy
+msgid "Remember Password"
+msgstr "Voicemail para"
+
+#: ../includes/login.php:451
+#, fuzzy
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr "Mailbox e senha do Voicemail"
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr "Esta é a mesma senha utilizada para o telefone"
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+"Para manutenção e assistência, entre em contato com o Administrador de seu "
+"Sistema de Telefonia"
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr ""
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr ""
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr ""
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr ""
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr ""
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr ""
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr ""
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr ""
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr ""
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr ""
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr ""
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr ""
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr ""
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr ""
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr ""
+
+#: ../includes/main.conf.php:239
+msgid "IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr ""
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr ""
+
+#: ../includes/main.conf.php:244
+msgid "Message Center (does not ask for extension)"
+msgstr ""
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr ""
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr ""
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr ""
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr ""
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr ""
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:291
+msgid "Say envelop (date/time) in recording emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr ""
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr ""
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+#, fuzzy
+msgid "Call Monitor"
+msgstr "Monitor de ligações para"
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr ""
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr ""
+
+#: ../modules/callmonitor.module:147
+msgid "duration"
+msgstr ""
+
+#: ../modules/callmonitor.module:150
+msgid "ignore"
+msgstr ""
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr ""
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr ""
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr ""
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr ""
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr ""
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr ""
+
+#: ../modules/callmonitor.module:202
+#, fuzzy
+msgid "Monitor"
+msgstr "Monitor de ligações para"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr ""
+
+#: ../modules/callmonitor.module:259
+#, fuzzy, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Monitor de ligações para"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr ""
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr ""
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr ""
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr ""
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, fuzzy, php-format
+msgid "Conference for %s (%s%s)"
+msgstr "Voicemail para"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr ""
+
+#: ../modules/help.module:70
+#, fuzzy, php-format
+msgid "Help for %s (%s)"
+msgstr "Configurações para"
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr ""
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr ""
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr "Configurações"
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr ""
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr "Senha do Voicemail não alterada"
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr "Senha e confirmação de senha não pode ser não pode estar em branco"
+
+#: ../modules/settings.module:157
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "A senha deve conter apenas números e apenas 4 dígitos"
+
+#: ../modules/settings.module:162
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "A senha deve conter apenas números e apenas 4 dígitos"
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr "Senha e confirmação de senha não batem"
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, fuzzy, php-format
+msgid "%s does not exist or is not writable"
+msgstr "não existe ou não tem permissão de escrita"
+
+#: ../modules/settings.module:223
+#, fuzzy
+msgid "Voicemail email and pager address not changed"
+msgstr "Senha do Voicemail não alterada"
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+#, fuzzy
+msgid "Voicemail email settings not changed"
+msgstr "Senha do Voicemail não alterada"
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr ""
+
+#: ../modules/settings.module:408
+#, fuzzy
+msgid "Call Routing"
+msgstr "Monitor de ligações para"
+
+#: ../modules/settings.module:411
+#, fuzzy
+msgid "Call Forwarding:"
+msgstr "Monitor de ligações para"
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+msgid "Enable"
+msgstr ""
+
+#: ../modules/settings.module:431
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "A senha deve conter apenas números e apenas 4 dígitos"
+
+#: ../modules/settings.module:434
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr "A senha deve conter apenas números e apenas 4 dígitos"
+
+#: ../modules/settings.module:439
+#, fuzzy
+msgid "Voicemail Password:"
+msgstr "Voicemail para"
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr ""
+
+#: ../modules/settings.module:492
+#, fuzzy
+msgid "Email Voicemail To:"
+msgstr "Voicemail para"
+
+#: ../modules/settings.module:498
+#, fuzzy
+msgid "Pager Voicemail To:"
+msgstr "Voicemail para"
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr ""
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr ""
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr ""
+
+#: ../modules/settings.module:570
+#, fuzzy
+msgid "Voicemail Settings"
+msgstr "Voicemail para"
+
+#: ../modules/settings.module:611
+#, fuzzy
+msgid "Call Monitor Settings"
+msgstr "Monitor de ligações para"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr ""
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr ""
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr ""
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr ""
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr ""
+
+#: ../modules/settings.module:669
+#, fuzzy, php-format
+msgid "Settings for %s (%s)"
+msgstr "Configurações para"
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr ""
+
+#: ../modules/voicemail.module:45
+#, fuzzy
+msgid "Voicemail"
+msgstr "Voicemail para"
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr "Uma pasta deve ser selecionada antes que a mensagem possa ser movida."
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr ""
+"Uma extensão deve ser selecionada antes que a mensagem possa ser repassada."
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr ""
+
+#: ../modules/voicemail.module:307
+msgid "Folder"
+msgstr ""
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr ""
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr ""
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr ""
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr ""
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr "Gravação do(s) Voicemail(s) não encontrada."
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+
+#: ../modules/voicemail.module:405
+#, fuzzy
+msgid "Voicemail Login not found."
+msgstr "Login do Voicemail não encontrado, utilizado login SIP"
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr "Sem acesso ao voicemail"
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr "Sem gravações para Admin"
+
+#: ../modules/voicemail.module:428
+#, fuzzy, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "Voicemail para"
+
+#: ../modules/voicemail.module:678
+#, fuzzy, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "Não foi possível criar caixa de mensagens"
+
+#: ../modules/voicemail.module:718
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr ""
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr ""
+
+#, fuzzy
+#~ msgid "Passwords must be all numbers and only 4 digits"
+#~ msgstr "A senha deve conter apenas números e apenas 4 dígitos"
+
+#, fuzzy
+#~ msgid "No Asterisk Manager Interface connection"
+#~ msgstr "Asterisk Call Manager não responde"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "não é um diretório ou não pode ser lido"
+
+#~ msgid "of"
+#~ msgstr "de"
+
+#~ msgid "Use your"
+#~ msgstr "Use seu"
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr "A senha deve conter apenas números e apenas 4 dígitos"
+
+#~ msgid "Check voicemail audio format on settings page to change from"
+#~ msgstr ""
+#~ "Verifique o formato do audio do voicemail na página de configurações para "
+#~ "mudar de"
+
+#~ msgid "on the server"
+#~ msgstr "no servidor"
+
+#~ msgid "No database connection"
+#~ msgstr "Sem conexão com o banco de dados"
diff --git a/fs_selfservice/fri/locale/readme.txt b/fs_selfservice/fri/locale/readme.txt
new file mode 100644
index 0000000..2491865
--- /dev/null
+++ b/fs_selfservice/fri/locale/readme.txt
@@ -0,0 +1,37 @@
+// To create the .po (write your translations to this file):
+$ find *.php ../includes/* ../modules/*.module ../misc/*.php ../theme/* | xargs xgettext -L PHP -o ari.po --keyword=_ -
+
+// To create the utf-8 .po
+$ iconv -f iso-8859-1 -t utf-8 -o ari.utf-8.po ari.po
+
+// To create the .mo:
+$ msgfmt -v ari.utf-8.po -o ari.mo
+
+// To update (assume both files to be merged are utf-8)
+$ msgmerge es_ES/LC_MESSAGES/ari.po ari.utf-8.po --output-file=es_ES/LC_MESSAGES/ari.po
+$ msgfmt -v es_ES/LC_MESSAGES/ari.po -o es_ES/LC_MESSAGES/ari.mo
+
+
+// script
+// for this to work all translated files need to be converted to utf-8 (use iconv)
+//
+find *.php ../includes/* ../modules/*.module ../misc/*.php ../theme/* | xargs xgettext -L PHP -o ari.po --keyword=_ -
+iconv -f iso-8859-1 -t utf-8 -o ari.utf-8.po ari.po
+msgmerge el_GR/LC_MESSAGES/ari.po ari.utf-8.po --output-file=el_GR/LC_MESSAGES/ari.po
+msgfmt -v el_GR/LC_MESSAGES/ari.po -o el_GR/LC_MESSAGES/ari.mo
+msgmerge es_ES/LC_MESSAGES/ari.po ari.utf-8.po --output-file=es_ES/LC_MESSAGES/ari.po
+msgfmt -v es_ES/LC_MESSAGES/ari.po -o es_ES/LC_MESSAGES/ari.mo
+msgmerge fr_FR/LC_MESSAGES/ari.po ari.utf-8.po --output-file=fr_FR/LC_MESSAGES/ari.po
+msgfmt -v fr_FR/LC_MESSAGES/ari.po -o fr_FR/LC_MESSAGES/ari.mo
+msgmerge he_IL/LC_MESSAGES/ari.po ari.utf-8.po --output-file=he_IL/LC_MESSAGES/ari.po
+msgfmt -v he_IL/LC_MESSAGES/ari.po -o he_IL/LC_MESSAGES/ari.mo
+msgmerge hu_HU/LC_MESSAGES/ari.po ari.utf-8.po --output-file=hu_HU/LC_MESSAGES/ari.po
+msgfmt -v hu_HU/LC_MESSAGES/ari.po -o hu_HU/LC_MESSAGES/ari.mo
+msgmerge it_IT/LC_MESSAGES/ari.po ari.utf-8.po --output-file=it_IT/LC_MESSAGES/ari.po
+msgfmt -v ot_IT/LC_MESSAGES/ari.po -o it_IT/LC_MESSAGES/ari.mo
+msgmerge pt_BR/LC_MESSAGES/ari.po ari.utf-8.po --output-file=pt_BR/LC_MESSAGES/ari.po
+msgfmt -v pt_BR/LC_MESSAGES/ari.po -o pt_BR/LC_MESSAGES/ari.mo
+msgmerge sv_SE/LC_MESSAGES/ari.po ari.utf-8.po --output-file=sv_SE/LC_MESSAGES/ari.po
+msgfmt -v sv_SE/LC_MESSAGES/ari.po -o sv_SE/LC_MESSAGES/ari.mo
+
+
diff --git a/fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.mo b/fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.mo
new file mode 100644
index 0000000..c8ea152
--- /dev/null
+++ b/fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.mo
Binary files differ
diff --git a/fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.po b/fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.po
new file mode 100644
index 0000000..f8f0ad3
--- /dev/null
+++ b/fs_selfservice/fri/locale/sv_SE/LC_MESSAGES/ari.po
@@ -0,0 +1,678 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-05-03 08:32-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Niklas Larsson <pnsystem@comhem.se>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../includes/asi.php:46
+msgid "Asterisk Call Manager not responding"
+msgstr "Asterisk Call Manager svara ej"
+
+#: ../includes/asi.php:54
+msgid "Asterisk authentication failed:"
+msgstr "Ej godk&auml;nd autentisering mot Asterisk:"
+
+#: ../includes/asi.php:96 ../includes/asi.php:111
+#, fuzzy
+msgid "Asterisk command not understood"
+msgstr "Asterisk f&ouml;rstod ej omladdningskommandot"
+
+#: ../includes/bootstrap.php:123
+#, php-format
+msgid "To many directories in %s Not all files processed"
+msgstr "F&ouml;r m&aring;nga mappar i %s Alla filer har inte behandlats"
+
+#: ../includes/bootstrap.php:226
+#, fuzzy
+msgid "ARI requires a version of PHP 4.3 or later"
+msgstr "ARI kr&auml;ver version 4.0 eller h&ouml;gre av PHP"
+
+#: ../includes/bootstrap.php:245
+msgid ""
+"PHP PEAR must be installed. Visit http://pear.php.net for help with "
+"installation."
+msgstr ""
+"PHP PEAR m&aring;ste installeras. G&aring; till http://pear.php.net, och "
+"installera."
+
+#: ../includes/common.php:173
+#, fuzzy
+msgid "ARI does not appear to have access to the Asterisk Manager."
+msgstr "Kan ej ansluta till Asterisk Manager"
+
+#: ../includes/common.php:174
+#, fuzzy
+msgid ""
+"Check the ARI 'main.conf.php' configuration file to set the Asterisk Manager "
+"Account."
+msgstr ""
+"Kontrollera ARI 'main.conf' filen och inst&auml;llningarna f&ouml;r Asterisk "
+"Manager kontot."
+
+#: ../includes/common.php:175
+#, fuzzy
+msgid "Check /etc/asterisk/manager.conf for a proper Asterisk Manager Account"
+msgstr ""
+"Kontrollera /etc/asterisk/manager.conf, se till att det finns ett korrekt "
+"Asterisk Manager konto"
+
+#: ../includes/common.php:176
+#, fuzzy
+msgid ""
+"make sure [general] enabled = yes and a 'permit=' line for localhost or the "
+"webserver."
+msgstr ""
+" som bla har [general] enabled = yes och en 'permit=' f&ouml;r localhost "
+"eller ip nummret f&ouml;r webservern"
+
+#: ../includes/common.php:193 ../includes/common.php:208
+#, fuzzy
+msgid "Check AMP installation, asterisk, and ARI main.conf"
+msgstr "Kontrollera AMP installationen, asterisk databas eller ARI main.conf"
+
+#: ../includes/common.php:344
+msgid "Logout"
+msgstr "Logga ut"
+
+#: ../includes/common.php:349
+msgid "Page Not Found."
+msgstr "Sidan hittas ej."
+
+#: ../includes/display.php:92
+msgid "Search"
+msgstr "S&ouml;k"
+
+#: ../includes/display.php:135
+msgid "Searched for"
+msgstr "S&ouml;kte efter"
+
+#: ../includes/display.php:139
+#, fuzzy, php-format
+msgid "Results %d - %d of %d"
+msgstr "Resultat %d av %d"
+
+#: ../includes/display.php:141
+#, fuzzy, php-format
+msgid "Results %d"
+msgstr "Resultat %d"
+
+#: ../includes/display.php:195
+msgid "First"
+msgstr "F&ouml;rst"
+
+#: ../includes/display.php:208
+msgid "Last"
+msgstr "Sist"
+
+#: ../includes/login.php:267
+msgid "Incorrect Password"
+msgstr "Felaktigt l&ouml;senord"
+
+#: ../includes/login.php:279
+msgid "Incorrect Username or Password"
+msgstr "Felaktigt l&ouml;senord"
+
+#: ../includes/login.php:402 ../includes/login.php:411
+msgid "Login"
+msgstr "Anv&auml;ndarnamn"
+
+#: ../includes/login.php:419
+msgid "Password"
+msgstr "L&ouml;senord"
+
+#: ../includes/login.php:428
+msgid "Submit"
+msgstr "Logga in"
+
+#: ../includes/login.php:436
+msgid "Remember Password"
+msgstr "Kom ih&aring;g l&ouml;senord"
+
+#: ../includes/login.php:451
+#, fuzzy
+msgid "Use your <b>Voicemail Mailbox and Password</b>"
+msgstr ""
+"Anv&auml;nd din <b>R&ouml;stbrevl&aring;das nummer och l&ouml;senord</b>"
+
+#: ../includes/login.php:452
+msgid "This is the same password used for the phone"
+msgstr "Det &auml;r samma l&ouml;senord som till din telefon"
+
+#: ../includes/login.php:454
+msgid ""
+"For password maintenance or assistance, contact your Phone System "
+"Administrator."
+msgstr ""
+"Om du har problem med l&ouml;senord eller beh&ouml;ver hj&auml;lp ska du "
+"kontakta din v&auml;xel ansvarig"
+
+#: ../includes/main.conf.php:152
+msgid "INBOX"
+msgstr "Inbox"
+
+#: ../includes/main.conf.php:154
+msgid "Family"
+msgstr "Familj"
+
+#: ../includes/main.conf.php:156
+msgid "Friends"
+msgstr "V&auml;nner"
+
+#: ../includes/main.conf.php:158
+msgid "Old"
+msgstr "Gamla"
+
+#: ../includes/main.conf.php:160
+msgid "Work"
+msgstr "Arbete"
+
+#: ../includes/main.conf.php:229
+msgid "Directory"
+msgstr "Katalog"
+
+#: ../includes/main.conf.php:230
+msgid "Echo Test"
+msgstr "Eko test"
+
+#: ../includes/main.conf.php:231 ../modules/callmonitor.module:161
+#: ../modules/voicemail.module:324
+msgid "Time"
+msgstr "Tid"
+
+#: ../includes/main.conf.php:232
+msgid "Weather"
+msgstr "V&auml;der"
+
+#: ../includes/main.conf.php:233
+msgid "Schedule wakeup call"
+msgstr "Schemal&auml;gg v&auml;ckningssamtal"
+
+#: ../includes/main.conf.php:234
+msgid "festival test (your extension is XXX)"
+msgstr "Festival test (din anknytning &auml;r XXX)"
+
+#: ../includes/main.conf.php:235
+msgid "Activate Call Waiting (deactivated by default)"
+msgstr "Aktivera Samtal V&auml;ntar"
+
+#: ../includes/main.conf.php:236
+msgid "Deactivate Call Waiting"
+msgstr "Avaktivera Samtal V&auml;ntar"
+
+#: ../includes/main.conf.php:237
+msgid "Call Forwarding System"
+msgstr "Vidarekoppla"
+
+#: ../includes/main.conf.php:238
+msgid "Disable Call Forwarding"
+msgstr "Avaktivera vidarekoppling"
+
+#: ../includes/main.conf.php:239
+#, fuzzy
+msgid "IVR Recording"
+msgstr "R&ouml;stmeny inspelning"
+
+#: ../includes/main.conf.php:240
+msgid "Enable Do-Not-Disturb"
+msgstr "Aktivera St&ouml;r Ej"
+
+#: ../includes/main.conf.php:241
+msgid "Disable Do-Not-Disturb"
+msgstr "Avaktivera St&ouml;r Ej"
+
+#: ../includes/main.conf.php:242
+msgid "Call Forward on Busy"
+msgstr "Vidarekoppla vid upptaget"
+
+#: ../includes/main.conf.php:243
+msgid "Disable Call Forward on Busy"
+msgstr "Avaktivera vidarekoppla vid upptaget"
+
+#: ../includes/main.conf.php:244
+#, fuzzy
+msgid "Message Center (does not ask for extension)"
+msgstr "R&ouml;stbrevl&aring;da (fr&aring;ga ej efter anknytning)"
+
+#: ../includes/main.conf.php:245
+msgid "Enter Message Center"
+msgstr "G&aring; till r&ouml;stbrevl&aring;dan"
+
+#: ../includes/main.conf.php:246
+msgid "Playback IVR Recording"
+msgstr "Spela upp r&ouml;stmeny"
+
+#: ../includes/main.conf.php:247
+msgid "Test Fax"
+msgstr "Fax test"
+
+#: ../includes/main.conf.php:248
+msgid "Simulate incoming call"
+msgstr "Simulera inkommande samtal"
+
+#: ../includes/main.conf.php:289
+msgid "Email voicemail as attachment"
+msgstr "Bifoga meddeladen i E-Post"
+
+#: ../includes/main.conf.php:290
+msgid "Say caller id in recording emailed"
+msgstr "L&auml;ser upp nummret i meddelandet"
+
+#: ../includes/main.conf.php:291
+#, fuzzy
+msgid "Say envelop (date/time) in recording emailed"
+msgstr "L&auml;ser upp informationen i meddelandet"
+
+#: ../includes/main.conf.php:292
+msgid "Delete voicemail when emailed"
+msgstr "Radera meddelandet n&auml;r det e-postats"
+
+#: ../includes/main.conf.php:293
+msgid "Play next message after deleting current message"
+msgstr "Spelar upp n&auml;sta eftera att ha raderat nuvarande"
+
+#: ../includes/main.conf.php:294
+msgid "Ask caller to review their voicemail before sending"
+msgstr ""
+
+#: ../includes/main.conf.php:295
+msgid "Maximum time in seconds a voicemail will record"
+msgstr ""
+
+#: ../modules/callmonitor.module:37 ../modules/callmonitor.module:257
+msgid "Call Monitor"
+msgstr "Samtalsregister"
+
+#: ../modules/callmonitor.module:132
+#, php-format
+msgid "Path is not a directory: %s"
+msgstr "S&oulm;kv&auml;gen leder ej till en mapp: %s"
+
+#: ../modules/callmonitor.module:141 ../modules/voicemail.module:301
+msgid "delete"
+msgstr "Radera"
+
+#: ../modules/callmonitor.module:147
+#, fuzzy
+msgid "duration"
+msgstr "L&auml;ngd"
+
+#: ../modules/callmonitor.module:150
+#, fuzzy
+msgid "ignore"
+msgstr "ignorera"
+
+#: ../modules/callmonitor.module:159 ../modules/voicemail.module:322
+msgid "Date"
+msgstr "Datum"
+
+#: ../modules/callmonitor.module:163 ../modules/voicemail.module:326
+msgid "Caller ID"
+msgstr "Nummerpresentation"
+
+#: ../modules/callmonitor.module:165
+msgid "Source"
+msgstr "K&auml;lla"
+
+#: ../modules/callmonitor.module:167
+msgid "Destination"
+msgstr "M&aring;l"
+
+#: ../modules/callmonitor.module:169
+msgid "Context"
+msgstr "Sammanhang"
+
+#: ../modules/callmonitor.module:171 ../modules/voicemail.module:332
+msgid "Duration"
+msgstr "L&auml;ngd"
+
+#: ../modules/callmonitor.module:202
+msgid "Monitor"
+msgstr "Inspelning"
+
+#: ../modules/callmonitor.module:222 ../modules/voicemail.module:373
+msgid "play"
+msgstr "spela"
+
+#: ../modules/callmonitor.module:259
+#, fuzzy, php-format
+msgid "Call Monitor for %s (%s)"
+msgstr "Samtalsregister f&ouml;r %s (%s)"
+
+#: ../modules/callmonitor.module:311 ../modules/voicemail.module:475
+msgid "select"
+msgstr "Val"
+
+#: ../modules/callmonitor.module:312 ../modules/voicemail.module:476
+msgid "all"
+msgstr "alla"
+
+#: ../modules/callmonitor.module:313 ../modules/voicemail.module:477
+msgid "none"
+msgstr "inga"
+
+#: ../modules/callmonitor.module:533
+msgid "Only deletes recording files, not cdr log"
+msgstr "Raderar endast inspelade filer, inte samtalsloggen"
+
+#: ../modules/conference.module:55
+msgid "My Conference room"
+msgstr ""
+
+#: ../modules/conference.module:78
+#, fuzzy, php-format
+msgid "Conference for %s (%s%s)"
+msgstr "R&ouml;stbrevl&aring;da f&ouml;r %s (%s)"
+
+#: ../modules/help.module:39 ../modules/help.module:68
+msgid "Help"
+msgstr "Hj&auml;lp"
+
+#: ../modules/help.module:70
+#, php-format
+msgid "Help for %s (%s)"
+msgstr "Hj&auml;lp f&ouml;r %s (%s)"
+
+#: ../modules/help.module:77
+msgid "Handset Feature Code"
+msgstr "Kortkoder"
+
+#: ../modules/help.module:80
+msgid "Action"
+msgstr "Utf&ouml;r"
+
+#: ../modules/settings.module:61 ../modules/settings.module:667
+msgid "Settings"
+msgstr "Inst&auml;llningar"
+
+#: ../modules/settings.module:125
+msgid "Call forward number not changed"
+msgstr "Vidarekopplingsnummret ej &auml;ndrat"
+
+#: ../modules/settings.module:126
+#, php-format
+msgid ""
+"Number %s must contain dial numbers (characters like '(', '-', and ')' are "
+"ok)"
+msgstr ""
+"Nummer %s ska inneh&aring;lla nummer (tecknen; '(', '-' och ')' &auml;r "
+"till&aring;tna"
+
+#: ../modules/settings.module:151 ../modules/settings.module:156
+#: ../modules/settings.module:161 ../modules/settings.module:166
+#: ../modules/settings.module:176 ../modules/settings.module:181
+msgid "Voicemail password not changed"
+msgstr "L&ouml;senord f&ouml;r r&ouml;stbrevl&aring;dan har inte &auml;ndrats"
+
+#: ../modules/settings.module:152
+msgid "Password and password confirm must not be blank"
+msgstr ""
+"L&ouml;senord och bekr&auml;fta l&ouml;senord f&aring;r inte vara tomma"
+
+#: ../modules/settings.module:157
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and greater than %d digits"
+msgstr "L&ouml;senordet m&aring;ste vara %d siffror"
+
+#: ../modules/settings.module:162
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %d digits"
+msgstr "L&ouml;senordet m&aring;ste vara %d siffror"
+
+#: ../modules/settings.module:167
+msgid "Password and password confirm do not match"
+msgstr "L&ouml;senord och bekr&auml;ftat l&ouml;senord st&auml;mmer inte"
+
+#: ../modules/settings.module:177 ../modules/settings.module:182
+#: ../modules/settings.module:234 ../modules/settings.module:239
+#, fuzzy, php-format
+msgid "%s does not exist or is not writable"
+msgstr "%s finns ej eller &auml;r ej l&auml;sbar"
+
+#: ../modules/settings.module:223
+#, fuzzy
+msgid "Voicemail email and pager address not changed"
+msgstr "L&ouml;senord f&ouml;r r&ouml;stbrevl&aring;dan har inte &auml;ndrats"
+
+#: ../modules/settings.module:233 ../modules/settings.module:238
+#, fuzzy
+msgid "Voicemail email settings not changed"
+msgstr "L&ouml;senord f&ouml;r r&ouml;stbrevl&aring;dan har inte &auml;ndrats"
+
+#: ../modules/settings.module:385
+msgid "Language:"
+msgstr "Spr&aring;k:"
+
+#: ../modules/settings.module:408
+#, fuzzy
+msgid "Call Routing"
+msgstr "Inst&auml;llningar f&ouml;r Vidarekoppling"
+
+#: ../modules/settings.module:411
+#, fuzzy
+msgid "Call Forwarding:"
+msgstr "Vidarekoppling"
+
+#: ../modules/settings.module:419 ../modules/settings.module:507
+#, fuzzy
+msgid "Enable"
+msgstr "Aktivera"
+
+#: ../modules/settings.module:431
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and only %s digits"
+msgstr "L&ouml;senordet m&aring;ste vara %s siffror"
+
+#: ../modules/settings.module:434
+#, fuzzy, php-format
+msgid "Passwords must be all numbers and at least %s digits"
+msgstr "L&ouml;senordet m&aring;ste vara %s siffror"
+
+#: ../modules/settings.module:439
+#, fuzzy
+msgid "Voicemail Password:"
+msgstr "L&ouml;senord f&ouml;r r&ouml;stbrevl&aring;da"
+
+#: ../modules/settings.module:445
+msgid "Enter again to confirm:"
+msgstr "Bekr&auml;fta:"
+
+#: ../modules/settings.module:492
+#, fuzzy
+msgid "Email Voicemail To:"
+msgstr "R&ouml;stbrevl&aring;da"
+
+#: ../modules/settings.module:498
+#, fuzzy
+msgid "Pager Voicemail To:"
+msgstr "R&ouml;stbrevl&aring;da"
+
+#: ../modules/settings.module:558
+msgid "Audio Format:"
+msgstr "Ljud format:"
+
+#: ../modules/settings.module:561
+msgid "Best Quality"
+msgstr "B&auml;sta kvaliten"
+
+#: ../modules/settings.module:562
+msgid "Smallest Download"
+msgstr "Minsta storlek"
+
+#: ../modules/settings.module:570
+msgid "Voicemail Settings"
+msgstr "Inst&auml;llningar f&ouml;r R&ouml;stbrevl&aring;da"
+
+#: ../modules/settings.module:611
+msgid "Call Monitor Settings"
+msgstr "Inst&auml;llningar f&ouml;r Samtalsregister"
+
+#: ../modules/settings.module:614
+msgid "Record INCOMING:"
+msgstr "Spela in inkommande samtal:"
+
+#: ../modules/settings.module:616 ../modules/settings.module:624
+msgid "Always"
+msgstr "Alltid"
+
+#: ../modules/settings.module:617 ../modules/settings.module:625
+msgid "Never"
+msgstr "Aldrig"
+
+#: ../modules/settings.module:618 ../modules/settings.module:626
+msgid "On-Demand"
+msgstr "Vid behov"
+
+#: ../modules/settings.module:622
+msgid "Record OUTGOING:"
+msgstr "Spela in utg&aring;ende samtal:"
+
+#: ../modules/settings.module:669
+#, fuzzy, php-format
+msgid "Settings for %s (%s)"
+msgstr "Inst&auml;llningar f&ouml;r %s (%s)"
+
+#: ../modules/settings.module:705
+msgid "Update"
+msgstr "Uppdatera"
+
+#: ../modules/voicemail.module:45
+msgid "Voicemail"
+msgstr "R&ouml;stbrevl&aring;da"
+
+#: ../modules/voicemail.module:164
+msgid "A folder must be selected before the message can be moved."
+msgstr "En mapp m&aring;sta v&auml;ljas innan meddelandet kan flyttas."
+
+#: ../modules/voicemail.module:178
+msgid "An extension must be selected before the message can be forwarded."
+msgstr ""
+"En anknytning m&aring;ste v&auml;ljas innan meddelandet kan vidarebefodras."
+
+#: ../modules/voicemail.module:304
+msgid "move_to"
+msgstr "Flytta till"
+
+#: ../modules/voicemail.module:307
+#, fuzzy
+msgid "Folder"
+msgstr "Mappar"
+
+#: ../modules/voicemail.module:311
+msgid "forward_to"
+msgstr "Vidarebefodra till"
+
+#: ../modules/voicemail.module:328
+msgid "Priority"
+msgstr "Prioritet"
+
+#: ../modules/voicemail.module:330
+msgid "Orig Mailbox"
+msgstr "Ursprunglig r&ouml;stbrevl&aring;da"
+
+#: ../modules/voicemail.module:362
+msgid "Message"
+msgstr "Meddelande"
+
+#: ../modules/voicemail.module:377
+msgid "Voicemail recording(s) was not found."
+msgstr "R&ouml;stmeddelande hittades inte."
+
+#: ../modules/voicemail.module:378
+#, php-format
+msgid ""
+"On settings page, change voicemail audio format. It is currently set to %s"
+msgstr ""
+"P&aring; inst&auml;llningssidan, &auml;ndra r&ouml;stbrevl&aring;dans "
+"ljudformat. Det &auml;r nu %s"
+
+#: ../modules/voicemail.module:405
+#, fuzzy
+msgid "Voicemail Login not found."
+msgstr "Hittar inte r&ouml;stbrevl&aring;da."
+
+#: ../modules/voicemail.module:406
+msgid "No access to voicemail"
+msgstr "Inget tilltr&auml;de till r&ouml;stbrevl&aring;dan"
+
+#: ../modules/voicemail.module:412
+msgid "No Voicemail Recordings for Admin"
+msgstr "Inga r&ouml;stmeddelande f&ouml;r Admin"
+
+#: ../modules/voicemail.module:428
+#, fuzzy, php-format
+msgid "Voicemail for %s (%s)"
+msgstr "R&ouml;stbrevl&aring;da f&ouml;r %s (%s)"
+
+#: ../modules/voicemail.module:678
+#, fuzzy, php-format
+msgid "Could not create mailbox folder %s on the server"
+msgstr "Kan inte skapa mapp f&ouml;r r&ouml;stbrevl&aring;da"
+
+#: ../modules/voicemail.module:718
+#, php-format
+msgid "Permission denied on folder %s or %s"
+msgstr "Saknar r&auml;ttigheter f&ouml;r mappen %s eller %s"
+
+#: ../misc/recording_popup.php:39
+msgid "download"
+msgstr "ladda ner"
+
+#~ msgid "Folders"
+#~ msgstr "Mappar"
+
+#~ msgid "Version"
+#~ msgstr "Version"
+
+#~ msgid "Passwords must be all numbers and only 4 digits"
+#~ msgstr "L&ouml;senordet m&aring;ste vara 4 siffror"
+
+#~ msgid "Unable to connect to Asterisk Manager"
+#~ msgstr "Kan ej ansluta till Asterisk Manager"
+
+#, fuzzy
+#~ msgid "No Asterisk Manager Interface connection"
+#~ msgstr "Asterisk Call Manager svara ej"
+
+#~ msgid "of"
+#~ msgstr "av"
+
+#~ msgid "Login used"
+#~ msgstr "Anv&auml;nd Login"
+
+#~ msgid "help"
+#~ msgstr "hj&auml;lp"
+
+#~ msgid "not a directory or not readable"
+#~ msgstr "inte en mapp eller ej l&auml;sbar"
+
+#~ msgid "Use your"
+#~ msgstr "Anv&auml;nd din"
+
+#~ msgid "for"
+#~ msgstr "f&ouml;r"
+
+#~ msgid "Password must be all numbers and 4 digits"
+#~ msgstr "L&ouml;senordet m&aring;ste vara 4 siffror"
+
+#~ msgid "Check voicemail audio format on settings page to change from"
+#~ msgstr ""
+#~ "&Auml;ndra inst&auml;llningar f&ouml;r r&ouml;stbrevl&aring;dans ljud "
+#~ "format f&ouml;r att &auml;ndra fr&aring;n"
+
+#~ msgid "on the server"
+#~ msgstr "p&aring; servern"
+
+#~ msgid "No database connection"
+#~ msgstr "Ingen kontakt med databasen"
diff --git a/fs_selfservice/fri/misc/audio.php b/fs_selfservice/fri/misc/audio.php
new file mode 100644
index 0000000..2dc355c
--- /dev/null
+++ b/fs_selfservice/fri/misc/audio.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * plays recording file
+ */
+
+
+
+if (isset($_GET['recording'])) {
+
+ chdir("..");
+ include_once("./includes/bootstrap.php");
+
+ global $ARI_CRYPT_PASSWORD;
+
+ $crypt = new Crypt();
+
+ $path = $crypt->decrypt($_GET['recording'],$ARI_CRYPT_PASSWORD);
+
+ // strip ".." from path for security
+ $path = preg_replace('/\.\./','',$path);
+
+ // See if the file exists
+ if (!is_file($path)) { die("<b>404 File not found!</b>"); }
+
+ // Gather relevent info about file
+ $size = filesize($path);
+ $name = basename($path);
+ $extension = strtolower(substr(strrchr($name,"."),1));
+
+ // This will set the Content-Type to the appropriate setting for the file
+ $ctype ='';
+ switch( $extension ) {
+ case "mp3": $ctype="audio/mpeg"; break;
+ case "wav": $ctype="audio/x-wav"; break;
+ case "Wav": $ctype="audio/x-wav"; break;
+ case "WAV": $ctype="audio/x-wav"; break;
+ case "gsm": $ctype="audio/x-gsm"; break;
+
+ // not downloadable
+ default: die("<b>404 File not found!</b>"); break ;
+ }
+
+ // need to check if file is mislabeled or a liar.
+ $fp=fopen($path, "rb");
+ if ($size && $ctype && $fp) {
+ header("Pragma: public");
+ header("Expires: 0");
+ header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+ header("Cache-Control: public");
+ header("Content-Description: wav file");
+ header("Content-Type: " . $ctype);
+ header("Content-Disposition: attachment; filename=" . $name);
+ header("Content-Transfer-Encoding: binary");
+ header("Content-length: " . $size);
+ fpassthru($fp);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/fs_selfservice/fri/misc/popup.css b/fs_selfservice/fri/misc/popup.css
new file mode 100644
index 0000000..7a53528
--- /dev/null
+++ b/fs_selfservice/fri/misc/popup.css
@@ -0,0 +1,10 @@
+/*
+ * popup
+ */
+
+.popup_download {
+ color: #105D90;
+ margin: 250px;
+ font-size: 12px;
+ text-align: right;
+} \ No newline at end of file
diff --git a/fs_selfservice/fri/misc/recording_popup.php b/fs_selfservice/fri/misc/recording_popup.php
new file mode 100644
index 0000000..1546adc
--- /dev/null
+++ b/fs_selfservice/fri/misc/recording_popup.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * popup window for playing recording
+ */
+
+chdir("..");
+include_once("./includes/bootstrap.php");
+
+?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <TITLE>ARI</TITLE>
+ <link rel="stylesheet" href="popup.css" type="text/css">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ </head>
+ <body>
+
+<?php
+
+ global $ARI_CRYPT_PASSWORD;
+
+ $crypt = new Crypt();
+
+ $path = $crypt->encrypt($_GET['recording'],$ARI_CRYPT_PASSWORD);
+
+ if (isset($path)) {
+ if (isset($_GET['date'])) {
+ echo($_GET['date'] . "<br>");
+ }
+ if (isset($_GET['time'])) {
+ echo($_GET['time'] . "<br>");
+ }
+ echo("<br>");
+ echo("<embed src='audio.php?recording=" . $path . "' width=300, height=20 autoplay=true loop=false></embed><br>");
+ echo("<a class='popup_download' href=/recordings/misc/audio.php?recording=" . $path . ">" . _("download") . "</a><br>");
+ }
+
+?>
+
+ </body>
+</html>
+
diff --git a/fs_selfservice/fri/modules.template/blank.module b/fs_selfservice/fri/modules.template/blank.module
new file mode 100644
index 0000000..a3676c4
--- /dev/null
+++ b/fs_selfservice/fri/modules.template/blank.module
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the help page
+ */
+
+/**
+ * Class for help
+ */
+class blank {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 8;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=blank&f=display'>" . _("Blank") . "</a></small></small></p><br>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ global $ARI_HELP_FEATURE_CODES;
+
+ $display = new Display();
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $header_text = _("Blank");
+ if (!$_SESSION['ari_user']['admin_help']) {
+ $header_text .= sprintf(_(" for %s (%s)"), $displayname, $extension);
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText($header_text);
+ $ret .= $display->displayLine();
+
+ $ret .= 'Blank goes here';
+
+ return $ret;
+ }
+
+}
+
+?>
diff --git a/fs_selfservice/fri/modules/VmX.module b/fs_selfservice/fri/modules/VmX.module
new file mode 100644
index 0000000..61ef653
--- /dev/null
+++ b/fs_selfservice/fri/modules/VmX.module
@@ -0,0 +1,661 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the call monitor recordings
+ */
+
+/**
+ * Class for Followme
+ */
+class VmX {
+
+ var $protocol_table;
+ var $protocol_config_files;
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 6;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ global $SETTINGS_ALLOW_VMX_SETTINGS;
+ global $ARI_ADMIN_USERNAME;
+
+ $ret = "";
+
+ // We are only going to show the menu
+ // if VmX is allowed
+ if ($SETTINGS_ALLOW_VMX_SETTINGS) {
+
+ $exten = $_SESSION['ari_user']['extension'];
+
+ // and we are not logged in as admin
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+
+ $vmx_enabled = $this->getVmxState($exten,'unavail');
+
+ // and vmx is enabled for this user
+ if ($vmx_enabled !== false)
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=VmX&f=display'>" . _("VmX&#8482 Locator") . "</a></small></small></p>";
+ }
+ }
+
+ return $ret;
+ }
+
+ /*
+ * Acts on the user settings
+ *
+ * @param $args
+ * Common arguments
+ * @param $a
+ * action
+ */
+ function action($args) {
+
+ global $STANDALONE;
+ global $ARI_ADMIN_USERNAME;
+ global $SETTINGS_ALLOW_VMX_SETTINGS;
+
+ // args
+ $m = getArgument($args,'m');
+ $a = getArgument($args,'a');
+
+ $follow_me_disabled = getArgument($args,'follow_me_disabled');
+
+ $vmx_option_0_number = getArgument($args, 'vmx_option_0_number');
+ $vmx_option_0_system_default = getArgument($args, 'vmx_option_0_system_default');
+ $vmx_option_1_number = getArgument($args, 'vmx_option_1_number');
+ $vmx_option_1_system_default = getArgument($args, 'vmx_option_1_system_default');
+ $vmx_option_2_number = getArgument($args, 'vmx_option_2_number');
+ $vmx_unavail_enabled = getArgument($args, 'vmx_unavail_enabled');
+ $vmx_busy_enabled = getArgument($args, 'vmx_busy_enabled');
+ $vmx_play_instructions = getArgument($args, 'vmx_play_instructions');
+ $vmx_disabled = getArgument($args, 'vmx_disabled');
+
+ $exten = $_SESSION['ari_user']['extension'];
+
+ // The action is 'update
+ if ($a=='update') {
+
+ $follow_me_disabled = ($this->getFollowMeListRingTime($exten) > 0)?0:1;
+
+
+ $vmx_disabled = $this->getVmxState($exten,'unavail');
+ if ($vmx_disabled === false) {
+ $vmx_disabled = true;
+ $SETTINGS_ALLOW_VMX_SETTINGS=false;
+ } else {
+ $vmx_disabled = false;
+ }
+ if ($vmx_disabled) {
+
+ setcookie("ari_vmx_disabled", $vmx_disabled, time()+365*24*60*60);
+ $vmx_disabled_delayed = $vmx_disabled;
+ $_SESSION['ari_error'] =
+ _("Your Premium VmX Locator service has been disabled, REFRESH your browser to remove this message") . "<br>" .
+ sprintf(_("Check with your Telephone System Administrator if you think there is a problem"));
+ }
+
+ if (! $vmx_disabled) {
+
+ // set database
+ $this->setVmxState($exten,'unavail',$vmx_unavail_enabled);
+ $this->setVmxState($exten,'busy',$vmx_busy_enabled);
+ $this->setVmxPlayInstructions($exten,'unavail',$vmx_play_instructions);
+ $this->setVmxPlayInstructions($exten,'busy',$vmx_play_instructions);
+
+ // store cookie
+ setcookie("ari_vmx_unavail_enabled", $vmx_unavail_enabled, time()+365*24*60*60);
+ setcookie("ari_vmx_busy_enabled", $vmx_busy_enabled, time()+365*24*60*60);
+ setcookie("ari_vmx_play_instructions", $vmx_play_instructions, time()+365*24*60*60);
+
+ $stripped_vmx_option_0_number = preg_replace('/-|\(|\)|\s/','',$vmx_option_0_number);
+
+ if ($vmx_option_0_system_default) {
+ $this->setVmxOptionNumber($exten,'0','unavail',"");
+ $this->setVmxOptionNumber($exten,'0','busy',"");
+ setcookie("ari_vmx_option_0_system_default", $vmx_option_0_system_default, time()+365*24*60*60);
+ if (is_numeric($stripped_vmx_option_0_number) || !$stripped_vmx_option_0_number) {
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_vmx_option_0_number']);
+ if ($vmx_option_0_number && $stripped!=$stripped_vmx_option_0_number) {
+ setcookie("ari_vmx_option_0_number", $call_vmx_option_0_number, time()+365*24*60*60);
+ }
+ }
+ } else {
+ if (!is_numeric($stripped_vmx_option_0_number) && $stripped_vmx_option_0_number) {
+ $_SESSION['ari_error'] =
+ _("Option 0 not changed") . "<br>" .
+ sprintf(_("Number %s must contain dial numbers (characters like '(', '-', and ')' are ok)"),$vmx_option_0_number);
+ }
+ else {
+
+ // set database
+ $this->setVmxOptionNumber($exten,'0','unavail',$stripped_vmx_option_0_number);
+ $this->setVmxOptionNumber($exten,'0','busy',$stripped_vmx_option_0_number);
+
+ // store cookie
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_vmx_option_0_number']);
+ if ($vmx_option_0_number && $stripped!=$stripped_vmx_option_0_number) {
+ setcookie("ari_vmx_option_0_number", $call_vmx_option_0_number, time()+365*24*60*60);
+ }
+ }
+ }
+
+ $stripped_vmx_option_1_number = preg_replace('/-|\(|\)|\s/','',$vmx_option_1_number);
+ if ($vmx_option_1_system_default && !$follow_me_disabled) {
+ $this->setVmxOptionFollowMe($exten,'1','unavail');
+ $this->setVmxOptionFollowMe($exten,'1','busy');
+ setcookie("ari_vmx_option_1_system_default", $vmx_option_1_system_default, time()+365*24*60*60);
+ if (is_numeric($stripped_vmx_option_1_number) || !$stripped_vmx_option_1_number) {
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_vmx_option_1_number']);
+ if ($vmx_option_1_number && $stripped!=$stripped_vmx_option_1_number) {
+ setcookie("ari_vmx_option_1_number", $call_vmx_option_1_number, time()+365*24*60*60);
+ }
+ }
+ }
+ else {
+
+ if (!is_numeric($stripped_vmx_option_1_number) && $stripped_vmx_option_1_number) {
+ $_SESSION['ari_error'] =
+ _("Option 1 not changed") . "<br>" .
+ sprintf(_("Number %s must contain dial numbers (characters like '(', '-', and ')' are ok)"),$vmx_option_1_number);
+ }
+ else {
+
+ // set database
+ $this->setVmxOptionNumber($exten,'1','unavail',$stripped_vmx_option_1_number);
+ $this->setVmxOptionNumber($exten,'1','busy',$stripped_vmx_option_1_number);
+
+ // store cookie
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_vmx_option_1_number']);
+ if ($vmx_option_1_number && $stripped!=$stripped_vmx_option_1_number) {
+ setcookie("ari_vmx_option_1_number", $call_vmx_option_1_number, time()+365*24*60*60);
+ }
+ }
+ }
+
+ $stripped_vmx_option_2_number = preg_replace('/-|\(|\)|\s/','',$vmx_option_2_number);
+ if (!is_numeric($stripped_vmx_option_2_number) && $stripped_vmx_option_2_number) {
+ $_SESSION['ari_error'] =
+ _("Option 2 not changed") . "<br>" .
+ sprintf(_("Number %s must contain dial numbers (characters like '(', '-', and ')' are ok)"),$vmx_option_2_number);
+ }
+ else {
+
+ // set database
+ $this->setVmxOptionNumber($exten,'2','unavail',$stripped_vmx_option_2_number);
+ $this->setVmxOptionNumber($exten,'2','busy',$stripped_vmx_option_2_number);
+
+ // store cookie
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_vmx_option_2_number']);
+ if ($vmx_option_2_number && $stripped!=$stripped_vmx_option_2_number) {
+ setcookie("ari_vmx_option_2_number", $call_vmx_option_2_number, time()+365*24*60*60);
+ }
+ }
+ } // vmx_disabled false
+ }
+
+ // redirect to see updated page
+ $ret .= "
+ <head>
+ <script>
+ <!--
+ window.location = \"" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "\"
+ // -->
+ </script>
+ </head>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+ global $SETTINGS_ALLOW_VMX_SETTINGS;
+
+ global $loaded_modules;
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $exten = $_SESSION['ari_user']['extension'];
+
+ $display = new DisplaySearch();
+
+ $follow_me_listring_time = $this->getFollowMeListRingTime($exten);
+
+ //TODO: Set this better than this?
+ $follow_me_disabled = ($follow_me_listring_time > 0)?0:1;
+ setcookie("ari_follow_me_disabled", $follow_me_disabled, time()+365*24*60*60);
+
+
+ $vmx_unavail_enabled=$this->getVmxState($exten,'unavail');
+ if ($vmx_unavail_enabled === false) {
+ $vmx_disabled = true;
+ setcookie("ari_vmx_disabled", $vmx_disabled, time()+365*24*60*60);
+ $SETTINGS_ALLOW_VMX_SETTINGS=false;
+ } else {
+ $vmx_disabled = false;
+ setcookie("ari_vmx_disabled", false, time()+365*24*60*60);
+ $vmx_busy_enabled=$this->getVmxState($exten,'busy');
+ $vmx_play_instructions=$this->getVmxPlayInstructions($exten);
+ $vmx_option_0_number=$this->getVmxOptionNumber($exten,'0');;
+ $vmx_option_1_number=$this->getVmxOptionNumber($exten,'1');;
+ $vmx_option_2_number=$this->getVmxOptionNumber($exten,'2');;
+
+ if (is_numeric($vmx_option_0_number)) {
+ $vmx_option_0_system_default='';
+ $vmx_option_0_number_text_box_options='';
+ } else {
+ $vmx_option_0_system_default='checked';
+ $vmx_option_0_number_text_box_options="disabled style='background: #DDD;'";
+ }
+
+ // if follow-me is enabled then the options are a numberic value (dial a phone number)
+ // or a followme target (FMnnn) which should not be displayed but means the box is checked
+ // or otherwise blank (or garbage in which case blank it)
+ //
+ if (!$follow_me_disabled) {
+ $vmx_option_1_system_default=$this->getVmxOptionFollowMe($exten,'1');
+ if ($vmx_option_1_system_default) {
+ $vmx_option_1_number = '';
+ $vmx_option_1_number_text_box_options="disabled style='background: #DDD;'";
+ }
+ }
+ }
+
+ $set_vmx_text .=
+ "
+ <br>
+ <table class='settings'>
+ <tr>
+ <td><a href='#' class='info'>" . _("Use When:") . "<span>" . _("Menu options below are available during your personal voicemail greeting playback. <br/><br/>Check both to use at all times.") . "<br></span></a></td> <td>
+ <input " . $vmx_unavail_enabled . " type=checkbox name='vmx_unavail_enabled' value='checked'>
+ <small>" . _("unavailable") . "</small>
+ </td>
+ <td>
+ <input " . $vmx_busy_enabled . " type=checkbox name='vmx_busy_enabled' value='checked'>
+ <small>" . _("busy") . "</small>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='#' class='info'>" . _("Voicemail Instructions:") ."<span>" . _("Uncheck to play a beep after your personal voicemail greeting.") . "<br></span></a></td>
+ <td>
+ <input " . $vmx_play_instructions . " type=checkbox name='vmx_play_instructions' value='checked'>
+ <small>" . _("Standard voicemail prompts.") . "</small>
+ </td>
+ </tr>
+ </table>
+ <br>
+ <br>
+ <table class='settings'>
+ <tr>
+ <td><a href='#' class='info'>" . _("Press 0:") . "<span>" . _("Pressing 0 during your personal voicemail greeing goes to the Operator.
+ Uncheck to enter another destination here.") . "<br></span></a>
+ </td>
+ <td>
+ <input " . $vmx_option_0_number_text_box_options . " name='vmx_option_0_number' type='text' size=24 value='" . $vmx_option_0_number . "'>
+ </td>
+ <td>
+ <input " . $vmx_option_0_system_default . " type=checkbox name='vmx_option_0_system_default' value='checked' OnClick=\"disable_fields(); return true;\">
+ <small>" . _("Go To Operator") . "</small>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='#' class='info'>" . _("Press 1:") . "<span>";
+
+ if ($follow_me_disabled)
+ $set_vmx_text .= _("The remaining options can have internal extensions, ringgroups, queues and external numbers that may be rung. It is often used to include your cell phone. You should run a test to make sure that the number is functional any time a change is made so you don't leave a caller stranded or receiving invalid number messages.");
+ else
+ $set_vmx_text .= _("Enter an alternate number here, then change your personal voicemail greeting to let callers know to press 1 to reach that number. <br/><br/>If you'd like to use your Follow Me List, check \"Send to Follow Me\" and disable Follow Me above.");
+
+
+ $set_vmx_text .=
+ " <br></span></a>
+ </td>
+ <td>
+ <input " . $vmx_option_1_number_text_box_options . " name='vmx_option_1_number' type='text' size=24 value='" . $vmx_option_1_number . "'>
+ </td>
+ <td>";
+
+
+ if (!$follow_me_disabled)
+ $set_vmx_text .= "<input " . $vmx_option_1_system_default . " type=checkbox name='vmx_option_1_system_default' value='checked' OnClick=\"disable_fields(); return true;\"><small>" . _("Send to Follow-Me") . "</small>";
+
+
+ $set_vmx_text .=
+ "
+ </td>
+ </tr>
+ <tr>
+ <td><a href='#' class='info'>" . _("Press 2:") . "<span>" . _("Use any extensions, ringgroups, queues or external numbers. <br/><br/>Remember to re-record your personal voicemail greeting and include instructions. Run a test to make sure that the number is functional.") . "<br></span></a></td>
+ <td>
+ <input " . $vmx_option_2_number_text_box_options . " name='vmx_option_2_number' type='text' size=24 value='" . $vmx_option_2_number . "'>
+ </td>
+ </tr>
+ </table>
+ <br>
+ <br>
+ ";
+
+
+ // Now we should be ready to build the page
+ $ret .= checkErrorMessage();
+
+ $headerText = sprintf(_("VmX Locator&#8482; Settings for %s (%s)"),$displayname,$exten);
+
+ $ret .= $display->displayHeaderText($headerText);
+ $ret .= $display->displayLine();
+
+ $ret .=
+ "<SCRIPT LANGUAGE='JavaScript'>
+ <!-- Begin
+ function disable_fields() {
+
+ if (document.ari_settings.vmx_option_0_system_default.checked) {
+ document.ari_settings.vmx_option_0_number.style.backgroundColor = '#DDD';
+ document.ari_settings.vmx_option_0_number.disabled = true;
+ }
+ else {
+ document.ari_settings.vmx_option_0_number.style.backgroundColor = '#FFF';
+ document.ari_settings.vmx_option_0_number.disabled = false;
+ }";
+
+ if (!$follow_me_disabled) {
+ $ret .= "
+ if (document.ari_settings.vmx_option_1_system_default.checked) {
+ document.ari_settings.vmx_option_1_number.style.backgroundColor = '#DDD';
+ document.ari_settings.vmx_option_1_number.disabled = true;
+ }
+ else {
+ document.ari_settings.vmx_option_1_number.style.backgroundColor = '#FFF';
+ document.ari_settings.vmx_option_1_number.disabled = false;
+ }";
+ }
+ $ret .=
+ "}
+ // End -->
+ </script>";
+
+ $ret .=
+ "<form class='settings' name='ari_settings' action='' method='GET'>
+ <input type=hidden name=m value=" . $m . ">
+ <input type=hidden name=f value='action'>
+ <input type=hidden name=a value='update'>
+ " . $set_vmx_text . "
+ <br>
+ <input name='submit' type='submit' value='" . _("Update") . "'>
+ </form>";
+
+ return $ret;
+ }
+
+ /*
+ * Gets VMX option FollowMe
+ *
+ * @param $exten
+ * Extension to get information about
+ * @param $digit
+ * Option number to get
+ * @param $mode
+ * Mode to get (unavail/busy)
+ * @return $response
+ * checked if set to got to extesion's follow-me on this option
+ */
+ function getVmxOptionFollowMe($exten, $digit, $mode='unavail') {
+
+ global $asterisk_manager_interface;
+
+ $digit = trim($digit);
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/vmx/$mode/$digit/ext\r\n\r\n");
+ return (($response == 'FM'.$exten) ? 'checked':'');
+ }
+
+ /*
+ * Sets VMX option FollowMe
+ *
+ * @param $exten
+ * Extension to set information about
+ * @param $digit
+ * Option number to set
+ * @param $mode
+ * Mode to set (unavail/busy)
+ * @param $context
+ * Context to set ext to (default from-findmefollow)
+ * @param $priority
+ * Priority to set ext to (default 1)
+ */
+ function setVmxOptionFollowMe($exten, $digit, $mode, $context='ext-findmefollow', $priority='1') {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = "FM$exten";
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/$digit/ext $value_opt\r\n\r\n");
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/$digit/context $context\r\n\r\n");
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/$digit/pri $priority\r\n\r\n");
+ }
+
+ /*
+ * Gets VMX option number
+ *
+ * @param $exten
+ * Extension to get information about
+ * @param $digit
+ * Option number to get
+ * @param $mode
+ * Mode to get (unavail/busy)
+ * @return $number
+ * Number to use or blank if disabled
+ */
+ function getVmxOptionNumber($exten, $digit, $mode='unavail') {
+
+ global $asterisk_manager_interface;
+
+ $number = '';
+ $digit = trim($digit);
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/vmx/$mode/$digit/ext\r\n\r\n");
+ if (is_numeric($response)) {
+ $number = $response;
+ }
+
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE["ari_vmx_option_${digit}_number"]);
+ if ($stripped==$number) {
+ $number = $_COOKIE["ari_vmx_option_${digit}_number"];
+ }
+
+ return $number;
+ }
+
+ /*
+ * Sets VMX option number
+ *
+ * @param $exten
+ * Extension to set information about
+ * @param $digit
+ * Option number to set
+ * @param $mode
+ * Mode to set (unavail/busy)
+ * @param $number
+ * Number to set ext to (blank will delete it)
+ * @param $context
+ * Context to set ext to (default from-internal)
+ * @param $priority
+ * Priority to set ext to (default 1)
+ */
+ function setVmxOptionNumber($exten, $digit, $mode, $number, $context='from-internal', $priority='1') {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = trim($number);
+
+ if (is_numeric($value_opt)) {
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/$digit/ext $value_opt\r\n\r\n");
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/$digit/context $context\r\n\r\n");
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/$digit/pri $priority\r\n\r\n");
+ } else {
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database deltree AMPUSER $exten/vmx/$mode/$digit\r\n\r\n");
+ }
+ }
+
+ /*
+ * Sets VMX State
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $mode
+ * Mode to set (unavail/busy)
+ * @param $vmx_state
+ * enabled/disabled state based on check box value
+ */
+ function setVmxState($exten,$mode,$vmx_state) {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = ($vmx_state)?'enabled':'disabled';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/state $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Gets VMX State
+ *
+ * @param $exten
+ * Extension to get information about
+ * @param $mode
+ * Mode to get (unavail/busy)
+ * @return $data
+ * state of variable (checked/blank) or false if no poper value
+ */
+ function getVmxState($exten, $mode='unavail') {
+
+ global $asterisk_manager_interface;
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/vmx/$mode/state\r\n\r\n");
+
+ if (preg_match("/enabled/",$response)) {
+ $response='checked';
+ }
+ elseif (preg_match("/disabled/",$response)) {
+ $response='';
+ }
+ else {
+ $response = false;
+ }
+
+ //TODO: really need to check for a bogus response, see how other side does it
+ //
+ return $response;
+
+ }
+
+ /*
+ * Sets VMX Play Instructions
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $vmx_play_instructions
+ * play instructions or just beep (checked, blank)
+ * @param $mode
+ * Mode to set (unavail/busy)
+ */
+ function setVmxPlayInstructions($exten,$mode,$vmx_play_instructions) {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = ($vmx_play_instructions)?'""':'s';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/vmx/$mode/vmxopts/timeout $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Get VMX Play Instructions
+ *
+ * @param $exten
+ * Extension to get information about
+ * @param $mode
+ * Mode to get (unavail/busy)
+ * @return $data
+ * state of variable (checked/blank) or false if no poper value
+ */
+ function getVmxPlayInstructions($exten, $mode='unavail') {
+
+ global $asterisk_manager_interface;
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/vmx/$mode/vmxopts/timeout\r\n\r\n");
+
+ if (preg_match("/s/",$response)) {
+ $response='';
+ }
+ else {
+ $response='checked';
+ }
+
+ //TODO: really need to check for a bogus response, see how other side does it
+ //
+ return $response;
+
+ }
+
+
+ /*
+ * Gets Follow Me List-Ring Time if set
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $number
+ * follow me list-ring time returned if set
+ */
+ function getFollowMeListRingTime($exten) {
+
+ global $asterisk_manager_interface;
+
+ $number = '';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/followme/grptime\r\n\r\n");
+ if (is_numeric($response)) {
+ $number = $response;
+ }
+
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_follow_me_listring_time']);
+ if ($stripped==$number) {
+ $number = $_COOKIE['ari_follow_me_listring_time'];
+ }
+
+ return $number;
+ }
+
+
+} // class
+
+?>
diff --git a/fs_selfservice/fri/modules/billing.module b/fs_selfservice/fri/modules/billing.module
new file mode 100644
index 0000000..6ef16e5
--- /dev/null
+++ b/fs_selfservice/fri/modules/billing.module
@@ -0,0 +1,250 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the help page
+ */
+
+/**
+ * Class for help
+ */
+class billing {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = -2;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=billing&f=display'>" . _("Billing") . "</a></small></small></p><br>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ $display = new Display();
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $header_text = _("Billing");
+ if (!$_SESSION['ari_user']['admin_help']) {
+ $header_text .= sprintf(_(" for %s (%s)"), $displayname, $extension);
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText($header_text);
+ $ret .= $display->displayLine();
+
+
+ $freeside = new FreesideSelfService();
+
+ $fs_info = $freeside->customer_info( array(
+ 'session_id' => $_SESSION['freeside_session_id'],
+ ) );
+ $error = $fs_info['error'];
+ if ( $error ) {
+ //$_SESSION['ari_error'] = _("Incorrect Username or Password");
+ $_SESSION['ari_error'] = $error; #// XXX report as ari_error???!
+ }
+
+ //$ret .= $fs_info['small_custview'];
+ //$ret .= '<BR>';
+
+ $ret .= 'Balance: <b>$'. $fs_info['balance']. '</b><BR><BR>';
+
+ if ( $fs_info['balance'] > 0 ) {
+
+ #$ret .= '<B><A HREF="'. $_SESSION['ARI_ROOT'].
+ # '?m=billing&f=make_payment">Make a payment</A></B><BR><BR>';
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=make_payment">Make a payment</A></B><BR><BR>';
+
+ }
+
+ // XXX count() ???
+ if ( count($fs_info['open_invoices']) ) {
+
+ $ret .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#ff6666" COLSPAN=5>Open Invoices</TH></TR>';
+ $link = '<A HREF="'. $_SESSION['ARI_ROOT'].
+ '?m=billing&f=view_invoice&invnum=';
+
+ $col1 = "eeeeee";
+ $col2 = "cccccc";
+ $col = $col1;
+
+ while ( $i = each($fs_info['open_invoices']) ) {
+
+ $invoice = $i[value];
+
+ $td = '<TD BGCOLOR="#'. $col. '">';
+ $a = $link. $invoice['invnum']. '">';
+ $ret .=
+ "<TR>$td$a". 'Invoice #'. $invoice['invnum']. "</A></TD>$td</TD>".
+ "$td$a". $invoice['date']. "</A></TD>$td</TD>".
+ '<TD BGCOLOR="#'. $col. '" ALIGN="right">'. $a. '$'. $invoice['owed'].
+ '</A></TD>'.
+ '</TR>';
+
+ if ( $col == $col1 ) {
+ $col = $col2;
+ } else {
+ $col = $col1;
+ }
+
+ }
+
+ $ret .= '</TABLE><BR>';
+ } else {
+ $ret .= 'You have no outstanding invoices.<BR><BR>';
+ }
+
+ #$fs_info = $freeside->customer_info( array(
+ # 'session_id' => $_SESSION['freeside_session_id'],
+ #) );
+ #$error = $fs_info['error'];
+ #if ( $error ) {
+ # //$_SESSION['ari_error'] = _("Incorrect Username or Password");
+ # $_SESSION['ari_error'] = $error; #// XXX report as ari_error???!
+ #}
+
+ // $ret .= 'Billing goes here';
+ // XXX navigate to make payment, view invoice,
+ // & myaccount change payment info
+
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=make_payment">Make a credit card payment</A></B><BR><BR>';
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=make_payment">Make an electronic check payment</A></B><BR><BR>';
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=make_payment">Use a prepaid card</A></B><BR><BR>';
+
+ return $ret;
+
+ }
+
+ function make_payment($args) {
+
+ $display = new Display();
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $header_text = _("Billing");
+ if (!$_SESSION['ari_user']['admin_help']) {
+ $header_text .= sprintf(_(" for %s (%s)"), $displayname, $extension);
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText($header_text);
+ $ret .= $display->displayLine();
+
+
+ #$freeside = new FreesideSelfService();
+
+ $ret .= 'Make payment goes here';
+
+ return $ret;
+
+ }
+
+ function view_invoice($args) {
+
+ $display = new Display();
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $header_text = _("Billing");
+ if (!$_SESSION['ari_user']['admin_help']) {
+ $header_text .= sprintf(_(" for %s (%s)"), $displayname, $extension);
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText($header_text);
+ #$ret .= $display->displayLine();
+
+ $invnum = getArgument($args, 'invnum');
+
+ $freeside = new FreesideSelfService();
+ $invoice = $freeside->invoice( array(
+ 'session_id' => $_SESSION['freeside_session_id'],
+ 'invnum' => $invnum,
+ ) );
+ $error = $invoice['error'];
+ if ( $error ) {
+ //$_SESSION['ari_error'] = _("Incorrect Username or Password");
+ $_SESSION['ari_error'] = $error; // XXX report as ari_error???!
+ }
+
+ $html = $invoice['invoice_html']->scalar;
+ $html = str_replace( "\xA0", '&nbsp;', $html); // XX doh
+ error_log($html);
+
+ $ret .= '<TABLE BGCOLOR="#000000" BORDER=0><TR><TD>'.
+ $html.
+ '</TD></TR></TABLE>';
+
+ return $ret;
+
+ }
+
+}
+
+?>
diff --git a/fs_selfservice/fri/modules/callmonitor.module b/fs_selfservice/fri/modules/callmonitor.module
new file mode 100644
index 0000000..36f5f28
--- /dev/null
+++ b/fs_selfservice/fri/modules/callmonitor.module
@@ -0,0 +1,675 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the call monitor recordings
+ */
+
+/**
+ * Class for Callmonitor
+ */
+class Callmonitor {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 2;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=Callmonitor&f=display'>" . _("Call History") . "</a></small></small></p><br>";
+
+ return $ret;
+ }
+
+ /*
+ * Acts on the selected call monitor recordings in the method indicated by the action and updates page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function recAction($args) {
+
+ // args
+ $m = getArgument($args,'m');
+ $a = getArgument($args,'a');
+ $q = getArgument($args,'q');
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+ $order = getArgument($args,'order');
+ $sort = getArgument($args,'sort');
+ $duration_filter = getArgument($args,'duration_filter');
+
+ // get files
+ $files = array();
+ foreach($_REQUEST as $key => $value) {
+ if (preg_match('/selected/',$key)) {
+ array_push($files, $value);
+ }
+ }
+
+ if ($a=='delete') {
+ $this->deleteRecData($files);
+ }
+
+ if ($a=='ignore') {
+
+ $start = 0;
+
+ setcookie("ari_duration_filter", $duration_filter, time()+365*24*60*60);
+ }
+
+ // redirect to see updated page
+ $ret .= "
+ <head>
+ <script>
+ <!--
+ window.location = \"" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "&q=" . $q . "&start=" . $start . "&span=" . $span . "&order=" . $order . "&sort=" . $sort . "&duration_filter=" . $duration_filter . "\"
+ // -->
+ </script>
+ </head>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ global $ASTERISK_CALLMONITOR_PATH;
+ global $CALLMONITOR_ALLOW_DELETE;
+ global $AJAX_PAGE_REFRESH_ENABLE;
+
+ $display = new DisplaySearch();
+
+ // get the search string
+ $m = getArgument($args,'m');
+ $f = getArgument($args,'f');
+ $q = getArgument($args,'q');
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+ $order = getArgument($args,'order');
+ $sort = getArgument($args,'sort');
+ $duration_filter = getArgument($args,'duration_filter');
+
+ $start = $start=='' ? 0 : $start;
+ $span = $span=='' ? 15 : $span;
+ $order = $order=='' ? 'calldate' : $order;
+ $sort = $sort=='' ? 'desc' : $sort;
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // get data
+ $record_count = $this->getCdrCount($q,$duration_filter);
+ $data = $this->getCdrData($q,$duration_filter,$start,$span,$order,$sort);
+
+ // get the call monitor recording files
+ $paths = split(';',$ASTERISK_CALLMONITOR_PATH);
+ foreach($paths as $key => $path) {
+ if (!is_dir($path)) {
+ $_SESSION['ari_error'] .= sprintf(_("Path is not a directory: %s"),$path) . "<br>";
+ }
+ }
+ $recordings = $this->getRecordings($ASTERISK_CALLMONITOR_PATH,$data);
+
+ // build controls
+ if ($CALLMONITOR_ALLOW_DELETE) {
+ $controls .= "
+ <button class='infobar' type='submit' onclick=\"document.callmonitor_form.a.value='delete'\">
+ " . _("delete") . "
+ </button>
+ &nbsp;";
+ }
+
+ $controls .= "
+ <small>" . _("duration") . "</small>
+ <input name='duration_filter' type='text' size=4 maxlength=8 value='" . $_COOKIE['ari_duration_filter'] . "'>
+ <button class='infobar' type='submit' onclick=\"document.callmonitor_form.a.value='ignore'\">
+ " . _("ignore") . "
+ </button>";
+
+ // table header
+ if ($CALLMONITOR_ALLOW_DELETE) {
+ $recording_delete_header = "<th></th>";
+ }
+
+ $fields[0]['field'] = "calldate";
+ $fields[0]['text'] = _("Date");
+ $fields[1]['field'] = "calldate";
+ $fields[1]['text'] = _("Time");
+ $fields[2]['field'] = "clid";
+ $fields[2]['text'] = _("Caller ID");
+ $fields[3]['field'] = "src";
+ $fields[3]['text'] = _("Source");
+ $fields[4]['field'] = "dst";
+ $fields[4]['text'] = _("Destination");
+ $fields[5]['field'] = "dcontext";
+ $fields[5]['text'] = _("Context");
+ $fields[6]['field'] = "duration";
+ $fields[6]['text'] = _("Duration");
+
+ $i = 0;
+ while ($fields[$i]) {
+
+ $field = $fields[$i]['field'];
+ $text = $fields[$i]['text'];
+ if ($order==$field) {
+ if ($sort=='asc') {
+ $currentSort = 'desc';
+ $arrowImg = "<img src='theme/images/arrow-asc.gif' alt='sort'>";
+ }
+ else {
+ $currentSort = 'asc';
+ $arrowImg = "<img src='theme/images/arrow-desc.gif' alt='sort'>";
+ }
+
+ if ($i==1) {
+ $arrowImg = '';
+ }
+ }
+ else {
+ $arrowImg = '';
+ $currentSort = 'desc';
+ }
+
+ $unicode_q = urlencode($q);
+ $recording_header .= "<th><a href=" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "&f=" . $f . "&q=" . $unicode_q . "&order=" . $field . "&sort=" . $currentSort . ">" . $text . $arrowImg . "</a></th>";
+
+ $i++;
+ }
+ $recording_header .= "<th>" . _("Monitor") . "</th>";
+
+ // table body
+ foreach($data as $key=>$value) {
+
+ // recording file
+ $recording = $recordings[$value['uniqueid'] . $value['calldate']];
+
+ // date and time
+ $buf = split(' ', $value[calldate]);
+ $date = $buf[0];
+ $time = $buf[1];
+
+ // recording delete checkbox
+ if ($CALLMONITOR_ALLOW_DELETE) {
+ $recording_delete_checkbox = "<td class='checkbox'><input type=checkbox name='selected" . ++$i . "' value=" . $recording . "></td>";
+ }
+
+ $recordingLink = '';
+ if (is_file($recordings[$value['uniqueid'] . $value['calldate']])) {
+ $recordingLink = "<a href='#' onClick=\"javascript:popUp('misc/recording_popup.php?recording=" . $recording . "&date=" . $date . "&time=" . $time . "'); return false;\">" . _("play") . "</a>";
+ }
+
+ $recording_body .= "<tr>
+ " . $recording_delete_checkbox . "
+ <td width=70>" . $date . "</td>
+ <td>" . $time . "</td>
+ <td>" . $value[clid] . "</td>
+ <td>" . $value[src] . "</td>
+ <td>" . $value[dst] . "</td>
+ <td>" . $value[dcontext] . "</td>
+ <td width=90>" . $value[duration] . " sec</td>
+ <td>" . $recordingLink . "</td>
+ </tr>";
+ }
+ if (!count($data)) {
+ $recording_body .= "<tr></tr>";
+ }
+
+ // options
+ $url_opts = array();
+ $url_opts['sort'] = $sort;
+ $url_opts['order'] = $order;
+ $url_opts['duration_filter'] = $duration_filter;
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ // ajax page refresh script
+ if ($AJAX_PAGE_REFRESH_ENABLE) {
+ // $ret .= ajaxRefreshScript($args);
+ }
+
+ // header
+ if ($_SESSION['ari_user']['admin_callmonitor']) {
+ $header_text = _("Call History");
+ } else {
+ $header_text = sprintf(_("Call History for %s (%s)"),$displayname,$extension);
+ }
+ $ret .= $display->displayHeaderText($header_text);
+ $ret .= $display->displaySearchBlock('left',$m,$q,$url_opts,true);
+
+ // start form
+ if ($CALLMONITOR_ALLOW_DELETE) {
+
+ $ret .= "
+ <form name='callmonitor_form' action='" . $_SESSION['ARI_ROOT'] . "' method='GET'>
+ <input type=hidden name=m value=" . $m . ">
+ <input type=hidden name=f value=recAction>
+ <input type=hidden name=a value=''>
+ <input type=hidden name=q value=" . $q . ">
+ <input type=hidden name=start value=" . $start . ">
+ <input type=hidden name=span value=" . $span . ">
+ <input type=hidden name=order value=" . $order . ">
+ <input type=hidden name=sort value=" . $sort . ">";
+ }
+
+ $ret .= $display->displayInfoBarBlock($controls,$q,$start,$span,$record_count);
+
+ // javascript for popup and message actions
+ $ret .= "
+ <SCRIPT LANGUAGE='JavaScript'>
+ <!-- Begin
+ function popUp(URL) {
+ eval(\"page = window.open(URL, 'play', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=1,width=324,height=110');\");
+ }
+
+ function checkAll(form,set) {
+ var elem = 0;
+ var i = 0;
+ while (elem = form.elements[i]) {
+ if (set) {
+ elem.checked = true;
+ } else {
+ elem.checked = false;
+ }
+ i++;
+ }
+ return true;
+ }
+ // End -->
+ </script>";
+
+ // call monitor delete recording controls
+ if ($CALLMONITOR_ALLOW_DELETE) {
+ $ret .= "
+ <table>
+ <tr>
+ <td>
+ <small>" . _("select") . ": </small>
+ <small><a href='' OnClick=\"checkAll(document.callmonitor_form,true); return false;\">" . _("all") . "</a></small>
+ <small><a href='' OnClick=\"checkAll(document.callmonitor_form,false); return false;\">" . _("none") . "</a></small>
+ </td>
+ </tr>
+ </table>";
+ }
+ else {
+ $ret .= "<br>";
+ }
+
+ // table
+ $ret .= "
+ <table class='callmonitor'>
+ <tr>
+ " . $recording_delete_header . "
+ " . $recording_header . "
+ </tr>
+ " . $recording_body . "
+ </table>";
+
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+ $order = getArgument($args,'order');
+ $sort = getArgument($args,'sort');
+
+ // end form
+ if ($CALLMONITOR_ALLOW_DELETE) {
+ $ret .= "</form>";
+ }
+
+ $ret .= $display->displaySearchBlock('center',$m,$q,$url_opts,false);
+ $ret .= $display->displayNavigationBlock($m,$q,$url_opts,$start,$span,$record_count);
+
+ return $ret;
+ }
+
+ /*
+ * Checks for a recording file
+ *
+ * @param $asterisk_callmonitor_path
+ * path call monitor recording directory on the asterisk server
+ * @param $data
+ * current call monitor recordings on the asterisk server
+ * @return $recording
+ * returns an array of $recording file names if found
+ */
+ function getRecordings($asterisk_callmonitor_path,$data) {
+
+ global $CALLMONITOR_ONLY_EXACT_MATCHING;
+ global $CALLMONITOR_AGGRESSIVE_MATCHING;
+
+ $recordings = array();
+
+ $extension = $_SESSION['ari_user']['extension'];
+
+ $paths = split(';',$asterisk_callmonitor_path);
+ foreach($paths as $key => $path) {
+ $paths[$key] = fixPathSlash($paths[$key]);
+ }
+
+ $files = array();
+ if (!$CALLMONITOR_ONLY_EXACT_MATCHING) {
+ $filter = '';
+ $recursiveMax = 6;
+ $recursiveCount = 0;
+ foreach($paths as $key => $path) {
+ $path_files = getFiles($path,$filter,$recursiveMax,$recursiveCount);
+ if ($path_files) {
+ $files = array_merge($files,$path_files);
+ }
+ }
+ rsort($files);
+ }
+
+ foreach($data as $data_key => $data_value) {
+
+ $recording='';
+
+ $calldate = $data_value['calldate'];
+ $duration = $data_value['duration'];
+ $lastdata = $data_value['lastdata'];
+ $uniqueid = $data_value['uniqueid'];
+ $userfield = $data_value['userfield'];
+
+ // timestamps
+ $st = trim(strtotime($calldate));
+ $et = trim(strtotime($calldate) + $duration); // for on-demand call recordings
+
+ // unique file key
+ if ($uniqueid) {
+ $buf = preg_replace('/\-|\:/', '', $calldate);
+ $calldate_key = preg_replace('/\s+/', '-', $buf);
+ $unique_file_key = $calldate_key . "-" . $uniqueid;
+ }
+ if ($unique_file_key=='') {
+ $buf = preg_split("/\|/", $lastdata);
+ $unique_file_key = $buf[1];
+ }
+
+ $recordingLink = '';
+ foreach($paths as $callmonitor_key => $path) {
+
+ // try to find an exact match using the uniqueid
+ if (isset($uniqueid)) {
+
+ $check_files = array();
+ array_push($check_files,$path . $uniqueid . ".WAV");
+ array_push($check_files,$path . $uniqueid . ".wav");
+ array_push($check_files,$path . $uniqueid . ".gsm");
+
+ array_push($check_files,$path . $unique_file_key . ".WAV");
+ array_push($check_files,$path . $unique_file_key . ".wav");
+ array_push($check_files,$path . $unique_file_key . ".gsm");
+
+ array_push($check_files,$path . "g" . $extension . "-" . $unique_file_key . ".WAV");
+ array_push($check_files,$path . "g" . $extension . "-" . $unique_file_key . ".wav");
+ array_push($check_files,$path . "g" . $extension . "-" . $unique_file_key . ".gsm");
+
+ array_push($check_files,$path . "q" . $extension . "-" . $unique_file_key . ".WAV");
+ array_push($check_files,$path . "q" . $extension . "-" . $unique_file_key . ".wav");
+ array_push($check_files,$path . "q" . $extension . "-" . $unique_file_key . ".gsm");
+
+ array_push($check_files,$path . "OUT" . $extension . "-" . $unique_file_key . ".WAV");
+ array_push($check_files,$path . "OUT" . $extension . "-" . $unique_file_key . ".wav");
+ array_push($check_files,$path . "OUT" . $extension . "-" . $unique_file_key . ".gsm");
+
+ array_push($check_files,$path . $userfield);
+
+ // try to match
+ foreach($check_files as $check_file) {
+ if (is_file($check_file)) {
+ $recording = $check_file;
+ break;
+ }
+ }
+ }
+
+ // if found do not need to check the rest of the paths
+ if ($recording!='') {
+ break;
+ }
+ }
+
+ // get all the callmonitor recordings on server and try to find a non-exact match for this log entry
+ if (!$CALLMONITOR_ONLY_EXACT_MATCHING) {
+
+ // try to find a file using the uniqueid
+ if (!$recording) {
+
+ // try and match the unique id
+ if (!$recording) {
+ foreach($files as $key => $path) {
+ if (strlen($uniqueid)>1 && strpos($path,$uniqueid)!==FALSE) {
+ $recording = $path;
+ $files[$key] = ''; // remove it from the recording files so it will not be matched twice
+ break;
+ }
+ }
+ }
+ }
+
+ // try and match a file using the calldate (if no unique number from database)
+ if (!$recording) {
+
+ foreach($files as $key => $path) {
+ $parts = split("-", $path);
+ if (strlen($st)>1 &&
+ (strpos($path,$st)!==FALSE) ||
+ (strpos($path,"auto")!==FALSE && $parts[1] >= $st && $parts[1] <= $et)) {
+ $recording = $path;
+ $files[$key] = ''; // remove it from the recording files so it will not be matched twice
+ break;
+ }
+ }
+ }
+
+ if ($CALLMONITOR_AGGRESSIVE_MATCHING) {
+
+ // one last stab at finding a recording by adding one or two seconds to the call time
+ if (!$recording) {
+ $st_1 = trim($st+1);
+ $st_2 = trim($st+2);
+ $et_1 = trim($et+1);
+ $et_2 = trim($et+2);
+ foreach($files as $key => $path) {
+ $split = explode("-", $path);
+ if (strlen($st)>1
+ && ((strpos($path,$st_1)!==FALSE) ||
+ (strpos($path,$st_2)!==FALSE) ||
+ (strpos($path,"auto")!==FALSE && $parts[1] >= $st_1 && $parts[1] <= $et_1) ||
+ (strpos($path,"auto")!==FALSE && $parts[1] >= $st_2 && $parts[1] <= $et_2))) {
+ $recording = $path;
+ $files[$key] = ''; // remove it from the recording files so it will not be matched twice
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // add to array to be returned
+ if ($recording) {
+ $recordings[$uniqueid . $calldate] = $recording;
+ }
+ }
+
+ return $recordings;
+ }
+
+ /*
+ * Deletes selected call monitor recordings
+ *
+ * @param $files
+ * Array of files to delete
+ */
+ function deleteRecData($files) {
+
+ foreach($files as $key => $file) {
+ if (is_writable($file)) {
+ unlink($file);
+ } else {
+ $_SESSION['ari_error'] = _("Only deletes recording files, not cdr log");
+ }
+ }
+ }
+
+ /*
+ * Gets cdr record count
+ *
+ * @param $q
+ * query text
+ */
+ function getSearchText($q,$duration_filter) {
+
+ // search text
+ if ($q!='*' && $q!=NULL) {
+ $searchText .= "WHERE ";
+ $tok = strtok($q," \n\t");
+ while ($tok) {
+ $searchText .= " (calldate regexp '" . $tok . "'
+ OR clid regexp '" . $tok . "'
+ OR src regexp '" . $tok . "'
+ OR dst regexp '" . $tok . "'
+ OR dstchannel regexp '" . $tok . "'
+ OR dcontext regexp '" . $tok . "'
+ OR duration regexp '" . $tok . "'
+ OR disposition regexp '" . $tok . "'
+ OR uniqueid regexp '" . $tok . "'
+ OR userfield regexp '" . $tok . "'
+ )";
+ $tok = strtok(" \n\t");
+ if ($tok) {
+ $searchText .= " AND";
+ }
+ }
+ }
+
+ // duration_filter
+ if ($duration_filter) {
+ if (!$searchText) {
+ $searchText .= "WHERE ";
+ } else {
+ $searchText .= "AND ";
+ }
+ $searchText .= "duration>" . $duration_filter . " ";
+ }
+
+ // admin
+ if (!$_SESSION['ari_user']['admin_callmonitor']) {
+ if (!$searchText) {
+ $searchText .= "WHERE ";
+ } else {
+ $searchText .= "AND ";
+ }
+
+ // allow entries to be viewed with users extension
+ $searchText .= "(src = '" . $_SESSION['ari_user']['extension'] . "'
+ OR dst = '" . $_SESSION['ari_user']['extension'] . "'
+
+ OR channel LIKE 'IAX2/" . $_SESSION['ari_user']['extension'] ."-%'
+ OR dstchannel LIKE 'IAX2/" . $_SESSION['ari_user']['extension'] ."-%'
+
+ OR channel LIKE 'SIP/" . $_SESSION['ari_user']['extension'] ."-%'
+ OR dstchannel LIKE 'SIP/" . $_SESSION['ari_user']['extension'] ."-%')";
+
+ // allow entries to be viewed with users outbound CID
+ if (isset($_SESSION['ari_user']['outboundCID']) && trim($_SESSION['ari_user']['outboundCID']) != '') {
+ $searchText .= "OR (src = '" . $_SESSION['ari_user']['outboundCID'] . "'
+ OR dst = '" . $_SESSION['ari_user']['outboundCID'] . "')";
+ }
+ }
+
+ return $searchText;
+ }
+
+ /*
+ * Gets cdr record count
+ *
+ * @param $q
+ * query text
+ * @return $count
+ * Number of cdr records counted
+ */
+ function getCdrCount($q,$duration_filter) {
+
+ global $ASTERISKCDR_DBTABLE;
+
+ $searchText = $this->getSearchText($q,$duration_filter);
+
+ $dbh = $_SESSION['dbh_cdr'];
+ $sql = "SELECT count(*)
+ FROM " . $ASTERISKCDR_DBTABLE . "
+ " . $searchText;
+
+ $result = $dbh->getAll($sql);
+ if (DB::isError($result)) {
+ $_SESSION['ari_error'] = $result->getMessage();
+ return;
+ }
+ $count = $result[0][0];
+
+ return $count;
+ }
+
+ /*
+ * Gets cdr data
+ *
+ * @param $q
+ * query text
+ * @param $start
+ * start record
+ * @param $span
+ * number of records to return
+ * @return $data
+ * cdr data to be returned
+ */
+ function getCdrData($q,$duration_filter,$start,$span,$order,$sort) {
+
+ global $ASTERISKCDR_DBTABLE;
+
+ $data = array();
+
+ $searchText = $this->getSearchText($q,$duration_filter);
+
+ $dbh = $_SESSION['dbh_cdr'];
+ $sql = "SELECT *
+ FROM " . $ASTERISKCDR_DBTABLE . "
+ " . $searchText . "
+ ORDER BY " . $order . " " . $sort . "
+ LIMIT " . $start . "," . $span;
+ $result = $dbh->getAll($sql,DB_FETCHMODE_ASSOC);
+ if (DB::isError($result)) {
+ $_SESSION['ari_error'] = $result->getMessage();
+ return;
+ }
+ $data = $result;
+
+ return $data;
+ }
+
+
+}
+
+
+?>
diff --git a/fs_selfservice/fri/modules/dashboard.module b/fs_selfservice/fri/modules/dashboard.module
new file mode 100644
index 0000000..62d6de4
--- /dev/null
+++ b/fs_selfservice/fri/modules/dashboard.module
@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the help page
+ */
+
+/**
+ * Class for help
+ */
+class dashboard {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = -4;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=dashboard&f=display'>" . _("Dashboard") . "</a></small></small></p><br>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ $display = new Display();
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $header_text = _("Dashboard");
+ if (!$_SESSION['ari_user']['admin_help']) {
+ $header_text .= sprintf(_(" for %s (%s)"), $displayname, $extension);
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText($header_text);
+ $ret .= $display->displayLine();
+
+ $freeside = new FreesideSelfService();
+ $fs_info = $freeside->customer_info( array(
+ 'session_id' => $_SESSION['freeside_session_id'],
+ ) );
+ $error = $fs_info['error'];
+ if ( $error ) {
+ //$_SESSION['ari_error'] = _("Incorrect Username or Password");
+ $_SESSION['ari_error'] = $error; #// XXX report as ari_error???!
+ }
+
+ $ret .= $fs_info['small_custview'];
+ $ret .= '<BR>';
+
+ if ( $fs_info['balance'] > 0 ) {
+
+ #$ret .= '<B><A HREF="'. $_SESSION['ARI_ROOT'].
+ # '?m=billing&f=make_payment">Make a payment</A></B><BR><BR>';
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=make_payment">Make a payment</A></B><BR><BR>';
+
+ }
+
+ // XXX count() ???
+ if ( count($fs_info['open_invoices']) ) {
+
+ $ret .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#ff6666" COLSPAN=5>Open Invoices</TH></TR>';
+ $link = '<A HREF="'. $_SESSION['ARI_ROOT'].
+ '?m=billing&f=view_invoice&invnum=';
+
+ $col1 = "eeeeee";
+ $col2 = "cccccc";
+ $col = $col1;
+
+ while ( $i = each($fs_info['open_invoices']) ) {
+
+ $invoice = $i[value];
+
+ $td = '<TD BGCOLOR="#'. $col. '">';
+ $a = $link. $invoice['invnum']. '">';
+ $ret .=
+ "<TR>$td$a". 'Invoice #'. $invoice['invnum']. "</A></TD>$td</TD>".
+ "$td$a". $invoice['date']. "</A></TD>$td</TD>".
+ '<TD BGCOLOR="#'. $col. '" ALIGN="right">'. $a. '$'. $invoice['owed'].
+ '</A></TD>'.
+ '</TR>';
+
+ if ( $col == $col1 ) {
+ $col = $col2;
+ } else {
+ $col = $col1;
+ }
+
+ }
+
+ $ret .= '</TABLE><BR>';
+ } else {
+ $ret .= 'You have no outstanding invoices.<BR><BR>';
+ }
+
+ #$ret .= 'Received calls (10)<br><br>';
+ #$ret .= 'Placed calls (10)';
+
+// if ( @tickets ) {
+// $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+// '<TR><TH BGCOLOR="#ff6666" COLSPAN=5>Open Tickets</TH></TR>'.
+// '<TR><TH>#</TH><TH>Subject</TH><TH>Priority</TH><TH>Queue</TH>'.
+// '<TH>Status</TH></TR>';
+// my $col1 = "ffffff";
+// my $col2 = "dddddd";
+// my $col = $col1;
+//
+// foreach my $ticket ( @tickets ) {
+// my $td = qq!<TD BGCOLOR="#$col">!;
+// $OUT .=
+// "<TR>$td". $ticket->{'id'}. "</TD>".
+// $td. $ticket->{'subject'}. "</TD>".
+// $td. ($ticket->{'content'} || $ticket->{'priority'}). "</TD>".
+// $td. $ticket->{'name'}. "</TD>".
+// $td. $ticket->{'status'}. "</TD>".
+// '</TR>';
+// $col = $col eq $col1 ? $col2 : $col1;
+// }
+// $OUT .= '</TABLE>';
+// } else {
+// $OUT .= '';
+// }
+
+ return $ret;
+ }
+
+}
+
+?>
diff --git a/fs_selfservice/fri/modules/featurecodes.module b/fs_selfservice/fri/modules/featurecodes.module
new file mode 100644
index 0000000..75d1d5c
--- /dev/null
+++ b/fs_selfservice/fri/modules/featurecodes.module
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the help page
+ */
+
+/**
+ * Class for help
+ */
+class featurecodes {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 7;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=featurecodes&f=display'>" . _("Feature Codes") . "</a></small></small></p><br>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ global $ARI_HELP_FEATURE_CODES;
+
+ $display = new Display();
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $header_text = _("Feature Codes");
+ if (!$_SESSION['ari_user']['admin_help']) {
+ $header_text .= sprintf(_(" for %s (%s)"), $displayname, $extension);
+ }
+
+ // handset feature code header
+ $handset_feature_codes_header =
+ "<tr>
+ <th class='feature_codes'>
+ " . _("Handset Feature Code") . "
+ </th>
+ <th>
+ " . _("Action") . "
+ </th>
+ </tr>";
+
+ // handset feature code body
+ if (isset($_SESSION['dbh_asterisk'])) {
+
+ $sql = "
+ SELECT keycode, description
+ FROM (
+ SELECT modulename, description, defaultcode keycode
+ FROM featurecodes
+ WHERE customcode IS NULL
+ AND enabled = '1'
+ UNION ALL SELECT modulename, description, customcode keycode
+ FROM featurecodes
+ WHERE customcode IS NOT NULL
+ AND enabled = '1'
+ )c
+ WHERE modulename NOT
+ IN ( 'core', 'recordings', 'infoservices', 'polycomreassign')
+ ORDER BY modulename, keycode
+ ";
+
+ $results = $_SESSION['dbh_asterisk']->getAll($sql, DB_FETCHMODE_ASSOC);
+ if(DB::IsError($results)) {
+ $_SESSION['ari_error'] = $results->getMessage();
+ }
+ else {
+ foreach ($results as $item ) {
+ $handset_feature_codes_body .=
+ "<tr>
+ <td class='feature_codes'>
+ " . $item['keycode'] . "
+ </td>
+ <td>
+ " . $item['description'] . "
+ </td>
+ </tr>";
+ }
+ }
+ }
+ else {
+
+ // handset feature code body
+ foreach($ARI_HELP_FEATURE_CODES as $key => $feature_code) {
+
+ $handset_feature_codes_body .=
+ "<tr>
+ <td class='feature_codes'>
+ " . $key . "
+ </td>
+ <td>
+ " . $feature_code . "
+ </td>
+ </tr>";
+ }
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText($header_text);
+ $ret .= $display->displayLine();
+
+ // table
+ $ret .= "
+ <table class='help'>
+ " . $handset_feature_codes_header . "
+ " . $handset_feature_codes_body . "
+ </table>";
+
+ return $ret;
+ }
+
+}
+
+?>
diff --git a/fs_selfservice/fri/modules/followme.module b/fs_selfservice/fri/modules/followme.module
new file mode 100644
index 0000000..85a1f37
--- /dev/null
+++ b/fs_selfservice/fri/modules/followme.module
@@ -0,0 +1,678 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the call monitor recordings
+ */
+
+/**
+ * Class for Followme
+ */
+class followme {
+
+ var $protocol_table;
+ var $protocol_config_files;
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 5;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+ global $ARI_ADMIN_USERNAME;
+
+ $exten = $_SESSION['ari_user']['extension'];
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=followme&f=display'>" . _("Follow Me") . "</a></small></small></p>";
+ }
+
+ return $ret;
+ }
+
+ /*
+ * Acts on the user settings
+ *
+ * @param $args
+ * Common arguments
+ * @param $a
+ * action
+ */
+ function action($args) {
+
+ global $STANDALONE;
+ global $ARI_ADMIN_USERNAME;
+ global $SETTINGS_ALLOW_VMX_SETTINGS;
+
+ // args
+ $m = getArgument($args,'m');
+ $a = getArgument($args,'a');
+
+ $lang_code = getArgument($args,'lang_code');
+
+ $follow_me_prering_time = getArgument($args,'follow_me_prering_time');
+ $follow_me_listring_time = getArgument($args,'follow_me_listring_time');
+ $follow_me_list = getArgument($args,'follow_me_list');
+ $follow_me_confirm = getArgument($args,'follow_me_confirm');
+ $follow_me_ddial = getArgument($args,'follow_me_ddial');
+ $follow_me_disabled = getArgument($args,'follow_me_disabled');
+
+ $language = new Language();
+
+ // Lets see if we can make heads or tails of this code?!?
+
+ // The action is 'update
+ if ($a=='update') {
+
+ // Get the extension and make sure we are not in
+ // admin mode
+ $exten = $_SESSION['ari_user']['extension'];
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+
+
+ // Make sure Follow-Me setup has not been deleted for this user since the last refresh
+ $follow_me_disabled_delayed = $_COOKIE['ari_follow_me_disabled'];
+ if (! $_COOKIE['ari_follow_me_disabled']) {
+
+ $follow_me_disabled = ($this->getFollowMeListRingTime($exten) > 0)?0:1;
+
+ if ($follow_me_disabled) {
+
+ setcookie("ari_follow_me_disabled", $follow_me_disabled, time()+365*24*60*60);
+ $follow_me_disabled_delayed = $follow_me_disabled;
+ $_SESSION['ari_error'] =
+ _("Your Follow-Me has been disabled, REFRESH your browser to remove this message") . "<br>" .
+ sprintf(_("Check with your Telephone System Administrator if you think there is a problem"));
+ }
+ }
+
+
+
+ if (! $follow_me_disabled_delayed) {
+
+ // assume no errors, don't update SQL if errors occured
+ $follow_me_update_succeeded=1;
+
+ // update follow me pre-ring time
+ if (!$STANDALONE['use']) {
+
+ $stripped_follow_me_prering_time = preg_replace('/-|\s/','',$follow_me_prering_time);
+ if (!is_numeric($stripped_follow_me_prering_time)) {
+ $_SESSION['ari_error'] =
+ _("Follow-Me pre-ring time not changed") . "<br>" .
+ sprintf(_("Number %s must be an interger number of seconds"),$follow_me_prering_time);
+ $follow_me_update_succeeded=0;
+ }
+ else {
+
+ // set database
+ $this->setFollowMePreRingTime($exten,$stripped_follow_me_prering_time);
+
+ // store cookie
+ $stripped = preg_replace('/-|\s/','',$_COOKIE['ari_follow_me_prering_time']);
+ if ($follow_me_prering_time && $stripped!=$stripped_follow_me_prering_time) {
+ setcookie("ari_follow_me_prering_time", $follow_me_prering_time, time()+365*24*60*60);
+ }
+ }
+ }
+
+ // update follow me list ring time
+ if (!$STANDALONE['use']) {
+
+ $stripped_follow_me_listring_time = preg_replace('/-|\s/','',$follow_me_listring_time);
+ if (!is_numeric($stripped_follow_me_listring_time)) {
+ $_SESSION['ari_error'] =
+ _("Follow-Me list ring time not changed") . "<br>" .
+ sprintf(_("Number %s must be an interger number of seconds"),$follow_me_listring_time);
+ $follow_me_update_succeeded=0;
+ }
+ else {
+
+ // set database
+ $this->setFollowMeListRingTime($exten,$stripped_follow_me_listring_time);
+
+ // store cookie
+ $stripped = preg_replace('/-|\s/','',$_COOKIE['ari_follow_me_listring_time']);
+ if ($follow_me_listring_time && $stripped!=$stripped_follow_me_listring_time) {
+ setcookie("ari_follow_me_listring_time", $follow_me_listring_time, time()+365*24*60*60);
+ }
+ }
+ }
+
+ // update follow me list
+ if (!$STANDALONE['use']) {
+
+ $grplist = explode("\n", $follow_me_list);
+
+ if (!$grplist) {
+ $grplist = null;
+ }
+
+ foreach (array_keys($grplist) as $key) {
+ //trim it
+ $grplist[$key] = trim($grplist[$key]);
+
+ // Lookup the extension and append hash if not a user, and remove invalid chars
+ $grplist[$key] = $this->lookupSetExtensionFormat($grplist[$key]);
+
+ // remove blanks
+ if ($grplist[$key] == "") unset($grplist[$key]);
+ }
+
+ // check for duplicates, and re-sequence
+ $grplist = array_values(array_unique($grplist));
+
+ $stripped_follow_me_list = implode("-",$grplist);
+
+ if ($stripped_follow_me_list == "") {
+ $_SESSION['ari_error'] =
+ _("Follow-Me list must contain at least one valid number") . "<br>" .
+ sprintf(_("The following: %s is not valid"),$follow_me_list);
+ $follow_me_update_succeeded=0;
+ }
+ else {
+
+ // set database
+ $this->setFollowMeList($exten,$stripped_follow_me_list);
+
+ // store cookie
+ $stripped = preg_replace('/|\(|\)|\s/','',$_COOKIE['ari_follow_me_list']);
+ if ($follow_me_list && $stripped!=$stripped_follow_me_list) {
+ setcookie("ari_follow_me_list", $follow_me_list, time()+365*24*60*60);
+ }
+ }
+ }
+
+ // update follow me confirm
+ if (!$STANDALONE['use']) {
+
+ // set database
+ $this->setFollowMeConfirm($exten,$follow_me_confirm);
+ $this->setFollowMeDDial($exten,$follow_me_ddial);
+
+ // store cookie
+ setcookie("ari_follow_me_confirm", $follow_me_confirm, time()+365*24*60*60);
+ setcookie("ari_follow_me_ddial", $follow_me_ddial, time()+365*24*60*60);
+ }
+
+ //If no errors than update the SQL table to keep in sync
+ if ($follow_me_update_succeeded) {
+ $this->setFollowMeMySQL($exten, $follow_me_prering_time, $follow_me_listring_time, $follow_me_list, $follow_me_confirm);
+ }
+
+ } //if !follow_me_disabled
+ }
+ }
+
+ // redirect to see updated page
+ $ret .= "
+ <head>
+ <script>
+ <!--
+ window.location = \"" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "\"
+ // -->
+ </script>
+ </head>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ global $STANDALONE;
+ global $ARI_ADMIN_USERNAME;
+ global $SETTINGS_PRERING_LOW;
+ global $SETTINGS_PRERING_HIGH;
+ global $SETTINGS_LISTRING_LOW;
+ global $SETTINGS_LISTRING_HIGH;
+
+ global $SETTINGS_FOLLOW_ME_LIST_MAX;
+
+ global $loaded_modules;
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $exten = $_SESSION['ari_user']['extension'];
+
+ $language = new Language();
+ $display = new DisplaySearch();
+
+ // build controls
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+
+ // call forward settings
+ if (!$STANDALONE['use']) {
+
+ $follow_me_prering_time = $this->getFollowMePreRingTime($exten);
+ $follow_me_listring_time = $this->getFollowMeListRingTime($exten);
+ $follow_me_list = explode("-", $this->getFollowMeList($exten) );
+ $follow_me_confirm = $this->getFollowMeConfirm($exten);
+ $follow_me_ddial = $this->getFollowMeDDial($exten);
+
+ $FOLLOW_ME_LIST_MAX = (count($follow_me_list) > $SETTINGS_FOLLOW_ME_LIST_MAX) ? count($follow_me_list):$SETTINGS_FOLLOW_ME_LIST_MAX;
+
+ //TODO: Set this better than this?
+ $follow_me_disabled = ($follow_me_listring_time > 0)?0:1;
+ setcookie("ari_follow_me_disabled", $follow_me_disabled, time()+365*24*60*60);
+
+ $followme_text.= "<table class='settings'>";
+
+ if (!$follow_me_disabled) {
+ // $followme_text .= "<tr><td><h3><br>" . _("Follow Me") . "</h3></td></tr>";
+ $followme_text .= "<tr><td>&nbsp;</td></tr>"; // Blank Line
+
+ $followme_text .= "<tr><td><a href='#' class='info'>" . _("Enable") . "<span>";
+ $followme_text .= _( "Dial-by-name Directory, IVR, and internal
+ calls will ring the numbers in the FollowMe
+ List. Any FreePBX routes that directly
+ reference a FollowMe are unaffected by this
+ enable/disable setting.");
+ $followme_text .= "<br></span></a></td>";
+
+ $followme_text .= "<td><input " . $follow_me_ddial . " type=checkbox name='follow_me_ddial' value='checked'></td></tr>";
+
+ $followme_text .= "<tr><td>&nbsp;</td></tr>"; // Blank Line
+ $followme_text .= "<tr><td valign='top'><a href='#' class='info'>" . _("Follow Me List:");
+ $followme_text .= "<span>" . sprintf(_("Extensions and outside numbers to ring next.")) ."<br/><br/>";
+ $followme_text .= sprintf(_("Include %s to keep it ringing."),"<strong>".$exten."</strong>") . "<br></span></a></td>";
+ $followme_text .= "<td><textarea " . $follow_me_list_options . " id='follow_me_list' name='follow_me_list' type='text' cols='20' rows='".$FOLLOW_ME_LIST_MAX."' value='' onKeyUp='rowCounter(this.form.follow_me_list, ".$FOLLOW_ME_LIST_MAX.");' onKeyDown='rowCounter(this.form.follow_me_list, ".$FOLLOW_ME_LIST_MAX.");'>".implode("\n",$follow_me_list)."</textarea>";
+ $followme_text .= "</td></tr>";
+
+ $followme_text .= "<tr><td>&nbsp;</td></tr>"; // Blank Line
+ $followme_text .= "<tr><td><a href='#' class='info'>";
+ $followme_text .= sprintf(_("Ring %s First For:"), $exten);
+ $followme_text .= "<span>" . sprintf( _("Time to ring extension %s before ringing the %s Follow Me List %s"), "<strong>".$exten."</strong>","<strong>","</strong>");
+ $followme_text .= "<br></span></a></td><td>";
+
+ $followme_text .= "<select " . $follow_me_prering_time_text_box_options . " name='follow_me_prering_time'/>";
+ $default_prering = $follow_me_prering_time;
+ for ($i=$SETTINGS_PRERING_LOW; $i <= $SETTINGS_PRERING_HIGH; $i++) {
+ $followme_text .= '<option value="'.$i.'" '.($i == $default_prering ? 'SELECTED' : '').'>'.$i.'</option>';
+ }
+ $followme_text .= "</select>";
+
+ $followme_text .= "<small>" . _("seconds") . "</small>";
+ $followme_text .= "</td></tr>";
+
+ $followme_text .= "<tr><td><a href='#' class='info'>" . _("Ring Followme List for:") . "<span>" . _("Time to ring the Follow Me List.") . "<br></span></a></td>";
+ $followme_text .= "<td>";
+
+ $followme_text .= "<select " . $follow_me_listring_time_text_box_options . " name='follow_me_listring_time'/>";
+ $default_listring = $follow_me_listring_time;
+ for ($i=$SETTINGS_LISTRING_LOW; $i <= $SETTINGS_LISTRING_HIGH; $i++) {
+ $followme_text .= '<option value="'.$i.'" '.($i == $default_listring ? 'SELECTED' : '').'>'.$i.'</option>';
+ }
+ $followme_text .= "</select>";
+
+ $followme_text .= "<small>" . _("seconds") . "</small></td></tr>";
+
+
+ $followme_text .= "<tr><td>&nbsp;</td></tr>"; // Blank Line
+
+ $followme_text .= "<tr><td><a href='#' class='info'>" . _("Use Confirmation:") . "<span>". _("Outside lines that are part of the Follow Me List will be called and offered a menu:<br/><br/> \"You have an incoming call. Press 1 to accept or 2 to decline.\"<br/><br/> This keeps calls from ending up in external voicemail. Make sure that the List Ring Time is long enough to allow for you to hear and react to this message.");
+ $followme_text .= "<br></span></a></td><td>";
+ $followme_text .= "<input " . $follow_me_confirm . " type=checkbox name='follow_me_confirm' value='checked'>";
+ $followme_text .= "<small>" . _("Enable") . "</small></td></tr>";
+ $followme_text .= "<tr><td>&nbsp;</td></tr>"; // Blank Line
+ $followme_text .= "</table>";
+ }
+ }
+
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ if ($_SESSION['ari_user']['admin_settings']) {
+ $headerText = _("Followme Settings");
+ } else {
+ $headerText = sprintf(_("Followme Settings for %s (%s)"),$displayname,$exten);
+ }
+
+ $ret .= $display->displayHeaderText($headerText);
+ $ret .= $display->displayLine();
+
+ $ret .=
+ "\n<SCRIPT LANGUAGE='JavaScript'>
+ <!-- Begin
+ function rowCounter(field, maxlimit) {
+ temp = field.value.split('\u000A',maxlimit+1)
+ field.value = temp.join('\u000A')
+ if (temp.length == maxlimit+1) {
+ field.value = field.value.substring(0, field.value.length-1)
+ }
+ }
+ // End -->
+ </script>\n";
+
+ $ret .=
+ "<form class='settings' name='ari_settings' action='' method='GET'>
+ <input type=hidden name=m value=" . $m . ">
+ <input type=hidden name=f value='action'>
+ <input type=hidden name=a value='update'>
+ " . $followme_text . "
+ <br>
+ <input name='submit' type='submit' value='" . _("Update") . "'>
+ </form>";
+
+ return $ret;
+ }
+
+
+ /*
+ * Sets Follow Me Pre-Ring Time
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $follow_me_prering_time
+ * Pre-Ring Time to ring
+ */
+ function setFollowMePreRingTime($exten,$follow_me_prering_time) {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = $follow_me_prering_time;
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/followme/prering $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Gets Follow Me Pre-Ring Time if set
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $number
+ * follow me pre-ring time returned if set
+ */
+ function getFollowMePreRingTime($exten) {
+
+ global $asterisk_manager_interface;
+
+ $number = '';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/followme/prering\r\n\r\n");
+ if (is_numeric($response)) {
+ $number = $response;
+ }
+
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_follow_me_prering_time']);
+ if ($stripped==$number) {
+ $number = $_COOKIE['ari_follow_me_prering_time'];
+ }
+
+ return $number;
+ }
+
+ /*
+ * Sets Follow Me List Ring Time
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $follow_me_listring_time
+ * List Ring Time to ring
+ */
+ function setFollowMeListRingTime($exten,$follow_me_listring_time) {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = $follow_me_listring_time;
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/followme/grptime $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Gets Follow Me List-Ring Time if set
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $number
+ * follow me list-ring time returned if set
+ */
+ function getFollowMeListRingTime($exten) {
+
+ global $asterisk_manager_interface;
+
+ $number = '';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/followme/grptime\r\n\r\n");
+ if (is_numeric($response)) {
+ $number = $response;
+ }
+
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_follow_me_listring_time']);
+ if ($stripped==$number) {
+ $number = $_COOKIE['ari_follow_me_listring_time'];
+ }
+
+ return $number;
+ }
+
+ /*
+ * Sets Follow Me List
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $follow_me_list
+ * Follow Me List
+ */
+ function setFollowMeList($exten,$follow_me_list) {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = $follow_me_list;
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/followme/grplist $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Gets Follow Me List if set
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $data
+ * follow me list if set
+ */
+ function getFollowMeList($exten) {
+
+ global $asterisk_manager_interface;
+
+ $number = '';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/followme/grplist\r\n\r\n");
+
+ //TODO: really need to check for a bogus response, see how other side does it
+ //
+ return preg_replace("/[^0-9*\-]/", "", $response);
+ }
+
+ /*
+ * Sets Follow Confirmation Setting
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $follow_me_cofirm
+ * Follow Me Confirm Setting
+ */
+ function setFollowMeConfirm($exten,$follow_me_confirm) {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = ($follow_me_confirm)?'ENABLED':'DISABLED';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/followme/grpconf $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Gets Follow Me Confirmation Setting
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $data
+ * follow me confirm setting
+ */
+ function getFollowMeConfirm($exten) {
+
+ global $asterisk_manager_interface;
+
+ $number = '';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/followme/grpconf\r\n\r\n");
+
+ if (preg_match("/ENABLED/",$response)) {
+ $response='checked';
+ }
+ else {
+ $response='';
+ }
+
+ //TODO: really need to check for a bogus response, see how other side does it
+ //
+ return $response;
+
+ }
+
+ /*
+ * Sets Follow Ddial Setting
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $follow_me_ddial
+ * Follow Me Ddial Setting
+ */
+ function setFollowMeDDial($exten,$follow_me_ddial) {
+
+ global $asterisk_manager_interface;
+
+ $value_opt = ($follow_me_ddial)?'DIRECT':'EXTENSION';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/followme/ddial $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Gets Follow Me Ddial Setting
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $data
+ * follow me ddial setting
+ */
+ function getFollowMeDDial($exten) {
+
+ global $asterisk_manager_interface;
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/followme/ddial\r\n\r\n");
+
+ if (preg_match("/EXTENSION/",$response)) {
+ $response='';
+ }
+ else {
+ $response='checked';
+ }
+
+ //TODO: really need to check for a bogus response, see how other side does it
+ //
+ return $response;
+
+ }
+
+
+
+
+
+ /*
+ * Gets FreePBX Version
+ */
+ function getFreePBXVersion() {
+
+ if (isset($_SESSION['dbh_asterisk'])) {
+ $sql = "SELECT * FROM admin WHERE variable = 'version'";
+ $results = $_SESSION['dbh_asterisk']->getAll($sql);
+ if(DB::IsError($results)) {
+ $_SESSION['ari_error'] = $results->getMessage();
+ }
+
+ return $results[0][1];
+ }
+ }
+
+ /*
+ * Sets Follow-Me Settings in FreePBX MySQL Database
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $follow_me_prering_time
+ * Pre-Ring Time to ring
+ * @param $follow_me_listring_time
+ * List Ring Time to ring
+ * @param $follow_me_list
+ * Follow Me List
+ * @param $follow_me_list
+ * Follow Me Confirm Setting
+ *
+ */
+ function setFollowMeMySQL($exten, $follow_me_prering_time, $follow_me_listring_time, $follow_me_list, $follow_me_confirm) {
+
+ if (isset($_SESSION['dbh_asterisk'])) {
+
+ //format for SQL database
+ $follow_me_confirm = ($follow_me_confirm)?'CHECKED':'';
+
+ $sql = "UPDATE findmefollow SET grptime = '" . $follow_me_listring_time . "', grplist = '".
+ str_replace("'", "''", trim($follow_me_list)) . "', pre_ring = '" . $follow_me_prering_time .
+ "', needsconf = '" . $follow_me_confirm . "' WHERE grpnum = $exten LIMIT 1";
+ $results = $_SESSION['dbh_asterisk']->query($sql);
+
+ if(DB::IsError($results)) {
+ $_SESSION['ari_error'] = $results->getMessage();
+ }
+
+ return 1;
+ }
+ }
+
+ function lookupSetExtensionFormat($exten) {
+
+ if (trim($exten) == "") return $exten;
+
+ $exten = preg_replace("/[^0-9*]/", "", $exten);
+
+ $sql = "SELECT extension FROM users WHERE extension = '".$exten."'";
+ $asa = $_SESSION['dbh_asterisk']->getrow($sql, DB_FETCHMODE_ASSOC);
+ if (!is_array($asa)) {
+ return $exten.'#';
+ } else {
+ return $exten;
+ }
+ }
+
+
+} // class
+
+?>
diff --git a/fs_selfservice/fri/modules/myaccount.module b/fs_selfservice/fri/modules/myaccount.module
new file mode 100644
index 0000000..6b7cb83
--- /dev/null
+++ b/fs_selfservice/fri/modules/myaccount.module
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the help page
+ */
+
+/**
+ * Class for help
+ */
+class myaccount {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 9;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=myaccount&f=display'>" . _("My Account") . "</a></small></small></p><br>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ global $ARI_HELP_FEATURE_CODES;
+
+ $display = new Display();
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $header_text = _("My Account");
+ if (!$_SESSION['ari_user']['admin_help']) {
+ $header_text .= sprintf(_(" for %s (%s)"), $displayname, $extension);
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $ret .= $display->displayHeaderText($header_text);
+ $ret .= $display->displayLine();
+
+ $freeside = new FreesideSelfService();
+ $fs_info = $freeside->customer_info( array(
+ 'session_id' => $_SESSION['freeside_session_id'],
+ ) );
+ $error = $fs_info['error'];
+ if ( $error ) {
+ //$_SESSION['ari_error'] = _("Incorrect Username or Password");
+ $_SESSION['ari_error'] = $error; #// XXX report as ari_error???!
+ }
+
+ $ret .= $fs_info['small_custview'];
+ $ret .= '<BR>';
+
+
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=change_bill">Change billing address</A></B>';
+
+ $ret .= '&nbsp;&nbsp;|&nbsp;&nbsp;';
+
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=change_ship">Change service address</A></B>';
+
+ $ret .= '<BR><BR>';
+
+ $ret .= '<B><A HREF="/selfservice/selfservice.cgi?session='.
+ $_SESSION['freeside_session_id'].
+ ';action=change_pay">Change payment information</A></B><BR><BR>';
+
+ return $ret;
+ }
+
+}
+
+?>
diff --git a/fs_selfservice/fri/modules/phonefeatures.module b/fs_selfservice/fri/modules/phonefeatures.module
new file mode 100644
index 0000000..89dc903
--- /dev/null
+++ b/fs_selfservice/fri/modules/phonefeatures.module
@@ -0,0 +1,342 @@
+<?php
+//*****************************************************************************
+class PhoneFeatures {
+//*****************************************************************************
+ function rank() {
+
+ $rank = 4;
+ return $rank;
+ }
+
+//*****************************************************************************
+ function init() {
+ }
+//*****************************************************************************
+ function navMenu($args) {
+
+ global $ARI_NO_LOGIN;
+ global $SETTINGS_ALLOW_PHONE_SETTINGS;
+ global $SETTINGS_ALLOW_CALLFORWARD_SETTINGS;
+
+ // If we're not allowing call forwarding AND PHONE SETTINGS get out of here
+ if (!$SETTINGS_ALLOW_PHONE_SETTINGS && !$SETTINGS_ALLOW_CALLFORWARD_SETTINGS) return "";
+
+ $ret .= "
+ <p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=PhoneFeatures&f=display'>" . _("Phone Features") . "</a></small></small></p>";
+
+ return $ret;
+ }
+//*****************************************************************************
+ function action($args) {
+
+ global $ARI_ADMIN_USERNAME;
+ global $SETTINGS_ALLOW_PHONE_SETTINGS;
+ global $SETTINGS_ALLOW_CALLFORWARD_SETTINGS;
+
+ // args
+ $m = getArgument($args,'m');
+ $a = getArgument($args,'a');
+ $lang_code = getArgument( $args,'lang_code');
+ $exten = $_SESSION['ari_user']['extension'];
+
+ if ($a=='update') {
+
+ if ($SETTINGS_ALLOW_PHONE_SETTINGS) {
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+ $this->storePhoneSetting( $args, $exten, 'call_waiting', 'CW', 'ENABLED');
+ $this->storePhoneSetting( $args, $exten, 'do_not_disturb', 'DND', 'YES');
+ }
+ }
+
+ if ($SETTINGS_ALLOW_CALLFORWARD_SETTINGS) {
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+ $this->storeCallForwardNumber( $args, $exten, 'call_forward', 'CF');
+ $this->storeCallForwardNumber( $args, $exten, 'call_forward_busy', 'CFB');
+ $this->storeCallForwardNumber( $args, $exten, 'call_forward_unavailable', 'CFU');
+ }
+ }
+ }
+
+ // redirect to see updated page
+ $ret .= "
+ <head>
+ <script>
+ <!--
+ window.location = \"" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "\"
+ // -->
+ </script>
+ </head>";
+
+ return $ret;
+ }
+//*****************************************************************************
+function display($args) {
+
+ global $STANDALONE;
+ global $ARI_ADMIN_USERNAME;
+ global $SETTINGS_ALLOW_PHONE_SETTINGS;
+ global $SETTINGS_ALLOW_CALLFORWARD_SETTINGS;
+
+ // args
+ $m = getArgument($args,'m');
+ $a = getArgument($args,'a');
+ $lang_code = getArgument( $args,'lang_code');
+ $exten = $_SESSION['ari_user']['extension'];
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $exten = $_SESSION['ari_user']['extension'];
+
+ $display = new DisplaySearch();
+
+ // build controls
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+
+ if ($SETTINGS_ALLOW_PHONE_SETTINGS) {
+ $dnd_cw_text = "<table class='settings'>";
+ $dnd_cw_text.= "<tr><td><h3>" . _("Phone Features") . "</h3></td></tr>";
+
+ $dnd_cw_text.= $this->displayPhoneControls( $exten, 'call_waiting', 'CW', "Call Waiting");
+ $dnd_cw_text.= $this->displayPhoneControls( $exten, 'do_not_disturb', 'DND', "Do Not Disturb");
+
+ $dnd_cw_text .= "</table>";
+ }
+
+ if ($SETTINGS_ALLOW_CALLFORWARD_SETTINGS) {
+
+ $set_call_forward_text .= "<SCRIPT LANGUAGE='JavaScript'>
+ <!-- Begin
+ function rowCounter(field, maxlimit) {
+ temp = field.value.split('\u000A',maxlimit+1)
+ field.value = temp.join('\u000A')
+ if (temp.length == maxlimit+1) {
+ field.value = field.value.substring(0, field.value.length-1)
+ }
+ }
+
+ function disable_fields() {
+
+ if (document.ari_settings.call_forward_enable.checked) {
+ document.ari_settings.call_forward_number.style.backgroundColor = '#FFF';
+ document.ari_settings.call_forward_number.disabled = false;
+ }
+ else {
+ document.ari_settings.call_forward_number.style.backgroundColor = '#DDD';
+ document.ari_settings.call_forward_number.disabled = true;
+ }
+
+ if (document.ari_settings.call_forward_busy_enable.checked) {
+ document.ari_settings.call_forward_busy_number.style.backgroundColor = '#FFF';
+ document.ari_settings.call_forward_busy_number.disabled = false;
+ }
+ else {
+ document.ari_settings.call_forward_busy_number.style.backgroundColor = '#DDD';
+ document.ari_settings.call_forward_busy_number.disabled = true;
+ }
+
+ if (document.ari_settings.call_forward_unavailable_enable.checked) {
+ document.ari_settings.call_forward_unavailable_number.style.backgroundColor = '#FFF';
+ document.ari_settings.call_forward_unavailable_number.disabled = false;
+ }
+ else {
+ document.ari_settings.call_forward_unavailable_number.style.backgroundColor = '#DDD';
+ document.ari_settings.call_forward_unavailable_number.disabled = true;
+ }
+ }
+ // End -->
+ </script>";
+
+ $set_call_forward_text.= "<table class='settings'>";
+ $set_call_forward_text.= "<tr><td><h3>" . _("Call Forwarding") . "</h3></td></tr>";
+
+ $set_call_forward_text.= $this->displayCallForwardControls( $exten, 'call_forward', 'CF', "Unconditional:");
+ $set_call_forward_text.= $this->displayCallForwardControls( $exten, 'call_forward_unavailable', 'CFU', "Unavailable:");
+ $set_call_forward_text.= $this->displayCallForwardControls( $exten, 'call_forward_busy', 'CFB', "Busy:");
+
+ $set_call_forward_text .= "</table>";
+ }
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ if ($_SESSION['ari_user']['admin_settings']) {
+ $headerText = _("Phone Features");
+ } else {
+ $headerText = sprintf(_("Phone Features for %s (%s)"),$displayname,$exten);
+ }
+
+ $ret .= $display->displayHeaderText($headerText);
+ $ret .= $display->displayLine();
+ $ret .= "
+ <form class='settings' name='ari_settings' action='' method='GET'>
+ <input type=hidden name=m value=" . $m . ">
+ <input type=hidden name=f value='action'>
+ <input type=hidden name=a value='update'>
+ <br>
+ " . $dnd_cw_text . "
+ <br>
+ " . $set_call_forward_text . "
+ <br>
+ <input name='submit' type='submit' value='" . _("Update") . "'>
+ </form>";
+
+return $ret;
+}
+//*****************************************************************************
+ function setPhoneSetting( $databaseCallFwdType, $exten, $state_value) {
+
+ global $asterisk_manager_interface;
+
+ $type_opt = ($state_value != "") ? "put":"del";
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database $type_opt $databaseCallFwdType $exten $state_value\r\n\r\n");
+ }
+
+//*****************************************************************************
+ function getPhoneSetting($exten, $databaseCallFwdType) {
+
+ global $asterisk_manager_interface;
+ $number = '';
+
+ $result = false;
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get $databaseCallFwdType $exten\r\n\r\n");
+ if (stristr($response, 'ENABLED')) {
+ $result = true;
+ }
+ elseif (stristr($response, 'YES')) {
+ $result = true;
+ }
+
+ return $result;
+ }
+//*****************************************************************************
+ function storePhoneSetting( $args, $exten, $settingType, $databaseCallFwdType, $state_value)
+ {
+ $setting_enable = getArgument( $args, $settingType . '_enable');
+
+ $this->setPhoneSetting( $databaseCallFwdType, $exten, ($setting_enable == 'checked')?$state_value:"");
+ }
+
+//*****************************************************************************
+ function displayPhoneControls( $exten, $callFwdType, $databaseCallFwdType, $title)
+ {
+
+ $phone_setting_enable = ($this->getPhoneSetting($exten, $databaseCallFwdType)) ? 'checked':'';
+
+ $ret = "\n<tr>";
+ $ret.= "<td>";
+ $ret.= "<label><input " . $phone_setting_enable . " type=checkbox name='" . $callFwdType . "_enable' value='checked' >";
+ $ret.= "<small>" . _($title) . "</small></label>";
+ $ret.= "</td>";
+ $ret.= "</tr>\n";
+
+ return $ret;
+ }
+//*****************************************************************************
+ /*
+ * Sets Asterisk call forward setting
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $state
+ * Call forward enable or disable
+ * @param $call_forward_number
+ * Call forward number
+ * @param $variable_opt
+ * Call forward type (CF, CFU, CFB)
+ */
+ function setCallForward($exten,$state,$call_forward_number, $variable_opt = "CF") {
+
+ global $asterisk_manager_interface;
+
+ if ($state) {
+ $type_opt = "put";
+ $value_opt = $call_forward_number;
+ }
+ else {
+ $type_opt = "del";
+ }
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database $type_opt $variable_opt $exten $value_opt\r\n\r\n");
+ }
+
+ /*
+ * Gets call forward number if set
+ *
+ * @param $exten
+ * Extension to get information about
+ * @return $number
+ * call forward number returned if set
+ * @param $variable_opt
+ * Call forward type (CF, CFU, CFB)
+ */
+ function getCallForwardNumber($exten, $variable_opt = "CF") {
+
+ global $asterisk_manager_interface;
+
+ $number = '';
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get $variable_opt $exten\r\n\r\n");
+ if (is_numeric($response)) {
+ $number = $response;
+ }
+
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_call_forward_number']);
+ if ($stripped==$number) {
+ $number = $_COOKIE['ari_call_forward_number'];
+ }
+
+ return $number;
+ }
+
+
+ function storeCallForwardNumber( $args, $exten, $callFwdType, $databaseCallFwdType)
+ {
+ $call_forward_enable = getArgument($args, $callFwdType . '_enable');
+ $call_forward_number = getArgument($args, $callFwdType . '_number');
+
+ $stripped_call_forward_number = preg_replace('/-|\(|\)|\s/','',$call_forward_number);
+
+ if ($call_forward_enable && !is_numeric($stripped_call_forward_number)) {
+ $_SESSION['ari_error'] = _("Call forward number not changed") . "<br>" .
+ sprintf(_("Number %s must contain dial numbers (characters like '(', '-', and ')' are ok)"), $call_forward_number);
+ }
+ else {
+ $this->setCallForward( $exten, $call_forward_enable, $stripped_call_forward_number, $databaseCallFwdType);
+
+ // store cookie
+ $stripped = preg_replace('/-|\(|\)|\s/','',$_COOKIE['ari_' . $callFwdType]);
+ if ($call_forward_number && $stripped!=$stripped_call_forward_number) {
+ setcookie('ari_' . $callFwdType, $call_forward_number, time()+365*24*60*60);
+ }
+ }
+ }
+
+ function displayCallForwardControls( $exten, $callFwdType, $databaseCallFwdType, $title)
+ {
+ $call_forward_number = $this->getCallForwardNumber($exten, $databaseCallFwdType);
+
+ // If we have a value, we want the item checked
+ if ($call_forward_number) {
+ $call_forward_enable = 'checked';
+ }
+ else {
+ $call_forward_number = $_COOKIE['ari_' . $callFwdType ];
+ $call_forward_text_box_options = "disabled style='background: #DDD;'";
+ }
+
+ $ret = "\n<tr>";
+ $ret.= "<td>" . _($title) . "</td>";
+ $ret.= "<td>";
+ $ret.= "<input " . $call_forward_text_box_options . " name='" . $callFwdType . "_number' type='text' size=24 value='" . $call_forward_number . "'>";
+ $ret.= "</td>";
+ $ret.= "<td>";
+ $ret.= "<input " . $call_forward_enable . " type=checkbox name='" . $callFwdType . "_enable' value='checked' OnClick=\"disable_fields(); return true;\">";
+ $ret.= "<small>" . _("Enable") . "</small>";
+ $ret.= "</td>";
+ $ret.= "</tr>\n";
+
+ return $ret;
+ }
+} // class
+?>
diff --git a/fs_selfservice/fri/modules/settings.module b/fs_selfservice/fri/modules/settings.module
new file mode 100644
index 0000000..f20eb02
--- /dev/null
+++ b/fs_selfservice/fri/modules/settings.module
@@ -0,0 +1,813 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the call monitor recordings
+ */
+
+/**
+ * Class for settings
+ */
+class Settings {
+
+ var $protocol_table;
+ var $protocol_config_files;
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 9;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+
+ // determine what protocol user is using
+ global $ASTERISK_PROTOCOLS;
+
+ foreach ($ASTERISK_PROTOCOLS as $protocol => $value) {
+ $data = $this->getProtocolRecordSettings($value['table'],$_SESSION['ari_user']['extension']);
+ if (count($data)) {
+ $this->protocol_table = $value['table'];
+ $this->protocol_config_files = $value['config_files'];
+ break;
+ }
+ }
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ $ret = "";
+ $exten = $_SESSION['ari_user']['extension'];
+
+ // and we are not logged in as admin
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=Settings&f=display'>" . _("Phone Settings") . "</a></small></small></p><br>";
+ }
+
+ return $ret;
+ }
+
+ /*
+ * Acts on the user settings
+ *
+ * @param $args
+ * Common arguments
+ * @param $a
+ * action
+ */
+ function action($args) {
+
+ global $ARI_ADMIN_USERNAME;
+ global $ASTERISK_VOICEMAIL_CONF;
+ global $SETTINGS_ALLOW_VOICEMAIL_SETTINGS;
+ global $SETTINGS_ALLOW_VOICEMAIL_PASSWORD_SET;
+ global $SETTINGS_VOICEMAIL_PASSWORD_LENGTH;
+ global $SETTINGS_VOICEMAIL_PASSWORD_EXACT;
+ global $SETTINGS_ALLOW_CALL_RECORDING_SET;
+
+ // args
+ $m = getArgument($args,'m');
+ $a = getArgument($args,'a');
+
+ $voicemail_password = getArgument($args,'voicemail_password');
+ $voicemail_password_confirm = getArgument($args,'voicemail_password_confirm');
+ $voicemail_email_address = getArgument($args,'voicemail_email_address');
+ $voicemail_pager_address = getArgument($args,'voicemail_pager_address');
+ $voicemail_email_enable = getArgument($args,'voicemail_email_enable');
+ $voicemail_audio_format = getArgument($args,'voicemail_audio_format');
+ $record_in = getArgument($args,'record_in');
+ $record_out = getArgument($args,'record_out');
+
+ if (isset($_SESSION['ari_user']['voicemail_email'])) {
+ foreach (array_keys($_SESSION['ari_user']['voicemail_email']) as $key) {
+ $var = "voicemail_email_$key";
+ $$var = getArgument($args,$var);
+ }
+ }
+
+ if ($a=='update') {
+
+ $exten = $_SESSION['ari_user']['extension'];
+ if ($exten!=$ARI_ADMIN_USERNAME) {
+
+ // Make sure Follow-Me setup has not been deleted for this user since the last refresh
+ $follow_me_disabled_delayed = $_COOKIE['ari_follow_me_disabled'];
+
+ // voicemail settings
+ if ($SETTINGS_ALLOW_VOICEMAIL_SETTINGS && $_SESSION['ari_user']['voicemail_enabled']==1) {
+
+
+ // update voicemail password
+ if ($SETTINGS_ALLOW_VOICEMAIL_PASSWORD_SET) {
+
+ // update voicemail password
+ if ($voicemail_password=='' || $voicemail_password_confirm=='') {
+ $_SESSION['ari_error'] =
+ _("Voicemail password not changed") . "<br>" .
+ _("Password and password confirm must not be blank");
+ }
+ else if ((strlen($voicemail_password)<$SETTINGS_VOICEMAIL_PASSWORD_LENGTH) || !is_numeric($voicemail_password)) {
+ $_SESSION['ari_error'] =
+ _("Voicemail password not changed") . "<br>" .
+ sprintf(_("Passwords must be all numbers and greater than %d digits"),$SETTINGS_VOICEMAIL_PASSWORD_LENGTH);
+ }
+ else if (strlen($voicemail_password)!=$SETTINGS_VOICEMAIL_PASSWORD_LENGTH && $SETTINGS_VOICEMAIL_PASSWORD_EXACT || !is_numeric($voicemail_password)) {
+ $_SESSION['ari_error'] =
+ _("Voicemail password not changed") . "<br>" .
+ sprintf(_("Passwords must be all numbers and only %d digits"),$SETTINGS_VOICEMAIL_PASSWORD_LENGTH);
+ }
+ else if ($voicemail_password!=$voicemail_password_confirm) {
+ $_SESSION['ari_error'] =
+ _("Voicemail password not changed") . "<br>" .
+ _("Password and password confirm do not match");
+ }
+ else {
+
+ // check for writable the files
+ $temp_file = $ASTERISK_VOICEMAIL_CONF . ".tmp";
+ $fp = fopen($temp_file, "w");
+ if (!$fp) {
+ $_SESSION['ari_error'] =
+ _("Voicemail password not changed") . "<br>" .
+ sprintf(_("%s does not exist or is not writable"),$temp_file);
+ }
+ else if (!is_writable($ASTERISK_VOICEMAIL_CONF)) {
+ $_SESSION['ari_error'] =
+ _("Voicemail password not changed") . "<br>" .
+ sprintf(_("%s does not exist or is not writable"),$ASTERISK_VOICEMAIL_CONF);
+ }
+ else {
+
+ // update session
+ $_SESSION['ari_user']['voicemail_password'] = $voicemail_password;
+
+ // save password
+ $lines = file($ASTERISK_VOICEMAIL_CONF);
+ foreach ($lines as $key => $line) {
+ unset($value);
+ list($var,$value) = split('=>',$line);
+ $var = trim($var);
+ if ($var==$exten && $value) {
+
+ // write out line with password change
+ $buf = split(',',$value);
+ $buf[0] = $voicemail_password;
+ $line = $var . " => " . join(',', $buf);
+
+ fwrite($fp, $line);
+ }
+ else {
+ // write out original line with no changes
+ fwrite($fp, $line);
+ }
+ }
+ fclose($fp);
+ unlink($ASTERISK_VOICEMAIL_CONF);
+ rename($temp_file,$ASTERISK_VOICEMAIL_CONF);
+
+ $voicemail_reload = 1;
+ }
+ }
+
+ // voicemail email address
+ if ($voicemail_email_enable &&
+ ($voicemail_email_address && !preg_match('/@/',$voicemail_email_address) ||
+ ($voicemail_pager_address && !preg_match('/@/',$voicemail_pager_address)))) {
+ $_SESSION['ari_error'] =
+ _("Voicemail email and pager address not changed") . "<br>" .
+ ("'$voicemail_email_address' and '$voicemail_pager_address' must be a valid email addresses");
+ }
+ else {
+
+ // check for writable the files
+ $temp_file = $ASTERISK_VOICEMAIL_CONF . ".tmp";
+ $fp = fopen($temp_file, "w");
+ if (!$fp) {
+ $_SESSION['ari_error'] =
+ _("Voicemail email settings not changed") . "<br>" .
+ sprintf(_("%s does not exist or is not writable"),$temp_file);
+ }
+ else if (!is_writable($ASTERISK_VOICEMAIL_CONF)) {
+ $_SESSION['ari_error'] =
+ _("Voicemail email settings not changed") . "<br>" .
+ sprintf(_("%s does not exist or is not writable"),$ASTERISK_VOICEMAIL_CONF);
+ }
+ else {
+
+ // store cookie
+ if ($voicemail_email_enable) {
+ setcookie("ari_voicemail_email_address", $voicemail_email_address, time()+365*24*60*60);
+ setcookie("ari_voicemail_pager_address", $voicemail_pager_address, time()+365*24*60*60);
+ foreach (array_keys($_SESSION['ari_user']['voicemail_email']) as $key) {
+ $var = "voicemail_email_$key";
+ $var_cookie = "ari_" . $var;
+ setcookie("$var_cookie", $$var, time()+365*24*60*60);
+ }
+ }
+
+ // update session
+ $_SESSION['ari_user']['voicemail_email_enable'] = $voicemail_email_enable;
+ if ($voicemail_email_enable) {
+ $_SESSION['ari_user']['voicemail_email_address'] = $voicemail_email_address;
+ $_SESSION['ari_user']['voicemail_pager_address'] = $voicemail_pager_address;
+ foreach (array_keys($_SESSION['ari_user']['voicemail_email']) as $key) {
+ $option = "voicemail_email_$key";
+ $_SESSION['ari_user']['voicemail_email'][$key] = $$option;
+ }
+ }
+
+ // save settings
+ if (!$voicemail_email_enable) {
+ $voicemail_email_address = '';
+ $voicemail_pager_address = '';
+ }
+
+ $lines = file($ASTERISK_VOICEMAIL_CONF);
+ foreach ($lines as $key => $line) {
+ unset($value);
+ list($var,$value) = split('=>',$line);
+ $var = trim($var);
+ if ($var==$exten && $value) {
+
+ // write out line with voicemail email change
+ $buf = split(',',$value);
+ $buf[2] = $voicemail_email_address;
+ $buf[3] = $voicemail_pager_address;
+
+ foreach ($_SESSION['ari_user']['voicemail_email'] as $key => $value) {
+ $option = "voicemail_email_$key";
+ if ($$option && $key) {
+ $options .= $key . "=" . $value;
+ }
+ else {
+ $options .= $key . "=no";
+ }
+ $options .= "|";
+ }
+ $buf[4] = substr($options, 0, -1);
+
+ $line = $var . " =>" . join(',', $buf);
+ if (substr($line, 0, -1)!="\n") {
+ $line .= "\n";
+ }
+
+ fwrite($fp, $line);
+ }
+ else {
+
+ // write out original line with no changes
+ fwrite($fp, $line);
+ }
+ }
+ fclose($fp);
+ unlink($ASTERISK_VOICEMAIL_CONF);
+ rename($temp_file,$ASTERISK_VOICEMAIL_CONF);
+
+ $voicemail_reload = 1;
+ }
+ }
+
+ // reload asterisk voicemail
+ if ($voicemail_reload) {
+ $this->reloadAsteriskVoicemail();
+ }
+ }
+
+ // update voicemail audio format setting
+ setcookie("ari_voicemail_audio_format", $voicemail_audio_format, time()+365*24*60*60);
+ }
+
+ // update call monitor record setting
+ if ($SETTINGS_ALLOW_CALL_RECORDING_SET) {
+ if ($record_in && $record_out) {
+ $this->setRecordSettings($exten,$record_in,$record_out);
+ }
+ }
+ }
+ }
+
+ // redirect to see updated page
+ $ret .= "
+ <head>
+ <script>
+ <!--
+ window.location = \"" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "\"
+ // -->
+ </script>
+ </head>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+ global $SETTINGS_ALLOW_VOICEMAIL_SETTINGS;
+ global $SETTINGS_ALLOW_VOICEMAIL_PASSWORD_SET;
+ global $SETTINGS_VOICEMAIL_PASSWORD_LENGTH;
+ global $SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS;
+ global $ARI_VOICEMAIL_AUDIO_FORMAT_DEFAULT;
+ global $SETTINGS_ALLOW_CALL_RECORDING_SET;
+
+ global $loaded_modules;
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $exten = $_SESSION['ari_user']['extension'];
+
+ $language = new Language();
+ $display = new DisplaySearch();
+
+ // get data
+ $data = $this->getRecordSettings($_SESSION['ari_user']['extension']);
+
+ // lang setting options
+ if (extension_loaded('gettext')) {
+ $setLangText = "<p class='lang'>" . _("Language:") . " " . $language->GetForm() . "</p>";
+ }
+
+
+ // voicemail settings
+ if ($SETTINGS_ALLOW_VOICEMAIL_SETTINGS && $_SESSION['ari_user']['voicemail_enabled']==1 &&
+ in_array('voicemail',array_keys($loaded_modules))) {
+ if ($SETTINGS_ALLOW_VOICEMAIL_PASSWORD_SET) {
+
+ if ($SETTINGS_VOICEMAIL_PASSWORD_EXACT) {
+ $voicemail_password_length_message = sprintf(_("Passwords must be all numbers and only %s digits"),$SETTINGS_VOICEMAIL_PASSWORD_LENGTH);
+ }
+ else {
+ $voicemail_password_length_message = sprintf(_("Passwords must be all numbers and at least %s digits"),$SETTINGS_VOICEMAIL_PASSWORD_LENGTH);
+ }
+
+ $set_voicemail_password_text = "
+ <tr>
+ <td>" . _("Voicemail Password:") . "</td>
+ <td>
+ <input name='voicemail_password' type='password' size=16 value=" . $_SESSION['ari_user']['voicemail_password'] . ">
+ </td>
+ </tr>
+ <tr>
+ <td>" . _("Enter again to confirm:") . "</td>
+ <td>
+ <input name='voicemail_password_confirm' type='password' size=16 value=" . $_SESSION['ari_user']['voicemail_password'] . ">
+ </td>
+ </tr>
+ <tr>
+ <td class='note' colspan=2><small>" . $voicemail_password_length_message . "</small></td>
+ </tr>";
+ }
+
+ if (isset($_SESSION['ari_user']['voicemail_email'])) {
+
+ if ($_SESSION['ari_user']['voicemail_email_enable']) {
+ $voicemail_email_address = $_SESSION['ari_user']['voicemail_email_address'];
+ $voicemail_pager_address = $_SESSION['ari_user']['voicemail_pager_address'];
+ $voicemail_email_enable = 'checked';
+
+ foreach (array_keys($_SESSION['ari_user']['voicemail_email']) as $key) {
+ $var = "voicemail_email_$key";
+ $var_enable = $var . "enable";
+ if ($_SESSION['ari_user']['voicemail_email'][$key]=='yes') {
+ $$var_enable = 'checked';
+ }
+ }
+ }
+ else {
+
+ $voicemail_email_address = $_COOKIE['ari_voicemail_email_address'];
+ $voicemail_email_text_box_options = "disabled style='background: #DDD;'";
+ $voicemail_pager_address = $_COOKIE['ari_voicemail_pager_address'];
+ $voicemail_pager_text_box_options = "disabled style='background: #DDD;'";
+
+ foreach ($_SESSION['ari_user']['voicemail_email'] as $key => $value) {
+ $var = "voicemail_email_$key";
+ $var_cookie = "ari_" . $var;
+ $var_enable = $var . "enable";
+ $var_text_box_options = $var . "text_box_options";
+
+ $$var_text_box_options = "disabled";
+ if ($_COOKIE[$var_cookie]=='yes') {
+ $$var_enable = 'checked';
+ }
+ }
+ }
+
+ $set_voicemail_email_text = "
+
+ <tr>
+ <td> " . _("Email Notification") . " <input " . $voicemail_email_enable . " type=checkbox name='voicemail_email_enable' value='1' OnClick=\"disable_fields(); return true;\">
+ <small> " ._("Enable") . " </small>
+ </td>
+ </tr><tr>
+ <td><a href='#' class='info'>" . _("Email Voicemail To:") . "<span>" . ("Email a notification, including audio file if indicated below.") . " </span></a></td>
+ <td>
+ <input " . $voicemail_email_text_box_options . " name='voicemail_email_address' type='text' size=48 value='" . $voicemail_email_address . "'>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='#' class='info'>" . _("Pager Email Notification To:") . "<span>" . ("Email a short notification") . " </span></a></td>
+ <td>
+ <input " . $voicemail_pager_text_box_options . " name='voicemail_pager_address' type='text' size=48 value='" . $voicemail_pager_address . "'>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ </tr>";
+
+ foreach ($_SESSION['ari_user']['voicemail_email'] as $key => $value) {
+
+ $var = "voicemail_email_$key";
+ $var_enable = $var . "enable";
+ $var_text_box_options = $var . "text_box_options";
+ if ($SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS[$key]) {
+ $var_text = $SETTINGS_VOICEMAIL_EMAIL_OPTION_DESCRIPTIONS[$key];
+ }
+ else {
+ $var_text = $key;
+ }
+
+ if ($value != 'yes' && $value != 'no' && $value !='') {
+
+ $size = strlen($value) - 1;
+ $set_voicemail_email_text .= "
+ <tr>
+ <td></td>
+ <td>
+ <input type=text size='" . $size . "' name='" . $var . "' value='" . $value . "' OnClick=\"disable_fields(); return true;\">
+ <small>" . $var_text . "</small>
+ </td>
+ </tr>";
+ }
+ else {
+
+ $set_voicemail_email_text .= "
+ <tr>
+ <td></td>
+ <td>
+ <input " . $$var_enable . " " . $$var_text_box_options . " type=checkbox name='" . $var . "' value='yes' OnClick=\"disable_fields(); return true;\">
+ <small>" . $var_text . "</small>
+ </td>
+ </tr>";
+ }
+ }
+ }
+
+ $wav_enable = 'selected';
+ if ($_COOKIE['ari_voicemail_audio_format']=='.gsm'||
+ ($_COOKIE['ari_voicemail_audio_format']=='' && $ARI_VOICEMAIL_AUDIO_FORMAT_DEFAULT='.gsm')) {
+ $wav_enable = '';
+ $gsm_enable = 'selected';
+ }
+
+ $set_voicemail_audio_format_text = "
+ <tr>
+ <td>" . _("Audio Format:") . "</td>
+ <td>
+ <select name='voicemail_audio_format'>
+ <option value='.wav' " . $wav_enable . ">" . _("Best Quality") . " (.wav)</option>
+ <option value='.gsm' " . $gsm_enable . ">" . _("Smallest Download") . " (.gsm)</option>
+ </select>
+ </td>
+ </tr>";
+
+ $set_voicemail_text = "
+ <table class='settings'>
+ <tr>
+ <td><h3>" . _("Voicemail Settings") . "</h3></td>
+ </tr>
+ " . $set_voicemail_password_text . "
+ " . $set_voicemail_email_text . "
+ " . $set_voicemail_audio_format_text . "
+ </table>";
+ }
+
+ // call monitor settings
+ if ($this->getFreePBXVersion() &&
+ $SETTINGS_ALLOW_CALL_RECORDING_SET &&
+ in_array('callmonitor',array_keys($loaded_modules))) {
+
+ foreach($data as $key=>$value) {
+ if ($key=='record_in') {
+ if ($value=='Always') {
+ $ri_always = 'checked=checked';
+ }
+ elseif ($value=='Never') {
+ $ri_never = 'checked=checked';
+ }
+ elseif ($value=='Adhoc') {
+ $ri_on_demand = 'checked=checked';
+ }
+ }
+ if ($key=='record_out') {
+ if ($value=='Always') {
+ $ro_always = 'checked=checked';
+ }
+ elseif ($value=='Never') {
+ $ro_never = 'checked=checked';
+ }
+ elseif ($value=='Adhoc') {
+ $ro_on_demand = 'checked=checked';
+ }
+ }
+ }
+
+ $set_callmonitor_text = "
+ <table class='settings'>
+ <tr>
+ <td><h3>" . _("Call Monitor Settings") . "</h3></td>
+ </tr>
+ <tr>
+ <td>" . _("Record INCOMING:") . " </td>
+ <td>
+ <input type='radio' name='record_in' value='Always' " . $ri_always . "/> " . _("Always") . "
+ <input type='radio' name='record_in' value='Never' " . $ri_never . "/> " . _("Never") . "
+ <input type='radio' name='record_in' value='Adhoc' " . $ri_on_demand . "/> " . _("On-Demand") . "
+ </td>
+ </tr>
+ <tr>
+ <td>" . _("Record OUTGOING:") . " </td>
+ <td>
+ <input type='radio' name='record_out' value='Always' " . $ro_always . "/> " . _("Always") . "
+ <input type='radio' name='record_out' value='Never' " . $ro_never . "/> " . _("Never") . "
+ <input type='radio' name='record_out' value='Adhoc' " . $ro_on_demand . "/> " . _("On-Demand") . "
+ </td>
+ </tr>
+ </table>";
+ }
+
+ // javascript enable options
+ if (isset($_SESSION['ari_user']['voicemail_email']) &&
+ in_array('voicemail',array_keys($loaded_modules))) {
+ foreach ($_SESSION['ari_user']['voicemail_email'] as $key => $value) {
+ $var = "voicemail_email_$key";
+ $js_voicemail_email_disable .= "
+ document.ari_settings.$var.disabled = false;";
+ $js_voicemail_email_enable .= "
+ document.ari_settings.$var.disabled = true;";
+ }
+
+ $js_voicemail_script = "
+ if (document.ari_settings.voicemail_email_enable.checked) {
+ document.ari_settings.voicemail_email_address.style.backgroundColor = '#FFF';
+ document.ari_settings.voicemail_email_address.disabled = false;
+ document.ari_settings.voicemail_email_address.value='" . $voicemail_email_address . "';
+ document.ari_settings.voicemail_pager_address.style.backgroundColor = '#FFF';
+ document.ari_settings.voicemail_pager_address.disabled = false;
+ document.ari_settings.voicemail_pager_address.value='" . $voicemail_pager_address . "';
+ " . $js_voicemail_email_disable . "
+ }
+ else {
+ document.ari_settings.voicemail_email_address.style.backgroundColor = '#DDD';
+ document.ari_settings.voicemail_email_address.disabled = true;
+ document.ari_settings.voicemail_pager_address.style.backgroundColor = '#DDD';
+ document.ari_settings.voicemail_pager_address.disabled = true;
+ " . $js_voicemail_email_enable . "
+ }";
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+
+ $headerText = sprintf(_("Phone Settings for %s (%s)"),$displayname,$exten);
+
+ $ret .= $display->displayHeaderText($headerText);
+ $ret .= $display->displayLine();
+
+ $ret .= "
+ <SCRIPT LANGUAGE='JavaScript'>
+ <!-- Begin
+ function rowCounter(field, maxlimit) {
+ temp = field.value.split('\u000A',maxlimit+1)
+ field.value = temp.join('\u000A')
+ if (temp.length == maxlimit+1) {
+ field.value = field.value.substring(0, field.value.length-1)
+ }
+ }
+
+ function disable_fields() {";
+ $ret .= $js_voicemail_script . "
+ }
+ // End -->
+ </script>";
+
+ $ret .= "
+ " . $setLangText . "
+ <form class='settings' name='ari_settings' action='' method='GET'>
+ <input type=hidden name=m value=" . $m . ">
+ <input type=hidden name=f value='action'>
+ <input type=hidden name=a value='update'>
+ <br>
+ " . $set_voicemail_text . "
+ <br>
+ " . $set_callmonitor_text . "
+ <br>
+ <input name='submit' type='submit' value='" . _("Update") . "'>
+ </form>";
+
+ return $ret;
+ }
+
+
+
+
+
+
+ /*
+ * Sets Asterisk call recording setting
+ *
+ * @param $exten
+ * Extension to modify
+ * @param $direction
+ * Call direction
+ * @param $state
+ * State to set to
+ */
+ function setRecordSettings($exten,$state_in,$state_out) {
+
+ global $asterisk_manager_interface;
+
+ if (version_compare($this->getFreePBXVersion(), '1.10', '<')) {
+
+ if ($state_in=="Always") {
+ $type_opt = "put";
+ $value_opt = " " . "ENABLED";
+ }
+ elseif ($state_in=="Never") {
+ $type_opt = "put";
+ $value_opt = " " . "DISABLED";
+ }
+ else {
+ $type_opt = "del";
+ $value_opt = "";
+ }
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database $type_opt RECORD-IN $exten $value_opt\r\n\r\n");
+
+ if ($state_out=="Always") {
+ $type_opt = "put";
+ $value_opt = " " . "ENABLED";
+ }
+ elseif ($state_out=="Never") {
+ $type_opt = "put";
+ $value_opt = " " . "DISABLED";
+ }
+ else {
+ $type_opt = "del";
+ $value_opt = "";
+ }
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database $type_opt RECORD-OUT $exten $value_opt\r\n\r\n");
+ }
+ else {
+
+ $value_opt= "out=".$state_out."|in=".$state_in;
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database put AMPUSER $exten/recording $value_opt\r\n\r\n");
+ }
+ }
+
+ /*
+ * Gets record settings for a protocol
+ *
+ * @param $table
+ * Table to pull information from
+ * @param $exten
+ * Extension to get information about
+ * @return $data
+ * call monitor record settings
+ */
+ function getProtocolRecordSettings($table,$exten) {
+
+ global $asterisk_manager_interface;
+
+ $data = array();
+
+ if (version_compare($this->getFreePBXVersion(), '1.10', '<')) {
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get RECORD-IN $exten\r\n\r\n");
+ if (preg_match("/ENABLED/",$response)) {
+ $data['record_in'] = 'Always';
+ }
+ elseif (preg_match("/DISABLED/",$response)) {
+ $data['record_in'] = 'Never';
+ }
+ else {
+ $data['record_in'] = 'Adhoc';
+ }
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get RECORD-OUT $exten\r\n\r\n");
+ if (preg_match("/ENABLED/",$response)) {
+ $data['record_out'] = 'Always';
+ }
+ elseif (preg_match("/DISABLED/",$response)) {
+ $data['record_out'] = 'Never';
+ }
+ else {
+ $data['record_out'] = 'Adhoc';
+ }
+ }
+ else {
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: database get AMPUSER $exten/recording\r\n\r\n");
+ if (strstr($response,"in=Always")) {
+ $data['record_in'] = 'Always';
+ }
+ elseif (strstr($response,"in=Never")) {
+ $data['record_in'] = 'Never';
+ }
+ else {
+ $data['record_in'] = 'Adhoc';
+ }
+ if (strstr($response,"out=Always")) {
+ $data['record_out'] = 'Always';
+ }
+ elseif (strstr($response,"out=Never")) {
+ $data['record_out'] = 'Never';
+ }
+ else {
+ $data['record_out'] = 'Adhoc';
+ }
+ }
+
+ return $data;
+ }
+
+ /*
+ * Gets record settings
+ *
+ * @param $exten
+ * Extension to get information about
+ * @param $data
+ * Reference to the variable to store the data in
+ */
+ function getRecordSettings($exten) {
+
+ // check protocol tables first
+ $data = $this->getProtocolRecordSettings($this->protocol_table,$exten);
+
+ return $data;
+ }
+
+ /*
+ * Reloads Asterisk Configuration
+ */
+ function reloadAsteriskVoicemail() {
+
+ global $asterisk_manager_interface;
+
+ $response = $asterisk_manager_interface->Command("Action: Command\r\nCommand: Reload app_voicemail.so\r\n\r\n");
+ }
+
+ /*
+ * Gets FreePBX Version
+ */
+ function getFreePBXVersion() {
+
+ if (isset($_SESSION['dbh_asterisk'])) {
+ $sql = "SELECT * FROM admin WHERE variable = 'version'";
+ $results = $_SESSION['dbh_asterisk']->getAll($sql);
+ if(DB::IsError($results)) {
+ $_SESSION['ari_error'] = $results->getMessage();
+ }
+
+ return $results[0][1];
+ }
+ }
+
+ function lookupSetExtensionFormat($exten) {
+
+ if (trim($exten) == "") return $exten;
+
+ $exten = preg_replace("/[^0-9*]/", "", $exten);
+
+ $sql = "SELECT extension FROM users WHERE extension = '".$exten."'";
+ $asa = $_SESSION['dbh_asterisk']->getrow($sql, DB_FETCHMODE_ASSOC);
+ if (!is_array($asa)) {
+ return $exten.'#';
+ } else {
+ return $exten;
+ }
+ }
+
+
+} // class
+
+?>
diff --git a/fs_selfservice/fri/modules/voicemail.module b/fs_selfservice/fri/modules/voicemail.module
new file mode 100644
index 0000000..aad1456
--- /dev/null
+++ b/fs_selfservice/fri/modules/voicemail.module
@@ -0,0 +1,805 @@
+<?php
+
+/**
+ * @file
+ * Functions for the interface to the voicemail recordings
+ */
+
+/**
+ * Class for voicemail
+ */
+class Voicemail {
+
+ /*
+ * rank (for prioritizing modules)
+ */
+ function rank() {
+
+ $rank = 1;
+ return $rank;
+ }
+
+ /*
+ * init
+ */
+ function init() {
+ }
+
+ /*
+ * Adds menu item to nav menu
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navMenu($args) {
+
+ global $ARI_NO_LOGIN;
+
+ // check logout
+ if ($_SESSION['ari_user'] && !$ARI_NO_LOGIN) {
+ $logout = 1;
+ }
+
+ if ($logout!='') {
+ $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=Voicemail&f=display'>" . _("Voicemail") . "</a></small></small></p>";
+ }
+
+ return $ret;
+ }
+
+ /*
+ * Deletes selected voicemails and updates page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function navSubMenu($args) {
+
+ global $ASTERISK_VOICEMAIL_PATH;
+ global $ASTERISK_VOICEMAIL_FOLDERS;
+
+ // args
+ $m = getArgument($args,'m');
+ $q = getArgument($args,'q');
+ $current_folder = getArgument($args,'folder');
+
+ $context = $_SESSION['ari_user']['context'];
+ $extension = $_SESSION['ari_user']['extension'];
+
+ // check for voicemail enabled or admin
+ if ($_SESSION['ari_user']['voicemail_enabled']!=1 ||
+ $extension=='admin') {
+ return;
+ }
+
+ // make folder list
+ $paths = split(';',$ASTERISK_VOICEMAIL_PATH);
+ $i = 0;
+ while ($ASTERISK_VOICEMAIL_FOLDERS[$i]) {
+
+ $f = $ASTERISK_VOICEMAIL_FOLDERS[$i]['folder'];
+ $fn = $ASTERISK_VOICEMAIL_FOLDERS[$i]['name'];
+
+ foreach($paths as $key => $path) {
+
+ $path = appendPath($path,$context);
+ $path = appendPath($path,$extension);
+
+ if (is_dir($path) && is_readable($path)) {
+ $dh = opendir($path);
+ while (false!== ($folder = readdir($dh))) {
+
+ $folder_path = AppendPath($path,$folder);
+
+ if($folder!="." && $folder!=".." &&
+ filetype($folder_path)=='dir') {
+
+ if ($f==$folder) {
+
+ // get message count
+ $indexes = $this->getVoicemailIndex($folder_path,$q,$order,$sort);
+ $record_count = 0;
+ $record_count += $this->getVoicemailCount($indexes);
+
+ // set current folder color
+ $class='';
+ if ($current_folder==$folder ||
+ ($current_folder=='' && $ASTERISK_VOICEMAIL_FOLDERS[0]['folder']==$folder)) {
+ $class = "class='current'";
+ }
+
+ // add folder to list
+ $ret .= "<p><small><small>
+ <a " . $class . " href='" . $_SESSION['ARI_ROOT'] . "?m=Voicemail&q=" . $q . "&folder=" . $f. "'>
+ " . $fn . " (" . $record_count . ")" . "
+ </a>
+ </small></small></p>";
+ }
+ }
+ }
+ }
+ }
+ $i++;
+ }
+
+ return $ret;
+ }
+
+ /*
+ * Acts on the selected voicemails in the method indicated by the action and updates page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function msgAction($args) {
+
+ global $ASTERISK_VOICEMAIL_FOLDERS;
+
+ // args
+ $m = getArgument($args,'m');
+ $a = getArgument($args,'a');
+ $folder = getArgument($args,'folder');
+ $q = getArgument($args,'q');
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+ $order = getArgument($args,'order');
+ $sort = getArgument($args,'sort');
+
+ // get files
+ $files = array();
+ foreach($_REQUEST as $key => $value) {
+ if (preg_match('/selected/',$key)) {
+ array_push($files, $value);
+ }
+ }
+
+ if ($a=='delete') {
+ $this->deleteVoicemailData($files);
+ }
+ else if ($a=='move_to') {
+ $folder_rx = getArgument($args,'folder_rx');
+ if ($folder_rx=='') {
+ $_SESSION['ari_error']
+ = _("A folder must be selected before the message can be moved.");
+ }
+ else {
+ $context = $_SESSION['ari_user']['context'];
+ $extension = $_SESSION['ari_user']['extension'];
+ $this->moveVoicemailData($files, $context, $extension, $folder_rx);
+ }
+ }
+ else if ($a=='forward_to') {
+
+ $mailbox_rx = getArgument($args,'mailbox_rx');
+ list($context_rx,$extension_rx) = split('/',$mailbox_rx);
+ if ($extension_rx=='') {
+ $_SESSION['ari_error']
+ = _("An extension must be selected before the message can be forwarded.");
+ }
+ else {
+ $folder_rx = $ASTERISK_VOICEMAIL_FOLDERS[0]['folder'];
+ $this->moveVoicemailData($files, $context_rx, $extension_rx, $folder_rx);
+ }
+ }
+
+ // redirect to see updated page
+ $ret .= "
+ <head>
+ <script>
+ <!--
+ window.location = \"" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "&folder=" . $folder . "&q=" . $q . "&start=" . $start . "&span=" . $span . "&order=" . $order . "&sort=" . $sort . "\"
+ // -->
+ </script>
+ </head>";
+
+ return $ret;
+ }
+
+ /*
+ * Displays stats page
+ *
+ * @param $args
+ * Common arguments
+ */
+ function display($args) {
+
+ global $ASTERISK_VOICEMAIL_CONF;
+ global $ASTERISK_VOICEMAIL_PATH;
+ global $ASTERISK_VOICEMAIL_FOLDERS;
+ global $AJAX_PAGE_REFRESH_ENABLE;
+
+ $voicemail_audio_format = $_COOKIE['ari_voicemail_audio_format'];
+
+ $display = new DisplaySearch();
+
+ // args
+ $m = getArgument($args,'m');
+ $f = getArgument($args,'f');
+ $q = getArgument($args,'q');
+ $start = getArgument($args,'start');
+ $span = getArgument($args,'span');
+ $order = getArgument($args,'order');
+ $sort = getArgument($args,'sort');
+
+ $start = $start=='' ? 0 : $start;
+ $span = $span=='' ? 15 : $span;
+ $order = $order=='' ? 'calldate' : $order;
+ $sort = $sort=='' ? 'desc' : $sort;
+
+ $paths = split(';',$ASTERISK_VOICEMAIL_PATH);
+
+ $displayname = $_SESSION['ari_user']['displayname'];
+ $extension = $_SESSION['ari_user']['extension'];
+ $context = $_SESSION['ari_user']['context'];
+ $folder = getArgument($args,'folder');
+ if (!$folder) {
+ $folder = $ASTERISK_VOICEMAIL_FOLDERS[0]['folder'];
+ }
+
+ // get data
+ $data = array();
+ foreach($paths as $key => $path) {
+ $path = fixPathSlash($path);
+ $vm_path = $path . "$context/$extension/$folder";
+ $indexes = $this->getVoicemailIndex($vm_path,$q,$order,$sort);
+ $record_count += $this->getVoicemailCount($indexes);
+ $data = array_merge($data,$this->getVoicemailData($indexes,$start,$span));
+ }
+
+ // build controls
+
+ // get the recordings from the asterisk server
+ $filter = '';
+ $recursiveMax = 1;
+ $recursiveCount = 0;
+ $files = array();
+ foreach($paths as $key => $path) {
+ $path_files = GetFiles($path,$filter,$recursiveMax,$recursiveCount);
+ $files = array_merge($files,$path_files);
+ }
+
+ // move options
+ $i=0;
+ while ($ASTERISK_VOICEMAIL_FOLDERS[$i]) {
+ $cf = $ASTERISK_VOICEMAIL_FOLDERS[$i]['folder'];
+ $fn = $ASTERISK_VOICEMAIL_FOLDERS[$i]['name'];
+ if ($cf!=$folder) {
+ $move_options .= "<option VALUE='" . $cf . "'>&nbsp;&nbsp;&nbsp;&nbsp;" . $fn;
+ }
+ $i++;
+ }
+
+ // forward options
+ if (is_readable($ASTERISK_VOICEMAIL_CONF)) {
+ $lines = file($ASTERISK_VOICEMAIL_CONF);
+ $ext_array = array();
+ foreach ($lines as $key => $line) {
+
+ // get context for forward to mailbox
+ if (preg_match("/\[.*\]/i",$line)) {
+ $forwardContext = trim(preg_replace('/\[|\]/', '', $line));
+ }
+
+ // get username and add to options
+ if (preg_match("/\=\>/i",$line)) {
+ list($username,$value) = split('=>',$line);
+ $username = trim($username);
+ if ($username!=$_SESSION['ari_user']['extension']) {
+ //$ext_array[] = $username . "|" . $forwardContext;
+ list(,$real_name,) = split(",",$value,3);
+ $ext_array[] = $real_name . "|" . $username . "|" . $forwardContext;
+ }
+ }
+ } //foreach
+ //sort the array
+ sort($ext_array);
+
+ //get the size of the array
+ $array_size = count($ext_array) - 1;
+
+ //loop through the array and build the drop down list
+ foreach ($ext_array as $item)
+ {
+ //split the values apart
+ list($real_name,$username,$context) = explode("|",$item);
+
+ //add it to the drop down
+ $forward_options .= "<option VALUE='" . $context . "/" . $username . "'>" . substr($real_name,0,15) . " <" . $username . ">";
+ }
+ }
+ else {
+ $_SESSION['ari_error'] = "File not readable: " . $ASTERISK_VOICEMAIL_CONF;
+ return;
+ }
+
+ // table controls
+ $controls = "
+ <button class='infobar' type='submit' onclick=\"document.voicemail_form.a.value='delete'\">
+ " . _("delete") . "
+ </button>
+ <button class='infobar' type='submit' onclick=\"document.voicemail_form.a.value='move_to'\">
+ " . _("move_to") . "
+ </button>
+ <select name='folder_rx' style='width:124px;'>
+ <option VALUE=''>" . _("Folder") . "
+ " . $move_options . "
+ </select>
+ <button class='infobar' type='submit' onclick=\"document.voicemail_form.a.value='forward_to'\">
+ " . _("forward_to") . "
+ </button>
+ <select name='mailbox_rx'>
+ <option VALUE=''>
+ " . $forward_options . "
+ </select>";
+
+ // table header
+ $recording_delete_header = "<th></th>";
+
+ $fields[0]['field'] = "calldate";
+ $fields[0]['text'] = _("Date");
+ $fields[1]['field'] = "calldate";
+ $fields[1]['text'] = _("Time");
+ $fields[2]['field'] = "clid";
+ $fields[2]['text'] = _("Caller ID");
+ $fields[3]['field'] = "priority";
+ $fields[3]['text'] = _("Priority");
+ $fields[4]['field'] = "origmailbox";
+ $fields[4]['text'] = _("Orig Mailbox");
+ $fields[5]['field'] = "duration";
+ $fields[5]['text'] = _("Duration");
+ $i = 0;
+ while ($fields[$i]) {
+
+ $field = $fields[$i]['field'];
+ $text = $fields[$i]['text'];
+ if ($order==$field) {
+ if ($sort=='asc') {
+ $currentSort = 'desc';
+ $arrowImg = "<img src='theme/images/arrow-asc.gif' alt='sort'>";
+ }
+ else {
+ $currentSort = 'asc';
+ $arrowImg = "<img src='theme/images/arrow-desc.gif' alt='sort'>";
+ }
+
+ if ($i==1) {
+ $arrowImg = '';
+ }
+ }
+ else {
+ $arrowImg = '';
+ $currentSort = 'desc';
+ }
+
+ $unicode_q = urlencode($q);
+ $recording_header .= "<th><a href=" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "&f=" . $f . "&q=" . $unicode_q . "&order=" . $field . "&sort=" . $currentSort . ">" . $text . $arrowImg . "</a></th>";
+
+ $i++;
+ }
+ $recording_header .= "<th>" . _("Message") . "</th>";
+
+ // table body
+ if (isset($data)) {
+ foreach($data as $file=>$value) {
+
+ // recording popup link
+ $voicemail_audio_format = $voicemail_audio_format=='' ? '.wav' : $voicemail_audio_format;
+ $recording = preg_replace('/.txt/', $voicemail_audio_format, $file);
+ if (is_file($recording)) {
+ $recordingLink = "<a href='#' onClick=\"javascript:popUp('misc/recording_popup.php?recording=" . $recording . "&date=" . $date . "&time=" . $time . "'); return false;\">
+ " . _("play") . "
+ </a>";
+ }
+ else {
+ $_SESSION['ari_error'] = _("Voicemail recording(s) was not found.") . "<br>" .
+ sprintf(_("On settings page, change voicemail audio format. It is currently set to %s"),$voicemail_audio_format);
+ }
+
+ $tableText .= "
+ <tr>
+ <td class='checkbox'><input type=checkbox name='selected" . ++$i . "' value=" . $file . "></td>
+ <td width=68>" . GetDateFormat($value['origtime']) . "</td>
+ <td>" . GetTimeFormat($value['origtime']) . "</td>
+ <td width=100>" . $value[callerid] . "</td>
+ <td>" . $value[priority] . "</td>
+ <td width=90>" . $value[origmailbox] . "</td>
+ <td>" . $value[duration] . " sec</td>
+ <td>" . $recordingLink . "</td>
+ </tr>";
+ }
+ }
+
+ // options
+ $url_opts = array();
+ $url_opts['folder'] = $folder;
+ $url_opts['sort'] = $sort;
+ $url_opts['order'] = $order;
+
+ $error = 0;
+
+ // check for voicemail enabled
+ if ($_SESSION['ari_user']['voicemail_enabled']!=1) {
+ $_SESSION['ari_error'] = _("Voicemail Login not found.") . "<br>" .
+ _("No access to voicemail");
+ $error = 1;
+ }
+
+ // check admin
+ if ($extension=='admin') {
+ $_SESSION['ari_error'] = _("No Voicemail Recordings for Admin");
+ $error = 1;
+ }
+
+ // build page content
+ $ret .= checkErrorMessage();
+ if ($error) {
+ return $ret;
+ }
+
+ // ajax page refresh script
+ if ($AJAX_PAGE_REFRESH_ENABLE) {
+// $ret .= ajaxRefreshScript($args);
+ }
+
+ // header
+ $ret .= $display->displayHeaderText(sprintf(_("Voicemail for %s (%s)"),$displayname,$extension));
+ $ret .= $display->displaySearchBlock('left',$m,$q,$url_opts,true);
+
+ // start form
+ $ret .= "
+ <form name='voicemail_form' action='" . $_SESSION['ARI_ROOT'] . "' method='GET'>
+ <input type=hidden name=m value=" . $m . ">
+ <input type=hidden name=f value=msgAction>
+ <input type=hidden name=a value=''>
+ <input type=hidden name=q value=" . $q . ">
+ <input type=hidden name=folder value=" . $folder . ">
+ <input type=hidden name=start value=" . $start . ">
+ <input type=hidden name=span value=" . $span . ">
+ <input type=hidden name=order value=" . $order . ">
+ <input type=hidden name=sort value=" . $sort . ">";
+
+ $ret .= $display->displayInfoBarBlock($controls,$q,$start,$span,$record_count);
+
+ // add javascript for popup and message actions
+ $ret .= "
+ <SCRIPT LANGUAGE='JavaScript'>
+ <!-- Begin
+ function popUp(URL) {
+ popup = window.open(URL, 'play', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=1,width=324,height=110');
+ }
+
+ function checkAll(form,set) {
+ var elem = 0;
+ var i = 0;
+ while (elem = form.elements[i]) {
+ if (set) {
+ elem.checked = true;
+ } else {
+ elem.checked = false;
+ }
+ i++;
+ }
+ return true;
+ }
+ // End -->
+ </script>";
+
+ // voicemail delete recording controls
+ $ret .= "
+ <table>
+ <tr>
+ <td>
+ <small>" . _("select") . ": </small>
+ <small><a href='' OnClick=\"checkAll(document.voicemail_form,true); return false;\">" . _("all") . "</a></small>
+ <small><a href='' OnClick=\"checkAll(document.voicemail_form,false); return false;\">" . _("none") . "</a></small>
+ </td>
+ </tr>
+ </table>";
+
+ // table
+ $ret .= "
+ <table class='voicemail'>
+ <tr>
+ " . $recording_delete_header . "
+ " . $recording_header . "
+ </tr>
+ " . $tableText . "
+ </table>";
+
+ // end form
+ $ret .= "</form>";
+
+ $ret .= $display->displaySearchBlock('center',$m,$q,$url_opts,false);
+ $ret .= $display->displayNavigationBlock($m,$q,$url_opts,$start,$span,$record_count);
+
+ return $ret;
+ }
+
+ /*
+ * Gets voicemail data
+ *
+ * @param $data
+ * Reference to the variable to store the data in
+ * @param $q
+ * search string
+ */
+ function getVoicemailIndex($path,$q,$order,$sort) {
+
+ $indexes = array();
+
+ $filter = '.txt';
+ $recursiveMax = 0;
+ $recursiveCount = 0;
+ $files = getFiles($path,$filter,$recursiveMax,$recursiveCount);
+
+ if (isset($files)) {
+
+ // ugly, but sorts array by time stamp
+ foreach ($files as $file) {
+
+ if (is_file($file)) {
+
+ $lines = file($file);
+ foreach ($lines as $key => $line) {
+ unset($value);
+ list($key,$value) = split('=',$line);
+ if ($value) {
+
+ if ($key=="origtime") {
+ $calldate = $value;
+ $date = GetDateFormat($value);
+ $time = GetTimeFormat($value);
+ }
+ if ($key=="callerid") {
+ $callerid = $value;
+ }
+ if ($key=="priority") {
+ $priority = $value;
+ }
+ if ($key=="origmailbox") {
+ $origmailbox = $value;
+ }
+ if ($key=="duration") {
+ $duration = (int)$value;
+ }
+ }
+ }
+
+ // search filter
+ $found = 1;
+ if ($q) {
+
+ $found = 0;
+
+ if (preg_match("/" . $q . "/", $origmailbox) ||
+ preg_match("/" . $q . "/", $callerid) ||
+ preg_match("/" . $q . "/", $date) ||
+ preg_match("/" . $q . "/", $time)) {
+ $found = 1;
+ }
+ }
+ }
+
+ // add to index
+ if ($found) {
+ $indexes[$file] = $$order;
+ }
+ }
+
+ if (count($indexes)) {
+ if ($sort=='desc') {
+ arsort($indexes);
+ }
+ else {
+ asort($indexes);
+ }
+ }
+ }
+
+ return $indexes;
+ }
+
+ /*
+ * Deletes selected voicemails
+ *
+ * @param $files
+ * Array of files to delete
+ */
+ function deleteVoicemailData($files) {
+
+ foreach($files as $key => $path) {
+
+ // get file parts for search
+ $path_parts = pathinfo($path);
+ $path = fixPathSlash($path_parts['dirname']);
+
+ list($name,$ext) = split("\.",$path_parts['basename']);
+
+ // delete all related files using a wildcard
+ if (is_dir($path)) {
+ $hdl = opendir($path);
+ while ($fn = readdir($hdl)) {
+ if (preg_match("/" . $name ."/",$fn)) {
+ $file = $path . $fn;
+ unlink($file);
+ }
+ }
+ closedir($hdl);
+ }
+ }
+ }
+
+ /*
+ * Moves selected voicemails to a specified folder
+ *
+ * @param $files
+ * Array of files to delete
+ * @param $extension_rx
+ * Mailbox to move message to
+ * @param $folder_rx
+ * Folder to move the messages to
+ */
+ function moveVoicemailData($files,$context_rx,$extension_rx,$folder_rx) {
+
+ global $ASTERISK_VOICEMAIL_PATH;
+
+ $perm = fileperms($ASTERISK_VOICEMAIL_PATH);
+ $uid = fileowner($ASTERISK_VOICEMAIL_PATH);
+ $gid = filegroup($ASTERISK_VOICEMAIL_PATH);
+
+ // recieving path
+ $paths = split(';',$ASTERISK_VOICEMAIL_PATH);
+ $path_rx = appendPath($paths[0],$context_rx);
+ if (!is_dir($path_rx)) {
+ mkdir($path_rx, $perm);
+ chown($path_rx,intval($uid));
+ chgrp($path_rx,intval($gid));
+ }
+ $path_rx = appendPath($path_rx,$extension_rx);
+ if (!is_dir($path_rx)) {
+ mkdir($path_rx, $perm);
+ chown($path_rx,intval($uid));
+ chgrp($path_rx,intval($gid));
+ }
+ $path_rx = appendPath($path_rx,$folder_rx);
+ if (!is_dir($path_rx)) {
+ mkdir($path_rx, $perm);
+ chown($path_rx,intval($uid));
+ chgrp($path_rx,intval($gid));
+ }
+
+ // get recieving folder last message number
+ if (is_dir($path_rx)) {
+
+ $lastNum = -1;
+ $lastNumLen = 4;
+
+ $dh = opendir($path_rx);
+ while (false != ($filename = readdir($dh))) {
+ if($filename!="." && $filename!="..") {
+
+ $msg_path = $path_rx;
+ $msg_path = appendPath($msg_path,$filename);
+ if (is_file($msg_path)) {
+ $path_parts = pathinfo($msg_path);
+ $num = preg_replace("/[a-zA-Z]|\./",'', $path_parts['basename']);
+ if ($num > $lastNum) {
+ $lastNum = $num;
+ $lastNumLen = strlen($lastNum);
+ }
+ }
+ }
+ }
+ }
+ else {
+ $_SESSION['ari_error'] = sprintf(_("Could not create mailbox folder %s on the server"),$folder_rx);
+ return;
+ }
+
+ // copy files to new location, incrementing each message number
+ asort($files);
+ foreach($files as $key => $path) {
+
+ // get file parts for search
+ $path_parts = pathinfo($path);
+ $path = $path_parts['dirname'];
+ $path = fixPathSlash($path);
+ list($name,$ext) = split("\.",$path_parts['basename']);
+ if (is_dir($path)) {
+
+ $lastNum++;
+ $hdl = opendir($path);
+ while ($fn = readdir($hdl)) {
+ if (preg_match("/" . $name . "/",$fn)) {
+ $src = $path . $fn;
+ $path_parts = pathinfo($src);
+ $folder_rx = preg_replace("/\d+/",sprintf("%0" . $lastNumLen . "d",$lastNum),$path_parts['basename']);
+ $dst = appendPath($path_rx,$folder_rx);
+ if (is_writable($src) && is_writable($path_rx)) {
+
+ $perm = fileperms($src);
+ $uid = fileowner($src);
+ $gid = filegroup($src);
+
+ copy($src,$dst);
+
+ if (is_writable($dst)) {
+ chmod($dst, $perm);
+ chown($dst,intval($uid));
+ chgrp($dst,intval($gid));
+ }
+
+ unlink($src);
+ }
+ else {
+ $_SESSION['ari_error'] = sprintf(_("Permission denied on folder %s or %s"),$src,$path_rx);
+ return;
+ }
+ }
+ }
+ closedir($hdl);
+ }
+ }
+ }
+
+ /*
+ * Gets voicemail record count
+ *
+ * @param $indexes
+ * array of files to be counted
+ * @return $count
+ * number of cdr records counted
+ */
+ function getVoicemailCount($indexes) {
+
+ $count = count($indexes);
+
+ return $count;
+ }
+
+ /*
+ * Gets voicemail data
+ *
+ * @param $indexes
+ * array of voicemail files
+ * @param $start
+ * message number to start page with
+ * @param $span
+ * number of messages to display on page
+ * @param $data
+ * Reference to the variable to store the data in
+ */
+ function getVoicemailData($indexes,$start,$span) {
+
+ $data = array();
+
+ if (!isset($indexes)) {
+ return;
+ }
+
+ // populate array
+ $i = 0;
+ foreach ($indexes as $file => $index) {
+ if ($i>$start-1+$span) {
+ return $data;
+ }
+ elseif ($i>$start-1 && $i<$start+$span) {
+ $lines = file($file);
+ foreach ($lines as $key => $line) {
+ unset($value);
+ list($key,$value) = split('=',$line);
+ if ($value) {
+ $data[$file][$key] = $value;
+ }
+ }
+ }
+ $i++;
+ }
+
+ return $data;
+ }
+
+}
+
+
+?> \ No newline at end of file
diff --git a/fs_selfservice/fri/theme/global.css b/fs_selfservice/fri/theme/global.css
new file mode 100644
index 0000000..cd97aa2
--- /dev/null
+++ b/fs_selfservice/fri/theme/global.css
@@ -0,0 +1,87 @@
+/*
+ * Global Styles
+ */
+
+body {
+ color: #333;
+ background-color: white;
+ font-family: Verdana, Helvetica, Arial, sans-serif;
+}
+
+div {
+ font-family: Verdana, Helvetica, Arial, sans-serif;
+}
+
+h2 {
+ font-size: 1.2em;
+ font-family: "Trebuchet MS", Arial, Helvetica, Tahoma, Verdana, sans-serif;
+ margin-top: 0;
+ margin-bottom: 0;
+ color: #555;
+}
+
+h3 {
+ font-size: 1em;
+ margin-top: 1.5em;
+ font-family: "Trebuchet MS", Arial, Helvetica, Tahoma, Verdana, sans-serif;
+ margin-top: 0;
+ margin-bottom: 0;
+ color: #555;
+}
+
+
+h4 {
+ font-family: "Trebuchet MS", Arial, Helvetica, Tahoma, Verdana, sans-serif;
+ margin-top: 0;
+ margin-bottom: 0;
+ color: #555;
+ margin-top: 1.5em
+}
+
+
+
+sup {
+ font-size: 9px
+}
+
+small small {
+ font-family: Verdana, Helvetica, Arial, sans-serif;
+ font-weight: bold;
+}
+
+
+
+/***** info popups *****/
+a.info {
+ position:relative;
+ color:black;
+ border-bottom:1px dashed #ccc;
+}
+/* Added to solve the z-order problem of IE
+*/
+a.info:hover {
+ background-color: #FFA178;
+ z-index:2;
+}
+/* End */
+a.info span{
+ display: none;
+ background-color: #FFA178;
+}
+a.info:hover span{
+ display:block;
+ position:absolute;
+ z-index:1;
+ top:2em;
+ left:-10em;
+ width:25em;
+ border:1px solid #F2AF1D;
+ background-color:#FDF1D5;
+ color:#000;
+ text-align:justify;
+ font-size:10px;
+ font-weight:normal;
+ padding:3px;
+ line-height:15px;
+}
+
diff --git a/fs_selfservice/fri/theme/header.css b/fs_selfservice/fri/theme/header.css
new file mode 100644
index 0000000..1c28e7a
--- /dev/null
+++ b/fs_selfservice/fri/theme/header.css
@@ -0,0 +1,83 @@
+/*
+ * Header
+ */
+
+/* Header */
+
+#ariHeader {
+ position: relative;
+ background: #105D90;
+ height: 72px;
+ margin: 0;
+ padding: 0;
+ clear: both;
+}
+
+#ariHeader span.left {
+ position: relative;
+ height: 72px;
+ border: 0px;
+ padding: 0px;
+ margin: 0px;
+ float: left;
+}
+
+#ariHeader img {
+ border: 0px;
+}
+
+#ariHeader span.right {
+ height: 72px;
+ border: 0px;
+ padding: 0px;
+ margin: 0px;
+ float: right;
+}
+
+#ariHeader img {
+ border: 0px;
+}
+
+/* Topnav */
+
+#topnav {
+ width: 100%;
+ height: 36px;
+ border: 0;
+ padding: 0;
+ margin-top: -1px; /* stupid browser hack */
+ color: #999;
+ background-color: #333;
+}
+
+#topnav span.left {
+ float: left;
+ text-align: left;
+ font-weight: bold;
+ color: #fff;
+ width: 49%;
+}
+
+#topnav span.right {
+ float: right;
+ text-align: right;
+ font-weight: bold;
+ color: #fff;
+ width: 49%;
+}
+
+.topnav small b {
+ font-family: Verdana, Helvetica, Arial, sans-serif;
+ font-weight: bold;
+ background-color: #105D90;
+}
+
+/* Headerspacer */
+
+#headerspacer {
+ border: 0;
+ padding: 0;
+ margin-top: -16px; /* stupid browser hack */
+ background-color: #fff;
+ height: 16px
+} \ No newline at end of file
diff --git a/fs_selfservice/fri/theme/iefixes.css b/fs_selfservice/fri/theme/iefixes.css
new file mode 100644
index 0000000..a7939a4
--- /dev/null
+++ b/fs_selfservice/fri/theme/iefixes.css
@@ -0,0 +1,16 @@
+/*
+ * IE Fixes
+ */
+
+/*Win IE fix \*/
+* html .minwidth { border-left: 760px solid #fff; position: relative; float: left; z-index: 1; }
+
+/*End Win IE fix*/
+
+/*Win IE fix \*/
+* html .container { margin-left: -760px; position: relative; float: left; z-index :2; }
+/*End Win IE fix*/
+
+
+
+
diff --git a/fs_selfservice/fri/theme/images/arrow-asc.gif b/fs_selfservice/fri/theme/images/arrow-asc.gif
new file mode 100644
index 0000000..46a5848
--- /dev/null
+++ b/fs_selfservice/fri/theme/images/arrow-asc.gif
Binary files differ
diff --git a/fs_selfservice/fri/theme/images/arrow-desc.gif b/fs_selfservice/fri/theme/images/arrow-desc.gif
new file mode 100644
index 0000000..6f4e5e6
--- /dev/null
+++ b/fs_selfservice/fri/theme/images/arrow-desc.gif
Binary files differ
diff --git a/fs_selfservice/fri/theme/layout.css b/fs_selfservice/fri/theme/layout.css
new file mode 100644
index 0000000..a398714
--- /dev/null
+++ b/fs_selfservice/fri/theme/layout.css
@@ -0,0 +1,420 @@
+/*
+ * Layout
+ */
+
+/* Page */
+
+#page {
+ background-color: white;
+ text-align: left;
+ min-width: 760px;
+}
+
+/* main */
+
+#main {
+ min-width: 760px;
+ float: left;
+}
+
+#main span.left {
+ float: left;
+}
+
+#main span.right {
+ float: left;
+}
+
+/* Center */
+
+#center {
+ float: left;
+ margin-bottom: 20px;
+}
+
+/* Login */
+
+#login {
+ margin: 0;
+ padding: 0;
+}
+#login p {
+ font-size: 0.7em;
+}
+table#login {
+ width: 600px;
+ border: 0px;
+}
+table#login td.right {
+ text-align: right;
+ width: 20%;
+}
+table#login td.left {
+ text-align: left;
+}
+table#login td.small {
+ font-size: 0.7em;
+}
+table#login_text {
+ margin-left: 60px;
+ font-size: 0.8em;
+ text-align: left;
+}
+
+/* i18n lang */
+
+.lang {
+ display: inline;
+ font-size: 0.8em;
+ margin: 0;
+ padding: 0;
+}
+.lang_code {
+ margin: 0;
+ padding: 0;
+ width: 10em;
+}
+
+/* Line */
+
+#line {
+ min-width: 604px;
+ border: 1px solid #333;
+ padding: 0;
+ margin: 0;
+ color: #999;
+ background-color: #333;
+ height: 1px;
+}
+#line span.left {
+ float: left;
+ text-align: left;
+ font-weight: bold;
+ color: #fff;
+ width: 49%;
+}
+#line span.right {
+ float: right;
+ text-align: right;
+ font-weight: bold;
+ color: #fff;
+ width: 49%;
+}
+
+/* Navbar */
+
+#navbar {
+ width: 604px;
+ height: 24px;
+ border: 1px;
+ padding: 0;
+ margin-bottom: 0;
+ color: #fff;
+ background-color: #333;
+}
+#navbar span.left {
+ margin: 2px;
+ float: left;
+ text-align: left;
+ font-weight: bold;
+ vertical-align: middle;
+ width: 49%;
+}
+#navbar span.right {
+ margin: 2px;
+ float: right;
+ text-align: right;
+ font-weight: bold;
+ vertical-align: middle;
+ width: 49%;
+}
+
+/* Info Bar */
+
+#info_bar {
+ min-width: 604px;
+ border: 1px solid #333;
+ padding: 3px;
+ margin-top: -1px; /* stupid browser hack */
+ color: #999;
+ background-color: #333;
+ height: 20px;
+}
+#info_bar span.left {
+ float: left;
+ text-align: left;
+ font-weight: bold;
+ color: #fff;
+ width: 49%;
+}
+#info_bar span.right {
+ float: right;
+ text-align: right;
+ font-weight: bold;
+ color: #fff;
+ width: 49%;
+}
+.info_bar a:link {
+ color: white;
+ text-decoration: none;
+}
+.info_bar a:active, a:link {
+ color: #105D90;
+}
+.info_bar a:hover {
+ color: #fc0;
+}
+.info_bar small b {
+ font-family: Verdana, Helvetica, Arial, sans-serif;
+ font-weight: bold;
+}
+input.infoBar {
+ font-size: 11px;
+ padding: 0px;
+ height: 22px;
+}
+
+/* bars */
+
+.bar {
+ margin: 0;
+}
+
+.bar_left {
+ width: 604px;
+ margin: 0 0 16px 0;
+ padding: 0;
+}
+
+.bar_center {
+ width: 604px;
+ text-align: center;
+ margin: 0 0 16px 0;
+ padding: 0;
+}
+.bar_center a:active, .bar_center a:hover {
+ color: red;
+}
+
+/* Subheader */
+
+#subheader {
+ padding: 0px;
+ margin: 0px;
+ margin-bottom: 16px;
+}
+
+/* servBodL */
+
+.servBodL {
+ border-left: 1px dotted #CEDCEA;
+}
+
+/* Callmonitor */
+
+table.callmonitor {
+ border: 1px #6699CC solid;
+ border-collapse: collapse;
+ border-spacing: 0px;
+ margin: 0 0 16px 0;
+ width: 604px;
+}
+table.callmonitor th {
+ background-color: #BEC8D1;
+ border: 1px solid #6699CC;
+ border-bottom: 2px solid #6699CC;
+ text-align: center;
+ font-family: Verdana;
+ font-weight: bold;
+ font-size: 0.7em;
+ color: #404040;
+}
+table.callmonitor th a {
+ color: #404040;
+}
+table.callmonitor img {
+ border: 0;
+}
+table.callmonitor td {
+ background-color: white;
+ border: 1px solid #6699CC;
+ color: #404040;
+ font-family: Verdana, sans-serif, Arial;
+ font-weight: normal;
+ font-size: 0.7em;
+ padding: 3px;
+ text-align: center;
+}
+table.callmonitor td.checkbox {
+ padding: 1px;
+}
+
+/* Voicemail */
+
+.voicemail {
+ margin: 0px;
+}
+table.voicemail {
+ border: 1px #6699CC solid;
+ border-collapse: collapse;
+ border-spacing: 0px;
+ margin: 0 0 16px 0;
+ width: 604px;
+}
+table.voicemail th {
+ background-color: #BEC8D1;
+ border: 1px solid #6699CC;
+ border-bottom: 2px solid #6699CC;
+ text-align: center;
+ font-family: Verdana;
+ font-weight: bold;
+ font-size: 0.7em;
+ color: #404040;
+}
+table.voicemail th a {
+ color: #404040;
+}
+table.voicemail img {
+ border: 0;
+}
+table.voicemail td {
+ background-color: white;
+ border: 1px solid #6699CC;
+ color: #404040;
+ font-family: Verdana, sans-serif, Arial;
+ font-weight: normal;
+ font-size: 0.7em;
+ padding: 3px;
+ text-align: center;
+}
+table.voicemail td.checkbox {
+ padding: 1px;
+}
+
+/* Help */
+
+.help {
+ margin: 0px;
+}
+table.help {
+ border: 1px #6699CC solid;
+ border-collapse: collapse;
+ border-spacing: 0px;
+ margin: 0 0 16px 0;
+}
+table.help th {
+ background-color: #BEC8D1;
+ border: 1px solid #6699CC;
+ border-bottom: 2px solid #6699CC;
+ font-family: Verdana;
+ font-weight: bold;
+ font-size: 0.7em;
+ color: #404040;
+}
+table.help th.feature_codes {
+ text-align: center;
+ width: 9em;
+}
+table.help th a {
+ color: #404040;
+}
+table.help img {
+ border: 0;
+}
+table.help td {
+ background-color: white;
+ border: 1px solid #6699CC;
+ color: #404040;
+ font-family: Verdana, sans-serif, Arial;
+ font-weight: normal;
+ font-size: 0.7em;
+ padding: 3px;
+}
+table.help td.feature_codes {
+ text-align: center;
+}
+table.help td.checkbox {
+ padding: 1px;
+}
+
+/* Settings */
+
+.settings {
+ font-family: Verdana, sans-serif, Arial;
+ font-weight: normal;
+ font-size: 0.9em;
+ padding: 0;
+ margin: 0;
+}
+table.settings {
+ font-family: Verdana;
+ color: #404040;
+ border-collapse: collapse;
+ border-spacing: 0px;
+ padding-bottom: 3px;
+}
+table.settings td {
+ color: #404040;
+ background-color: white;
+ padding: 3px;
+}
+table.settings td.note {
+ color: #105D90;
+}
+
+/* Footer */
+
+#ariFooter {
+ color: #999;
+ margin-left: 148px;
+ font-size: 10px;
+ overflow: auto;
+/* width: 100%; */
+ clear: both;
+}
+
+#ariFooter a {
+ text-decoration: none;
+ color: #999;
+}
+
+#ariFooter a:hover {
+ text-decoration: underline;
+ color: #105D90;
+}
+
+#ariFooter a:link {
+ text-decoration: none;
+ color: #999;
+}
+
+/* Misc */
+
+.ariClearBoth {
+ clear: both;
+ margin: 0;
+ padding: 0;
+}
+
+.ariBlockHide {
+ display: none;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ position: absolute; /* IE5 Mac */
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fs_selfservice/fri/theme/logo.gif b/fs_selfservice/fri/theme/logo.gif
new file mode 100644
index 0000000..b2d23d7
--- /dev/null
+++ b/fs_selfservice/fri/theme/logo.gif
Binary files differ
diff --git a/fs_selfservice/fri/theme/main.css b/fs_selfservice/fri/theme/main.css
new file mode 100644
index 0000000..6b9ba94
--- /dev/null
+++ b/fs_selfservice/fri/theme/main.css
@@ -0,0 +1,13 @@
+/*
+ * Main
+ */
+
+@import url("global.css");
+@import url("text.css");
+@import url("layout.css");
+@import url("header.css");
+@import url("navigation.css");
+
+@import url("iefixes.css");
+
+
diff --git a/fs_selfservice/fri/theme/navigation.css b/fs_selfservice/fri/theme/navigation.css
new file mode 100644
index 0000000..907851b
--- /dev/null
+++ b/fs_selfservice/fri/theme/navigation.css
@@ -0,0 +1,166 @@
+/*
+ * Navigation
+ */
+
+/* Menu */
+
+#menu {
+ width: 148px;
+ float: left;
+}
+
+/* Nav */
+
+.nav {
+ font-weight: bold;
+ color: #105D90;
+ margin-right: 20px;
+}
+
+.nav p {
+ margin: 0px;
+ padding-top: 2px;
+ padding-bottom: 3px;
+ background: #FFF;
+}
+
+.nav a:visited {
+ color: #105D90;
+}
+
+.sub {
+ margin-left: 1em;
+}
+
+.navtext {
+ margin-left: 20px;
+}
+
+.nav_b1 {
+ height: 1px;
+ font-size: 1px;
+ overflow: hidden;
+ display: block;
+ background: #EEE;
+ margin:0 5px;
+}
+
+.nav_b2 {
+ height: 1px;
+ font-size: 1px;
+ overflow: hidden;
+ display: block;
+ background: #FFF;
+ border-right: 2px solid #EEE;
+ border-left: 2px solid #EEE;
+ margin:0 3px;
+}
+
+.nav_b3 {
+ height: 1px;
+ font-size: 1px;
+ overflow: hidden;
+ display:block;
+ background: #FFF;
+ border-right: 1px solid #EEE;
+ border-left: 1px solid #EEE;
+ margin: 0 2px;
+}
+
+.nav_b4 {
+ height: 2px;
+ font-size: 1px;
+ overflow: hidden;
+ display:block;
+ background: #FFF;
+ border-right: 1px solid #EEE;
+ border-left:1px solid #EEE;
+ margin:0 1px;
+}
+
+#nav_menu {
+ background: #FFF;
+ border-right: 1px solid #EEE;
+ border-left: 1px solid #EEE;
+ padding-left: 0.75em;
+}
+
+/* Subnav */
+
+.subnav {
+ font-weight: bold;
+ color: #105D90;
+ margin-right: 20px;
+}
+
+.subnav p {
+ margin: 0px;
+ padding-top: 2px;
+ padding-bottom: 3px;
+ background: #BEC8D1;
+}
+
+.subnav a:visited {
+ color: #105D90;
+}
+
+.subnav a.current, a:visited.current {
+ color: #404040;
+}
+
+.subnav_b1 {
+ height: 1px;
+ font-size: 1px;
+ overflow: hidden;
+ display: block;
+ background: #aaa;
+ margin:0 5px;
+}
+
+.subnav_b2 {
+ height: 1px;
+ font-size: 1px;
+ overflow: hidden;
+ display: block;
+ background: #BEC8D1;
+ border-right: 2px solid #aaa;
+ border-left: 2px solid #aaa;
+ margin:0 3px;
+}
+
+.subnav_b3 {
+ height: 1px;
+ font-size: 1px;
+ overflow: hidden;
+ display:block;
+ background: #BEC8D1;
+ border-right: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ margin: 0 2px;
+}
+
+.subnav_b4 {
+ height: 2px;
+ font-size: 1px;
+ overflow: hidden;
+ display:block;
+ background: #BEC8D1;
+ border-right: 1px solid #aaa;
+ border-left:1px solid #aaa;
+ margin:0 1px;
+}
+
+.subnav_title {
+ font-weight: normal;
+ color: #105D90;
+ font-size: 12px;
+ padding-left: 1em;
+}
+
+#subnav_menu {
+ background: #BEC8D1;
+ border-right: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ padding-left: 1.25em;
+}
+
diff --git a/fs_selfservice/fri/theme/page.tpl.php b/fs_selfservice/fri/theme/page.tpl.php
new file mode 100644
index 0000000..9d54659
--- /dev/null
+++ b/fs_selfservice/fri/theme/page.tpl.php
@@ -0,0 +1,78 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <TITLE>User Portal</TITLE>
+ <link rel="stylesheet" href="theme/main.css" type="text/css">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ </head>
+ <body>
+ <div id="page">
+ <div class="minwidth">
+ <div class="container">
+ <div id="topnav">
+ <div class="spacer"></div>
+ <span class="left">
+ </span>
+ <div class="spacer"></div>
+ </div>
+ <div id="headerspacer"><img src="theme/spacer.gif" alt=""></div>
+ <div id="main">
+ <div class="minwidth">
+ <div class="container">
+ <div class="spacer"></div>
+ <span class="left">
+ <div id="menu">
+ <div><img height=4 src="theme/spacer.gif" alt=""></div>
+ <div class="nav">
+ <?php if ($nav_menu != '') { ?>
+ <b class='nav_b1'></b><b class='nav_b2'></b><b class='nav_b3'></b><b class='nav_b4'></b>
+ <div id='nav_menu'>
+ <?php print($nav_menu) ?>
+ </div>
+ <b class='nav_b4'></b><b class='nav_b3'></b><b class='nav_b2'></b><b class='nav_b1'></b>
+ <?php } ?>
+ </div>
+ <div><img height=14 src="theme/spacer.gif" alt=""></div>
+ <?php if ($subnav_menu != '') { ?>
+ <div class="subnav">
+ <div class="subnav_title"><?php echo _("Folders")?>:</div>
+ <b class='subnav_b1'></b><b class='subnav_b2'></b><b class='subnav_b3'></b><b class='subnav_b4'></b>
+ <div id='subnav_menu'>
+ <?php print($subnav_menu) ?>
+ </div>
+ <b class='subnav_b4'></b><b class='subnav_b3'></b><b class='subnav_b2'></b><b class='subnav_b1'></b>
+ </div>
+ <?php } ?>
+ </div>
+ </span>
+ <span class="right">
+ <div id="center">
+ <?php if ($login != "") { ?>
+ <?php print($login) ?>
+ <?php } ?>
+ <div id="content">
+ <!-- begin main content -->
+ <?php print($content) ?>
+ <!-- end main content -->
+ </div>
+ </div>
+ </span>
+ <div class="spacer"></div>
+ </div>
+ </div>
+ </div>
+ <!--begin footer-->
+ <div id="ariFooter">
+ <small>
+ <!--&nbsp;&nbsp;<?php print($ari_version) ?> <?php echo _("Version")?><br> -->
+ Freeside Recording Interface (c) 2008 Freeside Internet Services, Inc.<br>
+ <a href="http<?php print(isset($_SERVER['HTTPS'])&&$_SERVER['HTTPS']!=''?'s':''); ?>://www.littlejohnconsulting.com">Based on ARI from Littlejohn Consulting</a>
+ </small>
+ </div>
+ <!-- end footer -->
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/fs_selfservice/fri/theme/spacer.gif b/fs_selfservice/fri/theme/spacer.gif
new file mode 100644
index 0000000..8f09684
--- /dev/null
+++ b/fs_selfservice/fri/theme/spacer.gif
Binary files differ
diff --git a/fs_selfservice/fri/theme/text.css b/fs_selfservice/fri/theme/text.css
new file mode 100644
index 0000000..9625ca0
--- /dev/null
+++ b/fs_selfservice/fri/theme/text.css
@@ -0,0 +1,10 @@
+/*
+ * Text
+ */
+
+/* Error */
+
+.error {
+ color: #CC3333;
+}
+
diff --git a/fs_selfservice/fri/version.php b/fs_selfservice/fri/version.php
new file mode 100644
index 0000000..7f313a1
--- /dev/null
+++ b/fs_selfservice/fri/version.php
@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * @file
+ * version
+ */
+
+$ARI_VERSION = 'FreePBX 2.3';
+
+?>
diff --git a/fs_selfservice/fs_passwd_test b/fs_selfservice/fs_passwd_test
new file mode 100755
index 0000000..4f8b8a8
--- /dev/null
+++ b/fs_selfservice/fs_passwd_test
@@ -0,0 +1,19 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::SelfService qw(passwd);
+
+my $rv = passwd(
+ 'username' => 'ivan',
+ 'old_password' => 'heyhoo',
+ 'new_password' => 'haloo',
+);
+my $error = $rv->{error};
+
+if ( $error eq 'Incorrect password.' ) {
+ exit;
+} else {
+ die $error if $error;
+ die "no error";
+}
+
diff --git a/fs_selfservice/java/biz/freeside/SelfService.java b/fs_selfservice/java/biz/freeside/SelfService.java
new file mode 100755
index 0000000..752815a
--- /dev/null
+++ b/fs_selfservice/java/biz/freeside/SelfService.java
@@ -0,0 +1,52 @@
+package biz.freeside;
+
+// see http://ws.apache.org/xmlrpc/client.html for these classes
+import org.apache.xmlrpc.XmlRpcException;
+import org.apache.xmlrpc.client.XmlRpcClient;
+import org.apache.xmlrpc.client.XmlRpcClientConfig;
+import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.net.URL;
+
+public class SelfService extends XmlRpcClient {
+
+ public SelfService( String url ) throws Exception {
+ super();
+ XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
+ config.setServerURL(new URL( url ));
+ this.setConfig(config);
+ }
+
+ private String canonicalMethod ( String method ) {
+ String canonical = new String(method);
+ if (!canonical.startsWith( "FS.SelfService.XMLRPC." )) {
+ canonical = "FS.SelfService.XMLRPC." + canonical;
+ }
+ return canonical;
+ }
+
+ private HashMap testResponse ( Object toTest ) throws XmlRpcException {
+ if (! ( toTest instanceof HashMap )) {
+ throw new XmlRpcException("expected HashMap but got" + toTest.getClass());
+ }
+ return (HashMap) toTest;
+ }
+
+ public HashMap execute( String method, List params ) throws XmlRpcException {
+ return testResponse(super.execute( canonicalMethod(method), params ));
+ }
+
+ public HashMap execute( String method, Object[] params ) throws XmlRpcException {
+ return testResponse(super.execute( canonicalMethod(method), params ));
+ }
+
+ public HashMap execute( XmlRpcClientConfig config, String method, List params ) throws XmlRpcException {
+ return testResponse(super.execute( config, canonicalMethod(method), params ));
+ }
+
+ public HashMap execute( XmlRpcClientConfig config, String method, Object[] params ) throws XmlRpcException {
+ return testResponse(super.execute( config, canonicalMethod(method), params ));
+ }
+}
diff --git a/fs_selfservice/java/freeside_login_example.java b/fs_selfservice/java/freeside_login_example.java
new file mode 100755
index 0000000..cb6d2bc
--- /dev/null
+++ b/fs_selfservice/java/freeside_login_example.java
@@ -0,0 +1,45 @@
+
+import biz.freeside.SelfService;
+import org.apache.commons.logging.impl.SimpleLog; //included in apache xmlrpc
+import java.util.HashMap;
+import java.util.Vector;
+
+public class freeside_login_example {
+ private static SimpleLog logger = new SimpleLog("SelfService");
+
+ public static void main( String args[] ) throws Exception {
+ SelfService client =
+ new SelfService( "http://192.168.1.221:8081/xmlrpc.cgi" );
+
+ Vector params = new Vector();
+ params.addElement( "username" );
+ params.addElement( "testuser" );
+ params.addElement( "domain" );
+ params.addElement( "example.com" );
+ params.addElement( "password" );
+ params.addElement( "testpass" );
+ HashMap result = client.execute( "login", params );
+
+ String error = (String) result.get("error");
+
+ if (error.length() < 1) {
+
+ // successful login
+
+ String sessionId = (String) result.get("session_id");
+
+ logger.trace("[login] logged into freeside with session_id="+sessionId);
+
+ // store session id in your session store to be used for other calls
+
+ }else{
+
+ // successful login
+
+ logger.warn("[login] error logging into freeside: "+error);
+
+ // display error message to user
+
+ }
+ }
+}
diff --git a/fs_selfservice/java/freeside_signup_example.java b/fs_selfservice/java/freeside_signup_example.java
new file mode 100755
index 0000000..6c695c4
--- /dev/null
+++ b/fs_selfservice/java/freeside_signup_example.java
@@ -0,0 +1,69 @@
+
+import biz.freeside.SelfService;
+import org.apache.commons.logging.impl.SimpleLog; // included in apache xmlrpc
+import java.util.HashMap;
+import java.util.Vector;
+
+public class freeside_signup_example {
+ private static SimpleLog logger = new SimpleLog("SelfService");
+
+ public static void main( String args[] ) throws Exception {
+ SelfService client =
+ new SelfService( "http://192.168.1.221:8081/xmlrpc.cgi" );
+
+ Vector params = new Vector();
+ params.addElement( "first" );
+ params.addElement( "Test" );
+ params.addElement( "last" );
+ params.addElement( "User" );
+ params.addElement( "address1");
+ params.addElement( "123 Test Street" );
+ params.addElement( "address2");
+ params.addElement( "Suite A" );
+ params.addElement( "city");
+ params.addElement( "Testville" );
+ params.addElement( "state");
+ params.addElement( "OH" );
+ params.addElement( "zip");
+ params.addElement( "44632" );
+ params.addElement( "country");
+ params.addElement( "US" );
+ params.addElement( "daytime" );
+ params.addElement( "216-412-1234" );
+ params.addElement( "fax" );
+ params.addElement( "216-412-1235" );
+ params.addElement( "payby" );
+ params.addElement( "BILL" );
+ params.addElement( "invoicing_list" );
+ params.addElement( "test@test.example.com" );
+ params.addElement( "pkgpart" );
+ params.addElement( "101" );
+ params.addElement( "popnum" );
+ params.addElement( "4018" );
+ params.addElement( "username" );
+ params.addElement( "testy" );
+ params.addElement( "_password" );
+ params.addElement( "tester" );
+ HashMap result = client.execute( "new_customer", params );
+
+ String error = (String) result.get("error");
+
+ if (error.length() < 1) {
+
+ // successful signup
+
+ String custnum = (String) result.get("custnum");
+
+ logger.trace("[new_customer] signup with custnum "+custnum);
+
+ }else{
+
+ // unsuccessful signup
+
+ logger.warn("[new_customer] signup error: "+error);
+
+ // display error message to user
+
+ }
+ }
+}
diff --git a/fs_selfservice/php/freeside.class.php b/fs_selfservice/php/freeside.class.php
new file mode 100644
index 0000000..bb2ac98
--- /dev/null
+++ b/fs_selfservice/php/freeside.class.php
@@ -0,0 +1,34 @@
+<?php
+class FreesideSelfService {
+
+ //Change this to match the location of your selfservice xmlrpc.cgi or daemon
+ #var $URL = 'https://localhost/selfservice/xmlrpc.cgi';
+ var $URL = 'http://localhost/selfservice/xmlrpc.cgi';
+
+ function FreesideSelfService() {
+ $this;
+ }
+
+ public function __call($name, $arguments) {
+
+ error_log("[FreesideSelfService] $name called, sending to ". $this->URL);
+
+ $request = xmlrpc_encode_request("FS.SelfService.XMLRPC.$name", $arguments);
+ $context = stream_context_create( array( 'http' => array(
+ 'method' => "POST",
+ 'header' => "Content-Type: text/xml",
+ 'content' => $request
+ )));
+ $file = file_get_contents($this->URL, false, $context);
+ $response = xmlrpc_decode($file);
+ if (xmlrpc_is_fault($response)) {
+ trigger_error("[FreesideSelfService] XML-RPC communication error: $response[faultString] ($response[faultCode])");
+ } else {
+ //error_log("[FreesideSelfService] $response");
+ return $response;
+ }
+ }
+
+}
+
+?>
diff --git a/fs_selfservice/php/freeside.login_example.php b/fs_selfservice/php/freeside.login_example.php
new file mode 100644
index 0000000..69174a4
--- /dev/null
+++ b/fs_selfservice/php/freeside.login_example.php
@@ -0,0 +1,37 @@
+<?
+
+require('freeside.class.php');
+$freeside = new FreesideSelfService();
+
+$domain = 'example.com';
+
+$response = $freeside->login( array(
+ 'username' => strtolower($_POST['username']),
+ 'domain' => $domain,
+ 'password' => strtolower($_POST['password']),
+) );
+
+error_log("[login] received response from freeside: $response");
+$error = $response['error'];
+
+if ( ! $error ) {
+
+ // sucessful login
+
+ $session_id = $response['session_id'];
+
+ error_log("[login] logged into freeside with session_id=$session_id");
+
+ // store session id in your session store, to be used for other calls
+
+} else {
+
+ // unsucessful login
+
+ error_log("[login] error logging into freeside: $error");
+
+ // display error message to user
+
+}
+
+?>
diff --git a/fs_selfservice/php/freeside_signup_example.php b/fs_selfservice/php/freeside_signup_example.php
new file mode 100644
index 0000000..8b1dc19
--- /dev/null
+++ b/fs_selfservice/php/freeside_signup_example.php
@@ -0,0 +1,49 @@
+<?
+
+require('freeside.class.php');
+$freeside = new FreesideSelfService();
+
+$response = $freeside->new_customer( array(
+ 'agentnum' => 1,
+
+ 'first' => $_POST['first'],
+ 'last' => $_POST['last'],
+ 'address1' => $_POST['address1'],
+ 'address2' => $_POST['address2'],
+ 'city' => $_POST['city'],
+ 'state' => $_POST['state'],
+ 'zip' => $_POST['zip'],
+ 'country' => 'US',
+ 'daytime' => $_POST['daytime'],
+ 'fax' => $_POST['fax'],
+
+ 'payby' => 'BILL',
+ 'invoicing_list' => $_POST['email'],
+
+ 'pkgpart' => 2,
+ 'username' => strtolower($_POST['username']),
+ '_password' => strtolower($_POST['password'])
+) );
+
+error_log("[new_customer] received response from freeside: $response");
+$error = $response['error'];
+
+if ( ! $error ) {
+
+ // sucessful signup
+
+ $custnum = $response['custnum'];
+
+ error_log("[new_customer] signup up with custnum $custnum");
+
+} else {
+
+ // unsucessful signup
+
+ error_log("[new_customer] signup error:: $error");
+
+ // display error message to user
+
+}
+
+?>
diff --git a/fs_selfservice/php/login.php b/fs_selfservice/php/login.php
new file mode 100644
index 0000000..d960914
--- /dev/null
+++ b/fs_selfservice/php/login.php
@@ -0,0 +1,90 @@
+<?php
+
+require('freeside.class.php');
+$freeside = new FreesideSelfService();
+
+$login_info = $freeside->login_info();
+
+extract($login_info);
+
+$error = $_GET['error'];
+if ( $error ) {
+ $username = $_GET['username'];
+ $domain = $_GET['domain'];
+}
+
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD><TITLE>Login</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=5>Login</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><?php echo htmlspecialchars($error); ?></FONT>
+
+<FORM ACTION="process_login.php" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="login">
+
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+
+<TR>
+ <TH ALIGN="right">Username </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<?php echo htmlspecialchars($username); ?>"><?php if ( $single_domain ) { echo '@'.$single_domain; } ?>
+ </TD>
+</TR>
+
+<?php if ( $single_domain ) { ?>
+
+ <INPUT TYPE="hidden" NAME="domain" VALUE="<?php echo $single_domain ?>">
+
+<?php } else { ?>
+
+ <TR>
+ <TH ALIGN="right">Domain </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="domain" VALUE="<?php echo htmlspecialchars($domain); ?>">
+ </TD>
+ </TR>
+
+<?php } ?>
+
+<TR>
+ <TH ALIGN="right">Password </TH>
+ <TD>
+ <INPUT TYPE="password" NAME="password">
+ </TD>
+</TR>
+<TR>
+ <TD COLSPAN=2 ALIGN="center"><INPUT TYPE="submit" VALUE="Login"></TD>
+</TR>
+</TABLE>
+</FORM>
+
+<?php if ( $phone_login ) { ?>
+
+ <B>OR</B><BR><BR>
+
+ <FORM ACTION="process_login.php" METHOD=POST>
+ <INPUT TYPE="hidden" NAME="session" VALUE="login">
+ <TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+ <TR>
+ <TH ALIGN="right">Phone number </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<?php echo htmlspecialchars($username) ?>">
+ </TD>
+ </TR>
+ <INPUT TYPE="hidden" NAME="domain" VALUE="svc_phone">
+ <TR>
+ <TH ALIGN="right">PIN </TH>
+ <TD>
+ <INPUT TYPE="password" NAME="password">
+ </TD>
+ </TR>
+ <TR>
+ <TD COLSPAN=2 ALIGN="center"><INPUT TYPE="submit" VALUE="Login"></TD>
+ </TR>
+ </TABLE>
+ </FORM>
+
+<?php } ?>
+
+</BODY></HTML>
+
diff --git a/fs_selfservice/php/main.php b/fs_selfservice/php/main.php
new file mode 100644
index 0000000..b34a477
--- /dev/null
+++ b/fs_selfservice/php/main.php
@@ -0,0 +1,39 @@
+<?php
+
+require('freeside.class.php');
+$freeside = new FreesideSelfService();
+
+$session_id = $_GET['session_id'];
+
+$response = $freeside->customer_info( array(
+ 'session_id' => $session_id,
+) );
+
+$error = $response['error'];
+
+if ( $error ) {
+ header('Location:login.php?error='. urlencode($error));
+ die();
+}
+
+extract($response);
+
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+ <HEAD>
+ <TITLE>My Account</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>My Account</H1>
+
+ Hello, <?php echo htmlspecialchars($name); ?><BR><BR>
+
+ <?php echo $small_custview; ?>
+
+ <BR>
+
+ <A HREF="order_renew.php?session_id=<?php echo $session_id; ?>">Renew early</A>
+
+ </BODY>
+</HTML>
diff --git a/fs_selfservice/php/order_renew.php b/fs_selfservice/php/order_renew.php
new file mode 100644
index 0000000..e74ba40
--- /dev/null
+++ b/fs_selfservice/php/order_renew.php
@@ -0,0 +1,166 @@
+<?php
+
+require('freeside.class.php');
+$freeside = new FreesideSelfService();
+
+$session_id = $_GET['session_id'];
+
+$renew_info = $freeside->renew_info( array(
+ 'session_id' => $session_id,
+) );
+
+$error = $renew_info['error'];
+
+if ( $error ) {
+ header('Location:login.php?error='. urlencode($error));
+ die();
+}
+
+#in the simple case, just deal with the first package
+$bill_date = $renew_info['dates'][0]['bill_date'];
+$bill_date_pretty = $renew_info['dates'][0]['bill_date_pretty'];
+$renew_date = $renew_info['dates'][0]['renew_date'];
+$renew_date_pretty = $renew_info['dates'][0]['renew_date_pretty'];
+$amount = $renew_info['dates'][0]['amount'];
+
+$payment_info = $freeside->payment_info( array(
+ 'session_id' => $session_id,
+) );
+
+extract($payment_info);
+
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Renew Early</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>Renew Early</H1>
+
+ <FONT SIZE="+1" COLOR="#ff0000"><?php echo htmlspecialchars($_GET['error']); ?></FONT>
+
+ <FORM NAME="OneTrueForm" METHOD="POST" ACTION="process_payment_order_renew.php" onSubmit="document.OneTrueForm.process.disabled=true">
+
+ <INPUT TYPE="hidden" NAME="date" VALUE="<?php echo $date; ?>">
+ <INPUT TYPE="hidden" NAME="session_id" VALUE="<?php echo $session_id; ?>">
+ <INPUT TYPE="hidden" NAME="amount" VALUE="<?php echo $amount; ?>">
+
+ A payment of $<?php echo $amount; ?> will renew your account through <?php echo $renew_date_pretty; ?>.<BR><BR>
+
+ <TABLE BGCOLOR="#cccccc">
+ <TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<?php echo $amount; ?>
+ </TD></TR></TABLE>
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Card&nbsp;type</TD>
+ <TD>
+ <SELECT NAME="card_type"><OPTION></OPTION>
+ <?php foreach ( array_keys($card_types) as $t ) { ?>
+ <OPTION <?php if ($card_type == $card_types[$t] ) { ?> SELECTED <?php } ?>
+ VALUE="<?php echo $card_types[$t]; ?>"
+ ><?php echo $t; ?>
+ <?php } ?>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Card&nbsp;number</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="payinfo" SIZE=20 MAXLENGTH=19 VALUE="<?php echo $payinfo; ?>"> </TD>
+ <TD>Exp.</TD>
+ <TD>
+ <SELECT NAME="month">
+ <?php foreach ( array('01','02','03','04','05','06','07','08','09','10','11','12') as $m) { ?>
+ <OPTION<?php if ($m == $month ) { ?> SELECTED<?php } ?>
+ ><?php echo $m; ?>
+ <?php } ?>
+ </SELECT>
+ </TD>
+ <TD> / </TD>
+ <TD>
+ <SELECT NAME="year">
+ <?php $lt = localtime(); $y = $lt[5] + 1900;
+ for ($y = $lt[5]+1900; $y < $lt[5] + 1910; $y++ ) { ?>
+ <OPTION<?php if ($y == $year ) { ?> SELECTED<?php } ?>
+ ><?php echo $y; ?>
+ <?php } ?>
+ </SELECT>
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+ </TR>
+ <?php if ( $withcvv ) { ?>
+ <TR>
+ <TD ALIGN="right">CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)</TD>
+ <TD><INPUT TYPE="text" NAME="paycvv" VALUE="" SIZE=4 MAXLENGTH=4></TD>
+ </TR>
+ <?php } ?>
+ <TR>
+ <TD ALIGN="right">Exact&nbsp;name&nbsp;on&nbsp;card</TD>
+ <TD><INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="payname" VALUE="<?php echo $payname; ?>"></TD>
+ </TR><TR>
+ <TD ALIGN="right">Card&nbsp;billing&nbsp;address</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address1" VALUE="<?php echo $address1; ?>">
+ </TD>
+ </TR><TR>
+ <TD ALIGN="right">Address&nbsp;line&nbsp;2</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address2" VALUE="<?php echo $address2; ?>">
+ </TD>
+ </TR><TR>
+ <TD ALIGN="right">City</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="city" SIZE="12" MAXLENGTH=80 VALUE="<?php echo $city; ?>">
+ </TD>
+ <TD>State</TD>
+ <TD>
+ <SELECT NAME="state">
+ <?php foreach ( $states as $s ) { ?>
+ <OPTION<?php if ($s == $state) { ?> SELECTED<?php } ?>
+ ><?php echo $s; ?>
+ <?php } ?>
+ </SELECT>
+ </TD>
+ <TD>Zip</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="zip" SIZE=11 MAXLENGTH=10 VALUE="<?php echo $zip; ?>">
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox" CHECKED NAME="save" VALUE="1">
+ Remember this information
+ </TD>
+ </TR><TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox"<?php if ( $payby == 'CARD' ) { ?> CHECKED<?php } ?> NAME="auto" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.save.checked=true; }">
+ Charge future payments to this card automatically
+ </TD>
+ </TR>
+ </TABLE>
+ <BR>
+ <INPUT TYPE="hidden" NAME="paybatch" VALUE="<?php echo $paybatch; ?>">
+ <INPUT TYPE="submit" NAME="process" VALUE="Process payment"> <!-- onClick="this.disabled=true"> -->
+ </FORM>
+
+ </BODY>
+</HTML>
diff --git a/fs_selfservice/php/process_login.php b/fs_selfservice/php/process_login.php
new file mode 100644
index 0000000..1f4fd9a
--- /dev/null
+++ b/fs_selfservice/php/process_login.php
@@ -0,0 +1,38 @@
+<?php
+
+require('freeside.class.php');
+$freeside = new FreesideSelfService();
+
+$response = $freeside->login( array(
+ 'username' => strtolower($_POST['username']),
+ 'domain' => strtolower($_POST['domain']),
+ 'password' => strtolower($_POST['password']),
+) );
+
+#error_log("[login] received response from freeside: $response");
+
+$error = $response['error'];
+
+if ( $error ) {
+
+ header('Location:login.php?username='. urlencode($username).
+ '&domain='. urlencode($domain).
+ '&error='. urlencode($error)
+ );
+ die();
+
+}
+
+// sucessful login
+
+$session_id = $response['session_id'];
+
+#error_log("[login] logged into freeside with session_id=$session_id");
+
+// now what? for now, always redirect to the main page.
+// eventually, other options?
+
+header("Location:main.php?session_id=$session_id")
+#die();
+
+?>
diff --git a/fs_selfservice/php/process_payment_order_renew.php b/fs_selfservice/php/process_payment_order_renew.php
new file mode 100644
index 0000000..2059462
--- /dev/null
+++ b/fs_selfservice/php/process_payment_order_renew.php
@@ -0,0 +1,74 @@
+<?php
+
+require('freeside.class.php');
+$freeside = new FreesideSelfService();
+
+$response = $freeside->process_payment_order_renew( array(
+ 'session_id' => $_POST['session_id'],
+ 'payby' => 'CARD',
+ 'amount' => $_POST['amount'],
+ 'payinfo' => $_POST['payinfo'],
+ 'paycvv' => $_POST['paycvv'],
+ 'month' => $_POST['month'],
+ 'year' => $_POST['year'],
+ 'payname' => $_POST['payname'],
+ 'address1' => $_POST['address1'],
+ 'address2' => $_POST['address2'],
+ 'city' => $_POST['city'],
+ 'state' => $_POST['state'],
+ 'zip' => $_POST['zip'],
+ 'save' => $_POST['save'],
+ 'auto' => $_POST['auto'],
+ 'paybatch' => $_POST['paybatch'],
+) );
+
+error_log("[process_payment_order_renew] received response from freeside: $response");
+
+$error = $response['error'];
+
+if ( $error ) {
+
+ error_log("[process_payment_order_renew] response error: $error");
+
+ header('Location:order_renew.php'.
+ '?session_id='. urlencode($_POST['session_id']).
+ '?error='. urlencode($error).
+ '&payby=CARD'.
+ '&amount='. urlencode($_POST['amount']).
+ '&payinfo='. urlencode($_POST['payinfo']).
+ '&paycvv='. urlencode($_POST['paycvv']).
+ '&month='. urlencode($_POST['month']).
+ '&year='. urlencode($_POST['year']).
+ '&payname='. urlencode($_POST['payname']).
+ '&address1='. urlencode($_POST['address1']).
+ '&address2='. urlencode($_POST['address2']).
+ '&city='. urlencode($_POST['city']).
+ '&state='. urlencode($_POST['state']).
+ '&zip='. urlencode($_POST['zip']).
+ '&save='. urlencode($_POST['save']).
+ '&auto='. urlencode($_POST['auto']).
+ '&paybatch='. urlencode($_POST['paybatch'])
+ );
+ die();
+
+}
+
+// sucessful renewal.
+
+$session_id = $response['session_id'];
+
+// now what?
+
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Renew Early</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>Renew Early</H1>
+
+ Renewal processed sucessfully.
+
+ </BODY>
+</HTML>
diff --git a/htdocs/browse/agent.cgi b/htdocs/browse/agent.cgi
deleted file mode 100755
index cf5f228..0000000
--- a/htdocs/browse/agent.cgi
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# agent.cgi: browse agent
-#
-# ivan@sisd.com 97-dec-12
-#
-# changes to allow pages to load from a relative location in the web tree.
-# bmccane@maxbaud.net 98-mar-25
-#
-# changed 'type' to 'atype' because type is reserved word in Pg6.3
-# bmccane@maxbaud.net 98-apr-3
-#
-# agent type was linking to wrong cgi ivan@sisd.com 98-jul-18
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use FS::UID qw(cgisuidsetup swapuid);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-print header('Agent Listing', menubar(
- 'Main Menu' => '../',
- 'Add new agent' => '../edit/agent.cgi'
-)), <<END;
- <BR>
- Click on agent number to edit.
- <TABLE BORDER>
- <TR>
- <TH><FONT SIZE=-1>Agent #</FONT></TH>
- <TH>Agent</TH>
- <TH>Type</TH>
- <TH><FONT SIZE=-1>Freq. (unimp.)</FONT></TH>
- <TH><FONT SIZE=-1>Prog. (unimp.)</FONT></TH>
- </TR>
-END
-
-my($agent);
-foreach $agent ( sort {
- $a->getfield('agentnum') <=> $b->getfield('agentnum')
-} qsearch('agent',{}) ) {
- my($hashref)=$agent->hashref;
- my($typenum)=$hashref->{typenum};
- my($agent_type)=qsearchs('agent_type',{'typenum'=>$typenum});
- my($atype)=$agent_type->getfield('atype');
- print <<END;
- <TR>
- <TD><A HREF="../edit/agent.cgi?$hashref->{agentnum}">
- $hashref->{agentnum}</A></TD>
- <TD>$hashref->{agent}</TD>
- <TD><A HREF="../edit/agent_type.cgi?$typenum">$atype</A></TD>
- <TD>$hashref->{freq}</TD>
- <TD>$hashref->{prog}</TD>
- </TR>
-END
-
-}
-
-print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/browse/agent_type.cgi b/htdocs/browse/agent_type.cgi
deleted file mode 100755
index 5f05bd5..0000000
--- a/htdocs/browse/agent_type.cgi
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# agent_type.cgi: browse agent_type
-#
-# ivan@sisd.com 97-dec-10
-#
-# Changes to allow page to work at a relative position in server
-# Changes to make "Packages" display 2-wide in table (old way was too vertical)
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use FS::UID qw(cgisuidsetup swapuid);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-print header("Agent Type Listing", menubar(
- 'Main Menu' => '../',
- 'Add new agent type' => "../edit/agent_type.cgi",
-)), <<END;
- <BR>Click on agent type number to edit.
- <TABLE BORDER>
- <TR>
- <TH><FONT SIZE=-1>Type #</FONT></TH>
- <TH>Type</TH>
- <TH colspan="2">Packages</TH>
- </TR>
-END
-
-my($agent_type);
-foreach $agent_type ( sort {
- $a->getfield('typenum') <=> $b->getfield('typenum')
-} qsearch('agent_type',{}) ) {
- my($hashref)=$agent_type->hashref;
- my(@type_pkgs)=qsearch('type_pkgs',{'typenum'=> $hashref->{typenum} });
- my($rowspan)=scalar(@type_pkgs);
- $rowspan = int($rowspan/2+0.5) ;
- print <<END;
- <TR>
- <TD ROWSPAN=$rowspan><A HREF="../edit/agent_type.cgi?$hashref->{typenum}">
- $hashref->{typenum}
- </A></TD>
- <TD ROWSPAN=$rowspan>$hashref->{atype}</TD>
-END
-
- my($type_pkgs);
- my($tdcount) = -1 ;
- foreach $type_pkgs ( @type_pkgs ) {
- my($pkgpart)=$type_pkgs->getfield('pkgpart');
- my($part_pkg) = qsearchs('part_pkg',{'pkgpart'=> $pkgpart });
- print qq!<TR>! if ($tdcount == 0) ;
- $tdcount = 0 if ($tdcount == -1) ;
- print qq!<TD><A HREF="../edit/part_pkg.cgi?$pkgpart">!,
- $part_pkg->getfield('pkg'),"</A></TD>";
- $tdcount ++ ;
- if ($tdcount == 2)
- {
- print qq!</TR>\n! ;
- $tdcount = 0 ;
- }
- }
-
- print "</TR>";
-}
-
-print <<END;
- </TR></TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/browse/cust_main_county.cgi b/htdocs/browse/cust_main_county.cgi
deleted file mode 100755
index d615198..0000000
--- a/htdocs/browse/cust_main_county.cgi
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_main_county.cgi: browse cust_main_county
-#
-# ivan@sisd.com 97-dec-13
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use FS::UID qw(cgisuidsetup swapuid);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-print header("Tax Rate Listing", menubar(
- 'Main Menu' => '../',
- 'Edit tax rates' => "../edit/cust_main_county.cgi",
-)),<<END;
- <BR>Click on <u>expand</u> to specify tax rates by county.
- <P><TABLE BORDER>
- <TR>
- <TH><FONT SIZE=-1>State</FONT></TH>
- <TH>County</TH>
- <TH><FONT SIZE=-1>Tax</FONT></TH>
- </TR>
-END
-
-my($cust_main_county);
-foreach $cust_main_county ( qsearch('cust_main_county',{}) ) {
- my($hashref)=$cust_main_county->hashref;
- print <<END;
- <TR>
- <TD>$hashref->{state}</TD>
-END
-
- print "<TD>", $hashref->{county}
- ? $hashref->{county}
- : qq!(ALL) <FONT SIZE=-1>!.
- qq!<A HREF="../edit/cust_main_county-expand.cgi?!. $hashref->{taxnum}.
- qq!">expand</A></FONT>!
- , "</TD>";
-
- print <<END;
- <TD>$hashref->{tax}%</TD>
- </TR>
-END
-
-}
-
-print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/browse/part_pkg.cgi b/htdocs/browse/part_pkg.cgi
deleted file mode 100755
index e5ff31e..0000000
--- a/htdocs/browse/part_pkg.cgi
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# part_svc.cgi: browse part_pkg
-#
-# ivan@sisd.com 97-dec-5,9
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use FS::UID qw(cgisuidsetup swapuid);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-print header("Package Part Listing",menubar(
- 'Main Menu' => '../',
- 'Add new package' => "../edit/part_pkg.cgi",
-)), <<END;
- <BR>Click on package part number to edit.
- <TABLE BORDER>
- <TR>
- <TH><FONT SIZE=-1>Part #</FONT></TH>
- <TH>Package</TH>
- <TH>Comment</TH>
- <TH><FONT SIZE=-1>Setup Fee</FONT></TH>
- <TH><FONT SIZE=-1>Freq.</FONT></TH>
- <TH><FONT SIZE=-1>Recur. Fee</FONT></TH>
- <TH>Service</TH>
- <TH><FONT SIZE=-1>Quan.</FONT></TH>
- </TR>
-END
-
-my($part_pkg);
-foreach $part_pkg ( sort {
- $a->getfield('pkgpart') <=> $b->getfield('pkgpart')
-} qsearch('part_pkg',{}) ) {
- my($hashref)=$part_pkg->hashref;
- my(@pkg_svc)=grep $_->getfield('quantity'),
- qsearch('pkg_svc',{'pkgpart'=> $hashref->{pkgpart} });
- my($rowspan)=scalar(@pkg_svc);
- print <<END;
- <TR>
- <TD ROWSPAN=$rowspan><A HREF="../edit/part_pkg.cgi?$hashref->{pkgpart}">
- $hashref->{pkgpart}
- </A></TD>
- <TD ROWSPAN=$rowspan>$hashref->{pkg}</TD>
- <TD ROWSPAN=$rowspan>$hashref->{comment}</TD>
- <TD ROWSPAN=$rowspan>$hashref->{setup}</TD>
- <TD ROWSPAN=$rowspan>$hashref->{freq}</TD>
- <TD ROWSPAN=$rowspan>$hashref->{recur}</TD>
-END
-
- my($pkg_svc);
- foreach $pkg_svc ( @pkg_svc ) {
- my($svcpart)=$pkg_svc->getfield('svcpart');
- my($part_svc) = qsearchs('part_svc',{'svcpart'=> $svcpart });
- print qq!<TD><A HREF="../edit/part_svc.cgi?$svcpart">!,
- $part_svc->getfield('svc'),"</A></TD><TD>",
- $pkg_svc->getfield('quantity'),"</TD></TR><TR>\n";
- }
-
- print "</TR>";
-}
-
-print <<END;
- </TR></TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/browse/part_referral.cgi b/htdocs/browse/part_referral.cgi
deleted file mode 100755
index b16fa89..0000000
--- a/htdocs/browse/part_referral.cgi
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# part_referral.cgi: Browse part_referral
-#
-# ivan@sisd.com 98-feb-23
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use FS::UID qw(cgisuidsetup swapuid);
-use FS::Record qw(qsearch);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-print header("Referral Listing", menubar(
- 'Main Menu' => '../',
- 'Add new referral' => "../edit/part_referral.cgi",
-)), <<END;
- <BR>Click on referral number to edit.
- <TABLE BORDER>
- <TR>
- <TH><FONT SIZE=-1>Referral #</FONT></TH>
- <TH>Referral</TH>
- </TR>
-END
-
-my($part_referral);
-foreach $part_referral ( sort {
- $a->getfield('refnum') <=> $b->getfield('refnum')
-} qsearch('part_referral',{}) ) {
- my($hashref)=$part_referral->hashref;
- print <<END;
- <TR>
- <TD><A HREF="../edit/part_referral.cgi?$hashref->{refnum}">
- $hashref->{refnum}</A></TD>
- <TD>$hashref->{referral}</TD>
- </TR>
-END
-
-}
-
-print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/browse/part_svc.cgi b/htdocs/browse/part_svc.cgi
deleted file mode 100755
index 71a5564..0000000
--- a/htdocs/browse/part_svc.cgi
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# part_svc.cgi: browse part_svc
-#
-# ivan@sisd.com 97-nov-14, 97-dec-9
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use FS::UID qw(cgisuidsetup swapuid);
-use FS::Record qw(qsearch);
-use FS::part_svc qw(fields);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-print header('Service Part Listing', menubar(
- 'Main Menu' => '../',
- 'Add new service' => "../edit/part_svc.cgi",
-)),<<END;
- <BR>Click on service part number to edit.
- <TABLE BORDER>
- <TR>
- <TH>Part #</TH>
- <TH>Service</TH>
- <TH>Table</TH>
- <TH>Field</TH>
- <TH>Action</TH>
- <TH>Value</TH>
- </TR>
-END
-
-my($part_svc);
-foreach $part_svc ( sort {
- $a->getfield('svcpart') <=> $b->getfield('svcpart')
-} qsearch('part_svc',{}) ) {
- my($hashref)=$part_svc->hashref;
- my($svcdb)=$hashref->{svcdb};
- my(@rows)=
- grep $hashref->{${svcdb}.'__'.$_.'_flag'},
- map { /^${svcdb}__(.*)$/; $1 }
- grep ! /_flag$/,
- grep /^${svcdb}__/,
- fields('part_svc')
- ;
- my($rowspan)=scalar(@rows);
- print <<END;
- <TR>
- <TD ROWSPAN=$rowspan><A HREF="../edit/part_svc.cgi?$hashref->{svcpart}">
- $hashref->{svcpart}
- </A></TD>
- <TD ROWSPAN=$rowspan>$hashref->{svc}</TD>
- <TD ROWSPAN=$rowspan>$hashref->{svcdb}</TD>
-END
- my($row);
- foreach $row ( @rows ) {
- my($flag)=$part_svc->getfield($svcdb.'__'.$row.'_flag');
- print "<TD>$row</TD><TD>";
- if ( $flag eq "D" ) { print "Default"; }
- elsif ( $flag eq "F" ) { print "Fixed"; }
- else { print "(Unknown!)"; }
- print "</TD><TD>",$part_svc->getfield($svcdb."__".$row),"</TD></TR><TR>";
- }
-print "</TR>";
-}
-
-print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/browse/svc_acct_pop.cgi b/htdocs/browse/svc_acct_pop.cgi
deleted file mode 100755
index a8a3a92..0000000
--- a/htdocs/browse/svc_acct_pop.cgi
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_acct_pop.cgi: browse pops
-#
-# ivan@sisd.com 98-mar-8
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use FS::UID qw(cgisuidsetup swapuid);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-print header('POP Listing', menubar(
- 'Main Menu' => '../',
- 'Add new POP' => "../edit/svc_acct_pop.cgi",
-)), <<END;
- <BR>Click on pop number to edit.
- <TABLE BORDER>
- <TR>
- <TH><FONT SIZE=-1>POP #</FONT></TH>
- <TH>City</TH>
- <TH>State</TH>
- <TH>Area code</TH>
- <TH>Exchange</TH>
- </TR>
-END
-
-my($svc_acct_pop);
-foreach $svc_acct_pop ( sort {
- $a->getfield('popnum') <=> $b->getfield('popnum')
-} qsearch('svc_acct_pop',{}) ) {
- my($hashref)=$svc_acct_pop->hashref;
- print <<END;
- <TR>
- <TD><A HREF="../edit/svc_acct_pop.cgi?$hashref->{popnum}">
- $hashref->{popnum}</A></TD>
- <TD>$hashref->{city}</TD>
- <TD>$hashref->{state}</TD>
- <TD>$hashref->{ac}</TD>
- <TD>$hashref->{exch}</TD>
- </TR>
-END
-
-}
-
-print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/docs/CGI-modules-2.76-patch.txt b/htdocs/docs/CGI-modules-2.76-patch.txt
deleted file mode 100755
index 55b50bb..0000000
--- a/htdocs/docs/CGI-modules-2.76-patch.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-ivan@rootwood:~/src/CGI-modules-2.76/CGI$ diff -c Base.pm Base.pm.orig
-*** Base.pm Sat Jul 18 00:33:21 1998
---- Base.pm.orig Sat Jul 18 00:06:12 1998
-***************
-*** 938,945 ****
- my $orig_uri = $self->get_uri;
- $self->log("Redirecting $CGI::Base::REQUEST_METHOD $orig_uri to $to_uri")
- if $Debug;
-! my $msg = ($perm) ? StatusHdr(301,"Moved Permanently")
-! : StatusHdr(302,"Moved Temporarily");
- my $hdrs = SendHeaders($msg, LocationHdr($to_uri));
- $self->log($hdrs);
- }
---- 938,945 ----
- my $orig_uri = $self->get_uri;
- $self->log("Redirecting $CGI::Base::REQUEST_METHOD $orig_uri to $to_uri")
- if $Debug;
-! my $msg = ($perm) ? ServerHdr(301,"Moved Permanently")
-! : ServerHdr(302,"Moved Temporarily");
- my $hdrs = SendHeaders($msg, LocationHdr($to_uri));
- $self->log($hdrs);
- }
-
diff --git a/htdocs/docs/admin.html b/htdocs/docs/admin.html
deleted file mode 100644
index 8adddbe..0000000
--- a/htdocs/docs/admin.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<head>
- <title>Administration</title>
-</head>
-<body>
- <h1>Administration</h1>
-</body>
diff --git a/htdocs/docs/billing.html b/htdocs/docs/billing.html
deleted file mode 100644
index 02bfbd7..0000000
--- a/htdocs/docs/billing.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<head>
- <title>Billing</title>
-</head>
-<body>
- <h1>Billing</h1>
- The bin/bill script can be run daily to bill all customers. Usage: bill [ -c [ i ] ] [ -d <i>date</i> ] [ -b ]
- <ul>
- <li>-c: Turn on collecting (you probably want this).
- <li>-i: Real-time billing (as opposed to bacth billing). Only relevant for credit cards. Not available without modifying site_perl/Bill.pm
- <li>-d: Pretend it is <i>date</i> (parsed by Date::Parse)
- <li>-b: N/A
- </ul>
- Printing should be configured on your freeside machine to print invoices.
- <br><br>Batch credit card processing
- <ul>
- <li>After this script is run, a credit card batch will be in the <a href="schema.html#cust_pay_batch">cust_pay_batch</a> table. Export this table to your credit card batching.
- <li>When your batch completes, erase the cust_pay_batch records in that batch and add any necessary paymants to the <a href="schema.html#cust_pay">cust_pay</a> table. Example code to add payments is:
- <pre>use FS::cust_pay;
-
-# loop over all records in batch
-
-my $payment=create FS::cust_pay (
- 'invnum' => $invnum,
- 'paid' => $paid,
- '_date' => $_date,
- 'payby' => $payby,
- 'payinfo' => $payinfo,
- 'paybatch' => $paybatch,
-);
-
-my $error=$payment->insert;
-if ( $error ) {
- #process error
-}
-
-# end loop
-</pre>
-All fields except paybatch are contained in the cust_pay_batch table. You can use paybatch field to track particular batches and/or particular transactions within a batch.
- </ul>
-</body>
diff --git a/htdocs/docs/config.html b/htdocs/docs/config.html
deleted file mode 100644
index 9b80026..0000000
--- a/htdocs/docs/config.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<head>
- <title>Configuration files</title>
-</head>
-<body>
- <h1>Configuration files</h1>
-Configuration files and directories are located in `/var/spool/freeside/conf'.
-<ul>
- <li>address - Your company name and address, four lines.
- <li>bsdshellmachines - Your BSD flavored shell (and mail) machines, one per line. This enables export of `/etc/passwd' and `/etc/master.passwd'.
- <li>cybercash2 - <a href="http://www.cybercash.com/cybercash/services/cashreg214.html">CyberCash v2</a> support, four lines: paymentserverhost, paymentserverport, paymentserversecret, and transaction type (`mauthonly' or `mauthcapture'). CCLib.pm is required.
- <li>cybercash3.2 - <a href="http://www.cybercash.com/cybercash/services/technology.html">CyberCash v3.2</a> support. Two lines: the full path and name of your merchant_conf file, and the transaction type (`mauthonly' or `mauthcapture'). CCMckLib3_2.pm, CCMckDirectLib3_2.pm and CCMckErrno3_2 are required.
- <li>domain - Your domain name.
- <li>erpcdmachines - Your ERPCD authenticaion machines, one per line. This enables export of `/usr/annex/acp_passwd' and `/usr/annex/acp_dialup'.
- <li>home - For new users, prefixed to usrename to create a directory name. Should have a leading but not a trailing slash.
- <li>lpr - Print command for paper invoices, for example `lpr -h'.
- <li>nismachines - Your NIS master (not slave master) machines, one per line. This enables export of `/etc/global/passwd' and `/etc/global/shadow'.
- <li>qmailmachines - Your qmail machines, one per line. This enables export of `/var/qmail/control/virtualdomains', `/var/qmail/control/recipientmap', and `/var/qmail/control/rcpthosts'. The existance of this file (even if empty) also turns on user `.qmail-extension' file maintenance in conjunction with `shellmachine'.
- <li>radiusmachines - Your RADIUS authentication machines, one per line. This enables export of `/etc/raddb/users'.
- <li>registries - Directory which contains domain registry information. Each registry is a directory.
- <ul>
- <li>registries/internic - Currently the only supported registry
- <ul>
- <li>registries/internic/from - Email address from which InterNIC domain registrations are sent.
- <li>regestries/internic/nameservers - The nameservers for InterNIC domain registrations, one per line. Each line contains an IP address and hostname, separated by whitespace.
- <li>registries/internic/tech_contact - Technical contact NIC handle for domain registrations.
- <li>registries/internic/template - Template for InterNIC domain registrations with special markup. A suitable copy of the InterNIC domain template v4.0 is in `fs-x.y.z/etc/domain-template.txt'.
- <li>registries/internic/to - Email address to which InterNIC domain registrations are sent.
- </ul>
- </ul>
- <li>secrets - Three lines: Database engine datasource (for example, `DBI:mysql:freeside' or `DBI:Pg:dbname=freeside'), username, and password. This file should not be world readable.
- <li>sendmailmachines - Your sendmail machines, one per line. This enables export of `/etc/virtusertable' and `/etc/sendmail.cw'.
- <li>shellmachine - A single machine with user home directories mounted. This enables home directory creation, renaming and archiving/deletion. In conjunction with `qmailmachines', it also enables `.qmail-extension' file maintenance.
- <li>shellmachines - Your Linux and System V flavored shell (and mail) machines, one per line. This enables export of `/etc/passwd' and `/etc/shadow' files.
- <li>shells - Legal shells (think /etc/shells). You probably want to `cut -d: -f7 /etc/passwd | sort | uniq' initially so that importing doesn't fail with `Illegal shell' errors, then remove any special entries afterwords. A blank line specifies that an empty shell is permitted.
- <li>smtpmachine - SMTP relay for Freeside's outgoing mail.
-</ul>
-</body>
-
diff --git a/htdocs/docs/export.html b/htdocs/docs/export.html
deleted file mode 100644
index f760b97..0000000
--- a/htdocs/docs/export.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<head>
- <title>File exporting</title>
-</head>
-<body>
- <h1>File exporting</h1>
- <ul>
- <li>bin/svc_acct.export will create UNIX `passwd', `shadow' and `master.passwd' files, ERPCD `acp_passwd' and `acp_dialup' files and a RADIUS `users' file in the `/var/spool/freeside/export' directory. Using the appropriate <a href="config.html">configuration files</a>, you can export these files to your remote machines unattended; see below.
- <ul>
- <li>shellmachines - passwd and shadow are copied to the remote machine as /etc/passwd.new and /etc/shadow.net and then moved to /etc/passwd and /etc/shadow if no errors occur.
- <li>bsdshellmachines - passwd and master.passwd are copied to the remote machine as /etc/passwd.new and /etc/master.passwd.new and moved to /etc/passwd and /etc/master.passwd if no errors occur.
- <li>nismachines - passwd and shadow are copied to the `/etc/global' directory on the remote machine. If no errors occur, the command `( cd /var/yp; make; )' is executed on the remote machine.
- <li>erpcdmachines - acp_passwd and acp_dialup are copied to the `/usr/annex' directory on the remote machine. If no errors occur, the command `( kill -USR1 `cat /usr/annex/erpcd.pid` )' is executed on the remote machine.
- <li>radiusmachines - users is copied to the `/etc/raddb' directory on the remote machine. If no errors occur, the command `( builddbm )' is executed on the remote machine.
- </ul>
- <li>site_perl/svc_acct.pm - If a shellmachine is defined, users can be created, modified and deleted remotely; see below.
- <ul>
- <li>The command `useradd -d <i>homedir</i> -s <i>shell</i> -u <i>uid</i> <i>username</i>' is executed when a user is added.
- <li>The command `userdel <i>username</i>' is executed with a user is deleted.
- <li>If a user's home directory changes, the command `[ -d <i>old_homedir</i> &amp;&amp; ( chmod u+t <i>old_homedir</i>; umask 022; mkdir <i>new_homedir</i>; cd <i>old_homedir</i>; find . -depth -print | cpio -pdm <i>new_homedir</i>; chmod u-t <i>new_homedir</i>; chown -R <i>uid</i>.<i>gid</i> <i>new_homedir</i>; rm -rf <i>old_homedir</i> )' is executed.
- </ul>
- <li>bin/svc_acct_sm.export will create <a href="http://www.qmail.org">Qmail</a> `rcpthosts', `recipientmap' and `virtualdomains' files and <a href="http://www.sendmail.org">Sendmail</a> `virtusertable' and `sendmail.cw' files in the `/var/spool/freeside/export' directory. Using the appropriate <a href="config.html">configuration files</a>, you can export these files to your remote machines unattemded; see below.
- <ul>
- <li>qmailmachines - recipientmap, virtualdomains and rcpthosts are copied to the `/var/qmail/control' directory on the remote machine. Note: If you <a href="legacy.html#svc_acct_sm">imported</a> qmail configuration files, run the generated `/var/spool/freeside/export/virtualdomains.FIX' on a machine with your user home directories before exporting qmail configuration files.
- <li>shellmachine - The command `[ -e <i>homedir</i>/.qmail-default ] || { touch <i>homedir</i>/.qmail-default; chown <i>uid</i>.<i>gid</i> <i>homedir</i>/.qmail-default; }' will be run on this machine for users in the virtualdomains file.
- <li>sendmailmachines - sendmail.cw and virtusertable are copied to the remote machine as /etc/sendmail.cw.new and /etc/virtusertable.new and moved to /etc/sendmail.cw and /etc/virtusertable if no errors occur.
- </ul>
- <li>site_perl/svc_acct_sm.pm - If the qmailmachines configuration file exists and a shellmachine is defined, user `.qmail-' files can be updated.
- <ul>
- <li>The command `[ -e <i>homedir</i>/.qmail-<i>domain</i>-default ] || { touch <i>homedir</i>/.qmail-<i>domain</i>-default; chown <i>uid</i>.<i>gid</i> <i>homedir</i>/.qmail-<i>domain</i>-default; }' is run.
- </ul>
- </ul>
- <br><a name=ssh>Unattended remote login</a> - Freeside can login to remote machines unattended using SSH. This can pose a security risk if not configured correctly, and will allow an intruder who breaks into your freeside machine full access to your remote machines. <b>Do not use this feature unless you understand what you are doing!</b>
- <ul>
- <li>As the freeside user (on your freeside machine), generate an authentication key using <a href="http://www.tac.nyc.ny.us/cgi-bin/man-cgi?ssh-keygen+1">ssh-keygen</a>. Since this is for unattended operation, you need to use a blank passphrase.
- <li>Append the newly-created identity.pub file to root's authorized_keys on the remote machine(s).
- </ul>
-
-</body>
-
diff --git a/htdocs/docs/index.html b/htdocs/docs/index.html
deleted file mode 100644
index 20051ca..0000000
--- a/htdocs/docs/index.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<head>
- <title>Documentation</title>
-</head>
-<body>
- <h1>Documentation</h1>
-<ul>
- <li><a href="install.html">New Installation</a>
- <li><a href="upgrade.html">Upgrading from 1.0.x to 1.1.x</a>
- <li><a href="upgrade2.html">Upgrading from 1.1.x to 1.1.3</a>
- <li><a href="config.html">Configuration files</a>
-<!--
- <li><a href="admin.html">Administration</a>
-!-->
- <li><a href="../index.html#admin">Administration</a>
- <li><a href="legacy.html">Importing legacy data</a>
- <li><a href="export.html">File exporting and remote setup</a>
- <li><a href="passwd.html">fs_passwd</a>
- <li><a href="billing.html">Billing</a>
- <li><a href="trouble.html">Troubleshooting</a>
- <li><a href="schema.html">Schema reference</a>
- <li><a href="man/">Perl API</a>
-</ul>
-</body>
diff --git a/htdocs/docs/install.html b/htdocs/docs/install.html
deleted file mode 100644
index c4784eb..0000000
--- a/htdocs/docs/install.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<head>
- <title>Installation</title>
-</head>
-<body>
-<h1>Installation</h1>
-Before installing, you need:
-<ul>
- <li>A web server, such as <a href="http://www.apache-ssl.org">Apache-SSL</a> or <a href="http://www.apache.org">Apache</a>
- <li><a href="ftp://ftp.cs.hut.fi/pub/ssh/">SSH</a>
- <li>agrep from the <a href="http://glimpse.cs.arizona.edu">Glimpse</a> distribution, if you want fuzzy searching capability
- <li><a href="http://www.perl.com/CPANl/doc/relinfo/INSTALL.html">Perl</a> (at least 5.004_04)
- <li>A database engine supported by Perl's <a href="http://www.hermetica.com/technologia/DBI/">DBI</a>, such as <a href="http://www.tcx.se/">MySQL</a> or <a href="http://www.postgresql.org/">PostgreSQL</a>
- <li>Perl modules
- <ul>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/MIME/">MIME-Base64</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Data">Data-Dumper</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/MD5">MD5</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Net">libnet</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/LWP/">libwww-perl</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/CGI/">CGI-modules</a> (<b>NOT</b> CGI.pm) with this <a href="CGI-modules-2.76-patch.txt">patch</a> applied
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Business/">Business-CreditCard</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Data/">Data-ShowTable</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Mail/">MailTools</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Time/">TimeDate</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Date/">DateManip</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/File/">File-CounterFile</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/FreezeThaw/">FreezeThaw</a>
- <li><a href="http://www.perl.com/CPAN/modules/by-module/DBI/">DBI
- <li><a href="http://www.perl.com/CPAN/modules/by-module/DBD/">DBD for your database engine</a>
- </ul>
-</ul>
-Install the Freeside distribution:
-<ul>
- <li>Add the user `freeside' to your system.
- <li>Add the freeside database to your database engine. (with <a href="http://www.mysql.com/Manual_chapter/manual_Syntax.html#Create database">MySQL</a>) (with <a href="http://www.postgresql.org/docs/admin/manage-ag.htm#AEN854">PostgreSQL</a>)
- <li>Allow the freeside user full access to the freeside database. (with <a href="http://www.mysql.com/Manual_chapter/manual_Privilege_system.html#Privilege system">MySQL</a>) (with <a href="http://www.postgresql.org/docs/admin/newuser.htm">PostgreSQL</a>)
- <li>Unpack the tarball: <pre>gunzip -c fs-x.y.z.tar.gz | tar xvf -</pre>
- <li>Copy or link fs-x.y.z/site_perl to FS in your site_perl directory. (try `<code>perl -V</code>' if unsure) <pre>mkdir /usr/local/lib/site_perl/FS
-cp fs-x.y.z/site_perl/* /usr/local/lib/site_perl/FS</pre> or <pre>ln -s /full/path/to/fs-x.y.z/site_perl /usr/local/lib/site_perl/FS</pre>
- <li>Copy or link fs-x.y.z/htdocs to your web server's document space. <pre>mkdir /usr/local/apache/htdocs/freeside
-cp -r fs-x.y.z/htdocs/* /usr/local/apache/htdocs/freeside</pre> or <pre>ln -s /full/path/to/fs-x.y.z/htdocs /usr/local/apache/htdocs/freeside</pre>
- <li>Restrict access to this web interface. (with <a href="http://www.apache.org/docs/misc/FAQ.html#user-authentication">Apache</a>)
- <li>Enable CGI execution for files with the `.cgi' extension. (with <a href="http://www.apache.org/docs/mod/mod_mime.html#addhandler">Apache</a>)
- <li>Set ownership and permissions for the web interface. Your system should support secure setuid scripts or Perl's emulation, see <a href="http://www.perl.com/CPAN-local/doc/manual/html/pod/perlsec.html#Security_Bugs">perlsec: Security Bugs</a> for information and workarounds.
-<pre>cd /usr/local/apache/htdocs/freeside
-chown -R freeside .
-chmod 4755 browse/*.cgi edit/*.cgi edit/process/*.cgi misc/*.cgi misc/process/*.cgi search/*.cgi view/*.cgi</pre>
-<li>Create the base Freeside directory `/var/spool/freeside', and the subdirectories `conf', `counters', and `export'. <pre>mkdir /var/spool/freeside
-mkdir /var/spool/freeside/conf
-mkdir /var/spool/freeside/counters
-mkdir /var/spool/freeside/export
-chown -R freeside /var/spool/freeside</pre>
- <li>Create the necessary <a href="config.html">configuration files</a>.
- <li>Run bin/fs-setup to create the database tables.
-</ul>
-</body>
diff --git a/htdocs/docs/legacy.html b/htdocs/docs/legacy.html
deleted file mode 100644
index 40e09cb..0000000
--- a/htdocs/docs/legacy.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<head>
- <title>Importing legacy data</title>
-</head>
-<body>
- <h1>Importing legacy data</h1>
-<ul>
- <li><a name="svc_acct">bin/svc_acct.import</a> - Import `passwd', ( `shadow' or `master.passwd' ) and RADIUS `users'. Before running bin/svc_acct.import, you need <a href="http://rootwood.sisd.com/freeside/browse/part_svc.cgi">services</a> (with table svc_acct) as follows:
- <ul>
- <li>Most accounts probably have entries in passwd and users (with Port-Limit nonexistant or 1)
- <li>Some accounts have entries in passwd and users, but with Port-Limit 2 (or more)
- <li>Some accounts might have entries in users only (Port-Limit 1)
- <li>Some accounts might have entries in users only (Port-Limit >= 2)
- <li>POP mail accounts have entries in passwd only, and have a particular shell.
- <li>Everything else in passwd is a shell account.
- </ul>
- <li><a name="svc_acct_sm">bin/svc_acct_sm.import</a> - Import qmail ( `virtualdomains' and `rcpthosts' ), or sendmail ( `virtusertable' and `sendmail.cw' ) files. Before running bin/svc_acct_sm.import, you need <a href="http://rootwood.sisd.com/freeside/browse/part_svc.cgi">services</a> as follows:
- <ul>
- <li>Domain (table svc_acct)
- <li>Mail alias (table svc_acct_sm)
- </ul>
- <li><a name="cust_main">Importing customer data</a>
- <ul>
- <li>Manually
- <ul>
- <li>Add a <a href="../edit/cust_main.cgi">new customer</a>
- <li>Add one or more packages for this customer
- <li>Enter a package by clicking on the package number
- <li>Pick the `Link to existing' option
- </ul>
- <li>Batch - You will need to write a script to import your particular legacy data. You can use eg/TEMPLATE_cust_main.import as a starting point.
- </ul>
-</ul>
-</body>
-
diff --git a/htdocs/docs/man/Bill.txt b/htdocs/docs/man/Bill.txt
deleted file mode 100644
index 545dd1a..0000000
--- a/htdocs/docs/man/Bill.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-NAME
- FS::Bill - Legacy stub
-
-SYNOPSIS
- The functionality of FS::Bill has been integrated into
- FS::cust_main.
-
-HISTORY
- ivan@voicenet.com 97-jul-24 - 25 - 28
-
- use Safe; evaluate all fees with perl (still on TODO list until
- I write some examples & test opmask to see if we can read db)
- %hash=$obj->hash later ivan@sisd.com 98-mar-13
-
- packages with no next bill date start at $time not time, this
- should eliminate the last of the problems with billing at a past
- date also rewrite the invoice priting logic not to print
- invoices for things that haven't happended yet and update
- $cust_bill->printed when we print so PAST DUE notices work, and
- s/date/_date/ ivan@sisd.com 98-jun-4
-
- more logic for past due stuff - packages with no next bill date
- start at $cust_pkg->setup || $time ivan@sisd.com 98-jul-13
-
- moved a few things in collection logic; negative charges should
- work ivan@sisd.com 98-aug-6
-
- pod, moved everything to FS::cust_main ivan@sisd.com 98-sep-19
-
diff --git a/htdocs/docs/man/CGI.txt b/htdocs/docs/man/CGI.txt
deleted file mode 100644
index 54f9b8a..0000000
--- a/htdocs/docs/man/CGI.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-NAME
- FS::CGI - Subroutines for the web interface
-
-SYNOPSIS
- use FS::CGI qw(header menubar idiot eidiot);
-
- print header( 'Title', '' );
- print header( 'Title', menubar('item', 'URL', ... ) );
-
- idiot "error message";
- eidiot "error message";
-
-DESCRIPTION
- Provides a few common subroutines for the web interface.
-
-SUBROUTINES
- header TITLE, MENUBAR
- Returns an HTML header.
-
- menubar ITEM, URL, ...
- Returns an HTML menubar.
-
- idiot ERROR
- Sends headers and an HTML error message.
-
- eidiot ERROR
- Sends headers and an HTML error message, then exits.
-
-BUGS
- Not OO.
-
- Not complete.
-
- Uses CGI-modules instead of CGI.pm
-
-SEE ALSO
- the CGI::Base manpage
-
-HISTORY
- subroutines for the HTML/CGI GUI, not properly OO. :(
-
- ivan@sisd.com 98-apr-16 ivan@sisd.com 98-jun-22
-
- lose the background, eidiot ivan@sisd.com 98-sep-2
-
- pod ivan@sisd.com 98-sep-12
-
diff --git a/htdocs/docs/man/Conf.txt b/htdocs/docs/man/Conf.txt
deleted file mode 100644
index c46c9ee..0000000
--- a/htdocs/docs/man/Conf.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-NAME
- FS::Conf - Read access to Freeside configuration values
-
-SYNOPSIS
- use FS::Conf;
-
- $conf = new FS::Conf;
- $conf = new FS::Conf "/non/standard/config/directory";
-
- $dir = $conf->dir;
-
- $value = $conf->config('key');
- @list = $conf->config('key');
- $bool = $conf->exists('key');
-
-DESCRIPTION
- Read access to Freeside configuration values. Keys currently map
- to filenames, but this may change in the future.
-
-METHODS
- new [ DIRECTORY ]
- Create a new configuration object. Optionally, a non-default
- directory may be specified.
-
- dir Returns the directory.
-
- config
- Returns the configuration value or values (depending on
- context) for key.
-
- exists
- Returns true if the specified key exists, even if the
- corresponding value is undefined.
-
-BUGS
- The option to specify a non-default directory should probably be
- removed.
-
- Write access (with locking) should be implemented.
-
-SEE ALSO
- config.html from the base documentation contains a list of
- configuration files.
-
-HISTORY
- Ivan Kohler <ivan@sisd.com> 98-sep-6
-
diff --git a/htdocs/docs/man/Invoice.txt b/htdocs/docs/man/Invoice.txt
deleted file mode 100644
index 17953d5..0000000
--- a/htdocs/docs/man/Invoice.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-NAME
- FS::Invoice - Legacy stub
-
-SYNOPSIS
- The functioanlity of FS::invoice has been integrated in
- FS::cust_bill.
-
-HISTORY
- ivan@voicenet.com 97-jun-25 - 27
-
- maybe should be changed to be OO-functions on $cust_bill
- objects? (instead of passing invnum, ugh).
-
- ISA cust_bill and return inovice instead of passing filehandle
- ivan@sisd.com 98-mar-13 (add postscript output!)
-
- close our kid when we're done ivan@sisd.com 98-jun-4
-
- separated code which shuffled data from code which formatted.
- (so i could) fixed past due notices showing up when balance due
- =< 0 return address comes from /var/spool/freeside/conf/address
- ivan@sisd.com 98-jul-2
-
diff --git a/htdocs/docs/man/Record.txt b/htdocs/docs/man/Record.txt
deleted file mode 100644
index 0accb65..0000000
--- a/htdocs/docs/man/Record.txt
+++ /dev/null
@@ -1,332 +0,0 @@
-NAME
- FS::Record - Database record objects
-
-SYNOPSIS
- use FS::Record;
- use FS::Record qw(dbh fields hfields qsearch qsearchs dbdef);
-
- $record = new FS::Record 'table', \%hash;
- $record = new FS::Record 'table', { 'column' => 'value', ... };
-
- $record = qsearchs FS::Record 'table', \%hash;
- $record = qsearchs FS::Record 'table', { 'column' => 'value', ... };
- @records = qsearch FS::Record 'table', \%hash;
- @records = qsearch FS::Record 'table', { 'column' => 'value', ... };
-
- $table = $record->table;
- $dbdef_table = $record->dbdef_table;
-
- $value = $record->get('column');
- $value = $record->getfield('column');
- $value = $record->column;
-
- $record->set( 'column' => 'value' );
- $record->setfield( 'column' => 'value' );
- $record->column('value');
-
- %hash = $record->hash;
-
- $hashref = $record->hashref;
-
- $error = $record->add;
-
- $error = $record->del;
-
- $error = $new_record->rep($old_record);
-
- $value = $record->unique('column');
-
- $value = $record->ut_float('column');
- $value = $record->ut_number('column');
- $value = $record->ut_numbern('column');
- $value = $record->ut_money('column');
- $value = $record->ut_text('column');
- $value = $record->ut_textn('column');
- $value = $record->ut_alpha('column');
- $value = $record->ut_alphan('column');
- $value = $record->ut_phonen('column');
- $value = $record->ut_anythingn('column');
-
- $dbdef = reload_dbdef;
- $dbdef = reload_dbdef "/non/standard/filename";
- $dbdef = dbdef;
-
- $quoted_value = _quote($value,'table','field');
-
- #depriciated
- $fields = hfields('table');
- if ( $fields->{Field} ) { # etc.
-
- @fields = fields 'table';
-
-DESCRIPTION
- (Mostly) object-oriented interface to database records. Records
- are currently implemented on top of DBI. FS::Record is intended
- as a base class for table-specific classes to inherit from, i.e.
- FS::cust_main.
-
-METHODS
- new TABLE, HASHREF
- Creates a new record. It doesn't store it in the database,
- though. See the section on "add" for that.
-
- Note that the object stores this hash reference, not a
- distinct copy of the hash it points to. You can ask the
- object for a copy with the *hash* method.
-
- qsearch TABLE, HASHREF
- Searches the database for all records matching (at least)
- the key/value pairs in HASHREF. Returns all the records
- found as FS::Record objects.
-
- qsearchs TABLE, HASHREF
- Searches the database for a record matching (at least) the
- key/value pairs in HASHREF, and returns the record found as
- an FS::Record object. If more than one record matches, it
- carps but returns the first. If this happens, you either
- made a logic error in asking for a single item, or your data
- is corrupted.
-
- table
- Returns the table name.
-
- dbdef_table
- Returns the FS::dbdef_table object for the table.
-
- get, getfield COLUMN
- Returns the value of the column/field/key COLUMN.
-
- set, setfield COLUMN, VALUE
- Sets the value of the column/field/key COLUMN to VALUE.
- Returns VALUE.
-
- AUTLOADED METHODS
- $record->column is a synonym for $record->get('column');
-
- $record->column('value') is a synonym for $record-
- >set('column','value');
-
- hash
- Returns a list of the column/value pairs, usually for
- assigning to a new hash.
-
- To make a distinct duplicate of an FS::Record object, you
- can do:
-
- $new = new FS::Record ( $old->table, { $old->hash } );
-
- hashref
- Returns a reference to the column/value hash.
-
- add Adds this record to the database. If there is an error, returns
- the error, otherwise returns false.
-
- del Delete this record from the database. If there is an error,
- returns the error, otherwise returns false.
-
- rep OLD_RECORD
- Replace the OLD_RECORD with this one in the database. If
- there is an error, returns the error, otherwise returns
- false.
-
- unique COLUMN
- Replaces COLUMN in record with a unique number. Called by
- the add method on primary keys and single-field unique
- columns (see the FS::dbdef_table manpage). Returns the new
- value.
-
- ut_float COLUMN
- Check/untaint floating point numeric data: 1.1, 1, 1.1e10,
- 1e10. May not be null. If there is an error, returns the
- error, otherwise returns false.
-
- ut_number COLUMN
- Check/untaint simple numeric data (whole numbers). May not
- be null. If there is an error, returns the error, otherwise
- returns false.
-
- ut_numbern COLUMN
- Check/untaint simple numeric data (whole numbers). May be
- null. If there is an error, returns the error, otherwise
- returns false.
-
- ut_money COLUMN
- Check/untaint monetary numbers. May be negative. Set to 0 if
- null. If there is an error, returns the error, otherwise
- returns false.
-
- ut_text COLUMN
- Check/untaint text. Alphanumerics, spaces, and the following
- punctuation symbols are currently permitted: ! @ # $ % & ( )
- - + ; : ' " , . ? / May not be null. If there is an error,
- returns the error, otherwise returns false.
-
- ut_textn COLUMN
- Check/untaint text. Alphanumerics, spaces, and the following
- punctuation symbols are currently permitted: ! @ # $ % & ( )
- - + ; : ' " , . ? / May be null. If there is an error,
- returns the error, otherwise returns false.
-
- ut_alpha COLUMN
- Check/untaint alphanumeric strings (no spaces). May not be
- null. If there is an error, returns the error, otherwise
- returns false.
-
- ut_alpha COLUMN
- Check/untaint alphanumeric strings (no spaces). May be null.
- If there is an error, returns the error, otherwise returns
- false.
-
- ut_phonen COLUMN
- Check/untaint phone numbers. May be null. If there is an
- error, returns the error, otherwise returns false.
-
- ut_anything COLUMN
- Untaints arbitrary data. Be careful.
-
-SUBROUTINES
- reload_dbdef([FILENAME])
- Load a database definition (see the FS::dbdef manpage),
- optionally from a non-default filename. This command is
- executed at startup unless *$FS::Record::setup_hack* is
- true. Returns a FS::dbdef object.
-
- dbdef Returns the current database definition. See the FS::dbdef
- manpage.
-
- _quote VALUE, TABLE, COLUMN
- This is an internal function used to construct SQL
- statements. It returns VALUE DBI-quoted (see the section
- on "quote" in the DBI manpage) unless VALUE is a number
- and the column type (see the dbdef_column manpage) does
- not end in `char' or `binary'.
-
- hfields TABLE
- This is depriciated. Don't use it.
-
- It returns a hash-type list with the fields of this
- record's table set true.
-
- fields TABLE
- This returns a list of the columns in this record's
- table (See the dbdef_table manpage).
-
-BUGS
- This module should probably be renamed, since much of the
- functionality is of general use. It is not completely unlike
- Adapter::DBI (see below).
-
- Exported qsearch and qsearchs should be depriciated in favor
- of method calls (against an FS::Record object like the old
- search and searchs that qsearch and qsearchs were on top
- of.)
-
- The whole fields / hfields mess should be removed.
-
- The various WHERE clauses should be subroutined.
-
- table string should be depriciated in favor of
- FS::dbdef_table.
-
- No doubt we could benefit from a Tied hash. Documenting how
- exists / defined true maps to the database (and WHERE
- clauses) would also help.
-
- The ut_ methods should ask the dbdef for a default length.
-
- ut_sqltype (like ut_varchar) should all be defined
-
- A fallback check method should be provided with uses the
- dbdef.
-
- The ut_money method assumes money has two decimal digits.
-
- The Pg money kludge in the new method only strips `$'.
-
- The ut_phonen method assumes US-style phone numbers.
-
- The _quote function should probably use ut_float instead of
- a regex.
-
- All the subroutines probably should be methods, here or
- elsewhere.
-
-SEE ALSO
- the FS::dbdef manpage, the FS::UID manpage, the DBI manpage
-
- Adapter::DBI from Ch. 11 of Advanced Perl Programming by
- Sriram Srinivasan.
-
-HISTORY
- ivan@voicenet.com 97-jun-2 - 9, 19, 25, 27, 30
-
- DBI version ivan@sisd.com 97-nov-8 - 12
-
- cleaned up, added autoloaded $self->any_field calls, moved
- DBI login stuff to FS::UID ivan@sisd.com 97-nov-21-23
-
- since AUTO_INCREMENT is MySQL specific, use my own unique
- number generator (again) ivan@sisd.com 97-dec-4
-
- untaint $user in unique (web demo hack...bah) make unique
- skip multiple-field unique's from dbdef ivan@sisd.com 97-
- dec-11
-
- merge with FS::Search, which after all was just alternate
- constructors for FS::Record objects. Makes lots of things
- cleaner. :) ivan@sisd.com 97-dec-13
-
- use FS::dbdef::primary key in replace searches, hopefully
- for all practical purposes the string/number problem in SQL
- statements should be gone? (SQL bites) ivan@sisd.com 98-jan-
- 20
-
- Put all SQL statments in $statment before we $sth=$dbh-
- >prepare( them, for debugging reasons (warn $statement)
- ivan@sisd.com 98-feb-19
-
- (sigh)... use dbdef type (char, etc.) instead of a regex to
- decide what to quote in _quote (more sillines...) SQL bites.
- ivan@sisd.com 98-feb-20
-
- more friendly error messages ivan@sisd.com 98-mar-13
-
- Added import of datasrc from FS::UID to allow Pg6.3 to work
- Added code to right-trim strings read from Pg6.3 databases
- Modified 'add' to only insert fields that actually have data
- Added ut_float to handle floating point numbers (for sales
- tax). Pg6.3 does not have a "SHOW FIELDS" statement, so I
- faked it 8). bmccane@maxbaud.net 98-apr-3
-
- commented out Pg wrapper around `` Modified 'add' to only
- insert fields that actually have data '' ivan@sisd.com 98-
- apr-16
-
- dbdef usage changes ivan@sisd.com 98-jun-1
-
- sub fields now asks dbdef, not database ivan@sisd.com 98-
- jun-2
-
- added debugging method ->_dump ivan@sisd.com 98-jun-16
-
- use FS::dbdef::primary key in delete searches as well as
- replace searches (SQL still bites) ivan@sisd.com 98-jun-22
-
- sub dbdef_table ivan@sisd.com 98-jun-28
-
- removed Pg wrapper around `` Modified 'add' to only insert
- fields that actually have data '' ivan@sisd.com 98-jul-14
-
- sub fields croaks on errors ivan@sisd.com 98-jul-17
-
- $rc eq '0E0' doesn't mean we couldn't delete for all rdbmss
- ivan@sisd.com 98-jul-18
-
- commented out code to right-trim strings read from Pg6.3
- databases; ChopBlanks is in UID.pm ivan@sisd.com 98-aug-16
-
- added code (with Pg wrapper) to deal with Pg money fields
- ivan@sisd.com 98-aug-18
-
- added pod documentation ivan@sisd.com 98-sep-6
-
diff --git a/htdocs/docs/man/SSH.txt b/htdocs/docs/man/SSH.txt
deleted file mode 100644
index b6d205b..0000000
--- a/htdocs/docs/man/SSH.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-NAME
- FS::SSH - Subroutines to call ssh and scp
-
-SYNOPSIS
- use FS::SSH qw(ssh scp issh iscp sshopen2 sshopen3);
-
- ssh($host, $command);
-
- issh($host, $command);
-
- scp($source, $destination);
-
- iscp($source, $destination);
-
- sshopen2($host, $reader, $writer, $command);
-
- sshopen3($host, $reader, $writer, $error, $command);
-
-DESCRIPTION
- Simple wrappers around ssh and scp commands.
-
-SUBROUTINES
- ssh HOST, COMMAND
- Calls ssh in batch mode.
-
- issh HOST, COMMAND
- Prints the ssh command to be executed, waits for the user to
- confirm, and (optionally) executes the command.
-
- scp SOURCE, DESTINATION
- Calls scp in batch mode.
-
- iscp SOURCE, DESTINATION
- Prints the scp command to be executed, waits for the user to
- confirm, and (optionally) executes the command.
-
- sshopen2 HOST, READER, WRITER, COMMAND
- Connects the supplied filehandles to the ssh process (in
- batch mode).
-
- sshopen3 HOST, WRITER, READER, ERROR, COMMAND
- Connects the supplied filehandles to the ssh process (in
- batch mode).
-
-BUGS
- Not OO.
-
- scp stuff should transparantly use rsync-over-ssh instead.
-
-SEE ALSO
- the ssh manpage, the scp manpage, the IPC::Open2 manpage,
- the IPC::Open3 manpage
-
-HISTORY
- ivan@voicenet.com 97-jul-17
-
- added sshopen2 and sshopen3 ivan@sisd.com 98-mar-9
-
- added iscp ivan@sisd.com 98-jul-25 now iscp asks y/n, issh
- and took out path ivan@sisd.com 98-jul-30
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/UID.txt b/htdocs/docs/man/UID.txt
deleted file mode 100644
index bf9f6b4..0000000
--- a/htdocs/docs/man/UID.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-NAME
- FS::UID - Subroutines for database login and assorted other
- stuff
-
-SYNOPSIS
- use FS::UID qw(adminsuidsetup cgisuidsetup dbh datasrc getotaker
- checkeuid checkruid swapuid);
-
- adminsuidsetup;
-
- $cgi = new CGI::Base;
- $cgi->get;
- $dbh = cgisuidsetup($cgi);
-
- $dbh = dbh;
-
- $datasrc = datasrc;
-
-DESCRIPTION
- Provides a hodgepodge of subroutines.
-
-SUBROUTINES
- adminsuidsetup
- Cleans the environment. Make sure the script is running as
- freeside, or setuid freeside. Opens a connection to the
- database. Swaps real and effective UIDs. Returns the DBI
- database handle (usually you don't need this).
-
- dbh Returns the DBI database handle.
-
- datasrc
- Returns the DBI data source.
-
- getotaker
- Returns the current Freeside user. Currently that means the
- CGI REMOTE_USER, or 'freeside'.
-
- checkeuid
- Returns true if effective UID is that of the freeside user.
-
- checkruid
- Returns true if the real UID is that of the freeside user.
-
- swapuid
- Swaps real and effective UIDs.
-
-BUGS
- Not OO.
-
- No capabilities yet. When mod_perl and Authen::DBI are
- implemented, cgisuidsetup will go away as well.
-
-SEE ALSO
- the FS::Record manpage, the CGI::Base manpage, the DBI manpage
-
-HISTORY
- ivan@voicenet.com 97-jun-4 - 9 untaint otaker ivan@voicenet.com
- 97-jul-7
-
- generalize and auto-get uid (getotaker still needs to be db'ed)
- ivan@sisd.com 97-nov-10
-
- &cgisuidsetup logs into database. other cleaning. ivan@sisd.com
- 97-nov-22,23
-
- &adminsuidsetup logs into database with otaker='freeside' (for
- automated tasks like billing) ivan@sisd.com 97-dec-13
-
- added sub datasrc for fs-setup ivan@sisd.com 98-feb-21
-
- datasrc, user and pass now come from conf/secrets ivan@sisd.com
- 98-jun-28
-
- added ChopBlanks to DBI call (see man DBI) ivan@sisd.com 98-aug-
- 16
-
- pod, use FS::Conf, implemented cgisuidsetup as adminsuidsetup,
- inlined suidsetup ivan@sisd.com 98-sep-12
-
diff --git a/htdocs/docs/man/agent.txt b/htdocs/docs/man/agent.txt
deleted file mode 100644
index b0317f6..0000000
--- a/htdocs/docs/man/agent.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-NAME
- FS::agent - Object methods for agent records
-
-SYNOPSIS
- use FS::agent;
-
- $record = create FS::agent \%hash;
- $record = create FS::agent { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::agent object represents an agent. Every customer has an
- agent. Agents can be used to track things like resellers or
- salespeople. FS::agent inherits from FS::Record. The following
- fields are currently supported:
-
- agemtnum - primary key (assigned automatically for new agents)
- agent - Text name of this agent
- typenum - Agent type. See the FS::agent_type manpage
- prog - For future use.
- freq - For future use.
-METHODS
- create HASHREF
- Creates a new agent. To add the agent to the database, see
- the section on "insert".
-
- insert
- Adds this agent to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Deletes this agent from the database. Only agents with no
- customers can be deleted. If there is an error, returns the
- error, otherwise returns false.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid agent. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
-SEE ALSO
- the FS::Record manpage, the FS::agent_type manpage, the
- FS::cust_main manpage, schema.html from the base documentation.
-
-HISTORY
- Class dealing with agent (resellers)
-
- ivan@sisd.com 97-nov-13, 97-dec-10
-
- pod, added check in ->delete ivan@sisd.com 98-sep-22
-
diff --git a/htdocs/docs/man/agent_type.txt b/htdocs/docs/man/agent_type.txt
deleted file mode 100644
index ea1edec..0000000
--- a/htdocs/docs/man/agent_type.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-NAME
- FS::agent_type - Object methods for agent_type records
-
-SYNOPSIS
- use FS::agent_type;
-
- $record = create FS::agent_type \%hash;
- $record = create FS::agent_type { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::agent_type object represents an agent type. Every agent
- (see the FS::agent manpage) has an agent type. Agent types
- define which packages (see the FS::part_pkg manpage) may be
- purchased by customers (see the FS::cust_main manpage), via
- FS::type_pkgs records (see the FS::type_pkgs manpage).
- FS::agent_type inherits from FS::Record. The following fields
- are currently supported:
-
- typenum - primary key (assigned automatically for new agent types)
- atype - Text name of this agent type
-METHODS
- create HASHREF
- Creates a new agent type. To add the agent type to the
- database, see the section on "insert".
-
- insert
- Adds this agent type to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Deletes this agent type from the database. Only agent types
- with no agents can be deleted. If there is an error, returns
- the error, otherwise returns false.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid agent type.
- If there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
-SEE ALSO
- the FS::Record manpage, the FS::agent manpage, the FS::type_pkgs
- manpage, the FS::cust_main manpage, the FS::part_pkg manpage,
- schema.html from the base documentation.
-
-HISTORY
- Class for the different sets of allowable packages you can
- assign to an agent.
-
- ivan@sisd.com 97-nov-13
-
- ut_ FS::Record methods ivan@sisd.com 97-dec-10
-
- Changed 'type' to 'atype' because Pg6.3 reserves the type word
- bmccane@maxbaud.net 98-apr-3
-
- pod, added check in delete ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/cust_bill.txt b/htdocs/docs/man/cust_bill.txt
deleted file mode 100644
index 9762dd3..0000000
--- a/htdocs/docs/man/cust_bill.txt
+++ /dev/null
@@ -1,140 +0,0 @@
-NAME
- FS::cust_bill - Object methods for cust_bill records
-
-SYNOPSIS
- use FS::cust_bill;
-
- $record = create FS::cust_bill \%hash;
- $record = create FS::cust_bill { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- ( $total_previous_balance, @previous_cust_bill ) = $record->previous;
-
- @cust_bill_pkg_objects = $cust_bill->cust_bill_pkg;
-
- ( $total_previous_credits, @previous_cust_credit ) = $record->cust_credit;
-
- @cust_pay_objects = $cust_bill->cust_pay;
-
- @lines = $cust_bill->print_text;
- @lines = $cust_bill->print_text $time;
-
-DESCRIPTION
- An FS::cust_bill object represents an invoice. FS::cust_bill
- inherits from FS::Record. The following fields are currently
- supported:
-
- invnum - primary key (assigned automatically for new invoices)
- custnum - customer (see the FS::cust_main manpage)
- _date - specified as a UNIX timestamp; see the section on "time" in the perlfunc manpage. Also see
- the Time::Local manpage and the Date::Parse manpage for conversion functions.
- charged - amount of this invoice
- owed - amount still outstanding on this invoice, which is charged minus
- all payments (see the FS::cust_pay manpage).
- printed - how many times this invoice has been printed automatically
- (see the section on "collect" in the FS::cust_main manpage).
-METHODS
- create HASHREF
- Creates a new invoice. To add the invoice to the database,
- see the section on "insert". Invoices are normally created
- by calling the bill method of a customer object (see the
- FS::cust_main manpage).
-
- insert
- Adds this invoice to the database ("Posts" the invoice). If
- there is an error, returns the error, otherwise returns
- false.
-
- When adding new invoices, owed must be charged (or null, in
- which case it is automatically set to charged).
-
- delete
- Currently unimplemented. I don't remove invoices because
- there would then be no record you ever posted this invoice
- (which is bad, no?)
-
- replace OLD_RECORD
- Replaces the OLD_RECORD with this one in the database. If
- there is an error, returns the error, otherwise returns
- false.
-
- Only owed and printed may be changed. Owed is normally
- updated by creating and inserting a payment (see the
- FS::cust_pay manpage). Printed is normally updated by
- calling the collect method of a customer object (see the
- FS::cust_main manpage).
-
- check
- Checks all fields to make sure this is a valid invoice. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
- previous
- Returns a list consisting of the total previous balance for
- this customer, followed by the previous outstanding invoices
- (as FS::cust_bill objects also).
-
- cust_bill_pkg
- Returns the line items (see the FS::cust_bill_pkg manpage)
- for this invoice.
-
- cust_credit
- Returns a list consisting of the total previous credited
- (see the FS::cust_credit manpage) for this customer,
- followed by the previous outstanding credits
- (FS::cust_credit objects).
-
- cust_pay
- Returns all payments (see the FS::cust_pay manpage) for this
- invoice.
-
- print_text [TIME];
- Returns an ASCII invoice, as a list of lines.
-
- TIME an optional value used to control the printing of
- overdue messages. The default is now. It isn't the date of
- the invoice; that's the `_date' field. It is specified as a
- UNIX timestamp; see the section on "time" in the perlfunc
- manpage. Also see the Time::Local manpage and the
- Date::Parse manpage for conversion functions.
-
-BUGS
- The delete method.
-
- It doesn't properly override FS::Record yet.
-
- print_text formatting (and some logic :/) is in source as a
- format declaration, which needs to be slurped in from a file.
- the fork is rather kludgy as well. It could be cleaned with
- swrite from man perlform, and the picture could be put in a
- /var/spool/freeside/conf file. Also number of lines ($=).
-
- missing print_ps for a nice postscript copy (maybe HylaFAX-
- cover-page-style or something similar so the look can be
- completely customized?)
-
- There is an off-by-one error in print_text which causes a visual
- error: "Page 1 of 2" printed on some single-page invoices?
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_main manpage, the
- FS::cust_pay manpage, the FS::cust_bill_pkg manpage, the
- FS::cust_credit manpage, schema.html from the base
- documentation.
-
-HISTORY
- ivan@voicenet.com 97-jul-1
-
- small fix for new API ivan@sisd.com 98-mar-14
-
- charges can be negative ivan@sisd.com 98-jul-13
-
- pod, ingegrate with FS::Invoice ivan@sisd.com 98-sep-20
-
diff --git a/htdocs/docs/man/cust_bill_pkg.txt b/htdocs/docs/man/cust_bill_pkg.txt
deleted file mode 100644
index 1ca4b8c..0000000
--- a/htdocs/docs/man/cust_bill_pkg.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-NAME
- FS::cust_bill_pkg - Object methods for cust_bill_pkg records
-
-SYNOPSIS
- use FS::cust_bill_pkg;
-
- $record = create FS::cust_bill_pkg \%hash;
- $record = create FS::cust_bill_pkg { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::cust_bill_pkg object represents an invoice line item.
- FS::cust_bill_pkg inherits from FS::Record. The following fields
- are currently supported:
-
- invnum - invoice (see the FS::cust_bill manpage)
- pkgnum - package (see the FS::cust_pkg manpage)
- setup - setup fee
- recur - recurring fee
- sdate - starting date of recurring fee
- edate - ending date of recurring fee
- sdate and edate are specified as UNIX timestamps; see the
- section on "time" in the perlfunc manpage. Also see the
- Time::Local manpage and the Date::Parse manpage for conversion
- functions.
-
-METHODS
- create HASHREF
- Creates a new line item. To add the line item to the
- database, see the section on "insert". Line items are
- normally created by calling the bill method of a customer
- object (see the FS::cust_main manpage).
-
- insert
- Adds this line item to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Currently unimplemented. I don't remove line items because
- there would then be no record the items ever existed (which
- is bad, no?)
-
- replace OLD_RECORD
- Currently unimplemented. This would be even more of an
- accounting nightmare than deleteing the items. Just don't do
- it.
-
- check
- Checks all fields to make sure this is a valid line item. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert method.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_bill manpage, the
- FS::cust_pkg manpage, the FS::cust_main manpage, schema.html
- from the base documentation.
-
-HISTORY
- ivan@sisd.com 98-mar-13
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/cust_credit.txt b/htdocs/docs/man/cust_credit.txt
deleted file mode 100644
index 84591ee..0000000
--- a/htdocs/docs/man/cust_credit.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-NAME
- FS::cust_credit - Object methods for cust_credit records
-
-SYNOPSIS
- use FS::cust_credit;
-
- $record = create FS::cust_credit \%hash;
- $record = create FS::cust_credit { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::cust_credit object represents a credit. FS::cust_credit
- inherits from FS::Record. The following fields are currently
- supported:
-
- crednum - primary key (assigned automatically for new credits)
- custnum - customer (see the FS::cust_main manpage)
- amount - amount of the credit
- credited - how much of this credit that is still outstanding, which is
- amount minus all refunds (see the FS::cust_refund manpage).
- _date - specified as a UNIX timestamp; see the section on "time" in the perlfunc manpage. Also see
- the Time::Local manpage and the Date::Parse manpage for conversion functions.
- otaker - order taker (assigned automatically, see the FS::UID manpage)
- reason - text
-METHODS
- create HASHREF
- Creates a new credit. To add the credit to the database, see
- the section on "insert".
-
- insert
- Adds this credit to the database ("Posts" the credit). If
- there is an error, returns the error, otherwise returns
- false.
-
- When adding new invoices, credited must be amount (or null,
- in which case it is automatically set to amount).
-
- delete
- Currently unimplemented.
-
- replace OLD_RECORD
- Replaces the OLD_RECORD with this one in the database. If
- there is an error, returns the error, otherwise returns
- false.
-
- Only credited may be changed. Credited is normally updated
- by creating and inserting a refund (see the FS::cust_refund
- manpage).
-
- check
- Checks all fields to make sure this is a valid credit. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
-BUGS
- The delete method.
-
- It doesn't properly override FS::Record yet.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_refund manpage, the
- FS::cust_bill manpage, schema.html from the base documentation.
-
-HISTORY
- ivan@sisd.com 98-mar-17
-
- pod, otaker from FS::UID ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/cust_main.txt b/htdocs/docs/man/cust_main.txt
deleted file mode 100644
index df78487..0000000
--- a/htdocs/docs/man/cust_main.txt
+++ /dev/null
@@ -1,200 +0,0 @@
-NAME
- FS::cust_main - Object methods for cust_main records
-
-SYNOPSIS
- use FS::cust_main;
-
- $record = create FS::cust_main \%hash;
- $record = create FS::cust_main { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- @cust_pkg = $record->all_pkgs;
-
- @cust_pkg = $record->ncancelled_pkgs;
-
- $error = $record->bill;
- $error = $record->bill %options;
- $error = $record->bill 'time' => $time;
-
- $error = $record->collect;
- $error = $record->collect %options;
- $error = $record->collect 'invoice_time' => $time,
- 'batch_card' => 'yes',
- 'report_badcard' => 'yes',
- ;
-
-DESCRIPTION
- An FS::cust_main object represents a customer. FS::cust_main
- inherits from FS::Record. The following fields are currently
- supported:
-
- custnum - primary key (assigned automatically for new customers)
- agentnum - agent (see the FS::agent manpage)
- refnum - referral (see the FS::part_referral manpage)
- first - name
- last - name
- ss - social security number (optional)
- company - (optional)
- address1
- address2 - (optional)
- city
- county - (optional, see the FS::cust_main_county manpage)
- state - (see the FS::cust_main_county manpage)
- zip
- country - (see the FS::cust_main_county manpage)
- daytime - phone (optional)
- night - phone (optional)
- payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free)
- payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username)
- paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
- payname - name on card or billing name
- tax - tax exempt, empty or `Y'
- otaker - order taker (assigned automatically, see the FS::UID manpage)
-METHODS
- create HASHREF
- Creates a new customer. To add the customer to the database,
- see the section on "insert".
-
- Note that this stores the hash reference, not a distinct
- copy of the hash it points to. You can ask the object for a
- copy with the *hash* method.
-
- insert
- Adds this customer to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Currently unimplemented. Maybe cancel all of this customer's
- packages (cust_pkg)?
-
- I don't remove the customer record in the database because
- there would then be no record the customer ever existed
- (which is bad, no?)
-
- replace OLD_RECORD
- Replaces the OLD_RECORD with this one in the database. If
- there is an error, returns the error, otherwise returns
- false.
-
- check
- Checks all fields to make sure this is a valid customer
- record. If there is an error, returns the error, otherwise
- returns false. Called by the insert and repalce methods.
-
- all_pkgs
- Returns all packages (see the FS::cust_pkg manpage) for this
- customer.
-
- ncancelled_pkgs
- Returns all non-cancelled packages (see the FS::cust_pkg
- manpage) for this customer.
-
- bill OPTIONS
- Generates invoices (see the FS::cust_bill manpage) for this
- customer. Usually used in conjunction with the collect
- method.
-
- The only currently available option is `time', which bills
- the customer as if it were that time. It is specified as a
- UNIX timestamp; see the section on "time" in the perlfunc
- manpage). Also see the Time::Local manpage and the
- Date::Parse manpage for conversion functions.
-
- If there is an error, returns the error, otherwise returns
- false.
-
- collect OPTIONS
- (Attempt to) collect money for this customer's outstanding
- invoices (see the FS::cust_bill manpage). Usually used after
- the bill method.
-
- Depending on the value of `payby', this may print an invoice
- (`BILL'), charge a credit card (`CARD'), or just add any
- necessary (pseudo-)payment (`COMP').
-
- If there is an error, returns the error, otherwise returns
- false.
-
- Currently available options are:
-
- invoice_time - Use this time when deciding when to print
- invoices and late notices on those invoices. The default is
- now. It is specified as a UNIX timestamp; see the section on
- "time" in the perlfunc manpage). Also see the Time::Local
- manpage and the Date::Parse manpage for conversion
- functions.
-
- batch_card - Set this true to batch cards (see the
- cust_pay_batch manpage). By default, cards are processed
- immediately, which will generate an error if CyberCash is
- not installed.
-
- report_badcard - Set this true if you want bad card
- transactions to return an error. By default, they don't.
-
- total_owed
- Returns the total owed for this customer on all invoices
- (see the FS::cust_bill manpage).
-
- total_credited
- Returns the total credits (see the FS::cust_credit manpage)
- for this customer.
-
- balance
- Returns the balance for this customer (total owed minus
- total credited).
-
-BUGS
- The delete method.
-
- It doesn't properly override FS::Record yet.
-
- hfields should be removed.
-
- Bill and collect options should probably be passed as references
- instead of a list.
-
- CyberCash v2 forces us to define some variables in package main.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_pkg manpage, the
- FS::cust_bill manpage, the FS::cust_credit manpage the
- FS::cust_pay_batch manpage, the FS::agent manpage, the
- FS::part_referral manpage, the FS::cust_main_county manpage, the
- FS::UID manpage, schema.html from the base documentation.
-
-HISTORY
- ivan@voicenet.com 97-jul-28
-
- Changed to standard Business::CreditCard no more TableUtil
- EXPORT_OK FS::Record's hfields removed unique calls and locking
- (not needed here now) wrapped the (now) optional fields in if
- statements in sub check (notyetdone!) ivan@sisd.com 97-nov-12
-
- updated paydate with SQL-type date info ivan@sisd.com 98-mar-5
-
- Added export of datasrc from UID.pm for Pg6.3 changed 'day' to
- 'daytime' because Pg6.3 reserves the day word
- bmccane@maxbaud.net 98-apr-3
-
- in ->create, s/svc_acct/cust_main/, now it should actually
- eliminate the warnings it was meant to ivan@sisd.com 98-jul-16
-
- don't require a phone number and allow '/' in company names
- ivan@sisd.com 98-jul-18
-
- use ut_ and rewrite &check, &*_pkgs ivan@sisd.com 98-sep-5
-
- pod, merge with FS::Bill (about time!), total_owed,
- total_credited and balance methods, cleaned collect method,
- source modifications no longer necessary to enable cybercash,
- cybercash v3 support, don't need to import
- FS::UID::{datasrc,checkruid} ivan@sisd.com 98-sep-19-21
-
diff --git a/htdocs/docs/man/cust_main_county.txt b/htdocs/docs/man/cust_main_county.txt
deleted file mode 100644
index 8e99397..0000000
--- a/htdocs/docs/man/cust_main_county.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-NAME
- FS::cust_main_county - Object methods for cust_main_county
- objects
-
-SYNOPSIS
- use FS::cust_main_county;
-
- $record = create FS::cust_main_county \%hash;
- $record = create FS::cust_main_county { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::cust_main_county object represents a tax rate, defined by
- locale. FS::cust_main_county inherits from FS::Record. The
- following fields are currently supported:
-
- taxnum - primary key (assigned automatically for new tax rates)
- state
- county
- tax - percentage
-METHODS
- create HASHREF
- Creates a new tax rate. To add the tax rate to the database,
- see the section on "insert".
-
- insert
- Adds this tax rate to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Deletes this tax rate from the database. If there is an
- error, returns the error, otherwise returns false.
-
- replace OLD_RECORD
- Replaces the OLD_RECORD with this one in the database. If
- there is an error, returns the error, otherwise returns
- false.
-
- check
- Checks all fields to make sure this is a valid tax rate. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- A country field (and possibly a currency field) should be added.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_main manpage, the
- FS::cust_bill manpage, schema.html from the base documentation.
-
-HISTORY
- ivan@voicenet.com 97-dec-16
-
- Changed check for 'tax' to use the new ut_float subroutine
- bmccane@maxbaud.net 98-apr-3
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/cust_pay.txt b/htdocs/docs/man/cust_pay.txt
deleted file mode 100644
index 9f28d08..0000000
--- a/htdocs/docs/man/cust_pay.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-NAME
- FS::cust_pay - Object methods for cust_pay objects
-
-SYNOPSIS
- use FS::cust_pay;
-
- $record = create FS::cust_pay \%hash;
- $record = create FS::cust_pay { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::cust_pay object represents a payment. FS::cust_pay
- inherits from FS::Record. The following fields are currently
- supported:
-
- paynum - primary key (assigned automatically for new payments)
- invnum - Invoice (see the FS::cust_bill manpage)
- paid - Amount of this payment
- _date - specified as a UNIX timestamp; see the section on "time" in the perlfunc manpage. Also see
- the Time::Local manpage and the Date::Parse manpage for conversion functions.
- payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free)
- payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username)
- paybatch - text field for tracking card processing
-METHODS
- create HASHREF
- Creates a new payment. To add the payment to the databse,
- see the section on "insert".
-
- insert
- Adds this payment to the databse, and updates the invoice
- (see the FS::cust_bill manpage).
-
- delete
- Currently unimplemented (accounting reasons).
-
- replace OLD_RECORD
- Currently unimplemented (accounting reasons).
-
- check
- Checks all fields to make sure this is a valid payment. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert method.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- Delete and replace methods.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_bill manpage, schema.html
- from the base documentation.
-
-HISTORY
- ivan@voicenet.com 97-jul-1 - 25 - 29
-
- new api ivan@sisd.com 98-mar-13
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/cust_pkg.txt b/htdocs/docs/man/cust_pkg.txt
deleted file mode 100644
index 5409083..0000000
--- a/htdocs/docs/man/cust_pkg.txt
+++ /dev/null
@@ -1,150 +0,0 @@
-NAME
- FS::cust_pkg - Object methods for cust_pkg objects
-
-SYNOPSIS
- use FS::cust_pkg;
-
- $record = create FS::cust_pkg \%hash;
- $record = create FS::cust_pkg { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->cancel;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = FS::cust_pkg::order( $custnum, \@pkgparts );
- $error = FS::cust_pkg::order( $custnum, \@pkgparts, \@remove_pkgnums ] );
-
-DESCRIPTION
- An FS::cust_pkg object represents a customer billing item.
- FS::cust_pkg inherits from FS::Record. The following fields are
- currently supported:
-
- pkgnum - primary key (assigned automatically for new billing items)
- custnum - Customer (see the FS::cust_main manpage)
- pkgpart - Billing item definition (see the FS::part_pkg manpage)
- setup - date
- bill - date
- susp - date
- expire - date
- cancel - date
- otaker - order taker (assigned automatically if null, see the FS::UID manpage)
- Note: setup, bill, susp, expire and cancel are specified as UNIX
- timestamps; see the section on "time" in the perlfunc manpage.
- Also see the Time::Local manpage and the Date::Parse manpage for
- conversion functions.
-
-METHODS
- create HASHREF
- Create a new billing item. To add the item to the database,
- see the section on "insert".
-
- insert
- Adds this billing item to the database ("Orders" the item).
- If there is an error, returns the error, otherwise returns
- false.
-
- delete
- Currently unimplemented. You don't want to delete billing
- items, because there would then be no record the customer
- ever purchased the item. Instead, see the cancel method.
-
- sub delete { return "Can't delete cust_pkg records!"; }
-
- replace OLD_RECORD
- Replaces the OLD_RECORD with this one in the database. If
- there is an error, returns the error, otherwise returns
- false.
-
- Currently, custnum, setup, bill, susp, expire, and cancel
- may be changed.
-
- pkgpart may not be changed, but see the order subroutine.
-
- setup and bill are normally updated by calling the bill
- method of a customer object (see the FS::cust_main manpage).
-
- suspend is normally updated by the suspend and unsuspend
- methods.
-
- cancel is normally updated by the cancel method (and also
- the order subroutine in some cases).
-
- check
- Checks all fields to make sure this is a valid billing item.
- If there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
- cancel
- Cancels and removes all services (see the FS::cust_svc
- manpage and the FS::part_svc manpage) in this package, then
- cancels the package itself (sets the cancel field to now).
-
- If there is an error, returns the error, otherwise returns
- false.
-
- suspend
- Suspends all services (see the FS::cust_svc manpage and the
- FS::part_svc manpage) in this package, then suspends the
- package itself (sets the susp field to now).
-
- If there is an error, returns the error, otherwise returns
- false.
-
- unsuspend
- Unsuspends all services (see the FS::cust_svc manpage and
- the FS::part_svc manpage) in this package, then unsuspends
- the package itself (clears the susp field).
-
- If there is an error, returns the error, otherwise returns
- false.
-
-SUBROUTINES
- order CUSTNUM, PKGPARTS_ARYREF, [ REMOVE_PKGNUMS_ARYREF ]
- CUSTNUM is a customer (see the FS::cust_main manpage)
-
- PKGPARTS is a list of pkgparts specifying the the billing
- item definitions (see the FS::part_pkg manpage) to order for
- this customer. Duplicates are of course permitted.
-
- REMOVE_PKGNUMS is an optional list of pkgnums specifying the
- billing items to remove for this customer. The services (see
- the FS::cust_svc manpage) are moved to the new billing
- items. An error is returned if this is not possible (see the
- FS::pkg_svc manpage).
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- sub order is not OO. Perhaps it should be moved to FS::cust_main
- and made so?
-
- In sub order, the @pkgparts array (passed by reference) is
- clobbered.
-
- Also in sub order, no money is adjusted. Once FS::part_pkg
- defines a standard method to pass dates to the recur_prog
- expression, it should do so.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_main manpage, the
- FS::part_pkg manpage, the FS::cust_svc manpage , the FS::pkg_svc
- manpage, schema.html from the base documentation
-
-HISTORY
- ivan@voicenet.com 97-jul-1 - 21
-
- fixed for new agent->agent_type->type_pkgs in &order
- ivan@sisd.com 98-mar-7
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/cust_refund.txt b/htdocs/docs/man/cust_refund.txt
deleted file mode 100644
index 392a0b5..0000000
--- a/htdocs/docs/man/cust_refund.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-NAME
- FS::cust_refund - Object method for cust_refund objects
-
-SYNOPSIS
- use FS::cust_refund;
-
- $record = create FS::cust_refund \%hash;
- $record = create FS::cust_refund { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::cust_refund represents a refund. FS::cust_refund inherits
- from FS::Record. The following fields are currently supported:
-
- refundnum - primary key (assigned automatically for new refunds)
- crednum - Credit (see the FS::cust_credit manpage)
- refund - Amount of the refund
- _date - specified as a UNIX timestamp; see the section on "time" in the perlfunc manpage. Also see
- the Time::Local manpage and the Date::Parse manpage for conversion functions.
- payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free)
- payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username)
- otaker - order taker (assigned automatically, see the FS::UID manpage)
-METHODS
- create HASHREF
- Creates a new refund. To add the refund to the database, see
- the section on "insert".
-
- insert
- Adds this refund to the database, and updates the credit
- (see the FS::cust_credit manpage).
-
- delete
- Currently unimplemented (accounting reasons).
-
- replace OLD_RECORD
- Currently unimplemented (accounting reasons).
-
- check
- Checks all fields to make sure this is a valid refund. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert method.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- Delete and replace methods.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_credit manpage, schema.html
- from the base documentation.
-
-HISTORY
- ivan@sisd.com 98-mar-18
-
- ->create had wrong tablename ivan@sisd.com 98-jun-16 (finish
- me!)
-
- pod and finish up ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/cust_svc.txt b/htdocs/docs/man/cust_svc.txt
deleted file mode 100644
index d863ea8..0000000
--- a/htdocs/docs/man/cust_svc.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-NAME
- FS::cust_svc - Object method for cust_svc objects
-
-SYNOPSIS
- use FS::cust_svc;
-
- $record = create FS::cust_svc \%hash
- $record = create FS::cust_svc { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::cust_svc represents a service. FS::cust_svc inherits from
- FS::Record. The following fields are currently supported:
-
- svcnum - primary key (assigned automatically for new services)
- pkgnum - Package (see the FS::cust_pkg manpage)
- svcpart - Service definition (see the FS::part_svc manpage)
-METHODS
- create HASHREF
- Creates a new service. To add the refund to the database,
- see the section on "insert". Services are normally created
- by creating FS::svc_ objects (see the FS::svc_acct manpage,
- the FS::svc_domain manpage, and the FS::svc_acct_sm manpage,
- among others).
-
- insert
- Adds this service to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Deletes this service from the database. If there is an
- error, returns the error, otherwise returns false.
-
- Called by the cancel method of the package (see the
- FS::cust_pkg manpage).
-
- replace OLD_RECORD
- Replaces the OLD_RECORD with this one in the database. If
- there is an error, returns the error, otherwise returns
- false.
-
- check
- Checks all fields to make sure this is a valid service. If
- there is an error, returns the error, otehrwise returns
- false. Called by the insert and replace methods.
-
-BUGS
- Behaviour of changing the svcpart of cust_svc records is
- undefined and should possibly be prohibited, and pkg_svc records
- are not checked.
-
- pkg_svc records are not checket in general (here).
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_pkg manpage, the
- FS::part_svc manpage, the FS::pkg_svc manpage, schema.html from
- the base documentation
-
-HISTORY
- ivan@voicenet.com 97-jul-10,14
-
- no TableUtil, no FS::Lock ivan@sisd.com 98-mar-7
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/dbdef.txt b/htdocs/docs/man/dbdef.txt
deleted file mode 100644
index 6f1215a..0000000
--- a/htdocs/docs/man/dbdef.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-NAME
- FS::dbdef - Database objects
-
-SYNOPSIS
- use FS::dbdef;
-
- $dbdef = new FS::dbdef (@dbdef_table_objects);
- $dbdef = load FS::dbdef "filename";
-
- $dbdef->save("filename");
-
- $dbdef->addtable($dbdef_table_object);
-
- @table_names = $dbdef->tables;
-
- $FS_dbdef_table_object = $dbdef->table;
-
-DESCRIPTION
- FS::dbdef objects are collections of FS::dbdef_table objects and
- represnt a database (a collection of tables).
-
-METHODS
- new TABLE, TABLE, ...
- Creates a new FS::dbdef object
-
- load FILENAME
- Loads an FS::dbdef object from a file.
-
- save FILENAME
- Saves an FS::dbdef object to a file.
-
- addtable TABLE
- Adds this FS::dbdef_table object.
-
- tables
- Returns the names of all tables.
-
- table TABLENAME
- Returns the named FS::dbdef_table object.
-
-BUGS
- Each FS::dbdef object should have a name which corresponds
- to its name within the SQL database engine.
-
-SEE ALSO
- the FS::dbdef_table manpage, the FS::Record manpage,
-
-HISTORY
- beginning of abstraction into a class (not really)
-
- ivan@sisd.com 97-dec-4
-
- added primary_key ivan@sisd.com 98-jan-20
-
- added datatype (very kludgy and needs to be cleaned)
- ivan@sisd.com 98-feb-21
-
- perltrap (sigh) masked by mysql 3.20->3,21 ivan@sisd.com 98-
- mar-2
-
- Change 'type' to 'atype' in agent_type Changed attributes to
- special words which are changed in fs-setup ie. double(10,2)
- <=> MONEYTYPE Changed order of some of the field definitions
- because Pg6.3 is picky Changed 'day' to 'daytime' in
- cust_main Changed type of tax from tinyint to real Change
- 'password' to '_password' in svc_acct Pg6.3 does not allow
- 'field char(x) NULL' bmccane@maxbaud.net 98-apr-3
-
- rewrite: now properly OO. See also
- FS::dbdef_{table,column,unique,index}
-
- ivan@sisd.com 98-apr-17
-
- gained some extra functions ivan@sisd.com 98-may-11
-
- now knows how to Freeze and Thaw itself ivan@sisd.com 98-
- jun-2
-
- pod ivan@sisd.com 98-sep-23
-
diff --git a/htdocs/docs/man/dbdef_colgroup.txt b/htdocs/docs/man/dbdef_colgroup.txt
deleted file mode 100644
index a7eebc6..0000000
--- a/htdocs/docs/man/dbdef_colgroup.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-NAME
- FS::dbdef_colgroup - Column group objects
-
-SYNOPSIS
- use FS::dbdef_colgroup;
-
- $colgroup = new FS::dbdef_colgroup ( $lol );
- $colgroup = new FS::dbdef_colgroup (
- [
- [ 'single_column' ],
- [ 'multiple_columns', 'another_column', ],
- ]
- );
-
- @sql_lists = $colgroup->sql_list;
-
- @singles = $colgroup->singles;
-
-DESCRIPTION
- FS::dbdef_colgroup objects represent sets of sets of columns.
-
-METHODS
- new Creates a new FS::dbdef_colgroup object.
-
- sql_list
- Returns a flat list of comma-separated values, for SQL
- statements.
-
- singles
- Returns a flat list of all single item lists.
-
-BUGS
-SEE ALSO
- the FS::dbdef_table manpage, the FS::dbdef_unique manpage, the
- FS::dbdef_index manpage, the FS::dbdef_column manpage, the
- FS::dbdef manpage, the perldsc manpage
-
-HISTORY
- class for dealing with groups of groups of columns (used as a
- base class by FS::dbdef_{unique,index} )
-
- ivan@sisd.com 98-apr-19
-
- added singles, fixed sql_list to skip empty lists ivan@sisd.com
- 98-jun-2
-
- untaint things we're returning in sub singels ivan@sisd.com 98-
- jun-4
-
- pod ivan@sisd.com 98-sep-24
-
diff --git a/htdocs/docs/man/dbdef_column.txt b/htdocs/docs/man/dbdef_column.txt
deleted file mode 100644
index 93e2395..0000000
--- a/htdocs/docs/man/dbdef_column.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-NAME
- FS::dbdef_column - Column object
-
-SYNOPSIS
- use FS::dbdef_column;
-
- $column_object = new FS::dbdef_column ( $name, $sql_type, '' );
- $column_object = new FS::dbdef_column ( $name, $sql_type, 'NULL' );
- $column_object = new FS::dbdef_column ( $name, $sql_type, '', $length );
- $column_object = new FS::dbdef_column ( $name, $sql_type, 'NULL', $length );
-
- $name = $column_object->name;
- $column_object->name ( 'name' );
-
- $name = $column_object->type;
- $column_object->name ( 'sql_type' );
-
- $name = $column_object->null;
- $column_object->name ( 'NOT NULL' );
-
- $name = $column_object->length;
- $column_object->name ( $length );
-
- $sql_line = $column->line;
- $sql_line = $column->line $datasrc;
-
-DESCRIPTION
- FS::dbdef::column objects represend columns in tables (see the
- FS::dbdef_table manpage).
-
-METHODS
- new Creates a new FS::dbdef_column object.
-
- name
- Returns or sets the column name.
-
- type
- Returns or sets the column type.
-
- null
- Returns or sets the column null flag.
-
- type
- Returns or sets the column length.
-
- line [ $datasrc ]
- Returns an SQL column definition.
-
- If passed a DBI $datasrc specifying the DBD::mysql manpage,
- will use MySQL-specific syntax. Non-standard syntax for
- other engines (if applicable) may also be supported in the
- future.
-
-BUGS
-SEE ALSO
- the FS::dbdef_table manpage, the FS::dbdef manpage, the DBI
- manpage
-
-HISTORY
- class for dealing with column definitions
-
- ivan@sisd.com 98-apr-17
-
- now methods can be used to get or set data ivan@sisd.com 98-may-
- 11
-
- mySQL-specific hack for null (what should be default?)
- ivan@sisd.com 98-jun-2
-
diff --git a/htdocs/docs/man/dbdef_index.txt b/htdocs/docs/man/dbdef_index.txt
deleted file mode 100644
index 8cf339b..0000000
--- a/htdocs/docs/man/dbdef_index.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-NAME
- FS::dbdef_unique.pm - Index object
-
-SYNOPSIS
- use FS::dbdef_index;
-
- # see FS::dbdef_colgroup methods
-
-DESCRIPTION
- FS::dbdef_unique objects represent the (non-unique) indices of a
- table (the FS::dbdef_table manpage). FS::dbdef_unique inherits
- from FS::dbdef_colgroup.
-
-BUGS
- Is this empty subclass needed?
-
-SEE ALSO
- the FS::dbdef_colgroup manpage, the FS::dbdef_record manpage,
- the FS::Record manpage
-
-HISTORY
- class for dealing with index definitions
-
- ivan@sisd.com 98-apr-19
-
- pod ivan@sisd.com 98-sep-24
-
diff --git a/htdocs/docs/man/dbdef_table.txt b/htdocs/docs/man/dbdef_table.txt
deleted file mode 100644
index 25e010d..0000000
--- a/htdocs/docs/man/dbdef_table.txt
+++ /dev/null
@@ -1,94 +0,0 @@
-NAME
- FS::dbdef_table - Table objects
-
-SYNOPSIS
- use FS::dbdef_table;
-
- $dbdef_table = new FS::dbdef_table (
- "table_name",
- "primary_key",
- $FS_dbdef_unique_object,
- $FS_dbdef_index_object,
- @FS_dbdef_column_objects,
- );
-
- $dbdef_table->addcolumn ( $FS_dbdef_column_object );
-
- $table_name = $dbdef_table->name;
- $dbdef_table->name ("table_name");
-
- $table_name = $dbdef_table->primary_keye;
- $dbdef_table->primary_key ("primary_key");
-
- $FS_dbdef_unique_object = $dbdef_table->unique;
- $dbdef_table->unique ( $FS_dbdef_unique_object );
-
- $FS_dbdef_index_object = $dbdef_table->index;
- $dbdef_table->index ( $FS_dbdef_index_object );
-
- @column_names = $dbdef->columns;
-
- $FS_dbdef_column_object = $dbdef->column;
-
- @sql_statements = $dbdef->sql_create_table;
- @sql_statements = $dbdef->sql_create_table $datasrc;
-
-DESCRIPTION
- FS::dbdef_table objects represent a single database table.
-
-METHODS
- new Creates a new FS::dbdef_table object.
-
- addcolumn
- Adds this FS::dbdef_column object.
-
- name
- Returns or sets the table name.
-
- primary_key
- Returns or sets the primary key.
-
- unique
- Returns or sets the FS::dbdef_unique object.
-
- index
- Returns or sets the FS::dbdef_index object.
-
- columns
- Returns a list consisting of the names of all columns.
-
- column "column"
- Returns the column object (see the FS::dbdef_column manpage)
- for "column".
-
- sql_create_table [ $datasrc ]
- Returns an array of SQL statments to create this table.
-
- If passed a DBI $datasrc specifying the DBD::mysql manpage,
- will use MySQL-specific syntax. Non-standard syntax for
- other engines (if applicable) may also be supported in the
- future.
-
-BUGS
-SEE ALSO
- the FS::dbdef manpage, the FS::dbdef_unique manpage, the
- FS::dbdef_index manpage, the FS::dbdef_unique manpage, the DBI
- manpage
-
-HISTORY
- class for dealing with table definitions
-
- ivan@sisd.com 98-apr-18
-
- gained extra functions (should %columns be an IxHash?)
- ivan@sisd.com 98-may-11
-
- sql_create_table returns a list of statments, not just one, and
- now it does indices (plus mysql hack) ivan@sisd.com 98-jun-2
-
- untaint primary_key... hmm. is this a hack around a bigger
- problem? looks like, did the same thing singles in colgroup!
- ivan@sisd.com 98-jun-4
-
- pod ivan@sisd.com 98-sep-24
-
diff --git a/htdocs/docs/man/dbdef_unique.txt b/htdocs/docs/man/dbdef_unique.txt
deleted file mode 100644
index 0e4f015..0000000
--- a/htdocs/docs/man/dbdef_unique.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-NAME
- FS::dbdef_unique.pm - Unique object
-
-SYNOPSIS
- use FS::dbdef_unique;
-
- # see FS::dbdef_colgroup methods
-
-DESCRIPTION
- FS::dbdef_unique objects represent the unique indices of a
- database table (the FS::dbdef_table manpage). FS::dbdef_unique
- inherits from FS::dbdef_colgroup.
-
-BUGS
- Is this empty subclass needed?
-
-SEE ALSO
- the FS::dbdef_colgroup manpage, the FS::dbdef_record manpage,
- the FS::Record manpage
-
-HISTORY
- class for dealing with unique definitions
-
- ivan@sisd.com 98-apr-19
-
- pod ivan@sisd.com 98-sep-24
-
diff --git a/htdocs/docs/man/index.html b/htdocs/docs/man/index.html
deleted file mode 100644
index 4f33dd4..0000000
--- a/htdocs/docs/man/index.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<head>
- <title>Perl API</title>
-</head>
-<body>
- <h1>Perl API</h1>
- <ul>
-<li><a href="agent.txt">FS::agent</a>
-<li><a href="agent_type.txt">FS::agent_type</a>
-<li><a href="cust_bill.txt">FS::cust_bill</a>
-<li><a href="cust_bill_pkg.txt">FS::cust_bill_pkg</a>
-<li><a href="cust_credit.txt">FS::cust_credit</a>
-<li><a href="cust_main.txt">FS::cust_main</a>
-<li><a href="cust_main_county.txt">FS::cust_main_county</a>
-<li><a href="cust_pay.txt">FS::cust_pay</a>
-<li><a href="cust_pkg.txt">FS::cust_pkg</a>
-<li><a href="cust_refund.txt">FS::cust_refund</a>
-<li><a href="cust_svc.txt">FS::cust_svc</a>
-<li><a href="part_pkg.txt">FS::part_pkg</a>
-<li><a href="part_referral.txt">FS::part_referral</a>
-<li><a href="part_svc.txt">FS::part_svc</a>
-<li><a href="pkg_svc.txt">FS::pkg_svc</a>
-<li><a href="svc_acct.txt">FS::svc_acct</a>
-<li><a href="svc_acct_pop.txt">FS::svc_acct_pop</a>
-<li><a href="svc_acct_sm.txt">FS::svc_acct_sm</a>
-<li><a href="svc_domain.txt">FS::svc_domain</a>
-<li><a href="type_pkgs.txt">FS::type_pkgs</a>
-</ul>
-<br>
-<ul>
-<li><a href="Bill.txt">FS::Bill</a>
-<li><a href="CGI.txt">FS::CGI</a>
-<li><a href="Conf.txt">FS::Conf</a>
-<li><a href="Invoice.txt">FS::Invoice</a>
-<li><a href="Record.txt">FS::Record</a>
-<li><a href="SSH.txt">FS::SSH</a>
-<li><a href="UID.txt">FS::UID</a>
-</ul>
-<br>
-<ul>
-<li><a href="dbdef.txt">FS::dbdef</a>
-<li><a href="dbdef_colgroup.txt">FS::dbdef_colgroup</a>
-<li><a href="dbdef_column.txt">FS::dbdef_column</a>
-<li><a href="dbdef_index.txt">FS::dbdef_index</a>
-<li><a href="dbdef_table.txt">FS::dbdef_table</a>
-<li><a href="dbdef_unique.txt">FS::dbdef_unique</a>
-
-<ul>
-</body>
diff --git a/htdocs/docs/man/part_pkg.txt b/htdocs/docs/man/part_pkg.txt
deleted file mode 100644
index dc1bce4..0000000
--- a/htdocs/docs/man/part_pkg.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-NAME
- FS::part_pkg - Object methods for part_pkg objects
-
-SYNOPSIS
- use FS::part_pkg;
-
- $record = create FS::part_pkg \%hash
- $record = create FS::part_pkg { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::part_pkg represents a billing item definition.
- FS::part_pkg inherits from FS::Record. The following fields are
- currently supported:
-
- pkgpart - primary key (assigned automatically for new billing item definitions)
- pkg - Text name of this billing item definition (customer-viewable)
- comment - Text name of this billing item definition (non-customer-viewable)
- setup - Setup fee
- freq - Frequency of recurring fee
- recur - Recurring fee
- setup and recur are evaluated as Safe perl expressions. You can
- use numbers just as you would normally. More advanced semantics
- are not yet defined.
-
-METHODS
- create HASHREF
- Creates a new billing item definition. To add the billing
- item definition to the database, see the section on
- "insert".
-
- insert
- Adds this billing item definition to the database. If there
- is an error, returns the error, otherwise returns false.
-
- delete
- Currently unimplemented.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid billing item
- definition. If there is an error, returns the error,
- otherwise returns false. Called by the insert and replace
- methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- The delete method is unimplemented.
-
- setup and recur semantics are not yet defined (and are
- implemented in FS::cust_bill. hmm.).
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_pkg manpage, the
- FS::type_pkgs manpage, the FS::pkg_svc manpage, the Safe
- manpage. schema.html from the base documentation.
-
-HISTORY
- ivan@sisd.com 97-dec-5
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/part_referral.txt b/htdocs/docs/man/part_referral.txt
deleted file mode 100644
index 5349963..0000000
--- a/htdocs/docs/man/part_referral.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-NAME
- FS::part_referral - Object methods for part_referral objects
-
-SYNOPSIS
- use FS::part_referral;
-
- $record = create FS::part_referral \%hash
- $record = create FS::part_referral { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::part_referral represents a referral - where a customer
- heard of your services. This can be used to track the
- effectiveness of a particular piece of advertising, for example.
- FS::part_referral inherits from FS::Record. The following fields
- are currently supported:
-
- refnum - primary key (assigned automatically for new referrals)
- referral - Text name of this referral
-METHODS
- create HASHREF
- Creates a new referral. To add the referral to the database,
- see the section on "insert".
-
- insert
- Adds this referral to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Currently unimplemented.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid referral. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- The delete method is unimplemented.
-
-SEE ALSO
- the FS::Record manpage, the FS::cust_main manpage, schema.html
- from the base documentation.
-
-HISTORY
- Class dealing with referrals
-
- ivan@sisd.com 98-feb-23
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/part_svc.txt b/htdocs/docs/man/part_svc.txt
deleted file mode 100644
index 680944e..0000000
--- a/htdocs/docs/man/part_svc.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-NAME
- FS::part_svc - Object methods for part_svc objects
-
-SYNOPSIS
- use FS::part_svc;
-
- $record = create FS::part_referral \%hash
- $record = create FS::part_referral { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::part_svc represents a service definition. FS::part_svc
- inherits from FS::Record. The following fields are currently
- supported:
-
- svcpart - primary key (assigned automatically for new service definitions)
- svc - text name of this service definition
- svcdb - table used for this service. See the FS::svc_acct manpage,
- the FS::svc_domain manpage, and the FS::svc_acct_sm manpage, among others.
- *svcdb*__*field* - Default or fixed value for *field* in *svcdb*.
- *svcdb*__*field*_flag - defines *svcdb*__*field* action: null, `D' for default, or `F' for fixed
-METHODS
- create HASHREF
- Creates a new service definition. To add the service
- definition to the database, see the section on "insert".
-
- insert
- Adds this service definition to the database. If there is an
- error, returns the error, otherwise returns false.
-
- delete
- Currently unimplemented.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid service
- definition. If there is an error, returns the error,
- otherwise returns false. Called by the insert and replace
- methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- Delete is unimplemented.
-
-SEE ALSO
- the FS::Record manpage, the FS::part_pkg manpage, the
- FS::pkg_svc manpage, the FS::cust_svc manpage, the FS::svc_acct
- manpage, the FS::svc_acct_sm manpage, the FS::svc_domain
- manpage, schema.html from the base documentation.
-
-HISTORY
- ivan@sisd.com 97-nov-14
-
- data checking/untainting calls into FS::Record added
- ivan@sisd.com 97-dec-6
-
- pod ivan@sisd.com 98-sep-21
-
diff --git a/htdocs/docs/man/pkg_svc.txt b/htdocs/docs/man/pkg_svc.txt
deleted file mode 100644
index bde0043..0000000
--- a/htdocs/docs/man/pkg_svc.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-NAME
- FS::pkg_svc - Object methods for pkg_svc records
-
-SYNOPSIS
- use FS::pkg_svc;
-
- $record = create FS::pkg_svc \%hash;
- $record = create FS::pkg_svc { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::pkg_svc record links a billing item definition (see the
- FS::part_pkg manpage) to a service definition (see the
- FS::part_svc manpage). FS::pkg_svc inherits from FS::Record. The
- following fields are currently supported:
-
- pkgpart - Billing item definition (see the FS::part_pkg manpage)
- svcpart - Service definition (see the FS::part_svc manpage)
- quantity - Quantity of this service definition that this billing item
- definition includes
-METHODS
- create HASHREF
- Create a new record. To add the record to the database, see
- the section on "insert".
-
- insert
- Adds this record to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Deletes this record from the database. If there is an error,
- returns the error, otherwise returns false.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid record. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
-SEE ALSO
- the FS::Record manpage, the FS::part_pkg manpage, the
- FS::part_svc manpage, schema.html from the base documentation.
-
-HISTORY
- ivan@voicenet.com 97-jul-1 added hfields ivan@sisd.com 97-nov-13
-
- pod ivan@sisd.com 98-sep-22
-
diff --git a/htdocs/docs/man/svc_acct.txt b/htdocs/docs/man/svc_acct.txt
deleted file mode 100644
index 1c9caf5..0000000
--- a/htdocs/docs/man/svc_acct.txt
+++ /dev/null
@@ -1,168 +0,0 @@
-NAME
- FS::svc_acct - Object methods for svc_acct records
-
-SYNOPSIS
- use FS::svc_acct;
-
- $record = create FS::svc_acct \%hash;
- $record = create FS::svc_acct { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-DESCRIPTION
- An FS::svc_acct object represents an account. FS::svc_acct
- inherits from FS::Record. The following fields are currently
- supported:
-
- svcnum - primary key (assigned automatcially for new accounts)
- username
- _password - generated if blank
- popnum - Point of presence (see the FS::svc_acct_pop manpage)
- uid
- gid
- finger - GECOS
- dir - set automatically if blank (and uid is not)
- shell
- quota - (unimplementd)
- slipip - IP address
- radius_*Radius_Attribute* - *Radius-Attribute*
-METHODS
- create HASHREF
- Creates a new account. To add the account to the database,
- see the section on "insert".
-
- insert
- Adds this account to the database. If there is an error,
- returns the error, otherwise returns false.
-
- The additional fields pkgnum and svcpart (see the
- FS::cust_svc manpage) should be defined. An FS::cust_svc
- record will be created and inserted.
-
- If the configuration value (see the FS::Conf manpage)
- shellmachine exists, and the username, uid, and dir fields
- are defined, the command
-
- useradd -d $dir -m -s $shell -u $uid $username
-
- is executed on shellmachine via ssh. This behaviour can be
- surpressed by setting $FS::svc_acct::nossh_hack true.
-
- delete
- Deletes this account from the database. If there is an
- error, returns the error, otherwise returns false.
-
- The corresponding FS::cust_svc record will be deleted as
- well.
-
- If the configuration value (see the FS::Conf manpage)
- shellmachine exists, the command:
-
- userdel $username
-
- is executed on shellmachine via ssh. This behaviour can be
- surpressed by setting $FS::svc_acct::nossh_hack true.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- If the configuration value (see the FS::Conf manpage)
- shellmachine exists, and the dir field has changed, the
- command:
-
- [ -d $old_dir ] && (
- chmod u+t $old_dir;
- umask 022;
- mkdir $new_dir;
- cd $old_dir;
- find . -depth -print | cpio -pdm $new_dir;
- chmod u-t $new_dir;
- chown -R $uid.$gid $new_dir;
- rm -rf $old_dir
- )
-
- is executed on shellmachine via ssh. This behaviour can be
- surpressed by setting $FS::svc_acct::nossh_hack true.
-
- suspend
- Suspends this account by prefixing *SUSPENDED* to the
- password. If there is an error, returns the error, otherwise
- returns false.
-
- Called by the suspend method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- unsuspend
- Unsuspends this account by removing *SUSPENDED* from the
- password. If there is an error, returns the error, otherwise
- returns false.
-
- Called by the unsuspend method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- cancel
- Just returns false (no error) for now.
-
- Called by the cancel method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- check
- Checks all fields to make sure this is a valid service. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
- Sets any fixed values; see the FS::part_svc manpage.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- The remote commands should be configurable.
-
- The create method should set defaults from part_svc (like the
- check method sets fixed values).
-
-SEE ALSO
- the FS::Record manpage, the FS::Conf manpage, the FS::cust_svc
- manpage, the FS::part_svc manpage, the FS::cust_pkg manpage, the
- FS::SSH manpage, the ssh manpage, the FS::svc_acct_pop manpage,
- schema.html from the base documentation.
-
-HISTORY
- ivan@voicenet.com 97-jul-16 - 21
-
- rewrite (among other things, now know about part_svc)
- ivan@sisd.com 98-mar-8
-
- Changed 'password' to '_password' because Pg6.3 reserves the
- password word bmccane@maxbaud.net 98-apr-3
-
- username length and shell no longer hardcoded ivan@sisd.com 98-
- jun-28
-
- eww but needed: ignore uid duplicates for 'fax' and 'hylafax'
- ivan@sisd.com 98-jun-29
-
- $nossh_hack ivan@sisd.com 98-jul-13
-
- protections against UID/GID of 0 for incorrectly-setup RDBMSs
- (also in bin/svc_acct.export) ivan@sisd.com 98-jul-13
-
- arbitrary radius attributes ivan@sisd.com 98-aug-13
-
- /var/spool/freeside/conf/shellmachine ivan@sisd.com 98-aug-13
-
- pod and FS::conf ivan@sisd.com 98-sep-22
-
diff --git a/htdocs/docs/man/svc_acct_pop.txt b/htdocs/docs/man/svc_acct_pop.txt
deleted file mode 100644
index ac09654..0000000
--- a/htdocs/docs/man/svc_acct_pop.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-NAME
- FS::svc_acct_pop - Object methods for svc_acct_pop records
-
-SYNOPSIS
- use FS::svc_acct_pop;
-
- $record = create FS::svc_acct_pop \%hash;
- $record = create FS::svc_acct_pop { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::svc_acct object represents an point of presence.
- FS::svc_acct_pop inherits from FS::Record. The following fields
- are currently supported:
-
- popnum - primary key (assigned automatically for new accounts)
- city
- state
- ac - area code
- exch - exchange
-METHODS
- create HASHREF
- Creates a new point of presence (if only it were that
- easy!). To add the point of presence to the database, see
- the section on "insert".
-
- insert
- Adds this point of presence to the databaes. If there is an
- error, returns the error, otherwise returns false.
-
- delete
- Currently unimplemented.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid point of
- presence. If there is an error, returns the error, otherwise
- returns false. Called by the insert and replace methods.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- It should be renamed to part_pop.
-
-SEE ALSO
- the FS::Record manpage, the svc_acct manpage, schema.html from
- the base documentation.
-
-HISTORY
- Class dealing with pops
-
- ivan@sisd.com 98-mar-8
-
- pod ivan@sisd.com 98-sep-23
-
diff --git a/htdocs/docs/man/svc_acct_sm.txt b/htdocs/docs/man/svc_acct_sm.txt
deleted file mode 100644
index e9940af..0000000
--- a/htdocs/docs/man/svc_acct_sm.txt
+++ /dev/null
@@ -1,121 +0,0 @@
-NAME
- FS::svc_acct_sm - Object methods for svc_acct_sm records
-
-SYNOPSIS
- use FS::svc_acct_sm;
-
- $record = create FS::svc_acct_sm \%hash;
- $record = create FS::svc_acct_sm { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-DESCRIPTION
- An FS::svc_acct object represents a virtual mail alias.
- FS::svc_acct inherits from FS::Record. The following fields are
- currently supported:
-
- svcnum - primary key (assigned automatcially for new accounts)
- domsvc - svcnum of the virtual domain (see the FS::svc_domain manpage)
- domuid - uid of the target account (see the FS::svc_acct manpage)
- domuser - virtual username
-METHODS
- create HASHREF
- Creates a new virtual mail alias. To add the virtual mail
- alias to the database, see the section on "insert".
-
- insert
- Adds this virtual mail alias to the database. If there is an
- error, returns the error, otherwise returns false.
-
- The additional fields pkgnum and svcpart (see the
- FS::cust_svc manpage) should be defined. An FS::cust_svc
- record will be created and inserted.
-
- If the configuration values (see the FS::Conf manpage)
- shellmachine and qmailmachines exist, and domuser is `*'
- (meaning a catch-all mailbox), the command:
-
- [ -e $dir/.qmail-$qdomain-default ] || {
- touch $dir/.qmail-$qdomain-default;
- chown $uid:$gid $dir/.qmail-$qdomain-default;
- }
-
- is executed on shellmachine via ssh (see the section on
- "EXTENSION ADDRESSES" in the dot-qmail manpage). This
- behaviour can be surpressed by setting
- $FS::svc_acct_sm::nossh_hack true.
-
- delete
- Deletes this virtual mail alias from the database. If there
- is an error, returns the error, otherwise returns false.
-
- The corresponding FS::cust_svc record will be deleted as
- well.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- suspend
- Just returns false (no error) for now.
-
- Called by the suspend method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- unsuspend
- Just returns false (no error) for now.
-
- Called by the unsuspend method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- cancel
- Just returns false (no error) for now.
-
- Called by the cancel method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- check
- Checks all fields to make sure this is a valid virtual mail
- alias. If there is an error, returns the error, otherwise
- returns false. Called by the insert and replace methods.
-
- Sets any fixed values; see the FS::part_svc manpage.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- The remote commands should be configurable.
-
-SEE ALSO
- the FS::Record manpage, the FS::Conf manpage, the FS::cust_svc
- manpage, the FS::part_svc manpage, the FS::cust_pkg manpage, the
- FS::svc_acct manpage, the FS::svc_domain manpage, the FS::SSH
- manpage, the ssh manpage, the dot-qmail manpage, schema.html
- from the base documentation.
-
-HISTORY
- ivan@voicenet.com 97-jul-16 - 21
-
- rewrite ivan@sisd.com 98-mar-10
-
- s/qsearchs/qsearch/ to eliminate warning ivan@sisd.com 98-apr-19
-
- uses conf/shellmachine and has an nossh_hack ivan@sisd.com 98-
- jul-14
-
- s/\./:/g in .qmail-domain:com ivan@sisd.com 98-aug-13
-
- pod, FS::Conf, moved .qmail file from check to insert 98-sep-23
-
diff --git a/htdocs/docs/man/svc_domain.txt b/htdocs/docs/man/svc_domain.txt
deleted file mode 100644
index 03d3dbc..0000000
--- a/htdocs/docs/man/svc_domain.txt
+++ /dev/null
@@ -1,131 +0,0 @@
-NAME
- FS::svc_domain - Object methods for svc_domain records
-
-SYNOPSIS
- use FS::svc_domain;
-
- $record = create FS::svc_domain \%hash;
- $record = create FS::svc_domain { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-DESCRIPTION
- An FS::svc_domain object represents a domain. FS::svc_domain
- inherits from FS::Record. The following fields are currently
- supported:
-
- svcnum - primary key (assigned automatically for new accounts)
- domain
-METHODS
- create HASHREF
- Creates a new domain. To add the domain to the database, see
- the section on "insert".
-
- insert
- Adds this domain to the database. If there is an error,
- returns the error, otherwise returns false.
-
- The additional fields *pkgnum* and *svcpart* (see the
- FS::cust_svc manpage) should be defined. An FS::cust_svc
- record will be created and inserted.
-
- The additional field *action* should be set to *N* for new
- domains or *M* for transfers.
-
- A registration or transfer email will be submitted unless
- $FS::svc_domain::whois_hack is true.
-
- delete
- Deletes this domain from the database. If there is an error,
- returns the error, otherwise returns false.
-
- The corresponding FS::cust_svc record will be deleted as
- well.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- suspend
- Just returns false (no error) for now.
-
- Called by the suspend method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- unsuspend
- Just returns false (no error) for now.
-
- Called by the unsuspend method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- cancel
- Just returns false (no error) for now.
-
- Called by the cancel method of FS::cust_pkg (see the
- FS::cust_pkg manpage).
-
- check
- Checks all fields to make sure this is a valid domain. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
- Sets any fixed values; see the FS::part_svc manpage.
-
- _whois
- Executes the command:
-
- whois do $domain
-
- and returns the output.
-
- (Always returns *No match for domian "$domain".* if
- $FS::svc_domain::whois_hack is set true.)
-
- submit_internic
- Submits a registration email for this domain.
-
-BUGS
- It doesn't properly override FS::Record yet.
-
- All BIND/DNS fields should be included (and exported).
-
- All registries should be supported.
-
- Not all configuration access is through FS::Conf!
-
- Should change action to a real field.
-
-SEE ALSO
- the FS::Record manpage, the FS::Conf manpage, the FS::cust_svc
- manpage, the FS::part_svc manpage, the FS::cust_pkg manpage, the
- FS::SSH manpage, the ssh manpage, the dot-qmail manpage,
- schema.html from the base documentation, config.html from the
- base documentation.
-
-HISTORY
- ivan@voicenet.com 97-jul-21
-
- rewrite ivan@sisd.com 98-mar-10
-
- add internic bits ivan@sisd.com 98-mar-14
-
- Changed 'day' to 'daytime' because Pg6.3 reserves the day word
- bmccane@maxbaud.net 98-apr-3
-
- /var/spool/freeside/conf/registries/internic/, Mail::Internet,
- etc. ivan@sisd.com 98-jul-17-19
-
- pod, some FS::Conf (not complete) ivan@sisd.com 98-sep-23
-
diff --git a/htdocs/docs/man/type_pkgs.txt b/htdocs/docs/man/type_pkgs.txt
deleted file mode 100644
index 9822b48..0000000
--- a/htdocs/docs/man/type_pkgs.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-NAME
- FS::type_pkgs - Object methods for type_pkgs records
-
-SYNOPSIS
- use FS::type_pkgs;
-
- $record = create FS::type_pkgs \%hash;
- $record = create FS::type_pkgs { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-DESCRIPTION
- An FS::type_pkgs record links an agent type (see the
- FS::agent_type manpage) to a billing item definition (see the
- FS::part_pkg manpage). FS::type_pkgs inherits from FS::Record.
- The following fields are currently supported:
-
- typenum - Agent type, see the FS::agent_type manpage
- pkgpart - Billing item definition, see the FS::part_pkg manpage
-METHODS
- create HASHREF
- Create a new record. To add the record to the database, see
- the section on "insert".
-
- insert
- Adds this record to the database. If there is an error,
- returns the error, otherwise returns false.
-
- delete
- Deletes this record from the database. If there is an error,
- returns the error, otherwise returns false.
-
- replace OLD_RECORD
- Replaces OLD_RECORD with this one in the database. If there
- is an error, returns the error, otherwise returns false.
-
- check
- Checks all fields to make sure this is a valid record. If
- there is an error, returns the error, otherwise returns
- false. Called by the insert and replace methods.
-
-HISTORY
- Defines the relation between agent types and pkgparts (Which
- pkgparts can the different [types of] agents sell?)
-
- ivan@sisd.com 97-nov-13
-
- change to ut_ FS::Record, fixed bugs ivan@sisd.com 97-dec-10
-
diff --git a/htdocs/docs/passwd.html b/htdocs/docs/passwd.html
deleted file mode 100644
index a8f8151..0000000
--- a/htdocs/docs/passwd.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<head>
- <title>fs_passwd</title>
-</head>
-<body>
- <h1>fs_passwd</h1>
-You may use fs_passwd/fs_passwd as a "passwd", "chfn" and "chsh" replacement on your shell machine(s) to cause password, gecos and shell changes to update your freeside machine. This can pose a security risk if not configured correctly. <b>Do not use this feature unless you understand what you are doing!</b>
-<br><br>Currently it is assumed that the the crypt(3) function in the C library is the same on the Freeside machine as on the target machine.
-<ul>
- <li>Create a freeside account on the shell machine(s).
- <li>Append the identity.pub from the freeside user on your freeside machine to the authorized_keys file of the newly created freeside user on the shell machine(s).
- <li>Copy fs_passwd/fs_passwd to /usr/local/bin on the shell machine(s). (chown freeside, chmod 4755). You may link it to passwd, chfn and chsh as well.
- <li>Copy fs_passwd/fs_passwdd to /usr/local/sbin on the shell machine(s). (chown freeside, chmod 500)
- <li>Create /usr/local/freeside on the shell machine(s). (chown freeside, chmod 700)
- <li>Run an iteration of "fs_passwd/fs_passwd_server shell.machine" as the freeside user for each shell machine (this is a daemon process).
-</ul>
-</body>
diff --git a/htdocs/docs/schema.html b/htdocs/docs/schema.html
deleted file mode 100644
index 5a296ec..0000000
--- a/htdocs/docs/schema.html
+++ /dev/null
@@ -1,205 +0,0 @@
-<head>
- <title>Schema reference</title>
-</head>
-<body>
- <h1>Schema reference</h1>
- <ul>
- <li><a name="agent">agent</a> - Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their agent type).
- <ul>
- <li>agentnum - primary key
- <li>agent - name of this agent
- <li>typenum - <a href="#agent_type">agent type</a>
- <li>prog - (unimplemented)
- <li>freq - (unimplemented)
- </ul>
- <li><a name="agent_type">agent_type</a> - Agent types define groups of packages that you can then assign to particular agents.
- <ul>
- <li>typenum - primary key
- <li>atype - name of this agent type
- </ul>
- <li><a name="cust_bill">cust_bill</a> - Invoices
- <ul>
- <li>invnum - primary key
- <li>custnum - <a href="#cust_main">customer</a>
- <li>_date
- <li>charged - amount of this invoice
- <li>owed - amount still outstanding on this invoice
- <li>printed - how many times this invoice has been printed automatically
- </ul>
- <li><a name="cust_bill_pkg">cust_bill_pkg</a> - Invoice line items
- <ul>
- <li>invnum - (multiple) key
- <li>pkgnum - <a href="#cust_pkg">package</a>
- <li>setup - setup fee
- <li>recur - recurring fee
- <li>sdate - starting date
- <li>edate - ending date
- </ul>
- <li><a name="cust_credit">cust_credit</a> - Credits
- <ul>
- <li>crednum - primary key
- <li>custnum - <a href="#cust_main">customer</a>
- <li>amount - amount credited
- <li>credited - amount still outstanding (not yet refunded) on this credit
- <li>_date
- <li>otaker - order taker
- <li>reason
- </ul>
- <li><a name="cust_main">cust_main</a> - Customers
- <ul>
- <li>custnum - primary key
- <li>agentnum - <a href="#agent">agent</a>
- <li>refnum - <a href="#part_referral">referral</a>
- <li>first - name
- <li>last - name
- <li>ss - social security number
- <li>company
- <li>address1
- <li>address2
- <li>city
- <li>county
- <li>state
- <li>zip
- <li>country
- <li>daytime - phone
- <li>night - phone
- <li>payby - CARD, BILL, or COMP
- <li>payinfo - card number, P.O.#, or comp issuer
- <li>paydate - expiration date
- <li>payname - billing name (name on card)
- <li>tax - tax exempt, Y or null
- <li>otaker - order taker
- </ul>
- <li><a name="cust_main_county">cust_main_county</a> - Tax rates
- <ul>
- <li>taxnum - primary key
- <li>state
- <li>county
- <li>tax - % rate
- </ul>
- <li><a name="cust_pay">cust_pay</a> - Payments
- <ul>
- <li>paynum - primary key
- <li>invnum - <a href="#cust_bill">invoice</a>
- <li>paid - amount
- <li>_date
- <li>payby - CARD, BILL, or COMP
- <li>payinfo - card number, P.O.#, or comp issuer
- <li>paybatch - text field for tracking card processor batches
- </ul>
- <li><a name="cust_pay_batch">cust_pay_batch</a> - Pending batch
- <ul>
- <li>trancode - 77 for charges
- <li>cardnum
- <li>exp - card expiration
- <li>amount
- <li>invnum - <a href="#cust_bill">invoice</a>
- <li>custnum - <a href="#cust_main">customer</a>
- <li>payname - name on card
- <li>first - name
- <li>last - name
- <li>address1
- <li>address2
- <li>city
- <li>state
- <li>zip
- <li>country
- </ul>
- <li><a name="cust_pkg">cust_pkg</a> - Customer billing items
- <ul>
- <li>pkgnum - primary key
- <li>custnum - <a href="#cust_main">customer</a>
- <li>pkgpart - <a href="#part_pkg">Package definition</a>
- <li>setup - date
- <li>bill - next bill date
- <li>susp - (past) suspension date
- <li>expire - (future) cancellation date
- <li>cancel - (past) cancellation date
- <li>otaker - order taker
- </ul>
- <li><a name="cust_refund">cust_refund</a> - Refunds
- <ul>
- <li>refundnum - primary key
- <li>crednum - <a href="#cust_credit">credit</a>
- <li>refund - amount
- <li>_date
- <li>payby - CARD, BILL or COMP
- <li>payinfo - card number, P.O.#, or comp issuer
- <li>otaker - order taker
- </ul>
- <li><a name="cust_svc">cust_svc</a> - Customer services
- <ul>
- <li>svcnum - primary key
- <li>pkgnum - <a href="#cust_pkg">package</a>
- <li>svcpart - <a href="#part_svc">Service definition</a>
- </ul>
- <li><a name="part_pkg">part_pkg</a> - Package definitions
- <ul>
- <li>pkgpart - primary key
- <li>pkg - package name
- <li>comment - non-customer visable package comment
- <li>setup - setup fee
- <li>freq - recurring frequency (months)
- <li>recur - recurring fee
- </ul>
- <li><a name="part_referral">part_referral</a> - Referral listing
- <ul>
- <li>refnum</li> - primary key
- <li>referral</li> - referral
- </ul>
- <li><a name="part_svc">part_svc</a> - Service definitions
- <ul>
- <li>svcpart - primary key
- <li>svc - name of this service
- <li>svcdb - table used for this service: svc_acct, svc_acct_sm, svc_domain, svc_charge or svc_wo
- <li><i>table</i>__<i>field</i> - Default or fixed value for <i>field</i> in <i>table</i>
- <li><i>table</i>__<i>field</i>_flag - null, D or F
- </ul>
- <li><a name="pkg_svc">pkg_svc</a>
- <ul>
- <li>pkgpart - <a href="#part_pkg">Package definition</a>
- <li>svcpart - <a href="#part_svc">Service definition</a>
- <li>quantity - quantity of this service that this package includes
- </ul>
- <li><a name="svc_acct">svc_acct</a> - Accounts
- <ul>
- <li>svcnum - <a href="#cust_svc">primary key</a>
- <li>username
- <li>_password
- <li>popnum - <a href="#svc_acct_pop">Point of Presence</a>
- <li>uid
- <li>gid
- <li>finger - GECOS
- <li>dir
- <li>shell
- <li>quota - (unimplementd)
- <li>slipip - IP address
- <li>radius_<i>Radius_Attribute</i> - Radius-Attribute
- </ul>
- <li><a name="svc_acct_pop">svc_acct_pop</a> - Points of Presence
- <ul>
- <li>popnum - primary key
- <li>city
- <li>state
- <li>ac - area code
- <li>exch - exchange
- </ul>
- <li><a name="svc_acct_sm">svc_acct_sm</a> - Domain mail aliases
- <ul>
- <li>svcnum - <a href="#cust_svc">primary key</a>
- <li>domsvc - <a href="#svc_domain">Domain</a> (by svcnum)
- <li>domuid - <a href="#svc_acct">Account</a> (by uid)
- <li>domuser - domuser @ <a href="#svc_domain">Domain</a> forwards to <a href="#svc_acct">Account</a>
- </ul>
- <li><a name="svc_domain">svc_domain</a> - Domains
- <ul>
- <li>svcnum - <a href="#cust_svc">primary key</a>
- <li>domain
- </ul>
- <li><a name="type_pkgs">type_pkgs</a>
- <ul>
- <li>typenum - <a href="#agent_type">agent type</a>
- <li>pkgpart - <a href="#part_pkg">Package definition</a>
- </ul>
- </ul>
-</body>
diff --git a/htdocs/docs/trouble.html b/htdocs/docs/trouble.html
deleted file mode 100644
index 2cf6d4e..0000000
--- a/htdocs/docs/trouble.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<head>
- <title>Troubleshooting</title>
-</head>
-<body>
- <h1>Troubleshooting</h1>
- <ul>
- <li>When troubleshooting the web interface, helpful information is often in your web server's error log.
- <li>Internet Explorer will not work with Freeside's HTML interface.
-<a HREF="http://www.netscape.com">Netscape</a>,
-<a HREF="http://lynx.browser.org">Lynx</a>, and
-<a HREF="http://www.cs.indiana.edu/elisp/w3/docs.html">Emacs/W3</a>,
-among others, should work fine.
- <li>If bin/svc_acct.import fails with an "Out of memory!" error using MySQL, upgrede MySQL and recompile the Perl DBD. There was a memory leak in some older versions of MySQL.
- <li>If you get tons of errors in your web server's error log like this:
-<pre>
-Ambiguous use of value => resolved to "value" =>
-at /usr/lib/perl5/site_perl/File/CounterFile.pm line 132.
-</pre>
- This clutters up your log files but is otherwise harmless. Upgrade to the latest File::CounterFile.
- <li>If you get an Internal Server Error when adding or editing, but find that the update has occured, and you get something like the following in your web server's error log:
-<pre>
-access to <i>/your/path</i>/edit/process/<i>some_table</i>.cgi failed for
-<i>machine.domain.tld</i>, reason: malformed header from script.
-Bad header=HTTP/1.0 302 Moved Temporarily
-</pre>
- Then you forgot to apply this <a href="CGI-modules-2.76-patch.txt">patch</a> as mentioned in the <a href="install.html">New Installation</a> section of the documentation.
- <li>If you get errors like this:
-<pre>
-UID.pm: Can't open /var/spool/freeside/conf/secrets: Permission denied
-at <i>/your/path</i>/site_perl/FS/UID.pm line 26.
-BEGIN failed--compilation aborted at
-<i>/your/path</i>/edit/process/part_svc.cgi line 15.
-</pre>
- Then the scripts are not running setuid freeside. If you were editing
-the files, it is possible you inadvertantly removed the setuid bit.
-As mentioned in the <a href="install.html">New Installation</a> section of the documentation, set ownership and permissions for the web interface. Your system should support secure setuid scripts or Perl's emulation, see <a href="http://www.perl.com/CPAN-local/doc/manual/html/pod/perlsec.html#Security_Bugs">perlsec: Security Bugs</a> for information and workarounds.
-<pre>cd /usr/local/apache/htdocs/freeside
-chown -R freeside .
-chmod 4755 browse/*.cgi edit/*.cgi edit/process/*.cgi misc/*.cgi misc/process/*.cgi search/*.cgi view/*.cgi</pre>
- </ul>
-</body>
diff --git a/htdocs/docs/upgrade.html b/htdocs/docs/upgrade.html
deleted file mode 100644
index d2201f6..0000000
--- a/htdocs/docs/upgrade.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<head>
- <title>Upgrading to 1.1.x</title>
-</head>
-<body>
-<h1>Upgrading to 1.1.x</h1>
-<ul>
- <li>Back up your data and current Freeside installation.
- <li>Unpack a copy of the 1.0.0 distribution in a separate location.
- <li>Diff your current installation against the 1.0.0 distribution.
- <li>Apply all the diffs you found above, if applicable.
- <li>Apply (at least) the following changes to your database:
-<pre>
-ALTER TABLE cust_main CHANGE ss ss char(11) NULL;
-ALTER TABLE cust_main CHANGE day daytime varchar(20) NULL;
-ALTER TABLE svc_acct CHANGE password _password varchar(25) NOT NULL;
-ALTER TABLE part_svc CHANGE svc_acct__password svc_acct___password varchar(25) NULL;
-ALTER TABLE part_svc CHANGE svc_acct__password_flag svc_acct___password_flag char(1) NULL;
-ALTER TABLE agent_type CHANGE type atype varchar(80) NOT NULL;
-</pre>
- <li>Optionally change the field lengths and types to match a 1.1.x install; see `bin/fs-setup'.
- <li>Create the necessary <a href="config.html">configuration files</a>,
- <li>Copy or symlink htdocs and site_perl to the new 1.1.x copies.
- <li>Run bin/dbdef-create. This file uses MySQL-specific syntax. If you are running a different database engine you will need to modify it slightly.
-</body>
diff --git a/htdocs/docs/upgrade2.html b/htdocs/docs/upgrade2.html
deleted file mode 100644
index 4bf7ea4..0000000
--- a/htdocs/docs/upgrade2.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<head>
- <title>Upgrading to 1.1.3</title>
-</head>
-<body>
-<h1>Upgrading to 1.1.3 from 1.1.x</h1>
-<ul>
- <li>If migrating from 1.0.0, see these <a href="upgrade.html">instructions</a> first.
- <li>Back up your data and current Freeside installation.
- <li>If applicable, create the new <a href="config.html">configuration files</a>: lpr, cybercash2, cybercash3.2
- <li>Copy or symlink htdocs and site_perl to the new copies.
-</body>
diff --git a/htdocs/edit/agent.cgi b/htdocs/edit/agent.cgi
deleted file mode 100755
index 5bd1165..0000000
--- a/htdocs/edit/agent.cgi
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# agent.cgi: Add/Edit agent (output form)
-#
-# ivan@sisd.com 97-dec-12
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'type' to 'atype' because Pg6.3 reserves the type word
-# bmccane@maxbaud.net 98-apr-3
-#
-# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::agent;
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-my($agent,$action);
-if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
- $agent=qsearchs('agent',{'agentnum'=>$1});
- $action='Edit';
-} else { #adding
- $agent=create FS::agent {};
- $action='Add';
-}
-my($hashref)=$agent->hashref;
-
-print header("$action Agent", menubar(
- 'Main Menu' => '../',
- 'View all agents' => '../browse/agent.cgi',
-)), '<FORM ACTION="process/agent.cgi" METHOD=POST>';
-
-print qq!<INPUT TYPE="hidden" NAME="agentnum" VALUE="$hashref->{agentnum}">!,
- "Agent #", $hashref->{agentnum} ? $hashref->{agentnum} : "(NEW)";
-
-print <<END;
-<PRE>
-Agent <INPUT TYPE="text" NAME="agent" SIZE=32 VALUE="$hashref->{agent}">
-Agent type <SELECT NAME="typenum" SIZE=1>
-END
-
-my($agent_type);
-foreach $agent_type (qsearch('agent_type',{})) {
- print "<OPTION";
- print " SELECTED"
- if $hashref->{typenum} == $agent_type->getfield('typenum');
- print ">", $agent_type->getfield('typenum'), ": ",
- $agent_type->getfield('atype'),"\n";
-}
-
-print <<END;
-</SELECT>
-Frequency (unimplemented) <INPUT TYPE="text" NAME="freq" VALUE="$hashref->{freq}">
-Program (unimplemented) <INPUT TYPE="text" NAME="prog" VALUE="$hashref->{prog}">
-</PRE>
-END
-
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{agentnum} ? "Apply changes" : "Add agent",
- qq!">!;
-
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/agent_type.cgi b/htdocs/edit/agent_type.cgi
deleted file mode 100755
index b9fff45..0000000
--- a/htdocs/edit/agent_type.cgi
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# agent_type.cgi: Add/Edit agent type (output form)
-#
-# ivan@sisd.com 97-dec-10
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'type' to 'atype' because Pg6.3 reserves the type word
-# bmccane@maxbaud.net 98-apr-3
-#
-# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::agent_type;
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-my($agent_type,$action);
-if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
- $agent_type=qsearchs('agent_type',{'typenum'=>$1});
- $action='Edit';
-} else { #adding
- $agent_type=create FS::agent_type {};
- $action='Add';
-}
-my($hashref)=$agent_type->hashref;
-
-print header("$action Agent Type", menubar(
- 'Main Menu' => '../',
- 'View all agent types' => '../browse/agent_type.cgi',
-)), '<FORM ACTION="process/agent_type.cgi" METHOD=POST>';
-
-print qq!<INPUT TYPE="hidden" NAME="typenum" VALUE="$hashref->{typenum}">!,
- "Agent Type #", $hashref->{typenum} ? $hashref->{typenum} : "(NEW)";
-
-print <<END;
-<BR>Type <INPUT TYPE="text" NAME="atype" SIZE=32 VALUE="$hashref->{atype}">
-<P>Select which packages agents of this type may sell to customers</P>
-END
-
-my($part_pkg);
-foreach $part_pkg ( qsearch('part_pkg',{}) ) {
- print qq!<BR><INPUT TYPE="checkbox" NAME="pkgpart!,
- $part_pkg->getfield('pkgpart'), qq!" !,
- # ( 'CHECKED 'x scalar(
- qsearchs('type_pkgs',{
- 'typenum' => $agent_type->getfield('typenum'),
- 'pkgpart' => $part_pkg->getfield('pkgpart'),
- })
- ? 'CHECKED '
- : '',
- qq!"VALUE="ON"> !,$part_pkg->getfield('pkg')
- ;
-}
-
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{typenum} ? "Apply changes" : "Add agent type",
- qq!">!;
-
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/cust_credit.cgi b/htdocs/edit/cust_credit.cgi
deleted file mode 100755
index 75ef212..0000000
--- a/htdocs/edit/cust_credit.cgi
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_credit.cgi: Add a credit (output form)
-#
-# Usage: cust_credit.cgi custnum [ -paybatch ]
-# http://server.name/path/cust_credit?custnum [ -paybatch ]
-#
-# Note: Should be run setuid root as user nobody.
-#
-# some hooks in here for modifications as well as additions, but needs (lots) more work.
-# also see process/cust_credit.cgi, the script that processes the form.
-#
-# ivan@voicenet.com 96-dec-05
-#
-# paybatch field, differentiates between credits & credits+refunds by commandline
-# ivan@voicenet.com 96-dec-08
-#
-# added (but commented out) sprintf("%.2f" in amount field. Hmm.
-# ivan@voicenet.com 97-jan-3
-#
-# paybatch stuff thrown out - has checkbox now instead.
-# (well, sort of. still passed around for backward compatability and possible editing hook)
-# ivan@voicenet.com 97-apr-21
-#
-# rewrite ivan@sisd.com 98-mar-16
-
-use strict;
-use Date::Format;
-use CGI::Base qw(:DEFAULT :CGI); #CGI module
-use FS::UID qw(cgisuidsetup getotaker);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-cgisuidsetup($cgi);
-
-#untaint custnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($custnum)=$1;
-
-#untaint otaker
-my($otaker)=getotaker;
-
-SendHeaders(); # one guess.
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Post Credit</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Post Credit</H1>
- </CENTER>
- <FORM ACTION="process/cust_credit.cgi" METHOD=POST>
- <HR><PRE>
-END
-
-#crednum
-my($crednum)="";
-print qq!Credit #<B>!, $crednum ? $crednum : " <I>(NEW)</I>", qq!</B><INPUT TYPE="hidden" NAME="crednum" VALUE="$crednum">!;
-
-#custnum
-print qq!\nCustomer #<B>$custnum</B><INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">!;
-
-#paybatch
-print qq!<INPUT TYPE="hidden" NAME="paybatch" VALUE="">!;
-
-#date
-my($date)=time;
-print qq!\nDate: <B>!, time2str("%D",$date), qq!</B><INPUT TYPE="hidden" NAME="_date" VALUE="$date">!;
-
-#amount
-my($amount)='';
-print qq!\nAmount \$<INPUT TYPE="text" NAME="amount" VALUE="$amount" SIZE=8 MAXLENGTH=8>!;
-
-#refund?
-#print qq! <INPUT TYPE="checkbox" NAME="refund" VALUE="yes">Also post refund!;
-
-#otaker (hidden)
-print qq!<INPUT TYPE="hidden" NAME="otaker" VALUE="$otaker">!;
-
-#reason
-my($reason)='';
-print qq!\nReason <INPUT TYPE="text" NAME="reason" VALUE="$reason" SIZE=72>!;
-
-print <<END;
-</PRE>
-<BR>
-<CENTER><INPUT TYPE="submit" VALUE="Post"></CENTER>
-END
-
-print <<END;
-
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/cust_main.cgi b/htdocs/edit/cust_main.cgi
deleted file mode 100755
index 1455601..0000000
--- a/htdocs/edit/cust_main.cgi
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_main.cgi: Edit a customer (output form)
-#
-# Usage: cust_main.cgi custnum
-# http://server.name/path/cust_main.cgi?custnum
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 96-nov-29 -> 96-dec-04
-#
-# Blank custnum for new customer.
-# ivan@voicenet.com 96-dec-16
-#
-# referral defaults to blank, to force people to pick something
-# ivan@voicenet.com 97-jun-4
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-28
-#
-# new customer is null, not '#'
-# otaker gotten from &getotaker instead of $ENV{REMOTE_USER}
-# ivan@sisd.com 97-nov-12
-#
-# cgisuidsetup($cgi);
-# no need for old_ fields.
-# now state+county is a select field (took out PA hack)
-# used autoloaded $cust_main->field methods
-# ivan@sisd.com 97-dec-17
-#
-# fixed quoting problems ivan@sisd.com 98-feb-23
-#
-# paydate sql update ivan@sisd.com 98-mar-5
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'day' to 'daytime' because Pg6.3 reserves the day word
-# Added test for paydate in mm-dd-yyyy format for Pg6.3 default format
-# bmccane@maxbaud.net 98-apr-3
-#
-# fixed one missed day->daytime ivan@sisd.com 98-jul-13
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup getotaker);
-use FS::Record qw(qsearch qsearchs);
-use FS::cust_main;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-#get record
-my($custnum,$action,$cust_main);
-if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
- $custnum=$1;
- $cust_main = qsearchs('cust_main',{'custnum'=>$custnum});
- $action='Edit';
-} else {
- $custnum='';
- $cust_main = create FS::cust_main ( {} );
- $cust_main->setfield('otaker',&getotaker);
- $cust_main->setfield('country','US');
- $action='Add';
-}
-
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Customer $action</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Customer $action</H1>
- </CENTER>
- <FORM ACTION="process/cust_main.cgi" METHOD=POST>
- <PRE>
-END
-
-print qq!<INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">!,
- qq!Customer #<FONT SIZE="+1"><B>!;
-print $custnum ? $custnum : " (NEW)" , "</B></FONT>";
-
-#agentnum
-my($agentnum)=$cust_main->agentnum || 1; #set to first agent by default
-my(@agents) = qsearch('agent',{});
-print qq!\n\nAgent # <SELECT NAME="agentnum" SIZE="1">!;
-my($agent);
-foreach $agent (sort {
- $a->agent cmp $b->agent;
-} @agents) {
- print "<OPTION" . " SELECTED"x($agent->agentnum==$agentnum),
- ">", $agent->agentnum,": ", $agent->agent, "\n";
-}
-print "</SELECT>";
-
-#referral
-#unless ($custnum) {
- my($refnum)=$cust_main->refnum || 0; #to avoid "arguement not numeric" error
- my(@referrals) = qsearch('part_referral',{});
- print qq!\nReferral <SELECT NAME="refnum" SIZE="1">!;
- print "<OPTION> \n";
- my($referral);
- foreach $referral (sort {
- $a->refnum <=> $b->refnum;
- } @referrals) {
- print "<OPTION" . " SELECTED"x($referral->refnum==$refnum),
- ">", $referral->refnum, ": ", $referral->referral,"\n";
- }
- print "</SELECT>";
-#}
-
-my($last,$first,$ss,$company,$address1,$address2,$city)=(
- $cust_main->last,
- $cust_main->first,
- $cust_main->ss,
- $cust_main->company,
- $cust_main->address1,
- $cust_main->address2,
- $cust_main->city,
-);
-
-print <<END;
-
-
-Name (last)<INPUT TYPE="text" NAME="last" VALUE="$last"> (first)<INPUT TYPE="text" NAME="first" VALUE="$first"> SS# <INPUT TYPE="text" NAME="ss" VALUE="$ss" SIZE=11 MAXLENGTH=11>
-Company <INPUT TYPE="text" NAME="company" VALUE="$company">
-Address <INPUT TYPE="text" NAME="address1" VALUE="$address1" SIZE=40 MAXLENGTH=40>
- <INPUT TYPE="text" NAME="address2" VALUE="$address2" SIZE=40 MAXLENGTH=40>
-City <INPUT TYPE="text" NAME="city" VALUE="$city"> State (county) <SELECT NAME="state" SIZE="1">
-END
-
-foreach ( qsearch('cust_main_county',{}) ) {
- print "<OPTION";
- print " SELECTED" if ( $cust_main->state eq $_->state
- && $cust_main->county eq $_->county );
- print ">",$_->state;
- print " (",$_->county,")" if $_->county;
-}
-print "</SELECT>";
-
-my($zip,$country,$daytime,$night,$fax)=(
- $cust_main->zip,
- $cust_main->country,
- $cust_main->daytime,
- $cust_main->night,
- $cust_main->fax,
-);
-
-print <<END;
- Zip <INPUT TYPE="text" NAME="zip" VALUE="$zip" SIZE=10 MAXLENGTH=10>
-Country: <FONT SIZE="+1"><B>$country</B></FONT><INPUT TYPE="hidden" NAME="country" VALUE="$country">
-
-Phone (daytime)<INPUT TYPE="text" NAME="daytime" VALUE="$daytime" SIZE=18 MAXLENGTH=20> (night)<INPUT TYPE="text" NAME="night" VALUE="$night" SIZE=18 MAXLENGTH=20> (fax)<INPUT TYPE="text" NAME="fax" VALUE="$fax" SIZE=12 MAXLENGTH=12>
-
-END
-
-my(%payby)=(
- 'CARD' => "Credit card ",
- 'BILL' => "Billing ",
- 'COMP' => "Complimentary",
-);
-for (qw(CARD BILL COMP)) {
- print qq!<INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
- print qq! CHECKED! if ($cust_main->payby eq "$_");
- print qq!>$payby{$_}!;
-}
-
-
-my($payinfo,$payname,$otaker)=(
- $cust_main->payinfo,
- $cust_main->payname,
- $cust_main->otaker,
-);
-
-my($paydate);
-if ( $cust_main->paydate =~ /^(\d{4})-(\d{2})-\d{2}$/ ) {
- $paydate="$2/$1"
-} elsif ( $cust_main->paydate =~ /^(\d{2})-\d{2}-(\d{4}$)/ ) {
- $paydate="$1/$2"
-}
-else {
- $paydate='';
-}
-
-print <<END;
-
- Card number , P.O. # or Authorization <INPUT TYPE="text" NAME="payinfo" VALUE="$payinfo" SIZE=19 MAXLENGTH=19>
-END
-
-print qq!Exp. date (MM/YY or MM/YYYY)<INPUT TYPE="text" NAME="paydate" VALUE="$paydate" SIZE=8 MAXLENGTH=7> Billing name <INPUT TYPE="text" NAME="payname" VALUE="$payname">\n<INPUT TYPE="checkbox" NAME="tax" VALUE="Y"!;
-print qq! CHECKED! if $cust_main->tax eq "Y";
-print qq!> Tax Exempt!;
-
-print <<END;
-
-
-Order taken by: <FONT SIZE="+1"><B>$otaker</B></FONT><INPUT TYPE="hidden" NAME="otaker" VALUE="$otaker">
-</PRE>
-END
-
-print qq!<CENTER><INPUT TYPE="submit" VALUE="!,
- $custnum ? "Apply Changes" : "Add Customer", qq!"></CENTER>!;
-
-print <<END;
-
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/cust_main_county-expand.cgi b/htdocs/edit/cust_main_county-expand.cgi
deleted file mode 100755
index 59ff704..0000000
--- a/htdocs/edit/cust_main_county-expand.cgi
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_main_county-expand.cgi: Expand a state into counties (output form)
-#
-# ivan@sisd.com 97-dec-16
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-$cgi->var('QUERY_STRING') =~ /^(\d+)$/
- or die "Illegal taxnum!";
-my($taxnum)=$1;
-
-my($cust_main_county)=qsearchs('cust_main_county',{'taxnum'=>$taxnum});
-die "Can't expand entry!" if $cust_main_county->getfield('county');
-
-print header("Tax Rate (expand state)", menubar(
- 'Main Menu' => '../',
-)), <<END;
- <FORM ACTION="process/cust_main_county-expand.cgi" METHOD=POST>
- <INPUT TYPE="hidden" NAME="taxnum" VALUE="$taxnum">
- Separate counties by
- <INPUT TYPE="radio" NAME="delim" VALUE="n" CHECKED>line
- (rumor has it broken on some browsers) or
- <INPUT TYPE="radio" NAME="delim" VALUE="s">whitespace.
- <BR><INPUT TYPE="submit" VALUE="Submit">
- <BR><TEXTAREA NAME="counties" ROWS=100></TEXTAREA>
- </FORM>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/cust_main_county.cgi b/htdocs/edit/cust_main_county.cgi
deleted file mode 100755
index 904d583..0000000
--- a/htdocs/edit/cust_main_county.cgi
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_main_county.cgi: Edit tax rates (output form)
-#
-# ivan@sisd.com 97-dec-13-16
-#
-# Changes to allow page to work at a relative position in server
-# Changed tax field to accept 6 chars (MO uses 6.1%)
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-print header("Edit tax rates", menubar(
- 'Main Menu' => '../',
-)),<<END;
- <FORM ACTION="process/cust_main_county.cgi" METHOD=POST>
- <TABLE BORDER>
- <TR>
- <TH><FONT SIZE=-1>State</FONT></TH>
- <TH>County</TH>
- <TH><FONT SIZE=-1>Tax</FONT></TH>
- </TR>
-END
-
-my($cust_main_county);
-foreach $cust_main_county ( qsearch('cust_main_county',{}) ) {
- my($hashref)=$cust_main_county->hashref;
- print <<END;
- <TR>
- <TD>$hashref->{state}</TD>
-END
-
- print "<TD>", $hashref->{county}
- ? $hashref->{county}
- : '(ALL)'
- , "</TD>";
-
- print qq!<TD><INPUT TYPE="text" NAME="tax!, $hashref->{taxnum},
- qq!" VALUE="!, $hashref->{tax}, qq!" SIZE=6 MAXLENGTH=6>%</TD></TR>!;
-END
-
-}
-
-print <<END;
- </TABLE>
- <INPUT TYPE="submit" VALUE="Apply changes">
- </FORM>
- </CENTER>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/cust_pay.cgi b/htdocs/edit/cust_pay.cgi
deleted file mode 100755
index a6cb204..0000000
--- a/htdocs/edit/cust_pay.cgi
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_pay.cgi: Add a payment (output form)
-#
-# Usage: cust_pay.cgi invnum
-# http://server.name/path/cust_pay.cgi?invnum
-#
-# Note: Should be run setuid as user nobody.
-#
-# some hooks for modifications as well as additions, but needs work.
-#
-# ivan@voicenet.com 96-dec-11
-#
-# rewrite ivan@sisd.com 98-mar-16
-
-use strict;
-use Date::Format;
-use CGI::Base qw(:DEFAULT :CGI);
-use FS::UID qw(cgisuidsetup);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-cgisuidsetup($cgi);
-
-#untaint invnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($invnum)=$1;
-
-SendHeaders(); # one guess.
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Enter payment</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Enter payment</H1>
- </CENTER>
- <FORM ACTION="process/cust_pay.cgi" METHOD=POST>
- <HR><PRE>
-END
-
-#invnum
-print qq!Invoice #<B>$invnum</B><INPUT TYPE="hidden" NAME="invnum" VALUE="$invnum">!;
-
-#date
-my($date)=time;
-print qq!<BR>Date: <B>!, time2str("%D",$date), qq!</B><INPUT TYPE="hidden" NAME="_date" VALUE="$date">!;
-
-#paid
-print qq!<BR>Amount \$<INPUT TYPE="text" NAME="paid" VALUE="" SIZE=8 MAXLENGTH=8>!;
-
-#payby
-my($payby)="BILL";
-print qq!<BR>Payby: <B>$payby</B><INPUT TYPE="hidden" NAME="payby" VALUE="$payby">!;
-
-#payinfo (check # now as payby="BILL" hardcoded.. what to do later?)
-my($payinfo)="";
-print qq!<BR>Check #<INPUT TYPE="text" NAME="payinfo" VALUE="$payinfo">!;
-
-#paybatch
-print qq!<INPUT TYPE="hidden" NAME="paybatch" VALUE="">!;
-
-print <<END;
-</PRE>
-<BR>
-<CENTER><INPUT TYPE="submit" VALUE="Post"></CENTER>
-END
-
-print <<END;
-
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/cust_pkg.cgi b/htdocs/edit/cust_pkg.cgi
deleted file mode 100755
index d7f143d..0000000
--- a/htdocs/edit/cust_pkg.cgi
+++ /dev/null
@@ -1,137 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_pkg.cgi: Add/edit packages (output form)
-#
-# this is for changing packages around, not editing things within the package
-#
-# Usage: cust_pkg.cgi custnum
-# http://server.name/path/cust_pkg.cgi?custnum
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# started with /sales/add/cust_pkg.cgi, which added packages
-# ivan@voicenet.com 97-jan-5, 97-mar-21
-#
-# Rewrote for new API
-# ivan@voicenet.com 97-jul-7
-#
-# FS::Search is no more, &cgisuidsetup needs $cgi, ivan@sisd.com 98-mar-7
-#
-# Changes to allow page to work at a relative position in server
-# Changed to display packages 2-wide in a table
-# bmccane@maxbaud.net 98-apr-3
-#
-# fixed a pretty cool bug from above which caused a visual glitch ivan@sisd.com
-# 98-jun-1
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup getotaker);
-use FS::Record qw(qsearch qsearchs);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-my(%pkg,%comment);
-foreach (qsearch('part_pkg', {})) {
- $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg');
- $comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment');
-}
-
-#untaint custnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($custnum)=$1;
-
-my($otaker)=&getotaker;
-
-SendHeaders();
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Add/Edit Packages</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Add/Edit Packages</H1>
- </CENTER>
- <FORM ACTION="process/cust_pkg.cgi" METHOD=POST>
- <HR>
-END
-
-#custnum
-print qq!<INPUT TYPE="hidden" NAME="new_custnum" VALUE="$custnum">!;
-
-#current packages (except cancelled packages)
-my(@cust_pkg) = grep ! $_->getfield('cancel'),
- qsearch('cust_pkg',{'custnum'=>$custnum});
-
-if (@cust_pkg) {
- print <<END;
-<CENTER><FONT SIZE="+2">Current packages</FONT></CENTER>
-These are packages the customer currently has. Select those packages you
-wish to remove (if any).<BR><BR>
-END
-
- my ($count) = 0 ;
- print qq!<CENTER><TABLE>! ;
- foreach (@cust_pkg) {
- print qq!<TR>! if ($count ==0) ;
- my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') );
- print qq!<TD><INPUT TYPE="checkbox" NAME="remove_pkg" VALUE="$pkgnum">!,
- #qq!$pkgnum: $pkg{$pkgpart} - $comment{$pkgpart}</TD>\n!,
- #now you've got to admit this bug was pretty cool
- qq!$pkgnum: $pkg{$pkgpart} - $comment{$pkgpart}</TD>\n!;
- $count ++ ;
- if ($count == 2)
- {
- $count = 0 ;
- print qq!</TR>\n! ;
- }
- }
- print qq!</TABLE></CENTER>! ;
-
- print "<HR>";
-}
-
-print <<END;
-<CENTER><FONT SIZE="+2">New packages</FONT></CENTER>
-These are packages the customer can purchase. Specify the quantity to add
-of each package.<BR><BR>
-END
-
-my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum});
-my($agent)=qsearchs('agent',{'agentnum'=> $cust_main->agentnum });
-
-my($type_pkgs);
-my ($count) = 0 ;
-print qq!<CENTER><TABLE>! ;
-foreach $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) {
- my($pkgpart)=$type_pkgs->pkgpart;
- print qq!<TR>! if ($count == 0) ;
- print <<END;
- <TD>
- <INPUT TYPE="text" NAME="pkg$pkgpart" VALUE="0" SIZE="2" MAXLENGTH="2">
- $pkgpart: $pkg{$pkgpart} - $comment{$pkgpart}</TD>\n
-END
- $count ++ ;
- if ($count == 2)
- {
- print qq!</TR>\n! ;
- $count = 0 ;
- }
-}
-print qq!</TABLE></CENTER>! ;
-
-#otaker
-print qq!<INPUT TYPE="hidden" NAME="new_otaker" VALUE="$otaker">\n!;
-
-#submit
-print qq!<P><CENTER><INPUT TYPE="submit" VALUE="Order"></CENTER>\n!;
-
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
diff --git a/htdocs/edit/part_pkg.cgi b/htdocs/edit/part_pkg.cgi
deleted file mode 100755
index 9fe739b..0000000
--- a/htdocs/edit/part_pkg.cgi
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# part_pkg.cgi: Add/Edit package (output form)
-#
-# ivan@sisd.com 97-dec-10
-#
-# Changes to allow page to work at a relative position in server
-# Changed to display services 2-wide in table
-# bmccane@maxbaud.net 98-apr-3
-#
-# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg;
-use FS::pkg_svc;
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-my($part_pkg,$action);
-if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
- $part_pkg=qsearchs('part_pkg',{'pkgpart'=>$1});
- $action='Edit';
-} else { #adding
- $part_pkg=create FS::part_pkg {};
- $action='Add';
-}
-my($hashref)=$part_pkg->hashref;
-
-print header("$action Package Definition", menubar(
- 'Main Menu' => '../',
- 'View all packages' => '../browse/part_pkg.cgi',
-)), '<FORM ACTION="process/part_pkg.cgi" METHOD=POST>';
-
-print qq!<INPUT TYPE="hidden" NAME="pkgpart" VALUE="$hashref->{pkgpart}">!,
- "Package Part #", $hashref->{pkgpart} ? $hashref->{pkgpart} : "(NEW)";
-
-print <<END;
-<PRE>
-Package (customer-visable) <INPUT TYPE="text" NAME="pkg" SIZE=32 VALUE="$hashref->{pkg}">
-Comment (customer-hidden) <INPUT TYPE="text" NAME="comment" SIZE=32 VALUE="$hashref->{comment}">
-Setup fee for this package <INPUT TYPE="text" NAME="setup" VALUE="$hashref->{setup}">
-Recurring fee for this package <INPUT TYPE="text" NAME="recur" VALUE="$hashref->{recur}">
-Frequency (months) of recurring fee <INPUT TYPE="text" NAME="freq" VALUE="$hashref->{freq}">
-
-</PRE>
-
-Enter the quantity of each service this package includes.<BR><BR>
-<TABLE BORDER><TR><TH><FONT SIZE=-1>Quan.</FONT></TH><TH>Service</TH>
- <TH><FONT SIZE=-1>Quan.</FONT></TH><TH>Service</TH></TR>
-END
-
-my($part_svc);
-my($count) = 0 ;
-foreach $part_svc ( qsearch('part_svc',{}) ) {
-
- my($svcpart)=$part_svc->getfield('svcpart');
- my($pkg_svc)=qsearchs('pkg_svc',{
- 'pkgpart' => $part_pkg->getfield('pkgpart'),
- 'svcpart' => $svcpart,
- }) || create FS::pkg_svc({
- 'pkgpart' => $part_pkg->getfield('pkgpart'),
- 'svcpart' => $svcpart,
- 'quantity' => 0,
- });
- next unless $pkg_svc;
-
- print qq!<TR>! if $count == 0 ;
- print qq!<TD><INPUT TYPE="text" NAME="pkg_svc$svcpart" SIZE=3 VALUE="!,
- $pkg_svc->getfield('quantity') || 0,qq!"></TD>!,
- qq!<TD><A HREF="part_svc.cgi?!,$part_svc->getfield('svcpart'),
- qq!">!, $part_svc->getfield('svc'), "</A></TD>";
- $count ++ ;
- if ($count == 2)
- {
- print qq!</TR>! ;
- $count = 0 ;
- }
-}
-print qq!</TR>! if ($count != 0) ;
-
-print "</TABLE>";
-
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{pkgpart} ? "Apply changes" : "Add package",
- qq!">!;
-
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/part_referral.cgi b/htdocs/edit/part_referral.cgi
deleted file mode 100755
index f298022..0000000
--- a/htdocs/edit/part_referral.cgi
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# agent.cgi: Add/Edit referral (output form)
-#
-# ivan@sisd.com 98-feb-23
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# confisuing typo on submit button ivan@sisd.com 98-jun-14
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::part_referral;
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-my($part_referral,$action);
-if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
- $part_referral=qsearchs('part_referral',{'refnum'=>$1});
- $action='Edit';
-} else { #adding
- $part_referral=create FS::part_referral {};
- $action='Add';
-}
-my($hashref)=$part_referral->hashref;
-
-print header("$action Referral", menubar(
- 'Main Menu' => '../',
- 'View all referrals' => "../browse/part_referral.cgi",
-)), <<END;
- <FORM ACTION="process/part_referral.cgi" METHOD=POST>
-END
-
-#display
-
-print qq!<INPUT TYPE="hidden" NAME="refnum" VALUE="$hashref->{refnum}">!,
- "Referral #", $hashref->{refnum} ? $hashref->{refnum} : "(NEW)";
-
-print <<END;
-<PRE>
-Referral <INPUT TYPE="text" NAME="referral" SIZE=32 VALUE="$hashref->{referral}">
-</PRE>
-END
-
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{refnum} ? "Apply changes" : "Add referral",
- qq!">!;
-
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/part_svc.cgi b/htdocs/edit/part_svc.cgi
deleted file mode 100755
index 491c013..0000000
--- a/htdocs/edit/part_svc.cgi
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# part_svc.cgi: Add/Edit service (output form)
-#
-# ivan@sisd.com 97-nov-14
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::part_svc qw(fields);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-my($part_svc,$action);
-if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
- $part_svc=qsearchs('part_svc',{'svcpart'=>$1});
- $action='Edit';
-} else { #adding
- $part_svc=create FS::part_svc {};
- $action='Add';
-}
-my($hashref)=$part_svc->hashref;
-
-print header("$action Service Definition", menubar(
- 'Main Menu' => '../',
- 'View all services' => '../browse/part_svc.cgi',
-)), '<FORM ACTION="process/part_svc.cgi" METHOD=POST>';
-
-
-
-print qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$hashref->{svcpart}">!,
- "Service Part #", $hashref->{svcpart} ? $hashref->{svcpart} : "(NEW)";
-
-print <<END;
-<PRE>
-Service <INPUT TYPE="text" NAME="svc" VALUE="$hashref->{svc}">
-Table <SELECT NAME="svcdb" SIZE=1>
-END
-
-print map '<OPTION'. ' SELECTED'x($_ eq $hashref->{svcdb}). ">$_\n", qw(
- svc_acct svc_domain svc_acct_sm svc_charge svc_wo
-);
-
-print <<END;
-</SELECT></PRE>
-Services are items you offer to your customers.
-<UL><LI>svc_acct - Shell accounts, POP mailboxes, SLIP/PPP and ISDN accounts
- <LI>svc_domain - Virtual domains
- <LI>svc_acct_sm - Virtual domain mail aliasing
- <LI>svc_charge - One-time charges (Partially unimplemented)
- <LI>svc_wo - Work orders (Partially unimplemented)
-</UL>
-For the columns in the table selected above, you can set default or fixed
-values. For example, a SLIP/PPP account may have a default (or perhaps fixed)
-<B>slipip</B> of <B>0.0.0.0</B>, while a POP mailbox will probably have a fixed
-blank <B>slipip</B> as well as a fixed shell something like <B>/bin/true</B> or
-<B>/usr/bin/passwd</B>.
-<BR><BR>
-<TABLE BORDER CELLPADDING=4><TR><TH>Table</TH><TH>Field</TH>
-<TH COLSPAN=2>Modifier</TH></TR>
-END
-
-#these might belong somewhere else for other user interfaces
-#pry need to eventually create stuff that's shared amount UIs
-my(%defs)=(
- 'svc_acct' => {
- 'dir' => 'Home directory',
- 'uid' => 'UID (set to fixed and blank for dial-only)',
- 'slipip' => 'IP address',
- 'popnum' => '<A HREF="../browse/svc_acct_pop.cgi/">POP number</A>',
- 'username' => 'Username',
- 'quota' => '(unimplemented)',
- '_password' => 'Password',
- 'gid' => 'GID (when blank, defaults to UID)',
- 'shell' => 'Shell',
- 'finger' => 'GECOS',
- },
- 'svc_domain' => {
- 'domain' => 'Domain',
- },
- 'svc_acct_sm' => {
- 'domuser' => 'domuser@virtualdomain.com',
- 'domuid' => 'UID where domuser@virtualdomain.com mail is forwarded',
- 'domsvc' => 'svcnum from svc_domain for virtualdomain.com',
- },
- 'svc_charge' => {
- 'amount' => 'amount',
- },
- 'svc_wo' => {
- 'worker' => 'Worker',
- '_date' => 'Date',
- },
-);
-
-my($svcdb);
-foreach $svcdb ( qw(
- svc_acct svc_domain svc_acct_sm svc_charge svc_wo
-) ) {
-
- my(@rows)=map { /^${svcdb}__(.*)$/; $1 }
- grep ! /_flag$/,
- grep /^${svcdb}__/,
- fields('part_svc');
- my($rowspan)=scalar(@rows);
-
- my($ptmp)="<TD ROWSPAN=$rowspan>$svcdb</TD>";
- my($row);
- foreach $row (@rows) {
- my($value)=$part_svc->getfield($svcdb.'__'.$row);
- my($flag)=$part_svc->getfield($svcdb.'__'.$row.'_flag');
- print "<TR>$ptmp<TD>$row - <FONT SIZE=-1>$defs{$svcdb}{$row}</FONT></TD>";
- print qq!<TD><INPUT TYPE="radio" NAME="${svcdb}__${row}_flag" VALUE=""!.
- ' CHECKED'x($flag eq ''). "><BR>Off</TD>";
- print qq!<TD><INPUT TYPE="radio" NAME="${svcdb}__${row}_flag" VALUE="D"!.
- ' CHECKED'x($flag eq 'D'). ">Default ";
- print qq!<INPUT TYPE="radio" NAME="${svcdb}__${row}_flag" VALUE="F"!.
- ' CHECKED'x($flag eq 'F'). ">Fixed ";
- print qq!<BR><INPUT TYPE="text" NAME="${svcdb}__${row}" VALUE="$value">!,
- "</TD></TR>";
- $ptmp='';
- }
-}
-print "</TABLE>";
-
-print qq!\n<CENTER><BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{svcpart} ? "Apply changes" : "Add service",
- qq!"></CENTER>!;
-
-print <<END;
-
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/process/agent.cgi b/htdocs/edit/process/agent.cgi
deleted file mode 100755
index 5d1ce32..0000000
--- a/htdocs/edit/process/agent.cgi
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/agent.cgi: Edit agent (process form)
-#
-# ivan@sisd.com 97-dec-12
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::agent qw(fields);
-use FS::CGI qw(idiot);
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-my($agentnum)=$req->param('agentnum');
-
-my($old)=qsearchs('agent',{'agentnum'=>$agentnum}) if $agentnum;
-
-#unmunge typenum
-$req->param('typenum') =~ /^(\d+)(:.*)?$/;
-$req->param('typenum',$1);
-
-my($new)=create FS::agent ( {
- map {
- $_, $req->param($_);
- } fields('agent')
-} );
-
-my($error);
-if ( $agentnum ) {
- $error=$new->replace($old);
-} else {
- $error=$new->insert;
- $agentnum=$new->getfield('agentnum');
-}
-
-if ( $error ) {
- &idiot($error);
-} else {
- #$req->cgi->redirect("../../view/agent.cgi?$agentnum");
- #$req->cgi->redirect("../../edit/agent.cgi?$agentnum");
- $req->cgi->redirect("../../browse/agent.cgi");
-}
-
diff --git a/htdocs/edit/process/agent_type.cgi b/htdocs/edit/process/agent_type.cgi
deleted file mode 100755
index 43f129f..0000000
--- a/htdocs/edit/process/agent_type.cgi
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/agent_type.cgi: Edit agent type (process form)
-#
-# ivan@sisd.com 97-dec-11
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::agent_type qw(fields);
-use FS::type_pkgs;
-use FS::CGI qw(idiot);
-
-my($req)=new CGI::Request;
-&cgisuidsetup($req->cgi);
-
-my($typenum)=$req->param('typenum');
-my($old)=qsearchs('agent_type',{'typenum'=>$typenum}) if $typenum;
-
-my($new)=create FS::agent_type ( {
- map {
- $_, $req->param($_);
- } fields('agent_type')
-} );
-
-my($error);
-if ( $typenum ) {
- $error=$new->replace($old);
-} else {
- $error=$new->insert;
- $typenum=$new->getfield('typenum');
-}
-
-if ( $error ) {
- idiot($error);
- exit;
-}
-
-my($part_pkg);
-foreach $part_pkg (qsearch('part_pkg',{})) {
- my($pkgpart)=$part_pkg->getfield('pkgpart');
-
- my($type_pkgs)=qsearchs('type_pkgs',{
- 'typenum' => $typenum,
- 'pkgpart' => $pkgpart,
- });
- if ( $type_pkgs && ! $req->param("pkgpart$pkgpart") ) {
- my($d_type_pkgs)=$type_pkgs; #need to save $type_pkgs for below.
- $error=$d_type_pkgs->del; #FS::Record not FS::type_pkgs,
- #so ->del not ->delete. hmm. hmm.
- if ( $error ) {
- idiot($error);
- exit;
- }
-
- } elsif ( $req->param("pkgpart$pkgpart")
- && ! $type_pkgs
- ) {
- #ok to clobber it now (but bad form nonetheless?)
- $type_pkgs=create FS::type_pkgs ({
- 'typenum' => $typenum,
- 'pkgpart' => $pkgpart,
- });
- $error= $type_pkgs->insert;
- if ( $error ) {
- idiot($error);
- exit;
- }
- }
-
-}
-
-#$req->cgi->redirect("../../view/agent_type.cgi?$typenum");
-#$req->cgi->redirect("../../edit/agent_type.cgi?$typenum");
-$req->cgi->redirect("../../browse/agent_type.cgi");
-
diff --git a/htdocs/edit/process/cust_credit.cgi b/htdocs/edit/process/cust_credit.cgi
deleted file mode 100755
index e660b4c..0000000
--- a/htdocs/edit/process/cust_credit.cgi
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/cust_credit.cgi: Add a credit (process form)
-#
-# Usage: post form to:
-# http://server.name/path/cust_credit.cgi
-#
-# Note: Should be run setuid root as user nobody.
-#
-# ivan@voicenet.com 96-dec-05 -> 96-dec-08
-#
-# post a refund if $new_paybatch
-# ivan@voicenet.com 96-dec-08
-#
-# refunds are no longer applied against a specific payment (paybatch)
-# paybatch field removed
-# ivan@voicenet.com 97-apr-22
-#
-# rewrite ivan@sisd.com 98-mar-16
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use FS::UID qw(cgisuidsetup getotaker);
-use FS::cust_credit;
-
-my($req)=new CGI::Request; # create form object
-cgisuidsetup($req->cgi);
-
-$req->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!";
-my($custnum)=$1;
-
-$req->param('otaker',getotaker);
-
-my($new) = create FS::cust_credit ( {
- map {
- $_, $req->param($_);
- } qw(custnum _date amount otaker reason)
-} );
-
-my($error);
-$error=$new->insert;
-&idiot($error) if $error;
-
-#no errors, no refund, so view our credit.
-$req->cgi->redirect("../../view/cust_main.cgi?$custnum#history");
-
-sub idiot {
- my($error)=@_;
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error posting credit/refund</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error posting credit/refund</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and press the <I>Post</I> button again.
- </BODY>
-</HTML>
-END
-
-}
-
diff --git a/htdocs/edit/process/cust_main.cgi b/htdocs/edit/process/cust_main.cgi
deleted file mode 100755
index 7664dfc..0000000
--- a/htdocs/edit/process/cust_main.cgi
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/cust_main.cgi: Edit a customer (process form)
-#
-# Usage: post form to:
-# http://server.name/path/cust_main.cgi
-#
-# Note: Should be run setuid root as user nobody.
-#
-# ivan@voicenet.com 96-dec-04
-#
-# added referral check
-# ivan@voicenet.com 97-jun-4
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-28
-#
-# same as above (again) and clean up some stuff ivan@sisd.com 98-feb-23
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'day' to 'daytime' because Pg6.3 reserves the day word
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::cust_main;
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-#create new record object
-
-#unmunge agentnum
-$req->param('agentnum',
- (split(/:/, ($req->param('agentnum'))[0] ))[0]
-);
-
-#unmunge tax
-$req->param('tax','') unless defined($req->param('tax'));
-
-#unmunge refnum
-$req->param('refnum',
- (split(/:/, ($req->param('refnum'))[0] ))[0]
-);
-
-#unmunge state/county
-$req->param('state') =~ /^(\w+)( \((\w+)\))?$/;
-$req->param('state', $1);
-$req->param('county', $3 || '');
-
-my($new) = create FS::cust_main ( {
- map {
- $_, $req->param("$_") || ''
- } qw(custnum agentnum last first ss company address1 address2 city county
- state zip country daytime night fax payby payinfo paydate payname tax
- otaker refnum)
-} );
-
-if ( $new->custnum eq '' ) {
-
- my($error)=$new->insert;
- &idiot($error) if $error;
-
-} else { #create old record object
-
- my($old) = qsearchs( 'cust_main', { 'custnum', $new->custnum } );
- &idiot("Old record not found!") unless $old;
- my($error)=$new->replace($old);
- &idiot($error) if $error;
-
-}
-
-my($custnum)=$new->custnum;
-$req->cgi->redirect("../../view/cust_main.cgi?$custnum#cust_main");
-
-sub idiot {
- my($error)=@_;
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error updating customer information</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error updating customer information</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and submit the form again.
- </BODY>
-</HTML>
-END
-
- exit;
-
-}
-
diff --git a/htdocs/edit/process/cust_main_county-expand.cgi b/htdocs/edit/process/cust_main_county-expand.cgi
deleted file mode 100755
index a821560..0000000
--- a/htdocs/edit/process/cust_main_county-expand.cgi
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/cust_main_county-expand.cgi: Expand counties (process form)
-#
-# ivan@sisd.com 97-dec-16
-#
-# Changes to allow page to work at a relative position in server
-# Added import of datasrc from UID.pm for Pg6.3
-# Default tax to 0.0 if using Pg6.3
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI
-# undo default tax to 0.0 if using Pg6.3: comes from pre-expanded record
-# for that state
-#ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup datasrc);
-use FS::Record qw(qsearch qsearchs);
-use FS::cust_main_county;
-use FS::CGI qw(eidiot);
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-$req->param('taxnum') =~ /^(\d+)$/ or die "Illegal taxnum!";
-my($taxnum)=$1;
-my($cust_main_county)=qsearchs('cust_main_county',{'taxnum'=>$taxnum})
- or die ("Unknown taxnum!");
-
-my(@counties);
-if ( $req->param('delim') eq 'n' ) {
- @counties=split(/\n/,$req->param('counties'));
-} elsif ( $req->param('delim') eq 's' ) {
- @counties=split(/\s+/,$req->param('counties'));
-} else {
- die "Illegal delim!";
-}
-
-@counties=map {
- /^\s*([\w\- ]+)\s*$/ or eidiot("Illegal county");
- $1;
-} @counties;
-
-my($county);
-foreach ( @counties) {
- my(%hash)=$cust_main_county->hash;
- my($new)=create FS::cust_main_county \%hash;
- $new->setfield('taxnum','');
- $new->setfield('county',$_);
- #if (datasrc =~ m/Pg/)
- #{
- # $new->setfield('tax',0.0);
- #}
- my($error)=$new->insert;
- die $error if $error;
-}
-
-unless ( qsearch('cust_main',{
- 'state' => $cust_main_county->getfield('state'),
- 'county' => $cust_main_county->getfield('county'),
-} ) ) {
- my($error)=($cust_main_county->delete);
- die $error if $error;
-}
-
-$req->cgi->redirect("../../edit/cust_main_county.cgi");
-
diff --git a/htdocs/edit/process/cust_main_county.cgi b/htdocs/edit/process/cust_main_county.cgi
deleted file mode 100755
index 58eaa63..0000000
--- a/htdocs/edit/process/cust_main_county.cgi
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/agent.cgi: Edit cust_main_county (process form)
-#
-# ivan@sisd.com 97-dec-16
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::cust_main_county;
-use FS::CGI qw(eidiot);
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-foreach ( $req->params ) {
- /^tax(\d+)$/ or die "Illegal form $_!";
- my($taxnum)=$1;
- my($old)=qsearchs('cust_main_county',{'taxnum'=>$taxnum})
- or die "Couldn't find taxnum $taxnum!";
- next unless $old->getfield('tax') ne $req->param("tax$taxnum");
- my(%hash)=$old->hash;
- $hash{tax}=$req->param("tax$taxnum");
- my($new)=create FS::cust_main_county \%hash;
- my($error)=$new->replace($old);
- eidiot($error) if $error;
-}
-
-$req->cgi->redirect("../../browse/cust_main_county.cgi");
-
diff --git a/htdocs/edit/process/cust_pay.cgi b/htdocs/edit/process/cust_pay.cgi
deleted file mode 100755
index 9ec9753..0000000
--- a/htdocs/edit/process/cust_pay.cgi
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/cust_pay.cgi: Add a payment (process form)
-#
-# Usage: post form to:
-# http://server.name/path/cust_pay.cgi
-#
-# Note: Should be run setuid root as user nobody.
-#
-# ivan@voicenet.com 96-dec-11
-#
-# rewrite ivan@sisd.com 98-mar-16
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use FS::UID qw(cgisuidsetup);
-use FS::cust_pay qw(fields);
-
-my($req)=new CGI::Request;
-&cgisuidsetup($req->cgi);
-
-$req->param('invnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
-my($invnum)=$1;
-
-my($new) = create FS::cust_pay ( {
- map {
- $_, $req->param($_);
- } qw(invnum paid _date payby payinfo paybatch)
-} );
-
-my($error);
-$error=$new->insert;
-
-if ($error) { #error!
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error posting payment</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error posting payment</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and press the <I>Post</I> button again.
- </BODY>
-</HTML>
-END
-} else { #no errors!
- $req->cgi->redirect("../../view/cust_bill.cgi?$invnum");
-}
-
diff --git a/htdocs/edit/process/cust_pkg.cgi b/htdocs/edit/process/cust_pkg.cgi
deleted file mode 100755
index 6f5bc87..0000000
--- a/htdocs/edit/process/cust_pkg.cgi
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/cust_pkg.cgi: Add/edit packages (process form)
-#
-# this is for changing packages around, not for editing things within the
-# package
-#
-# Usage: post form to:
-# http://server.name/path/cust_pkg.cgi
-#
-# Note: Should be run setuid root as user nobody.
-#
-# ivan@voicenet.com 97-mar-21 - 97-mar-24
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-7 - 15
-#
-# &cgisuidsetup($cgi) ivan@sisd.com 98-mar-7
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::cust_pkg;
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-#untaint custnum
-$req->param('new_custnum') =~ /^(\d+)$/;
-my($custnum)=$1;
-
-my(@remove_pkgnums) = map {
- /^(\d+)$/ or die "Illegal remove_pkg value!";
- $1;
-} $req->param('remove_pkg');
-
-my(@pkgparts);
-my($pkgpart);
-foreach $pkgpart ( map /^pkg(\d+)$/ ? $1 : (), $req->params ) {
- my($num_pkgs)=$req->param("pkg$pkgpart");
- while ( $num_pkgs-- ) {
- push @pkgparts,$pkgpart;
- }
-}
-
-my($error) = FS::cust_pkg::order($custnum,\@pkgparts,\@remove_pkgnums);
-
-if ($error) {
- CGI::Base::SendHeaders();
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error updating packages</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error updating packages</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and submit the form again.
- </BODY>
-</HTML>
-END
-} else {
- $req->cgi->redirect("../../view/cust_main.cgi?$custnum#cust_pkg");
-}
-
diff --git a/htdocs/edit/process/part_pkg.cgi b/htdocs/edit/process/part_pkg.cgi
deleted file mode 100755
index 7d78781..0000000
--- a/htdocs/edit/process/part_pkg.cgi
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/part_pkg.cgi: Edit package definitions (process form)
-#
-# ivan@sisd.com 97-dec-10
-#
-# don't update non-changing records in part_svc (causing harmless but annoying
-# "Records identical" errors). ivan@sisd.com 98-feb-19
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# Added `|| 0 ' when getting quantity off web page ivan@sisd.com 98-jun-4
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::part_pkg qw(fields);
-use FS::pkg_svc;
-use FS::CGI qw(eidiot);
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-my($pkgpart)=$req->param('pkgpart');
-
-my($old)=qsearchs('part_pkg',{'pkgpart'=>$pkgpart}) if $pkgpart;
-
-my($new)=create FS::part_pkg ( {
- map {
- $_, $req->param($_);
- } fields('part_pkg')
-} );
-
-if ( $pkgpart ) {
- my($error)=$new->replace($old);
- eidiot($error) if $error;
-} else {
- my($error)=$new->insert;
- eidiot($error) if $error;
- $pkgpart=$new->getfield('pkgpart');
-}
-
-my($part_svc);
-foreach $part_svc (qsearch('part_svc',{})) {
-# don't update non-changing records in part_svc (causing harmless but annoying
-# "Records identical" errors). ivan@sisd.com 98-jan-19
- #my($quantity)=$req->param('pkg_svc'. $part_svc->getfield('svcpart')),
- my($quantity)=$req->param('pkg_svc'. $part_svc->svcpart) || 0,
- my($old_pkg_svc)=qsearchs('pkg_svc',{
- 'pkgpart' => $pkgpart,
- 'svcpart' => $part_svc->getfield('svcpart'),
- });
- my($old_quantity)=$old_pkg_svc ? $old_pkg_svc->quantity : 0;
- next unless $old_quantity != $quantity; #!here
- my($new_pkg_svc)=create FS::pkg_svc({
- 'pkgpart' => $pkgpart,
- 'svcpart' => $part_svc->getfield('svcpart'),
- #'quantity' => $req->param('pkg_svc'. $part_svc->getfield('svcpart')),
- 'quantity' => $quantity,
- });
- if ($old_pkg_svc) {
- my($error)=$new_pkg_svc->replace($old_pkg_svc);
- eidiot($error) if $error;
- } else {
- my($error)=$new_pkg_svc->insert;
- eidiot($error) if $error;
- }
-}
-
-#$req->cgi->redirect("../../view/part_pkg.cgi?$pkgpart");
-#$req->cgi->redirect("../../edit/part_pkg.cgi?$pkgpart");
-$req->cgi->redirect("../../browse/part_pkg.cgi");
-
diff --git a/htdocs/edit/process/part_referral.cgi b/htdocs/edit/process/part_referral.cgi
deleted file mode 100755
index 08a4c01..0000000
--- a/htdocs/edit/process/part_referral.cgi
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/part_referral.cgi: Edit referrals (process form)
-#
-# ivan@sisd.com 98-feb-23
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::part_referral qw(fields);
-use FS::CGI qw(eidiot);
-use FS::CGI qw(eidiot);
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-my($refnum)=$req->param('refnum');
-
-my($new)=create FS::part_referral ( {
- map {
- $_, $req->param($_);
- } fields('part_referral')
-} );
-
-if ( $refnum ) {
- my($old)=qsearchs('part_referral',{'refnum'=>$refnum});
- eidiot("(Old) Record not found!") unless $old;
- my($error)=$new->replace($old);
- eidiot($error) if $error;
-} else {
- my($error)=$new->insert;
- eidiot($error) if $error;
-}
-
-$refnum=$new->getfield('refnum');
-$req->cgi->redirect("../../browse/part_referral.cgi");
-
diff --git a/htdocs/edit/process/part_svc.cgi b/htdocs/edit/process/part_svc.cgi
deleted file mode 100755
index 0f0fbc6..0000000
--- a/htdocs/edit/process/part_svc.cgi
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/part_svc.cgi: Edit service definitions (process form)
-#
-# ivan@sisd.com 97-nov-14
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::part_svc qw(fields);
-use FS::CGI qw(eidiot);
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-my($svcpart)=$req->param('svcpart');
-
-my($old)=qsearchs('part_svc',{'svcpart'=>$svcpart}) if $svcpart;
-
-my($new)=create FS::part_svc ( {
- map {
- $_, $req->param($_);
-# } qw(svcpart svc svcdb)
- } fields('part_svc')
-} );
-
-if ( $svcpart ) {
- my($error)=$new->replace($old);
- eidiot($error) if $error;
-} else {
- my($error)=$new->insert;
- eidiot($error) if $error;
- $svcpart=$new->getfield('svcpart');
-}
-
-#$req->cgi->redirect("../../view/part_svc.cgi?$svcpart");
-#$req->cgi->redirect("../../edit/part_svc.cgi?$svcpart");
-$req->cgi->redirect("../../browse/part_svc.cgi");
-
diff --git a/htdocs/edit/process/svc_acct.cgi b/htdocs/edit/process/svc_acct.cgi
deleted file mode 100755
index 8d77ba7..0000000
--- a/htdocs/edit/process/svc_acct.cgi
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/svc_acct.cgi: Add/edit a customer (process form)
-#
-# Usage: post form to:
-# http://server.name/path/svc_acct.cgi
-#
-# Note: Should br run setuid root as user nobody.
-#
-# ivan@voicenet.com 96-dec-18
-#
-# Changed /u to /u2
-# ivan@voicenet.com 97-may-6
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-17 - 21
-#
-# no FS::Search, FS::svc_acct creates FS::cust_svc record, used for adding
-# and editing ivan@sisd.com 98-mar-8
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'password' to '_password' because Pg6.3 reserves the password word
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::svc_acct;
-
-my($req) = new CGI::Request; # create form object
-&cgisuidsetup($req->cgi);
-
-$req->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
-my($svcnum)=$1;
-
-my($old)=qsearchs('svc_acct',{'svcnum'=>$svcnum}) if $svcnum;
-
-#unmunge popnum
-$req->param('popnum', (split(/:/, $req->param('popnum') ))[0] );
-
-#unmunge passwd
-if ( $req->param('_password') eq '*HIDDEN*' ) {
- $req->param('_password',$old->getfield('_password'));
-}
-
-my($new) = create FS::svc_acct ( {
- map {
- $_, $req->param($_);
- } qw(svcnum pkgnum svcpart username _password popnum uid gid finger dir
- shell quota slipip)
-} );
-
-if ( $svcnum ) {
- my($error) = $new->replace($old);
- &idiot($error) if $error;
-} else {
- my($error) = $new->insert;
- &idiot($error) if $error;
- $svcnum = $new->getfield('svcnum');
-}
-
-#no errors, view account
-$req->cgi->redirect("../../view/svc_acct.cgi?" . $svcnum );
-
-sub idiot {
- my($error)=@_;
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error adding/updating account</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error adding/updating account</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and submit the form again.
- </BODY>
-</HTML>
-END
- exit;
-}
-
diff --git a/htdocs/edit/process/svc_acct_pop.cgi b/htdocs/edit/process/svc_acct_pop.cgi
deleted file mode 100755
index 18d7940..0000000
--- a/htdocs/edit/process/svc_acct_pop.cgi
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/svc_acct_pop.cgi: Edit POP (process form)
-#
-# ivan@sisd.com 98-mar-8
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::svc_acct_pop qw(fields);
-use FS::CGI qw(eidiot);
-
-my($req)=new CGI::Request; # create form object
-
-&cgisuidsetup($req->cgi);
-
-my($popnum)=$req->param('popnum');
-
-my($old)=qsearchs('svc_acct_pop',{'popnum'=>$popnum}) if $popnum;
-
-my($new)=create FS::svc_acct_pop ( {
- map {
- $_, $req->param($_);
- } fields('svc_acct_pop')
-} );
-
-if ( $popnum ) {
- my($error)=$new->replace($old);
- eidiot($error) if $error;
-} else {
- my($error)=$new->insert;
- eidiot($error) if $error;
- $popnum=$new->getfield('popnum');
-}
-$req->cgi->redirect("../../browse/svc_acct_pop.cgi");
-
diff --git a/htdocs/edit/process/svc_acct_sm.cgi b/htdocs/edit/process/svc_acct_sm.cgi
deleted file mode 100755
index 9ad546b..0000000
--- a/htdocs/edit/process/svc_acct_sm.cgi
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/svc_acct_sm.cgi: Add/edit a mail alias (process form)
-#
-# Usage: post form to:
-# http://server.name/path/svc_acct_sm.cgi
-#
-# Note: Should br run setuid root as user nobody.
-#
-# lots of crufty stuff from svc_acct still in here, and modifications are (unelegantly) disabled.
-#
-# ivan@voicenet.com 97-jan-6
-#
-# enabled modifications
-#
-# ivan@voicenet.com 97-may-7
-#
-# fixed removal of cust_svc record on modifications!
-# ivan@voicenet.com 97-jun-5
-#
-# rewrite ivan@sisd.com 98-mar-15
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::svc_acct_sm;
-
-my($req)=new CGI::Request; # create form object
-cgisuidsetup($req->cgi);
-
-$req->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
-my($svcnum)=$1;
-
-my($old)=qsearchs('svc_acct_sm',{'svcnum'=>$svcnum}) if $svcnum;
-
-#unmunge domsvc and domuid
-$req->param('domsvc',(split(/:/, $req->param('domsvc') ))[0] );
-$req->param('domuid',(split(/:/, $req->param('domuid') ))[0] );
-
-my($new) = create FS::svc_acct_sm ( {
- map {
- ($_, scalar($req->param($_)));
- } qw(svcnum pkgnum svcpart domuser domuid domsvc)
-} );
-
-my($error);
-if ( $svcnum ) {
- $error = $new->replace($old);
-} else {
- $error = $new->insert;
- $svcnum = $new->getfield('svcnum');
-}
-
-unless ($error) {
- $req->cgi->redirect("../../view/svc_acct_sm.cgi?$svcnum");
-} else {
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error adding/editing mail alias</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error adding/editing mail alias</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and submit the form again.
- </BODY>
-</HTML>
-END
-
-}
-
diff --git a/htdocs/edit/process/svc_domain.cgi b/htdocs/edit/process/svc_domain.cgi
deleted file mode 100755
index 0782772..0000000
--- a/htdocs/edit/process/svc_domain.cgi
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/svc_domain.cgi: Add a domain (process form)
-#
-# Usage: post form to:
-# http://server.name/path/svc_domain.cgi
-#
-# Note: Should br run setuid root as user nobody.
-#
-# lots of yucky stuff in this one... bleachlkjhui!
-#
-# ivan@voicenet.com 97-jan-6
-#
-# kludged for new domain template 3.5
-# ivan@voicenet.com 97-jul-24
-#
-# moved internic bits to svc_domain.pm ivan@sisd.com 98-mar-14
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::svc_domain;
-
-#remove this to actually test the domains!
-$FS::svc_domain::whois_hack = 1;
-
-my($req) = new CGI::Request;
-&cgisuidsetup($req->cgi);
-
-$req->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
-my($svcnum)=$1;
-
-my($new) = create FS::svc_domain ( {
- map {
- $_, $req->param($_);
- } qw(svcnum pkgnum svcpart domain action purpose)
-} );
-
-my($error);
-if ($req->param('legal') ne "Yes") {
- $error = "Customer did not agree to be bound by NSI's ".
- qq!<A HREF="http://rs.internic.net/help/agreement.txt">!.
- "Domain Name Resgistration Agreement</A>";
-} elsif ($req->param('svcnum')) {
- $error="Can't modify a domain!";
-} else {
- $error=$new->insert;
- $svcnum=$new->svcnum;
-}
-
-unless ($error) {
- $req->cgi->redirect("../../view/svc_domain.cgi?$svcnum");
-} else {
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error adding domain</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error adding domain</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and submit the form again.
- </BODY>
-</HTML>
-END
-
-}
-
-
diff --git a/htdocs/edit/svc_acct.cgi b/htdocs/edit/svc_acct.cgi
deleted file mode 100755
index 61d0fdc..0000000
--- a/htdocs/edit/svc_acct.cgi
+++ /dev/null
@@ -1,191 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_acct.cgi: Add/edit account (output form)
-#
-# Usage: svc_acct.cgi {svcnum} | pkgnum{pkgnum}-svcpart{svcpart}
-# http://server.name/path/svc_acct.cgi? {svcnum} | pkgnum{pkgnum}-svcpart{svcpart}
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# ivan@voicenet.com 96-dec-18
-#
-# rewrite ivan@sisd.com 98-mar-8
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'password' to '_password' because Pg6.3 reserves the password word
-# bmccane@maxbaud.net 98-apr-3
-#
-# use conf/shells and dbdef username length ivan@sisd.com 98-jul-13
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use FS::UID qw(cgisuidsetup getotaker);
-use FS::Record qw(qsearch qsearchs);
-use FS::svc_acct qw(fields);
-
-my($shells)="/var/spool/freeside/conf/shells";
-open(SHELLS,$shells) or die "Can't open $shells: $!";
-my(@shells)=map {
- /^([\/\w]*)$/ or die "Illegal shell in conf/shells!";
- $1;
-} grep $_ !~ /^#/, <SHELLS>;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-my($action,$svcnum,$svc_acct,$pkgnum,$svcpart,$part_svc);
-
-if ( $QUERY_STRING =~ /^(\d+)$/ ) { #editing
-
- $svcnum=$1;
- $svc_acct=qsearchs('svc_acct',{'svcnum'=>$svcnum})
- or die "Unknown (svc_acct) svcnum!";
-
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
- or die "Unknown (cust_svc) svcnum!";
-
- $pkgnum=$cust_svc->pkgnum;
- $svcpart=$cust_svc->svcpart;
-
- $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
- die "No part_svc entry!" unless $part_svc;
-
- $action="Edit";
-
-} else { #adding
-
- $svc_acct=create FS::svc_acct({});
-
- foreach $_ (split(/-/,$QUERY_STRING)) {
- $pkgnum=$1 if /^pkgnum(\d+)$/;
- $svcpart=$1 if /^svcpart(\d+)$/;
- }
- $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
- die "No part_svc entry!" unless $part_svc;
-
- $svcnum='';
-
- #set gecos
- my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
- if ($cust_pkg) {
- my($cust_main)=qsearchs('cust_main',{'custnum'=> $cust_pkg->custnum } );
- $svc_acct->setfield('finger',
- $cust_main->getfield('first') . " " . $cust_main->getfield('last')
- ) ;
- }
-
- #set fixed and default fields from part_svc
- my($field);
- foreach $field ( fields('svc_acct') ) {
- if ( $part_svc->getfield('svc_acct__'. $field. '_flag') ne '' ) {
- $svc_acct->setfield($field,$part_svc->getfield('svc_acct__'. $field) );
- }
- }
-
- $action="Add";
-
-}
-
-my($svc)=$part_svc->getfield('svc');
-
-my($otaker)=getotaker;
-
-my($username,$password)=(
- $svc_acct->username,
- $svc_acct->_password ? "*HIDDEN*" : '',
-);
-
-my($ulen)=$svc_acct->dbdef_table->column('username')->length;
-my($ulen2)=$ulen+2;
-
-SendHeaders();
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>$action $svc account</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>$action $svc account</H1>
- </CENTER><HR>
- <FORM ACTION="process/svc_acct.cgi" METHOD=POST>
- <INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">
- <INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">
- <INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">
-Username:
-<INPUT TYPE="text" NAME="username" VALUE="$username" SIZE=$ulen2 MAXLENGTH=$ulen>
-<BR>Password:
-<INPUT TYPE="text" NAME="_password" VALUE="$password" SIZE=10 MAXLENGTH=8>
-(blank to generate)
-END
-
-#pop
-my($popnum)=$svc_acct->popnum || 0;
-if ( $part_svc->svc_acct__popnum_flag eq "F" ) {
- print qq!<INPUT TYPE="hidden" NAME="popnum" VALUE="$popnum">!;
-} else {
- print qq!<BR>POP: <SELECT NAME="popnum" SIZE=1><OPTION>\n!;
- my($svc_acct_pop);
- foreach $svc_acct_pop ( qsearch ('svc_acct_pop',{} ) ) {
- print "<OPTION", $svc_acct_pop->popnum == $popnum ? ' SELECTED' : '', ">",
- $svc_acct_pop->popnum, ": ",
- $svc_acct_pop->city, ", ",
- $svc_acct_pop->state,
- "(", $svc_acct_pop->ac, ")/",
- $svc_acct_pop->exch, "\n"
- ;
- }
- print "</SELECT>";
-}
-
-my($uid,$gid,$finger,$dir)=(
- $svc_acct->uid,
- $svc_acct->gid,
- $svc_acct->finger,
- $svc_acct->dir,
-);
-
-print <<END;
-<INPUT TYPE="hidden" NAME="uid" VALUE="$uid">
-<INPUT TYPE="hidden" NAME="gid" VALUE="$gid">
-<BR>GECOS: <INPUT TYPE="text" NAME="finger" VALUE="$finger">
-<INPUT TYPE="hidden" NAME="dir" VALUE="$dir">
-END
-
-my($shell)=$svc_acct->shell;
-if ( $part_svc->svc_acct__shell_flag eq "F" ) {
- print qq!<INPUT TYPE="hidden" NAME="shell" VALUE="$shell">!;
-} else {
- print qq!<BR>Shell: <SELECT NAME="shell" SIZE=1>!;
- my($etc_shell);
- foreach $etc_shell (@shells) {
- print "<OPTION", $etc_shell eq $shell ? ' SELECTED' : '', ">",
- $etc_shell, "\n";
- }
- print "</SELECT>";
-}
-
-my($quota,$slipip)=(
- $svc_acct->quota,
- $svc_acct->slipip,
-);
-
-print qq!<INPUT TYPE="hidden" NAME="quota" VALUE="$quota">!;
-
-if ( $part_svc->svc_acct__slipip_flag eq "F" ) {
- print qq!<INPUT TYPE="hidden" NAME="slipip" VALUE="$slipip">!;
-} else {
- print qq!<BR>IP: <INPUT TYPE="text" NAME="slipip" VALUE="$slipip">!;
-}
-
-#submit
-print qq!<P><CENTER><INPUT TYPE="submit" VALUE="Submit"></CENTER>!;
-
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
-
-
diff --git a/htdocs/edit/svc_acct_pop.cgi b/htdocs/edit/svc_acct_pop.cgi
deleted file mode 100755
index 46d803f..0000000
--- a/htdocs/edit/svc_acct_pop.cgi
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_acct_pop.cgi: Add/Edit pop (output form)
-#
-# ivan@sisd.com 98-mar-8
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::svc_acct_pop;
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-my($svc_acct_pop,$action);
-if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
- $svc_acct_pop=qsearchs('svc_acct_pop',{'popnum'=>$1});
- $action='Edit';
-} else { #adding
- $svc_acct_pop=create FS::svc_acct_pop {};
- $action='Add';
-}
-my($hashref)=$svc_acct_pop->hashref;
-
-print header("$action POP", menubar(
- 'Main Menu' => '../',
- 'View all POPs' => "../browse/svc_acct_pop.cgi",
-)), <<END;
- <FORM ACTION="process/svc_acct_pop.cgi" METHOD=POST>
-END
-
-#display
-
-print qq!<INPUT TYPE="hidden" NAME="popnum" VALUE="$hashref->{popnum}">!,
- "POP #", $hashref->{popnum} ? $hashref->{popnum} : "(NEW)";
-
-print <<END;
-<PRE>
-City <INPUT TYPE="text" NAME="city" SIZE=32 VALUE="$hashref->{city}">
-State <INPUT TYPE="text" NAME="state" SIZE=3 MAXLENGTH=2 VALUE="$hashref->{state}">
-Area Code <INPUT TYPE="text" NAME="ac" SIZE=4 MAXLENGTH=3 VALUE="$hashref->{ac}">
-Exchange <INPUT TYPE="text" NAME="exch" SIZE=4 MAXLENGTH=3 VALUE="$hashref->{exch}">
-</PRE>
-END
-
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{popnum} ? "Apply changes" : "Add POP",
- qq!">!;
-
-print <<END;
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/svc_acct_sm.cgi b/htdocs/edit/svc_acct_sm.cgi
deleted file mode 100755
index 45a8eb8..0000000
--- a/htdocs/edit/svc_acct_sm.cgi
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_acct_sm.cgi: Add/edit a mail alias (output form)
-#
-# Usage: svc_acct_sm.cgi {svcnum} | pkgnum{pkgnum}-svcpart{svcpart}
-# http://server.name/path/svc_acct_sm.cgi? {svcnum} | pkgnum{pkgnum}-svcpart{svcpart}
-#
-# use {svcnum} for edit, pkgnum{pkgnum}-svcpart{svcpart} for add
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# should error out in a more CGI-friendly way, and should have more error checking (sigh).
-#
-# ivan@voicenet.com 97-jan-5
-#
-# added debugging code; fixed CPU-sucking problem with trying to edit an (unaudited) mail alias (no pkgnum)
-#
-# ivan@voicenet.com 97-may-7
-#
-# fixed uid selection
-# ivan@voicenet.com 97-jun-4
-#
-# uid selection across _CUSTOMER_, not just _PACKAGE_
-#
-# ( i need to be rewritten with fast searches)
-#
-# ivan@voicenet.com 97-oct-3
-#
-# added fast searches in some of the places where it is sorely needed...
-# I see DBI::mysql in your future...
-# ivan@voicenet.com 97-oct-23
-#
-# rewrite ivan@sisd.com 98-mar-15
-#
-# /var/spool/freeside/conf/domain ivan@sisd.com 98-jul-26
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::svc_acct_sm qw(fields);
-
-my($conf_domain)="/var/spool/freeside/conf/domain";
-open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!";
-my($mydomain)=map {
- /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file
- $1
-} grep $_ !~ /^(#|$)/, <DOMAIN>;
-close DOMAIN;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-
-my($action,$svcnum,$svc_acct_sm,$pkgnum,$svcpart,$part_svc);
-if ( $QUERY_STRING =~ /^(\d+)$/ ) { #editing
-
- $svcnum=$1;
- $svc_acct_sm=qsearchs('svc_acct_sm',{'svcnum'=>$svcnum})
- or die "Unknown (svc_acct_sm) svcnum!";
-
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
- or die "Unknown (cust_svc) svcnum!";
-
- $pkgnum=$cust_svc->pkgnum;
- $svcpart=$cust_svc->svcpart;
-
- $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
- die "No part_svc entry!" unless $part_svc;
-
- $action="Edit";
-
-} else { #adding
-
- $svc_acct_sm=create FS::svc_acct_sm({});
-
- foreach $_ (split(/-/,$QUERY_STRING)) { #get & untaint pkgnum & svcpart
- $pkgnum=$1 if /^pkgnum(\d+)$/;
- $svcpart=$1 if /^svcpart(\d+)$/;
- }
- $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
- die "No part_svc entry!" unless $part_svc;
-
- $svcnum='';
-
- #set fixed and default fields from part_svc
- my($field);
- foreach $field ( fields('svc_acct_sm') ) {
- if ( $part_svc->getfield('svc_acct_sm__'. $field. '_flag') ne '' ) {
- $svc_acct_sm->setfield($field,$part_svc->getfield('svc_acct_sm__'. $field) );
- }
- }
-
- $action='Add';
-
-}
-
-my(%username,%domain);
-if ($pkgnum) {
-
- #find all possible uids (and usernames)
-
- my($u_part_svc,@u_acct_svcparts);
- foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) {
- push @u_acct_svcparts,$u_part_svc->getfield('svcpart');
- }
-
- my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
- my($custnum)=$cust_pkg->getfield('custnum');
- my($i_cust_pkg);
- foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
- my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
- my($acct_svcpart);
- foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding
- #record(s) in cust_svc ( for this
- #pkgnum ! )
- my($i_cust_svc);
- foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) {
- my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')});
- $username{$svc_acct->getfield('uid')}=$svc_acct->getfield('username');
- }
- }
- }
-
- #find all possible domains (and domsvc's)
-
- my($d_part_svc,@d_acct_svcparts);
- foreach $d_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_domain'}) ) {
- push @d_acct_svcparts,$d_part_svc->getfield('svcpart');
- }
-
- foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
- my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
- my($acct_svcpart);
- foreach $acct_svcpart (@d_acct_svcparts) {
- my($i_cust_svc);
- foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) {
- my($svc_domain)=qsearch('svc_domain',{'svcnum'=>$i_cust_svc->getfield('svcnum')});
- $domain{$svc_domain->getfield('svcnum')}=$svc_domain->getfield('domain');
- }
- }
- }
-
-} elsif ( $action eq 'Edit' ) {
-
- my($svc_acct)=qsearchs('svc_acct',{'uid'=>$svc_acct_sm->domuid});
- $username{$svc_acct_sm->uid} = $svc_acct->username;
-
- my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$svc_acct_sm->domsvc});
- $domain{$svc_acct_sm->domsvc} = $svc_domain->domain;
-
-} else {
- die "\$action eq Add, but \$pkgnum is null!\n";
-}
-
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Mail Alias $action</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Mail Alias $action</H1>
- </CENTER>
- <FORM ACTION="process/svc_acct_sm.cgi" METHOD=POST>
-END
-
-#display
-
- #formatting
- print "<PRE>";
-
-#svcnum
-print qq!<INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">!;
-print qq!Service #<FONT SIZE=+1><B>!, $svcnum ? $svcnum : " (NEW)", "</B></FONT>";
-
-#pkgnum
-print qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!;
-
-#svcpart
-print qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!;
-
-my($domuser,$domsvc,$domuid)=(
- $svc_acct_sm->domuser,
- $svc_acct_sm->domsvc,
- $svc_acct_sm->domuid,
-);
-
-#domuser
-print qq!\n\nMail to <INPUT TYPE="text" NAME="domuser" VALUE="$domuser"> <I>( * for anything )</I>!;
-
-#domsvc
-print qq! \@ <SELECT NAME="domsvc" SIZE=1>!;
-foreach $_ (keys %domain) {
- print "<OPTION", $_ eq $domsvc ? " SELECTED" : "", ">$_: $domain{$_}";
-}
-print "</SELECT>";
-
-#uid
-print qq!\nforwards to <SELECT NAME="domuid" SIZE=1>!;
-foreach $_ (keys %username) {
- print "<OPTION", ($_ eq $domuid) ? " SELECTED" : "", ">$_: $username{$_}";
-}
-print "</SELECT>\@$mydomain mailbox.";
-
- #formatting
- print "</PRE>\n";
-
-print qq!<CENTER><INPUT TYPE="submit" VALUE="Submit"></CENTER>!;
-
-print <<END;
-
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/edit/svc_domain.cgi b/htdocs/edit/svc_domain.cgi
deleted file mode 100755
index 0717a2c..0000000
--- a/htdocs/edit/svc_domain.cgi
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_domain.cgi: Add domain (output form)
-#
-# Usage: svc_domain.cgi pkgnum{pkgnum}-svcpart{svcpart}
-# http://server.name/path/svc_domain.cgi?pkgnum{pkgnum}-svcpart{svcpart}
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# ivan@voicenet.com 97-jan-5 -> 97-jan-6
-#
-# changes for domain template 3.5
-# ivan@voicenet.com 97-jul-24
-#
-# rewrite ivan@sisd.com 98-mar-14
-#
-# no GOV in instructions ivan@sisd.com 98-jul-17
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use FS::UID qw(cgisuidsetup getotaker);
-use FS::Record qw(qsearch qsearchs);
-use FS::svc_domain qw(fields);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-my($action,$svcnum,$svc_domain,$pkgnum,$svcpart,$part_svc);
-
-if ( $QUERY_STRING =~ /^(\d+)$/ ) { #editing
-
- $svcnum=$1;
- $svc_domain=qsearchs('svc_domain',{'svcnum'=>$svcnum})
- or die "Unknown (svc_domain) svcnum!";
-
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
- or die "Unknown (cust_svc) svcnum!";
-
- $pkgnum=$cust_svc->pkgnum;
- $svcpart=$cust_svc->svcpart;
-
- $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
- die "No part_svc entry!" unless $part_svc;
-
- $action="Edit";
-
-} else { #adding
-
- $svc_domain=create FS::svc_domain({});
-
- foreach $_ (split(/-/,$QUERY_STRING)) {
- $pkgnum=$1 if /^pkgnum(\d+)$/;
- $svcpart=$1 if /^svcpart(\d+)$/;
- }
- $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
- die "No part_svc entry!" unless $part_svc;
-
- $svcnum='';
-
- #set fixed and default fields from part_svc
- my($field);
- foreach $field ( fields('svc_domain') ) {
- if ( $part_svc->getfield('svc_domain__'. $field. '_flag') ne '' ) {
- $svc_domain->setfield($field,$part_svc->getfield('svc_domain__'. $field) );
- }
- }
-
- $action="Add";
-
-}
-
-my($svc)=$part_svc->getfield('svc');
-
-my($otaker)=getotaker;
-
-my($domain)=(
- $svc_domain->domain,
-);
-
-SendHeaders();
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>$action $svc</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>$action $svc</H1>
- </CENTER><HR>
- <FORM ACTION="process/svc_domain.cgi" METHOD=POST>
- <INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">
- <INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">
- <INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">
- <INPUT TYPE="radio" NAME="action" VALUE="N">New
- <BR><INPUT TYPE="radio" NAME="action" VALUE="M">Transfer
-
-<P>Customer agrees to be bound by NSI's
-<A HREF="http://rs.internic.net/help/agreement.txt">
-Domain Name Registration Agreement</A>
-<SELECT NAME="legal" SIZE=1><OPTION SELECTED>No<OPTION>Yes</SELECT>
-<P>Domain <INPUT TYPE="text" NAME="domain" VALUE="$domain" SIZE=28 MAXLENGTH=26>
-<BR>Purpose/Description: <INPUT TYPE="text" NAME="purpose" VALUE="" SIZE=64>
-<P><CENTER><INPUT TYPE="submit" VALUE="Submit"></CENTER>
-<UL>
- <LI>COM is for commercial, for-profit organziations
- <LI>ORG is for miscellaneous, usually, non-profit organizations
- <LI>NET is for network infrastructure machines and organizations
- <LI>EDU is for 4-year, degree granting institutions
-<!-- <LI>GOV is for United States federal government agencies
-!-->
-</UL>
-US state and local government agencies, schools, libraries, museums, and individuals should register under the US domain. See RFC 1480 for a complete description of the US domain
-and registration procedures.
-<P>GOV registrations are limited to top-level US Federal Government agencies (see RFC 1816).
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/images/mid-logo.gif b/htdocs/images/mid-logo.gif
deleted file mode 100644
index 4ceb3ad..0000000
--- a/htdocs/images/mid-logo.gif
+++ /dev/null
Binary files differ
diff --git a/htdocs/images/sisd.jpg b/htdocs/images/sisd.jpg
deleted file mode 100755
index 908a5ea..0000000
--- a/htdocs/images/sisd.jpg
+++ /dev/null
Binary files differ
diff --git a/htdocs/images/small-logo.gif b/htdocs/images/small-logo.gif
deleted file mode 100644
index a8e9c57..0000000
--- a/htdocs/images/small-logo.gif
+++ /dev/null
Binary files differ
diff --git a/htdocs/index.html b/htdocs/index.html
deleted file mode 100755
index de0667e..0000000
--- a/htdocs/index.html
+++ /dev/null
@@ -1,96 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>
- Freeside Main Menu
- </TITLE>
- </HEAD>
- <BODY BGCOLOR="#FFFFFF">
- <table>
- <tr><td>
- <P ALIGN=CENTER>
- <IMG BORDER=0 ALT="Silicon Interactive Software Design" SRC="images/small-logo.gif">
- </td><td>
- <center><font color="#ff0000" size=7>freeside main menu</font></center>
- </td></tr>
- </table>
- <A HREF="http://www.sisd.com/freeside">
- Information
- </A>
- <BR><A HREF="docs/">
- Documentation
- </A>
- </P>
- <HR>
- <H3><A HREF="edit/cust_main.cgi">New Customer</A></H3>
- <A NAME="search"><H3>Search</H3></A>
- <MENU>
- <LI><A HREF="search/cust_main.html">
- customers (by last name and/or company)
- </A>
- <LI><A HREF="search/cust_main-payinfo.html">customers (by credit card number)</A>
- <LI><A HREF="search/svc_acct.html">accounts (by username)</A>
- <LI><A HREF="search/svc_domain.html">domains (by domain)</A>
- <LI><A HREF="search/svc_acct_sm.html">mail aliases (by domain, and optionally username)</A>
- <LI><A HREF="search/cust_bill.html">invoices (by invoice number)</A>
- </MENU>
- <A NAME="browse"><H3>Browse</H3></A>
- <MENU>
- <LI><A HREF="search/cust_main.cgi?custnum">customers (by customer number)</A>
- <LI><A HREF="search/cust_main.cgi?last">customers (by last name)</A>
- <LI><A HREF="search/cust_main.cgi?company">customers (by company)</A>
- <LI><A HREF="search/cust_pkg.cgi?pkgnum">packages (by package number)</A>
- <LI><A HREF="search/cust_pkg.cgi?APKG_pkgnum">packages with unconfigured services (by package number)</A>
- <LI><A HREF="search/svc_acct.cgi?svcnum">accounts (by service number)</A>
- <LI><A HREF="search/svc_acct.cgi?username">accounts (by username)</A>
- <LI><A HREF="search/svc_acct.cgi?uid">accounts (by uid)</A>
- <LI><A HREF="search/svc_acct.cgi?UN_svcnum">unlinked accounts (by service number)</A>
- <LI><A HREF="search/svc_acct.cgi?UN_username">unlinked accounts (by username)</A>
- <LI><A HREF="search/svc_acct.cgi?UN_uid">unlinked accounts (by uid)</A>
- <LI><A HREF="search/svc_domain.cgi?svcnum">domains (by service number)</A>
- <LI><A HREF="search/svc_domain.cgi?domain">domains (by domain)</A>
- <LI><A HREF="search/svc_domain.cgi?UN_svcnum">unlinked domains (by service number)</A>
- <LI><A HREF="search/svc_domain.cgi?UN_domain">unlinked domains (by domain)</A>
- </MENU>
- <A NAME="admin"><H3>Administration</H3></a>
- <MENU>
- <LI><A HREF="browse/part_svc.cgi">
- View/Edit services
- </A>
- - Services are items you offer to your customers.
- <LI><A HREF="browse/part_pkg.cgi">
- View/Edit packages
- </A>
- - One or more services are grouped together into a package and
- given pricing information. Customers purchase packages, not
- services.
- <LI><A HREF="browse/agent_type.cgi">
- View/Edit agent types
- </A>
- - Agent types define groups of packages that you can then assign
- to particular agents.
- <LI><A HREF="browse/agent.cgi">
- View/Edit agents
- </A>
- - Agents are resellers of your service. Agents may be limited
- to a subset of your full offerings (via their agent type).
- <BR>
- <LI><A HREF="browse/part_referral.cgi">
- View/Edit referrals
- </A>
- - Where a customer heard about your service. Tracked for
- informational purposes.
- <BR>
- <LI><A HREF="browse/cust_main_county.cgi">
- View/Edit locales and tax rates
- </A>
- - Change tax rates by state, or break down a state into counties
- and assign different tax rates to each county.
- <BR>
- <LI><A HREF="browse/svc_acct_pop.cgi">
- View/Edit POPs
- </A>
- - Points of Presence
- </MENU>
- </FONT>
- </BODY>
-</HTML>
diff --git a/htdocs/misc/bill.cgi b/htdocs/misc/bill.cgi
deleted file mode 100755
index d41f6d1..0000000
--- a/htdocs/misc/bill.cgi
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# s/FS:Search/FS::Record/ and cgisuidsetup($cgi) ivan@sisd.com 98-mar-13
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::Bill;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint custnum
-$QUERY_STRING =~ /^(\d*)$/;
-my($custnum)=$1;
-my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum});
-die "Can't find customer!\n" unless $cust_main;
-
-# ?
-bless($cust_main,"FS::Bill");
-
-my($error);
-
-$error = $cust_main->bill(
-# 'time'=>$time
- );
-&idiot($error) if $error;
-
-$error = $cust_main->collect(
-# 'invoice-time'=>$time,
-# 'batch_card'=> 'yes',
- 'batch_card'=> 'no',
- 'report_badcard'=> 'yes',
- );
-&idiot($error) if $error;
-
-$cgi->redirect("../view/cust_main.cgi?$custnum#history");
-
-sub idiot {
- my($error)=@_;
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error billing customer</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error billing customer</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- </BODY>
-</HTML>
-END
-
- exit;
-
-}
-
diff --git a/htdocs/misc/cancel-unaudited.cgi b/htdocs/misc/cancel-unaudited.cgi
deleted file mode 100755
index 929274f..0000000
--- a/htdocs/misc/cancel-unaudited.cgi
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cancel-unaudited.cgi: Cancel an unaudited account
-#
-# Usage: cancel-unaudited.cgi svcnum
-# http://server.name/path/cancel-unaudited.cgi pkgnum
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# ivan@voicenet.com 97-apr-23
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-21
-#
-# Search->Record, cgisuidsetup($cgi) ivan@sids.com 98-mar-19
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::cust_svc;
-use FS::svc_acct;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint svcnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($svcnum)=$1;
-
-my($svc_acct) = qsearchs('svc_acct',{'svcnum'=>$svcnum});
-&idiot("Unknown svcnum!") unless $svc_acct;
-
-my($cust_svc) = qsearchs('cust_svc',{'svcnum'=>$svcnum});
-&idiot(qq!This account has already been audited. Cancel the
- <A HREF="../view/cust_pkg.cgi?! . $cust_svc->getfield('pkgnum') .
- qq!pkgnum"> package</A> instead.!)
- if $cust_svc->getfield('pkgnum') ne '';
-
-local $SIG{HUP} = 'IGNORE';
-local $SIG{INT} = 'IGNORE';
-local $SIG{QUIT} = 'IGNORE';
-local $SIG{TERM} = 'IGNORE';
-local $SIG{TSTP} = 'IGNORE';
-
-my($error);
-
-bless($svc_acct,"FS::svc_acct");
-$error = $svc_acct->cancel;
-&idiot($error) if $error;
-$error = $svc_acct->delete;
-&idiot($error) if $error;
-
-bless($cust_svc,"FS::cust_svc");
-$error = $cust_svc->delete;
-&idiot($error) if $error;
-
-$cgi->redirect("../");
-
-sub idiot {
- my($error)=@_;
- SendHeaders();
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error cancelling account</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Error cancelling account</H1>
- </CENTER>
- <HR>
- There has been an error cancelling this acocunt: $error
- </BODY>
- </HEAD>
-</HTML>
-END
- exit;
-}
-
diff --git a/htdocs/misc/cancel_pkg.cgi b/htdocs/misc/cancel_pkg.cgi
deleted file mode 100755
index 6702a03..0000000
--- a/htdocs/misc/cancel_pkg.cgi
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cancel_pkg.cgi: Cancel a package
-#
-# Usage: cancel_pkg.cgi pkgnum
-# http://server.name/path/cancel_pkg.cgi pkgnum
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# IT DOESN'T RUN THE APPROPRIATE PROGRAMS YET!!!!
-#
-# probably should generalize this to do cancels, suspensions, unsuspensions, etc.
-#
-# ivan@voicenet.com 97-jan-2
-#
-# still kludgy, but now runs /dbin/cancel $pkgnum
-# ivan@voicenet.com 97-feb-27
-#
-# doesn't run if pkgnum doesn't match regex
-# ivan@voicenet.com 97-mar-6
-#
-# now redirects to enter comments
-# ivan@voicenet.com 97-may-8
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-21
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::cust_pkg;
-use FS::CGI qw(idiot);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint pkgnum
-$QUERY_STRING =~ /^(\d+)$/ || die "Illegal pkgnum";
-my($pkgnum)=$1;
-
-my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
-
-bless($cust_pkg,'FS::cust_pkg');
-my($error)=$cust_pkg->cancel;
-idiot($error) if $error;
-
-$cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum'));
-
diff --git a/htdocs/misc/expire_pkg.cgi b/htdocs/misc/expire_pkg.cgi
deleted file mode 100755
index 1635166..0000000
--- a/htdocs/misc/expire_pkg.cgi
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# expire_pkg.cgi: Expire a package
-#
-# Usage: post form to:
-# http://server.name/path/expire_pkg.cgi
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# based on susp_pkg
-# ivan@voicenet.com 97-jul-29
-#
-# ivan@sisd.com 98-mar-17 FS::Search->FS::Record
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use Date::Parse;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::cust_pkg;
-
-my($req) = new CGI::Request;
-&cgisuidsetup($req->cgi);
-
-#untaint date & pkgnum
-
-my($date);
-if ( $req->param('date') ) {
- str2time($req->param('date')) =~ /^(\d+)$/ or die "Illegal date";
- $date=$1;
-} else {
- $date='';
-}
-
-$req->param('pkgnum') =~ /^(\d+)$/ or die "Illegal pkgnum";
-my($pkgnum)=$1;
-
-my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
-my(%hash)=$cust_pkg->hash;
-$hash{expire}=$date;
-my($new)=create FS::cust_pkg ( \%hash );
-my($error) = $new->replace($cust_pkg);
-&idiot($error) if $error;
-
-$req->cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum'));
-
-sub idiot {
- my($error)=@_;
- SendHeaders();
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error expiring package</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Error expiring package</H1>
- </CENTER>
- <HR>
- There has been an error expiring this package: $error
- </BODY>
- </HEAD>
-</HTML>
-END
- exit;
-}
-
diff --git a/htdocs/misc/link.cgi b/htdocs/misc/link.cgi
deleted file mode 100755
index d1db000..0000000
--- a/htdocs/misc/link.cgi
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# link: instead of adding a new account, link to an existing. (output form)
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# ivan@voicenet.com 97-feb-5
-#
-# rewrite ivan@sisd.com 98-mar-17
-#
-# can also link on some other fields now (about time) ivan@sisd.com 98-jun-24
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-
-my(%link_field)=(
- 'svc_acct' => 'username',
- 'svc_domain' => 'domain',
- 'svc_acct_sm' => '',
- 'svc_charge' => '',
- 'svc_wo' => '',
-);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-cgisuidsetup($cgi);
-
-my($pkgnum,$svcpart);
-foreach $_ (split(/-/,$QUERY_STRING)) { #get & untaint pkgnum & svcpart
- $pkgnum=$1 if /^pkgnum(\d+)$/;
- $svcpart=$1 if /^svcpart(\d+)$/;
-}
-
-my($part_svc) = qsearchs('part_svc',{'svcpart'=>$svcpart});
-my($svc) = $part_svc->getfield('svc');
-my($svcdb) = $part_svc->getfield('svcdb');
-my($link_field) = $link_field{$svcdb};
-
-CGI::Base::SendHeaders();
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Link to existing $svc account</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Link to existing $svc account</H1>
- </CENTER><HR>
- <FORM ACTION="process/link.cgi" METHOD=POST>
-END
-
-if ( $link_field ) {
- print <<END;
- <INPUT TYPE="hidden" NAME="svcnum" VALUE="">
- <INPUT TYPE="hidden" NAME="link_field" VALUE="$link_field">
- $link_field of existing service: <INPUT TYPE="text" NAME="link_value">
-END
-} else {
- print qq!Service # of existing service: <INPUT TYPE="text" NAME="svcnum" VALUE="">!;
-}
-
-print <<END;
-<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">
-<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">
-<P><CENTER><INPUT TYPE="submit" VALUE="Link"></CENTER>
- </FORM>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/misc/print-invoice.cgi b/htdocs/misc/print-invoice.cgi
deleted file mode 100755
index 084dcc1..0000000
--- a/htdocs/misc/print-invoice.cgi
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# just a kludge for now, since this duplicates in a way it shouldn't stuff from
-# Bill.pm (like $lpr) ivan@sisd.com 98-jun-16
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::Invoice;
-
-my($lpr) = "|lpr -h";
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint invnum
-$QUERY_STRING =~ /^(\d*)$/;
-my($invnum)=$1;
-my($cust_bill)=qsearchs('cust_bill',{'invnum'=>$invnum});
-die "Can't find invoice!\n" unless $cust_bill;
-
- bless($cust_bill,"FS::Invoice");
- open(LPR,$lpr) or die "Can't open $lpr: $!";
- print LPR $cust_bill->print_text; #( date )
- close LPR
- or die $! ? "Error closing $lpr: $!"
- : "Exit status $? from $lpr";
-
-my($custnum)=$cust_bill->getfield('custnum');
-
-$cgi->redirect("../view/cust_main.cgi?$custnum#history");
-
-sub idiot {
- my($error)=@_;
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error printing invoice</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error printing invoice</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- </BODY>
-</HTML>
-END
-
- exit;
-
-}
-
diff --git a/htdocs/misc/process/link.cgi b/htdocs/misc/process/link.cgi
deleted file mode 100755
index 23fb053..0000000
--- a/htdocs/misc/process/link.cgi
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/link.cgi: link to existing customer (process form)
-#
-# ivan@voicenet.com 97-feb-5
-#
-# rewrite ivan@sisd.com 98-mar-18
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# can also link on some other fields now (about time) ivan@sisd.com 98-jun-24
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::CGI qw(idiot);
-use FS::UID qw(cgisuidsetup);
-use FS::cust_svc;
-use FS::Record qw(qsearchs);
-
-my($req)=new CGI::Request; # create form object
-cgisuidsetup($req->cgi);
-
-#$req->import_names('R'); #import CGI variables into package 'R';
-
-$req->param('pkgnum') =~ /^(\d+)$/; my($pkgnum)=$1;
-$req->param('svcpart') =~ /^(\d+)$/; my($svcpart)=$1;
-
-$req->param('svcnum') =~ /^(\d*)$/; my($svcnum)=$1;
-unless ( $svcnum ) {
- my($part_svc) = qsearchs('part_svc',{'svcpart'=>$svcpart});
- my($svcdb) = $part_svc->getfield('svcdb');
- $req->param('link_field') =~ /^(\w+)$/; my($link_field)=$1;
- my($svc_acct)=qsearchs($svcdb,{$link_field => $req->param('link_value') });
- idiot("$link_field not found!") unless $svc_acct;
- $svcnum=$svc_acct->svcnum;
-}
-
-my($old)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
-die "svcnum not found!" unless $old;
-my($new)=create FS::cust_svc ({
- 'svcnum' => $svcnum,
- 'pkgnum' => $pkgnum,
- 'svcpart' => $svcpart,
-});
-
-my($error);
-$error = $new->replace($old);
-
-unless ($error) {
- #no errors, so let's view this customer.
- $req->cgi->redirect("../../view/cust_pkg.cgi?$pkgnum");
-} else {
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error</H4>
- </CENTER>
- Your update did not occur because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and submit the form again.
- </BODY>
-</HTML>
-END
-
-}
-
diff --git a/htdocs/misc/susp_pkg.cgi b/htdocs/misc/susp_pkg.cgi
deleted file mode 100755
index 7b23cae..0000000
--- a/htdocs/misc/susp_pkg.cgi
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# susp_pkg.cgi: Suspend a package
-#
-# Usage: susp_pkg.cgi pkgnum
-# http://server.name/path/susp_pkg.cgi pkgnum
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# probably should generalize this to do cancels, suspensions, unsuspensions, etc.
-#
-# ivan@voicenet.com 97-feb-27
-#
-# now redirects to enter comments
-# ivan@voicenet.com 97-may-8
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-21
-#
-# FS::Search -> FS::Record ivan@sisd.com 98-mar-17
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::cust_pkg;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint pkgnum
-$QUERY_STRING =~ /^(\d+)$/ || die "Illegal pkgnum";
-my($pkgnum)=$1;
-
-my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
-
-bless($cust_pkg,'FS::cust_pkg');
-my($error)=$cust_pkg->suspend;
-&idiot($error) if $error;
-
-$cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum'));
-
-sub idiot {
- my($error)=@_;
- SendHeaders();
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error suspending package</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Error suspending package</H1>
- </CENTER>
- <HR>
- There has been an error suspending this package: $error
- </BODY>
- </HEAD>
-</HTML>
-END
- exit;
-}
-
diff --git a/htdocs/misc/unsusp_pkg.cgi b/htdocs/misc/unsusp_pkg.cgi
deleted file mode 100755
index 2f340c6..0000000
--- a/htdocs/misc/unsusp_pkg.cgi
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# susp_pkg.cgi: Unsuspend a package
-#
-# Usage: susp_pkg.cgi pkgnum
-# http://server.name/path/susp_pkg.cgi pkgnum
-#
-# Note: Should be run setuid freeside as user nobody
-#
-# probably should generalize this to do cancels, suspensions, unsuspensions, etc.
-#
-# ivan@voicenet.com 97-feb-27
-#
-# now redirects to enter comments
-# ivan@voicenet.com 97-may-8
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-21
-#
-# FS::Search -> FS::Record ivan@sisd.com 98-mar-17
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::cust_pkg;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint pkgnum
-$QUERY_STRING =~ /^(\d+)$/ || die "Illegal pkgnum";
-my($pkgnum)=$1;
-
-my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
-
-bless($cust_pkg,'FS::cust_pkg');
-my($error)=$cust_pkg->unsuspend;
-&idiot($error) if $error;
-
-$cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum'));
-
-sub idiot {
- my($error)=@_;
- SendHeaders();
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error unsuspending package</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Error unsuspending package</H1>
- </CENTER>
- <HR>
- There has been an error unsuspending this package: $error
- </BODY>
- </HEAD>
-</HTML>
-END
- exit;
-}
-
diff --git a/htdocs/search/cust_bill.cgi b/htdocs/search/cust_bill.cgi
deleted file mode 100755
index 5be84b7..0000000
--- a/htdocs/search/cust_bill.cgi
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_bill.cgi: Search for invoices (process form)
-#
-# Usage: post form to:
-# http://server.name/path/cust_bill.cgi
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 97-apr-4
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-
-my($req)=new CGI::Request;
-cgisuidsetup($req->cgi);
-
-$req->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/;
-my($invnum)=$2;
-
-if ( qsearchs('cust_bill',{'invnum'=>$invnum}) ) {
- $req->cgi->redirect("../view/cust_bill.cgi?$invnum"); #redirect
-} else { #error
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Invoice Search Error</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H3>Invoice Search Error</H3>
- <HR>
- Invoice not found.
- </CENTER>
- </BODY>
-</HTML>
-END
-
-}
-
diff --git a/htdocs/search/cust_bill.html b/htdocs/search/cust_bill.html
deleted file mode 100755
index 4adb40e..0000000
--- a/htdocs/search/cust_bill.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Invoice Search</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Invoice Search</H1>
- </CENTER>
- <HR>
- <FORM ACTION="cust_bill.cgi" METHOD="post">
- Search for <B>invoice #</B>:
- <INPUT TYPE="text" NAME="invnum">
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
-
- <HR>
- </BODY>
-</HTML>
-
diff --git a/htdocs/search/cust_main-payinfo.html b/htdocs/search/cust_main-payinfo.html
deleted file mode 100755
index 92341ad..0000000
--- a/htdocs/search/cust_main-payinfo.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Customer Search</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Customer Search</H1>
- </CENTER>
- <HR>
- <FORM ACTION="cust_main.cgi" METHOD="post">
- Search for <B>Credit card #</B>:
- <INPUT TYPE="hidden" NAME="card_on" VALUE="TRUE">
- <INPUT TYPE="text" NAME="card">
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
- <HR>
- </BODY>
-</HTML>
-
diff --git a/htdocs/search/cust_main.cgi b/htdocs/search/cust_main.cgi
deleted file mode 100755
index 70ce991..0000000
--- a/htdocs/search/cust_main.cgi
+++ /dev/null
@@ -1,235 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# process/cust_main.cgi: Search for customers (process form)
-#
-# Usage: post form to:
-# http://server.name/path/cust_main.cgi
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 96-dec-12
-#
-# rewrite ivan@sisd.com 98-mar-4
-#
-# now does browsing too ivan@sisd.com 98-mar-6
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# display total, use FS::CGI ivan@sisd.com 98-jul-17
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use IO::Handle;
-use IPC::Open2;
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header idiot);
-
-my($fuzziness)=2; #fuzziness for fuzzy searches, see man agrep
- #0-4: 0=no fuzz, 4=very fuzzy (too much fuzz!)
-
-my($req)=new CGI::Request;
-&cgisuidsetup($req->cgi);
-
-my(@cust_main);
-my($sortby);
-
-my($query)=$req->cgi->var('QUERY_STRING');
-if ( $query eq 'custnum' ) {
- $sortby=\*custnum_sort;
- @cust_main=qsearch('cust_main',{});
-} elsif ( $query eq 'last' ) {
- $sortby=\*last_sort;
- @cust_main=qsearch('cust_main',{});
-} elsif ( $query eq 'company' ) {
- $sortby=\*company_sort;
- @cust_main=qsearch('cust_main',{});
-} else {
- &cardsearch if ($req->param('card_on') );
- &lastsearch if ($req->param('last_on') );
- &companysearch if ($req->param('company_on') );
-}
-
-if ( scalar(@cust_main) == 1 ) {
- $req->cgi->redirect("../view/cust_main.cgi?". $cust_main[0]->custnum);
- exit;
-} elsif ( scalar(@cust_main) == 0 ) {
- idiot "No matching customers found!\n";
- exit;
-} else {
-
- my($total)=scalar(@cust_main);
- CGI::Base::SendHeaders(); # one guess
- print header("Customer Search Results",''), <<END;
-
- $total matching customers found
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Cust. #</TH>
- <TH>Contact name</TH>
- <TH>Company</TH>
- </TR>
-END
-
- my($lines)=16;
- my($lcount)=$lines;
- my(%saw,$cust_main);
- foreach $cust_main (
- sort $sortby grep(!$saw{$_->custnum}++, @cust_main)
- ) {
- my($custnum,$last,$first,$company)=(
- $cust_main->custnum,
- $cust_main->getfield('last'),
- $cust_main->getfield('first'),
- $cust_main->company,
- );
- print <<END;
- <TR>
- <TD><A HREF="../view/cust_main.cgi?$custnum"><FONT SIZE=-1>$custnum</FONT></A></TD>
- <TD><FONT SIZE=-1>$last, $first</FONT></TD>
- <TD><FONT SIZE=-1>$company</FONT></TD>
- </TR>
-END
- if ($lcount-- == 0) { # lots of little tables instead of one big one
- $lcount=$lines;
- print <<END;
- </TABLE>
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Cust. #</TH>
- <TH>Contact name</TH>
- <TH>Company<TH>
- </TR>
-END
- }
- }
-
- print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
-}
-
-#
-
-sub last_sort {
- $a->getfield('last') cmp $b->getfield('last');
-}
-
-sub company_sort {
- $a->getfield('company') cmp $b->getfield('company');
-}
-
-sub custnum_sort {
- $a->getfield('custnum') <=> $b->getfield('custnum');
-}
-
-sub cardsearch {
-
- my($card)=$req->param('card');
- $card =~ s/\D//g;
- $card =~ /^(\d{13,16})$/ or do { idiot "Illegal card number\n"; exit; };
- my($payinfo)=$1;
-
- push @cust_main, qsearch('cust_main',{'payinfo'=>$payinfo, 'payby'=>'CARD'});
-
-}
-
-sub lastsearch {
- my(%last_type);
- foreach ( $req->param('last_type') ) {
- $last_type{$_}++;
- }
-
- $req->param('last_text') =~ /^([\w \,\.\-\']*)$/
- or do { idiot "Illegal last name"; exit; };
- my($last)=$1;
-
- if ( $last_type{'Exact'}
- && ! $last_type{'Fuzzy'}
- # && ! $last_type{'Sound-alike'}
- ) {
-
- push @cust_main, qsearch('cust_main',{'last'=>$last});
-
- } else {
-
- my(%last);
-
- my(@all_last)=map $_->getfield('last'), qsearch('cust_main',{});
- if ($last_type{'Fuzzy'}) {
- my($reader,$writer) = ( new IO::Handle, new IO::Handle );
- open2($reader,$writer,'agrep',"-$fuzziness",'-i','-k',
- substr($last,0,30));
- print $writer join("\n",@all_last),"\n";
- close $writer;
- while (<$reader>) {
- chop;
- $last{$_}++;
- }
- close $reader;
- }
-
- #if ($last_type{'Sound-alike'}) {
- #}
-
- foreach ( keys %last ) {
- push @cust_main, qsearch('cust_main',{'last'=>$_});
- }
-
- }
- $sortby=\*last_sort;
-}
-
-sub companysearch {
-
- my(%company_type);
- foreach ( $req->param('company_type') ) {
- $company_type{$_}++
- };
-
- $req->param('company_text') =~ /^([\w \,\.\-\']*)$/
- or do { idiot "Illegal company"; exit; };
- my($company)=$1;
-
- if ( $company_type{'Exact'}
- && ! $company_type{'Fuzzy'}
- # && ! $company_type{'Sound-alike'}
- ) {
-
- push @cust_main, qsearch('cust_main',{'company'=>$company});
-
- } else {
-
- my(%company);
- my(@all_company)=map $_->company, qsearch('cust_main',{});
-
- if ($company_type{'Fuzzy'}) {
- my($reader,$writer) = ( new IO::Handle, new IO::Handle );
- open2($reader,$writer,'agrep',"-$fuzziness",'-i','-k',
- substr($company,0,30));
- print $writer join("\n",@all_company),"\n";
- close $writer;
- while (<$reader>) {
- chop;
- $company{$_}++;
- }
- close $reader;
- }
-
- #if ($company_type{'Sound-alike'}) {
- #}
-
- foreach ( keys %company ) {
- push @cust_main, qsearch('cust_main',{'company'=>$_});
- }
-
- }
- $sortby=\*company_sort;
-
-}
diff --git a/htdocs/search/cust_main.html b/htdocs/search/cust_main.html
deleted file mode 100755
index 656943f..0000000
--- a/htdocs/search/cust_main.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Customer Search</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Customer Search</H1>
- </CENTER>
- <HR>
- <FORM ACTION="cust_main.cgi" METHOD="post">
- <INPUT TYPE="checkbox" NAME="last_on"> Search for <B>last name</B>:
- <INPUT TYPE="text" NAME="last_text">
- using search method(s): <SELECT NAME="last_type" MULTIPLE>
- <OPTION SELECTED>Fuzzy
- <OPTION>Exact
- </SELECT>
-
- <P><INPUT TYPE="checkbox" NAME="company_on"> Search for <B>company</B>:
- <INPUT TYPE="text" NAME="company_text">
- using search methods(s): <SELECT NAME="company_type" MULTIPLE>
- <OPTION SELECTED>Fuzzy
- <OPTION>Exact
- </SELECT>
-
- <P><INPUT TYPE="submit" VALUE="Search"> Note: Fuzzy searching can take a while. Please be patient.
-
- </FORM>
-
- <HR>Explanation of search methods:
- <UL>
- <LI><B>Fuzzy</B> - Searches for matches that are close to your text.
- <LI><B>Exact</B> - Finds exact matches only, but much faster than the other search methods.
- </UL>
- </BODY>
-</HTML>
-
diff --git a/htdocs/search/cust_pkg.cgi b/htdocs/search/cust_pkg.cgi
deleted file mode 100755
index 967068f..0000000
--- a/htdocs/search/cust_pkg.cgi
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_pkg.cgi: search/browse for packages
-#
-# based on search/svc_acct.cgi ivan@sisd.com 98-jul-17
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header idiot);
-
-my($req)=new CGI::Request;
-&cgisuidsetup($req->cgi);
-
-my(@cust_pkg,$sortby);
-
-my($query)=$req->cgi->var('QUERY_STRING');
-#this tree is a little bit redundant
-if ( $query eq 'pkgnum' ) {
- $sortby=\*pkgnum_sort;
- @cust_pkg=qsearch('cust_pkg',{});
-} elsif ( $query eq 'APKG_pkgnum' ) {
- $sortby=\*pkgnum_sort;
-
- #perhaps this should go in cust_pkg as a qsearch-like constructor?
- my($cust_pkg);
- foreach $cust_pkg (qsearch('cust_pkg',{})) {
- my($flag)=0;
- my($pkg_svc);
- PKG_SVC:
- foreach $pkg_svc (qsearch('pkg_svc',{ 'pkgpart' => $cust_pkg->pkgpart })) {
- if ( $pkg_svc->quantity
- > scalar(qsearch('cust_svc',{
- 'pkgnum' => $cust_pkg->pkgnum,
- 'svcpart' => $pkg_svc->svcpart,
- }))
- )
- {
- $flag=1;
- last PKG_SVC;
- }
- }
- push @cust_pkg, $cust_pkg if $flag;
- }
-} else {
- die "Empty QUERY_STRING!";
-}
-
-if ( scalar(@cust_pkg) == 1 ) {
- my($pkgnum)=$cust_pkg[0]->pkgnum;
- $req->cgi->redirect("../view/cust_pkg.cgi?$pkgnum");
- exit;
-} elsif ( scalar(@cust_pkg) == 0 ) { #error
- &idiot("No packages found");
- exit;
-} else {
- my($total)=scalar(@cust_pkg);
- CGI::Base::SendHeaders(); # one guess
- print header('Package Search Results',''), <<END;
- $total matching packages found
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Package #</TH>
- <TH>Customer #</TH>
- <TH>Name</TH>
- <TH>Company</TH>
- </TR>
-END
-
- my($lines)=16;
- my($lcount)=$lines;
- my(%saw,$cust_pkg);
- foreach $cust_pkg (
- sort $sortby grep(!$saw{$_->pkgnum}++, @cust_pkg)
- ) {
- my($cust_main)=qsearchs('cust_main',{'custnum'=>$cust_pkg->custnum});
- my($pkgnum,$custnum,$name,$company)=(
- $cust_pkg->pkgnum,
- $cust_main->custnum,
- $cust_main->last. ', '. $cust_main->first,
- $cust_main->company,
- );
- print <<END;
- <TR>
- <TD><A HREF="../view/cust_pkg.cgi?$pkgnum"><FONT SIZE=-1>$pkgnum</FONT></A></TD>
- <TD><FONT SIZE=-1>$custnum</FONT></TD>
- <TD><FONT SIZE=-1>$name</FONT></TD>
- <TD><FONT SIZE=-1>$company</FONT></TD>
- </TR>
-END
- if ($lcount-- == 0) { # lots of little tables instead of one big one
- $lcount=$lines;
- print <<END;
- </TABLE>
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Package #</TH>
- <TH>Customer #</TH>
- <TH>Name</TH>
- <TH>Company</TH>
- <TH>
- </TR>
-END
- }
- }
-
- print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
- exit;
-
-}
-
-sub pkgnum_sort {
- $a->getfield('pkgnum') <=> $b->getfield('pkgnum');
-}
-
diff --git a/htdocs/search/svc_acct.cgi b/htdocs/search/svc_acct.cgi
deleted file mode 100755
index 250a741..0000000
--- a/htdocs/search/svc_acct.cgi
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_acct.cgi: Search for customers (process form)
-#
-# Usage: post form to:
-# http://server.name/path/svc_acct.cgi
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# loosely (sp?) based on search/cust_main.cgi
-#
-# ivan@voicenet.com 96-jan-3 -> 96-jan-4
-#
-# rewrite (now does browsing too) ivan@sisd.com 98-mar-9
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# show unlinked accounts ivan@sisd.com 98-jun-22
-#
-# use FS::CGI, show total ivan@sisd.com 98-jul-17
-#
-# give service and customer info too ivan@sisd.com 98-aug-16
-
-use strict;
-use CGI::Request; # form processing module
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header idiot);
-
-my($req)=new CGI::Request; # create form object
-&cgisuidsetup($req->cgi);
-
-my(@svc_acct,$sortby);
-
-my($query)=$req->cgi->var('QUERY_STRING');
-#this tree is a little bit redundant
-if ( $query eq 'svcnum' ) {
- $sortby=\*svcnum_sort;
- @svc_acct=qsearch('svc_acct',{});
-} elsif ( $query eq 'username' ) {
- $sortby=\*username_sort;
- @svc_acct=qsearch('svc_acct',{});
-} elsif ( $query eq 'uid' ) {
- $sortby=\*uid_sort;
- @svc_acct=grep $_->uid ne '', qsearch('svc_acct',{});
-} elsif ( $query eq 'UN_svcnum' ) {
- $sortby=\*svcnum_sort;
- @svc_acct = grep qsearchs('cust_svc',{
- 'svcnum' => $_->svcnum,
- 'pkgnum' => '',
- }), qsearch('svc_acct',{});
-} elsif ( $query eq 'UN_username' ) {
- $sortby=\*username_sort;
- @svc_acct = grep qsearchs('cust_svc',{
- 'svcnum' => $_->svcnum,
- 'pkgnum' => '',
- }), qsearch('svc_acct',{});
-} elsif ( $query eq 'UN_uid' ) {
- $sortby=\*uid_sort;
- @svc_acct = grep qsearchs('cust_svc',{
- 'svcnum' => $_->svcnum,
- 'pkgnum' => '',
- }), qsearch('svc_acct',{});
-} else {
- &usernamesearch;
-}
-
-if ( scalar(@svc_acct) == 1 ) {
- my($svcnum)=$svc_acct[0]->svcnum;
- $req->cgi->redirect("../view/svc_acct.cgi?$svcnum"); #redirect
- exit;
-} elsif ( scalar(@svc_acct) == 0 ) { #error
- idiot("Account not found");
- exit;
-} else {
- my($total)=scalar(@svc_acct);
- CGI::Base::SendHeaders(); # one guess
- print header("Account Search Results",''), <<END;
- $total matching accounts found
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Service #</TH>
- <TH>Username</TH>
- <TH>UID</TH>
- <TH>Service</TH>
- <TH>Customer #</TH>
- <TH>Contact name</TH>
- <TH>Company</TH>
- </TR>
-END
-
- my($lines)=16;
- my($lcount)=$lines;
- my(%saw,$svc_acct);
- foreach $svc_acct (
- sort $sortby grep(!$saw{$_->svcnum}++, @svc_acct)
- ) {
- my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct->svcnum })
- or die "No cust_svc record for svcnum ". $svc_acct->svcnum;
- my $part_svc = qsearchs('part_svc', { 'svcpart' => $cust_svc->svcpart })
- or die "No part_svc record for svcpart ". $cust_svc->svcpart;
- my($cust_pkg,$cust_main);
- if ( $cust_svc->pkgnum ) {
- $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc->pkgnum })
- or die "No cust_pkg record for pkgnum ". $cust_svc->pkgnum;
- $cust_main = qsearchs('cust_main', { 'custnum' => $cust_pkg->custnum })
- or die "No cust_main record for custnum ". $cust_pkg->custnum;
- }
- my($svcnum,$username,$uid,$svc,$custnum,$last,$first,$company)=(
- $svc_acct->svcnum,
- $svc_acct->getfield('username'),
- $svc_acct->getfield('uid'),
- $part_svc->svc,
- $cust_svc->pkgnum ? $cust_main->custnum : '',
- $cust_svc->pkgnum ? $cust_main->getfield('last') : '',
- $cust_svc->pkgnum ? $cust_main->getfield('first') : '',
- $cust_svc->pkgnum ? $cust_main->company : '',
- );
- my($pcustnum) = $custnum
- ? "<A HREF=\"../view/cust_main.cgi?$custnum\"><FONT SIZE=-1>$custnum</FONT></A>"
- : "<I>(unlinked)</I>"
- ;
- my($pname) = $custnum ? "$last, $first" : '';
- print <<END;
- <TR>
- <TD><A HREF="../view/svc_acct.cgi?$svcnum"><FONT SIZE=-1>$svcnum</FONT></A></TD>
- <TD><FONT SIZE=-1>$username</FONT></TD>
- <TD><FONT SIZE=-1>$uid</FONT></TD>
- <TD><FONT SIZE=-1>$svc</FONT></TH>
- <TD><FONT SIZE=-1>$pcustnum</FONT></TH>
- <TD><FONT SIZE=-1>$pname<FONT></TH>
- <TD><FONT SIZE=-1>$company</FONT></TH>
- </TR>
-END
- if ($lcount-- == 0) { # lots of little tables instead of one big one
- $lcount=$lines;
- print <<END;
- </TABLE>
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Service #</TH>
- <TH>Userame</TH>
- <TH>UID</TH>
- <TH>Service</TH>
- <TH>Customer #</TH>
- <TH>Contact name</TH>
- <TH>Company</TH>
- </TR>
-END
- }
- }
-
- print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
- exit;
-
-}
-
-sub svcnum_sort {
- $a->getfield('svcnum') <=> $b->getfield('svcnum');
-}
-
-sub username_sort {
- $a->getfield('username') cmp $b->getfield('username');
-}
-
-sub uid_sort {
- $a->getfield('uid') <=> $b->getfield('uid');
-}
-
-sub usernamesearch {
-
- $req->param('username') =~ /^([\w\d\-]{2,8})$/; #untaint username_text
- my($username)=$1;
-
- @svc_acct=qsearch('svc_acct',{'username'=>$username});
-
-}
-
-
diff --git a/htdocs/search/svc_acct.html b/htdocs/search/svc_acct.html
deleted file mode 100755
index 91291be..0000000
--- a/htdocs/search/svc_acct.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Account Search</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Account Search</H1>
- </CENTER>
- <HR>
- <FORM ACTION="svc_acct.cgi" METHOD="post">
- Search for <B>username</B>:
- <INPUT TYPE="text" NAME="username">
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
-
- <HR>
- </BODY>
-</HTML>
-
diff --git a/htdocs/search/svc_acct_sm.cgi b/htdocs/search/svc_acct_sm.cgi
deleted file mode 100755
index 3b1a4cf..0000000
--- a/htdocs/search/svc_acct_sm.cgi
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_acct_sm.cgi: Search for domains (process form)
-#
-# Usage: post form to:
-# http://server.name/path/svc_domain.cgi
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 96-mar-5
-#
-# need to look at table in results to make it more readable
-#
-# ivan@voicenet.com
-#
-# rewrite ivan@sisd.com 98-mar-15
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-
-my($conf_domain)="/var/spool/freeside/conf/domain";
-open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!";
-my($mydomain)=map {
- /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file
- $1
-} grep $_ !~ /^(#|$)/, <DOMAIN>;
-close DOMAIN;
-
-my($req)=new CGI::Request; # create form object
-&cgisuidsetup($req->cgi);
-
-$req->param('domuser') =~ /^([a-z0-9_\-]{0,32})$/;
-my($domuser)=$1;
-
-$req->param('domain') =~ /^([\w\-\.]+)$/ or die "Illegal domain";
-my($svc_domain)=qsearchs('svc_domain',{'domain'=>$1})
- or die "Unknown domain";
-my($domsvc)=$svc_domain->svcnum;
-
-my(@svc_acct_sm);
-if ($domuser) {
- @svc_acct_sm=qsearch('svc_acct_sm',{
- 'domuser' => $domuser,
- 'domsvc' => $domsvc,
- });
-} else {
- @svc_acct_sm=qsearch('svc_acct_sm',{'domsvc' => $domsvc});
-}
-
-if ( scalar(@svc_acct_sm) == 1 ) {
- my($svcnum)=$svc_acct_sm[0]->svcnum;
- $req->cgi->redirect("../view/svc_acct_sm.cgi?$svcnum"); #redirect
-} elsif ( scalar(@svc_acct_sm) > 1 ) {
- CGI::Base::SendHeaders();
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Mail Alias Search Results</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Mail Alias Search Results</H4>
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Mail to<BR><FONT SIZE=-2>(click here to view mail alias)</FONT></TH>
- <TH>Forwards to<BR><FONT SIZE=-2>(click here to view account)</FONT></TH>
- </TR>
-END
-
- my($svc_acct_sm);
- foreach $svc_acct_sm (@svc_acct_sm) {
- my($svcnum,$domuser,$domuid,$domsvc)=(
- $svc_acct_sm->svcnum,
- $svc_acct_sm->domuser,
- $svc_acct_sm->domuid,
- $svc_acct_sm->domsvc,
- );
- my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$domsvc});
- my($domain)=$svc_domain->domain;
- my($svc_acct)=qsearchs('svc_acct',{'uid'=>$domuid});
- my($username)=$svc_acct->username;
- my($svc_acct_svcnum)=$svc_acct->svcnum;
-
- print <<END;
-<TR>\n <TD> <A HREF="../view/svc_acct_sm.cgi?$svcnum">
-END
-
- print '', ( ($domuser eq '*') ? "<I>(anything)</I>" : $domuser );
-
- print <<END;
-\@$domain</A> </TD>\n
-<TD> <A HREF="../view/svc_acct.cgi?$svc_acct_svcnum">$username\@$mydomain</A> </TD>\n </TR>\n
-END
-
- }
-
- print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
-} else { #error
- CGI::Base::SendHeaders(); # one guess
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Mail Alias Search Error</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H3>Mail Alias Search Error</H3>
- <HR>
- Mail Alias not found.
- </CENTER>
- </BODY>
-</HTML>
-END
-
-}
-
diff --git a/htdocs/search/svc_acct_sm.html b/htdocs/search/svc_acct_sm.html
deleted file mode 100755
index 0719856..0000000
--- a/htdocs/search/svc_acct_sm.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Mail Alias Search</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Mail Alias Search</H1>
- </CENTER>
- <HR>
- <FORM ACTION="svc_acct_sm.cgi" METHOD="post">
- Search for <B>mail alias</B>:
- <INPUT TYPE="text" NAME="domuser"><FONT SIZE=-1>(opt.)</FONT> @
- <INPUT TYPE="text" NAME="domain"><FONT SIZE=-1>(req.)</FONT>
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
-
- <HR>
-
- </BODY>
-</HTML>
-
diff --git a/htdocs/search/svc_domain.cgi b/htdocs/search/svc_domain.cgi
deleted file mode 100755
index d527703..0000000
--- a/htdocs/search/svc_domain.cgi
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# svc_domain.cgi: Search for domains (process form)
-#
-# Usage: post form to:
-# http://server.name/path/svc_domain.cgi
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 97-mar-5
-#
-# rewrite ivan@sisd.com 98-mar-14
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# display total, use FS::CGI now does browsing too ivan@sisd.com 98-jul-17
-
-use strict;
-use CGI::Request;
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-use FS::CGI qw(header idiot);
-
-my($req)=new CGI::Request;
-&cgisuidsetup($req->cgi);
-
-my(@svc_domain);
-my($sortby);
-
-my($query)=$req->cgi->var('QUERY_STRING');
-if ( $query eq 'svcnum' ) {
- $sortby=\*svcnum_sort;
- @svc_domain=qsearch('svc_domain',{});
-} elsif ( $query eq 'domain' ) {
- $sortby=\*domain_sort;
- @svc_domain=qsearch('svc_domain',{});
-} elsif ( $query eq 'UN_svcnum' ) {
- $sortby=\*svcnum_sort;
- @svc_domain = grep qsearchs('cust_svc',{
- 'svcnum' => $_->svcnum,
- 'pkgnum' => '',
- }), qsearch('svc_domain',{});
-} elsif ( $query eq 'UN_domain' ) {
- $sortby=\*domain_sort;
- @svc_domain = grep qsearchs('cust_svc',{
- 'svcnum' => $_->svcnum,
- 'pkgnum' => '',
- }), qsearch('svc_domain',{});
-} else {
- $req->param('domain') =~ /^([\w\-\.]+)$/;
- my($domain)=$1;
- push @svc_domain, qsearchs('svc_domain',{'domain'=>$domain});
-}
-
-if ( scalar(@svc_domain) == 1 ) {
- $req->cgi->redirect("../view/svc_domain.cgi?". $svc_domain[0]->svcnum);
- exit;
-} elsif ( scalar(@svc_domain) == 0 ) {
- idiot "No matching domains found!\n";
- exit;
-} else {
- CGI::Base::SendHeaders(); # one guess
-
- my($total)=scalar(@svc_domain);
- CGI::Base::SendHeaders(); # one guess
- print header("Domain Search Results",''), <<END;
-
- $total matching domains found
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Service #</TH>
- <TH>Domain</TH>
- <TH></TH>
- </TR>
-END
-
- my($lines)=16;
- my($lcount)=$lines;
- my(%saw,$svc_domain);
- foreach $svc_domain (
- sort $sortby grep(!$saw{$_->svcnum}++, @svc_domain)
- ) {
- my($svcnum,$domain)=(
- $svc_domain->svcnum,
- $svc_domain->domain,
- );
- my($malias);
- if ( qsearch('svc_acct_sm',{'domsvc'=>$svcnum}) ) {
- $malias=(
- qq|<FORM ACTION="svc_acct_sm.cgi" METHOD="post">|.
- qq|<INPUT TYPE="hidden" NAME="domuser" VALUE="">|.
- qq|<INPUT TYPE="hidden" NAME="domain" VALUE="$domain">|.
- qq|<INPUT TYPE="submit" VALUE="(mail aliases)">|.
- qq|</FORM>|
- );
- } else {
- $malias='';
- }
- print <<END;
- <TR>
- <TD><A HREF="../view/svc_domain.cgi?$svcnum"><FONT SIZE=-1>$svcnum</FONT></A></TD>
- <TD><FONT SIZE=-1>$domain</FONT></TD>
- <TD><FONT SIZE=-1>$malias</FONT></TD>
- </TR>
-END
- if ($lcount-- == 0) { # lots of little tables instead of one big one
- $lcount=$lines;
- print <<END;
- </TABLE>
- <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
- <TR>
- <TH>Service #</TH>
- <TH>Domain</TH>
- <TH></TH>
- </TR>
-END
- }
- }
-
- print <<END;
- </TABLE>
- </CENTER>
- </BODY>
-</HTML>
-END
-
-}
-
-sub svcnum_sort {
- $a->getfield('svcnum') <=> $b->getfield('svcnum');
-}
-
-sub domain_sort {
- $a->getfield('domain') cmp $b->getfield('doimain');
-}
-
-
diff --git a/htdocs/search/svc_domain.html b/htdocs/search/svc_domain.html
deleted file mode 100755
index 533743b..0000000
--- a/htdocs/search/svc_domain.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<HTML>
- <HEAD>
- <TITLE>Domain Search</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Domain Search</H1>
- </CENTER>
- <HR>
- <FORM ACTION="svc_domain.cgi" METHOD="post">
- Search for <B>domain</B>:
- <INPUT TYPE="text" NAME="domain">
-
- <P><INPUT TYPE="submit" VALUE="Search">
-
- </FORM>
-
- <HR>
-
- </BODY>
-</HTML>
-
diff --git a/htdocs/view/cust_bill.cgi b/htdocs/view/cust_bill.cgi
deleted file mode 100755
index 96101d0..0000000
--- a/htdocs/view/cust_bill.cgi
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# Usage: cust_bill.cgi invnum
-# http://server.name/path/cust_bill.cgi?invnum
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# this is a quick & ugly hack which does little more than add some formatting to the ascii output from /dbin/print-invoice
-#
-# ivan@voicenet.com 96-dec-05
-#
-# added navigation bar
-# ivan@voicenet.com 97-jan-30
-#
-# now uses Invoice.pm
-# ivan@voicenet.com 97-jun-30
-#
-# what to do if cust_bill search errors?
-# ivan@voicenet.com 97-jul-7
-#
-# s/FS::Search/FS::Record/; $cgisuidsetup($cgi); ivan@sisd.com 98-mar-14
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# also print 'printed' field ivan@sisd.com 98-jul-10
-
-use strict;
-use IO::File;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-use FS::Invoice;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint invnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($invnum)=$1;
-
-my($cust_bill) = qsearchs('cust_bill',{'invnum'=>$invnum});
-die "Invoice #$invnum not found!" unless $cust_bill;
-my($custnum) = $cust_bill->getfield('custnum');
-
-my($printed) = $cust_bill->printed;
-
-SendHeaders(); # one guess.
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Invoice View</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Invoice View</H1>
- <A HREF="../view/cust_main.cgi?$custnum">View this customer (#$custnum)</A> | <A HREF="../">Main menu</A>
- </CENTER><HR>
- <BASEFONT SIZE=3>
- <CENTER>
- <A HREF="../edit/cust_pay.cgi?$invnum">Enter payments (check/cash) against this invoice</A>
- <BR><A HREF="../misc/print-invoice.cgi?$invnum">Reprint this invoice</A>
- <BR><BR>(Printed $printed times)
- </CENTER>
- <FONT SIZE=-1><PRE>
-END
-
-bless($cust_bill,"FS::Invoice");
-print $cust_bill->print_text;
-
- #formatting
- print <<END;
- </PRE></FONT>
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/view/cust_main.cgi b/htdocs/view/cust_main.cgi
deleted file mode 100755
index ca5fcd9..0000000
--- a/htdocs/view/cust_main.cgi
+++ /dev/null
@@ -1,336 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_main.cgi: View a customer
-#
-# Usage: cust_main.cgi custnum
-# http://server.name/path/cust_main.cgi?custnum
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# the payment history section could use some work, see below
-#
-# ivan@voicenet.com 96-nov-29 -> 96-dec-11
-#
-# added navigation bar (go to main menu ;)
-# ivan@voicenet.com 97-jan-30
-#
-# changes to the way credits/payments are applied (the links are here).
-# ivan@voicenet.com 97-apr-21
-#
-# added debugging code to diagnose CPU sucking problem.
-# ivan@voicenet.com 97-may-19
-#
-# CPU sucking problem was in comment code? fixed?
-# ivan@voicenet.com 97-may-22
-#
-# rewrote for new API
-# ivan@voicenet.com 97-jul-22
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'day' to 'daytime' because Pg6.3 reserves the day word
-# bmccane@maxbaud.net 98-apr-3
-#
-# lose background, FS::CGI ivan@sisd.com 98-sep-2
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use CGI::Carp qw(fatalsToBrowser);
-use Date::Format;
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs qsearch);
-use FS::CGI qw(header menubar);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-SendHeaders(); # one guess.
-print header("Customer View", menubar(
- 'Main Menu' => '../',
-)),<<END;
- <BASEFONT SIZE=3>
-END
-
-#untaint custnum & get customer record
-$QUERY_STRING =~ /^(\d+)$/;
-my($custnum)=$1;
-my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum});
-die "Customer not found!" unless $cust_main;
-my($hashref)=$cust_main->hashref;
-
-#custnum
-print "<FONT SIZE=+1><CENTER>Customer #<B>$custnum</B></CENTER></FONT>",
- qq!<CENTER><A HREF="#cust_main">Customer Information</A> | !,
- qq!<A HREF="#cust_comments">Comments</A> | !,
- qq!<A HREF="#cust_pkg">Packages</A> | !,
- qq!<A HREF="#history">Payment History</A> </CENTER>!;
-
-#bill now linke
-print qq!<HR><CENTER><A HREF="../misc/bill.cgi?$custnum">!,
- qq!Bill this customer now</A></CENTER>!;
-
-#formatting
-print qq!<HR><A NAME="cust_main"><CENTER><FONT SIZE=+1>Customer Information!,
- qq!</FONT>!,
- qq!<BR><A HREF="../edit/cust_main.cgi?$custnum!,
- qq!">Edit this information</A></CENTER><FONT SIZE=-1>!;
-
-#agentnum
-my($agent)=qsearchs('agent',{
- 'agentnum' => $cust_main->getfield('agentnum')
-} );
-die "Agent not found!" unless $agent;
-print "<BR>Agent #<B>" , $agent->getfield('agentnum') , ": " ,
- $agent->getfield('agent') , "</B>";
-
-#refnum
-my($referral)=qsearchs('part_referral',{'refnum' => $cust_main->refnum});
-die "Referral not found!" unless $referral;
-print "<BR>Referral #<B>", $referral->refnum, ": ",
- $referral->referral, "<\B>";
-
-#last, first
-print "<P><B>", $hashref->{'last'}, ", ", $hashref->{first}, "</B>";
-
-#ss
-print " (SS# <B>", $hashref->{ss}, "</B>)" if $hashref->{ss};
-
-#company
-print "<BR><B>", $hashref->{company}, "</B>" if $hashref->{company};
-
-#address1
-print "<BR><B>", $hashref->{address1}, "</B>";
-
-#address2
-print "<BR><B>", $hashref->{address2}, "</B>" if $hashref->{address2};
-
-#city
-print "<BR><B>", $hashref->{city}, "</B>";
-
-#county
-print " (<B>", $hashref->{county}, "</B> county)" if $hashref->{county};
-
-#state
-print ",<B>", $hashref->{state}, "</B>";
-
-#zip
-print " <B>", $hashref->{zip}, "</B>";
-
-#country
-print "<BR><B>", $hashref->{country}, "</B>"
- unless $hashref->{country} eq "US";
-
-#daytime
-print "<P><B>", $hashref->{daytime}, "</B>" if $hashref->{daytime};
-print " (Day)" if $hashref->{daytime} && $hashref->{night};
-
-#night
-print "<BR><B>", $hashref->{night}, "</B>" if $hashref->{night};
-print " (Night)" if $hashref->{daytime} && $hashref->{night};
-
-#fax
-print "<BR><B>", $hashref->{fax}, "</B> (Fax)" if $hashref->{fax};
-
-#payby/payinfo/paydate/payname
-if ($hashref->{payby} eq "CARD") {
- print "<P>Card #<B>", $hashref->{payinfo}, "</B> Exp. <B>",
- $hashref->{paydate}, "</B>";
- print " (<B>", $hashref->{payname}, "</B>)" if $hashref->{payname};
-} elsif ($hashref->{payby} eq "BILL") {
- print "<P>Bill";
- print " on P.O. #<B>", $hashref->{payinfo}, "</B>"
- if $hashref->{payinfo};
- print " until <B>", $hashref->{paydate}, "</B>"
- if $hashref->{paydate};
- print " to <B>", $hashref->{payname}, "</B> at above address"
- if $hashref->{payname};
-} elsif ($hashref->{payby} eq "COMP") {
- print "<P>Access complimentary";
- print " courtesy of <B>", $hashref->{payinfo}, "</B>"
- if $hashref->{payinfo};
- print " until <B>", $hashref->{paydate}, "</B>"
- if $hashref->{paydate};
-} else {
- print "Unknown payment type ", $hashref->{payby}, "!";
-}
-
-#tax
-print "<BR>(Tax exempt)" if $hashref->{tax};
-
-#otaker
-print "<P>Order taken by <B>", $hashref->{otaker}, "</B>";
-
-#formatting
-print qq!<HR><FONT SIZE=+1><A NAME="cust_pkg"><CENTER>Packages</A></FONT>!,
- qq!<BR>Click on package number to view/edit package.!,
- qq!<BR><A HREF="../edit/cust_pkg.cgi?$custnum">Add/Edit packages</A>!,
- qq!</CENTER><BR>!;
-
-#display packages
-
-#formatting
-print qq!<CENTER><TABLE BORDER=4>\n!,
- qq!<TR><TH ROWSPAN=2>#</TH><TH ROWSPAN=2>Package</TH><TH COLSPAN=5>!,
- qq!Dates</TH></TR>\n!,
- qq!<TR><TH><FONT SIZE=-1>Setup</FONT></TH><TH>!,
- qq!<FONT SIZE=-1>Next bill</FONT>!,
- qq!</TH><TH><FONT SIZE=-1>Susp.</FONT></TH><TH><FONT SIZE=-1>Expire!,
- qq!</FONT></TH>!,
- qq!<TH><FONT SIZE=-1>Cancel</FONT></TH>!,
- qq!</TR>\n!;
-
-#get package info
-my(@packages)=qsearch('cust_pkg',{'custnum'=>$custnum});
-my($package);
-foreach $package (@packages) {
- my($pref)=$package->hashref;
- my($part_pkg)=qsearchs('part_pkg',{
- 'pkgpart' => $pref->{pkgpart}
- } );
- print qq!<TR><TD><FONT SIZE=-1><A HREF="../view/cust_pkg.cgi?!,
- $pref->{pkgnum}, qq!">!,
- $pref->{pkgnum}, qq!</A></FONT></TD>!,
- "<TD><FONT SIZE=-1>", $part_pkg->getfield('pkg'), " - ",
- $part_pkg->getfield('comment'), "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- $pref->{setup} ? time2str("%D",$pref->{setup} ) : "" ,
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- $pref->{bill} ? time2str("%D",$pref->{bill} ) : "" ,
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- $pref->{susp} ? time2str("%D",$pref->{susp} ) : "" ,
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- $pref->{expire} ? time2str("%D",$pref->{expire} ) : "" ,
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- $pref->{cancel} ? time2str("%D",$pref->{cancel} ) : "" ,
- "</FONT></TD>",
- "</TR>";
-}
-
-#formatting
-print "</TABLE></CENTER>";
-
-#formatting
-print qq!<CENTER><HR><A NAME="history"><FONT SIZE=+1>Payment History!,
- qq!</FONT></A><BR>!,
- qq!Click on invoice to view invoice/enter payment.<BR>!,
- qq!<A HREF="../edit/cust_credit.cgi?$custnum">!,
- qq!Post Credit / Refund</A></CENTER><BR>!;
-
-#get payment history
-#
-# major problem: this whole thing is way too sloppy.
-# minor problem: the description lines need better formatting.
-
-my(@history);
-
-my(@bills)=qsearch('cust_bill',{'custnum'=>$custnum});
-my($bill);
-foreach $bill (@bills) {
- my($bref)=$bill->hashref;
- push @history,
- $bref->{_date} . qq!\t<A HREF="../view/cust_bill.cgi?! .
- $bref->{invnum} . qq!">Invoice #! . $bref->{invnum} .
- qq! (Balance \$! . $bref->{owed} . qq!)</A>\t! .
- $bref->{charged} . qq!\t\t\t!;
-
- my(@payments)=qsearch('cust_pay',{'invnum'=> $bref->{invnum} } );
- my($payment);
- foreach $payment (@payments) {
-# my($pref)=$payment->hashref;
- my($date,$invnum,$payby,$payinfo,$paid)=($payment->getfield('_date'),
- $payment->getfield('invnum'),
- $payment->getfield('payby'),
- $payment->getfield('payinfo'),
- $payment->getfield('paid'),
- );
- push @history,
- "$date\tPayment, Invoice #$invnum ($payby $payinfo)\t\t$paid\t\t";
- }
-}
-
-my(@credits)=qsearch('cust_credit',{'custnum'=>$custnum});
-my($credit);
-foreach $credit (@credits) {
- my($cref)=$credit->hashref;
- push @history,
- $cref->{_date} . "\tCredit #" . $cref->{crednum} . ", (Balance \$" .
- $cref->{credited} . ") by " . $cref->{otaker} . " - " .
- $cref->{reason} . "\t\t\t" . $cref->{amount} . "\t";
-
- my(@refunds)=qsearch('cust_refund',{'crednum'=> $cref->{crednum} } );
- my($refund);
- foreach $refund (@refunds) {
- my($rref)=$refund->hashref;
- push @history,
- $rref->{_date} . "\tRefund, Credit #" . $rref->{crednum} . " (" .
- $rref->{payby} . " " . $rref->{payinfo} . ") by " .
- $rref->{otaker} . " - ". $rref->{reason} . "\t\t\t\t" .
- $rref->{refund};
- }
-}
-
- #formatting
- print <<END;
-<CENTER><TABLE BORDER=4>
-<TR>
- <TH>Date</TH>
- <TH>Description</TH>
- <TH><FONT SIZE=-1>Charge</FONT></TH>
- <TH><FONT SIZE=-1>Payment</FONT></TH>
- <TH><FONT SIZE=-1>In-house<BR>Credit</FONT></TH>
- <TH><FONT SIZE=-1>Refund</FONT></TH>
- <TH><FONT SIZE=-1>Balance</FONT></TH>
-</TR>
-END
-
-#display payment history
-
-my($balance)=0;
-my($item);
-foreach $item (sort keyfield_numerically @history) {
- my($date,$desc,$charge,$payment,$credit,$refund)=split(/\t/,$item);
- $charge ||= 0;
- $payment ||= 0;
- $credit ||= 0;
- $refund ||= 0;
- $balance += $charge - $payment;
- $balance -= $credit - $refund;
-
- print "<TR><TD><FONT SIZE=-1>",time2str("%D",$date),"</FONT></TD>",
- "<TD><FONT SIZE=-1>$desc</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- ( $charge ? "\$".sprintf("%.2f",$charge) : '' ),
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- ( $payment ? "- \$".sprintf("%.2f",$payment) : '' ),
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- ( $credit ? "- \$".sprintf("%.2f",$credit) : '' ),
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>",
- ( $refund ? "\$".sprintf("%.2f",$refund) : '' ),
- "</FONT></TD>",
- "<TD><FONT SIZE=-1>\$" . sprintf("%.2f",$balance),
- "</FONT></TD>",
- "\n";
-}
-
-#formatting
-print "</TABLE></CENTER>";
-
-#end
-
-#formatting
-print <<END;
-
- </BODY>
-</HTML>
-END
-
-#subroutiens
-sub keyfield_numerically { (split(/\t/,$a))[0] <=> (split(/\t/,$b))[0] ; }
-
diff --git a/htdocs/view/cust_pkg.cgi b/htdocs/view/cust_pkg.cgi
deleted file mode 100755
index 04e3832..0000000
--- a/htdocs/view/cust_pkg.cgi
+++ /dev/null
@@ -1,181 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# cust_pkg.cgi: View a package
-#
-# Usage: cust_pkg.cgi pkgnum
-# http://server.name/path/cust_pkg.cgi?pkgnum
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 96-dec-15
-#
-# services section needs to be cleaned up, needs to display extraneous
-# entries in cust_pkg!
-# ivan@voicenet.com 96-dec-31
-#
-# added navigation bar
-# ivan@voicenet.com 97-jan-30
-#
-# changed and fixed up suspension and cancel stuff, now you can't add
-# services to a cancelled package
-# ivan@voicenet.com 97-feb-27
-#
-# rewrote for new API, still needs to be cleaned up!
-# ivan@voicenet.com 97-jul-29
-#
-# no FS::Search ivan@sisd.com 98-mar-7
-
-use strict;
-use Date::Format;
-use CGI::Base qw(:DEFAULT :CGI); # CGI module
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearch qsearchs);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-my(%uiview,%uiadd);
-my($part_svc);
-foreach $part_svc ( qsearch('part_svc',{}) ) {
- $uiview{$part_svc->svcpart}="../view/". $part_svc->svcdb . ".cgi";
- $uiadd{$part_svc->svcpart}="../edit/". $part_svc->svcdb . ".cgi";
-}
-
-SendHeaders(); # one guess.
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Package View</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Package View</H1>
- </CENTER>
- <BASEFONT SIZE=3>
-END
-
-#untaint pkgnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($pkgnum)=$1;
-
-#get package record
-my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
-die "No package!" unless $cust_pkg;
-my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->getfield('pkgpart')});
-
-#nav bar
-my($custnum)=$cust_pkg->getfield('custnum');
-print qq!<CENTER><A HREF="../view/cust_main.cgi?$custnum">View this customer!,
- qq! (#$custnum)</A> | <A HREF="../">Main menu</A></CENTER><BR>!;
-
-#print info
-my($susp,$cancel,$expire)=(
- $cust_pkg->getfield('susp'),
- $cust_pkg->getfield('cancel'),
- $cust_pkg->getfield('expire'),
-);
-print "<FONT SIZE=+1><CENTER>Package #<B>$pkgnum</B></FONT>";
-print qq!<BR><A HREF="#package">Package Information</A>!;
-print qq! | <A HREF="#services">Service Information</A>! unless $cancel;
-print qq!</CENTER><HR>\n!;
-
-my($pkg,$comment)=($part_pkg->getfield('pkg'),$part_pkg->getfield('comment'));
-print qq!<A NAME="package"><CENTER><FONT SIZE=+1>Package Information!,
- qq!</FONT></A>!;
-print qq!<BR><A HREF="../unimp.html">Edit this information</A></CENTER>!;
-print "<P>Package: <B>$pkg - $comment</B>";
-
-my($setup,$bill)=($cust_pkg->getfield('setup'),$cust_pkg->getfield('bill'));
-print "<BR>Setup: <B>", $setup ? time2str("%D",$setup) : "(Not setup)" ,"</B>";
-print "<BR>Next bill: <B>", $bill ? time2str("%D",$bill) : "" ,"</B>";
-
-if ($susp) {
- print "<BR>Suspended: <B>", time2str("%D",$susp), "</B>";
- print qq! <A HREF="../misc/unsusp_pkg.cgi?$pkgnum">Unsuspend</A>! unless $cancel;
-} else {
- print qq!<BR><A HREF="../misc/susp_pkg.cgi?$pkgnum">Suspend</A>! unless $cancel;
-}
-
-if ($expire) {
- print "<BR>Expire: <B>", time2str("%D",$expire), "</B>";
-}
- print <<END;
-<FORM ACTION="../misc/expire_pkg.cgi" METHOD="post">
-<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">
-Expire (date): <INPUT TYPE="text" NAME="date" VALUE="" >
-<INPUT TYPE="submit" VALUE="Cancel later">
-END
-
-if ($cancel) {
- print "<BR>Cancelled: <B>", time2str("%D",$cancel), "</B>";
-} else {
- print qq!<BR><A HREF="../misc/cancel_pkg.cgi?$pkgnum">Cancel now</A>!;
-}
-
-#otaker
-my($otaker)=$cust_pkg->getfield('otaker');
-print "<P>Order taken by <B>$otaker</B>";
-
-unless ($cancel) {
-
- #services
- print <<END;
-<HR><A NAME="services"><CENTER><FONT SIZE=+1>Service Information</FONT></A>
-<BR>Click on service to view/edit/add service.</CENTER><BR>
-<CENTER><B>Do NOT pick the "Link to existing" option unless you are auditing!!!</B></CENTER>
-<CENTER><TABLE BORDER=4>
-<TR><TH>Service</TH>
-END
-
- #list of services this pkgpart includes
- my($pkg_svc,%pkg_svc);
- foreach $pkg_svc ( qsearch('pkg_svc',{'pkgpart'=> $cust_pkg->pkgpart }) ) {
- $pkg_svc{$pkg_svc->svcpart} = $pkg_svc->quantity if $pkg_svc->quantity;
- }
-
- #list of records from cust_svc
- my($svcpart);
- foreach $svcpart (sort {$a <=> $b} keys %pkg_svc) {
-
- my($svc)=qsearchs('part_svc',{'svcpart'=>$svcpart})->getfield('svc');
-
- my(@cust_svc)=qsearch('cust_svc',{'pkgnum'=>$pkgnum,
- 'svcpart'=>$svcpart,
- });
-
- my($enum);
- for $enum ( 1 .. $pkg_svc{$svcpart} ) {
-
- my($cust_svc);
- if ( $cust_svc=shift @cust_svc ) {
- my($svcnum)=$cust_svc->svcnum;
- print <<END;
-<TR><TD><A HREF="$uiview{$svcpart}?$svcnum">(View) $svc<A></TD></TR>
-END
- } else {
- print <<END;
-<TR>
- <TD><A HREF="$uiadd{$svcpart}?pkgnum$pkgnum-svcpart$svcpart">
- (Add) $svc</A>
- or <A HREF="../misc/link.cgi?pkgnum$pkgnum-svcpart$svcpart">
- (Link to existing) $svc</A>
- </TD>
-</TR>
-END
- }
-
- }
- warn "WARNING: Leftover services pkgnum $pkgnum!" if @cust_svc;;
- }
-
- print "</TABLE></CENTER>";
-
-}
-
-#formatting
-print <<END;
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/view/svc_acct.cgi b/htdocs/view/svc_acct.cgi
deleted file mode 100755
index 7096c2f..0000000
--- a/htdocs/view/svc_acct.cgi
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# View svc_acct records
-#
-# Usage: svc_acct.cgi svcnum
-# http://server.name/path/svc_acct.cgi?svcnum
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 96-dec-17
-#
-# added link to send info
-# ivan@voicenet.com 97-jan-4
-#
-# added navigation bar and ability to change username, etc.
-# ivan@voicenet.com 97-jan-30
-#
-# activate 800 service
-# ivan@voicenet.com 97-feb-10
-#
-# modified navbar code (should be a subroutine?), added link to cancel account (only if not audited)
-# ivan@voicenet.com 97-apr-16
-#
-# INCOMPLETELY rewrote some things for new API
-# ivan@voicenet.com 97-jul-29
-#
-# FS::Search became FS::Record, use strict, etc. ivan@sisd.com 98-mar-9
-#
-# Changes to allow page to work at a relative position in server
-# Changed 'password' to '_password' because Pg6.3 reserves the password word
-# bmccane@maxbaud.net 98-apr-3
-#
-# /var/spool/freeside/conf/domain ivan@sisd.com 98-jul-17
-#
-# displays arbitrary radius attributes ivan@sisd.com 98-aug-16
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use CGI::Carp qw(fatalsToBrowser);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs fields);
-
-my($conf_domain)="/var/spool/freeside/conf/domain";
-open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!";
-my($mydomain)=map {
- /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file
- $1;
-} grep $_ !~ /^(#|$)/, <DOMAIN>;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-&cgisuidsetup($cgi);
-
-#untaint svcnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($svcnum)=$1;
-my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$svcnum});
-die "Unkonwn svcnum" unless $svc_acct;
-
-my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
-my($pkgnum)=$cust_svc->getfield('pkgnum');
-my($cust_pkg,$custnum);
-if ($pkgnum) {
- $cust_pkg=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
- $custnum=$cust_pkg->getfield('custnum');
-}
-
-my($part_svc)=qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } );
-die "Unkonwn svcpart" unless $part_svc;
-
-SendHeaders(); # one guess.
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Account View</TITLE>
- </HEAD>
- <BODY>
- <CENTER><H1>Account View</H1>
- <BASEFONT SIZE=3>
-<CENTER>
-END
-
-if ($pkgnum || $custnum) {
- print <<END;
-<A HREF="../view/cust_pkg.cgi?$pkgnum">View this package (#$pkgnum)</A> |
-<A HREF="../view/cust_main.cgi?$custnum">View this customer (#$custnum)</A> |
-END
-} else {
- print <<END;
-<A HREF="../misc/cancel-unaudited.cgi?$svcnum">Cancel this (unaudited)account</A> |
-END
-}
-
-print <<END;
-<A HREF="../">Main menu</A></CENTER><BR>
-<FONT SIZE=+1>Service #$svcnum</FONT>
-END
-
-print qq!<BR><A HREF="../edit/svc_acct.cgi?$svcnum">Edit this information</A>!;
-#print qq!<BR><A HREF="../misc/sendconfig.cgi?$svcnum">Send account information</A>!;
-print qq!<BR><BR><A HREF="#general">General</A> | <A HREF="#shell">Shell account</A> | !;
-print qq!<A HREF="#slip">SLIP/PPP account</A></CENTER>!;
-
-#formatting
-print qq!<HR><CENTER><FONT SIZE=+1><A NAME="general">General</A></FONT></CENTER>!;
-
-#svc
-print "Service: <B>", $part_svc->svc, "</B>";
-
-#username
-print "<BR>Username: <B>", $svc_acct->username, "</B>";
-
-#password
-if (substr($svc_acct->_password,0,1) eq "*") {
- print "<BR>Password: <I>(Login disabled)</I><BR>";
-} else {
- print "<BR>Password: <I>(hidden)</I><BR>";
-}
-
-# popnum -> svc_acct_pop record
-my($svc_acct_pop)=qsearchs('svc_acct_pop',{'popnum'=>$svc_acct->popnum});
-
-#pop
-print "POP: <B>", $svc_acct_pop->city, ", ", $svc_acct_pop->state,
- " (", $svc_acct_pop->ac, ")/", $svc_acct_pop->exch, "<\B>"
- if $svc_acct_pop;
-
-#shell account
-print qq!<HR><CENTER><FONT SIZE=+1><A NAME="shell">!;
-if ($svc_acct->uid ne '') {
- print "Shell account";
- print "</A></FONT></CENTER>";
- print "Uid: <B>", $svc_acct->uid, "</B>";
- print "<BR>Gid: <B>", $svc_acct->gid, "</B>";
-
- print qq!<BR>Finger name: <B>!, $svc_acct->finger, qq!</B><BR>!;
-
- print "Home directory: <B>", $svc_acct->dir, "</B><BR>";
-
- print "Shell: <B>", $svc_acct->shell, "</B><BR>";
-
- print "Quota: <B>", $svc_acct->quota, "</B> <I>(unimplemented)</I>";
-} else {
- print "No shell account.</A></FONT></CENTER>";
-}
-
-# SLIP/PPP
-print qq!<HR><CENTER><FONT SIZE=+1><A NAME="slip">!;
-if ($svc_acct->slipip) {
- print "SLIP/PPP account</A></FONT></CENTER>";
- print "IP address: <B>", ( $svc_acct->slipip eq "0.0.0.0" || $svc_acct->slipip eq '0e0' ) ? "<I>(Dynamic)</I>" : $svc_acct->slipip ,"</B>";
- my($attribute);
- foreach $attribute ( grep /^radius_/, fields('svc_acct') ) {
- #warn $attribute;
- $attribute =~ /^radius_(.*)$/;
- my($pattribute) = ($1);
- $pattribute =~ s/_/-/g;
- print "<BR>Radius $pattribute: <B>". $svc_acct->getfield($attribute), "</B>";
- }
-} else {
- print "No SLIP/PPP account</A></FONT></CENTER>"
-}
-
-print "<HR>";
-
- #formatting
- print <<END;
-
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/view/svc_acct_sm.cgi b/htdocs/view/svc_acct_sm.cgi
deleted file mode 100755
index 42623ee..0000000
--- a/htdocs/view/svc_acct_sm.cgi
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# View svc_acct_sm records
-#
-# Usage: svc_acct_sm.cgi svcnum
-# http://server.name/path/svc_acct_sm.cgi?svcnum
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# based on view/svc_acct.cgi
-#
-# ivan@voicenet.com 97-jan-5
-#
-# added navigation bar
-# ivan@voicenet.com 97-jan-30
-#
-# rewrite ivan@sisd.com 98-mar-15
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-#
-# /var/spool/freeside/conf/domain ivan@sisd.com 98-jul-17
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-
-my($conf_domain)="/var/spool/freeside/conf/domain";
-open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!";
-my($mydomain)=map {
- /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file
- $1
-} grep $_ !~ /^(#|$)/, <DOMAIN>;
-close DOMAIN;
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-cgisuidsetup($cgi);
-
-#untaint svcnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($svcnum)=$1;
-my($svc_acct_sm)=qsearchs('svc_acct_sm',{'svcnum'=>$svcnum});
-die "Unknown svcnum" unless $svc_acct_sm;
-
-my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
-my($pkgnum)=$cust_svc->getfield('pkgnum');
-my($cust_pkg,$custnum);
-if ($pkgnum) {
- $cust_pkg=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
- $custnum=$cust_pkg->getfield('custnum');
-}
-
-my($part_svc)=qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } );
-die "Unkonwn svcpart" unless $part_svc;
-
-SendHeaders(); # one guess.
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Mail Alias View</TITLE>
- </HEAD>
- <BODY>
- <CENTER><H1>Mail Alias View</H1>
-END
-if ($pkgnum || $custnum) {
- print <<END;
-<A HREF="../view/cust_pkg.cgi?$pkgnum">View this package (#$pkgnum)</A> |
-<A HREF="../view/cust_main.cgi?$custnum">View this customer (#$custnum)</A> |
-END
-} else {
- print <<END;
-<A HREF="../misc/cancel-unaudited.cgi?$svcnum">Cancel this (unaudited)account</A> |
-END
-}
-
-print <<END;
- <A HREF="../">Main menu</A></CENTER><BR<
- <FONT SIZE=+1>Service #$svcnum</FONT>
- <P><A HREF="../edit/svc_acct_sm.cgi?$svcnum">Edit this information</A>
- <BASEFONT SIZE=3>
-END
-
-my($domsvc,$domuid,$domuser)=(
- $svc_acct_sm->domsvc,
- $svc_acct_sm->domuid,
- $svc_acct_sm->domuser,
-);
-my($svc) = $part_svc->svc;
-my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$domsvc});
-my($domain)=$svc_domain->domain;
-my($svc_acct)=qsearchs('svc_acct',{'uid'=>$domuid});
-my($username)=$svc_acct->username;
-
-#formatting
-print qq!<HR>!;
-
-#svc
-print "Service: <B>$svc</B>";
-
-print "<HR>";
-
-print qq!Mail to <B>!, ( ($domuser eq '*') ? "<I>(anything)</I>" : $domuser ) , qq!</B>\@<B>$domain</B> forwards to <B>$username</B>\@$mydomain mailbox.!;
-
-print "<HR>";
-
- #formatting
- print <<END;
-
- </BODY>
-</HTML>
-END
-
diff --git a/htdocs/view/svc_domain.cgi b/htdocs/view/svc_domain.cgi
deleted file mode 100755
index 78ff6ac..0000000
--- a/htdocs/view/svc_domain.cgi
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# View svc_domain records
-#
-# Usage: svc_domain svcnum
-# http://server.name/path/svc_domain.cgi?svcnum
-#
-# Note: Should be run setuid freeside as user nobody.
-#
-# ivan@voicenet.com 97-jan-6
-#
-# rewrite ivan@sisd.com 98-mar-14
-#
-# Changes to allow page to work at a relative position in server
-# bmccane@maxbaud.net 98-apr-3
-
-use strict;
-use CGI::Base qw(:DEFAULT :CGI);
-use FS::UID qw(cgisuidsetup);
-use FS::Record qw(qsearchs);
-
-my($cgi) = new CGI::Base;
-$cgi->get;
-cgisuidsetup($cgi);
-
-#untaint svcnum
-$QUERY_STRING =~ /^(\d+)$/;
-my($svcnum)=$1;
-my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$svcnum});
-die "Unknown svcnum" unless $svc_domain;
-my($domain)=$svc_domain->domain;
-
-my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
-my($pkgnum)=$cust_svc->getfield('pkgnum');
-my($cust_pkg,$custnum);
-if ($pkgnum) {
- $cust_pkg=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
- $custnum=$cust_pkg->getfield('custnum');
-}
-
-my($part_svc)=qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } );
-die "Unkonwn svcpart" unless $part_svc;
-
-SendHeaders(); # one guess.
-print <<END;
-<HTML>
- <HEAD>
- <TITLE>Domain View</TITLE>
- </HEAD>
- <BODY>
- <CENTER><H1>Domain View</H1>
- <BASEFONT SIZE=3>
-<CENTER>
-<A HREF="../view/cust_pkg.cgi?$pkgnum">View this package (#$pkgnum)</A> |
-<A HREF="../view/cust_main.cgi?$custnum">View this customer (#$custnum)</A> |
-<A HREF="../">Main menu</A></CENTER><BR>
- <FONT SIZE=+1>Service #$svcnum</FONT>
- </CENTER>
-END
-
-print "<HR>";
-print "Service: <B>", $part_svc->svc, "</B>";
-print "<HR>";
-
-print qq!Domain name <B>$domain</B>.!;
-print qq!<P><A HREF="http://rs.internic.net/cgi-bin/whois?do+$domain">View whois information.</A>!;
-
-print "<HR>";
-
- #formatting
- print <<END;
-
- </BODY>
-</HTML>
-END
-
diff --git a/htetc/freeside-base1.99.conf b/htetc/freeside-base1.99.conf
new file mode 100644
index 0000000..8e890e6
--- /dev/null
+++ b/htetc/freeside-base1.99.conf
@@ -0,0 +1,21 @@
+PerlModule Apache::compat
+
+#PerlModule Apache::DBI
+
+PerlModule HTML::Mason
+PerlSetVar MasonArgsMethod CGI
+PerlModule HTML::Mason::ApacheHandler
+
+PerlRequire "%%%MASON_HANDLER%%%"
+
+<Directory %%%FREESIDE_DOCUMENT_ROOT%%%>
+AuthName Freeside
+AuthType Basic
+AuthUserFile %%%FREESIDE_CONF%%%/htpasswd
+require valid-user
+<Files ~ (\.cgi|\.html)>
+SetHandler perl-script
+PerlHandler HTML::Mason
+</Files>
+</Directory>
+
diff --git a/htetc/freeside-base1.conf b/htetc/freeside-base1.conf
new file mode 100644
index 0000000..73962a7
--- /dev/null
+++ b/htetc/freeside-base1.conf
@@ -0,0 +1,18 @@
+#PerlModule Apache::DBI
+
+PerlModule HTML::Mason
+
+<Directory %%%FREESIDE_DOCUMENT_ROOT%%%>
+AuthName Freeside
+AuthType Basic
+AuthUserFile %%%FREESIDE_CONF%%%/htpasswd
+require valid-user
+<Files ~ (\.cgi|\.html)>
+AddHandler perl-script .cgi .html
+PerlHandler HTML::Mason
+</Files>
+<Perl>
+require "%%%MASON_HANDLER%%%";
+</Perl>
+</Directory>
+
diff --git a/htetc/freeside-base2.conf b/htetc/freeside-base2.conf
new file mode 100644
index 0000000..6606129
--- /dev/null
+++ b/htetc/freeside-base2.conf
@@ -0,0 +1,21 @@
+PerlModule Apache2::compat
+
+#PerlModule Apache::DBI
+
+PerlModule HTML::Mason
+PerlSetVar MasonArgsMethod CGI
+PerlModule HTML::Mason::ApacheHandler
+
+PerlRequire "%%%MASON_HANDLER%%%"
+
+<Directory %%%FREESIDE_DOCUMENT_ROOT%%%>
+AuthName Freeside
+AuthType Basic
+AuthUserFile %%%FREESIDE_CONF%%%/htpasswd
+require valid-user
+<Files ~ (\.cgi|\.html)>
+SetHandler perl-script
+PerlHandler HTML::Mason
+</Files>
+</Directory>
+
diff --git a/htetc/freeside-rt.conf b/htetc/freeside-rt.conf
new file mode 100644
index 0000000..9b5ccf8
--- /dev/null
+++ b/htetc/freeside-rt.conf
@@ -0,0 +1,36 @@
+<Directory %%%FREESIDE_DOCUMENT_ROOT%%%/rt/NoAuth>
+<Limit GET POST>
+allow from all
+Satisfy any
+SetHandler perl-script
+PerlHandler HTML::Mason
+</Limit>
+</Directory>
+
+<Directory %%%FREESIDE_DOCUMENT_ROOT%%%/rt/REST/1.0/NoAuth>
+<Limit GET POST>
+allow from all
+Satisfy any
+SetHandler perl-script
+PerlHandler HTML::Mason
+</Limit>
+</Directory>
+
+<DirectoryMatch "^%%%FREESIDE_DOCUMENT_ROOT%%%/rt/.*NoAuth/images">
+SetHandler None
+</DirectoryMatch>
+
+<Directory %%%FREESIDE_DOCUMENT_ROOT%%%/rt/Ticket/Attachment>
+SetHandler perl-script
+PerlHandler HTML::Mason
+</Directory>
+
+<Directory %%%FREESIDE_DOCUMENT_ROOT%%%/rt/Search>
+SetHandler perl-script
+PerlHandler HTML::Mason
+</Directory>
+
+<DirectoryMatch "^%%%FREESIDE_DOCUMENT_ROOT%%%/rt/RTx/Statistics/.*/Elements>
+SetHandler perl-script
+PerlHandler HTML::Mason
+</DirectoryMatch>
diff --git a/htetc/handler.pl b/htetc/handler.pl
new file mode 100644
index 0000000..1dd16ec
--- /dev/null
+++ b/htetc/handler.pl
@@ -0,0 +1,98 @@
+#!/usr/bin/perl
+
+package HTML::Mason;
+
+use strict;
+use warnings;
+use FS::Mason qw( mason_interps );
+
+#use vars qw($r);
+
+# Bring in ApacheHandler, necessary for mod_perl integration.
+# Uncomment the second line (and comment the first) to use
+# Apache::Request instead of CGI.pm to parse arguments.
+use HTML::Mason::ApacheHandler;
+# use HTML::Mason::ApacheHandler (args_method=>'mod_perl');
+
+###use Module::Refresh;###
+
+# Create Mason objects
+
+my( $fs_interp, $rt_interp ) = mason_interps('apache');
+
+my $ah = new HTML::Mason::ApacheHandler (
+ interp => $fs_interp,
+ request_class => 'FS::Mason::Request',
+ args_method => 'CGI', #(and FS too)
+);
+
+# Activate the following if running httpd as root (the normal case).
+# Resets ownership of all files created by Mason at startup.
+#
+#chown (Apache->server->uid, Apache->server->gid, $interp->files_written);
+
+sub handler
+{
+ #($r) = @_;
+ my $r = shift;
+
+ # If you plan to intermix images in the same directory as
+ # components, activate the following to prevent Mason from
+ # evaluating image files as components.
+ #
+ #return -1 if $r->content_type && $r->content_type !~ m|^text/|i;
+
+ ###Module::Refresh->refresh;###
+
+ $r->content_type('text/html');
+ #eorar
+
+ my $headers = $r->headers_out;
+ $headers->{'Cache-control'} = 'no-cache';
+ #$r->no_cache(1);
+ $headers->{'Expires'} = '0';
+
+# $r->send_http_header;
+
+ if ( $r->filename =~ /\/rt\// ) { #RT
+
+ $ah->interp($rt_interp);
+
+ local $SIG{__WARN__};
+ local $SIG{__DIE__};
+
+ RT::Init();
+
+ # We don't need to handle non-text, non-xml items
+ return -1 if defined( $r->content_type )
+ && $r->content_type !~ m!(^text/|\bxml\b)!io;
+
+ } else {
+
+ $ah->interp($fs_interp);
+
+ }
+
+ my %session;
+ my $status;
+ eval { $status = $ah->handle_request($r); };
+#!!
+# if ( $@ ) {
+# $RT::Logger->crit($@);
+# }
+ warn $@ if $@;
+
+ undef %session;
+
+#!!
+# if ($RT::Handle->TransactionDepth) {
+# $RT::Handle->ForceRollback;
+# $RT::Logger->crit(
+#"Transaction not committed. Usually indicates a software fault. Data loss may have occurred"
+# );
+# }
+
+ $status;
+}
+
+1;
diff --git a/httemplate/.htaccess b/httemplate/.htaccess
new file mode 100755
index 0000000..f8c6b9c
--- /dev/null
+++ b/httemplate/.htaccess
@@ -0,0 +1,3 @@
+AuthName Freeside
+AuthType Basic
+require valid-user
diff --git a/httemplate/autohandler b/httemplate/autohandler
new file mode 100644
index 0000000..ae04d42
--- /dev/null
+++ b/httemplate/autohandler
@@ -0,0 +1,44 @@
+% $m->call_next;
+<%init>
+ dbh->{'private_profile'} = {} if UNIVERSAL::can(dbh, 'sprintProfile');
+</%init>
+<%filter>
+
+my $profile = '';
+if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
+
+ if ( lc($r->content_type) eq 'text/html'
+ && $FS::CurrentUser::CurrentUser->option('show_db_profile')
+ )
+ {
+
+ ## barely worth it, just in case someone tries to use profiling on a
+ ## non-RT install
+ #eval "use Text::Wrapper;";
+ #die $@ if $@;
+
+ my $text = dbh->sprintProfile();
+ #$text =~ s/^/ /mg;
+
+ $profile = '<PRE>'. encode_entities( $text ). "\n\n". '</PRE>';
+
+ }
+
+ #well, could do this without sprintProfile, but definiately don't want it on
+ #unless DBIx::Profile is loaded
+ if ( $FS::CurrentUser::CurrentUser->option('save_db_profile') ) {
+ #my $file = %%%FREESIDE_LOG%%%; #substitute here? maybe get from FS.pm?
+ my $file = '/usr/local/etc/freeside/'; #bah
+ $file .= "dbix_profile.$$.". time;
+ dbh->setLogFile($file);
+ dbh->printProfile();
+ }
+
+ dbh->{'private_profile'} = {};
+}
+
+s/(<\/BODY>[\s\n]*<\/HTML>[\s\n]*)$/$profile$1/i;
+</%filter>
+<%cleanup>
+ dbh->commit();
+</%cleanup>
diff --git a/httemplate/browse/access_group.html b/httemplate/browse/access_group.html
new file mode 100644
index 0000000..aa9097f
--- /dev/null
+++ b/httemplate/browse/access_group.html
@@ -0,0 +1,106 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Employee Groups',
+ 'menubar' => [ 'View Employees' => $p.'browse/access_user.html', ],
+ 'html_init' => $html_init,
+ 'name' => 'employee groups',
+ 'query' => { 'table' => 'access_group',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY groupname', #??
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Group name',
+ 'Agents',
+ 'Rights',
+ ],
+ 'fields' => [ 'groupnum',
+ 'groupname',
+ $agents_sub,
+ $rights_sub,
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ '',
+ ],
+ )
+%>
+<%once>
+
+my $html_init =
+ "Employee groups control access to the back-office interface. Each employee can be assigned to one or more groups.<BR><BR>".
+ qq!<A HREF="${p}edit/access_group.html"><I>Add an employee group</I></A><BR><BR>!;
+
+#false laziness w/access_user.html & agent_type.cgi
+my $agents_sub = sub {
+ my $access_group = shift;
+
+ [ map {
+ my $access_groupagent = $_;
+ my $agent = $access_groupagent->agent;
+ [
+ {
+ 'data' => $agent->agent,
+ 'align' => 'left',
+ 'link' => $p. 'edit/agent.cgi?'. $agent->agentnum,
+ },
+ ];
+ }
+ grep { $_->agent } #?
+ $access_group->access_groupagent,
+
+ ];
+
+};
+
+tie my %rights, 'Tie::IxHash', FS::AccessRight->rights_info;
+
+my $rights_sub = sub {
+ my $access_group = shift;
+
+ #[ map { my $access_right = $_;
+ # [
+ # {
+ # 'data' => $access_right->rightname,
+ # 'align' => 'left',
+ # },
+ # ];
+ # }
+ # $access_group->access_rights,
+ #];
+
+ #some false laziness w/edit/access_group.html
+ my $columns = 3;
+ my $count = 0;
+
+ #include('/elements/table-grid.html', bgcolor=>'#cccccc' ).
+ '<TABLE>'.
+ '<TR>'. join( '', map {
+
+ '<TD CLASS="inv" VALIGN="top"><TABLE WIDTH=100%>'.
+ '<TR><TH BGCOLOR="#dcdcdc">'. $_. '</TH></TR>'.
+ '<TR><TD>'.
+
+ join('<BR>', grep { $access_group->access_right($_); }
+ map { ref($_) ? $_->{'rightname'} : $_; }
+ @{ $rights{$_} }
+ ).
+
+ '</TD></TR></TABLE></TD>'.
+ ( ++$count % $columns ? '' : '</TR><TR>')
+
+ } keys %rights ). '</TR></TABLE>';
+
+};
+
+my $count_query = 'SELECT COUNT(*) FROM access_group';
+
+my $link = [ $p.'edit/access_group.html?', 'groupnum' ];
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/browse/access_user.html b/httemplate/browse/access_user.html
new file mode 100644
index 0000000..321025b
--- /dev/null
+++ b/httemplate/browse/access_user.html
@@ -0,0 +1,61 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Employees',
+ 'menubar' => [ 'View Employee groups' => $p.'browse/access_group.html', ],
+ 'html_init' => $html_init,
+ 'name' => 'employees',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 2,
+ 'query' => { 'table' => 'access_user',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY last, first'
+ },
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'links' => \@links,
+ 'align' => $align,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init =
+ "Employees have access to the back-office interface. Typically, this is your employees and contractors. In a virtualized setup, you can also add accounts for your reseller's employees.<BR><BR>It is <B>highly recommended</B> to add a <B>separate account for each person</B> rather than using role accounts.<BR><BR>".
+ qq!<A HREF="${p}edit/access_user.html"><I>Add an employee</I></A><BR><BR>!;
+
+#false laziness w/access_group.html & agent_type.cgi
+my $groups_sub = sub {
+ my $access_user = shift;
+
+ [ map {
+ my $access_usergroup = $_;
+ my $access_group = $access_usergroup->access_group;
+ [
+ {
+ 'data' => $access_group->groupname,
+ 'align' => 'left',
+ 'link' =>
+ $p. 'edit/access_group.html?'. $access_usergroup->groupnum,
+ },
+ ];
+ }
+ grep { $_->access_group # and ! $_->access_group->disabled
+ }
+ $access_user->access_usergroup,
+
+ ];
+
+};
+
+my $count_query = 'SELECT COUNT(*) FROM access_user';
+
+my $link = [ $p.'edit/access_user.html?', 'usernum' ];
+
+my @header = ( '#', 'Username', 'Full name', 'Groups' );
+my @fields = ( 'usernum', 'username', 'name', $groups_sub );
+my $align = 'rlll';
+my @links = ( $link, $link, $link, '' );
+
+</%init>
diff --git a/httemplate/browse/addr_block.cgi b/httemplate/browse/addr_block.cgi
new file mode 100644
index 0000000..1bbcdcb
--- /dev/null
+++ b/httemplate/browse/addr_block.cgi
@@ -0,0 +1,145 @@
+<% include('elements/browse.html',
+ 'title' => 'Address Blocks',
+ 'name' => 'address block',
+ 'html_init' => $html_init,
+ 'html_foot' => $html_foot,
+ 'query' => { 'table' => 'addr_block',
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $order_by,
+ },
+ 'count_query' => "SELECT count(*) from addr_block $count_sql",
+ 'header' => [ 'Address Block',
+ 'Router',
+ 'Action(s)',
+ '',
+ '',
+ ],
+ 'fields' => [ 'NetAddr',
+ sub { my $block = shift;
+ my $router = $block->router;
+ my $result = '';
+ if ($router) {
+ $result .= $router->routername. ' (';
+ $result .= scalar($block->svc_broadband). ' services)';
+ }
+ $result;
+ },
+ $allocate_text,
+ sub { shift->router ? '' : '<FONT SIZE="-2">(split)</FONT>' },
+ sub { '<FONT SIZE="-2">('. (shift->manual_flag ? 'allow' : 'prevent'). ' automatic ip assignment)</FONT>' },
+ ],
+ 'links' => [ '',
+ '',
+ [ 'javascript:void(0)', '' ],
+ $split_link,
+ $autoassign_link,
+ ],
+ 'link_onclicks' => [ '',
+ '',
+ $allocate_link,
+ '',
+ ],
+ 'cell_styles' => [ '',
+ '',
+ 'border-right:none;',
+ 'border-left:none;',
+ ],
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Broadband global configuration',
+ 'agent_pos' => 1,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Broadband configuration')
+ || $FS::CurrentUser::CurrentUser->access_right('Broadband global configuration');
+
+my $p2 = popurl(2);
+my $path = $p2 . "edit/process/addr_block";
+
+my $extra_sql = "";
+
+my $count_sql = "WHERE ". $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'Broadband global configuration',
+);
+
+my $order_by = "ORDER BY ";
+$order_by .= "inet(ip_gateway), " if driver_name =~ /^Pg/i;
+$order_by .= "inet_aton(ip_gateway), " if driver_name =~ /^mysql/i;
+$order_by .= "ip_netmask";
+
+my $html_init = qq(
+<SCRIPT>
+ function addr_block_areyousure(href, word) {
+ if(confirm("Are you sure you want to "+word+" this address block?") == true)
+ window.location.href = href;
+ }
+</SCRIPT>
+);
+
+$html_init .= include('/elements/error.html');
+
+my $confirm = sub {
+ my ($verb, $num) = (shift, shift);
+ "javascript:addr_block_areyousure('$path/$verb.cgi?blocknum=$num', '$verb')";
+};
+
+my $html_foot = qq(
+ <FORM ACTION="$path/add.cgi" METHOD="POST">
+ Gateway/Netmask:
+ <INPUT TYPE="text" NAME="ip_gateway" SIZE="15">/<INPUT TYPE="text" NAME="ip_netmask" SIZE="2">
+);
+$html_foot .= include( '/elements/select-agent.html',
+ 'agent_null_right' => 'Broadband global configuration',
+ );
+$html_foot .= qq(
+ <INPUT TYPE="submit" NAME="submit" VALUE="Add">
+ </FORM>
+);
+
+my $allocate_text = sub { my $block = shift;
+ my $router = $block->router;
+ my $result = '';
+ if ($router) {
+ $result = '<FONT SIZE="-2">(deallocate)</FONT>'
+ unless scalar($block->svc_broadband);
+ }else{
+ $result .= '<FONT SIZE="-2">(allocate)</FONT>'
+ }
+ $result;
+};
+
+my $allocate_link = sub {
+ my $block = shift;
+ if ($block->router) {
+ if (scalar($block->svc_broadband) == 0) {
+ &{$confirm}('deallocate', $block->blocknum);
+ } else {
+ "";
+ }
+ } else {
+ include( '/elements/popup_link_onclick.html',
+ 'action' => "${p2}edit/allocate.html?blocknum=". $block->blocknum,
+ 'actionlabel' => 'Allocate block to router',
+ );
+ }
+};
+
+my $split_link = sub {
+ my $block = shift;
+ my $ref = [ '', '' ];
+ $ref = [ &{$confirm}('split', $block->blocknum), '' ]
+ unless ($block->router);
+ $ref;
+};
+
+my $autoassign_link = sub {
+ my $block = shift;
+ my $url = "$path/manual_flag.cgi?manual_flag=";
+ $url .= $block->manual_flag ? '' : 'Y';
+ [ "$url;blocknum=", 'blocknum' ];
+};
+
+</%init>
diff --git a/httemplate/browse/agent.cgi b/httemplate/browse/agent.cgi
new file mode 100755
index 0000000..0a516ed
--- /dev/null
+++ b/httemplate/browse/agent.cgi
@@ -0,0 +1,422 @@
+<% include("/elements/header.html",'Agent Listing', menubar(
+ 'Agent Types' => $p. 'browse/agent_type.cgi',
+# 'Add new agent' => '../edit/agent.cgi'
+)) %>
+Agents are resellers of your service. Agents may be limited to a subset of your
+full offerings (via their type).<BR><BR>
+<A HREF="<% $p %>edit/agent.cgi"><I>Add a new agent</I></A><BR><BR>
+% if ( dbdef->table('agent')->column('disabled') ) {
+
+ <% $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled agents</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled agents</a> )'; }
+ %>
+% }
+
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+%
+
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=<% ( $cgi->param('showdisabled') || !dbdef->table('agent')->column('disabled') ) ? 2 : 3 %>>Agent</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Type</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Master Customer</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Access Groups</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Invoice<BR>Template</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Customers</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Customer<BR>packages</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Reports</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Registration<BR>codes</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Prepaid cards</TH>
+% if ( $conf->config('ticket_system') ) {
+
+ <TH CLASS="grid" BGCOLOR="#cccccc">Ticketing</TH>
+% }
+
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Payment Gateway Overrides</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Configuration Overrides</FONT></TH>
+</TR>
+%
+%# <TH><FONT SIZE=-1>Agent #</FONT></TH>
+%# <TH>Agent</TH>
+%
+%foreach my $agent ( sort {
+% #$a->getfield('agentnum') <=> $b->getfield('agentnum')
+% $a->getfield('agent') cmp $b->getfield('agent')
+%} qsearch('agent', \%search ) ) {
+%
+% my $cust_main_link = $p. 'search/cust_main.cgi?agentnum_on=1&'.
+% 'agentnum='. $agent->agentnum;
+%
+% my $cust_pkg_link = $p. 'search/cust_pkg.cgi?agentnum='. $agent->agentnum;
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+%
+
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/agent.cgi?<% $agent->agentnum %>"><% $agent->agentnum %></A>
+ </TD>
+
+% if ( dbdef->table('agent')->column('disabled')
+% && !$cgi->param('showdisabled') ) {
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $agent->disabled ? 'DISABLED' : '' %>
+ </TD>
+% }
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/agent.cgi?<% $agent->agentnum %>"><% $agent->agent %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/agent_type.cgi?<% $agent->typenum %>"><% $agent->agent_type->atype %></A>
+ </TD>
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+% if ( $agent->agent_custnum ) {
+ <% include('/elements/small_custview.html',
+ $agent->agent_custnum,
+ scalar($conf->config('countrydefault')),
+ 1, #show balance
+ )
+ %>
+% }
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% foreach my $access_group (
+% map $_->access_group,
+% qsearch('access_groupagent', { 'agentnum' => $agent->agentnum })
+% ) {
+ <A HREF="<%$p%>edit/access_group.html?<% $access_group->groupnum %>"><% $access_group->groupname |h %><BR>
+% }
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $agent->invoice_template || '(Default)' %>
+ </TD>
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>" VALIGN="bottom">
+ <TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#7e0079">
+ <% my $num_prospect = $agent->num_prospect_cust_main %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_prospect ) {
+
+ <A HREF="<% $cust_main_link %>&prospect=1">
+% }
+prospects
+% if ($num_prospect ) {
+</A>
+% }
+
+ <TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#0000CC">
+ <% my $num_inactive = $agent->num_inactive_cust_main %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_inactive ) {
+
+ <A HREF="<% $cust_main_link %>&inactive=1">
+% }
+inactive
+% if ( $num_inactive ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#00CC00">
+ <% my $num_active = $agent->num_active_cust_main %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_active ) {
+
+ <A HREF="<% $cust_main_link %>&active=1">
+% }
+active
+% if ( $num_active ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#FF9900">
+ <% my $num_susp = $agent->num_susp_cust_main %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_susp ) {
+
+ <A HREF="<% $cust_main_link %>&suspended=1">
+% }
+suspended
+% if ( $num_susp ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#FF0000">
+ <% my $num_cancel = $agent->num_cancel_cust_main %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_cancel ) {
+
+ <A HREF="<% $cust_main_link %>&showcancelledcustomers=1&cancelled=1">
+% }
+cancelled
+% if ( $num_cancel ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ </TABLE>
+ </TD>
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>" VALIGN="bottom">
+ <TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#0000CC">
+ <% my $num_inactive_pkg = $agent->num_inactive_cust_pkg %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_inactive_pkg ) {
+
+ <A HREF="<% $cust_pkg_link %>&magic=inactive">
+% }
+inactive
+% if ( $num_inactive_pkg ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#00CC00">
+ <% my $num_active_pkg = $agent->num_active_cust_pkg %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_active_pkg ) {
+
+ <A HREF="<% $cust_pkg_link %>&magic=active">
+% }
+active
+% if ( $num_active_pkg ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#FF9900">
+ <% my $num_susp_pkg = $agent->num_susp_cust_pkg %>&nbsp;
+ </FONT>
+
+ </TH>
+ <TD>
+% if ( $num_susp_pkg ) {
+
+ <A HREF="<% $cust_pkg_link %>&magic=suspended">
+% }
+suspended
+% if ( $num_susp_pkg ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right" WIDTH="40%">
+ <FONT COLOR="#FF0000">
+ <% my $num_cancel_pkg = $agent->num_cancel_cust_pkg %>&nbsp;
+ </FONT>
+ </TH>
+
+ <TD>
+% if ( $num_cancel_pkg ) {
+
+ <A HREF="<% $cust_pkg_link %>&magic=cancelled">
+% }
+cancelled
+% if ( $num_cancel_pkg ) {
+</A>
+% }
+
+ </TD>
+ </TR>
+
+ </TABLE>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<% $p %>graph/report_cust_pkg.html?agentnum=<% $agent->agentnum %>">Package&nbsp;Churn</A>
+ <BR><A HREF="<% $p %>search/report_cust_pay.html?agentnum=<% $agent->agentnum %>">Payments</A>
+ <BR><A HREF="<% $p %>search/report_cust_credit.html?agentnum=<% $agent->agentnum %>">Credits</A>
+ <BR><A HREF="<% $p %>search/report_receivables.cgi?agentnum=<% $agent->agentnum %>">A/R&nbsp;Aging</A>
+ <!--<BR><A HREF="<% $p %>search/money_time.cgi?agentnum=<% $agent->agentnum %>">Sales/Credits/Receipts</A>-->
+
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% my $num_reg_code = $agent->num_reg_code %>
+% if ( $num_reg_code ) {
+
+ <A HREF="<%$p%>search/reg_code.html?agentnum=<% $agent->agentnum %>">
+% }
+Unused
+% if ( $num_reg_code ) {
+</A>
+% }
+
+ <BR><A HREF="<%$p%>edit/reg_code.cgi?agentnum=<% $agent->agentnum %>">Generate codes</A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% my $num_prepay_credit = $agent->num_prepay_credit %>
+% if ( $num_prepay_credit ) {
+
+ <A HREF="<%$p%>search/prepay_credit.html?agentnum=<% $agent->agentnum %>">
+% }
+Unused
+% if ( $num_prepay_credit ) {
+</A>
+% }
+
+ <BR><A HREF="<%$p%>edit/prepay_credit.cgi?agentnum=<% $agent->agentnum %>">Generate cards</A>
+ </TD>
+% if ( $conf->config('ticket_system') ) {
+
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% if ( $agent->ticketing_queueid ) {
+
+ Queue: <% $agent->ticketing_queueid %>: <% $agent->ticketing_queue %><BR>
+% }
+
+ </TD>
+% }
+
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+ <TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
+% foreach my $override (
+% # sort { } want taxclass-full stuff first? and default cards (empty cardtype)
+% qsearch('agent_payment_gateway', { 'agentnum' => $agent->agentnum } )
+% ) {
+%
+
+ <TR>
+ <TD>
+ <% $override->cardtype || 'Default' %> to <% $override->payment_gateway->gateway_module %> (<% $override->payment_gateway->gateway_username %>)
+ <% $override->taxclass
+ ? ' for '. $override->taxclass. ' only'
+ : ''
+ %>
+ <FONT SIZE=-1><A HREF="<%$p%>misc/delete-agent_payment_gateway.cgi?<% $override->agentgatewaynum %>">(delete)</A></FONT>
+ </TD>
+ </TR>
+% }
+
+ <TR>
+ <TD><FONT SIZE=-1><A HREF="<%$p%>edit/agent_payment_gateway.html?agentnum=<% $agent->agentnum %>">(add override)</A></FONT></TD>
+ </TR>
+ </TABLE>
+ </TD>
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+ <TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
+% foreach my $override (
+% qsearch('conf', { 'agentnum' => $agent->agentnum } )
+% ) {
+%
+
+ <TR>
+ <TD>
+ <% $override->name %>&nbsp;<FONT SIZE=-1><A HREF="<%$p%>config/config-delete.cgi?<% $override->confnum %>">(delete)</A></FONT>
+ </TD>
+ </TR>
+% }
+
+ <TR>
+ <TD><FONT SIZE=-1><A HREF="<%$p%>config/config-view.cgi?agentnum=<% $agent->agentnum %>">(view/add/edit overrides)</A></FONT></TD>
+ </TR>
+ </TABLE>
+ </TD>
+
+ </TR>
+% }
+
+
+ </TABLE>
+ </BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my %search;
+if ( $cgi->param('showdisabled')
+ || !dbdef->table('agent')->column('disabled') ) {
+ %search = ();
+} else {
+ %search = ( 'disabled' => '' );
+}
+
+my $conf = new FS::Conf;
+
+</%init>
diff --git a/httemplate/browse/agent_type.cgi b/httemplate/browse/agent_type.cgi
new file mode 100755
index 0000000..d64ff18
--- /dev/null
+++ b/httemplate/browse/agent_type.cgi
@@ -0,0 +1,61 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Agent Types',
+ 'menubar' => [ 'Agents' =>"${p}browse/agent.cgi", ],
+ 'html_init' => $html_init,
+ 'name' => 'agent types',
+ 'query' => { 'table' => 'agent_type',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY typenum', # 'ORDER BY atype',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Agent Type',
+ 'Packages',
+ ],
+ 'fields' => [ 'typenum',
+ 'atype',
+ $packages_sub,
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init =
+'Agent types define groups of packages that you can then assign to'.
+' particular agents.<BR><BR>'.
+qq!<A HREF="${p}edit/agent_type.cgi"><I>Add a new agent type</I></A><BR><BR>!;
+
+my $count_query = 'SELECT COUNT(*) FROM agent_type';
+
+#false laziness w/access_user.html
+my $packages_sub = sub {
+my $agent_type = shift;
+
+[ map {
+ my $type_pkgs = $_;
+ #my $part_pkg = $type_pkgs->part_pkg;
+ [
+ {
+ #'data' => $part_pkg->pkg. ' - '. $part_pkg->comment,
+ 'data' => $type_pkgs->pkg. ' - '. $type_pkgs->comment,
+ 'align' => 'left',
+ 'link' => $p. 'edit/part_pkg.cgi?'. $type_pkgs->pkgpart,
+ },
+ ];
+ }
+
+ $agent_type->type_pkgs_enabled
+];
+
+};
+
+my $link = [ $p.'edit/agent_type.cgi?', 'typenum' ];
+
+</%init>
diff --git a/httemplate/browse/cust_main_county.cgi b/httemplate/browse/cust_main_county.cgi
new file mode 100755
index 0000000..736d7fd
--- /dev/null
+++ b/httemplate/browse/cust_main_county.cgi
@@ -0,0 +1,454 @@
+<% include( 'elements/browse.html',
+ 'title' => "Tax Rates $title",
+ 'name_singular' => 'tax rate',
+ 'menubar' => \@menubar,
+ 'html_init' => $html_init,
+ 'html_posttotal' => $html_posttotal,
+ 'html_form' => '<FORM NAME="taxesForm">',
+ 'html_foot' => $html_foot,
+ 'query' => {
+ 'table' => 'cust_main_county',
+ 'hashref' => $hashref,
+ 'order_by' =>
+ 'ORDER BY country, state, county, taxclass',
+ },
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'header2' => \@header2,
+ 'fields' => \@fields,
+ 'align' => $align,
+ 'color' => \@color,
+ 'cell_style' => \@cell_style,
+ 'links' => \@links,
+ 'link_onclicks' => \@link_onclicks,
+ )
+%>
+%
+% # <FONT SIZE=-1><A HREF="<% $p %>edit/process/cust_main_county-collapse.cgi?<% $hashref->{taxnum} %>">collapse state</A></FONT>
+% # % }
+%
+<%once>
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $exempt_sub = sub {
+ my $cust_main_county = shift;
+
+ my @exempt = ();
+ push @exempt,
+ sprintf("$money_char%.2f&nbsp;per&nbsp;month", $cust_main_county->exempt_amount )
+ if $cust_main_county->exempt_amount > 0;
+
+ push @exempt, 'Setup&nbsp;fee'
+ if $cust_main_county->setuptax =~ /^Y$/i;
+
+ push @exempt, 'Recurring&nbsp;fee'
+ if $cust_main_county->recurtax =~ /^Y$/i;
+
+ [ map [ {'data'=>$_} ], @exempt ];
+};
+
+my $oldrow;
+my $cell_style;
+my $cell_style_sub = sub {
+ my $row = shift;
+ if ( $oldrow ne $row ) {
+ if ( $oldrow ) {
+ if ( $oldrow->country ne $row->country ) {
+ $cell_style = 'border-top:1px solid #000000';
+ } elsif ( $oldrow->state ne $row->state ) {
+ $cell_style = 'border-top:1px solid #cccccc'; #default?
+ } elsif ( $oldrow->state eq $row->state ) {
+ #$cell_style = 'border-top:dashed 1px dark gray';
+ $cell_style = 'border-top:1px dashed #cccccc';
+ }
+ }
+ $oldrow = $row;
+ }
+ return $cell_style;
+};
+
+#my $edit_link = [ "${p}edit/cust_main_county.html", 'taxnum' ];
+my $edit_link = [ 'javascript:void(0);', sub { ''; } ];
+
+my $edit_onclick = sub {
+ my $row = shift;
+ my $taxnum = $row->taxnum;
+ include( '/elements/popup_link_onclick.html',
+ 'action' => "${p}edit/cust_main_county.html?$taxnum",
+ 'actionlabel' => 'Edit tax rate',
+ 'height' => 420,
+ #default# 'width' => 540,
+ #default# 'color' => '#333399',
+ );
+};
+
+sub expand_link {
+ my %param = @_;
+
+ my $taxnum = $param{'row'}->taxnum;
+ my $url = "${p}edit/cust_main_county-expand.cgi?$taxnum";
+
+ '<FONT SIZE="-1">'.
+ include( '/elements/popup_link.html',
+ 'label' => $param{'label'},
+ 'action' => $url,
+ 'actionlabel' => $param{'desc'},
+ 'height' => 420,
+ #default# 'width' => 540,
+ #default# 'color' => '#333399',
+ ).
+ '</FONT>';
+}
+
+sub separate_taxclasses_link {
+ my( $row ) = @_;
+ my $taxnum = $row->taxnum;
+ my $url = "${p}edit/process/cust_main_county-expand.cgi?taxclass=1;taxnum=$taxnum";
+
+ qq!<FONT SIZE="-1"><A HREF="$url">!;
+}
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+#my $conf = new FS::Conf;
+#my $money_char = $conf->config('money_char') || '$';
+my $enable_taxclasses = $conf->exists('enable_taxclasses');
+
+my @menubar;
+
+my $html_init =
+ "Click on <u>add states</u> to specify a country's tax rates by state or province.
+ <BR>Click on <u>add counties</u> to specify a state's tax rates by county.";
+$html_init .= "<BR>Click on <u>separate taxclasses</u> to specify taxes per taxclass."
+ if $enable_taxclasses;
+$html_init .= '<BR><BR>';
+
+$html_init .= include('/elements/init_overlib.html');
+
+my $title = '';
+
+my $country = '';
+if ( $cgi->param('country') =~ /^(\w\w)$/ ) {
+ $country = $1;
+ $title = $country;
+}
+$cgi->delete('country');
+
+my $state = '';
+if ( $country && $cgi->param('state') =~ /^([\w \-\'\[\]]+)$/ ) {
+ $state = $1;
+ $title = "$state, $title";
+}
+$cgi->delete('state');
+
+my $county = '';
+if ( $country && $state &&
+ $cgi->param('county') =~
+ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]+)$/
+ )
+{
+ $county = $1;
+ if ( $county eq '__NONE__' ) {
+ $title = "No county, $title";
+ } else {
+ $title = "$county county, $title";
+ }
+}
+$cgi->delete('county');
+
+$title = " for $title" if $title;
+
+my $taxclass = '';
+if ( $cgi->param('taxclass') =~ /^([\w \-]+)$/ ) {
+ $taxclass = $1;
+ $title .= " for $taxclass tax class";
+}
+$cgi->delete('taxclass');
+
+if ( $country || $taxclass ) {
+ push @menubar, 'View all tax rates' => $p.'browse/cust_main_county.cgi';
+}
+
+$cgi->param('dummy', 1);
+
+my $filter_change =
+ "window.location = '". $cgi->self_url.
+ ";country=' + encodeURIComponent( document.getElementById('country').options[document.getElementById('country').selectedIndex].value ) + ".
+ "';state=' + encodeURIComponent( document.getElementById('state').options[document.getElementById('state').selectedIndex].value ) +".
+ "';county=' + encodeURIComponent( document.getElementById('county').options[document.getElementById('county').selectedIndex].value );";
+
+#restore this so pagination works
+$cgi->param('country', $country) if $country;
+$cgi->param('state', $state ) if $state;
+$cgi->param('county', $county ) if $county;
+$cgi->param('taxclass', $county ) if $taxclass;
+
+my $html_posttotal =
+ '<BR>( show country: '.
+ include('/elements/select-country.html',
+ 'country' => $country,
+ 'onchange' => $filter_change,
+ 'empty_label' => '(all)',
+ 'disable_empty' => 0,
+ 'disable_stateupdate' => 1,
+ );
+
+my %states_hash = $country ? states_hash($country) : ();
+if ( scalar(keys(%states_hash)) > 1 ) {
+ $html_posttotal .=
+ ' show state: '.
+ include('/elements/select-state.html',
+ 'country' => $country,
+ 'state' => $state,
+ 'onchange' => $filter_change,
+ 'empty_label' => '(all)',
+ 'disable_empty' => 0,
+ 'disable_countyupdate' => 1,
+ );
+} else {
+ $html_posttotal .=
+ '<SELECT NAME="state" ID="state" STYLE="display:none">'.
+ ' <OPTION VALUE="" SELECTED>'.
+ '</SELECT>';
+}
+
+my @counties = ( $country && $state ) ? counties($state, $country) : ();
+if ( scalar(@counties) > 1 ) {
+ $html_posttotal .=
+ ' show county: '.
+ include('/elements/select-county.html',
+ 'country' => $country,
+ 'state' => $state,
+ 'county' => $county,
+ 'onchange' => $filter_change,
+ 'empty_label' => '(all)',
+ 'empty_data_label' => '(none)',
+ 'empty_data_value' => '__NONE__',
+ 'disable_empty' => 0,
+ 'disable_countyupdate' => 1,
+ );
+} else {
+ $html_posttotal .=
+ '<SELECT NAME="county" ID="county" STYLE="display:none">'.
+ ' <OPTION VALUE="" SELECTED>'.
+ '</SELECT>';
+}
+
+$html_posttotal .= ' )';
+
+my $bulk_popup_link =
+ include( '/elements/popup_link_onclick.html',
+ 'action' => "${p}edit/bulk-cust_main_county.html?MAGIC_taxnum_MAGIC",
+ 'actionlabel' => 'Bulk add new tax',
+ 'nofalse' => 1,
+ 'height' => 420,
+ #default# 'width' => 540,
+ #default# 'color' => '#333399',
+ );
+
+my $html_foot = <<END;
+<SCRIPT TYPE="text/javascript">
+
+ function setAll(setTo) {
+ theForm = document.taxesForm;
+ for (i=0,n=theForm.elements.length;i<n;i++) {
+ if (theForm.elements[i].name.indexOf("cust_main_county") != -1) {
+ theForm.elements[i].checked = setTo;
+ }
+ }
+ }
+
+ function toggleAll() {
+ theForm = document.taxesForm;
+ for (i=0,n=theForm.elements.length;i<n;i++) {
+ if (theForm.elements[i].name.indexOf("cust_main_county") != -1) {
+ if ( theForm.elements[i].checked == true ) {
+ theForm.elements[i].checked = false;
+ } else {
+ theForm.elements[i].checked = true;
+ }
+ }
+ }
+ }
+
+ function bulkPopup() {
+ var bulk_popup_link = "$bulk_popup_link";
+ var bulkstring = '';
+ theForm = document.taxesForm;
+ for (i=0,n=theForm.elements.length;i<n;i++) {
+ if ( theForm.elements[i].name.indexOf("cust_main_county") != -1
+ && theForm.elements[i].checked == true
+ ) {
+ var name = theForm.elements[i].name;
+ var taxnum = name.replace(/cust_main_county/, '');
+ if ( bulkstring != '' ) {
+ bulkstring = bulkstring + ',';
+ }
+ bulkstring = bulkstring + taxnum;
+
+ }
+ }
+ if ( bulk_popup_link.length > 1920 ) { // IE 2083 URL limit
+ alert('Too many selections'); // should do some session thing...
+ return false;
+ }
+ bulk_popup_link = bulk_popup_link.replace(/MAGIC_taxnum_MAGIC/, bulkstring);
+ eval(bulk_popup_link);
+ }
+
+</SCRIPT>
+
+<BR>
+<A HREF="javascript:setAll(true)">select all</A> |
+<A HREF="javascript:setAll(false)">unselect all</A> |
+<A HREF="javascript:toggleAll()">toggle all</A>
+<BR><BR>
+<A HREF="javascript:void(0);" onClick="bulkPopup();">Add new tax to selected</A>
+
+END
+
+my $hashref = {};
+my $count_query = 'SELECT COUNT(*) FROM cust_main_county';
+if ( $country ) {
+ $hashref->{'country'} = $country;
+ $count_query .= ' WHERE country = '. dbh->quote($country);
+}
+if ( $state ) {
+ $hashref->{'state'} = $state;
+ $count_query .= ' AND state = '. dbh->quote($state);
+}
+if ( $county ) {
+ if ( $county eq '__NONE__' ) {
+ $hashref->{'county'} = '';
+ $count_query .= " AND ( county = '' OR county IS NULL ) ";
+ } else {
+ $hashref->{'county'} = $county;
+ $count_query .= ' AND county = '. dbh->quote($county);
+ }
+}
+if ( $taxclass ) {
+ $hashref->{'taxclass'} = $taxclass;
+ $count_query .= ( $count_query =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ ' taxclass = '. dbh->quote($taxclass);
+}
+
+
+$cell_style = '';
+
+my @header = ( 'Country', 'State/Province', 'County',);
+my @header2 = ( '', '', '', );
+my @links = ( '', '', '', );
+my @link_onclicks = ( '', '', '', );
+my $align = 'lll';
+
+my @fields = (
+ sub { my $country = shift->country;
+ code2country($country). " ($country)";
+ },
+ sub { state_label($_[0]->state, $_[0]->country).
+ ( $_[0]->state
+ ? ''
+ : '&nbsp'. expand_link( desc => 'Add States',
+ row => $_[0],
+ label => 'add&nbsp;states',
+ )
+ )
+ },
+ sub { $_[0]->county || '(all)&nbsp'.
+ expand_link( desc => 'Add Counties',
+ row => $_[0],
+ label => 'add&nbsp;counties',
+ )
+ },
+);
+
+my @color = (
+ '000000',
+ sub { shift->state ? '000000' : '999999' },
+ sub { shift->county ? '000000' : '999999' },
+);
+
+if ( $conf->exists('enable_taxclasses') ) {
+ push @header, qq!Tax class (<A HREF="${p}edit/part_pkg_taxclass.html">add new</A>)!;
+ push @header2, '(per-package classification)';
+ push @fields, sub { $_[0]->taxclass || '(all)&nbsp'.
+ separate_taxclasses_link($_[0], 'Separate Taxclasses').
+ 'separate&nbsp;taxclasses</A></FONT>'
+ };
+ push @color, sub { shift->taxclass ? '000000' : '999999' };
+ push @links, '';
+ push @link_onclicks, '';
+ $align .= 'l';
+}
+
+push @header,
+ '', #checkbox column
+ 'Tax name',
+ 'Rate', #'Tax',
+ 'Exemptions',
+ ;
+
+push @header2,
+ '',
+ '(printed on invoices)',
+ '',
+ '',
+ ;
+
+my $newregion = 1;
+my $cb_oldrow = '';
+my $cb_sub = sub {
+ my $cust_main_county = shift;
+
+ if ( $cb_oldrow ) {
+ if ( $cb_oldrow->country ne $cust_main_county->country
+ || $cb_oldrow->state ne $cust_main_county->state
+ || $cb_oldrow->county ne $cust_main_county->county
+ || $cb_oldrow->taxclass ne $cust_main_county->taxclass )
+ {
+ $newregion = 1;
+ } else {
+ $newregion = 0;
+ }
+
+ } else {
+ $newregion = 1;
+ }
+ $cb_oldrow = $cust_main_county;
+
+ if ( $newregion ) {
+ my $taxnum = $cust_main_county->taxnum;
+ qq!<INPUT NAME="cust_main_county$taxnum" TYPE="checkbox" VALUE="1">!;
+ } else {
+ '';
+ }
+};
+
+push @fields,
+ $cb_sub,
+ sub { shift->taxname || 'Tax' },
+ sub { shift->tax. '%&nbsp;<FONT SIZE="-1">(edit)</FONT>' },
+ $exempt_sub,
+;
+
+push @color,
+ '000000',
+ sub { shift->taxname ? '000000' : '666666' },
+ sub { shift->tax ? '000000' : '666666' },
+ '000000',
+;
+
+$align .= 'clrl';
+
+my @cell_style = map $cell_style_sub, (1..scalar(@header));
+
+push @links, '', '', $edit_link, '';
+push @link_onclicks, '', '', $edit_onclick, '';
+
+</%init>
diff --git a/httemplate/browse/elements/browse.html b/httemplate/browse/elements/browse.html
new file mode 100644
index 0000000..513c2c4
--- /dev/null
+++ b/httemplate/browse/elements/browse.html
@@ -0,0 +1,6 @@
+<% include( '/search/elements/search.html',
+ 'disable_download' => 1,
+ 'disable_nonefound' => 1,
+ @_,
+ )
+%>
diff --git a/httemplate/browse/inventory_class.html b/httemplate/browse/inventory_class.html
new file mode 100644
index 0000000..8ce131a
--- /dev/null
+++ b/httemplate/browse/inventory_class.html
@@ -0,0 +1,93 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Inventory Classes',
+ 'name' => 'inventory classes',
+ 'menubar' => [ 'Add a new inventory class' =>
+ $p.'edit/inventory_class.html',
+ ],
+ 'query' => { 'table' => 'inventory_class', },
+ 'count_query' => 'SELECT COUNT(*) FROM inventory_class',
+ 'header' => [ '#', 'Inventory class', 'Inventory' ],
+ 'fields' => [ 'classnum',
+ 'classname',
+ sub {
+ #my $inventory_class = shift;
+ my $i_c = shift;
+
+ my $link =
+ $p. 'search/inventory_item.html?'.
+ 'classnum='. $i_c->classnum;
+
+ my %actioncol = ();
+ foreach ( keys %inv_action_link ) {
+ my($label, $baseurl, $method) =
+ @{ $inv_action_link{$_} };
+ my $url = $baseurl. $i_c->$method();
+ $actioncol{$_} =
+ '<FONT SIZE="-1">'.
+ '('.
+ '<A HREF="'.$url.'">'.
+ $label.
+ '</A>'.
+ ')'.
+ '</FONT>';
+ }
+
+ my %num = map {
+ $_ => $i_c->$_();
+ } keys %labels;
+
+ [ map {
+ [
+ {
+ 'data' => '<B>'. $num{$_}. '</B>',
+ 'align' => 'right',
+ },
+ {
+ 'data' => $labels{$_},
+ 'align' => 'left',
+ 'link' => ( $num{$_}
+ ? $link.$link{$_}
+ : ''
+ ),
+ },
+ { 'data' => $actioncol{$_},
+ 'align' => 'left',
+ },
+ ]
+ } keys %labels
+ ];
+ },
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+tie my %labels, 'Tie::IxHash',
+ 'num_avail' => 'Available', # <FONT SIZE="-1"><A HREF="eventually">(upload batch)</A></FONT>',
+ 'num_used' => 'In use', #'Used', #'Allocated',
+ 'num_total' => 'Total',
+;
+
+my %link = (
+ 'num_avail' => ';avail=1',
+ 'num_used' => ';used=1',
+ 'num_total' => '',
+);
+
+my %inv_action_link = (
+ 'num_avail' => [ 'upload batch',
+ $p.'misc/inventory_item-import.html?classnum=',
+ 'classnum'
+ ],
+);
+
+my $link = [ "${p}edit/inventory_class.html?", 'classnum' ];
+
+</%init>
diff --git a/httemplate/browse/invoice_template.html b/httemplate/browse/invoice_template.html
new file mode 100644
index 0000000..0bbfb24
--- /dev/null
+++ b/httemplate/browse/invoice_template.html
@@ -0,0 +1,124 @@
+<% include("/elements/header.html", 'Invoice templates') %>
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Template</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">HTML</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Print/PDF (typeset)</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Plaintext</TH>
+</TR>
+
+% foreach my $templatename ( '', @templatenames ) {
+% my $tname = length($templatename) ? "_$templatename" : '';
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% my $display = length($templatename) ? $templatename : '<i>(Default)</i>';
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $display %>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+
+% my( $logo_label, $logo_link_label)= length( $templatename )
+% ? labels("logo_$templatename.png")
+% : ( '', 'edit' );
+ <% $logo_label %> Logo
+ (<A HREF="<% $p %>edit/invoice_logo.html?type=png;name=<% $templatename %>"><% $logo_link_label %></A>)
+ <BR>
+
+% foreach my $suffix (qw( returnaddress notes footer), '' ) {
+% my $file = "invoice_html$suffix$tname";
+% my($label, $link_label) = length($templatename)
+% ? labels($file)
+% : ( '', 'edit' );
+
+ <% $label %> <% $suffix2name{$suffix} %>
+ (<A HREF="<% $p %>edit/invoice_template.html?type=html;suffix=<% $suffix %>;name=<% $templatename %>"><% $link_label %></A>)
+ <BR>
+
+% }
+
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+
+% my( $logo_label, $logo_link_label)= length( $templatename )
+% ? labels("logo_$templatename.eps")
+% : ( '', 'edit' );
+ <% $logo_label %> Logo
+ (<A HREF="<% $p %>edit/invoice_logo.html?type=eps;name=<% $templatename %>"><% $logo_link_label %></A>)
+ <BR>
+
+% foreach my $suffix (qw( returnaddress notes footer smallfooter), '' ) {
+% my $file = "invoice_latex$suffix$tname";
+% my($label, $link_label) = length($templatename)
+% ? labels($file)
+% : ( '', 'edit' );
+
+ <% $label %> <% $suffix2name{$suffix} %>
+ (<A HREF="<% $p %>edit/invoice_template.html?type=latex;suffix=<% $suffix %>;name=<% $templatename %>"><% $link_label %></A>)
+ <BR>
+
+% }
+
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+
+% my( $txt_label, $txtlink_label)=
+% length( $templatename )
+% ? labels("invoice_template_$templatename.png")
+% : ( 'Main template', 'edit' );
+ <% $txt_label %>
+ (<A HREF="<% $p %>edit/invoice_template.html?type=text;name=<% $templatename %>"><% $txtlink_label %></A>)
+
+ </TD>
+
+ </TR>
+
+% }
+
+<% include("/elements/footer.html") %>
+
+<%once>
+
+my %suffix2name = (
+ 'returnaddress' => 'Return address',
+ 'notes' => 'Notes',
+ 'footer' => 'Footer',
+ 'smallfooter' => 'Small footer',
+ '' => 'Main template',
+);
+
+my $conf = new FS::Conf;
+
+sub labels {
+ my $filename = shift;
+ if ( $conf->exists($filename) ) {
+ ( 'Custom', 'edit' );
+ } else {
+ ( 'Standard', 'customize' );
+ }
+}
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @templatenames = $conf->invoice_templatenames;
+
+</%init>
diff --git a/httemplate/browse/msgcat.cgi b/httemplate/browse/msgcat.cgi
new file mode 100755
index 0000000..2c916dc
--- /dev/null
+++ b/httemplate/browse/msgcat.cgi
@@ -0,0 +1,44 @@
+<% include('/elements/header.html', "View Message catalog", menubar(
+ 'Edit message catalog' => $p. "edit/msgcat.cgi",
+)) %>
+<% $widget->html %>
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $widget = new HTML::Widgets::SelectLayers(
+ 'selected_layer' => 'en_US',
+ 'options' => { 'en_US'=>'en_US' },
+ 'layer_callback' => sub {
+ my $layer = shift;
+ my $html = "<BR>Messages for locale $layer<BR>". table().
+ "<TR><TH COLSPAN=2>Code</TH>".
+ "<TH>Message</TH>";
+ $html .= "<TH>en_US Message</TH>" unless $layer eq 'en_US';
+ $html .= '</TR>';
+
+ #foreach my $msgcat ( sort { $a->msgcode cmp $b->msgcode }
+ # qsearch('msgcat', { 'locale' => $layer } ) ) {
+ foreach my $msgcat ( qsearch('msgcat', { 'locale' => $layer } ) ) {
+ $html .= '<TR><TD>'. $msgcat->msgnum. '</TD>'.
+ '<TD>'. $msgcat->msgcode. '</TD>'.
+ '<TD>'. $msgcat->msg. '</TD>';
+ unless ( $layer eq 'en_US' ) {
+ my $en_msgcat = qsearchs('msgcat', {
+ 'locale' => 'en_US',
+ 'msgcode' => $msgcat->msgcode,
+ } );
+ $html .= '<TD>'. $en_msgcat->msg. '</TD>';
+ }
+ $html .= '</TR>';
+ }
+
+ $html .= '</TABLE>';
+ $html;
+ },
+
+);
+
+</%init>
diff --git a/httemplate/browse/nas.cgi b/httemplate/browse/nas.cgi
new file mode 100755
index 0000000..b5e0ef8
--- /dev/null
+++ b/httemplate/browse/nas.cgi
@@ -0,0 +1,82 @@
+%print header('NAS ports');
+%
+%my $now = time;
+%
+%foreach my $nas ( sort { $a->nasnum <=> $b->nasnum } qsearch( 'nas', {} ) ) {
+% print $nas->nasnum. ": ". $nas->nas. " ".
+% $nas->nasfqdn. " (". $nas->nasip. ") ".
+% "as of ". time2str("%c",$nas->last).
+% " (". &pretty_interval($now - $nas->last). " ago)<br>".
+% &table(). "<TR><TH>Nas<BR>Port #</TH><TH>Global<BR>Port #</BR></TH>".
+% "<TH>IP address</TH><TH>User</TH><TH>Since</TH><TH>Duration</TH><TR>",
+% ;
+% foreach my $port ( sort {
+% $a->nasport <=> $b->nasport || $a->portnum <=> $b->portnum
+% } qsearch( 'port', { 'nasnum' => $nas->nasnum } ) ) {
+% my $session = $port->session;
+% my($user, $since, $pretty_since, $duration);
+% if ( ! $session ) {
+% $user = "(empty)";
+% $since = 0;
+% $pretty_since = "(never)";
+% $duration = '';
+% } elsif ( $session->logout ) {
+% $user = "(empty)";
+% $since = $session->logout;
+% } else {
+% my $svc_acct = $session->svc_acct;
+% $user = "<A HREF=\"$p/view/svc_acct.cgi?". $svc_acct->svcnum. "\">".
+% $svc_acct->username. "</A>";
+% $since = $session->login;
+% }
+% $pretty_since = time2str("%c", $since) if $since;
+% $duration = pretty_interval( $now - $since ). " ago"
+% unless defined($duration);
+% print "<TR><TD>". $port->nasport. "</TD><TD>". $port->portnum. "</TD><TD>".
+% $port->ip. "</TD><TD>$user</TD><TD>$pretty_since".
+% "</TD><TD>$duration</TD></TR>"
+% ;
+% }
+% print "</TABLE><BR>";
+%}
+%
+%#Time::Duration??
+%sub pretty_interval {
+% my $interval = shift;
+% my %howlong = (
+% '604800' => 'week',
+% '86400' => 'day',
+% '3600' => 'hour',
+% '60' => 'minute',
+% '1' => 'second',
+% );
+%
+% my $pretty = "";
+% foreach my $key ( sort { $b <=> $a } keys %howlong ) {
+% my $value = int( $interval / $key );
+% if ( $value ) {
+% if ( $value == 1 ) {
+% $pretty .=
+% ( $howlong{$key} eq 'hour' ? 'an ' : 'a ' ). $howlong{$key}. " "
+% } else {
+% $pretty .= $value. ' '. $howlong{$key}. 's ';
+% }
+% }
+% $interval -= $value * $key;
+% }
+% $pretty =~ /^\s*(\S.*\S)\s*$/;
+% $1;
+%}
+%
+%#print &table(), <<END;
+%#<TR>
+%# <TH>#</TH>
+%# <TH>NAS</
+%
+
+<%init>
+
+#this hasn't been used in ages, and isn't linked from anywhere...
+die 'NAS browse not currently active';
+
+</%init>
diff --git a/httemplate/browse/part_bill_event.cgi b/httemplate/browse/part_bill_event.cgi
new file mode 100755
index 0000000..11bc14e
--- /dev/null
+++ b/httemplate/browse/part_bill_event.cgi
@@ -0,0 +1,122 @@
+<% include('/elements/header.html', 'Invoice Event Listing') %>
+
+ <FONT SIZE="+1">Invoice events are the deprecated, old-style actions taken on open invoices. Any events still listed here should be migrated to new-style events.</FONT><BR><BR>
+
+<A HREF="<% $p %>edit/part_bill_event.cgi"><I>Add a new invoice event</I></A>
+<BR><BR>
+
+<% $total %> events
+<% $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled events</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled events</a> )'; }
+%>
+<BR><BR>
+% tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2longname;
+% tie my %freq, 'Tie::IxHash', '1d' => 'daily', '1m' => 'monthly';
+% foreach my $payby ( keys %payby ) {
+% my $oldfreq = '';
+%
+% my @payby_part_bill_event =
+% grep { $payby eq $_->payby }
+% sort { ( $a->freq || '1d') cmp ( $b->freq || '1d' ) # for now
+% || $a->seconds <=> $b->seconds
+% || $a->weight <=> $b->weight
+% || $a->eventpart <=> $b->eventpart
+% }
+% @part_bill_event;
+%
+%
+% if ( @payby_part_bill_event ) {
+
+
+ <% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor;
+%
+%
+% foreach my $part_bill_event ( @payby_part_bill_event ) {
+% my $url = "${p}edit/part_bill_event.cgi?". $part_bill_event->eventpart;
+% my $delay = duration_exact($part_bill_event->seconds);
+% ( my $plandata = $part_bill_event->plandata ) =~ s/\n/<BR>/go;
+% my $freq = $part_bill_event->freq || '1d';
+% my $reason = $part_bill_event->reasontext ;
+%
+% if ( $oldfreq ne $freq ) {
+
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#999999" COLSPAN=<% $cgi->param('showdisabled') ? 7 : 8 %>><% ucfirst($freq{$freq}) %> event tests for <FONT SIZE="+1"><I><% $payby{$payby} %> customers</I></FONT></TH>
+ </TR>
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=<% $cgi->param('showdisabled') ? 2 : 3 %>>Event</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">After</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Action</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Reason</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Options</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Code</TH>
+ </TR>
+%
+% $oldfreq = $freq;
+% $bgcolor = '';
+%
+% }
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+
+
+ <TR>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><A HREF="<% $url %>">
+ <% $part_bill_event->eventpart %></A></TD>
+% unless ( $cgi->param('showdisabled') ) {
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $part_bill_event->disabled ? 'DISABLED' : '' %></TD>
+% }
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><A HREF="<% $url %>">
+ <% $part_bill_event->event %></A></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $delay %></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $part_bill_event->plan %></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $reason %></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $plandata %></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><FONT SIZE="-1">
+ <% $part_bill_event->eventcode %></FONT></TD>
+ </TR>
+% }
+
+ </TABLE>
+ <BR><BR>
+% }
+% }
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my %search;
+if ( $cgi->param('showdisabled') ) {
+%search = ();
+} else {
+%search = ( 'disabled' => '' );
+}
+
+my @part_bill_event = qsearch('part_bill_event', \%search );
+my $total = scalar(@part_bill_event);
+
+</%init>
diff --git a/httemplate/browse/part_event.html b/httemplate/browse/part_event.html
new file mode 100644
index 0000000..674004b
--- /dev/null
+++ b/httemplate/browse/part_event.html
@@ -0,0 +1,167 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Billing Event Definitions',
+ 'html_init' => $html_init,
+ 'name' => 'billing event definitions',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 2,
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Edit global billing events',
+ 'agent_pos' => 3,
+ 'query' => { 'select' => 'part_event.*',
+ 'table' => 'part_event',
+ 'addl_from' => $join_conditions,
+ 'hashref' => {},
+ 'order_by' => $order_conditions,
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Event',
+ 'Type',
+ 'Check freq.',
+ 'Conditions',
+ 'Action',
+ ],
+ 'fields' => [ 'eventpart',
+ 'event',
+ $eventtable_sub,
+ $check_freq_sub,
+ $conditions_sub,
+ $action_sub,
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ '',
+ '',
+ '',
+ ],
+ 'align' => 'rllccc',
+ )
+%>
+<%once>
+
+my $eventtable_labels = FS::part_event->eventtable_labels;
+my $eventtable_sub = sub { $eventtable_labels->{ shift->eventtable }; };
+
+my $check_freq_labels = FS::part_event->check_freq_labels;
+my $check_freq_sub = sub { $check_freq_labels->{ shift->check_freq }; };
+
+my $conditions_sub = sub {
+ my $part_event = shift;
+ my $addl = 0;
+
+ [
+ map {
+ my $part_event_condition = $_;
+ my %options = $part_event_condition->options;
+
+ [
+ {
+ 'data' => $part_event_condition->description,
+ 'width' => '100%',
+ 'align' => 'center',
+ 'colspan' => 2,
+ 'style' => ( $addl++ ? 'border-top: 1px solid gray' : '' ),
+ },
+ ],
+
+ map {
+
+ my $data = $options{$_};
+ if ( ref($data) ) {
+ $data = join('<BR>', keys %$data); #XXX display hash values too?
+ }
+
+ [
+ {
+ 'data' => $part_event_condition->option_label($_). ':',
+ 'align' => 'right',
+ 'valign' => 'top',
+ 'size' => '-1',
+ },
+ {
+ 'data' => $data,
+ 'align' => 'left',
+ 'size' => '-1',
+ },
+ ];
+
+ } keys %options
+
+ }
+ $part_event->part_event_condition
+
+ ];
+
+};
+
+my $action_sub = sub {
+ my $part_event = shift;
+
+ my %options = $part_event->options;
+
+ [
+
+ [
+ {
+ 'data' => $part_event->description,
+ 'width' => '100%',
+ 'align' => 'center',
+ 'colspan' => 2,
+ },
+ ],
+
+ map {
+ [
+ {
+ 'data' => $part_event->option_label($_). ':',
+ 'align' => 'right',
+ 'size' => '-1',
+ },
+ {
+ 'data' => $options{$_},
+ 'align' => 'left',
+ 'size' => '-1',
+ },
+ ];
+ }
+
+ keys %options
+ ];
+
+};
+
+my $link = [ $p.'edit/part_event.html?', 'eventpart' ];
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit billing events')
+ || $FS::CurrentUser::CurrentUser->access_right('Edit global billing events');
+
+my $html_init =
+ #XXX better description
+ 'Events are billing, collection or other actions triggered when certain '.
+ 'customer, invoice, package or other conditions are met.<BR><BR>'.
+ qq!<FORM METHOD="POST" ACTION="${p}edit/part_event.html">!.
+ qq!<A HREF="${p}edit/part_event.html"><I>Add a new event</I></A>!.
+ '&nbsp;or&nbsp;<SELECT NAME="clone"><OPTION></OPTION>';
+
+foreach my $part_event ( qsearch('part_event', {'diabled'=>''}) ) {
+ $html_init .= '<OPTION VALUE="'. $part_event->eventpart. '">'.
+ $part_event->event. '</OPTION>';
+}
+
+$html_init .= '</SELECT><INPUT TYPE="submit" VALUE="Clone existing event">'.
+ '</FORM><BR>';
+
+my $count_query = 'SELECT COUNT(*) FROM part_event WHERE '.
+ $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'Edit global billing events',
+ );
+
+my $join_conditions = FS::part_event_condition->join_conditions_sql;
+my $order_conditions = FS::part_event_condition->order_conditions_sql;
+
+</%init>
diff --git a/httemplate/browse/part_export.cgi b/httemplate/browse/part_export.cgi
new file mode 100755
index 0000000..1cd2013
--- /dev/null
+++ b/httemplate/browse/part_export.cgi
@@ -0,0 +1,65 @@
+<% include("/elements/header.html", "Export Listing") %>
+
+Provisioning services to external machines, databases and APIs.<BR><BR>
+
+<A HREF="<% $p %>edit/part_export.cgi"><I>Add a new export</I></A><BR><BR>
+
+<SCRIPT>
+function part_export_areyousure(href) {
+ if (confirm("Are you sure you want to delete this export?") == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+ <TR>
+ <TH COLSPAN=2 CLASS="grid" BGCOLOR="#cccccc">Export</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Options</TH>
+ </TR>
+
+% foreach my $part_export ( sort {
+% $a->getfield('exportnum') <=> $b->getfield('exportnum')
+% } qsearch('part_export',{})
+% ) {
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><A HREF="<% $p %>edit/part_export.cgi?<% $part_export->exportnum %>"><% $part_export->exportnum %></A></TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $part_export->exporttype %> to <% $part_export->machine %> (<A HREF="<% $p %>edit/part_export.cgi?<% $part_export->exportnum %>">edit</A>&nbsp;|&nbsp;<A HREF="javascript:part_export_areyousure('<% $p %>misc/delete-part_export.cgi?<% $part_export->exportnum %>')">delete</A>)</TD>
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+ <% itable() %>
+% my %opt = $part_export->options;
+% foreach my $opt ( keys %opt ) {
+
+ <TR>
+ <TD ALIGN="right" VALIGN="top" WIDTH="33%"><% $opt %>:&nbsp;</TD>
+ <TD ALIGN="left" WIDTH="67%"><% encode_entities($opt{$opt}) %></TD>
+ </TR>
+% }
+
+ </TABLE>
+ </TD>
+
+ </TR>
+
+% }
+
+</TABLE>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+</%init>
diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi
new file mode 100755
index 0000000..801c09f
--- /dev/null
+++ b/httemplate/browse/part_pkg.cgi
@@ -0,0 +1,367 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Package Definitions',
+ 'html_init' => $html_init,
+ 'name' => 'package definitions',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 3,
+ 'agent_virt' => 1,
+ 'agent_null_right' => [ $edit, $edit_global ],
+ 'agent_null_right_link' => $edit_global,
+ 'agent_pos' => 5,
+ 'query' => { 'select' => $select,
+ 'table' => 'part_pkg',
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ 'order_by' => "ORDER BY $orderby"
+ },
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'links' => \@links,
+ 'align' => $align,
+ )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $edit = 'Edit package definitions';
+my $edit_global = 'Edit global package definitions';
+my $acl_edit = $curuser->access_right($edit);
+my $acl_edit_global = $curuser->access_right($edit_global);
+my $acl_config = $curuser->access_right('Configuration'); #to edit services
+ #and agent types
+
+die "access denied"
+ unless $acl_edit || $acl_edit_global;
+
+my $conf = new FS::Conf;
+my $taxclasses = $conf->exists('enable_taxclasses');
+my $money_char = $conf->config('money_char') || '$';
+
+my $select = '*';
+my $orderby = 'pkgpart';
+if ( $cgi->param('active') ) {
+ $orderby = 'num_active DESC';
+}
+
+my $extra_sql = '';
+
+unless ( $acl_edit_global ) {
+ $extra_sql .= ' WHERE '. FS::part_pkg->curuser_pkgs_sql;
+}
+
+my $agentnums = join(',', $curuser->agentnums);
+my $count_cust_pkg = "
+ SELECT COUNT(*) FROM cust_pkg LEFT JOIN cust_main USING ( custnum )
+ WHERE cust_pkg.pkgpart = part_pkg.pkgpart
+ AND cust_main.agentnum IN ($agentnums)
+";
+
+$select = "
+
+ *,
+
+ ( $count_cust_pkg
+ AND ( cancel IS NULL OR cancel = 0 )
+ AND ( susp IS NULL OR susp = 0 )
+ ) AS num_active,
+
+ ( $count_cust_pkg
+ AND ( cancel IS NULL OR cancel = 0 )
+ AND susp IS NOT NULL AND susp != 0
+ ) AS num_suspended,
+
+ ( $count_cust_pkg
+ AND cancel IS NOT NULL AND cancel != 0
+ ) AS num_cancelled
+
+";
+
+my $html_init;
+#unless ( $cgi->param('active') ) {
+ $html_init = qq!
+ One or more service definitions are grouped together into a package
+ definition and given pricing information. Customers purchase packages
+ rather than purchase services directly.<BR><BR>
+ <FORM METHOD="POST" ACTION="${p}edit/part_pkg.cgi">
+ <A HREF="${p}edit/part_pkg.cgi"><I>Add a new package definition</I></A>
+ or
+ !.include('/elements/select-part_pkg.html', 'element_name' => 'clone' ). qq!
+ <INPUT TYPE="submit" VALUE="Clone existing package">
+ </FORM>
+ <BR><BR>
+ !;
+#}
+
+# ------
+
+my $link = [ $p.'edit/part_pkg.cgi?', 'pkgpart' ];
+
+my @header = ( '#', 'Package', 'Comment' );
+my @fields = ( 'pkgpart', 'pkg', 'comment' );
+my $align = 'rll';
+my @links = ( $link, $link, '' );
+
+unless ( 0 ) { #already showing only one class or something?
+ push @header, 'Class';
+ push @fields, sub { shift->classname || '(none)'; };
+ $align .= 'l';
+}
+
+tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() };
+
+tie my %plan_labels, 'Tie::IxHash',
+ map { $_ => ( $plans{$_}->{'shortname'} || $plans{$_}->{'name'} ) }
+ keys %plans;
+
+push @header, 'Pricing';
+$align .= 'r'; #?
+push @fields, sub {
+ my $part_pkg = shift;
+ (my $plan = $plan_labels{$part_pkg->plan} ) =~ s/ /&nbsp;/g;
+ my $is_recur = ( $part_pkg->freq ne '0' );
+
+ [
+ [
+ { data =>$plan,
+ align=>'center',
+ colspan=>2,
+ },
+ ],
+ [
+ { data =>$money_char.
+ sprintf('%.2f', $part_pkg->option('setup_fee') ),
+ align=>'right'
+ },
+ { data => ( $is_recur ? ' setup' : ' one-time' ),
+ align=>'left',
+ },
+ ],
+ [
+ { data=>( $is_recur
+ ? $money_char.sprintf('%.2f ', $part_pkg->option('recur_fee') )
+ : $part_pkg->freq_pretty
+ ),
+ align=> ( $is_recur ? 'right' : 'center' ),
+ colspan=> ( $is_recur ? 1 : 2 ),
+ },
+ ( $is_recur
+ ? { data => ( $is_recur ? $part_pkg->freq_pretty : '' ),
+ align=>'left',
+ }
+ : ()
+ ),
+ ],
+ ( map {
+ my $dst_pkg = $_->dst_pkg;
+ [
+ { data => 'Add-on:&nbsp;'.$dst_pkg->pkg_comment,
+ align=>'center', #?
+ colspan=>2,
+ }
+ ]
+ }
+ $part_pkg->bill_part_pkg_link
+ ),
+ ];
+
+# $plan_labels{$part_pkg->plan}.'<BR>'.
+# $money_char.sprintf('%.2f setup<BR>', $part_pkg->option('setup_fee') ).
+# ( $part_pkg->freq ne '0'
+# ? $money_char.sprintf('%.2f ', $part_pkg->option('recur_fee') )
+# : ''
+# ).
+# $part_pkg->freq_pretty; #.'<BR>'
+};
+
+###
+# Agent goes here if displayed
+###
+
+#agent type
+if ( $acl_edit_global ) {
+ #really we just want a count, but this is fine unless someone has tons
+ my @all_agent_types = map {$_->typenum} qsearch('agent_type',{});
+ if ( scalar(@all_agent_types) > 1 ) {
+ push @header, 'Agent types';
+ my $typelink = $p. 'edit/agent_type.cgi?';
+ push @fields, sub { my $part_pkg = shift;
+ [
+ map { warn $_;
+ my $agent_type = $_->agent_type;
+ warn $agent_type;
+ [
+ { 'data' => $agent_type->atype, #escape?
+ 'align' => 'left',
+ 'link' => ( $acl_config
+ ? $typelink.
+ $agent_type->typenum
+ : ''
+ ),
+ },
+ ];
+ }
+ $part_pkg->type_pkgs
+ ];
+ };
+ $align .= 'l';
+ }
+}
+
+#if ( $cgi->param('active') ) {
+ push @header, 'Customer<BR>packages';
+ my %col = (
+ 'active' => '00CC00',
+ 'suspended' => 'FF9900',
+ 'cancelled' => 'FF0000',
+ #'one-time charge' => '000000',
+ 'charge' => '000000',
+ );
+ my $cust_pkg_link = $p. 'search/cust_pkg.cgi?pkgpart=';
+ push @fields, sub { my $part_pkg = shift;
+ [
+ map {
+ my $magic = $_;
+ my $label = $_;
+ if ( $magic eq 'active' && $part_pkg->freq == 0 ) {
+ $magic = 'inactive';
+ #$label = 'one-time charge',
+ $label = 'charge',
+ }
+
+ [
+ {
+ 'data' => '<B><FONT COLOR="#'. $col{$label}. '">'.
+ $part_pkg->get("num_$_").
+ '</FONT></B>',
+ 'align' => 'right',
+ },
+ {
+ 'data' => $label.
+ ( $part_pkg->get("num_$_") != 1
+ && $label =~ /charge$/
+ ? 's'
+ : ''
+ ),
+ 'align' => 'left',
+ 'link' => ( $part_pkg->get("num_$_")
+ ? $cust_pkg_link.
+ $part_pkg->pkgpart.
+ ";magic=$magic"
+ : ''
+ ),
+ },
+ ],
+ } (qw( active suspended cancelled ))
+ ]; };
+ $align .= 'r';
+#}
+
+if ( $taxclasses ) {
+ push @header, 'Taxclass';
+ push @fields, sub { shift->taxclass() || '&nbsp;'; };
+ $align .= 'l';
+}
+
+push @header, 'Plan options',
+ 'Services';
+ #'Service', 'Quan', 'Primary';
+
+push @fields,
+ sub {
+ my $part_pkg = shift;
+ if ( $part_pkg->plan ) {
+
+ my %options = $part_pkg->options;
+
+ [ map {
+ [
+ { 'data' => $_,
+ 'align' => 'right',
+ },
+ { 'data' => $part_pkg->format($_,$options{$_}),
+ 'align' => 'left',
+ },
+ ];
+ }
+ grep { $options{$_} =~ /\S/ }
+ grep { $_ !~ /^(setup|recur)_fee$/ }
+ keys %options
+ ];
+
+ } else {
+
+ [ map { [
+ { 'data' => uc($_),
+ 'align' => 'right',
+ },
+ {
+ 'data' => $part_pkg->$_(),
+ 'align' => 'left',
+ },
+ ];
+ }
+ (qw(setup recur))
+ ];
+
+ }
+
+ },
+
+ sub {
+ my $part_pkg = shift;
+
+ [
+ (map {
+ my $pkg_svc = $_;
+ my $part_svc = $pkg_svc->part_svc;
+ my $svc = $part_svc->svc;
+ if ( $pkg_svc->primary_svc =~ /^Y/i ) {
+ $svc = "<B>$svc (PRIMARY)</B>";
+ }
+ $svc =~ s/ +/&nbsp;/g;
+
+ [
+ {
+ 'data' => '<B>'. $pkg_svc->quantity. '</B>',
+ 'align' => 'right'
+ },
+ {
+ 'data' => $svc,
+ 'align' => 'left',
+ 'link' => ( $acl_config
+ ? $p. 'edit/part_svc.cgi?'.
+ $part_svc->svcpart
+ : ''
+ ),
+ },
+ ];
+ }
+ sort { $b->primary_svc =~ /^Y/i
+ <=> $a->primary_svc =~ /^Y/i
+ }
+ $part_pkg->pkg_svc('disable_linked'=>1)
+ ),
+ ( map {
+ my $dst_pkg = $_->dst_pkg;
+ [
+ { data => 'Add-on:&nbsp;'.$dst_pkg->pkg_comment,
+ align=>'center', #?
+ colspan=>2,
+ }
+ ]
+ }
+ $part_pkg->svc_part_pkg_link
+ )
+ ];
+
+ };
+
+$align .= 'lrl'; #rr';
+
+# --------
+
+my $count_query = "SELECT COUNT(*) FROM part_pkg $extra_sql";
+
+</%init>
diff --git a/httemplate/browse/part_pkg_taxproduct.cgi b/httemplate/browse/part_pkg_taxproduct.cgi
new file mode 100755
index 0000000..7e0cb81
--- /dev/null
+++ b/httemplate/browse/part_pkg_taxproduct.cgi
@@ -0,0 +1,263 @@
+<% include( 'elements/browse.html',
+ 'title' => "Tax Products $title",
+ 'name_singular' => 'tax product',
+ 'menubar' => \@menubar,
+ 'html_init' => $html_init,
+ 'query' => {
+ 'table' => 'part_pkg_taxproduct',
+ 'hashref' => $hashref,
+ 'order_by' => 'ORDER BY description',
+ 'extra_sql' => $extra_sql,
+ },
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'align' => $align,
+ 'links' => \@links,
+ 'link_onclicks' => \@link_onclicks,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+
+my $select_link = [ 'javascript:void(0);', sub { ''; } ];
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @menubar;
+my $title = '';
+my $onclick = 'cClick';
+
+my $data_vendor = '';
+if ( $cgi->param('data_vendor') =~ /^(\w+)$/ ) {
+ $data_vendor = $1;
+ $title = "$data_vendor";
+}
+$cgi->delete('data_vendor');
+
+$title = " for $title" if $title;
+
+my $taxproductnum = $1
+ if ( $cgi->param('taxproductnum') =~ /^(\d+)$/ );
+my $tax_group = $1
+ if ( $cgi->param('tax_group') =~ /^([- \w\(\).\/]+)$/ );
+my $tax_item = $1
+ if ( $cgi->param('tax_item') =~ /^([- \w\(\).\/&%]+)$/ );
+my $tax_provider = $1
+ if ( $cgi->param('tax_provider') =~ /^([ \w]+)$/ );
+my $tax_customer = $1
+ if ( $cgi->param('tax_customer') =~ /^([ \w]+)$/ );
+my $id = $1
+ if ( $cgi->param('id') =~ /^([ \w]+)$/ );
+
+$onclick = $1
+ if ( $cgi->param('onclick') =~ /^(\w+)$/ );
+$cgi->delete('onclick');
+
+my $remove_onclick = <<EOS
+ parent.document.getElementById('$id').value = '';
+ parent.document.getElementById('${id}_description').value = '';
+ parent.$onclick();
+EOS
+ if $id;
+
+my $select_onclick = sub {
+ my $row = shift;
+ my $taxnum = $row->taxproductnum;
+ my $desc = $row->description;
+ "parent.document.getElementById('$id').value = $taxnum;".
+ "parent.document.getElementById('${id}_description').value = '$desc';".
+ "parent.$onclick();";
+}
+ if $id;
+
+my $selected_part_pkg_taxproduct;
+if ($taxproductnum) {
+ $selected_part_pkg_taxproduct =
+ qsearchs('part_pkg_taxproduct', { 'taxproductnum' => $taxproductnum });
+}
+
+my $hashref = {};
+my $extra_sql = '';
+if ( $data_vendor ) {
+ $extra_sql .= ' WHERE data_vendor = '. dbh->quote($data_vendor);
+}
+
+if ($tax_group || $tax_item || $tax_customer || $tax_provider) {
+ my $compare = "LIKE '". ( $tax_group || "%" ). " : ". ( $tax_item || "%" ). " : ".
+ ( $tax_provider || "%" ). " : ". ( $tax_customer || "%" ). "'";
+ $compare = "= '$tax_group:$tax_item:$tax_provider:$tax_customer'"
+ if ($tax_group && $tax_item && $tax_provider && $tax_customer);
+
+ $extra_sql .= ($extra_sql =~ /WHERE/ ? ' AND ' : ' WHERE ').
+ "description $compare";
+
+}
+$cgi->delete('tax_group');
+$cgi->delete('tax_item');
+$cgi->delete('tax_provider');
+$cgi->delete('tax_customer');
+
+
+if ( $tax_group || $tax_item || $tax_provider || $tax_customer ) {
+ push @menubar, 'View all tax products' => $p.'browse/part_pkg_taxproduct.cgi';
+}
+
+$cgi->param('dummy', 1);
+
+#restore this so pagination works
+$cgi->param('data_vendor', $data_vendor) if $data_vendor;
+$cgi->param('tax_group', $tax_group) if $tax_group;
+$cgi->param('tax_item', $tax_item ) if $tax_item;
+$cgi->param('tax_provider', $tax_provider ) if $tax_provider;
+$cgi->param('tax_customer', $tax_customer ) if $tax_customer;
+$cgi->param('onclick', $onclick ) if $onclick;
+
+my $count_query = "SELECT COUNT(*) FROM part_pkg_taxproduct $extra_sql";
+
+my @header = ( 'Data Vendor', 'Group', 'Item', 'Provider', 'Customer' );
+my @links = ( $select_link,
+ $select_link,
+ $select_link,
+ $select_link,
+ $select_link,
+ );
+my @link_onclicks = ( $select_onclick,
+ $select_onclick,
+ $select_onclick,
+ $select_onclick,
+ $select_onclick,
+ );
+my $align = 'lllll';
+
+my @fields = (
+ 'data_vendor',
+ sub { shift->description =~ /^(.*):.*:.*:.*$/; $1;},
+ sub { shift->description =~ /^.*:(.*):.*:.*$/; $1;},
+ sub { shift->description =~ /^.*:.*:(.*):.*$/; $1;},
+ sub { shift->description =~ /^.*:.*:.*:(.*)$/; $1;},
+);
+
+my $html_init = '';
+
+my $select_link = [ 'javascript:void(0);', sub { ''; } ];
+$html_init = '<TABLE><TR><TD><A HREF="javascript:void(0)" '.
+ qq!onClick="$remove_onclick">(remove)</A>&nbsp;!.
+ 'Current tax product: </TD><TD>'.
+ $selected_part_pkg_taxproduct->description.
+ '</TD></TR></TABLE><BR><BR>'
+ if $selected_part_pkg_taxproduct;
+
+my $type = $cgi->param('_type');
+$html_init .= qq(
+ <FORM>
+ <INPUT NAME="_type" TYPE="hidden" VALUE="$type">
+ <INPUT NAME="taxproductnum" TYPE="hidden" VALUE="$taxproductnum">
+ <INPUT NAME="onclick" TYPE="hidden" VALUE="$onclick">
+ <INPUT NAME="id" TYPE="hidden" VALUE="$id">
+ <TABLE>
+ <TR>
+ <TD><SELECT NAME="data_vendor" onChange="this.form.submit()">
+);
+
+my $sql = "SELECT DISTINCT data_vendor FROM part_pkg_taxproduct ORDER BY data_vendor";
+my $dbh = dbh;
+my $sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (['(choose data vendor)'], @{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $data_vendor ? " SELECTED" : "").
+ '">'. $_->[0];
+}
+$html_init .= qq(
+ </SELECT>
+
+<!-- cch specific -->
+ <TD><SELECT NAME="tax_group" onChange="this.form.submit()">
+);
+
+$sql = "SELECT DISTINCT ".
+ qq!substring(description from '#"%#" : % : % : %' for '#'),!.
+ qq!substring(description from '#"%#" : % : % : %' for '#')!.
+ "FROM part_pkg_taxproduct ORDER BY 1";
+
+$sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (['', '(choose group)'], @{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $tax_group ? " SELECTED" : "").
+ '">'. $_->[1];
+}
+
+$html_init .= qq(
+ </SELECT>
+
+ <TD><SELECT NAME="tax_item" onChange="this.form.submit()">
+);
+
+$sql = "SELECT DISTINCT ".
+ qq!substring(description from '% : #"%#" : %: %' for '#'),!.
+ qq!substring(description from '% : #"%#" : %: %' for '#')!.
+ "FROM part_pkg_taxproduct ORDER BY 1";
+
+$sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (@{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $tax_item ? " SELECTED" : "").
+ '">'. ($_->[0] ? $_->[1] : '(choose item)');
+}
+
+$html_init .= qq(
+ </SELECT>
+
+ <TD><SELECT NAME="tax_provider" onChange="this.form.submit()">
+);
+
+$sql = "SELECT DISTINCT ".
+ qq!substring(description from '% : % : #"%#" : %' for '#'),!.
+ qq!substring(description from '% : % : #"%#" : %' for '#')!.
+ "FROM part_pkg_taxproduct ORDER BY 1";
+
+$sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (@{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $tax_provider ? " SELECTED" : "").
+ '">'. ($_->[0] ? $_->[1] : '(choose provider type)');
+}
+
+$html_init .= qq(
+ </SELECT>
+
+ <TD><SELECT NAME="tax_customer" onChange="this.form.submit()">
+);
+
+$sql = "SELECT DISTINCT ".
+ qq!substring(description from '% : % : % : #"%#"' for '#'),!.
+ qq!substring(description from '% : % : % : #"%#"' for '#')!.
+ "FROM part_pkg_taxproduct ORDER BY 1";
+
+$sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (@{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $tax_customer ? " SELECTED" : "").
+ '">'. ($_->[0] ? $_->[1] : '(choose customer type)');
+}
+
+$html_init .= qq(
+ </SELECT>
+
+ </TR>
+ </TABLE>
+ </FORM>
+
+);
+
+</%init>
diff --git a/httemplate/browse/part_referral.html b/httemplate/browse/part_referral.html
new file mode 100755
index 0000000..9cc32c4
--- /dev/null
+++ b/httemplate/browse/part_referral.html
@@ -0,0 +1,181 @@
+<% include("/elements/header.html","Advertising source Listing" ) %>
+
+Where a customer heard about your service. Tracked for informational purposes.
+<BR><BR>
+
+<A HREF="<% $p %>edit/part_referral.html"><I>Add a new advertising source</I></A>
+<BR><BR>
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2 ROWSPAN=2>Advertising source</TH>
+% if ( $show_agentnums ) {
+
+ <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2>Agent</TH>
+% }
+
+ <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=<% scalar(keys %after) %>>Customers and Packages</TH>
+</TR>
+% for my $period ( keys %after ) {
+
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1><% $period %></FONT></TH>
+% }
+
+</TR>
+
+%foreach my $part_referral ( FS::part_referral->all_part_referral(1) ) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% $a = 0;
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% if ( $part_referral->agentnum || $curuser->access_right('Edit global advertising sources') ) {
+% $a++;
+%
+
+ <A HREF="<% $p %>edit/part_referral.html?<% $part_referral->refnum %>">
+% }
+
+ <% $part_referral->refnum %><% $a ? '</A>' : '' %></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% if ( $a ) {
+
+ <A HREF="<% $p %>edit/part_referral.html?<% $part_referral->refnum %>">
+% }
+
+ <% $part_referral->referral %><% $a ? '</A>' : '' %></TD>
+% if ( $show_agentnums ) {
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $part_referral->agentnum ? $part_referral->agent->agent : '(global)' %></TD>
+% }
+% for my $period ( keys %after ) {
+% my @param = ( $part_referral->refnum,
+% $today-$after{$period},
+% $today+$before{$period},
+% );
+% $cust_sth->execute(@param) or die $cust_sth->errstr;
+% my $num_cust = $cust_sth->fetchrow_arrayref->[0];
+% $pkg_sth->execute(@param) or die $pkg_sth->errstr;
+% my $num_pkg = $pkg_sth->fetchrow_arrayref->[0];
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>" ALIGN="right">
+ <TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
+ <TR>
+ <TD ALIGN="right"><B><% $num_cust %></B></TD>
+ <TD ALIGN="left">customers</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><B><% $num_pkg %></B></TD>
+ <TD ALIGN="left">packages</TD>
+ </TR>
+ </TABLE>
+ </TD>
+% }
+
+ </TR>
+% }
+%
+% $cust_statement =~ s/AND refnum = \?//;
+% $cust_sth = dbh->prepare($cust_statement)
+% or die dbh->errstr;
+% $pkg_statement =~ s/AND h_pkg_referral\.refnum = \?//;
+% $pkg_sth = dbh->prepare($pkg_statement)
+% or die dbh->errstr;
+
+ <TR>
+ <TD BGCOLOR="#dddddd" ALIGN="center" COLSPAN=3><B>Total</B></TD>
+% for my $period ( keys %after ) {
+% my @param = ( $today-$after{$period},
+% $today+$before{$period},
+% );
+% $cust_sth->execute( @param ) or die $cust_sth->errstr;
+% my $num_cust = $cust_sth->fetchrow_arrayref->[0];
+% $pkg_sth->execute(@param) or die $pkg_sth->errstr;
+% my $num_pkg = $pkg_sth->fetchrow_arrayref->[0];
+
+ <TD CLASS="inv" BGCOLOR="#dddddd" ALIGN="right">
+ <TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
+ <TR>
+ <TD ALIGN="right"><B><% $num_cust %></B></TD>
+ <TD ALIGN="left">customers</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><B><% $num_pkg %></B></TD>
+ <TD ALIGN="left">packages</TD>
+ </TR>
+ </TABLE>
+ </TD>
+
+% }
+
+ </TR>
+ </TABLE>
+ </BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit advertising sources')
+ || $FS::CurrentUser::CurrentUser->access_right('Edit global advertising sources');
+
+my $today = timelocal(0, 0, 0, (localtime(time))[3..5] );
+
+tie my %after, 'Tie::IxHash',
+ 'Today' => 0,
+ 'Yesterday' => 86400, # 60sec * 60min * 24hrs
+ 'Past week' => 518400, # 60sec * 60min * 24hrs * 6days
+ 'Past 30 days' => 2505600, # 60sec * 60min * 24hrs * 29days
+ 'Past 60 days' => 5097600, # 60sec * 60min * 24hrs * 59days
+ 'Past 90 days' => 7689600, # 60sec * 60min * 24hrs * 89days
+ 'Past 6 months' => 15724800, # 60sec * 60min * 24hrs * 182days
+ 'Past year' => 31486000, # 60sec * 60min * 24hrs * 364days
+ 'Total' => $today,
+;
+my %before = (
+ 'Today' => 86400, # 60sec * 60min * 24hrs
+ 'Yesterday' => 0,
+ 'Past week' => 86400, # 60sec * 60min * 24hrs
+ 'Past 30 days' => 86400, # 60sec * 60min * 24hrs
+ 'Past 60 days' => 86400, # 60sec * 60min * 24hrs
+ 'Past 90 days' => 86400, # 60sec * 60min * 24hrs
+ 'Past 6 months' => 86400, # 60sec * 60min * 24hrs
+ 'Past year' => 86400, # 60sec * 60min * 24hrs
+ 'Total' => 86400, # 60sec * 60min * 24hrs
+);
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $show_agentnums = ( scalar($curuser->agentnums) > 1 );
+
+my $cust_statement = "SELECT COUNT(*) FROM h_cust_main
+ WHERE history_action = 'insert'
+ AND refnum = ?
+ AND history_date >= ?
+ AND history_date < ?
+ AND ". $curuser->agentnums_sql;
+my $cust_sth = dbh->prepare($cust_statement)
+ or die dbh->errstr;
+
+my $pkg_statement = "SELECT COUNT(*) FROM h_pkg_referral
+ LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE history_action = 'insert'
+ AND h_pkg_referral.refnum = ?
+ AND history_date >= ?
+ AND history_date < ?
+ AND ". $curuser->agentnums_sql;
+my $pkg_sth = dbh->prepare($pkg_statement)
+ or die dbh->errstr;
+
+</%init>
diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi
new file mode 100755
index 0000000..f1b2836
--- /dev/null
+++ b/httemplate/browse/part_svc.cgi
@@ -0,0 +1,215 @@
+<% include('/elements/header.html', 'Service Definition Listing') %>
+
+<SCRIPT>
+function part_export_areyousure(href) {
+ if (confirm("Are you sure you want to delete this export?") == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
+ Service definitions are the templates for items you offer to your customers.<BR><BR>
+
+<FORM METHOD="POST" ACTION="<% $p %>edit/part_svc.cgi">
+<A HREF="<% $p %>edit/part_svc.cgi"><I>Add a new service definition</I></A>
+% if ( @part_svc ) {
+&nbsp;or&nbsp;<SELECT NAME="clone"><OPTION></OPTION>
+% foreach my $part_svc ( @part_svc ) {
+
+ <OPTION VALUE="<% $part_svc->svcpart %>"><% $part_svc->svc %></OPTION>
+% }
+
+</SELECT><INPUT TYPE="submit" VALUE="Clone existing service">
+% }
+
+</FORM><BR>
+
+<% $total %> service definitions
+<% $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled services</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled services</a> )'; }
+%>
+% $cgi->param('showdisabled', ( 1 ^ $cgi->param('showdisabled') ) );
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+ <TR>
+
+ <TH CLASS="grid" BGCOLOR="#cccccc"><A HREF="<% do { $cgi->param('orderby', 'svcpart'); $cgi->self_url } %>">#</A></TH>
+
+% if ( $cgi->param('showdisabled') ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc">Status</TH>
+% }
+
+ <TH CLASS="grid" BGCOLOR="#cccccc"><A HREF="<% do { $cgi->param('orderby', 'svc'); $cgi->self_url; } %>">Service</A></TH>
+
+ <TH CLASS="grid" BGCOLOR="#cccccc">Table</TH>
+
+ <TH CLASS="grid" BGCOLOR="#cccccc"><A HREF="<% do { $cgi->param('orderby', 'active'); $cgi->self_url; } %>"><FONT SIZE=-1>Customer<BR>Services</FONT></A></TH>
+
+ <TH CLASS="grid" BGCOLOR="#cccccc">Export</TH>
+
+ <TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>
+
+ <TH COLSPAN=2 CLASS="grid" BGCOLOR="#cccccc">Modifier</TH>
+
+ </TR>
+
+% foreach my $part_svc ( @part_svc ) {
+% my $svcdb = $part_svc->svcdb;
+% my $svc_x = "FS::$svcdb"->new( { svcpart => $part_svc->svcpart } );
+% my @dfields = $svc_x->fields;
+% push @dfields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge
+% my @fields =
+% grep { $svc_x->pvf($_)
+% or $_ ne 'svcnum' && $part_svc->part_svc_column($_)->columnflag }
+% @dfields ;
+% my $rowspan = scalar(@fields) || 1;
+% my $url = "${p}edit/part_svc.cgi?". $part_svc->svcpart;
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+
+ <TR>
+
+ <TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<% $url %>"><% $part_svc->svcpart %></A>
+ </TD>
+
+% if ( $cgi->param('showdisabled') ) {
+ <TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $part_svc->disabled
+ ? '<FONT COLOR="#FF0000"><B>Disabled</B></FONT>'
+ : '<FONT COLOR="#00CC00"><B>Enabled</B></FONT>'
+ %>
+ </TD>
+% }
+
+ <TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>"><A HREF="<% $url %>">
+ <% $part_svc->svc %></A></TD>
+
+ <TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $svcdb %></TD>
+
+ <TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <FONT COLOR="#00CC00"><B><% $num_active_cust_svc{$part_svc->svcpart} %></B></FONT>&nbsp;<% $num_active_cust_svc{$part_svc->svcpart} ? svc_url( 'ahref' => 1, 'm' => $m, 'action' => 'search', 'part_svc' => $part_svc, 'query' => "svcpart=". $part_svc->svcpart ) : '<A NAME="zero">' %>active</A>
+
+% if ( $num_active_cust_svc{$part_svc->svcpart} ) {
+ <BR><FONT SIZE="-1">[ <A HREF="<%$p%>edit/bulk-cust_svc.html?svcpart=<% $part_svc->svcpart %>">change</A> ]</FONT>
+% }
+
+ </TD>
+
+ <TD ROWSPAN=<% $rowspan %> CLASS="inv" BGCOLOR="<% $bgcolor %>">
+ <TABLE CLASS="inv">
+%
+%# my @part_export =
+%map { qsearchs('part_export', { exportnum => $_->exportnum } ) } qsearch('export_svc', { svcpart => $part_svc->svcpart } ) ;
+% foreach my $part_export (
+% map { qsearchs('part_export', { exportnum => $_->exportnum } ) }
+% qsearch('export_svc', { svcpart => $part_svc->svcpart } )
+% ) {
+%
+
+ <TR>
+ <TD><A HREF="<% $p %>edit/part_export.cgi?<% $part_export->exportnum %>"><% $part_export->exportnum %>:&nbsp;<% $part_export->exporttype %>&nbsp;to&nbsp;<% $part_export->machine %></A></TD>
+ </TR>
+% }
+
+ </TABLE>
+ </TD>
+
+% unless ( @fields ) {
+% for ( 1..3 ) {
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"</TD>
+% }
+% }
+%
+% my($n1)='';
+% foreach my $field ( @fields ) {
+% my $formatter =
+% FS::part_svc->svc_table_fields($svcdb)->{$field}->{format}
+% || sub { shift };
+% my $flag = $part_svc->part_svc_column($field)->columnflag;
+%
+
+ <% $n1 %>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $field %></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $flag{$flag} %></TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% my $value = &$formatter($part_svc->part_svc_column($field)->columnvalue);
+% if ( $flag =~ /^[MA]$/ ) {
+% $inventory_class{$value}
+% ||= qsearchs('inventory_class', { 'classnum' => $value } );
+%
+
+ <% $inventory_class{$value}
+ ? $inventory_class{$value}->classname
+ : "WARNING: inventory_class.classnum $value not found" %>
+% } else {
+
+ <% $value %>
+% }
+
+ </TD>
+% $n1="</TR><TR>";
+% }
+%
+
+ </TR>
+% }
+
+</TABLE>
+</BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+#code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm
+my %flag = (
+ '' => '',
+ 'D' => 'Default',
+ 'F' => 'Fixed (unchangeable)',
+ 'S' => 'Selectable choice',
+ #'M' => 'Manual selection from inventory',
+ 'M' => 'Manual selected from inventory',
+ #'A' => 'Automatically fill in from inventory',
+ 'A' => 'Automatically filled in from inventory',
+ 'X' => 'Excluded',
+);
+
+my %search;
+if ( $cgi->param('showdisabled') ) {
+ %search = ();
+} else {
+ %search = ( 'disabled' => '' );
+}
+
+my @part_svc =
+ sort { $a->getfield('svcpart') <=> $b->getfield('svcpart') }
+ qsearch('part_svc', \%search );
+my $total = scalar(@part_svc);
+
+my %num_active_cust_svc = map { $_->svcpart => $_->num_cust_svc } @part_svc;
+
+if ( $cgi->param('orderby') eq 'active' ) {
+ @part_svc = sort { $num_active_cust_svc{$b->svcpart} <=>
+ $num_active_cust_svc{$a->svcpart} } @part_svc;
+} elsif ( $cgi->param('orderby') eq 'svc' ) {
+ @part_svc = sort { lc($a->svc) cmp lc($b->svc) } @part_svc;
+}
+
+my %inventory_class = ();
+
+</%init>
diff --git a/httemplate/browse/part_virtual_field.cgi b/httemplate/browse/part_virtual_field.cgi
new file mode 100644
index 0000000..b184400
--- /dev/null
+++ b/httemplate/browse/part_virtual_field.cgi
@@ -0,0 +1,42 @@
+<% include('/elements/header.html', 'Virtual field definitions') %>
+
+<% include('/elements/error.html') %>
+
+<A HREF="<%$p2%>edit/part_virtual_field.cgi"><I>Add a new field</I></A><BR><BR>
+% foreach $dbtable (sort { $a cmp $b } keys (%pvfs)) {
+
+<H3><%$dbtable%></H3>
+
+<%table()%>
+<TH><TD>Field name</TD><TD>Description</TD></TH>
+% foreach my $pvf (sort {$a->name cmp $b->name} @{ $pvfs{$dbtable} }) {
+
+ <TR>
+ <TD></TD>
+ <TD>
+ <A HREF="<%$p2%>edit/part_virtual_field.cgi?<%$pvf->vfieldpart%>">
+ <%$pvf->name%></A></TD>
+ <TD><%$pvf->label%></TD>
+ </TR>
+% }
+
+</TABLE>
+% }
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my %pvfs;
+my $block;
+my $p2 = popurl(2);
+my $dbtable;
+
+foreach (qsearch('part_virtual_field', {})) {
+ push @{ $pvfs{$_->dbtable} }, $_;
+}
+
+</%init>
diff --git a/httemplate/browse/payment_gateway.html b/httemplate/browse/payment_gateway.html
new file mode 100644
index 0000000..848c58a
--- /dev/null
+++ b/httemplate/browse/payment_gateway.html
@@ -0,0 +1,94 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Payment gateways',
+ 'menubar' => [ 'Agents' => $p.'browse/agent.cgi', ],
+ 'html_init' => $html_init,
+ 'name' => 'payment gateways',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 1,
+ 'query' => { 'table' => 'payment_gateway',
+ 'hashref' => {},
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Gateway',
+ 'Username',
+ 'Password',
+ 'Action',
+ 'Options',
+ ],
+ 'fields' => [ 'gatewaynum',
+ $gateway_sub,
+ 'gateway_username',
+ sub { ' - '; },
+ 'gateway_action',
+ $options_sub,
+ ],
+ )
+%>
+
+</TABLE>
+
+<% include('/elements/footer.html') %>
+<%once>
+
+my $html_init = qq!
+ <A HREF="${p}edit/payment_gateway.html"><I>Add a new payment gateway</I></A>
+ <BR><BR>
+
+ <SCRIPT>
+ function areyousure(href) {
+ if (confirm("Are you sure you want to disable this payment gateway?") == true)
+ window.location.href = href;
+ }
+ </SCRIPT>
+
+!;
+
+my $gateway_sub = sub {
+ my($payment_gateway) = @_;
+
+ my $gatewaynum = $payment_gateway->gatewaynum;
+
+ my $html = $payment_gateway->gateway_module. ' '. qq!
+ <FONT SIZE="-1">
+ <A HREF="${p}edit/payment_gateway.html?$gatewaynum">(edit)</A>
+ !;
+
+ unless ( $payment_gateway->disabled ) {
+ $html .= qq!
+ <A HREF="javascript:areyousure('${p}misc/disable-payment_gateway.cgi?$gatewaynum')">(disable)</A>
+ !;
+ }
+
+ $html .= '</FONT>';
+
+ $html;
+
+};
+
+my $options_sub = sub {
+ my($payment_gateway) = @_;
+
+ #should return a structure instead of this manual formatting...
+
+ my $html = '<TABLE CELLSPACING=0 CELLPADDING=0>';
+
+ my %options = $payment_gateway->options;
+ foreach my $option ( keys %options ) {
+ $html .= '<TR><TH>'. $option. ':</TH>'.
+ '<TD>'. $options{$option}. '</TD></TR>';
+ }
+ $html .= '</TABLE>';
+
+ $html;
+};
+
+my $count_query = 'SELECT COUNT(*) FROM payment_gateway';
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/browse/pkg_category.html b/httemplate/browse/pkg_category.html
new file mode 100644
index 0000000..20bf1a8
--- /dev/null
+++ b/httemplate/browse/pkg_category.html
@@ -0,0 +1,33 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Package categories',
+ 'html_init' => $html_init,
+ 'name' => 'package categories',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 2,
+ 'query' => { 'table' => 'pkg_category',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY categorynum',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#', 'Category' ],
+ 'fields' => [ 'categorynum', 'categoryname' ],
+ 'links' => [ $link, $link ],
+ )
+%>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init =
+ qq!<A HREF="${p}browse/pkg_class.html">Package classes</A><BR><BR>!.
+ 'Package categories define groups of package classes, for reporting and '.
+ 'convenience purposes.<BR><BR>'.
+ qq!<A HREF="${p}edit/pkg_category.html"><I>Add a package category</I></A><BR><BR>!;
+
+my $count_query = 'SELECT COUNT(*) FROM pkg_category';
+
+my $link = [ $p.'edit/pkg_category.html?', 'categorynum' ];
+
+</%init>
diff --git a/httemplate/browse/pkg_class.html b/httemplate/browse/pkg_class.html
new file mode 100644
index 0000000..75969db
--- /dev/null
+++ b/httemplate/browse/pkg_class.html
@@ -0,0 +1,46 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Package classes',
+ 'html_init' => $html_init,
+ 'name' => 'package classes',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 2,
+ 'query' => { 'table' => 'pkg_class',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY classnum',
+ },
+ 'count_query' => $count_query,
+ 'header' => $header,
+ 'fields' => $fields,
+ 'links' => $links,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init =
+ 'Package classes define groups of packages, for reporting and '.
+ 'convenience purposes.<BR><BR>'.
+ qq!<A HREF="${p}edit/pkg_class.html"><I>Add a package class</I></A><BR><BR>!;
+
+my $count_query = 'SELECT COUNT(*) FROM pkg_class';
+
+my $link = [ $p.'edit/pkg_class.html?', 'classnum' ];
+
+my $header = [ '#', 'Class' ];
+my $fields = [ 'classnum', 'classname' ];
+my $links = [ $link, $link ];
+
+my $cat_query = 'SELECT COUNT(*) FROM pkg_class where categorynum IS NOT NULL';
+my $sth = dbh->prepare($cat_query)
+ or die "Error preparing $cat_query: ". dbh->errstr;
+$sth->execute
+ or die "Error executing $cat_query: ". $sth->errstr;
+if ($sth->fetchrow_arrayref->[0]) {
+ push @$header, 'Category';
+ push @$fields, 'categoryname';
+ push @$links, $link;
+}
+
+</%init>
diff --git a/httemplate/browse/rate.cgi b/httemplate/browse/rate.cgi
new file mode 100644
index 0000000..02d670f
--- /dev/null
+++ b/httemplate/browse/rate.cgi
@@ -0,0 +1,64 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Rate plans',
+ 'menubar' => [ 'Regions and Prefixes' =>
+ $p.'browse/rate_region.html',
+ ],
+ 'html_init' => $html_init,
+ 'name' => 'rate plans',
+ 'query' => { 'table' => 'rate',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY ratenum',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#', 'Rate plan', 'Rates' ],
+ 'fields' => [ 'ratenum', 'ratename', $rates_sub ],
+ 'links' => [ $link, $link, '' ],
+ )
+%>
+<%once>
+
+my $all_countrycodes = join("\n", map qq(<OPTION VALUE="$_">$_),
+ FS::rate_prefix->all_countrycodes
+ );
+
+my $rates_sub = sub {
+ my $rate = shift;
+ my $ratenum = $rate->ratenum;
+
+ qq( <FORM METHOD="GET" ACTION="${p}browse/rate_detail.html">
+ <INPUT TYPE="hidden" NAME="ratenum" VALUE="$ratenum">
+ <SELECT NAME="countrycode" onChange="this.form.submit();">
+ <OPTION SELECTED>Select Country Code
+ <OPTION VALUE="">(all)
+ $all_countrycodes
+ </SELECT>
+ </FORM>
+ );
+
+
+};
+
+my $html_init =
+ 'Rate plans for VoIP and call billing.<BR><BR>'.
+ qq!<A HREF="${p}edit/rate.cgi"><I>Add a rate plan</I></A>!.
+ qq! | <A HREF="${p}misc/copy-rate_detail.html"><I>Copy rates between plans</I></A>!.
+ '<BR><BR>
+ <SCRIPT>
+ function rate_areyousure(href) {
+ if (confirm("Are you sure you want to delete this rate plan?") == true)
+ window.location.href = href;
+ }
+ </SCRIPT>
+ ';
+
+my $count_query = 'SELECT COUNT(*) FROM rate';
+
+my $link = [ $p.'edit/rate.cgi?', 'ratenum' ];
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/browse/rate_detail.html b/httemplate/browse/rate_detail.html
new file mode 100644
index 0000000..23bc23f
--- /dev/null
+++ b/httemplate/browse/rate_detail.html
@@ -0,0 +1,92 @@
+<% include( 'elements/browse.html',
+ 'title' => $title,
+ 'name_singular' => 'rate',
+ 'html_init' => $html_init,
+ 'menubar' => [ 'Rate plans' => $p.'browse/rate.cgi' ],
+ 'query' => {
+ 'table' => 'rate_detail',
+ 'addl_from' => $join,
+ 'hashref' => { 'ratenum' => $ratenum },
+ 'extra_sql' => $where,
+ },
+ 'count_query' => "SELECT COUNT(*) FROM rate_detail $join".
+ " WHERE ratenum = $ratenum $where",
+ 'header' => [
+ 'Region',
+ 'Prefix(es)',
+ 'Included<BR>minutes',
+ 'Charge per<BR>minute',
+ 'Granularity',
+ 'Usage class',
+ ],
+ 'fields' => [
+ 'regionname',
+ sub { shift->dest_region->prefixes_short },
+ sub { shift->min_included.
+ '&nbsp;<FONT SIZE="-1">(edit)</FONT>';
+ },
+ sub { $money_char. shift->min_charge.
+ '&nbsp;<FONT SIZE="-1">(edit)</FONT>';
+ },
+ sub { $granularity{ shift->sec_granularity } },
+ 'classname',
+ ],
+ 'links' => [ '', '', $edit_link, $edit_link, '', '' ],
+ 'link_onclicks' => [ '', '', $edit_onclick, $edit_onclick, '', '' ],
+ 'align' => 'llrrcc',
+ )
+%>
+<%once>
+
+tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $join =
+ ' JOIN rate_region ON ( rate_detail.dest_regionnum = rate_region.regionnum )';
+
+my $edit_link = [ 'javascript:void(0);', sub { ''; } ];
+
+my $edit_onclick = sub {
+ my $rate_detail = shift;
+ my $ratedetailnum = $rate_detail->ratedetailnum;
+ include( '/elements/popup_link_onclick.html',
+ 'action' => "${p}edit/rate_detail.html?$ratedetailnum",
+ 'actionlabel' => 'Edit rate',
+ 'height' => 420,
+ #default# 'width' => 540,
+ #default# 'color' => '#333399',
+ );
+};
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init = include('/elements/init_overlib.html');
+
+$cgi->param('ratenum') =~ /^(\d+)$/ or die "unparsable ratenum";
+my $ratenum = $1;
+my $rate = qsearchs('rate', { 'ratenum' => $ratenum } )
+ or die "unknown ratenum $ratenum";
+my $ratename = $rate->ratename;
+my $title = "$ratename rates";
+
+my @where = ();
+
+if ( $cgi->param('countrycode') =~ /^(\d+)$/ ) {
+ my $countrycode = $1;
+ push @where, "0 < ( SELECT COUNT(*) FROM rate_prefix
+ WHERE rate_prefix.regionnum = rate_region.regionnum
+ AND countrycode = '$countrycode'
+ )
+ ";
+ $title .= " for +$countrycode";
+}
+
+my $where = scalar(@where) ? ' AND '.join(' AND ', @where ) : '';
+
+</%init>
diff --git a/httemplate/browse/rate_region.html b/httemplate/browse/rate_region.html
new file mode 100644
index 0000000..b454a9e
--- /dev/null
+++ b/httemplate/browse/rate_region.html
@@ -0,0 +1,91 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Rating Regions and Prefixes',
+ 'name_singular' => 'region', #'rate region',
+ 'menubar' => [ 'Rate plans' => $p.'browse/rate.cgi' ],
+ 'html_init' => $html_init,
+ 'html_posttotal' => $html_posttotal,
+ 'query' => {
+ 'select' => $select,
+ 'table' => 'rate_region',
+ 'addl_from' => $join,
+ 'extra_sql' => $extra_sql,
+ 'order_by' => 'ORDER BY LOWER(regionname)',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#', 'Region', 'Country code', 'Prefixes' ],
+ 'fields' => [ 'regionnum', 'regionname', 'ccode', 'prefixes' ],
+ 'links' => [ $link, $link, $link, $link ],
+ )
+%>
+<%once>
+
+my $edit_url = $p.'edit/rate_region.cgi';
+
+my $link = [ "$edit_url?", 'regionnum' ];
+
+my $html_init =
+ 'Regions and prefixes for VoIP and call billing.<BR><BR>'.
+ qq(<A HREF="$edit_url"><I>Add a new region</I></A><BR><BR>);
+
+#not quite right for the shouldn't-happen multiple countrycode per region case
+my $select = 'rate_region.*, ';
+my $join = '';
+my $group_sql = '';
+if ( driver_name =~ /^Pg/ ) {
+ my $fromwhere = 'FROM rate_prefix'.
+ ' WHERE rate_prefix.regionnum = rate_region.regionnum';
+ my $prefix_sql = " CASE WHEN nxx IS NULL OR nxx = '' ".
+ " THEN npa ".
+ " ELSE npa || '-' || nxx ".
+ " END";
+ my $prefixes_sql = "SELECT $prefix_sql $fromwhere AND npa IS NOT NULL";
+ $select .= "( SELECT countrycode $fromwhere LIMIT 1 ) AS ccode,
+ ARRAY_TO_STRING( ARRAY($prefixes_sql), ',' ) AS prefixes";
+} elsif ( driver_name =~ /^mysql/i ) {
+ $join = 'LEFT JOIN rate_prefix USING ( regionnum )';
+ $select .= "GROUP_CONCAT( DISTINCT countrycode ) AS ccode,
+ GROUP_CONCAT( npa ORDER BY npa ) AS prefixes ";
+ $group_sql = 'GROUP BY regionnum, regionname';
+} else {
+ die 'unknown database '. driver_name;
+}
+
+my $base_count_sql = 'SELECT COUNT(*) FROM rate_region';
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('dummy', 1);
+my $countrycode_filter_change =
+ "window.location = '".
+ $cgi->self_url. ";countrycode=' + this.options[this.selectedIndex].value;";
+
+my $countrycode = '';
+my $extra_sql = $group_sql;
+my $count_query = $base_count_sql;
+if ( $cgi->param('countrycode') =~ /^(\d+)$/ ) {
+ $countrycode = $1;
+ my $ccode_sql = '( SELECT countrycode FROM rate_prefix
+ WHERE rate_prefix.regionnum = rate_region.regionnum
+ LIMIT 1
+ )';
+ $extra_sql = " WHERE $ccode_sql = '$1' $extra_sql";
+ $count_query .= " WHERE $ccode_sql = '$1'";
+}
+
+my $html_posttotal =
+ '(show country code: '.
+ qq(<SELECT NAME="countrycode" onChange="$countrycode_filter_change">).
+ qq(<OPTION VALUE="">(all)).
+ join("\n", map { qq(<OPTION VALUE="$_").
+ ($_ eq $countrycode ? ' SELECTED' : '' ).
+ ">$_",
+ }
+ FS::rate_prefix->all_countrycodes
+ ).
+ '</SELECT>)';
+
+</%init>
diff --git a/httemplate/browse/reason.html b/httemplate/browse/reason.html
new file mode 100644
index 0000000..fe285be
--- /dev/null
+++ b/httemplate/browse/reason.html
@@ -0,0 +1,53 @@
+<% include( 'elements/browse.html',
+ 'title' => ucfirst($classname) . ' Reasons',
+ 'menubar' => [ ucfirst($classname).' Reason Types' =>
+ $p."browse/reason_type.html?class=$class"
+ ],
+ 'html_init' => $html_init,
+ 'name' => $classname . ' reasons',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 3,
+ 'query' => { 'table' => 'reason',
+ 'hashref' => {},
+ 'extra_sql' => $where_clause.
+ ' ORDER BY reason_type',
+ 'addl_from' => 'LEFT JOIN reason_type ON reason_type.typenum = reason.reason_type',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ ucfirst($classname) . ' Reason Type',
+ ucfirst($classname) . ' Reason',
+ ],
+ 'fields' => [ 'reasonnum',
+ sub { shift->reasontype->type },
+ 'reason',
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('class') =~ /^(\w)$/ or die "illegal class";
+my $class = $1;
+
+my $classname = $FS::reason_type::class_name{$class};
+my $classpurpose = $FS::reason_type::class_purpose{$class};
+
+my $html_init = ucfirst($classname). " reasons $classpurpose.<BR><BR>".
+qq!<A HREF="${p}edit/reason.html?class=$class">!.
+"<I>Add a $classname reason</I></A><BR><BR>";
+
+my $where_clause = " WHERE class='$class' ";
+
+my $count_query = 'SELECT COUNT(*) FROM reason LEFT JOIN reason_type on ' .
+ 'reason_type.typenum = reason.reason_type ' . $where_clause;
+
+my $link = [ $p."edit/reason.html?class=$class&reasonnum=", 'reasonnum' ];
+
+</%init>
diff --git a/httemplate/browse/reason_type.html b/httemplate/browse/reason_type.html
new file mode 100644
index 0000000..6b444ba
--- /dev/null
+++ b/httemplate/browse/reason_type.html
@@ -0,0 +1,68 @@
+<% include( 'elements/browse.html',
+ 'title' => ucfirst($classname) . " Reason Types",
+ 'menubar' => [ ucfirst($classname) . " reasons" =>
+ $p.'browse/reason.html?class=' . $class,
+ ],
+ 'html_init' => $html_init,
+ 'name' => $classname . " reason types",
+ 'query' => { 'table' => 'reason_type',
+ 'hashref' => {},
+ 'extra_sql' => $where_clause .
+ 'ORDER BY typenum',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ ucfirst($classname) . ' Reason Type',
+ ucfirst($classname) . ' Reasons',
+ ],
+ 'fields' => [ 'typenum',
+ 'type',
+ $reasons_sub,
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('class') =~ /^(\w)$/ or die "illegal class";
+my $class=$1;
+
+my $classname = $FS::reason_type::class_name{$class};
+
+my $html_init = ucfirst($classname) .
+ " reason types allow groups of $classname reasons for reporting purposes." .
+ qq!<BR><BR><A HREF="${p}edit/reason_type.html?class=$class"><I>Add a ! .
+ $classname . " reason type</I></A><BR><BR>";
+
+my $reasons_sub = sub {
+ my $reason_type = shift;
+
+ [ map {
+ [
+ {
+ 'data' => $_->reason,
+ 'align' => 'left',
+ 'link' => $p. "edit/reason.html?class=$class&reasonnum=".
+ $_->reasonnum,
+ },
+ ];
+ }
+ $reason_type->enabled_reasons,
+
+ ];
+
+};
+
+my $where_clause = "WHERE class='$class'";
+my $count_query = 'SELECT COUNT(*) FROM reason_type ';
+$count_query .= $where_clause;
+
+my $link = [ $p.'edit/reason_type.html?class='.$class.'&typenum=', 'typenum' ];
+
+</%init>
diff --git a/httemplate/browse/router.cgi b/httemplate/browse/router.cgi
new file mode 100644
index 0000000..541e967
--- /dev/null
+++ b/httemplate/browse/router.cgi
@@ -0,0 +1,52 @@
+<% include('elements/browse.html',
+ 'title' => 'Routers',
+ 'menubar' => [ @menubar ],
+ 'name_singular' => 'router',
+ 'query' => { 'table' => 'router',
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ },
+ 'count_query' => "SELECT count(*) from router $count_sql",
+ 'header' => [ 'Router name',
+ 'Address block(s)',
+ ],
+ 'fields' => [ 'routername',
+ sub { join( '<BR>', map { $_->NetAddr }
+ shift->addr_block
+ );
+ },
+ ],
+ 'links' => [ [ "${p2}edit/router.cgi?", 'routernum' ],
+ '',
+ ],
+ 'agent_virt' => 1,
+ 'agent_null_right'=> "Broadband global configuration",
+ 'agent_pos' => 1,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Broadband configuration')
+ || $FS::CurrentUser::CurrentUser->access_right('Broadband global configuration');
+
+my $p2 = popurl(2);
+my $extra_sql = '';
+
+my @menubar = ( 'Add a new router', "${p2}edit/router.cgi" );
+
+if ($cgi->param('hidecustomerrouters') eq '1') {
+ $extra_sql = 'WHERE svcnum > 0';
+ $cgi->param('hidecustomerrouters', 0);
+ push @menubar, 'Show customer routers', $cgi->self_url();
+} else {
+ $cgi->param('hidecustomerrouters', 1);
+ push @menubar, 'Hide customer routers', $cgi->self_url();
+}
+
+my $count_sql = $extra_sql. ( $extra_sql =~ /WHERE/ ? ' AND' : 'WHERE' ).
+ $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'Broadband global configuration',
+ );
+
+</%init>
diff --git a/httemplate/browse/svc_acct_pop.cgi b/httemplate/browse/svc_acct_pop.cgi
new file mode 100755
index 0000000..c6e615d
--- /dev/null
+++ b/httemplate/browse/svc_acct_pop.cgi
@@ -0,0 +1,77 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Access Numbers',
+ 'html_init' => $html_init,
+ 'name_singular' => 'access number',
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'header' => [
+ '#',
+ 'City',
+ 'State',
+ 'Area code',
+ 'Exchange',
+ 'Local',
+ 'Accounts',
+ ],
+ 'fields' => [
+ 'popnum',
+ 'city',
+ 'state',
+ 'ac',
+ 'exch',
+ 'loc',
+ $num_accounts_sub,
+ ],
+ 'align' => 'rllrrrr',
+ )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Dialup configuration')
+ || $curuser->access_right('Dialup global configuration');
+
+my $html_init = qq!
+ <A HREF="${p}edit/svc_acct_pop.cgi"><I>Add new Access Number</I></A>
+ <BR><BR>
+!;
+
+my $query = { 'select' => '*,
+ ( SELECT COUNT(*) FROM svc_acct
+ WHERE svc_acct.popnum = svc_acct_pop.popnum
+ ) AS num_accounts
+ ',
+ 'table' => 'svc_acct_pop',
+ #'hashref' => { 'disabled' => '' },
+ 'extra_sql' => 'ORDER BY state, city, ac, exch, loc',
+ };
+
+my $count_query = "SELECT COUNT(*) FROM svc_acct_pop"; # WHERE DISABLED IS NULL OR DISABLED = ''";
+
+my $svc_acct_pop_link = [ $p.'edit/svc_acct_pop.cgi?', 'popnum' ];
+
+my $svc_acct_link = $p. 'search/svc_acct.cgi?popnum=';
+
+my $num_accounts_sub = sub {
+ my $svc_acct_pop = shift;
+ [
+ [
+ { 'data' => '<B><FONT COLOR="#00CC00">'.
+ $svc_acct_pop->get('num_accounts').
+ '</FONT></B>',
+ 'align' => 'right',
+ },
+ { 'data' => 'active',
+ 'align' => 'left',
+ 'link' => ( $svc_acct_pop->get('num_accounts')
+ ? $svc_acct_link. $svc_acct_pop->popnum
+ : ''
+ ),
+ },
+ ],
+ ];
+};
+
+</%init>
diff --git a/httemplate/browse/tax_class.html b/httemplate/browse/tax_class.html
new file mode 100755
index 0000000..76d266b
--- /dev/null
+++ b/httemplate/browse/tax_class.html
@@ -0,0 +1,92 @@
+<% include( 'elements/browse.html',
+ 'title' => "Tax classes $title",
+ 'name_singular' => 'tax class',
+ 'menubar' => \@menubar,
+ 'html_init' => $html_init,
+ 'query' => {
+ 'table' => 'tax_class',
+ 'hashref' => $hashref,
+ 'extra_sql' => $where,
+ 'order_by' => 'ORDER BY taxclass',
+ },
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'align' => $align,
+ 'links' => \@links,
+ 'link_onclicks' => \@link_onclicks,
+ 'disable_maxselect' => 1,
+ 'disable_total' => 1,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $title = '';
+my @menubar = ();
+my $html_init = '';
+my $hashref = {};
+my @where = ();
+my $onclick = 'return true;';
+
+my $omit = '';
+if ( $cgi->param('magic') eq 'omit' ) {
+ $cgi->param('omit') =~ /^([,\d]+)$/;
+ $omit = $1;
+ $title .= " unselected";
+ push @where, map { "taxclassnum != $_" } grep {$_} split( /,/, $omit );
+ $onclick = sub{ 'parent.doSelect('. shift->taxclassnum. '); return false;' }
+}
+$cgi->delete('omit');
+
+my $data_vendor = '';
+if ( $cgi->param('datavendor') =~ /^([\w]+)$/ ) {
+ $data_vendor = $1;
+ $title .= " for data vendor $1";
+ push @where, 'data_vendor = '. dbh->quote($data_vendor);
+}
+$cgi->delete('data_vendor');
+
+my $selected = '';
+if ( $cgi->param('magic') eq 'select')
+{
+ $cgi->param('selected') =~ /^([,\d]*)$/;
+ $selected = $1;
+ $title = " selected";
+ my @clauses = map { "taxclassnum = $_" } grep {$_} split( /,/, $selected );
+ @where = scalar(@clauses) ? '( '. join(' OR ', @clauses) .')' : '1=0';
+ $onclick = sub{ 'parent.doUnselect('. shift->taxclassnum. '); return false;' } ;
+}
+$cgi->delete('selected');
+
+
+if ( $data_vendor ) {
+ push @menubar, 'View all tax classes' => $p.'browse/tax_class.html';
+}
+
+$cgi->param('dummy', 1);
+
+#restore this so pagination works
+$cgi->param('omit', $omit ) if $omit;
+$cgi->param('selected', $selected ) if $selected;
+$cgi->param('data_vendor', $data_vendor ) if $data_vendor;
+
+my $where = scalar(@where) ? 'WHERE '. join( ' AND ', @where ) : '';
+my $count_query = 'SELECT COUNT(*) FROM tax_class '. $where;
+
+my $link = [ 'javascript:void(0);', sub{ ''; } ];
+
+my @header = ( '', '', '' );
+my @links = ( $link, $link, $link );
+my @link_onclicks = ( $onclick, $onclick, $onclick );
+my $align = 'lll';
+my @fields = ( 'data_vendor', 'taxclass', 'description' );
+
+</%init>
diff --git a/httemplate/browse/tax_rate.cgi b/httemplate/browse/tax_rate.cgi
new file mode 100755
index 0000000..cb997fa
--- /dev/null
+++ b/httemplate/browse/tax_rate.cgi
@@ -0,0 +1,348 @@
+<% include( 'elements/browse.html',
+ 'title' => "Tax Rates $title",
+ 'name_singular' => 'tax rate',
+ 'menubar' => \@menubar,
+ 'html_init' => $html_init,
+ 'html_form' => $html_form,
+ 'disableable' => 1,
+ 'disabled_statuspos' => 5,
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'header2' => \@header2,
+ 'fields' => \@fields,
+ 'align' => $align,
+ 'color' => \@color,
+ 'cell_style' => \@cell_style,
+ 'links' => \@links,
+ 'link_onclicks' => \@link_onclicks,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $rate_sub = sub {
+ my $tax_rate = shift;
+
+ my $units = $tax_rate->unittype_name;
+ $units =~ s/ /&nbsp;/g;
+
+ my @rate = ();
+ push @rate,
+ ($tax_rate->tax * 100). '%&nbsp;<FONT SIZE="-1">(edit)</FONT>'
+ if $tax_rate->tax > 0 || $tax_rate->taxbase > 0;
+ push @rate,
+ ($tax_rate->excessrate * 100). '%&nbsp;<FONT SIZE="-1">(edit)</FONT>'
+ if $tax_rate->excessrate > 0;
+ push @rate,
+ $money_char. $tax_rate->fee.
+ qq!&nbsp;per&nbsp;$units<FONT SIZE="-1">(edit)</FONT>!
+ if $tax_rate->fee > 0 || $tax_rate->feebase > 0;
+ push @rate,
+ $money_char. $tax_rate->excessfee.
+ qq!&nbsp;per&nbsp;$units<FONT SIZE="-1">(edit)</FONT>!
+ if $tax_rate->excessfee > 0;
+
+
+ [ map [ {'data'=>$_} ], @rate ];
+};
+
+my $limit_sub = sub {
+ my $tax_rate = shift;
+
+ my $maxtype = $tax_rate->maxtype_name;
+ $maxtype =~ s/ /&nbsp;/g;
+
+ my $units = $tax_rate->unittype_name;
+ $units =~ s/ /&nbsp;/g;
+
+ my @limit = ();
+ push @limit,
+ sprintf("$money_char%.2f&nbsp%s", $tax_rate->taxbase, $maxtype )
+ if $tax_rate->taxbase > 0;
+ push @limit,
+ sprintf("$money_char%.2f&nbsp;tax", $tax_rate->taxmax )
+ if $tax_rate->taxmax > 0;
+ push @limit,
+ $tax_rate->feebase. "&nbsp;$units". ($tax_rate->feebase == 1 ? '' : 's')
+ if $tax_rate->feebase > 0;
+ push @limit,
+ $tax_rate->feemax. "&nbsp;$units". ($tax_rate->feebase == 1 ? '' : 's')
+ if $tax_rate->feemax > 0;
+
+ push @limit, 'Excluding&nbsp;setup&nbsp;fee'
+ if $tax_rate->setuptax =~ /^Y$/i;
+
+ push @limit, 'Excluding&nbsp;recurring&nbsp;fee'
+ if $tax_rate->recurtax =~ /^Y$/i;
+
+ [ map [ {'data'=>$_} ], @limit ];
+};
+
+my $oldrow;
+my $cell_style;
+my $cell_style_sub = sub {
+ my $row = shift;
+ if ( $oldrow ne $row ) {
+ if ( $oldrow ) {
+ if ( $oldrow->country ne $row->country ) {
+ $cell_style = 'border-top:1px solid #000000';
+ } elsif ( $oldrow->state ne $row->state ) {
+ $cell_style = 'border-top:1px solid #cccccc'; #default?
+ } elsif ( $oldrow->state eq $row->state ) {
+ #$cell_style = 'border-top:dashed 1px dark gray';
+ $cell_style = 'border-top:1px dashed #cccccc';
+ }
+ }
+ $oldrow = $row;
+ }
+ return $cell_style;
+};
+
+my $select_link = [ 'javascript:void(0);', sub { ''; } ];
+
+my $select_onclick = sub {
+ my $row = shift;
+ my $taxnum = $row->taxnum;
+ my $color = '#333399';
+ qq!overlib( OLiframeContent('${p}edit/tax_rate.html?$taxnum', 540, 620, 'edit_tax_rate_popup' ), CAPTION, 'Edit tax rate', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK, BGCOLOR, '$color', CGCOLOR, '$color' ); return false;!;
+};
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @menubar;
+my $title = '';
+
+my $data_vendor = '';
+if ( $cgi->param('data_vendor') =~ /^(\w+)$/ ) {
+ $data_vendor = $1;
+ $title = "$data_vendor";
+}
+$cgi->delete('data_vendor');
+
+my $geocode = '';
+if ( $cgi->param('geocode') =~ /^(\w+)$/ ) {
+ $geocode = $1;
+ $title = " geocode $geocode";
+}
+$cgi->delete('geocode');
+
+$title = " for $title" if $title;
+
+my $taxclassnum = '';
+if ( $cgi->param('taxclassnum') =~ /^(\d+)$/ ) {
+ $taxclassnum = $1;
+ my $tax_class = qsearchs('tax_class', {'taxclassnum' => $taxclassnum});
+ if ($tax_class) {
+ $title .= " for ". $tax_class->taxclass.
+ " (". $tax_class->description. ") tax class";
+ }else{
+ $taxclassnum = '';
+ }
+}
+$cgi->delete('taxclassnum');
+
+my $tax_type = $1
+ if ( $cgi->param('tax_type') =~ /^(\d+)$/ );
+my $tax_cat = $1
+ if ( $cgi->param('tax_cat') =~ /^(\d+)$/ );
+
+if ($tax_type || $tax_cat ) {
+ my $compare = "LIKE '". ( $tax_type || "%" ). ":". ( $tax_cat || "%" ). "'";
+ $compare = "= '$tax_type:$tax_cat'" if ($tax_type && $tax_cat);
+ my @tax_class =
+ qsearch({ 'table' => 'tax_class',
+ 'hashref' => {},
+ 'extra_sql' => "WHERE taxclass $compare",
+ });
+ if (@tax_class) {
+ $tax_class[0]->description =~ /^(.*):(.*)/;
+ $title .= " for";
+ $title .= " $tax_type ($1) tax type" if $tax_type;
+ $title .= " and" if ($tax_type && $tax_cat);
+ $title .= " $tax_cat ($2) tax category" if $tax_cat;
+ }else{
+ $tax_type = '';
+ $tax_cat = '';
+ }
+}
+$cgi->delete('tax_type');
+$cgi->delete('tax_cat');
+
+if ( $geocode || $taxclassnum ) {
+ push @menubar, 'View all tax rates' => $p.'browse/tax_rate.cgi';
+}
+
+$cgi->param('dummy', 1);
+
+#restore this so pagination works
+$cgi->param('data_vendor', $data_vendor) if $data_vendor;
+$cgi->param('geocode', $geocode) if $geocode;
+$cgi->param('taxclassnum', $taxclassnum ) if $taxclassnum;
+$cgi->param('tax_type', $tax_type ) if $tax_type;
+$cgi->param('tax_cat', $tax_cat ) if $tax_cat;
+
+my $html_form = include('/elements/init_overlib.html'). '<BR><BR>'.
+ join(' ',
+ map {
+ include('/elements/popup_link.html',
+ {
+ 'action' => $p. "misc/enable_or_disable_tax.html?action=$_&".
+ $cgi->query_string,
+ 'label' => ucfirst($_). ' all these taxes',
+ 'actionlabel' => ucfirst($_). ' taxes',
+ },
+ );
+ }
+ qw(disable enable)
+ );
+
+my ($query, $count_query) = FS::tax_rate::browse_queries(scalar($cgi->Vars));
+
+$cell_style = '';
+
+my @header = ( 'Location Code', );
+my @header2 = ( '', );
+my @links = ( '', );
+my @link_onclicks = ( '', );
+my $align = 'l';
+
+my @fields = (
+ 'geocode',
+);
+
+my @color = (
+ '000000',
+);
+
+push @header, qq!Tax class (<A HREF="${p}edit/tax_class.html">add new</A>)!;
+push @header2, '(per-tax classification)';
+push @fields, 'taxclass_description';
+push @color, '000000';
+push @links, '';
+push @link_onclicks, '';
+$align .= 'l';
+
+push @header, 'Tax name',
+ 'Rate', #'Tax',
+ 'Limits',
+ ;
+
+push @header2, '(printed on invoices)',
+ '',
+ '',
+ ;
+
+push @fields,
+ sub { shift->taxname || 'Tax' },
+ $rate_sub,
+ $limit_sub,
+;
+
+push @color,
+ sub { shift->taxname ? '000000' : '666666' },
+ sub { shift->tax ? '000000' : '666666' },
+ '000000',
+;
+
+$align .= 'lrl';
+
+my @cell_style = map $cell_style_sub, (1..scalar(@header));
+
+push @links, '', $select_link, '';
+push @link_onclicks, '', $select_onclick, '';
+
+my $html_init = '';
+
+$html_init .= qq(
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws_iframe.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws_draggable.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/iframecontentmws.js"></SCRIPT>
+
+);
+
+$html_init .= qq(
+ <FORM>
+ <TABLE>
+ <TR>
+ <TD><SELECT NAME="data_vendor" onChange="this.form.submit()">
+);
+
+my $sql = "SELECT DISTINCT data_vendor FROM tax_rate ORDER BY data_vendor";
+my $dbh = dbh;
+my $sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (['(choose data vendor)'], @{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $data_vendor ? " SELECTED" : "").
+ '">'. $_->[0];
+}
+$html_init .= qq(
+ </SELECT>
+
+ <TD><INPUT NAME="geocode" TYPE="text" SIZE="12" VALUE="$geocode"></TD>
+
+<!-- generic
+ <TD><INPUT NAME="taxclassnum" TYPE="text" SIZE="12" VALUE="$taxclassnum"></TD>
+ <TD><INPUT TYPE="submit" VALUE="Filter by tax_class"></TD>
+-->
+
+<!-- cch specific -->
+ <TD><SELECT NAME="tax_type" onChange="this.form.submit()">
+);
+
+$sql = "SELECT DISTINCT ".
+ "substring(taxclass from 1 for position(':' in taxclass)-1),".
+ "substring(description from 1 for position(':' in description)-1) ".
+ "FROM tax_class WHERE data_vendor='cch' ORDER BY 2";
+
+$sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (['', '(choose tax type)'], @{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $tax_type ? " SELECTED" : "").
+ '">'. $_->[1];
+}
+
+$html_init .= qq(
+ </SELECT>
+
+ <TD><SELECT NAME="tax_cat" onChange="this.form.submit()">
+);
+
+$sql = "SELECT DISTINCT ".
+ "substring(taxclass from position(':' in taxclass)+1),".
+ "substring(description from position(':' in description)+1) ".
+ "from tax_class WHERE data_vendor='cch' ORDER BY 2";
+
+$sth = $dbh->prepare($sql) or die $dbh->errstr;
+$sth->execute or die $sth->errstr;
+for (['', '(choose tax category)'], @{$sth->fetchall_arrayref}) {
+ $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
+ ($_->[0] eq $tax_cat ? " SELECTED" : "").
+ '">'. $_->[1];
+}
+
+$html_init .= qq(
+ </SELECT>
+
+ </TR>
+ <TR>
+ <TD></TD>
+ <TD><INPUT TYPE="submit" VALUE="Filter by geocode"></TD>
+ <TD></TD>
+ <TD></TD>
+ </TR>
+ </TABLE>
+ </FORM>
+
+);
+
+</%init>
diff --git a/httemplate/browse/usage_class.html b/httemplate/browse/usage_class.html
new file mode 100644
index 0000000..63fd2c5
--- /dev/null
+++ b/httemplate/browse/usage_class.html
@@ -0,0 +1,28 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Usage classes',
+ 'html_init' => $html_init,
+ 'name' => 'usage classes',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 2,
+ 'query' => { 'table' => 'usage_class',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY classnum',
+ },
+ 'count_query' => 'SELECT COUNT(*) FROM usage_class',
+ 'header' => [ '#', 'Class' ],
+ 'fields' => [ 'classnum', 'classname' ],
+ 'links' => [ $link, $link ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init =
+ 'Usage classes define groups of usage for taxation purposes.<BR><BR>'.
+ qq!<A HREF="${p}edit/usage_class.html"><I>Add a usage class</I></A><BR><BR>!;
+
+my $link = [ $p.'edit/usage_class.html?', 'classnum' ];
+
+</%init>
diff --git a/httemplate/config/config-delete.cgi b/httemplate/config/config-delete.cgi
new file mode 100644
index 0000000..cdac434
--- /dev/null
+++ b/httemplate/config/config-delete.cgi
@@ -0,0 +1,15 @@
+<%init>
+die "access denied\n"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+die "No configuration item specified (bad URL)!" unless $cgi->keywords;
+my ($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $confnum = $1;
+
+my $conf = qsearchs('conf', {'confnum' => $confnum});
+die "Configuration not found!" unless $conf;
+$conf->delete;
+
+</%init>
+<% $cgi->redirect(popurl(2) . "browse/agent.cgi") %>
diff --git a/httemplate/config/config-download.cgi b/httemplate/config/config-download.cgi
new file mode 100644
index 0000000..6979246
--- /dev/null
+++ b/httemplate/config/config-download.cgi
@@ -0,0 +1,28 @@
+%
+%
+%my $conf=new FS::Conf;
+%
+%http_header('Content-Type' => 'application/x-unknown' );
+%
+%die "No configuration variable specified (bad URL)!" # umm
+% unless $cgi->param('key');
+%$cgi->param('key') =~ /^([-\w.]+)$/;
+%my $name = $1;
+%
+%my $agentnum;
+%if ($cgi->param('agentnum') =~ /^(\d+)$/) {
+% $agentnum = $1;
+%}
+%
+%http_header('Content-Disposition' => "attachment; filename=$name" );
+% print $conf->config_binary($name, $agentnum);
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $agentnum;
+if ($cgi->param('agentnum') =~ /^(\d+)$/) {
+ $agentnum = $1;
+}
+
+</%init>
diff --git a/httemplate/config/config-image.cgi b/httemplate/config/config-image.cgi
new file mode 100644
index 0000000..892f7c6
--- /dev/null
+++ b/httemplate/config/config-image.cgi
@@ -0,0 +1,19 @@
+<% $conf->config_binary($name, $agentnum) %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+http_header( 'Content-Type' => 'image/png' ); #just png for now
+
+$cgi->param('key') =~ /^([-\w.]+)$/ or die "illegal config option";
+my $name = $1;
+
+my $agentnum = '';
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agentnum = $1;
+}
+
+</%init>
diff --git a/httemplate/config/config-process.cgi b/httemplate/config/config-process.cgi
new file mode 100644
index 0000000..84bfdef
--- /dev/null
+++ b/httemplate/config/config-process.cgi
@@ -0,0 +1,105 @@
+<%init>
+die "access denied\n"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+$FS::Conf::DEBUG = 1;
+my @config_items = grep { $_->key != ~/^invoice_(html|latex|template)/ }
+ $conf->config_items;
+my %confitems = map { $_->key => $_ } $conf->config_items;
+
+my $agentnum = $cgi->param('agentnum');
+my $key = $cgi->param('key');
+my $i = $confitems{$key};
+
+my @touch = ();
+my @delete = ();
+my $n = 0;
+foreach my $type ( ref($i->type) ? @{$i->type} : $i->type ) {
+ if ( $type eq '' ) {
+ } elsif ( $type eq 'textarea' ) {
+ if ( $cgi->param($i->key.$n) ne '' ) {
+ my $value = $cgi->param($i->key.$n);
+ $value =~ s/\r\n/\n/g; #browsers?
+ $conf->set($i->key, $value, $agentnum);
+ } else {
+ $conf->delete($i->key, $agentnum);
+ }
+ } elsif ( $type eq 'binary' || $type eq 'image' ) {
+ if ( defined($cgi->param($i->key.$n)) && $cgi->param($i->key.$n) ) {
+ my $fh = $cgi->upload($i->key.$n);
+ if (defined($fh)) {
+ local $/;
+ $conf->set_binary($i->key, <$fh>, $agentnum);
+ }
+ }else{
+ warn "Condition failed for " . $i->key;
+ }
+ } elsif ( $type eq 'checkbox' ) {
+ if ( defined $cgi->param($i->key.$n) ) {
+ push @touch, $i->key;
+ } else {
+ push @delete, $i->key;
+ }
+ } elsif ( $type eq 'text' || $type eq 'select' || $type eq 'select-sub' ) {
+ if ( $cgi->param($i->key.$n) ne '' ) {
+ $conf->set($i->key, $cgi->param($i->key.$n), $agentnum);
+ } else {
+ $conf->delete($i->key, $agentnum);
+ }
+ } elsif ( $type eq 'editlist' || $type eq 'selectmultiple' ) {
+ if ( scalar(@{[ $cgi->param($i->key.$n) ]}) ) {
+ $conf->set($i->key, join("\n", @{[ $cgi->param($i->key.$n) ]} ), $agentnum);
+ } else {
+ $conf->delete($i->key, $agentnum);
+ }
+ }
+ $n++;
+}
+# warn @touch;
+$conf->touch($_, $agentnum) foreach @touch;
+$conf->delete($_, $agentnum) foreach @delete;
+
+</%init>
+<% header('Configuration set') %>
+ <SCRIPT TYPE="text/javascript">
+% my $n = 0;
+% foreach my $type ( ref($i->type) ? @{$i->type} : $i->type ) {
+ var configCell = window.top.document.getElementById('<% $i->key. $n %>');
+ //alert('found cell ' + configCell);
+% if ( $type eq 'textarea'
+% || $type eq 'editlist'
+% || $type eq 'selectmultiple' ) {
+ configCell.innerHTML =
+ '<font size="-2"><pre>' + "\n" +
+ <% encode_entities(join("\n",
+ map { length($_) > 88 ? substr($_,0,88).'...' : $_ }
+ $conf->config($i->key, $agentnum)
+ ) )
+ |js_string %> +
+ '</pre></font>';
+
+% } elsif ( $type eq 'checkbox' ) {
+% if ( $conf->exists($i->key, $agentnum) ) {
+ configCell.style.backgroundColor = '#00ff00';
+ configCell.innerHTML = 'YES';
+% } else {
+ configCell.style.backgroundColor = '#ff0000';
+ configCell.innerHTML = 'NO';
+% }
+% } elsif ( $type eq 'text' || $type eq 'select' ) {
+ configCell.innerHTML = <% $conf->exists($i->key, $agentnum) ? $conf->config($i->key, $agentnum) : '' |js_string %>;
+% } elsif ( $type eq 'select-sub' ) {
+ configCell.innerHTML =
+ <% $conf->config($i->key, $agentnum) |js_string %> + ': ' +
+ <% &{ $i->option_sub }( $conf->config($i->key, $agentnum) ) |js_string %>;
+% } else {
+ //alert('unknown type <% $type %>');
+ window.top.location.reload();
+% }
+
+% $n++;
+% }
+ parent.cClick();
+ </SCRIPT>
+ </BODY></HTML>
diff --git a/httemplate/config/config-view.cgi b/httemplate/config/config-view.cgi
new file mode 100644
index 0000000..0f5fd62
--- /dev/null
+++ b/httemplate/config/config-view.cgi
@@ -0,0 +1,177 @@
+<% include("/elements/header.html", $title, menubar(@menubar)) %>
+
+Click on a configuration value to change it.
+<BR><BR>
+
+<% include('/elements/init_overlib.html') %>
+
+% if ($FS::UID::use_confcompat) {
+ <FONT SIZE="+1" COLOR="#ff0000">CONFIGURATION NOT STORED IN DATABASE -- USING COMPATIBILITY MODE</FONT><BR><BR>
+%}
+
+% foreach my $section (@sections) {
+
+ <A NAME="<% $section || 'unclassified' %>"></A>
+ <FONT SIZE="-2">
+
+% foreach my $nav_section (@sections) {
+%
+% if ( $section eq $nav_section ) {
+ [<A NAME="not<% $nav_section || 'unclassified' %>" style="background-color: #cccccc"><% ucfirst($nav_section || 'unclassified') %></A>]
+% } else {
+ [<A HREF="#<% $nav_section || 'unclassified' %>"><% ucfirst($nav_section || 'unclassified') %></A>]
+% }
+%
+% }
+
+ </FONT><BR>
+ <TABLE BGCOLOR="#cccccc" BORDER=1 CELLSPACING=0 CELLPADDING=0 BORDERCOLOR="#999999">
+ <tr>
+ <th colspan="2" bgcolor="#dcdcdc">
+ <% ucfirst($section || 'unclassified') %> configuration options
+ </th>
+ </tr>
+% foreach my $i (@{ $section_items{$section} }) {
+% my @types = ref($i->type) ? @{$i->type} : ($i->type);
+% my( $width, $height ) = ( 522, 336 );
+% if ( grep $_ eq 'textarea', @types ) {
+% #800x600
+% $width = 763;
+% $height = 408;
+% #1024x768
+% #$width =
+% #$height =
+% }
+
+ <tr>
+ <td><% include('/elements/popup_link.html',
+ 'action' => 'config.cgi?key='. $i->key.
+ ';agentnum='. $agentnum,
+ 'width' => $width,
+ 'height' => $height,
+ 'actionlabel' => 'Enter configuration value',
+ 'label' => '<b>'. $i->key. '</b>',
+ 'aname' => $i->key,
+ )
+ %>: <% $i->description %>
+ </td>
+ <td><table border=0>
+
+% my $n = 0;
+% foreach my $type (@types) {
+
+% if ( $type eq '' ) {
+
+ <tr>
+ <td><font color="#ff0000">no type</font></td>
+ </tr>
+
+% } elsif ( $type eq 'image' ) {
+
+ <tr>
+
+ <% $conf->exists($i->key, $agentnum)
+ ? '<img src="config-image.cgi?key='. $i->key.
+ ';agentnum='. $agentnum. '">'
+ : 'empty'
+ %>
+ </tr>
+
+% } elsif ( $type eq 'binary' ) {
+
+ <tr>
+
+ <% $conf->exists($i->key, $agentnum)
+ ? qq!<a href="config-download.cgi?key=!. $i->key. ';agentnum='. $agentnum. qq!">download</a>!
+ : 'empty'
+ %>
+ </tr>
+
+% } elsif ( $type eq 'textarea'
+% || $type eq 'editlist'
+% || $type eq 'selectmultiple' ) {
+
+ <tr>
+ <td id="<% $i->key.$n %>" bgcolor="#ffffff">
+<font size="-2"><pre>
+<% encode_entities(join("\n",
+ map { length($_) > 88 ? substr($_,0,88).'...' : $_ }
+ $conf->config($i->key, $agentnum)
+ ) )
+%>
+</pre></font>
+ </td>
+ </tr>
+% } elsif ( $type eq 'checkbox' ) {
+
+ <tr>
+ <td id="<% $i->key.$n %>" bgcolor="#<% $conf->exists($i->key, $agentnum) ? '00ff00">YES' : 'ff0000">NO' %></td>
+ </tr>
+% } elsif ( $type eq 'text' || $type eq 'select' ) {
+
+ <tr>
+ <td id="<% $i->key.$n %>" bgcolor="#ffffff">
+ <% $conf->exists($i->key, $agentnum) ? $conf->config($i->key, $agentnum) : '' %>
+ </td></tr>
+% } elsif ( $type eq 'select-sub' ) {
+
+ <tr>
+ <td id="<% $i->key.$n %>" bgcolor="#ffffff">
+ <% $conf->config($i->key, $agentnum) %>:
+ <% &{ $i->option_sub }( $conf->config($i->key, $agentnum) ) %>
+ </td>
+ </tr>
+% } else {
+
+ <tr><td>
+ <font color="#ff0000">unknown type <% $type %></font>
+ </td></tr>
+% }
+% $n++;
+% }
+
+ </table></td>
+ </tr>
+% }
+
+ </table><br><br>
+% }
+
+
+</body></html>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $agentnum = '';
+my $title;
+my @menubar = ();
+if ($cgi->param('agentnum') =~ /^(\d+)$/) {
+ $agentnum = $1;
+ my $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+ die "Agent $agentnum not found!" unless $agent;
+
+ push @menubar, 'View all agents' => $p.'browse/agent.cgi';
+ $title = 'Agent Configuration for '. $agent->agent;
+} else {
+ $title = 'Global Configuration';
+}
+
+my $conf = new FS::Conf;
+
+my @config_items = grep { $agentnum ? $_->per_agent : 1 }
+ grep { $_->key != ~/^invoice_(html|latex|template)/ }
+ $conf->config_items;
+
+my @sections = qw(required billing username password UI session shell BIND );
+push @sections, '', 'deprecated';
+
+my %section_items = ();
+foreach my $section (@sections) {
+ $section_items{$section} = [ grep $_->section eq $section, @config_items ];
+}
+
+@sections = grep scalar( @{ $section_items{$_} } ), @sections;
+
+</%init>
diff --git a/httemplate/config/config.cgi b/httemplate/config/config.cgi
new file mode 100644
index 0000000..f390c64
--- /dev/null
+++ b/httemplate/config/config.cgi
@@ -0,0 +1,331 @@
+<% include("/elements/header-popup.html", $title) %>
+
+<SCRIPT>
+var gSafeOnload = new Array();
+var gSafeOnsubmit = new Array();
+window.onload = SafeOnload;
+function SafeAddOnLoad(f) {
+ gSafeOnload[gSafeOnload.length] = f;
+}
+function SafeOnload() {
+ for (var i=0;i<gSafeOnload.length;i++)
+ gSafeOnload[i]();
+}
+function SafeAddOnSubmit(f) {
+ gSafeOnsubmit[gSafeOnsubmit.length] = f;
+}
+function SafeOnsubmit() {
+ for (var i=0;i<gSafeOnsubmit.length;i++)
+ gSafeOnsubmit[i]();
+}
+</SCRIPT>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="OneTrueForm" ACTION="config-process.cgi" METHOD="POST" enctype="multipart/form-data" onSubmit="SafeOnsubmit()">
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agentnum %>">
+<INPUT TYPE="hidden" NAME="key" VALUE="<% $key %>">
+
+Setting <b><% $key %></b>
+
+% my $description_printed = 0;
+% if ( grep $_ eq 'textarea', @types ) {
+% $description_printed = 1;
+
+ - <% $description %>
+
+% }
+
+<table><tr><td>
+
+% my $n = 0;
+% foreach my $type (@types) {
+% if ( $type eq '' ) {
+
+ <font color="#ff0000">no type</font>
+
+% } elsif ( $type eq 'image' ) {
+
+ <% $conf->exists($key, $agentnum)
+ ? 'Current image<br>'.
+ '<img src="config-image.cgi?key='. $key.
+ ';agentnum='. $agentnum. '"><br>'
+ : ''
+ %>
+
+ <BR>
+ New image filename <input type="file" name="<% "$key$n" %>">
+
+% } elsif ( $type eq 'binary' ) {
+
+ Filename <input type="file" name="<% "$key$n" %>">
+
+% } elsif ( $type eq 'textarea' ) {
+
+ <textarea name="<% "$key$n" %>" rows=12 cols=78 wrap="off"><% join("\n", $conf->config($key, $agentnum)) |h %></textarea>
+
+% } elsif ( $type eq 'checkbox' ) {
+
+ <input name="<% "$key$n" %>" type="checkbox" value="1"
+ <% $conf->exists($key, $agentnum) ? 'CHECKED' : '' %> >
+
+% } elsif ( $type eq 'text' ) {
+
+ <input name="<% "$key$n" %>" type="text" value="<% $conf->exists($key, $agentnum) ? $conf->config($key, $agentnum) : '' |h %>">
+
+% } elsif ( $type eq 'select' || $type eq 'selectmultiple' ) {
+
+ <select name="<% "$key$n" %>" <% $type eq 'selectmultiple' ? 'MULTIPLE' : '' %>>
+
+%
+% my %hash = ();
+% if ( $config_item->select_enum ) {
+% tie %hash, 'Tie::IxHash',
+% '' => '', map { $_ => $_ } @{ $config_item->select_enum };
+% } elsif ( $config_item->select_hash ) {
+% if ( ref($config_item->select_hash) eq 'ARRAY' ) {
+% tie %hash, 'Tie::IxHash',
+% '' => '', @{ $config_item->select_hash };
+% } else {
+% tie %hash, 'Tie::IxHash',
+% '' => '', %{ $config_item->select_hash };
+% }
+% } else {
+% %hash = ( '' => 'WARNING: neither select_enum nor select_hash specified in Conf.pm for configuration option "'. $key. '"' );
+% }
+%
+% my %saw = ();
+% foreach my $value ( keys %hash ) {
+% local($^W)=0; next if $saw{$value}++;
+% my $label = $hash{$value};
+%
+
+ <option value="<% $value %>"
+
+% if ( $value eq $conf->config($key, $agentnum)
+% || ( $type eq 'selectmultiple'
+% && grep { $_ eq $value } $conf->config($key, $agentnum) ) ) {
+
+ SELECTED
+
+% }
+
+ ><% $label %>
+
+% }
+% my $curvalue = $conf->config($key, $agentnum);
+% if ( $conf->exists($key, $agentnum) && $curvalue && ! $hash{$curvalue} ) {
+
+ <option value="<% $curvalue %>" SELECTED>
+
+% if ( exists( $hash{ $conf->config($key, $agentnum) } ) ) {
+
+ <% $hash{ $conf->config($key, $agentnum) } %>
+
+% }else{
+
+ <% $curvalue %>
+
+% }
+% }
+
+ </select>
+
+% } elsif ( $type eq 'select-sub' ) {
+
+ <select name="<% "$key$n" %>"><option value="">
+
+% my %options = &{$config_item->options_sub};
+% my @options = sort { $a <=> $b } keys %options;
+% my %saw;
+% foreach my $value ( @options ) {
+% local($^W)=0; next if $saw{$value}++;
+
+ <option value="<% $value %>" <% $value eq $conf->config($key, $agentnum) ? 'SELECTED' : '' %>><% $value %>: <% $options{$value} %>
+
+% }
+% my $curvalue = $conf->config($key, $agentnum);
+% if ( $conf->exists($key, $agentnum) && $curvalue && ! $options{$curvalue} ) {
+
+ <option value="<% $curvalue %>" SELECTED> <% $curvalue %>: <% &{ $config_item->option_sub }( $curvalue ) %>
+
+% }
+
+ </select>
+
+% } elsif ( $type eq 'editlist' ) {
+%
+ <script>
+ function doremove<% "$key$n" %>() {
+ fromObject = document.OneTrueForm.<% "$key$n" %>;
+ for (var i=fromObject.options.length-1;i>-1;i--) {
+ if (fromObject.options[i].selected)
+ deleteOption<% "$key$n" %>(fromObject,i);
+ }
+ }
+ function deleteOption<% "$key$n" %>(object,index) {
+ object.options[index] = null;
+ }
+ function selectall<% "$key$n" %>() {
+ fromObject = document.OneTrueForm.<% "$key$n" %>;
+ for (var i=fromObject.options.length-1;i>-1;i--) {
+ fromObject.options[i].selected = true;
+ }
+ }
+ function doadd<% "$key$n" %>(object) {
+ var myvalue = "";
+
+% if ( defined($config_item->editlist_parts) ) {
+% foreach my $pnum ( 0 .. scalar(@{$config_item->editlist_parts})-1 ) {
+
+ if ( myvalue != "" ) { myvalue = myvalue + " "; }
+
+% if ( $config_item->editlist_parts->[$pnum]{type} eq 'select' ) {
+
+ myvalue = myvalue + object.add<% "$key${n}_$pnum" %>.options[object.add<% "$key${n}_$pnum" %>.selectedIndex].value
+ <!-- #RESET SELECT?? maybe not... -->
+
+% } elsif ( $config_item->editlist_parts->[$pnum]{type} eq 'immutable' ) {
+
+ myvalue = myvalue + object.add<% "$key${n}_$pnum" %>.value
+
+% } else {
+
+ myvalue = myvalue + object.add<% "$key${n}_$pnum" %>.value
+ object.add<% "$key${n}_$pnum" %>.value = ""
+
+% }
+% }
+% } else {
+
+ myvalue = object.add<% "$key${n}_1" %>.value
+
+% }
+
+ var optionName = new Option(myvalue, myvalue);
+ var length = object.<% "$key$n" %>.length;
+ object.<% "$key$n" %>.options[length] = optionName;
+ }
+ </script>
+ <select multiple size=5 name="<% "$key$n" %>">
+ <option selected>----------------------------------------------------------------</option>
+
+% foreach my $line ( $conf->config($key, $agentnum) ) {
+
+ <option value="<% $line %>"><% $line %></option>
+
+% }
+
+ </select><br>
+ <input type="button" value="remove selected" onClick="doremove<% "$key$n" %>()">
+ <script>SafeAddOnLoad(doremove<% "$key$n" %>);
+ SafeAddOnSubmit(selectall<% "$key$n" %>);
+ </script>
+ <br><% itable() %><tr>
+
+% if ( defined $config_item->editlist_parts ) {
+% my $pnum=0;
+% foreach my $part ( @{$config_item->editlist_parts} ) {
+
+ <td>
+
+% if ( $part->{type} eq 'text' ) {
+
+ <input type="text" name="add<% "$key${n}_$pnum" %>">
+
+% } elsif ( $part->{type} eq 'immutable' ) {
+
+ <% $part->{value} %>
+ <input type="hidden" name="add<% "$key${n}_$pnum" %>" value="<% $part->{value} %>">
+
+% } elsif ( $part->{type} eq 'select' ) {
+
+ <select name="add<% qq!$key${n}_$pnum! %>">
+
+% foreach my $key ( keys %{$part->{select_enum}} ) {
+
+ <option value="<% $key %>"><% $part->{select_enum}{$key} %></option>
+
+% }
+
+ </select>
+
+% } else {
+
+ <font color="#ff0000">unknown type <% $part->type %> </font>
+
+% }
+
+ </td>
+
+% $pnum++;
+% }
+% } else {
+
+ <td><input type="text" name="add<% "$key${n}_0" %>></td>
+
+% }
+
+ <td><input type="button" value="add" onClick="doadd<% "$key$n" %>(this.form)"></td>
+ </tr></table>
+
+% } else {
+
+ <font color="#ff0000">unknown type <% $type %></font>
+
+% }
+% $n++;
+% }
+
+ </td>
+% unless ( $description_printed ) {
+ <td><% $description %></td>
+% }
+</tr>
+</table>
+<INPUT TYPE="submit" VALUE="<% $title %>">
+</FORM>
+
+</BODY>
+</HTML>
+<%once>
+
+my $conf = new FS::Conf;
+my @config_items = grep { $_->key != ~/^invoice_(html|latex|template)/ }
+ $conf->config_items;
+my %confitems = map { $_->key => $_ } @config_items;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $action = 'Set';
+
+my $agentnum = '';
+if ($cgi->param('agentnum') =~ /(\d+)$/) {
+ $agentnum=$1;
+}
+
+my $agent = '';
+my $title;
+if ($agentnum) {
+ $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "Agent $agentnum not found!" unless $agent;
+
+ $title = "$action configuration override for ". $agent->agent;
+} else {
+ $title = "$action global configuration";
+}
+
+$cgi->param('key') =~ /^([-.\w]+)$/ or die "illegal configuration item";
+my $key = $1;
+my $value = $conf->config($key);
+my $config_item = $confitems{$key};
+
+my $description = $config_item->description;
+my $config_type = $config_item->type;
+my @types = ref($config_type) ? @$config_type : ($config_type);
+
+</%init>
diff --git a/httemplate/docs/AGPL.html b/httemplate/docs/AGPL.html
new file mode 100644
index 0000000..f55bebb
--- /dev/null
+++ b/httemplate/docs/AGPL.html
@@ -0,0 +1,672 @@
+<h3 style="text-align: center;">GNU AFFERO GENERAL PUBLIC LICENSE</h3>
+<p style="text-align: center;">Version 3, 19 November 2007</p>
+
+<p>Copyright (C) 2007 Free Software Foundation, Inc. &lt;http://fsf.org/&gt;
+ <br>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.</p>
+
+<h3>Preamble</h3>
+
+<p>The GNU Affero General Public License is a free, copyleft license
+for software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.</p>
+
+<p>The licenses for most software and other practical works are
+designed to take away your freedom to share and change the works. By
+contrast, our General Public Licenses are intended to guarantee your
+freedom to share and change all versions of a program--to make sure it
+remains free software for all its users.</p>
+
+<p>When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.</p>
+
+<p>Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.</p>
+
+<p>A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.</p>
+
+<p>The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.</p>
+
+<p>An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.</p>
+
+<p>The precise terms and conditions for copying, distribution and
+modification follow.</p>
+
+<h3>TERMS AND CONDITIONS</h3>
+
+<h4>0. Definitions.</h4>
+
+<p>"This License" refers to version 3 of the GNU Affero General Public
+License.</p>
+
+<p>"Copyright" also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.</p>
+
+<p>"The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.</p>
+
+<p>To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.</p>
+
+<p>A "covered work" means either the unmodified Program or a work based
+on the Program.</p>
+
+<p>To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.</p>
+
+<p>To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.</p>
+
+<p>An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.</p>
+
+<h4>1. Source Code.</h4>
+
+<p>The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.</p>
+
+<p>A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.</p>
+
+<p>The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.</p>
+
+<p>The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.</p>
+
+<p>The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.</p>
+
+<p>The Corresponding Source for a work in source code form is that
+same work.</p>
+
+<h4>2. Basic Permissions.</h4>
+
+<p>All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.</p>
+
+<p>You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.</p>
+
+<p>Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.</p>
+
+<h4>3. Protecting Users' Legal Rights From Anti-Circumvention Law.</h4>
+
+<p>No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.</p>
+
+<p>When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.</p>
+
+<h4>4. Conveying Verbatim Copies.</h4>
+
+<p>You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.</p>
+
+<p>You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.</p>
+
+<h4>5. Conveying Modified Source Versions.</h4>
+
+<p>You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:</p>
+
+<ul>
+<li>a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.</li>
+
+<li>b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".</li>
+
+<li>c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.</li>
+
+<li>d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.</li>
+
+</ul>
+<p>A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.</p>
+
+<h4>6. Conveying Non-Source Forms.</h4>
+
+<p>You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:</p>
+
+<ul>
+<li>a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.</li>
+
+<li>b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.</li>
+
+<li>c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.</li>
+
+<li>d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.</li>
+
+<li>e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.</li>
+
+</ul>
+<p>A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.</p>
+
+<p>A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.</p>
+
+<p>"Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.</p>
+
+<p>If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).</p>
+
+<p>The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.</p>
+
+<p>Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.</p>
+
+<h4>7. Additional Terms.</h4>
+
+<p>"Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.</p>
+
+<p>When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.</p>
+
+<p>Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:</p>
+
+<ul>
+<li>a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or</li>
+
+<li>b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or</li>
+
+<li>c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or</li>
+
+<li>d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or</li>
+
+<li>e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or</li>
+
+<li>f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.</li>
+
+</ul>
+<p>All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further restriction,
+you may remove that term. If a license document contains a further
+restriction but permits relicensing or conveying under this License, you
+may add to a covered work material governed by the terms of that license
+document, provided that the further restriction does not survive such
+relicensing or conveying.</p>
+
+<p>If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.</p>
+
+<p>Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.</p>
+
+<h4>8. Termination.</h4>
+
+<p>You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).</p>
+
+<p>However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.</p>
+
+<p>Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.</p>
+
+<p>Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.</p>
+
+<h4>9. Acceptance Not Required for Having Copies.</h4>
+
+<p>You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.</p>
+
+<h4>10. Automatic Licensing of Downstream Recipients.</h4>
+
+<p>Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.</p>
+
+<p>An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.</p>
+
+<p>You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.</p>
+
+<h4>11. Patents.</h4>
+
+<p>A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".</p>
+
+<p>A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.</p>
+
+<p>Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.</p>
+
+<p>In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.</p>
+
+<p>If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.</p>
+
+<p>If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.</p>
+
+<p>A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.</p>
+
+<p>Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.</p>
+
+<h4>12. No Surrender of Others' Freedom.</h4>
+
+<p>If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.</p>
+
+<h4>13. Remote Network Interaction; Use with the GNU General Public License.</h4>
+
+<p>Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.</p>
+
+<p>Notwithstanding any other provision of this License, you have permission
+to link or combine any covered work with a work licensed under version 3
+of the GNU General Public License into a single combined work, and to
+convey the resulting work. The terms of this License will continue to
+apply to the part which is the covered work, but the work with which it is
+combined will remain governed by version 3 of the GNU General Public
+License.</p>
+
+<h4>14. Revised Versions of this License.</h4>
+
+<p>The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may differ
+in detail to address new problems or concerns.</p>
+
+<p>Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero
+General Public License "or any later version" applies to it, you have
+the option of following the terms and conditions either of that
+numbered version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number
+of the GNU Affero General Public License, you may choose any version
+ever published by the Free Software Foundation.</p>
+
+<p>If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that
+proxy's public statement of acceptance of a version permanently
+authorizes you to choose that version for the Program.</p>
+
+<p>Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.</p>
+
+<h4>15. Disclaimer of Warranty.</h4>
+
+<p>THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.</p>
+
+<h4>16. Limitation of Liability.</h4>
+
+<p>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.</p>
+
+<h4>17. Interpretation of Sections 15 and 16.</h4>
+
+<p>If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.</p>
+
+<p>END OF TERMS AND CONDITIONS</p>
+
+<h3>How to Apply These Terms to Your New Programs</h3>
+
+<p>If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.</p>
+
+<p>To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.</p>
+
+<pre> &lt;one line to give the program's name and a brief idea of what it does.&gt;
+ Copyright (C) &lt;year&gt; &lt;name of author&gt;
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.
+</pre>
+
+<p>Also add information on how to contact you by electronic and paper mail.</p>
+
+<p>If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.</p>
+
+<p>You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+&lt;http://www.gnu.org/licenses/&gt;.
+</p>
+
diff --git a/httemplate/docs/about.html b/httemplate/docs/about.html
new file mode 100644
index 0000000..dee4247
--- /dev/null
+++ b/httemplate/docs/about.html
@@ -0,0 +1,53 @@
+<% include('/elements/header-popup.html', 'Freeside') %>
+
+<% include('/elements/init_overlib.html') %>
+
+<CENTER>
+<IMG SRC="<%$fsurl%>images/small-logo.png" BORDER=0"><BR>
+<H3>version <% $FS::VERSION %></H3>
+</CENTER>
+
+<CENTER>
+<FONT SIZE="-1">&copy; 2008 Freeside Internet Services, Inc.<BR>
+All rights reserved.<BR>
+Licensed under the terms of the<BR>
+GNU <i>Affero</i> General Public License.<BR>
+</FONT>
+</CENTER>
+<BR>
+
+<CENTER>
+<A HREF="credits.html">Credits</A>
+ &nbsp;&nbsp;&nbsp;&nbsp;
+<A HREF="javascript:void(0)" onClick="openLicense()">License</A>
+
+
+<BR><BR>
+<A HREF="http://www.freeside.biz/freeside" TARGET="_blank">Freeside homepage</A>
+</CENTER>
+
+<BR>
+
+<CENTER>
+<FONT SIZE="-3">"I need a miracle every day." -J.P. Barlow</FONT>
+</CENTER>
+
+<SCRIPT TYPE="text/javascript">
+
+function openLicense() {
+ parent.<% include('/elements/popup_link_onclick.html',
+ 'action' => $fsurl.'docs/license.html',
+ 'label' => 'License',
+ 'actionlabel' => 'License',
+ 'width' => 600,
+ 'height' => 360,
+ 'color' => '#7e0079',
+ 'nofalse' => 1,
+ )
+ %>
+}
+
+</SCRIPT>
+
+</BODY>
+</HTML>
diff --git a/httemplate/docs/ach.html b/httemplate/docs/ach.html
new file mode 100644
index 0000000..b8a17c8
--- /dev/null
+++ b/httemplate/docs/ach.html
@@ -0,0 +1,10 @@
+<HTML>
+ <HEAD>
+ <TITLE>
+ Electronic check (ACH) information
+ </TITLE>
+ </HEAD>
+ <BODY BGCOLOR="#ffffff">
+ <IMG BORDER=0 SRC="../images/ach.png">
+ </BODY>
+</HTML>
diff --git a/httemplate/docs/admin.html b/httemplate/docs/admin.html
new file mode 100755
index 0000000..2aa9348
--- /dev/null
+++ b/httemplate/docs/admin.html
@@ -0,0 +1,41 @@
+<head>
+ <title>Administration</title>
+</head>
+<body>
+ <h1>Administration</h1>
+</body>
+<ul>
+ <li>Open up the root of the Freeside document tree in your web
+ browser. For example, if you created the Freeside document tree in
+ /home/httpd/html/freeside, and your web browser's DocumentRoot is
+ /home/httpd/html, open https://your_host/freeside/. Replace
+ "your_host" with the name or network address of your web server.
+ <li>Select <u>Configuration</u> from the main menu and update your configuration values.
+
+ <li>Go to <u>View/Edit service definitions</u> on the main menu, and
+ <u>Add a new service definition</u> with <i>Table</i> <b>svc_acct</b>.
+ Select your domain in the <b>domsvc</b> Modifier. Set <b>Fixed</b> to define
+ a service locked-in to this domain, or <b>Default</b> to define a service
+ which may select from among this domain and the customer's domains.
+
+ <li><table><tr>
+ <td> Create at least POP (Point of Presence) by selecting
+ <u>View/Edit POPs</u> from the main menu.</td>
+ <th align="left"> OR </th>
+ <td>If you are not doing dialup, set slipip to fixed and blank for all your
+ Service Definitions which have Table <b>svc_acct</b>.</td>
+ </tr></table>
+
+ <li>If you are using Freeside to keep track of sales taxes, define tax
+ information for your locales by clicking on the <u>View/Edit locales and tax
+ rates</u> on the main menu.
+
+ <li>If you would like Freeside to notify your customers when their credit
+ cards and other billing arrangements are about to expire, arrange for
+ <b>freeside-expiration-alerter</b> to be run daily by cron or similar
+ facility. The message it sends can be configured from the
+ <u>Configuration</u> choice of the main menu as <u>alerter_template</u>.
+
+</ul>
+</body>
+</html>
diff --git a/httemplate/docs/credits.html b/httemplate/docs/credits.html
new file mode 100644
index 0000000..3c5564d
--- /dev/null
+++ b/httemplate/docs/credits.html
@@ -0,0 +1,175 @@
+<% include('/elements/header-popup.html', '') %>
+
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+
+<FONT SIZE=6>
+ <CENTER>Freeside</CENTER>
+</FONT>
+
+<BR>
+
+<CENTER>
+<IMG SRC="<%$fsurl%>images/small-logo.png" BORDER=0"><BR>
+<H3>version <% $FS::VERSION %></H3>
+</CENTER>
+
+<CENTER>
+
+<H3>Core team</H3>
+Peter Bowen<BR>
+Jeff Finucane<BR>
+Jason Hall<BR>
+Kristian Hoffman<BR>
+Ivan Kohler<BR>
+Richard Siddall<BR>
+<BR>
+
+<H3>Contributors</H3>
+Stephen Amadei<BR>
+Eric Arvidsson<BR>
+Mark Asplen-Taylor<BR>
+Mihai Bazon<BR>
+Charles A. Beasley<BR>
+Stephen Bechard<BR>
+Eric Bosrup<BR>
+Dave Burgess<BR>
+Joe Camadine<BR>
+Chris Cappuccio<BR>
+Rebecca Cardennis<BR>
+Shane Chrisp<BR>
+Luke Crawford<BR>
+Brad Dameron<BR>
+Dave Denney<BR>
+Serge Dolgov<BR>
+Scott Edwards<BR>
+Kenny Elliott<BR>
+Donald Greer<BR>
+Joel Griffiths<BR>
+Ryan Gunn<BR>
+Troy Hammonds<BR>
+Sean Hanson<BR>
+Dale Hege<BR>
+Kelly Hickel<BR>
+Mark James<BR>
+Frederico Caldeira Knabben<BR>
+Greg Kuhnert<BR>
+Randall Lucas<BR>
+Foteos Macrides<BR>
+Roger Mangraviti<BR>
+Brian McCane<BR>
+mimooh<BR>
+Mack Nagashima<BR>
+Matt Peterson<BR>
+Luke Pfeifer<BR>
+Ricardo Signes<BR>
+Matt Simerson<BR>
+Steve Simitzis<BR>
+Jason Spence<BR>
+James Switzer<BR>
+Audrey Tang<BR>
+Jason Thomas<BR>
+Jesse Vincent<BR>
+Mark Wells<BR>
+Peter Wemm<BR>
+Mark Williamson<BR>
+Tim Yardley<BR>
+
+</CENTER>
+
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+<BR>
+
+<SCRIPT TYPE="text/javascript">
+
+function myScroll() {
+
+ documentYposition += 1;
+ window.scroll(0,documentYposition);
+
+ var timeout = 25;
+
+ if ( documentYposition > documentLength ) {
+ documentYposition = 0;
+ }
+
+ if ( documentYposition == startingPosition ) {
+ timeout = 5000;
+ }
+
+ setTimeout('myScroll()', timeout);
+}
+
+function DelayThenScroll() {
+ window.scroll(0,documentYposition);
+ documentLength = myHeight();
+ setTimeout('myScroll()', 3000);
+}
+
+function myHeight() {
+/* if (document.all)
+ return document.body.offsetHeight;
+ else if (document.layers)
+ return document.body.document.height;
+ else
+*/
+ return 1700; // approx height (add more per contributors)
+}
+
+document.body.style.overflow = 'hidden';
+
+var startingPosition = 360;
+
+//huh, adjust for firefox
+var ua = navigator.userAgent;
+var opera = /opera [56789]|opera\/[56789]/i.test(ua);
+var webkit = /webkit/i.test(ua)
+var moz = !opera && !webkit && /gecko/i.test(ua);
+if ( moz ) {
+ startingPosition += 20;
+} else if ( opera ) {
+ startingPosition += 21;
+}
+
+var documentYposition = startingPosition;
+var documentLength;
+window.onLoad = DelayThenScroll();
+
+</SCRIPT>
+
+</BODY>
+</HTML>
diff --git a/httemplate/docs/cvv2.html b/httemplate/docs/cvv2.html
new file mode 100644
index 0000000..7670985
--- /dev/null
+++ b/httemplate/docs/cvv2.html
@@ -0,0 +1,24 @@
+<HTML>
+ <HEAD>
+ <TITLE>
+ CVV2 information
+ </TITLE>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+ security code used to reduce credit card fraud.<BR><BR>
+ <TABLE BORDER=0 CELLSPACING=4>
+ <TR>
+ <TH>Visa / MasterCard / Discover</TH>
+ <TH>American Express</TH>
+ </TR>
+ <TR>
+ <TD>
+ <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="../images/cvv2.png">
+ </TD>
+ <TD>
+ <IMG BORDER=0 ALT="American Express" SRC="../images/cvv2_amex.png">
+ </TD>
+ </TABLE>
+ </BODY>
+</HTML>
diff --git a/httemplate/docs/ieak.html b/httemplate/docs/ieak.html
new file mode 100644
index 0000000..00c5342
--- /dev/null
+++ b/httemplate/docs/ieak.html
@@ -0,0 +1,75 @@
+<pre>
+this is incomplete
+mostly it should be merged into signup.html and fs_signup/ieak.template
+
+- download and install the IEAK from
+ http://www.microsoft.com/windows/ieak/default.asp
+
+- Good examples may be found in
+ C:\Program Files\IEAK\toolkit\isp\server\ICW\signup\perl\signup08.pl
+ C:\Program Files\IEAK\toolkit\isp\server\ICW\reconfig\perl\reconfig04.pl
+ C:\Program Files\IEAK6\toolkit\isp\servless\basic\sample.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\4567.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\4568.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\7890.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\7891.ins
+
+- Full documentation on all the settings available in .INS files is
+ avaialble under Program Files | Microsoft IEAK 6 | IEAK Help
+ | Reference | Internet Settings (.ins) Files
+
+- Freeside will make the following substitutions before sending the file
+ to the user:
+
+ { $ac } - area code of selected POP
+ { $exch } - exchange of selected POP
+ { $loc } - local part of selected POP
+ { $username }
+ { $password }
+ { $email_name } - first and last name
+ { $pkg } - package name
+
+- Simple example follows:
+
+[Entry]
+Entry Name = IEAK Sample
+[Phone]
+Dial_As_Is = No
+Phone_Number = { $exch }{ $loc }
+Area_Code = { $ac }
+Country_Code = 1
+Country_Id = 1
+[Server]
+Type = PPP
+SW_Compress = Yes
+PW_Encrypt = Yes
+Negotiate_TCP/IP = Yes
+Disable_LCP = No
+[TCP/IP]
+Specity_IP_Address = No
+Specity_Server_Address = No
+IP_Header_Compress = Yes
+Gateway_On_Remote = Yes
+[User]
+Name = { $username }
+Passowrd = { $password }
+Display_Password = Yes
+[Internet_Mail]
+Email_Name = { $email_name }
+Email_Address = { $username }@example.com
+POP_Server = mail.example.com
+POP_Server_Port_Number = 110
+POP_Logon_Password = { $password }
+SMTP_Server = mail.example.com
+SMTP_Server_Port_Number = 25
+Install_Mail = 1
+[URL]
+Help_Page = http://www.ieaksample.net/helpdesk
+Home_Page = http://www.ieaksample.net
+Search_Page = http://www.ieaksample,net/search
+[Favorites]
+IEAK Sample \\ IEAK Sample Home Page.url = http://acme.ieaksample.net/
+[Branding]
+Window_Title = Internet Explorer from Acme Internet Services
+
+</pre>
diff --git a/httemplate/docs/index.html b/httemplate/docs/index.html
new file mode 100644
index 0000000..3b419de
--- /dev/null
+++ b/httemplate/docs/index.html
@@ -0,0 +1,32 @@
+<head>
+ <title>Freeside Documentation</title>
+</head>
+<body bgcolor="#ffffff">
+ <h1>Freeside Documentation</h1>
+<img src="overview-new.png">
+<h3>Installation and upgrades</h3>
+<ul>
+ <li><a href="http://www.sisd.com/mediawiki/index.php/Freeside:1.7:Documentation:Installation">New Installation</a>
+ <li><a href="http://www.sisd.com/mediawiki/index.php/Freeside:1.7:Documentation:RT_Installation">Installing integrated RT ticketing</a>
+ <li><a href="http://www.sisd.com/mediawiki/index.php/Freeside:1.7:Documentation:Self-Service_Installation">Signup/Self-service installation</a>
+ <li><a href="http://www.sisd.com/mediawiki/index.php/Freeside:1.7:Documentation:Upgrading">Upgrading from 1.5.8 or 1.6.X</a>
+</ul>
+<h3>Configuration and setup</h3>
+<ul>
+<!--
+ <li><a href="config.html">Configuration files</a>
+!-->
+ <li><a href="admin.html">Administration</a>
+<!--
+ <li><a href="../index.html#admin">Administration</a>
+!-->
+ <li><a href="http://www.sisd.com/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Exports_.28provisioning.29">Exports</a>
+ <li><a href="http://www.sisd.com/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Billing">Billing</a>
+</ul>
+<h3>Developer</h3>
+<ul>
+ <li><a href="schema.html">Schema reference</a>
+ <li><a href="man/FS.html">Perl API</a>
+ <li><a href="legacy.html">Importing legacy data</a>
+</ul>
+</body>
diff --git a/httemplate/docs/legacy.html b/httemplate/docs/legacy.html
new file mode 100755
index 0000000..94efe53
--- /dev/null
+++ b/httemplate/docs/legacy.html
@@ -0,0 +1,39 @@
+<head>
+ <title>Importing legacy data</title>
+</head>
+<body>
+ <h1>Importing legacy data</h1>
+<font size="+2">In almost all cases, legacy data import will require writing custom code to deal with your particular legacy data. The example scripts here will probably <b>not</b> work "out-of-the-box", and are provided <b>as a starting point only</b>.</font>
+<br><br><i>Some import scripts may require installation of the <a href="http://search.cpan.org/search?dist=Array-PrintCols">Array-PrintCols</a> and <a href="http://search.cpan.org/search?dist=Term-Query">Term-Query</a> (make test broken; install manually) modules.</i><br>
+<ul>
+ <li><a name="bind">bin/bind.import</a> - Import domain information from BIND named
+ <li><a name="passwd">bin/passwd.import</a> - Just import `passwd' and `shadow' or `master.passwd', no RADIUS import.
+ <li><a name="svc_acct">bin/svc_acct.import</a> - Import `passwd', ( `shadow' or `master.passwd' ) and RADIUS `users'. Before running bin/svc_acct.import, you need <a href="../browse/part_svc.cgi">services</a> (with table svc_acct) as follows:
+ <ul>
+ <li>Most accounts probably have entries in passwd and users (with Port-Limit nonexistant or 1)
+ <li>Some accounts have entries in passwd and users, but with Port-Limit 2 (or more)
+ <li>Some accounts might have entries in users only (Port-Limit 1)
+ <li>Some accounts might have entries in users only (Port-Limit >= 2)
+ <li>POP mail accounts have entries in passwd only, and have a particular shell.
+ <li>Everything else in passwd is a shell account.
+ </ul>
+<!-- <li><a name="svc_acct_sm">bin/svc_acct_sm.import</a> - Import qmail ( `virtualdomains' and `rcpthosts' ), or sendmail ( `virtusertable' and `sendmail.cw' ) files. Before running bin/svc_acct_sm.import, you need <a href="../browse/part_svc.cgi">services</a> as follows:
+ <ul>
+ <li>Domain (table svc_acct)
+ <li>Mail alias (table svc_acct_sm)
+ </ul>
+-->
+ <li><a name="cust_main">Importing customer data</a>
+ <ul>
+ <li>Manually
+ <ul>
+ <li>Add a <a href="../edit/cust_main.cgi">new customer</a>
+ <li>Add one or more packages for this customer
+ <li>Enter a package by clicking on the package number
+ <li>Pick the `Link to existing' option
+ </ul>
+ <li>Batch - You will need to write a script to import your particular legacy data. You can use eg/TEMPLATE_cust_main.import as a starting point.
+ </ul>
+</ul>
+</body>
+
diff --git a/httemplate/docs/license.html b/httemplate/docs/license.html
new file mode 100644
index 0000000..5453730
--- /dev/null
+++ b/httemplate/docs/license.html
@@ -0,0 +1,116 @@
+<% include('/elements/header-popup.html', 'Freeside') %>
+<CENTER>
+<IMG SRC="<%$fsurl%>images/small-logo.png" BORDER=0"><BR>
+<H3>version <% $FS::VERSION %></H3>
+</CENTER>
+
+<P>
+
+Copyright &copy; 2005-2008 Freeside Internet Services, Inc.<BR>
+Copyright &copy; 2000-2005 Ivan Kohler<BR>
+Copyright &copy; 1999 Silicon Interactive Software Design<BR>
+All rights reserved<BR>
+
+<P>
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU <B>Affero</B> General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or (at
+ your option) any later version.
+
+<P>
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+<P>
+ You should have received a copy of the GNU Affero General Public
+ License along with this program, in the file `<A HREF="AGPL.html" TARGET="_blank">AGPL</A>'; if not,
+ see &lt;<A HREF="http://www.fsf.org/licensing/licenses/agpl-3.0.html" TARGET="_blank"">http://www.fsf.org/licensing/licenses/agpl-3.0.html</a>&gt;.
+
+<P>
+ At your option, you may also redistribute and/or modify the files in the
+ fs_selfservice/ directory (but not the rest of the software) under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or (at your option) any later
+ version.
+
+<P>
+ At your option, you may also redistribute and/or modify the
+ fs_selfservice/php/freeside.class.php file (but not the rest of the
+ software) under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+<!--entire other packages-->
+
+<P>
+Contains "Request Tracker" <http://www.bestpractical.com/rt/> and
+"RTx::Extension::ActivityReports" from Best Practical Solutions, licensed under
+the terms of the GNU GPL, version two. Best Practical Solutions considers the
+Request Tracker software in this case to be "merely aggregated" with Freeside,
+and not a "combined work", and as such Request Tracker is distributed only
+under the original GPLv2 license.
+
+<!--important widgets or other "whole" bits-->
+
+<P>
+Latex invoice template based on a template from eBills
+<http://ebills.sourceforge.net/> by Mark Asplen-Taylor <mark@asplen.co.uk>,
+licensed under the terms fo the GNU GPL.
+
+<P>
+Contains "JS Calendar" <http://dynarch.com/mishoo/calendar.epl>
+by Mihai Bazon <mishoo@infoiasi.ro> licensed under the terms of the GNU LGPL.
+
+<P>
+Contains FCKeditor by Frederico Caldeira Knabben, licensed under the terms of
+the GNU GPL.
+
+<P>
+Contains XMenu <http://webfx.eae.net/dhtml/xmenu/xmenu.html>
+by Erik Arvidsson, licensed under the terms of the GNU GPL.
+
+<!--RT add-ons-->
+
+<P>
+Contains "RTx::Statistics Package"
+<http://wiki.bestpractical.com/view/RT3StatisticsPackage> from Kelly Hickel
+<kfh@mqsoftware.com>, licensed under the same terms as Perl (GPL/Artistic).
+
+<P>
+Contains "RTx::WebCronTool" <http://search.cpan.org/dist/RTx-WebCronTool/> from
+Audrey Tang, licensed under the same terms as Perl (GPL/Artistic).
+
+<!--libraries-->
+
+<P>
+Contains the QLIB JavaScript library <http://qlib.quazzle.com/> by
+Quazzle.com, Serge Dolgov, licensed under the terms of the GNU GPL.
+
+<P>
+Contains the overlibmws DHTML Popup Library <http://www.macridesweb.com/oltest/>
+by Foteos Macrides (derived from overLIB <http://www.bosrup.com/web/overlib/>
+by Erik Bosrup), licensed under the terms of the Artistic license
+<http://www.macridesweb.com/oltest/license.html>.
+
+<P>
+XMLHttpRequest implementation based on the SAJAX toolkit, licensed under the
+terms of the BSD license.<BR>
+&copy; 2005 modernmethod, inc<BR>
+Perl backend version &copy; 2005 Nathan Schmidt
+
+<!-- artwork -->
+
+<P>
+Contains public domain artwork from openclipart.org by mimooh and other
+authors.
+
+<P>
+Contains icons from
+<A HREF="http://famfamfam.com/" TARGET="_blank">famfamfam.com</A>
+by Mark James, licensed under the terms of the Creative Commons Attribution
+2.5 License.
+
+</BODY>
+</HTML>
diff --git a/httemplate/docs/man/FS/part_export/.cvs_is_on_crack b/httemplate/docs/man/FS/part_export/.cvs_is_on_crack
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/httemplate/docs/man/FS/part_export/.cvs_is_on_crack
diff --git a/httemplate/docs/overview-new.dia b/httemplate/docs/overview-new.dia
new file mode 100644
index 0000000..d9989a3
--- /dev/null
+++ b/httemplate/docs/overview-new.dia
Binary files differ
diff --git a/httemplate/docs/overview-new.png b/httemplate/docs/overview-new.png
new file mode 100644
index 0000000..bf81546
--- /dev/null
+++ b/httemplate/docs/overview-new.png
Binary files differ
diff --git a/httemplate/docs/overview.dia b/httemplate/docs/overview.dia
new file mode 100644
index 0000000..a0e34c3
--- /dev/null
+++ b/httemplate/docs/overview.dia
Binary files differ
diff --git a/httemplate/docs/overview.png b/httemplate/docs/overview.png
new file mode 100644
index 0000000..bf2dbc2
--- /dev/null
+++ b/httemplate/docs/overview.png
Binary files differ
diff --git a/httemplate/docs/passwd.html b/httemplate/docs/passwd.html
new file mode 100755
index 0000000..fc1dde9
--- /dev/null
+++ b/httemplate/docs/passwd.html
@@ -0,0 +1,23 @@
+<head>
+ <title>fs_passwd</title>
+</head>
+<body>
+ <h1>fs_passwd</h1>
+You may use fs_passwd/fs_passwd as a "passwd", "chfn" and "chsh" replacement on your shell machine(s) to cause password, gecos and shell changes to update your freeside machine. You can also use the fs_passwd/fs_passwd.html and fs_passwd/fs_passwd.cgi to run a public password change CGI on a public web server. This can pose a security risk if not configured correctly. <b>Do not use this feature unless you understand what you are doing!</b>
+<br><br>Currently it is assumed that the the crypt(3) function in the C library is the same on the Freeside machine as on the target machine.
+<ul>
+ <li>Create a freeside account on the shell or web machine(s).
+ <li>Setup SSH keys:
+ <ul>
+ <li>As the freeside user (on your freeside machine), generate an authentication key using <a href="http://www.tac.eu.org/cgi-bin/man-cgi?ssh-keygen+1">ssh-keygen</a>. Since this is for unattended operation, use a blank passphrase.
+ <li>Append the newly-created <code>identity.pub</code> file to <code>~freeside
+/.ssh/authorized_keys</code> on the shell or web machine(s).
+ <li>Some new SSH v2 implementation accept v2 style keys only. Use the <code>-t</code> option to <a href="http://www.tac.eu.org/cgi-bin/man-cgi?ssh-keygen+1">ssh-keygen</a>, and append the created <code>id_dsa.pub</code> or <code>id_rsa.pub</code> to <code>~freeside/.ssh/authorized_keys2</code> on the remote machine(s).
+ </ul>
+ <li>Copy fs_passwd/fs_passwdd to /usr/local/sbin on the shell or web machine(s). (chown freeside, chmod 500)
+ <li>Create /usr/local/freeside on the shell or web machine(s). (chown freeside, chmod 700)
+ <li>Run an iteration of "fs_passwd/fs_passwd_server <i>user</i> shell.machine" as the freeside user for each shell or web machine (this is a daemon process). <i>user</i> refers to a freeside user added by <a href="man/bin/freeside-adduser.html">freeside-adduser</a>.
+ <li>Copy fs_passwd/fs_passwd to /usr/local/bin on the shell machine(s). (chown freeside, chmod 4755). You may link it to passwd, chfn and chsh as well.
+ <li>Copy fs_passwd/fs_passwd.cgi to the cgi-bin directory on your web machine(s). Use <a href="http://www.apache.org/docs/suexec.html">suEXEC</a> or <a href="http://www.perldoc.com/perl5.6.1/pod/perlsec.html">suidperl</a> to run fs_passwd.cgi as the freeside user.
+</ul>
+</body>
diff --git a/httemplate/docs/schema.dia b/httemplate/docs/schema.dia
new file mode 100644
index 0000000..e00f59c
--- /dev/null
+++ b/httemplate/docs/schema.dia
Binary files differ
diff --git a/httemplate/docs/schema.html b/httemplate/docs/schema.html
new file mode 100644
index 0000000..cd4914a
--- /dev/null
+++ b/httemplate/docs/schema.html
@@ -0,0 +1,533 @@
+
+ <title>Schema reference</title>
+</head>
+<body>
+ <h1>Schema reference</h1>
+ Schema diagram (1.4.1): <a href="schema.png">as a giant .png</a> or <a href="schema.dia">dia source</a> (<a href="http://www.lysator.liu.se/~alla/dia/">dia homepage</a>).
+ <ul>
+ <li><a name="agent" href="man/FS/agent.html">agent</a> - Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their agent type).
+ <ul>
+ <li>agentnum - primary key
+ <li>agent - name of this agent
+ <li>typenum - <a href="#agent_type">agent type</a>
+ <li>prog - (unimplemented)
+ <li>freq - (unimplemented)
+ <li>disabled - Disabled flag, empty or 'Y'
+ <li>username - Username for the Agent interface
+ <li>_password - Password for the Agent interface
+ </ul>
+ <li><a name="agent_type" href="man/FS/agent_type.html">agent_type</a> - Agent types define groups of packages that you can then assign to particular agents.
+ <ul>
+ <li>typenum - primary key
+ <li>atype - name of this agent type
+ </ul>
+ <li><a name="cust_bill" href="man/FS/cust_bill.html">cust_bill</a> - Invoices. Declarations that a customer owes you money. The specific charges are itemized in <a href="#cust_bill_pkg">cust_bill_pkg</a>.
+ <ul>
+ <li>billpkgnum - primary_key
+ <li>invnum - primary key
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>_date
+ <li>charged - amount of this invoice
+ <li>printed - how many times this invoice has been printed automatically
+ <li>closed - books closed flag, empty or `Y'
+ </ul>
+ <li><a name="cust_bill_event" href="man/FS/cust_bill_event.html">cust_bill_event</a> - Invoice event history
+ <ul>
+ <li>eventnum - primary key
+ <li>invnum - <a href="#cust_bill">invoice</a>
+ <li>eventpart - <a href="#part_bill_event">event definition</a>
+ <li>_date
+ <li>status
+ <li>statustext
+ </ul>
+ <li><a name="part_bill_event" href="man/FS/part_bill_event.html">part_bill_event</a> - Invoice event definitions
+ <ul>
+ <li>eventpart - primary key
+ <li>payby - CARD, DCRD, CHEK, DCHK, LECB, BILL, or COMP
+ <li>event - event name
+ <li>eventcode - event action
+ <li>seconds - how long after the invoice date (<a href="#cust_bill">cust_bill</a>._date) events of this type are triggered
+ <li>weight - ordering for events with identical seconds
+ <li>plan - eventcode plan
+ <li>plandata - additional plan data
+ <li>disabled - Disabled flag, empty or `Y'
+ <li>taxclass - Texas tax class flag, empty or "none", "access", or "hosting"
+ </ul>
+ <li><a name="cust_bill_pkg" href="man/FS/cust_bill_pkg.html">cust_bill_pkg</a> - Invoice line items
+ <ul>
+ <li>invnum - (multiple) key
+ <li>pkgnum - <a href="#cust_pkg">package</a> or 0 for the special virtual sales tax package
+ <li>setup - setup fee
+ <li>recur - recurring fee
+ <li>sdate - starting date
+ <li>edate - ending date
+ <li>itemdesc - Line item description (currently used only when pkgnum is 0)
+ </ul>
+ <li><a name="cust_bill_pkg_detail" href="man/FS/cust_bill_pkg_detail.html">cust_bill_pkg_detail</a> - Invoice line items detail
+ <ul>
+ <li>detailnum - primary key
+ <li>pkgnum -
+ <li>invnum -
+ <li>detail - Detail description
+ </ul>
+ <li><a name="cust_credit" href="man/FS/cust_credit.html">cust_credit</a> - Credits. The equivalent of a negative <a href="#cust_bill">cust_bill</a> record.
+ <ul>
+ <li>crednum - primary key
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>amount - amount credited
+ <li>_date
+ <li>otaker - order taker
+ <li>reason
+ <li>closed - books closed flag, empty or `Y'
+ </ul>
+ <li><a name="cust_credit_bill" href="man/FS/cust_credit_bill.html">cust_credit_bill</a> - Credit invoice application. Links a credit to an invoice.
+ <ul>
+ <li>creditbillnum - primary key
+ <li>crednum - <a href="#cust_credit">credit</a> being applied
+ <li>invnum - <a href="#cust_bill">invoice</a> to which credit is applied
+ <li>amount - amount applied
+ <li>_date
+ </ul>
+ <li><a name="cust_pay_refund" href="man/FS/cust_pay_refund.html">cust_credit_bill</a> - Refund payment application. Links a refund to a payment.
+ <ul>
+ <li>payrefundnum - primary key
+ <li>paynum - <a href="#cust_pay">payment</a>
+ <li>refundnum - <a href="#cust_refund">refund</a>
+ <li>amount - amount applied
+ <li>_date
+ </ul>
+ <li><a name="cust_main" href="man/FS/cust_main.html">cust_main</a> - Customers
+ <ul>
+ <li>custnum - primary key
+ <li>agentnum - <a href="#agent">agent</a>
+ <li>refnum - <a href="#part_referral">referral</a>
+ <li>first - name
+ <li>last - name
+ <li>ss - social security number
+ <li>company
+ <li>address1
+ <li>address2
+ <li>city
+ <li>county
+ <li>state
+ <li>zip
+ <li>country
+ <li>daytime - phone
+ <li>night - phone
+ <li>fax - phone
+ <li><i>ship_first</i>
+ <li><i>ship_last</i>
+ <li><i>ship_company</i>
+ <li><i>ship_address1</i>
+ <li><i>ship_address2</i>
+ <li><i>ship_city</i>
+ <li><i>ship_county</i>
+ <li><i>ship_state</i>
+ <li><i>ship_zip</i>
+ <li><i>ship_country</i>
+ <li><i>ship_daytime</i>
+ <li><i>ship_night</i>
+ <li><i>ship_fax</i>
+ <li>payby - CARD, DCHK, CHEK, DCHK, LECB, BILL, or COMP
+ <li>payinfo - card number, P.O.#, or comp issuer
+ <li>paycvv - Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
+ <li>paydate - expiration date
+ <li>payname - billing name (name on card)
+ <li>tax - tax exempt, Y or null
+ <li>otaker - order taker
+ <li>referral_custnum
+ <li>comments
+ </ul>
+ (columns in <i>italics</i> are optional)
+ <li><a name="cust_main_invoice" href="man/FS/cust_main_invoice.html">cust_main_invoice</a> - Invoice destinations for email invoices. Note that a customer can have many email destinations for their invoice (either literal or via svcnum), but only one postal destination.
+ <ul>
+ <li>destnum - primary key
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>dest - Invoice destination. Freeside supports three types of invoice delivery: send directly to a service defined in Freeside, send to an arbitrary email address, or print the invoice to a printer and have someone send it out via snail mail. Freeside determines which method to use based on the contents of the dest field. If the contents are numeric, a <a href="#svc_acct">svcnum</a> pointing to a valid service is expected in the field. If the contents are a string, a literal email address is expected to be in the field. If the special keyword `POST' is present, the snail mail method is used (which is the default if no cust_main_invoice records exist). Snail mail invoices get their address information from <A name="#cust_main">cust_main</A> and are printed with the printer defined in the configuration files.
+ </ul>
+ <li><a name="cust_main_county" href="man/FS/cust_main_county.html">cust_main_county</a> - Tax rates
+ <ul>
+ <li>taxnum - primary key
+ <li>state
+ <li>county
+ <li>country
+ <li>tax - % rate
+ <li>taxclass
+ <li>exempt_amount
+ <li>taxname - if defined, printed on invoices instead of "Tax"
+ <li>setuptax - if 'Y', this tax does not apply to setup fees
+ <li>recurtax - if 'Y', this tax does not apply to recurring fees
+ </ul>
+ <li><a name="cust_tax_exempt" href="man/FS/cust_tax_exempt.html">cust_tax_exempt</a> - Tax exemption record
+ <ul>
+ <li>exemptnum - primary key
+ <li>taxnum - <a href="#cust_main_county">tax rate</a>
+ <li>year
+ <li>month
+ <li>amount
+ </ul>
+ <li><a name="cust_pay" href="man/FS/cust_pay.html">cust_pay</a> - Payments. Money being transferred from a customer.
+ <ul>
+ <li>paynum - primary key
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>paid - amount
+ <li>_date
+ <li>payby - CARD, CHEK, LECB, BILL, or COMP
+ <li>payinfo - card number, P.O.#, or comp issuer
+ <li>paybatch - text field for tracking card processor batches
+ <li>closed - books closed flag, empty or `Y'
+ </ul>
+ <li><a name="cust_pay-void" href="man/FS/cust_pay_void.html">cust_pay_void</a> - Voided payments.
+ <ul>
+ <li>paynum - primary key
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>paid - amount
+ <li>_date
+ <li>payby - CARD, CHEK, LECB, BILL, or COMP
+ <li>payinfo - card number, P.O.#, or comp issuer
+ <li>paybatch - text field for tracking card processor batches
+ <li>closed - books closed flag, empty or `Y'
+ <li>void_date
+ <li>reason
+ <li>otaker - order taker
+ </ul>
+ <li><a name="cust_bill_pay" href="man/FS/cust_bill_pay.html">cust_bill_pay</a> - Applicaton of a payment to a specific invoice.
+ <ul>
+ <li>billpaynum
+ <li>invnum - <a href="#cust_bill">invoice</a>
+ <li>paynum - <a href="#cust_pay">payment</a>
+ <li>amount
+ <li>_date
+ </ul>
+ <li><a name="pay_batch" href="man/FS/pay_batch.html">pay_batch</a> - Pending batch
+ <ul>
+ <li>batchnum
+ <li>status
+ <li>download
+ <li>upload
+ </ul>
+ <li><a name="cust_pay_batch" href="man/FS/cust_pay_batch.html">cust_pay_batch</a> - Pending batch members
+ <ul>
+ <li>paybatchnum
+ <li>batchnum
+ <li>payby - CARD, CHEK, LECB, BILL, or COMP
+ <li>payinfo - account number
+ <li>exp - card expiration
+ <li>amount
+ <li>invnum - <a href="#cust_bill">invoice</a>
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>payname - name on card
+ <li>first - name
+ <li>last - name
+ <li>address1
+ <li>address2
+ <li>city
+ <li>state
+ <li>zip
+ <li>country
+ <li>status
+ </ul>
+ <li><a name="cust_pkg" href="man/FS/cust_pkg.html">cust_pkg</a> - Customer billing items
+ <ul>
+ <li>pkgnum - primary key
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>pkgpart - <a href="#part_pkg">Package definition</a>
+ <li>setup - date
+ <li>bill - next bill date
+ <li>last_bill - last bill date
+ <li>susp - (past) suspension date
+ <li>expire - (future) cancellation date
+ <li>cancel - (past) cancellation date
+ <li>otaker - order taker
+ <li>manual_flag - If this field is set to 1, disables the automatic unsuspensiond of this package when using the <a href="config.html#unsuspendauto">unsuspendauto</a> config file.
+ </ul>
+ <li><a name="cust_refund" href="man/FS/cust_refund.html">cust_refund</a> - Refunds. The transfer of money to a customer; equivalent to a negative <a href="#cust_pay">cust_pay</a> record.
+ <ul>
+ <li>refundnum - primary key
+ <li>custnum - <a href="#cust_main">customer</a>
+ <li>refund - amount
+ <li>_date
+ <li>payby - CARD, CHEK, LECB, BILL or COMP
+ <li>payinfo - card number, P.O.#, or comp issuer
+ <li>otaker - order taker
+ <li>closed - books closed flag, empty or `Y'
+ </ul>
+ <li><a name="cust_credit_refund" href="man/FS/cust_credit_refund.html">cust_credit_refund</a> - Applicaton of a refund to a specific credit.
+ <ul>
+ <li>creditrefundnum - primary key
+ <li>crednum - <a href="#cust_credit">credit</a>
+ <li>refundnum - <a href="#cust_refund">refund</a>
+ <li>amount
+ <li>_date
+ </ul>
+ <li><a name="cust_svc" href="man/FS/cust_svc.html">cust_svc</a> - Customer services
+ <ul>
+ <li>svcnum - primary key
+ <li>pkgnum - <a href="#cust_pkg">package</a>
+ <li>svcpart - <a href="#part_svc">Service definition</a>
+ </ul>
+ <li><a name="nas" href="man/FS/nas.html">nas</a> - Network Access Server (terminal server)
+ <ul>
+ <li>nasnum - primary key
+ <li>nas - NAS name
+ <li>nasip - NAS ip address
+ <li>nasfqdn - NAS fully-qualified domain name
+ <li>last - timestamp indicating the last instant the NAS was in a known state (used by the session monitoring).
+ </ul>
+ <li><a name="part_pkg" href="man/FS/part_pkg.html">part_pkg</a> - Package definitions
+ <ul>
+ <li>pkgpart - primary key
+ <li>pkg - package name
+ <li>comment - non-customer visable package comment
+ <li>promo_code - promotional code
+ <li><i>deprecated</i> setup - setup fee expression
+ <li>freq - recurring frequency (months)
+ <li><i>deprecated</i> recur - recurring fee expression
+ <li>setuptax - Setup fee tax exempt flag, empty or `Y'
+ <li>recurtax - Recurring fee tax exempt flag, empty or `Y'
+ <li>plan - price plan
+ <li><i>deprecated</i> plandata - additional price plan data
+ <li>disabled - Disabled flag, empty or `Y'
+ </ul>
+ <li><a name="part_pkg_option" href="man/FS/part_pkg_option.html">part_pkg_option</a> - Package definition options
+ <ul>
+ <li>optionnum - primary key
+ <li>pkgpart - <a href="#part_pkg">Package definition</a>
+ <li>optionname - option name
+ <li>optionvalue - option value
+ </ul>
+ <li><a name="reg_code" href="man/FS/reg_code.html">reg_code</A> - One-time registration codes
+ <ul>
+ <li>codenum - primary key
+ <li>code
+ <li>agentnum - <a href="#agent">Agent</a>
+ </ul>
+ <li><a name="reg_code_pkg" href="man/FS/reg_code_pkg.html">reg_code_pkg</A> - Registration code link to package definitions
+ <ul>
+ <li>codepkgnum - primary key
+ <li>codenum - <a href="#reg_code">Registration code</a>
+ <li>pkgpart - <a href="#part_pkg">Package definition</a>
+ </ul>
+ <li><a name="part_referral" href="man/FS/part_referral.html">part_referral</a> - Referral listing
+ <ul>
+ <li>refnum - primary key
+ <li>referral - referral
+ </ul>
+ <li><a name="part_svc" href="man/FS/part_svc.html">part_svc</a> - Service definitions
+ <ul>
+ <li>svcpart - primary key
+ <li>svc - name of this service
+ <li>svcdb - table used for this service: svc_acct, svc_forward, svc_domain, svc_charge or svc_wo
+ <li>disabled - Disabled flag, empty or `Y'
+<!-- <li><i>table</i>__<i>field</i> - Default or fixed value for <i>field</i> in <i>table</i>
+ <li><i>table</i>__<i>field</i>_flag - null, D or F
+-->
+ </ul>
+ <li><a name="part_svc_column" href="man/FS/part_svc_column.html">part_svc_column</a>
+ <ul>
+ <li>columnnum - primary key
+ <li>svcpart - <a href="#part_svc">Service definition</a>
+ <li>columnname - column name in part_svc.svcdb table
+ <li>columnvalue - default or fixed value for the column
+ <li>columnflag - null, D or F
+ </ul>
+ <li><a name="pkg_svc" href="man/FS/pkg_svc.html">pkg_svc</a>
+ <ul>
+ <li>pkgsvcnum - primary key
+ <li>pkgpart - <a href="#part_pkg">Package definition</a>
+ <li>svcpart - <a href="#part_svc">Service definition</a>
+ <li>quantity - quantity of this service that this package includes
+ <li>primary_svc - blank or Y: primary service
+ </ul>
+ <li><a name="export_svc" href="man/FS/export_svc.html">export_svc</a>
+ <ul>
+ <li>exportsvcnum - primary key
+ <li>svcpart - <a href="#part_svc">Service definition</a>
+ <li>exportnum - <a href="#exportnum">Export</a>
+ </ul>
+ <li><a name="part_export" href="man/FS/part_export.html">part_export</a> - Export to external provisioning
+ <ul>
+ <li>exportnum - primary key
+ <li>machine - Machine name
+ <li>exporttype - Export type
+ <li>nodomain - blank or Y: usernames are exported to this service with no domain
+ </ul>
+ <li><a name="part_export_option" href="man/FS/part_export_option.html">part_export_option</a> - provisioning options
+ <ul>
+ <li>optionnum - primary key
+ <li>exportnum - <a href="#part_export">Export</a>
+ <li>optionname - option name
+ <li>optionvalue - option value
+ </ul>
+ <li><a name="port" href="man/FS/port.html">port</a> - individual port on a <a href="#nas">nas</a>
+ <ul>
+ <li>portnum - primary key
+ <li>ip - IP address of this port
+ <li>nasport - port number on the NAS
+ <li>nasnum - <a href="#nas">NAS</a>
+ </ul>
+ <li><a name="prepay_credit" href="man/FS/prepay_credit.html">prepay_credit</a> - prepaid cards
+ <ul>
+ <li>prepaynum - primary key
+ <li>identifier - text or numeric string of prepaid card
+ <li>amount - amount of prepayment
+ <li>seconds - prepaid time instead of (or in addition to) monetary value
+ <li>agentnum - optional agent assignment for prepaid cards
+ </ul>
+ <li><a name="session" href="man/FS/session.html">session</a>
+ <ul>
+ <li>sessionnum - primary key
+ <li>portnum - <a href="#port">Port</a>
+ <li>svcnum - <a href="#svc_acct">Account</a>
+ <li>login - timestamp indicating the beginning of this user session.
+ <li>logout - timestamp indicating the end of this user session. May be null, which indicates a currently open session.
+ </ul>
+
+ <li><a name="svc_acct" href="man/FS/svc_acct.html">svc_acct</a> - Accounts
+ <ul>
+ <li>svcnum - <a href="#cust_svc">primary key</a>
+ <li>username
+ <li>_password
+ <li>sec_phrase - security phrase
+ <li>popnum - <a href="#svc_acct_pop">Point of Presence</a>
+ <li>uid
+ <li>gid
+ <li>finger - GECOS
+ <li>dir
+ <li>shell
+ <li>quota - (unimplementd)
+ <li>slipip - IP address
+ <li>seconds
+ <li>domsvc
+ <li>radius_<i>Radius_Reply_Attribute</i> - Radius-Reply-Attribute
+ <li>rc_<i>Radius_Check_Attribute</i> - Radius-Check-Attribute
+ </ul>
+ <li><a name="svc_acct_pop" href="man/FS/svc_acct_pop.html">svc_acct_pop</a> - Points of Presence
+ <ul>
+ <li>popnum - primary key
+ <li>city
+ <li>state
+ <li>ac - area code
+ <li>exch - exchange
+ <li>loc - rest of number
+ </ul>
+ <li><a name="part_pop_local" href="man/FS/part_pop_local.html">part_pop_local</a> - Local calling areas
+ <ul>
+ <li>localnum - primary key
+ <li>popnum - primary key
+ <li>city
+ <li>state
+ <li>npa - area code
+ <li>nxx - exchange
+ </ul>
+ <li><a name="svc_domain" href="man/FS/svc_domain.html">svc_domain</a> - Domains
+ <ul>
+ <li>svcnum - <a href="#cust_svc">primary key</a>
+ <li>domain
+ </ul>
+ <li><a name="svc_forward" href="man/FS/svc_forward.html">svc_forward</a> - Mail forwarding aliases
+ <ul>
+ <li>svcnum - <a href="#cust_svc">primary key</a>
+ <li>srcsvc - <a href="#svc_acct">svcnum of the source of this forward</a>
+ <li>src - literal source (username or full email address)
+ <li>dstsvc - <a href="#svc_acct">svcnum of the destination of this forward</a>
+ <li>dst - literal destination (username or full email address)
+ </ul>
+ <li><a name="domain_record" href="man/FS/domain_record.html">domain_record</a> - Domain zone detail
+ <ul>
+ <li>recnum - primary key
+ <li>svcnum - <a href="#svc_domain">Domain</a> (by svcnum)
+ <li>reczone - zone for this line
+ <li>recaf - address family, usually <b>IN</b>
+ <li>rectype - type for this record (<b>A</b>, <b>MX</b>, etc.)
+ <li>recdata - data for this record
+ </ul>
+ <li><a name="svc_www" href="man/FS/svc_www.html">svc_www</a>
+ <ul>
+ <li>svcnum - <a href="#cust-svc">primary key</a>
+ <li>recnum - <a href="#domain_record">host</a>
+ <li>usersvc - <a href="#svc_acct">account</a>
+ </ul>
+ <li><a name="type_pkgs" href="man/FS/type_pkgs.html">type_pkgs</a>
+ <ul>
+ <li>typepkgnum - primary key
+ <li>typenum - <a href="#agent_type">agent type</a>
+ <li>pkgpart - <a href="#part_pkg">Package definition</a>
+ </ul>
+ <li><a name="queue" href="man/FS/queue.html">queue</a> - job queue
+ <ul>
+ <li>jobnum - primary key
+ <li>job
+ <li>_date
+ <li>status
+ <li>statustext
+ <li>svcnum
+ </ul>
+ <li><a name="queue_arg" href="man/FS/queue_arg.html">queue_arg</a> - job arguments
+ <ul>
+ <li>argnum - primary key
+ <li>jobnum - <a href="#queue">job</a>
+ <li>arg - argument
+ </ul>
+ <li><a name="queue_depend" href="man/FS/queue_depend.html">queue_depend</a> - job dependancies
+ <ul>
+ <li>dependnum - primary key
+ <li>jobnum - source jobnum
+ <li>depend_jobnum - dependancy jobnum
+ </ul>
+ <li><a name="radius_usergroup" href="man/FS/radius_usergroup.html">radius_usergroup</a> - Link users to RADIUS groups.
+ <ul>
+ <li>usergroupnum - primary key
+ <li>svcnum - <a href="#svc_acct">account</a>
+ <li>groupname
+ </ul>
+ <li><a name="rate" href="man/FS/rate.html">rate</a> - Call rate plans
+ <ul>
+ <li>ratenum - primary key
+ <li>ratename
+ </ul>
+ <li><a name="rate_detail" href="man/FS/rate_detail.html">rate_detail</a> - Call rate detail
+ <ul>
+ <li>ratedetailnum - primary key
+ <li>ratenum - <a href="#rate">rate plan</a>
+ <li>orig_regionnum - call origination <a href="#rate_region">region</a>
+ <li>dest_regionnum - call destination <a href="#rate_region">region</a>
+ <li>min_included - included minutes
+ <li>min_charge - charge per minute
+ <li>sec_granularity - granularity in seconds, i.e. 6 or 60
+ </ul>
+ <li><a name="rate_region" href="man/FS/rate_region.html">rate_region</a> - Call rate region
+ <ul>
+ <li>regionnum - primary key
+ <li>regionname
+ </ul>
+ <li><a name="rate_prefix" href="man/FS/rate_prefix.html">rate_prefix</a> - Call rate prefix
+ <ul>
+ <li>prefixnum - primary key
+ <li>regionnum - <a href="#rate_region">rate region</a>
+ <li>countrycode
+ <li>npa
+ <li>nxx
+ </ul>
+ <li><a name="msgcat" href="man/FS/msgcat.html">msgcat</a> - i18n message catalog
+ <ul>
+ <li>msgnum - primary key
+ <li>msgcode - message code
+ <li>locale - locale
+ <li>msg - Message text
+ </ul>
+ <li><a name="clientapi_session" href="man/FS/clientapi_session.html">clientapi_session</a> - ClientAPI session store
+ <ul>
+ <li>sessionnum - primary key
+ <li>sessionid - session ID
+ <li>namespace - session namespace
+ </ul>
+ <li><a name="clientapi_session_field" href="man/FS/clientapi_session_field.html">clientapi_session_field</a> - Client API session store data
+ <ul>
+ <li>fieldnum - primary key
+ <li>sessionnum - <a href="#session">session</a>
+ <li>fieldname
+ <li>fieldvalue
+ </ul>
+ </ul>
+</body>
diff --git a/httemplate/docs/schema.png b/httemplate/docs/schema.png
new file mode 100644
index 0000000..d0392e7
--- /dev/null
+++ b/httemplate/docs/schema.png
Binary files differ
diff --git a/httemplate/docs/session.html b/httemplate/docs/session.html
new file mode 100644
index 0000000..72e1642
--- /dev/null
+++ b/httemplate/docs/session.html
@@ -0,0 +1,59 @@
+<head>
+ <title>Session monitor</title>
+</head>
+<body>
+<h1>Session monitor</h1>
+<h2>Installation</h2>
+For security reasons, the client portion of the session montior may run on one
+or more external public machine(s). On these machines, install:
+<ul>
+ <li><a href="http://www.perl.com/CPAN/doc/relinfo/INSTALL.html">Perl</a> (at l
+east 5.004_05 for the 5.004 series or 5.005_03 for the 5.005 series. Don't enable experimental features like threads or the PerlIO abstraction layer.)
+ <li><a href="man/FS/SessionClient.html">FS::SessionClient</a> (copy the fs_session/FS-SessionClient directory to the external machine, then: perl Makefile.PL; make; make install)
+</ul>
+Then:
+<ul>
+ <li>Add the user `freeside' to the the external machine.
+ <li>Create the /usr/local/freeside directory on the external machine (owned by the freeside user).
+ <li>touch /usr/local/freeside/fs_sessiond_socket; chown freeside /usr/local/freeside/fs_sessiond_socket; chmod 600 /usr/local/freeside/fs_sessiond_socket
+ <li>Append the identity.pub from the freeside user on your freeside machine to the authorized_keys file of the newly created freeside user on the external machine(s).
+ <li>Run <pre>fs_session_server <i>user</i> <i>machine</i></pre> on the Freeside machine.
+ <ul>
+ <li><i>user</i> is a user from the mapsecrets file.
+ <li><i>machine</i> is the name of the external machine.
+ </ul>
+</ul>
+<h2>Usage</h2>
+<ul>
+ <li>Web
+ <ul>
+ <li>Copy FS-SessionClient/cgi/login.cgi and logout.cgi to your web
+ server's document space.
+ <li>Use <a href="http://www.apache.org/docs/suexec.html">suEXEC</a> or <a href="http://www.perl.com/CPAN-local/doc/manual/html/pod/perlsec.html#Security_Bugs">setuid</a> (see <a href="install.html">install.html</a> for details) to run login.cgi and logout.cgi as the freeside user.
+ </ul>
+ <li>Command-line
+ <br><pre>freeside-login username ( portnum | ip | nasnum nasport )
+freeside-logout username ( portnum | ip | nasnum nasport )</pre>
+ <ul>
+ <li><i>username</i> is a customer username from the svc_acct table
+ <li><i>portnum</i>, <i>ip</i> or <i>nasport</i> and <i>nasnum</i> uniquely identify a port in the <a href="schema.html#port">port</a> database table.
+ </ul>
+ <li>RADIUS - One of:
+ <ul>
+ <li>Run the <b>freeside-sqlradius-radacctd</b> daemon to import radacct
+ records from all configured sqlradius exports:
+ <tt>freeside-sqlradius-radacctd username</tt>
+ <li>Configure your RADIUS server's login and logout callbacks to use the command-line <tt>freeside-login</tt> and <tt>freeside-logout</tt> utilites.
+ <li> <i>(incomplete)</i>Use the <b>fs_radlog/fs_radlogd</b> tool to
+ import records from a text radacct file.
+ </ul>
+</ul>
+<h2>Callbacks</h2>
+<ul>
+ <li>Sesstion start - The command(s) specified in the <a href="config.html#session-start">session-start</a> configuration file are executed on the Freeside machine. The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.
+ <li>Session end - The command(s) specified in the <a href="config.html#session-stop">session-stop</a> configuration file are executed on the Freeside machine. The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.
+</ul>
+<h2>Dropping expired users</h2>
+Run <pre>bin/freeside-session-kill username</pre> periodically from cron.
+</body>
+</html>
diff --git a/httemplate/docs/signup.html b/httemplate/docs/signup.html
new file mode 100644
index 0000000..97d7aa7
--- /dev/null
+++ b/httemplate/docs/signup.html
@@ -0,0 +1,54 @@
+<head>
+ <title>Signup server</title>
+</head>
+<body>
+ <h1>Signup server</h1>
+For security reasons, the signup server should run on an external public
+webserver. On this machine, install:
+<ul>
+ <li>A web server, such as <a href="http://www.apache-ssl.org">Apache-SSL</a> or <a href="http://www.apache.org">Apache</a>
+ <li><a href="ftp://ftp.cs.hut.fi/pub/ssh/">SSH</a>
+ <li><a href="http://www.perl.com/CPAN/doc/relinfo/INSTALL.html">Perl</a> (at least 5.004_05 for the 5.004 series or 5.005_03 for the 5.005 series. Don't enable experimental features like threads or the PerlIO abstraction layer.)
+ <li><a href="http://search.cpan.org/search?dist=Text-Template">Text::Template</a>
+ <li><a href="http://search.cpan.org/search?dist=Storable">Storable</a>
+ <li><a href="http://search.cpan.org/search?dist=Business-CreditCard">Business-CreditCard</a>
+ <li><a href="http://search.cpan.org/search?dist=HTTP-BrowserDetect">HTTP::BrowserDetect</a>
+
+ <li><a href="man/FS/SignupClient.html">FS::SignupClient</a> (copy the fs_signup/FS-SignupClient directory to the external machine, then: perl Makefile.PL; make; make install)
+</ul>
+Then:
+<ul>
+ <li>Add the user `freeside' to the the external machine.
+ <li>Copy or symlink fs_signup/FS-SignupClient/cgi/signup.cgi into the web server's document space.
+ <li>When linking to signup.cgi, you can include a referring custnum in the URL as follows: <code>http://public.web.server/path/signup.cgi?ref=1542</code>
+ <li>Enable CGI execution for files with the `.cgi' extension. (with <a href="http://www.apache.org/docs/mod/mod_mime.html#addhandler">Apache</a>)
+ <li>Create the /usr/local/freeside directory on the external machine (owned by the freeside user).
+ <li>touch /usr/local/freeside/fs_signupd_socket; chown freeside /usr/local/freeside/fs_signupd_socket; chmod 600 /usr/local/freeside/fs_signupd_socket
+ <li>Use <a href="http://www.apache.org/docs/suexec.html">suEXEC</a> or <a href="http://www.perl.com/CPAN-local/doc/manual/html/pod/perlsec.html#Security_Bugs">setuid</a> (see <a href="install.html">install.html</a> for details) to run signup.cgi as the freeside user.
+ <li>Append the identity.pub from the freeside user on your freeside machine to the authorized_keys file of the newly created freeside user on the external machine(s).
+ <li>Run <pre>fs_signup_server <i>user</i> <i>machine</i> <i>agentnum</i> <i>refnum</i></pre> on the Freeside machine.
+ <ul>
+ <li><i>user</i> is a user from the mapsecrets file.
+ <li><i>machine</i> is the name of the external machine.
+ <li><i>agentnum</i> and <i>refnum</i> are the <a href="schema.html#agent">agent</a> and <a href="schema.html#part_referral">referral</a>, respectively, to use for customers who sign up via this signup server.
+ </ul>
+</ul>
+Optional:
+<ul>
+ <li>If you create a <b>/usr/local/freeside/ieak.template</b> file on the external machine, it will be sent to IE users with MIME type <i>application/x-Internet-signup</i>. This file will be processed with <a href="http://search.cpan.org/doc/MJD/Text-Template-1.23/Template.pm">Text::Template</a> with the variables listed below available.
+ (an example file is included as <b>fs_signup/ieak.template</b>) See the section on <a href="http://www.microsoft.com/windows/ieak/techinfo/deploy/60/en/INS.HTM">internet settings files</a> in the <a href="http://www.microsoft.com/windows/ieak/techinfo/deploy/60/en/toc.asp">IEAK documentation</a> for more information.
+ <li>If you create a <b>/usr/local/freeside/success.html</b> file on the external machine, it will be used as the success HTML page. Although template substiutions are available, a regular HTML file will work fine here, unlike signup.html. An example file is included as <b>fs_signup/FS-SignupClient/cgi/success.html</b>
+ <li>Variable substitutions available in <b>ieak.template</b>, <b>cck.template</b> and <b>success.html</b>:
+ <ul>
+ <li>$ac - area code of selected POP
+ <li>$exch - exchange of selected POP
+ <li>$loc - local part of selected POP
+ <li>$username
+ <li>$password
+ <li>$email_name - first and last name
+ <li>$pkg - package name
+ </ul>
+ <li>If you create a <b>/usr/local/freeside/signup.html</b> file on the external machine, it will be used as a template for the form HTML. This requires the template to be constructed appropriately; probably best to start with the example file included as <b>fs_signup/FS-SignupClient/cgi/signup.html</b>.
+ <li>If there are any entries in the <i>prepay_credit</i> table, a user can enter a string matching the <b>identifier</i> column to receive the credit specified in the <b>amount</b> column, and/or the time specified in the <b>seconds</b> column (for use with the <a href="session.html">session monitor</a>), after which that <b>identifier</b> is no longer valid. This can be used to implement pre-paid "calling card" type signups. The <i>bin/generate-prepay</i> script can be used to populate the <i>prepay_credit</i> table.
+</ul>
+</body>
diff --git a/httemplate/docs/ssh.html b/httemplate/docs/ssh.html
new file mode 100755
index 0000000..d2c501e
--- /dev/null
+++ b/httemplate/docs/ssh.html
@@ -0,0 +1,16 @@
+<head>
+ <title>Unattended SSH</title>
+</head>
+<body>
+ <h1>Unattended SSH</h1>
+ <br><a name=ssh>Unattended remote login</a> - Freeside can login to remote machines unattended using SSH. This can pose a security risk if not configured correctly, and will allow an intruder who breaks into your freeside machine full access to your remote machines. <b>Do not use this feature unless you understand what you are doing!</b>
+ <ul>
+ <li>As the freeside user (on your freeside machine), generate an authentication key using <a href="http://www.tac.eu.org/cgi-bin/man-cgi?ssh-keygen+1">ssh-keygen</a>. Since this is for unattended operation, use a blank passphrase.
+ <li>Append the newly-created <code>identity.pub</code> file to <code>~root/.ssh/authorized_keys</code> (or the appopriate <code>~username/.ssh/authorized_keys</code>) on the remote machine(s).
+ <li>Some new SSH v2 implementation accept v2 style keys only. Use the <code>-t</code> option to <a href="http://www.tac.eu.org/cgi-bin/man-cgi?ssh-keygen+1">ssh-keygen</a>, and append the created <code>id_dsa.pub</code> or <code>id_rsa.pub</code> to <code>~root/.ssh/authorized_keys2</code> (or the appopriate <code>~username/.ssh/authorized_keys</code>) on the remote machine(s).
+ <li>You may need to set <code>PermitRootLogin without-password</code> (meaning with keys only) in your <code>sshd_config</code> file on the remote machine(s).
+ <li>You may want to set <code>ForwardX11 = no</code> in <code>~root/.ssh/config</code> to prevent spurious errors if your distribution turns on X11 forwarding by default.
+ </ul>
+
+</body>
+
diff --git a/httemplate/edit/REAL_cust_pkg.cgi b/httemplate/edit/REAL_cust_pkg.cgi
new file mode 100755
index 0000000..b2c89c3
--- /dev/null
+++ b/httemplate/edit/REAL_cust_pkg.cgi
@@ -0,0 +1,186 @@
+<% include("/elements/header.html",'Customer package - Edit dates') %>
+
+%#, menubar(
+%# "View this customer (#$custnum)" => popurl(2). "view/cust_main.cgi?$custnum",
+%#));
+
+<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+<SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
+
+<FORM NAME="formname" ACTION="process/REAL_cust_pkg.cgi" METHOD="POST">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+
+% # raw error from below
+% if ( $error ) {
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error %></FONT>
+% }
+% #or, regular error handler
+<% include('/elements/error.html') %>
+
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TD ALIGN="right">Package number</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_pkg->pkgnum %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Package</TD>
+ <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Comment</TD>
+ <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Order taker</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_pkg->otaker %></TD>
+ </TR>
+
+ <& .row_edit, cust_pkg=>$cust_pkg, column=>'setup', label=>'Setup' &>
+ <& .row_edit, cust_pkg=>$cust_pkg, column=>'last_bill', label=>$last_bill_or_renewed &>
+ <& .row_edit, cust_pkg=>$cust_pkg, column=>'bill', label=>$next_bill_or_prepaid_until &>
+ <& .row_display, cust_pkg=>$cust_pkg, column=>'adjourn', label=>'Adjournment', note=>'(will <b>suspend</b> this package when the date is reached)' &>
+ <& .row_display, cust_pkg=>$cust_pkg, column=>'susp', label=>'Suspension' &>
+
+ <& .row_display, cust_pkg=>$cust_pkg, column=>'expire', label=>'Expiration', note=>'(will <b>cancel</b> this package when the date is reached)' &>
+ <& .row_display, cust_pkg=>$cust_pkg, column=>'cancel', label=>'Cancellation' &>
+
+<%def .row_edit>
+<%args>
+ $cust_pkg
+ $column
+ $label
+ $note => ''
+</%args>
+% my $value = $cust_pkg->get($column);
+% $value = $value ? time2str($format, $value) : "";
+
+ <TR>
+ <TD ALIGN="right"><% $label %> date</TD>
+ <TD>
+ <INPUT TYPE = "text"
+ NAME = "<% $column %>"
+ SIZE = 32
+ ID = "<% $column %>_text"
+ VALUE = "<% $value %>"
+ >
+ <IMG SRC = "../images/calendar.png"
+ ID = "<% $column %>_button"
+ STYLE = "cursor: pointer"
+ TITLE = "Select date"
+ >
+% if ( $note ) {
+ <BR><FONT SIZE=-1><% $note %></FONT>
+% }
+ </TD>
+ </TR>
+
+ <SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "<% $column %>_text",
+ ifFormat: "%m/%d/%Y",
+ button: "<% $column %>_button",
+ align: "BR"
+ });
+ </SCRIPT>
+
+</%def>
+
+<%def .row_display>
+<%args>
+ $cust_pkg
+ $column
+ $label
+ $note => ''
+</%args>
+% if ( $cust_pkg->get($column) ) {
+ <TR>
+ <TD ALIGN="right"><% $label %> date</TD>
+ <TD BGCOLOR="#ffffff"><% time2str($format,$cust_pkg->get($column)) %>
+% if ( $note ) {
+ <BR><FONT SIZE=-1><% $note %></FONT>
+% }
+ </TD>
+ </TR>
+% }
+</%def>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Apply Changes">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+#my $format = "%c %z (%Z)";
+my $format = "%m/%d/%Y %T %z (%Z)";
+
+#false laziness w/view/cust_main/packages.html
+#my( $billed_or_prepaid,
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit customer package dates');
+
+my $error = '';
+my( $pkgnum, $cust_pkg );
+
+if ( $cgi->param('error') ) {
+
+ $pkgnum = $cgi->param('pkgnum');
+ if ( $cgi->param('error') eq '_bill_areyousure' ) {
+ if ( $cgi->param('bill') =~ /^([\s\d\/\:\-\(\w\)]*)$/ ) {
+ my $bill = $1;
+ $cgi->param('error', '');
+ $error = "You are attempting to set the next bill date to $bill, which is
+ in the past. This will charge the customer for the interval
+ from $bill until now. Are you sure you want to do this? ".
+ '<INPUT TYPE="checkbox" NAME="bill_areyousure" VALUE="1">';
+ }
+ }
+
+ #get package record
+ $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ die "No package!" unless $cust_pkg;
+
+ foreach my $col (qw( setup last_bill bill adjourn expire )) {
+ my $value = $cgi->param($col);
+ $cust_pkg->set( $col, $value ? str2time($value) : '' );
+ }
+
+} else {
+
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "no pkgnum";
+ $pkgnum = $1;
+
+ #get package record
+ $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ die "No package!" unless $cust_pkg;
+
+}
+
+my $part_pkg = qsearchs( 'part_pkg', { 'pkgpart' => $cust_pkg->pkgpart } );
+
+my( $last_bill_or_renewed, $next_bill_or_prepaid_until );
+unless ( $part_pkg->is_prepaid ) {
+ #$billed_or_prepaid = 'billed';
+ $last_bill_or_renewed = 'Last bill';
+ $next_bill_or_prepaid_until = 'Next bill';
+} else {
+ #$billed_or_prepaid = 'prepaid';
+ $last_bill_or_renewed = 'Renewed';
+ $next_bill_or_prepaid_until = 'Prepaid until';
+}
+
+</%init>
diff --git a/httemplate/edit/access_group.html b/httemplate/edit/access_group.html
new file mode 100644
index 0000000..1eed26d
--- /dev/null
+++ b/httemplate/edit/access_group.html
@@ -0,0 +1,80 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Employee Group',
+ 'table' => 'access_group',
+ 'labels' => {
+ 'groupnum' => 'Group number',
+ 'groupname' => 'Group name',
+ },
+
+ 'viewall_dir' => 'browse',
+
+ 'html_bottom' => $html_bottom_sub,
+ )
+%>
+<%once>
+
+tie my %rights, 'Tie::IxHash', FS::AccessRight->rights_info;
+
+</%once>
+<%init>
+
+my $html_bottom_sub = sub {
+ my $access_group = shift;
+
+ #some false laziness w/browse/access_group.html
+ my $columns = 3;
+ my $count = 0;
+
+ '<BR>'.
+ '<FONT SIZE="+1">Group limited to these agent(s)</FONT><BR>'.
+ 'Employees in this group will only see customers of the selected agents in the system and reports.<BR>'.
+ ntable("#cccccc",2).
+ '<TR><TD>'.
+ include( '/elements/checkboxes-table.html',
+ 'source_obj' => $access_group,
+ 'link_table' => 'access_groupagent',
+ 'target_table' => 'agent',
+ 'name_col' => 'agent',
+ 'target_link' => $p.'edit/agent.cgi?',
+ 'disable-able' => 1,
+ ).
+ '</TD></TR></TABLE>'.
+
+ '<BR><FONT SIZE="+1">Group access rights</FONT><BR>'.
+ include('/elements/table-grid.html', bgcolor=>'#cccccc' ).
+ '<TR>'. join( '', map {
+ '<TD CLASS="inv" VALIGN="top"><TABLE BGCOLOR="#cccccc" WIDTH=100%>'.
+ '<TR><TH BGCOLOR="#dcdcdc">'. $_. '</TH></TR>'.
+ '<TR><TD>'.
+ include( '/elements/checkboxes-table-name.html',
+ 'source_obj' => $access_group,
+ 'link_table' => 'access_right',
+ 'link_static' => { 'righttype' =>
+ 'FS::access_group',
+ },
+ 'num_col' => 'rightobjnum',
+ 'name_col' => 'rightname',
+ 'names_list' => [ map {
+ my $rn =
+ ref($_) ? $_->{'rightname'} : $_;
+ my %hash = ();
+ $hash{'note'} = '&nbsp;*'
+ if ref($_) && $_->{'global'};
+ $hash{'desc'} = $_->{'desc'}
+ if ref($_) && $_->{'desc'};
+ [ $rn => \%hash ];
+ }
+ @{ $rights{$_} }
+ ],
+ ).
+ '<BR>'.
+ '</TD></TR></TABLE></TD>'.
+ ( ++$count % $columns ? '' : '</TR><TR>')
+
+ } keys %rights ). '</TR></TABLE>'.
+
+ '* Global rights. These rights provide access to global data which is shared among all agents. Their use is not recommended for groups which are limited to a subset of agents.<BR>';
+
+};
+
+</%init>
diff --git a/httemplate/edit/access_user.html b/httemplate/edit/access_user.html
new file mode 100644
index 0000000..73488ef
--- /dev/null
+++ b/httemplate/edit/access_user.html
@@ -0,0 +1,50 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Employee',
+ 'table' => 'access_user',
+ 'fields' => [
+ 'username',
+ { field=>'_password', type=>'password' },
+ { field=>'_password2', type=>'password' },
+ 'last',
+ 'first',
+ { field=>'disabled', type=>'checkbox', value=>'Y' },
+ ],
+ 'labels' => {
+ 'usernum' => 'User number',
+ 'username' => 'Username',
+ '_password' => 'Password',
+ '_password2'=> 'Re-enter Password',
+ 'last' => 'Last name',
+ 'first' => 'First name',
+ 'disabled' => 'Disable employee',
+ },
+ 'edit_callback' => sub { my( $c, $o ) = @_;
+ $o->set('_password', '');
+ },
+ 'viewall_dir' => 'browse',
+ 'html_bottom' =>
+ sub {
+ my $access_user = shift;
+
+ '<BR>Employee Groups<BR>'.
+ ntable("#cccccc",2).
+ '<TR><TD>'.
+ include( '/elements/checkboxes-table.html',
+ 'source_obj' => $access_user,
+ 'link_table' => 'access_usergroup',
+ 'target_table' => 'access_group',
+ 'name_col' => 'groupname',
+ 'target_link' => $p.'edit/access_group.html?',
+ #'disable-able' => 1,
+ ).
+ '</TR></TD></TABLE>'
+ ;
+ },
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/agent.cgi b/httemplate/edit/agent.cgi
new file mode 100755
index 0000000..215542d
--- /dev/null
+++ b/httemplate/edit/agent.cgi
@@ -0,0 +1,123 @@
+<% include("/elements/header.html","$action Agent", menubar(
+ 'View all agents' => $p. 'browse/agent.cgi',
+)) %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%popurl(1)%>process/agent.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>">
+Agent #<% $agent->agentnum ? $agent->agentnum : "(NEW)" %>
+
+<% &ntable("#cccccc", 2, '') %>
+
+ <TR>
+ <TH ALIGN="right">Agent</TH>
+ <TD><INPUT TYPE="text" NAME="agent" SIZE=32 VALUE="<% $agent->agent %>"></TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">Agent type</TH>
+ <TD>
+ <SELECT NAME="typenum" SIZE=1>
+% foreach my $agent_type (qsearch('agent_type',{})) {
+
+ <OPTION VALUE="<% $agent_type->typenum %>"<% ( $agent->typenum && ( $agent->typenum == $agent_type->typenum ) ) ? ' SELECTED' : '' %>>
+ <% $agent_type->getfield('typenum') %>: <% $agent_type->getfield('atype') %>
+% }
+
+ </SELECT>
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">Master customer</TH>
+ <TD>
+ <% include('/elements/search-cust_main.html',
+ 'field_name' => 'agent_custnum',
+ 'curr_value' => $agent->agent_custnum,
+ 'find_button' => 1,
+ )
+ %>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Disable</TD>
+ <TD><INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $agent->disabled eq 'Y' ? ' CHECKED' : '' %>></TD>
+ </TR>
+
+ <% include('/elements/tr-select-invoice_template.html',
+ 'label' => 'Invoice template',
+ 'field' => 'invoice_template',
+ 'curr_value' => $agent->invoice_template,
+ )
+ %>
+
+% if ( $conf->config('ticket_system') ) {
+% my $default_queueid = $conf->config('ticket_system-default_queueid');
+% my $default_queue = FS::TicketSystem->queue($default_queueid);
+% $default_queue = "(default) $default_queueid: $default_queue"
+% if $default_queueid;
+% my %queues = FS::TicketSystem->queues();
+% my @queueids = sort { $a <=> $b } keys %queues;
+%
+
+ <TR>
+ <TD ALIGN="right">Ticketing queue</TD>
+ <TD>
+ <SELECT NAME="ticketing_queueid">
+ <OPTION VALUE=""><% $default_queue %>
+% foreach my $queueid ( @queueids ) {
+
+ <OPTION VALUE="<% $queueid %>" <% $agent->ticketing_queueid == $queueid ? ' SELECTED' : '' %>><% $queueid %>: <% $queues{$queueid} %>
+% }
+
+ </SELECT>
+ </TD>
+ </TR>
+% }
+
+ <TR>
+ <TD ALIGN="right">Access Groups</TD>
+ <TD><% include('/elements/checkboxes-table.html',
+ 'source_obj' => $agent,
+ 'link_table' => 'access_groupagent',
+ 'target_table' => 'access_group',
+ 'name_col' => 'groupname',
+ 'target_link' => $p. 'edit/access_group.html?',
+ )
+ %>
+ </TD>
+ </TR>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="<% $agent->agentnum ? "Apply changes" : "Add agent" %>">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $agent;
+if ( $cgi->param('error') ) {
+ $agent = new FS::agent ( {
+ map { $_, scalar($cgi->param($_)) } fields('agent')
+ } );
+} elsif ( $cgi->keywords ) {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $agent = qsearchs( 'agent', { 'agentnum' => $1 } );
+} else { #adding
+ $agent = new FS::agent {};
+}
+my $action = $agent->agentnum ? 'Edit' : 'Add';
+
+my $conf = new FS::Conf;
+
+</%init>
diff --git a/httemplate/edit/agent_payment_gateway.html b/httemplate/edit/agent_payment_gateway.html
new file mode 100644
index 0000000..4a7cedf
--- /dev/null
+++ b/httemplate/edit/agent_payment_gateway.html
@@ -0,0 +1,68 @@
+<% include("/elements/header.html","$action payment gateway override for ". $agent->agent, menubar(
+ #'View all payment gateways' => $p. 'browse/payment_gateway.html',
+ 'View all agents' => $p. 'browse/agent.html',
+)) %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%popurl(1)%>process/agent_payment_gateway.html" METHOD=POST>
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>">
+
+Use gateway <SELECT NAME="gatewaynum">
+% foreach my $payment_gateway (
+% qsearch('payment_gateway', { 'disabled' => '' } )
+% ) {
+%
+
+ <OPTION VALUE="<% $payment_gateway->gatewaynum %>"><% $payment_gateway->gateway_module %> (<% $payment_gateway->gateway_username %>)
+% }
+
+</SELECT>
+<BR><BR>
+
+for <SELECT NAME="cardtype" MULTIPLE>
+% foreach my $cardtype (
+% "",
+% "VISA card",
+% "MasterCard",
+% "Discover card",
+% "American Express card",
+% "Diner's Club/Carte Blanche",
+% "enRoute",
+% "JCB",
+% "BankCard",
+% "Switch",
+% "Solo",
+% 'ACH',
+%) {
+
+ <OPTION VALUE="<% $cardtype %>"><% $cardtype || '(Default fallback)' %>
+% }
+
+</SELECT>
+<BR><BR>
+
+(optional) when invoice contains only items of taxclass <INPUT TYPE="text" NAME="taxclass">
+<BR><BR>
+
+<INPUT TYPE="submit" VALUE="Add gateway override">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('agentnum') =~ /(\d+)$/ or die "illegal agentnum";
+my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+die "agentnum $1 not found" unless $agent;
+
+#my @agent_payment_gateway;
+if ( $cgi->param('error') ) {
+}
+
+my $action = 'Add';
+
+</%init>
diff --git a/httemplate/edit/agent_type.cgi b/httemplate/edit/agent_type.cgi
new file mode 100755
index 0000000..abf4bf8
--- /dev/null
+++ b/httemplate/edit/agent_type.cgi
@@ -0,0 +1,57 @@
+<% include("/elements/header.html","$action Agent Type", menubar(
+ 'View all agent types' => "${p}browse/agent_type.cgi",
+))
+%>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% popurl(1) %>process/agent_type.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="typenum" VALUE="<% $agent_type->typenum %>">
+Agent Type #<% $agent_type->typenum || "(NEW)" %>
+<BR>
+
+Agent Type
+<INPUT TYPE="text" NAME="atype" SIZE=32 VALUE="<% $agent_type->atype %>">
+<BR><BR>
+
+Select which packages agents of this type may sell to customers<BR>
+<% ntable("#cccccc", 2) %><TR><TD>
+<% include('/elements/checkboxes-table.html',
+ 'source_obj' => $agent_type,
+ 'link_table' => 'type_pkgs',
+ 'target_table' => 'part_pkg',
+ 'name_callback' => sub { $_[0]->pkg. ' - '. $_[0]->comment; },
+ 'target_link' => $p.'edit/part_pkg.cgi?',
+ 'disable-able' => 1,
+
+ )
+%>
+</TD></TR></TABLE>
+<BR>
+
+<INPUT TYPE="submit" VALUE="<% $agent_type->typenum ? "Apply changes" : "Add agent type" %>">
+
+ </FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my($agent_type);
+if ( $cgi->param('error') ) {
+ $agent_type = new FS::agent_type ( {
+ map { $_, scalar($cgi->param($_)) } fields('agent')
+ } );
+} elsif ( $cgi->keywords ) { #editing
+ my( $query ) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $agent_type=qsearchs('agent_type',{'typenum'=>$1});
+} else { #adding
+ $agent_type = new FS::agent_type {};
+}
+my $action = $agent_type->typenum ? 'Edit' : 'Add';
+
+</%init>
diff --git a/httemplate/edit/allocate.html b/httemplate/edit/allocate.html
new file mode 100644
index 0000000..8d1347d
--- /dev/null
+++ b/httemplate/edit/allocate.html
@@ -0,0 +1,33 @@
+<% include('elements/edit.html',
+ 'name' => 'Allocation',
+ 'table' => 'addr_block',
+ 'labels' => { 'NetAddr' => 'Block',
+ 'routernum' => 'Router',
+ },
+ 'fields' => [ { 'field' => 'NetAddr',
+ 'type' => 'fixed',
+ },
+ { 'field' => 'routernum',
+ 'type' => 'select-table',
+ 'table' => 'router',
+ 'name_col' => 'routername',
+ 'disable_empty' => 1,
+ 'agent_virt' => 1,
+ 'agent_null_right' =>
+ 'Broadband global configuration',
+ },
+ ],
+ 'post_url' => "process/addr_block/allocate.cgi",
+ 'popup' => 1,
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Broadband global configuration',
+ )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+</%init>
diff --git a/httemplate/edit/bulk-cust_main_county.html b/httemplate/edit/bulk-cust_main_county.html
new file mode 100644
index 0000000..bb57fc5
--- /dev/null
+++ b/httemplate/edit/bulk-cust_main_county.html
@@ -0,0 +1,130 @@
+<% include('/elements/header-popup.html', 'Bulk Tax rate') %>
+
+<FORM ACTION="<% popurl(1)."process/bulk-cust_main_county.html" %>" METHOD="POST">
+
+<INPUT TYPE="hidden" NAME="taxnum" VALUE="<% join(',', @taxnum) %>">
+
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<% include('/elements/tr-td-label.html', 'label' => 'Country' ) %>
+ <TD BGCOLOR="#dddddd"><% $countries %>
+ </TD>
+</TR>
+
+<% include('/elements/tr-td-label.html', 'label' => 'State' ) %>
+ <TD BGCOLOR="#dddddd"><% $states %>
+ </TD>
+</TR>
+
+% if ( $counties ) {
+ <% include('/elements/tr-td-label.html', 'label' => 'County' ) %>
+ <TD BGCOLOR="#dddddd"><% $counties %>
+ </TD>
+ </TR>
+% }
+
+% if ( $conf->exists('enable_taxclasses') && $taxclasses ) {
+ <% include('/elements/tr-td-label.html', 'label' => 'Tax Class' ) %>
+ <TD BGCOLOR="#dddddd"><% $taxclasses %>
+ </TD>
+ </TR>
+% }
+
+<% include('/elements/tr-input-text.html',
+ 'field' => 'taxname',
+ 'label' => 'Tax name'
+ )
+%>
+
+<% include('/elements/tr-input-percentage.html',
+ 'field' => 'tax',
+ 'label' => 'Tax rate',
+ )
+%>
+
+<% include('/elements/tablebreak-tr-title.html', value=>'Exemptions' ) %>
+
+<% include('/elements/tr-checkbox.html',
+ 'field' => 'setuptax',
+ 'value' => 'Y',
+ 'label' => 'This tax not applicable to setup fees',
+ )
+%>
+
+<% include('/elements/tr-checkbox.html',
+ 'field' => 'recurtax',
+ 'value' => 'Y',
+ 'label' => 'This tax not applicable to recurring fees',
+ )
+%>
+
+<% include('/elements/tr-input-money.html',
+ 'field' => 'exempt_amount',
+ 'label' => 'Monthly exemption per customer ($25 "Texas tax")',
+ )
+%>
+
+</TABLE>
+
+<BR>
+
+<INPUT TYPE="submit" VALUE="Bulk add tax">
+
+<%init>
+
+my $conf = new FS::Conf;
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @taxnum;
+if ( $cgi->param('error') ) {
+ $cgi->param('taxnum') =~ /^([\d,]+)$/
+ or die "no taxnum, but error: ". $cgi->param('error');
+ @taxnum = split(',', $1);
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^([\d,]+)$/
+ or die 'Nothing selected'; #XXX better error when nothing selected
+ @taxnum = split(',', $1);
+}
+
+my @cust_main_county =
+ map {
+ qsearchs('cust_main_county', { 'taxnum' => $_ })
+ or die "unknown taxnum $1";
+ }
+ @taxnum;
+
+my %seen_country = {};
+my @countries = map code2country($_)."&nbsp;($_)",
+ grep !$seen_country{$_}++,
+ map $_->country,
+ @cust_main_county;
+my $countries = join(', ', @countries);
+
+my %seen_state = {};
+my @states = map state_label($_->[0], $_->[1]),
+ grep !$seen_state{$_->[0]}++,
+ map [ $_->state, $_->country ],
+ @cust_main_county;
+my $states = join(', ', @states);
+
+my %seen_county = {};
+my @counties = grep !$seen_county{$_}++, map $_->county, @cust_main_county;
+my $counties = join(', ', @counties);
+
+my %seen_taxclass = {};
+my @taxclasses = grep !$seen_taxclass{$_}++, map $_->taxclass, @cust_main_county;
+my $taxclasses = join(', ', @taxclasses);
+
+#my @fields = (
+# { field=>'country', type=>'fixed-country', },
+# { field=>'state', type=>'fixed-state', },
+# { field=>'county', type=>'fixed', },
+#);
+
+#push @fields, { field=>'taxclass', type=>'fixed', }
+# if $conf->exists('enable_taxclasses');
+
+</%init>
diff --git a/httemplate/edit/bulk-cust_svc.html b/httemplate/edit/bulk-cust_svc.html
new file mode 100644
index 0000000..a3c21b1
--- /dev/null
+++ b/httemplate/edit/bulk-cust_svc.html
@@ -0,0 +1,95 @@
+<% include('/elements/header.html', 'Bulk customer service change') %>
+
+<% include('/elements/init_overlib.html') %>
+
+<% include('/elements/progress-init.html',
+ 'OneTrueForm',
+ [qw( old_svcpart new_svcpart pkgpart )],
+ 'process/bulk-cust_svc.cgi',
+ $p.'browse/part_svc.cgi',
+ )
+%>
+
+<FORM NAME="OneTrueForm">
+%
+% $cgi->param('svcpart') =~ /^(\d+)$/
+% or die "illegal svcpart: ". $cgi->param('svcpart');
+%
+% my $old_svcpart = $1;
+% my $src_part_svc = qsearchs('part_svc', { 'svcpart' => $old_svcpart } )
+% or die "unknown svcpart: $old_svcpart";
+%
+
+
+<INPUT NAME="old_svcpart" TYPE="hidden" VALUE="<% $old_svcpart %>">
+Change <!-- customer
+<B><% $src_part_svc->svcpart %>: <% $src_part_svc->svc %></B> services
+<BR>
+-->
+
+<SELECT NAME="pkgpart">
+% my $num_cust_svc = $src_part_svc->num_cust_svc;
+% if ( $num_cust_svc > 1 ) {
+
+ <OPTION VALUE="">all <% $num_cust_svc %> <% $src_part_svc->svc %> services
+% } else {
+
+ <OPTION VALUE="">the <% $num_cust_svc %> <% $src_part_svc->svc %> service
+% }
+%
+% my $num_unlinked = $src_part_svc->num_cust_svc(0);
+% if ( $num_unlinked ) {
+%
+
+ <OPTION VALUE="0">the <% $num_unlinked %> unlinked <% $src_part_svc->svc %> services
+% }
+% foreach my $schwartz (
+% grep { $_->[1] }
+% map { [ $_, $src_part_svc->num_cust_svc($_->pkgpart) ] }
+% qsearch('part_pkg', {} )
+% ) {
+% my( $part_pkg, $num_cust_svc ) = @$schwartz;
+%
+
+ <OPTION VALUE="<% $part_pkg->pkgpart %>">the <% $num_cust_svc %>
+ <% $src_part_svc->svc %> service<% $num_cust_svc > 1 ? 's in' : ' in a' %>
+ <% $part_pkg->pkg %> package<% $num_cust_svc > 1 ? 's' : '' %>
+% }
+
+</SELECT>
+<BR>
+
+to new service definition
+<SELECT NAME="new_svcpart">
+% foreach my $dest_part_svc (
+% grep { $_->svcpart != $old_svcpart
+% && $_->svcdb eq $src_part_svc->svcdb
+% }
+% qsearch('part_svc', { 'disabled' => '' } )
+% ) {
+%
+
+ <OPTION VALUE="<% $dest_part_svc->svcpart %>"><% $dest_part_svc->svcpart %>: <% $dest_part_svc->svc %>
+% }
+
+</SELECT>
+<BR>
+
+<BR>
+
+<SCRIPT TYPE="text/javascript">
+var confirm_change = '<P ALIGN="center"><B>Bulk customer service change - Are you sure?</B><BR><P ALIGN="CENTER" <INPUT TYPE="button" VALUE="Yes, make changes" onClick="process();">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT TYPE="BUTTON" VALUE="Cancel" onClick="cClick()">';
+</SCRIPT>
+
+<INPUT TYPE="button" VALUE="Bulk change customer services" onClick="overlib(confirm_change, CAPTION, 'Confirm bulk customer service change', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 128, TEXTSIZE, 3, BGCOLOR, '#ff0000', CGCOLOR, '#ff0000' );">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/cust_bill_pay.cgi b/httemplate/edit/cust_bill_pay.cgi
new file mode 100755
index 0000000..532db6a
--- /dev/null
+++ b/httemplate/edit/cust_bill_pay.cgi
@@ -0,0 +1,14 @@
+<% include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_bill_pay.cgi',
+ 'src_table' => 'cust_pay',
+ 'src_thing' => 'payment',
+ 'dst_table' => 'cust_bill',
+ 'dst_thing' => 'invoice',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply payment');
+
+</%init>
diff --git a/httemplate/edit/cust_credit.cgi b/httemplate/edit/cust_credit.cgi
new file mode 100755
index 0000000..c9ca31f
--- /dev/null
+++ b/httemplate/edit/cust_credit.cgi
@@ -0,0 +1,68 @@
+<% include('/elements/header-popup.html', 'Enter Credit') %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="credit_popup" ACTION="<% $p1 %>process/cust_credit.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="crednum" VALUE="">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum |h %>">
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="">
+<INPUT TYPE="hidden" NAME="_date" VALUE="<% $_date %>">
+<INPUT TYPE="hidden" NAME="credited" VALUE="">
+<INPUT TYPE="hidden" NAME="otaker" VALUE="<% $otaker %>">
+
+Credit
+<% ntable("#cccccc", 2) %>
+
+ <TR>
+ <TD ALIGN="right">Date</TD>
+ <TD BGCOLOR="#ffffff"><% time2str("%D",$_date) %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#ffffff">$<INPUT TYPE="text" NAME="amount" VALUE="<% $amount |h %>" SIZE=8 MAXLENGTH=8></TD>
+ </TR>
+
+%
+%#print qq! <INPUT TYPE="checkbox" NAME="refund" VALUE="$refund">Also post refund!;
+%
+
+<% include( '/elements/tr-select-reason.html',
+ 'field' => 'reasonnum',
+ 'reason_class' => 'R',
+ 'control_button' => "document.getElementById('confirm_credit_button')",
+ 'cgi' => $cgi,
+ )
+%>
+
+ <TR>
+ <TD ALIGN="right">Auto-apply<BR>to invoices</TD>
+ <TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED>yes<OPTION>no</SELECT></TD>
+ </TR>
+
+</TABLE>
+
+<BR>
+
+<CENTER><INPUT TYPE="submit" ID="confirm_credit_button" VALUE="Enter credit" DISABLED></CENTER>
+
+</FORM>
+</BODY>
+</HTML>
+<%once>
+
+my $conf = new FS::Conf;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Post credit');
+
+my $custnum = $cgi->param('custnum');
+my $amount = $cgi->param('amount');
+my $_date = time;
+my $otaker = getotaker;
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/cust_credit_bill.cgi b/httemplate/edit/cust_credit_bill.cgi
new file mode 100755
index 0000000..e3627ff
--- /dev/null
+++ b/httemplate/edit/cust_credit_bill.cgi
@@ -0,0 +1,14 @@
+<% include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_credit_bill.cgi',
+ 'src_table' => 'cust_credit',
+ 'src_thing' => 'credit',
+ 'dst_table' => 'cust_bill',
+ 'dst_thing' => 'invoice',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply credit');
+
+</%init>
diff --git a/httemplate/edit/cust_credit_refund.cgi b/httemplate/edit/cust_credit_refund.cgi
new file mode 100755
index 0000000..f5bbb56
--- /dev/null
+++ b/httemplate/edit/cust_credit_refund.cgi
@@ -0,0 +1,14 @@
+<% include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_credit_refund.cgi',
+ 'src_table' => 'cust_credit',
+ 'src_thing' => 'credit',
+ 'dst_table' => 'cust_refund',
+ 'dst_thing' => 'refund',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply credit');
+
+</%init>
diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi
new file mode 100755
index 0000000..d3004f1
--- /dev/null
+++ b/httemplate/edit/cust_main.cgi
@@ -0,0 +1,780 @@
+<% include('/elements/header.html',
+ "Customer $action",
+ '',
+ ' onUnload="myclose()"'
+) %>
+
+<% include('/elements/init_overlib.html') %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="topform" STYLE="margin-bottom: 0">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+% if ( $custnum ) {
+ Customer #<B><% $cust_main->display_custnum %></B> -
+ <B><FONT COLOR="#<% $cust_main->statuscolor %>">
+ <% ucfirst($cust_main->status) %>
+ </FONT></B>
+ <BR><BR>
+% }
+
+<% &ntable("#cccccc") %>
+
+%# agent
+<% include('/elements/tr-select-agent.html',
+ 'curr_value' => $cust_main->agentnum,
+ 'label' => "<B>${r}Agent</B>",
+ 'empty_label' => 'Select agent',
+ 'disable_empty' => ( $cust_main->agentnum ? 1 : 0 ),
+ )
+%>
+
+%# agent_custid
+% if ( $conf->exists('cust_main-edit_agent_custid') ) {
+
+ <TR>
+ <TD ALIGN="right">Customer identifier</TD>
+ <TD><INPUT TYPE="text" NAME="agent_custid" VALUE="<% $cust_main->agent_custid %>"></TD>
+ </TR>
+
+% } else {
+
+ <INPUT TYPE="hidden" NAME="agent_custid" VALUE="<% $cust_main->agent_custid %>">
+
+% }
+
+%# referral (advertising source)
+%my $refnum = $cust_main->refnum || $conf->config('referraldefault') || 0;
+%if ( $custnum && ! $conf->exists('editreferrals') ) {
+
+ <INPUT TYPE="hidden" NAME="refnum" VALUE="<% $refnum %>">
+
+% } else {
+
+ <% include('/elements/tr-select-part_referral.html',
+ 'curr_value' => $refnum
+ )
+ %>
+% }
+
+
+%# referring customer
+%my $referring_cust_main = '';
+%if ( $cust_main->referral_custnum
+% and $referring_cust_main =
+% qsearchs('cust_main', { custnum => $cust_main->referral_custnum } )
+%) {
+
+ <TR>
+ <TD ALIGN="right">Referring customer</TD>
+ <TD>
+ <A HREF="<% popurl(1) %>/cust_main.cgi?<% $cust_main->referral_custnum %>"><% $cust_main->referral_custnum %>: <% $referring_cust_main->name %></A>
+ </TD>
+ </TR>
+ <INPUT TYPE="hidden" NAME="referral_custnum" VALUE="<% $cust_main->referral_custnum %>">
+% } elsif ( ! $conf->exists('disable_customer_referrals') ) {
+
+
+ <TR>
+ <TD ALIGN="right">Referring customer</TD>
+ <TD>
+ <!-- <INPUT TYPE="text" NAME="referral_custnum" VALUE=""> -->
+ <% include('/elements/search-cust_main.html',
+ 'field_name' => 'referral_custnum',
+ )
+ %>
+ </TD>
+ </TR>
+% } else {
+
+
+ <INPUT TYPE="hidden" NAME="referral_custnum" VALUE="">
+% }
+
+
+</TABLE>
+
+<!-- birthdate -->
+
+% if ( $conf->exists('cust_main-enable_birthdate') ) {
+
+ <BR>
+ <% ntable("#cccccc", 2) %>
+ <% include ('/elements/tr-input-date-field.html',
+ 'birthdate',
+ $cust_main->birthdate,
+ 'Date of Birth',
+ $conf->config('date_format') || "%m/%d/%Y",
+ 1)
+ %>
+
+ </TABLE>
+
+% }
+
+<!-- contact info -->
+
+% my $same_checked = '';
+% my $ship_disabled = '';
+% unless ( $cust_main->ship_last && $same ne 'Y' ) {
+% $same_checked = 'CHECKED';
+% $ship_disabled = 'DISABLED STYLE="background-color: #dddddd"';
+% foreach (
+% qw( last first company address1 address2 city county state zip country
+% daytime night fax )
+% ) {
+% $cust_main->set("ship_$_", $cust_main->get($_) );
+% }
+% }
+
+<BR><BR>
+Billing address
+<% include('cust_main/contact.html',
+ 'cust_main' => $cust_main,
+ 'pre' => '',
+ 'onchange' => 'bill_changed(this)',
+ 'disabled' => '',
+ 'ss' => $ss,
+ 'stateid' => $stateid,
+ 'same_checked' => $same_checked, #for address2 "Unit #" labeling
+ )
+%>
+
+<SCRIPT>
+function bill_changed(what) {
+ if ( what.form.same.checked ) {
+% for (qw( last first company address1 address2 city zip daytime night fax )) {
+
+ what.form.ship_<%$_%>.value = what.form.<%$_%>.value;
+% }
+
+ what.form.ship_country.selectedIndex = what.form.country.selectedIndex;
+
+ function fix_ship_county() {
+ what.form.ship_county.selectedIndex = what.form.county.selectedIndex;
+ }
+
+ function fix_ship_state() {
+ what.form.ship_state.selectedIndex = what.form.state.selectedIndex;
+ ship_state_changed(what.form.ship_state, fix_ship_county );
+ }
+
+ ship_country_changed(what.form.ship_country, fix_ship_state );
+
+ }
+}
+function samechanged(what) {
+ if ( what.checked ) {
+ bill_changed(what);
+
+% for (qw( last first company address1 address2 city county state zip country daytime night fax )) {
+ what.form.ship_<%$_%>.disabled = true;
+ what.form.ship_<%$_%>.style.backgroundColor = '#dddddd';
+% }
+
+% if ( $conf->exists('cust_main-require_address2') ) {
+ document.getElementById('address2_required').style.visibility = '';
+ document.getElementById('address2_label').style.visibility = '';
+ document.getElementById('ship_address2_required').style.visibility = 'hidden';
+ document.getElementById('ship_address2_label').style.visibility = 'hidden';
+% }
+
+ } else {
+
+% for (qw( last first company address1 address2 city county state zip country daytime night fax )) {
+ what.form.ship_<%$_%>.disabled = false;
+ what.form.ship_<%$_%>.style.backgroundColor = '#ffffff';
+% }
+
+% if ( $conf->exists('cust_main-require_address2') ) {
+ document.getElementById('address2_required').style.visibility = 'hidden';
+ document.getElementById('address2_label').style.visibility = 'hidden';
+ document.getElementById('ship_address2_required').style.visibility = '';
+ document.getElementById('ship_address2_label').style.visibility = '';
+% }
+
+ }
+}
+</SCRIPT>
+
+<BR>
+Service address
+(<INPUT TYPE="checkbox" NAME="same" VALUE="Y" onClick="samechanged(this)" <%$same_checked%>>same as billing address)
+<% include('cust_main/contact.html',
+ 'cust_main' => $cust_main,
+ 'pre' => 'ship_',
+ 'onchange' => '',
+ 'disabled' => $ship_disabled,
+ )
+%>
+
+
+<!-- billing info -->
+
+<% include( 'cust_main/billing.html', $cust_main,
+ 'payinfo' => $payinfo,
+ 'invoicing_list' => \@invoicing_list,
+ )
+%>
+
+<% include( '/elements/xmlhttp.html',
+ 'url' => $p.'misc/xmlhttp-cust_main-address_standardize.html',
+ 'subs' => [ 'address_standardize' ],
+ #'method' => 'POST', #could get too long?
+ )
+%>
+
+<SCRIPT>
+function bottomfixup(what) {
+
+ //i don't think we need to copy things between two forms anymore, modern
+ //browsers are fine with DIVs inside FORMs
+
+ var topvars = new Array(
+ 'birthdate',
+
+ 'custnum', 'agentnum', 'agent_custid', 'refnum', 'referral_custnum',
+
+ 'last', 'first', 'ss', 'company',
+ 'address1', 'address2', 'city',
+ 'county', 'state', 'zip', 'country',
+ 'daytime', 'night', 'fax',
+ 'stateid', 'stateid_state',
+
+ 'same',
+
+ 'ship_last', 'ship_first', 'ship_company',
+ 'ship_address1', 'ship_address2', 'ship_city',
+ 'ship_county', 'ship_state', 'ship_zip', 'ship_country',
+ 'ship_daytime','ship_night', 'ship_fax',
+
+ 'geocode',
+
+ 'select' // XXX key
+ );
+
+ var layervars = new Array(
+ 'payauto',
+ 'payinfo', 'payinfo1', 'payinfo2', 'paytype',
+ 'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv',
+ 'paystart_month', 'paystart_year', 'payissue',
+ 'payip',
+ 'paid'
+ );
+
+ var billing_bottomvars = new Array(
+ 'tax',
+ 'invoicing_list', 'invoicing_list_POST', 'invoicing_list_FAX',
+ 'invoice_terms',
+ 'spool_cdr',
+ 'squelch_cdr'
+ );
+
+ for ( f=0; f < topvars.length; f++ ) {
+ var field = topvars[f];
+ copyelement( document.topform.elements[field],
+ document.bottomform.elements[field]
+ );
+ }
+
+ var layerform = document.topform.select.options[document.topform.select.selectedIndex].value;
+ for ( f=0; f < layervars.length; f++ ) {
+ var field = layervars[f];
+ copyelement( document.forms[layerform].elements[field],
+ document.bottomform.elements[field]
+ );
+ }
+
+ for ( f=0; f < billing_bottomvars.length; f++ ) {
+ var field = billing_bottomvars[f];
+ copyelement( document.billing_bottomform.elements[field],
+ document.bottomform.elements[field]
+ );
+ }
+
+ //this part does USPS address correction
+
+ // XXX should this be first and should we update the form fields that are
+ // displayed???
+
+ //var state_el = document.bottomform.elements['state'];
+
+ //address_standardize(
+ var cust_main = new Array(
+ 'company', document.bottomform.elements['company'].value,
+ 'address1', document.bottomform.elements['address1'].value,
+ 'address2', document.bottomform.elements['address2'].value,
+ 'city', document.bottomform.elements['city'].value,
+ 'state', document.bottomform.elements['state'].value,
+ //'state', state_el.options[ state_el.selectedIndex ].value,
+ 'zip', document.bottomform.elements['zip'].value,
+
+ 'ship_company', document.bottomform.elements['ship_company'].value,
+ 'ship_address1', document.bottomform.elements['ship_address1'].value,
+ 'ship_address2', document.bottomform.elements['ship_address2'].value,
+ 'ship_city', document.bottomform.elements['ship_city'].value,
+ 'ship_state', document.bottomform.elements['ship_state'].value,
+ //'ship_state', state_el.options[ state_el.selectedIndex ].value,
+ 'ship_zip', document.bottomform.elements['ship_zip'].value
+ );
+
+ address_standardize( cust_main, update_address );
+
+}
+
+var standardize_address;
+
+function update_address(arg) {
+
+ var argsHash = eval('(' + arg + ')');
+
+ var changed = argsHash['address_standardized'];
+ var ship_changed = argsHash['ship_address_standardized'];
+ var error = argsHash['error'];
+ var ship_error = argsHash['ship_error'];
+
+ //yay closures
+ standardize_address = function () {
+
+ if ( changed ) {
+ document.bottomform.elements['company'].value = argsHash['new_company'];
+ document.bottomform.elements['address1'].value = argsHash['new_address1'];
+ document.bottomform.elements['address2'].value = argsHash['new_address2'];
+ document.bottomform.elements['city'].value = argsHash['new_city'];
+ document.bottomform.elements['state'].value = argsHash['new_state'];
+ //'state', state_el.options[ state_el.selectedIndex ].value,
+ document.bottomform.elements['zip'].value = argsHash['new_zip'];
+ }
+
+ if ( ship_changed ) {
+ document.bottomform.elements['ship_company'].value = argsHash['new_ship_company'];
+ document.bottomform.elements['ship_address1'].value = argsHash['new_ship_address1'];
+ document.bottomform.elements['ship_address2'].value = argsHash['new_ship_address2'];
+ document.bottomform.elements['ship_city'].value = argsHash['new_ship_city'];
+ document.bottomform.elements['ship_state'].value = argsHash['new_ship_state'];
+ //'state', state_el.options[ state_el.selectedIndex ].value,
+ document.bottomform.elements['ship_zip'].value = argsHash['new_ship_zip'];
+ }
+
+ }
+
+% if ( $conf->exists('enable_taxproducts') ) {
+
+ if ( <% $taxpre %>error ) {
+
+ if ( document.bottomform.elements['country'].value == 'CA' ||
+ document.bottomform.elements['country'].value == 'US'
+ )
+ {
+
+ var url = "cust_main/choose_tax_location.html?data_vendor=cch-zip;city="+document.bottomform.elements['city'].value+";state="+document.bottomform.elements['state'].value+";zip="+document.bottomform.elements['zip'].value+";country="+document.bottomform.elements['country'].value+";";
+ // popup a chooser
+ OLgetAJAX( url, update_geocode, 300 );
+
+ } else {
+
+ document.bottomform.elements['geocode'].value = 'DEFAULT';
+ document.bottomform.submit();
+
+ }
+
+ } else
+
+% }
+
+ if ( changed || ship_changed ) {
+
+% if ( $conf->exists('cust_main-auto_standardize_address') ) {
+
+ standardize_address();
+ document.bottomform.submit();
+
+% } else {
+
+ // popup a confirmation popup
+
+ var confirm_change =
+ '<CENTER><BR><B>Confirm address standardization</B><BR><BR>' +
+ '<TABLE>';
+
+ if ( changed ) {
+
+ confirm_change = confirm_change +
+ '<TR><TH>Entered billing address</TH>' +
+ '<TH>Standardized billing address</TH></TR>';
+ // + '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ if ( argsHash['company'] || argsHash['new_company'] ) {
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['company'] +
+ '</TD><TD>' + argsHash['new_company'] + '</TD></TR>';
+ }
+
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['address1'] +
+ '</TD><TD>' + argsHash['new_address1'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['address2'] +
+ '</TD><TD>' + argsHash['new_address2'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['city'] + ', ' + argsHash['state'] + ' ' + argsHash['zip'] +
+ '</TD><TD>' + argsHash['new_city'] + ', ' + argsHash['new_state'] + ' ' + argsHash['new_zip'] + '</TD></TR>' +
+ '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ }
+
+ if ( ship_changed ) {
+
+ confirm_change = confirm_change +
+ '<TR><TH>Entered service address</TH>' +
+ '<TH>Standardized service address</TH></TR>';
+ // + '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ if ( argsHash['ship_company'] || argsHash['new_ship_company'] ) {
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['ship_company'] +
+ '</TD><TD>' + argsHash['new_ship_company'] + '</TD></TR>';
+ }
+
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['ship_address1'] +
+ '</TD><TD>' + argsHash['new_ship_address1'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['ship_address2'] +
+ '</TD><TD>' + argsHash['new_ship_address2'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['ship_city'] + ', ' + argsHash['ship_state'] + ' ' + argsHash['ship_zip'] +
+ '</TD><TD>' + argsHash['new_ship_city'] + ', ' + argsHash['new_ship_state'] + ' ' + argsHash['new_ship_zip'] + '</TD></TR>' +
+ '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ }
+
+ var addresses = 'address';
+ var height = 268;
+ if ( changed && ship_changed ) {
+ addresses = 'addresses';
+ height = 396; // #what
+ }
+
+ confirm_change = confirm_change +
+ '<TR><TD>' +
+ '<BUTTON TYPE="button" onClick="document.bottomform.submit();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' +
+ '</TD><TD>' +
+ '<BUTTON TYPE="button" onClick="standardize_address(); document.bottomform.submit();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' +
+ '</TD></TR>' +
+ '<TR><TD COLSPAN=2 ALIGN="center">' +
+ '<BUTTON TYPE="button" onClick="document.bottomform.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' +
+
+ '</TABLE></CENTER>';
+
+ overlib( confirm_change, CAPTION, 'Confirm address standardization', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, height, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 );
+
+% }
+
+ } else {
+
+ document.bottomform.submit();
+
+ }
+
+}
+
+function update_geocode() {
+
+ //yay closures
+ set_geocode = function (what) {
+
+ //alert(what.options[what.selectedIndex].value);
+ var argsHash = eval('(' + what.options[what.selectedIndex].value + ')');
+ document.bottomform.elements['city'].value = argsHash['city'];
+ document.bottomform.elements['state'].value = argsHash['state'];
+ document.bottomform.elements['zip'].value = argsHash['zip'];
+ document.bottomform.elements['geocode'].value = argsHash['geocode'];
+
+ }
+
+ // popup a chooser
+
+ overlib( OLresponseAJAX, CAPTION, 'Select tax location', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 );
+
+}
+
+function copyelement(from, to) {
+ if ( from == undefined ) {
+ to.value = '';
+ } else if ( from.type == 'select-one' ) {
+ to.value = from.options[from.selectedIndex].value;
+ //alert(from + " (" + from.type + "): " + to.name + " => (" + from.selectedIndex + ") " + to.value);
+ } else if ( from.type == 'checkbox' ) {
+ if ( from.checked ) {
+ to.value = from.value;
+ } else {
+ to.value = '';
+ }
+ } else {
+ if ( from.value == undefined ) {
+ to.value = '';
+ } else {
+ to.value = from.value;
+ }
+ }
+ //alert(from + " (" + from.type + "): " + to.name + " => " + to.value);
+}
+
+</SCRIPT>
+
+<FORM ACTION="<% popurl(1) %>process/cust_main.cgi" METHOD=POST NAME="bottomform" STYLE="margin-top: 0; margin-bottom: 0">
+% foreach my $hidden (
+% 'birthdate',
+%
+% 'custnum', 'agentnum', 'agent_custid', 'refnum', 'referral_custnum',
+% 'last', 'first', 'ss', 'company',
+% 'address1', 'address2', 'city',
+% 'county', 'state', 'zip', 'country',
+% 'daytime', 'night', 'fax',
+% 'stateid', 'stateid_state',
+%
+% 'same',
+%
+% 'ship_last', 'ship_first', 'ship_company',
+% 'ship_address1', 'ship_address2', 'ship_city',
+% 'ship_county', 'ship_state', 'ship_zip', 'ship_country',
+% 'ship_daytime','ship_night', 'ship_fax',
+%
+% 'geocode',
+%
+% 'select', #XXX key
+%
+% 'payauto',
+% 'payinfo', 'payinfo1', 'payinfo2', 'paytype',
+% 'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv',
+% 'paystart_month', 'paystart_year', 'payissue',
+% 'payip',
+% 'paid',
+%
+% 'tax',
+% 'invoicing_list', 'invoicing_list_POST', 'invoicing_list_FAX',
+% 'invoice_terms',
+% 'spool_cdr',
+% 'squelch_cdr'
+% ) {
+%
+
+ <INPUT TYPE="hidden" NAME="<% $hidden %>" VALUE="">
+% }
+%
+% my $ro_comments = $conf->exists('cust_main-use_comments')?'':'readonly';
+% if (!$ro_comments || $cust_main->comments) {
+
+<BR>Comments
+<% &ntable("#cccccc") %>
+ <TR>
+ <TD>
+ <TEXTAREA COLS=80 ROWS=5 WRAP="HARD" NAME="comments" <%$ro_comments%>><% $cust_main->comments %></TEXTAREA>
+ </TD>
+ </TR>
+</TABLE>
+%
+% }
+%
+%unless ( $custnum ) {
+% # pry the wrong place for this logic. also pretty expensive
+% #use FS::part_pkg;
+%
+% #false laziness, copied from FS::cust_pkg::order
+% my $pkgpart;
+% my $agentnum = '';
+% my @agents = $FS::CurrentUser::CurrentUser->agents;
+% if ( scalar(@agents) == 1 ) {
+% # $pkgpart->{PKGPART} is true iff $custnum may purchase PKGPART
+% $pkgpart = $agents[0]->pkgpart_hashref;
+% $agentnum = $agents[0]->agentnum;
+% } else {
+% #can't know (agent not chosen), so, allow all
+% $agentnum = 'all';
+% my %typenum;
+% foreach my $agent ( @agents ) {
+% next if $typenum{$agent->typenum}++;
+% $pkgpart->{$_}++ foreach keys %{ $agent->pkgpart_hashref }
+% }
+% }
+% #eslaf
+%
+% my @part_pkg = grep { $_->svcpart('svc_acct')
+% && ( $pkgpart->{ $_->pkgpart }
+% || $agentnum eq 'all'
+% || ( $agentnum ne 'all'
+% && $agentnum
+% && $_->agentnum
+% && $_->agentnum == $agentnum
+% )
+% )
+% }
+% qsearch( 'part_pkg', { 'disabled' => '' }, '', 'ORDER BY pkg' ); # case?
+%
+% if ( @part_pkg ) {
+%
+% # print "<BR><BR>First package", &itable("#cccccc", "0 ALIGN=LEFT"),
+% #apiabuse & undesirable wrapping
+%
+%
+
+ <BR>First package
+ <% ntable("#cccccc") %>
+
+ <TR>
+ <TD COLSPAN=2>
+ <% include('cust_main/select-domain.html',
+ 'pkgparts' => \@part_pkg,
+ 'saved_pkgpart' => $saved_pkgpart,
+ 'saved_domsvc' => $saved_domsvc,
+ )
+ %>
+ </TD>
+ </TR>
+%
+% #false laziness: (mostly) copied from edit/svc_acct.cgi
+% #$ulen = $svc_acct->dbdef_table->column('username')->length;
+% my $ulen = dbdef->table('svc_acct')->column('username')->length;
+% my $ulen2 = $ulen+2;
+% my $passwordmax = $conf->config('passwordmax') || 8;
+% my $pmax2 = $passwordmax + 2;
+%
+
+
+ <TR>
+ <TD ALIGN="right">Username</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<% $username %>" SIZE=<% $ulen2 %> MAXLENGTH=<% $ulen %>>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Domain</TD>
+ <TD>
+ <SELECT NAME="domsvc">
+ <OPTION>(none)</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Password</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $passwordmax %>>
+ (blank to generate)
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Access number</TD>
+ <TD><% FS::svc_acct_pop::popselector($popnum) %></TD>
+ </TR>
+ </TABLE>
+% }
+% }
+
+
+<INPUT TYPE="hidden" NAME="otaker" VALUE="<% $cust_main->otaker %>">
+<BR>
+<INPUT TYPE="button" NAME="submitButton" ID="submitButton" VALUE="<% $custnum ? "Apply Changes" : "Add Customer" %>" onClick="document.bottomform.submitButton.disabled=true; bottomfixup(this.form);">
+<BR>
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit customer');
+
+#for misplaced logic below
+#use FS::part_pkg;
+
+#for false laziness below (now more properly lazy)
+#use FS::svc_acct_pop;
+
+#for (other) false laziness below
+#use FS::agent;
+#use FS::type_pkgs;
+
+my $conf = new FS::Conf;
+
+my $taxpre = $conf->exists('tax-ship_address') ? 'ship_' : '';
+#get record
+
+my($custnum, $username, $password, $popnum, $cust_main, $saved_pkgpart, $saved_domsvc);
+my(@invoicing_list);
+my ($ss,$stateid,$payinfo);
+my $same = '';
+if ( $cgi->param('error') ) {
+ $cust_main = new FS::cust_main ( {
+ map { $_, scalar($cgi->param($_)) } fields('cust_main')
+ } );
+ $custnum = $cust_main->custnum;
+ $saved_domsvc = $cgi->param('domsvc') || '';
+ if ( $saved_domsvc =~ /^(\d+)$/ ) {
+ $saved_domsvc = $1;
+ } else {
+ $saved_domsvc = '';
+ }
+ $saved_pkgpart = $cgi->param('pkgpart_svcpart') || '';
+ if ( $saved_pkgpart =~ /^(\d+)_/ ) {
+ $saved_pkgpart = $1;
+ } else {
+ $saved_pkgpart = '';
+ }
+ $username = $cgi->param('username');
+ $password = $cgi->param('_password');
+ $popnum = $cgi->param('popnum');
+ @invoicing_list = split( /\s*,\s*/, $cgi->param('invoicing_list') );
+ $same = $cgi->param('same');
+ $cust_main->setfield('paid' => $cgi->param('paid')) if $cgi->param('paid');
+ $ss = $cust_main->ss; # don't mask an entered value on errors
+ $stateid = $cust_main->stateid; # don't mask an entered value on errors
+ $payinfo = $cust_main->payinfo; # don't mask an entered value on errors
+} elsif ( $cgi->keywords ) { #editing
+ my( $query ) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $custnum=$1;
+ $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+ if ( $cust_main->dbdef_table->column('paycvv')
+ && length($cust_main->paycvv) ) {
+ my $paycvv = $cust_main->paycvv;
+ $paycvv =~ s/./*/g;
+ $cust_main->paycvv($paycvv);
+ }
+ $saved_pkgpart = 0;
+ $saved_domsvc = 0;
+ $username = '';
+ $password = '';
+ $popnum = 0;
+ @invoicing_list = $cust_main->invoicing_list;
+ $ss = $cust_main->masked('ss');
+ $stateid = $cust_main->masked('stateid');
+ $payinfo = $cust_main->paymask;
+} else {
+ $custnum='';
+ $cust_main = new FS::cust_main ( {} );
+ $cust_main->otaker( &getotaker );
+ $cust_main->referral_custnum( $cgi->param('referral_custnum') );
+ $saved_pkgpart = 0;
+ $saved_domsvc = 0;
+ $username = '';
+ $password = '';
+ $popnum = 0;
+ @invoicing_list = ();
+ push @invoicing_list, 'POST'
+ unless $conf->exists('disablepostalinvoicedefault');
+ $ss = '';
+ $stateid = '';
+ $payinfo = '';
+}
+
+my $error = $cgi->param('error');
+$cgi->delete_all();
+$cgi->param('error', $error);
+
+my $action = $custnum ? 'Edit' : 'Add';
+$action .= ": ". $cust_main->name if $custnum;
+
+my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
+
+</%init>
diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html
new file mode 100644
index 0000000..8724db9
--- /dev/null
+++ b/httemplate/edit/cust_main/billing.html
@@ -0,0 +1,484 @@
+%if ( $payby_default eq 'HIDE' ) {
+%
+% $cust_main->payby('BILL') unless $cust_main->payby;
+
+ <INPUT TYPE="hidden" NAME="select" VALUE="<% $cust_main->payby %>">
+
+ </FORM>
+
+ <FORM NAME="<% $cust_main->payby %>" STYLE="margin-top: 0; margin-bottom: 0">
+
+ <INPUT TYPE="hidden" NAME="payinfo" VALUE="<% $cust_main->paymask %>">
+
+% foreach my $field (qw( payname paycvv paystart_month paystart_year payissue payip paytype paystate )) {
+
+ <INPUT TYPE="hidden" NAME="<% $field %>" VALUE="<% $cust_main->getfield($field) %>">
+
+% }
+
+% #false laziness w/elements/select-month_year.html & view/cust_main/billing.html
+% my( $mon, $year );
+% my $date = $cust_main->paydate || '12-2037';
+% if ( $date =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
+% ( $mon, $year ) = ( $2, $1 );
+% } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+% ( $mon, $year ) = ( $1, $3 );
+% } else {
+% die "unrecognized expiration date format: $date";
+% }
+
+ <INPUT TYPE="hidden" NAME="exp_month" VALUE="<% $mon %>">
+ <INPUT TYPE="hidden" NAME="exp_year" VALUE="<% $year %>">
+
+ </FORM>
+
+ <FORM NAME="billing_bottomform" STYLE="margin-top: 0; margin-bottom: 0">
+
+ <INPUT TYPE="hidden" NAME="tax" VALUE="<% $cust_main->tax %>">
+
+ <INPUT TYPE="hidden" NAME="invoicing_list" VALUE="<% join(', ', @invoicing_list) %>">
+
+ </FORM>
+
+% } else {
+%
+% my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
+
+ <BR>Billing information
+ <% &ntable("#cccccc") %>
+
+ <TR>
+ <TD ALIGN="right" WIDTH="200"><%$r%>Billing type</TD>
+
+ <SCRIPT>
+
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1;
+ }
+
+ var achwindow = -1;
+ function achopen(filename,windowname,properties) {
+ achclose();
+ achwindow = window.open(filename,windowname,properties);
+ }
+ function achclose() {
+ if ( achwindow != -1 )
+ achwindow.close();
+ achwindow = -1;
+ }
+
+ function card_changed(what) {
+ if (
+ what.form.payinfo.value.substring(0, 4) == '4093'
+ || what.form.payinfo.value.substring(0, 4) == '4911'
+ || what.form.payinfo.value.substring(0, 4) == '4936'
+ || what.form.payinfo.value.substring(0, 6) == '564132'
+ || what.form.payinfo.value.substring(0, 2) == '63'
+ || what.form.payinfo.value.substring(0, 2) == '67'
+ )
+ {
+ what.form.paystart_month.disabled = false;
+ what.form.paystart_year.disabled = false;
+ what.form.payissue.disabled = false;
+ what.form.paystart_month.style.backgroundColor = '#ffffff';
+ what.form.paystart_year.style.backgroundColor = '#ffffff';
+ what.form.payissue.style.backgroundColor = '#ffffff';
+ document.getElementById('paystart_label').style.color = '#000000';
+ document.getElementById('payissue_label').style.color = '#000000';
+ } else {
+ what.form.paystart_month.disabled = true;
+ what.form.paystart_year.disabled = true;
+ what.form.payissue.disabled = true;
+ what.form.paystart_month.style.backgroundColor = '#dddddd';
+ what.form.paystart_year.style.backgroundColor = '#dddddd';
+ what.form.payissue.style.backgroundColor = '#dddddd';
+ document.getElementById('paystart_label').style.color = '#999999';
+ document.getElementById('payissue_label').style.color = '#999999';
+ }
+ return true;
+ }
+
+ </SCRIPT>
+
+ <% include('/elements/init_overlib.html') %>
+
+% my $payby = $cust_main->payby;
+% my $paytype = $cust_main->paytype;
+% my( $account, $aba ) = split('@', $payinfo);
+%
+% my $disabled = 'DISABLED style="background-color: #dddddd"';
+% my $text_disabled = 'style="color: #999999"';
+%
+% if ( $payby =~ /^(CARD|DCRD)$/ && cardtype($payinfo) =~ /^(Switch|Solo)$/ ) {
+% $disabled = 'style="background-color: #ffffff"';
+% $text_disabled = 'style="color: #000000";'
+% }
+%
+% my %payby = (
+%
+% 'CARD' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Card number </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="payinfo" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $payinfo : '' ). qq!" MAXLENGTH=19 onChange="card_changed(this)" onKeyUp="card_changed(this)"></TD></TR>!.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Expiration </TD>!.
+% '<TD WIDTH="408">'.
+%
+% include('/elements/select-month_year.html',
+% 'prefix' => 'exp',
+% 'selected_date' =>
+% ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->paydate : '' ),
+% ).
+%
+% '</TD></TR>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">CVV2&nbsp;!.
+%
+% qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/cvv2.html', 480, 352, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)!.
+% qq!</TD>!.
+% '<TD WIDTH="408"><INPUT TYPE="text" NAME="paycvv" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ && !$cust_main->is_encrypted($cust_main->paycvv) ? $cust_main->paycvv : '' ). '" SIZE=4 MAXLENGTH=4>'.
+%
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200"><SPAN ID="paystart_label" $text_disabled>Start date </SPAN></TD>!.
+% '<TD WIDTH="408">'.
+%
+% include('/elements/select-month_year.html',
+% 'prefix' => 'paystart',
+% 'disabled' => $disabled,
+% 'empty_option' => 1,
+% 'start_year' => 2000,
+% 'end_year' => (localtime())[5] + 1900,
+% 'selected_date' => (
+% ( $payby =~ /^(CARD|DCRD)$/
+% && cardtype($payinfo) =~ /^(Switch|Solo)$/ )
+% ? $cust_main->paystart_month. '-'.
+% $cust_main->paystart_year
+% : ''
+% )
+% ).
+%
+% qq!<SPAN ID="payissue_label" $text_disabled> or Issue number </SPAN>!.
+% '<INPUT TYPE="text" NAME="payissue" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payissue : '' ). qq!" SIZE=3 MAXLENGTH=2 $disabled></TD></TR>!.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Exact name on card </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="payname" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!.
+%
+% qq!<TR><TD COLSPAN=2 WIDTH="608"><INPUT TYPE="checkbox" NAME="payauto" !. ( $payby eq 'DCRD' ? '' : 'CHECKED' ). '> Charge future payments to this card automatically</TD></TR>'.
+%
+% '</TABLE>',
+%
+% 'CHEK' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Account number </TD>!.
+% qq!<TD><INPUT TYPE="text" SIZE=12 NAME="payinfo1" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $account : '' ). '"></TD>'.
+% qq!<TD ALIGN="right">Type</TD><TD><SELECT NAME="paytype">!.
+% join('', map { qq!<OPTION VALUE="$_" !.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>" } @FS::cust_main::paytypes).
+% qq!</SELECT></TD></TR>!.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}ABA/Routing number </TD>!.
+% qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" SIZE=10 MAXLENGTH=9 NAME="payinfo2" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $aba : '' ). qq!" SIZE=10 MAXLENGTH=9> !.
+% qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/ach.html', 380, 240, 'ach_popup' ), CAPTION, 'ACH Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)!.
+% qq!</TD></TR>!.
+%
+% qq!<INPUT TYPE="hidden" NAME="exp_month" VALUE="12">!.
+% qq!<INPUT TYPE="hidden" NAME="exp_year" VALUE="2037">!.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Bank name </TD>!.
+% qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" NAME="payname" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!.
+% ( $conf->exists('show_bankstate') ?
+% qq!<TR><TD ALIGN="right" WIDTH="200">$paystate_label</TD>!.
+% qq!<TD COLSPAN="3" WIDTH="408">!.
+% include('/elements/select-state.html',
+% 'empty' => '(choose)',
+% 'state' => $cust_main->paystate,
+% 'country' => $cust_main->country,
+% 'prefix' => 'pay',
+% ). "</TD></TR>"
+% : '<INPUT TYPE="hidden" NAME="paystate" VALUE="'.
+% $cust_main->paystate. '">'
+% ).
+%
+%
+% qq!<TR><TD COLSPAN=4 WIDTH="608"><INPUT TYPE="checkbox" NAME="payauto" !. ( $payby eq 'DCHK' ? '' : 'CHECKED' ). '> Charge future payments to this electronic check automatically</TD></TR>'.
+%
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+%
+% '</TABLE>',
+%
+% 'LECB' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Phone number </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="payinfo" VALUE="!. ( $payby eq 'LECB' ? $cust_main->payinfo : '' ). qq!" MAXLENGTH=15 SIZE=16></TD></TR>!.
+%
+% qq!<INPUT TYPE="hidden" NAME="exp_month" VALUE="12">!.
+% qq!<INPUT TYPE="hidden" NAME="exp_year" VALUE="2037">!.
+% qq!<INPUT TYPE="hidden" NAME="payname" VALUE="">!.
+%
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+%
+% '</TABLE>',
+%
+% 'BILL' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">P.O. </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="payinfo" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payinfo : '' ). qq!"></TD></TR>!.
+%
+% qq!<INPUT TYPE="hidden" NAME="exp_month" VALUE="12">!.
+% qq!<INPUT TYPE="hidden" NAME="exp_year" VALUE="2037">!.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">Attention </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="payname" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payname : '' ). qq!"></TD></TR>!.
+%
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+%
+% '</TABLE>',
+%
+% 'COMP' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Approved by </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="payinfo" VALUE=""></TD></TR>!.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Expiration </TD>!.
+% '<TD WIDTH="408">'.
+%
+% include('/elements/select-month_year.html',
+% 'prefix' => 'exp',
+% 'selected_date' =>
+% ( $payby eq 'COMP' ? $cust_main->paydate : '' ),
+% ).
+%
+% '</TD></TR>'.
+%
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+%
+% '</TABLE>',
+%
+% 'CASH' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="paid" VALUE="!. ( $payby eq 'CASH' ? $cust_main->paid : '' ). qq!"></TD></TR>!.
+%
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+%
+% '</TABLE>',
+%
+% 'WEST' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="paid" VALUE="!. ( $payby eq 'WEST' ? $cust_main->paid : '' ). qq!"></TD></TR>!.
+%
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+%
+% '</TABLE>',
+%
+% 'MCRD' =>
+%
+% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'.
+%
+% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="paid" VALUE="!. ( $payby eq 'MCRD' ? $cust_main->paid : '' ). qq!"></TD></TR>!.
+%
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+% '<TR><TD>&nbsp;</TD></TR>'.
+%
+% '</TABLE>',
+%
+% );
+%
+% #this should use FS::payby
+% my %allopt = (
+% 'CARD' => 'Credit card',
+% 'CHEK' => 'Electronic check',
+% 'LECB' => 'Phone bill billing',
+% 'BILL' => 'Billing',
+% 'CASH' => 'Cash', # initial payment, then billing',
+% 'WEST' => 'Western Union', # initial payment, then billing',
+% 'MCRD' => 'Manual credit card', # initial payment, then billing',
+% 'COMP' => 'Complimentary',
+% );
+% if ( $cust_main->custnum ) { #don't offer CASH/WEST/MCRD initial payment types
+% # when editing customer
+% delete $allopt{$_} for qw(CASH WEST MCRD);
+% }
+%
+% tie my %options, 'Tie::IxHash',
+% map { $_ => $allopt{$_} }
+% grep { exists $allopt{$_} }
+% @payby;
+%
+% my %payby2option = (
+% ( map { $_ => $_ } keys %options ),
+% 'DCRD' => 'CARD',
+% 'DCHK' => 'CHEK',
+% );
+%
+% my $widget = new HTML::Widgets::SelectLayers(
+% 'options' => \%options,
+% #'form_name' => 'dummy',
+% #'form_action' => 'nothingyet',
+% #chops bottom of page in IE# 'under_position' => 'absolute',
+% 'html_between' => '</TD></TR></TABLE>',
+% 'selected_layer' => $payby2option{$payby || $payby_default || $payby[0] },
+% 'layer_callback' => sub { my $layer = shift; $payby{$layer}; },
+% );
+%
+%
+
+
+ <TD WIDTH="408"><% $widget->html %>
+
+ <FORM NAME="billing_bottomform" STYLE="margin-top: 0; margin-bottom: 0">
+
+ <% &ntable("#cccccc") %>
+
+ <TR><TD>&nbsp;</TD></TR>
+
+ <TR>
+ <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="tax" VALUE="Y" <% $cust_main->tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt</TD>
+ </TR>
+
+% unless ( $conf->exists('emailinvoiceonly') ) {
+
+ <TR>
+ <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST" <%
+
+ ( grep { $_ eq 'POST' } @invoicing_list )
+
+ ? 'CHECKED'
+ : ''
+
+ %>> Postal mail invoice
+
+ </TD>
+ </TR>
+
+ <TR>
+ <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_FAX" VALUE="FAX" <%
+
+ ( grep { $_ eq 'FAX' } @invoicing_list )
+ ? 'CHECKED'
+ : ''
+
+ %>> Fax invoice
+
+ </TD>
+ </TR>
+
+% }
+
+ <TR>
+ <TD ALIGN="right" WIDTH="200">
+ <% $conf->exists('cust_main-require_invoicing_list_email') ? $r : '' %>Email address(es)
+ </TD>
+ <TD WIDTH="408"><INPUT TYPE="text" NAME="invoicing_list" VALUE="<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) %>"></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right" WIDTH="200">Invoice terms </TD>
+ <TD WIDTH="408">
+ <SELECT NAME="invoice_terms">
+ <OPTION VALUE="">Default (<% $conf->config('invoice_default_terms') || 'Payable upon receipt' %>)
+% foreach my $term ( 'Payable upon receipt',
+% ( map "Net $_", 0, 10, 15, 30, 45, 60 ),
+% ) {
+ <OPTION VALUE="<% $term %>" <% $cust_main->invoice_terms eq $term ? ' SELECTED' : '' %>><% $term %>
+% }
+ </SELECT>
+ </TD>
+ </TR>
+
+% if ( $conf->exists('voip-cust_cdr_spools') ) {
+ <TR>
+ <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="spool_cdr" VALUE="Y" <% $cust_main->spool_cdr eq "Y" ? 'CHECKED' : '' %>> Spool CDRs</TD>
+ </TR>
+% } else {
+
+ <INPUT TYPE="hidden" NAME="spool_cdr" VALUE="<% $cust_main->spool_cdr %>">
+% }
+
+% if ( $conf->exists('voip-cust_cdr_squelch') ) {
+ <TR>
+ <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="squelch_cdr" VALUE="Y" <% $cust_main->squelch_cdr eq "Y" ? 'CHECKED' : '' %>> Omit CDRs from invoices</TD>
+ </TR>
+% } else {
+
+ <INPUT TYPE="hidden" NAME="squelch_cdr" VALUE="<% $cust_main->squelch_cdr %>">
+% }
+
+ </TABLE>
+
+ </FORM>
+
+ <% $r %> required fields
+% }
+
+<%once>
+
+my $paystate_label = FS::Msgcat::_gettext('paystate');
+$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/;
+
+</%once>
+<%init>
+
+my( $cust_main, %options ) = @_;
+my @invoicing_list = @{ $options{'invoicing_list'} };
+my $payinfo = $options{'payinfo'};
+my $conf = new FS::Conf;
+my $payby_default = $conf->config('payby-default');
+
+my @payby = grep /\w/, $conf->config('payby');
+#@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
+@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
+ unless @payby;
+
+</%init>
diff --git a/httemplate/edit/cust_main/choose_tax_location.html b/httemplate/edit/cust_main/choose_tax_location.html
new file mode 100644
index 0000000..bd8b95c
--- /dev/null
+++ b/httemplate/edit/cust_main/choose_tax_location.html
@@ -0,0 +1,85 @@
+<FORM NAME="choosegeocodeform">
+<CENTER><BR><B>Choose tax location</B><BR><BR>
+<P STYLE="<% $style %>"><% $header %></P>
+
+<SELECT NAME='geocodes' ID='geocodes' STYLE="<% $style %>">
+% foreach my $location (@cust_tax_location) {
+% my %value = ( zip => $zip5,
+% map { $_ => $location->$_ }
+% qw ( city state geocode )
+% );
+% map { $value{$_} = $location{$_} } qw ( city state )
+% if $location{country} eq 'CA';
+%
+% my $value = encode_entities(objToJson({ %value })
+% );
+% my $content = '';
+% $content .= $location->$_. '&nbsp;' x ( $max{$_} - length($location->$_) )
+% foreach qw( city county state );
+% $content .= $location->cityflag eq 'I' ? 'Y' : 'N' ;
+% my $selected = '' ;
+% if (!$have_selected && lc($location->city) eq lc($location{city})) {
+% $selected = 'SELECTED';
+% }
+ <OPTION VALUE="<% $value %>" STYLE="<% $style %>" <% $selected %>><% $content %>
+% }
+</SELECT><BR><BR>
+
+<TABLE><TR>
+ <TD> <BUTTON TYPE="button" onClick="set_geocode(document.getElementById('geocodes')); document.bottomform.submit();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Set location </BUTTON></TD>
+ <TD><BUTTON TYPE="button" onClick="document.bottomform.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission </BUTTON></TD>
+</TR>
+</TABLE>
+
+</CENTER>
+</FORM>
+<%init>
+
+my $conf = new FS::Conf;
+my $have_selected = 0;
+
+my %location = ();
+
+($location{data_vendor}) = $cgi->param('data_vendor') =~ /^([-\w]+)$/;
+($location{city}) = $cgi->param('city') =~ /^([\w ]+)$/;
+($location{state}) = $cgi->param('state') =~ /^(\w+)$/;
+($location{zip}) = $cgi->param('zip') =~ /^([-\w ]+)$/;
+($location{country}) = $cgi->param('country') =~ /^([\w ]+)$/;
+
+my($zip5, $zip4) = split('-', $location{zip});
+
+#only support US & CA
+my $hashref = { 'data_vendor' => $location{data_vendor} };
+$hashref->{zip} = $location{country} eq 'CA' ? substr($zip5,0,1) : $zip5,
+
+my @keys = keys(%$hashref);
+my @cust_tax_location = ();
+until ( @cust_tax_location ) {
+ @cust_tax_location = qsearch({ table => 'cust_tax_location',
+ hashref => $hashref,
+ order_by => 'LIMIT 50',
+ });
+ last unless scalar(@keys);
+ delete $hashref->{ shift @keys };
+}
+
+my %max = ( city => 4, county => 6, state => 5);
+foreach my $location (@cust_tax_location) {
+ foreach ( qw( city county state ) ) {
+ my $length = length($location->$_);
+ $max{$_} = ($length > $max{$_}) ? $length : $max{$_};
+ }
+}
+foreach ( qw( city county state ) ) {
+ $max{$_} = $location{$_} if $location{$_} > $max{$_};
+ $max{$_}++;
+}
+
+my $header = '&nbsp;&nbsp;';
+$header .= $_. '&nbsp;' x ( $max{lc($_)} - length($_) )
+ foreach qw( City County State );
+$header .= "In city?";
+
+my $style = "font-family:monospace;";
+
+</%init>
diff --git a/httemplate/edit/cust_main/contact.html b/httemplate/edit/cust_main/contact.html
new file mode 100644
index 0000000..27dd385
--- /dev/null
+++ b/httemplate/edit/cust_main/contact.html
@@ -0,0 +1,137 @@
+<% &ntable("#cccccc") %>
+
+<TR>
+ <TH ALIGN="right"><%$r%>Contact&nbsp;name<BR>(last,&nbsp;first)</TH>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%$pre%>last" VALUE="<% $cust_main->get($pre.'last') %>" onChange="<% $onchange %>" <%$disabled%>> ,
+ <INPUT TYPE="text" NAME="<%$pre%>first" VALUE="<% $cust_main->get($pre.'first') %>" onChange="<% $onchange %>" <%$disabled%>>
+ </TD>
+% if ( $conf->exists('show_ss') && !$pre ) {
+
+ <TD ALIGN="right">SS#</TD>
+ <TD><INPUT TYPE="text" NAME="ss" VALUE="<% $opt{ss} %>" SIZE=11></TD>
+% } elsif ( !$pre ) {
+
+ <TD><INPUT TYPE="hidden" NAME="ss" VALUE="<% $opt{ss} %>"></TD>
+% }
+
+
+</TR>
+
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=7>
+ <INPUT TYPE="text" NAME="<%$pre%>company" VALUE="<% $cust_main->get($pre.'company') %>" SIZE=70 onChange="<% $onchange %>" <%$disabled%>>
+ </TD>
+</TR>
+
+<% include('/elements/location.html',
+ 'prefix' => $pre,
+ 'object' => $cust_main,
+ 'onchange' => $onchange,
+ 'disabled' => $disabled,
+ 'same_checked' => $opt{'same_checked'},
+ 'geocode' => $opt{'geocode'},
+ )
+%>
+
+<TR>
+ <TD ALIGN="right"><% $daytime_label %></TD>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%$pre%>daytime" VALUE="<% $cust_main->get($pre.'daytime') %>" SIZE=18 onChange="<% $onchange %>" <%$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right"><% $night_label %></TD>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%$pre%>night" VALUE="<% $cust_main->get($pre.'night') %>" SIZE=18 onChange="<% $onchange %>" <%$disabled%>>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5>
+ <INPUT TYPE="text" NAME="<%$pre%>fax" VALUE="<% $cust_main->get($pre.'fax') %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%>>
+ </TD>
+</TR>
+
+% if ( $conf->exists('show_stateid') && !$pre ) {
+
+<TR>
+ <TD ALIGN="right"><% $stateid_label %></TD>
+ <TD><INPUT TYPE="text" NAME="stateid" VALUE="<% $opt{stateid} %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%>></TD>
+ <TD ALIGN="right"><% $stateid_state_label %></TD>
+ <TD><% include('/elements/select-state.html',
+ 'state' => $cust_main->stateid_state,
+ 'country' => $cust_main->country,
+ 'prefix' => 'stateid_',
+ 'onchange' => $onchange,
+ 'disabled' => $disabled,
+ )
+ %>
+ </TD>
+</TR>
+% } elsif ( !$pre ) {
+
+ <TD><INPUT TYPE="hidden" NAME="stateid" VALUE="<% $opt{stateid} %>"></TD>
+ <TD><INPUT TYPE="hidden" NAME="stateid_state" VALUE="<% $cust_main->stateid_state %>"></TD>
+% }
+
+</TABLE>
+<%$r%>required fields<BR>
+
+<%init>
+
+#my( $cust_main, $pre, $onchange, $disabled, %opt ) = @_;
+my %opt = @_;
+my $cust_main = $opt{'cust_main'};
+my $pre = $opt{'pre'};
+my $onchange = $opt{'onchange'};
+my $disabled = $opt{'disabled'};
+
+my $conf = new FS::Conf;
+
+foreach (qw(ss stateid)) {
+ $opt{$_} = $cust_main->masked($_) unless exists $opt{$_};
+}
+
+#false laziness with ship state
+my $countrydefault = $conf->config('countrydefault') || 'US';
+$cust_main->set($pre.'country', $countrydefault )
+ unless $cust_main->get($pre.'country');
+
+my $statedefault = $conf->config('statedefault')
+ || ($countrydefault eq 'US' ? 'CA' : '');
+$cust_main->set($pre.'state', $statedefault )
+ unless $cust_main->get($pre.'state')
+ || $cust_main->get($pre.'country') ne $countrydefault;
+
+$cust_main->set('stateid_state', $cust_main->state )
+ unless $pre || $cust_main->get('stateid_state');
+
+#my($county_html, $state_html, $country_html) =
+# FS::cust_main_county::regionselector( $cust_main->get($pre.'county'),
+# $cust_main->get($pre.'state'),
+# $cust_main->get($pre.'country'),
+# $pre,
+# $onchange,
+# $disabled,
+# );
+
+my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/
+ ? 'Day Phone'
+ : FS::Msgcat::_gettext('daytime');
+my $night_label = FS::Msgcat::_gettext('night') =~/^(night)?$/
+ ? 'Night Phone'
+ : FS::Msgcat::_gettext('night') || 'Night Phone';
+my $stateid_label = FS::Msgcat::_gettext('stateid') =~ /^(stateid)?$/
+ ? 'Driver&rsquo;s License'
+ : FS::Msgcat::_gettext('stateid') || 'Driver&rsquo;s License';
+my $stateid_state_label = FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_state)?$/
+ ? 'Driver&rsquo;s License State'
+ : FS::Msgcat::_gettext('stateid_state') || 'Driver&rsquo;s License State';
+
+my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
+
+</%init>
diff --git a/httemplate/edit/cust_main/select-domain.html b/httemplate/edit/cust_main/select-domain.html
new file mode 100644
index 0000000..bec1e83
--- /dev/null
+++ b/httemplate/edit/cust_main/select-domain.html
@@ -0,0 +1,67 @@
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/svc_acct-domains.cgi',
+ 'subs' => [ $opt{'prefix'}. 'get_domains' ],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ function selopt(what,value,text,selected) {
+ var optionName = new Option(text, value, false, selected);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function <% $opt{'prefix'} %>pkgpart_svcpart_changed(what,selected) {
+
+ pkgpart_svcpart = what.options[what.selectedIndex].value;
+
+ function <% $opt{'prefix'} %>update_domains(domains) {
+
+ // blank the current domain list
+ for ( var i = what.form.<% $opt{'prefix'} %>domsvc.length; i >= 0; i-- )
+ what.form.<% $opt{'prefix'} %>domsvc.options[i] = null;
+
+ // add the new domains
+ var domainArray = eval('(' + domains + ')' );
+ for ( var s = 0; s < domainArray.length; s=s+2 ) {
+ var domainLabel = domainArray[s+1];
+ if ( domainLabel == "" )
+ domainLabel = '(n/a)';
+ selopt(what.form.<% $opt{'prefix'} %>domsvc, domainArray[s], domainLabel, (domainArray[s] == selected) ? true : false);
+ }
+
+ }
+
+ // go get the new domains
+ <% $opt{'prefix'} %>get_domains( pkgpart_svcpart, <% $opt{'prefix'} %>update_domains );
+
+ }
+
+</SCRIPT>
+
+<SELECT NAME="<% $opt{'prefix'} %>pkgpart_svcpart" onchange="<% $opt{'prefix'} %>pkgpart_svcpart_changed(this,0);" >
+ <OPTION VALUE="">(none)
+
+% foreach my $part_pkg ( @part_pkg ) {
+
+ <OPTION VALUE="<% $part_pkg->pkgpart. "_". $part_pkg->svcpart('svc_acct') %>"<% ( $opt{saved_pkgpart} && $part_pkg->pkgpart == $opt{saved_pkgpart} ) ? ' SELECTED' : '' %>><% $part_pkg->pkg. " - ". $part_pkg->comment %>
+
+% }
+
+</SELECT>
+<SCRIPT>
+ pkgpart_svcpart_changed(document.bottomform.pkgpart_svcpart, <% $opt{saved_domsvc} %>);
+</SCRIPT>
+
+<%init>
+my %opt = @_;
+foreach my $opt (qw( svc_part pkgparts saved_pkgpart saved_domsvc prefix)) {
+ $opt{$_} = '' unless exists($opt{$_}) && defined($opt{$_});
+}
+$opt{saved_domsvc} = 0 unless $opt{saved_domsvc};
+my @part_pkg = @{$opt{'pkgparts'}};
+
+</%init>
+
diff --git a/httemplate/edit/cust_main_county-expand.cgi b/httemplate/edit/cust_main_county-expand.cgi
new file mode 100755
index 0000000..d5297ab
--- /dev/null
+++ b/httemplate/edit/cust_main_county-expand.cgi
@@ -0,0 +1,50 @@
+<% include('/elements/header-popup.html', "Enter $title") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p1 %>process/cust_main_county-expand.cgi" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="taxnum" VALUE="<% $taxnum %>">
+
+<TEXTAREA NAME="expansion" COLS="50" ROWS="16"><% $expansion |h %></TEXTAREA>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Add <% $title %>">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my($taxnum, $expansion);
+my($query) = $cgi->keywords;
+if ( $cgi->param('error') ) {
+ $taxnum = $cgi->param('taxnum');
+ $expansion = $cgi->param('expansion');
+} else {
+ $query =~ /^(\d+)$/
+ or die "Illegal taxnum (query $query)";
+ $taxnum = $1;
+ $expansion = '';
+}
+
+my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum})
+ or die "cust_main_county.taxnum $taxnum not found";
+
+my $title;
+
+die "Can't expand entry!" if $cust_main_county->county;
+
+if ( $cust_main_county->state ) {
+ $title = 'Counties';
+} else {
+ $title = 'States/Provinces';
+}
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/cust_main_county.html b/httemplate/edit/cust_main_county.html
new file mode 100644
index 0000000..5d7f9e6
--- /dev/null
+++ b/httemplate/edit/cust_main_county.html
@@ -0,0 +1,62 @@
+<% include('elements/edit.html',
+ 'popup' => 1,
+ 'name' => 'Tax rate', #Edit tax rate
+ 'table' => 'cust_main_county',
+ 'labels' => { 'taxnum' => 'Tax',
+ 'country' => 'Country',
+ 'state' => 'State',
+ 'county' => 'County',
+ 'taxclass' => 'Tax class',
+ 'taxname' => 'Tax name',
+ 'tax' => 'Tax rate',
+ 'setuptax' => 'This tax not applicable to setup fees',
+ 'recurtax' => 'This tax not applicable to recurring fees',
+ 'exempt_amount' => 'Monthly exemption per customer ($25 "Texas tax")',
+ },
+ 'fields' => \@fields,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $taxnum;
+if ( $cgi->param('error') ) {
+ $cgi->param('taxnum') =~ /^(\d+)$/
+ or die "no taxnum, but error: ". $cgi->param('error');
+ $taxnum = $1;
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die 'no taxnum';
+ $taxnum = $1;
+}
+
+my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum })
+ or die "unknown taxnum $1";
+
+my @fields = (
+ { field=>'country', type=>'fixed-country', },
+ { field=>'state', type=>'fixed-state', },
+ { field=>'county', type=>'fixed', },
+);
+
+push @fields, { field=>'taxclass', type=>'fixed', }
+ if $conf->exists('enable_taxclasses');
+
+push @fields,
+ 'taxname',
+ { field=>'tax', type=>'percentage', },
+
+ { type=>'tablebreak-tr-title', value=>'Exemptions' },
+ { field=>'setuptax', type=>'checkbox', value=>'Y', },
+ { field=>'recurtax', type=>'checkbox', value=>'Y', },
+ { field=>'exempt_amount', type=>'money', },
+;
+
+</%init>
diff --git a/httemplate/edit/cust_main_note.cgi b/httemplate/edit/cust_main_note.cgi
new file mode 100755
index 0000000..6c6a1a9
--- /dev/null
+++ b/httemplate/edit/cust_main_note.cgi
@@ -0,0 +1,45 @@
+<% include('/elements/header-popup.html', "$action Customer Note") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% popurl(1) %>process/cust_main_note.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+<INPUT TYPE="hidden" NAME="notenum" VALUE="<% $notenum %>">
+
+
+<BR><BR>
+<TEXTAREA NAME="comment" ROWS="12" COLS="60">
+<% $comment %>
+</TEXTAREA>
+
+<BR><BR>
+<INPUT TYPE="submit" VALUE="<% $notenum ? "Apply Changes" : "Add Note" %>">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+my $comment;
+my $notenum = '';
+if ( $cgi->param('error') ) {
+ $comment = $cgi->param('comment');
+} elsif ( $cgi->param('notenum') =~ /^(\d+)$/ ) {
+ $notenum = $1;
+ die "illegal query ". $cgi->keywords unless $notenum;
+ my $note = qsearchs('cust_main_note', { 'notenum' => $notenum });
+ die "no such note: ". $notenum unless $note;
+ $comment = $note->comments;
+}
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die "illeagl custnum";
+my $custnum = $1;
+
+my $action = $notenum ? 'Edit' : 'Add';
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right("$action customer note");
+
+</%init>
+
diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi
new file mode 100755
index 0000000..3c28774
--- /dev/null
+++ b/httemplate/edit/cust_pay.cgi
@@ -0,0 +1,138 @@
+% if ( $link eq 'popup' ) {
+ <% include('/elements/header-popup.html', $title ) %>
+% } else {
+ <% include("/elements/header.html", $title, '') %>
+% }
+
+<% include('/elements/init_calendar.html') %>
+
+<% include('/elements/error.html') %>
+
+% unless ( $link eq 'popup' ) {
+ <% small_custview($custnum, $conf->config('countrydefault')) %>
+% }
+
+<FORM NAME="PaymentForm" ACTION="<% popurl(1) %>process/cust_pay.cgi" METHOD=POST onSubmit="document.PaymentForm.submit.disabled=true">
+<INPUT TYPE="hidden" NAME="link" VALUE="<% $link %>">
+<INPUT TYPE="hidden" NAME="linknum" VALUE="<% $linknum %>">
+<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>">
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="<% $paybatch %>">
+
+<BR><BR>
+
+Payment
+<% ntable("#cccccc", 2) %>
+
+<TR>
+ <TD ALIGN="right">Date</TD>
+ <TD COLSPAN=2>
+ <INPUT TYPE="text" NAME="_date" ID="_date_text" VALUE="<% time2str("%m/%d/%Y %r",$_date) %>">
+ <IMG SRC="../images/calendar.png" ID="_date_button" STYLE="cursor: pointer" TITLE="Select date">
+ </TD>
+</TR>
+
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "_date_text",
+ ifFormat: "%m/%d/%Y",
+ button: "_date_button",
+ align: "BR"
+ });
+</SCRIPT>
+
+<TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#ffffff" ALIGN="right"><% $money_char %></TD>
+ <TD><INPUT TYPE="text" NAME="paid" VALUE="<% $paid %>" SIZE=8 MAXLENGTH=8> by <B><% FS::payby->payname($payby) %></B></TD>
+</TR>
+
+% if ( $payby eq 'BILL' ) {
+ <TR>
+ <TD ALIGN="right">Check #</TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
+ </TR>
+% }
+
+<TR>
+% if ( $link eq 'custnum' || $link eq 'popup' ) {
+
+ <TD ALIGN="right">Auto-apply<BR>to invoices</TD>
+ <TD COLSPAN=2>
+ <SELECT NAME="apply">
+ <OPTION VALUE="yes" SELECTED>yes
+ <OPTION>no</SELECT>
+ </TD>
+
+% } elsif ( $link eq 'invnum' ) {
+
+ <TD ALIGN="right">Apply to</TD>
+ <TD COLSPAN=2 BGCOLOR="#ffffff">Invoice #<B><% $linknum %></B> only</TD>
+ <INPUT TYPE="hidden" NAME="apply" VALUE="no">
+
+% }
+</TR>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Post payment">
+
+</FORM>
+
+% if ( $link eq 'popup' ) {
+ </BODY>
+ </HTML>
+% } else {
+ <% include('/elements/footer.html') %>
+% }
+
+<%init>
+
+my $conf = new FS::Conf;
+
+my $money_char = $conf->config('money_char') || '$';
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Post payment');
+
+my($link, $linknum, $paid, $payby, $payinfo, $_date);
+if ( $cgi->param('error') ) {
+ $link = $cgi->param('link');
+ $linknum = $cgi->param('linknum');
+ $paid = $cgi->param('paid');
+ $payby = $cgi->param('payby');
+ $payinfo = $cgi->param('payinfo');
+ $_date = $cgi->param('_date') ? str2time($cgi->param('_date')) : time;
+} elsif ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $link = $cgi->param('popup') ? 'popup' : 'custnum';
+ $linknum = $1;
+ $paid = '';
+ $payby = $cgi->param('payby') || 'BILL';
+ $payinfo = '';
+ $_date = time;
+} elsif ( $cgi->param('invnum') =~ /^(\d+)$/ ) {
+ $link = 'invnum';
+ $linknum = $1;
+ $paid = '';
+ $payby = $cgi->param('payby') || 'BILL';
+ $payinfo = "";
+ $_date = time;
+} else {
+ die "illegal query ". $cgi->keywords;
+}
+
+my $paybatch = "webui-$_date-$$-". rand() * 2**32;
+
+my $title = 'Post '. FS::payby->payname($payby). ' payment';
+$title .= " against Invoice #$linknum" if $link eq 'invnum';
+
+my $custnum;
+if ( $link eq 'invnum' ) {
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $linknum } )
+ or die "unknown invnum $linknum";
+ $custnum = $cust_bill->custnum;
+} elsif ( $link eq 'custnum' ) {
+ $custnum = $linknum;
+}
+
+</%init>
diff --git a/httemplate/edit/cust_pay_pending.html b/httemplate/edit/cust_pay_pending.html
new file mode 100644
index 0000000..0916a1c
--- /dev/null
+++ b/httemplate/edit/cust_pay_pending.html
@@ -0,0 +1,154 @@
+<% include('/elements/header-popup.html', $title ) %>
+
+% if ( $action eq 'delete' ) {
+
+ <CENTER><FONT SIZE="+1"><B>Are you sure you want to delete this pending payment?</B></FONT></CENTER>
+
+% } elsif ( $action eq 'complete' ) {
+
+ <CENTER><FONT SIZE="+1"><B>No response was received from <% $cust_pay_pending->processor || 'the payment gateway' %> for this transaction. Check <% $cust_pay_pending->processor || 'the payment gateway' %>'s reporting and determine if this transaction completed successfully.</B></FONT></CENTER>
+
+% }
+
+<BR>
+
+%#false laziness w/view/cust_pay.html
+<% include('/elements/small_custview.html',
+ $cust_pay_pending->custnum,
+ scalar($conf->config('countrydefault')),
+ 1, #no balance
+ )
+%>
+<BR>
+
+<% ntable("#cccccc", 2) %>
+
+<TR>
+ <TD ALIGN="right">Pending payment#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->paypendingnum %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Date</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% time2str"%a&nbsp;%b&nbsp;%o,&nbsp;%Y&nbsp;%r", $cust_pay_pending->_date %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $money_char. $cust_pay_pending->paid %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Payment method</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->payby_name %> #<% $cust_pay_pending->paymask %></B></TD>
+</TR>
+
+% #if ( $cust_pay_pending->payby =~ /^(CARD|CHEK|LECB)$/ && $cust_pay_pending->paybatch ) {
+
+ <TR>
+ <TD ALIGN="right">Processor</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->processor %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Authorization#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->authorization %></B></TD>
+ </TR>
+
+% if ( $cust_pay_pending->order_number ) {
+ <TR>
+ <TD ALIGN="right">Order#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->order_number %></B></TD>
+ </TR>
+% }
+
+% #}
+
+</TABLE>
+
+<BR>
+
+<FORM NAME = "pendingform"
+ METHOD = "POST"
+ ACTION = "process/cust_pay_pending.html"
+>
+
+<INPUT TYPE="hidden" NAME="paypendingnum" VALUE="<% $paypendingnum %>">
+
+<% itable() %>
+
+% if ( $action eq 'delete' ) {
+
+ <INPUT TYPE="hidden" NAME="action" VALUE="<% $action %>">
+
+ <TR>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, delete payment</BUTTON>
+ </TD>
+ <TD>&nbsp;&nbsp;&nbsp;</TD>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, cancel deletion</BUTTON>
+ </TD>
+ </TR>
+
+% } elsif ( $action eq 'complete' ) {
+
+ <INPUT TYPE="hidden" NAME="action" VALUE="">
+
+ <TR>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'insert_cust_pay'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, transaction completed sucessfully.</BUTTON>
+ </TD>
+ <TD>&nbsp;&nbsp;&nbsp;</TD>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'decline'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was declined</BUTTON>
+ </TD>
+ <TD>&nbsp;&nbsp;&nbsp;</TD>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'delete'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was not received</BUTTON>
+ </TD>
+ </TR>
+
+ <TR><TD COLSPAN=5></TD></TR>
+
+ <TR>
+ <TD COLSPAN=5 ALIGN="center">
+ <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->Cancel payment completion; transaction status not yet known</BUTTON>
+ </TD>
+ </TR>
+
+% }
+
+</TABLE>
+
+</FORM>
+</BODY>
+</HTML>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Edit customer pending payments');
+
+$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action';
+my $action = $1;
+my $title = ucfirst($action). ' pending payment';
+
+$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum';
+my $paypendingnum = $1;
+my $cust_pay_pending =
+ qsearchs({
+ 'select' => 'cust_pay_pending.*',
+ 'table' => 'cust_pay_pending',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'paypendingnum' => $paypendingnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+ })
+ or die 'unknown paypendingnum';
+
+my $conf = new FS::Conf;
+
+my $money_char = $conf->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/edit/cust_pay_refund.cgi b/httemplate/edit/cust_pay_refund.cgi
new file mode 100755
index 0000000..f82fe36
--- /dev/null
+++ b/httemplate/edit/cust_pay_refund.cgi
@@ -0,0 +1,14 @@
+<% include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_pay_refund.cgi',
+ 'src_table' => 'cust_pay',
+ 'src_thing' => 'payment',
+ 'dst_table' => 'cust_refund',
+ 'dst_thing' => 'refund',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply payment');
+
+</%init>
diff --git a/httemplate/edit/cust_pkg.cgi b/httemplate/edit/cust_pkg.cgi
new file mode 100755
index 0000000..f927e10
--- /dev/null
+++ b/httemplate/edit/cust_pkg.cgi
@@ -0,0 +1,150 @@
+<% include('/elements/header.html', "Add/Edit Packages", '') %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p1 %>process/cust_pkg.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="action" VALUE="bulk">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+%#current packages
+%my @cust_pkg = qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } );
+%if (@cust_pkg) {
+
+ Current packages - select to remove (services are moved to a new package below)
+ <TABLE>
+ <TR STYLE="background-color: #cccccc;">
+ <TH COLSPAN="2">Pkg #</TH>
+ <TH>Package description</TH>
+ </TR>
+ <BR><BR>
+%
+%
+% foreach ( sort { $all_pkg{ $a->getfield('pkgpart') }
+% cmp $all_pkg{ $b->getfield('pkgpart') }
+% }
+% @cust_pkg
+% )
+% {
+% my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') );
+% my $checked = $remove_pkg{$pkgnum} ? ' CHECKED' : '';
+%
+%
+
+
+ <TR>
+ <TD><INPUT TYPE="checkbox" NAME="remove_pkg" VALUE="<% $pkgnum %>"<% $checked %>></TD>
+ <TD ALIGN="right"><% $pkgnum %>:</TD>
+ <TD><% $all_pkg{$pkgpart} %> - <% $all_comment{$pkgpart} %></TD>
+ </TR>
+% }
+
+
+ </TABLE>
+ <BR><BR>
+% }
+
+
+Order new packages
+<BR><BR>
+%
+%my $cust_main = qsearchs('cust_main',{'custnum'=>$custnum});
+%my $agent = qsearchs('agent',{'agentnum'=> $cust_main->agentnum });
+%
+%my %agent_pkgs = map { ( $_->pkgpart , $all_pkg{$_->pkgpart} ) }
+% qsearch('type_pkgs',{'typenum'=> $agent->typenum });
+%
+%my $count = 0;
+%my $pkgparts = 0;
+%
+
+
+<TABLE>
+ <TR STYLE="background-color: #cccccc;">
+ <TH>Qty.</TH>
+ <TH COLSPAN="2">Package Description</TH>
+ </TR>
+%
+%#foreach my $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) {
+%foreach my $pkgpart ( sort { $agent_pkgs{$a} cmp $agent_pkgs{$b} }
+% keys(%agent_pkgs) ) {
+% $pkgparts++;
+% next unless exists $pkg{$pkgpart}; #skip disabled ones
+% #print qq!<TR>! if ( $count == 0 );
+% my $value = $cgi->param("pkg$pkgpart") || 0;
+%
+
+
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="<% "pkg$pkgpart" %>" VALUE="<% $value %>" SIZE="2" MAXLENGTH="2">
+ </TD>
+ <TD ALIGN="right"><% $pkgpart %>:</TD>
+ <TD><% $pkg{$pkgpart} %> - <% $comment{$pkgpart}%></TD>
+ </TR>
+%
+% $count ++ ;
+% #if ( $count == 2 ) {
+% # print qq!</TR>\n! ;
+% # $count = 0;
+% #}
+%}
+%
+
+
+</TABLE>
+% unless ( $pkgparts ) {
+% my $p2 = popurl(2);
+% my $typenum = $agent->typenum;
+% my $agent_type = qsearchs( 'agent_type', { 'typenum' => $typenum } );
+% my $atype = $agent_type->atype;
+%
+
+
+ (No <A HREF="<% $p2 %>browse/part_pkg.cgi">package definitions</A>,
+ or agent type
+ <A HREF="<% $p2 %>edit/agent_type.cgi?<% $typenum %>"><% $atype %></a>
+ is not allowed to purchase any packages.)
+% }
+
+
+<P><INPUT TYPE="submit" VALUE="Order">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages');
+
+my %pkg = ();
+my %comment = ();
+my %all_pkg = ();
+my %all_comment = ();
+#foreach (qsearch('part_pkg', { 'disabled' => '' })) {
+# $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg');
+# $comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment');
+#}
+foreach (qsearch('part_pkg', {} )) {
+ $all_pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg');
+ $all_comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment');
+ next if $_->disabled;
+ $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg');
+ $comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment');
+}
+
+my($custnum, %remove_pkg);
+if ( $cgi->param('error') ) {
+ $custnum = $cgi->param('custnum');
+ %remove_pkg = map { $_ => 1 } $cgi->param('remove_pkg');
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $custnum = $1;
+ %remove_pkg = ();
+}
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/cust_pkg_detail.html
new file mode 100644
index 0000000..009ed5c
--- /dev/null
+++ b/httemplate/edit/cust_pkg_detail.html
@@ -0,0 +1,142 @@
+<% include("/elements/header-popup.html", $title, '',
+ ( $cgi->param('error') ? '' : 'onload="addRow()"' ),
+ )
+%>
+
+%# <% include('/elements/error.html') %>
+
+<FORM ACTION="process/cust_pkg_detail.html" NAME="DetailForm" ID="DetailForm" METHOD="POST">
+
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+<INPUT TYPE="hidden" NAME="detailtype" VALUE="<% $detailtype %>">
+
+<TABLE ID="DetailTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=1 STYLE="background-color: #cccccc">
+
+% if ( $curuser->option('show_pkgnum') ) {
+
+ <TR>
+ <TD ALIGN="right">Package #</TD>
+ <TD BGCOLOR="#ffffff"><% $pkgnum %></TD>
+ </TR>
+
+% }
+
+ <TR>
+ <TD ALIGN="right">Package</TD>
+ <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Comment</TD>
+ <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Status</TD>
+ <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_pkg->statuscolor %>"><B><% ucfirst($cust_pkg->status) %></B></FONT></TD>
+ </TR>
+
+ <TR>
+ <TD COLSPAN=2><% ucfirst($name{$detailtype}) %>: </TD>
+ </TR>
+
+% my $row = 0;
+% for ( @details ) {
+
+ <TR>
+ <TD></TD>
+ <TD>
+ <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup = "possiblyAddRow;" >
+ </TD>
+ </TR>
+
+% }
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="<% $title %>">
+
+</FORM>
+
+<SCRIPT TYPE="text/javascript">
+
+ var rownum = <% $row %>;
+
+ function possiblyAddRow() {
+ if ( ( rownum - this.getAttribute('rownum') ) == 1 ) {
+ addRow();
+ }
+ }
+
+ function addRow() {
+
+ var table = document.getElementById('DetailTable');
+ var tablebody = table.getElementsByTagName('tbody').item(0);
+
+ var row = document.createElement('TR');
+
+ var empty_cell = document.createElement('TD');
+ row.appendChild(empty_cell);
+
+ var detail_cell = document.createElement('TD');
+
+ var detail_input = document.createElement('INPUT');
+ detail_input.setAttribute('name', 'detail'+rownum);
+ detail_input.setAttribute('id', 'detail'+rownum);
+ detail_input.setAttribute('size', 60);
+ detail_input.setAttribute('maxLength', 65);
+ detail_input.setAttribute('rownum', rownum);
+ detail_input.onkeyup = possiblyAddRow;
+ detail_cell.appendChild(detail_input);
+
+ row.appendChild(detail_cell);
+
+ tablebody.appendChild(row);
+
+ rownum++;
+
+ }
+
+</SCRIPT>
+
+</BODY>
+</HTML>
+<%init>
+
+my %access_right = (
+ 'I' => 'Edit customer package invoice details',
+ 'C' => 'Edit customer package comments',
+);
+
+my %name = (
+ 'I' => 'invoice details',
+ 'C' => 'package comments',
+);
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype';
+my $detailtype = $1;
+
+my $right = $access_right{$detailtype};
+die "access denied"
+ unless $curuser->access_right($right);
+
+$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum';
+my $pkgnum = $1;
+
+my $cust_pkg = qsearchs({
+ 'table' => 'cust_pkg',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'pkgnum' => $pkgnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+
+my $part_pkg = $cust_pkg->part_pkg;
+
+my @details = $cust_pkg->cust_pkg_detail($detailtype);
+
+my $title = ( scalar(@details) ? 'Edit ' : 'Add ' ). $name{$detailtype};
+
+</%init>
diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi
new file mode 100755
index 0000000..94c0993
--- /dev/null
+++ b/httemplate/edit/cust_refund.cgi
@@ -0,0 +1,165 @@
+% if ( $link eq 'popup' ) {
+ <% include('/elements/header-popup.html', $title ) %>
+% } else {
+ <% include("/elements/header.html", $title, '') %>
+% }
+
+<% include('/elements/error.html') %>
+
+% unless ( $link eq 'popup' ) {
+ <% small_custview($custnum, $conf->config('countrydefault')) %>
+% }
+
+<FORM NAME="RefundForm" ACTION="<% $p1 %>process/cust_refund.cgi" METHOD=POST onSubmit="document.RefundForm.submit.disabled=true">
+<INPUT TYPE="hidden" NAME="popup" VALUE="<% $link %>">
+<INPUT TYPE="hidden" NAME="refundnum" VALUE="">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+<INPUT TYPE="hidden" NAME="paynum" VALUE="<% $paynum %>">
+<INPUT TYPE="hidden" NAME="_date" VALUE="<% $_date %>">
+<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>">
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="">
+<INPUT TYPE="hidden" NAME="credited" VALUE="">
+
+<BR>
+
+% if ( $cust_pay ) {
+%
+% #false laziness w/FS/FS/cust_pay.pm
+% my $payby = FS::payby->payname($cust_pay->payby);
+% my $paymask = $cust_pay->paymask;
+% my $paydate = $cust_pay->paydate;
+% if ( $cgi->param('error') ) {
+% $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01';
+% $paydate = '' unless ($paydate =~ /^\d{2,4}-\d{1,2}-01$'/);
+% }
+
+ <BR>Payment
+ <% ntable("#cccccc", 2) %>
+
+ <TR>
+ <TD ALIGN="right">Amount</TD><TD BGCOLOR="#ffffff">$<% $cust_pay->paid %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Date</TD><TD BGCOLOR="#ffffff"><% time2str("%D",$cust_pay->_date) %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Method</TD><TD BGCOLOR="#ffffff"><% $payby %> # <% $paymask %></TD>
+ </TR>
+
+% unless ( $paydate ) { # possibly other reasons: i.e. card has since expired
+ <TR>
+ <TD ALIGN="right">Expiration</TD><TD BGCOLOR="#ffffff">
+ <% include( '/elements/select-month_year.html',
+ 'prefix' => 'exp',
+ 'selected_date' => $paydate,
+ 'empty_option' => !$paydate,
+ ) %>
+ </TD>
+ </TR>
+% }
+
+%
+% #false laziness w/FS/FS/cust_main::realtime_refund_bop
+% if ( $cust_pay->paybatch =~ /^(\w+):(\w+)(:(\w+))?$/ ) {
+% my ( $processor, $auth, $order_number ) = ( $1, $2, $4 );
+%
+
+
+ <TR>
+ <TD ALIGN="right">Processor</TD><TD BGCOLOR="#ffffff"><% $processor %></TD>
+ </TR>
+% if ( length($auth) ) {
+
+ <TR>
+ <TD ALIGN="right">Authorization</TD><TD BGCOLOR="#ffffff"><% $auth %></TD>
+ </TR>
+% }
+% if ( length($order_number) ) {
+
+ <TR>
+ <TD ALIGN="right">Order number</TD><TD BGCOLOR="#ffffff"><% $order_number %></TD>
+ </TR>
+% }
+% }
+
+ </TABLE>
+% }
+
+
+<BR>Refund
+<% ntable("#cccccc", 2) %>
+
+ <TR>
+ <TD ALIGN="right">Date</TD>
+ <TD BGCOLOR="#ffffff"><% time2str("%D",$_date) %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#ffffff">$<INPUT TYPE="text" NAME="refund" VALUE="<% $refund %>" SIZE=8 MAXLENGTH=8> by <B><% FS::payby->payname($payby) %></B></TD>
+ </TR>
+
+% if ( $payby eq 'BILL' ) {
+ <TR>
+ <TD ALIGN="right">Check #</TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
+ </TR>
+% } else {
+ <INPUT TYPE="hidden" NAME="payinfo" VALUE="">
+% }
+
+ <TR>
+ <TD ALIGN="right">Reason</TD>
+ <TD BGCOLOR="#ffffff"><INPUT TYPE="text" NAME="reason" VALUE="<% $reason %>"></TD>
+ </TR>
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="Post refund">
+
+</FORM>
+
+% if ( $link eq 'popup' ) {
+ </BODY>
+ </HTML>
+% } else {
+ <% include('/elements/footer.html') %>
+% }
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Refund payment');
+
+my $conf = new FS::Conf;
+my $custnum = $cgi->param('custnum');
+my $refund = $cgi->param('refund');
+my $payby = $cgi->param('payby');
+my $payinfo = $cgi->param('payinfo');
+my $reason = $cgi->param('reason');
+my $link = $cgi->param('popup') ? 'popup' : '';
+
+my( $paynum, $cust_pay ) = ( '', '' );
+if ( $cgi->param('paynum') =~ /^(\d+)$/ ) {
+ $paynum = $1;
+ $cust_pay = qsearchs('cust_pay', { paynum=>$paynum } )
+ or die "unknown payment # $paynum";
+ $refund ||= $cust_pay->unrefunded;
+ if ( $custnum ) {
+ die "payment # $paynum is not for specified customer # $custnum"
+ unless $custnum == $cust_pay->custnum;
+ } else {
+ $custnum = $cust_pay->custnum;
+ }
+}
+die "no custnum or paynum specified!" unless $custnum;
+
+my $_date = time;
+
+my $p1 = popurl(1);
+
+my $title = 'Refund '. FS::payby->payname($payby). ' payment';
+
+</%init>
diff --git a/httemplate/edit/elements/ApplicationCommon.html b/httemplate/edit/elements/ApplicationCommon.html
new file mode 100644
index 0000000..a485d37
--- /dev/null
+++ b/httemplate/edit/elements/ApplicationCommon.html
@@ -0,0 +1,177 @@
+<%doc>
+
+Examples:
+
+ #cust_bill_pay
+ include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_bill_pay.cgi',
+ 'src_table' => 'cust_pay',
+ 'src_thing' => 'payment',
+ 'dst_table' => 'cust_bill',
+ 'dst_thing' => 'invoice',
+ )
+
+ #cust_credit_bill
+ include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_credit_bill.cgi',
+ 'src_table' => 'cust_credit',
+ 'src_thing' => 'credit',
+ 'dst_table' => 'cust_bill',
+ 'dst_thing' => 'invoice',
+ )
+
+ #cust_pay_refund
+ include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_pay_refund.cgi',
+ 'src_table' => 'cust_pay',
+ 'src_thing' => 'payment',
+ 'dst_table' => 'cust_refund',
+ 'dst_thing' => 'refund',
+ )
+
+ #cust_credit_refund
+ include('elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_credit_refund.cgi',
+ 'src_table' => 'cust_credit',
+ 'src_thing' => 'credit',
+ 'dst_table' => 'cust_refund',
+ 'dst_thing' => 'refund',
+ )
+
+</%doc>
+<% include('/elements/header-popup.html', "Apply $src_thing$to" ) %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p1. $opt{'form_action'} %>" NAME="ApplicationForm" ID="ApplicationForm" METHOD=POST>
+
+<% $src_thing %> #<B><% $src_pkeyvalue %></B><BR>
+<INPUT TYPE="hidden" NAME="<% $src_pkey %>" VALUE="<% $src_pkeyvalue %>">
+
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<TR>
+ <TD ALIGN="right">Date: </TD>
+ <TD><B><% time2str("%D", $src->_date) %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Amount: </TD>
+ <TD><B><% $money_char %><% $src->amount %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Unapplied amount: </TD>
+ <TD><B><% $money_char %><% $unapplied %></B></TD>
+</TR>
+
+% if ( $src_table eq 'cust_credit' ) {
+ <TR>
+ <TD ALIGN="right">Reason: </TD>
+ <TD><B><% $src->reason %></B></TD>
+ </TR>
+% }
+
+</TABLE>
+<BR>
+
+<SCRIPT TYPE="text/javascript">
+function changed(what) {
+ dst = what.options[what.selectedIndex].value;
+
+ if ( dst == '' ) {
+ what.form.submit.disabled=true;
+ return true;
+ }
+
+ what.form.submit.disabled=false;
+
+% foreach my $dst ( @dst ) {
+
+ if ( dst == <% $dst->$dst_pkey %> ) {
+ what.form.amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>";
+ }
+
+% }
+
+}
+</SCRIPT>
+
+Apply to:
+
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<TR>
+ <TD ALIGN="right"><% $dst_thing %>: </TD>
+ <TD><SELECT NAME="<% $dst_pkey %>" SIZE=1 onChange="changed(this)">
+<OPTION VALUE="">Select <% $dst_thing %>
+
+% foreach my $dst ( @dst ) {
+ <OPTION<% $dst->$dst_pkey eq $dst_pkeyvalue ? ' SELECTED' : '' %> VALUE="<% $dst->$dst_pkey %>">#<% $dst->$dst_pkey %> - <% time2str("%D", $dst->_date) %> - $<% $dst->$dst_unapplied %>
+% }
+
+</SELECT>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Amount: </TD>
+ <TD><% $money_char %><INPUT TYPE="text" NAME="amount" VALUE="<% $amount %>" SIZE=8 MAXLENGTH=8></TD>
+</TR>
+
+</TABLE>
+
+<BR>
+<CENTER><INPUT TYPE="submit" VALUE="Apply" NAME="submit" ID="submit" DISABLED></CENTER>
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+my %opt = @_;
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $src_thing = ucfirst($opt{'src_thing'});
+my $src_table = $opt{'src_table'};
+my $src_pkey = dbdef->table($src_table)->primary_key;
+
+my $dst_thing = ucfirst($opt{'dst_thing'});
+my $dst_table = $opt{'dst_table'};
+my $dst_pkey = dbdef->table($dst_table)->primary_key;
+my $dst_unapplied = $dst_table eq 'cust_bill' ? 'owed' : 'unapplied';
+
+my $to = $dst_table eq 'cust_refund' ? ' to Refund' : '';
+
+my($src_pkeyvalue, $amount, $dst_pkeyvalue);
+if ( $cgi->param('error') ) {
+ $src_pkeyvalue = $cgi->param($src_pkey);
+ $amount = $cgi->param('amount');
+ $dst_pkeyvalue = $cgi->param($dst_pkey);
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $src_pkeyvalue = $1;
+ $amount = '';
+ $dst_pkeyvalue = '';
+}
+
+my $otaker = getotaker;
+
+my $p1 = popurl(1);
+
+my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } );
+die "$src_thing $src_pkeyvalue not found!" unless $src;
+
+my $unapplied = $src->unapplied;
+
+my @dst = sort { $a->_date <=> $b->_date
+ or $a->$dst_pkey <=> $b->$dst_pkey
+ }
+ grep { $_->$dst_unapplied != 0 }
+ qsearch($dst_table, { 'custnum' => $src->custnum } );
+
+</%init>
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
new file mode 100644
index 0000000..d18a37d
--- /dev/null
+++ b/httemplate/edit/elements/edit.html
@@ -0,0 +1,700 @@
+<%doc>
+
+Example:
+
+ include( 'elements/edit.html',
+ 'name_singular' => #singular name for the record
+ # (preferred, will be pluralized automatically)
+ 'name' => #name for the record
+ # (deprecated, will be pluralized simplistically)
+ 'table' =>
+
+ #? 'primary_key' => #required when the dbdef doesn't know...???
+ 'labels' => {
+ 'column' => 'Label',
+ }
+
+ #listref - each item is a literal column name (or method) or hashref
+ # or (notyet) coderef
+ #if not specified all columns (except for the primary key) will be editable
+ 'fields' => [
+ 'columname',
+ { 'field' => 'another_columname',
+ 'type' => 'text', #text
+ #password
+ #money
+ #percentage
+ #checkbox
+ #select
+ #selectlayers (can now use after a tablebreak-tr-title... but not inside columnstart/columnnext/columnend)
+ #title
+ #tablebreak-tr-title
+ #columnstart
+ #columnnext
+ #columnend
+ #hidden - hidden value from object
+ #fixed - display fixed value from object or here
+ #fixed-country
+ #fixed-state
+ 'value' => 'Y', #for checkbox, title, fixed, hidden
+ 'disabled' => 0,
+ 'onchange' => 'javascript_function',
+
+ #m2 stuff only tested w/selectlayers so far
+ #might work w/select too, dunno others
+ 'm2name_table' => 'table_name',
+ 'm2name_namecol' => 'name_column',
+ #OR#
+ 'm2m_method' =>
+ #'m2m_srccol' => #opt, if not the same as this table
+ 'm2m_dstcol' => #required for now, eventuaully opt, if not the same as target table
+
+ 'm2_label' => 'Label', #
+ 'm2_new_default' => \@table_name_objects, #default
+ #m2 objects for
+ #new records
+ 'm2_error_callback' => sub { my($cgi, $object) = @_; },
+ 'm2_remove_warnings' => \%warnings, #hashref of warning
+ #messages for m2
+ #removal
+ 'm2_new_js' => 'function_name', #javascript function called
+ #on spawned rows (one arg:
+ #new_element)
+ 'm2_remove_js' => 'function_name', #js function called when
+ #a row is deleted (three
+ #args: value, text,
+ #'no_match')
+ #layer_fields & layer_values_callback only for selectlayer
+ 'layer_fields' => [
+ 'fieldname' => 'Label',
+ 'another_field' => {
+ label=>'Label',
+ type =>'text', #text, money
+ },
+ ],
+ 'layer_values_callback' =>
+ sub {
+ my( $cgi, $object ) = @_;
+ { 'layer' => { 'fieldname' => 'current_value',
+ 'fieldname2' => 'field2value',
+ ...
+ },
+ 'layer2' => { 'l2fieldname' => 'l2value',
+ ...
+ },
+ ...
+ };
+ },
+ },
+ ]
+
+ 'menubar' => '', #menubar arrayref
+
+ #agent virtualization
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Access Right Name',
+ 'agent_clone_extra_sql' => '', #if provided, this overrides the extra_sql
+ #implementing agent virt, for clone
+ #operations. i.e. pass "1=1" to allow
+ #cloning anything
+
+ 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
+
+ # overrides default popurl(1)."process/$table.html"
+ 'post_url' => popurl(1).'process/something',
+
+ #we're in a popup (no title/menu/searchboxes)
+ 'popup' => 1,
+
+ ###
+ # HTML callbacks
+ ###
+
+ 'body_etc' => '', # Additional BODY attributes, i.e. onLoad=""
+
+ 'html_init' => '', #after the header/menubar
+
+ #string or coderef of additional HTML to add before </TABLE>
+ 'html_table_bottom' => '',
+
+ #after </TABLE> but before the submit
+ 'html_bottom' => '', #string
+ 'html_bottom' => sub {
+ my $object = shift;
+ # ...
+ "html_string";
+ },
+
+ #at the very bottom (well, as low as you can go from here)
+ 'html_foot' => '',
+
+ ###
+ # initialization callbacks
+ ###
+
+ ###global callbacks
+
+ #always run if provided, after decoding long CGI "redirect=" responses but
+ # before object creation/search
+ # (useful if you have a long form that might trigger redirect= and you need
+ # to do things with $cgi params - they're not decoded in the calling
+ # <%init> block yet)
+ 'begin_callback' = sub { my( $cgi, $fields_listref, $opt_hashref ) = @_; },
+
+ #always run, after the mode-specific object creation/search
+ 'end_callback' = sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; },
+
+ ###mode-specific callbacks
+
+ #run when re-displaying with an error
+ 'error_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; },
+
+ #run when editing
+ 'edit_callback' => sub { my( $cgi, $object, $fields_listref ) = @_; },
+
+ # returns a hashref for the new object
+ 'new_hashref_callback'
+
+ # returns the new object iself (otherwise, ->new is called)
+ 'new_object_callback'
+
+ #run when adding
+ 'new_callback' => sub { my( $cgi, $object, $fields_listref ) = @_; },
+
+ #run when cloning
+ 'clone_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; },
+
+ ###display callbacks
+
+ #run before display to return a different value
+ 'value_callback' => sub { my( $columname, $value ) = @_; },
+
+ #run before display to manipulate element of the 'fields' arrayref
+ 'field_callback' => sub { my( $cgi, $object, $field_hashref ) = @_; },
+
+ );
+
+</%doc>
+
+<% include('/elements/header'. ( $opt{popup} ? '-popup' : '' ). '.html',
+ $title,
+ include( '/elements/menubar.html', @menubar ),
+ $opt{'body_etc'},
+ )
+%>
+
+<% defined($opt{'html_init'})
+ ? ( ref($opt{'html_init'})
+ ? &{$opt{'html_init'}}()
+ : $opt{'html_init'}
+ )
+ : ''
+%>
+
+<% include('/elements/error.html') %>
+
+% my $url = $opt{'post_url'} || popurl(1)."process/$table.html";
+
+<FORM ACTION="<% $url %>" METHOD=POST NAME="edit_topform">
+
+<INPUT TYPE="hidden" NAME="svcdb" VALUE="<% $table %>">
+<INPUT TYPE="hidden" NAME="<% $pkey %>" VALUE="<% $clone ? '' : $object->$pkey() %>">
+
+<FONT SIZE="+1"><B>
+<% ( $opt{labels} && exists $opt{labels}->{$pkey} )
+ ? $opt{labels}->{$pkey}
+ : $pkey
+%>
+</B></FONT>
+#<% ( !$clone && $object->$pkey() ) || "(NEW)" %>
+
+% my $tablenum = 0;
+<TABLE ID="TableNumber<% $tablenum++ %>" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+% my $g_row = 0;
+% my @g_row_stack = ();
+% foreach my $f ( map { ref($_) ? $_ : {'field'=>$_} }
+% @$fields
+% ) {
+%
+% my $trash = &{ $opt{'field_callback'} }( $cgi, $object, $f )
+% if $opt{'field_callback'};
+%
+% my $field = $f->{'field'};
+% my $type = $f->{'type'} ||= 'text';
+%
+% my $label = ( $opt{labels} && exists $opt{labels}->{$field} )
+% ? $opt{labels}->{$field}
+% : $field;
+%
+% my $onchange = $f->{'onchange'};
+%
+% my $layer_values = {};
+% $layer_values = &{ $f->{'layer_values_callback'} }( $cgi, $object )
+% if $f->{'layer_values_callback'}
+% && ! $f->{'m2name_table'}
+% && ! $f->{'m2m_method'};
+%
+% warn "layer values: ". Dumper($layer_values)
+% if $opt{'debug'};
+%
+% my %include_common = (
+%
+% #text and derivitives
+% 'size' => $f->{'size'},
+% 'maxlength' => $f->{'maxlength'},
+%
+% #checkbox, title, fixed, hidden
+% #& deprecated weird value hashref used only by reason.html
+% 'value' => $f->{'value'},
+%
+% #select(-*)
+% 'options' => $f->{'options'},
+% 'labels' => $f->{'labels'},
+% 'multiple' => $f->{'multiple'},
+% 'disable_empty' => $f->{'disable_empty'},
+% #select-reason
+% 'reason_class' => $f->{'reason_class'},
+%
+% #selectlayers
+% 'layer_fields' => $f->{'layer_fields'},
+% 'layer_values' => $layer_values,
+% 'html_between' => $f->{'html_between'},
+%
+% #umm. for select-agent_types at least
+% 'disabled' => $f->{'disabled'},
+% );
+%
+% #selectlayers, others?
+% $include_common{$_} = $f->{$_}
+% foreach grep exists($f->{$_}),
+% qw( js_only html_only select_only layers_only cell_style);
+%
+% #select-*
+% $include_common{$_} = $f->{$_}
+% foreach grep exists($f->{$_}), qw( empty_label );
+%
+% #select-table, checkboxes-table
+% $include_common{$_} = $f->{$_}
+% foreach grep exists($f->{$_}), qw( table name_col );
+%
+% #checkboxes-table
+% $include_common{$_} = $f->{$_}
+% foreach grep exists($f->{$_}), qw( target_table link_table );
+%
+% #*-table
+% $include_common{$_} = $f->{$_}
+% foreach grep exists($f->{$_}), qw( hashref agent_virt agent_null_right );
+%
+% if ( $type eq 'tablebreak-tr-title' ) {
+% $include_common{'table_id'} = 'TableNumber'. $tablenum++
+% }
+%
+% my $layer_prefix_on = '';
+%
+% my $include_sub = sub {
+% my %opt = @_;
+%
+% my $fieldnum = delete $opt{'fieldnum'};
+%
+% my $include = $type;
+% $include = "input-$include" if $include =~ /^(text|money|percentage)$/;
+% $include = "tr-$include" unless $include =~ /^(hidden|tablebreak|column)/;
+%
+% $include_common{'layer_prefix'} = "$field$fieldnum."
+% if $layer_prefix_on;
+%
+% my @include =
+% ( "/elements/$include.html",
+% 'field' => "$field$fieldnum",
+% 'id' => "$field$fieldnum", #separate?
+% 'label_id' => $field."_label$fieldnum", #don't want field0_label0...
+% %include_common,
+% %opt,
+% );
+% @include;
+% };
+%
+% unless ( $type =~ /^column/ ) {
+% $g_row = 1 if $type eq 'tablebreak-tr-title';
+% $g_row++;
+% $g_row++ if $type eq 'title';
+% } else {
+% if ( $type eq 'columnstart' ) {
+% push @g_row_stack, $g_row;
+% $g_row = 0;
+% #} elsif ( $type eq 'columnnext' ) {
+% } elsif ( $type eq 'columnend' ) {
+% $g_row = pop @g_row_stack;
+% }
+%
+% }
+%
+% my $fieldnum = '';
+% my $curr_value = '';
+% if ( $f->{'m2name_table'} || $f->{'m2m_method'} ) { #XXX test this for all
+% #types of fields
+% my($table, $col);
+% if ( $f->{'m2name_table'} ) {
+% $table = $f->{'m2name_table'};
+% $col = $f->{'m2name_namecol'};
+% } elsif ( $f->{'m2m_method'} ) {
+% $table = $f->{'m2m_method'};
+% $col = $f->{'m2m_dstcol'};
+% }
+% $fieldnum = 0;
+% $layer_prefix_on = 1;
+% #print out the fields for the existing m2s
+% my @existing = ();
+% if ( $mode eq 'error' ) {
+% @existing = &{ $f->{'m2_error_callback'} }( $cgi, $object );
+% } elsif ( $object->$pkey() ) { # $mode eq 'edit'||'clone'
+% @existing = $object->$table();
+% warn scalar(@existing). " from $object->$table: ". join('/', @existing)
+% if $opt{'debug'};
+% } elsif ( $f->{'m2_new_default'} ) { # && $mode eq 'new'
+% @existing = @{ $f->{'m2_new_default'} };
+% }
+% foreach my $name_obj ( @existing ) {
+%
+% my $ex_label = '<INPUT TYPE="button" VALUE="X" TITLE="Remove this '.
+% lc($f->{'m2_label'}).
+% qq(" onClick="remove_$field($fieldnum);").
+% ' STYLE="color:#ff0000;font-weight:bold;'.
+% 'padding-left:2px;padding-right:2px"'.
+% '>&nbsp;'. ($f->{'m2_label'} || $field ). ' ';
+%
+% if ( $f->{'layer_values_callback'} ) {
+% my %switches = ( 'mode' => $mode );
+% $layer_values =
+% &{ $f->{'layer_values_callback'} }( $cgi, $name_obj, \%switches );
+% }
+% warn "layer values: ". Dumper($layer_values)
+% if $opt{'debug'};
+%
+% my @existing = &{ $include_sub }(
+% 'label' => $ex_label,
+% 'fieldnum' => $fieldnum,
+% 'curr_value' => $name_obj->$col(),
+% 'onchange' => $onchange,
+% 'layer_values' => $layer_values,
+% 'cell_style' => ( $fieldnum ? 'border-top:1px solid black' : '' ),
+% );
+
+ <% include( @existing ) %>
+
+% $fieldnum++;
+% $g_row++;
+% }
+% #$field .= $fieldnum;
+% $onchange .= "\nspawn_$field(what);";
+% } else {
+% if ( $f->{curr_value_callback} ) {
+% $curr_value = &{ $f->{curr_value_callback} }( $cgi, $object, $field ),
+% } else {
+% $curr_value = $object->$field();
+% }
+% $curr_value = &{ $opt{'value_callback'} }( $f->{'field'}, $curr_value )
+% if $opt{'value_callback'} && $mode ne 'error';
+% }
+%
+% my @include = &{ $include_sub }(
+% 'label' => $label,
+% 'fieldnum' => $fieldnum,
+% 'curr_value' => $curr_value,
+% 'object' => $object,
+% 'cgi' => $cgi,
+% 'onchange' => $onchange,
+% ( $fieldnum ? ('cell_style' => 'border-top:1px solid black') : () ),
+% );
+
+ <% include( @include ) %>
+
+% if ( $f->{'m2name_table'} || $f->{'m2m_method'} ) {
+
+ <SCRIPT TYPE="text/javascript">
+
+ var <%$field%>_rownum = <% $g_row %>;
+ var <%$field%>_fieldnum = <% $fieldnum %>;
+
+ function spawn_<%$field%>(what) {
+
+ // only spawn if we're the last element... return if not
+
+ var field_regex = /(\d+)$/;
+ var match = field_regex.exec(what.name);
+ if ( !match ) {
+ alert(what.name + " didn't match?!");
+ return;
+ }
+ if ( match[1] != <%$field%>_fieldnum ) {
+ return;
+ }
+
+ // change the label on the last entry & add a remove button
+ var prev_label = document.getElementById('<% $field %>_label' + <%$field%>_fieldnum );
+ prev_label.innerHTML = '<INPUT TYPE="button" VALUE="X" TITLE="Remove this <% lc($f->{'m2_label'}) %>" onClick="remove_<% $field %>(' + <%$field%>_fieldnum + ');" STYLE="color:#ff0000;font-weight:bold;padding-left:2px;padding-right:2px" >&nbsp;<% $f->{'m2_label'} || $field %>';
+
+ <%$field%>_fieldnum++;
+
+ //get the new widget
+
+% $include[0] =~ s(^/elements/tr-)(/elements/);
+% my @layer_opt = ( @include,
+% 'field' => $field."MAGIC_NUMBER",
+% 'id' => $field."MAGIC_NUMBER",
+% 'layer_prefix' => $field."MAGIC_NUMBER.",
+% );
+% warn @layer_opt if $opt{'debug'};
+
+ var newrow = <% include(@layer_opt, html_only=>1) |js_string %>;
+
+% if ( $type eq 'selectlayers' ) { #until the rest have html/js_only
+ var newfunc = <% include(@layer_opt, js_only =>1) |js_string %>;
+% } else {
+ var newfunc = '';
+% }
+
+ // substitute in the new field name
+ var magic_regex = /MAGIC_NUMBER/g;
+ newrow = newrow.replace( magic_regex, <%$field%>_fieldnum );
+ newfunc = newfunc.replace( magic_regex, <%$field%>_fieldnum );
+
+ // evaluate new_func
+ if (window.ActiveXObject) {
+ window.execScript(newfunc);
+ } else { /* (window.XMLHttpRequest) */
+ //window.eval(newfunc);
+ setTimeout(newfunc, 0);
+ }
+
+ // add new row
+
+ //hmm, can't use selectlayers after a tablebreak-title for now
+ var table = document.getElementById('TableNumber<% $tablenum-1 %>');
+
+ var row = table.insertRow(<%$field%>_rownum++);
+
+ var label_cell = document.createElement('TD');
+
+ label_cell.id = '<% $field %>_label' + <%$field%>_fieldnum;
+
+ label_cell.style.textAlign = "right";
+ label_cell.style.verticalAlign = "top";
+ label_cell.style.borderTop = "1px solid black";
+ label_cell.style.paddingTop = "5px";
+
+ label_cell.innerHTML = '<% $label %>';
+
+ row.appendChild(label_cell);
+
+ var widget_cell = document.createElement('TD');
+
+ widget_cell.style.borderTop = "1px solid black";
+ widget_cell.style.paddingTop = "3px";
+
+ widget_cell.innerHTML = newrow;
+
+ row.appendChild(widget_cell);
+
+% if ( $f->{'m2_new_js'} ) {
+ // take out items selected in previous dropdowns
+ var new_element = document.getElementById("<%$field%>" + <%$field%>_fieldnum );
+ <% $f->{'m2_new_js'} %>(new_element);
+
+ if ( new_element.length < 2 ) {
+ //just the ** Select new **, so don't display the row
+ row.style.display = 'none';
+ }
+% }
+
+ }
+
+ function remove_<%$field%>(remove_fieldnum) {
+ //alert("remove <%$field%> " + remove_fieldnum);
+ var select = document.getElementById('<%$field%>' + remove_fieldnum);
+
+ if ( ! select ) {
+ alert("can't find element <%$field%>" + remove_fieldnum);
+ return;
+ }
+
+% my $warnings = $f->{'m2_remove_warnings'};
+% if ( $warnings ) {
+ var sel_value = select.options[select.selectedIndex].value;
+% foreach my $value ( keys %$warnings ) {
+ if ( sel_value == '<% $value %>' ) {
+ if ( ! confirm( <% $warnings->{$value} |js_string %> ) ) {
+ return;
+ }
+ }
+% }
+% }
+
+ select.disabled = 'disabled'; // this seems to prevent it from being submitted on tested browsers so far (IE, moz, konq at least)
+ var label_td = document.getElementById('<%$field%>_label' + remove_fieldnum );
+ label_td.parentNode.style.display = 'none';
+
+% if ( $f->{m2_remove_js} ) {
+ var opt = select.options[select.selectedIndex];
+ <% $f->{m2_remove_js} %>( opt.value, opt.text, 'no_match');
+% }
+
+ }
+
+ </SCRIPT>
+
+% }
+
+% }
+
+<% ref( $opt{'html_table_bottom'} )
+ ? &{ $opt{'html_table_bottom'} }( $object )
+ : $opt{'html_table_bottom'}
+%>
+
+</TABLE>
+
+<% ref( $opt{'html_bottom'} )
+ ? &{ $opt{'html_bottom'} }( $object )
+ : $opt{'html_bottom'}
+%>
+
+<BR>
+
+<INPUT TYPE="submit" ID="submit" VALUE="<% ( !$clone && $object->$pkey() ) ? "Apply changes" : "Add $opt{'name'}" %>">
+
+</FORM>
+
+<% ref( $opt{'html_foot'} )
+ ? &{ $opt{'html_foot'} }( $object )
+ : $opt{'html_foot'}
+%>
+
+<% include("/elements/footer.html") %>
+<%init>
+
+my(%opt) = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+#false laziness w/process.html
+my $table = $opt{'table'};
+my $class = "FS::$table";
+my $pkey = dbdef->table($table)->primary_key; #? $opt{'primary_key'} ||
+my $fields = $opt{'fields'}
+ #|| [ grep { $_ ne $pkey } dbdef->table($table)->columns ];
+ || [ grep { $_ ne $pkey } fields($table) ];
+#my @actualfields = map { ref($_) ? $_->{'field'} : $_ } @$fields;
+
+if ( $cgi->param('redirect') ) {
+ my $session = $cgi->param('redirect');
+ my $pref = $curuser->option("redirect$session");
+ die "unknown redirect session $session\n" unless length($pref);
+ $cgi = new CGI($pref);
+}
+
+&{$opt{'begin_callback'}}( $cgi, $fields, \%opt )
+ if $opt{'begin_callback'};
+
+my %qsearch = (
+ 'table' => $table,
+ 'extra_sql' => ( $opt{'agent_virt'}
+ ? ' AND '. $curuser->agentnums_sql(
+ 'null_right' => $opt{'agent_null_right'}
+ )
+ : ''
+ ),
+);
+
+my $mode;
+my $object;
+my $clone = '';
+if ( $cgi->param('error') ) {
+
+ $mode = 'error';
+
+ $object = $class->new( {
+ map { $_ => scalar($cgi->param($_)) } fields($table)
+ });
+
+ &{$opt{'error_callback'}}($cgi, $object, $fields, \%opt )
+ if $opt{'error_callback'};
+
+} elsif ( $cgi->param('clone') =~ /^(\d+)$/ ) {
+
+ $mode = 'clone';
+
+ $clone = $1;
+
+ $qsearch{'extra_sql'} = ' AND '. $opt{'agent_clone_extra_sql'}
+ if $opt{'agent_clone_extra_sql'};
+
+ $object = qsearchs({ %qsearch, 'hashref' => { $pkey => $clone } });
+
+ &{$opt{'clone_callback'}}($cgi, $object, $fields, \%opt )
+ if $opt{'clone_callback'};
+
+ #$object->$pkey('');
+
+ $opt{action} ||= 'Add';
+
+} elsif ( $cgi->keywords || $cgi->param($pkey) ) { #editing
+
+ $mode = 'edit';
+
+ my $value;
+ if ( $cgi->param($pkey) ) {
+ $value = $cgi->param($pkey)
+ } else {
+ my( $query ) = $cgi->keywords;
+ $value = $query;
+ }
+ $value =~ /^(\d+)$/ or die "unparsable $pkey";
+ $object = qsearchs({ %qsearch, 'hashref' => { $pkey => $1 } })
+ or die "$pkey $1 not found in $table";
+
+ warn "$table $pkey => $1"
+ if $opt{'debug'};
+
+ &{$opt{'edit_callback'}}($cgi, $object, $fields)
+ if $opt{'edit_callback'};
+
+} else { #adding
+
+ $mode = 'new';
+
+ my $hashref = $opt{'new_hashref_callback'}
+ ? &{$opt{'new_hashref_callback'}}
+ : {};
+
+ $object = $opt{'new_object_callback'}
+ ? &{$opt{'new_object_callback'}}( $cgi, $hashref, $fields, \%opt )
+ : $class->new( $hashref );
+
+ &{$opt{'new_callback'}}($cgi, $object, $fields)
+ if $opt{'new_callback'};
+
+}
+
+&{$opt{'end_callback'}}( $cgi, $object, $fields, \%opt )
+ if $opt{'end_callback'};
+
+$opt{action} ||= $object->$pkey() ? 'Edit' : 'Add';
+
+my $title = $opt{action}. ' '. $opt{name};
+
+my $viewall_url = $p . ( $opt{'viewall_dir'} || 'search' ) . "/$table.html";
+$viewall_url = $opt{'viewall_url'} if $opt{'viewall_url'};
+
+my @menubar = ();
+if ( $opt{'menubar'} ) {
+ @menubar = @{ $opt{'menubar'} };
+} else {
+ my $items = $opt{'name'} ? $opt{'name'}.'s' : PL($opt{'name_singular'});
+ @menubar = (
+ "View all $items" => $viewall_url,
+ );
+}
+
+</%init>
diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html
new file mode 100644
index 0000000..0b64120
--- /dev/null
+++ b/httemplate/edit/elements/svc_Common.html
@@ -0,0 +1,122 @@
+<% include( 'edit.html',
+
+ 'menubar' => [],
+
+ 'error_callback' => sub {
+ my( $cgi, $svc_x ) = @_;
+ #$svcnum = $svc_x->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+
+ $part_svc = qsearchs( 'part_svc', { svcpart=>$svcpart });
+ die "No part_svc entry!" unless $part_svc;
+
+ $svc_x->setfield('svcpart', $svcpart);
+ },
+
+ 'edit_callback' => sub {
+ my( $cgi, $svc_x ) = @_;
+ #$svcnum = $svc_x->svcnum;
+ my $cust_svc = $svc_x->cust_svc
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum = $cust_svc->pkgnum;
+ $svcpart = $cust_svc->svcpart;
+
+ $part_svc = qsearchs ('part_svc', { svcpart=>$svcpart });
+ die "No part_svc entry!" unless $part_svc;
+ },
+
+ 'new_hashref_callback' => sub {
+ #my( $cgi, $svc_x ) = @_;
+
+ { svcpart => $svcpart };
+
+ },
+
+ 'new_callback' => sub {
+ my( $cgi, $svc_x ) = @_;;
+
+ $part_svc = qsearchs( 'part_svc', { svcpart=>$svcpart });
+ die "No part_svc entry!" unless $part_svc;
+
+ #$svcnum='';
+
+ $svc_x->set_default_and_fixed;
+
+ },
+
+ 'field_callback' => sub {
+ my ($cgi, $object, $f) = @_;
+ my $columndef = $part_svc->part_svc_column($f->{'field'});
+ my $flag = $columndef->columnflag;
+ if ( $flag eq 'F' ) {
+ $f->{'type'} = length($columndef->columnvalue)
+ ? 'fixed'
+ : 'hidden';
+ $f->{'value'} = $columndef->columnvalue;
+ }
+ },
+
+ 'html_init' => sub {
+ my $cust_main;
+ if ( $pkgnum ) {
+ my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum});
+ $cust_main = $cust_pkg->cust_main if $cust_pkg;
+ }
+ $cust_main
+ ? include( '/elements/small_custview.html',
+ $cust_main,
+ '',
+ 1,
+ popurl(2). "view/cust_main.cgi"
+ ). '<BR>'
+ : '';
+
+ },
+
+ 'html_table_bottom' => sub {
+ my $svc_x = shift;
+ my $html = '';
+ foreach my $field ($svc_x->virtual_fields) {
+ if ($part_svc->part_svc_column($field)->columnflag ne 'F'){
+ # If the flag is X, it won't even show up
+ # in $svc_acct->virtual_fields.
+ $html .=
+ $svc_x->pvf($field)->widget( 'HTML',
+ 'edit',
+ $svc_x->getfield($field)
+ );
+ }
+ }
+ $html;
+ },
+
+ 'html_bottom' => sub {
+ qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!.
+ qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!;
+ },
+
+ %opt #pass through/override params
+ )
+%>
+<%init>
+
+my %opt = @_;
+
+#my( $svcnum, $pkgnum, $svcpart, $part_svc );
+my( $pkgnum, $svcpart, $part_svc );
+
+#get & untaint pkgnum & svcpart
+if ( ! $cgi->param('error')
+ && $cgi->param('pkgnum') && $cgi->param('svcpart')
+ )
+{
+ $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum';
+ $pkgnum = $1;
+ $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart';
+ $svcpart = $1;
+ #$cgi->delete_all(); #so edit.html treats this correctly as new??
+}
+
+</%init>
diff --git a/httemplate/edit/inventory_class.html b/httemplate/edit/inventory_class.html
new file mode 100644
index 0000000..3ab47fe
--- /dev/null
+++ b/httemplate/edit/inventory_class.html
@@ -0,0 +1,16 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Inventory Class',
+ 'table' => 'inventory_class',
+ 'labels' => {
+ 'classnum' => 'Class number',
+ 'classname' => 'Class name',
+ },
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/invoice_logo.html b/httemplate/edit/invoice_logo.html
new file mode 100644
index 0000000..e1c6149
--- /dev/null
+++ b/httemplate/edit/invoice_logo.html
@@ -0,0 +1,136 @@
+<% include("/elements/header.html", "Edit $type2desc{$type} invoice logo",
+ menubar(
+ 'View all invoice templates' => $p.'browse/invoice_template.html'
+ )
+ )
+%>
+
+% if ( $error ) {
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error %></FONT>
+ <BR><BR>
+% }
+
+% if ( $cgi->param('msg') ) {
+ <FONT SIZE="+1"><B><% $cgi->param('msg') |h %></B></FONT>
+ <BR><BR>
+% }
+
+% if ( $mode eq 'upload' ) {
+ <FORM ACTION="invoice_logo.html" METHOD="POST" ENCTYPE="multipart/form-data">
+ <INPUT TYPE="hidden" NAME="mode" VALUE="preview">
+% } elsif ( $mode eq 'preview' ) {
+ <FORM ACTION="process/invoice_logo.html" METHOD="POST">
+ <INPUT TYPE="hidden" NAME="preview_session" VALUE="<% $session %>">
+% }
+
+<INPUT TYPE="hidden" NAME="type" VALUE="<% $type %>">
+<INPUT TYPE="hidden" NAME="name" VALUE="<% $name %>">
+
+<% include('/elements/table-grid.html') %>
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Current logo</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">New logo preview</TH>
+</TR>
+
+<TR>
+
+ <TD CLASS="grid" BGCOLOR="#ffffff">
+
+% if ( $type eq 'png' ) {
+
+ <IMG SRC="<% $p %>view/logo.cgi?type=png;name=<% $name %>">
+
+% } elsif ( $type eq 'eps' ) {
+
+ <i>EPS preview not yet supported</i>
+
+% }
+
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="#ffffff">
+
+% if ( $mode eq 'upload' ) {
+
+ Upload new logo (.<%uc($type)%> format): <INPUT TYPE="file" NAME="new_logo">
+ <BR><INPUT TYPE="submit" NAME="submit" VALUE="Upload">
+
+% } elsif ( $mode eq 'preview' ) {
+
+ <IMG SRC="<% $p %>view/logo.cgi?type=png;preview_session=<% $session %>">
+
+% }
+
+ </TD>
+
+
+</TR>
+
+</TABLE>
+
+% if ( $mode eq 'preview' ) {
+ <BR>
+ <INPUT TYPE="submit" NAME="submit" VALUE="Change logo">
+% }
+
+</FORM>
+
+<% include("/elements/footer.html") %>
+
+<%once>
+
+my %type2desc = (
+ 'png' => 'online',
+ 'eps' => 'Print/PDF (typeset)',
+);
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+my $type = $cgi->param('type');
+
+$cgi->param('name') =~ /^([^\.\/]*)$/ or die "illegal name";
+my $name = $1;
+
+$cgi->param('mode') =~ /^(\w*)$/ or die "illegal mode";
+my $mode = $1 || 'upload';
+
+my $error = '';
+my $session = '';
+if ( $mode eq 'preview' ) {
+
+ my $fh = $cgi->upload('new_logo');
+
+ if ( defined $fh ) {
+
+ local $/;
+ my $logo_data = <$fh>;
+
+ $session = int(rand(4294967296)); #XXX
+ my $pref = new FS::access_user_pref({
+ 'usernum' => $FS::CurrentUser::CurrentUser->usernum,
+ 'prefname' => "logo_preview$session",
+ 'prefvalue' => encode_base64($logo_data),
+ 'expiration' => time + 3600, #1h? 1m?
+ });
+ my $pref_error = $pref->insert;
+ if ( $pref_error ) {
+ die "FATAL: couldn't set preview cookie: $pref_error\n";
+ }
+
+ } else {
+
+ $mode = 'upload';
+ $error = 'No file uploaded';
+
+ }
+
+}
+
+</%init>
diff --git a/httemplate/edit/invoice_template.html b/httemplate/edit/invoice_template.html
new file mode 100644
index 0000000..9cec62c
--- /dev/null
+++ b/httemplate/edit/invoice_template.html
@@ -0,0 +1,69 @@
+<% include("/elements/header.html", "Edit $type2desc{$type} invoice template",
+ menubar(
+ 'View all invoice templates' => $p.'browse/invoice_template.html'
+ )
+ )
+%>
+
+<FORM ACTION="process/invoice_template.html" METHOD="POST">
+<INPUT TYPE="hidden" NAME="confname" VALUE="<% $confname %>">
+
+% if ( $type eq 'html' ) {
+
+ <% include('/elements/htmlarea.html',
+ 'field' => 'value',
+ 'curr_value' => $value,
+ 'height' => 800,
+ )
+ %>
+
+% } else {
+
+ <TEXTAREA NAME="value" ROWS=30 COLS=80 WRAP="off"><%$value |h %></TEXTAREA>
+
+% }
+
+<BR><BR>
+<INPUT TYPE="submit" VALUE="Change template">
+
+</FORM>
+
+<% include("/elements/footer.html") %>
+
+<%once>
+
+my %type2desc = (
+ 'html' => 'HTML',
+ 'latex' => 'Print/PDF (typeset)',
+ 'text' => 'Plaintext',
+);
+
+my %type2base = (
+ 'html' => 'invoice_html',
+ 'latex' => 'invoice_latex',
+ 'text' => 'invoice_template',
+);
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $type = $cgi->param('type');
+my $name = $cgi->param('name');
+my $suffix = $cgi->param('suffix');
+
+#XXX type handling, just testing this out for now
+
+my $conf = new FS::Conf;
+
+my $value = length($name)
+ ? join("\n", $conf->config_orbase($type2base{$type}.$suffix, $name) )
+ : join("\n", $conf->config($type2base{$type}.$suffix) );
+
+my $confname = length($name)
+ ? $type2base{$type}.$suffix. '_'. $name
+ : $type2base{$type}.$suffix;
+
+</%init>
diff --git a/httemplate/edit/msgcat.cgi b/httemplate/edit/msgcat.cgi
new file mode 100755
index 0000000..85b3008
--- /dev/null
+++ b/httemplate/edit/msgcat.cgi
@@ -0,0 +1,54 @@
+<% header("Edit Message catalog" ) %>
+<BR>
+
+<% include('/elements/error.html') %>
+
+<% $widget->html %>
+
+ </TABLE>
+ </BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $widget = new HTML::Widgets::SelectLayers(
+ 'selected_layer' => 'en_US',
+ 'options' => { 'en_US'=>'en_US' },
+ 'form_action' => 'process/msgcat.cgi',
+ 'layer_callback' => sub {
+ my $layer = shift;
+ my $html = qq!<INPUT TYPE="hidden" NAME="locale" VALUE="$layer">!.
+ "<BR>Messages for locale $layer<BR>". table().
+ "<TR><TH COLSPAN=2>Code</TH>".
+ "<TH>Message</TH>";
+ $html .= "<TH>en_US Message</TH>" unless $layer eq 'en_US';
+ $html .= '</TR>';
+
+ #foreach my $msgcat ( sort { $a->msgcode cmp $b->msgcode }
+ # qsearch('msgcat', { 'locale' => $layer } ) ) {
+ foreach my $msgcat ( qsearch('msgcat', { 'locale' => $layer } ) ) {
+ $html .=
+ '<TR><TD>'. $msgcat->msgnum. '</TD><TD>'. $msgcat->msgcode. '</TD>'.
+ '<TD><INPUT TYPE="text" SIZE=32 '.
+ qq! NAME="!. $msgcat->msgnum. '" '.
+ qq!VALUE="!. ($cgi->param($msgcat->msgnum)||$msgcat->msg). qq!"></TD>!;
+ unless ( $layer eq 'en_US' ) {
+ my $en_msgcat = qsearchs('msgcat', {
+ 'locale' => 'en_US',
+ 'msgcode' => $msgcat->msgcode,
+ } );
+ $html .= '<TD>'. $en_msgcat->msg. '</TD>';
+ }
+ $html .= '</TR>';
+ }
+
+ $html .= '</TABLE><BR><INPUT TYPE="submit" VALUE="Apply changes">';
+
+ $html;
+ },
+
+);
+
+</%init>
diff --git a/httemplate/edit/part_bill_event.cgi b/httemplate/edit/part_bill_event.cgi
new file mode 100755
index 0000000..3b51141
--- /dev/null
+++ b/httemplate/edit/part_bill_event.cgi
@@ -0,0 +1,570 @@
+<% include('/elements/header.html',
+ "$action Invoice Event Definition",
+ menubar(
+ 'View all invoice events' => popurl(2). 'browse/part_bill_event.cgi',
+ )
+ )
+%>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% popurl(1) %>process/part_bill_event.cgi" NAME="editEvent" METHOD=POST>
+<INPUT TYPE="hidden" NAME="eventpart" VALUE="<% $part_bill_event->eventpart %>">
+Invoice Event #<% $hashref->{eventpart} ? $hashref->{eventpart} : "(NEW)" %>
+
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TD ALIGN="right">Event name </TD>
+ <TD><INPUT TYPE="text" NAME="event" VALUE="<% $hashref->{event} %>"></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">For </TD>
+ <TD>
+ <SELECT NAME="payby" <% $hashref->{eventpart} ? '' : 'MULTIPLE SIZE=7'%>>
+% tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2longname;
+% foreach my $payby ( keys %payby ) {
+ <OPTION VALUE="<% $payby %>"<% ($part_bill_event->payby eq $payby) ? ' SELECTED' : '' %>><% $payby{$payby} %></OPTION>
+% }
+ </SELECT> customers
+ </TD>
+ </TR>
+% my $days = $hashref->{seconds}/86400;
+
+
+ <TR>
+ <TD ALIGN="right">After</TD>
+ <TD><INPUT TYPE="text" NAME="days" VALUE="<% $days %>"> days</TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Test event</TD>
+ <TD>
+ <SELECT NAME="freq">
+% tie my %freq, 'Tie::IxHash', '1d' => 'daily', '1m' => 'monthly';
+% foreach my $freq ( keys %freq ) {
+%
+
+
+ <OPTION VALUE="<% $freq %>"<% ($part_bill_event->freq eq $freq) ? ' SELECTED' : '' %>><% $freq{$freq} %></OPTION>
+% }
+
+
+ </SELECT>
+ </TD>
+ </TR>
+
+
+ <TR>
+ <TD ALIGN="right">Disabled</TD>
+ <TD>
+ <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD VALIGN="top" ALIGN="right">Action</TD>
+ <TD>
+%
+%
+%#print ntable();
+%
+%sub select_pkgpart {
+% my $label = shift;
+% my $plandata = shift;
+% my %selected = map { $_=>1 } split(/,\s*/, $plandata->{$label});
+% qq(<SELECT NAME="$label" MULTIPLE>).
+% join("\n", map {
+% '<OPTION VALUE="'. $_->pkgpart. '"'.
+% ( $selected{$_->pkgpart} ? ' SELECTED' : '' ).
+% '>'. $_->pkg. ' - '. $_->comment
+% } qsearch('part_pkg', { 'disabled' => '' } ) ).
+% '</SELECT>';
+%}
+%
+%sub select_agentnum {
+% my $plandata = shift;
+% #my $agentnum = $plandata->{'agentnum'};
+% my %agentnums = map { $_=>1 } split(/,\s*/, $plandata->{'agentnum'});
+% '<SELECT NAME="agentnum" MULTIPLE>'.
+% join("\n", map {
+% '<OPTION VALUE="'. $_->agentnum. '"'.
+% ( $agentnums{$_->agentnum} ? ' SELECTED' : '' ).
+% '>'. $_->agent
+% } qsearch('agent', { 'disabled' => '' } ) ).
+% '</SELECT>';
+%}
+%
+%sub honor_dundate {
+% my $label = shift;
+% my $plandata = shift;
+% '<TABLE>'.
+% '<TR><TD ALIGN="right">Allow delay until dun date? </TD>'.
+% qq(<TD><INPUT TYPE="checkbox" NAME="$label" VALUE="$label => 1," ).
+% ( $plandata->{$label} eq "$label => 1," ? 'CHECKED' : '' ).
+% '>'.
+% '</TD></TR>'.
+% '</TABLE>'
+%}
+%
+%my $conf = new FS::Conf;
+%my $money_char = $conf->config('money_char') || '$';
+%
+%my $late_taxclass = '';
+%my $late_percent_taxclass = '';
+%if ( $conf->exists('enable_taxclasses') ) {
+% $late_taxclass =
+% '<BR>Taxclass '.
+% include('/elements/select-taxclass.html',
+% 'curr_value' => '%%%late_taxclass%%%',
+% 'name' => 'late_taxclass' );
+% $late_percent_taxclass =
+% '<BR>Taxclass '.
+% include('/elements/select-taxclass.html',
+% 'curr_value' => '%%%late_percent_taxclass%%%',
+% 'name' => 'late_percent_taxclass' );
+%}
+%
+%#this is pretty kludgy right here.
+%tie my %events, 'Tie::IxHash',
+%
+% 'fee' => {
+% 'name' => 'Late fee (flat)',
+% 'code' => '$cust_main->charge( %%%charge%%%, \'%%%reason%%%\', \'$%%%charge%%%\', \'%%%late_taxclass%%%\' );',
+% 'html' =>
+% 'Amount <INPUT TYPE="text" SIZE="7" NAME="charge" VALUE="%%%charge%%%">'.
+% '<BR>Reason <INPUT TYPE="text" NAME="reason" VALUE="%%%reason%%%">'.
+% $late_taxclass,
+% 'weight' => 10,
+% },
+% 'fee_percent' => {
+% 'name' => 'Late fee (percentage)',
+% 'code' => '$cust_main->charge( sprintf(\'%.2f\', $cust_bill->owed * %%%percent%%% / 100 ), \'%%%percent_reason%%%\', \'%%%percent%%% percent\', \'%%%late_percent_taxclass%%%\' );',
+% 'html' =>
+% 'Percent <INPUT TYPE="text" SIZE="2" NAME="percent" VALUE="%%%percent%%%">%'.
+% '<BR>Reason <INPUT TYPE="text" NAME="percent_reason" VALUE="%%%percent_reason%%%">'.
+% $late_percent_taxclass,
+% 'weight' => 10,
+% },
+% 'suspend' => {
+% 'name' => 'Suspend',
+% 'code' => '$cust_main->suspend(reason => %%%sreason%%%, %%%honor_dundate%%% );',
+% 'html' => sub { &honor_dundate('honor_dundate', @_) },
+% 'weight' => 10,
+% 'reason' => 'S',
+% },
+% 'suspend-if-balance' => {
+% 'name' => 'Suspend if balance (this invoice and previous) over',
+% 'code' => '$cust_bill->cust_suspend_if_balance_over( %%%balanceover%%%, reason => %%%sreason%%%, %%%balance_honor_dundate%%% );',
+% 'html' => sub { " $money_char ". '<INPUT TYPE="text" SIZE="7" NAME="balanceover" VALUE="%%%balanceover%%%"> '. &honor_dundate('balance_honor_dundate', @_) },
+% 'weight' => 10,
+% 'reason' => 'S',
+% },
+% 'suspend-if-pkgpart' => {
+% 'name' => 'Suspend packages',
+% 'code' => '$cust_main->suspend_if_pkgpart({pkgparts => [%%%if_pkgpart%%%,], reason => %%%sreason%%%, %%%if_pkgpart_honor_dundate%%% });',
+% 'html' => sub { &select_pkgpart('if_pkgpart', @_). &honor_dundate('if_pkgpart_honor_dundate', @_) },
+% 'weight' => 10,
+% 'reason' => 'S',
+% },
+% 'suspend-unless-pkgpart' => {
+% 'name' => 'Suspend packages except',
+% 'code' => '$cust_main->suspend_unless_pkgpart({unless_pkgpart => [%%%unless_pkgpart%%%], reason => %%%sreason%%%, %%%unless_pkgpart_honor_dundate%%% });',
+% 'html' => sub { &select_pkgpart('unless_pkgpart', @_). &honor_dundate('unless_pkgpart_honor_dundate' => @_) },
+% 'weight' => 10,
+% 'reason' => 'S',
+% },
+% 'cancel' => {
+% 'name' => 'Cancel',
+% 'code' => '$cust_main->cancel(reason => %%%creason%%%);',
+% 'weight' => 10,
+% 'reason' => 'C',
+% },
+%
+% 'addpost' => {
+% 'name' => 'Add postal invoicing',
+% 'code' => '$cust_main->invoicing_list_addpost(); "";',
+% 'weight' => 20,
+% },
+%
+% 'comp' => {
+% 'name' => 'Pay invoice with a complimentary "payment"',
+% 'code' => '$cust_bill->comp();',
+% 'weight' => 30,
+% },
+%
+% 'credit' => {
+% 'name' => "Create and apply a credit for the customer's balance (i.e. write off as bad debt)",
+% 'code' => '$cust_main->credit( $cust_main->balance, \'%%%credit_reason%%%\' );',
+% 'html' => '<INPUT TYPE="text" NAME="credit_reason" VALUE="%%%credit_reason%%%">',
+% 'weight' => 30,
+% },
+%
+% 'realtime-card' => {
+% 'name' => 'Run card with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway',
+% 'code' => '$cust_bill->realtime_card();',
+% 'weight' => 30,
+% },
+%
+% 'realtime-check' => {
+% 'name' => 'Run check with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway',
+% 'code' => '$cust_bill->realtime_ach();',
+% 'weight' => 30,
+% },
+%
+% 'realtime-lec' => {
+% 'name' => 'Run phone bill ("LEC") billing with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway',
+% 'code' => '$cust_bill->realtime_lec();',
+% 'weight' => 30,
+% },
+%
+% 'batch-card' => {
+% 'name' => 'Add card or check to a pending batch',
+% 'code' => '$cust_bill->batch_card(%options);',
+% 'weight' => 40,
+% },
+%
+%
+% #'retriable' => {
+% # 'name' => 'Mark batched card event as retriable',
+% # 'code' => '$cust_pay_batch->retriable();',
+% # 'weight' => 60,
+% #},
+%
+% 'send' => {
+% 'name' => 'Send invoice (email/print/fax)',
+% 'code' => '$cust_bill->send();',
+% 'weight' => 50,
+% },
+%
+% 'send_email' => {
+% 'name' => 'Send invoice (email only)',
+% 'code' => '$cust_bill->email();',
+% 'weight' => 50,
+% },
+%
+% 'send_alternate' => {
+% 'name' => 'Send invoice (email/print/fax) with alternate template',
+% 'code' => '$cust_bill->send(\'%%%templatename%%%\');',
+% 'html' =>
+% '<INPUT TYPE="text" NAME="templatename" VALUE="%%%templatename%%%">',
+% 'weight' => 50,
+% },
+%
+% 'send_if_newest' => {
+% 'name' => 'Send invoice (email/print/fax) with alternate template, if it is still the newest invoice (useful for late notices - set to 31 days or later)',
+% 'code' => '$cust_bill->send_if_newest(\'%%%if_newest_templatename%%%\');',
+% 'html' =>
+% '<INPUT TYPE="text" NAME="if_newest_templatename" VALUE="%%%if_newest_templatename%%%">',
+% 'weight' => 50,
+% },
+%
+% 'send_agent' => {
+% 'name' => 'Send invoice (email/print/fax) ',
+% 'code' => '$cust_bill->send( \'%%%agent_templatename%%%\',
+% [ %%%agentnum%%% ],
+% \'%%%agent_invoice_from%%%\',
+% %%%agent_balanceover%%%
+% );',
+% 'html' => sub {
+% '<TABLE BORDER=0>
+% <TR>
+% <TD ALIGN="right">only for agent(s) </TD>
+% <TD>'. &select_agentnum(@_). '</TD>
+% </TR>
+% <TR>
+% <TD ALIGN="right">with template </TD>
+% <TD>
+% <INPUT TYPE="text" NAME="agent_templatename" VALUE="%%%agent_templatename%%%">
+% </TD>
+% </TR>
+% <TR>
+% <TD ALIGN="right">email From: </TD>
+% <TD>
+% <INPUT TYPE="text" NAME="agent_invoice_from" VALUE="%%%agent_invoice_from%%%">
+% </TD>
+% </TR>
+% <TR>
+% <TD ALIGN="right">if balance (this invoice and previous) over
+% </TD>
+% <TD>
+% '. $money_char. '<INPUT TYPE="text" SIZE="7" NAME="agent_balanceover" VALUE="%%%agent_balanceover%%%">
+% </TD>
+% </TR>
+% </TABLE>';
+% },
+% 'weight' => 50,
+% },
+%
+% 'send_csv_ftp' => {
+% 'name' => 'Upload CSV invoice data to an FTP server',
+% 'code' => '$cust_bill->send_csv( protocol => \'ftp\',
+% server => \'%%%ftpserver%%%\',
+% username => \'%%%ftpusername%%%\',
+% password => \'%%%ftppassword%%%\',
+% dir => \'%%%ftpdir%%%\',
+% \'format\' => \'%%%ftpformat%%%\',
+% );',
+% 'html' =>
+% '<TABLE BORDER=0>'.
+% '<TR><TD ALIGN="right">Format ("default" or "billco"): </TD>'.
+% '<TD>'.
+% '<!--'.
+% '<SELECT NAME="ftpformat">'.
+% '<OPTION VALUE="default">Default'.
+% '<OPTION VALUE="billco">Billco'.
+% '</SELECT>'.
+% '-->'.
+% '<INPUT TYPE="text" NAME="ftpformat" VALUE="%%%ftpformat%%%">'.
+% '</TD></TR>'.
+% '<TR><TD ALIGN="right">FTP server: </TD>'.
+% '<TD><INPUT TYPE="text" NAME="ftpserver" VALUE="%%%ftpserver%%%">'.
+% '</TD></TR>'.
+% '<TR><TD ALIGN="right">FTP username: </TD><TD>'.
+% '<INPUT TYPE="text" NAME="ftpusername" VALUE="%%%ftpusername%%%">'.
+% '</TD></TR>'.
+% '<TR><TD ALIGN="right">FTP password: </TD><TD>'.
+% '<INPUT TYPE="text" NAME="ftppassword" VALUE="%%%ftppassword%%%">'.
+% '</TD></TR>'.
+% '<TR><TD ALIGN="right">FTP directory: </TD>'.
+% '<TD><INPUT TYPE="text" NAME="ftpdir" VALUE="%%%ftpdir%%%">'.
+% '</TD></TR>'.
+% '</TABLE>',
+% 'weight' => 50,
+% },
+%
+% 'spool_csv' => {
+% 'name' => 'Spool CSV invoice data',
+% 'code' => '$cust_bill->spool_csv(
+% \'format\' => \'%%%spoolformat%%%\',
+% \'dest\' => \'%%%spooldest%%%\',
+% \'balanceover\' => \'%%%spoolbalanceover%%%\',
+% \'agent_spools\' => \'%%%spoolagent_spools%%%\',
+% );',
+% 'html' => sub {
+% my $plandata = shift;
+%
+% my $html =
+% '<TABLE BORDER=0>'.
+% '<TR><TD ALIGN="right">Format: </TD>'.
+% '<TD>'.
+% '<SELECT NAME="spoolformat">';
+%
+% foreach my $option (qw( default billco )) {
+% $html .= qq(<OPTION VALUE="$option");
+% $html .= ' SELECTED' if $option eq $plandata->{'spoolformat'};
+% $html .= ">\u$option";
+% }
+%
+% $html .=
+% '</SELECT>'.
+% '</TD></TR>'.
+% '<TR><TD ALIGN="right">For destination: </TD>'.
+% '<TD>'.
+% '<SELECT NAME="spooldest">';
+%
+% tie my %dest, 'Tie::IxHash',
+% '' => '(all)',
+% 'POST' => 'Postal Mail',
+% 'EMAIL' => 'Email',
+% 'FAX' => 'Fax',
+% ;
+%
+% foreach my $dest (keys %dest) {
+% $html .= qq(<OPTION VALUE="$dest");
+% $html .= ' SELECTED' if $dest eq $plandata->{'spooldest'};
+% $html .= '>'. $dest{$dest};
+% }
+%
+% $html .=
+% '</SELECT>'.
+% '</TD></TR>'.
+%
+% '<TR>'.
+% '<TD ALIGN="right">if balance (this invoice and previous) over </TD>'.
+% '<TD>'.
+% "$money_char ".
+% '<INPUT TYPE="text" SIZE="7" NAME="spoolbalanceover" VALUE="%%%spoolbalanceover%%%">'.
+% '</TD>'.
+% '<TR><TD ALIGN="right">Individual per-agent spools? </TD>'.
+% '<TD><INPUT TYPE="checkbox" NAME="spoolagent_spools" VALUE="1" '.
+% ( $plandata->{'spoolagent_spools'} ? 'CHECKED' : '' ).
+% '>'.
+% '</TD></TR>'.
+% '</TABLE>';
+%
+% $html;
+% },
+% 'weight' => 50,
+% },
+%
+% 'bill' => {
+% 'name' => 'Generate invoices (normally only used with a <i>Late Fee</i> event)',
+% 'code' => '$cust_main->bill();',
+% 'weight' => 60,
+% },
+%
+% 'apply' => {
+% 'name' => 'Apply unapplied payments and credits',
+% 'code' => '$cust_main->apply_payments_and_credits; "";',
+% 'weight' => 70,
+% },
+%
+%;
+%
+<SCRIPT TYPE="text/javascript">var myreasons = new Array();</SCRIPT>
+%foreach my $event ( keys %events ) {
+% my %plandata = map { /^(\w+) (.*)$/; ($1, $2); }
+% split(/\n/, $part_bill_event->plandata);
+% my $html = $events{$event}{html};
+% if ( ref($html) eq 'CODE' ) {
+% $html = &{$html}(\%plandata);
+% }
+% while ( $html =~ /%%%(\w+)%%%/ ) {
+% my $field = $1;
+% $html =~ s/%%%$field%%%/$plandata{$field}/;
+% }
+%
+<SCRIPT TYPE="text/javascript">myreasons.push('<% $events{$event}{reason} %>');
+</SCRIPT>
+% if ($event eq $part_bill_event->plan){
+% $currentreasonclass=$events{$event}{reason};
+% }
+% print ntable( "#cccccc", 2).
+% qq!<TR><TD><INPUT TYPE="radio" NAME="plan_weight_eventcode" !;
+% print "CHECKED " if $event eq $part_bill_event->plan;
+% print qq!onClick="showhide_table()" !;
+% print qq!VALUE="!. $event. ":". $events{$event}{weight}. ":".
+% encode_entities($events{$event}{code}).
+% qq!">$events{$event}{name}</TD>!;
+% print '<TD>'. $html. '</TD>' if $html;
+% print qq!</TR>!;
+% print '</TABLE>';
+% print qq!<HR WIDTH="90%">!;
+%}
+%
+% if ($currentreasonclass eq 'C'){
+% if ($cgi->param('creason') =~ /^(-?\d+)$/){
+% $creason = $1;
+% }else{
+% $creason = $part_bill_event->reason;
+% }
+% if ($cgi->param('newcreasonT') =~ /^(\d+)$/){
+% $newcreasonT = $1;
+% }
+% if ($cgi->param('newcreason') =~ /^([\w\s]+)$/){
+% $newcreason = $1;
+% }
+% }elsif ($currentreasonclass eq 'S'){
+% if ($cgi->param('sreason') =~ /^(-?\d+)$/){
+% $sreason = $1;
+% }else{
+% $sreason = $part_bill_event->reason;
+% }
+% if ($cgi->param('newsreasonT') =~ /^(\d+)$/){
+% $newsreasonT = $1;
+% }
+% if ($cgi->param('newsreason') =~ /^([\w\s]+)$/){
+% $newsreason = $1;
+% }
+% }
+%
+
+</TD></TR>
+</TABLE>
+
+<SCRIPT TYPE="text/javascript">
+ function showhide_table()
+ {
+ for(i=0;i<document.editEvent.plan_weight_eventcode.length;i++){
+ if (document.editEvent.plan_weight_eventcode[i].checked == true){
+ currentevent=i;
+ }
+ }
+ if(myreasons[currentevent] == 'C'){
+ document.getElementById('Ctable').style.display = 'inline';
+ document.getElementById('Stable').style.display = 'none';
+ }else if(myreasons[currentevent] == 'S'){
+ document.getElementById('Ctable').style.display = 'none';
+ document.getElementById('Stable').style.display = 'inline';
+ }else{
+ document.getElementById('Ctable').style.display = 'none';
+ document.getElementById('Stable').style.display = 'none';
+ }
+ }
+</SCRIPT>
+
+<TABLE BGCOLOR="#cccccc" BORDER=0 WIDTH="100%">
+<TR><TD>
+<TABLE BORDER=0 id="Ctable" style="display:<% $currentreasonclass eq 'C' ? 'inline' : 'none' %>">
+<% include('/elements/tr-select-reason.html',
+ 'field' => 'creason',
+ 'reason_class' => 'C',
+ 'curr_value' => $creason,
+ 'init_type' => $newcreasonT,
+ 'init_newreason' => $newcreason
+ )
+%>
+</TABLE>
+</TR></TD>
+</TABLE>
+
+<TABLE BGCOLOR="#cccccc" BORDER=0 WIDTH="100%">
+<TR><TD>
+<TABLE BORDER=0 id="Stable" style="display:<% $currentreasonclass eq 'S' ? 'inline' : 'none' %>">
+<% include('/elements/tr-select-reason.html',
+ 'field' => 'sreason',
+ 'reason_class' => 'S',
+ 'curr_value' => $sreason,
+ 'init_type' => $newsreasonT,
+ 'init_newreason' => $newsreason
+ )
+%>
+</TABLE>
+</TR></TD>
+</TABLE>
+
+%
+%print qq!<INPUT TYPE="submit" VALUE="!,
+% $hashref->{eventpart} ? "Apply changes" : "Add invoice event",
+% qq!">!;
+%
+
+
+ </FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+if ( $cgi->param('eventpart') && $cgi->param('eventpart') =~ /^(\d+)$/ ) {
+ $cgi->param('eventpart', $1);
+} else {
+ $cgi->param('eventpart', '');
+}
+
+my ($creason, $newcreasonT, $newcreason);
+my ($sreason, $newsreasonT, $newsreason);
+
+my ($query) = $cgi->keywords;
+my $action = '';
+my $part_bill_event = '';
+my $currentreasonclass = '';
+if ( $cgi->param('error') ) {
+ $part_bill_event = new FS::part_bill_event ( {
+ map { $_, scalar($cgi->param($_)) } fields('part_bill_event')
+ } );
+}
+if ( $query && $query =~ /^(\d+)$/ ) {
+ $part_bill_event ||= qsearchs('part_bill_event',{'eventpart'=>$1});
+} else {
+ $part_bill_event ||= new FS::part_bill_event {};
+}
+$action ||= $part_bill_event->eventpart ? 'Edit' : 'Add';
+my $hashref = $part_bill_event->hashref;
+
+</%init>
diff --git a/httemplate/edit/part_event.html b/httemplate/edit/part_event.html
new file mode 100644
index 0000000..6a53222
--- /dev/null
+++ b/httemplate/edit/part_event.html
@@ -0,0 +1,679 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Billing event definition',
+ 'table' => 'part_event',
+ 'fields' => [
+ 'event',
+ { field => 'eventtable',
+ type => 'select',
+ options => [ FS::part_event->eventtables ],
+ labels => $eventtable_labels,
+ onchange => 'eventtable_changed',
+ },
+ { field => 'agentnum',
+ type => 'select-agent',
+ disable_empty => $disable_empty_agent,
+ },
+ { field => 'check_freq',
+ type => 'select',
+ options => [ '1d', '1m' ],
+ labels => $check_freq_labels,
+ },
+ { field => 'disabled',
+ type => 'checkbox',
+ value => 'Y',
+ },
+ { type => 'title',
+ value => 'Event Conditions',
+ },
+ { field => 'conditionname',
+ type => 'selectlayers',
+ options => [ keys %all_conditions ],
+ labels => \%condition_labels,
+ onchange => 'condition_changed(what);',
+ layer_fields => \%condition_fields,
+ layer_values_callback => $condition_layer_values,
+ html_between => n_a('action'),
+ m2name_table => 'part_event_condition',
+ m2name_namecol => 'conditionname',
+ m2_label => 'Condition',
+ m2_new_default => \@implicit_condition_objs,
+ m2_error_callback => $condition_error_callback,
+ m2_remove_warnings => \%condition_remove_warnings,
+ m2_new_js => 'condition_repop',
+ m2_remove_js => 'condition_add',
+ },
+ { type => 'title',
+ value => 'Event Action',
+ },
+ { field => 'action',
+ type => 'selectlayers',
+ options => [ keys %all_actions ],
+ labels => \%action_labels,
+ onchange => 'action_changed(what);',
+ layer_fields => \%action_fields,
+ layer_values_callback => $action_layer_values,
+ html_between => n_a('action'),
+ },
+
+ ],
+ 'labels' => {
+ 'eventpart' => 'Event',
+ 'event' => 'Event name',
+ 'eventtable' => 'Type',
+ 'agentnum' => 'Agent',
+ 'check_freq' => 'Check frequency',
+ 'disabled' => 'Disable event',
+
+ 'conditionname' => 'Add&nbsp;new&nbsp;condition',
+ #'weight',
+ 'action' => 'Action',
+ },
+ 'viewall_dir' => 'browse',
+ 'new_callback' => sub { #start empty for new events only
+ my( $cgi, $object, $fields_listref ) = @_;
+ unshift @{ $fields_listref->[1]{'options'} }, '';
+ },
+ 'error_callback' => $error_callback,
+
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Edit global billing events',
+ )
+%>
+<SCRIPT TYPE="text/javascript">
+
+ window.onload = function () { eventtable_changed(document.getElementById('eventtable')) };
+ var notonload = 0;
+
+ function eventtable_changed(what) {
+
+% if ( $JS_DEBUG ) {
+ alert('eventtable_changed called on ' + what );
+% }
+
+ var eventtable = what.options[what.selectedIndex].value;
+% if ( $JS_DEBUG ) {
+ alert ("eventtable: " + eventtable);
+% }
+ var eventdesc = what.options[what.selectedIndex].text;
+
+ //remove the ** Select type **
+ if ( what.options[0].value == '' && notonload++ > 0 ) {
+ what.options[0] = null;
+ }
+
+ ////
+ // XXX gray out conditions that can't apply (in addition to the warning)?
+ ////
+
+ ////
+ // update condition selects
+ ////
+
+ for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
+ var cond_id = 'conditionname' + cnum;
+ var cond_select = document.getElementById(cond_id);
+
+% if ( $JS_DEBUG ) {
+ alert('updating ' + cond_id);
+% }
+
+ // save off the current value
+ var conditionname = cond_select.options[cond_select.selectedIndex].value;
+ var cond_desc = cond_select.options[cond_select.selectedIndex].text;
+
+ var seen_condition = condition_repop(cond_select);
+
+ var warning = document.getElementById(cond_id + '_warning');
+% if ( $JS_DEBUG ) {
+ alert('turning off warning; setting style.display of '+ cond_id +
+ '_warning (' + warning + ') to none');
+% }
+ warning.style.display = 'none';
+
+ if ( ! seen_condition && conditionname != '' ) {
+ // add the current (not valid) condition back
+ opt(cond_select, conditionname, cond_desc, true );
+ if ( ! condition_is_implicit(conditionname) ) {
+ cond_select.parentNode.parentNode.style.display = '';
+ cond_select.disabled = '';
+ // turn on a warning and gray out the condition row
+% if ( $JS_DEBUG ) {
+ alert('turning on warning; setting style.display of '+ cond_id +
+ '_warning (' + warning + ') to ""');
+% }
+ warning.innerHTML = 'Not applicable to ' + eventdesc + ' events';
+ warning.style.display = '';
+ } else {
+ if ( ! condition_in_eventtable(conditionname) ) {
+% if ( $JS_DEBUG ) {
+ alert(conditionname + " not in " + eventtable + "; disabling");
+% }
+ cond_select.parentNode.parentNode.style.display = 'none';
+ cond_select.disabled = 'disabled';
+ } else {
+% if ( $JS_DEBUG ) {
+ alert(conditionname + " implicit for " + eventtable + "; enabling");
+% }
+ cond_select.parentNode.parentNode.style.display = '';
+ cond_select.disabled = '';
+ }
+ }
+ }
+
+ }
+
+
+ ////
+ // update action select
+ ////
+
+ // save off the current value first!!
+ var action = what.form.action.options[what.form.action.selectedIndex].value;
+ var a_desc = what.form.action.options[what.form.action.selectedIndex].text;
+ var seen_action = false;
+
+ // blank the current action select
+ for ( var i = what.form.action.length; i >= 0; i-- )
+ what.form.action.options[i] = null;
+
+ if ( action == '' ) {
+ opt(what.form.action, action, a_desc, true );
+ }
+
+ // repopulate it
+% foreach my $eventtable ( FS::part_event->eventtables ) {
+% tie my %actions, 'Tie::IxHash', FS::part_event->actions($eventtable);
+% #use Data::Dumper; warn Dumper(%actions);
+
+ if ( eventtable == '<% $eventtable %>' ) {
+
+% foreach my $action ( keys %actions ) {
+% ( my $description = $actions{$action}->{'description'} ) =~ s/'/\\'/g;
+
+ var sel = false;
+ if ( action == '<% $action %>' ) {
+ seen_action = true;
+ sel = true;
+ }
+ opt( what.form.action, '<% $action %>', '<% $description %>', sel );
+% }
+
+ }
+
+% }
+
+ // by default, turn off warnings and enable the submit button
+ var warning = document.getElementById('action_warning');
+ warning.style.display = 'none';
+ var submit_button = document.getElementById('submit');
+ submit_button.disabled = '';
+
+ if ( ! seen_action && action != '' ) {
+ // add the current (not valid) action back
+ opt( what.form.action, action, a_desc, true );
+ // turn on a warning and disable the submit button
+ //warning.innerHTML = a_desc + ' event not available as a ' +
+ warning.innerHTML = 'Not available as a ' + eventdesc + ' action';
+ warning.style.display = '';
+ submit_button.disabled = 'disabled';
+ }
+
+ }
+
+ function opt(what,value,text,selected) {
+ var optionName = new Option(text, value, false, selected);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function action_changed(what) {
+ // remove '** Select new **'
+ if ( what.options[0].value == '' ) {
+ what.options[0] = null;
+ }
+ // remove the warning, remove the invalid action, enable the submit button
+ var warning = document.getElementById('action_warning');
+ if ( warning.style.display == '' ) {
+ warning.style.display = 'none';
+ what.options[what.length-1] = null;
+ document.getElementById('submit').disabled = '';
+ }
+ }
+
+ function condition_changed(what) {
+ // remove '** Select new **'
+ if ( what.options[0].value == '' ) {
+ what.options[0] = null;
+ }
+
+ var previousValue = what.getAttribute('previousValue');
+ var previousText = what.getAttribute('previousText');
+ var value = what.options[what.selectedIndex].value;
+ var text = what.options[what.selectedIndex].text;
+
+% foreach my $value ( keys %condition_remove_warnings ) {
+ if ( previousValue == '<% $value %>' ) {
+ if ( !confirm( <% $condition_remove_warnings{$value} |js_string %> ) ) {
+ for ( var i=0; i < what.length; i++ ) {
+ if ( what.options[i].value == previousValue ) {
+ what.selectedIndex = i;
+ }
+ }
+ return false;
+ }
+ }
+% }
+
+ //alert(previous + ' changed to ' + value);
+
+ var field_regex = /(\d+)$/;
+ var match = field_regex.exec(what.name);
+ if ( !match ) {
+ alert(what.name + " didn't match?!");
+ return;
+ }
+
+ //add the previous condition *back* to all the other selects...
+ condition_add(previousValue, previousText, match[1]);
+
+ what.setAttribute('previousValue', value);
+ what.setAttribute('previousText', text);
+
+ // remove the new condition from all other selects
+ condition_remove(value, match[1]);
+
+ }
+
+ function condition_avail(check_cond, curnum) {
+ for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
+ if ( cnum == curnum ) continue;
+
+ var cond_id = 'conditionname' + cnum;
+ var cond_select = document.getElementById(cond_id);
+
+ //alert("checking " + cond_id + " (" + cond_select.disabled + ")");
+
+ if ( cond_select.disabled ) continue;
+
+ // the current value
+ var conditionname = cond_select.options[cond_select.selectedIndex].value;
+
+ if ( check_cond == conditionname ) return false;
+
+ }
+
+ return true;
+
+ }
+
+ function condition_remove(remove_cond, curnum) {
+
+ if ( remove_cond.length == 0 ) return;
+
+ for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
+ if ( cnum == curnum ) continue;
+
+ var cond_id = 'conditionname' + cnum;
+ var cond_select = document.getElementById(cond_id);
+
+ //for ( var i = cond_select.length; i >= 0; i-- ) {
+ for ( var i=0; i < cond_select.length; i++ ) {
+ if ( cond_select.options[i].value == remove_cond ) {
+ cond_select.options[i] = null;
+ }
+ }
+
+ }
+
+ }
+
+ function condition_add(add_condname, add_conddesc, curnum) {
+
+ if ( add_condname.length == 0 ) return;
+
+ var in_eventtable = condition_in_eventtable(add_condname);
+
+ if ( ! in_eventtable ) return;
+
+ for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
+ if ( cnum == curnum ) continue;
+
+ var cond_id = 'conditionname' + cnum;
+ var cond_select = document.getElementById(cond_id);
+
+ if ( cond_select.disabled ) continue;
+
+ //alert("adding " + add_condname + " to " + cond_id);
+
+ opt(cond_select, add_condname, add_conddesc, false );
+
+ cond_select.parentNode.parentNode.style.display = '';
+
+ }
+
+ }
+
+ function condition_in_eventtable(condname) {
+
+ var eventtable_el = document.getElementById('eventtable');
+ var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
+
+ var in_eventtable = false;
+
+% foreach my $eventtable ( FS::part_event->eventtables ) {
+% tie my %conditions, 'Tie::IxHash',
+% FS::part_event_condition->conditions($eventtable);
+
+ if ( eventtable == '<% $eventtable %>' ) {
+
+% foreach my $conditionname ( keys %conditions ) {
+
+ if ( condname == '<% $conditionname %>' ) {
+ in_eventtable = true;
+ }
+
+% }
+
+ }
+
+% }
+
+ return in_eventtable;
+
+ }
+
+ function condition_is_implicit(condname) {
+
+ if ( true <% @implicit_conditions
+ ? ( ' && '. join(' && ', map { "condname != '$_'" }
+ @implicit_conditions
+ )
+ )
+ : ''
+ %> ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ function condition_repop(cond_select) {
+
+ var eventtable_el = document.getElementById('eventtable');
+ var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
+
+ // save off the current value
+ var conditionname = cond_select.options[cond_select.selectedIndex].value;
+ var cond_desc = cond_select.options[cond_select.selectedIndex].text;
+ var seen_condition = false;
+
+ //skip deleted conditions
+ if ( cond_select.disabled && conditionname != '' && ! condition_is_implicit(conditionname) ) {
+ return false;
+ }
+
+ var field_regex = /(\d+)$/;
+ var match = field_regex.exec(cond_select.name);
+ if ( !match ) {
+ alert(what.name + " didn't match?!");
+ return;
+ }
+ var cnum = match[1];
+
+ // blank the current condition select
+ for ( var i = cond_select.length; i >= 0; i-- )
+ cond_select.options[i] = null;
+
+ if ( conditionname == '' ) {
+ opt(cond_select, conditionname, cond_desc, true );
+ }
+
+ // repopulate it
+% foreach my $eventtable ( FS::part_event->eventtables ) {
+% tie my %conditions, 'Tie::IxHash',
+% FS::part_event_condition->conditions($eventtable);
+
+ if ( eventtable == '<% $eventtable %>' ) {
+
+% foreach my $conditionname ( keys %conditions ) {
+% my $description = $conditions{$conditionname}->{'description'};
+% $description =~ s/'/\\'/g;
+
+ var sel = false;
+ if ( conditionname == '<% $conditionname %>' ) {
+ seen_condition = true;
+ sel = true;
+ }
+
+ if ( condition_avail("<% $conditionname %>", cnum) ) {
+ opt(cond_select, '<% $conditionname %>', '<% $description %>', sel);
+ }
+
+% }
+
+ }
+
+% }
+
+ if ( cond_select.length > 1 || cond_select.length == 1 && cond_select.options[0].value.length > 0 ) {
+
+ cond_select.parentNode.parentNode.style.display = '';
+ cond_select.disabled = '';
+
+ } else {
+ cond_select.parentNode.parentNode.style.display = 'none';
+ cond_select.disabled = 'disabled';
+ }
+
+ return seen_condition;
+
+ }
+
+</SCRIPT>
+<%once>
+
+#misc (eventtable, check_freq)
+
+my $eventtable_labels = FS::part_event->eventtable_labels;
+$eventtable_labels->{''} = '** Select type **';
+
+my $check_freq_labels = FS::part_event->check_freq_labels;
+
+#conditions
+
+tie my %all_conditions, 'Tie::IxHash',
+ '' => { 'description' => '*** Select new condition ***', },
+ FS::part_event_condition->conditions();
+
+my %condition_labels = map { $_ => $all_conditions{$_}->{'description'} }
+ keys %all_conditions;
+
+#my %condition_fields = map { $_ => $all_conditions{$_}->{option_fields} }
+# keys %all_conditions;
+my %condition_fields = map { my $c = $_;
+ tie my %opts, 'Tie::IxHash',
+ @{ $all_conditions{$c}->{'option_fields'} || []};
+ %opts = ( map { ( "$c.$_" => $opts{$_} ); }
+ keys %opts
+ );
+ ( $c => [ %opts ] );
+ }
+ keys %all_conditions;
+
+my @implicit_conditions = sort { $all_conditions{$a}->{'implicit_flag'} <=>
+ $all_conditions{$b}->{'implicit_flag'}
+ }
+ grep { $all_conditions{$_}->{'implicit_flag'} }
+ keys %all_conditions;
+
+my @implicit_condition_objs = map {
+ new FS::part_event_condition {
+ 'conditionname' => $_,
+ };
+ }
+ @implicit_conditions;
+
+my %condition_remove_warnings =
+ map { ( $_ => $all_conditions{$_}->{'remove_warning'} ); }
+ grep { $all_conditions{$_}->{'remove_warning'} }
+ keys %all_conditions;
+
+#actions
+
+tie my %all_actions, 'Tie::IxHash',
+ '' => { 'description' => '*** Select event action ***', },
+ FS::part_event->actions();
+
+my %action_labels = map { $_ => $all_actions{$_}->{'description'} }
+ keys %all_actions;
+
+#my %action_fields = map { $_ => $all_actions{$_}->{option_fields} }
+# keys %all_actions;
+my %action_fields = map { my $action = $_;
+ tie my %opts, 'Tie::IxHash',
+ @{ $all_actions{$action}->{option_fields} || [] };
+ %opts = ( map { ( "$action.$_" => $opts{$_} ); }
+ keys %opts
+ );
+ ( $action => [ %opts ] );
+ }
+ keys %all_actions;
+
+#subs
+
+sub n_a {
+ my $t = shift;
+
+ return sub {
+ my $field = shift;
+ qq( <FONT ID="${field}_warning" STYLE="display:none" COLOR="#FF0000">).
+ "Party Party Join us Join us".
+ '</FONT>';
+ };
+}
+
+my $action_layer_values = sub {
+ my( $cgi, $part_event ) = @_;
+ my $action = $cgi->param('action') || $part_event->action;
+ return {} unless $action;
+ scalar( #force hashref
+ {
+ #map { $_ => { $part_event->options } }
+ # keys %action_fields
+ map { my $action = $_;
+ my %fields = @{ $action_fields{$action} };
+ my %obj_opts = $part_event->options;
+ %obj_opts = map { ( "$action.$_" => $obj_opts{$_} ); }
+ keys %obj_opts;
+ my %opts =
+ map { #false laziness w/process/part_event.html
+ my $option = $_;
+ my $value = scalar($cgi->param($_)) || $obj_opts{$_};
+
+ if ( $option =~ /^(.*)\.reasonnum$/ && $value == -1 ) {
+ $value = {
+ 'typenum' => scalar( $cgi->param( "new${option}T" ) ),
+ 'reason' => scalar( $cgi->param( "new${option}" ) ),
+ };
+ }
+
+ ( $option => $value );
+
+ }
+ keys %fields;
+ ( $action => \%opts );
+ }
+ keys %action_fields
+ }
+ );
+};
+
+tie my %cgi_conditions, 'Tie::IxHash';
+
+my $error_callback = sub {
+ my( $cgi, $object, $fields_listref ) = @_;
+
+ my @cond_params = grep /^conditionname\d+$/, $cgi->param;
+
+ %cgi_conditions = map {
+ my $param = $_;
+ my $conditionname = $cgi->param($param);
+ $conditionname => {
+ map {
+
+ my $cgi_key = $_;
+ $cgi_key =~ /^$param\.$conditionname\.(.*)$/ or die 'wtf!';
+ my $key = $1;
+ #my $value = $cgi->param($_);
+
+ #my $info = $all_conditions->{$conditionname}
+ my %cond_opts =
+ @{ $all_conditions{$conditionname}->{'option_fields'} || []};
+ my $info = $cond_opts{$key};
+
+ my $value;
+ #false laziness w/process/part_event.html
+ if ( $info->{'type'} =~ /^(select|checkbox)-?multiple$/
+ or $info->{'type'} =~ /^select/ && $info->{'multiple'} ) {
+ $value = { map { $_ => 1 } $cgi->param($cgi_key) };
+ } elsif ( $info->{'type'} eq 'freq' ) {
+ $value = $cgi->param($cgi_key). $cgi->param($cgi_key.'_units');
+ } else {
+ $value = $cgi->param($cgi_key);
+ }
+
+ $key => $value;
+
+ } grep /^$param\.$conditionname\./, $cgi->param
+ };
+ } grep $cgi->param($_), grep /^conditionname\d+$/, $cgi->param;
+
+};
+
+my $condition_error_callback = sub {
+ map {
+ new FS::part_event_condition { 'conditionname' => $_, };
+ } keys %cgi_conditions;
+};
+
+my $condition_layer_values = sub {
+ #m2_table option causes this to be
+ # part_event_condition instead of part_event
+ my ( $cgi, $part_event_condition, $switches ) = @_;
+ scalar( #force hashref
+ {
+ #map { $_ => { $part_event_condition->options } }
+ # keys %condition_fields
+ map { my $conditionname = $_;
+ my %opts = $switches->{'mode'} eq 'error'
+ ? %{ $cgi_conditions{$conditionname} || {} }
+ : $part_event_condition->options;
+ %opts = (
+ map { ( "$conditionname.$_" => $opts{$_} ); }
+ keys %opts
+ );
+ ( $conditionname => \%opts );
+ }
+ keys %condition_fields
+ }
+ );
+};
+
+
+</%once>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Edit billing events')
+ || $curuser->access_right('Edit global billing events');
+
+my $disable_empty_agent= ! $curuser->access_right('Edit global billing events');
+
+%cgi_conditions = ();
+my $use_cgi_conditions = 0;
+
+my $JS_DEBUG = 0;
+
+</%init>
diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi
new file mode 100644
index 0000000..d579797
--- /dev/null
+++ b/httemplate/edit/part_export.cgi
@@ -0,0 +1,123 @@
+<% include('/elements/header.html', "$action Export", '', ' onLoad="visualize()"') %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="dummy">
+<INPUT TYPE="hidden" NAME="exportnum" VALUE="<% $part_export->exportnum %>">
+
+<% ntable("#cccccc",2) %>
+<TR>
+ <TD ALIGN="right">Export host</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="machine" VALUE="<% $part_export->machine %>">
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Export</TD>
+ <TD><% $widget->html %>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+#if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {
+# $cgi->param('clone', $1);
+#} else {
+# $cgi->param('clone', '');
+#}
+
+my($query) = $cgi->keywords;
+my $action = '';
+my $part_export = '';
+if ( $cgi->param('error') ) {
+ $part_export = new FS::part_export ( {
+ map { $_, scalar($cgi->param($_)) } fields('part_export')
+ } );
+} elsif ( $query =~ /^(\d+)$/ ) {
+ $part_export = qsearchs('part_export', { 'exportnum' => $1 } );
+} else {
+ $part_export = new FS::part_export;
+}
+$action ||= $part_export->exportnum ? 'Edit' : 'Add';
+
+#my $exports = FS::part_export::export_info($svcdb);
+my $exports = FS::part_export::export_info();
+
+my %layers = map { $_ => "$_ - ". $exports->{$_}{desc} } keys %$exports;
+$layers{''}='';
+
+my $widget = new HTML::Widgets::SelectLayers(
+ 'selected_layer' => $part_export->exporttype,
+ 'options' => \%layers,
+ 'form_name' => 'dummy',
+ 'form_action' => 'process/part_export.cgi',
+ 'form_text' => [qw( exportnum machine )],
+# 'form_checkbox' => [qw()],
+ 'html_between' => "</TD></TR></TABLE>\n",
+ 'layer_callback' => sub {
+ my $layer = shift;
+ my $html = qq!<INPUT TYPE="hidden" NAME="exporttype" VALUE="$layer">!.
+ ntable("#cccccc",2);
+
+ $html .= '<TR><TD ALIGN="right">Description</TD><TD BGCOLOR=#ffffff>'.
+ $exports->{$layer}{notes}. '</TD></TR>'
+ if $layer;
+
+ foreach my $option ( keys %{$exports->{$layer}{options}} ) {
+ my $optinfo = $exports->{$layer}{options}{$option};
+ die "Retreived non-ref export info option from $layer export: $optinfo"
+ unless ref($optinfo);
+ my $label = $optinfo->{label};
+ my $type = defined($optinfo->{type}) ? $optinfo->{type} : 'text';
+ my $value = $cgi->param($option)
+ || ( $part_export->exportnum && $part_export->option($option) )
+ || ( (exists $optinfo->{default} && !$part_export->exportnum)
+ ? $optinfo->{default}
+ : ''
+ );
+ $html .= qq!<TR><TD ALIGN="right">$label</TD><TD>!;
+ if ( $type eq 'select' ) {
+ $html .= qq!<SELECT NAME="$option">!;
+ foreach my $select_option ( @{$optinfo->{options}} ) {
+ #if ( ref($select_option) ) {
+ #} else {
+ my $selected = $select_option eq $value ? ' SELECTED' : '';
+ $html .= qq!<OPTION VALUE="$select_option"$selected>!.
+ qq!$select_option</OPTION>!;
+ #}
+ }
+ $html .= '</SELECT>';
+ } elsif ( $type eq 'textarea' ) {
+ $html .= qq!<TEXTAREA NAME="$option" COLS=80 ROWS=8 WRAP="virtual">!.
+ encode_entities($value). '</TEXTAREA>';
+ } elsif ( $type eq 'text' ) {
+ $html .= qq!<INPUT TYPE="text" NAME="$option" VALUE="!.
+ encode_entities($value). '" SIZE=64>';
+ } elsif ( $type eq 'checkbox' ) {
+ $html .= qq!<INPUT TYPE="checkbox" NAME="$option" VALUE="1"!;
+ $html .= ' CHECKED' if $value;
+ $html .= '>';
+ } else {
+ $html .= "unknown type $type";
+ }
+ $html .= '</TD></TR>';
+ }
+ $html .= '</TABLE>';
+
+ $html .= '<INPUT TYPE="hidden" NAME="options" VALUE="'.
+ join(',', keys %{$exports->{$layer}{options}} ). '">';
+
+ $html .= '<INPUT TYPE="hidden" NAME="nodomain" VALUE="'.
+ $exports->{$layer}{nodomain}. '">';
+
+ $html .= '<INPUT TYPE="submit" VALUE="'.
+ ( $part_export->exportnum ? "Apply changes" : "Add export" ).
+ '">';
+
+ $html;
+ },
+);
+
+</%init>
diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi
new file mode 100755
index 0000000..f404699
--- /dev/null
+++ b/httemplate/edit/part_pkg.cgi
@@ -0,0 +1,637 @@
+<% include( 'elements/edit.html',
+ 'post_url' => popurl(1).'process/part_pkg.cgi',
+ 'name' => "Package definition",
+ 'table' => 'part_pkg',
+
+ 'agent_virt' => 1,
+ 'agent_null_right' => $edit_global,
+ 'agent_clone_extra_sql' => $agent_clone_extra_sql,
+ #'viewall_dir' => 'browse',
+ 'viewall_url' => $p.'browse/part_pkg.cgi',
+ 'html_init' => include('/elements/init_overlib.html').
+ $javascript,
+ 'html_bottom' => $html_bottom,
+ 'body_etc' =>
+ 'onLoad="agent_changed(document.edit_topform.agentnum)"',
+
+ 'begin_callback' => $begin_callback,
+ 'end_callback' => $end_callback,
+ 'new_hashref_callback' => $new_hashref_callback,
+ 'new_object_callback' => $new_object_callback,
+ 'new_callback' => $new_callback,
+ 'clone_callback' => $clone_callback,
+ 'edit_callback' => $edit_callback,
+ 'error_callback' => $error_callback,
+ 'field_callback' => $field_callback,
+
+ 'labels' => {
+ 'pkgpart' => 'Package Definition',
+ 'pkg' => 'Package (customer-visible)',
+ 'comment' => 'Comment (customer-hidden)',
+ 'classnum' => 'Package class',
+ 'promo_code' => 'Promotional code',
+ 'freq' => 'Recurring fee frequency',
+ 'setuptax' => 'Setup fee tax exempt',
+ 'recurtax' => 'Recurring fee tax exempt',
+ 'taxclass' => 'Tax class',
+ 'taxproduct_select'=> 'Tax products',
+ 'plan' => 'Price plan',
+ 'disabled' => 'Disable new orders',
+ 'pay_weight' => 'Payment weight',
+ 'credit_weight' => 'Credit weight',
+ 'agentnum' => 'Agent',
+ 'setup_fee' => 'Setup fee',
+ 'recur_fee' => 'Recurring fee',
+ 'bill_dst_pkgpart' => 'Include line item(s) from package',
+ 'svc_dst_pkgpart' => 'Include services of package',
+ },
+
+ 'fields' => [
+ { field=>'clone', type=>'hidden',
+ curr_value_callback =>
+ sub { shift->param('clone') },
+ },
+ { field=>'pkgnum', type=>'hidden',
+ curr_value_callback =>
+ sub { shift->param('pkgnum') },
+ },
+
+ { type => 'columnstart' },
+
+ { field => 'pkg',
+ type => 'text',
+ size => 40, #32
+ maxlength => 50,
+ },
+ {field=>'comment', type=>'text', size=>40 }, #32
+ { field => 'agentnum',
+ type => 'select-agent',
+ disable_empty => ! $acl_edit_global,
+ empty_label => '(global)',
+ onchange => 'agent_changed',
+ },
+ {field=>'classnum', type=>'select-pkg_class' },
+ {field=>'disabled', type=>$disabled_type, value=>'Y'},
+
+ { type => 'tablebreak-tr-title',
+ value => 'Pricing', #better name?
+ },
+ { field => 'plan',
+ type => 'selectlayers-select',
+ options => [ keys %plan_labels ],
+ labels => \%plan_labels,
+ },
+ { field => 'setup_fee',
+ type => 'money',
+ },
+ { field => 'freq',
+ type => 'part_pkg_freq',
+ onchange => 'freq_changed',
+ },
+ { field => 'recur_fee',
+ type => 'money',
+ disabled => sub { $recur_disabled },
+ },
+
+ #price plan
+ #setup fee
+ #recurring frequency
+ #recurring fee (auto-disable)
+
+ { type => 'columnnext' },
+
+ {type=>'justtitle', value=>'Taxation' },
+ {field=>'setuptax', type=>'checkbox', value=>'Y'},
+ {field=>'recurtax', type=>'checkbox', value=>'Y'},
+ {field=>'taxclass', type=>'select-taxclass' },
+ { field => 'taxproductnums',
+ type => 'hidden',
+ value => join(',', @taxproductnums),
+ },
+ { field => 'taxproduct_select',
+ type => 'selectlayers',
+ options => [ '(default)', @taxproductnums ],
+ curr_value => '(default)',
+ labels => { ( '(default)' => '(default)' ),
+ map {($_=>$usage_class{$_})}
+ @taxproductnums
+ },
+ layer_fields => \%taxproduct_fields,
+ layer_values_callback => $taxproduct_values,
+ layers_only => !$taxproducts,
+ cell_style => ( !$taxproducts
+ ? 'display:none'
+ : ''
+ ),
+ },
+
+ { type => 'tablebreak-tr-title',
+ value => 'Promotions', #better name?
+ },
+ { field=>'promo_code', type=>'text', size=>15 },
+
+ { type => 'tablebreak-tr-title',
+ value => 'Line-item revenue recogition', #better name?
+ },
+ { field=>'pay_weight', type=>'text', size=>6 },
+ { field=>'credit_weight', type=>'text', size=>6 },
+
+ { type => 'columnnext' },
+
+ { field => 'agent_type',
+ type => 'select-agent_types',
+ disabled => ! $acl_edit_global,
+ curr_value_callback => sub {
+ my($cgi, $object, $field) = @_;
+ #in the other callbacks..? hmm.
+ \@agent_type;
+ },
+ },
+
+ { type => 'columnend' },
+
+ { 'type' => 'tablebreak-tr-title',
+ 'value' => 'Pricing add-ons',
+ },
+ { 'field' => 'bill_dst_pkgpart',
+ 'type' => 'select-part_pkg',
+ 'm2_label' => 'Include line item(s) from package',
+ 'm2m_method' => 'bill_part_pkg_link',
+ 'm2m_dstcol' => 'dst_pkgpart',
+ 'm2_error_callback' =>
+ &{$m2_error_callback_maker}('bill'),
+ },
+
+ { type => 'tablebreak-tr-title',
+ value => 'Services',
+ },
+ { type => 'pkg_svc', },
+
+ { 'field' => 'svc_dst_pkgpart',
+ 'label' => 'Also include services from package: ',
+ 'type' => 'select-part_pkg',
+ 'm2_label' => 'Include services of package: ',
+ 'm2m_method' => 'svc_part_pkg_link',
+ 'm2m_dstcol' => 'dst_pkgpart',
+ 'm2_error_callback' =>
+ &{$m2_error_callback_maker}('svc'),
+ },
+
+ { type => 'tablebreak-tr-title',
+ value => 'Price plan options',
+ },
+
+ ],
+
+ )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $edit_global = 'Edit global package definitions';
+my $acl_edit = $curuser->access_right('Edit package definitions');
+my $acl_edit_global = $curuser->access_right($edit_global);
+
+my $acl_edit_either = $acl_edit || $acl_edit_global;
+
+my $begin_callback = sub {
+ my( $cgi, $fields, $opt ) = @_;
+ die "access denied"
+ unless $acl_edit_either
+ || ( $cgi->param('pkgnum')
+ && $curuser->access_right('Customize customer package')
+ );
+};
+
+my $disabled_type = $acl_edit_either ? 'checkbox' : 'hidden';
+
+my $agent_clone_extra_sql =
+ ' ( '. FS::part_pkg->curuser_pkgs_sql.
+ #kludge to clone custom customer packages you otherwise couldn't see
+ " OR ( part_pkg.disabled = 'Y' AND part_pkg.comment LIKE '(CUSTOM)%' ) ".
+ ' ) ';
+
+my $conf = new FS::Conf;
+my $taxproducts = $conf->exists('enable_taxproducts');
+
+#XXX
+# - tr-part_pkg_freq: month_increments_only (from price plans)
+# - test cloning
+# - test errors cloning
+# - test custom pricing
+# - move the selectlayer divs away from lame layer_callback
+
+#my ($query) = $cgi->keywords;
+#
+#my $part_pkg = '';
+
+my @agent_type = ();
+my %tax_override = ();
+
+my %taxproductnums = map { ($_->classnum => 1) }
+ qsearch('usage_class', { 'disabled' => '' });
+my @taxproductnums = ( qw( setup recur ), sort (keys %taxproductnums) );
+
+my %options = ();
+my $recur_disabled = 1;
+
+my $error_callback = sub {
+ my($cgi, $object, $fields, $opt ) = @_;
+
+ (@agent_type) = $cgi->param('agent_type');
+
+ $opt->{action} = 'Custom' if $cgi->param('pkgnum');
+
+ $recur_disabled = $cgi->param('freq') ? 0 : 1;
+
+ foreach ($cgi->param) {
+ /^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1);
+ }
+ $tax_override{''} = $cgi->param('tax_override');
+ $tax_override{$_} = $cgi->param('tax_override_$_')
+ foreach(grep { /^tax_override_(\w+)$/ } $cgi->param);
+
+ #some false laziness w/process
+ $cgi->param('plan') =~ /^(\w+)$/ or die 'unparsable plan';
+ my $plan = $1;
+ my $options = $cgi->param($plan."__OPTIONS");
+ my @options = split(',', $options);
+ %options =
+ map { my $optionname = $_;
+ my $param = $plan."__$optionname";
+ my $value = join(', ', $cgi->param($param));
+ ( $optionname => $value );
+ }
+ @options;
+
+ #$cgi->param($_, $options{$_}) foreach (qw( setup_fee recur_fee ));
+ $object->set($_ => scalar($cgi->param($_)) )
+ foreach (qw( setup_fee recur_fee ));
+
+};
+
+my $new_hashref_callback = sub { { 'plan' => 'flat' }; };
+
+my $new_object_callback = sub {
+ my( $cgi, $hashref, $fields, $opt ) = @_;
+
+ my $part_pkg = FS::part_pkg->new( $hashref );
+ $part_pkg->set($_ => '0')
+ foreach (qw( setup_fee recur_fee ));
+
+ $part_pkg;
+
+};
+
+my $edit_callback = sub {
+ my( $cgi, $object, $fields, $opt ) = @_;
+
+ $recur_disabled = $object->freq ? 0 : 1;
+
+ (@agent_type) = map {$_->typenum} qsearch('type_pkgs',{'pkgpart'=>$1});
+
+ foreach ($object->options) {
+ /^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1);
+ }
+ foreach ($object->part_pkg_taxoverride) {
+ $taxproductnums{$_->usage_class} = 1
+ if $_->usage_class;
+ }
+
+ %options = $object->options;
+
+ $object->set($_ => $object->option($_))
+ foreach (qw( setup_fee recur_fee ));
+
+};
+
+my $new_callback = sub {
+ my( $cgi, $object, $fields ) = @_;
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('agent_defaultpkg') ) {
+ #my @all_agent_types = map {$_->typenum} qsearch('agent_type',{});
+ @agent_type = map {$_->typenum} qsearch('agent_type',{});
+ }
+
+};
+
+my $clone_callback = sub {
+ my( $cgi, $object, $fields, $opt ) = @_;
+
+ if ( $cgi->param('pkgnum') ) {
+
+ my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cgi->param('pkgnum') } );
+ $object->agentnum( $cust_pkg->cust_main->agentnum );
+
+ $opt->{action} = 'Custom';
+
+ #my $part_pkg = $clone_part_pkg->clone;
+ #this is all clone did anyway
+ $object->comment( '(CUSTOM) '. $object->comment )
+ unless $object->comment =~ /^\(CUSTOM\) /;
+
+ $object->disabled('Y');
+
+ }
+
+ %options = $object->options;
+
+ $object->set($_ => $options{$_})
+ foreach (qw( setup_fee recur_fee ));
+
+ $recur_disabled = $object->freq ? 0 : 1;
+};
+
+my $m2_error_callback_maker = sub {
+ my $link_type = shift; #yay closures
+ return sub {
+ my( $cgi, $object ) = @_;
+ map {
+ new FS::part_pkg_link {
+ 'link_type' => $link_type,
+ 'src_pkgpart' => $object->pkgpart,
+ 'dst_pkgpart' => $_,
+ };
+ }
+ grep $_,
+ map $cgi->param($_),
+ grep /^${link_type}_dst_pkgpart(\d+)$/, $cgi->param;
+ };
+};
+
+my $javascript = <<'END';
+ <SCRIPT TYPE="text/javascript">
+
+ function freq_changed(what) {
+ var freq = what.options[what.selectedIndex].value;
+
+ if ( freq == '0' ) {
+ what.form.recur_fee.disabled = true;
+ what.form.recur_fee.style.backgroundColor = '#dddddd';
+ } else {
+ what.form.recur_fee.disabled = false;
+ what.form.recur_fee.style.backgroundColor = '#ffffff';
+ }
+
+ }
+
+ function agent_changed(what) {
+
+ var agentnum = what.options[what.selectedIndex].value;
+
+ if ( agentnum == 0 ) {
+ what.form.agent_type.disabled = false;
+ //what.form.agent_type.style.backgroundColor = '#ffffff';
+ what.form.agent_type.style.visibility = '';
+ } else {
+ what.form.agent_type.disabled = true;
+ //what.form.agent_type.style.backgroundColor = '#dddddd';
+ what.form.agent_type.style.visibility = 'hidden';
+ }
+
+ }
+
+ </SCRIPT>
+END
+
+tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() };
+
+tie my %plan_labels, 'Tie::IxHash',
+ map { $_ => ( $plans{$_}->{'shortname'} || $plans{$_}->{'name'} ) }
+ keys %plans;
+
+my $html_bottom = sub {
+ my( $object ) = @_;
+
+ #warn join("\n", map { "$_: $options{$_}" } keys %options ). "\n";
+
+ my $layer_callback = sub {
+
+ my $layer = shift;
+ my $html = ntable("#cccccc",2);
+
+ #$html .= '
+ # <TR>
+ # <TD ALIGN="right">Recurring fee frequency </TD>
+ # <TD><SELECT NAME="freq">
+ #';
+ #
+ #my @freq = keys %freq;
+ #@freq = grep { /^\d+$/ } @freq
+ #XXX this bit# # if exists($plans{$layer}->{'freq'}) && $plans{$layer}->{'freq'} eq 'm';
+ #foreach my $freq ( @freq ) {
+ # $html .= qq(<OPTION VALUE="$freq");
+ # $html .= ' SELECTED' if $freq eq $part_pkg->freq;
+ # $html .= ">$freq{$freq}";
+ #}
+ #$html .= '</SELECT></TD></TR>';
+
+ my $href = $plans{$layer}->{'fields'};
+ my @fields = exists($plans{$layer}->{'fieldorder'})
+ ? @{$plans{$layer}->{'fieldorder'}}
+ : keys %{ $href };
+
+ foreach my $field ( grep $_ !~ /^(setup|recur)_fee$/, @fields ) {
+
+ $html .= '<TR><TD ALIGN="right">'. $href->{$field}{'name'}. '</TD><TD>';
+
+ my $format = sub { shift };
+ $format = $href->{$field}{'format'} if exists($href->{$field}{'format'});
+
+ #XXX these should use elements/ fields... (or this whole thing should
+ #just use layer_fields instead of layer_callback)
+
+ if ( ! exists($href->{$field}{'type'}) ) {
+
+ $html .= qq!<INPUT TYPE="text" NAME="${layer}__$field" VALUE="!.
+ ( exists($options{$field})
+ ? &$format($options{$field})
+ : $href->{$field}{'default'} ).
+ qq!">!;
+
+ } elsif ( $href->{$field}{'type'} eq 'checkbox' ) {
+
+ $html .= qq!<INPUT TYPE="checkbox" NAME="${layer}__$field" VALUE=1 !.
+ ( exists($options{$field}) && $options{$field}
+ ? ' CHECKED'
+ : ''
+ ). '>';
+
+ } elsif ( $href->{$field}{'type'} =~ /^select/ ) {
+
+ $html .= '<SELECT';
+ $html .= ' MULTIPLE'
+ if $href->{$field}{'type'} eq 'select_multiple';
+ $html .= qq! NAME="${layer}__$field">!;
+
+ if ( $href->{$field}{'select_table'} ) {
+ foreach my $record (
+ qsearch( $href->{$field}{'select_table'},
+ $href->{$field}{'select_hash'} )
+ ) {
+ my $value = $record->getfield($href->{$field}{'select_key'});
+ $html .= qq!<OPTION VALUE="$value"!.
+ ( $options{$field} =~ /(^|, *)$value *(,|$)/ #?
+ ? ' SELECTED'
+ : ''
+ ).
+ '>'. $record->getfield($href->{$field}{'select_label'});
+ }
+ } elsif ( $href->{$field}{'select_options'} ) {
+ foreach my $key ( keys %{ $href->{$field}{'select_options'} } ) {
+ my $label = $href->{$field}{'select_options'}{$key};
+ $html .= qq!<OPTION VALUE="$key"!.
+ ( $options{$field} =~ /(^|, *)$key *(,|$)/ #?
+ ? ' SELECTED'
+ : ''
+ ).
+ '>'. $label;
+ }
+
+ } else {
+ $html .= '<font color="#ff0000">warning: '.
+ "don't know how to retreive options for $field select field".
+ '</font>';
+ }
+ $html .= '</SELECT>';
+
+ } elsif ( $href->{$field}{'type'} eq 'radio' ) {
+
+ my $radio =
+ qq!<INPUT TYPE="radio" NAME="${layer}__$field"!;
+
+ foreach my $key ( keys %{ $href->{$field}{'options'} } ) {
+ my $label = $href->{$field}{'options'}{$key};
+ $html .= qq!$radio VALUE="$key"!.
+ ( $options{$field} =~ /(^|, *)$key *(,|$)/ #?
+ ? ' CHECKED'
+ : ''
+ ).
+ "> $label<BR>";
+ }
+
+ }
+
+ $html .= '</TD></TR>';
+ }
+ $html .= '</TABLE>';
+
+ $html .= qq(<INPUT TYPE="hidden" NAME="${layer}__OPTIONS" VALUE=").
+ join(',', keys %{ $href } ). '">';
+
+ $html;
+
+ };
+
+ my %selectlayers = (
+ field => 'plan',
+ options => [ keys %plan_labels ],
+ labels => \%plan_labels,
+ curr_value => $object->plan,
+ layer_callback => $layer_callback,
+ );
+
+ my $return =
+ include('/elements/selectlayers.html', %selectlayers, 'layers_only'=>1 ).
+ '<SCRIPT TYPE="text/javascript">'.
+ include('/elements/selectlayers.html', %selectlayers, 'js_only'=>1 );
+
+ $return .=
+ "taxproduct_selectchanged(document.getElementById('taxproduct_select'));\n"
+ if $taxproducts;
+
+ $return .= '</SCRIPT>';
+
+ $return;
+
+};
+
+my %usage_class = map { ($_->classnum => $_->classname) }
+ qsearch('usage_class', {});
+$usage_class{setup} = 'Setup';
+$usage_class{recur} = 'Recurring';
+
+my %taxproduct_fields = ();
+my $end_callback = sub {
+ my( $cgi, $object, $fields, $opt ) = @_;
+
+ @taxproductnums = ( qw( setup recur ), sort (keys %taxproductnums) );
+
+ if ( $object->pkgpart ) {
+ foreach my $usage_class ( '', @taxproductnums ) {
+ $tax_override{$usage_class} =
+ join (",", map $_->taxclassnum,
+ qsearch( 'part_pkg_taxoverride', {
+ 'pkgpart' => $object->pkgpart,
+ 'usage_class' => $usage_class,
+ })
+ );
+ }
+ }
+
+ %taxproduct_fields =
+ map { $_ => [ "taxproductnum_$_",
+ { type => 'select-taxproduct',
+ #label => "$usage_class{$_} tax product",
+ },
+ "tax_override_$_",
+ { type => 'select-taxoverride' }
+ ]
+ }
+ @taxproductnums;
+
+ $taxproduct_fields{'(default)'} =
+ [ 'taxproductnum', { type => 'select-taxproduct',
+ #label => 'Default tax product',
+ },
+ 'tax_override', { type => 'select-taxoverride' },
+ ];
+};
+
+my $taxproduct_values = sub {
+ my ($cgi, $object, $flags) = @_;
+ my $routine =
+ sub { my $layer = shift;
+ my @fields = @{$taxproduct_fields{$layer}};
+ my @values = ();
+ while( @fields ) {
+ my $field = shift @fields;
+ shift @fields;
+ $field =~ /^taxproductnum_\w+$/ &&
+ push @values, ( $field => $options{"usage_$field"} );
+ $field =~ /^tax_override_(\w+)$/ &&
+ push @values, ( $field => $tax_override{$1} );
+ $field =~ /^taxproductnum$/ &&
+ push @values, ( $field => $object->taxproductnum );
+ $field =~ /^tax_override$/ &&
+ push @values, ( $field => $tax_override{''} );
+ }
+ { (@values) };
+ };
+
+ my @result =
+ map { ( $_ => { &{$routine}($_) } ) } ( '(default)', @taxproductnums );
+ return({ @result });
+
+};
+
+my $field_callback = sub {
+ my ($cgi, $object, $fieldref) = @_;
+
+ my $field = $fieldref->{field};
+ if ($field eq 'taxproductnums') {
+ $fieldref->{value} = join(',', @taxproductnums);
+ } elsif ($field eq 'taxproduct_select') {
+ $fieldref->{options} = [ '(default)', @taxproductnums ];
+ $fieldref->{labels} = { ( '(default)' => '(default)' ),
+ map {( $_ => ($usage_class{$_} || $_) )}
+ @taxproductnums
+ };
+ $fieldref->{layer_fields} = \%taxproduct_fields;
+ $fieldref->{layer_values_callback} = $taxproduct_values;
+ }
+};
+
+</%init>
diff --git a/httemplate/edit/part_pkg_taxclass.html b/httemplate/edit/part_pkg_taxclass.html
new file mode 100644
index 0000000..e767057
--- /dev/null
+++ b/httemplate/edit/part_pkg_taxclass.html
@@ -0,0 +1,32 @@
+<% include('/elements/header.html', "$action taxclass") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p1 %>process/part_pkg_taxclass.html" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="taxclassnum" VALUE="">
+
+Tax class <INPUT TYPE="text" NAME="taxclass" VALUE="<% $taxclass |h %>">
+
+<BR><BR>
+<INPUT TYPE="submit" VALUE="<% $action %> taxclass">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $taxclass = '';
+if ( $cgi->param('error') ) {
+ $taxclass = $cgi->param('taxclass');
+}
+
+my $action = 'Add';
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/part_pkg_taxoverride.html b/httemplate/edit/part_pkg_taxoverride.html
new file mode 100644
index 0000000..61dfa2a
--- /dev/null
+++ b/httemplate/edit/part_pkg_taxoverride.html
@@ -0,0 +1,132 @@
+<% include('/elements/header-popup.html', 'Override taxes', '', 'onload="resizeFrames()"') %>
+
+<TABLE WIDTH="100%" HEIGHT="100%">
+ <TR><TD>
+ <iframe name="selected" src="<% $p %>browse/tax_class.html?_type=select;magic=select;maxrecords=15;offset=<% $selected_offset %>;selected=<% $selected %>;" width="100%" frameborder="0" border="0" id="selectorSelected" scrolling="no">
+</iframe>
+ <BR>
+ </TD></TR>
+
+ <TR><TD>
+<FORM="dummy">
+ <CENTER>
+ <INPUT type="submit" value="Finish" onclick="s=fetchSelected(); s.shift(); parent.document.getElementById('<% $element_name || "tax_override" %>').value=s.toString(); parent.<% $onclick %>();">
+ <INPUT type="reset" value="Cancel" onclick="parent.<% $onclick %>();">
+ </CENTER>
+</FORM>
+ </TD></TR>
+
+ <TR><TD>
+ <iframe name="unselected" src="<% $p %>browse/tax_class.html?_type=select;magic=omit;maxrecords=15;offset=<% $unselected_offset %>;omit=<% $selected %>;" width="100%" frameborder="0" border="0" id="selectorUnselected" scrolling="no">
+</iframe>
+ <BR>
+ </TD></TR>
+
+</TABLE>
+<SCRIPT>
+
+ function resizeFrames() {
+ //frames['selected'].style.height =
+ // frames['selected'].contentWindow.document.body.scrollHeight + "px";
+ //frames['unselected'].style.height =
+ // frames['unselected'].contentWindow.document.body.scrollHeight + "px";
+ var f = document.getElementById('selectorSelected');
+ f.style.height = f.contentWindow.document.body.scrollHeight + "px";
+ var f = document.getElementById('selectorUnselected');
+ f.style.height = f.contentWindow.document.body.scrollHeight + "px";
+ }
+
+ function fetchOffset(search) {
+ var value = 0;
+ if (search.length > 1) {
+ var params = search.split(';');
+ for (i=0; i<params.length; i++) {
+ if (params[i].substr(0,7) == 'offset=') {
+ value = params[i].substr(7);
+ }
+ }
+ }
+ return value;
+ }
+
+ function fetchOffsetStrings() {
+ return 'selected_offset=' +
+ fetchOffset(frames['selected'].location.search) + ';' +
+ 'unselected_offset=' +
+ fetchOffset(frames['unselected'].location.search) + ';';
+ }
+
+ function fetchSelected() {
+ var i;
+ var selected = new Array;
+ var replace = '?';
+ if (window.location.search.length > 1) {
+ var search = window.location.search.substr(1).split(';');
+ for (i=0; i<search.length; i++) {
+ if (search[i].substr(0,9) == 'selected=') {
+ selected = search[i].substr(9).split(',')
+ }else if (search[i].substr(0,16) == 'selected_offset=') {
+ }else if (search[i].substr(0,18) == 'unselected_offset=') {
+ }else if (search[i].length) {
+ replace += search[i] + ';';
+ }
+ }
+ }
+ selected.unshift(replace);
+ return selected;
+ }
+ function doUnselect(classnum) {
+ var selected = fetchSelected();
+ var search = selected.shift();
+ //alert("discovered: "+selected.toString());
+ var i=-1, j=-1, k=selected.length;
+ while(++j < k) {
+ if (!(selected[j]==classnum)) {
+ selected[++i]=selected[j];
+ }
+ }
+ selected.length = ++i;
+ //alert("finished: "+selected.toString());
+
+ search += "selected=" + selected.toString() + ';';
+ window.location.search = search + fetchOffsetStrings();
+ }
+ function doSelect(classnum) {
+ var selected = fetchSelected();
+ var search = selected.shift();
+ selected.push(classnum);
+ search += "selected=" + selected.toString() + ';';
+ window.location.search = search + fetchOffsetStrings();
+ }
+</SCRIPT>
+
+<% include('/elements/footer.html') %>
+<%once>
+
+my $conf = new FS::Conf;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+
+my $selected_offset = $1
+ if $cgi->param('selected_offset') =~/^(\d+)$/;
+
+my $unselected_offset = $1
+ if $cgi->param('unselected_offset') =~/^(\d+)$/;
+
+my $selected = $1
+ if $cgi->param('selected') =~/^([,\d]+)$/;
+
+my $element_name = $1
+ if $cgi->param('element_name') =~/^(\w+)$/;
+
+my $onclick = $1
+ if $cgi->param('onclick') =~/^(\w+)$/;
+
+$onclick = 'cClick' unless $onclick;
+
+</%init>
diff --git a/httemplate/edit/part_referral.html b/httemplate/edit/part_referral.html
new file mode 100755
index 0000000..daf8773
--- /dev/null
+++ b/httemplate/edit/part_referral.html
@@ -0,0 +1,19 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Advertising source',
+ 'table' => 'part_referral',
+ 'fields' => [ 'referral',
+ { field=>'agentnum', type=>'select-agent', },
+ ],
+ 'labels' => { 'referral' => 'Advertising source',
+ 'agentnum' => 'Agent',
+ },
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit advertising sources')
+ || $FS::CurrentUser::CurrentUser->access_right('Edit global advertising sources');
+
+</%init>
diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi
new file mode 100755
index 0000000..e0fb615
--- /dev/null
+++ b/httemplate/edit/part_svc.cgi
@@ -0,0 +1,361 @@
+<% include('/elements/header.html', "$action Service Definition",
+ menubar('View all service definitions' => "${p}browse/part_svc.cgi"),
+ #" onLoad=\"visualize()\""
+ )
+%>
+
+<FORM NAME="dummy">
+
+ Service Part #<% $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %>
+<BR><BR>
+Service <INPUT TYPE="text" NAME="svc" VALUE="<% $hashref->{svc} %>"><BR>
+Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR>
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $hashref->{svcpart} %>">
+<BR>
+Service definitions are the templates for items you offer to your customers.
+<UL><LI>svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, RADIUS entries for broadband, etc.)
+ <LI>svc_domain - Domains
+ <LI>svc_forward - mail forwarding
+ <LI>svc_www - Virtual domain website
+ <LI>svc_broadband - Broadband/High-speed Internet service (always-on)
+ <LI>svc_phone - Customer phone numbers
+ <LI>svc_external - Externally-tracked service
+<!-- <LI>svc_charge - One-time charges (Partially unimplemented)
+ <LI>svc_wo - Work orders (Partially unimplemented)
+-->
+</UL>
+For the selected table, you can give fields default or fixed (unchangable)
+values, or select an inventory class to manually or automatically fill in
+that field.
+<BR><BR>
+
+% #YUCK. false laziness w/part_svc.pm. go away virtual fields, please
+% my %vfields;
+% foreach my $svcdb ( FS::part_svc->svc_tables() ) {
+% eval "use FS::$svcdb;";
+% my $self = "FS::$svcdb"->new;
+% $vfields{$svcdb} = {};
+% foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
+% my $pvf = $self->pvf($field);
+% $vfields{$svcdb}->{$field} = $pvf;
+% #warn "\$vfields{$svcdb}->{$field} = $pvf";
+% } #next $field
+% } #next $svcdb
+%
+% #code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm
+% # and generalize the subs
+% # condition sub is tested to see whether to disable display of this choice
+% # params: ( $def, $layer, $field ) (see SUB below)
+% my $inv_sub = sub {
+% $_[0]->{disable_inventory}
+% || $_[0]->{'type'} ne 'text'
+% };
+% tie my %flag, 'Tie::IxHash',
+% '' => { 'desc' => 'No default', },
+% 'D' => { 'desc' => 'Default',
+% 'condition' =>
+% sub { $_[0]->{disable_default} },
+% },
+% 'F' => { 'desc' => 'Fixed (unchangeable)',
+% 'condition' =>
+% sub { $_[0]->{disable_fixed} },
+% },
+% 'S' => { 'desc' => 'Selectable Choice',
+% 'condition' =>
+% sub { !ref($_[0]) || $_[0]->{disable_select} },
+% },
+%# need to template-ize httemplate/edit/svc_* first
+%# 'M' => { 'desc' => 'Manual selection from inventory',
+%# 'condition' => $inv_sub,
+%# },
+% 'A' => { 'desc' => 'Automatically fill in from inventory',
+% 'condition' => $inv_sub,
+% },
+% 'X' => { 'desc' => 'Excluded',
+% 'condition' =>
+% sub { ! $vfields{$_[1]}->{$_[2]} },
+%
+% },
+% ;
+%
+% my @dbs = $hashref->{svcdb}
+% ? ( $hashref->{svcdb} )
+% : FS::part_svc->svc_tables();
+%
+% tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
+% my $widget = new HTML::Widgets::SelectLayers(
+% #'selected_layer' => $p_svcdb,
+% 'selected_layer' => $hashref->{svcdb} || 'svc_acct',
+% 'options' => \%svcdb,
+% 'form_name' => 'dummy',
+% #'form_action' => 'process/part_svc.cgi',
+% 'form_action' => 'part_svc.cgi', #self
+% 'form_text' => [ qw( svc svcpart ) ],
+% 'form_checkbox' => [ 'disabled' ],
+% 'layer_callback' => sub {
+% my $layer = shift;
+%
+% my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!;
+%
+% my $columns = 3;
+% my $count = 0;
+% my @part_export =
+% map { qsearch( 'part_export', {exporttype => $_ } ) }
+% keys %{FS::part_export::export_info($layer)};
+% $html .= '<BR><BR>'. table().
+% "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>";
+% foreach my $part_export ( @part_export ) {
+% $html .= '<TD><INPUT TYPE="checkbox"'.
+% ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" ';
+% $html .= 'CHECKED'
+% if ( $clone || $part_svc->svcpart ) #null svcpart search causing error
+% && qsearchs( 'export_svc', {
+% exportnum => $part_export->exportnum,
+% svcpart => $clone || $part_svc->svcpart });
+% $html .= '>'. $part_export->exportnum. ': '. $part_export->exporttype.
+% ' to '. $part_export->machine. '</TD>';
+% $count++;
+% $html .= '</TR><TR>' unless $count % $columns;
+% }
+% $html .= '</TR></TABLE><BR><BR>';
+%
+% $html .= include('/elements/table-grid.html', 'cellpadding' => 4 ).
+% '<TR>'.
+% '<TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>'.
+% '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'.
+% '</TR>';
+%
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor;
+%
+% #yucky kludge
+% my @fields = defined( dbdef->table($layer) )
+% ? grep { $_ ne 'svcnum' } fields($layer)
+% : ();
+% push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge
+% $part_svc->svcpart($clone) if $clone; #haha, undone below
+%
+%
+% foreach my $field (@fields) {
+%
+% #my $def = $defs{$layer}{$field};
+% my $def = FS::part_svc->svc_table_fields($layer)->{$field};
+% my $label = $def->{'def_label'} || $def->{'label'};
+% my $formatter = $def->{'format'} || sub { shift };
+% my $part_svc_column = $part_svc->part_svc_column($field);
+% my $value = &$formatter($part_svc_column->columnvalue);
+% my $flag = $part_svc_column->columnflag;
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% $html .= qq!<TR><TD CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!.
+% ( $label || $field ).
+% "</TD>";
+% $flag = '' if $def->{type} eq 'disabled';
+%
+% $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!;
+%
+% if ( $def->{type} eq 'disabled' ) {
+%
+% $html .= 'No default';
+%
+% } else {
+%
+% $html .= qq!<SELECT NAME="${layer}__${field}_flag"!.
+% qq! onChange="${layer}__${field}_flag_changed(this)">!;
+%
+% foreach my $f ( keys %flag ) {
+%
+% #here is where the SUB from above is called, to skip some choices
+% next if $flag{$f}->{condition}
+% && &{ $flag{$f}->{condition} }( $def, $layer, $field );
+%
+% $html .= qq!<OPTION VALUE="$f"!.
+% ' SELECTED'x($flag eq $f ).
+% '>'. $flag{$f}->{desc};
+%
+% }
+%
+% $html .= '</SELECT>';
+%
+% $html .= join("\n",
+% '<SCRIPT>',
+% " function ${layer}__${field}_flag_changed(what) {",
+% ' var f = what.options[what.selectedIndex].value;',
+% ' if ( f == "" || f == "X" ) { //disable',
+% " what.form.${layer}__${field}.disabled = true;".
+% " what.form.${layer}__${field}.style.backgroundColor = '#dddddd';".
+% " if ( what.form.${layer}__${field}_classnum ) {".
+% " what.form.${layer}__${field}_classnum.disabled = true;".
+% " what.form.${layer}__${field}_classnum.style.backgroundColor = '#dddddd';".
+% " }".
+% ' } else if ( f == "D" || f == "F" || f =="S" ) { //enable, text box',
+% " what.form.${layer}__${field}.disabled = false;".
+% " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
+% " if ( f == 'S' || '${field}' == 'usergroup' ) {". # kludge
+% " what.form.${layer}__${field}.multiple = true;".
+% " } else {".
+% " what.form.${layer}__${field}.multiple = false;".
+% " }".
+% " what.form.${layer}__${field}.style.display = '';".
+% " if ( what.form.${layer}__${field}_classnum ) {".
+% " what.form.${layer}__${field}_classnum.disabled = false;".
+% " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
+% " what.form.${layer}__${field}_classnum.style.display = 'none';".
+% " }".
+% ' } else if ( f == "M" || f == "A" ) { //enable, inventory',
+% " what.form.${layer}__${field}.disabled = false;".
+% " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
+% " what.form.${layer}__${field}.style.display = 'none';".
+% " if ( what.form.${layer}__${field}_classnum ) {".
+% " what.form.${layer}__${field}_classnum.disabled = false;".
+% " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
+% " what.form.${layer}__${field}_classnum.style.display = '';".
+% " }".
+% ' }',
+% ' }',
+% '</SCRIPT>',
+% );
+%
+% }
+%
+% $html .= qq!</TD><TD CLASS="grid" BGCOLOR="$bgcolor">!;
+%
+% my $disabled = $flag ? ''
+% : 'DISABLED STYLE="background-color: #dddddd"';
+%
+% if ( !$def->{type} || $def->{type} eq 'text' ) {
+%
+% my $nodisplay = ' STYLE="display:none"';
+% my $is_inv = ( $flag =~ /^[MA]$/ );
+%
+% $html .=
+% qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value" !.
+% $disabled.
+% ( $is_inv ? $nodisplay : $disabled ).
+% '>';
+%
+% $html .= include('/elements/select-table.html',
+% 'element_name' => "${layer}__${field}_classnum",
+% 'element_etc' => ( $is_inv
+% ? $disabled
+% : $nodisplay
+% ),
+% 'table' => 'inventory_class',
+% 'name_col' => 'classname',
+% 'value' => $value,
+% 'empty_label' => 'Select inventory class',
+% );
+%
+% } elsif ( $def->{type} eq 'select' ) {
+%
+% $html .= qq!<SELECT NAME="${layer}__${field}" $disabled!;
+% $html .= ' MULTIPLE' if $flag eq 'S';
+% $html .= '>';
+% $html .= '<OPTION> </OPTION>' unless $value;
+% if ( $def->{select_table} ) {
+% foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
+% my $rvalue = $record->getfield($def->{select_key});
+% my $select_label = $def->{select_label};
+% $html .= qq!<OPTION VALUE="$rvalue"!.
+% (grep(/^$rvalue$/, split(',',$value)) ? ' SELECTED>' : '>' ).
+% $record->$select_label(). '</OPTION>';
+% } #next $record
+% } else { # select_list
+% foreach my $item ( @{$def->{select_list}} ) {
+% $html .= qq!<OPTION VALUE="$item"!.
+% (grep(/^$item$/, split(',',$value)) ? ' SELECTED>' : '>' ).
+% $item. '</OPTION>';
+% } #next $item
+% } #endif
+% $html .= '</SELECT>';
+%
+% } elsif ( $def->{type} eq 'radius_usergroup_selector' ) {
+%
+% #XXX disable the RADIUS usergroup selector? ugh it sure does need
+% #an overhaul, people have dum group problems because of it
+%
+% $html .= FS::svc_acct::radius_usergroup_selector(
+% [ split(',', $value) ], "${layer}__${field}" );
+%
+% } elsif ( $def->{type} eq 'disabled' ) {
+%
+% $html .=
+% qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!;
+%
+% } else {
+%
+% $html .= '<font color="#ff0000">unknown type'. $def->{type};
+%
+% }
+%
+% $html .= "</TD></TR>\n";
+%
+% } #foreach my $field (@fields) {
+%
+% $part_svc->svcpart('') if $clone; #undone
+% $html .= "</TABLE>";
+%
+% $html .= include('/elements/progress-init.html',
+% $layer, #form name
+% [ qw(svc svcpart disabled exportnum), @fields ],
+% 'process/part_svc.cgi',
+% $p.'browse/part_svc.cgi',
+% $layer,
+% );
+% $html .= '<BR><INPUT NAME="submit" TYPE="button" VALUE="'.
+% ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '" '.
+% ' onClick="document.'. "$layer.submit.disabled=true; ".
+% "fixup(document.$layer); $layer". 'process();">';
+%
+% #$html .= '<BR><INPUT TYPE="submit" VALUE="'.
+% # ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">';
+%
+% $html;
+%
+% },
+% );
+%
+%
+
+Table <% $widget->html %>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $part_svc;
+my $clone = '';
+if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone
+ #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query";
+ $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } )
+ or die "unknown svcpart: $1";
+ $clone = $part_svc->svcpart;
+ $part_svc->svcpart('');
+} elsif ( $cgi->keywords ) { #edit
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "malformed query: $query";
+ $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } )
+ or die "unknown svcpart: $1";
+} else { #adding
+ $part_svc = new FS::part_svc {};
+}
+
+my $action = $part_svc->svcpart ? 'Edit' : 'Add';
+my $hashref = $part_svc->hashref;
+# my $p_svcdb = $part_svc->svcdb || 'svc_acct';
+
+
+
+</%init>
+
+
+
diff --git a/httemplate/edit/part_virtual_field.cgi b/httemplate/edit/part_virtual_field.cgi
new file mode 100644
index 0000000..04ba9b0
--- /dev/null
+++ b/httemplate/edit/part_virtual_field.cgi
@@ -0,0 +1,104 @@
+<% include('/elements/header.html', "$action Virtual Field Definition") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%$p1%>process/generic.cgi" METHOD="POST">
+
+<INPUT TYPE="hidden" NAME="table" VALUE="part_virtual_field">
+<INPUT TYPE="hidden" NAME="redirect_ok"
+ VALUE="<%popurl(2)%>browse/part_virtual_field.cgi">
+<INPUT TYPE="hidden" NAME="vfieldpart" VALUE="<%
+ $vfieldpart%>">
+Field #<B><%$vfieldpart or "(NEW)"%></B><BR><BR>
+
+<%ntable("#cccccc",2)%>
+ <TR>
+ <TD ALIGN="right">Name</TD>
+ <TD><INPUT TYPE="text" NAME="name" MAXLENGTH=32 VALUE="<%
+ $part_virtual_field->name%>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Table</TD>
+ <TD>
+% if ($action eq 'Add') {
+
+ <SELECT SIZE=1 NAME="dbtable">
+%
+% my $dbdef = dbdef; # ick
+% #foreach my $dbtable (sort { $a cmp $b } $dbdef->tables) {
+% foreach my $dbtable (qw( svc_broadband router )) {
+% if ($dbtable !~ /^h_/
+% and $dbdef->table($dbtable)->primary_key) {
+
+ <OPTION VALUE="<%$dbtable%>"><%$dbtable%></OPTION>
+%
+% }
+% }
+%
+</SELECT>
+%
+% } else { # Edit
+%
+<%$part_virtual_field->dbtable%>
+ <INPUT TYPE="hidden" NAME="dbtable" VALUE="<%$part_virtual_field->dbtable%>">
+% }
+
+ </TD>
+ <TR>
+ <TD ALIGN="right">Label</TD>
+ <TD><INPUT TYPE="text" NAME="label" MAXLENGTH="80" VALUE="<%
+ $part_virtual_field->label%>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Length</TD>
+ <TD><INPUT TYPE="text" NAME="length" MAXLENGTH=4 VALUE="<%
+ $part_virtual_field->length%>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Check</TD>
+ <TD><TEXTAREA COLS="20" ROWS="4" NAME="check_block"><%
+ $part_virtual_field->check_block%></TEXTAREA></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">List source</TD>
+ <TD><TEXTAREA COLS="20" ROWS="4" NAME="list_source"><%
+ $part_virtual_field->list_source%></TEXTAREA></TD>
+ </TR>
+</TABLE><BR><INPUT TYPE="submit" VALUE="Submit">
+
+</FORM>
+
+<BR>
+<FONT SIZE=-2>If you don't understand what <I>check_block</I> and
+<I>list_source</I> mean, <B>LEAVE THEM BLANK</B>. We mean it.</FONT>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my ($vfieldpart, $part_virtual_field);
+
+if ( $cgi->param('error') ) {
+ $part_virtual_field = new FS::part_virtual_field ( {
+ map { $_, scalar($cgi->param($_)) } fields('part_virtual_field')});
+ $vfieldpart = $part_virtual_field->vfieldpart;
+} else {
+ my($query) = $cgi->keywords;
+ if ( $query =~ /^(\d+)$/ ) { #editing
+ $vfieldpart=$1;
+ $part_virtual_field=qsearchs('part_virtual_field',
+ {'vfieldpart' => $vfieldpart})
+ or die "Unknown vfieldpart!";
+
+ } else { #adding
+ $part_virtual_field = new FS::part_virtual_field({});
+ }
+}
+my $action = $part_virtual_field->vfieldpart ? 'Edit' : 'Add';
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html
new file mode 100644
index 0000000..e3893cf
--- /dev/null
+++ b/httemplate/edit/payment_gateway.html
@@ -0,0 +1,132 @@
+<% include("/elements/header.html","$action Payment gateway", menubar(
+ 'View all payment gateways' => $p. 'browse/payment_gateway.html',
+)) %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%popurl(1)%>process/payment_gateway.html" METHOD=POST>
+<INPUT TYPE="hidden" NAME="gatewaynum" VALUE="<% $payment_gateway->gatewaynum %>">
+Gateway #<% $payment_gateway->gatewaynum || "(NEW)" %>
+
+<% ntable('#cccccc', 2, '') %>
+
+<TR>
+ <TH ALIGN="right">Gateway: </TH>
+ <TD>
+% if ( $payment_gateway->gatewaynum ) {
+
+
+ <% $payment_gateway->gateway_module %>
+ <INPUT TYPE="hidden" NAME="gateway_module" VALUE="<% $payment_gateway->gateway_module %>">
+% } else {
+
+
+ <SELECT NAME="gateway_module" SIZE=1>
+% foreach my $module ( qw(
+% 2CheckOut
+% AuthorizeNet
+% BankOfAmerica
+% Beanstream
+% Capstone
+% Cardstream
+% CashCow
+% CyberSource
+% eSec
+% eSelectPlus
+% Exact
+% iAuthorizer
+% IPaymentTPG
+% Jettis
+% LinkPoint
+% MerchantCommerce
+% Network1Financial
+% OCV
+% OpenECHO
+% PayConnect
+% PayflowPro
+% PaymentsGateway
+% PXPost
+% SecureHostingUPG
+% Skipjack
+% StGeorge
+% SurePay
+% TCLink
+% TransactionCentral
+% TransFirsteLink
+% VirtualNet
+% ) ) {
+%
+
+ <OPTION VALUE="<% $module %>"><% $module %>
+% }
+
+ </SELECT>
+% }
+
+
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">Username: </TH>
+ <TD><INPUT TYPE="text" NAME="gateway_username" VALUE="<% $payment_gateway->gateway_username %>"></TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">Password: </TH>
+ <TD><INPUT TYPE="text" NAME="gateway_password" VALUE="<% $payment_gateway->gateway_password %>"></TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">Action: </TH>
+ <TD>
+ <SELECT NAME="gateway_action" SIZE=1>
+% foreach my $action (
+% 'Normal Authorization',
+% 'Authorization Only',
+% 'Authorization Only, Post Authorization',
+% ) {
+%
+
+ <OPTION VALUE="<% $action %>"<% $action eq $payment_gateway->gateway_action ? ' SELECTED' : '' %>><% $action %>
+% }
+
+ </SELECT>
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">Options: (Name/Value pairs, one element per line)</TH>
+ <TD>
+ <TEXTAREA ROWS="5" NAME="gateway_options"><% join("\r", $payment_gateway->options ) %></TEXTAREA>
+ </TD>
+</TR>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="<% $payment_gateway->gatewaynum ? "Apply changes" : "Add gateway" %>">
+ </FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $payment_gateway;
+if ( $cgi->param('error') ) {
+ $payment_gateway = new FS::payment_gateway ( {
+ map { $_, scalar($cgi->param($_)) } fields('payment_gateway')
+ } );
+} elsif ( $cgi->keywords ) {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $payment_gateway = qsearchs( 'payment_gateway', { 'gatewaynum' => $1 } );
+} else { #adding
+ $payment_gateway = new FS::payment_gateway {};
+}
+my $action = $payment_gateway->gatewaynum ? 'Edit' : 'Add';
+#my $hashref = $payment_gateway->hashref;
+
+</%init>
diff --git a/httemplate/edit/pkg_category.html b/httemplate/edit/pkg_category.html
new file mode 100644
index 0000000..fdc8da6
--- /dev/null
+++ b/httemplate/edit/pkg_category.html
@@ -0,0 +1,22 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Package Category',
+ 'table' => 'pkg_category',
+ 'fields' => [
+ 'categoryname',
+ { field=>'disabled', type=>'checkbox', value=>'Y', },
+ ],
+ 'labels' => {
+ 'categorynum' => 'Category number',
+ 'categoryname' => 'Category name',
+ 'disabled' => 'Disable category',
+ },
+ 'viewall_dir' => 'browse',
+ )
+
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/pkg_class.html b/httemplate/edit/pkg_class.html
new file mode 100644
index 0000000..26bc8ba
--- /dev/null
+++ b/httemplate/edit/pkg_class.html
@@ -0,0 +1,28 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Package Class',
+ 'table' => 'pkg_class',
+ 'fields' => [
+ 'classname',
+ (scalar(@category)
+ ? { field=>'categorynum', type=>'select-table', 'empty_label'=>'(none)', 'table'=>'pkg_category', 'name_col'=>'categoryname' }
+ : { field=>'categorynum', type=>'hidden' }
+ ),
+ { field=>'disabled', type=>'checkbox', value=>'Y', },
+ ],
+ 'labels' => {
+ 'classnum' => 'Class number',
+ 'classname' => 'Class name',
+ 'categorynum' => 'Category',
+ 'disabled' => 'Disable class',
+ },
+ 'viewall_dir' => 'browse',
+ )
+
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @category = qsearch('pkg_category', { 'disabled' => '' });
+</%init>
diff --git a/httemplate/edit/prepay_credit.cgi b/httemplate/edit/prepay_credit.cgi
new file mode 100644
index 0000000..9e1c30b
--- /dev/null
+++ b/httemplate/edit/prepay_credit.cgi
@@ -0,0 +1,110 @@
+<% include("/elements/header.html",'Generate prepaid cards'. ($agent ? ' for '. $agent->agent : '') ) %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%popurl(1)%>process/prepay_credit.cgi" METHOD="POST" NAME="OneTrueForm" onSubmit="document.OneTrueForm.submit.disabled=true">
+
+Generate
+<INPUT TYPE="text" NAME="num" VALUE="<% $cgi->param('num') || '(quantity)' |h %>" SIZE=10 MAXLENGTH=10 onFocus="if ( this.value == '(quantity)' ) { this.value = ''; }">
+
+<SELECT NAME="type">
+% foreach (qw(alpha alphanumeric numeric)) {
+ <OPTION<% $cgi->param('type') eq $_ ? ' SELECTED' : '' %>><% $_ %>
+% }
+</SELECT>
+
+prepaid cards
+
+<BR>for <SELECT NAME="agentnum"><OPTION>(any agent)
+% foreach my $opt_agent ( qsearch('agent', { 'disabled' => '' } ) ) {
+
+ <OPTION VALUE="<% $opt_agent->agentnum %>"<% $opt_agent->agentnum == $agentnum ? ' SELECTED' : '' %>><% $opt_agent->agent %>
+% }
+
+</SELECT>
+
+<TABLE>
+<TR><TD>Value:
+$<INPUT TYPE="text" NAME="amount" SIZE=8 MAXLENGTH=7 VALUE="<% $cgi->param('amount') |h %>">
+</TD>
+<TD>and/or
+<INPUT TYPE="text" NAME="seconds" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('seconds') |h %>">
+<SELECT NAME="multiplier">
+% foreach my $multiplier ( keys %multiplier ) {
+
+ <OPTION VALUE="<% $multiplier %>"<% $cgi->param('multiplier') eq $multiplier ? ' SELECTED' : '' %>><% $multiplier{$multiplier} %>
+% }
+
+</SELECT>
+</TD></TR>
+<TR><TD></TD>
+<TD>and/or
+<INPUT TYPE="text" NAME="upbytes" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('upbytes') |h %>">
+<SELECT NAME="upmultiplier">
+% foreach my $multiplier ( keys %bytemultiplier ) {
+
+ <OPTION VALUE="<% $multiplier %>"<% $cgi->param('upmultiplier') eq $multiplier ? ' SELECTED' : '' %>><% $bytemultiplier{$multiplier} %>
+% }
+
+</SELECT> upload
+</TD></TR>
+<TR><TD></TD>
+<TD>and/or
+<INPUT TYPE="text" NAME="downbytes" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('downbytes') |h %>">
+<SELECT NAME="downmultiplier">
+% foreach my $multiplier ( keys %bytemultiplier ) {
+
+ <OPTION VALUE="<% $multiplier %>"<% $cgi->param('downmultiplier') eq $multiplier ? ' SELECTED' : '' %>><% $bytemultiplier{$multiplier} %>
+% }
+
+</SELECT> download
+</TD></TR>
+<TR><TD></TD>
+<TD>and/or
+<INPUT TYPE="text" NAME="totalbytes" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('totalbytes') |h %>">
+<SELECT NAME="totalmultiplier">
+% foreach my $multiplier ( keys %bytemultiplier ) {
+
+ <OPTION VALUE="<% $multiplier %>"<% $cgi->param('totalmultiplier') eq $multiplier ? ' SELECTED' : '' %>><% $bytemultiplier{$multiplier} %>
+% }
+
+</SELECT> total transfer
+</TD></TR>
+</TABLE>
+<BR><BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="Generate" onSubmit="this.disabled = true">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $agent = '';
+my $agentnum = '';
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agent = qsearchs('agent', { 'agentnum' => $agentnum=$1 } );
+}
+
+tie my %multiplier, 'Tie::IxHash',
+ 1 => 'seconds',
+ 60 => 'minutes',
+ 3600 => 'hours',
+;
+
+tie my %bytemultiplier, 'Tie::IxHash',
+ 1 => 'bytes',
+ 1000 => 'Kbytes',
+ 1000000 => 'Mbytes',
+ 1000000000 => 'Gbytes',
+;
+
+$cgi->param('multiplier', '60') unless $cgi->param('multiplier');
+$cgi->param('upmultiplier', '1000000') unless $cgi->param('upmultiplier');
+$cgi->param('downmultiplier', '1000000') unless $cgi->param('downmultiplier');
+$cgi->param('totalmultiplier','1000000') unless $cgi->param('totalmultiplier');
+
+</%init>
diff --git a/httemplate/edit/process/REAL_cust_pkg.cgi b/httemplate/edit/process/REAL_cust_pkg.cgi
new file mode 100755
index 0000000..ebcb7e4
--- /dev/null
+++ b/httemplate/edit/process/REAL_cust_pkg.cgi
@@ -0,0 +1,36 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "REAL_cust_pkg.cgi?". $cgi->query_string ) %>
+%} else {
+% my $custnum = $new->custnum;
+<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum#cust_pkg$pkgnum" ) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit customer package dates');
+
+my $pkgnum = $cgi->param('pkgnum') or die;
+my $old = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+my %hash = $old->hash;
+$hash{'setup'} = $cgi->param('setup') ? str2time($cgi->param('setup')) : '';
+$hash{'bill'} = $cgi->param('bill') ? str2time($cgi->param('bill')) : '';
+$hash{'last_bill'} =
+ $cgi->param('last_bill') ? str2time($cgi->param('last_bill')) : '';
+$hash{'adjourn'} = $cgi->param('adjourn') ? str2time($cgi->param('adjourn')) : '';
+$hash{'expire'} = $cgi->param('expire') ? str2time($cgi->param('expire')) : '';
+
+my $new;
+my $error;
+if ( $hash{'bill'} != $old->bill # if the next bill date was changed
+ && $hash{'bill'} < time # to a date in the past
+ && ! $cgi->param('bill_areyousure') # and it wasn't confirmed
+ )
+{
+ $error = '_bill_areyousure';
+} else {
+ $new = new FS::cust_pkg \%hash;
+ $error = $new->replace($old);
+}
+
+</%init>
diff --git a/httemplate/edit/process/access_group.html b/httemplate/edit/process/access_group.html
new file mode 100644
index 0000000..ab25cb3
--- /dev/null
+++ b/httemplate/edit/process/access_group.html
@@ -0,0 +1,28 @@
+% if ( $conf->exists('disable_acl_changes') ) {
+ ACL changes disabled in public demo.
+% } else {
+<% include( 'elements/process.html',
+ 'table' => 'access_group',
+ 'viewall_dir' => 'browse',
+ 'process_m2m' => { 'link_table' => 'access_groupagent',
+ 'target_table' => 'agent',
+ },
+ 'process_m2name' => {
+ 'link_table' => 'access_right',
+ 'link_static' => { 'righttype' => 'FS::access_group', },
+ 'num_col' => 'rightobjnum',
+ 'name_col' => 'rightname',
+ 'names_list' => [ FS::AccessRight->rights() ],
+ 'param_style' => 'link_table.value checkboxes',
+ },
+ )
+%>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+</%init>
diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html
new file mode 100644
index 0000000..ca6bb60
--- /dev/null
+++ b/httemplate/edit/process/access_user.html
@@ -0,0 +1,21 @@
+% if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+% $cgi->param('error', "The passwords do not match");
+% print $cgi->redirect(popurl(2) . "access_user.html?" . $cgi->query_string);
+% } else {
+<% include( 'elements/process.html',
+ 'table' => 'access_user',
+ 'viewall_dir' => 'browse',
+ 'copy_on_empty' => [ '_password' ],
+ 'clear_on_error' => [ '_password', '_password2' ],
+ 'process_m2m' => { 'link_table' => 'access_usergroup',
+ 'target_table' => 'access_group',
+ },
+ )
+%>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/addr_block/add.cgi b/httemplate/edit/process/addr_block/add.cgi
new file mode 100755
index 0000000..39d6348
--- /dev/null
+++ b/httemplate/edit/process/addr_block/add.cgi
@@ -0,0 +1,20 @@
+<% include( '../elements/process.html',
+ 'table' => 'addr_block',
+ 'redirect' => popurl(4). 'browse/addr_block.cgi?dummy=',
+ 'error_redirect' => popurl(4). 'browse/addr_block.cgi?',
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Broadband global configuration',
+
+ )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+$cgi->param('routernum', 0) # in FS::addr_block::check instead?
+ unless $cgi->param('routernum');
+
+</%init>
diff --git a/httemplate/edit/process/addr_block/allocate.cgi b/httemplate/edit/process/addr_block/allocate.cgi
new file mode 100755
index 0000000..40d04b3
--- /dev/null
+++ b/httemplate/edit/process/addr_block/allocate.cgi
@@ -0,0 +1,16 @@
+<% include( '../elements/process.html',
+ 'table' => 'addr_block',
+ 'copy_on_empty' => [ fields 'addr_block' ],
+ 'error_redirect' => popurl(3). 'allocate.html?',
+ 'popup_reload' => 'Block allocated',
+ )
+%>
+<%init>
+
+my $conf = new FS::Conf;
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+</%init>
diff --git a/httemplate/edit/process/addr_block/deallocate.cgi b/httemplate/edit/process/addr_block/deallocate.cgi
new file mode 100755
index 0000000..128824e
--- /dev/null
+++ b/httemplate/edit/process/addr_block/deallocate.cgi
@@ -0,0 +1,20 @@
+<% include( '../elements/process.html',
+ 'table' => 'addr_block',
+ 'copy_on_empty' => [ grep { $_ ne 'routernum' }
+ fields 'addr_block' ],
+ 'redirect' => popurl(4). 'browse/addr_block.cgi?',
+ 'error_redirect' => popurl(4). 'browse/addr_block.cgi?',
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Broadband global configuration',
+ )
+%>
+<%init>
+
+my $conf = new FS::Conf;
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+$cgi->param('routernum', 0); # just to be explicit about what we are doing
+</%init>
diff --git a/httemplate/edit/process/addr_block/manual_flag.cgi b/httemplate/edit/process/addr_block/manual_flag.cgi
new file mode 100755
index 0000000..dc0cbbb
--- /dev/null
+++ b/httemplate/edit/process/addr_block/manual_flag.cgi
@@ -0,0 +1,30 @@
+<% $cgi->redirect(popurl(4). "browse/addr_block.cgi?". $cgi->query_string ) %>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+my $error = '';
+$cgi->param('blocknum') =~ /^(\d+)$/ or die "invalid blocknum";
+my $blocknum = $1;
+
+my $addr_block = qsearchs({ 'table' => 'addr_block',
+ 'hashref' => { blocknum => $blocknum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql(
+ 'null_right' => 'Broadband global configuration'
+ ),
+ })
+ or $error = "Unknown blocknum: $blocknum";
+
+$addr_block->manual_flag($cgi->param('manual_flag'))
+ unless $error;
+
+$error ||= $addr_block->replace;
+
+$cgi->param('error', $error)
+ if $error;
+
+</%init>
diff --git a/httemplate/edit/process/addr_block/split.cgi b/httemplate/edit/process/addr_block/split.cgi
new file mode 100755
index 0000000..045fd30
--- /dev/null
+++ b/httemplate/edit/process/addr_block/split.cgi
@@ -0,0 +1,27 @@
+<% $cgi->redirect(popurl(4). "browse/addr_block.cgi?". $cgi->query_string ) %>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+my $error = '';
+$cgi->param('blocknum') =~ /^(\d+)$/ or die "invalid blocknum";
+my $blocknum = $1;
+
+my $addr_block = qsearchs({ 'table' => 'addr_block',
+ 'hashref' => { blocknum => $blocknum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql(
+ 'null_right' => 'Broadband global configuration'
+ ),
+ })
+ or $error = "Unknown blocknum: $blocknum";
+
+$error ||= $addr_block->split_block;
+
+$cgi->param('error', $error)
+ if $error;
+
+</%init>
diff --git a/httemplate/edit/process/agent.cgi b/httemplate/edit/process/agent.cgi
new file mode 100755
index 0000000..3cdf40c
--- /dev/null
+++ b/httemplate/edit/process/agent.cgi
@@ -0,0 +1,16 @@
+<% include( 'elements/process.html',
+ 'table' => 'agent',
+ 'viewall_dir' => 'browse',
+ 'viewall_ext' => 'cgi',
+ 'process_m2m' => { 'link_table' => 'access_groupagent',
+ 'target_table' => 'access_group',
+ },
+ 'edit_ext' => 'cgi',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/agent_payment_gateway.html b/httemplate/edit/process/agent_payment_gateway.html
new file mode 100644
index 0000000..5b5fd94
--- /dev/null
+++ b/httemplate/edit/process/agent_payment_gateway.html
@@ -0,0 +1,29 @@
+<% $cgi->redirect(popurl(3). "browse/agent.cgi") %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('agentnum') =~ /(\d+)$/ or die "illegal agentnum";
+my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+die "agentnum $1 not found" unless $agent;
+
+#my $old
+
+my @new = map {
+ my $cardtype = $_;
+ new FS::agent_payment_gateway {
+ ( map { $_ => scalar($cgi->param($_)) }
+ fields('agent_payment_gateway')
+ ),
+ 'cardtype' => $cardtype,
+ };
+ }
+ $cgi->param('cardtype');
+
+foreach my $new (@new) {
+ my $error = $new->insert;
+ die $error if $error;
+}
+
+</%init>
diff --git a/httemplate/edit/process/agent_type.cgi b/httemplate/edit/process/agent_type.cgi
new file mode 100755
index 0000000..ad5963b
--- /dev/null
+++ b/httemplate/edit/process/agent_type.cgi
@@ -0,0 +1,35 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "agent_type.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/agent_type.cgi") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $typenum = $cgi->param('typenum');
+my $old = qsearchs('agent_type',{'typenum'=>$typenum}) if $typenum;
+
+my $new = new FS::agent_type ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('agent_type')
+} );
+
+my $error;
+if ( $typenum ) {
+ $error = $new->replace($old);
+} else {
+ $error = $new->insert;
+ $typenum = $new->getfield('typenum');
+}
+
+ $error ||= $new->process_m2m(
+ 'link_table' => 'type_pkgs',
+ 'target_table' => 'part_pkg',
+ 'params' => scalar($cgi->Vars)
+ );
+
+</%init>
diff --git a/httemplate/edit/process/bulk-cust_main_county.html b/httemplate/edit/process/bulk-cust_main_county.html
new file mode 100644
index 0000000..e05192e
--- /dev/null
+++ b/httemplate/edit/process/bulk-cust_main_county.html
@@ -0,0 +1,63 @@
+% if ( $error ) { #better to redirect back to
+%# <% $cgi->redirect("$url?". $cgi->query_string ) %>
+ <% include('/elements/header-popup.html', 'Error adding taxes' ) %>
+
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT>
+ <BR><BR>
+
+ </BODY>
+ </HTML>
+
+% } else {
+ <% include('/elements/header-popup.html', 'Taxes added') %>
+
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY>
+ </HTML>
+% }
+<%init>
+
+$cgi->param('taxnum') =~ /^([\d,]+)$/
+ or die 'Guru Meditation #69'; #??? should have been passed in
+my @taxnum = split(',', $1);
+
+my $error = '';
+foreach my $taxnum ( @taxnum ) {
+
+ my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } )
+ or die "unknown taxnum: $taxnum";
+
+ if ( $cust_main_county->tax == 0 ) { #let's replace
+
+ foreach (qw( taxname tax exempt_amount setuptax recurtax )) {
+ $cust_main_county->set( $_ => scalar($cgi->param($_)) )
+ }
+
+ $error = $cust_main_county->replace and last;
+
+ } else { #let's insert a new record
+
+ my $new =
+ new FS::cust_main_county {
+ ( map { $_ => scalar($cgi->param($_)) }
+ qw( taxname tax exempt_amount setuptax recurtax )
+ ),
+ ( map { $_ => $cust_main_county->get($_) }
+ qw( country state county taxclass )
+ )
+ };
+
+ $error = $new->insert and last;
+
+ }
+
+}
+
+if ( $error ) {
+ $cgi->param('error', $error);
+}
+
+</%init>
diff --git a/httemplate/edit/process/bulk-cust_svc.cgi b/httemplate/edit/process/bulk-cust_svc.cgi
new file mode 100644
index 0000000..313b061
--- /dev/null
+++ b/httemplate/edit/process/bulk-cust_svc.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $server = new FS::UI::Web::JSRPC 'FS::part_svc::process_bulk_cust_svc', $cgi;
+
+</%init>
diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html
new file mode 100644
index 0000000..7356e61
--- /dev/null
+++ b/httemplate/edit/process/change-cust_pkg.html
@@ -0,0 +1,46 @@
+% if ($error) {
+% $cgi->param('error', $error);
+% $cgi->redirect(popurl(3). 'misc/change_pkg.cgi?'. $cgi->query_string );
+% } else {
+
+ <% header("Package changed") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+ </HTML>
+
+% }
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Change customer package');
+
+my $cust_pkg = qsearchs({
+ #'select' => 'cust_pkg.*',
+ 'table' => 'cust_pkg',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'pkgnum' => scalar($cgi->param('pkgnum')), },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+die 'unknown pkgnum' unless $cust_pkg;
+
+my %change = map { $_ => scalar($cgi->param($_)) }
+ qw( locationnum pkgpart );
+
+if ( $cgi->param('locationnum') == -1 ) {
+ my $cust_location = new FS::cust_location {
+ 'custnum' => $cust_pkg->custnum,
+ map { $_ => scalar($cgi->param($_)) }
+ qw( address1 address2 city county state zip country )
+ };
+ $change{'cust_location'} = $cust_location;
+}
+
+my $pkg_or_error = $cust_pkg->change( \%change );
+
+my $error = ref($pkg_or_error) ? '' : $pkg_or_error;
+
+</%init>
diff --git a/httemplate/edit/process/cust_bill_pay.cgi b/httemplate/edit/process/cust_bill_pay.cgi
new file mode 100755
index 0000000..2845d32
--- /dev/null
+++ b/httemplate/edit/process/cust_bill_pay.cgi
@@ -0,0 +1,13 @@
+<% include('elements/ApplicationCommon.html',
+ 'error_redirect' => 'cust_bill_pay.cgi',
+ 'src_table' => 'cust_pay',
+ 'src_thing' => 'payment',
+ 'link_table' => 'cust_bill_pay',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply payment');
+
+</%init>
diff --git a/httemplate/edit/process/cust_credit.cgi b/httemplate/edit/process/cust_credit.cgi
new file mode 100755
index 0000000..8715ad6
--- /dev/null
+++ b/httemplate/edit/process/cust_credit.cgi
@@ -0,0 +1,63 @@
+%if ( $error ) {
+% $cgi->param('reasonnum', $reasonnum);
+% $cgi->param('error', $error);
+% $dbh->rollback if $oldAutoCommit;
+%
+<% $cgi->redirect(popurl(2). "cust_credit.cgi?". $cgi->query_string ) %>
+%
+%} else {
+%
+% if ( $cgi->param('apply') eq 'yes' ) {
+% my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum })
+% or die "unknown custnum $custnum";
+% $cust_main->apply_credits;
+% }
+% #print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum");
+%
+% $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+%
+<% header('Credit sucessful') %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY></HTML>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Post credit');
+
+$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!";
+my $custnum = $1;
+
+$cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum";
+my $reasonnum = $1;
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+my $dbh = dbh;
+
+my $error = '';
+if ($reasonnum == -1) {
+
+ $error = 'Enter a new reason (or select an existing one)'
+ unless $cgi->param('newreasonnum') !~ /^\s*$/;
+ my $reason = new FS::reason({ 'reason_type' => $cgi->param('newreasonnumT'),
+ 'reason' => $cgi->param('newreasonnum'),
+ });
+ $error ||= $reason->insert;
+ $cgi->param('reasonnum', $reason->reasonnum)
+ unless $error;
+}
+
+unless ($error) {
+ my $new = new FS::cust_credit ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('cust_credit')
+ } );
+ $error = $new->insert;
+}
+
+</%init>
diff --git a/httemplate/edit/process/cust_credit_bill.cgi b/httemplate/edit/process/cust_credit_bill.cgi
new file mode 100755
index 0000000..c0f34ae
--- /dev/null
+++ b/httemplate/edit/process/cust_credit_bill.cgi
@@ -0,0 +1,13 @@
+<% include('elements/ApplicationCommon.html',
+ 'error_redirect' => 'cust_credit_bill.cgi',
+ 'src_table' => 'cust_credit',
+ 'src_thing' => 'credit',
+ 'link_table' => 'cust_credit_bill',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply credit');
+
+</%init>
diff --git a/httemplate/edit/process/cust_credit_refund.cgi b/httemplate/edit/process/cust_credit_refund.cgi
new file mode 100755
index 0000000..88420f8
--- /dev/null
+++ b/httemplate/edit/process/cust_credit_refund.cgi
@@ -0,0 +1,13 @@
+<% include('elements/ApplicationCommon.html',
+ 'error_redirect' => 'cust_credit_refund.cgi',
+ 'src_table' => 'cust_credit',
+ 'src_thing' => 'credit',
+ 'link_table' => 'cust_credit_refund',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply credit');
+
+</%init>
diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi
new file mode 100755
index 0000000..097d382
--- /dev/null
+++ b/httemplate/edit/process/cust_main.cgi
@@ -0,0 +1,210 @@
+% if ( $error ) {
+% $cgi->param('error', $error);
+%
+<% $cgi->redirect(popurl(2). "cust_main.cgi?". $cgi->query_string ) %>
+%
+% } else {
+%
+<% $cgi->redirect(popurl(3). "view/cust_main.cgi?". $new->custnum) %>
+%
+% }
+<%once>
+
+my $me = '[edit/process/cust_main.cgi]';
+my $DEBUG = 0;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit customer');
+
+my $error = '';
+
+#unmunge stuff
+
+$cgi->param('tax','') unless defined $cgi->param('tax');
+
+$cgi->param('refnum', (split(/:/, ($cgi->param('refnum'))[0] ))[0] );
+
+#my $payby = $cgi->param('payby');
+my $payby = $cgi->param('select'); # XXX key
+
+my %noauto = (
+ 'CARD' => 'DCRD',
+ 'CHEK' => 'DCHK',
+);
+$payby = $noauto{$payby}
+ if ! $cgi->param('payauto') && exists $noauto{$payby};
+
+$cgi->param('payby', $payby);
+
+if ( $payby ) {
+ if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+ $cgi->param('payinfo',
+ $cgi->param('payinfo1'). '@'. $cgi->param('payinfo2') );
+ }
+ $cgi->param('paydate',
+ $cgi->param( 'exp_month' ). '-'. $cgi->param( 'exp_year' ) );
+}
+
+my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') );
+push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
+push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX');
+$cgi->param('invoicing_list', join(',', @invoicing_list) );
+
+
+#create new record object
+
+my $new = new FS::cust_main ( {
+ map {
+ $_, scalar($cgi->param($_))
+# } qw(custnum agentnum last first ss company address1 address2 city county
+# state zip daytime night fax payby payinfo paydate payname tax
+# otaker refnum)
+ } fields('cust_main')
+} );
+
+if ( defined($cgi->param('same')) && $cgi->param('same') eq "Y" ) {
+ $new->setfield("ship_$_", '') foreach qw(
+ last first company address1 address2 city county state zip
+ country daytime night fax
+ );
+}
+
+if ( $cgi->param('birthdate') && $cgi->param('birthdate') =~ /^([ 0-9\-\/]{0,10})$/) {
+ my $conf = new FS::Conf;
+ my $format = $conf->config('date_format') || "%m/%d/%Y";
+ my $parser = DateTime::Format::Strptime->new(pattern => $format,
+ time_zone => 'floating',
+ );
+ my $dt = $parser->parse_datetime($1);
+ if ($dt) {
+ $new->setfield('birthdate', $dt->epoch);
+ $cgi->param('birthdate', $dt->epoch);
+ } else {
+# $error ||= $cgi->param('birthdate') . " is an invalid birthdate:" . $parser->errmsg;
+ $error ||= "Invalid birthdate: " . $cgi->param('birthdate') . ".";
+ $cgi->param('birthdate', '');
+ }
+}
+
+$new->setfield('paid', $cgi->param('paid') )
+ if $cgi->param('paid');
+
+#perhaps this stuff should go to cust_main.pm
+my $cust_pkg = '';
+my $svc_acct = '';
+if ( $new->custnum eq '' ) {
+
+ if ( $cgi->param('pkgpart_svcpart') ) {
+ my $x = $cgi->param('pkgpart_svcpart');
+ $x =~ /^(\d+)_(\d+)$/ or die "illegal pkgpart_svcpart $x\n";
+ my($pkgpart, $svcpart) = ($1, $2);
+ #false laziness: copied from FS::cust_pkg::order (which should become a
+ #FS::cust_main method)
+ my(%part_pkg);
+ # generate %part_pkg
+ # $part_pkg{$pkgpart} is true iff $custnum may purchase $pkgpart
+ my $agent = qsearchs('agent',{'agentnum'=> $new->agentnum });
+ #my($type_pkgs);
+ #foreach $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) {
+ # my($pkgpart)=$type_pkgs->pkgpart;
+ # $part_pkg{$pkgpart}++;
+ #}
+ # $pkgpart_href->{PKGPART} is true iff $custnum may purchase $pkgpart
+ my $pkgpart_href = $agent->pkgpart_hashref;
+ #eslaf
+
+ # this should wind up in FS::cust_pkg!
+ $error ||= "Agent ". $new->agentnum. " (type ". $agent->typenum. ") can't ".
+ "purchase pkgpart ". $pkgpart
+ #unless $part_pkg{ $pkgpart };
+ unless $pkgpart_href->{ $pkgpart };
+
+ $cust_pkg = new FS::cust_pkg ( {
+ #later 'custnum' => $custnum,
+ 'pkgpart' => $pkgpart,
+ } );
+ #$error ||= $cust_pkg->check;
+
+ #$cust_svc = new FS::cust_svc ( { 'svcpart' => $svcpart } );
+
+ #$error ||= $cust_svc->check;
+
+ my %svc_acct = (
+ 'svcpart' => $svcpart,
+ 'username' => $cgi->param('username'),
+ '_password' => $cgi->param('_password'),
+ 'popnum' => $cgi->param('popnum'),
+ );
+ $svc_acct{'domsvc'} = $cgi->param('domsvc')
+ if $cgi->param('domsvc');
+
+ $svc_acct = new FS::svc_acct \%svc_acct;
+
+ #and just in case you were silly
+ $svc_acct->svcpart($svcpart);
+ $svc_acct->username($cgi->param('username'));
+ $svc_acct->_password($cgi->param('_password'));
+ $svc_acct->popnum($cgi->param('popnum'));
+
+ #$error ||= $svc_acct->check;
+
+ } elsif ( $cgi->param('username') ) { #good thing to catch
+ $error = "Can't assign username without a package!";
+ }
+
+ use Tie::RefHash;
+ tie my %hash, 'Tie::RefHash';
+ %hash = ( $cust_pkg => [ $svc_acct ] ) if $cust_pkg;
+ $error ||= $new->insert( \%hash, \@invoicing_list );
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('backend-realtime') && ! $error ) {
+
+ my $berror = $new->bill
+ || $new->apply_payments_and_credits
+ || $new->collect( 'realtime' => 1 );
+ warn "Warning, error billing during backend-realtime: $berror" if $berror;
+
+ }
+
+} else { #create old record object
+
+ my $old = qsearchs( 'cust_main', { 'custnum' => $new->custnum } );
+ $error ||= "Old record not found!" unless $old;
+ if ( length($old->paycvv) && $new->paycvv =~ /^\s*\*+\s*$/ ) {
+ $new->paycvv($old->paycvv);
+ }
+ if ($new->ss =~ /xx/) {
+ $new->ss($old->ss);
+ }
+ if ($new->stateid =~ /^xxx/) {
+ $new->stateid($old->stateid);
+ }
+ if ($new->payby =~ /^(CARD|DCRD)$/ && $new->payinfo =~ /xx/) {
+ $new->payinfo($old->payinfo);
+ } elsif ($new->payby =~ /^(CHEK|DCHK)$/ && $new->payinfo =~ /xx/) {
+ #fix for #3085 "edit of customer's routing code only surprisingly causes
+ #nothing to happen...
+ # this probably won't do the right thing when we don't have the
+ # public key (can't actually get the real $old->payinfo)
+ my($new_account, $new_aba) = split('@', $new->payinfo);
+ my($old_account, $old_aba) = split('@', $old->payinfo);
+ $new_account = $old_account if $new_account =~ /xx/;
+ $new_aba = $old_aba if $new_aba =~ /xx/;
+ $new->payinfo($new_account.'@'.$new_aba);
+ }
+
+ warn "$me calling $new -> replace( $old, \ @invoicing_list )" if $DEBUG;
+ local($FS::cust_main::DEBUG) = $DEBUG if $DEBUG;
+ local($FS::Record::DEBUG) = $DEBUG if $DEBUG;
+
+ $error ||= $new->replace($old, \@invoicing_list);
+
+ warn "$me returned from replace" if $DEBUG;
+
+}
+
+</%init>
diff --git a/httemplate/edit/process/cust_main_county-collapse.cgi b/httemplate/edit/process/cust_main_county-collapse.cgi
new file mode 100755
index 0000000..a917825
--- /dev/null
+++ b/httemplate/edit/process/cust_main_county-collapse.cgi
@@ -0,0 +1,44 @@
+%
+%
+%my($query) = $cgi->keywords;
+%$query =~ /^(\d+)$/ or die "Illegal taxnum!";
+%my $taxnum = $1;
+%my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } )
+% or die "Unknown taxnum $taxnum";
+%
+%#really should do this in a .pm & start transaction
+%
+%foreach my $delete ( qsearch('cust_main_county', {
+% 'country' => $cust_main_county->country,
+% 'state' => $cust_main_county->state
+% } ) ) {
+%# unless ( qsearch('cust_main',{
+%# 'state' => $cust_main_county->getfield('state'),
+%# 'county' => $cust_main_county->getfield('county'),
+%# 'country' => $cust_main_county->getfield('country'),
+%# } ) ) {
+% my $error = $delete->delete;
+% die $error if $error;
+%# } else {
+% #should really fix the $cust_main record
+%# }
+%
+%}
+%
+%$cust_main_county->taxnum('');
+%$cust_main_county->county('');
+%my $error = $cust_main_county->insert;
+%die $error if $error;
+%
+%print $cgi->redirect(popurl(3). "browse/cust_main_county.cgi");
+%
+%
+<%init>
+
+#this isn't actually linked from anywhere just now, but it will be again soon
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+
+</%init>
diff --git a/httemplate/edit/process/cust_main_county-expand.cgi b/httemplate/edit/process/cust_main_county-expand.cgi
new file mode 100755
index 0000000..04533a5
--- /dev/null
+++ b/httemplate/edit/process/cust_main_county-expand.cgi
@@ -0,0 +1,78 @@
+<% include('/elements/header-popup.html', 'Addition successful' ) %>
+
+<SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+</SCRIPT>
+
+</BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('taxnum') =~ /^(\d+)$/ or die "Illegal taxnum!";
+my $taxnum = $1;
+my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum})
+ or die ("Unknown taxnum!");
+
+my @expansion;
+if ( $cgi->param('taxclass') ) {
+ my $sth = dbh->prepare('SELECT taxclass FROM part_pkg_taxclass')
+ or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ @expansion = map $_->[0], @{$sth->fetchall_arrayref};
+ die "no taxclasses - add one first" unless @expansion;#XXX better err handling
+} else {
+ @expansion = split /[\n\r]{1,2}/, $cgi->param('expansion');
+
+ #warn scalar(@expansion);
+ #warn "$_: $expansion[$_]\n" foreach (0..$#expansion);
+
+ @expansion=map {
+ unless ( /^\s*([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]+)\s*$/ ) {
+ $cgi->param('error', "Illegal item in expansion: $_");
+ print $cgi->redirect(popurl(2). "cust_main_county-expand.cgi?". $cgi->query_string );
+ myexit();
+ }
+ $1;
+ } @expansion;
+
+}
+
+foreach ( @expansion) {
+ my(%hash)=$cust_main_county->hash;
+ my($new)=new FS::cust_main_county \%hash;
+ $new->setfield('taxnum','');
+ if ( $cgi->param('taxclass') ) {
+ $new->setfield('taxclass', $_);
+ } elsif ( ! $cust_main_county->state ) {
+ $new->setfield('state',$_);
+ } else {
+ $new->setfield('county',$_);
+ }
+ my $error = $new->insert;
+ die $error if $error;
+}
+
+unless ( qsearch( 'cust_main', {
+ 'state' => $cust_main_county->state,
+ 'county' => $cust_main_county->county,
+ 'country' => $cust_main_county->country,
+ } )
+ || ! @expansion
+) {
+ my $error = $cust_main_county->delete;
+ die $error if $error;
+}
+
+if ( $cgi->param('taxclass') ) {
+ print $cgi->redirect(popurl(3). "browse/cust_main_county.cgi?".
+ 'state='. uri_escape($cust_main_county->state ).';'.
+ 'county='. uri_escape($cust_main_county->county ).';'.
+ 'country='. uri_escape($cust_main_county->country)
+ );
+ myexit;
+}
+
+</%init>
diff --git a/httemplate/edit/process/cust_main_county.html b/httemplate/edit/process/cust_main_county.html
new file mode 100644
index 0000000..cb56166
--- /dev/null
+++ b/httemplate/edit/process/cust_main_county.html
@@ -0,0 +1,13 @@
+<% include( 'elements/process.html',
+ 'table' => 'cust_main_county',
+ 'popup_reload' => 'Tax changed', #a popup "parent reload" for now
+ #someday change the individual element and go away instead
+ )
+%>
+<%init>
+
+my $conf = new FS::Conf;
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/cust_main_note.cgi b/httemplate/edit/process/cust_main_note.cgi
new file mode 100755
index 0000000..5127c72
--- /dev/null
+++ b/httemplate/edit/process/cust_main_note.cgi
@@ -0,0 +1,54 @@
+%if ($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). 'cust_main_note.cgi?'. $cgi->query_string ) %>
+%} else {
+<% header('Note ' . ($notenum ? 'updated' : 'added') ) %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY></HTML>
+% }
+<%init>
+
+$cgi->param('custnum') =~ /^(\d+)$/
+ or die "Illegal custnum: ". $cgi->param('custnum');
+my $custnum = $1;
+
+$cgi->param('notenum') =~ /^(\d*)$/
+ or die "Illegal notenum: ". $cgi->param('notenum');
+my $notenum = $1;
+
+my $otaker = $FS::CurrentUser::CurrentUser->name;
+$otaker = $FS::CurrentUser::CurrentUser->username
+ if ($otaker eq "User, Legacy");
+
+my $new = new FS::cust_main_note ( {
+ notenum => $notenum,
+ custnum => $custnum,
+ _date => time,
+ otaker => $otaker,
+ comments => $cgi->param('comment'),
+} );
+
+my $error;
+if ($notenum) {
+
+ die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit customer note');
+
+ my $old = qsearchs('cust_main_note', { 'notenum' => $notenum });
+ $error = "No such note: $notenum" unless $old;
+ unless ($error) {
+ map { $new->$_($old->$_) } ('_date', 'otaker');
+ $error = $new->replace($old);
+ }
+
+} else {
+
+ die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Add customer note');
+
+ $error = $new->insert;
+}
+
+</%init>
diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi
new file mode 100755
index 0000000..647f6fc
--- /dev/null
+++ b/httemplate/edit/process/cust_pay.cgi
@@ -0,0 +1,55 @@
+%if ($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). 'cust_pay.cgi?'. $cgi->query_string ) %>
+%} elsif ( $field eq 'invnum' ) {
+<% $cgi->redirect(popurl(3). "view/cust_bill.cgi?$linknum") %>
+%} elsif ( $field eq 'custnum' ) {
+% if ( $cgi->param('apply') eq 'yes' ) {
+% my $cust_main = qsearchs('cust_main', { 'custnum' => $linknum })
+% or die "unknown custnum $linknum";
+% $cust_main->apply_payments;
+% }
+% if ( $link eq 'popup' ) {
+%
+<% header('Payment entered') %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY></HTML>
+%
+% } elsif ( $link eq 'custnum' ) {
+<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$linknum") %>
+% } else {
+% die "unknown link $link";
+% }
+%
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Post payment');
+
+$cgi->param('linknum') =~ /^(\d+)$/
+ or die "Illegal linknum: ". $cgi->param('linknum');
+my $linknum = $1;
+
+$cgi->param('link') =~ /^(custnum|invnum|popup)$/
+ or die "Illegal link: ". $cgi->param('link');
+my $field = my $link = $1;
+$field = 'custnum' if $field eq 'popup';
+
+my $_date = str2time($cgi->param('_date'));
+
+my $new = new FS::cust_pay ( {
+ $field => $linknum,
+ _date => $_date,
+ map {
+ $_, scalar($cgi->param($_));
+ } qw(paid payby payinfo paybatch)
+ #} fields('cust_pay')
+} );
+
+my $error = $new->insert( 'manual' => 1 );
+
+</%init>
diff --git a/httemplate/edit/process/cust_pay_pending.html b/httemplate/edit/process/cust_pay_pending.html
new file mode 100644
index 0000000..1bad6cf
--- /dev/null
+++ b/httemplate/edit/process/cust_pay_pending.html
@@ -0,0 +1,68 @@
+<% include('/elements/header-popup.html', $title ) %>
+% if ( $error ) {
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT>
+% } else {
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+% }
+</BODY>
+</HTML>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Edit customer pending payments');
+
+$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action';
+my $action = $1;
+
+$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum';
+my $paypendingnum = $1;
+my $cust_pay_pending =
+ qsearchs({
+ 'select' => 'cust_pay_pending.*',
+ 'table' => 'cust_pay_pending',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'paypendingnum' => $paypendingnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+ })
+ or die 'unknown paypendingnum';
+
+my $error;
+my $title;
+if ( $action eq 'delete' ) {
+
+ $error = $cust_pay_pending->delete;
+ if ( $error ) {
+ $title = 'Error deleting pending payment';
+ } else {
+ $title = 'Pending payment deletion sucessful';
+ }
+
+} elsif ( $action eq 'insert_cust_pay' ) {
+
+ $error = $cust_pay_pending->insert_cust_pay;
+ if ( $error ) {
+ $title = 'Error completing pending payment';
+ } else {
+ $title = 'Pending payment completed';
+ }
+
+} elsif ( $action eq 'decline' ) {
+
+ $error = $cust_pay_pending->decline;
+ if ( $error ) {
+ $title = 'Error declining pending payment';
+ } else {
+ $title = 'Pending payment completed (decline)';
+ }
+
+} else {
+
+ die "unknown action $action";
+
+}
+
+</%init>
diff --git a/httemplate/edit/process/cust_pay_refund.cgi b/httemplate/edit/process/cust_pay_refund.cgi
new file mode 100755
index 0000000..2616cad
--- /dev/null
+++ b/httemplate/edit/process/cust_pay_refund.cgi
@@ -0,0 +1,13 @@
+<% include('elements/ApplicationCommon.html',
+ 'error_redirect' => 'cust_pay_refund.cgi',
+ 'src_table' => 'cust_pay',
+ 'src_thing' => 'payment',
+ 'link_table' => 'cust_pay_refund',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Apply payment');
+
+</%init>
diff --git a/httemplate/edit/process/cust_pkg.cgi b/httemplate/edit/process/cust_pkg.cgi
new file mode 100755
index 0000000..c564c41
--- /dev/null
+++ b/httemplate/edit/process/cust_pkg.cgi
@@ -0,0 +1,42 @@
+% if ($error) {
+% $cgi->param('error', $error);
+% $cgi->redirect(popurl(3). 'edit/cust_pkg.cgi?'. $cgi->query_string );
+% } else {
+<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum") %>
+% }
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Bulk change customer packages');
+
+my $error = '';
+
+#untaint custnum
+$cgi->param('custnum') =~ /^(\d+)$/;
+my $custnum = $1;
+
+my @remove_pkgnums = map {
+ /^(\d+)$/ or die "Illegal remove_pkg value!";
+ $1;
+} $cgi->param('remove_pkg');
+
+my( $action, $error_redirect ) = ( '', '' );
+my @pkgparts = ();
+
+foreach my $pkgpart ( map /^pkg(\d+)$/ ? $1 : (), $cgi->param ) {
+ if ( $cgi->param("pkg$pkgpart") =~ /^(\d+)$/ ) {
+ my $num_pkgs = $1;
+ while ( $num_pkgs-- ) {
+ push @pkgparts,$pkgpart;
+ }
+ } else {
+ $error = "Illegal quantity";
+ last;
+ }
+}
+
+$error ||= FS::cust_pkg::order($custnum,\@pkgparts,\@remove_pkgnums);
+
+</%init>
diff --git a/httemplate/edit/process/cust_pkg_detail.html b/httemplate/edit/process/cust_pkg_detail.html
new file mode 100644
index 0000000..132ff63
--- /dev/null
+++ b/httemplate/edit/process/cust_pkg_detail.html
@@ -0,0 +1,59 @@
+% if ( $error ) {
+<% header('Error') %>
+<FONT COLOR="#ff0000"><B><% $error |h %></B></FONT><BR><BR>
+<CENTER><INPUT TYPE="BUTTON" VALUE="OK" onClick="parent.cClick()"></CENTER>
+</BODY></HTML>
+% } else {
+<% header($action) %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY></HTML>
+% }
+<%init>
+
+my %access_right = (
+ 'I' => 'Edit customer package invoice details',
+ 'C' => 'Edit customer package comments',
+);
+
+my %name = (
+ 'I' => 'invoice details',
+ 'C' => 'package comments',
+);
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype';
+my $detailtype = $1;
+
+my $right = $access_right{$detailtype};
+die "access denied"
+ unless $curuser->access_right($right);
+
+$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum';
+my $pkgnum = $1;
+
+my $cust_pkg = qsearchs({
+ 'table' => 'cust_pkg',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'pkgnum' => $pkgnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+
+
+my @orig_details = $cust_pkg->cust_pkg_detail($detailtype);
+
+my $action = ucfirst($name{$detailtype}).
+ ( scalar(@orig_details) ? ' changed ' : ' added ' );
+
+my $param = $cgi->Vars;
+my @details = ();
+for ( my $row = 0; exists($param->{"detail$row"}); $row++ ) {
+ push @details, $param->{"detail$row"}
+ if $param->{"detail$row"} =~ /\S/;
+}
+
+my $error = $cust_pkg->set_cust_pkg_detail($detailtype, @details);
+
+</%init>
diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi
new file mode 100755
index 0000000..5749e53
--- /dev/null
+++ b/httemplate/edit/process/cust_refund.cgi
@@ -0,0 +1,56 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "cust_refund.cgi?". $cgi->query_string ) %>
+%} else {
+%
+% if ( $link eq 'popup' ) {
+%
+<% header('Refund entered') %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY></HTML>
+% } else {
+<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum") %>
+% }
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Refund payment')
+ || $FS::CurrentUser::CurrentUser->access_right('Post refund');
+
+$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!";
+my $custnum = $1;
+my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or die "unknown custnum $custnum";
+
+my $link = $cgi->param('popup') ? 'popup' : '';
+
+my $error = '';
+if ( $cgi->param('payby') =~ /^(CARD|CHEK)$/ ) {
+ my %options = ();
+ my $bop = $FS::payby::payby2bop{$1};
+ $cgi->param('refund') =~ /^(\d*)(\.\d{2})?$/
+ or die "illegal refund amount ". $cgi->param('refund');
+ my $refund = "$1$2";
+ $cgi->param('paynum') =~ /^(\d*)$/ or die "Illegal paynum!";
+ my $paynum = $1;
+ my $reason = $cgi->param('reason');
+ my $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01';
+ $options{'paydate'} = $paydate if $paydate =~ /^\d{2,4}-\d{1,2}-01$/;
+ $error = $cust_main->realtime_refund_bop( $bop, 'amount' => $refund,
+ 'paynum' => $paynum,
+ 'reason' => $reason,
+ %options );
+} else {
+ my $new = new FS::cust_refund ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('cust_refund') #huh? , 'paynum' )
+ } );
+ $error = $new->insert;
+}
+
+</%init>
diff --git a/httemplate/edit/process/cust_svc.cgi b/httemplate/edit/process/cust_svc.cgi
new file mode 100644
index 0000000..e22cbb2
--- /dev/null
+++ b/httemplate/edit/process/cust_svc.cgi
@@ -0,0 +1,30 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+% my $svcdb = $new->part_svc->svcdb;
+<% $cgi->redirect(popurl(3). "view/$svcdb.cgi?$svcnum") %>
+%}
+<%init>
+
+die 'access deined'
+ unless $FS::CurrentUser::CurrentUser->access_right('Change customer service');
+
+my $svcnum = $cgi->param('svcnum');
+
+my $old = qsearchs('cust_svc',{'svcnum'=>$svcnum}) if $svcnum;
+
+my $new = new FS::cust_svc ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('cust_svc')
+} );
+
+my $error;
+if ( $svcnum ) {
+ $error=$new->replace($old);
+} else {
+ $error=$new->insert;
+ $svcnum=$new->getfield('svcnum');
+}
+
+</%init>
diff --git a/httemplate/edit/process/domain_record.cgi b/httemplate/edit/process/domain_record.cgi
new file mode 100755
index 0000000..2e427e4
--- /dev/null
+++ b/httemplate/edit/process/domain_record.cgi
@@ -0,0 +1,30 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+% my $svcnum = $new->svcnum;
+<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice');
+
+my $recnum = $cgi->param('recnum');
+
+my $old = qsearchs('agent',{'recnum'=>$recnum}) if $recnum;
+
+my $new = new FS::domain_record ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('domain_record')
+} );
+
+my $error;
+if ( $recnum ) {
+ $error=$new->replace($old);
+} else {
+ $error=$new->insert;
+ $recnum=$new->getfield('recnum');
+}
+
+</%init>
diff --git a/httemplate/edit/process/elements/ApplicationCommon.html b/httemplate/edit/process/elements/ApplicationCommon.html
new file mode 100644
index 0000000..2782dc2
--- /dev/null
+++ b/httemplate/edit/process/elements/ApplicationCommon.html
@@ -0,0 +1,77 @@
+<%doc>
+
+Examples:
+
+ #cust_bill_pay
+ include('elements/ApplicationCommon.html',
+ 'error_redirect' => 'cust_bill_pay.cgi',
+ 'src_table' => 'cust_pay',
+ 'src_thing' => 'payment',
+ 'link_table' => 'cust_bill_pay',
+ )
+
+ #cust_credit_bill
+ include('elements/ApplicationCommon.html',
+ 'error_redirect' => 'cust_credit_bill.cgi',
+ 'src_table' => 'cust_credit',
+ 'src_thing' => 'credit',
+ 'link_table' => 'cust_credit_bill',
+ )
+
+</%doc>
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). $opt{error_redirect}. '?'. $cgi->query_string ) %>
+%} else {
+<% header("$src_thing application$to sucessful") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+ </HTML>
+% }
+<%init>
+
+my %opt = @_;
+
+my $src_thing = ucfirst($opt{'src_thing'});
+my $src_table = $opt{'src_table'};
+my $src_pkey = dbdef->table($src_table)->primary_key;
+
+my $to = $opt{'link_table'} =~ /refund/ ? ' to Refund' : '';
+
+$cgi->param($src_pkey) =~ /^(\d+)$/ or die "Illegal $src_pkey!";
+my $src_pkeyvalue = $1;
+
+my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } )
+ or die "No such $src_pkey: $src_pkeyvalue";
+
+my $cust_main = qsearchs('cust_main', { 'custnum' => $src->custnum } )
+ or die "Bogus $src_thing: not attached to customer";
+
+my $custnum = $cust_main->custnum;
+
+my $new;
+# $new = new FS::cust_refund ( {
+# 'reason' => 'Refunding payment', #enter reason in UI
+# 'refund' => $cgi->param('amount'),
+# 'payby' => 'BILL',
+# #'_date' => $cgi->param('_date'),
+# 'payinfo' => 'Cash', #enter payinfo in UI
+# 'paynum' => $paynum,
+# } );
+#} else {
+
+ my $class = 'FS::'. $opt{link_table};
+
+ $new = $class->new( {
+ map {
+ $_ => scalar($cgi->param($_));
+ } fields($opt{link_table})
+ } );
+
+#}
+
+my $error = $new->insert;
+
+</%init>
diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html
new file mode 100644
index 0000000..5befdd3
--- /dev/null
+++ b/httemplate/edit/process/elements/process.html
@@ -0,0 +1,268 @@
+<%doc>
+
+Example:
+
+ include( 'elements/process.html',
+
+ ###
+ # required
+ ###
+
+ 'table' => 'tablename',
+
+ #? 'primary_key' => #required when the dbdef doesn't know...???
+ #? 'fields' => [] #""
+
+ ###
+ # optional
+ ###
+
+ 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
+ 'viewall_ext' => 'html', #'cgi' or 'html', defaults to 'html'
+ OR
+ 'redirect' => 'view/table.cgi?', # value of primary key is appended
+ # (string or coderef returning a string)
+ OR
+ 'popup_reload' => 'Momentary success message', #will reload parent window
+
+ 'error_redirect' => popurl(2).'edit/table.cgi?', #query string appended
+
+ 'edit_ext' => 'html', #defaults to 'html', you might want 'cgi' while the
+ #naming is still inconsistent
+
+ 'copy_on_empty' => [ 'old_field_name', 'another_old_field', ... ],
+
+ 'clear_on_error' => [ 'form_field1', 'form_field2', ... ],
+
+ #pass an arrayref of hashrefs for multiple m2ms or m2names
+ #be certain you incorporate m2m_Common if you see error: param
+
+ 'process_m2m' => { 'link_table' => 'link_table_name',
+ 'target_table' => 'target_table_name',
+ #optional (see m2m_Common::process_m2m), if not specified
+ # all CGI params will be passed)
+ 'params' =>
+ },
+ 'process_m2name' => { 'link_table' => 'link_table_name',
+ 'link_static' => { 'column' => 'value' },
+ 'num_col' => 'column', #if column name is different in
+ #link_table than source_table
+ 'name_col' => 'name_column',
+ 'names_list' => [ 'list', 'names' ],
+
+ 'param_style' => 'link_table.value checkboxes',
+ #or#
+ 'param_style' => 'name_colN values',
+
+
+ },
+
+ #checks CGI params and whatever else before much else runs
+ #return an error string or empty for no error
+ 'precheck_callback' => sub { my( $cgi ) = @_; },
+
+ #supplies arguments to insert() and replace()
+ # for use with tables that are FS::option_Common
+ 'args_callback' => sub { my( $cgi, $object ) = @_; },
+
+ 'debug' => 1, #turns on debugging output
+
+ #agent virtualization
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Access Right Name',
+
+ )
+
+</%doc>
+%if ( $error ) {
+%
+% my $edit_ext = $opt{'edit_ext'} || 'html';
+% my $url = $opt{'error_redirect'} || popurl(2)."$table.$edit_ext";
+% if ( length($cgi->query_string) > 1920 ) { #stupid IE 2083 URL limit
+%
+% my $session = int(rand(4294967296)); #XXX
+% my $pref = new FS::access_user_pref({
+% 'usernum' => $FS::CurrentUser::CurrentUser->usernum,
+% 'prefname' => "redirect$session",
+% 'prefvalue' => $cgi->query_string,
+% 'expiration' => time + 3600, #1h? 1m?
+% });
+% my $pref_error = $pref->insert;
+% if ( $pref_error ) {
+% die "FATAL: couldn't even set redirect cookie: $pref_error".
+% " attempting to set redirect$session to ". $cgi->query_string."\n";
+% }
+%
+<% $cgi->redirect("$url?redirect=$session") %>
+%
+% } else {
+%
+<% $cgi->redirect("$url?". $cgi->query_string ) %>
+%
+% }
+%
+% #different ways of handling success
+%
+%} elsif ( $opt{'popup_reload'} ) {
+
+ <% include('/elements/header-popup.html', $opt{'popup_reload'} ) %>
+
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY>
+ </HTML>
+
+%} else {
+%
+% $opt{'redirect'} = &{$opt{'redirect'}}($cgi, $new)
+% if ref($opt{'redirect'}) eq 'CODE';
+%
+% if ( $opt{'redirect'} ) {
+%
+<% $cgi->redirect( $opt{'redirect'}. $pkeyvalue ) %>
+%
+% } else {
+%
+% my $ext = $opt{'viewall_ext'} || 'html';
+%
+<% $cgi->redirect( popurl(3). ($opt{viewall_dir}||'search'). "/$table.$ext" ) %>
+%
+% }
+%
+%}
+%
+<%init>
+
+my $me = 'process.html:';
+
+my(%opt) = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $error = '';
+if ( $opt{'precheck_callback'} ) {
+ $error = &{ $opt{'precheck_callback'} }( $cgi );
+}
+
+#false laziness w/edit.html
+my $table = $opt{'table'};
+my $class = "FS::$table";
+my $pkey = dbdef->table($table)->primary_key; #? $opt{'primary_key'} ||
+my $fields = $opt{'fields'}
+ #|| [ grep { $_ ne $pkey } dbdef->table($table)->columns ];
+ || [ fields($table) ];
+
+my $pkeyvalue = $cgi->param($pkey);
+
+my $old = '';
+if ( $pkeyvalue ) {
+ $old = qsearchs({
+ 'table' => $table,
+ 'hashref' => { $pkey => $pkeyvalue },
+ 'extra_sql' => ( $opt{'agent_virt'}
+ ? ' AND '. $curuser->agentnums_sql(
+ 'null_right' => $opt{'agent_null_right'}
+ )
+ : ''
+ ),
+ });
+}
+
+my %hash =
+ map { my @entry = ( $_ => scalar($cgi->param($_)) );
+ $opt{'value_callback'} ? ( $_ => &{ $opt{'value_callback'} }( @entry ))
+ : ( @entry )
+ } @$fields;
+
+my $new = $class->new( \%hash );
+
+if ($old && exists($opt{'copy_on_empty'})) {
+ foreach my $field (@{$opt{'copy_on_empty'}}) {
+ $new->set($field, $old->get($field))
+ unless scalar($cgi->param($field));
+ }
+}
+
+if ( $opt{'agent_virt'} ) {
+ die "illegal agentnum"
+ unless $curuser->agentnums_href->{$new->agentnum}
+ or $opt{'agent_null_right'}
+ && ! $new->agentnum
+ && $curuser->access_right($opt{'agent_null_right'});
+}
+
+$error ||= $new->check;
+
+my @args = ();
+if ( !$error && $opt{'args_callback'} ) {
+ @args = &{ $opt{'args_callback'} }( $cgi, $new );
+}
+
+if ( !$error && $opt{'debug'} ) {
+ warn "$me updating record in $table table using $class class\n";
+ warn Dumper(\%hash);
+ warn "with args: \n". Dumper(\@args) if @args;
+}
+
+if ( !$error ) {
+ if ( $pkeyvalue ) {
+ $error = $new->replace($old, @args);
+ } else {
+ $error = $new->insert(@args);
+ $pkeyvalue = $new->getfield($pkey);
+ }
+}
+
+if ( !$error && $opt{'process_m2m'} ) {
+
+ my @process_m2m = ref($opt{'process_m2m'}) eq 'ARRAY'
+ ? @{ $opt{'process_m2m'} }
+ : ( $opt{'process_m2m'} );
+
+ foreach my $process_m2m (@process_m2m) {
+
+ $process_m2m->{'params'} ||= scalar($cgi->Vars);
+
+ warn "$me processing m2m:\n". Dumper( %$process_m2m )
+ if $opt{'debug'};
+
+ $error = $new->process_m2m( %$process_m2m );
+ }
+
+}
+
+if ( !$error && $opt{'process_m2name'} ) {
+
+ my @process_m2name = ref($opt{'process_m2name'}) eq 'ARRAY'
+ ? @{ $opt{'process_m2name'} }
+ : ( $opt{'process_m2name'} );
+
+
+ foreach my $process_m2name (@process_m2name) {
+
+ if ( $opt{'debug'} ) {
+ warn "$me processing m2name:\n". Dumper( %{ $process_m2name },
+ 'params' => scalar($cgi->Vars),
+ );
+ }
+
+ $error = $new->process_m2name( %{ $process_m2name },
+ 'params' => scalar($cgi->Vars),
+ );
+ }
+
+}
+
+
+if ( $error ) {
+ $cgi->param('error', $error);
+ if ( $opt{'clear_on_error'} && scalar(@{$opt{'clear_on_error'}}) ) {
+ foreach my $field (@{$opt{'clear_on_error'}}) {
+ $cgi->param($field, '')
+ }
+ }
+}
+
+</%init>
diff --git a/httemplate/edit/process/elements/svc_Common.html b/httemplate/edit/process/elements/svc_Common.html
new file mode 100644
index 0000000..8e8c99a
--- /dev/null
+++ b/httemplate/edit/process/elements/svc_Common.html
@@ -0,0 +1,15 @@
+%
+%
+% my %opt = @_;
+% my $table = $opt{'table'};
+% $opt{'fields'} ||= [ fields($table) ];
+% push @{ $opt{'fields'} }, qw( pkgnum svcpart );
+%
+%
+<% include( 'process.html',
+ 'edit_ext' => 'cgi',
+ 'redirect' => popurl(3)."view/$table.cgi?",
+ %opt,
+ )
+%>
+
diff --git a/httemplate/edit/process/generic.cgi b/httemplate/edit/process/generic.cgi
new file mode 100644
index 0000000..6428763
--- /dev/null
+++ b/httemplate/edit/process/generic.cgi
@@ -0,0 +1,77 @@
+%if($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect($redirect_error . '?' . $cgi->query_string) %>
+%} else {
+<% $cgi->redirect($redirect_ok) %>
+%}
+<%doc>
+
+See elements/process.html, newer and somewhat along the same lines,
+though it still makes you setup a process file for the table.
+Perhaps safer, perhaps more of a pain in the ass.
+
+In any case, this is probably pretty deprecated; it is only used by
+part_virtual_field.cgi, and so its ACL is hardcoded to 'Configuration'.
+
+Welcome to generic.cgi.
+
+This script provides a generic edit/process/ backend for simple table
+editing. All it knows how to do is take the values entered into
+the script and insert them into the table specified by $cgi->param('table').
+If there's an existing record with the same primary key, it will be
+replaced. (Deletion will be added in the future.)
+
+Special cgi params for this script:
+table: the name of the table to be edited. The script will die horribly
+ if it can't find the table.
+redirect_ok: URL to be displayed after a successful edit. The value of
+ the record's primary key will be passed as a keyword.
+ Defaults to (freeside root)/view/$table.cgi.
+redirect_error: URL to be displayed if there's an error. The original
+ query string, plus the error message, will be passed.
+ Defaults to $cgi->referer() (i.e. go back where you
+ came from).
+
+</%doc>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $error;
+my $p2 = popurl(2);
+my $p3 = popurl(3);
+my $table = $cgi->param('table');
+my $dbdef = dbdef or die "Cannot fetch dbdef!";
+
+my $dbdef_table = $dbdef->table($table) or die "Cannot fetch schema for $table";
+
+my $pkey = $dbdef_table->primary_key or die "Cannot fetch pkey for $table";
+my $pkey_val = $cgi->param($pkey);
+
+
+#warn "new FS::Record ( $table, (hashref) )";
+my $new = FS::Record::new ( "FS::$table", {
+ map { $_, scalar($cgi->param($_)) } fields($table)
+} );
+
+#warn 'created $new of class '.ref($new);
+
+if($pkey_val and (my $old = qsearchs($table, { $pkey, $pkey_val} ))) {
+ # edit
+ $error = $new->replace($old);
+} else {
+ #add
+ $error = $new->insert;
+ $pkey_val = $new->getfield($pkey);
+ # New records usually don't have their primary keys set until after
+ # they've been checked/inserted, so grab the new $pkey_val so we can
+ # redirect to it.
+}
+
+my $redirect_ok = (($cgi->param('redirect_ok')) ?
+ $cgi->param('redirect_ok') : $p3."browse/generic.cgi?$table");
+my $redirect_error = (($cgi->param('redirect_error')) ?
+ $cgi->param('redirect_error') : $cgi->referer());
+
+</%init>
diff --git a/httemplate/edit/process/inventory_class.html b/httemplate/edit/process/inventory_class.html
new file mode 100644
index 0000000..dbf978e
--- /dev/null
+++ b/httemplate/edit/process/inventory_class.html
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+ 'table' => 'inventory_class',
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/invoice_logo.html b/httemplate/edit/process/invoice_logo.html
new file mode 100644
index 0000000..524d325
--- /dev/null
+++ b/httemplate/edit/process/invoice_logo.html
@@ -0,0 +1,25 @@
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+$cgi->param('type') =~ /^(png|eps)$/ or die "illegal type";
+my $type = $1;
+
+$cgi->param('name') =~ /^([^\.\/]*)$/ or die "illegal name";
+my $tname = my $name = $1;
+$tname = "_$tname" if length($tname);
+
+$cgi->param('preview_session') =~ /^(\w*)$/ or die "illegal preview_session";
+my $session = $1;
+my $data = decode_base64( $curuser->option("logo_preview$session") );
+
+$conf->set_binary("logo$name.$type", $data);
+
+$cgi->redirect(popurl(3). "edit/invoice_logo.html?type=$type;name=$name;msg=Logo%20changed");
+
+</%init>
diff --git a/httemplate/edit/process/invoice_template.html b/httemplate/edit/process/invoice_template.html
new file mode 100644
index 0000000..6c9371a
--- /dev/null
+++ b/httemplate/edit/process/invoice_template.html
@@ -0,0 +1,15 @@
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+my $confname = $cgi->param('confname');
+my $value = $cgi->param('value');
+
+$conf->set($confname, $value);
+
+$cgi->redirect(popurl(3). 'browse/invoice_template.html');
+
+</%init>
diff --git a/httemplate/edit/process/msgcat.cgi b/httemplate/edit/process/msgcat.cgi
new file mode 100644
index 0000000..7175fa2
--- /dev/null
+++ b/httemplate/edit/process/msgcat.cgi
@@ -0,0 +1,22 @@
+%if ( $error ) {
+% $cgi->param('error',$error);
+<% $cgi->redirect($p. "msgcat.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/msgcat.cgi") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $error;
+foreach my $param ( grep { /^\d+$/ } $cgi->param ) {
+ my $old = qsearchs('msgcat', { msgnum=>$param } );
+ next if $old->msg eq $cgi->param($param); #no need to update identical records
+ my $new = new FS::msgcat { $old->hash };
+ $new->msg($cgi->param($param));
+ $error = $new->replace($old);
+ last if $error;
+}
+
+</%init>
diff --git a/httemplate/edit/process/part_bill_event.cgi b/httemplate/edit/process/part_bill_event.cgi
new file mode 100755
index 0000000..eb0529b
--- /dev/null
+++ b/httemplate/edit/process/part_bill_event.cgi
@@ -0,0 +1,106 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "part_bill_event.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3)."browse/part_bill_event.cgi") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $eventpart = $cgi->param('eventpart');
+
+my $old = qsearchs('part_bill_event',{'eventpart'=>$eventpart}) if $eventpart;
+
+#s/days/seconds/
+$cgi->param('seconds', int( $cgi->param('days') * 86400 ) );
+
+my $error;
+if ( ! $cgi->param('plan_weight_eventcode') ) {
+ $error = "Must select an action";
+} else {
+
+ $cgi->param('plan_weight_eventcode') =~ /^([\w\-]+):(\d+):(.*)$/s
+ or die "illegal plan_weight_eventcode:".
+ $cgi->param('plan_weight_eventcode');
+ $cgi->param('plan', $1);
+ $cgi->param('weight', $2);
+ my $eventcode = $3;
+ my $plandata = '';
+
+ my $rnum;
+ my $rtype;
+ my $reasonm;
+ my $class = '';
+ $class='c' if ($eventcode =~ /cancel/);
+ $class='s' if ($eventcode =~ /suspend/);
+ if ($class) {
+ $cgi->param("${class}reason") =~ /^(-?\d+)$/
+ or $error = "Invalid ${class}reason";
+ $rnum = $1;
+ if ($rnum == -1) {
+ $cgi->param("new${class}reasonT") =~ /^(\d+)$/
+ or $error = "Invalid new${class}reasonT";
+ $rtype = $1;
+ $cgi->param("new${class}reason") =~ /^([\s\w]+)$/
+ or $error = "Invalid new${class}reason";
+ $reasonm = $1;
+ }
+ }
+
+ if ($rnum == -1 && !$error) {
+ my $reason = new FS::reason ({ 'reason' => $reasonm,
+ 'reason_type' => $rtype,
+ });
+ $error = $reason->insert;
+ unless ($error) {
+ $rnum = $reason->reasonnum;
+ $cgi->param("${class}reason", $rnum);
+ $cgi->param("new${class}reason", '');
+ $cgi->param("new${class}reasonT", '');
+ }
+ }
+
+ while ( $eventcode =~ /%%%(\w+)%%%/ ) {
+ my $field = $1;
+ my $value = join(', ', $cgi->param($field) );
+ $cgi->param($field, $value); #in case it errors out
+ $eventcode =~ s/%%%$field%%%/$value/;
+ $plandata .= "$field $value\n";
+ }
+ $cgi->param('eventcode', $eventcode);
+ $cgi->param('plandata', $plandata);
+
+ unless($error) {
+
+ if ( $eventpart ) {
+
+ my $new = new FS::part_bill_event ( {
+ map { $_ => scalar($cgi->param($_)) }
+ fields('part_bill_event'),
+ } );
+ $new->setfield('reason' => $rnum);
+ $error = $new->replace($old);
+
+ } else {
+
+ foreach my $payby ( $cgi->param('payby') ) {
+ my $new = new FS::part_bill_event ( {
+ map { $_ => scalar($cgi->param($_)) }
+ grep { $_ ne 'payby' }
+ fields('part_bill_event')
+ } );
+ $new->setfield('payby' => $payby);
+ $new->setfield('reason' => $rnum );
+ $error = $new->insert;
+ last if $error;
+ }
+
+ }
+
+ }
+
+}
+
+</%init>
diff --git a/httemplate/edit/process/part_event.html b/httemplate/edit/process/part_event.html
new file mode 100644
index 0000000..428025f
--- /dev/null
+++ b/httemplate/edit/process/part_event.html
@@ -0,0 +1,86 @@
+<% include( 'elements/process.html',
+ #'debug' => 1,
+ 'table' => 'part_event',
+ 'viewall_dir' => 'browse',
+ 'process_m2name' =>
+ {
+ 'link_table' => 'part_event_condition',
+ 'num_col' => 'eventpart',
+ 'name_col' => 'conditionname',
+ 'names_list' => [ FS::part_event_condition->all_conditionnames() ],
+ 'param_style' => 'name_colN values',
+ 'args_callback' => sub { # FS/FS/m2name_Common.pm
+ my( $object, $prefix, $params, $listref ) = @_;
+ #warn "$object $prefix $params $listref\n";
+
+ my $cond = $object->conditionname;
+
+ my %option_fields = $object->option_fields;
+
+ push @$listref, map {
+ my $field = $_;
+
+ my $cgi_field = "$prefix$cond.$field";
+
+ my $value = $params->{$cgi_field};
+
+ my $info = $option_fields{$_};
+ $info = { label=>$info, type=>'text' }
+ unless ref($info);
+
+ if ( $info->{'type'} =~
+ /^(select|checkbox)-?multiple$/
+ or $info->{'type'} =~ /^select/
+ && $info->{'multiple'}
+ )
+ {
+ #special processing for compound fields
+ $value = { map { $_ => 1 }
+ split(/\0/, $value)
+ };
+ } elsif ( $info->{'type'} eq 'freq' ) {
+ $value .= $params->{$cgi_field.'_units'};
+ }
+
+ #warn "value of $cgi_field is $value\n";
+
+ ( $field => $value );
+ }
+ keys %option_fields;
+ },
+ },
+
+ 'args_callback' => sub {
+
+ my( $cgi, $object ) = @_;
+
+ my $prefix = $object->action.'.';
+
+ map { my $option = $_;
+ #my $value = scalar( $cgi->param( "$prefix$option" ) );
+ my $value = join(',', $cgi->param( "$prefix$option" ) );
+
+ if ( $option eq 'reasonnum' && $value == -1 ) {
+ $value = {
+ 'typenum' => scalar( $cgi->param( "new$prefix${option}T" ) ),
+ 'reason' => scalar( $cgi->param( "new$prefix${option}" ) ),
+ };
+ }
+
+ ( $option => $value );
+ }
+ @{ $object->option_fields_listref };
+
+ },
+
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Edit global billing events',
+)
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit billing events')
+ || $FS::CurrentUser::CurrentUser->access_right('Edit global billing events');
+
+</%init>
diff --git a/httemplate/edit/process/part_export.cgi b/httemplate/edit/process/part_export.cgi
new file mode 100644
index 0000000..b5f82e8
--- /dev/null
+++ b/httemplate/edit/process/part_export.cgi
@@ -0,0 +1,41 @@
+%if ( $error ) {
+% $cgi->param('error', $error );
+<% $cgi->redirect(popurl(2). "part_export.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/part_export.cgi") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $exportnum = $cgi->param('exportnum');
+
+my $old = qsearchs('part_export', { 'exportnum'=>$exportnum } ) if $exportnum;
+
+#fixup options
+#warn join('-', split(',',$cgi->param('options')));
+my %options = map {
+ my $value = $cgi->param($_);
+ $value =~ s/\r\n/\n/g; #browsers? (textarea)
+ $_ => $value;
+} split(',', $cgi->param('options'));
+
+my $new = new FS::part_export ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('part_export')
+} );
+
+my $error;
+if ( $exportnum ) {
+ #warn $old;
+ #warn $exportnum;
+ #warn $new->machine;
+ $error = $new->replace($old,\%options);
+} else {
+ $error = $new->insert(\%options);
+# $exportnum = $new->exportnum;
+}
+
+</%init>
diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi
new file mode 100755
index 0000000..96c5b36
--- /dev/null
+++ b/httemplate/edit/process/part_pkg.cgi
@@ -0,0 +1,198 @@
+<% include( 'elements/process.html',
+ #'debug' => 1,
+ 'table' => 'part_pkg',
+ 'agent_virt' => 1,
+ 'agent_null_right' => \@agent_null_right,
+ 'redirect' => $redirect_callback,
+ 'viewall_dir' => 'browse',
+ 'viewall_ext' => 'cgi',
+ 'edit_ext' => 'cgi',
+ 'precheck_callback' => $precheck_callback,
+ 'args_callback' => $args_callback,
+ 'process_m2m' => \@process_m2m,
+ )
+%>
+<%init>
+
+my $customizing = ( ! $cgi->param('pkgpart') && $cgi->param('pkgnum') );
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $edit_global = 'Edit global package definitions';
+my $customize = 'Customize customer package';
+
+die "access denied"
+ unless $curuser->access_right('Edit package definitions')
+ || $curuser->access_right($edit_global)
+ || ( $customizing && $curuser->access_right($customize) );
+
+my @agent_null_right = ( $edit_global );
+push @agent_null_right, $customize if $customizing;
+
+
+my $precheck_callback = sub {
+ my( $cgi ) = @_;
+
+ my $conf = new FS::Conf;
+
+ foreach (qw( setuptax recurtax disabled )) {
+ $cgi->param($_, '') unless defined $cgi->param($_);
+ }
+
+ return 'Must select a tax class'
+ if $cgi->param('taxclass') eq '(select)';
+
+ my @agents = ();
+ foreach ($cgi->param('agent_type')) {
+ /^(\d+)$/;
+ push @agents, $1 if $1;
+ }
+ return "At least one agent type must be specified."
+ unless scalar(@agents)
+ || ( $cgi->param('clone') && $cgi->param('clone') =~ /^\d+$/ )
+ || ( !$cgi->param('pkgpart') && $conf->exists('agent-defaultpkg') )
+ || $cgi->param('disabled')
+ || $cgi->param('agentnum');
+
+ return '';
+
+};
+
+my $custnum = '';
+
+my $args_callback = sub {
+ my( $cgi, $new ) = @_;
+
+ my @args = ( 'primary_svc' => scalar($cgi->param('pkg_svc_primary')) );
+
+ ##
+ #options
+ ##
+
+ $cgi->param('plan') =~ /^(\w+)$/ or die 'unparsable plan';
+ my $plan = $1;
+
+ tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() };
+ my $href = $plans{$plan}->{'fields'};
+
+ my $error = '';
+ my $options = $cgi->param($plan."__OPTIONS");
+ my @options = split(',', $options);
+ my %options =
+ map { my $optionname = $_;
+ my $param = $plan."__$optionname";
+ my $parser = exists($href->{$optionname}{parse})
+ ? $href->{$optionname}{parse}
+ : sub { shift };
+ my $value = join(', ', &$parser($cgi->param($param)));
+ my $check = $href->{$optionname}{check};
+ if ( $check && ! &$check($value) ) {
+ $value = join(', ', $cgi->param($param));
+ $error ||= "Illegal ".
+ ($href->{$optionname}{name}||$optionname). ": $value";
+ }
+ ( $optionname => $value );
+ }
+ @options;
+
+ foreach ( split(',', $cgi->param('taxproductnums') ) ) {
+ my $value = $cgi->param("taxproductnum_$_");
+ $error ||= "Illegal taxproductnum_$_: $value"
+ unless ( $value =~ /^\d*$/ );
+ $options{"usage_taxproductnum_$_"} = $value;
+ }
+
+ $options{$_} = scalar( $cgi->param($_) )
+ for (qw( setup_fee recur_fee ));
+
+ push @args, 'options' => \%options;
+
+ ###
+ #pkg_svc
+ ###
+
+ my %pkg_svc = map { $_ => scalar($cgi->param("pkg_svc$_")) }
+ map { $_->svcpart }
+ qsearch('part_svc', {} );
+
+ push @args, 'pkg_svc' => \%pkg_svc;
+
+ ###
+ # cust_pkg and custnum_ref (inserts only)
+ ###
+ unless ( $cgi->param('pkgpart') ) {
+ push @args, 'cust_pkg' => scalar($cgi->param('pkgnum')),
+ 'custnum_ref' => \$custnum;
+ }
+
+ warn "args: ".join('/', @args). "\n";
+
+ @args;
+
+};
+
+my $redirect_callback = sub {
+ #my( $cgi, $new ) = @_;
+ return '' unless $custnum;
+ popurl(3). "view/cust_main.cgi?keywords=$custnum;dummy=";
+};
+
+#these should probably move to @args above and be processed by part_pkg.pm...
+
+$cgi->param('tax_override') =~ /^([\d,]+)$/;
+my (@tax_overrides) = (grep "$_", split (",", $1));
+
+my @process_m2m = (
+ {
+ 'link_table' => 'part_pkg_taxoverride',
+ 'target_table' => 'tax_class',
+ 'params' => \@tax_overrides,
+ },
+ { 'link_table' => 'part_pkg_link',
+ 'target_table' => 'part_pkg',
+ 'base_field' => 'src_pkgpart',
+ 'target_field' => 'dst_pkgpart',
+ 'hashref' => { 'link_type' => 'bill' },
+ 'params' => [ map $cgi->param($_), grep /^bill_dst_pkgpart/, $cgi->param ],
+ },
+ { 'link_table' => 'part_pkg_link',
+ 'target_table' => 'part_pkg',
+ 'base_field' => 'src_pkgpart',
+ 'target_field' => 'dst_pkgpart',
+ 'hashref' => { 'link_type' => 'svc' },
+ 'params' => [ map $cgi->param($_), grep /^svc_dst_pkgpart/, $cgi->param ],
+ },
+);
+
+foreach my $override_class ($cgi->param) {
+ next unless $override_class =~ /^tax_override_(\w+)$/;
+ my $class = $1;
+
+ my (@tax_overrides) = (grep "$_", split (",", $1))
+ if $cgi->param($override_class) =~ /^([\d,]+)$/;
+
+ push @process_m2m, {
+ 'link_table' => 'part_pkg_taxoverride',
+ 'target_table' => 'tax_class',
+ 'hashref' => { 'usage_class' => $class },
+ 'params' => [ @tax_overrides ],
+ };
+
+}
+
+my $conf = new FS::Conf;
+
+if ( $cgi->param('pkgpart') || ! $conf->exists('agent_defaultpkg') ) {
+ my @agents = ();
+ foreach ($cgi->param('agent_type')) {
+ /^(\d+)$/;
+ push @agents, $1 if $1;
+ }
+ push @process_m2m, {
+ 'link_table' => 'type_pkgs',
+ 'target_table' => 'agent_type',
+ 'params' => \@agents,
+ };
+}
+
+</%init>
diff --git a/httemplate/edit/process/part_pkg_taxclass.html b/httemplate/edit/process/part_pkg_taxclass.html
new file mode 100644
index 0000000..8f149bb
--- /dev/null
+++ b/httemplate/edit/process/part_pkg_taxclass.html
@@ -0,0 +1,53 @@
+% if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "part_pkg_taxclass.html?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/cust_main_county.cgi?taxclass=". uri_escape($part_pkg_taxclass->taxclass) ) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $part_pkg_taxclass = new FS::part_pkg_taxclass {
+ 'taxclass' => $cgi->param('taxclass'),
+};
+
+#maybe this whole thing should be in a transaction. at some point, no biggie
+#none of the follow-up stuff will fail unless there's a more serious problem
+#than a hanging record in part_pkg_taxclass...
+
+my $error = $part_pkg_taxclass->insert;
+
+unless ( $error ) {
+ #auto-add the new taxclass to any regions that have taxclasses already
+
+ my $sth = dbh->prepare("
+ SELECT country, state, county FROM cust_main_county
+ WHERE taxclass IS NOT NULL AND taxclass != ''
+ GROUP BY country, state, county
+ ") or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+
+ while ( my $row = $sth->fetchrow_hashref ) {
+ warn "inserting for $row";
+ my $cust_main_county = new FS::cust_main_county {
+ 'country' => $row->{country},
+ 'state' => $row->{state},
+ 'county' => $row->{county},
+ 'tax' => 0,
+ 'taxclass' => $part_pkg_taxclass->taxclass,
+ #exempt_amount
+ #taxname
+ #setuptax
+ #recurtax
+ };
+ $error = $cust_main_county->insert;
+ #last if $error;
+ die $error if $error;
+ }
+
+
+}
+
+</%init>
diff --git a/httemplate/edit/process/part_referral.html b/httemplate/edit/process/part_referral.html
new file mode 100755
index 0000000..40cbc97
--- /dev/null
+++ b/httemplate/edit/process/part_referral.html
@@ -0,0 +1,12 @@
+<% include( 'elements/process.html',
+ 'table' => 'part_referral',
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit advertising sources')
+ || $FS::CurrentUser::CurrentUser->access_right('Edit global advertising sources');
+
+</%init>
diff --git a/httemplate/edit/process/part_svc.cgi b/httemplate/edit/process/part_svc.cgi
new file mode 100755
index 0000000..65de3fc
--- /dev/null
+++ b/httemplate/edit/process/part_svc.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $server = new FS::UI::Web::JSRPC 'FS::part_svc::process', $cgi;
+
+</%init>
diff --git a/httemplate/edit/process/payment_gateway.html b/httemplate/edit/process/payment_gateway.html
new file mode 100644
index 0000000..b16bc3d
--- /dev/null
+++ b/httemplate/edit/process/payment_gateway.html
@@ -0,0 +1,35 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "payment_gateway.html?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/payment_gateway.html") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $gatewaynum = $cgi->param('gatewaynum');
+
+my $old = qsearchs('payment_gateway',{'gatewaynum'=>$gatewaynum}) if $gatewaynum;
+
+my $new = new FS::payment_gateway ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('payment_gateway')
+} );
+
+my @options = split(/\r?\n/, $cgi->param('gateway_options') );
+pop @options
+ if scalar(@options) % 2 && $options[-1] =~ /^\s*$/;
+my %options = @options;
+
+my $error;
+if ( $gatewaynum ) {
+ $error=$new->replace($old, \%options);
+} else {
+ $error=$new->insert(\%options);
+ $gatewaynum=$new->getfield('gatewaynum');
+}
+
+</%init>
diff --git a/httemplate/edit/process/pkg_category.html b/httemplate/edit/process/pkg_category.html
new file mode 100644
index 0000000..50cd5cb
--- /dev/null
+++ b/httemplate/edit/process/pkg_category.html
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+ 'table' => 'pkg_category',
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/pkg_class.html b/httemplate/edit/process/pkg_class.html
new file mode 100644
index 0000000..b196df3
--- /dev/null
+++ b/httemplate/edit/process/pkg_class.html
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+ 'table' => 'pkg_class',
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/prepay_credit.cgi b/httemplate/edit/process/prepay_credit.cgi
new file mode 100644
index 0000000..8f2eb2b
--- /dev/null
+++ b/httemplate/edit/process/prepay_credit.cgi
@@ -0,0 +1,62 @@
+%unless ( ref($error) ) {
+% $cgi->param('error', $error );
+<% $cgi->redirect(popurl(3). "edit/prepay_credit.cgi?". $cgi->query_string ) %>
+% } else {
+
+<% include('/elements/header.html', "$num prepaid cards generated".
+ ( $agent ? ' for '.$agent->agent : '' )
+ )
+%>
+
+<FONT SIZE="+1">
+% foreach my $card ( @$error ) {
+
+ <code><% $card %></code>
+ -
+ <% $hashref->{amount} ? sprintf('$%.2f', $hashref->{amount} ) : '' %>
+ <% $hashref->{amount} && $hashref->{seconds} ? 'and' : '' %>
+ <% $hashref->{seconds} ? duration_exact($hashref->{seconds}) : '' %>
+ <% $hashref->{upbytes} ? FS::UI::bytecount::bytecount_unexact($hashref->{upbytes}) : '' %>
+ <% $hashref->{downbytes} ? FS::UI::bytecount::bytecount_unexact($hashref->{downbytes}) : '' %>
+ <% $hashref->{totalbytes} ? FS::UI::bytecount::bytecount_unexact($hashref->{totalbytes}) : '' %>
+ <br>
+% }
+
+</FONT>
+
+<% include('/elements/footer.html') %>
+
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $hashref = {};
+
+my $agent = '';
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agent = qsearchs('agent', { 'agentnum' => $hashref->{agentnum}=$1 } );
+}
+
+my $error = '';
+
+my $num = 0;
+if ( $cgi->param('num') =~ /^\s*(\d+)\s*$/ ) {
+ $num = $1;
+} else {
+ $error = 'Illegal number of prepaid cards: '. $cgi->param('num');
+}
+
+$hashref->{amount} = $cgi->param('amount');
+$hashref->{seconds} = $cgi->param('seconds') * $cgi->param('multiplier');
+$hashref->{upbytes} = $cgi->param('upbytes') * $cgi->param('upmultiplier');
+$hashref->{downbytes} = $cgi->param('downbytes') * $cgi->param('downmultiplier');
+$hashref->{totalbytes} = $cgi->param('totalbytes') * $cgi->param('totalmultiplier');
+
+$error ||= FS::prepay_credit::generate( $num,
+ scalar($cgi->param('type')),
+ $hashref
+ );
+
+</%init>
diff --git a/httemplate/edit/process/quick-charge.cgi b/httemplate/edit/process/quick-charge.cgi
new file mode 100644
index 0000000..8fa57dd
--- /dev/null
+++ b/httemplate/edit/process/quick-charge.cgi
@@ -0,0 +1,68 @@
+% if ( $error ) {
+% $cgi->param('error', $error );
+<% $cgi->redirect($p.'quick-charge.html?'. $cgi->query_string) %>
+% } else {
+<% header("One-time charge added") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY></HTML>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('One-time charge');
+
+my $error = '';
+my $conf = new FS::conf;
+my $param = $cgi->Vars;
+
+my @description = ();
+for ( my $row = 0; exists($param->{"description$row"}); $row++ ) {
+ push @description, $param->{"description$row"}
+ if ($param->{"description$row"} =~ /\S/);
+}
+
+$param->{"custnum"} =~ /^(\d+)$/
+ or $error .= "Illegal customer number " . $param->{"custnum"} . " ";
+my $custnum = $1;
+
+$param->{"amount"} =~ /^\s*(\d+(\.\d{1,2})?)\s*$/
+ or $error .= "Illegal amount " . $param->{"amount"} . " ";
+my $amount = $1;
+
+my $quantity = 1;
+if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) {
+ $quantity = $1;
+}
+
+$param->{'tax_override'} =~ /^\s*([,\d]*)\s*$/
+ or $error .= "Illegal tax override " . $param->{"tax_override"} . " ";
+my $override = $1;
+
+if ( $param->{'taxclass'} eq '(select)' ) {
+ $error .= "Must select a tax class. "
+ unless ($conf->exists('enable_taxproducts') &&
+ ( $override || $param->{taxproductnum} )
+ );
+ $cgi->param('taxclass', '');
+}
+
+unless ( $error ) {
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or $error .= "Unknown customer number $custnum. ";
+
+ $error ||= $cust_main->charge( {
+ 'amount' => $amount,
+ 'quantity' => $quantity,
+ 'pkg' => scalar($cgi->param('pkg')),
+ 'setuptax' => scalar($cgi->param('setuptax')),
+ 'taxclass' => scalar($cgi->param('taxclass')),
+ 'taxproductnum' => scalar($cgi->param('taxproductnum')),
+ 'tax_override' => $override,
+ 'classnum' => scalar($cgi->param('classnum')),
+ 'additional' => \@description,
+ } );
+}
+
+</%init>
diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi
new file mode 100644
index 0000000..9c24743
--- /dev/null
+++ b/httemplate/edit/process/quick-cust_pkg.cgi
@@ -0,0 +1,63 @@
+%if ($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(3). 'misc/order_pkg.html?'. $cgi->query_string ) %>
+%} else {
+% my $frag = "cust_pkg". $cust_pkg->pkgnum;
+<% header('Package ordered') %>
+ <SCRIPT TYPE="text/javascript">
+ // XXX fancy ajax rebuild table at some point, but a page reload will do for now
+
+ // XXX chop off trailing #target and replace... ?
+ window.top.location = '<% popurl(3). "view/cust_main.cgi?keywords=$custnum;fragment=$frag#$frag" %>';
+
+ </SCRIPT>
+
+ </BODY></HTML>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Order customer package');
+
+#untaint custnum (probably not necessary, searching for it is escape enough)
+$cgi->param('custnum') =~ /^(\d+)$/
+ or die 'illegal custnum '. $cgi->param('custnum');
+my $custnum = $1;
+my $cust_main = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die 'unknown custnum' unless $cust_main;
+
+#probably not necessary, taken care of by cust_pkg::check
+$cgi->param('pkgpart') =~ /^(\d+)$/
+ or die 'illegal pkgpart '. $cgi->param('pkgpart');
+my $pkgpart = $1;
+$cgi->param('refnum') =~ /^(\d*)$/
+ or die 'illegal refnum '. $cgi->param('refnum');
+my $refnum = $1;
+$cgi->param('locationnum') =~ /^(\-?\d*)$/
+ or die 'illegal locationnum '. $cgi->param('locationnum');
+my $locationnum = $1;
+
+my $cust_pkg = new FS::cust_pkg {
+ 'custnum' => $custnum,
+ 'pkgpart' => $pkgpart,
+ 'refnum' => $refnum,
+ 'locationnum' => $locationnum,
+};
+
+my %opt = ( 'cust_pkg' => $cust_pkg );
+
+if ( $locationnum == -1 ) {
+ my $cust_location = new FS::cust_location {
+ map { $_ => scalar($cgi->param($_)) }
+ qw( custnum address1 address2 city county state zip country )
+ };
+ $opt{'cust_location'} = $cust_location;
+}
+
+my $error = $cust_main->order_pkg( %opt );
+
+</%init>
diff --git a/httemplate/edit/process/rate.cgi b/httemplate/edit/process/rate.cgi
new file mode 100755
index 0000000..48d9322
--- /dev/null
+++ b/httemplate/edit/process/rate.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $server = new FS::UI::Web::JSRPC 'FS::rate::process', $cgi;
+
+</%init>
diff --git a/httemplate/edit/process/rate_detail.html b/httemplate/edit/process/rate_detail.html
new file mode 100644
index 0000000..6200d61
--- /dev/null
+++ b/httemplate/edit/process/rate_detail.html
@@ -0,0 +1,13 @@
+<% include( 'elements/process.html',
+ 'table' => 'rate_detail',
+ 'popup_reload' => 'Rate changed', #a popup "parent reload" for now
+ #someday change the individual element and go away instead
+ )
+%>
+<%init>
+
+my $conf = new FS::Conf;
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/rate_region.cgi b/httemplate/edit/process/rate_region.cgi
new file mode 100755
index 0000000..882991e
--- /dev/null
+++ b/httemplate/edit/process/rate_region.cgi
@@ -0,0 +1,57 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "rate_region.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/rate_region.html") %>
+%}
+<%init>
+
+my $conf = new FS::Conf;
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $regionnum = $cgi->param('regionnum');
+
+my $old = qsearchs('rate_region', { 'regionnum' => $regionnum } ) if $regionnum;
+
+my $new = new FS::rate_region ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } ( fields('rate_region') )
+} );
+
+my $countrycode = $cgi->param('countrycode');
+my @npa = split(/\s*,\s*/, $cgi->param('npa'));
+$npa[0] = '' unless @npa;
+my @rate_prefix = map {
+ #my($npa,$nxx) = split('-', $_);
+ s/\D//g;
+ new FS::rate_prefix {
+ 'countrycode' => $countrycode,
+ #'npa' => $npa,
+ #'nxx' => $nxx,
+ 'npa' => $_,
+ }
+ } @npa;
+
+my @dest_detail = map {
+ my $ratenum = $_->ratenum;
+ new FS::rate_detail {
+ 'ratenum' => $ratenum,
+ map { $_ => $cgi->param("$_$ratenum") }
+ qw( min_included min_charge sec_granularity classnum )
+ };
+} qsearch('rate', {} );
+
+
+my $error;
+if ( $regionnum ) {
+ $error = $new->replace($old, 'rate_prefix' => \@rate_prefix,
+ 'dest_detail' => \@dest_detail, );
+} else {
+ $error = $new->insert( 'rate_prefix' => \@rate_prefix,
+ 'dest_detail' => \@dest_detail, );
+ $regionnum = $new->getfield('regionnum');
+}
+
+</%init>
diff --git a/httemplate/edit/process/reason.html b/httemplate/edit/process/reason.html
new file mode 100644
index 0000000..cb79ed2
--- /dev/null
+++ b/httemplate/edit/process/reason.html
@@ -0,0 +1,12 @@
+<% include( 'elements/process.html',
+ 'table' => 'reason',
+ 'redirect' => popurl(3) . 'browse/reason.html?class=' .
+ $cgi->param('class') . '&',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/reason_type.html b/httemplate/edit/process/reason_type.html
new file mode 100644
index 0000000..3172b27
--- /dev/null
+++ b/httemplate/edit/process/reason_type.html
@@ -0,0 +1,12 @@
+<% include( 'elements/process.html',
+ 'table' => 'reason_type',
+ 'redirect' => popurl(3) . 'browse/reason_type.html?class=' .
+ $cgi->param('class') . '&',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/reg_code.cgi b/httemplate/edit/process/reg_code.cgi
new file mode 100644
index 0000000..035e10b
--- /dev/null
+++ b/httemplate/edit/process/reg_code.cgi
@@ -0,0 +1,45 @@
+%unless ( ref($error) ) {
+% $cgi->param('error'. $error );
+<% $cgi->redirect(popurl(3). "edit/reg_code.cgi?". $cgi->query_string ) %>
+% } else {
+
+<% include("/elements/header.html","$num registration codes generated for ". $agent->agent, menubar(
+ 'View all agents' => popurl(3). 'browse/agent.cgi',
+) ) %>
+
+<PRE><FONT SIZE="+1">
+% foreach my $code ( @$error ) {
+ <% $code %>
+% }
+</FONT></PRE>
+
+<% include('/elements/footer.html') %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('agentnum') =~ /^(\d+)$/
+ or errorpage('illegal agentnum '. $cgi->param('agentnum'));
+my $agentnum = $1;
+my $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+
+my $error = '';
+
+my $num = 0;
+if ( $cgi->param('num') =~ /^\s*(\d+)\s*$/ ) {
+ $num = $1;
+} else {
+ $error = 'Illegal number of codes: '. $cgi->param('num');
+}
+
+my @pkgparts =
+ map { /^pkgpart(.*)$/; $1 }
+ grep { $cgi->param($_) }
+ grep { /^pkgpart/ }
+ $cgi->param;
+
+$error ||= $agent->generate_reg_codes($num, \@pkgparts);
+
+</%init>
diff --git a/httemplate/edit/process/router.cgi b/httemplate/edit/process/router.cgi
new file mode 100644
index 0000000..3cbb8c5
--- /dev/null
+++ b/httemplate/edit/process/router.cgi
@@ -0,0 +1,20 @@
+<% include('elements/process.html',
+ 'table' => 'router',
+ 'viewall_dir' => 'browse',
+ 'viewall_ext' => 'cgi',
+ 'edit_ext' => 'cgi',
+ 'process_m2m' => { 'link_table' => 'part_svc_router',
+ 'target_table' => 'part_svc',
+ },
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Broadband global configuration',
+ )
+%>
+<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+</%init>
diff --git a/httemplate/edit/process/svc_Common.html b/httemplate/edit/process/svc_Common.html
new file mode 100644
index 0000000..cf5f01f
--- /dev/null
+++ b/httemplate/edit/process/svc_Common.html
@@ -0,0 +1,16 @@
+<% include( 'elements/svc_Common.html',
+ 'table' => $table,
+ 'redirect' => popurl(3)."view/svc_Common.html?svcdb=$table;svcnum=",
+ 'error_redirect' => popurl(3)."edit/svc_Common.html?svcdb=$table;",
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+$cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unparsable svcdb";
+my $table = $1;
+require "FS/$table.pm";
+
+</%init>
diff --git a/httemplate/edit/process/svc_acct.cgi b/httemplate/edit/process/svc_acct.cgi
new file mode 100755
index 0000000..0a89e25
--- /dev/null
+++ b/httemplate/edit/process/svc_acct.cgi
@@ -0,0 +1,64 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "svc_acct.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "view/svc_acct.cgi?" . $svcnum ) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum = $1;
+
+my $old;
+if ( $svcnum ) {
+ $old = qsearchs('svc_acct', { 'svcnum' => $svcnum } )
+ or die "fatal: can't find account (svcnum $svcnum)!";
+} else {
+ $old = '';
+}
+
+#unmunge popnum
+$cgi->param('popnum', (split(/:/, $cgi->param('popnum') ))[0] );
+
+#unmunge passwd
+if ( $cgi->param('_password') eq '*HIDDEN*' ) {
+ die "fatal: no previous account to recall hidden password from!" unless $old;
+ $cgi->param('_password',$old->getfield('_password'));
+}
+
+#unmunge usergroup
+$cgi->param('usergroup', [ $cgi->param('radius_usergroup') ] );
+
+#unmunge bytecounts
+foreach (map { $_,$_."_threshold" } qw( upbytes downbytes totalbytes )) {
+ $cgi->param($_, FS::UI::bytecount::parse_bytecount($cgi->param($_)) );
+}
+
+my %hash = $svcnum ? $old->hash : ();
+map {
+ $hash{$_} = scalar($cgi->param($_));
+ #} qw(svcnum pkgnum svcpart username _password popnum uid gid finger dir
+ # shell quota slipip)
+ } (fields('svc_acct'), qw ( pkgnum svcpart usergroup ));
+my $new = new FS::svc_acct ( \%hash );
+
+my $error;
+if ( $svcnum ) {
+ foreach (grep { $old->$_ != $new->$_ } qw( seconds upbytes downbytes totalbytes )) {
+ my %hash = map { $_ => $new->$_ }
+ grep { $new->$_ }
+ qw( seconds upbytes downbytes totalbytes );
+
+ $error = $new->set_usage(\%hash); #unoverlimit and trigger radius changes
+ last; #once is enough
+ }
+ $error ||= $new->replace($old);
+} else {
+ $error = $new->insert;
+ $svcnum = $new->svcnum;
+}
+
+</%init>
diff --git a/httemplate/edit/process/svc_acct_pop.cgi b/httemplate/edit/process/svc_acct_pop.cgi
new file mode 100755
index 0000000..6e823a8
--- /dev/null
+++ b/httemplate/edit/process/svc_acct_pop.cgi
@@ -0,0 +1,33 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "svc_acct_pop.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/svc_acct_pop.cgi") %>
+%}
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Dialup configuration')
+ || $curuser->access_right('Dialup global configuration');
+
+my $popnum = $cgi->param('popnum');
+
+my $old = qsearchs('svc_acct_pop',{'popnum'=>$popnum}) if $popnum;
+
+my $new = new FS::svc_acct_pop ( {
+ map {
+ $_, scalar($cgi->param($_));
+ } fields('svc_acct_pop')
+} );
+
+my $error = '';
+if ( $popnum ) {
+ $error = $new->replace($old);
+} else {
+ $error = $new->insert;
+ $popnum=$new->getfield('popnum');
+}
+
+</%init>
diff --git a/httemplate/edit/process/svc_broadband.cgi b/httemplate/edit/process/svc_broadband.cgi
new file mode 100644
index 0000000..d5c9820
--- /dev/null
+++ b/httemplate/edit/process/svc_broadband.cgi
@@ -0,0 +1,8 @@
+<% include('elements/svc_Common.html', 'table' => 'svc_broadband') %>
+<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Provision customer service'); #something else more specific?
+
+</%init>
diff --git a/httemplate/edit/process/svc_domain.cgi b/httemplate/edit/process/svc_domain.cgi
new file mode 100755
index 0000000..9993a87
--- /dev/null
+++ b/httemplate/edit/process/svc_domain.cgi
@@ -0,0 +1,33 @@
+%if ($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "svc_domain.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+#remove this to actually test the domains!
+$FS::svc_domain::whois_hack = 1;
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum = $1;
+
+my $new = new FS::svc_domain ( {
+ map {
+ $_, scalar($cgi->param($_));
+ #} qw(svcnum pkgnum svcpart domain action purpose)
+ } ( fields('svc_domain'), qw( pkgnum svcpart action purpose ) )
+} );
+
+my $error = '';
+if ($cgi->param('svcnum')) {
+ $error="Can't modify a domain!";
+} else {
+ $error=$new->insert;
+ $svcnum=$new->svcnum;
+}
+
+</%init>
diff --git a/httemplate/edit/process/svc_external.cgi b/httemplate/edit/process/svc_external.cgi
new file mode 100755
index 0000000..673e5a5
--- /dev/null
+++ b/httemplate/edit/process/svc_external.cgi
@@ -0,0 +1,31 @@
+%if ($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "svc_external.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "view/svc_external.cgi?$svcnum") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum =$1;
+
+my $old = qsearchs('svc_external',{'svcnum'=>$svcnum}) if $svcnum;
+
+my $new = new FS::svc_external ( {
+ map {
+ ($_, scalar($cgi->param($_)));
+ } ( fields('svc_external'), qw( pkgnum svcpart ) )
+} );
+
+my $error = '';
+if ( $svcnum ) {
+ $error = $new->replace($old);
+} else {
+ $error = $new->insert;
+ $svcnum = $new->getfield('svcnum');
+}
+
+</%init>
diff --git a/httemplate/edit/process/svc_forward.cgi b/httemplate/edit/process/svc_forward.cgi
new file mode 100755
index 0000000..fffad84
--- /dev/null
+++ b/httemplate/edit/process/svc_forward.cgi
@@ -0,0 +1,31 @@
+%if ($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "svc_forward.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "view/svc_forward.cgi?$svcnum") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum =$1;
+
+my $old = qsearchs('svc_forward',{'svcnum'=>$svcnum}) if $svcnum;
+
+my $new = new FS::svc_forward ( {
+ map {
+ ($_, scalar($cgi->param($_)));
+ } ( fields('svc_forward'), qw( pkgnum svcpart ) )
+} );
+
+my $error = '';
+if ( $svcnum ) {
+ $error = $new->replace($old);
+} else {
+ $error = $new->insert;
+ $svcnum = $new->getfield('svcnum');
+}
+
+</%init>
diff --git a/httemplate/edit/process/svc_phone.html b/httemplate/edit/process/svc_phone.html
new file mode 100644
index 0000000..27a703c
--- /dev/null
+++ b/httemplate/edit/process/svc_phone.html
@@ -0,0 +1,10 @@
+<% include( 'elements/svc_Common.html',
+ 'table' => 'svc_phone',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+</%init>
diff --git a/httemplate/edit/process/svc_www.cgi b/httemplate/edit/process/svc_www.cgi
new file mode 100644
index 0000000..f02d253
--- /dev/null
+++ b/httemplate/edit/process/svc_www.cgi
@@ -0,0 +1,38 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "svc_www.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "view/svc_www.cgi?" . $svcnum ) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum = $1;
+
+my $old;
+if ( $svcnum ) {
+ $old = qsearchs('svc_www', { 'svcnum' => $svcnum } )
+ or die "fatal: can't find website (svcnum $svcnum)!";
+} else {
+ $old = '';
+}
+
+my $new = new FS::svc_www ( {
+ map {
+ ($_, scalar($cgi->param($_)));
+ #} qw(svcnum pkgnum svcpart recnum usersvc)
+ } ( fields('svc_www'), qw( pkgnum svcpart ) )
+} );
+
+my $error;
+if ( $svcnum ) {
+ $error = $new->replace($old);
+} else {
+ $error = $new->insert;
+ $svcnum = $new->svcnum;
+}
+
+</%init>
diff --git a/httemplate/edit/process/tax_class.html b/httemplate/edit/process/tax_class.html
new file mode 100644
index 0000000..339c908
--- /dev/null
+++ b/httemplate/edit/process/tax_class.html
@@ -0,0 +1,49 @@
+% if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "tax_class.html?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/tax_rate.cgi?taxclassnum=". uri_escape($tax_class->taxclassnum) ) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $tax_class = new FS::tax_class {
+ 'taxclass' => $cgi->param('taxclass'),
+ 'description' => $cgi->param('description'),
+};
+
+#maybe this whole thing should be in a transaction. at some point, no biggie
+#none of the follow-up stuff will fail unless there's a more serious problem
+#than a hanging record in tax_class...
+
+my $error = $tax_class->insert;
+
+# all of this is highly dubious at the moment
+
+#unless ( $error ) {
+# #auto-add the new taxclass to any regions that have taxclasses already
+#
+# my $sth = dbh->prepare("
+# SELECT geocode FROM tax_rate
+# WHERE taxclass IS NOT NULL AND taxclass != ''
+# GROUP BY geocode
+# ") or die dbh->errstr;
+# $sth->execute or die $sth->errstr;
+#
+# while ( my $row = $sth->fetchrow_hashref ) {
+# warn "inserting for $row";
+# my $cust_main_county = new FS::tax_rate {
+# 'geocode' => $row->{geocode},
+# 'tax' => 0,
+# 'taxclassnum' => $tax_class->taxclassnum,
+# };
+# $error = $cust_main_county->insert;
+# #last if $error;
+# die $error if $error;
+# }
+#
+#}
+
+</%init>
diff --git a/httemplate/edit/process/tax_rate.html b/httemplate/edit/process/tax_rate.html
new file mode 100644
index 0000000..431e542
--- /dev/null
+++ b/httemplate/edit/process/tax_rate.html
@@ -0,0 +1,22 @@
+<% include( 'elements/process.html',
+ 'table' => 'tax_rate',
+ 'value_callback' => $value_callback,
+ 'popup_reload' => 'Tax changed', #a popup "parent reload" for now
+ #someday change the individual element and go away instead
+ )
+%>
+<%once>
+
+my $value_callback = sub { my ($field, $value) = @_;
+ ($field =~ /^(tax|excessrate|usetax|useexcessrate)$/)
+ ? $value/100
+ : $value
+ };
+</%once>
+<%init>
+
+my $conf = new FS::Conf;
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/usage_class.html b/httemplate/edit/process/usage_class.html
new file mode 100644
index 0000000..cf50cb7
--- /dev/null
+++ b/httemplate/edit/process/usage_class.html
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+ 'table' => 'usage_class',
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/quick-charge.html b/httemplate/edit/quick-charge.html
new file mode 100644
index 0000000..c18b2bc
--- /dev/null
+++ b/httemplate/edit/quick-charge.html
@@ -0,0 +1,197 @@
+<% include("/elements/header-popup.html", 'One-time charge', '',
+ ( $cgi->param('error') ? '' : 'onload="addRow()"' ),
+ )
+%>
+
+<% include('/elements/error.html') %>
+
+<SCRIPT TYPE="text/javascript">
+
+function enable_quick_charge () {
+ if ( document.QuickChargeForm.amount.value
+ && document.QuickChargeForm.pkg.value ) {
+ document.QuickChargeForm.submit.disabled = false;
+ } else {
+ document.QuickChargeForm.submit.disabled = true;
+ }
+}
+
+function validate_quick_charge () {
+ var pkg = document.QuickChargeForm.pkg.value;
+ var pkg_regex = /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ ;
+ var amount = document.QuickChargeForm.amount.value;
+ var amount_regex = /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ;
+ var rval = true;
+
+ if ( ! amount_regex.test(amount) ) {
+ alert('Illegal amount - enter an amount to charge, for example, "5" or "43" or "21.46".');
+ return false;
+ }
+ if ( String(pkg).length < 1 ) {
+ rval = false;
+ }
+ if ( ! pkg_regex.test(pkg) ) {
+ rval = false;
+ }
+ var i=0;
+ for (i=0; i < rownum; i++) {
+ if (! eval('pkg_regex.test(document.QuickChargeForm.description' + i + '.value)')){
+ rval = false;
+ break;
+ }
+ }
+ if (rval == true) {
+ return true;
+ }
+
+ if ( ! pkg ) {
+ alert('Enter a description for the one-time charge');
+ return false;
+ }
+
+ alert('Illegal description - spaces, letters, numbers, and the following punctuation characters are allowed: . , ! ? @ # $ % & ( ) - + ; : ' + "'" + ' " = [ ]' );
+ return false;
+}
+
+</SCRIPT>
+
+<FORM ACTION="process/quick-charge.cgi" NAME="QuickChargeForm" ID="QuickChargeForm" METHOD="POST" onsubmit="document.QuickChargeForm.submit.disabled=true;return validate_quick_charge();">
+
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+<TABLE ID="QuickChargeTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 STYLE="background-color: #cccccc">
+
+<TR>
+ <TD ALIGN="right">Amount </TD>
+ <TD>
+ $<INPUT TYPE="text" NAME="amount" SIZE=6 VALUE="<% $amount %>" onChange="enable_quick_charge()" onKeyPress="enable_quick_charge()">
+ </TD>
+</TR>
+
+% if ( $conf->exists('invoice-unitprice') ) {
+ <TR>
+ <TD ALIGN="right">Quantity </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="quantity" SIZE=4 VALUE="<% $quantity %>">
+ </TD>
+ </TR>
+% }
+
+<% include('/elements/tr-select-pkg_class.html', 'curr_value' => $cgi->param('classnum') ) %>
+
+
+<TR>
+ <TD ALIGN="right">Tax exempt </TD>
+ <TD><INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y" <% $cgi->param('setuptax') ? 'CHECKED' : '' %>></TD>
+</TR>
+
+<% include('/elements/tr-select-taxclass.html', 'curr_value' => $cgi->param('taxclass') ) %>
+
+<% include('/elements/tr-select-taxproduct.html', 'label' => 'Tax product', 'onclick' => 'parent.taxproductmagic(this);', 'curr_value' => $cgi->param('taxproductnum') ) %>
+
+<% include('/elements/tr-select-taxoverride.html', 'onclick' => 'parent.taxoverridemagic(this);', 'curr_value' => $cgi->param('tax_override') ) %>
+
+<TR>
+ <TD ALIGN="right">Description </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="pkg" SIZE="50" MAXLENGTH="50" VALUE="<% $pkg %>" onChange="enable_quick_charge()" onKeyPress="enable_quick_charge()">
+ </TD>
+</TR>
+
+<TR>
+ <TD></TD>
+ <TD><FONT SIZE="-1">Optional additional description (also printed on invoice): </FONT></TD>
+</TR>
+
+% my $row = 0;
+% if ( $cgi->param('error') || $cgi->param('magic') ) {
+% my $param = $cgi->Vars;
+%
+% for ( $row = 0; exists($param->{"description$row"}); $row++ ) {
+
+ <TR>
+ <TD></TD>
+ <TD>
+ <INPUT TYPE="text" NAME="description<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $param->{"description$row"} |h %>" rownum="<% $row %>" onkeyup = "possiblyAddRow;" >
+ </TD>
+ </TR>
+% }
+% }
+
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="Add one-time charge" <% $cgi->param('error') ? '' :' DISABLED' %>>
+
+</FORM>
+
+
+<SCRIPT TYPE="text/javascript">
+
+ var rownum = <% $row %>;
+
+ function possiblyAddRow() {
+ if ( ( rownum - this.getAttribute('rownum') ) == 1 ) {
+ addRow();
+ }
+ }
+
+ function addRow() {
+
+ var table = document.getElementById('QuickChargeTable');
+ var tablebody = table.getElementsByTagName('tbody').item(0);
+
+ var row = document.createElement('TR');
+
+ var empty_cell = document.createElement('TD');
+ row.appendChild(empty_cell);
+
+ var description_cell = document.createElement('TD');
+
+ var description_input = document.createElement('INPUT');
+ description_input.setAttribute('name', 'description'+rownum);
+ description_input.setAttribute('id', 'description'+rownum);
+ description_input.setAttribute('size', 60);
+ description_input.setAttribute('maxLength', 65);
+ description_input.setAttribute('rownum', rownum);
+ description_input.onkeyup = possiblyAddRow;
+ description_cell.appendChild(description_input);
+
+ row.appendChild(description_cell);
+
+ tablebody.appendChild(row);
+
+ rownum++;
+
+ }
+
+</SCRIPT>
+
+</BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('One-time charge');
+
+my $conf = new FS::Conf;
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
+my $custnum = $1;
+
+my $amount = '';
+if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) {
+ $amount = $1;
+}
+
+my $quantity = 1;
+if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) {
+ $quantity = $1;
+}
+
+$cgi->param('pkg') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/
+ or die 'illegal description';
+my $pkg = $1;
+
+</%init>
diff --git a/httemplate/edit/rate.cgi b/httemplate/edit/rate.cgi
new file mode 100644
index 0000000..4c0abfe
--- /dev/null
+++ b/httemplate/edit/rate.cgi
@@ -0,0 +1,43 @@
+<% include("/elements/header.html","$action Rate plan", menubar(
+ 'View all rate plans' => "${p}browse/rate.cgi",
+ ))
+%>
+
+<% include('/elements/progress-init.html',
+ 'OneTrueForm',
+ [ 'rate', 'min_', 'sec_' ],
+ 'process/rate.cgi',
+ $p.'browse/rate.cgi',
+ )
+%>
+<FORM NAME="OneTrueForm">
+<INPUT TYPE="hidden" NAME="ratenum" VALUE="<% $rate->ratenum %>">
+
+Rate plan
+<INPUT TYPE="text" NAME="ratename" SIZE=32 VALUE="<% $rate->ratename %>">
+<BR><BR>
+
+<INPUT NAME="submit" TYPE="button" VALUE="<%
+ $rate->ratenum ? "Apply changes" : "Add rate plan"
+%>" onClick="document.OneTrueForm.submit.disabled=true; process();">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $rate;
+if ( $cgi->keywords ) {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $rate = qsearchs( 'rate', { 'ratenum' => $1 } );
+} else { #adding
+ $rate = new FS::rate {};
+}
+my $action = $rate->ratenum ? 'Edit' : 'Add';
+
+</%init>
diff --git a/httemplate/edit/rate_detail.html b/httemplate/edit/rate_detail.html
new file mode 100644
index 0000000..dd8c3f6
--- /dev/null
+++ b/httemplate/edit/rate_detail.html
@@ -0,0 +1,63 @@
+<% include('elements/edit.html',
+ 'popup' => 1,
+ 'name' => $name,
+ 'table' => 'rate_detail',
+ 'labels' => { 'ratedetailnum' => 'Rate', #should hide...
+ 'dest_regionname' => 'Region',
+ 'dest_prefixes_short' => 'Prefix(es)',
+ 'min_included' => 'Included minutes/calls',
+ 'min_charge' => 'Charge per minute/call',
+ 'sec_granularity' => 'Granularity',
+ 'classnum' => 'Usage class',
+ },
+ 'fields' => [
+ { field=>'ratenum', type=>'hidden', },
+ { field=>'orig_regionnum', type=>'hidden', },
+ { field=>'dest_regionnum', type=>'hidden', },
+ { field=>'dest_regionname', type=>'fixed', },
+ { field=>'dest_prefixes_short', type=>'fixed', },
+ { field=>'min_included', type=>'text', size=>5 },
+ { field=>'min_charge', type=>'money', size=>4 },
+ { field =>'sec_granularity',
+ type =>'select',
+ options => [ keys %granularity ],
+ labels => \%granularity,
+ disable_empty => 1,
+ },
+ { field =>'classnum',
+ type =>'select-table',
+ table =>'usage_class',
+ name_col =>'classname',
+ empty_label =>'(default)',
+ hashref =>{ disabled => '' },
+ },
+
+ ],
+ )
+%>
+<%once>
+
+tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
+
+</%once>
+
+<%init>
+
+my $conf = new FS::Conf;
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+#slightly inefficient, i suppose an edit+error callback would be better
+my $name = 'rate';
+my ($keywords) = $cgi->keywords;
+if ( $keywords =~ /^(\d+)$/
+ || $cgi->param('ratedetailnum') =~ /^(\d+)$/ ) {
+ my $rate_detail = qsearchs('rate_detail', { 'ratedetailnum' => $1 } )
+ or die "unknown ratedetailnum $1";
+ $name =
+ $rate_detail->rate->ratename. ' rate for '. $rate_detail->dest_regionname;
+}
+
+#sec_granularity should default to 60! for new rates when this gets used for em
+
+</%init>
diff --git a/httemplate/edit/rate_region.cgi b/httemplate/edit/rate_region.cgi
new file mode 100644
index 0000000..9ca3a35
--- /dev/null
+++ b/httemplate/edit/rate_region.cgi
@@ -0,0 +1,163 @@
+<% include("/elements/header.html","$action Region", menubar(
+ 'View all regions' => "${p}browse/rate_region.html",
+ ))
+%>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%$p1%>process/rate_region.cgi" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="regionnum" VALUE="<% $rate_region->regionnum %>">
+
+%# region info
+
+<% ntable('#cccccc') %>
+
+ <TR>
+ <TH ALIGN="right">Region name</TH>
+ <TD><INPUT TYPE="text" NAME="regionname" SIZE=32 VALUE="<% $rate_region->regionname %>"></TR>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">Country code</TH>
+ <TD><INPUT TYPE="text" NAME="countrycode" SIZE=4 MAXLENGTH=3 VALUE="<% $countrycode %>"></TR>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">
+ <B>Prefixes</B>
+ <BR><FONT SIZE="-1">(comma-separated)</FONT>
+ </TD>
+ <TD>
+ <TEXTAREA NAME="npa" WRAP=SOFT><% join(', ', map { $_->npa. (length($_->nxx) ? '-'.$_->nxx : '') } @rate_prefix ) %></TEXTAREA>
+ </TD>
+ </TR>
+
+</TABLE>
+
+%# rate plan info
+
+<BR>
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ Rate plan
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Included<BR>minutes/calls</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Charge per<BR>minute/call</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Granularity</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Usage class</FONT>
+ </TH>
+ </TR>
+
+% foreach my $rate ( qsearch('rate', {}) ) {
+%
+% my $n = $rate->ratenum;
+% my $rate_detail = $rate->dest_detail($rate_region)
+% || new FS::rate_region { 'min_included' => 0,
+% 'min_charge' => 0,
+% 'sec_granularity' => '60'
+% };
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/rate.cgi?<% $rate->ratenum %>"><% $rate->ratename %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <INPUT TYPE="text" SIZE=9 NAME="min_included<%$n%>" VALUE="<% $cgi->param("min_included$n") || $rate_detail->min_included |h %>">
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ $<INPUT TYPE="text" SIZE=6 NAME="min_charge<%$n%>" VALUE="<% $cgi->param("min_charge$n") || $rate_detail->min_charge |h %>">
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <SELECT NAME="sec_granularity<%$n%>">
+% foreach my $granularity ( keys %granularity ) {
+ <OPTION VALUE="<%$granularity%>"<% $granularity == ( $cgi->param("sec_granularity$n") || $rate_detail->sec_granularity ) ? ' SELECTED' : '' %>><%$granularity{$granularity}%>
+% }
+ </SELECT>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% include( '/elements/select-table.html',
+ 'element_name' => "classnum$n",
+ 'table' => 'usage_class',
+ 'name_col' => 'classname',
+ 'empty_label' => '(default)',
+ 'hashref' => { disabled => '' },
+ 'curr_value' => ( $cgi->param("classnum$n") ||
+ $rate_detail->classnum ),
+ )
+ %>
+ </TD>
+
+ </TR>
+
+% }
+
+</TABLE>
+
+
+<BR><BR>
+<INPUT TYPE="submit" VALUE="<% $rate_region->regionnum ? "Apply changes" : "Add region" %>">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $rate_region;
+if ( $cgi->param('error') ) {
+ $rate_region = new FS::rate_region ( {
+ map { $_, scalar($cgi->param($_)) } fields('rate_region')
+ } );
+} elsif ( $cgi->keywords ) {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "unparsable regionnum";
+ $rate_region = qsearchs( 'rate_region', { 'regionnum' => $1 } )
+ or die "unknown regionnum $1\n";
+} else { #adding
+ $rate_region = new FS::rate_region {};
+}
+my $action = $rate_region->regionnum ? 'Edit' : 'Add';
+
+my $p1 = popurl(1);
+
+tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
+
+my @rate_prefix = $rate_region->rate_prefix;
+my $countrycode = '';
+if ( @rate_prefix ) {
+ $countrycode = $rate_prefix[0]->countrycode;
+ foreach my $rate_prefix ( @rate_prefix ) {
+ errorpage('multiple country codes per region not yet supported by web UI')
+ unless $rate_prefix->countrycode eq $countrycode;
+ }
+}
+
+</%init>
diff --git a/httemplate/edit/reason.html b/httemplate/edit/reason.html
new file mode 100644
index 0000000..620a2ea
--- /dev/null
+++ b/httemplate/edit/reason.html
@@ -0,0 +1,50 @@
+%
+% $cgi->param('class') =~ /^(\w)$/ or die "illegal class";
+% my $class=$1;
+%
+% my $classname = $FS::reason_type::class_name{$class};
+%
+% my (@types) = qsearch( 'reason_type', { 'class' => $class } );
+%
+% unless (scalar(@types)) {
+% print $cgi->redirect( "reason_type.html?class=$class" );
+% }
+<% include( 'elements/edit.html',
+ 'name' => ucfirst($classname) . ' Reason',
+ 'table' => 'reason',
+ 'labels' => {
+ 'reasonnum' => ucfirst($classname) . ' Reason',
+ 'reason_type' => ucfirst($classname) . ' Reason type',
+ 'reason' => ucfirst($classname) . ' Reason',
+ 'disabled' => 'Disabled',
+ 'class' => '',
+ },
+ 'fields' => [
+ { 'field' => 'reason_type',
+ 'type' => 'select',
+ #XXX use something more sane than a hashref
+ #then fix tr-select.html
+ 'value' => { 'vcolumn' => 'typenum',
+ 'ccolumn' => 'type',
+ 'values' => \@types,
+ },
+ },
+ 'reason',
+ { 'field' => 'class',
+ 'type' => 'hidden',
+ 'value' => $class,
+ },
+ { 'field' => 'disabled',
+ 'type' => 'checkbox',
+ 'value' => 'Y'
+ },
+ ],
+ 'viewall_url' => $p . "browse/reason.html?class=$class",
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/reason_type.html b/httemplate/edit/reason_type.html
new file mode 100644
index 0000000..ea5650e
--- /dev/null
+++ b/httemplate/edit/reason_type.html
@@ -0,0 +1,29 @@
+<% include( 'elements/edit.html',
+ 'name' => $classname . ' Reason Type',
+ 'table' => 'reason_type',
+ 'labels' => {
+ 'typenum' => $classname . ' reason type',
+ 'type' => $classname . ' reason type name',
+ 'class' => '',
+ },
+ 'fields' => [
+ 'type',
+ { 'field' => 'class',
+ 'type' => 'hidden',
+ },
+ ],
+ 'viewall_url' => $p . "browse/reason_type.html?class=$class",
+ 'new_hashref_callback' => sub {{ 'class' => $class }},
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('class') =~ /^(\w)$/;
+my $class = $1;
+
+my $classname = $FS::reason_type::class_name{$class};
+
+</%init>
diff --git a/httemplate/edit/reg_code.cgi b/httemplate/edit/reg_code.cgi
new file mode 100644
index 0000000..e57ac09
--- /dev/null
+++ b/httemplate/edit/reg_code.cgi
@@ -0,0 +1,44 @@
+<% include('/elements/header.html', 'Generate registration codes for '. $agent->agent) %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%popurl(1)%>process/reg_code.cgi" METHOD="POST" NAME="OneTrueForm" onSubmit="document.OneTrueForm.submit.disabled=true">
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>">
+
+Generate
+% my $num = '';
+% if ( $cgi->param('num') =~ /^\s*(\d+)\s*$/ ) {
+% $num = $1;
+% }
+<INPUT TYPE="text" NAME="num" VALUE="<% $num %>" SIZE=5 MAXLENGTH=4>
+registration codes for <B><% $agent->agent %></B> allowing the following packages:
+<BR><BR>
+
+% foreach my $part_pkg ( qsearch('part_pkg', { 'disabled' => '' } ) ) {
+% my $pkgpart = $part_pkg->pkgpart;
+
+ <INPUT TYPE="checkbox" NAME="pkgpart<% $pkgpart %>" <% $cgi->param("pkgpart$pkgpart") ? 'CHECKED' : '' %>>
+ <% $part_pkg->pkg %> - <% $part_pkg->comment %>
+ <BR>
+
+% }
+
+
+<BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="Generate">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $agentnum = $cgi->param('agentnum');
+$agentnum =~ /^(\d+)$/ or errorpage("illegal agentnum $agentnum");
+$agentnum = $1;
+my $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+
+</%init>
diff --git a/httemplate/edit/router.cgi b/httemplate/edit/router.cgi
new file mode 100755
index 0000000..19e63b3
--- /dev/null
+++ b/httemplate/edit/router.cgi
@@ -0,0 +1,44 @@
+<% include('elements/edit.html',
+ 'post_url' => popurl(1).'process/router.cgi',
+ 'name' => 'router',
+ 'table' => 'router',
+ 'viewall_url' => "${p}browse/router.cgi",
+ 'labels' => { 'routernum' => 'Router',
+ 'routername' => 'Name',
+ 'svc_part' => 'Service',
+ },
+ 'fields' => [
+ { 'field'=>'routername', 'type'=>'text', 'size'=>32 },
+ { 'field'=>'agentnum', 'type'=>'select-agent' },
+ ],
+ 'error_callback' => $callback,
+ 'edit_callback' => $callback,
+ 'new_callback' => $callback,
+ )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Broadband global configuration');
+
+my $callback = sub {
+ my ($cgi, $object, $fields) = (shift, shift, shift);
+ unless ($object->svcnum) {
+ push @{$fields},
+ { 'type' => 'tablebreak-tr-title',
+ 'value' => 'Select the service types available on this router',
+ },
+ { 'field' => 'svc_part',
+ 'type' => 'checkboxes-table',
+ 'target_table' => 'part_svc',
+ 'link_table' => 'part_svc_router',
+ 'name_col' => 'svc',
+ 'hashref' => { 'svcdb' => 'svc_broadband', 'disabled' => '' },
+ };
+ }
+};
+
+</%init>
diff --git a/httemplate/edit/svc_Common.html b/httemplate/edit/svc_Common.html
new file mode 100644
index 0000000..6666d97
--- /dev/null
+++ b/httemplate/edit/svc_Common.html
@@ -0,0 +1,33 @@
+<% include('elements/svc_Common.html',
+ 'table' => $table,
+ 'post_url' => popurl(1). "process/svc_Common.html",
+ %opt,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+# false laziness w/view/svc_Common.html
+
+$cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unparsable svcdb";
+my $table = $1;
+require "FS/$table.pm";
+
+my %opt;
+if ( UNIVERSAL::can("FS::$table", 'table_info') ) {
+ $opt{'name'} = "FS::$table"->table_info->{'name'};
+
+ my $fields = "FS::$table"->table_info->{'fields'};
+ my %labels = map { $_ => ( ref($fields->{$_})
+ ? $fields->{$_}{'label'}
+ : $fields->{$_}
+ );
+ }
+ keys %$fields;
+ $opt{'labels'} = \%labels;
+
+}
+
+</%init>
diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi
new file mode 100755
index 0000000..58283ef
--- /dev/null
+++ b/httemplate/edit/svc_acct.cgi
@@ -0,0 +1,452 @@
+<% include('/elements/header.html', "$action $svc account") %>
+
+<% include('/elements/error.html') %>
+
+% if ( $cust_main ) {
+
+ <% include( '/elements/small_custview.html', $cust_main, '', 1,
+ popurl(2) . "view/cust_main.cgi") %>
+ <BR>
+% }
+
+
+<FORM NAME="OneTrueForm" ACTION="<% $p1 %>process/svc_acct.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
+
+Service # <% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR>
+
+<% ntable("#cccccc",2) %>
+
+<TR>
+ <TD ALIGN="right">Service</TD>
+ <TD BGCOLOR="#eeeeee"><% $part_svc->svc %></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<% $username %>" SIZE=<% $ulen2 %> MAXLENGTH=<% $ulen %>>
+ </TD>
+</TR>
+
+%if ( $part_svc->part_svc_column('_password')->columnflag ne 'F' ) {
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>>
+ (blank to generate)
+ </TD>
+</TR>
+%}else{
+ <INPUT TYPE="hidden" NAME="_password" VALUE="<% $password %>">
+%}
+%
+%my $sec_phrase = $svc_acct->sec_phrase;
+%if ( $conf->exists('security_phrase')
+% && $part_svc->part_svc_column('sec_phrase')->columnflag ne 'F' ) {
+%
+
+
+ <TR>
+ <TD ALIGN="right">Security phrase</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $sec_phrase %>" SIZE=32>
+ (for forgotten passwords)
+ </TD>
+ </TD>
+% } else {
+
+
+ <INPUT TYPE="hidden" NAME="sec_phrase" VALUE="<% $sec_phrase %>">
+% }
+%
+%#domain
+%my $domsvc = $svc_acct->domsvc || 0;
+%if ( $part_svc->part_svc_column('domsvc')->columnflag eq 'F' ) {
+%
+
+
+ <INPUT TYPE="hidden" NAME="domsvc" VALUE="<% $domsvc %>">
+% } else {
+%
+% my %svc_domain = ();
+%
+% if ( $domsvc ) {
+% my $svc_domain = qsearchs('svc_domain', { 'svcnum' => $domsvc, } );
+% if ( $svc_domain ) {
+% $svc_domain{$svc_domain->svcnum} = $svc_domain;
+% } else {
+% warn "unknown svc_domain.svcnum for svc_acct.domsvc: $domsvc";
+% }
+% }
+%
+% %svc_domain = (%svc_domain,
+% domain_select_hash FS::svc_acct('svcpart' => $svcpart,
+% 'pkgnum' => $pkgnum,
+% )
+% );
+%
+
+
+ <TR>
+ <TD ALIGN="right">Domain</TD>
+ <TD>
+ <SELECT NAME="domsvc" SIZE=1>
+% foreach my $svcnum (
+% sort { $svc_domain{$a} cmp $svc_domain{$b} }
+% keys %svc_domain
+% ) {
+% my $svc_domain = $svc_domain{$svcnum};
+%
+
+
+ <OPTION VALUE="<% $svcnum %>" <% $svcnum == $domsvc ? ' SELECTED' : '' %>><% $svc_domain{$svcnum} %>
+% }
+
+ </SELECT>
+ </TD>
+ </TR>
+% }
+%
+%#pop
+%my $popnum = $svc_acct->popnum || 0;
+%if ( $part_svc->part_svc_column('popnum')->columnflag eq 'F' ) {
+%
+
+
+ <INPUT TYPE="hidden" NAME="popnum" VALUE="<% $popnum %>">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right">Access number</TD>
+ <TD><% FS::svc_acct_pop::popselector($popnum) %></TD>
+ </TR>
+% }
+% #uid/gid
+% foreach my $xid (qw( uid gid )) {
+%
+% if ( $part_svc->part_svc_column($xid)->columnflag =~ /^[FA]$/
+% || ! $conf->exists("svc_acct-edit_$xid")
+% ) {
+%
+% if ( length($svc_acct->$xid()) ) {
+
+
+ <TR>
+ <TD ALIGN="right"><% uc($xid) %></TD>
+ <TD BGCOLOR="#eeeeee"><% $svc_acct->$xid() %></TD>
+ <TD>
+ </TD>
+ </TR>
+% }
+
+
+ <INPUT TYPE="hidden" NAME="<% $xid %>" VALUE="<% $svc_acct->$xid() %>">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right"><% uc($xid) %></TD>
+ <TD>
+ <INPUT TYPE="text" NAME="<% $xid %>" SIZE=8 MAXLENGTH=6 VALUE="<% $svc_acct->$xid() %>">
+ </TD>
+ </TR>
+% }
+% }
+%
+%#finger
+%if ( $part_svc->part_svc_column('uid')->columnflag eq 'F'
+% && ! $svc_acct->finger ) {
+%
+
+
+ <INPUT TYPE="hidden" NAME="finger" VALUE="">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right">GECOS</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="finger" VALUE="<% $svc_acct->finger %>">
+ </TD>
+ </TR>
+% }
+%
+%#dir
+%if ( $part_svc->part_svc_column('dir')->columnflag eq 'F'
+% || !$curuser->access_right('Edit home dir')
+% ) {
+
+
+<INPUT TYPE="hidden" NAME="dir" VALUE="<% $svc_acct->dir %>">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right">Home directory</TD>
+ <TD><INPUT TYPE="text" NAME="dir" VALUE="<% $svc_acct->dir %>"></TD>
+ </TR>
+% }
+%
+%#shell
+%my $shell = $svc_acct->shell;
+%if ( $part_svc->part_svc_column('shell')->columnflag eq 'F'
+% || ( !$shell && $part_svc->part_svc_column('uid')->columnflag eq 'F' )
+% ) {
+%
+
+
+ <INPUT TYPE="hidden" NAME="shell" VALUE="<% $shell %>">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right">Shell</TD>
+ <TD>
+ <SELECT NAME="shell" SIZE=1>
+%
+% my($etc_shell);
+% foreach $etc_shell (@shells) {
+%
+
+
+ <OPTION<% $etc_shell eq $shell ? ' SELECTED' : '' %>><% $etc_shell %>
+% }
+
+
+ </SELECT>
+ </TD>
+ </TR>
+% }
+% if ( $part_svc->part_svc_column('quota')->columnflag eq 'F' ) {
+
+
+ <INPUT TYPE="hidden" NAME="quota" VALUE="<% $svc_acct->quota %>">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right">Quota:</TD>
+ <TD><INPUT TYPE="text" NAME="quota" VALUE="<% $svc_acct->quota %>"></TD>
+ </TR>
+% }
+% if ( $part_svc->part_svc_column('slipip')->columnflag =~ /^[FA]$/ ) {
+
+
+ <INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right">IP</TD>
+ <TD><INPUT TYPE="text" NAME="slipip" VALUE="<% $svc_acct->slipip %>"></TD>
+ </TR>
+% }
+%
+% my %label = ( seconds => 'Time',
+% upbytes => 'Upload bytes',
+% downbytes => 'Download bytes',
+% totalbytes => 'Total bytes',
+% );
+% foreach my $uf (keys %label) {
+% my $tf = $uf . "_threshold";
+% if ( $curuser->access_right('Edit usage') ) {
+ <TR>
+ <TD ALIGN="right"><% $label{$uf} %> remaining</TD>
+ <TD><INPUT TYPE="text" NAME="<% $uf %>" VALUE="<% $svc_acct->$uf %>">(blank disables)</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% $label{$uf} %> threshold</TD>
+ <TD><INPUT TYPE="text" NAME="<% $tf %>" VALUE="<% $svc_acct->$tf %>">(blank disables)</TD>
+ </TR>
+% }else{
+ <INPUT TYPE="hidden" NAME="<% $uf %>" VALUE="<% $svc_acct->$uf %>">
+ <INPUT TYPE="hidden" NAME="<% $tf %>" VALUE="<% $svc_acct->$tf %>">
+% }
+% }
+%
+%foreach my $r ( grep { /^r(adius|[cr])_/ } fields('svc_acct') ) {
+% $r =~ /^^r(adius|[cr])_(.+)$/ or next; #?
+% my $a = $2;
+%
+% if ( $part_svc->part_svc_column($r)->columnflag =~ /^[FA]$/ ) {
+
+
+ <INPUT TYPE="hidden" NAME="<% $r %>" VALUE="<% $svc_acct->getfield($r) %>">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right"><% $FS::raddb::attrib{$a} %></TD>
+ <TD><INPUT TYPE="text" NAME="<% $r %>" VALUE="<% $svc_acct->getfield($r) %>"></TD>
+ </TR>
+% }
+% }
+
+
+
+<TR>
+ <TD ALIGN="right">RADIUS groups</TD>
+% if ( $part_svc->part_svc_column('usergroup')->columnflag eq 'F' ) {
+
+
+ <TD BGCOLOR="#eeeeee"><% join('<BR>', @groups) %></TD>
+% } else {
+
+
+ <TD><% FS::svc_acct::radius_usergroup_selector( \@groups ) %></TD>
+% }
+
+
+</TR>
+% foreach my $field ($svc_acct->virtual_fields) {
+% # If the flag is X, it won't even show up in $svc_acct->virtual_fields.
+% if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) {
+
+
+ <% $svc_acct->pvf($field)->widget('HTML', 'edit', $svc_acct->getfield($field)) %>
+% }
+% }
+
+
+</TABLE>
+<BR>
+
+<INPUT TYPE="submit" VALUE="Submit">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+my $conf = new FS::Conf;
+my @shells = $conf->config('shells');
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my($svcnum, $pkgnum, $svcpart, $part_svc, $svc_acct, @groups);
+if ( $cgi->param('error') ) {
+
+ $svc_acct = new FS::svc_acct ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_acct')
+ } );
+ $svcnum = $svc_acct->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } );
+ die "No part_svc entry for svcpart $svcpart!" unless $part_svc;
+ @groups = $cgi->param('radius_usergroup');
+
+} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding
+
+ $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum';
+ $pkgnum = $1;
+ $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart';
+ $svcpart = $1;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ $svc_acct = new FS::svc_acct({svcpart => $svcpart});
+
+ $svcnum='';
+
+} else { #editing
+
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "unparsable svcnum";
+ $svcnum=$1;
+ $svc_acct=qsearchs('svc_acct',{'svcnum'=>$svcnum})
+ or die "Unknown (svc_acct) svcnum!";
+
+ my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum=$cust_svc->pkgnum;
+ $svcpart=$cust_svc->svcpart;
+
+ $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } );
+ die "No part_svc entry for svcpart $svcpart!" unless $part_svc;
+
+ @groups = $svc_acct->radius_groups;
+
+}
+
+my( $cust_pkg, $cust_main ) = ( '', '' );
+if ( $pkgnum ) {
+ $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $pkgnum } );
+ $cust_main = $cust_pkg->cust_main;
+}
+
+unless ( $svcnum || $cgi->param('error') ) { #adding
+
+ #set gecos
+ if ($cust_main) {
+ unless ( $part_svc->part_svc_column('uid')->columnflag eq 'F' ) {
+ $svc_acct->setfield('finger',
+ $cust_main->getfield('first') . " " . $cust_main->getfield('last')
+ );
+ }
+ }
+
+ $svc_acct->set_default_and_fixed( {
+ #false laziness w/svc-acct::_fieldhandlers
+ 'usergroup' => sub {
+ my( $self, $groups ) = @_;
+ if ( ref($groups) eq 'ARRAY' ) {
+ @groups = @$groups;
+ $groups;
+ } elsif ( length($groups) ) {
+ @groups = split(/\s*,\s*/, $groups);
+ [ @groups ];
+ } else {
+ @groups = ();
+ [];
+ }
+ }
+ } );
+
+}
+
+#fixed radius groups always override & display
+if ( $part_svc->part_svc_column('usergroup')->columnflag eq 'F' ) {
+ @groups = split(',', $part_svc->part_svc_column('usergroup')->columnvalue);
+}
+
+my $action = $svcnum ? 'Edit' : 'Add';
+
+my $svc = $part_svc->getfield('svc');
+
+my $otaker = getotaker;
+
+my $username = $svc_acct->username;
+my $password;
+if ( $svc_acct->_password ) {
+ if ( $conf->exists('showpasswords') || ! $svcnum ) {
+ $password = $svc_acct->_password;
+ } else {
+ $password = "*HIDDEN*";
+ }
+} else {
+ $password = '';
+}
+
+my $ulen =
+ $conf->exists('usernamemax')
+ ? $conf->config('usernamemax')
+ : dbdef->table('svc_acct')->column('username')->length;
+my $ulen2 = $ulen+2;
+
+my $pmax = $conf->config('passwordmax') || 8;
+my $pmax2 = $pmax+2;
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/svc_acct_pop.cgi b/httemplate/edit/svc_acct_pop.cgi
new file mode 100755
index 0000000..5930a38
--- /dev/null
+++ b/httemplate/edit/svc_acct_pop.cgi
@@ -0,0 +1,53 @@
+<% include('/elements/header.html', "$action Access Number", menubar(
+ 'View all Access Numbers' => popurl(2). "browse/svc_acct_pop.cgi",
+ ))
+%>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%$p1%>process/svc_acct_pop.cgi" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="popnum" VALUE="<% $hashref->{popnum} %>">
+Access Number #<% $hashref->{popnum} ? $hashref->{popnum} : "(NEW)" %>
+
+<PRE>
+City <INPUT TYPE="text" NAME="city" SIZE=32 VALUE="<% $hashref->{city} %>">
+State <INPUT TYPE="text" NAME="state" SIZE=16 MAXLENGTH=16 VALUE="<% $hashref->{state} %>">
+Area Code <INPUT TYPE="text" NAME="ac" SIZE=4 MAXLENGTH=3 VALUE="<% $hashref->{ac} %>">
+Exchange <INPUT TYPE="text" NAME="exch" SIZE=4 MAXLENGTH=3 VALUE="<% $hashref->{exch} %>">
+Local <INPUT TYPE="text" NAME="loc" SIZE=5 MAXLENGTH=4 VALUE="<% $hashref->{loc} %>">
+</PRE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="<% $hashref->{popnum} ? "Apply changes" : "Add Access Number" %>">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Dialup configuration')
+ || $curuser->access_right('Dialup global configuration');
+
+my $svc_acct_pop;
+if ( $cgi->param('error') ) {
+ $svc_acct_pop = new FS::svc_acct_pop ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_acct_pop')
+ } );
+} elsif ( $cgi->keywords ) { #editing
+ my($query)=$cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $svc_acct_pop=qsearchs('svc_acct_pop',{'popnum'=>$1});
+} else { #adding
+ $svc_acct_pop = new FS::svc_acct_pop {};
+}
+my $action = $svc_acct_pop->popnum ? 'Edit' : 'Add';
+my $hashref = $svc_acct_pop->hashref;
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi
new file mode 100644
index 0000000..e60c76c
--- /dev/null
+++ b/httemplate/edit/svc_broadband.cgi
@@ -0,0 +1,105 @@
+<% include('elements/svc_Common.html',
+ 'post_url' => popurl(1). 'process/svc_broadband.cgi',
+ 'name' => 'broadband service',
+ 'table' => 'svc_broadband',
+ 'labels' => { 'svcnum' => 'Service #',
+ 'description' => 'Description',
+ 'ip_addr' => 'IP address',
+ 'speed_down' => 'Download speed',
+ 'speed_up' => 'Upload speed',
+ 'blocknum' => 'Router/Block',
+ 'block_label' => 'Router/Block',
+ 'mac_addr' => 'MAC address',
+ 'latitude' => 'Latitude',
+ 'longitude' => 'Longitude',
+ 'altitude' => 'Altitude',
+ 'vlan_profile' => 'VLAN profile',
+ 'authkey' => 'Authentication key',
+ },
+ 'fields' => \@fields,
+ 'field_callback' => $callback,
+ 'dummy' => $cgi->query_string,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+# If it's stupid but it works, it's still stupid.
+# -Kristian
+
+my $conf = new FS::Conf;
+
+my @fields = (
+ qw( description ip_addr speed_down speed_up blocknum ),
+ { field=>'block_label', type=>'fixed' },
+ qw( mac_addr latitude longitude altitude vlan_profile authkey )
+);
+
+my $fixedblock = '';
+
+my $callback = sub {
+ my ($cgi, $object, $fieldref) = @_;
+
+ my $svcpart = $object->svcnum ? $object->cust_svc->svcpart
+ : $cgi->param('svcpart');
+
+ my $part_svc = qsearchs( 'part_svc', { svcpart => $svcpart } );
+ die "No part_svc entry!" unless $part_svc;
+
+ my $columndef = $part_svc->part_svc_column($fieldref->{'field'});
+ if ($columndef->columnflag eq 'F') {
+ $fieldref->{'type'} = 'fixed';
+ $fieldref->{'value'} = $columndef->columnvalue;
+ $fixedblock = $fieldref->{value}
+ if $fieldref->{field} eq 'blocknum';
+ }
+
+ if ($object->svcnum) {
+
+ $fieldref->{type} = 'hidden'
+ if $fieldref->{field} eq 'blocknum';
+
+ $fieldref->{value} = $object->addr_block->label
+ if $fieldref->{field} eq 'block_label';
+
+ } else {
+
+ if ($fieldref->{field} eq 'block_label') {
+ if ($fixedblock) {
+ $object->blocknum($fixedblock);
+ $fieldref->{value} = $object->addr_block->label;
+ }else{
+ $fieldref->{type} = 'hidden';
+ }
+ }
+
+ if ($fieldref->{field} eq 'blocknum') {
+ if ( $fixedblock or $conf->exists('auto_router') ) {
+ $fieldref->{type} = 'hidden';
+ $fieldref->{value} = $fixedblock;
+ return;
+ }
+
+ my $cust_pkg = qsearchs( 'cust_pkg', {pkgnum => $cgi->param('pkgnum')} );
+ die "No cust_pkg entry!" unless $cust_pkg;
+
+ $object->svcpart($part_svc->svcpart);
+ my @addr_block =
+ grep { ! $_->agentnum
+ || $cust_pkg->cust_main->agentnum == $_->agentnum
+ && $FS::CurrentUser::CurrentUser->agentnum($_->agentnum)
+ }
+ map { $_->addr_block } $object->allowed_routers;
+ my @options = map { $_->blocknum } @addr_block;
+ my %option_labels = map { ( $_->blocknum => $_->label ) } @addr_block;
+ $fieldref->{type} = 'select';
+ $fieldref->{options} = \@options;
+ $fieldref->{labels} = \%option_labels;
+ }
+
+ }
+};
+
+</%init>
diff --git a/httemplate/edit/svc_domain.cgi b/httemplate/edit/svc_domain.cgi
new file mode 100755
index 0000000..56ba604
--- /dev/null
+++ b/httemplate/edit/svc_domain.cgi
@@ -0,0 +1,91 @@
+<% include('/elements/header.html', "$action $svc", '') %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p1 %>process/svc_domain.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
+
+<INPUT TYPE="radio" NAME="action" VALUE="N"<% $kludge_action eq 'N' ? ' CHECKED' : '' %>>New
+<BR>
+
+<INPUT TYPE="radio" NAME="action" VALUE="M"<% $kludge_action eq 'M' ? ' CHECKED' : '' %>>Transfer
+
+<P>Domain <INPUT TYPE="text" NAME="domain" VALUE="<% $domain %>" SIZE=28 MAXLENGTH=63>
+
+<BR>Purpose/Description: <INPUT TYPE="text" NAME="purpose" VALUE="<% $purpose %>" SIZE=64>
+
+<P><INPUT TYPE="submit" VALUE="Submit">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+my($svcnum, $pkgnum, $svcpart, $kludge_action, $purpose, $part_svc,
+ $svc_domain);
+if ( $cgi->param('error') ) {
+
+ $svc_domain = new FS::svc_domain ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_domain')
+ } );
+ $svcnum = $svc_domain->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $kludge_action = $cgi->param('action');
+ $purpose = $cgi->param('purpose');
+ $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } );
+ die "No part_svc entry!" unless $part_svc;
+
+} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding
+
+ $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum';
+ $pkgnum = $1;
+ $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart';
+ $svcpart = $1;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ $svc_domain = new FS::svc_domain({});
+
+ $svcnum='';
+
+ $svc_domain->set_default_and_fixed;
+
+} else { #editing
+
+ $kludge_action = '';
+ $purpose = '';
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "unparsable svcnum";
+ $svcnum=$1;
+ $svc_domain=qsearchs('svc_domain',{'svcnum'=>$svcnum})
+ or die "Unknown (svc_domain) svcnum!";
+
+ my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum=$cust_svc->pkgnum;
+ $svcpart=$cust_svc->svcpart;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+}
+my $action = $svcnum ? 'Edit' : 'Add';
+
+my $svc = $part_svc->getfield('svc');
+
+my $otaker = getotaker;
+
+my $domain = $svc_domain->domain;
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/svc_external.cgi b/httemplate/edit/svc_external.cgi
new file mode 100644
index 0000000..0df842b
--- /dev/null
+++ b/httemplate/edit/svc_external.cgi
@@ -0,0 +1,102 @@
+<% include('/elements/header.html', "External service $action") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%$p1%>process/svc_external.cgi" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
+Service #<B><% $svcnum ? $svcnum : "(NEW)" %></B>
+<BR><BR>
+
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
+
+% my $id = $svc_external->id;
+% my $title = $svc_external->title;
+%
+<% &ntable("#cccccc",2) %>
+ <TR>
+ <TD ALIGN="right">External ID</TD>
+ <TD><INPUT TYPE="text" NAME="id" VALUE="<% $id %>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Title</TD>
+ <TD><INPUT TYPE="text" NAME="title" VALUE="<% $title %>"></TD>
+ </TR>
+
+% foreach my $field ($svc_external->virtual_fields) {
+% if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) {
+% # If the flag is X, it won't even show up in $svc_acct->virtual_fields.
+ <% $svc_external->pvf($field)->widget( 'HTML',
+ 'edit',
+ $svc_external->getfield($field)
+ )
+ %>
+% }
+% }
+
+</TABLE>
+<BR>
+
+<INPUT TYPE="submit" VALUE="Submit">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+my( $svcnum, $pkgnum, $svcpart, $part_svc, $svc_external );
+if ( $cgi->param('error') ) {
+
+ $svc_external = new FS::svc_external ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_external')
+ } );
+ $svcnum = $svc_external->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding
+
+ $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum';
+ $pkgnum = $1;
+ $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart';
+ $svcpart = $1;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ $svc_external = new FS::svc_external { svcpart => $svcpart };
+
+ $svcnum='';
+
+ $svc_external->set_default_and_fixed;
+
+} else { #adding
+
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "unparsable svcnum";
+ $svcnum=$1;
+ $svc_external=qsearchs('svc_external',{'svcnum'=>$svcnum})
+ or die "Unknown (svc_external) svcnum!";
+
+ my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum=$cust_svc->pkgnum;
+ $svcpart=$cust_svc->svcpart;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+}
+my $action = $svc_external->svcnum ? 'Edit' : 'Add';
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/svc_forward.cgi b/httemplate/edit/svc_forward.cgi
new file mode 100755
index 0000000..96a00a5
--- /dev/null
+++ b/httemplate/edit/svc_forward.cgi
@@ -0,0 +1,175 @@
+<% include('/elements/header.html', "Mail Forward $action") %>
+
+<% include('/elements/error.html') %>
+
+Service #<% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR>
+Service: <B><% $part_svc->svc %></B><BR><BR>
+
+<FORM ACTION="process/svc_forward.cgi" METHOD="POST">
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
+
+<SCRIPT TYPE="text/javascript">
+function srcchanged(what) {
+ if ( what.options[what.selectedIndex].value == 0 ) {
+ what.form.src.disabled = false;
+ what.form.src.style.backgroundColor = "white";
+ } else {
+ what.form.src.disabled = true;
+ what.form.src.style.backgroundColor = "lightgrey";
+ }
+}
+function dstchanged(what) {
+ if ( what.options[what.selectedIndex].value == 0 ) {
+ what.form.dst.disabled = false;
+ what.form.dst.style.backgroundColor = "white";
+ } else {
+ what.form.dst.disabled = true;
+ what.form.dst.style.backgroundColor = "lightgrey";
+ }
+}
+</SCRIPT>
+
+<% ntable("#cccccc",2) %>
+<TR><TD ALIGN="right">Email to</TD>
+<TD><SELECT NAME="srcsvc" SIZE=1 onChange="srcchanged(this)">
+% foreach $_ (keys %email) {
+
+ <OPTION<% $_ eq $srcsvc ? " SELECTED" : "" %> VALUE="<% $_ %>"><% $email{$_} %></OPTION>
+% }
+% if ( $svc_forward->dbdef_table->column('src') ) {
+
+ <OPTION <% $src ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION>
+% }
+
+</SELECT>
+% if ( $svc_forward->dbdef_table->column('src') ) {
+
+<INPUT TYPE="text" NAME="src" VALUE="<% $src %>" <% ( $src || !scalar(%email) ) ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>>
+% }
+
+</TD></TR>
+
+<TR><TD ALIGN="right">Forwards to</TD>
+<TD><SELECT NAME="dstsvc" SIZE=1 onChange="dstchanged(this)">
+% foreach $_ (keys %email) {
+
+ <OPTION<% $_ eq $dstsvc ? " SELECTED" : "" %> VALUE="<% $_ %>"><% $email{$_} %></OPTION>
+% }
+
+<OPTION <% $dst ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION>
+</SELECT>
+<INPUT TYPE="text" NAME="dst" VALUE="<% $dst %>" <% ( $dst || !scalar(%email) ) ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>>
+</TD></TR>
+ </TABLE>
+<BR><INPUT TYPE="submit" VALUE="Submit">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+my $conf = new FS::Conf;
+
+my($svcnum, $pkgnum, $svcpart, $part_svc, $svc_forward);
+if ( $cgi->param('error') ) {
+ $svc_forward = new FS::svc_forward ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_forward')
+ } );
+ $svcnum = $svc_forward->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding
+
+ $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum';
+ $pkgnum = $1;
+ $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart';
+ $svcpart = $1;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ $svc_forward = new FS::svc_forward({});
+
+ $svcnum='';
+
+ $svc_forward->set_default_and_fixed;
+
+} else { #editing
+
+ my($query) = $cgi->keywords;
+
+ $query =~ /^(\d+)$/ or die "unparsable svcnum";
+ $svcnum=$1;
+ $svc_forward=qsearchs('svc_forward',{'svcnum'=>$svcnum})
+ or die "Unknown (svc_forward) svcnum!";
+
+ my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum=$cust_svc->pkgnum;
+ $svcpart=$cust_svc->svcpart;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+}
+my $action = $svc_forward->svcnum ? 'Edit' : 'Add';
+
+my %email;
+
+#starting with those currently attached
+foreach my $method (qw( srcsvc_acct dstsvc_acct )) {
+ my $svc_acct = $svc_forward->$method();
+ $email{$svc_acct->svcnum} = $svc_acct->email if $svc_acct;
+}
+
+if ($pkgnum) {
+
+ #find all possible user svcnums (and emails)
+
+ #and including the rest for this customer
+ my($u_part_svc,@u_acct_svcparts);
+ foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) {
+ push @u_acct_svcparts,$u_part_svc->getfield('svcpart');
+ }
+
+ my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ my($custnum)=$cust_pkg->getfield('custnum');
+ my($i_cust_pkg);
+ foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
+ my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
+ my($acct_svcpart);
+ foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding
+ #record(s) in cust_svc ( for this
+ #pkgnum ! )
+ foreach my $i_cust_svc (
+ qsearch( 'cust_svc', { 'pkgnum' => $cust_pkgnum,
+ 'svcpart' => $acct_svcpart } )
+ ) {
+ my $svc_acct =
+ qsearchs( 'svc_acct', { 'svcnum' => $i_cust_svc->svcnum } );
+ $email{$svc_acct->svcnum} = $svc_acct->email;
+ }
+ }
+ }
+
+} elsif ( $action eq 'Add' ) {
+ die "\$action eq Add, but \$pkgnum is null!\n";
+}
+
+my($srcsvc,$dstsvc,$dst)=(
+ $svc_forward->srcsvc,
+ $svc_forward->dstsvc,
+ $svc_forward->dst,
+);
+my $src = $svc_forward->dbdef_table->column('src') ? $svc_forward->src : '';
+
+</%init>
diff --git a/httemplate/edit/svc_phone.cgi b/httemplate/edit/svc_phone.cgi
new file mode 100644
index 0000000..d7629ab
--- /dev/null
+++ b/httemplate/edit/svc_phone.cgi
@@ -0,0 +1,27 @@
+<% include( 'elements/svc_Common.html',
+ 'name' => 'Phone number',
+ 'table' => 'svc_phone',
+ 'fields' => [ 'countrycode',
+ { field => 'phonenum',
+ type => 'select-did',
+ label => 'Phone number',
+ },
+ 'sip_password',
+ 'pin',
+ 'phone_name',
+ ],
+ 'labels' => {
+ 'countrycode' => 'Country code',
+ 'phonenum' => 'Phone number',
+ 'sip_password' => 'SIP password',
+ 'pin' => 'Voicemail PIN',
+ 'phone_name' => 'Name',
+ },
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+</%init>
diff --git a/httemplate/edit/svc_www.cgi b/httemplate/edit/svc_www.cgi
new file mode 100644
index 0000000..eeb6f67
--- /dev/null
+++ b/httemplate/edit/svc_www.cgi
@@ -0,0 +1,240 @@
+<% include('/elements/header.html', "Web Hosting $action") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%$p1%>process/svc_www.cgi" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
+Service #<B><% $svcnum ? $svcnum : "(NEW)" %></B>
+<BR><BR>
+
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
+
+% my $recnum = $svc_www->recnum;
+% my $usersvc = $svc_www->usersvc;
+
+<% &ntable("#cccccc",2) %>
+
+ <TR>
+ <TD ALIGN="right">Zone</TD>
+ <TD>
+ <SELECT NAME="recnum" SIZE=1>
+% foreach $_ (keys %arec) {
+ <OPTION<% $_ eq $recnum ? " SELECTED" : "" %> VALUE="<%$_%>"><%$arec{$_}%>
+% }
+ </SELECT>
+ </TD>
+ </TR>
+
+% if ( $part_svc->part_svc_column('usersvc')->columnflag ne 'F'
+% || $part_svc->part_svc_column('usersvc')->columnvalue !~ /^\s*$/) {
+ <TR>
+ <TD ALIGN="right">Username</TD>
+ <TD>
+ <SELECT NAME="usersvc" SIZE=1>
+ <OPTION VALUE="">(none)
+% foreach $_ (keys %svc_acct) {
+ <OPTION<% ($_ eq $usersvc) ? " SELECTED" : "" %> VALUE="<%$_%>"><% $svc_acct{$_} %>
+% }
+ <SELECT>
+ </TD>
+ </TR>
+% }
+
+% if ( $part_svc->part_svc_column('config')->columnflag ne 'F' &&
+% $FS::CurrentUser::CurrentUser->access_right('Edit www config') ) {
+ <TR>
+ <TD ALIGN="right">Config lines</TD>
+ <TD>
+ <TEXTAREA NAME="config" rows="15" cols="80"><% $config |h %></TEXTAREA>
+ </TD>
+ </TR>
+% } else {
+ <INPUT TYPE="hidden" NAME="config" VALUE="<% $config |h %>">
+%}
+
+% foreach my $field ($svc_www->virtual_fields) {
+% if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) {
+% # If the flag is X, it won't even show up in $svc_acct->virtual_fields.
+ <% $svc_www->pvf($field)->widget( 'HTML', 'edit',
+ $svc_www->getfield($field)
+ )
+ %>
+% }
+% }
+
+</TABLE>
+<BR>
+
+<INPUT TYPE="submit" VALUE="Submit">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
+
+my $conf = new FS::Conf;
+
+my( $svcnum, $pkgnum, $svcpart, $part_svc, $svc_www, $config );
+
+if ( $cgi->param('error') ) {
+
+ $svc_www = new FS::svc_www ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_www')
+ } );
+ $svcnum = $svc_www->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $config = $cgi->param('config');
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding
+
+ $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum';
+ $pkgnum = $1;
+ $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart';
+ $svcpart = $1;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ $svc_www = new FS::svc_www { svcpart => $svcpart };
+
+ $svcnum='';
+
+ $svc_www->set_default_and_fixed;
+
+} else { #editing
+
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "unparsable svcnum";
+ $svcnum=$1;
+ $svc_www=qsearchs('svc_www',{'svcnum'=>$svcnum})
+ or die "Unknown (svc_www) svcnum!";
+
+ my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum = $cust_svc->pkgnum;
+ $svcpart = $cust_svc->svcpart;
+ $config = $svc_www->config;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+}
+my $action = $svc_www->svcnum ? 'Edit' : 'Add';
+
+my( %svc_acct, %arec );
+if ($pkgnum) {
+
+ my @u_acct_svcparts;
+ foreach my $svcpart (
+ map { $_->svcpart } qsearch( 'part_svc', { 'svcdb' => 'svc_acct' } )
+ ) {
+ next if $conf->exists('svc_www-usersvc_svcpart')
+ && ! grep { $svcpart == $_ }
+ $conf->config('svc_www-usersvc_svcpart');
+ push @u_acct_svcparts, $svcpart;
+ }
+
+ my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ my($custnum)=$cust_pkg->getfield('custnum');
+ my($i_cust_pkg);
+ foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
+ my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
+ my($acct_svcpart);
+ foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding
+ #record(s) in cust_svc ( for this
+ #pkgnum ! )
+ my($i_cust_svc);
+ foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) {
+ my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')});
+ $svc_acct{$svc_acct->getfield('svcnum')}=
+ $svc_acct->cust_svc->part_svc->svc. ': '. $svc_acct->email;
+ }
+ }
+ }
+
+
+ my($d_part_svc,@d_acct_svcparts);
+ foreach $d_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_domain'}) ) {
+ push @d_acct_svcparts,$d_part_svc->getfield('svcpart');
+ }
+
+ foreach $i_cust_pkg ( qsearch( 'cust_pkg', { 'custnum' => $custnum } ) ) {
+ my $cust_pkgnum = $i_cust_pkg->pkgnum;
+
+ foreach my $acct_svcpart (@d_acct_svcparts) {
+
+ foreach my $i_cust_svc (
+ qsearch( 'cust_svc', { 'pkgnum' => $cust_pkgnum,
+ 'svcpart' => $acct_svcpart } )
+ ) {
+ my $svc_domain =
+ qsearchs( 'svc_domain', { 'svcnum' => $i_cust_svc->svcnum } );
+
+ my $extra_sql = "AND ( rectype = 'A' OR rectype = 'CNAME' )";
+ unless ( $conf->exists('svc_www-enable_subdomains') ) {
+ $extra_sql .= " AND ( reczone = '\@' OR reczone = '".
+ $svc_domain->domain. ".' )";
+ }
+
+ foreach my $domain_rec (
+ qsearch( 'domain_record',
+ {
+ 'svcnum' => $svc_domain->svcnum,
+ },
+ '',
+ $extra_sql,
+ )
+ ) {
+ $arec{$domain_rec->recnum} = $domain_rec->zone;
+ }
+
+ if ( $conf->exists('svc_www-enable_subdomains') ) {
+ $arec{'www.'. $svc_domain->domain} = 'www.'. $svc_domain->domain
+ unless qsearchs( 'domain_record', {
+ svcnum => $svc_domain->svcnum,
+ reczone => 'www',
+ } )
+ || qsearchs( 'domain_record', {
+ svcnum => $svc_domain->svcnum,
+ reczone => 'www.'.$svc_domain->domain.'.',
+ } );
+ }
+
+ $arec{'@.'. $svc_domain->domain} = $svc_domain->domain
+ unless qsearchs('domain_record', {
+ svcnum => $svc_domain->svcnum,
+ reczone => '@',
+ } )
+ || qsearchs('domain_record', {
+ svcnum => $svc_domain->svcnum,
+ reczone => $svc_domain->domain.'.',
+ } );
+
+ }
+
+ }
+ }
+
+} elsif ( $action eq 'Edit' ) {
+
+ my($domain_rec) = qsearchs('domain_record', { 'recnum'=>$svc_www->recnum });
+ $arec{$svc_www->recnum} = join '.', $domain_rec->recdata, $domain_rec->reczone;
+
+} else {
+ die "\$action eq Add, but \$pkgnum is null!\n";
+}
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/tax_class.html b/httemplate/edit/tax_class.html
new file mode 100644
index 0000000..d3e2e82
--- /dev/null
+++ b/httemplate/edit/tax_class.html
@@ -0,0 +1,36 @@
+<% include('/elements/header.html', "$action taxclass") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p1 %>process/tax_class.html" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="taxclassnum" VALUE="">
+<INPUT TYPE="hidden" NAME="data_vendor" VALUE="">
+
+Tax class <INPUT TYPE="text" NAME="taxclass" VALUE="<% $taxclass |h %>"><BR>
+Description <INPUT TYPE="text" NAME="description" VALUE="<% $description |h %>">
+
+<BR><BR>
+<INPUT TYPE="submit" VALUE="<% $action %> taxclass">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $taxclass = '';
+my $description = '';
+if ( $cgi->param('error') ) {
+ $taxclass = $cgi->param('taxclass');
+ $description = $cgi->param('description');
+}
+
+my $action = 'Add';
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/tax_rate.html b/httemplate/edit/tax_rate.html
new file mode 100644
index 0000000..bff6999
--- /dev/null
+++ b/httemplate/edit/tax_rate.html
@@ -0,0 +1,106 @@
+<% include('elements/edit.html',
+ 'popup' => 1,
+ 'name' => 'Tax rate', #Edit tax rate
+ 'table' => 'tax_rate',
+ 'labels' => $labels,
+ 'fields' => \@fields,
+ 'value_callback' => $value_callback,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+my $value_callback =
+ sub { my ( $field, $value ) = @_;
+ ( $field =~ /^(tax|excessrate|usetax|useexcessrate)$/ )
+ ? $value*100
+ : $value;
+ };
+
+</%once>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $taxnum;
+if ( $cgi->param('error') ) {
+ $cgi->param('taxnum') =~ /^(\d+)$/ or die 'error, but no taxnum';
+ $taxnum = $1;
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die 'no taxnum';
+ $taxnum = $1;
+}
+
+my $tax_rate = qsearchs('tax_rate', { 'taxnum' => $taxnum })
+ or die "unknown taxnum $1";
+
+my $labels = { 'taxnum' => 'Tax',
+ 'data_vendor' => 'Data vendor',
+ 'geocode' => 'Vendor location code',
+ 'location' => 'Tax auth loc code',
+ 'taxclass_description' => 'Tax class',
+ 'taxname' => 'Tax name',
+ 'effective_date' => 'Effective date',
+ 'tax' => 'Tax rate (1st bracket)',
+ 'excessrate' => 'Tax rate (2nd bracket)',
+ 'taxbase' => 'First bracket',
+ 'taxmax' => 'Max tax',
+ 'usetax' => 'Use tax rate (1st bracket)',
+ 'useexcessrate' => 'Use tax rate (2nd bracket)',
+ 'unittype_name' => 'Units',
+ 'fee' => 'Fee per unit (1st bracket)',
+ 'excessfee' => 'Fee per unit (2st bracket)',
+ 'feebase' => 'Units in first bracket',
+ 'feemax' => 'Max Units',
+ 'maxtype_name' => 'Threshold accumulation',
+ 'taxauth_name', => 'Tax authority',
+ 'basetype_name' => 'Basis',
+ 'passtype_name' => 'Passthru',
+ 'passflag' => 'Passable',
+ 'setuptax' => 'This tax not applicable to setup fees',
+ 'recurtax' => 'This tax not applicable to recurring fees',
+ };
+
+my @fields = (
+ { type=>'tablebreak-tr-title', value=>'Location' },
+ { field=>'data_vendor', type=>'hidden',},
+ { field=>'geocode', type=>'fixed' },
+ { field=>'taxclassnum', type=>'hidden' } ,
+ { field=>'taxclass_description', type=>'fixed' } ,
+ { field=>'taxname', type=>'text' } ,
+ { field=>'effective_date', type=>'fixed' } ,
+ { field=>'location', type=>'text' },
+ { type=>'tablebreak-tr-title', value=>'Money based rates' },
+ { field=>'tax', type=>'percentage' } ,
+ { field=>'excessrate', type=>'percentage' } ,
+ { field=>'taxbase', type=>'money' } ,
+ { field=>'taxmax', type=>'money' } ,
+ { field=>'usetax', type=>'percentage' } ,
+ { field=>'useexcessrate', type=>'percentage' } ,
+ { type=>'tablebreak-tr-title', value=>'Service based rates' },
+ { field=>'unittype', type=>'hidden' } ,
+ { field=>'unittype_name', type=>'fixed' } ,
+ { field=>'fee', type=>'money' } ,
+ { field=>'excessfee', type=>'money' } ,
+ { field=>'feebase', type=>'text' } ,
+ { field=>'feemax', type=>'text' } ,
+ { type=>'tablebreak-tr-title', value=>'Taxation rules' },
+ { field=>'maxtype', type=>'hidden' } ,
+ { field=>'maxtype_name', type=>'fixed' } ,
+ { field=>'taxauth', type=>'hidden' } ,
+ { field=>'taxauth_name', type=>'fixed' } ,
+ { field=>'basetype', type=>'hidden' } ,
+ { field=>'basetype_name', type=>'fixed' } ,
+ { field=>'passtype', type=>'hidden' } ,
+ { field=>'passtype_name', type=>'fixed' } ,
+ { field=>'passflag', type=>'fixed' } ,
+ { field=>'setuptax', type=>'checkbox', value=>'Y' } ,
+ { field=>'recurtax', type=>'checkbox', value=>'Y' } ,
+ { field=>'disabled', type=>'checkbox', value=>'Y' } ,
+ { field=>'manual', type=>'hidden', value=>'Y' } ,
+);
+
+</%init>
diff --git a/httemplate/edit/usage_class.html b/httemplate/edit/usage_class.html
new file mode 100644
index 0000000..ef4b1ff
--- /dev/null
+++ b/httemplate/edit/usage_class.html
@@ -0,0 +1,25 @@
+<% include( 'elements/edit.html',
+ 'name_singular' => 'Usage Class',
+ 'table' => 'usage_class',
+ 'fields' => [
+ 'classname',
+ { field=>'disabled',
+ type=>'checkbox',
+ value=>'Y',
+ },
+ ],
+ 'labels' => {
+ 'classnum' => 'Class number',
+ 'classname' => 'Class name',
+ 'disabled' => 'Disable class',
+ },
+ 'viewall_dir' => 'browse',
+ )
+
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/elements/ajaxcontentmws.js b/httemplate/elements/ajaxcontentmws.js
new file mode 100644
index 0000000..9177049
--- /dev/null
+++ b/httemplate/elements/ajaxcontentmws.js
@@ -0,0 +1,185 @@
+/*
+ ajaxcontentmws.js - Foteos Macrides (author and Copyright holder)
+ Initial: June 22, 2006 - Last Revised: March 24, 2008
+ Wrapper function set for getting and using the responseText and / or
+ responseXML from a GET or POST XMLHttpRequest, which can be used to
+ generate dynamic content for overlib or overlib2 calls, or to modify
+ the content of a displayed STICKY popup dynamically.
+
+ For GET Use:
+ onmouseover="return OLgetAJAX(url, command, delay, css);"
+ onmouseout="OLclearAJAX();" (if delay > 0)
+ or:
+ onclick="OLgetAJAX(url, command, 0, css); return false;"
+ or:
+ onload="OLgetAJAX(url, command, 0, css);
+
+ Where:
+ url (required)
+ is a quoted string, or unquoted string variable name or array entry, with
+ the full, relative, or partial URL for a file or a server-side script (php,
+ asp, or cgi, e.g. perl), and may have a query string appended (e.g.,
+ 'http://my.domain.com/scripts/myScript.php?foo=bar&life=grand').
+ And:
+ command (required)
+ is the function reference (unquoted name without parens) of a function to
+ be called when the server's response has been received (it could instead be
+ an inline function, i.e., defined within the 2nd argument, or a quoted string
+ for a function with parens and any args)
+ And:
+ delay (may be omitted unless css is included)
+ is an unquoted number indicating the number of millisecs to wait before
+ initiating an XMLHttpRequest GET request. It should be 0 when using onclick
+ or onload, but may be a modest value such as 300 for onmouseover to avoid
+ any chatter of requests. When used with onmouseover, include:
+ onmouseout="OLclearAJAX();"
+ to clear the request if the user does not hover for at least that long. If
+ the popup is not STICKY, include an nd or nd2 call, e.g.,
+ onmouseout="OLclearAJAX(); nd();"
+ And:
+ css (may be omitted)
+ is a quoted string with the CSS class (e.g. 'ovfl510' for
+ .ovfl510 {width:510px; height:145px; overflow:auto; ...} ) for a div to
+ encase the responseText and set the width, height and scrollbars in the
+ main text area of the popup, or the unquoted number 0 if no encasing div
+ is to be used.
+
+ For POST substitute OLpostAJAX(url, qry, command, delay, css);
+ Where
+ qry (required)
+ is the string to be posted, typically a query string (without a lead ?)
+ and the other arguments are as above.
+
+ See http://www.macridesweb.com/oltest/AJAX.html for more information.
+*/
+
+// Initialize our global variables for this function set.
+var OLhttp=false,OLcommandAJAX=null,OLdelayidAJAX=0,OLclassAJAX='',
+OLresponseAJAX='',OLabortAJAX=0,OLdebugAJAX=0;
+
+// Create a series of wrapper functions (e.g. OLcmdT#() for ones which
+// use OLhttp.responseText via the OLresponseAJAX global, and OLcmdX#()
+// for ones which use OLhttp.responseXML) whose reference (unquoted name
+// without parens) is the 2nd argument in OLgetAJAX(url,command,delay,css)
+// calls. This one is for the first example in the AJAX.html support
+// document, to use the OLresponseAJAX global as the lead argument for an
+// overlib popup. Put your functions in the head, or in another imported
+// .js file, so that they will not be affected by updates of this .js file.
+//
+function OLcmdExT1() {
+ return overlib(OLresponseAJAX, TEXTPADDING,0, CAPTIONPADDING,4,
+ CAPTION,'Example with AJAX content via <span '
+ +'class="yellow">responseText</span>.&nbsp; Popup scrolls with the window.',
+ WRAP, BORDER,2, STICKY, CLOSECLICK, SCROLL,
+ MIDX,0, RELY,100,
+ STATUS,'Example with AJAX content via responseText of XMLHttpResponse');
+}
+
+// Alert for old browsers which lack XMLHttpRequest support.
+function OLsorryAJAX() {
+ alert('Sorry, AJAX is not supported by your browser.');
+ return false;
+}
+
+// Check 2nd arg for function
+function OLchkFuncAJAX(ar){
+ var t=typeof ar;return (((t=='function'))||((t=='string')&&(/.+\(.*\)/.test(ar))));
+}
+
+// Alert for bad 2nd argument
+function OLnotFuncAJAX(m) {
+ if(over)cClick();
+ alert('The 2nd arg of OL'+m+'AJAX is not a function reference, nor an inline function, '
+ +'nor a quoted string with a function indicated.');
+ return OLclearAJAX();
+}
+
+// Alert for indicating an XMLHttpRequest network error.
+function OLerrorAJAX() {
+ if(OLhttp.status&&OLhttp.status!=2147746065)alert('Network error '+OLhttp.status+'. Try again later.');
+ return false;
+}
+
+// Returns a new XMLHttpRequest object, or false for older browsers
+// which did not yet support it. Called as OLhttp=OLnewXMLHttp() via
+// the OLgetAJAX(url,command,delay,css) wrapper function.
+//
+function OLnewXMLHttp() {
+ var f=false,req=f;
+ if(window.XMLHttpRequest)eval(new Array('try{',
+ 'req=new XMLHttpRequest();','}catch(e){','req=f;','}').join('\n'));
+ /*@cc_on @if(@_jscript_version>=5)if(!req)
+ eval(new Array('try{','req=new ActiveXObject("Msxml2.XMLHTTP");',
+ '}catch(e){','try{','req=new ActiveXObject("Microsoft.XMLHTTP");',
+ '}catch(e){','req=f;','}}').join('\n')); @end @*/
+ return req;
+}
+
+// Handle the OLhttp.responseText string from the XMLHttpRequest object.
+function OLdoAJAX() {
+ if(OLhttp.readyState==4){
+ if(OLdebugAJAX)alert(
+ 'OLhttp.status = '+OLhttp.status+'\n'
+ +'OLhttp.statusText = '+OLhttp.statusText+'\n'
+ +'OLhttp.getAllResponseHeaders() = \n'
+ +OLhttp.getAllResponseHeaders()+'\n'
+ +'OLhttp.getResponseHeader("Content-Type") = '
+ +OLhttp.getResponseHeader("Content-Type")+'\n');
+ if(OLhttp.status==200||(OLhttp.status==0&&!OLabortAJAX&&!OLie55)){
+ OLresponseAJAX=OLclassAJAX?'<div class="'+OLclassAJAX+'">':'';
+ OLresponseAJAX += OLhttp.responseText;
+ OLresponseAJAX += OLclassAJAX?'</div>':'';
+ if(OLdebugAJAX)alert('OLresponseAJAX = \n'+OLresponseAJAX);
+ OLclassAJAX=0;
+ return (typeof OLcommandAJAX=='string')?eval(OLcommandAJAX):OLcommandAJAX();
+ }else{
+ OLclassAJAX=0;
+ OLabortAJAX=0;
+ return OLerrorAJAX();
+ }
+ }
+}
+
+// Actually make the request initiated via OLgetAJAX or OLpostAJAX, or
+// invoke a "permission denied" alert if a cross-domain URL was used.
+function OLsetAJAX(url,qry) {
+ if(window.location.protocol.indexOf('http')==0&&
+ (url.indexOf('file:')==0||url.indexOf('ftp:')==0)){
+ alert('[object Error]\n(Cross-domain access not permitted)');return false;}
+ qry=(qry||null);var s='',m=(qry)?'POST':'GET';OLabortAJAX=0;
+ OLdelayidAJAX=0;eval(new Array('try{','OLhttp.open(m,url,true);',
+ '}catch(e){','s=e','OLhttp=false;','}').join('\n'));if(!OLhttp){
+ alert(s+'\n(Cross-domain access not permitted)');return false;}if(qry)
+ OLhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
+ OLhttp.onreadystatechange=OLdoAJAX;
+ OLhttp.send(qry);
+}
+
+// Clear or abort any delayed OLsetAJAX call or pending request.
+function OLclearAJAX() {
+ if(OLdelayidAJAX){clearTimeout(OLdelayidAJAX);OLdelayidAJAX=0;}
+ if(OLhttp&&!OLdebugAJAX){OLabortAJAX=1;OLhttp.abort();}
+ return false;
+}
+
+// Load a new XMLHttpRequest object into the OLhttp global, load the
+// OLcommandAJAX and OLclassAJAX globals, and initiate a GET request
+// via OLsetAJAX(url) to populate OLhttp.
+function OLgetAJAX(url,command,delay,css) {
+ if(!OLchkFuncAJAX(command))return OLnotFuncAJAX('get');
+ OLclearAJAX();OLhttp=OLnewXMLHttp();if(!OLhttp)return OLsorryAJAX();
+ OLcommandAJAX=command;delay=(delay||0);css=(css||0);OLclassAJAX=css;
+ if(delay)OLdelayidAJAX=setTimeout("OLsetAJAX('"+url+"')",delay);
+ else OLsetAJAX(url);
+}
+
+// Load a new XMLHttpRequest object into the OLhttp global, load the
+// OLcommandAJAX and OLclassAJAX globals, and initiate a POST request
+// via OLsetAJAX(url,qry) to populate OLhttp.
+function OLpostAJAX(url,qry,command,delay,css) {
+ if(!OLchkFuncAJAX(command))return OLnotFuncAJAX('post');
+ OLclearAJAX();OLhttp=OLnewXMLHttp();if(!OLhttp)return OLsorryAJAX();
+ qry=(qry||0);OLcommandAJAX=command;delay=(delay||0);css=(css||0);OLclassAJAX=css;
+ if(delay)OLdelayidAJAX=setTimeout("OLsetAJAX('"+url+"','"+qry+"')",delay);
+ else OLsetAJAX(url,qry);
+}
diff --git a/httemplate/elements/calendar-en.js b/httemplate/elements/calendar-en.js
new file mode 100644
index 0000000..0dbde79
--- /dev/null
+++ b/httemplate/elements/calendar-en.js
@@ -0,0 +1,127 @@
+// ** I18N
+
+// Calendar EN language
+// Author: Mihai Bazon, <mihai_bazon@yahoo.com>
+// Encoding: any
+// Distributed under the same terms as the calendar itself.
+
+// For translators: please use UTF-8 if possible. We strongly believe that
+// Unicode is the answer to a real internationalized world. Also please
+// include your contact information in the header, as can be seen above.
+
+// full day names
+Calendar._DN = new Array
+("Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ "Sunday");
+
+// Please note that the following array of short day names (and the same goes
+// for short month names, _SMN) isn't absolutely necessary. We give it here
+// for exemplification on how one can customize the short day names, but if
+// they are simply the first N letters of the full name you can simply say:
+//
+// Calendar._SDN_len = N; // short day name length
+// Calendar._SMN_len = N; // short month name length
+//
+// If N = 3 then this is not needed either since we assume a value of 3 if not
+// present, to be compatible with translation files that were written before
+// this feature.
+
+// short day names
+Calendar._SDN = new Array
+("Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun");
+
+// First day of the week. "0" means display Sunday first, "1" means display
+// Monday first, etc.
+Calendar._FD = 0;
+
+// full month names
+Calendar._MN = new Array
+("January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December");
+
+// short month names
+Calendar._SMN = new Array
+("Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec");
+
+// tooltips
+Calendar._TT = {};
+Calendar._TT["INFO"] = "About the calendar";
+
+Calendar._TT["ABOUT"] =
+"DHTML Date/Time Selector\n" +
+"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-)
+"For latest version visit: http://www.dynarch.com/projects/calendar/\n" +
+"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." +
+"\n\n" +
+"Date selection:\n" +
+"- Use the \xab, \xbb buttons to select year\n" +
+"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
+"- Hold mouse button on any of the above buttons for faster selection.";
+Calendar._TT["ABOUT_TIME"] = "\n\n" +
+"Time selection:\n" +
+"- Click on any of the time parts to increase it\n" +
+"- or Shift-click to decrease it\n" +
+"- or click and drag for faster selection.";
+
+Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)";
+Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)";
+Calendar._TT["GO_TODAY"] = "Go Today";
+Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)";
+Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)";
+Calendar._TT["SEL_DATE"] = "Select date";
+Calendar._TT["DRAG_TO_MOVE"] = "Drag to move";
+Calendar._TT["PART_TODAY"] = " (today)";
+
+// the following is to inform that "%s" is to be the first day of week
+// %s will be replaced with the day name.
+Calendar._TT["DAY_FIRST"] = "Display %s first";
+
+// This may be locale-dependent. It specifies the week-end days, as an array
+// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1
+// means Monday, etc.
+Calendar._TT["WEEKEND"] = "0,6";
+
+Calendar._TT["CLOSE"] = "Close";
+Calendar._TT["TODAY"] = "Today";
+Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value";
+
+// date formats
+Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d";
+Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";
+
+Calendar._TT["WK"] = "wk";
+Calendar._TT["TIME"] = "Time:";
diff --git a/httemplate/elements/calendar-setup.js b/httemplate/elements/calendar-setup.js
new file mode 100644
index 0000000..b27d9be
--- /dev/null
+++ b/httemplate/elements/calendar-setup.js
@@ -0,0 +1,200 @@
+/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
+ * ---------------------------------------------------------------------------
+ *
+ * The DHTML Calendar
+ *
+ * Details and latest version at:
+ * http://dynarch.com/mishoo/calendar.epl
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ *
+ * This file defines helper functions for setting up the calendar. They are
+ * intended to help non-programmers get a working calendar on their site
+ * quickly. This script should not be seen as part of the calendar. It just
+ * shows you what one can do with the calendar, while in the same time
+ * providing a quick and simple method for setting it up. If you need
+ * exhaustive customization of the calendar creation process feel free to
+ * modify this code to suit your needs (this is recommended and much better
+ * than modifying calendar.js itself).
+ */
+
+// $Id: calendar-setup.js,v 1.5 2006-02-09 07:18:08 ivan Exp $
+
+/**
+ * This function "patches" an input field (or other element) to use a calendar
+ * widget for date selection.
+ *
+ * The "params" is a single object that can have the following properties:
+ *
+ * prop. name | description
+ * -------------------------------------------------------------------------------------------------
+ * inputField | the ID of an input field to store the date
+ * displayArea | the ID of a DIV or other element to show the date
+ * button | ID of a button or other element that will trigger the calendar
+ * eventName | event that will trigger the calendar, without the "on" prefix (default: "click")
+ * ifFormat | date format that will be stored in the input field
+ * daFormat | the date format that will be used to display the date in displayArea
+ * singleClick | (true/false) wether the calendar is in single click mode or not (default: true)
+ * firstDay | numeric: 0 to 6. "0" means display Sunday first, "1" means display Monday first, etc.
+ * align | alignment (default: "Br"); if you don't know what's this see the calendar documentation
+ * range | array with 2 elements. Default: [1900, 2999] -- the range of years available
+ * weekNumbers | (true/false) if it's true (default) the calendar will display week numbers
+ * flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID
+ * flatCallback | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar)
+ * disableFunc | function that receives a JS Date object and should return true if that date has to be disabled in the calendar
+ * onSelect | function that gets called when a date is selected. You don't _have_ to supply this (the default is generally okay)
+ * onClose | function that gets called when the calendar is closed. [default]
+ * onUpdate | function that gets called after the date is updated in the input field. Receives a reference to the calendar.
+ * date | the date that the calendar will be initially displayed to
+ * showsTime | default: false; if true the calendar will include a time selector
+ * timeFormat | the time format; can be "12" or "24", default is "12"
+ * electric | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close
+ * step | configures the step of the years in drop-down boxes; default: 2
+ * position | configures the calendar absolute position; default: null
+ * cache | if "true" (but default: "false") it will reuse the same calendar object, where possible
+ * showOthers | if "true" (but default: "false") it will show days from other months too
+ *
+ * None of them is required, they all have default values. However, if you
+ * pass none of "inputField", "displayArea" or "button" you'll get a warning
+ * saying "nothing to setup".
+ */
+Calendar.setup = function (params) {
+ function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };
+
+ param_default("inputField", null);
+ param_default("displayArea", null);
+ param_default("button", null);
+ param_default("eventName", "click");
+ param_default("ifFormat", "%Y/%m/%d");
+ param_default("daFormat", "%Y/%m/%d");
+ param_default("singleClick", true);
+ param_default("disableFunc", null);
+ param_default("dateStatusFunc", params["disableFunc"]); // takes precedence if both are defined
+ param_default("dateText", null);
+ param_default("firstDay", null);
+ param_default("align", "Br");
+ param_default("range", [1900, 2999]);
+ param_default("weekNumbers", true);
+ param_default("flat", null);
+ param_default("flatCallback", null);
+ param_default("onSelect", null);
+ param_default("onClose", null);
+ param_default("onUpdate", null);
+ param_default("date", null);
+ param_default("showsTime", false);
+ param_default("timeFormat", "24");
+ param_default("electric", true);
+ param_default("step", 2);
+ param_default("position", null);
+ param_default("cache", false);
+ param_default("showOthers", false);
+ param_default("multiple", null);
+
+ var tmp = ["inputField", "displayArea", "button"];
+ for (var i in tmp) {
+ if (typeof params[tmp[i]] == "string") {
+ params[tmp[i]] = document.getElementById(params[tmp[i]]);
+ }
+ }
+ if (!(params.flat || params.multiple || params.inputField || params.displayArea || params.button)) {
+ alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code");
+ return false;
+ }
+
+ function onSelect(cal) {
+ var p = cal.params;
+ var update = (cal.dateClicked || p.electric);
+ if (update && p.inputField) {
+ p.inputField.value = cal.date.print(p.ifFormat);
+ if (typeof p.inputField.onchange == "function")
+ p.inputField.onchange();
+ }
+ if (update && p.displayArea)
+ p.displayArea.innerHTML = cal.date.print(p.daFormat);
+ if (update && typeof p.onUpdate == "function")
+ p.onUpdate(cal);
+ if (update && p.flat) {
+ if (typeof p.flatCallback == "function")
+ p.flatCallback(cal);
+ }
+ if (update && p.singleClick && cal.dateClicked)
+ cal.callCloseHandler();
+ };
+
+ if (params.flat != null) {
+ if (typeof params.flat == "string")
+ params.flat = document.getElementById(params.flat);
+ if (!params.flat) {
+ alert("Calendar.setup:\n Flat specified but can't find parent.");
+ return false;
+ }
+ var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect);
+ cal.showsOtherMonths = params.showOthers;
+ cal.showsTime = params.showsTime;
+ cal.time24 = (params.timeFormat == "24");
+ cal.params = params;
+ cal.weekNumbers = params.weekNumbers;
+ cal.setRange(params.range[0], params.range[1]);
+ cal.setDateStatusHandler(params.dateStatusFunc);
+ cal.getDateText = params.dateText;
+ if (params.ifFormat) {
+ cal.setDateFormat(params.ifFormat);
+ }
+ if (params.inputField && typeof params.inputField.value == "string") {
+ cal.parseDate(params.inputField.value);
+ }
+ cal.create(params.flat);
+ cal.show();
+ return false;
+ }
+
+ var triggerEl = params.button || params.displayArea || params.inputField;
+ triggerEl["on" + params.eventName] = function() {
+ var dateEl = params.inputField || params.displayArea;
+ var dateFmt = params.inputField ? params.ifFormat : params.daFormat;
+ var mustCreate = false;
+ var cal = window.calendar;
+ if (dateEl)
+ params.date = Date.parseDate(dateEl.value || dateEl.innerHTML, dateFmt);
+ if (!(cal && params.cache)) {
+ window.calendar = cal = new Calendar(params.firstDay,
+ params.date,
+ params.onSelect || onSelect,
+ params.onClose || function(cal) { cal.hide(); });
+ cal.showsTime = params.showsTime;
+ cal.time24 = (params.timeFormat == "24");
+ cal.weekNumbers = params.weekNumbers;
+ mustCreate = true;
+ } else {
+ if (params.date)
+ cal.setDate(params.date);
+ cal.hide();
+ }
+ if (params.multiple) {
+ cal.multiple = {};
+ for (var i = params.multiple.length; --i >= 0;) {
+ var d = params.multiple[i];
+ var ds = d.print("%Y%m%d");
+ cal.multiple[ds] = d;
+ }
+ }
+ cal.showsOtherMonths = params.showOthers;
+ cal.yearStep = params.step;
+ cal.setRange(params.range[0], params.range[1]);
+ cal.params = params;
+ cal.setDateStatusHandler(params.dateStatusFunc);
+ cal.getDateText = params.dateText;
+ cal.setDateFormat(dateFmt);
+ if (mustCreate)
+ cal.create();
+ cal.refresh();
+ if (!params.position)
+ cal.showAtElement(params.button || params.displayArea || params.inputField, params.align);
+ else
+ cal.showAt(params.position[0], params.position[1]);
+ return false;
+ };
+
+ return cal;
+};
diff --git a/httemplate/elements/calendar-win2k-2.css b/httemplate/elements/calendar-win2k-2.css
new file mode 100644
index 0000000..6f37b7d
--- /dev/null
+++ b/httemplate/elements/calendar-win2k-2.css
@@ -0,0 +1,271 @@
+/* The main calendar widget. DIV containing a table. */
+
+.calendar {
+ position: relative;
+ display: none;
+ border-top: 2px solid #fff;
+ border-right: 2px solid #000;
+ border-bottom: 2px solid #000;
+ border-left: 2px solid #fff;
+ font-size: 11px;
+ color: #000;
+ cursor: default;
+ background: #d4c8d0;
+ font-family: tahoma,verdana,sans-serif;
+}
+
+.calendar table {
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+ font-size: 11px;
+ color: #000;
+ cursor: default;
+ background: #d4c8d0;
+ font-family: tahoma,verdana,sans-serif;
+}
+
+/* Header part -- contains navigation buttons and day names. */
+
+.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */
+ text-align: center;
+ padding: 1px;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+}
+
+.calendar .nav {
+ background: transparent url(menuarrow.gif) no-repeat 100% 100%;
+}
+
+.calendar thead .title { /* This holds the current "month, year" */
+ font-weight: bold;
+ padding: 1px;
+ border: 1px solid #000;
+ background: #847880;
+ color: #fff;
+ text-align: center;
+}
+
+.calendar thead .headrow { /* Row <TR> containing navigation buttons */
+}
+
+.calendar thead .daynames { /* Row <TR> containing the day names */
+}
+
+.calendar thead .name { /* Cells <TD> containing the day names */
+ border-bottom: 1px solid #000;
+ padding: 2px;
+ text-align: center;
+ background: #f4e8f0;
+}
+
+.calendar thead .weekend { /* How a weekend day name shows in header */
+ color: #f00;
+}
+
+.calendar thead .hilite { /* How do the buttons in header appear when hover */
+ border-top: 2px solid #fff;
+ border-right: 2px solid #000;
+ border-bottom: 2px solid #000;
+ border-left: 2px solid #fff;
+ padding: 0px;
+ background-color: #e4d8e0;
+}
+
+.calendar thead .active { /* Active (pressed) buttons in header */
+ padding: 2px 0px 0px 2px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+ background-color: #c4b8c0;
+}
+
+/* The body part -- contains all the days in month. */
+
+.calendar tbody .day { /* Cells <TD> containing month days dates */
+ width: 2em;
+ text-align: right;
+ padding: 2px 4px 2px 2px;
+}
+.calendar tbody .day.othermonth {
+ font-size: 80%;
+ color: #aaa;
+}
+.calendar tbody .day.othermonth.oweekend {
+ color: #faa;
+}
+
+.calendar table .wn {
+ padding: 2px 3px 2px 2px;
+ border-right: 1px solid #000;
+ background: #f4e8f0;
+}
+
+.calendar tbody .rowhilite td {
+ background: #e4d8e0;
+}
+
+.calendar tbody .rowhilite td.wn {
+ background: #d4c8d0;
+}
+
+.calendar tbody td.hilite { /* Hovered cells <TD> */
+ padding: 1px 3px 1px 1px;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+}
+
+.calendar tbody td.active { /* Active (pressed) cells <TD> */
+ padding: 2px 2px 0px 2px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+.calendar tbody td.selected { /* Cell showing selected date */
+ font-weight: bold;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+ padding: 2px 2px 0px 2px;
+ background: #e4d8e0;
+}
+
+.calendar tbody td.weekend { /* Cells showing weekend days */
+ color: #f00;
+}
+
+.calendar tbody td.today { /* Cell showing today date */
+ font-weight: bold;
+ color: #00f;
+}
+
+.calendar tbody .disabled { color: #999; }
+
+.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */
+ visibility: hidden;
+}
+
+.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */
+ display: none;
+}
+
+/* The footer part -- status bar and "Close" button */
+
+.calendar tfoot .footrow { /* The <TR> in footer (only one right now) */
+}
+
+.calendar tfoot .ttip { /* Tooltip (status bar) cell <TD> */
+ background: #f4e8f0;
+ padding: 1px;
+ border: 1px solid #000;
+ background: #847880;
+ color: #fff;
+ text-align: center;
+}
+
+.calendar tfoot .hilite { /* Hover style for buttons in footer */
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+ padding: 1px;
+ background: #e4d8e0;
+}
+
+.calendar tfoot .active { /* Active (pressed) style for buttons in footer */
+ padding: 2px 0px 0px 2px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+/* Combo boxes (menus that display months/years for direct selection) */
+
+.calendar .combo {
+ position: absolute;
+ display: none;
+ width: 4em;
+ top: 0px;
+ left: 0px;
+ cursor: default;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+ background: #e4d8e0;
+ font-size: 90%;
+ padding: 1px;
+ z-index: 100;
+}
+
+.calendar .combo .label,
+.calendar .combo .label-IEfix {
+ text-align: center;
+ padding: 1px;
+}
+
+.calendar .combo .label-IEfix {
+ width: 4em;
+}
+
+.calendar .combo .active {
+ background: #d4c8d0;
+ padding: 0px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+.calendar .combo .hilite {
+ background: #408;
+ color: #fea;
+}
+
+.calendar td.time {
+ border-top: 1px solid #000;
+ padding: 1px 0px;
+ text-align: center;
+ background-color: #f4f0e8;
+}
+
+.calendar td.time .hour,
+.calendar td.time .minute,
+.calendar td.time .ampm {
+ padding: 0px 3px 0px 4px;
+ border: 1px solid #889;
+ font-weight: bold;
+ background-color: #fff;
+}
+
+.calendar td.time .ampm {
+ text-align: center;
+}
+
+.calendar td.time .colon {
+ padding: 0px 2px 0px 3px;
+ font-weight: bold;
+}
+
+.calendar td.time span.hilite {
+ border-color: #000;
+ background-color: #766;
+ color: #fff;
+}
+
+.calendar td.time span.active {
+ border-color: #f00;
+ background-color: #000;
+ color: #0f0;
+}
diff --git a/httemplate/elements/calendar.js b/httemplate/elements/calendar.js
new file mode 100644
index 0000000..f5c74f6
--- /dev/null
+++ b/httemplate/elements/calendar.js
@@ -0,0 +1,1806 @@
+/* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo
+ * -----------------------------------------------------------
+ *
+ * The DHTML Calendar, version 1.0 "It is happening again"
+ *
+ * Details and latest version at:
+ * www.dynarch.com/projects/calendar
+ *
+ * This script is developed by Dynarch.com. Visit us at www.dynarch.com.
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ */
+
+// $Id: calendar.js,v 1.5 2006-02-09 07:18:08 ivan Exp $
+
+/** The Calendar object constructor. */
+Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
+ // member variables
+ this.activeDiv = null;
+ this.currentDateEl = null;
+ this.getDateStatus = null;
+ this.getDateToolTip = null;
+ this.getDateText = null;
+ this.timeout = null;
+ this.onSelected = onSelected || null;
+ this.onClose = onClose || null;
+ this.dragging = false;
+ this.hidden = false;
+ this.minYear = 1970;
+ this.maxYear = 2050;
+ this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
+ this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
+ this.isPopup = true;
+ this.weekNumbers = true;
+ this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
+ this.showsOtherMonths = false;
+ this.dateStr = dateStr;
+ this.ar_days = null;
+ this.showsTime = false;
+ this.time24 = true;
+ this.yearStep = 2;
+ this.hiliteToday = true;
+ this.multiple = null;
+ // HTML elements
+ this.table = null;
+ this.element = null;
+ this.tbody = null;
+ this.firstdayname = null;
+ // Combo boxes
+ this.monthsCombo = null;
+ this.yearsCombo = null;
+ this.hilitedMonth = null;
+ this.activeMonth = null;
+ this.hilitedYear = null;
+ this.activeYear = null;
+ // Information
+ this.dateClicked = false;
+
+ // one-time initializations
+ if (typeof Calendar._SDN == "undefined") {
+ // table of short day names
+ if (typeof Calendar._SDN_len == "undefined")
+ Calendar._SDN_len = 3;
+ var ar = new Array();
+ for (var i = 8; i > 0;) {
+ ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
+ }
+ Calendar._SDN = ar;
+ // table of short month names
+ if (typeof Calendar._SMN_len == "undefined")
+ Calendar._SMN_len = 3;
+ ar = new Array();
+ for (var i = 12; i > 0;) {
+ ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
+ }
+ Calendar._SMN = ar;
+ }
+};
+
+// ** constants
+
+/// "static", needed for event handlers.
+Calendar._C = null;
+
+/// detect a special case of "web browser"
+Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
+ !/opera/i.test(navigator.userAgent) );
+
+Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
+
+/// detect Opera browser
+Calendar.is_opera = /opera/i.test(navigator.userAgent);
+
+/// detect KHTML-based browsers
+Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
+
+// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
+// library, at some point.
+
+Calendar.getAbsolutePos = function(el) {
+ var SL = 0, ST = 0;
+ var is_div = /^div$/i.test(el.tagName);
+ if (is_div && el.scrollLeft)
+ SL = el.scrollLeft;
+ if (is_div && el.scrollTop)
+ ST = el.scrollTop;
+ var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
+ if (el.offsetParent) {
+ var tmp = this.getAbsolutePos(el.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+};
+
+Calendar.isRelated = function (el, evt) {
+ var related = evt.relatedTarget;
+ if (!related) {
+ var type = evt.type;
+ if (type == "mouseover") {
+ related = evt.fromElement;
+ } else if (type == "mouseout") {
+ related = evt.toElement;
+ }
+ }
+ while (related) {
+ if (related == el) {
+ return true;
+ }
+ related = related.parentNode;
+ }
+ return false;
+};
+
+Calendar.removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+Calendar.addClass = function(el, className) {
+ Calendar.removeClass(el, className);
+ el.className += " " + className;
+};
+
+// FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
+Calendar.getElement = function(ev) {
+ var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
+ while (f.nodeType != 1 || /^div$/i.test(f.tagName))
+ f = f.parentNode;
+ return f;
+};
+
+Calendar.getTargetElement = function(ev) {
+ var f = Calendar.is_ie ? window.event.srcElement : ev.target;
+ while (f.nodeType != 1)
+ f = f.parentNode;
+ return f;
+};
+
+Calendar.stopEvent = function(ev) {
+ ev || (ev = window.event);
+ if (Calendar.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ return false;
+};
+
+Calendar.addEvent = function(el, evname, func) {
+ if (el.attachEvent) { // IE
+ el.attachEvent("on" + evname, func);
+ } else if (el.addEventListener) { // Gecko / W3C
+ el.addEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = func;
+ }
+};
+
+Calendar.removeEvent = function(el, evname, func) {
+ if (el.detachEvent) { // IE
+ el.detachEvent("on" + evname, func);
+ } else if (el.removeEventListener) { // Gecko / W3C
+ el.removeEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = null;
+ }
+};
+
+Calendar.createElement = function(type, parent) {
+ var el = null;
+ if (document.createElementNS) {
+ // use the XHTML namespace; IE won't normally get here unless
+ // _they_ "fix" the DOM2 implementation.
+ el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
+ } else {
+ el = document.createElement(type);
+ }
+ if (typeof parent != "undefined") {
+ parent.appendChild(el);
+ }
+ return el;
+};
+
+// END: UTILITY FUNCTIONS
+
+// BEGIN: CALENDAR STATIC FUNCTIONS
+
+/** Internal -- adds a set of events to make some element behave like a button. */
+Calendar._add_evs = function(el) {
+ with (Calendar) {
+ addEvent(el, "mouseover", dayMouseOver);
+ addEvent(el, "mousedown", dayMouseDown);
+ addEvent(el, "mouseout", dayMouseOut);
+ if (is_ie) {
+ addEvent(el, "dblclick", dayMouseDblClick);
+ el.setAttribute("unselectable", true);
+ }
+ }
+};
+
+Calendar.findMonth = function(el) {
+ if (typeof el.month != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.month != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.findYear = function(el) {
+ if (typeof el.year != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.year != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.showMonthsCombo = function () {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var mc = cal.monthsCombo;
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ if (cal.activeMonth) {
+ Calendar.removeClass(cal.activeMonth, "active");
+ }
+ var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
+ Calendar.addClass(mon, "active");
+ cal.activeMonth = mon;
+ var s = mc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else {
+ var mcw = mc.offsetWidth;
+ if (typeof mcw == "undefined")
+ // Konqueror brain-dead techniques
+ mcw = 50;
+ s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
+ }
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+};
+
+Calendar.showYearsCombo = function (fwd) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var yc = cal.yearsCombo;
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ if (cal.activeYear) {
+ Calendar.removeClass(cal.activeYear, "active");
+ }
+ cal.activeYear = null;
+ var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
+ var yr = yc.firstChild;
+ var show = false;
+ for (var i = 12; i > 0; --i) {
+ if (Y >= cal.minYear && Y <= cal.maxYear) {
+ yr.innerHTML = Y;
+ yr.year = Y;
+ yr.style.display = "block";
+ show = true;
+ } else {
+ yr.style.display = "none";
+ }
+ yr = yr.nextSibling;
+ Y += fwd ? cal.yearStep : -cal.yearStep;
+ }
+ if (show) {
+ var s = yc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else {
+ var ycw = yc.offsetWidth;
+ if (typeof ycw == "undefined")
+ // Konqueror brain-dead techniques
+ ycw = 50;
+ s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
+ }
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+ }
+};
+
+// event handlers
+
+Calendar.tableMouseUp = function(ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ if (cal.timeout) {
+ clearTimeout(cal.timeout);
+ }
+ var el = cal.activeDiv;
+ if (!el) {
+ return false;
+ }
+ var target = Calendar.getTargetElement(ev);
+ ev || (ev = window.event);
+ Calendar.removeClass(el, "active");
+ if (target == el || target.parentNode == el) {
+ Calendar.cellClick(el, ev);
+ }
+ var mon = Calendar.findMonth(target);
+ var date = null;
+ if (mon) {
+ date = new Date(cal.date);
+ if (mon.month != date.getMonth()) {
+ date.setMonth(mon.month);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ } else {
+ var year = Calendar.findYear(target);
+ if (year) {
+ date = new Date(cal.date);
+ if (year.year != date.getFullYear()) {
+ date.setFullYear(year.year);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ }
+ }
+ with (Calendar) {
+ removeEvent(document, "mouseup", tableMouseUp);
+ removeEvent(document, "mouseover", tableMouseOver);
+ removeEvent(document, "mousemove", tableMouseOver);
+ cal._hideCombos();
+ _C = null;
+ return stopEvent(ev);
+ }
+};
+
+Calendar.tableMouseOver = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return;
+ }
+ var el = cal.activeDiv;
+ var target = Calendar.getTargetElement(ev);
+ if (target == el || target.parentNode == el) {
+ Calendar.addClass(el, "hilite active");
+ Calendar.addClass(el.parentNode, "rowhilite");
+ } else {
+ if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
+ Calendar.removeClass(el, "active");
+ Calendar.removeClass(el, "hilite");
+ Calendar.removeClass(el.parentNode, "rowhilite");
+ }
+ ev || (ev = window.event);
+ if (el.navtype == 50 && target != el) {
+ var pos = Calendar.getAbsolutePos(el);
+ var w = el.offsetWidth;
+ var x = ev.clientX;
+ var dx;
+ var decrease = true;
+ if (x > pos.x + w) {
+ dx = x - pos.x - w;
+ decrease = false;
+ } else
+ dx = pos.x - x;
+
+ if (dx < 0) dx = 0;
+ var range = el._range;
+ var current = el._current;
+ var count = Math.floor(dx / 10) % range.length;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ while (count-- > 0)
+ if (decrease) {
+ if (--i < 0)
+ i = range.length - 1;
+ } else if ( ++i >= range.length )
+ i = 0;
+ var newval = range[i];
+ el.innerHTML = newval;
+
+ cal.onUpdateTime();
+ }
+ var mon = Calendar.findMonth(target);
+ if (mon) {
+ if (mon.month != cal.date.getMonth()) {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ Calendar.addClass(mon, "hilite");
+ cal.hilitedMonth = mon;
+ } else if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ } else {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ var year = Calendar.findYear(target);
+ if (year) {
+ if (year.year != cal.date.getFullYear()) {
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ Calendar.addClass(year, "hilite");
+ cal.hilitedYear = year;
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.tableMouseDown = function (ev) {
+ if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
+ return Calendar.stopEvent(ev);
+ }
+};
+
+Calendar.calDragIt = function (ev) {
+ var cal = Calendar._C;
+ if (!(cal && cal.dragging)) {
+ return false;
+ }
+ var posX;
+ var posY;
+ if (Calendar.is_ie) {
+ posY = window.event.clientY + document.body.scrollTop;
+ posX = window.event.clientX + document.body.scrollLeft;
+ } else {
+ posX = ev.pageX;
+ posY = ev.pageY;
+ }
+ cal.hideShowCovered();
+ var st = cal.element.style;
+ st.left = (posX - cal.xOffs) + "px";
+ st.top = (posY - cal.yOffs) + "px";
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.calDragEnd = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ cal.dragging = false;
+ with (Calendar) {
+ removeEvent(document, "mousemove", calDragIt);
+ removeEvent(document, "mouseup", calDragEnd);
+ tableMouseUp(ev);
+ }
+ cal.hideShowCovered();
+};
+
+Calendar.dayMouseDown = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (el.disabled) {
+ return false;
+ }
+ var cal = el.calendar;
+ cal.activeDiv = el;
+ Calendar._C = cal;
+ if (el.navtype != 300) with (Calendar) {
+ if (el.navtype == 50) {
+ el._current = el.innerHTML;
+ addEvent(document, "mousemove", tableMouseOver);
+ } else
+ addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
+ addClass(el, "hilite active");
+ addEvent(document, "mouseup", tableMouseUp);
+ } else if (cal.isPopup) {
+ cal._dragStart(ev);
+ }
+ if (el.navtype == -1 || el.navtype == 1) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
+ } else if (el.navtype == -2 || el.navtype == 2) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
+ } else {
+ cal.timeout = null;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseDblClick = function(ev) {
+ Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
+ if (Calendar.is_ie) {
+ document.selection.empty();
+ }
+};
+
+Calendar.dayMouseOver = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
+ return false;
+ }
+ if (el.ttip) {
+ if (el.ttip.substr(0, 1) == "_") {
+ el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
+ }
+ el.calendar.tooltips.innerHTML = el.ttip;
+ }
+ if (el.navtype != 300) {
+ Calendar.addClass(el, "hilite");
+ if (el.caldate) {
+ Calendar.addClass(el.parentNode, "rowhilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseOut = function(ev) {
+ with (Calendar) {
+ var el = getElement(ev);
+ if (isRelated(el, ev) || _C || el.disabled)
+ return false;
+ removeClass(el, "hilite");
+ if (el.caldate)
+ removeClass(el.parentNode, "rowhilite");
+ if (el.calendar)
+ el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
+ return stopEvent(ev);
+ }
+};
+
+/**
+ * A generic "click" handler :) handles all types of buttons defined in this
+ * calendar.
+ */
+Calendar.cellClick = function(el, ev) {
+ var cal = el.calendar;
+ var closing = false;
+ var newdate = false;
+ var date = null;
+ if (typeof el.navtype == "undefined") {
+ if (cal.currentDateEl) {
+ Calendar.removeClass(cal.currentDateEl, "selected");
+ Calendar.addClass(el, "selected");
+ closing = (cal.currentDateEl == el);
+ if (!closing) {
+ cal.currentDateEl = el;
+ }
+ }
+ cal.date.setDateOnly(el.caldate);
+ date = cal.date;
+ var other_month = !(cal.dateClicked = !el.otherMonth);
+ if (!other_month && !cal.currentDateEl)
+ cal._toggleMultipleDate(new Date(date));
+ else
+ newdate = !el.disabled;
+ // a date was clicked
+ if (other_month)
+ cal._init(cal.firstDayOfWeek, date);
+ } else {
+ if (el.navtype == 200) {
+ Calendar.removeClass(el, "hilite");
+ cal.callCloseHandler();
+ return;
+ }
+ date = new Date(cal.date);
+ if (el.navtype == 0)
+ date.setDateOnly(new Date()); // TODAY
+ // unless "today" was clicked, we assume no date was clicked so
+ // the selected handler will know not to close the calenar when
+ // in single-click mode.
+ // cal.dateClicked = (el.navtype == 0);
+ cal.dateClicked = false;
+ var year = date.getFullYear();
+ var mon = date.getMonth();
+ function setMonth(m) {
+ var day = date.getDate();
+ var max = date.getMonthDays(m);
+ if (day > max) {
+ date.setDate(max);
+ }
+ date.setMonth(m);
+ };
+ switch (el.navtype) {
+ case 400:
+ Calendar.removeClass(el, "hilite");
+ var text = Calendar._TT["ABOUT"];
+ if (typeof text != "undefined") {
+ text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
+ } else {
+ // FIXME: this should be removed as soon as lang files get updated!
+ text = "Help and about box text is not translated into this language.\n" +
+ "If you know this language and you feel generous please update\n" +
+ "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
+ "and send it back to <mihai_bazon@yahoo.com> to get it into the distribution ;-)\n\n" +
+ "Thank you!\n" +
+ "http://dynarch.com/mishoo/calendar.epl\n";
+ }
+ alert(text);
+ return;
+ case -2:
+ if (year > cal.minYear) {
+ date.setFullYear(year - 1);
+ }
+ break;
+ case -1:
+ if (mon > 0) {
+ setMonth(mon - 1);
+ } else if (year-- > cal.minYear) {
+ date.setFullYear(year);
+ setMonth(11);
+ }
+ break;
+ case 1:
+ if (mon < 11) {
+ setMonth(mon + 1);
+ } else if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ setMonth(0);
+ }
+ break;
+ case 2:
+ if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ }
+ break;
+ case 100:
+ cal.setFirstDayOfWeek(el.fdow);
+ return;
+ case 50:
+ var range = el._range;
+ var current = el.innerHTML;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ if (ev && ev.shiftKey) {
+ if (--i < 0)
+ i = range.length - 1;
+ } else if ( ++i >= range.length )
+ i = 0;
+ var newval = range[i];
+ el.innerHTML = newval;
+ cal.onUpdateTime();
+ return;
+ case 0:
+ // TODAY will bring us here
+ if ((typeof cal.getDateStatus == "function") &&
+ cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
+ return false;
+ }
+ break;
+ }
+ if (!date.equalsTo(cal.date)) {
+ cal.setDate(date);
+ newdate = true;
+ } else if (el.navtype == 0)
+ newdate = closing = true;
+ }
+ if (newdate) {
+ ev && cal.callHandler();
+ }
+ if (closing) {
+ Calendar.removeClass(el, "hilite");
+ ev && cal.callCloseHandler();
+ }
+};
+
+// END: CALENDAR STATIC FUNCTIONS
+
+// BEGIN: CALENDAR OBJECT FUNCTIONS
+
+/**
+ * This function creates the calendar inside the given parent. If _par is
+ * null than it creates a popup calendar inside the BODY element. If _par is
+ * an element, be it BODY, then it creates a non-popup calendar (still
+ * hidden). Some properties need to be set before calling this function.
+ */
+Calendar.prototype.create = function (_par) {
+ var parent = null;
+ if (! _par) {
+ // default parent is the document body, in which case we create
+ // a popup calendar.
+ parent = document.getElementsByTagName("body")[0];
+ this.isPopup = true;
+ } else {
+ parent = _par;
+ this.isPopup = false;
+ }
+ this.date = this.dateStr ? new Date(this.dateStr) : new Date();
+
+ var table = Calendar.createElement("table");
+ this.table = table;
+ table.cellSpacing = 0;
+ table.cellPadding = 0;
+ table.calendar = this;
+ Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
+
+ var div = Calendar.createElement("div");
+ this.element = div;
+ div.className = "calendar";
+ if (this.isPopup) {
+ div.style.position = "absolute";
+ div.style.display = "none";
+ }
+ div.appendChild(table);
+
+ var thead = Calendar.createElement("thead", table);
+ var cell = null;
+ var row = null;
+
+ var cal = this;
+ var hh = function (text, cs, navtype) {
+ cell = Calendar.createElement("td", row);
+ cell.colSpan = cs;
+ cell.className = "button";
+ if (navtype != 0 && Math.abs(navtype) <= 2)
+ cell.className += " nav";
+ Calendar._add_evs(cell);
+ cell.calendar = cal;
+ cell.navtype = navtype;
+ cell.innerHTML = "<div unselectable='on'>" + text + "</div>";
+ return cell;
+ };
+
+ row = Calendar.createElement("tr", thead);
+ var title_length = 6;
+ (this.isPopup) && --title_length;
+ (this.weekNumbers) && ++title_length;
+
+ hh("?", 1, 400).ttip = Calendar._TT["INFO"];
+ this.title = hh("", title_length, 300);
+ this.title.className = "title";
+ if (this.isPopup) {
+ this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ this.title.style.cursor = "move";
+ hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
+ }
+
+ row = Calendar.createElement("tr", thead);
+ row.className = "headrow";
+
+ this._nav_py = hh("&#x00ab;", 1, -2);
+ this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
+
+ this._nav_pm = hh("&#x2039;", 1, -1);
+ this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
+
+ this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
+ this._nav_now.ttip = Calendar._TT["GO_TODAY"];
+
+ this._nav_nm = hh("&#x203a;", 1, 1);
+ this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
+
+ this._nav_ny = hh("&#x00bb;", 1, 2);
+ this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
+
+ // day names
+ row = Calendar.createElement("tr", thead);
+ row.className = "daynames";
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ cell.className = "name wn";
+ cell.innerHTML = Calendar._TT["WK"];
+ }
+ for (var i = 7; i > 0; --i) {
+ cell = Calendar.createElement("td", row);
+ if (!i) {
+ cell.navtype = 100;
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+ this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
+ this._displayWeekdays();
+
+ var tbody = Calendar.createElement("tbody", table);
+ this.tbody = tbody;
+
+ for (i = 6; i > 0; --i) {
+ row = Calendar.createElement("tr", tbody);
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ }
+ for (var j = 7; j > 0; --j) {
+ cell = Calendar.createElement("td", row);
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+
+ if (this.showsTime) {
+ row = Calendar.createElement("tr", tbody);
+ row.className = "time";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = this.weekNumbers ? 4 : 3;
+
+ (function(){
+ function makeTimePart(className, init, range_start, range_end) {
+ var part = Calendar.createElement("span", cell);
+ part.className = className;
+ part.innerHTML = init;
+ part.calendar = cal;
+ part.ttip = Calendar._TT["TIME_PART"];
+ part.navtype = 50;
+ part._range = [];
+ if (typeof range_start != "number")
+ part._range = range_start;
+ else {
+ for (var i = range_start; i <= range_end; ++i) {
+ var txt;
+ if (i < 10 && range_end >= 10) txt = '0' + i;
+ else txt = '' + i;
+ part._range[part._range.length] = txt;
+ }
+ }
+ Calendar._add_evs(part);
+ return part;
+ };
+ var hrs = cal.date.getHours();
+ var mins = cal.date.getMinutes();
+ var t12 = !cal.time24;
+ var pm = (hrs > 12);
+ if (t12 && pm) hrs -= 12;
+ var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
+ var span = Calendar.createElement("span", cell);
+ span.innerHTML = ":";
+ span.className = "colon";
+ var M = makeTimePart("minute", mins, 0, 59);
+ var AP = null;
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ if (t12)
+ AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
+ else
+ cell.innerHTML = "&nbsp;";
+
+ cal.onSetTime = function() {
+ var pm, hrs = this.date.getHours(),
+ mins = this.date.getMinutes();
+ if (t12) {
+ pm = (hrs >= 12);
+ if (pm) hrs -= 12;
+ if (hrs == 0) hrs = 12;
+ AP.innerHTML = pm ? "pm" : "am";
+ }
+ H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
+ M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
+ };
+
+ cal.onUpdateTime = function() {
+ var date = this.date;
+ var h = parseInt(H.innerHTML, 10);
+ if (t12) {
+ if (/pm/i.test(AP.innerHTML) && h < 12)
+ h += 12;
+ else if (/am/i.test(AP.innerHTML) && h == 12)
+ h = 0;
+ }
+ var d = date.getDate();
+ var m = date.getMonth();
+ var y = date.getFullYear();
+ date.setHours(h);
+ date.setMinutes(parseInt(M.innerHTML, 10));
+ date.setFullYear(y);
+ date.setMonth(m);
+ date.setDate(d);
+ this.dateClicked = false;
+ this.callHandler();
+ };
+ })();
+ } else {
+ this.onSetTime = this.onUpdateTime = function() {};
+ }
+
+ var tfoot = Calendar.createElement("tfoot", table);
+
+ row = Calendar.createElement("tr", tfoot);
+ row.className = "footrow";
+
+ cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
+ cell.className = "ttip";
+ if (this.isPopup) {
+ cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ cell.style.cursor = "move";
+ }
+ this.tooltips = cell;
+
+ div = Calendar.createElement("div", this.element);
+ this.monthsCombo = div;
+ div.className = "combo";
+ for (i = 0; i < Calendar._MN.length; ++i) {
+ var mn = Calendar.createElement("div");
+ mn.className = Calendar.is_ie ? "label-IEfix" : "label";
+ mn.month = i;
+ mn.innerHTML = Calendar._SMN[i];
+ div.appendChild(mn);
+ }
+
+ div = Calendar.createElement("div", this.element);
+ this.yearsCombo = div;
+ div.className = "combo";
+ for (i = 12; i > 0; --i) {
+ var yr = Calendar.createElement("div");
+ yr.className = Calendar.is_ie ? "label-IEfix" : "label";
+ div.appendChild(yr);
+ }
+
+ this._init(this.firstDayOfWeek, this.date);
+ parent.appendChild(this.element);
+};
+
+/** keyboard navigation, only for popup calendars */
+Calendar._keyEvent = function(ev) {
+ var cal = window._dynarch_popupCalendar;
+ if (!cal || cal.multiple)
+ return false;
+ (Calendar.is_ie) && (ev = window.event);
+ var act = (Calendar.is_ie || ev.type == "keypress"),
+ K = ev.keyCode;
+ if (ev.ctrlKey) {
+ switch (K) {
+ case 37: // KEY left
+ act && Calendar.cellClick(cal._nav_pm);
+ break;
+ case 38: // KEY up
+ act && Calendar.cellClick(cal._nav_py);
+ break;
+ case 39: // KEY right
+ act && Calendar.cellClick(cal._nav_nm);
+ break;
+ case 40: // KEY down
+ act && Calendar.cellClick(cal._nav_ny);
+ break;
+ default:
+ return false;
+ }
+ } else switch (K) {
+ case 32: // KEY space (now)
+ Calendar.cellClick(cal._nav_now);
+ break;
+ case 27: // KEY esc
+ act && cal.callCloseHandler();
+ break;
+ case 37: // KEY left
+ case 38: // KEY up
+ case 39: // KEY right
+ case 40: // KEY down
+ if (act) {
+ var prev, x, y, ne, el, step;
+ prev = K == 37 || K == 38;
+ step = (K == 37 || K == 39) ? 1 : 7;
+ function setVars() {
+ el = cal.currentDateEl;
+ var p = el.pos;
+ x = p & 15;
+ y = p >> 4;
+ ne = cal.ar_days[y][x];
+ };setVars();
+ function prevMonth() {
+ var date = new Date(cal.date);
+ date.setDate(date.getDate() - step);
+ cal.setDate(date);
+ };
+ function nextMonth() {
+ var date = new Date(cal.date);
+ date.setDate(date.getDate() + step);
+ cal.setDate(date);
+ };
+ while (1) {
+ switch (K) {
+ case 37: // KEY left
+ if (--x >= 0)
+ ne = cal.ar_days[y][x];
+ else {
+ x = 6;
+ K = 38;
+ continue;
+ }
+ break;
+ case 38: // KEY up
+ if (--y >= 0)
+ ne = cal.ar_days[y][x];
+ else {
+ prevMonth();
+ setVars();
+ }
+ break;
+ case 39: // KEY right
+ if (++x < 7)
+ ne = cal.ar_days[y][x];
+ else {
+ x = 0;
+ K = 40;
+ continue;
+ }
+ break;
+ case 40: // KEY down
+ if (++y < cal.ar_days.length)
+ ne = cal.ar_days[y][x];
+ else {
+ nextMonth();
+ setVars();
+ }
+ break;
+ }
+ break;
+ }
+ if (ne) {
+ if (!ne.disabled)
+ Calendar.cellClick(ne);
+ else if (prev)
+ prevMonth();
+ else
+ nextMonth();
+ }
+ }
+ break;
+ case 13: // KEY enter
+ if (act)
+ Calendar.cellClick(cal.currentDateEl, ev);
+ break;
+ default:
+ return false;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+/**
+ * (RE)Initializes the calendar to the given date and firstDayOfWeek
+ */
+Calendar.prototype._init = function (firstDayOfWeek, date) {
+ var today = new Date(),
+ TY = today.getFullYear(),
+ TM = today.getMonth(),
+ TD = today.getDate();
+ this.table.style.visibility = "hidden";
+ var year = date.getFullYear();
+ if (year < this.minYear) {
+ year = this.minYear;
+ date.setFullYear(year);
+ } else if (year > this.maxYear) {
+ year = this.maxYear;
+ date.setFullYear(year);
+ }
+ this.firstDayOfWeek = firstDayOfWeek;
+ this.date = new Date(date);
+ var month = date.getMonth();
+ var mday = date.getDate();
+ var no_days = date.getMonthDays();
+
+ // calendar voodoo for computing the first day that would actually be
+ // displayed in the calendar, even if it's from the previous month.
+ // WARNING: this is magic. ;-)
+ date.setDate(1);
+ var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
+ if (day1 < 0)
+ day1 += 7;
+ date.setDate(-day1);
+ date.setDate(date.getDate() + 1);
+
+ var row = this.tbody.firstChild;
+ var MN = Calendar._SMN[month];
+ var ar_days = this.ar_days = new Array();
+ var weekend = Calendar._TT["WEEKEND"];
+ var dates = this.multiple ? (this.datesCells = {}) : null;
+ for (var i = 0; i < 6; ++i, row = row.nextSibling) {
+ var cell = row.firstChild;
+ if (this.weekNumbers) {
+ cell.className = "day wn";
+ cell.innerHTML = date.getWeekNumber();
+ cell = cell.nextSibling;
+ }
+ row.className = "daysrow";
+ var hasdays = false, iday, dpos = ar_days[i] = [];
+ for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {
+ iday = date.getDate();
+ var wday = date.getDay();
+ cell.className = "day";
+ cell.pos = i << 4 | j;
+ dpos[j] = cell;
+ var current_month = (date.getMonth() == month);
+ if (!current_month) {
+ if (this.showsOtherMonths) {
+ cell.className += " othermonth";
+ cell.otherMonth = true;
+ } else {
+ cell.className = "emptycell";
+ cell.innerHTML = "&nbsp;";
+ cell.disabled = true;
+ continue;
+ }
+ } else {
+ cell.otherMonth = false;
+ hasdays = true;
+ }
+ cell.disabled = false;
+ cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;
+ if (dates)
+ dates[date.print("%Y%m%d")] = cell;
+ if (this.getDateStatus) {
+ var status = this.getDateStatus(date, year, month, iday);
+ if (this.getDateToolTip) {
+ var toolTip = this.getDateToolTip(date, year, month, iday);
+ if (toolTip)
+ cell.title = toolTip;
+ }
+ if (status === true) {
+ cell.className += " disabled";
+ cell.disabled = true;
+ } else {
+ if (/disabled/i.test(status))
+ cell.disabled = true;
+ cell.className += " " + status;
+ }
+ }
+ if (!cell.disabled) {
+ cell.caldate = new Date(date);
+ cell.ttip = "_";
+ if (!this.multiple && current_month
+ && iday == mday && this.hiliteToday) {
+ cell.className += " selected";
+ this.currentDateEl = cell;
+ }
+ if (date.getFullYear() == TY &&
+ date.getMonth() == TM &&
+ iday == TD) {
+ cell.className += " today";
+ cell.ttip += Calendar._TT["PART_TODAY"];
+ }
+ if (weekend.indexOf(wday.toString()) != -1)
+ cell.className += cell.otherMonth ? " oweekend" : " weekend";
+ }
+ }
+ if (!(hasdays || this.showsOtherMonths))
+ row.className = "emptyrow";
+ }
+ this.title.innerHTML = Calendar._MN[month] + ", " + year;
+ this.onSetTime();
+ this.table.style.visibility = "visible";
+ this._initMultipleDates();
+ // PROFILE
+ // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";
+};
+
+Calendar.prototype._initMultipleDates = function() {
+ if (this.multiple) {
+ for (var i in this.multiple) {
+ var cell = this.datesCells[i];
+ var d = this.multiple[i];
+ if (!d)
+ continue;
+ if (cell)
+ cell.className += " selected";
+ }
+ }
+};
+
+Calendar.prototype._toggleMultipleDate = function(date) {
+ if (this.multiple) {
+ var ds = date.print("%Y%m%d");
+ var cell = this.datesCells[ds];
+ if (cell) {
+ var d = this.multiple[ds];
+ if (!d) {
+ Calendar.addClass(cell, "selected");
+ this.multiple[ds] = date;
+ } else {
+ Calendar.removeClass(cell, "selected");
+ delete this.multiple[ds];
+ }
+ }
+ }
+};
+
+Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
+ this.getDateToolTip = unaryFunction;
+};
+
+/**
+ * Calls _init function above for going to a certain date (but only if the
+ * date is different than the currently selected one).
+ */
+Calendar.prototype.setDate = function (date) {
+ if (!date.equalsTo(this.date)) {
+ this._init(this.firstDayOfWeek, date);
+ }
+};
+
+/**
+ * Refreshes the calendar. Useful if the "disabledHandler" function is
+ * dynamic, meaning that the list of disabled date can change at runtime.
+ * Just * call this function if you think that the list of disabled dates
+ * should * change.
+ */
+Calendar.prototype.refresh = function () {
+ this._init(this.firstDayOfWeek, this.date);
+};
+
+/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
+Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
+ this._init(firstDayOfWeek, this.date);
+ this._displayWeekdays();
+};
+
+/**
+ * Allows customization of what dates are enabled. The "unaryFunction"
+ * parameter must be a function object that receives the date (as a JS Date
+ * object) and returns a boolean value. If the returned value is true then
+ * the passed date will be marked as disabled.
+ */
+Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
+ this.getDateStatus = unaryFunction;
+};
+
+/** Customization of allowed year range for the calendar. */
+Calendar.prototype.setRange = function (a, z) {
+ this.minYear = a;
+ this.maxYear = z;
+};
+
+/** Calls the first user handler (selectedHandler). */
+Calendar.prototype.callHandler = function () {
+ if (this.onSelected) {
+ this.onSelected(this, this.date.print(this.dateFormat));
+ }
+};
+
+/** Calls the second user handler (closeHandler). */
+Calendar.prototype.callCloseHandler = function () {
+ if (this.onClose) {
+ this.onClose(this);
+ }
+ this.hideShowCovered();
+};
+
+/** Removes the calendar object from the DOM tree and destroys it. */
+Calendar.prototype.destroy = function () {
+ var el = this.element.parentNode;
+ el.removeChild(this.element);
+ Calendar._C = null;
+ window._dynarch_popupCalendar = null;
+};
+
+/**
+ * Moves the calendar element to a different section in the DOM tree (changes
+ * its parent).
+ */
+Calendar.prototype.reparent = function (new_parent) {
+ var el = this.element;
+ el.parentNode.removeChild(el);
+ new_parent.appendChild(el);
+};
+
+// This gets called when the user presses a mouse button anywhere in the
+// document, if the calendar is shown. If the click was outside the open
+// calendar this function closes it.
+Calendar._checkCalendar = function(ev) {
+ var calendar = window._dynarch_popupCalendar;
+ if (!calendar) {
+ return false;
+ }
+ var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
+ for (; el != null && el != calendar.element; el = el.parentNode);
+ if (el == null) {
+ // calls closeHandler which should hide the calendar.
+ window._dynarch_popupCalendar.callCloseHandler();
+ return Calendar.stopEvent(ev);
+ }
+};
+
+/** Shows the calendar. */
+Calendar.prototype.show = function () {
+ var rows = this.table.getElementsByTagName("tr");
+ for (var i = rows.length; i > 0;) {
+ var row = rows[--i];
+ Calendar.removeClass(row, "rowhilite");
+ var cells = row.getElementsByTagName("td");
+ for (var j = cells.length; j > 0;) {
+ var cell = cells[--j];
+ Calendar.removeClass(cell, "hilite");
+ Calendar.removeClass(cell, "active");
+ }
+ }
+ this.element.style.display = "block";
+ this.hidden = false;
+ if (this.isPopup) {
+ window._dynarch_popupCalendar = this;
+ Calendar.addEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.addEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.hideShowCovered();
+};
+
+/**
+ * Hides the calendar. Also removes any "hilite" from the class of any TD
+ * element.
+ */
+Calendar.prototype.hide = function () {
+ if (this.isPopup) {
+ Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.element.style.display = "none";
+ this.hidden = true;
+ this.hideShowCovered();
+};
+
+/**
+ * Shows the calendar at a given absolute position (beware that, depending on
+ * the calendar element style -- position property -- this might be relative
+ * to the parent's containing rectangle).
+ */
+Calendar.prototype.showAt = function (x, y) {
+ var s = this.element.style;
+ s.left = x + "px";
+ s.top = y + "px";
+ this.show();
+};
+
+/** Shows the calendar near a given element. */
+Calendar.prototype.showAtElement = function (el, opts) {
+ var self = this;
+ var p = Calendar.getAbsolutePos(el);
+ if (!opts || typeof opts != "string") {
+ this.showAt(p.x, p.y + el.offsetHeight);
+ return true;
+ }
+ function fixPosition(box) {
+ if (box.x < 0)
+ box.x = 0;
+ if (box.y < 0)
+ box.y = 0;
+ var cp = document.createElement("div");
+ var s = cp.style;
+ s.position = "absolute";
+ s.right = s.bottom = s.width = s.height = "0px";
+ document.body.appendChild(cp);
+ var br = Calendar.getAbsolutePos(cp);
+ document.body.removeChild(cp);
+ if (Calendar.is_ie) {
+ br.y += document.body.scrollTop;
+ br.x += document.body.scrollLeft;
+ } else {
+ br.y += window.scrollY;
+ br.x += window.scrollX;
+ }
+ var tmp = box.x + box.width - br.x;
+ if (tmp > 0) box.x -= tmp;
+ tmp = box.y + box.height - br.y;
+ if (tmp > 0) box.y -= tmp;
+ };
+ this.element.style.display = "block";
+ Calendar.continuation_for_the_fucking_khtml_browser = function() {
+ var w = self.element.offsetWidth;
+ var h = self.element.offsetHeight;
+ self.element.style.display = "none";
+ var valign = opts.substr(0, 1);
+ var halign = "l";
+ if (opts.length > 1) {
+ halign = opts.substr(1, 1);
+ }
+ // vertical alignment
+ switch (valign) {
+ case "T": p.y -= h; break;
+ case "B": p.y += el.offsetHeight; break;
+ case "C": p.y += (el.offsetHeight - h) / 2; break;
+ case "t": p.y += el.offsetHeight - h; break;
+ case "b": break; // already there
+ }
+ // horizontal alignment
+ switch (halign) {
+ case "L": p.x -= w; break;
+ case "R": p.x += el.offsetWidth; break;
+ case "C": p.x += (el.offsetWidth - w) / 2; break;
+ case "l": p.x += el.offsetWidth - w; break;
+ case "r": break; // already there
+ }
+ p.width = w;
+ p.height = h + 40;
+ self.monthsCombo.style.display = "none";
+ fixPosition(p);
+ self.showAt(p.x, p.y);
+ };
+ if (Calendar.is_khtml)
+ setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
+ else
+ Calendar.continuation_for_the_fucking_khtml_browser();
+};
+
+/** Customizes the date format. */
+Calendar.prototype.setDateFormat = function (str) {
+ this.dateFormat = str;
+};
+
+/** Customizes the tooltip date format. */
+Calendar.prototype.setTtDateFormat = function (str) {
+ this.ttDateFormat = str;
+};
+
+/**
+ * Tries to identify the date represented in a string. If successful it also
+ * calls this.setDate which moves the calendar to the given date.
+ */
+Calendar.prototype.parseDate = function(str, fmt) {
+ if (!fmt)
+ fmt = this.dateFormat;
+ this.setDate(Date.parseDate(str, fmt));
+};
+
+Calendar.prototype.hideShowCovered = function () {
+ if (!Calendar.is_ie && !Calendar.is_opera)
+ return;
+ function getVisib(obj){
+ var value = obj.style.visibility;
+ if (!value) {
+ if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
+ if (!Calendar.is_khtml)
+ value = document.defaultView.
+ getComputedStyle(obj, "").getPropertyValue("visibility");
+ else
+ value = '';
+ } else if (obj.currentStyle) { // IE
+ value = obj.currentStyle.visibility;
+ } else
+ value = '';
+ }
+ return value;
+ };
+
+ var tags = new Array("applet", "iframe", "select");
+ var el = this.element;
+
+ var p = Calendar.getAbsolutePos(el);
+ var EX1 = p.x;
+ var EX2 = el.offsetWidth + EX1;
+ var EY1 = p.y;
+ var EY2 = el.offsetHeight + EY1;
+
+ for (var k = tags.length; k > 0; ) {
+ var ar = document.getElementsByTagName(tags[--k]);
+ var cc = null;
+
+ for (var i = ar.length; i > 0;) {
+ cc = ar[--i];
+
+ p = Calendar.getAbsolutePos(cc);
+ var CX1 = p.x;
+ var CX2 = cc.offsetWidth + CX1;
+ var CY1 = p.y;
+ var CY2 = cc.offsetHeight + CY1;
+
+ if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
+ if (!cc.__msh_save_visibility) {
+ cc.__msh_save_visibility = getVisib(cc);
+ }
+ cc.style.visibility = cc.__msh_save_visibility;
+ } else {
+ if (!cc.__msh_save_visibility) {
+ cc.__msh_save_visibility = getVisib(cc);
+ }
+ cc.style.visibility = "hidden";
+ }
+ }
+ }
+};
+
+/** Internal function; it displays the bar with the names of the weekday. */
+Calendar.prototype._displayWeekdays = function () {
+ var fdow = this.firstDayOfWeek;
+ var cell = this.firstdayname;
+ var weekend = Calendar._TT["WEEKEND"];
+ for (var i = 0; i < 7; ++i) {
+ cell.className = "day name";
+ var realday = (i + fdow) % 7;
+ if (i) {
+ cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
+ cell.navtype = 100;
+ cell.calendar = this;
+ cell.fdow = realday;
+ Calendar._add_evs(cell);
+ }
+ if (weekend.indexOf(realday.toString()) != -1) {
+ Calendar.addClass(cell, "weekend");
+ }
+ cell.innerHTML = Calendar._SDN[(i + fdow) % 7];
+ cell = cell.nextSibling;
+ }
+};
+
+/** Internal function. Hides all combo boxes that might be displayed. */
+Calendar.prototype._hideCombos = function () {
+ this.monthsCombo.style.display = "none";
+ this.yearsCombo.style.display = "none";
+};
+
+/** Internal function. Starts dragging the element. */
+Calendar.prototype._dragStart = function (ev) {
+ if (this.dragging) {
+ return;
+ }
+ this.dragging = true;
+ var posX;
+ var posY;
+ if (Calendar.is_ie) {
+ posY = window.event.clientY + document.body.scrollTop;
+ posX = window.event.clientX + document.body.scrollLeft;
+ } else {
+ posY = ev.clientY + window.scrollY;
+ posX = ev.clientX + window.scrollX;
+ }
+ var st = this.element.style;
+ this.xOffs = posX - parseInt(st.left);
+ this.yOffs = posY - parseInt(st.top);
+ with (Calendar) {
+ addEvent(document, "mousemove", calDragIt);
+ addEvent(document, "mouseup", calDragEnd);
+ }
+};
+
+// BEGIN: DATE OBJECT PATCHES
+
+/** Adds the number of days array to the Date object. */
+Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
+
+/** Constants used for time computations */
+Date.SECOND = 1000 /* milliseconds */;
+Date.MINUTE = 60 * Date.SECOND;
+Date.HOUR = 60 * Date.MINUTE;
+Date.DAY = 24 * Date.HOUR;
+Date.WEEK = 7 * Date.DAY;
+
+Date.parseDate = function(str, fmt) {
+ var today = new Date();
+ var y = 0;
+ var m = -1;
+ var d = 0;
+ var a = str.split(/\W+/);
+ var b = fmt.match(/%./g);
+ var i = 0, j = 0;
+ var hr = 0;
+ var min = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (!a[i])
+ continue;
+ switch (b[i]) {
+ case "%d":
+ case "%e":
+ d = parseInt(a[i], 10);
+ break;
+
+ case "%m":
+ m = parseInt(a[i], 10) - 1;
+ break;
+
+ case "%Y":
+ case "%y":
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ break;
+
+ case "%b":
+ case "%B":
+ for (j = 0; j < 12; ++j) {
+ if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
+ }
+ break;
+
+ case "%H":
+ case "%I":
+ case "%k":
+ case "%l":
+ hr = parseInt(a[i], 10);
+ break;
+
+ case "%P":
+ case "%p":
+ if (/pm/i.test(a[i]) && hr < 12)
+ hr += 12;
+ else if (/am/i.test(a[i]) && hr >= 12)
+ hr -= 12;
+ break;
+
+ case "%M":
+ min = parseInt(a[i], 10);
+ break;
+ }
+ }
+ if (isNaN(y)) y = today.getFullYear();
+ if (isNaN(m)) m = today.getMonth();
+ if (isNaN(d)) d = today.getDate();
+ if (isNaN(hr)) hr = today.getHours();
+ if (isNaN(min)) min = today.getMinutes();
+ if (y != 0 && m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, 0);
+ y = 0; m = -1; d = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (a[i].search(/[a-zA-Z]+/) != -1) {
+ var t = -1;
+ for (j = 0; j < 12; ++j) {
+ if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
+ }
+ if (t != -1) {
+ if (m != -1) {
+ d = m+1;
+ }
+ m = t;
+ }
+ } else if (parseInt(a[i], 10) <= 12 && m == -1) {
+ m = a[i]-1;
+ } else if (parseInt(a[i], 10) > 31 && y == 0) {
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ } else if (d == 0) {
+ d = a[i];
+ }
+ }
+ if (y == 0)
+ y = today.getFullYear();
+ if (m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, 0);
+ return today;
+};
+
+/** Returns the number of days in the current month */
+Date.prototype.getMonthDays = function(month) {
+ var year = this.getFullYear();
+ if (typeof month == "undefined") {
+ month = this.getMonth();
+ }
+ if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
+ return 29;
+ } else {
+ return Date._MD[month];
+ }
+};
+
+/** Returns the number of day in the year. */
+Date.prototype.getDayOfYear = function() {
+ var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
+ var time = now - then;
+ return Math.floor(time / Date.DAY);
+};
+
+/** Returns the number of the week in year, as defined in ISO 8601. */
+Date.prototype.getWeekNumber = function() {
+ var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var DoW = d.getDay();
+ d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
+ var ms = d.valueOf(); // GMT
+ d.setMonth(0);
+ d.setDate(4); // Thu in Week 1
+ return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
+};
+
+/** Checks date and time equality */
+Date.prototype.equalsTo = function(date) {
+ return ((this.getFullYear() == date.getFullYear()) &&
+ (this.getMonth() == date.getMonth()) &&
+ (this.getDate() == date.getDate()) &&
+ (this.getHours() == date.getHours()) &&
+ (this.getMinutes() == date.getMinutes()));
+};
+
+/** Set only the year, month, date parts (keep existing time) */
+Date.prototype.setDateOnly = function(date) {
+ var tmp = new Date(date);
+ this.setDate(1);
+ this.setFullYear(tmp.getFullYear());
+ this.setMonth(tmp.getMonth());
+ this.setDate(tmp.getDate());
+};
+
+/** Prints the date in a string according to the given format. */
+Date.prototype.print = function (str) {
+ var m = this.getMonth();
+ var d = this.getDate();
+ var y = this.getFullYear();
+ var wn = this.getWeekNumber();
+ var w = this.getDay();
+ var s = {};
+ var hr = this.getHours();
+ var pm = (hr >= 12);
+ var ir = (pm) ? (hr - 12) : hr;
+ var dy = this.getDayOfYear();
+ if (ir == 0)
+ ir = 12;
+ var min = this.getMinutes();
+ var sec = this.getSeconds();
+ s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
+ s["%A"] = Calendar._DN[w]; // full weekday name
+ s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
+ s["%B"] = Calendar._MN[m]; // full month name
+ // FIXME: %c : preferred date and time representation for the current locale
+ s["%C"] = 1 + Math.floor(y / 100); // the century number
+ s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
+ s["%e"] = d; // the day of the month (range 1 to 31)
+ // FIXME: %D : american date style: %m/%d/%y
+ // FIXME: %E, %F, %G, %g, %h (man strftime)
+ s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
+ s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
+ s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
+ s["%k"] = hr; // hour, range 0 to 23 (24h format)
+ s["%l"] = ir; // hour, range 1 to 12 (12h format)
+ s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
+ s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
+ s["%n"] = "\n"; // a newline character
+ s["%p"] = pm ? "PM" : "AM";
+ s["%P"] = pm ? "pm" : "am";
+ // FIXME: %r : the time in am/pm notation %I:%M:%S %p
+ // FIXME: %R : the time in 24-hour notation %H:%M
+ s["%s"] = Math.floor(this.getTime() / 1000);
+ s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
+ s["%t"] = "\t"; // a tab character
+ // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
+ s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
+ s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
+ s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
+ // FIXME: %x : preferred date representation for the current locale without the time
+ // FIXME: %X : preferred time representation for the current locale without the date
+ s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
+ s["%Y"] = y; // year with the century
+ s["%%"] = "%"; // a literal '%' character
+
+ var re = /%./g;
+ if (!Calendar.is_ie5 && !Calendar.is_khtml)
+ return str.replace(re, function (par) { return s[par] || par; });
+
+ var a = str.match(re);
+ for (var i = 0; i < a.length; i++) {
+ var tmp = s[a[i]];
+ if (tmp) {
+ re = new RegExp(a[i], 'g');
+ str = str.replace(re, tmp);
+ }
+ }
+
+ return str;
+};
+
+Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
+Date.prototype.setFullYear = function(y) {
+ var d = new Date(this);
+ d.__msh_oldSetFullYear(y);
+ if (d.getMonth() != this.getMonth())
+ this.setDate(28);
+ this.__msh_oldSetFullYear(y);
+};
+
+// END: DATE OBJECT PATCHES
+
+
+// global object that remembers the calendar
+window._dynarch_popupCalendar = null;
diff --git a/httemplate/elements/calendar_stripped.js b/httemplate/elements/calendar_stripped.js
new file mode 100644
index 0000000..4fe03f1
--- /dev/null
+++ b/httemplate/elements/calendar_stripped.js
@@ -0,0 +1,14 @@
+/* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo
+ * -----------------------------------------------------------
+ *
+ * The DHTML Calendar, version 1.0 "It is happening again"
+ *
+ * Details and latest version at:
+ * www.dynarch.com/projects/calendar
+ *
+ * This script is developed by Dynarch.com. Visit us at www.dynarch.com.
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ */
+ Calendar=function(firstDayOfWeek,dateStr,onSelected,onClose){this.activeDiv=null;this.currentDateEl=null;this.getDateStatus=null;this.getDateToolTip=null;this.getDateText=null;this.timeout=null;this.onSelected=onSelected||null;this.onClose=onClose||null;this.dragging=false;this.hidden=false;this.minYear=1970;this.maxYear=2050;this.dateFormat=Calendar._TT["DEF_DATE_FORMAT"];this.ttDateFormat=Calendar._TT["TT_DATE_FORMAT"];this.isPopup=true;this.weekNumbers=true;this.firstDayOfWeek=typeof firstDayOfWeek=="number"?firstDayOfWeek:Calendar._FD;this.showsOtherMonths=false;this.dateStr=dateStr;this.ar_days=null;this.showsTime=false;this.time24=true;this.yearStep=2;this.hiliteToday=true;this.multiple=null;this.table=null;this.element=null;this.tbody=null;this.firstdayname=null;this.monthsCombo=null;this.yearsCombo=null;this.hilitedMonth=null;this.activeMonth=null;this.hilitedYear=null;this.activeYear=null;this.dateClicked=false;if(typeof Calendar._SDN=="undefined"){if(typeof Calendar._SDN_len=="undefined")Calendar._SDN_len=3;var ar=new Array();for(var i=8;i>0;){ar[--i]=Calendar._DN[i].substr(0,Calendar._SDN_len);}Calendar._SDN=ar;if(typeof Calendar._SMN_len=="undefined")Calendar._SMN_len=3;ar=new Array();for(var i=12;i>0;){ar[--i]=Calendar._MN[i].substr(0,Calendar._SMN_len);}Calendar._SMN=ar;}};Calendar._C=null;Calendar.is_ie=(/msie/i.test(navigator.userAgent)&&!/opera/i.test(navigator.userAgent));Calendar.is_ie5=(Calendar.is_ie&&/msie 5\.0/i.test(navigator.userAgent));Calendar.is_opera=/opera/i.test(navigator.userAgent);Calendar.is_khtml=/Konqueror|Safari|KHTML/i.test(navigator.userAgent);Calendar.getAbsolutePos=function(el){var SL=0,ST=0;var is_div=/^div$/i.test(el.tagName);if(is_div&&el.scrollLeft)SL=el.scrollLeft;if(is_div&&el.scrollTop)ST=el.scrollTop;var r={x:el.offsetLeft-SL,y:el.offsetTop-ST};if(el.offsetParent){var tmp=this.getAbsolutePos(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;}return r;};Calendar.isRelated=function(el,evt){var related=evt.relatedTarget;if(!related){var type=evt.type;if(type=="mouseover"){related=evt.fromElement;}else if(type=="mouseout"){related=evt.toElement;}}while(related){if(related==el){return true;}related=related.parentNode;}return false;};Calendar.removeClass=function(el,className){if(!(el&&el.className)){return;}var cls=el.className.split(" ");var ar=new Array();for(var i=cls.length;i>0;){if(cls[--i]!=className){ar[ar.length]=cls[i];}}el.className=ar.join(" ");};Calendar.addClass=function(el,className){Calendar.removeClass(el,className);el.className+=" "+className;};Calendar.getElement=function(ev){var f=Calendar.is_ie?window.event.srcElement:ev.currentTarget;while(f.nodeType!=1||/^div$/i.test(f.tagName))f=f.parentNode;return f;};Calendar.getTargetElement=function(ev){var f=Calendar.is_ie?window.event.srcElement:ev.target;while(f.nodeType!=1)f=f.parentNode;return f;};Calendar.stopEvent=function(ev){ev||(ev=window.event);if(Calendar.is_ie){ev.cancelBubble=true;ev.returnValue=false;}else{ev.preventDefault();ev.stopPropagation();}return false;};Calendar.addEvent=function(el,evname,func){if(el.attachEvent){el.attachEvent("on"+evname,func);}else if(el.addEventListener){el.addEventListener(evname,func,true);}else{el["on"+evname]=func;}};Calendar.removeEvent=function(el,evname,func){if(el.detachEvent){el.detachEvent("on"+evname,func);}else if(el.removeEventListener){el.removeEventListener(evname,func,true);}else{el["on"+evname]=null;}};Calendar.createElement=function(type,parent){var el=null;if(document.createElementNS){el=document.createElementNS("http://www.w3.org/1999/xhtml",type);}else{el=document.createElement(type);}if(typeof parent!="undefined"){parent.appendChild(el);}return el;};Calendar._add_evs=function(el){with(Calendar){addEvent(el,"mouseover",dayMouseOver);addEvent(el,"mousedown",dayMouseDown);addEvent(el,"mouseout",dayMouseOut);if(is_ie){addEvent(el,"dblclick",dayMouseDblClick);el.setAttribute("unselectable",true);}}};Calendar.findMonth=function(el){if(typeof el.month!="undefined"){return el;}else if(typeof el.parentNode.month!="undefined"){return el.parentNode;}return null;};Calendar.findYear=function(el){if(typeof el.year!="undefined"){return el;}else if(typeof el.parentNode.year!="undefined"){return el.parentNode;}return null;};Calendar.showMonthsCombo=function(){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var mc=cal.monthsCombo;if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}if(cal.activeMonth){Calendar.removeClass(cal.activeMonth,"active");}var mon=cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];Calendar.addClass(mon,"active");cal.activeMonth=mon;var s=mc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else{var mcw=mc.offsetWidth;if(typeof mcw=="undefined")mcw=50;s.left=(cd.offsetLeft+cd.offsetWidth-mcw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";};Calendar.showYearsCombo=function(fwd){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var yc=cal.yearsCombo;if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}if(cal.activeYear){Calendar.removeClass(cal.activeYear,"active");}cal.activeYear=null;var Y=cal.date.getFullYear()+(fwd?1:-1);var yr=yc.firstChild;var show=false;for(var i=12;i>0;--i){if(Y>=cal.minYear&&Y<=cal.maxYear){yr.innerHTML=Y;yr.year=Y;yr.style.display="block";show=true;}else{yr.style.display="none";}yr=yr.nextSibling;Y+=fwd?cal.yearStep:-cal.yearStep;}if(show){var s=yc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else{var ycw=yc.offsetWidth;if(typeof ycw=="undefined")ycw=50;s.left=(cd.offsetLeft+cd.offsetWidth-ycw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";}};Calendar.tableMouseUp=function(ev){var cal=Calendar._C;if(!cal){return false;}if(cal.timeout){clearTimeout(cal.timeout);}var el=cal.activeDiv;if(!el){return false;}var target=Calendar.getTargetElement(ev);ev||(ev=window.event);Calendar.removeClass(el,"active");if(target==el||target.parentNode==el){Calendar.cellClick(el,ev);}var mon=Calendar.findMonth(target);var date=null;if(mon){date=new Date(cal.date);if(mon.month!=date.getMonth()){date.setMonth(mon.month);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}else{var year=Calendar.findYear(target);if(year){date=new Date(cal.date);if(year.year!=date.getFullYear()){date.setFullYear(year.year);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}}with(Calendar){removeEvent(document,"mouseup",tableMouseUp);removeEvent(document,"mouseover",tableMouseOver);removeEvent(document,"mousemove",tableMouseOver);cal._hideCombos();_C=null;return stopEvent(ev);}};Calendar.tableMouseOver=function(ev){var cal=Calendar._C;if(!cal){return;}var el=cal.activeDiv;var target=Calendar.getTargetElement(ev);if(target==el||target.parentNode==el){Calendar.addClass(el,"hilite active");Calendar.addClass(el.parentNode,"rowhilite");}else{if(typeof el.navtype=="undefined"||(el.navtype!=50&&(el.navtype==0||Math.abs(el.navtype)>2)))Calendar.removeClass(el,"active");Calendar.removeClass(el,"hilite");Calendar.removeClass(el.parentNode,"rowhilite");}ev||(ev=window.event);if(el.navtype==50&&target!=el){var pos=Calendar.getAbsolutePos(el);var w=el.offsetWidth;var x=ev.clientX;var dx;var decrease=true;if(x>pos.x+w){dx=x-pos.x-w;decrease=false;}else dx=pos.x-x;if(dx<0)dx=0;var range=el._range;var current=el._current;var count=Math.floor(dx/10)%range.length;for(var i=range.length;--i>=0;)if(range[i]==current)break;while(count-->0)if(decrease){if(--i<0)i=range.length-1;}else if(++i>=range.length)i=0;var newval=range[i];el.innerHTML=newval;cal.onUpdateTime();}var mon=Calendar.findMonth(target);if(mon){if(mon.month!=cal.date.getMonth()){if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}Calendar.addClass(mon,"hilite");cal.hilitedMonth=mon;}else if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}}else{if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}var year=Calendar.findYear(target);if(year){if(year.year!=cal.date.getFullYear()){if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}Calendar.addClass(year,"hilite");cal.hilitedYear=year;}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}return Calendar.stopEvent(ev);};Calendar.tableMouseDown=function(ev){if(Calendar.getTargetElement(ev)==Calendar.getElement(ev)){return Calendar.stopEvent(ev);}};Calendar.calDragIt=function(ev){var cal=Calendar._C;if(!(cal&&cal.dragging)){return false;}var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posX=ev.pageX;posY=ev.pageY;}cal.hideShowCovered();var st=cal.element.style;st.left=(posX-cal.xOffs)+"px";st.top=(posY-cal.yOffs)+"px";return Calendar.stopEvent(ev);};Calendar.calDragEnd=function(ev){var cal=Calendar._C;if(!cal){return false;}cal.dragging=false;with(Calendar){removeEvent(document,"mousemove",calDragIt);removeEvent(document,"mouseup",calDragEnd);tableMouseUp(ev);}cal.hideShowCovered();};Calendar.dayMouseDown=function(ev){var el=Calendar.getElement(ev);if(el.disabled){return false;}var cal=el.calendar;cal.activeDiv=el;Calendar._C=cal;if(el.navtype!=300)with(Calendar){if(el.navtype==50){el._current=el.innerHTML;addEvent(document,"mousemove",tableMouseOver);}else addEvent(document,Calendar.is_ie5?"mousemove":"mouseover",tableMouseOver);addClass(el,"hilite active");addEvent(document,"mouseup",tableMouseUp);}else if(cal.isPopup){cal._dragStart(ev);}if(el.navtype==-1||el.navtype==1){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout("Calendar.showMonthsCombo()",250);}else if(el.navtype==-2||el.navtype==2){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout((el.navtype>0)?"Calendar.showYearsCombo(true)":"Calendar.showYearsCombo(false)",250);}else{cal.timeout=null;}return Calendar.stopEvent(ev);};Calendar.dayMouseDblClick=function(ev){Calendar.cellClick(Calendar.getElement(ev),ev||window.event);if(Calendar.is_ie){document.selection.empty();}};Calendar.dayMouseOver=function(ev){var el=Calendar.getElement(ev);if(Calendar.isRelated(el,ev)||Calendar._C||el.disabled){return false;}if(el.ttip){if(el.ttip.substr(0,1)=="_"){el.ttip=el.caldate.print(el.calendar.ttDateFormat)+el.ttip.substr(1);}el.calendar.tooltips.innerHTML=el.ttip;}if(el.navtype!=300){Calendar.addClass(el,"hilite");if(el.caldate){Calendar.addClass(el.parentNode,"rowhilite");}}return Calendar.stopEvent(ev);};Calendar.dayMouseOut=function(ev){with(Calendar){var el=getElement(ev);if(isRelated(el,ev)||_C||el.disabled)return false;removeClass(el,"hilite");if(el.caldate)removeClass(el.parentNode,"rowhilite");if(el.calendar)el.calendar.tooltips.innerHTML=_TT["SEL_DATE"];return stopEvent(ev);}};Calendar.cellClick=function(el,ev){var cal=el.calendar;var closing=false;var newdate=false;var date=null;if(typeof el.navtype=="undefined"){if(cal.currentDateEl){Calendar.removeClass(cal.currentDateEl,"selected");Calendar.addClass(el,"selected");closing=(cal.currentDateEl==el);if(!closing){cal.currentDateEl=el;}}cal.date.setDateOnly(el.caldate);date=cal.date;var other_month=!(cal.dateClicked=!el.otherMonth);if(!other_month&&!cal.currentDateEl)cal._toggleMultipleDate(new Date(date));else newdate=!el.disabled;if(other_month)cal._init(cal.firstDayOfWeek,date);}else{if(el.navtype==200){Calendar.removeClass(el,"hilite");cal.callCloseHandler();return;}date=new Date(cal.date);if(el.navtype==0)date.setDateOnly(new Date());cal.dateClicked=false;var year=date.getFullYear();var mon=date.getMonth();function setMonth(m){var day=date.getDate();var max=date.getMonthDays(m);if(day>max){date.setDate(max);}date.setMonth(m);};switch(el.navtype){case 400:Calendar.removeClass(el,"hilite");var text=Calendar._TT["ABOUT"];if(typeof text!="undefined"){text+=cal.showsTime?Calendar._TT["ABOUT_TIME"]:"";}else{text="Help and about box text is not translated into this language.\n"+"If you know this language and you feel generous please update\n"+"the corresponding file in \"lang\" subdir to match calendar-en.js\n"+"and send it back to <mihai_bazon@yahoo.com> to get it into the distribution ;-)\n\n"+"Thank you!\n"+"http://dynarch.com/mishoo/calendar.epl\n";}alert(text);return;case-2:if(year>cal.minYear){date.setFullYear(year-1);}break;case-1:if(mon>0){setMonth(mon-1);}else if(year-->cal.minYear){date.setFullYear(year);setMonth(11);}break;case 1:if(mon<11){setMonth(mon+1);}else if(year<cal.maxYear){date.setFullYear(year+1);setMonth(0);}break;case 2:if(year<cal.maxYear){date.setFullYear(year+1);}break;case 100:cal.setFirstDayOfWeek(el.fdow);return;case 50:var range=el._range;var current=el.innerHTML;for(var i=range.length;--i>=0;)if(range[i]==current)break;if(ev&&ev.shiftKey){if(--i<0)i=range.length-1;}else if(++i>=range.length)i=0;var newval=range[i];el.innerHTML=newval;cal.onUpdateTime();return;case 0:if((typeof cal.getDateStatus=="function")&&cal.getDateStatus(date,date.getFullYear(),date.getMonth(),date.getDate())){return false;}break;}if(!date.equalsTo(cal.date)){cal.setDate(date);newdate=true;}else if(el.navtype==0)newdate=closing=true;}if(newdate){ev&&cal.callHandler();}if(closing){Calendar.removeClass(el,"hilite");ev&&cal.callCloseHandler();}};Calendar.prototype.create=function(_par){var parent=null;if(!_par){parent=document.getElementsByTagName("body")[0];this.isPopup=true;}else{parent=_par;this.isPopup=false;}this.date=this.dateStr?new Date(this.dateStr):new Date();var table=Calendar.createElement("table");this.table=table;table.cellSpacing=0;table.cellPadding=0;table.calendar=this;Calendar.addEvent(table,"mousedown",Calendar.tableMouseDown);var div=Calendar.createElement("div");this.element=div;div.className="calendar";if(this.isPopup){div.style.position="absolute";div.style.display="none";}div.appendChild(table);var thead=Calendar.createElement("thead",table);var cell=null;var row=null;var cal=this;var hh=function(text,cs,navtype){cell=Calendar.createElement("td",row);cell.colSpan=cs;cell.className="button";if(navtype!=0&&Math.abs(navtype)<=2)cell.className+=" nav";Calendar._add_evs(cell);cell.calendar=cal;cell.navtype=navtype;cell.innerHTML="<div unselectable='on'>"+text+"</div>";return cell;};row=Calendar.createElement("tr",thead);var title_length=6;(this.isPopup)&&--title_length;(this.weekNumbers)&&++title_length;hh("?",1,400).ttip=Calendar._TT["INFO"];this.title=hh("",title_length,300);this.title.className="title";if(this.isPopup){this.title.ttip=Calendar._TT["DRAG_TO_MOVE"];this.title.style.cursor="move";hh("&#x00d7;",1,200).ttip=Calendar._TT["CLOSE"];}row=Calendar.createElement("tr",thead);row.className="headrow";this._nav_py=hh("&#x00ab;",1,-2);this._nav_py.ttip=Calendar._TT["PREV_YEAR"];this._nav_pm=hh("&#x2039;",1,-1);this._nav_pm.ttip=Calendar._TT["PREV_MONTH"];this._nav_now=hh(Calendar._TT["TODAY"],this.weekNumbers?4:3,0);this._nav_now.ttip=Calendar._TT["GO_TODAY"];this._nav_nm=hh("&#x203a;",1,1);this._nav_nm.ttip=Calendar._TT["NEXT_MONTH"];this._nav_ny=hh("&#x00bb;",1,2);this._nav_ny.ttip=Calendar._TT["NEXT_YEAR"];row=Calendar.createElement("tr",thead);row.className="daynames";if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.className="name wn";cell.innerHTML=Calendar._TT["WK"];}for(var i=7;i>0;--i){cell=Calendar.createElement("td",row);if(!i){cell.navtype=100;cell.calendar=this;Calendar._add_evs(cell);}}this.firstdayname=(this.weekNumbers)?row.firstChild.nextSibling:row.firstChild;this._displayWeekdays();var tbody=Calendar.createElement("tbody",table);this.tbody=tbody;for(i=6;i>0;--i){row=Calendar.createElement("tr",tbody);if(this.weekNumbers){cell=Calendar.createElement("td",row);}for(var j=7;j>0;--j){cell=Calendar.createElement("td",row);cell.calendar=this;Calendar._add_evs(cell);}}if(this.showsTime){row=Calendar.createElement("tr",tbody);row.className="time";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;cell.innerHTML=Calendar._TT["TIME"]||"&nbsp;";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=this.weekNumbers?4:3;(function(){function makeTimePart(className,init,range_start,range_end){var part=Calendar.createElement("span",cell);part.className=className;part.innerHTML=init;part.calendar=cal;part.ttip=Calendar._TT["TIME_PART"];part.navtype=50;part._range=[];if(typeof range_start!="number")part._range=range_start;else{for(var i=range_start;i<=range_end;++i){var txt;if(i<10&&range_end>=10)txt='0'+i;else txt=''+i;part._range[part._range.length]=txt;}}Calendar._add_evs(part);return part;};var hrs=cal.date.getHours();var mins=cal.date.getMinutes();var t12=!cal.time24;var pm=(hrs>12);if(t12&&pm)hrs-=12;var H=makeTimePart("hour",hrs,t12?1:0,t12?12:23);var span=Calendar.createElement("span",cell);span.innerHTML=":";span.className="colon";var M=makeTimePart("minute",mins,0,59);var AP=null;cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;if(t12)AP=makeTimePart("ampm",pm?"pm":"am",["am","pm"]);else cell.innerHTML="&nbsp;";cal.onSetTime=function(){var pm,hrs=this.date.getHours(),mins=this.date.getMinutes();if(t12){pm=(hrs>=12);if(pm)hrs-=12;if(hrs==0)hrs=12;AP.innerHTML=pm?"pm":"am";}H.innerHTML=(hrs<10)?("0"+hrs):hrs;M.innerHTML=(mins<10)?("0"+mins):mins;};cal.onUpdateTime=function(){var date=this.date;var h=parseInt(H.innerHTML,10);if(t12){if(/pm/i.test(AP.innerHTML)&&h<12)h+=12;else if(/am/i.test(AP.innerHTML)&&h==12)h=0;}var d=date.getDate();var m=date.getMonth();var y=date.getFullYear();date.setHours(h);date.setMinutes(parseInt(M.innerHTML,10));date.setFullYear(y);date.setMonth(m);date.setDate(d);this.dateClicked=false;this.callHandler();};})();}else{this.onSetTime=this.onUpdateTime=function(){};}var tfoot=Calendar.createElement("tfoot",table);row=Calendar.createElement("tr",tfoot);row.className="footrow";cell=hh(Calendar._TT["SEL_DATE"],this.weekNumbers?8:7,300);cell.className="ttip";if(this.isPopup){cell.ttip=Calendar._TT["DRAG_TO_MOVE"];cell.style.cursor="move";}this.tooltips=cell;div=Calendar.createElement("div",this.element);this.monthsCombo=div;div.className="combo";for(i=0;i<Calendar._MN.length;++i){var mn=Calendar.createElement("div");mn.className=Calendar.is_ie?"label-IEfix":"label";mn.month=i;mn.innerHTML=Calendar._SMN[i];div.appendChild(mn);}div=Calendar.createElement("div",this.element);this.yearsCombo=div;div.className="combo";for(i=12;i>0;--i){var yr=Calendar.createElement("div");yr.className=Calendar.is_ie?"label-IEfix":"label";div.appendChild(yr);}this._init(this.firstDayOfWeek,this.date);parent.appendChild(this.element);};Calendar._keyEvent=function(ev){var cal=window._dynarch_popupCalendar;if(!cal||cal.multiple)return false;(Calendar.is_ie)&&(ev=window.event);var act=(Calendar.is_ie||ev.type=="keypress"),K=ev.keyCode;if(ev.ctrlKey){switch(K){case 37:act&&Calendar.cellClick(cal._nav_pm);break;case 38:act&&Calendar.cellClick(cal._nav_py);break;case 39:act&&Calendar.cellClick(cal._nav_nm);break;case 40:act&&Calendar.cellClick(cal._nav_ny);break;default:return false;}}else switch(K){case 32:Calendar.cellClick(cal._nav_now);break;case 27:act&&cal.callCloseHandler();break;case 37:case 38:case 39:case 40:if(act){var prev,x,y,ne,el,step;prev=K==37||K==38;step=(K==37||K==39)?1:7;function setVars(){el=cal.currentDateEl;var p=el.pos;x=p&15;y=p>>4;ne=cal.ar_days[y][x];};setVars();function prevMonth(){var date=new Date(cal.date);date.setDate(date.getDate()-step);cal.setDate(date);};function nextMonth(){var date=new Date(cal.date);date.setDate(date.getDate()+step);cal.setDate(date);};while(1){switch(K){case 37:if(--x>=0)ne=cal.ar_days[y][x];else{x=6;K=38;continue;}break;case 38:if(--y>=0)ne=cal.ar_days[y][x];else{prevMonth();setVars();}break;case 39:if(++x<7)ne=cal.ar_days[y][x];else{x=0;K=40;continue;}break;case 40:if(++y<cal.ar_days.length)ne=cal.ar_days[y][x];else{nextMonth();setVars();}break;}break;}if(ne){if(!ne.disabled)Calendar.cellClick(ne);else if(prev)prevMonth();else nextMonth();}}break;case 13:if(act)Calendar.cellClick(cal.currentDateEl,ev);break;default:return false;}return Calendar.stopEvent(ev);};Calendar.prototype._init=function(firstDayOfWeek,date){var today=new Date(),TY=today.getFullYear(),TM=today.getMonth(),TD=today.getDate();this.table.style.visibility="hidden";var year=date.getFullYear();if(year<this.minYear){year=this.minYear;date.setFullYear(year);}else if(year>this.maxYear){year=this.maxYear;date.setFullYear(year);}this.firstDayOfWeek=firstDayOfWeek;this.date=new Date(date);var month=date.getMonth();var mday=date.getDate();var no_days=date.getMonthDays();date.setDate(1);var day1=(date.getDay()-this.firstDayOfWeek)%7;if(day1<0)day1+=7;date.setDate(-day1);date.setDate(date.getDate()+1);var row=this.tbody.firstChild;var MN=Calendar._SMN[month];var ar_days=this.ar_days=new Array();var weekend=Calendar._TT["WEEKEND"];var dates=this.multiple?(this.datesCells={}):null;for(var i=0;i<6;++i,row=row.nextSibling){var cell=row.firstChild;if(this.weekNumbers){cell.className="day wn";cell.innerHTML=date.getWeekNumber();cell=cell.nextSibling;}row.className="daysrow";var hasdays=false,iday,dpos=ar_days[i]=[];for(var j=0;j<7;++j,cell=cell.nextSibling,date.setDate(iday+1)){iday=date.getDate();var wday=date.getDay();cell.className="day";cell.pos=i<<4|j;dpos[j]=cell;var current_month=(date.getMonth()==month);if(!current_month){if(this.showsOtherMonths){cell.className+=" othermonth";cell.otherMonth=true;}else{cell.className="emptycell";cell.innerHTML="&nbsp;";cell.disabled=true;continue;}}else{cell.otherMonth=false;hasdays=true;}cell.disabled=false;cell.innerHTML=this.getDateText?this.getDateText(date,iday):iday;if(dates)dates[date.print("%Y%m%d")]=cell;if(this.getDateStatus){var status=this.getDateStatus(date,year,month,iday);if(this.getDateToolTip){var toolTip=this.getDateToolTip(date,year,month,iday);if(toolTip)cell.title=toolTip;}if(status===true){cell.className+=" disabled";cell.disabled=true;}else{if(/disabled/i.test(status))cell.disabled=true;cell.className+=" "+status;}}if(!cell.disabled){cell.caldate=new Date(date);cell.ttip="_";if(!this.multiple&&current_month&&iday==mday&&this.hiliteToday){cell.className+=" selected";this.currentDateEl=cell;}if(date.getFullYear()==TY&&date.getMonth()==TM&&iday==TD){cell.className+=" today";cell.ttip+=Calendar._TT["PART_TODAY"];}if(weekend.indexOf(wday.toString())!=-1)cell.className+=cell.otherMonth?" oweekend":" weekend";}}if(!(hasdays||this.showsOtherMonths))row.className="emptyrow";}this.title.innerHTML=Calendar._MN[month]+", "+year;this.onSetTime();this.table.style.visibility="visible";this._initMultipleDates();};Calendar.prototype._initMultipleDates=function(){if(this.multiple){for(var i in this.multiple){var cell=this.datesCells[i];var d=this.multiple[i];if(!d)continue;if(cell)cell.className+=" selected";}}};Calendar.prototype._toggleMultipleDate=function(date){if(this.multiple){var ds=date.print("%Y%m%d");var cell=this.datesCells[ds];if(cell){var d=this.multiple[ds];if(!d){Calendar.addClass(cell,"selected");this.multiple[ds]=date;}else{Calendar.removeClass(cell,"selected");delete this.multiple[ds];}}}};Calendar.prototype.setDateToolTipHandler=function(unaryFunction){this.getDateToolTip=unaryFunction;};Calendar.prototype.setDate=function(date){if(!date.equalsTo(this.date)){this._init(this.firstDayOfWeek,date);}};Calendar.prototype.refresh=function(){this._init(this.firstDayOfWeek,this.date);};Calendar.prototype.setFirstDayOfWeek=function(firstDayOfWeek){this._init(firstDayOfWeek,this.date);this._displayWeekdays();};Calendar.prototype.setDateStatusHandler=Calendar.prototype.setDisabledHandler=function(unaryFunction){this.getDateStatus=unaryFunction;};Calendar.prototype.setRange=function(a,z){this.minYear=a;this.maxYear=z;};Calendar.prototype.callHandler=function(){if(this.onSelected){this.onSelected(this,this.date.print(this.dateFormat));}};Calendar.prototype.callCloseHandler=function(){if(this.onClose){this.onClose(this);}this.hideShowCovered();};Calendar.prototype.destroy=function(){var el=this.element.parentNode;el.removeChild(this.element);Calendar._C=null;window._dynarch_popupCalendar=null;};Calendar.prototype.reparent=function(new_parent){var el=this.element;el.parentNode.removeChild(el);new_parent.appendChild(el);};Calendar._checkCalendar=function(ev){var calendar=window._dynarch_popupCalendar;if(!calendar){return false;}var el=Calendar.is_ie?Calendar.getElement(ev):Calendar.getTargetElement(ev);for(;el!=null&&el!=calendar.element;el=el.parentNode);if(el==null){window._dynarch_popupCalendar.callCloseHandler();return Calendar.stopEvent(ev);}};Calendar.prototype.show=function(){var rows=this.table.getElementsByTagName("tr");for(var i=rows.length;i>0;){var row=rows[--i];Calendar.removeClass(row,"rowhilite");var cells=row.getElementsByTagName("td");for(var j=cells.length;j>0;){var cell=cells[--j];Calendar.removeClass(cell,"hilite");Calendar.removeClass(cell,"active");}}this.element.style.display="block";this.hidden=false;if(this.isPopup){window._dynarch_popupCalendar=this;Calendar.addEvent(document,"keydown",Calendar._keyEvent);Calendar.addEvent(document,"keypress",Calendar._keyEvent);Calendar.addEvent(document,"mousedown",Calendar._checkCalendar);}this.hideShowCovered();};Calendar.prototype.hide=function(){if(this.isPopup){Calendar.removeEvent(document,"keydown",Calendar._keyEvent);Calendar.removeEvent(document,"keypress",Calendar._keyEvent);Calendar.removeEvent(document,"mousedown",Calendar._checkCalendar);}this.element.style.display="none";this.hidden=true;this.hideShowCovered();};Calendar.prototype.showAt=function(x,y){var s=this.element.style;s.left=x+"px";s.top=y+"px";this.show();};Calendar.prototype.showAtElement=function(el,opts){var self=this;var p=Calendar.getAbsolutePos(el);if(!opts||typeof opts!="string"){this.showAt(p.x,p.y+el.offsetHeight);return true;}function fixPosition(box){if(box.x<0)box.x=0;if(box.y<0)box.y=0;var cp=document.createElement("div");var s=cp.style;s.position="absolute";s.right=s.bottom=s.width=s.height="0px";document.body.appendChild(cp);var br=Calendar.getAbsolutePos(cp);document.body.removeChild(cp);if(Calendar.is_ie){br.y+=document.body.scrollTop;br.x+=document.body.scrollLeft;}else{br.y+=window.scrollY;br.x+=window.scrollX;}var tmp=box.x+box.width-br.x;if(tmp>0)box.x-=tmp;tmp=box.y+box.height-br.y;if(tmp>0)box.y-=tmp;};this.element.style.display="block";Calendar.continuation_for_the_fucking_khtml_browser=function(){var w=self.element.offsetWidth;var h=self.element.offsetHeight;self.element.style.display="none";var valign=opts.substr(0,1);var halign="l";if(opts.length>1){halign=opts.substr(1,1);}switch(valign){case "T":p.y-=h;break;case "B":p.y+=el.offsetHeight;break;case "C":p.y+=(el.offsetHeight-h)/2;break;case "t":p.y+=el.offsetHeight-h;break;case "b":break;}switch(halign){case "L":p.x-=w;break;case "R":p.x+=el.offsetWidth;break;case "C":p.x+=(el.offsetWidth-w)/2;break;case "l":p.x+=el.offsetWidth-w;break;case "r":break;}p.width=w;p.height=h+40;self.monthsCombo.style.display="none";fixPosition(p);self.showAt(p.x,p.y);};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype.setDateFormat=function(str){this.dateFormat=str;};Calendar.prototype.setTtDateFormat=function(str){this.ttDateFormat=str;};Calendar.prototype.parseDate=function(str,fmt){if(!fmt)fmt=this.dateFormat;this.setDate(Date.parseDate(str,fmt));};Calendar.prototype.hideShowCovered=function(){if(!Calendar.is_ie&&!Calendar.is_opera)return;function getVisib(obj){var value=obj.style.visibility;if(!value){if(document.defaultView&&typeof(document.defaultView.getComputedStyle)=="function"){if(!Calendar.is_khtml)value=document.defaultView. getComputedStyle(obj,"").getPropertyValue("visibility");else value='';}else if(obj.currentStyle){value=obj.currentStyle.visibility;}else value='';}return value;};var tags=new Array("applet","iframe","select");var el=this.element;var p=Calendar.getAbsolutePos(el);var EX1=p.x;var EX2=el.offsetWidth+EX1;var EY1=p.y;var EY2=el.offsetHeight+EY1;for(var k=tags.length;k>0;){var ar=document.getElementsByTagName(tags[--k]);var cc=null;for(var i=ar.length;i>0;){cc=ar[--i];p=Calendar.getAbsolutePos(cc);var CX1=p.x;var CX2=cc.offsetWidth+CX1;var CY1=p.y;var CY2=cc.offsetHeight+CY1;if(this.hidden||(CX1>EX2)||(CX2<EX1)||(CY1>EY2)||(CY2<EY1)){if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility=cc.__msh_save_visibility;}else{if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility="hidden";}}}};Calendar.prototype._displayWeekdays=function(){var fdow=this.firstDayOfWeek;var cell=this.firstdayname;var weekend=Calendar._TT["WEEKEND"];for(var i=0;i<7;++i){cell.className="day name";var realday=(i+fdow)%7;if(i){cell.ttip=Calendar._TT["DAY_FIRST"].replace("%s",Calendar._DN[realday]);cell.navtype=100;cell.calendar=this;cell.fdow=realday;Calendar._add_evs(cell);}if(weekend.indexOf(realday.toString())!=-1){Calendar.addClass(cell,"weekend");}cell.innerHTML=Calendar._SDN[(i+fdow)%7];cell=cell.nextSibling;}};Calendar.prototype._hideCombos=function(){this.monthsCombo.style.display="none";this.yearsCombo.style.display="none";};Calendar.prototype._dragStart=function(ev){if(this.dragging){return;}this.dragging=true;var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posY=ev.clientY+window.scrollY;posX=ev.clientX+window.scrollX;}var st=this.element.style;this.xOffs=posX-parseInt(st.left);this.yOffs=posY-parseInt(st.top);with(Calendar){addEvent(document,"mousemove",calDragIt);addEvent(document,"mouseup",calDragEnd);}};Date._MD=new Array(31,28,31,30,31,30,31,31,30,31,30,31);Date.SECOND=1000;Date.MINUTE=60*Date.SECOND;Date.HOUR=60*Date.MINUTE;Date.DAY=24*Date.HOUR;Date.WEEK=7*Date.DAY;Date.parseDate=function(str,fmt){var today=new Date();var y=0;var m=-1;var d=0;var a=str.split(/\W+/);var b=fmt.match(/%./g);var i=0,j=0;var hr=0;var min=0;for(i=0;i<a.length;++i){if(!a[i])continue;switch(b[i]){case "%d":case "%e":d=parseInt(a[i],10);break;case "%m":m=parseInt(a[i],10)-1;break;case "%Y":case "%y":y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);break;case "%b":case "%B":for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){m=j;break;}}break;case "%H":case "%I":case "%k":case "%l":hr=parseInt(a[i],10);break;case "%P":case "%p":if(/pm/i.test(a[i])&&hr<12)hr+=12;else if(/am/i.test(a[i])&&hr>=12)hr-=12;break;case "%M":min=parseInt(a[i],10);break;}}if(isNaN(y))y=today.getFullYear();if(isNaN(m))m=today.getMonth();if(isNaN(d))d=today.getDate();if(isNaN(hr))hr=today.getHours();if(isNaN(min))min=today.getMinutes();if(y!=0&&m!=-1&&d!=0)return new Date(y,m,d,hr,min,0);y=0;m=-1;d=0;for(i=0;i<a.length;++i){if(a[i].search(/[a-zA-Z]+/)!=-1){var t=-1;for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){t=j;break;}}if(t!=-1){if(m!=-1){d=m+1;}m=t;}}else if(parseInt(a[i],10)<=12&&m==-1){m=a[i]-1;}else if(parseInt(a[i],10)>31&&y==0){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}else if(d==0){d=a[i];}}if(y==0)y=today.getFullYear();if(m!=-1&&d!=0)return new Date(y,m,d,hr,min,0);return today;};Date.prototype.getMonthDays=function(month){var year=this.getFullYear();if(typeof month=="undefined"){month=this.getMonth();}if(((0==(year%4))&&((0!=(year%100))||(0==(year%400))))&&month==1){return 29;}else{return Date._MD[month];}};Date.prototype.getDayOfYear=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,0,0,0,0);var time=now-then;return Math.floor(time/Date.DAY);};Date.prototype.getWeekNumber=function(){var d=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var DoW=d.getDay();d.setDate(d.getDate()-(DoW+6)%7+3);var ms=d.valueOf();d.setMonth(0);d.setDate(4);return Math.round((ms-d.valueOf())/(7*864e5))+1;};Date.prototype.equalsTo=function(date){return((this.getFullYear()==date.getFullYear())&&(this.getMonth()==date.getMonth())&&(this.getDate()==date.getDate())&&(this.getHours()==date.getHours())&&(this.getMinutes()==date.getMinutes()));};Date.prototype.setDateOnly=function(date){var tmp=new Date(date);this.setDate(1);this.setFullYear(tmp.getFullYear());this.setMonth(tmp.getMonth());this.setDate(tmp.getDate());};Date.prototype.print=function(str){var m=this.getMonth();var d=this.getDate();var y=this.getFullYear();var wn=this.getWeekNumber();var w=this.getDay();var s={};var hr=this.getHours();var pm=(hr>=12);var ir=(pm)?(hr-12):hr;var dy=this.getDayOfYear();if(ir==0)ir=12;var min=this.getMinutes();var sec=this.getSeconds();s["%a"]=Calendar._SDN[w];s["%A"]=Calendar._DN[w];s["%b"]=Calendar._SMN[m];s["%B"]=Calendar._MN[m];s["%C"]=1+Math.floor(y/100);s["%d"]=(d<10)?("0"+d):d;s["%e"]=d;s["%H"]=(hr<10)?("0"+hr):hr;s["%I"]=(ir<10)?("0"+ir):ir;s["%j"]=(dy<100)?((dy<10)?("00"+dy):("0"+dy)):dy;s["%k"]=hr;s["%l"]=ir;s["%m"]=(m<9)?("0"+(1+m)):(1+m);s["%M"]=(min<10)?("0"+min):min;s["%n"]="\n";s["%p"]=pm?"PM":"AM";s["%P"]=pm?"pm":"am";s["%s"]=Math.floor(this.getTime()/1000);s["%S"]=(sec<10)?("0"+sec):sec;s["%t"]="\t";s["%U"]=s["%W"]=s["%V"]=(wn<10)?("0"+wn):wn;s["%u"]=w+1;s["%w"]=w;s["%y"]=(''+y).substr(2,2);s["%Y"]=y;s["%%"]="%";var re=/%./g;if(!Calendar.is_ie5&&!Calendar.is_khtml)return str.replace(re,function(par){return s[par]||par;});var a=str.match(re);for(var i=0;i<a.length;i++){var tmp=s[a[i]];if(tmp){re=new RegExp(a[i],'g');str=str.replace(re,tmp);}}return str;};Date.prototype.__msh_oldSetFullYear=Date.prototype.setFullYear;Date.prototype.setFullYear=function(y){var d=new Date(this);d.__msh_oldSetFullYear(y);if(d.getMonth()!=this.getMonth())this.setDate(28);this.__msh_oldSetFullYear(y);};window._dynarch_popupCalendar=null; \ No newline at end of file
diff --git a/httemplate/elements/checkboxes-table-name.html b/httemplate/elements/checkboxes-table-name.html
new file mode 100644
index 0000000..31652f3
--- /dev/null
+++ b/httemplate/elements/checkboxes-table-name.html
@@ -0,0 +1,90 @@
+<%doc>
+
+Example:
+
+ include( '/elements/checkboxes-table-name.html',
+
+ ###
+ # required
+ ###
+ 'link_table' => 'table_name',
+
+ 'name_col' => 'name_column',
+ #or
+ 'name_callback' => sub { },
+
+ 'names_list' => [ 'value',
+ 'other value',
+ [ 'complex value' => { 'desc' => "Add'l description",
+ 'note' => '&nbsp;*',
+ }
+ ],
+ ],
+
+ ###
+ # recommended (required?)
+ ###
+ 'source_obj' => $obj,
+ #or?
+ #'source_table' => 'table_name',
+ #'sourcenum' => '4', #current value of primary key in source_table
+ # # (none is okay, just pass it if you have it)
+
+ ###
+ # optional
+ ###
+ 'num_col' => 'col_name' #if column name is different in link_table than
+ #source_table
+ 'link_static' => { 'column' => 'value' },
+
+ )
+
+</%doc>
+
+<% include('checkboxes.html',
+ 'names_list' => $opt{'names_list'},
+ 'checked_callback' => $checked_callback,
+ 'element_name_prefix' => $opt{'link_table'}. '.',
+ )
+%>
+
+<%init>
+
+my( %opt ) = @_;
+
+my @pset = ( 'a'..'z', 'A'..'Z', '0'..'9' );
+
+my $prefix = $opt{prefix}
+ || join('', map $pset[ int(rand $#pset) ], (0..20) );
+
+my( $source_pkey, $sourcenum, $source_obj );
+if ( $opt{'source_obj'} ) {
+
+ $source_obj = $opt{'source_obj'};
+ #$source_table = $source_obj->dbdef_table->table;
+ $source_pkey = $source_obj->dbdef_table->primary_key;
+ $sourcenum = $source_obj->$source_pkey();
+
+} else {
+
+ #$source_obj?
+ $source_pkey = $opt{'source_table'}
+ ? dbdef->table($opt{'source_table'})->primary_key
+ : '';
+ $sourcenum = $opt{'sourcenum'};
+}
+
+$source_pkey = $opt{'num_col'} || $source_pkey;
+
+my $link_static = $opt{'link_static'} || {};
+
+my $checked_callback = sub {
+ my( $cgi, $name ) = @_;
+ qsearchs( $opt{'link_table'}, {
+ $source_pkey => $sourcenum,
+ $opt{'name_col'} => $name,
+ %$link_static,
+ });
+};
+
+</%init>
diff --git a/httemplate/elements/checkboxes-table.html b/httemplate/elements/checkboxes-table.html
new file mode 100644
index 0000000..b6b04d1
--- /dev/null
+++ b/httemplate/elements/checkboxes-table.html
@@ -0,0 +1,129 @@
+%
+%
+% ##
+% # required
+% ##
+% # 'target_table' => 'table_name',
+% # 'link_table' => 'table_name',
+% #
+% # 'name_col' => 'name_column',
+% # #or
+% # 'name_callback' => sub { },
+% #
+% ##
+% # recommended (required?)
+% ##
+% # 'source_obj' => $obj,
+% # #or?
+% # #'source_table' => 'table_name',
+% # #'sourcenum' => '4', #current value of primary key in source_table
+% # # # (none is okay, just pass it if you have it)
+% ##
+% # optional
+% ##
+% # 'disable-able' => 1,
+%
+% my( %opt ) = @_;
+%
+% my $target_pkey = dbdef->table($opt{'target_table'})->primary_key;
+%
+% my( $source_pkey, $sourcenum, $source_obj );
+% if ( $opt{'source_obj'} || $opt{'object'} ) {
+%
+% $source_obj = $opt{'source_obj'} || $opt{'object'};
+% #$source_table = $source_obj->dbdef_table->table;
+% $source_pkey = $source_obj->dbdef_table->primary_key;
+% $sourcenum = $source_obj->$source_pkey();
+%
+% } else {
+%
+% #$source_obj?
+% $source_pkey = $opt{'source_table'}
+% ? dbdef->table($opt{'source_table'})->primary_key
+% : '';
+% $sourcenum = $opt{'sourcenum'};
+% }
+%
+% my $hashref = $opt{'hashref'} || {};
+%
+% my $extra_sql = '';
+%
+% if ( $opt{'agent_virt'} ) {
+% $extra_sql .= ' AND' . $FS::CurrentUser::CurrentUser->agentnums_sql(
+% 'null_right' => $opt{'agent_null_right'}
+% );
+% }
+%
+% if ( $opt{'disable-able'} ) {
+% $hashref->{'disabled'} = '';
+%
+% $extra_sql .= ( $sourcenum && $source_pkey )
+% ? " OR $source_pkey = $sourcenum"
+% : '';
+% }
+%
+%
+% foreach my $target_obj (
+% qsearch({ 'table' => $opt{'target_table'},
+% 'hashref' => $hashref,
+% 'select' => $opt{'target_table'}. '.*',
+% 'addl_from' => "LEFT JOIN $opt{'link_table'} USING ( $target_pkey )",
+% 'extra_sql' => $extra_sql,
+% })
+% ) {
+%
+% my $targetnum = $target_obj->$target_pkey();
+%
+% my $checked;
+% if ( $cgi->param('error') ) {
+%
+% $checked = $cgi->param($target_pkey.$targetnum)
+% ? 'CHECKED'
+% : '';
+%
+% } else {
+%
+% $checked = qsearchs( $opt{'link_table'}, {
+% $source_pkey => $sourcenum,
+% $target_pkey => $targetnum,
+% } )
+% ? 'CHECKED'
+% : ''
+%
+% }
+%
+%
+
+
+ <INPUT TYPE="checkbox" NAME="<% $target_pkey. $targetnum %>" <% $checked %> VALUE="ON">
+% if ( $opt{'target_link'} ) {
+
+
+ <A HREF="<% $opt{'target_link'} %><% $targetnum %>">
+%
+%
+% }
+%
+<% $targetnum %>:
+% if ( $opt{'name_callback'} ) {
+
+
+ <% &{ $opt{'name_callback'} }( $target_obj ) %><% $opt{'target_link'} ? '</A>' : '' %>
+% } else {
+% my $name_col = $opt{'name_col'};
+%
+
+
+ <% $target_obj->$name_col() %><% $opt{'target_link'} ? '</A>' : '' %>
+% }
+% if ( $opt{'disable-able'} ) {
+
+
+ <% $target_obj->disabled =~ /^Y/i ? ' (DISABLED)' : '' %>
+% }
+
+
+ <BR>
+% }
+
+
diff --git a/httemplate/elements/checkboxes.html b/httemplate/elements/checkboxes.html
new file mode 100644
index 0000000..1262245
--- /dev/null
+++ b/httemplate/elements/checkboxes.html
@@ -0,0 +1,103 @@
+<%doc>
+
+Example:
+
+ include( '/elements/checkboxes.html',
+
+ # required
+
+ #? 'name_callback' => sub { },
+
+ 'names_list' => [ 'value',
+ 'other value',
+ [ 'complex value' => { 'desc' => "Add'l description",
+ 'note' => '&nbsp;*',
+ }
+ ],
+ ],
+
+ 'element_name_prefix' => "$link_table.",
+
+ #recommended
+
+ 'checked_callback' => sub { my( $cgi, $name ) = @_; },
+
+ )
+
+</%doc>
+
+<TABLE CELLSPACING=0 CELLPADDING=0>
+
+<TR>
+ <TD COLSPAN=2 ALIGN="center"><FONT SIZE="-1">(
+ <A HREF="javascript:setAll<%$prefix%>(true)">select all</A> |
+ <A HREF="javascript:setAll<%$prefix%>(false)">unselect all</A> |
+ <A HREF="javascript:toggleAll<%$prefix%>()">toggle all</A>
+ )</FONT></TD>
+</TR>
+
+% my $num=0;
+% foreach my $item ( @{ $opt{'names_list'} } ) {
+%
+% my $name = ref($item) ? $item->[0] : $item;
+% ( my $display = $name ) =~ s/ /&nbsp;/g;
+% $display .= $item->[1]{note} if ref($item) && $item->[1]{note};
+% my $desc = ref($item) && $item->[1]{desc} ? $item->[1]{desc} : '';
+%
+% my $callback =
+% ( $cgi->param('error') ? 'error_' : '' ). 'checked_callback';
+% my $checked = &{ $opt{$callback} }( $cgi, $name ) ? 'CHECKED' : '';
+
+ <TR>
+ <TD VALIGN="top">
+ <INPUT TYPE="checkbox" NAME="<% $opt{'element_name_prefix'}. $name %>" <% $checked %> ID="<%$prefix.$num++%>" VALUE="ON">
+ </TD>
+ <TD><% $display %>
+% if ( $desc ) {
+ <BR><FONT SIZE="-2"><% $desc %></FONT>
+% }
+ </TD>
+ </TR>
+
+% }
+
+</TABLE>
+
+<SCRIPT TYPE="text/javascript">
+
+ function setAll<%$prefix%>(setTo) {
+% for ( 0 .. ($num-1) ) {
+ document.getElementById('<%$prefix.$_%>').checked = setTo;
+% }
+ }
+
+ function toggleAll<%$prefix%>(setTo) {
+% for ( 0 .. ($num-1) ) {
+ var element = document.getElementById('<%$prefix.$_%>');
+ if ( element.checked == true ) {
+ element.checked = false;
+ } else {
+ element.checked = true;
+ }
+% }
+ }
+
+</SCRIPT>
+
+<%init>
+
+my( %opt ) = @_;
+
+my @pset = ( 'a'..'z', 'A'..'Z', '0'..'9' );
+
+my $prefix = $opt{prefix}
+ || join('', map $pset[ int(rand $#pset) ], (0..20) );
+
+$opt{checked_callback} ||= sub {};
+
+$opt{'error_checked_callback'} ||= sub {
+ my( $cgi, $name ) = @_;
+ $cgi->param($opt{'element_name_prefix'}. $name );
+};
+
+</%init>
diff --git a/httemplate/elements/columnend.html b/httemplate/elements/columnend.html
new file mode 100644
index 0000000..021a328
--- /dev/null
+++ b/httemplate/elements/columnend.html
@@ -0,0 +1,6 @@
+ </TABLE>
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+</TR>
diff --git a/httemplate/elements/columnnext.html b/httemplate/elements/columnnext.html
new file mode 100644
index 0000000..4dfe82f
--- /dev/null
+++ b/httemplate/elements/columnnext.html
@@ -0,0 +1,4 @@
+ </TABLE>
+ </TD>
+ <TD VALIGN="top" STYLE="padding-left:12px">
+ <TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
diff --git a/httemplate/elements/columnstart.html b/httemplate/elements/columnstart.html
new file mode 100644
index 0000000..0341b27
--- /dev/null
+++ b/httemplate/elements/columnstart.html
@@ -0,0 +1,6 @@
+<TR>
+ <TD BGCOLOR="#e8e8e8" COLSPAN=99>
+ <TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0>
+ <TR>
+ <TD VALIGN="top">
+ <TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
diff --git a/httemplate/elements/cssexpr.js b/httemplate/elements/cssexpr.js
new file mode 100644
index 0000000..c434d8d
--- /dev/null
+++ b/httemplate/elements/cssexpr.js
@@ -0,0 +1,66 @@
+function constExpression(x) {
+ return x;
+}
+
+function simplifyCSSExpression() {
+ try {
+ var ss,sl, rs, rl;
+ ss = document.styleSheets;
+ sl = ss.length
+
+ for (var i = 0; i < sl; i++) {
+ simplifyCSSBlock(ss[i]);
+ }
+ }
+ catch (exc) {
+ //alert("Got an error while processing css. The page should still work but might be a bit slower");
+ throw exc;
+ }
+}
+
+function simplifyCSSBlock(ss) {
+ var rs, rl;
+
+ for (var i = 0; i < ss.imports.length; i++)
+ simplifyCSSBlock(ss.imports[i]);
+
+ if (ss.cssText.indexOf("expression(constExpression(") == -1)
+ return;
+
+ rs = ss.rules;
+ rl = rs.length;
+ for (var j = 0; j < rl; j++)
+ simplifyCSSRule(rs[j]);
+
+}
+
+function simplifyCSSRule(r) {
+ var str = r.style.cssText;
+ var str2 = str;
+ var lastStr;
+ do {
+ lastStr = str2;
+ str2 = simplifyCSSRuleHelper(lastStr);
+ } while (str2 != lastStr)
+
+ if (str2 != str)
+ r.style.cssText = str2;
+}
+
+function simplifyCSSRuleHelper(str) {
+ var i, i2;
+ i = str.indexOf("expression(constExpression(");
+ if (i == -1) return str;
+ i2 = str.indexOf("))", i);
+ var hd = str.substring(0, i);
+ var tl = str.substring(i2 + 2);
+ var exp = str.substring(i + 27, i2);
+ var val = eval(exp)
+ return hd + val + tl;
+}
+
+if (/msie/i.test(navigator.userAgent) && window.attachEvent != null) {
+ window.attachEvent("onload", function () {
+ simplifyCSSExpression();
+ });
+}
diff --git a/httemplate/elements/customer-table.html b/httemplate/elements/customer-table.html
new file mode 100644
index 0000000..f00419f
--- /dev/null
+++ b/httemplate/elements/customer-table.html
@@ -0,0 +1,524 @@
+<%doc>
+
+Example:
+
+ include( '/elements/customer-table.html',
+
+ ###
+ # required
+ ###
+
+ #listrefs...
+ 'header' => [ '#', 'Item' ],
+ 'fields' => [
+ 'column',
+ sub { my ($row,$param) = @_;
+ $param->{"column$row"};
+ },
+ ],
+
+ ###
+ # optional
+ ###
+
+ 'name_singular' => 'customer', #label
+
+ #listrefs
+ 'types' => ['immutable', ''], # immutable or ''/text
+ 'align' => [ 'c', 'l', 'r', '' ],
+ 'size' => [], # sizes ignored for immutable
+ 'color' => [],
+ 'footer' => ['string', '_TOTAL'], # strings or the special
+ #value _TOTAL
+ 'footer_align' => [ 'c', 'l', 'r', '' ],
+
+ 'param' => { column0 => 1 }, # preset column of row 0 to 1
+
+ )
+
+</%doc>
+
+<SCRIPT TYPE="text/javascript">
+
+ function clearhint_custnum() {
+
+ if ( this.value == 'Not found' || this.value == 'Multiple' ) {
+ this.value = '';
+ this.style.color = '#000000';
+ }
+
+ }
+
+ function clearhint_customer() {
+
+ this.style.color = '#000000';
+
+ if ( this.value == '(last name or company)' || this.value == 'Not found' )
+ this.value = '';
+
+ }
+
+ function <% $opt{prefix} %>search_custnum() {
+
+ this.style.color = '#000000'
+
+ var custnum_obj = this;
+ var searchrow = this.getAttribute('rownum');
+ var custnum = this.value;
+
+ if ( custnum == 'searching...' || custnum == 'Not found' || custnum == '' )
+ return;
+
+ if ( this.getAttribute('magic') == 'nosearch' ) {
+ this.setAttribute('magic', '');
+ return;
+ }
+
+ if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
+ <% $opt{prefix} %>addRow();
+ }
+ var customer = document.getElementById('customer'+searchrow);
+ customer.value = 'searching...';
+ customer.disabled = true;
+ customer.style.color = '#000000';
+ customer.style.backgroundColor = '#dddddd';
+
+ var customer_select = document.getElementById('cust_select'+searchrow);
+
+ customer.style.display = '';
+ customer_select.style.display = 'none';
+
+ function search_custnum_update(name) {
+
+ var name = eval('(' + name + ')' );
+
+ customer.disabled = false;
+ customer.style.backgroundColor = '#ffffff';
+
+ if ( name.length > 0 ) {
+ customer.value = name;
+ customer.setAttribute('magic', 'nosearch');
+ } else {
+ customer.value = 'Not found';
+ customer.style.color = '#ff0000';
+ custnum_obj.style.color = '#ff0000';
+
+ }
+
+ }
+
+ custnum_search( custnum, search_custnum_update );
+
+ }
+
+ function <% $opt{prefix} %>search_customer() {
+
+ var customer_obj = this;
+ var searchrow = this.getAttribute('rownum');
+ var customer = this.value;
+
+ if ( customer == 'searching...' || customer == 'Not found' || customer == '' )
+ return;
+
+ if ( this.getAttribute('magic') == 'nosearch' ) {
+ this.setAttribute('magic', '');
+ return;
+ }
+
+ if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
+ <% $opt{prefix} %>addRow();
+ }
+
+ var custnum_obj = document.getElementById('custnum'+searchrow);
+ custnum_obj.value = 'searching...';
+ custnum_obj.disabled = true;
+ custnum_obj.style.color = '#000000';
+ custnum_obj.style.backgroundColor = '#dddddd';
+
+ var customer_select = document.getElementById('cust_select'+searchrow);
+
+ function search_customer_update(customers) {
+
+ var customerArray = eval('(' + customers + ')');
+
+ custnum_obj.disabled = false;
+ custnum_obj.style.backgroundColor = '#ffffff';
+
+ if ( customerArray.length == 0 ) {
+
+ custnum_obj.value = 'Not found';
+ custnum_obj.style.color = '#ff0000';
+ customer_obj.style.color = '#ff0000';
+
+ customer_obj.style.display = '';
+ customer_select.style.display = 'none';
+
+
+ } else if ( customerArray.length == 1 ) {
+
+ custnum_obj.value = customerArray[0][0];
+ customer_obj.value = customerArray[0][1];
+
+ customer_obj.style.display = '';
+ customer_select.style.display = 'none';
+
+
+ } else {
+
+ custnum_obj.value = 'Multiple'; // or something
+ custnum_obj.style.color = '#ff0000';
+
+ //blank the current list
+ for ( var i = customer_select.length; i >= 0; i-- )
+ customer_select.options[i] = null;
+
+ opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
+
+ //add the multiple customers
+ for ( var s = 0; s < customerArray.length; s++ )
+ opt(customer_select, customerArray[s][0], customerArray[s][1], '#000000');
+
+ opt(customer_select, 'cancel', '(Edit search string)', '#000000');
+
+ customer_obj.style.display = 'none';
+
+ customer_select.style.display = '';
+
+ }
+
+ }
+
+ smart_search( customer, search_customer_update );
+
+ }
+
+ function select_customer() {
+
+ var custnum = this.options[this.selectedIndex].value;
+ var customer = this.options[this.selectedIndex].text;
+
+ var searchrow = this.getAttribute('rownum');
+ var custnum_obj = document.getElementById('custnum'+searchrow);
+ var customer_obj = document.getElementById('customer'+searchrow);
+
+ if ( custnum == '' ) {
+
+ } else if ( custnum == 'cancel' ) {
+
+ custnum_obj.value = '';
+ custnum_obj.style.color = '#000000';
+
+ this.style.display = 'none';
+ customer_obj.style.display = '';
+ customer_obj.focus();
+
+ } else {
+
+ custnum_obj.value = custnum;
+ custnum_obj.style.color = '#000000';
+
+ customer_obj.value = customer;
+ customer_obj.style.color = '#000000';
+
+ this.style.display = 'none';
+ customer_obj.style.display = '';
+
+ }
+
+ }
+
+ function opt(what,value,text,color) {
+ var optionName = new Option(text, value, false, false);
+ optionName.style.color = color;
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+</SCRIPT>
+
+<TABLE ID="<% $opt{prefix} %>OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<TR>
+ <TH>Cust #</TH>
+ <TH>Customer</TH>
+% foreach my $header ( @{$opt{header}} ) {
+ <TH><% $header %></TH>
+% }
+</TR>
+% my $row = 0;
+% for ( $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+
+ <TR>
+
+ <TD>
+ <INPUT TYPE = "text"
+ NAME = "custnum<% $row %>"
+ ID = "custnum<% $row %>"
+ SIZE = 8
+ MAXLENGTH = 12
+ STYLE = "text-align:right;"
+ VALUE = "<% $param->{"custnum$row"} %>"
+ rownum = "<% $row %>"
+ >
+ <SCRIPT TYPE="text/javascript">
+ var custnum_input<% $row %> = document.getElementById("custnum<% $row %>");
+ custnum_input<% $row %>.onfocus = clearhint_custnum;
+ custnum_input<% $row %>.onchange = <% $opt{prefix} %>search_custnum;
+ </SCRIPT>
+ </TD>
+
+ <TD>
+ <INPUT TYPE="text" NAME="customer<% $row %>" ID="customer<% $row %>" SIZE=64 VALUE="<% $param->{"customer$row"} %>" rownum="<% $row %>">
+ <SCRIPT TYPE="text/javascript">
+ var customer_input<% $row %> = document.getElementById("customer<% $row %>");
+ customer_input<% $row %>.onfocus = clearhint_customer;
+ customer_input<% $row %>.onclick = clearhint_customer;
+ customer_input<% $row %>.onchange = <% $opt{prefix} %>search_customer;
+ </SCRIPT>
+ <SELECT NAME="cust_select<% $row %>" ID="cust_select<% $row %>" rownum="<% $row %>" STYLE="color:#ff0000; display:none">
+ </SELECT>
+ <SCRIPT TYPE="text/javascript">
+ var customer_select<% $row %> = document.getElementById("cust_select<% $row %>");
+ customer_select<% $row %>.onchange = select_customer;
+ </SCRIPT>
+ </TD>
+
+% my $col = 0;
+% foreach my $field ( @{$opt{fields}} ) {
+% my $value;
+% if ( ref($field) eq 'CODE' ) {
+% $value = &{$field}($row,$param);
+% } else {
+% $value = $param->{"$field$row"};
+% }
+% my $name = (ref($field) eq 'CODE') ? "column${col}_$row" : "$field$row";
+% my $align = $align{ $opt{align}->[$col] || 'l' };
+% my $size = $sizes->[$col] || 10;
+% my $color = $opt{color}->[$col];
+% my $font = $color ? qq(<FONT COLOR="$color">) : '';
+% my $onchange = '';
+% if ( $opt{footer}->[$col] eq '_TOTAL' ) {
+% $total[$col] += $value;
+% $onchange = $opt{prefix}. "calc_total$col();";
+% $onchange = qq(onchange="$onchange" onkeyup="$onchange");
+% }
+ <TD ALIGN="<% $align %>">
+% if (! $types->[$col] || $types->[$col] eq 'text') {
+ <INPUT TYPE = "text"
+ NAME = "<% $name %>"
+ ID = "<% $name %>"
+ SIZE = "<% $size %>"
+ STYLE = "text-align: <% $align %>;"
+ VALUE = "<% $value %>"
+ <% $onchange %>
+ >
+% } elsif ($types->[$col] eq 'immutable') {
+ <% $font %><% $value %><% $font ? '</FONT>' : '' %>
+ <INPUT TYPE="hidden" NAME="<% $name %>" VALUE="<% $value %>" >
+% } else {
+ Cannot represent unknown type: <% $types->[$col] %>
+% }
+ </TD>
+% $col++;
+% }
+
+ </TR>
+% }
+
+<TR>
+ <TH COLSPAN=2 ID="<% $opt{'prefix'} %>_TOTAL_TOTAL">
+ Total <% $row ? $row-1 : 0 %>
+ <% PL($opt{name_singular} || 'customer', ( $row ? $row-1 : 0 ) ) %>
+ </TH>
+% my $col = 0;
+% foreach my $footer ( @{$opt{footer}} ) {
+% my $align = $align{ $opt{'footer_align'}->[$col] || 'c' };
+% if ($footer eq '_TOTAL' ) {
+% my $id = $opt{'fields'}->[$col];
+% $id = ref($id) ? "column${col}_TOTAL" : "${id}_TOTAL";
+ <TH ALIGN="<% $align %>" ID="<% $id %>">&nbsp;<% sprintf('%.2f', $total[$col] ) %></TH>
+% } else {
+ <TH ALIGN="<% $align %>"><% $footer %></TH>
+% }
+% $col++;
+% }
+</TR>
+
+</TABLE>
+
+<SCRIPT TYPE="text/javascript">
+% my $col = 0;
+% foreach my $footer ( @{$opt{footer}} ) {
+% if ($footer eq '_TOTAL' ) {
+% my $name = $opt{fields}->[$col];
+% $name = ref($name) ? "column$col" : $name;
+ var <% $opt{prefix}.$name %>_CACHE = new Array ();
+ var <% $opt{prefix} %>th_el = document.getElementById("<%$name%>_TOTAL");
+ function <% $opt{prefix} %>calc_total<% $col %>() {
+ var row = 0;
+ var total = 0;
+ for ( var row = 0;
+
+ ( <% $opt{prefix}.$name%>_CACHE[row] =
+ <% $opt{prefix}.$name%>_CACHE[row]
+ || document.getElementById("<%$name%>"+row)
+ ) != null;
+
+ row++
+ )
+ {
+ var value = <%$name%>_CACHE[row].value;
+ value = parseFloat(value);
+ if ( ! isNaN(value) ) {
+ total = total + value;
+ }
+ }
+ <% $opt{prefix} %>th_el.innerHTML = '&nbsp;' + total.toFixed(2);
+
+ }
+% }
+% $col++;
+% }
+</SCRIPT>
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
+ 'subs' => [qw( custnum_search smart_search )],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ var <% $opt{prefix} %>total_el =
+ document.getElementById("<% $opt{'prefix'} %>_TOTAL_TOTAL");
+
+ var <% $opt{prefix} %>rownum = <% $row %>;
+
+ function <% $opt{prefix} %>addRow() {
+
+ var table = document.getElementById('<% $opt{prefix} %>OneTrueTable');
+ var tablebody = table.getElementsByTagName('tbody').item(0);
+
+ var row = table.insertRow(rownum+1);
+
+ var custnum_cell = document.createElement('TD');
+
+ var custnum_input = document.createElement('INPUT');
+ custnum_input.setAttribute('name', 'custnum'+<% $opt{prefix} %>rownum);
+ custnum_input.setAttribute('id', 'custnum'+<% $opt{prefix} %>rownum);
+ custnum_input.style.textAlign = 'right';
+ custnum_input.setAttribute('size', 8);
+ custnum_input.setAttribute('maxlength', 12);
+ custnum_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ custnum_input.onfocus = clearhint_custnum;
+ custnum_input.onchange = <% $opt{prefix} %>search_custnum;
+ custnum_cell.appendChild(custnum_input);
+
+ row.appendChild(custnum_cell);
+
+ var customer_cell = document.createElement('TD');
+
+ var customer_input = document.createElement('INPUT');
+ customer_input.setAttribute('name', 'customer'+<% $opt{prefix} %>rownum);
+ customer_input.setAttribute('id', 'customer'+<% $opt{prefix} %>rownum);
+ customer_input.setAttribute('size', 64);
+ customer_input.setAttribute('value', '(last name or company)' );
+ customer_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ customer_input.onfocus = clearhint_customer;
+ customer_input.onclick = clearhint_customer;
+ customer_input.onchange = <% $opt{prefix} %>search_customer;
+ customer_cell.appendChild(customer_input);
+
+ var customer_select = document.createElement('SELECT');
+ customer_select.setAttribute('name', 'cust_select'+<% $opt{prefix} %>rownum);
+ customer_select.setAttribute('id', 'cust_select'+<% $opt{prefix} %>rownum);
+ customer_select.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ customer_select.style.color = '#ff0000';
+ customer_select.style.display = 'none';
+ customer_select.onchange = select_customer;
+ customer_cell.appendChild(customer_select);
+
+ row.appendChild(customer_cell);
+
+% my $col = 0;
+% foreach my $field ( @{$opt{fields}} ) {
+
+ var my_cell = document.createElement('TD');
+ my_cell.setAttribute('align', '<% $align{ $opt{align}->[$col] || 'l' } %>');
+
+% if ($types->[$col] eq 'immutable') {
+% my $value;
+% if ( ref($field) eq 'CODE' ) {
+% $value = &{$field}($row,$param);
+% } else {
+% $value = $param->{"$field$row"};
+% }
+ var my_text = document.createTextNode('<% $value %>');
+ my_cell.appendChild(my_text);
+% }
+
+ var my_input = document.createElement('INPUT');
+ my_input.setAttribute('name', '<% $field %>'+<% $opt{prefix} %>rownum);
+ my_input.setAttribute('id', '<% $field %>'+<% $opt{prefix} %>rownum);
+ my_input.style.textAlign = '<% $align{ $opt{align}->[$col] || 'l' } %>';
+ my_input.setAttribute('size', <% $sizes->[$col] || 10 %>);
+% if ($types->[$col] eq 'immutable') {
+ my_input.setAttribute('type', 'hidden');
+% }
+% if ( $opt{footer}->[$col] eq '_TOTAL' ) {
+ my_input.onchange = <% $opt{prefix} %>calc_total<%$col%>;
+ my_input.onkeyup = <% $opt{prefix} %>calc_total<%$col%>;
+% }
+ my_cell.appendChild(my_input);
+
+ row.appendChild(my_cell);
+
+% $col++;
+% }
+
+ //update the total # of rows display
+ if ( <% $opt{prefix} %>rownum == 1 ) {
+ <% $opt{prefix} %>total_el.innerHTML =
+ 'Total '
+ + <% $opt{prefix} %>rownum
+ + ' <% $opt{name_singular} || 'customer' %>';
+ } else {
+ <% $opt{prefix} %>total_el.innerHTML =
+ 'Total '
+ + <% $opt{prefix} %>rownum
+ + ' <% PL($opt{name_singular} || 'customer') %>';
+ }
+
+ <% $opt{prefix} %>rownum++;
+
+ }
+
+% unless ($cgi->param('error')) {
+ <% $opt{prefix} %>addRow();
+% }
+</SCRIPT>
+
+<%init>
+
+my(%opt) = @_;
+
+$opt{prefix} = '' unless defined $opt{prefix};
+$opt{prefix} .= '_' if $opt{prefix};
+
+my $types = $opt{'types'} ? [ @{$opt{'types'}} ] : [];
+my $sizes = $opt{'size'} ? [ @{$opt{'size'}} ] : [];
+
+my $param = $opt{param};
+$param = $cgi->Vars if $cgi->param('error');
+
+$opt{$_} ||= [] foreach qw(align color footer footer_align);
+
+my @total = map 0, @{$opt{footer}};
+
+my %align = (
+ 'l' => 'left',
+ 'r' => 'right',
+ 'c' => 'center',
+);
+
+</%init>
diff --git a/httemplate/elements/dashboard-toplist.html b/httemplate/elements/dashboard-toplist.html
new file mode 100644
index 0000000..d8cd7f3
--- /dev/null
+++ b/httemplate/elements/dashboard-toplist.html
@@ -0,0 +1,113 @@
+% if ( $conf->exists('dashboard-toplist') ) {
+
+ <% include('/elements/table-grid.html') %>
+
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = $bgcolor2;
+
+% foreach my $line ( $conf->config('dashboard-toplist') ) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+% if ( $line =~ /^\s*cust_main:\s*(\d+)\s*$/ ) { #customer line
+% my $custnum = $1;
+% my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+% if ( $cust_main ) {
+
+ <TR>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="view/cust_main.cgi?<% $custnum %>"><% $cust_main->name %></A>
+ </TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% include('/elements/mcp_lint.html', 'cust_main'=>$cust_main) %>
+ </TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="right">
+ <FONT SIZE="-1"><A HREF="<% FS::TicketSystem->href_new_ticket($cust_main, join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list ) ) %>">(new ticket)</A></FONT>
+ </TD>
+
+% foreach my $priority ( @custom_priorities, '' ) {
+% my $num =
+% FS::TicketSystem->num_customer_tickets($custnum,$priority);
+% my $ahref = '';
+% $ahref= '<A HREF="'.
+% FS::TicketSystem->href_customer_tickets($custnum,$priority).
+% '">'
+% if $num;
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="right">
+ <% $ahref.$num %></A>
+ </TD>
+% }
+ </TR>
+
+% } else {
+
+ <TR>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ Unknown customer number <% $custnum %>
+ </TD>
+ </TR>
+
+% }
+%
+% } elsif ( $line =~ /^\-\-+$/ ) { #divider
+%
+ <TR>
+ <TH CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>"></TH>
+ </TR>
+
+% next;
+%
+% } elsif ( $line =~ /^\s*$/ ) {
+
+ <TR>
+ <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>" BGCOLOR="<% $bgcolor %>">&nbsp;</TD>
+ </TR>
+
+% } elsif ( $line =~ /^\S/ ) { #label line
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% $line %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Lint</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+% foreach my $priority ( @custom_priorities, '' ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <% $priority || '<i>(none)</i>'%>
+ </TH>
+% }
+ </TR>
+
+% } else { #regular line
+
+ <TR>
+ <TD CLASS="grid" COLSPAN="<% scalar(@custom_priorities) + 4 %>" BGCOLOR="<% $bgcolor %>"><% $line %></TD>
+ </TR>
+
+% }
+
+%
+% }
+
+ </TABLE>
+ <BR>
+
+% }
+<%init>
+
+my $conf = new FS::Conf;
+
+#false laziness w/httemplate/search/cust_main.cgi... care if
+# custom_priority_field becomes anything but a local hack...
+my @custom_priorities = ();
+if ( $conf->config('ticket_system-custom_priority_field')
+ && @{[ $conf->config('ticket_system-custom_priority_field-values') ]} ) {
+ @custom_priorities =
+ $conf->config('ticket_system-custom_priority_field-values');
+}
+
+</%init>
diff --git a/httemplate/elements/error.html b/httemplate/elements/error.html
new file mode 100644
index 0000000..f467de2
--- /dev/null
+++ b/httemplate/elements/error.html
@@ -0,0 +1,4 @@
+% if ( $cgi->param('error') ) {
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $cgi->param('error') |h %></FONT>
+ <BR><BR>
+% }
diff --git a/httemplate/elements/errorpage.html b/httemplate/elements/errorpage.html
new file mode 100644
index 0000000..76a0bf3
--- /dev/null
+++ b/httemplate/elements/errorpage.html
@@ -0,0 +1,11 @@
+<% include("/elements/header.html", "Error") %>
+
+% while (@_) {
+
+<P><FONT SIZE="+1" COLOR="#ff0000"><% shift |h %></FONT>
+
+%}
+
+% $m->flush_buffer();
+% $HTML::Mason::Commands::m->abort();
+% #die "shouldn't fall through to here (mason \$m->abort didn't)";
diff --git a/httemplate/elements/fckeditor/editor/css/behaviors/disablehandles.htc b/httemplate/elements/fckeditor/editor/css/behaviors/disablehandles.htc
new file mode 100644
index 0000000..8dfb661
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/behaviors/disablehandles.htc
@@ -0,0 +1,15 @@
+<public:component lightweight="true">
+
+<script language="javascript">
+
+function CancelEvent()
+{
+ return false ;
+}
+
+this.onresizestart = CancelEvent ;
+this.onbeforeeditfocus = CancelEvent ;
+
+</script>
+
+</public:component>
diff --git a/httemplate/elements/fckeditor/editor/css/behaviors/showtableborders.htc b/httemplate/elements/fckeditor/editor/css/behaviors/showtableborders.htc
new file mode 100644
index 0000000..77418b9
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/behaviors/showtableborders.htc
@@ -0,0 +1,36 @@
+<public:component lightweight="true">
+
+<public:attach event="oncontentready" onevent="ShowBorders()" />
+<public:attach event="onpropertychange" onevent="OnPropertyChange()" />
+
+<script language="javascript">
+
+var oClassRegex = /\s*FCK__ShowTableBorders/ ;
+
+function ShowBorders()
+{
+ if ( this.border == 0 )
+ {
+ if ( !oClassRegex.test( this.className ) )
+ this.className += ' FCK__ShowTableBorders' ;
+ }
+ else
+ {
+ if ( oClassRegex.test( this.className ) )
+ {
+ this.className = this.className.replace( oClassRegex, '' ) ;
+ if ( this.className.length == 0 )
+ this.removeAttribute( 'className', 0 ) ;
+ }
+ }
+}
+
+function OnPropertyChange()
+{
+ if ( event.propertyName == 'border' || event.propertyName == 'className' )
+ ShowBorders.call(this) ;
+}
+
+</script>
+
+</public:component>
diff --git a/httemplate/elements/fckeditor/editor/css/fck_editorarea.css b/httemplate/elements/fckeditor/editor/css/fck_editorarea.css
new file mode 100644
index 0000000..8539aa4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/fck_editorarea.css
@@ -0,0 +1,91 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This is the default CSS file used by the editor area. It defines the
+ * initial font of the editor and background color.
+ *
+ * A user can configure the editor to use another CSS file. Just change
+ * the value of the FCKConfig.EditorAreaCSS key in the configuration
+ * file.
+ */
+
+/*
+ The "body" styles should match your editor web site, mainly regarding
+ background color and font family and size.
+*/
+
+body
+{
+ background-color: #ffffff;
+ padding: 5px 5px 5px 5px;
+ margin: 0px;
+}
+
+body, td
+{
+ font-family: Arial, Verdana, Sans-Serif;
+ font-size: 12px;
+}
+
+a[href]
+{
+ color: #0000FF !important; /* For Firefox... mark as important, otherwise it becomes black */
+}
+
+/*
+ Just uncomment the following block if you want to avoid spaces between
+ paragraphs. Remember to apply the same style in your output front end page.
+*/
+
+/*
+p, ul, li
+{
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+*/
+
+/*
+ The following are some sample styles used in the "Styles" toolbar command.
+ You should instead remove them, and include the styles used by the site
+ you are using the editor in.
+*/
+
+.Bold
+{
+ font-weight: bold;
+}
+
+.Title
+{
+ font-weight: bold;
+ font-size: 18px;
+ color: #cc3300;
+}
+
+.Code
+{
+ border: #8b4513 1px solid;
+ padding-right: 5px;
+ padding-left: 5px;
+ color: #000066;
+ font-family: 'Courier New' , Monospace;
+ background-color: #ff9933;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/css/fck_internal.css b/httemplate/elements/fckeditor/editor/css/fck_internal.css
new file mode 100644
index 0000000..e686560
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/fck_internal.css
@@ -0,0 +1,111 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This CSS Style Sheet defines rules used by the editor for its internal use.
+ */
+
+/* Fix to allow putting the caret at the end of the
+content in Firefox if clicking below the content */
+html
+{
+ min-height: 100%;
+}
+
+
+table.FCK__ShowTableBorders, table.FCK__ShowTableBorders td, table.FCK__ShowTableBorders th
+{
+ border: #d3d3d3 1px solid;
+}
+
+form
+{
+ border: 1px dotted #FF0000;
+ padding: 2px;
+}
+
+.FCK__Flash
+{
+ border: #a9a9a9 1px solid;
+ background-position: center center;
+ background-image: url(images/fck_flashlogo.gif);
+ background-repeat: no-repeat;
+ width: 80px;
+ height: 80px;
+}
+
+/* Empty anchors images */
+.FCK__Anchor
+{
+ border: 1px dotted #00F;
+ background-position: center center;
+ background-image: url(images/fck_anchor.gif);
+ background-repeat: no-repeat;
+ width: 16px;
+ height: 15px;
+ vertical-align: middle;
+}
+
+/* Anchors with content */
+.FCK__AnchorC
+{
+ border: 1px dotted #00F;
+ background-position: 1px center;
+ background-image: url(images/fck_anchor.gif);
+ background-repeat: no-repeat;
+ padding-left: 18px;
+}
+
+/* Any anchor for non-IE, if we combine it
+ with the previous rule IE ignores all. */
+a[name]
+{
+ border: 1px dotted #00F;
+ background-position: 0 center;
+ background-image: url(images/fck_anchor.gif);
+ background-repeat: no-repeat;
+ padding-left: 18px;
+}
+
+.FCK__PageBreak
+{
+ background-position: center center;
+ background-image: url(images/fck_pagebreak.gif);
+ background-repeat: no-repeat;
+ clear: both;
+ display: block;
+ float: none;
+ width: 100%;
+ border-top: #999999 1px dotted;
+ border-bottom: #999999 1px dotted;
+ border-right: 0px;
+ border-left: 0px;
+ height: 5px;
+}
+
+/* Hidden fields */
+.FCK__InputHidden
+{
+ width: 19px;
+ height: 18px;
+ background-image: url(images/fck_hiddenfield.gif);
+ background-repeat: no-repeat;
+ vertical-align: text-bottom;
+ background-position: center center;
+}
diff --git a/httemplate/elements/fckeditor/editor/css/fck_showtableborders_gecko.css b/httemplate/elements/fckeditor/editor/css/fck_showtableborders_gecko.css
new file mode 100644
index 0000000..5947114
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/fck_showtableborders_gecko.css
@@ -0,0 +1,42 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This CSS Style Sheet defines the rules to show table borders on Gecko.
+ */
+
+/* For tables with the "border" attribute set to "0" */
+table[border="0"],
+table[border="0"] > tr > td, table[border="0"] > tr > th,
+table[border="0"] > tbody > tr > td, table[border="0"] > tbody > tr > th,
+table[border="0"] > thead > tr > td, table[border="0"] > thead > tr > th,
+table[border="0"] > tfoot > tr > td, table[border="0"] > tfoot > tr > th
+{
+ border: #d3d3d3 1px dotted ;
+}
+
+/* For tables with no "border" attribute set */
+table:not([border]),
+table:not([border]) > tr > td, table:not([border]) > tr > th,
+table:not([border]) > tbody > tr > td, table:not([border]) > tbody > tr > th,
+table:not([border]) > thead > tr > td, table:not([border]) > thead > tr > th,
+table:not([border]) > tfoot > tr > td, table:not([border]) > tfoot > tr > th
+{
+ border: #d3d3d3 1px dotted ;
+}
diff --git a/httemplate/elements/fckeditor/editor/css/images/fck_anchor.gif b/httemplate/elements/fckeditor/editor/css/images/fck_anchor.gif
new file mode 100644
index 0000000..5aa797b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/images/fck_anchor.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/css/images/fck_flashlogo.gif b/httemplate/elements/fckeditor/editor/css/images/fck_flashlogo.gif
new file mode 100644
index 0000000..141aac4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/images/fck_flashlogo.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/css/images/fck_hiddenfield.gif b/httemplate/elements/fckeditor/editor/css/images/fck_hiddenfield.gif
new file mode 100644
index 0000000..953f643
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/images/fck_hiddenfield.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/css/images/fck_pagebreak.gif b/httemplate/elements/fckeditor/editor/css/images/fck_pagebreak.gif
new file mode 100644
index 0000000..8d1cffd
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/css/images/fck_pagebreak.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.css b/httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.css
new file mode 100644
index 0000000..c1db114
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.css
@@ -0,0 +1,83 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This is the CSS file used for interface details in some dialog
+ * windows.
+ */
+
+.ImagePreviewArea
+{
+ border: #000000 1px solid;
+ overflow: auto;
+ width: 100%;
+ height: 170px;
+ background-color: #ffffff;
+}
+
+.FlashPreviewArea
+{
+ border: #000000 1px solid;
+ padding: 5px;
+ overflow: auto;
+ width: 100%;
+ height: 170px;
+ background-color: #ffffff;
+}
+
+.BtnReset
+{
+ float: left;
+ background-position: center center;
+ background-image: url(images/reset.gif);
+ width: 16px;
+ height: 16px;
+ background-repeat: no-repeat;
+ border: 1px none;
+ font-size: 1px ;
+}
+
+.BtnLocked, .BtnUnlocked
+{
+ float: left;
+ background-position: center center;
+ background-image: url(images/locked.gif);
+ width: 16px;
+ height: 16px;
+ background-repeat: no-repeat;
+ border: none 1px;
+ font-size: 1px ;
+}
+
+.BtnUnlocked
+{
+ background-image: url(images/unlocked.gif);
+}
+
+.BtnOver
+{
+ border: outset 1px;
+ cursor: pointer;
+ cursor: hand;
+}
+
+.FCK__FieldNumeric
+{
+ behavior: url(common/fcknumericfield.htc) ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.js b/httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.js
new file mode 100644
index 0000000..26b5628
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/common/fck_dialog_common.js
@@ -0,0 +1,154 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Useful functions used by almost all dialog window pages.
+ */
+
+var GECKO_BOGUS = '<br type="_moz">' ;
+
+// Gets a element by its Id. Used for shorter coding.
+function GetE( elementId )
+{
+ return document.getElementById( elementId ) ;
+}
+
+function ShowE( element, isVisible )
+{
+ if ( typeof( element ) == 'string' )
+ element = GetE( element ) ;
+ element.style.display = isVisible ? '' : 'none' ;
+}
+
+function SetAttribute( element, attName, attValue )
+{
+ if ( attValue == null || attValue.length == 0 )
+ element.removeAttribute( attName, 0 ) ; // 0 : Case Insensitive
+ else
+ element.setAttribute( attName, attValue, 0 ) ; // 0 : Case Insensitive
+}
+
+function GetAttribute( element, attName, valueIfNull )
+{
+ var oAtt = element.attributes[attName] ;
+
+ if ( oAtt == null || !oAtt.specified )
+ return valueIfNull ? valueIfNull : '' ;
+
+ var oValue = element.getAttribute( attName, 2 ) ;
+
+ if ( oValue == null )
+ oValue = oAtt.nodeValue ;
+
+ return ( oValue == null ? valueIfNull : oValue ) ;
+}
+
+// Functions used by text fiels to accept numbers only.
+function IsDigit( e )
+{
+ if ( !e )
+ e = event ;
+
+ var iCode = ( e.keyCode || e.charCode ) ;
+
+ return (
+ ( iCode >= 48 && iCode <= 57 ) // Numbers
+ || (iCode >= 37 && iCode <= 40) // Arrows
+ || iCode == 8 // Backspace
+ || iCode == 46 // Delete
+ ) ;
+}
+
+String.prototype.Trim = function()
+{
+ return this.replace( /(^\s*)|(\s*$)/g, '' ) ;
+}
+
+String.prototype.StartsWith = function( value )
+{
+ return ( this.substr( 0, value.length ) == value ) ;
+}
+
+String.prototype.Remove = function( start, length )
+{
+ var s = '' ;
+
+ if ( start > 0 )
+ s = this.substring( 0, start ) ;
+
+ if ( start + length < this.length )
+ s += this.substring( start + length , this.length ) ;
+
+ return s ;
+}
+
+String.prototype.ReplaceAll = function( searchArray, replaceArray )
+{
+ var replaced = this ;
+
+ for ( var i = 0 ; i < searchArray.length ; i++ )
+ {
+ replaced = replaced.replace( searchArray[i], replaceArray[i] ) ;
+ }
+
+ return replaced ;
+}
+
+function OpenFileBrowser( url, width, height )
+{
+ // oEditor must be defined.
+
+ var iLeft = ( oEditor.FCKConfig.ScreenWidth - width ) / 2 ;
+ var iTop = ( oEditor.FCKConfig.ScreenHeight - height ) / 2 ;
+
+ var sOptions = "toolbar=no,status=no,resizable=yes,dependent=yes,scrollbars=yes" ;
+ sOptions += ",width=" + width ;
+ sOptions += ",height=" + height ;
+ sOptions += ",left=" + iLeft ;
+ sOptions += ",top=" + iTop ;
+
+ // The "PreserveSessionOnFileBrowser" because the above code could be
+ // blocked by popup blockers.
+ if ( oEditor.FCKConfig.PreserveSessionOnFileBrowser && oEditor.FCKBrowserInfo.IsIE )
+ {
+ // The following change has been made otherwise IE will open the file
+ // browser on a different server session (on some cases):
+ // http://support.microsoft.com/default.aspx?scid=kb;en-us;831678
+ // by Simone Chiaretta.
+ var oWindow = oEditor.window.open( url, 'FCKBrowseWindow', sOptions ) ;
+
+ if ( oWindow )
+ {
+ // Detect Yahoo popup blocker.
+ try
+ {
+ var sTest = oWindow.name ; // Yahoo returns "something", but we can't access it, so detect that and avoid strange errors for the user.
+ oWindow.opener = window ;
+ }
+ catch(e)
+ {
+ alert( oEditor.FCKLang.BrowseServerBlocked ) ;
+ }
+ }
+ else
+ alert( oEditor.FCKLang.BrowseServerBlocked ) ;
+ }
+ else
+ window.open( url, 'FCKBrowseWindow', sOptions ) ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/common/fcknumericfield.htc b/httemplate/elements/fckeditor/editor/dialog/common/fcknumericfield.htc
new file mode 100644
index 0000000..74f26d0
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/common/fcknumericfield.htc
@@ -0,0 +1,24 @@
+<public:component lightweight="true">
+
+<script language="javascript">
+
+function CheckIsDigit()
+{
+ var iCode = event.keyCode ;
+
+ event.returnValue =
+ (
+ ( iCode >= 48 && iCode <= 57 ) // Numbers
+ || (iCode >= 37 && iCode <= 40) // Arrows
+ || iCode == 8 // Backspace
+ || iCode == 46 // Delete
+ ) ;
+
+ return event.returnValue ;
+}
+
+this.onkeypress = CheckIsDigit ;
+
+</script>
+
+</public:component>
diff --git a/httemplate/elements/fckeditor/editor/dialog/common/images/locked.gif b/httemplate/elements/fckeditor/editor/dialog/common/images/locked.gif
new file mode 100644
index 0000000..ea07870
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/common/images/locked.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/common/images/reset.gif b/httemplate/elements/fckeditor/editor/dialog/common/images/reset.gif
new file mode 100644
index 0000000..5e9a2fc
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/common/images/reset.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/common/images/unlocked.gif b/httemplate/elements/fckeditor/editor/dialog/common/images/unlocked.gif
new file mode 100644
index 0000000..801e423
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/common/images/unlocked.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/common/moz-bindings.xml b/httemplate/elements/fckeditor/editor/dialog/common/moz-bindings.xml
new file mode 100644
index 0000000..a457577
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/common/moz-bindings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="numericfield">
+ <implementation>
+ <constructor>
+ this.keypress = CheckIsDigit ;
+ </constructor>
+ <method name="CheckIsDigit">
+ <body>
+ <![CDATA[
+ var iCode = keyCode ;
+
+ var bAccepted =
+ (
+ ( iCode >= 48 && iCode <= 57 ) // Numbers
+ || (iCode >= 37 && iCode <= 40) // Arrows
+ || iCode == 8 // Backspace
+ || iCode == 46 // Delete
+ ) ;
+
+ return bAccepted ;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ <events>
+ <event type="keypress" value="CheckIsDigit()" />
+ </events>
+ </binding>
+</bindings> \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_about.html b/httemplate/elements/fckeditor/editor/dialog/fck_about.html
new file mode 100644
index 0000000..a5825ce
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_about.html
@@ -0,0 +1,155 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * "About" dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCKLang = oEditor.FCKLang ;
+
+window.parent.AddTab( 'About', FCKLang.DlgAboutAboutTab ) ;
+window.parent.AddTab( 'License', FCKLang.DlgAboutLicenseTab ) ;
+window.parent.AddTab( 'BrowserInfo', FCKLang.DlgAboutBrowserInfoTab ) ;
+
+// Function called when a dialog tag is selected.
+function OnDialogTabChange( tabCode )
+{
+ ShowE('divAbout', ( tabCode == 'About' ) ) ;
+ ShowE('divLicense', ( tabCode == 'License' ) ) ;
+ ShowE('divInfo' , ( tabCode == 'BrowserInfo' ) ) ;
+}
+
+function SendEMail()
+{
+ var eMail = 'mailto:' ;
+ eMail += 'fredck' ;
+ eMail += '@' ;
+ eMail += 'fckeditor' ;
+ eMail += '.' ;
+ eMail += 'net' ;
+
+ window.location = eMail ;
+}
+
+window.onload = function()
+{
+ // Translate the dialog box texts.
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ window.parent.SetAutoSize( true ) ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <div id="divAbout">
+ <table cellpadding="0" cellspacing="0" border="0" width="100%" style="height: 100%">
+ <tr>
+ <td>
+ <img alt="" src="fck_about/logo_fckeditor.gif" width="236" height="41" align="left" />
+ <table width="80" border="0" cellspacing="0" cellpadding="5" bgcolor="#ffffff" align="right">
+ <tr>
+ <td align="center" nowrap="nowrap" style="border-right: #000000 1px solid; border-top: #000000 1px solid;
+ border-left: #000000 1px solid; border-bottom: #000000 1px solid">
+ <span fcklang="DlgAboutVersion">version</span>
+ <br />
+ <b>2.4.3</b><br />
+ Build 15657</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr style="height: 100%">
+ <td align="center">
+ &nbsp;<br />
+ <span style="font-size: 14px" dir="ltr">
+ <br />
+ <b><a href="http://www.fckeditor.net/?about" target="_blank" title="Visit the FCKeditor web site">
+ Support <b>Open Source</b> Software</a></b> </span>
+ <br />
+ <br />
+ <br />
+ <span fcklang="DlgAboutInfo">For further information go to</span> <a href="http://www.fckeditor.net/?About"
+ target="_blank">http://www.fckeditor.net/</a>.
+ <br />
+ Copyright &copy; 2003-2007 <a href="#" onclick="SendEMail();">Frederico Caldeira Knabben</a>
+ </td>
+ </tr>
+ <tr>
+ <td align="center">
+ <img alt="" src="fck_about/logo_fredck.gif" width="87" height="36" />
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="divLicense" style="display: none">
+ <p>
+ Licensed under the terms of any of the following licenses at your
+ choice:
+ </p>
+ <ul>
+ <li style="margin-bottom:15px">
+ <b>GNU General Public License</b> Version 2 or later (the "GPL")<br />
+ <a href="http://www.gnu.org/licenses/gpl.html" target="_blank">http://www.gnu.org/licenses/gpl.html</a>
+ </li>
+ <li style="margin-bottom:15px">
+ <b>GNU Lesser General Public License</b> Version 2.1 or later (the "LGPL")<br />
+ <a href="http://www.gnu.org/licenses/lgpl.html" target="_blank">http://www.gnu.org/licenses/lgpl.html</a>
+ </li>
+ <li>
+ <b>Mozilla Public License</b> Version 1.1 or later (the "MPL")<br />
+ <a href="http://www.mozilla.org/MPL/MPL-1.1.html" target="_blank">http://www.mozilla.org/MPL/MPL-1.1.html</a>
+ </li>
+ </ul>
+ </div>
+ <div id="divInfo" style="display: none" dir="ltr">
+ <table align="center" width="80%" border="0">
+ <tr>
+ <td>
+ <script type="text/javascript">
+<!--
+document.write( '<b>User Agent<\/b><br />' + window.navigator.userAgent + '<br /><br />' ) ;
+document.write( '<b>Browser<\/b><br />' + window.navigator.appName + ' ' + window.navigator.appVersion + '<br /><br />' ) ;
+document.write( '<b>Platform<\/b><br />' + window.navigator.platform + '<br /><br />' ) ;
+
+var sUserLang = '?' ;
+
+if ( window.navigator.language )
+ sUserLang = window.navigator.language.toLowerCase() ;
+else if ( window.navigator.userLanguage )
+ sUserLang = window.navigator.userLanguage.toLowerCase() ;
+
+document.write( '<b>User Language<\/b><br />' + sUserLang ) ;
+//-->
+ </script>
+ </td>
+ </tr>
+ </table>
+ </div>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fckeditor.gif b/httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fckeditor.gif
new file mode 100644
index 0000000..b7d6bc6
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fckeditor.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fredck.gif b/httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fredck.gif
new file mode 100644
index 0000000..3108dd9
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_about/logo_fredck.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_anchor.html b/httemplate/elements/fckeditor/editor/dialog/fck_anchor.html
new file mode 100644
index 0000000..a9f2f50
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_anchor.html
@@ -0,0 +1,236 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Anchor dialog window.
+-->
+<html>
+ <head>
+ <title>Anchor Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+var FCKBrowserInfo = oEditor.FCKBrowserInfo ;
+var FCKTools = oEditor.FCKTools ;
+var FCKRegexLib = oEditor.FCKRegexLib ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oFakeImage = FCK.Selection.GetSelectedElement() ;
+var oAnchor ;
+
+if ( oFakeImage )
+{
+ if ( oFakeImage.tagName == 'IMG' && oFakeImage.getAttribute('_fckanchor') )
+ oAnchor = FCK.GetRealElement( oFakeImage ) ;
+ else
+ oFakeImage = null ;
+}
+
+//Search for a real anchor
+if ( !oFakeImage )
+{
+ oAnchor = FCK.Selection.MoveToAncestorNode( 'A' ) ;
+ if ( oAnchor )
+ FCK.Selection.SelectNode( oAnchor ) ;
+}
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oAnchor )
+ GetE('txtName').value = oAnchor.name ;
+ else
+ oAnchor = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ var sNewName = GetE('txtName').value ;
+
+ // Remove any illegal character in a name attribute:
+ // A name should start with a letter, but the validator passes anyway.
+ sNewName = sNewName.replace( /[^\w-_\.:]/g, '_' ) ;
+
+ if ( sNewName.length == 0 )
+ {
+ // Remove the anchor if the user leaves the name blank
+ if ( oAnchor )
+ {
+ RemoveAnchor() ;
+ return true ;
+ }
+
+ alert( oEditor.FCKLang.DlgAnchorErrorName ) ;
+ return false ;
+ }
+
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ if ( oAnchor ) // Modifying an existent anchor.
+ {
+ ReadjustLinksToAnchor( oAnchor.name, sNewName );
+
+ // Buggy explorer, bad bad browser. http://alt-tag.com/blog/archives/2006/02/ie-dom-bugs/
+ // Instead of just replacing the .name for the existing anchor (in order to preserve the content), we must remove the .name
+ // and assign .name, although it won't appear until it's specially processed in fckxhtml.js
+
+ // We remove the previous name
+ oAnchor.removeAttribute( 'name' ) ;
+ // Now we set it, but later we must process it specially
+ oAnchor.name = sNewName ;
+
+ return true ;
+ }
+
+ // Create a new anchor preserving the current selection
+ var aNewAnchors = oEditor.FCK.CreateLink( '#' ) ;
+
+ if ( aNewAnchors.length == 0 )
+ {
+ // Nothing was selected, so now just create a normal A
+ aNewAnchors.push( oEditor.FCK.CreateElement( 'a' ) ) ;
+ }
+ else
+ {
+ // Remove the fake href
+ for ( var i = 0 ; i < aNewAnchors.length ; i++ )
+ aNewAnchors[i].removeAttribute( 'href' ) ;
+ }
+
+ // More than one anchors may have been created, so interact through all of them (see #220).
+ for ( var i = 0 ; i < aNewAnchors.length ; i++ )
+ {
+ oAnchor = aNewAnchors[i] ;
+
+ // Set the name
+ oAnchor.name = sNewName ;
+
+ // IE does require special processing to show the Anchor's image
+ // Opera doesn't allow to select empty anchors
+ if ( FCKBrowserInfo.IsIE || FCKBrowserInfo.IsOpera )
+ {
+ if ( oAnchor.innerHTML != '' )
+ {
+ if ( FCKBrowserInfo.IsIE )
+ oAnchor.className += ' FCK__AnchorC' ;
+ }
+ else
+ {
+ // Create a fake image for both IE and Opera
+ var oImg = oEditor.FCKDocumentProcessor_CreateFakeImage( 'FCK__Anchor', oAnchor.cloneNode(true) ) ;
+ oImg.setAttribute( '_fckanchor', 'true', 0 ) ;
+
+ oAnchor.parentNode.insertBefore( oImg, oAnchor ) ;
+ oAnchor.parentNode.removeChild( oAnchor ) ;
+ }
+
+ }
+ }
+
+ return true ;
+}
+
+// Removes the current anchor from the document
+function RemoveAnchor()
+{
+ // If it's also a link, then just remove the name and exit
+ if ( oAnchor.href.length != 0 )
+ {
+ oAnchor.removeAttribute( 'name' ) ;
+ // Remove temporary class for IE
+ if ( FCKBrowserInfo.IsIE )
+ oAnchor.className = oAnchor.className.replace( FCKRegexLib.FCK_Class, '' ) ;
+ return ;
+ }
+
+ // We need to remove the anchor
+ // If we got a fake image, then just remove it and we're done
+ if ( oFakeImage )
+ {
+ oFakeImage.parentNode.removeChild( oFakeImage ) ;
+ return ;
+ }
+ // Empty anchor, so just remove it
+ if ( oAnchor.innerHTML.length == 0 )
+ {
+ oAnchor.parentNode.removeChild( oAnchor ) ;
+ return ;
+ }
+ // Anchor with content, leave the content
+ FCKTools.RemoveOuterTags( oAnchor ) ;
+}
+
+// Checks all the links in the current page pointing to the current name and changes them to the new name
+function ReadjustLinksToAnchor( sCurrent, sNew )
+{
+ var oDoc = FCK.EditorDocument ;
+
+ var aLinks = oDoc.getElementsByTagName( 'A' ) ;
+
+ var sReference = '#' + sCurrent ;
+ // The url of the document, so we check absolute and partial references.
+ var sFullReference = oDoc.location.href.replace( /(#.*$)/, '') ;
+ sFullReference += sReference ;
+
+ var oLink ;
+ var i = aLinks.length - 1 ;
+ while ( i >= 0 && ( oLink = aLinks[i--] ) )
+ {
+ var sHRef = oLink.getAttribute( '_fcksavedurl' ) ;
+ if ( sHRef == null )
+ sHRef = oLink.getAttribute( 'href' , 2 ) || '' ;
+
+ if ( sHRef == sReference || sHRef == sFullReference )
+ {
+ oLink.href = '#' + sNew ;
+ SetAttribute( oLink, '_fcksavedurl', '#' + sNew ) ;
+ }
+ }
+}
+
+ </script>
+ </head>
+ <body style="OVERFLOW: hidden" scroll="no">
+ <table height="100%" width="100%">
+ <tr>
+ <td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="80%">
+ <tr>
+ <td>
+ <span fckLang="DlgAnchorName">Anchor Name</span><BR>
+ <input id="txtName" style="WIDTH: 100%" type="text">
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_button.html b/httemplate/elements/fckeditor/editor/dialog/fck_button.html
new file mode 100644
index 0000000..6e5c2bb
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_button.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Button dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Button Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oActiveEl = oEditor.FCKSelection.GetSelectedElement() ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oActiveEl && oActiveEl.tagName.toUpperCase() == "INPUT" && ( oActiveEl.type == "button" || oActiveEl.type == "submit" || oActiveEl.type == "reset" ) )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtValue').value = oActiveEl.value ;
+ GetE('txtType').value = oActiveEl.type ;
+
+ GetE('txtType').disabled = true ;
+ }
+ else
+ oActiveEl = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ if ( !oActiveEl )
+ {
+ oActiveEl = oEditor.FCK.EditorDocument.createElement( 'INPUT' ) ;
+ oActiveEl.type = GetE('txtType').value ;
+ oActiveEl = oEditor.FCK.InsertElementAndGetIt( oActiveEl ) ;
+ }
+
+ oActiveEl.name = GetE('txtName').value ;
+ SetAttribute( oActiveEl, 'value', GetE('txtValue').value ) ;
+
+ return true ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <table width="100%" style="height: 100%">
+ <tr>
+ <td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="80%">
+ <tr>
+ <td colspan="">
+ <span fcklang="DlgCheckboxName">Name</span><br />
+ <input type="text" size="20" id="txtName" style="width: 100%" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgButtonText">Text (Value)</span><br />
+ <input type="text" id="txtValue" style="width: 100%" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgButtonType">Type</span><br />
+ <select id="txtType">
+ <option fcklang="DlgButtonTypeBtn" value="button" selected="selected">Button</option>
+ <option fcklang="DlgButtonTypeSbm" value="submit">Submit</option>
+ <option fcklang="DlgButtonTypeRst" value="reset">Reset</option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_checkbox.html b/httemplate/elements/fckeditor/editor/dialog/fck_checkbox.html
new file mode 100644
index 0000000..ac7b4f3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_checkbox.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Checkbox dialog window.
+-->
+<html>
+ <head>
+ <title>Checkbox Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oActiveEl = oEditor.FCKSelection.GetSelectedElement() ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oActiveEl && oActiveEl.tagName == 'INPUT' && oActiveEl.type == 'checkbox' )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtValue').value = oEditor.FCKBrowserInfo.IsIE ? oActiveEl.value : GetAttribute( oActiveEl, 'value' ) ;
+ GetE('txtSelected').checked = oActiveEl.checked ;
+ }
+ else
+ oActiveEl = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ if ( !oActiveEl )
+ {
+ oActiveEl = oEditor.FCK.EditorDocument.createElement( 'INPUT' ) ;
+ oActiveEl.type = 'checkbox' ;
+ oActiveEl = oEditor.FCK.InsertElementAndGetIt( oActiveEl ) ;
+ }
+
+ if ( GetE('txtName').value.length > 0 )
+ oActiveEl.name = GetE('txtName').value ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ oActiveEl.value = GetE('txtValue').value ;
+ else
+ SetAttribute( oActiveEl, 'value', GetE('txtValue').value ) ;
+
+ var bIsChecked = GetE('txtSelected').checked ;
+ SetAttribute( oActiveEl, 'checked', bIsChecked ? 'checked' : null ) ; // For Firefox
+ oActiveEl.checked = bIsChecked ;
+
+ return true ;
+}
+
+ </script>
+ </head>
+ <body style="OVERFLOW: hidden" scroll="no">
+ <table height="100%" width="100%">
+ <tr>
+ <td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="80%">
+ <tr>
+ <td>
+ <span fckLang="DlgCheckboxName">Name</span><br>
+ <input type="text" size="20" id="txtName" style="WIDTH: 100%">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fckLang="DlgCheckboxValue">Value</span><br>
+ <input type="text" size="20" id="txtValue" style="WIDTH: 100%">
+ </td>
+ </tr>
+ <tr>
+ <td><input type="checkbox" id="txtSelected"><label for="txtSelected" fckLang="DlgCheckboxSelected">Checked</label></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_colorselector.html b/httemplate/elements/fckeditor/editor/dialog/fck_colorselector.html
new file mode 100644
index 0000000..1778f51
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_colorselector.html
@@ -0,0 +1,171 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Color Selection dialog window.
+-->
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <style TYPE="text/css">
+ #ColorTable { cursor: pointer ; cursor: hand ; }
+ #hicolor { height: 74px ; width: 74px ; border-width: 1px ; border-style: solid ; }
+ #hicolortext { width: 75px ; text-align: right ; margin-bottom: 7px ; }
+ #selhicolor { height: 20px ; width: 74px ; border-width: 1px ; border-style: solid ; }
+ #selcolor { width: 75px ; height: 20px ; margin-top: 0px ; margin-bottom: 7px ; }
+ #btnClear { width: 75px ; height: 22px ; margin-bottom: 6px ; }
+ .ColorCell { height: 15px ; width: 15px ; }
+ </style>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+function OnLoad()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ CreateColorTable() ;
+
+ window.parent.SetOkButton( true ) ;
+ window.parent.SetAutoSize( true ) ;
+}
+
+function CreateColorTable()
+{
+ // Get the target table.
+ var oTable = document.getElementById('ColorTable') ;
+
+ // Create the base colors array.
+ var aColors = ['00','33','66','99','cc','ff'] ;
+
+ // This function combines two ranges of three values from the color array into a row.
+ function AppendColorRow( rangeA, rangeB )
+ {
+ for ( var i = rangeA ; i < rangeA + 3 ; i++ )
+ {
+ var oRow = oTable.insertRow(-1) ;
+
+ for ( var j = rangeB ; j < rangeB + 3 ; j++ )
+ {
+ for ( var n = 0 ; n < 6 ; n++ )
+ {
+ AppendColorCell( oRow, '#' + aColors[j] + aColors[n] + aColors[i] ) ;
+ }
+ }
+ }
+ }
+
+ // This function create a single color cell in the color table.
+ function AppendColorCell( targetRow, color )
+ {
+ var oCell = targetRow.insertCell(-1) ;
+ oCell.className = 'ColorCell' ;
+ oCell.bgColor = color ;
+
+ oCell.onmouseover = function()
+ {
+ document.getElementById('hicolor').style.backgroundColor = this.bgColor ;
+ document.getElementById('hicolortext').innerHTML = this.bgColor ;
+ }
+
+ oCell.onclick = function()
+ {
+ document.getElementById('selhicolor').style.backgroundColor = this.bgColor ;
+ document.getElementById('selcolor').value = this.bgColor ;
+ }
+ }
+
+ AppendColorRow( 0, 0 ) ;
+ AppendColorRow( 3, 0 ) ;
+ AppendColorRow( 0, 3 ) ;
+ AppendColorRow( 3, 3 ) ;
+
+ // Create the last row.
+ var oRow = oTable.insertRow(-1) ;
+
+ // Create the gray scale colors cells.
+ for ( var n = 0 ; n < 6 ; n++ )
+ {
+ AppendColorCell( oRow, '#' + aColors[n] + aColors[n] + aColors[n] ) ;
+ }
+
+ // Fill the row with black cells.
+ for ( var i = 0 ; i < 12 ; i++ )
+ {
+ AppendColorCell( oRow, '#000000' ) ;
+ }
+}
+
+function Clear()
+{
+ document.getElementById('selhicolor').style.backgroundColor = '' ;
+ document.getElementById('selcolor').value = '' ;
+}
+
+function ClearActual()
+{
+ document.getElementById('hicolor').style.backgroundColor = '' ;
+ document.getElementById('hicolortext').innerHTML = '&nbsp;' ;
+}
+
+function UpdateColor()
+{
+ try { document.getElementById('selhicolor').style.backgroundColor = document.getElementById('selcolor').value ; }
+ catch (e) { Clear() ; }
+}
+
+function Ok()
+{
+ if ( typeof(window.parent.dialogArguments.CustomValue) == 'function' )
+ window.parent.dialogArguments.CustomValue( document.getElementById('selcolor').value ) ;
+
+ return true ;
+}
+ </script>
+ </head>
+ <body onload="OnLoad()" scroll="no" style="OVERFLOW: hidden">
+ <table cellpadding="0" cellspacing="0" border="0" width="100%" height="100%">
+ <tr>
+ <td align="center" valign="middle">
+ <table border="0" cellspacing="5" cellpadding="0" width="100%">
+ <tr>
+ <td valign="top" align="center" nowrap width="100%">
+ <table id="ColorTable" border="0" cellspacing="0" cellpadding="0" width="270" onmouseout="ClearActual();">
+ </table>
+ </td>
+ <td valign="top" align="left" nowrap>
+ <span fckLang="DlgColorHighlight">Highlight</span>
+ <div id="hicolor"></div>
+ <div id="hicolortext">&nbsp;</div>
+ <span fckLang="DlgColorSelected">Selected</span>
+ <div id="selhicolor"></div>
+ <input id="selcolor" type="text" maxlength="20" onchange="UpdateColor();">
+ <br>
+ <input id="btnClear" type="button" fckLang="DlgColorBtnClear" value="Clear" onclick="Clear();" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_docprops.html b/httemplate/elements/fckeditor/editor/dialog/fck_docprops.html
new file mode 100644
index 0000000..3083466
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_docprops.html
@@ -0,0 +1,600 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Link dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+var FCKLang = oEditor.FCKLang ;
+var FCKConfig = oEditor.FCKConfig ;
+
+//#### Dialog Tabs
+
+// Set the dialog tabs.
+window.parent.AddTab( 'General' , FCKLang.DlgDocGeneralTab ) ;
+window.parent.AddTab( 'Background' , FCKLang.DlgDocBackTab ) ;
+window.parent.AddTab( 'Colors' , FCKLang.DlgDocColorsTab ) ;
+window.parent.AddTab( 'Meta' , FCKLang.DlgDocMetaTab ) ;
+
+// Function called when a dialog tag is selected.
+function OnDialogTabChange( tabCode )
+{
+ ShowE( 'divGeneral' , ( tabCode == 'General' ) ) ;
+ ShowE( 'divBackground' , ( tabCode == 'Background' ) ) ;
+ ShowE( 'divColors' , ( tabCode == 'Colors' ) ) ;
+ ShowE( 'divMeta' , ( tabCode == 'Meta' ) ) ;
+
+ ShowE( 'ePreview' , ( tabCode == 'Background' || tabCode == 'Colors' ) ) ;
+}
+
+//#### Get Base elements from the document: BEGIN
+
+// The HTML element of the document.
+var oHTML = FCK.EditorDocument.getElementsByTagName('html')[0] ;
+
+// The HEAD element of the document.
+var oHead = oHTML.getElementsByTagName('head')[0] ;
+
+var oBody = FCK.EditorDocument.body ;
+
+// This object contains all META tags defined in the document.
+var oMetaTags = new Object() ;
+
+// Get all META tags defined in the document.
+AppendMetaCollection( oMetaTags, oHead.getElementsByTagName('meta') ) ;
+AppendMetaCollection( oMetaTags, oHead.getElementsByTagName('fck:meta') ) ;
+
+function AppendMetaCollection( targetObject, metaCollection )
+{
+ // Loop throw all METAs and put it in the HashTable.
+ for ( var i = 0 ; i < metaCollection.length ; i++ )
+ {
+ // Try to get the "name" attribute.
+ var sName = GetAttribute( metaCollection[i], 'name', GetAttribute( metaCollection[i], '___fcktoreplace:name', '' ) ) ;
+
+ // If no "name", try with the "http-equiv" attribute.
+ if ( sName.length == 0 )
+ {
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ {
+ // Get the http-equiv value from the outerHTML.
+ var oHttpEquivMatch = metaCollection[i].outerHTML.match( oEditor.FCKRegexLib.MetaHttpEquiv ) ;
+ if ( oHttpEquivMatch )
+ sName = oHttpEquivMatch[1] ;
+ }
+ else
+ sName = GetAttribute( metaCollection[i], 'http-equiv', '' ) ;
+ }
+
+ if ( sName.length > 0 )
+ targetObject[ sName.toLowerCase() ] = metaCollection[i] ;
+ }
+}
+
+//#### END
+
+// Set a META tag in the document.
+function SetMetadata( name, content, isHttp )
+{
+ if ( content.length == 0 )
+ {
+ RemoveMetadata( name ) ;
+ return ;
+ }
+
+ var oMeta = oMetaTags[ name.toLowerCase() ] ;
+
+ if ( !oMeta )
+ {
+ oMeta = oHead.appendChild( FCK.EditorDocument.createElement('META') ) ;
+
+ if ( isHttp )
+ SetAttribute( oMeta, 'http-equiv', name ) ;
+ else
+ {
+ // On IE, it is not possible to set the "name" attribute of the META tag.
+ // So a temporary attribute is used and it is replaced when getting the
+ // editor's HTML/XHTML value. This is sad, I know :(
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ SetAttribute( oMeta, '___fcktoreplace:name', name ) ;
+ else
+ SetAttribute( oMeta, 'name', name ) ;
+ }
+
+ oMetaTags[ name.toLowerCase() ] = oMeta ;
+ }
+
+ SetAttribute( oMeta, 'content', content ) ;
+// oMeta.content = content ;
+}
+
+function RemoveMetadata( name )
+{
+ var oMeta = oMetaTags[ name.toLowerCase() ] ;
+
+ if ( oMeta && oMeta != null )
+ {
+ oMeta.parentNode.removeChild( oMeta ) ;
+ oMetaTags[ name.toLowerCase() ] = null ;
+ }
+}
+
+function GetMetadata( name )
+{
+ var oMeta = oMetaTags[ name.toLowerCase() ] ;
+
+ if ( oMeta && oMeta != null )
+ return oMeta.getAttribute( 'content', 2 ) ;
+ else
+ return '' ;
+}
+
+window.onload = function ()
+{
+ // Show/Hide the "Browse Server" button.
+ GetE('tdBrowse').style.display = oEditor.FCKConfig.ImageBrowser ? "" : "none";
+
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage( document ) ;
+
+ FillFields() ;
+
+ UpdatePreview() ;
+
+ // Show the "Ok" button.
+ window.parent.SetOkButton( true ) ;
+
+ window.parent.SetAutoSize( true ) ;
+}
+
+function FillFields()
+{
+ // ### General Info
+ GetE('txtPageTitle').value = FCK.EditorDocument.title ;
+
+ GetE('selDirection').value = GetAttribute( oHTML, 'dir', '' ) ;
+ GetE('txtLang').value = GetAttribute( oHTML, 'xml:lang', GetAttribute( oHTML, 'lang', '' ) ) ; // "xml:lang" takes precedence to "lang".
+
+ // Character Set Encoding.
+// if ( oEditor.FCKBrowserInfo.IsIE )
+// var sCharSet = FCK.EditorDocument.charset ;
+// else
+ var sCharSet = GetMetadata( 'Content-Type' ) ;
+
+ if ( sCharSet != null && sCharSet.length > 0 )
+ {
+// if ( !oEditor.FCKBrowserInfo.IsIE )
+ sCharSet = sCharSet.match( /[^=]*$/ ) ;
+
+ GetE('selCharSet').value = sCharSet ;
+
+ if ( GetE('selCharSet').selectedIndex == -1 )
+ {
+ GetE('selCharSet').value = '...' ;
+ GetE('txtCustomCharSet').value = sCharSet ;
+
+ CheckOther( GetE('selCharSet'), 'txtCustomCharSet' ) ;
+ }
+ }
+
+ // Document Type.
+ if ( FCK.DocTypeDeclaration && FCK.DocTypeDeclaration.length > 0 )
+ {
+ GetE('selDocType').value = FCK.DocTypeDeclaration ;
+
+ if ( GetE('selDocType').selectedIndex == -1 )
+ {
+ GetE('selDocType').value = '...' ;
+ GetE('txtDocType').value = FCK.DocTypeDeclaration ;
+
+ CheckOther( GetE('selDocType'), 'txtDocType' ) ;
+ }
+ }
+
+ // Document Type.
+ GetE('chkIncXHTMLDecl').checked = ( FCK.XmlDeclaration && FCK.XmlDeclaration.length > 0 ) ;
+
+ // ### Background
+ GetE('txtBackColor').value = GetAttribute( oBody, 'bgColor' , '' ) ;
+ GetE('txtBackImage').value = GetAttribute( oBody, 'background' , '' ) ;
+ GetE('chkBackNoScroll').checked = ( GetAttribute( oBody, 'bgProperties', '' ).toLowerCase() == 'fixed' ) ;
+
+ // ### Colors
+ GetE('txtColorText').value = GetAttribute( oBody, 'text' , '' ) ;
+ GetE('txtColorLink').value = GetAttribute( oBody, 'link' , '' ) ;
+ GetE('txtColorVisited').value = GetAttribute( oBody, 'vLink' , '' ) ;
+ GetE('txtColorActive').value = GetAttribute( oBody, 'aLink' , '' ) ;
+
+ // ### Margins
+ GetE('txtMarginTop').value = GetAttribute( oBody, 'topMargin' , '' ) ;
+ GetE('txtMarginLeft').value = GetAttribute( oBody, 'leftMargin' , '' ) ;
+ GetE('txtMarginRight').value = GetAttribute( oBody, 'rightMargin' , '' ) ;
+ GetE('txtMarginBottom').value = GetAttribute( oBody, 'bottomMargin' , '' ) ;
+
+ // ### Meta Data
+ GetE('txtMetaKeywords').value = GetMetadata( 'keywords' ) ;
+ GetE('txtMetaDescription').value = GetMetadata( 'description' ) ;
+ GetE('txtMetaAuthor').value = GetMetadata( 'author' ) ;
+ GetE('txtMetaCopyright').value = GetMetadata( 'copyright' ) ;
+}
+
+// Called when the "Ok" button is clicked.
+function Ok()
+{
+ // ### General Info
+ FCK.EditorDocument.title = GetE('txtPageTitle').value ;
+
+ var oHTML = FCK.EditorDocument.getElementsByTagName('html')[0] ;
+
+ SetAttribute( oHTML, 'dir' , GetE('selDirection').value ) ;
+ SetAttribute( oHTML, 'lang' , GetE('txtLang').value ) ;
+ SetAttribute( oHTML, 'xml:lang' , GetE('txtLang').value ) ;
+
+ // Character Set Enconding.
+ var sCharSet = GetE('selCharSet').value ;
+ if ( sCharSet == '...' )
+ sCharSet = GetE('txtCustomCharSet').value ;
+
+ if ( sCharSet.length > 0 )
+ sCharSet = 'text/html; charset=' + sCharSet ;
+
+// if ( oEditor.FCKBrowserInfo.IsIE )
+// FCK.EditorDocument.charset = sCharSet ;
+// else
+ SetMetadata( 'Content-Type', sCharSet, true ) ;
+
+ // Document Type
+ var sDocType = GetE('selDocType').value ;
+ if ( sDocType == '...' )
+ sDocType = GetE('txtDocType').value ;
+
+ FCK.DocTypeDeclaration = sDocType ;
+
+ // XHTML Declarations.
+ if ( GetE('chkIncXHTMLDecl').checked )
+ {
+ if ( sCharSet.length == 0 )
+ sCharSet = 'utf-8' ;
+
+ FCK.XmlDeclaration = '<?xml version="1.0" encoding="' + sCharSet + '"?>' ;
+
+ SetAttribute( oHTML, 'xmlns', 'http://www.w3.org/1999/xhtml' ) ;
+ }
+ else
+ {
+ FCK.XmlDeclaration = null ;
+ oHTML.removeAttribute( 'xmlns', 0 ) ;
+ }
+
+ // ### Background
+ SetAttribute( oBody, 'bgcolor' , GetE('txtBackColor').value ) ;
+ SetAttribute( oBody, 'background' , GetE('txtBackImage').value ) ;
+ SetAttribute( oBody, 'bgproperties' , GetE('chkBackNoScroll').checked ? 'fixed' : '' ) ;
+
+ // ### Colors
+ SetAttribute( oBody, 'text' , GetE('txtColorText').value ) ;
+ SetAttribute( oBody, 'link' , GetE('txtColorLink').value ) ;
+ SetAttribute( oBody, 'vlink', GetE('txtColorVisited').value ) ;
+ SetAttribute( oBody, 'alink', GetE('txtColorActive').value ) ;
+
+ // ### Margins
+ SetAttribute( oBody, 'topmargin' , GetE('txtMarginTop').value ) ;
+ SetAttribute( oBody, 'leftmargin' , GetE('txtMarginLeft').value ) ;
+ SetAttribute( oBody, 'rightmargin' , GetE('txtMarginRight').value ) ;
+ SetAttribute( oBody, 'bottommargin' , GetE('txtMarginBottom').value ) ;
+
+ // ### Meta data
+ SetMetadata( 'keywords' , GetE('txtMetaKeywords').value ) ;
+ SetMetadata( 'description' , GetE('txtMetaDescription').value ) ;
+ SetMetadata( 'author' , GetE('txtMetaAuthor').value ) ;
+ SetMetadata( 'copyright' , GetE('txtMetaCopyright').value ) ;
+
+ return true ;
+}
+
+var bPreviewIsLoaded = false ;
+var oPreviewWindow ;
+var oPreviewBody ;
+
+// Called by the Preview page when loaded.
+function OnPreviewLoad( previewWindow, previewBody )
+{
+ oPreviewWindow = previewWindow ;
+ oPreviewBody = previewBody ;
+
+ bPreviewIsLoaded = true ;
+ UpdatePreview() ;
+}
+
+function UpdatePreview()
+{
+ if ( !bPreviewIsLoaded )
+ return ;
+
+ // ### Background
+ SetAttribute( oPreviewBody, 'bgcolor' , GetE('txtBackColor').value ) ;
+ SetAttribute( oPreviewBody, 'background' , GetE('txtBackImage').value ) ;
+ SetAttribute( oPreviewBody, 'bgproperties' , GetE('chkBackNoScroll').checked ? 'fixed' : '' ) ;
+
+ // ### Colors
+ SetAttribute( oPreviewBody, 'text', GetE('txtColorText').value ) ;
+
+ oPreviewWindow.SetLinkColor( GetE('txtColorLink').value ) ;
+ oPreviewWindow.SetVisitedColor( GetE('txtColorVisited').value ) ;
+ oPreviewWindow.SetActiveColor( GetE('txtColorActive').value ) ;
+}
+
+function CheckOther( combo, txtField )
+{
+ var bNotOther = ( combo.value != '...' ) ;
+
+ GetE(txtField).style.backgroundColor = ( bNotOther ? '#cccccc' : '' ) ;
+ GetE(txtField).disabled = bNotOther ;
+}
+
+function SetColor( inputId, color )
+{
+ GetE( inputId ).value = color + '' ;
+ UpdatePreview() ;
+}
+
+function SelectBackColor( color ) { SetColor('txtBackColor', color ) ; }
+function SelectColorText( color ) { SetColor('txtColorText', color ) ; }
+function SelectColorLink( color ) { SetColor('txtColorLink', color ) ; }
+function SelectColorVisited( color ) { SetColor('txtColorVisited', color ) ; }
+function SelectColorActive( color ) { SetColor('txtColorActive', color ) ; }
+
+function SelectColor( wich )
+{
+ switch ( wich )
+ {
+ case 'Back' : oEditor.FCKDialog.OpenDialog( 'FCKDialog_Color', FCKLang.DlgColorTitle, 'dialog/fck_colorselector.html', 400, 330, SelectBackColor, window ) ; return ;
+ case 'ColorText' : oEditor.FCKDialog.OpenDialog( 'FCKDialog_Color', FCKLang.DlgColorTitle, 'dialog/fck_colorselector.html', 400, 330, SelectColorText, window ) ; return ;
+ case 'ColorLink' : oEditor.FCKDialog.OpenDialog( 'FCKDialog_Color', FCKLang.DlgColorTitle, 'dialog/fck_colorselector.html', 400, 330, SelectColorLink, window ) ; return ;
+ case 'ColorVisited' : oEditor.FCKDialog.OpenDialog( 'FCKDialog_Color', FCKLang.DlgColorTitle, 'dialog/fck_colorselector.html', 400, 330, SelectColorVisited, window ) ; return ;
+ case 'ColorActive' : oEditor.FCKDialog.OpenDialog( 'FCKDialog_Color', FCKLang.DlgColorTitle, 'dialog/fck_colorselector.html', 400, 330, SelectColorActive, window ) ; return ;
+ }
+}
+
+function BrowseServerBack()
+{
+ OpenFileBrowser( FCKConfig.ImageBrowserURL, FCKConfig.ImageBrowserWindowWidth, FCKConfig.ImageBrowserWindowHeight ) ;
+}
+
+function SetUrl( url )
+{
+ GetE('txtBackImage').value = url ;
+ UpdatePreview() ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <table cellspacing="0" cellpadding="0" width="100%" border="0" style="height: 100%">
+ <tr>
+ <td valign="top" style="height: 100%">
+ <div id="divGeneral">
+ <span fcklang="DlgDocPageTitle">Page Title</span><br />
+ <input id="txtPageTitle" style="width: 100%" type="text" />
+ <br />
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td>
+ <span fcklang="DlgDocLangDir">Language Direction</span><br />
+ <select id="selDirection">
+ <option value="" selected="selected"></option>
+ <option value="ltr" fcklang="DlgDocLangDirLTR">Left to Right (LTR)</option>
+ <option value="rtl" fcklang="DlgDocLangDirRTL">Right to Left (RTL)</option>
+ </select>
+ </td>
+ <td>
+ &nbsp;&nbsp;&nbsp;</td>
+ <td>
+ <span fcklang="DlgDocLangCode">Language Code</span><br />
+ <input id="txtLang" type="text" />
+ </td>
+ </tr>
+ </table>
+ <br />
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td style="white-space: nowrap">
+ <span fcklang="DlgDocCharSet">Character Set Encoding</span><br />
+ <select id="selCharSet" onchange="CheckOther( this, 'txtCustomCharSet' );">
+ <option value="" selected="selected"></option>
+ <option value="us-ascii">ASCII</option>
+ <option fcklang="DlgDocCharSetCE" value="iso-8859-2">Central European</option>
+ <option fcklang="DlgDocCharSetCT" value="big5">Chinese Traditional (Big5)</option>
+ <option fcklang="DlgDocCharSetCR" value="iso-8859-5">Cyrillic</option>
+ <option fcklang="DlgDocCharSetGR" value="iso-8859-7">Greek</option>
+ <option fcklang="DlgDocCharSetJP" value="iso-2022-jp">Japanese</option>
+ <option fcklang="DlgDocCharSetKR" value="iso-2022-kr">Korean</option>
+ <option fcklang="DlgDocCharSetTR" value="iso-8859-9">Turkish</option>
+ <option fcklang="DlgDocCharSetUN" value="utf-8">Unicode (UTF-8)</option>
+ <option fcklang="DlgDocCharSetWE" value="iso-8859-1">Western European</option>
+ <option fcklang="DlgOpOther" value="...">&lt;Other&gt;</option>
+ </select>
+ </td>
+ <td>
+ &nbsp;&nbsp;&nbsp;</td>
+ <td width="100%">
+ <span fcklang="DlgDocCharSetOther">Other Character Set Encoding</span><br />
+ <input id="txtCustomCharSet" style="width: 100%; background-color: #cccccc" disabled="disabled"
+ type="text" />
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgDocDocType">Document Type Heading</span><br />
+ <select id="selDocType" name="selDocType" onchange="CheckOther( this, 'txtDocType' );">
+ <option value="" selected="selected"></option>
+ <option value='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'>HTML
+ 4.01 Transitional</option>
+ <option value='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'>
+ HTML 4.01 Strict</option>
+ <option value='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'>
+ HTML 4.01 Frameset</option>
+ <option value='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'>
+ XHTML 1.0 Transitional</option>
+ <option value='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'>
+ XHTML 1.0 Strict</option>
+ <option value='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'>
+ XHTML 1.0 Frameset</option>
+ <option value='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'>
+ XHTML 1.1</option>
+ <option value='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'>HTML 3.2</option>
+ <option value='<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">'>HTML 2.0</option>
+ <option value="..." fcklang="DlgOpOther">&lt;Other&gt;</option>
+ </select>
+ </td>
+ <td>
+ </td>
+ <td width="100%">
+ <span fcklang="DlgDocDocTypeOther">Other Document Type Heading</span><br />
+ <input id="txtDocType" style="width: 100%; background-color: #cccccc" disabled="disabled"
+ type="text" />
+ </td>
+ </tr>
+ </table>
+ <br />
+ <input id="chkIncXHTMLDecl" type="checkbox" />
+ <label for="chkIncXHTMLDecl" fcklang="DlgDocIncXHTML">
+ Include XHTML Declarations</label>
+ </div>
+ <div id="divBackground" style="display: none">
+ <span fcklang="DlgDocBgColor">Background Color</span><br />
+ <input id="txtBackColor" type="text" onchange="UpdatePreview();" onkeyup="UpdatePreview();" />&nbsp;<input
+ id="btnSelBackColor" onclick="SelectColor( 'Back' )" type="button" value="Select..."
+ fcklang="DlgCellBtnSelect" /><br />
+ <br />
+ <span fcklang="DlgDocBgImage">Background Image URL</span><br />
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td width="100%">
+ <input id="txtBackImage" style="width: 100%" type="text" onchange="UpdatePreview();"
+ onkeyup="UpdatePreview();" /></td>
+ <td id="tdBrowse" nowrap="nowrap">
+ &nbsp;<input id="btnBrowse" onclick="BrowseServerBack();" type="button" fcklang="DlgBtnBrowseServer"
+ value="Browse Server" /></td>
+ </tr>
+ </table>
+ <input id="chkBackNoScroll" type="checkbox" onclick="UpdatePreview();" />
+ <label for="chkBackNoScroll" fcklang="DlgDocBgNoScroll">
+ Nonscrolling Background</label>
+ </div>
+ <div id="divColors" style="display: none">
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td>
+ <span fcklang="DlgDocCText">Text</span><br />
+ <input id="txtColorText" type="text" onchange="UpdatePreview();" onkeyup="UpdatePreview();" /><input
+ onclick="SelectColor( 'ColorText' )" type="button" value="Select..." fcklang="DlgCellBtnSelect" />
+ <br />
+ <span fcklang="DlgDocCLink">Link</span><br />
+ <input id="txtColorLink" type="text" onchange="UpdatePreview();" onkeyup="UpdatePreview();" /><input
+ onclick="SelectColor( 'ColorLink' )" type="button" value="Select..." fcklang="DlgCellBtnSelect" />
+ <br />
+ <span fcklang="DlgDocCVisited">Visited Link</span><br />
+ <input id="txtColorVisited" type="text" onchange="UpdatePreview();" onkeyup="UpdatePreview();" /><input
+ onclick="SelectColor( 'ColorVisited' )" type="button" value="Select..." fcklang="DlgCellBtnSelect" />
+ <br />
+ <span fcklang="DlgDocCActive">Active Link</span><br />
+ <input id="txtColorActive" type="text" onchange="UpdatePreview();" onkeyup="UpdatePreview();" /><input
+ onclick="SelectColor( 'ColorActive' )" type="button" value="Select..." fcklang="DlgCellBtnSelect" />
+ </td>
+ <td valign="middle" align="center">
+ <table cellspacing="2" cellpadding="0" border="0">
+ <tr>
+ <td>
+ <span fcklang="DlgDocMargins">Page Margins</span></td>
+ </tr>
+ <tr>
+ <td style="border: #000000 1px solid; padding: 5px">
+ <table cellpadding="0" cellspacing="0" border="0" dir="ltr">
+ <tr>
+ <td align="center" colspan="3">
+ <span fcklang="DlgDocMaTop">Top</span><br />
+ <input id="txtMarginTop" type="text" size="3" />
+ </td>
+ </tr>
+ <tr>
+ <td align="left">
+ <span fcklang="DlgDocMaLeft">Left</span><br />
+ <input id="txtMarginLeft" type="text" size="3" />
+ </td>
+ <td>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ <td align="right">
+ <span fcklang="DlgDocMaRight">Right</span><br />
+ <input id="txtMarginRight" type="text" size="3" />
+ </td>
+ </tr>
+ <tr>
+ <td align="center" colspan="3">
+ <span fcklang="DlgDocMaBottom">Bottom</span><br />
+ <input id="txtMarginBottom" type="text" size="3" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="divMeta" style="display: none">
+ <span fcklang="DlgDocMeIndex">Document Indexing Keywords (comma separated)</span><br />
+ <textarea id="txtMetaKeywords" style="width: 100%" rows="2" cols="20"></textarea>
+ <br />
+ <span fcklang="DlgDocMeDescr">Document Description</span><br />
+ <textarea id="txtMetaDescription" style="width: 100%" rows="4" cols="20"></textarea>
+ <br />
+ <span fcklang="DlgDocMeAuthor">Author</span><br />
+ <input id="txtMetaAuthor" style="width: 100%" type="text" /><br />
+ <br />
+ <span fcklang="DlgDocMeCopy">Copyright</span><br />
+ <input id="txtMetaCopyright" type="text" style="width: 100%" />
+ </div>
+ </td>
+ </tr>
+ <tr id="ePreview" style="display: none">
+ <td>
+ <span fcklang="DlgDocPreview">Preview</span><br />
+ <iframe id="frmPreview" src="fck_docprops/fck_document_preview.html" width="100%"
+ height="100"></iframe>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_docprops/fck_document_preview.html b/httemplate/elements/fckeditor/editor/dialog/fck_docprops/fck_document_preview.html
new file mode 100644
index 0000000..2092775
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_docprops/fck_document_preview.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Preview shown in the "Document Properties" dialog window.
+-->
+<html>
+ <head>
+ <title>Document Properties - Preview</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="robots" content="noindex, nofollow">
+ <script language="javascript">
+
+var eBase = parent.FCK.EditorDocument.getElementsByTagName( 'BASE' ) ;
+if ( eBase.length > 0 && eBase[0].href.length > 0 )
+{
+ document.write( '<base href="' + eBase[0].href + '">' ) ;
+}
+
+window.onload = function()
+{
+ if ( typeof( parent.OnPreviewLoad ) == 'function' )
+ parent.OnPreviewLoad( window, document.body ) ;
+}
+
+function SetBaseHRef( baseHref )
+{
+ var eBase = document.createElement( 'BASE' ) ;
+ eBase.href = baseHref ;
+
+ var eHead = document.getElementsByTagName( 'HEAD' )[0] ;
+ eHead.appendChild( eBase ) ;
+}
+
+function SetLinkColor( color )
+{
+ if ( color && color.length > 0 )
+ document.getElementById('eLink').style.color = color ;
+ else
+ document.getElementById('eLink').style.color = window.document.linkColor ;
+}
+
+function SetVisitedColor( color )
+{
+ if ( color && color.length > 0 )
+ document.getElementById('eVisited').style.color = color ;
+ else
+ document.getElementById('eVisited').style.color = window.document.vlinkColor ;
+}
+
+function SetActiveColor( color )
+{
+ if ( color && color.length > 0 )
+ document.getElementById('eActive').style.color = color ;
+ else
+ document.getElementById('eActive').style.color = window.document.alinkColor ;
+}
+ </script>
+ </head>
+ <body>
+ <table width="100%" height="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td align="center" valign="middle">
+ Normal Text
+ </td>
+ <td id="eLink" align="center" valign="middle">
+ <u>Link Text</u>
+ </td>
+ </tr>
+ <tr>
+ <td id="eVisited" valign="middle" align="center">
+ <u>Visited Link</u>
+ </td>
+ <td id="eActive" valign="middle" align="center">
+ <u>Active Link</u>
+ </td>
+ </tr>
+ </table>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_find.html b/httemplate/elements/fckeditor/editor/dialog/fck_find.html
new file mode 100644
index 0000000..eba7f90
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_find.html
@@ -0,0 +1,173 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * "Find" dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+function OnLoad()
+{
+ // Whole word is available on IE only.
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ document.getElementById('divWord').style.display = '' ;
+
+ // First of all, translate the dialog box texts.
+ oEditor.FCKLanguageManager.TranslatePage( document ) ;
+
+ window.parent.SetAutoSize( true ) ;
+}
+
+function btnStat(frm)
+{
+ document.getElementById('btnFind').disabled =
+ ( document.getElementById('txtFind').value.length == 0 ) ;
+}
+
+function ReplaceTextNodes( parentNode, regex, replaceValue, replaceAll )
+{
+ for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
+ {
+ var oNode = parentNode.childNodes[i] ;
+ if ( oNode.nodeType == 3 )
+ {
+ var sReplaced = oNode.nodeValue.replace( regex, replaceValue ) ;
+ if ( oNode.nodeValue != sReplaced )
+ {
+ oNode.nodeValue = sReplaced ;
+ if ( ! replaceAll )
+ return true ;
+ }
+ }
+ else
+ {
+ if ( ReplaceTextNodes( oNode, regex, replaceValue ) )
+ return true ;
+ }
+ }
+ return false ;
+}
+
+function GetRegexExpr()
+{
+ var sExpr ;
+
+ if ( document.getElementById('chkWord').checked )
+ sExpr = '\\b' + document.getElementById('txtFind').value + '\\b' ;
+ else
+ sExpr = document.getElementById('txtFind').value ;
+
+ return sExpr ;
+}
+
+function GetCase()
+{
+ return ( document.getElementById('chkCase').checked ? '' : 'i' ) ;
+}
+
+function Ok()
+{
+ if ( document.getElementById('txtFind').value.length == 0 )
+ return ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ FindIE() ;
+ else
+ FindGecko() ;
+}
+
+var oRange ;
+
+if ( oEditor.FCKBrowserInfo.IsIE )
+ oRange = oEditor.FCK.EditorDocument.body.createTextRange() ;
+
+function FindIE()
+{
+ var iFlags = 0 ;
+
+ if ( chkCase.checked )
+ iFlags = iFlags | 4 ;
+
+ if ( chkWord.checked )
+ iFlags = iFlags | 2 ;
+
+ var bFound = oRange.findText( document.getElementById('txtFind').value, 1, iFlags ) ;
+
+ if ( bFound )
+ {
+ oRange.scrollIntoView() ;
+ oRange.select() ;
+ oRange.collapse(false) ;
+ oLastRangeFound = oRange ;
+ }
+ else
+ {
+ oRange = oEditor.FCK.EditorDocument.body.createTextRange() ;
+ alert( oEditor.FCKLang.DlgFindNotFoundMsg ) ;
+ }
+}
+
+function FindGecko()
+{
+ var bCase = document.getElementById('chkCase').checked ;
+ var bWord = document.getElementById('chkWord').checked ;
+
+ // window.find( searchString, caseSensitive, backwards, wrapAround, wholeWord, searchInFrames, showDialog ) ;
+ if ( !oEditor.FCK.EditorWindow.find( document.getElementById('txtFind').value, bCase, false, false, bWord, false, false ) )
+ alert( oEditor.FCKLang.DlgFindNotFoundMsg ) ;
+}
+ </script>
+</head>
+<body onload="OnLoad()" style="overflow: hidden">
+ <table cellspacing="3" cellpadding="2" width="100%" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <label for="txtFind" fcklang="DlgReplaceFindLbl">
+ Find what:</label>&nbsp;
+ </td>
+ <td width="100%">
+ <input id="txtFind" style="width: 100%" tabindex="1" type="text" />
+ </td>
+ <td>
+ <input id="btnFind" style="padding-right: 5px; padding-left: 5px" onclick="Ok();"
+ type="button" value="Find" fcklang="DlgFindFindBtn" />
+ </td>
+ </tr>
+ <tr>
+ <td valign="bottom" colspan="3">
+ &nbsp;<input id="chkCase" tabindex="3" type="checkbox" /><label for="chkCase" fcklang="DlgReplaceCaseChk">Match
+ case</label>
+ <br />
+ <div id="divWord" style="display: none">
+ &nbsp;<input id="chkWord" tabindex="4" type="checkbox" /><label for="chkWord" fcklang="DlgReplaceWordChk">Match
+ whole word</label>
+ </div>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_flash.html b/httemplate/elements/fckeditor/editor/dialog/fck_flash.html
new file mode 100644
index 0000000..be529e3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_flash.html
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Flash Properties dialog window.
+-->
+<html>
+ <head>
+ <title>Flash Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script src="fck_flash/fck_flash.js" type="text/javascript"></script>
+ <link href="common/fck_dialog_common.css" type="text/css" rel="stylesheet">
+ </head>
+ <body scroll="no" style="OVERFLOW: hidden">
+ <div id="divInfo">
+ <table cellSpacing="1" cellPadding="1" width="100%" border="0">
+ <tr>
+ <td>
+ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td width="100%"><span fckLang="DlgImgURL">URL</span>
+ </td>
+ <td id="tdBrowse" style="DISPLAY: none" noWrap rowSpan="2">&nbsp; <input id="btnBrowse" onclick="BrowseServer();" type="button" value="Browse Server" fckLang="DlgBtnBrowseServer">
+ </td>
+ </tr>
+ <tr>
+ <td vAlign="top"><input id="txtUrl" onblur="UpdatePreview();" style="WIDTH: 100%" type="text">
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <TR>
+ <TD>
+ <table cellSpacing="0" cellPadding="0" border="0">
+ <TR>
+ <TD nowrap>
+ <span fckLang="DlgImgWidth">Width</span><br>
+ <input id="txtWidth" class="FCK__FieldNumeric" type="text" size="3">
+ </TD>
+ <TD>&nbsp;</TD>
+ <TD>
+ <span fckLang="DlgImgHeight">Height</span><br>
+ <input id="txtHeight" class="FCK__FieldNumeric" type="text" size="3">
+ </TD>
+ </TR>
+ </table>
+ </TD>
+ </TR>
+ <tr>
+ <td vAlign="top">
+ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td valign="top" width="100%">
+ <table cellSpacing="0" cellPadding="0" width="100%">
+ <tr>
+ <td><span fckLang="DlgImgPreview">Preview</span></td>
+ </tr>
+ <tr>
+ <td id="ePreviewCell" valign="top" class="FlashPreviewArea"><iframe src="fck_flash/fck_flash_preview.html" frameborder="0" marginheight="0" marginwidth="0"></iframe></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="divUpload" style="DISPLAY: none">
+ <form id="frmUpload" method="post" target="UploadWindow" enctype="multipart/form-data" action="" onsubmit="return CheckUpload();">
+ <span fckLang="DlgLnkUpload">Upload</span><br />
+ <input id="txtUploadFile" style="WIDTH: 100%" type="file" size="40" name="NewFile" /><br />
+ <br />
+ <input id="btnUpload" type="submit" value="Send it to the Server" fckLang="DlgLnkBtnUpload" />
+ <iframe name="UploadWindow" style="DISPLAY: none" src="javascript:void(0)"></iframe>
+ </form>
+ </div>
+ <div id="divAdvanced" style="DISPLAY: none">
+ <TABLE cellSpacing="0" cellPadding="0" border="0">
+ <TR>
+ <TD nowrap>
+ <span fckLang="DlgFlashScale">Scale</span><BR>
+ <select id="cmbScale">
+ <option value="" selected></option>
+ <option value="showall" fckLang="DlgFlashScaleAll">Show all</option>
+ <option value="noborder" fckLang="DlgFlashScaleNoBorder">No Border</option>
+ <option value="exactfit" fckLang="DlgFlashScaleFit">Exact Fit</option>
+ </select></TD>
+ <TD>&nbsp;&nbsp;&nbsp; &nbsp;
+ </TD>
+ <td valign="bottom">
+ <table>
+ <tr>
+ <td><input id="chkAutoPlay" type="checkbox" checked></td>
+ <td><label for="chkAutoPlay" nowrap fckLang="DlgFlashChkPlay">Auto Play</label>&nbsp;&nbsp;</td>
+ <td><input id="chkLoop" type="checkbox" checked></td>
+ <td><label for="chkLoop" nowrap fckLang="DlgFlashChkLoop">Loop</label>&nbsp;&nbsp;</td>
+ <td><input id="chkMenu" type="checkbox" checked></td>
+ <td><label for="chkMenu" nowrap fckLang="DlgFlashChkMenu">Enable Flash Menu</label></td>
+ </tr>
+ </table>
+ </td>
+ </TR>
+ </TABLE>
+ <br>
+ &nbsp;
+ <table cellSpacing="0" cellPadding="0" width="100%" align="center" border="0">
+ <tr>
+ <td vAlign="top" width="50%"><span fckLang="DlgGenId">Id</span><br>
+ <input id="txtAttId" style="WIDTH: 100%" type="text">
+ </td>
+ <td>&nbsp;&nbsp;</td>
+ <td vAlign="top" nowrap><span fckLang="DlgGenClass">Stylesheet Classes</span><br>
+ <input id="txtAttClasses" style="WIDTH: 100%" type="text">
+ </td>
+ <td>&nbsp;&nbsp;</td>
+ <td vAlign="top" nowrap width="50%">&nbsp;<span fckLang="DlgGenTitle">Advisory Title</span><br>
+ <input id="txtAttTitle" style="WIDTH: 100%" type="text">
+ </td>
+ </tr>
+ </table>
+ <span fckLang="DlgGenStyle">Style</span><br>
+ <input id="txtAttStyle" style="WIDTH: 100%" type="text">
+ </div>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash.js b/httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash.js
new file mode 100644
index 0000000..ee97bc5
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash.js
@@ -0,0 +1,286 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Scripts related to the Flash dialog window (see fck_flash.html).
+ */
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+var FCKLang = oEditor.FCKLang ;
+var FCKConfig = oEditor.FCKConfig ;
+
+//#### Dialog Tabs
+
+// Set the dialog tabs.
+window.parent.AddTab( 'Info', oEditor.FCKLang.DlgInfoTab ) ;
+
+if ( FCKConfig.FlashUpload )
+ window.parent.AddTab( 'Upload', FCKLang.DlgLnkUpload ) ;
+
+if ( !FCKConfig.FlashDlgHideAdvanced )
+ window.parent.AddTab( 'Advanced', oEditor.FCKLang.DlgAdvancedTag ) ;
+
+// Function called when a dialog tag is selected.
+function OnDialogTabChange( tabCode )
+{
+ ShowE('divInfo' , ( tabCode == 'Info' ) ) ;
+ ShowE('divUpload' , ( tabCode == 'Upload' ) ) ;
+ ShowE('divAdvanced' , ( tabCode == 'Advanced' ) ) ;
+}
+
+// Get the selected flash embed (if available).
+var oFakeImage = FCK.Selection.GetSelectedElement() ;
+var oEmbed ;
+
+if ( oFakeImage )
+{
+ if ( oFakeImage.tagName == 'IMG' && oFakeImage.getAttribute('_fckflash') )
+ oEmbed = FCK.GetRealElement( oFakeImage ) ;
+ else
+ oFakeImage = null ;
+}
+
+window.onload = function()
+{
+ // Translate the dialog box texts.
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ // Load the selected element information (if any).
+ LoadSelection() ;
+
+ // Show/Hide the "Browse Server" button.
+ GetE('tdBrowse').style.display = FCKConfig.FlashBrowser ? '' : 'none' ;
+
+ // Set the actual uploader URL.
+ if ( FCKConfig.FlashUpload )
+ GetE('frmUpload').action = FCKConfig.FlashUploadURL ;
+
+ window.parent.SetAutoSize( true ) ;
+
+ // Activate the "OK" button.
+ window.parent.SetOkButton( true ) ;
+}
+
+function LoadSelection()
+{
+ if ( ! oEmbed ) return ;
+
+ GetE('txtUrl').value = GetAttribute( oEmbed, 'src', '' ) ;
+ GetE('txtWidth').value = GetAttribute( oEmbed, 'width', '' ) ;
+ GetE('txtHeight').value = GetAttribute( oEmbed, 'height', '' ) ;
+
+ // Get Advances Attributes
+ GetE('txtAttId').value = oEmbed.id ;
+ GetE('chkAutoPlay').checked = GetAttribute( oEmbed, 'play', 'true' ) == 'true' ;
+ GetE('chkLoop').checked = GetAttribute( oEmbed, 'loop', 'true' ) == 'true' ;
+ GetE('chkMenu').checked = GetAttribute( oEmbed, 'menu', 'true' ) == 'true' ;
+ GetE('cmbScale').value = GetAttribute( oEmbed, 'scale', '' ).toLowerCase() ;
+
+ GetE('txtAttTitle').value = oEmbed.title ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ {
+ GetE('txtAttClasses').value = oEmbed.getAttribute('className') || '' ;
+ GetE('txtAttStyle').value = oEmbed.style.cssText ;
+ }
+ else
+ {
+ GetE('txtAttClasses').value = oEmbed.getAttribute('class',2) || '' ;
+ GetE('txtAttStyle').value = oEmbed.getAttribute('style',2) || '' ;
+ }
+
+ UpdatePreview() ;
+}
+
+//#### The OK button was hit.
+function Ok()
+{
+ if ( GetE('txtUrl').value.length == 0 )
+ {
+ window.parent.SetSelectedTab( 'Info' ) ;
+ GetE('txtUrl').focus() ;
+
+ alert( oEditor.FCKLang.DlgAlertUrl ) ;
+
+ return false ;
+ }
+
+ if ( !oEmbed )
+ {
+ oEmbed = FCK.EditorDocument.createElement( 'EMBED' ) ;
+ oFakeImage = null ;
+ }
+ UpdateEmbed( oEmbed ) ;
+
+ if ( !oFakeImage )
+ {
+ oFakeImage = oEditor.FCKDocumentProcessor_CreateFakeImage( 'FCK__Flash', oEmbed ) ;
+ oFakeImage.setAttribute( '_fckflash', 'true', 0 ) ;
+ oFakeImage = FCK.InsertElementAndGetIt( oFakeImage ) ;
+ }
+ else
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ oEditor.FCKFlashProcessor.RefreshView( oFakeImage, oEmbed ) ;
+
+ return true ;
+}
+
+function UpdateEmbed( e )
+{
+ SetAttribute( e, 'type' , 'application/x-shockwave-flash' ) ;
+ SetAttribute( e, 'pluginspage' , 'http://www.macromedia.com/go/getflashplayer' ) ;
+
+ SetAttribute( e, 'src', GetE('txtUrl').value ) ;
+ SetAttribute( e, "width" , GetE('txtWidth').value ) ;
+ SetAttribute( e, "height", GetE('txtHeight').value ) ;
+
+ // Advances Attributes
+
+ SetAttribute( e, 'id' , GetE('txtAttId').value ) ;
+ SetAttribute( e, 'scale', GetE('cmbScale').value ) ;
+
+ SetAttribute( e, 'play', GetE('chkAutoPlay').checked ? 'true' : 'false' ) ;
+ SetAttribute( e, 'loop', GetE('chkLoop').checked ? 'true' : 'false' ) ;
+ SetAttribute( e, 'menu', GetE('chkMenu').checked ? 'true' : 'false' ) ;
+
+ SetAttribute( e, 'title' , GetE('txtAttTitle').value ) ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ {
+ SetAttribute( e, 'className', GetE('txtAttClasses').value ) ;
+ e.style.cssText = GetE('txtAttStyle').value ;
+ }
+ else
+ {
+ SetAttribute( e, 'class', GetE('txtAttClasses').value ) ;
+ SetAttribute( e, 'style', GetE('txtAttStyle').value ) ;
+ }
+}
+
+var ePreview ;
+
+function SetPreviewElement( previewEl )
+{
+ ePreview = previewEl ;
+
+ if ( GetE('txtUrl').value.length > 0 )
+ UpdatePreview() ;
+}
+
+function UpdatePreview()
+{
+ if ( !ePreview )
+ return ;
+
+ while ( ePreview.firstChild )
+ ePreview.removeChild( ePreview.firstChild ) ;
+
+ if ( GetE('txtUrl').value.length == 0 )
+ ePreview.innerHTML = '&nbsp;' ;
+ else
+ {
+ var oDoc = ePreview.ownerDocument || ePreview.document ;
+ var e = oDoc.createElement( 'EMBED' ) ;
+
+ SetAttribute( e, 'src', GetE('txtUrl').value ) ;
+ SetAttribute( e, 'type', 'application/x-shockwave-flash' ) ;
+ SetAttribute( e, 'width', '100%' ) ;
+ SetAttribute( e, 'height', '100%' ) ;
+
+ ePreview.appendChild( e ) ;
+ }
+}
+
+// <embed id="ePreview" src="fck_flash/claims.swf" width="100%" height="100%" style="visibility:hidden" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer">
+
+function BrowseServer()
+{
+ OpenFileBrowser( FCKConfig.FlashBrowserURL, FCKConfig.FlashBrowserWindowWidth, FCKConfig.FlashBrowserWindowHeight ) ;
+}
+
+function SetUrl( url, width, height )
+{
+ GetE('txtUrl').value = url ;
+
+ if ( width )
+ GetE('txtWidth').value = width ;
+
+ if ( height )
+ GetE('txtHeight').value = height ;
+
+ UpdatePreview() ;
+
+ window.parent.SetSelectedTab( 'Info' ) ;
+}
+
+function OnUploadCompleted( errorNumber, fileUrl, fileName, customMsg )
+{
+ switch ( errorNumber )
+ {
+ case 0 : // No errors
+ alert( 'Your file has been successfully uploaded' ) ;
+ break ;
+ case 1 : // Custom error
+ alert( customMsg ) ;
+ return ;
+ case 101 : // Custom warning
+ alert( customMsg ) ;
+ break ;
+ case 201 :
+ alert( 'A file with the same name is already available. The uploaded file has been renamed to "' + fileName + '"' ) ;
+ break ;
+ case 202 :
+ alert( 'Invalid file type' ) ;
+ return ;
+ case 203 :
+ alert( "Security error. You probably don't have enough permissions to upload. Please check your server." ) ;
+ return ;
+ default :
+ alert( 'Error on file upload. Error number: ' + errorNumber ) ;
+ return ;
+ }
+
+ SetUrl( fileUrl ) ;
+ GetE('frmUpload').reset() ;
+}
+
+var oUploadAllowedExtRegex = new RegExp( FCKConfig.FlashUploadAllowedExtensions, 'i' ) ;
+var oUploadDeniedExtRegex = new RegExp( FCKConfig.FlashUploadDeniedExtensions, 'i' ) ;
+
+function CheckUpload()
+{
+ var sFile = GetE('txtUploadFile').value ;
+
+ if ( sFile.length == 0 )
+ {
+ alert( 'Please select a file to upload' ) ;
+ return false ;
+ }
+
+ if ( ( FCKConfig.FlashUploadAllowedExtensions.length > 0 && !oUploadAllowedExtRegex.test( sFile ) ) ||
+ ( FCKConfig.FlashUploadDeniedExtensions.length > 0 && oUploadDeniedExtRegex.test( sFile ) ) )
+ {
+ OnUploadCompleted( 202 ) ;
+ return false ;
+ }
+
+ return true ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash_preview.html b/httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash_preview.html
new file mode 100644
index 0000000..ad3a0a1
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_flash/fck_flash_preview.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Preview page for the Flash dialog window.
+-->
+<html>
+ <head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="robots" content="noindex, nofollow">
+ <link href="../common/fck_dialog_common.css" rel="stylesheet" type="text/css" />
+ <script language="javascript">
+
+// Sets the Skin CSS
+document.write( '<link href="' + window.parent.FCKConfig.SkinPath + 'fck_dialog.css" type="text/css" rel="stylesheet">' ) ;
+
+if ( window.parent.FCKConfig.BaseHref.length > 0 )
+ document.write( '<base href="' + window.parent.FCKConfig.BaseHref + '">' ) ;
+
+window.onload = function()
+{
+ window.parent.SetPreviewElement( document.body ) ;
+}
+
+ </script>
+ </head>
+ <body style="COLOR: #000000; BACKGROUND-COLOR: #ffffff"></body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_form.html b/httemplate/elements/fckeditor/editor/dialog/fck_form.html
new file mode 100644
index 0000000..66e56d9
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_form.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Form dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oActiveEl = oEditor.FCKSelection.MoveToAncestorNode( 'FORM' ) ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oActiveEl )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtAction').value = oActiveEl.getAttribute( 'action', 2 ) ;
+ GetE('txtMethod').value = oActiveEl.method ;
+ }
+ else
+ oActiveEl = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ if ( !oActiveEl )
+ {
+ oActiveEl = oEditor.FCK.EditorDocument.createElement( 'FORM' ) ;
+ oActiveEl = oEditor.FCK.InsertElementAndGetIt( oActiveEl ) ;
+ oActiveEl.innerHTML = '&nbsp;' ;
+ }
+
+ oActiveEl.name = GetE('txtName').value ;
+ SetAttribute( oActiveEl, 'action' , GetE('txtAction').value ) ;
+ oActiveEl.method = GetE('txtMethod').value ;
+
+ return true ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <table width="100%" style="height: 100%">
+ <tr>
+ <td align="center">
+ <table cellspacing="0" cellpadding="0" width="80%" border="0">
+ <tr>
+ <td>
+ <span fcklang="DlgFormName">Name</span><br />
+ <input style="width: 100%" type="text" id="txtName" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgFormAction">Action</span><br />
+ <input style="width: 100%" type="text" id="txtAction" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgFormMethod">Method</span><br />
+ <select id="txtMethod">
+ <option value="get" selected="selected">GET</option>
+ <option value="post">POST</option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_hiddenfield.html b/httemplate/elements/fckeditor/editor/dialog/fck_hiddenfield.html
new file mode 100644
index 0000000..1ae8ae6
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_hiddenfield.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Hidden Field dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Hidden Field Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+
+// Gets the document DOM
+var oDOM = FCK.EditorDocument ;
+
+// Get the selected flash embed (if available).
+var oFakeImage = FCK.Selection.GetSelectedElement() ;
+var oActiveEl ;
+
+if ( oFakeImage )
+{
+ if ( oFakeImage.tagName == 'IMG' && oFakeImage.getAttribute('_fckinputhidden') )
+ oActiveEl = FCK.GetRealElement( oFakeImage ) ;
+ else
+ oFakeImage = null ;
+}
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oActiveEl )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtValue').value = oActiveEl.value ;
+ }
+
+ window.parent.SetOkButton( true ) ;
+}
+
+
+function Ok()
+{
+ if ( !oActiveEl )
+ {
+ oActiveEl = FCK.EditorDocument.createElement( 'INPUT' ) ;
+ oActiveEl.type = 'hidden' ;
+
+ oFakeImage = null ;
+ }
+
+ oActiveEl.name = GetE('txtName').value ;
+ SetAttribute( oActiveEl, 'value', GetE('txtValue').value ) ;
+
+ if ( !oFakeImage )
+ {
+ oFakeImage = oEditor.FCKDocumentProcessor_CreateFakeImage( 'FCK__InputHidden', oActiveEl ) ;
+ oFakeImage.setAttribute( '_fckinputhidden', 'true', 0 ) ;
+ oFakeImage = FCK.InsertElementAndGetIt( oFakeImage ) ;
+ }
+ else
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ oEditor.FCKFlashProcessor.RefreshView( oFakeImage, oActiveEl ) ;
+
+ return true ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden" scroll="no">
+ <table height="100%" width="100%">
+ <tr>
+ <td align="center">
+ <table border="0" class="inhoud" cellpadding="0" cellspacing="0" width="80%">
+ <tr>
+ <td>
+ <span fcklang="DlgHiddenName">Name</span><br />
+ <input type="text" size="20" id="txtName" style="width: 100%" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgHiddenValue">Value</span><br />
+ <input type="text" size="30" id="txtValue" style="width: 100%" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_image.html b/httemplate/elements/fckeditor/editor/dialog/fck_image.html
new file mode 100644
index 0000000..e4a8b03
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_image.html
@@ -0,0 +1,252 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Image Properties dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Image Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script src="fck_image/fck_image.js" type="text/javascript"></script>
+ <link href="common/fck_dialog_common.css" rel="stylesheet" type="text/css" />
+</head>
+<body scroll="no" style="overflow: hidden">
+ <div id="divInfo">
+ <table cellspacing="1" cellpadding="1" border="0" width="100%" height="100%">
+ <tr>
+ <td>
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td width="100%">
+ <span fcklang="DlgImgURL">URL</span>
+ </td>
+ <td id="tdBrowse" style="display: none" nowrap="nowrap" rowspan="2">
+ &nbsp;
+ <input id="btnBrowse" onclick="BrowseServer();" type="button" value="Browse Server"
+ fcklang="DlgBtnBrowseServer" />
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <input id="txtUrl" style="width: 100%" type="text" onblur="UpdatePreview();" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgImgAlt">Short Description</span><br />
+ <input id="txtAlt" style="width: 100%" type="text" /><br />
+ </td>
+ </tr>
+ <tr height="100%">
+ <td valign="top">
+ <table cellspacing="0" cellpadding="0" width="100%" border="0" height="100%">
+ <tr>
+ <td valign="top">
+ <br />
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgImgWidth">Width</span>&nbsp;</td>
+ <td>
+ <input type="text" size="3" id="txtWidth" onkeyup="OnSizeChanged('Width',this.value);" /></td>
+ <td rowspan="2">
+ <div id="btnLockSizes" class="BtnLocked" onmouseover="this.className = (bLockRatio ? 'BtnLocked' : 'BtnUnlocked' ) + ' BtnOver';"
+ onmouseout="this.className = (bLockRatio ? 'BtnLocked' : 'BtnUnlocked' );" title="Lock Sizes"
+ onclick="SwitchLock(this);">
+ </div>
+ </td>
+ <td rowspan="2">
+ <div id="btnResetSize" class="BtnReset" onmouseover="this.className='BtnReset BtnOver';"
+ onmouseout="this.className='BtnReset';" title="Reset Size" onclick="ResetSizes();">
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgImgHeight">Height</span>&nbsp;</td>
+ <td>
+ <input type="text" size="3" id="txtHeight" onkeyup="OnSizeChanged('Height',this.value);" /></td>
+ </tr>
+ </table>
+ <br />
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgImgBorder">Border</span>&nbsp;</td>
+ <td>
+ <input type="text" size="2" value="" id="txtBorder" onkeyup="UpdatePreview();" /></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgImgHSpace">HSpace</span>&nbsp;</td>
+ <td>
+ <input type="text" size="2" id="txtHSpace" onkeyup="UpdatePreview();" /></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgImgVSpace">VSpace</span>&nbsp;</td>
+ <td>
+ <input type="text" size="2" id="txtVSpace" onkeyup="UpdatePreview();" /></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgImgAlign">Align</span>&nbsp;</td>
+ <td>
+ <select id="cmbAlign" onchange="UpdatePreview();">
+ <option value="" selected="selected"></option>
+ <option fcklang="DlgImgAlignLeft" value="left">Left</option>
+ <option fcklang="DlgImgAlignAbsBottom" value="absBottom">Abs Bottom</option>
+ <option fcklang="DlgImgAlignAbsMiddle" value="absMiddle">Abs Middle</option>
+ <option fcklang="DlgImgAlignBaseline" value="baseline">Baseline</option>
+ <option fcklang="DlgImgAlignBottom" value="bottom">Bottom</option>
+ <option fcklang="DlgImgAlignMiddle" value="middle">Middle</option>
+ <option fcklang="DlgImgAlignRight" value="right">Right</option>
+ <option fcklang="DlgImgAlignTextTop" value="textTop">Text Top</option>
+ <option fcklang="DlgImgAlignTop" value="top">Top</option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ &nbsp;&nbsp;&nbsp;</td>
+ <td width="100%" valign="top">
+ <table cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed">
+ <tr>
+ <td>
+ <span fcklang="DlgImgPreview">Preview</span></td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <iframe class="ImagePreviewArea" src="fck_image/fck_image_preview.html" frameborder="0"
+ marginheight="0" marginwidth="0"></iframe>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="divUpload" style="display: none">
+ <form id="frmUpload" method="post" target="UploadWindow" enctype="multipart/form-data"
+ action="" onsubmit="return CheckUpload();">
+ <span fcklang="DlgLnkUpload">Upload</span><br />
+ <input id="txtUploadFile" style="width: 100%" type="file" size="40" name="NewFile" /><br />
+ <br />
+ <input id="btnUpload" type="submit" value="Send it to the Server" fcklang="DlgLnkBtnUpload" />
+ <iframe name="UploadWindow" style="display: none" src="javascript:void(0)"></iframe>
+ </form>
+ </div>
+ <div id="divLink" style="display: none">
+ <table cellspacing="1" cellpadding="1" border="0" width="100%">
+ <tr>
+ <td>
+ <div>
+ <span fcklang="DlgLnkURL">URL</span><br />
+ <input id="txtLnkUrl" style="width: 100%" type="text" onblur="UpdatePreview();" />
+ </div>
+ <div id="divLnkBrowseServer" align="right">
+ <input type="button" value="Browse Server" fcklang="DlgBtnBrowseServer" onclick="LnkBrowseServer();" />
+ </div>
+ <div>
+ <span fcklang="DlgLnkTarget">Target</span><br />
+ <select id="cmbLnkTarget">
+ <option value="" fcklang="DlgGenNotSet" selected="selected">&lt;not set&gt;</option>
+ <option value="_blank" fcklang="DlgLnkTargetBlank">New Window (_blank)</option>
+ <option value="_top" fcklang="DlgLnkTargetTop">Topmost Window (_top)</option>
+ <option value="_self" fcklang="DlgLnkTargetSelf">Same Window (_self)</option>
+ <option value="_parent" fcklang="DlgLnkTargetParent">Parent Window (_parent)</option>
+ </select>
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="divAdvanced" style="display: none">
+ <table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
+ <tr>
+ <td valign="top" width="50%">
+ <span fcklang="DlgGenId">Id</span><br />
+ <input id="txtAttId" style="width: 100%" type="text" />
+ </td>
+ <td width="1">
+ &nbsp;&nbsp;</td>
+ <td valign="top">
+ <table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
+ <tr>
+ <td width="60%">
+ <span fcklang="DlgGenLangDir">Language Direction</span><br />
+ <select id="cmbAttLangDir" style="width: 100%">
+ <option value="" fcklang="DlgGenNotSet" selected="selected">&lt;not set&gt;</option>
+ <option value="ltr" fcklang="DlgGenLangDirLtr">Left to Right (LTR)</option>
+ <option value="rtl" fcklang="DlgGenLangDirRtl">Right to Left (RTL)</option>
+ </select>
+ </td>
+ <td width="1%">
+ &nbsp;&nbsp;</td>
+ <td nowrap="nowrap">
+ <span fcklang="DlgGenLangCode">Language Code</span><br />
+ <input id="txtAttLangCode" style="width: 100%" type="text" />&nbsp;
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <span fcklang="DlgGenLongDescr">Long Description URL</span><br />
+ <input id="txtLongDesc" style="width: 100%" type="text" />
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <span fcklang="DlgGenClass">Stylesheet Classes</span><br />
+ <input id="txtAttClasses" style="width: 100%" type="text" />
+ </td>
+ <td>
+ </td>
+ <td valign="top">
+ &nbsp;<span fcklang="DlgGenTitle">Advisory Title</span><br />
+ <input id="txtAttTitle" style="width: 100%" type="text" />
+ </td>
+ </tr>
+ </table>
+ <span fcklang="DlgGenStyle">Style</span><br />
+ <input id="txtAttStyle" style="width: 100%" type="text" />
+ </div>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image.js b/httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image.js
new file mode 100644
index 0000000..89b0f95
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image.js
@@ -0,0 +1,493 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Scripts related to the Image dialog window (see fck_image.html).
+ */
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+var FCKLang = oEditor.FCKLang ;
+var FCKConfig = oEditor.FCKConfig ;
+var FCKDebug = oEditor.FCKDebug ;
+
+var bImageButton = ( document.location.search.length > 0 && document.location.search.substr(1) == 'ImageButton' ) ;
+
+//#### Dialog Tabs
+
+// Set the dialog tabs.
+window.parent.AddTab( 'Info', FCKLang.DlgImgInfoTab ) ;
+
+if ( !bImageButton && !FCKConfig.ImageDlgHideLink )
+ window.parent.AddTab( 'Link', FCKLang.DlgImgLinkTab ) ;
+
+if ( FCKConfig.ImageUpload )
+ window.parent.AddTab( 'Upload', FCKLang.DlgLnkUpload ) ;
+
+if ( !FCKConfig.ImageDlgHideAdvanced )
+ window.parent.AddTab( 'Advanced', FCKLang.DlgAdvancedTag ) ;
+
+// Function called when a dialog tag is selected.
+function OnDialogTabChange( tabCode )
+{
+ ShowE('divInfo' , ( tabCode == 'Info' ) ) ;
+ ShowE('divLink' , ( tabCode == 'Link' ) ) ;
+ ShowE('divUpload' , ( tabCode == 'Upload' ) ) ;
+ ShowE('divAdvanced' , ( tabCode == 'Advanced' ) ) ;
+}
+
+// Get the selected image (if available).
+var oImage = FCK.Selection.GetSelectedElement() ;
+
+if ( oImage && oImage.tagName != 'IMG' && !( oImage.tagName == 'INPUT' && oImage.type == 'image' ) )
+ oImage = null ;
+
+// Get the active link.
+var oLink = FCK.Selection.MoveToAncestorNode( 'A' ) ;
+
+var oImageOriginal ;
+
+function UpdateOriginal( resetSize )
+{
+ if ( !eImgPreview )
+ return ;
+
+ if ( GetE('txtUrl').value.length == 0 )
+ {
+ oImageOriginal = null ;
+ return ;
+ }
+
+ oImageOriginal = document.createElement( 'IMG' ) ; // new Image() ;
+
+ if ( resetSize )
+ {
+ oImageOriginal.onload = function()
+ {
+ this.onload = null ;
+ ResetSizes() ;
+ }
+ }
+
+ oImageOriginal.src = eImgPreview.src ;
+}
+
+var bPreviewInitialized ;
+
+window.onload = function()
+{
+ // Translate the dialog box texts.
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ GetE('btnLockSizes').title = FCKLang.DlgImgLockRatio ;
+ GetE('btnResetSize').title = FCKLang.DlgBtnResetSize ;
+
+ // Load the selected element information (if any).
+ LoadSelection() ;
+
+ // Show/Hide the "Browse Server" button.
+ GetE('tdBrowse').style.display = FCKConfig.ImageBrowser ? '' : 'none' ;
+ GetE('divLnkBrowseServer').style.display = FCKConfig.LinkBrowser ? '' : 'none' ;
+
+ UpdateOriginal() ;
+
+ // Set the actual uploader URL.
+ if ( FCKConfig.ImageUpload )
+ GetE('frmUpload').action = FCKConfig.ImageUploadURL ;
+
+ window.parent.SetAutoSize( true ) ;
+
+ // Activate the "OK" button.
+ window.parent.SetOkButton( true ) ;
+}
+
+function LoadSelection()
+{
+ if ( ! oImage ) return ;
+
+ var sUrl = oImage.getAttribute( '_fcksavedurl' ) ;
+ if ( sUrl == null )
+ sUrl = GetAttribute( oImage, 'src', '' ) ;
+
+ GetE('txtUrl').value = sUrl ;
+ GetE('txtAlt').value = GetAttribute( oImage, 'alt', '' ) ;
+ GetE('txtVSpace').value = GetAttribute( oImage, 'vspace', '' ) ;
+ GetE('txtHSpace').value = GetAttribute( oImage, 'hspace', '' ) ;
+ GetE('txtBorder').value = GetAttribute( oImage, 'border', '' ) ;
+ GetE('cmbAlign').value = GetAttribute( oImage, 'align', '' ) ;
+
+ var iWidth, iHeight ;
+
+ var regexSize = /^\s*(\d+)px\s*$/i ;
+
+ if ( oImage.style.width )
+ {
+ var aMatchW = oImage.style.width.match( regexSize ) ;
+ if ( aMatchW )
+ {
+ iWidth = aMatchW[1] ;
+ oImage.style.width = '' ;
+ SetAttribute( oImage, 'width' , iWidth ) ;
+ }
+ }
+
+ if ( oImage.style.height )
+ {
+ var aMatchH = oImage.style.height.match( regexSize ) ;
+ if ( aMatchH )
+ {
+ iHeight = aMatchH[1] ;
+ oImage.style.height = '' ;
+ SetAttribute( oImage, 'height', iHeight ) ;
+ }
+ }
+
+ GetE('txtWidth').value = iWidth ? iWidth : GetAttribute( oImage, "width", '' ) ;
+ GetE('txtHeight').value = iHeight ? iHeight : GetAttribute( oImage, "height", '' ) ;
+
+ // Get Advances Attributes
+ GetE('txtAttId').value = oImage.id ;
+ GetE('cmbAttLangDir').value = oImage.dir ;
+ GetE('txtAttLangCode').value = oImage.lang ;
+ GetE('txtAttTitle').value = oImage.title ;
+ GetE('txtLongDesc').value = oImage.longDesc ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ {
+ GetE('txtAttClasses').value = oImage.className || '' ;
+ GetE('txtAttStyle').value = oImage.style.cssText ;
+ }
+ else
+ {
+ GetE('txtAttClasses').value = oImage.getAttribute('class',2) || '' ;
+ GetE('txtAttStyle').value = oImage.getAttribute('style',2) ;
+ }
+
+ if ( oLink )
+ {
+ var sLinkUrl = oLink.getAttribute( '_fcksavedurl' ) ;
+ if ( sLinkUrl == null )
+ sLinkUrl = oLink.getAttribute('href',2) ;
+
+ GetE('txtLnkUrl').value = sLinkUrl ;
+ GetE('cmbLnkTarget').value = oLink.target ;
+ }
+
+ UpdatePreview() ;
+}
+
+//#### The OK button was hit.
+function Ok()
+{
+ if ( GetE('txtUrl').value.length == 0 )
+ {
+ window.parent.SetSelectedTab( 'Info' ) ;
+ GetE('txtUrl').focus() ;
+
+ alert( FCKLang.DlgImgAlertUrl ) ;
+
+ return false ;
+ }
+
+ var bHasImage = ( oImage != null ) ;
+
+ if ( bHasImage && bImageButton && oImage.tagName == 'IMG' )
+ {
+ if ( confirm( 'Do you want to transform the selected image on a image button?' ) )
+ oImage = null ;
+ }
+ else if ( bHasImage && !bImageButton && oImage.tagName == 'INPUT' )
+ {
+ if ( confirm( 'Do you want to transform the selected image button on a simple image?' ) )
+ oImage = null ;
+ }
+
+ if ( !bHasImage )
+ {
+ if ( bImageButton )
+ {
+ oImage = FCK.EditorDocument.createElement( 'INPUT' ) ;
+ oImage.type = 'image' ;
+ oImage = FCK.InsertElementAndGetIt( oImage ) ;
+ }
+ else
+ oImage = FCK.CreateElement( 'IMG' ) ;
+ }
+ else
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ UpdateImage( oImage ) ;
+
+ var sLnkUrl = GetE('txtLnkUrl').value.Trim() ;
+
+ if ( sLnkUrl.length == 0 )
+ {
+ if ( oLink )
+ FCK.ExecuteNamedCommand( 'Unlink' ) ;
+ }
+ else
+ {
+ if ( oLink ) // Modifying an existent link.
+ oLink.href = sLnkUrl ;
+ else // Creating a new link.
+ {
+ if ( !bHasImage )
+ oEditor.FCKSelection.SelectNode( oImage ) ;
+
+ oLink = oEditor.FCK.CreateLink( sLnkUrl )[0] ;
+
+ if ( !bHasImage )
+ {
+ oEditor.FCKSelection.SelectNode( oLink ) ;
+ oEditor.FCKSelection.Collapse( false ) ;
+ }
+ }
+
+ SetAttribute( oLink, '_fcksavedurl', sLnkUrl ) ;
+ SetAttribute( oLink, 'target', GetE('cmbLnkTarget').value ) ;
+ }
+
+ return true ;
+}
+
+function UpdateImage( e, skipId )
+{
+ e.src = GetE('txtUrl').value ;
+ SetAttribute( e, "_fcksavedurl", GetE('txtUrl').value ) ;
+ SetAttribute( e, "alt" , GetE('txtAlt').value ) ;
+ SetAttribute( e, "width" , GetE('txtWidth').value ) ;
+ SetAttribute( e, "height", GetE('txtHeight').value ) ;
+ SetAttribute( e, "vspace", GetE('txtVSpace').value ) ;
+ SetAttribute( e, "hspace", GetE('txtHSpace').value ) ;
+ SetAttribute( e, "border", GetE('txtBorder').value ) ;
+ SetAttribute( e, "align" , GetE('cmbAlign').value ) ;
+
+ // Advances Attributes
+
+ if ( ! skipId )
+ SetAttribute( e, 'id', GetE('txtAttId').value ) ;
+
+ SetAttribute( e, 'dir' , GetE('cmbAttLangDir').value ) ;
+ SetAttribute( e, 'lang' , GetE('txtAttLangCode').value ) ;
+ SetAttribute( e, 'title' , GetE('txtAttTitle').value ) ;
+ SetAttribute( e, 'longDesc' , GetE('txtLongDesc').value ) ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ {
+ e.className = GetE('txtAttClasses').value ;
+ e.style.cssText = GetE('txtAttStyle').value ;
+ }
+ else
+ {
+ SetAttribute( e, 'class' , GetE('txtAttClasses').value ) ;
+ SetAttribute( e, 'style', GetE('txtAttStyle').value ) ;
+ }
+}
+
+var eImgPreview ;
+var eImgPreviewLink ;
+
+function SetPreviewElements( imageElement, linkElement )
+{
+ eImgPreview = imageElement ;
+ eImgPreviewLink = linkElement ;
+
+ UpdatePreview() ;
+ UpdateOriginal() ;
+
+ bPreviewInitialized = true ;
+}
+
+function UpdatePreview()
+{
+ if ( !eImgPreview || !eImgPreviewLink )
+ return ;
+
+ if ( GetE('txtUrl').value.length == 0 )
+ eImgPreviewLink.style.display = 'none' ;
+ else
+ {
+ UpdateImage( eImgPreview, true ) ;
+
+ if ( GetE('txtLnkUrl').value.Trim().length > 0 )
+ eImgPreviewLink.href = 'javascript:void(null);' ;
+ else
+ SetAttribute( eImgPreviewLink, 'href', '' ) ;
+
+ eImgPreviewLink.style.display = '' ;
+ }
+}
+
+var bLockRatio = true ;
+
+function SwitchLock( lockButton )
+{
+ bLockRatio = !bLockRatio ;
+ lockButton.className = bLockRatio ? 'BtnLocked' : 'BtnUnlocked' ;
+ lockButton.title = bLockRatio ? 'Lock sizes' : 'Unlock sizes' ;
+
+ if ( bLockRatio )
+ {
+ if ( GetE('txtWidth').value.length > 0 )
+ OnSizeChanged( 'Width', GetE('txtWidth').value ) ;
+ else
+ OnSizeChanged( 'Height', GetE('txtHeight').value ) ;
+ }
+}
+
+// Fired when the width or height input texts change
+function OnSizeChanged( dimension, value )
+{
+ // Verifies if the aspect ration has to be mantained
+ if ( oImageOriginal && bLockRatio )
+ {
+ var e = dimension == 'Width' ? GetE('txtHeight') : GetE('txtWidth') ;
+
+ if ( value.length == 0 || isNaN( value ) )
+ {
+ e.value = '' ;
+ return ;
+ }
+
+ if ( dimension == 'Width' )
+ value = value == 0 ? 0 : Math.round( oImageOriginal.height * ( value / oImageOriginal.width ) ) ;
+ else
+ value = value == 0 ? 0 : Math.round( oImageOriginal.width * ( value / oImageOriginal.height ) ) ;
+
+ if ( !isNaN( value ) )
+ e.value = value ;
+ }
+
+ UpdatePreview() ;
+}
+
+// Fired when the Reset Size button is clicked
+function ResetSizes()
+{
+ if ( ! oImageOriginal ) return ;
+
+ GetE('txtWidth').value = oImageOriginal.width ;
+ GetE('txtHeight').value = oImageOriginal.height ;
+
+ UpdatePreview() ;
+}
+
+function BrowseServer()
+{
+ OpenServerBrowser(
+ 'Image',
+ FCKConfig.ImageBrowserURL,
+ FCKConfig.ImageBrowserWindowWidth,
+ FCKConfig.ImageBrowserWindowHeight ) ;
+}
+
+function LnkBrowseServer()
+{
+ OpenServerBrowser(
+ 'Link',
+ FCKConfig.LinkBrowserURL,
+ FCKConfig.LinkBrowserWindowWidth,
+ FCKConfig.LinkBrowserWindowHeight ) ;
+}
+
+function OpenServerBrowser( type, url, width, height )
+{
+ sActualBrowser = type ;
+ OpenFileBrowser( url, width, height ) ;
+}
+
+var sActualBrowser ;
+
+function SetUrl( url, width, height, alt )
+{
+ if ( sActualBrowser == 'Link' )
+ {
+ GetE('txtLnkUrl').value = url ;
+ UpdatePreview() ;
+ }
+ else
+ {
+ GetE('txtUrl').value = url ;
+ GetE('txtWidth').value = width ? width : '' ;
+ GetE('txtHeight').value = height ? height : '' ;
+
+ if ( alt )
+ GetE('txtAlt').value = alt;
+
+ UpdatePreview() ;
+ UpdateOriginal( true ) ;
+ }
+
+ window.parent.SetSelectedTab( 'Info' ) ;
+}
+
+function OnUploadCompleted( errorNumber, fileUrl, fileName, customMsg )
+{
+ switch ( errorNumber )
+ {
+ case 0 : // No errors
+ alert( 'Your file has been successfully uploaded' ) ;
+ break ;
+ case 1 : // Custom error
+ alert( customMsg ) ;
+ return ;
+ case 101 : // Custom warning
+ alert( customMsg ) ;
+ break ;
+ case 201 :
+ alert( 'A file with the same name is already available. The uploaded file has been renamed to "' + fileName + '"' ) ;
+ break ;
+ case 202 :
+ alert( 'Invalid file type' ) ;
+ return ;
+ case 203 :
+ alert( "Security error. You probably don't have enough permissions to upload. Please check your server." ) ;
+ return ;
+ default :
+ alert( 'Error on file upload. Error number: ' + errorNumber ) ;
+ return ;
+ }
+
+ sActualBrowser = '' ;
+ SetUrl( fileUrl ) ;
+ GetE('frmUpload').reset() ;
+}
+
+var oUploadAllowedExtRegex = new RegExp( FCKConfig.ImageUploadAllowedExtensions, 'i' ) ;
+var oUploadDeniedExtRegex = new RegExp( FCKConfig.ImageUploadDeniedExtensions, 'i' ) ;
+
+function CheckUpload()
+{
+ var sFile = GetE('txtUploadFile').value ;
+
+ if ( sFile.length == 0 )
+ {
+ alert( 'Please select a file to upload' ) ;
+ return false ;
+ }
+
+ if ( ( FCKConfig.ImageUploadAllowedExtensions.length > 0 && !oUploadAllowedExtRegex.test( sFile ) ) ||
+ ( FCKConfig.ImageUploadDeniedExtensions.length > 0 && oUploadDeniedExtRegex.test( sFile ) ) )
+ {
+ OnUploadCompleted( 202 ) ;
+ return false ;
+ }
+
+ return true ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image_preview.html b/httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image_preview.html
new file mode 100644
index 0000000..21bdc25
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_image/fck_image_preview.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Preview page for the Image dialog window.
+ *
+ * Curiosity: http://en.wikipedia.org/wiki/Lorem_ipsum
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <link href="../common/fck_dialog_common.css" rel="stylesheet" type="text/css" />
+ <script type="text/javascript">
+
+// Sets the Skin CSS
+document.write( '<link href="' + window.parent.FCKConfig.SkinPath + 'fck_dialog.css" type="text/css" rel="stylesheet">' ) ;
+
+if ( window.parent.FCKConfig.BaseHref.length > 0 )
+ document.write( '<base href="' + window.parent.FCKConfig.BaseHref + '">' ) ;
+
+window.onload = function()
+{
+ window.parent.SetPreviewElements(
+ document.getElementById( 'imgPreview' ),
+ document.getElementById( 'lnkPreview' ) ) ;
+}
+
+ </script>
+</head>
+<body style="color: #000000; background-color: #ffffff">
+ <a id="lnkPreview" onclick="return false;" style="cursor: default">
+ <img id="imgPreview" onload="window.parent.UpdateOriginal();" style="display: none" /></a>Lorem
+ ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas feugiat consequat diam.
+ Maecenas metus. Vivamus diam purus, cursus a, commodo non, facilisis vitae, nulla.
+ Aenean dictum lacinia tortor. Nunc iaculis, nibh non iaculis aliquam, orci felis
+ euismod neque, sed ornare massa mauris sed velit. Nulla pretium mi et risus. Fusce
+ mi pede, tempor id, cursus ac, ullamcorper nec, enim. Sed tortor. Curabitur molestie.
+ Duis velit augue, condimentum at, ultrices a, luctus ut, orci. Donec pellentesque
+ egestas eros. Integer cursus, augue in cursus faucibus, eros pede bibendum sem,
+ in tempus tellus justo quis ligula. Etiam eget tortor. Vestibulum rutrum, est ut
+ placerat elementum, lectus nisl aliquam velit, tempor aliquam eros nunc nonummy
+ metus. In eros metus, gravida a, gravida sed, lobortis id, turpis. Ut ultrices,
+ ipsum at venenatis fringilla, sem nulla lacinia tellus, eget aliquet turpis mauris
+ non enim. Nam turpis. Suspendisse lacinia. Curabitur ac tortor ut ipsum egestas
+ elementum. Nunc imperdiet gravida mauris.
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_link.html b/httemplate/elements/fckeditor/editor/dialog/fck_link.html
new file mode 100644
index 0000000..c8f37b6
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_link.html
@@ -0,0 +1,293 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Link dialog window.
+-->
+<html>
+ <head>
+ <title>Link Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script src="fck_link/fck_link.js" type="text/javascript"></script>
+ </head>
+ <body scroll="no" style="OVERFLOW: hidden">
+ <div id="divInfo" style="DISPLAY: none">
+ <span fckLang="DlgLnkType">Link Type</span><br />
+ <select id="cmbLinkType" onchange="SetLinkType(this.value);">
+ <option value="url" fckLang="DlgLnkTypeURL" selected="selected">URL</option>
+ <option value="anchor" fckLang="DlgLnkTypeAnchor">Anchor in this page</option>
+ <option value="email" fckLang="DlgLnkTypeEMail">E-Mail</option>
+ </select>
+ <br />
+ <br />
+ <div id="divLinkTypeUrl">
+ <table cellspacing="0" cellpadding="0" width="100%" border="0" dir="ltr">
+ <tr>
+ <td nowrap="nowrap">
+ <span fckLang="DlgLnkProto">Protocol</span><br />
+ <select id="cmbLinkProtocol">
+ <option value="http://" selected="selected">http://</option>
+ <option value="https://">https://</option>
+ <option value="ftp://">ftp://</option>
+ <option value="news://">news://</option>
+ <option value="" fckLang="DlgLnkProtoOther">&lt;other&gt;</option>
+ </select>
+ </td>
+ <td nowrap="nowrap">&nbsp;</td>
+ <td nowrap="nowrap" width="100%">
+ <span fckLang="DlgLnkURL">URL</span><br />
+ <input id="txtUrl" style="WIDTH: 100%" type="text" onkeyup="OnUrlChange();" onchange="OnUrlChange();" />
+ </td>
+ </tr>
+ </table>
+ <br />
+ <div id="divBrowseServer">
+ <input type="button" value="Browse Server" fckLang="DlgBtnBrowseServer" onclick="BrowseServer();" />
+ </div>
+ </div>
+ <div id="divLinkTypeAnchor" style="DISPLAY: none" align="center">
+ <div id="divSelAnchor" style="DISPLAY: none">
+ <table cellspacing="0" cellpadding="0" border="0" width="70%">
+ <tr>
+ <td colspan="3">
+ <span fckLang="DlgLnkAnchorSel">Select an Anchor</span>
+ </td>
+ </tr>
+ <tr>
+ <td width="50%">
+ <span fckLang="DlgLnkAnchorByName">By Anchor Name</span><br />
+ <select id="cmbAnchorName" onchange="GetE('cmbAnchorId').value='';" style="WIDTH: 100%">
+ <option value="" selected="selected"></option>
+ </select>
+ </td>
+ <td>&nbsp;&nbsp;&nbsp;</td>
+ <td width="50%">
+ <span fckLang="DlgLnkAnchorById">By Element Id</span><br />
+ <select id="cmbAnchorId" onchange="GetE('cmbAnchorName').value='';" style="WIDTH: 100%">
+ <option value="" selected="selected"></option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="divNoAnchor" style="DISPLAY: none">
+ <span fckLang="DlgLnkNoAnchors">&lt;No anchors available in the document&gt;</span>
+ </div>
+ </div>
+ <div id="divLinkTypeEMail" style="DISPLAY: none">
+ <span fckLang="DlgLnkEMail">E-Mail Address</span><br />
+ <input id="txtEMailAddress" style="WIDTH: 100%" type="text" /><br />
+ <span fckLang="DlgLnkEMailSubject">Message Subject</span><br />
+ <input id="txtEMailSubject" style="WIDTH: 100%" type="text" /><br />
+ <span fckLang="DlgLnkEMailBody">Message Body</span><br />
+ <textarea id="txtEMailBody" style="WIDTH: 100%" rows="3" cols="20"></textarea>
+ </div>
+ </div>
+ <div id="divUpload" style="DISPLAY: none">
+ <form id="frmUpload" method="post" target="UploadWindow" enctype="multipart/form-data" action="" onsubmit="return CheckUpload();">
+ <span fckLang="DlgLnkUpload">Upload</span><br />
+ <input id="txtUploadFile" style="WIDTH: 100%" type="file" size="40" name="NewFile" /><br />
+ <br />
+ <input id="btnUpload" type="submit" value="Send it to the Server" fckLang="DlgLnkBtnUpload" />
+ <iframe name="UploadWindow" style="DISPLAY: none" src="javascript:void(0)"></iframe>
+ </form>
+ </div>
+ <div id="divTarget" style="DISPLAY: none">
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <span fckLang="DlgLnkTarget">Target</span><br />
+ <select id="cmbTarget" onchange="SetTarget(this.value);">
+ <option value="" fckLang="DlgGenNotSet" selected="selected">&lt;not set&gt;</option>
+ <option value="frame" fckLang="DlgLnkTargetFrame">&lt;frame&gt;</option>
+ <option value="popup" fckLang="DlgLnkTargetPopup">&lt;popup window&gt;</option>
+ <option value="_blank" fckLang="DlgLnkTargetBlank">New Window (_blank)</option>
+ <option value="_top" fckLang="DlgLnkTargetTop">Topmost Window (_top)</option>
+ <option value="_self" fckLang="DlgLnkTargetSelf">Same Window (_self)</option>
+ <option value="_parent" fckLang="DlgLnkTargetParent">Parent Window (_parent)</option>
+ </select>
+ </td>
+ <td>&nbsp;</td>
+ <td id="tdTargetFrame" nowrap="nowrap" width="100%">
+ <span fckLang="DlgLnkTargetFrameName">Target Frame Name</span><br />
+ <input id="txtTargetFrame" style="WIDTH: 100%" type="text" onkeyup="OnTargetNameChange();"
+ onchange="OnTargetNameChange();" />
+ </td>
+ <td id="tdPopupName" style="DISPLAY: none" nowrap="nowrap" width="100%">
+ <span fckLang="DlgLnkPopWinName">Popup Window Name</span><br />
+ <input id="txtPopupName" style="WIDTH: 100%" type="text" />
+ </td>
+ </tr>
+ </table>
+ <br />
+ <table id="tablePopupFeatures" style="DISPLAY: none" cellspacing="0" cellpadding="0" align="center"
+ border="0">
+ <tr>
+ <td>
+ <span fckLang="DlgLnkPopWinFeat">Popup Window Features</span><br />
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td valign="top" nowrap="nowrap" width="50%">
+ <input id="chkPopupResizable" name="chkFeature" value="resizable" type="checkbox" /><label for="chkPopupResizable" fckLang="DlgLnkPopResize">Resizable</label><br />
+ <input id="chkPopupLocationBar" name="chkFeature" value="location" type="checkbox" /><label for="chkPopupLocationBar" fckLang="DlgLnkPopLocation">Location
+ Bar</label><br />
+ <input id="chkPopupManuBar" name="chkFeature" value="menubar" type="checkbox" /><label for="chkPopupManuBar" fckLang="DlgLnkPopMenu">Menu
+ Bar</label><br />
+ <input id="chkPopupScrollBars" name="chkFeature" value="scrollbars" type="checkbox" /><label for="chkPopupScrollBars" fckLang="DlgLnkPopScroll">Scroll
+ Bars</label>
+ </td>
+ <td></td>
+ <td valign="top" nowrap="nowrap" width="50%">
+ <input id="chkPopupStatusBar" name="chkFeature" value="status" type="checkbox" /><label for="chkPopupStatusBar" fckLang="DlgLnkPopStatus">Status
+ Bar</label><br />
+ <input id="chkPopupToolbar" name="chkFeature" value="toolbar" type="checkbox" /><label for="chkPopupToolbar" fckLang="DlgLnkPopToolbar">Toolbar</label><br />
+ <input id="chkPopupFullScreen" name="chkFeature" value="fullscreen" type="checkbox" /><label for="chkPopupFullScreen" fckLang="DlgLnkPopFullScrn">Full
+ Screen (IE)</label><br />
+ <input id="chkPopupDependent" name="chkFeature" value="dependent" type="checkbox" /><label for="chkPopupDependent" fckLang="DlgLnkPopDependent">Dependent
+ (Netscape)</label>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap="nowrap" width="50%">&nbsp;</td>
+ <td></td>
+ <td valign="top" nowrap="nowrap" width="50%"></td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td nowrap="nowrap"><span fckLang="DlgLnkPopWidth">Width</span></td>
+ <td>&nbsp;<input id="txtPopupWidth" type="text" maxlength="4" size="4" /></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap"><span fckLang="DlgLnkPopHeight">Height</span></td>
+ <td>&nbsp;<input id="txtPopupHeight" type="text" maxlength="4" size="4" /></td>
+ </tr>
+ </table>
+ </td>
+ <td>&nbsp;&nbsp;</td>
+ <td valign="top">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td nowrap="nowrap"><span fckLang="DlgLnkPopLeft">Left Position</span></td>
+ <td>&nbsp;<input id="txtPopupLeft" type="text" maxlength="4" size="4" /></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap"><span fckLang="DlgLnkPopTop">Top Position</span></td>
+ <td>&nbsp;<input id="txtPopupTop" type="text" maxlength="4" size="4" /></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="divAttribs" style="DISPLAY: none">
+ <table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
+ <tr>
+ <td valign="top" width="50%">
+ <span fckLang="DlgGenId">Id</span><br />
+ <input id="txtAttId" style="WIDTH: 100%" type="text" />
+ </td>
+ <td width="1"></td>
+ <td valign="top">
+ <table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
+ <tr>
+ <td width="60%">
+ <span fckLang="DlgGenLangDir">Language Direction</span><br />
+ <select id="cmbAttLangDir" style="WIDTH: 100%">
+ <option value="" fckLang="DlgGenNotSet" selected>&lt;not set&gt;</option>
+ <option value="ltr" fckLang="DlgGenLangDirLtr">Left to Right (LTR)</option>
+ <option value="rtl" fckLang="DlgGenLangDirRtl">Right to Left (RTL)</option>
+ </select>
+ </td>
+ <td width="1%">&nbsp;&nbsp;&nbsp;</td>
+ <td nowrap="nowrap"><span fckLang="DlgGenAccessKey">Access Key</span><br />
+ <input id="txtAttAccessKey" style="WIDTH: 100%" type="text" maxlength="1" size="1" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" width="50%">
+ <span fckLang="DlgGenName">Name</span><br />
+ <input id="txtAttName" style="WIDTH: 100%" type="text" />
+ </td>
+ <td width="1"></td>
+ <td valign="top">
+ <table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
+ <tr>
+ <td width="60%">
+ <span fckLang="DlgGenLangCode">Language Code</span><br />
+ <input id="txtAttLangCode" style="WIDTH: 100%" type="text" />
+ </td>
+ <td width="1%">&nbsp;&nbsp;&nbsp;</td>
+ <td nowrap="nowrap">
+ <span fckLang="DlgGenTabIndex">Tab Index</span><br />
+ <input id="txtAttTabIndex" style="WIDTH: 100%" type="text" maxlength="5" size="5" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" width="50%">&nbsp;</td>
+ <td width="1"></td>
+ <td valign="top"></td>
+ </tr>
+ <tr>
+ <td valign="top" width="50%">
+ <span fckLang="DlgGenTitle">Advisory Title</span><br />
+ <input id="txtAttTitle" style="WIDTH: 100%" type="text" />
+ </td>
+ <td width="1">&nbsp;&nbsp;&nbsp;</td>
+ <td valign="top">
+ <span fckLang="DlgGenContType">Advisory Content Type</span><br />
+ <input id="txtAttContentType" style="WIDTH: 100%" type="text" />
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <span fckLang="DlgGenClass">Stylesheet Classes</span><br />
+ <input id="txtAttClasses" style="WIDTH: 100%" type="text" />
+ </td>
+ <td></td>
+ <td valign="top">
+ <span fckLang="DlgGenLinkCharset">Linked Resource Charset</span><br />
+ <input id="txtAttCharSet" style="WIDTH: 100%" type="text" />
+ </td>
+ </tr>
+ </table>
+ <table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
+ <tr>
+ <td>
+ <span fckLang="DlgGenStyle">Style</span><br />
+ <input id="txtAttStyle" style="WIDTH: 100%" type="text" />
+ </td>
+ </tr>
+ </table>
+ </div>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_link/fck_link.js b/httemplate/elements/fckeditor/editor/dialog/fck_link/fck_link.js
new file mode 100644
index 0000000..6d96499
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_link/fck_link.js
@@ -0,0 +1,698 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Scripts related to the Link dialog window (see fck_link.html).
+ */
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+var FCKLang = oEditor.FCKLang ;
+var FCKConfig = oEditor.FCKConfig ;
+var FCKRegexLib = oEditor.FCKRegexLib ;
+
+//#### Dialog Tabs
+
+// Set the dialog tabs.
+window.parent.AddTab( 'Info', FCKLang.DlgLnkInfoTab ) ;
+
+if ( !FCKConfig.LinkDlgHideTarget )
+ window.parent.AddTab( 'Target', FCKLang.DlgLnkTargetTab, true ) ;
+
+if ( FCKConfig.LinkUpload )
+ window.parent.AddTab( 'Upload', FCKLang.DlgLnkUpload, true ) ;
+
+if ( !FCKConfig.LinkDlgHideAdvanced )
+ window.parent.AddTab( 'Advanced', FCKLang.DlgAdvancedTag ) ;
+
+// Function called when a dialog tag is selected.
+function OnDialogTabChange( tabCode )
+{
+ ShowE('divInfo' , ( tabCode == 'Info' ) ) ;
+ ShowE('divTarget' , ( tabCode == 'Target' ) ) ;
+ ShowE('divUpload' , ( tabCode == 'Upload' ) ) ;
+ ShowE('divAttribs' , ( tabCode == 'Advanced' ) ) ;
+
+ window.parent.SetAutoSize( true ) ;
+}
+
+//#### Regular Expressions library.
+var oRegex = new Object() ;
+
+oRegex.UriProtocol = /^(((http|https|ftp|news):\/\/)|mailto:)/gi ;
+
+oRegex.UrlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/gi ;
+
+oRegex.UrlOnChangeTestOther = /^((javascript:)|[#\/\.])/gi ;
+
+oRegex.ReserveTarget = /^_(blank|self|top|parent)$/i ;
+
+oRegex.PopupUri = /^javascript:void\(\s*window.open\(\s*'([^']+)'\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*\)\s*$/ ;
+
+// Accessible popups
+oRegex.OnClickPopup = /^\s*on[cC]lick="\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*"$/ ;
+
+oRegex.PopupFeatures = /(?:^|,)([^=]+)=(\d+|yes|no)/gi ;
+
+//#### Parser Functions
+
+var oParser = new Object() ;
+
+oParser.ParseEMailUrl = function( emailUrl )
+{
+ // Initializes the EMailInfo object.
+ var oEMailInfo = new Object() ;
+ oEMailInfo.Address = '' ;
+ oEMailInfo.Subject = '' ;
+ oEMailInfo.Body = '' ;
+
+ var oParts = emailUrl.match( /^([^\?]+)\??(.+)?/ ) ;
+ if ( oParts )
+ {
+ // Set the e-mail address.
+ oEMailInfo.Address = oParts[1] ;
+
+ // Look for the optional e-mail parameters.
+ if ( oParts[2] )
+ {
+ var oMatch = oParts[2].match( /(^|&)subject=([^&]+)/i ) ;
+ if ( oMatch ) oEMailInfo.Subject = decodeURIComponent( oMatch[2] ) ;
+
+ oMatch = oParts[2].match( /(^|&)body=([^&]+)/i ) ;
+ if ( oMatch ) oEMailInfo.Body = decodeURIComponent( oMatch[2] ) ;
+ }
+ }
+
+ return oEMailInfo ;
+}
+
+oParser.CreateEMailUri = function( address, subject, body )
+{
+ var sBaseUri = 'mailto:' + address ;
+
+ var sParams = '' ;
+
+ if ( subject.length > 0 )
+ sParams = '?subject=' + encodeURIComponent( subject ) ;
+
+ if ( body.length > 0 )
+ {
+ sParams += ( sParams.length == 0 ? '?' : '&' ) ;
+ sParams += 'body=' + encodeURIComponent( body ) ;
+ }
+
+ return sBaseUri + sParams ;
+}
+
+//#### Initialization Code
+
+// oLink: The actual selected link in the editor.
+var oLink = FCK.Selection.MoveToAncestorNode( 'A' ) ;
+if ( oLink )
+ FCK.Selection.SelectNode( oLink ) ;
+
+window.onload = function()
+{
+ // Translate the dialog box texts.
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ // Fill the Anchor Names and Ids combos.
+ LoadAnchorNamesAndIds() ;
+
+ // Load the selected link information (if any).
+ LoadSelection() ;
+
+ // Update the dialog box.
+ SetLinkType( GetE('cmbLinkType').value ) ;
+
+ // Show/Hide the "Browse Server" button.
+ GetE('divBrowseServer').style.display = FCKConfig.LinkBrowser ? '' : 'none' ;
+
+ // Show the initial dialog content.
+ GetE('divInfo').style.display = '' ;
+
+ // Set the actual uploader URL.
+ if ( FCKConfig.LinkUpload )
+ GetE('frmUpload').action = FCKConfig.LinkUploadURL ;
+
+ // Set the default target (from configuration).
+ SetDefaultTarget() ;
+
+ // Activate the "OK" button.
+ window.parent.SetOkButton( true ) ;
+}
+
+var bHasAnchors ;
+
+function LoadAnchorNamesAndIds()
+{
+ // Since version 2.0, the anchors are replaced in the DOM by IMGs so the user see the icon
+ // to edit them. So, we must look for that images now.
+ var aAnchors = new Array() ;
+ var i ;
+ var oImages = oEditor.FCK.EditorDocument.getElementsByTagName( 'IMG' ) ;
+ for( i = 0 ; i < oImages.length ; i++ )
+ {
+ if ( oImages[i].getAttribute('_fckanchor') )
+ aAnchors[ aAnchors.length ] = oEditor.FCK.GetRealElement( oImages[i] ) ;
+ }
+
+ // Add also real anchors
+ var oLinks = oEditor.FCK.EditorDocument.getElementsByTagName( 'A' ) ;
+ for( i = 0 ; i < oLinks.length ; i++ )
+ {
+ if ( oLinks[i].name && ( oLinks[i].name.length > 0 ) )
+ aAnchors[ aAnchors.length ] = oLinks[i] ;
+ }
+
+ var aIds = oEditor.FCKTools.GetAllChildrenIds( oEditor.FCK.EditorDocument.body ) ;
+
+ bHasAnchors = ( aAnchors.length > 0 || aIds.length > 0 ) ;
+
+ for ( i = 0 ; i < aAnchors.length ; i++ )
+ {
+ var sName = aAnchors[i].name ;
+ if ( sName && sName.length > 0 )
+ oEditor.FCKTools.AddSelectOption( GetE('cmbAnchorName'), sName, sName ) ;
+ }
+
+ for ( i = 0 ; i < aIds.length ; i++ )
+ {
+ oEditor.FCKTools.AddSelectOption( GetE('cmbAnchorId'), aIds[i], aIds[i] ) ;
+ }
+
+ ShowE( 'divSelAnchor' , bHasAnchors ) ;
+ ShowE( 'divNoAnchor' , !bHasAnchors ) ;
+}
+
+function LoadSelection()
+{
+ if ( !oLink ) return ;
+
+ var sType = 'url' ;
+
+ // Get the actual Link href.
+ var sHRef = oLink.getAttribute( '_fcksavedurl' ) ;
+ if ( sHRef == null )
+ sHRef = oLink.getAttribute( 'href' , 2 ) || '' ;
+
+ // Look for a popup javascript link.
+ var oPopupMatch = oRegex.PopupUri.exec( sHRef ) ;
+ if( oPopupMatch )
+ {
+ GetE('cmbTarget').value = 'popup' ;
+ sHRef = oPopupMatch[1] ;
+ FillPopupFields( oPopupMatch[2], oPopupMatch[3] ) ;
+ SetTarget( 'popup' ) ;
+ }
+
+ // Accesible popups, the popup data is in the onclick attribute
+ if ( !oPopupMatch ) {
+ var onclick = oLink.getAttribute( 'onclick_fckprotectedatt' ) ;
+ oPopupMatch = oRegex.OnClickPopup.exec( onclick ) ;
+ if( oPopupMatch )
+ {
+ GetE( 'cmbTarget' ).value = 'popup' ;
+ FillPopupFields( oPopupMatch[1], oPopupMatch[2] ) ;
+ SetTarget( 'popup' ) ;
+ }
+ }
+
+ // Search for the protocol.
+ var sProtocol = oRegex.UriProtocol.exec( sHRef ) ;
+
+ if ( sProtocol )
+ {
+ sProtocol = sProtocol[0].toLowerCase() ;
+ GetE('cmbLinkProtocol').value = sProtocol ;
+
+ // Remove the protocol and get the remainig URL.
+ var sUrl = sHRef.replace( oRegex.UriProtocol, '' ) ;
+
+ if ( sProtocol == 'mailto:' ) // It is an e-mail link.
+ {
+ sType = 'email' ;
+
+ var oEMailInfo = oParser.ParseEMailUrl( sUrl ) ;
+ GetE('txtEMailAddress').value = oEMailInfo.Address ;
+ GetE('txtEMailSubject').value = oEMailInfo.Subject ;
+ GetE('txtEMailBody').value = oEMailInfo.Body ;
+ }
+ else // It is a normal link.
+ {
+ sType = 'url' ;
+ GetE('txtUrl').value = sUrl ;
+ }
+ }
+ else if ( sHRef.substr(0,1) == '#' && sHRef.length > 1 ) // It is an anchor link.
+ {
+ sType = 'anchor' ;
+ GetE('cmbAnchorName').value = GetE('cmbAnchorId').value = sHRef.substr(1) ;
+ }
+ else // It is another type of link.
+ {
+ sType = 'url' ;
+
+ GetE('cmbLinkProtocol').value = '' ;
+ GetE('txtUrl').value = sHRef ;
+ }
+
+ if ( !oPopupMatch )
+ {
+ // Get the target.
+ var sTarget = oLink.target ;
+
+ if ( sTarget && sTarget.length > 0 )
+ {
+ if ( oRegex.ReserveTarget.test( sTarget ) )
+ {
+ sTarget = sTarget.toLowerCase() ;
+ GetE('cmbTarget').value = sTarget ;
+ }
+ else
+ GetE('cmbTarget').value = 'frame' ;
+ GetE('txtTargetFrame').value = sTarget ;
+ }
+ }
+
+ // Get Advances Attributes
+ GetE('txtAttId').value = oLink.id ;
+ GetE('txtAttName').value = oLink.name ;
+ GetE('cmbAttLangDir').value = oLink.dir ;
+ GetE('txtAttLangCode').value = oLink.lang ;
+ GetE('txtAttAccessKey').value = oLink.accessKey ;
+ GetE('txtAttTabIndex').value = oLink.tabIndex <= 0 ? '' : oLink.tabIndex ;
+ GetE('txtAttTitle').value = oLink.title ;
+ GetE('txtAttContentType').value = oLink.type ;
+ GetE('txtAttCharSet').value = oLink.charset ;
+
+ var sClass ;
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ {
+ sClass = oLink.getAttribute('className',2) || '' ;
+ // Clean up temporary classes for internal use:
+ sClass = sClass.replace( FCKRegexLib.FCK_Class, '' ) ;
+
+ GetE('txtAttStyle').value = oLink.style.cssText ;
+ }
+ else
+ {
+ sClass = oLink.getAttribute('class',2) || '' ;
+ GetE('txtAttStyle').value = oLink.getAttribute('style',2) || '' ;
+ }
+ GetE('txtAttClasses').value = sClass ;
+
+ // Update the Link type combo.
+ GetE('cmbLinkType').value = sType ;
+}
+
+//#### Link type selection.
+function SetLinkType( linkType )
+{
+ ShowE('divLinkTypeUrl' , (linkType == 'url') ) ;
+ ShowE('divLinkTypeAnchor' , (linkType == 'anchor') ) ;
+ ShowE('divLinkTypeEMail' , (linkType == 'email') ) ;
+
+ if ( !FCKConfig.LinkDlgHideTarget )
+ window.parent.SetTabVisibility( 'Target' , (linkType == 'url') ) ;
+
+ if ( FCKConfig.LinkUpload )
+ window.parent.SetTabVisibility( 'Upload' , (linkType == 'url') ) ;
+
+ if ( !FCKConfig.LinkDlgHideAdvanced )
+ window.parent.SetTabVisibility( 'Advanced' , (linkType != 'anchor' || bHasAnchors) ) ;
+
+ if ( linkType == 'email' )
+ window.parent.SetAutoSize( true ) ;
+}
+
+//#### Target type selection.
+function SetTarget( targetType )
+{
+ GetE('tdTargetFrame').style.display = ( targetType == 'popup' ? 'none' : '' ) ;
+ GetE('tdPopupName').style.display =
+ GetE('tablePopupFeatures').style.display = ( targetType == 'popup' ? '' : 'none' ) ;
+
+ switch ( targetType )
+ {
+ case "_blank" :
+ case "_self" :
+ case "_parent" :
+ case "_top" :
+ GetE('txtTargetFrame').value = targetType ;
+ break ;
+ case "" :
+ GetE('txtTargetFrame').value = '' ;
+ break ;
+ }
+
+ if ( targetType == 'popup' )
+ window.parent.SetAutoSize( true ) ;
+}
+
+//#### Called while the user types the URL.
+function OnUrlChange()
+{
+ var sUrl = GetE('txtUrl').value ;
+ var sProtocol = oRegex.UrlOnChangeProtocol.exec( sUrl ) ;
+
+ if ( sProtocol )
+ {
+ sUrl = sUrl.substr( sProtocol[0].length ) ;
+ GetE('txtUrl').value = sUrl ;
+ GetE('cmbLinkProtocol').value = sProtocol[0].toLowerCase() ;
+ }
+ else if ( oRegex.UrlOnChangeTestOther.test( sUrl ) )
+ {
+ GetE('cmbLinkProtocol').value = '' ;
+ }
+}
+
+//#### Called while the user types the target name.
+function OnTargetNameChange()
+{
+ var sFrame = GetE('txtTargetFrame').value ;
+
+ if ( sFrame.length == 0 )
+ GetE('cmbTarget').value = '' ;
+ else if ( oRegex.ReserveTarget.test( sFrame ) )
+ GetE('cmbTarget').value = sFrame.toLowerCase() ;
+ else
+ GetE('cmbTarget').value = 'frame' ;
+}
+
+// Accesible popups
+function BuildOnClickPopup()
+{
+ var sWindowName = "'" + GetE('txtPopupName').value.replace(/\W/gi, "") + "'" ;
+
+ var sFeatures = '' ;
+ var aChkFeatures = document.getElementsByName( 'chkFeature' ) ;
+ for ( var i = 0 ; i < aChkFeatures.length ; i++ )
+ {
+ if ( i > 0 ) sFeatures += ',' ;
+ sFeatures += aChkFeatures[i].value + '=' + ( aChkFeatures[i].checked ? 'yes' : 'no' ) ;
+ }
+
+ if ( GetE('txtPopupWidth').value.length > 0 ) sFeatures += ',width=' + GetE('txtPopupWidth').value ;
+ if ( GetE('txtPopupHeight').value.length > 0 ) sFeatures += ',height=' + GetE('txtPopupHeight').value ;
+ if ( GetE('txtPopupLeft').value.length > 0 ) sFeatures += ',left=' + GetE('txtPopupLeft').value ;
+ if ( GetE('txtPopupTop').value.length > 0 ) sFeatures += ',top=' + GetE('txtPopupTop').value ;
+
+ if ( sFeatures != '' )
+ sFeatures = sFeatures + ",status" ;
+
+ return ( "window.open(this.href," + sWindowName + ",'" + sFeatures + "'); return false" ) ;
+}
+
+//#### Fills all Popup related fields.
+function FillPopupFields( windowName, features )
+{
+ if ( windowName )
+ GetE('txtPopupName').value = windowName ;
+
+ var oFeatures = new Object() ;
+ var oFeaturesMatch ;
+ while( ( oFeaturesMatch = oRegex.PopupFeatures.exec( features ) ) != null )
+ {
+ var sValue = oFeaturesMatch[2] ;
+ if ( sValue == ( 'yes' || '1' ) )
+ oFeatures[ oFeaturesMatch[1] ] = true ;
+ else if ( ! isNaN( sValue ) && sValue != 0 )
+ oFeatures[ oFeaturesMatch[1] ] = sValue ;
+ }
+
+ // Update all features check boxes.
+ var aChkFeatures = document.getElementsByName('chkFeature') ;
+ for ( var i = 0 ; i < aChkFeatures.length ; i++ )
+ {
+ if ( oFeatures[ aChkFeatures[i].value ] )
+ aChkFeatures[i].checked = true ;
+ }
+
+ // Update position and size text boxes.
+ if ( oFeatures['width'] ) GetE('txtPopupWidth').value = oFeatures['width'] ;
+ if ( oFeatures['height'] ) GetE('txtPopupHeight').value = oFeatures['height'] ;
+ if ( oFeatures['left'] ) GetE('txtPopupLeft').value = oFeatures['left'] ;
+ if ( oFeatures['top'] ) GetE('txtPopupTop').value = oFeatures['top'] ;
+}
+
+//#### The OK button was hit.
+function Ok()
+{
+ var sUri, sInnerHtml ;
+
+ switch ( GetE('cmbLinkType').value )
+ {
+ case 'url' :
+ sUri = GetE('txtUrl').value ;
+
+ if ( sUri.length == 0 )
+ {
+ alert( FCKLang.DlnLnkMsgNoUrl ) ;
+ return false ;
+ }
+
+ sUri = GetE('cmbLinkProtocol').value + sUri ;
+
+ break ;
+
+ case 'email' :
+ sUri = GetE('txtEMailAddress').value ;
+
+ if ( sUri.length == 0 )
+ {
+ alert( FCKLang.DlnLnkMsgNoEMail ) ;
+ return false ;
+ }
+
+ sUri = oParser.CreateEMailUri(
+ sUri,
+ GetE('txtEMailSubject').value,
+ GetE('txtEMailBody').value ) ;
+ break ;
+
+ case 'anchor' :
+ var sAnchor = GetE('cmbAnchorName').value ;
+ if ( sAnchor.length == 0 ) sAnchor = GetE('cmbAnchorId').value ;
+
+ if ( sAnchor.length == 0 )
+ {
+ alert( FCKLang.DlnLnkMsgNoAnchor ) ;
+ return false ;
+ }
+
+ sUri = '#' + sAnchor ;
+ break ;
+ }
+
+ // If no link is selected, create a new one (it may result in more than one link creation - #220).
+ var aLinks = oLink ? [ oLink ] : oEditor.FCK.CreateLink( sUri ) ;
+
+ // If no selection, no links are created, so use the uri as the link text (by dom, 2006-05-26)
+ var aHasSelection = ( aLinks.length > 0 ) ;
+ if ( !aHasSelection )
+ {
+ sInnerHtml = sUri;
+
+ // Built a better text for empty links.
+ switch ( GetE('cmbLinkType').value )
+ {
+ // anchor: use old behavior --> return true
+ case 'anchor':
+ sInnerHtml = sInnerHtml.replace( /^#/, '' ) ;
+ break ;
+
+ // url: try to get path
+ case 'url':
+ var oLinkPathRegEx = new RegExp("//?([^?\"']+)([?].*)?$") ;
+ var asLinkPath = oLinkPathRegEx.exec( sUri ) ;
+ if (asLinkPath != null)
+ sInnerHtml = asLinkPath[1]; // use matched path
+ break ;
+
+ // mailto: try to get email address
+ case 'email':
+ sInnerHtml = GetE('txtEMailAddress').value ;
+ break ;
+ }
+
+ // Create a new (empty) anchor.
+ aLinks = [ oEditor.FCK.CreateElement( 'a' ) ] ;
+ }
+
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ for ( var i = 0 ; i < aLinks.length ; i++ )
+ {
+ oLink = aLinks[i] ;
+
+ if ( aHasSelection )
+ sInnerHtml = oLink.innerHTML ; // Save the innerHTML (IE changes it if it is like an URL).
+
+ oLink.href = sUri ;
+ SetAttribute( oLink, '_fcksavedurl', sUri ) ;
+
+ // Accesible popups
+ if( GetE('cmbTarget').value == 'popup' )
+ {
+ SetAttribute( oLink, 'onclick_fckprotectedatt', " onclick=\"" + BuildOnClickPopup() + "\"") ;
+ }
+ else
+ {
+ // Check if the previous onclick was for a popup:
+ // In that case remove the onclick handler.
+ var onclick = oLink.getAttribute( 'onclick_fckprotectedatt' ) ;
+ if( oRegex.OnClickPopup.test( onclick ) )
+ SetAttribute( oLink, 'onclick_fckprotectedatt', '' ) ;
+ }
+
+ oLink.innerHTML = sInnerHtml ; // Set (or restore) the innerHTML
+
+ // Target
+ if( GetE('cmbTarget').value != 'popup' )
+ SetAttribute( oLink, 'target', GetE('txtTargetFrame').value ) ;
+ else
+ SetAttribute( oLink, 'target', null ) ;
+
+ // Let's set the "id" only for the first link to avoid duplication.
+ if ( i == 0 )
+ SetAttribute( oLink, 'id', GetE('txtAttId').value ) ;
+
+ // Advances Attributes
+ SetAttribute( oLink, 'name' , GetE('txtAttName').value ) ;
+ SetAttribute( oLink, 'dir' , GetE('cmbAttLangDir').value ) ;
+ SetAttribute( oLink, 'lang' , GetE('txtAttLangCode').value ) ;
+ SetAttribute( oLink, 'accesskey', GetE('txtAttAccessKey').value ) ;
+ SetAttribute( oLink, 'tabindex' , ( GetE('txtAttTabIndex').value > 0 ? GetE('txtAttTabIndex').value : null ) ) ;
+ SetAttribute( oLink, 'title' , GetE('txtAttTitle').value ) ;
+ SetAttribute( oLink, 'type' , GetE('txtAttContentType').value ) ;
+ SetAttribute( oLink, 'charset' , GetE('txtAttCharSet').value ) ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ {
+ var sClass = GetE('txtAttClasses').value ;
+ // If it's also an anchor add an internal class
+ if ( GetE('txtAttName').value.length != 0 )
+ sClass += ' FCK__AnchorC' ;
+ SetAttribute( oLink, 'className', sClass ) ;
+
+ oLink.style.cssText = GetE('txtAttStyle').value ;
+ }
+ else
+ {
+ SetAttribute( oLink, 'class', GetE('txtAttClasses').value ) ;
+ SetAttribute( oLink, 'style', GetE('txtAttStyle').value ) ;
+ }
+ }
+
+ // Select the (first) link.
+ oEditor.FCKSelection.SelectNode( aLinks[0] );
+
+ return true ;
+}
+
+function BrowseServer()
+{
+ OpenFileBrowser( FCKConfig.LinkBrowserURL, FCKConfig.LinkBrowserWindowWidth, FCKConfig.LinkBrowserWindowHeight ) ;
+}
+
+function SetUrl( url )
+{
+ document.getElementById('txtUrl').value = url ;
+ OnUrlChange() ;
+ window.parent.SetSelectedTab( 'Info' ) ;
+}
+
+function OnUploadCompleted( errorNumber, fileUrl, fileName, customMsg )
+{
+ switch ( errorNumber )
+ {
+ case 0 : // No errors
+ alert( 'Your file has been successfully uploaded' ) ;
+ break ;
+ case 1 : // Custom error
+ alert( customMsg ) ;
+ return ;
+ case 101 : // Custom warning
+ alert( customMsg ) ;
+ break ;
+ case 201 :
+ alert( 'A file with the same name is already available. The uploaded file has been renamed to "' + fileName + '"' ) ;
+ break ;
+ case 202 :
+ alert( 'Invalid file type' ) ;
+ return ;
+ case 203 :
+ alert( "Security error. You probably don't have enough permissions to upload. Please check your server." ) ;
+ return ;
+ default :
+ alert( 'Error on file upload. Error number: ' + errorNumber ) ;
+ return ;
+ }
+
+ SetUrl( fileUrl ) ;
+ GetE('frmUpload').reset() ;
+}
+
+var oUploadAllowedExtRegex = new RegExp( FCKConfig.LinkUploadAllowedExtensions, 'i' ) ;
+var oUploadDeniedExtRegex = new RegExp( FCKConfig.LinkUploadDeniedExtensions, 'i' ) ;
+
+function CheckUpload()
+{
+ var sFile = GetE('txtUploadFile').value ;
+
+ if ( sFile.length == 0 )
+ {
+ alert( 'Please select a file to upload' ) ;
+ return false ;
+ }
+
+ if ( ( FCKConfig.LinkUploadAllowedExtensions.length > 0 && !oUploadAllowedExtRegex.test( sFile ) ) ||
+ ( FCKConfig.LinkUploadDeniedExtensions.length > 0 && oUploadDeniedExtRegex.test( sFile ) ) )
+ {
+ OnUploadCompleted( 202 ) ;
+ return false ;
+ }
+
+ return true ;
+}
+
+function SetDefaultTarget()
+{
+ var target = FCKConfig.DefaultLinkTarget + '' ;
+
+ if ( oLink || target.length == 0 )
+ return ;
+
+ switch ( target )
+ {
+ case '_blank' :
+ case '_self' :
+ case '_parent' :
+ case '_top' :
+ GetE('cmbTarget').value = target ;
+ break ;
+ default :
+ GetE('cmbTarget').value = 'frame' ;
+ break ;
+ }
+
+ GetE('txtTargetFrame').value = target ;
+}
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_listprop.html b/httemplate/elements/fckeditor/editor/dialog/fck_listprop.html
new file mode 100644
index 0000000..a0a927e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_listprop.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Bulleted List dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+var sListType = ( location.search == '?OL' ? 'OL' : 'UL' ) ;
+
+var oActiveEl = oEditor.FCKSelection.MoveToAncestorNode( sListType ) ;
+var oActiveSel ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( sListType == 'UL' )
+ oActiveSel = GetE('selBulleted') ;
+ else
+ {
+ if ( oActiveEl )
+ {
+ oActiveSel = GetE('selNumbered') ;
+ GetE('eStart').style.display = '' ;
+ GetE('txtStartPosition').value = GetAttribute( oActiveEl, 'start' ) ;
+ }
+ }
+
+ oActiveSel.style.display = '' ;
+
+ if ( oActiveEl )
+ {
+ if ( oActiveEl.getAttribute('type') )
+ oActiveSel.value = oActiveEl.getAttribute('type') ;
+ }
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ if ( oActiveEl ){
+ SetAttribute( oActiveEl, 'type' , oActiveSel.value ) ;
+ if(oActiveEl.tagName == 'OL')
+ SetAttribute( oActiveEl, 'start', GetE('txtStartPosition').value ) ;
+ }
+
+ return true ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <table width="100%" style="height: 100%">
+ <tr>
+ <td style="text-align:center">
+ <table cellspacing="0" cellpadding="0" border="0" style="margin-left: auto; margin-right: auto;">
+ <tr>
+ <td id="eStart" style="display: none; padding-right: 5px; padding-left: 5px">
+ <span fcklang="DlgLstStart">Start</span><br />
+ <input type="text" id="txtStartPosition" size="5" />
+ </td>
+ <td style="padding-right: 5px; padding-left: 5px">
+ <span fcklang="DlgLstType">List Type</span><br />
+ <select id="selBulleted" style="display: none">
+ <option value="" selected="selected"></option>
+ <option value="circle" fcklang="DlgLstTypeCircle">Circle</option>
+ <option value="disc" fcklang="DlgLstTypeDisc">Disc</option>
+ <option value="square" fcklang="DlgLstTypeSquare">Square</option>
+ </select>
+ <select id="selNumbered" style="display: none">
+ <option value="" selected="selected"></option>
+ <option value="1" fcklang="DlgLstTypeNumbers">Numbers (1, 2, 3)</option>
+ <option value="a" fcklang="DlgLstTypeLCase">Lowercase Letters (a, b, c)</option>
+ <option value="A" fcklang="DlgLstTypeUCase">Uppercase Letters (A, B, C)</option>
+ <option value="i" fcklang="DlgLstTypeSRoman">Small Roman Numerals (i, ii, iii)</option>
+ <option value="I" fcklang="DlgLstTypeLRoman">Large Roman Numerals (I, II, III)</option>
+ </select>
+ &nbsp;
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_paste.html b/httemplate/elements/fckeditor/editor/dialog/fck_paste.html
new file mode 100644
index 0000000..fd16c31
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_paste.html
@@ -0,0 +1,285 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This dialog is shown when, for some reason (usually security settings),
+ * the user is not able to paste data from the clipboard to the editor using
+ * the toolbar buttons or the context menu.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+
+ <script type="text/javascript">
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK;
+var FCKTools = oEditor.FCKTools ;
+var FCKConfig = oEditor.FCKConfig ;
+
+window.onload = function ()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ var sPastingType = window.parent.dialogArguments.CustomValue ;
+
+ if ( sPastingType == 'Word' || sPastingType == 'Security' )
+ {
+ if ( sPastingType == 'Security' )
+ document.getElementById( 'xSecurityMsg' ).style.display = '' ;
+
+ var oFrame = document.getElementById('frmData') ;
+ oFrame.style.display = '' ;
+
+ if ( oFrame.contentDocument )
+ oFrame.contentDocument.designMode = 'on' ;
+ else
+ oFrame.contentWindow.document.body.contentEditable = true ;
+ }
+ else
+ {
+ document.getElementById('txtData').style.display = '' ;
+ }
+
+ if ( sPastingType != 'Word' )
+ document.getElementById('oWordCommands').style.display = 'none' ;
+
+ window.parent.SetOkButton( true ) ;
+ window.parent.SetAutoSize( true ) ;
+}
+
+function Ok()
+{
+ var sHtml ;
+
+ var sPastingType = window.parent.dialogArguments.CustomValue ;
+
+ if ( sPastingType == 'Word' || sPastingType == 'Security' )
+ {
+ var oFrame = document.getElementById('frmData') ;
+ var oBody ;
+
+ if ( oFrame.contentDocument )
+ oBody = oFrame.contentDocument.body ;
+ else
+ oBody = oFrame.contentWindow.document.body ;
+
+ if ( sPastingType == 'Word' )
+ {
+ // If a plugin creates a FCK.CustomCleanWord function it will be called instead of the default one
+ if ( typeof( FCK.CustomCleanWord ) == 'function' )
+ sHtml = FCK.CustomCleanWord( oBody, document.getElementById('chkRemoveFont').checked, document.getElementById('chkRemoveStyles').checked ) ;
+ else
+ sHtml = CleanWord( oBody, document.getElementById('chkRemoveFont').checked, document.getElementById('chkRemoveStyles').checked ) ;
+ }
+ else
+ sHtml = oBody.innerHTML ;
+
+ // Fix relative anchor URLs (IE automatically adds the current page URL).
+ var re = new RegExp( window.location + "#", "g" ) ;
+ sHtml = sHtml.replace( re, '#') ;
+ }
+ else
+ {
+ sHtml = oEditor.FCKTools.HTMLEncode( document.getElementById('txtData').value ) ;
+ sHtml = sHtml.replace( /\n/g, '<BR>' ) ;
+ }
+
+ oEditor.FCK.InsertHtml( sHtml ) ;
+
+ return true ;
+}
+
+function CleanUpBox()
+{
+ var oFrame = document.getElementById('frmData') ;
+
+ if ( oFrame.contentDocument )
+ oFrame.contentDocument.body.innerHTML = '' ;
+ else
+ oFrame.contentWindow.document.body.innerHTML = '' ;
+}
+
+
+// This function will be called from the PasteFromWord dialog (fck_paste.html)
+// Input: oNode a DOM node that contains the raw paste from the clipboard
+// bIgnoreFont, bRemoveStyles booleans according to the values set in the dialog
+// Output: the cleaned string
+function CleanWord( oNode, bIgnoreFont, bRemoveStyles )
+{
+ var html = oNode.innerHTML ;
+
+ html = html.replace(/<o:p>\s*<\/o:p>/g, '') ;
+ html = html.replace(/<o:p>.*?<\/o:p>/g, '&nbsp;') ;
+
+ // Remove mso-xxx styles.
+ html = html.replace( /\s*mso-[^:]+:[^;"]+;?/gi, '' ) ;
+
+ // Remove margin styles.
+ html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '' ) ;
+ html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"" ) ;
+
+ html = html.replace( /\s*TEXT-INDENT: 0cm\s*;/gi, '' ) ;
+ html = html.replace( /\s*TEXT-INDENT: 0cm\s*"/gi, "\"" ) ;
+
+ html = html.replace( /\s*TEXT-ALIGN: [^\s;]+;?"/gi, "\"" ) ;
+
+ html = html.replace( /\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"" ) ;
+
+ html = html.replace( /\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"" ) ;
+
+ html = html.replace( /\s*tab-stops:[^;"]*;?/gi, '' ) ;
+ html = html.replace( /\s*tab-stops:[^"]*/gi, '' ) ;
+
+ // Remove FONT face attributes.
+ if ( bIgnoreFont )
+ {
+ html = html.replace( /\s*face="[^"]*"/gi, '' ) ;
+ html = html.replace( /\s*face=[^ >]*/gi, '' ) ;
+
+ html = html.replace( /\s*FONT-FAMILY:[^;"]*;?/gi, '' ) ;
+ }
+
+ // Remove Class attributes
+ html = html.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3") ;
+
+ // Remove styles.
+ if ( bRemoveStyles )
+ html = html.replace( /<(\w[^>]*) style="([^\"]*)"([^>]*)/gi, "<$1$3" ) ;
+
+ // Remove empty styles.
+ html = html.replace( /\s*style="\s*"/gi, '' ) ;
+
+ html = html.replace( /<SPAN\s*[^>]*>\s*&nbsp;\s*<\/SPAN>/gi, '&nbsp;' ) ;
+
+ html = html.replace( /<SPAN\s*[^>]*><\/SPAN>/gi, '' ) ;
+
+ // Remove Lang attributes
+ html = html.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3") ;
+
+ html = html.replace( /<SPAN\s*>(.*?)<\/SPAN>/gi, '$1' ) ;
+
+ html = html.replace( /<FONT\s*>(.*?)<\/FONT>/gi, '$1' ) ;
+
+ // Remove XML elements and declarations
+ html = html.replace(/<\\?\?xml[^>]*>/gi, '' ) ;
+
+ // Remove Tags with XML namespace declarations: <o:p><\/o:p>
+ html = html.replace(/<\/?\w+:[^>]*>/gi, '' ) ;
+
+ // Remove comments [SF BUG-1481861].
+ html = html.replace(/<\!--.*-->/g, '' ) ;
+
+ html = html.replace( /<(U|I|STRIKE)>&nbsp;<\/\1>/g, '&nbsp;' ) ;
+
+ html = html.replace( /<H\d>\s*<\/H\d>/gi, '' ) ;
+
+ // Remove "display:none" tags.
+ html = html.replace( /<(\w+)[^>]*\sstyle="[^"]*DISPLAY\s?:\s?none(.*?)<\/\1>/ig, '' ) ;
+
+ if ( FCKConfig.CleanWordKeepsStructure )
+ {
+ // The original <Hn> tag send from Word is something like this: <Hn style="margin-top:0px;margin-bottom:0px">
+ html = html.replace( /<H(\d)([^>]*)>/gi, '<h$1>' ) ;
+
+ // Word likes to insert extra <font> tags, when using MSIE. (Wierd).
+ html = html.replace( /<(H\d)><FONT[^>]*>(.*?)<\/FONT><\/\1>/gi, '<$1>$2<\/$1>' );
+ html = html.replace( /<(H\d)><EM>(.*?)<\/EM><\/\1>/gi, '<$1>$2<\/$1>' );
+ }
+ else
+ {
+ html = html.replace( /<H1([^>]*)>/gi, '<div$1><b><font size="6">' ) ;
+ html = html.replace( /<H2([^>]*)>/gi, '<div$1><b><font size="5">' ) ;
+ html = html.replace( /<H3([^>]*)>/gi, '<div$1><b><font size="4">' ) ;
+ html = html.replace( /<H4([^>]*)>/gi, '<div$1><b><font size="3">' ) ;
+ html = html.replace( /<H5([^>]*)>/gi, '<div$1><b><font size="2">' ) ;
+ html = html.replace( /<H6([^>]*)>/gi, '<div$1><b><font size="1">' ) ;
+
+ html = html.replace( /<\/H\d>/gi, '<\/font><\/b><\/div>' ) ;
+
+ // Transform <P> to <DIV>
+ var re = new RegExp( '(<P)([^>]*>.*?)(<\/P>)', 'gi' ) ; // Different because of a IE 5.0 error
+ html = html.replace( re, '<div$2<\/div>' ) ;
+
+ // Remove empty tags (three times, just to be sure).
+ // This also removes any empty anchor
+ html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
+ html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
+ html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
+ }
+
+ return html ;
+}
+
+ </script>
+
+</head>
+<body style="overflow: hidden">
+ <table cellspacing="0" cellpadding="0" width="100%" border="0" style="height: 98%">
+ <tr>
+ <td>
+ <div id="xSecurityMsg" style="display: none">
+ <span fcklang="DlgPasteSec">Because of your browser security settings,
+ the editor is not able to access your clipboard data directly. You are required
+ to paste it again in this window.</span><br />
+ &nbsp;
+ </div>
+ <div>
+ <span fcklang="DlgPasteMsg2">Please paste inside the following box using the keyboard
+ (<strong>Ctrl+V</strong>) and hit <strong>OK</strong>.</span><br />
+ &nbsp;
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" height="100%" style="border-right: #000000 1px solid; border-top: #000000 1px solid;
+ border-left: #000000 1px solid; border-bottom: #000000 1px solid">
+ <textarea id="txtData" cols="80" rows="5" style="border: #000000 1px; display: none;
+ width: 99%; height: 98%"></textarea>
+ <iframe id="frmData" src="javascript:void(0)" height="98%" width="99%" frameborder="0"
+ style="border-right: #000000 1px; border-top: #000000 1px; display: none; border-left: #000000 1px;
+ border-bottom: #000000 1px; background-color: #ffffff"></iframe>
+ </td>
+ </tr>
+ <tr id="oWordCommands">
+ <td>
+ <table border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr>
+ <td nowrap="nowrap">
+ <input id="chkRemoveFont" type="checkbox" checked="checked" />
+ <label for="chkRemoveFont" fcklang="DlgPasteIgnoreFont">
+ Ignore Font Face definitions</label>
+ <br />
+ <input id="chkRemoveStyles" type="checkbox" />
+ <label for="chkRemoveStyles" fcklang="DlgPasteRemoveStyles">
+ Remove Styles definitions</label>
+ </td>
+ <td align="right" valign="top">
+ <input type="button" fcklang="DlgPasteCleanBox" value="Clean Up Box" onclick="CleanUpBox()" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_radiobutton.html b/httemplate/elements/fckeditor/editor/dialog/fck_radiobutton.html
new file mode 100644
index 0000000..f239ad3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_radiobutton.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Radio Button dialog window.
+-->
+<html>
+ <head>
+ <title>Radio Button Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oActiveEl = oEditor.FCKSelection.GetSelectedElement() ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oActiveEl && oActiveEl.tagName.toUpperCase() == 'INPUT' && oActiveEl.type == 'radio' )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtValue').value = oEditor.FCKBrowserInfo.IsIE ? oActiveEl.value : GetAttribute( oActiveEl, 'value' ) ;
+ GetE('txtSelected').checked = oActiveEl.checked ;
+ }
+ else
+ oActiveEl = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ if ( !oActiveEl )
+ {
+ oActiveEl = oEditor.FCK.EditorDocument.createElement( 'INPUT' ) ;
+ oActiveEl.type = 'radio' ;
+ oActiveEl = oEditor.FCK.InsertElementAndGetIt( oActiveEl ) ;
+ }
+
+ if ( GetE('txtName').value.length > 0 )
+ oActiveEl.name = GetE('txtName').value ;
+
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ oActiveEl.value = GetE('txtValue').value ;
+ else
+ SetAttribute( oActiveEl, 'value', GetE('txtValue').value ) ;
+
+ var bIsChecked = GetE('txtSelected').checked ;
+ SetAttribute( oActiveEl, 'checked', bIsChecked ? 'checked' : null ) ; // For Firefox
+ oActiveEl.checked = bIsChecked ;
+
+ return true ;
+}
+
+ </script>
+ </head>
+ <body style="OVERFLOW: hidden" scroll="no">
+ <table height="100%" width="100%">
+ <tr>
+ <td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="80%">
+ <tr>
+ <td>
+ <span fckLang="DlgCheckboxName">Name</span><br>
+ <input type="text" size="20" id="txtName" style="WIDTH: 100%">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fckLang="DlgCheckboxValue">Value</span><br>
+ <input type="text" size="20" id="txtValue" style="WIDTH: 100%">
+ </td>
+ </tr>
+ <tr>
+ <td><input type="checkbox" id="txtSelected"><label for="txtSelected" fckLang="DlgCheckboxSelected">Checked</label></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_replace.html b/httemplate/elements/fckeditor/editor/dialog/fck_replace.html
new file mode 100644
index 0000000..fe5a788
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_replace.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * "Replace" dialog box window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+function OnLoad()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage( document ) ;
+
+ window.parent.SetAutoSize( true ) ;
+
+ oEditor.FCKUndo.SaveUndoStep() ;
+}
+
+function btnStat(frm)
+{
+ document.getElementById('btnReplace').disabled =
+ document.getElementById('btnReplaceAll').disabled =
+ ( document.getElementById('txtFind').value.length == 0 ) ;
+}
+
+function ReplaceTextNodes( parentNode, regex, replaceValue, replaceAll, hasFound )
+{
+ for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
+ {
+ var oNode = parentNode.childNodes[i] ;
+ if ( oNode.nodeType == 3 )
+ {
+ var sReplaced = oNode.nodeValue.replace( regex, replaceValue ) ;
+ if ( oNode.nodeValue != sReplaced )
+ {
+ oNode.nodeValue = sReplaced ;
+ if ( ! replaceAll )
+ return true ;
+ hasFound = true ;
+ }
+ }
+
+ hasFound = ReplaceTextNodes( oNode, regex, replaceValue, replaceAll, hasFound ) ;
+ if ( ! replaceAll && hasFound )
+ return true ;
+ }
+
+ return hasFound ;
+}
+
+function GetRegexExpr()
+{
+ var sExpr = EscapeRegexString( document.getElementById('txtFind').value ) ;
+
+ if ( document.getElementById('chkWord').checked )
+ sExpr = '\\b' + sExpr + '\\b' ;
+
+ return sExpr ;
+}
+
+function GetCase()
+{
+ return ( document.getElementById('chkCase').checked ? '' : 'i' ) ;
+}
+
+function GetReplacement()
+{
+ return document.getElementById('txtReplace').value.replace( /\$/g, '$$$$' ) ;
+}
+
+function EscapeRegexString( str )
+{
+ return str.replace( /[\\\^\$\*\+\?\{\}\.\(\)\!\|\[\]\-]/g, '\\$&' ) ;
+}
+
+function Replace()
+{
+ var oRegex = new RegExp( GetRegexExpr(), GetCase() ) ;
+ if ( !ReplaceTextNodes( oEditor.FCK.EditorDocument.body, oRegex, GetReplacement(), false, false ) )
+ alert( oEditor.FCKLang.DlgFindNotFoundMsg ) ;
+}
+
+function ReplaceAll()
+{
+ var oRegex = new RegExp( GetRegexExpr(), GetCase() + 'g' ) ;
+ if ( !ReplaceTextNodes( oEditor.FCK.EditorDocument.body, oRegex, GetReplacement(), true, false ) )
+ alert( oEditor.FCKLang.DlgFindNotFoundMsg ) ;
+ window.parent.Cancel() ;
+}
+ </script>
+</head>
+<body onload="OnLoad()" style="overflow: hidden">
+ <table cellspacing="3" cellpadding="2" width="100%" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <label for="txtFind" fcklang="DlgReplaceFindLbl">
+ Find what:</label>
+ </td>
+ <td width="100%">
+ <input id="txtFind" onkeyup="btnStat(this.form)" style="width: 100%" tabindex="1"
+ type="text" />
+ </td>
+ <td>
+ <input id="btnReplace" style="width: 100%" disabled="disabled" onclick="Replace();"
+ type="button" value="Replace" fcklang="DlgReplaceReplaceBtn" />
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap="nowrap">
+ <label for="txtReplace" fcklang="DlgReplaceReplaceLbl">
+ Replace with:</label>
+ </td>
+ <td valign="top">
+ <input id="txtReplace" style="width: 100%" tabindex="2" type="text" />
+ </td>
+ <td>
+ <input id="btnReplaceAll" disabled="disabled" onclick="ReplaceAll()" type="button"
+ value="Replace All" fcklang="DlgReplaceReplAllBtn" />
+ </td>
+ </tr>
+ <tr>
+ <td valign="bottom" colspan="3">
+ &nbsp;<input id="chkCase" tabindex="3" type="checkbox" /><label for="chkCase" fcklang="DlgReplaceCaseChk">Match
+ case</label>
+ <br />
+ &nbsp;<input id="chkWord" tabindex="4" type="checkbox" /><label for="chkWord" fcklang="DlgReplaceWordChk">Match
+ whole word</label>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_select.html b/httemplate/elements/fckeditor/editor/dialog/fck_select.html
new file mode 100644
index 0000000..cb48b50
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_select.html
@@ -0,0 +1,176 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Select dialog window.
+-->
+<html>
+ <head>
+ <title>Select Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript" src="fck_select/fck_select.js"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oActiveEl = oEditor.FCKSelection.GetSelectedElement() ;
+
+var oListText ;
+var oListValue ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ oListText = document.getElementById( 'cmbText' ) ;
+ oListValue = document.getElementById( 'cmbValue' ) ;
+
+ if ( oActiveEl && oActiveEl.tagName == 'SELECT' )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtSelValue').value = oActiveEl.value ;
+ GetE('txtLines').value = GetAttribute( oActiveEl, 'size' ) ;
+ GetE('chkMultiple').checked = oActiveEl.multiple ;
+
+ // Load the actual options
+ for ( var i = 0 ; i < oActiveEl.options.length ; i++ )
+ {
+ var sText = HTMLDecode( oActiveEl.options[i].innerHTML ) ;
+ var sValue = oActiveEl.options[i].value ;
+
+ AddComboOption( oListText, sText, sText ) ;
+ AddComboOption( oListValue, sValue, sValue ) ;
+ }
+ }
+ else
+ oActiveEl = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ var sSize = GetE('txtLines').value ;
+ if ( sSize == null || isNaN( sSize ) || sSize <= 1 )
+ sSize = '' ;
+
+ if ( !oActiveEl )
+ {
+ oActiveEl = oEditor.FCK.EditorDocument.createElement( 'SELECT' ) ;
+ oActiveEl = oEditor.FCK.InsertElementAndGetIt( oActiveEl ) ;
+ }
+
+ SetAttribute( oActiveEl, 'name' , GetE('txtName').value ) ;
+ SetAttribute( oActiveEl, 'size' , sSize ) ;
+ oActiveEl.multiple = ( sSize.length > 0 && GetE('chkMultiple').checked ) ;
+
+ // Remove all options.
+ while ( oActiveEl.options.length > 0 )
+ oActiveEl.remove(0) ;
+
+ // Add all available options.
+ for ( var i = 0 ; i < oListText.options.length ; i++ )
+ {
+ var sText = oListText.options[i].value ;
+ var sValue = oListValue.options[i].value ;
+ if ( sValue.length == 0 ) sValue = sText ;
+
+ var oOption = AddComboOption( oActiveEl, sText, sValue, oDOM ) ;
+
+ if ( sValue == GetE('txtSelValue').value )
+ {
+ SetAttribute( oOption, 'selected', 'selected' ) ;
+ oOption.selected = true ;
+ }
+ }
+
+ return true ;
+}
+
+ </script>
+ </head>
+ <body style='OVERFLOW: hidden' scroll='no'>
+ <table width="100%" height="100%">
+ <tr>
+ <td>
+ <table width="100%">
+ <tr>
+ <td nowrap><span fckLang="DlgSelectName">Name</span>&nbsp;</td>
+ <td width="100%" colSpan="2"><input id="txtName" style="WIDTH: 100%" type="text"></td>
+ </tr>
+ <tr>
+ <td nowrap><span fckLang="DlgSelectValue">Value</span>&nbsp;</td>
+ <td width="100%" colSpan="2"><input id="txtSelValue" style="WIDTH: 100%; BACKGROUND-COLOR: buttonface" type="text" readonly></td>
+ </tr>
+ <tr>
+ <td nowrap><span fckLang="DlgSelectSize">Size</span>&nbsp;</td>
+ <td nowrap><input id="txtLines" type="text" size="2" value="">&nbsp;<span fckLang="DlgSelectLines">lines</span></td>
+ <td nowrap align="right"><input id="chkMultiple" name="chkMultiple" type="checkbox"><label for="chkMultiple" fckLang="DlgSelectChkMulti">Allow
+ multiple selections</label></td>
+ </tr>
+ </table>
+ <br>
+ <hr style="POSITION: absolute">
+ <span style="LEFT: 10px; POSITION: relative; TOP: -7px" class="BackColor">&nbsp;<span fckLang="DlgSelectOpAvail">Available
+ Options</span>&nbsp;</span>
+ <table width="100%">
+ <tr>
+ <td width="50%"><span fckLang="DlgSelectOpText">Text</span><br>
+ <input id="txtText" style="WIDTH: 100%" type="text" name="txtText">
+ </td>
+ <td width="50%"><span fckLang="DlgSelectOpValue">Value</span><br>
+ <input id="txtValue" style="WIDTH: 100%" type="text" name="txtValue">
+ </td>
+ <td vAlign="bottom"><input onclick="Add();" type="button" fckLang="DlgSelectBtnAdd" value="Add"></td>
+ <td vAlign="bottom"><input onclick="Modify();" type="button" fckLang="DlgSelectBtnModify" value="Modify"></td>
+ </tr>
+ <tr>
+ <td rowSpan="2"><select id="cmbText" style="WIDTH: 100%" onchange="GetE('cmbValue').selectedIndex = this.selectedIndex;Select(this);"
+ size="5" name="cmbText"></select>
+ </td>
+ <td rowSpan="2"><select id="cmbValue" style="WIDTH: 100%" onchange="GetE('cmbText').selectedIndex = this.selectedIndex;Select(this);"
+ size="5" name="cmbValue"></select>
+ </td>
+ <td vAlign="top" colSpan="2">
+ </td>
+ </tr>
+ <tr>
+ <td vAlign="bottom" colSpan="2"><input style="WIDTH: 100%" onclick="Move(-1);" type="button" fckLang="DlgSelectBtnUp" value="Up">
+ <br>
+ <input style="WIDTH: 100%" onclick="Move(1);" type="button" fckLang="DlgSelectBtnDown"
+ value="Down">
+ </td>
+ </tr>
+ <TR>
+ <TD vAlign="bottom" colSpan="4"><INPUT onclick="SetSelectedValue();" type="button" fckLang="DlgSelectBtnSetValue" value="Set as selected value">&nbsp;&nbsp;
+ <input onclick="Delete();" type="button" fckLang="DlgSelectBtnDelete" value="Delete"></TD>
+ </TR>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_select/fck_select.js b/httemplate/elements/fckeditor/editor/dialog/fck_select/fck_select.js
new file mode 100644
index 0000000..181b666
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_select/fck_select.js
@@ -0,0 +1,194 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Scripts for the fck_select.html page.
+ */
+
+function Select( combo )
+{
+ var iIndex = combo.selectedIndex ;
+
+ oListText.selectedIndex = iIndex ;
+ oListValue.selectedIndex = iIndex ;
+
+ var oTxtText = document.getElementById( "txtText" ) ;
+ var oTxtValue = document.getElementById( "txtValue" ) ;
+
+ oTxtText.value = oListText.value ;
+ oTxtValue.value = oListValue.value ;
+}
+
+function Add()
+{
+ var oTxtText = document.getElementById( "txtText" ) ;
+ var oTxtValue = document.getElementById( "txtValue" ) ;
+
+ AddComboOption( oListText, oTxtText.value, oTxtText.value ) ;
+ AddComboOption( oListValue, oTxtValue.value, oTxtValue.value ) ;
+
+ oListText.selectedIndex = oListText.options.length - 1 ;
+ oListValue.selectedIndex = oListValue.options.length - 1 ;
+
+ oTxtText.value = '' ;
+ oTxtValue.value = '' ;
+
+ oTxtText.focus() ;
+}
+
+function Modify()
+{
+ var iIndex = oListText.selectedIndex ;
+
+ if ( iIndex < 0 ) return ;
+
+ var oTxtText = document.getElementById( "txtText" ) ;
+ var oTxtValue = document.getElementById( "txtValue" ) ;
+
+ oListText.options[ iIndex ].innerHTML = HTMLEncode( oTxtText.value ) ;
+ oListText.options[ iIndex ].value = oTxtText.value ;
+
+ oListValue.options[ iIndex ].innerHTML = HTMLEncode( oTxtValue.value ) ;
+ oListValue.options[ iIndex ].value = oTxtValue.value ;
+
+ oTxtText.value = '' ;
+ oTxtValue.value = '' ;
+
+ oTxtText.focus() ;
+}
+
+function Move( steps )
+{
+ ChangeOptionPosition( oListText, steps ) ;
+ ChangeOptionPosition( oListValue, steps ) ;
+}
+
+function Delete()
+{
+ RemoveSelectedOptions( oListText ) ;
+ RemoveSelectedOptions( oListValue ) ;
+}
+
+function SetSelectedValue()
+{
+ var iIndex = oListValue.selectedIndex ;
+ if ( iIndex < 0 ) return ;
+
+ var oTxtValue = document.getElementById( "txtSelValue" ) ;
+
+ oTxtValue.value = oListValue.options[ iIndex ].value ;
+}
+
+// Moves the selected option by a number of steps (also negative)
+function ChangeOptionPosition( combo, steps )
+{
+ var iActualIndex = combo.selectedIndex ;
+
+ if ( iActualIndex < 0 )
+ return ;
+
+ var iFinalIndex = iActualIndex + steps ;
+
+ if ( iFinalIndex < 0 )
+ iFinalIndex = 0 ;
+
+ if ( iFinalIndex > ( combo.options.length - 1 ) )
+ iFinalIndex = combo.options.length - 1 ;
+
+ if ( iActualIndex == iFinalIndex )
+ return ;
+
+ var oOption = combo.options[ iActualIndex ] ;
+ var sText = HTMLDecode( oOption.innerHTML ) ;
+ var sValue = oOption.value ;
+
+ combo.remove( iActualIndex ) ;
+
+ oOption = AddComboOption( combo, sText, sValue, null, iFinalIndex ) ;
+
+ oOption.selected = true ;
+}
+
+// Remove all selected options from a SELECT object
+function RemoveSelectedOptions(combo)
+{
+ // Save the selected index
+ var iSelectedIndex = combo.selectedIndex ;
+
+ var oOptions = combo.options ;
+
+ // Remove all selected options
+ for ( var i = oOptions.length - 1 ; i >= 0 ; i-- )
+ {
+ if (oOptions[i].selected) combo.remove(i) ;
+ }
+
+ // Reset the selection based on the original selected index
+ if ( combo.options.length > 0 )
+ {
+ if ( iSelectedIndex >= combo.options.length ) iSelectedIndex = combo.options.length - 1 ;
+ combo.selectedIndex = iSelectedIndex ;
+ }
+}
+
+// Add a new option to a SELECT object (combo or list)
+function AddComboOption( combo, optionText, optionValue, documentObject, index )
+{
+ var oOption ;
+
+ if ( documentObject )
+ oOption = documentObject.createElement("OPTION") ;
+ else
+ oOption = document.createElement("OPTION") ;
+
+ if ( index != null )
+ combo.options.add( oOption, index ) ;
+ else
+ combo.options.add( oOption ) ;
+
+ oOption.innerHTML = optionText.length > 0 ? HTMLEncode( optionText ) : '&nbsp;' ;
+ oOption.value = optionValue ;
+
+ return oOption ;
+}
+
+function HTMLEncode( text )
+{
+ if ( !text )
+ return '' ;
+
+ text = text.replace( /&/g, '&amp;' ) ;
+ text = text.replace( /</g, '&lt;' ) ;
+ text = text.replace( />/g, '&gt;' ) ;
+
+ return text ;
+}
+
+
+function HTMLDecode( text )
+{
+ if ( !text )
+ return '' ;
+
+ text = text.replace( /&gt;/g, '>' ) ;
+ text = text.replace( /&lt;/g, '<' ) ;
+ text = text.replace( /&amp;/g, '&' ) ;
+
+ return text ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_smiley.html b/httemplate/elements/fckeditor/editor/dialog/fck_smiley.html
new file mode 100644
index 0000000..c8efd0c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_smiley.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Smileys (emoticons) dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <style type="text/css">
+ .Hand
+ {
+ cursor: pointer;
+ cursor: hand;
+ }
+ </style>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+window.onload = function ()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+}
+
+function InsertSmiley( url )
+{
+ var oImg = oEditor.FCK.CreateElement( 'IMG' ) ;
+ oImg.src = url ;
+ oImg.setAttribute( '_fcksavedurl', url ) ;
+
+ // For long smileys list, it seams that IE continues loading the images in
+ // the background when you quickly select one image. so, let's clear
+ // everything before closing.
+ document.body.innerHTML = '' ;
+
+ window.parent.Cancel() ;
+}
+
+function over(td)
+{
+ td.className = 'LightBackground Hand' ;
+}
+
+function out(td)
+{
+ td.className = 'DarkBackground Hand' ;
+}
+ </script>
+</head>
+<body scroll="no">
+ <table cellpadding="2" cellspacing="2" align="center" border="0" width="100%" height="100%">
+ <script type="text/javascript">
+
+var FCKConfig = oEditor.FCKConfig ;
+
+var sBasePath = FCKConfig.SmileyPath ;
+var aImages = FCKConfig.SmileyImages ;
+var iCols = FCKConfig.SmileyColumns ;
+var iColWidth = parseInt( 100 / iCols, 10 ) ;
+
+var i = 0 ;
+while (i < aImages.length)
+{
+ document.write( '<tr>' ) ;
+ for(var j = 0 ; j < iCols ; j++)
+ {
+ if (aImages[i])
+ {
+ var sUrl = sBasePath + aImages[i] ;
+ document.write( '<td width="' + iColWidth + '%" align="center" class="DarkBackground Hand" onclick="InsertSmiley(\'' + sUrl.replace(/'/g, "\\'" ) + '\')" onmouseover="over(this)" onmouseout="out(this)">' ) ;
+ document.write( '<img src="' + sUrl + '" border="0" />' ) ;
+ }
+ else
+ document.write( '<td width="' + iColWidth + '%" class="DarkBackground">&nbsp;' ) ;
+ document.write( '<\/td>' ) ;
+ i++ ;
+ }
+ document.write('<\/tr>') ;
+}
+
+ </script>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_source.html b/httemplate/elements/fckeditor/editor/dialog/fck_source.html
new file mode 100644
index 0000000..aba9b39
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_source.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Source editor dialog window.
+-->
+<html>
+ <head>
+ <title>Source</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="robots" content="noindex, nofollow">
+ <link href="common/fck_dialog_common.css" rel="stylesheet" type="text/css" />
+ <script language="javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+var FCKConfig = oEditor.FCKConfig ;
+
+window.onload = function()
+{
+ // EnableXHTML and EnableSourceXHTML has been deprecated
+// document.getElementById('txtSource').value = ( FCKConfig.EnableXHTML && FCKConfig.EnableSourceXHTML ? FCK.GetXHTML( FCKConfig.FormatSource ) : FCK.GetHTML( FCKConfig.FormatSource ) ) ;
+ document.getElementById('txtSource').value = FCK.GetXHTML( FCKConfig.FormatSource ) ;
+
+ // Activate the "OK" button.
+ window.parent.SetOkButton( true ) ;
+}
+
+//#### The OK button was hit.
+function Ok()
+{
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ FCK.SetHTML( document.getElementById('txtSource').value, false ) ;
+
+ return true ;
+}
+ </script>
+ </head>
+ <body scroll="no" style="OVERFLOW: hidden">
+ <table width="100%" height="100%">
+ <tr>
+ <td height="100%"><textarea id="txtSource" dir="ltr" style="PADDING-RIGHT: 5px; PADDING-LEFT: 5px; FONT-SIZE: 14px; PADDING-BOTTOM: 5px; WIDTH: 100%; PADDING-TOP: 5px; FONT-FAMILY: Monospace; HEIGHT: 100%">Loading. Please wait...</textarea></td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_specialchar.html b/httemplate/elements/fckeditor/editor/dialog/fck_specialchar.html
new file mode 100644
index 0000000..e6d0a5a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_specialchar.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Special Chars Selector dialog window.
+-->
+<html>
+ <head>
+ <meta name="robots" content="noindex, nofollow">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <style type="text/css">
+ .Hand
+ {
+ cursor: pointer ;
+ cursor: hand ;
+ }
+ .Sample { font-size: 24px; }
+ </style>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+var oSample ;
+
+function insertChar(charValue)
+{
+ oEditor.FCK.InsertHtml( charValue || "" ) ;
+ window.parent.Cancel() ;
+}
+
+function over(td)
+{
+ oSample.innerHTML = td.innerHTML ;
+ td.className = 'LightBackground SpecialCharsOver Hand' ;
+}
+
+function out(td)
+{
+ oSample.innerHTML = "&nbsp;" ;
+ td.className = 'DarkBackground SpecialCharsOut Hand' ;
+}
+
+function setDefaults()
+{
+ // Gets the sample placeholder.
+ oSample = document.getElementById("SampleTD") ;
+
+ // First of all, translates the dialog box texts.
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+}
+
+ </script>
+ </HEAD>
+ <BODY onload="setDefaults()" scroll="no">
+ <table cellpadding="0" cellspacing="0" width="100%" height="100%">
+ <tr>
+ <td width="100%">
+ <table cellpadding="1" cellspacing="1" align="center" border="0" width="100%" height="100%">
+ <script type="text/javascript">
+var aChars = ["!","&quot;","#","$","%","&amp;","\\'","(",")","*","+","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","&lt;","=","&gt;","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","]","^","_","`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~","&euro;","&lsquo;","&rsquo;","&rsquo;","&ldquo;","&rdquo;","&ndash;","&mdash;","&iexcl;","&cent;","&pound;","&curren;","&yen;","&brvbar;","&sect;","&uml;","&copy;","&ordf;","&laquo;","&not;","&reg;","&macr;","&deg;","&plusmn;","&sup2;","&sup3;","&acute;","&micro;","&para;","&middot;","&cedil;","&sup1;","&ordm;","&raquo;","&frac14;","&frac12;","&frac34;","&iquest;","&Agrave;","&Aacute;","&Acirc;","&Atilde;","&Auml;","&Aring;","&AElig;","&Ccedil;","&Egrave;","&Eacute;","&Ecirc;","&Euml;","&Igrave;","&Iacute;","&Icirc;","&Iuml;","&ETH;","&Ntilde;","&Ograve;","&Oacute;","&Ocirc;","&Otilde;","&Ouml;","&times;","&Oslash;","&Ugrave;","&Uacute;","&Ucirc;","&Uuml;","&Yacute;","&THORN;","&szlig;","&agrave;","&aacute;","&acirc;","&atilde;","&auml;","&aring;","&aelig;","&ccedil;","&egrave;","&eacute;","&ecirc;","&euml;","&igrave;","&iacute;","&icirc;","&iuml;","&eth;","&ntilde;","&ograve;","&oacute;","&ocirc;","&otilde;","&ouml;","&divide;","&oslash;","&ugrave;","&uacute;","&ucirc;","&uuml;","&uuml;","&yacute;","&thorn;","&yuml;","&OElig;","&oelig;","&sbquo;","&#8219;","&bdquo;","&hellip;","&trade;","&#9658;","&bull;","&rarr;","&rArr;","&hArr;","&diams;","&asymp;"] ;
+
+var cols = 20 ;
+
+var i = 0 ;
+while (i < aChars.length)
+{
+ document.write("<TR>") ;
+ for(var j = 0 ; j < cols ; j++)
+ {
+ if (aChars[i])
+ {
+ document.write('<TD width="1%" class="DarkBackground SpecialCharsOut Hand" align="center" onclick="insertChar(\'' + aChars[i].replace(/&/g, "&amp;") + '\')" onmouseover="over(this)" onmouseout="out(this)">') ;
+ document.write(aChars[i]) ;
+ }
+ else
+ document.write("<TD class='DarkBackground SpecialCharsOut'>&nbsp;") ;
+ document.write("<\/TD>") ;
+ i++ ;
+ }
+ document.write("<\/TR>") ;
+}
+ </script>
+ </table>
+ </td>
+ <td nowrap>&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ <td valign="top">
+ <table width="40" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td id="SampleTD" width="40" height="40" align="center" class="DarkBackground SpecialCharsOut Sample">&nbsp;</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </BODY>
+</HTML> \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages.html b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages.html
new file mode 100644
index 0000000..66596e1
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Spell Check dialog window.
+-->
+<html>
+ <head>
+ <title>Spell Check</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script src="fck_spellerpages/spellerpages/spellChecker.js"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCKLang = oEditor.FCKLang ;
+
+window.onload = function()
+{
+ document.getElementById('txtHtml').value = oEditor.FCK.EditorDocument.body.innerHTML ;
+
+ var oSpeller = new spellChecker( document.getElementById('txtHtml') ) ;
+ oSpeller.spellCheckScript = oEditor.FCKConfig.SpellerPagesServerScript || 'server-scripts/spellchecker.php' ;
+ oSpeller.OnFinished = oSpeller_OnFinished ;
+ oSpeller.openChecker() ;
+}
+
+function OnSpellerControlsLoad( controlsWindow )
+{
+ // Translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage( controlsWindow.document ) ;
+}
+
+function oSpeller_OnFinished( numberOCorrections )
+{
+ if ( numberOCorrections > 0 )
+ oEditor.FCK.SetHTML( document.getElementById('txtHtml').value ) ;
+ window.parent.Cancel() ;
+}
+
+ </script>
+ </head>
+ <body style="OVERFLOW: hidden" scroll="no" style="padding:0px;">
+ <input type="hidden" id="txtHtml" value="">
+ <iframe id="frmSpell" src="javascript:void(0)" name="spellchecker" width="100%" height="100%" frameborder="0"></iframe>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/blank.html b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/blank.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/blank.html
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controlWindow.js b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controlWindow.js
new file mode 100644
index 0000000..80af849
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controlWindow.js
@@ -0,0 +1,87 @@
+////////////////////////////////////////////////////
+// controlWindow object
+////////////////////////////////////////////////////
+function controlWindow( controlForm ) {
+ // private properties
+ this._form = controlForm;
+
+ // public properties
+ this.windowType = "controlWindow";
+// this.noSuggestionSelection = "- No suggestions -"; // by FredCK
+ this.noSuggestionSelection = FCKLang.DlgSpellNoSuggestions ;
+ // set up the properties for elements of the given control form
+ this.suggestionList = this._form.sugg;
+ this.evaluatedText = this._form.misword;
+ this.replacementText = this._form.txtsugg;
+ this.undoButton = this._form.btnUndo;
+
+ // public methods
+ this.addSuggestion = addSuggestion;
+ this.clearSuggestions = clearSuggestions;
+ this.selectDefaultSuggestion = selectDefaultSuggestion;
+ this.resetForm = resetForm;
+ this.setSuggestedText = setSuggestedText;
+ this.enableUndo = enableUndo;
+ this.disableUndo = disableUndo;
+}
+
+function resetForm() {
+ if( this._form ) {
+ this._form.reset();
+ }
+}
+
+function setSuggestedText() {
+ var slct = this.suggestionList;
+ var txt = this.replacementText;
+ var str = "";
+ if( (slct.options[0].text) && slct.options[0].text != this.noSuggestionSelection ) {
+ str = slct.options[slct.selectedIndex].text;
+ }
+ txt.value = str;
+}
+
+function selectDefaultSuggestion() {
+ var slct = this.suggestionList;
+ var txt = this.replacementText;
+ if( slct.options.length == 0 ) {
+ this.addSuggestion( this.noSuggestionSelection );
+ } else {
+ slct.options[0].selected = true;
+ }
+ this.setSuggestedText();
+}
+
+function addSuggestion( sugg_text ) {
+ var slct = this.suggestionList;
+ if( sugg_text ) {
+ var i = slct.options.length;
+ var newOption = new Option( sugg_text, 'sugg_text'+i );
+ slct.options[i] = newOption;
+ }
+}
+
+function clearSuggestions() {
+ var slct = this.suggestionList;
+ for( var j = slct.length - 1; j > -1; j-- ) {
+ if( slct.options[j] ) {
+ slct.options[j] = null;
+ }
+ }
+}
+
+function enableUndo() {
+ if( this.undoButton ) {
+ if( this.undoButton.disabled == true ) {
+ this.undoButton.disabled = false;
+ }
+ }
+}
+
+function disableUndo() {
+ if( this.undoButton ) {
+ if( this.undoButton.disabled == false ) {
+ this.undoButton.disabled = true;
+ }
+ }
+}
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controls.html b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controls.html
new file mode 100644
index 0000000..d91bcce
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/controls.html
@@ -0,0 +1,153 @@
+<html>
+ <head>
+ <link rel="stylesheet" type="text/css" href="spellerStyle.css" />
+ <script type="text/javascript" src="controlWindow.js"></script>
+ <script type="text/javascript">
+var spellerObject;
+var controlWindowObj;
+
+if( parent.opener ) {
+ spellerObject = parent.opener.speller;
+}
+
+function ignore_word() {
+ if( spellerObject ) {
+ spellerObject.ignoreWord();
+ }
+}
+
+function ignore_all() {
+ if( spellerObject ) {
+ spellerObject.ignoreAll();
+ }
+}
+
+function replace_word() {
+ if( spellerObject ) {
+ spellerObject.replaceWord();
+ }
+}
+
+function replace_all() {
+ if( spellerObject ) {
+ spellerObject.replaceAll();
+ }
+}
+
+function end_spell() {
+ if( spellerObject ) {
+ spellerObject.terminateSpell();
+ }
+}
+
+function undo() {
+ if( spellerObject ) {
+ spellerObject.undo();
+ }
+}
+
+function suggText() {
+ if( controlWindowObj ) {
+ controlWindowObj.setSuggestedText();
+ }
+}
+
+var FCKLang = window.parent.parent.FCKLang ; // by FredCK
+
+function init_spell() {
+ // By FredCK (fckLang attributes have been added to the HTML source of this page)
+ window.parent.parent.OnSpellerControlsLoad( this ) ;
+
+ var controlForm = document.spellcheck;
+
+ // create a new controlWindow object
+ controlWindowObj = new controlWindow( controlForm );
+
+ // call the init_spell() function in the parent frameset
+ if( parent.frames.length ) {
+ parent.init_spell( controlWindowObj );
+ } else {
+ alert( 'This page was loaded outside of a frameset. It might not display properly' );
+ }
+}
+
+</script>
+ </head>
+ <body class="controlWindowBody" onLoad="init_spell();" style="OVERFLOW: hidden" scroll="no"> <!-- by FredCK -->
+ <form name="spellcheck">
+ <table border="0" cellpadding="0" cellspacing="0" border="0" align="center">
+ <tr>
+ <td colspan="3" class="normalLabel"><span fckLang="DlgSpellNotInDic">Not in dictionary:</span></td>
+ </tr>
+ <tr>
+ <td colspan="3"><input class="readonlyInput" type="text" name="misword" readonly /></td>
+ </tr>
+ <tr>
+ <td colspan="3" height="5"></td>
+ </tr>
+ <tr>
+ <td class="normalLabel"><span fckLang="DlgSpellChangeTo">Change to:</span></td>
+ </tr>
+ <tr valign="top">
+ <td>
+ <table border="0" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td class="normalLabel">
+ <input class="textDefault" type="text" name="txtsugg" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <select class="suggSlct" name="sugg" size="7" onChange="suggText();" onDblClick="replace_word();">
+ <option></option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </td>
+ <td>&nbsp;&nbsp;</td>
+ <td>
+ <table border="0" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td>
+ <input class="buttonDefault" type="button" fckLang="DlgSpellBtnIgnore" value="Ignore" onClick="ignore_word();">
+ </td>
+ <td>&nbsp;&nbsp;</td>
+ <td>
+ <input class="buttonDefault" type="button" fckLang="DlgSpellBtnIgnoreAll" value="Ignore All" onClick="ignore_all();">
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3" height="5"></td>
+ </tr>
+ <tr>
+ <td>
+ <input class="buttonDefault" type="button" fckLang="DlgSpellBtnReplace" value="Replace" onClick="replace_word();">
+ </td>
+ <td>&nbsp;&nbsp;</td>
+ <td>
+ <input class="buttonDefault" type="button" fckLang="DlgSpellBtnReplaceAll" value="Replace All" onClick="replace_all();">
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3" height="5"></td>
+ </tr>
+ <tr>
+ <td>
+ <input class="buttonDefault" type="button" name="btnUndo" fckLang="DlgSpellBtnUndo" value="Undo" onClick="undo();"
+ disabled>
+ </td>
+ <td>&nbsp;&nbsp;</td>
+ <td>
+ <!-- by FredCK
+ <input class="buttonDefault" type="button" value="Close" onClick="end_spell();">
+ -->
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </form>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.pl b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.pl
new file mode 100644
index 0000000..8d3df65
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.pl
@@ -0,0 +1,180 @@
+#!/usr/bin/perl
+
+use CGI qw/ :standard /;
+use File::Temp qw/ tempfile tempdir /;
+
+# my $spellercss = '/speller/spellerStyle.css'; # by FredCK
+my $spellercss = '../spellerStyle.css'; # by FredCK
+# my $wordWindowSrc = '/speller/wordWindow.js'; # by FredCK
+my $wordWindowSrc = '../wordWindow.js'; # by FredCK
+my @textinputs = param( 'textinputs[]' ); # array
+# my $aspell_cmd = 'aspell'; # by FredCK (for Linux)
+my $aspell_cmd = '"C:\Program Files\Aspell\bin\aspell.exe"'; # by FredCK (for Windows)
+my $lang = 'en_US';
+# my $aspell_opts = "-a --lang=$lang --encoding=utf-8"; # by FredCK
+my $aspell_opts = "-a --lang=$lang --encoding=utf-8 -H --rem-sgml-check=alt"; # by FredCK
+my $input_separator = "A";
+
+# set the 'wordtext' JavaScript variable to the submitted text.
+sub printTextVar {
+ for( my $i = 0; $i <= $#textinputs; $i++ ) {
+ print "textinputs[$i] = decodeURIComponent('" . escapeQuote( $textinputs[$i] ) . "')\n";
+ }
+}
+
+sub printTextIdxDecl {
+ my $idx = shift;
+ print "words[$idx] = [];\n";
+ print "suggs[$idx] = [];\n";
+}
+
+sub printWordsElem {
+ my( $textIdx, $wordIdx, $word ) = @_;
+ print "words[$textIdx][$wordIdx] = '" . escapeQuote( $word ) . "';\n";
+}
+
+sub printSuggsElem {
+ my( $textIdx, $wordIdx, @suggs ) = @_;
+ print "suggs[$textIdx][$wordIdx] = [";
+ for my $i ( 0..$#suggs ) {
+ print "'" . escapeQuote( $suggs[$i] ) . "'";
+ if( $i < $#suggs ) {
+ print ", ";
+ }
+ }
+ print "];\n";
+}
+
+sub printCheckerResults {
+ my $textInputIdx = -1;
+ my $wordIdx = 0;
+ my $unhandledText;
+ # create temp file
+ my $dir = tempdir( CLEANUP => 1 );
+ my( $fh, $tmpfilename ) = tempfile( DIR => $dir );
+
+ # temp file was created properly?
+
+ # open temp file, add the submitted text.
+ for( my $i = 0; $i <= $#textinputs; $i++ ) {
+ $text = url_decode( $textinputs[$i] );
+ @lines = split( /\n/, $text );
+ print $fh "\%\n"; # exit terse mode
+ print $fh "^$input_separator\n";
+ print $fh "!\n"; # enter terse mode
+ for my $line ( @lines ) {
+ # use carat on each line to escape possible aspell commands
+ print $fh "^$line\n";
+ }
+
+ }
+ # exec aspell command
+ my $cmd = "$aspell_cmd $aspell_opts < $tmpfilename 2>&1";
+ open ASPELL, "$cmd |" or handleError( "Could not execute `$cmd`\\n$!" ) and return;
+ # parse each line of aspell return
+ for my $ret ( <ASPELL> ) {
+ chomp( $ret );
+ # if '&', then not in dictionary but has suggestions
+ # if '#', then not in dictionary and no suggestions
+ # if '*', then it is a delimiter between text inputs
+ if( $ret =~ /^\*/ ) {
+ $textInputIdx++;
+ printTextIdxDecl( $textInputIdx );
+ $wordIdx = 0;
+
+ } elsif( $ret =~ /^(&|#)/ ) {
+ my @tokens = split( " ", $ret, 5 );
+ printWordsElem( $textInputIdx, $wordIdx, $tokens[1] );
+ my @suggs = ();
+ if( $tokens[4] ) {
+ @suggs = split( ", ", $tokens[4] );
+ }
+ printSuggsElem( $textInputIdx, $wordIdx, @suggs );
+ $wordIdx++;
+ } else {
+ $unhandledText .= $ret;
+ }
+ }
+ close ASPELL or handleError( "Error executing `$cmd`\\n$unhandledText" ) and return;
+}
+
+sub escapeQuote {
+ my $str = shift;
+ $str =~ s/'/\\'/g;
+ return $str;
+}
+
+sub handleError {
+ my $err = shift;
+ print "error = '" . escapeQuote( $err ) . "';\n";
+}
+
+sub url_decode {
+ local $_ = @_ ? shift : $_;
+ defined or return;
+ # change + signs to spaces
+ tr/+/ /;
+ # change hex escapes to the proper characters
+ s/%([a-fA-F0-9]{2})/pack "H2", $1/eg;
+ return $_;
+}
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Display HTML
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+print <<EOF;
+Content-type: text/html; charset=utf-8
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel="stylesheet" type="text/css" href="$spellercss"/>
+<script src="$wordWindowSrc"></script>
+<script type="text/javascript">
+var suggs = new Array();
+var words = new Array();
+var textinputs = new Array();
+var error;
+EOF
+
+printTextVar();
+
+printCheckerResults();
+
+print <<EOF;
+var wordWindowObj = new wordWindow();
+wordWindowObj.originalSpellings = words;
+wordWindowObj.suggestions = suggs;
+wordWindowObj.textInputs = textinputs;
+
+
+function init_spell() {
+ // check if any error occured during server-side processing
+ if( error ) {
+ alert( error );
+ } else {
+ // call the init_spell() function in the parent frameset
+ if (parent.frames.length) {
+ parent.init_spell( wordWindowObj );
+ } else {
+ error = "This page was loaded outside of a frameset. ";
+ error += "It might not display properly";
+ alert( error );
+ }
+ }
+}
+
+</script>
+
+</head>
+<body onLoad="init_spell();">
+
+<script type="text/javascript">
+wordWindowObj.writeBody();
+</script>
+
+</body>
+</html>
+EOF
+
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellChecker.js b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellChecker.js
new file mode 100644
index 0000000..b5e55b7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellChecker.js
@@ -0,0 +1,462 @@
+////////////////////////////////////////////////////
+// spellChecker.js
+//
+// spellChecker object
+//
+// This file is sourced on web pages that have a textarea object to evaluate
+// for spelling. It includes the implementation for the spellCheckObject.
+//
+////////////////////////////////////////////////////
+
+
+// constructor
+function spellChecker( textObject ) {
+
+ // public properties - configurable
+// this.popUpUrl = '/speller/spellchecker.html'; // by FredCK
+ this.popUpUrl = 'fck_spellerpages/spellerpages/spellchecker.html'; // by FredCK
+ this.popUpName = 'spellchecker';
+// this.popUpProps = "menu=no,width=440,height=350,top=70,left=120,resizable=yes,status=yes"; // by FredCK
+ this.popUpProps = null ; // by FredCK
+// this.spellCheckScript = '/speller/server-scripts/spellchecker.php'; // by FredCK
+ //this.spellCheckScript = '/cgi-bin/spellchecker.pl';
+
+ // values used to keep track of what happened to a word
+ this.replWordFlag = "R"; // single replace
+ this.ignrWordFlag = "I"; // single ignore
+ this.replAllFlag = "RA"; // replace all occurances
+ this.ignrAllFlag = "IA"; // ignore all occurances
+ this.fromReplAll = "~RA"; // an occurance of a "replace all" word
+ this.fromIgnrAll = "~IA"; // an occurance of a "ignore all" word
+ // properties set at run time
+ this.wordFlags = new Array();
+ this.currentTextIndex = 0;
+ this.currentWordIndex = 0;
+ this.spellCheckerWin = null;
+ this.controlWin = null;
+ this.wordWin = null;
+ this.textArea = textObject; // deprecated
+ this.textInputs = arguments;
+
+ // private methods
+ this._spellcheck = _spellcheck;
+ this._getSuggestions = _getSuggestions;
+ this._setAsIgnored = _setAsIgnored;
+ this._getTotalReplaced = _getTotalReplaced;
+ this._setWordText = _setWordText;
+ this._getFormInputs = _getFormInputs;
+
+ // public methods
+ this.openChecker = openChecker;
+ this.startCheck = startCheck;
+ this.checkTextBoxes = checkTextBoxes;
+ this.checkTextAreas = checkTextAreas;
+ this.spellCheckAll = spellCheckAll;
+ this.ignoreWord = ignoreWord;
+ this.ignoreAll = ignoreAll;
+ this.replaceWord = replaceWord;
+ this.replaceAll = replaceAll;
+ this.terminateSpell = terminateSpell;
+ this.undo = undo;
+
+ // set the current window's "speller" property to the instance of this class.
+ // this object can now be referenced by child windows/frames.
+ window.speller = this;
+}
+
+// call this method to check all text boxes (and only text boxes) in the HTML document
+function checkTextBoxes() {
+ this.textInputs = this._getFormInputs( "^text$" );
+ this.openChecker();
+}
+
+// call this method to check all textareas (and only textareas ) in the HTML document
+function checkTextAreas() {
+ this.textInputs = this._getFormInputs( "^textarea$" );
+ this.openChecker();
+}
+
+// call this method to check all text boxes and textareas in the HTML document
+function spellCheckAll() {
+ this.textInputs = this._getFormInputs( "^text(area)?$" );
+ this.openChecker();
+}
+
+// call this method to check text boxe(s) and/or textarea(s) that were passed in to the
+// object's constructor or to the textInputs property
+function openChecker() {
+ this.spellCheckerWin = window.open( this.popUpUrl, this.popUpName, this.popUpProps );
+ if( !this.spellCheckerWin.opener ) {
+ this.spellCheckerWin.opener = window;
+ }
+}
+
+function startCheck( wordWindowObj, controlWindowObj ) {
+
+ // set properties from args
+ this.wordWin = wordWindowObj;
+ this.controlWin = controlWindowObj;
+
+ // reset properties
+ this.wordWin.resetForm();
+ this.controlWin.resetForm();
+ this.currentTextIndex = 0;
+ this.currentWordIndex = 0;
+ // initialize the flags to an array - one element for each text input
+ this.wordFlags = new Array( this.wordWin.textInputs.length );
+ // each element will be an array that keeps track of each word in the text
+ for( var i=0; i<this.wordFlags.length; i++ ) {
+ this.wordFlags[i] = [];
+ }
+
+ // start
+ this._spellcheck();
+
+ return true;
+}
+
+function ignoreWord() {
+ var wi = this.currentWordIndex;
+ var ti = this.currentTextIndex;
+ if( !this.wordWin ) {
+ alert( 'Error: Word frame not available.' );
+ return false;
+ }
+ if( !this.wordWin.getTextVal( ti, wi )) {
+ alert( 'Error: "Not in dictionary" text is missing.' );
+ return false;
+ }
+ // set as ignored
+ if( this._setAsIgnored( ti, wi, this.ignrWordFlag )) {
+ this.currentWordIndex++;
+ this._spellcheck();
+ }
+ return true;
+}
+
+function ignoreAll() {
+ var wi = this.currentWordIndex;
+ var ti = this.currentTextIndex;
+ if( !this.wordWin ) {
+ alert( 'Error: Word frame not available.' );
+ return false;
+ }
+ // get the word that is currently being evaluated.
+ var s_word_to_repl = this.wordWin.getTextVal( ti, wi );
+ if( !s_word_to_repl ) {
+ alert( 'Error: "Not in dictionary" text is missing' );
+ return false;
+ }
+
+ // set this word as an "ignore all" word.
+ this._setAsIgnored( ti, wi, this.ignrAllFlag );
+
+ // loop through all the words after this word
+ for( var i = ti; i < this.wordWin.textInputs.length; i++ ) {
+ for( var j = 0; j < this.wordWin.totalWords( i ); j++ ) {
+ if(( i == ti && j > wi ) || i > ti ) {
+ // future word: set as "from ignore all" if
+ // 1) do not already have a flag and
+ // 2) have the same value as current word
+ if(( this.wordWin.getTextVal( i, j ) == s_word_to_repl )
+ && ( !this.wordFlags[i][j] )) {
+ this._setAsIgnored( i, j, this.fromIgnrAll );
+ }
+ }
+ }
+ }
+
+ // finally, move on
+ this.currentWordIndex++;
+ this._spellcheck();
+ return true;
+}
+
+function replaceWord() {
+ var wi = this.currentWordIndex;
+ var ti = this.currentTextIndex;
+ if( !this.wordWin ) {
+ alert( 'Error: Word frame not available.' );
+ return false;
+ }
+ if( !this.wordWin.getTextVal( ti, wi )) {
+ alert( 'Error: "Not in dictionary" text is missing' );
+ return false;
+ }
+ if( !this.controlWin.replacementText ) {
+ return false ;
+ }
+ var txt = this.controlWin.replacementText;
+ if( txt.value ) {
+ var newspell = new String( txt.value );
+ if( this._setWordText( ti, wi, newspell, this.replWordFlag )) {
+ this.currentWordIndex++;
+ this._spellcheck();
+ }
+ }
+ return true;
+}
+
+function replaceAll() {
+ var ti = this.currentTextIndex;
+ var wi = this.currentWordIndex;
+ if( !this.wordWin ) {
+ alert( 'Error: Word frame not available.' );
+ return false;
+ }
+ var s_word_to_repl = this.wordWin.getTextVal( ti, wi );
+ if( !s_word_to_repl ) {
+ alert( 'Error: "Not in dictionary" text is missing' );
+ return false;
+ }
+ var txt = this.controlWin.replacementText;
+ if( !txt.value ) return false;
+ var newspell = new String( txt.value );
+
+ // set this word as a "replace all" word.
+ this._setWordText( ti, wi, newspell, this.replAllFlag );
+
+ // loop through all the words after this word
+ for( var i = ti; i < this.wordWin.textInputs.length; i++ ) {
+ for( var j = 0; j < this.wordWin.totalWords( i ); j++ ) {
+ if(( i == ti && j > wi ) || i > ti ) {
+ // future word: set word text to s_word_to_repl if
+ // 1) do not already have a flag and
+ // 2) have the same value as s_word_to_repl
+ if(( this.wordWin.getTextVal( i, j ) == s_word_to_repl )
+ && ( !this.wordFlags[i][j] )) {
+ this._setWordText( i, j, newspell, this.fromReplAll );
+ }
+ }
+ }
+ }
+
+ // finally, move on
+ this.currentWordIndex++;
+ this._spellcheck();
+ return true;
+}
+
+function terminateSpell() {
+ // called when we have reached the end of the spell checking.
+ var msg = ""; // by FredCK
+ var numrepl = this._getTotalReplaced();
+ if( numrepl == 0 ) {
+ // see if there were no misspellings to begin with
+ if( !this.wordWin ) {
+ msg = "";
+ } else {
+ if( this.wordWin.totalMisspellings() ) {
+// msg += "No words changed."; // by FredCK
+ msg += FCKLang.DlgSpellNoChanges ; // by FredCK
+ } else {
+// msg += "No misspellings found."; // by FredCK
+ msg += FCKLang.DlgSpellNoMispell ; // by FredCK
+ }
+ }
+ } else if( numrepl == 1 ) {
+// msg += "One word changed."; // by FredCK
+ msg += FCKLang.DlgSpellOneChange ; // by FredCK
+ } else {
+// msg += numrepl + " words changed."; // by FredCK
+ msg += FCKLang.DlgSpellManyChanges.replace( /%1/g, numrepl ) ;
+ }
+ if( msg ) {
+// msg += "\n"; // by FredCK
+ alert( msg );
+ }
+
+ if( numrepl > 0 ) {
+ // update the text field(s) on the opener window
+ for( var i = 0; i < this.textInputs.length; i++ ) {
+ // this.textArea.value = this.wordWin.text;
+ if( this.wordWin ) {
+ if( this.wordWin.textInputs[i] ) {
+ this.textInputs[i].value = this.wordWin.textInputs[i];
+ }
+ }
+ }
+ }
+
+ // return back to the calling window
+// this.spellCheckerWin.close(); // by FredCK
+ if ( typeof( this.OnFinished ) == 'function' ) // by FredCK
+ this.OnFinished(numrepl) ; // by FredCK
+
+ return true;
+}
+
+function undo() {
+ // skip if this is the first word!
+ var ti = this.currentTextIndex;
+ var wi = this.currentWordIndex;
+
+ if( this.wordWin.totalPreviousWords( ti, wi ) > 0 ) {
+ this.wordWin.removeFocus( ti, wi );
+
+ // go back to the last word index that was acted upon
+ do {
+ // if the current word index is zero then reset the seed
+ if( this.currentWordIndex == 0 && this.currentTextIndex > 0 ) {
+ this.currentTextIndex--;
+ this.currentWordIndex = this.wordWin.totalWords( this.currentTextIndex )-1;
+ if( this.currentWordIndex < 0 ) this.currentWordIndex = 0;
+ } else {
+ if( this.currentWordIndex > 0 ) {
+ this.currentWordIndex--;
+ }
+ }
+ } while (
+ this.wordWin.totalWords( this.currentTextIndex ) == 0
+ || this.wordFlags[this.currentTextIndex][this.currentWordIndex] == this.fromIgnrAll
+ || this.wordFlags[this.currentTextIndex][this.currentWordIndex] == this.fromReplAll
+ );
+
+ var text_idx = this.currentTextIndex;
+ var idx = this.currentWordIndex;
+ var preReplSpell = this.wordWin.originalSpellings[text_idx][idx];
+
+ // if we got back to the first word then set the Undo button back to disabled
+ if( this.wordWin.totalPreviousWords( text_idx, idx ) == 0 ) {
+ this.controlWin.disableUndo();
+ }
+
+ var i, j, origSpell ;
+ // examine what happened to this current word.
+ switch( this.wordFlags[text_idx][idx] ) {
+ // replace all: go through this and all the future occurances of the word
+ // and revert them all to the original spelling and clear their flags
+ case this.replAllFlag :
+ for( i = text_idx; i < this.wordWin.textInputs.length; i++ ) {
+ for( j = 0; j < this.wordWin.totalWords( i ); j++ ) {
+ if(( i == text_idx && j >= idx ) || i > text_idx ) {
+ origSpell = this.wordWin.originalSpellings[i][j];
+ if( origSpell == preReplSpell ) {
+ this._setWordText ( i, j, origSpell, undefined );
+ }
+ }
+ }
+ }
+ break;
+
+ // ignore all: go through all the future occurances of the word
+ // and clear their flags
+ case this.ignrAllFlag :
+ for( i = text_idx; i < this.wordWin.textInputs.length; i++ ) {
+ for( j = 0; j < this.wordWin.totalWords( i ); j++ ) {
+ if(( i == text_idx && j >= idx ) || i > text_idx ) {
+ origSpell = this.wordWin.originalSpellings[i][j];
+ if( origSpell == preReplSpell ) {
+ this.wordFlags[i][j] = undefined;
+ }
+ }
+ }
+ }
+ break;
+
+ // replace: revert the word to its original spelling
+ case this.replWordFlag :
+ this._setWordText ( text_idx, idx, preReplSpell, undefined );
+ break;
+ }
+
+ // For all four cases, clear the wordFlag of this word. re-start the process
+ this.wordFlags[text_idx][idx] = undefined;
+ this._spellcheck();
+ }
+}
+
+function _spellcheck() {
+ var ww = this.wordWin;
+
+ // check if this is the last word in the current text element
+ if( this.currentWordIndex == ww.totalWords( this.currentTextIndex) ) {
+ this.currentTextIndex++;
+ this.currentWordIndex = 0;
+ // keep going if we're not yet past the last text element
+ if( this.currentTextIndex < this.wordWin.textInputs.length ) {
+ this._spellcheck();
+ return;
+ } else {
+ this.terminateSpell();
+ return;
+ }
+ }
+
+ // if this is after the first one make sure the Undo button is enabled
+ if( this.currentWordIndex > 0 ) {
+ this.controlWin.enableUndo();
+ }
+
+ // skip the current word if it has already been worked on
+ if( this.wordFlags[this.currentTextIndex][this.currentWordIndex] ) {
+ // increment the global current word index and move on.
+ this.currentWordIndex++;
+ this._spellcheck();
+ } else {
+ var evalText = ww.getTextVal( this.currentTextIndex, this.currentWordIndex );
+ if( evalText ) {
+ this.controlWin.evaluatedText.value = evalText;
+ ww.setFocus( this.currentTextIndex, this.currentWordIndex );
+ this._getSuggestions( this.currentTextIndex, this.currentWordIndex );
+ }
+ }
+}
+
+function _getSuggestions( text_num, word_num ) {
+ this.controlWin.clearSuggestions();
+ // add suggestion in list for each suggested word.
+ // get the array of suggested words out of the
+ // three-dimensional array containing all suggestions.
+ var a_suggests = this.wordWin.suggestions[text_num][word_num];
+ if( a_suggests ) {
+ // got an array of suggestions.
+ for( var ii = 0; ii < a_suggests.length; ii++ ) {
+ this.controlWin.addSuggestion( a_suggests[ii] );
+ }
+ }
+ this.controlWin.selectDefaultSuggestion();
+}
+
+function _setAsIgnored( text_num, word_num, flag ) {
+ // set the UI
+ this.wordWin.removeFocus( text_num, word_num );
+ // do the bookkeeping
+ this.wordFlags[text_num][word_num] = flag;
+ return true;
+}
+
+function _getTotalReplaced() {
+ var i_replaced = 0;
+ for( var i = 0; i < this.wordFlags.length; i++ ) {
+ for( var j = 0; j < this.wordFlags[i].length; j++ ) {
+ if(( this.wordFlags[i][j] == this.replWordFlag )
+ || ( this.wordFlags[i][j] == this.replAllFlag )
+ || ( this.wordFlags[i][j] == this.fromReplAll )) {
+ i_replaced++;
+ }
+ }
+ }
+ return i_replaced;
+}
+
+function _setWordText( text_num, word_num, newText, flag ) {
+ // set the UI and form inputs
+ this.wordWin.setText( text_num, word_num, newText );
+ // keep track of what happened to this word:
+ this.wordFlags[text_num][word_num] = flag;
+ return true;
+}
+
+function _getFormInputs( inputPattern ) {
+ var inputs = new Array();
+ for( var i = 0; i < document.forms.length; i++ ) {
+ for( var j = 0; j < document.forms[i].elements.length; j++ ) {
+ if( document.forms[i].elements[j].type.match( inputPattern )) {
+ inputs[inputs.length] = document.forms[i].elements[j];
+ }
+ }
+ }
+ return inputs;
+}
+
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellchecker.html b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellchecker.html
new file mode 100644
index 0000000..cbcd7db
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellchecker.html
@@ -0,0 +1,71 @@
+
+<script>
+
+var wordWindow = null;
+var controlWindow = null;
+
+function init_spell( spellerWindow ) {
+
+ if( spellerWindow ) {
+ if( spellerWindow.windowType == "wordWindow" ) {
+ wordWindow = spellerWindow;
+ } else if ( spellerWindow.windowType == "controlWindow" ) {
+ controlWindow = spellerWindow;
+ }
+ }
+
+ if( controlWindow && wordWindow ) {
+ // populate the speller object and start it off!
+ var speller = opener.speller;
+ wordWindow.speller = speller;
+ speller.startCheck( wordWindow, controlWindow );
+ }
+}
+
+// encodeForPost
+function encodeForPost( str ) {
+ var s = new String( str );
+ s = encodeURIComponent( s );
+ // additionally encode single quotes to evade any PHP
+ // magic_quotes_gpc setting (it inserts escape characters and
+ // therefore skews the btye positions of misspelled words)
+ return s.replace( /\'/g, '%27' );
+}
+
+// post the text area data to the script that populates the speller
+function postWords() {
+ var bodyDoc = window.frames[0].document;
+ bodyDoc.open();
+ bodyDoc.write('<html>');
+ bodyDoc.write('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">');
+ bodyDoc.write('<link rel="stylesheet" type="text/css" href="spellerStyle.css"/>');
+ if (opener) {
+ var speller = opener.speller;
+ bodyDoc.write('<body class="normalText" onLoad="document.forms[0].submit();">');
+ bodyDoc.write('<p>' + window.parent.FCKLang.DlgSpellProgress + '<\/p>'); // by FredCK
+ bodyDoc.write('<form action="'+speller.spellCheckScript+'" method="post">');
+ for( var i = 0; i < speller.textInputs.length; i++ ) {
+ bodyDoc.write('<input type="hidden" name="textinputs[]" value="'+encodeForPost(speller.textInputs[i].value)+'">');
+ }
+ bodyDoc.write('<\/form>');
+ bodyDoc.write('<\/body>');
+ } else {
+ bodyDoc.write('<body class="normalText">');
+ bodyDoc.write('<p><b>This page cannot be displayed<\/b><\/p><p>The window was not opened from another window.<\/p>');
+ bodyDoc.write('<\/body>');
+ }
+ bodyDoc.write('<\/html>');
+ bodyDoc.close();
+}
+</script>
+
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<head>
+<title>Speller Pages</title>
+</head>
+<frameset rows="*,201" onLoad="postWords();">
+<frame src="blank.html">
+<frame src="controls.html">
+</frameset>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellerStyle.css b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellerStyle.css
new file mode 100644
index 0000000..4df608d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/spellerStyle.css
@@ -0,0 +1,49 @@
+.blend {
+ font-family: courier new;
+ font-size: 10pt;
+ border: 0;
+ margin-bottom:-1;
+}
+.normalLabel {
+ font-size:8pt;
+}
+.normalText {
+ font-family:arial, helvetica, sans-serif;
+ font-size:10pt;
+ color:000000;
+ background-color:FFFFFF;
+}
+.plainText {
+ font-family: courier new, courier, monospace;
+ font-size: 10pt;
+ color:000000;
+ background-color:FFFFFF;
+}
+.controlWindowBody {
+ font-family:arial, helvetica, sans-serif;
+ font-size:8pt;
+ padding: 7px ; /* by FredCK */
+ margin: 0px ; /* by FredCK */
+ /* color:000000; by FredCK */
+ /* background-color:DADADA; by FredCK */
+}
+.readonlyInput {
+ background-color:DADADA;
+ color:000000;
+ font-size:8pt;
+ width:392px;
+}
+.textDefault {
+ font-size:8pt;
+ width: 200px;
+}
+.buttonDefault {
+ width:90px;
+ height:22px;
+ font-size:8pt;
+}
+.suggSlct {
+ width:200px;
+ margin-top:2;
+ font-size:8pt;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/wordWindow.js b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/wordWindow.js
new file mode 100644
index 0000000..7990296
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_spellerpages/spellerpages/wordWindow.js
@@ -0,0 +1,272 @@
+////////////////////////////////////////////////////
+// wordWindow object
+////////////////////////////////////////////////////
+function wordWindow() {
+ // private properties
+ this._forms = [];
+
+ // private methods
+ this._getWordObject = _getWordObject;
+ //this._getSpellerObject = _getSpellerObject;
+ this._wordInputStr = _wordInputStr;
+ this._adjustIndexes = _adjustIndexes;
+ this._isWordChar = _isWordChar;
+ this._lastPos = _lastPos;
+
+ // public properties
+ this.wordChar = /[a-zA-Z]/;
+ this.windowType = "wordWindow";
+ this.originalSpellings = new Array();
+ this.suggestions = new Array();
+ this.checkWordBgColor = "pink";
+ this.normWordBgColor = "white";
+ this.text = "";
+ this.textInputs = new Array();
+ this.indexes = new Array();
+ //this.speller = this._getSpellerObject();
+
+ // public methods
+ this.resetForm = resetForm;
+ this.totalMisspellings = totalMisspellings;
+ this.totalWords = totalWords;
+ this.totalPreviousWords = totalPreviousWords;
+ //this.getTextObjectArray = getTextObjectArray;
+ this.getTextVal = getTextVal;
+ this.setFocus = setFocus;
+ this.removeFocus = removeFocus;
+ this.setText = setText;
+ //this.getTotalWords = getTotalWords;
+ this.writeBody = writeBody;
+ this.printForHtml = printForHtml;
+}
+
+function resetForm() {
+ if( this._forms ) {
+ for( var i = 0; i < this._forms.length; i++ ) {
+ this._forms[i].reset();
+ }
+ }
+ return true;
+}
+
+function totalMisspellings() {
+ var total_words = 0;
+ for( var i = 0; i < this.textInputs.length; i++ ) {
+ total_words += this.totalWords( i );
+ }
+ return total_words;
+}
+
+function totalWords( textIndex ) {
+ return this.originalSpellings[textIndex].length;
+}
+
+function totalPreviousWords( textIndex, wordIndex ) {
+ var total_words = 0;
+ for( var i = 0; i <= textIndex; i++ ) {
+ for( var j = 0; j < this.totalWords( i ); j++ ) {
+ if( i == textIndex && j == wordIndex ) {
+ break;
+ } else {
+ total_words++;
+ }
+ }
+ }
+ return total_words;
+}
+
+//function getTextObjectArray() {
+// return this._form.elements;
+//}
+
+function getTextVal( textIndex, wordIndex ) {
+ var word = this._getWordObject( textIndex, wordIndex );
+ if( word ) {
+ return word.value;
+ }
+}
+
+function setFocus( textIndex, wordIndex ) {
+ var word = this._getWordObject( textIndex, wordIndex );
+ if( word ) {
+ if( word.type == "text" ) {
+ word.focus();
+ word.style.backgroundColor = this.checkWordBgColor;
+ }
+ }
+}
+
+function removeFocus( textIndex, wordIndex ) {
+ var word = this._getWordObject( textIndex, wordIndex );
+ if( word ) {
+ if( word.type == "text" ) {
+ word.blur();
+ word.style.backgroundColor = this.normWordBgColor;
+ }
+ }
+}
+
+function setText( textIndex, wordIndex, newText ) {
+ var word = this._getWordObject( textIndex, wordIndex );
+ var beginStr;
+ var endStr;
+ if( word ) {
+ var pos = this.indexes[textIndex][wordIndex];
+ var oldText = word.value;
+ // update the text given the index of the string
+ beginStr = this.textInputs[textIndex].substring( 0, pos );
+ endStr = this.textInputs[textIndex].substring(
+ pos + oldText.length,
+ this.textInputs[textIndex].length
+ );
+ this.textInputs[textIndex] = beginStr + newText + endStr;
+
+ // adjust the indexes on the stack given the differences in
+ // length between the new word and old word.
+ var lengthDiff = newText.length - oldText.length;
+ this._adjustIndexes( textIndex, wordIndex, lengthDiff );
+
+ word.size = newText.length;
+ word.value = newText;
+ this.removeFocus( textIndex, wordIndex );
+ }
+}
+
+
+function writeBody() {
+ var d = window.document;
+ var is_html = false;
+
+ d.open();
+
+ // iterate through each text input.
+ for( var txtid = 0; txtid < this.textInputs.length; txtid++ ) {
+ var end_idx = 0;
+ var begin_idx = 0;
+ d.writeln( '<form name="textInput'+txtid+'">' );
+ var wordtxt = this.textInputs[txtid];
+ this.indexes[txtid] = [];
+
+ if( wordtxt ) {
+ var orig = this.originalSpellings[txtid];
+ if( !orig ) break;
+
+ //!!! plain text, or HTML mode?
+ d.writeln( '<div class="plainText">' );
+ // iterate through each occurrence of a misspelled word.
+ for( var i = 0; i < orig.length; i++ ) {
+ // find the position of the current misspelled word,
+ // starting at the last misspelled word.
+ // and keep looking if it's a substring of another word
+ do {
+ begin_idx = wordtxt.indexOf( orig[i], end_idx );
+ end_idx = begin_idx + orig[i].length;
+ // word not found? messed up!
+ if( begin_idx == -1 ) break;
+ // look at the characters immediately before and after
+ // the word. If they are word characters we'll keep looking.
+ var before_char = wordtxt.charAt( begin_idx - 1 );
+ var after_char = wordtxt.charAt( end_idx );
+ } while (
+ this._isWordChar( before_char )
+ || this._isWordChar( after_char )
+ );
+
+ // keep track of its position in the original text.
+ this.indexes[txtid][i] = begin_idx;
+
+ // write out the characters before the current misspelled word
+ for( var j = this._lastPos( txtid, i ); j < begin_idx; j++ ) {
+ // !!! html mode? make it html compatible
+ d.write( this.printForHtml( wordtxt.charAt( j )));
+ }
+
+ // write out the misspelled word.
+ d.write( this._wordInputStr( orig[i] ));
+
+ // if it's the last word, write out the rest of the text
+ if( i == orig.length-1 ){
+ d.write( printForHtml( wordtxt.substr( end_idx )));
+ }
+ }
+
+ d.writeln( '</div>' );
+
+ }
+ d.writeln( '</form>' );
+ }
+ //for ( var j = 0; j < d.forms.length; j++ ) {
+ // alert( d.forms[j].name );
+ // for( var k = 0; k < d.forms[j].elements.length; k++ ) {
+ // alert( d.forms[j].elements[k].name + ": " + d.forms[j].elements[k].value );
+ // }
+ //}
+
+ // set the _forms property
+ this._forms = d.forms;
+ d.close();
+}
+
+// return the character index in the full text after the last word we evaluated
+function _lastPos( txtid, idx ) {
+ if( idx > 0 )
+ return this.indexes[txtid][idx-1] + this.originalSpellings[txtid][idx-1].length;
+ else
+ return 0;
+}
+
+function printForHtml( n ) {
+ return n ; // by FredCK
+/*
+ var htmlstr = n;
+ if( htmlstr.length == 1 ) {
+ // do simple case statement if it's just one character
+ switch ( n ) {
+ case "\n":
+ htmlstr = '<br/>';
+ break;
+ case "<":
+ htmlstr = '&lt;';
+ break;
+ case ">":
+ htmlstr = '&gt;';
+ break;
+ }
+ return htmlstr;
+ } else {
+ htmlstr = htmlstr.replace( /</g, '&lt' );
+ htmlstr = htmlstr.replace( />/g, '&gt' );
+ htmlstr = htmlstr.replace( /\n/g, '<br/>' );
+ return htmlstr;
+ }
+*/
+}
+
+function _isWordChar( letter ) {
+ if( letter.search( this.wordChar ) == -1 ) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+function _getWordObject( textIndex, wordIndex ) {
+ if( this._forms[textIndex] ) {
+ if( this._forms[textIndex].elements[wordIndex] ) {
+ return this._forms[textIndex].elements[wordIndex];
+ }
+ }
+ return null;
+}
+
+function _wordInputStr( word ) {
+ var str = '<input readonly ';
+ str += 'class="blend" type="text" value="' + word + '" size="' + word.length + '">';
+ return str;
+}
+
+function _adjustIndexes( textIndex, wordIndex, lengthDiff ) {
+ for( var i = wordIndex + 1; i < this.originalSpellings[textIndex].length; i++ ) {
+ this.indexes[textIndex][i] = this.indexes[textIndex][i] + lengthDiff;
+ }
+}
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_table.html b/httemplate/elements/fckeditor/editor/dialog/fck_table.html
new file mode 100644
index 0000000..6bb9d11
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_table.html
@@ -0,0 +1,291 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Table dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Table Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+// Gets the table if there is one selected.
+var table ;
+var e = oEditor.FCKSelection.GetSelectedElement() ;
+
+if ( ( !e && document.location.search.substr(1) == 'Parent' ) || ( e && e.tagName != 'TABLE' ) )
+ e = oEditor.FCKSelection.MoveToAncestorNode( 'TABLE' ) ;
+
+if ( e && e.tagName == "TABLE" )
+ table = e ;
+
+// Fired when the window loading process is finished. It sets the fields with the
+// actual values if a table is selected in the editor.
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if (table)
+ {
+ document.getElementById('txtRows').value = table.rows.length ;
+ document.getElementById('txtColumns').value = table.rows[0].cells.length ;
+
+ // Gets the value from the Width or the Style attribute
+ var iWidth = (table.style.width ? table.style.width : table.width ) ;
+ var iHeight = (table.style.height ? table.style.height : table.height ) ;
+
+ if (iWidth.indexOf('%') >= 0) // Percentual = %
+ {
+ iWidth = parseInt( iWidth.substr(0,iWidth.length - 1), 10 ) ;
+ document.getElementById('selWidthType').value = "percent" ;
+ }
+ else if (iWidth.indexOf('px') >= 0) // Style Pixel = px
+ { //
+ iWidth = iWidth.substr(0,iWidth.length - 2);
+ document.getElementById('selWidthType').value = "pixels" ;
+ }
+
+ if (iHeight && iHeight.indexOf('px') >= 0) // Style Pixel = px
+ iHeight = iHeight.substr(0,iHeight.length - 2);
+
+ document.getElementById('txtWidth').value = iWidth || '' ;
+ document.getElementById('txtHeight').value = iHeight || '' ;
+ document.getElementById('txtBorder').value = GetAttribute( table, 'border', '' ) ;
+ document.getElementById('selAlignment').value = GetAttribute( table, 'align', '' ) ;
+ document.getElementById('txtCellPadding').value = GetAttribute( table, 'cellPadding', '' ) ;
+ document.getElementById('txtCellSpacing').value = GetAttribute( table, 'cellSpacing', '' ) ;
+ document.getElementById('txtSummary').value = GetAttribute( table, 'summary', '' ) ;
+// document.getElementById('cmbFontStyle').value = table.className ;
+
+ if (table.caption) document.getElementById('txtCaption').value = table.caption.innerHTML ;
+
+ document.getElementById('txtRows').disabled = true ;
+ document.getElementById('txtColumns').disabled = true ;
+ }
+
+ window.parent.SetOkButton( true ) ;
+ window.parent.SetAutoSize( true ) ;
+}
+
+// Fired when the user press the OK button
+function Ok()
+{
+ var bExists = ( table != null ) ;
+
+ if ( ! bExists )
+ table = oEditor.FCK.EditorDocument.createElement( "TABLE" ) ;
+
+ // Removes the Width and Height styles
+ if ( bExists && table.style.width ) table.style.width = null ; //.removeAttribute("width") ;
+ if ( bExists && table.style.height ) table.style.height = null ; //.removeAttribute("height") ;
+
+ var sWidth = GetE('txtWidth').value ;
+ if ( sWidth.length > 0 && GetE('selWidthType').value == 'percent' )
+ sWidth += '%' ;
+
+ SetAttribute( table, 'width' , sWidth ) ;
+ SetAttribute( table, 'height' , GetE('txtHeight').value ) ;
+ SetAttribute( table, 'border' , GetE('txtBorder').value ) ;
+ SetAttribute( table, 'align' , GetE('selAlignment').value ) ;
+ SetAttribute( table, 'cellPadding' , GetE('txtCellPadding').value ) ;
+ SetAttribute( table, 'cellSpacing' , GetE('txtCellSpacing').value ) ;
+ SetAttribute( table, 'summary' , GetE('txtSummary').value ) ;
+
+ var eCaption = oEditor.FCKDomTools.GetFirstChild( table, 'CAPTION' ) ;
+
+ if ( document.getElementById('txtCaption').value != '')
+ {
+ if ( !eCaption )
+ {
+ eCaption = oEditor.FCK.EditorDocument.createElement( 'CAPTION' ) ;
+ table.insertBefore( eCaption, table.firstChild ) ;
+ }
+
+ eCaption.innerHTML = document.getElementById('txtCaption').value ;
+ }
+ else if ( bExists && eCaption )
+ {
+ if ( oEditor.FCKBrowserInfo.IsIE )
+ eCaption.innerHTML = '' ; // TODO: It causes an IE internal error if using removeChild or table.deleteCaption().
+ else
+ eCaption.parentNode.removeChild( eCaption ) ;
+ }
+
+ if (! bExists)
+ {
+ var iRows = document.getElementById('txtRows').value ;
+ var iCols = document.getElementById('txtColumns').value ;
+
+ for ( var r = 0 ; r < iRows ; r++ )
+ {
+ var oRow = table.insertRow(-1) ;
+ for ( var c = 0 ; c < iCols ; c++ )
+ {
+ var oCell = oRow.insertCell(-1) ;
+ if ( oEditor.FCKBrowserInfo.IsGeckoLike )
+ oCell.innerHTML = GECKO_BOGUS ;
+ //oCell.innerHTML = "&nbsp;" ;
+ }
+ }
+
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ oEditor.FCK.InsertElement( table ) ;
+ }
+
+ return true ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <table id="otable" cellspacing="0" cellpadding="0" width="100%" border="0" style="height: 100%">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="1" width="100%" border="0">
+ <tr>
+ <td valign="top">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td>
+ <span fcklang="DlgTableRows">Rows</span>:</td>
+ <td>
+ &nbsp;<input id="txtRows" type="text" maxlength="3" size="2" value="3" name="txtRows"
+ onkeypress="return IsDigit(event);" /></td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgTableColumns">Columns</span>:</td>
+ <td>
+ &nbsp;<input id="txtColumns" type="text" maxlength="2" size="2" value="2" name="txtColumns"
+ onkeypress="return IsDigit(event);" /></td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;</td>
+ <td>
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgTableBorder">Border size</span>:</td>
+ <td>
+ &nbsp;<input id="txtBorder" type="text" maxlength="2" size="2" value="1" name="txtBorder"
+ onkeypress="return IsDigit(event);" /></td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgTableAlign">Alignment</span>:</td>
+ <td>
+ &nbsp;<select id="selAlignment" name="selAlignment">
+ <option fcklang="DlgTableAlignNotSet" value="" selected="selected">&lt;Not set&gt;</option>
+ <option fcklang="DlgTableAlignLeft" value="left">Left</option>
+ <option fcklang="DlgTableAlignCenter" value="center">Center</option>
+ <option fcklang="DlgTableAlignRight" value="right">Right</option>
+ </select></td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ &nbsp;&nbsp;&nbsp;</td>
+ <td align="right" valign="top">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td>
+ <span fcklang="DlgTableWidth">Width</span>:</td>
+ <td>
+ &nbsp;<input id="txtWidth" type="text" maxlength="4" size="3" value="200" name="txtWidth"
+ onkeypress="return IsDigit(event);" /></td>
+ <td>
+ &nbsp;<select id="selWidthType" name="selWidthType">
+ <option fcklang="DlgTableWidthPx" value="pixels" selected="selected">pixels</option>
+ <option fcklang="DlgTableWidthPc" value="percent">percent</option>
+ </select></td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgTableHeight">Height</span>:</td>
+ <td>
+ &nbsp;<input id="txtHeight" type="text" maxlength="4" size="3" name="txtHeight" onkeypress="return IsDigit(event);" /></td>
+ <td>
+ &nbsp;<span fcklang="DlgTableWidthPx">pixels</span></td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;</td>
+ <td>
+ &nbsp;</td>
+ <td>
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgTableCellSpace">Cell spacing</span>:</td>
+ <td>
+ &nbsp;<input id="txtCellSpacing" type="text" maxlength="2" size="2" value="1" name="txtCellSpacing"
+ onkeypress="return IsDigit(event);" /></td>
+ <td>
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgTableCellPad">Cell padding</span>:</td>
+ <td>
+ &nbsp;<input id="txtCellPadding" type="text" maxlength="2" size="2" value="1" name="txtCellPadding"
+ onkeypress="return IsDigit(event);" /></td>
+ <td>
+ &nbsp;</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgTableCaption">Caption</span>:&nbsp;</td>
+ <td>
+ &nbsp;</td>
+ <td width="100%" nowrap="nowrap">
+ <input id="txtCaption" type="text" style="width: 100%" /></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgTableSummary">Summary</span>:&nbsp;</td>
+ <td>
+ &nbsp;</td>
+ <td width="100%" nowrap="nowrap">
+ <input id="txtSummary" type="text" style="width: 100%" /></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_tablecell.html b/httemplate/elements/fckeditor/editor/dialog/fck_tablecell.html
new file mode 100644
index 0000000..b7c536b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_tablecell.html
@@ -0,0 +1,255 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Cell properties dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Table Cell Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+// Array of selected Cells
+var aCells = oEditor.FCKTableHandler.GetSelectedCells() ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage( document ) ;
+
+ SetStartupValue() ;
+
+ window.parent.SetOkButton( true ) ;
+ window.parent.SetAutoSize( true ) ;
+}
+
+function SetStartupValue()
+{
+ if ( aCells.length > 0 )
+ {
+ var oCell = aCells[0] ;
+ var iWidth = GetAttribute( oCell, 'width' ) ;
+
+ if ( iWidth.indexOf && iWidth.indexOf( '%' ) >= 0 )
+ {
+ iWidth = iWidth.substr( 0, iWidth.length - 1 ) ;
+ GetE('selWidthType').value = 'percent' ;
+ }
+
+ if ( oCell.attributes['noWrap'] != null && oCell.attributes['noWrap'].specified )
+ GetE('selWordWrap').value = !oCell.noWrap ;
+
+ GetE('txtWidth').value = iWidth ;
+ GetE('txtHeight').value = GetAttribute( oCell, 'height' ) ;
+ GetE('selHAlign').value = GetAttribute( oCell, 'align' ) ;
+ GetE('selVAlign').value = GetAttribute( oCell, 'vAlign' ) ;
+ GetE('txtRowSpan').value = GetAttribute( oCell, 'rowSpan' ) ;
+ GetE('txtCollSpan').value = GetAttribute( oCell, 'colSpan' ) ;
+ GetE('txtBackColor').value = GetAttribute( oCell, 'bgColor' ) ;
+ GetE('txtBorderColor').value = GetAttribute( oCell, 'borderColor' ) ;
+// GetE('cmbFontStyle').value = oCell.className ;
+ }
+}
+
+// Fired when the user press the OK button
+function Ok()
+{
+ for( i = 0 ; i < aCells.length ; i++ )
+ {
+ if ( GetE('txtWidth').value.length > 0 )
+ aCells[i].width = GetE('txtWidth').value + ( GetE('selWidthType').value == 'percent' ? '%' : '') ;
+ else
+ aCells[i].removeAttribute( 'width', 0 ) ;
+
+ if ( GetE('selWordWrap').value == 'false' )
+ aCells[i].noWrap = true ;
+ else
+ aCells[i].removeAttribute( 'noWrap' ) ;
+
+ SetAttribute( aCells[i], 'height' , GetE('txtHeight').value ) ;
+ SetAttribute( aCells[i], 'align' , GetE('selHAlign').value ) ;
+ SetAttribute( aCells[i], 'vAlign' , GetE('selVAlign').value ) ;
+ SetAttribute( aCells[i], 'rowSpan' , GetE('txtRowSpan').value ) ;
+ SetAttribute( aCells[i], 'colSpan' , GetE('txtCollSpan').value ) ;
+ SetAttribute( aCells[i], 'bgColor' , GetE('txtBackColor').value ) ;
+ SetAttribute( aCells[i], 'borderColor' , GetE('txtBorderColor').value ) ;
+// SetAttribute( aCells[i], 'className' , GetE('cmbFontStyle').value ) ;
+ }
+
+ return true ;
+}
+
+function SelectBackColor( color )
+{
+ if ( color && color.length > 0 )
+ GetE('txtBackColor').value = color ;
+}
+
+function SelectBorderColor( color )
+{
+ if ( color && color.length > 0 )
+ GetE('txtBorderColor').value = color ;
+}
+
+function SelectColor( wich )
+{
+ oEditor.FCKDialog.OpenDialog( 'FCKDialog_Color', oEditor.FCKLang.DlgColorTitle, 'dialog/fck_colorselector.html', 400, 330, wich == 'Back' ? SelectBackColor : SelectBorderColor, window ) ;
+}
+
+ </script>
+</head>
+<body scroll="no" style="overflow: hidden">
+ <table cellspacing="0" cellpadding="0" width="100%" border="0" height="100%">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="1" width="100%" border="0">
+ <tr>
+ <td>
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellWidth">Width</span>:</td>
+ <td>
+ &nbsp;<input onkeypress="return IsDigit(event);" id="txtWidth" type="text" maxlength="4"
+ size="3" name="txtWidth" />&nbsp;<select id="selWidthType" name="selWidthType">
+ <option fcklang="DlgCellWidthPx" value="pixels" selected="selected">pixels</option>
+ <option fcklang="DlgCellWidthPc" value="percent">percent</option>
+ </select></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellHeight">Height</span>:</td>
+ <td>
+ &nbsp;<input id="txtHeight" type="text" maxlength="4" size="3" name="txtHeight" onkeypress="return IsDigit(event);" />&nbsp;<span
+ fcklang="DlgCellWidthPx">pixels</span></td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;</td>
+ <td>
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellWordWrap">Word Wrap</span>:</td>
+ <td>
+ &nbsp;<select id="selWordWrap" name="selAlignment">
+ <option fcklang="DlgCellWordWrapYes" value="true" selected="selected">Yes</option>
+ <option fcklang="DlgCellWordWrapNo" value="false">No</option>
+ </select></td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;</td>
+ <td>
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellHorAlign">Horizontal Alignment</span>:</td>
+ <td>
+ &nbsp;<select id="selHAlign" name="selAlignment">
+ <option fcklang="DlgCellHorAlignNotSet" value="" selected>&lt;Not set&gt;</option>
+ <option fcklang="DlgCellHorAlignLeft" value="left">Left</option>
+ <option fcklang="DlgCellHorAlignCenter" value="center">Center</option>
+ <option fcklang="DlgCellHorAlignRight" value="right">Right</option>
+ </select></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellVerAlign">Vertical Alignment</span>:</td>
+ <td>
+ &nbsp;<select id="selVAlign" name="selAlignment">
+ <option fcklang="DlgCellVerAlignNotSet" value="" selected>&lt;Not set&gt;</option>
+ <option fcklang="DlgCellVerAlignTop" value="top">Top</option>
+ <option fcklang="DlgCellVerAlignMiddle" value="middle">Middle</option>
+ <option fcklang="DlgCellVerAlignBottom" value="bottom">Bottom</option>
+ <option fcklang="DlgCellVerAlignBaseline" value="baseline">Baseline</option>
+ </select></td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ &nbsp;&nbsp;&nbsp;</td>
+ <td align="right">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellRowSpan">Rows Span</span>:</td>
+ <td>
+ &nbsp;
+ <input onkeypress="return IsDigit(event);" id="txtRowSpan" type="text" maxlength="3" size="2"
+ name="txtRows"></td>
+ <td>
+ </td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellCollSpan">Columns Span</span>:</td>
+ <td>
+ &nbsp;
+ <input onkeypress="return IsDigit(event);" id="txtCollSpan" type="text" maxlength="2"
+ size="2" name="txtColumns"></td>
+ <td>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;</td>
+ <td>
+ &nbsp;</td>
+ <td>
+ &nbsp;</td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellBackColor">Background Color</span>:</td>
+ <td>
+ &nbsp;<input id="txtBackColor" type="text" size="8" name="txtCellSpacing"></td>
+ <td>
+ &nbsp;
+ <input type="button" fcklang="DlgCellBtnSelect" value="Select..." onclick="SelectColor( 'Back' )"></td>
+ </tr>
+ <tr>
+ <td nowrap="nowrap">
+ <span fcklang="DlgCellBorderColor">Border Color</span>:</td>
+ <td>
+ &nbsp;<input id="txtBorderColor" type="text" size="8" name="txtCellPadding" /></td>
+ <td>
+ &nbsp;
+ <input type="button" fcklang="DlgCellBtnSelect" value="Select..." onclick="SelectColor( 'Border' )" /></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_template.html b/httemplate/elements/fckeditor/editor/dialog/fck_template.html
new file mode 100644
index 0000000..418e9df
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_template.html
@@ -0,0 +1,242 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Template selection dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <style type="text/css">
+ .TplList
+ {
+ border: #dcdcdc 2px solid;
+ background-color: #ffffff;
+ overflow: auto;
+ width: 90%;
+ }
+
+ .TplItem
+ {
+ margin: 5px;
+ padding: 7px;
+ border: #eeeeee 1px solid;
+ }
+
+ .TplItem TABLE
+ {
+ display: inline;
+ }
+
+ .TplTitle
+ {
+ font-weight: bold;
+ }
+ </style>
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCK = oEditor.FCK ;
+var FCKLang = oEditor.FCKLang ;
+var FCKConfig = oEditor.FCKConfig ;
+
+window.onload = function()
+{
+ // Set the right box height (browser dependent).
+ GetE('eList').style.height = document.all ? '100%' : '295px' ;
+
+ // Translate the dialog box texts.
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ GetE('xChkReplaceAll').checked = ( FCKConfig.TemplateReplaceAll !== false ) ;
+
+ if ( FCKConfig.TemplateReplaceCheckbox !== false )
+ GetE('xReplaceBlock').style.display = '' ;
+
+ window.parent.SetAutoSize( true ) ;
+
+ LoadTemplatesXml() ;
+}
+
+function LoadTemplatesXml()
+{
+ var oTemplate ;
+
+ if ( !FCK._Templates )
+ {
+ GetE('eLoading').style.display = '' ;
+
+ // Create the Templates array.
+ FCK._Templates = new Array() ;
+
+ // Load the XML file.
+ var oXml = new oEditor.FCKXml() ;
+ oXml.LoadUrl( FCKConfig.TemplatesXmlPath ) ;
+
+ // Get the Images Base Path.
+ var oAtt = oXml.SelectSingleNode( 'Templates/@imagesBasePath' ) ;
+ var sImagesBasePath = oAtt ? oAtt.value : '' ;
+
+ // Get the "Template" nodes defined in the XML file.
+ var aTplNodes = oXml.SelectNodes( 'Templates/Template' ) ;
+
+ for ( var i = 0 ; i < aTplNodes.length ; i++ )
+ {
+ var oNode = aTplNodes[i] ;
+
+ oTemplate = new Object() ;
+
+ var oPart ;
+
+ // Get the Template Title.
+ if ( (oPart = oNode.attributes.getNamedItem('title')) )
+ oTemplate.Title = oPart.value ;
+ else
+ oTemplate.Title = 'Template ' + ( i + 1 ) ;
+
+ // Get the Template Description.
+ if ( (oPart = oXml.SelectSingleNode( 'Description', oNode )) )
+ oTemplate.Description = oPart.text ? oPart.text : oPart.textContent ;
+
+ // Get the Template Image.
+ if ( (oPart = oNode.attributes.getNamedItem('image')) )
+ oTemplate.Image = sImagesBasePath + oPart.value ;
+
+ // Get the Template HTML.
+ if ( (oPart = oXml.SelectSingleNode( 'Html', oNode )) )
+ oTemplate.Html = oPart.text ? oPart.text : oPart.textContent ;
+ else
+ {
+ alert( 'No HTML defined for template index ' + i + '. Please review the "' + FCKConfig.TemplatesXmlPath + '" file.' ) ;
+ continue ;
+ }
+
+ FCK._Templates[ FCK._Templates.length ] = oTemplate ;
+ }
+
+ GetE('eLoading').style.display = 'none' ;
+ }
+
+ if ( FCK._Templates.length == 0 )
+ GetE('eEmpty').style.display = '' ;
+ else
+ {
+ for ( var j = 0 ; j < FCK._Templates.length ; j++ )
+ {
+ oTemplate = FCK._Templates[j] ;
+
+ var oItemDiv = GetE('eList').appendChild( document.createElement( 'DIV' ) ) ;
+ oItemDiv.TplIndex = j ;
+ oItemDiv.className = 'TplItem' ;
+
+ // Build the inner HTML of our new item DIV.
+ var sInner = '<table><tr>' ;
+
+ if ( oTemplate.Image )
+ sInner += '<td valign="top"><img src="' + oTemplate.Image + '"><\/td>' ;
+
+ sInner += '<td valign="top"><div class="TplTitle">' + oTemplate.Title + '<\/div>' ;
+
+ if ( oTemplate.Description )
+ sInner += '<div>' + oTemplate.Description + '<\/div>' ;
+
+ sInner += '<\/td><\/tr><\/table>' ;
+
+ oItemDiv.innerHTML = sInner ;
+
+ oItemDiv.onmouseover = ItemDiv_OnMouseOver ;
+ oItemDiv.onmouseout = ItemDiv_OnMouseOut ;
+ oItemDiv.onclick = ItemDiv_OnClick ;
+ }
+ }
+}
+
+function ItemDiv_OnMouseOver()
+{
+ this.className += ' PopupSelectionBox' ;
+}
+
+function ItemDiv_OnMouseOut()
+{
+ this.className = this.className.replace( /\s*PopupSelectionBox\s*/, '' ) ;
+}
+
+function ItemDiv_OnClick()
+{
+ SelectTemplate( this.TplIndex ) ;
+}
+
+function SelectTemplate( index )
+{
+ oEditor.FCKUndo.SaveUndoStep() ;
+
+ if ( GetE('xChkReplaceAll').checked )
+ FCK.SetHTML( FCK._Templates[index].Html ) ;
+ else
+ FCK.InsertHtml( FCK._Templates[index].Html ) ;
+
+ window.parent.Cancel( true ) ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <table width="100%" style="height: 100%">
+ <tr>
+ <td align="center">
+ <span fcklang="DlgTemplatesSelMsg">Please select the template to open in the editor<br />
+ (the actual contents will be lost):</span>
+ </td>
+ </tr>
+ <tr>
+ <td height="100%" align="center">
+ <div id="eList" align="left" class="TplList">
+ <div id="eLoading" align="center" style="display: none">
+ <br />
+ <span fcklang="DlgTemplatesLoading">Loading templates list. Please wait...</span>
+ </div>
+ <div id="eEmpty" align="center" style="display: none">
+ <br />
+ <span fcklang="DlgTemplatesNoTpl">(No templates defined)</span>
+ </div>
+ </div>
+ </td>
+ </tr>
+ <tr id="xReplaceBlock" style="display: none">
+ <td>
+ <table cellpadding="0" cellspacing="0">
+ <tr>
+ <td>
+ <input id="xChkReplaceAll" type="checkbox" /></td>
+ <td>
+ &nbsp;</td>
+ <td>
+ <label for="xChkReplaceAll" fcklang="DlgTemplatesReplace">
+ Replace actual contents</label></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template1.gif b/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template1.gif
new file mode 100644
index 0000000..efdabbe
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template1.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template2.gif b/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template2.gif
new file mode 100644
index 0000000..d1cebb3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template2.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template3.gif b/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template3.gif
new file mode 100644
index 0000000..db41cb4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_template/images/template3.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_textarea.html b/httemplate/elements/fckeditor/editor/dialog/fck_textarea.html
new file mode 100644
index 0000000..b7de33a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_textarea.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Text Area dialog window.
+-->
+<html>
+ <head>
+ <title>Text Area Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oActiveEl = oEditor.FCKSelection.GetSelectedElement() ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oActiveEl && oActiveEl.tagName == 'TEXTAREA' )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtCols').value = GetAttribute( oActiveEl, 'cols' ) ;
+ GetE('txtRows').value = GetAttribute( oActiveEl, 'rows' ) ;
+ }
+ else
+ oActiveEl = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ if ( !oActiveEl )
+ {
+ oActiveEl = oEditor.FCK.EditorDocument.createElement( 'TEXTAREA' ) ;
+ oActiveEl = oEditor.FCK.InsertElementAndGetIt( oActiveEl ) ;
+ }
+
+ oActiveEl.name = GetE('txtName').value ;
+ SetAttribute( oActiveEl, 'cols', GetE('txtCols').value ) ;
+ SetAttribute( oActiveEl, 'rows', GetE('txtRows').value ) ;
+
+ return true ;
+}
+
+ </script>
+ </head>
+ <body style='OVERFLOW: hidden' scroll='no'>
+ <table height="100%" width="100%">
+ <tr>
+ <td align="center">
+ <table border="0" cellpadding="0" cellspacing="0" width="80%">
+ <tr>
+ <td>
+ <span fckLang="DlgTextareaName">Name</span><br>
+ <input type="text" id="txtName" style="WIDTH: 100%">
+ <span fckLang="DlgTextareaCols">Collumns</span><br>
+ <input id="txtCols" type="text" size="5">
+ <br>
+ <span fckLang="DlgTextareaRows">Rows</span><br>
+ <input id="txtRows" type="text" size="5">
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/dialog/fck_textfield.html b/httemplate/elements/fckeditor/editor/dialog/fck_textfield.html
new file mode 100644
index 0000000..7b4c8ef
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/dialog/fck_textfield.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Text field dialog window.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta content="noindex, nofollow" name="robots" />
+ <script src="common/fck_dialog_common.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+
+// Gets the document DOM
+var oDOM = oEditor.FCK.EditorDocument ;
+
+var oActiveEl = oEditor.FCKSelection.GetSelectedElement() ;
+
+window.onload = function()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage(document) ;
+
+ if ( oActiveEl && oActiveEl.tagName == 'INPUT' && ( oActiveEl.type == 'text' || oActiveEl.type == 'password' ) )
+ {
+ GetE('txtName').value = oActiveEl.name ;
+ GetE('txtValue').value = oActiveEl.value ;
+ GetE('txtSize').value = GetAttribute( oActiveEl, 'size' ) ;
+ GetE('txtMax').value = GetAttribute( oActiveEl, 'maxLength' ) ;
+ GetE('txtType').value = oActiveEl.type ;
+
+ GetE('txtType').disabled = true ;
+ }
+ else
+ oActiveEl = null ;
+
+ window.parent.SetOkButton( true ) ;
+}
+
+function Ok()
+{
+ if ( isNaN( GetE('txtMax').value ) || GetE('txtMax').value < 0 )
+ {
+ alert( "Maximum characters must be a positive number." ) ;
+ GetE('txtMax').focus() ;
+ return false ;
+ }
+ else if( isNaN( GetE('txtSize').value ) || GetE('txtSize').value < 0 )
+ {
+ alert( "Width must be a positive number." ) ;
+ GetE('txtSize').focus() ;
+ return false ;
+ }
+
+ if ( !oActiveEl )
+ {
+ oActiveEl = oEditor.FCK.EditorDocument.createElement( 'INPUT' ) ;
+ oActiveEl.type = GetE('txtType').value ;
+ oActiveEl = oEditor.FCK.InsertElementAndGetIt( oActiveEl ) ;
+ }
+
+ oActiveEl.name = GetE('txtName').value ;
+ SetAttribute( oActiveEl, 'value' , GetE('txtValue').value ) ;
+ SetAttribute( oActiveEl, 'size' , GetE('txtSize').value ) ;
+ SetAttribute( oActiveEl, 'maxlength', GetE('txtMax').value ) ;
+
+ return true ;
+}
+
+ </script>
+</head>
+<body style="overflow: hidden">
+ <table width="100%" style="height: 100%">
+ <tr>
+ <td align="center">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td>
+ <span fcklang="DlgTextName">Name</span><br />
+ <input id="txtName" type="text" size="20" />
+ </td>
+ <td>
+ </td>
+ <td>
+ <span fcklang="DlgTextValue">Value</span><br />
+ <input id="txtValue" type="text" size="25" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgTextCharWidth">Character Width</span><br />
+ <input id="txtSize" type="text" size="5" />
+ </td>
+ <td>
+ </td>
+ <td>
+ <span fcklang="DlgTextMaxChars">Maximum Characters</span><br />
+ <input id="txtMax" type="text" size="5" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span fcklang="DlgTextType">Type</span><br />
+ <select id="txtType">
+ <option value="text" selected="selected" fcklang="DlgTextTypeText">Text</option>
+ <option value="password" fcklang="DlgTextTypePass">Password</option>
+ </select>
+ </td>
+ <td>
+ &nbsp;</td>
+ <td>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/fckdebug.html b/httemplate/elements/fckeditor/editor/fckdebug.html
new file mode 100644
index 0000000..db99d60
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/fckdebug.html
@@ -0,0 +1,153 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This is the Debug window.
+ * It automatically popups if the Debug = true in the configuration file.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>FCKeditor Debug Window</title>
+ <meta name="robots" content="noindex, nofollow" />
+ <script type="text/javascript">
+
+var oWindow ;
+var oDiv ;
+
+if ( !window.FCKMessages )
+ window.FCKMessages = new Array() ;
+
+window.onload = function()
+{
+ oWindow = document.getElementById('xOutput').contentWindow ;
+ oWindow.document.open() ;
+ oWindow.document.write( '<div id="divMsg"><\/div>' ) ;
+ oWindow.document.close() ;
+ oDiv = oWindow.document.getElementById('divMsg') ;
+}
+
+function Output( message, color, noParse )
+{
+ if ( !noParse && message != null && isNaN( message ) )
+ message = message.replace(/</g, "&lt;") ;
+
+ if ( color )
+ message = '<font color="' + color + '">' + message + '<\/font>' ;
+
+ window.FCKMessages[ window.FCKMessages.length ] = message ;
+ StartTimer() ;
+}
+
+function OutputObject( anyObject, color )
+{
+ var message ;
+
+ if ( anyObject != null )
+ {
+ message = 'Properties of: ' + anyObject + '</b><blockquote>' ;
+
+ for (var prop in anyObject)
+ {
+ try
+ {
+ var sVal = anyObject[ prop ] != null ? anyObject[ prop ] + '' : '[null]' ;
+ message += '<b>' + prop + '</b> : ' + sVal.replace(/</g, '&lt;') + '<br>' ;
+ }
+ catch (e)
+ {
+ try
+ {
+ message += '<b>' + prop + '</b> : [' + typeof( anyObject[ prop ] ) + ']<br>' ;
+ }
+ catch (e)
+ {
+ message += '<b>' + prop + '</b> : [-error-]<br>' ;
+ }
+ }
+ }
+
+ message += '</blockquote><b>' ;
+ } else
+ message = 'OutputObject : Object is "null".' ;
+
+ Output( message, color, true ) ;
+}
+
+function StartTimer()
+{
+ window.setTimeout( 'CheckMessages()', 100 ) ;
+}
+
+function CheckMessages()
+{
+ if ( window.FCKMessages.length > 0 )
+ {
+ // Get the first item in the queue
+ var sMessage = window.FCKMessages[0] ;
+
+ // Removes the first item from the queue
+ var oTempArray = new Array() ;
+ for ( i = 1 ; i < window.FCKMessages.length ; i++ )
+ oTempArray[ i - 1 ] = window.FCKMessages[ i ] ;
+ window.FCKMessages = oTempArray ;
+
+ var d = new Date() ;
+ var sTime =
+ ( d.getHours() + 100 + '' ).substr( 1,2 ) + ':' +
+ ( d.getMinutes() + 100 + '' ).substr( 1,2 ) + ':' +
+ ( d.getSeconds() + 100 + '' ).substr( 1,2 ) + ':' +
+ ( d.getMilliseconds() + 1000 + '' ).substr( 1,3 ) ;
+
+ var oMsgDiv = oWindow.document.createElement( 'div' ) ;
+ oMsgDiv.innerHTML = sTime + ': <b>' + sMessage + '<\/b>' ;
+ oDiv.appendChild( oMsgDiv ) ;
+ oMsgDiv.scrollIntoView() ;
+ }
+}
+
+function Clear()
+{
+ oDiv.innerHTML = '' ;
+}
+ </script>
+</head>
+<body style="margin: 10px">
+ <table style="height: 100%" cellspacing="5" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td>
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td style="font-weight: bold; font-size: 1.2em;">
+ FCKeditor Debug Window</td>
+ <td align="right">
+ <input type="button" value="Clear" onclick="Clear();" /></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr style="height: 100%">
+ <td style="border: #696969 1px solid">
+ <iframe id="xOutput" width="100%" height="100%" scrolling="auto" src="javascript:void(0)"
+ frameborder="0"></iframe>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/fckdialog.html b/httemplate/elements/fckeditor/editor/fckdialog.html
new file mode 100644
index 0000000..7f26822
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/fckdialog.html
@@ -0,0 +1,324 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This page is used by all dialog box as the container.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="noindex, nofollow" />
+ <script type="text/javascript">
+
+// On some Gecko browsers (probably over slow connections) the
+// "dialogArguments" are not set so we must get it from the opener window.
+if ( !window.dialogArguments )
+ window.dialogArguments = window.opener.FCKLastDialogInfo ;
+
+// Sets the Skin CSS
+document.write( '<link href="' + window.dialogArguments.Editor.FCKConfig.SkinPath + 'fck_dialog.css" type="text/css" rel="stylesheet">' ) ;
+
+// Sets the language direction.
+window.document.dir = window.dialogArguments.Editor.FCKLang.Dir ;
+
+var sTitle = window.dialogArguments.Title ;
+document.write( '<title>' + sTitle + '<\/title>' ) ;
+
+function LoadInnerDialog()
+{
+ if ( window.onresize )
+ window.onresize() ;
+
+ // First of all, translate the dialog box contents.
+ window.dialogArguments.Editor.FCKLanguageManager.TranslatePage( document ) ;
+
+ window.frames["frmMain"].document.location.href = window.dialogArguments.Page ;
+}
+
+function InnerDialogLoaded()
+{
+ var oInnerDoc = document.getElementById('frmMain').contentWindow.document ;
+
+ // Set the language direction.
+ oInnerDoc.dir = window.dialogArguments.Editor.FCKLang.Dir ;
+
+ // Sets the Skin CSS.
+ oInnerDoc.write( '<link href="' + window.dialogArguments.Editor.FCKConfig.SkinPath + 'fck_dialog.css" type="text/css" rel="stylesheet">' ) ;
+
+ SetOnKeyDown( oInnerDoc ) ;
+ DisableContextMenu( oInnerDoc ) ;
+
+ return window.dialogArguments.Editor ;
+}
+
+function SetOkButton( showIt )
+{
+ document.getElementById('btnOk').style.visibility = ( showIt ? '' : 'hidden' ) ;
+}
+
+var bAutoSize = false ;
+
+function SetAutoSize( autoSize )
+{
+ bAutoSize = autoSize ;
+ RefreshSize() ;
+}
+
+function RefreshSize()
+{
+ if ( bAutoSize )
+ {
+ var oInnerDoc = document.getElementById('frmMain').contentWindow.document ;
+
+ var iFrameHeight ;
+ if ( document.all )
+ iFrameHeight = oInnerDoc.body.offsetHeight ;
+ else
+ iFrameHeight = document.getElementById('frmMain').contentWindow.innerHeight ;
+
+ var iInnerHeight = oInnerDoc.body.scrollHeight ;
+
+ var iDiff = iInnerHeight - iFrameHeight ;
+
+ if ( iDiff > 0 )
+ {
+ if ( document.all )
+ window.dialogHeight = ( parseInt( window.dialogHeight, 10 ) + iDiff ) + 'px' ;
+ else
+ window.resizeBy( 0, iDiff ) ;
+ }
+ }
+}
+
+function Ok()
+{
+ if ( window.frames["frmMain"].Ok && window.frames["frmMain"].Ok() )
+ Cancel() ;
+}
+
+function Cancel( dontFireChange )
+{
+ if ( !dontFireChange )
+ {
+ // All dialog windows, by default, will fire the "OnSelectionChange"
+ // event, no matter the Ok or Cancel button has been pressed.
+ window.dialogArguments.Editor.FCK.Events.FireEvent( 'OnSelectionChange' ) ;
+ }
+ window.close() ;
+}
+
+// Object that holds all available tabs.
+var oTabs = new Object() ;
+
+function TabDiv_OnClick()
+{
+ SetSelectedTab( this.TabCode ) ;
+}
+
+function AddTab( tabCode, tabText, startHidden )
+{
+ if ( typeof( oTabs[ tabCode ] ) != 'undefined' )
+ return ;
+
+ var eTabsRow = document.getElementById( 'Tabs' ) ;
+
+ var oCell = eTabsRow.insertCell( eTabsRow.cells.length - 1 ) ;
+ oCell.noWrap = true ;
+
+ var oDiv = document.createElement( 'DIV' ) ;
+ oDiv.className = 'PopupTab' ;
+ oDiv.innerHTML = tabText ;
+ oDiv.TabCode = tabCode ;
+ oDiv.onclick = TabDiv_OnClick ;
+
+ if ( startHidden )
+ oDiv.style.display = 'none' ;
+
+ eTabsRow = document.getElementById( 'TabsRow' ) ;
+
+ oCell.appendChild( oDiv ) ;
+
+ if ( eTabsRow.style.display == 'none' )
+ {
+ var eTitleArea = document.getElementById( 'TitleArea' ) ;
+ eTitleArea.className = 'PopupTitle' ;
+
+ oDiv.className = 'PopupTabSelected' ;
+ eTabsRow.style.display = '' ;
+
+ if ( ! window.dialogArguments.Editor.FCKBrowserInfo.IsIE )
+ window.onresize() ;
+ }
+
+ oTabs[ tabCode ] = oDiv ;
+}
+
+function SetSelectedTab( tabCode )
+{
+ for ( var sCode in oTabs )
+ {
+ if ( sCode == tabCode )
+ oTabs[sCode].className = 'PopupTabSelected' ;
+ else
+ oTabs[sCode].className = 'PopupTab' ;
+ }
+
+ if ( typeof( window.frames["frmMain"].OnDialogTabChange ) == 'function' )
+ window.frames["frmMain"].OnDialogTabChange( tabCode ) ;
+}
+
+function SetTabVisibility( tabCode, isVisible )
+{
+ var oTab = oTabs[ tabCode ] ;
+ oTab.style.display = isVisible ? '' : 'none' ;
+
+ if ( ! isVisible && oTab.className == 'PopupTabSelected' )
+ {
+ for ( var sCode in oTabs )
+ {
+ if ( oTabs[sCode].style.display != 'none' )
+ {
+ SetSelectedTab( sCode ) ;
+ break ;
+ }
+ }
+ }
+}
+
+function SetOnKeyDown( targetDocument )
+{
+ targetDocument.onkeydown = function ( e )
+ {
+ e = e || event || this.parentWindow.event ;
+ switch ( e.keyCode )
+ {
+ case 13 : // ENTER
+ var oTarget = e.srcElement || e.target ;
+ if ( oTarget.tagName == 'TEXTAREA' )
+ return true ;
+ Ok() ;
+ return false ;
+ case 27 : // ESC
+ Cancel() ;
+ return false ;
+ break ;
+ }
+ return true ;
+ }
+}
+SetOnKeyDown( document ) ;
+
+function DisableContextMenu( targetDocument )
+{
+ if ( window.dialogArguments.Editor.FCKBrowserInfo.IsIE ) return ;
+
+ // Disable Right-Click
+ var oOnContextMenu = function( e )
+ {
+ var sTagName = e.target.tagName ;
+ if ( ! ( ( sTagName == "INPUT" && e.target.type == "text" ) || sTagName == "TEXTAREA" ) )
+ e.preventDefault() ;
+ }
+ targetDocument.addEventListener( 'contextmenu', oOnContextMenu, true ) ;
+}
+DisableContextMenu( document ) ;
+
+if ( ! window.dialogArguments.Editor.FCKBrowserInfo.IsIE )
+{
+ window.onresize = function()
+ {
+ var oFrame = document.getElementById("frmMain") ;
+
+ if ( ! oFrame )
+ return ;
+
+ oFrame.height = 0 ;
+
+ var oCell = document.getElementById("FrameCell") ;
+ var iHeight = oCell.offsetHeight ;
+
+ oFrame.height = iHeight - 2 ;
+ }
+}
+
+if ( window.dialogArguments.Editor.FCKBrowserInfo.IsIE )
+{
+ function Window_OnBeforeUnload()
+ {
+ for ( var t in oTabs )
+ oTabs[t] = null ;
+
+ window.dialogArguments.Editor = null ;
+ }
+ window.attachEvent( "onbeforeunload", Window_OnBeforeUnload ) ;
+}
+
+function Window_OnClose()
+{
+ window.dialogArguments.Editor.FCKFocusManager.Unlock() ;
+}
+
+if ( window.addEventListener )
+ window.addEventListener( 'unload', Window_OnClose, false ) ;
+
+ </script>
+ </head>
+ <body onload="LoadInnerDialog();" class="PopupBody">
+ <table height="100%" cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td id="TitleArea" class="PopupTitle PopupTitleBorder">
+ <script type="text/javascript">
+document.write( sTitle ) ;
+ </script>
+ </td>
+ </tr>
+ <tr id="TabsRow" style="DISPLAY: none">
+ <td class="PopupTabArea">
+ <table border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr id="Tabs" onselectstart="return false;">
+ <td class="PopupTabEmptyArea">&nbsp;</td>
+ <td class="PopupTabEmptyArea" width="100%">&nbsp;</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td id="FrameCell" height="100%" valign="top">
+ <iframe id="frmMain" src="javascript:void(0)" name="frmMain" frameborder="0" height="100%" width="100%" scrolling="auto">
+ </iframe>
+ </td>
+ </tr>
+ <tr>
+ <td class="PopupButtons">
+ <table border="0" cellpadding="0" cellspacing="0">
+ <tr>
+ <td width="100%">&nbsp;</td>
+ <td nowrap="nowrap">
+ <input id="btnOk" style="VISIBILITY: hidden;" type="button" value="Ok" class="Button" onclick="Ok();" fckLang="DlgBtnOK" />
+ &nbsp;
+ <input id="btnCancel" type="button" value="Cancel" class="Button" onclick="Cancel();" fckLang="DlgBtnCancel" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html> \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/fckeditor.html b/httemplate/elements/fckeditor/editor/fckeditor.html
new file mode 100644
index 0000000..25ad37e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/fckeditor.html
@@ -0,0 +1,227 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Main page that holds the editor.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>FCKeditor</title>
+ <meta name="robots" content="noindex, nofollow" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Cache-Control" content="public" />
+ <script type="text/javascript">
+
+// Instead of loading scripts and CSSs using inline tags, all scripts are
+// loaded by code. In this way we can guarantee the correct processing order,
+// otherwise external scripts and inline scripts could be executed in an
+// unwanted order (IE).
+
+function LoadScript( url )
+{
+ document.write( '<scr' + 'ipt type="text/javascript" src="' + url + '" onerror="alert(\'Error loading \' + this.src);"><\/scr' + 'ipt>' ) ;
+}
+
+function LoadCss( url )
+{
+ document.write( '<link href="' + url + '" type="text/css" rel="stylesheet" onerror="alert(\'Error loading \' + this.src);" />' ) ;
+}
+
+// Main editor scripts.
+var sSuffix = /msie/.test( navigator.userAgent.toLowerCase() ) ? 'ie' : 'gecko' ;
+
+LoadScript( 'js/fckeditorcode_' + sSuffix + '.js' ) ;
+
+// Base configuration file.
+LoadScript( '../fckconfig.js' ) ;
+
+ </script>
+ <script type="text/javascript">
+
+if ( FCKBrowserInfo.IsIE )
+{
+ // Remove IE mouse flickering.
+ try
+ {
+ document.execCommand( 'BackgroundImageCache', false, true ) ;
+ }
+ catch (e)
+ {
+ // We have been reported about loading problems caused by the above
+ // line. For safety, let's just ignore errors.
+ }
+
+ // Create the default cleanup object used by the editor.
+ FCK.IECleanup = new FCKIECleanup( window ) ;
+ FCK.IECleanup.AddItem( FCKTempBin, FCKTempBin.Reset ) ;
+ FCK.IECleanup.AddItem( FCK, FCK_Cleanup ) ;
+}
+
+// The config hidden field is processed immediately, because
+// CustomConfigurationsPath may be set in the page.
+FCKConfig.ProcessHiddenField() ;
+
+// Load the custom configurations file (if defined).
+if ( FCKConfig.CustomConfigurationsPath.length > 0 )
+ LoadScript( FCKConfig.CustomConfigurationsPath ) ;
+
+ </script>
+ <script type="text/javascript">
+
+// Load configurations defined at page level.
+FCKConfig_LoadPageConfig() ;
+
+FCKConfig_PreProcess() ;
+
+// Load the active skin CSS.
+LoadCss( FCKConfig.SkinPath + 'fck_editor.css' ) ;
+
+// Load the language file.
+FCKLanguageManager.Initialize() ;
+LoadScript( 'lang/' + FCKLanguageManager.ActiveLanguage.Code + '.js' ) ;
+
+ </script>
+ <script type="text/javascript">
+
+// Initialize the editing area context menu.
+FCK_ContextMenu_Init() ;
+
+FCKPlugins.Load() ;
+
+ </script>
+ <script type="text/javascript">
+
+// Set the editor interface direction.
+window.document.dir = FCKLang.Dir ;
+
+// Activate pasting operations.
+if ( FCKConfig.ForcePasteAsPlainText || FCKConfig.AutoDetectPasteFromWord )
+ FCK.Events.AttachEvent( 'OnPaste', FCK.Paste ) ;
+
+ </script>
+ <script type="text/javascript">
+
+window.onload = function()
+{
+ InitializeAPI() ;
+
+ if ( FCKBrowserInfo.IsIE )
+ FCK_PreloadImages() ;
+ else
+ LoadToolbarSetup() ;
+}
+
+function LoadToolbarSetup()
+{
+ FCKeditorAPI._FunctionQueue.Add( LoadToolbar ) ;
+}
+
+function LoadToolbar()
+{
+ var oToolbarSet = FCK.ToolbarSet = FCKToolbarSet_Create() ;
+
+ if ( oToolbarSet.IsLoaded )
+ StartEditor() ;
+ else
+ {
+ oToolbarSet.OnLoad = StartEditor ;
+ oToolbarSet.Load( FCKURLParams['Toolbar'] || 'Default' ) ;
+ }
+}
+
+function StartEditor()
+{
+ // Remove the onload listener.
+ FCK.ToolbarSet.OnLoad = null ;
+
+ FCKeditorAPI._FunctionQueue.Remove( LoadToolbar ) ;
+
+ FCK.Events.AttachEvent( 'OnStatusChange', WaitForActive ) ;
+
+ // Start the editor.
+ FCK.StartEditor() ;
+}
+
+function WaitForActive( editorInstance, newStatus )
+{
+ if ( newStatus == FCK_STATUS_ACTIVE )
+ {
+ if ( FCKBrowserInfo.IsGecko )
+ FCKTools.RunFunction( window.onresize ) ;
+
+ _AttachFormSubmitToAPI() ;
+
+ FCK.SetStatus( FCK_STATUS_COMPLETE ) ;
+
+ // Call the special "FCKeditor_OnComplete" function that should be present in
+ // the HTML page where the editor is located.
+ if ( typeof( window.parent.FCKeditor_OnComplete ) == 'function' )
+ window.parent.FCKeditor_OnComplete( FCK ) ;
+ }
+}
+
+// Gecko browsers doens't calculate well that IFRAME size so we must
+// recalculate it every time the window size changes.
+if ( FCKBrowserInfo.IsGecko )
+{
+ function Window_OnResize()
+ {
+ if ( FCKBrowserInfo.IsOpera )
+ return ;
+
+ var oCell = document.getElementById( 'xEditingArea' ) ;
+
+ var eInnerElement = oCell.firstChild ;
+ if ( eInnerElement )
+ {
+ eInnerElement.style.height = 0 ;
+ eInnerElement.style.height = oCell.scrollHeight - 2 ;
+ }
+ }
+ window.onresize = Window_OnResize ;
+}
+
+ </script>
+</head>
+<body>
+ <table width="100%" cellpadding="0" cellspacing="0" style="height: 100%; table-layout: fixed">
+ <tr id="xToolbarRow" style="display: none">
+ <td id="xToolbarSpace" style="overflow: hidden">
+ <table width="100%" cellpadding="0" cellspacing="0">
+ <tr id="xCollapsed" style="display: none">
+ <td id="xExpandHandle" class="TB_Expand" colspan="3">
+ <img class="TB_ExpandImg" alt="" src="images/spacer.gif" width="8" height="4" /></td>
+ </tr>
+ <tr id="xExpanded" style="display: none">
+ <td id="xTBLeftBorder" class="TB_SideBorder" style="width: 1px; display: none;"></td>
+ <td id="xCollapseHandle" style="display: none" class="TB_Collapse" valign="bottom">
+ <img class="TB_CollapseImg" alt="" src="images/spacer.gif" width="8" height="4" /></td>
+ <td id="xToolbar" class="TB_ToolbarSet"></td>
+ <td class="TB_SideBorder" style="width: 1px"></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td id="xEditingArea" valign="top" style="height: 100%"></td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/fckeditor.original.html b/httemplate/elements/fckeditor/editor/fckeditor.original.html
new file mode 100644
index 0000000..846eed9
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/fckeditor.original.html
@@ -0,0 +1,319 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Main page that holds the editor.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>FCKeditor</title>
+ <meta name="robots" content="noindex, nofollow" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <!-- @Packager.RemoveLine
+ <meta http-equiv="Cache-Control" content="public" />
+ @Packager.RemoveLine -->
+ <script type="text/javascript">
+
+// Instead of loading scripts and CSSs using inline tags, all scripts are
+// loaded by code. In this way we can guarantee the correct processing order,
+// otherwise external scripts and inline scripts could be executed in an
+// unwanted order (IE).
+
+function LoadScript( url )
+{
+ document.write( '<scr' + 'ipt type="text/javascript" src="' + url + '" onerror="alert(\'Error loading \' + this.src);"><\/scr' + 'ipt>' ) ;
+}
+
+function LoadCss( url )
+{
+ document.write( '<link href="' + url + '" type="text/css" rel="stylesheet" onerror="alert(\'Error loading \' + this.src);" />' ) ;
+}
+
+// Main editor scripts.
+var sSuffix = /msie/.test( navigator.userAgent.toLowerCase() ) ? 'ie' : 'gecko' ;
+
+/* @Packager.RemoveLine
+LoadScript( 'js/fckeditorcode_' + sSuffix + '.js' ) ;
+@Packager.RemoveLine */
+// @Packager.Remove.Start
+
+LoadScript( '_source/fckconstants.js' ) ;
+LoadScript( '_source/fckjscoreextensions.js' ) ;
+
+if ( sSuffix == 'ie' )
+ LoadScript( '_source/classes/fckiecleanup.js' ) ;
+
+LoadScript( '_source/internals/fckbrowserinfo.js' ) ;
+LoadScript( '_source/internals/fckurlparams.js' ) ;
+LoadScript( '_source/classes/fckevents.js' ) ;
+LoadScript( '_source/internals/fck.js' ) ;
+LoadScript( '_source/internals/fck_' + sSuffix + '.js' ) ;
+LoadScript( '_source/internals/fckconfig.js' ) ;
+
+LoadScript( '_source/internals/fckdebug.js' ) ;
+LoadScript( '_source/internals/fckdomtools.js' ) ;
+LoadScript( '_source/internals/fcktools.js' ) ;
+LoadScript( '_source/internals/fcktools_' + sSuffix + '.js' ) ;
+LoadScript( '_source/fckeditorapi.js' ) ;
+LoadScript( '_source/classes/fckimagepreloader.js' ) ;
+LoadScript( '_source/internals/fckregexlib.js' ) ;
+LoadScript( '_source/internals/fcklistslib.js' ) ;
+LoadScript( '_source/internals/fcklanguagemanager.js' ) ;
+LoadScript( '_source/internals/fckxhtmlentities.js' ) ;
+LoadScript( '_source/internals/fckxhtml.js' ) ;
+LoadScript( '_source/internals/fckxhtml_' + sSuffix + '.js' ) ;
+LoadScript( '_source/internals/fckcodeformatter.js' ) ;
+LoadScript( '_source/internals/fckundo_' + sSuffix + '.js' ) ;
+LoadScript( '_source/classes/fckeditingarea.js' ) ;
+LoadScript( '_source/classes/fckkeystrokehandler.js' ) ;
+
+LoadScript( '_source/internals/fcklisthandler.js' ) ;
+LoadScript( '_source/classes/fckelementpath.js' ) ;
+LoadScript( '_source/classes/fckdomrange.js' ) ;
+LoadScript( '_source/classes/fckdocumentfragment_' + sSuffix + '.js' ) ;
+LoadScript( '_source/classes/fckw3crange.js' ) ;
+LoadScript( '_source/classes/fckdomrange_' + sSuffix + '.js' ) ;
+LoadScript( '_source/classes/fckenterkey.js' ) ;
+
+LoadScript( '_source/internals/fckdocumentprocessor.js' ) ;
+LoadScript( '_source/internals/fckselection.js' ) ;
+LoadScript( '_source/internals/fckselection_' + sSuffix + '.js' ) ;
+
+LoadScript( '_source/internals/fcktablehandler.js' ) ;
+LoadScript( '_source/internals/fcktablehandler_' + sSuffix + '.js' ) ;
+LoadScript( '_source/classes/fckxml_' + sSuffix + '.js' ) ;
+LoadScript( '_source/classes/fckstyledef.js' ) ;
+LoadScript( '_source/classes/fckstyledef_' + sSuffix + '.js' ) ;
+LoadScript( '_source/classes/fckstylesloader.js' ) ;
+
+LoadScript( '_source/commandclasses/fcknamedcommand.js' ) ;
+LoadScript( '_source/commandclasses/fck_othercommands.js' ) ;
+LoadScript( '_source/commandclasses/fckspellcheckcommand_' + sSuffix + '.js' ) ;
+LoadScript( '_source/commandclasses/fcktextcolorcommand.js' ) ;
+LoadScript( '_source/commandclasses/fckpasteplaintextcommand.js' ) ;
+LoadScript( '_source/commandclasses/fckpastewordcommand.js' ) ;
+LoadScript( '_source/commandclasses/fcktablecommand.js' ) ;
+LoadScript( '_source/commandclasses/fckstylecommand.js' ) ;
+LoadScript( '_source/commandclasses/fckfitwindow.js' ) ;
+LoadScript( '_source/internals/fckcommands.js' ) ;
+
+LoadScript( '_source/classes/fckpanel.js' ) ;
+LoadScript( '_source/classes/fckicon.js' ) ;
+LoadScript( '_source/classes/fcktoolbarbuttonui.js' ) ;
+LoadScript( '_source/classes/fcktoolbarbutton.js' ) ;
+LoadScript( '_source/classes/fckspecialcombo.js' ) ;
+LoadScript( '_source/classes/fcktoolbarspecialcombo.js' ) ;
+LoadScript( '_source/classes/fcktoolbarfontscombo.js' ) ;
+LoadScript( '_source/classes/fcktoolbarfontsizecombo.js' ) ;
+LoadScript( '_source/classes/fcktoolbarfontformatcombo.js' ) ;
+LoadScript( '_source/classes/fcktoolbarstylecombo.js' ) ;
+LoadScript( '_source/classes/fcktoolbarpanelbutton.js' ) ;
+LoadScript( '_source/internals/fcktoolbaritems.js' ) ;
+LoadScript( '_source/classes/fcktoolbar.js' ) ;
+LoadScript( '_source/classes/fcktoolbarbreak_' + sSuffix + '.js' ) ;
+LoadScript( '_source/internals/fcktoolbarset.js' ) ;
+LoadScript( '_source/internals/fckdialog.js' ) ;
+LoadScript( '_source/internals/fckdialog_' + sSuffix + '.js' ) ;
+LoadScript( '_source/classes/fckmenuitem.js' ) ;
+LoadScript( '_source/classes/fckmenublock.js' ) ;
+LoadScript( '_source/classes/fckmenublockpanel.js' ) ;
+LoadScript( '_source/classes/fckcontextmenu.js' ) ;
+LoadScript( '_source/internals/fck_contextmenu.js' ) ;
+LoadScript( '_source/classes/fckplugin.js' ) ;
+LoadScript( '_source/internals/fckplugins.js' ) ;
+
+// @Packager.Remove.End
+
+// Base configuration file.
+LoadScript( '../fckconfig.js' ) ;
+
+ </script>
+ <script type="text/javascript">
+
+if ( FCKBrowserInfo.IsIE )
+{
+ // Remove IE mouse flickering.
+ try
+ {
+ document.execCommand( 'BackgroundImageCache', false, true ) ;
+ }
+ catch (e)
+ {
+ // We have been reported about loading problems caused by the above
+ // line. For safety, let's just ignore errors.
+ }
+
+ // Create the default cleanup object used by the editor.
+ FCK.IECleanup = new FCKIECleanup( window ) ;
+ FCK.IECleanup.AddItem( FCKTempBin, FCKTempBin.Reset ) ;
+ FCK.IECleanup.AddItem( FCK, FCK_Cleanup ) ;
+}
+
+// The config hidden field is processed immediately, because
+// CustomConfigurationsPath may be set in the page.
+FCKConfig.ProcessHiddenField() ;
+
+// Load the custom configurations file (if defined).
+if ( FCKConfig.CustomConfigurationsPath.length > 0 )
+ LoadScript( FCKConfig.CustomConfigurationsPath ) ;
+
+ </script>
+ <script type="text/javascript">
+
+// Load configurations defined at page level.
+FCKConfig_LoadPageConfig() ;
+
+FCKConfig_PreProcess() ;
+
+// Load the active skin CSS.
+LoadCss( FCKConfig.SkinPath + 'fck_editor.css' ) ;
+
+// Load the language file.
+FCKLanguageManager.Initialize() ;
+LoadScript( 'lang/' + FCKLanguageManager.ActiveLanguage.Code + '.js' ) ;
+
+ </script>
+ <script type="text/javascript">
+
+// Initialize the editing area context menu.
+FCK_ContextMenu_Init() ;
+
+FCKPlugins.Load() ;
+
+ </script>
+ <script type="text/javascript">
+
+// Set the editor interface direction.
+window.document.dir = FCKLang.Dir ;
+
+// Activate pasting operations.
+if ( FCKConfig.ForcePasteAsPlainText || FCKConfig.AutoDetectPasteFromWord )
+ FCK.Events.AttachEvent( 'OnPaste', FCK.Paste ) ;
+
+ </script>
+ <script type="text/javascript">
+
+window.onload = function()
+{
+ InitializeAPI() ;
+
+ if ( FCKBrowserInfo.IsIE )
+ FCK_PreloadImages() ;
+ else
+ LoadToolbarSetup() ;
+}
+
+function LoadToolbarSetup()
+{
+ FCKeditorAPI._FunctionQueue.Add( LoadToolbar ) ;
+}
+
+function LoadToolbar()
+{
+ var oToolbarSet = FCK.ToolbarSet = FCKToolbarSet_Create() ;
+
+ if ( oToolbarSet.IsLoaded )
+ StartEditor() ;
+ else
+ {
+ oToolbarSet.OnLoad = StartEditor ;
+ oToolbarSet.Load( FCKURLParams['Toolbar'] || 'Default' ) ;
+ }
+}
+
+function StartEditor()
+{
+ // Remove the onload listener.
+ FCK.ToolbarSet.OnLoad = null ;
+
+ FCKeditorAPI._FunctionQueue.Remove( LoadToolbar ) ;
+
+ FCK.Events.AttachEvent( 'OnStatusChange', WaitForActive ) ;
+
+ // Start the editor.
+ FCK.StartEditor() ;
+}
+
+function WaitForActive( editorInstance, newStatus )
+{
+ if ( newStatus == FCK_STATUS_ACTIVE )
+ {
+ if ( FCKBrowserInfo.IsGecko )
+ FCKTools.RunFunction( window.onresize ) ;
+
+ _AttachFormSubmitToAPI() ;
+
+ FCK.SetStatus( FCK_STATUS_COMPLETE ) ;
+
+ // Call the special "FCKeditor_OnComplete" function that should be present in
+ // the HTML page where the editor is located.
+ if ( typeof( window.parent.FCKeditor_OnComplete ) == 'function' )
+ window.parent.FCKeditor_OnComplete( FCK ) ;
+ }
+}
+
+// Gecko browsers doens't calculate well that IFRAME size so we must
+// recalculate it every time the window size changes.
+if ( FCKBrowserInfo.IsGecko )
+{
+ function Window_OnResize()
+ {
+ if ( FCKBrowserInfo.IsOpera )
+ return ;
+
+ var oCell = document.getElementById( 'xEditingArea' ) ;
+
+ var eInnerElement = oCell.firstChild ;
+ if ( eInnerElement )
+ {
+ eInnerElement.style.height = 0 ;
+ eInnerElement.style.height = oCell.scrollHeight - 2 ;
+ }
+ }
+ window.onresize = Window_OnResize ;
+}
+
+ </script>
+</head>
+<body>
+ <table width="100%" cellpadding="0" cellspacing="0" style="height: 100%; table-layout: fixed">
+ <tr id="xToolbarRow" style="display: none">
+ <td id="xToolbarSpace" style="overflow: hidden">
+ <table width="100%" cellpadding="0" cellspacing="0">
+ <tr id="xCollapsed" style="display: none">
+ <td id="xExpandHandle" class="TB_Expand" colspan="3">
+ <img class="TB_ExpandImg" alt="" src="images/spacer.gif" width="8" height="4" /></td>
+ </tr>
+ <tr id="xExpanded" style="display: none">
+ <td id="xTBLeftBorder" class="TB_SideBorder" style="width: 1px; display: none;"></td>
+ <td id="xCollapseHandle" style="display: none" class="TB_Collapse" valign="bottom">
+ <img class="TB_CollapseImg" alt="" src="images/spacer.gif" width="8" height="4" /></td>
+ <td id="xToolbar" class="TB_ToolbarSet"></td>
+ <td class="TB_SideBorder" style="width: 1px"></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td id="xEditingArea" valign="top" style="height: 100%"></td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.css b/httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.css
new file mode 100644
index 0000000..ba464ba
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.css
@@ -0,0 +1,88 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * CSS styles used by all pages that compose the File Browser.
+ */
+
+body
+{
+ background-color: #f1f1e3;
+}
+
+form
+{
+ margin: 0px 0px 0px 0px ;
+ padding: 0px 0px 0px 0px ;
+}
+
+.Frame
+{
+ background-color: #f1f1e3;
+ border-color: #f1f1e3;
+ border-right: thin inset;
+ border-top: thin inset;
+ border-left: thin inset;
+ border-bottom: thin inset;
+}
+
+body.FileArea
+{
+
+ background-color: #ffffff;
+}
+
+body, td, input, select
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana;
+}
+
+.ActualFolder
+{
+ font-weight: bold;
+ font-size: 14px;
+}
+
+.PopupButtons
+{
+ border-top: #d5d59d 1px solid;
+ background-color: #e3e3c7;
+ padding: 7px 10px 7px 10px;
+}
+
+.Button, button
+{
+ border-right: #737357 1px solid;
+ border-top: #737357 1px solid;
+ border-left: #737357 1px solid;
+ color: #3b3b1f;
+ border-bottom: #737357 1px solid;
+ background-color: #c7c78f;
+}
+
+.FolderListCurrentFolder img
+{
+ background-image: url(images/FolderOpened.gif);
+}
+
+.FolderListFolder img
+{
+ background-image: url(images/Folder.gif);
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.html b/httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.html
new file mode 100644
index 0000000..8b776a2
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/browser.html
@@ -0,0 +1,154 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This page compose the File Browser dialog frameset.
+-->
+<html>
+ <head>
+ <title>FCKeditor - Resources Browser</title>
+ <link href="browser.css" type="text/css" rel="stylesheet">
+ <script type="text/javascript" src="js/fckxml.js"></script>
+ <script language="javascript">
+
+function GetUrlParam( paramName )
+{
+ var oRegex = new RegExp( '[\?&]' + paramName + '=([^&]+)', 'i' ) ;
+ var oMatch = oRegex.exec( window.top.location.search ) ;
+
+ if ( oMatch && oMatch.length > 1 )
+ return decodeURIComponent( oMatch[1] ) ;
+ else
+ return '' ;
+}
+
+var oConnector = new Object() ;
+oConnector.CurrentFolder = '/' ;
+
+var sConnUrl = GetUrlParam( 'Connector' ) ;
+
+// Gecko has some problems when using relative URLs (not starting with slash).
+if ( sConnUrl.substr(0,1) != '/' && sConnUrl.indexOf( '://' ) < 0 )
+ sConnUrl = window.location.href.replace( /browser.html.*$/, '' ) + sConnUrl ;
+
+oConnector.ConnectorUrl = sConnUrl + ( sConnUrl.indexOf('?') != -1 ? '&' : '?' ) ;
+
+var sServerPath = GetUrlParam( 'ServerPath' ) ;
+if ( sServerPath.length > 0 )
+ oConnector.ConnectorUrl += 'ServerPath=' + encodeURIComponent( sServerPath ) + '&' ;
+
+oConnector.ResourceType = GetUrlParam( 'Type' ) ;
+oConnector.ShowAllTypes = ( oConnector.ResourceType.length == 0 ) ;
+
+if ( oConnector.ShowAllTypes )
+ oConnector.ResourceType = 'File' ;
+
+oConnector.SendCommand = function( command, params, callBackFunction )
+{
+ var sUrl = this.ConnectorUrl + 'Command=' + command ;
+ sUrl += '&Type=' + this.ResourceType ;
+ sUrl += '&CurrentFolder=' + encodeURIComponent( this.CurrentFolder ) ;
+
+ if ( params ) sUrl += '&' + params ;
+
+ var oXML = new FCKXml() ;
+
+ if ( callBackFunction )
+ oXML.LoadUrl( sUrl, callBackFunction ) ; // Asynchronous load.
+ else
+ return oXML.LoadUrl( sUrl ) ;
+
+ return null ;
+}
+
+oConnector.CheckError = function( responseXml )
+{
+ var iErrorNumber = 0 ;
+ var oErrorNode = responseXml.SelectSingleNode( 'Connector/Error' ) ;
+
+ if ( oErrorNode )
+ {
+ iErrorNumber = parseInt( oErrorNode.attributes.getNamedItem('number').value, 10 ) ;
+
+ switch ( iErrorNumber )
+ {
+ case 0 :
+ break ;
+ case 1 : // Custom error. Message placed in the "text" attribute.
+ alert( oErrorNode.attributes.getNamedItem('text').value ) ;
+ break ;
+ case 101 :
+ alert( 'Folder already exists' ) ;
+ break ;
+ case 102 :
+ alert( 'Invalid folder name' ) ;
+ break ;
+ case 103 :
+ alert( 'You have no permissions to create the folder' ) ;
+ break ;
+ case 110 :
+ alert( 'Unknown error creating folder' ) ;
+ break ;
+ default :
+ alert( 'Error on your request. Error number: ' + iErrorNumber ) ;
+ break ;
+ }
+ }
+ return iErrorNumber ;
+}
+
+var oIcons = new Object() ;
+
+oIcons.AvailableIconsArray = [
+ 'ai','avi','bmp','cs','dll','doc','exe','fla','gif','htm','html','jpg','js',
+ 'mdb','mp3','pdf','png','ppt','rdp','swf','swt','txt','vsd','xls','xml','zip' ] ;
+
+oIcons.AvailableIcons = new Object() ;
+
+for ( var i = 0 ; i < oIcons.AvailableIconsArray.length ; i++ )
+ oIcons.AvailableIcons[ oIcons.AvailableIconsArray[i] ] = true ;
+
+oIcons.GetIcon = function( fileName )
+{
+ var sExtension = fileName.substr( fileName.lastIndexOf('.') + 1 ).toLowerCase() ;
+
+ if ( this.AvailableIcons[ sExtension ] == true )
+ return sExtension ;
+ else
+ return 'default.icon' ;
+}
+ </script>
+ </head>
+ <frameset cols="150,*" class="Frame" framespacing="3" bordercolor="#f1f1e3" frameborder="1">
+ <frameset rows="50,*" framespacing="0">
+ <frame src="frmresourcetype.html" scrolling="no" frameborder="0">
+ <frame name="frmFolders" src="frmfolders.html" scrolling="auto" frameborder="1">
+ </frameset>
+ <frameset rows="50,*,50" framespacing="0">
+ <frame name="frmActualFolder" src="frmactualfolder.html" scrolling="no" frameborder="0">
+ <frame name="frmResourcesList" src="frmresourceslist.html" scrolling="auto" frameborder="1">
+ <frameset cols="150,*,0" framespacing="0" frameborder="0">
+ <frame name="frmCreateFolder" src="frmcreatefolder.html" scrolling="no" frameborder="0">
+ <frame name="frmUpload" src="frmupload.html" scrolling="no" frameborder="0">
+ <frame name="frmUploadWorker" src="javascript:void(0)" scrolling="no" frameborder="0">
+ </frameset>
+ </frameset>
+ </frameset>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/basexml.pl b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/basexml.pl
new file mode 100644
index 0000000..f64b7c7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/basexml.pl
@@ -0,0 +1,63 @@
+#####
+# FCKeditor - The text editor for Internet - http://www.fckeditor.net
+# Copyright (C) 2003-2007 Frederico Caldeira Knabben
+#
+# == BEGIN LICENSE ==
+#
+# Licensed under the terms of any of the following licenses at your
+# choice:
+#
+# - GNU General Public License Version 2 or later (the "GPL")
+# http://www.gnu.org/licenses/gpl.html
+#
+# - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+# http://www.gnu.org/licenses/lgpl.html
+#
+# - Mozilla Public License Version 1.1 or later (the "MPL")
+# http://www.mozilla.org/MPL/MPL-1.1.html
+#
+# == END LICENSE ==
+#
+# This is the File Manager Connector for Perl.
+#####
+
+sub CreateXmlHeader
+{
+ local($command,$resourceType,$currentFolder) = @_;
+
+ # Create the XML document header.
+ print '<?xml version="1.0" encoding="utf-8" ?>';
+
+ # Create the main "Connector" node.
+ print '<Connector command="' . $command . '" resourceType="' . $resourceType . '">';
+
+ # Add the current folder node.
+ print '<CurrentFolder path="' . ConvertToXmlAttribute($currentFolder) . '" url="' . ConvertToXmlAttribute(GetUrlFromPath($resourceType,$currentFolder)) . '" />';
+}
+
+sub CreateXmlFooter
+{
+ print '</Connector>';
+}
+
+sub SendError
+{
+ local( $number, $text ) = @_;
+
+ print << "_HTML_HEAD_";
+Content-Type:text/xml; charset=utf-8
+Pragma: no-cache
+Cache-Control: no-cache
+Expires: Thu, 01 Dec 1994 16:00:00 GMT
+
+_HTML_HEAD_
+
+ # Create the XML document header
+ print '<?xml version="1.0" encoding="utf-8" ?>' ;
+
+ print '<Connector><Error number="' . $number . '" text="' . &specialchar_cnv( $text ) . '" /></Connector>' ;
+
+ exit ;
+}
+
+1;
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/commands.pl b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/commands.pl
new file mode 100644
index 0000000..2ed2e62
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/commands.pl
@@ -0,0 +1,158 @@
+#####
+# FCKeditor - The text editor for Internet - http://www.fckeditor.net
+# Copyright (C) 2003-2007 Frederico Caldeira Knabben
+#
+# == BEGIN LICENSE ==
+#
+# Licensed under the terms of any of the following licenses at your
+# choice:
+#
+# - GNU General Public License Version 2 or later (the "GPL")
+# http://www.gnu.org/licenses/gpl.html
+#
+# - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+# http://www.gnu.org/licenses/lgpl.html
+#
+# - Mozilla Public License Version 1.1 or later (the "MPL")
+# http://www.mozilla.org/MPL/MPL-1.1.html
+#
+# == END LICENSE ==
+#
+# This is the File Manager Connector for Perl.
+#####
+
+sub GetFolders
+{
+
+ local($resourceType, $currentFolder) = @_;
+
+ # Map the virtual path to the local server path.
+ $sServerDir = &ServerMapFolder($resourceType, $currentFolder);
+ print "<Folders>"; # Open the "Folders" node.
+
+ opendir(DIR,"$sServerDir");
+ @files = grep(!/^\.\.?$/,readdir(DIR));
+ closedir(DIR);
+
+ foreach $sFile (@files) {
+ if($sFile != '.' && $sFile != '..' && (-d "$sServerDir$sFile")) {
+ $cnv_filename = &ConvertToXmlAttribute($sFile);
+ print '<Folder name="' . $cnv_filename . '" />';
+ }
+ }
+ print "</Folders>"; # Close the "Folders" node.
+}
+
+sub GetFoldersAndFiles
+{
+
+ local($resourceType, $currentFolder) = @_;
+ # Map the virtual path to the local server path.
+ $sServerDir = &ServerMapFolder($resourceType,$currentFolder);
+
+ # Initialize the output buffers for "Folders" and "Files".
+ $sFolders = '<Folders>';
+ $sFiles = '<Files>';
+
+ opendir(DIR,"$sServerDir");
+ @files = grep(!/^\.\.?$/,readdir(DIR));
+ closedir(DIR);
+
+ foreach $sFile (@files) {
+ if($sFile ne '.' && $sFile ne '..') {
+ if(-d "$sServerDir$sFile") {
+ $cnv_filename = &ConvertToXmlAttribute($sFile);
+ $sFolders .= '<Folder name="' . $cnv_filename . '" />' ;
+ } else {
+ ($iFileSize,$refdate,$filedate,$fileperm) = (stat("$sServerDir$sFile"))[7,8,9,2];
+ if($iFileSize > 0) {
+ $iFileSize = int($iFileSize / 1024);
+ if($iFileSize < 1) {
+ $iFileSize = 1;
+ }
+ }
+ $cnv_filename = &ConvertToXmlAttribute($sFile);
+ $sFiles .= '<File name="' . $cnv_filename . '" size="' . $iFileSize . '" />' ;
+ }
+ }
+ }
+ print $sFolders ;
+ print '</Folders>'; # Close the "Folders" node.
+ print $sFiles ;
+ print '</Files>'; # Close the "Files" node.
+}
+
+sub CreateFolder
+{
+
+ local($resourceType, $currentFolder) = @_;
+ $sErrorNumber = '0' ;
+ $sErrorMsg = '' ;
+
+ if($FORM{'NewFolderName'} ne "") {
+ $sNewFolderName = $FORM{'NewFolderName'};
+ # Map the virtual path to the local server path of the current folder.
+ $sServerDir = &ServerMapFolder($resourceType, $currentFolder);
+ if(-w $sServerDir) {
+ $sServerDir .= $sNewFolderName;
+ $sErrorMsg = &CreateServerFolder($sServerDir);
+ if($sErrorMsg == 0) {
+ $sErrorNumber = '0';
+ } elsif($sErrorMsg eq 'Invalid argument' || $sErrorMsg eq 'No such file or directory') {
+ $sErrorNumber = '102'; #// Path too long.
+ } else {
+ $sErrorNumber = '110';
+ }
+ } else {
+ $sErrorNumber = '103';
+ }
+ } else {
+ $sErrorNumber = '102' ;
+ }
+ # Create the "Error" node.
+ $cnv_errmsg = &ConvertToXmlAttribute($sErrorMsg);
+ print '<Error number="' . $sErrorNumber . '" originalDescription="' . $cnv_errmsg . '" />';
+}
+
+sub FileUpload
+{
+eval("use File::Copy;");
+
+ local($resourceType, $currentFolder) = @_;
+
+ $sErrorNumber = '0' ;
+ $sFileName = '' ;
+ if($new_fname) {
+ # Map the virtual path to the local server path.
+ $sServerDir = &ServerMapFolder($resourceType,$currentFolder);
+
+ # Get the uploaded file name.
+ $sFileName = $new_fname;
+ $sOriginalFileName = $sFileName;
+
+ $iCounter = 0;
+ while(1) {
+ $sFilePath = $sServerDir . $sFileName;
+ if(-e $sFilePath) {
+ $iCounter++ ;
+ ($path,$BaseName,$ext) = &RemoveExtension($sOriginalFileName);
+ $sFileName = $BaseName . '(' . $iCounter . ').' . $ext;
+ $sErrorNumber = '201';
+ } else {
+ copy("$img_dir/$new_fname","$sFilePath");
+ chmod(0777,$sFilePath);
+ unlink("$img_dir/$new_fname");
+ last;
+ }
+ }
+ } else {
+ $sErrorNumber = '202' ;
+ }
+ $sFileName =~ s/"/\\"/g;
+ print "Content-type: text/html\n\n";
+ print '<script type="text/javascript">';
+ print 'window.parent.frames["frmUpload"].OnUploadCompleted(' . $sErrorNumber . ',"' . $sFileName . '") ;';
+ print '</script>';
+ exit ;
+}
+1;
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/connector.cgi b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/connector.cgi
new file mode 100644
index 0000000..a741215
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/connector.cgi
@@ -0,0 +1,137 @@
+#!/usr/bin/env perl
+
+#####
+# FCKeditor - The text editor for Internet - http://www.fckeditor.net
+# Copyright (C) 2003-2007 Frederico Caldeira Knabben
+#
+# == BEGIN LICENSE ==
+#
+# Licensed under the terms of any of the following licenses at your
+# choice:
+#
+# - GNU General Public License Version 2 or later (the "GPL")
+# http://www.gnu.org/licenses/gpl.html
+#
+# - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+# http://www.gnu.org/licenses/lgpl.html
+#
+# - Mozilla Public License Version 1.1 or later (the "MPL")
+# http://www.mozilla.org/MPL/MPL-1.1.html
+#
+# == END LICENSE ==
+#
+# This is the File Manager Connector for Perl.
+#####
+
+##
+# ATTENTION: To enable this connector, look for the "SECURITY" comment in this file.
+##
+
+## START: Hack for Windows (Not important to understand the editor code... Perl specific).
+if(Windows_check()) {
+ chdir(GetScriptPath($0));
+}
+
+sub Windows_check
+{
+ # IIS,PWS(NT/95)
+ $www_server_os = $^O;
+ # Win98 & NT(SP4)
+ if($www_server_os eq "") { $www_server_os= $ENV{'OS'}; }
+ # AnHTTPd/Omni/IIS
+ if($ENV{'SERVER_SOFTWARE'} =~ /AnWeb|Omni|IIS\//i) { $www_server_os= 'win'; }
+ # Win Apache
+ if($ENV{'WINDIR'} ne "") { $www_server_os= 'win'; }
+ if($www_server_os=~ /win/i) { return(1); }
+ return(0);
+}
+
+sub GetScriptPath {
+ local($path) = @_;
+ if($path =~ /[\:\/\\]/) { $path =~ s/(.*?)[\/\\][^\/\\]+$/$1/; } else { $path = '.'; }
+ $path;
+}
+## END: Hack for IIS
+
+require 'util.pl';
+require 'io.pl';
+require 'basexml.pl';
+require 'commands.pl';
+require 'upload_fck.pl';
+
+##
+# SECURITY: REMOVE/COMMENT THE FOLLOWING LINE TO ENABLE THIS CONNECTOR.
+##
+&SendError( 1, 'This connector is disabled. Please check the "editor/filemanager/browser/default/connectors/perl/connector.cgi" file' ) ;
+
+ &read_input();
+
+ if($FORM{'ServerPath'} ne "") {
+ $GLOBALS{'UserFilesPath'} = $FORM{'ServerPath'};
+ if(!($GLOBALS{'UserFilesPath'} =~ /\/$/)) {
+ $GLOBALS{'UserFilesPath'} .= '/' ;
+ }
+ } else {
+ $GLOBALS{'UserFilesPath'} = '/userfiles/';
+ }
+
+ # Map the "UserFiles" path to a local directory.
+ $rootpath = &GetRootPath();
+ $GLOBALS{'UserFilesDirectory'} = $rootpath . $GLOBALS{'UserFilesPath'};
+
+ &DoResponse();
+
+sub DoResponse
+{
+
+ if($FORM{'Command'} eq "" || $FORM{'Type'} eq "" || $FORM{'CurrentFolder'} eq "") {
+ return ;
+ }
+ # Get the main request informaiton.
+ $sCommand = $FORM{'Command'};
+ $sResourceType = $FORM{'Type'};
+ $sCurrentFolder = $FORM{'CurrentFolder'};
+
+ # Check the current folder syntax (must begin and start with a slash).
+ if(!($sCurrentFolder =~ /\/$/)) {
+ $sCurrentFolder .= '/';
+ }
+ if(!($sCurrentFolder =~ /^\//)) {
+ $sCurrentFolder = '/' . $sCurrentFolder;
+ }
+
+ # Check for invalid folder paths (..)
+ if ( $sCurrentFolder =~ /\.\./ ) {
+ SendError( 102, "" ) ;
+ }
+
+ # File Upload doesn't have to Return XML, so it must be intercepted before anything.
+ if($sCommand eq 'FileUpload') {
+ FileUpload($sResourceType,$sCurrentFolder);
+ return ;
+ }
+
+ print << "_HTML_HEAD_";
+Content-Type:text/xml; charset=utf-8
+Pragma: no-cache
+Cache-Control: no-cache
+Expires: Thu, 01 Dec 1994 16:00:00 GMT
+
+_HTML_HEAD_
+
+ &CreateXmlHeader($sCommand,$sResourceType,$sCurrentFolder);
+
+ # Execute the required command.
+ if($sCommand eq 'GetFolders') {
+ &GetFolders($sResourceType,$sCurrentFolder);
+ } elsif($sCommand eq 'GetFoldersAndFiles') {
+ &GetFoldersAndFiles($sResourceType,$sCurrentFolder);
+ } elsif($sCommand eq 'CreateFolder') {
+ &CreateFolder($sResourceType,$sCurrentFolder);
+ }
+
+ &CreateXmlFooter();
+
+ exit ;
+}
+
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/io.pl b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/io.pl
new file mode 100644
index 0000000..c1dbccf
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/io.pl
@@ -0,0 +1,131 @@
+#####
+# FCKeditor - The text editor for Internet - http://www.fckeditor.net
+# Copyright (C) 2003-2007 Frederico Caldeira Knabben
+#
+# == BEGIN LICENSE ==
+#
+# Licensed under the terms of any of the following licenses at your
+# choice:
+#
+# - GNU General Public License Version 2 or later (the "GPL")
+# http://www.gnu.org/licenses/gpl.html
+#
+# - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+# http://www.gnu.org/licenses/lgpl.html
+#
+# - Mozilla Public License Version 1.1 or later (the "MPL")
+# http://www.mozilla.org/MPL/MPL-1.1.html
+#
+# == END LICENSE ==
+#
+# This is the File Manager Connector for Perl.
+#####
+
+sub GetUrlFromPath
+{
+ local($resourceType, $folderPath) = @_;
+
+ if($resourceType eq '') {
+ $rmpath = &RemoveFromEnd($GLOBALS{'UserFilesPath'},'/');
+ return("$rmpath$folderPath");
+ } else {
+ return("$GLOBALS{'UserFilesPath'}$resourceType$folderPath");
+ }
+}
+
+sub RemoveExtension
+{
+ local($fileName) = @_;
+ local($path, $base, $ext);
+ if($fileName !~ /\./) {
+ $fileName .= '.';
+ }
+ if($fileName =~ /([^\\\/]*)\.(.*)$/) {
+ $base = $1;
+ $ext = $2;
+ if($fileName =~ /(.*)$base\.$ext$/) {
+ $path = $1;
+ }
+ }
+ return($path,$base,$ext);
+
+}
+
+sub ServerMapFolder
+{
+ local($resourceType,$folderPath) = @_;
+
+ # Get the resource type directory.
+ $sResourceTypePath = $GLOBALS{'UserFilesDirectory'} . $resourceType . '/';
+
+ # Ensure that the directory exists.
+ &CreateServerFolder($sResourceTypePath);
+
+ # Return the resource type directory combined with the required path.
+ $rmpath = &RemoveFromStart($folderPath,'/');
+ return("$sResourceTypePath$rmpath");
+}
+
+sub GetParentFolder
+{
+ local($folderPath) = @_;
+
+ $folderPath =~ s/[\/][^\/]+[\/]?$//g;
+ return $folderPath;
+}
+
+sub CreateServerFolder
+{
+ local($folderPath) = @_;
+
+ $sParent = &GetParentFolder($folderPath);
+ # Check if the parent exists, or create it.
+ if(!(-e $sParent)) {
+ $sErrorMsg = &CreateServerFolder($sParent);
+ if($sErrorMsg == 1) {
+ return(1);
+ }
+ }
+ if(!(-e $folderPath)) {
+ umask(000);
+ mkdir("$folderPath",0777);
+ chmod(0777,"$folderPath");
+ return(0);
+ } else {
+ return(1);
+ }
+}
+
+sub GetRootPath
+{
+#use Cwd;
+
+# my $dir = getcwd;
+# print $dir;
+# $dir =~ s/$ENV{'DOCUMENT_ROOT'}//g;
+# print $dir;
+# return($dir);
+
+# $wk = $0;
+# $wk =~ s/\/connector\.cgi//g;
+# if($wk) {
+# $current_dir = $wk;
+# } else {
+# $current_dir = `pwd`;
+# }
+# return($current_dir);
+use Cwd;
+
+ if($ENV{'DOCUMENT_ROOT'}) {
+ $dir = $ENV{'DOCUMENT_ROOT'};
+ } else {
+ my $dir = getcwd;
+ $workdir =~ s/\/connector\.cgi//g;
+ $dir =~ s/$workdir//g;
+ }
+ return($dir);
+
+
+
+}
+1;
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/upload_fck.pl b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/upload_fck.pl
new file mode 100644
index 0000000..1c3f4e2
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/upload_fck.pl
@@ -0,0 +1,667 @@
+#####
+# FCKeditor - The text editor for Internet - http://www.fckeditor.net
+# Copyright (C) 2003-2007 Frederico Caldeira Knabben
+#
+# == BEGIN LICENSE ==
+#
+# Licensed under the terms of any of the following licenses at your
+# choice:
+#
+# - GNU General Public License Version 2 or later (the "GPL")
+# http://www.gnu.org/licenses/gpl.html
+#
+# - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+# http://www.gnu.org/licenses/lgpl.html
+#
+# - Mozilla Public License Version 1.1 or later (the "MPL")
+# http://www.mozilla.org/MPL/MPL-1.1.html
+#
+# == END LICENSE ==
+#
+# This is the File Manager Connector for Perl.
+#####
+
+# image data save dir
+$img_dir = './temp/';
+
+
+# File size max(unit KB)
+$MAX_CONTENT_SIZE = 30000;
+
+# Filelock (1=use,0=not use)
+$PM{'flock'} = '1';
+
+
+# upload Content-Type list
+my %UPLOAD_CONTENT_TYPE_LIST = (
+ 'image/(x-)?png' => 'png', # PNG image
+ 'image/p?jpe?g' => 'jpg', # JPEG image
+ 'image/gif' => 'gif', # GIF image
+ 'image/x-xbitmap' => 'xbm', # XBM image
+
+ 'image/(x-(MS-)?)?bmp' => 'bmp', # Windows BMP image
+ 'image/pict' => 'pict', # Macintosh PICT image
+ 'image/tiff' => 'tif', # TIFF image
+ 'application/pdf' => 'pdf', # PDF image
+ 'application/x-shockwave-flash' => 'swf', # Shockwave Flash
+
+ 'video/(x-)?msvideo' => 'avi', # Microsoft Video
+ 'video/quicktime' => 'mov', # QuickTime Video
+ 'video/mpeg' => 'mpeg', # MPEG Video
+ 'video/x-mpeg2' => 'mpv2', # MPEG2 Video
+
+ 'audio/(x-)?midi?' => 'mid', # MIDI Audio
+ 'audio/(x-)?wav' => 'wav', # WAV Audio
+ 'audio/basic' => 'au', # ULAW Audio
+ 'audio/mpeg' => 'mpga', # MPEG Audio
+
+ 'application/(x-)?zip(-compressed)?' => 'zip', # ZIP Compress
+
+ 'text/html' => 'html', # HTML
+ 'text/plain' => 'txt', # TEXT
+ '(?:application|text)/(?:rtf|richtext)' => 'rtf', # RichText
+
+ 'application/msword' => 'doc', # Microsoft Word
+ 'application/vnd.ms-excel' => 'xls', # Microsoft Excel
+
+ ''
+);
+
+# Upload is permitted.
+# A regular expression is possible.
+my %UPLOAD_EXT_LIST = (
+ 'png' => 'PNG image',
+ 'p?jpe?g|jpe|jfif|pjp' => 'JPEG image',
+ 'gif' => 'GIF image',
+ 'xbm' => 'XBM image',
+
+ 'bmp|dib|rle' => 'Windows BMP image',
+ 'pi?ct' => 'Macintosh PICT image',
+ 'tiff?' => 'TIFF image',
+ 'pdf' => 'PDF image',
+ 'swf' => 'Shockwave Flash',
+
+ 'avi' => 'Microsoft Video',
+ 'moo?v|qt' => 'QuickTime Video',
+ 'm(p(e?gv?|e|v)|1v)' => 'MPEG Video',
+ 'mp(v2|2v)' => 'MPEG2 Video',
+
+ 'midi?|kar|smf|rmi|mff' => 'MIDI Audio',
+ 'wav' => 'WAVE Audio',
+ 'au|snd' => 'ULAW Audio',
+ 'mp(e?ga|2|a|3)|abs' => 'MPEG Audio',
+
+ 'zip' => 'ZIP Compress',
+ 'lzh' => 'LZH Compress',
+ 'cab' => 'CAB Compress',
+
+ 'd?html?' => 'HTML',
+ 'rtf|rtx' => 'RichText',
+ 'txt|text' => 'Text',
+
+ ''
+);
+
+
+# sjis or euc
+my $CHARCODE = 'sjis';
+
+$TRANS_2BYTE_CODE = 0;
+
+##############################################################################
+# Summary
+#
+# Form Read input
+#
+# Parameters
+# Returns
+# Memo
+##############################################################################
+sub read_input
+{
+eval("use File::Copy;");
+eval("use File::Path;");
+
+ my ($FORM) = @_;
+
+
+ mkdir($img_dir,0777);
+ chmod(0777,$img_dir);
+
+ undef $img_data_exists;
+ undef @NEWFNAMES;
+ undef @NEWFNAME_DATA;
+
+ if($ENV{'CONTENT_LENGTH'} > 10000000 || $ENV{'CONTENT_LENGTH'} > $MAX_CONTENT_SIZE * 1024) {
+ &upload_error(
+ 'Size Error',
+ sprintf(
+ "Transmitting size is too large.MAX <strong>%d KB</strong> Now Size <strong>%d KB</strong>(<strong>%d bytes</strong> Over)",
+ $MAX_CONTENT_SIZE,
+ int($ENV{'CONTENT_LENGTH'} / 1024),
+ $ENV{'CONTENT_LENGTH'} - $MAX_CONTENT_SIZE * 1024
+ )
+ );
+ }
+
+ my $Buffer;
+ if($ENV{'CONTENT_TYPE'} =~ /multipart\/form-data/) {
+ # METHOD POST only
+ return unless($ENV{'CONTENT_LENGTH'});
+
+ binmode(STDIN);
+ # STDIN A pause character is detected.'(MacIE3.0 boundary of $ENV{'CONTENT_TYPE'} cannot be trusted.)
+ my $Boundary = <STDIN>;
+ $Boundary =~ s/\x0D\x0A//;
+ $Boundary = quotemeta($Boundary);
+ while(<STDIN>) {
+ if(/^\s*Content-Disposition:/i) {
+ my($name,$ContentType,$FileName);
+ # form data get
+ if(/\bname="([^"]+)"/i || /\bname=([^\s:;]+)/i) {
+ $name = $1;
+ $name =~ tr/+/ /;
+ $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+ &Encode(\$name);
+ }
+ if(/\bfilename="([^"]*)"/i || /\bfilename=([^\s:;]*)/i) {
+ $FileName = $1 || 'unknown';
+ }
+ # head read
+ while(<STDIN>) {
+ last if(! /\w/);
+ if(/^\s*Content-Type:\s*"([^"]+)"/i || /^\s*Content-Type:\s*([^\s:;]+)/i) {
+ $ContentType = $1;
+ }
+ }
+ # body read
+ $value = "";
+ while(<STDIN>) {
+ last if(/^$Boundary/o);
+ $value .= $_;
+ };
+ $lastline = $_;
+ $value =~s /\x0D\x0A$//;
+ if($value ne '') {
+ if($FileName || $ContentType) {
+ $img_data_exists = 1;
+ (
+ $FileName, #
+ $Ext, #
+ $Length, #
+ $ImageWidth, #
+ $ImageHeight, #
+ $ContentName #
+ ) = &CheckContentType(\$value,$FileName,$ContentType);
+
+ $FORM{$name} = $FileName;
+ $new_fname = $FileName;
+ push(@NEWFNAME_DATA,"$FileName\t$Ext\t$Length\t$ImageWidth\t$ImageHeight\t$ContentName");
+
+ # Multi-upload correspondence
+ push(@NEWFNAMES,$new_fname);
+ open(OUT,">$img_dir/$new_fname");
+ binmode(OUT);
+ eval "flock(OUT,2);" if($PM{'flock'} == 1);
+ print OUT $value;
+ eval "flock(OUT,8);" if($PM{'flock'} == 1);
+ close(OUT);
+
+ } elsif($name) {
+ $value =~ tr/+/ /;
+ $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+ &Encode(\$value,'trans');
+ $FORM{$name} .= "\0" if(defined($FORM{$name}));
+ $FORM{$name} .= $value;
+ }
+ }
+ };
+ last if($lastline =~ /^$Boundary\-\-/o);
+ }
+ } elsif($ENV{'CONTENT_LENGTH'}) {
+ read(STDIN,$Buffer,$ENV{'CONTENT_LENGTH'});
+ }
+ foreach(split(/&/,$Buffer),split(/&/,$ENV{'QUERY_STRING'})) {
+ my($name, $value) = split(/=/);
+ $name =~ tr/+/ /;
+ $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+ $value =~ tr/+/ /;
+ $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+
+ &Encode(\$name);
+ &Encode(\$value,'trans');
+ $FORM{$name} .= "\0" if(defined($FORM{$name}));
+ $FORM{$name} .= $value;
+
+ }
+
+}
+
+##############################################################################
+# Summary
+#
+# CheckContentType
+#
+# Parameters
+# Returns
+# Memo
+##############################################################################
+sub CheckContentType
+{
+
+ my($DATA,$FileName,$ContentType) = @_;
+ my($Ext,$ImageWidth,$ImageHeight,$ContentName,$Infomation);
+ my $DataLength = length($$DATA);
+
+ # An unknown file type
+
+ $_ = $ContentType;
+ my $UnknownType = (
+ !$_
+ || /^application\/(x-)?macbinary$/i
+ || /^application\/applefile$/i
+ || /^application\/octet-stream$/i
+ || /^text\/plane$/i
+ || /^x-unknown-content-type/i
+ );
+
+ # MacBinary(Mac Unnecessary data are deleted.)
+ if($UnknownType || $ENV{'HTTP_USER_AGENT'} =~ /Macintosh|Mac_/) {
+ if($DataLength > 128 && !unpack("C",substr($$DATA,0,1)) && !unpack("C",substr($$DATA,74,1)) && !unpack("C",substr($$DATA,82,1)) ) {
+ my $MacBinary_ForkLength = unpack("N", substr($$DATA, 83, 4)); # ForkLength Get
+ my $MacBinary_FileName = quotemeta(substr($$DATA, 2, unpack("C",substr($$DATA, 1, 1))));
+ if($MacBinary_FileName && $MacBinary_ForkLength && $DataLength >= $MacBinary_ForkLength + 128
+ && ($FileName =~ /$MacBinary_FileName/i || substr($$DATA,102,4) eq 'mBIN')) { # DATA TOP 128byte MacBinary!!
+ $$DATA = substr($$DATA,128,$MacBinary_ForkLength);
+ my $ResourceLength = $DataLength - $MacBinary_ForkLength - 128;
+ $DataLength = $MacBinary_ForkLength;
+ }
+ }
+ }
+
+ # A file name is changed into EUC.
+# &jcode::convert(\$FileName,'euc',$FormCodeDefault);
+# &jcode::h2z_euc(\$FileName);
+ $FileName =~ s/^.*\\//; # Windows, Mac
+ $FileName =~ s/^.*\///; # UNIX
+ $FileName =~ s/&/&amp;/g;
+ $FileName =~ s/"/&quot;/g;
+ $FileName =~ s/</&lt;/g;
+ $FileName =~ s/>/&gt;/g;
+#
+# if($CHARCODE ne 'euc') {
+# &jcode::convert(\$FileName,$CHARCODE,'euc');
+# }
+
+ # An extension is extracted and it changes into a small letter.
+ my $FileExt;
+ if($FileName =~ /\.(\w+)$/) {
+ $FileExt = $1;
+ $FileExt =~ tr/A-Z/a-z/;
+ }
+
+ # Executable file detection (ban on upload)
+ if($$DATA =~ /^MZ/) {
+ $Ext = 'exe';
+ }
+ # text
+ if(!$Ext && ($UnknownType || $ContentType =~ /^text\//i || $ContentType =~ /^application\/(?:rtf|richtext)$/i || $ContentType =~ /^image\/x-xbitmap$/i)
+ && ! $$DATA =~ /[\000-\006\177\377]/) {
+# $$DATA =~ s/\x0D\x0A/\n/g;
+# $$DATA =~ tr/\x0D\x0A/\n\n/;
+#
+# if(
+# $$DATA =~ /<\s*SCRIPT(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*(?:.|\n)*?\bONLOAD\s*=(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*(?:.|\n)*?\bONCLICK\s*=(?:.|\n)*?>/i
+# ) {
+# $Infomation = '(JavaScript contains)';
+# }
+# if($$DATA =~ /<\s*TABLE(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*BLINK(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*MARQUEE(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*OBJECT(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*EMBED(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*FRAME(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*APPLET(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*FORM(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*(?:.|\n)*?\bSRC\s*=(?:.|\n)*?>/i
+# || $$DATA =~ /<\s*(?:.|\n)*?\bDYNSRC\s*=(?:.|\n)*?>/i
+# ) {
+# $Infomation = '(the HTML tag which is not safe is included)';
+# }
+
+ if($FileExt =~ /^txt$/i || $FileExt =~ /^cgi$/i || $FileExt =~ /^pl$/i) { # Text File
+ $Ext = 'txt';
+ } elsif($ContentType =~ /^text\/html$/i || $FileExt =~ /html?/i || $$DATA =~ /<\s*HTML(?:.|\n)*?>/i) { # HTML File
+ $Ext = 'html';
+ } elsif($ContentType =~ /^image\/x-xbitmap$/i || $FileExt =~ /^xbm$/i) { # XBM(x-BitMap) Image
+ my $XbmName = $1;
+ my ($XbmWidth, $XbmHeight);
+ if($$DATA =~ /\#define\s*$XbmName\_width\s*(\d+)/i) {
+ $XbmWidth = $1;
+ }
+ if($$DATA =~ /\#define\s*$XbmName\_height\s*(\d+)/i) {
+ $XbmHeight = $1;
+ }
+ if($XbmWidth && $XbmHeight) {
+ $Ext = 'xbm';
+ $ImageWidth = $XbmWidth;
+ $ImageHeight = $XbmHeight;
+ }
+ } else { #
+ $Ext = 'txt';
+ }
+ }
+
+ # image
+ if(!$Ext && ($UnknownType || $ContentType =~ /^image\//i)) {
+ # PNG
+ if($$DATA =~ /^\x89PNG\x0D\x0A\x1A\x0A/) {
+ if(substr($$DATA, 12, 4) eq 'IHDR') {
+ $Ext = 'png';
+ ($ImageWidth, $ImageHeight) = unpack("N2", substr($$DATA, 16, 8));
+ }
+ } elsif($$DATA =~ /^GIF8(?:9|7)a/) { # GIF89a(modified), GIF89a, GIF87a
+ $Ext = 'gif';
+ ($ImageWidth, $ImageHeight) = unpack("v2", substr($$DATA, 6, 4));
+ } elsif($$DATA =~ /^II\x2a\x00\x08\x00\x00\x00/ || $$DATA =~ /^MM\x00\x2a\x00\x00\x00\x08/) { # TIFF
+ $Ext = 'tif';
+ } elsif($$DATA =~ /^BM/) { # BMP
+ $Ext = 'bmp';
+ } elsif($$DATA =~ /^\xFF\xD8\xFF/ || $$DATA =~ /JFIF/) { # JPEG
+ my $HeaderPoint = index($$DATA, "\xFF\xD8\xFF", 0);
+ my $Point = $HeaderPoint + 2;
+ while($Point < $DataLength) {
+ my($Maker, $MakerType, $MakerLength) = unpack("C2n",substr($$DATA,$Point,4));
+ if($Maker != 0xFF || $MakerType == 0xd9 || $MakerType == 0xda) {
+ last;
+ } elsif($MakerType >= 0xC0 && $MakerType <= 0xC3) {
+ $Ext = 'jpg';
+ ($ImageHeight, $ImageWidth) = unpack("n2", substr($$DATA, $Point + 5, 4));
+ if($HeaderPoint > 0) {
+ $$DATA = substr($$DATA, $HeaderPoint);
+ $DataLength = length($$DATA);
+ }
+ last;
+ } else {
+ $Point += $MakerLength + 2;
+ }
+ }
+ }
+ }
+
+ # audio
+ if(!$Ext && ($UnknownType || $ContentType =~ /^audio\//i)) {
+ # MIDI Audio
+ if($$DATA =~ /^MThd/) {
+ $Ext = 'mid';
+ } elsif($$DATA =~ /^\x2esnd/) { # ULAW Audio
+ $Ext = 'au';
+ } elsif($$DATA =~ /^RIFF/ || $$DATA =~ /^ID3/ && $$DATA =~ /RIFF/) {
+ my $HeaderPoint = index($$DATA, "RIFF", 0);
+ $_ = substr($$DATA, $HeaderPoint + 8, 8);
+ if(/^WAVEfmt $/) {
+ # WAVE
+ if(unpack("V",substr($$DATA, $HeaderPoint + 16, 4)) == 16) {
+ $Ext = 'wav';
+ } else { # RIFF WAVE MP3
+ $Ext = 'mp3';
+ }
+ } elsif(/^RMIDdata$/) { # RIFF MIDI
+ $Ext = 'rmi';
+ } elsif(/^RMP3data$/) { # RIFF MP3
+ $Ext = 'rmp';
+ }
+ if($ContentType =~ /^audio\//i) {
+ $Infomation .= '(RIFF '. substr($$DATA, $HeaderPoint + 8, 4). ')';
+ }
+ }
+ }
+
+ # a binary file
+ unless ($Ext) {
+ # PDF image
+ if($$DATA =~ /^\%PDF/) {
+ # Picture size is not measured.
+ $Ext = 'pdf';
+ } elsif($$DATA =~ /^FWS/) { # Shockwave Flash
+ $Ext = 'swf';
+ } elsif($$DATA =~ /^RIFF/ || $$DATA =~ /^ID3/ && $$DATA =~ /RIFF/) {
+ my $HeaderPoint = index($$DATA, "RIFF", 0);
+ $_ = substr($$DATA,$HeaderPoint + 8, 8);
+ # AVI
+ if(/^AVI LIST$/) {
+ $Ext = 'avi';
+ }
+ if($ContentType =~ /^video\//i) {
+ $Infomation .= '(RIFF '. substr($$DATA, $HeaderPoint + 8, 4). ')';
+ }
+ } elsif($$DATA =~ /^PK/) { # ZIP Compress File
+ $Ext = 'zip';
+ } elsif($$DATA =~ /^MSCF/) { # CAB Compress File
+ $Ext = 'cab';
+ } elsif($$DATA =~ /^Rar\!/) { # RAR Compress File
+ $Ext = 'rar';
+ } elsif(substr($$DATA, 2, 5) =~ /^\-lh(\d+|d)\-$/) { # LHA Compress File
+ $Infomation .= "(lh$1)";
+ $Ext = 'lzh';
+ } elsif(substr($$DATA, 325, 25) eq "Apple Video Media Handler" || substr($$DATA, 325, 30) eq "Apple \x83\x72\x83\x66\x83\x49\x81\x45\x83\x81\x83\x66\x83\x42\x83\x41\x83\x6E\x83\x93\x83\x68\x83\x89") {
+ # QuickTime
+ $Ext = 'mov';
+ }
+ }
+
+ # Header analysis failure
+ unless ($Ext) {
+ # It will be followed if it applies for the MIME type from the browser.
+ foreach (keys %UPLOAD_CONTENT_TYPE_LIST) {
+ next unless ($_);
+ if($ContentType =~ /^$_$/i) {
+ $Ext = $UPLOAD_CONTENT_TYPE_LIST{$_};
+ $ContentName = &CheckContentExt($Ext);
+ if(
+ grep {$_ eq $Ext;} (
+ 'png',
+ 'gif',
+ 'jpg',
+ 'xbm',
+ 'tif',
+ 'bmp',
+ 'pdf',
+ 'swf',
+ 'mov',
+ 'zip',
+ 'cab',
+ 'lzh',
+ 'rar',
+ 'mid',
+ 'rmi',
+ 'au',
+ 'wav',
+ 'avi',
+ 'exe'
+ )
+ ) {
+ $Infomation .= ' / Header analysis failure';
+ }
+ if($Ext ne $FileExt && &CheckContentExt($FileExt) eq $ContentName) {
+ $Ext = $FileExt;
+ }
+ last;
+ }
+ }
+ # a MIME type is unknown--It judges from an extension.
+ unless ($Ext) {
+ $ContentName = &CheckContentExt($FileExt);
+ if($ContentName) {
+ $Ext = $FileExt;
+ $Infomation .= ' / MIME type is unknown('. $ContentType. ')';
+ last;
+ }
+ }
+ }
+
+# $ContentName = &CheckContentExt($Ext) unless($ContentName);
+# if($Ext && $ContentName) {
+# $ContentName .= $Infomation;
+# } else {
+# &upload_error(
+# 'Extension Error',
+# "$FileName A not corresponding extension ($Ext)<BR>The extension which can be responded ". join(',', sort values(%UPLOAD_EXT_LIST))
+# );
+# }
+
+# # SSI Tag Deletion
+# if($Ext =~ /.?html?/ && $$DATA =~ /<\!/) {
+# foreach (
+# 'config',
+# 'echo',
+# 'exec',
+# 'flastmod',
+# 'fsize',
+# 'include'
+# ) {
+# $$DATA =~ s/\#\s*$_/\&\#35\;$_/ig
+# }
+# }
+
+ return (
+ $FileName,
+ $Ext,
+ int($DataLength / 1024 + 1),
+ $ImageWidth,
+ $ImageHeight,
+ $ContentName
+ );
+}
+
+##############################################################################
+# Summary
+#
+# Extension discernment
+#
+# Parameters
+# Returns
+# Memo
+##############################################################################
+
+sub CheckContentExt
+{
+
+ my($Ext) = @_;
+ my $ContentName;
+ foreach (keys %UPLOAD_EXT_LIST) {
+ next unless ($_);
+ if($_ && $Ext =~ /^$_$/) {
+ $ContentName = $UPLOAD_EXT_LIST{$_};
+ last;
+ }
+ }
+ return $ContentName;
+
+}
+
+##############################################################################
+# Summary
+#
+# Form decode
+#
+# Parameters
+# Returns
+# Memo
+##############################################################################
+sub Encode
+{
+
+ my($value,$Trans) = @_;
+
+# my $FormCode = &jcode::getcode($value) || $FormCodeDefault;
+# $FormCodeDefault ||= $FormCode;
+#
+# if($Trans && $TRANS_2BYTE_CODE) {
+# if($FormCode ne 'euc') {
+# &jcode::convert($value, 'euc', $FormCode);
+# }
+# &jcode::tr(
+# $value,
+# "\xA3\xB0-\xA3\xB9\xA3\xC1-\xA3\xDA\xA3\xE1-\xA3\xFA",
+# '0-9A-Za-z'
+# );
+# if($CHARCODE ne 'euc') {
+# &jcode::convert($value,$CHARCODE,'euc');
+# }
+# } else {
+# if($CHARCODE ne $FormCode) {
+# &jcode::convert($value,$CHARCODE,$FormCode);
+# }
+# }
+# if($CHARCODE eq 'euc') {
+# &jcode::h2z_euc($value);
+# } elsif($CHARCODE eq 'sjis') {
+# &jcode::h2z_sjis($value);
+# }
+
+}
+
+##############################################################################
+# Summary
+#
+# Error Msg
+#
+# Parameters
+# Returns
+# Memo
+##############################################################################
+
+sub upload_error
+{
+
+ local($error_message) = $_[0];
+ local($error_message2) = $_[1];
+
+ print "Content-type: text/html\n\n";
+ print<<EOF;
+<HTML>
+<HEAD>
+<TITLE>Error Message</TITLE></HEAD>
+<BODY>
+<table border="1" cellspacing="10" cellpadding="10">
+ <TR bgcolor="#0000B0">
+ <TD bgcolor="#0000B0" NOWRAP><font size="-1" color="white"><B>Error Message</B></font></TD>
+ </TR>
+</table>
+<UL>
+<H4> $error_message </H4>
+$error_message2 <BR>
+</UL>
+</BODY>
+</HTML>
+EOF
+ &rm_tmp_uploaded_files; # Image Temporary deletion
+ exit;
+}
+
+##############################################################################
+# Summary
+#
+# Image Temporary deletion
+#
+# Parameters
+# Returns
+# Memo
+##############################################################################
+
+sub rm_tmp_uploaded_files
+{
+ if($img_data_exists == 1){
+ sleep 1;
+ foreach $fname_list(@NEWFNAMES) {
+ if(-e "$img_dir/$fname_list") {
+ unlink("$img_dir/$fname_list");
+ }
+ }
+ }
+
+}
+1;
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/util.pl b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/util.pl
new file mode 100644
index 0000000..e860292
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/connectors/perl/util.pl
@@ -0,0 +1,60 @@
+#####
+# FCKeditor - The text editor for Internet - http://www.fckeditor.net
+# Copyright (C) 2003-2007 Frederico Caldeira Knabben
+#
+# == BEGIN LICENSE ==
+#
+# Licensed under the terms of any of the following licenses at your
+# choice:
+#
+# - GNU General Public License Version 2 or later (the "GPL")
+# http://www.gnu.org/licenses/gpl.html
+#
+# - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+# http://www.gnu.org/licenses/lgpl.html
+#
+# - Mozilla Public License Version 1.1 or later (the "MPL")
+# http://www.mozilla.org/MPL/MPL-1.1.html
+#
+# == END LICENSE ==
+#
+# This is the File Manager Connector for Perl.
+#####
+
+sub RemoveFromStart
+{
+ local($sourceString, $charToRemove) = @_;
+ $sPattern = '^' . $charToRemove . '+' ;
+ $sourceString =~ s/^$charToRemove+//g;
+ return $sourceString;
+}
+
+sub RemoveFromEnd
+{
+ local($sourceString, $charToRemove) = @_;
+ $sPattern = $charToRemove . '+$' ;
+ $sourceString =~ s/$charToRemove+$//g;
+ return $sourceString;
+}
+
+sub ConvertToXmlAttribute
+{
+ local($value) = @_;
+ return $value;
+# return utf8_encode(htmlspecialchars($value));
+
+}
+
+sub specialchar_cnv
+{
+ local($ch) = @_;
+
+ $ch =~ s/&/&amp;/g; # &
+ $ch =~ s/\"/&quot;/g; #"
+ $ch =~ s/\'/&#39;/g; # '
+ $ch =~ s/</&lt;/g; # <
+ $ch =~ s/>/&gt;/g; # >
+ return($ch);
+}
+
+1;
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmactualfolder.html b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmactualfolder.html
new file mode 100644
index 0000000..90653d6
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmactualfolder.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This page shows the actual folder path.
+-->
+<html>
+ <head>
+ <link href="browser.css" type="text/css" rel="stylesheet">
+ <script type="text/javascript">
+
+function OnResize()
+{
+ divName.style.width = "1px" ;
+ divName.style.width = tdName.offsetWidth + "px" ;
+}
+
+function SetCurrentFolder( resourceType, folderPath )
+{
+ document.getElementById('tdName').innerHTML = folderPath ;
+}
+
+window.onload = function()
+{
+ window.top.IsLoadedActualFolder = true ;
+}
+
+ </script>
+ </head>
+ <body bottomMargin="0" topMargin="0">
+ <table height="100%" cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td>
+ <button style="WIDTH: 100%" type="button">
+ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td><img height="32" alt="" src="images/FolderOpened32.gif" width="32"></td>
+ <td>&nbsp;</td>
+ <td id="tdName" width="100%" nowrap class="ActualFolder">/</td>
+ <td>&nbsp;</td>
+ <td><img height="8" src="images/ButtonArrow.gif" width="12"></td>
+ <td>&nbsp;</td>
+ </tr>
+ </table>
+ </button>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmcreatefolder.html b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmcreatefolder.html
new file mode 100644
index 0000000..8f72ff5
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmcreatefolder.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Page used to create new folders in the current folder.
+-->
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <link href="browser.css" type="text/css" rel="stylesheet">
+ <script type="text/javascript" src="js/common.js"></script>
+ <script language="javascript">
+
+function SetCurrentFolder( resourceType, folderPath )
+{
+ oConnector.ResourceType = resourceType ;
+ oConnector.CurrentFolder = folderPath ;
+}
+
+function CreateFolder()
+{
+ var sFolderName ;
+
+ while ( true )
+ {
+ sFolderName = prompt( 'Type the name of the new folder:', '' ) ;
+
+ if ( sFolderName == null )
+ return ;
+ else if ( sFolderName.length == 0 )
+ alert( 'Please type the folder name' ) ;
+ else
+ break ;
+ }
+
+ oConnector.SendCommand( 'CreateFolder', 'NewFolderName=' + encodeURIComponent( sFolderName) , CreateFolderCallBack ) ;
+}
+
+function CreateFolderCallBack( fckXml )
+{
+ if ( oConnector.CheckError( fckXml ) == 0 )
+ window.parent.frames['frmResourcesList'].Refresh() ;
+
+ /*
+ // Get the current folder path.
+ var oNode = fckXml.SelectSingleNode( 'Connector/Error' ) ;
+ var iErrorNumber = parseInt( oNode.attributes.getNamedItem('number').value ) ;
+
+ switch ( iErrorNumber )
+ {
+ case 0 :
+ window.parent.frames['frmResourcesList'].Refresh() ;
+ break ;
+ case 101 :
+ alert( 'Folder already exists' ) ;
+ break ;
+ case 102 :
+ alert( 'Invalid folder name' ) ;
+ break ;
+ case 103 :
+ alert( 'You have no permissions to create the folder' ) ;
+ break ;
+ case 110 :
+ alert( 'Unknown error creating folder' ) ;
+ break ;
+ default :
+ alert( 'Error creating folder. Error number: ' + iErrorNumber ) ;
+ break ;
+ }
+ */
+}
+
+window.onload = function()
+{
+ window.top.IsLoadedCreateFolder = true ;
+}
+ </script>
+ </head>
+ <body bottomMargin="0" topMargin="0">
+ <table height="100%" cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td>
+ <button type="button" style="WIDTH: 100%" onclick="CreateFolder();">
+ <table cellSpacing="0" cellPadding="0" border="0">
+ <tr>
+ <td><img height="16" alt="" src="images/Folder.gif" width="16"></td>
+ <td>&nbsp;</td>
+ <td nowrap>Create New Folder</td>
+ </tr>
+ </table>
+ </button>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmfolders.html b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmfolders.html
new file mode 100644
index 0000000..2dc0eb0
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmfolders.html
@@ -0,0 +1,196 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This page shows the list of folders available in the parent folder
+ * of the current folder.
+-->
+<html>
+ <head>
+ <link href="browser.css" type="text/css" rel="stylesheet">
+ <script type="text/javascript" src="js/common.js"></script>
+ <script language="javascript">
+
+var sActiveFolder ;
+
+var bIsLoaded = false ;
+var iIntervalId ;
+
+var oListManager = new Object() ;
+
+oListManager.Init = function()
+{
+ this.Table = document.getElementById('tableFiles') ;
+ this.UpRow = document.getElementById('trUp') ;
+
+ this.TableRows = new Object() ;
+}
+
+oListManager.Clear = function()
+{
+ // Remove all other rows available.
+ while ( this.Table.rows.length > 1 )
+ this.Table.deleteRow(1) ;
+
+ // Reset the TableRows collection.
+ this.TableRows = new Object() ;
+}
+
+oListManager.AddItem = function( folderName, folderPath )
+{
+ // Create the new row.
+ var oRow = this.Table.insertRow(-1) ;
+ oRow.className = 'FolderListFolder' ;
+
+ // Build the link to view the folder.
+ var sLink = '<a href="#" onclick="OpenFolder(\'' + folderPath + '\');return false;">' ;
+
+ // Add the folder icon cell.
+ var oCell = oRow.insertCell(-1) ;
+ oCell.width = 16 ;
+ oCell.innerHTML = sLink + '<img alt="" src="images/spacer.gif" width="16" height="16" border="0"></a>' ;
+
+ // Add the folder name cell.
+ oCell = oRow.insertCell(-1) ;
+ oCell.noWrap = true ;
+ oCell.innerHTML = '&nbsp;' + sLink + folderName + '</a>' ;
+
+ this.TableRows[ folderPath ] = oRow ;
+}
+
+oListManager.ShowUpFolder = function( upFolderPath )
+{
+ this.UpRow.style.display = ( upFolderPath != null ? '' : 'none' ) ;
+
+ if ( upFolderPath != null )
+ {
+ document.getElementById('linkUpIcon').onclick = document.getElementById('linkUp').onclick = function()
+ {
+ LoadFolders( upFolderPath ) ;
+ return false ;
+ }
+ }
+}
+
+function CheckLoaded()
+{
+ if ( window.top.IsLoadedActualFolder
+ && window.top.IsLoadedCreateFolder
+ && window.top.IsLoadedUpload
+ && window.top.IsLoadedResourcesList )
+ {
+ window.clearInterval( iIntervalId ) ;
+ bIsLoaded = true ;
+ OpenFolder( sActiveFolder ) ;
+ }
+}
+
+function OpenFolder( folderPath )
+{
+ sActiveFolder = folderPath ;
+
+ if ( ! bIsLoaded )
+ {
+ if ( ! iIntervalId )
+ iIntervalId = window.setInterval( CheckLoaded, 100 ) ;
+ return ;
+ }
+
+ // Change the style for the select row (to show the opened folder).
+ for ( var sFolderPath in oListManager.TableRows )
+ {
+ oListManager.TableRows[ sFolderPath ].className =
+ ( sFolderPath == folderPath ? 'FolderListCurrentFolder' : 'FolderListFolder' ) ;
+ }
+
+ // Set the current folder in all frames.
+ window.parent.frames['frmActualFolder'].SetCurrentFolder( oConnector.ResourceType, folderPath ) ;
+ window.parent.frames['frmCreateFolder'].SetCurrentFolder( oConnector.ResourceType, folderPath ) ;
+ window.parent.frames['frmUpload'].SetCurrentFolder( oConnector.ResourceType, folderPath ) ;
+
+ // Load the resources list for this folder.
+ window.parent.frames['frmResourcesList'].LoadResources( oConnector.ResourceType, folderPath ) ;
+}
+
+function LoadFolders( folderPath )
+{
+ // Clear the folders list.
+ oListManager.Clear() ;
+
+ // Get the parent folder path.
+ var sParentFolderPath ;
+ if ( folderPath != '/' )
+ sParentFolderPath = folderPath.substring( 0, folderPath.lastIndexOf( '/', folderPath.length - 2 ) + 1 ) ;
+
+ // Show/Hide the Up Folder.
+ oListManager.ShowUpFolder( sParentFolderPath ) ;
+
+ if ( folderPath != '/' )
+ {
+ sActiveFolder = folderPath ;
+ oConnector.CurrentFolder = sParentFolderPath ;
+ oConnector.SendCommand( 'GetFolders', null, GetFoldersCallBack ) ;
+ }
+ else
+ OpenFolder( '/' ) ;
+}
+
+function GetFoldersCallBack( fckXml )
+{
+ if ( oConnector.CheckError( fckXml ) != 0 )
+ return ;
+
+ // Get the current folder path.
+ var oNode = fckXml.SelectSingleNode( 'Connector/CurrentFolder' ) ;
+ var sCurrentFolderPath = oNode.attributes.getNamedItem('path').value ;
+
+ var oNodes = fckXml.SelectNodes( 'Connector/Folders/Folder' ) ;
+
+ for ( var i = 0 ; i < oNodes.length ; i++ )
+ {
+ var sFolderName = oNodes[i].attributes.getNamedItem('name').value ;
+ oListManager.AddItem( sFolderName, sCurrentFolderPath + sFolderName + "/" ) ;
+ }
+
+ OpenFolder( sActiveFolder ) ;
+}
+
+function SetResourceType( type )
+{
+ oConnector.ResourceType = type ;
+ LoadFolders( '/' ) ;
+}
+
+window.onload = function()
+{
+ oListManager.Init() ;
+ LoadFolders( '/' ) ;
+}
+ </script>
+ </head>
+ <body class="FileArea" bottomMargin="10" leftMargin="10" topMargin="10" rightMargin="10">
+ <table id="tableFiles" cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr id="trUp" style="DISPLAY: none">
+ <td width="16"><a id="linkUpIcon" href="#"><img alt="" src="images/FolderUp.gif" width="16" height="16" border="0"></a></td>
+ <td nowrap width="100%">&nbsp;<a id="linkUp" href="#">..</a></td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourceslist.html b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourceslist.html
new file mode 100644
index 0000000..3f041f7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourceslist.html
@@ -0,0 +1,160 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This page shows all resources available in a folder in the File Browser.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <link href="browser.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="js/common.js"></script>
+ <script type="text/javascript">
+
+var oListManager = new Object() ;
+
+oListManager.Clear = function()
+{
+ document.body.innerHTML = '' ;
+}
+
+oListManager.GetFolderRowHtml = function( folderName, folderPath )
+{
+ // Build the link to view the folder.
+ var sLink = '<a href="#" onclick="OpenFolder(\'' + folderPath.replace( /'/g, '\\\'') + '\');return false;">' ;
+
+ return '<tr>' +
+ '<td width="16">' +
+ sLink +
+ '<img alt="" src="images/Folder.gif" width="16" height="16" border="0"></a>' +
+ '</td><td nowrap colspan="2">&nbsp;' +
+ sLink +
+ folderName +
+ '</a>' +
+ '</td></tr>' ;
+}
+
+oListManager.GetFileRowHtml = function( fileName, fileUrl, fileSize )
+{
+ // Build the link to view the folder.
+ var sLink = '<a href="#" onclick="OpenFile(\'' + fileUrl.replace( /'/g, '\\\'') + '\');return false;">' ;
+
+ // Get the file icon.
+ var sIcon = oIcons.GetIcon( fileName ) ;
+
+ return '<tr>' +
+ '<td width="16">' +
+ sLink +
+ '<img alt="" src="images/icons/' + sIcon + '.gif" width="16" height="16" border="0"></a>' +
+ '</td><td>&nbsp;' +
+ sLink +
+ fileName +
+ '</a>' +
+ '</td><td align="right" nowrap>&nbsp;' +
+ fileSize +
+ ' KB' +
+ '</td></tr>' ;
+}
+
+function OpenFolder( folderPath )
+{
+ // Load the resources list for this folder.
+ window.parent.frames['frmFolders'].LoadFolders( folderPath ) ;
+}
+
+function OpenFile( fileUrl )
+{
+ window.top.opener.SetUrl( encodeURI( fileUrl ) ) ;
+ window.top.close() ;
+ window.top.opener.focus() ;
+}
+
+function LoadResources( resourceType, folderPath )
+{
+ oListManager.Clear() ;
+ oConnector.ResourceType = resourceType ;
+ oConnector.CurrentFolder = folderPath ;
+ oConnector.SendCommand( 'GetFoldersAndFiles', null, GetFoldersAndFilesCallBack ) ;
+}
+
+function Refresh()
+{
+ LoadResources( oConnector.ResourceType, oConnector.CurrentFolder ) ;
+}
+
+function GetFoldersAndFilesCallBack( fckXml )
+{
+ if ( oConnector.CheckError( fckXml ) != 0 )
+ return ;
+
+ // Get the current folder path.
+ var oFolderNode = fckXml.SelectSingleNode( 'Connector/CurrentFolder' ) ;
+ if ( oFolderNode == null )
+ {
+ alert( 'The server didn\'t reply with a proper XML data. Please check your configuration.' ) ;
+ return ;
+ }
+ var sCurrentFolderPath = oFolderNode.attributes.getNamedItem('path').value ;
+ var sCurrentFolderUrl = oFolderNode.attributes.getNamedItem('url').value ;
+
+// var dTimer = new Date() ;
+
+ var oHtml = new StringBuilder( '<table id="tableFiles" cellspacing="1" cellpadding="0" width="100%" border="0">' ) ;
+
+ // Add the Folders.
+ var oNodes ;
+ oNodes = fckXml.SelectNodes( 'Connector/Folders/Folder' ) ;
+ for ( var i = 0 ; i < oNodes.length ; i++ )
+ {
+ var sFolderName = oNodes[i].attributes.getNamedItem('name').value ;
+ oHtml.Append( oListManager.GetFolderRowHtml( sFolderName, sCurrentFolderPath + sFolderName + "/" ) ) ;
+ }
+
+ // Add the Files.
+ oNodes = fckXml.SelectNodes( 'Connector/Files/File' ) ;
+ for ( var j = 0 ; j < oNodes.length ; j++ )
+ {
+ var oNode = oNodes[j] ;
+ var sFileName = oNode.attributes.getNamedItem('name').value ;
+ var sFileSize = oNode.attributes.getNamedItem('size').value ;
+
+ // Get the optional "url" attribute. If not available, build the url.
+ var oFileUrlAtt = oNodes[j].attributes.getNamedItem('url') ;
+ var sFileUrl = oFileUrlAtt != null ? oFileUrlAtt.value : sCurrentFolderUrl + sFileName ;
+
+ oHtml.Append( oListManager.GetFileRowHtml( sFileName, sFileUrl, sFileSize ) ) ;
+ }
+
+ oHtml.Append( '<\/table>' ) ;
+
+ document.body.innerHTML = oHtml.ToString() ;
+
+// window.top.document.title = 'Finished processing in ' + ( ( ( new Date() ) - dTimer ) / 1000 ) + ' seconds' ;
+
+}
+
+window.onload = function()
+{
+ window.top.IsLoadedResourcesList = true ;
+}
+ </script>
+</head>
+<body class="FileArea" bottommargin="10" leftmargin="10" topmargin="10" rightmargin="10">
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourcetype.html b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourcetype.html
new file mode 100644
index 0000000..933e855
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmresourcetype.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This page shows the list of available resource types.
+-->
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <link href="browser.css" type="text/css" rel="stylesheet">
+ <script type="text/javascript" src="js/common.js"></script>
+ <script language="javascript">
+
+function SetResourceType( type )
+{
+ window.parent.frames["frmFolders"].SetResourceType( type ) ;
+}
+
+var aTypes = [
+ ['File','File'],
+ ['Image','Image'],
+ ['Flash','Flash'],
+ ['Media','Media']
+] ;
+
+window.onload = function()
+{
+ for ( var i = 0 ; i < aTypes.length ; i++ )
+ {
+ if ( oConnector.ShowAllTypes || aTypes[i][0] == oConnector.ResourceType )
+ AddSelectOption( document.getElementById('cmbType'), aTypes[i][1], aTypes[i][0] ) ;
+ }
+}
+
+ </script>
+ </head>
+ <body bottomMargin="0" topMargin="0">
+ <table height="100%" cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td nowrap>
+ Resource Type<BR>
+ <select id="cmbType" style="WIDTH: 100%" onchange="SetResourceType(this.value);">
+ </select>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmupload.html b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmupload.html
new file mode 100644
index 0000000..b84882d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/frmupload.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Page used to upload new files in the current folder.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <link href="browser.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="js/common.js"></script>
+ <script type="text/javascript">
+
+function SetCurrentFolder( resourceType, folderPath )
+{
+ var sUrl = oConnector.ConnectorUrl + 'Command=FileUpload' ;
+ sUrl += '&Type=' + resourceType ;
+ sUrl += '&CurrentFolder=' + encodeURIComponent( folderPath ) ;
+
+ document.getElementById('frmUpload').action = sUrl ;
+}
+
+function OnSubmit()
+{
+ if ( document.getElementById('NewFile').value.length == 0 )
+ {
+ alert( 'Please select a file from your computer' ) ;
+ return false ;
+ }
+
+ // Set the interface elements.
+ document.getElementById('eUploadMessage').innerHTML = 'Upload a new file in this folder (Upload in progress, please wait...)' ;
+ document.getElementById('btnUpload').disabled = true ;
+
+ return true ;
+}
+
+function OnUploadCompleted( errorNumber, data )
+{
+ // Reset the Upload Worker Frame.
+ window.parent.frames['frmUploadWorker'].location = 'javascript:void(0)' ;
+
+ // Reset the upload form (On IE we must do a little trick to avout problems).
+ if ( document.all )
+ document.getElementById('NewFile').outerHTML = '<input id="NewFile" name="NewFile" style="WIDTH: 100%" type="file">' ;
+ else
+ document.getElementById('frmUpload').reset() ;
+
+ // Reset the interface elements.
+ document.getElementById('eUploadMessage').innerHTML = 'Upload a new file in this folder' ;
+ document.getElementById('btnUpload').disabled = false ;
+
+ switch ( errorNumber )
+ {
+ case 0 :
+ window.parent.frames['frmResourcesList'].Refresh() ;
+ break ;
+ case 1 : // Custom error.
+ alert( data ) ;
+ break ;
+ case 201 :
+ window.parent.frames['frmResourcesList'].Refresh() ;
+ alert( 'A file with the same name is already available. The uploaded file has been renamed to "' + data + '"' ) ;
+ break ;
+ case 202 :
+ alert( 'Invalid file' ) ;
+ break ;
+ default :
+ alert( 'Error on file upload. Error number: ' + errorNumber ) ;
+ break ;
+ }
+}
+
+window.onload = function()
+{
+ window.top.IsLoadedUpload = true ;
+}
+ </script>
+ </head>
+ <body bottommargin="0" topmargin="0">
+ <form id="frmUpload" action="" target="frmUploadWorker" method="post" enctype="multipart/form-data" onsubmit="return OnSubmit();">
+ <table height="100%" cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td nowrap="nowrap">
+ <span id="eUploadMessage">Upload a new file in this folder</span><br>
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tr>
+ <td width="100%"><input id="NewFile" name="NewFile" style="WIDTH: 100%" type="file"></td>
+ <td nowrap="nowrap">&nbsp;<input id="btnUpload" type="submit" value="Upload"></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </form>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/ButtonArrow.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/ButtonArrow.gif
new file mode 100644
index 0000000..a355e5a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/ButtonArrow.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder.gif
new file mode 100644
index 0000000..ab6824d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder32.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder32.gif
new file mode 100644
index 0000000..b93b752
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/Folder32.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened.gif
new file mode 100644
index 0000000..0c5dd41
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened32.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened32.gif
new file mode 100644
index 0000000..3e3fcf5
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderOpened32.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderUp.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderUp.gif
new file mode 100644
index 0000000..ad5bc20
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/FolderUp.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ai.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ai.gif
new file mode 100644
index 0000000..699e6a3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ai.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/avi.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/avi.gif
new file mode 100644
index 0000000..97025bb
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/avi.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/bmp.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/bmp.gif
new file mode 100644
index 0000000..f3c7f82
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/bmp.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/cs.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/cs.gif
new file mode 100644
index 0000000..b62bd02
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/cs.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/default.icon.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/default.icon.gif
new file mode 100644
index 0000000..976997b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/default.icon.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/dll.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/dll.gif
new file mode 100644
index 0000000..9b54964
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/dll.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/doc.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/doc.gif
new file mode 100644
index 0000000..b557568
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/doc.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/exe.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/exe.gif
new file mode 100644
index 0000000..7584993
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/exe.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/fla.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/fla.gif
new file mode 100644
index 0000000..923079f
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/fla.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/gif.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/gif.gif
new file mode 100644
index 0000000..df5f579
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/gif.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/htm.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/htm.gif
new file mode 100644
index 0000000..a9bdf00
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/htm.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/html.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/html.gif
new file mode 100644
index 0000000..a9bdf00
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/html.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/jpg.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/jpg.gif
new file mode 100644
index 0000000..de78363
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/jpg.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/js.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/js.gif
new file mode 100644
index 0000000..fe0c98e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/js.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mdb.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mdb.gif
new file mode 100644
index 0000000..d3af9e8
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mdb.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mp3.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mp3.gif
new file mode 100644
index 0000000..7d6360f
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/mp3.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/pdf.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/pdf.gif
new file mode 100644
index 0000000..4950ec8
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/pdf.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/png.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/png.gif
new file mode 100644
index 0000000..0a79ebf
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/png.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ppt.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ppt.gif
new file mode 100644
index 0000000..023431c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/ppt.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/rdp.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/rdp.gif
new file mode 100644
index 0000000..b9eace7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/rdp.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swf.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swf.gif
new file mode 100644
index 0000000..5df7de5
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swf.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swt.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swt.gif
new file mode 100644
index 0000000..7807c07
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/swt.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/txt.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/txt.gif
new file mode 100644
index 0000000..4e2c2e3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/txt.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/vsd.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/vsd.gif
new file mode 100644
index 0000000..7624697
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/vsd.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xls.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xls.gif
new file mode 100644
index 0000000..afe724a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xls.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xml.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xml.gif
new file mode 100644
index 0000000..4fae356
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/xml.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/zip.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/zip.gif
new file mode 100644
index 0000000..7157f72
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/32/zip.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ai.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ai.gif
new file mode 100644
index 0000000..ba5a913
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ai.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/avi.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/avi.gif
new file mode 100644
index 0000000..6f3bac9
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/avi.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/bmp.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/bmp.gif
new file mode 100644
index 0000000..7708dd8
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/bmp.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/cs.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/cs.gif
new file mode 100644
index 0000000..4d92723
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/cs.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/default.icon.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/default.icon.gif
new file mode 100644
index 0000000..6ce26a4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/default.icon.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/dll.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/dll.gif
new file mode 100644
index 0000000..48d445a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/dll.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/doc.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/doc.gif
new file mode 100644
index 0000000..6535b4c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/doc.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/exe.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/exe.gif
new file mode 100644
index 0000000..315817f
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/exe.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/fla.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/fla.gif
new file mode 100644
index 0000000..8f91a98
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/fla.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/gif.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/gif.gif
new file mode 100644
index 0000000..a5e3e6c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/gif.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/htm.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/htm.gif
new file mode 100644
index 0000000..0b5d6ba
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/htm.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/html.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/html.gif
new file mode 100644
index 0000000..0b5d6ba
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/html.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/jpg.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/jpg.gif
new file mode 100644
index 0000000..634b386
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/jpg.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/js.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/js.gif
new file mode 100644
index 0000000..4ea17d4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/js.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mdb.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mdb.gif
new file mode 100644
index 0000000..0d7c102
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mdb.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mp3.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mp3.gif
new file mode 100644
index 0000000..6f3bac9
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/mp3.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/pdf.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/pdf.gif
new file mode 100644
index 0000000..ca1f94a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/pdf.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/png.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/png.gif
new file mode 100644
index 0000000..b6d1b32
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/png.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ppt.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ppt.gif
new file mode 100644
index 0000000..877a8c8
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/ppt.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/rdp.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/rdp.gif
new file mode 100644
index 0000000..916cd7e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/rdp.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swf.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swf.gif
new file mode 100644
index 0000000..314469d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swf.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swt.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swt.gif
new file mode 100644
index 0000000..314469d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/swt.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/txt.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/txt.gif
new file mode 100644
index 0000000..1511ba3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/txt.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/vsd.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/vsd.gif
new file mode 100644
index 0000000..9be3daa
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/vsd.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xls.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xls.gif
new file mode 100644
index 0000000..f57715d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xls.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xml.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xml.gif
new file mode 100644
index 0000000..4559928
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/xml.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/zip.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/zip.gif
new file mode 100644
index 0000000..b1e2492
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/icons/zip.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/spacer.gif b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/spacer.gif
new file mode 100644
index 0000000..35d42e8
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/images/spacer.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/js/common.js b/httemplate/elements/fckeditor/editor/filemanager/browser/default/js/common.js
new file mode 100644
index 0000000..2f47217
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/js/common.js
@@ -0,0 +1,55 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Common objects and functions shared by all pages that compose the
+ * File Browser dialog window.
+ */
+
+function AddSelectOption( selectElement, optionText, optionValue )
+{
+ var oOption = document.createElement("OPTION") ;
+
+ oOption.text = optionText ;
+ oOption.value = optionValue ;
+
+ selectElement.options.add(oOption) ;
+
+ return oOption ;
+}
+
+var oConnector = window.parent.oConnector ;
+var oIcons = window.parent.oIcons ;
+
+
+function StringBuilder( value )
+{
+ this._Strings = new Array( value || '' ) ;
+}
+
+StringBuilder.prototype.Append = function( value )
+{
+ if ( value )
+ this._Strings.push( value ) ;
+}
+
+StringBuilder.prototype.ToString = function()
+{
+ return this._Strings.join( '' ) ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/filemanager/browser/default/js/fckxml.js b/httemplate/elements/fckeditor/editor/filemanager/browser/default/js/fckxml.js
new file mode 100644
index 0000000..043ca84
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/browser/default/js/fckxml.js
@@ -0,0 +1,129 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Defines the FCKXml object that is used for XML data calls
+ * and XML processing.
+ *
+ * This script is shared by almost all pages that compose the
+ * File Browser frameset.
+ */
+
+var FCKXml = function()
+{}
+
+FCKXml.prototype.GetHttpRequest = function()
+{
+ // Gecko / IE7
+ if ( typeof(XMLHttpRequest) != 'undefined' )
+ return new XMLHttpRequest() ;
+
+ // IE6
+ try { return new ActiveXObject( 'Msxml2.XMLHTTP' ) ; }
+ catch(e) {}
+
+ // IE5
+ try { return new ActiveXObject( 'Microsoft.XMLHTTP' ) ; }
+ catch(e) {}
+
+ return null ;
+}
+
+FCKXml.prototype.LoadUrl = function( urlToCall, asyncFunctionPointer )
+{
+ var oFCKXml = this ;
+
+ var bAsync = ( typeof(asyncFunctionPointer) == 'function' ) ;
+
+ var oXmlHttp = this.GetHttpRequest() ;
+
+ oXmlHttp.open( "GET", urlToCall, bAsync ) ;
+
+ if ( bAsync )
+ {
+ oXmlHttp.onreadystatechange = function()
+ {
+ if ( oXmlHttp.readyState == 4 )
+ {
+ if ( ( oXmlHttp.status != 200 && oXmlHttp.status != 304 ) || oXmlHttp.responseXML == null || oXmlHttp.responseXML.firstChild == null )
+ {
+ alert( 'The server didn\'t send back a proper XML response. Please contact your system administrator.\n\n' +
+ 'XML request error: ' + oXmlHttp.statusText + ' (' + oXmlHttp.status + ')\n\n' +
+ 'Requested URL:\n' + urlToCall + '\n\n' +
+ 'Response text:\n' + oXmlHttp.responseText ) ;
+ return ;
+ }
+
+ oFCKXml.DOMDocument = oXmlHttp.responseXML ;
+ asyncFunctionPointer( oFCKXml ) ;
+ }
+ }
+ }
+
+ oXmlHttp.send( null ) ;
+
+ if ( ! bAsync )
+ {
+ if ( oXmlHttp.status == 200 || oXmlHttp.status == 304 )
+ this.DOMDocument = oXmlHttp.responseXML ;
+ else
+ {
+ alert( 'XML request error: ' + oXmlHttp.statusText + ' (' + oXmlHttp.status + ')' ) ;
+ }
+ }
+}
+
+FCKXml.prototype.SelectNodes = function( xpath )
+{
+ if ( navigator.userAgent.indexOf('MSIE') >= 0 ) // IE
+ return this.DOMDocument.selectNodes( xpath ) ;
+ else // Gecko
+ {
+ var aNodeArray = new Array();
+
+ var xPathResult = this.DOMDocument.evaluate( xpath, this.DOMDocument,
+ this.DOMDocument.createNSResolver(this.DOMDocument.documentElement), XPathResult.ORDERED_NODE_ITERATOR_TYPE, null) ;
+ if ( xPathResult )
+ {
+ var oNode = xPathResult.iterateNext() ;
+ while( oNode )
+ {
+ aNodeArray[aNodeArray.length] = oNode ;
+ oNode = xPathResult.iterateNext();
+ }
+ }
+ return aNodeArray ;
+ }
+}
+
+FCKXml.prototype.SelectSingleNode = function( xpath )
+{
+ if ( navigator.userAgent.indexOf('MSIE') >= 0 ) // IE
+ return this.DOMDocument.selectSingleNode( xpath ) ;
+ else // Gecko
+ {
+ var xPathResult = this.DOMDocument.evaluate( xpath, this.DOMDocument,
+ this.DOMDocument.createNSResolver(this.DOMDocument.documentElement), 9, null);
+
+ if ( xPathResult && xPathResult.singleNodeValue )
+ return xPathResult.singleNodeValue ;
+ else
+ return null ;
+ }
+}
diff --git a/httemplate/elements/fckeditor/editor/filemanager/upload/test.html b/httemplate/elements/fckeditor/editor/filemanager/upload/test.html
new file mode 100644
index 0000000..cf29e97
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/filemanager/upload/test.html
@@ -0,0 +1,133 @@
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Test page for the "File Uploaders".
+-->
+<html>
+ <head>
+ <title>FCKeditor - Uploaders Tests</title>
+ <script language="javascript">
+
+function SendFile()
+{
+ var sUploaderUrl = cmbUploaderUrl.value ;
+
+ if ( sUploaderUrl.length == 0 )
+ sUploaderUrl = txtCustomUrl.value ;
+
+ if ( sUploaderUrl.length == 0 )
+ {
+ alert( 'Please provide your custom URL or select a default one' ) ;
+ return ;
+ }
+
+ eURL.innerHTML = sUploaderUrl ;
+ txtUrl.value = '' ;
+
+ frmUpload.action = sUploaderUrl ;
+ frmUpload.submit() ;
+}
+
+function OnUploadCompleted( errorNumber, fileUrl, fileName, customMsg )
+{
+ switch ( errorNumber )
+ {
+ case 0 : // No errors
+ txtUrl.value = fileUrl ;
+ alert( 'File uploaded with no errors' ) ;
+ break ;
+ case 1 : // Custom error
+ alert( customMsg ) ;
+ break ;
+ case 10 : // Custom warning
+ txtUrl.value = fileUrl ;
+ alert( customMsg ) ;
+ break ;
+ case 201 :
+ txtUrl.value = fileUrl ;
+ alert( 'A file with the same name is already available. The uploaded file has been renamed to "' + fileName + '"' ) ;
+ break ;
+ case 202 :
+ alert( 'Invalid file' ) ;
+ break ;
+ case 203 :
+ alert( "Security error. You probably don't have enough permissions to upload. Please check your server." ) ;
+ break ;
+ default :
+ alert( 'Error on file upload. Error number: ' + errorNumber ) ;
+ break ;
+ }
+}
+
+ </script>
+ </head>
+ <body>
+ <table cellSpacing="0" cellPadding="0" width="100%" border="0" height="100%">
+ <tr>
+ <td>
+ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td nowrap>
+ Select the "File Uploader" to use:<br>
+ <select id="cmbUploaderUrl">
+ <option selected value="asp/upload.asp">ASP</option>
+ <option value="aspx/upload.aspx">ASP.Net</option>
+ <option value="cfm/upload.cfm">ColdFusion</option>
+ <option value="lasso/upload.lasso">Lasso</option>
+ <option value="php/upload.php">PHP</option>
+ <option value="">(Custom)</option>
+ </select>
+ </td>
+ <td nowrap>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ <td width="100%">
+ Custom Uploader URL:<BR>
+ <input id="txtCustomUrl" style="WIDTH: 100%; BACKGROUND-COLOR: #dcdcdc" disabled type="text">
+ </td>
+ </tr>
+ </table>
+ <br>
+ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td noWrap>
+ <form id="frmUpload" target="UploadWindow" enctype="multipart/form-data" action="" method="post">
+ Upload a new file:<br>
+ <input type="file" name="NewFile"><br>
+ <input type="button" value="Send it to the Server" onclick="SendFile();">
+ </form>
+ </td>
+ <td style="WIDTH: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ <td vAlign="top" width="100%">
+ Uploaded File URL:<br>
+ <INPUT id="txtUrl" style="WIDTH: 100%" readonly type="text">
+ </td>
+ </tr>
+ </table>
+ <br>
+ Post URL: <span id="eURL">&nbsp;</span>
+ </td>
+ </tr>
+ <tr>
+ <td height="100%">
+ <iframe name="UploadWindow" width="100%" height="100%" src="javascript:void(0)"></iframe>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/images/anchor.gif b/httemplate/elements/fckeditor/editor/images/anchor.gif
new file mode 100644
index 0000000..5aa797b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/anchor.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/arrow_ltr.gif b/httemplate/elements/fckeditor/editor/images/arrow_ltr.gif
new file mode 100644
index 0000000..9c59bfe
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/arrow_ltr.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/arrow_rtl.gif b/httemplate/elements/fckeditor/editor/images/arrow_rtl.gif
new file mode 100644
index 0000000..22e8649
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/arrow_rtl.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/angel_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/angel_smile.gif
new file mode 100644
index 0000000..a95e053
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/angel_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/angry_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/angry_smile.gif
new file mode 100644
index 0000000..c667c5d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/angry_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/broken_heart.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/broken_heart.gif
new file mode 100644
index 0000000..938cce1
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/broken_heart.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/cake.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/cake.gif
new file mode 100644
index 0000000..f6489d7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/cake.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/confused_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/confused_smile.gif
new file mode 100644
index 0000000..aeb0539
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/confused_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/cry_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/cry_smile.gif
new file mode 100644
index 0000000..0758f42
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/cry_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/devil_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/devil_smile.gif
new file mode 100644
index 0000000..15518d7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/devil_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/embaressed_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/embaressed_smile.gif
new file mode 100644
index 0000000..c431946
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/embaressed_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/envelope.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/envelope.gif
new file mode 100644
index 0000000..66d3656
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/envelope.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/heart.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/heart.gif
new file mode 100644
index 0000000..305714f
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/heart.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/kiss.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/kiss.gif
new file mode 100644
index 0000000..f840ea6
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/kiss.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/lightbulb.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/lightbulb.gif
new file mode 100644
index 0000000..863be6e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/lightbulb.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/omg_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/omg_smile.gif
new file mode 100644
index 0000000..aabc7fd
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/omg_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/regular_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/regular_smile.gif
new file mode 100644
index 0000000..33f297e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/regular_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/sad_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/sad_smile.gif
new file mode 100644
index 0000000..dfb78ef
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/sad_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/shades_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/shades_smile.gif
new file mode 100644
index 0000000..157df77
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/shades_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/teeth_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/teeth_smile.gif
new file mode 100644
index 0000000..26b5a55
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/teeth_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_down.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_down.gif
new file mode 100644
index 0000000..f53ee72
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_down.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_up.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_up.gif
new file mode 100644
index 0000000..7e8c746
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/thumbs_up.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/tounge_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/tounge_smile.gif
new file mode 100644
index 0000000..b87ec44
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/tounge_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/whatchutalkingabout_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/whatchutalkingabout_smile.gif
new file mode 100644
index 0000000..c074122
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/whatchutalkingabout_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/smiley/msn/wink_smile.gif b/httemplate/elements/fckeditor/editor/images/smiley/msn/wink_smile.gif
new file mode 100644
index 0000000..eefe61d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/smiley/msn/wink_smile.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/images/spacer.gif b/httemplate/elements/fckeditor/editor/images/spacer.gif
new file mode 100644
index 0000000..5bfd67a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/images/spacer.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/js/fckeditorcode_gecko.js b/httemplate/elements/fckeditor/editor/js/fckeditorcode_gecko.js
new file mode 100644
index 0000000..8d5d31a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/js/fckeditorcode_gecko.js
@@ -0,0 +1,98 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This file has been compressed for better performance. The original source
+ * can be found at "editor/_source".
+ */
+
+var FCK_STATUS_NOTLOADED=window.parent.FCK_STATUS_NOTLOADED=0;var FCK_STATUS_ACTIVE=window.parent.FCK_STATUS_ACTIVE=1;var FCK_STATUS_COMPLETE=window.parent.FCK_STATUS_COMPLETE=2;var FCK_TRISTATE_OFF=window.parent.FCK_TRISTATE_OFF=0;var FCK_TRISTATE_ON=window.parent.FCK_TRISTATE_ON=1;var FCK_TRISTATE_DISABLED=window.parent.FCK_TRISTATE_DISABLED=-1;var FCK_UNKNOWN=window.parent.FCK_UNKNOWN=-9;var FCK_TOOLBARITEM_ONLYICON=window.parent.FCK_TOOLBARITEM_ONLYICON=0;var FCK_TOOLBARITEM_ONLYTEXT=window.parent.FCK_TOOLBARITEM_ONLYTEXT=1;var FCK_TOOLBARITEM_ICONTEXT=window.parent.FCK_TOOLBARITEM_ICONTEXT=2;var FCK_EDITMODE_WYSIWYG=window.parent.FCK_EDITMODE_WYSIWYG=0;var FCK_EDITMODE_SOURCE=window.parent.FCK_EDITMODE_SOURCE=1;var FCK_IMAGES_PATH='images/';var FCK_SPACER_PATH='images/spacer.gif';var CTRL=1000;var SHIFT=2000;var ALT=4000;
+String.prototype.Contains=function(A){return (this.indexOf(A)>-1);};String.prototype.Equals=function(){var A=arguments;if (A.length==1&&A[0].pop) A=A[0];for (var i=0;i<A.length;i++){if (this==A[i]) return true;};return false;};String.prototype.IEquals=function(){var A=this.toUpperCase();var B=arguments;if (B.length==1&&B[0].pop) B=B[0];for (var i=0;i<B.length;i++){if (A==B[i].toUpperCase()) return true;};return false;};String.prototype.ReplaceAll=function(A,B){var C=this;for (var i=0;i<A.length;i++){C=C.replace(A[i],B[i]);};return C;};Array.prototype.AddItem=function(A){var i=this.length;this[i]=A;return i;};Array.prototype.IndexOf=function(A){for (var i=0;i<this.length;i++){if (this[i]==A) return i;};return-1;};String.prototype.StartsWith=function(A){return (this.substr(0,A.length)==A);};String.prototype.EndsWith=function(A,B){var C=this.length;var D=A.length;if (D>C) return false;if (B){var E=new RegExp(A+'$','i');return E.test(this);}else return (D==0||this.substr(C-D,D)==A);};String.prototype.Remove=function(A,B){var s='';if (A>0) s=this.substring(0,A);if (A+B<this.length) s+=this.substring(A+B,this.length);return s;};String.prototype.Trim=function(){return this.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g,'');};String.prototype.LTrim=function(){return this.replace(/^[ \t\n\r]*/g,'');};String.prototype.RTrim=function(){return this.replace(/[ \t\n\r]*$/g,'');};String.prototype.ReplaceNewLineChars=function(A){return this.replace(/\n/g,A);}
+var s=navigator.userAgent.toLowerCase();var FCKBrowserInfo={IsIE:s.Contains('msie'),IsIE7:s.Contains('msie 7'),IsGecko:s.Contains('gecko/'),IsSafari:s.Contains('safari'),IsOpera:s.Contains('opera'),IsMac:s.Contains('macintosh')};(function(A){A.IsGeckoLike=(A.IsGecko||A.IsSafari||A.IsOpera);if (A.IsGecko){var B=s.match(/gecko\/(\d+)/)[1];A.IsGecko10=((B<20051111)||(/rv:1\.7/.test(s)));}else A.IsGecko10=false;})(FCKBrowserInfo);
+var FCKURLParams={};(function(){var A=document.location.search.substr(1).split('&');for (var i=0;i<A.length;i++){var B=A[i].split('=');var C=decodeURIComponent(B[0]);var D=decodeURIComponent(B[1]);FCKURLParams[C]=D;}})();
+var FCKEvents=function(A){this.Owner=A;this._RegisteredEvents={};};FCKEvents.prototype.AttachEvent=function(A,B){var C;if (!(C=this._RegisteredEvents[A])) this._RegisteredEvents[A]=[B];else C.push(B);};FCKEvents.prototype.FireEvent=function(A,B){var C=true;var D=this._RegisteredEvents[A];if (D){for (var i=0;i<D.length;i++) C=(D[i](this.Owner,B)&&C);};return C;};
+var FCK={Name:FCKURLParams['InstanceName'],Status:0,EditMode:0,Toolbar:null,HasFocus:false,AttachToOnSelectionChange:function(A){this.Events.AttachEvent('OnSelectionChange',A);},GetLinkedFieldValue:function(){return this.LinkedField.value;},GetParentForm:function(){return this.LinkedField.form;},StartupValue:'',IsDirty:function(){if (this.EditMode==1) return (this.StartupValue!=this.EditingArea.Textarea.value);else return (this.StartupValue!=this.EditorDocument.body.innerHTML);},ResetIsDirty:function(){if (this.EditMode==1) this.StartupValue=this.EditingArea.Textarea.value;else if (this.EditorDocument.body) this.StartupValue=this.EditorDocument.body.innerHTML;},StartEditor:function(){this.TempBaseTag=FCKConfig.BaseHref.length>0?'<base href="'+FCKConfig.BaseHref+'" _fcktemp="true"></base>':'';var A=FCK.KeystrokeHandler=new FCKKeystrokeHandler();A.OnKeystroke=_FCK_KeystrokeHandler_OnKeystroke;A.SetKeystrokes(FCKConfig.Keystrokes);if (FCKBrowserInfo.IsIE7){if ((CTRL+86/*V*/) in A.Keystrokes) A.SetKeystrokes([CTRL+86,true]);if ((SHIFT+45/*INS*/) in A.Keystrokes) A.SetKeystrokes([SHIFT+45,true]);};this.EditingArea=new FCKEditingArea(document.getElementById('xEditingArea'));this.EditingArea.FFSpellChecker=FCKConfig.FirefoxSpellChecker;FCKListsLib.Setup();this.SetHTML(this.GetLinkedFieldValue(),true);},Focus:function(){FCK.EditingArea.Focus();},SetStatus:function(A){this.Status=A;if (A==1){FCKFocusManager.AddWindow(window,true);if (FCKBrowserInfo.IsIE) FCKFocusManager.AddWindow(window.frameElement,true);if (FCKConfig.StartupFocus) FCK.Focus();};this.Events.FireEvent('OnStatusChange',A);},FixBody:function(){var A=FCKConfig.EnterMode;if (A!='p'&&A!='div') return;var B=this.EditorDocument;if (!B) return;var C=B.body;if (!C) return;FCKDomTools.TrimNode(C);var D=C.firstChild;var E;while (D){var F=false;switch (D.nodeType){case 1:if (!FCKListsLib.BlockElements[D.nodeName.toLowerCase()]) F=true;break;case 3:if (E||D.nodeValue.Trim().length>0) F=true;};if (F){var G=D.parentNode;if (!E) E=G.insertBefore(B.createElement(A),D);E.appendChild(G.removeChild(D));D=E.nextSibling;}else{if (E){FCKDomTools.TrimNode(E);E=null;};D=D.nextSibling;}};if (E) FCKDomTools.TrimNode(E);},GetXHTML:function(A){if (FCK.EditMode==1) return FCK.EditingArea.Textarea.value;this.FixBody();var B;var C=FCK.EditorDocument;if (!C) return null;if (FCKConfig.FullPage){B=FCKXHtml.GetXHTML(C.getElementsByTagName('html')[0],true,A);if (FCK.DocTypeDeclaration&&FCK.DocTypeDeclaration.length>0) B=FCK.DocTypeDeclaration+'\n'+B;if (FCK.XmlDeclaration&&FCK.XmlDeclaration.length>0) B=FCK.XmlDeclaration+'\n'+B;}else{B=FCKXHtml.GetXHTML(C.body,false,A);if (FCKConfig.IgnoreEmptyParagraphValue&&FCKRegexLib.EmptyOutParagraph.test(B)) B='';};B=FCK.ProtectEventsRestore(B);if (FCKBrowserInfo.IsIE) B=B.replace(FCKRegexLib.ToReplace,'$1');return FCKConfig.ProtectedSource.Revert(B);},UpdateLinkedField:function(){FCK.LinkedField.value=FCK.GetXHTML(FCKConfig.FormatOutput);FCK.Events.FireEvent('OnAfterLinkedFieldUpdate');},RegisteredDoubleClickHandlers:{},OnDoubleClick:function(A){var B=FCK.RegisteredDoubleClickHandlers[A.tagName];if (B) B(A);},RegisterDoubleClickHandler:function(A,B){FCK.RegisteredDoubleClickHandlers[B.toUpperCase()]=A;},OnAfterSetHTML:function(){FCKDocumentProcessor.Process(FCK.EditorDocument);FCKUndo.SaveUndoStep();FCK.Events.FireEvent('OnSelectionChange');FCK.Events.FireEvent('OnAfterSetHTML');},ProtectUrls:function(A){A=A.replace(FCKRegexLib.ProtectUrlsA,'$& _fcksavedurl=$1');A=A.replace(FCKRegexLib.ProtectUrlsImg,'$& _fcksavedurl=$1');return A;},ProtectEvents:function(A){return A.replace(FCKRegexLib.TagsWithEvent,_FCK_ProtectEvents_ReplaceTags);},ProtectEventsRestore:function(A){return A.replace(FCKRegexLib.ProtectedEvents,_FCK_ProtectEvents_RestoreEvents);},ProtectTags:function(A){var B=FCKConfig.ProtectedTags;if (FCKBrowserInfo.IsIE) B+=B.length>0?'|ABBR|XML':'ABBR|XML';var C;if (B.length>0){C=new RegExp('<('+B+')(?!\w|:)','gi');A=A.replace(C,'<FCK:$1');C=new RegExp('<\/('+B+')>','gi');A=A.replace(C,'<\/FCK:$1>');};B='META';if (FCKBrowserInfo.IsIE) B+='|HR';C=new RegExp('<(('+B+')(?=\\s|>|/)[\\s\\S]*?)/?>','gi');A=A.replace(C,'<FCK:$1 />');return A;},SetHTML:function(A,B){this.EditingArea.Mode=FCK.EditMode;if (FCK.EditMode==0){A=FCKConfig.ProtectedSource.Protect(A);A=A.replace(FCKRegexLib.InvalidSelfCloseTags,'$1></$2>');A=FCK.ProtectEvents(A);A=FCK.ProtectUrls(A);A=FCK.ProtectTags(A);if (FCKBrowserInfo.IsGecko){A=A.replace(FCKRegexLib.StrongOpener,'<b$1');A=A.replace(FCKRegexLib.StrongCloser,'<\/b>');A=A.replace(FCKRegexLib.EmOpener,'<i$1');A=A.replace(FCKRegexLib.EmCloser,'<\/i>');};this._ForceResetIsDirty=(B===true);var C='';if (FCKConfig.FullPage){if (!FCKRegexLib.HeadOpener.test(A)){if (!FCKRegexLib.HtmlOpener.test(A)) A='<html dir="'+FCKConfig.ContentLangDirection+'">'+A+'</html>';A=A.replace(FCKRegexLib.HtmlOpener,'$&<head></head>');};FCK.DocTypeDeclaration=A.match(FCKRegexLib.DocTypeTag);if (FCKBrowserInfo.IsIE) C=FCK._GetBehaviorsStyle();else if (FCKConfig.ShowBorders) C='<link href="'+FCKConfig.FullBasePath+'css/fck_showtableborders_gecko.css" rel="stylesheet" type="text/css" _fcktemp="true" />';C+='<link href="'+FCKConfig.FullBasePath+'css/fck_internal.css" rel="stylesheet" type="text/css" _fcktemp="true" />';C=A.replace(FCKRegexLib.HeadCloser,C+'$&');if (FCK.TempBaseTag.length>0&&!FCKRegexLib.HasBaseTag.test(A)) C=C.replace(FCKRegexLib.HeadOpener,'$&'+FCK.TempBaseTag);}else{C=FCKConfig.DocType+'<html dir="'+FCKConfig.ContentLangDirection+'"';if (FCKBrowserInfo.IsIE&&!FCKRegexLib.Html4DocType.test(FCKConfig.DocType)) C+=' style="overflow-y: scroll"';C+='><head><title></title>'+_FCK_GetEditorAreaStyleTags()+'<link href="'+FCKConfig.FullBasePath+'css/fck_internal.css" rel="stylesheet" type="text/css" _fcktemp="true" />';if (FCKBrowserInfo.IsIE) C+=FCK._GetBehaviorsStyle();else if (FCKConfig.ShowBorders) C+='<link href="'+FCKConfig.FullBasePath+'css/fck_showtableborders_gecko.css" rel="stylesheet" type="text/css" _fcktemp="true" />';C+=FCK.TempBaseTag;var D='<body';if (FCKConfig.BodyId&&FCKConfig.BodyId.length>0) D+=' id="'+FCKConfig.BodyId+'"';if (FCKConfig.BodyClass&&FCKConfig.BodyClass.length>0) D+=' class="'+FCKConfig.BodyClass+'"';C+='</head>'+D+'>';if (FCKBrowserInfo.IsGecko&&(A.length==0||FCKRegexLib.EmptyParagraph.test(A))) C+=GECKO_BOGUS;else C+=A;C+='</body></html>';};this.EditingArea.OnLoad=_FCK_EditingArea_OnLoad;this.EditingArea.Start(C);}else{FCK.EditorWindow=null;FCK.EditorDocument=null;this.EditingArea.OnLoad=null;this.EditingArea.Start(A);this.EditingArea.Textarea._FCKShowContextMenu=true;FCK.EnterKeyHandler=null;if (B) this.ResetIsDirty();FCK.KeystrokeHandler.AttachToElement(this.EditingArea.Textarea);this.EditingArea.Textarea.focus();FCK.Events.FireEvent('OnAfterSetHTML');};if (FCKBrowserInfo.IsGecko) window.onresize();},HasFocus:false,RedirectNamedCommands:{},ExecuteNamedCommand:function(A,B,C){FCKUndo.SaveUndoStep();if (!C&&FCK.RedirectNamedCommands[A]!=null) FCK.ExecuteRedirectedNamedCommand(A,B);else{FCK.Focus();FCK.EditorDocument.execCommand(A,false,B);FCK.Events.FireEvent('OnSelectionChange');};FCKUndo.SaveUndoStep();},GetNamedCommandState:function(A){try{if (!FCK.EditorDocument.queryCommandEnabled(A)) return -1;else return FCK.EditorDocument.queryCommandState(A)?1:0;}catch (e){return 0;}},GetNamedCommandValue:function(A){var B='';var C=FCK.GetNamedCommandState(A);if (C==-1) return null;try{B=this.EditorDocument.queryCommandValue(A);}catch(e) {};return B?B:'';},PasteFromWord:function(){FCKDialog.OpenDialog('FCKDialog_Paste',FCKLang.PasteFromWord,'dialog/fck_paste.html',400,330,'Word');},Preview:function(){var A=FCKConfig.ScreenWidth*0.8;var B=FCKConfig.ScreenHeight*0.7;var C=(FCKConfig.ScreenWidth-A)/2;var D=window.open('',null,'toolbar=yes,location=no,status=yes,menubar=yes,scrollbars=yes,resizable=yes,width='+A+',height='+B+',left='+C);var E;if (FCKConfig.FullPage){if (FCK.TempBaseTag.length>0) E=FCK.TempBaseTag+FCK.GetXHTML();else E=FCK.GetXHTML();}else{E=FCKConfig.DocType+'<html dir="'+FCKConfig.ContentLangDirection+'"><head>'+FCK.TempBaseTag+'<title>'+FCKLang.Preview+'</title>'+_FCK_GetEditorAreaStyleTags()+'</head><body>'+FCK.GetXHTML()+'</body></html>';};D.document.write(E);D.document.close();},SwitchEditMode:function(A){var B=(FCK.EditMode==0);var C=FCK.IsDirty();var D;if (B){if (!A&&FCKBrowserInfo.IsIE) FCKUndo.SaveUndoStep();D=FCK.GetXHTML(FCKConfig.FormatSource);if (D==null) return false;}else D=this.EditingArea.Textarea.value;FCK.EditMode=B?1:0;FCK.SetHTML(D,!C);FCK.Focus();FCKTools.RunFunction(FCK.ToolbarSet.RefreshModeState,FCK.ToolbarSet);return true;},CreateElement:function(A){var e=FCK.EditorDocument.createElement(A);return FCK.InsertElementAndGetIt(e);},InsertElementAndGetIt:function(e){e.setAttribute('FCKTempLabel','true');this.InsertElement(e);var A=FCK.EditorDocument.getElementsByTagName(e.tagName);for (var i=0;i<A.length;i++){if (A[i].getAttribute('FCKTempLabel')){A[i].removeAttribute('FCKTempLabel');return A[i];}};return null;}};FCK.Events=new FCKEvents(FCK);FCK.GetHTML=FCK.GetXHTML;function _FCK_ProtectEvents_ReplaceTags(A){return A.replace(FCKRegexLib.EventAttributes,_FCK_ProtectEvents_ReplaceEvents);};function _FCK_ProtectEvents_ReplaceEvents(A,B){return ' '+B+'_fckprotectedatt="'+A.ReplaceAll([/&/g,/'/g,/"/g,/=/g,/</g,/>/g,/\r/g,/\n/g],['&apos;','&#39;','&quot;','&#61;','&lt;','&gt;','&#10;','&#13;'])+'"';};function _FCK_ProtectEvents_RestoreEvents(A,B){return B.ReplaceAll([/&#39;/g,/&quot;/g,/&#61;/g,/&lt;/g,/&gt;/g,/&#10;/g,/&#13;/g,/&apos;/g],["'",'"','=','<','>','\r','\n','&']);};function _FCK_EditingArea_OnLoad(){FCK.EditorWindow=FCK.EditingArea.Window;FCK.EditorDocument=FCK.EditingArea.Document;FCK.InitializeBehaviors();if (!FCKConfig.DisableEnterKeyHandler) FCK.EnterKeyHandler=new FCKEnterKey(FCK.EditorWindow,FCKConfig.EnterMode,FCKConfig.ShiftEnterMode);FCK.KeystrokeHandler.AttachToElement(FCK.EditorDocument);if (FCK._ForceResetIsDirty) FCK.ResetIsDirty();if (FCKBrowserInfo.IsIE&&FCK.HasFocus) FCK.EditorDocument.body.setActive();FCK.OnAfterSetHTML();if (FCK.Status!=0) return;FCK.SetStatus(1);};function _FCK_GetEditorAreaStyleTags(){var A='';var B=FCKConfig.EditorAreaCSS;for (var i=0;i<B.length;i++) A+='<link href="'+B[i]+'" rel="stylesheet" type="text/css" />';return A;};function _FCK_KeystrokeHandler_OnKeystroke(A,B){if (FCK.Status!=2) return false;if (FCK.EditMode==0){if (B=='Paste') return!FCK.Events.FireEvent('OnPaste');}else{if (B.Equals('Paste','Undo','Redo','SelectAll')) return false;};var C=FCK.Commands.GetCommand(B);return (C.Execute.apply(C,FCKTools.ArgumentsToArray(arguments,2))!==false);};(function(){var A=window.parent.document;var B=A.getElementById(FCK.Name);var i=0;while (B||i==0){if (B&&B.tagName.toLowerCase().Equals('input','textarea')){FCK.LinkedField=B;break;};B=A.getElementsByName(FCK.Name)[i++];}})();var FCKTempBin={Elements:[],AddElement:function(A){var B=this.Elements.length;this.Elements[B]=A;return B;},RemoveElement:function(A){var e=this.Elements[A];this.Elements[A]=null;return e;},Reset:function(){var i=0;while (i<this.Elements.length) this.Elements[i++]=null;this.Elements.length=0;}};var FCKFocusManager=FCK.FocusManager={IsLocked:false,AddWindow:function(A,B){var C;if (FCKBrowserInfo.IsIE) C=A.nodeType==1?A:A.frameElement?A.frameElement:A.document;else C=A.document;FCKTools.AddEventListener(C,'blur',FCKFocusManager_Win_OnBlur);FCKTools.AddEventListener(C,'focus',B?FCKFocusManager_Win_OnFocus_Area:FCKFocusManager_Win_OnFocus);},RemoveWindow:function(A){if (FCKBrowserInfo.IsIE) oTarget=A.nodeType==1?A:A.frameElement?A.frameElement:A.document;else oTarget=A.document;FCKTools.RemoveEventListener(oTarget,'blur',FCKFocusManager_Win_OnBlur);FCKTools.RemoveEventListener(oTarget,'focus',FCKFocusManager_Win_OnFocus_Area);FCKTools.RemoveEventListener(oTarget,'focus',FCKFocusManager_Win_OnFocus);},Lock:function(){this.IsLocked=true;},Unlock:function(){if (this._HasPendingBlur) FCKFocusManager._Timer=window.setTimeout(FCKFocusManager_FireOnBlur,100);this.IsLocked=false;},_ResetTimer:function(){this._HasPendingBlur=false;if (this._Timer){window.clearTimeout(this._Timer);delete this._Timer;}}};function FCKFocusManager_Win_OnBlur(){if (typeof(FCK)!='undefined'&&FCK.HasFocus){FCKFocusManager._ResetTimer();FCKFocusManager._Timer=window.setTimeout(FCKFocusManager_FireOnBlur,100);}};function FCKFocusManager_FireOnBlur(){if (FCKFocusManager.IsLocked) FCKFocusManager._HasPendingBlur=true;else{FCK.HasFocus=false;FCK.Events.FireEvent("OnBlur");}};function FCKFocusManager_Win_OnFocus_Area(){FCK.Focus();FCKFocusManager_Win_OnFocus();};function FCKFocusManager_Win_OnFocus(){FCKFocusManager._ResetTimer();if (!FCK.HasFocus&&!FCKFocusManager.IsLocked){FCK.HasFocus=true;FCK.Events.FireEvent("OnFocus");}};
+FCK.Description="FCKeditor for Gecko Browsers";FCK.InitializeBehaviors=function(){if (FCKBrowserInfo.IsGecko) Window_OnResize();FCKFocusManager.AddWindow(this.EditorWindow);this.ExecOnSelectionChange=function(){FCK.Events.FireEvent("OnSelectionChange");};this.ExecOnSelectionChangeTimer=function(){if (FCK.LastOnChangeTimer) window.clearTimeout(FCK.LastOnChangeTimer);FCK.LastOnChangeTimer=window.setTimeout(FCK.ExecOnSelectionChange,100);};this.EditorDocument.addEventListener('mouseup',this.ExecOnSelectionChange,false);this.EditorDocument.addEventListener('keyup',this.ExecOnSelectionChangeTimer,false);this._DblClickListener=function(e){FCK.OnDoubleClick(e.target);e.stopPropagation();};this.EditorDocument.addEventListener('dblclick',this._DblClickListener,true);FCK.ContextMenu._InnerContextMenu.SetMouseClickWindow(FCK.EditorWindow);FCK.ContextMenu._InnerContextMenu.AttachToElement(FCK.EditorDocument);};FCK.MakeEditable=function(){this.EditingArea.MakeEditable();};function Document_OnContextMenu(e){if (!e.target._FCKShowContextMenu) e.preventDefault();};document.oncontextmenu=Document_OnContextMenu;FCK._BaseGetNamedCommandState=FCK.GetNamedCommandState;FCK.GetNamedCommandState=function(A){switch (A){case 'Unlink':return FCKSelection.HasAncestorNode('A')?0:-1;default:return FCK._BaseGetNamedCommandState(A);}};FCK.RedirectNamedCommands={Print:true,Paste:true,Cut:true,Copy:true};FCK.ExecuteRedirectedNamedCommand=function(A,B){switch (A){case 'Print':FCK.EditorWindow.print();break;case 'Paste':try { if (FCK.Paste()) FCK.ExecuteNamedCommand('Paste',null,true);}catch (e) { FCKDialog.OpenDialog('FCKDialog_Paste',FCKLang.Paste,'dialog/fck_paste.html',400,330,'Security');};break;case 'Cut':try { FCK.ExecuteNamedCommand('Cut',null,true);}catch (e) { alert(FCKLang.PasteErrorCut);};break;case 'Copy':try { FCK.ExecuteNamedCommand('Copy',null,true);}catch (e) { alert(FCKLang.PasteErrorCopy);};break;default:FCK.ExecuteNamedCommand(A,B);}};FCK.Paste=function(){if (FCKConfig.ForcePasteAsPlainText){FCK.PasteAsPlainText();return false;};return true;};FCK.InsertHtml=function(A){A=FCKConfig.ProtectedSource.Protect(A);A=FCK.ProtectEvents(A);A=FCK.ProtectUrls(A);A=FCK.ProtectTags(A);A=A.replace(FCKRegexLib.StrongOpener,'<b$1');A=A.replace(FCKRegexLib.StrongCloser,'<\/b>');A=A.replace(FCKRegexLib.EmOpener,'<i$1');A=A.replace(FCKRegexLib.EmCloser,'<\/i>');var B=FCKSelection.Delete();var C=B.getRangeAt(0);var D=C.createContextualFragment(A);var E=D.lastChild;C.insertNode(D);FCKSelection.SelectNode(E);FCKSelection.Collapse(false);this.Focus();};FCK.InsertElement=function(A){var B=FCKSelection.Delete();var C=B.getRangeAt(0);C.insertNode(A);FCKSelection.SelectNode(A);FCKSelection.Collapse(false);this.Focus();};FCK.PasteAsPlainText=function(){FCKTools.RunFunction(FCKDialog.OpenDialog,FCKDialog,['FCKDialog_Paste',FCKLang.PasteAsText,'dialog/fck_paste.html',400,330,'PlainText']);};FCK.GetClipboardHTML=function(){return '';};FCK.CreateLink=function(A){var B=[];FCK.ExecuteNamedCommand('Unlink');if (A.length>0){var C='javascript:void(0);/*'+(new Date().getTime())+'*/';FCK.ExecuteNamedCommand('CreateLink',C);var D=this.EditorDocument.evaluate("//a[@href='"+C+"']",this.EditorDocument.body,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);for (var i=0;i<D.snapshotLength;i++){var E=D.snapshotItem(i);E.href=A;B.push(E);}};return B;};
+var FCKConfig=FCK.Config={};if (document.location.protocol=='file:'){FCKConfig.BasePath=decodeURIComponent(document.location.pathname.substr(1));FCKConfig.BasePath=FCKConfig.BasePath.replace(/\\/gi, '/');FCKConfig.BasePath='file://'+FCKConfig.BasePath.substring(0,FCKConfig.BasePath.lastIndexOf('/')+1);FCKConfig.FullBasePath=FCKConfig.BasePath;}else{FCKConfig.BasePath=document.location.pathname.substring(0,document.location.pathname.lastIndexOf('/')+1);FCKConfig.FullBasePath=document.location.protocol+'//'+document.location.host+FCKConfig.BasePath;};FCKConfig.EditorPath=FCKConfig.BasePath.replace(/editor\/$/,'');try{FCKConfig.ScreenWidth=screen.width;FCKConfig.ScreenHeight=screen.height;}catch (e){FCKConfig.ScreenWidth=800;FCKConfig.ScreenHeight=600;};FCKConfig.ProcessHiddenField=function(){this.PageConfig={};var A=window.parent.document.getElementById(FCK.Name+'___Config');if (!A) return;var B=A.value.split('&');for (var i=0;i<B.length;i++){if (B[i].length==0) continue;var C=B[i].split('=');var D=decodeURIComponent(C[0]);var E=decodeURIComponent(C[1]);if (D=='CustomConfigurationsPath') FCKConfig[D]=E;else if (E.toLowerCase()=="true") this.PageConfig[D]=true;else if (E.toLowerCase()=="false") this.PageConfig[D]=false;else if (E.length>0&&!isNaN(E)) this.PageConfig[D]=parseInt(E,10);else this.PageConfig[D]=E;}};function FCKConfig_LoadPageConfig(){var A=FCKConfig.PageConfig;for (var B in A) FCKConfig[B]=A[B];};function FCKConfig_PreProcess(){var A=FCKConfig;if (A.AllowQueryStringDebug){try{if ((/fckdebug=true/i).test(window.top.location.search)) A.Debug=true;}catch (e) {/*Ignore it. Much probably we are inside a FRAME where the "top" is in another domain (security error).*/}};if (!A.PluginsPath.EndsWith('/')) A.PluginsPath+='/';if (typeof(A.EditorAreaCSS)=='string') A.EditorAreaCSS=[A.EditorAreaCSS];var B=A.ToolbarComboPreviewCSS;if (!B||B.length==0) A.ToolbarComboPreviewCSS=A.EditorAreaCSS;else if (typeof(B)=='string') A.ToolbarComboPreviewCSS=[B];};FCKConfig.ToolbarSets={};FCKConfig.Plugins={};FCKConfig.Plugins.Items=[];FCKConfig.Plugins.Add=function(A,B,C){FCKConfig.Plugins.Items.AddItem([A,B,C]);};FCKConfig.ProtectedSource={};FCKConfig.ProtectedSource.RegexEntries=[/<!--[\s\S]*?-->/g,/<script[\s\S]*?<\/script>/gi,/<noscript[\s\S]*?<\/noscript>/gi,/<object[\s\S]+?<\/object>/gi];FCKConfig.ProtectedSource.Add=function(A){this.RegexEntries.AddItem(A);};FCKConfig.ProtectedSource.Protect=function(A){function _Replace(protectedSource){var B=FCKTempBin.AddElement(protectedSource);return '<!--{PS..'+B+'}-->';};for (var i=0;i<this.RegexEntries.length;i++){A=A.replace(this.RegexEntries[i],_Replace);};return A;};FCKConfig.ProtectedSource.Revert=function(A,B){function _Replace(m,opener,index){var C=B?FCKTempBin.RemoveElement(index):FCKTempBin.Elements[index];return FCKConfig.ProtectedSource.Revert(C,B);};return A.replace(/(<|&lt;)!--\{PS..(\d+)\}--(>|&gt;)/g,_Replace);}
+var FCKDebug={};FCKDebug._GetWindow=function(){if (!this.DebugWindow||this.DebugWindow.closed) this.DebugWindow=window.open(FCKConfig.BasePath+'fckdebug.html','FCKeditorDebug','menubar=no,scrollbars=yes,resizable=yes,location=no,toolbar=no,width=600,height=500',true);return this.DebugWindow;};FCKDebug.Output=function(A,B,C){if (!FCKConfig.Debug) return;try{this._GetWindow().Output(A,B);}catch (e) {}};FCKDebug.OutputObject=function(A,B){if (!FCKConfig.Debug) return;try{this._GetWindow().OutputObject(A,B);}catch (e) {}}
+var FCKDomTools={MoveChildren:function(A,B){if (A==B) return;var C;while ((C=A.firstChild)) B.appendChild(A.removeChild(C));},TrimNode:function(A,B){this.LTrimNode(A);this.RTrimNode(A,B);},LTrimNode:function(A){var B;while ((B=A.firstChild)){if (B.nodeType==3){var C=B.nodeValue.LTrim();var D=B.nodeValue.length;if (C.length==0){A.removeChild(B);continue;}else if (C.length<D){B.splitText(D-C.length);A.removeChild(A.firstChild);}};break;}},RTrimNode:function(A,B){var C;while ((C=A.lastChild)){switch (C.nodeType){case 1:if (C.nodeName.toUpperCase()=='BR'&&(B||C.getAttribute('type',2)=='_moz')){C.parentNode.removeChild(C);continue;};break;case 3:var D=C.nodeValue.RTrim();var E=C.nodeValue.length;if (D.length==0){C.parentNode.removeChild(C);continue;}else if (D.length<E){C.splitText(D.length);A.lastChild.parentNode.removeChild(A.lastChild);}};break;}},RemoveNode:function(A,B){if (B){var C;while ((C=A.firstChild)) A.parentNode.insertBefore(A.removeChild(C),A);};return A.parentNode.removeChild(A);},GetFirstChild:function(A,B){if (typeof (B)=='string') B=[B];var C=A.firstChild;while(C){if (C.nodeType==1&&C.tagName.Equals.apply(C.tagName,B)) return C;C=C.nextSibling;};return null;},GetLastChild:function(A,B){if (typeof (B)=='string') B=[B];var C=A.lastChild;while(C){if (C.nodeType==1&&(!B||C.tagName.Equals(B))) return C;C=C.previousSibling;};return null;},GetPreviousSourceElement:function(A,B,C,D){if (!A) return null;if (C&&A.nodeType==1&&A.nodeName.IEquals(C)) return null;if (A.previousSibling) A=A.previousSibling;else return this.GetPreviousSourceElement(A.parentNode,B,C,D);while (A){if (A.nodeType==1){if (C&&A.nodeName.IEquals(C)) break;if (!D||!A.nodeName.IEquals(D)) return A;}else if (B&&A.nodeType==3&&A.nodeValue.RTrim().length>0) break;if (A.lastChild) A=A.lastChild;else return this.GetPreviousSourceElement(A,B,C,D);};return null;},GetNextSourceElement:function(A,B,C,D){if (!A) return null;if (A.nextSibling) A=A.nextSibling;else return this.GetNextSourceElement(A.parentNode,B,C,D);while (A){if (A.nodeType==1){if (C&&A.nodeName.IEquals(C)) break;if (!D||!A.nodeName.IEquals(D)) return A;}else if (B&&A.nodeType==3&&A.nodeValue.RTrim().length>0) break;if (A.firstChild) A=A.firstChild;else return this.GetNextSourceElement(A,B,C,D);};return null;},InsertAfterNode:function(A,B){return A.parentNode.insertBefore(B,A.nextSibling);},GetParents:function(A){var B=[];while (A){B.splice(0,0,A);A=A.parentNode;};return B;},GetIndexOf:function(A){var B=A.parentNode?A.parentNode.firstChild:null;var C=-1;while (B){C++;if (B==A) return C;B=B.nextSibling;};return-1;}};
+var GECKO_BOGUS='<br type="_moz">';var FCKTools={};FCKTools.CreateBogusBR=function(A){var B=A.createElement('br');B.setAttribute('type','_moz');return B;};FCKTools.AppendStyleSheet=function(A,B){if (typeof(B)=='string') return this._AppendStyleSheet(A,B);else{var C=[];for (var i=0;i<B.length;i++) C.push(this._AppendStyleSheet(A,B[i]));return C;}};FCKTools.GetElementDocument=function (A){return A.ownerDocument||A.document;};FCKTools.GetElementWindow=function(A){return this.GetDocumentWindow(this.GetElementDocument(A));};FCKTools.GetDocumentWindow=function(A){if (FCKBrowserInfo.IsSafari&&!A.parentWindow) this.FixDocumentParentWindow(window.top);return A.parentWindow||A.defaultView;};FCKTools.FixDocumentParentWindow=function(A){A.document.parentWindow=A;for (var i=0;i<A.frames.length;i++) FCKTools.FixDocumentParentWindow(A.frames[i]);};FCKTools.HTMLEncode=function(A){if (!A) return '';A=A.replace(/&/g,'&amp;');A=A.replace(/</g,'&lt;');A=A.replace(/>/g,'&gt;');return A;};FCKTools.HTMLDecode=function(A){if (!A) return '';A=A.replace(/&gt;/g,'>');A=A.replace(/&lt;/g,'<');A=A.replace(/&amp;/g,'&');return A;};FCKTools.AddSelectOption=function(A,B,C){var D=FCKTools.GetElementDocument(A).createElement("OPTION");D.text=B;D.value=C;A.options.add(D);return D;};FCKTools.RunFunction=function(A,B,C,D){if (A) this.SetTimeout(A,0,B,C,D);};FCKTools.SetTimeout=function(A,B,C,D,E){return (E||window).setTimeout(function(){if (D) A.apply(C,[].concat(D));else A.apply(C);},B);};FCKTools.SetInterval=function(A,B,C,D,E){return (E||window).setInterval(function(){A.apply(C,D||[]);},B);};FCKTools.ConvertStyleSizeToHtml=function(A){return A.EndsWith('%')?A:parseInt(A,10);};FCKTools.ConvertHtmlSizeToStyle=function(A){return A.EndsWith('%')?A:(A+'px');};FCKTools.GetElementAscensor=function(A,B){var e=A;var C=","+B.toUpperCase()+",";while (e){if (C.indexOf(","+e.nodeName.toUpperCase()+",")!=-1) return e;e=e.parentNode;};return null;};FCKTools.CreateEventListener=function(A,B){var f=function(){var C=[];for (var i=0;i<arguments.length;i++) C.push(arguments[i]);A.apply(this,C.concat(B));};return f;};FCKTools.IsStrictMode=function(A){return ('CSS1Compat'==(A.compatMode||'CSS1Compat'));};FCKTools.ArgumentsToArray=function(A,B,C){B=B||0;C=C||A.length;var D=[];for (var i=B;i<B+C&&i<A.length;i++) D.push(A[i]);return D;};FCKTools.CloneObject=function(A){var B=function() {};B.prototype=A;return new B;};FCKTools.GetLastItem=function(A){if (A.length>0) return A[A.length-1];return null;};
+FCKTools.CancelEvent=function(e){if (e) e.preventDefault();};FCKTools.DisableSelection=function(A){if (FCKBrowserInfo.IsGecko) A.style.MozUserSelect='none';else A.style.userSelect='none';};FCKTools._AppendStyleSheet=function(A,B){var e=A.createElement('LINK');e.rel='stylesheet';e.type='text/css';e.href=B;A.getElementsByTagName("HEAD")[0].appendChild(e);return e;};FCKTools.ClearElementAttributes=function(A){for (var i=0;i<A.attributes.length;i++){A.removeAttribute(A.attributes[i].name,0);}};FCKTools.GetAllChildrenIds=function(A){var B=[];var C=function(parent){for (var i=0;i<parent.childNodes.length;i++){var D=parent.childNodes[i].id;if (D&&D.length>0) B[B.length]=D;C(parent.childNodes[i]);}};C(A);return B;};FCKTools.RemoveOuterTags=function(e){var A=e.ownerDocument.createDocumentFragment();for (var i=0;i<e.childNodes.length;i++) A.appendChild(e.childNodes[i].cloneNode(true));e.parentNode.replaceChild(A,e);};FCKTools.CreateXmlObject=function(A){switch (A){case 'XmlHttp':return new XMLHttpRequest();case 'DOMDocument':return document.implementation.createDocument('','',null);};return null;};FCKTools.GetScrollPosition=function(A){return { X:A.pageXOffset,Y:A.pageYOffset };};FCKTools.AddEventListener=function(A,B,C){A.addEventListener(B,C,false);};FCKTools.RemoveEventListener=function(A,B,C){A.removeEventListener(B,C,false);};FCKTools.AddEventListenerEx=function(A,B,C,D){A.addEventListener(B,function(e){C.apply(A,[e].concat(D||[]));},false);};FCKTools.GetViewPaneSize=function(A){return { Width:A.innerWidth,Height:A.innerHeight };};FCKTools.SaveStyles=function(A){var B={};if (A.className.length>0){B.Class=A.className;A.className='';};var C=A.getAttribute('style');if (C&&C.length>0){B.Inline=C;A.setAttribute('style','',0);};return B;};FCKTools.RestoreStyles=function(A,B){A.className=B.Class||'';if (B.Inline) A.setAttribute('style',B.Inline,0);else A.removeAttribute('style',0);};FCKTools.RegisterDollarFunction=function(A){A.$=function(id){return this.document.getElementById(id);};};FCKTools.AppendElement=function(A,B){return A.appendChild(A.ownerDocument.createElement(B));};FCKTools.GetElementPosition=function(A,B){var c={ X:0,Y:0 };var C=B||window;var D=FCKTools.GetElementWindow(A);while (A){var E=D.getComputedStyle(A,'').position;if (E&&E!='static'&&A.style.zIndex!=FCKConfig.FloatingPanelsZIndex) break;c.X+=A.offsetLeft-A.scrollLeft;c.Y+=A.offsetTop-A.scrollTop;if (A.offsetParent) A=A.offsetParent;else{if (D!=C){A=D.frameElement;if (A) D=FCKTools.GetElementWindow(A);}else{c.X+=A.scrollLeft;c.Y+=A.scrollTop;break;}}};return c;}
+var FCKeditorAPI;function InitializeAPI(){var A=window.parent;if (!(FCKeditorAPI=A.FCKeditorAPI)){var B='var FCKeditorAPI = {Version : "2.4.3",VersionBuild : "15657",__Instances : new Object(),GetInstance : function( name ){return this.__Instances[ name ];},_FormSubmit : function(){for ( var name in FCKeditorAPI.__Instances ){var oEditor = FCKeditorAPI.__Instances[ name ] ;if ( oEditor.GetParentForm && oEditor.GetParentForm() == this )oEditor.UpdateLinkedField() ;}this._FCKOriginalSubmit() ;},_FunctionQueue : {Functions : new Array(),IsRunning : false,Add : function( f ){this.Functions.push( f );if ( !this.IsRunning )this.StartNext();},StartNext : function(){var aQueue = this.Functions ;if ( aQueue.length > 0 ){this.IsRunning = true;aQueue[0].call();}else this.IsRunning = false;},Remove : function( f ){var aQueue = this.Functions;var i = 0, fFunc;while( (fFunc = aQueue[ i ]) ){if ( fFunc == f )aQueue.splice( i,1 );i++ ;}this.StartNext();}}}';if (A.execScript) A.execScript(B,'JavaScript');else{if (FCKBrowserInfo.IsGecko10){eval.call(A,B);}else if (FCKBrowserInfo.IsSafari){var C=A.document;var D=C.createElement('script');D.appendChild(C.createTextNode(B));C.documentElement.appendChild(D);}else A.eval(B);};FCKeditorAPI=A.FCKeditorAPI;};FCKeditorAPI.__Instances[FCK.Name]=FCK;};function _AttachFormSubmitToAPI(){var A=FCK.GetParentForm();if (A){FCKTools.AddEventListener(A,'submit',FCK.UpdateLinkedField);if (!A._FCKOriginalSubmit&&(typeof(A.submit)=='function'||(!A.submit.tagName&&!A.submit.length))){A._FCKOriginalSubmit=A.submit;A.submit=FCKeditorAPI._FormSubmit;}}};function FCKeditorAPI_Cleanup(){delete FCKeditorAPI.__Instances[FCK.Name];};FCKTools.AddEventListener(window,'unload',FCKeditorAPI_Cleanup);
+var FCKImagePreloader=function(){this._Images=[];};FCKImagePreloader.prototype={AddImages:function(A){if (typeof(A)=='string') A=A.split(';');this._Images=this._Images.concat(A);},Start:function(){var A=this._Images;this._PreloadCount=A.length;for (var i=0;i<A.length;i++){var B=document.createElement('img');B.onload=B.onerror=_FCKImagePreloader_OnImage;B._FCKImagePreloader=this;B.src=A[i];_FCKImagePreloader_ImageCache.push(B);}}};var _FCKImagePreloader_ImageCache=[];function _FCKImagePreloader_OnImage(){var A=this._FCKImagePreloader;if ((--A._PreloadCount)==0&&A.OnComplete) A.OnComplete();this._FCKImagePreloader=null;}
+var FCKRegexLib={AposEntity:/&apos;/gi,ObjectElements:/^(?:IMG|TABLE|TR|TD|TH|INPUT|SELECT|TEXTAREA|HR|OBJECT|A|UL|OL|LI)$/i,NamedCommands:/^(?:Cut|Copy|Paste|Print|SelectAll|RemoveFormat|Unlink|Undo|Redo|Bold|Italic|Underline|StrikeThrough|Subscript|Superscript|JustifyLeft|JustifyCenter|JustifyRight|JustifyFull|Outdent|Indent|InsertOrderedList|InsertUnorderedList|InsertHorizontalRule)$/i,BodyContents:/([\s\S]*\<body[^\>]*\>)([\s\S]*)(\<\/body\>[\s\S]*)/i,ToReplace:/___fcktoreplace:([\w]+)/ig,MetaHttpEquiv:/http-equiv\s*=\s*["']?([^"' ]+)/i,HasBaseTag:/<base /i,HtmlOpener:/<html\s?[^>]*>/i,HeadOpener:/<head\s?[^>]*>/i,HeadCloser:/<\/head\s*>/i,FCK_Class:/(\s*FCK__[A-Za-z]*\s*)/,ElementName:/(^[a-z_:][\w.\-:]*\w$)|(^[a-z_]$)/,ForceSimpleAmpersand:/___FCKAmp___/g,SpaceNoClose:/\/>/g,EmptyParagraph:/^<(p|div|address|h\d|center)(?=[ >])[^>]*>\s*(<\/\1>)?$/,EmptyOutParagraph:/^<(p|div|address|h\d|center)(?=[ >])[^>]*>(?:\s*|&nbsp;)(<\/\1>)?$/,TagBody:/></,StrongOpener:/<STRONG([ \>])/gi,StrongCloser:/<\/STRONG>/gi,EmOpener:/<EM([ \>])/gi,EmCloser:/<\/EM>/gi,GeckoEntitiesMarker:/#\?-\:/g,ProtectUrlsImg:/<img(?=\s).*?\ssrc=((?:(?:\s*)("|').*?\2)|(?:[^"'][^ >]+))/gi,ProtectUrlsA:/<a(?=\s).*?\shref=((?:(?:\s*)("|').*?\2)|(?:[^"'][^ >]+))/gi,Html4DocType:/HTML 4\.0 Transitional/i,DocTypeTag:/<!DOCTYPE[^>]*>/i,TagsWithEvent:/<[^\>]+ on\w+[\s\r\n]*=[\s\r\n]*?('|")[\s\S]+?\>/g,EventAttributes:/\s(on\w+)[\s\r\n]*=[\s\r\n]*?('|")([\s\S]*?)\2/g,ProtectedEvents:/\s\w+_fckprotectedatt="([^"]+)"/g,StyleProperties:/\S+\s*:/g,InvalidSelfCloseTags:/(<(?!base|meta|link|hr|br|param|img|area|input)([a-zA-Z0-9:]+)[^>]*)\/>/gi};
+var FCKListsLib={BlockElements:{ address:1,blockquote:1,center:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,marquee:1,noscript:1,ol:1,p:1,pre:1,script:1,table:1,ul:1 },NonEmptyBlockElements:{ p:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,address:1,pre:1,ol:1,ul:1,li:1,td:1,th:1 },InlineChildReqElements:{ abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1 },EmptyElements:{ base:1,meta:1,link:1,hr:1,br:1,param:1,img:1,area:1,input:1 },PathBlockElements:{ address:1,blockquote:1,dl:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1,ol:1,ul:1,li:1,dt:1,de:1 },PathBlockLimitElements:{ body:1,td:1,th:1,caption:1,form:1 },Setup:function(){if (FCKConfig.EnterMode=='div') this.PathBlockElements.div=1;else this.PathBlockLimitElements.div=1;}};
+var FCKLanguageManager=FCK.Language={AvailableLanguages:{af:'Afrikaans',ar:'Arabic',bg:'Bulgarian',bn:'Bengali/Bangla',bs:'Bosnian',ca:'Catalan',cs:'Czech',da:'Danish',de:'German',el:'Greek',en:'English','en-au':'English (Australia)','en-ca':'English (Canadian)','en-uk':'English (United Kingdom)',eo:'Esperanto',es:'Spanish',et:'Estonian',eu:'Basque',fa:'Persian',fi:'Finnish',fo:'Faroese',fr:'French',gl:'Galician',he:'Hebrew',hi:'Hindi',hr:'Croatian',hu:'Hungarian',it:'Italian',ja:'Japanese',km:'Khmer',ko:'Korean',lt:'Lithuanian',lv:'Latvian',mn:'Mongolian',ms:'Malay',nb:'Norwegian Bokmal',nl:'Dutch',no:'Norwegian',pl:'Polish',pt:'Portuguese (Portugal)','pt-br':'Portuguese (Brazil)',ro:'Romanian',ru:'Russian',sk:'Slovak',sl:'Slovenian',sr:'Serbian (Cyrillic)','sr-latn':'Serbian (Latin)',sv:'Swedish',th:'Thai',tr:'Turkish',uk:'Ukrainian',vi:'Vietnamese',zh:'Chinese Traditional','zh-cn':'Chinese Simplified'},GetActiveLanguage:function(){if (FCKConfig.AutoDetectLanguage){var A;if (navigator.userLanguage) A=navigator.userLanguage.toLowerCase();else if (navigator.language) A=navigator.language.toLowerCase();else{return FCKConfig.DefaultLanguage;};if (A.length>=5){A=A.substr(0,5);if (this.AvailableLanguages[A]) return A;};if (A.length>=2){A=A.substr(0,2);if (this.AvailableLanguages[A]) return A;}};return this.DefaultLanguage;},TranslateElements:function(A,B,C,D){var e=A.getElementsByTagName(B);var E,s;for (var i=0;i<e.length;i++){if ((E=e[i].getAttribute('fckLang'))){if ((s=FCKLang[E])){if (D) s=FCKTools.HTMLEncode(s);eval('e[i].'+C+' = s');}}}},TranslatePage:function(A){this.TranslateElements(A,'INPUT','value');this.TranslateElements(A,'SPAN','innerHTML');this.TranslateElements(A,'LABEL','innerHTML');this.TranslateElements(A,'OPTION','innerHTML',true);},Initialize:function(){if (this.AvailableLanguages[FCKConfig.DefaultLanguage]) this.DefaultLanguage=FCKConfig.DefaultLanguage;else this.DefaultLanguage='en';this.ActiveLanguage={};this.ActiveLanguage.Code=this.GetActiveLanguage();this.ActiveLanguage.Name=this.AvailableLanguages[this.ActiveLanguage.Code];}};
+var FCKXHtmlEntities={};FCKXHtmlEntities.Initialize=function(){if (FCKXHtmlEntities.Entities) return;var A='';var B,e;if (FCKConfig.ProcessHTMLEntities){FCKXHtmlEntities.Entities={' ':'nbsp','¡':'iexcl','¢':'cent','£':'pound','¤':'curren','Â¥':'yen','¦':'brvbar','§':'sect','¨':'uml','©':'copy','ª':'ordf','«':'laquo','¬':'not','­':'shy','®':'reg','¯':'macr','°':'deg','±':'plusmn','²':'sup2','³':'sup3','´':'acute','µ':'micro','¶':'para','·':'middot','¸':'cedil','¹':'sup1','º':'ordm','»':'raquo','¼':'frac14','½':'frac12','¾':'frac34','¿':'iquest','×':'times','÷':'divide','Æ’':'fnof','•':'bull','…':'hellip','′':'prime','″':'Prime','‾':'oline','â„':'frasl','℘':'weierp','â„‘':'image','â„œ':'real','â„¢':'trade','ℵ':'alefsym','â†':'larr','↑':'uarr','→':'rarr','↓':'darr','↔':'harr','↵':'crarr','â‡':'lArr','⇑':'uArr','⇒':'rArr','⇓':'dArr','⇔':'hArr','∀':'forall','∂':'part','∃':'exist','∅':'empty','∇':'nabla','∈':'isin','∉':'notin','∋':'ni','âˆ':'prod','∑':'sum','−':'minus','∗':'lowast','√':'radic','âˆ':'prop','∞':'infin','∠':'ang','∧':'and','∨':'or','∩':'cap','∪':'cup','∫':'int','∴':'there4','∼':'sim','≅':'cong','≈':'asymp','≠':'ne','≡':'equiv','≤':'le','≥':'ge','⊂':'sub','⊃':'sup','⊄':'nsub','⊆':'sube','⊇':'supe','⊕':'oplus','⊗':'otimes','⊥':'perp','â‹…':'sdot','â—Š':'loz','â™ ':'spades','♣':'clubs','♥':'hearts','♦':'diams','"':'quot','ˆ':'circ','Ëœ':'tilde',' ':'ensp',' ':'emsp',' ':'thinsp','‌':'zwnj','â€':'zwj','‎':'lrm','â€':'rlm','–':'ndash','—':'mdash','‘':'lsquo','’':'rsquo','‚':'sbquo','“':'ldquo','â€':'rdquo','„':'bdquo','†':'dagger','‡':'Dagger','‰':'permil','‹':'lsaquo','›':'rsaquo','€':'euro'};for (e in FCKXHtmlEntities.Entities) A+=e;if (FCKConfig.IncludeLatinEntities){B={'À':'Agrave','Ã':'Aacute','Â':'Acirc','Ã':'Atilde','Ä':'Auml','Ã…':'Aring','Æ':'AElig','Ç':'Ccedil','È':'Egrave','É':'Eacute','Ê':'Ecirc','Ë':'Euml','ÃŒ':'Igrave','Ã':'Iacute','ÃŽ':'Icirc','Ã':'Iuml','Ã':'ETH','Ñ':'Ntilde','Ã’':'Ograve','Ó':'Oacute','Ô':'Ocirc','Õ':'Otilde','Ö':'Ouml','Ø':'Oslash','Ù':'Ugrave','Ú':'Uacute','Û':'Ucirc','Ãœ':'Uuml','Ã':'Yacute','Þ':'THORN','ß':'szlig','à':'agrave','á':'aacute','â':'acirc','ã':'atilde','ä':'auml','Ã¥':'aring','æ':'aelig','ç':'ccedil','è':'egrave','é':'eacute','ê':'ecirc','ë':'euml','ì':'igrave','í':'iacute','î':'icirc','ï':'iuml','ð':'eth','ñ':'ntilde','ò':'ograve','ó':'oacute','ô':'ocirc','õ':'otilde','ö':'ouml','ø':'oslash','ù':'ugrave','ú':'uacute','û':'ucirc','ü':'uuml','ý':'yacute','þ':'thorn','ÿ':'yuml','Å’':'OElig','Å“':'oelig','Å ':'Scaron','Å¡':'scaron','Ÿ':'Yuml'};for (e in B){FCKXHtmlEntities.Entities[e]=B[e];A+=e;};B=null;};if (FCKConfig.IncludeGreekEntities){B={'Α':'Alpha','Î’':'Beta','Γ':'Gamma','Δ':'Delta','Ε':'Epsilon','Ζ':'Zeta','Η':'Eta','Θ':'Theta','Ι':'Iota','Κ':'Kappa','Λ':'Lambda','Îœ':'Mu','Î':'Nu','Ξ':'Xi','Ο':'Omicron','Π':'Pi','Ρ':'Rho','Σ':'Sigma','Τ':'Tau','Î¥':'Upsilon','Φ':'Phi','Χ':'Chi','Ψ':'Psi','Ω':'Omega','α':'alpha','β':'beta','γ':'gamma','δ':'delta','ε':'epsilon','ζ':'zeta','η':'eta','θ':'theta','ι':'iota','κ':'kappa','λ':'lambda','μ':'mu','ν':'nu','ξ':'xi','ο':'omicron','Ï€':'pi','Ï':'rho','Ï‚':'sigmaf','σ':'sigma','Ï„':'tau','Ï…':'upsilon','φ':'phi','χ':'chi','ψ':'psi','ω':'omega'};for (e in B){FCKXHtmlEntities.Entities[e]=B[e];A+=e;};B=null;}}else{FCKXHtmlEntities.Entities={};A=' ';};var C='['+A+']';if (FCKConfig.ProcessNumericEntities) C='[^ -~]|'+C;var D=FCKConfig.AdditionalNumericEntities;if (D&&D.length>0) C+='|'+FCKConfig.AdditionalNumericEntities;FCKXHtmlEntities.EntitiesRegex=new RegExp(C,'g');}
+var FCKXHtml={};FCKXHtml.CurrentJobNum=0;FCKXHtml.GetXHTML=function(A,B,C){FCKXHtmlEntities.Initialize();this._NbspEntity=(FCKConfig.ProcessHTMLEntities?'nbsp':'#160');var D=FCK.IsDirty();this._CreateNode=FCKConfig.ForceStrongEm?FCKXHtml_CreateNode_StrongEm:FCKXHtml_CreateNode_Normal;FCKXHtml.SpecialBlocks=[];this.XML=FCKTools.CreateXmlObject('DOMDocument');this.MainNode=this.XML.appendChild(this.XML.createElement('xhtml'));FCKXHtml.CurrentJobNum++;if (B) this._AppendNode(this.MainNode,A);else this._AppendChildNodes(this.MainNode,A,false);var E=this._GetMainXmlString();this.XML=null;E=E.substr(7,E.length-15).Trim();if (FCKBrowserInfo.IsGecko) E=E.replace(/<br\/>$/,'');E=E.replace(FCKRegexLib.SpaceNoClose,' />');if (FCKConfig.ForceSimpleAmpersand) E=E.replace(FCKRegexLib.ForceSimpleAmpersand,'&');if (C) E=FCKCodeFormatter.Format(E);for (var i=0;i<FCKXHtml.SpecialBlocks.length;i++){var F=new RegExp('___FCKsi___'+i);E=E.replace(F,FCKXHtml.SpecialBlocks[i]);};E=E.replace(FCKRegexLib.GeckoEntitiesMarker,'&');if (!D) FCK.ResetIsDirty();return E;};FCKXHtml._AppendAttribute=function(A,B,C){try{if (C==undefined||C==null) C='';else if (C.replace){if (FCKConfig.ForceSimpleAmpersand) C=C.replace(/&/g,'___FCKAmp___');C=C.replace(FCKXHtmlEntities.EntitiesRegex,FCKXHtml_GetEntity);};var D=this.XML.createAttribute(B);D.value=C;A.attributes.setNamedItem(D);}catch (e){}};FCKXHtml._AppendChildNodes=function(A,B,C){var D=B.firstChild;while (D){this._AppendNode(A,D);D=D.nextSibling;};if (C) FCKDomTools.TrimNode(A,true);if (A.childNodes.length==0){if (C&&FCKConfig.FillEmptyBlocks){this._AppendEntity(A,this._NbspEntity);return A;};var E=A.nodeName;if (FCKListsLib.InlineChildReqElements[E]) return null;if (!FCKListsLib.EmptyElements[E]) A.appendChild(this.XML.createTextNode(''));};return A;};FCKXHtml._AppendNode=function(A,B){if (!B) return false;switch (B.nodeType){case 1:if (B.getAttribute('_fckfakelement')) return FCKXHtml._AppendNode(A,FCK.GetRealElement(B));if (FCKBrowserInfo.IsGecko&&B.hasAttribute('_moz_editor_bogus_node')) return false;if (B.getAttribute('_fcktemp')) return false;var C=B.tagName.toLowerCase();if (FCKBrowserInfo.IsIE){if (B.scopeName&&B.scopeName!='HTML'&&B.scopeName!='FCK') C=B.scopeName.toLowerCase()+':'+C;}else{if (C.StartsWith('fck:')) C=C.Remove(0,4);};if (!FCKRegexLib.ElementName.test(C)) return false;if (C=='br'&&B.getAttribute('type',2)=='_moz') return false;if (B._fckxhtmljob&&B._fckxhtmljob==FCKXHtml.CurrentJobNum) return false;var D=this._CreateNode(C);FCKXHtml._AppendAttributes(A,B,D,C);B._fckxhtmljob=FCKXHtml.CurrentJobNum;var E=FCKXHtml.TagProcessors[C];if (E) D=E(D,B,A);else D=this._AppendChildNodes(D,B,Boolean(FCKListsLib.NonEmptyBlockElements[C]));if (!D) return false;A.appendChild(D);break;case 3:return this._AppendTextNode(A,B.nodeValue.ReplaceNewLineChars(' '));case 8:if (FCKBrowserInfo.IsIE&&!B.innerHTML) break;try { A.appendChild(this.XML.createComment(B.nodeValue));}catch (e) {/*Do nothing... probably this is a wrong format comment.*/};break;default:A.appendChild(this.XML.createComment("Element not supported - Type: "+B.nodeType+" Name: "+B.nodeName));break;};return true;};function FCKXHtml_CreateNode_StrongEm(A){switch (A){case 'b':A='strong';break;case 'i':A='em';break;};return this.XML.createElement(A);};function FCKXHtml_CreateNode_Normal(A){return this.XML.createElement(A);};FCKXHtml._AppendSpecialItem=function(A){return '___FCKsi___'+FCKXHtml.SpecialBlocks.AddItem(A);};FCKXHtml._AppendEntity=function(A,B){A.appendChild(this.XML.createTextNode('#?-:'+B+';'));};FCKXHtml._AppendTextNode=function(A,B){var C=B.length>0;if (C) A.appendChild(this.XML.createTextNode(B.replace(FCKXHtmlEntities.EntitiesRegex,FCKXHtml_GetEntity)));return C;};function FCKXHtml_GetEntity(A){var B=FCKXHtmlEntities.Entities[A]||('#'+A.charCodeAt(0));return '#?-:'+B+';';};FCKXHtml._RemoveAttribute=function(A,B,C){var D=A.attributes.getNamedItem(C);if (D&&B.test(D.nodeValue)){var E=D.nodeValue.replace(B,'');if (E.length==0) A.attributes.removeNamedItem(C);else D.nodeValue=E;}};FCKXHtml.TagProcessors={img:function(A,B){if (!A.attributes.getNamedItem('alt')) FCKXHtml._AppendAttribute(A,'alt','');var C=B.getAttribute('_fcksavedurl');if (C!=null) FCKXHtml._AppendAttribute(A,'src',C);return A;},a:function(A,B){if (B.innerHTML.Trim().length==0&&!B.name) return false;var C=B.getAttribute('_fcksavedurl');if (C!=null) FCKXHtml._AppendAttribute(A,'href',C);if (FCKBrowserInfo.IsIE){FCKXHtml._RemoveAttribute(A,FCKRegexLib.FCK_Class,'class');if (B.name) FCKXHtml._AppendAttribute(A,'name',B.name);};A=FCKXHtml._AppendChildNodes(A,B,false);return A;},script:function(A,B){if (!A.attributes.getNamedItem('type')) FCKXHtml._AppendAttribute(A,'type','text/javascript');A.appendChild(FCKXHtml.XML.createTextNode(FCKXHtml._AppendSpecialItem(B.text)));return A;},style:function(A,B){if (!A.attributes.getNamedItem('type')) FCKXHtml._AppendAttribute(A,'type','text/css');A.appendChild(FCKXHtml.XML.createTextNode(FCKXHtml._AppendSpecialItem(B.innerHTML)));return A;},title:function(A,B){A.appendChild(FCKXHtml.XML.createTextNode(FCK.EditorDocument.title));return A;},table:function(A,B){if (FCKBrowserInfo.IsIE) FCKXHtml._RemoveAttribute(A,FCKRegexLib.FCK_Class,'class');A=FCKXHtml._AppendChildNodes(A,B,false);return A;},ol:function(A,B,C){if (B.innerHTML.Trim().length==0) return false;var D=C.lastChild;if (D&&D.nodeType==3) D=D.previousSibling;if (D&&D.nodeName.toUpperCase()=='LI'){B._fckxhtmljob=null;FCKXHtml._AppendNode(D,B);return false;};A=FCKXHtml._AppendChildNodes(A,B);return A;},span:function(A,B){if (B.innerHTML.length==0) return false;A=FCKXHtml._AppendChildNodes(A,B,false);return A;},iframe:function(A,B){var C=B.innerHTML;if (FCKBrowserInfo.IsGecko) C=FCKTools.HTMLDecode(C);C=C.replace(/\s_fcksavedurl="[^"]*"/g,'');A.appendChild(FCKXHtml.XML.createTextNode(FCKXHtml._AppendSpecialItem(C)));return A;}};FCKXHtml.TagProcessors.ul=FCKXHtml.TagProcessors.ol;
+FCKXHtml._GetMainXmlString=function(){var A=new XMLSerializer();return A.serializeToString(this.MainNode);};FCKXHtml._AppendAttributes=function(A,B,C){var D=B.attributes;for (var n=0;n<D.length;n++){var E=D[n];if (E.specified){var F=E.nodeName.toLowerCase();var G;if (F.StartsWith('_fck')) continue;else if (F.indexOf('_moz')==0) continue;else if (F=='class') G=E.nodeValue;else if (E.nodeValue===true) G=F;else G=B.getAttribute(F,2);this._AppendAttribute(C,F,G);}}}
+var FCKCodeFormatter={};FCKCodeFormatter.Init=function(){var A=this.Regex={};A.BlocksOpener=/\<(P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|TITLE|META|LINK|BASE|SCRIPT|LINK|TD|TH|AREA|OPTION)[^\>]*\>/gi;A.BlocksCloser=/\<\/(P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|TITLE|META|LINK|BASE|SCRIPT|LINK|TD|TH|AREA|OPTION)[^\>]*\>/gi;A.NewLineTags=/\<(BR|HR)[^\>]*\>/gi;A.MainTags=/\<\/?(HTML|HEAD|BODY|FORM|TABLE|TBODY|THEAD|TR)[^\>]*\>/gi;A.LineSplitter=/\s*\n+\s*/g;A.IncreaseIndent=/^\<(HTML|HEAD|BODY|FORM|TABLE|TBODY|THEAD|TR|UL|OL)[ \/\>]/i;A.DecreaseIndent=/^\<\/(HTML|HEAD|BODY|FORM|TABLE|TBODY|THEAD|TR|UL|OL)[ \>]/i;A.FormatIndentatorRemove=new RegExp('^'+FCKConfig.FormatIndentator);A.ProtectedTags=/(<PRE[^>]*>)([\s\S]*?)(<\/PRE>)/gi;};FCKCodeFormatter._ProtectData=function(A,B,C,D){return B+'___FCKpd___'+FCKCodeFormatter.ProtectedData.AddItem(C)+D;};FCKCodeFormatter.Format=function(A){if (!this.Regex) this.Init();FCKCodeFormatter.ProtectedData=[];var B=A.replace(this.Regex.ProtectedTags,FCKCodeFormatter._ProtectData);B=B.replace(this.Regex.BlocksOpener,'\n$&');B=B.replace(this.Regex.BlocksCloser,'$&\n');B=B.replace(this.Regex.NewLineTags,'$&\n');B=B.replace(this.Regex.MainTags,'\n$&\n');var C='';var D=B.split(this.Regex.LineSplitter);B='';for (var i=0;i<D.length;i++){var E=D[i];if (E.length==0) continue;if (this.Regex.DecreaseIndent.test(E)) C=C.replace(this.Regex.FormatIndentatorRemove,'');B+=C+E+'\n';if (this.Regex.IncreaseIndent.test(E)) C+=FCKConfig.FormatIndentator;};for (var j=0;j<FCKCodeFormatter.ProtectedData.length;j++){var F=new RegExp('___FCKpd___'+j);B=B.replace(F,FCKCodeFormatter.ProtectedData[j].replace(/\$/g,'$$$$'));};return B.Trim();}
+var FCKUndo={};FCKUndo.SaveUndoStep=function(){}
+var FCKEditingArea=function(A){this.TargetElement=A;this.Mode=0;if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKEditingArea_Cleanup);};FCKEditingArea.prototype.Start=function(A,B){var C=this.TargetElement;var D=FCKTools.GetElementDocument(C);while(C.childNodes.length>0) C.removeChild(C.childNodes[0]);if (this.Mode==0){var E=this.IFrame=D.createElement('iframe');E.src='javascript:void(0)';E.frameBorder=0;E.width=E.height='100%';C.appendChild(E);if (FCKBrowserInfo.IsIE) A=A.replace(/(<base[^>]*?)\s*\/?>(?!\s*<\/base>)/gi,'$1></base>');else if (!B){if (FCKBrowserInfo.IsGecko) A=A.replace(/(<body[^>]*>)\s*(<\/body>)/i,'$1'+GECKO_BOGUS+'$2');var F=A.match(FCKRegexLib.BodyContents);if (F){A=F[1]+'&nbsp;'+F[3];this._BodyHTML=F[2];}else this._BodyHTML=A;};this.Window=E.contentWindow;var G=this.Document=this.Window.document;G.open();G.write(A);G.close();if (FCKBrowserInfo.IsGecko10&&!B){this.Start(A,true);return;};this.Window._FCKEditingArea=this;if (FCKBrowserInfo.IsGecko10) this.Window.setTimeout(FCKEditingArea_CompleteStart,500);else FCKEditingArea_CompleteStart.call(this.Window);}else{var H=this.Textarea=D.createElement('textarea');H.className='SourceField';H.dir='ltr';H.style.width=H.style.height='100%';H.style.border='none';C.appendChild(H);H.value=A;FCKTools.RunFunction(this.OnLoad);}};function FCKEditingArea_CompleteStart(){if (!this.document.body){this.setTimeout(FCKEditingArea_CompleteStart,50);return;};var A=this._FCKEditingArea;A.MakeEditable();FCKTools.RunFunction(A.OnLoad);};FCKEditingArea.prototype.MakeEditable=function(){var A=this.Document;if (FCKBrowserInfo.IsIE){A.body.contentEditable=true;}else{try{A.body.spellcheck=(this.FFSpellChecker!==false);if (this._BodyHTML){A.body.innerHTML=this._BodyHTML;this._BodyHTML=null;};A.designMode='on';try{A.execCommand('styleWithCSS',false,FCKConfig.GeckoUseSPAN);}catch (e){A.execCommand('useCSS',false,!FCKConfig.GeckoUseSPAN);};A.execCommand('enableObjectResizing',false,!FCKConfig.DisableObjectResizing);A.execCommand('enableInlineTableEditing',false,!FCKConfig.DisableFFTableHandles);}catch (e) {}}};FCKEditingArea.prototype.Focus=function(){try{if (this.Mode==0){if (FCKBrowserInfo.IsIE&&this.Document.hasFocus()) return;if (FCKBrowserInfo.IsSafari) this.IFrame.focus();else{this.Window.focus();}}else{var A=FCKTools.GetElementDocument(this.Textarea);if ((!A.hasFocus||A.hasFocus())&&A.activeElement==this.Textarea) return;this.Textarea.focus();}}catch(e) {}};function FCKEditingArea_Cleanup(){this.TargetElement=null;this.IFrame=null;this.Document=null;this.Textarea=null;if (this.Window){this.Window._FCKEditingArea=null;this.Window=null;}};
+var FCKKeystrokeHandler=function(A){this.Keystrokes={};this.CancelCtrlDefaults=(A!==false);};FCKKeystrokeHandler.prototype.AttachToElement=function(A){FCKTools.AddEventListenerEx(A,'keydown',_FCKKeystrokeHandler_OnKeyDown,this);if (FCKBrowserInfo.IsGecko10||FCKBrowserInfo.IsOpera||(FCKBrowserInfo.IsGecko&&FCKBrowserInfo.IsMac)) FCKTools.AddEventListenerEx(A,'keypress',_FCKKeystrokeHandler_OnKeyPress,this);};FCKKeystrokeHandler.prototype.SetKeystrokes=function(){for (var i=0;i<arguments.length;i++){var A=arguments[i];if (typeof(A[0])=='object') this.SetKeystrokes.apply(this,A);else{if (A.length==1) delete this.Keystrokes[A[0]];else this.Keystrokes[A[0]]=A[1]===true?true:A;}}};function _FCKKeystrokeHandler_OnKeyDown(A,B){var C=A.keyCode||A.which;var D=0;if (A.ctrlKey||A.metaKey) D+=CTRL;if (A.shiftKey) D+=SHIFT;if (A.altKey) D+=ALT;var E=C+D;var F=B._CancelIt=false;var G=B.Keystrokes[E];if (G){if (G===true||!(B.OnKeystroke&&B.OnKeystroke.apply(B,G))) return true;F=true;};if (F||(B.CancelCtrlDefaults&&D==CTRL&&(C<33||C>40))){B._CancelIt=true;if (A.preventDefault) return A.preventDefault();A.returnValue=false;A.cancelBubble=true;return false;};return true;};function _FCKKeystrokeHandler_OnKeyPress(A,B){if (B._CancelIt){if (A.preventDefault) return A.preventDefault();return false;};return true;}
+var FCKListHandler={OutdentListItem:function(A){var B=A.parentNode;if (B.tagName.toUpperCase().Equals('UL','OL')){var C=FCKTools.GetElementDocument(A);var D=new FCKDocumentFragment(C);var E=D.RootNode;var F=false;var G=FCKDomTools.GetFirstChild(A,['UL','OL']);if (G){F=true;var H;while ((H=G.firstChild)) E.appendChild(G.removeChild(H));FCKDomTools.RemoveNode(G);};var I;var J=false;while ((I=A.nextSibling)){if (!F&&I.nodeType==1&&I.nodeName.toUpperCase()=='LI') J=F=true;E.appendChild(I.parentNode.removeChild(I));if (!J&&I.nodeType==1&&I.nodeName.toUpperCase().Equals('UL','OL')) FCKDomTools.RemoveNode(I,true);};var K=B.parentNode.tagName.toUpperCase();var L=(K=='LI');if (L||K.Equals('UL','OL')){if (F){var G=B.cloneNode(false);D.AppendTo(G);A.appendChild(G);}else if (L) D.InsertAfterNode(B.parentNode);else D.InsertAfterNode(B);if (L) FCKDomTools.InsertAfterNode(B.parentNode,B.removeChild(A));else FCKDomTools.InsertAfterNode(B,B.removeChild(A));}else{if (F){var N=B.cloneNode(false);D.AppendTo(N);FCKDomTools.InsertAfterNode(B,N);};var O=C.createElement(FCKConfig.EnterMode=='p'?'p':'div');FCKDomTools.MoveChildren(B.removeChild(A),O);FCKDomTools.InsertAfterNode(B,O);if (FCKConfig.EnterMode=='br'){if (FCKBrowserInfo.IsGecko) O.parentNode.insertBefore(FCKTools.CreateBogusBR(C),O);else FCKDomTools.InsertAfterNode(O,FCKTools.CreateBogusBR(C));FCKDomTools.RemoveNode(O,true);}};if (this.CheckEmptyList(B)) FCKDomTools.RemoveNode(B,true);}},CheckEmptyList:function(A){return (FCKDomTools.GetFirstChild(A,'LI')==null);},CheckListHasContents:function(A){var B=A.firstChild;while (B){switch (B.nodeType){case 1:if (!B.nodeName.IEquals('UL','LI')) return true;break;case 3:if (B.nodeValue.Trim().length>0) return true;};B=B.nextSibling;};return false;}};
+var FCKElementPath=function(A){var B=null;var C=null;var D=[];var e=A;while (e){if (e.nodeType==1){if (!this.LastElement) this.LastElement=e;var E=e.nodeName.toLowerCase();if (!C){if (!B&&FCKListsLib.PathBlockElements[E]!=null) B=e;if (FCKListsLib.PathBlockLimitElements[E]!=null) C=e;};D.push(e);if (E=='body') break;};e=e.parentNode;};this.Block=B;this.BlockLimit=C;this.Elements=D;};
+var FCKDomRange=function(A){this.Window=A;};FCKDomRange.prototype={_UpdateElementInfo:function(){if (!this._Range) this.Release(true);else{var A=this._Range.startContainer;var B=this._Range.endContainer;var C=new FCKElementPath(A);this.StartContainer=C.LastElement;this.StartBlock=C.Block;this.StartBlockLimit=C.BlockLimit;if (A!=B) C=new FCKElementPath(B);this.EndContainer=C.LastElement;this.EndBlock=C.Block;this.EndBlockLimit=C.BlockLimit;}},CreateRange:function(){return new FCKW3CRange(this.Window.document);},DeleteContents:function(){if (this._Range){this._Range.deleteContents();this._UpdateElementInfo();}},ExtractContents:function(){if (this._Range){var A=this._Range.extractContents();this._UpdateElementInfo();return A;}},CheckIsCollapsed:function(){if (this._Range) return this._Range.collapsed;},Collapse:function(A){if (this._Range) this._Range.collapse(A);this._UpdateElementInfo();},Clone:function(){var A=FCKTools.CloneObject(this);if (this._Range) A._Range=this._Range.cloneRange();return A;},MoveToNodeContents:function(A){if (!this._Range) this._Range=this.CreateRange();this._Range.selectNodeContents(A);this._UpdateElementInfo();},MoveToElementStart:function(A){this.SetStart(A,1);this.SetEnd(A,1);},MoveToElementEditStart:function(A){var B;while ((B=A.firstChild)&&B.nodeType==1&&FCKListsLib.EmptyElements[B.nodeName.toLowerCase()]==null) A=B;this.MoveToElementStart(A);},InsertNode:function(A){if (this._Range) this._Range.insertNode(A);},CheckIsEmpty:function(A){if (this.CheckIsCollapsed()) return true;var B=this.Window.document.createElement('div');this._Range.cloneContents().AppendTo(B);FCKDomTools.TrimNode(B,A);return (B.innerHTML.length==0);},CheckStartOfBlock:function(){var A=this.Clone();A.Collapse(true);A.SetStart(A.StartBlock||A.StartBlockLimit,1);var B=A.CheckIsEmpty();A.Release();return B;},CheckEndOfBlock:function(A){var B=this.Clone();B.Collapse(false);B.SetEnd(B.EndBlock||B.EndBlockLimit,2);var C=B.CheckIsCollapsed();if (!C){var D=this.Window.document.createElement('div');B._Range.cloneContents().AppendTo(D);FCKDomTools.TrimNode(D,true);C=true;var E=D;while ((E=E.lastChild)){if (E.previousSibling||E.nodeType!=1||FCKListsLib.InlineChildReqElements[E.nodeName.toLowerCase()]==null){C=false;break;}}};B.Release();if (A) this.Select();return C;},CreateBookmark:function(){var A={StartId:'fck_dom_range_start_'+(new Date()).valueOf()+'_'+Math.floor(Math.random()*1000),EndId:'fck_dom_range_end_'+(new Date()).valueOf()+'_'+Math.floor(Math.random()*1000)};var B=this.Window.document;var C;var D;if (!this.CheckIsCollapsed()){C=B.createElement('span');C.id=A.EndId;C.innerHTML='&nbsp;';D=this.Clone();D.Collapse(false);D.InsertNode(C);};C=B.createElement('span');C.id=A.StartId;C.innerHTML='&nbsp;';D=this.Clone();D.Collapse(true);D.InsertNode(C);return A;},MoveToBookmark:function(A,B){var C=this.Window.document;var D=C.getElementById(A.StartId);var E=C.getElementById(A.EndId);this.SetStart(D,3);if (!B) FCKDomTools.RemoveNode(D);if (E){this.SetEnd(E,3);if (!B) FCKDomTools.RemoveNode(E);}else this.Collapse(true);},SetStart:function(A,B){var C=this._Range;if (!C) C=this._Range=this.CreateRange();switch(B){case 1:C.setStart(A,0);break;case 2:C.setStart(A,A.childNodes.length);break;case 3:C.setStartBefore(A);break;case 4:C.setStartAfter(A);};this._UpdateElementInfo();},SetEnd:function(A,B){var C=this._Range;if (!C) C=this._Range=this.CreateRange();switch(B){case 1:C.setEnd(A,0);break;case 2:C.setEnd(A,A.childNodes.length);break;case 3:C.setEndBefore(A);break;case 4:C.setEndAfter(A);};this._UpdateElementInfo();},Expand:function(A){var B,oSibling;switch (A){case 'block_contents':if (this.StartBlock) this.SetStart(this.StartBlock,1);else{B=this._Range.startContainer;if (B.nodeType==1){if (!(B=B.childNodes[this._Range.startOffset])) B=B.firstChild;};if (!B) return;while (true){oSibling=B.previousSibling;if (!oSibling){if (B.parentNode!=this.StartBlockLimit) B=B.parentNode;else break;}else if (oSibling.nodeType!=1||!(/^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/).test(oSibling.nodeName.toUpperCase())){B=oSibling;}else break;};this._Range.setStartBefore(B);};if (this.EndBlock) this.SetEnd(this.EndBlock,2);else{B=this._Range.endContainer;if (B.nodeType==1) B=B.childNodes[this._Range.endOffset]||B.lastChild;if (!B) return;while (true){oSibling=B.nextSibling;if (!oSibling){if (B.parentNode!=this.EndBlockLimit) B=B.parentNode;else break;}else if (oSibling.nodeType!=1||!(/^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/).test(oSibling.nodeName.toUpperCase())){B=oSibling;}else break;};this._Range.setEndAfter(B);};this._UpdateElementInfo();}},Release:function(A){if (!A) this.Window=null;this.StartContainer=null;this.StartBlock=null;this.StartBlockLimit=null;this.EndContainer=null;this.EndBlock=null;this.EndBlockLimit=null;this._Range=null;}};
+FCKDomRange.prototype.MoveToSelection=function(){this.Release(true);var A=this.Window.getSelection();if (A.rangeCount==1){this._Range=FCKW3CRange.CreateFromRange(this.Window.document,A.getRangeAt(0));this._UpdateElementInfo();}};FCKDomRange.prototype.Select=function(){var A=this._Range;if (A){var B=this.Window.document.createRange();B.setStart(A.startContainer,A.startOffset);try{B.setEnd(A.endContainer,A.endOffset);}catch (e){if (e.toString().Contains('NS_ERROR_ILLEGAL_VALUE')){A.collapse(true);B.setEnd(A.endContainer,A.endOffset);}else throw(e);};var C=this.Window.getSelection();C.removeAllRanges();C.addRange(B);}};
+var FCKDocumentFragment=function(A,B){this.RootNode=B||A.createDocumentFragment();};FCKDocumentFragment.prototype={AppendTo:function(A){A.appendChild(this.RootNode);},InsertAfterNode:function(A){FCKDomTools.InsertAfterNode(A,this.RootNode);}}
+var FCKW3CRange=function(A){this._Document=A;this.startContainer=null;this.startOffset=null;this.endContainer=null;this.endOffset=null;this.collapsed=true;};FCKW3CRange.CreateRange=function(A){return new FCKW3CRange(A);};FCKW3CRange.CreateFromRange=function(A,B){var C=FCKW3CRange.CreateRange(A);C.setStart(B.startContainer,B.startOffset);C.setEnd(B.endContainer,B.endOffset);return C;};FCKW3CRange.prototype={_UpdateCollapsed:function(){this.collapsed=(this.startContainer==this.endContainer&&this.startOffset==this.endOffset);},setStart:function(A,B){this.startContainer=A;this.startOffset=B;if (!this.endContainer){this.endContainer=A;this.endOffset=B;};this._UpdateCollapsed();},setEnd:function(A,B){this.endContainer=A;this.endOffset=B;if (!this.startContainer){this.startContainer=A;this.startOffset=B;};this._UpdateCollapsed();},setStartAfter:function(A){this.setStart(A.parentNode,FCKDomTools.GetIndexOf(A)+1);},setStartBefore:function(A){this.setStart(A.parentNode,FCKDomTools.GetIndexOf(A));},setEndAfter:function(A){this.setEnd(A.parentNode,FCKDomTools.GetIndexOf(A)+1);},setEndBefore:function(A){this.setEnd(A.parentNode,FCKDomTools.GetIndexOf(A));},collapse:function(A){if (A){this.endContainer=this.startContainer;this.endOffset=this.startOffset;}else{this.startContainer=this.endContainer;this.startOffset=this.endOffset;};this.collapsed=true;},selectNodeContents:function(A){this.setStart(A,0);this.setEnd(A,A.nodeType==3?A.data.length:A.childNodes.length);},insertNode:function(A){var B=this.startContainer;var C=this.startOffset;if (B.nodeType==3){B.splitText(C);if (B==this.endContainer) this.setEnd(B.nextSibling,this.endOffset-this.startOffset);FCKDomTools.InsertAfterNode(B,A);return;}else{B.insertBefore(A,B.childNodes[C]||null);if (B==this.endContainer){this.endOffset++;this.collapsed=false;}}},deleteContents:function(){if (this.collapsed) return;this._ExecContentsAction(0);},extractContents:function(){var A=new FCKDocumentFragment(this._Document);if (!this.collapsed) this._ExecContentsAction(1,A);return A;},cloneContents:function(){var A=new FCKDocumentFragment(this._Document);if (!this.collapsed) this._ExecContentsAction(2,A);return A;},_ExecContentsAction:function(A,B){var C=this.startContainer;var D=this.endContainer;var E=this.startOffset;var F=this.endOffset;var G=false;var H=false;if (D.nodeType==3) D=D.splitText(F);else{if (D.childNodes.length>0){if (F>D.childNodes.length-1){D=FCKDomTools.InsertAfterNode(D.lastChild,this._Document.createTextNode(''));H=true;}else D=D.childNodes[F];}};if (C.nodeType==3){C.splitText(E);if (C==D) D=C.nextSibling;}else{if (C.childNodes.length>0&&E<=C.childNodes.length-1){if (E==0){C=C.insertBefore(this._Document.createTextNode(''),C.firstChild);G=true;}else C=C.childNodes[E].previousSibling;}};var I=FCKDomTools.GetParents(C);var J=FCKDomTools.GetParents(D);var i,topStart,topEnd;for (i=0;i<I.length;i++){topStart=I[i];topEnd=J[i];if (topStart!=topEnd) break;};var K,levelStartNode,levelClone,currentNode,currentSibling;if (B) K=B.RootNode;for (var j=i;j<I.length;j++){levelStartNode=I[j];if (K&&levelStartNode!=C) levelClone=K.appendChild(levelStartNode.cloneNode(levelStartNode==C));currentNode=levelStartNode.nextSibling;while(currentNode){if (currentNode==J[j]||currentNode==D) break;currentSibling=currentNode.nextSibling;if (A==2) K.appendChild(currentNode.cloneNode(true));else{currentNode.parentNode.removeChild(currentNode);if (A==1) K.appendChild(currentNode);};currentNode=currentSibling;};if (K) K=levelClone;};if (B) K=B.RootNode;for (var k=i;k<J.length;k++){levelStartNode=J[k];if (A>0&&levelStartNode!=D) levelClone=K.appendChild(levelStartNode.cloneNode(levelStartNode==D));if (!I[k]||levelStartNode.parentNode!=I[k].parentNode){currentNode=levelStartNode.previousSibling;while(currentNode){if (currentNode==I[k]||currentNode==C) break;currentSibling=currentNode.previousSibling;if (A==2) K.insertBefore(currentNode.cloneNode(true),K.firstChild);else{currentNode.parentNode.removeChild(currentNode);if (A==1) K.insertBefore(currentNode,K.firstChild);};currentNode=currentSibling;}};if (K) K=levelClone;};if (A==2){var L=this.startContainer;if (L.nodeType==3){L.data+=L.nextSibling.data;L.parentNode.removeChild(L.nextSibling);};var M=this.endContainer;if (M.nodeType==3&&M.nextSibling){M.data+=M.nextSibling.data;M.parentNode.removeChild(M.nextSibling);}}else{if (topStart&&topEnd&&(C.parentNode!=topStart.parentNode||D.parentNode!=topEnd.parentNode)) this.setStart(topEnd.parentNode,FCKDomTools.GetIndexOf(topEnd));this.collapse(true);};if(G) C.parentNode.removeChild(C);if(H&&D.parentNode) D.parentNode.removeChild(D);},cloneRange:function(){return FCKW3CRange.CreateFromRange(this._Document,this);},toString:function(){var A=this.cloneContents();var B=this._Document.createElement('div');A.AppendTo(B);return B.textContent||B.innerText;}};
+var FCKEnterKey=function(A,B,C){this.Window=A;this.EnterMode=B||'p';this.ShiftEnterMode=C||'br';var D=new FCKKeystrokeHandler(false);D._EnterKey=this;D.OnKeystroke=FCKEnterKey_OnKeystroke;D.SetKeystrokes([[13,'Enter'],[SHIFT+13,'ShiftEnter'],[8,'Backspace'],[46,'Delete']]);D.AttachToElement(A.document);};function FCKEnterKey_OnKeystroke(A,B){var C=this._EnterKey;try{switch (B){case 'Enter':return C.DoEnter();break;case 'ShiftEnter':return C.DoShiftEnter();break;case 'Backspace':return C.DoBackspace();break;case 'Delete':return C.DoDelete();}}catch (e){};return false;};FCKEnterKey.prototype.DoEnter=function(A,B){this._HasShift=(B===true);var C=A||this.EnterMode;if (C=='br') return this._ExecuteEnterBr();else return this._ExecuteEnterBlock(C);};FCKEnterKey.prototype.DoShiftEnter=function(){return this.DoEnter(this.ShiftEnterMode,true);};FCKEnterKey.prototype.DoBackspace=function(){var A=false;var B=new FCKDomRange(this.Window);B.MoveToSelection();if (!B.CheckIsCollapsed()) return false;var C=B.StartBlock;var D=B.EndBlock;if (B.StartBlockLimit==B.EndBlockLimit&&C&&D){if (!B.CheckIsCollapsed()){var E=B.CheckEndOfBlock();B.DeleteContents();if (C!=D){B.SetStart(D,1);B.SetEnd(D,1);};B.Select();A=(C==D);};if (B.CheckStartOfBlock()){var F=B.StartBlock;var G=FCKDomTools.GetPreviousSourceElement(F,true,['BODY',B.StartBlockLimit.nodeName],['UL','OL']);A=this._ExecuteBackspace(B,G,F);}else if (FCKBrowserInfo.IsGecko){B.Select();}};B.Release();return A;};FCKEnterKey.prototype._ExecuteBackspace=function(A,B,C){var D=false;if (!B&&C&&C.nodeName.IEquals('LI')&&C.parentNode.parentNode.nodeName.IEquals('LI')){this._OutdentWithSelection(C,A);return true;};if (B&&B.nodeName.IEquals('LI')){var E=FCKDomTools.GetLastChild(B,['UL','OL']);while (E){B=FCKDomTools.GetLastChild(E,'LI');E=FCKDomTools.GetLastChild(B,['UL','OL']);}};if (B&&C){if (C.nodeName.IEquals('LI')&&!B.nodeName.IEquals('LI')){this._OutdentWithSelection(C,A);return true;};var F=C.parentNode;var G=B.nodeName.toLowerCase();if (FCKListsLib.EmptyElements[G]!=null||G=='table'){FCKDomTools.RemoveNode(B);D=true;}else{FCKDomTools.RemoveNode(C);while (F.innerHTML.Trim().length==0){var H=F.parentNode;H.removeChild(F);F=H;};FCKDomTools.TrimNode(C);FCKDomTools.TrimNode(B);A.SetStart(B,2);A.Collapse(true);var I=A.CreateBookmark();FCKDomTools.MoveChildren(C,B);A.MoveToBookmark(I);A.Select();D=true;}};return D;};FCKEnterKey.prototype.DoDelete=function(){var A=false;var B=new FCKDomRange(this.Window);B.MoveToSelection();if (B.CheckIsCollapsed()&&B.CheckEndOfBlock(FCKBrowserInfo.IsGeckoLike)){var C=B.StartBlock;var D=FCKDomTools.GetNextSourceElement(C,true,[B.StartBlockLimit.nodeName],['UL','OL']);A=this._ExecuteBackspace(B,C,D);};B.Release();return A;};FCKEnterKey.prototype._ExecuteEnterBlock=function(A,B){var C=B||new FCKDomRange(this.Window);if (!B) C.MoveToSelection();if (C.StartBlockLimit==C.EndBlockLimit){if (!C.StartBlock) this._FixBlock(C,true,A);if (!C.EndBlock) this._FixBlock(C,false,A);var D=C.StartBlock;var E=C.EndBlock;if (!C.CheckIsEmpty()) C.DeleteContents();if (D==E){var F;var G=C.CheckStartOfBlock();var H=C.CheckEndOfBlock();if (G&&!H){F=D.cloneNode(false);if (FCKBrowserInfo.IsGeckoLike) F.innerHTML=GECKO_BOGUS;D.parentNode.insertBefore(F,D);if (FCKBrowserInfo.IsIE){C.MoveToNodeContents(F);C.Select();};C.MoveToElementEditStart(D);}else{if (H){var I=D.tagName.toUpperCase();if (G&&I=='LI'){this._OutdentWithSelection(D,C);C.Release();return true;}else{if ((/^H[1-6]$/).test(I)||this._HasShift) F=this.Window.document.createElement(A);else{F=D.cloneNode(false);this._RecreateEndingTree(D,F);};if (FCKBrowserInfo.IsGeckoLike){F.innerHTML=GECKO_BOGUS;if (G) D.innerHTML=GECKO_BOGUS;}}}else{C.SetEnd(D,2);var J=C.ExtractContents();F=D.cloneNode(false);FCKDomTools.TrimNode(J.RootNode);if (J.RootNode.firstChild.nodeType==1&&J.RootNode.firstChild.tagName.toUpperCase().Equals('UL','OL')) F.innerHTML=GECKO_BOGUS;J.AppendTo(F);if (FCKBrowserInfo.IsGecko){this._AppendBogusBr(D);this._AppendBogusBr(F);}};if (F){FCKDomTools.InsertAfterNode(D,F);C.MoveToElementEditStart(F);if (FCKBrowserInfo.IsGeckoLike) F.scrollIntoView(false);}}}else{C.MoveToElementEditStart(E);};C.Select();};C.Release();return true;};FCKEnterKey.prototype._ExecuteEnterBr=function(A){var B=new FCKDomRange(this.Window);B.MoveToSelection();if (B.StartBlockLimit==B.EndBlockLimit){B.DeleteContents();B.MoveToSelection();var C=B.CheckStartOfBlock();var D=B.CheckEndOfBlock();var E=B.StartBlock?B.StartBlock.tagName.toUpperCase():'';var F=this._HasShift;if (!F&&E=='LI') return this._ExecuteEnterBlock(null,B);if (!F&&D&&(/^H[1-6]$/).test(E)){FCKDebug.Output('BR - Header');FCKDomTools.InsertAfterNode(B.StartBlock,this.Window.document.createElement('br'));if (FCKBrowserInfo.IsGecko) FCKDomTools.InsertAfterNode(B.StartBlock,this.Window.document.createTextNode(''));B.SetStart(B.StartBlock.nextSibling,FCKBrowserInfo.IsIE?3:1);}else{FCKDebug.Output('BR - No Header');var G=this.Window.document.createElement('br');B.InsertNode(G);if (FCKBrowserInfo.IsGecko) FCKDomTools.InsertAfterNode(G,this.Window.document.createTextNode(''));if (D&&FCKBrowserInfo.IsGeckoLike) this._AppendBogusBr(G.parentNode);if (FCKBrowserInfo.IsIE) B.SetStart(G,4);else B.SetStart(G.nextSibling,1);};B.Collapse(true);B.Select();};B.Release();return true;};FCKEnterKey.prototype._FixBlock=function(A,B,C){var D=A.CreateBookmark();A.Collapse(B);A.Expand('block_contents');var E=this.Window.document.createElement(C);A.ExtractContents().AppendTo(E);FCKDomTools.TrimNode(E);A.InsertNode(E);A.MoveToBookmark(D);};FCKEnterKey.prototype._AppendBogusBr=function(A){if (!A) return;var B=FCKTools.GetLastItem(A.getElementsByTagName('br'));if (!B||B.getAttribute('type',2)!='_moz') A.appendChild(FCKTools.CreateBogusBR(this.Window.document));};FCKEnterKey.prototype._RecreateEndingTree=function(A,B){while ((A=A.lastChild)&&A.nodeType==1&&FCKListsLib.InlineChildReqElements[A.nodeName.toLowerCase()]!=null) B=B.insertBefore(A.cloneNode(false),B.firstChild);};FCKEnterKey.prototype._OutdentWithSelection=function(A,B){var C=B.CreateBookmark();FCKListHandler.OutdentListItem(A);B.MoveToBookmark(C);B.Select();}
+var FCKDocumentProcessor={};FCKDocumentProcessor._Items=[];FCKDocumentProcessor.AppendNew=function(){var A={};this._Items.AddItem(A);return A;};FCKDocumentProcessor.Process=function(A){var B,i=0;while((B=this._Items[i++])) B.ProcessDocument(A);};var FCKDocumentProcessor_CreateFakeImage=function(A,B){var C=FCK.EditorDocument.createElement('IMG');C.className=A;C.src=FCKConfig.FullBasePath+'images/spacer.gif';C.setAttribute('_fckfakelement','true',0);C.setAttribute('_fckrealelement',FCKTempBin.AddElement(B),0);return C;};if (FCKBrowserInfo.IsIE||FCKBrowserInfo.IsOpera){var FCKAnchorsProcessor=FCKDocumentProcessor.AppendNew();FCKAnchorsProcessor.ProcessDocument=function(A){var B=A.getElementsByTagName('A');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.name.length>0){if (C.innerHTML!==''){if (FCKBrowserInfo.IsIE) C.className+=' FCK__AnchorC';}else{var D=FCKDocumentProcessor_CreateFakeImage('FCK__Anchor',C.cloneNode(true));D.setAttribute('_fckanchor','true',0);C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}}}};var FCKPageBreaksProcessor=FCKDocumentProcessor.AppendNew();FCKPageBreaksProcessor.ProcessDocument=function(A){var B=A.getElementsByTagName('DIV');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.style.pageBreakAfter=='always'&&C.childNodes.length==1&&C.childNodes[0].style&&C.childNodes[0].style.display=='none'){var D=FCKDocumentProcessor_CreateFakeImage('FCK__PageBreak',C.cloneNode(true));C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}};var FCKFlashProcessor=FCKDocumentProcessor.AppendNew();FCKFlashProcessor.ProcessDocument=function(A){var B=A.getElementsByTagName('EMBED');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){var D=C.attributes['type'];if ((C.src&&C.src.EndsWith('.swf',true))||(D&&D.nodeValue=='application/x-shockwave-flash')){var E=C.cloneNode(true);if (FCKBrowserInfo.IsIE){var F=['scale','play','loop','menu','wmode','quality'];for (var G=0;G<F.length;G++){var H=C.getAttribute(F[G]);if (H) E.setAttribute(F[G],H);};E.setAttribute('type',D.nodeValue);};var I=FCKDocumentProcessor_CreateFakeImage('FCK__Flash',E);I.setAttribute('_fckflash','true',0);FCKFlashProcessor.RefreshView(I,C);C.parentNode.insertBefore(I,C);C.parentNode.removeChild(C);}}};FCKFlashProcessor.RefreshView=function(A,B){if (B.getAttribute('width')>0) A.style.width=FCKTools.ConvertHtmlSizeToStyle(B.getAttribute('width'));if (B.getAttribute('height')>0) A.style.height=FCKTools.ConvertHtmlSizeToStyle(B.getAttribute('height'));};FCK.GetRealElement=function(A){var e=FCKTempBin.Elements[A.getAttribute('_fckrealelement')];if (A.getAttribute('_fckflash')){if (A.style.width.length>0) e.width=FCKTools.ConvertStyleSizeToHtml(A.style.width);if (A.style.height.length>0) e.height=FCKTools.ConvertStyleSizeToHtml(A.style.height);};return e;};if (FCKBrowserInfo.IsIE){FCKDocumentProcessor.AppendNew().ProcessDocument=function(A){var B=A.getElementsByTagName('HR');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){var D=A.createElement('hr');D.mergeAttributes(C,true);FCKDomTools.InsertAfterNode(C,D);C.parentNode.removeChild(C);}}};FCKDocumentProcessor.AppendNew().ProcessDocument=function(A){var B=A.getElementsByTagName('INPUT');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.type=='hidden'){var D=FCKDocumentProcessor_CreateFakeImage('FCK__InputHidden',C.cloneNode(true));D.setAttribute('_fckinputhidden','true',0);C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}}
+var FCKSelection=FCK.Selection={};
+FCKSelection.GetType=function(){this._Type='Text';var A;try { A=FCK.EditorWindow.getSelection();}catch (e) {};if (A&&A.rangeCount==1){var B=A.getRangeAt(0);if (B.startContainer==B.endContainer&&(B.endOffset-B.startOffset)==1&&B.startContainer.nodeType!=Node.TEXT_NODE) this._Type='Control';};return this._Type;};FCKSelection.GetSelectedElement=function(){if (this.GetType()=='Control'){var A=FCK.EditorWindow.getSelection();return A.anchorNode.childNodes[A.anchorOffset];};return null;};FCKSelection.GetParentElement=function(){if (this.GetType()=='Control') return FCKSelection.GetSelectedElement().parentNode;else{var A=FCK.EditorWindow.getSelection();if (A){var B=A.anchorNode;while (B&&B.nodeType!=1) B=B.parentNode;return B;}};return null;};FCKSelection.SelectNode=function(A){var B=FCK.EditorDocument.createRange();B.selectNode(A);var C=FCK.EditorWindow.getSelection();C.removeAllRanges();C.addRange(B);};FCKSelection.Collapse=function(A){var B=FCK.EditorWindow.getSelection();if (A==null||A===true) B.collapseToStart();else B.collapseToEnd();};FCKSelection.HasAncestorNode=function(A){var B=this.GetSelectedElement();if (!B&&FCK.EditorWindow){try { B=FCK.EditorWindow.getSelection().getRangeAt(0).startContainer;}catch(e){}};while (B){if (B.nodeType==1&&B.tagName==A) return true;B=B.parentNode;};return false;};FCKSelection.MoveToAncestorNode=function(A){var B;var C=this.GetSelectedElement();if (!C) C=FCK.EditorWindow.getSelection().getRangeAt(0).startContainer;while (C){if (C.nodeName==A) return C;C=C.parentNode;};return null;};FCKSelection.Delete=function(){var A=FCK.EditorWindow.getSelection();for (var i=0;i<A.rangeCount;i++){A.getRangeAt(i).deleteContents();};return A;};
+var FCKTableHandler={};FCKTableHandler.InsertRow=function(){var A=FCKSelection.MoveToAncestorNode('TR');if (!A) return;var B=A.cloneNode(true);A.parentNode.insertBefore(B,A);FCKTableHandler.ClearRow(A);};FCKTableHandler.DeleteRows=function(A){if (!A) A=FCKSelection.MoveToAncestorNode('TR');if (!A) return;var B=FCKTools.GetElementAscensor(A,'TABLE');if (B.rows.length==1){FCKTableHandler.DeleteTable(B);return;};A.parentNode.removeChild(A);};FCKTableHandler.DeleteTable=function(A){if (!A){A=FCKSelection.GetSelectedElement();if (!A||A.tagName!='TABLE') A=FCKSelection.MoveToAncestorNode('TABLE');};if (!A) return;FCKSelection.SelectNode(A);FCKSelection.Collapse();A.parentNode.removeChild(A);};FCKTableHandler.InsertColumn=function(){var A=FCKSelection.MoveToAncestorNode('TD')||FCKSelection.MoveToAncestorNode('TH');if (!A) return;var B=FCKTools.GetElementAscensor(A,'TABLE');var C=A.cellIndex+1;for (var i=0;i<B.rows.length;i++){var D=B.rows[i];if (D.cells.length<C) continue;A=D.cells[C-1].cloneNode(false);if (FCKBrowserInfo.IsGecko) A.innerHTML=GECKO_BOGUS;var E=D.cells[C];if (E) D.insertBefore(A,E);else D.appendChild(A);}};FCKTableHandler.DeleteColumns=function(){var A=FCKSelection.MoveToAncestorNode('TD')||FCKSelection.MoveToAncestorNode('TH');if (!A) return;var B=FCKTools.GetElementAscensor(A,'TABLE');var C=A.cellIndex;for (var i=B.rows.length-1;i>=0;i--){var D=B.rows[i];if (C==0&&D.cells.length==1){FCKTableHandler.DeleteRows(D);continue;};if (D.cells[C]) D.removeChild(D.cells[C]);}};FCKTableHandler.InsertCell=function(A){var B=A?A:FCKSelection.MoveToAncestorNode('TD');if (!B) return null;var C=FCK.EditorDocument.createElement('TD');if (FCKBrowserInfo.IsGecko) C.innerHTML=GECKO_BOGUS;if (B.cellIndex==B.parentNode.cells.length-1){B.parentNode.appendChild(C);}else{B.parentNode.insertBefore(C,B.nextSibling);};return C;};FCKTableHandler.DeleteCell=function(A){if (A.parentNode.cells.length==1){FCKTableHandler.DeleteRows(FCKTools.GetElementAscensor(A,'TR'));return;};A.parentNode.removeChild(A);};FCKTableHandler.DeleteCells=function(){var A=FCKTableHandler.GetSelectedCells();for (var i=A.length-1;i>=0;i--){FCKTableHandler.DeleteCell(A[i]);}};FCKTableHandler.MergeCells=function(){var A=FCKTableHandler.GetSelectedCells();if (A.length<2) return;if (A[0].parentNode!=A[A.length-1].parentNode) return;var B=isNaN(A[0].colSpan)?1:A[0].colSpan;var C='';var D=FCK.EditorDocument.createDocumentFragment();for (var i=A.length-1;i>=0;i--){var E=A[i];for (var c=E.childNodes.length-1;c>=0;c--){var F=E.removeChild(E.childNodes[c]);if ((F.hasAttribute&&F.hasAttribute('_moz_editor_bogus_node'))||(F.getAttribute&&F.getAttribute('type',2)=='_moz')) continue;D.insertBefore(F,D.firstChild);};if (i>0){B+=isNaN(E.colSpan)?1:E.colSpan;FCKTableHandler.DeleteCell(E);}};A[0].colSpan=B;if (FCKBrowserInfo.IsGecko&&D.childNodes.length==0) A[0].innerHTML=GECKO_BOGUS;else A[0].appendChild(D);};FCKTableHandler.SplitCell=function(){var A=FCKTableHandler.GetSelectedCells();if (A.length!=1) return;var B=this._CreateTableMap(A[0].parentNode.parentNode);var C=FCKTableHandler._GetCellIndexSpan(B,A[0].parentNode.rowIndex,A[0]);var D=this._GetCollumnCells(B,C);for (var i=0;i<D.length;i++){if (D[i]==A[0]){var E=this.InsertCell(A[0]);if (!isNaN(A[0].rowSpan)&&A[0].rowSpan>1) E.rowSpan=A[0].rowSpan;}else{if (isNaN(D[i].colSpan)) D[i].colSpan=2;else D[i].colSpan+=1;}}};FCKTableHandler._GetCellIndexSpan=function(A,B,C){if (A.length<B+1) return null;var D=A[B];for (var c=0;c<D.length;c++){if (D[c]==C) return c;};return null;};FCKTableHandler._GetCollumnCells=function(A,B){var C=[];for (var r=0;r<A.length;r++){var D=A[r][B];if (D&&(C.length==0||C[C.length-1]!=D)) C[C.length]=D;};return C;};FCKTableHandler._CreateTableMap=function(A){var B=A.rows;var r=-1;var C=[];for (var i=0;i<B.length;i++){r++;if (!C[r]) C[r]=[];var c=-1;for (var j=0;j<B[i].cells.length;j++){var D=B[i].cells[j];c++;while (C[r][c]) c++;var E=isNaN(D.colSpan)?1:D.colSpan;var F=isNaN(D.rowSpan)?1:D.rowSpan;for (var G=0;G<F;G++){if (!C[r+G]) C[r+G]=[];for (var H=0;H<E;H++){C[r+G][c+H]=B[i].cells[j];}};c+=E-1;}};return C;};FCKTableHandler.ClearRow=function(A){var B=A.cells;for (var i=0;i<B.length;i++){if (FCKBrowserInfo.IsGecko) B[i].innerHTML=GECKO_BOGUS;else B[i].innerHTML='';}};
+FCKTableHandler.GetSelectedCells=function(){var A=[];var B=FCK.EditorWindow.getSelection();if (B.rangeCount==1&&B.anchorNode.nodeType==3){var C=FCKTools.GetElementAscensor(B.anchorNode,'TD,TH');if (C){A[0]=C;return A;}};for (var i=0;i<B.rangeCount;i++){var D=B.getRangeAt(i);var E;if (D.startContainer.tagName.Equals('TD','TH')) E=D.startContainer;else E=D.startContainer.childNodes[D.startOffset];if (E.tagName.Equals('TD','TH')) A[A.length]=E;};return A;};
+var FCKXml=function(){};FCKXml.prototype.LoadUrl=function(A){this.Error=false;var B=this;var C=FCKTools.CreateXmlObject('XmlHttp');C.open("GET",A,false);C.send(null);if (C.status==200||C.status==304) this.DOMDocument=C.responseXML;else if (C.status==0&&C.readyState==4) this.DOMDocument=C.responseXML;else this.DOMDocument=null;if (this.DOMDocument==null||this.DOMDocument.firstChild==null){this.Error=true;if (window.confirm('Error loading "'+A+'"\r\nDo you want to see more info?')) alert('URL requested: "'+A+'"\r\nServer response:\r\nStatus: '+C.status+'\r\nResponse text:\r\n'+C.responseText);}};FCKXml.prototype.SelectNodes=function(A,B){if (this.Error) return [];var C=[];var D=this.DOMDocument.evaluate(A,B?B:this.DOMDocument,this.DOMDocument.createNSResolver(this.DOMDocument.documentElement),XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);if (D){var E=D.iterateNext();while(E){C[C.length]=E;E=D.iterateNext();}};return C;};FCKXml.prototype.SelectSingleNode=function(A,B){if (this.Error) return null;var C=this.DOMDocument.evaluate(A,B?B:this.DOMDocument,this.DOMDocument.createNSResolver(this.DOMDocument.documentElement),9,null);if (C&&C.singleNodeValue) return C.singleNodeValue;else return null;}
+var FCKStyleDef=function(A,B){this.Name=A;this.Element=B.toUpperCase();this.IsObjectElement=FCKRegexLib.ObjectElements.test(this.Element);this.Attributes={};};FCKStyleDef.prototype.AddAttribute=function(A,B){this.Attributes[A]=B;};FCKStyleDef.prototype.GetOpenerTag=function(){var s='<'+this.Element;for (var a in this.Attributes) s+=' '+a+'="'+this.Attributes[a]+'"';return s+'>';};FCKStyleDef.prototype.GetCloserTag=function(){return '</'+this.Element+'>';};FCKStyleDef.prototype.RemoveFromSelection=function(){if (FCKSelection.GetType()=='Control') this._RemoveMe(FCK.ToolbarSet.CurrentInstance.Selection.GetSelectedElement());else this._RemoveMe(FCK.ToolbarSet.CurrentInstance.Selection.GetParentElement());}
+FCKStyleDef.prototype.ApplyToSelection=function(){if (FCKSelection.GetType()=='Text'&&!this.IsObjectElement){var A=FCK.ToolbarSet.CurrentInstance.EditorWindow.getSelection();var e=FCK.ToolbarSet.CurrentInstance.EditorDocument.createElement(this.Element);for (var i=0;i<A.rangeCount;i++){e.appendChild(A.getRangeAt(i).extractContents());};this._AddAttributes(e);this._RemoveDuplicates(e);var B=A.getRangeAt(0);B.insertNode(e);}else{var C=FCK.ToolbarSet.CurrentInstance.Selection.GetSelectedElement();if (C.tagName==this.Element) this._AddAttributes(C);}};FCKStyleDef.prototype._AddAttributes=function(A){for (var a in this.Attributes){switch (a.toLowerCase()){case 'src':A.setAttribute('_fcksavedurl',this.Attributes[a],0);default:A.setAttribute(a,this.Attributes[a],0);}}};FCKStyleDef.prototype._RemoveDuplicates=function(A){for (var i=0;i<A.childNodes.length;i++){var B=A.childNodes[i];if (B.nodeType!=1) continue;this._RemoveDuplicates(B);if (this.IsEqual(B)) FCKTools.RemoveOuterTags(B);}};FCKStyleDef.prototype.IsEqual=function(e){if (e.tagName!=this.Element) return false;for (var a in this.Attributes){if (e.getAttribute(a)!=this.Attributes[a]) return false;};return true;};FCKStyleDef.prototype._RemoveMe=function(A){if (!A) return;var B=A.parentNode;if (A.nodeType==1&&this.IsEqual(A)){if (this.IsObjectElement){for (var a in this.Attributes) A.removeAttribute(a,0);return;}else FCKTools.RemoveOuterTags(A);};this._RemoveMe(B);}
+var FCKStylesLoader=function(){this.Styles={};this.StyleGroups={};this.Loaded=false;this.HasObjectElements=false;};FCKStylesLoader.prototype.Load=function(A){var B=new FCKXml();B.LoadUrl(A);var C=B.SelectNodes('Styles/Style');for (var i=0;i<C.length;i++){var D=C[i].attributes.getNamedItem('element').value.toUpperCase();var E=new FCKStyleDef(C[i].attributes.getNamedItem('name').value,D);if (E.IsObjectElement) this.HasObjectElements=true;var F=B.SelectNodes('Attribute',C[i]);for (var j=0;j<F.length;j++){var G=F[j].attributes.getNamedItem('name').value;var H=F[j].attributes.getNamedItem('value').value;if (G.toLowerCase()=='style'){var I=document.createElement('SPAN');I.style.cssText=H;H=I.style.cssText;};E.AddAttribute(G,H);};this.Styles[E.Name]=E;var J=this.StyleGroups[D];if (J==null){this.StyleGroups[D]=[];J=this.StyleGroups[D];};J[J.length]=E;};this.Loaded=true;}
+var FCKNamedCommand=function(A){this.Name=A;};FCKNamedCommand.prototype.Execute=function(){FCK.ExecuteNamedCommand(this.Name);};FCKNamedCommand.prototype.GetState=function(){return FCK.GetNamedCommandState(this.Name);};
+var FCKDialogCommand=function(A,B,C,D,E,F,G){this.Name=A;this.Title=B;this.Url=C;this.Width=D;this.Height=E;this.GetStateFunction=F;this.GetStateParam=G;this.Resizable=false;};FCKDialogCommand.prototype.Execute=function(){FCKDialog.OpenDialog('FCKDialog_'+this.Name,this.Title,this.Url,this.Width,this.Height,null,null,this.Resizable);};FCKDialogCommand.prototype.GetState=function(){if (this.GetStateFunction) return this.GetStateFunction(this.GetStateParam);else return 0;};var FCKUndefinedCommand=function(){this.Name='Undefined';};FCKUndefinedCommand.prototype.Execute=function(){alert(FCKLang.NotImplemented);};FCKUndefinedCommand.prototype.GetState=function(){return 0;};var FCKFontNameCommand=function(){this.Name='FontName';};FCKFontNameCommand.prototype.Execute=function(A){if (A==null||A==""){}else FCK.ExecuteNamedCommand('FontName',A);};FCKFontNameCommand.prototype.GetState=function(){return FCK.GetNamedCommandValue('FontName');};var FCKFontSizeCommand=function(){this.Name='FontSize';};FCKFontSizeCommand.prototype.Execute=function(A){if (typeof(A)=='string') A=parseInt(A,10);if (A==null||A==''){FCK.ExecuteNamedCommand('FontSize',3);}else FCK.ExecuteNamedCommand('FontSize',A);};FCKFontSizeCommand.prototype.GetState=function(){return FCK.GetNamedCommandValue('FontSize');};var FCKFormatBlockCommand=function(){this.Name='FormatBlock';};FCKFormatBlockCommand.prototype.Execute=function(A){if (A==null||A=='') FCK.ExecuteNamedCommand('FormatBlock','<P>');else if (A=='div'&&FCKBrowserInfo.IsGecko) FCK.ExecuteNamedCommand('FormatBlock','div');else FCK.ExecuteNamedCommand('FormatBlock','<'+A+'>');};FCKFormatBlockCommand.prototype.GetState=function(){return FCK.GetNamedCommandValue('FormatBlock');};var FCKPreviewCommand=function(){this.Name='Preview';};FCKPreviewCommand.prototype.Execute=function(){FCK.Preview();};FCKPreviewCommand.prototype.GetState=function(){return 0;};var FCKSaveCommand=function(){this.Name='Save';};FCKSaveCommand.prototype.Execute=function(){var A=FCK.GetParentForm();if (typeof(A.onsubmit)=='function'){var B=A.onsubmit();if (B!=null&&B===false) return;};if (typeof(A.submit)=='function') A.submit();else A.submit.click();};FCKSaveCommand.prototype.GetState=function(){return 0;};var FCKNewPageCommand=function(){this.Name='NewPage';};FCKNewPageCommand.prototype.Execute=function(){FCKUndo.SaveUndoStep();FCK.SetHTML('');FCKUndo.Typing=true;};FCKNewPageCommand.prototype.GetState=function(){return 0;};var FCKSourceCommand=function(){this.Name='Source';};FCKSourceCommand.prototype.Execute=function(){if (FCKConfig.SourcePopup){var A=FCKConfig.ScreenWidth*0.65;var B=FCKConfig.ScreenHeight*0.65;FCKDialog.OpenDialog('FCKDialog_Source',FCKLang.Source,'dialog/fck_source.html',A,B,null,null,true);}else FCK.SwitchEditMode();};FCKSourceCommand.prototype.GetState=function(){return (FCK.EditMode==0?0:1);};var FCKUndoCommand=function(){this.Name='Undo';};FCKUndoCommand.prototype.Execute=function(){if (FCKBrowserInfo.IsIE) FCKUndo.Undo();else FCK.ExecuteNamedCommand('Undo');};FCKUndoCommand.prototype.GetState=function(){if (FCKBrowserInfo.IsIE) return (FCKUndo.CheckUndoState()?0:-1);else return FCK.GetNamedCommandState('Undo');};var FCKRedoCommand=function(){this.Name='Redo';};FCKRedoCommand.prototype.Execute=function(){if (FCKBrowserInfo.IsIE) FCKUndo.Redo();else FCK.ExecuteNamedCommand('Redo');};FCKRedoCommand.prototype.GetState=function(){if (FCKBrowserInfo.IsIE) return (FCKUndo.CheckRedoState()?0:-1);else return FCK.GetNamedCommandState('Redo');};var FCKPageBreakCommand=function(){this.Name='PageBreak';};FCKPageBreakCommand.prototype.Execute=function(){var e=FCK.EditorDocument.createElement('DIV');e.style.pageBreakAfter='always';e.innerHTML='<span style="DISPLAY:none">&nbsp;</span>';var A=FCKDocumentProcessor_CreateFakeImage('FCK__PageBreak',e);A=FCK.InsertElement(A);};FCKPageBreakCommand.prototype.GetState=function(){return 0;};var FCKUnlinkCommand=function(){this.Name='Unlink';};FCKUnlinkCommand.prototype.Execute=function(){if (FCKBrowserInfo.IsGecko){var A=FCK.Selection.MoveToAncestorNode('A');if (A) FCKTools.RemoveOuterTags(A);return;};FCK.ExecuteNamedCommand(this.Name);};FCKUnlinkCommand.prototype.GetState=function(){var A=FCK.GetNamedCommandState(this.Name);if (A==0&&FCK.EditMode==0){var B=FCKSelection.MoveToAncestorNode('A');var C=(B&&B.name.length>0&&B.href.length==0);if (C) A=-1;};return A;};var FCKSelectAllCommand=function(){this.Name='SelectAll';};FCKSelectAllCommand.prototype.Execute=function(){if (FCK.EditMode==0){FCK.ExecuteNamedCommand('SelectAll');}else{var A=FCK.EditingArea.Textarea;if (FCKBrowserInfo.IsIE){A.createTextRange().execCommand('SelectAll');}else{A.selectionStart=0;A.selectionEnd=A.value.length;};A.focus();}};FCKSelectAllCommand.prototype.GetState=function(){return 0;};var FCKPasteCommand=function(){this.Name='Paste';};FCKPasteCommand.prototype={Execute:function(){if (FCKBrowserInfo.IsIE) FCK.Paste();else FCK.ExecuteNamedCommand('Paste');},GetState:function(){return FCK.GetNamedCommandState('Paste');}};
+var FCKSpellCheckCommand=function(){this.Name='SpellCheck';this.IsEnabled=(FCKConfig.SpellChecker=='SpellerPages');};FCKSpellCheckCommand.prototype.Execute=function(){FCKDialog.OpenDialog('FCKDialog_SpellCheck','Spell Check','dialog/fck_spellerpages.html',440,480);};FCKSpellCheckCommand.prototype.GetState=function(){return this.IsEnabled?0:-1;}
+var FCKTextColorCommand=function(A){this.Name=A=='ForeColor'?'TextColor':'BGColor';this.Type=A;var B;if (FCKBrowserInfo.IsIE) B=window;else if (FCK.ToolbarSet._IFrame) B=FCKTools.GetElementWindow(FCK.ToolbarSet._IFrame);else B=window.parent;this._Panel=new FCKPanel(B);this._Panel.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');this._Panel.MainNode.className='FCK_Panel';this._CreatePanelBody(this._Panel.Document,this._Panel.MainNode);FCKTools.DisableSelection(this._Panel.Document.body);};FCKTextColorCommand.prototype.Execute=function(A,B,C){FCK._ActiveColorPanelType=this.Type;this._Panel.Show(A,B,C);};FCKTextColorCommand.prototype.SetColor=function(A){if (FCK._ActiveColorPanelType=='ForeColor') FCK.ExecuteNamedCommand('ForeColor',A);else if (FCKBrowserInfo.IsGeckoLike){if (FCKBrowserInfo.IsGecko&&!FCKConfig.GeckoUseSPAN) FCK.EditorDocument.execCommand('useCSS',false,false);FCK.ExecuteNamedCommand('hilitecolor',A);if (FCKBrowserInfo.IsGecko&&!FCKConfig.GeckoUseSPAN) FCK.EditorDocument.execCommand('useCSS',false,true);}else FCK.ExecuteNamedCommand('BackColor',A);delete FCK._ActiveColorPanelType;};FCKTextColorCommand.prototype.GetState=function(){return 0;};function FCKTextColorCommand_OnMouseOver() { this.className='ColorSelected';};function FCKTextColorCommand_OnMouseOut() { this.className='ColorDeselected';};function FCKTextColorCommand_OnClick(){this.className='ColorDeselected';this.Command.SetColor('#'+this.Color);this.Command._Panel.Hide();};function FCKTextColorCommand_AutoOnClick(){this.className='ColorDeselected';this.Command.SetColor('');this.Command._Panel.Hide();};function FCKTextColorCommand_MoreOnClick(){this.className='ColorDeselected';this.Command._Panel.Hide();FCKDialog.OpenDialog('FCKDialog_Color',FCKLang.DlgColorTitle,'dialog/fck_colorselector.html',400,330,this.Command.SetColor);};FCKTextColorCommand.prototype._CreatePanelBody=function(A,B){function CreateSelectionDiv(){var C=A.createElement("DIV");C.className='ColorDeselected';C.onmouseover=FCKTextColorCommand_OnMouseOver;C.onmouseout=FCKTextColorCommand_OnMouseOut;return C;};var D=B.appendChild(A.createElement("TABLE"));D.className='ForceBaseFont';D.style.tableLayout='fixed';D.cellPadding=0;D.cellSpacing=0;D.border=0;D.width=150;var E=D.insertRow(-1).insertCell(-1);E.colSpan=8;var C=E.appendChild(CreateSelectionDiv());C.innerHTML='<table cellspacing="0" cellpadding="0" width="100%" border="0">\n <tr>\n <td><div class="ColorBoxBorder"><div class="ColorBox" style="background-color: #000000"></div></div></td>\n <td nowrap width="100%" align="center">'+FCKLang.ColorAutomatic+'</td>\n </tr>\n </table>';C.Command=this;C.onclick=FCKTextColorCommand_AutoOnClick;var G=FCKConfig.FontColors.toString().split(',');var H=0;while (H<G.length){var I=D.insertRow(-1);for (var i=0;i<8&&H<G.length;i++,H++){C=I.insertCell(-1).appendChild(CreateSelectionDiv());C.Color=G[H];C.innerHTML='<div class="ColorBoxBorder"><div class="ColorBox" style="background-color: #'+G[H]+'"></div></div>';C.Command=this;C.onclick=FCKTextColorCommand_OnClick;}};E=D.insertRow(-1).insertCell(-1);E.colSpan=8;C=E.appendChild(CreateSelectionDiv());C.innerHTML='<table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td nowrap align="center">'+FCKLang.ColorMoreColors+'</td></tr></table>';C.Command=this;C.onclick=FCKTextColorCommand_MoreOnClick;}
+var FCKPastePlainTextCommand=function(){this.Name='PasteText';};FCKPastePlainTextCommand.prototype.Execute=function(){FCK.PasteAsPlainText();};FCKPastePlainTextCommand.prototype.GetState=function(){return FCK.GetNamedCommandState('Paste');};
+var FCKPasteWordCommand=function(){this.Name='PasteWord';};FCKPasteWordCommand.prototype.Execute=function(){FCK.PasteFromWord();};FCKPasteWordCommand.prototype.GetState=function(){if (FCKConfig.ForcePasteAsPlainText) return -1;else return FCK.GetNamedCommandState('Paste');};
+var FCKTableCommand=function(A){this.Name=A;};FCKTableCommand.prototype.Execute=function(){FCKUndo.SaveUndoStep();switch (this.Name){case 'TableInsertRow':FCKTableHandler.InsertRow();break;case 'TableDeleteRows':FCKTableHandler.DeleteRows();break;case 'TableInsertColumn':FCKTableHandler.InsertColumn();break;case 'TableDeleteColumns':FCKTableHandler.DeleteColumns();break;case 'TableInsertCell':FCKTableHandler.InsertCell();break;case 'TableDeleteCells':FCKTableHandler.DeleteCells();break;case 'TableMergeCells':FCKTableHandler.MergeCells();break;case 'TableSplitCell':FCKTableHandler.SplitCell();break;case 'TableDelete':FCKTableHandler.DeleteTable();break;default:alert(FCKLang.UnknownCommand.replace(/%1/g,this.Name));}};FCKTableCommand.prototype.GetState=function(){return 0;}
+var FCKStyleCommand=function(){this.Name='Style';this.StylesLoader=new FCKStylesLoader();this.StylesLoader.Load(FCKConfig.StylesXmlPath);this.Styles=this.StylesLoader.Styles;};FCKStyleCommand.prototype.Execute=function(A,B){FCKUndo.SaveUndoStep();if (B.Selected) B.Style.RemoveFromSelection();else B.Style.ApplyToSelection();FCKUndo.SaveUndoStep();FCK.Focus();FCK.Events.FireEvent("OnSelectionChange");};FCKStyleCommand.prototype.GetState=function(){if (!FCK.EditorDocument) return -1;var A=FCK.EditorDocument.selection;if (FCKSelection.GetType()=='Control'){var e=FCKSelection.GetSelectedElement();if (e) return this.StylesLoader.StyleGroups[e.tagName]?0:-1;};return 0;};FCKStyleCommand.prototype.GetActiveStyles=function(){var A=[];if (FCKSelection.GetType()=='Control') this._CheckStyle(FCKSelection.GetSelectedElement(),A,false);else this._CheckStyle(FCKSelection.GetParentElement(),A,true);return A;};FCKStyleCommand.prototype._CheckStyle=function(A,B,C){if (!A) return;if (A.nodeType==1){var D=this.StylesLoader.StyleGroups[A.tagName];if (D){for (var i=0;i<D.length;i++){if (D[i].IsEqual(A)) B[B.length]=D[i];}}};if (C) this._CheckStyle(A.parentNode,B,C);}
+var FCKFitWindow=function(){this.Name='FitWindow';};FCKFitWindow.prototype.Execute=function(){var A=window.frameElement;var B=A.style;var C=parent;var D=C.document.documentElement;var E=C.document.body;var F=E.style;var G;if (!this.IsMaximized){if(FCKBrowserInfo.IsIE) C.attachEvent('onresize',FCKFitWindow_Resize);else C.addEventListener('resize',FCKFitWindow_Resize,true);this._ScrollPos=FCKTools.GetScrollPosition(C);G=A;while((G=G.parentNode)){if (G.nodeType==1) G._fckSavedStyles=FCKTools.SaveStyles(G);};if (FCKBrowserInfo.IsIE){this.documentElementOverflow=D.style.overflow;D.style.overflow='hidden';F.overflow='hidden';}else{F.overflow='hidden';F.width='0px';F.height='0px';};this._EditorFrameStyles=FCKTools.SaveStyles(A);var H=FCKTools.GetViewPaneSize(C);B.position="absolute";B.zIndex=FCKConfig.FloatingPanelsZIndex-1;B.left="0px";B.top="0px";B.width=H.Width+"px";B.height=H.Height+"px";if (!FCKBrowserInfo.IsIE){B.borderRight=B.borderBottom="9999px solid white";B.backgroundColor="white";};C.scrollTo(0,0);this.IsMaximized=true;}else{if(FCKBrowserInfo.IsIE) C.detachEvent("onresize",FCKFitWindow_Resize);else C.removeEventListener("resize",FCKFitWindow_Resize,true);G=A;while((G=G.parentNode)){if (G._fckSavedStyles){FCKTools.RestoreStyles(G,G._fckSavedStyles);G._fckSavedStyles=null;}};if (FCKBrowserInfo.IsIE) D.style.overflow=this.documentElementOverflow;FCKTools.RestoreStyles(A,this._EditorFrameStyles);C.scrollTo(this._ScrollPos.X,this._ScrollPos.Y);this.IsMaximized=false;};FCKToolbarItems.GetItem('FitWindow').RefreshState();FCK.EditingArea.MakeEditable();FCK.Focus();};FCKFitWindow.prototype.GetState=function(){if (FCKConfig.ToolbarLocation!='In') return -1;else return (this.IsMaximized?1:0);};function FCKFitWindow_Resize(){var A=FCKTools.GetViewPaneSize(parent);var B=window.frameElement.style;B.width=A.Width+'px';B.height=A.Height+'px';};
+var FCKCommands=FCK.Commands={};FCKCommands.LoadedCommands={};FCKCommands.RegisterCommand=function(A,B){this.LoadedCommands[A]=B;};FCKCommands.GetCommand=function(A){var B=FCKCommands.LoadedCommands[A];if (B) return B;switch (A){case 'DocProps':B=new FCKDialogCommand('DocProps',FCKLang.DocProps,'dialog/fck_docprops.html',400,390,FCKCommands.GetFullPageState);break;case 'Templates':B=new FCKDialogCommand('Templates',FCKLang.DlgTemplatesTitle,'dialog/fck_template.html',380,450);break;case 'Link':B=new FCKDialogCommand('Link',FCKLang.DlgLnkWindowTitle,'dialog/fck_link.html',400,330);break;case 'Unlink':B=new FCKUnlinkCommand();break;case 'Anchor':B=new FCKDialogCommand('Anchor',FCKLang.DlgAnchorTitle,'dialog/fck_anchor.html',370,170);break;case 'BulletedList':B=new FCKDialogCommand('BulletedList',FCKLang.BulletedListProp,'dialog/fck_listprop.html?UL',370,170);break;case 'NumberedList':B=new FCKDialogCommand('NumberedList',FCKLang.NumberedListProp,'dialog/fck_listprop.html?OL',370,170);break;case 'About':B=new FCKDialogCommand('About',FCKLang.About,'dialog/fck_about.html',400,330);break;case 'Find':B=new FCKDialogCommand('Find',FCKLang.DlgFindTitle,'dialog/fck_find.html',340,170);break;case 'Replace':B=new FCKDialogCommand('Replace',FCKLang.DlgReplaceTitle,'dialog/fck_replace.html',340,200);break;case 'Image':B=new FCKDialogCommand('Image',FCKLang.DlgImgTitle,'dialog/fck_image.html',450,400);break;case 'Flash':B=new FCKDialogCommand('Flash',FCKLang.DlgFlashTitle,'dialog/fck_flash.html',450,400);break;case 'SpecialChar':B=new FCKDialogCommand('SpecialChar',FCKLang.DlgSpecialCharTitle,'dialog/fck_specialchar.html',400,320);break;case 'Smiley':B=new FCKDialogCommand('Smiley',FCKLang.DlgSmileyTitle,'dialog/fck_smiley.html',FCKConfig.SmileyWindowWidth,FCKConfig.SmileyWindowHeight);break;case 'Table':B=new FCKDialogCommand('Table',FCKLang.DlgTableTitle,'dialog/fck_table.html',450,250);break;case 'TableProp':B=new FCKDialogCommand('Table',FCKLang.DlgTableTitle,'dialog/fck_table.html?Parent',400,250);break;case 'TableCellProp':B=new FCKDialogCommand('TableCell',FCKLang.DlgCellTitle,'dialog/fck_tablecell.html',550,250);break;case 'Style':B=new FCKStyleCommand();break;case 'FontName':B=new FCKFontNameCommand();break;case 'FontSize':B=new FCKFontSizeCommand();break;case 'FontFormat':B=new FCKFormatBlockCommand();break;case 'Source':B=new FCKSourceCommand();break;case 'Preview':B=new FCKPreviewCommand();break;case 'Save':B=new FCKSaveCommand();break;case 'NewPage':B=new FCKNewPageCommand();break;case 'PageBreak':B=new FCKPageBreakCommand();break;case 'TextColor':B=new FCKTextColorCommand('ForeColor');break;case 'BGColor':B=new FCKTextColorCommand('BackColor');break;case 'Paste':B=new FCKPasteCommand();break;case 'PasteText':B=new FCKPastePlainTextCommand();break;case 'PasteWord':B=new FCKPasteWordCommand();break;case 'TableInsertRow':B=new FCKTableCommand('TableInsertRow');break;case 'TableDeleteRows':B=new FCKTableCommand('TableDeleteRows');break;case 'TableInsertColumn':B=new FCKTableCommand('TableInsertColumn');break;case 'TableDeleteColumns':B=new FCKTableCommand('TableDeleteColumns');break;case 'TableInsertCell':B=new FCKTableCommand('TableInsertCell');break;case 'TableDeleteCells':B=new FCKTableCommand('TableDeleteCells');break;case 'TableMergeCells':B=new FCKTableCommand('TableMergeCells');break;case 'TableSplitCell':B=new FCKTableCommand('TableSplitCell');break;case 'TableDelete':B=new FCKTableCommand('TableDelete');break;case 'Form':B=new FCKDialogCommand('Form',FCKLang.Form,'dialog/fck_form.html',380,230);break;case 'Checkbox':B=new FCKDialogCommand('Checkbox',FCKLang.Checkbox,'dialog/fck_checkbox.html',380,230);break;case 'Radio':B=new FCKDialogCommand('Radio',FCKLang.RadioButton,'dialog/fck_radiobutton.html',380,230);break;case 'TextField':B=new FCKDialogCommand('TextField',FCKLang.TextField,'dialog/fck_textfield.html',380,230);break;case 'Textarea':B=new FCKDialogCommand('Textarea',FCKLang.Textarea,'dialog/fck_textarea.html',380,230);break;case 'HiddenField':B=new FCKDialogCommand('HiddenField',FCKLang.HiddenField,'dialog/fck_hiddenfield.html',380,230);break;case 'Button':B=new FCKDialogCommand('Button',FCKLang.Button,'dialog/fck_button.html',380,230);break;case 'Select':B=new FCKDialogCommand('Select',FCKLang.SelectionField,'dialog/fck_select.html',400,380);break;case 'ImageButton':B=new FCKDialogCommand('ImageButton',FCKLang.ImageButton,'dialog/fck_image.html?ImageButton',450,400);break;case 'SpellCheck':B=new FCKSpellCheckCommand();break;case 'FitWindow':B=new FCKFitWindow();break;case 'Undo':B=new FCKUndoCommand();break;case 'Redo':B=new FCKRedoCommand();break;case 'SelectAll':B=new FCKSelectAllCommand();break;case 'Undefined':B=new FCKUndefinedCommand();break;default:if (FCKRegexLib.NamedCommands.test(A)) B=new FCKNamedCommand(A);else{alert(FCKLang.UnknownCommand.replace(/%1/g,A));return null;}};FCKCommands.LoadedCommands[A]=B;return B;};FCKCommands.GetFullPageState=function(){return FCKConfig.FullPage?0:-1;};
+var FCKPanel=function(A){this.IsRTL=(FCKLang.Dir=='rtl');this.IsContextMenu=false;this._LockCounter=0;this._Window=A||window;var B;if (FCKBrowserInfo.IsIE){this._Popup=this._Window.createPopup();B=this.Document=this._Popup.document;FCK.IECleanup.AddItem(this,FCKPanel_Cleanup);}else{var C=this._IFrame=this._Window.document.createElement('iframe');C.src='javascript:void(0)';C.allowTransparency=true;C.frameBorder='0';C.scrolling='no';C.style.position='absolute';C.style.zIndex=FCKConfig.FloatingPanelsZIndex;C.width=C.height=0;if (this._Window==window.parent&&window.frameElement) window.frameElement.parentNode.insertBefore(C,window.frameElement);else this._Window.document.body.appendChild(C);var D=C.contentWindow;B=this.Document=D.document;var E='';if (FCKBrowserInfo.IsSafari) E='<base href="'+window.document.location+'">';B.open();B.write('<html><head>'+E+'<\/head><body style="margin:0px;padding:0px;"><\/body><\/html>');B.close();FCKTools.AddEventListenerEx(D,'focus',FCKPanel_Window_OnFocus,this);FCKTools.AddEventListenerEx(D,'blur',FCKPanel_Window_OnBlur,this);};B.dir=FCKLang.Dir;B.oncontextmenu=FCKTools.CancelEvent;this.MainNode=B.body.appendChild(B.createElement('DIV'));this.MainNode.style.cssFloat=this.IsRTL?'right':'left';};FCKPanel.prototype.AppendStyleSheet=function(A){FCKTools.AppendStyleSheet(this.Document,A);};FCKPanel.prototype.Preload=function(x,y,A){if (this._Popup) this._Popup.show(x,y,0,0,A);};FCKPanel.prototype.Show=function(x,y,A,B,C){var D;if (this._Popup){this._Popup.show(x,y,0,0,A);this.MainNode.style.width=B?B+'px':'';this.MainNode.style.height=C?C+'px':'';D=this.MainNode.offsetWidth;if (this.IsRTL){if (this.IsContextMenu) x=x-D+1;else if (A) x=(x*-1)+A.offsetWidth-D;};this._Popup.show(x,y,D,this.MainNode.offsetHeight,A);if (this.OnHide){if (this._Timer) CheckPopupOnHide.call(this,true);this._Timer=FCKTools.SetInterval(CheckPopupOnHide,100,this);}}else{if (typeof(FCKFocusManager)!='undefined') FCKFocusManager.Lock();if (this.ParentPanel) this.ParentPanel.Lock();this.MainNode.style.width=B?B+'px':'';this.MainNode.style.height=C?C+'px':'';D=this.MainNode.offsetWidth;if (!B) this._IFrame.width=1;if (!C) this._IFrame.height=1;D=this.MainNode.offsetWidth;var E=FCKTools.GetElementPosition(A.nodeType==9?(FCKTools.IsStrictMode(A)?A.documentElement:A.body):A,this._Window);if (this.IsRTL&&!this.IsContextMenu) x=(x*-1);x+=E.X;y+=E.Y;if (this.IsRTL){if (this.IsContextMenu) x=x-D+1;else if (A) x=x+A.offsetWidth-D;}else{var F=FCKTools.GetViewPaneSize(this._Window);var G=FCKTools.GetScrollPosition(this._Window);var H=F.Height+G.Y;var I=F.Width+G.X;if ((x+D)>I) x-=x+D-I;if ((y+this.MainNode.offsetHeight)>H) y-=y+this.MainNode.offsetHeight-H;};if (x<0) x=0;this._IFrame.style.left=x+'px';this._IFrame.style.top=y+'px';var J=D;var K=this.MainNode.offsetHeight;this._IFrame.width=J;this._IFrame.height=K;this._IFrame.contentWindow.focus();};this._IsOpened=true;FCKTools.RunFunction(this.OnShow,this);};FCKPanel.prototype.Hide=function(A){if (this._Popup) this._Popup.hide();else{if (!this._IsOpened) return;if (typeof(FCKFocusManager)!='undefined') FCKFocusManager.Unlock();this._IFrame.width=this._IFrame.height=0;this._IsOpened=false;if (this.ParentPanel) this.ParentPanel.Unlock();if (!A) FCKTools.RunFunction(this.OnHide,this);}};FCKPanel.prototype.CheckIsOpened=function(){if (this._Popup) return this._Popup.isOpen;else return this._IsOpened;};FCKPanel.prototype.CreateChildPanel=function(){var A=this._Popup?FCKTools.GetDocumentWindow(this.Document):this._Window;var B=new FCKPanel(A);B.ParentPanel=this;return B;};FCKPanel.prototype.Lock=function(){this._LockCounter++;};FCKPanel.prototype.Unlock=function(){if (--this._LockCounter==0&&!this.HasFocus) this.Hide();};function FCKPanel_Window_OnFocus(e,A){A.HasFocus=true;};function FCKPanel_Window_OnBlur(e,A){A.HasFocus=false;if (A._LockCounter==0) FCKTools.RunFunction(A.Hide,A);};function CheckPopupOnHide(A){if (A||!this._Popup.isOpen){window.clearInterval(this._Timer);this._Timer=null;FCKTools.RunFunction(this.OnHide,this);}};function FCKPanel_Cleanup(){this._Popup=null;this._Window=null;this.Document=null;this.MainNode=null;}
+var FCKIcon=function(A){var B=A?typeof(A):'undefined';switch (B){case 'number':this.Path=FCKConfig.SkinPath+'fck_strip.gif';this.Size=16;this.Position=A;break;case 'undefined':this.Path=FCK_SPACER_PATH;break;case 'string':this.Path=A;break;default:this.Path=A[0];this.Size=A[1];this.Position=A[2];}};FCKIcon.prototype.CreateIconElement=function(A){var B,eIconImage;if (this.Position){var C='-'+((this.Position-1)*this.Size)+'px';if (FCKBrowserInfo.IsIE){B=A.createElement('DIV');eIconImage=B.appendChild(A.createElement('IMG'));eIconImage.src=this.Path;eIconImage.style.top=C;}else{B=A.createElement('IMG');B.src=FCK_SPACER_PATH;B.style.backgroundPosition='0px '+C;B.style.backgroundImage='url('+this.Path+')';}}else{if (FCKBrowserInfo.IsIE){B=A.createElement('DIV');eIconImage=B.appendChild(A.createElement('IMG'));eIconImage.src=this.Path?this.Path:FCK_SPACER_PATH;}else{B=A.createElement('IMG');B.src=this.Path?this.Path:FCK_SPACER_PATH;}};B.className='TB_Button_Image';return B;}
+var FCKToolbarButtonUI=function(A,B,C,D,E,F){this.Name=A;this.Label=B||A;this.Tooltip=C||this.Label;this.Style=E||0;this.State=F||0;this.Icon=new FCKIcon(D);if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKToolbarButtonUI_Cleanup);};FCKToolbarButtonUI.prototype._CreatePaddingElement=function(A){var B=A.createElement('IMG');B.className='TB_Button_Padding';B.src=FCK_SPACER_PATH;return B;};FCKToolbarButtonUI.prototype.Create=function(A){var B=this.MainElement;if (B){FCKToolbarButtonUI_Cleanup.call(this);if (B.parentNode) B.parentNode.removeChild(B);B=this.MainElement=null;};var C=FCKTools.GetElementDocument(A);B=this.MainElement=C.createElement('DIV');B._FCKButton=this;B.title=this.Tooltip;if (FCKBrowserInfo.IsGecko) B.onmousedown=FCKTools.CancelEvent;this.ChangeState(this.State,true);if (this.Style==0&&!this.ShowArrow){B.appendChild(this.Icon.CreateIconElement(C));}else{var D=B.appendChild(C.createElement('TABLE'));D.cellPadding=0;D.cellSpacing=0;var E=D.insertRow(-1);var F=E.insertCell(-1);if (this.Style==0||this.Style==2) F.appendChild(this.Icon.CreateIconElement(C));else F.appendChild(this._CreatePaddingElement(C));if (this.Style==1||this.Style==2){F=E.insertCell(-1);F.className='TB_Button_Text';F.noWrap=true;F.appendChild(C.createTextNode(this.Label));};if (this.ShowArrow){if (this.Style!=0){E.insertCell(-1).appendChild(this._CreatePaddingElement(C));};F=E.insertCell(-1);var G=F.appendChild(C.createElement('IMG'));G.src=FCKConfig.SkinPath+'images/toolbar.buttonarrow.gif';G.width=5;G.height=3;};F=E.insertCell(-1);F.appendChild(this._CreatePaddingElement(C));};A.appendChild(B);};FCKToolbarButtonUI.prototype.ChangeState=function(A,B){if (!B&&this.State==A) return;var e=this.MainElement;switch (parseInt(A,10)){case 0:e.className='TB_Button_Off';e.onmouseover=FCKToolbarButton_OnMouseOverOff;e.onmouseout=FCKToolbarButton_OnMouseOutOff;e.onclick=FCKToolbarButton_OnClick;break;case 1:e.className='TB_Button_On';e.onmouseover=FCKToolbarButton_OnMouseOverOn;e.onmouseout=FCKToolbarButton_OnMouseOutOn;e.onclick=FCKToolbarButton_OnClick;break;case -1:e.className='TB_Button_Disabled';e.onmouseover=null;e.onmouseout=null;e.onclick=null;break;};this.State=A;};function FCKToolbarButtonUI_Cleanup(){if (this.MainElement){this.MainElement._FCKButton=null;this.MainElement=null;}};function FCKToolbarButton_OnMouseOverOn(){this.className='TB_Button_On_Over';};function FCKToolbarButton_OnMouseOutOn(){this.className='TB_Button_On';};function FCKToolbarButton_OnMouseOverOff(){this.className='TB_Button_Off_Over';};function FCKToolbarButton_OnMouseOutOff(){this.className='TB_Button_Off';};function FCKToolbarButton_OnClick(e){if (this._FCKButton.OnClick) this._FCKButton.OnClick(this._FCKButton);};
+var FCKToolbarButton=function(A,B,C,D,E,F,G){this.CommandName=A;this.Label=B;this.Tooltip=C;this.Style=D;this.SourceView=E?true:false;this.ContextSensitive=F?true:false;if (G==null) this.IconPath=FCKConfig.SkinPath+'toolbar/'+A.toLowerCase()+'.gif';else if (typeof(G)=='number') this.IconPath=[FCKConfig.SkinPath+'fck_strip.gif',16,G];};FCKToolbarButton.prototype.Create=function(A){this._UIButton=new FCKToolbarButtonUI(this.CommandName,this.Label,this.Tooltip,this.IconPath,this.Style);this._UIButton.OnClick=this.Click;this._UIButton._ToolbarButton=this;this._UIButton.Create(A);};FCKToolbarButton.prototype.RefreshState=function(){var A=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).GetState();if (A==this._UIButton.State) return;this._UIButton.ChangeState(A);};FCKToolbarButton.prototype.Click=function(){var A=this._ToolbarButton||this;FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(A.CommandName).Execute();};FCKToolbarButton.prototype.Enable=function(){this.RefreshState();};FCKToolbarButton.prototype.Disable=function(){this._UIButton.ChangeState(-1);}
+var FCKSpecialCombo=function(A,B,C,D,E){this.FieldWidth=B||100;this.PanelWidth=C||150;this.PanelMaxHeight=D||150;this.Label='&nbsp;';this.Caption=A;this.Tooltip=A;this.Style=2;this.Enabled=true;this.Items={};this._Panel=new FCKPanel(E||window);this._Panel.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');this._PanelBox=this._Panel.MainNode.appendChild(this._Panel.Document.createElement('DIV'));this._PanelBox.className='SC_Panel';this._PanelBox.style.width=this.PanelWidth+'px';this._PanelBox.innerHTML='<table cellpadding="0" cellspacing="0" width="100%" style="TABLE-LAYOUT: fixed"><tr><td nowrap></td></tr></table>';this._ItemsHolderEl=this._PanelBox.getElementsByTagName('TD')[0];if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKSpecialCombo_Cleanup);};function FCKSpecialCombo_ItemOnMouseOver(){this.className+=' SC_ItemOver';};function FCKSpecialCombo_ItemOnMouseOut(){this.className=this.originalClass;};function FCKSpecialCombo_ItemOnClick(){this.className=this.originalClass;this.FCKSpecialCombo._Panel.Hide();this.FCKSpecialCombo.SetLabel(this.FCKItemLabel);if (typeof(this.FCKSpecialCombo.OnSelect)=='function') this.FCKSpecialCombo.OnSelect(this.FCKItemID,this);};FCKSpecialCombo.prototype.AddItem=function(A,B,C,D){var E=this._ItemsHolderEl.appendChild(this._Panel.Document.createElement('DIV'));E.className=E.originalClass='SC_Item';E.innerHTML=B;E.FCKItemID=A;E.FCKItemLabel=C||A;E.FCKSpecialCombo=this;E.Selected=false;if (FCKBrowserInfo.IsIE) E.style.width='100%';if (D) E.style.backgroundColor=D;E.onmouseover=FCKSpecialCombo_ItemOnMouseOver;E.onmouseout=FCKSpecialCombo_ItemOnMouseOut;E.onclick=FCKSpecialCombo_ItemOnClick;this.Items[A.toString().toLowerCase()]=E;return E;};FCKSpecialCombo.prototype.SelectItem=function(A){A=A?A.toString().toLowerCase():'';var B=this.Items[A];if (B){B.className=B.originalClass='SC_ItemSelected';B.Selected=true;}};FCKSpecialCombo.prototype.SelectItemByLabel=function(A,B){for (var C in this.Items){var D=this.Items[C];if (D.FCKItemLabel==A){D.className=D.originalClass='SC_ItemSelected';D.Selected=true;if (B) this.SetLabel(A);}}};FCKSpecialCombo.prototype.DeselectAll=function(A){for (var i in this.Items){this.Items[i].className=this.Items[i].originalClass='SC_Item';this.Items[i].Selected=false;};if (A) this.SetLabel('');};FCKSpecialCombo.prototype.SetLabelById=function(A){A=A?A.toString().toLowerCase():'';var B=this.Items[A];this.SetLabel(B?B.FCKItemLabel:'');};FCKSpecialCombo.prototype.SetLabel=function(A){this.Label=A.length==0?'&nbsp;':A;if (this._LabelEl){this._LabelEl.innerHTML=this.Label;FCKTools.DisableSelection(this._LabelEl);}};FCKSpecialCombo.prototype.SetEnabled=function(A){this.Enabled=A;this._OuterTable.className=A?'':'SC_FieldDisabled';};FCKSpecialCombo.prototype.Create=function(A){var B=FCKTools.GetElementDocument(A);var C=this._OuterTable=A.appendChild(B.createElement('TABLE'));C.cellPadding=0;C.cellSpacing=0;C.insertRow(-1);var D;var E;switch (this.Style){case 0:D='TB_ButtonType_Icon';E=false;break;case 1:D='TB_ButtonType_Text';E=false;break;case 2:E=true;break;};if (this.Caption&&this.Caption.length>0&&E){var F=C.rows[0].insertCell(-1);F.innerHTML=this.Caption;F.className='SC_FieldCaption';};var G=FCKTools.AppendElement(C.rows[0].insertCell(-1),'div');if (E){G.className='SC_Field';G.style.width=this.FieldWidth+'px';G.innerHTML='<table width="100%" cellpadding="0" cellspacing="0" style="TABLE-LAYOUT: fixed;"><tbody><tr><td class="SC_FieldLabel"><label>&nbsp;</label></td><td class="SC_FieldButton">&nbsp;</td></tr></tbody></table>';this._LabelEl=G.getElementsByTagName('label')[0];this._LabelEl.innerHTML=this.Label;}else{G.className='TB_Button_Off';G.innerHTML='<table title="'+this.Tooltip+'" class="'+D+'" cellspacing="0" cellpadding="0" border="0"><tr><td><img class="TB_Button_Padding" src="'+FCK_SPACER_PATH+'" /></td><td class="TB_Text">'+this.Caption+'</td><td><img class="TB_Button_Padding" src="'+FCK_SPACER_PATH+'" /></td><td class="TB_ButtonArrow"><img src="'+FCKConfig.SkinPath+'images/toolbar.buttonarrow.gif" width="5" height="3"></td><td><img class="TB_Button_Padding" src="'+FCK_SPACER_PATH+'" /></td></tr></table>';};G.SpecialCombo=this;G.onmouseover=FCKSpecialCombo_OnMouseOver;G.onmouseout=FCKSpecialCombo_OnMouseOut;G.onclick=FCKSpecialCombo_OnClick;FCKTools.DisableSelection(this._Panel.Document.body);};function FCKSpecialCombo_Cleanup(){this._LabelEl=null;this._OuterTable=null;this._ItemsHolderEl=null;this._PanelBox=null;if (this.Items){for (var A in this.Items) this.Items[A]=null;}};function FCKSpecialCombo_OnMouseOver(){if (this.SpecialCombo.Enabled){switch (this.SpecialCombo.Style){case 0:this.className='TB_Button_On_Over';break;case 1:this.className='TB_Button_On_Over';break;case 2:this.className='SC_Field SC_FieldOver';break;}}};function FCKSpecialCombo_OnMouseOut(){switch (this.SpecialCombo.Style){case 0:this.className='TB_Button_Off';break;case 1:this.className='TB_Button_Off';break;case 2:this.className='SC_Field';break;}};function FCKSpecialCombo_OnClick(e){var A=this.SpecialCombo;if (A.Enabled){var B=A._Panel;var C=A._PanelBox;var D=A._ItemsHolderEl;var E=A.PanelMaxHeight;if (A.OnBeforeClick) A.OnBeforeClick(A);if (FCKBrowserInfo.IsIE) B.Preload(0,this.offsetHeight,this);if (D.offsetHeight>E) C.style.height=E+'px';else C.style.height='';B.Show(0,this.offsetHeight,this);}};
+var FCKToolbarSpecialCombo=function(){this.SourceView=false;this.ContextSensitive=true;this._LastValue=null;};function FCKToolbarSpecialCombo_OnSelect(A,B){FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).Execute(A,B);};FCKToolbarSpecialCombo.prototype.Create=function(A){this._Combo=new FCKSpecialCombo(this.GetLabel(),this.FieldWidth,this.PanelWidth,this.PanelMaxHeight,FCKBrowserInfo.IsIE?window:FCKTools.GetElementWindow(A).parent);this._Combo.Tooltip=this.Tooltip;this._Combo.Style=this.Style;this.CreateItems(this._Combo);this._Combo.Create(A);this._Combo.CommandName=this.CommandName;this._Combo.OnSelect=FCKToolbarSpecialCombo_OnSelect;};function FCKToolbarSpecialCombo_RefreshActiveItems(A,B){A.DeselectAll();A.SelectItem(B);A.SetLabelById(B);};FCKToolbarSpecialCombo.prototype.RefreshState=function(){var A;var B=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).GetState();if (B!=-1){A=1;if (this.RefreshActiveItems) this.RefreshActiveItems(this._Combo,B);else{if (this._LastValue!=B){this._LastValue=B;FCKToolbarSpecialCombo_RefreshActiveItems(this._Combo,B);}}}else A=-1;if (A==this.State) return;if (A==-1){this._Combo.DeselectAll();this._Combo.SetLabel('');};this.State=A;this._Combo.SetEnabled(A!=-1);};FCKToolbarSpecialCombo.prototype.Enable=function(){this.RefreshState();};FCKToolbarSpecialCombo.prototype.Disable=function(){this.State=-1;this._Combo.DeselectAll();this._Combo.SetLabel('');this._Combo.SetEnabled(false);};
+var FCKToolbarFontsCombo=function(A,B){this.CommandName='FontName';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;};FCKToolbarFontsCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarFontsCombo.prototype.GetLabel=function(){return FCKLang.Font;};FCKToolbarFontsCombo.prototype.CreateItems=function(A){var B=FCKConfig.FontNames.split(';');for (var i=0;i<B.length;i++) this._Combo.AddItem(B[i],'<font face="'+B[i]+'" style="font-size: 12px">'+B[i]+'</font>');}
+var FCKToolbarFontSizeCombo=function(A,B){this.CommandName='FontSize';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;};FCKToolbarFontSizeCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarFontSizeCombo.prototype.GetLabel=function(){return FCKLang.FontSize;};FCKToolbarFontSizeCombo.prototype.CreateItems=function(A){A.FieldWidth=70;var B=FCKConfig.FontSizes.split(';');for (var i=0;i<B.length;i++){var C=B[i].split('/');this._Combo.AddItem(C[0],'<font size="'+C[0]+'">'+C[1]+'</font>',C[1]);}}
+var FCKToolbarFontFormatCombo=function(A,B){this.CommandName='FontFormat';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;this.NormalLabel='Normal';this.PanelWidth=190;};FCKToolbarFontFormatCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarFontFormatCombo.prototype.GetLabel=function(){return FCKLang.FontFormat;};FCKToolbarFontFormatCombo.prototype.CreateItems=function(A){var B=A._Panel.Document;FCKTools.AppendStyleSheet(B,FCKConfig.ToolbarComboPreviewCSS);if (FCKConfig.BodyId&&FCKConfig.BodyId.length>0) B.body.id=FCKConfig.BodyId;if (FCKConfig.BodyClass&&FCKConfig.BodyClass.length>0) B.body.className+=' '+FCKConfig.BodyClass;var C=FCKLang['FontFormats'].split(';');var D={p:C[0],pre:C[1],address:C[2],h1:C[3],h2:C[4],h3:C[5],h4:C[6],h5:C[7],h6:C[8],div:C[9]};var E=FCKConfig.FontFormats.split(';');for (var i=0;i<E.length;i++){var F=E[i];var G=D[F];if (F=='p') this.NormalLabel=G;this._Combo.AddItem(F,'<div class="BaseFont"><'+F+'>'+G+'</'+F+'></div>',G);}};if (FCKBrowserInfo.IsIE){FCKToolbarFontFormatCombo.prototype.RefreshActiveItems=function(A,B){if (B==this.NormalLabel){if (A.Label!='&nbsp;') A.DeselectAll(true);}else{if (this._LastValue==B) return;A.SelectItemByLabel(B,true);};this._LastValue=B;}}
+var FCKToolbarStyleCombo=function(A,B){this.CommandName='Style';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;};FCKToolbarStyleCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarStyleCombo.prototype.GetLabel=function(){return FCKLang.Style;};FCKToolbarStyleCombo.prototype.CreateItems=function(A){var B=A._Panel.Document;FCKTools.AppendStyleSheet(B,FCKConfig.ToolbarComboPreviewCSS);B.body.className+=' ForceBaseFont';if (FCKConfig.BodyId&&FCKConfig.BodyId.length>0) B.body.id=FCKConfig.BodyId;if (FCKConfig.BodyClass&&FCKConfig.BodyClass.length>0) B.body.className+=' '+FCKConfig.BodyClass;if (!(FCKBrowserInfo.IsGecko&&FCKBrowserInfo.IsGecko10)) A.OnBeforeClick=this.RefreshVisibleItems;var C=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).Styles;for (var s in C){var D=C[s];var E;if (D.IsObjectElement) E=A.AddItem(s,s);else E=A.AddItem(s,D.GetOpenerTag()+s+D.GetCloserTag());E.Style=D;}};FCKToolbarStyleCombo.prototype.RefreshActiveItems=function(A){A.DeselectAll();var B=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).GetActiveStyles();if (B.length>0){for (var i=0;i<B.length;i++) A.SelectItem(B[i].Name);A.SetLabelById(B[0].Name);}else A.SetLabel('');};FCKToolbarStyleCombo.prototype.RefreshVisibleItems=function(A){if (FCKSelection.GetType()=='Control') var B=FCKSelection.GetSelectedElement().tagName;for (var i in A.Items){var C=A.Items[i];if ((B&&C.Style.Element==B)||(!B&&!C.Style.IsObjectElement)) C.style.display='';else C.style.display='none';}}
+var FCKToolbarPanelButton=function(A,B,C,D,E){this.CommandName=A;var F;if (E==null) F=FCKConfig.SkinPath+'toolbar/'+A.toLowerCase()+'.gif';else if (typeof(E)=='number') F=[FCKConfig.SkinPath+'fck_strip.gif',16,E];var G=this._UIButton=new FCKToolbarButtonUI(A,B,C,F,D);G._FCKToolbarPanelButton=this;G.ShowArrow=true;G.OnClick=FCKToolbarPanelButton_OnButtonClick;};FCKToolbarPanelButton.prototype.TypeName='FCKToolbarPanelButton';FCKToolbarPanelButton.prototype.Create=function(A){A.className+='Menu';this._UIButton.Create(A);var B=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName)._Panel;B._FCKToolbarPanelButton=this;var C=B.Document.body.appendChild(B.Document.createElement('div'));C.style.position='absolute';C.style.top='0px';var D=this.LineImg=C.appendChild(B.Document.createElement('IMG'));D.className='TB_ConnectionLine';D.src=FCK_SPACER_PATH;B.OnHide=FCKToolbarPanelButton_OnPanelHide;};function FCKToolbarPanelButton_OnButtonClick(A){var B=this._FCKToolbarPanelButton;var e=B._UIButton.MainElement;B._UIButton.ChangeState(1);B.LineImg.style.width=(e.offsetWidth-2)+'px';FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(B.CommandName).Execute(0,e.offsetHeight-1,e);};function FCKToolbarPanelButton_OnPanelHide(){var A=this._FCKToolbarPanelButton;A._UIButton.ChangeState(0);};FCKToolbarPanelButton.prototype.RefreshState=FCKToolbarButton.prototype.RefreshState;FCKToolbarPanelButton.prototype.Enable=FCKToolbarButton.prototype.Enable;FCKToolbarPanelButton.prototype.Disable=FCKToolbarButton.prototype.Disable;
+var FCKToolbarItems={};FCKToolbarItems.LoadedItems={};FCKToolbarItems.RegisterItem=function(A,B){this.LoadedItems[A]=B;};FCKToolbarItems.GetItem=function(A){var B=FCKToolbarItems.LoadedItems[A];if (B) return B;switch (A){case 'Source':B=new FCKToolbarButton('Source',FCKLang.Source,null,2,true,true,1);break;case 'DocProps':B=new FCKToolbarButton('DocProps',FCKLang.DocProps,null,null,null,null,2);break;case 'Save':B=new FCKToolbarButton('Save',FCKLang.Save,null,null,true,null,3);break;case 'NewPage':B=new FCKToolbarButton('NewPage',FCKLang.NewPage,null,null,true,null,4);break;case 'Preview':B=new FCKToolbarButton('Preview',FCKLang.Preview,null,null,true,null,5);break;case 'Templates':B=new FCKToolbarButton('Templates',FCKLang.Templates,null,null,null,null,6);break;case 'About':B=new FCKToolbarButton('About',FCKLang.About,null,null,true,null,47);break;case 'Cut':B=new FCKToolbarButton('Cut',FCKLang.Cut,null,null,false,true,7);break;case 'Copy':B=new FCKToolbarButton('Copy',FCKLang.Copy,null,null,false,true,8);break;case 'Paste':B=new FCKToolbarButton('Paste',FCKLang.Paste,null,null,false,true,9);break;case 'PasteText':B=new FCKToolbarButton('PasteText',FCKLang.PasteText,null,null,false,true,10);break;case 'PasteWord':B=new FCKToolbarButton('PasteWord',FCKLang.PasteWord,null,null,false,true,11);break;case 'Print':B=new FCKToolbarButton('Print',FCKLang.Print,null,null,false,true,12);break;case 'SpellCheck':B=new FCKToolbarButton('SpellCheck',FCKLang.SpellCheck,null,null,null,null,13);break;case 'Undo':B=new FCKToolbarButton('Undo',FCKLang.Undo,null,null,false,true,14);break;case 'Redo':B=new FCKToolbarButton('Redo',FCKLang.Redo,null,null,false,true,15);break;case 'SelectAll':B=new FCKToolbarButton('SelectAll',FCKLang.SelectAll,null,null,true,null,18);break;case 'RemoveFormat':B=new FCKToolbarButton('RemoveFormat',FCKLang.RemoveFormat,null,null,false,true,19);break;case 'FitWindow':B=new FCKToolbarButton('FitWindow',FCKLang.FitWindow,null,null,true,true,66);break;case 'Bold':B=new FCKToolbarButton('Bold',FCKLang.Bold,null,null,false,true,20);break;case 'Italic':B=new FCKToolbarButton('Italic',FCKLang.Italic,null,null,false,true,21);break;case 'Underline':B=new FCKToolbarButton('Underline',FCKLang.Underline,null,null,false,true,22);break;case 'StrikeThrough':B=new FCKToolbarButton('StrikeThrough',FCKLang.StrikeThrough,null,null,false,true,23);break;case 'Subscript':B=new FCKToolbarButton('Subscript',FCKLang.Subscript,null,null,false,true,24);break;case 'Superscript':B=new FCKToolbarButton('Superscript',FCKLang.Superscript,null,null,false,true,25);break;case 'OrderedList':B=new FCKToolbarButton('InsertOrderedList',FCKLang.NumberedListLbl,FCKLang.NumberedList,null,false,true,26);break;case 'UnorderedList':B=new FCKToolbarButton('InsertUnorderedList',FCKLang.BulletedListLbl,FCKLang.BulletedList,null,false,true,27);break;case 'Outdent':B=new FCKToolbarButton('Outdent',FCKLang.DecreaseIndent,null,null,false,true,28);break;case 'Indent':B=new FCKToolbarButton('Indent',FCKLang.IncreaseIndent,null,null,false,true,29);break;case 'Link':B=new FCKToolbarButton('Link',FCKLang.InsertLinkLbl,FCKLang.InsertLink,null,false,true,34);break;case 'Unlink':B=new FCKToolbarButton('Unlink',FCKLang.RemoveLink,null,null,false,true,35);break;case 'Anchor':B=new FCKToolbarButton('Anchor',FCKLang.Anchor,null,null,null,null,36);break;case 'Image':B=new FCKToolbarButton('Image',FCKLang.InsertImageLbl,FCKLang.InsertImage,null,false,true,37);break;case 'Flash':B=new FCKToolbarButton('Flash',FCKLang.InsertFlashLbl,FCKLang.InsertFlash,null,false,true,38);break;case 'Table':B=new FCKToolbarButton('Table',FCKLang.InsertTableLbl,FCKLang.InsertTable,null,false,true,39);break;case 'SpecialChar':B=new FCKToolbarButton('SpecialChar',FCKLang.InsertSpecialCharLbl,FCKLang.InsertSpecialChar,null,false,true,42);break;case 'Smiley':B=new FCKToolbarButton('Smiley',FCKLang.InsertSmileyLbl,FCKLang.InsertSmiley,null,false,true,41);break;case 'PageBreak':B=new FCKToolbarButton('PageBreak',FCKLang.PageBreakLbl,FCKLang.PageBreak,null,false,true,43);break;case 'Rule':B=new FCKToolbarButton('InsertHorizontalRule',FCKLang.InsertLineLbl,FCKLang.InsertLine,null,false,true,40);break;case 'JustifyLeft':B=new FCKToolbarButton('JustifyLeft',FCKLang.LeftJustify,null,null,false,true,30);break;case 'JustifyCenter':B=new FCKToolbarButton('JustifyCenter',FCKLang.CenterJustify,null,null,false,true,31);break;case 'JustifyRight':B=new FCKToolbarButton('JustifyRight',FCKLang.RightJustify,null,null,false,true,32);break;case 'JustifyFull':B=new FCKToolbarButton('JustifyFull',FCKLang.BlockJustify,null,null,false,true,33);break;case 'Style':B=new FCKToolbarStyleCombo();break;case 'FontName':B=new FCKToolbarFontsCombo();break;case 'FontSize':B=new FCKToolbarFontSizeCombo();break;case 'FontFormat':B=new FCKToolbarFontFormatCombo();break;case 'TextColor':B=new FCKToolbarPanelButton('TextColor',FCKLang.TextColor,null,null,45);break;case 'BGColor':B=new FCKToolbarPanelButton('BGColor',FCKLang.BGColor,null,null,46);break;case 'Find':B=new FCKToolbarButton('Find',FCKLang.Find,null,null,null,null,16);break;case 'Replace':B=new FCKToolbarButton('Replace',FCKLang.Replace,null,null,null,null,17);break;case 'Form':B=new FCKToolbarButton('Form',FCKLang.Form,null,null,null,null,48);break;case 'Checkbox':B=new FCKToolbarButton('Checkbox',FCKLang.Checkbox,null,null,null,null,49);break;case 'Radio':B=new FCKToolbarButton('Radio',FCKLang.RadioButton,null,null,null,null,50);break;case 'TextField':B=new FCKToolbarButton('TextField',FCKLang.TextField,null,null,null,null,51);break;case 'Textarea':B=new FCKToolbarButton('Textarea',FCKLang.Textarea,null,null,null,null,52);break;case 'HiddenField':B=new FCKToolbarButton('HiddenField',FCKLang.HiddenField,null,null,null,null,56);break;case 'Button':B=new FCKToolbarButton('Button',FCKLang.Button,null,null,null,null,54);break;case 'Select':B=new FCKToolbarButton('Select',FCKLang.SelectionField,null,null,null,null,53);break;case 'ImageButton':B=new FCKToolbarButton('ImageButton',FCKLang.ImageButton,null,null,null,null,55);break;default:alert(FCKLang.UnknownToolbarItem.replace(/%1/g,A));return null;};FCKToolbarItems.LoadedItems[A]=B;return B;}
+var FCKToolbar=function(){this.Items=[];if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKToolbar_Cleanup);};FCKToolbar.prototype.AddItem=function(A){return this.Items[this.Items.length]=A;};FCKToolbar.prototype.AddButton=function(A,B,C,D,E,F){if (typeof(D)=='number') D=[this.DefaultIconsStrip,this.DefaultIconSize,D];var G=new FCKToolbarButtonUI(A,B,C,D,E,F);G._FCKToolbar=this;G.OnClick=FCKToolbar_OnItemClick;return this.AddItem(G);};function FCKToolbar_OnItemClick(A){var B=A._FCKToolbar;if (B.OnItemClick) B.OnItemClick(B,A);};FCKToolbar.prototype.AddSeparator=function(){this.AddItem(new FCKToolbarSeparator());};FCKToolbar.prototype.Create=function(A){if (this.MainElement){if (this.MainElement.parentNode) this.MainElement.parentNode.removeChild(this.MainElement);this.MainElement=null;};var B=FCKTools.GetElementDocument(A);var e=this.MainElement=B.createElement('table');e.className='TB_Toolbar';e.style.styleFloat=e.style.cssFloat=(FCKLang.Dir=='ltr'?'left':'right');e.dir=FCKLang.Dir;e.cellPadding=0;e.cellSpacing=0;this.RowElement=e.insertRow(-1);var C;if (!this.HideStart){C=this.RowElement.insertCell(-1);C.appendChild(B.createElement('div')).className='TB_Start';};for (var i=0;i<this.Items.length;i++){this.Items[i].Create(this.RowElement.insertCell(-1));};if (!this.HideEnd){C=this.RowElement.insertCell(-1);C.appendChild(B.createElement('div')).className='TB_End';};A.appendChild(e);};function FCKToolbar_Cleanup(){this.MainElement=null;this.RowElement=null;};var FCKToolbarSeparator=function(){};FCKToolbarSeparator.prototype.Create=function(A){FCKTools.AppendElement(A,'div').className='TB_Separator';}
+var FCKToolbarBreak=function(){};FCKToolbarBreak.prototype.Create=function(A){var B=A.ownerDocument.createElement('div');B.style.clear=B.style.cssFloat=FCKLang.Dir=='rtl'?'right':'left';A.appendChild(B);}
+function FCKToolbarSet_Create(A){var B;var C=A||FCKConfig.ToolbarLocation;switch (C){case 'In':document.getElementById('xToolbarRow').style.display='';B=new FCKToolbarSet(document);break;default:FCK.Events.AttachEvent('OnBlur',FCK_OnBlur);FCK.Events.AttachEvent('OnFocus',FCK_OnFocus);var D;var E=C.match(/^Out:(.+)\((\w+)\)$/);if (E){D=eval('parent.'+E[1]).document.getElementById(E[2]);}else{E=C.match(/^Out:(\w+)$/);if (E) D=parent.document.getElementById(E[1]);};if (!D){alert('Invalid value for "ToolbarLocation"');return this._Init('In');};B=D.__FCKToolbarSet;if (B) break;var F=FCKTools.GetElementDocument(D).createElement('iframe');F.src='javascript:void(0)';F.frameBorder=0;F.width='100%';F.height='10';D.appendChild(F);F.unselectable='on';var G=F.contentWindow.document;G.open();G.write('<html><head><script type="text/javascript"> window.onload = window.onresize = function() { window.frameElement.height = document.body.scrollHeight ; } </script></head><body style="overflow: hidden">'+document.getElementById('xToolbarSpace').innerHTML+'</body></html>');G.close();G.oncontextmenu=FCKTools.CancelEvent;FCKTools.AppendStyleSheet(G,FCKConfig.SkinPath+'fck_editor.css');B=D.__FCKToolbarSet=new FCKToolbarSet(G);B._IFrame=F;if (FCK.IECleanup) FCK.IECleanup.AddItem(D,FCKToolbarSet_Target_Cleanup);};B.CurrentInstance=FCK;FCK.AttachToOnSelectionChange(B.RefreshItemsState);return B;};function FCK_OnBlur(A){var B=A.ToolbarSet;if (B.CurrentInstance==A) B.Disable();};function FCK_OnFocus(A){var B=A.ToolbarSet;var C=A||FCK;B.CurrentInstance.FocusManager.RemoveWindow(B._IFrame.contentWindow);B.CurrentInstance=C;C.FocusManager.AddWindow(B._IFrame.contentWindow,true);B.Enable();};function FCKToolbarSet_Cleanup(){this._TargetElement=null;this._IFrame=null;};function FCKToolbarSet_Target_Cleanup(){this.__FCKToolbarSet=null;};var FCKToolbarSet=function(A){this._Document=A;this._TargetElement=A.getElementById('xToolbar');var B=A.getElementById('xExpandHandle');var C=A.getElementById('xCollapseHandle');B.title=FCKLang.ToolbarExpand;B.onclick=FCKToolbarSet_Expand_OnClick;C.title=FCKLang.ToolbarCollapse;C.onclick=FCKToolbarSet_Collapse_OnClick;if (!FCKConfig.ToolbarCanCollapse||FCKConfig.ToolbarStartExpanded) this.Expand();else this.Collapse();C.style.display=FCKConfig.ToolbarCanCollapse?'':'none';if (FCKConfig.ToolbarCanCollapse) C.style.display='';else A.getElementById('xTBLeftBorder').style.display='';this.Toolbars=[];this.IsLoaded=false;if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKToolbarSet_Cleanup);};function FCKToolbarSet_Expand_OnClick(){FCK.ToolbarSet.Expand();};function FCKToolbarSet_Collapse_OnClick(){FCK.ToolbarSet.Collapse();};FCKToolbarSet.prototype.Expand=function(){this._ChangeVisibility(false);};FCKToolbarSet.prototype.Collapse=function(){this._ChangeVisibility(true);};FCKToolbarSet.prototype._ChangeVisibility=function(A){this._Document.getElementById('xCollapsed').style.display=A?'':'none';this._Document.getElementById('xExpanded').style.display=A?'none':'';if (FCKBrowserInfo.IsGecko){FCKTools.RunFunction(window.onresize);}};FCKToolbarSet.prototype.Load=function(A){this.Name=A;this.Items=[];this.ItemsWysiwygOnly=[];this.ItemsContextSensitive=[];this._TargetElement.innerHTML='';var B=FCKConfig.ToolbarSets[A];if (!B){alert(FCKLang.UnknownToolbarSet.replace(/%1/g,A));return;};this.Toolbars=[];for (var x=0;x<B.length;x++){var C=B[x];if (!C) continue;var D;if (typeof(C)=='string'){if (C=='/') D=new FCKToolbarBreak();}else{D=new FCKToolbar();for (var j=0;j<C.length;j++){var E=C[j];if (E=='-') D.AddSeparator();else{var F=FCKToolbarItems.GetItem(E);if (F){D.AddItem(F);this.Items.push(F);if (!F.SourceView) this.ItemsWysiwygOnly.push(F);if (F.ContextSensitive) this.ItemsContextSensitive.push(F);}}}};D.Create(this._TargetElement);this.Toolbars[this.Toolbars.length]=D;};FCKTools.DisableSelection(this._Document.getElementById('xCollapseHandle').parentNode);if (FCK.Status!=2) FCK.Events.AttachEvent('OnStatusChange',this.RefreshModeState);else this.RefreshModeState();this.IsLoaded=true;this.IsEnabled=true;FCKTools.RunFunction(this.OnLoad);};FCKToolbarSet.prototype.Enable=function(){if (this.IsEnabled) return;this.IsEnabled=true;var A=this.Items;for (var i=0;i<A.length;i++) A[i].RefreshState();};FCKToolbarSet.prototype.Disable=function(){if (!this.IsEnabled) return;this.IsEnabled=false;var A=this.Items;for (var i=0;i<A.length;i++) A[i].Disable();};FCKToolbarSet.prototype.RefreshModeState=function(A){if (FCK.Status!=2) return;var B=A?A.ToolbarSet:this;var C=B.ItemsWysiwygOnly;if (FCK.EditMode==0){for (var i=0;i<C.length;i++) C[i].Enable();B.RefreshItemsState(A);}else{B.RefreshItemsState(A);for (var j=0;j<C.length;j++) C[j].Disable();}};FCKToolbarSet.prototype.RefreshItemsState=function(A){var B=(A?A.ToolbarSet:this).ItemsContextSensitive;for (var i=0;i<B.length;i++) B[i].RefreshState();};
+var FCKDialog={};FCKDialog.OpenDialog=function(A,B,C,D,E,F,G,H){var I={};I.Title=B;I.Page=C;I.Editor=window;I.CustomValue=F;var J=FCKConfig.BasePath+'fckdialog.html';this.Show(I,A,J,D,E,G,H);};
+FCKDialog.Show=function(A,B,C,D,E,F,G){var H=(FCKConfig.ScreenHeight-E)/2;var I=(FCKConfig.ScreenWidth-D)/2;var J="location=no,menubar=no,toolbar=no,dependent=yes,dialog=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable="+(G?'yes':'no')+",width="+D+",height="+E+",top="+H+",left="+I;if (!F) F=window;FCKFocusManager.Lock();var K=F.open('','FCKeditorDialog_'+B,J,true);if (!K){alert(FCKLang.DialogBlocked);FCKFocusManager.Unlock();return;};K.moveTo(I,H);K.resizeTo(D,E);K.focus();K.location.href=C;K.dialogArguments=A;F.FCKLastDialogInfo=A;this.Window=K;try{window.top.parent.addEventListener('mousedown',this.CheckFocus,true);window.top.parent.addEventListener('mouseup',this.CheckFocus,true);window.top.parent.addEventListener('click',this.CheckFocus,true);window.top.parent.addEventListener('focus',this.CheckFocus,true);}catch (e){}};FCKDialog.CheckFocus=function(){if (typeof(FCKDialog)!="object") return false;if (FCKDialog.Window&&!FCKDialog.Window.closed) FCKDialog.Window.focus();else{try{window.top.parent.removeEventListener('onmousedown',FCKDialog.CheckFocus,true);window.top.parent.removeEventListener('mouseup',FCKDialog.CheckFocus,true);window.top.parent.removeEventListener('click',FCKDialog.CheckFocus,true);window.top.parent.removeEventListener('onfocus',FCKDialog.CheckFocus,true);}catch (e){}};return false;};
+var FCKMenuItem=function(A,B,C,D,E){this.Name=B;this.Label=C||B;this.IsDisabled=E;this.Icon=new FCKIcon(D);this.SubMenu=new FCKMenuBlockPanel();this.SubMenu.Parent=A;this.SubMenu.OnClick=FCKTools.CreateEventListener(FCKMenuItem_SubMenu_OnClick,this);if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKMenuItem_Cleanup);};FCKMenuItem.prototype.AddItem=function(A,B,C,D){this.HasSubMenu=true;return this.SubMenu.AddItem(A,B,C,D);};FCKMenuItem.prototype.AddSeparator=function(){this.SubMenu.AddSeparator();};FCKMenuItem.prototype.Create=function(A){var B=this.HasSubMenu;var C=FCKTools.GetElementDocument(A);var r=this.MainElement=A.insertRow(-1);r.className=this.IsDisabled?'MN_Item_Disabled':'MN_Item';if (!this.IsDisabled){FCKTools.AddEventListenerEx(r,'mouseover',FCKMenuItem_OnMouseOver,[this]);FCKTools.AddEventListenerEx(r,'click',FCKMenuItem_OnClick,[this]);if (!B) FCKTools.AddEventListenerEx(r,'mouseout',FCKMenuItem_OnMouseOut,[this]);};var D=r.insertCell(-1);D.className='MN_Icon';D.appendChild(this.Icon.CreateIconElement(C));D=r.insertCell(-1);D.className='MN_Label';D.noWrap=true;D.appendChild(C.createTextNode(this.Label));D=r.insertCell(-1);if (B){D.className='MN_Arrow';var E=D.appendChild(C.createElement('IMG'));E.src=FCK_IMAGES_PATH+'arrow_'+FCKLang.Dir+'.gif';E.width=4;E.height=7;this.SubMenu.Create();this.SubMenu.Panel.OnHide=FCKTools.CreateEventListener(FCKMenuItem_SubMenu_OnHide,this);}};FCKMenuItem.prototype.Activate=function(){this.MainElement.className='MN_Item_Over';if (this.HasSubMenu){this.SubMenu.Show(this.MainElement.offsetWidth+2,-2,this.MainElement);};FCKTools.RunFunction(this.OnActivate,this);};FCKMenuItem.prototype.Deactivate=function(){this.MainElement.className='MN_Item';if (this.HasSubMenu) this.SubMenu.Hide();};function FCKMenuItem_SubMenu_OnClick(A,B){FCKTools.RunFunction(B.OnClick,B,[A]);};function FCKMenuItem_SubMenu_OnHide(A){A.Deactivate();};function FCKMenuItem_OnClick(A,B){if (B.HasSubMenu) B.Activate();else{B.Deactivate();FCKTools.RunFunction(B.OnClick,B,[B]);}};function FCKMenuItem_OnMouseOver(A,B){B.Activate();};function FCKMenuItem_OnMouseOut(A,B){B.Deactivate();};function FCKMenuItem_Cleanup(){this.MainElement=null;}
+var FCKMenuBlock=function(){this._Items=[];};FCKMenuBlock.prototype.Count=function(){return this._Items.length;};FCKMenuBlock.prototype.AddItem=function(A,B,C,D){var E=new FCKMenuItem(this,A,B,C,D);E.OnClick=FCKTools.CreateEventListener(FCKMenuBlock_Item_OnClick,this);E.OnActivate=FCKTools.CreateEventListener(FCKMenuBlock_Item_OnActivate,this);this._Items.push(E);return E;};FCKMenuBlock.prototype.AddSeparator=function(){this._Items.push(new FCKMenuSeparator());};FCKMenuBlock.prototype.RemoveAllItems=function(){this._Items=[];var A=this._ItemsTable;if (A){while (A.rows.length>0) A.deleteRow(0);}};FCKMenuBlock.prototype.Create=function(A){if (!this._ItemsTable){if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKMenuBlock_Cleanup);this._Window=FCKTools.GetElementWindow(A);var B=FCKTools.GetElementDocument(A);var C=A.appendChild(B.createElement('table'));C.cellPadding=0;C.cellSpacing=0;FCKTools.DisableSelection(C);var D=C.insertRow(-1).insertCell(-1);D.className='MN_Menu';var E=this._ItemsTable=D.appendChild(B.createElement('table'));E.cellPadding=0;E.cellSpacing=0;};for (var i=0;i<this._Items.length;i++) this._Items[i].Create(this._ItemsTable);};function FCKMenuBlock_Item_OnClick(A,B){FCKTools.RunFunction(B.OnClick,B,[A]);};function FCKMenuBlock_Item_OnActivate(A){var B=A._ActiveItem;if (B&&B!=this){if (!FCKBrowserInfo.IsIE&&B.HasSubMenu&&!this.HasSubMenu) A._Window.focus();B.Deactivate();};A._ActiveItem=this;};function FCKMenuBlock_Cleanup(){this._Window=null;this._ItemsTable=null;};var FCKMenuSeparator=function(){};FCKMenuSeparator.prototype.Create=function(A){var B=FCKTools.GetElementDocument(A);var r=A.insertRow(-1);var C=r.insertCell(-1);C.className='MN_Separator MN_Icon';C=r.insertCell(-1);C.className='MN_Separator';C.appendChild(B.createElement('DIV')).className='MN_Separator_Line';C=r.insertCell(-1);C.className='MN_Separator';C.appendChild(B.createElement('DIV')).className='MN_Separator_Line';}
+var FCKMenuBlockPanel=function(){FCKMenuBlock.call(this);};FCKMenuBlockPanel.prototype=new FCKMenuBlock();FCKMenuBlockPanel.prototype.Create=function(){var A=this.Panel=(this.Parent&&this.Parent.Panel?this.Parent.Panel.CreateChildPanel():new FCKPanel());A.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');FCKMenuBlock.prototype.Create.call(this,A.MainNode);};FCKMenuBlockPanel.prototype.Show=function(x,y,A){if (!this.Panel.CheckIsOpened()) this.Panel.Show(x,y,A);};FCKMenuBlockPanel.prototype.Hide=function(){if (this.Panel.CheckIsOpened()) this.Panel.Hide();}
+var FCKContextMenu=function(A,B){this.CtrlDisable=false;var C=this._Panel=new FCKPanel(A);C.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');C.IsContextMenu=true;if (FCKBrowserInfo.IsGecko) C.Document.addEventListener('draggesture',function(e) {e.preventDefault();return false;},true);var D=this._MenuBlock=new FCKMenuBlock();D.Panel=C;D.OnClick=FCKTools.CreateEventListener(FCKContextMenu_MenuBlock_OnClick,this);this._Redraw=true;};FCKContextMenu.prototype.SetMouseClickWindow=function(A){if (!FCKBrowserInfo.IsIE){this._Document=A.document;this._Document.addEventListener('contextmenu',FCKContextMenu_Document_OnContextMenu,false);}};FCKContextMenu.prototype.AddItem=function(A,B,C,D){var E=this._MenuBlock.AddItem(A,B,C,D);this._Redraw=true;return E;};FCKContextMenu.prototype.AddSeparator=function(){this._MenuBlock.AddSeparator();this._Redraw=true;};FCKContextMenu.prototype.RemoveAllItems=function(){this._MenuBlock.RemoveAllItems();this._Redraw=true;};FCKContextMenu.prototype.AttachToElement=function(A){if (FCKBrowserInfo.IsIE) FCKTools.AddEventListenerEx(A,'contextmenu',FCKContextMenu_AttachedElement_OnContextMenu,this);else A._FCKContextMenu=this;};function FCKContextMenu_Document_OnContextMenu(e){var A=e.target;while (A){if (A._FCKContextMenu){if (A._FCKContextMenu.CtrlDisable&&(e.ctrlKey||e.metaKey)) return true;FCKTools.CancelEvent(e);FCKContextMenu_AttachedElement_OnContextMenu(e,A._FCKContextMenu,A);};A=A.parentNode;}};function FCKContextMenu_AttachedElement_OnContextMenu(A,B,C){if (B.CtrlDisable&&(A.ctrlKey||A.metaKey)) return true;var D=C||this;if (B.OnBeforeOpen) B.OnBeforeOpen.call(B,D);if (B._MenuBlock.Count()==0) return false;if (B._Redraw){B._MenuBlock.Create(B._Panel.MainNode);B._Redraw=false;};FCKTools.DisableSelection(B._Panel.Document.body);B._Panel.Show(A.pageX||A.screenX,A.pageY||A.screenY,A.currentTarget||null);return false;};function FCKContextMenu_MenuBlock_OnClick(A,B){B._Panel.Hide();FCKTools.RunFunction(B.OnItemClick,B,A);}
+FCK.ContextMenu={};FCK.ContextMenu.Listeners=[];FCK.ContextMenu.RegisterListener=function(A){if (A) this.Listeners.push(A);};function FCK_ContextMenu_Init(){var A=FCK.ContextMenu._InnerContextMenu=new FCKContextMenu(FCKBrowserInfo.IsIE?window:window.parent,FCKLang.Dir);A.CtrlDisable=FCKConfig.BrowserContextMenuOnCtrl;A.OnBeforeOpen=FCK_ContextMenu_OnBeforeOpen;A.OnItemClick=FCK_ContextMenu_OnItemClick;var B=FCK.ContextMenu;for (var i=0;i<FCKConfig.ContextMenu.length;i++) B.RegisterListener(FCK_ContextMenu_GetListener(FCKConfig.ContextMenu[i]));};function FCK_ContextMenu_GetListener(A){switch (A){case 'Generic':return {AddItems:function(menu,tag,tagName){menu.AddItem('Cut',FCKLang.Cut,7,FCKCommands.GetCommand('Cut').GetState()==-1);menu.AddItem('Copy',FCKLang.Copy,8,FCKCommands.GetCommand('Copy').GetState()==-1);menu.AddItem('Paste',FCKLang.Paste,9,FCKCommands.GetCommand('Paste').GetState()==-1);}};case 'Table':return {AddItems:function(menu,tag,tagName){var B=(tagName=='TABLE');var C=(!B&&FCKSelection.HasAncestorNode('TABLE'));if (C){menu.AddSeparator();var D=menu.AddItem('Cell',FCKLang.CellCM);D.AddItem('TableInsertCell',FCKLang.InsertCell,58);D.AddItem('TableDeleteCells',FCKLang.DeleteCells,59);D.AddItem('TableMergeCells',FCKLang.MergeCells,60);D.AddItem('TableSplitCell',FCKLang.SplitCell,61);D.AddSeparator();D.AddItem('TableCellProp',FCKLang.CellProperties,57);menu.AddSeparator();D=menu.AddItem('Row',FCKLang.RowCM);D.AddItem('TableInsertRow',FCKLang.InsertRow,62);D.AddItem('TableDeleteRows',FCKLang.DeleteRows,63);menu.AddSeparator();D=menu.AddItem('Column',FCKLang.ColumnCM);D.AddItem('TableInsertColumn',FCKLang.InsertColumn,64);D.AddItem('TableDeleteColumns',FCKLang.DeleteColumns,65);};if (B||C){menu.AddSeparator();menu.AddItem('TableDelete',FCKLang.TableDelete);menu.AddItem('TableProp',FCKLang.TableProperties,39);}}};case 'Link':return {AddItems:function(menu,tag,tagName){var E=(tagName=='A'||FCKSelection.HasAncestorNode('A'));if (E||FCK.GetNamedCommandState('Unlink')!=-1){var F=FCKSelection.MoveToAncestorNode('A');var G=(F&&F.name.length>0&&F.href.length==0);if (G) return;menu.AddSeparator();if (E) menu.AddItem('Link',FCKLang.EditLink,34);menu.AddItem('Unlink',FCKLang.RemoveLink,35);}}};case 'Image':return {AddItems:function(menu,tag,tagName){if (tagName=='IMG'&&!tag.getAttribute('_fckfakelement')){menu.AddSeparator();menu.AddItem('Image',FCKLang.ImageProperties,37);}}};case 'Anchor':return {AddItems:function(menu,tag,tagName){var F=FCKSelection.MoveToAncestorNode('A');var G=(F&&F.name.length>0);if (G||(tagName=='IMG'&&tag.getAttribute('_fckanchor'))){menu.AddSeparator();menu.AddItem('Anchor',FCKLang.AnchorProp,36);}}};case 'Flash':return {AddItems:function(menu,tag,tagName){if (tagName=='IMG'&&tag.getAttribute('_fckflash')){menu.AddSeparator();menu.AddItem('Flash',FCKLang.FlashProperties,38);}}};case 'Form':return {AddItems:function(menu,tag,tagName){if (FCKSelection.HasAncestorNode('FORM')){menu.AddSeparator();menu.AddItem('Form',FCKLang.FormProp,48);}}};case 'Checkbox':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&tag.type=='checkbox'){menu.AddSeparator();menu.AddItem('Checkbox',FCKLang.CheckboxProp,49);}}};case 'Radio':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&tag.type=='radio'){menu.AddSeparator();menu.AddItem('Radio',FCKLang.RadioButtonProp,50);}}};case 'TextField':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&(tag.type=='text'||tag.type=='password')){menu.AddSeparator();menu.AddItem('TextField',FCKLang.TextFieldProp,51);}}};case 'HiddenField':return {AddItems:function(menu,tag,tagName){if (tagName=='IMG'&&tag.getAttribute('_fckinputhidden')){menu.AddSeparator();menu.AddItem('HiddenField',FCKLang.HiddenFieldProp,56);}}};case 'ImageButton':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&tag.type=='image'){menu.AddSeparator();menu.AddItem('ImageButton',FCKLang.ImageButtonProp,55);}}};case 'Button':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&(tag.type=='button'||tag.type=='submit'||tag.type=='reset')){menu.AddSeparator();menu.AddItem('Button',FCKLang.ButtonProp,54);}}};case 'Select':return {AddItems:function(menu,tag,tagName){if (tagName=='SELECT'){menu.AddSeparator();menu.AddItem('Select',FCKLang.SelectionFieldProp,53);}}};case 'Textarea':return {AddItems:function(menu,tag,tagName){if (tagName=='TEXTAREA'){menu.AddSeparator();menu.AddItem('Textarea',FCKLang.TextareaProp,52);}}};case 'BulletedList':return {AddItems:function(menu,tag,tagName){if (FCKSelection.HasAncestorNode('UL')){menu.AddSeparator();menu.AddItem('BulletedList',FCKLang.BulletedListProp,27);}}};case 'NumberedList':return {AddItems:function(menu,tag,tagName){if (FCKSelection.HasAncestorNode('OL')){menu.AddSeparator();menu.AddItem('NumberedList',FCKLang.NumberedListProp,26);}}};};return null;};function FCK_ContextMenu_OnBeforeOpen(){FCK.Events.FireEvent('OnSelectionChange');var A,sTagName;if ((A=FCKSelection.GetSelectedElement())) sTagName=A.tagName;var B=FCK.ContextMenu._InnerContextMenu;B.RemoveAllItems();var C=FCK.ContextMenu.Listeners;for (var i=0;i<C.length;i++) C[i].AddItems(B,A,sTagName);};function FCK_ContextMenu_OnItemClick(A){FCK.Focus();FCKCommands.GetCommand(A.Name).Execute();};
+var FCKPlugin=function(A,B,C){this.Name=A;this.BasePath=C?C:FCKConfig.PluginsPath;this.Path=this.BasePath+A+'/';if (!B||B.length==0) this.AvailableLangs=[];else this.AvailableLangs=B.split(',');};FCKPlugin.prototype.Load=function(){if (this.AvailableLangs.length>0){var A;if (this.AvailableLangs.IndexOf(FCKLanguageManager.ActiveLanguage.Code)>=0) A=FCKLanguageManager.ActiveLanguage.Code;else A=this.AvailableLangs[0];LoadScript(this.Path+'lang/'+A+'.js');};LoadScript(this.Path+'fckplugin.js');}
+var FCKPlugins=FCK.Plugins={};FCKPlugins.ItemsCount=0;FCKPlugins.Items={};FCKPlugins.Load=function(){var A=FCKPlugins.Items;for (var i=0;i<FCKConfig.Plugins.Items.length;i++){var B=FCKConfig.Plugins.Items[i];var C=A[B[0]]=new FCKPlugin(B[0],B[1],B[2]);FCKPlugins.ItemsCount++;};for (var s in A) A[s].Load();FCKPlugins.Load=null;}
diff --git a/httemplate/elements/fckeditor/editor/js/fckeditorcode_ie.js b/httemplate/elements/fckeditor/editor/js/fckeditorcode_ie.js
new file mode 100644
index 0000000..0d43957
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/js/fckeditorcode_ie.js
@@ -0,0 +1,99 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This file has been compressed for better performance. The original source
+ * can be found at "editor/_source".
+ */
+
+var FCK_STATUS_NOTLOADED=window.parent.FCK_STATUS_NOTLOADED=0;var FCK_STATUS_ACTIVE=window.parent.FCK_STATUS_ACTIVE=1;var FCK_STATUS_COMPLETE=window.parent.FCK_STATUS_COMPLETE=2;var FCK_TRISTATE_OFF=window.parent.FCK_TRISTATE_OFF=0;var FCK_TRISTATE_ON=window.parent.FCK_TRISTATE_ON=1;var FCK_TRISTATE_DISABLED=window.parent.FCK_TRISTATE_DISABLED=-1;var FCK_UNKNOWN=window.parent.FCK_UNKNOWN=-9;var FCK_TOOLBARITEM_ONLYICON=window.parent.FCK_TOOLBARITEM_ONLYICON=0;var FCK_TOOLBARITEM_ONLYTEXT=window.parent.FCK_TOOLBARITEM_ONLYTEXT=1;var FCK_TOOLBARITEM_ICONTEXT=window.parent.FCK_TOOLBARITEM_ICONTEXT=2;var FCK_EDITMODE_WYSIWYG=window.parent.FCK_EDITMODE_WYSIWYG=0;var FCK_EDITMODE_SOURCE=window.parent.FCK_EDITMODE_SOURCE=1;var FCK_IMAGES_PATH='images/';var FCK_SPACER_PATH='images/spacer.gif';var CTRL=1000;var SHIFT=2000;var ALT=4000;
+String.prototype.Contains=function(A){return (this.indexOf(A)>-1);};String.prototype.Equals=function(){var A=arguments;if (A.length==1&&A[0].pop) A=A[0];for (var i=0;i<A.length;i++){if (this==A[i]) return true;};return false;};String.prototype.IEquals=function(){var A=this.toUpperCase();var B=arguments;if (B.length==1&&B[0].pop) B=B[0];for (var i=0;i<B.length;i++){if (A==B[i].toUpperCase()) return true;};return false;};String.prototype.ReplaceAll=function(A,B){var C=this;for (var i=0;i<A.length;i++){C=C.replace(A[i],B[i]);};return C;};Array.prototype.AddItem=function(A){var i=this.length;this[i]=A;return i;};Array.prototype.IndexOf=function(A){for (var i=0;i<this.length;i++){if (this[i]==A) return i;};return-1;};String.prototype.StartsWith=function(A){return (this.substr(0,A.length)==A);};String.prototype.EndsWith=function(A,B){var C=this.length;var D=A.length;if (D>C) return false;if (B){var E=new RegExp(A+'$','i');return E.test(this);}else return (D==0||this.substr(C-D,D)==A);};String.prototype.Remove=function(A,B){var s='';if (A>0) s=this.substring(0,A);if (A+B<this.length) s+=this.substring(A+B,this.length);return s;};String.prototype.Trim=function(){return this.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g,'');};String.prototype.LTrim=function(){return this.replace(/^[ \t\n\r]*/g,'');};String.prototype.RTrim=function(){return this.replace(/[ \t\n\r]*$/g,'');};String.prototype.ReplaceNewLineChars=function(A){return this.replace(/\n/g,A);}
+var FCKIECleanup=function(A){if (A._FCKCleanupObj) this.Items=A._FCKCleanupObj.Items;else{this.Items=[];A._FCKCleanupObj=this;FCKTools.AddEventListenerEx(A,'unload',FCKIECleanup_Cleanup);}};FCKIECleanup.prototype.AddItem=function(A,B){this.Items.push([A,B]);};function FCKIECleanup_Cleanup(){if (!this._FCKCleanupObj) return;var A=this._FCKCleanupObj.Items;while (A.length>0){var B=A.pop();if (B) B[1].call(B[0]);};this._FCKCleanupObj=null;if (CollectGarbage) CollectGarbage();}
+var s=navigator.userAgent.toLowerCase();var FCKBrowserInfo={IsIE:s.Contains('msie'),IsIE7:s.Contains('msie 7'),IsGecko:s.Contains('gecko/'),IsSafari:s.Contains('safari'),IsOpera:s.Contains('opera'),IsMac:s.Contains('macintosh')};(function(A){A.IsGeckoLike=(A.IsGecko||A.IsSafari||A.IsOpera);if (A.IsGecko){var B=s.match(/gecko\/(\d+)/)[1];A.IsGecko10=((B<20051111)||(/rv:1\.7/.test(s)));}else A.IsGecko10=false;})(FCKBrowserInfo);
+var FCKURLParams={};(function(){var A=document.location.search.substr(1).split('&');for (var i=0;i<A.length;i++){var B=A[i].split('=');var C=decodeURIComponent(B[0]);var D=decodeURIComponent(B[1]);FCKURLParams[C]=D;}})();
+var FCKEvents=function(A){this.Owner=A;this._RegisteredEvents={};};FCKEvents.prototype.AttachEvent=function(A,B){var C;if (!(C=this._RegisteredEvents[A])) this._RegisteredEvents[A]=[B];else C.push(B);};FCKEvents.prototype.FireEvent=function(A,B){var C=true;var D=this._RegisteredEvents[A];if (D){for (var i=0;i<D.length;i++) C=(D[i](this.Owner,B)&&C);};return C;};
+var FCK={Name:FCKURLParams['InstanceName'],Status:0,EditMode:0,Toolbar:null,HasFocus:false,AttachToOnSelectionChange:function(A){this.Events.AttachEvent('OnSelectionChange',A);},GetLinkedFieldValue:function(){return this.LinkedField.value;},GetParentForm:function(){return this.LinkedField.form;},StartupValue:'',IsDirty:function(){if (this.EditMode==1) return (this.StartupValue!=this.EditingArea.Textarea.value);else return (this.StartupValue!=this.EditorDocument.body.innerHTML);},ResetIsDirty:function(){if (this.EditMode==1) this.StartupValue=this.EditingArea.Textarea.value;else if (this.EditorDocument.body) this.StartupValue=this.EditorDocument.body.innerHTML;},StartEditor:function(){this.TempBaseTag=FCKConfig.BaseHref.length>0?'<base href="'+FCKConfig.BaseHref+'" _fcktemp="true"></base>':'';var A=FCK.KeystrokeHandler=new FCKKeystrokeHandler();A.OnKeystroke=_FCK_KeystrokeHandler_OnKeystroke;A.SetKeystrokes(FCKConfig.Keystrokes);if (FCKBrowserInfo.IsIE7){if ((CTRL+86/*V*/) in A.Keystrokes) A.SetKeystrokes([CTRL+86,true]);if ((SHIFT+45/*INS*/) in A.Keystrokes) A.SetKeystrokes([SHIFT+45,true]);};this.EditingArea=new FCKEditingArea(document.getElementById('xEditingArea'));this.EditingArea.FFSpellChecker=FCKConfig.FirefoxSpellChecker;FCKListsLib.Setup();this.SetHTML(this.GetLinkedFieldValue(),true);},Focus:function(){FCK.EditingArea.Focus();},SetStatus:function(A){this.Status=A;if (A==1){FCKFocusManager.AddWindow(window,true);if (FCKBrowserInfo.IsIE) FCKFocusManager.AddWindow(window.frameElement,true);if (FCKConfig.StartupFocus) FCK.Focus();};this.Events.FireEvent('OnStatusChange',A);},FixBody:function(){var A=FCKConfig.EnterMode;if (A!='p'&&A!='div') return;var B=this.EditorDocument;if (!B) return;var C=B.body;if (!C) return;FCKDomTools.TrimNode(C);var D=C.firstChild;var E;while (D){var F=false;switch (D.nodeType){case 1:if (!FCKListsLib.BlockElements[D.nodeName.toLowerCase()]) F=true;break;case 3:if (E||D.nodeValue.Trim().length>0) F=true;};if (F){var G=D.parentNode;if (!E) E=G.insertBefore(B.createElement(A),D);E.appendChild(G.removeChild(D));D=E.nextSibling;}else{if (E){FCKDomTools.TrimNode(E);E=null;};D=D.nextSibling;}};if (E) FCKDomTools.TrimNode(E);},GetXHTML:function(A){if (FCK.EditMode==1) return FCK.EditingArea.Textarea.value;this.FixBody();var B;var C=FCK.EditorDocument;if (!C) return null;if (FCKConfig.FullPage){B=FCKXHtml.GetXHTML(C.getElementsByTagName('html')[0],true,A);if (FCK.DocTypeDeclaration&&FCK.DocTypeDeclaration.length>0) B=FCK.DocTypeDeclaration+'\n'+B;if (FCK.XmlDeclaration&&FCK.XmlDeclaration.length>0) B=FCK.XmlDeclaration+'\n'+B;}else{B=FCKXHtml.GetXHTML(C.body,false,A);if (FCKConfig.IgnoreEmptyParagraphValue&&FCKRegexLib.EmptyOutParagraph.test(B)) B='';};B=FCK.ProtectEventsRestore(B);if (FCKBrowserInfo.IsIE) B=B.replace(FCKRegexLib.ToReplace,'$1');return FCKConfig.ProtectedSource.Revert(B);},UpdateLinkedField:function(){FCK.LinkedField.value=FCK.GetXHTML(FCKConfig.FormatOutput);FCK.Events.FireEvent('OnAfterLinkedFieldUpdate');},RegisteredDoubleClickHandlers:{},OnDoubleClick:function(A){var B=FCK.RegisteredDoubleClickHandlers[A.tagName];if (B) B(A);},RegisterDoubleClickHandler:function(A,B){FCK.RegisteredDoubleClickHandlers[B.toUpperCase()]=A;},OnAfterSetHTML:function(){FCKDocumentProcessor.Process(FCK.EditorDocument);FCKUndo.SaveUndoStep();FCK.Events.FireEvent('OnSelectionChange');FCK.Events.FireEvent('OnAfterSetHTML');},ProtectUrls:function(A){A=A.replace(FCKRegexLib.ProtectUrlsA,'$& _fcksavedurl=$1');A=A.replace(FCKRegexLib.ProtectUrlsImg,'$& _fcksavedurl=$1');return A;},ProtectEvents:function(A){return A.replace(FCKRegexLib.TagsWithEvent,_FCK_ProtectEvents_ReplaceTags);},ProtectEventsRestore:function(A){return A.replace(FCKRegexLib.ProtectedEvents,_FCK_ProtectEvents_RestoreEvents);},ProtectTags:function(A){var B=FCKConfig.ProtectedTags;if (FCKBrowserInfo.IsIE) B+=B.length>0?'|ABBR|XML':'ABBR|XML';var C;if (B.length>0){C=new RegExp('<('+B+')(?!\w|:)','gi');A=A.replace(C,'<FCK:$1');C=new RegExp('<\/('+B+')>','gi');A=A.replace(C,'<\/FCK:$1>');};B='META';if (FCKBrowserInfo.IsIE) B+='|HR';C=new RegExp('<(('+B+')(?=\\s|>|/)[\\s\\S]*?)/?>','gi');A=A.replace(C,'<FCK:$1 />');return A;},SetHTML:function(A,B){this.EditingArea.Mode=FCK.EditMode;if (FCK.EditMode==0){A=FCKConfig.ProtectedSource.Protect(A);A=A.replace(FCKRegexLib.InvalidSelfCloseTags,'$1></$2>');A=FCK.ProtectEvents(A);A=FCK.ProtectUrls(A);A=FCK.ProtectTags(A);if (FCKBrowserInfo.IsGecko){A=A.replace(FCKRegexLib.StrongOpener,'<b$1');A=A.replace(FCKRegexLib.StrongCloser,'<\/b>');A=A.replace(FCKRegexLib.EmOpener,'<i$1');A=A.replace(FCKRegexLib.EmCloser,'<\/i>');};this._ForceResetIsDirty=(B===true);var C='';if (FCKConfig.FullPage){if (!FCKRegexLib.HeadOpener.test(A)){if (!FCKRegexLib.HtmlOpener.test(A)) A='<html dir="'+FCKConfig.ContentLangDirection+'">'+A+'</html>';A=A.replace(FCKRegexLib.HtmlOpener,'$&<head></head>');};FCK.DocTypeDeclaration=A.match(FCKRegexLib.DocTypeTag);if (FCKBrowserInfo.IsIE) C=FCK._GetBehaviorsStyle();else if (FCKConfig.ShowBorders) C='<link href="'+FCKConfig.FullBasePath+'css/fck_showtableborders_gecko.css" rel="stylesheet" type="text/css" _fcktemp="true" />';C+='<link href="'+FCKConfig.FullBasePath+'css/fck_internal.css" rel="stylesheet" type="text/css" _fcktemp="true" />';C=A.replace(FCKRegexLib.HeadCloser,C+'$&');if (FCK.TempBaseTag.length>0&&!FCKRegexLib.HasBaseTag.test(A)) C=C.replace(FCKRegexLib.HeadOpener,'$&'+FCK.TempBaseTag);}else{C=FCKConfig.DocType+'<html dir="'+FCKConfig.ContentLangDirection+'"';if (FCKBrowserInfo.IsIE&&!FCKRegexLib.Html4DocType.test(FCKConfig.DocType)) C+=' style="overflow-y: scroll"';C+='><head><title></title>'+_FCK_GetEditorAreaStyleTags()+'<link href="'+FCKConfig.FullBasePath+'css/fck_internal.css" rel="stylesheet" type="text/css" _fcktemp="true" />';if (FCKBrowserInfo.IsIE) C+=FCK._GetBehaviorsStyle();else if (FCKConfig.ShowBorders) C+='<link href="'+FCKConfig.FullBasePath+'css/fck_showtableborders_gecko.css" rel="stylesheet" type="text/css" _fcktemp="true" />';C+=FCK.TempBaseTag;var D='<body';if (FCKConfig.BodyId&&FCKConfig.BodyId.length>0) D+=' id="'+FCKConfig.BodyId+'"';if (FCKConfig.BodyClass&&FCKConfig.BodyClass.length>0) D+=' class="'+FCKConfig.BodyClass+'"';C+='</head>'+D+'>';if (FCKBrowserInfo.IsGecko&&(A.length==0||FCKRegexLib.EmptyParagraph.test(A))) C+=GECKO_BOGUS;else C+=A;C+='</body></html>';};this.EditingArea.OnLoad=_FCK_EditingArea_OnLoad;this.EditingArea.Start(C);}else{FCK.EditorWindow=null;FCK.EditorDocument=null;this.EditingArea.OnLoad=null;this.EditingArea.Start(A);this.EditingArea.Textarea._FCKShowContextMenu=true;FCK.EnterKeyHandler=null;if (B) this.ResetIsDirty();FCK.KeystrokeHandler.AttachToElement(this.EditingArea.Textarea);this.EditingArea.Textarea.focus();FCK.Events.FireEvent('OnAfterSetHTML');};if (FCKBrowserInfo.IsGecko) window.onresize();},HasFocus:false,RedirectNamedCommands:{},ExecuteNamedCommand:function(A,B,C){FCKUndo.SaveUndoStep();if (!C&&FCK.RedirectNamedCommands[A]!=null) FCK.ExecuteRedirectedNamedCommand(A,B);else{FCK.Focus();FCK.EditorDocument.execCommand(A,false,B);FCK.Events.FireEvent('OnSelectionChange');};FCKUndo.SaveUndoStep();},GetNamedCommandState:function(A){try{if (!FCK.EditorDocument.queryCommandEnabled(A)) return -1;else return FCK.EditorDocument.queryCommandState(A)?1:0;}catch (e){return 0;}},GetNamedCommandValue:function(A){var B='';var C=FCK.GetNamedCommandState(A);if (C==-1) return null;try{B=this.EditorDocument.queryCommandValue(A);}catch(e) {};return B?B:'';},PasteFromWord:function(){FCKDialog.OpenDialog('FCKDialog_Paste',FCKLang.PasteFromWord,'dialog/fck_paste.html',400,330,'Word');},Preview:function(){var A=FCKConfig.ScreenWidth*0.8;var B=FCKConfig.ScreenHeight*0.7;var C=(FCKConfig.ScreenWidth-A)/2;var D=window.open('',null,'toolbar=yes,location=no,status=yes,menubar=yes,scrollbars=yes,resizable=yes,width='+A+',height='+B+',left='+C);var E;if (FCKConfig.FullPage){if (FCK.TempBaseTag.length>0) E=FCK.TempBaseTag+FCK.GetXHTML();else E=FCK.GetXHTML();}else{E=FCKConfig.DocType+'<html dir="'+FCKConfig.ContentLangDirection+'"><head>'+FCK.TempBaseTag+'<title>'+FCKLang.Preview+'</title>'+_FCK_GetEditorAreaStyleTags()+'</head><body>'+FCK.GetXHTML()+'</body></html>';};D.document.write(E);D.document.close();},SwitchEditMode:function(A){var B=(FCK.EditMode==0);var C=FCK.IsDirty();var D;if (B){if (!A&&FCKBrowserInfo.IsIE) FCKUndo.SaveUndoStep();D=FCK.GetXHTML(FCKConfig.FormatSource);if (D==null) return false;}else D=this.EditingArea.Textarea.value;FCK.EditMode=B?1:0;FCK.SetHTML(D,!C);FCK.Focus();FCKTools.RunFunction(FCK.ToolbarSet.RefreshModeState,FCK.ToolbarSet);return true;},CreateElement:function(A){var e=FCK.EditorDocument.createElement(A);return FCK.InsertElementAndGetIt(e);},InsertElementAndGetIt:function(e){e.setAttribute('FCKTempLabel','true');this.InsertElement(e);var A=FCK.EditorDocument.getElementsByTagName(e.tagName);for (var i=0;i<A.length;i++){if (A[i].getAttribute('FCKTempLabel')){A[i].removeAttribute('FCKTempLabel');return A[i];}};return null;}};FCK.Events=new FCKEvents(FCK);FCK.GetHTML=FCK.GetXHTML;function _FCK_ProtectEvents_ReplaceTags(A){return A.replace(FCKRegexLib.EventAttributes,_FCK_ProtectEvents_ReplaceEvents);};function _FCK_ProtectEvents_ReplaceEvents(A,B){return ' '+B+'_fckprotectedatt="'+A.ReplaceAll([/&/g,/'/g,/"/g,/=/g,/</g,/>/g,/\r/g,/\n/g],['&apos;','&#39;','&quot;','&#61;','&lt;','&gt;','&#10;','&#13;'])+'"';};function _FCK_ProtectEvents_RestoreEvents(A,B){return B.ReplaceAll([/&#39;/g,/&quot;/g,/&#61;/g,/&lt;/g,/&gt;/g,/&#10;/g,/&#13;/g,/&apos;/g],["'",'"','=','<','>','\r','\n','&']);};function _FCK_EditingArea_OnLoad(){FCK.EditorWindow=FCK.EditingArea.Window;FCK.EditorDocument=FCK.EditingArea.Document;FCK.InitializeBehaviors();if (!FCKConfig.DisableEnterKeyHandler) FCK.EnterKeyHandler=new FCKEnterKey(FCK.EditorWindow,FCKConfig.EnterMode,FCKConfig.ShiftEnterMode);FCK.KeystrokeHandler.AttachToElement(FCK.EditorDocument);if (FCK._ForceResetIsDirty) FCK.ResetIsDirty();if (FCKBrowserInfo.IsIE&&FCK.HasFocus) FCK.EditorDocument.body.setActive();FCK.OnAfterSetHTML();if (FCK.Status!=0) return;FCK.SetStatus(1);};function _FCK_GetEditorAreaStyleTags(){var A='';var B=FCKConfig.EditorAreaCSS;for (var i=0;i<B.length;i++) A+='<link href="'+B[i]+'" rel="stylesheet" type="text/css" />';return A;};function _FCK_KeystrokeHandler_OnKeystroke(A,B){if (FCK.Status!=2) return false;if (FCK.EditMode==0){if (B=='Paste') return!FCK.Events.FireEvent('OnPaste');}else{if (B.Equals('Paste','Undo','Redo','SelectAll')) return false;};var C=FCK.Commands.GetCommand(B);return (C.Execute.apply(C,FCKTools.ArgumentsToArray(arguments,2))!==false);};(function(){var A=window.parent.document;var B=A.getElementById(FCK.Name);var i=0;while (B||i==0){if (B&&B.tagName.toLowerCase().Equals('input','textarea')){FCK.LinkedField=B;break;};B=A.getElementsByName(FCK.Name)[i++];}})();var FCKTempBin={Elements:[],AddElement:function(A){var B=this.Elements.length;this.Elements[B]=A;return B;},RemoveElement:function(A){var e=this.Elements[A];this.Elements[A]=null;return e;},Reset:function(){var i=0;while (i<this.Elements.length) this.Elements[i++]=null;this.Elements.length=0;}};var FCKFocusManager=FCK.FocusManager={IsLocked:false,AddWindow:function(A,B){var C;if (FCKBrowserInfo.IsIE) C=A.nodeType==1?A:A.frameElement?A.frameElement:A.document;else C=A.document;FCKTools.AddEventListener(C,'blur',FCKFocusManager_Win_OnBlur);FCKTools.AddEventListener(C,'focus',B?FCKFocusManager_Win_OnFocus_Area:FCKFocusManager_Win_OnFocus);},RemoveWindow:function(A){if (FCKBrowserInfo.IsIE) oTarget=A.nodeType==1?A:A.frameElement?A.frameElement:A.document;else oTarget=A.document;FCKTools.RemoveEventListener(oTarget,'blur',FCKFocusManager_Win_OnBlur);FCKTools.RemoveEventListener(oTarget,'focus',FCKFocusManager_Win_OnFocus_Area);FCKTools.RemoveEventListener(oTarget,'focus',FCKFocusManager_Win_OnFocus);},Lock:function(){this.IsLocked=true;},Unlock:function(){if (this._HasPendingBlur) FCKFocusManager._Timer=window.setTimeout(FCKFocusManager_FireOnBlur,100);this.IsLocked=false;},_ResetTimer:function(){this._HasPendingBlur=false;if (this._Timer){window.clearTimeout(this._Timer);delete this._Timer;}}};function FCKFocusManager_Win_OnBlur(){if (typeof(FCK)!='undefined'&&FCK.HasFocus){FCKFocusManager._ResetTimer();FCKFocusManager._Timer=window.setTimeout(FCKFocusManager_FireOnBlur,100);}};function FCKFocusManager_FireOnBlur(){if (FCKFocusManager.IsLocked) FCKFocusManager._HasPendingBlur=true;else{FCK.HasFocus=false;FCK.Events.FireEvent("OnBlur");}};function FCKFocusManager_Win_OnFocus_Area(){FCK.Focus();FCKFocusManager_Win_OnFocus();};function FCKFocusManager_Win_OnFocus(){FCKFocusManager._ResetTimer();if (!FCK.HasFocus&&!FCKFocusManager.IsLocked){FCK.HasFocus=true;FCK.Events.FireEvent("OnFocus");}};
+FCK.Description="FCKeditor for Internet Explorer 5.5+";FCK._GetBehaviorsStyle=function(){if (!FCK._BehaviorsStyle){var A=FCKConfig.FullBasePath;var B='';var C;C='<style type="text/css" _fcktemp="true">';if (FCKConfig.ShowBorders) B='url('+A+'css/behaviors/showtableborders.htc)';C+='INPUT,TEXTAREA,SELECT,.FCK__Anchor,.FCK__PageBreak,.FCK__InputHidden';if (FCKConfig.DisableObjectResizing){C+=',IMG';B+=' url('+A+'css/behaviors/disablehandles.htc)';};C+=' { behavior: url('+A+'css/behaviors/disablehandles.htc) ; }';if (B.length>0) C+='TABLE { behavior: '+B+' ; }';C+='</style>';FCK._BehaviorsStyle=C;};return FCK._BehaviorsStyle;};function Doc_OnMouseUp(){if (FCK.EditorWindow.event.srcElement.tagName=='HTML'){FCK.Focus();FCK.EditorWindow.event.cancelBubble=true;FCK.EditorWindow.event.returnValue=false;}};function Doc_OnPaste(){return (FCK.Status==2&&FCK.Events.FireEvent("OnPaste"));};function Doc_OnKeyDown(){if (FCK.EditorWindow){var e=FCK.EditorWindow.event;if (!(e.keyCode>=16&&e.keyCode<=18)) Doc_OnKeyDownUndo();};return true;};function Doc_OnKeyDownUndo(){if (!FCKUndo.Typing){FCKUndo.SaveUndoStep();FCKUndo.Typing=true;FCK.Events.FireEvent("OnSelectionChange");};FCKUndo.TypesCount++;if (FCKUndo.TypesCount>FCKUndo.MaxTypes){FCKUndo.TypesCount=0;FCKUndo.SaveUndoStep();}};function Doc_OnDblClick(){FCK.OnDoubleClick(FCK.EditorWindow.event.srcElement);FCK.EditorWindow.event.cancelBubble=true;};function Doc_OnSelectionChange(){FCK.Events.FireEvent("OnSelectionChange");};FCK.InitializeBehaviors=function(A){this.EditorDocument.attachEvent('onmouseup',Doc_OnMouseUp);this.EditorDocument.body.attachEvent('onpaste',Doc_OnPaste);FCK.ContextMenu._InnerContextMenu.AttachToElement(FCK.EditorDocument.body);if (FCKConfig.TabSpaces>0){window.FCKTabHTML='';for (i=0;i<FCKConfig.TabSpaces;i++) window.FCKTabHTML+="&nbsp;";};this.EditorDocument.attachEvent("onkeydown",Doc_OnKeyDown);this.EditorDocument.attachEvent("ondblclick",Doc_OnDblClick);this.EditorDocument.attachEvent("onselectionchange",Doc_OnSelectionChange);};FCK.InsertHtml=function(A){A=FCKConfig.ProtectedSource.Protect(A);A=FCK.ProtectEvents(A);A=FCK.ProtectUrls(A);A=FCK.ProtectTags(A);FCK.EditorWindow.focus();FCKUndo.SaveUndoStep();var B=FCK.EditorDocument.selection;if (B.type.toLowerCase()=='control') B.clear();A='<span id="__fakeFCKRemove__">&nbsp;</span>'+A;B.createRange().pasteHTML(A);FCK.EditorDocument.getElementById('__fakeFCKRemove__').removeNode(true);FCKDocumentProcessor.Process(FCK.EditorDocument);};FCK.SetInnerHtml=function(A){var B=FCK.EditorDocument;B.body.innerHTML='<div id="__fakeFCKRemove__">&nbsp;</div>'+A;B.getElementById('__fakeFCKRemove__').removeNode(true);};function FCK_PreloadImages(){var A=new FCKImagePreloader();A.AddImages(FCKConfig.PreloadImages);A.AddImages(FCKConfig.SkinPath+'fck_strip.gif');A.OnComplete=LoadToolbarSetup;A.Start();};function Document_OnContextMenu(){return (event.srcElement._FCKShowContextMenu==true);};document.oncontextmenu=Document_OnContextMenu;function FCK_Cleanup(){this.EditorWindow=null;this.EditorDocument=null;};FCK.Paste=function(){if (FCK._PasteIsRunning) return true;if (FCKConfig.ForcePasteAsPlainText){FCK.PasteAsPlainText();return false;};var A=FCK._CheckIsPastingEnabled(true);if (A===false) FCKTools.RunFunction(FCKDialog.OpenDialog,FCKDialog,['FCKDialog_Paste',FCKLang.Paste,'dialog/fck_paste.html',400,330,'Security']);else{if (FCKConfig.AutoDetectPasteFromWord&&A.length>0){var B=/<\w[^>]*(( class="?MsoNormal"?)|(="mso-))/gi;if (B.test(A)){if (confirm(FCKLang.PasteWordConfirm)){FCK.PasteFromWord();return false;}}};FCK._PasteIsRunning=true;FCK.ExecuteNamedCommand('Paste');delete FCK._PasteIsRunning;};return false;};FCK.PasteAsPlainText=function(){if (!FCK._CheckIsPastingEnabled()){FCKDialog.OpenDialog('FCKDialog_Paste',FCKLang.PasteAsText,'dialog/fck_paste.html',400,330,'PlainText');return;};var A=clipboardData.getData("Text");if (A&&A.length>0){A=FCKTools.HTMLEncode(A).replace(/\n/g,'<BR>');this.InsertHtml(A);}};FCK._CheckIsPastingEnabled=function(A){FCK._PasteIsEnabled=false;document.body.attachEvent('onpaste',FCK_CheckPasting_Listener);var B=FCK.GetClipboardHTML();document.body.detachEvent('onpaste',FCK_CheckPasting_Listener);if (FCK._PasteIsEnabled){if (!A) B=true;}else B=false;delete FCK._PasteIsEnabled;return B;};function FCK_CheckPasting_Listener(){FCK._PasteIsEnabled=true;};FCK.InsertElement=function(A){FCK.InsertHtml(A.outerHTML);};FCK.GetClipboardHTML=function(){var A=document.getElementById('___FCKHiddenDiv');if (!A){A=document.createElement('DIV');A.id='___FCKHiddenDiv';var B=A.style;B.position='absolute';B.visibility=B.overflow='hidden';B.width=B.height=1;document.body.appendChild(A);};A.innerHTML='';var C=document.body.createTextRange();C.moveToElementText(A);C.execCommand('Paste');var D=A.innerHTML;A.innerHTML='';return D;};FCK.CreateLink=function(A){var B=[];FCK.ExecuteNamedCommand('Unlink');if (A.length>0){if (FCKSelection.GetType()=='Control'){var C=this.EditorDocument.createElement('A');C.href=A;var D=FCKSelection.GetSelectedElement();D.parentNode.insertBefore(C,D);D.parentNode.removeChild(D);C.appendChild(D);return [C];};var E='javascript:void(0);/*'+(new Date().getTime())+'*/';FCK.ExecuteNamedCommand('CreateLink',E);var F=this.EditorDocument.links;for (i=0;i<F.length;i++){var C=F[i];if (C.getAttribute('href',2)==E){var H=C.innerHTML;C.href=A;C.innerHTML=H;var I=C.lastChild;if (I&&I.nodeName=='BR'){FCKDomTools.InsertAfterNode(C,C.removeChild(I));};B.push(C);}}};return B;};
+var FCKConfig=FCK.Config={};if (document.location.protocol=='file:'){FCKConfig.BasePath=decodeURIComponent(document.location.pathname.substr(1));FCKConfig.BasePath=FCKConfig.BasePath.replace(/\\/gi, '/');FCKConfig.BasePath='file://'+FCKConfig.BasePath.substring(0,FCKConfig.BasePath.lastIndexOf('/')+1);FCKConfig.FullBasePath=FCKConfig.BasePath;}else{FCKConfig.BasePath=document.location.pathname.substring(0,document.location.pathname.lastIndexOf('/')+1);FCKConfig.FullBasePath=document.location.protocol+'//'+document.location.host+FCKConfig.BasePath;};FCKConfig.EditorPath=FCKConfig.BasePath.replace(/editor\/$/,'');try{FCKConfig.ScreenWidth=screen.width;FCKConfig.ScreenHeight=screen.height;}catch (e){FCKConfig.ScreenWidth=800;FCKConfig.ScreenHeight=600;};FCKConfig.ProcessHiddenField=function(){this.PageConfig={};var A=window.parent.document.getElementById(FCK.Name+'___Config');if (!A) return;var B=A.value.split('&');for (var i=0;i<B.length;i++){if (B[i].length==0) continue;var C=B[i].split('=');var D=decodeURIComponent(C[0]);var E=decodeURIComponent(C[1]);if (D=='CustomConfigurationsPath') FCKConfig[D]=E;else if (E.toLowerCase()=="true") this.PageConfig[D]=true;else if (E.toLowerCase()=="false") this.PageConfig[D]=false;else if (E.length>0&&!isNaN(E)) this.PageConfig[D]=parseInt(E,10);else this.PageConfig[D]=E;}};function FCKConfig_LoadPageConfig(){var A=FCKConfig.PageConfig;for (var B in A) FCKConfig[B]=A[B];};function FCKConfig_PreProcess(){var A=FCKConfig;if (A.AllowQueryStringDebug){try{if ((/fckdebug=true/i).test(window.top.location.search)) A.Debug=true;}catch (e) {/*Ignore it. Much probably we are inside a FRAME where the "top" is in another domain (security error).*/}};if (!A.PluginsPath.EndsWith('/')) A.PluginsPath+='/';if (typeof(A.EditorAreaCSS)=='string') A.EditorAreaCSS=[A.EditorAreaCSS];var B=A.ToolbarComboPreviewCSS;if (!B||B.length==0) A.ToolbarComboPreviewCSS=A.EditorAreaCSS;else if (typeof(B)=='string') A.ToolbarComboPreviewCSS=[B];};FCKConfig.ToolbarSets={};FCKConfig.Plugins={};FCKConfig.Plugins.Items=[];FCKConfig.Plugins.Add=function(A,B,C){FCKConfig.Plugins.Items.AddItem([A,B,C]);};FCKConfig.ProtectedSource={};FCKConfig.ProtectedSource.RegexEntries=[/<!--[\s\S]*?-->/g,/<script[\s\S]*?<\/script>/gi,/<noscript[\s\S]*?<\/noscript>/gi,/<object[\s\S]+?<\/object>/gi];FCKConfig.ProtectedSource.Add=function(A){this.RegexEntries.AddItem(A);};FCKConfig.ProtectedSource.Protect=function(A){function _Replace(protectedSource){var B=FCKTempBin.AddElement(protectedSource);return '<!--{PS..'+B+'}-->';};for (var i=0;i<this.RegexEntries.length;i++){A=A.replace(this.RegexEntries[i],_Replace);};return A;};FCKConfig.ProtectedSource.Revert=function(A,B){function _Replace(m,opener,index){var C=B?FCKTempBin.RemoveElement(index):FCKTempBin.Elements[index];return FCKConfig.ProtectedSource.Revert(C,B);};return A.replace(/(<|&lt;)!--\{PS..(\d+)\}--(>|&gt;)/g,_Replace);}
+var FCKDebug={};FCKDebug._GetWindow=function(){if (!this.DebugWindow||this.DebugWindow.closed) this.DebugWindow=window.open(FCKConfig.BasePath+'fckdebug.html','FCKeditorDebug','menubar=no,scrollbars=yes,resizable=yes,location=no,toolbar=no,width=600,height=500',true);return this.DebugWindow;};FCKDebug.Output=function(A,B,C){if (!FCKConfig.Debug) return;try{this._GetWindow().Output(A,B);}catch (e) {}};FCKDebug.OutputObject=function(A,B){if (!FCKConfig.Debug) return;try{this._GetWindow().OutputObject(A,B);}catch (e) {}}
+var FCKDomTools={MoveChildren:function(A,B){if (A==B) return;var C;while ((C=A.firstChild)) B.appendChild(A.removeChild(C));},TrimNode:function(A,B){this.LTrimNode(A);this.RTrimNode(A,B);},LTrimNode:function(A){var B;while ((B=A.firstChild)){if (B.nodeType==3){var C=B.nodeValue.LTrim();var D=B.nodeValue.length;if (C.length==0){A.removeChild(B);continue;}else if (C.length<D){B.splitText(D-C.length);A.removeChild(A.firstChild);}};break;}},RTrimNode:function(A,B){var C;while ((C=A.lastChild)){switch (C.nodeType){case 1:if (C.nodeName.toUpperCase()=='BR'&&(B||C.getAttribute('type',2)=='_moz')){C.parentNode.removeChild(C);continue;};break;case 3:var D=C.nodeValue.RTrim();var E=C.nodeValue.length;if (D.length==0){C.parentNode.removeChild(C);continue;}else if (D.length<E){C.splitText(D.length);A.lastChild.parentNode.removeChild(A.lastChild);}};break;}},RemoveNode:function(A,B){if (B){var C;while ((C=A.firstChild)) A.parentNode.insertBefore(A.removeChild(C),A);};return A.parentNode.removeChild(A);},GetFirstChild:function(A,B){if (typeof (B)=='string') B=[B];var C=A.firstChild;while(C){if (C.nodeType==1&&C.tagName.Equals.apply(C.tagName,B)) return C;C=C.nextSibling;};return null;},GetLastChild:function(A,B){if (typeof (B)=='string') B=[B];var C=A.lastChild;while(C){if (C.nodeType==1&&(!B||C.tagName.Equals(B))) return C;C=C.previousSibling;};return null;},GetPreviousSourceElement:function(A,B,C,D){if (!A) return null;if (C&&A.nodeType==1&&A.nodeName.IEquals(C)) return null;if (A.previousSibling) A=A.previousSibling;else return this.GetPreviousSourceElement(A.parentNode,B,C,D);while (A){if (A.nodeType==1){if (C&&A.nodeName.IEquals(C)) break;if (!D||!A.nodeName.IEquals(D)) return A;}else if (B&&A.nodeType==3&&A.nodeValue.RTrim().length>0) break;if (A.lastChild) A=A.lastChild;else return this.GetPreviousSourceElement(A,B,C,D);};return null;},GetNextSourceElement:function(A,B,C,D){if (!A) return null;if (A.nextSibling) A=A.nextSibling;else return this.GetNextSourceElement(A.parentNode,B,C,D);while (A){if (A.nodeType==1){if (C&&A.nodeName.IEquals(C)) break;if (!D||!A.nodeName.IEquals(D)) return A;}else if (B&&A.nodeType==3&&A.nodeValue.RTrim().length>0) break;if (A.firstChild) A=A.firstChild;else return this.GetNextSourceElement(A,B,C,D);};return null;},InsertAfterNode:function(A,B){return A.parentNode.insertBefore(B,A.nextSibling);},GetParents:function(A){var B=[];while (A){B.splice(0,0,A);A=A.parentNode;};return B;},GetIndexOf:function(A){var B=A.parentNode?A.parentNode.firstChild:null;var C=-1;while (B){C++;if (B==A) return C;B=B.nextSibling;};return-1;}};
+var GECKO_BOGUS='<br type="_moz">';var FCKTools={};FCKTools.CreateBogusBR=function(A){var B=A.createElement('br');B.setAttribute('type','_moz');return B;};FCKTools.AppendStyleSheet=function(A,B){if (typeof(B)=='string') return this._AppendStyleSheet(A,B);else{var C=[];for (var i=0;i<B.length;i++) C.push(this._AppendStyleSheet(A,B[i]));return C;}};FCKTools.GetElementDocument=function (A){return A.ownerDocument||A.document;};FCKTools.GetElementWindow=function(A){return this.GetDocumentWindow(this.GetElementDocument(A));};FCKTools.GetDocumentWindow=function(A){if (FCKBrowserInfo.IsSafari&&!A.parentWindow) this.FixDocumentParentWindow(window.top);return A.parentWindow||A.defaultView;};FCKTools.FixDocumentParentWindow=function(A){A.document.parentWindow=A;for (var i=0;i<A.frames.length;i++) FCKTools.FixDocumentParentWindow(A.frames[i]);};FCKTools.HTMLEncode=function(A){if (!A) return '';A=A.replace(/&/g,'&amp;');A=A.replace(/</g,'&lt;');A=A.replace(/>/g,'&gt;');return A;};FCKTools.HTMLDecode=function(A){if (!A) return '';A=A.replace(/&gt;/g,'>');A=A.replace(/&lt;/g,'<');A=A.replace(/&amp;/g,'&');return A;};FCKTools.AddSelectOption=function(A,B,C){var D=FCKTools.GetElementDocument(A).createElement("OPTION");D.text=B;D.value=C;A.options.add(D);return D;};FCKTools.RunFunction=function(A,B,C,D){if (A) this.SetTimeout(A,0,B,C,D);};FCKTools.SetTimeout=function(A,B,C,D,E){return (E||window).setTimeout(function(){if (D) A.apply(C,[].concat(D));else A.apply(C);},B);};FCKTools.SetInterval=function(A,B,C,D,E){return (E||window).setInterval(function(){A.apply(C,D||[]);},B);};FCKTools.ConvertStyleSizeToHtml=function(A){return A.EndsWith('%')?A:parseInt(A,10);};FCKTools.ConvertHtmlSizeToStyle=function(A){return A.EndsWith('%')?A:(A+'px');};FCKTools.GetElementAscensor=function(A,B){var e=A;var C=","+B.toUpperCase()+",";while (e){if (C.indexOf(","+e.nodeName.toUpperCase()+",")!=-1) return e;e=e.parentNode;};return null;};FCKTools.CreateEventListener=function(A,B){var f=function(){var C=[];for (var i=0;i<arguments.length;i++) C.push(arguments[i]);A.apply(this,C.concat(B));};return f;};FCKTools.IsStrictMode=function(A){return ('CSS1Compat'==(A.compatMode||'CSS1Compat'));};FCKTools.ArgumentsToArray=function(A,B,C){B=B||0;C=C||A.length;var D=[];for (var i=B;i<B+C&&i<A.length;i++) D.push(A[i]);return D;};FCKTools.CloneObject=function(A){var B=function() {};B.prototype=A;return new B;};FCKTools.GetLastItem=function(A){if (A.length>0) return A[A.length-1];return null;};
+FCKTools.CancelEvent=function(e){return false;};FCKTools._AppendStyleSheet=function(A,B){return A.createStyleSheet(B).owningElement;};FCKTools.ClearElementAttributes=function(A){A.clearAttributes();};FCKTools.GetAllChildrenIds=function(A){var B=[];for (var i=0;i<A.all.length;i++){var C=A.all[i].id;if (C&&C.length>0) B[B.length]=C;};return B;};FCKTools.RemoveOuterTags=function(e){e.insertAdjacentHTML('beforeBegin',e.innerHTML);e.parentNode.removeChild(e);};FCKTools.CreateXmlObject=function(A){var B;switch (A){case 'XmlHttp':B=['MSXML2.XmlHttp','Microsoft.XmlHttp'];break;case 'DOMDocument':B=['MSXML2.DOMDocument','Microsoft.XmlDom'];break;};for (var i=0;i<2;i++){try { return new ActiveXObject(B[i]);}catch (e){}};if (FCKLang.NoActiveX){alert(FCKLang.NoActiveX);FCKLang.NoActiveX=null;};return null;};FCKTools.DisableSelection=function(A){A.unselectable='on';var e,i=0;while ((e=A.all[i++])){switch (e.tagName){case 'IFRAME':case 'TEXTAREA':case 'INPUT':case 'SELECT':break;default:e.unselectable='on';}}};FCKTools.GetScrollPosition=function(A){var B=A.document;var C={ X:B.documentElement.scrollLeft,Y:B.documentElement.scrollTop };if (C.X>0||C.Y>0) return C;return { X:B.body.scrollLeft,Y:B.body.scrollTop };};FCKTools.AddEventListener=function(A,B,C){A.attachEvent('on'+B,C);};FCKTools.RemoveEventListener=function(A,B,C){A.detachEvent('on'+B,C);};FCKTools.AddEventListenerEx=function(A,B,C,D){var o={};o.Source=A;o.Params=D||[];o.Listener=function(ev){return C.apply(o.Source,[ev].concat(o.Params));};if (FCK.IECleanup) FCK.IECleanup.AddItem(null,function() { o.Source=null;o.Params=null;});A.attachEvent('on'+B,o.Listener);A=null;D=null;};FCKTools.GetViewPaneSize=function(A){var B;var C=A.document.documentElement;if (C&&C.clientWidth) B=C;else B=top.document.body;if (B) return { Width:B.clientWidth,Height:B.clientHeight };else return { Width:0,Height:0 };};FCKTools.SaveStyles=function(A){var B={};if (A.className.length>0){B.Class=A.className;A.className='';};var C=A.style.cssText;if (C.length>0){B.Inline=C;A.style.cssText='';};return B;};FCKTools.RestoreStyles=function(A,B){A.className=B.Class||'';A.style.cssText=B.Inline||'';};FCKTools.RegisterDollarFunction=function(A){A.$=A.document.getElementById;};FCKTools.AppendElement=function(A,B){return A.appendChild(this.GetElementDocument(A).createElement(B));};FCKTools.ToLowerCase=function(A){return A.toLowerCase();}
+var FCKeditorAPI;function InitializeAPI(){var A=window.parent;if (!(FCKeditorAPI=A.FCKeditorAPI)){var B='var FCKeditorAPI = {Version : "2.4.3",VersionBuild : "15657",__Instances : new Object(),GetInstance : function( name ){return this.__Instances[ name ];},_FormSubmit : function(){for ( var name in FCKeditorAPI.__Instances ){var oEditor = FCKeditorAPI.__Instances[ name ] ;if ( oEditor.GetParentForm && oEditor.GetParentForm() == this )oEditor.UpdateLinkedField() ;}this._FCKOriginalSubmit() ;},_FunctionQueue : {Functions : new Array(),IsRunning : false,Add : function( f ){this.Functions.push( f );if ( !this.IsRunning )this.StartNext();},StartNext : function(){var aQueue = this.Functions ;if ( aQueue.length > 0 ){this.IsRunning = true;aQueue[0].call();}else this.IsRunning = false;},Remove : function( f ){var aQueue = this.Functions;var i = 0, fFunc;while( (fFunc = aQueue[ i ]) ){if ( fFunc == f )aQueue.splice( i,1 );i++ ;}this.StartNext();}}}';if (A.execScript) A.execScript(B,'JavaScript');else{if (FCKBrowserInfo.IsGecko10){eval.call(A,B);}else if (FCKBrowserInfo.IsSafari){var C=A.document;var D=C.createElement('script');D.appendChild(C.createTextNode(B));C.documentElement.appendChild(D);}else A.eval(B);};FCKeditorAPI=A.FCKeditorAPI;};FCKeditorAPI.__Instances[FCK.Name]=FCK;};function _AttachFormSubmitToAPI(){var A=FCK.GetParentForm();if (A){FCKTools.AddEventListener(A,'submit',FCK.UpdateLinkedField);if (!A._FCKOriginalSubmit&&(typeof(A.submit)=='function'||(!A.submit.tagName&&!A.submit.length))){A._FCKOriginalSubmit=A.submit;A.submit=FCKeditorAPI._FormSubmit;}}};function FCKeditorAPI_Cleanup(){delete FCKeditorAPI.__Instances[FCK.Name];};FCKTools.AddEventListener(window,'unload',FCKeditorAPI_Cleanup);
+var FCKImagePreloader=function(){this._Images=[];};FCKImagePreloader.prototype={AddImages:function(A){if (typeof(A)=='string') A=A.split(';');this._Images=this._Images.concat(A);},Start:function(){var A=this._Images;this._PreloadCount=A.length;for (var i=0;i<A.length;i++){var B=document.createElement('img');B.onload=B.onerror=_FCKImagePreloader_OnImage;B._FCKImagePreloader=this;B.src=A[i];_FCKImagePreloader_ImageCache.push(B);}}};var _FCKImagePreloader_ImageCache=[];function _FCKImagePreloader_OnImage(){var A=this._FCKImagePreloader;if ((--A._PreloadCount)==0&&A.OnComplete) A.OnComplete();this._FCKImagePreloader=null;}
+var FCKRegexLib={AposEntity:/&apos;/gi,ObjectElements:/^(?:IMG|TABLE|TR|TD|TH|INPUT|SELECT|TEXTAREA|HR|OBJECT|A|UL|OL|LI)$/i,NamedCommands:/^(?:Cut|Copy|Paste|Print|SelectAll|RemoveFormat|Unlink|Undo|Redo|Bold|Italic|Underline|StrikeThrough|Subscript|Superscript|JustifyLeft|JustifyCenter|JustifyRight|JustifyFull|Outdent|Indent|InsertOrderedList|InsertUnorderedList|InsertHorizontalRule)$/i,BodyContents:/([\s\S]*\<body[^\>]*\>)([\s\S]*)(\<\/body\>[\s\S]*)/i,ToReplace:/___fcktoreplace:([\w]+)/ig,MetaHttpEquiv:/http-equiv\s*=\s*["']?([^"' ]+)/i,HasBaseTag:/<base /i,HtmlOpener:/<html\s?[^>]*>/i,HeadOpener:/<head\s?[^>]*>/i,HeadCloser:/<\/head\s*>/i,FCK_Class:/(\s*FCK__[A-Za-z]*\s*)/,ElementName:/(^[a-z_:][\w.\-:]*\w$)|(^[a-z_]$)/,ForceSimpleAmpersand:/___FCKAmp___/g,SpaceNoClose:/\/>/g,EmptyParagraph:/^<(p|div|address|h\d|center)(?=[ >])[^>]*>\s*(<\/\1>)?$/,EmptyOutParagraph:/^<(p|div|address|h\d|center)(?=[ >])[^>]*>(?:\s*|&nbsp;)(<\/\1>)?$/,TagBody:/></,StrongOpener:/<STRONG([ \>])/gi,StrongCloser:/<\/STRONG>/gi,EmOpener:/<EM([ \>])/gi,EmCloser:/<\/EM>/gi,GeckoEntitiesMarker:/#\?-\:/g,ProtectUrlsImg:/<img(?=\s).*?\ssrc=((?:(?:\s*)("|').*?\2)|(?:[^"'][^ >]+))/gi,ProtectUrlsA:/<a(?=\s).*?\shref=((?:(?:\s*)("|').*?\2)|(?:[^"'][^ >]+))/gi,Html4DocType:/HTML 4\.0 Transitional/i,DocTypeTag:/<!DOCTYPE[^>]*>/i,TagsWithEvent:/<[^\>]+ on\w+[\s\r\n]*=[\s\r\n]*?('|")[\s\S]+?\>/g,EventAttributes:/\s(on\w+)[\s\r\n]*=[\s\r\n]*?('|")([\s\S]*?)\2/g,ProtectedEvents:/\s\w+_fckprotectedatt="([^"]+)"/g,StyleProperties:/\S+\s*:/g,InvalidSelfCloseTags:/(<(?!base|meta|link|hr|br|param|img|area|input)([a-zA-Z0-9:]+)[^>]*)\/>/gi};
+var FCKListsLib={BlockElements:{ address:1,blockquote:1,center:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,marquee:1,noscript:1,ol:1,p:1,pre:1,script:1,table:1,ul:1 },NonEmptyBlockElements:{ p:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,address:1,pre:1,ol:1,ul:1,li:1,td:1,th:1 },InlineChildReqElements:{ abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1 },EmptyElements:{ base:1,meta:1,link:1,hr:1,br:1,param:1,img:1,area:1,input:1 },PathBlockElements:{ address:1,blockquote:1,dl:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1,ol:1,ul:1,li:1,dt:1,de:1 },PathBlockLimitElements:{ body:1,td:1,th:1,caption:1,form:1 },Setup:function(){if (FCKConfig.EnterMode=='div') this.PathBlockElements.div=1;else this.PathBlockLimitElements.div=1;}};
+var FCKLanguageManager=FCK.Language={AvailableLanguages:{af:'Afrikaans',ar:'Arabic',bg:'Bulgarian',bn:'Bengali/Bangla',bs:'Bosnian',ca:'Catalan',cs:'Czech',da:'Danish',de:'German',el:'Greek',en:'English','en-au':'English (Australia)','en-ca':'English (Canadian)','en-uk':'English (United Kingdom)',eo:'Esperanto',es:'Spanish',et:'Estonian',eu:'Basque',fa:'Persian',fi:'Finnish',fo:'Faroese',fr:'French',gl:'Galician',he:'Hebrew',hi:'Hindi',hr:'Croatian',hu:'Hungarian',it:'Italian',ja:'Japanese',km:'Khmer',ko:'Korean',lt:'Lithuanian',lv:'Latvian',mn:'Mongolian',ms:'Malay',nb:'Norwegian Bokmal',nl:'Dutch',no:'Norwegian',pl:'Polish',pt:'Portuguese (Portugal)','pt-br':'Portuguese (Brazil)',ro:'Romanian',ru:'Russian',sk:'Slovak',sl:'Slovenian',sr:'Serbian (Cyrillic)','sr-latn':'Serbian (Latin)',sv:'Swedish',th:'Thai',tr:'Turkish',uk:'Ukrainian',vi:'Vietnamese',zh:'Chinese Traditional','zh-cn':'Chinese Simplified'},GetActiveLanguage:function(){if (FCKConfig.AutoDetectLanguage){var A;if (navigator.userLanguage) A=navigator.userLanguage.toLowerCase();else if (navigator.language) A=navigator.language.toLowerCase();else{return FCKConfig.DefaultLanguage;};if (A.length>=5){A=A.substr(0,5);if (this.AvailableLanguages[A]) return A;};if (A.length>=2){A=A.substr(0,2);if (this.AvailableLanguages[A]) return A;}};return this.DefaultLanguage;},TranslateElements:function(A,B,C,D){var e=A.getElementsByTagName(B);var E,s;for (var i=0;i<e.length;i++){if ((E=e[i].getAttribute('fckLang'))){if ((s=FCKLang[E])){if (D) s=FCKTools.HTMLEncode(s);eval('e[i].'+C+' = s');}}}},TranslatePage:function(A){this.TranslateElements(A,'INPUT','value');this.TranslateElements(A,'SPAN','innerHTML');this.TranslateElements(A,'LABEL','innerHTML');this.TranslateElements(A,'OPTION','innerHTML',true);},Initialize:function(){if (this.AvailableLanguages[FCKConfig.DefaultLanguage]) this.DefaultLanguage=FCKConfig.DefaultLanguage;else this.DefaultLanguage='en';this.ActiveLanguage={};this.ActiveLanguage.Code=this.GetActiveLanguage();this.ActiveLanguage.Name=this.AvailableLanguages[this.ActiveLanguage.Code];}};
+var FCKXHtmlEntities={};FCKXHtmlEntities.Initialize=function(){if (FCKXHtmlEntities.Entities) return;var A='';var B,e;if (FCKConfig.ProcessHTMLEntities){FCKXHtmlEntities.Entities={' ':'nbsp','¡':'iexcl','¢':'cent','£':'pound','¤':'curren','Â¥':'yen','¦':'brvbar','§':'sect','¨':'uml','©':'copy','ª':'ordf','«':'laquo','¬':'not','­':'shy','®':'reg','¯':'macr','°':'deg','±':'plusmn','²':'sup2','³':'sup3','´':'acute','µ':'micro','¶':'para','·':'middot','¸':'cedil','¹':'sup1','º':'ordm','»':'raquo','¼':'frac14','½':'frac12','¾':'frac34','¿':'iquest','×':'times','÷':'divide','Æ’':'fnof','•':'bull','…':'hellip','′':'prime','″':'Prime','‾':'oline','â„':'frasl','℘':'weierp','â„‘':'image','â„œ':'real','â„¢':'trade','ℵ':'alefsym','â†':'larr','↑':'uarr','→':'rarr','↓':'darr','↔':'harr','↵':'crarr','â‡':'lArr','⇑':'uArr','⇒':'rArr','⇓':'dArr','⇔':'hArr','∀':'forall','∂':'part','∃':'exist','∅':'empty','∇':'nabla','∈':'isin','∉':'notin','∋':'ni','âˆ':'prod','∑':'sum','−':'minus','∗':'lowast','√':'radic','âˆ':'prop','∞':'infin','∠':'ang','∧':'and','∨':'or','∩':'cap','∪':'cup','∫':'int','∴':'there4','∼':'sim','≅':'cong','≈':'asymp','≠':'ne','≡':'equiv','≤':'le','≥':'ge','⊂':'sub','⊃':'sup','⊄':'nsub','⊆':'sube','⊇':'supe','⊕':'oplus','⊗':'otimes','⊥':'perp','â‹…':'sdot','â—Š':'loz','â™ ':'spades','♣':'clubs','♥':'hearts','♦':'diams','"':'quot','ˆ':'circ','Ëœ':'tilde',' ':'ensp',' ':'emsp',' ':'thinsp','‌':'zwnj','â€':'zwj','‎':'lrm','â€':'rlm','–':'ndash','—':'mdash','‘':'lsquo','’':'rsquo','‚':'sbquo','“':'ldquo','â€':'rdquo','„':'bdquo','†':'dagger','‡':'Dagger','‰':'permil','‹':'lsaquo','›':'rsaquo','€':'euro'};for (e in FCKXHtmlEntities.Entities) A+=e;if (FCKConfig.IncludeLatinEntities){B={'À':'Agrave','Ã':'Aacute','Â':'Acirc','Ã':'Atilde','Ä':'Auml','Ã…':'Aring','Æ':'AElig','Ç':'Ccedil','È':'Egrave','É':'Eacute','Ê':'Ecirc','Ë':'Euml','ÃŒ':'Igrave','Ã':'Iacute','ÃŽ':'Icirc','Ã':'Iuml','Ã':'ETH','Ñ':'Ntilde','Ã’':'Ograve','Ó':'Oacute','Ô':'Ocirc','Õ':'Otilde','Ö':'Ouml','Ø':'Oslash','Ù':'Ugrave','Ú':'Uacute','Û':'Ucirc','Ãœ':'Uuml','Ã':'Yacute','Þ':'THORN','ß':'szlig','à':'agrave','á':'aacute','â':'acirc','ã':'atilde','ä':'auml','Ã¥':'aring','æ':'aelig','ç':'ccedil','è':'egrave','é':'eacute','ê':'ecirc','ë':'euml','ì':'igrave','í':'iacute','î':'icirc','ï':'iuml','ð':'eth','ñ':'ntilde','ò':'ograve','ó':'oacute','ô':'ocirc','õ':'otilde','ö':'ouml','ø':'oslash','ù':'ugrave','ú':'uacute','û':'ucirc','ü':'uuml','ý':'yacute','þ':'thorn','ÿ':'yuml','Å’':'OElig','Å“':'oelig','Å ':'Scaron','Å¡':'scaron','Ÿ':'Yuml'};for (e in B){FCKXHtmlEntities.Entities[e]=B[e];A+=e;};B=null;};if (FCKConfig.IncludeGreekEntities){B={'Α':'Alpha','Î’':'Beta','Γ':'Gamma','Δ':'Delta','Ε':'Epsilon','Ζ':'Zeta','Η':'Eta','Θ':'Theta','Ι':'Iota','Κ':'Kappa','Λ':'Lambda','Îœ':'Mu','Î':'Nu','Ξ':'Xi','Ο':'Omicron','Π':'Pi','Ρ':'Rho','Σ':'Sigma','Τ':'Tau','Î¥':'Upsilon','Φ':'Phi','Χ':'Chi','Ψ':'Psi','Ω':'Omega','α':'alpha','β':'beta','γ':'gamma','δ':'delta','ε':'epsilon','ζ':'zeta','η':'eta','θ':'theta','ι':'iota','κ':'kappa','λ':'lambda','μ':'mu','ν':'nu','ξ':'xi','ο':'omicron','Ï€':'pi','Ï':'rho','Ï‚':'sigmaf','σ':'sigma','Ï„':'tau','Ï…':'upsilon','φ':'phi','χ':'chi','ψ':'psi','ω':'omega'};for (e in B){FCKXHtmlEntities.Entities[e]=B[e];A+=e;};B=null;}}else{FCKXHtmlEntities.Entities={};A=' ';};var C='['+A+']';if (FCKConfig.ProcessNumericEntities) C='[^ -~]|'+C;var D=FCKConfig.AdditionalNumericEntities;if (D&&D.length>0) C+='|'+FCKConfig.AdditionalNumericEntities;FCKXHtmlEntities.EntitiesRegex=new RegExp(C,'g');}
+var FCKXHtml={};FCKXHtml.CurrentJobNum=0;FCKXHtml.GetXHTML=function(A,B,C){FCKXHtmlEntities.Initialize();this._NbspEntity=(FCKConfig.ProcessHTMLEntities?'nbsp':'#160');var D=FCK.IsDirty();this._CreateNode=FCKConfig.ForceStrongEm?FCKXHtml_CreateNode_StrongEm:FCKXHtml_CreateNode_Normal;FCKXHtml.SpecialBlocks=[];this.XML=FCKTools.CreateXmlObject('DOMDocument');this.MainNode=this.XML.appendChild(this.XML.createElement('xhtml'));FCKXHtml.CurrentJobNum++;if (B) this._AppendNode(this.MainNode,A);else this._AppendChildNodes(this.MainNode,A,false);var E=this._GetMainXmlString();this.XML=null;E=E.substr(7,E.length-15).Trim();if (FCKBrowserInfo.IsGecko) E=E.replace(/<br\/>$/,'');E=E.replace(FCKRegexLib.SpaceNoClose,' />');if (FCKConfig.ForceSimpleAmpersand) E=E.replace(FCKRegexLib.ForceSimpleAmpersand,'&');if (C) E=FCKCodeFormatter.Format(E);for (var i=0;i<FCKXHtml.SpecialBlocks.length;i++){var F=new RegExp('___FCKsi___'+i);E=E.replace(F,FCKXHtml.SpecialBlocks[i]);};E=E.replace(FCKRegexLib.GeckoEntitiesMarker,'&');if (!D) FCK.ResetIsDirty();return E;};FCKXHtml._AppendAttribute=function(A,B,C){try{if (C==undefined||C==null) C='';else if (C.replace){if (FCKConfig.ForceSimpleAmpersand) C=C.replace(/&/g,'___FCKAmp___');C=C.replace(FCKXHtmlEntities.EntitiesRegex,FCKXHtml_GetEntity);};var D=this.XML.createAttribute(B);D.value=C;A.attributes.setNamedItem(D);}catch (e){}};FCKXHtml._AppendChildNodes=function(A,B,C){var D=B.firstChild;while (D){this._AppendNode(A,D);D=D.nextSibling;};if (C) FCKDomTools.TrimNode(A,true);if (A.childNodes.length==0){if (C&&FCKConfig.FillEmptyBlocks){this._AppendEntity(A,this._NbspEntity);return A;};var E=A.nodeName;if (FCKListsLib.InlineChildReqElements[E]) return null;if (!FCKListsLib.EmptyElements[E]) A.appendChild(this.XML.createTextNode(''));};return A;};FCKXHtml._AppendNode=function(A,B){if (!B) return false;switch (B.nodeType){case 1:if (B.getAttribute('_fckfakelement')) return FCKXHtml._AppendNode(A,FCK.GetRealElement(B));if (FCKBrowserInfo.IsGecko&&B.hasAttribute('_moz_editor_bogus_node')) return false;if (B.getAttribute('_fcktemp')) return false;var C=B.tagName.toLowerCase();if (FCKBrowserInfo.IsIE){if (B.scopeName&&B.scopeName!='HTML'&&B.scopeName!='FCK') C=B.scopeName.toLowerCase()+':'+C;}else{if (C.StartsWith('fck:')) C=C.Remove(0,4);};if (!FCKRegexLib.ElementName.test(C)) return false;if (C=='br'&&B.getAttribute('type',2)=='_moz') return false;if (B._fckxhtmljob&&B._fckxhtmljob==FCKXHtml.CurrentJobNum) return false;var D=this._CreateNode(C);FCKXHtml._AppendAttributes(A,B,D,C);B._fckxhtmljob=FCKXHtml.CurrentJobNum;var E=FCKXHtml.TagProcessors[C];if (E) D=E(D,B,A);else D=this._AppendChildNodes(D,B,Boolean(FCKListsLib.NonEmptyBlockElements[C]));if (!D) return false;A.appendChild(D);break;case 3:return this._AppendTextNode(A,B.nodeValue.ReplaceNewLineChars(' '));case 8:if (FCKBrowserInfo.IsIE&&!B.innerHTML) break;try { A.appendChild(this.XML.createComment(B.nodeValue));}catch (e) {/*Do nothing... probably this is a wrong format comment.*/};break;default:A.appendChild(this.XML.createComment("Element not supported - Type: "+B.nodeType+" Name: "+B.nodeName));break;};return true;};function FCKXHtml_CreateNode_StrongEm(A){switch (A){case 'b':A='strong';break;case 'i':A='em';break;};return this.XML.createElement(A);};function FCKXHtml_CreateNode_Normal(A){return this.XML.createElement(A);};FCKXHtml._AppendSpecialItem=function(A){return '___FCKsi___'+FCKXHtml.SpecialBlocks.AddItem(A);};FCKXHtml._AppendEntity=function(A,B){A.appendChild(this.XML.createTextNode('#?-:'+B+';'));};FCKXHtml._AppendTextNode=function(A,B){var C=B.length>0;if (C) A.appendChild(this.XML.createTextNode(B.replace(FCKXHtmlEntities.EntitiesRegex,FCKXHtml_GetEntity)));return C;};function FCKXHtml_GetEntity(A){var B=FCKXHtmlEntities.Entities[A]||('#'+A.charCodeAt(0));return '#?-:'+B+';';};FCKXHtml._RemoveAttribute=function(A,B,C){var D=A.attributes.getNamedItem(C);if (D&&B.test(D.nodeValue)){var E=D.nodeValue.replace(B,'');if (E.length==0) A.attributes.removeNamedItem(C);else D.nodeValue=E;}};FCKXHtml.TagProcessors={img:function(A,B){if (!A.attributes.getNamedItem('alt')) FCKXHtml._AppendAttribute(A,'alt','');var C=B.getAttribute('_fcksavedurl');if (C!=null) FCKXHtml._AppendAttribute(A,'src',C);return A;},a:function(A,B){if (B.innerHTML.Trim().length==0&&!B.name) return false;var C=B.getAttribute('_fcksavedurl');if (C!=null) FCKXHtml._AppendAttribute(A,'href',C);if (FCKBrowserInfo.IsIE){FCKXHtml._RemoveAttribute(A,FCKRegexLib.FCK_Class,'class');if (B.name) FCKXHtml._AppendAttribute(A,'name',B.name);};A=FCKXHtml._AppendChildNodes(A,B,false);return A;},script:function(A,B){if (!A.attributes.getNamedItem('type')) FCKXHtml._AppendAttribute(A,'type','text/javascript');A.appendChild(FCKXHtml.XML.createTextNode(FCKXHtml._AppendSpecialItem(B.text)));return A;},style:function(A,B){if (!A.attributes.getNamedItem('type')) FCKXHtml._AppendAttribute(A,'type','text/css');A.appendChild(FCKXHtml.XML.createTextNode(FCKXHtml._AppendSpecialItem(B.innerHTML)));return A;},title:function(A,B){A.appendChild(FCKXHtml.XML.createTextNode(FCK.EditorDocument.title));return A;},table:function(A,B){if (FCKBrowserInfo.IsIE) FCKXHtml._RemoveAttribute(A,FCKRegexLib.FCK_Class,'class');A=FCKXHtml._AppendChildNodes(A,B,false);return A;},ol:function(A,B,C){if (B.innerHTML.Trim().length==0) return false;var D=C.lastChild;if (D&&D.nodeType==3) D=D.previousSibling;if (D&&D.nodeName.toUpperCase()=='LI'){B._fckxhtmljob=null;FCKXHtml._AppendNode(D,B);return false;};A=FCKXHtml._AppendChildNodes(A,B);return A;},span:function(A,B){if (B.innerHTML.length==0) return false;A=FCKXHtml._AppendChildNodes(A,B,false);return A;},iframe:function(A,B){var C=B.innerHTML;if (FCKBrowserInfo.IsGecko) C=FCKTools.HTMLDecode(C);C=C.replace(/\s_fcksavedurl="[^"]*"/g,'');A.appendChild(FCKXHtml.XML.createTextNode(FCKXHtml._AppendSpecialItem(C)));return A;}};FCKXHtml.TagProcessors.ul=FCKXHtml.TagProcessors.ol;
+FCKXHtml._GetMainXmlString=function(){return this.MainNode.xml;};FCKXHtml._AppendAttributes=function(A,B,C,D){var E=B.attributes;for (var n=0;n<E.length;n++){var F=E[n];if (F.specified){var G=F.nodeName.toLowerCase();var H;if (G.StartsWith('_fck')) continue;else if (G=='style') H=B.style.cssText.replace(FCKRegexLib.StyleProperties,FCKTools.ToLowerCase);else if (G=='class'||G.indexOf('on')==0) H=F.nodeValue;else if (D=='body'&&G=='contenteditable') continue;else if (F.nodeValue===true) H=G;else{try{H=B.getAttribute(G,2);}catch (e) {}};this._AppendAttribute(C,G,H||F.nodeValue);}}};FCKXHtml.TagProcessors['meta']=function(A,B){var C=A.attributes.getNamedItem('http-equiv');if (C==null||C.value.length==0){var D=B.outerHTML.match(FCKRegexLib.MetaHttpEquiv);if (D){D=D[1];FCKXHtml._AppendAttribute(A,'http-equiv',D);}};return A;};FCKXHtml.TagProcessors['font']=function(A,B){if (A.attributes.length==0) A=FCKXHtml.XML.createDocumentFragment();A=FCKXHtml._AppendChildNodes(A,B);return A;};FCKXHtml.TagProcessors['input']=function(A,B){if (B.name) FCKXHtml._AppendAttribute(A,'name',B.name);if (B.value&&!A.attributes.getNamedItem('value')) FCKXHtml._AppendAttribute(A,'value',B.value);if (!A.attributes.getNamedItem('type')) FCKXHtml._AppendAttribute(A,'type','text');return A;};FCKXHtml.TagProcessors['option']=function(A,B){if (B.selected&&!A.attributes.getNamedItem('selected')) FCKXHtml._AppendAttribute(A,'selected','selected');A=FCKXHtml._AppendChildNodes(A,B);return A;};FCKXHtml.TagProcessors['area']=function(A,B){if (!A.attributes.getNamedItem('coords')){var C=B.getAttribute('coords',2);if (C&&C!='0,0,0') FCKXHtml._AppendAttribute(A,'coords',C);};if (!A.attributes.getNamedItem('shape')){var D=B.getAttribute('shape',2);if (D&&D.length>0) FCKXHtml._AppendAttribute(A,'shape',D);};return A;};FCKXHtml.TagProcessors['label']=function(A,B){if (B.htmlFor.length>0) FCKXHtml._AppendAttribute(A,'for',B.htmlFor);A=FCKXHtml._AppendChildNodes(A,B);return A;};FCKXHtml.TagProcessors['form']=function(A,B){if (B.acceptCharset&&B.acceptCharset.length>0&&B.acceptCharset!='UNKNOWN') FCKXHtml._AppendAttribute(A,'accept-charset',B.acceptCharset);var C=B.attributes['name'];if (C&&C.value.length>0) FCKXHtml._AppendAttribute(A,'name',C.value);A=FCKXHtml._AppendChildNodes(A,B);return A;};FCKXHtml.TagProcessors['textarea']=FCKXHtml.TagProcessors['select']=function(A,B){if (B.name) FCKXHtml._AppendAttribute(A,'name',B.name);A=FCKXHtml._AppendChildNodes(A,B);return A;};FCKXHtml.TagProcessors['div']=function(A,B){if (B.align.length>0) FCKXHtml._AppendAttribute(A,'align',B.align);A=FCKXHtml._AppendChildNodes(A,B,true);return A;}
+var FCKCodeFormatter={};FCKCodeFormatter.Init=function(){var A=this.Regex={};A.BlocksOpener=/\<(P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|TITLE|META|LINK|BASE|SCRIPT|LINK|TD|TH|AREA|OPTION)[^\>]*\>/gi;A.BlocksCloser=/\<\/(P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|TITLE|META|LINK|BASE|SCRIPT|LINK|TD|TH|AREA|OPTION)[^\>]*\>/gi;A.NewLineTags=/\<(BR|HR)[^\>]*\>/gi;A.MainTags=/\<\/?(HTML|HEAD|BODY|FORM|TABLE|TBODY|THEAD|TR)[^\>]*\>/gi;A.LineSplitter=/\s*\n+\s*/g;A.IncreaseIndent=/^\<(HTML|HEAD|BODY|FORM|TABLE|TBODY|THEAD|TR|UL|OL)[ \/\>]/i;A.DecreaseIndent=/^\<\/(HTML|HEAD|BODY|FORM|TABLE|TBODY|THEAD|TR|UL|OL)[ \>]/i;A.FormatIndentatorRemove=new RegExp('^'+FCKConfig.FormatIndentator);A.ProtectedTags=/(<PRE[^>]*>)([\s\S]*?)(<\/PRE>)/gi;};FCKCodeFormatter._ProtectData=function(A,B,C,D){return B+'___FCKpd___'+FCKCodeFormatter.ProtectedData.AddItem(C)+D;};FCKCodeFormatter.Format=function(A){if (!this.Regex) this.Init();FCKCodeFormatter.ProtectedData=[];var B=A.replace(this.Regex.ProtectedTags,FCKCodeFormatter._ProtectData);B=B.replace(this.Regex.BlocksOpener,'\n$&');B=B.replace(this.Regex.BlocksCloser,'$&\n');B=B.replace(this.Regex.NewLineTags,'$&\n');B=B.replace(this.Regex.MainTags,'\n$&\n');var C='';var D=B.split(this.Regex.LineSplitter);B='';for (var i=0;i<D.length;i++){var E=D[i];if (E.length==0) continue;if (this.Regex.DecreaseIndent.test(E)) C=C.replace(this.Regex.FormatIndentatorRemove,'');B+=C+E+'\n';if (this.Regex.IncreaseIndent.test(E)) C+=FCKConfig.FormatIndentator;};for (var j=0;j<FCKCodeFormatter.ProtectedData.length;j++){var F=new RegExp('___FCKpd___'+j);B=B.replace(F,FCKCodeFormatter.ProtectedData[j].replace(/\$/g,'$$$$'));};return B.Trim();}
+var FCKUndo={};FCKUndo.SavedData=[];FCKUndo.CurrentIndex=-1;FCKUndo.TypesCount=FCKUndo.MaxTypes=25;FCKUndo.Typing=false;FCKUndo.SaveUndoStep=function(){if (FCK.EditMode!=0) return;FCKUndo.SavedData=FCKUndo.SavedData.slice(0,FCKUndo.CurrentIndex+1);var A=FCK.EditorDocument.body.innerHTML;if (FCKUndo.CurrentIndex>=0&&A==FCKUndo.SavedData[FCKUndo.CurrentIndex][0]) return;if (FCKUndo.CurrentIndex+1>=FCKConfig.MaxUndoLevels) FCKUndo.SavedData.shift();else FCKUndo.CurrentIndex++;var B;if (FCK.EditorDocument.selection.type=='Text') B=FCK.EditorDocument.selection.createRange().getBookmark();FCKUndo.SavedData[FCKUndo.CurrentIndex]=[A,B];FCK.Events.FireEvent("OnSelectionChange");};FCKUndo.CheckUndoState=function(){return (FCKUndo.Typing||FCKUndo.CurrentIndex>0);};FCKUndo.CheckRedoState=function(){return (!FCKUndo.Typing&&FCKUndo.CurrentIndex<(FCKUndo.SavedData.length-1));};FCKUndo.Undo=function(){if (FCKUndo.CheckUndoState()){if (FCKUndo.CurrentIndex==(FCKUndo.SavedData.length-1)){FCKUndo.SaveUndoStep();};FCKUndo._ApplyUndoLevel(--FCKUndo.CurrentIndex);FCK.Events.FireEvent("OnSelectionChange");}};FCKUndo.Redo=function(){if (FCKUndo.CheckRedoState()){FCKUndo._ApplyUndoLevel(++FCKUndo.CurrentIndex);FCK.Events.FireEvent("OnSelectionChange");}};FCKUndo._ApplyUndoLevel=function(A){var B=FCKUndo.SavedData[A];if (!B) return;FCK.SetInnerHtml(B[0]);if (B[1]){var C=FCK.EditorDocument.selection.createRange();C.moveToBookmark(B[1]);C.select();};FCKUndo.TypesCount=0;FCKUndo.Typing=false;}
+var FCKEditingArea=function(A){this.TargetElement=A;this.Mode=0;if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKEditingArea_Cleanup);};FCKEditingArea.prototype.Start=function(A,B){var C=this.TargetElement;var D=FCKTools.GetElementDocument(C);while(C.childNodes.length>0) C.removeChild(C.childNodes[0]);if (this.Mode==0){var E=this.IFrame=D.createElement('iframe');E.src='javascript:void(0)';E.frameBorder=0;E.width=E.height='100%';C.appendChild(E);if (FCKBrowserInfo.IsIE) A=A.replace(/(<base[^>]*?)\s*\/?>(?!\s*<\/base>)/gi,'$1></base>');else if (!B){if (FCKBrowserInfo.IsGecko) A=A.replace(/(<body[^>]*>)\s*(<\/body>)/i,'$1'+GECKO_BOGUS+'$2');var F=A.match(FCKRegexLib.BodyContents);if (F){A=F[1]+'&nbsp;'+F[3];this._BodyHTML=F[2];}else this._BodyHTML=A;};this.Window=E.contentWindow;var G=this.Document=this.Window.document;G.open();G.write(A);G.close();if (FCKBrowserInfo.IsGecko10&&!B){this.Start(A,true);return;};this.Window._FCKEditingArea=this;if (FCKBrowserInfo.IsGecko10) this.Window.setTimeout(FCKEditingArea_CompleteStart,500);else FCKEditingArea_CompleteStart.call(this.Window);}else{var H=this.Textarea=D.createElement('textarea');H.className='SourceField';H.dir='ltr';H.style.width=H.style.height='100%';H.style.border='none';C.appendChild(H);H.value=A;FCKTools.RunFunction(this.OnLoad);}};function FCKEditingArea_CompleteStart(){if (!this.document.body){this.setTimeout(FCKEditingArea_CompleteStart,50);return;};var A=this._FCKEditingArea;A.MakeEditable();FCKTools.RunFunction(A.OnLoad);};FCKEditingArea.prototype.MakeEditable=function(){var A=this.Document;if (FCKBrowserInfo.IsIE){A.body.contentEditable=true;}else{try{A.body.spellcheck=(this.FFSpellChecker!==false);if (this._BodyHTML){A.body.innerHTML=this._BodyHTML;this._BodyHTML=null;};A.designMode='on';try{A.execCommand('styleWithCSS',false,FCKConfig.GeckoUseSPAN);}catch (e){A.execCommand('useCSS',false,!FCKConfig.GeckoUseSPAN);};A.execCommand('enableObjectResizing',false,!FCKConfig.DisableObjectResizing);A.execCommand('enableInlineTableEditing',false,!FCKConfig.DisableFFTableHandles);}catch (e) {}}};FCKEditingArea.prototype.Focus=function(){try{if (this.Mode==0){if (FCKBrowserInfo.IsIE&&this.Document.hasFocus()) return;if (FCKBrowserInfo.IsSafari) this.IFrame.focus();else{this.Window.focus();}}else{var A=FCKTools.GetElementDocument(this.Textarea);if ((!A.hasFocus||A.hasFocus())&&A.activeElement==this.Textarea) return;this.Textarea.focus();}}catch(e) {}};function FCKEditingArea_Cleanup(){this.TargetElement=null;this.IFrame=null;this.Document=null;this.Textarea=null;if (this.Window){this.Window._FCKEditingArea=null;this.Window=null;}};
+var FCKKeystrokeHandler=function(A){this.Keystrokes={};this.CancelCtrlDefaults=(A!==false);};FCKKeystrokeHandler.prototype.AttachToElement=function(A){FCKTools.AddEventListenerEx(A,'keydown',_FCKKeystrokeHandler_OnKeyDown,this);if (FCKBrowserInfo.IsGecko10||FCKBrowserInfo.IsOpera||(FCKBrowserInfo.IsGecko&&FCKBrowserInfo.IsMac)) FCKTools.AddEventListenerEx(A,'keypress',_FCKKeystrokeHandler_OnKeyPress,this);};FCKKeystrokeHandler.prototype.SetKeystrokes=function(){for (var i=0;i<arguments.length;i++){var A=arguments[i];if (typeof(A[0])=='object') this.SetKeystrokes.apply(this,A);else{if (A.length==1) delete this.Keystrokes[A[0]];else this.Keystrokes[A[0]]=A[1]===true?true:A;}}};function _FCKKeystrokeHandler_OnKeyDown(A,B){var C=A.keyCode||A.which;var D=0;if (A.ctrlKey||A.metaKey) D+=CTRL;if (A.shiftKey) D+=SHIFT;if (A.altKey) D+=ALT;var E=C+D;var F=B._CancelIt=false;var G=B.Keystrokes[E];if (G){if (G===true||!(B.OnKeystroke&&B.OnKeystroke.apply(B,G))) return true;F=true;};if (F||(B.CancelCtrlDefaults&&D==CTRL&&(C<33||C>40))){B._CancelIt=true;if (A.preventDefault) return A.preventDefault();A.returnValue=false;A.cancelBubble=true;return false;};return true;};function _FCKKeystrokeHandler_OnKeyPress(A,B){if (B._CancelIt){if (A.preventDefault) return A.preventDefault();return false;};return true;}
+var FCKListHandler={OutdentListItem:function(A){var B=A.parentNode;if (B.tagName.toUpperCase().Equals('UL','OL')){var C=FCKTools.GetElementDocument(A);var D=new FCKDocumentFragment(C);var E=D.RootNode;var F=false;var G=FCKDomTools.GetFirstChild(A,['UL','OL']);if (G){F=true;var H;while ((H=G.firstChild)) E.appendChild(G.removeChild(H));FCKDomTools.RemoveNode(G);};var I;var J=false;while ((I=A.nextSibling)){if (!F&&I.nodeType==1&&I.nodeName.toUpperCase()=='LI') J=F=true;E.appendChild(I.parentNode.removeChild(I));if (!J&&I.nodeType==1&&I.nodeName.toUpperCase().Equals('UL','OL')) FCKDomTools.RemoveNode(I,true);};var K=B.parentNode.tagName.toUpperCase();var L=(K=='LI');if (L||K.Equals('UL','OL')){if (F){var G=B.cloneNode(false);D.AppendTo(G);A.appendChild(G);}else if (L) D.InsertAfterNode(B.parentNode);else D.InsertAfterNode(B);if (L) FCKDomTools.InsertAfterNode(B.parentNode,B.removeChild(A));else FCKDomTools.InsertAfterNode(B,B.removeChild(A));}else{if (F){var N=B.cloneNode(false);D.AppendTo(N);FCKDomTools.InsertAfterNode(B,N);};var O=C.createElement(FCKConfig.EnterMode=='p'?'p':'div');FCKDomTools.MoveChildren(B.removeChild(A),O);FCKDomTools.InsertAfterNode(B,O);if (FCKConfig.EnterMode=='br'){if (FCKBrowserInfo.IsGecko) O.parentNode.insertBefore(FCKTools.CreateBogusBR(C),O);else FCKDomTools.InsertAfterNode(O,FCKTools.CreateBogusBR(C));FCKDomTools.RemoveNode(O,true);}};if (this.CheckEmptyList(B)) FCKDomTools.RemoveNode(B,true);}},CheckEmptyList:function(A){return (FCKDomTools.GetFirstChild(A,'LI')==null);},CheckListHasContents:function(A){var B=A.firstChild;while (B){switch (B.nodeType){case 1:if (!B.nodeName.IEquals('UL','LI')) return true;break;case 3:if (B.nodeValue.Trim().length>0) return true;};B=B.nextSibling;};return false;}};
+var FCKElementPath=function(A){var B=null;var C=null;var D=[];var e=A;while (e){if (e.nodeType==1){if (!this.LastElement) this.LastElement=e;var E=e.nodeName.toLowerCase();if (!C){if (!B&&FCKListsLib.PathBlockElements[E]!=null) B=e;if (FCKListsLib.PathBlockLimitElements[E]!=null) C=e;};D.push(e);if (E=='body') break;};e=e.parentNode;};this.Block=B;this.BlockLimit=C;this.Elements=D;};
+var FCKDomRange=function(A){this.Window=A;};FCKDomRange.prototype={_UpdateElementInfo:function(){if (!this._Range) this.Release(true);else{var A=this._Range.startContainer;var B=this._Range.endContainer;var C=new FCKElementPath(A);this.StartContainer=C.LastElement;this.StartBlock=C.Block;this.StartBlockLimit=C.BlockLimit;if (A!=B) C=new FCKElementPath(B);this.EndContainer=C.LastElement;this.EndBlock=C.Block;this.EndBlockLimit=C.BlockLimit;}},CreateRange:function(){return new FCKW3CRange(this.Window.document);},DeleteContents:function(){if (this._Range){this._Range.deleteContents();this._UpdateElementInfo();}},ExtractContents:function(){if (this._Range){var A=this._Range.extractContents();this._UpdateElementInfo();return A;}},CheckIsCollapsed:function(){if (this._Range) return this._Range.collapsed;},Collapse:function(A){if (this._Range) this._Range.collapse(A);this._UpdateElementInfo();},Clone:function(){var A=FCKTools.CloneObject(this);if (this._Range) A._Range=this._Range.cloneRange();return A;},MoveToNodeContents:function(A){if (!this._Range) this._Range=this.CreateRange();this._Range.selectNodeContents(A);this._UpdateElementInfo();},MoveToElementStart:function(A){this.SetStart(A,1);this.SetEnd(A,1);},MoveToElementEditStart:function(A){var B;while ((B=A.firstChild)&&B.nodeType==1&&FCKListsLib.EmptyElements[B.nodeName.toLowerCase()]==null) A=B;this.MoveToElementStart(A);},InsertNode:function(A){if (this._Range) this._Range.insertNode(A);},CheckIsEmpty:function(A){if (this.CheckIsCollapsed()) return true;var B=this.Window.document.createElement('div');this._Range.cloneContents().AppendTo(B);FCKDomTools.TrimNode(B,A);return (B.innerHTML.length==0);},CheckStartOfBlock:function(){var A=this.Clone();A.Collapse(true);A.SetStart(A.StartBlock||A.StartBlockLimit,1);var B=A.CheckIsEmpty();A.Release();return B;},CheckEndOfBlock:function(A){var B=this.Clone();B.Collapse(false);B.SetEnd(B.EndBlock||B.EndBlockLimit,2);var C=B.CheckIsCollapsed();if (!C){var D=this.Window.document.createElement('div');B._Range.cloneContents().AppendTo(D);FCKDomTools.TrimNode(D,true);C=true;var E=D;while ((E=E.lastChild)){if (E.previousSibling||E.nodeType!=1||FCKListsLib.InlineChildReqElements[E.nodeName.toLowerCase()]==null){C=false;break;}}};B.Release();if (A) this.Select();return C;},CreateBookmark:function(){var A={StartId:'fck_dom_range_start_'+(new Date()).valueOf()+'_'+Math.floor(Math.random()*1000),EndId:'fck_dom_range_end_'+(new Date()).valueOf()+'_'+Math.floor(Math.random()*1000)};var B=this.Window.document;var C;var D;if (!this.CheckIsCollapsed()){C=B.createElement('span');C.id=A.EndId;C.innerHTML='&nbsp;';D=this.Clone();D.Collapse(false);D.InsertNode(C);};C=B.createElement('span');C.id=A.StartId;C.innerHTML='&nbsp;';D=this.Clone();D.Collapse(true);D.InsertNode(C);return A;},MoveToBookmark:function(A,B){var C=this.Window.document;var D=C.getElementById(A.StartId);var E=C.getElementById(A.EndId);this.SetStart(D,3);if (!B) FCKDomTools.RemoveNode(D);if (E){this.SetEnd(E,3);if (!B) FCKDomTools.RemoveNode(E);}else this.Collapse(true);},SetStart:function(A,B){var C=this._Range;if (!C) C=this._Range=this.CreateRange();switch(B){case 1:C.setStart(A,0);break;case 2:C.setStart(A,A.childNodes.length);break;case 3:C.setStartBefore(A);break;case 4:C.setStartAfter(A);};this._UpdateElementInfo();},SetEnd:function(A,B){var C=this._Range;if (!C) C=this._Range=this.CreateRange();switch(B){case 1:C.setEnd(A,0);break;case 2:C.setEnd(A,A.childNodes.length);break;case 3:C.setEndBefore(A);break;case 4:C.setEndAfter(A);};this._UpdateElementInfo();},Expand:function(A){var B,oSibling;switch (A){case 'block_contents':if (this.StartBlock) this.SetStart(this.StartBlock,1);else{B=this._Range.startContainer;if (B.nodeType==1){if (!(B=B.childNodes[this._Range.startOffset])) B=B.firstChild;};if (!B) return;while (true){oSibling=B.previousSibling;if (!oSibling){if (B.parentNode!=this.StartBlockLimit) B=B.parentNode;else break;}else if (oSibling.nodeType!=1||!(/^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/).test(oSibling.nodeName.toUpperCase())){B=oSibling;}else break;};this._Range.setStartBefore(B);};if (this.EndBlock) this.SetEnd(this.EndBlock,2);else{B=this._Range.endContainer;if (B.nodeType==1) B=B.childNodes[this._Range.endOffset]||B.lastChild;if (!B) return;while (true){oSibling=B.nextSibling;if (!oSibling){if (B.parentNode!=this.EndBlockLimit) B=B.parentNode;else break;}else if (oSibling.nodeType!=1||!(/^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/).test(oSibling.nodeName.toUpperCase())){B=oSibling;}else break;};this._Range.setEndAfter(B);};this._UpdateElementInfo();}},Release:function(A){if (!A) this.Window=null;this.StartContainer=null;this.StartBlock=null;this.StartBlockLimit=null;this.EndContainer=null;this.EndBlock=null;this.EndBlockLimit=null;this._Range=null;}};
+FCKDomRange.prototype.MoveToSelection=function(){this.Release(true);this._Range=new FCKW3CRange(this.Window.document);var A=this.Window.document.selection;if (A.type!='Control'){B=this._GetSelectionMarkerTag(true);this._Range.setStart(B.parentNode,FCKDomTools.GetIndexOf(B));B.parentNode.removeChild(B);var B=this._GetSelectionMarkerTag(false);this._Range.setEnd(B.parentNode,FCKDomTools.GetIndexOf(B));B.parentNode.removeChild(B);this._UpdateElementInfo();}else{var C=A.createRange().item(0);if (C){this._Range.setStartBefore(C);this._Range.setEndAfter(C);this._UpdateElementInfo();}}};FCKDomRange.prototype.Select=function(){if (this._Range){var A=this.CheckIsCollapsed();var B=this._GetRangeMarkerTag(true);if (!A) var C=this._GetRangeMarkerTag(false);var D=this.Window.document.body.createTextRange();D.moveToElementText(B);D.moveStart('character',1);if (!A){var E=this.Window.document.body.createTextRange();E.moveToElementText(C);D.setEndPoint('EndToEnd',E);D.moveEnd('character',-1);};this._Range.setStartBefore(B);B.parentNode.removeChild(B);if (A){try{D.pasteHTML('&nbsp;');D.moveStart('character',-1);}catch (e){};D.select();D.pasteHTML('');}else{this._Range.setEndBefore(C);C.parentNode.removeChild(C);D.select();}}};FCKDomRange.prototype._GetSelectionMarkerTag=function(A){var B=this.Window.document.selection.createRange();B.collapse(A===true);var C='fck_dom_range_temp_'+(new Date()).valueOf()+'_'+Math.floor(Math.random()*1000);B.pasteHTML('<span id="'+C+'"></span>');return this.Window.document.getElementById(C);};FCKDomRange.prototype._GetRangeMarkerTag=function(A){var B=this._Range;if (!A){B=B.cloneRange();B.collapse(A===true);};var C=this.Window.document.createElement('span');C.innerHTML='&nbsp;';B.insertNode(C);return C;}
+var FCKDocumentFragment=function(A){this._Document=A;this.RootNode=A.createElement('div');};FCKDocumentFragment.prototype={AppendTo:function(A){FCKDomTools.MoveChildren(this.RootNode,A);},AppendHtml:function(A){var B=this._Document.createElement('div');B.innerHTML=A;FCKDomTools.MoveChildren(B,this.RootNode);},InsertAfterNode:function(A){var B=this.RootNode;var C;while((C=B.lastChild)) FCKDomTools.InsertAfterNode(A,B.removeChild(C));}};
+var FCKW3CRange=function(A){this._Document=A;this.startContainer=null;this.startOffset=null;this.endContainer=null;this.endOffset=null;this.collapsed=true;};FCKW3CRange.CreateRange=function(A){return new FCKW3CRange(A);};FCKW3CRange.CreateFromRange=function(A,B){var C=FCKW3CRange.CreateRange(A);C.setStart(B.startContainer,B.startOffset);C.setEnd(B.endContainer,B.endOffset);return C;};FCKW3CRange.prototype={_UpdateCollapsed:function(){this.collapsed=(this.startContainer==this.endContainer&&this.startOffset==this.endOffset);},setStart:function(A,B){this.startContainer=A;this.startOffset=B;if (!this.endContainer){this.endContainer=A;this.endOffset=B;};this._UpdateCollapsed();},setEnd:function(A,B){this.endContainer=A;this.endOffset=B;if (!this.startContainer){this.startContainer=A;this.startOffset=B;};this._UpdateCollapsed();},setStartAfter:function(A){this.setStart(A.parentNode,FCKDomTools.GetIndexOf(A)+1);},setStartBefore:function(A){this.setStart(A.parentNode,FCKDomTools.GetIndexOf(A));},setEndAfter:function(A){this.setEnd(A.parentNode,FCKDomTools.GetIndexOf(A)+1);},setEndBefore:function(A){this.setEnd(A.parentNode,FCKDomTools.GetIndexOf(A));},collapse:function(A){if (A){this.endContainer=this.startContainer;this.endOffset=this.startOffset;}else{this.startContainer=this.endContainer;this.startOffset=this.endOffset;};this.collapsed=true;},selectNodeContents:function(A){this.setStart(A,0);this.setEnd(A,A.nodeType==3?A.data.length:A.childNodes.length);},insertNode:function(A){var B=this.startContainer;var C=this.startOffset;if (B.nodeType==3){B.splitText(C);if (B==this.endContainer) this.setEnd(B.nextSibling,this.endOffset-this.startOffset);FCKDomTools.InsertAfterNode(B,A);return;}else{B.insertBefore(A,B.childNodes[C]||null);if (B==this.endContainer){this.endOffset++;this.collapsed=false;}}},deleteContents:function(){if (this.collapsed) return;this._ExecContentsAction(0);},extractContents:function(){var A=new FCKDocumentFragment(this._Document);if (!this.collapsed) this._ExecContentsAction(1,A);return A;},cloneContents:function(){var A=new FCKDocumentFragment(this._Document);if (!this.collapsed) this._ExecContentsAction(2,A);return A;},_ExecContentsAction:function(A,B){var C=this.startContainer;var D=this.endContainer;var E=this.startOffset;var F=this.endOffset;var G=false;var H=false;if (D.nodeType==3) D=D.splitText(F);else{if (D.childNodes.length>0){if (F>D.childNodes.length-1){D=FCKDomTools.InsertAfterNode(D.lastChild,this._Document.createTextNode(''));H=true;}else D=D.childNodes[F];}};if (C.nodeType==3){C.splitText(E);if (C==D) D=C.nextSibling;}else{if (C.childNodes.length>0&&E<=C.childNodes.length-1){if (E==0){C=C.insertBefore(this._Document.createTextNode(''),C.firstChild);G=true;}else C=C.childNodes[E].previousSibling;}};var I=FCKDomTools.GetParents(C);var J=FCKDomTools.GetParents(D);var i,topStart,topEnd;for (i=0;i<I.length;i++){topStart=I[i];topEnd=J[i];if (topStart!=topEnd) break;};var K,levelStartNode,levelClone,currentNode,currentSibling;if (B) K=B.RootNode;for (var j=i;j<I.length;j++){levelStartNode=I[j];if (K&&levelStartNode!=C) levelClone=K.appendChild(levelStartNode.cloneNode(levelStartNode==C));currentNode=levelStartNode.nextSibling;while(currentNode){if (currentNode==J[j]||currentNode==D) break;currentSibling=currentNode.nextSibling;if (A==2) K.appendChild(currentNode.cloneNode(true));else{currentNode.parentNode.removeChild(currentNode);if (A==1) K.appendChild(currentNode);};currentNode=currentSibling;};if (K) K=levelClone;};if (B) K=B.RootNode;for (var k=i;k<J.length;k++){levelStartNode=J[k];if (A>0&&levelStartNode!=D) levelClone=K.appendChild(levelStartNode.cloneNode(levelStartNode==D));if (!I[k]||levelStartNode.parentNode!=I[k].parentNode){currentNode=levelStartNode.previousSibling;while(currentNode){if (currentNode==I[k]||currentNode==C) break;currentSibling=currentNode.previousSibling;if (A==2) K.insertBefore(currentNode.cloneNode(true),K.firstChild);else{currentNode.parentNode.removeChild(currentNode);if (A==1) K.insertBefore(currentNode,K.firstChild);};currentNode=currentSibling;}};if (K) K=levelClone;};if (A==2){var L=this.startContainer;if (L.nodeType==3){L.data+=L.nextSibling.data;L.parentNode.removeChild(L.nextSibling);};var M=this.endContainer;if (M.nodeType==3&&M.nextSibling){M.data+=M.nextSibling.data;M.parentNode.removeChild(M.nextSibling);}}else{if (topStart&&topEnd&&(C.parentNode!=topStart.parentNode||D.parentNode!=topEnd.parentNode)) this.setStart(topEnd.parentNode,FCKDomTools.GetIndexOf(topEnd));this.collapse(true);};if(G) C.parentNode.removeChild(C);if(H&&D.parentNode) D.parentNode.removeChild(D);},cloneRange:function(){return FCKW3CRange.CreateFromRange(this._Document,this);},toString:function(){var A=this.cloneContents();var B=this._Document.createElement('div');A.AppendTo(B);return B.textContent||B.innerText;}};
+var FCKEnterKey=function(A,B,C){this.Window=A;this.EnterMode=B||'p';this.ShiftEnterMode=C||'br';var D=new FCKKeystrokeHandler(false);D._EnterKey=this;D.OnKeystroke=FCKEnterKey_OnKeystroke;D.SetKeystrokes([[13,'Enter'],[SHIFT+13,'ShiftEnter'],[8,'Backspace'],[46,'Delete']]);D.AttachToElement(A.document);};function FCKEnterKey_OnKeystroke(A,B){var C=this._EnterKey;try{switch (B){case 'Enter':return C.DoEnter();break;case 'ShiftEnter':return C.DoShiftEnter();break;case 'Backspace':return C.DoBackspace();break;case 'Delete':return C.DoDelete();}}catch (e){};return false;};FCKEnterKey.prototype.DoEnter=function(A,B){this._HasShift=(B===true);var C=A||this.EnterMode;if (C=='br') return this._ExecuteEnterBr();else return this._ExecuteEnterBlock(C);};FCKEnterKey.prototype.DoShiftEnter=function(){return this.DoEnter(this.ShiftEnterMode,true);};FCKEnterKey.prototype.DoBackspace=function(){var A=false;var B=new FCKDomRange(this.Window);B.MoveToSelection();if (!B.CheckIsCollapsed()) return false;var C=B.StartBlock;var D=B.EndBlock;if (B.StartBlockLimit==B.EndBlockLimit&&C&&D){if (!B.CheckIsCollapsed()){var E=B.CheckEndOfBlock();B.DeleteContents();if (C!=D){B.SetStart(D,1);B.SetEnd(D,1);};B.Select();A=(C==D);};if (B.CheckStartOfBlock()){var F=B.StartBlock;var G=FCKDomTools.GetPreviousSourceElement(F,true,['BODY',B.StartBlockLimit.nodeName],['UL','OL']);A=this._ExecuteBackspace(B,G,F);}else if (FCKBrowserInfo.IsGecko){B.Select();}};B.Release();return A;};FCKEnterKey.prototype._ExecuteBackspace=function(A,B,C){var D=false;if (!B&&C&&C.nodeName.IEquals('LI')&&C.parentNode.parentNode.nodeName.IEquals('LI')){this._OutdentWithSelection(C,A);return true;};if (B&&B.nodeName.IEquals('LI')){var E=FCKDomTools.GetLastChild(B,['UL','OL']);while (E){B=FCKDomTools.GetLastChild(E,'LI');E=FCKDomTools.GetLastChild(B,['UL','OL']);}};if (B&&C){if (C.nodeName.IEquals('LI')&&!B.nodeName.IEquals('LI')){this._OutdentWithSelection(C,A);return true;};var F=C.parentNode;var G=B.nodeName.toLowerCase();if (FCKListsLib.EmptyElements[G]!=null||G=='table'){FCKDomTools.RemoveNode(B);D=true;}else{FCKDomTools.RemoveNode(C);while (F.innerHTML.Trim().length==0){var H=F.parentNode;H.removeChild(F);F=H;};FCKDomTools.TrimNode(C);FCKDomTools.TrimNode(B);A.SetStart(B,2);A.Collapse(true);var I=A.CreateBookmark();FCKDomTools.MoveChildren(C,B);A.MoveToBookmark(I);A.Select();D=true;}};return D;};FCKEnterKey.prototype.DoDelete=function(){var A=false;var B=new FCKDomRange(this.Window);B.MoveToSelection();if (B.CheckIsCollapsed()&&B.CheckEndOfBlock(FCKBrowserInfo.IsGeckoLike)){var C=B.StartBlock;var D=FCKDomTools.GetNextSourceElement(C,true,[B.StartBlockLimit.nodeName],['UL','OL']);A=this._ExecuteBackspace(B,C,D);};B.Release();return A;};FCKEnterKey.prototype._ExecuteEnterBlock=function(A,B){var C=B||new FCKDomRange(this.Window);if (!B) C.MoveToSelection();if (C.StartBlockLimit==C.EndBlockLimit){if (!C.StartBlock) this._FixBlock(C,true,A);if (!C.EndBlock) this._FixBlock(C,false,A);var D=C.StartBlock;var E=C.EndBlock;if (!C.CheckIsEmpty()) C.DeleteContents();if (D==E){var F;var G=C.CheckStartOfBlock();var H=C.CheckEndOfBlock();if (G&&!H){F=D.cloneNode(false);if (FCKBrowserInfo.IsGeckoLike) F.innerHTML=GECKO_BOGUS;D.parentNode.insertBefore(F,D);if (FCKBrowserInfo.IsIE){C.MoveToNodeContents(F);C.Select();};C.MoveToElementEditStart(D);}else{if (H){var I=D.tagName.toUpperCase();if (G&&I=='LI'){this._OutdentWithSelection(D,C);C.Release();return true;}else{if ((/^H[1-6]$/).test(I)||this._HasShift) F=this.Window.document.createElement(A);else{F=D.cloneNode(false);this._RecreateEndingTree(D,F);};if (FCKBrowserInfo.IsGeckoLike){F.innerHTML=GECKO_BOGUS;if (G) D.innerHTML=GECKO_BOGUS;}}}else{C.SetEnd(D,2);var J=C.ExtractContents();F=D.cloneNode(false);FCKDomTools.TrimNode(J.RootNode);if (J.RootNode.firstChild.nodeType==1&&J.RootNode.firstChild.tagName.toUpperCase().Equals('UL','OL')) F.innerHTML=GECKO_BOGUS;J.AppendTo(F);if (FCKBrowserInfo.IsGecko){this._AppendBogusBr(D);this._AppendBogusBr(F);}};if (F){FCKDomTools.InsertAfterNode(D,F);C.MoveToElementEditStart(F);if (FCKBrowserInfo.IsGeckoLike) F.scrollIntoView(false);}}}else{C.MoveToElementEditStart(E);};C.Select();};C.Release();return true;};FCKEnterKey.prototype._ExecuteEnterBr=function(A){var B=new FCKDomRange(this.Window);B.MoveToSelection();if (B.StartBlockLimit==B.EndBlockLimit){B.DeleteContents();B.MoveToSelection();var C=B.CheckStartOfBlock();var D=B.CheckEndOfBlock();var E=B.StartBlock?B.StartBlock.tagName.toUpperCase():'';var F=this._HasShift;if (!F&&E=='LI') return this._ExecuteEnterBlock(null,B);if (!F&&D&&(/^H[1-6]$/).test(E)){FCKDebug.Output('BR - Header');FCKDomTools.InsertAfterNode(B.StartBlock,this.Window.document.createElement('br'));if (FCKBrowserInfo.IsGecko) FCKDomTools.InsertAfterNode(B.StartBlock,this.Window.document.createTextNode(''));B.SetStart(B.StartBlock.nextSibling,FCKBrowserInfo.IsIE?3:1);}else{FCKDebug.Output('BR - No Header');var G=this.Window.document.createElement('br');B.InsertNode(G);if (FCKBrowserInfo.IsGecko) FCKDomTools.InsertAfterNode(G,this.Window.document.createTextNode(''));if (D&&FCKBrowserInfo.IsGeckoLike) this._AppendBogusBr(G.parentNode);if (FCKBrowserInfo.IsIE) B.SetStart(G,4);else B.SetStart(G.nextSibling,1);};B.Collapse(true);B.Select();};B.Release();return true;};FCKEnterKey.prototype._FixBlock=function(A,B,C){var D=A.CreateBookmark();A.Collapse(B);A.Expand('block_contents');var E=this.Window.document.createElement(C);A.ExtractContents().AppendTo(E);FCKDomTools.TrimNode(E);A.InsertNode(E);A.MoveToBookmark(D);};FCKEnterKey.prototype._AppendBogusBr=function(A){if (!A) return;var B=FCKTools.GetLastItem(A.getElementsByTagName('br'));if (!B||B.getAttribute('type',2)!='_moz') A.appendChild(FCKTools.CreateBogusBR(this.Window.document));};FCKEnterKey.prototype._RecreateEndingTree=function(A,B){while ((A=A.lastChild)&&A.nodeType==1&&FCKListsLib.InlineChildReqElements[A.nodeName.toLowerCase()]!=null) B=B.insertBefore(A.cloneNode(false),B.firstChild);};FCKEnterKey.prototype._OutdentWithSelection=function(A,B){var C=B.CreateBookmark();FCKListHandler.OutdentListItem(A);B.MoveToBookmark(C);B.Select();}
+var FCKDocumentProcessor={};FCKDocumentProcessor._Items=[];FCKDocumentProcessor.AppendNew=function(){var A={};this._Items.AddItem(A);return A;};FCKDocumentProcessor.Process=function(A){var B,i=0;while((B=this._Items[i++])) B.ProcessDocument(A);};var FCKDocumentProcessor_CreateFakeImage=function(A,B){var C=FCK.EditorDocument.createElement('IMG');C.className=A;C.src=FCKConfig.FullBasePath+'images/spacer.gif';C.setAttribute('_fckfakelement','true',0);C.setAttribute('_fckrealelement',FCKTempBin.AddElement(B),0);return C;};if (FCKBrowserInfo.IsIE||FCKBrowserInfo.IsOpera){var FCKAnchorsProcessor=FCKDocumentProcessor.AppendNew();FCKAnchorsProcessor.ProcessDocument=function(A){var B=A.getElementsByTagName('A');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.name.length>0){if (C.innerHTML!==''){if (FCKBrowserInfo.IsIE) C.className+=' FCK__AnchorC';}else{var D=FCKDocumentProcessor_CreateFakeImage('FCK__Anchor',C.cloneNode(true));D.setAttribute('_fckanchor','true',0);C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}}}};var FCKPageBreaksProcessor=FCKDocumentProcessor.AppendNew();FCKPageBreaksProcessor.ProcessDocument=function(A){var B=A.getElementsByTagName('DIV');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.style.pageBreakAfter=='always'&&C.childNodes.length==1&&C.childNodes[0].style&&C.childNodes[0].style.display=='none'){var D=FCKDocumentProcessor_CreateFakeImage('FCK__PageBreak',C.cloneNode(true));C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}};var FCKFlashProcessor=FCKDocumentProcessor.AppendNew();FCKFlashProcessor.ProcessDocument=function(A){var B=A.getElementsByTagName('EMBED');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){var D=C.attributes['type'];if ((C.src&&C.src.EndsWith('.swf',true))||(D&&D.nodeValue=='application/x-shockwave-flash')){var E=C.cloneNode(true);if (FCKBrowserInfo.IsIE){var F=['scale','play','loop','menu','wmode','quality'];for (var G=0;G<F.length;G++){var H=C.getAttribute(F[G]);if (H) E.setAttribute(F[G],H);};E.setAttribute('type',D.nodeValue);};var I=FCKDocumentProcessor_CreateFakeImage('FCK__Flash',E);I.setAttribute('_fckflash','true',0);FCKFlashProcessor.RefreshView(I,C);C.parentNode.insertBefore(I,C);C.parentNode.removeChild(C);}}};FCKFlashProcessor.RefreshView=function(A,B){if (B.getAttribute('width')>0) A.style.width=FCKTools.ConvertHtmlSizeToStyle(B.getAttribute('width'));if (B.getAttribute('height')>0) A.style.height=FCKTools.ConvertHtmlSizeToStyle(B.getAttribute('height'));};FCK.GetRealElement=function(A){var e=FCKTempBin.Elements[A.getAttribute('_fckrealelement')];if (A.getAttribute('_fckflash')){if (A.style.width.length>0) e.width=FCKTools.ConvertStyleSizeToHtml(A.style.width);if (A.style.height.length>0) e.height=FCKTools.ConvertStyleSizeToHtml(A.style.height);};return e;};if (FCKBrowserInfo.IsIE){FCKDocumentProcessor.AppendNew().ProcessDocument=function(A){var B=A.getElementsByTagName('HR');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){var D=A.createElement('hr');D.mergeAttributes(C,true);FCKDomTools.InsertAfterNode(C,D);C.parentNode.removeChild(C);}}};FCKDocumentProcessor.AppendNew().ProcessDocument=function(A){var B=A.getElementsByTagName('INPUT');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.type=='hidden'){var D=FCKDocumentProcessor_CreateFakeImage('FCK__InputHidden',C.cloneNode(true));D.setAttribute('_fckinputhidden','true',0);C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}}
+var FCKSelection=FCK.Selection={};
+FCKSelection.GetType=function(){return FCK.EditorDocument.selection.type;};FCKSelection.GetSelectedElement=function(){if (this.GetType()=='Control'){var A=FCK.EditorDocument.selection.createRange();if (A&&A.item) return FCK.EditorDocument.selection.createRange().item(0);};return null;};FCKSelection.GetParentElement=function(){switch (this.GetType()){case 'Control':return FCKSelection.GetSelectedElement().parentElement;case 'None':return null;default:return FCK.EditorDocument.selection.createRange().parentElement();}};FCKSelection.SelectNode=function(A){FCK.Focus();FCK.EditorDocument.selection.empty();var B;try{B=FCK.EditorDocument.body.createControlRange();B.addElement(A);}catch(e){B=FCK.EditorDocument.body.createTextRange();B.moveToElementText(A);};B.select();};FCKSelection.Collapse=function(A){FCK.Focus();if (this.GetType()=='Text'){var B=FCK.EditorDocument.selection.createRange();B.collapse(A==null||A===true);B.select();}};FCKSelection.HasAncestorNode=function(A){var B;if (FCK.EditorDocument.selection.type=="Control"){B=this.GetSelectedElement();}else{var C=FCK.EditorDocument.selection.createRange();B=C.parentElement();};while (B){if (B.tagName==A) return true;B=B.parentNode;};return false;};FCKSelection.MoveToAncestorNode=function(A){var B,oRange;if (!FCK.EditorDocument) return null;if (FCK.EditorDocument.selection.type=="Control"){oRange=FCK.EditorDocument.selection.createRange();for (i=0;i<oRange.length;i++){if (oRange(i).parentNode){B=oRange(i).parentNode;break;}}}else{oRange=FCK.EditorDocument.selection.createRange();B=oRange.parentElement();};while (B&&B.nodeName!=A) B=B.parentNode;return B;};FCKSelection.Delete=function(){var A=FCK.EditorDocument.selection;if (A.type.toLowerCase()!="none"){A.clear();};return A;};
+var FCKTableHandler={};FCKTableHandler.InsertRow=function(){var A=FCKSelection.MoveToAncestorNode('TR');if (!A) return;var B=A.cloneNode(true);A.parentNode.insertBefore(B,A);FCKTableHandler.ClearRow(A);};FCKTableHandler.DeleteRows=function(A){if (!A) A=FCKSelection.MoveToAncestorNode('TR');if (!A) return;var B=FCKTools.GetElementAscensor(A,'TABLE');if (B.rows.length==1){FCKTableHandler.DeleteTable(B);return;};A.parentNode.removeChild(A);};FCKTableHandler.DeleteTable=function(A){if (!A){A=FCKSelection.GetSelectedElement();if (!A||A.tagName!='TABLE') A=FCKSelection.MoveToAncestorNode('TABLE');};if (!A) return;FCKSelection.SelectNode(A);FCKSelection.Collapse();A.parentNode.removeChild(A);};FCKTableHandler.InsertColumn=function(){var A=FCKSelection.MoveToAncestorNode('TD')||FCKSelection.MoveToAncestorNode('TH');if (!A) return;var B=FCKTools.GetElementAscensor(A,'TABLE');var C=A.cellIndex+1;for (var i=0;i<B.rows.length;i++){var D=B.rows[i];if (D.cells.length<C) continue;A=D.cells[C-1].cloneNode(false);if (FCKBrowserInfo.IsGecko) A.innerHTML=GECKO_BOGUS;var E=D.cells[C];if (E) D.insertBefore(A,E);else D.appendChild(A);}};FCKTableHandler.DeleteColumns=function(){var A=FCKSelection.MoveToAncestorNode('TD')||FCKSelection.MoveToAncestorNode('TH');if (!A) return;var B=FCKTools.GetElementAscensor(A,'TABLE');var C=A.cellIndex;for (var i=B.rows.length-1;i>=0;i--){var D=B.rows[i];if (C==0&&D.cells.length==1){FCKTableHandler.DeleteRows(D);continue;};if (D.cells[C]) D.removeChild(D.cells[C]);}};FCKTableHandler.InsertCell=function(A){var B=A?A:FCKSelection.MoveToAncestorNode('TD');if (!B) return null;var C=FCK.EditorDocument.createElement('TD');if (FCKBrowserInfo.IsGecko) C.innerHTML=GECKO_BOGUS;if (B.cellIndex==B.parentNode.cells.length-1){B.parentNode.appendChild(C);}else{B.parentNode.insertBefore(C,B.nextSibling);};return C;};FCKTableHandler.DeleteCell=function(A){if (A.parentNode.cells.length==1){FCKTableHandler.DeleteRows(FCKTools.GetElementAscensor(A,'TR'));return;};A.parentNode.removeChild(A);};FCKTableHandler.DeleteCells=function(){var A=FCKTableHandler.GetSelectedCells();for (var i=A.length-1;i>=0;i--){FCKTableHandler.DeleteCell(A[i]);}};FCKTableHandler.MergeCells=function(){var A=FCKTableHandler.GetSelectedCells();if (A.length<2) return;if (A[0].parentNode!=A[A.length-1].parentNode) return;var B=isNaN(A[0].colSpan)?1:A[0].colSpan;var C='';var D=FCK.EditorDocument.createDocumentFragment();for (var i=A.length-1;i>=0;i--){var E=A[i];for (var c=E.childNodes.length-1;c>=0;c--){var F=E.removeChild(E.childNodes[c]);if ((F.hasAttribute&&F.hasAttribute('_moz_editor_bogus_node'))||(F.getAttribute&&F.getAttribute('type',2)=='_moz')) continue;D.insertBefore(F,D.firstChild);};if (i>0){B+=isNaN(E.colSpan)?1:E.colSpan;FCKTableHandler.DeleteCell(E);}};A[0].colSpan=B;if (FCKBrowserInfo.IsGecko&&D.childNodes.length==0) A[0].innerHTML=GECKO_BOGUS;else A[0].appendChild(D);};FCKTableHandler.SplitCell=function(){var A=FCKTableHandler.GetSelectedCells();if (A.length!=1) return;var B=this._CreateTableMap(A[0].parentNode.parentNode);var C=FCKTableHandler._GetCellIndexSpan(B,A[0].parentNode.rowIndex,A[0]);var D=this._GetCollumnCells(B,C);for (var i=0;i<D.length;i++){if (D[i]==A[0]){var E=this.InsertCell(A[0]);if (!isNaN(A[0].rowSpan)&&A[0].rowSpan>1) E.rowSpan=A[0].rowSpan;}else{if (isNaN(D[i].colSpan)) D[i].colSpan=2;else D[i].colSpan+=1;}}};FCKTableHandler._GetCellIndexSpan=function(A,B,C){if (A.length<B+1) return null;var D=A[B];for (var c=0;c<D.length;c++){if (D[c]==C) return c;};return null;};FCKTableHandler._GetCollumnCells=function(A,B){var C=[];for (var r=0;r<A.length;r++){var D=A[r][B];if (D&&(C.length==0||C[C.length-1]!=D)) C[C.length]=D;};return C;};FCKTableHandler._CreateTableMap=function(A){var B=A.rows;var r=-1;var C=[];for (var i=0;i<B.length;i++){r++;if (!C[r]) C[r]=[];var c=-1;for (var j=0;j<B[i].cells.length;j++){var D=B[i].cells[j];c++;while (C[r][c]) c++;var E=isNaN(D.colSpan)?1:D.colSpan;var F=isNaN(D.rowSpan)?1:D.rowSpan;for (var G=0;G<F;G++){if (!C[r+G]) C[r+G]=[];for (var H=0;H<E;H++){C[r+G][c+H]=B[i].cells[j];}};c+=E-1;}};return C;};FCKTableHandler.ClearRow=function(A){var B=A.cells;for (var i=0;i<B.length;i++){if (FCKBrowserInfo.IsGecko) B[i].innerHTML=GECKO_BOGUS;else B[i].innerHTML='';}};
+FCKTableHandler.GetSelectedCells=function(){var A=[];var B=FCK.EditorDocument.selection.createRange();var C=FCKSelection.GetParentElement();if (C&&C.tagName.Equals('TD','TH')) A[0]=C;else{C=FCKSelection.MoveToAncestorNode('TABLE');if (C){for (var i=0;i<C.cells.length;i++){var D=FCK.EditorDocument.selection.createRange();D.moveToElementText(C.cells[i]);if (B.inRange(D)||(B.compareEndPoints('StartToStart',D)>=0&&B.compareEndPoints('StartToEnd',D)<=0)||(B.compareEndPoints('EndToStart',D)>=0&&B.compareEndPoints('EndToEnd',D)<=0)){A[A.length]=C.cells[i];}}}};return A;};
+var FCKXml=function(){this.Error=false;};FCKXml.prototype.LoadUrl=function(A){this.Error=false;var B=FCKTools.CreateXmlObject('XmlHttp');if (!B){this.Error=true;return;};B.open("GET",A,false);B.send(null);if (B.status==200||B.status==304) this.DOMDocument=B.responseXML;else if (B.status==0&&B.readyState==4){this.DOMDocument=FCKTools.CreateXmlObject('DOMDocument');this.DOMDocument.async=false;this.DOMDocument.resolveExternals=false;this.DOMDocument.loadXML(B.responseText);}else{this.DOMDocument=null;};if (this.DOMDocument==null||this.DOMDocument.firstChild==null){this.Error=true;if (window.confirm('Error loading "'+A+'"\r\nDo you want to see more info?')) alert('URL requested: "'+A+'"\r\nServer response:\r\nStatus: '+B.status+'\r\nResponse text:\r\n'+B.responseText);}};FCKXml.prototype.SelectNodes=function(A,B){if (this.Error) return [];if (B) return B.selectNodes(A);else return this.DOMDocument.selectNodes(A);};FCKXml.prototype.SelectSingleNode=function(A,B){if (this.Error) return null;if (B) return B.selectSingleNode(A);else return this.DOMDocument.selectSingleNode(A);}
+var FCKStyleDef=function(A,B){this.Name=A;this.Element=B.toUpperCase();this.IsObjectElement=FCKRegexLib.ObjectElements.test(this.Element);this.Attributes={};};FCKStyleDef.prototype.AddAttribute=function(A,B){this.Attributes[A]=B;};FCKStyleDef.prototype.GetOpenerTag=function(){var s='<'+this.Element;for (var a in this.Attributes) s+=' '+a+'="'+this.Attributes[a]+'"';return s+'>';};FCKStyleDef.prototype.GetCloserTag=function(){return '</'+this.Element+'>';};FCKStyleDef.prototype.RemoveFromSelection=function(){if (FCKSelection.GetType()=='Control') this._RemoveMe(FCK.ToolbarSet.CurrentInstance.Selection.GetSelectedElement());else this._RemoveMe(FCK.ToolbarSet.CurrentInstance.Selection.GetParentElement());}
+FCKStyleDef.prototype.ApplyToSelection=function(){var A=FCK.ToolbarSet.CurrentInstance.EditorDocument.selection;if (A.type=='Text'){var B=A.createRange();var e=document.createElement(this.Element);e.innerHTML=B.htmlText;this._AddAttributes(e);this._RemoveDuplicates(e);B.pasteHTML(e.outerHTML);}else if (A.type=='Control'){var C=FCK.ToolbarSet.CurrentInstance.Selection.GetSelectedElement();if (C.tagName==this.Element) this._AddAttributes(C);}};FCKStyleDef.prototype._AddAttributes=function(A){for (var a in this.Attributes){switch (a.toLowerCase()){case 'style':A.style.cssText=this.Attributes[a];break;case 'class':A.setAttribute('className',this.Attributes[a],0);break;case 'src':A.setAttribute('_fcksavedurl',this.Attributes[a],0);default:A.setAttribute(a,this.Attributes[a],0);}}};FCKStyleDef.prototype._RemoveDuplicates=function(A){for (var i=0;i<A.children.length;i++){var B=A.children[i];this._RemoveDuplicates(B);if (this.IsEqual(B)) FCKTools.RemoveOuterTags(B);}};FCKStyleDef.prototype.IsEqual=function(e){if (e.tagName!=this.Element) return false;for (var a in this.Attributes){switch (a.toLowerCase()){case 'style':if (e.style.cssText.toLowerCase()!=this.Attributes[a].toLowerCase()) return false;break;case 'class':if (e.getAttribute('className',0)!=this.Attributes[a]) return false;break;default:if (e.getAttribute(a,0)!=this.Attributes[a]) return false;}};return true;};FCKStyleDef.prototype._RemoveMe=function(A){if (!A) return;var B=A.parentElement;if (this.IsEqual(A)){if (this.IsObjectElement){for (var a in this.Attributes){switch (a.toLowerCase()){case 'class':A.removeAttribute('className',0);break;default:A.removeAttribute(a,0);}};return;}else FCKTools.RemoveOuterTags(A);};this._RemoveMe(B);}
+var FCKStylesLoader=function(){this.Styles={};this.StyleGroups={};this.Loaded=false;this.HasObjectElements=false;};FCKStylesLoader.prototype.Load=function(A){var B=new FCKXml();B.LoadUrl(A);var C=B.SelectNodes('Styles/Style');for (var i=0;i<C.length;i++){var D=C[i].attributes.getNamedItem('element').value.toUpperCase();var E=new FCKStyleDef(C[i].attributes.getNamedItem('name').value,D);if (E.IsObjectElement) this.HasObjectElements=true;var F=B.SelectNodes('Attribute',C[i]);for (var j=0;j<F.length;j++){var G=F[j].attributes.getNamedItem('name').value;var H=F[j].attributes.getNamedItem('value').value;if (G.toLowerCase()=='style'){var I=document.createElement('SPAN');I.style.cssText=H;H=I.style.cssText;};E.AddAttribute(G,H);};this.Styles[E.Name]=E;var J=this.StyleGroups[D];if (J==null){this.StyleGroups[D]=[];J=this.StyleGroups[D];};J[J.length]=E;};this.Loaded=true;}
+var FCKNamedCommand=function(A){this.Name=A;};FCKNamedCommand.prototype.Execute=function(){FCK.ExecuteNamedCommand(this.Name);};FCKNamedCommand.prototype.GetState=function(){return FCK.GetNamedCommandState(this.Name);};
+var FCKDialogCommand=function(A,B,C,D,E,F,G){this.Name=A;this.Title=B;this.Url=C;this.Width=D;this.Height=E;this.GetStateFunction=F;this.GetStateParam=G;this.Resizable=false;};FCKDialogCommand.prototype.Execute=function(){FCKDialog.OpenDialog('FCKDialog_'+this.Name,this.Title,this.Url,this.Width,this.Height,null,null,this.Resizable);};FCKDialogCommand.prototype.GetState=function(){if (this.GetStateFunction) return this.GetStateFunction(this.GetStateParam);else return 0;};var FCKUndefinedCommand=function(){this.Name='Undefined';};FCKUndefinedCommand.prototype.Execute=function(){alert(FCKLang.NotImplemented);};FCKUndefinedCommand.prototype.GetState=function(){return 0;};var FCKFontNameCommand=function(){this.Name='FontName';};FCKFontNameCommand.prototype.Execute=function(A){if (A==null||A==""){}else FCK.ExecuteNamedCommand('FontName',A);};FCKFontNameCommand.prototype.GetState=function(){return FCK.GetNamedCommandValue('FontName');};var FCKFontSizeCommand=function(){this.Name='FontSize';};FCKFontSizeCommand.prototype.Execute=function(A){if (typeof(A)=='string') A=parseInt(A,10);if (A==null||A==''){FCK.ExecuteNamedCommand('FontSize',3);}else FCK.ExecuteNamedCommand('FontSize',A);};FCKFontSizeCommand.prototype.GetState=function(){return FCK.GetNamedCommandValue('FontSize');};var FCKFormatBlockCommand=function(){this.Name='FormatBlock';};FCKFormatBlockCommand.prototype.Execute=function(A){if (A==null||A=='') FCK.ExecuteNamedCommand('FormatBlock','<P>');else if (A=='div'&&FCKBrowserInfo.IsGecko) FCK.ExecuteNamedCommand('FormatBlock','div');else FCK.ExecuteNamedCommand('FormatBlock','<'+A+'>');};FCKFormatBlockCommand.prototype.GetState=function(){return FCK.GetNamedCommandValue('FormatBlock');};var FCKPreviewCommand=function(){this.Name='Preview';};FCKPreviewCommand.prototype.Execute=function(){FCK.Preview();};FCKPreviewCommand.prototype.GetState=function(){return 0;};var FCKSaveCommand=function(){this.Name='Save';};FCKSaveCommand.prototype.Execute=function(){var A=FCK.GetParentForm();if (typeof(A.onsubmit)=='function'){var B=A.onsubmit();if (B!=null&&B===false) return;};if (typeof(A.submit)=='function') A.submit();else A.submit.click();};FCKSaveCommand.prototype.GetState=function(){return 0;};var FCKNewPageCommand=function(){this.Name='NewPage';};FCKNewPageCommand.prototype.Execute=function(){FCKUndo.SaveUndoStep();FCK.SetHTML('');FCKUndo.Typing=true;};FCKNewPageCommand.prototype.GetState=function(){return 0;};var FCKSourceCommand=function(){this.Name='Source';};FCKSourceCommand.prototype.Execute=function(){if (FCKConfig.SourcePopup){var A=FCKConfig.ScreenWidth*0.65;var B=FCKConfig.ScreenHeight*0.65;FCKDialog.OpenDialog('FCKDialog_Source',FCKLang.Source,'dialog/fck_source.html',A,B,null,null,true);}else FCK.SwitchEditMode();};FCKSourceCommand.prototype.GetState=function(){return (FCK.EditMode==0?0:1);};var FCKUndoCommand=function(){this.Name='Undo';};FCKUndoCommand.prototype.Execute=function(){if (FCKBrowserInfo.IsIE) FCKUndo.Undo();else FCK.ExecuteNamedCommand('Undo');};FCKUndoCommand.prototype.GetState=function(){if (FCKBrowserInfo.IsIE) return (FCKUndo.CheckUndoState()?0:-1);else return FCK.GetNamedCommandState('Undo');};var FCKRedoCommand=function(){this.Name='Redo';};FCKRedoCommand.prototype.Execute=function(){if (FCKBrowserInfo.IsIE) FCKUndo.Redo();else FCK.ExecuteNamedCommand('Redo');};FCKRedoCommand.prototype.GetState=function(){if (FCKBrowserInfo.IsIE) return (FCKUndo.CheckRedoState()?0:-1);else return FCK.GetNamedCommandState('Redo');};var FCKPageBreakCommand=function(){this.Name='PageBreak';};FCKPageBreakCommand.prototype.Execute=function(){var e=FCK.EditorDocument.createElement('DIV');e.style.pageBreakAfter='always';e.innerHTML='<span style="DISPLAY:none">&nbsp;</span>';var A=FCKDocumentProcessor_CreateFakeImage('FCK__PageBreak',e);A=FCK.InsertElement(A);};FCKPageBreakCommand.prototype.GetState=function(){return 0;};var FCKUnlinkCommand=function(){this.Name='Unlink';};FCKUnlinkCommand.prototype.Execute=function(){if (FCKBrowserInfo.IsGecko){var A=FCK.Selection.MoveToAncestorNode('A');if (A) FCKTools.RemoveOuterTags(A);return;};FCK.ExecuteNamedCommand(this.Name);};FCKUnlinkCommand.prototype.GetState=function(){var A=FCK.GetNamedCommandState(this.Name);if (A==0&&FCK.EditMode==0){var B=FCKSelection.MoveToAncestorNode('A');var C=(B&&B.name.length>0&&B.href.length==0);if (C) A=-1;};return A;};var FCKSelectAllCommand=function(){this.Name='SelectAll';};FCKSelectAllCommand.prototype.Execute=function(){if (FCK.EditMode==0){FCK.ExecuteNamedCommand('SelectAll');}else{var A=FCK.EditingArea.Textarea;if (FCKBrowserInfo.IsIE){A.createTextRange().execCommand('SelectAll');}else{A.selectionStart=0;A.selectionEnd=A.value.length;};A.focus();}};FCKSelectAllCommand.prototype.GetState=function(){return 0;};var FCKPasteCommand=function(){this.Name='Paste';};FCKPasteCommand.prototype={Execute:function(){if (FCKBrowserInfo.IsIE) FCK.Paste();else FCK.ExecuteNamedCommand('Paste');},GetState:function(){return FCK.GetNamedCommandState('Paste');}};
+var FCKSpellCheckCommand=function(){this.Name='SpellCheck';this.IsEnabled=(FCKConfig.SpellChecker=='ieSpell'||FCKConfig.SpellChecker=='SpellerPages');};FCKSpellCheckCommand.prototype.Execute=function(){switch (FCKConfig.SpellChecker){case 'ieSpell':this._RunIeSpell();break;case 'SpellerPages':FCKDialog.OpenDialog('FCKDialog_SpellCheck','Spell Check','dialog/fck_spellerpages.html',440,480);break;}};FCKSpellCheckCommand.prototype._RunIeSpell=function(){try{var A=new ActiveXObject("ieSpell.ieSpellExtension");A.CheckAllLinkedDocuments(FCK.EditorDocument);}catch(e){if(e.number==-2146827859){if (confirm(FCKLang.IeSpellDownload)) window.open(FCKConfig.IeSpellDownloadUrl,'IeSpellDownload');}else alert('Error Loading ieSpell: '+e.message+' ('+e.number+')');}};FCKSpellCheckCommand.prototype.GetState=function(){return this.IsEnabled?0:-1;}
+var FCKTextColorCommand=function(A){this.Name=A=='ForeColor'?'TextColor':'BGColor';this.Type=A;var B;if (FCKBrowserInfo.IsIE) B=window;else if (FCK.ToolbarSet._IFrame) B=FCKTools.GetElementWindow(FCK.ToolbarSet._IFrame);else B=window.parent;this._Panel=new FCKPanel(B);this._Panel.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');this._Panel.MainNode.className='FCK_Panel';this._CreatePanelBody(this._Panel.Document,this._Panel.MainNode);FCKTools.DisableSelection(this._Panel.Document.body);};FCKTextColorCommand.prototype.Execute=function(A,B,C){FCK._ActiveColorPanelType=this.Type;this._Panel.Show(A,B,C);};FCKTextColorCommand.prototype.SetColor=function(A){if (FCK._ActiveColorPanelType=='ForeColor') FCK.ExecuteNamedCommand('ForeColor',A);else if (FCKBrowserInfo.IsGeckoLike){if (FCKBrowserInfo.IsGecko&&!FCKConfig.GeckoUseSPAN) FCK.EditorDocument.execCommand('useCSS',false,false);FCK.ExecuteNamedCommand('hilitecolor',A);if (FCKBrowserInfo.IsGecko&&!FCKConfig.GeckoUseSPAN) FCK.EditorDocument.execCommand('useCSS',false,true);}else FCK.ExecuteNamedCommand('BackColor',A);delete FCK._ActiveColorPanelType;};FCKTextColorCommand.prototype.GetState=function(){return 0;};function FCKTextColorCommand_OnMouseOver() { this.className='ColorSelected';};function FCKTextColorCommand_OnMouseOut() { this.className='ColorDeselected';};function FCKTextColorCommand_OnClick(){this.className='ColorDeselected';this.Command.SetColor('#'+this.Color);this.Command._Panel.Hide();};function FCKTextColorCommand_AutoOnClick(){this.className='ColorDeselected';this.Command.SetColor('');this.Command._Panel.Hide();};function FCKTextColorCommand_MoreOnClick(){this.className='ColorDeselected';this.Command._Panel.Hide();FCKDialog.OpenDialog('FCKDialog_Color',FCKLang.DlgColorTitle,'dialog/fck_colorselector.html',400,330,this.Command.SetColor);};FCKTextColorCommand.prototype._CreatePanelBody=function(A,B){function CreateSelectionDiv(){var C=A.createElement("DIV");C.className='ColorDeselected';C.onmouseover=FCKTextColorCommand_OnMouseOver;C.onmouseout=FCKTextColorCommand_OnMouseOut;return C;};var D=B.appendChild(A.createElement("TABLE"));D.className='ForceBaseFont';D.style.tableLayout='fixed';D.cellPadding=0;D.cellSpacing=0;D.border=0;D.width=150;var E=D.insertRow(-1).insertCell(-1);E.colSpan=8;var C=E.appendChild(CreateSelectionDiv());C.innerHTML='<table cellspacing="0" cellpadding="0" width="100%" border="0">\n <tr>\n <td><div class="ColorBoxBorder"><div class="ColorBox" style="background-color: #000000"></div></div></td>\n <td nowrap width="100%" align="center">'+FCKLang.ColorAutomatic+'</td>\n </tr>\n </table>';C.Command=this;C.onclick=FCKTextColorCommand_AutoOnClick;var G=FCKConfig.FontColors.toString().split(',');var H=0;while (H<G.length){var I=D.insertRow(-1);for (var i=0;i<8&&H<G.length;i++,H++){C=I.insertCell(-1).appendChild(CreateSelectionDiv());C.Color=G[H];C.innerHTML='<div class="ColorBoxBorder"><div class="ColorBox" style="background-color: #'+G[H]+'"></div></div>';C.Command=this;C.onclick=FCKTextColorCommand_OnClick;}};E=D.insertRow(-1).insertCell(-1);E.colSpan=8;C=E.appendChild(CreateSelectionDiv());C.innerHTML='<table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td nowrap align="center">'+FCKLang.ColorMoreColors+'</td></tr></table>';C.Command=this;C.onclick=FCKTextColorCommand_MoreOnClick;}
+var FCKPastePlainTextCommand=function(){this.Name='PasteText';};FCKPastePlainTextCommand.prototype.Execute=function(){FCK.PasteAsPlainText();};FCKPastePlainTextCommand.prototype.GetState=function(){return FCK.GetNamedCommandState('Paste');};
+var FCKPasteWordCommand=function(){this.Name='PasteWord';};FCKPasteWordCommand.prototype.Execute=function(){FCK.PasteFromWord();};FCKPasteWordCommand.prototype.GetState=function(){if (FCKConfig.ForcePasteAsPlainText) return -1;else return FCK.GetNamedCommandState('Paste');};
+var FCKTableCommand=function(A){this.Name=A;};FCKTableCommand.prototype.Execute=function(){FCKUndo.SaveUndoStep();switch (this.Name){case 'TableInsertRow':FCKTableHandler.InsertRow();break;case 'TableDeleteRows':FCKTableHandler.DeleteRows();break;case 'TableInsertColumn':FCKTableHandler.InsertColumn();break;case 'TableDeleteColumns':FCKTableHandler.DeleteColumns();break;case 'TableInsertCell':FCKTableHandler.InsertCell();break;case 'TableDeleteCells':FCKTableHandler.DeleteCells();break;case 'TableMergeCells':FCKTableHandler.MergeCells();break;case 'TableSplitCell':FCKTableHandler.SplitCell();break;case 'TableDelete':FCKTableHandler.DeleteTable();break;default:alert(FCKLang.UnknownCommand.replace(/%1/g,this.Name));}};FCKTableCommand.prototype.GetState=function(){return 0;}
+var FCKStyleCommand=function(){this.Name='Style';this.StylesLoader=new FCKStylesLoader();this.StylesLoader.Load(FCKConfig.StylesXmlPath);this.Styles=this.StylesLoader.Styles;};FCKStyleCommand.prototype.Execute=function(A,B){FCKUndo.SaveUndoStep();if (B.Selected) B.Style.RemoveFromSelection();else B.Style.ApplyToSelection();FCKUndo.SaveUndoStep();FCK.Focus();FCK.Events.FireEvent("OnSelectionChange");};FCKStyleCommand.prototype.GetState=function(){if (!FCK.EditorDocument) return -1;var A=FCK.EditorDocument.selection;if (FCKSelection.GetType()=='Control'){var e=FCKSelection.GetSelectedElement();if (e) return this.StylesLoader.StyleGroups[e.tagName]?0:-1;};return 0;};FCKStyleCommand.prototype.GetActiveStyles=function(){var A=[];if (FCKSelection.GetType()=='Control') this._CheckStyle(FCKSelection.GetSelectedElement(),A,false);else this._CheckStyle(FCKSelection.GetParentElement(),A,true);return A;};FCKStyleCommand.prototype._CheckStyle=function(A,B,C){if (!A) return;if (A.nodeType==1){var D=this.StylesLoader.StyleGroups[A.tagName];if (D){for (var i=0;i<D.length;i++){if (D[i].IsEqual(A)) B[B.length]=D[i];}}};if (C) this._CheckStyle(A.parentNode,B,C);}
+var FCKFitWindow=function(){this.Name='FitWindow';};FCKFitWindow.prototype.Execute=function(){var A=window.frameElement;var B=A.style;var C=parent;var D=C.document.documentElement;var E=C.document.body;var F=E.style;var G;if (!this.IsMaximized){if(FCKBrowserInfo.IsIE) C.attachEvent('onresize',FCKFitWindow_Resize);else C.addEventListener('resize',FCKFitWindow_Resize,true);this._ScrollPos=FCKTools.GetScrollPosition(C);G=A;while((G=G.parentNode)){if (G.nodeType==1) G._fckSavedStyles=FCKTools.SaveStyles(G);};if (FCKBrowserInfo.IsIE){this.documentElementOverflow=D.style.overflow;D.style.overflow='hidden';F.overflow='hidden';}else{F.overflow='hidden';F.width='0px';F.height='0px';};this._EditorFrameStyles=FCKTools.SaveStyles(A);var H=FCKTools.GetViewPaneSize(C);B.position="absolute";B.zIndex=FCKConfig.FloatingPanelsZIndex-1;B.left="0px";B.top="0px";B.width=H.Width+"px";B.height=H.Height+"px";if (!FCKBrowserInfo.IsIE){B.borderRight=B.borderBottom="9999px solid white";B.backgroundColor="white";};C.scrollTo(0,0);this.IsMaximized=true;}else{if(FCKBrowserInfo.IsIE) C.detachEvent("onresize",FCKFitWindow_Resize);else C.removeEventListener("resize",FCKFitWindow_Resize,true);G=A;while((G=G.parentNode)){if (G._fckSavedStyles){FCKTools.RestoreStyles(G,G._fckSavedStyles);G._fckSavedStyles=null;}};if (FCKBrowserInfo.IsIE) D.style.overflow=this.documentElementOverflow;FCKTools.RestoreStyles(A,this._EditorFrameStyles);C.scrollTo(this._ScrollPos.X,this._ScrollPos.Y);this.IsMaximized=false;};FCKToolbarItems.GetItem('FitWindow').RefreshState();FCK.EditingArea.MakeEditable();FCK.Focus();};FCKFitWindow.prototype.GetState=function(){if (FCKConfig.ToolbarLocation!='In') return -1;else return (this.IsMaximized?1:0);};function FCKFitWindow_Resize(){var A=FCKTools.GetViewPaneSize(parent);var B=window.frameElement.style;B.width=A.Width+'px';B.height=A.Height+'px';};
+var FCKCommands=FCK.Commands={};FCKCommands.LoadedCommands={};FCKCommands.RegisterCommand=function(A,B){this.LoadedCommands[A]=B;};FCKCommands.GetCommand=function(A){var B=FCKCommands.LoadedCommands[A];if (B) return B;switch (A){case 'DocProps':B=new FCKDialogCommand('DocProps',FCKLang.DocProps,'dialog/fck_docprops.html',400,390,FCKCommands.GetFullPageState);break;case 'Templates':B=new FCKDialogCommand('Templates',FCKLang.DlgTemplatesTitle,'dialog/fck_template.html',380,450);break;case 'Link':B=new FCKDialogCommand('Link',FCKLang.DlgLnkWindowTitle,'dialog/fck_link.html',400,330);break;case 'Unlink':B=new FCKUnlinkCommand();break;case 'Anchor':B=new FCKDialogCommand('Anchor',FCKLang.DlgAnchorTitle,'dialog/fck_anchor.html',370,170);break;case 'BulletedList':B=new FCKDialogCommand('BulletedList',FCKLang.BulletedListProp,'dialog/fck_listprop.html?UL',370,170);break;case 'NumberedList':B=new FCKDialogCommand('NumberedList',FCKLang.NumberedListProp,'dialog/fck_listprop.html?OL',370,170);break;case 'About':B=new FCKDialogCommand('About',FCKLang.About,'dialog/fck_about.html',400,330);break;case 'Find':B=new FCKDialogCommand('Find',FCKLang.DlgFindTitle,'dialog/fck_find.html',340,170);break;case 'Replace':B=new FCKDialogCommand('Replace',FCKLang.DlgReplaceTitle,'dialog/fck_replace.html',340,200);break;case 'Image':B=new FCKDialogCommand('Image',FCKLang.DlgImgTitle,'dialog/fck_image.html',450,400);break;case 'Flash':B=new FCKDialogCommand('Flash',FCKLang.DlgFlashTitle,'dialog/fck_flash.html',450,400);break;case 'SpecialChar':B=new FCKDialogCommand('SpecialChar',FCKLang.DlgSpecialCharTitle,'dialog/fck_specialchar.html',400,320);break;case 'Smiley':B=new FCKDialogCommand('Smiley',FCKLang.DlgSmileyTitle,'dialog/fck_smiley.html',FCKConfig.SmileyWindowWidth,FCKConfig.SmileyWindowHeight);break;case 'Table':B=new FCKDialogCommand('Table',FCKLang.DlgTableTitle,'dialog/fck_table.html',450,250);break;case 'TableProp':B=new FCKDialogCommand('Table',FCKLang.DlgTableTitle,'dialog/fck_table.html?Parent',400,250);break;case 'TableCellProp':B=new FCKDialogCommand('TableCell',FCKLang.DlgCellTitle,'dialog/fck_tablecell.html',550,250);break;case 'Style':B=new FCKStyleCommand();break;case 'FontName':B=new FCKFontNameCommand();break;case 'FontSize':B=new FCKFontSizeCommand();break;case 'FontFormat':B=new FCKFormatBlockCommand();break;case 'Source':B=new FCKSourceCommand();break;case 'Preview':B=new FCKPreviewCommand();break;case 'Save':B=new FCKSaveCommand();break;case 'NewPage':B=new FCKNewPageCommand();break;case 'PageBreak':B=new FCKPageBreakCommand();break;case 'TextColor':B=new FCKTextColorCommand('ForeColor');break;case 'BGColor':B=new FCKTextColorCommand('BackColor');break;case 'Paste':B=new FCKPasteCommand();break;case 'PasteText':B=new FCKPastePlainTextCommand();break;case 'PasteWord':B=new FCKPasteWordCommand();break;case 'TableInsertRow':B=new FCKTableCommand('TableInsertRow');break;case 'TableDeleteRows':B=new FCKTableCommand('TableDeleteRows');break;case 'TableInsertColumn':B=new FCKTableCommand('TableInsertColumn');break;case 'TableDeleteColumns':B=new FCKTableCommand('TableDeleteColumns');break;case 'TableInsertCell':B=new FCKTableCommand('TableInsertCell');break;case 'TableDeleteCells':B=new FCKTableCommand('TableDeleteCells');break;case 'TableMergeCells':B=new FCKTableCommand('TableMergeCells');break;case 'TableSplitCell':B=new FCKTableCommand('TableSplitCell');break;case 'TableDelete':B=new FCKTableCommand('TableDelete');break;case 'Form':B=new FCKDialogCommand('Form',FCKLang.Form,'dialog/fck_form.html',380,230);break;case 'Checkbox':B=new FCKDialogCommand('Checkbox',FCKLang.Checkbox,'dialog/fck_checkbox.html',380,230);break;case 'Radio':B=new FCKDialogCommand('Radio',FCKLang.RadioButton,'dialog/fck_radiobutton.html',380,230);break;case 'TextField':B=new FCKDialogCommand('TextField',FCKLang.TextField,'dialog/fck_textfield.html',380,230);break;case 'Textarea':B=new FCKDialogCommand('Textarea',FCKLang.Textarea,'dialog/fck_textarea.html',380,230);break;case 'HiddenField':B=new FCKDialogCommand('HiddenField',FCKLang.HiddenField,'dialog/fck_hiddenfield.html',380,230);break;case 'Button':B=new FCKDialogCommand('Button',FCKLang.Button,'dialog/fck_button.html',380,230);break;case 'Select':B=new FCKDialogCommand('Select',FCKLang.SelectionField,'dialog/fck_select.html',400,380);break;case 'ImageButton':B=new FCKDialogCommand('ImageButton',FCKLang.ImageButton,'dialog/fck_image.html?ImageButton',450,400);break;case 'SpellCheck':B=new FCKSpellCheckCommand();break;case 'FitWindow':B=new FCKFitWindow();break;case 'Undo':B=new FCKUndoCommand();break;case 'Redo':B=new FCKRedoCommand();break;case 'SelectAll':B=new FCKSelectAllCommand();break;case 'Undefined':B=new FCKUndefinedCommand();break;default:if (FCKRegexLib.NamedCommands.test(A)) B=new FCKNamedCommand(A);else{alert(FCKLang.UnknownCommand.replace(/%1/g,A));return null;}};FCKCommands.LoadedCommands[A]=B;return B;};FCKCommands.GetFullPageState=function(){return FCKConfig.FullPage?0:-1;};
+var FCKPanel=function(A){this.IsRTL=(FCKLang.Dir=='rtl');this.IsContextMenu=false;this._LockCounter=0;this._Window=A||window;var B;if (FCKBrowserInfo.IsIE){this._Popup=this._Window.createPopup();B=this.Document=this._Popup.document;FCK.IECleanup.AddItem(this,FCKPanel_Cleanup);}else{var C=this._IFrame=this._Window.document.createElement('iframe');C.src='javascript:void(0)';C.allowTransparency=true;C.frameBorder='0';C.scrolling='no';C.style.position='absolute';C.style.zIndex=FCKConfig.FloatingPanelsZIndex;C.width=C.height=0;if (this._Window==window.parent&&window.frameElement) window.frameElement.parentNode.insertBefore(C,window.frameElement);else this._Window.document.body.appendChild(C);var D=C.contentWindow;B=this.Document=D.document;var E='';if (FCKBrowserInfo.IsSafari) E='<base href="'+window.document.location+'">';B.open();B.write('<html><head>'+E+'<\/head><body style="margin:0px;padding:0px;"><\/body><\/html>');B.close();FCKTools.AddEventListenerEx(D,'focus',FCKPanel_Window_OnFocus,this);FCKTools.AddEventListenerEx(D,'blur',FCKPanel_Window_OnBlur,this);};B.dir=FCKLang.Dir;B.oncontextmenu=FCKTools.CancelEvent;this.MainNode=B.body.appendChild(B.createElement('DIV'));this.MainNode.style.cssFloat=this.IsRTL?'right':'left';};FCKPanel.prototype.AppendStyleSheet=function(A){FCKTools.AppendStyleSheet(this.Document,A);};FCKPanel.prototype.Preload=function(x,y,A){if (this._Popup) this._Popup.show(x,y,0,0,A);};FCKPanel.prototype.Show=function(x,y,A,B,C){var D;if (this._Popup){this._Popup.show(x,y,0,0,A);this.MainNode.style.width=B?B+'px':'';this.MainNode.style.height=C?C+'px':'';D=this.MainNode.offsetWidth;if (this.IsRTL){if (this.IsContextMenu) x=x-D+1;else if (A) x=(x*-1)+A.offsetWidth-D;};this._Popup.show(x,y,D,this.MainNode.offsetHeight,A);if (this.OnHide){if (this._Timer) CheckPopupOnHide.call(this,true);this._Timer=FCKTools.SetInterval(CheckPopupOnHide,100,this);}}else{if (typeof(FCKFocusManager)!='undefined') FCKFocusManager.Lock();if (this.ParentPanel) this.ParentPanel.Lock();this.MainNode.style.width=B?B+'px':'';this.MainNode.style.height=C?C+'px':'';D=this.MainNode.offsetWidth;if (!B) this._IFrame.width=1;if (!C) this._IFrame.height=1;D=this.MainNode.offsetWidth;var E=FCKTools.GetElementPosition(A.nodeType==9?(FCKTools.IsStrictMode(A)?A.documentElement:A.body):A,this._Window);if (this.IsRTL&&!this.IsContextMenu) x=(x*-1);x+=E.X;y+=E.Y;if (this.IsRTL){if (this.IsContextMenu) x=x-D+1;else if (A) x=x+A.offsetWidth-D;}else{var F=FCKTools.GetViewPaneSize(this._Window);var G=FCKTools.GetScrollPosition(this._Window);var H=F.Height+G.Y;var I=F.Width+G.X;if ((x+D)>I) x-=x+D-I;if ((y+this.MainNode.offsetHeight)>H) y-=y+this.MainNode.offsetHeight-H;};if (x<0) x=0;this._IFrame.style.left=x+'px';this._IFrame.style.top=y+'px';var J=D;var K=this.MainNode.offsetHeight;this._IFrame.width=J;this._IFrame.height=K;this._IFrame.contentWindow.focus();};this._IsOpened=true;FCKTools.RunFunction(this.OnShow,this);};FCKPanel.prototype.Hide=function(A){if (this._Popup) this._Popup.hide();else{if (!this._IsOpened) return;if (typeof(FCKFocusManager)!='undefined') FCKFocusManager.Unlock();this._IFrame.width=this._IFrame.height=0;this._IsOpened=false;if (this.ParentPanel) this.ParentPanel.Unlock();if (!A) FCKTools.RunFunction(this.OnHide,this);}};FCKPanel.prototype.CheckIsOpened=function(){if (this._Popup) return this._Popup.isOpen;else return this._IsOpened;};FCKPanel.prototype.CreateChildPanel=function(){var A=this._Popup?FCKTools.GetDocumentWindow(this.Document):this._Window;var B=new FCKPanel(A);B.ParentPanel=this;return B;};FCKPanel.prototype.Lock=function(){this._LockCounter++;};FCKPanel.prototype.Unlock=function(){if (--this._LockCounter==0&&!this.HasFocus) this.Hide();};function FCKPanel_Window_OnFocus(e,A){A.HasFocus=true;};function FCKPanel_Window_OnBlur(e,A){A.HasFocus=false;if (A._LockCounter==0) FCKTools.RunFunction(A.Hide,A);};function CheckPopupOnHide(A){if (A||!this._Popup.isOpen){window.clearInterval(this._Timer);this._Timer=null;FCKTools.RunFunction(this.OnHide,this);}};function FCKPanel_Cleanup(){this._Popup=null;this._Window=null;this.Document=null;this.MainNode=null;}
+var FCKIcon=function(A){var B=A?typeof(A):'undefined';switch (B){case 'number':this.Path=FCKConfig.SkinPath+'fck_strip.gif';this.Size=16;this.Position=A;break;case 'undefined':this.Path=FCK_SPACER_PATH;break;case 'string':this.Path=A;break;default:this.Path=A[0];this.Size=A[1];this.Position=A[2];}};FCKIcon.prototype.CreateIconElement=function(A){var B,eIconImage;if (this.Position){var C='-'+((this.Position-1)*this.Size)+'px';if (FCKBrowserInfo.IsIE){B=A.createElement('DIV');eIconImage=B.appendChild(A.createElement('IMG'));eIconImage.src=this.Path;eIconImage.style.top=C;}else{B=A.createElement('IMG');B.src=FCK_SPACER_PATH;B.style.backgroundPosition='0px '+C;B.style.backgroundImage='url('+this.Path+')';}}else{if (FCKBrowserInfo.IsIE){B=A.createElement('DIV');eIconImage=B.appendChild(A.createElement('IMG'));eIconImage.src=this.Path?this.Path:FCK_SPACER_PATH;}else{B=A.createElement('IMG');B.src=this.Path?this.Path:FCK_SPACER_PATH;}};B.className='TB_Button_Image';return B;}
+var FCKToolbarButtonUI=function(A,B,C,D,E,F){this.Name=A;this.Label=B||A;this.Tooltip=C||this.Label;this.Style=E||0;this.State=F||0;this.Icon=new FCKIcon(D);if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKToolbarButtonUI_Cleanup);};FCKToolbarButtonUI.prototype._CreatePaddingElement=function(A){var B=A.createElement('IMG');B.className='TB_Button_Padding';B.src=FCK_SPACER_PATH;return B;};FCKToolbarButtonUI.prototype.Create=function(A){var B=this.MainElement;if (B){FCKToolbarButtonUI_Cleanup.call(this);if (B.parentNode) B.parentNode.removeChild(B);B=this.MainElement=null;};var C=FCKTools.GetElementDocument(A);B=this.MainElement=C.createElement('DIV');B._FCKButton=this;B.title=this.Tooltip;if (FCKBrowserInfo.IsGecko) B.onmousedown=FCKTools.CancelEvent;this.ChangeState(this.State,true);if (this.Style==0&&!this.ShowArrow){B.appendChild(this.Icon.CreateIconElement(C));}else{var D=B.appendChild(C.createElement('TABLE'));D.cellPadding=0;D.cellSpacing=0;var E=D.insertRow(-1);var F=E.insertCell(-1);if (this.Style==0||this.Style==2) F.appendChild(this.Icon.CreateIconElement(C));else F.appendChild(this._CreatePaddingElement(C));if (this.Style==1||this.Style==2){F=E.insertCell(-1);F.className='TB_Button_Text';F.noWrap=true;F.appendChild(C.createTextNode(this.Label));};if (this.ShowArrow){if (this.Style!=0){E.insertCell(-1).appendChild(this._CreatePaddingElement(C));};F=E.insertCell(-1);var G=F.appendChild(C.createElement('IMG'));G.src=FCKConfig.SkinPath+'images/toolbar.buttonarrow.gif';G.width=5;G.height=3;};F=E.insertCell(-1);F.appendChild(this._CreatePaddingElement(C));};A.appendChild(B);};FCKToolbarButtonUI.prototype.ChangeState=function(A,B){if (!B&&this.State==A) return;var e=this.MainElement;switch (parseInt(A,10)){case 0:e.className='TB_Button_Off';e.onmouseover=FCKToolbarButton_OnMouseOverOff;e.onmouseout=FCKToolbarButton_OnMouseOutOff;e.onclick=FCKToolbarButton_OnClick;break;case 1:e.className='TB_Button_On';e.onmouseover=FCKToolbarButton_OnMouseOverOn;e.onmouseout=FCKToolbarButton_OnMouseOutOn;e.onclick=FCKToolbarButton_OnClick;break;case -1:e.className='TB_Button_Disabled';e.onmouseover=null;e.onmouseout=null;e.onclick=null;break;};this.State=A;};function FCKToolbarButtonUI_Cleanup(){if (this.MainElement){this.MainElement._FCKButton=null;this.MainElement=null;}};function FCKToolbarButton_OnMouseOverOn(){this.className='TB_Button_On_Over';};function FCKToolbarButton_OnMouseOutOn(){this.className='TB_Button_On';};function FCKToolbarButton_OnMouseOverOff(){this.className='TB_Button_Off_Over';};function FCKToolbarButton_OnMouseOutOff(){this.className='TB_Button_Off';};function FCKToolbarButton_OnClick(e){if (this._FCKButton.OnClick) this._FCKButton.OnClick(this._FCKButton);};
+var FCKToolbarButton=function(A,B,C,D,E,F,G){this.CommandName=A;this.Label=B;this.Tooltip=C;this.Style=D;this.SourceView=E?true:false;this.ContextSensitive=F?true:false;if (G==null) this.IconPath=FCKConfig.SkinPath+'toolbar/'+A.toLowerCase()+'.gif';else if (typeof(G)=='number') this.IconPath=[FCKConfig.SkinPath+'fck_strip.gif',16,G];};FCKToolbarButton.prototype.Create=function(A){this._UIButton=new FCKToolbarButtonUI(this.CommandName,this.Label,this.Tooltip,this.IconPath,this.Style);this._UIButton.OnClick=this.Click;this._UIButton._ToolbarButton=this;this._UIButton.Create(A);};FCKToolbarButton.prototype.RefreshState=function(){var A=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).GetState();if (A==this._UIButton.State) return;this._UIButton.ChangeState(A);};FCKToolbarButton.prototype.Click=function(){var A=this._ToolbarButton||this;FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(A.CommandName).Execute();};FCKToolbarButton.prototype.Enable=function(){this.RefreshState();};FCKToolbarButton.prototype.Disable=function(){this._UIButton.ChangeState(-1);}
+var FCKSpecialCombo=function(A,B,C,D,E){this.FieldWidth=B||100;this.PanelWidth=C||150;this.PanelMaxHeight=D||150;this.Label='&nbsp;';this.Caption=A;this.Tooltip=A;this.Style=2;this.Enabled=true;this.Items={};this._Panel=new FCKPanel(E||window);this._Panel.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');this._PanelBox=this._Panel.MainNode.appendChild(this._Panel.Document.createElement('DIV'));this._PanelBox.className='SC_Panel';this._PanelBox.style.width=this.PanelWidth+'px';this._PanelBox.innerHTML='<table cellpadding="0" cellspacing="0" width="100%" style="TABLE-LAYOUT: fixed"><tr><td nowrap></td></tr></table>';this._ItemsHolderEl=this._PanelBox.getElementsByTagName('TD')[0];if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKSpecialCombo_Cleanup);};function FCKSpecialCombo_ItemOnMouseOver(){this.className+=' SC_ItemOver';};function FCKSpecialCombo_ItemOnMouseOut(){this.className=this.originalClass;};function FCKSpecialCombo_ItemOnClick(){this.className=this.originalClass;this.FCKSpecialCombo._Panel.Hide();this.FCKSpecialCombo.SetLabel(this.FCKItemLabel);if (typeof(this.FCKSpecialCombo.OnSelect)=='function') this.FCKSpecialCombo.OnSelect(this.FCKItemID,this);};FCKSpecialCombo.prototype.AddItem=function(A,B,C,D){var E=this._ItemsHolderEl.appendChild(this._Panel.Document.createElement('DIV'));E.className=E.originalClass='SC_Item';E.innerHTML=B;E.FCKItemID=A;E.FCKItemLabel=C||A;E.FCKSpecialCombo=this;E.Selected=false;if (FCKBrowserInfo.IsIE) E.style.width='100%';if (D) E.style.backgroundColor=D;E.onmouseover=FCKSpecialCombo_ItemOnMouseOver;E.onmouseout=FCKSpecialCombo_ItemOnMouseOut;E.onclick=FCKSpecialCombo_ItemOnClick;this.Items[A.toString().toLowerCase()]=E;return E;};FCKSpecialCombo.prototype.SelectItem=function(A){A=A?A.toString().toLowerCase():'';var B=this.Items[A];if (B){B.className=B.originalClass='SC_ItemSelected';B.Selected=true;}};FCKSpecialCombo.prototype.SelectItemByLabel=function(A,B){for (var C in this.Items){var D=this.Items[C];if (D.FCKItemLabel==A){D.className=D.originalClass='SC_ItemSelected';D.Selected=true;if (B) this.SetLabel(A);}}};FCKSpecialCombo.prototype.DeselectAll=function(A){for (var i in this.Items){this.Items[i].className=this.Items[i].originalClass='SC_Item';this.Items[i].Selected=false;};if (A) this.SetLabel('');};FCKSpecialCombo.prototype.SetLabelById=function(A){A=A?A.toString().toLowerCase():'';var B=this.Items[A];this.SetLabel(B?B.FCKItemLabel:'');};FCKSpecialCombo.prototype.SetLabel=function(A){this.Label=A.length==0?'&nbsp;':A;if (this._LabelEl){this._LabelEl.innerHTML=this.Label;FCKTools.DisableSelection(this._LabelEl);}};FCKSpecialCombo.prototype.SetEnabled=function(A){this.Enabled=A;this._OuterTable.className=A?'':'SC_FieldDisabled';};FCKSpecialCombo.prototype.Create=function(A){var B=FCKTools.GetElementDocument(A);var C=this._OuterTable=A.appendChild(B.createElement('TABLE'));C.cellPadding=0;C.cellSpacing=0;C.insertRow(-1);var D;var E;switch (this.Style){case 0:D='TB_ButtonType_Icon';E=false;break;case 1:D='TB_ButtonType_Text';E=false;break;case 2:E=true;break;};if (this.Caption&&this.Caption.length>0&&E){var F=C.rows[0].insertCell(-1);F.innerHTML=this.Caption;F.className='SC_FieldCaption';};var G=FCKTools.AppendElement(C.rows[0].insertCell(-1),'div');if (E){G.className='SC_Field';G.style.width=this.FieldWidth+'px';G.innerHTML='<table width="100%" cellpadding="0" cellspacing="0" style="TABLE-LAYOUT: fixed;"><tbody><tr><td class="SC_FieldLabel"><label>&nbsp;</label></td><td class="SC_FieldButton">&nbsp;</td></tr></tbody></table>';this._LabelEl=G.getElementsByTagName('label')[0];this._LabelEl.innerHTML=this.Label;}else{G.className='TB_Button_Off';G.innerHTML='<table title="'+this.Tooltip+'" class="'+D+'" cellspacing="0" cellpadding="0" border="0"><tr><td><img class="TB_Button_Padding" src="'+FCK_SPACER_PATH+'" /></td><td class="TB_Text">'+this.Caption+'</td><td><img class="TB_Button_Padding" src="'+FCK_SPACER_PATH+'" /></td><td class="TB_ButtonArrow"><img src="'+FCKConfig.SkinPath+'images/toolbar.buttonarrow.gif" width="5" height="3"></td><td><img class="TB_Button_Padding" src="'+FCK_SPACER_PATH+'" /></td></tr></table>';};G.SpecialCombo=this;G.onmouseover=FCKSpecialCombo_OnMouseOver;G.onmouseout=FCKSpecialCombo_OnMouseOut;G.onclick=FCKSpecialCombo_OnClick;FCKTools.DisableSelection(this._Panel.Document.body);};function FCKSpecialCombo_Cleanup(){this._LabelEl=null;this._OuterTable=null;this._ItemsHolderEl=null;this._PanelBox=null;if (this.Items){for (var A in this.Items) this.Items[A]=null;}};function FCKSpecialCombo_OnMouseOver(){if (this.SpecialCombo.Enabled){switch (this.SpecialCombo.Style){case 0:this.className='TB_Button_On_Over';break;case 1:this.className='TB_Button_On_Over';break;case 2:this.className='SC_Field SC_FieldOver';break;}}};function FCKSpecialCombo_OnMouseOut(){switch (this.SpecialCombo.Style){case 0:this.className='TB_Button_Off';break;case 1:this.className='TB_Button_Off';break;case 2:this.className='SC_Field';break;}};function FCKSpecialCombo_OnClick(e){var A=this.SpecialCombo;if (A.Enabled){var B=A._Panel;var C=A._PanelBox;var D=A._ItemsHolderEl;var E=A.PanelMaxHeight;if (A.OnBeforeClick) A.OnBeforeClick(A);if (FCKBrowserInfo.IsIE) B.Preload(0,this.offsetHeight,this);if (D.offsetHeight>E) C.style.height=E+'px';else C.style.height='';B.Show(0,this.offsetHeight,this);}};
+var FCKToolbarSpecialCombo=function(){this.SourceView=false;this.ContextSensitive=true;this._LastValue=null;};function FCKToolbarSpecialCombo_OnSelect(A,B){FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).Execute(A,B);};FCKToolbarSpecialCombo.prototype.Create=function(A){this._Combo=new FCKSpecialCombo(this.GetLabel(),this.FieldWidth,this.PanelWidth,this.PanelMaxHeight,FCKBrowserInfo.IsIE?window:FCKTools.GetElementWindow(A).parent);this._Combo.Tooltip=this.Tooltip;this._Combo.Style=this.Style;this.CreateItems(this._Combo);this._Combo.Create(A);this._Combo.CommandName=this.CommandName;this._Combo.OnSelect=FCKToolbarSpecialCombo_OnSelect;};function FCKToolbarSpecialCombo_RefreshActiveItems(A,B){A.DeselectAll();A.SelectItem(B);A.SetLabelById(B);};FCKToolbarSpecialCombo.prototype.RefreshState=function(){var A;var B=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).GetState();if (B!=-1){A=1;if (this.RefreshActiveItems) this.RefreshActiveItems(this._Combo,B);else{if (this._LastValue!=B){this._LastValue=B;FCKToolbarSpecialCombo_RefreshActiveItems(this._Combo,B);}}}else A=-1;if (A==this.State) return;if (A==-1){this._Combo.DeselectAll();this._Combo.SetLabel('');};this.State=A;this._Combo.SetEnabled(A!=-1);};FCKToolbarSpecialCombo.prototype.Enable=function(){this.RefreshState();};FCKToolbarSpecialCombo.prototype.Disable=function(){this.State=-1;this._Combo.DeselectAll();this._Combo.SetLabel('');this._Combo.SetEnabled(false);};
+var FCKToolbarFontsCombo=function(A,B){this.CommandName='FontName';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;};FCKToolbarFontsCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarFontsCombo.prototype.GetLabel=function(){return FCKLang.Font;};FCKToolbarFontsCombo.prototype.CreateItems=function(A){var B=FCKConfig.FontNames.split(';');for (var i=0;i<B.length;i++) this._Combo.AddItem(B[i],'<font face="'+B[i]+'" style="font-size: 12px">'+B[i]+'</font>');}
+var FCKToolbarFontSizeCombo=function(A,B){this.CommandName='FontSize';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;};FCKToolbarFontSizeCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarFontSizeCombo.prototype.GetLabel=function(){return FCKLang.FontSize;};FCKToolbarFontSizeCombo.prototype.CreateItems=function(A){A.FieldWidth=70;var B=FCKConfig.FontSizes.split(';');for (var i=0;i<B.length;i++){var C=B[i].split('/');this._Combo.AddItem(C[0],'<font size="'+C[0]+'">'+C[1]+'</font>',C[1]);}}
+var FCKToolbarFontFormatCombo=function(A,B){this.CommandName='FontFormat';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;this.NormalLabel='Normal';this.PanelWidth=190;};FCKToolbarFontFormatCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarFontFormatCombo.prototype.GetLabel=function(){return FCKLang.FontFormat;};FCKToolbarFontFormatCombo.prototype.CreateItems=function(A){var B=A._Panel.Document;FCKTools.AppendStyleSheet(B,FCKConfig.ToolbarComboPreviewCSS);if (FCKConfig.BodyId&&FCKConfig.BodyId.length>0) B.body.id=FCKConfig.BodyId;if (FCKConfig.BodyClass&&FCKConfig.BodyClass.length>0) B.body.className+=' '+FCKConfig.BodyClass;var C=FCKLang['FontFormats'].split(';');var D={p:C[0],pre:C[1],address:C[2],h1:C[3],h2:C[4],h3:C[5],h4:C[6],h5:C[7],h6:C[8],div:C[9]};var E=FCKConfig.FontFormats.split(';');for (var i=0;i<E.length;i++){var F=E[i];var G=D[F];if (F=='p') this.NormalLabel=G;this._Combo.AddItem(F,'<div class="BaseFont"><'+F+'>'+G+'</'+F+'></div>',G);}};if (FCKBrowserInfo.IsIE){FCKToolbarFontFormatCombo.prototype.RefreshActiveItems=function(A,B){if (B==this.NormalLabel){if (A.Label!='&nbsp;') A.DeselectAll(true);}else{if (this._LastValue==B) return;A.SelectItemByLabel(B,true);};this._LastValue=B;}}
+var FCKToolbarStyleCombo=function(A,B){this.CommandName='Style';this.Label=this.GetLabel();this.Tooltip=A?A:this.Label;this.Style=B?B:2;};FCKToolbarStyleCombo.prototype=new FCKToolbarSpecialCombo;FCKToolbarStyleCombo.prototype.GetLabel=function(){return FCKLang.Style;};FCKToolbarStyleCombo.prototype.CreateItems=function(A){var B=A._Panel.Document;FCKTools.AppendStyleSheet(B,FCKConfig.ToolbarComboPreviewCSS);B.body.className+=' ForceBaseFont';if (FCKConfig.BodyId&&FCKConfig.BodyId.length>0) B.body.id=FCKConfig.BodyId;if (FCKConfig.BodyClass&&FCKConfig.BodyClass.length>0) B.body.className+=' '+FCKConfig.BodyClass;if (!(FCKBrowserInfo.IsGecko&&FCKBrowserInfo.IsGecko10)) A.OnBeforeClick=this.RefreshVisibleItems;var C=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).Styles;for (var s in C){var D=C[s];var E;if (D.IsObjectElement) E=A.AddItem(s,s);else E=A.AddItem(s,D.GetOpenerTag()+s+D.GetCloserTag());E.Style=D;}};FCKToolbarStyleCombo.prototype.RefreshActiveItems=function(A){A.DeselectAll();var B=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName).GetActiveStyles();if (B.length>0){for (var i=0;i<B.length;i++) A.SelectItem(B[i].Name);A.SetLabelById(B[0].Name);}else A.SetLabel('');};FCKToolbarStyleCombo.prototype.RefreshVisibleItems=function(A){if (FCKSelection.GetType()=='Control') var B=FCKSelection.GetSelectedElement().tagName;for (var i in A.Items){var C=A.Items[i];if ((B&&C.Style.Element==B)||(!B&&!C.Style.IsObjectElement)) C.style.display='';else C.style.display='none';}}
+var FCKToolbarPanelButton=function(A,B,C,D,E){this.CommandName=A;var F;if (E==null) F=FCKConfig.SkinPath+'toolbar/'+A.toLowerCase()+'.gif';else if (typeof(E)=='number') F=[FCKConfig.SkinPath+'fck_strip.gif',16,E];var G=this._UIButton=new FCKToolbarButtonUI(A,B,C,F,D);G._FCKToolbarPanelButton=this;G.ShowArrow=true;G.OnClick=FCKToolbarPanelButton_OnButtonClick;};FCKToolbarPanelButton.prototype.TypeName='FCKToolbarPanelButton';FCKToolbarPanelButton.prototype.Create=function(A){A.className+='Menu';this._UIButton.Create(A);var B=FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(this.CommandName)._Panel;B._FCKToolbarPanelButton=this;var C=B.Document.body.appendChild(B.Document.createElement('div'));C.style.position='absolute';C.style.top='0px';var D=this.LineImg=C.appendChild(B.Document.createElement('IMG'));D.className='TB_ConnectionLine';D.src=FCK_SPACER_PATH;B.OnHide=FCKToolbarPanelButton_OnPanelHide;};function FCKToolbarPanelButton_OnButtonClick(A){var B=this._FCKToolbarPanelButton;var e=B._UIButton.MainElement;B._UIButton.ChangeState(1);B.LineImg.style.width=(e.offsetWidth-2)+'px';FCK.ToolbarSet.CurrentInstance.Commands.GetCommand(B.CommandName).Execute(0,e.offsetHeight-1,e);};function FCKToolbarPanelButton_OnPanelHide(){var A=this._FCKToolbarPanelButton;A._UIButton.ChangeState(0);};FCKToolbarPanelButton.prototype.RefreshState=FCKToolbarButton.prototype.RefreshState;FCKToolbarPanelButton.prototype.Enable=FCKToolbarButton.prototype.Enable;FCKToolbarPanelButton.prototype.Disable=FCKToolbarButton.prototype.Disable;
+var FCKToolbarItems={};FCKToolbarItems.LoadedItems={};FCKToolbarItems.RegisterItem=function(A,B){this.LoadedItems[A]=B;};FCKToolbarItems.GetItem=function(A){var B=FCKToolbarItems.LoadedItems[A];if (B) return B;switch (A){case 'Source':B=new FCKToolbarButton('Source',FCKLang.Source,null,2,true,true,1);break;case 'DocProps':B=new FCKToolbarButton('DocProps',FCKLang.DocProps,null,null,null,null,2);break;case 'Save':B=new FCKToolbarButton('Save',FCKLang.Save,null,null,true,null,3);break;case 'NewPage':B=new FCKToolbarButton('NewPage',FCKLang.NewPage,null,null,true,null,4);break;case 'Preview':B=new FCKToolbarButton('Preview',FCKLang.Preview,null,null,true,null,5);break;case 'Templates':B=new FCKToolbarButton('Templates',FCKLang.Templates,null,null,null,null,6);break;case 'About':B=new FCKToolbarButton('About',FCKLang.About,null,null,true,null,47);break;case 'Cut':B=new FCKToolbarButton('Cut',FCKLang.Cut,null,null,false,true,7);break;case 'Copy':B=new FCKToolbarButton('Copy',FCKLang.Copy,null,null,false,true,8);break;case 'Paste':B=new FCKToolbarButton('Paste',FCKLang.Paste,null,null,false,true,9);break;case 'PasteText':B=new FCKToolbarButton('PasteText',FCKLang.PasteText,null,null,false,true,10);break;case 'PasteWord':B=new FCKToolbarButton('PasteWord',FCKLang.PasteWord,null,null,false,true,11);break;case 'Print':B=new FCKToolbarButton('Print',FCKLang.Print,null,null,false,true,12);break;case 'SpellCheck':B=new FCKToolbarButton('SpellCheck',FCKLang.SpellCheck,null,null,null,null,13);break;case 'Undo':B=new FCKToolbarButton('Undo',FCKLang.Undo,null,null,false,true,14);break;case 'Redo':B=new FCKToolbarButton('Redo',FCKLang.Redo,null,null,false,true,15);break;case 'SelectAll':B=new FCKToolbarButton('SelectAll',FCKLang.SelectAll,null,null,true,null,18);break;case 'RemoveFormat':B=new FCKToolbarButton('RemoveFormat',FCKLang.RemoveFormat,null,null,false,true,19);break;case 'FitWindow':B=new FCKToolbarButton('FitWindow',FCKLang.FitWindow,null,null,true,true,66);break;case 'Bold':B=new FCKToolbarButton('Bold',FCKLang.Bold,null,null,false,true,20);break;case 'Italic':B=new FCKToolbarButton('Italic',FCKLang.Italic,null,null,false,true,21);break;case 'Underline':B=new FCKToolbarButton('Underline',FCKLang.Underline,null,null,false,true,22);break;case 'StrikeThrough':B=new FCKToolbarButton('StrikeThrough',FCKLang.StrikeThrough,null,null,false,true,23);break;case 'Subscript':B=new FCKToolbarButton('Subscript',FCKLang.Subscript,null,null,false,true,24);break;case 'Superscript':B=new FCKToolbarButton('Superscript',FCKLang.Superscript,null,null,false,true,25);break;case 'OrderedList':B=new FCKToolbarButton('InsertOrderedList',FCKLang.NumberedListLbl,FCKLang.NumberedList,null,false,true,26);break;case 'UnorderedList':B=new FCKToolbarButton('InsertUnorderedList',FCKLang.BulletedListLbl,FCKLang.BulletedList,null,false,true,27);break;case 'Outdent':B=new FCKToolbarButton('Outdent',FCKLang.DecreaseIndent,null,null,false,true,28);break;case 'Indent':B=new FCKToolbarButton('Indent',FCKLang.IncreaseIndent,null,null,false,true,29);break;case 'Link':B=new FCKToolbarButton('Link',FCKLang.InsertLinkLbl,FCKLang.InsertLink,null,false,true,34);break;case 'Unlink':B=new FCKToolbarButton('Unlink',FCKLang.RemoveLink,null,null,false,true,35);break;case 'Anchor':B=new FCKToolbarButton('Anchor',FCKLang.Anchor,null,null,null,null,36);break;case 'Image':B=new FCKToolbarButton('Image',FCKLang.InsertImageLbl,FCKLang.InsertImage,null,false,true,37);break;case 'Flash':B=new FCKToolbarButton('Flash',FCKLang.InsertFlashLbl,FCKLang.InsertFlash,null,false,true,38);break;case 'Table':B=new FCKToolbarButton('Table',FCKLang.InsertTableLbl,FCKLang.InsertTable,null,false,true,39);break;case 'SpecialChar':B=new FCKToolbarButton('SpecialChar',FCKLang.InsertSpecialCharLbl,FCKLang.InsertSpecialChar,null,false,true,42);break;case 'Smiley':B=new FCKToolbarButton('Smiley',FCKLang.InsertSmileyLbl,FCKLang.InsertSmiley,null,false,true,41);break;case 'PageBreak':B=new FCKToolbarButton('PageBreak',FCKLang.PageBreakLbl,FCKLang.PageBreak,null,false,true,43);break;case 'Rule':B=new FCKToolbarButton('InsertHorizontalRule',FCKLang.InsertLineLbl,FCKLang.InsertLine,null,false,true,40);break;case 'JustifyLeft':B=new FCKToolbarButton('JustifyLeft',FCKLang.LeftJustify,null,null,false,true,30);break;case 'JustifyCenter':B=new FCKToolbarButton('JustifyCenter',FCKLang.CenterJustify,null,null,false,true,31);break;case 'JustifyRight':B=new FCKToolbarButton('JustifyRight',FCKLang.RightJustify,null,null,false,true,32);break;case 'JustifyFull':B=new FCKToolbarButton('JustifyFull',FCKLang.BlockJustify,null,null,false,true,33);break;case 'Style':B=new FCKToolbarStyleCombo();break;case 'FontName':B=new FCKToolbarFontsCombo();break;case 'FontSize':B=new FCKToolbarFontSizeCombo();break;case 'FontFormat':B=new FCKToolbarFontFormatCombo();break;case 'TextColor':B=new FCKToolbarPanelButton('TextColor',FCKLang.TextColor,null,null,45);break;case 'BGColor':B=new FCKToolbarPanelButton('BGColor',FCKLang.BGColor,null,null,46);break;case 'Find':B=new FCKToolbarButton('Find',FCKLang.Find,null,null,null,null,16);break;case 'Replace':B=new FCKToolbarButton('Replace',FCKLang.Replace,null,null,null,null,17);break;case 'Form':B=new FCKToolbarButton('Form',FCKLang.Form,null,null,null,null,48);break;case 'Checkbox':B=new FCKToolbarButton('Checkbox',FCKLang.Checkbox,null,null,null,null,49);break;case 'Radio':B=new FCKToolbarButton('Radio',FCKLang.RadioButton,null,null,null,null,50);break;case 'TextField':B=new FCKToolbarButton('TextField',FCKLang.TextField,null,null,null,null,51);break;case 'Textarea':B=new FCKToolbarButton('Textarea',FCKLang.Textarea,null,null,null,null,52);break;case 'HiddenField':B=new FCKToolbarButton('HiddenField',FCKLang.HiddenField,null,null,null,null,56);break;case 'Button':B=new FCKToolbarButton('Button',FCKLang.Button,null,null,null,null,54);break;case 'Select':B=new FCKToolbarButton('Select',FCKLang.SelectionField,null,null,null,null,53);break;case 'ImageButton':B=new FCKToolbarButton('ImageButton',FCKLang.ImageButton,null,null,null,null,55);break;default:alert(FCKLang.UnknownToolbarItem.replace(/%1/g,A));return null;};FCKToolbarItems.LoadedItems[A]=B;return B;}
+var FCKToolbar=function(){this.Items=[];if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKToolbar_Cleanup);};FCKToolbar.prototype.AddItem=function(A){return this.Items[this.Items.length]=A;};FCKToolbar.prototype.AddButton=function(A,B,C,D,E,F){if (typeof(D)=='number') D=[this.DefaultIconsStrip,this.DefaultIconSize,D];var G=new FCKToolbarButtonUI(A,B,C,D,E,F);G._FCKToolbar=this;G.OnClick=FCKToolbar_OnItemClick;return this.AddItem(G);};function FCKToolbar_OnItemClick(A){var B=A._FCKToolbar;if (B.OnItemClick) B.OnItemClick(B,A);};FCKToolbar.prototype.AddSeparator=function(){this.AddItem(new FCKToolbarSeparator());};FCKToolbar.prototype.Create=function(A){if (this.MainElement){if (this.MainElement.parentNode) this.MainElement.parentNode.removeChild(this.MainElement);this.MainElement=null;};var B=FCKTools.GetElementDocument(A);var e=this.MainElement=B.createElement('table');e.className='TB_Toolbar';e.style.styleFloat=e.style.cssFloat=(FCKLang.Dir=='ltr'?'left':'right');e.dir=FCKLang.Dir;e.cellPadding=0;e.cellSpacing=0;this.RowElement=e.insertRow(-1);var C;if (!this.HideStart){C=this.RowElement.insertCell(-1);C.appendChild(B.createElement('div')).className='TB_Start';};for (var i=0;i<this.Items.length;i++){this.Items[i].Create(this.RowElement.insertCell(-1));};if (!this.HideEnd){C=this.RowElement.insertCell(-1);C.appendChild(B.createElement('div')).className='TB_End';};A.appendChild(e);};function FCKToolbar_Cleanup(){this.MainElement=null;this.RowElement=null;};var FCKToolbarSeparator=function(){};FCKToolbarSeparator.prototype.Create=function(A){FCKTools.AppendElement(A,'div').className='TB_Separator';}
+var FCKToolbarBreak=function(){};FCKToolbarBreak.prototype.Create=function(A){var B=FCKTools.GetElementDocument(A).createElement('div');B.className='TB_Break';B.style.clear=FCKLang.Dir=='rtl'?'left':'right';A.appendChild(B);}
+function FCKToolbarSet_Create(A){var B;var C=A||FCKConfig.ToolbarLocation;switch (C){case 'In':document.getElementById('xToolbarRow').style.display='';B=new FCKToolbarSet(document);break;default:FCK.Events.AttachEvent('OnBlur',FCK_OnBlur);FCK.Events.AttachEvent('OnFocus',FCK_OnFocus);var D;var E=C.match(/^Out:(.+)\((\w+)\)$/);if (E){D=eval('parent.'+E[1]).document.getElementById(E[2]);}else{E=C.match(/^Out:(\w+)$/);if (E) D=parent.document.getElementById(E[1]);};if (!D){alert('Invalid value for "ToolbarLocation"');return this._Init('In');};B=D.__FCKToolbarSet;if (B) break;var F=FCKTools.GetElementDocument(D).createElement('iframe');F.src='javascript:void(0)';F.frameBorder=0;F.width='100%';F.height='10';D.appendChild(F);F.unselectable='on';var G=F.contentWindow.document;G.open();G.write('<html><head><script type="text/javascript"> window.onload = window.onresize = function() { window.frameElement.height = document.body.scrollHeight ; } </script></head><body style="overflow: hidden">'+document.getElementById('xToolbarSpace').innerHTML+'</body></html>');G.close();G.oncontextmenu=FCKTools.CancelEvent;FCKTools.AppendStyleSheet(G,FCKConfig.SkinPath+'fck_editor.css');B=D.__FCKToolbarSet=new FCKToolbarSet(G);B._IFrame=F;if (FCK.IECleanup) FCK.IECleanup.AddItem(D,FCKToolbarSet_Target_Cleanup);};B.CurrentInstance=FCK;FCK.AttachToOnSelectionChange(B.RefreshItemsState);return B;};function FCK_OnBlur(A){var B=A.ToolbarSet;if (B.CurrentInstance==A) B.Disable();};function FCK_OnFocus(A){var B=A.ToolbarSet;var C=A||FCK;B.CurrentInstance.FocusManager.RemoveWindow(B._IFrame.contentWindow);B.CurrentInstance=C;C.FocusManager.AddWindow(B._IFrame.contentWindow,true);B.Enable();};function FCKToolbarSet_Cleanup(){this._TargetElement=null;this._IFrame=null;};function FCKToolbarSet_Target_Cleanup(){this.__FCKToolbarSet=null;};var FCKToolbarSet=function(A){this._Document=A;this._TargetElement=A.getElementById('xToolbar');var B=A.getElementById('xExpandHandle');var C=A.getElementById('xCollapseHandle');B.title=FCKLang.ToolbarExpand;B.onclick=FCKToolbarSet_Expand_OnClick;C.title=FCKLang.ToolbarCollapse;C.onclick=FCKToolbarSet_Collapse_OnClick;if (!FCKConfig.ToolbarCanCollapse||FCKConfig.ToolbarStartExpanded) this.Expand();else this.Collapse();C.style.display=FCKConfig.ToolbarCanCollapse?'':'none';if (FCKConfig.ToolbarCanCollapse) C.style.display='';else A.getElementById('xTBLeftBorder').style.display='';this.Toolbars=[];this.IsLoaded=false;if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKToolbarSet_Cleanup);};function FCKToolbarSet_Expand_OnClick(){FCK.ToolbarSet.Expand();};function FCKToolbarSet_Collapse_OnClick(){FCK.ToolbarSet.Collapse();};FCKToolbarSet.prototype.Expand=function(){this._ChangeVisibility(false);};FCKToolbarSet.prototype.Collapse=function(){this._ChangeVisibility(true);};FCKToolbarSet.prototype._ChangeVisibility=function(A){this._Document.getElementById('xCollapsed').style.display=A?'':'none';this._Document.getElementById('xExpanded').style.display=A?'none':'';if (FCKBrowserInfo.IsGecko){FCKTools.RunFunction(window.onresize);}};FCKToolbarSet.prototype.Load=function(A){this.Name=A;this.Items=[];this.ItemsWysiwygOnly=[];this.ItemsContextSensitive=[];this._TargetElement.innerHTML='';var B=FCKConfig.ToolbarSets[A];if (!B){alert(FCKLang.UnknownToolbarSet.replace(/%1/g,A));return;};this.Toolbars=[];for (var x=0;x<B.length;x++){var C=B[x];if (!C) continue;var D;if (typeof(C)=='string'){if (C=='/') D=new FCKToolbarBreak();}else{D=new FCKToolbar();for (var j=0;j<C.length;j++){var E=C[j];if (E=='-') D.AddSeparator();else{var F=FCKToolbarItems.GetItem(E);if (F){D.AddItem(F);this.Items.push(F);if (!F.SourceView) this.ItemsWysiwygOnly.push(F);if (F.ContextSensitive) this.ItemsContextSensitive.push(F);}}}};D.Create(this._TargetElement);this.Toolbars[this.Toolbars.length]=D;};FCKTools.DisableSelection(this._Document.getElementById('xCollapseHandle').parentNode);if (FCK.Status!=2) FCK.Events.AttachEvent('OnStatusChange',this.RefreshModeState);else this.RefreshModeState();this.IsLoaded=true;this.IsEnabled=true;FCKTools.RunFunction(this.OnLoad);};FCKToolbarSet.prototype.Enable=function(){if (this.IsEnabled) return;this.IsEnabled=true;var A=this.Items;for (var i=0;i<A.length;i++) A[i].RefreshState();};FCKToolbarSet.prototype.Disable=function(){if (!this.IsEnabled) return;this.IsEnabled=false;var A=this.Items;for (var i=0;i<A.length;i++) A[i].Disable();};FCKToolbarSet.prototype.RefreshModeState=function(A){if (FCK.Status!=2) return;var B=A?A.ToolbarSet:this;var C=B.ItemsWysiwygOnly;if (FCK.EditMode==0){for (var i=0;i<C.length;i++) C[i].Enable();B.RefreshItemsState(A);}else{B.RefreshItemsState(A);for (var j=0;j<C.length;j++) C[j].Disable();}};FCKToolbarSet.prototype.RefreshItemsState=function(A){var B=(A?A.ToolbarSet:this).ItemsContextSensitive;for (var i=0;i<B.length;i++) B[i].RefreshState();};
+var FCKDialog={};FCKDialog.OpenDialog=function(A,B,C,D,E,F,G,H){var I={};I.Title=B;I.Page=C;I.Editor=window;I.CustomValue=F;var J=FCKConfig.BasePath+'fckdialog.html';this.Show(I,A,J,D,E,G,H);};
+FCKDialog.Show=function(A,B,C,D,E,F,G){if (!F) F=window;var H='help:no;scroll:no;status:no;resizable:'+(G?'yes':'no')+';dialogWidth:'+D+'px;dialogHeight:'+E+'px';FCKFocusManager.Lock();var I='B';try{I=F.showModalDialog(C,A,H);}catch(e) {};if ('B'===I) alert(FCKLang.DialogBlocked);FCKFocusManager.Unlock();};
+var FCKMenuItem=function(A,B,C,D,E){this.Name=B;this.Label=C||B;this.IsDisabled=E;this.Icon=new FCKIcon(D);this.SubMenu=new FCKMenuBlockPanel();this.SubMenu.Parent=A;this.SubMenu.OnClick=FCKTools.CreateEventListener(FCKMenuItem_SubMenu_OnClick,this);if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKMenuItem_Cleanup);};FCKMenuItem.prototype.AddItem=function(A,B,C,D){this.HasSubMenu=true;return this.SubMenu.AddItem(A,B,C,D);};FCKMenuItem.prototype.AddSeparator=function(){this.SubMenu.AddSeparator();};FCKMenuItem.prototype.Create=function(A){var B=this.HasSubMenu;var C=FCKTools.GetElementDocument(A);var r=this.MainElement=A.insertRow(-1);r.className=this.IsDisabled?'MN_Item_Disabled':'MN_Item';if (!this.IsDisabled){FCKTools.AddEventListenerEx(r,'mouseover',FCKMenuItem_OnMouseOver,[this]);FCKTools.AddEventListenerEx(r,'click',FCKMenuItem_OnClick,[this]);if (!B) FCKTools.AddEventListenerEx(r,'mouseout',FCKMenuItem_OnMouseOut,[this]);};var D=r.insertCell(-1);D.className='MN_Icon';D.appendChild(this.Icon.CreateIconElement(C));D=r.insertCell(-1);D.className='MN_Label';D.noWrap=true;D.appendChild(C.createTextNode(this.Label));D=r.insertCell(-1);if (B){D.className='MN_Arrow';var E=D.appendChild(C.createElement('IMG'));E.src=FCK_IMAGES_PATH+'arrow_'+FCKLang.Dir+'.gif';E.width=4;E.height=7;this.SubMenu.Create();this.SubMenu.Panel.OnHide=FCKTools.CreateEventListener(FCKMenuItem_SubMenu_OnHide,this);}};FCKMenuItem.prototype.Activate=function(){this.MainElement.className='MN_Item_Over';if (this.HasSubMenu){this.SubMenu.Show(this.MainElement.offsetWidth+2,-2,this.MainElement);};FCKTools.RunFunction(this.OnActivate,this);};FCKMenuItem.prototype.Deactivate=function(){this.MainElement.className='MN_Item';if (this.HasSubMenu) this.SubMenu.Hide();};function FCKMenuItem_SubMenu_OnClick(A,B){FCKTools.RunFunction(B.OnClick,B,[A]);};function FCKMenuItem_SubMenu_OnHide(A){A.Deactivate();};function FCKMenuItem_OnClick(A,B){if (B.HasSubMenu) B.Activate();else{B.Deactivate();FCKTools.RunFunction(B.OnClick,B,[B]);}};function FCKMenuItem_OnMouseOver(A,B){B.Activate();};function FCKMenuItem_OnMouseOut(A,B){B.Deactivate();};function FCKMenuItem_Cleanup(){this.MainElement=null;}
+var FCKMenuBlock=function(){this._Items=[];};FCKMenuBlock.prototype.Count=function(){return this._Items.length;};FCKMenuBlock.prototype.AddItem=function(A,B,C,D){var E=new FCKMenuItem(this,A,B,C,D);E.OnClick=FCKTools.CreateEventListener(FCKMenuBlock_Item_OnClick,this);E.OnActivate=FCKTools.CreateEventListener(FCKMenuBlock_Item_OnActivate,this);this._Items.push(E);return E;};FCKMenuBlock.prototype.AddSeparator=function(){this._Items.push(new FCKMenuSeparator());};FCKMenuBlock.prototype.RemoveAllItems=function(){this._Items=[];var A=this._ItemsTable;if (A){while (A.rows.length>0) A.deleteRow(0);}};FCKMenuBlock.prototype.Create=function(A){if (!this._ItemsTable){if (FCK.IECleanup) FCK.IECleanup.AddItem(this,FCKMenuBlock_Cleanup);this._Window=FCKTools.GetElementWindow(A);var B=FCKTools.GetElementDocument(A);var C=A.appendChild(B.createElement('table'));C.cellPadding=0;C.cellSpacing=0;FCKTools.DisableSelection(C);var D=C.insertRow(-1).insertCell(-1);D.className='MN_Menu';var E=this._ItemsTable=D.appendChild(B.createElement('table'));E.cellPadding=0;E.cellSpacing=0;};for (var i=0;i<this._Items.length;i++) this._Items[i].Create(this._ItemsTable);};function FCKMenuBlock_Item_OnClick(A,B){FCKTools.RunFunction(B.OnClick,B,[A]);};function FCKMenuBlock_Item_OnActivate(A){var B=A._ActiveItem;if (B&&B!=this){if (!FCKBrowserInfo.IsIE&&B.HasSubMenu&&!this.HasSubMenu) A._Window.focus();B.Deactivate();};A._ActiveItem=this;};function FCKMenuBlock_Cleanup(){this._Window=null;this._ItemsTable=null;};var FCKMenuSeparator=function(){};FCKMenuSeparator.prototype.Create=function(A){var B=FCKTools.GetElementDocument(A);var r=A.insertRow(-1);var C=r.insertCell(-1);C.className='MN_Separator MN_Icon';C=r.insertCell(-1);C.className='MN_Separator';C.appendChild(B.createElement('DIV')).className='MN_Separator_Line';C=r.insertCell(-1);C.className='MN_Separator';C.appendChild(B.createElement('DIV')).className='MN_Separator_Line';}
+var FCKMenuBlockPanel=function(){FCKMenuBlock.call(this);};FCKMenuBlockPanel.prototype=new FCKMenuBlock();FCKMenuBlockPanel.prototype.Create=function(){var A=this.Panel=(this.Parent&&this.Parent.Panel?this.Parent.Panel.CreateChildPanel():new FCKPanel());A.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');FCKMenuBlock.prototype.Create.call(this,A.MainNode);};FCKMenuBlockPanel.prototype.Show=function(x,y,A){if (!this.Panel.CheckIsOpened()) this.Panel.Show(x,y,A);};FCKMenuBlockPanel.prototype.Hide=function(){if (this.Panel.CheckIsOpened()) this.Panel.Hide();}
+var FCKContextMenu=function(A,B){this.CtrlDisable=false;var C=this._Panel=new FCKPanel(A);C.AppendStyleSheet(FCKConfig.SkinPath+'fck_editor.css');C.IsContextMenu=true;if (FCKBrowserInfo.IsGecko) C.Document.addEventListener('draggesture',function(e) {e.preventDefault();return false;},true);var D=this._MenuBlock=new FCKMenuBlock();D.Panel=C;D.OnClick=FCKTools.CreateEventListener(FCKContextMenu_MenuBlock_OnClick,this);this._Redraw=true;};FCKContextMenu.prototype.SetMouseClickWindow=function(A){if (!FCKBrowserInfo.IsIE){this._Document=A.document;this._Document.addEventListener('contextmenu',FCKContextMenu_Document_OnContextMenu,false);}};FCKContextMenu.prototype.AddItem=function(A,B,C,D){var E=this._MenuBlock.AddItem(A,B,C,D);this._Redraw=true;return E;};FCKContextMenu.prototype.AddSeparator=function(){this._MenuBlock.AddSeparator();this._Redraw=true;};FCKContextMenu.prototype.RemoveAllItems=function(){this._MenuBlock.RemoveAllItems();this._Redraw=true;};FCKContextMenu.prototype.AttachToElement=function(A){if (FCKBrowserInfo.IsIE) FCKTools.AddEventListenerEx(A,'contextmenu',FCKContextMenu_AttachedElement_OnContextMenu,this);else A._FCKContextMenu=this;};function FCKContextMenu_Document_OnContextMenu(e){var A=e.target;while (A){if (A._FCKContextMenu){if (A._FCKContextMenu.CtrlDisable&&(e.ctrlKey||e.metaKey)) return true;FCKTools.CancelEvent(e);FCKContextMenu_AttachedElement_OnContextMenu(e,A._FCKContextMenu,A);};A=A.parentNode;}};function FCKContextMenu_AttachedElement_OnContextMenu(A,B,C){if (B.CtrlDisable&&(A.ctrlKey||A.metaKey)) return true;var D=C||this;if (B.OnBeforeOpen) B.OnBeforeOpen.call(B,D);if (B._MenuBlock.Count()==0) return false;if (B._Redraw){B._MenuBlock.Create(B._Panel.MainNode);B._Redraw=false;};FCKTools.DisableSelection(B._Panel.Document.body);B._Panel.Show(A.pageX||A.screenX,A.pageY||A.screenY,A.currentTarget||null);return false;};function FCKContextMenu_MenuBlock_OnClick(A,B){B._Panel.Hide();FCKTools.RunFunction(B.OnItemClick,B,A);}
+FCK.ContextMenu={};FCK.ContextMenu.Listeners=[];FCK.ContextMenu.RegisterListener=function(A){if (A) this.Listeners.push(A);};function FCK_ContextMenu_Init(){var A=FCK.ContextMenu._InnerContextMenu=new FCKContextMenu(FCKBrowserInfo.IsIE?window:window.parent,FCKLang.Dir);A.CtrlDisable=FCKConfig.BrowserContextMenuOnCtrl;A.OnBeforeOpen=FCK_ContextMenu_OnBeforeOpen;A.OnItemClick=FCK_ContextMenu_OnItemClick;var B=FCK.ContextMenu;for (var i=0;i<FCKConfig.ContextMenu.length;i++) B.RegisterListener(FCK_ContextMenu_GetListener(FCKConfig.ContextMenu[i]));};function FCK_ContextMenu_GetListener(A){switch (A){case 'Generic':return {AddItems:function(menu,tag,tagName){menu.AddItem('Cut',FCKLang.Cut,7,FCKCommands.GetCommand('Cut').GetState()==-1);menu.AddItem('Copy',FCKLang.Copy,8,FCKCommands.GetCommand('Copy').GetState()==-1);menu.AddItem('Paste',FCKLang.Paste,9,FCKCommands.GetCommand('Paste').GetState()==-1);}};case 'Table':return {AddItems:function(menu,tag,tagName){var B=(tagName=='TABLE');var C=(!B&&FCKSelection.HasAncestorNode('TABLE'));if (C){menu.AddSeparator();var D=menu.AddItem('Cell',FCKLang.CellCM);D.AddItem('TableInsertCell',FCKLang.InsertCell,58);D.AddItem('TableDeleteCells',FCKLang.DeleteCells,59);D.AddItem('TableMergeCells',FCKLang.MergeCells,60);D.AddItem('TableSplitCell',FCKLang.SplitCell,61);D.AddSeparator();D.AddItem('TableCellProp',FCKLang.CellProperties,57);menu.AddSeparator();D=menu.AddItem('Row',FCKLang.RowCM);D.AddItem('TableInsertRow',FCKLang.InsertRow,62);D.AddItem('TableDeleteRows',FCKLang.DeleteRows,63);menu.AddSeparator();D=menu.AddItem('Column',FCKLang.ColumnCM);D.AddItem('TableInsertColumn',FCKLang.InsertColumn,64);D.AddItem('TableDeleteColumns',FCKLang.DeleteColumns,65);};if (B||C){menu.AddSeparator();menu.AddItem('TableDelete',FCKLang.TableDelete);menu.AddItem('TableProp',FCKLang.TableProperties,39);}}};case 'Link':return {AddItems:function(menu,tag,tagName){var E=(tagName=='A'||FCKSelection.HasAncestorNode('A'));if (E||FCK.GetNamedCommandState('Unlink')!=-1){var F=FCKSelection.MoveToAncestorNode('A');var G=(F&&F.name.length>0&&F.href.length==0);if (G) return;menu.AddSeparator();if (E) menu.AddItem('Link',FCKLang.EditLink,34);menu.AddItem('Unlink',FCKLang.RemoveLink,35);}}};case 'Image':return {AddItems:function(menu,tag,tagName){if (tagName=='IMG'&&!tag.getAttribute('_fckfakelement')){menu.AddSeparator();menu.AddItem('Image',FCKLang.ImageProperties,37);}}};case 'Anchor':return {AddItems:function(menu,tag,tagName){var F=FCKSelection.MoveToAncestorNode('A');var G=(F&&F.name.length>0);if (G||(tagName=='IMG'&&tag.getAttribute('_fckanchor'))){menu.AddSeparator();menu.AddItem('Anchor',FCKLang.AnchorProp,36);}}};case 'Flash':return {AddItems:function(menu,tag,tagName){if (tagName=='IMG'&&tag.getAttribute('_fckflash')){menu.AddSeparator();menu.AddItem('Flash',FCKLang.FlashProperties,38);}}};case 'Form':return {AddItems:function(menu,tag,tagName){if (FCKSelection.HasAncestorNode('FORM')){menu.AddSeparator();menu.AddItem('Form',FCKLang.FormProp,48);}}};case 'Checkbox':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&tag.type=='checkbox'){menu.AddSeparator();menu.AddItem('Checkbox',FCKLang.CheckboxProp,49);}}};case 'Radio':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&tag.type=='radio'){menu.AddSeparator();menu.AddItem('Radio',FCKLang.RadioButtonProp,50);}}};case 'TextField':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&(tag.type=='text'||tag.type=='password')){menu.AddSeparator();menu.AddItem('TextField',FCKLang.TextFieldProp,51);}}};case 'HiddenField':return {AddItems:function(menu,tag,tagName){if (tagName=='IMG'&&tag.getAttribute('_fckinputhidden')){menu.AddSeparator();menu.AddItem('HiddenField',FCKLang.HiddenFieldProp,56);}}};case 'ImageButton':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&tag.type=='image'){menu.AddSeparator();menu.AddItem('ImageButton',FCKLang.ImageButtonProp,55);}}};case 'Button':return {AddItems:function(menu,tag,tagName){if (tagName=='INPUT'&&(tag.type=='button'||tag.type=='submit'||tag.type=='reset')){menu.AddSeparator();menu.AddItem('Button',FCKLang.ButtonProp,54);}}};case 'Select':return {AddItems:function(menu,tag,tagName){if (tagName=='SELECT'){menu.AddSeparator();menu.AddItem('Select',FCKLang.SelectionFieldProp,53);}}};case 'Textarea':return {AddItems:function(menu,tag,tagName){if (tagName=='TEXTAREA'){menu.AddSeparator();menu.AddItem('Textarea',FCKLang.TextareaProp,52);}}};case 'BulletedList':return {AddItems:function(menu,tag,tagName){if (FCKSelection.HasAncestorNode('UL')){menu.AddSeparator();menu.AddItem('BulletedList',FCKLang.BulletedListProp,27);}}};case 'NumberedList':return {AddItems:function(menu,tag,tagName){if (FCKSelection.HasAncestorNode('OL')){menu.AddSeparator();menu.AddItem('NumberedList',FCKLang.NumberedListProp,26);}}};};return null;};function FCK_ContextMenu_OnBeforeOpen(){FCK.Events.FireEvent('OnSelectionChange');var A,sTagName;if ((A=FCKSelection.GetSelectedElement())) sTagName=A.tagName;var B=FCK.ContextMenu._InnerContextMenu;B.RemoveAllItems();var C=FCK.ContextMenu.Listeners;for (var i=0;i<C.length;i++) C[i].AddItems(B,A,sTagName);};function FCK_ContextMenu_OnItemClick(A){FCK.Focus();FCKCommands.GetCommand(A.Name).Execute();};
+var FCKPlugin=function(A,B,C){this.Name=A;this.BasePath=C?C:FCKConfig.PluginsPath;this.Path=this.BasePath+A+'/';if (!B||B.length==0) this.AvailableLangs=[];else this.AvailableLangs=B.split(',');};FCKPlugin.prototype.Load=function(){if (this.AvailableLangs.length>0){var A;if (this.AvailableLangs.IndexOf(FCKLanguageManager.ActiveLanguage.Code)>=0) A=FCKLanguageManager.ActiveLanguage.Code;else A=this.AvailableLangs[0];LoadScript(this.Path+'lang/'+A+'.js');};LoadScript(this.Path+'fckplugin.js');}
+var FCKPlugins=FCK.Plugins={};FCKPlugins.ItemsCount=0;FCKPlugins.Items={};FCKPlugins.Load=function(){var A=FCKPlugins.Items;for (var i=0;i<FCKConfig.Plugins.Items.length;i++){var B=FCKConfig.Plugins.Items[i];var C=A[B[0]]=new FCKPlugin(B[0],B[1],B[2]);FCKPlugins.ItemsCount++;};for (var s in A) A[s].Load();FCKPlugins.Load=null;}
diff --git a/httemplate/elements/fckeditor/editor/lang/_getfontformat.html b/httemplate/elements/fckeditor/editor/lang/_getfontformat.html
new file mode 100644
index 0000000..a408642
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/_getfontformat.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html>
+ <head>
+ <title></title>
+ </head>
+ <script language="javascript">
+
+window.onload = function()
+{
+ var oRange = document.selection.createRange() ;
+
+ var sNormal ;
+ var sFormats = '' ;
+ for ( var i = 1 ; i <= 9 ; i++ )
+ {
+ oRange.moveToElementText( document.getElementById( 'x' + i ) ) ;
+ sFormats += oRange.queryCommandValue( 'FormatBlock' ) ;
+ if ( i == 1 )
+ sNormal = sFormats ;
+ sFormats += ';' ;
+ }
+
+ document.getElementById('xFontFormats').innerHTML = sFormats + sNormal + ' (DIV)' ;
+}
+ </script>
+ <body>
+ <table width="70%" align="center">
+ <tr>
+ <td>
+ <h3>FontFormats Localization</h3>
+ <p>
+ IE has some limits when handling the "Font Format". It actually uses localized
+ strings to retrieve the current format value. This makes it very difficult to
+ make a system that works on every single computer in the world.
+ </p>
+ <p>
+ With FCKeditor, this problem impacts in the "Format" toolbar command that
+ doesn't reflects the format of the current cursor position.
+ </p>
+ <p>
+ There is only one way to make it work. We must localize FCKeditor using the
+ strings used by IE. In this way, we will have the expected behavior at least
+ when using FCKeditor in the same language as the browser. So, when localizing
+ FCKeditor, go to a computer with IE in the target language, open this page and
+ use the following string to the "FontFormats" value:
+ </p>
+ <div style="white-space: nowrap">
+ FontFormats : "<span id="xFontFormats" style="COLOR: #000099"></span>",
+ </div>
+ </td>
+ </tr>
+ </table>
+ <div style="DISPLAY: none">
+ <p id="x1">&nbsp;</p>
+ <pre id="x2">&nbsp;</pre>
+ <address id="x3">&nbsp;</address>
+ <h1 id="x4">&nbsp;</h1>
+ <h2 id="x5">&nbsp;</h2>
+ <h3 id="x6">&nbsp;</h3>
+ <h4 id="x7">&nbsp;</h4>
+ <h5 id="x8">&nbsp;</h5>
+ <h6 id="x9">&nbsp;</h6>
+ </div>
+ </body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/lang/_translationstatus.txt b/httemplate/elements/fckeditor/editor/lang/_translationstatus.txt
new file mode 100644
index 0000000..a53ea8f
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/_translationstatus.txt
@@ -0,0 +1,76 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Translations Status.
+ */
+
+af.js Found: 401 Missing: 1
+ar.js Found: 401 Missing: 1
+bg.js Found: 378 Missing: 24
+bn.js Found: 386 Missing: 16
+bs.js Found: 230 Missing: 172
+ca.js Found: 402 Missing: 0
+cs.js Found: 400 Missing: 2
+da.js Found: 386 Missing: 16
+de.js Found: 401 Missing: 1
+el.js Found: 401 Missing: 1
+en-au.js Found: 402 Missing: 0
+en-ca.js Found: 402 Missing: 0
+en-uk.js Found: 402 Missing: 0
+eo.js Found: 350 Missing: 52
+es.js Found: 386 Missing: 16
+et.js Found: 402 Missing: 0
+eu.js Found: 386 Missing: 16
+fa.js Found: 402 Missing: 0
+fi.js Found: 402 Missing: 0
+fo.js Found: 401 Missing: 1
+fr.js Found: 401 Missing: 1
+gl.js Found: 386 Missing: 16
+he.js Found: 402 Missing: 0
+hi.js Found: 401 Missing: 1
+hr.js Found: 401 Missing: 1
+hu.js Found: 401 Missing: 1
+it.js Found: 401 Missing: 1
+ja.js Found: 401 Missing: 1
+km.js Found: 376 Missing: 26
+ko.js Found: 373 Missing: 29
+lt.js Found: 381 Missing: 21
+lv.js Found: 386 Missing: 16
+mn.js Found: 230 Missing: 172
+ms.js Found: 356 Missing: 46
+nb.js Found: 400 Missing: 2
+nl.js Found: 401 Missing: 1
+no.js Found: 400 Missing: 2
+pl.js Found: 386 Missing: 16
+pt-br.js Found: 401 Missing: 1
+pt.js Found: 386 Missing: 16
+ro.js Found: 400 Missing: 2
+ru.js Found: 401 Missing: 1
+sk.js Found: 401 Missing: 1
+sl.js Found: 378 Missing: 24
+sr-latn.js Found: 373 Missing: 29
+sr.js Found: 373 Missing: 29
+sv.js Found: 401 Missing: 1
+th.js Found: 398 Missing: 4
+tr.js Found: 401 Missing: 1
+uk.js Found: 402 Missing: 0
+vi.js Found: 401 Missing: 1
+zh-cn.js Found: 401 Missing: 1
+zh.js Found: 401 Missing: 1
diff --git a/httemplate/elements/fckeditor/editor/lang/af.js b/httemplate/elements/fckeditor/editor/lang/af.js
new file mode 100644
index 0000000..857dc3e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/af.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Afrikaans language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Vou Gereedskaps balk toe",
+ToolbarExpand : "Vou Gereedskaps balk oop",
+
+// Toolbar Items and Context Menu
+Save : "Bewaar",
+NewPage : "Nuwe Bladsy",
+Preview : "Voorskou",
+Cut : "Uitsny ",
+Copy : "Kopieer",
+Paste : "Byvoeg",
+PasteText : "Slegs inhoud byvoeg",
+PasteWord : "Van Word af byvoeg",
+Print : "Druk",
+SelectAll : "Selekteer alles",
+RemoveFormat : "Formaat verweider",
+InsertLinkLbl : "Skakel",
+InsertLink : "Skakel byvoeg/verander",
+RemoveLink : "Skakel verweider",
+Anchor : "Plekhouer byvoeg/verander",
+InsertImageLbl : "Beeld",
+InsertImage : "Beeld byvoeg/verander",
+InsertFlashLbl : "Flash",
+InsertFlash : "Flash byvoeg/verander",
+InsertTableLbl : "Tabel",
+InsertTable : "Tabel byvoeg/verander",
+InsertLineLbl : "Lyn",
+InsertLine : "Horisontale lyn byvoeg",
+InsertSpecialCharLbl: "Spesiaale karakter",
+InsertSpecialChar : "Spesiaale Karakter byvoeg",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Smiley byvoeg",
+About : "Meer oor FCKeditor",
+Bold : "Vet",
+Italic : "Skuins",
+Underline : "Onderstreep",
+StrikeThrough : "Gestreik",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Links rig",
+CenterJustify : "Rig Middel",
+RightJustify : "Regs rig",
+BlockJustify : "Blok paradeer",
+DecreaseIndent : "Paradeering verkort",
+IncreaseIndent : "Paradeering verleng",
+Undo : "Ont-skep",
+Redo : "Her-skep",
+NumberedListLbl : "Genommerde lys",
+NumberedList : "Genommerde lys byvoeg/verweider",
+BulletedListLbl : "Gepunkte lys",
+BulletedList : "Gepunkte lys byvoeg/verweider",
+ShowTableBorders : "Wys tabel kante",
+ShowDetails : "Wys informasie",
+Style : "Styl",
+FontFormat : "Karakter formaat",
+Font : "Karakters",
+FontSize : "Karakter grote",
+TextColor : "Karakter kleur",
+BGColor : "Agtergrond kleur",
+Source : "Source",
+Find : "Vind",
+Replace : "Vervang",
+SpellCheck : "Spelling nagaan",
+UniversalKeyboard : "Universeele Sleutelbord",
+PageBreakLbl : "Bladsy breek",
+PageBreak : "Bladsy breek byvoeg",
+
+Form : "Form",
+Checkbox : "HakBox",
+RadioButton : "PuntBox",
+TextField : "Byvoegbare karakter strook",
+Textarea : "Byvoegbare karakter area",
+HiddenField : "Blinde strook",
+Button : "Knop",
+SelectionField : "Opklapbare keuse strook",
+ImageButton : "Beeld knop",
+
+FitWindow : "Maksimaliseer venster grote",
+
+// Context Menu
+EditLink : "Verander skakel",
+CellCM : "Cell",
+RowCM : "Ry",
+ColumnCM : "Kolom",
+InsertRow : "Ry byvoeg",
+DeleteRows : "Ry verweider",
+InsertColumn : "Kolom byvoeg",
+DeleteColumns : "Kolom verweider",
+InsertCell : "Cell byvoeg",
+DeleteCells : "Cell verweider",
+MergeCells : "Cell verenig",
+SplitCell : "Cell verdeel",
+TableDelete : "Tabel verweider",
+CellProperties : "Cell eienskappe",
+TableProperties : "Tabel eienskappe",
+ImageProperties : "Beeld eienskappe",
+FlashProperties : "Flash eienskappe",
+
+AnchorProp : "Plekhouer eienskappe",
+ButtonProp : "Knop eienskappe",
+CheckboxProp : "HakBox eienskappe",
+HiddenFieldProp : "Blinde strook eienskappe",
+RadioButtonProp : "PuntBox eienskappe",
+ImageButtonProp : "Beeld knop eienskappe",
+TextFieldProp : "Karakter strook eienskappe",
+SelectionFieldProp : "Opklapbare keuse strook eienskappe",
+TextareaProp : "Karakter area eienskappe",
+FormProp : "Form eienskappe",
+
+FontFormats : "Normaal;Geformateerd;Adres;Opskrif 1;Opskrif 2;Opskrif 3;Opskrif 4;Opskrif 5;Opskrif 6;Normaal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML word verarbeit. U geduld asseblief...",
+Done : "Kompleet",
+PasteWordConfirm : "Die informasie wat U probeer byvoeg is warskynlik van Word. Wil U dit reinig voor die byvoeging?",
+NotCompatiblePaste : "Die instruksie is beskikbaar vir Internet Explorer weergawe 5.5 of hor. Wil U dir byvoeg sonder reiniging?",
+UnknownToolbarItem : "Unbekende gereedskaps balk item \"%1\"",
+UnknownCommand : "Unbekende instruksie naam \"%1\"",
+NotImplemented : "Instruksie is nie geimplementeer nie.",
+UnknownToolbarSet : "Gereedskaps balk \"%1\" bestaan nie",
+NoActiveX : "U browser sekuriteit instellings kan die funksies van die editor behinder. U moet die opsie \"Run ActiveX controls and plug-ins\" aktiveer. U ondervinding mag problematies geskiet of sekere funksionaliteit mag verhinder word.",
+BrowseServerBlocked : "Die vorraad venster word geblok! Verseker asseblief dat U die \"popup blocker\" instelling verander.",
+DialogBlocked : "Die dialoog venster vir verdere informasie word geblok. De-aktiveer asseblief die \"popup blocker\" instellings wat dit behinder.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Kanseleer",
+DlgBtnClose : "Sluit",
+DlgBtnBrowseServer : "Server deurblaai",
+DlgAdvancedTag : "Ingewikkeld",
+DlgOpOther : "<Ander>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Voeg asseblief die URL in",
+
+// General Dialogs Labels
+DlgGenNotSet : "<geen instelling>",
+DlgGenId : "Id",
+DlgGenLangDir : "Taal rigting",
+DlgGenLangDirLtr : "Links na regs (LTR)",
+DlgGenLangDirRtl : "Regs na links (RTL)",
+DlgGenLangCode : "Taal kode",
+DlgGenAccessKey : "Toegang sleutel",
+DlgGenName : "Naam",
+DlgGenTabIndex : "Tab Index",
+DlgGenLongDescr : "Lang beskreiwing URL",
+DlgGenClass : "Skakel Tiepe",
+DlgGenTitle : "Voorbeveelings Titel",
+DlgGenContType : "Voorbeveelings inhoud soort",
+DlgGenLinkCharset : "Geskakelde voorbeeld karakterstel",
+DlgGenStyle : "Styl",
+
+// Image Dialog
+DlgImgTitle : "Beeld eienskappe",
+DlgImgInfoTab : "Beeld informasie",
+DlgImgBtnUpload : "Stuur dit na die Server",
+DlgImgURL : "URL",
+DlgImgUpload : "Uplaai",
+DlgImgAlt : "Alternatiewe beskrywing",
+DlgImgWidth : "Weidte",
+DlgImgHeight : "Hoogde",
+DlgImgLockRatio : "Behou preporsie",
+DlgBtnResetSize : "Herstel groote",
+DlgImgBorder : "Kant",
+DlgImgHSpace : "HSpasie",
+DlgImgVSpace : "VSpasie",
+DlgImgAlign : "Paradeer",
+DlgImgAlignLeft : "Links",
+DlgImgAlignAbsBottom: "Abs Onder",
+DlgImgAlignAbsMiddle: "Abs Middel",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Onder",
+DlgImgAlignMiddle : "Middel",
+DlgImgAlignRight : "Regs",
+DlgImgAlignTextTop : "Text Bo",
+DlgImgAlignTop : "Bo",
+DlgImgPreview : "Voorskou",
+DlgImgAlertUrl : "Voeg asseblief Beeld URL in.",
+DlgImgLinkTab : "Skakel",
+
+// Flash Dialog
+DlgFlashTitle : "Flash eienskappe",
+DlgFlashChkPlay : "Automaties Speel",
+DlgFlashChkLoop : "Herhaling",
+DlgFlashChkMenu : "Laat Flash Menu toe",
+DlgFlashScale : "Scale",
+DlgFlashScaleAll : "Wys alles",
+DlgFlashScaleNoBorder : "Geen kante",
+DlgFlashScaleFit : "Presiese pas",
+
+// Link Dialog
+DlgLnkWindowTitle : "Skakel",
+DlgLnkInfoTab : "Skakel informasie",
+DlgLnkTargetTab : "Mikpunt",
+
+DlgLnkType : "Skakel soort",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Skakel na plekhouers in text",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<ander>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Kies 'n plekhouer",
+DlgLnkAnchorByName : "Volgens plekhouer naam",
+DlgLnkAnchorById : "Volgens element Id",
+DlgLnkNoAnchors : "<Geen plekhouers beskikbaar in dokument>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail Adres",
+DlgLnkEMailSubject : "Boodskap Opskrif",
+DlgLnkEMailBody : "Boodskap Inhoud",
+DlgLnkUpload : "Oplaai",
+DlgLnkBtnUpload : "Stuur na Server",
+
+DlgLnkTarget : "Mikpunt",
+DlgLnkTargetFrame : "<raam>",
+DlgLnkTargetPopup : "<popup venster>",
+DlgLnkTargetBlank : "Nuwe Venster (_blank)",
+DlgLnkTargetParent : "Vorige Venster (_parent)",
+DlgLnkTargetSelf : "Selfde Venster (_self)",
+DlgLnkTargetTop : "Boonste Venster (_top)",
+DlgLnkTargetFrameName : "Mikpunt Venster Naam",
+DlgLnkPopWinName : "Popup Venster Naam",
+DlgLnkPopWinFeat : "Popup Venster Geaartheid",
+DlgLnkPopResize : "Verstelbare Groote",
+DlgLnkPopLocation : "Adres Balk",
+DlgLnkPopMenu : "Menu Balk",
+DlgLnkPopScroll : "Gleibalkstuk",
+DlgLnkPopStatus : "Status Balk",
+DlgLnkPopToolbar : "Gereedskap Balk",
+DlgLnkPopFullScrn : "Voll Skerm (IE)",
+DlgLnkPopDependent : "Afhanklik (Netscape)",
+DlgLnkPopWidth : "Weite",
+DlgLnkPopHeight : "Hoogde",
+DlgLnkPopLeft : "Links Posisie",
+DlgLnkPopTop : "Bo Posisie",
+
+DlnLnkMsgNoUrl : "Voeg asseblief die URL in",
+DlnLnkMsgNoEMail : "Voeg asseblief die e-mail adres in",
+DlnLnkMsgNoAnchor : "Kies asseblief 'n plekhouer",
+DlnLnkMsgInvPopName : "Die popup naam moet begin met alphabetiese karakters sonder spasies.",
+
+// Color Dialog
+DlgColorTitle : "Kies Kleur",
+DlgColorBtnClear : "Maak skoon",
+DlgColorHighlight : "Highlight",
+DlgColorSelected : "Geselekteer",
+
+// Smiley Dialog
+DlgSmileyTitle : "Voeg Smiley by",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Kies spesiale karakter",
+
+// Table Dialog
+DlgTableTitle : "Tabel eienskappe",
+DlgTableRows : "Reie",
+DlgTableColumns : "Kolome",
+DlgTableBorder : "Kant groote",
+DlgTableAlign : "Parideering",
+DlgTableAlignNotSet : "<geen instelling>",
+DlgTableAlignLeft : "Links",
+DlgTableAlignCenter : "Middel",
+DlgTableAlignRight : "Regs",
+DlgTableWidth : "Weite",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "percent",
+DlgTableHeight : "Hoogde",
+DlgTableCellSpace : "Cell spasieering",
+DlgTableCellPad : "Cell buffer",
+DlgTableCaption : "Beskreiwing",
+DlgTableSummary : "Opsomming",
+
+// Table Cell Dialog
+DlgCellTitle : "Cell eienskappe",
+DlgCellWidth : "Weite",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "percent",
+DlgCellHeight : "Hoogde",
+DlgCellWordWrap : "Woord Wrap",
+DlgCellWordWrapNotSet : "<geen instelling>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nee",
+DlgCellHorAlign : "Horisontale rigting",
+DlgCellHorAlignNotSet : "<geen instelling>",
+DlgCellHorAlignLeft : "Links",
+DlgCellHorAlignCenter : "Middel",
+DlgCellHorAlignRight: "Regs",
+DlgCellVerAlign : "Vertikale rigting",
+DlgCellVerAlignNotSet : "<geen instelling>",
+DlgCellVerAlignTop : "Bo",
+DlgCellVerAlignMiddle : "Middel",
+DlgCellVerAlignBottom : "Onder",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Rei strekking",
+DlgCellCollSpan : "Kolom strekking",
+DlgCellBackColor : "Agtergrond Kleur",
+DlgCellBorderColor : "Kant Kleur",
+DlgCellBtnSelect : "Keuse...",
+
+// Find Dialog
+DlgFindTitle : "Vind",
+DlgFindFindBtn : "Vind",
+DlgFindNotFoundMsg : "Die gespesifiseerde karakters word nie gevind nie.",
+
+// Replace Dialog
+DlgReplaceTitle : "Vervang",
+DlgReplaceFindLbl : "Soek wat:",
+DlgReplaceReplaceLbl : "Vervang met:",
+DlgReplaceCaseChk : "Vergelyk karakter skryfweise",
+DlgReplaceReplaceBtn : "Vervang",
+DlgReplaceReplAllBtn : "Vervang alles",
+DlgReplaceWordChk : "Vergelyk komplete woord",
+
+// Paste Operations / Dialog
+PasteErrorCut : "U browser se sekuriteit instelling behinder die uitsny aksie. Gebruik asseblief die sleutel kombenasie(Ctrl+X).",
+PasteErrorCopy : "U browser se sekuriteit instelling behinder die kopieerings aksie. Gebruik asseblief die sleutel kombenasie(Ctrl+C).",
+
+PasteAsText : "Voeg slegs karakters by",
+PasteFromWord : "Byvoeging uit Word",
+
+DlgPasteMsg2 : "Voeg asseblief die inhoud in die gegewe box by met sleutel kombenasie(<STRONG>Ctrl+V</STRONG>) en druk <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignoreer karakter soort defenisies",
+DlgPasteRemoveStyles : "Verweider Styl defenisies",
+DlgPasteCleanBox : "Maak Box Skoon",
+
+// Color Picker
+ColorAutomatic : "Automaties",
+ColorMoreColors : "Meer Kleure...",
+
+// Document Properties
+DocProps : "Dokument Eienskappe",
+
+// Anchor Dialog
+DlgAnchorTitle : "Plekhouer Eienskappe",
+DlgAnchorName : "Plekhouer Naam",
+DlgAnchorErrorName : "Voltooi die plekhouer naam asseblief",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Nie in woordeboek nie",
+DlgSpellChangeTo : "Verander na",
+DlgSpellBtnIgnore : "Ignoreer",
+DlgSpellBtnIgnoreAll : "Ignoreer na-volgende",
+DlgSpellBtnReplace : "Vervang",
+DlgSpellBtnReplaceAll : "vervang na-volgende",
+DlgSpellBtnUndo : "Ont-skep",
+DlgSpellNoSuggestions : "- Geen voorstel -",
+DlgSpellProgress : "Spelling word beproef...",
+DlgSpellNoMispell : "Spellproef kompleet: Geen foute",
+DlgSpellNoChanges : "Spellproef kompleet: Geen woord veranderings",
+DlgSpellOneChange : "Spellproef kompleet: Een woord verander",
+DlgSpellManyChanges : "Spellproef kompleet: %1 woorde verander",
+
+IeSpellDownload : "Geen Spellproefer geinstaleer nie. Wil U dit aflaai?",
+
+// Button Dialog
+DlgButtonText : "Karakters (Waarde)",
+DlgButtonType : "Soort",
+DlgButtonTypeBtn : "Knop",
+DlgButtonTypeSbm : "Indien",
+DlgButtonTypeRst : "Reset",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Naam",
+DlgCheckboxValue : "Waarde",
+DlgCheckboxSelected : "Uitgekies",
+
+// Form Dialog
+DlgFormName : "Naam",
+DlgFormAction : "Aksie",
+DlgFormMethod : "Metode",
+
+// Select Field Dialog
+DlgSelectName : "Naam",
+DlgSelectValue : "Waarde",
+DlgSelectSize : "Grote",
+DlgSelectLines : "lyne",
+DlgSelectChkMulti : "Laat meerere keuses toe",
+DlgSelectOpAvail : "Beskikbare Opsies",
+DlgSelectOpText : "Karakters",
+DlgSelectOpValue : "Waarde",
+DlgSelectBtnAdd : "Byvoeg",
+DlgSelectBtnModify : "Verander",
+DlgSelectBtnUp : "Op",
+DlgSelectBtnDown : "Af",
+DlgSelectBtnSetValue : "Stel as uitgekiesde waarde",
+DlgSelectBtnDelete : "Verweider",
+
+// Textarea Dialog
+DlgTextareaName : "Naam",
+DlgTextareaCols : "Kolom",
+DlgTextareaRows : "Reie",
+
+// Text Field Dialog
+DlgTextName : "Naam",
+DlgTextValue : "Waarde",
+DlgTextCharWidth : "Karakter weite",
+DlgTextMaxChars : "Maximale karakters",
+DlgTextType : "Soort",
+DlgTextTypeText : "Karakters",
+DlgTextTypePass : "Wagwoord",
+
+// Hidden Field Dialog
+DlgHiddenName : "Naam",
+DlgHiddenValue : "Waarde",
+
+// Bulleted List Dialog
+BulletedListProp : "Gepunkte lys eienskappe",
+NumberedListProp : "Genommerde lys eienskappe",
+DlgLstStart : "Begin",
+DlgLstType : "Soort",
+DlgLstTypeCircle : "Sirkel",
+DlgLstTypeDisc : "Skyf",
+DlgLstTypeSquare : "Vierkant",
+DlgLstTypeNumbers : "Nommer (1, 2, 3)",
+DlgLstTypeLCase : "Klein Letters (a, b, c)",
+DlgLstTypeUCase : "Hoof Letters (A, B, C)",
+DlgLstTypeSRoman : "Klein Romeinse nommers (i, ii, iii)",
+DlgLstTypeLRoman : "Groot Romeinse nommers (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Algemeen",
+DlgDocBackTab : "Agtergrond",
+DlgDocColorsTab : "Kleure en Rante",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Bladsy Opskrif",
+DlgDocLangDir : "Taal rigting",
+DlgDocLangDirLTR : "Link na Regs (LTR)",
+DlgDocLangDirRTL : "Regs na Links (RTL)",
+DlgDocLangCode : "Taal Kode",
+DlgDocCharSet : "Karakterstel Kodeering",
+DlgDocCharSetCE : "Sentraal Europa",
+DlgDocCharSetCT : "Chinees Traditioneel (Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Grieks",
+DlgDocCharSetJP : "Japanees",
+DlgDocCharSetKR : "Koreans",
+DlgDocCharSetTR : "Turks",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Western European",
+DlgDocCharSetOther : "Ander Karakterstel Kodeering",
+
+DlgDocDocType : "Dokument Opskrif Soort",
+DlgDocDocTypeOther : "Ander Dokument Opskrif Soort",
+DlgDocIncXHTML : "Voeg XHTML verklaring by",
+DlgDocBgColor : "Agtergrond kleur",
+DlgDocBgImage : "Agtergrond Beeld URL",
+DlgDocBgNoScroll : "Vasgeklemde Agtergrond",
+DlgDocCText : "Karakters",
+DlgDocCLink : "Skakel",
+DlgDocCVisited : "Besoekte Skakel",
+DlgDocCActive : "Aktiewe Skakel",
+DlgDocMargins : "Bladsy Rante",
+DlgDocMaTop : "Bo",
+DlgDocMaLeft : "Links",
+DlgDocMaRight : "Regs",
+DlgDocMaBottom : "Onder",
+DlgDocMeIndex : "Dokument Index Sleutelwoorde(comma verdeelt)",
+DlgDocMeDescr : "Dokument Beskrywing",
+DlgDocMeAuthor : "Skrywer",
+DlgDocMeCopy : "Kopiereg",
+DlgDocPreview : "Voorskou",
+
+// Templates Dialog
+Templates : "Templates",
+DlgTemplatesTitle : "Inhoud Templates",
+DlgTemplatesSelMsg : "Kies die template om te gebruik in die editor<br>(Inhoud word vervang!):",
+DlgTemplatesLoading : "Templates word gelaai. U geduld asseblief...",
+DlgTemplatesNoTpl : "(Geen templates gedefinieerd)",
+DlgTemplatesReplace : "Vervang bestaande inhoud",
+
+// About Dialog
+DlgAboutAboutTab : "Meer oor",
+DlgAboutBrowserInfoTab : "Blaai Informasie deur",
+DlgAboutLicenseTab : "Lesensie",
+DlgAboutVersion : "weergawe",
+DlgAboutInfo : "Vir meer informasie gaan na "
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/ar.js b/httemplate/elements/fckeditor/editor/lang/ar.js
new file mode 100644
index 0000000..91d34f1
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/ar.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Arabic language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "rtl",
+
+ToolbarCollapse : "ضم شريط الأدوات",
+ToolbarExpand : "تمدد شريط الأدوات",
+
+// Toolbar Items and Context Menu
+Save : "Ø­Ùظ",
+NewPage : "صÙحة جديدة",
+Preview : "معاينة الصÙحة",
+Cut : "قص",
+Copy : "نسخ",
+Paste : "لصق",
+PasteText : "لصق كنص بسيط",
+PasteWord : "لصق من وورد",
+Print : "طباعة",
+SelectAll : "تحديد الكل",
+RemoveFormat : "إزالة التنسيقات",
+InsertLinkLbl : "رابط",
+InsertLink : "إدراج/تحرير رابط",
+RemoveLink : "إزالة رابط",
+Anchor : "إدراج/تحرير إشارة مرجعية",
+InsertImageLbl : "صورة",
+InsertImage : "إدراج/تحرير صورة",
+InsertFlashLbl : "Ùلاش",
+InsertFlash : "إدراج/تحرير Ùيلم Ùلاش",
+InsertTableLbl : "جدول",
+InsertTable : "إدراج/تحرير جدول",
+InsertLineLbl : "خط Ùاصل",
+InsertLine : "إدراج خط Ùاصل",
+InsertSpecialCharLbl: "رموز",
+InsertSpecialChar : "إدراج رموز..Ù",
+InsertSmileyLbl : "ابتسامات",
+InsertSmiley : "إدراج ابتسامات",
+About : "حول FCKeditor",
+Bold : "غامق",
+Italic : "مائل",
+Underline : "تسطير",
+StrikeThrough : "يتوسطه خط",
+Subscript : "منخÙض",
+Superscript : "مرتÙع",
+LeftJustify : "محاذاة إلى اليسار",
+CenterJustify : "توسيط",
+RightJustify : "محاذاة إلى اليمين",
+BlockJustify : "ضبط",
+DecreaseIndent : "إنقاص المساÙØ© البادئة",
+IncreaseIndent : "زيادة المساÙØ© البادئة",
+Undo : "تراجع",
+Redo : "إعادة",
+NumberedListLbl : "تعداد رقمي",
+NumberedList : "إدراج/إلغاء تعداد رقمي",
+BulletedListLbl : "تعداد نقطي",
+BulletedList : "إدراج/إلغاء تعداد نقطي",
+ShowTableBorders : "معاينة حدود الجداول",
+ShowDetails : "معاينة التÙاصيل",
+Style : "نمط",
+FontFormat : "تنسيق",
+Font : "خط",
+FontSize : "حجم الخط",
+TextColor : "لون النص",
+BGColor : "لون الخلÙية",
+Source : "Ø´Ùرة المصدر",
+Find : "بحث",
+Replace : "إستبدال",
+SpellCheck : "تدقيق إملائي",
+UniversalKeyboard : "لوحة المÙاتيح العالمية",
+PageBreakLbl : "Ùصل الصÙحة",
+PageBreak : "إدخال صÙحة جديدة",
+
+Form : "نموذج",
+Checkbox : "خانة إختيار",
+RadioButton : "زر خيار",
+TextField : "مربع نص",
+Textarea : "ناحية نص",
+HiddenField : "إدراج حقل Ø®ÙÙŠ",
+Button : "زر ضغط",
+SelectionField : "قائمة منسدلة",
+ImageButton : "زر صورة",
+
+FitWindow : "تكبير حجم المحرر",
+
+// Context Menu
+EditLink : "تحرير رابط",
+CellCM : "خلية",
+RowCM : "صÙ",
+ColumnCM : "عمود",
+InsertRow : "إدراج صÙ",
+DeleteRows : "حذ٠صÙÙˆÙ",
+InsertColumn : "إدراج عمود",
+DeleteColumns : "حذ٠أعمدة",
+InsertCell : "إدراج خلية",
+DeleteCells : "حذ٠خلايا",
+MergeCells : "دمج خلايا",
+SplitCell : "تقسيم خلية",
+TableDelete : "حذ٠الجدول",
+CellProperties : "خصائص الخلية",
+TableProperties : "خصائص الجدول",
+ImageProperties : "خصائص الصورة",
+FlashProperties : "خصائص Ùيلم الÙلاش",
+
+AnchorProp : "خصائص الإشارة المرجعية",
+ButtonProp : "خصائص زر الضغط",
+CheckboxProp : "خصائص خانة الإختيار",
+HiddenFieldProp : "خصائص الحقل الخÙÙŠ",
+RadioButtonProp : "خصائص زر الخيار",
+ImageButtonProp : "خصائص زر الصورة",
+TextFieldProp : "خصائص مربع النص",
+SelectionFieldProp : "خصائص القائمة المنسدلة",
+TextareaProp : "خصائص ناحية النص",
+FormProp : "خصائص النموذج",
+
+FontFormats : "عادي;منسّق;دوس;العنوان 1;العنوان 2;العنوان 3;العنوان 4;العنوان 5;العنوان 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "إنتظر قليلاً ريثما تتم معالَجة†XHTML. لن يستغرق طويلاً...",
+Done : "تم",
+PasteWordConfirm : "يبدو أن النص المراد لصقه منسوخ من برنامج وورد. هل تود تنظيÙÙ‡ قبل الشروع ÙÙŠ عملية اللصق؟",
+NotCompatiblePaste : "هذه الميزة تحتاج لمتصÙØ­ من النوعInternet Explorer إصدار 5.5 Ùما Ùوق. هل تود اللصق دون تنظي٠الكود؟",
+UnknownToolbarItem : "عنصر شريط أدوات غير معرو٠\"%1\"",
+UnknownCommand : "أمر غير معرو٠\"%1\"",
+NotImplemented : "لم يتم دعم هذا الأمر",
+UnknownToolbarSet : "لم أتمكن من العثور على طقم الأدوات \"%1\" ",
+NoActiveX : "لتأمين متصÙحك يجب أن تحدد بعض مميزات المحرر. يتوجب عليك تمكين الخيار \"Run ActiveX controls and plug-ins\". قد تواجة أخطاء وتلاحظ مميزات Ù…Ùقودة",
+BrowseServerBlocked : "لايمكن Ùتح مصدر المتصÙØ­. Ùضلا يجب التأكد بأن جميع موانع النواÙØ° المنبثقة معطلة",
+DialogBlocked : "لايمكن Ùتح ناÙذة الحوار . Ùضلا تأكد من أن مانع النواÙØ° المنبثة معطل .",
+
+// Dialogs
+DlgBtnOK : "مواÙÙ‚",
+DlgBtnCancel : "إلغاء الأمر",
+DlgBtnClose : "إغلاق",
+DlgBtnBrowseServer : "تصÙØ­ الخادم",
+DlgAdvancedTag : "متقدم",
+DlgOpOther : "<أخرى>",
+DlgInfoTab : "معلومات",
+DlgAlertUrl : "الرجاء كتابة عنوان الإنترنت",
+
+// General Dialogs Labels
+DlgGenNotSet : "<بدون تحديد>",
+DlgGenId : "الرقم",
+DlgGenLangDir : "إتجاه النص",
+DlgGenLangDirLtr : "اليسار لليمين (LTR)",
+DlgGenLangDirRtl : "اليمين لليسار (RTL)",
+DlgGenLangCode : "رمز اللغة",
+DlgGenAccessKey : "Ù…Ùاتيح الإختصار",
+DlgGenName : "الاسم",
+DlgGenTabIndex : "الترتيب",
+DlgGenLongDescr : "عنوان الوص٠المÙصّل",
+DlgGenClass : "Ùئات التنسيق",
+DlgGenTitle : "تلميح الشاشة",
+DlgGenContType : "نوع التلميح",
+DlgGenLinkCharset : "ترميز المادة المطلوبة",
+DlgGenStyle : "نمط",
+
+// Image Dialog
+DlgImgTitle : "خصائص الصورة",
+DlgImgInfoTab : "معلومات الصورة",
+DlgImgBtnUpload : "أرسلها للخادم",
+DlgImgURL : "موقع الصورة",
+DlgImgUpload : "رÙع",
+DlgImgAlt : "الوصÙ",
+DlgImgWidth : "العرض",
+DlgImgHeight : "الإرتÙاع",
+DlgImgLockRatio : "تناسق الحجم",
+DlgBtnResetSize : "إستعادة الحجم الأصلي",
+DlgImgBorder : "سمك الحدود",
+DlgImgHSpace : "تباعد Ø£Ùقي",
+DlgImgVSpace : "تباعد عمودي",
+DlgImgAlign : "محاذاة",
+DlgImgAlignLeft : "يسار",
+DlgImgAlignAbsBottom: "أسÙÙ„ النص",
+DlgImgAlignAbsMiddle: "وسط السطر",
+DlgImgAlignBaseline : "على السطر",
+DlgImgAlignBottom : "أسÙÙ„",
+DlgImgAlignMiddle : "وسط",
+DlgImgAlignRight : "يمين",
+DlgImgAlignTextTop : "أعلى النص",
+DlgImgAlignTop : "أعلى",
+DlgImgPreview : "معاينة",
+DlgImgAlertUrl : "Ùضلاً أكتب الموقع الذي توجد عليه هذه الصورة.",
+DlgImgLinkTab : "الرابط",
+
+// Flash Dialog
+DlgFlashTitle : "خصائص Ùيلم الÙلاش",
+DlgFlashChkPlay : "تشغيل تلقائي",
+DlgFlashChkLoop : "تكرار",
+DlgFlashChkMenu : "تمكين قائمة Ùيلم الÙلاش",
+DlgFlashScale : "الحجم",
+DlgFlashScaleAll : "إظهار الكل",
+DlgFlashScaleNoBorder : "بلا حدود",
+DlgFlashScaleFit : "ضبط تام",
+
+// Link Dialog
+DlgLnkWindowTitle : "إرتباط تشعبي",
+DlgLnkInfoTab : "معلومات الرابط",
+DlgLnkTargetTab : "الهدÙ",
+
+DlgLnkType : "نوع الربط",
+DlgLnkTypeURL : "العنوان",
+DlgLnkTypeAnchor : "مكان ÙÙŠ هذا المستند",
+DlgLnkTypeEMail : "بريد إلكتروني",
+DlgLnkProto : "البروتوكول",
+DlgLnkProtoOther : "<أخرى>",
+DlgLnkURL : "الموقع",
+DlgLnkAnchorSel : "اختر علامة مرجعية",
+DlgLnkAnchorByName : "حسب اسم العلامة",
+DlgLnkAnchorById : "حسب تعري٠العنصر",
+DlgLnkNoAnchors : "<لا يوجد علامات مرجعية ÙÙŠ هذا المستند>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "عنوان بريد إلكتروني",
+DlgLnkEMailSubject : "موضوع الرسالة",
+DlgLnkEMailBody : "محتوى الرسالة",
+DlgLnkUpload : "رÙع",
+DlgLnkBtnUpload : "أرسلها للخادم",
+
+DlgLnkTarget : "الهدÙ",
+DlgLnkTargetFrame : "<إطار>",
+DlgLnkTargetPopup : "<ناÙذة منبثقة>",
+DlgLnkTargetBlank : "إطار جديد (_blank)",
+DlgLnkTargetParent : "الإطار الأصل (_parent)",
+DlgLnkTargetSelf : "Ù†Ùس الإطار (_self)",
+DlgLnkTargetTop : "صÙحة كاملة (_top)",
+DlgLnkTargetFrameName : "اسم الإطار الهدÙ",
+DlgLnkPopWinName : "تسمية الناÙذة المنبثقة",
+DlgLnkPopWinFeat : "خصائص الناÙذة المنبثقة",
+DlgLnkPopResize : "قابلة للتحجيم",
+DlgLnkPopLocation : "شريط العنوان",
+DlgLnkPopMenu : "القوائم الرئيسية",
+DlgLnkPopScroll : "أشرطة التمرير",
+DlgLnkPopStatus : "شريط الحالة السÙلي",
+DlgLnkPopToolbar : "شريط الأدوات",
+DlgLnkPopFullScrn : "ملئ الشاشة (IE)",
+DlgLnkPopDependent : "تابع (Netscape)",
+DlgLnkPopWidth : "العرض",
+DlgLnkPopHeight : "الإرتÙاع",
+DlgLnkPopLeft : "التمركز لليسار",
+DlgLnkPopTop : "التمركز للأعلى",
+
+DlnLnkMsgNoUrl : "Ùضلاً أدخل عنوان الموقع الذي يشير إليه الرابط",
+DlnLnkMsgNoEMail : "Ùضلاً أدخل عنوان البريد الإلكتروني",
+DlnLnkMsgNoAnchor : "Ùضلاً حدد العلامة المرجعية المرغوبة",
+DlnLnkMsgInvPopName : "اسم الناÙذة المنبثقة يجب أن يبدأ بحر٠أبجدي دون مساÙات",
+
+// Color Dialog
+DlgColorTitle : "اختر لوناً",
+DlgColorBtnClear : "مسح",
+DlgColorHighlight : "تحديد",
+DlgColorSelected : "إختيار",
+
+// Smiley Dialog
+DlgSmileyTitle : "إدراج إبتسامات ",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "إدراج رمز",
+
+// Table Dialog
+DlgTableTitle : "إدراج جدول",
+DlgTableRows : "صÙÙˆÙ",
+DlgTableColumns : "أعمدة",
+DlgTableBorder : "سمك الحدود",
+DlgTableAlign : "المحاذاة",
+DlgTableAlignNotSet : "<بدون تحديد>",
+DlgTableAlignLeft : "يسار",
+DlgTableAlignCenter : "وسط",
+DlgTableAlignRight : "يمين",
+DlgTableWidth : "العرض",
+DlgTableWidthPx : "بكسل",
+DlgTableWidthPc : "بالمئة",
+DlgTableHeight : "الإرتÙاع",
+DlgTableCellSpace : "تباعد الخلايا",
+DlgTableCellPad : "المساÙØ© البادئة",
+DlgTableCaption : "الوصÙ",
+DlgTableSummary : "الخلاصة",
+
+// Table Cell Dialog
+DlgCellTitle : "خصائص الخلية",
+DlgCellWidth : "العرض",
+DlgCellWidthPx : "بكسل",
+DlgCellWidthPc : "بالمئة",
+DlgCellHeight : "الإرتÙاع",
+DlgCellWordWrap : "التÙا٠النص",
+DlgCellWordWrapNotSet : "<بدون تحديد>",
+DlgCellWordWrapYes : "نعم",
+DlgCellWordWrapNo : "لا",
+DlgCellHorAlign : "المحاذاة الأÙقية",
+DlgCellHorAlignNotSet : "<بدون تحديد>",
+DlgCellHorAlignLeft : "يسار",
+DlgCellHorAlignCenter : "وسط",
+DlgCellHorAlignRight: "يمين",
+DlgCellVerAlign : "المحاذاة العمودية",
+DlgCellVerAlignNotSet : "<بدون تحديد>",
+DlgCellVerAlignTop : "أعلى",
+DlgCellVerAlignMiddle : "وسط",
+DlgCellVerAlignBottom : "أسÙÙ„",
+DlgCellVerAlignBaseline : "على السطر",
+DlgCellRowSpan : "إمتداد الصÙÙˆÙ",
+DlgCellCollSpan : "إمتداد الأعمدة",
+DlgCellBackColor : "لون الخلÙية",
+DlgCellBorderColor : "لون الحدود",
+DlgCellBtnSelect : "حدّد...",
+
+// Find Dialog
+DlgFindTitle : "بحث",
+DlgFindFindBtn : "ابحث",
+DlgFindNotFoundMsg : "لم يتم العثور على النص المحدد.",
+
+// Replace Dialog
+DlgReplaceTitle : "إستبدال",
+DlgReplaceFindLbl : "البحث عن:",
+DlgReplaceReplaceLbl : "إستبدال بـ:",
+DlgReplaceCaseChk : "مطابقة حالة الأحرÙ",
+DlgReplaceReplaceBtn : "إستبدال",
+DlgReplaceReplAllBtn : "إستبدال الكل",
+DlgReplaceWordChk : "الكلمة بالكامل Ùقط",
+
+// Paste Operations / Dialog
+PasteErrorCut : "الإعدادات الأمنية للمتصÙØ­ الذي تستخدمه تمنع القص التلقائي. Ùضلاً إستخدم لوحة المÙاتيح Ù„Ùعل ذلك (Ctrl+X).",
+PasteErrorCopy : "الإعدادات الأمنية للمتصÙØ­ الذي تستخدمه تمنع النسخ التلقائي. Ùضلاً إستخدم لوحة المÙاتيح Ù„Ùعل ذلك (Ctrl+C).",
+
+PasteAsText : "لصق كنص بسيط",
+PasteFromWord : "لصق من وورد",
+
+DlgPasteMsg2 : "الصق داخل الصندوق بإستخدام زرّي (<STRONG>Ctrl+V</STRONG>) ÙÙŠ لوحة المÙاتيح، ثم اضغط زر <STRONG>مواÙÙ‚</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "تجاهل تعريÙات أسماء الخطوط",
+DlgPasteRemoveStyles : "إزالة تعريÙات الأنماط",
+DlgPasteCleanBox : "نظّ٠محتوى الصندوق",
+
+// Color Picker
+ColorAutomatic : "تلقائي",
+ColorMoreColors : "ألوان إضاÙية...",
+
+// Document Properties
+DocProps : "خصائص الصÙحة",
+
+// Anchor Dialog
+DlgAnchorTitle : "خصائص إشارة مرجعية",
+DlgAnchorName : "اسم الإشارة المرجعية",
+DlgAnchorErrorName : "الرجاء كتابة اسم الإشارة المرجعية",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "ليست ÙÙŠ القاموس",
+DlgSpellChangeTo : "التغيير إلى",
+DlgSpellBtnIgnore : "تجاهل",
+DlgSpellBtnIgnoreAll : "تجاهل الكل",
+DlgSpellBtnReplace : "تغيير",
+DlgSpellBtnReplaceAll : "تغيير الكل",
+DlgSpellBtnUndo : "تراجع",
+DlgSpellNoSuggestions : "- لا توجد إقتراحات -",
+DlgSpellProgress : "جاري التدقيق إملائياً",
+DlgSpellNoMispell : "تم إكمال التدقيق الإملائي: لم يتم العثور على أي أخطاء إملائية",
+DlgSpellNoChanges : "تم إكمال التدقيق الإملائي: لم يتم تغيير أي كلمة",
+DlgSpellOneChange : "تم إكمال التدقيق الإملائي: تم تغيير كلمة واحدة Ùقط",
+DlgSpellManyChanges : "تم إكمال التدقيق الإملائي: تم تغيير %1 كلمات\كلمة",
+
+IeSpellDownload : "المدقق الإملائي (الإنجليزي) غير مثبّت. هل تود تحميله الآن؟",
+
+// Button Dialog
+DlgButtonText : "القيمة/التسمية",
+DlgButtonType : "نوع الزر",
+DlgButtonTypeBtn : "زر",
+DlgButtonTypeSbm : "إرسال",
+DlgButtonTypeRst : "إعادة تعيين",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "الاسم",
+DlgCheckboxValue : "القيمة",
+DlgCheckboxSelected : "محدد",
+
+// Form Dialog
+DlgFormName : "الاسم",
+DlgFormAction : "اسم الملÙ",
+DlgFormMethod : "الأسلوب",
+
+// Select Field Dialog
+DlgSelectName : "الاسم",
+DlgSelectValue : "القيمة",
+DlgSelectSize : "الحجم",
+DlgSelectLines : "الأسطر",
+DlgSelectChkMulti : "السماح بتحديدات متعددة",
+DlgSelectOpAvail : "الخيارات المتاحة",
+DlgSelectOpText : "النص",
+DlgSelectOpValue : "القيمة",
+DlgSelectBtnAdd : "إضاÙØ©",
+DlgSelectBtnModify : "تعديل",
+DlgSelectBtnUp : "تحريك لأعلى",
+DlgSelectBtnDown : "تحريك لأسÙÙ„",
+DlgSelectBtnSetValue : "إجعلها محددة",
+DlgSelectBtnDelete : "إزالة",
+
+// Textarea Dialog
+DlgTextareaName : "الاسم",
+DlgTextareaCols : "الأعمدة",
+DlgTextareaRows : "الصÙÙˆÙ",
+
+// Text Field Dialog
+DlgTextName : "الاسم",
+DlgTextValue : "القيمة",
+DlgTextCharWidth : "العرض بالأحرÙ",
+DlgTextMaxChars : "عدد الحرو٠الأقصى",
+DlgTextType : "نوع المحتوى",
+DlgTextTypeText : "نص",
+DlgTextTypePass : "كلمة مرور",
+
+// Hidden Field Dialog
+DlgHiddenName : "الاسم",
+DlgHiddenValue : "القيمة",
+
+// Bulleted List Dialog
+BulletedListProp : "خصائص التعداد النقطي",
+NumberedListProp : "خصائص التعداد الرقمي",
+DlgLstStart : "البدء عند",
+DlgLstType : "النوع",
+DlgLstTypeCircle : "دائرة",
+DlgLstTypeDisc : "قرص",
+DlgLstTypeSquare : "مربع",
+DlgLstTypeNumbers : "أرقام (1، 2، 3)َ",
+DlgLstTypeLCase : "حرو٠صغيرة (a, b, c)َ",
+DlgLstTypeUCase : "حرو٠كبيرة (A, B, C)َ",
+DlgLstTypeSRoman : "ترقيم روماني صغير (i, ii, iii)َ",
+DlgLstTypeLRoman : "ترقيم روماني كبير (I, II, III)َ",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "عام",
+DlgDocBackTab : "الخلÙية",
+DlgDocColorsTab : "الألوان والهوامش",
+DlgDocMetaTab : "المعرّÙات الرأسية",
+
+DlgDocPageTitle : "عنوان الصÙحة",
+DlgDocLangDir : "إتجاه اللغة",
+DlgDocLangDirLTR : "اليسار لليمين (LTR)",
+DlgDocLangDirRTL : "اليمين لليسار (RTL)",
+DlgDocLangCode : "رمز اللغة",
+DlgDocCharSet : "ترميز الحروÙ",
+DlgDocCharSetCE : "أوروبا الوسطى",
+DlgDocCharSetCT : "الصينية التقليدية (Big5)",
+DlgDocCharSetCR : "السيريلية",
+DlgDocCharSetGR : "اليونانية",
+DlgDocCharSetJP : "اليابانية",
+DlgDocCharSetKR : "الكورية",
+DlgDocCharSetTR : "التركية",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "أوروبا الغربية",
+DlgDocCharSetOther : "ترميز آخر",
+
+DlgDocDocType : "ترويسة نوع الصÙحة",
+DlgDocDocTypeOther : "ترويسة نوع صÙحة أخرى",
+DlgDocIncXHTML : "تضمين إعلانات†لغة XHTMLَ",
+DlgDocBgColor : "لون الخلÙية",
+DlgDocBgImage : "رابط الصورة الخلÙية",
+DlgDocBgNoScroll : "جعلها علامة مائية",
+DlgDocCText : "النص",
+DlgDocCLink : "الروابط",
+DlgDocCVisited : "المزارة",
+DlgDocCActive : "النشطة",
+DlgDocMargins : "هوامش الصÙحة",
+DlgDocMaTop : "علوي",
+DlgDocMaLeft : "أيسر",
+DlgDocMaRight : "أيمن",
+DlgDocMaBottom : "سÙلي",
+DlgDocMeIndex : "الكلمات الأساسية (Ù…Ùصولة بÙواصل)ÙŽ",
+DlgDocMeDescr : "وص٠الصÙحة",
+DlgDocMeAuthor : "الكاتب",
+DlgDocMeCopy : "المالك",
+DlgDocPreview : "معاينة",
+
+// Templates Dialog
+Templates : "القوالب",
+DlgTemplatesTitle : "قوالب المحتوى",
+DlgTemplatesSelMsg : "اختر القالب الذي تود وضعه ÙÙŠ المحرر <br>(سيتم Ùقدان المحتوى الحالي):",
+DlgTemplatesLoading : "جاري تحميل قائمة القوالب، الرجاء الإنتظار...",
+DlgTemplatesNoTpl : "(لم يتم تعري٠أي قالب)",
+DlgTemplatesReplace : "استبدال المحتوى",
+
+// About Dialog
+DlgAboutAboutTab : "نبذة",
+DlgAboutBrowserInfoTab : "معلومات متصÙحك",
+DlgAboutLicenseTab : "الترخيص",
+DlgAboutVersion : "الإصدار",
+DlgAboutInfo : "لمزيد من المعلومات تÙضل بزيارة"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/bg.js b/httemplate/elements/fckeditor/editor/lang/bg.js
new file mode 100644
index 0000000..423bd02
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/bg.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Bulgarian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Скрий панела Ñ Ð¸Ð½Ñтрументите",
+ToolbarExpand : "Покажи панела Ñ Ð¸Ð½Ñтрументите",
+
+// Toolbar Items and Context Menu
+Save : "Запази",
+NewPage : "Ðова Ñтраница",
+Preview : "Предварителен изглед",
+Cut : "Изрежи",
+Copy : "Запамети",
+Paste : "Вмъкни",
+PasteText : "Вмъкни Ñамо текÑÑ‚",
+PasteWord : "Вмъкни от MS Word",
+Print : "Печат",
+SelectAll : "Селектирай вÑичко",
+RemoveFormat : "Изтрий форматирането",
+InsertLinkLbl : "Връзка",
+InsertLink : "Добави/Редактирай връзка",
+RemoveLink : "Изтрий връзка",
+Anchor : "Добави/Редактирай котва",
+InsertImageLbl : "Изображение",
+InsertImage : "Добави/Редактирай изображение",
+InsertFlashLbl : "Flash",
+InsertFlash : "Добави/Редактиай Flash обект",
+InsertTableLbl : "Таблица",
+InsertTable : "Добави/Редактирай таблица",
+InsertLineLbl : "ЛиниÑ",
+InsertLine : "Вмъкни хоризонтална линиÑ",
+InsertSpecialCharLbl: "Специален Ñимвол",
+InsertSpecialChar : "Вмъкни Ñпециален Ñимвол",
+InsertSmileyLbl : "УÑмивка",
+InsertSmiley : "Добави уÑмивка",
+About : "За FCKeditor",
+Bold : "Удебелен",
+Italic : "КурÑив",
+Underline : "Подчертан",
+StrikeThrough : "Зачертан",
+Subscript : "Ð˜Ð½Ð´ÐµÐºÑ Ð·Ð° база",
+Superscript : "Ð˜Ð½Ð´ÐµÐºÑ Ð·Ð° Ñтепен",
+LeftJustify : "ПодравнÑване в лÑво",
+CenterJustify : "ПодравнÑвне в Ñредата",
+RightJustify : "ПодравнÑване в дÑÑно",
+BlockJustify : "ДвуÑтранно подравнÑване",
+DecreaseIndent : "Ðамали отÑтъпа",
+IncreaseIndent : "Увеличи отÑтъпа",
+Undo : "Отмени",
+Redo : "Повтори",
+NumberedListLbl : "Ðумериран ÑпиÑък",
+NumberedList : "Добави/Изтрий нумериран ÑпиÑък",
+BulletedListLbl : "Ðенумериран ÑпиÑък",
+BulletedList : "Добави/Изтрий ненумериран ÑпиÑък",
+ShowTableBorders : "Покажи рамките на таблицата",
+ShowDetails : "Покажи подробноÑти",
+Style : "Стил",
+FontFormat : "Формат",
+Font : "Шрифт",
+FontSize : "Размер",
+TextColor : "ЦвÑÑ‚ на текÑта",
+BGColor : "ЦвÑÑ‚ на фона",
+Source : "Код",
+Find : "ТърÑи",
+Replace : "ЗамеÑти",
+SpellCheck : "Провери правопиÑа",
+UniversalKeyboard : "УниверÑална клавиатура",
+PageBreakLbl : "Ðов ред",
+PageBreak : "Вмъкни нов ред",
+
+Form : "ФормулÑÑ€",
+Checkbox : "Поле за отметка",
+RadioButton : "Поле за опциÑ",
+TextField : "ТекÑтово поле",
+Textarea : "ТекÑтова облаÑÑ‚",
+HiddenField : "Скрито поле",
+Button : "Бутон",
+SelectionField : "Падащо меню Ñ Ð¾Ð¿Ñ†Ð¸Ð¸",
+ImageButton : "Бутон-изображение",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Редактирай връзка",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Добави ред",
+DeleteRows : "Изтрий редовете",
+InsertColumn : "Добави колона",
+DeleteColumns : "Изтрий колоните",
+InsertCell : "Добави клетка",
+DeleteCells : "Изтрий клетките",
+MergeCells : "Обедини клетките",
+SplitCell : "Раздели клетката",
+TableDelete : "Изтрий таблицата",
+CellProperties : "Параметри на клетката",
+TableProperties : "Параметри на таблицата",
+ImageProperties : "Параметри на изображението",
+FlashProperties : "Параметри на Flash обекта",
+
+AnchorProp : "Параметри на котвата",
+ButtonProp : "Параметри на бутона",
+CheckboxProp : "Параметри на полето за отметка",
+HiddenFieldProp : "Параметри на Ñкритото поле",
+RadioButtonProp : "Параметри на полето за опциÑ",
+ImageButtonProp : "Параметри на бутона-изображение",
+TextFieldProp : "Параметри на текÑтовото-поле",
+SelectionFieldProp : "Параметри на падащото меню Ñ Ð¾Ð¿Ñ†Ð¸Ð¸",
+TextareaProp : "Параметри на текÑтовата облаÑÑ‚",
+FormProp : "Параметри на формулÑра",
+
+FontFormats : "Ðормален;Форматиран;ÐдреÑ;Заглавие 1;Заглавие 2;Заглавие 3;Заглавие 4;Заглавие 5;Заглавие 6;Параграф (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Обработка на XHTML. ÐœÐ¾Ð»Ñ Ð¸Ð·Ñ‡Ð°ÐºÐ°Ð¹Ñ‚Ðµ...",
+Done : "Готово",
+PasteWordConfirm : "ТекÑÑ‚ÑŠÑ‚, който иÑкате да вмъкнете е копиран от MS Word. Желаете ли да бъде изчиÑтен преди вмъкването?",
+NotCompatiblePaste : "Тази Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð·Ð¸Ñква MS Internet Explorer верÑÐ¸Ñ 5.5 или по-виÑока. Желаете ли да вмъкнете запаметеното без изчиÑтване?",
+UnknownToolbarItem : "Ðепознат инÑтрумент \"%1\"",
+UnknownCommand : "Ðепозната команда \"%1\"",
+NotImplemented : "Командата не е имплементирана",
+UnknownToolbarSet : "Панелът \"%1\" не ÑъщеÑтвува",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "ОК",
+DlgBtnCancel : "Отказ",
+DlgBtnClose : "Затвори",
+DlgBtnBrowseServer : "Разгледай Ñървъра",
+DlgAdvancedTag : "ПодробноÑти...",
+DlgOpOther : "<Друго>",
+DlgInfoTab : "ИнформациÑ",
+DlgAlertUrl : "МолÑ, въведете Ð¿ÑŠÐ»Ð½Ð¸Ñ Ð¿ÑŠÑ‚ (URL)",
+
+// General Dialogs Labels
+DlgGenNotSet : "<не е наÑтроен>",
+DlgGenId : "Идентификатор",
+DlgGenLangDir : "поÑока на речта",
+DlgGenLangDirLtr : "От лÑво на дÑÑно",
+DlgGenLangDirRtl : "От дÑÑно на лÑво",
+DlgGenLangCode : "Код на езика",
+DlgGenAccessKey : "Бърз клавиш",
+DlgGenName : "Име",
+DlgGenTabIndex : "Ред на доÑтъп",
+DlgGenLongDescr : "ОпиÑание на връзката",
+DlgGenClass : "ÐšÐ»Ð°Ñ Ð¾Ñ‚ Ñтиловите таблици",
+DlgGenTitle : "Препоръчително заглавие",
+DlgGenContType : "Препоръчителен тип на Ñъдържанието",
+DlgGenLinkCharset : "Тип на ÑÐ²ÑŠÑ€Ð·Ð°Ð½Ð¸Ñ Ñ€ÐµÑурÑ",
+DlgGenStyle : "Стил",
+
+// Image Dialog
+DlgImgTitle : "Параметри на изображението",
+DlgImgInfoTab : "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° изображението",
+DlgImgBtnUpload : "Прати към Ñървъра",
+DlgImgURL : "Пълен път (URL)",
+DlgImgUpload : "Качи",
+DlgImgAlt : "Ðлтернативен текÑÑ‚",
+DlgImgWidth : "Ширина",
+DlgImgHeight : "ВиÑочина",
+DlgImgLockRatio : "Запази пропорциÑта",
+DlgBtnResetSize : "ВъзÑтанови размера",
+DlgImgBorder : "Рамка",
+DlgImgHSpace : "Хоризонтален отÑтъп",
+DlgImgVSpace : "Вертикален отÑтъп",
+DlgImgAlign : "ПодравнÑване",
+DlgImgAlignLeft : "ЛÑво",
+DlgImgAlignAbsBottom: "Ðай-долу",
+DlgImgAlignAbsMiddle: "Точно по Ñредата",
+DlgImgAlignBaseline : "По базовата линиÑ",
+DlgImgAlignBottom : "Долу",
+DlgImgAlignMiddle : "По Ñредата",
+DlgImgAlignRight : "ДÑÑно",
+DlgImgAlignTextTop : "Върху текÑта",
+DlgImgAlignTop : "Отгоре",
+DlgImgPreview : "Изглед",
+DlgImgAlertUrl : "МолÑ, въведете Ð¿ÑŠÐ»Ð½Ð¸Ñ Ð¿ÑŠÑ‚ до изображението",
+DlgImgLinkTab : "Връзка",
+
+// Flash Dialog
+DlgFlashTitle : "Параметри на Flash обекта",
+DlgFlashChkPlay : "Ðвтоматично Ñтартиране",
+DlgFlashChkLoop : "Ðово Ñтартиране Ñлед завършването",
+DlgFlashChkMenu : "Разрешено Flash меню",
+DlgFlashScale : "ОразмерÑване",
+DlgFlashScaleAll : "Покажи Ñ†ÐµÐ»Ð¸Ñ Ð¾Ð±ÐµÐºÑ‚",
+DlgFlashScaleNoBorder : "Без рамка",
+DlgFlashScaleFit : "Според мÑÑтото",
+
+// Link Dialog
+DlgLnkWindowTitle : "Връзка",
+DlgLnkInfoTab : "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° връзката",
+DlgLnkTargetTab : "Цел",
+
+DlgLnkType : "Вид на връзката",
+DlgLnkTypeURL : "Пълен път (URL)",
+DlgLnkTypeAnchor : "Котва в текущата Ñтраница",
+DlgLnkTypeEMail : "Е-поща",
+DlgLnkProto : "Протокол",
+DlgLnkProtoOther : "<друго>",
+DlgLnkURL : "Пълен път (URL)",
+DlgLnkAnchorSel : "Изберете котва",
+DlgLnkAnchorByName : "По име на котвата",
+DlgLnkAnchorById : "По идентификатор на елемент",
+DlgLnkNoAnchors : "<ÐÑма котви в Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ÐÐ´Ñ€ÐµÑ Ð·Ð° е-поща",
+DlgLnkEMailSubject : "Тема на пиÑмото",
+DlgLnkEMailBody : "ТекÑÑ‚ на пиÑмото",
+DlgLnkUpload : "Качи",
+DlgLnkBtnUpload : "Прати на Ñървъра",
+
+DlgLnkTarget : "Цел",
+DlgLnkTargetFrame : "<рамка>",
+DlgLnkTargetPopup : "<дъщерен прозорец>",
+DlgLnkTargetBlank : "Ðов прозорец (_blank)",
+DlgLnkTargetParent : "РодителÑки прозорец (_parent)",
+DlgLnkTargetSelf : "ÐÐºÑ‚Ð¸Ð²Ð½Ð¸Ñ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€ÐµÑ† (_self)",
+DlgLnkTargetTop : "Ð¦ÐµÐ»Ð¸Ñ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€ÐµÑ† (_top)",
+DlgLnkTargetFrameName : "Име на Ñ†ÐµÐ»ÐµÐ²Ð¸Ñ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€ÐµÑ†",
+DlgLnkPopWinName : "Име на Ð´ÑŠÑ‰ÐµÑ€Ð½Ð¸Ñ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€ÐµÑ†",
+DlgLnkPopWinFeat : "Параметри на Ð´ÑŠÑ‰ÐµÑ€Ð½Ð¸Ñ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€ÐµÑ†",
+DlgLnkPopResize : "С променливи размери",
+DlgLnkPopLocation : "Поле за адреÑ",
+DlgLnkPopMenu : "Меню",
+DlgLnkPopScroll : "Плъзгач",
+DlgLnkPopStatus : "Поле за ÑтатуÑ",
+DlgLnkPopToolbar : "Панел Ñ Ð±ÑƒÑ‚Ð¾Ð½Ð¸",
+DlgLnkPopFullScrn : "ГолÑм екран (MS IE)",
+DlgLnkPopDependent : "ЗавиÑим (Netscape)",
+DlgLnkPopWidth : "Ширина",
+DlgLnkPopHeight : "ВиÑочина",
+DlgLnkPopLeft : "Координати - X",
+DlgLnkPopTop : "Координати - Y",
+
+DlnLnkMsgNoUrl : "МолÑ, напишете Ð¿ÑŠÐ»Ð½Ð¸Ñ Ð¿ÑŠÑ‚ (URL)",
+DlnLnkMsgNoEMail : "МолÑ, напишете адреÑа за е-поща",
+DlnLnkMsgNoAnchor : "МолÑ, изберете котва",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Изберете цвÑÑ‚",
+DlgColorBtnClear : "ИзчиÑти",
+DlgColorHighlight : "Текущ",
+DlgColorSelected : "Избран",
+
+// Smiley Dialog
+DlgSmileyTitle : "Добави уÑмивка",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Изберете Ñпециален Ñимвол",
+
+// Table Dialog
+DlgTableTitle : "Параметри на таблицата",
+DlgTableRows : "Редове",
+DlgTableColumns : "Колони",
+DlgTableBorder : "Размер на рамката",
+DlgTableAlign : "ПодравнÑване",
+DlgTableAlignNotSet : "<Ðе е избрано>",
+DlgTableAlignLeft : "ЛÑво",
+DlgTableAlignCenter : "Център",
+DlgTableAlignRight : "ДÑÑно",
+DlgTableWidth : "Ширина",
+DlgTableWidthPx : "пикÑели",
+DlgTableWidthPc : "проценти",
+DlgTableHeight : "ВиÑочина",
+DlgTableCellSpace : "РазÑтоÑние между клетките",
+DlgTableCellPad : "ОтÑтъп на Ñъдържанието в клетките",
+DlgTableCaption : "Заглавие",
+DlgTableSummary : "Резюме",
+
+// Table Cell Dialog
+DlgCellTitle : "Параметри на клетката",
+DlgCellWidth : "Ширина",
+DlgCellWidthPx : "пикÑели",
+DlgCellWidthPc : "проценти",
+DlgCellHeight : "ВиÑочина",
+DlgCellWordWrap : "пренаÑÑне на нов ред",
+DlgCellWordWrapNotSet : "<Ðе е наÑтроено>",
+DlgCellWordWrapYes : "Да",
+DlgCellWordWrapNo : "не",
+DlgCellHorAlign : "Хоризонтално подравнÑване",
+DlgCellHorAlignNotSet : "<Ðе е наÑтроено>",
+DlgCellHorAlignLeft : "ЛÑво",
+DlgCellHorAlignCenter : "Център",
+DlgCellHorAlignRight: "ДÑÑно",
+DlgCellVerAlign : "Вертикално подравнÑване",
+DlgCellVerAlignNotSet : "<Ðе е наÑтроено>",
+DlgCellVerAlignTop : "Горе",
+DlgCellVerAlignMiddle : "По Ñредата",
+DlgCellVerAlignBottom : "Долу",
+DlgCellVerAlignBaseline : "По базовата линиÑ",
+DlgCellRowSpan : "повече от един ред",
+DlgCellCollSpan : "повече от една колона",
+DlgCellBackColor : "фонов цвÑÑ‚",
+DlgCellBorderColor : "цвÑÑ‚ на рамката",
+DlgCellBtnSelect : "Изберете...",
+
+// Find Dialog
+DlgFindTitle : "ТърÑи",
+DlgFindFindBtn : "ТърÑи",
+DlgFindNotFoundMsg : "Ð£ÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñ‚ÐµÐºÑÑ‚ не беше намерен.",
+
+// Replace Dialog
+DlgReplaceTitle : "ЗамеÑти",
+DlgReplaceFindLbl : "ТърÑи:",
+DlgReplaceReplaceLbl : "ЗамеÑти Ñ:",
+DlgReplaceCaseChk : "Ð¡ÑŠÑ ÑÑŠÑ‰Ð¸Ñ Ñ€ÐµÐ³Ð¸ÑÑ‚ÑŠÑ€",
+DlgReplaceReplaceBtn : "ЗамеÑти",
+DlgReplaceReplAllBtn : "ЗамеÑти вÑички",
+DlgReplaceWordChk : "ТърÑи Ñъщата дума",
+
+// Paste Operations / Dialog
+PasteErrorCut : "ÐаÑтройките за ÑигурноÑÑ‚ на Ð²Ð°ÑˆÐ¸Ñ Ð±Ñ€Ð°Ð·ÑƒÑŠÑ€ не разрешават на редактора да изпълни изрÑзването. За целта използвайте клавиатурата (Ctrl+X).",
+PasteErrorCopy : "ÐаÑтройките за ÑигурноÑÑ‚ на Ð²Ð°ÑˆÐ¸Ñ Ð±Ñ€Ð°Ð·ÑƒÑŠÑ€ не разрешават на редактора да изпълни запаметÑването. За целта използвайте клавиатурата (Ctrl+C).",
+
+PasteAsText : "Вмъкни като чиÑÑ‚ текÑÑ‚",
+PasteFromWord : "Вмъкни от MS Word",
+
+DlgPasteMsg2 : "Вмъкнете тук Ñъдъжанието Ñ ÐºÐ»Ð°Ð²Ð¸Ð°Ñ‚ÑƒÐ°Ñ€Ð°Ñ‚Ð° (<STRONG>Ctrl+V</STRONG>) и натиÑнете <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Игнорирай шрифтовите дефиниции",
+DlgPasteRemoveStyles : "Изтрий Ñтиловите дефиниции",
+DlgPasteCleanBox : "ИзчиÑти",
+
+// Color Picker
+ColorAutomatic : "По подразбиране",
+ColorMoreColors : "Други цветове...",
+
+// Document Properties
+DocProps : "Параметри на документа",
+
+// Anchor Dialog
+DlgAnchorTitle : "Параметри на котвата",
+DlgAnchorName : "Име на котвата",
+DlgAnchorErrorName : "МолÑ, въведете име на котвата",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "ЛипÑва в речника",
+DlgSpellChangeTo : "Промени на",
+DlgSpellBtnIgnore : "Игнорирай",
+DlgSpellBtnIgnoreAll : "Игнорирай вÑички",
+DlgSpellBtnReplace : "ЗамеÑти",
+DlgSpellBtnReplaceAll : "ЗамеÑти вÑички",
+DlgSpellBtnUndo : "Отмени",
+DlgSpellNoSuggestions : "- ÐÑма Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ -",
+DlgSpellProgress : "Извършване на проверката за правопиÑ...",
+DlgSpellNoMispell : "Проверката за Ð¿Ñ€Ð°Ð²Ð¾Ð¿Ð¸Ñ Ð·Ð°Ð²ÑŠÑ€ÑˆÐµÐ½Ð°: не Ñа открити правопиÑни грешки",
+DlgSpellNoChanges : "Проверката за Ð¿Ñ€Ð°Ð²Ð¾Ð¿Ð¸Ñ Ð·Ð°Ð²ÑŠÑ€ÑˆÐµÐ½Ð°: нÑма променени думи",
+DlgSpellOneChange : "Проверката за Ð¿Ñ€Ð°Ð²Ð¾Ð¿Ð¸Ñ Ð·Ð°Ð²ÑŠÑ€ÑˆÐµÐ½Ð°: една дума е променена",
+DlgSpellManyChanges : "Проверката за Ð¿Ñ€Ð°Ð²Ð¾Ð¿Ð¸Ñ Ð·Ð°Ð²ÑŠÑ€ÑˆÐµÐ½Ð°: %1 думи Ñа променени",
+
+IeSpellDownload : "ИнÑтрументът за проверка на Ð¿Ñ€Ð°Ð²Ð¾Ð¿Ð¸Ñ Ð½Ðµ е инÑталиран. Желаете ли да го инÑталирате ?",
+
+// Button Dialog
+DlgButtonText : "ТекÑÑ‚ (СтойноÑÑ‚)",
+DlgButtonType : "Тип",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Име",
+DlgCheckboxValue : "СтойноÑÑ‚",
+DlgCheckboxSelected : "Отметнато",
+
+// Form Dialog
+DlgFormName : "Име",
+DlgFormAction : "ДейÑтвие",
+DlgFormMethod : "Метод",
+
+// Select Field Dialog
+DlgSelectName : "Име",
+DlgSelectValue : "СтойноÑÑ‚",
+DlgSelectSize : "Размер",
+DlgSelectLines : "линии",
+DlgSelectChkMulti : "Разрешено множеÑтвено Ñелектиране",
+DlgSelectOpAvail : "Възможни опции",
+DlgSelectOpText : "ТекÑÑ‚",
+DlgSelectOpValue : "СтойноÑÑ‚",
+DlgSelectBtnAdd : "Добави",
+DlgSelectBtnModify : "Промени",
+DlgSelectBtnUp : "Ðагоре",
+DlgSelectBtnDown : "Ðадолу",
+DlgSelectBtnSetValue : "ÐаÑтрой като избрана ÑтойноÑÑ‚",
+DlgSelectBtnDelete : "Изтрий",
+
+// Textarea Dialog
+DlgTextareaName : "Име",
+DlgTextareaCols : "Колони",
+DlgTextareaRows : "Редове",
+
+// Text Field Dialog
+DlgTextName : "Име",
+DlgTextValue : "СтойноÑÑ‚",
+DlgTextCharWidth : "Ширина на Ñимволите",
+DlgTextMaxChars : "МакÑимум Ñимволи",
+DlgTextType : "Тип",
+DlgTextTypeText : "ТекÑÑ‚",
+DlgTextTypePass : "Парола",
+
+// Hidden Field Dialog
+DlgHiddenName : "Име",
+DlgHiddenValue : "СтойноÑÑ‚",
+
+// Bulleted List Dialog
+BulletedListProp : "Параметри на Ð½ÐµÐ½ÑƒÐ¼ÐµÑ€Ð¸Ñ€Ð°Ð½Ð¸Ñ ÑпиÑък",
+NumberedListProp : "Параметри на Ð½ÑƒÐ¼ÐµÑ€Ð¸Ñ€Ð°Ð½Ð¸Ñ ÑпиÑък",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Тип",
+DlgLstTypeCircle : "ОкръжноÑÑ‚",
+DlgLstTypeDisc : "Кръг",
+DlgLstTypeSquare : "Квадрат",
+DlgLstTypeNumbers : "ЧиÑла (1, 2, 3)",
+DlgLstTypeLCase : "Малки букви (a, b, c)",
+DlgLstTypeUCase : "Големи букви (A, B, C)",
+DlgLstTypeSRoman : "Малки римÑки чиÑла (i, ii, iii)",
+DlgLstTypeLRoman : "Големи римÑки чиÑла (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Общи",
+DlgDocBackTab : "Фон",
+DlgDocColorsTab : "Цветове и отÑтъпи",
+DlgDocMetaTab : "Мета данни",
+
+DlgDocPageTitle : "Заглавие на Ñтраницата",
+DlgDocLangDir : "ПоÑока на речта",
+DlgDocLangDirLTR : "От лÑво на дÑÑно",
+DlgDocLangDirRTL : "От дÑÑно на лÑво",
+DlgDocLangCode : "Код на езика",
+DlgDocCharSet : "Кодиране на Ñимволите",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Друго кодиране на Ñимволите",
+
+DlgDocDocType : "Тип на документа",
+DlgDocDocTypeOther : "Друг тип на документа",
+DlgDocIncXHTML : "Включи XHTML декларациÑ",
+DlgDocBgColor : "ЦвÑÑ‚ на фона",
+DlgDocBgImage : "Пълен път до фоновото изображение",
+DlgDocBgNoScroll : "Ðе-повтарÑщо Ñе фоново изображение",
+DlgDocCText : "ТекÑÑ‚",
+DlgDocCLink : "Връзка",
+DlgDocCVisited : "ПоÑетена връзка",
+DlgDocCActive : "Ðктивна връзка",
+DlgDocMargins : "ОтÑтъпи на Ñтраницата",
+DlgDocMaTop : "Горе",
+DlgDocMaLeft : "ЛÑво",
+DlgDocMaRight : "ДÑÑно",
+DlgDocMaBottom : "Долу",
+DlgDocMeIndex : "Ключови думи за документа (разделени ÑÑŠÑ Ð·Ð°Ð¿ÐµÑ‚Ð°Ð¸)",
+DlgDocMeDescr : "ОпиÑание на документа",
+DlgDocMeAuthor : "Ðвтор",
+DlgDocMeCopy : "ÐвторÑки права",
+DlgDocPreview : "Изглед",
+
+// Templates Dialog
+Templates : "Шаблони",
+DlgTemplatesTitle : "Шаблони",
+DlgTemplatesSelMsg : "Изберете шаблон <br>(текущото Ñъдържание на редактора ще бъде загубено):",
+DlgTemplatesLoading : "Зареждане на ÑпиÑъка Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¸Ñ‚Ðµ. ÐœÐ¾Ð»Ñ Ð¸Ð·Ñ‡Ð°ÐºÐ°Ð¹Ñ‚Ðµ...",
+DlgTemplatesNoTpl : "(ÐÑма дефинирани шаблони)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "За",
+DlgAboutBrowserInfoTab : "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° браузъра",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "верÑиÑ",
+DlgAboutInfo : "За повече Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾Ñетете"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/bn.js b/httemplate/elements/fckeditor/editor/lang/bn.js
new file mode 100644
index 0000000..8f76754
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/bn.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Bengali/Bangla language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "টূলবার গà§à¦Ÿà¦¿à§Ÿà§‡ দাও",
+ToolbarExpand : "টূলবার ছড়িয়ে দাও",
+
+// Toolbar Items and Context Menu
+Save : "সংরকà§à¦·à¦¨ কর",
+NewPage : "নতà§à¦¨ পেজ",
+Preview : "পà§à¦°à¦¿à¦­à¦¿à¦‰",
+Cut : "কাট",
+Copy : "কপি",
+Paste : "পেসà§à¦Ÿ",
+PasteText : "পেসà§à¦Ÿ (সাদা টেকà§à¦¸à¦Ÿ)",
+PasteWord : "পেসà§à¦Ÿ (শবà§à¦¦)",
+Print : "পà§à¦°à¦¿à¦¨à§à¦Ÿ",
+SelectAll : "সব সিলেকà§à¦Ÿ কর",
+RemoveFormat : "ফরমেট সরাও",
+InsertLinkLbl : "লিংকের যà§à¦•à§à¦¤ করার লেবেল",
+InsertLink : "লিংক যà§à¦•à§à¦¤ কর",
+RemoveLink : "লিংক সরাও",
+Anchor : "নোঙà§à¦—র",
+InsertImageLbl : "ছবির লেবেল যà§à¦•à§à¦¤ কর",
+InsertImage : "ছবি যà§à¦•à§à¦¤ কর",
+InsertFlashLbl : "ফà§à¦²à¦¾à¦¶ লেবেল যà§à¦•à§à¦¤ কর",
+InsertFlash : "ফà§à¦²à¦¾à¦¶ যà§à¦•à§à¦¤ কর",
+InsertTableLbl : "টেবিলের লেবেল যà§à¦•à§à¦¤ কর",
+InsertTable : "টেবিল যà§à¦•à§à¦¤ কর",
+InsertLineLbl : "রেখা যà§à¦•à§à¦¤ কর",
+InsertLine : "রেখা যà§à¦•à§à¦¤ কর",
+InsertSpecialCharLbl: "বিশেষ অকà§à¦·à¦°à§‡à¦° লেবেল যà§à¦•à§à¦¤ কর",
+InsertSpecialChar : "বিশেষ অকà§à¦·à¦° যà§à¦•à§à¦¤ কর",
+InsertSmileyLbl : "সà§à¦®à¦¾à¦‡à¦²à§€",
+InsertSmiley : "সà§à¦®à¦¾à¦‡à¦²à§€ যà§à¦•à§à¦¤ কর",
+About : "FCKeditor কে বানিয়েছে",
+Bold : "বোলà§à¦¡",
+Italic : "ইটালিক",
+Underline : "আনà§à¦¡à¦¾à¦°à¦²à¦¾à¦‡à¦¨",
+StrikeThrough : "সà§à¦Ÿà§à¦°à¦¾à¦‡à¦• থà§à¦°à§",
+Subscript : "অধোলেখ",
+Superscript : "অভিলেখ",
+LeftJustify : "বা দিকে ঘেà¦à¦·à¦¾",
+CenterJustify : "মাঠবরাবর ঘেষা",
+RightJustify : "ডান দিকে ঘেà¦à¦·à¦¾",
+BlockJustify : "বà§à¦²à¦• জাসà§à¦Ÿà¦¿à¦«à¦¾à¦‡",
+DecreaseIndent : "ইনডেনà§à¦Ÿ কমাও",
+IncreaseIndent : "ইনডেনà§à¦Ÿ বাড়াও",
+Undo : "আনডà§",
+Redo : "রি-ডà§",
+NumberedListLbl : "সাংখà§à¦¯à¦¿à¦• লিসà§à¦Ÿà§‡à¦° লেবেল",
+NumberedList : "সাংখà§à¦¯à¦¿à¦• লিসà§à¦Ÿ",
+BulletedListLbl : "বà§à¦²à§‡à¦Ÿ লিসà§à¦Ÿ লেবেল",
+BulletedList : "বà§à¦²à§‡à¦Ÿà§‡à¦¡ লিসà§à¦Ÿ",
+ShowTableBorders : "টেবিল বরà§à¦¡à¦¾à¦°",
+ShowDetails : "সবটà§à¦•à§ দেখাও",
+Style : "সà§à¦Ÿà¦¾à¦‡à¦²",
+FontFormat : "ফনà§à¦Ÿ ফরমেট",
+Font : "ফনà§à¦Ÿ",
+FontSize : "সাইজ",
+TextColor : "টেকà§à¦¸à§à¦Ÿ রং",
+BGColor : "বেকগà§à¦°à¦¾à¦‰à¦¨à§à¦¡ রং",
+Source : "সোরà§à¦¸",
+Find : "খোজো",
+Replace : "রিপà§à¦²à§‡à¦¸",
+SpellCheck : "বানান চেক",
+UniversalKeyboard : "সারà§à¦¬à¦œà¦¨à§€à¦¨ কিবোরà§à¦¡",
+PageBreakLbl : "পেজ বà§à¦°à§‡à¦• লেবেল",
+PageBreak : "পেজ বà§à¦°à§‡à¦•",
+
+Form : "ফরà§à¦®",
+Checkbox : "চেক বাকà§à¦¸",
+RadioButton : "রেডিও বাটন",
+TextField : "টেকà§à¦¸à¦Ÿ ফীলà§à¦¡",
+Textarea : "টেকà§à¦¸à¦Ÿ à¦à¦°à¦¿à§Ÿà¦¾",
+HiddenField : "গà§à¦ªà§à¦¤ ফীলà§à¦¡",
+Button : "বাটন",
+SelectionField : "বাছাই ফীলà§à¦¡",
+ImageButton : "ছবির বাটন",
+
+FitWindow : "উইনà§à¦¡à§‹ ফিট কর",
+
+// Context Menu
+EditLink : "লিংক সমà§à¦ªà¦¾à¦¦à¦¨",
+CellCM : "সেল",
+RowCM : "রো",
+ColumnCM : "কলাম",
+InsertRow : "রো যà§à¦•à§à¦¤ কর",
+DeleteRows : "রো মà§à¦›à§‡ দাও",
+InsertColumn : "কলাম যà§à¦•à§à¦¤ কর",
+DeleteColumns : "কলাম মà§à¦›à§‡ দাও",
+InsertCell : "সেল যà§à¦•à§à¦¤ কর",
+DeleteCells : "সেল মà§à¦›à§‡ দাও",
+MergeCells : "সেল জোড়া দাও",
+SplitCell : "সেল আলাদা কর",
+TableDelete : "টেবিল ডিলীট কর",
+CellProperties : "সেলের পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿à¦œ",
+TableProperties : "টেবিল পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+ImageProperties : "ছবি পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+FlashProperties : "ফà§à¦²à¦¾à¦¶ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+
+AnchorProp : "নোঙর পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+ButtonProp : "বাটন পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+CheckboxProp : "চেক বকà§à¦¸ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+HiddenFieldProp : "গà§à¦ªà§à¦¤ ফীলà§à¦¡ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+RadioButtonProp : "রেডিও বাটন পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+ImageButtonProp : "ছবি বাটন পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+TextFieldProp : "টেকà§à¦¸à¦Ÿ ফীলà§à¦¡ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+SelectionFieldProp : "বাছাই ফীলà§à¦¡ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+TextareaProp : "টেকà§à¦¸à¦Ÿ à¦à¦°à¦¿à§Ÿà¦¾ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+FormProp : "ফরà§à¦® পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+
+FontFormats : "সাধারণ;ফরà§à¦®à§‡à¦Ÿà§‡à¦¡;ঠিকানা;শীরà§à¦·à¦• ১;শীরà§à¦·à¦• ২;শীরà§à¦·à¦• ৩;শীরà§à¦·à¦• ৪;শীরà§à¦·à¦• ৫;শীরà§à¦·à¦• ৬;শীরà§à¦·à¦• (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML পà§à¦°à¦¸à§‡à¦¸ করা হচà§à¦›à§‡",
+Done : "শেষ হয়েছে",
+PasteWordConfirm : "যে টেকসà§à¦Ÿà¦Ÿà¦¿ আপনি পেসà§à¦Ÿ করতে চাচà§à¦›à§‡à¦¨ মনে হচà§à¦›à§‡ সেটি ওয়ারà§à¦¡ থেকে কপি করা। আপনি কি পেসà§à¦Ÿ করার আগে à¦à¦•à§‡ পরিষà§à¦•à¦¾à¦° করতে চান?",
+NotCompatiblePaste : "à¦à¦‡ কমানà§à¦¡à¦Ÿà¦¿ শà§à¦§à§à¦®à¦¾à¦¤à§à¦° ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ à¦à¦•à§à¦¸à¦ªà§à¦²à§‹à¦°à¦¾à¦° ৫.০ বা তার পরের ভারà§à¦¸à¦¨à§‡ পাওয়া সমà§à¦­à¦¬à¥¤ আপনি কি পরিষà§à¦•à¦¾à¦° না করেই পেসà§à¦Ÿ করতে চান?",
+UnknownToolbarItem : "অজানা টà§à¦²à¦¬à¦¾à¦° আইটেম \"%1\"",
+UnknownCommand : "অজানা কমানà§à¦¡ \"%1\"",
+NotImplemented : "কমানà§à¦¡ ইমপà§à¦²à¦¿à¦®à§‡à¦¨à§à¦Ÿ করা হয়নি",
+UnknownToolbarSet : "টà§à¦²à¦¬à¦¾à¦° সেট \"%1\" à¦à¦° অসà§à¦¤à¦¿à¦¤à§à¦¬ নেই",
+NoActiveX : "আপনার বà§à¦°à¦¾à¦‰à¦œà¦¾à¦°à§‡à¦° সà§à¦°à¦•à§à¦·à¦¾ সেটিংস কারনে à¦à¦¡à¦¿à¦Ÿà¦°à§‡à¦° কিছৠফিচার পাওয়া নাও যেতে পারে। আপনাকে অবশà§à¦¯à¦‡ \"Run ActiveX controls and plug-ins\" à¦à¦¨à¦¾à¦¬à§‡à¦² করে নিতে হবে। আপনি ভà§à¦²à¦­à§à¦°à¦¾à¦¨à§à¦¤à¦¿ কিছৠকিছৠফিচারের অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤à¦¿ উপলবà§à¦§à¦¿ করতে পারেন।",
+BrowseServerBlocked : "রিসোরà§à¦¸ বà§à¦°à¦¾à¦‰à¦œà¦¾à¦° খোলা গেল না। নিশà§à¦šà¦¿à¦¤ করà§à¦¨ যে সব পপআপ বà§à¦²à¦•à¦¾à¦° বনà§à¦§ করা আছে।",
+DialogBlocked : "ডায়ালগ ইউনà§à¦¡à§‹ খোলা গেল না। নিশà§à¦šà¦¿à¦¤ করà§à¦¨ যে সব পপআপ বà§à¦²à¦•à¦¾à¦° বনà§à¦§ করা আছে।",
+
+// Dialogs
+DlgBtnOK : "ওকে",
+DlgBtnCancel : "বাতিল",
+DlgBtnClose : "বনà§à¦§ কর",
+DlgBtnBrowseServer : "বà§à¦°à¦¾à¦‰à¦œ সারà§à¦­à¦¾à¦°",
+DlgAdvancedTag : "à¦à¦¡à¦­à¦¾à¦¨à§à¦¸à¦¡",
+DlgOpOther : "<অনà§à¦¯>",
+DlgInfoTab : "তথà§à¦¯",
+DlgAlertUrl : "দয়া করে URL যà§à¦•à§à¦¤ করà§à¦¨",
+
+// General Dialogs Labels
+DlgGenNotSet : "<সেট নেই>",
+DlgGenId : "আইডি",
+DlgGenLangDir : "ভাষা লেখার দিক",
+DlgGenLangDirLtr : "বাম থেকে ডান (LTR)",
+DlgGenLangDirRtl : "ডান থেকে বাম (RTL)",
+DlgGenLangCode : "ভাষা কোড",
+DlgGenAccessKey : "à¦à¦•à§à¦¸à§‡à¦¸ কী",
+DlgGenName : "নাম",
+DlgGenTabIndex : "টà§à¦¯à¦¾à¦¬ ইনà§à¦¡à§‡à¦•à§à¦¸",
+DlgGenLongDescr : "URL à¦à¦° লমà§à¦¬à¦¾ বরà§à¦£à¦¨à¦¾",
+DlgGenClass : "সà§à¦Ÿà¦¾à¦‡à¦²-শীট কà§à¦²à¦¾à¦¸",
+DlgGenTitle : "পরামরà§à¦¶ শীরà§à¦·à¦•",
+DlgGenContType : "পরামরà§à¦¶ কনà§à¦Ÿà§‡à¦¨à§à¦Ÿà§‡à¦° পà§à¦°à¦•à¦¾à¦°",
+DlgGenLinkCharset : "লিংক রিসোরà§à¦¸ কà§à¦¯à¦¾à¦°à§‡à¦•à§à¦Ÿà¦° সেট",
+DlgGenStyle : "সà§à¦Ÿà¦¾à¦‡à¦²",
+
+// Image Dialog
+DlgImgTitle : "ছবির পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+DlgImgInfoTab : "ছবির তথà§à¦¯",
+DlgImgBtnUpload : "ইহাকে সারà§à¦­à¦¾à¦°à§‡ পà§à¦°à§‡à¦°à¦¨ কর",
+DlgImgURL : "URL",
+DlgImgUpload : "আপলোড",
+DlgImgAlt : "বিকলà§à¦ª টেকà§à¦¸à¦Ÿ",
+DlgImgWidth : "পà§à¦°à¦¸à§à¦¥",
+DlgImgHeight : "দৈরà§à¦˜à§à¦¯",
+DlgImgLockRatio : "অনà§à¦ªà¦¾à¦¤ লক কর",
+DlgBtnResetSize : "সাইজ পূরà§à¦¬à¦¾à¦¬à¦¸à§à¦¥à¦¾à§Ÿ ফিরিয়ে দাও",
+DlgImgBorder : "বরà§à¦¡à¦¾à¦°",
+DlgImgHSpace : "হরাইজনà§à¦Ÿà¦¾à¦² সà§à¦ªà§‡à¦¸",
+DlgImgVSpace : "ভারà§à¦Ÿà¦¿à¦•à§‡à¦² সà§à¦ªà§‡à¦¸",
+DlgImgAlign : "à¦à¦²à¦¾à¦‡à¦¨",
+DlgImgAlignLeft : "বামে",
+DlgImgAlignAbsBottom: "Abs নীচে",
+DlgImgAlignAbsMiddle: "Abs উপর",
+DlgImgAlignBaseline : "মূল রেখা",
+DlgImgAlignBottom : "নীচে",
+DlgImgAlignMiddle : "মধà§à¦¯",
+DlgImgAlignRight : "ডানে",
+DlgImgAlignTextTop : "টেকà§à¦¸à¦Ÿ উপর",
+DlgImgAlignTop : "উপর",
+DlgImgPreview : "পà§à¦°à§€à¦­à¦¿à¦‰",
+DlgImgAlertUrl : "অনà§à¦—à§à¦°à¦¹à¦• করে ছবির URL টাইপ করà§à¦¨",
+DlgImgLinkTab : "লিংক",
+
+// Flash Dialog
+DlgFlashTitle : "ফà§à¦²à§à¦¯à¦¾à¦¶ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+DlgFlashChkPlay : "অটো পà§à¦²à§‡",
+DlgFlashChkLoop : "লূপ",
+DlgFlashChkMenu : "ফà§à¦²à§à¦¯à¦¾à¦¶ মেনৠà¦à¦¨à¦¾à¦¬à¦² কর",
+DlgFlashScale : "সà§à¦•à§‡à¦²",
+DlgFlashScaleAll : "সব দেখাও",
+DlgFlashScaleNoBorder : "কোনো বরà§à¦¡à¦¾à¦° নেই",
+DlgFlashScaleFit : "নিখà§à¦à¦¤ ফিট",
+
+// Link Dialog
+DlgLnkWindowTitle : "লিংক",
+DlgLnkInfoTab : "লিংক তথà§à¦¯",
+DlgLnkTargetTab : "টারà§à¦—েট",
+
+DlgLnkType : "লিংক পà§à¦°à¦•à¦¾à¦°",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "à¦à¦‡ পেজে নোঙর কর",
+DlgLnkTypeEMail : "ইমেইল",
+DlgLnkProto : "পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²",
+DlgLnkProtoOther : "<অনà§à¦¯>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "নোঙর বাছাই",
+DlgLnkAnchorByName : "নোঙরের নাম দিয়ে",
+DlgLnkAnchorById : "নোঙরের আইডি দিয়ে",
+DlgLnkNoAnchors : "<ডকà§à¦®à§‡à¦¨à§à¦Ÿà§‡ আর কোন নোঙর নেই>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ইমেইল ঠিকানা",
+DlgLnkEMailSubject : "মেসেজের বিষয়",
+DlgLnkEMailBody : "মেসেজের দেহ",
+DlgLnkUpload : "আপলোড",
+DlgLnkBtnUpload : "à¦à¦•à§‡ সারà§à¦­à¦¾à¦°à§‡ পাঠাও",
+
+DlgLnkTarget : "টারà§à¦—েট",
+DlgLnkTargetFrame : "<ফà§à¦°à§‡à¦®>",
+DlgLnkTargetPopup : "<পপআপ উইনà§à¦¡à§‹>",
+DlgLnkTargetBlank : "নতà§à¦¨ উইনà§à¦¡à§‹ (_blank)",
+DlgLnkTargetParent : "মূল উইনà§à¦¡à§‹ (_parent)",
+DlgLnkTargetSelf : "à¦à¦‡ উইনà§à¦¡à§‹ (_self)",
+DlgLnkTargetTop : "শীরà§à¦· উইনà§à¦¡à§‹ (_top)",
+DlgLnkTargetFrameName : "টারà§à¦—েট ফà§à¦°à§‡à¦®à§‡à¦° নাম",
+DlgLnkPopWinName : "পপআপ উইনà§à¦¡à§‹à¦° নাম",
+DlgLnkPopWinFeat : "পপআপ উইনà§à¦¡à§‹ ফীচার সমূহ",
+DlgLnkPopResize : "রিসাইজ করা সমà§à¦­à¦¬",
+DlgLnkPopLocation : "লোকেশন বার",
+DlgLnkPopMenu : "মেনà§à¦¯à§ বার",
+DlgLnkPopScroll : "সà§à¦•à§à¦°à¦² বার",
+DlgLnkPopStatus : "সà§à¦Ÿà§à¦¯à¦¾à¦Ÿà¦¾à¦¸ বার",
+DlgLnkPopToolbar : "টà§à¦² বার",
+DlgLnkPopFullScrn : "পূরà§à¦£ পরà§à¦¦à¦¾ জà§à§œà§‡ (IE)",
+DlgLnkPopDependent : "ডিপেনà§à¦¡à§‡à¦¨à§à¦Ÿ (Netscape)",
+DlgLnkPopWidth : "পà§à¦°à¦¸à§à¦¥",
+DlgLnkPopHeight : "দৈরà§à¦˜à§à¦¯",
+DlgLnkPopLeft : "বামের পজিশন",
+DlgLnkPopTop : "ডানের পজিশন",
+
+DlnLnkMsgNoUrl : "অনà§à¦—à§à¦°à¦¹ করে URL লিংক টাইপ করà§à¦¨",
+DlnLnkMsgNoEMail : "অনà§à¦—à§à¦°à¦¹ করে ইমেইল à¦à¦¡à§à¦°à§‡à¦¸ টাইপ করà§à¦¨",
+DlnLnkMsgNoAnchor : "অনà§à¦—à§à¦°à¦¹ করে নোঙর বাছাই করà§à¦¨",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "রং বাছাই কর",
+DlgColorBtnClear : "পরিষà§à¦•à¦¾à¦° কর",
+DlgColorHighlight : "হাইলাইট",
+DlgColorSelected : "সিলেকà§à¦Ÿà§‡à¦¡",
+
+// Smiley Dialog
+DlgSmileyTitle : "সà§à¦®à¦¾à¦‡à¦²à§€ যà§à¦•à§à¦¤ কর",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "বিশেষ কà§à¦¯à¦¾à¦°à§‡à¦•à§à¦Ÿà¦¾à¦° বাছাই কর",
+
+// Table Dialog
+DlgTableTitle : "টেবিল পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+DlgTableRows : "রো",
+DlgTableColumns : "কলাম",
+DlgTableBorder : "বরà§à¦¡à¦¾à¦° সাইজ",
+DlgTableAlign : "à¦à¦²à¦¾à¦‡à¦¨à¦®à§‡à¦¨à§à¦Ÿ",
+DlgTableAlignNotSet : "<সেট নেই>",
+DlgTableAlignLeft : "বামে",
+DlgTableAlignCenter : "মাà¦à¦–ানে",
+DlgTableAlignRight : "ডানে",
+DlgTableWidth : "পà§à¦°à¦¸à§à¦¥",
+DlgTableWidthPx : "পিকà§à¦¸à§‡à¦²",
+DlgTableWidthPc : "শতকরা",
+DlgTableHeight : "দৈরà§à¦˜à§à¦¯",
+DlgTableCellSpace : "সেল সà§à¦ªà§‡à¦¸",
+DlgTableCellPad : "সেল পà§à¦¯à¦¾à¦¡à¦¿à¦‚",
+DlgTableCaption : "শীরà§à¦·à¦•",
+DlgTableSummary : "সারাংশ",
+
+// Table Cell Dialog
+DlgCellTitle : "সেল পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+DlgCellWidth : "পà§à¦°à¦¸à§à¦¥",
+DlgCellWidthPx : "পিকà§à¦¸à§‡à¦²",
+DlgCellWidthPc : "শতকরা",
+DlgCellHeight : "দৈরà§à¦˜à§à¦¯",
+DlgCellWordWrap : "ওয়ারà§à¦¡ রেপ",
+DlgCellWordWrapNotSet : "<সেট নেই>",
+DlgCellWordWrapYes : "হাà¦",
+DlgCellWordWrapNo : "না",
+DlgCellHorAlign : "হরাইজনà§à¦Ÿà¦¾à¦² à¦à¦²à¦¾à¦‡à¦¨à¦®à§‡à¦¨à§à¦Ÿ",
+DlgCellHorAlignNotSet : "<সেট নেই>",
+DlgCellHorAlignLeft : "বামে",
+DlgCellHorAlignCenter : "মাà¦à¦–ানে",
+DlgCellHorAlignRight: "ডানে",
+DlgCellVerAlign : "ভারà§à¦Ÿà¦¿à¦•à§à¦¯à¦¾à¦² à¦à¦²à¦¾à¦‡à¦¨à¦®à§‡à¦¨à§à¦Ÿ",
+DlgCellVerAlignNotSet : "<সেট নেই>",
+DlgCellVerAlignTop : "উপর",
+DlgCellVerAlignMiddle : "মধà§à¦¯",
+DlgCellVerAlignBottom : "নীচে",
+DlgCellVerAlignBaseline : "মূলরেখা",
+DlgCellRowSpan : "রো সà§à¦ªà§à¦¯à¦¾à¦¨",
+DlgCellCollSpan : "কলাম সà§à¦ªà§à¦¯à¦¾à¦¨",
+DlgCellBackColor : "বà§à¦¯à¦¾à¦•à¦—à§à¦°à¦¾à¦‰à¦¨à§à¦¡ রং",
+DlgCellBorderColor : "বরà§à¦¡à¦¾à¦°à§‡à¦° রং",
+DlgCellBtnSelect : "বাছাই কর",
+
+// Find Dialog
+DlgFindTitle : "খোà¦à¦œà§‹",
+DlgFindFindBtn : "খোà¦à¦œà§‹",
+DlgFindNotFoundMsg : "আপনার উলà§à¦²à§‡à¦–িত টেকসà§à¦Ÿ পাওয়া যায়নি",
+
+// Replace Dialog
+DlgReplaceTitle : "বদলে দাও",
+DlgReplaceFindLbl : "যা খà§à¦à¦œà¦¤à§‡ হবে:",
+DlgReplaceReplaceLbl : "যার সাথে বদলাতে হবে:",
+DlgReplaceCaseChk : "কেস মিলাও",
+DlgReplaceReplaceBtn : "বদলে দাও",
+DlgReplaceReplAllBtn : "সব বদলে দাও",
+DlgReplaceWordChk : "পà§à¦°à¦¾ শবà§à¦¦ মেলাও",
+
+// Paste Operations / Dialog
+PasteErrorCut : "আপনার বà§à¦°à¦¾à¦‰à¦œà¦¾à¦°à§‡à¦° সà§à¦°à¦•à§à¦·à¦¾ সেটিংস à¦à¦¡à¦¿à¦Ÿà¦°à¦•à§‡ অটোমেটিক কাট করার অনà§à¦®à¦¤à¦¿ দেয়নি। দয়া করে à¦à¦‡ কাজের জনà§à¦¯ কিবোরà§à¦¡ বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨ (Ctrl+X)।",
+PasteErrorCopy : "আপনার বà§à¦°à¦¾à¦‰à¦œà¦¾à¦°à§‡à¦° সà§à¦°à¦•à§à¦·à¦¾ সেটিংস à¦à¦¡à¦¿à¦Ÿà¦°à¦•à§‡ অটোমেটিক কপি করার অনà§à¦®à¦¤à¦¿ দেয়নি। দয়া করে à¦à¦‡ কাজের জনà§à¦¯ কিবোরà§à¦¡ বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨ (Ctrl+C)।",
+
+PasteAsText : "সাদা টেকà§à¦¸à¦Ÿ হিসেবে পেসà§à¦Ÿ কর",
+PasteFromWord : "ওয়ারà§à¦¡ থেকে পেসà§à¦Ÿ কর",
+
+DlgPasteMsg2 : "অনà§à¦—à§à¦°à¦¹ করে নীচের বাকà§à¦¸à§‡ কিবোরà§à¦¡ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে (<STRONG>Ctrl+V</STRONG>) পেসà§à¦Ÿ করà§à¦¨ à¦à¦¬à¦‚ <STRONG>OK</STRONG> চাপ দিন",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "ফনà§à¦Ÿ ফেস ডেফিনেশন ইগনোর করà§à¦¨",
+DlgPasteRemoveStyles : "সà§à¦Ÿà¦¾à¦‡à¦² ডেফিনেশন সরিয়ে দিন",
+DlgPasteCleanBox : "বাকà§à¦¸ পরিষà§à¦•à¦¾à¦° করà§à¦¨",
+
+// Color Picker
+ColorAutomatic : "অটোমেটিক",
+ColorMoreColors : "আরও রং...",
+
+// Document Properties
+DocProps : "ডকà§à¦¯à§à¦®à§‡à¦¨à§à¦Ÿ পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+
+// Anchor Dialog
+DlgAnchorTitle : "নোঙরের পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+DlgAnchorName : "নোঙরের নাম",
+DlgAnchorErrorName : "নোঙরের নাম টাইপ করà§à¦¨",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "শবà§à¦¦à¦•à§‹à¦·à§‡ নেই",
+DlgSpellChangeTo : "à¦à¦¤à§‡ বদলাও",
+DlgSpellBtnIgnore : "ইগনোর কর",
+DlgSpellBtnIgnoreAll : "সব ইগনোর কর",
+DlgSpellBtnReplace : "বদলে দাও",
+DlgSpellBtnReplaceAll : "সব বদলে দাও",
+DlgSpellBtnUndo : "আনà§à¦¡à§",
+DlgSpellNoSuggestions : "- কোন সাজেশন নেই -",
+DlgSpellProgress : "বানান পরীকà§à¦·à¦¾ চলছে...",
+DlgSpellNoMispell : "বানান পরীকà§à¦·à¦¾ শেষ: কোন ভà§à¦² বানান পাওয়া যায়নি",
+DlgSpellNoChanges : "বানান পরীকà§à¦·à¦¾ শেষ: কোন শবà§à¦¦ পরিবরà§à¦¤à¦¨ করা হয়নি",
+DlgSpellOneChange : "বানান পরীকà§à¦·à¦¾ শেষ: à¦à¦•à¦Ÿà¦¿ মাতà§à¦° শবà§à¦¦ পরিবরà§à¦¤à¦¨ করা হয়েছে",
+DlgSpellManyChanges : "বানান পরীকà§à¦·à¦¾ শেষ: %1 গà§à¦²à§‹ শবà§à¦¦ বদলে গà§à¦¯à¦¾à¦›à§‡",
+
+IeSpellDownload : "বানান পরীকà§à¦·à¦• ইনসà§à¦Ÿà¦² করা নেই। আপনি কি à¦à¦–নই à¦à¦Ÿà¦¾ ডাউনলোড করতে চান?",
+
+// Button Dialog
+DlgButtonText : "টেকà§à¦¸à¦Ÿ (ভà§à¦¯à¦¾à¦²à§)",
+DlgButtonType : "পà§à¦°à¦•à¦¾à¦°",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "নাম",
+DlgCheckboxValue : "ভà§à¦¯à¦¾à¦²à§",
+DlgCheckboxSelected : "সিলেকà§à¦Ÿà§‡à¦¡",
+
+// Form Dialog
+DlgFormName : "নাম",
+DlgFormAction : "à¦à¦•à¦¶à§à¦¯à¦¨",
+DlgFormMethod : "পদà§à¦§à¦¤à¦¿",
+
+// Select Field Dialog
+DlgSelectName : "নাম",
+DlgSelectValue : "ভà§à¦¯à¦¾à¦²à§",
+DlgSelectSize : "সাইজ",
+DlgSelectLines : "লাইন সমূহ",
+DlgSelectChkMulti : "à¦à¦•à¦¾à¦§à¦¿à¦• সিলেকশন à¦à¦²à¦¾à¦‰ কর",
+DlgSelectOpAvail : "অনà§à¦¯à¦¾à¦¨à§à¦¯ বিকলà§à¦ª",
+DlgSelectOpText : "টেকà§à¦¸à¦Ÿ",
+DlgSelectOpValue : "ভà§à¦¯à¦¾à¦²à§",
+DlgSelectBtnAdd : "যà§à¦•à§à¦¤",
+DlgSelectBtnModify : "বদলে দাও",
+DlgSelectBtnUp : "উপর",
+DlgSelectBtnDown : "নীচে",
+DlgSelectBtnSetValue : "বাছাই করা ভà§à¦¯à¦¾à¦²à§ হিসেবে সেট কর",
+DlgSelectBtnDelete : "ডিলীট",
+
+// Textarea Dialog
+DlgTextareaName : "নাম",
+DlgTextareaCols : "কলাম",
+DlgTextareaRows : "রো",
+
+// Text Field Dialog
+DlgTextName : "নাম",
+DlgTextValue : "ভà§à¦¯à¦¾à¦²à§",
+DlgTextCharWidth : "কà§à¦¯à¦¾à¦°à§‡à¦•à§à¦Ÿà¦¾à¦° পà§à¦°à¦¶à¦¸à§à¦¤à¦¤à¦¾",
+DlgTextMaxChars : "সরà§à¦¬à¦¾à¦§à¦¿à¦• কà§à¦¯à¦¾à¦°à§‡à¦•à§à¦Ÿà¦¾à¦°",
+DlgTextType : "টাইপ",
+DlgTextTypeText : "টেকà§à¦¸à¦Ÿ",
+DlgTextTypePass : "পাসওয়ারà§à¦¡",
+
+// Hidden Field Dialog
+DlgHiddenName : "নাম",
+DlgHiddenValue : "ভà§à¦¯à¦¾à¦²à§",
+
+// Bulleted List Dialog
+BulletedListProp : "বà§à¦²à§‡à¦Ÿà§‡à¦¡ সূচী পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+NumberedListProp : "সাংখà§à¦¯à¦¿à¦• সূচী পà§à¦°à§‹à¦ªà¦¾à¦°à§à¦Ÿà¦¿",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "পà§à¦°à¦•à¦¾à¦°",
+DlgLstTypeCircle : "গোল",
+DlgLstTypeDisc : "ডিসà§à¦•",
+DlgLstTypeSquare : "চৌকোণা",
+DlgLstTypeNumbers : "সংখà§à¦¯à¦¾ (1, 2, 3)",
+DlgLstTypeLCase : "ছোট অকà§à¦·à¦° (a, b, c)",
+DlgLstTypeUCase : "বড় অকà§à¦·à¦° (A, B, C)",
+DlgLstTypeSRoman : "ছোট রোমান সংখà§à¦¯à¦¾ (i, ii, iii)",
+DlgLstTypeLRoman : "বড় রোমান সংখà§à¦¯à¦¾ (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "সাধারন",
+DlgDocBackTab : "বà§à¦¯à¦¾à¦•à¦—à§à¦°à¦¾à¦‰à¦¨à§à¦¡",
+DlgDocColorsTab : "রং à¦à¦¬à¦‚ মারà§à¦œà¦¿à¦¨",
+DlgDocMetaTab : "মেটাডেটা",
+
+DlgDocPageTitle : "পেজ শীরà§à¦·à¦•",
+DlgDocLangDir : "ভাষা লিখার দিক",
+DlgDocLangDirLTR : "বাম থেকে ডানে (LTR)",
+DlgDocLangDirRTL : "ডান থেকে বামে (RTL)",
+DlgDocLangCode : "ভাষা কোড",
+DlgDocCharSet : "কà§à¦¯à¦¾à¦°à§‡à¦•à§à¦Ÿà¦¾à¦° সেট à¦à¦¨à¦•à§‹à¦¡à¦¿à¦‚",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "অনà§à¦¯ কà§à¦¯à¦¾à¦°à§‡à¦•à§à¦Ÿà¦¾à¦° সেট à¦à¦¨à¦•à§‹à¦¡à¦¿à¦‚",
+
+DlgDocDocType : "ডকà§à¦¯à§à¦®à§‡à¦¨à§à¦Ÿ টাইপ হেডিং",
+DlgDocDocTypeOther : "অনà§à¦¯ ডকà§à¦¯à§à¦®à§‡à¦¨à§à¦Ÿ টাইপ হেডিং",
+DlgDocIncXHTML : "XHTML ডেকà§à¦²à¦¾à¦°à§‡à¦¶à¦¨ যà§à¦•à§à¦¤ কর",
+DlgDocBgColor : "বà§à¦¯à¦¾à¦•à¦—à§à¦°à¦¾à¦‰à¦¨à§à¦¡ রং",
+DlgDocBgImage : "বà§à¦¯à¦¾à¦•à¦—à§à¦°à¦¾à¦‰à¦¨à§à¦¡ ছবির URL",
+DlgDocBgNoScroll : "সà§à¦•à§à¦°à¦²à¦¹à§€à¦¨ বà§à¦¯à¦¾à¦•à¦—à§à¦°à¦¾à¦‰à¦¨à§à¦¡",
+DlgDocCText : "টেকà§à¦¸à¦Ÿ",
+DlgDocCLink : "লিংক",
+DlgDocCVisited : "ভিজিট করা লিংক",
+DlgDocCActive : "সকà§à¦°à¦¿à§Ÿ লিংক",
+DlgDocMargins : "পেজ মারà§à¦œà¦¿à¦¨",
+DlgDocMaTop : "উপর",
+DlgDocMaLeft : "বামে",
+DlgDocMaRight : "ডানে",
+DlgDocMaBottom : "নীচে",
+DlgDocMeIndex : "ডকà§à¦¯à§à¦®à§‡à¦¨à§à¦Ÿ ইনà§à¦¡à§‡à¦•à§à¦¸ কিওয়ারà§à¦¡ (কমা দà§à¦¬à¦¾à¦°à¦¾ বিচà§à¦›à¦¿à¦¨à§à¦¨)",
+DlgDocMeDescr : "ডকà§à¦¯à§‚মেনà§à¦Ÿ বরà§à¦£à¦¨à¦¾",
+DlgDocMeAuthor : "লেখক",
+DlgDocMeCopy : "কপীরাইট",
+DlgDocPreview : "পà§à¦°à§€à¦­à¦¿à¦‰",
+
+// Templates Dialog
+Templates : "টেমপà§à¦²à§‡à¦Ÿ",
+DlgTemplatesTitle : "কনটেনà§à¦Ÿ টেমপà§à¦²à§‡à¦Ÿ",
+DlgTemplatesSelMsg : "অনà§à¦—à§à¦°à¦¹ করে à¦à¦¡à¦¿à¦Ÿà¦°à§‡ ওপেন করার জনà§à¦¯ টেমপà§à¦²à§‡à¦Ÿ বাছাই করà§à¦¨<br>(আসল কনটেনà§à¦Ÿ হারিয়ে যাবে):",
+DlgTemplatesLoading : "টেমপà§à¦²à§‡à¦Ÿ লিসà§à¦Ÿ হারিয়ে যাবে। অনà§à¦—à§à¦°à¦¹ করে অপেকà§à¦·à¦¾ করà§à¦¨...",
+DlgTemplatesNoTpl : "(কোন টেমপà§à¦²à§‡à¦Ÿ ডিফাইন করা নেই)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "কে বানিয়েছে",
+DlgAboutBrowserInfoTab : "বà§à¦°à¦¾à¦‰à¦œà¦¾à¦°à§‡à¦° বà§à¦¯à¦¾à¦ªà¦¾à¦°à§‡ তথà§à¦¯",
+DlgAboutLicenseTab : "লাইসেনà§à¦¸",
+DlgAboutVersion : "ভারà§à¦¸à¦¨",
+DlgAboutInfo : "আরও তথà§à¦¯à§‡à¦° জনà§à¦¯ যান"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/bs.js b/httemplate/elements/fckeditor/editor/lang/bs.js
new file mode 100644
index 0000000..fbaa451
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/bs.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Bosnian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Skupi trake sa alatima",
+ToolbarExpand : "Otvori trake sa alatima",
+
+// Toolbar Items and Context Menu
+Save : "Snimi",
+NewPage : "Novi dokument",
+Preview : "Prikaži",
+Cut : "Izreži",
+Copy : "Kopiraj",
+Paste : "Zalijepi",
+PasteText : "Zalijepi kao obièan tekst",
+PasteWord : "Zalijepi iz Word-a",
+Print : "Å tampaj",
+SelectAll : "Selektuj sve",
+RemoveFormat : "Poništi format",
+InsertLinkLbl : "Link",
+InsertLink : "Ubaci/Izmjeni link",
+RemoveLink : "Izbriši link",
+Anchor : "Insert/Edit Anchor", //MISSING
+InsertImageLbl : "Slika",
+InsertImage : "Ubaci/Izmjeni sliku",
+InsertFlashLbl : "Flash", //MISSING
+InsertFlash : "Insert/Edit Flash", //MISSING
+InsertTableLbl : "Tabela",
+InsertTable : "Ubaci/Izmjeni tabelu",
+InsertLineLbl : "Linija",
+InsertLine : "Ubaci horizontalnu liniju",
+InsertSpecialCharLbl: "Specijalni karakter",
+InsertSpecialChar : "Ubaci specijalni karater",
+InsertSmileyLbl : "Smješko",
+InsertSmiley : "Ubaci smješka",
+About : "O FCKeditor-u",
+Bold : "Boldiraj",
+Italic : "Ukosi",
+Underline : "Podvuci",
+StrikeThrough : "Precrtaj",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Lijevo poravnanje",
+CenterJustify : "Centralno poravnanje",
+RightJustify : "Desno poravnanje",
+BlockJustify : "Puno poravnanje",
+DecreaseIndent : "Smanji uvod",
+IncreaseIndent : "Poveæaj uvod",
+Undo : "Vrati",
+Redo : "Ponovi",
+NumberedListLbl : "Numerisana lista",
+NumberedList : "Ubaci/Izmjeni numerisanu listu",
+BulletedListLbl : "Lista",
+BulletedList : "Ubaci/Izmjeni listu",
+ShowTableBorders : "Pokaži okvire tabela",
+ShowDetails : "Pokaži detalje",
+Style : "Stil",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "Velièina",
+TextColor : "Boja teksta",
+BGColor : "Boja pozadine",
+Source : "HTML kôd",
+Find : "Naði",
+Replace : "Zamjeni",
+SpellCheck : "Check Spelling", //MISSING
+UniversalKeyboard : "Universal Keyboard", //MISSING
+PageBreakLbl : "Page Break", //MISSING
+PageBreak : "Insert Page Break", //MISSING
+
+Form : "Form", //MISSING
+Checkbox : "Checkbox", //MISSING
+RadioButton : "Radio Button", //MISSING
+TextField : "Text Field", //MISSING
+Textarea : "Textarea", //MISSING
+HiddenField : "Hidden Field", //MISSING
+Button : "Button", //MISSING
+SelectionField : "Selection Field", //MISSING
+ImageButton : "Image Button", //MISSING
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Izmjeni link",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Ubaci red",
+DeleteRows : "Briši redove",
+InsertColumn : "Ubaci kolonu",
+DeleteColumns : "Briši kolone",
+InsertCell : "Ubaci æeliju",
+DeleteCells : "Briši æelije",
+MergeCells : "Spoji æelije",
+SplitCell : "Razdvoji æeliju",
+TableDelete : "Delete Table", //MISSING
+CellProperties : "Svojstva æelije",
+TableProperties : "Svojstva tabele",
+ImageProperties : "Svojstva slike",
+FlashProperties : "Flash Properties", //MISSING
+
+AnchorProp : "Anchor Properties", //MISSING
+ButtonProp : "Button Properties", //MISSING
+CheckboxProp : "Checkbox Properties", //MISSING
+HiddenFieldProp : "Hidden Field Properties", //MISSING
+RadioButtonProp : "Radio Button Properties", //MISSING
+ImageButtonProp : "Image Button Properties", //MISSING
+TextFieldProp : "Text Field Properties", //MISSING
+SelectionFieldProp : "Selection Field Properties", //MISSING
+TextareaProp : "Textarea Properties", //MISSING
+FormProp : "Form Properties", //MISSING
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Procesiram XHTML. Molim saèekajte...",
+Done : "Gotovo",
+PasteWordConfirm : "Tekst koji želite zalijepiti èini se da je kopiran iz Worda. Da li želite da se prvo oèisti?",
+NotCompatiblePaste : "Ova komanda je podržana u Internet Explorer-u verzijama 5.5 ili novijim. Da li želite da izvršite lijepljenje teksta bez èišæenja?",
+UnknownToolbarItem : "Nepoznata stavka sa trake sa alatima \"%1\"",
+UnknownCommand : "Nepoznata komanda \"%1\"",
+NotImplemented : "Komanda nije implementirana",
+UnknownToolbarSet : "Traka sa alatima \"%1\" ne postoji",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Odustani",
+DlgBtnClose : "Zatvori",
+DlgBtnBrowseServer : "Browse Server", //MISSING
+DlgAdvancedTag : "Naprednije",
+DlgOpOther : "<Other>", //MISSING
+DlgInfoTab : "Info", //MISSING
+DlgAlertUrl : "Please insert the URL", //MISSING
+
+// General Dialogs Labels
+DlgGenNotSet : "<nije podešeno>",
+DlgGenId : "Id",
+DlgGenLangDir : "Smjer pisanja",
+DlgGenLangDirLtr : "S lijeva na desno (LTR)",
+DlgGenLangDirRtl : "S desna na lijevo (RTL)",
+DlgGenLangCode : "Jezièni kôd",
+DlgGenAccessKey : "Pristupna tipka",
+DlgGenName : "Naziv",
+DlgGenTabIndex : "Tab indeks",
+DlgGenLongDescr : "Dugaèki opis URL-a",
+DlgGenClass : "Klase CSS stilova",
+DlgGenTitle : "Advisory title",
+DlgGenContType : "Advisory vrsta sadržaja",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Stil",
+
+// Image Dialog
+DlgImgTitle : "Svojstva slike",
+DlgImgInfoTab : "Info slike",
+DlgImgBtnUpload : "Å alji na server",
+DlgImgURL : "URL",
+DlgImgUpload : "Å alji",
+DlgImgAlt : "Tekst na slici",
+DlgImgWidth : "Å irina",
+DlgImgHeight : "Visina",
+DlgImgLockRatio : "Zakljuèaj odnos",
+DlgBtnResetSize : "Resetuj dimenzije",
+DlgImgBorder : "Okvir",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Poravnanje",
+DlgImgAlignLeft : "Lijevo",
+DlgImgAlignAbsBottom: "Abs dole",
+DlgImgAlignAbsMiddle: "Abs sredina",
+DlgImgAlignBaseline : "Bazno",
+DlgImgAlignBottom : "Dno",
+DlgImgAlignMiddle : "Sredina",
+DlgImgAlignRight : "Desno",
+DlgImgAlignTextTop : "Vrh teksta",
+DlgImgAlignTop : "Vrh",
+DlgImgPreview : "Prikaz",
+DlgImgAlertUrl : "Molimo ukucajte URL od slike.",
+DlgImgLinkTab : "Link", //MISSING
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties", //MISSING
+DlgFlashChkPlay : "Auto Play", //MISSING
+DlgFlashChkLoop : "Loop", //MISSING
+DlgFlashChkMenu : "Enable Flash Menu", //MISSING
+DlgFlashScale : "Scale", //MISSING
+DlgFlashScaleAll : "Show all", //MISSING
+DlgFlashScaleNoBorder : "No Border", //MISSING
+DlgFlashScaleFit : "Exact Fit", //MISSING
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link info",
+DlgLnkTargetTab : "Prozor",
+
+DlgLnkType : "Tip linka",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Sidro na ovoj stranici",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<drugi>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Izaberi sidro",
+DlgLnkAnchorByName : "Po nazivu sidra",
+DlgLnkAnchorById : "Po Id-u elementa",
+DlgLnkNoAnchors : "<Nema dostupnih sidra na stranici>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail Adresa",
+DlgLnkEMailSubject : "Subjekt poruke",
+DlgLnkEMailBody : "Poruka",
+DlgLnkUpload : "Å alji",
+DlgLnkBtnUpload : "Å alji na server",
+
+DlgLnkTarget : "Prozor",
+DlgLnkTargetFrame : "<frejm>",
+DlgLnkTargetPopup : "<popup prozor>",
+DlgLnkTargetBlank : "Novi prozor (_blank)",
+DlgLnkTargetParent : "Glavni prozor (_parent)",
+DlgLnkTargetSelf : "Isti prozor (_self)",
+DlgLnkTargetTop : "Najgornji prozor (_top)",
+DlgLnkTargetFrameName : "Target Frame Name", //MISSING
+DlgLnkPopWinName : "Naziv popup prozora",
+DlgLnkPopWinFeat : "Moguænosti popup prozora",
+DlgLnkPopResize : "Promjenljive velièine",
+DlgLnkPopLocation : "Traka za lokaciju",
+DlgLnkPopMenu : "Izborna traka",
+DlgLnkPopScroll : "Scroll traka",
+DlgLnkPopStatus : "Statusna traka",
+DlgLnkPopToolbar : "Traka sa alatima",
+DlgLnkPopFullScrn : "Cijeli ekran (IE)",
+DlgLnkPopDependent : "Ovisno (Netscape)",
+DlgLnkPopWidth : "Å irina",
+DlgLnkPopHeight : "Visina",
+DlgLnkPopLeft : "Lijeva pozicija",
+DlgLnkPopTop : "Gornja pozicija",
+
+DlnLnkMsgNoUrl : "Molimo ukucajte URL link",
+DlnLnkMsgNoEMail : "Molimo ukucajte e-mail adresu",
+DlnLnkMsgNoAnchor : "Molimo izaberite sidro",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Izaberi boju",
+DlgColorBtnClear : "Oèisti",
+DlgColorHighlight : "Igled",
+DlgColorSelected : "Selektovana",
+
+// Smiley Dialog
+DlgSmileyTitle : "Ubaci smješka",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Izaberi specijalni karakter",
+
+// Table Dialog
+DlgTableTitle : "Svojstva tabele",
+DlgTableRows : "Redova",
+DlgTableColumns : "Kolona",
+DlgTableBorder : "Okvir",
+DlgTableAlign : "Poravnanje",
+DlgTableAlignNotSet : "<Nije podešeno>",
+DlgTableAlignLeft : "Lijevo",
+DlgTableAlignCenter : "Centar",
+DlgTableAlignRight : "Desno",
+DlgTableWidth : "Å irina",
+DlgTableWidthPx : "piksela",
+DlgTableWidthPc : "posto",
+DlgTableHeight : "Visina",
+DlgTableCellSpace : "Razmak æelija",
+DlgTableCellPad : "Uvod æelija",
+DlgTableCaption : "Naslov",
+DlgTableSummary : "Summary", //MISSING
+
+// Table Cell Dialog
+DlgCellTitle : "Svojstva æelije",
+DlgCellWidth : "Å irina",
+DlgCellWidthPx : "piksela",
+DlgCellWidthPc : "posto",
+DlgCellHeight : "Visina",
+DlgCellWordWrap : "Vrapuj tekst",
+DlgCellWordWrapNotSet : "<Nije podešeno>",
+DlgCellWordWrapYes : "Da",
+DlgCellWordWrapNo : "Ne",
+DlgCellHorAlign : "Horizontalno poravnanje",
+DlgCellHorAlignNotSet : "<Nije podešeno>",
+DlgCellHorAlignLeft : "Lijevo",
+DlgCellHorAlignCenter : "Centar",
+DlgCellHorAlignRight: "Desno",
+DlgCellVerAlign : "Vertikalno poravnanje",
+DlgCellVerAlignNotSet : "<Nije podešeno>",
+DlgCellVerAlignTop : "Gore",
+DlgCellVerAlignMiddle : "Sredina",
+DlgCellVerAlignBottom : "Dno",
+DlgCellVerAlignBaseline : "Bazno",
+DlgCellRowSpan : "Spajanje æelija",
+DlgCellCollSpan : "Spajanje kolona",
+DlgCellBackColor : "Boja pozadine",
+DlgCellBorderColor : "Boja okvira",
+DlgCellBtnSelect : "Selektuj...",
+
+// Find Dialog
+DlgFindTitle : "Naði",
+DlgFindFindBtn : "Naði",
+DlgFindNotFoundMsg : "Traženi tekst nije pronaðen.",
+
+// Replace Dialog
+DlgReplaceTitle : "Zamjeni",
+DlgReplaceFindLbl : "Naði šta:",
+DlgReplaceReplaceLbl : "Zamjeni sa:",
+DlgReplaceCaseChk : "Uporeðuj velika/mala slova",
+DlgReplaceReplaceBtn : "Zamjeni",
+DlgReplaceReplAllBtn : "Zamjeni sve",
+DlgReplaceWordChk : "Uporeðuj samo cijelu rijeè",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Sigurnosne postavke vašeg pretraživaèa ne dozvoljavaju operacije automatskog rezanja. Molimo koristite kraticu na tastaturi (Ctrl+X).",
+PasteErrorCopy : "Sigurnosne postavke Vašeg pretraživaèa ne dozvoljavaju operacije automatskog kopiranja. Molimo koristite kraticu na tastaturi (Ctrl+C).",
+
+PasteAsText : "Zalijepi kao obièan tekst",
+PasteFromWord : "Zalijepi iz Word-a",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<strong>Ctrl+V</strong>) and hit <strong>OK</strong>.", //MISSING
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignore Font Face definitions", //MISSING
+DlgPasteRemoveStyles : "Remove Styles definitions", //MISSING
+DlgPasteCleanBox : "Clean Up Box", //MISSING
+
+// Color Picker
+ColorAutomatic : "Automatska",
+ColorMoreColors : "Više boja...",
+
+// Document Properties
+DocProps : "Document Properties", //MISSING
+
+// Anchor Dialog
+DlgAnchorTitle : "Anchor Properties", //MISSING
+DlgAnchorName : "Anchor Name", //MISSING
+DlgAnchorErrorName : "Please type the anchor name", //MISSING
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Not in dictionary", //MISSING
+DlgSpellChangeTo : "Change to", //MISSING
+DlgSpellBtnIgnore : "Ignore", //MISSING
+DlgSpellBtnIgnoreAll : "Ignore All", //MISSING
+DlgSpellBtnReplace : "Replace", //MISSING
+DlgSpellBtnReplaceAll : "Replace All", //MISSING
+DlgSpellBtnUndo : "Undo", //MISSING
+DlgSpellNoSuggestions : "- No suggestions -", //MISSING
+DlgSpellProgress : "Spell check in progress...", //MISSING
+DlgSpellNoMispell : "Spell check complete: No misspellings found", //MISSING
+DlgSpellNoChanges : "Spell check complete: No words changed", //MISSING
+DlgSpellOneChange : "Spell check complete: One word changed", //MISSING
+DlgSpellManyChanges : "Spell check complete: %1 words changed", //MISSING
+
+IeSpellDownload : "Spell checker not installed. Do you want to download it now?", //MISSING
+
+// Button Dialog
+DlgButtonText : "Text (Value)", //MISSING
+DlgButtonType : "Type", //MISSING
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Name", //MISSING
+DlgCheckboxValue : "Value", //MISSING
+DlgCheckboxSelected : "Selected", //MISSING
+
+// Form Dialog
+DlgFormName : "Name", //MISSING
+DlgFormAction : "Action", //MISSING
+DlgFormMethod : "Method", //MISSING
+
+// Select Field Dialog
+DlgSelectName : "Name", //MISSING
+DlgSelectValue : "Value", //MISSING
+DlgSelectSize : "Size", //MISSING
+DlgSelectLines : "lines", //MISSING
+DlgSelectChkMulti : "Allow multiple selections", //MISSING
+DlgSelectOpAvail : "Available Options", //MISSING
+DlgSelectOpText : "Text", //MISSING
+DlgSelectOpValue : "Value", //MISSING
+DlgSelectBtnAdd : "Add", //MISSING
+DlgSelectBtnModify : "Modify", //MISSING
+DlgSelectBtnUp : "Up", //MISSING
+DlgSelectBtnDown : "Down", //MISSING
+DlgSelectBtnSetValue : "Set as selected value", //MISSING
+DlgSelectBtnDelete : "Delete", //MISSING
+
+// Textarea Dialog
+DlgTextareaName : "Name", //MISSING
+DlgTextareaCols : "Columns", //MISSING
+DlgTextareaRows : "Rows", //MISSING
+
+// Text Field Dialog
+DlgTextName : "Name", //MISSING
+DlgTextValue : "Value", //MISSING
+DlgTextCharWidth : "Character Width", //MISSING
+DlgTextMaxChars : "Maximum Characters", //MISSING
+DlgTextType : "Type", //MISSING
+DlgTextTypeText : "Text", //MISSING
+DlgTextTypePass : "Password", //MISSING
+
+// Hidden Field Dialog
+DlgHiddenName : "Name", //MISSING
+DlgHiddenValue : "Value", //MISSING
+
+// Bulleted List Dialog
+BulletedListProp : "Bulleted List Properties", //MISSING
+NumberedListProp : "Numbered List Properties", //MISSING
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Type", //MISSING
+DlgLstTypeCircle : "Circle", //MISSING
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "Square", //MISSING
+DlgLstTypeNumbers : "Numbers (1, 2, 3)", //MISSING
+DlgLstTypeLCase : "Lowercase Letters (a, b, c)", //MISSING
+DlgLstTypeUCase : "Uppercase Letters (A, B, C)", //MISSING
+DlgLstTypeSRoman : "Small Roman Numerals (i, ii, iii)", //MISSING
+DlgLstTypeLRoman : "Large Roman Numerals (I, II, III)", //MISSING
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General", //MISSING
+DlgDocBackTab : "Background", //MISSING
+DlgDocColorsTab : "Colors and Margins", //MISSING
+DlgDocMetaTab : "Meta Data", //MISSING
+
+DlgDocPageTitle : "Page Title", //MISSING
+DlgDocLangDir : "Language Direction", //MISSING
+DlgDocLangDirLTR : "Left to Right (LTR)", //MISSING
+DlgDocLangDirRTL : "Right to Left (RTL)", //MISSING
+DlgDocLangCode : "Language Code", //MISSING
+DlgDocCharSet : "Character Set Encoding", //MISSING
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Other Character Set Encoding", //MISSING
+
+DlgDocDocType : "Document Type Heading", //MISSING
+DlgDocDocTypeOther : "Other Document Type Heading", //MISSING
+DlgDocIncXHTML : "Include XHTML Declarations", //MISSING
+DlgDocBgColor : "Background Color", //MISSING
+DlgDocBgImage : "Background Image URL", //MISSING
+DlgDocBgNoScroll : "Nonscrolling Background", //MISSING
+DlgDocCText : "Text", //MISSING
+DlgDocCLink : "Link", //MISSING
+DlgDocCVisited : "Visited Link", //MISSING
+DlgDocCActive : "Active Link", //MISSING
+DlgDocMargins : "Page Margins", //MISSING
+DlgDocMaTop : "Top", //MISSING
+DlgDocMaLeft : "Left", //MISSING
+DlgDocMaRight : "Right", //MISSING
+DlgDocMaBottom : "Bottom", //MISSING
+DlgDocMeIndex : "Document Indexing Keywords (comma separated)", //MISSING
+DlgDocMeDescr : "Document Description", //MISSING
+DlgDocMeAuthor : "Author", //MISSING
+DlgDocMeCopy : "Copyright", //MISSING
+DlgDocPreview : "Preview", //MISSING
+
+// Templates Dialog
+Templates : "Templates", //MISSING
+DlgTemplatesTitle : "Content Templates", //MISSING
+DlgTemplatesSelMsg : "Please select the template to open in the editor<br />(the actual contents will be lost):", //MISSING
+DlgTemplatesLoading : "Loading templates list. Please wait...", //MISSING
+DlgTemplatesNoTpl : "(No templates defined)", //MISSING
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "About", //MISSING
+DlgAboutBrowserInfoTab : "Browser Info", //MISSING
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "verzija",
+DlgAboutInfo : "Za više informacija posjetite"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/ca.js b/httemplate/elements/fckeditor/editor/lang/ca.js
new file mode 100644
index 0000000..c3859cd
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/ca.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Catalan language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Col·lapsa la barra",
+ToolbarExpand : "Amplia la barra",
+
+// Toolbar Items and Context Menu
+Save : "Desa",
+NewPage : "Nova Pàgina",
+Preview : "Vista Prèvia",
+Cut : "Retalla",
+Copy : "Copia",
+Paste : "Enganxa",
+PasteText : "Enganxa com a text no formatat",
+PasteWord : "Enganxa des del Word",
+Print : "Imprimeix",
+SelectAll : "Selecciona-ho tot",
+RemoveFormat : "Elimina Format",
+InsertLinkLbl : "Enllaç",
+InsertLink : "Insereix/Edita enllaç",
+RemoveLink : "Elimina enllaç",
+Anchor : "Insereix/Edita àncora",
+InsertImageLbl : "Imatge",
+InsertImage : "Insereix/Edita imatge",
+InsertFlashLbl : "Flash",
+InsertFlash : "Insereix/Edita Flash",
+InsertTableLbl : "Taula",
+InsertTable : "Insereix/Edita taula",
+InsertLineLbl : "Línia",
+InsertLine : "Insereix línia horitzontal",
+InsertSpecialCharLbl: "Caràcter Especial",
+InsertSpecialChar : "Insereix caràcter especial",
+InsertSmileyLbl : "Icona",
+InsertSmiley : "Insereix icona",
+About : "Quant a FCKeditor",
+Bold : "Negreta",
+Italic : "Cursiva",
+Underline : "Subratllat",
+StrikeThrough : "Barrat",
+Subscript : "Subíndex",
+Superscript : "Superíndex",
+LeftJustify : "Aliniament esquerra",
+CenterJustify : "Aliniament centrat",
+RightJustify : "Aliniament dreta",
+BlockJustify : "Justifica",
+DecreaseIndent : "Sagna el text",
+IncreaseIndent : "Treu el sagnat del text",
+Undo : "Desfés",
+Redo : "Refés",
+NumberedListLbl : "Llista numerada",
+NumberedList : "Aplica o elimina la llista numerada",
+BulletedListLbl : "Llista de pics",
+BulletedList : "Aplica o elimina la llista de pics",
+ShowTableBorders : "Mostra les vores de les taules",
+ShowDetails : "Mostra detalls",
+Style : "Estil",
+FontFormat : "Format",
+Font : "Tipus de lletra",
+FontSize : "Mida",
+TextColor : "Color de Text",
+BGColor : "Color de Fons",
+Source : "Codi font",
+Find : "Cerca",
+Replace : "Reemplaça",
+SpellCheck : "Revisa l'ortografia",
+UniversalKeyboard : "Teclat universal",
+PageBreakLbl : "Salt de pàgina",
+PageBreak : "Insereix salt de pàgina",
+
+Form : "Formulari",
+Checkbox : "Casella de verificació",
+RadioButton : "Botó d'opció",
+TextField : "Camp de text",
+Textarea : "Àrea de text",
+HiddenField : "Camp ocult",
+Button : "Botó",
+SelectionField : "Camp de selecció",
+ImageButton : "Botó d'imatge",
+
+FitWindow : "Maximiza la mida de l'editor",
+
+// Context Menu
+EditLink : "Edita l'enllaç",
+CellCM : "Cel·la",
+RowCM : "Fila",
+ColumnCM : "Columna",
+InsertRow : "Insereix una fila",
+DeleteRows : "Suprimeix una fila",
+InsertColumn : "Afegeix una columna",
+DeleteColumns : "Suprimeix una columna",
+InsertCell : "Insereix una cel·la",
+DeleteCells : "Suprimeix les cel·les",
+MergeCells : "Fusiona les cel·les",
+SplitCell : "Separa les cel·les",
+TableDelete : "Suprimeix la taula",
+CellProperties : "Propietats de la cel·la",
+TableProperties : "Propietats de la taula",
+ImageProperties : "Propietats de la imatge",
+FlashProperties : "Propietats del Flash",
+
+AnchorProp : "Propietats de l'àncora",
+ButtonProp : "Propietats del botó",
+CheckboxProp : "Propietats de la casella de verificació",
+HiddenFieldProp : "Propietats del camp ocult",
+RadioButtonProp : "Propietats del botó d'opció",
+ImageButtonProp : "Propietats del botó d'imatge",
+TextFieldProp : "Propietats del camp de text",
+SelectionFieldProp : "Propietats del camp de selecció",
+TextareaProp : "Propietats de l'àrea de text",
+FormProp : "Propietats del formulari",
+
+FontFormats : "Normal;Formatejat;Adreça;Encapçalament 1;Encapçalament 2;Encapçalament 3;Encapçalament 4;Encapçalament 5;Encapçalament 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Processant XHTML. Si us plau esperi...",
+Done : "Fet",
+PasteWordConfirm : "El text que voleu enganxar sembla provenir de Word. Voleu netejar aquest text abans que sigui enganxat?",
+NotCompatiblePaste : "Aquesta funció és disponible per a Internet Explorer versió 5.5 o superior. Voleu enganxar sense netejar?",
+UnknownToolbarItem : "Element de la barra d'eines desconegut \"%1\"",
+UnknownCommand : "Nom de comanda desconegut \"%1\"",
+NotImplemented : "Mètode no implementat",
+UnknownToolbarSet : "Conjunt de barra d'eines \"%1\" inexistent",
+NoActiveX : "Les preferències del navegador poden limitar algunes funcions d'aquest editor. Cal habilitar l'opció \"Executa controls ActiveX i plug-ins\". Poden sorgir errors i poden faltar algunes funcions.",
+BrowseServerBlocked : "El visualitzador de recursos no s'ha pogut obrir. Assegura't de que els bloquejos de finestres emergents estan desactivats.",
+DialogBlocked : "No ha estat possible obrir una finestra de diàleg. Assegura't de que els bloquejos de finestres emergents estan desactivats.",
+
+// Dialogs
+DlgBtnOK : "D'acord",
+DlgBtnCancel : "Cancel·la",
+DlgBtnClose : "Tanca",
+DlgBtnBrowseServer : "Veure servidor",
+DlgAdvancedTag : "Avançat",
+DlgOpOther : "Altres",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Si us plau, afegiu la URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<no definit>",
+DlgGenId : "Id",
+DlgGenLangDir : "Direcció de l'idioma",
+DlgGenLangDirLtr : "D'esquerra a dreta (LTR)",
+DlgGenLangDirRtl : "De dreta a esquerra (RTL)",
+DlgGenLangCode : "Codi d'idioma",
+DlgGenAccessKey : "Clau d'accés",
+DlgGenName : "Nom",
+DlgGenTabIndex : "Index de Tab",
+DlgGenLongDescr : "Descripció llarga de la URL",
+DlgGenClass : "Classes del full d'estil",
+DlgGenTitle : "Títol consultiu",
+DlgGenContType : "Tipus de contingut consultiu",
+DlgGenLinkCharset : "Conjunt de caràcters font enllaçat",
+DlgGenStyle : "Estil",
+
+// Image Dialog
+DlgImgTitle : "Propietats de la imatge",
+DlgImgInfoTab : "Informació de la imatge",
+DlgImgBtnUpload : "Envia-la al servidor",
+DlgImgURL : "URL",
+DlgImgUpload : "Puja",
+DlgImgAlt : "Text alternatiu",
+DlgImgWidth : "Amplada",
+DlgImgHeight : "Alçada",
+DlgImgLockRatio : "Bloqueja les proporcions",
+DlgBtnResetSize : "Restaura la mida",
+DlgImgBorder : "Vora",
+DlgImgHSpace : "Espaiat horit.",
+DlgImgVSpace : "Espaiat vert.",
+DlgImgAlign : "Alineació",
+DlgImgAlignLeft : "Ajusta a l'esquerra",
+DlgImgAlignAbsBottom: "Abs Bottom",
+DlgImgAlignAbsMiddle: "Abs Middle",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Bottom",
+DlgImgAlignMiddle : "Middle",
+DlgImgAlignRight : "Ajusta a la dreta",
+DlgImgAlignTextTop : "Text Top",
+DlgImgAlignTop : "Top",
+DlgImgPreview : "Vista prèvia",
+DlgImgAlertUrl : "Si us plau, escriviu la URL de la imatge",
+DlgImgLinkTab : "Enllaç",
+
+// Flash Dialog
+DlgFlashTitle : "Propietats del Flash",
+DlgFlashChkPlay : "Reprodució automàtica",
+DlgFlashChkLoop : "Bucle",
+DlgFlashChkMenu : "Habilita menú Flash",
+DlgFlashScale : "Escala",
+DlgFlashScaleAll : "Mostra-ho tot",
+DlgFlashScaleNoBorder : "Sense vores",
+DlgFlashScaleFit : "Mida exacta",
+
+// Link Dialog
+DlgLnkWindowTitle : "Enllaç",
+DlgLnkInfoTab : "Informació de l'enllaç",
+DlgLnkTargetTab : "Destí",
+
+DlgLnkType : "Tipus d'enllaç",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Àncora en aquesta pàgina",
+DlgLnkTypeEMail : "Correu electrònic",
+DlgLnkProto : "Protocol",
+DlgLnkProtoOther : "<altra>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Selecciona una àncora",
+DlgLnkAnchorByName : "Per nom d'àncora",
+DlgLnkAnchorById : "Per Id d'element",
+DlgLnkNoAnchors : "(No hi ha àncores disponibles en aquest document)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Adreça de correu electrònic",
+DlgLnkEMailSubject : "Assumpte del missatge",
+DlgLnkEMailBody : "Cos del missatge",
+DlgLnkUpload : "Puja",
+DlgLnkBtnUpload : "Envia al servidor",
+
+DlgLnkTarget : "Destí",
+DlgLnkTargetFrame : "<marc>",
+DlgLnkTargetPopup : "<finestra emergent>",
+DlgLnkTargetBlank : "Nova finestra (_blank)",
+DlgLnkTargetParent : "Finestra pare (_parent)",
+DlgLnkTargetSelf : "Mateixa finestra (_self)",
+DlgLnkTargetTop : "Finestra Major (_top)",
+DlgLnkTargetFrameName : "Nom del marc de destí",
+DlgLnkPopWinName : "Nom finestra popup",
+DlgLnkPopWinFeat : "Característiques finestra popup",
+DlgLnkPopResize : "Redimensionable",
+DlgLnkPopLocation : "Barra d'adreça",
+DlgLnkPopMenu : "Barra de menú",
+DlgLnkPopScroll : "Barres d'scroll",
+DlgLnkPopStatus : "Barra d'estat",
+DlgLnkPopToolbar : "Barra d'eines",
+DlgLnkPopFullScrn : "Pantalla completa (IE)",
+DlgLnkPopDependent : "Depenent (Netscape)",
+DlgLnkPopWidth : "Amplada",
+DlgLnkPopHeight : "Alçada",
+DlgLnkPopLeft : "Posició esquerra",
+DlgLnkPopTop : "Posició dalt",
+
+DlnLnkMsgNoUrl : "Si us plau, escrigui l'enllaç URL",
+DlnLnkMsgNoEMail : "Si us plau, escrigui l'adreça correu electrònic",
+DlnLnkMsgNoAnchor : "Si us plau, escrigui l'àncora",
+DlnLnkMsgInvPopName : "El nom de la finestra emergent ha de començar amb una lletra i no pot tenir espais",
+
+// Color Dialog
+DlgColorTitle : "Selecciona el color",
+DlgColorBtnClear : "Neteja",
+DlgColorHighlight : "Realça",
+DlgColorSelected : "Selecciona",
+
+// Smiley Dialog
+DlgSmileyTitle : "Insereix una icona",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Selecciona el caràcter especial",
+
+// Table Dialog
+DlgTableTitle : "Propietats de la taula",
+DlgTableRows : "Files",
+DlgTableColumns : "Columnes",
+DlgTableBorder : "Mida vora",
+DlgTableAlign : "Alineació",
+DlgTableAlignNotSet : "<No Definit>",
+DlgTableAlignLeft : "Esquerra",
+DlgTableAlignCenter : "Centre",
+DlgTableAlignRight : "Dreta",
+DlgTableWidth : "Amplada",
+DlgTableWidthPx : "píxels",
+DlgTableWidthPc : "percentatge",
+DlgTableHeight : "Alçada",
+DlgTableCellSpace : "Espaiat de cel·les",
+DlgTableCellPad : "Encoixinament de cel·les",
+DlgTableCaption : "Títol",
+DlgTableSummary : "Resum",
+
+// Table Cell Dialog
+DlgCellTitle : "Propietats de la cel·la",
+DlgCellWidth : "Amplada",
+DlgCellWidthPx : "píxels",
+DlgCellWidthPc : "percentatge",
+DlgCellHeight : "Alçada",
+DlgCellWordWrap : "Ajust de paraula",
+DlgCellWordWrapNotSet : "<No Definit>",
+DlgCellWordWrapYes : "Si",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "Alineació horitzontal",
+DlgCellHorAlignNotSet : "<No Definit>",
+DlgCellHorAlignLeft : "Esquerra",
+DlgCellHorAlignCenter : "Centre",
+DlgCellHorAlignRight: "Dreta",
+DlgCellVerAlign : "Alineació vertical",
+DlgCellVerAlignNotSet : "<No definit>",
+DlgCellVerAlignTop : "Top",
+DlgCellVerAlignMiddle : "Middle",
+DlgCellVerAlignBottom : "Bottom",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Rows Span",
+DlgCellCollSpan : "Columns Span",
+DlgCellBackColor : "Color de fons",
+DlgCellBorderColor : "Color de la vora",
+DlgCellBtnSelect : "Seleccioneu...",
+
+// Find Dialog
+DlgFindTitle : "Cerca",
+DlgFindFindBtn : "Cerca",
+DlgFindNotFoundMsg : "El text especificat no s'ha trobat.",
+
+// Replace Dialog
+DlgReplaceTitle : "Reemplaça",
+DlgReplaceFindLbl : "Cerca:",
+DlgReplaceReplaceLbl : "Remplaça amb:",
+DlgReplaceCaseChk : "Distingeix majúscules/minúscules",
+DlgReplaceReplaceBtn : "Reemplaça",
+DlgReplaceReplAllBtn : "Reemplaça-ho tot",
+DlgReplaceWordChk : "Només paraules completes",
+
+// Paste Operations / Dialog
+PasteErrorCut : "La seguretat del vostre navegador no permet executar automàticament les operacions de retallar. Si us plau, utilitzeu el teclat (Ctrl+X).",
+PasteErrorCopy : "La seguretat del vostre navegador no permet executar automàticament les operacions de copiar. Si us plau, utilitzeu el teclat (Ctrl+C).",
+
+PasteAsText : "Enganxa com a text no formatat",
+PasteFromWord : "Enganxa com a Word",
+
+DlgPasteMsg2 : "Si us plau, enganxeu dins del següent camp utilitzant el teclat (<STRONG>Ctrl+V</STRONG>) i premeu <STRONG>OK</STRONG>.",
+DlgPasteSec : "A causa de la configuració de seguretat del vostre navegador, l'editor no pot accedir al porta-retalls directament. Enganxeu-ho un altre cop en aquesta finestra.",
+DlgPasteIgnoreFont : "Ignora definicions de font",
+DlgPasteRemoveStyles : "Elimina definicions d'estil",
+DlgPasteCleanBox : "Neteja camp",
+
+// Color Picker
+ColorAutomatic : "Automàtic",
+ColorMoreColors : "Més colors...",
+
+// Document Properties
+DocProps : "Propietats del document",
+
+// Anchor Dialog
+DlgAnchorTitle : "Propietats de l'àncora",
+DlgAnchorName : "Nom de l'àncora",
+DlgAnchorErrorName : "Si us plau, escriviu el nom de l'ancora",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "No és al diccionari",
+DlgSpellChangeTo : "Canvia a",
+DlgSpellBtnIgnore : "Ignora",
+DlgSpellBtnIgnoreAll : "Ignora-les totes",
+DlgSpellBtnReplace : "Canvia",
+DlgSpellBtnReplaceAll : "Canvia-les totes",
+DlgSpellBtnUndo : "Desfés",
+DlgSpellNoSuggestions : "Cap sugerència",
+DlgSpellProgress : "Comprovació ortogràfica en progrés",
+DlgSpellNoMispell : "Comprovació ortogràfica completada",
+DlgSpellNoChanges : "Comprovació ortogràfica: cap paraulada canviada",
+DlgSpellOneChange : "Comprovació ortogràfica: una paraula canviada",
+DlgSpellManyChanges : "Comprovació ortogràfica %1 paraules canviades",
+
+IeSpellDownload : "Comprovació ortogràfica no instal·lada. Voleu descarregar-ho ara?",
+
+// Button Dialog
+DlgButtonText : "Text (Valor)",
+DlgButtonType : "Tipus",
+DlgButtonTypeBtn : "Botó",
+DlgButtonTypeSbm : "Transmet formulari",
+DlgButtonTypeRst : "Reinicia formulari",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nom",
+DlgCheckboxValue : "Valor",
+DlgCheckboxSelected : "Seleccionat",
+
+// Form Dialog
+DlgFormName : "Nom",
+DlgFormAction : "Acció",
+DlgFormMethod : "Mètode",
+
+// Select Field Dialog
+DlgSelectName : "Nom",
+DlgSelectValue : "Valor",
+DlgSelectSize : "Mida",
+DlgSelectLines : "Línies",
+DlgSelectChkMulti : "Permet múltiples seleccions",
+DlgSelectOpAvail : "Opcions disponibles",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Valor",
+DlgSelectBtnAdd : "Afegeix",
+DlgSelectBtnModify : "Modifica",
+DlgSelectBtnUp : "Amunt",
+DlgSelectBtnDown : "Avall",
+DlgSelectBtnSetValue : "Selecciona per defecte",
+DlgSelectBtnDelete : "Elimina",
+
+// Textarea Dialog
+DlgTextareaName : "Nom",
+DlgTextareaCols : "Columnes",
+DlgTextareaRows : "Files",
+
+// Text Field Dialog
+DlgTextName : "Nom",
+DlgTextValue : "Valor",
+DlgTextCharWidth : "Amplada de caràcter",
+DlgTextMaxChars : "Màxim de caràcters",
+DlgTextType : "Tipus",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Contrasenya",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nom",
+DlgHiddenValue : "Valor",
+
+// Bulleted List Dialog
+BulletedListProp : "Propietats de la llista de pics",
+NumberedListProp : "Propietats de llista numerada",
+DlgLstStart : "Inici",
+DlgLstType : "Tipus",
+DlgLstTypeCircle : "Cercle",
+DlgLstTypeDisc : "Disc",
+DlgLstTypeSquare : "Quadrat",
+DlgLstTypeNumbers : "Números (1, 2, 3)",
+DlgLstTypeLCase : "Lletres minúscules (a, b, c)",
+DlgLstTypeUCase : "Lletres majúscules (A, B, C)",
+DlgLstTypeSRoman : "Números romans minúscules (i, ii, iii)",
+DlgLstTypeLRoman : "Números romans majúscules (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General",
+DlgDocBackTab : "Fons",
+DlgDocColorsTab : "Colors i marges",
+DlgDocMetaTab : "Dades Meta",
+
+DlgDocPageTitle : "Títol de la pàgina",
+DlgDocLangDir : "Direcció idioma",
+DlgDocLangDirLTR : "Esquerra a dreta (LTR)",
+DlgDocLangDirRTL : "Dreta a esquerra (RTL)",
+DlgDocLangCode : "Codi d'idioma",
+DlgDocCharSet : "Codificació de conjunt de caràcters",
+DlgDocCharSetCE : "Centreeuropeu",
+DlgDocCharSetCT : "Xinès tradicional (Big5)",
+DlgDocCharSetCR : "Ciríl·lic",
+DlgDocCharSetGR : "Grec",
+DlgDocCharSetJP : "Japonès",
+DlgDocCharSetKR : "Coreà",
+DlgDocCharSetTR : "Turc",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Europeu occidental",
+DlgDocCharSetOther : "Una altra codificació de caràcters",
+
+DlgDocDocType : "Capçalera de tipus de document",
+DlgDocDocTypeOther : "Un altra capçalera de tipus de document",
+DlgDocIncXHTML : "Incloure declaracions XHTML",
+DlgDocBgColor : "Color de fons",
+DlgDocBgImage : "URL de la imatge de fons",
+DlgDocBgNoScroll : "Fons fixe",
+DlgDocCText : "Text",
+DlgDocCLink : "Enllaç",
+DlgDocCVisited : "Enllaç visitat",
+DlgDocCActive : "Enllaç actiu",
+DlgDocMargins : "Marges de pàgina",
+DlgDocMaTop : "Cap",
+DlgDocMaLeft : "Esquerra",
+DlgDocMaRight : "Dreta",
+DlgDocMaBottom : "Peu",
+DlgDocMeIndex : "Mots clau per a indexació (separats per coma)",
+DlgDocMeDescr : "Descripció del document",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Vista prèvia",
+
+// Templates Dialog
+Templates : "Plantilles",
+DlgTemplatesTitle : "Contingut plantilles",
+DlgTemplatesSelMsg : "Si us plau, seleccioneu la plantilla per obrir en l'editor<br>(el contingut actual no serà enregistrat):",
+DlgTemplatesLoading : "Carregant la llista de plantilles. Si us plau, espereu...",
+DlgTemplatesNoTpl : "(No hi ha plantilles definides)",
+DlgTemplatesReplace : "Reemplaça el contingut actual",
+
+// About Dialog
+DlgAboutAboutTab : "Quant a",
+DlgAboutBrowserInfoTab : "Informació del navegador",
+DlgAboutLicenseTab : "Llicència",
+DlgAboutVersion : "versió",
+DlgAboutInfo : "Per a més informació aneu a"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/cs.js b/httemplate/elements/fckeditor/editor/lang/cs.js
new file mode 100644
index 0000000..49b5f87
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/cs.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Czech language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Skrýt panel nástrojů",
+ToolbarExpand : "Zobrazit panel nástrojů",
+
+// Toolbar Items and Context Menu
+Save : "Uložit",
+NewPage : "Nová stránka",
+Preview : "Náhled",
+Cut : "Vyjmout",
+Copy : "Kopírovat",
+Paste : "Vložit",
+PasteText : "Vložit jako Äistý text",
+PasteWord : "Vložit z Wordu",
+Print : "Tisk",
+SelectAll : "Vybrat vše",
+RemoveFormat : "Odstranit formátování",
+InsertLinkLbl : "Odkaz",
+InsertLink : "Vložit/změnit odkaz",
+RemoveLink : "Odstranit odkaz",
+Anchor : "Vložít/změnit záložku",
+InsertImageLbl : "Obrázek",
+InsertImage : "Vložit/změnit obrázek",
+InsertFlashLbl : "Flash",
+InsertFlash : "Vložit/Upravit Flash",
+InsertTableLbl : "Tabulka",
+InsertTable : "Vložit/změnit tabulku",
+InsertLineLbl : "Linka",
+InsertLine : "Vložit vodorovnou linku",
+InsertSpecialCharLbl: "Speciální znaky",
+InsertSpecialChar : "Vložit speciální znaky",
+InsertSmileyLbl : "Smajlíky",
+InsertSmiley : "Vložit smajlík",
+About : "O aplikaci FCKeditor",
+Bold : "TuÄné",
+Italic : "Kurzíva",
+Underline : "Podtržené",
+StrikeThrough : "Přeškrtnuté",
+Subscript : "Dolní index",
+Superscript : "Horní index",
+LeftJustify : "Zarovnat vlevo",
+CenterJustify : "Zarovnat na střed",
+RightJustify : "Zarovnat vpravo",
+BlockJustify : "Zarovnat do bloku",
+DecreaseIndent : "Zmenšit odsazení",
+IncreaseIndent : "Zvětšit odsazení",
+Undo : "Zpět",
+Redo : "Znovu",
+NumberedListLbl : "Číslování",
+NumberedList : "Vložit/odstranit Äíslovaný seznam",
+BulletedListLbl : "Odrážky",
+BulletedList : "Vložit/odstranit odrážky",
+ShowTableBorders : "Zobrazit okraje tabulek",
+ShowDetails : "Zobrazit podrobnosti",
+Style : "Styl",
+FontFormat : "Formát",
+Font : "Písmo",
+FontSize : "Velikost",
+TextColor : "Barva textu",
+BGColor : "Barva pozadí",
+Source : "Zdroj",
+Find : "Hledat",
+Replace : "Nahradit",
+SpellCheck : "Zkontrolovat pravopis",
+UniversalKeyboard : "Univerzální klávesnice",
+PageBreakLbl : "Konec stránky",
+PageBreak : "Vložit konec stránky",
+
+Form : "Formulář",
+Checkbox : "ZaÅ¡krtávací políÄko",
+RadioButton : "PÅ™epínaÄ",
+TextField : "Textové pole",
+Textarea : "Textová oblast",
+HiddenField : "Skryté pole",
+Button : "TlaÄítko",
+SelectionField : "Seznam",
+ImageButton : "Obrázkové tlaÄítko",
+
+FitWindow : "Maximalizovat velikost editoru",
+
+// Context Menu
+EditLink : "Změnit odkaz",
+CellCM : "Buňka",
+RowCM : "Řádek",
+ColumnCM : "Sloupec",
+InsertRow : "Vložit řádek",
+DeleteRows : "Smazat řádek",
+InsertColumn : "Vložit sloupec",
+DeleteColumns : "Smazat sloupec",
+InsertCell : "Vložit buňku",
+DeleteCells : "Smazat buňky",
+MergeCells : "SlouÄit buňky",
+SplitCell : "Rozdělit buňku",
+TableDelete : "Smazat tabulku",
+CellProperties : "Vlastnosti buňky",
+TableProperties : "Vlastnosti tabulky",
+ImageProperties : "Vlastnosti obrázku",
+FlashProperties : "Vlastnosti Flashe",
+
+AnchorProp : "Vlastnosti záložky",
+ButtonProp : "Vlastnosti tlaÄítka",
+CheckboxProp : "Vlastnosti zaÅ¡krtávacího políÄka",
+HiddenFieldProp : "Vlastnosti skrytého pole",
+RadioButtonProp : "Vlastnosti pÅ™epínaÄe",
+ImageButtonProp : "Vlastností obrázkového tlaÄítka",
+TextFieldProp : "Vlastnosti textového pole",
+SelectionFieldProp : "Vlastnosti seznamu",
+TextareaProp : "Vlastnosti textové oblasti",
+FormProp : "Vlastnosti formuláře",
+
+FontFormats : "Normální;Formátovaný;Adresa;Nadpis 1;Nadpis 2;Nadpis 3;Nadpis 4;Nadpis 5;Nadpis 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Probíhá zpracování XHTML. Prosím Äekejte...",
+Done : "Hotovo",
+PasteWordConfirm : "Jak je vidÄ›t, vkládaný text je kopírován z Wordu. Chcete jej pÅ™ed vložením vyÄistit?",
+NotCompatiblePaste : "Tento příkaz je dostupný pouze v Internet Exploreru verze 5.5 nebo vyšší. Chcete vložit text bez vyÄiÅ¡tÄ›ní?",
+UnknownToolbarItem : "Neznámá položka panelu nástrojů \"%1\"",
+UnknownCommand : "Neznámý příkaz \"%1\"",
+NotImplemented : "Příkaz není implementován",
+UnknownToolbarSet : "Panel nástrojů \"%1\" neexistuje",
+NoActiveX : "Nastavení bezpeÄnosti VaÅ¡eho prohlížeÄe omezuje funkÄnost nÄ›kterých jeho možností. Je tÅ™eba zapnout volbu \"SpouÅ¡tÄ›t ovládáací prvky ActiveX a moduly plug-in\", jinak nebude možné využívat vÅ¡echny dosputné schopnosti editoru.",
+BrowseServerBlocked : "Průzkumník zdrojů nelze otevřít. Prověřte, zda nemáte aktivováno blokování popup oken.",
+DialogBlocked : "Nelze otevřít dialogové okno. Prověřte, zda nemáte aktivováno blokování popup oken.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Storno",
+DlgBtnClose : "Zavřít",
+DlgBtnBrowseServer : "Vybrat na serveru",
+DlgAdvancedTag : "Rozšířené",
+DlgOpOther : "<Ostatní>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Prosím vložte URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nenastaveno>",
+DlgGenId : "Id",
+DlgGenLangDir : "Orientace jazyka",
+DlgGenLangDirLtr : "Zleva do prava (LTR)",
+DlgGenLangDirRtl : "Zprava do leva (RTL)",
+DlgGenLangCode : "Kód jazyka",
+DlgGenAccessKey : "Přístupový klíÄ",
+DlgGenName : "Jméno",
+DlgGenTabIndex : "Pořadí prvku",
+DlgGenLongDescr : "Dlouhý popis URL",
+DlgGenClass : "Třída stylu",
+DlgGenTitle : "Pomocný titulek",
+DlgGenContType : "Pomocný typ obsahu",
+DlgGenLinkCharset : "Přiřazená znaková sada",
+DlgGenStyle : "Styl",
+
+// Image Dialog
+DlgImgTitle : "Vlastnosti obrázku",
+DlgImgInfoTab : "Informace o obrázku",
+DlgImgBtnUpload : "Odeslat na server",
+DlgImgURL : "URL",
+DlgImgUpload : "Odeslat",
+DlgImgAlt : "Alternativní text",
+DlgImgWidth : "Šířka",
+DlgImgHeight : "Výška",
+DlgImgLockRatio : "Zámek",
+DlgBtnResetSize : "Původní velikost",
+DlgImgBorder : "Okraje",
+DlgImgHSpace : "H-mezera",
+DlgImgVSpace : "V-mezera",
+DlgImgAlign : "Zarovnání",
+DlgImgAlignLeft : "Vlevo",
+DlgImgAlignAbsBottom: "Zcela dolů",
+DlgImgAlignAbsMiddle: "Doprostřed",
+DlgImgAlignBaseline : "Na úÄaří",
+DlgImgAlignBottom : "Dolů",
+DlgImgAlignMiddle : "Na střed",
+DlgImgAlignRight : "Vpravo",
+DlgImgAlignTextTop : "Na horní okraj textu",
+DlgImgAlignTop : "Nahoru",
+DlgImgPreview : "Náhled",
+DlgImgAlertUrl : "Zadejte prosím URL obrázku",
+DlgImgLinkTab : "Odkaz",
+
+// Flash Dialog
+DlgFlashTitle : "Vlastnosti Flashe",
+DlgFlashChkPlay : "Automatické spuštění",
+DlgFlashChkLoop : "Opakování",
+DlgFlashChkMenu : "Nabídka Flash",
+DlgFlashScale : "Zobrazit",
+DlgFlashScaleAll : "Zobrazit vše",
+DlgFlashScaleNoBorder : "Bez okraje",
+DlgFlashScaleFit : "Přizpůsobit",
+
+// Link Dialog
+DlgLnkWindowTitle : "Odkaz",
+DlgLnkInfoTab : "Informace o odkazu",
+DlgLnkTargetTab : "Cíl",
+
+DlgLnkType : "Typ odkazu",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Kotva v této stránce",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<jiný>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Vybrat kotvu",
+DlgLnkAnchorByName : "Podle jména kotvy",
+DlgLnkAnchorById : "Podle Id objektu",
+DlgLnkNoAnchors : "<Ve stránce žádná kotva není definována>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mailová adresa",
+DlgLnkEMailSubject : "Předmět zprávy",
+DlgLnkEMailBody : "Tělo zprávy",
+DlgLnkUpload : "Odeslat",
+DlgLnkBtnUpload : "Odeslat na Server",
+
+DlgLnkTarget : "Cíl",
+DlgLnkTargetFrame : "<rámec>",
+DlgLnkTargetPopup : "<vyskakovací okno>",
+DlgLnkTargetBlank : "Nové okno (_blank)",
+DlgLnkTargetParent : "RodiÄovské okno (_parent)",
+DlgLnkTargetSelf : "Stejné okno (_self)",
+DlgLnkTargetTop : "Hlavní okno (_top)",
+DlgLnkTargetFrameName : "Název cílového rámu",
+DlgLnkPopWinName : "Název vyskakovacího okna",
+DlgLnkPopWinFeat : "Vlastnosti vyskakovacího okna",
+DlgLnkPopResize : "Měnitelná velikost",
+DlgLnkPopLocation : "Panel umístění",
+DlgLnkPopMenu : "Panel nabídky",
+DlgLnkPopScroll : "Posuvníky",
+DlgLnkPopStatus : "Stavový řádek",
+DlgLnkPopToolbar : "Panel nástrojů",
+DlgLnkPopFullScrn : "Celá obrazovka (IE)",
+DlgLnkPopDependent : "Závislost (Netscape)",
+DlgLnkPopWidth : "Šířka",
+DlgLnkPopHeight : "Výška",
+DlgLnkPopLeft : "Levý okraj",
+DlgLnkPopTop : "Horní okraj",
+
+DlnLnkMsgNoUrl : "Zadejte prosím URL odkazu",
+DlnLnkMsgNoEMail : "Zadejte prosím e-mailovou adresu",
+DlnLnkMsgNoAnchor : "Vyberte prosím kotvu",
+DlnLnkMsgInvPopName : "Název vyskakovacího okna musí zaÄínat písmenem a nesmí obsahovat mezery",
+
+// Color Dialog
+DlgColorTitle : "Výběr barvy",
+DlgColorBtnClear : "Vymazat",
+DlgColorHighlight : "Zvýrazněná",
+DlgColorSelected : "Vybraná",
+
+// Smiley Dialog
+DlgSmileyTitle : "Vkládání smajlíků",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Výběr speciálního znaku",
+
+// Table Dialog
+DlgTableTitle : "Vlastnosti tabulky",
+DlgTableRows : "Řádky",
+DlgTableColumns : "Sloupce",
+DlgTableBorder : "OhraniÄení",
+DlgTableAlign : "Zarovnání",
+DlgTableAlignNotSet : "<nenastaveno>",
+DlgTableAlignLeft : "Vlevo",
+DlgTableAlignCenter : "Na střed",
+DlgTableAlignRight : "Vpravo",
+DlgTableWidth : "Šířka",
+DlgTableWidthPx : "bodů",
+DlgTableWidthPc : "procent",
+DlgTableHeight : "Výška",
+DlgTableCellSpace : "Vzdálenost buněk",
+DlgTableCellPad : "Odsazení obsahu",
+DlgTableCaption : "Popis",
+DlgTableSummary : "Souhrn",
+
+// Table Cell Dialog
+DlgCellTitle : "Vlastnosti buňky",
+DlgCellWidth : "Šířka",
+DlgCellWidthPx : "bodů",
+DlgCellWidthPc : "procent",
+DlgCellHeight : "Výška",
+DlgCellWordWrap : "Zalamování",
+DlgCellWordWrapNotSet : "<nenanstaveno>",
+DlgCellWordWrapYes : "Ano",
+DlgCellWordWrapNo : "Ne",
+DlgCellHorAlign : "Vodorovné zarovnání",
+DlgCellHorAlignNotSet : "<nenastaveno>",
+DlgCellHorAlignLeft : "Vlevo",
+DlgCellHorAlignCenter : "Na střed",
+DlgCellHorAlignRight: "Vpravo",
+DlgCellVerAlign : "Svislé zarovnání",
+DlgCellVerAlignNotSet : "<nenastaveno>",
+DlgCellVerAlignTop : "Nahoru",
+DlgCellVerAlignMiddle : "Doprostřed",
+DlgCellVerAlignBottom : "Dolů",
+DlgCellVerAlignBaseline : "Na úÄaří",
+DlgCellRowSpan : "SlouÄené řádky",
+DlgCellCollSpan : "SlouÄené sloupce",
+DlgCellBackColor : "Barva pozadí",
+DlgCellBorderColor : "Barva ohraniÄení",
+DlgCellBtnSelect : "Výběr...",
+
+// Find Dialog
+DlgFindTitle : "Hledat",
+DlgFindFindBtn : "Hledat",
+DlgFindNotFoundMsg : "Hledaný text nebyl nalezen.",
+
+// Replace Dialog
+DlgReplaceTitle : "Nahradit",
+DlgReplaceFindLbl : "Co hledat:",
+DlgReplaceReplaceLbl : "Čím nahradit:",
+DlgReplaceCaseChk : "Rozlišovat velikost písma",
+DlgReplaceReplaceBtn : "Nahradit",
+DlgReplaceReplAllBtn : "Nahradit vše",
+DlgReplaceWordChk : "Pouze celá slova",
+
+// Paste Operations / Dialog
+PasteErrorCut : "BezpeÄnostní nastavení VaÅ¡eho prohlížeÄe nedovolují editoru spustit funkci pro vyjmutí zvoleného textu do schránky. Prosím vyjmÄ›te zvolený text do schránky pomocí klávesnice (Ctrl+X).",
+PasteErrorCopy : "BezpeÄnostní nastavení VaÅ¡eho prohlížeÄe nedovolují editoru spustit funkci pro kopírování zvoleného textu do schránky. Prosím zkopírujte zvolený text do schránky pomocí klávesnice (Ctrl+C).",
+
+PasteAsText : "Vložit jako Äistý text",
+PasteFromWord : "Vložit text z Wordu",
+
+DlgPasteMsg2 : "Do následujícího pole vložte požadovaný obsah pomocí klávesnice (<STRONG>Ctrl+V</STRONG>) a stiskněte <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorovat písmo",
+DlgPasteRemoveStyles : "Odstranit styly",
+DlgPasteCleanBox : "VyÄistit",
+
+// Color Picker
+ColorAutomatic : "Automaticky",
+ColorMoreColors : "Více barev...",
+
+// Document Properties
+DocProps : "Vlastnosti dokumentu",
+
+// Anchor Dialog
+DlgAnchorTitle : "Vlastnosti záložky",
+DlgAnchorName : "Název záložky",
+DlgAnchorErrorName : "Zadejte prosím název záložky",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Není ve slovníku",
+DlgSpellChangeTo : "Změnit na",
+DlgSpellBtnIgnore : "PÅ™eskoÄit",
+DlgSpellBtnIgnoreAll : "Přeskakovat vše",
+DlgSpellBtnReplace : "Zaměnit",
+DlgSpellBtnReplaceAll : "Zaměňovat vše",
+DlgSpellBtnUndo : "Zpět",
+DlgSpellNoSuggestions : "- žádné návrhy -",
+DlgSpellProgress : "Probíhá kontrola pravopisu...",
+DlgSpellNoMispell : "Kontrola pravopisu dokonÄena: Žádné pravopisné chyby nenalezeny",
+DlgSpellNoChanges : "Kontrola pravopisu dokonÄena: Beze zmÄ›n",
+DlgSpellOneChange : "Kontrola pravopisu dokonÄena: Jedno slovo zmÄ›nÄ›no",
+DlgSpellManyChanges : "Kontrola pravopisu dokonÄena: %1 slov zmÄ›nÄ›no",
+
+IeSpellDownload : "Kontrola pravopisu není nainstalována. Chcete ji nyní stáhnout?",
+
+// Button Dialog
+DlgButtonText : "Popisek",
+DlgButtonType : "Typ",
+DlgButtonTypeBtn : "TlaÄítko",
+DlgButtonTypeSbm : "Odeslat",
+DlgButtonTypeRst : "Obnovit",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Název",
+DlgCheckboxValue : "Hodnota",
+DlgCheckboxSelected : "Zaškrtnuto",
+
+// Form Dialog
+DlgFormName : "Název",
+DlgFormAction : "Akce",
+DlgFormMethod : "Metoda",
+
+// Select Field Dialog
+DlgSelectName : "Název",
+DlgSelectValue : "Hodnota",
+DlgSelectSize : "Velikost",
+DlgSelectLines : "Řádků",
+DlgSelectChkMulti : "Povolit mnohonásobné výběry",
+DlgSelectOpAvail : "Dostupná nastavení",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Hodnota",
+DlgSelectBtnAdd : "Přidat",
+DlgSelectBtnModify : "Změnit",
+DlgSelectBtnUp : "Nahoru",
+DlgSelectBtnDown : "Dolů",
+DlgSelectBtnSetValue : "Nastavit jako vybranou hodnotu",
+DlgSelectBtnDelete : "Smazat",
+
+// Textarea Dialog
+DlgTextareaName : "Název",
+DlgTextareaCols : "Sloupců",
+DlgTextareaRows : "Řádků",
+
+// Text Field Dialog
+DlgTextName : "Název",
+DlgTextValue : "Hodnota",
+DlgTextCharWidth : "Šířka ve znacích",
+DlgTextMaxChars : "Maximální poÄet znaků",
+DlgTextType : "Typ",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Heslo",
+
+// Hidden Field Dialog
+DlgHiddenName : "Název",
+DlgHiddenValue : "Hodnota",
+
+// Bulleted List Dialog
+BulletedListProp : "Vlastnosti odrážek",
+NumberedListProp : "Vlastnosti Äíslovaného seznamu",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Typ",
+DlgLstTypeCircle : "Kružnice",
+DlgLstTypeDisc : "Kruh",
+DlgLstTypeSquare : "ÄŒtverec",
+DlgLstTypeNumbers : "Čísla (1, 2, 3)",
+DlgLstTypeLCase : "Malá písmena (a, b, c)",
+DlgLstTypeUCase : "Velká písmena (A, B, C)",
+DlgLstTypeSRoman : "Malé římská Äíslice (i, ii, iii)",
+DlgLstTypeLRoman : "Velké římské Äíslice (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Obecné",
+DlgDocBackTab : "Pozadí",
+DlgDocColorsTab : "Barvy a okraje",
+DlgDocMetaTab : "Metadata",
+
+DlgDocPageTitle : "Titulek stránky",
+DlgDocLangDir : "Směr jazyku",
+DlgDocLangDirLTR : "Zleva do prava ",
+DlgDocLangDirRTL : "Zprava doleva",
+DlgDocLangCode : "Kód jazyku",
+DlgDocCharSet : "Znaková sada",
+DlgDocCharSetCE : "Středoevropské jazyky",
+DlgDocCharSetCT : "TradiÄní ÄínÅ¡tina (Big5)",
+DlgDocCharSetCR : "Cyrilice",
+DlgDocCharSetGR : "ŘeÄtina",
+DlgDocCharSetJP : "Japonština",
+DlgDocCharSetKR : "Korejština",
+DlgDocCharSetTR : "TureÄtina",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Západoevropské jazyky",
+DlgDocCharSetOther : "Další znaková sada",
+
+DlgDocDocType : "Typ dokumentu",
+DlgDocDocTypeOther : "Jiný typ dokumetu",
+DlgDocIncXHTML : "Zahrnou deklarace XHTML",
+DlgDocBgColor : "Barva pozadí",
+DlgDocBgImage : "URL obrázku na pozadí",
+DlgDocBgNoScroll : "Nerolovatelné pozadí",
+DlgDocCText : "Text",
+DlgDocCLink : "Odkaz",
+DlgDocCVisited : "Navštívený odkaz",
+DlgDocCActive : "Vybraný odkaz",
+DlgDocMargins : "Okraje stránky",
+DlgDocMaTop : "Horní",
+DlgDocMaLeft : "Levý",
+DlgDocMaRight : "Pravý",
+DlgDocMaBottom : "Dolní",
+DlgDocMeIndex : "KlíÄová slova (oddÄ›lená Äárkou)",
+DlgDocMeDescr : "Popis dokumentu",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Autorská práva",
+DlgDocPreview : "Náhled",
+
+// Templates Dialog
+Templates : "Å ablony",
+DlgTemplatesTitle : "Å ablony obsahu",
+DlgTemplatesSelMsg : "Prosím zvolte šablonu pro otevření v editoru<br>(aktuální obsah editoru bude ztracen):",
+DlgTemplatesLoading : "Nahrávám pÅ™eheld Å¡ablon. Prosím Äekejte...",
+DlgTemplatesNoTpl : "(Není definována žádná šablona)",
+DlgTemplatesReplace : "Nahradit aktuální obsah",
+
+// About Dialog
+DlgAboutAboutTab : "O aplikaci",
+DlgAboutBrowserInfoTab : "Informace o prohlížeÄi",
+DlgAboutLicenseTab : "Licence",
+DlgAboutVersion : "verze",
+DlgAboutInfo : "Více informací získáte na"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/da.js b/httemplate/elements/fckeditor/editor/lang/da.js
new file mode 100644
index 0000000..8143241
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/da.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Danish language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Skjul værktøjslinier",
+ToolbarExpand : "Vis værktøjslinier",
+
+// Toolbar Items and Context Menu
+Save : "Gem",
+NewPage : "Ny side",
+Preview : "Vis eksempel",
+Cut : "Klip",
+Copy : "Kopier",
+Paste : "Indsæt",
+PasteText : "Indsæt som ikke-formateret tekst",
+PasteWord : "Indsæt fra Word",
+Print : "Udskriv",
+SelectAll : "Vælg alt",
+RemoveFormat : "Fjern formatering",
+InsertLinkLbl : "Hyperlink",
+InsertLink : "Indsæt/rediger hyperlink",
+RemoveLink : "Fjern hyperlink",
+Anchor : "Indsæt/rediger bogmærke",
+InsertImageLbl : "Indsæt billede",
+InsertImage : "Indsæt/rediger billede",
+InsertFlashLbl : "Flash",
+InsertFlash : "Indsæt/rediger Flash",
+InsertTableLbl : "Table",
+InsertTable : "Indsæt/rediger tabel",
+InsertLineLbl : "Linie",
+InsertLine : "Indsæt vandret linie",
+InsertSpecialCharLbl: "Symbol",
+InsertSpecialChar : "Indsæt symbol",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Indsæt smiley",
+About : "Om FCKeditor",
+Bold : "Fed",
+Italic : "Kursiv",
+Underline : "Understreget",
+StrikeThrough : "Overstreget",
+Subscript : "Sænket skrift",
+Superscript : "Hævet skrift",
+LeftJustify : "Venstrestillet",
+CenterJustify : "Centreret",
+RightJustify : "Højrestillet",
+BlockJustify : "Lige margener",
+DecreaseIndent : "Formindsk indrykning",
+IncreaseIndent : "Forøg indrykning",
+Undo : "Fortryd",
+Redo : "Annuller fortryd",
+NumberedListLbl : "Talopstilling",
+NumberedList : "Indsæt/fjern talopstilling",
+BulletedListLbl : "Punktopstilling",
+BulletedList : "Indsæt/fjern punktopstilling",
+ShowTableBorders : "Vis tabelkanter",
+ShowDetails : "Vis detaljer",
+Style : "Typografi",
+FontFormat : "Formatering",
+Font : "Skrifttype",
+FontSize : "Skriftstørrelse",
+TextColor : "Tekstfarve",
+BGColor : "Baggrundsfarve",
+Source : "Kilde",
+Find : "Søg",
+Replace : "Erstat",
+SpellCheck : "Stavekontrol",
+UniversalKeyboard : "Universaltastatur",
+PageBreakLbl : "Sidskift",
+PageBreak : "Indsæt sideskift",
+
+Form : "Indsæt formular",
+Checkbox : "Indsæt afkrydsningsfelt",
+RadioButton : "Indsæt alternativknap",
+TextField : "Indsæt tekstfelt",
+Textarea : "Indsæt tekstboks",
+HiddenField : "Indsæt skjult felt",
+Button : "Indsæt knap",
+SelectionField : "Indsæt liste",
+ImageButton : "Indsæt billedknap",
+
+FitWindow : "Maksimer editor vinduet",
+
+// Context Menu
+EditLink : "Rediger hyperlink",
+CellCM : "Celle",
+RowCM : "Række",
+ColumnCM : "Kolonne",
+InsertRow : "Indsæt række",
+DeleteRows : "Slet række",
+InsertColumn : "Indsæt kolonne",
+DeleteColumns : "Slet kolonne",
+InsertCell : "Indsæt celle",
+DeleteCells : "Slet celle",
+MergeCells : "Flet celler",
+SplitCell : "Opdel celle",
+TableDelete : "Slet tabel",
+CellProperties : "Egenskaber for celle",
+TableProperties : "Egenskaber for tabel",
+ImageProperties : "Egenskaber for billede",
+FlashProperties : "Egenskaber for Flash",
+
+AnchorProp : "Egenskaber for bogmærke",
+ButtonProp : "Egenskaber for knap",
+CheckboxProp : "Egenskaber for afkrydsningsfelt",
+HiddenFieldProp : "Egenskaber for skjult felt",
+RadioButtonProp : "Egenskaber for alternativknap",
+ImageButtonProp : "Egenskaber for billedknap",
+TextFieldProp : "Egenskaber for tekstfelt",
+SelectionFieldProp : "Egenskaber for liste",
+TextareaProp : "Egenskaber for tekstboks",
+FormProp : "Egenskaber for formular",
+
+FontFormats : "Normal;Formateret;Adresse;Overskrift 1;Overskrift 2;Overskrift 3;Overskrift 4;Overskrift 5;Overskrift 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Behandler XHTML...",
+Done : "Færdig",
+PasteWordConfirm : "Den tekst du forsøger at indsætte ser ud til at komme fra Word.<br>Vil du rense teksten før den indsættes?",
+NotCompatiblePaste : "Denne kommando er tilgændelig i Internet Explorer 5.5 eller senere.<br>Vil du indsætte teksten uden at rense den ?",
+UnknownToolbarItem : "Ukendt værktøjslinjeobjekt \"%1\"!",
+UnknownCommand : "Ukendt kommandonavn \"%1\"!",
+NotImplemented : "Kommandoen er ikke implementeret!",
+UnknownToolbarSet : "Værktøjslinjen \"%1\" eksisterer ikke!",
+NoActiveX : "Din browsers sikkerhedsindstillinger begrænser nogle af editorens muligheder.<br>Slå \"Kør ActiveX-objekter og plug-ins\" til, ellers vil du opleve fejl og manglende muligheder.",
+BrowseServerBlocked : "Browseren kunne ikke åbne de nødvendige ressourcer!<br>Slå pop-up blokering fra.",
+DialogBlocked : "Dialogvinduet kunne ikke åbnes!<br>Slå pop-up blokering fra.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Annuller",
+DlgBtnClose : "Luk",
+DlgBtnBrowseServer : "Gennemse...",
+DlgAdvancedTag : "Avanceret",
+DlgOpOther : "<Andet>",
+DlgInfoTab : "Generelt",
+DlgAlertUrl : "Indtast URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<intet valgt>",
+DlgGenId : "Id",
+DlgGenLangDir : "Tekstretning",
+DlgGenLangDirLtr : "Fra venstre mod højre (LTR)",
+DlgGenLangDirRtl : "Fra højre mod venstre (RTL)",
+DlgGenLangCode : "Sprogkode",
+DlgGenAccessKey : "Genvejstast",
+DlgGenName : "Navn",
+DlgGenTabIndex : "Tabulator indeks",
+DlgGenLongDescr : "Udvidet beskrivelse",
+DlgGenClass : "Typografiark",
+DlgGenTitle : "Titel",
+DlgGenContType : "Indholdstype",
+DlgGenLinkCharset : "Tegnsæt",
+DlgGenStyle : "Typografi",
+
+// Image Dialog
+DlgImgTitle : "Egenskaber for billede",
+DlgImgInfoTab : "Generelt",
+DlgImgBtnUpload : "Upload",
+DlgImgURL : "URL",
+DlgImgUpload : "Upload",
+DlgImgAlt : "Alternativ tekst",
+DlgImgWidth : "Bredde",
+DlgImgHeight : "Højde",
+DlgImgLockRatio : "Lås størrelsesforhold",
+DlgBtnResetSize : "Nulstil størrelse",
+DlgImgBorder : "Ramme",
+DlgImgHSpace : "HMargen",
+DlgImgVSpace : "VMargen",
+DlgImgAlign : "Justering",
+DlgImgAlignLeft : "Venstre",
+DlgImgAlignAbsBottom: "Absolut nederst",
+DlgImgAlignAbsMiddle: "Absolut centreret",
+DlgImgAlignBaseline : "Grundlinje",
+DlgImgAlignBottom : "Nederst",
+DlgImgAlignMiddle : "Centreret",
+DlgImgAlignRight : "Højre",
+DlgImgAlignTextTop : "Toppen af teksten",
+DlgImgAlignTop : "Øverst",
+DlgImgPreview : "Vis eksempel",
+DlgImgAlertUrl : "Indtast stien til billedet",
+DlgImgLinkTab : "Hyperlink",
+
+// Flash Dialog
+DlgFlashTitle : "Egenskaber for Flash",
+DlgFlashChkPlay : "Automatisk afspilning",
+DlgFlashChkLoop : "Gentagelse",
+DlgFlashChkMenu : "Vis Flash menu",
+DlgFlashScale : "Skalér",
+DlgFlashScaleAll : "Vis alt",
+DlgFlashScaleNoBorder : "Ingen ramme",
+DlgFlashScaleFit : "Tilpas størrelse",
+
+// Link Dialog
+DlgLnkWindowTitle : "Egenskaber for hyperlink",
+DlgLnkInfoTab : "Generelt",
+DlgLnkTargetTab : "MÃ¥l",
+
+DlgLnkType : "Hyperlink type",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Bogmærke på denne side",
+DlgLnkTypeEMail : "E-mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<anden>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Vælg et anker",
+DlgLnkAnchorByName : "Efter anker navn",
+DlgLnkAnchorById : "Efter element Id",
+DlgLnkNoAnchors : "<Ingen bogmærker dokumentet>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-mailadresse",
+DlgLnkEMailSubject : "Emne",
+DlgLnkEMailBody : "Brødtekst",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Upload",
+
+DlgLnkTarget : "MÃ¥l",
+DlgLnkTargetFrame : "<ramme>",
+DlgLnkTargetPopup : "<popup vindue>",
+DlgLnkTargetBlank : "Nyt vindue (_blank)",
+DlgLnkTargetParent : "Overordnet ramme (_parent)",
+DlgLnkTargetSelf : "Samme vindue (_self)",
+DlgLnkTargetTop : "Hele vinduet (_top)",
+DlgLnkTargetFrameName : "Destinationsvinduets navn",
+DlgLnkPopWinName : "Pop-up vinduets navn",
+DlgLnkPopWinFeat : "Egenskaber for pop-up",
+DlgLnkPopResize : "Skalering",
+DlgLnkPopLocation : "Adresselinje",
+DlgLnkPopMenu : "Menulinje",
+DlgLnkPopScroll : "Scrollbars",
+DlgLnkPopStatus : "Statuslinje",
+DlgLnkPopToolbar : "Værktøjslinje",
+DlgLnkPopFullScrn : "Fuld skærm (IE)",
+DlgLnkPopDependent : "Koblet/dependent (Netscape)",
+DlgLnkPopWidth : "Bredde",
+DlgLnkPopHeight : "Højde",
+DlgLnkPopLeft : "Position fra venstre",
+DlgLnkPopTop : "Position fra toppen",
+
+DlnLnkMsgNoUrl : "Indtast hyperlink URL!",
+DlnLnkMsgNoEMail : "Indtast e-mailaddresse!",
+DlnLnkMsgNoAnchor : "Vælg bogmærke!",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Vælg farve",
+DlgColorBtnClear : "Nulstil",
+DlgColorHighlight : "Markeret",
+DlgColorSelected : "Valgt",
+
+// Smiley Dialog
+DlgSmileyTitle : "Vælg smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Vælg symbol",
+
+// Table Dialog
+DlgTableTitle : "Egenskaber for tabel",
+DlgTableRows : "Rækker",
+DlgTableColumns : "Kolonner",
+DlgTableBorder : "Rammebredde",
+DlgTableAlign : "Justering",
+DlgTableAlignNotSet : "<intet valgt>",
+DlgTableAlignLeft : "Venstrestillet",
+DlgTableAlignCenter : "Centreret",
+DlgTableAlignRight : "Højrestillet",
+DlgTableWidth : "Bredde",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "procent",
+DlgTableHeight : "Højde",
+DlgTableCellSpace : "Celleafstand",
+DlgTableCellPad : "Cellemargen",
+DlgTableCaption : "Titel",
+DlgTableSummary : "Resume",
+
+// Table Cell Dialog
+DlgCellTitle : "Egenskaber for celle",
+DlgCellWidth : "Bredde",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "procent",
+DlgCellHeight : "Højde",
+DlgCellWordWrap : "Orddeling",
+DlgCellWordWrapNotSet : "<intet valgt>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nej",
+DlgCellHorAlign : "Vandret justering",
+DlgCellHorAlignNotSet : "<intet valgt>",
+DlgCellHorAlignLeft : "Venstrestillet",
+DlgCellHorAlignCenter : "Centreret",
+DlgCellHorAlignRight: "Højrestillet",
+DlgCellVerAlign : "Lodret justering",
+DlgCellVerAlignNotSet : "<intet valgt>",
+DlgCellVerAlignTop : "Øverst",
+DlgCellVerAlignMiddle : "Centreret",
+DlgCellVerAlignBottom : "Nederst",
+DlgCellVerAlignBaseline : "Grundlinje",
+DlgCellRowSpan : "Højde i antal rækker",
+DlgCellCollSpan : "Bredde i antal kolonner",
+DlgCellBackColor : "Baggrundsfarve",
+DlgCellBorderColor : "Rammefarve",
+DlgCellBtnSelect : "Vælg...",
+
+// Find Dialog
+DlgFindTitle : "Find",
+DlgFindFindBtn : "Find",
+DlgFindNotFoundMsg : "Søgeteksten blev ikke fundet!",
+
+// Replace Dialog
+DlgReplaceTitle : "Erstat",
+DlgReplaceFindLbl : "Søg efter:",
+DlgReplaceReplaceLbl : "Erstat med:",
+DlgReplaceCaseChk : "Forskel på store og små bogstaver",
+DlgReplaceReplaceBtn : "Erstat",
+DlgReplaceReplAllBtn : "Erstat alle",
+DlgReplaceWordChk : "Kun hele ord",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Din browsers sikkerhedsindstillinger tillader ikke editoren at klippe tekst automatisk!<br>Brug i stedet tastaturet til at klippe teksten (Ctrl+X).",
+PasteErrorCopy : "Din browsers sikkerhedsindstillinger tillader ikke editoren at kopiere tekst automatisk!<br>Brug i stedet tastaturet til at kopiere teksten (Ctrl+C).",
+
+PasteAsText : "Indsæt som ikke-formateret tekst",
+PasteFromWord : "Indsæt fra Word",
+
+DlgPasteMsg2 : "Indsæt i feltet herunder (<STRONG>Ctrl+V</STRONG>) og klik <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorer font definitioner",
+DlgPasteRemoveStyles : "Ignorer typografi",
+DlgPasteCleanBox : "Slet indhold",
+
+// Color Picker
+ColorAutomatic : "Automatisk",
+ColorMoreColors : "Flere farver...",
+
+// Document Properties
+DocProps : "Egenskaber for dokument",
+
+// Anchor Dialog
+DlgAnchorTitle : "Egenskaber for bogmærke",
+DlgAnchorName : "Bogmærke navn",
+DlgAnchorErrorName : "Indtast bogmærke navn!",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ikke i ordbogen",
+DlgSpellChangeTo : "Forslag",
+DlgSpellBtnIgnore : "Ignorer",
+DlgSpellBtnIgnoreAll : "Ignorer alle",
+DlgSpellBtnReplace : "Erstat",
+DlgSpellBtnReplaceAll : "Erstat alle",
+DlgSpellBtnUndo : "Tilbage",
+DlgSpellNoSuggestions : "- ingen forslag -",
+DlgSpellProgress : "Stavekontrolen arbejder...",
+DlgSpellNoMispell : "Stavekontrol færdig: Ingen fejl fundet",
+DlgSpellNoChanges : "Stavekontrol færdig: Ingen ord ændret",
+DlgSpellOneChange : "Stavekontrol færdig: Et ord ændret",
+DlgSpellManyChanges : "Stavekontrol færdig: %1 ord ændret",
+
+IeSpellDownload : "Stavekontrol ikke installeret.<br>Vil du hente den nu?",
+
+// Button Dialog
+DlgButtonText : "Tekst",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Navn",
+DlgCheckboxValue : "Værdi",
+DlgCheckboxSelected : "Valgt",
+
+// Form Dialog
+DlgFormName : "Navn",
+DlgFormAction : "Handling",
+DlgFormMethod : "Metod",
+
+// Select Field Dialog
+DlgSelectName : "Navn",
+DlgSelectValue : "Værdi",
+DlgSelectSize : "Størrelse",
+DlgSelectLines : "linier",
+DlgSelectChkMulti : "Tillad flere valg",
+DlgSelectOpAvail : "Valgmuligheder",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Værdi",
+DlgSelectBtnAdd : "Tilføj",
+DlgSelectBtnModify : "Rediger",
+DlgSelectBtnUp : "Op",
+DlgSelectBtnDown : "Ned",
+DlgSelectBtnSetValue : "Sæt som valgt",
+DlgSelectBtnDelete : "Slet",
+
+// Textarea Dialog
+DlgTextareaName : "Navn",
+DlgTextareaCols : "Kolonner",
+DlgTextareaRows : "Rækker",
+
+// Text Field Dialog
+DlgTextName : "Navn",
+DlgTextValue : "Værdi",
+DlgTextCharWidth : "Bredde (tegn)",
+DlgTextMaxChars : "Max antal tegn",
+DlgTextType : "Type",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Adgangskode",
+
+// Hidden Field Dialog
+DlgHiddenName : "Navn",
+DlgHiddenValue : "Værdi",
+
+// Bulleted List Dialog
+BulletedListProp : "Egenskaber for punktopstilling",
+NumberedListProp : "Egenskaber for talopstilling",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Type",
+DlgLstTypeCircle : "Cirkel",
+DlgLstTypeDisc : "Udfyldt cirkel",
+DlgLstTypeSquare : "Firkant",
+DlgLstTypeNumbers : "Nummereret (1, 2, 3)",
+DlgLstTypeLCase : "Små bogstaver (a, b, c)",
+DlgLstTypeUCase : "Store bogstaver (A, B, C)",
+DlgLstTypeSRoman : "Små romertal (i, ii, iii)",
+DlgLstTypeLRoman : "Store romertal (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Generelt",
+DlgDocBackTab : "Baggrund",
+DlgDocColorsTab : "Farver og margen",
+DlgDocMetaTab : "Metadata",
+
+DlgDocPageTitle : "Sidetitel",
+DlgDocLangDir : "Sprog",
+DlgDocLangDirLTR : "Fra venstre mod højre (LTR)",
+DlgDocLangDirRTL : "Fra højre mod venstre (RTL)",
+DlgDocLangCode : "Landekode",
+DlgDocCharSet : "Tegnsæt kode",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Anden tegnsæt kode",
+
+DlgDocDocType : "Dokumenttype kategori",
+DlgDocDocTypeOther : "Anden dokumenttype kategori",
+DlgDocIncXHTML : "Inkludere XHTML deklartion",
+DlgDocBgColor : "Baggrundsfarve",
+DlgDocBgImage : "Baggrundsbillede URL",
+DlgDocBgNoScroll : "Fastlåst baggrund",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Hyperlink",
+DlgDocCVisited : "Besøgt hyperlink",
+DlgDocCActive : "Aktivt hyperlink",
+DlgDocMargins : "Sidemargen",
+DlgDocMaTop : "Øverst",
+DlgDocMaLeft : "Venstre",
+DlgDocMaRight : "Højre",
+DlgDocMaBottom : "Nederst",
+DlgDocMeIndex : "Dokument index nøgleord (kommasepareret)",
+DlgDocMeDescr : "Dokument beskrivelse",
+DlgDocMeAuthor : "Forfatter",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Vis",
+
+// Templates Dialog
+Templates : "Skabeloner",
+DlgTemplatesTitle : "Indholdsskabeloner",
+DlgTemplatesSelMsg : "Vælg den skabelon, som skal åbnes i editoren.<br>(Nuværende indhold vil blive overskrevet!):",
+DlgTemplatesLoading : "Henter liste over skabeloner...",
+DlgTemplatesNoTpl : "(Der er ikke defineret nogen skabelon!)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Om",
+DlgAboutBrowserInfoTab : "Generelt",
+DlgAboutLicenseTab : "Licens",
+DlgAboutVersion : "version",
+DlgAboutInfo : "For yderlig information gå til"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/de.js b/httemplate/elements/fckeditor/editor/lang/de.js
new file mode 100644
index 0000000..2848d34
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/de.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * German language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Symbolleiste einklappen",
+ToolbarExpand : "Symbolleiste ausklappen",
+
+// Toolbar Items and Context Menu
+Save : "Speichern",
+NewPage : "Neue Seite",
+Preview : "Vorschau",
+Cut : "Ausschneiden",
+Copy : "Kopieren",
+Paste : "Einfügen",
+PasteText : "aus Textdatei einfügen",
+PasteWord : "aus MS-Word einfügen",
+Print : "Drucken",
+SelectAll : "Alles auswählen",
+RemoveFormat : "Formatierungen entfernen",
+InsertLinkLbl : "Link",
+InsertLink : "Link einfügen/editieren",
+RemoveLink : "Link entfernen",
+Anchor : "Anker einfügen/editieren",
+InsertImageLbl : "Bild",
+InsertImage : "Bild einfügen/editieren",
+InsertFlashLbl : "Flash",
+InsertFlash : "Flash einfügen/editieren",
+InsertTableLbl : "Tabelle",
+InsertTable : "Tabelle einfügen/editieren",
+InsertLineLbl : "Linie",
+InsertLine : "Horizontale Linie einfügen",
+InsertSpecialCharLbl: "Sonderzeichen",
+InsertSpecialChar : "Sonderzeichen einfügen/editieren",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Smiley einfügen",
+About : "Ãœber FCKeditor",
+Bold : "Fett",
+Italic : "Kursiv",
+Underline : "Unterstrichen",
+StrikeThrough : "Durchgestrichen",
+Subscript : "Tiefgestellt",
+Superscript : "Hochgestellt",
+LeftJustify : "Linksbündig",
+CenterJustify : "Zentriert",
+RightJustify : "Rechtsbündig",
+BlockJustify : "Blocksatz",
+DecreaseIndent : "Einzug verringern",
+IncreaseIndent : "Einzug erhöhen",
+Undo : "Rückgängig",
+Redo : "Wiederherstellen",
+NumberedListLbl : "Nummerierte Liste",
+NumberedList : "Nummerierte Liste einfügen/entfernen",
+BulletedListLbl : "Liste",
+BulletedList : "Liste einfügen/entfernen",
+ShowTableBorders : "Zeige Tabellenrahmen",
+ShowDetails : "Zeige Details",
+Style : "Stil",
+FontFormat : "Format",
+Font : "Schriftart",
+FontSize : "Größe",
+TextColor : "Textfarbe",
+BGColor : "Hintergrundfarbe",
+Source : "Quellcode",
+Find : "Finden",
+Replace : "Ersetzen",
+SpellCheck : "Rechtschreibprüfung",
+UniversalKeyboard : "Universal-Tastatur",
+PageBreakLbl : "Seitenumbruch",
+PageBreak : "Seitenumbruch einfügen",
+
+Form : "Formular",
+Checkbox : "Checkbox",
+RadioButton : "Radiobutton",
+TextField : "Textfeld einzeilig",
+Textarea : "Textfeld mehrzeilig",
+HiddenField : "verstecktes Feld",
+Button : "Klickbutton",
+SelectionField : "Auswahlfeld",
+ImageButton : "Bildbutton",
+
+FitWindow : "Editor maximieren",
+
+// Context Menu
+EditLink : "Link editieren",
+CellCM : "Zelle",
+RowCM : "Zeile",
+ColumnCM : "Spalte",
+InsertRow : "Zeile einfügen",
+DeleteRows : "Zeile entfernen",
+InsertColumn : "Spalte einfügen",
+DeleteColumns : "Spalte löschen",
+InsertCell : "Zelle einfügen",
+DeleteCells : "Zelle löschen",
+MergeCells : "Zellen vereinen",
+SplitCell : "Zelle teilen",
+TableDelete : "Tabelle löschen",
+CellProperties : "Zellen Eigenschaften",
+TableProperties : "Tabellen Eigenschaften",
+ImageProperties : "Bild Eigenschaften",
+FlashProperties : "Flash Eigenschaften",
+
+AnchorProp : "Anker Eigenschaften",
+ButtonProp : "Button Eigenschaften",
+CheckboxProp : "Checkbox Eigenschaften",
+HiddenFieldProp : "Verstecktes Feld Eigenschaften",
+RadioButtonProp : "Optionsfeld Eigenschaften",
+ImageButtonProp : "Bildbutton Eigenschaften",
+TextFieldProp : "Textfeld (einzeilig) Eigenschaften",
+SelectionFieldProp : "Auswahlfeld Eigenschaften",
+TextareaProp : "Textfeld (mehrzeilig) Eigenschaften",
+FormProp : "Formular Eigenschaften",
+
+FontFormats : "Normal;Formatiert;Addresse;Ãœberschrift 1;Ãœberschrift 2;Ãœberschrift 3;Ãœberschrift 4;Ãœberschrift 5;Ãœberschrift 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Bearbeite XHTML. Bitte warten...",
+Done : "Fertig",
+PasteWordConfirm : "Der Text, den Sie einfügen möchten, scheint aus MS-Word kopiert zu sein. Möchten Sie ihn zuvor bereinigen lassen?",
+NotCompatiblePaste : "Diese Funktion steht nur im Internet Explorer ab Version 5.5 zur Verfügung. Möchten Sie den Text unbereinigt einfügen?",
+UnknownToolbarItem : "Unbekanntes Menüleisten-Objekt \"%1\"",
+UnknownCommand : "Unbekannter Befehl \"%1\"",
+NotImplemented : "Befehl nicht implementiert",
+UnknownToolbarSet : "Menüleiste \"%1\" existiert nicht",
+NoActiveX : "Die Sicherheitseinstellungen Ihres Browsers beschränken evtl. einige Funktionen des Editors. Aktivieren Sie die Option \"ActiveX-Steuerelemente und Plugins ausführen\" in den Sicherheitseinstellungen, um diese Funktionen nutzen zu können",
+BrowseServerBlocked : "Ein Auswahlfenster konnte nicht geöffnet werden. Stellen Sie sicher, das alle Popup-Blocker ausgeschaltet sind.",
+DialogBlocked : "Das Dialog-Fenster konnte nicht geöffnet werden. Stellen Sie sicher, das alle Popup-Blocker ausgeschaltet sind.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Abbrechen",
+DlgBtnClose : "Schließen",
+DlgBtnBrowseServer : "Server durchsuchen",
+DlgAdvancedTag : "Erweitert",
+DlgOpOther : "<andere>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Bitte tragen Sie die URL ein",
+
+// General Dialogs Labels
+DlgGenNotSet : "< nichts >",
+DlgGenId : "ID",
+DlgGenLangDir : "Schreibrichtung",
+DlgGenLangDirLtr : "Links nach Rechts (LTR)",
+DlgGenLangDirRtl : "Rechts nach Links (RTL)",
+DlgGenLangCode : "Sprachenkürzel",
+DlgGenAccessKey : "Schlüssel",
+DlgGenName : "Name",
+DlgGenTabIndex : "Tab Index",
+DlgGenLongDescr : "Langform URL",
+DlgGenClass : "Stylesheet Klasse",
+DlgGenTitle : "Titel Beschreibung",
+DlgGenContType : "Content Beschreibung",
+DlgGenLinkCharset : "Ziel-Zeichensatz",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "Bild Eigenschaften",
+DlgImgInfoTab : "Bild-Info",
+DlgImgBtnUpload : "Zum Server senden",
+DlgImgURL : "Bildauswahl",
+DlgImgUpload : "Upload",
+DlgImgAlt : "Alternativer Text",
+DlgImgWidth : "Breite",
+DlgImgHeight : "Höhe",
+DlgImgLockRatio : "Größenverhältniss beibehalten",
+DlgBtnResetSize : "Größe zurücksetzen",
+DlgImgBorder : "Rahmen",
+DlgImgHSpace : "H-Abstand",
+DlgImgVSpace : "V-Abstand",
+DlgImgAlign : "Ausrichtung",
+DlgImgAlignLeft : "Links",
+DlgImgAlignAbsBottom: "Abs Unten",
+DlgImgAlignAbsMiddle: "Abs Mitte",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Unten",
+DlgImgAlignMiddle : "Mitte",
+DlgImgAlignRight : "Rechts",
+DlgImgAlignTextTop : "Text Oben",
+DlgImgAlignTop : "Oben",
+DlgImgPreview : "Vorschau",
+DlgImgAlertUrl : "Bitte geben Sie die Bild-URL an",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Eigenschaften",
+DlgFlashChkPlay : "autom. Abspielen",
+DlgFlashChkLoop : "Endlosschleife",
+DlgFlashChkMenu : "Flash-Menü aktivieren",
+DlgFlashScale : "Skalierung",
+DlgFlashScaleAll : "Alles anzeigen",
+DlgFlashScaleNoBorder : "ohne Rand",
+DlgFlashScaleFit : "Passgenau",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link Info",
+DlgLnkTargetTab : "Zielseite",
+
+DlgLnkType : "Link-Typ",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Anker in dieser Seite",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokoll",
+DlgLnkProtoOther : "<anderes>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Anker auswählen",
+DlgLnkAnchorByName : "nach Anker Name",
+DlgLnkAnchorById : "nach Element Id",
+DlgLnkNoAnchors : "<keine Anker im Dokument vorhanden>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail Addresse",
+DlgLnkEMailSubject : "Betreffzeile",
+DlgLnkEMailBody : "Nachrichtentext",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Zum Server senden",
+
+DlgLnkTarget : "Zielseite",
+DlgLnkTargetFrame : "<Frame>",
+DlgLnkTargetPopup : "<Pop-up Fenster>",
+DlgLnkTargetBlank : "Neues Fenster (_blank)",
+DlgLnkTargetParent : "Oberes Fenster (_parent)",
+DlgLnkTargetSelf : "Gleiches Fenster (_self)",
+DlgLnkTargetTop : "Oberstes Fenster (_top)",
+DlgLnkTargetFrameName : "Ziel-Fenster Name",
+DlgLnkPopWinName : "Pop-up Fenster Name",
+DlgLnkPopWinFeat : "Pop-up Fenster Eigenschaften",
+DlgLnkPopResize : "Vergrößerbar",
+DlgLnkPopLocation : "Adress-Leiste",
+DlgLnkPopMenu : "Menü-Leiste",
+DlgLnkPopScroll : "Rollbalken",
+DlgLnkPopStatus : "Statusleiste",
+DlgLnkPopToolbar : "Werkzeugleiste",
+DlgLnkPopFullScrn : "Vollbild (IE)",
+DlgLnkPopDependent : "Abhängig (Netscape)",
+DlgLnkPopWidth : "Breite",
+DlgLnkPopHeight : "Höhe",
+DlgLnkPopLeft : "Linke Position",
+DlgLnkPopTop : "Obere Position",
+
+DlnLnkMsgNoUrl : "Bitte geben Sie die Link-URL an",
+DlnLnkMsgNoEMail : "Bitte geben Sie e-Mail Adresse an",
+DlnLnkMsgNoAnchor : "Bitte wählen Sie einen Anker aus",
+DlnLnkMsgInvPopName : "Der Name des Popups muss mit einem Buchstaben beginnen und darf keine Leerzeichen enthalten",
+
+// Color Dialog
+DlgColorTitle : "Farbauswahl",
+DlgColorBtnClear : "Keine Farbe",
+DlgColorHighlight : "Vorschau",
+DlgColorSelected : "Ausgewählt",
+
+// Smiley Dialog
+DlgSmileyTitle : "Smiley auswählen",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Sonderzeichen auswählen",
+
+// Table Dialog
+DlgTableTitle : "Tabellen Eigenschaften",
+DlgTableRows : "Zeile",
+DlgTableColumns : "Spalte",
+DlgTableBorder : "Rahmen",
+DlgTableAlign : "Ausrichtung",
+DlgTableAlignNotSet : "<nichts>",
+DlgTableAlignLeft : "Links",
+DlgTableAlignCenter : "Zentriert",
+DlgTableAlignRight : "Rechts",
+DlgTableWidth : "Breite",
+DlgTableWidthPx : "Pixel",
+DlgTableWidthPc : "%",
+DlgTableHeight : "Höhe",
+DlgTableCellSpace : "Zellenabstand außen",
+DlgTableCellPad : "Zellenabstand innen",
+DlgTableCaption : "Ãœberschrift",
+DlgTableSummary : "Inhaltsübersicht",
+
+// Table Cell Dialog
+DlgCellTitle : "Zellen-Eigenschaften",
+DlgCellWidth : "Breite",
+DlgCellWidthPx : "Pixel",
+DlgCellWidthPc : "%",
+DlgCellHeight : "Höhe",
+DlgCellWordWrap : "Umbruch",
+DlgCellWordWrapNotSet : "<nichts>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nein",
+DlgCellHorAlign : "Horizontale Ausrichtung",
+DlgCellHorAlignNotSet : "<nichts>",
+DlgCellHorAlignLeft : "Links",
+DlgCellHorAlignCenter : "Zentriert",
+DlgCellHorAlignRight: "Rechts",
+DlgCellVerAlign : "Vertikale Ausrichtung",
+DlgCellVerAlignNotSet : "<nichts>",
+DlgCellVerAlignTop : "Oben",
+DlgCellVerAlignMiddle : "Mitte",
+DlgCellVerAlignBottom : "Unten",
+DlgCellVerAlignBaseline : "Grundlinie",
+DlgCellRowSpan : "Zeilen zusammenfassen",
+DlgCellCollSpan : "Spalten zusammenfassen",
+DlgCellBackColor : "Hintergrundfarbe",
+DlgCellBorderColor : "Rahmenfarbe",
+DlgCellBtnSelect : "Auswahl...",
+
+// Find Dialog
+DlgFindTitle : "Finden",
+DlgFindFindBtn : "Finden",
+DlgFindNotFoundMsg : "Der gesuchte Text wurde nicht gefunden.",
+
+// Replace Dialog
+DlgReplaceTitle : "Ersetzen",
+DlgReplaceFindLbl : "Suche nach:",
+DlgReplaceReplaceLbl : "Ersetze mit:",
+DlgReplaceCaseChk : "Groß-Kleinschreibung beachten",
+DlgReplaceReplaceBtn : "Ersetzen",
+DlgReplaceReplAllBtn : "Alle Ersetzen",
+DlgReplaceWordChk : "Nur ganze Worte suchen",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Die Sicherheitseinstellungen Ihres Browsers lassen es nicht zu, den Text automatisch auszuschneiden. Bitte benutzen Sie die System-Zwischenablage über STRG-X (ausschneiden) und STRG-V (einfügen).",
+PasteErrorCopy : "Die Sicherheitseinstellungen Ihres Browsers lassen es nicht zu, den Text automatisch kopieren. Bitte benutzen Sie die System-Zwischenablage über STRG-C (kopieren).",
+
+PasteAsText : "Als Text einfügen",
+PasteFromWord : "Aus Word einfügen",
+
+DlgPasteMsg2 : "Bitte fügen Sie den Text in der folgenden Box über die Tastatur (mit <STRONG>Ctrl+V</STRONG>) ein und bestätigen Sie mit <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignoriere Schriftart-Definitionen",
+DlgPasteRemoveStyles : "Entferne Style-Definitionen",
+DlgPasteCleanBox : "Inhalt aufräumen",
+
+// Color Picker
+ColorAutomatic : "Automatisch",
+ColorMoreColors : "Weitere Farben...",
+
+// Document Properties
+DocProps : "Dokument Eigenschaften",
+
+// Anchor Dialog
+DlgAnchorTitle : "Anker Eigenschaften",
+DlgAnchorName : "Anker Name",
+DlgAnchorErrorName : "Bitte geben Sie den Namen des Ankers ein",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Nicht im Wörterbuch",
+DlgSpellChangeTo : "Ändern in",
+DlgSpellBtnIgnore : "Ignorieren",
+DlgSpellBtnIgnoreAll : "Alle Ignorieren",
+DlgSpellBtnReplace : "Ersetzen",
+DlgSpellBtnReplaceAll : "Alle Ersetzen",
+DlgSpellBtnUndo : "Rückgängig",
+DlgSpellNoSuggestions : " - keine Vorschläge - ",
+DlgSpellProgress : "Rechtschreibprüfung läuft...",
+DlgSpellNoMispell : "Rechtschreibprüfung abgeschlossen - keine Fehler gefunden",
+DlgSpellNoChanges : "Rechtschreibprüfung abgeschlossen - keine Worte geändert",
+DlgSpellOneChange : "Rechtschreibprüfung abgeschlossen - ein Wort geändert",
+DlgSpellManyChanges : "Rechtschreibprüfung abgeschlossen - %1 Wörter geändert",
+
+IeSpellDownload : "Rechtschreibprüfung nicht installiert. Möchten Sie sie jetzt herunterladen?",
+
+// Button Dialog
+DlgButtonText : "Text (Wert)",
+DlgButtonType : "Typ",
+DlgButtonTypeBtn : "Button",
+DlgButtonTypeSbm : "Absenden",
+DlgButtonTypeRst : "Zurücksetzen",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Name",
+DlgCheckboxValue : "Wert",
+DlgCheckboxSelected : "ausgewählt",
+
+// Form Dialog
+DlgFormName : "Name",
+DlgFormAction : "Action",
+DlgFormMethod : "Method",
+
+// Select Field Dialog
+DlgSelectName : "Name",
+DlgSelectValue : "Wert",
+DlgSelectSize : "Größe",
+DlgSelectLines : "Linien",
+DlgSelectChkMulti : "Erlaube Mehrfachauswahl",
+DlgSelectOpAvail : "Mögliche Optionen",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Wert",
+DlgSelectBtnAdd : "Hinzufügen",
+DlgSelectBtnModify : "Ändern",
+DlgSelectBtnUp : "Hoch",
+DlgSelectBtnDown : "Runter",
+DlgSelectBtnSetValue : "Setze als Standardwert",
+DlgSelectBtnDelete : "Entfernen",
+
+// Textarea Dialog
+DlgTextareaName : "Name",
+DlgTextareaCols : "Spalten",
+DlgTextareaRows : "Reihen",
+
+// Text Field Dialog
+DlgTextName : "Name",
+DlgTextValue : "Wert",
+DlgTextCharWidth : "Zeichenbreite",
+DlgTextMaxChars : "Max. Zeichen",
+DlgTextType : "Typ",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Passwort",
+
+// Hidden Field Dialog
+DlgHiddenName : "Name",
+DlgHiddenValue : "Wert",
+
+// Bulleted List Dialog
+BulletedListProp : "Listen-Eigenschaften",
+NumberedListProp : "Nummerierte Listen-Eigenschaften",
+DlgLstStart : "Start",
+DlgLstType : "Typ",
+DlgLstTypeCircle : "Ring",
+DlgLstTypeDisc : "Kreis",
+DlgLstTypeSquare : "Quadrat",
+DlgLstTypeNumbers : "Nummern (1, 2, 3)",
+DlgLstTypeLCase : "Kleinbuchstaben (a, b, c)",
+DlgLstTypeUCase : "Großbuchstaben (A, B, C)",
+DlgLstTypeSRoman : "Kleine römische Zahlen (i, ii, iii)",
+DlgLstTypeLRoman : "Große römische Zahlen (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Allgemein",
+DlgDocBackTab : "Hintergrund",
+DlgDocColorsTab : "Farben und Abstände",
+DlgDocMetaTab : "Metadaten",
+
+DlgDocPageTitle : "Seitentitel",
+DlgDocLangDir : "Schriftrichtung",
+DlgDocLangDirLTR : "Links nach Rechts",
+DlgDocLangDirRTL : "Rechts nach Links",
+DlgDocLangCode : "Sprachkürzel",
+DlgDocCharSet : "Zeichenkodierung",
+DlgDocCharSetCE : "Zentraleuropäisch",
+DlgDocCharSetCT : "traditionell Chinesisch (Big5)",
+DlgDocCharSetCR : "Kyrillisch",
+DlgDocCharSetGR : "Griechisch",
+DlgDocCharSetJP : "Japanisch",
+DlgDocCharSetKR : "Koreanisch",
+DlgDocCharSetTR : "Türkisch",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Westeuropäisch",
+DlgDocCharSetOther : "Andere Zeichenkodierung",
+
+DlgDocDocType : "Dokumententyp",
+DlgDocDocTypeOther : "Anderer Dokumententyp",
+DlgDocIncXHTML : "Beziehe XHTML Deklarationen ein",
+DlgDocBgColor : "Hintergrundfarbe",
+DlgDocBgImage : "Hintergrundbild URL",
+DlgDocBgNoScroll : "feststehender Hintergrund",
+DlgDocCText : "Text",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Besuchter Link",
+DlgDocCActive : "Aktiver Link",
+DlgDocMargins : "Seitenränder",
+DlgDocMaTop : "Oben",
+DlgDocMaLeft : "Links",
+DlgDocMaRight : "Rechts",
+DlgDocMaBottom : "Unten",
+DlgDocMeIndex : "Schlüsselwörter (durch Komma getrennt)",
+DlgDocMeDescr : "Dokument-Beschreibung",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Vorschau",
+
+// Templates Dialog
+Templates : "Vorlagen",
+DlgTemplatesTitle : "Vorlagen",
+DlgTemplatesSelMsg : "Klicken Sie auf eine Vorlage, um sie im Editor zu öffnen (der aktuelle Inhalt wird dabei gelöscht!):",
+DlgTemplatesLoading : "Liste der Vorlagen wird geladen. Bitte warten...",
+DlgTemplatesNoTpl : "(keine Vorlagen definiert)",
+DlgTemplatesReplace : "Aktuellen Inhalt ersetzen",
+
+// About Dialog
+DlgAboutAboutTab : "Ãœber",
+DlgAboutBrowserInfoTab : "Browser-Info",
+DlgAboutLicenseTab : "Lizenz",
+DlgAboutVersion : "Version",
+DlgAboutInfo : "Für weitere Informationen siehe"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/el.js b/httemplate/elements/fckeditor/editor/lang/el.js
new file mode 100644
index 0000000..90fefc4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/el.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Greek language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "ΑπόκÏυψη ΜπάÏας ΕÏγαλείων",
+ToolbarExpand : "Εμφάνιση ΜπάÏας ΕÏγαλείων",
+
+// Toolbar Items and Context Menu
+Save : "Αποθήκευση",
+NewPage : "Îέα Σελίδα",
+Preview : "ΠÏοεπισκόπιση",
+Cut : "Αποκοπή",
+Copy : "ΑντιγÏαφή",
+Paste : "Επικόλληση",
+PasteText : "Επικόλληση (απλό κείμενο)",
+PasteWord : "Επικόλληση από το Word",
+Print : "ΕκτÏπωση",
+SelectAll : "Επιλογή όλων",
+RemoveFormat : "ΑφαίÏεση ΜοÏφοποίησης",
+InsertLinkLbl : "ΣÏνδεσμος (Link)",
+InsertLink : "Εισαγωγή/Μεταβολή Συνδέσμου (Link)",
+RemoveLink : "ΑφαίÏεση Συνδέσμου (Link)",
+Anchor : "Εισαγωγή/επεξεÏγασία Anchor",
+InsertImageLbl : "Εικόνα",
+InsertImage : "Εισαγωγή/Μεταβολή Εικόνας",
+InsertFlashLbl : "Εισαγωγή Flash",
+InsertFlash : "Εισαγωγή/επεξεÏγασία Flash",
+InsertTableLbl : "Πίνακας",
+InsertTable : "Εισαγωγή/Μεταβολή Πίνακα",
+InsertLineLbl : "ΓÏαμμή",
+InsertLine : "Εισαγωγή ΟÏιζόντιας ΓÏαμμής",
+InsertSpecialCharLbl: "Ειδικό ΣÏμβολο",
+InsertSpecialChar : "Εισαγωγή Î•Î¹Î´Î¹ÎºÎ¿Ï Î£Ï…Î¼Î²ÏŒÎ»Î¿Ï…",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Εισαγωγή Smiley",
+About : "ΠεÏί του FCKeditor",
+Bold : "Έντονα",
+Italic : "Πλάγια",
+Underline : "ΥπογÏάμμιση",
+StrikeThrough : "ΔιαγÏάμμιση",
+Subscript : "Δείκτης",
+Superscript : "Εκθέτης",
+LeftJustify : "Στοίχιση ΑÏιστεÏά",
+CenterJustify : "Στοίχιση στο ΚέντÏο",
+RightJustify : "Στοίχιση Δεξιά",
+BlockJustify : "ΠλήÏης Στοίχιση (Block)",
+DecreaseIndent : "Μείωση Εσοχής",
+IncreaseIndent : "ΑÏξηση Εσοχής",
+Undo : "ΑναίÏεση",
+Redo : "ΕπαναφοÏά",
+NumberedListLbl : "Λίστα με ΑÏιθμοÏÏ‚",
+NumberedList : "Εισαγωγή/ΔιαγÏαφή Λίστας με ΑÏιθμοÏÏ‚",
+BulletedListLbl : "Λίστα με Bullets",
+BulletedList : "Εισαγωγή/ΔιαγÏαφή Λίστας με Bullets",
+ShowTableBorders : "ΠÏοβολή ΟÏίων Πίνακα",
+ShowDetails : "ΠÏοβολή ΛεπτομεÏειών",
+Style : "Στυλ",
+FontFormat : "ΜοÏφή ΓÏαμματοσειÏάς",
+Font : "ΓÏαμματοσειÏά",
+FontSize : "Μέγεθος",
+TextColor : "ΧÏώμα ΓÏαμμάτων",
+BGColor : "ΧÏώμα ΥποβάθÏου",
+Source : "HTML κώδικας",
+Find : "Αναζήτηση",
+Replace : "Αντικατάσταση",
+SpellCheck : "ΟÏθογÏαφικός έλεγχος",
+UniversalKeyboard : "Διεθνής πληκτÏολόγιο",
+PageBreakLbl : "Τέλος σελίδας",
+PageBreak : "Εισαγωγή τέλους σελίδας",
+
+Form : "ΦόÏμα",
+Checkbox : "Κουτί επιλογής",
+RadioButton : "Κουμπί Radio",
+TextField : "Πεδίο κειμένου",
+Textarea : "ΠεÏιοχή κειμένου",
+HiddenField : "ΚÏυφό πεδίο",
+Button : "Κουμπί",
+SelectionField : "Πεδίο επιλογής",
+ImageButton : "Κουμπί εικόνας",
+
+FitWindow : "Μεγιστοποίηση Ï€ÏογÏάμματος",
+
+// Context Menu
+EditLink : "Μεταβολή Συνδέσμου (Link)",
+CellCM : "Κελί",
+RowCM : "ΣειÏά",
+ColumnCM : "Στήλη",
+InsertRow : "Εισαγωγή ΓÏαμμής",
+DeleteRows : "ΔιαγÏαφή ΓÏαμμών",
+InsertColumn : "Εισαγωγή Κολώνας",
+DeleteColumns : "ΔιαγÏαφή Κολωνών",
+InsertCell : "Εισαγωγή ΚελιοÏ",
+DeleteCells : "ΔιαγÏαφή Κελιών",
+MergeCells : "Ενοποίηση Κελιών",
+SplitCell : "ΔιαχωÏισμός ΚελιοÏ",
+TableDelete : "ΔιαγÏαφή πίνακα",
+CellProperties : "Ιδιότητες ΚελιοÏ",
+TableProperties : "Ιδιότητες Πίνακα",
+ImageProperties : "Ιδιότητες Εικόνας",
+FlashProperties : "Ιδιότητες Flash",
+
+AnchorProp : "Ιδιότητες άγκυÏας",
+ButtonProp : "Ιδιότητες κουμπιοÏ",
+CheckboxProp : "Ιδιότητες ÎºÎ¿Ï…Î¼Ï€Î¹Î¿Ï ÎµÏ€Î¹Î»Î¿Î³Î®Ï‚",
+HiddenFieldProp : "Ιδιότητες κÏÏ…Ï†Î¿Ï Ï€ÎµÎ´Î¯Î¿Ï…",
+RadioButtonProp : "Ιδιότητες ÎºÎ¿Ï…Î¼Ï€Î¹Î¿Ï radio",
+ImageButtonProp : "Ιδιότητες ÎºÎ¿Ï…Î¼Ï€Î¹Î¿Ï ÎµÎ¹ÎºÏŒÎ½Î±Ï‚",
+TextFieldProp : "Ιδιότητες πεδίου κειμένου",
+SelectionFieldProp : "Ιδιότητες πεδίου επιλογής",
+TextareaProp : "Ιδιότητες πεÏιοχής κειμένου",
+FormProp : "Ιδιότητες φόÏμας",
+
+FontFormats : "Κανονικό;ΜοÏφοποιημένο;ΔιεÏθυνση;Επικεφαλίδα 1;Επικεφαλίδα 2;Επικεφαλίδα 3;Επικεφαλίδα 4;Επικεφαλίδα 5;Επικεφαλίδα 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "ΕπεξεÏγασία XHTML. ΠαÏακαλώ πεÏιμένετε...",
+Done : "Έτοιμο",
+PasteWordConfirm : "Το κείμενο που θέλετε να επικολήσετε, φαίνεται πως Ï€ÏοέÏχεται από το Word. Θέλετε να καθαÏιστεί Ï€Ïιν επικοληθεί;",
+NotCompatiblePaste : "Αυτή η επιλογή είναι διαθέσιμη στον Internet Explorer έκδοση 5.5+. Θέλετε να γίνει η επικόλληση χωÏίς καθαÏισμό;",
+UnknownToolbarItem : "Άγνωστο αντικείμενο της μπάÏας εÏγαλείων \"%1\"",
+UnknownCommand : "Άγνωστή εντολή \"%1\"",
+NotImplemented : "Η εντολή δεν έχει ενεÏγοποιηθεί",
+UnknownToolbarSet : "Η μπάÏα εÏγαλείων \"%1\" δεν υπάÏχει",
+NoActiveX : "Οι Ïυθμίσεις ασφαλείας του browser σας μποÏεί να πεÏιοÏίσουν κάποιες Ïυθμίσεις του Ï€ÏογÏάμματος. ΧÏειάζεται να ενεÏγοποιήσετε την επιλογή \"Run ActiveX controls and plug-ins\". Ίσως παÏουσιαστοÏν λάθη και παÏατηÏήσετε ελειπείς λειτουÏγίες.",
+BrowseServerBlocked : "Οι πόÏοι του browser σας δεν είναι Ï€Ïοσπελάσιμοι. ΣιγουÏευτείτε ότι δεν υπάÏχουν ενεÏγοί popup blockers.",
+DialogBlocked : "Δεν ήταν δυνατό να ανοίξει το παÏάθυÏο διαλόγου. ΣιγουÏευτείτε ότι δεν υπάÏχουν ενεÏγοί popup blockers.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "ΑκÏÏωση",
+DlgBtnClose : "Κλείσιμο",
+DlgBtnBrowseServer : "ΕξεÏεÏνηση διακομιστή",
+DlgAdvancedTag : "Για Ï€ÏοχωÏημένους",
+DlgOpOther : "<Άλλα>",
+DlgInfoTab : "ΠληÏοφοÏίες",
+DlgAlertUrl : "ΠαÏακαλώ εισάγετε URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<χωÏίς>",
+DlgGenId : "Id",
+DlgGenLangDir : "ΚατεÏθυνση κειμένου",
+DlgGenLangDirLtr : "ΑÏιστεÏά Ï€Ïος Δεξιά (LTR)",
+DlgGenLangDirRtl : "Δεξιά Ï€Ïος ΑÏιστεÏά (RTL)",
+DlgGenLangCode : "Κωδικός Γλώσσας",
+DlgGenAccessKey : "Συντόμευση (Access Key)",
+DlgGenName : "Όνομα",
+DlgGenTabIndex : "Tab Index",
+DlgGenLongDescr : "Αναλυτική πεÏιγÏαφή URL",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "Συμβουλευτικός τίτλος",
+DlgGenContType : "Συμβουλευτικός τίτλος πεÏιεχομένου",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "ΣτÏλ",
+
+// Image Dialog
+DlgImgTitle : "Ιδιότητες Εικόνας",
+DlgImgInfoTab : "ΠληÏοφοÏίες Εικόνας",
+DlgImgBtnUpload : "Αποστολή στον Διακομιστή",
+DlgImgURL : "URL",
+DlgImgUpload : "Αποστολή",
+DlgImgAlt : "Εναλλακτικό Κείμενο (ALT)",
+DlgImgWidth : "Πλάτος",
+DlgImgHeight : "Ύψος",
+DlgImgLockRatio : "Κλείδωμα Αναλογίας",
+DlgBtnResetSize : "ΕπαναφοÏά ΑÏÏ‡Î¹ÎºÎ¿Ï ÎœÎµÎ³Î­Î¸Î¿Ï…Ï‚",
+DlgImgBorder : "ΠεÏιθώÏιο",
+DlgImgHSpace : "ΟÏιζόντιος ΧώÏος (HSpace)",
+DlgImgVSpace : "Κάθετος ΧώÏος (VSpace)",
+DlgImgAlign : "ΕυθυγÏάμμιση (Align)",
+DlgImgAlignLeft : "ΑÏιστεÏά",
+DlgImgAlignAbsBottom: "Απόλυτα Κάτω (Abs Bottom)",
+DlgImgAlignAbsMiddle: "Απόλυτα στη Μέση (Abs Middle)",
+DlgImgAlignBaseline : "ΓÏαμμή Βάσης (Baseline)",
+DlgImgAlignBottom : "Κάτω (Bottom)",
+DlgImgAlignMiddle : "Μέση (Middle)",
+DlgImgAlignRight : "Δεξιά (Right)",
+DlgImgAlignTextTop : "ΚοÏυφή Κειμένου (Text Top)",
+DlgImgAlignTop : "Πάνω (Top)",
+DlgImgPreview : "ΠÏοεπισκόπιση",
+DlgImgAlertUrl : "Εισάγετε την τοποθεσία (URL) της εικόνας",
+DlgImgLinkTab : "ΣÏνδεσμος",
+
+// Flash Dialog
+DlgFlashTitle : "Ιδιότητες flash",
+DlgFlashChkPlay : "Αυτόματη έναÏξη",
+DlgFlashChkLoop : "Επανάληψη",
+DlgFlashChkMenu : "ΕνεÏγοποίηση Flash Menu",
+DlgFlashScale : "Κλίμακα",
+DlgFlashScaleAll : "Εμφάνιση όλων",
+DlgFlashScaleNoBorder : "ΧωÏίς ÏŒÏια",
+DlgFlashScaleFit : "ΑκÏιβής εφαÏμογή",
+
+// Link Dialog
+DlgLnkWindowTitle : "ΣÏνδεσμος (Link)",
+DlgLnkInfoTab : "Link",
+DlgLnkTargetTab : "ΠαÏάθυÏο Στόχος (Target)",
+
+DlgLnkType : "ΤÏπος συνδέσμου (Link)",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "ΆγκυÏα σε αυτή τη σελίδα",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "ΠÏοτόκολο",
+DlgLnkProtoOther : "<άλλο>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Επιλέξτε μια άγκυÏα",
+DlgLnkAnchorByName : "Βάσει του Ονόματος (Name) της άγκυÏας",
+DlgLnkAnchorById : "Βάσει του Element Id",
+DlgLnkNoAnchors : "<Δεν υπάÏχουν άγκυÏες στο κείμενο>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ΔιεÏθυνση ΗλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï Î¤Î±Ï‡Ï…Î´Ïομείου",
+DlgLnkEMailSubject : "Θέμα ΜηνÏματος",
+DlgLnkEMailBody : "Κείμενο ΜηνÏματος",
+DlgLnkUpload : "Αποστολή",
+DlgLnkBtnUpload : "Αποστολή στον Διακομιστή",
+
+DlgLnkTarget : "ΠαÏάθυÏο Στόχος (Target)",
+DlgLnkTargetFrame : "<πλαίσιο>",
+DlgLnkTargetPopup : "<παÏάθυÏο popup>",
+DlgLnkTargetBlank : "Îέο ΠαÏάθυÏο (_blank)",
+DlgLnkTargetParent : "Γονικό ΠαÏάθυÏο (_parent)",
+DlgLnkTargetSelf : "Ίδιο ΠαÏάθυÏο (_self)",
+DlgLnkTargetTop : "Ανώτατο ΠαÏάθυÏο (_top)",
+DlgLnkTargetFrameName : "Όνομα πλαισίου στόχου",
+DlgLnkPopWinName : "Όνομα Popup Window",
+DlgLnkPopWinFeat : "Επιλογές Popup Window",
+DlgLnkPopResize : "Με αλλαγή Μεγέθους",
+DlgLnkPopLocation : "ΜπάÏα Τοποθεσίας",
+DlgLnkPopMenu : "ΜπάÏα Menu",
+DlgLnkPopScroll : "ΜπάÏες ΚÏλισης",
+DlgLnkPopStatus : "ΜπάÏα Status",
+DlgLnkPopToolbar : "ΜπάÏα ΕÏγαλείων",
+DlgLnkPopFullScrn : "ΟλόκληÏη η Οθόνη (IE)",
+DlgLnkPopDependent : "Dependent (Netscape)",
+DlgLnkPopWidth : "Πλάτος",
+DlgLnkPopHeight : "Ύψος",
+DlgLnkPopLeft : "Τοποθεσία ΑÏιστεÏής ΆκÏης",
+DlgLnkPopTop : "Τοποθεσία Πάνω ΆκÏης",
+
+DlnLnkMsgNoUrl : "Εισάγετε την τοποθεσία (URL) του υπεÏσυνδέσμου (Link)",
+DlnLnkMsgNoEMail : "Εισάγετε την διεÏθυνση ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï Ï„Î±Ï‡Ï…Î´Ïομείου",
+DlnLnkMsgNoAnchor : "Επιλέξτε ένα Anchor",
+DlnLnkMsgInvPopName : "Το όνομα του popup Ï€Ïέπει να αÏχίζει με χαÏακτήÏα της αλφαβήτου και να μην πεÏιέχει κενά",
+
+// Color Dialog
+DlgColorTitle : "Επιλογή χÏώματος",
+DlgColorBtnClear : "ΚαθαÏισμός",
+DlgColorHighlight : "ΠÏοεπισκόπιση",
+DlgColorSelected : "Επιλεγμένο",
+
+// Smiley Dialog
+DlgSmileyTitle : "Επιλέξτε ένα Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Επιλέξτε ένα Ειδικό ΣÏμβολο",
+
+// Table Dialog
+DlgTableTitle : "Ιδιότητες Πίνακα",
+DlgTableRows : "ΓÏαμμές",
+DlgTableColumns : "Κολώνες",
+DlgTableBorder : "Μέγεθος ΠεÏιθωÏίου",
+DlgTableAlign : "Στοίχιση",
+DlgTableAlignNotSet : "<χωÏίς>",
+DlgTableAlignLeft : "ΑÏιστεÏά",
+DlgTableAlignCenter : "ΚέντÏο",
+DlgTableAlignRight : "Δεξιά",
+DlgTableWidth : "Πλάτος",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "\%",
+DlgTableHeight : "Ύψος",
+DlgTableCellSpace : "Απόσταση κελιών",
+DlgTableCellPad : "Γέμισμα κελιών",
+DlgTableCaption : "ΥπέÏτιτλος",
+DlgTableSummary : "ΠεÏίληψη",
+
+// Table Cell Dialog
+DlgCellTitle : "Ιδιότητες ΚελιοÏ",
+DlgCellWidth : "Πλάτος",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "\%",
+DlgCellHeight : "Ύψος",
+DlgCellWordWrap : "Με αλλαγή γÏαμμής",
+DlgCellWordWrapNotSet : "<χωÏίς>",
+DlgCellWordWrapYes : "Îαι",
+DlgCellWordWrapNo : "Όχι",
+DlgCellHorAlign : "ΟÏιζόντια Στοίχιση",
+DlgCellHorAlignNotSet : "<χωÏίς>",
+DlgCellHorAlignLeft : "ΑÏιστεÏά",
+DlgCellHorAlignCenter : "ΚέντÏο",
+DlgCellHorAlignRight: "Δεξιά",
+DlgCellVerAlign : "Κάθετη Στοίχιση",
+DlgCellVerAlignNotSet : "<χωÏίς>",
+DlgCellVerAlignTop : "Πάνω (Top)",
+DlgCellVerAlignMiddle : "Μέση (Middle)",
+DlgCellVerAlignBottom : "Κάτω (Bottom)",
+DlgCellVerAlignBaseline : "ΓÏαμμή Βάσης (Baseline)",
+DlgCellRowSpan : "ΑÏιθμός ΓÏαμμών (Rows Span)",
+DlgCellCollSpan : "ΑÏιθμός Κολωνών (Columns Span)",
+DlgCellBackColor : "ΧÏώμα ΥποβάθÏου",
+DlgCellBorderColor : "ΧÏώμα ΠεÏιθωÏίου",
+DlgCellBtnSelect : "Επιλογή...",
+
+// Find Dialog
+DlgFindTitle : "Αναζήτηση",
+DlgFindFindBtn : "Αναζήτηση",
+DlgFindNotFoundMsg : "Το κείμενο δεν βÏέθηκε.",
+
+// Replace Dialog
+DlgReplaceTitle : "Αντικατάσταση",
+DlgReplaceFindLbl : "Αναζήτηση:",
+DlgReplaceReplaceLbl : "Αντικατάσταση με:",
+DlgReplaceCaseChk : "Έλεγχος πεζών/κεφαλαίων",
+DlgReplaceReplaceBtn : "Αντικατάσταση",
+DlgReplaceReplAllBtn : "Αντικατάσταση Όλων",
+DlgReplaceWordChk : "ΕÏÏεση πλήÏους λέξης",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Οι Ïυθμίσεις ασφαλείας του φυλλομετÏητή σας δεν επιτÏέπουν την επιλεγμένη εÏγασία αποκοπής. ΧÏησιμοποιείστε το πληκτÏολόγιο (Ctrl+X).",
+PasteErrorCopy : "Οι Ïυθμίσεις ασφαλείας του φυλλομετÏητή σας δεν επιτÏέπουν την επιλεγμένη εÏγασία αντιγÏαφής. ΧÏησιμοποιείστε το πληκτÏολόγιο (Ctrl+C).",
+
+PasteAsText : "Επικόλληση ως Απλό Κείμενο",
+PasteFromWord : "Επικόλληση από το Word",
+
+DlgPasteMsg2 : "ΠαÏακαλώ επικολήστε στο ακόλουθο κουτί χÏησιμοποιόντας το πληκτÏολόγιο (<STRONG>Ctrl+V</STRONG>) και πατήστε <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Αγνόηση Ï€ÏοδιαγÏαφών γÏαμματοσειÏάς",
+DlgPasteRemoveStyles : "ΑφαίÏεση Ï€ÏοδιαγÏαφών στÏλ",
+DlgPasteCleanBox : "Κουτί εκαθάÏισης",
+
+// Color Picker
+ColorAutomatic : "Αυτόματο",
+ColorMoreColors : "ΠεÏισσότεÏα χÏώματα...",
+
+// Document Properties
+DocProps : "Ιδιότητες εγγÏάφου",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ιδιότητες άγκυÏας",
+DlgAnchorName : "Όνομα άγκυÏας",
+DlgAnchorErrorName : "ΠαÏακαλοÏμε εισάγετε όνομα άγκυÏας",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Δεν υπάÏχει στο λεξικό",
+DlgSpellChangeTo : "Αλλαγή σε",
+DlgSpellBtnIgnore : "Αγνόηση",
+DlgSpellBtnIgnoreAll : "Αγνόηση όλων",
+DlgSpellBtnReplace : "Αντικατάσταση",
+DlgSpellBtnReplaceAll : "Αντικατάσταση όλων",
+DlgSpellBtnUndo : "ΑναίÏεση",
+DlgSpellNoSuggestions : "- Δεν υπάÏχουν Ï€Ïοτάσεις -",
+DlgSpellProgress : "ΟÏθογÏαφικός έλεγχος σε εξέλιξη...",
+DlgSpellNoMispell : "Ο οÏθογÏαφικός έλεγχος ολοκληÏώθηκε: Δεν βÏέθηκαν λάθη",
+DlgSpellNoChanges : "Ο οÏθογÏαφικός έλεγχος ολοκληÏώθηκε: Δεν άλλαξαν λέξεις",
+DlgSpellOneChange : "Ο οÏθογÏαφικός έλεγχος ολοκληÏώθηκε: Μια λέξη άλλαξε",
+DlgSpellManyChanges : "Ο οÏθογÏαφικός έλεγχος ολοκληÏώθηκε: %1 λέξεις άλλαξαν",
+
+IeSpellDownload : "Δεν υπάÏχει εγκατεστημένος οÏθογÏάφος. Θέλετε να τον κατεβάσετε Ï„ÏŽÏα;",
+
+// Button Dialog
+DlgButtonText : "Κείμενο (Τιμή)",
+DlgButtonType : "ΤÏπος",
+DlgButtonTypeBtn : "Κουμπί",
+DlgButtonTypeSbm : "ΚαταχώÏηση",
+DlgButtonTypeRst : "ΕπαναφοÏά",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Όνομα",
+DlgCheckboxValue : "Τιμή",
+DlgCheckboxSelected : "Επιλεγμένο",
+
+// Form Dialog
+DlgFormName : "Όνομα",
+DlgFormAction : "ΔÏάση",
+DlgFormMethod : "Μάθοδος",
+
+// Select Field Dialog
+DlgSelectName : "Όνομα",
+DlgSelectValue : "Τιμή",
+DlgSelectSize : "Μέγεθος",
+DlgSelectLines : "γÏαμμές",
+DlgSelectChkMulti : "Πολλαπλές επιλογές",
+DlgSelectOpAvail : "Διαθέσιμες επιλογές",
+DlgSelectOpText : "Κείμενο",
+DlgSelectOpValue : "Τιμή",
+DlgSelectBtnAdd : "ΠÏοσθήκη",
+DlgSelectBtnModify : "Αλλαγή",
+DlgSelectBtnUp : "Πάνω",
+DlgSelectBtnDown : "Κάτω",
+DlgSelectBtnSetValue : "ΠÏοεπιλεγμένη επιλογή",
+DlgSelectBtnDelete : "ΔιαγÏαφή",
+
+// Textarea Dialog
+DlgTextareaName : "Όνομα",
+DlgTextareaCols : "Στήλες",
+DlgTextareaRows : "ΣειÏές",
+
+// Text Field Dialog
+DlgTextName : "Όνομα",
+DlgTextValue : "Τιμή",
+DlgTextCharWidth : "Μήκος χαÏακτήÏων",
+DlgTextMaxChars : "Μέγιστοι χαÏακτήÏες",
+DlgTextType : "ΤÏπος",
+DlgTextTypeText : "Κείμενο",
+DlgTextTypePass : "Κωδικός",
+
+// Hidden Field Dialog
+DlgHiddenName : "Όνομα",
+DlgHiddenValue : "Τιμή",
+
+// Bulleted List Dialog
+BulletedListProp : "Ιδιότητες λίστας Bulleted",
+NumberedListProp : "Ιδιότητες αÏιθμημένης λίστας ",
+DlgLstStart : "ΑÏχή",
+DlgLstType : "ΤÏπος",
+DlgLstTypeCircle : "ΚÏκλος",
+DlgLstTypeDisc : "Δίσκος",
+DlgLstTypeSquare : "ΤετÏάγωνο",
+DlgLstTypeNumbers : "ΑÏιθμοί (1, 2, 3)",
+DlgLstTypeLCase : "Πεζά γÏάμματα (a, b, c)",
+DlgLstTypeUCase : "Κεφαλαία γÏάμματα (A, B, C)",
+DlgLstTypeSRoman : "ΜικÏά λατινικά αÏιθμητικά (i, ii, iii)",
+DlgLstTypeLRoman : "Μεγάλα λατινικά αÏιθμητικά (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Γενικά",
+DlgDocBackTab : "Φόντο",
+DlgDocColorsTab : "ΧÏώματα και πεÏιθώÏια",
+DlgDocMetaTab : "Δεδομένα Meta",
+
+DlgDocPageTitle : "Τίτλος σελίδας",
+DlgDocLangDir : "ΚατεÏθυνση γÏαφής",
+DlgDocLangDirLTR : "αÏιστεÏά Ï€Ïος δεξιά (LTR)",
+DlgDocLangDirRTL : "δεξιά Ï€Ïος αÏιστεÏά (RTL)",
+DlgDocLangCode : "Κωδικός γλώσσας",
+DlgDocCharSet : "Κωδικοποίηση χαÏακτήÏων",
+DlgDocCharSetCE : "ΚεντÏικής ΕυÏώπης",
+DlgDocCharSetCT : "ΠαÏαδοσιακά κινέζικα (Big5)",
+DlgDocCharSetCR : "ΚυÏιλλική",
+DlgDocCharSetGR : "Ελληνική",
+DlgDocCharSetJP : "Ιαπωνική",
+DlgDocCharSetKR : "ΚοÏεάτικη",
+DlgDocCharSetTR : "ΤουÏκική",
+DlgDocCharSetUN : "Διεθνής (UTF-8)",
+DlgDocCharSetWE : "Δυτικής ΕυÏώπης",
+DlgDocCharSetOther : "Άλλη κωδικοποίηση χαÏακτήÏων",
+
+DlgDocDocType : "Επικεφαλίδα Ï„Ïπου εγγÏάφου",
+DlgDocDocTypeOther : "Άλλη επικεφαλίδα Ï„Ïπου εγγÏάφου",
+DlgDocIncXHTML : "Îα συμπεÏιληφθοÏν οι δηλώσεις XHTML",
+DlgDocBgColor : "ΧÏώμα φόντου",
+DlgDocBgImage : "ΔιεÏθυνση εικόνας φόντου",
+DlgDocBgNoScroll : "Φόντο χωÏίς κÏλιση",
+DlgDocCText : "Κείμενο",
+DlgDocCLink : "ΣÏνδεσμος",
+DlgDocCVisited : "ΣÏνδεσμος που έχει επισκευθεί",
+DlgDocCActive : "ΕνεÏγός σÏνδεσμος",
+DlgDocMargins : "ΠεÏιθώÏια σελίδας",
+DlgDocMaTop : "ΚοÏυφή",
+DlgDocMaLeft : "ΑÏιστεÏά",
+DlgDocMaRight : "Δεξιά",
+DlgDocMaBottom : "Κάτω",
+DlgDocMeIndex : "Λέξεις κλειδιά δείκτες εγγÏάφου (διαχωÏισμός με κόμμα)",
+DlgDocMeDescr : "ΠεÏιγÏαφή εγγÏάφου",
+DlgDocMeAuthor : "ΣυγγÏαφέας",
+DlgDocMeCopy : "Πνευματικά δικαιώματα",
+DlgDocPreview : "ΠÏοεπισκόπηση",
+
+// Templates Dialog
+Templates : "ΠÏότυπα",
+DlgTemplatesTitle : "ΠÏότυπα πεÏιεχομένου",
+DlgTemplatesSelMsg : "ΠαÏακαλώ επιλέξτε Ï€Ïότυπο για εισαγωγή στο Ï€ÏόγÏαμμα<br>(τα υπάÏχοντα πεÏιεχόμενα θα χαθοÏν):",
+DlgTemplatesLoading : "ΦόÏτωση καταλόγου Ï€ÏοτÏπων. ΠαÏακαλώ πεÏιμένετε...",
+DlgTemplatesNoTpl : "(Δεν έχουν καθοÏιστεί Ï€Ïότυπα)",
+DlgTemplatesReplace : "Αντικατάσταση υπάÏχοντων πεÏιεχομένων",
+
+// About Dialog
+DlgAboutAboutTab : "Σχετικά",
+DlgAboutBrowserInfoTab : "ΠληÏοφοÏίες Browser",
+DlgAboutLicenseTab : "Άδεια",
+DlgAboutVersion : "έκδοση",
+DlgAboutInfo : "Για πεÏισσότεÏες πληÏοφοÏίες"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/en-au.js b/httemplate/elements/fckeditor/editor/lang/en-au.js
new file mode 100644
index 0000000..b6960b6
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/en-au.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * English (Australia) language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Collapse Toolbar",
+ToolbarExpand : "Expand Toolbar",
+
+// Toolbar Items and Context Menu
+Save : "Save",
+NewPage : "New Page",
+Preview : "Preview",
+Cut : "Cut",
+Copy : "Copy",
+Paste : "Paste",
+PasteText : "Paste as plain text",
+PasteWord : "Paste from Word",
+Print : "Print",
+SelectAll : "Select All",
+RemoveFormat : "Remove Format",
+InsertLinkLbl : "Link",
+InsertLink : "Insert/Edit Link",
+RemoveLink : "Remove Link",
+Anchor : "Insert/Edit Anchor",
+InsertImageLbl : "Image",
+InsertImage : "Insert/Edit Image",
+InsertFlashLbl : "Flash",
+InsertFlash : "Insert/Edit Flash",
+InsertTableLbl : "Table",
+InsertTable : "Insert/Edit Table",
+InsertLineLbl : "Line",
+InsertLine : "Insert Horizontal Line",
+InsertSpecialCharLbl: "Special Character",
+InsertSpecialChar : "Insert Special Character",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Insert Smiley",
+About : "About FCKeditor",
+Bold : "Bold",
+Italic : "Italic",
+Underline : "Underline",
+StrikeThrough : "Strike Through",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Left Justify",
+CenterJustify : "Centre Justify",
+RightJustify : "Right Justify",
+BlockJustify : "Block Justify",
+DecreaseIndent : "Decrease Indent",
+IncreaseIndent : "Increase Indent",
+Undo : "Undo",
+Redo : "Redo",
+NumberedListLbl : "Numbered List",
+NumberedList : "Insert/Remove Numbered List",
+BulletedListLbl : "Bulleted List",
+BulletedList : "Insert/Remove Bulleted List",
+ShowTableBorders : "Show Table Borders",
+ShowDetails : "Show Details",
+Style : "Style",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "Size",
+TextColor : "Text Colour",
+BGColor : "Background Colour",
+Source : "Source",
+Find : "Find",
+Replace : "Replace",
+SpellCheck : "Check Spelling",
+UniversalKeyboard : "Universal Keyboard",
+PageBreakLbl : "Page Break",
+PageBreak : "Insert Page Break",
+
+Form : "Form",
+Checkbox : "Checkbox",
+RadioButton : "Radio Button",
+TextField : "Text Field",
+Textarea : "Textarea",
+HiddenField : "Hidden Field",
+Button : "Button",
+SelectionField : "Selection Field",
+ImageButton : "Image Button",
+
+FitWindow : "Maximize the editor size",
+
+// Context Menu
+EditLink : "Edit Link",
+CellCM : "Cell",
+RowCM : "Row",
+ColumnCM : "Column",
+InsertRow : "Insert Row",
+DeleteRows : "Delete Rows",
+InsertColumn : "Insert Column",
+DeleteColumns : "Delete Columns",
+InsertCell : "Insert Cell",
+DeleteCells : "Delete Cells",
+MergeCells : "Merge Cells",
+SplitCell : "Split Cell",
+TableDelete : "Delete Table",
+CellProperties : "Cell Properties",
+TableProperties : "Table Properties",
+ImageProperties : "Image Properties",
+FlashProperties : "Flash Properties",
+
+AnchorProp : "Anchor Properties",
+ButtonProp : "Button Properties",
+CheckboxProp : "Checkbox Properties",
+HiddenFieldProp : "Hidden Field Properties",
+RadioButtonProp : "Radio Button Properties",
+ImageButtonProp : "Image Button Properties",
+TextFieldProp : "Text Field Properties",
+SelectionFieldProp : "Selection Field Properties",
+TextareaProp : "Textarea Properties",
+FormProp : "Form Properties",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Processing XHTML. Please wait...",
+Done : "Done",
+PasteWordConfirm : "The text you want to paste seems to be copied from Word. Do you want to clean it before pasting?",
+NotCompatiblePaste : "This command is available for Internet Explorer version 5.5 or more. Do you want to paste without cleaning?",
+UnknownToolbarItem : "Unknown toolbar item \"%1\"",
+UnknownCommand : "Unknown command name \"%1\"",
+NotImplemented : "Command not implemented",
+UnknownToolbarSet : "Toolbar set \"%1\" doesn't exist",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.",
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.",
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancel",
+DlgBtnClose : "Close",
+DlgBtnBrowseServer : "Browse Server",
+DlgAdvancedTag : "Advanced",
+DlgOpOther : "<Other>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Please insert the URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<not set>",
+DlgGenId : "Id",
+DlgGenLangDir : "Language Direction",
+DlgGenLangDirLtr : "Left to Right (LTR)",
+DlgGenLangDirRtl : "Right to Left (RTL)",
+DlgGenLangCode : "Language Code",
+DlgGenAccessKey : "Access Key",
+DlgGenName : "Name",
+DlgGenTabIndex : "Tab Index",
+DlgGenLongDescr : "Long Description URL",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "Advisory Title",
+DlgGenContType : "Advisory Content Type",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "Image Properties",
+DlgImgInfoTab : "Image Info",
+DlgImgBtnUpload : "Send it to the Server",
+DlgImgURL : "URL",
+DlgImgUpload : "Upload",
+DlgImgAlt : "Alternative Text",
+DlgImgWidth : "Width",
+DlgImgHeight : "Height",
+DlgImgLockRatio : "Lock Ratio",
+DlgBtnResetSize : "Reset Size",
+DlgImgBorder : "Border",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Align",
+DlgImgAlignLeft : "Left",
+DlgImgAlignAbsBottom: "Abs Bottom",
+DlgImgAlignAbsMiddle: "Abs Middle",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Bottom",
+DlgImgAlignMiddle : "Middle",
+DlgImgAlignRight : "Right",
+DlgImgAlignTextTop : "Text Top",
+DlgImgAlignTop : "Top",
+DlgImgPreview : "Preview",
+DlgImgAlertUrl : "Please type the image URL",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties",
+DlgFlashChkPlay : "Auto Play",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Enable Flash Menu",
+DlgFlashScale : "Scale",
+DlgFlashScaleAll : "Show all",
+DlgFlashScaleNoBorder : "No Border",
+DlgFlashScaleFit : "Exact Fit",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link Info",
+DlgLnkTargetTab : "Target",
+
+DlgLnkType : "Link Type",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Link to anchor in the text",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocol",
+DlgLnkProtoOther : "<other>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Select an Anchor",
+DlgLnkAnchorByName : "By Anchor Name",
+DlgLnkAnchorById : "By Element Id",
+DlgLnkNoAnchors : "(No anchors available in the document)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail Address",
+DlgLnkEMailSubject : "Message Subject",
+DlgLnkEMailBody : "Message Body",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Send it to the Server",
+
+DlgLnkTarget : "Target",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<popup window>",
+DlgLnkTargetBlank : "New Window (_blank)",
+DlgLnkTargetParent : "Parent Window (_parent)",
+DlgLnkTargetSelf : "Same Window (_self)",
+DlgLnkTargetTop : "Topmost Window (_top)",
+DlgLnkTargetFrameName : "Target Frame Name",
+DlgLnkPopWinName : "Popup Window Name",
+DlgLnkPopWinFeat : "Popup Window Features",
+DlgLnkPopResize : "Resizable",
+DlgLnkPopLocation : "Location Bar",
+DlgLnkPopMenu : "Menu Bar",
+DlgLnkPopScroll : "Scroll Bars",
+DlgLnkPopStatus : "Status Bar",
+DlgLnkPopToolbar : "Toolbar",
+DlgLnkPopFullScrn : "Full Screen (IE)",
+DlgLnkPopDependent : "Dependent (Netscape)",
+DlgLnkPopWidth : "Width",
+DlgLnkPopHeight : "Height",
+DlgLnkPopLeft : "Left Position",
+DlgLnkPopTop : "Top Position",
+
+DlnLnkMsgNoUrl : "Please type the link URL",
+DlnLnkMsgNoEMail : "Please type the e-mail address",
+DlnLnkMsgNoAnchor : "Please select an anchor",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces",
+
+// Color Dialog
+DlgColorTitle : "Select Colour",
+DlgColorBtnClear : "Clear",
+DlgColorHighlight : "Highlight",
+DlgColorSelected : "Selected",
+
+// Smiley Dialog
+DlgSmileyTitle : "Insert a Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Select Special Character",
+
+// Table Dialog
+DlgTableTitle : "Table Properties",
+DlgTableRows : "Rows",
+DlgTableColumns : "Columns",
+DlgTableBorder : "Border size",
+DlgTableAlign : "Alignment",
+DlgTableAlignNotSet : "<Not set>",
+DlgTableAlignLeft : "Left",
+DlgTableAlignCenter : "Centre",
+DlgTableAlignRight : "Right",
+DlgTableWidth : "Width",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "percent",
+DlgTableHeight : "Height",
+DlgTableCellSpace : "Cell spacing",
+DlgTableCellPad : "Cell padding",
+DlgTableCaption : "Caption",
+DlgTableSummary : "Summary",
+
+// Table Cell Dialog
+DlgCellTitle : "Cell Properties",
+DlgCellWidth : "Width",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "percent",
+DlgCellHeight : "Height",
+DlgCellWordWrap : "Word Wrap",
+DlgCellWordWrapNotSet : "<Not set>",
+DlgCellWordWrapYes : "Yes",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "Horizontal Alignment",
+DlgCellHorAlignNotSet : "<Not set>",
+DlgCellHorAlignLeft : "Left",
+DlgCellHorAlignCenter : "Centre",
+DlgCellHorAlignRight: "Right",
+DlgCellVerAlign : "Vertical Alignment",
+DlgCellVerAlignNotSet : "<Not set>",
+DlgCellVerAlignTop : "Top",
+DlgCellVerAlignMiddle : "Middle",
+DlgCellVerAlignBottom : "Bottom",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Rows Span",
+DlgCellCollSpan : "Columns Span",
+DlgCellBackColor : "Background Colour",
+DlgCellBorderColor : "Border Colour",
+DlgCellBtnSelect : "Select...",
+
+// Find Dialog
+DlgFindTitle : "Find",
+DlgFindFindBtn : "Find",
+DlgFindNotFoundMsg : "The specified text was not found.",
+
+// Replace Dialog
+DlgReplaceTitle : "Replace",
+DlgReplaceFindLbl : "Find what:",
+DlgReplaceReplaceLbl : "Replace with:",
+DlgReplaceCaseChk : "Match case",
+DlgReplaceReplaceBtn : "Replace",
+DlgReplaceReplAllBtn : "Replace All",
+DlgReplaceWordChk : "Match whole word",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Your browser security settings don't permit the editor to automatically execute cutting operations. Please use the keyboard for that (Ctrl+X).",
+PasteErrorCopy : "Your browser security settings don't permit the editor to automatically execute copying operations. Please use the keyboard for that (Ctrl+C).",
+
+PasteAsText : "Paste as Plain Text",
+PasteFromWord : "Paste from Word",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<STRONG>Ctrl+V</STRONG>) and hit <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.",
+DlgPasteIgnoreFont : "Ignore Font Face definitions",
+DlgPasteRemoveStyles : "Remove Styles definitions",
+DlgPasteCleanBox : "Clean Up Box",
+
+// Color Picker
+ColorAutomatic : "Automatic",
+ColorMoreColors : "More Colours...",
+
+// Document Properties
+DocProps : "Document Properties",
+
+// Anchor Dialog
+DlgAnchorTitle : "Anchor Properties",
+DlgAnchorName : "Anchor Name",
+DlgAnchorErrorName : "Please type the anchor name",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Not in dictionary",
+DlgSpellChangeTo : "Change to",
+DlgSpellBtnIgnore : "Ignore",
+DlgSpellBtnIgnoreAll : "Ignore All",
+DlgSpellBtnReplace : "Replace",
+DlgSpellBtnReplaceAll : "Replace All",
+DlgSpellBtnUndo : "Undo",
+DlgSpellNoSuggestions : "- No suggestions -",
+DlgSpellProgress : "Spell check in progress...",
+DlgSpellNoMispell : "Spell check complete: No misspellings found",
+DlgSpellNoChanges : "Spell check complete: No words changed",
+DlgSpellOneChange : "Spell check complete: One word changed",
+DlgSpellManyChanges : "Spell check complete: %1 words changed",
+
+IeSpellDownload : "Spell checker not installed. Do you want to download it now?",
+
+// Button Dialog
+DlgButtonText : "Text (Value)",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Button",
+DlgButtonTypeSbm : "Submit",
+DlgButtonTypeRst : "Reset",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Name",
+DlgCheckboxValue : "Value",
+DlgCheckboxSelected : "Selected",
+
+// Form Dialog
+DlgFormName : "Name",
+DlgFormAction : "Action",
+DlgFormMethod : "Method",
+
+// Select Field Dialog
+DlgSelectName : "Name",
+DlgSelectValue : "Value",
+DlgSelectSize : "Size",
+DlgSelectLines : "lines",
+DlgSelectChkMulti : "Allow multiple selections",
+DlgSelectOpAvail : "Available Options",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Value",
+DlgSelectBtnAdd : "Add",
+DlgSelectBtnModify : "Modify",
+DlgSelectBtnUp : "Up",
+DlgSelectBtnDown : "Down",
+DlgSelectBtnSetValue : "Set as selected value",
+DlgSelectBtnDelete : "Delete",
+
+// Textarea Dialog
+DlgTextareaName : "Name",
+DlgTextareaCols : "Columns",
+DlgTextareaRows : "Rows",
+
+// Text Field Dialog
+DlgTextName : "Name",
+DlgTextValue : "Value",
+DlgTextCharWidth : "Character Width",
+DlgTextMaxChars : "Maximum Characters",
+DlgTextType : "Type",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Password",
+
+// Hidden Field Dialog
+DlgHiddenName : "Name",
+DlgHiddenValue : "Value",
+
+// Bulleted List Dialog
+BulletedListProp : "Bulleted List Properties",
+NumberedListProp : "Numbered List Properties",
+DlgLstStart : "Start",
+DlgLstType : "Type",
+DlgLstTypeCircle : "Circle",
+DlgLstTypeDisc : "Disc",
+DlgLstTypeSquare : "Square",
+DlgLstTypeNumbers : "Numbers (1, 2, 3)",
+DlgLstTypeLCase : "Lowercase Letters (a, b, c)",
+DlgLstTypeUCase : "Uppercase Letters (A, B, C)",
+DlgLstTypeSRoman : "Small Roman Numerals (i, ii, iii)",
+DlgLstTypeLRoman : "Large Roman Numerals (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General",
+DlgDocBackTab : "Background",
+DlgDocColorsTab : "Colours and Margins",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Page Title",
+DlgDocLangDir : "Language Direction",
+DlgDocLangDirLTR : "Left to Right (LTR)",
+DlgDocLangDirRTL : "Right to Left (RTL)",
+DlgDocLangCode : "Language Code",
+DlgDocCharSet : "Character Set Encoding",
+DlgDocCharSetCE : "Central European",
+DlgDocCharSetCT : "Chinese Traditional (Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Greek",
+DlgDocCharSetJP : "Japanese",
+DlgDocCharSetKR : "Korean",
+DlgDocCharSetTR : "Turkish",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Western European",
+DlgDocCharSetOther : "Other Character Set Encoding",
+
+DlgDocDocType : "Document Type Heading",
+DlgDocDocTypeOther : "Other Document Type Heading",
+DlgDocIncXHTML : "Include XHTML Declarations",
+DlgDocBgColor : "Background Colour",
+DlgDocBgImage : "Background Image URL",
+DlgDocBgNoScroll : "Nonscrolling Background",
+DlgDocCText : "Text",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Visited Link",
+DlgDocCActive : "Active Link",
+DlgDocMargins : "Page Margins",
+DlgDocMaTop : "Top",
+DlgDocMaLeft : "Left",
+DlgDocMaRight : "Right",
+DlgDocMaBottom : "Bottom",
+DlgDocMeIndex : "Document Indexing Keywords (comma separated)",
+DlgDocMeDescr : "Document Description",
+DlgDocMeAuthor : "Author",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Preview",
+
+// Templates Dialog
+Templates : "Templates",
+DlgTemplatesTitle : "Content Templates",
+DlgTemplatesSelMsg : "Please select the template to open in the editor<br>(the actual contents will be lost):",
+DlgTemplatesLoading : "Loading templates list. Please wait...",
+DlgTemplatesNoTpl : "(No templates defined)",
+DlgTemplatesReplace : "Replace actual contents",
+
+// About Dialog
+DlgAboutAboutTab : "About",
+DlgAboutBrowserInfoTab : "Browser Info",
+DlgAboutLicenseTab : "License",
+DlgAboutVersion : "version",
+DlgAboutInfo : "For further information go to"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/en-ca.js b/httemplate/elements/fckeditor/editor/lang/en-ca.js
new file mode 100644
index 0000000..2900a9d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/en-ca.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * English (Canadian) language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Collapse Toolbar",
+ToolbarExpand : "Expand Toolbar",
+
+// Toolbar Items and Context Menu
+Save : "Save",
+NewPage : "New Page",
+Preview : "Preview",
+Cut : "Cut",
+Copy : "Copy",
+Paste : "Paste",
+PasteText : "Paste as plain text",
+PasteWord : "Paste from Word",
+Print : "Print",
+SelectAll : "Select All",
+RemoveFormat : "Remove Format",
+InsertLinkLbl : "Link",
+InsertLink : "Insert/Edit Link",
+RemoveLink : "Remove Link",
+Anchor : "Insert/Edit Anchor",
+InsertImageLbl : "Image",
+InsertImage : "Insert/Edit Image",
+InsertFlashLbl : "Flash",
+InsertFlash : "Insert/Edit Flash",
+InsertTableLbl : "Table",
+InsertTable : "Insert/Edit Table",
+InsertLineLbl : "Line",
+InsertLine : "Insert Horizontal Line",
+InsertSpecialCharLbl: "Special Character",
+InsertSpecialChar : "Insert Special Character",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Insert Smiley",
+About : "About FCKeditor",
+Bold : "Bold",
+Italic : "Italic",
+Underline : "Underline",
+StrikeThrough : "Strike Through",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Left Justify",
+CenterJustify : "Centre Justify",
+RightJustify : "Right Justify",
+BlockJustify : "Block Justify",
+DecreaseIndent : "Decrease Indent",
+IncreaseIndent : "Increase Indent",
+Undo : "Undo",
+Redo : "Redo",
+NumberedListLbl : "Numbered List",
+NumberedList : "Insert/Remove Numbered List",
+BulletedListLbl : "Bulleted List",
+BulletedList : "Insert/Remove Bulleted List",
+ShowTableBorders : "Show Table Borders",
+ShowDetails : "Show Details",
+Style : "Style",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "Size",
+TextColor : "Text Colour",
+BGColor : "Background Colour",
+Source : "Source",
+Find : "Find",
+Replace : "Replace",
+SpellCheck : "Check Spelling",
+UniversalKeyboard : "Universal Keyboard",
+PageBreakLbl : "Page Break",
+PageBreak : "Insert Page Break",
+
+Form : "Form",
+Checkbox : "Checkbox",
+RadioButton : "Radio Button",
+TextField : "Text Field",
+Textarea : "Textarea",
+HiddenField : "Hidden Field",
+Button : "Button",
+SelectionField : "Selection Field",
+ImageButton : "Image Button",
+
+FitWindow : "Maximize the editor size",
+
+// Context Menu
+EditLink : "Edit Link",
+CellCM : "Cell",
+RowCM : "Row",
+ColumnCM : "Column",
+InsertRow : "Insert Row",
+DeleteRows : "Delete Rows",
+InsertColumn : "Insert Column",
+DeleteColumns : "Delete Columns",
+InsertCell : "Insert Cell",
+DeleteCells : "Delete Cells",
+MergeCells : "Merge Cells",
+SplitCell : "Split Cell",
+TableDelete : "Delete Table",
+CellProperties : "Cell Properties",
+TableProperties : "Table Properties",
+ImageProperties : "Image Properties",
+FlashProperties : "Flash Properties",
+
+AnchorProp : "Anchor Properties",
+ButtonProp : "Button Properties",
+CheckboxProp : "Checkbox Properties",
+HiddenFieldProp : "Hidden Field Properties",
+RadioButtonProp : "Radio Button Properties",
+ImageButtonProp : "Image Button Properties",
+TextFieldProp : "Text Field Properties",
+SelectionFieldProp : "Selection Field Properties",
+TextareaProp : "Textarea Properties",
+FormProp : "Form Properties",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Processing XHTML. Please wait...",
+Done : "Done",
+PasteWordConfirm : "The text you want to paste seems to be copied from Word. Do you want to clean it before pasting?",
+NotCompatiblePaste : "This command is available for Internet Explorer version 5.5 or more. Do you want to paste without cleaning?",
+UnknownToolbarItem : "Unknown toolbar item \"%1\"",
+UnknownCommand : "Unknown command name \"%1\"",
+NotImplemented : "Command not implemented",
+UnknownToolbarSet : "Toolbar set \"%1\" doesn't exist",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.",
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.",
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancel",
+DlgBtnClose : "Close",
+DlgBtnBrowseServer : "Browse Server",
+DlgAdvancedTag : "Advanced",
+DlgOpOther : "<Other>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Please insert the URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<not set>",
+DlgGenId : "Id",
+DlgGenLangDir : "Language Direction",
+DlgGenLangDirLtr : "Left to Right (LTR)",
+DlgGenLangDirRtl : "Right to Left (RTL)",
+DlgGenLangCode : "Language Code",
+DlgGenAccessKey : "Access Key",
+DlgGenName : "Name",
+DlgGenTabIndex : "Tab Index",
+DlgGenLongDescr : "Long Description URL",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "Advisory Title",
+DlgGenContType : "Advisory Content Type",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "Image Properties",
+DlgImgInfoTab : "Image Info",
+DlgImgBtnUpload : "Send it to the Server",
+DlgImgURL : "URL",
+DlgImgUpload : "Upload",
+DlgImgAlt : "Alternative Text",
+DlgImgWidth : "Width",
+DlgImgHeight : "Height",
+DlgImgLockRatio : "Lock Ratio",
+DlgBtnResetSize : "Reset Size",
+DlgImgBorder : "Border",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Align",
+DlgImgAlignLeft : "Left",
+DlgImgAlignAbsBottom: "Abs Bottom",
+DlgImgAlignAbsMiddle: "Abs Middle",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Bottom",
+DlgImgAlignMiddle : "Middle",
+DlgImgAlignRight : "Right",
+DlgImgAlignTextTop : "Text Top",
+DlgImgAlignTop : "Top",
+DlgImgPreview : "Preview",
+DlgImgAlertUrl : "Please type the image URL",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties",
+DlgFlashChkPlay : "Auto Play",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Enable Flash Menu",
+DlgFlashScale : "Scale",
+DlgFlashScaleAll : "Show all",
+DlgFlashScaleNoBorder : "No Border",
+DlgFlashScaleFit : "Exact Fit",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link Info",
+DlgLnkTargetTab : "Target",
+
+DlgLnkType : "Link Type",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Link to anchor in the text",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocol",
+DlgLnkProtoOther : "<other>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Select an Anchor",
+DlgLnkAnchorByName : "By Anchor Name",
+DlgLnkAnchorById : "By Element Id",
+DlgLnkNoAnchors : "(No anchors available in the document)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail Address",
+DlgLnkEMailSubject : "Message Subject",
+DlgLnkEMailBody : "Message Body",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Send it to the Server",
+
+DlgLnkTarget : "Target",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<popup window>",
+DlgLnkTargetBlank : "New Window (_blank)",
+DlgLnkTargetParent : "Parent Window (_parent)",
+DlgLnkTargetSelf : "Same Window (_self)",
+DlgLnkTargetTop : "Topmost Window (_top)",
+DlgLnkTargetFrameName : "Target Frame Name",
+DlgLnkPopWinName : "Popup Window Name",
+DlgLnkPopWinFeat : "Popup Window Features",
+DlgLnkPopResize : "Resizable",
+DlgLnkPopLocation : "Location Bar",
+DlgLnkPopMenu : "Menu Bar",
+DlgLnkPopScroll : "Scroll Bars",
+DlgLnkPopStatus : "Status Bar",
+DlgLnkPopToolbar : "Toolbar",
+DlgLnkPopFullScrn : "Full Screen (IE)",
+DlgLnkPopDependent : "Dependent (Netscape)",
+DlgLnkPopWidth : "Width",
+DlgLnkPopHeight : "Height",
+DlgLnkPopLeft : "Left Position",
+DlgLnkPopTop : "Top Position",
+
+DlnLnkMsgNoUrl : "Please type the link URL",
+DlnLnkMsgNoEMail : "Please type the e-mail address",
+DlnLnkMsgNoAnchor : "Please select an anchor",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces",
+
+// Color Dialog
+DlgColorTitle : "Select Colour",
+DlgColorBtnClear : "Clear",
+DlgColorHighlight : "Highlight",
+DlgColorSelected : "Selected",
+
+// Smiley Dialog
+DlgSmileyTitle : "Insert a Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Select Special Character",
+
+// Table Dialog
+DlgTableTitle : "Table Properties",
+DlgTableRows : "Rows",
+DlgTableColumns : "Columns",
+DlgTableBorder : "Border size",
+DlgTableAlign : "Alignment",
+DlgTableAlignNotSet : "<Not set>",
+DlgTableAlignLeft : "Left",
+DlgTableAlignCenter : "Centre",
+DlgTableAlignRight : "Right",
+DlgTableWidth : "Width",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "percent",
+DlgTableHeight : "Height",
+DlgTableCellSpace : "Cell spacing",
+DlgTableCellPad : "Cell padding",
+DlgTableCaption : "Caption",
+DlgTableSummary : "Summary",
+
+// Table Cell Dialog
+DlgCellTitle : "Cell Properties",
+DlgCellWidth : "Width",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "percent",
+DlgCellHeight : "Height",
+DlgCellWordWrap : "Word Wrap",
+DlgCellWordWrapNotSet : "<Not set>",
+DlgCellWordWrapYes : "Yes",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "Horizontal Alignment",
+DlgCellHorAlignNotSet : "<Not set>",
+DlgCellHorAlignLeft : "Left",
+DlgCellHorAlignCenter : "Centre",
+DlgCellHorAlignRight: "Right",
+DlgCellVerAlign : "Vertical Alignment",
+DlgCellVerAlignNotSet : "<Not set>",
+DlgCellVerAlignTop : "Top",
+DlgCellVerAlignMiddle : "Middle",
+DlgCellVerAlignBottom : "Bottom",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Rows Span",
+DlgCellCollSpan : "Columns Span",
+DlgCellBackColor : "Background Colour",
+DlgCellBorderColor : "Border Colour",
+DlgCellBtnSelect : "Select...",
+
+// Find Dialog
+DlgFindTitle : "Find",
+DlgFindFindBtn : "Find",
+DlgFindNotFoundMsg : "The specified text was not found.",
+
+// Replace Dialog
+DlgReplaceTitle : "Replace",
+DlgReplaceFindLbl : "Find what:",
+DlgReplaceReplaceLbl : "Replace with:",
+DlgReplaceCaseChk : "Match case",
+DlgReplaceReplaceBtn : "Replace",
+DlgReplaceReplAllBtn : "Replace All",
+DlgReplaceWordChk : "Match whole word",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Your browser security settings don't permit the editor to automatically execute cutting operations. Please use the keyboard for that (Ctrl+X).",
+PasteErrorCopy : "Your browser security settings don't permit the editor to automatically execute copying operations. Please use the keyboard for that (Ctrl+C).",
+
+PasteAsText : "Paste as Plain Text",
+PasteFromWord : "Paste from Word",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<STRONG>Ctrl+V</STRONG>) and hit <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.",
+DlgPasteIgnoreFont : "Ignore Font Face definitions",
+DlgPasteRemoveStyles : "Remove Styles definitions",
+DlgPasteCleanBox : "Clean Up Box",
+
+// Color Picker
+ColorAutomatic : "Automatic",
+ColorMoreColors : "More Colours...",
+
+// Document Properties
+DocProps : "Document Properties",
+
+// Anchor Dialog
+DlgAnchorTitle : "Anchor Properties",
+DlgAnchorName : "Anchor Name",
+DlgAnchorErrorName : "Please type the anchor name",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Not in dictionary",
+DlgSpellChangeTo : "Change to",
+DlgSpellBtnIgnore : "Ignore",
+DlgSpellBtnIgnoreAll : "Ignore All",
+DlgSpellBtnReplace : "Replace",
+DlgSpellBtnReplaceAll : "Replace All",
+DlgSpellBtnUndo : "Undo",
+DlgSpellNoSuggestions : "- No suggestions -",
+DlgSpellProgress : "Spell check in progress...",
+DlgSpellNoMispell : "Spell check complete: No misspellings found",
+DlgSpellNoChanges : "Spell check complete: No words changed",
+DlgSpellOneChange : "Spell check complete: One word changed",
+DlgSpellManyChanges : "Spell check complete: %1 words changed",
+
+IeSpellDownload : "Spell checker not installed. Do you want to download it now?",
+
+// Button Dialog
+DlgButtonText : "Text (Value)",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Button",
+DlgButtonTypeSbm : "Submit",
+DlgButtonTypeRst : "Reset",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Name",
+DlgCheckboxValue : "Value",
+DlgCheckboxSelected : "Selected",
+
+// Form Dialog
+DlgFormName : "Name",
+DlgFormAction : "Action",
+DlgFormMethod : "Method",
+
+// Select Field Dialog
+DlgSelectName : "Name",
+DlgSelectValue : "Value",
+DlgSelectSize : "Size",
+DlgSelectLines : "lines",
+DlgSelectChkMulti : "Allow multiple selections",
+DlgSelectOpAvail : "Available Options",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Value",
+DlgSelectBtnAdd : "Add",
+DlgSelectBtnModify : "Modify",
+DlgSelectBtnUp : "Up",
+DlgSelectBtnDown : "Down",
+DlgSelectBtnSetValue : "Set as selected value",
+DlgSelectBtnDelete : "Delete",
+
+// Textarea Dialog
+DlgTextareaName : "Name",
+DlgTextareaCols : "Columns",
+DlgTextareaRows : "Rows",
+
+// Text Field Dialog
+DlgTextName : "Name",
+DlgTextValue : "Value",
+DlgTextCharWidth : "Character Width",
+DlgTextMaxChars : "Maximum Characters",
+DlgTextType : "Type",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Password",
+
+// Hidden Field Dialog
+DlgHiddenName : "Name",
+DlgHiddenValue : "Value",
+
+// Bulleted List Dialog
+BulletedListProp : "Bulleted List Properties",
+NumberedListProp : "Numbered List Properties",
+DlgLstStart : "Start",
+DlgLstType : "Type",
+DlgLstTypeCircle : "Circle",
+DlgLstTypeDisc : "Disc",
+DlgLstTypeSquare : "Square",
+DlgLstTypeNumbers : "Numbers (1, 2, 3)",
+DlgLstTypeLCase : "Lowercase Letters (a, b, c)",
+DlgLstTypeUCase : "Uppercase Letters (A, B, C)",
+DlgLstTypeSRoman : "Small Roman Numerals (i, ii, iii)",
+DlgLstTypeLRoman : "Large Roman Numerals (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General",
+DlgDocBackTab : "Background",
+DlgDocColorsTab : "Colours and Margins",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Page Title",
+DlgDocLangDir : "Language Direction",
+DlgDocLangDirLTR : "Left to Right (LTR)",
+DlgDocLangDirRTL : "Right to Left (RTL)",
+DlgDocLangCode : "Language Code",
+DlgDocCharSet : "Character Set Encoding",
+DlgDocCharSetCE : "Central European",
+DlgDocCharSetCT : "Chinese Traditional (Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Greek",
+DlgDocCharSetJP : "Japanese",
+DlgDocCharSetKR : "Korean",
+DlgDocCharSetTR : "Turkish",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Western European",
+DlgDocCharSetOther : "Other Character Set Encoding",
+
+DlgDocDocType : "Document Type Heading",
+DlgDocDocTypeOther : "Other Document Type Heading",
+DlgDocIncXHTML : "Include XHTML Declarations",
+DlgDocBgColor : "Background Colour",
+DlgDocBgImage : "Background Image URL",
+DlgDocBgNoScroll : "Nonscrolling Background",
+DlgDocCText : "Text",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Visited Link",
+DlgDocCActive : "Active Link",
+DlgDocMargins : "Page Margins",
+DlgDocMaTop : "Top",
+DlgDocMaLeft : "Left",
+DlgDocMaRight : "Right",
+DlgDocMaBottom : "Bottom",
+DlgDocMeIndex : "Document Indexing Keywords (comma separated)",
+DlgDocMeDescr : "Document Description",
+DlgDocMeAuthor : "Author",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Preview",
+
+// Templates Dialog
+Templates : "Templates",
+DlgTemplatesTitle : "Content Templates",
+DlgTemplatesSelMsg : "Please select the template to open in the editor<br>(the actual contents will be lost):",
+DlgTemplatesLoading : "Loading templates list. Please wait...",
+DlgTemplatesNoTpl : "(No templates defined)",
+DlgTemplatesReplace : "Replace actual contents",
+
+// About Dialog
+DlgAboutAboutTab : "About",
+DlgAboutBrowserInfoTab : "Browser Info",
+DlgAboutLicenseTab : "License",
+DlgAboutVersion : "version",
+DlgAboutInfo : "For further information go to"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/en-uk.js b/httemplate/elements/fckeditor/editor/lang/en-uk.js
new file mode 100644
index 0000000..eaaf3e4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/en-uk.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * English (United Kingdom) language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Collapse Toolbar",
+ToolbarExpand : "Expand Toolbar",
+
+// Toolbar Items and Context Menu
+Save : "Save",
+NewPage : "New Page",
+Preview : "Preview",
+Cut : "Cut",
+Copy : "Copy",
+Paste : "Paste",
+PasteText : "Paste as plain text",
+PasteWord : "Paste from Word",
+Print : "Print",
+SelectAll : "Select All",
+RemoveFormat : "Remove Format",
+InsertLinkLbl : "Link",
+InsertLink : "Insert/Edit Link",
+RemoveLink : "Remove Link",
+Anchor : "Insert/Edit Anchor",
+InsertImageLbl : "Image",
+InsertImage : "Insert/Edit Image",
+InsertFlashLbl : "Flash",
+InsertFlash : "Insert/Edit Flash",
+InsertTableLbl : "Table",
+InsertTable : "Insert/Edit Table",
+InsertLineLbl : "Line",
+InsertLine : "Insert Horizontal Line",
+InsertSpecialCharLbl: "Special Character",
+InsertSpecialChar : "Insert Special Character",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Insert Smiley",
+About : "About FCKeditor",
+Bold : "Bold",
+Italic : "Italic",
+Underline : "Underline",
+StrikeThrough : "Strike Through",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Left Justify",
+CenterJustify : "Centre Justify",
+RightJustify : "Right Justify",
+BlockJustify : "Block Justify",
+DecreaseIndent : "Decrease Indent",
+IncreaseIndent : "Increase Indent",
+Undo : "Undo",
+Redo : "Redo",
+NumberedListLbl : "Numbered List",
+NumberedList : "Insert/Remove Numbered List",
+BulletedListLbl : "Bulleted List",
+BulletedList : "Insert/Remove Bulleted List",
+ShowTableBorders : "Show Table Borders",
+ShowDetails : "Show Details",
+Style : "Style",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "Size",
+TextColor : "Text Colour",
+BGColor : "Background Colour",
+Source : "Source",
+Find : "Find",
+Replace : "Replace",
+SpellCheck : "Check Spelling",
+UniversalKeyboard : "Universal Keyboard",
+PageBreakLbl : "Page Break",
+PageBreak : "Insert Page Break",
+
+Form : "Form",
+Checkbox : "Checkbox",
+RadioButton : "Radio Button",
+TextField : "Text Field",
+Textarea : "Textarea",
+HiddenField : "Hidden Field",
+Button : "Button",
+SelectionField : "Selection Field",
+ImageButton : "Image Button",
+
+FitWindow : "Maximize the editor size",
+
+// Context Menu
+EditLink : "Edit Link",
+CellCM : "Cell",
+RowCM : "Row",
+ColumnCM : "Column",
+InsertRow : "Insert Row",
+DeleteRows : "Delete Rows",
+InsertColumn : "Insert Column",
+DeleteColumns : "Delete Columns",
+InsertCell : "Insert Cell",
+DeleteCells : "Delete Cells",
+MergeCells : "Merge Cells",
+SplitCell : "Split Cell",
+TableDelete : "Delete Table",
+CellProperties : "Cell Properties",
+TableProperties : "Table Properties",
+ImageProperties : "Image Properties",
+FlashProperties : "Flash Properties",
+
+AnchorProp : "Anchor Properties",
+ButtonProp : "Button Properties",
+CheckboxProp : "Checkbox Properties",
+HiddenFieldProp : "Hidden Field Properties",
+RadioButtonProp : "Radio Button Properties",
+ImageButtonProp : "Image Button Properties",
+TextFieldProp : "Text Field Properties",
+SelectionFieldProp : "Selection Field Properties",
+TextareaProp : "Textarea Properties",
+FormProp : "Form Properties",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Processing XHTML. Please wait...",
+Done : "Done",
+PasteWordConfirm : "The text you want to paste seems to be copied from Word. Do you want to clean it before pasting?",
+NotCompatiblePaste : "This command is available for Internet Explorer version 5.5 or more. Do you want to paste without cleaning?",
+UnknownToolbarItem : "Unknown toolbar item \"%1\"",
+UnknownCommand : "Unknown command name \"%1\"",
+NotImplemented : "Command not implemented",
+UnknownToolbarSet : "Toolbar set \"%1\" doesn't exist",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.",
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.",
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancel",
+DlgBtnClose : "Close",
+DlgBtnBrowseServer : "Browse Server",
+DlgAdvancedTag : "Advanced",
+DlgOpOther : "<Other>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Please insert the URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<not set>",
+DlgGenId : "Id",
+DlgGenLangDir : "Language Direction",
+DlgGenLangDirLtr : "Left to Right (LTR)",
+DlgGenLangDirRtl : "Right to Left (RTL)",
+DlgGenLangCode : "Language Code",
+DlgGenAccessKey : "Access Key",
+DlgGenName : "Name",
+DlgGenTabIndex : "Tab Index",
+DlgGenLongDescr : "Long Description URL",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "Advisory Title",
+DlgGenContType : "Advisory Content Type",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "Image Properties",
+DlgImgInfoTab : "Image Info",
+DlgImgBtnUpload : "Send it to the Server",
+DlgImgURL : "URL",
+DlgImgUpload : "Upload",
+DlgImgAlt : "Alternative Text",
+DlgImgWidth : "Width",
+DlgImgHeight : "Height",
+DlgImgLockRatio : "Lock Ratio",
+DlgBtnResetSize : "Reset Size",
+DlgImgBorder : "Border",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Align",
+DlgImgAlignLeft : "Left",
+DlgImgAlignAbsBottom: "Abs Bottom",
+DlgImgAlignAbsMiddle: "Abs Middle",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Bottom",
+DlgImgAlignMiddle : "Middle",
+DlgImgAlignRight : "Right",
+DlgImgAlignTextTop : "Text Top",
+DlgImgAlignTop : "Top",
+DlgImgPreview : "Preview",
+DlgImgAlertUrl : "Please type the image URL",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties",
+DlgFlashChkPlay : "Auto Play",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Enable Flash Menu",
+DlgFlashScale : "Scale",
+DlgFlashScaleAll : "Show all",
+DlgFlashScaleNoBorder : "No Border",
+DlgFlashScaleFit : "Exact Fit",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link Info",
+DlgLnkTargetTab : "Target",
+
+DlgLnkType : "Link Type",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Link to anchor in the text",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocol",
+DlgLnkProtoOther : "<other>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Select an Anchor",
+DlgLnkAnchorByName : "By Anchor Name",
+DlgLnkAnchorById : "By Element Id",
+DlgLnkNoAnchors : "(No anchors available in the document)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail Address",
+DlgLnkEMailSubject : "Message Subject",
+DlgLnkEMailBody : "Message Body",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Send it to the Server",
+
+DlgLnkTarget : "Target",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<popup window>",
+DlgLnkTargetBlank : "New Window (_blank)",
+DlgLnkTargetParent : "Parent Window (_parent)",
+DlgLnkTargetSelf : "Same Window (_self)",
+DlgLnkTargetTop : "Topmost Window (_top)",
+DlgLnkTargetFrameName : "Target Frame Name",
+DlgLnkPopWinName : "Popup Window Name",
+DlgLnkPopWinFeat : "Popup Window Features",
+DlgLnkPopResize : "Resizable",
+DlgLnkPopLocation : "Location Bar",
+DlgLnkPopMenu : "Menu Bar",
+DlgLnkPopScroll : "Scroll Bars",
+DlgLnkPopStatus : "Status Bar",
+DlgLnkPopToolbar : "Toolbar",
+DlgLnkPopFullScrn : "Full Screen (IE)",
+DlgLnkPopDependent : "Dependent (Netscape)",
+DlgLnkPopWidth : "Width",
+DlgLnkPopHeight : "Height",
+DlgLnkPopLeft : "Left Position",
+DlgLnkPopTop : "Top Position",
+
+DlnLnkMsgNoUrl : "Please type the link URL",
+DlnLnkMsgNoEMail : "Please type the e-mail address",
+DlnLnkMsgNoAnchor : "Please select an anchor",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces",
+
+// Color Dialog
+DlgColorTitle : "Select Colour",
+DlgColorBtnClear : "Clear",
+DlgColorHighlight : "Highlight",
+DlgColorSelected : "Selected",
+
+// Smiley Dialog
+DlgSmileyTitle : "Insert a Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Select Special Character",
+
+// Table Dialog
+DlgTableTitle : "Table Properties",
+DlgTableRows : "Rows",
+DlgTableColumns : "Columns",
+DlgTableBorder : "Border size",
+DlgTableAlign : "Alignment",
+DlgTableAlignNotSet : "<Not set>",
+DlgTableAlignLeft : "Left",
+DlgTableAlignCenter : "Centre",
+DlgTableAlignRight : "Right",
+DlgTableWidth : "Width",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "percent",
+DlgTableHeight : "Height",
+DlgTableCellSpace : "Cell spacing",
+DlgTableCellPad : "Cell padding",
+DlgTableCaption : "Caption",
+DlgTableSummary : "Summary",
+
+// Table Cell Dialog
+DlgCellTitle : "Cell Properties",
+DlgCellWidth : "Width",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "percent",
+DlgCellHeight : "Height",
+DlgCellWordWrap : "Word Wrap",
+DlgCellWordWrapNotSet : "<Not set>",
+DlgCellWordWrapYes : "Yes",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "Horizontal Alignment",
+DlgCellHorAlignNotSet : "<Not set>",
+DlgCellHorAlignLeft : "Left",
+DlgCellHorAlignCenter : "Centre",
+DlgCellHorAlignRight: "Right",
+DlgCellVerAlign : "Vertical Alignment",
+DlgCellVerAlignNotSet : "<Not set>",
+DlgCellVerAlignTop : "Top",
+DlgCellVerAlignMiddle : "Middle",
+DlgCellVerAlignBottom : "Bottom",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Rows Span",
+DlgCellCollSpan : "Columns Span",
+DlgCellBackColor : "Background Colour",
+DlgCellBorderColor : "Border Colour",
+DlgCellBtnSelect : "Select...",
+
+// Find Dialog
+DlgFindTitle : "Find",
+DlgFindFindBtn : "Find",
+DlgFindNotFoundMsg : "The specified text was not found.",
+
+// Replace Dialog
+DlgReplaceTitle : "Replace",
+DlgReplaceFindLbl : "Find what:",
+DlgReplaceReplaceLbl : "Replace with:",
+DlgReplaceCaseChk : "Match case",
+DlgReplaceReplaceBtn : "Replace",
+DlgReplaceReplAllBtn : "Replace All",
+DlgReplaceWordChk : "Match whole word",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Your browser security settings don't permit the editor to automatically execute cutting operations. Please use the keyboard for that (Ctrl+X).",
+PasteErrorCopy : "Your browser security settings don't permit the editor to automatically execute copying operations. Please use the keyboard for that (Ctrl+C).",
+
+PasteAsText : "Paste as Plain Text",
+PasteFromWord : "Paste from Word",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<STRONG>Ctrl+V</STRONG>) and hit <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.",
+DlgPasteIgnoreFont : "Ignore Font Face definitions",
+DlgPasteRemoveStyles : "Remove Styles definitions",
+DlgPasteCleanBox : "Clean Up Box",
+
+// Color Picker
+ColorAutomatic : "Automatic",
+ColorMoreColors : "More Colours...",
+
+// Document Properties
+DocProps : "Document Properties",
+
+// Anchor Dialog
+DlgAnchorTitle : "Anchor Properties",
+DlgAnchorName : "Anchor Name",
+DlgAnchorErrorName : "Please type the anchor name",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Not in dictionary",
+DlgSpellChangeTo : "Change to",
+DlgSpellBtnIgnore : "Ignore",
+DlgSpellBtnIgnoreAll : "Ignore All",
+DlgSpellBtnReplace : "Replace",
+DlgSpellBtnReplaceAll : "Replace All",
+DlgSpellBtnUndo : "Undo",
+DlgSpellNoSuggestions : "- No suggestions -",
+DlgSpellProgress : "Spell check in progress...",
+DlgSpellNoMispell : "Spell check complete: No misspellings found",
+DlgSpellNoChanges : "Spell check complete: No words changed",
+DlgSpellOneChange : "Spell check complete: One word changed",
+DlgSpellManyChanges : "Spell check complete: %1 words changed",
+
+IeSpellDownload : "Spell checker not installed. Do you want to download it now?",
+
+// Button Dialog
+DlgButtonText : "Text (Value)",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Button",
+DlgButtonTypeSbm : "Submit",
+DlgButtonTypeRst : "Reset",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Name",
+DlgCheckboxValue : "Value",
+DlgCheckboxSelected : "Selected",
+
+// Form Dialog
+DlgFormName : "Name",
+DlgFormAction : "Action",
+DlgFormMethod : "Method",
+
+// Select Field Dialog
+DlgSelectName : "Name",
+DlgSelectValue : "Value",
+DlgSelectSize : "Size",
+DlgSelectLines : "lines",
+DlgSelectChkMulti : "Allow multiple selections",
+DlgSelectOpAvail : "Available Options",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Value",
+DlgSelectBtnAdd : "Add",
+DlgSelectBtnModify : "Modify",
+DlgSelectBtnUp : "Up",
+DlgSelectBtnDown : "Down",
+DlgSelectBtnSetValue : "Set as selected value",
+DlgSelectBtnDelete : "Delete",
+
+// Textarea Dialog
+DlgTextareaName : "Name",
+DlgTextareaCols : "Columns",
+DlgTextareaRows : "Rows",
+
+// Text Field Dialog
+DlgTextName : "Name",
+DlgTextValue : "Value",
+DlgTextCharWidth : "Character Width",
+DlgTextMaxChars : "Maximum Characters",
+DlgTextType : "Type",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Password",
+
+// Hidden Field Dialog
+DlgHiddenName : "Name",
+DlgHiddenValue : "Value",
+
+// Bulleted List Dialog
+BulletedListProp : "Bulleted List Properties",
+NumberedListProp : "Numbered List Properties",
+DlgLstStart : "Start",
+DlgLstType : "Type",
+DlgLstTypeCircle : "Circle",
+DlgLstTypeDisc : "Disc",
+DlgLstTypeSquare : "Square",
+DlgLstTypeNumbers : "Numbers (1, 2, 3)",
+DlgLstTypeLCase : "Lowercase Letters (a, b, c)",
+DlgLstTypeUCase : "Uppercase Letters (A, B, C)",
+DlgLstTypeSRoman : "Small Roman Numerals (i, ii, iii)",
+DlgLstTypeLRoman : "Large Roman Numerals (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General",
+DlgDocBackTab : "Background",
+DlgDocColorsTab : "Colours and Margins",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Page Title",
+DlgDocLangDir : "Language Direction",
+DlgDocLangDirLTR : "Left to Right (LTR)",
+DlgDocLangDirRTL : "Right to Left (RTL)",
+DlgDocLangCode : "Language Code",
+DlgDocCharSet : "Character Set Encoding",
+DlgDocCharSetCE : "Central European",
+DlgDocCharSetCT : "Chinese Traditional (Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Greek",
+DlgDocCharSetJP : "Japanese",
+DlgDocCharSetKR : "Korean",
+DlgDocCharSetTR : "Turkish",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Western European",
+DlgDocCharSetOther : "Other Character Set Encoding",
+
+DlgDocDocType : "Document Type Heading",
+DlgDocDocTypeOther : "Other Document Type Heading",
+DlgDocIncXHTML : "Include XHTML Declarations",
+DlgDocBgColor : "Background Colour",
+DlgDocBgImage : "Background Image URL",
+DlgDocBgNoScroll : "Nonscrolling Background",
+DlgDocCText : "Text",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Visited Link",
+DlgDocCActive : "Active Link",
+DlgDocMargins : "Page Margins",
+DlgDocMaTop : "Top",
+DlgDocMaLeft : "Left",
+DlgDocMaRight : "Right",
+DlgDocMaBottom : "Bottom",
+DlgDocMeIndex : "Document Indexing Keywords (comma separated)",
+DlgDocMeDescr : "Document Description",
+DlgDocMeAuthor : "Author",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Preview",
+
+// Templates Dialog
+Templates : "Templates",
+DlgTemplatesTitle : "Content Templates",
+DlgTemplatesSelMsg : "Please select the template to open in the editor<br>(the actual contents will be lost):",
+DlgTemplatesLoading : "Loading templates list. Please wait...",
+DlgTemplatesNoTpl : "(No templates defined)",
+DlgTemplatesReplace : "Replace actual contents",
+
+// About Dialog
+DlgAboutAboutTab : "About",
+DlgAboutBrowserInfoTab : "Browser Info",
+DlgAboutLicenseTab : "License",
+DlgAboutVersion : "version",
+DlgAboutInfo : "For further information go to"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/en.js b/httemplate/elements/fckeditor/editor/lang/en.js
new file mode 100644
index 0000000..c579fc9
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/en.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * English language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Collapse Toolbar",
+ToolbarExpand : "Expand Toolbar",
+
+// Toolbar Items and Context Menu
+Save : "Save",
+NewPage : "New Page",
+Preview : "Preview",
+Cut : "Cut",
+Copy : "Copy",
+Paste : "Paste",
+PasteText : "Paste as plain text",
+PasteWord : "Paste from Word",
+Print : "Print",
+SelectAll : "Select All",
+RemoveFormat : "Remove Format",
+InsertLinkLbl : "Link",
+InsertLink : "Insert/Edit Link",
+RemoveLink : "Remove Link",
+Anchor : "Insert/Edit Anchor",
+InsertImageLbl : "Image",
+InsertImage : "Insert/Edit Image",
+InsertFlashLbl : "Flash",
+InsertFlash : "Insert/Edit Flash",
+InsertTableLbl : "Table",
+InsertTable : "Insert/Edit Table",
+InsertLineLbl : "Line",
+InsertLine : "Insert Horizontal Line",
+InsertSpecialCharLbl: "Special Character",
+InsertSpecialChar : "Insert Special Character",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Insert Smiley",
+About : "About FCKeditor",
+Bold : "Bold",
+Italic : "Italic",
+Underline : "Underline",
+StrikeThrough : "Strike Through",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Left Justify",
+CenterJustify : "Center Justify",
+RightJustify : "Right Justify",
+BlockJustify : "Block Justify",
+DecreaseIndent : "Decrease Indent",
+IncreaseIndent : "Increase Indent",
+Undo : "Undo",
+Redo : "Redo",
+NumberedListLbl : "Numbered List",
+NumberedList : "Insert/Remove Numbered List",
+BulletedListLbl : "Bulleted List",
+BulletedList : "Insert/Remove Bulleted List",
+ShowTableBorders : "Show Table Borders",
+ShowDetails : "Show Details",
+Style : "Style",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "Size",
+TextColor : "Text Color",
+BGColor : "Background Color",
+Source : "Source",
+Find : "Find",
+Replace : "Replace",
+SpellCheck : "Check Spelling",
+UniversalKeyboard : "Universal Keyboard",
+PageBreakLbl : "Page Break",
+PageBreak : "Insert Page Break",
+
+Form : "Form",
+Checkbox : "Checkbox",
+RadioButton : "Radio Button",
+TextField : "Text Field",
+Textarea : "Textarea",
+HiddenField : "Hidden Field",
+Button : "Button",
+SelectionField : "Selection Field",
+ImageButton : "Image Button",
+
+FitWindow : "Maximize the editor size",
+
+// Context Menu
+EditLink : "Edit Link",
+CellCM : "Cell",
+RowCM : "Row",
+ColumnCM : "Column",
+InsertRow : "Insert Row",
+DeleteRows : "Delete Rows",
+InsertColumn : "Insert Column",
+DeleteColumns : "Delete Columns",
+InsertCell : "Insert Cell",
+DeleteCells : "Delete Cells",
+MergeCells : "Merge Cells",
+SplitCell : "Split Cell",
+TableDelete : "Delete Table",
+CellProperties : "Cell Properties",
+TableProperties : "Table Properties",
+ImageProperties : "Image Properties",
+FlashProperties : "Flash Properties",
+
+AnchorProp : "Anchor Properties",
+ButtonProp : "Button Properties",
+CheckboxProp : "Checkbox Properties",
+HiddenFieldProp : "Hidden Field Properties",
+RadioButtonProp : "Radio Button Properties",
+ImageButtonProp : "Image Button Properties",
+TextFieldProp : "Text Field Properties",
+SelectionFieldProp : "Selection Field Properties",
+TextareaProp : "Textarea Properties",
+FormProp : "Form Properties",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Processing XHTML. Please wait...",
+Done : "Done",
+PasteWordConfirm : "The text you want to paste seems to be copied from Word. Do you want to clean it before pasting?",
+NotCompatiblePaste : "This command is available for Internet Explorer version 5.5 or more. Do you want to paste without cleaning?",
+UnknownToolbarItem : "Unknown toolbar item \"%1\"",
+UnknownCommand : "Unknown command name \"%1\"",
+NotImplemented : "Command not implemented",
+UnknownToolbarSet : "Toolbar set \"%1\" doesn't exist",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.",
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.",
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancel",
+DlgBtnClose : "Close",
+DlgBtnBrowseServer : "Browse Server",
+DlgAdvancedTag : "Advanced",
+DlgOpOther : "<Other>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Please insert the URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<not set>",
+DlgGenId : "Id",
+DlgGenLangDir : "Language Direction",
+DlgGenLangDirLtr : "Left to Right (LTR)",
+DlgGenLangDirRtl : "Right to Left (RTL)",
+DlgGenLangCode : "Language Code",
+DlgGenAccessKey : "Access Key",
+DlgGenName : "Name",
+DlgGenTabIndex : "Tab Index",
+DlgGenLongDescr : "Long Description URL",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "Advisory Title",
+DlgGenContType : "Advisory Content Type",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "Image Properties",
+DlgImgInfoTab : "Image Info",
+DlgImgBtnUpload : "Send it to the Server",
+DlgImgURL : "URL",
+DlgImgUpload : "Upload",
+DlgImgAlt : "Alternative Text",
+DlgImgWidth : "Width",
+DlgImgHeight : "Height",
+DlgImgLockRatio : "Lock Ratio",
+DlgBtnResetSize : "Reset Size",
+DlgImgBorder : "Border",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Align",
+DlgImgAlignLeft : "Left",
+DlgImgAlignAbsBottom: "Abs Bottom",
+DlgImgAlignAbsMiddle: "Abs Middle",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Bottom",
+DlgImgAlignMiddle : "Middle",
+DlgImgAlignRight : "Right",
+DlgImgAlignTextTop : "Text Top",
+DlgImgAlignTop : "Top",
+DlgImgPreview : "Preview",
+DlgImgAlertUrl : "Please type the image URL",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties",
+DlgFlashChkPlay : "Auto Play",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Enable Flash Menu",
+DlgFlashScale : "Scale",
+DlgFlashScaleAll : "Show all",
+DlgFlashScaleNoBorder : "No Border",
+DlgFlashScaleFit : "Exact Fit",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link Info",
+DlgLnkTargetTab : "Target",
+
+DlgLnkType : "Link Type",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Link to anchor in the text",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocol",
+DlgLnkProtoOther : "<other>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Select an Anchor",
+DlgLnkAnchorByName : "By Anchor Name",
+DlgLnkAnchorById : "By Element Id",
+DlgLnkNoAnchors : "(No anchors available in the document)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail Address",
+DlgLnkEMailSubject : "Message Subject",
+DlgLnkEMailBody : "Message Body",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Send it to the Server",
+
+DlgLnkTarget : "Target",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<popup window>",
+DlgLnkTargetBlank : "New Window (_blank)",
+DlgLnkTargetParent : "Parent Window (_parent)",
+DlgLnkTargetSelf : "Same Window (_self)",
+DlgLnkTargetTop : "Topmost Window (_top)",
+DlgLnkTargetFrameName : "Target Frame Name",
+DlgLnkPopWinName : "Popup Window Name",
+DlgLnkPopWinFeat : "Popup Window Features",
+DlgLnkPopResize : "Resizable",
+DlgLnkPopLocation : "Location Bar",
+DlgLnkPopMenu : "Menu Bar",
+DlgLnkPopScroll : "Scroll Bars",
+DlgLnkPopStatus : "Status Bar",
+DlgLnkPopToolbar : "Toolbar",
+DlgLnkPopFullScrn : "Full Screen (IE)",
+DlgLnkPopDependent : "Dependent (Netscape)",
+DlgLnkPopWidth : "Width",
+DlgLnkPopHeight : "Height",
+DlgLnkPopLeft : "Left Position",
+DlgLnkPopTop : "Top Position",
+
+DlnLnkMsgNoUrl : "Please type the link URL",
+DlnLnkMsgNoEMail : "Please type the e-mail address",
+DlnLnkMsgNoAnchor : "Please select an anchor",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces",
+
+// Color Dialog
+DlgColorTitle : "Select Color",
+DlgColorBtnClear : "Clear",
+DlgColorHighlight : "Highlight",
+DlgColorSelected : "Selected",
+
+// Smiley Dialog
+DlgSmileyTitle : "Insert a Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Select Special Character",
+
+// Table Dialog
+DlgTableTitle : "Table Properties",
+DlgTableRows : "Rows",
+DlgTableColumns : "Columns",
+DlgTableBorder : "Border size",
+DlgTableAlign : "Alignment",
+DlgTableAlignNotSet : "<Not set>",
+DlgTableAlignLeft : "Left",
+DlgTableAlignCenter : "Center",
+DlgTableAlignRight : "Right",
+DlgTableWidth : "Width",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "percent",
+DlgTableHeight : "Height",
+DlgTableCellSpace : "Cell spacing",
+DlgTableCellPad : "Cell padding",
+DlgTableCaption : "Caption",
+DlgTableSummary : "Summary",
+
+// Table Cell Dialog
+DlgCellTitle : "Cell Properties",
+DlgCellWidth : "Width",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "percent",
+DlgCellHeight : "Height",
+DlgCellWordWrap : "Word Wrap",
+DlgCellWordWrapNotSet : "<Not set>",
+DlgCellWordWrapYes : "Yes",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "Horizontal Alignment",
+DlgCellHorAlignNotSet : "<Not set>",
+DlgCellHorAlignLeft : "Left",
+DlgCellHorAlignCenter : "Center",
+DlgCellHorAlignRight: "Right",
+DlgCellVerAlign : "Vertical Alignment",
+DlgCellVerAlignNotSet : "<Not set>",
+DlgCellVerAlignTop : "Top",
+DlgCellVerAlignMiddle : "Middle",
+DlgCellVerAlignBottom : "Bottom",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Rows Span",
+DlgCellCollSpan : "Columns Span",
+DlgCellBackColor : "Background Color",
+DlgCellBorderColor : "Border Color",
+DlgCellBtnSelect : "Select...",
+
+// Find Dialog
+DlgFindTitle : "Find",
+DlgFindFindBtn : "Find",
+DlgFindNotFoundMsg : "The specified text was not found.",
+
+// Replace Dialog
+DlgReplaceTitle : "Replace",
+DlgReplaceFindLbl : "Find what:",
+DlgReplaceReplaceLbl : "Replace with:",
+DlgReplaceCaseChk : "Match case",
+DlgReplaceReplaceBtn : "Replace",
+DlgReplaceReplAllBtn : "Replace All",
+DlgReplaceWordChk : "Match whole word",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Your browser security settings don't permit the editor to automatically execute cutting operations. Please use the keyboard for that (Ctrl+X).",
+PasteErrorCopy : "Your browser security settings don't permit the editor to automatically execute copying operations. Please use the keyboard for that (Ctrl+C).",
+
+PasteAsText : "Paste as Plain Text",
+PasteFromWord : "Paste from Word",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<strong>Ctrl+V</strong>) and hit <strong>OK</strong>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.",
+DlgPasteIgnoreFont : "Ignore Font Face definitions",
+DlgPasteRemoveStyles : "Remove Styles definitions",
+DlgPasteCleanBox : "Clean Up Box",
+
+// Color Picker
+ColorAutomatic : "Automatic",
+ColorMoreColors : "More Colors...",
+
+// Document Properties
+DocProps : "Document Properties",
+
+// Anchor Dialog
+DlgAnchorTitle : "Anchor Properties",
+DlgAnchorName : "Anchor Name",
+DlgAnchorErrorName : "Please type the anchor name",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Not in dictionary",
+DlgSpellChangeTo : "Change to",
+DlgSpellBtnIgnore : "Ignore",
+DlgSpellBtnIgnoreAll : "Ignore All",
+DlgSpellBtnReplace : "Replace",
+DlgSpellBtnReplaceAll : "Replace All",
+DlgSpellBtnUndo : "Undo",
+DlgSpellNoSuggestions : "- No suggestions -",
+DlgSpellProgress : "Spell check in progress...",
+DlgSpellNoMispell : "Spell check complete: No misspellings found",
+DlgSpellNoChanges : "Spell check complete: No words changed",
+DlgSpellOneChange : "Spell check complete: One word changed",
+DlgSpellManyChanges : "Spell check complete: %1 words changed",
+
+IeSpellDownload : "Spell checker not installed. Do you want to download it now?",
+
+// Button Dialog
+DlgButtonText : "Text (Value)",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Button",
+DlgButtonTypeSbm : "Submit",
+DlgButtonTypeRst : "Reset",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Name",
+DlgCheckboxValue : "Value",
+DlgCheckboxSelected : "Selected",
+
+// Form Dialog
+DlgFormName : "Name",
+DlgFormAction : "Action",
+DlgFormMethod : "Method",
+
+// Select Field Dialog
+DlgSelectName : "Name",
+DlgSelectValue : "Value",
+DlgSelectSize : "Size",
+DlgSelectLines : "lines",
+DlgSelectChkMulti : "Allow multiple selections",
+DlgSelectOpAvail : "Available Options",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Value",
+DlgSelectBtnAdd : "Add",
+DlgSelectBtnModify : "Modify",
+DlgSelectBtnUp : "Up",
+DlgSelectBtnDown : "Down",
+DlgSelectBtnSetValue : "Set as selected value",
+DlgSelectBtnDelete : "Delete",
+
+// Textarea Dialog
+DlgTextareaName : "Name",
+DlgTextareaCols : "Columns",
+DlgTextareaRows : "Rows",
+
+// Text Field Dialog
+DlgTextName : "Name",
+DlgTextValue : "Value",
+DlgTextCharWidth : "Character Width",
+DlgTextMaxChars : "Maximum Characters",
+DlgTextType : "Type",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Password",
+
+// Hidden Field Dialog
+DlgHiddenName : "Name",
+DlgHiddenValue : "Value",
+
+// Bulleted List Dialog
+BulletedListProp : "Bulleted List Properties",
+NumberedListProp : "Numbered List Properties",
+DlgLstStart : "Start",
+DlgLstType : "Type",
+DlgLstTypeCircle : "Circle",
+DlgLstTypeDisc : "Disc",
+DlgLstTypeSquare : "Square",
+DlgLstTypeNumbers : "Numbers (1, 2, 3)",
+DlgLstTypeLCase : "Lowercase Letters (a, b, c)",
+DlgLstTypeUCase : "Uppercase Letters (A, B, C)",
+DlgLstTypeSRoman : "Small Roman Numerals (i, ii, iii)",
+DlgLstTypeLRoman : "Large Roman Numerals (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General",
+DlgDocBackTab : "Background",
+DlgDocColorsTab : "Colors and Margins",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Page Title",
+DlgDocLangDir : "Language Direction",
+DlgDocLangDirLTR : "Left to Right (LTR)",
+DlgDocLangDirRTL : "Right to Left (RTL)",
+DlgDocLangCode : "Language Code",
+DlgDocCharSet : "Character Set Encoding",
+DlgDocCharSetCE : "Central European",
+DlgDocCharSetCT : "Chinese Traditional (Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Greek",
+DlgDocCharSetJP : "Japanese",
+DlgDocCharSetKR : "Korean",
+DlgDocCharSetTR : "Turkish",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Western European",
+DlgDocCharSetOther : "Other Character Set Encoding",
+
+DlgDocDocType : "Document Type Heading",
+DlgDocDocTypeOther : "Other Document Type Heading",
+DlgDocIncXHTML : "Include XHTML Declarations",
+DlgDocBgColor : "Background Color",
+DlgDocBgImage : "Background Image URL",
+DlgDocBgNoScroll : "Nonscrolling Background",
+DlgDocCText : "Text",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Visited Link",
+DlgDocCActive : "Active Link",
+DlgDocMargins : "Page Margins",
+DlgDocMaTop : "Top",
+DlgDocMaLeft : "Left",
+DlgDocMaRight : "Right",
+DlgDocMaBottom : "Bottom",
+DlgDocMeIndex : "Document Indexing Keywords (comma separated)",
+DlgDocMeDescr : "Document Description",
+DlgDocMeAuthor : "Author",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Preview",
+
+// Templates Dialog
+Templates : "Templates",
+DlgTemplatesTitle : "Content Templates",
+DlgTemplatesSelMsg : "Please select the template to open in the editor<br />(the actual contents will be lost):",
+DlgTemplatesLoading : "Loading templates list. Please wait...",
+DlgTemplatesNoTpl : "(No templates defined)",
+DlgTemplatesReplace : "Replace actual contents",
+
+// About Dialog
+DlgAboutAboutTab : "About",
+DlgAboutBrowserInfoTab : "Browser Info",
+DlgAboutLicenseTab : "License",
+DlgAboutVersion : "version",
+DlgAboutInfo : "For further information go to"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/eo.js b/httemplate/elements/fckeditor/editor/lang/eo.js
new file mode 100644
index 0000000..04f7364
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/eo.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Esperanto language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "KaÅi Ilobreton",
+ToolbarExpand : "Vidigi Ilojn",
+
+// Toolbar Items and Context Menu
+Save : "Sekurigi",
+NewPage : "Nova PaÄo",
+Preview : "Vidigi Aspekton",
+Cut : "Eltondi",
+Copy : "Kopii",
+Paste : "Interglui",
+PasteText : "Interglui kiel Tekston",
+PasteWord : "Interglui el Word",
+Print : "Presi",
+SelectAll : "Elekti ĉion",
+RemoveFormat : "Forigi Formaton",
+InsertLinkLbl : "Ligilo",
+InsertLink : "Enmeti/ÅœanÄi Ligilon",
+RemoveLink : "Forigi Ligilon",
+Anchor : "Enmeti/ÅœanÄi Ankron",
+InsertImageLbl : "Bildo",
+InsertImage : "Enmeti/ÅœanÄi Bildon",
+InsertFlashLbl : "Flash", //MISSING
+InsertFlash : "Insert/Edit Flash", //MISSING
+InsertTableLbl : "Tabelo",
+InsertTable : "Enmeti/ÅœanÄi Tabelon",
+InsertLineLbl : "Horizonta Linio",
+InsertLine : "Enmeti Horizonta Linio",
+InsertSpecialCharLbl: "Speciala Signo",
+InsertSpecialChar : "Enmeti Specialan Signon",
+InsertSmileyLbl : "Mienvinjeto",
+InsertSmiley : "Enmeti Mienvinjeton",
+About : "Pri FCKeditor",
+Bold : "Grasa",
+Italic : "Kursiva",
+Underline : "Substreko",
+StrikeThrough : "Trastreko",
+Subscript : "Subskribo",
+Superscript : "Superskribo",
+LeftJustify : "Maldekstrigi",
+CenterJustify : "Centrigi",
+RightJustify : "Dekstrigi",
+BlockJustify : "Äœisrandigi AmbaÅ­flanke",
+DecreaseIndent : "Malpligrandigi KrommarÄenon",
+IncreaseIndent : "Pligrandigi KrommarÄenon",
+Undo : "Malfari",
+Redo : "Refari",
+NumberedListLbl : "Numera Listo",
+NumberedList : "Enmeti/Forigi Numeran Liston",
+BulletedListLbl : "Bula Listo",
+BulletedList : "Enmeti/Forigi Bulan Liston",
+ShowTableBorders : "Vidigi Borderojn de Tabelo",
+ShowDetails : "Vidigi Detalojn",
+Style : "Stilo",
+FontFormat : "Formato",
+Font : "Tiparo",
+FontSize : "Grando",
+TextColor : "Teksta Koloro",
+BGColor : "Fona Koloro",
+Source : "Fonto",
+Find : "Serĉi",
+Replace : "AnstataÅ­igi",
+SpellCheck : "Literumada Kontrolilo",
+UniversalKeyboard : "Universala Klavaro",
+PageBreakLbl : "Page Break", //MISSING
+PageBreak : "Insert Page Break", //MISSING
+
+Form : "Formularo",
+Checkbox : "Markobutono",
+RadioButton : "Radiobutono",
+TextField : "Teksta kampo",
+Textarea : "Teksta Areo",
+HiddenField : "KaÅita Kampo",
+Button : "Butono",
+SelectionField : "Elekta Kampo",
+ImageButton : "Bildbutono",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Modifier Ligilon",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Enmeti Linion",
+DeleteRows : "Forigi Liniojn",
+InsertColumn : "Enmeti Kolumnon",
+DeleteColumns : "Forigi Kolumnojn",
+InsertCell : "Enmeti Ĉelon",
+DeleteCells : "Forigi Ĉelojn",
+MergeCells : "Kunfandi Ĉelojn",
+SplitCell : "Dividi Ĉelojn",
+TableDelete : "Delete Table", //MISSING
+CellProperties : "Atributoj de Ĉelo",
+TableProperties : "Atributoj de Tabelo",
+ImageProperties : "Atributoj de Bildo",
+FlashProperties : "Flash Properties", //MISSING
+
+AnchorProp : "Ankraj Atributoj",
+ButtonProp : "Butonaj Atributoj",
+CheckboxProp : "Markobutonaj Atributoj",
+HiddenFieldProp : "Atributoj de KaÅita Kampo",
+RadioButtonProp : "Radiobutonaj Atributoj",
+ImageButtonProp : "Bildbutonaj Atributoj",
+TextFieldProp : "Atributoj de Teksta Kampo",
+SelectionFieldProp : "Atributoj de Elekta Kampo",
+TextareaProp : "Atributoj de Teksta Areo",
+FormProp : "Formularaj Atributoj",
+
+FontFormats : "Normala;Formatita;Adreso;Titolo 1;Titolo 2;Titolo 3;Titolo 4;Titolo 5;Titolo 6;Paragrafo (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Traktado de XHTML. Bonvolu pacienci...",
+Done : "Finita",
+PasteWordConfirm : "La algluota teksto Åajnas esti Word-devena. Ĉu vi volas purigi Äin antaÅ­ ol interglui?",
+NotCompatiblePaste : "Tiu ĉi komando bezonas almenaŭ Internet Explorer 5.5. Ĉu vi volas daŭrigi sen purigado?",
+UnknownToolbarItem : "Ilobretero nekonata \"%1\"",
+UnknownCommand : "Komandonomo nekonata \"%1\"",
+NotImplemented : "Komando ne ankoraÅ­ realigita",
+UnknownToolbarSet : "La ilobreto \"%1\" ne ekzistas",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "Akcepti",
+DlgBtnCancel : "Rezigni",
+DlgBtnClose : "Fermi",
+DlgBtnBrowseServer : "Foliumi en la Servilo",
+DlgAdvancedTag : "Speciala",
+DlgOpOther : "<Alia>",
+DlgInfoTab : "Info", //MISSING
+DlgAlertUrl : "Please insert the URL", //MISSING
+
+// General Dialogs Labels
+DlgGenNotSet : "<DefaÅ­lta>",
+DlgGenId : "Id",
+DlgGenLangDir : "Skribdirekto",
+DlgGenLangDirLtr : "De maldekstro dekstren (LTR)",
+DlgGenLangDirRtl : "De dekstro maldekstren (RTL)",
+DlgGenLangCode : "Lingva Kodo",
+DlgGenAccessKey : "Fulmoklavo",
+DlgGenName : "Nomo",
+DlgGenTabIndex : "Taba Ordo",
+DlgGenLongDescr : "URL de Longa Priskribo",
+DlgGenClass : "Klasoj de Stilfolioj",
+DlgGenTitle : "Indika Titolo",
+DlgGenContType : "Indika Enhavotipo",
+DlgGenLinkCharset : "Signaro de la Ligita Rimedo",
+DlgGenStyle : "Stilo",
+
+// Image Dialog
+DlgImgTitle : "Atributoj de Bildo",
+DlgImgInfoTab : "Informoj pri Bildo",
+DlgImgBtnUpload : "Sendu al Servilo",
+DlgImgURL : "URL",
+DlgImgUpload : "AlÅuti",
+DlgImgAlt : "AnstataÅ­iga Teksto",
+DlgImgWidth : "LarÄo",
+DlgImgHeight : "Alto",
+DlgImgLockRatio : "Konservi Proporcion",
+DlgBtnResetSize : "Origina Grando",
+DlgImgBorder : "Bordero",
+DlgImgHSpace : "HSpaco",
+DlgImgVSpace : "VSpaco",
+DlgImgAlign : "Äœisrandigo",
+DlgImgAlignLeft : "Maldekstre",
+DlgImgAlignAbsBottom: "Abs Malsupre",
+DlgImgAlignAbsMiddle: "Abs Centre",
+DlgImgAlignBaseline : "Je Malsupro de Teksto",
+DlgImgAlignBottom : "Malsupre",
+DlgImgAlignMiddle : "Centre",
+DlgImgAlignRight : "Dekstre",
+DlgImgAlignTextTop : "Je Supro de Teksto",
+DlgImgAlignTop : "Supre",
+DlgImgPreview : "Vidigi Aspekton",
+DlgImgAlertUrl : "Bonvolu tajpi la URL de la bildo",
+DlgImgLinkTab : "Link", //MISSING
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties", //MISSING
+DlgFlashChkPlay : "Auto Play", //MISSING
+DlgFlashChkLoop : "Loop", //MISSING
+DlgFlashChkMenu : "Enable Flash Menu", //MISSING
+DlgFlashScale : "Scale", //MISSING
+DlgFlashScaleAll : "Show all", //MISSING
+DlgFlashScaleNoBorder : "No Border", //MISSING
+DlgFlashScaleFit : "Exact Fit", //MISSING
+
+// Link Dialog
+DlgLnkWindowTitle : "Ligilo",
+DlgLnkInfoTab : "Informoj pri la Ligilo",
+DlgLnkTargetTab : "Celo",
+
+DlgLnkType : "Tipo de Ligilo",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Ankri en tiu ĉi paÄo",
+DlgLnkTypeEMail : "RetpoÅto",
+DlgLnkProto : "Protokolo",
+DlgLnkProtoOther : "<alia>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Elekti Ankron",
+DlgLnkAnchorByName : "Per Ankronomo",
+DlgLnkAnchorById : "Per Elementidentigilo",
+DlgLnkNoAnchors : "<Ne disponeblas ankroj en la dokumento>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Retadreso",
+DlgLnkEMailSubject : "Temlinio",
+DlgLnkEMailBody : "MesaÄa korpo",
+DlgLnkUpload : "AlÅuti",
+DlgLnkBtnUpload : "Sendi al Servilo",
+
+DlgLnkTarget : "Celo",
+DlgLnkTargetFrame : "<kadro>",
+DlgLnkTargetPopup : "<Åprucfenestro>",
+DlgLnkTargetBlank : "Nova Fenestro (_blank)",
+DlgLnkTargetParent : "Gepatra Fenestro (_parent)",
+DlgLnkTargetSelf : "Sama Fenestro (_self)",
+DlgLnkTargetTop : "Plej Supra Fenestro (_top)",
+DlgLnkTargetFrameName : "Nomo de Kadro",
+DlgLnkPopWinName : "Nomo de Åœprucfenestro",
+DlgLnkPopWinFeat : "Atributoj de la Åœprucfenestro",
+DlgLnkPopResize : "Grando ÅœanÄebla",
+DlgLnkPopLocation : "Adresobreto",
+DlgLnkPopMenu : "Menubreto",
+DlgLnkPopScroll : "Rulumlisteloj",
+DlgLnkPopStatus : "Statobreto",
+DlgLnkPopToolbar : "Ilobreto",
+DlgLnkPopFullScrn : "Tutekrane (IE)",
+DlgLnkPopDependent : "Dependa (Netscape)",
+DlgLnkPopWidth : "LarÄo",
+DlgLnkPopHeight : "Alto",
+DlgLnkPopLeft : "Pozicio de Maldekstro",
+DlgLnkPopTop : "Pozicio de Supro",
+
+DlnLnkMsgNoUrl : "Bonvolu entajpi la URL-on",
+DlnLnkMsgNoEMail : "Bonvolu entajpi la retadreson",
+DlnLnkMsgNoAnchor : "Bonvolu elekti ankron",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Elekti",
+DlgColorBtnClear : "Forigi",
+DlgColorHighlight : "Emfazi",
+DlgColorSelected : "Elektita",
+
+// Smiley Dialog
+DlgSmileyTitle : "Enmeti Mienvinjeton",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Enmeti Specialan Signon",
+
+// Table Dialog
+DlgTableTitle : "Atributoj de Tabelo",
+DlgTableRows : "Linioj",
+DlgTableColumns : "Kolumnoj",
+DlgTableBorder : "Bordero",
+DlgTableAlign : "Äœisrandigo",
+DlgTableAlignNotSet : "<DefaÅ­lte>",
+DlgTableAlignLeft : "Maldekstre",
+DlgTableAlignCenter : "Centre",
+DlgTableAlignRight : "Dekstre",
+DlgTableWidth : "LarÄo",
+DlgTableWidthPx : "Bitbilderoj",
+DlgTableWidthPc : "elcentoj",
+DlgTableHeight : "Alto",
+DlgTableCellSpace : "Interspacigo de Ĉeloj",
+DlgTableCellPad : "Ĉirkaŭenhava Plenigado",
+DlgTableCaption : "Titolo",
+DlgTableSummary : "Summary", //MISSING
+
+// Table Cell Dialog
+DlgCellTitle : "Atributoj de Celo",
+DlgCellWidth : "LarÄo",
+DlgCellWidthPx : "bitbilderoj",
+DlgCellWidthPc : "elcentoj",
+DlgCellHeight : "Alto",
+DlgCellWordWrap : "Linifaldo",
+DlgCellWordWrapNotSet : "<DefaÅ­lte>",
+DlgCellWordWrapYes : "Jes",
+DlgCellWordWrapNo : "Ne",
+DlgCellHorAlign : "Horizonta Äœisrandigo",
+DlgCellHorAlignNotSet : "<DefaÅ­lte>",
+DlgCellHorAlignLeft : "Maldekstre",
+DlgCellHorAlignCenter : "Centre",
+DlgCellHorAlignRight: "Dekstre",
+DlgCellVerAlign : "Vertikala Äœisrandigo",
+DlgCellVerAlignNotSet : "<DefaÅ­lte>",
+DlgCellVerAlignTop : "Supre",
+DlgCellVerAlignMiddle : "Centre",
+DlgCellVerAlignBottom : "Malsupre",
+DlgCellVerAlignBaseline : "Je Malsupro de Teksto",
+DlgCellRowSpan : "Linioj Kunfanditaj",
+DlgCellCollSpan : "Kolumnoj Kunfanditaj",
+DlgCellBackColor : "Fono",
+DlgCellBorderColor : "Bordero",
+DlgCellBtnSelect : "Elekti...",
+
+// Find Dialog
+DlgFindTitle : "Serĉi",
+DlgFindFindBtn : "Serĉi",
+DlgFindNotFoundMsg : "La celteksto ne estas trovita.",
+
+// Replace Dialog
+DlgReplaceTitle : "AnstataÅ­igi",
+DlgReplaceFindLbl : "Serĉi:",
+DlgReplaceReplaceLbl : "AnstataÅ­igi per:",
+DlgReplaceCaseChk : "Kongruigi Usklecon",
+DlgReplaceReplaceBtn : "AnstataÅ­igi",
+DlgReplaceReplAllBtn : "Anstataŭigi Ĉiun",
+DlgReplaceWordChk : "Tuta Vorto",
+
+// Paste Operations / Dialog
+PasteErrorCut : "La sekurecagordo de via TTT-legilo ne permesas, ke la redaktilo faras eltondajn operaciojn. Bonvolu uzi la klavaron por tio (ctrl-X).",
+PasteErrorCopy : "La sekurecagordo de via TTT-legilo ne permesas, ke la redaktilo faras kopiajn operaciojn. Bonvolu uzi la klavaron por tio (ctrl-C).",
+
+PasteAsText : "Interglui kiel Tekston",
+PasteFromWord : "Interglui el Word",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<strong>Ctrl+V</strong>) and hit <strong>OK</strong>.", //MISSING
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignore Font Face definitions", //MISSING
+DlgPasteRemoveStyles : "Remove Styles definitions", //MISSING
+DlgPasteCleanBox : "Clean Up Box", //MISSING
+
+// Color Picker
+ColorAutomatic : "AÅ­tomata",
+ColorMoreColors : "Pli da Koloroj...",
+
+// Document Properties
+DocProps : "Dokumentaj Atributoj",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ankraj Atributoj",
+DlgAnchorName : "Ankra Nomo",
+DlgAnchorErrorName : "Bv tajpi la ankran nomon",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ne trovita en la vortaro",
+DlgSpellChangeTo : "ÅœanÄi al",
+DlgSpellBtnIgnore : "Malatenti",
+DlgSpellBtnIgnoreAll : "Malatenti Ĉiun",
+DlgSpellBtnReplace : "AnstataÅ­igi",
+DlgSpellBtnReplaceAll : "Anstataŭigi Ĉiun",
+DlgSpellBtnUndo : "Malfari",
+DlgSpellNoSuggestions : "- Neniu propono -",
+DlgSpellProgress : "Literumkontrolado daÅ­ras...",
+DlgSpellNoMispell : "Literumkontrolado finita: neniu fuÅo trovita",
+DlgSpellNoChanges : "Literumkontrolado finita: neniu vorto ÅanÄita",
+DlgSpellOneChange : "Literumkontrolado finita: unu vorto ÅanÄita",
+DlgSpellManyChanges : "Literumkontrolado finita: %1 vortoj ÅanÄitaj",
+
+IeSpellDownload : "Literumada Kontrolilo ne instalita. Ĉu vi volas elÅuti Äin nun?",
+
+// Button Dialog
+DlgButtonText : "Teksto (Valoro)",
+DlgButtonType : "Tipo",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nomo",
+DlgCheckboxValue : "Valoro",
+DlgCheckboxSelected : "Elektita",
+
+// Form Dialog
+DlgFormName : "Nomo",
+DlgFormAction : "Ago",
+DlgFormMethod : "Metodo",
+
+// Select Field Dialog
+DlgSelectName : "Nomo",
+DlgSelectValue : "Valoro",
+DlgSelectSize : "Grando",
+DlgSelectLines : "Linioj",
+DlgSelectChkMulti : "Permesi Plurajn Elektojn",
+DlgSelectOpAvail : "Elektoj Disponeblaj",
+DlgSelectOpText : "Teksto",
+DlgSelectOpValue : "Valoro",
+DlgSelectBtnAdd : "Aldoni",
+DlgSelectBtnModify : "Modifi",
+DlgSelectBtnUp : "Supren",
+DlgSelectBtnDown : "Malsupren",
+DlgSelectBtnSetValue : "Agordi kiel Elektitan Valoron",
+DlgSelectBtnDelete : "Forigi",
+
+// Textarea Dialog
+DlgTextareaName : "Nomo",
+DlgTextareaCols : "Kolumnoj",
+DlgTextareaRows : "Vicoj",
+
+// Text Field Dialog
+DlgTextName : "Nomo",
+DlgTextValue : "Valoro",
+DlgTextCharWidth : "SignolarÄo",
+DlgTextMaxChars : "Maksimuma Nombro da Signoj",
+DlgTextType : "Tipo",
+DlgTextTypeText : "Teksto",
+DlgTextTypePass : "Pasvorto",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nomo",
+DlgHiddenValue : "Valoro",
+
+// Bulleted List Dialog
+BulletedListProp : "Atributoj de Bula Listo",
+NumberedListProp : "Atributoj de Numera Listo",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tipo",
+DlgLstTypeCircle : "Cirklo",
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "Kvadrato",
+DlgLstTypeNumbers : "Ciferoj (1, 2, 3)",
+DlgLstTypeLCase : "Minusklaj Literoj (a, b, c)",
+DlgLstTypeUCase : "Majusklaj Literoj (A, B, C)",
+DlgLstTypeSRoman : "Malgrandaj Romanaj Ciferoj (i, ii, iii)",
+DlgLstTypeLRoman : "Grandaj Romanaj Ciferoj (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Ĝeneralaĵoj",
+DlgDocBackTab : "Fono",
+DlgDocColorsTab : "Koloroj kaj MarÄenoj",
+DlgDocMetaTab : "Metadatumoj",
+
+DlgDocPageTitle : "PaÄotitolo",
+DlgDocLangDir : "Skribdirekto de la Lingvo",
+DlgDocLangDirLTR : "De maldekstro dekstren (LTR)",
+DlgDocLangDirRTL : "De dekstro maldekstren (LTR)",
+DlgDocLangCode : "Lingvokodo",
+DlgDocCharSet : "Signara Kodo",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Alia Signara Kodo",
+
+DlgDocDocType : "Dokumenta Tipo",
+DlgDocDocTypeOther : "Alia Dokumenta Tipo",
+DlgDocIncXHTML : "Inkluzivi XHTML Deklaroj",
+DlgDocBgColor : "Fona Koloro",
+DlgDocBgImage : "URL de Fona Bildo",
+DlgDocBgNoScroll : "Neruluma Fono",
+DlgDocCText : "Teksto",
+DlgDocCLink : "Ligilo",
+DlgDocCVisited : "Vizitita Ligilo",
+DlgDocCActive : "Aktiva Ligilo",
+DlgDocMargins : "PaÄaj MarÄenoj",
+DlgDocMaTop : "Supra",
+DlgDocMaLeft : "Maldekstra",
+DlgDocMaRight : "Dekstra",
+DlgDocMaBottom : "Malsupra",
+DlgDocMeIndex : "Åœlosilvortoj de la Dokumento (apartigita de komoj)",
+DlgDocMeDescr : "Dokumenta Priskribo",
+DlgDocMeAuthor : "Verkinto",
+DlgDocMeCopy : "Kopirajto",
+DlgDocPreview : "Aspekto",
+
+// Templates Dialog
+Templates : "Templates", //MISSING
+DlgTemplatesTitle : "Content Templates", //MISSING
+DlgTemplatesSelMsg : "Please select the template to open in the editor<br />(the actual contents will be lost):", //MISSING
+DlgTemplatesLoading : "Loading templates list. Please wait...", //MISSING
+DlgTemplatesNoTpl : "(No templates defined)", //MISSING
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Pri",
+DlgAboutBrowserInfoTab : "Informoj pri TTT-legilo",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "versio",
+DlgAboutInfo : "Por pli da informoj, vizitu"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/es.js b/httemplate/elements/fckeditor/editor/lang/es.js
new file mode 100644
index 0000000..cfed808
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/es.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Spanish language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Contraer Barra",
+ToolbarExpand : "Expandir Barra",
+
+// Toolbar Items and Context Menu
+Save : "Guardar",
+NewPage : "Nueva Página",
+Preview : "Vista Previa",
+Cut : "Cortar",
+Copy : "Copiar",
+Paste : "Pegar",
+PasteText : "Pegar como texto plano",
+PasteWord : "Pegar desde Word",
+Print : "Imprimir",
+SelectAll : "Seleccionar Todo",
+RemoveFormat : "Eliminar Formato",
+InsertLinkLbl : "Vínculo",
+InsertLink : "Insertar/Editar Vínculo",
+RemoveLink : "Eliminar Vínculo",
+Anchor : "Referencia",
+InsertImageLbl : "Imagen",
+InsertImage : "Insertar/Editar Imagen",
+InsertFlashLbl : "Flash",
+InsertFlash : "Insertar/Editar Flash",
+InsertTableLbl : "Tabla",
+InsertTable : "Insertar/Editar Tabla",
+InsertLineLbl : "Línea",
+InsertLine : "Insertar Línea Horizontal",
+InsertSpecialCharLbl: "Caracter Especial",
+InsertSpecialChar : "Insertar Caracter Especial",
+InsertSmileyLbl : "Emoticons",
+InsertSmiley : "Insertar Emoticons",
+About : "Acerca de FCKeditor",
+Bold : "Negrita",
+Italic : "Cursiva",
+Underline : "Subrayado",
+StrikeThrough : "Tachado",
+Subscript : "Subíndice",
+Superscript : "Superíndice",
+LeftJustify : "Alinear a Izquierda",
+CenterJustify : "Centrar",
+RightJustify : "Alinear a Derecha",
+BlockJustify : "Justificado",
+DecreaseIndent : "Disminuir Sangría",
+IncreaseIndent : "Aumentar Sangría",
+Undo : "Deshacer",
+Redo : "Rehacer",
+NumberedListLbl : "Numeración",
+NumberedList : "Insertar/Eliminar Numeración",
+BulletedListLbl : "Viñetas",
+BulletedList : "Insertar/Eliminar Viñetas",
+ShowTableBorders : "Mostrar Bordes de Tablas",
+ShowDetails : "Mostrar saltos de Párrafo",
+Style : "Estilo",
+FontFormat : "Formato",
+Font : "Fuente",
+FontSize : "Tamaño",
+TextColor : "Color de Texto",
+BGColor : "Color de Fondo",
+Source : "Fuente HTML",
+Find : "Buscar",
+Replace : "Reemplazar",
+SpellCheck : "Ortografía",
+UniversalKeyboard : "Teclado Universal",
+PageBreakLbl : "Salto de Página",
+PageBreak : "Insertar Salto de Página",
+
+Form : "Formulario",
+Checkbox : "Casilla de Verificación",
+RadioButton : "Botones de Radio",
+TextField : "Campo de Texto",
+Textarea : "Area de Texto",
+HiddenField : "Campo Oculto",
+Button : "Botón",
+SelectionField : "Campo de Selección",
+ImageButton : "Botón Imagen",
+
+FitWindow : "Maximizar el tamaño del editor",
+
+// Context Menu
+EditLink : "Editar Vínculo",
+CellCM : "Celda",
+RowCM : "Fila",
+ColumnCM : "Columna",
+InsertRow : "Insertar Fila",
+DeleteRows : "Eliminar Filas",
+InsertColumn : "Insertar Columna",
+DeleteColumns : "Eliminar Columnas",
+InsertCell : "Insertar Celda",
+DeleteCells : "Eliminar Celdas",
+MergeCells : "Combinar Celdas",
+SplitCell : "Dividir Celda",
+TableDelete : "Eliminar Tabla",
+CellProperties : "Propiedades de Celda",
+TableProperties : "Propiedades de Tabla",
+ImageProperties : "Propiedades de Imagen",
+FlashProperties : "Propiedades de Flash",
+
+AnchorProp : "Propiedades de Referencia",
+ButtonProp : "Propiedades de Botón",
+CheckboxProp : "Propiedades de Casilla",
+HiddenFieldProp : "Propiedades de Campo Oculto",
+RadioButtonProp : "Propiedades de Botón de Radio",
+ImageButtonProp : "Propiedades de Botón de Imagen",
+TextFieldProp : "Propiedades de Campo de Texto",
+SelectionFieldProp : "Propiedades de Campo de Selección",
+TextareaProp : "Propiedades de Area de Texto",
+FormProp : "Propiedades de Formulario",
+
+FontFormats : "Normal;Con formato;Dirección;Encabezado 1;Encabezado 2;Encabezado 3;Encabezado 4;Encabezado 5;Encabezado 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Procesando XHTML. Por favor, espere...",
+Done : "Hecho",
+PasteWordConfirm : "El texto que desea parece provenir de Word. Desea depurarlo antes de pegarlo?",
+NotCompatiblePaste : "Este comando está disponible sólo para Internet Explorer version 5.5 or superior. Desea pegar sin depurar?",
+UnknownToolbarItem : "Item de barra desconocido \"%1\"",
+UnknownCommand : "Nombre de comando desconocido \"%1\"",
+NotImplemented : "Comando no implementado",
+UnknownToolbarSet : "Nombre de barra \"%1\" no definido",
+NoActiveX : "La configuración de las opciones de seguridad de su navegador puede estar limitando algunas características del editor. Por favor active la opción \"Ejecutar controles y complementos de ActiveX \", de lo contrario puede experimentar errores o ausencia de funcionalidades.",
+BrowseServerBlocked : "La ventana de visualización del servidor no pudo ser abierta. Verifique que su navegador no esté bloqueando las ventanas emergentes (pop up).",
+DialogBlocked : "No se ha podido abrir la ventana de diálogo. Verifique que su navegador no esté bloqueando las ventanas emergentes (pop up).",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancelar",
+DlgBtnClose : "Cerrar",
+DlgBtnBrowseServer : "Ver Servidor",
+DlgAdvancedTag : "Avanzado",
+DlgOpOther : "<Otro>",
+DlgInfoTab : "Información",
+DlgAlertUrl : "Inserte el URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<No definido>",
+DlgGenId : "Id",
+DlgGenLangDir : "Orientación de idioma",
+DlgGenLangDirLtr : "Izquierda a Derecha (LTR)",
+DlgGenLangDirRtl : "Derecha a Izquierda (RTL)",
+DlgGenLangCode : "Código de idioma",
+DlgGenAccessKey : "Clave de Acceso",
+DlgGenName : "Nombre",
+DlgGenTabIndex : "Indice de tabulación",
+DlgGenLongDescr : "Descripción larga URL",
+DlgGenClass : "Clases de hojas de estilo",
+DlgGenTitle : "Título",
+DlgGenContType : "Tipo de Contenido",
+DlgGenLinkCharset : "Fuente de caracteres vinculado",
+DlgGenStyle : "Estilo",
+
+// Image Dialog
+DlgImgTitle : "Propiedades de Imagen",
+DlgImgInfoTab : "Información de Imagen",
+DlgImgBtnUpload : "Enviar al Servidor",
+DlgImgURL : "URL",
+DlgImgUpload : "Cargar",
+DlgImgAlt : "Texto Alternativo",
+DlgImgWidth : "Anchura",
+DlgImgHeight : "Altura",
+DlgImgLockRatio : "Proporcional",
+DlgBtnResetSize : "Tamaño Original",
+DlgImgBorder : "Borde",
+DlgImgHSpace : "Esp.Horiz",
+DlgImgVSpace : "Esp.Vert",
+DlgImgAlign : "Alineación",
+DlgImgAlignLeft : "Izquierda",
+DlgImgAlignAbsBottom: "Abs inferior",
+DlgImgAlignAbsMiddle: "Abs centro",
+DlgImgAlignBaseline : "Línea de base",
+DlgImgAlignBottom : "Pie",
+DlgImgAlignMiddle : "Centro",
+DlgImgAlignRight : "Derecha",
+DlgImgAlignTextTop : "Tope del texto",
+DlgImgAlignTop : "Tope",
+DlgImgPreview : "Vista Previa",
+DlgImgAlertUrl : "Por favor tipee el URL de la imagen",
+DlgImgLinkTab : "Vínculo",
+
+// Flash Dialog
+DlgFlashTitle : "Propiedades de Flash",
+DlgFlashChkPlay : "Autoejecución",
+DlgFlashChkLoop : "Repetir",
+DlgFlashChkMenu : "Activar Menú Flash",
+DlgFlashScale : "Escala",
+DlgFlashScaleAll : "Mostrar todo",
+DlgFlashScaleNoBorder : "Sin Borde",
+DlgFlashScaleFit : "Ajustado",
+
+// Link Dialog
+DlgLnkWindowTitle : "Vínculo",
+DlgLnkInfoTab : "Información de Vínculo",
+DlgLnkTargetTab : "Destino",
+
+DlgLnkType : "Tipo de vínculo",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Referencia en esta página",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocolo",
+DlgLnkProtoOther : "<otro>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Seleccionar una referencia",
+DlgLnkAnchorByName : "Por Nombre de Referencia",
+DlgLnkAnchorById : "Por ID de elemento",
+DlgLnkNoAnchors : "<No hay referencias disponibles en el documento>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Dirección de E-Mail",
+DlgLnkEMailSubject : "Título del Mensaje",
+DlgLnkEMailBody : "Cuerpo del Mensaje",
+DlgLnkUpload : "Cargar",
+DlgLnkBtnUpload : "Enviar al Servidor",
+
+DlgLnkTarget : "Destino",
+DlgLnkTargetFrame : "<marco>",
+DlgLnkTargetPopup : "<ventana emergente>",
+DlgLnkTargetBlank : "Nueva Ventana(_blank)",
+DlgLnkTargetParent : "Ventana Padre (_parent)",
+DlgLnkTargetSelf : "Misma Ventana (_self)",
+DlgLnkTargetTop : "Ventana primaria (_top)",
+DlgLnkTargetFrameName : "Nombre del Marco Destino",
+DlgLnkPopWinName : "Nombre de Ventana Emergente",
+DlgLnkPopWinFeat : "Características de Ventana Emergente",
+DlgLnkPopResize : "Ajustable",
+DlgLnkPopLocation : "Barra de ubicación",
+DlgLnkPopMenu : "Barra de Menú",
+DlgLnkPopScroll : "Barras de desplazamiento",
+DlgLnkPopStatus : "Barra de Estado",
+DlgLnkPopToolbar : "Barra de Herramientas",
+DlgLnkPopFullScrn : "Pantalla Completa (IE)",
+DlgLnkPopDependent : "Dependiente (Netscape)",
+DlgLnkPopWidth : "Anchura",
+DlgLnkPopHeight : "Altura",
+DlgLnkPopLeft : "Posición Izquierda",
+DlgLnkPopTop : "Posición Derecha",
+
+DlnLnkMsgNoUrl : "Por favor tipee el vínculo URL",
+DlnLnkMsgNoEMail : "Por favor tipee la dirección de e-mail",
+DlnLnkMsgNoAnchor : "Por favor seleccione una referencia",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Seleccionar Color",
+DlgColorBtnClear : "Ninguno",
+DlgColorHighlight : "Resaltado",
+DlgColorSelected : "Seleccionado",
+
+// Smiley Dialog
+DlgSmileyTitle : "Insertar un Emoticon",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Seleccione un caracter especial",
+
+// Table Dialog
+DlgTableTitle : "Propiedades de Tabla",
+DlgTableRows : "Filas",
+DlgTableColumns : "Columnas",
+DlgTableBorder : "Tamaño de Borde",
+DlgTableAlign : "Alineación",
+DlgTableAlignNotSet : "<No establecido>",
+DlgTableAlignLeft : "Izquierda",
+DlgTableAlignCenter : "Centrado",
+DlgTableAlignRight : "Derecha",
+DlgTableWidth : "Anchura",
+DlgTableWidthPx : "pixeles",
+DlgTableWidthPc : "porcentaje",
+DlgTableHeight : "Altura",
+DlgTableCellSpace : "Esp. e/celdas",
+DlgTableCellPad : "Esp. interior",
+DlgTableCaption : "Título",
+DlgTableSummary : "Síntesis",
+
+// Table Cell Dialog
+DlgCellTitle : "Propiedades de Celda",
+DlgCellWidth : "Anchura",
+DlgCellWidthPx : "pixeles",
+DlgCellWidthPc : "porcentaje",
+DlgCellHeight : "Altura",
+DlgCellWordWrap : "Cortar Línea",
+DlgCellWordWrapNotSet : "<No establecido>",
+DlgCellWordWrapYes : "Si",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "Alineación Horizontal",
+DlgCellHorAlignNotSet : "<No establecido>",
+DlgCellHorAlignLeft : "Izquierda",
+DlgCellHorAlignCenter : "Centrado",
+DlgCellHorAlignRight: "Derecha",
+DlgCellVerAlign : "Alineación Vertical",
+DlgCellVerAlignNotSet : "<Not establecido>",
+DlgCellVerAlignTop : "Tope",
+DlgCellVerAlignMiddle : "Medio",
+DlgCellVerAlignBottom : "ie",
+DlgCellVerAlignBaseline : "Línea de Base",
+DlgCellRowSpan : "Abarcar Filas",
+DlgCellCollSpan : "Abarcar Columnas",
+DlgCellBackColor : "Color de Fondo",
+DlgCellBorderColor : "Color de Borde",
+DlgCellBtnSelect : "Seleccione...",
+
+// Find Dialog
+DlgFindTitle : "Buscar",
+DlgFindFindBtn : "Buscar",
+DlgFindNotFoundMsg : "El texto especificado no ha sido encontrado.",
+
+// Replace Dialog
+DlgReplaceTitle : "Reemplazar",
+DlgReplaceFindLbl : "Texto a buscar:",
+DlgReplaceReplaceLbl : "Reemplazar con:",
+DlgReplaceCaseChk : "Coincidir may/min",
+DlgReplaceReplaceBtn : "Reemplazar",
+DlgReplaceReplAllBtn : "Reemplazar Todo",
+DlgReplaceWordChk : "Coincidir toda la palabra",
+
+// Paste Operations / Dialog
+PasteErrorCut : "La configuración de seguridad de este navegador no permite la ejecución automática de operaciones de cortado. Por favor use el teclado (Ctrl+X).",
+PasteErrorCopy : "La configuración de seguridad de este navegador no permite la ejecución automática de operaciones de copiado. Por favor use el teclado (Ctrl+C).",
+
+PasteAsText : "Pegar como Texto Plano",
+PasteFromWord : "Pegar desde Word",
+
+DlgPasteMsg2 : "Por favor pegue dentro del cuadro utilizando el teclado (<STRONG>Ctrl+V</STRONG>); luego presione <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorar definiciones de fuentes",
+DlgPasteRemoveStyles : "Remover definiciones de estilo",
+DlgPasteCleanBox : "Borrar el contenido del cuadro",
+
+// Color Picker
+ColorAutomatic : "Automático",
+ColorMoreColors : "Más Colores...",
+
+// Document Properties
+DocProps : "Propiedades del Documento",
+
+// Anchor Dialog
+DlgAnchorTitle : "Propiedades de la Referencia",
+DlgAnchorName : "Nombre de la Referencia",
+DlgAnchorErrorName : "Por favor, complete el nombre de la Referencia",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "No se encuentra en el Diccionario",
+DlgSpellChangeTo : "Cambiar a",
+DlgSpellBtnIgnore : "Ignorar",
+DlgSpellBtnIgnoreAll : "Ignorar Todo",
+DlgSpellBtnReplace : "Reemplazar",
+DlgSpellBtnReplaceAll : "Reemplazar Todo",
+DlgSpellBtnUndo : "Deshacer",
+DlgSpellNoSuggestions : "- No hay sugerencias -",
+DlgSpellProgress : "Control de Ortografía en progreso...",
+DlgSpellNoMispell : "Control finalizado: no se encontraron errores",
+DlgSpellNoChanges : "Control finalizado: no se ha cambiado ninguna palabra",
+DlgSpellOneChange : "Control finalizado: se ha cambiado una palabra",
+DlgSpellManyChanges : "Control finalizado: se ha cambiado %1 palabras",
+
+IeSpellDownload : "Módulo de Control de Ortografía no instalado. ¿Desea descargarlo ahora?",
+
+// Button Dialog
+DlgButtonText : "Texto (Valor)",
+DlgButtonType : "Tipo",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nombre",
+DlgCheckboxValue : "Valor",
+DlgCheckboxSelected : "Seleccionado",
+
+// Form Dialog
+DlgFormName : "Nombre",
+DlgFormAction : "Acción",
+DlgFormMethod : "Método",
+
+// Select Field Dialog
+DlgSelectName : "Nombre",
+DlgSelectValue : "Valor",
+DlgSelectSize : "Tamaño",
+DlgSelectLines : "Lineas",
+DlgSelectChkMulti : "Permitir múltiple selección",
+DlgSelectOpAvail : "Opciones disponibles",
+DlgSelectOpText : "Texto",
+DlgSelectOpValue : "Valor",
+DlgSelectBtnAdd : "Agregar",
+DlgSelectBtnModify : "Modificar",
+DlgSelectBtnUp : "Subir",
+DlgSelectBtnDown : "Bajar",
+DlgSelectBtnSetValue : "Establecer como predeterminado",
+DlgSelectBtnDelete : "Eliminar",
+
+// Textarea Dialog
+DlgTextareaName : "Nombre",
+DlgTextareaCols : "Columnas",
+DlgTextareaRows : "Filas",
+
+// Text Field Dialog
+DlgTextName : "Nombre",
+DlgTextValue : "Valor",
+DlgTextCharWidth : "Caracteres de ancho",
+DlgTextMaxChars : "Máximo caracteres",
+DlgTextType : "Tipo",
+DlgTextTypeText : "Texto",
+DlgTextTypePass : "Contraseña",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nombre",
+DlgHiddenValue : "Valor",
+
+// Bulleted List Dialog
+BulletedListProp : "Propiedades de Viñetas",
+NumberedListProp : "Propiedades de Numeraciones",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tipo",
+DlgLstTypeCircle : "Círculo",
+DlgLstTypeDisc : "Disco",
+DlgLstTypeSquare : "Cuadrado",
+DlgLstTypeNumbers : "Números (1, 2, 3)",
+DlgLstTypeLCase : "letras en minúsculas (a, b, c)",
+DlgLstTypeUCase : "letras en mayúsculas (A, B, C)",
+DlgLstTypeSRoman : "Números Romanos (i, ii, iii)",
+DlgLstTypeLRoman : "Números Romanos (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General",
+DlgDocBackTab : "Fondo",
+DlgDocColorsTab : "Colores y Márgenes",
+DlgDocMetaTab : "Meta Información",
+
+DlgDocPageTitle : "Título de Página",
+DlgDocLangDir : "Orientación de idioma",
+DlgDocLangDirLTR : "Izq. a Derecha (LTR)",
+DlgDocLangDirRTL : "Der. a Izquierda (RTL)",
+DlgDocLangCode : "Código de Idioma",
+DlgDocCharSet : "Codif. de Conjunto de Caracteres",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Otra Codificación",
+
+DlgDocDocType : "Encabezado de Tipo de Documento",
+DlgDocDocTypeOther : "Otro Encabezado",
+DlgDocIncXHTML : "Incluir Declaraciones XHTML",
+DlgDocBgColor : "Color de Fondo",
+DlgDocBgImage : "URL de Imagen de Fondo",
+DlgDocBgNoScroll : "Fondo sin rolido",
+DlgDocCText : "Texto",
+DlgDocCLink : "Vínculo",
+DlgDocCVisited : "Vínculo Visitado",
+DlgDocCActive : "Vínculo Activo",
+DlgDocMargins : "Márgenes de Página",
+DlgDocMaTop : "Tope",
+DlgDocMaLeft : "Izquierda",
+DlgDocMaRight : "Derecha",
+DlgDocMaBottom : "Pie",
+DlgDocMeIndex : "Claves de indexación del Documento (separados por comas)",
+DlgDocMeDescr : "Descripción del Documento",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Vista Previa",
+
+// Templates Dialog
+Templates : "Plantillas",
+DlgTemplatesTitle : "Contenido de Plantillas",
+DlgTemplatesSelMsg : "Por favor selecciona la plantilla a abrir en el editor<br>(el contenido actual se perderá):",
+DlgTemplatesLoading : "Cargando lista de Plantillas. Por favor, aguarde...",
+DlgTemplatesNoTpl : "(No hay plantillas definidas)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Acerca de",
+DlgAboutBrowserInfoTab : "Información de Navegador",
+DlgAboutLicenseTab : "Licencia",
+DlgAboutVersion : "versión",
+DlgAboutInfo : "Para mayor información por favor dirigirse a"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/et.js b/httemplate/elements/fckeditor/editor/lang/et.js
new file mode 100644
index 0000000..53a147e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/et.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Estonian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Voldi tööriistariba",
+ToolbarExpand : "Laienda tööriistariba",
+
+// Toolbar Items and Context Menu
+Save : "Salvesta",
+NewPage : "Uus leht",
+Preview : "Eelvaade",
+Cut : "Lõika",
+Copy : "Kopeeri",
+Paste : "Kleebi",
+PasteText : "Kleebi tavalise tekstina",
+PasteWord : "Kleebi Wordist",
+Print : "Prindi",
+SelectAll : "Vali kõik",
+RemoveFormat : "Eemalda vorming",
+InsertLinkLbl : "Link",
+InsertLink : "Sisesta/Muuda link",
+RemoveLink : "Eemalda link",
+Anchor : "Sisesta/Muuda ankur",
+InsertImageLbl : "Pilt",
+InsertImage : "Sisesta/Muuda pilt",
+InsertFlashLbl : "Flash",
+InsertFlash : "Sisesta/Muuda flash",
+InsertTableLbl : "Tabel",
+InsertTable : "Sisesta/Muuda tabel",
+InsertLineLbl : "Joon",
+InsertLine : "Sisesta horisontaaljoon",
+InsertSpecialCharLbl: "Erimärgid",
+InsertSpecialChar : "Sisesta erimärk",
+InsertSmileyLbl : "Emotikon",
+InsertSmiley : "Sisesta emotikon",
+About : "FCKeditor teave",
+Bold : "Rasvane kiri",
+Italic : "Kursiiv kiri",
+Underline : "Allajoonitud kiri",
+StrikeThrough : "Läbijoonitud kiri",
+Subscript : "Allindeks",
+Superscript : "Ãœlaindeks",
+LeftJustify : "Vasakjoondus",
+CenterJustify : "Keskjoondus",
+RightJustify : "Paremjoondus",
+BlockJustify : "Rööpjoondus",
+DecreaseIndent : "Vähenda taanet",
+IncreaseIndent : "Suurenda taanet",
+Undo : "Võta tagasi",
+Redo : "Korda toimingut",
+NumberedListLbl : "Nummerdatud loetelu",
+NumberedList : "Sisesta/Eemalda nummerdatud loetelu",
+BulletedListLbl : "Punktiseeritud loetelu",
+BulletedList : "Sisesta/Eemalda punktiseeritud loetelu",
+ShowTableBorders : "Näita tabeli jooni",
+ShowDetails : "Näita üksikasju",
+Style : "Laad",
+FontFormat : "Vorming",
+Font : "Kiri",
+FontSize : "Suurus",
+TextColor : "Teksti värv",
+BGColor : "Tausta värv",
+Source : "Lähtekood",
+Find : "Otsi",
+Replace : "Asenda",
+SpellCheck : "Kontrolli õigekirja",
+UniversalKeyboard : "Universaalne klaviatuur",
+PageBreakLbl : "Lehepiir",
+PageBreak : "Sisesta lehevahetus koht",
+
+Form : "Vorm",
+Checkbox : "Märkeruut",
+RadioButton : "Raadionupp",
+TextField : "Tekstilahter",
+Textarea : "Tekstiala",
+HiddenField : "Varjatud lahter",
+Button : "Nupp",
+SelectionField : "Valiklahter",
+ImageButton : "Piltnupp",
+
+FitWindow : "Maksimeeri redaktori mõõtmed",
+
+// Context Menu
+EditLink : "Muuda linki",
+CellCM : "Lahter",
+RowCM : "Rida",
+ColumnCM : "Veerg",
+InsertRow : "Lisa rida",
+DeleteRows : "Eemalda ridu",
+InsertColumn : "Lisa veerg",
+DeleteColumns : "Eemalda veerud",
+InsertCell : "Lisa lahter",
+DeleteCells : "Eemalda lahtrid",
+MergeCells : "Ãœhenda lahtrid",
+SplitCell : "Lahuta lahtrid",
+TableDelete : "Kustuta tabel",
+CellProperties : "Lahtri atribuudid",
+TableProperties : "Tabeli atribuudid",
+ImageProperties : "Pildi atribuudid",
+FlashProperties : "Flash omadused",
+
+AnchorProp : "Ankru omadused",
+ButtonProp : "Nupu omadused",
+CheckboxProp : "Märkeruudu omadused",
+HiddenFieldProp : "Varjatud lahtri omadused",
+RadioButtonProp : "Raadionupu omadused",
+ImageButtonProp : "Piltnupu omadused",
+TextFieldProp : "Tekstilahtri omadused",
+SelectionFieldProp : "Valiklahtri omadused",
+TextareaProp : "Tekstiala omadused",
+FormProp : "Vormi omadused",
+
+FontFormats : "Tavaline;Vormindatud;Aadress;Pealkiri 1;Pealkiri 2;Pealkiri 3;Pealkiri 4;Pealkiri 5;Pealkiri 6;Tavaline (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Töötlen XHTML. Palun oota...",
+Done : "Tehtud",
+PasteWordConfirm : "Tekst, mida soovid lisada paistab pärinevat Wordist. Kas soovid seda enne kleepimist puhastada?",
+NotCompatiblePaste : "See käsk on saadaval ainult Internet Explorer versioon 5.5 või uuema puhul. Kas soovid kleepida ilma puhastamata?",
+UnknownToolbarItem : "Tundmatu tööriistariba üksus \"%1\"",
+UnknownCommand : "Tundmatu käsunimi \"%1\"",
+NotImplemented : "Käsku ei täidetud",
+UnknownToolbarSet : "Tööriistariba \"%1\" ei eksisteeri",
+NoActiveX : "Sinu veebisirvija turvalisuse seaded võivad limiteerida mõningaid tekstirdaktori kasutus võimalusi. Sa peaksid võimaldama valiku \"Run ActiveX controls and plug-ins\" oma sirvija seadetes. Muidu võid sa täheldada vigu tekstiredaktori töös ja märgata puuduvaid funktsioone.",
+BrowseServerBlocked : "Ressursside sirvija avamine ebaõnnestus. Võimalda pop-up akende avanemine.",
+DialogBlocked : "Ei olenud võimalik avada dialoogi akent. Võimalda pop-up akende avanemine.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Loobu",
+DlgBtnClose : "Sulge",
+DlgBtnBrowseServer : "Sirvi serverit",
+DlgAdvancedTag : "Täpsemalt",
+DlgOpOther : "<Teine>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Palun sisesta URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<määramata>",
+DlgGenId : "Id",
+DlgGenLangDir : "Keele suund",
+DlgGenLangDirLtr : "Vasakult paremale (LTR)",
+DlgGenLangDirRtl : "Paremalt vasakule (RTL)",
+DlgGenLangCode : "Keele kood",
+DlgGenAccessKey : "Juurdepääsu võti",
+DlgGenName : "Nimi",
+DlgGenTabIndex : "Tab indeks",
+DlgGenLongDescr : "Pikk kirjeldus URL",
+DlgGenClass : "Stiilistiku klassid",
+DlgGenTitle : "Juhendav tiitel",
+DlgGenContType : "Juhendava sisu tüüp",
+DlgGenLinkCharset : "Lingitud ressurssi märgistik",
+DlgGenStyle : "Laad",
+
+// Image Dialog
+DlgImgTitle : "Pildi atribuudid",
+DlgImgInfoTab : "Pildi info",
+DlgImgBtnUpload : "Saada serverissee",
+DlgImgURL : "URL",
+DlgImgUpload : "Lae üles",
+DlgImgAlt : "Alternatiivne tekst",
+DlgImgWidth : "Laius",
+DlgImgHeight : "Kõrgus",
+DlgImgLockRatio : "Lukusta kuvasuhe",
+DlgBtnResetSize : "Lähtesta suurus",
+DlgImgBorder : "Joon",
+DlgImgHSpace : "H. vaheruum",
+DlgImgVSpace : "V. vaheruum",
+DlgImgAlign : "Joondus",
+DlgImgAlignLeft : "Vasak",
+DlgImgAlignAbsBottom: "Abs alla",
+DlgImgAlignAbsMiddle: "Abs keskele",
+DlgImgAlignBaseline : "Baasjoonele",
+DlgImgAlignBottom : "Alla",
+DlgImgAlignMiddle : "Keskele",
+DlgImgAlignRight : "Paremale",
+DlgImgAlignTextTop : "Tekstit üles",
+DlgImgAlignTop : "Ãœles",
+DlgImgPreview : "Eelvaade",
+DlgImgAlertUrl : "Palun kirjuta pildi URL",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Flash omadused",
+DlgFlashChkPlay : "Automaatne start ",
+DlgFlashChkLoop : "Korduv",
+DlgFlashChkMenu : "Võimalda flash menüü",
+DlgFlashScale : "Mastaap",
+DlgFlashScaleAll : "Näita kõike",
+DlgFlashScaleNoBorder : "Äärist ei ole",
+DlgFlashScaleFit : "Täpne sobivus",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Lingi info",
+DlgLnkTargetTab : "Sihtkoht",
+
+DlgLnkType : "Lingi tüüp",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Ankur sellel lehel",
+DlgLnkTypeEMail : "E-post",
+DlgLnkProto : "Protokoll",
+DlgLnkProtoOther : "<muu>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Vali ankur",
+DlgLnkAnchorByName : "Ankru nime järgi",
+DlgLnkAnchorById : "Elemendi id järgi",
+DlgLnkNoAnchors : "(Selles dokumendis ei ole ankruid)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-posti aadress",
+DlgLnkEMailSubject : "Sõnumi teema",
+DlgLnkEMailBody : "Sõnumi tekst",
+DlgLnkUpload : "Lae üles",
+DlgLnkBtnUpload : "Saada serverisse",
+
+DlgLnkTarget : "Sihtkoht",
+DlgLnkTargetFrame : "<raam>",
+DlgLnkTargetPopup : "<hüpikaken>",
+DlgLnkTargetBlank : "Uus aken (_blank)",
+DlgLnkTargetParent : "Vanem aken (_parent)",
+DlgLnkTargetSelf : "Sama aken (_self)",
+DlgLnkTargetTop : "Pealmine aken (_top)",
+DlgLnkTargetFrameName : "Sihtmärk raami nimi",
+DlgLnkPopWinName : "Hüpikakna nimi",
+DlgLnkPopWinFeat : "Hüpikakna omadused",
+DlgLnkPopResize : "Suurendatav",
+DlgLnkPopLocation : "Aadressiriba",
+DlgLnkPopMenu : "Menüüriba",
+DlgLnkPopScroll : "Kerimisribad",
+DlgLnkPopStatus : "Olekuriba",
+DlgLnkPopToolbar : "Tööriistariba",
+DlgLnkPopFullScrn : "Täisekraan (IE)",
+DlgLnkPopDependent : "Sõltuv (Netscape)",
+DlgLnkPopWidth : "Laius",
+DlgLnkPopHeight : "Kõrgus",
+DlgLnkPopLeft : "Vasak asukoht",
+DlgLnkPopTop : "Ãœlemine asukoht",
+
+DlnLnkMsgNoUrl : "Palun kirjuta lingi URL",
+DlnLnkMsgNoEMail : "Palun kirjuta E-Posti aadress",
+DlnLnkMsgNoAnchor : "Palun vali ankur",
+DlnLnkMsgInvPopName : "Hüpikakna nimi peab algama alfabeetilise tähega ja ei tohi sisaldada tühikuid",
+
+// Color Dialog
+DlgColorTitle : "Vali värv",
+DlgColorBtnClear : "Tühjenda",
+DlgColorHighlight : "Märgi",
+DlgColorSelected : "Valitud",
+
+// Smiley Dialog
+DlgSmileyTitle : "Sisesta emotikon",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Vali erimärk",
+
+// Table Dialog
+DlgTableTitle : "Tabeli atribuudid",
+DlgTableRows : "Read",
+DlgTableColumns : "Veerud",
+DlgTableBorder : "Joone suurus",
+DlgTableAlign : "Joondus",
+DlgTableAlignNotSet : "<Määramata>",
+DlgTableAlignLeft : "Vasak",
+DlgTableAlignCenter : "Kesk",
+DlgTableAlignRight : "Parem",
+DlgTableWidth : "Laius",
+DlgTableWidthPx : "pikslit",
+DlgTableWidthPc : "protsenti",
+DlgTableHeight : "Kõrgus",
+DlgTableCellSpace : "Lahtri vahe",
+DlgTableCellPad : "Lahtri täidis",
+DlgTableCaption : "Tabeli tiitel",
+DlgTableSummary : "Kokkuvõte",
+
+// Table Cell Dialog
+DlgCellTitle : "Lahtri atribuudid",
+DlgCellWidth : "Laius",
+DlgCellWidthPx : "pikslit",
+DlgCellWidthPc : "protsenti",
+DlgCellHeight : "Kõrgus",
+DlgCellWordWrap : "Sõna ülekanne",
+DlgCellWordWrapNotSet : "<Määramata>",
+DlgCellWordWrapYes : "Jah",
+DlgCellWordWrapNo : "Ei",
+DlgCellHorAlign : "Horisontaaljoondus",
+DlgCellHorAlignNotSet : "<Määramata>",
+DlgCellHorAlignLeft : "Vasak",
+DlgCellHorAlignCenter : "Kesk",
+DlgCellHorAlignRight: "Parem",
+DlgCellVerAlign : "Vertikaaljoondus",
+DlgCellVerAlignNotSet : "<Määramata>",
+DlgCellVerAlignTop : "Ãœles",
+DlgCellVerAlignMiddle : "Keskele",
+DlgCellVerAlignBottom : "Alla",
+DlgCellVerAlignBaseline : "Baasjoonele",
+DlgCellRowSpan : "Reaulatus",
+DlgCellCollSpan : "Veeruulatus",
+DlgCellBackColor : "Tausta värv",
+DlgCellBorderColor : "Joone värv",
+DlgCellBtnSelect : "Vali...",
+
+// Find Dialog
+DlgFindTitle : "Otsi",
+DlgFindFindBtn : "Otsi",
+DlgFindNotFoundMsg : "Valitud teksti ei leitud.",
+
+// Replace Dialog
+DlgReplaceTitle : "Asenda",
+DlgReplaceFindLbl : "Leia mida:",
+DlgReplaceReplaceLbl : "Asenda millega:",
+DlgReplaceCaseChk : "Erista suur- ja väiketähti",
+DlgReplaceReplaceBtn : "Asenda",
+DlgReplaceReplAllBtn : "Asenda kõik",
+DlgReplaceWordChk : "Otsi terviklike sõnu",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Sinu veebisirvija turvaseaded ei luba redaktoril automaatselt lõigata. Palun kasutage selleks klaviatuuri klahvikombinatsiooni (Ctrl+X).",
+PasteErrorCopy : "Sinu veebisirvija turvaseaded ei luba redaktoril automaatselt kopeerida. Palun kasutage selleks klaviatuuri klahvikombinatsiooni (Ctrl+C).",
+
+PasteAsText : "Kleebi tavalise tekstina",
+PasteFromWord : "Kleebi Wordist",
+
+DlgPasteMsg2 : "Palun kleebi järgnevasse kasti kasutades klaviatuuri klahvikombinatsiooni (<STRONG>Ctrl+V</STRONG>) ja vajuta seejärel <STRONG>OK</STRONG>.",
+DlgPasteSec : "Sinu veebisirvija turvaseadete tõttu, ei oma redaktor otsest ligipääsu lõikelaua andmetele. Sa pead kleepima need uuesti siia aknasse.",
+DlgPasteIgnoreFont : "Ignoreeri kirja definitsioone",
+DlgPasteRemoveStyles : "Eemalda stiilide definitsioonid",
+DlgPasteCleanBox : "Puhasta ära kast",
+
+// Color Picker
+ColorAutomatic : "Automaatne",
+ColorMoreColors : "Rohkem värve...",
+
+// Document Properties
+DocProps : "Dokumendi omadused",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ankru omadused",
+DlgAnchorName : "Ankru nimi",
+DlgAnchorErrorName : "Palun sisest ankru nimi",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Puudub sõnastikust",
+DlgSpellChangeTo : "Muuda",
+DlgSpellBtnIgnore : "Ignoreeri",
+DlgSpellBtnIgnoreAll : "Ignoreeri kõiki",
+DlgSpellBtnReplace : "Asenda",
+DlgSpellBtnReplaceAll : "Asenda kõik",
+DlgSpellBtnUndo : "Võta tagasi",
+DlgSpellNoSuggestions : "- Soovitused puuduvad -",
+DlgSpellProgress : "Toimub õigekirja kontroll...",
+DlgSpellNoMispell : "Õigekirja kontroll sooritatud: õigekirjuvigu ei leitud",
+DlgSpellNoChanges : "Õigekirja kontroll sooritatud: ühtegi sõna ei muudetud",
+DlgSpellOneChange : "Õigekirja kontroll sooritatud: üks sõna muudeti",
+DlgSpellManyChanges : "Õigekirja kontroll sooritatud: %1 sõna muudetud",
+
+IeSpellDownload : "Õigekirja kontrollija ei ole installeeritud. Soovid sa selle alla laadida?",
+
+// Button Dialog
+DlgButtonText : "Tekst (väärtus)",
+DlgButtonType : "Tüüp",
+DlgButtonTypeBtn : "Nupp",
+DlgButtonTypeSbm : "Saada",
+DlgButtonTypeRst : "Lähtesta",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nimi",
+DlgCheckboxValue : "Väärtus",
+DlgCheckboxSelected : "Valitud",
+
+// Form Dialog
+DlgFormName : "Nimi",
+DlgFormAction : "Toiming",
+DlgFormMethod : "Meetod",
+
+// Select Field Dialog
+DlgSelectName : "Nimi",
+DlgSelectValue : "Väärtus",
+DlgSelectSize : "Suurus",
+DlgSelectLines : "ridu",
+DlgSelectChkMulti : "Võimalda mitu valikut",
+DlgSelectOpAvail : "Võimalikud valikud",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Väärtus",
+DlgSelectBtnAdd : "Lisa",
+DlgSelectBtnModify : "Muuda",
+DlgSelectBtnUp : "Ãœles",
+DlgSelectBtnDown : "Alla",
+DlgSelectBtnSetValue : "Sea valitud olekuna",
+DlgSelectBtnDelete : "Kustuta",
+
+// Textarea Dialog
+DlgTextareaName : "Nimi",
+DlgTextareaCols : "Veerge",
+DlgTextareaRows : "Ridu",
+
+// Text Field Dialog
+DlgTextName : "Nimi",
+DlgTextValue : "Väärtus",
+DlgTextCharWidth : "Laius (tähemärkides)",
+DlgTextMaxChars : "Maksimaalselt tähemärke",
+DlgTextType : "Tüüp",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Parool",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nimi",
+DlgHiddenValue : "Väärtus",
+
+// Bulleted List Dialog
+BulletedListProp : "Täpitud loetelu omadused",
+NumberedListProp : "Nummerdatud loetelu omadused",
+DlgLstStart : "Alusta",
+DlgLstType : "Tüüp",
+DlgLstTypeCircle : "Ring",
+DlgLstTypeDisc : "Ketas",
+DlgLstTypeSquare : "Ruut",
+DlgLstTypeNumbers : "Numbrid (1, 2, 3)",
+DlgLstTypeLCase : "Väiketähed (a, b, c)",
+DlgLstTypeUCase : "Suurtähed (A, B, C)",
+DlgLstTypeSRoman : "Väiksed Rooma numbrid (i, ii, iii)",
+DlgLstTypeLRoman : "Suured Rooma numbrid (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Ãœldine",
+DlgDocBackTab : "Taust",
+DlgDocColorsTab : "Värvid ja veerised",
+DlgDocMetaTab : "Meta andmed",
+
+DlgDocPageTitle : "Lehekülje tiitel",
+DlgDocLangDir : "Kirja suund",
+DlgDocLangDirLTR : "Vasakult paremale (LTR)",
+DlgDocLangDirRTL : "Paremalt vasakule (RTL)",
+DlgDocLangCode : "Keele kood",
+DlgDocCharSet : "Märgistiku kodeering",
+DlgDocCharSetCE : "Kesk-Euroopa",
+DlgDocCharSetCT : "Hiina traditsiooniline (Big5)",
+DlgDocCharSetCR : "Kirillisa",
+DlgDocCharSetGR : "Kreeka",
+DlgDocCharSetJP : "Jaapani",
+DlgDocCharSetKR : "Korea",
+DlgDocCharSetTR : "Türgi",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Lääne-Euroopa",
+DlgDocCharSetOther : "Ülejäänud märgistike kodeeringud",
+
+DlgDocDocType : "Dokumendi tüüppäis",
+DlgDocDocTypeOther : "Teised dokumendi tüüppäised",
+DlgDocIncXHTML : "Arva kaasa XHTML deklaratsioonid",
+DlgDocBgColor : "Taustavärv",
+DlgDocBgImage : "Taustapildi URL",
+DlgDocBgNoScroll : "Mittekeritav tagataust",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Külastatud link",
+DlgDocCActive : "Aktiivne link",
+DlgDocMargins : "Lehekülje äärised",
+DlgDocMaTop : "Ãœlaserv",
+DlgDocMaLeft : "Vasakserv",
+DlgDocMaRight : "Paremserv",
+DlgDocMaBottom : "Alaserv",
+DlgDocMeIndex : "Dokumendi võtmesõnad (eraldatud komadega)",
+DlgDocMeDescr : "Dokumendi kirjeldus",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Autoriõigus",
+DlgDocPreview : "Eelvaade",
+
+// Templates Dialog
+Templates : "Å abloon",
+DlgTemplatesTitle : "Sisu Å¡abloonid",
+DlgTemplatesSelMsg : "Palun vali šabloon, et avada see redaktoris<br />(praegune sisu läheb kaotsi):",
+DlgTemplatesLoading : "Laen Å¡abloonide nimekirja. Palun oota...",
+DlgTemplatesNoTpl : "(Ãœhtegi Å¡ablooni ei ole defineeritud)",
+DlgTemplatesReplace : "Asenda tegelik sisu",
+
+// About Dialog
+DlgAboutAboutTab : "Teave",
+DlgAboutBrowserInfoTab : "Veebisirvija info",
+DlgAboutLicenseTab : "Litsents",
+DlgAboutVersion : "versioon",
+DlgAboutInfo : "Täpsema info saamiseks mine"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/eu.js b/httemplate/elements/fckeditor/editor/lang/eu.js
new file mode 100644
index 0000000..266d427
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/eu.js
@@ -0,0 +1,505 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Basque language file.
+ * Euskara hizkuntza fitxategia.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Estutu Tresna Barra",
+ToolbarExpand : "Hedatu Tresna Barra",
+
+// Toolbar Items and Context Menu
+Save : "Gorde",
+NewPage : "Orrialde Berria",
+Preview : "Aurrebista",
+Cut : "Ebaki",
+Copy : "Kopiatu",
+Paste : "Itsatsi",
+PasteText : "Itsatsi testu bezala",
+PasteWord : "Itsatsi Word-etik",
+Print : "Inprimatu",
+SelectAll : "Hautatu dena",
+RemoveFormat : "Kendu Formatoa",
+InsertLinkLbl : "Esteka",
+InsertLink : "Txertatu/Editatu Esteka",
+RemoveLink : "Kendu Esteka",
+Anchor : "Aingura",
+InsertImageLbl : "Irudia",
+InsertImage : "Txertatu/Editatu Irudia",
+InsertFlashLbl : "Flasha",
+InsertFlash : "Txertatu/Editatu Flasha",
+InsertTableLbl : "Taula",
+InsertTable : "Txertatu/Editatu Taula",
+InsertLineLbl : "Lerroa",
+InsertLine : "Txertatu Marra Horizontala",
+InsertSpecialCharLbl: "Karaktere Berezia",
+InsertSpecialChar : "Txertatu Karaktere Berezia",
+InsertSmileyLbl : "Aurpegierak",
+InsertSmiley : "Txertatu Aurpegierak",
+About : "FCKeditor-ri buruz",
+Bold : "Lodia",
+Italic : "Etzana",
+Underline : "Azpimarratu",
+StrikeThrough : "Marratua",
+Subscript : "Azpi-indize",
+Superscript : "Goi-indize",
+LeftJustify : "Lerrokatu Ezkerrean",
+CenterJustify : "Lerrokatu Erdian",
+RightJustify : "Lerrokatu Eskuman",
+BlockJustify : "Justifikatu",
+DecreaseIndent : "Txikitu Koska",
+IncreaseIndent : "Handitu Koska",
+Undo : "Desegin",
+Redo : "Berregin",
+NumberedListLbl : "Zenbakidun Zerrenda",
+NumberedList : "Txertatu/Kendu Zenbakidun zerrenda",
+BulletedListLbl : "Buletdun Zerrenda",
+BulletedList : "Txertatu/Kendu Buletdun zerrenda",
+ShowTableBorders : "Erakutsi Taularen Ertzak",
+ShowDetails : "Erakutsi Xehetasunak",
+Style : "Estiloa",
+FontFormat : "Formatoa",
+Font : "Letra-tipoa",
+FontSize : "Tamaina",
+TextColor : "Testu Kolorea",
+BGColor : "Atzeko kolorea",
+Source : "HTML Iturburua",
+Find : "Bilatu",
+Replace : "Ordezkatu",
+SpellCheck : "Ortografia",
+UniversalKeyboard : "Teklatu Unibertsala",
+PageBreakLbl : "Orrialde-jauzia",
+PageBreak : "Txertatu Orrialde-jauzia",
+
+Form : "Formularioa",
+Checkbox : "Kontrol-laukia",
+RadioButton : "Aukera-botoia",
+TextField : "Testu Eremua",
+Textarea : "Testu-area",
+HiddenField : "Ezkutuko Eremua",
+Button : "Botoia",
+SelectionField : "Hautespen Eremua",
+ImageButton : "Irudi Botoia",
+
+FitWindow : "Maximizatu editorearen tamaina",
+
+// Context Menu
+EditLink : "Aldatu Esteka",
+CellCM : "Gelaxka",
+RowCM : "Errenkada",
+ColumnCM : "Zutabea",
+InsertRow : "Txertatu Errenkada",
+DeleteRows : "Ezabatu Errenkadak",
+InsertColumn : "Txertatu Zutabea",
+DeleteColumns : "Ezabatu Zutabeak",
+InsertCell : "Txertatu Gelaxka",
+DeleteCells : "Kendu Gelaxkak",
+MergeCells : "Batu Gelaxkak",
+SplitCell : "Zatitu Gelaxka",
+TableDelete : "Ezabatu Taula",
+CellProperties : "Gelaxkaren Ezaugarriak",
+TableProperties : "Taularen Ezaugarriak",
+ImageProperties : "Irudiaren Ezaugarriak",
+FlashProperties : "Flasharen Ezaugarriak",
+
+AnchorProp : "Ainguraren Ezaugarriak",
+ButtonProp : "Botoiaren Ezaugarriak",
+CheckboxProp : "Kontrol-laukiko Ezaugarriak",
+HiddenFieldProp : "Ezkutuko Eremuaren Ezaugarriak",
+RadioButtonProp : "Aukera-botoiaren Ezaugarriak",
+ImageButtonProp : "Irudi Botoiaren Ezaugarriak",
+TextFieldProp : "Testu Eremuaren Ezaugarriak",
+SelectionFieldProp : "Hautespen Eremuaren Ezaugarriak",
+TextareaProp : "Testu-arearen Ezaugarriak",
+FormProp : "Formularioaren Ezaugarriak",
+
+FontFormats : "Arrunta;Formateatua;Helbidea;Izenburua 1;Izenburua 2;Izenburua 3;Izenburua 4;Izenburua 5;Izenburua 6;Paragrafoa (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML Prozesatzen. Itxaron mesedez...",
+Done : "Eginda",
+PasteWordConfirm : "Itsatsi nahi duzun textua Wordetik hartua dela dirudi. Itsatsi baino lehen garbitu nahi duzu?",
+NotCompatiblePaste : "Komando hau Internet Explorer 5.5 bertsiorako edo ondorengoentzako erabilgarria dago. Garbitu gabe itsatsi nahi duzu?",
+UnknownToolbarItem : "Ataza barrako elementu ezezaguna \"%1\"",
+UnknownCommand : "Komando izen ezezaguna \"%1\"",
+NotImplemented : "Komando ez inplementatua",
+UnknownToolbarSet : "Ataza barra \"%1\" taldea ez da existitzen",
+NoActiveX : "Zure nabigatzailearen segustasun hobespenak editore honen zenbait ezaugarri mugatu ditzake. \"ActiveX kontrolak eta plug-inak\" aktibatu beharko zenituzke, bestela erroreak eta ezaugarrietan mugak egon daitezke.",
+BrowseServerBlocked : "Baliabideen arakatzailea ezin da ireki. Ziurtatu popup blokeatzaileak desgaituta dituzula.",
+DialogBlocked : "Ezin da elkarrizketa-leihoa ireki. Ziurtatu popup blokeatzaileak desgaituta dituzula.",
+
+// Dialogs
+DlgBtnOK : "Ados",
+DlgBtnCancel : "Utzi",
+DlgBtnClose : "Itxi",
+DlgBtnBrowseServer : "Zerbitzaria arakatu",
+DlgAdvancedTag : "Aurreratua",
+DlgOpOther : "<Bestelakoak>",
+DlgInfoTab : "Informazioa",
+DlgAlertUrl : "Mesedez URLa idatzi ezazu",
+
+// General Dialogs Labels
+DlgGenNotSet : "<Ezarri gabe>",
+DlgGenId : "Id",
+DlgGenLangDir : "Hizkuntzaren Norabidea",
+DlgGenLangDirLtr : "Ezkerretik Eskumara(LTR)",
+DlgGenLangDirRtl : "Eskumatik Ezkerrera (RTL)",
+DlgGenLangCode : "Hizkuntza Kodea",
+DlgGenAccessKey : "Sarbide-gakoa",
+DlgGenName : "Izena",
+DlgGenTabIndex : "Tabulazio Indizea",
+DlgGenLongDescr : "URL Deskribapen Luzea",
+DlgGenClass : "Estilo-orriko Klaseak",
+DlgGenTitle : "Izenburua",
+DlgGenContType : "Eduki Mota (Content Type)",
+DlgGenLinkCharset : "Estekatutako Karaktere Multzoa",
+DlgGenStyle : "Estiloa",
+
+// Image Dialog
+DlgImgTitle : "Irudi Ezaugarriak",
+DlgImgInfoTab : "Irudi informazioa",
+DlgImgBtnUpload : "Zerbitzarira bidalia",
+DlgImgURL : "URL",
+DlgImgUpload : "Gora Kargatu",
+DlgImgAlt : "Textu Alternatiboa",
+DlgImgWidth : "Zabalera",
+DlgImgHeight : "Altuera",
+DlgImgLockRatio : "Erlazioa Blokeatu",
+DlgBtnResetSize : "Tamaina Berrezarri",
+DlgImgBorder : "Ertza",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Lerrokatu",
+DlgImgAlignLeft : "Ezkerrera",
+DlgImgAlignAbsBottom: "Abs Behean",
+DlgImgAlignAbsMiddle: "Abs Erdian",
+DlgImgAlignBaseline : "Oinan",
+DlgImgAlignBottom : "Behean",
+DlgImgAlignMiddle : "Erdian",
+DlgImgAlignRight : "Eskuman",
+DlgImgAlignTextTop : "Testua Goian",
+DlgImgAlignTop : "Goian",
+DlgImgPreview : "Aurrebista",
+DlgImgAlertUrl : "Mesedez Irudiaren URLa idatzi",
+DlgImgLinkTab : "Esteka",
+
+// Flash Dialog
+DlgFlashTitle : "Flasharen Ezaugarriak",
+DlgFlashChkPlay : "Automatikoki Erreproduzitu",
+DlgFlashChkLoop : "Begizta",
+DlgFlashChkMenu : "Flasharen Menua Gaitu",
+DlgFlashScale : "Eskalatu",
+DlgFlashScaleAll : "Dena erakutsi",
+DlgFlashScaleNoBorder : "Ertzarik gabe",
+DlgFlashScaleFit : "Doitu",
+
+// Link Dialog
+DlgLnkWindowTitle : "Esteka",
+DlgLnkInfoTab : "Estekaren Informazioa",
+DlgLnkTargetTab : "Helburua",
+
+DlgLnkType : "Esteka Mota",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Aingura horrialde honentan",
+DlgLnkTypeEMail : "ePosta",
+DlgLnkProto : "Protokoloa",
+DlgLnkProtoOther : "<Beste batzuk>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Aingura bat hautatu",
+DlgLnkAnchorByName : "Aingura izenagatik",
+DlgLnkAnchorById : "Elementuaren ID-gatik",
+DlgLnkNoAnchors : "<Ez daude aingurak eskuragarri dokumentuan>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ePosta Helbidea",
+DlgLnkEMailSubject : "Mezuaren Gaia",
+DlgLnkEMailBody : "Mezuaren Gorputza",
+DlgLnkUpload : "Gora kargatu",
+DlgLnkBtnUpload : "Zerbitzarira bidali",
+
+DlgLnkTarget : "Target (Helburua)",
+DlgLnkTargetFrame : "<marko>",
+DlgLnkTargetPopup : "<popup lehioa>",
+DlgLnkTargetBlank : "Lehio Berria (_blank)",
+DlgLnkTargetParent : "Lehio Gurasoa (_parent)",
+DlgLnkTargetSelf : "Lehio Berdina (_self)",
+DlgLnkTargetTop : "Goiko Lehioa (_top)",
+DlgLnkTargetFrameName : "Marko Helburuaren Izena",
+DlgLnkPopWinName : "Popup Lehioaren Izena",
+DlgLnkPopWinFeat : "Popup Lehioaren Ezaugarriak",
+DlgLnkPopResize : "Tamaina Aldakorra",
+DlgLnkPopLocation : "Kokaleku Barra",
+DlgLnkPopMenu : "Menu Barra",
+DlgLnkPopScroll : "Korritze Barrak",
+DlgLnkPopStatus : "Egoera Barra",
+DlgLnkPopToolbar : "Tresna Barra",
+DlgLnkPopFullScrn : "Pantaila Osoa (IE)",
+DlgLnkPopDependent : "Menpekoa (Netscape)",
+DlgLnkPopWidth : "Zabalera",
+DlgLnkPopHeight : "Altuera",
+DlgLnkPopLeft : "Ezkerreko Posizioa",
+DlgLnkPopTop : "Goiko Posizioa",
+
+DlnLnkMsgNoUrl : "Mesedez URL esteka idatzi",
+DlnLnkMsgNoEMail : "Mesedez ePosta helbidea idatzi",
+DlnLnkMsgNoAnchor : "Mesedez aingura bat aukeratu",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Kolore Aukeraketa",
+DlgColorBtnClear : "Garbitu",
+DlgColorHighlight : "Nabarmendu",
+DlgColorSelected : "Aukeratuta",
+
+// Smiley Dialog
+DlgSmileyTitle : "Aurpegiera Sartu",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Karaktere Berezia Aukeratu",
+
+// Table Dialog
+DlgTableTitle : "Taularen Ezaugarriak",
+DlgTableRows : "Lerroak",
+DlgTableColumns : "Zutabeak",
+DlgTableBorder : "Ertzaren Zabalera",
+DlgTableAlign : "Lerrokatu",
+DlgTableAlignNotSet : "<Ezarri gabe>",
+DlgTableAlignLeft : "Ezkerrean",
+DlgTableAlignCenter : "Erdian",
+DlgTableAlignRight : "Eskuman",
+DlgTableWidth : "Zabalera",
+DlgTableWidthPx : "pixel",
+DlgTableWidthPc : "ehuneko",
+DlgTableHeight : "Altuera",
+DlgTableCellSpace : "Gelaxka arteko tartea",
+DlgTableCellPad : "Gelaxken betegarria",
+DlgTableCaption : "Epigrafea",
+DlgTableSummary : "Laburpena",
+
+// Table Cell Dialog
+DlgCellTitle : "Gelaxken Ezaugarriak",
+DlgCellWidth : "Zabalera",
+DlgCellWidthPx : "pixel",
+DlgCellWidthPc : "ehuneko",
+DlgCellHeight : "Altuera",
+DlgCellWordWrap : "Itzulbira",
+DlgCellWordWrapNotSet : "<Ezarri gabe>",
+DlgCellWordWrapYes : "Bai",
+DlgCellWordWrapNo : "Ez",
+DlgCellHorAlign : "Horizontal Alignment",
+DlgCellHorAlignNotSet : "<Ezarri gabe>",
+DlgCellHorAlignLeft : "Ezkerrean",
+DlgCellHorAlignCenter : "Erdian",
+DlgCellHorAlignRight: "Eskuman",
+DlgCellVerAlign : "Lerrokatu Bertikalki",
+DlgCellVerAlignNotSet : "<Ezarri gabe>",
+DlgCellVerAlignTop : "Goian",
+DlgCellVerAlignMiddle : "Erdian",
+DlgCellVerAlignBottom : "Behean",
+DlgCellVerAlignBaseline : "Oinan",
+DlgCellRowSpan : "Lerroak Hedatu",
+DlgCellCollSpan : "Zutabeak Hedatu",
+DlgCellBackColor : "Atzeko Kolorea",
+DlgCellBorderColor : "Ertzako Kolorea",
+DlgCellBtnSelect : "Aukertau...",
+
+// Find Dialog
+DlgFindTitle : "Bilaketa",
+DlgFindFindBtn : "Bilatu",
+DlgFindNotFoundMsg : "Idatzitako testua ez da topatu.",
+
+// Replace Dialog
+DlgReplaceTitle : "Ordeztu",
+DlgReplaceFindLbl : "Zer bilatu:",
+DlgReplaceReplaceLbl : "Zerekin ordeztu:",
+DlgReplaceCaseChk : "Maiuskula/minuskula",
+DlgReplaceReplaceBtn : "Ordeztu",
+DlgReplaceReplAllBtn : "Ordeztu Guztiak",
+DlgReplaceWordChk : "Esaldi osoa bilatu",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Zure web nabigatzailearen segurtasun ezarpenak testuak automatikoki moztea ez dute baimentzen. Mesedez teklatua erabili ezazu (Ctrl+X).",
+PasteErrorCopy : "Zure web nabigatzailearen segurtasun ezarpenak testuak automatikoki kopiatzea ez dute baimentzen. Mesedez teklatua erabili ezazu (Ctrl+C).",
+
+PasteAsText : "Testu Arrunta bezala Itsatsi",
+PasteFromWord : "Word-etik itsatsi",
+
+DlgPasteMsg2 : "Mesedez teklatua erabilita (<STRONG>Ctrl+V</STRONG>) ondorego eremuan testua itsatsi eta <STRONG>OK</STRONG> sakatu.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Letra Motaren definizioa ezikusi",
+DlgPasteRemoveStyles : "Estilo definizioak kendu",
+DlgPasteCleanBox : "Testu-eremua Garbitu",
+
+// Color Picker
+ColorAutomatic : "Automatikoa",
+ColorMoreColors : "Kolore gehiago...",
+
+// Document Properties
+DocProps : "Dokumentuaren Ezarpenak",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ainguraren Ezaugarriak",
+DlgAnchorName : "Ainguraren Izena",
+DlgAnchorErrorName : "Idatzi ainguraren izena",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ez dago hiztegian",
+DlgSpellChangeTo : "Honekin ordezkatu",
+DlgSpellBtnIgnore : "Ezikusi",
+DlgSpellBtnIgnoreAll : "Denak Ezikusi",
+DlgSpellBtnReplace : "Ordezkatu",
+DlgSpellBtnReplaceAll : "Denak Ordezkatu",
+DlgSpellBtnUndo : "Desegin",
+DlgSpellNoSuggestions : "- Iradokizunik ez -",
+DlgSpellProgress : "Zuzenketa ortografikoa martxan...",
+DlgSpellNoMispell : "Zuzenketa ortografikoa bukatuta: Akatsik ez",
+DlgSpellNoChanges : "Zuzenketa ortografikoa bukatuta: Ez da ezer aldatu",
+DlgSpellOneChange : "Zuzenketa ortografikoa bukatuta: Hitz bat aldatu da",
+DlgSpellManyChanges : "Zuzenketa ortografikoa bukatuta: %1 hitz aldatu dira",
+
+IeSpellDownload : "Zuzentzaile ortografikoa ez dago instalatuta. Deskargatu nahi duzu?",
+
+// Button Dialog
+DlgButtonText : "Testua (Balorea)",
+DlgButtonType : "Mota",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Izena",
+DlgCheckboxValue : "Balorea",
+DlgCheckboxSelected : "Hautatuta",
+
+// Form Dialog
+DlgFormName : "Izena",
+DlgFormAction : "Ekintza",
+DlgFormMethod : "Method",
+
+// Select Field Dialog
+DlgSelectName : "Izena",
+DlgSelectValue : "Balorea",
+DlgSelectSize : "Tamaina",
+DlgSelectLines : "lerro kopurura",
+DlgSelectChkMulti : "Hautaketa anitzak baimendu",
+DlgSelectOpAvail : "Aukera Eskuragarriak",
+DlgSelectOpText : "Testua",
+DlgSelectOpValue : "Balorea",
+DlgSelectBtnAdd : "Gehitu",
+DlgSelectBtnModify : "Aldatu",
+DlgSelectBtnUp : "Gora",
+DlgSelectBtnDown : "Behera",
+DlgSelectBtnSetValue : "Aukeratutako balorea ezarri",
+DlgSelectBtnDelete : "Ezabatu",
+
+// Textarea Dialog
+DlgTextareaName : "Izena",
+DlgTextareaCols : "Zutabeak",
+DlgTextareaRows : "Lerroak",
+
+// Text Field Dialog
+DlgTextName : "Izena",
+DlgTextValue : "Balorea",
+DlgTextCharWidth : "Zabalera",
+DlgTextMaxChars : "Zenbat karaktere gehienez",
+DlgTextType : "Mota",
+DlgTextTypeText : "Testua",
+DlgTextTypePass : "Pasahitza",
+
+// Hidden Field Dialog
+DlgHiddenName : "Izena",
+DlgHiddenValue : "Balorea",
+
+// Bulleted List Dialog
+BulletedListProp : "Buletdun Zerrendaren Ezarpenak",
+NumberedListProp : "Zenbakidun Zerrendaren Ezarpenak",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Mota",
+DlgLstTypeCircle : "Zirkulua",
+DlgLstTypeDisc : "Diskoa",
+DlgLstTypeSquare : "Karratua",
+DlgLstTypeNumbers : "Zenbakiak (1, 2, 3)",
+DlgLstTypeLCase : "Letra xeheak (a, b, c)",
+DlgLstTypeUCase : "Letra larriak (A, B, C)",
+DlgLstTypeSRoman : "Erromatar zenbaki zeheak (i, ii, iii)",
+DlgLstTypeLRoman : "Erromatar zenbaki larriak (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Orokorra",
+DlgDocBackTab : "Atzekaldea",
+DlgDocColorsTab : "Koloreak eta Marjinak",
+DlgDocMetaTab : "Meta Informazioa",
+
+DlgDocPageTitle : "Orriaren Izenburua",
+DlgDocLangDir : "Hizkuntzaren Norabidea",
+DlgDocLangDirLTR : "Ezkerretik eskumara (LTR)",
+DlgDocLangDirRTL : "Eskumatik ezkerrera (RTL)",
+DlgDocLangCode : "Hizkuntzaren Kodea",
+DlgDocCharSet : "Karaktere Multzoaren Kodeketa",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Beste Karaktere Multzoaren Kodeketa",
+
+DlgDocDocType : "Document Type Goiburua",
+DlgDocDocTypeOther : "Beste Document Type Goiburua",
+DlgDocIncXHTML : "XHTML Ezarpenak",
+DlgDocBgColor : "Atzeko Kolorea",
+DlgDocBgImage : "Atzeko Irudiaren URL-a",
+DlgDocBgNoScroll : "Korritze gabeko Atzekaldea",
+DlgDocCText : "Testua",
+DlgDocCLink : "Estekak",
+DlgDocCVisited : "Bisitatutako Estekak",
+DlgDocCActive : "Esteka Aktiboa",
+DlgDocMargins : "Orrialdearen marjinak",
+DlgDocMaTop : "Goian",
+DlgDocMaLeft : "Ezkerrean",
+DlgDocMaRight : "Eskuman",
+DlgDocMaBottom : "Behean",
+DlgDocMeIndex : "Dokumentuaren Gako-hitzak (komarekin bananduta)",
+DlgDocMeDescr : "Dokumentuaren Deskribapena",
+DlgDocMeAuthor : "Egilea",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Aurrebista",
+
+// Templates Dialog
+Templates : "Txantiloiak",
+DlgTemplatesTitle : "Eduki Txantiloiak",
+DlgTemplatesSelMsg : "Mesedez txantiloia aukeratu editorean kargatzeko<br>(orain dauden edukiak galduko dira):",
+DlgTemplatesLoading : "Txantiloiak kargatzen. Itxaron mesedez...",
+DlgTemplatesNoTpl : "(Ez dago definitutako txantiloirik)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Honi buruz",
+DlgAboutBrowserInfoTab : "Nabigatzailearen Informazioa",
+DlgAboutLicenseTab : "Lizentzia",
+DlgAboutVersion : "bertsioa",
+DlgAboutInfo : "Informazio gehiago eskuratzeko hona joan"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/fa.js b/httemplate/elements/fckeditor/editor/lang/fa.js
new file mode 100644
index 0000000..e1bc973
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/fa.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Persian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "rtl",
+
+ToolbarCollapse : "برچیدن نوارابزار",
+ToolbarExpand : "گستردن نوارابزار",
+
+// Toolbar Items and Context Menu
+Save : "ذخیره",
+NewPage : "برگهٴ تازه",
+Preview : "پیش‌نمایش",
+Cut : "برش",
+Copy : "کپی",
+Paste : "چسباندن",
+PasteText : "چسباندن به عنوان متن Ùساده",
+PasteWord : "چسباندن از Word",
+Print : "چاپ",
+SelectAll : "گزینش همه",
+RemoveFormat : "برداشتن Ùرمت",
+InsertLinkLbl : "پیوند",
+InsertLink : "گنجاندن/ویرایش Ùپیوند",
+RemoveLink : "برداشتن پیوند",
+Anchor : "گنجاندن/ویرایش Ùلنگر",
+InsertImageLbl : "تصویر",
+InsertImage : "گنجاندن/ویرایش Ùتصویر",
+InsertFlashLbl : "Flash",
+InsertFlash : "گنجاندن/ویرایش ÙFlash",
+InsertTableLbl : "جدول",
+InsertTable : "گنجاندن/ویرایش Ùجدول",
+InsertLineLbl : "خط",
+InsertLine : "گنجاندن خط ÙاÙÙ‚ÛŒ",
+InsertSpecialCharLbl: "نویسهٴ ویژه",
+InsertSpecialChar : "گنجاندن نویسهٴ ویژه",
+InsertSmileyLbl : "خندانک",
+InsertSmiley : "گنجاندن خندانک",
+About : "دربارهٴ FCKeditor",
+Bold : "درشت",
+Italic : "خمیده",
+Underline : "خط‌زیردار",
+StrikeThrough : "میان‌خط",
+Subscript : "زیرنویس",
+Superscript : "بالانویس",
+LeftJustify : "چپ‌چین",
+CenterJustify : "میان‌چین",
+RightJustify : "راست‌چین",
+BlockJustify : "بلوک‌چین",
+DecreaseIndent : "کاهش تورÙتگی",
+IncreaseIndent : "اÙزایش تورÙتگی",
+Undo : "واچیدن",
+Redo : "بازچیدن",
+NumberedListLbl : "Ùهرست شماره‌دار",
+NumberedList : "گنجاندن/برداشتن Ùهرست شماره‌دار",
+BulletedListLbl : "Ùهرست نقطه‌ای",
+BulletedList : "گنجاندن/برداشتن Ùهرست نقطه‌ای",
+ShowTableBorders : "نمایش لبهٴ جدول",
+ShowDetails : "نمایش جزئیات",
+Style : "سبک",
+FontFormat : "Ùرمت",
+Font : "قلم",
+FontSize : "اندازه",
+TextColor : "رنگ متن",
+BGColor : "رنگ پس‌زمینه",
+Source : "منبع",
+Find : "جستجو",
+Replace : "جایگزینی",
+SpellCheck : "بررسی املا",
+UniversalKeyboard : "صÙحه‌کلید جهانی",
+PageBreakLbl : "شکستگی Ùپایان Ùبرگه",
+PageBreak : "گنجاندن شکستگی Ùپایان Ùبرگه",
+
+Form : "Ùرم",
+Checkbox : "خانهٴ گزینه‌ای",
+RadioButton : "دکمهٴ رادیویی",
+TextField : "Ùیلد متنی",
+Textarea : "ناحیهٴ متنی",
+HiddenField : "Ùیلد پنهان",
+Button : "دکمه",
+SelectionField : "Ùیلد چندگزینه‌ای",
+ImageButton : "دکمهٴ تصویری",
+
+FitWindow : "بیشینه‌سازی Ùاندازهٴ ویرایشگر",
+
+// Context Menu
+EditLink : "ویرایش پیوند",
+CellCM : "سلول",
+RowCM : "سطر",
+ColumnCM : "ستون",
+InsertRow : "گنجاندن سطر",
+DeleteRows : "حذ٠سطرها",
+InsertColumn : "گنجاندن ستون",
+DeleteColumns : "حذ٠ستونها",
+InsertCell : "گنجاندن سلول",
+DeleteCells : "حذ٠سلولها",
+MergeCells : "ادغام سلولها",
+SplitCell : "جداسازی سلول",
+TableDelete : "پاک‌کردن جدول",
+CellProperties : "ویژگیهای سلول",
+TableProperties : "ویژگیهای جدول",
+ImageProperties : "ویژگیهای تصویر",
+FlashProperties : "ویژگیهای Flash",
+
+AnchorProp : "ویژگیهای لنگر",
+ButtonProp : "ویژگیهای دکمه",
+CheckboxProp : "ویژگیهای خانهٴ گزینه‌ای",
+HiddenFieldProp : "ویژگیهای Ùیلد پنهان",
+RadioButtonProp : "ویژگیهای دکمهٴ رادیویی",
+ImageButtonProp : "ویژگیهای دکمهٴ تصویری",
+TextFieldProp : "ویژگیهای Ùیلد متنی",
+SelectionFieldProp : "ویژگیهای Ùیلد چندگزینه‌ای",
+TextareaProp : "ویژگیهای ناحیهٴ متنی",
+FormProp : "ویژگیهای Ùرم",
+
+FontFormats : "نرمال;Ùرمت‌شده;آدرس;سرنویس 1;سرنویس 2;سرنویس 3;سرنویس 4;سرنویس 5;سرنویس 6;بند;(DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "پردازش XHTML. لطÙا صبر کنید...",
+Done : "انجام شد",
+PasteWordConfirm : "متنی که می‌خواهید بچسبانید به نظر می‌رسد از Word کپی شده است. آیا می‌خواهید قبل از چسباندن آن را پاک‌سازی کنید؟",
+NotCompatiblePaste : "این Ùرمان برای مرورگر Internet Explorer از نگارش 5.5 یا بالاتر در دسترس است. آیا می‌خواهید بدون پاک‌سازی، متن را بچسبانید؟",
+UnknownToolbarItem : "Ùقرهٴ نوارابزار ناشناخته \"%1\"",
+UnknownCommand : "نام دستور ناشناخته \"%1\"",
+NotImplemented : "دستور پیاده‌سازی‌نشده",
+UnknownToolbarSet : "مجموعهٴ نوارابزار \"%1\" وجود ندارد",
+NoActiveX : "تنظیمات امنیتی مرورگر شما ممکن است در بعضی از ویژگیهای مرورگر محدودیت ایجاد کند. شما باید گزینهٴ \"Run ActiveX controls and plug-ins\" را Ùعال کنید. ممکن است شما با خطاهایی روبرو باشید Ùˆ متوجه کمبود ویژگیهایی شوید.",
+BrowseServerBlocked : "توانایی بازگشایی مرورگر منابع Ùراهم نیست. اطمینان حاصل کنید Ú©Ù‡ تمامی برنامه‌های پیشگیری از نمایش popup را از کار بازداشته‌اید.",
+DialogBlocked : "توانایی بازگشایی پنجرهٴ Ú©ÙˆÚ†Ú© ÙÚ¯Ùتگو Ùراهم نیست. اطمینان حاصل کنید Ú©Ù‡ تمامی برنامه‌های پیشگیری از نمایش popup را از کار بازداشته‌اید.",
+
+// Dialogs
+DlgBtnOK : "پذیرش",
+DlgBtnCancel : "انصراÙ",
+DlgBtnClose : "بستن",
+DlgBtnBrowseServer : "Ùهرست‌نمایی سرور",
+DlgAdvancedTag : "پیشرÙته",
+DlgOpOther : "<غیره>",
+DlgInfoTab : "اطلاعات",
+DlgAlertUrl : "لطÙاً URL را بنویسید",
+
+// General Dialogs Labels
+DlgGenNotSet : "<تعین‌نشده>",
+DlgGenId : "شناسه",
+DlgGenLangDir : "جهت‌نمای زبان",
+DlgGenLangDirLtr : "چپ به راست (LTR)",
+DlgGenLangDirRtl : "راست به چپ (RTL)",
+DlgGenLangCode : "کد زبان",
+DlgGenAccessKey : "کلید دستیابی",
+DlgGenName : "نام",
+DlgGenTabIndex : "نمایهٴ دسترسی با Tab",
+DlgGenLongDescr : "URL توصی٠طولانی",
+DlgGenClass : "کلاسهای شیوه‌نامه(Stylesheet)",
+DlgGenTitle : "عنوان کمکی",
+DlgGenContType : "نوع محتوای کمکی",
+DlgGenLinkCharset : "نویسه‌گان منبع Ùپیوندشده",
+DlgGenStyle : "شیوه(style)",
+
+// Image Dialog
+DlgImgTitle : "ویژگیهای تصویر",
+DlgImgInfoTab : "اطلاعات تصویر",
+DlgImgBtnUpload : "به سرور بÙرست",
+DlgImgURL : "URL",
+DlgImgUpload : "انتقال به سرور",
+DlgImgAlt : "متن جایگزین",
+DlgImgWidth : "پهنا",
+DlgImgHeight : "درازا",
+DlgImgLockRatio : "Ù‚Ùل‌کردن Ùنسبت",
+DlgBtnResetSize : "بازنشانی اندازه",
+DlgImgBorder : "لبه",
+DlgImgHSpace : "Ùاصلهٴ اÙÙ‚ÛŒ",
+DlgImgVSpace : "Ùاصلهٴ عمودی",
+DlgImgAlign : "چینش",
+DlgImgAlignLeft : "Ú†Ù¾",
+DlgImgAlignAbsBottom: "پائین مطلق",
+DlgImgAlignAbsMiddle: "وسط مطلق",
+DlgImgAlignBaseline : "خط‌پایه",
+DlgImgAlignBottom : "پائین",
+DlgImgAlignMiddle : "وسط",
+DlgImgAlignRight : "راست",
+DlgImgAlignTextTop : "متن بالا",
+DlgImgAlignTop : "بالا",
+DlgImgPreview : "پیش‌نمایش",
+DlgImgAlertUrl : "لطÙا URL تصویر را بنویسید",
+DlgImgLinkTab : "پیوند",
+
+// Flash Dialog
+DlgFlashTitle : "ویژگیهای Flash",
+DlgFlashChkPlay : "آغاز Ùخودکار",
+DlgFlashChkLoop : "اجرای پیاپی",
+DlgFlashChkMenu : "دردسترس‌بودن منوی Flash",
+DlgFlashScale : "مقیاس",
+DlgFlashScaleAll : "نمایش همه",
+DlgFlashScaleNoBorder : "بدون کران",
+DlgFlashScaleFit : "جایگیری کامل",
+
+// Link Dialog
+DlgLnkWindowTitle : "پیوند",
+DlgLnkInfoTab : "اطلاعات پیوند",
+DlgLnkTargetTab : "مقصد",
+
+DlgLnkType : "نوع پیوند",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "لنگر در همین صÙحه",
+DlgLnkTypeEMail : "پست الکترونیکی",
+DlgLnkProto : "پروتکل",
+DlgLnkProtoOther : "<دیگر>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "یک لنگر برگزینید",
+DlgLnkAnchorByName : "با نام لنگر",
+DlgLnkAnchorById : "با شناسهٴ المان",
+DlgLnkNoAnchors : "(در این سند لنگری دردسترس نیست)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "نشانی پست الکترونیکی",
+DlgLnkEMailSubject : "موضوع پیام",
+DlgLnkEMailBody : "متن پیام",
+DlgLnkUpload : "انتقال به سرور",
+DlgLnkBtnUpload : "به سرور بÙرست",
+
+DlgLnkTarget : "مقصد",
+DlgLnkTargetFrame : "<Ùریم>",
+DlgLnkTargetPopup : "<پنجرهٴ پاپاپ>",
+DlgLnkTargetBlank : "پنجرهٴ دیگر (_blank)",
+DlgLnkTargetParent : "پنجرهٴ والد (_parent)",
+DlgLnkTargetSelf : "همان پنجره (_self)",
+DlgLnkTargetTop : "بالاترین پنجره (_top)",
+DlgLnkTargetFrameName : "نام Ùریم مقصد",
+DlgLnkPopWinName : "نام پنجرهٴ پاپاپ",
+DlgLnkPopWinFeat : "ویژگیهای پنجرهٴ پاپاپ",
+DlgLnkPopResize : "قابل تغییر اندازه",
+DlgLnkPopLocation : "نوار موقعیت",
+DlgLnkPopMenu : "نوار منو",
+DlgLnkPopScroll : "میله‌های پیمایش",
+DlgLnkPopStatus : "نوار وضعیت",
+DlgLnkPopToolbar : "نوارابزار",
+DlgLnkPopFullScrn : "تمام‌صÙحه (IE)",
+DlgLnkPopDependent : "وابسته (Netscape)",
+DlgLnkPopWidth : "پهنا",
+DlgLnkPopHeight : "درازا",
+DlgLnkPopLeft : "موقعیت ÙÚ†Ù¾",
+DlgLnkPopTop : "موقعیت Ùبالا",
+
+DlnLnkMsgNoUrl : "لطÙا URL پیوند را بنویسید",
+DlnLnkMsgNoEMail : "لطÙا نشانی پست الکترونیکی را بنویسید",
+DlnLnkMsgNoAnchor : "لطÙا لنگری را برگزینید",
+DlnLnkMsgInvPopName : "نام پنجرهٴ پاپاپ باید با یک نویسهٴ الÙبایی آغاز گردد Ùˆ نباید Ùاصله‌های خالی در آن باشند",
+
+// Color Dialog
+DlgColorTitle : "گزینش رنگ",
+DlgColorBtnClear : "پاک‌کردن",
+DlgColorHighlight : "نمونه",
+DlgColorSelected : "برگزیده",
+
+// Smiley Dialog
+DlgSmileyTitle : "گنجاندن خندانک",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "گزینش نویسهٴ‌ویژه",
+
+// Table Dialog
+DlgTableTitle : "ویژگیهای جدول",
+DlgTableRows : "سطرها",
+DlgTableColumns : "ستونها",
+DlgTableBorder : "اندازهٴ لبه",
+DlgTableAlign : "چینش",
+DlgTableAlignNotSet : "<تعین‌نشده>",
+DlgTableAlignLeft : "Ú†Ù¾",
+DlgTableAlignCenter : "وسط",
+DlgTableAlignRight : "راست",
+DlgTableWidth : "پهنا",
+DlgTableWidthPx : "پیکسل",
+DlgTableWidthPc : "درصد",
+DlgTableHeight : "درازا",
+DlgTableCellSpace : "Ùاصلهٴ میان سلولها",
+DlgTableCellPad : "Ùاصلهٴ پرشده در سلول",
+DlgTableCaption : "عنوان",
+DlgTableSummary : "خلاصه",
+
+// Table Cell Dialog
+DlgCellTitle : "ویژگیهای سلول",
+DlgCellWidth : "پهنا",
+DlgCellWidthPx : "پیکسل",
+DlgCellWidthPc : "درصد",
+DlgCellHeight : "درازا",
+DlgCellWordWrap : "شکستن واژه‌ها",
+DlgCellWordWrapNotSet : "<تعین‌نشده>",
+DlgCellWordWrapYes : "بله",
+DlgCellWordWrapNo : "خیر",
+DlgCellHorAlign : "چینش ÙاÙÙ‚ÛŒ",
+DlgCellHorAlignNotSet : "<تعین‌نشده>",
+DlgCellHorAlignLeft : "Ú†Ù¾",
+DlgCellHorAlignCenter : "وسط",
+DlgCellHorAlignRight: "راست",
+DlgCellVerAlign : "چینش Ùعمودی",
+DlgCellVerAlignNotSet : "<تعین‌نشده>",
+DlgCellVerAlignTop : "بالا",
+DlgCellVerAlignMiddle : "میان",
+DlgCellVerAlignBottom : "پائین",
+DlgCellVerAlignBaseline : "خط‌پایه",
+DlgCellRowSpan : "گستردگی سطرها",
+DlgCellCollSpan : "گستردگی ستونها",
+DlgCellBackColor : "رنگ پس‌زمینه",
+DlgCellBorderColor : "رنگ لبه",
+DlgCellBtnSelect : "برگزینید...",
+
+// Find Dialog
+DlgFindTitle : "یاÙتن",
+DlgFindFindBtn : "یاÙتن",
+DlgFindNotFoundMsg : "متن موردنظر یاÙت نشد.",
+
+// Replace Dialog
+DlgReplaceTitle : "جایگزینی",
+DlgReplaceFindLbl : "چه‌چیز را می‌یابید:",
+DlgReplaceReplaceLbl : "جایگزینی با:",
+DlgReplaceCaseChk : "همسانی در بزرگی و کوچکی نویسه‌ها",
+DlgReplaceReplaceBtn : "جایگزینی",
+DlgReplaceReplAllBtn : "جایگزینی همهٴ یاÙته‌ها",
+DlgReplaceWordChk : "همسانی با واژهٴ کامل",
+
+// Paste Operations / Dialog
+PasteErrorCut : "تنظیمات امنیتی مرورگر شما اجازه نمی‌دهد Ú©Ù‡ ویرایشگر به طور خودکار عملکردهای برش را انجام دهد. لطÙا با دکمه‌های صÙحه‌کلید این کار را انجام دهید (Ctrl+X).",
+PasteErrorCopy : "تنظیمات امنیتی مرورگر شما اجازه نمی‌دهد Ú©Ù‡ ویرایشگر به طور خودکار عملکردهای کپی‌کردن را انجام دهد. لطÙا با دکمه‌های صÙحه‌کلید این کار را انجام دهید (Ctrl+C).",
+
+PasteAsText : "چسباندن به عنوان متن Ùساده",
+PasteFromWord : "چسباندن از Word",
+
+DlgPasteMsg2 : "لطÙا متن را با کلیدهای (<STRONG>Ctrl+V</STRONG>) در این جعبهٴ متنی بچسبانید Ùˆ <STRONG>پذیرش</STRONG> را بزنید.",
+DlgPasteSec : "به خاطر تنظیمات امنیتی مرورگر شما، ویرایشگر نمی‌تواند دسترسی مستقیم به داده‌های clipboard داشته باشد. شما باید دوباره آنرا در این پنجره بچسبانید.",
+DlgPasteIgnoreFont : "چشم‌پوشی از تعاری٠نوع قلم",
+DlgPasteRemoveStyles : "چشم‌پوشی از تعاری٠سبک (style)",
+DlgPasteCleanBox : "پاک‌کردن ناحیه",
+
+// Color Picker
+ColorAutomatic : "خودکار",
+ColorMoreColors : "رنگهای بیشتر...",
+
+// Document Properties
+DocProps : "ویژگیهای سند",
+
+// Anchor Dialog
+DlgAnchorTitle : "ویژگیهای لنگر",
+DlgAnchorName : "نام لنگر",
+DlgAnchorErrorName : "لطÙا نام لنگر را بنویسید",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "در واژه‌نامه یاÙت نشد",
+DlgSpellChangeTo : "تغییر به",
+DlgSpellBtnIgnore : "چشم‌پوشی",
+DlgSpellBtnIgnoreAll : "چشم‌پوشی همه",
+DlgSpellBtnReplace : "جایگزینی",
+DlgSpellBtnReplaceAll : "جایگزینی همه",
+DlgSpellBtnUndo : "واچینش",
+DlgSpellNoSuggestions : "- پیشنهادی نیست -",
+DlgSpellProgress : "بررسی املا در حال انجام...",
+DlgSpellNoMispell : "بررسی املا انجام شد. هیچ غلط‌املائی یاÙت نشد",
+DlgSpellNoChanges : "بررسی املا انجام شد. هیچ واژه‌ای تغییر نیاÙت",
+DlgSpellOneChange : "بررسی املا انجام شد. یک واژه تغییر یاÙت",
+DlgSpellManyChanges : "بررسی املا انجام شد. %1 واژه تغییر یاÙت",
+
+IeSpellDownload : "بررسی‌کنندهٴ املا نصب نشده است. آیا می‌خواهید آن را هم‌اکنون دریاÙت کنید؟",
+
+// Button Dialog
+DlgButtonText : "متن (مقدار)",
+DlgButtonType : "نوع",
+DlgButtonTypeBtn : "دکمه",
+DlgButtonTypeSbm : "Submit",
+DlgButtonTypeRst : "بازنشانی (Reset)",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "نام",
+DlgCheckboxValue : "مقدار",
+DlgCheckboxSelected : "برگزیده",
+
+// Form Dialog
+DlgFormName : "نام",
+DlgFormAction : "رویداد",
+DlgFormMethod : "متد",
+
+// Select Field Dialog
+DlgSelectName : "نام",
+DlgSelectValue : "مقدار",
+DlgSelectSize : "اندازه",
+DlgSelectLines : "خطوط",
+DlgSelectChkMulti : "گزینش چندگانه Ùراهم باشد",
+DlgSelectOpAvail : "گزینه‌های دردسترس",
+DlgSelectOpText : "متن",
+DlgSelectOpValue : "مقدار",
+DlgSelectBtnAdd : "اÙزودن",
+DlgSelectBtnModify : "ویرایش",
+DlgSelectBtnUp : "بالا",
+DlgSelectBtnDown : "پائین",
+DlgSelectBtnSetValue : "تنظیم به عنوان مقدار Ùبرگزیده",
+DlgSelectBtnDelete : "پاک‌کردن",
+
+// Textarea Dialog
+DlgTextareaName : "نام",
+DlgTextareaCols : "ستونها",
+DlgTextareaRows : "سطرها",
+
+// Text Field Dialog
+DlgTextName : "نام",
+DlgTextValue : "مقدار",
+DlgTextCharWidth : "پهنای نویسه",
+DlgTextMaxChars : "بیشینهٴ نویسه‌ها",
+DlgTextType : "نوع",
+DlgTextTypeText : "متن",
+DlgTextTypePass : "گذرواژه",
+
+// Hidden Field Dialog
+DlgHiddenName : "نام",
+DlgHiddenValue : "مقدار",
+
+// Bulleted List Dialog
+BulletedListProp : "ویژگیهای Ùهرست نقطه‌ای",
+NumberedListProp : "ویژگیهای Ùهرست شماره‌دار",
+DlgLstStart : "آغاز",
+DlgLstType : "نوع",
+DlgLstTypeCircle : "دایره",
+DlgLstTypeDisc : "قرص",
+DlgLstTypeSquare : "چهارگوش",
+DlgLstTypeNumbers : "شماره‌ها (1، 2، 3)",
+DlgLstTypeLCase : "نویسه‌های کوچک (a، b، c)",
+DlgLstTypeUCase : "نویسه‌های بزرگ (A، B، C)",
+DlgLstTypeSRoman : "شمارگان رومی کوچک (i، ii، iii)",
+DlgLstTypeLRoman : "شمارگان رومی بزرگ (I، II، III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "عمومی",
+DlgDocBackTab : "پس‌زمینه",
+DlgDocColorsTab : "رنگها و حاشیه‌ها",
+DlgDocMetaTab : "Ùراداده",
+
+DlgDocPageTitle : "عنوان صÙحه",
+DlgDocLangDir : "جهت زبان",
+DlgDocLangDirLTR : "چپ به راست (LTR(",
+DlgDocLangDirRTL : "راست به چپ (RTL(",
+DlgDocLangCode : "کد زبان",
+DlgDocCharSet : "رمزگذاری نویسه‌گان",
+DlgDocCharSetCE : "اروپای مرکزی",
+DlgDocCharSetCT : "چینی رسمی (Big5)",
+DlgDocCharSetCR : "سیریلیک",
+DlgDocCharSetGR : "یونانی",
+DlgDocCharSetJP : "ژاپنی",
+DlgDocCharSetKR : "کره‌ای",
+DlgDocCharSetTR : "ترکی",
+DlgDocCharSetUN : "یونیکÙد (UTF-8)",
+DlgDocCharSetWE : "اروپای غربی",
+DlgDocCharSetOther : "رمزگذاری نویسه‌گان دیگر",
+
+DlgDocDocType : "عنوان نوع سند",
+DlgDocDocTypeOther : "عنوان نوع سند دیگر",
+DlgDocIncXHTML : "شامل تعاری٠XHTML",
+DlgDocBgColor : "رنگ پس‌زمینه",
+DlgDocBgImage : "URL تصویر پس‌زمینه",
+DlgDocBgNoScroll : "پس‌زمینهٴ پیمایش‌ناپذیر",
+DlgDocCText : "متن",
+DlgDocCLink : "پیوند",
+DlgDocCVisited : "پیوند مشاهده‌شده",
+DlgDocCActive : "پیوند Ùعال",
+DlgDocMargins : "حاشیه‌های صÙحه",
+DlgDocMaTop : "بالا",
+DlgDocMaLeft : "Ú†Ù¾",
+DlgDocMaRight : "راست",
+DlgDocMaBottom : "پایین",
+DlgDocMeIndex : "کلیدواژگان نمایه‌گذاری سند (با کاما جدا شوند)",
+DlgDocMeDescr : "توصی٠سند",
+DlgDocMeAuthor : "نویسنده",
+DlgDocMeCopy : "کپی‌رایت",
+DlgDocPreview : "پیش‌نمایش",
+
+// Templates Dialog
+Templates : "الگوها",
+DlgTemplatesTitle : "الگوهای محتویات",
+DlgTemplatesSelMsg : "لطÙا الگوی موردنظر را برای بازکردن در ویرایشگر برگزینید<br>(محتویات کنونی از دست خواهند رÙت):",
+DlgTemplatesLoading : "بارگذاری Ùهرست الگوها. لطÙا صبر کنید...",
+DlgTemplatesNoTpl : "(الگوئی تعری٠نشده است)",
+DlgTemplatesReplace : "محتویات کنونی جایگزین شوند",
+
+// About Dialog
+DlgAboutAboutTab : "درباره",
+DlgAboutBrowserInfoTab : "اطلاعات مرورگر",
+DlgAboutLicenseTab : "گواهینامه",
+DlgAboutVersion : "نگارش",
+DlgAboutInfo : "برای آگاهی بیشتر به این نشانی بروید"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/fi.js b/httemplate/elements/fckeditor/editor/lang/fi.js
new file mode 100644
index 0000000..7e7986a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/fi.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Finnish language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Piilota työkalurivi",
+ToolbarExpand : "Näytä työkalurivi",
+
+// Toolbar Items and Context Menu
+Save : "Tallenna",
+NewPage : "Tyhjennä",
+Preview : "Esikatsele",
+Cut : "Leikkaa",
+Copy : "Kopioi",
+Paste : "Liitä",
+PasteText : "Liitä tekstinä",
+PasteWord : "Liitä Wordista",
+Print : "Tulosta",
+SelectAll : "Valitse kaikki",
+RemoveFormat : "Poista muotoilu",
+InsertLinkLbl : "Linkki",
+InsertLink : "Lisää linkki/muokkaa linkkiä",
+RemoveLink : "Poista linkki",
+Anchor : "Lisää ankkuri/muokkaa ankkuria",
+InsertImageLbl : "Kuva",
+InsertImage : "Lisää kuva/muokkaa kuvaa",
+InsertFlashLbl : "Flash",
+InsertFlash : "Lisää/muokkaa Flashia",
+InsertTableLbl : "Taulu",
+InsertTable : "Lisää taulu/muokkaa taulua",
+InsertLineLbl : "Murtoviiva",
+InsertLine : "Lisää murtoviiva",
+InsertSpecialCharLbl: "Erikoismerkki",
+InsertSpecialChar : "Lisää erikoismerkki",
+InsertSmileyLbl : "Hymiö",
+InsertSmiley : "Lisää hymiö",
+About : "FCKeditorista",
+Bold : "Lihavoitu",
+Italic : "Kursivoitu",
+Underline : "Alleviivattu",
+StrikeThrough : "Yliviivattu",
+Subscript : "Alaindeksi",
+Superscript : "Yläindeksi",
+LeftJustify : "Tasaa vasemmat reunat",
+CenterJustify : "Keskitä",
+RightJustify : "Tasaa oikeat reunat",
+BlockJustify : "Tasaa molemmat reunat",
+DecreaseIndent : "Pienennä sisennystä",
+IncreaseIndent : "Suurenna sisennystä",
+Undo : "Kumoa",
+Redo : "Toista",
+NumberedListLbl : "Numerointi",
+NumberedList : "Lisää/poista numerointi",
+BulletedListLbl : "Luottelomerkit",
+BulletedList : "Lisää/poista luottelomerkit",
+ShowTableBorders : "Näytä taulun rajat",
+ShowDetails : "Näytä muotoilu",
+Style : "Tyyli",
+FontFormat : "Muotoilu",
+Font : "Fontti",
+FontSize : "Koko",
+TextColor : "Tekstiväri",
+BGColor : "Taustaväri",
+Source : "Koodi",
+Find : "Etsi",
+Replace : "Korvaa",
+SpellCheck : "Tarkista oikeinkirjoitus",
+UniversalKeyboard : "Universaali näppäimistö",
+PageBreakLbl : "Sivun vaihto",
+PageBreak : "Lisää sivun vaihto",
+
+Form : "Lomake",
+Checkbox : "Valintaruutu",
+RadioButton : "Radiopainike",
+TextField : "Tekstikenttä",
+Textarea : "Tekstilaatikko",
+HiddenField : "Piilokenttä",
+Button : "Painike",
+SelectionField : "Valintakenttä",
+ImageButton : "Kuvapainike",
+
+FitWindow : "Suurenna editori koko ikkunaan",
+
+// Context Menu
+EditLink : "Muokkaa linkkiä",
+CellCM : "Solu",
+RowCM : "Rivi",
+ColumnCM : "Sarake",
+InsertRow : "Lisää rivi",
+DeleteRows : "Poista rivit",
+InsertColumn : "Lisää sarake",
+DeleteColumns : "Poista sarakkeet",
+InsertCell : "Lisää solu",
+DeleteCells : "Poista solut",
+MergeCells : "Yhdistä solut",
+SplitCell : "Jaa solu",
+TableDelete : "Poista taulu",
+CellProperties : "Solun ominaisuudet",
+TableProperties : "Taulun ominaisuudet",
+ImageProperties : "Kuvan ominaisuudet",
+FlashProperties : "Flash ominaisuudet",
+
+AnchorProp : "Ankkurin ominaisuudet",
+ButtonProp : "Painikkeen ominaisuudet",
+CheckboxProp : "Valintaruudun ominaisuudet",
+HiddenFieldProp : "Piilokentän ominaisuudet",
+RadioButtonProp : "Radiopainikkeen ominaisuudet",
+ImageButtonProp : "Kuvapainikkeen ominaisuudet",
+TextFieldProp : "Tekstikentän ominaisuudet",
+SelectionFieldProp : "Valintakentän ominaisuudet",
+TextareaProp : "Tekstilaatikon ominaisuudet",
+FormProp : "Lomakkeen ominaisuudet",
+
+FontFormats : "Normaali;Muotoiltu;Osoite;Otsikko 1;Otsikko 2;Otsikko 3;Otsikko 4;Otsikko 5;Otsikko 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Prosessoidaan XHTML:ää. Odota hetki...",
+Done : "Valmis",
+PasteWordConfirm : "Teksti, jonka haluat liittää, näyttää olevan kopioitu Wordista. Haluatko puhdistaa sen ennen liittämistä?",
+NotCompatiblePaste : "Tämä komento toimii vain Internet Explorer 5.5:ssa tai uudemmassa. Haluatko liittää ilman puhdistusta?",
+UnknownToolbarItem : "Tuntemanton työkalu \"%1\"",
+UnknownCommand : "Tuntematon komento \"%1\"",
+NotImplemented : "Komentoa ei ole liitetty sovellukseen",
+UnknownToolbarSet : "Työkalukokonaisuus \"%1\" ei ole olemassa",
+NoActiveX : "Selaimesi turvallisuusasetukset voivat rajoittaa joitain editorin ominaisuuksia. Sinun pitää ottaa käyttöön asetuksista \"Suorita ActiveX komponentit ja -plugin-laajennukset\". Saatat kohdata virheitä ja huomata puuttuvia ominaisuuksia.",
+BrowseServerBlocked : "Resurssiselainta ei voitu avata. Varmista, että ponnahdusikkunoiden estäjät eivät ole päällä.",
+DialogBlocked : "Apuikkunaa ei voitu avaata. Varmista, että ponnahdusikkunoiden estäjät eivät ole päällä.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Peruuta",
+DlgBtnClose : "Sulje",
+DlgBtnBrowseServer : "Selaa palvelinta",
+DlgAdvancedTag : "Lisäominaisuudet",
+DlgOpOther : "Muut",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Lisää URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ei asetettu>",
+DlgGenId : "Tunniste",
+DlgGenLangDir : "Kielen suunta",
+DlgGenLangDirLtr : "Vasemmalta oikealle (LTR)",
+DlgGenLangDirRtl : "Oikealta vasemmalle (RTL)",
+DlgGenLangCode : "Kielikoodi",
+DlgGenAccessKey : "Pikanäppäin",
+DlgGenName : "Nimi",
+DlgGenTabIndex : "Tabulaattori indeksi",
+DlgGenLongDescr : "Pitkän kuvauksen URL",
+DlgGenClass : "Tyyliluokat",
+DlgGenTitle : "Avustava otsikko",
+DlgGenContType : "Avustava sisällön tyyppi",
+DlgGenLinkCharset : "Linkitetty kirjaimisto",
+DlgGenStyle : "Tyyli",
+
+// Image Dialog
+DlgImgTitle : "Kuvan ominaisuudet",
+DlgImgInfoTab : "Kuvan tiedot",
+DlgImgBtnUpload : "Lähetä palvelimelle",
+DlgImgURL : "Osoite",
+DlgImgUpload : "Lisää kuva",
+DlgImgAlt : "Vaihtoehtoinen teksti",
+DlgImgWidth : "Leveys",
+DlgImgHeight : "Korkeus",
+DlgImgLockRatio : "Lukitse suhteet",
+DlgBtnResetSize : "Alkuperäinen koko",
+DlgImgBorder : "Raja",
+DlgImgHSpace : "Vaakatila",
+DlgImgVSpace : "Pystytila",
+DlgImgAlign : "Kohdistus",
+DlgImgAlignLeft : "Vasemmalle",
+DlgImgAlignAbsBottom: "Aivan alas",
+DlgImgAlignAbsMiddle: "Aivan keskelle",
+DlgImgAlignBaseline : "Alas (teksti)",
+DlgImgAlignBottom : "Alas",
+DlgImgAlignMiddle : "Keskelle",
+DlgImgAlignRight : "Oikealle",
+DlgImgAlignTextTop : "Ylös (teksti)",
+DlgImgAlignTop : "Ylös",
+DlgImgPreview : "Esikatselu",
+DlgImgAlertUrl : "Kirjoita kuvan osoite (URL)",
+DlgImgLinkTab : "Linkki",
+
+// Flash Dialog
+DlgFlashTitle : "Flash ominaisuudet",
+DlgFlashChkPlay : "Automaattinen käynnistys",
+DlgFlashChkLoop : "Toisto",
+DlgFlashChkMenu : "Näytä Flash-valikko",
+DlgFlashScale : "Levitä",
+DlgFlashScaleAll : "Näytä kaikki",
+DlgFlashScaleNoBorder : "Ei rajaa",
+DlgFlashScaleFit : "Tarkka koko",
+
+// Link Dialog
+DlgLnkWindowTitle : "Linkki",
+DlgLnkInfoTab : "Linkin tiedot",
+DlgLnkTargetTab : "Kohde",
+
+DlgLnkType : "Linkkityyppi",
+DlgLnkTypeURL : "Osoite",
+DlgLnkTypeAnchor : "Ankkuri tässä sivussa",
+DlgLnkTypeEMail : "Sähköposti",
+DlgLnkProto : "Protokolla",
+DlgLnkProtoOther : "<muu>",
+DlgLnkURL : "Osoite",
+DlgLnkAnchorSel : "Valitse ankkuri",
+DlgLnkAnchorByName : "Ankkurin nimen mukaan",
+DlgLnkAnchorById : "Ankkurin ID:n mukaan",
+DlgLnkNoAnchors : "<Ei ankkureita tässä dokumentissa>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Sähköpostiosoite",
+DlgLnkEMailSubject : "Aihe",
+DlgLnkEMailBody : "Viesti",
+DlgLnkUpload : "Lisää tiedosto",
+DlgLnkBtnUpload : "Lähetä palvelimelle",
+
+DlgLnkTarget : "Kohde",
+DlgLnkTargetFrame : "<kehys>",
+DlgLnkTargetPopup : "<popup ikkuna>",
+DlgLnkTargetBlank : "Uusi ikkuna (_blank)",
+DlgLnkTargetParent : "Emoikkuna (_parent)",
+DlgLnkTargetSelf : "Sama ikkuna (_self)",
+DlgLnkTargetTop : "Päällimmäisin ikkuna (_top)",
+DlgLnkTargetFrameName : "Kohdekehyksen nimi",
+DlgLnkPopWinName : "Popup ikkunan nimi",
+DlgLnkPopWinFeat : "Popup ikkunan ominaisuudet",
+DlgLnkPopResize : "Venytettävä",
+DlgLnkPopLocation : "Osoiterivi",
+DlgLnkPopMenu : "Valikkorivi",
+DlgLnkPopScroll : "Vierityspalkit",
+DlgLnkPopStatus : "Tilarivi",
+DlgLnkPopToolbar : "Vakiopainikkeet",
+DlgLnkPopFullScrn : "Täysi ikkuna (IE)",
+DlgLnkPopDependent : "Riippuva (Netscape)",
+DlgLnkPopWidth : "Leveys",
+DlgLnkPopHeight : "Korkeus",
+DlgLnkPopLeft : "Vasemmalta (px)",
+DlgLnkPopTop : "Ylhäältä (px)",
+
+DlnLnkMsgNoUrl : "Linkille on kirjoitettava URL",
+DlnLnkMsgNoEMail : "Kirjoita sähköpostiosoite",
+DlnLnkMsgNoAnchor : "Valitse ankkuri",
+DlnLnkMsgInvPopName : "Popup-ikkunan nimi pitää alkaa aakkosella ja ei saa sisältää välejä",
+
+// Color Dialog
+DlgColorTitle : "Valitse väri",
+DlgColorBtnClear : "Tyhjennä",
+DlgColorHighlight : "Kohdalla",
+DlgColorSelected : "Valittu",
+
+// Smiley Dialog
+DlgSmileyTitle : "Lisää hymiö",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Valitse erikoismerkki",
+
+// Table Dialog
+DlgTableTitle : "Taulun ominaisuudet",
+DlgTableRows : "Rivit",
+DlgTableColumns : "Sarakkeet",
+DlgTableBorder : "Rajan paksuus",
+DlgTableAlign : "Kohdistus",
+DlgTableAlignNotSet : "<ei asetettu>",
+DlgTableAlignLeft : "Vasemmalle",
+DlgTableAlignCenter : "Keskelle",
+DlgTableAlignRight : "Oikealle",
+DlgTableWidth : "Leveys",
+DlgTableWidthPx : "pikseliä",
+DlgTableWidthPc : "prosenttia",
+DlgTableHeight : "Korkeus",
+DlgTableCellSpace : "Solujen väli",
+DlgTableCellPad : "Solujen sisennys",
+DlgTableCaption : "Otsikko",
+DlgTableSummary : "Yhteenveto",
+
+// Table Cell Dialog
+DlgCellTitle : "Solun ominaisuudet",
+DlgCellWidth : "Leveys",
+DlgCellWidthPx : "pikseliä",
+DlgCellWidthPc : "prosenttia",
+DlgCellHeight : "Korkeus",
+DlgCellWordWrap : "Tekstikierrätys",
+DlgCellWordWrapNotSet : "<Ei asetettu>",
+DlgCellWordWrapYes : "Kyllä",
+DlgCellWordWrapNo : "Ei",
+DlgCellHorAlign : "Vaakakohdistus",
+DlgCellHorAlignNotSet : "<Ei asetettu>",
+DlgCellHorAlignLeft : "Vasemmalle",
+DlgCellHorAlignCenter : "Keskelle",
+DlgCellHorAlignRight: "Oikealle",
+DlgCellVerAlign : "Pystykohdistus",
+DlgCellVerAlignNotSet : "<Ei asetettu>",
+DlgCellVerAlignTop : "Ylös",
+DlgCellVerAlignMiddle : "Keskelle",
+DlgCellVerAlignBottom : "Alas",
+DlgCellVerAlignBaseline : "Tekstin alas",
+DlgCellRowSpan : "Rivin jatkuvuus",
+DlgCellCollSpan : "Sarakkeen jatkuvuus",
+DlgCellBackColor : "Taustaväri",
+DlgCellBorderColor : "Rajan väri",
+DlgCellBtnSelect : "Valitse...",
+
+// Find Dialog
+DlgFindTitle : "Etsi",
+DlgFindFindBtn : "Etsi",
+DlgFindNotFoundMsg : "Etsittyä tekstiä ei löytynyt.",
+
+// Replace Dialog
+DlgReplaceTitle : "Korvaa",
+DlgReplaceFindLbl : "Etsi mitä:",
+DlgReplaceReplaceLbl : "Korvaa tällä:",
+DlgReplaceCaseChk : "Sama kirjainkoko",
+DlgReplaceReplaceBtn : "Korvaa",
+DlgReplaceReplAllBtn : "Korvaa kaikki",
+DlgReplaceWordChk : "Koko sana",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Selaimesi turva-asetukset eivät salli editorin toteuttaa leikkaamista. Käytä näppäimistöä leikkaamiseen (Ctrl+X).",
+PasteErrorCopy : "Selaimesi turva-asetukset eivät salli editorin toteuttaa kopioimista. Käytä näppäimistöä kopioimiseen (Ctrl+C).",
+
+PasteAsText : "Liitä tekstinä",
+PasteFromWord : "Liitä Wordista",
+
+DlgPasteMsg2 : "Liitä painamalla (<STRONG>Ctrl+V</STRONG>) ja painamalla <STRONG>OK</STRONG>.",
+DlgPasteSec : "Selaimesi turva-asetukset eivät salli editorin käyttää leikepöytää suoraan. Sinun pitää suorittaa liittäminen tässä ikkunassa.",
+DlgPasteIgnoreFont : "Jätä huomioimatta fonttimääritykset",
+DlgPasteRemoveStyles : "Poista tyylimääritykset",
+DlgPasteCleanBox : "Tyhjennä",
+
+// Color Picker
+ColorAutomatic : "Automaattinen",
+ColorMoreColors : "Lisää värejä...",
+
+// Document Properties
+DocProps : "Dokumentin ominaisuudet",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ankkurin ominaisuudet",
+DlgAnchorName : "Nimi",
+DlgAnchorErrorName : "Ankkurille on kirjoitettava nimi",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ei sanakirjassa",
+DlgSpellChangeTo : "Vaihda",
+DlgSpellBtnIgnore : "Jätä huomioimatta",
+DlgSpellBtnIgnoreAll : "Jätä kaikki huomioimatta",
+DlgSpellBtnReplace : "Korvaa",
+DlgSpellBtnReplaceAll : "Korvaa kaikki",
+DlgSpellBtnUndo : "Kumoa",
+DlgSpellNoSuggestions : "Ei ehdotuksia",
+DlgSpellProgress : "Tarkistus käynnissä...",
+DlgSpellNoMispell : "Tarkistus valmis: Ei virheitä",
+DlgSpellNoChanges : "Tarkistus valmis: Yhtään sanaa ei muutettu",
+DlgSpellOneChange : "Tarkistus valmis: Yksi sana muutettiin",
+DlgSpellManyChanges : "Tarkistus valmis: %1 sanaa muutettiin",
+
+IeSpellDownload : "Oikeinkirjoituksen tarkistusta ei ole asennettu. Haluatko ladata sen nyt?",
+
+// Button Dialog
+DlgButtonText : "Teksti (arvo)",
+DlgButtonType : "Tyyppi",
+DlgButtonTypeBtn : "Painike",
+DlgButtonTypeSbm : "Lähetä",
+DlgButtonTypeRst : "Tyhjennä",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nimi",
+DlgCheckboxValue : "Arvo",
+DlgCheckboxSelected : "Valittu",
+
+// Form Dialog
+DlgFormName : "Nimi",
+DlgFormAction : "Toiminto",
+DlgFormMethod : "Tapa",
+
+// Select Field Dialog
+DlgSelectName : "Nimi",
+DlgSelectValue : "Arvo",
+DlgSelectSize : "Koko",
+DlgSelectLines : "Rivit",
+DlgSelectChkMulti : "Salli usea valinta",
+DlgSelectOpAvail : "Ominaisuudet",
+DlgSelectOpText : "Teksti",
+DlgSelectOpValue : "Arvo",
+DlgSelectBtnAdd : "Lisää",
+DlgSelectBtnModify : "Muuta",
+DlgSelectBtnUp : "Ylös",
+DlgSelectBtnDown : "Alas",
+DlgSelectBtnSetValue : "Aseta valituksi",
+DlgSelectBtnDelete : "Poista",
+
+// Textarea Dialog
+DlgTextareaName : "Nimi",
+DlgTextareaCols : "Sarakkeita",
+DlgTextareaRows : "Rivejä",
+
+// Text Field Dialog
+DlgTextName : "Nimi",
+DlgTextValue : "Arvo",
+DlgTextCharWidth : "Leveys",
+DlgTextMaxChars : "Maksimi merkkimäärä",
+DlgTextType : "Tyyppi",
+DlgTextTypeText : "Teksti",
+DlgTextTypePass : "Salasana",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nimi",
+DlgHiddenValue : "Arvo",
+
+// Bulleted List Dialog
+BulletedListProp : "Luettelon ominaisuudet",
+NumberedListProp : "Numeroinnin ominaisuudet",
+DlgLstStart : "Alku",
+DlgLstType : "Tyyppi",
+DlgLstTypeCircle : "Kehä",
+DlgLstTypeDisc : "Ympyrä",
+DlgLstTypeSquare : "Neliö",
+DlgLstTypeNumbers : "Numerot (1, 2, 3)",
+DlgLstTypeLCase : "Pienet kirjaimet (a, b, c)",
+DlgLstTypeUCase : "Isot kirjaimet (A, B, C)",
+DlgLstTypeSRoman : "Pienet roomalaiset numerot (i, ii, iii)",
+DlgLstTypeLRoman : "Isot roomalaiset numerot (Ii, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Yleiset",
+DlgDocBackTab : "Tausta",
+DlgDocColorsTab : "Värit ja marginaalit",
+DlgDocMetaTab : "Meta-tieto",
+
+DlgDocPageTitle : "Sivun nimi",
+DlgDocLangDir : "Kielen suunta",
+DlgDocLangDirLTR : "Vasemmalta oikealle (LTR)",
+DlgDocLangDirRTL : "Oikealta vasemmalle (RTL)",
+DlgDocLangCode : "Kielikoodi",
+DlgDocCharSet : "Merkistökoodaus",
+DlgDocCharSetCE : "Keskieurooppalainen",
+DlgDocCharSetCT : "Kiina, perinteinen (Big5)",
+DlgDocCharSetCR : "Kyrillinen",
+DlgDocCharSetGR : "Kreikka",
+DlgDocCharSetJP : "Japani",
+DlgDocCharSetKR : "Korealainen",
+DlgDocCharSetTR : "Turkkilainen",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Länsieurooppalainen",
+DlgDocCharSetOther : "Muu merkistökoodaus",
+
+DlgDocDocType : "Dokumentin tyyppi",
+DlgDocDocTypeOther : "Muu dokumentin tyyppi",
+DlgDocIncXHTML : "Lisää XHTML julistukset",
+DlgDocBgColor : "Taustaväri",
+DlgDocBgImage : "Taustakuva",
+DlgDocBgNoScroll : "Paikallaanpysyvä tausta",
+DlgDocCText : "Teksti",
+DlgDocCLink : "Linkki",
+DlgDocCVisited : "Vierailtu linkki",
+DlgDocCActive : "Aktiivinen linkki",
+DlgDocMargins : "Sivun marginaalit",
+DlgDocMaTop : "Ylä",
+DlgDocMaLeft : "Vasen",
+DlgDocMaRight : "Oikea",
+DlgDocMaBottom : "Ala",
+DlgDocMeIndex : "Hakusanat (pilkulla erotettuna)",
+DlgDocMeDescr : "Kuvaus",
+DlgDocMeAuthor : "Tekijä",
+DlgDocMeCopy : "Tekijänoikeudet",
+DlgDocPreview : "Esikatselu",
+
+// Templates Dialog
+Templates : "Pohjat",
+DlgTemplatesTitle : "Sisältöpohjat",
+DlgTemplatesSelMsg : "Valitse pohja editoriin<br>(aiempi sisältö menetetään):",
+DlgTemplatesLoading : "Ladataan listaa pohjista. Hetkinen...",
+DlgTemplatesNoTpl : "(Ei määriteltyjä pohjia)",
+DlgTemplatesReplace : "Korvaa editorin koko sisältö",
+
+// About Dialog
+DlgAboutAboutTab : "Editorista",
+DlgAboutBrowserInfoTab : "Selaimen tiedot",
+DlgAboutLicenseTab : "Lisenssi",
+DlgAboutVersion : "versio",
+DlgAboutInfo : "Lisää tietoa osoitteesta"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/fo.js b/httemplate/elements/fckeditor/editor/lang/fo.js
new file mode 100644
index 0000000..830c43e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/fo.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Faroese language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Fjal amboðsbjálkan",
+ToolbarExpand : "Vís amboðsbjálkan",
+
+// Toolbar Items and Context Menu
+Save : "Goym",
+NewPage : "Nýggj síða",
+Preview : "Frumsýning",
+Cut : "Kvett",
+Copy : "Avrita",
+Paste : "Innrita",
+PasteText : "Innrita reinan tekst",
+PasteWord : "Innrita frá Word",
+Print : "Prenta",
+SelectAll : "Markera alt",
+RemoveFormat : "Strika sniðgeving",
+InsertLinkLbl : "Tilknýti",
+InsertLink : "Ger/broyt tilknýti",
+RemoveLink : "Strika tilknýti",
+Anchor : "Ger/broyt marknastein",
+InsertImageLbl : "Myndir",
+InsertImage : "Set inn/broyt mynd",
+InsertFlashLbl : "Flash",
+InsertFlash : "Set inn/broyt Flash",
+InsertTableLbl : "Tabell",
+InsertTable : "Set inn/broyt tabell",
+InsertLineLbl : "Linja",
+InsertLine : "Ger vatnrætta linju",
+InsertSpecialCharLbl: "Sertekn",
+InsertSpecialChar : "Set inn sertekn",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Set inn Smiley",
+About : "Um FCKeditor",
+Bold : "Feit skrift",
+Italic : "Skráskrift",
+Underline : "Undirstrikað",
+StrikeThrough : "Yvirstrikað",
+Subscript : "Lækkað skrift",
+Superscript : "Hækkað skrift",
+LeftJustify : "Vinstrasett",
+CenterJustify : "Miðsett",
+RightJustify : "Høgrasett",
+BlockJustify : "Javnir tekstkantar",
+DecreaseIndent : "Minka reglubrotarinntriv",
+IncreaseIndent : "Økja reglubrotarinntriv",
+Undo : "Angra",
+Redo : "Vend aftur",
+NumberedListLbl : "Talmerktur listi",
+NumberedList : "Ger/strika talmerktan lista",
+BulletedListLbl : "Punktmerktur listi",
+BulletedList : "Ger/strika punktmerktan lista",
+ShowTableBorders : "Vís tabellbordar",
+ShowDetails : "Vís í smálutum",
+Style : "Typografi",
+FontFormat : "Skriftsnið",
+Font : "Skrift",
+FontSize : "Skriftstødd",
+TextColor : "Tekstlitur",
+BGColor : "Bakgrundslitur",
+Source : "Kelda",
+Find : "Leita",
+Replace : "Yvirskriva",
+SpellCheck : "Kanna stavseting",
+UniversalKeyboard : "Knappaborð",
+PageBreakLbl : "Síðuskift",
+PageBreak : "Ger síðuskift",
+
+Form : "Formur",
+Checkbox : "Flugubein",
+RadioButton : "Radioknøttur",
+TextField : "Tekstteigur",
+Textarea : "Tekstumráði",
+HiddenField : "Fjaldur teigur",
+Button : "Knøttur",
+SelectionField : "Valskrá",
+ImageButton : "Myndaknøttur",
+
+FitWindow : "Set tekstviðgera til fulla stødd",
+
+// Context Menu
+EditLink : "Broyt tilknýti",
+CellCM : "Meski",
+RowCM : "Rað",
+ColumnCM : "Kolonna",
+InsertRow : "Nýtt rað",
+DeleteRows : "Strika røðir",
+InsertColumn : "Nýggj kolonna",
+DeleteColumns : "Strika kolonnur",
+InsertCell : "Nýggjur meski",
+DeleteCells : "Strika meskar",
+MergeCells : "Flætta meskar",
+SplitCell : "Být sundur meskar",
+TableDelete : "Strika tabell",
+CellProperties : "Meskueginleikar",
+TableProperties : "Tabelleginleikar",
+ImageProperties : "Myndaeginleikar",
+FlashProperties : "Flash eginleikar",
+
+AnchorProp : "Eginleikar fyri marknastein",
+ButtonProp : "Eginleikar fyri knøtt",
+CheckboxProp : "Eginleikar fyri flugubein",
+HiddenFieldProp : "Eginleikar fyri fjaldan teig",
+RadioButtonProp : "Eginleikar fyri radioknøtt",
+ImageButtonProp : "Eginleikar fyri myndaknøtt",
+TextFieldProp : "Eginleikar fyri tekstteig",
+SelectionFieldProp : "Eginleikar fyri valskrá",
+TextareaProp : "Eginleikar fyri tekstumráði",
+FormProp : "Eginleikar fyri Form",
+
+FontFormats : "Vanligt;Sniðgivið;Adressa;Yvirskrift 1;Yvirskrift 2;Yvirskrift 3;Yvirskrift 4;Yvirskrift 5;Yvirskrift 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML verður viðgjørt. Bíða við...",
+Done : "Liðugt",
+PasteWordConfirm : "Teksturin, royndur verður at seta inn, tykist at stava frá Word. Vilt tú reinsa tekstin, áðrenn hann verður settur inn?",
+NotCompatiblePaste : "Hetta er bert tøkt í Internet Explorer 5.5 og nýggjari. Vilt tú seta tekstin inn kortini - óreinsaðan?",
+UnknownToolbarItem : "Ókendur lutur í amboðsbjálkanum \"%1\"",
+UnknownCommand : "Ókend kommando \"%1\"",
+NotImplemented : "Hetta er ikki tøkt í hesi útgávuni",
+UnknownToolbarSet : "Amboðsbjálkin \"%1\" finst ikki",
+NoActiveX : "Trygdaruppsetingin í alnótskaganum kann sum er avmarka onkrar hentleikar í tekstviðgeranum. Tú mást loyva møguleikanum \"Run/Kør ActiveX controls and plug-ins\". Tú kanst uppliva feilir og ávaringar um tvørrandi hentleikar.",
+BrowseServerBlocked : "Ambætarakagin kundi ikki opnast. Tryggja tær, at allar pop-up forðingar eru óvirknar.",
+DialogBlocked : "Tað eyðnaðist ikki at opna samskiftisrútin. Tryggja tær, at allar pop-up forðingar eru óvirknar.",
+
+// Dialogs
+DlgBtnOK : "Góðkent",
+DlgBtnCancel : "Avlýst",
+DlgBtnClose : "Lat aftur",
+DlgBtnBrowseServer : "Ambætarakagi",
+DlgAdvancedTag : "Fjølbroytt",
+DlgOpOther : "<Annað>",
+DlgInfoTab : "Upplýsingar",
+DlgAlertUrl : "Vinarliga veit ein URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ikki sett>",
+DlgGenId : "Id",
+DlgGenLangDir : "Tekstkós",
+DlgGenLangDirLtr : "Frá vinstru til høgru (LTR)",
+DlgGenLangDirRtl : "Frá høgru til vinstru (RTL)",
+DlgGenLangCode : "Málkoda",
+DlgGenAccessKey : "Snarvegisknappur",
+DlgGenName : "Navn",
+DlgGenTabIndex : "Inntriv indeks",
+DlgGenLongDescr : "Víðkað URL frágreiðing",
+DlgGenClass : "Typografi klassar",
+DlgGenTitle : "Vegleiðandi heiti",
+DlgGenContType : "Vegleiðandi innihaldsslag",
+DlgGenLinkCharset : "Atknýtt teknsett",
+DlgGenStyle : "Typografi",
+
+// Image Dialog
+DlgImgTitle : "Myndaeginleikar",
+DlgImgInfoTab : "Myndaupplýsingar",
+DlgImgBtnUpload : "Send til ambætaran",
+DlgImgURL : "URL",
+DlgImgUpload : "Send",
+DlgImgAlt : "Alternativur tekstur",
+DlgImgWidth : "Breidd",
+DlgImgHeight : "Hædd",
+DlgImgLockRatio : "Læs lutfallið",
+DlgBtnResetSize : "Upprunastødd",
+DlgImgBorder : "Bordi",
+DlgImgHSpace : "Høgri breddi",
+DlgImgVSpace : "Vinstri breddi",
+DlgImgAlign : "Justering",
+DlgImgAlignLeft : "Vinstra",
+DlgImgAlignAbsBottom: "Abs botnur",
+DlgImgAlignAbsMiddle: "Abs miðja",
+DlgImgAlignBaseline : "Basislinja",
+DlgImgAlignBottom : "Botnur",
+DlgImgAlignMiddle : "Miðja",
+DlgImgAlignRight : "Høgra",
+DlgImgAlignTextTop : "Tekst toppur",
+DlgImgAlignTop : "Ovast",
+DlgImgPreview : "Frumsýning",
+DlgImgAlertUrl : "Rita slóðina til myndina",
+DlgImgLinkTab : "Tilknýti",
+
+// Flash Dialog
+DlgFlashTitle : "Flash eginleikar",
+DlgFlashChkPlay : "Avspælingin byrjar sjálv",
+DlgFlashChkLoop : "Endurspæl",
+DlgFlashChkMenu : "Ger Flash skrá virkna",
+DlgFlashScale : "Skalering",
+DlgFlashScaleAll : "Vís alt",
+DlgFlashScaleNoBorder : "Eingin bordi",
+DlgFlashScaleFit : "Neyv skalering",
+
+// Link Dialog
+DlgLnkWindowTitle : "Tilknýti",
+DlgLnkInfoTab : "Tilknýtis upplýsingar",
+DlgLnkTargetTab : "Mál",
+
+DlgLnkType : "Tilknýtisslag",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Tilknýti til marknastein í tekstinum",
+DlgLnkTypeEMail : "Teldupostur",
+DlgLnkProto : "Protokoll",
+DlgLnkProtoOther : "<Annað>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Vel ein marknastein",
+DlgLnkAnchorByName : "Eftir navni á marknasteini",
+DlgLnkAnchorById : "Eftir element Id",
+DlgLnkNoAnchors : "(Eingir marknasteinar eru í hesum dokumentið)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Teldupost-adressa",
+DlgLnkEMailSubject : "Evni",
+DlgLnkEMailBody : "Breyðtekstur",
+DlgLnkUpload : "Send til ambætaran",
+DlgLnkBtnUpload : "Send til ambætaran",
+
+DlgLnkTarget : "Mál",
+DlgLnkTargetFrame : "<ramma>",
+DlgLnkTargetPopup : "<popup vindeyga>",
+DlgLnkTargetBlank : "Nýtt vindeyga (_blank)",
+DlgLnkTargetParent : "Upphavliga vindeygað (_parent)",
+DlgLnkTargetSelf : "Sama vindeygað (_self)",
+DlgLnkTargetTop : "Alt vindeygað (_top)",
+DlgLnkTargetFrameName : "Vís navn vindeygans",
+DlgLnkPopWinName : "Popup vindeygans navn",
+DlgLnkPopWinFeat : "Popup vindeygans víðkaðu eginleikar",
+DlgLnkPopResize : "Kann broyta stødd",
+DlgLnkPopLocation : "Adressulinja",
+DlgLnkPopMenu : "Skrábjálki",
+DlgLnkPopScroll : "Rullibjálki",
+DlgLnkPopStatus : "Støðufrágreiðingarbjálki",
+DlgLnkPopToolbar : "Amboðsbjálki",
+DlgLnkPopFullScrn : "Fullur skermur (IE)",
+DlgLnkPopDependent : "Bundið (Netscape)",
+DlgLnkPopWidth : "Breidd",
+DlgLnkPopHeight : "Hædd",
+DlgLnkPopLeft : "Frástøða frá vinstru",
+DlgLnkPopTop : "Frástøða frá íerva",
+
+DlnLnkMsgNoUrl : "Vinarliga skriva tilknýti (URL)",
+DlnLnkMsgNoEMail : "Vinarliga skriva teldupost-adressu",
+DlnLnkMsgNoAnchor : "Vinarliga vel marknastein",
+DlnLnkMsgInvPopName : "Popup navnið má byrja við bókstavi og má ikki hava millumrúm",
+
+// Color Dialog
+DlgColorTitle : "Vel lit",
+DlgColorBtnClear : "Strika alt",
+DlgColorHighlight : "Framhevja",
+DlgColorSelected : "Valt",
+
+// Smiley Dialog
+DlgSmileyTitle : "Vel Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Vel sertekn",
+
+// Table Dialog
+DlgTableTitle : "Eginleikar fyri tabell",
+DlgTableRows : "Røðir",
+DlgTableColumns : "Kolonnur",
+DlgTableBorder : "Bordabreidd",
+DlgTableAlign : "Justering",
+DlgTableAlignNotSet : "<Einki valt>",
+DlgTableAlignLeft : "Vinstrasett",
+DlgTableAlignCenter : "Miðsett",
+DlgTableAlignRight : "Høgrasett",
+DlgTableWidth : "Breidd",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "prosent",
+DlgTableHeight : "Hædd",
+DlgTableCellSpace : "Fjarstøða millum meskar",
+DlgTableCellPad : "Meskubreddi",
+DlgTableCaption : "Tabellfrágreiðing",
+DlgTableSummary : "Samandráttur",
+
+// Table Cell Dialog
+DlgCellTitle : "Mesku eginleikar",
+DlgCellWidth : "Breidd",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "prosent",
+DlgCellHeight : "Hædd",
+DlgCellWordWrap : "Orðkloyving",
+DlgCellWordWrapNotSet : "<Einki valt>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nei",
+DlgCellHorAlign : "Vatnrøtt justering",
+DlgCellHorAlignNotSet : "<Einki valt>",
+DlgCellHorAlignLeft : "Vinstrasett",
+DlgCellHorAlignCenter : "Miðsett",
+DlgCellHorAlignRight: "Høgrasett",
+DlgCellVerAlign : "Lodrøtt justering",
+DlgCellVerAlignNotSet : "<Ikki sett>",
+DlgCellVerAlignTop : "Ovast",
+DlgCellVerAlignMiddle : "Miðjan",
+DlgCellVerAlignBottom : "Niðast",
+DlgCellVerAlignBaseline : "Basislinja",
+DlgCellRowSpan : "Røðir, meskin fevnir um",
+DlgCellCollSpan : "Kolonnur, meskin fevnir um",
+DlgCellBackColor : "Bakgrundslitur",
+DlgCellBorderColor : "Litur á borda",
+DlgCellBtnSelect : "Vel...",
+
+// Find Dialog
+DlgFindTitle : "Finn",
+DlgFindFindBtn : "Finn",
+DlgFindNotFoundMsg : "Leititeksturin varð ikki funnin",
+
+// Replace Dialog
+DlgReplaceTitle : "Yvirskriva",
+DlgReplaceFindLbl : "Finn:",
+DlgReplaceReplaceLbl : "Yvirskriva við:",
+DlgReplaceCaseChk : "Munur á stórum og smáðum bókstavum",
+DlgReplaceReplaceBtn : "Yvirskriva",
+DlgReplaceReplAllBtn : "Yvirskriva alt",
+DlgReplaceWordChk : "Bert heil orð",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Trygdaruppseting alnótskagans forðar tekstviðgeranum í at kvetta tekstin. vinarliga nýt knappaborðið til at kvetta tekstin (CTRL+X).",
+PasteErrorCopy : "Trygdaruppseting alnótskagans forðar tekstviðgeranum í at avrita tekstin. Vinarliga nýt knappaborðið til at avrita tekstin (CTRL+C).",
+
+PasteAsText : "Innrita som reinan tekst",
+PasteFromWord : "Innrita fra Word",
+
+DlgPasteMsg2 : "Vinarliga koyr tekstin í hendan rútin við knappaborðinum (<strong>CTRL+V</strong>) og klikk á <strong>Góðtak</strong>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Forfjóna Font definitiónirnar",
+DlgPasteRemoveStyles : "Strika Styles definitiónir",
+DlgPasteCleanBox : "Reinskanarkassi",
+
+// Color Picker
+ColorAutomatic : "Av sær sjálvum",
+ColorMoreColors : "Fleiri litir...",
+
+// Document Properties
+DocProps : "Eginleikar fyri dokument",
+
+// Anchor Dialog
+DlgAnchorTitle : "Eginleikar fyri marknastein",
+DlgAnchorName : "Heiti marknasteinsins",
+DlgAnchorErrorName : "Vinarliga rita marknasteinsins heiti",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Finst ikki í orðabókini",
+DlgSpellChangeTo : "Broyt til",
+DlgSpellBtnIgnore : "Forfjóna",
+DlgSpellBtnIgnoreAll : "Forfjóna alt",
+DlgSpellBtnReplace : "Yvirskriva",
+DlgSpellBtnReplaceAll : "Yvirskriva alt",
+DlgSpellBtnUndo : "Angra",
+DlgSpellNoSuggestions : "- Einki uppskot -",
+DlgSpellProgress : "Rættstavarin arbeiðir...",
+DlgSpellNoMispell : "Rættstavarain liðugur: Eingin feilur funnin",
+DlgSpellNoChanges : "Rættstavarain liðugur: Einki orð varð broytt",
+DlgSpellOneChange : "Rættstavarain liðugur: Eitt orð er broytt",
+DlgSpellManyChanges : "Rættstavarain liðugur: %1 orð broytt",
+
+IeSpellDownload : "Rættstavarin er ikki tøkur í tekstviðgeranum. Vilt tú heinta hann nú?",
+
+// Button Dialog
+DlgButtonText : "Tekstur",
+DlgButtonType : "Slag",
+DlgButtonTypeBtn : "Knøttur",
+DlgButtonTypeSbm : "Send",
+DlgButtonTypeRst : "Nullstilla",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Navn",
+DlgCheckboxValue : "Virði",
+DlgCheckboxSelected : "Valt",
+
+// Form Dialog
+DlgFormName : "Navn",
+DlgFormAction : "Hending",
+DlgFormMethod : "Háttur",
+
+// Select Field Dialog
+DlgSelectName : "Navn",
+DlgSelectValue : "Virði",
+DlgSelectSize : "Stødd",
+DlgSelectLines : "Linjur",
+DlgSelectChkMulti : "Loyv fleiri valmøguleikum samstundis",
+DlgSelectOpAvail : "Tøkir møguleikar",
+DlgSelectOpText : "Tekstur",
+DlgSelectOpValue : "Virði",
+DlgSelectBtnAdd : "Legg afturat",
+DlgSelectBtnModify : "Broyt",
+DlgSelectBtnUp : "Upp",
+DlgSelectBtnDown : "Niður",
+DlgSelectBtnSetValue : "Set sum valt virði",
+DlgSelectBtnDelete : "Strika",
+
+// Textarea Dialog
+DlgTextareaName : "Navn",
+DlgTextareaCols : "kolonnur",
+DlgTextareaRows : "røðir",
+
+// Text Field Dialog
+DlgTextName : "Navn",
+DlgTextValue : "Virði",
+DlgTextCharWidth : "Breidd (sjónlig tekn)",
+DlgTextMaxChars : "Mest loyvdu tekn",
+DlgTextType : "Slag",
+DlgTextTypeText : "Tekstur",
+DlgTextTypePass : "Loyniorð",
+
+// Hidden Field Dialog
+DlgHiddenName : "Navn",
+DlgHiddenValue : "Virði",
+
+// Bulleted List Dialog
+BulletedListProp : "Eginleikar fyri punktmerktan lista",
+NumberedListProp : "Eginleikar fyri talmerktan lista",
+DlgLstStart : "Byrjan",
+DlgLstType : "Slag",
+DlgLstTypeCircle : "Sirkul",
+DlgLstTypeDisc : "Fyltur sirkul",
+DlgLstTypeSquare : "Fjórhyrningur",
+DlgLstTypeNumbers : "Talmerkt (1, 2, 3)",
+DlgLstTypeLCase : "Smáir bókstavir (a, b, c)",
+DlgLstTypeUCase : "Stórir bókstavir (A, B, C)",
+DlgLstTypeSRoman : "Smá rómaratøl (i, ii, iii)",
+DlgLstTypeLRoman : "Stór rómaratøl (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Generelt",
+DlgDocBackTab : "Bakgrund",
+DlgDocColorsTab : "Litir og breddar",
+DlgDocMetaTab : "META-upplýsingar",
+
+DlgDocPageTitle : "Síðuheiti",
+DlgDocLangDir : "Tekstkós",
+DlgDocLangDirLTR : "Frá vinstru móti høgru (LTR)",
+DlgDocLangDirRTL : "Frá høgru móti vinstru (RTL)",
+DlgDocLangCode : "Málkoda",
+DlgDocCharSet : "Teknsett koda",
+DlgDocCharSetCE : "Miðeuropa",
+DlgDocCharSetCT : "Kinesiskt traditionelt (Big5)",
+DlgDocCharSetCR : "Cyrilliskt",
+DlgDocCharSetGR : "Grikst",
+DlgDocCharSetJP : "Japanskt",
+DlgDocCharSetKR : "Koreanskt",
+DlgDocCharSetTR : "Turkiskt",
+DlgDocCharSetUN : "UNICODE (UTF-8)",
+DlgDocCharSetWE : "Vestureuropa",
+DlgDocCharSetOther : "Onnur teknsett koda",
+
+DlgDocDocType : "Dokumentslag yvirskrift",
+DlgDocDocTypeOther : "Annað dokumentslag yvirskrift",
+DlgDocIncXHTML : "Viðfest XHTML deklaratiónir",
+DlgDocBgColor : "Bakgrundslitur",
+DlgDocBgImage : "Leið til bakgrundsmynd (URL)",
+DlgDocBgNoScroll : "Læst bakgrund (rullar ikki)",
+DlgDocCText : "Tekstur",
+DlgDocCLink : "Tilknýti",
+DlgDocCVisited : "Vitjaði tilknýti",
+DlgDocCActive : "Virkin tilknýti",
+DlgDocMargins : "Síðubreddar",
+DlgDocMaTop : "Ovast",
+DlgDocMaLeft : "Vinstra",
+DlgDocMaRight : "Høgra",
+DlgDocMaBottom : "Niðast",
+DlgDocMeIndex : "Dokument index lyklaorð (sundurbýtt við komma)",
+DlgDocMeDescr : "Dokumentlýsing",
+DlgDocMeAuthor : "Høvundur",
+DlgDocMeCopy : "Upphavsrættindi",
+DlgDocPreview : "Frumsýning",
+
+// Templates Dialog
+Templates : "Skabelónir",
+DlgTemplatesTitle : "Innihaldsskabelónir",
+DlgTemplatesSelMsg : "Vinarliga vel ta skabelón, ið skal opnast í tekstviðgeranum<br>(Hetta yvirskrivar núverandi innihald):",
+DlgTemplatesLoading : "Heinti yvirlit yvir skabelónir. Vinarliga bíða við...",
+DlgTemplatesNoTpl : "(Ongar skabelónir tøkar)",
+DlgTemplatesReplace : "Yvirskriva núverandi innihald",
+
+// About Dialog
+DlgAboutAboutTab : "Um",
+DlgAboutBrowserInfoTab : "Upplýsingar um alnótskagan",
+DlgAboutLicenseTab : "License",
+DlgAboutVersion : "version",
+DlgAboutInfo : "Fyri fleiri upplýsingar, far til"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/fr.js b/httemplate/elements/fckeditor/editor/lang/fr.js
new file mode 100644
index 0000000..6d46fa0
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/fr.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * French language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Masquer Outils",
+ToolbarExpand : "Afficher Outils",
+
+// Toolbar Items and Context Menu
+Save : "Enregistrer",
+NewPage : "Nouvelle page",
+Preview : "Prévisualisation",
+Cut : "Couper",
+Copy : "Copier",
+Paste : "Coller",
+PasteText : "Coller comme texte",
+PasteWord : "Coller de Word",
+Print : "Imprimer",
+SelectAll : "Tout sélectionner",
+RemoveFormat : "Supprimer le format",
+InsertLinkLbl : "Lien",
+InsertLink : "Insérer/modifier le lien",
+RemoveLink : "Supprimer le lien",
+Anchor : "Insérer/modifier l'ancre",
+InsertImageLbl : "Image",
+InsertImage : "Insérer/modifier l'image",
+InsertFlashLbl : "Animation Flash",
+InsertFlash : "Insérer/modifier l'animation Flash",
+InsertTableLbl : "Tableau",
+InsertTable : "Insérer/modifier le tableau",
+InsertLineLbl : "Séparateur",
+InsertLine : "Insérer un séparateur",
+InsertSpecialCharLbl: "Caractères spéciaux",
+InsertSpecialChar : "Insérer un caractère spécial",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Insérer un Smiley",
+About : "A propos de FCKeditor",
+Bold : "Gras",
+Italic : "Italique",
+Underline : "Souligné",
+StrikeThrough : "Barré",
+Subscript : "Indice",
+Superscript : "Exposant",
+LeftJustify : "Aligné à gauche",
+CenterJustify : "Centré",
+RightJustify : "Aligné à Droite",
+BlockJustify : "Texte justifié",
+DecreaseIndent : "Diminuer le retrait",
+IncreaseIndent : "Augmenter le retrait",
+Undo : "Annuler",
+Redo : "Refaire",
+NumberedListLbl : "Liste numérotée",
+NumberedList : "Insérer/supprimer la liste numérotée",
+BulletedListLbl : "Liste à puces",
+BulletedList : "Insérer/supprimer la liste à puces",
+ShowTableBorders : "Afficher les bordures du tableau",
+ShowDetails : "Afficher les caractères invisibles",
+Style : "Style",
+FontFormat : "Format",
+Font : "Police",
+FontSize : "Taille",
+TextColor : "Couleur de caractère",
+BGColor : "Couleur de fond",
+Source : "Source",
+Find : "Chercher",
+Replace : "Remplacer",
+SpellCheck : "Orthographe",
+UniversalKeyboard : "Clavier universel",
+PageBreakLbl : "Saut de page",
+PageBreak : "Insérer un saut de page",
+
+Form : "Formulaire",
+Checkbox : "Case à cocher",
+RadioButton : "Bouton radio",
+TextField : "Champ texte",
+Textarea : "Zone de texte",
+HiddenField : "Champ caché",
+Button : "Bouton",
+SelectionField : "Liste/menu",
+ImageButton : "Bouton image",
+
+FitWindow : "Edition pleine page",
+
+// Context Menu
+EditLink : "Modifier le lien",
+CellCM : "Cellule",
+RowCM : "Ligne",
+ColumnCM : "Colonne",
+InsertRow : "Insérer une ligne",
+DeleteRows : "Supprimer des lignes",
+InsertColumn : "Insérer une colonne",
+DeleteColumns : "Supprimer des colonnes",
+InsertCell : "Insérer une cellule",
+DeleteCells : "Supprimer des cellules",
+MergeCells : "Fusionner les cellules",
+SplitCell : "Scinder les cellules",
+TableDelete : "Supprimer le tableau",
+CellProperties : "Propriétés de cellule",
+TableProperties : "Propriétés du tableau",
+ImageProperties : "Propriétés de l'image",
+FlashProperties : "Propriétés de l'animation Flash",
+
+AnchorProp : "Propriétés de l'ancre",
+ButtonProp : "Propriétés du bouton",
+CheckboxProp : "Propriétés de la case à cocher",
+HiddenFieldProp : "Propriétés du champ caché",
+RadioButtonProp : "Propriétés du bouton radio",
+ImageButtonProp : "Propriétés du bouton image",
+TextFieldProp : "Propriétés du champ texte",
+SelectionFieldProp : "Propriétés de la liste/du menu",
+TextareaProp : "Propriétés de la zone de texte",
+FormProp : "Propriétés du formulaire",
+
+FontFormats : "Normal;Formaté;Adresse;En-tête 1;En-tête 2;En-tête 3;En-tête 4;En-tête 5;En-tête 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Calcul XHTML. Veuillez patienter...",
+Done : "Terminé",
+PasteWordConfirm : "Le texte à coller semble provenir de Word. Désirez-vous le nettoyer avant de coller?",
+NotCompatiblePaste : "Cette commande nécessite Internet Explorer version 5.5 minimum. Souhaitez-vous coller sans nettoyage?",
+UnknownToolbarItem : "Elément de barre d'outil inconnu \"%1\"",
+UnknownCommand : "Nom de commande inconnu \"%1\"",
+NotImplemented : "Commande non encore écrite",
+UnknownToolbarSet : "La barre d'outils \"%1\" n'existe pas",
+NoActiveX : "Les paramètres de sécurité de votre navigateur peuvent limiter quelques fonctionnalités de l'éditeur. Veuillez activer l'option \"Exécuter les contrôles ActiveX et les plug-ins\". Il se peut que vous rencontriez des erreurs et remarquiez quelques limitations.",
+BrowseServerBlocked : "Le navigateur n'a pas pu être ouvert. Assurez-vous que les bloqueurs de popups soient désactivés.",
+DialogBlocked : "La fenêtre de dialogue n'a pas pu s'ouvrir. Assurez-vous que les bloqueurs de popups soient désactivés.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Annuler",
+DlgBtnClose : "Fermer",
+DlgBtnBrowseServer : "Parcourir le serveur",
+DlgAdvancedTag : "Avancé",
+DlgOpOther : "<Autre>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Veuillez saisir l'URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<Par défaut>",
+DlgGenId : "Id",
+DlgGenLangDir : "Sens d'écriture",
+DlgGenLangDirLtr : "De gauche à droite (LTR)",
+DlgGenLangDirRtl : "De droite à gauche (RTL)",
+DlgGenLangCode : "Code langue",
+DlgGenAccessKey : "Equivalent clavier",
+DlgGenName : "Nom",
+DlgGenTabIndex : "Ordre de tabulation",
+DlgGenLongDescr : "URL de description longue",
+DlgGenClass : "Classes de feuilles de style",
+DlgGenTitle : "Titre",
+DlgGenContType : "Type de contenu",
+DlgGenLinkCharset : "Encodage de caractère",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "Propriétés de l'image",
+DlgImgInfoTab : "Informations sur l'image",
+DlgImgBtnUpload : "Envoyer sur le serveur",
+DlgImgURL : "URL",
+DlgImgUpload : "Télécharger",
+DlgImgAlt : "Texte de remplacement",
+DlgImgWidth : "Largeur",
+DlgImgHeight : "Hauteur",
+DlgImgLockRatio : "Garder les proportions",
+DlgBtnResetSize : "Taille originale",
+DlgImgBorder : "Bordure",
+DlgImgHSpace : "Espacement horizontal",
+DlgImgVSpace : "Espacement vertical",
+DlgImgAlign : "Alignement",
+DlgImgAlignLeft : "Gauche",
+DlgImgAlignAbsBottom: "Abs Bas",
+DlgImgAlignAbsMiddle: "Abs Milieu",
+DlgImgAlignBaseline : "Bas du texte",
+DlgImgAlignBottom : "Bas",
+DlgImgAlignMiddle : "Milieu",
+DlgImgAlignRight : "Droite",
+DlgImgAlignTextTop : "Haut du texte",
+DlgImgAlignTop : "Haut",
+DlgImgPreview : "Prévisualisation",
+DlgImgAlertUrl : "Veuillez saisir l'URL de l'image",
+DlgImgLinkTab : "Lien",
+
+// Flash Dialog
+DlgFlashTitle : "Propriétés de l'animation Flash",
+DlgFlashChkPlay : "Lecture automatique",
+DlgFlashChkLoop : "Boucle",
+DlgFlashChkMenu : "Activer le menu Flash",
+DlgFlashScale : "Affichage",
+DlgFlashScaleAll : "Par défaut (tout montrer)",
+DlgFlashScaleNoBorder : "Sans bordure",
+DlgFlashScaleFit : "Ajuster aux dimensions",
+
+// Link Dialog
+DlgLnkWindowTitle : "Propriétés du lien",
+DlgLnkInfoTab : "Informations sur le lien",
+DlgLnkTargetTab : "Destination",
+
+DlgLnkType : "Type de lien",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Ancre dans cette page",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocole",
+DlgLnkProtoOther : "<autre>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Sélectionner une ancre",
+DlgLnkAnchorByName : "Par nom",
+DlgLnkAnchorById : "Par id",
+DlgLnkNoAnchors : "<Pas d'ancre disponible dans le document>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Adresse E-Mail",
+DlgLnkEMailSubject : "Sujet du message",
+DlgLnkEMailBody : "Corps du message",
+DlgLnkUpload : "Télécharger",
+DlgLnkBtnUpload : "Envoyer sur le serveur",
+
+DlgLnkTarget : "Destination",
+DlgLnkTargetFrame : "<cadre>",
+DlgLnkTargetPopup : "<fenêtre popup>",
+DlgLnkTargetBlank : "Nouvelle fenêtre (_blank)",
+DlgLnkTargetParent : "Fenêtre mère (_parent)",
+DlgLnkTargetSelf : "Même fenêtre (_self)",
+DlgLnkTargetTop : "Fenêtre supérieure (_top)",
+DlgLnkTargetFrameName : "Nom du cadre de destination",
+DlgLnkPopWinName : "Nom de la fenêtre popup",
+DlgLnkPopWinFeat : "Caractéristiques de la fenêtre popup",
+DlgLnkPopResize : "Taille modifiable",
+DlgLnkPopLocation : "Barre d'adresses",
+DlgLnkPopMenu : "Barre de menu",
+DlgLnkPopScroll : "Barres de défilement",
+DlgLnkPopStatus : "Barre d'état",
+DlgLnkPopToolbar : "Barre d'outils",
+DlgLnkPopFullScrn : "Plein écran (IE)",
+DlgLnkPopDependent : "Dépendante (Netscape)",
+DlgLnkPopWidth : "Largeur",
+DlgLnkPopHeight : "Hauteur",
+DlgLnkPopLeft : "Position à partir de la gauche",
+DlgLnkPopTop : "Position à partir du haut",
+
+DlnLnkMsgNoUrl : "Veuillez saisir l'URL",
+DlnLnkMsgNoEMail : "Veuillez saisir l'adresse e-mail",
+DlnLnkMsgNoAnchor : "Veuillez sélectionner une ancre",
+DlnLnkMsgInvPopName : "Le nom de la fenêtre popup doit commencer par une lettre et ne doit pas contenir d'espace",
+
+// Color Dialog
+DlgColorTitle : "Sélectionner",
+DlgColorBtnClear : "Effacer",
+DlgColorHighlight : "Prévisualisation",
+DlgColorSelected : "Sélectionné",
+
+// Smiley Dialog
+DlgSmileyTitle : "Insérer un Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Insérer un caractère spécial",
+
+// Table Dialog
+DlgTableTitle : "Propriétés du tableau",
+DlgTableRows : "Lignes",
+DlgTableColumns : "Colonnes",
+DlgTableBorder : "Bordure",
+DlgTableAlign : "Alignement",
+DlgTableAlignNotSet : "<Par défaut>",
+DlgTableAlignLeft : "Gauche",
+DlgTableAlignCenter : "Centré",
+DlgTableAlignRight : "Droite",
+DlgTableWidth : "Largeur",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "pourcentage",
+DlgTableHeight : "Hauteur",
+DlgTableCellSpace : "Espacement",
+DlgTableCellPad : "Contour",
+DlgTableCaption : "Titre",
+DlgTableSummary : "Résumé",
+
+// Table Cell Dialog
+DlgCellTitle : "Propriétés de la cellule",
+DlgCellWidth : "Largeur",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "pourcentage",
+DlgCellHeight : "Hauteur",
+DlgCellWordWrap : "Retour à la ligne",
+DlgCellWordWrapNotSet : "<Par défaut>",
+DlgCellWordWrapYes : "Oui",
+DlgCellWordWrapNo : "Non",
+DlgCellHorAlign : "Alignement horizontal",
+DlgCellHorAlignNotSet : "<Par défaut>",
+DlgCellHorAlignLeft : "Gauche",
+DlgCellHorAlignCenter : "Centré",
+DlgCellHorAlignRight: "Droite",
+DlgCellVerAlign : "Alignement vertical",
+DlgCellVerAlignNotSet : "<Par défaut>",
+DlgCellVerAlignTop : "Haut",
+DlgCellVerAlignMiddle : "Milieu",
+DlgCellVerAlignBottom : "Bas",
+DlgCellVerAlignBaseline : "Bas du texte",
+DlgCellRowSpan : "Lignes fusionnées",
+DlgCellCollSpan : "Colonnes fusionnées",
+DlgCellBackColor : "Fond",
+DlgCellBorderColor : "Bordure",
+DlgCellBtnSelect : "Choisir...",
+
+// Find Dialog
+DlgFindTitle : "Chercher",
+DlgFindFindBtn : "Chercher",
+DlgFindNotFoundMsg : "Le texte indiqué est introuvable.",
+
+// Replace Dialog
+DlgReplaceTitle : "Remplacer",
+DlgReplaceFindLbl : "Rechercher:",
+DlgReplaceReplaceLbl : "Remplacer par:",
+DlgReplaceCaseChk : "Respecter la casse",
+DlgReplaceReplaceBtn : "Remplacer",
+DlgReplaceReplAllBtn : "Tout remplacer",
+DlgReplaceWordChk : "Mot entier",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Les paramètres de sécurité de votre navigateur empêchent l'éditeur de couper automatiquement vos données. Veuillez utiliser les équivalents claviers (Ctrl+X).",
+PasteErrorCopy : "Les paramètres de sécurité de votre navigateur empêchent l'éditeur de copier automatiquement vos données. Veuillez utiliser les équivalents claviers (Ctrl+C).",
+
+PasteAsText : "Coller comme texte",
+PasteFromWord : "Coller à partir de Word",
+
+DlgPasteMsg2 : "Veuillez coller dans la zone ci-dessous en utilisant le clavier (<STRONG>Ctrl+V</STRONG>) et cliquez sur <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorer les polices de caractères",
+DlgPasteRemoveStyles : "Supprimer les styles",
+DlgPasteCleanBox : "Effacer le contenu",
+
+// Color Picker
+ColorAutomatic : "Automatique",
+ColorMoreColors : "Plus de couleurs...",
+
+// Document Properties
+DocProps : "Propriétés du document",
+
+// Anchor Dialog
+DlgAnchorTitle : "Propriétés de l'ancre",
+DlgAnchorName : "Nom de l'ancre",
+DlgAnchorErrorName : "Veuillez saisir le nom de l'ancre",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Pas dans le dictionnaire",
+DlgSpellChangeTo : "Changer en",
+DlgSpellBtnIgnore : "Ignorer",
+DlgSpellBtnIgnoreAll : "Ignorer tout",
+DlgSpellBtnReplace : "Remplacer",
+DlgSpellBtnReplaceAll : "Remplacer tout",
+DlgSpellBtnUndo : "Annuler",
+DlgSpellNoSuggestions : "- Aucune suggestion -",
+DlgSpellProgress : "Vérification d'orthographe en cours...",
+DlgSpellNoMispell : "Vérification d'orthographe terminée: Aucune erreur trouvée",
+DlgSpellNoChanges : "Vérification d'orthographe terminée: Pas de modifications",
+DlgSpellOneChange : "Vérification d'orthographe terminée: Un mot modifié",
+DlgSpellManyChanges : "Vérification d'orthographe terminée: %1 mots modifiés",
+
+IeSpellDownload : "Le Correcteur n'est pas installé. Souhaitez-vous le télécharger maintenant?",
+
+// Button Dialog
+DlgButtonText : "Texte (valeur)",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Bouton",
+DlgButtonTypeSbm : "Envoyer",
+DlgButtonTypeRst : "Réinitialiser",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nom",
+DlgCheckboxValue : "Valeur",
+DlgCheckboxSelected : "Sélectionné",
+
+// Form Dialog
+DlgFormName : "Nom",
+DlgFormAction : "Action",
+DlgFormMethod : "Méthode",
+
+// Select Field Dialog
+DlgSelectName : "Nom",
+DlgSelectValue : "Valeur",
+DlgSelectSize : "Taille",
+DlgSelectLines : "lignes",
+DlgSelectChkMulti : "Sélection multiple",
+DlgSelectOpAvail : "Options disponibles",
+DlgSelectOpText : "Texte",
+DlgSelectOpValue : "Valeur",
+DlgSelectBtnAdd : "Ajouter",
+DlgSelectBtnModify : "Modifier",
+DlgSelectBtnUp : "Monter",
+DlgSelectBtnDown : "Descendre",
+DlgSelectBtnSetValue : "Valeur sélectionnée",
+DlgSelectBtnDelete : "Supprimer",
+
+// Textarea Dialog
+DlgTextareaName : "Nom",
+DlgTextareaCols : "Colonnes",
+DlgTextareaRows : "Lignes",
+
+// Text Field Dialog
+DlgTextName : "Nom",
+DlgTextValue : "Valeur",
+DlgTextCharWidth : "Largeur en caractères",
+DlgTextMaxChars : "Nombre maximum de caractères",
+DlgTextType : "Type",
+DlgTextTypeText : "Texte",
+DlgTextTypePass : "Mot de passe",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nom",
+DlgHiddenValue : "Valeur",
+
+// Bulleted List Dialog
+BulletedListProp : "Propriétés de liste à puces",
+NumberedListProp : "Propriétés de liste numérotée",
+DlgLstStart : "Début",
+DlgLstType : "Type",
+DlgLstTypeCircle : "Cercle",
+DlgLstTypeDisc : "Disque",
+DlgLstTypeSquare : "Carré",
+DlgLstTypeNumbers : "Nombres (1, 2, 3)",
+DlgLstTypeLCase : "Lettres minuscules (a, b, c)",
+DlgLstTypeUCase : "Lettres majuscules (A, B, C)",
+DlgLstTypeSRoman : "Chiffres romains minuscules (i, ii, iii)",
+DlgLstTypeLRoman : "Chiffres romains majuscules (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Général",
+DlgDocBackTab : "Fond",
+DlgDocColorsTab : "Couleurs et marges",
+DlgDocMetaTab : "Métadonnées",
+
+DlgDocPageTitle : "Titre de la page",
+DlgDocLangDir : "Sens d'écriture",
+DlgDocLangDirLTR : "De la gauche vers la droite (LTR)",
+DlgDocLangDirRTL : "De la droite vers la gauche (RTL)",
+DlgDocLangCode : "Code langue",
+DlgDocCharSet : "Encodage de caractère",
+DlgDocCharSetCE : "Europe Centrale",
+DlgDocCharSetCT : "Chinois Traditionnel (Big5)",
+DlgDocCharSetCR : "Cyrillique",
+DlgDocCharSetGR : "Grec",
+DlgDocCharSetJP : "Japanais",
+DlgDocCharSetKR : "Coréen",
+DlgDocCharSetTR : "Turc",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Occidental",
+DlgDocCharSetOther : "Autre encodage de caractère",
+
+DlgDocDocType : "Type de document",
+DlgDocDocTypeOther : "Autre type de document",
+DlgDocIncXHTML : "Inclure les déclarations XHTML",
+DlgDocBgColor : "Couleur de fond",
+DlgDocBgImage : "Image de fond",
+DlgDocBgNoScroll : "Image fixe sans défilement",
+DlgDocCText : "Texte",
+DlgDocCLink : "Lien",
+DlgDocCVisited : "Lien visité",
+DlgDocCActive : "Lien activé",
+DlgDocMargins : "Marges",
+DlgDocMaTop : "Haut",
+DlgDocMaLeft : "Gauche",
+DlgDocMaRight : "Droite",
+DlgDocMaBottom : "Bas",
+DlgDocMeIndex : "Mots-clés (séparés par des virgules)",
+DlgDocMeDescr : "Description",
+DlgDocMeAuthor : "Auteur",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Prévisualisation",
+
+// Templates Dialog
+Templates : "Modèles",
+DlgTemplatesTitle : "Modèles de contenu",
+DlgTemplatesSelMsg : "Veuillez sélectionner le modèle à ouvrir dans l'éditeur<br>(le contenu actuel sera remplacé):",
+DlgTemplatesLoading : "Chargement de la liste des modèles. Veuillez patienter...",
+DlgTemplatesNoTpl : "(Aucun modèle disponible)",
+DlgTemplatesReplace : "Remplacer tout le contenu",
+
+// About Dialog
+DlgAboutAboutTab : "A propos de",
+DlgAboutBrowserInfoTab : "Navigateur",
+DlgAboutLicenseTab : "License",
+DlgAboutVersion : "version",
+DlgAboutInfo : "Pour plus d'informations, aller à"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/gl.js b/httemplate/elements/fckeditor/editor/lang/gl.js
new file mode 100644
index 0000000..238d108
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/gl.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Galician language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Ocultar Ferramentas",
+ToolbarExpand : "Mostrar Ferramentas",
+
+// Toolbar Items and Context Menu
+Save : "Gardar",
+NewPage : "Nova Páxina",
+Preview : "Vista Previa",
+Cut : "Cortar",
+Copy : "Copiar",
+Paste : "Pegar",
+PasteText : "Pegar como texto plano",
+PasteWord : "Pegar dende Word",
+Print : "Imprimir",
+SelectAll : "Seleccionar todo",
+RemoveFormat : "Eliminar Formato",
+InsertLinkLbl : "Ligazón",
+InsertLink : "Inserir/Editar Ligazón",
+RemoveLink : "Eliminar Ligazón",
+Anchor : "Inserir/Editar Referencia",
+InsertImageLbl : "Imaxe",
+InsertImage : "Inserir/Editar Imaxe",
+InsertFlashLbl : "Flash",
+InsertFlash : "Inserir/Editar Flash",
+InsertTableLbl : "Tabla",
+InsertTable : "Inserir/Editar Tabla",
+InsertLineLbl : "Liña",
+InsertLine : "Inserir Liña Horizontal",
+InsertSpecialCharLbl: "Carácter Special",
+InsertSpecialChar : "Inserir Carácter Especial",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Inserir Smiley",
+About : "Acerca de FCKeditor",
+Bold : "Negrita",
+Italic : "Cursiva",
+Underline : "Sub-raiado",
+StrikeThrough : "Tachado",
+Subscript : "Subíndice",
+Superscript : "Superíndice",
+LeftJustify : "Aliñar á Esquerda",
+CenterJustify : "Centrado",
+RightJustify : "Aliñar á Dereita",
+BlockJustify : "Xustificado",
+DecreaseIndent : "Disminuir Sangría",
+IncreaseIndent : "Aumentar Sangría",
+Undo : "Desfacer",
+Redo : "Refacer",
+NumberedListLbl : "Lista Numerada",
+NumberedList : "Inserir/Eliminar Lista Numerada",
+BulletedListLbl : "Marcas",
+BulletedList : "Inserir/Eliminar Marcas",
+ShowTableBorders : "Mostrar Bordes das Táboas",
+ShowDetails : "Mostrar Marcas Parágrafo",
+Style : "Estilo",
+FontFormat : "Formato",
+Font : "Tipo",
+FontSize : "Tamaño",
+TextColor : "Cor do Texto",
+BGColor : "Cor do Fondo",
+Source : "Código Fonte",
+Find : "Procurar",
+Replace : "Substituir",
+SpellCheck : "Corrección Ortográfica",
+UniversalKeyboard : "Teclado Universal",
+PageBreakLbl : "Salto de Páxina",
+PageBreak : "Inserir Salto de Páxina",
+
+Form : "Formulario",
+Checkbox : "Cadro de Verificación",
+RadioButton : "Botón de Radio",
+TextField : "Campo de Texto",
+Textarea : "Ãrea de Texto",
+HiddenField : "Campo Oculto",
+Button : "Botón",
+SelectionField : "Campo de Selección",
+ImageButton : "Botón de Imaxe",
+
+FitWindow : "Maximizar o tamaño do editor",
+
+// Context Menu
+EditLink : "Editar Ligazón",
+CellCM : "Cela",
+RowCM : "Fila",
+ColumnCM : "Columna",
+InsertRow : "Inserir Fila",
+DeleteRows : "Borrar Filas",
+InsertColumn : "Inserir Columna",
+DeleteColumns : "Borrar Columnas",
+InsertCell : "Inserir Cela",
+DeleteCells : "Borrar Cela",
+MergeCells : "Unir Celas",
+SplitCell : "Partir Celas",
+TableDelete : "Borrar Táboa",
+CellProperties : "Propriedades da Cela",
+TableProperties : "Propriedades da Táboa",
+ImageProperties : "Propriedades Imaxe",
+FlashProperties : "Propriedades Flash",
+
+AnchorProp : "Propriedades da Referencia",
+ButtonProp : "Propriedades do Botón",
+CheckboxProp : "Propriedades do Cadro de Verificación",
+HiddenFieldProp : "Propriedades do Campo Oculto",
+RadioButtonProp : "Propriedades do Botón de Radio",
+ImageButtonProp : "Propriedades do Botón de Imaxe",
+TextFieldProp : "Propriedades do Campo de Texto",
+SelectionFieldProp : "Propriedades do Campo de Selección",
+TextareaProp : "Propriedades da Ãrea de Texto",
+FormProp : "Propriedades do Formulario",
+
+FontFormats : "Normal;Formateado;Enderezo;Enacabezado 1;Encabezado 2;Encabezado 3;Encabezado 4;Encabezado 5;Encabezado 6;Paragraph (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Procesando XHTML. Por facor, agarde...",
+Done : "Feiro",
+PasteWordConfirm : "Parece que o texto que quere pegar está copiado do Word.¿Quere limpar o formato antes de pegalo?",
+NotCompatiblePaste : "Este comando está disponible para Internet Explorer versión 5.5 ou superior. ¿Quere pegalo sen limpar o formato?",
+UnknownToolbarItem : "Ãtem de ferramentas descoñecido \"%1\"",
+UnknownCommand : "Nome de comando descoñecido \"%1\"",
+NotImplemented : "Comando non implementado",
+UnknownToolbarSet : "O conxunto de ferramentas \"%1\" non existe",
+NoActiveX : "As opcións de seguridade do seu navegador poderían limitar algunha das características de editor. Debe activar a opción \"Executar controis ActiveX e plug-ins\". Pode notar que faltan características e experimentar erros",
+BrowseServerBlocked : "Non se poido abrir o navegador de recursos. Asegúrese de que están desactivados os bloqueadores de xanelas emerxentes",
+DialogBlocked : "Non foi posible abrir a xanela de diálogo. Asegúrese de que están desactivados os bloqueadores de xanelas emerxentes",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancelar",
+DlgBtnClose : "Pechar",
+DlgBtnBrowseServer : "Navegar no Servidor",
+DlgAdvancedTag : "Advanzado",
+DlgOpOther : "<Outro>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Por favor, insira a URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<non definido>",
+DlgGenId : "Id",
+DlgGenLangDir : "Orientación do Idioma",
+DlgGenLangDirLtr : "Esquerda a Dereita (LTR)",
+DlgGenLangDirRtl : "Dereita a Esquerda (RTL)",
+DlgGenLangCode : "Código do Idioma",
+DlgGenAccessKey : "Chave de Acceso",
+DlgGenName : "Nome",
+DlgGenTabIndex : "Ãndice de Tabulación",
+DlgGenLongDescr : "Descrición Completa da URL",
+DlgGenClass : "Clases da Folla de Estilos",
+DlgGenTitle : "Título",
+DlgGenContType : "Tipo de Contido",
+DlgGenLinkCharset : "Fonte de Caracteres Vinculado",
+DlgGenStyle : "Estilo",
+
+// Image Dialog
+DlgImgTitle : "Propriedades da Imaxe",
+DlgImgInfoTab : "Información da Imaxe",
+DlgImgBtnUpload : "Enviar ó Servidor",
+DlgImgURL : "URL",
+DlgImgUpload : "Carregar",
+DlgImgAlt : "Texto Alternativo",
+DlgImgWidth : "Largura",
+DlgImgHeight : "Altura",
+DlgImgLockRatio : "Proporcional",
+DlgBtnResetSize : "Tamaño Orixinal",
+DlgImgBorder : "Límite",
+DlgImgHSpace : "Esp. Horiz.",
+DlgImgVSpace : "Esp. Vert.",
+DlgImgAlign : "Aliñamento",
+DlgImgAlignLeft : "Esquerda",
+DlgImgAlignAbsBottom: "Abs Inferior",
+DlgImgAlignAbsMiddle: "Abs Centro",
+DlgImgAlignBaseline : "Liña Base",
+DlgImgAlignBottom : "Pé",
+DlgImgAlignMiddle : "Centro",
+DlgImgAlignRight : "Dereita",
+DlgImgAlignTextTop : "Tope do Texto",
+DlgImgAlignTop : "Tope",
+DlgImgPreview : "Vista Previa",
+DlgImgAlertUrl : "Por favor, escriba a URL da imaxe",
+DlgImgLinkTab : "Ligazón",
+
+// Flash Dialog
+DlgFlashTitle : "Propriedades Flash",
+DlgFlashChkPlay : "Auto Execución",
+DlgFlashChkLoop : "Bucle",
+DlgFlashChkMenu : "Activar Menú Flash",
+DlgFlashScale : "Escalar",
+DlgFlashScaleAll : "Amosar Todo",
+DlgFlashScaleNoBorder : "Sen Borde",
+DlgFlashScaleFit : "Encaixar axustando",
+
+// Link Dialog
+DlgLnkWindowTitle : "Ligazón",
+DlgLnkInfoTab : "Información da Ligazón",
+DlgLnkTargetTab : "Referencia a esta páxina",
+
+DlgLnkType : "Tipo de Ligazón",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Referencia nesta páxina",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocolo",
+DlgLnkProtoOther : "<outro>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Seleccionar unha Referencia",
+DlgLnkAnchorByName : "Por Nome de Referencia",
+DlgLnkAnchorById : "Por Element Id",
+DlgLnkNoAnchors : "<Non hai referencias disponibles no documento>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Enderezo de E-Mail",
+DlgLnkEMailSubject : "Asunto do Mensaxe",
+DlgLnkEMailBody : "Corpo do Mensaxe",
+DlgLnkUpload : "Carregar",
+DlgLnkBtnUpload : "Enviar ó servidor",
+
+DlgLnkTarget : "Destino",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<Xanela Emerxente>",
+DlgLnkTargetBlank : "Nova Xanela (_blank)",
+DlgLnkTargetParent : "Xanela Pai (_parent)",
+DlgLnkTargetSelf : "Mesma Xanela (_self)",
+DlgLnkTargetTop : "Xanela Primaria (_top)",
+DlgLnkTargetFrameName : "Nome do Marco Destino",
+DlgLnkPopWinName : "Nome da Xanela Emerxente",
+DlgLnkPopWinFeat : "Características da Xanela Emerxente",
+DlgLnkPopResize : "Axustable",
+DlgLnkPopLocation : "Barra de Localización",
+DlgLnkPopMenu : "Barra de Menú",
+DlgLnkPopScroll : "Barras de Desplazamento",
+DlgLnkPopStatus : "Barra de Estado",
+DlgLnkPopToolbar : "Barra de Ferramentas",
+DlgLnkPopFullScrn : "A Toda Pantalla (IE)",
+DlgLnkPopDependent : "Dependente (Netscape)",
+DlgLnkPopWidth : "Largura",
+DlgLnkPopHeight : "Altura",
+DlgLnkPopLeft : "Posición Esquerda",
+DlgLnkPopTop : "Posición dende Arriba",
+
+DlnLnkMsgNoUrl : "Por favor, escriba a ligazón URL",
+DlnLnkMsgNoEMail : "Por favor, escriba o enderezo de e-mail",
+DlnLnkMsgNoAnchor : "Por favor, seleccione un destino",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Seleccionar Color",
+DlgColorBtnClear : "Nengunha",
+DlgColorHighlight : "Destacado",
+DlgColorSelected : "Seleccionado",
+
+// Smiley Dialog
+DlgSmileyTitle : "Inserte un Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Seleccione Caracter Especial",
+
+// Table Dialog
+DlgTableTitle : "Propiedades da Táboa",
+DlgTableRows : "Filas",
+DlgTableColumns : "Columnas",
+DlgTableBorder : "Tamaño do Borde",
+DlgTableAlign : "Aliñamento",
+DlgTableAlignNotSet : "<Non Definido>",
+DlgTableAlignLeft : "Esquerda",
+DlgTableAlignCenter : "Centro",
+DlgTableAlignRight : "Ereita",
+DlgTableWidth : "Largura",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "percent",
+DlgTableHeight : "Altura",
+DlgTableCellSpace : "Marxe entre Celas",
+DlgTableCellPad : "Marxe interior",
+DlgTableCaption : "Título",
+DlgTableSummary : "Sumario",
+
+// Table Cell Dialog
+DlgCellTitle : "Propriedades da Cela",
+DlgCellWidth : "Largura",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "percent",
+DlgCellHeight : "Altura",
+DlgCellWordWrap : "Axustar Liñas",
+DlgCellWordWrapNotSet : "<Non Definido>",
+DlgCellWordWrapYes : "Si",
+DlgCellWordWrapNo : "Non",
+DlgCellHorAlign : "Aliñamento Horizontal",
+DlgCellHorAlignNotSet : "<Non definido>",
+DlgCellHorAlignLeft : "Esquerda",
+DlgCellHorAlignCenter : "Centro",
+DlgCellHorAlignRight: "Dereita",
+DlgCellVerAlign : "Aliñamento Vertical",
+DlgCellVerAlignNotSet : "<Non definido>",
+DlgCellVerAlignTop : "Arriba",
+DlgCellVerAlignMiddle : "Medio",
+DlgCellVerAlignBottom : "Abaixo",
+DlgCellVerAlignBaseline : "Liña de Base",
+DlgCellRowSpan : "Ocupar Filas",
+DlgCellCollSpan : "Ocupar Columnas",
+DlgCellBackColor : "Color de Fondo",
+DlgCellBorderColor : "Color de Borde",
+DlgCellBtnSelect : "Seleccionar...",
+
+// Find Dialog
+DlgFindTitle : "Procurar",
+DlgFindFindBtn : "Procurar",
+DlgFindNotFoundMsg : "Non te atopou o texto indicado.",
+
+// Replace Dialog
+DlgReplaceTitle : "Substituir",
+DlgReplaceFindLbl : "Texto a procurar:",
+DlgReplaceReplaceLbl : "Substituir con:",
+DlgReplaceCaseChk : "Coincidir Mai./min.",
+DlgReplaceReplaceBtn : "Substituir",
+DlgReplaceReplAllBtn : "Substitiur Todo",
+DlgReplaceWordChk : "Coincidir con toda a palabra",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Os axustes de seguridade do seu navegador non permiten que o editor realice automáticamente as tarefas de corte. Por favor, use o teclado para iso (Ctrl+X).",
+PasteErrorCopy : "Os axustes de seguridade do seu navegador non permiten que o editor realice automáticamente as tarefas de copia. Por favor, use o teclado para iso (Ctrl+C).",
+
+PasteAsText : "Pegar como texto plano",
+PasteFromWord : "Pegar dende Word",
+
+DlgPasteMsg2 : "Por favor, pegue dentro do seguinte cadro usando o teclado (<STRONG>Ctrl+V</STRONG>) e pulse <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorar as definicións de Tipografía",
+DlgPasteRemoveStyles : "Eliminar as definicións de Estilos",
+DlgPasteCleanBox : "Limpar o Cadro",
+
+// Color Picker
+ColorAutomatic : "Automático",
+ColorMoreColors : "Máis Cores...",
+
+// Document Properties
+DocProps : "Propriedades do Documento",
+
+// Anchor Dialog
+DlgAnchorTitle : "Propriedades da Referencia",
+DlgAnchorName : "Nome da Referencia",
+DlgAnchorErrorName : "Por favor, escriba o nome da referencia",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Non está no diccionario",
+DlgSpellChangeTo : "Cambiar a",
+DlgSpellBtnIgnore : "Ignorar",
+DlgSpellBtnIgnoreAll : "Ignorar Todas",
+DlgSpellBtnReplace : "Substituir",
+DlgSpellBtnReplaceAll : "Substituir Todas",
+DlgSpellBtnUndo : "Desfacer",
+DlgSpellNoSuggestions : "- Sen candidatos -",
+DlgSpellProgress : "Corrección ortográfica en progreso...",
+DlgSpellNoMispell : "Corrección ortográfica rematada: Non se atoparon erros",
+DlgSpellNoChanges : "Corrección ortográfica rematada: Non se substituiu nengunha verba",
+DlgSpellOneChange : "Corrección ortográfica rematada: Unha verba substituida",
+DlgSpellManyChanges : "Corrección ortográfica rematada: %1 verbas substituidas",
+
+IeSpellDownload : "O corrector ortográfico non está instalado. ¿Quere descargalo agora?",
+
+// Button Dialog
+DlgButtonText : "Texto (Valor)",
+DlgButtonType : "Tipo",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nome",
+DlgCheckboxValue : "Valor",
+DlgCheckboxSelected : "Seleccionado",
+
+// Form Dialog
+DlgFormName : "Nome",
+DlgFormAction : "Acción",
+DlgFormMethod : "Método",
+
+// Select Field Dialog
+DlgSelectName : "Nome",
+DlgSelectValue : "Valor",
+DlgSelectSize : "Tamaño",
+DlgSelectLines : "liñas",
+DlgSelectChkMulti : "Permitir múltiples seleccións",
+DlgSelectOpAvail : "Opcións Disponibles",
+DlgSelectOpText : "Texto",
+DlgSelectOpValue : "Valor",
+DlgSelectBtnAdd : "Engadir",
+DlgSelectBtnModify : "Modificar",
+DlgSelectBtnUp : "Subir",
+DlgSelectBtnDown : "Baixar",
+DlgSelectBtnSetValue : "Definir como valor por defecto",
+DlgSelectBtnDelete : "Borrar",
+
+// Textarea Dialog
+DlgTextareaName : "Nome",
+DlgTextareaCols : "Columnas",
+DlgTextareaRows : "Filas",
+
+// Text Field Dialog
+DlgTextName : "Nome",
+DlgTextValue : "Valor",
+DlgTextCharWidth : "Tamaño do Caracter",
+DlgTextMaxChars : "Máximo de Caracteres",
+DlgTextType : "Tipo",
+DlgTextTypeText : "Texto",
+DlgTextTypePass : "Chave",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nome",
+DlgHiddenValue : "Valor",
+
+// Bulleted List Dialog
+BulletedListProp : "Propriedades das Marcas",
+NumberedListProp : "Propriedades da Lista de Numeración",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tipo",
+DlgLstTypeCircle : "Círculo",
+DlgLstTypeDisc : "Disco",
+DlgLstTypeSquare : "Cuadrado",
+DlgLstTypeNumbers : "Números (1, 2, 3)",
+DlgLstTypeLCase : "Letras Minúsculas (a, b, c)",
+DlgLstTypeUCase : "Letras Maiúsculas (A, B, C)",
+DlgLstTypeSRoman : "Números Romanos en minúscula (i, ii, iii)",
+DlgLstTypeLRoman : "Números Romanos en Maiúscula (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Xeral",
+DlgDocBackTab : "Fondo",
+DlgDocColorsTab : "Cores e Marxes",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Título da Páxina",
+DlgDocLangDir : "Orientación do Idioma",
+DlgDocLangDirLTR : "Esquerda a Dereita (LTR)",
+DlgDocLangDirRTL : "Dereita a Esquerda (RTL)",
+DlgDocLangCode : "Código de Idioma",
+DlgDocCharSet : "Codificación do Xogo de Caracteres",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Outra Codificación do Xogo de Caracteres",
+
+DlgDocDocType : "Encabezado do Tipo de Documento",
+DlgDocDocTypeOther : "Outro Encabezado do Tipo de Documento",
+DlgDocIncXHTML : "Incluir Declaracións XHTML",
+DlgDocBgColor : "Cor de Fondo",
+DlgDocBgImage : "URL da Imaxe de Fondo",
+DlgDocBgNoScroll : "Fondo Fixo",
+DlgDocCText : "Texto",
+DlgDocCLink : "Ligazóns",
+DlgDocCVisited : "Ligazón Visitada",
+DlgDocCActive : "Ligazón Activa",
+DlgDocMargins : "Marxes da Páxina",
+DlgDocMaTop : "Arriba",
+DlgDocMaLeft : "Esquerda",
+DlgDocMaRight : "Dereita",
+DlgDocMaBottom : "Abaixo",
+DlgDocMeIndex : "Palabras Chave de Indexación do Documento (separadas por comas)",
+DlgDocMeDescr : "Descripción do Documento",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Vista Previa",
+
+// Templates Dialog
+Templates : "Plantillas",
+DlgTemplatesTitle : "Plantillas de Contido",
+DlgTemplatesSelMsg : "Por favor, seleccione a plantilla a abrir no editor<br>(o contido actual perderase):",
+DlgTemplatesLoading : "Cargando listado de plantillas. Por favor, espere...",
+DlgTemplatesNoTpl : "(Non hai plantillas definidas)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Acerca de",
+DlgAboutBrowserInfoTab : "Información do Navegador",
+DlgAboutLicenseTab : "Licencia",
+DlgAboutVersion : "versión",
+DlgAboutInfo : "Para máis información visitar:"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/he.js b/httemplate/elements/fckeditor/editor/lang/he.js
new file mode 100644
index 0000000..ef2b979
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/he.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Hebrew language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "rtl",
+
+ToolbarCollapse : "כיווץ סרגל הכלי×",
+ToolbarExpand : "פתיחת סרגל הכלי×",
+
+// Toolbar Items and Context Menu
+Save : "שמירה",
+NewPage : "דף חדש",
+Preview : "תצוגה מקדימה",
+Cut : "גזירה",
+Copy : "העתקה",
+Paste : "הדבקה",
+PasteText : "הדבקה כטקסט פשוט",
+PasteWord : "הדבקה מ-וורד",
+Print : "הדפסה",
+SelectAll : "בחירת הכל",
+RemoveFormat : "הסרת העיצוב",
+InsertLinkLbl : "קישור",
+InsertLink : "הוספת/עריכת קישור",
+RemoveLink : "הסרת הקישור",
+Anchor : "הוספת/עריכת נקודת עיגון",
+InsertImageLbl : "תמונה",
+InsertImage : "הוספת/עריכת תמונה",
+InsertFlashLbl : "פל×ש",
+InsertFlash : "הוסף/ערוך פל×ש",
+InsertTableLbl : "טבלה",
+InsertTable : "הוספת/עריכת טבלה",
+InsertLineLbl : "קו",
+InsertLine : "הוספת קו ×ופקי",
+InsertSpecialCharLbl: "תו מיוחד",
+InsertSpecialChar : "הוספת תו מיוחד",
+InsertSmileyLbl : "סמיילי",
+InsertSmiley : "הוספת סמיילי",
+About : "×ודות FCKeditor",
+Bold : "מודגש",
+Italic : "נטוי",
+Underline : "קו תחתון",
+StrikeThrough : "כתיב מחוק",
+Subscript : "כתיב תחתון",
+Superscript : "כתיב עליון",
+LeftJustify : "יישור לשמ×ל",
+CenterJustify : "מרכוז",
+RightJustify : "יישור לימין",
+BlockJustify : "יישור לשוליי×",
+DecreaseIndent : "הקטנת ×ינדנטציה",
+IncreaseIndent : "הגדלת ×ינדנטציה",
+Undo : "ביטול צעד ×חרון",
+Redo : "חזרה על צעד ×חרון",
+NumberedListLbl : "רשימה ממוספרת",
+NumberedList : "הוספת/הסרת רשימה ממוספרת",
+BulletedListLbl : "רשימת נקודות",
+BulletedList : "הוספת/הסרת רשימת נקודות",
+ShowTableBorders : "הצגת מסגרת הטבלה",
+ShowDetails : "הצגת פרטי×",
+Style : "סגנון",
+FontFormat : "עיצוב",
+Font : "גופן",
+FontSize : "גודל",
+TextColor : "צבע טקסט",
+BGColor : "צבע רקע",
+Source : "מקור",
+Find : "חיפוש",
+Replace : "החלפה",
+SpellCheck : "בדיקת ×יות",
+UniversalKeyboard : "מקלדת ×וניברסלית",
+PageBreakLbl : "שבירת דף",
+PageBreak : "הוסף שבירת דף",
+
+Form : "טופס",
+Checkbox : "תיבת סימון",
+RadioButton : "לחצן ×פשרויות",
+TextField : "שדה טקסט",
+Textarea : "×יזור טקסט",
+HiddenField : "שדה חבוי",
+Button : "כפתור",
+SelectionField : "שדה בחירה",
+ImageButton : "כפתור תמונה",
+
+FitWindow : "הגדל ×ת גודל העורך",
+
+// Context Menu
+EditLink : "עריכת קישור",
+CellCM : "ת×",
+RowCM : "שורה",
+ColumnCM : "עמודה",
+InsertRow : "הוספת שורה",
+DeleteRows : "מחיקת שורות",
+InsertColumn : "הוספת עמודה",
+DeleteColumns : "מחיקת עמודות",
+InsertCell : "הוספת ת×",
+DeleteCells : "מחיקת ת××™×",
+MergeCells : "מיזוג ת××™×",
+SplitCell : "פיצול ת××™×",
+TableDelete : "מחק טבלה",
+CellProperties : "תכונות הת×",
+TableProperties : "תכונות הטבלה",
+ImageProperties : "תכונות התמונה",
+FlashProperties : "מ×פייני פל×ש",
+
+AnchorProp : "מ×פייני נקודת עיגון",
+ButtonProp : "מ×פייני כפתור",
+CheckboxProp : "מ×פייני תיבת סימון",
+HiddenFieldProp : "מ×פיני שדה חבוי",
+RadioButtonProp : "מ×פייני לחצן ×פשרויות",
+ImageButtonProp : "מ×פיני כפתור תמונה",
+TextFieldProp : "מ×פייני שדה טקסט",
+SelectionFieldProp : "מ×פייני שדה בחירה",
+TextareaProp : "מ×פיני ×יזור טקסט",
+FormProp : "מ×פיני טופס",
+
+FontFormats : "נורמלי;קוד;כתובת;כותרת;כותרת 2;כותרת 3;כותרת 4;כותרת 5;כותרת 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "מעבד XHTML, × × ×œ×”×ž×ª×™×Ÿ...",
+Done : "המשימה הושלמה",
+PasteWordConfirm : "נר××” הטקסט שבכוונתך להדביק מקורו בקובץ וורד. ×”×× ×‘×¨×¦×•× ×š לנקות ×ותו ×˜×¨× ×”×”×“×‘×§×”?",
+NotCompatiblePaste : "פעולה זו זמינה לדפדפן ×ינטרנט ×קספלורר ×ž×’×™×¨×¡× 5.5 ומעלה. ×”×× ×œ×”×ž×©×™×š בהדבקה ×œ×œ× ×”× ×™×§×•×™?",
+UnknownToolbarItem : "פריט ×œ× ×™×“×•×¢ בסרגל ×”×›×œ×™× \"%1\"",
+UnknownCommand : "×©× ×¤×¢×•×œ×” ×œ× ×™×“×•×¢ \"%1\"",
+NotImplemented : "הפקודה ×œ× ×ž×™×•×©×ž×ª",
+UnknownToolbarSet : "ערכת סרגל ×”×›×œ×™× \"%1\" ×œ× ×§×™×™×ž×ª",
+NoActiveX : "הגדרות ×בטחה של הדפדפן עלולות לגביל ×ת ×פשרויות העריכה.יש ל×פשר ×ת ×”×ופציה \"הרץ ×¤×§×“×™× ×¤×¢×™×œ×™× ×•×ª×•×¡×¤×•×ª\". תוכל לחוות טעויות ×•×—×™×•×•×™× ×©×œ ×פשרויות שחסרי×.",
+BrowseServerBlocked : "×œ× × ×™×ª×Ÿ לגשת לדפדפן מש×בי×.×× × ×•×•×“× ×©×—×•×¡× ×—×œ×•× ×•×ª ×”×§×•×¤×¦×™× ×œ× ×¤×¢×™×œ.",
+DialogBlocked : "×œ× ×”×™×” ניתן לפתוח חלון די×לוג. ×× × ×•×•×“× ×©×—×•×¡× ×—×œ×•× ×•×ª ×§×•×¤×¦×™× ×œ× ×¤×¢×™×œ.",
+
+// Dialogs
+DlgBtnOK : "×ישור",
+DlgBtnCancel : "ביטול",
+DlgBtnClose : "סגירה",
+DlgBtnBrowseServer : "סייר השרת",
+DlgAdvancedTag : "×פשרויות מתקדמות",
+DlgOpOther : "<×חר>",
+DlgInfoTab : "מידע",
+DlgAlertUrl : "×× ×” הזן URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<×œ× × ×§×‘×¢>",
+DlgGenId : "זיהוי (Id)",
+DlgGenLangDir : "כיוון שפה",
+DlgGenLangDirLtr : "שמ×ל לימין (LTR)",
+DlgGenLangDirRtl : "ימין לשמ×ל (RTL)",
+DlgGenLangCode : "קוד שפה",
+DlgGenAccessKey : "מקש גישה",
+DlgGenName : "ש×",
+DlgGenTabIndex : "מספר ט×ב",
+DlgGenLongDescr : "קישור לתי×ור מפורט",
+DlgGenClass : "גיליונות עיצוב קבוצות",
+DlgGenTitle : "כותרת מוצעת",
+DlgGenContType : "Content Type מוצע",
+DlgGenLinkCharset : "קידוד המש×ב המקושר",
+DlgGenStyle : "סגנון",
+
+// Image Dialog
+DlgImgTitle : "תכונות התמונה",
+DlgImgInfoTab : "מידע על התמונה",
+DlgImgBtnUpload : "שליחה לשרת",
+DlgImgURL : "כתובת (URL)",
+DlgImgUpload : "העל××”",
+DlgImgAlt : "טקסט חלופי",
+DlgImgWidth : "רוחב",
+DlgImgHeight : "גובה",
+DlgImgLockRatio : "נעילת היחס",
+DlgBtnResetSize : "×יפוס הגודל",
+DlgImgBorder : "מסגרת",
+DlgImgHSpace : "מרווח ×ופקי",
+DlgImgVSpace : "מרווח ×× ×›×™",
+DlgImgAlign : "יישור",
+DlgImgAlignLeft : "לשמ×ל",
+DlgImgAlignAbsBottom: "לתחתית ×”×בסולוטית",
+DlgImgAlignAbsMiddle: "מרכוז ×בסולוטי",
+DlgImgAlignBaseline : "לקו התחתית",
+DlgImgAlignBottom : "לתחתית",
+DlgImgAlignMiddle : "ל×מצע",
+DlgImgAlignRight : "לימין",
+DlgImgAlignTextTop : "לר×ש הטקסט",
+DlgImgAlignTop : "למעלה",
+DlgImgPreview : "תצוגה מקדימה",
+DlgImgAlertUrl : "× × ×œ×”×§×œ×™×“ ×ת כתובת התמונה",
+DlgImgLinkTab : "קישור",
+
+// Flash Dialog
+DlgFlashTitle : "מ×פיני פל×ש",
+DlgFlashChkPlay : "נגן ×וטומטי",
+DlgFlashChkLoop : "לול××”",
+DlgFlashChkMenu : "×פשר תפריט פל×ש",
+DlgFlashScale : "גודל",
+DlgFlashScaleAll : "הצג הכל",
+DlgFlashScaleNoBorder : "×œ×œ× ×’×‘×•×œ×•×ª",
+DlgFlashScaleFit : "הת×מה מושלמת",
+
+// Link Dialog
+DlgLnkWindowTitle : "קישור",
+DlgLnkInfoTab : "מידע על הקישור",
+DlgLnkTargetTab : "מטרה",
+
+DlgLnkType : "סוג קישור",
+DlgLnkTypeURL : "כתובת (URL)",
+DlgLnkTypeAnchor : "עוגן בעמוד זה",
+DlgLnkTypeEMail : "דו×''ל",
+DlgLnkProto : "פרוטוקול",
+DlgLnkProtoOther : "<×חר>",
+DlgLnkURL : "כתובת (URL)",
+DlgLnkAnchorSel : "בחירת עוגן",
+DlgLnkAnchorByName : "עפ''×™ ×©× ×”×¢×•×’×Ÿ",
+DlgLnkAnchorById : "עפ''י זיהוי (Id) הרכיב",
+DlgLnkNoAnchors : "(×ין ×¢×•×’× ×™× ×–×ž×™× ×™× ×‘×“×£)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "כתובת הדו×''ל",
+DlgLnkEMailSubject : "× ×•×©× ×”×”×•×“×¢×”",
+DlgLnkEMailBody : "גוף ההודעה",
+DlgLnkUpload : "העל××”",
+DlgLnkBtnUpload : "שליחה לשרת",
+
+DlgLnkTarget : "מטרה",
+DlgLnkTargetFrame : "<מסגרת>",
+DlgLnkTargetPopup : "<חלון קופץ>",
+DlgLnkTargetBlank : "חלון חדש (_blank)",
+DlgLnkTargetParent : "חלון ×”×ב (_parent)",
+DlgLnkTargetSelf : "ב×ותו החלון (_self)",
+DlgLnkTargetTop : "חלון ר×שי (_top)",
+DlgLnkTargetFrameName : "×©× ×ž×¡×’×¨×ª היעד",
+DlgLnkPopWinName : "×©× ×”×—×œ×•×Ÿ הקופץ",
+DlgLnkPopWinFeat : "תכונות החלון הקופץ",
+DlgLnkPopResize : "בעל גודל ניתן לשינוי",
+DlgLnkPopLocation : "סרגל כתובת",
+DlgLnkPopMenu : "סרגל תפריט",
+DlgLnkPopScroll : "ניתן לגלילה",
+DlgLnkPopStatus : "סרגל חיווי",
+DlgLnkPopToolbar : "סרגל הכלי×",
+DlgLnkPopFullScrn : "מסך ×ž×œ× (IE)",
+DlgLnkPopDependent : "תלוי (Netscape)",
+DlgLnkPopWidth : "רוחב",
+DlgLnkPopHeight : "גובה",
+DlgLnkPopLeft : "×ž×™×§×•× ×¦×“ שמ×ל",
+DlgLnkPopTop : "×ž×™×§×•× ×¦×“ עליון",
+
+DlnLnkMsgNoUrl : "× × ×œ×”×§×œ×™×“ ×ת כתובת הקישור (URL)",
+DlnLnkMsgNoEMail : "× × ×œ×”×§×œ×™×“ ×ת כתובת הדו×''ל",
+DlnLnkMsgNoAnchor : "× × ×œ×‘×—×•×¨ עוגן במסמך",
+DlnLnkMsgInvPopName : "×©× ×”×—×œ×•×Ÿ הקופץ חייב להתחיל ב×ותיות ו×סור לכלול רווחי×",
+
+// Color Dialog
+DlgColorTitle : "בחירת צבע",
+DlgColorBtnClear : "×יפוס",
+DlgColorHighlight : "נוכחי",
+DlgColorSelected : "נבחר",
+
+// Smiley Dialog
+DlgSmileyTitle : "הוספת סמיילי",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "בחירת תו מיוחד",
+
+// Table Dialog
+DlgTableTitle : "תכונות טבלה",
+DlgTableRows : "שורות",
+DlgTableColumns : "עמודות",
+DlgTableBorder : "גודל מסגרת",
+DlgTableAlign : "יישור",
+DlgTableAlignNotSet : "<×œ× × ×§×‘×¢>",
+DlgTableAlignLeft : "שמ×ל",
+DlgTableAlignCenter : "מרכז",
+DlgTableAlignRight : "ימין",
+DlgTableWidth : "רוחב",
+DlgTableWidthPx : "פיקסלי×",
+DlgTableWidthPc : "×חוז",
+DlgTableHeight : "גובה",
+DlgTableCellSpace : "מרווח ת×",
+DlgTableCellPad : "ריפוד ת×",
+DlgTableCaption : "כיתוב",
+DlgTableSummary : "סיכו×",
+
+// Table Cell Dialog
+DlgCellTitle : "תכונות ת×",
+DlgCellWidth : "רוחב",
+DlgCellWidthPx : "פיקסלי×",
+DlgCellWidthPc : "×חוז",
+DlgCellHeight : "גובה",
+DlgCellWordWrap : "גלילת שורות",
+DlgCellWordWrapNotSet : "<×œ× × ×§×‘×¢>",
+DlgCellWordWrapYes : "כן",
+DlgCellWordWrapNo : "ל×",
+DlgCellHorAlign : "יישור ×ופקי",
+DlgCellHorAlignNotSet : "<×œ× × ×§×‘×¢>",
+DlgCellHorAlignLeft : "שמ×ל",
+DlgCellHorAlignCenter : "מרכז",
+DlgCellHorAlignRight: "ימין",
+DlgCellVerAlign : "יישור ×× ×›×™",
+DlgCellVerAlignNotSet : "<×œ× × ×§×‘×¢>",
+DlgCellVerAlignTop : "למעלה",
+DlgCellVerAlignMiddle : "ל×מצע",
+DlgCellVerAlignBottom : "לתחתית",
+DlgCellVerAlignBaseline : "קו תחתית",
+DlgCellRowSpan : "טווח שורות",
+DlgCellCollSpan : "טווח עמודות",
+DlgCellBackColor : "צבע רקע",
+DlgCellBorderColor : "צבע מסגרת",
+DlgCellBtnSelect : "בחירה...",
+
+// Find Dialog
+DlgFindTitle : "חיפוש",
+DlgFindFindBtn : "חיפוש",
+DlgFindNotFoundMsg : "הטקסט המבוקש ×œ× × ×ž×¦×.",
+
+// Replace Dialog
+DlgReplaceTitle : "החלפה",
+DlgReplaceFindLbl : "חיפוש מחרוזת:",
+DlgReplaceReplaceLbl : "החלפה במחרוזת:",
+DlgReplaceCaseChk : "הת×מת סוג ×ותיות (Case)",
+DlgReplaceReplaceBtn : "החלפה",
+DlgReplaceReplAllBtn : "החלפה בכל העמוד",
+DlgReplaceWordChk : "הת×מה למילה המל××”",
+
+// Paste Operations / Dialog
+PasteErrorCut : "הגדרות ×”×בטחה בדפדפן שלך ×œ× ×ž×פשרות לעורך לבצע פעולות גזירה ×וטומטיות. יש להשתמש במקלדת ×œ×©× ×›×š (Ctrl+X).",
+PasteErrorCopy : "הגדרות ×”×בטחה בדפדפן שלך ×œ× ×ž×פשרות לעורך לבצע פעולות העתקה ×וטומטיות. יש להשתמש במקלדת ×œ×©× ×›×š (Ctrl+C).",
+
+PasteAsText : "הדבקה כטקסט פשוט",
+PasteFromWord : "הדבקה מ-וורד",
+
+DlgPasteMsg2 : "×× × ×”×“×‘×§ בתוך הקופסה ב×מצעות (<STRONG>Ctrl+V</STRONG>) ולחץ על <STRONG>×ישור</STRONG>.",
+DlgPasteSec : "עקב הגדרות ×בטחה בדפדפן, ×œ× × ×™×ª×Ÿ לגשת ×ל לוח ×”×’×–×™×¨×™× (clipboard) בצורה ישירה.×× × ×‘×¦×¢ הדבק שוב בחלון ×–×”.",
+DlgPasteIgnoreFont : "×”×ª×¢×œ× ×ž×”×’×“×¨×•×ª סוג פונט",
+DlgPasteRemoveStyles : "הסר הגדרות סגנון",
+DlgPasteCleanBox : "ניקוי קופסה",
+
+// Color Picker
+ColorAutomatic : "×וטומטי",
+ColorMoreColors : "×¦×‘×¢×™× × ×•×¡×¤×™×...",
+
+// Document Properties
+DocProps : "מ×פיני מסמך",
+
+// Anchor Dialog
+DlgAnchorTitle : "מ×פיני נקודת עיגון",
+DlgAnchorName : "×©× ×œ× ×§×•×“×ª עיגון",
+DlgAnchorErrorName : "×× × ×”×–×Ÿ ×©× ×œ× ×§×•×“×ª עיגון",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "×œ× × ×ž×¦× ×‘×ž×™×œ×•×Ÿ",
+DlgSpellChangeTo : "שנה ל",
+DlgSpellBtnIgnore : "התעל×",
+DlgSpellBtnIgnoreAll : "×”×ª×¢×œ× ×ž×”×›×œ",
+DlgSpellBtnReplace : "החלף",
+DlgSpellBtnReplaceAll : "החלף הכל",
+DlgSpellBtnUndo : "החזר",
+DlgSpellNoSuggestions : "- ×ין הצעות -",
+DlgSpellProgress : "בדיקות ×יות בתהליך ....",
+DlgSpellNoMispell : "בדיקות ×יות הסתיימה: ×œ× × ×ž×¦×ו שגיעות כתיב",
+DlgSpellNoChanges : "בדיקות ×יות הסתיימה: ×œ× ×©×•× ×ª×” ××£ מילה",
+DlgSpellOneChange : "בדיקות ×יות הסתיימה: שונתה מילה ×חת",
+DlgSpellManyChanges : "בדיקות ×יות הסתיימה: %1 ×ž×™×œ×™× ×©×•× ×•",
+
+IeSpellDownload : "בודק ×”×יות ×œ× ×ž×•×ª×§×Ÿ, ×”×× ×תה מעוניין להוריד?",
+
+// Button Dialog
+DlgButtonText : "טקסט (ערך)",
+DlgButtonType : "סוג",
+DlgButtonTypeBtn : "כפתור",
+DlgButtonTypeSbm : "שלח",
+DlgButtonTypeRst : "×פס",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "ש×",
+DlgCheckboxValue : "ערך",
+DlgCheckboxSelected : "בחור",
+
+// Form Dialog
+DlgFormName : "ש×",
+DlgFormAction : "שלח ×ל",
+DlgFormMethod : "סוג שליחה",
+
+// Select Field Dialog
+DlgSelectName : "ש×",
+DlgSelectValue : "ערך",
+DlgSelectSize : "גודל",
+DlgSelectLines : "שורות",
+DlgSelectChkMulti : "×פשר בחירות מרובות",
+DlgSelectOpAvail : "×פשרויות זמינות",
+DlgSelectOpText : "טקסט",
+DlgSelectOpValue : "ערך",
+DlgSelectBtnAdd : "הוסף",
+DlgSelectBtnModify : "שנה",
+DlgSelectBtnUp : "למעלה",
+DlgSelectBtnDown : "למטה",
+DlgSelectBtnSetValue : "קבע כברירת מחדל",
+DlgSelectBtnDelete : "מחק",
+
+// Textarea Dialog
+DlgTextareaName : "ש×",
+DlgTextareaCols : "עמודות",
+DlgTextareaRows : "שורות",
+
+// Text Field Dialog
+DlgTextName : "ש×",
+DlgTextValue : "ערך",
+DlgTextCharWidth : "רוחב ב×ותיות",
+DlgTextMaxChars : "מקסימות ×ותיות",
+DlgTextType : "סוג",
+DlgTextTypeText : "טקסט",
+DlgTextTypePass : "סיסמה",
+
+// Hidden Field Dialog
+DlgHiddenName : "ש×",
+DlgHiddenValue : "ערך",
+
+// Bulleted List Dialog
+BulletedListProp : "מ×פייני רשימה",
+NumberedListProp : "מ×פייני רשימה ממוספרת",
+DlgLstStart : "התחלה",
+DlgLstType : "סוג",
+DlgLstTypeCircle : "עיגול",
+DlgLstTypeDisc : "דיסק",
+DlgLstTypeSquare : "מרובע",
+DlgLstTypeNumbers : "×ž×¡×¤×¨×™× (1, 2, 3)",
+DlgLstTypeLCase : "×ותיות קטנות (a, b, c)",
+DlgLstTypeUCase : "×ותיות גדולות (A, B, C)",
+DlgLstTypeSRoman : "ספרות רומ×יות קטנות (i, ii, iii)",
+DlgLstTypeLRoman : "ספרות רומ×יות גדולות (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "כללי",
+DlgDocBackTab : "רקע",
+DlgDocColorsTab : "×¦×‘×¢×™× ×•×’×‘×•×œ×•×ª",
+DlgDocMetaTab : "נתוני META",
+
+DlgDocPageTitle : "כותרת דף",
+DlgDocLangDir : "כיוון שפה",
+DlgDocLangDirLTR : "שמ×ל לימין (LTR)",
+DlgDocLangDirRTL : "ימין לשמ×ל (RTL)",
+DlgDocLangCode : "קוד שפה",
+DlgDocCharSet : "קידוד ×ותיות",
+DlgDocCharSetCE : "מרכז ×ירופה",
+DlgDocCharSetCT : "סיני מסורתי (Big5)",
+DlgDocCharSetCR : "קירילי",
+DlgDocCharSetGR : "יוונית",
+DlgDocCharSetJP : "יפנית",
+DlgDocCharSetKR : "קור×נית",
+DlgDocCharSetTR : "טורקית",
+DlgDocCharSetUN : "יוני קוד (UTF-8)",
+DlgDocCharSetWE : "מערב ×ירופה",
+DlgDocCharSetOther : "קידוד ×ותיות ×חר",
+
+DlgDocDocType : "הגדרות סוג מסמך",
+DlgDocDocTypeOther : "הגדרות סוג מסמך ×חרות",
+DlgDocIncXHTML : "כלול הגדרות XHTML",
+DlgDocBgColor : "צבע רקע",
+DlgDocBgImage : "URL לתמונת רקע",
+DlgDocBgNoScroll : "רגע ×œ×œ× ×’×œ×™×œ×”",
+DlgDocCText : "טקסט",
+DlgDocCLink : "קישור",
+DlgDocCVisited : "קישור שבוקר",
+DlgDocCActive : " קישור פעיל",
+DlgDocMargins : "גבולות דף",
+DlgDocMaTop : "למעלה",
+DlgDocMaLeft : "שמ×לה",
+DlgDocMaRight : "ימינה",
+DlgDocMaBottom : "למטה",
+DlgDocMeIndex : "מפתח ×¢× ×™×™× ×™× ×©×œ המסמך )מופרד בפסיק(",
+DlgDocMeDescr : "ת×ור מסמך",
+DlgDocMeAuthor : "מחבר",
+DlgDocMeCopy : "זכויות יוצרי×",
+DlgDocPreview : "תצוגה מקדימה",
+
+// Templates Dialog
+Templates : "תבניות",
+DlgTemplatesTitle : "תביות תוכן",
+DlgTemplatesSelMsg : "×× × ×‘×—×¨ תבנית לפתיחה בעורך <BR>התוכן המקורי ימחק:",
+DlgTemplatesLoading : "מעלה רשימת תבניות ×× × ×”×ž×ª×Ÿ",
+DlgTemplatesNoTpl : "(×œ× ×”×•×’×“×¨×• תבניות)",
+DlgTemplatesReplace : "החלפת תוכן ממשי",
+
+// About Dialog
+DlgAboutAboutTab : "×ודות",
+DlgAboutBrowserInfoTab : "גירסת דפדפן",
+DlgAboutLicenseTab : "רשיון",
+DlgAboutVersion : "גירס×",
+DlgAboutInfo : "מידע נוסף ניתן ×œ×ž×¦×•× ×›×ן:"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/hi.js b/httemplate/elements/fckeditor/editor/lang/hi.js
new file mode 100644
index 0000000..fdc5e39
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/hi.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Hindi language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "टूलबार सिमटायें",
+ToolbarExpand : "टूलबार का विसà¥à¤¤à¤¾à¤° करें",
+
+// Toolbar Items and Context Menu
+Save : "सेव",
+NewPage : "नया पेज",
+Preview : "पà¥à¤°à¥€à¤µà¥à¤¯à¥‚",
+Cut : "कट",
+Copy : "कॉपी",
+Paste : "पेसà¥à¤Ÿ",
+PasteText : "पेसà¥à¤Ÿ (सादा टॅकà¥à¤¸à¥à¤Ÿ)",
+PasteWord : "पेसà¥à¤Ÿ (वरà¥à¤¡ से)",
+Print : "पà¥à¤°à¤¿à¤¨à¥à¤Ÿ",
+SelectAll : "सब सॅलॅकà¥à¤Ÿ करें",
+RemoveFormat : "फ़ॉरà¥à¤®à¥ˆà¤Ÿ हटायें",
+InsertLinkLbl : "लिंक",
+InsertLink : "लिंक इनà¥à¤¸à¤°à¥à¤Ÿ/संपादन",
+RemoveLink : "लिंक हटायें",
+Anchor : "à¤à¤‚कर इनà¥à¤¸à¤°à¥à¤Ÿ/संपादन",
+InsertImageLbl : "तसà¥à¤µà¥€à¤°",
+InsertImage : "तसà¥à¤µà¥€à¤° इनà¥à¤¸à¤°à¥à¤Ÿ/संपादन",
+InsertFlashLbl : "फ़à¥à¤²à¥ˆà¤¶",
+InsertFlash : "फ़à¥à¤²à¥ˆà¤¶ इनà¥à¤¸à¤°à¥à¤Ÿ/संपादन",
+InsertTableLbl : "टेबल",
+InsertTable : "टेबल इनà¥à¤¸à¤°à¥à¤Ÿ/संपादन",
+InsertLineLbl : "रेखा",
+InsertLine : "हॉरिज़ॉनà¥à¤Ÿà¤² रेखा इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+InsertSpecialCharLbl: "विशेष करॅकà¥à¤Ÿà¤°",
+InsertSpecialChar : "विशेष करॅकà¥à¤Ÿà¤° इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+InsertSmileyLbl : "सà¥à¤®à¤¾à¤‡à¤²à¥€",
+InsertSmiley : "सà¥à¤®à¤¾à¤‡à¤²à¥€ इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+About : "FCKeditor के बारे में",
+Bold : "बोलà¥à¤¡",
+Italic : "इटैलिक",
+Underline : "रेखांकण",
+StrikeThrough : "सà¥à¤Ÿà¥à¤°à¤¾à¤‡à¤• थà¥à¤°à¥‚",
+Subscript : "अधोलेख",
+Superscript : "अभिलेख",
+LeftJustify : "बायीं तरफ",
+CenterJustify : "बीच में",
+RightJustify : "दायीं तरफ",
+BlockJustify : "बà¥à¤²à¥‰à¤• जसà¥à¤Ÿà¥€à¥žà¤¾à¤ˆ",
+DecreaseIndent : "इनà¥à¤¡à¥…नà¥à¤Ÿ कम करें",
+IncreaseIndent : "इनà¥à¤¡à¥…नà¥à¤Ÿ बà¥à¤¾à¤¯à¥‡à¤‚",
+Undo : "अनà¥à¤¡à¥‚",
+Redo : "रीडू",
+NumberedListLbl : "अंकीय सूची",
+NumberedList : "अंकीय सूची इनà¥à¤¸à¤°à¥à¤Ÿ/संपादन",
+BulletedListLbl : "बà¥à¤²à¥…ट सूची",
+BulletedList : "बà¥à¤²à¥…ट सूची इनà¥à¤¸à¤°à¥à¤Ÿ/संपादन",
+ShowTableBorders : "टेबल बॉरà¥à¤¡à¤°à¤¯à¥‡à¤‚ दिखायें",
+ShowDetails : "जà¥à¤¯à¤¾à¤¦à¤¾ दिखायें",
+Style : "सà¥à¤Ÿà¤¾à¤‡à¤²",
+FontFormat : "फ़ॉरà¥à¤®à¥ˆà¤Ÿ",
+Font : "फ़ॉनà¥à¤Ÿ",
+FontSize : "साइज़",
+TextColor : "टेकà¥à¤¸à¥à¤Ÿ रंग",
+BGColor : "बैकà¥à¤—à¥à¤°à¤¾à¤‰à¤¨à¥à¤¡ रंग",
+Source : "सोरà¥à¤¸",
+Find : "खोजें",
+Replace : "रीपà¥à¤²à¥‡à¤¸",
+SpellCheck : "वरà¥à¤¤à¤¨à¥€ (सà¥à¤ªà¥‡à¤²à¤¿à¤‚ग) जाà¤à¤š",
+UniversalKeyboard : "यूनीवरà¥à¤¸à¤² कीबोरà¥à¤¡",
+PageBreakLbl : "पेज बà¥à¤°à¥‡à¤•",
+PageBreak : "पेज बà¥à¤°à¥‡à¤• इनà¥à¤¸à¤°à¥à¤Ÿà¥ करें",
+
+Form : "फ़ॉरà¥à¤®",
+Checkbox : "चॅक बॉकà¥à¤¸",
+RadioButton : "रेडिओ बटन",
+TextField : "टेकà¥à¤¸à¥à¤Ÿ फ़ीलà¥à¤¡",
+Textarea : "टेकà¥à¤¸à¥à¤Ÿ à¤à¤°à¤¿à¤¯à¤¾",
+HiddenField : "गà¥à¤ªà¥à¤¤ फ़ीलà¥à¤¡",
+Button : "बटन",
+SelectionField : "चà¥à¤¨à¤¾à¤µ फ़ीलà¥à¤¡",
+ImageButton : "तसà¥à¤µà¥€à¤° बटन",
+
+FitWindow : "à¤à¤¡à¤¿à¤Ÿà¤° साइज़ को चरम सीमा तक बà¥à¤¾à¤¯à¥‡à¤‚",
+
+// Context Menu
+EditLink : "लिंक संपादन",
+CellCM : "खाना",
+RowCM : "पंकà¥à¤¤à¤¿",
+ColumnCM : "कालम",
+InsertRow : "पंकà¥à¤¤à¤¿ इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+DeleteRows : "पंकà¥à¤¤à¤¿à¤¯à¤¾à¤ डिलीट करें",
+InsertColumn : "कॉलम इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+DeleteColumns : "कॉलम डिलीट करें",
+InsertCell : "सॅल इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+DeleteCells : "सॅल डिलीट करें",
+MergeCells : "सॅल मिलायें",
+SplitCell : "सॅल अलग करें",
+TableDelete : "टेबल डिलीट करें",
+CellProperties : "सॅल पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+TableProperties : "टेबल पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+ImageProperties : "तसà¥à¤µà¥€à¤° पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+FlashProperties : "फ़à¥à¤²à¥ˆà¤¶ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+
+AnchorProp : "à¤à¤‚कर पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+ButtonProp : "बटन पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+CheckboxProp : "चॅक बॉकà¥à¤¸ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+HiddenFieldProp : "गà¥à¤ªà¥à¤¤ फ़ीलà¥à¤¡ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+RadioButtonProp : "रेडिओ बटन पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+ImageButtonProp : "तसà¥à¤µà¥€à¤° बटन पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+TextFieldProp : "टेकà¥à¤¸à¥à¤Ÿ फ़ीलà¥à¤¡ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+SelectionFieldProp : "चà¥à¤¨à¤¾à¤µ फ़ीलà¥à¤¡ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+TextareaProp : "टेकà¥à¤¸à¥à¤¤ à¤à¤°à¤¿à¤¯à¤¾ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+FormProp : "फ़ॉरà¥à¤® पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+
+FontFormats : "साधारण;फ़ॉरà¥à¤®à¥ˆà¤Ÿà¥…ड;पता;शीरà¥à¤·à¤• 1;शीरà¥à¤·à¤• 2;शीरà¥à¤·à¤• 3;शीरà¥à¤·à¤• 4;शीरà¥à¤·à¤• 5;शीरà¥à¤·à¤• 6;शीरà¥à¤·à¤• (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML पà¥à¤°à¥‹à¤¸à¥…स हो रहा है। ज़रा ठहरें...",
+Done : "पूरा हà¥à¤†",
+PasteWordConfirm : "आप जो टेकà¥à¤¸à¥à¤Ÿ पेसà¥à¤Ÿ करना चाहते हैं, वह वरà¥à¤¡ से कॉपी किया हà¥à¤† लग रहा है। कà¥à¤¯à¤¾ पेसà¥à¤Ÿ करने से पहले आप इसे साफ़ करना चाहेंगे?",
+NotCompatiblePaste : "यह कमांड इनà¥à¤Ÿà¤°à¤¨à¥…ट à¤à¤•à¥à¤¸à¥à¤ªà¥à¤²à¥‹à¤°à¤°(Internet Explorer) 5.5 या उसके बाद के वरà¥à¥›à¤¨ के लिठही उपलबà¥à¤§ है। कà¥à¤¯à¤¾ आप बिना साफ़ किठपेसà¥à¤Ÿ करना चाहेंगे?",
+UnknownToolbarItem : "अनजान टूलबार आइटम \"%1\"",
+UnknownCommand : "अनजान कमानà¥à¤¡ \"%1\"",
+NotImplemented : "कमानà¥à¤¡ इमà¥à¤ªà¥à¤²à¥€à¤®à¥…नà¥à¤Ÿ नहीं किया गया है",
+UnknownToolbarSet : "टूलबार सॅट \"%1\" उपलबà¥à¤§ नहीं है",
+NoActiveX : "आपके बà¥à¤°à¤¾à¤‰à¥›à¤°à¥ की सà¥à¤°à¤•à¥à¤¶à¤¾ सेटिंगà¥à¤¸à¥ à¤à¤¡à¤¿à¤Ÿà¤° की कà¥à¤›à¥ फ़ीचरों को सीमित करॠसकती हैं। कà¥à¤°à¤¿à¤ªà¤¯à¤¾ \"Run ActiveX controls and plug-ins\" विकलà¥à¤ª को à¤à¤¨à¥‡à¤¬à¤² करें. आपको à¤à¤°à¤°à¥à¤¸à¥ और गायब फ़ीचरà¥à¤¸à¥ का अनà¥à¤­à¤µ हो सकता है।",
+BrowseServerBlocked : "रिसोरà¥à¤¸à¥‡à¥› बà¥à¤°à¤¾à¤‰à¥›à¤°à¥ नहीं खोला जा सका। कà¥à¤°à¤¿à¤ªà¤¯à¤¾ सभी पॉपà¥-अपॠबà¥à¤²à¥‰à¤•à¤°à¥à¤¸à¥ को डिसेबल करें।",
+DialogBlocked : "डायलग विनà¥à¤¡à¥‹ नहीं खोला जा सका। कà¥à¤°à¤¿à¤ªà¤¯à¤¾ सभी पॉपà¥-अपॠबà¥à¤²à¥‰à¤•à¤°à¥à¤¸à¥ को डिसेबल करें।",
+
+// Dialogs
+DlgBtnOK : "ठीक है",
+DlgBtnCancel : "रदà¥à¤¦ करें",
+DlgBtnClose : "बनà¥à¤¦ करें",
+DlgBtnBrowseServer : "सरà¥à¤µà¤° बà¥à¤°à¤¾à¤‰à¥› करें",
+DlgAdvancedTag : "à¤à¤¡à¥à¤µà¤¾à¤¨à¥à¤¸à¥à¤¡",
+DlgOpOther : "<अनà¥à¤¯>",
+DlgInfoTab : "सूचना",
+DlgAlertUrl : "URL इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+
+// General Dialogs Labels
+DlgGenNotSet : "<सॅट नहीं>",
+DlgGenId : "Id",
+DlgGenLangDir : "भाषा लिखने की दिशा",
+DlgGenLangDirLtr : "बायें से दायें (LTR)",
+DlgGenLangDirRtl : "दायें से बायें (RTL)",
+DlgGenLangCode : "भाषा कोड",
+DlgGenAccessKey : "à¤à¤•à¥à¤¸à¥…स की",
+DlgGenName : "नाम",
+DlgGenTabIndex : "टैब इनà¥à¤¡à¥…कà¥à¤¸",
+DlgGenLongDescr : "अधिक विवरण के लिठURL",
+DlgGenClass : "सà¥à¤Ÿà¤¾à¤‡à¤²-शीट कà¥à¤²à¤¾à¤¸",
+DlgGenTitle : "परामरà¥à¤¶ शीरà¥à¤¶à¤•",
+DlgGenContType : "परामरà¥à¤¶ कनà¥à¤Ÿà¥…नà¥à¤Ÿ पà¥à¤°à¤•à¤¾à¤°",
+DlgGenLinkCharset : "लिंक रिसोरà¥à¤¸ करॅकà¥à¤Ÿà¤° सॅट",
+DlgGenStyle : "सà¥à¤Ÿà¤¾à¤‡à¤²",
+
+// Image Dialog
+DlgImgTitle : "तसà¥à¤µà¥€à¤° पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+DlgImgInfoTab : "तसà¥à¤µà¥€à¤° की जानकारी",
+DlgImgBtnUpload : "इसे सरà¥à¤µà¤° को भेजें",
+DlgImgURL : "URL",
+DlgImgUpload : "अपलोड",
+DlgImgAlt : "वैकलà¥à¤ªà¤¿à¤• टेकà¥à¤¸à¥à¤Ÿ",
+DlgImgWidth : "चौड़ाई",
+DlgImgHeight : "ऊà¤à¤šà¤¾à¤ˆ",
+DlgImgLockRatio : "लॉक अनà¥à¤ªà¤¾à¤¤",
+DlgBtnResetSize : "रीसॅट साइज़",
+DlgImgBorder : "बॉरà¥à¤¡à¤°",
+DlgImgHSpace : "हॉरिज़ॉनà¥à¤Ÿà¤² सà¥à¤ªà¥‡à¤¸",
+DlgImgVSpace : "वरà¥à¤Ÿà¤¿à¤•à¤² सà¥à¤ªà¥‡à¤¸",
+DlgImgAlign : "à¤à¤²à¤¾à¤‡à¤¨",
+DlgImgAlignLeft : "दायें",
+DlgImgAlignAbsBottom: "Abs नीचे",
+DlgImgAlignAbsMiddle: "Abs ऊपर",
+DlgImgAlignBaseline : "मूल रेखा",
+DlgImgAlignBottom : "नीचे",
+DlgImgAlignMiddle : "मधà¥à¤¯",
+DlgImgAlignRight : "दायें",
+DlgImgAlignTextTop : "टेकà¥à¤¸à¥à¤Ÿ ऊपर",
+DlgImgAlignTop : "ऊपर",
+DlgImgPreview : "पà¥à¤°à¥€à¤µà¥à¤¯à¥‚",
+DlgImgAlertUrl : "तसà¥à¤µà¥€à¤° का URL टाइप करें ",
+DlgImgLinkTab : "लिंक",
+
+// Flash Dialog
+DlgFlashTitle : "फ़à¥à¤²à¥ˆà¤¶ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+DlgFlashChkPlay : "ऑटो पà¥à¤²à¥‡",
+DlgFlashChkLoop : "लूप",
+DlgFlashChkMenu : "फ़à¥à¤²à¥ˆà¤¶ मॅनà¥à¤¯à¥‚ का पà¥à¤°à¤¯à¥‹à¤— करें",
+DlgFlashScale : "सà¥à¤•à¥‡à¤²",
+DlgFlashScaleAll : "सभी दिखायें",
+DlgFlashScaleNoBorder : "कोई बॉरà¥à¤¡à¤° नहीं",
+DlgFlashScaleFit : "बिलà¥à¤•à¥à¤² फ़िट",
+
+// Link Dialog
+DlgLnkWindowTitle : "लिंक",
+DlgLnkInfoTab : "लिंक ",
+DlgLnkTargetTab : "टारà¥à¤—ेट",
+
+DlgLnkType : "लिंक पà¥à¤°à¤•à¤¾à¤°",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "इस पेज का à¤à¤‚कर",
+DlgLnkTypeEMail : "ई-मेल",
+DlgLnkProto : "पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²",
+DlgLnkProtoOther : "<अनà¥à¤¯>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "à¤à¤‚कर चà¥à¤¨à¥‡à¤‚",
+DlgLnkAnchorByName : "à¤à¤‚कर नाम से",
+DlgLnkAnchorById : "à¤à¤²à¥€à¤®à¥…नà¥à¤Ÿ Id से",
+DlgLnkNoAnchors : "<डॉकà¥à¤¯à¥‚मॅनà¥à¤Ÿ में à¤à¤‚करà¥à¤¸ की संखà¥à¤¯à¤¾>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ई-मेल पता",
+DlgLnkEMailSubject : "संदेश विषय",
+DlgLnkEMailBody : "संदेश",
+DlgLnkUpload : "अपलोड",
+DlgLnkBtnUpload : "इसे सरà¥à¤µà¤° को भेजें",
+
+DlgLnkTarget : "टारà¥à¤—ेट",
+DlgLnkTargetFrame : "<फ़à¥à¤°à¥‡à¤®>",
+DlgLnkTargetPopup : "<पॉप-अप विनà¥à¤¡à¥‹>",
+DlgLnkTargetBlank : "नया विनà¥à¤¡à¥‹ (_blank)",
+DlgLnkTargetParent : "मूल विनà¥à¤¡à¥‹ (_parent)",
+DlgLnkTargetSelf : "इसी विनà¥à¤¡à¥‹ (_self)",
+DlgLnkTargetTop : "शीरà¥à¤· विनà¥à¤¡à¥‹ (_top)",
+DlgLnkTargetFrameName : "टारà¥à¤—ेट फ़à¥à¤°à¥‡à¤® का नाम",
+DlgLnkPopWinName : "पॉप-अप विनà¥à¤¡à¥‹ का नाम",
+DlgLnkPopWinFeat : "पॉप-अप विनà¥à¤¡à¥‹ फ़ीचरà¥à¤¸",
+DlgLnkPopResize : "साइज़ बदला जा सकता है",
+DlgLnkPopLocation : "लोकेशन बार",
+DlgLnkPopMenu : "मॅनà¥à¤¯à¥‚ बार",
+DlgLnkPopScroll : "सà¥à¤•à¥à¤°à¥‰à¤² बार",
+DlgLnkPopStatus : "सà¥à¤Ÿà¥‡à¤Ÿà¤¸ बार",
+DlgLnkPopToolbar : "टूल बार",
+DlgLnkPopFullScrn : "फ़à¥à¤² सà¥à¤•à¥à¤°à¥€à¤¨ (IE)",
+DlgLnkPopDependent : "डिपेनà¥à¤¡à¥…नà¥à¤Ÿ (Netscape)",
+DlgLnkPopWidth : "चौड़ाई",
+DlgLnkPopHeight : "ऊà¤à¤šà¤¾à¤ˆ",
+DlgLnkPopLeft : "बायीं तरफ",
+DlgLnkPopTop : "दायीं तरफ",
+
+DlnLnkMsgNoUrl : "लिंक URL टाइप करें",
+DlnLnkMsgNoEMail : "ई-मेल पता टाइप करें",
+DlnLnkMsgNoAnchor : "à¤à¤‚कर चà¥à¤¨à¥‡à¤‚",
+DlnLnkMsgInvPopName : "पॉप-अप का नाम अलà¥à¤«à¤¾à¤¬à¥‡à¤Ÿ से शà¥à¤°à¥‚ होना चाहिये और उसमें सà¥à¤ªà¥‡à¤¸ नहीं होने चाहिà¤",
+
+// Color Dialog
+DlgColorTitle : "रंग चà¥à¤¨à¥‡à¤‚",
+DlgColorBtnClear : "साफ़ करें",
+DlgColorHighlight : "हाइलाइट",
+DlgColorSelected : "सॅलॅकà¥à¤Ÿà¥…ड",
+
+// Smiley Dialog
+DlgSmileyTitle : "सà¥à¤®à¤¾à¤‡à¤²à¥€ इनà¥à¤¸à¤°à¥à¤Ÿ करें",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "विशेष करॅकà¥à¤Ÿà¤° चà¥à¤¨à¥‡à¤‚",
+
+// Table Dialog
+DlgTableTitle : "टेबल पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+DlgTableRows : "पंकà¥à¤¤à¤¿à¤¯à¤¾à¤",
+DlgTableColumns : "कॉलम",
+DlgTableBorder : "बॉरà¥à¤¡à¤° साइज़",
+DlgTableAlign : "à¤à¤²à¤¾à¤‡à¤¨à¥à¤®à¥…नà¥à¤Ÿ",
+DlgTableAlignNotSet : "<सॅट नहीं>",
+DlgTableAlignLeft : "दायें",
+DlgTableAlignCenter : "बीच में",
+DlgTableAlignRight : "बायें",
+DlgTableWidth : "चौड़ाई",
+DlgTableWidthPx : "पिकà¥à¤¸à¥…ल",
+DlgTableWidthPc : "पà¥à¤°à¤¤à¤¿à¤¶à¤¤",
+DlgTableHeight : "ऊà¤à¤šà¤¾à¤ˆ",
+DlgTableCellSpace : "सॅल अंतर",
+DlgTableCellPad : "सॅल पैडिंग",
+DlgTableCaption : "शीरà¥à¤·à¤•",
+DlgTableSummary : "सारांश",
+
+// Table Cell Dialog
+DlgCellTitle : "सॅल पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+DlgCellWidth : "चौड़ाई",
+DlgCellWidthPx : "पिकà¥à¤¸à¥…ल",
+DlgCellWidthPc : "पà¥à¤°à¤¤à¤¿à¤¶à¤¤",
+DlgCellHeight : "ऊà¤à¤šà¤¾à¤ˆ",
+DlgCellWordWrap : "वरà¥à¤¡ रैप",
+DlgCellWordWrapNotSet : "<सॅट नहीं>",
+DlgCellWordWrapYes : "हाà¤",
+DlgCellWordWrapNo : "नहीं",
+DlgCellHorAlign : "हॉरिज़ॉनà¥à¤Ÿà¤² à¤à¤²à¤¾à¤‡à¤¨à¥à¤®à¥…नà¥à¤Ÿ",
+DlgCellHorAlignNotSet : "<सॅट नहीं>",
+DlgCellHorAlignLeft : "दायें",
+DlgCellHorAlignCenter : "बीच में",
+DlgCellHorAlignRight: "बायें",
+DlgCellVerAlign : "वरà¥à¤Ÿà¤¿à¤•à¤² à¤à¤²à¤¾à¤‡à¤¨à¥à¤®à¥…नà¥à¤Ÿ",
+DlgCellVerAlignNotSet : "<सॅट नहीं>",
+DlgCellVerAlignTop : "ऊपर",
+DlgCellVerAlignMiddle : "मधà¥à¤¯",
+DlgCellVerAlignBottom : "नीचे",
+DlgCellVerAlignBaseline : "मूलरेखा",
+DlgCellRowSpan : "पंकà¥à¤¤à¤¿ सà¥à¤ªà¥ˆà¤¨",
+DlgCellCollSpan : "कॉलम सà¥à¤ªà¥ˆà¤¨",
+DlgCellBackColor : "बैकà¥à¤—à¥à¤°à¤¾à¤‰à¤¨à¥à¤¡ रंग",
+DlgCellBorderColor : "बॉरà¥à¤¡à¤° का रंग",
+DlgCellBtnSelect : "चà¥à¤¨à¥‡à¤‚...",
+
+// Find Dialog
+DlgFindTitle : "खोजें",
+DlgFindFindBtn : "खोजें",
+DlgFindNotFoundMsg : "आपके दà¥à¤µà¤¾à¤°à¤¾ दिया गया टेकà¥à¤¸à¥à¤Ÿ नहीं मिला",
+
+// Replace Dialog
+DlgReplaceTitle : "रिपà¥à¤²à¥‡à¤¸",
+DlgReplaceFindLbl : "यह खोजें:",
+DlgReplaceReplaceLbl : "इससे रिपà¥à¤²à¥‡à¤¸ करें:",
+DlgReplaceCaseChk : "केस मिलायें",
+DlgReplaceReplaceBtn : "रिपà¥à¤²à¥‡à¤¸",
+DlgReplaceReplAllBtn : "सभी रिपà¥à¤²à¥‡à¤¸ करें",
+DlgReplaceWordChk : "पूरा शबà¥à¤¦ मिलायें",
+
+// Paste Operations / Dialog
+PasteErrorCut : "आपके बà¥à¤°à¤¾à¤‰à¥›à¤° की सà¥à¤°à¤•à¥à¤·à¤¾ सॅटिनà¥à¤—à¥à¤¸ ने कट करने की अनà¥à¤®à¤¤à¤¿ नहीं पà¥à¤°à¤¦à¤¾à¤¨ की है। (Ctrl+X) का पà¥à¤°à¤¯à¥‹à¤— करें।",
+PasteErrorCopy : "आपके बà¥à¤°à¤¾à¤†à¤‰à¥›à¤° की सà¥à¤°à¤•à¥à¤·à¤¾ सॅटिनà¥à¤—à¥à¤¸ ने कॉपी करने की अनà¥à¤®à¤¤à¤¿ नहीं पà¥à¤°à¤¦à¤¾à¤¨ की है। (Ctrl+C) का पà¥à¤°à¤¯à¥‹à¤— करें।",
+
+PasteAsText : "पेसà¥à¤Ÿ (सादा टॅकà¥à¤¸à¥à¤Ÿ)",
+PasteFromWord : "पेसà¥à¤Ÿ (वरà¥à¤¡ से)",
+
+DlgPasteMsg2 : "Ctrl+V का पà¥à¤°à¤¯à¥‹à¤— करके पेसà¥à¤Ÿ करें और ठीक है करें.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "फ़ॉनà¥à¤Ÿ परिभाषा निकालें",
+DlgPasteRemoveStyles : "सà¥à¤Ÿà¤¾à¤‡à¤² परिभाषा निकालें",
+DlgPasteCleanBox : "बॉकà¥à¤¸ साफ़ करें",
+
+// Color Picker
+ColorAutomatic : "ऑटोमैटिक",
+ColorMoreColors : "और रंग...",
+
+// Document Properties
+DocProps : "डॉकà¥à¤¯à¥‚मॅनà¥à¤Ÿ पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+
+// Anchor Dialog
+DlgAnchorTitle : "à¤à¤‚कर पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+DlgAnchorName : "à¤à¤‚कर का नाम",
+DlgAnchorErrorName : "à¤à¤‚कर का नाम टाइप करें",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "शबà¥à¤¦à¤•à¥‹à¤¶ में नहीं",
+DlgSpellChangeTo : "इसमें बदलें",
+DlgSpellBtnIgnore : "इगà¥à¤¨à¥‹à¤°",
+DlgSpellBtnIgnoreAll : "सभी इगà¥à¤¨à¥‹à¤° करें",
+DlgSpellBtnReplace : "रिपà¥à¤²à¥‡à¤¸",
+DlgSpellBtnReplaceAll : "सभी रिपà¥à¤²à¥‡à¤¸ करें",
+DlgSpellBtnUndo : "अनà¥à¤¡à¥‚",
+DlgSpellNoSuggestions : "- कोई सà¥à¤à¤¾à¤µ नहीं -",
+DlgSpellProgress : "वरà¥à¤¤à¤¨à¥€ की जाà¤à¤š (सà¥à¤ªà¥…ल-चॅक) जारी है...",
+DlgSpellNoMispell : "वरà¥à¤¤à¤¨à¥€ की जाà¤à¤š : कोई गलत वरà¥à¤¤à¤¨à¥€ (सà¥à¤ªà¥…लिंग) नहीं पाई गई",
+DlgSpellNoChanges : "वरà¥à¤¤à¤¨à¥€ की जाà¤à¤š :कोई शबà¥à¤¦ नहीं बदला गया",
+DlgSpellOneChange : "वरà¥à¤¤à¤¨à¥€ की जाà¤à¤š : à¤à¤• शबà¥à¤¦ बदला गया",
+DlgSpellManyChanges : "वरà¥à¤¤à¤¨à¥€ की जाà¤à¤š : %1 शबà¥à¤¦ बदले गये",
+
+IeSpellDownload : "सà¥à¤ªà¥…ल-चॅकर इनà¥à¤¸à¥à¤Ÿà¤¾à¤² नहीं किया गया है। कà¥à¤¯à¤¾ आप इसे डा‌उनलोड करना चाहेंगे?",
+
+// Button Dialog
+DlgButtonText : "टेकà¥à¤¸à¥à¤Ÿ (वैलà¥à¤¯à¥‚)",
+DlgButtonType : "पà¥à¤°à¤•à¤¾à¤°",
+DlgButtonTypeBtn : "बटन",
+DlgButtonTypeSbm : "सबà¥à¤®à¤¿à¤Ÿ",
+DlgButtonTypeRst : "रिसेट",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "नाम",
+DlgCheckboxValue : "वैलà¥à¤¯à¥‚",
+DlgCheckboxSelected : "सॅलॅकà¥à¤Ÿà¥…ड",
+
+// Form Dialog
+DlgFormName : "नाम",
+DlgFormAction : "à¤à¤•à¥à¤¶à¤¨",
+DlgFormMethod : "तरीका",
+
+// Select Field Dialog
+DlgSelectName : "नाम",
+DlgSelectValue : "वैलà¥à¤¯à¥‚",
+DlgSelectSize : "साइज़",
+DlgSelectLines : "पंकà¥à¤¤à¤¿à¤¯à¤¾à¤",
+DlgSelectChkMulti : "à¤à¤• से जà¥à¤¯à¤¾à¤¦à¤¾ विकलà¥à¤ª चà¥à¤¨à¤¨à¥‡ दें",
+DlgSelectOpAvail : "उपलबà¥à¤§ विकलà¥à¤ª",
+DlgSelectOpText : "टेकà¥à¤¸à¥à¤Ÿ",
+DlgSelectOpValue : "वैलà¥à¤¯à¥‚",
+DlgSelectBtnAdd : "जोड़ें",
+DlgSelectBtnModify : "बदलें",
+DlgSelectBtnUp : "ऊपर",
+DlgSelectBtnDown : "नीचे",
+DlgSelectBtnSetValue : "चà¥à¤¨à¥€ गई वैलà¥à¤¯à¥‚ सॅट करें",
+DlgSelectBtnDelete : "डिलीट",
+
+// Textarea Dialog
+DlgTextareaName : "नाम",
+DlgTextareaCols : "कॉलम",
+DlgTextareaRows : "पंकà¥à¤¤à¤¿à¤¯à¤¾à¤‚",
+
+// Text Field Dialog
+DlgTextName : "नाम",
+DlgTextValue : "वैलà¥à¤¯à¥‚",
+DlgTextCharWidth : "करॅकà¥à¤Ÿà¤° की चौà¥à¤¾à¤ˆ",
+DlgTextMaxChars : "अधिकतम करॅकà¥à¤Ÿà¤°",
+DlgTextType : "टाइप",
+DlgTextTypeText : "टेकà¥à¤¸à¥à¤Ÿ",
+DlgTextTypePass : "पासà¥à¤µà¤°à¥à¤¡",
+
+// Hidden Field Dialog
+DlgHiddenName : "नाम",
+DlgHiddenValue : "वैलà¥à¤¯à¥‚",
+
+// Bulleted List Dialog
+BulletedListProp : "बà¥à¤²à¥…ट सूची पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+NumberedListProp : "अंकीय सूची पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€à¥›",
+DlgLstStart : "पà¥à¤°à¤¾à¤°à¤®à¥à¤­",
+DlgLstType : "पà¥à¤°à¤•à¤¾à¤°",
+DlgLstTypeCircle : "गोल",
+DlgLstTypeDisc : "डिसà¥à¤•",
+DlgLstTypeSquare : "चौकॊण",
+DlgLstTypeNumbers : "अंक (1, 2, 3)",
+DlgLstTypeLCase : "छोटे अकà¥à¤·à¤° (a, b, c)",
+DlgLstTypeUCase : "बड़े अकà¥à¤·à¤° (A, B, C)",
+DlgLstTypeSRoman : "छोटे रोमन अंक (i, ii, iii)",
+DlgLstTypeLRoman : "बड़े रोमन अंक (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "आम",
+DlgDocBackTab : "बैकà¥à¤—à¥à¤°à¤¾à¤‰à¤¨à¥à¤¡",
+DlgDocColorsTab : "रंग और मारà¥à¤œà¤¿à¤¨",
+DlgDocMetaTab : "मॅटाडेटा",
+
+DlgDocPageTitle : "पेज शीरà¥à¤·à¤•",
+DlgDocLangDir : "भाषा लिखने की दिशा",
+DlgDocLangDirLTR : "बायें से दायें (LTR)",
+DlgDocLangDirRTL : "दायें से बायें (RTL)",
+DlgDocLangCode : "भाषा कोड",
+DlgDocCharSet : "करेकà¥à¤Ÿà¤° सॅट à¤à¤¨à¥à¤•à¥‹à¤¡à¤¿à¤‚ग",
+DlgDocCharSetCE : "मधà¥à¤¯ यूरोपीय (Central European)",
+DlgDocCharSetCT : "चीनी (Chinese Traditional Big5)",
+DlgDocCharSetCR : "सिरीलिक (Cyrillic)",
+DlgDocCharSetGR : "यवन (Greek)",
+DlgDocCharSetJP : "जापानी (Japanese)",
+DlgDocCharSetKR : "कोरीयन (Korean)",
+DlgDocCharSetTR : "तà¥à¤°à¥à¤•à¥€ (Turkish)",
+DlgDocCharSetUN : "यूनीकोड (UTF-8)",
+DlgDocCharSetWE : "पशà¥à¤šà¤¿à¤® यूरोपीय (Western European)",
+DlgDocCharSetOther : "अनà¥à¤¯ करेकà¥à¤Ÿà¤° सॅट à¤à¤¨à¥à¤•à¥‹à¤¡à¤¿à¤‚ग",
+
+DlgDocDocType : "डॉकà¥à¤¯à¥‚मॅनà¥à¤Ÿ पà¥à¤°à¤•à¤¾à¤° शीरà¥à¤·à¤•",
+DlgDocDocTypeOther : "अनà¥à¤¯ डॉकà¥à¤¯à¥‚मॅनà¥à¤Ÿ पà¥à¤°à¤•à¤¾à¤° शीरà¥à¤·à¤•",
+DlgDocIncXHTML : "XHTML सूचना समà¥à¤®à¤¿à¤²à¤¿à¤¤ करें",
+DlgDocBgColor : "बैकà¥à¤—à¥à¤°à¤¾à¤‰à¤¨à¥à¤¡ रंग",
+DlgDocBgImage : "बैकà¥à¤—à¥à¤°à¤¾à¤‰à¤¨à¥à¤¡ तसà¥à¤µà¥€à¤° URL",
+DlgDocBgNoScroll : "सà¥à¤•à¥à¤°à¥‰à¤² न करने वाला बैकà¥à¤—à¥à¤°à¤¾à¤‰à¤¨à¥à¤¡",
+DlgDocCText : "टेकà¥à¤¸à¥à¤Ÿ",
+DlgDocCLink : "लिंक",
+DlgDocCVisited : "विज़िट किया गया लिंक",
+DlgDocCActive : "सकà¥à¤°à¤¿à¤¯ लिंक",
+DlgDocMargins : "पेज मारà¥à¤œà¤¿à¤¨",
+DlgDocMaTop : "ऊपर",
+DlgDocMaLeft : "बायें",
+DlgDocMaRight : "दायें",
+DlgDocMaBottom : "नीचे",
+DlgDocMeIndex : "डॉकà¥à¤¯à¥à¤®à¥…नà¥à¤Ÿ इनà¥à¤¡à¥‡à¤•à¥à¤¸ संकेतशबà¥à¤¦ (अलà¥à¤ªà¤µà¤¿à¤°à¤¾à¤® से अलग करें)",
+DlgDocMeDescr : "डॉकà¥à¤¯à¥‚मॅनà¥à¤Ÿ करॅकà¥à¤Ÿà¤°à¤¨",
+DlgDocMeAuthor : "लेखक",
+DlgDocMeCopy : "कॉपीराइट",
+DlgDocPreview : "पà¥à¤°à¥€à¤µà¥à¤¯à¥‚",
+
+// Templates Dialog
+Templates : "टॅमà¥à¤ªà¥à¤²à¥‡à¤Ÿ",
+DlgTemplatesTitle : "कनà¥à¤Ÿà¥‡à¤¨à¥à¤Ÿ टॅमà¥à¤ªà¥à¤²à¥‡à¤Ÿ",
+DlgTemplatesSelMsg : "à¤à¤¡à¤¿à¤Ÿà¤° में ओपन करने हेतॠटॅमà¥à¤ªà¥à¤²à¥‡à¤Ÿ चà¥à¤¨à¥‡à¤‚(वरà¥à¤¤à¤®à¤¾à¤¨ कनà¥à¤Ÿà¥…नà¥à¤Ÿ सेव नहीं होंगे):",
+DlgTemplatesLoading : "टॅमà¥à¤ªà¥à¤²à¥‡à¤Ÿ सूची लोड की जा रही है। ज़रा ठहरें...",
+DlgTemplatesNoTpl : "(कोई टॅमà¥à¤ªà¥à¤²à¥‡à¤Ÿ डिफ़ाइन नहीं किया गया है)",
+DlgTemplatesReplace : "मूल शबà¥à¤¦à¥‹à¤‚ को बदलें",
+
+// About Dialog
+DlgAboutAboutTab : "FCKEditor के बारे में",
+DlgAboutBrowserInfoTab : "बà¥à¤°à¤¾à¤‰à¥›à¤° के बारे में",
+DlgAboutLicenseTab : "लाइसैनà¥à¤¸",
+DlgAboutVersion : "वरà¥à¥›à¤¨",
+DlgAboutInfo : "अधिक जानकारी के लिये यहाठजायें:"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/hr.js b/httemplate/elements/fckeditor/editor/lang/hr.js
new file mode 100644
index 0000000..f088b10
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/hr.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Croatian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Smanji trake s alatima",
+ToolbarExpand : "Proširi trake s alatima",
+
+// Toolbar Items and Context Menu
+Save : "Snimi",
+NewPage : "Nova stranica",
+Preview : "Pregledaj",
+Cut : "Izreži",
+Copy : "Kopiraj",
+Paste : "Zalijepi",
+PasteText : "Zalijepi kao Äisti tekst",
+PasteWord : "Zalijepi iz Worda",
+Print : "Ispiši",
+SelectAll : "Odaberi sve",
+RemoveFormat : "Ukloni formatiranje",
+InsertLinkLbl : "Link",
+InsertLink : "Ubaci/promijeni link",
+RemoveLink : "Ukloni link",
+Anchor : "Ubaci/promijeni sidro",
+InsertImageLbl : "Slika",
+InsertImage : "Ubaci/promijeni sliku",
+InsertFlashLbl : "Flash",
+InsertFlash : "Ubaci/promijeni Flash",
+InsertTableLbl : "Tablica",
+InsertTable : "Ubaci/promijeni tablicu",
+InsertLineLbl : "Linija",
+InsertLine : "Ubaci vodoravnu liniju",
+InsertSpecialCharLbl: "Posebni karakteri",
+InsertSpecialChar : "Ubaci posebne znakove",
+InsertSmileyLbl : "Smješko",
+InsertSmiley : "Ubaci smješka",
+About : "O FCKeditoru",
+Bold : "Podebljaj",
+Italic : "Ukosi",
+Underline : "Potcrtano",
+StrikeThrough : "Precrtano",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Lijevo poravnanje",
+CenterJustify : "Središnje poravnanje",
+RightJustify : "Desno poravnanje",
+BlockJustify : "Blok poravnanje",
+DecreaseIndent : "Pomakni ulijevo",
+IncreaseIndent : "Pomakni udesno",
+Undo : "Poništi",
+Redo : "Ponovi",
+NumberedListLbl : "BrojÄana lista",
+NumberedList : "Ubaci/ukloni brojÄanu listu",
+BulletedListLbl : "ObiÄna lista",
+BulletedList : "Ubaci/ukloni obiÄnu listu",
+ShowTableBorders : "Prikaži okvir tablice",
+ShowDetails : "Prikaži detalje",
+Style : "Stil",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "VeliÄina",
+TextColor : "Boja teksta",
+BGColor : "Boja pozadine",
+Source : "Kôd",
+Find : "Pronađi",
+Replace : "Zamijeni",
+SpellCheck : "Provjeri pravopis",
+UniversalKeyboard : "Univerzalna tipkovnica",
+PageBreakLbl : "Prijelom stranice",
+PageBreak : "Ubaci prijelom stranice",
+
+Form : "Form",
+Checkbox : "Checkbox",
+RadioButton : "Radio Button",
+TextField : "Text Field",
+Textarea : "Textarea",
+HiddenField : "Hidden Field",
+Button : "Button",
+SelectionField : "Selection Field",
+ImageButton : "Image Button",
+
+FitWindow : "Povećaj veliÄinu editora",
+
+// Context Menu
+EditLink : "Promijeni link",
+CellCM : "Ćelija",
+RowCM : "Red",
+ColumnCM : "Kolona",
+InsertRow : "Ubaci red",
+DeleteRows : "Izbriši redove",
+InsertColumn : "Ubaci kolonu",
+DeleteColumns : "Izbriši kolone",
+InsertCell : "Ubaci ćelije",
+DeleteCells : "Izbriši ćelije",
+MergeCells : "Spoji ćelije",
+SplitCell : "Razdvoji ćelije",
+TableDelete : "Izbriši tablicu",
+CellProperties : "Svojstva ćelije",
+TableProperties : "Svojstva tablice",
+ImageProperties : "Svojstva slike",
+FlashProperties : "Flash svojstva",
+
+AnchorProp : "Svojstva sidra",
+ButtonProp : "Image Button svojstva",
+CheckboxProp : "Checkbox svojstva",
+HiddenFieldProp : "Hidden Field svojstva",
+RadioButtonProp : "Radio Button svojstva",
+ImageButtonProp : "Image Button svojstva",
+TextFieldProp : "Text Field svojstva",
+SelectionFieldProp : "Selection svojstva",
+TextareaProp : "Textarea svojstva",
+FormProp : "Form svojstva",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "ObraÄ‘ujem XHTML. Molimo priÄekajte...",
+Done : "Završio",
+PasteWordConfirm : "Tekst koji želite zalijepiti Äini se da je kopiran iz Worda. Želite li prije oÄistiti tekst?",
+NotCompatiblePaste : "Ova naredba je dostupna samo u Internet Exploreru 5.5 ili novijem. Želite li nastaviti bez Äišćenja?",
+UnknownToolbarItem : "Nepoznati Älan trake s alatima \"%1\"",
+UnknownCommand : "Nepoznata naredba \"%1\"",
+NotImplemented : "Naredba nije implementirana",
+UnknownToolbarSet : "Traka s alatima \"%1\" ne postoji",
+NoActiveX : "VaÅ¡e postavke pretraživaÄa mogle bi ograniÄiti neke od mogućnosti editora. Morate ukljuÄiti opciju \"Run ActiveX controls and plug-ins\" u postavkama. Ukoliko to ne uÄinite, moguće su razliite greÅ¡ke tijekom rada.",
+BrowseServerBlocked : "PretraivaÄ nije moguće otvoriti. Provjerite da li je ukljuÄeno blokiranje pop-up prozora.",
+DialogBlocked : "Nije moguće otvoriti novi prozor. Provjerite da li je ukljuÄeno blokiranje pop-up prozora.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Poništi",
+DlgBtnClose : "Zatvori",
+DlgBtnBrowseServer : "Pretraži server",
+DlgAdvancedTag : "Napredno",
+DlgOpOther : "<Drugo>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Molimo unesite URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nije postavljeno>",
+DlgGenId : "Id",
+DlgGenLangDir : "Smjer jezika",
+DlgGenLangDirLtr : "S lijeva na desno (LTR)",
+DlgGenLangDirRtl : "S desna na lijevo (RTL)",
+DlgGenLangCode : "Kôd jezika",
+DlgGenAccessKey : "Pristupna tipka",
+DlgGenName : "Naziv",
+DlgGenTabIndex : "Tab Indeks",
+DlgGenLongDescr : "DugaÄki opis URL",
+DlgGenClass : "Stylesheet klase",
+DlgGenTitle : "Advisory naslov",
+DlgGenContType : "Advisory vrsta sadržaja",
+DlgGenLinkCharset : "Kodna stranica povezanih resursa",
+DlgGenStyle : "Stil",
+
+// Image Dialog
+DlgImgTitle : "Svojstva slika",
+DlgImgInfoTab : "Info slike",
+DlgImgBtnUpload : "Pošalji na server",
+DlgImgURL : "URL",
+DlgImgUpload : "Pošalji",
+DlgImgAlt : "Alternativni tekst",
+DlgImgWidth : "Å irina",
+DlgImgHeight : "Visina",
+DlgImgLockRatio : "ZakljuÄaj odnos",
+DlgBtnResetSize : "ObriÅ¡i veliÄinu",
+DlgImgBorder : "Okvir",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Poravnaj",
+DlgImgAlignLeft : "Lijevo",
+DlgImgAlignAbsBottom: "Abs dolje",
+DlgImgAlignAbsMiddle: "Abs sredina",
+DlgImgAlignBaseline : "Bazno",
+DlgImgAlignBottom : "Dolje",
+DlgImgAlignMiddle : "Sredina",
+DlgImgAlignRight : "Desno",
+DlgImgAlignTextTop : "Vrh teksta",
+DlgImgAlignTop : "Vrh",
+DlgImgPreview : "Pregledaj",
+DlgImgAlertUrl : "Unesite URL slike",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Flash svojstva",
+DlgFlashChkPlay : "Auto Play",
+DlgFlashChkLoop : "Ponavljaj",
+DlgFlashChkMenu : "Omogući Flash izbornik",
+DlgFlashScale : "Omjer",
+DlgFlashScaleAll : "Prikaži sve",
+DlgFlashScaleNoBorder : "Bez okvira",
+DlgFlashScaleFit : "ToÄna veliÄina",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link Info",
+DlgLnkTargetTab : "Meta",
+
+DlgLnkType : "Link vrsta",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Sidro na ovoj stranici",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<drugo>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Odaberi sidro",
+DlgLnkAnchorByName : "Po nazivu sidra",
+DlgLnkAnchorById : "Po Id elementa",
+DlgLnkNoAnchors : "<Nema dostupnih sidra>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail adresa",
+DlgLnkEMailSubject : "Naslov",
+DlgLnkEMailBody : "Sadržaj poruke",
+DlgLnkUpload : "Pošalji",
+DlgLnkBtnUpload : "Pošalji na server",
+
+DlgLnkTarget : "Meta",
+DlgLnkTargetFrame : "<okvir>",
+DlgLnkTargetPopup : "<popup prozor>",
+DlgLnkTargetBlank : "Novi prozor (_blank)",
+DlgLnkTargetParent : "Roditeljski prozor (_parent)",
+DlgLnkTargetSelf : "Isti prozor (_self)",
+DlgLnkTargetTop : "Vršni prozor (_top)",
+DlgLnkTargetFrameName : "Ime ciljnog okvira",
+DlgLnkPopWinName : "Naziv popup prozora",
+DlgLnkPopWinFeat : "Mogućnosti popup prozora",
+DlgLnkPopResize : "Promjenljive veliÄine",
+DlgLnkPopLocation : "Traka za lokaciju",
+DlgLnkPopMenu : "Izborna traka",
+DlgLnkPopScroll : "Scroll traka",
+DlgLnkPopStatus : "Statusna traka",
+DlgLnkPopToolbar : "Traka s alatima",
+DlgLnkPopFullScrn : "Cijeli ekran (IE)",
+DlgLnkPopDependent : "Ovisno (Netscape)",
+DlgLnkPopWidth : "Å irina",
+DlgLnkPopHeight : "Visina",
+DlgLnkPopLeft : "Lijeva pozicija",
+DlgLnkPopTop : "Gornja pozicija",
+
+DlnLnkMsgNoUrl : "Molimo upišite URL link",
+DlnLnkMsgNoEMail : "Molimo upišite e-mail adresu",
+DlnLnkMsgNoAnchor : "Molimo odaberite sidro",
+DlnLnkMsgInvPopName : "Ime popup prozora mora poÄeti sa slovom i ne smije sadržavati razmake",
+
+// Color Dialog
+DlgColorTitle : "Odaberite boju",
+DlgColorBtnClear : "Obriši",
+DlgColorHighlight : "Osvijetli",
+DlgColorSelected : "Odaberi",
+
+// Smiley Dialog
+DlgSmileyTitle : "Ubaci smješka",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Odaberite posebni karakter",
+
+// Table Dialog
+DlgTableTitle : "Svojstva tablice",
+DlgTableRows : "Redova",
+DlgTableColumns : "Kolona",
+DlgTableBorder : "VeliÄina okvira",
+DlgTableAlign : "Poravnanje",
+DlgTableAlignNotSet : "<nije postavljeno>",
+DlgTableAlignLeft : "Lijevo",
+DlgTableAlignCenter : "Središnje",
+DlgTableAlignRight : "Desno",
+DlgTableWidth : "Å irina",
+DlgTableWidthPx : "piksela",
+DlgTableWidthPc : "postotaka",
+DlgTableHeight : "Visina",
+DlgTableCellSpace : "Prostornost ćelija",
+DlgTableCellPad : "Razmak ćelija",
+DlgTableCaption : "Naslov",
+DlgTableSummary : "Sažetak",
+
+// Table Cell Dialog
+DlgCellTitle : "Svojstva ćelije",
+DlgCellWidth : "Å irina",
+DlgCellWidthPx : "piksela",
+DlgCellWidthPc : "postotaka",
+DlgCellHeight : "Visina",
+DlgCellWordWrap : "Word Wrap",
+DlgCellWordWrapNotSet : "<nije postavljeno>",
+DlgCellWordWrapYes : "Da",
+DlgCellWordWrapNo : "Ne",
+DlgCellHorAlign : "Vodoravno poravnanje",
+DlgCellHorAlignNotSet : "<nije postavljeno>",
+DlgCellHorAlignLeft : "Lijevo",
+DlgCellHorAlignCenter : "Središnje",
+DlgCellHorAlignRight: "Desno",
+DlgCellVerAlign : "Okomito poravnanje",
+DlgCellVerAlignNotSet : "<nije postavljeno>",
+DlgCellVerAlignTop : "Gornje",
+DlgCellVerAlignMiddle : "Srednišnje",
+DlgCellVerAlignBottom : "Donje",
+DlgCellVerAlignBaseline : "Bazno",
+DlgCellRowSpan : "Spajanje redova",
+DlgCellCollSpan : "Spajanje kolona",
+DlgCellBackColor : "Boja pozadine",
+DlgCellBorderColor : "Boja okvira",
+DlgCellBtnSelect : "Odaberi...",
+
+// Find Dialog
+DlgFindTitle : "Pronađi",
+DlgFindFindBtn : "Pronađi",
+DlgFindNotFoundMsg : "Traženi tekst nije pronađen.",
+
+// Replace Dialog
+DlgReplaceTitle : "Zamijeni",
+DlgReplaceFindLbl : "Pronađi:",
+DlgReplaceReplaceLbl : "Zamijeni s:",
+DlgReplaceCaseChk : "Usporedi mala/velika slova",
+DlgReplaceReplaceBtn : "Zamijeni",
+DlgReplaceReplAllBtn : "Zamijeni sve",
+DlgReplaceWordChk : "Usporedi cijele rijeÄi",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Sigurnosne postavke VaÅ¡eg pretraživaÄa ne dozvoljavaju operacije automatskog izrezivanja. Molimo koristite kraticu na tipkovnici (Ctrl+X).",
+PasteErrorCopy : "Sigurnosne postavke VaÅ¡eg pretraživaÄa ne dozvoljavaju operacije automatskog kopiranja. Molimo koristite kraticu na tipkovnici (Ctrl+C).",
+
+PasteAsText : "Zalijepi kao Äisti tekst",
+PasteFromWord : "Zalijepi iz Worda",
+
+DlgPasteMsg2 : "Molimo zaljepite unutar doljnjeg okvira koristeći tipkovnicu (<STRONG>Ctrl+V</STRONG>) i kliknite <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Zanemari definiciju vrste fonta",
+DlgPasteRemoveStyles : "Ukloni definicije stilova",
+DlgPasteCleanBox : "OÄisti okvir",
+
+// Color Picker
+ColorAutomatic : "Automatski",
+ColorMoreColors : "Više boja...",
+
+// Document Properties
+DocProps : "Svojstva dokumenta",
+
+// Anchor Dialog
+DlgAnchorTitle : "Svojstva sidra",
+DlgAnchorName : "Ime sidra",
+DlgAnchorErrorName : "Molimo unesite ime sidra",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Nije u rjeÄniku",
+DlgSpellChangeTo : "Promijeni u",
+DlgSpellBtnIgnore : "Zanemari",
+DlgSpellBtnIgnoreAll : "Zanemari sve",
+DlgSpellBtnReplace : "Zamijeni",
+DlgSpellBtnReplaceAll : "Zamijeni sve",
+DlgSpellBtnUndo : "Vrati",
+DlgSpellNoSuggestions : "-Nema preporuke-",
+DlgSpellProgress : "Provjera u tijeku...",
+DlgSpellNoMispell : "Provjera završena: Nema grešaka",
+DlgSpellNoChanges : "Provjera završena: Nije napravljena promjena",
+DlgSpellOneChange : "Provjera zavrÅ¡ena: Jedna rijeÄ promjenjena",
+DlgSpellManyChanges : "Provjera zavrÅ¡ena: Promijenjeno %1 rijeÄi",
+
+IeSpellDownload : "Provjera pravopisa nije instalirana. Želite li skinuti provjeru pravopisa?",
+
+// Button Dialog
+DlgButtonText : "Tekst (vrijednost)",
+DlgButtonType : "Vrsta",
+DlgButtonTypeBtn : "Gumb",
+DlgButtonTypeSbm : "Pošalji",
+DlgButtonTypeRst : "Poništi",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Ime",
+DlgCheckboxValue : "Vrijednost",
+DlgCheckboxSelected : "Odabrano",
+
+// Form Dialog
+DlgFormName : "Ime",
+DlgFormAction : "Akcija",
+DlgFormMethod : "Metoda",
+
+// Select Field Dialog
+DlgSelectName : "Ime",
+DlgSelectValue : "Vrijednost",
+DlgSelectSize : "VeliÄina",
+DlgSelectLines : "linija",
+DlgSelectChkMulti : "Dozvoli višestruki odabir",
+DlgSelectOpAvail : "Dostupne opcije",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Vrijednost",
+DlgSelectBtnAdd : "Dodaj",
+DlgSelectBtnModify : "Promijeni",
+DlgSelectBtnUp : "Gore",
+DlgSelectBtnDown : "Dolje",
+DlgSelectBtnSetValue : "Postavi kao odabranu vrijednost",
+DlgSelectBtnDelete : "Obriši",
+
+// Textarea Dialog
+DlgTextareaName : "Ime",
+DlgTextareaCols : "Kolona",
+DlgTextareaRows : "Redova",
+
+// Text Field Dialog
+DlgTextName : "Ime",
+DlgTextValue : "Vrijednost",
+DlgTextCharWidth : "Å irina",
+DlgTextMaxChars : "Najviše karaktera",
+DlgTextType : "Vrsta",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Å ifra",
+
+// Hidden Field Dialog
+DlgHiddenName : "Ime",
+DlgHiddenValue : "Vrijednost",
+
+// Bulleted List Dialog
+BulletedListProp : "Svojstva liste",
+NumberedListProp : "Svojstva brojÄane liste",
+DlgLstStart : "PoÄetak",
+DlgLstType : "Vrsta",
+DlgLstTypeCircle : "Krug",
+DlgLstTypeDisc : "Disk",
+DlgLstTypeSquare : "Kvadrat",
+DlgLstTypeNumbers : "Brojevi (1, 2, 3)",
+DlgLstTypeLCase : "Mala slova (a, b, c)",
+DlgLstTypeUCase : "Velika slova (A, B, C)",
+DlgLstTypeSRoman : "Male rimske brojke (i, ii, iii)",
+DlgLstTypeLRoman : "Velike rimske brojke (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Općenito",
+DlgDocBackTab : "Pozadina",
+DlgDocColorsTab : "Boje i margine",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Naslov stranice",
+DlgDocLangDir : "Smjer jezika",
+DlgDocLangDirLTR : "S lijeva na desno",
+DlgDocLangDirRTL : "S desna na lijevo",
+DlgDocLangCode : "Kôd jezika",
+DlgDocCharSet : "Enkodiranje znakova",
+DlgDocCharSetCE : "Središnja Europa",
+DlgDocCharSetCT : "Tradicionalna kineska (Big5)",
+DlgDocCharSetCR : "Ćirilica",
+DlgDocCharSetGR : "GrÄka",
+DlgDocCharSetJP : "Japanska",
+DlgDocCharSetKR : "Koreanska",
+DlgDocCharSetTR : "Turska",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Zapadna Europa",
+DlgDocCharSetOther : "Ostalo enkodiranje znakova",
+
+DlgDocDocType : "Zaglavlje vrste dokumenta",
+DlgDocDocTypeOther : "Ostalo zaglavlje vrste dokumenta",
+DlgDocIncXHTML : "Ubaci XHTML deklaracije",
+DlgDocBgColor : "Boja pozadine",
+DlgDocBgImage : "URL slike pozadine",
+DlgDocBgNoScroll : "Pozadine se ne pomiÄe",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Posjećeni link",
+DlgDocCActive : "Aktivni link",
+DlgDocMargins : "Margine stranice",
+DlgDocMaTop : "Vrh",
+DlgDocMaLeft : "Lijevo",
+DlgDocMaRight : "Desno",
+DlgDocMaBottom : "Dolje",
+DlgDocMeIndex : "KljuÄne rijeÄi dokumenta (odvojene zarezom)",
+DlgDocMeDescr : "Opis dokumenta",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Autorska prava",
+DlgDocPreview : "Pregledaj",
+
+// Templates Dialog
+Templates : "Predlošci",
+DlgTemplatesTitle : "Predlošci sadržaja",
+DlgTemplatesSelMsg : "Molimo odaberite predložak koji želite otvoriti<br>(stvarni sadržaj će biti izgubljen):",
+DlgTemplatesLoading : "UÄitavam listu predložaka. Molimo priÄekajte...",
+DlgTemplatesNoTpl : "(Nema definiranih predložaka)",
+DlgTemplatesReplace : "Zamijeni trenutne sadržaje",
+
+// About Dialog
+DlgAboutAboutTab : "O FCKEditoru",
+DlgAboutBrowserInfoTab : "Podaci o pretraživaÄu",
+DlgAboutLicenseTab : "Licenca",
+DlgAboutVersion : "inaÄica",
+DlgAboutInfo : "Za više informacija posjetite"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/hu.js b/httemplate/elements/fckeditor/editor/lang/hu.js
new file mode 100644
index 0000000..73b912c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/hu.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Hungarian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Eszköztár elrejtése",
+ToolbarExpand : "Eszköztár megjelenítése",
+
+// Toolbar Items and Context Menu
+Save : "Mentés",
+NewPage : "Új oldal",
+Preview : "Előnézet",
+Cut : "Kivágás",
+Copy : "Másolás",
+Paste : "Beillesztés",
+PasteText : "Beillesztés formázás nélkül",
+PasteWord : "Beillesztés Word-ből",
+Print : "Nyomtatás",
+SelectAll : "Mindent kijelöl",
+RemoveFormat : "Formázás eltávolítása",
+InsertLinkLbl : "Hivatkozás",
+InsertLink : "Hivatkozás beillesztése/módosítása",
+RemoveLink : "Hivatkozás törlése",
+Anchor : "Horgony beillesztése/szerkesztése",
+InsertImageLbl : "Kép",
+InsertImage : "Kép beillesztése/módosítása",
+InsertFlashLbl : "Flash",
+InsertFlash : "Flash beillesztése, módosítása",
+InsertTableLbl : "Táblázat",
+InsertTable : "Táblázat beillesztése/módosítása",
+InsertLineLbl : "Vonal",
+InsertLine : "Elválasztóvonal beillesztése",
+InsertSpecialCharLbl: "Speciális karakter",
+InsertSpecialChar : "Speciális karakter beillesztése",
+InsertSmileyLbl : "Hangulatjelek",
+InsertSmiley : "Hangulatjelek beillesztése",
+About : "FCKeditor névjegy",
+Bold : "Félkövér",
+Italic : "DÅ‘lt",
+Underline : "Aláhúzott",
+StrikeThrough : "Ãthúzott",
+Subscript : "Alsó index",
+Superscript : "Felső index",
+LeftJustify : "Balra",
+CenterJustify : "Középre",
+RightJustify : "Jobbra",
+BlockJustify : "Sorkizárt",
+DecreaseIndent : "Behúzás csökkentése",
+IncreaseIndent : "Behúzás növelése",
+Undo : "Visszavonás",
+Redo : "Ismétlés",
+NumberedListLbl : "Számozás",
+NumberedList : "Számozás beillesztése/törlése",
+BulletedListLbl : "Felsorolás",
+BulletedList : "Felsorolás beillesztése/törlése",
+ShowTableBorders : "Táblázat szegély mutatása",
+ShowDetails : "Részletek mutatása",
+Style : "Stílus",
+FontFormat : "Formátum",
+Font : "Betűtípus",
+FontSize : "Méret",
+TextColor : "Betűszín",
+BGColor : "Háttérszín",
+Source : "Forráskód",
+Find : "Keresés",
+Replace : "Csere",
+SpellCheck : "Helyesírás-ellenőrzés",
+UniversalKeyboard : "Univerzális billentyűzet",
+PageBreakLbl : "Oldaltörés",
+PageBreak : "Oldaltörés beillesztése",
+
+Form : "Å°rlap",
+Checkbox : "Jelölőnégyzet",
+RadioButton : "Választógomb",
+TextField : "Szövegmező",
+Textarea : "Szövegterület",
+HiddenField : "Rejtettmező",
+Button : "Gomb",
+SelectionField : "Legördülő lista",
+ImageButton : "Képgomb",
+
+FitWindow : "Maximalizálás",
+
+// Context Menu
+EditLink : "Hivatkozás módosítása",
+CellCM : "Cella",
+RowCM : "Sor",
+ColumnCM : "Oszlop",
+InsertRow : "Sor beszúrása",
+DeleteRows : "Sorok törlése",
+InsertColumn : "Oszlop beszúrása",
+DeleteColumns : "Oszlopok törlése",
+InsertCell : "Cella beszúrása",
+DeleteCells : "Cellák törlése",
+MergeCells : "Cellák egyesítése",
+SplitCell : "Cella szétválasztása",
+TableDelete : "Táblázat törlése",
+CellProperties : "Cella tulajdonságai",
+TableProperties : "Táblázat tulajdonságai",
+ImageProperties : "Kép tulajdonságai",
+FlashProperties : "Flash tulajdonságai",
+
+AnchorProp : "Horgony tulajdonságai",
+ButtonProp : "Gomb tulajdonságai",
+CheckboxProp : "Jelölőnégyzet tulajdonságai",
+HiddenFieldProp : "Rejtett mező tulajdonságai",
+RadioButtonProp : "Választógomb tulajdonságai",
+ImageButtonProp : "Képgomb tulajdonságai",
+TextFieldProp : "Szövegmező tulajdonságai",
+SelectionFieldProp : "Legördülő lista tulajdonságai",
+TextareaProp : "Szövegterület tulajdonságai",
+FormProp : "Űrlap tulajdonságai",
+
+FontFormats : "Normál;Formázott;Címsor;Fejléc 1;Fejléc 2;Fejléc 3;Fejléc 4;Fejléc 5;Fejléc 6;Bekezdés (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML feldolgozása. Kérem várjon...",
+Done : "Kész",
+PasteWordConfirm : "A beilleszteni kívánt szöveg Word-ből van másolva. El kívánja távolítani a formázást a beillesztés előtt?",
+NotCompatiblePaste : "Ez a parancs csak Internet Explorer 5.5 verziótól használható. Megpróbálja beilleszteni a szöveget az eredeti formázással?",
+UnknownToolbarItem : "Ismeretlen eszköztár elem \"%1\"",
+UnknownCommand : "Ismeretlen parancs \"%1\"",
+NotImplemented : "A parancs nem hajtható végre",
+UnknownToolbarSet : "Az eszközkészlet \"%1\" nem létezik",
+NoActiveX : "A böngésző biztonsági beállításai korlátozzák a szerkesztő lehetőségeit. Engedélyezni kell ezt az opciót: \"Run ActiveX controls and plug-ins\". Ettől függetlenül előfordulhatnak hibaüzenetek ill. bizonyos funkciók hiányozhatnak.",
+BrowseServerBlocked : "Nem lehet megnyitni a fájlböngészőt. Bizonyosodjon meg róla, hogy a felbukkanó ablakok engedélyezve vannak.",
+DialogBlocked : "Nem lehet megnyitni a párbeszédablakot. Bizonyosodjon meg róla, hogy a felbukkanó ablakok engedélyezve vannak.",
+
+// Dialogs
+DlgBtnOK : "Rendben",
+DlgBtnCancel : "Mégsem",
+DlgBtnClose : "Bezárás",
+DlgBtnBrowseServer : "Böngészés a szerveren",
+DlgAdvancedTag : "További opciók",
+DlgOpOther : "Egyéb",
+DlgInfoTab : "Alaptulajdonságok",
+DlgAlertUrl : "Illessze be a webcímet",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nincs beállítva>",
+DlgGenId : "Azonosító",
+DlgGenLangDir : "Ãrás iránya",
+DlgGenLangDirLtr : "Balról jobbra",
+DlgGenLangDirRtl : "Jobbról balra",
+DlgGenLangCode : "Nyelv kódja",
+DlgGenAccessKey : "Billentyűkombináció",
+DlgGenName : "Név",
+DlgGenTabIndex : "Tabulátor index",
+DlgGenLongDescr : "Részletes leírás webcíme",
+DlgGenClass : "Stíluskészlet",
+DlgGenTitle : "Súgócimke",
+DlgGenContType : "Súgó tartalomtípusa",
+DlgGenLinkCharset : "Hivatkozott tartalom kódlapja",
+DlgGenStyle : "Stílus",
+
+// Image Dialog
+DlgImgTitle : "Kép tulajdonságai",
+DlgImgInfoTab : "Alaptulajdonságok",
+DlgImgBtnUpload : "Küldés a szerverre",
+DlgImgURL : "Hivatkozás",
+DlgImgUpload : "Feltöltés",
+DlgImgAlt : "Buborék szöveg",
+DlgImgWidth : "Szélesség",
+DlgImgHeight : "Magasság",
+DlgImgLockRatio : "Arány megtartása",
+DlgBtnResetSize : "Eredeti méret",
+DlgImgBorder : "Keret",
+DlgImgHSpace : "Vízsz. táv",
+DlgImgVSpace : "Függ. táv",
+DlgImgAlign : "Igazítás",
+DlgImgAlignLeft : "Bal",
+DlgImgAlignAbsBottom: "Legaljára",
+DlgImgAlignAbsMiddle: "Közepére",
+DlgImgAlignBaseline : "Alapvonalhoz",
+DlgImgAlignBottom : "Aljára",
+DlgImgAlignMiddle : "Középre",
+DlgImgAlignRight : "Jobbra",
+DlgImgAlignTextTop : "Szöveg tetejére",
+DlgImgAlignTop : "Tetejére",
+DlgImgPreview : "Előnézet",
+DlgImgAlertUrl : "Töltse ki a kép webcímét",
+DlgImgLinkTab : "Hivatkozás",
+
+// Flash Dialog
+DlgFlashTitle : "Flash tulajdonságai",
+DlgFlashChkPlay : "Automata lejátszás",
+DlgFlashChkLoop : "Folyamatosan",
+DlgFlashChkMenu : "Flash menü engedélyezése",
+DlgFlashScale : "Méretezés",
+DlgFlashScaleAll : "Mindent mutat",
+DlgFlashScaleNoBorder : "Keret nélkül",
+DlgFlashScaleFit : "Teljes kitöltés",
+
+// Link Dialog
+DlgLnkWindowTitle : "Hivatkozás tulajdonságai",
+DlgLnkInfoTab : "Alaptulajdonságok",
+DlgLnkTargetTab : "Megjelenítés",
+
+DlgLnkType : "Hivatkozás típusa",
+DlgLnkTypeURL : "Webcím",
+DlgLnkTypeAnchor : "Horgony az oldalon",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokoll",
+DlgLnkProtoOther : "<más>",
+DlgLnkURL : "Webcím",
+DlgLnkAnchorSel : "Horgony választása",
+DlgLnkAnchorByName : "Horgony név szerint",
+DlgLnkAnchorById : "Azonosító szerint",
+DlgLnkNoAnchors : "<Nincs horgony a dokumentumban>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail cím",
+DlgLnkEMailSubject : "Üzenet tárgya",
+DlgLnkEMailBody : "Ãœzenet",
+DlgLnkUpload : "Feltöltés",
+DlgLnkBtnUpload : "Küldés a szerverre",
+
+DlgLnkTarget : "Tartalom megjelenítése",
+DlgLnkTargetFrame : "<keretben>",
+DlgLnkTargetPopup : "<felugró ablakban>",
+DlgLnkTargetBlank : "Új ablakban (_blank)",
+DlgLnkTargetParent : "Szülő ablakban (_parent)",
+DlgLnkTargetSelf : "Azonos ablakban (_self)",
+DlgLnkTargetTop : "Legfelső ablakban (_top)",
+DlgLnkTargetFrameName : "Keret neve",
+DlgLnkPopWinName : "Felugró ablak neve",
+DlgLnkPopWinFeat : "Felugró ablak jellemzői",
+DlgLnkPopResize : "Méretezhető",
+DlgLnkPopLocation : "Címsor",
+DlgLnkPopMenu : "Menü sor",
+DlgLnkPopScroll : "Gördítősáv",
+DlgLnkPopStatus : "Ãllapotsor",
+DlgLnkPopToolbar : "Eszköztár",
+DlgLnkPopFullScrn : "Teljes képernyő (csak IE)",
+DlgLnkPopDependent : "Szülőhöz kapcsolt (csak Netscape)",
+DlgLnkPopWidth : "Szélesség",
+DlgLnkPopHeight : "Magasság",
+DlgLnkPopLeft : "Bal pozíció",
+DlgLnkPopTop : "Felső pozíció",
+
+DlnLnkMsgNoUrl : "Adja meg a hivatkozás webcímét",
+DlnLnkMsgNoEMail : "Adja meg az E-Mail címet",
+DlnLnkMsgNoAnchor : "Válasszon egy horgonyt",
+DlnLnkMsgInvPopName : "A felbukkanó ablak neve alfanumerikus karakterrel kezdôdjön, valamint ne tartalmazzon szóközt",
+
+// Color Dialog
+DlgColorTitle : "Színválasztás",
+DlgColorBtnClear : "Törlés",
+DlgColorHighlight : "Előnézet",
+DlgColorSelected : "Kiválasztott",
+
+// Smiley Dialog
+DlgSmileyTitle : "Hangulatjel beszúrása",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Speciális karakter választása",
+
+// Table Dialog
+DlgTableTitle : "Táblázat tulajdonságai",
+DlgTableRows : "Sorok",
+DlgTableColumns : "Oszlopok",
+DlgTableBorder : "Szegélyméret",
+DlgTableAlign : "Igazítás",
+DlgTableAlignNotSet : "<Nincs beállítva>",
+DlgTableAlignLeft : "Balra",
+DlgTableAlignCenter : "Középre",
+DlgTableAlignRight : "Jobbra",
+DlgTableWidth : "Szélesség",
+DlgTableWidthPx : "képpont",
+DlgTableWidthPc : "százalék",
+DlgTableHeight : "Magasság",
+DlgTableCellSpace : "Cella térköz",
+DlgTableCellPad : "Cella belső margó",
+DlgTableCaption : "Felirat",
+DlgTableSummary : "Leírás",
+
+// Table Cell Dialog
+DlgCellTitle : "Cella tulajdonságai",
+DlgCellWidth : "Szélesség",
+DlgCellWidthPx : "képpont",
+DlgCellWidthPc : "százalék",
+DlgCellHeight : "Magasság",
+DlgCellWordWrap : "Sortörés",
+DlgCellWordWrapNotSet : "<Nincs beállítva>",
+DlgCellWordWrapYes : "Igen",
+DlgCellWordWrapNo : "Nem",
+DlgCellHorAlign : "Vízsz. igazítás",
+DlgCellHorAlignNotSet : "<Nincs beállítva>",
+DlgCellHorAlignLeft : "Balra",
+DlgCellHorAlignCenter : "Középre",
+DlgCellHorAlignRight: "Jobbra",
+DlgCellVerAlign : "Függ. igazítás",
+DlgCellVerAlignNotSet : "<Nincs beállítva>",
+DlgCellVerAlignTop : "Tetejére",
+DlgCellVerAlignMiddle : "Középre",
+DlgCellVerAlignBottom : "Aljára",
+DlgCellVerAlignBaseline : "Egyvonalba",
+DlgCellRowSpan : "Sorok egyesítése",
+DlgCellCollSpan : "Oszlopok egyesítése",
+DlgCellBackColor : "Háttérszín",
+DlgCellBorderColor : "Szegélyszín",
+DlgCellBtnSelect : "Kiválasztás...",
+
+// Find Dialog
+DlgFindTitle : "Keresés",
+DlgFindFindBtn : "Keresés",
+DlgFindNotFoundMsg : "A keresett szöveg nem található.",
+
+// Replace Dialog
+DlgReplaceTitle : "Csere",
+DlgReplaceFindLbl : "Keresett szöveg:",
+DlgReplaceReplaceLbl : "Csere erre:",
+DlgReplaceCaseChk : "kis- és nagybetű megkülönböztetése",
+DlgReplaceReplaceBtn : "Csere",
+DlgReplaceReplAllBtn : "Az összes cseréje",
+DlgReplaceWordChk : "csak ha ez a teljes szó",
+
+// Paste Operations / Dialog
+PasteErrorCut : "A böngésző biztonsági beállításai nem engedélyezik a szerkesztőnek, hogy végrehajtsa a kivágás műveletet. Használja az alábbi billentyűkombinációt (Ctrl+X).",
+PasteErrorCopy : "A böngésző biztonsági beállításai nem engedélyezik a szerkesztőnek, hogy végrehajtsa a másolás műveletet. Használja az alábbi billentyűkombinációt (Ctrl+X).",
+
+PasteAsText : "Beillesztés formázatlan szövegként",
+PasteFromWord : "Beillesztés Word-ből",
+
+DlgPasteMsg2 : "Másolja be az alábbi mezőbe a <STRONG>Ctrl+V</STRONG> billentyűk lenyomásával, majd nyomjon <STRONG>Rendben</STRONG>-t.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Betű formázások megszüntetése",
+DlgPasteRemoveStyles : "Stílusok eltávolítása",
+DlgPasteCleanBox : "Törlés",
+
+// Color Picker
+ColorAutomatic : "Automatikus",
+ColorMoreColors : "További színek...",
+
+// Document Properties
+DocProps : "Dokumentum tulajdonságai",
+
+// Anchor Dialog
+DlgAnchorTitle : "Horgony tulajdonságai",
+DlgAnchorName : "Horgony neve",
+DlgAnchorErrorName : "Kérem adja meg a horgony nevét",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Nincs a szótárban",
+DlgSpellChangeTo : "Módosítás",
+DlgSpellBtnIgnore : "Kihagyja",
+DlgSpellBtnIgnoreAll : "Mindet kihagyja",
+DlgSpellBtnReplace : "Csere",
+DlgSpellBtnReplaceAll : "Összes cseréje",
+DlgSpellBtnUndo : "Visszavonás",
+DlgSpellNoSuggestions : "Nincs javaslat",
+DlgSpellProgress : "Helyesírás-ellenőrzés folyamatban...",
+DlgSpellNoMispell : "Helyesírás-ellenőrzés kész: Nem találtam hibát",
+DlgSpellNoChanges : "Helyesírás-ellenőrzés kész: Nincs változtatott szó",
+DlgSpellOneChange : "Helyesírás-ellenőrzés kész: Egy szó cserélve",
+DlgSpellManyChanges : "Helyesírás-ellenőrzés kész: %1 szó cserélve",
+
+IeSpellDownload : "A helyesírás-ellenőrző nincs telepítve. Szeretné letölteni most?",
+
+// Button Dialog
+DlgButtonText : "Szöveg (Érték)",
+DlgButtonType : "Típus",
+DlgButtonTypeBtn : "Gomb",
+DlgButtonTypeSbm : "Küldés",
+DlgButtonTypeRst : "Alaphelyzet",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Név",
+DlgCheckboxValue : "Érték",
+DlgCheckboxSelected : "Kiválasztott",
+
+// Form Dialog
+DlgFormName : "Név",
+DlgFormAction : "Adatfeldolgozást végző hivatkozás",
+DlgFormMethod : "Adatküldés módja",
+
+// Select Field Dialog
+DlgSelectName : "Név",
+DlgSelectValue : "Érték",
+DlgSelectSize : "Méret",
+DlgSelectLines : "sor",
+DlgSelectChkMulti : "több sor is kiválasztható",
+DlgSelectOpAvail : "Elérhető opciók",
+DlgSelectOpText : "Szöveg",
+DlgSelectOpValue : "Érték",
+DlgSelectBtnAdd : "Hozzáad",
+DlgSelectBtnModify : "Módosít",
+DlgSelectBtnUp : "Fel",
+DlgSelectBtnDown : "Le",
+DlgSelectBtnSetValue : "Legyen az alapértelmezett érték",
+DlgSelectBtnDelete : "Töröl",
+
+// Textarea Dialog
+DlgTextareaName : "Név",
+DlgTextareaCols : "Karakterek száma egy sorban",
+DlgTextareaRows : "Sorok száma",
+
+// Text Field Dialog
+DlgTextName : "Név",
+DlgTextValue : "Érték",
+DlgTextCharWidth : "Megjelenített karakterek száma",
+DlgTextMaxChars : "Maximális karakterszám",
+DlgTextType : "Típus",
+DlgTextTypeText : "Szöveg",
+DlgTextTypePass : "Jelszó",
+
+// Hidden Field Dialog
+DlgHiddenName : "Név",
+DlgHiddenValue : "Érték",
+
+// Bulleted List Dialog
+BulletedListProp : "Felsorolás tulajdonságai",
+NumberedListProp : "Számozás tulajdonságai",
+DlgLstStart : "Start",
+DlgLstType : "Formátum",
+DlgLstTypeCircle : "Kör",
+DlgLstTypeDisc : "Lemez",
+DlgLstTypeSquare : "Négyzet",
+DlgLstTypeNumbers : "Számok (1, 2, 3)",
+DlgLstTypeLCase : "Kisbetűk (a, b, c)",
+DlgLstTypeUCase : "Nagybetűk (A, B, C)",
+DlgLstTypeSRoman : "Kis római számok (i, ii, iii)",
+DlgLstTypeLRoman : "Nagy római számok (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Ãltalános",
+DlgDocBackTab : "Háttér",
+DlgDocColorsTab : "Színek és margók",
+DlgDocMetaTab : "Meta adatok",
+
+DlgDocPageTitle : "Oldalcím",
+DlgDocLangDir : "Ãrás iránya",
+DlgDocLangDirLTR : "Balról jobbra",
+DlgDocLangDirRTL : "Jobbról balra",
+DlgDocLangCode : "Nyelv kód",
+DlgDocCharSet : "Karakterkódolás",
+DlgDocCharSetCE : "Közép-Európai",
+DlgDocCharSetCT : "Kínai Tradicionális (Big5)",
+DlgDocCharSetCR : "Cyrill",
+DlgDocCharSetGR : "Görög",
+DlgDocCharSetJP : "Japán",
+DlgDocCharSetKR : "Koreai",
+DlgDocCharSetTR : "Török",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Nyugat-Európai",
+DlgDocCharSetOther : "Más karakterkódolás",
+
+DlgDocDocType : "Dokumentum típus fejléc",
+DlgDocDocTypeOther : "Más dokumentum típus fejléc",
+DlgDocIncXHTML : "XHTML deklarációk beillesztése",
+DlgDocBgColor : "Háttérszín",
+DlgDocBgImage : "Háttérkép cím",
+DlgDocBgNoScroll : "Nem gördíthető háttér",
+DlgDocCText : "Szöveg",
+DlgDocCLink : "Cím",
+DlgDocCVisited : "Látogatott cím",
+DlgDocCActive : "Aktív cím",
+DlgDocMargins : "Oldal margók",
+DlgDocMaTop : "Felső",
+DlgDocMaLeft : "Bal",
+DlgDocMaRight : "Jobb",
+DlgDocMaBottom : "Alsó",
+DlgDocMeIndex : "Dokumentum keresőszavak (vesszővel elválasztva)",
+DlgDocMeDescr : "Dokumentum leírás",
+DlgDocMeAuthor : "Szerző",
+DlgDocMeCopy : "Szerzői jog",
+DlgDocPreview : "Előnézet",
+
+// Templates Dialog
+Templates : "Sablonok",
+DlgTemplatesTitle : "Elérhető sablonok",
+DlgTemplatesSelMsg : "Válassza ki melyik sablon nyíljon meg a szerkesztőben<br>(a jelenlegi tartalom elveszik):",
+DlgTemplatesLoading : "Sablon lista betöltése. Kis türelmet...",
+DlgTemplatesNoTpl : "(Nincs sablon megadva)",
+DlgTemplatesReplace : "Kicseréli a jelenlegi tartalmat",
+
+// About Dialog
+DlgAboutAboutTab : "Névjegy",
+DlgAboutBrowserInfoTab : "Böngésző információ",
+DlgAboutLicenseTab : "Licensz",
+DlgAboutVersion : "verzió",
+DlgAboutInfo : "További információkért látogasson el ide:"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/it.js b/httemplate/elements/fckeditor/editor/lang/it.js
new file mode 100644
index 0000000..a3dee1b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/it.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Italian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Nascondi la barra degli strumenti",
+ToolbarExpand : "Mostra la barra degli strumenti",
+
+// Toolbar Items and Context Menu
+Save : "Salva",
+NewPage : "Nuova pagina vuota",
+Preview : "Anteprima",
+Cut : "Taglia",
+Copy : "Copia",
+Paste : "Incolla",
+PasteText : "Incolla come testo semplice",
+PasteWord : "Incolla da Word",
+Print : "Stampa",
+SelectAll : "Seleziona tutto",
+RemoveFormat : "Elimina formattazione",
+InsertLinkLbl : "Collegamento",
+InsertLink : "Inserisci/Modifica collegamento",
+RemoveLink : "Elimina collegamento",
+Anchor : "Inserisci/Modifica Ancora",
+InsertImageLbl : "Immagine",
+InsertImage : "Inserisci/Modifica immagine",
+InsertFlashLbl : "Oggetto Flash",
+InsertFlash : "Inserisci/Modifica Oggetto Flash",
+InsertTableLbl : "Tabella",
+InsertTable : "Inserisci/Modifica tabella",
+InsertLineLbl : "Riga orizzontale",
+InsertLine : "Inserisci riga orizzontale",
+InsertSpecialCharLbl: "Caratteri speciali",
+InsertSpecialChar : "Inserisci carattere speciale",
+InsertSmileyLbl : "Emoticon",
+InsertSmiley : "Inserisci emoticon",
+About : "Informazioni su FCKeditor",
+Bold : "Grassetto",
+Italic : "Corsivo",
+Underline : "Sottolineato",
+StrikeThrough : "Barrato",
+Subscript : "Pedice",
+Superscript : "Apice",
+LeftJustify : "Allinea a sinistra",
+CenterJustify : "Centra",
+RightJustify : "Allinea a destra",
+BlockJustify : "Giustifica",
+DecreaseIndent : "Riduci rientro",
+IncreaseIndent : "Aumenta rientro",
+Undo : "Annulla",
+Redo : "Ripristina",
+NumberedListLbl : "Elenco numerato",
+NumberedList : "Inserisci/Modifica elenco numerato",
+BulletedListLbl : "Elenco puntato",
+BulletedList : "Inserisci/Modifica elenco puntato",
+ShowTableBorders : "Mostra bordi tabelle",
+ShowDetails : "Mostra dettagli",
+Style : "Stile",
+FontFormat : "Formato",
+Font : "Font",
+FontSize : "Dimensione",
+TextColor : "Colore testo",
+BGColor : "Colore sfondo",
+Source : "Codice Sorgente",
+Find : "Trova",
+Replace : "Sostituisci",
+SpellCheck : "Correttore ortografico",
+UniversalKeyboard : "Tastiera universale",
+PageBreakLbl : "Interruzione di pagina",
+PageBreak : "Inserisci interruzione di pagina",
+
+Form : "Modulo",
+Checkbox : "Checkbox",
+RadioButton : "Radio Button",
+TextField : "Campo di testo",
+Textarea : "Area di testo",
+HiddenField : "Campo nascosto",
+Button : "Bottone",
+SelectionField : "Menu di selezione",
+ImageButton : "Bottone immagine",
+
+FitWindow : "Massimizza l'area dell'editor",
+
+// Context Menu
+EditLink : "Modifica collegamento",
+CellCM : "Cella",
+RowCM : "Riga",
+ColumnCM : "Colonna",
+InsertRow : "Inserisci riga",
+DeleteRows : "Elimina righe",
+InsertColumn : "Inserisci colonna",
+DeleteColumns : "Elimina colonne",
+InsertCell : "Inserisci cella",
+DeleteCells : "Elimina celle",
+MergeCells : "Unisce celle",
+SplitCell : "Dividi celle",
+TableDelete : "Cancella Tabella",
+CellProperties : "Proprietà cella",
+TableProperties : "Proprietà tabella",
+ImageProperties : "Proprietà immagine",
+FlashProperties : "Proprietà Oggetto Flash",
+
+AnchorProp : "Proprietà ancora",
+ButtonProp : "Proprietà bottone",
+CheckboxProp : "Proprietà checkbox",
+HiddenFieldProp : "Proprietà campo nascosto",
+RadioButtonProp : "Proprietà radio button",
+ImageButtonProp : "Proprietà bottone immagine",
+TextFieldProp : "Proprietà campo di testo",
+SelectionFieldProp : "Proprietà menu di selezione",
+TextareaProp : "Proprietà area di testo",
+FormProp : "Proprietà modulo",
+
+FontFormats : "Normale;Formattato;Indirizzo;Titolo 1;Titolo 2;Titolo 3;Titolo 4;Titolo 5;Titolo 6;Paragrafo (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Elaborazione XHTML in corso. Attendere prego...",
+Done : "Completato",
+PasteWordConfirm : "Il testo da incollare sembra provenire da Word. Desideri pulirlo prima di incollare?",
+NotCompatiblePaste : "Questa funzione è disponibile solo per Internet Explorer 5.5 o superiore. Desideri incollare il testo senza pulirlo?",
+UnknownToolbarItem : "Elemento della barra strumenti sconosciuto \"%1\"",
+UnknownCommand : "Comando sconosciuto \"%1\"",
+NotImplemented : "Comando non implementato",
+UnknownToolbarSet : "La barra di strumenti \"%1\" non esiste",
+NoActiveX : "Le impostazioni di sicurezza del tuo browser potrebbero limitare alcune funzionalità dell'editor. Devi abilitare l'opzione \"Esegui controlli e plug-in ActiveX\". Potresti avere errori e notare funzionalità mancanti.",
+BrowseServerBlocked : "Non è possibile aprire la finestra di espolorazione risorse. Verifica che tutti i blocca popup siano bloccati.",
+DialogBlocked : "Non è possibile aprire la finestra di dialogo. Verifica che tutti i blocca popup siano bloccati.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Annulla",
+DlgBtnClose : "Chiudi",
+DlgBtnBrowseServer : "Cerca sul server",
+DlgAdvancedTag : "Avanzate",
+DlgOpOther : "<Altro>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Devi inserire l'URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<non impostato>",
+DlgGenId : "Id",
+DlgGenLangDir : "Direzione scrittura",
+DlgGenLangDirLtr : "Da Sinistra a Destra (LTR)",
+DlgGenLangDirRtl : "Da Destra a Sinistra (RTL)",
+DlgGenLangCode : "Codice Lingua",
+DlgGenAccessKey : "Scorciatoia<br />da tastiera",
+DlgGenName : "Nome",
+DlgGenTabIndex : "Ordine di tabulazione",
+DlgGenLongDescr : "URL descrizione estesa",
+DlgGenClass : "Nome classe CSS",
+DlgGenTitle : "Titolo",
+DlgGenContType : "Tipo della risorsa collegata",
+DlgGenLinkCharset : "Set di caretteri della risorsa collegata",
+DlgGenStyle : "Stile",
+
+// Image Dialog
+DlgImgTitle : "Proprietà immagine",
+DlgImgInfoTab : "Informazioni immagine",
+DlgImgBtnUpload : "Invia al server",
+DlgImgURL : "URL",
+DlgImgUpload : "Carica",
+DlgImgAlt : "Testo alternativo",
+DlgImgWidth : "Larghezza",
+DlgImgHeight : "Altezza",
+DlgImgLockRatio : "Blocca rapporto",
+DlgBtnResetSize : "Reimposta dimensione",
+DlgImgBorder : "Bordo",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Allineamento",
+DlgImgAlignLeft : "Sinistra",
+DlgImgAlignAbsBottom: "In basso assoluto",
+DlgImgAlignAbsMiddle: "Centrato assoluto",
+DlgImgAlignBaseline : "Linea base",
+DlgImgAlignBottom : "In Basso",
+DlgImgAlignMiddle : "Centrato",
+DlgImgAlignRight : "Destra",
+DlgImgAlignTextTop : "In alto al testo",
+DlgImgAlignTop : "In Alto",
+DlgImgPreview : "Anteprima",
+DlgImgAlertUrl : "Devi inserire l'URL per l'immagine",
+DlgImgLinkTab : "Collegamento",
+
+// Flash Dialog
+DlgFlashTitle : "Proprietà Oggetto Flash",
+DlgFlashChkPlay : "Avvio Automatico",
+DlgFlashChkLoop : "Cicla",
+DlgFlashChkMenu : "Abilita Menu di Flash",
+DlgFlashScale : "Ridimensiona",
+DlgFlashScaleAll : "Mostra Tutto",
+DlgFlashScaleNoBorder : "Senza Bordo",
+DlgFlashScaleFit : "Dimensione Esatta",
+
+// Link Dialog
+DlgLnkWindowTitle : "Collegamento",
+DlgLnkInfoTab : "Informazioni collegamento",
+DlgLnkTargetTab : "Destinazione",
+
+DlgLnkType : "Tipo di Collegamento",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Ancora nella pagina",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocollo",
+DlgLnkProtoOther : "<altro>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Scegli Ancora",
+DlgLnkAnchorByName : "Per Nome",
+DlgLnkAnchorById : "Per id elemento",
+DlgLnkNoAnchors : "<Nessuna ancora disponibile nel documento>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Indirizzo E-Mail",
+DlgLnkEMailSubject : "Oggetto del messaggio",
+DlgLnkEMailBody : "Corpo del messaggio",
+DlgLnkUpload : "Carica",
+DlgLnkBtnUpload : "Invia al Server",
+
+DlgLnkTarget : "Destinazione",
+DlgLnkTargetFrame : "<riquadro>",
+DlgLnkTargetPopup : "<finestra popup>",
+DlgLnkTargetBlank : "Nuova finestra (_blank)",
+DlgLnkTargetParent : "Finestra padre (_parent)",
+DlgLnkTargetSelf : "Stessa finestra (_self)",
+DlgLnkTargetTop : "Finestra superiore (_top)",
+DlgLnkTargetFrameName : "Nome del riquadro di destinazione",
+DlgLnkPopWinName : "Nome finestra popup",
+DlgLnkPopWinFeat : "Caratteristiche finestra popup",
+DlgLnkPopResize : "Ridimensionabile",
+DlgLnkPopLocation : "Barra degli indirizzi",
+DlgLnkPopMenu : "Barra del menu",
+DlgLnkPopScroll : "Barre di scorrimento",
+DlgLnkPopStatus : "Barra di stato",
+DlgLnkPopToolbar : "Barra degli strumenti",
+DlgLnkPopFullScrn : "A tutto schermo (IE)",
+DlgLnkPopDependent : "Dipendente (Netscape)",
+DlgLnkPopWidth : "Larghezza",
+DlgLnkPopHeight : "Altezza",
+DlgLnkPopLeft : "Posizione da sinistra",
+DlgLnkPopTop : "Posizione dall'alto",
+
+DlnLnkMsgNoUrl : "Devi inserire l'URL del collegamento",
+DlnLnkMsgNoEMail : "Devi inserire un'indirizzo e-mail",
+DlnLnkMsgNoAnchor : "Devi selezionare un'ancora",
+DlnLnkMsgInvPopName : "Il nome del popup deve iniziare con una lettera, e non può contenere spazi",
+
+// Color Dialog
+DlgColorTitle : "Seleziona colore",
+DlgColorBtnClear : "Vuota",
+DlgColorHighlight : "Evidenziato",
+DlgColorSelected : "Selezionato",
+
+// Smiley Dialog
+DlgSmileyTitle : "Inserisci emoticon",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Seleziona carattere speciale",
+
+// Table Dialog
+DlgTableTitle : "Proprietà tabella",
+DlgTableRows : "Righe",
+DlgTableColumns : "Colonne",
+DlgTableBorder : "Dimensione bordo",
+DlgTableAlign : "Allineamento",
+DlgTableAlignNotSet : "<non impostato>",
+DlgTableAlignLeft : "Sinistra",
+DlgTableAlignCenter : "Centrato",
+DlgTableAlignRight : "Destra",
+DlgTableWidth : "Larghezza",
+DlgTableWidthPx : "pixel",
+DlgTableWidthPc : "percento",
+DlgTableHeight : "Altezza",
+DlgTableCellSpace : "Spaziatura celle",
+DlgTableCellPad : "Padding celle",
+DlgTableCaption : "Intestazione",
+DlgTableSummary : "Indice",
+
+// Table Cell Dialog
+DlgCellTitle : "Proprietà cella",
+DlgCellWidth : "Larghezza",
+DlgCellWidthPx : "pixel",
+DlgCellWidthPc : "percento",
+DlgCellHeight : "Altezza",
+DlgCellWordWrap : "A capo automatico",
+DlgCellWordWrapNotSet : "<non impostato>",
+DlgCellWordWrapYes : "Si",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "Allineamento orizzontale",
+DlgCellHorAlignNotSet : "<non impostato>",
+DlgCellHorAlignLeft : "Sinistra",
+DlgCellHorAlignCenter : "Centrato",
+DlgCellHorAlignRight: "Destra",
+DlgCellVerAlign : "Allineamento verticale",
+DlgCellVerAlignNotSet : "<non impostato>",
+DlgCellVerAlignTop : "In Alto",
+DlgCellVerAlignMiddle : "Centrato",
+DlgCellVerAlignBottom : "In Basso",
+DlgCellVerAlignBaseline : "Linea base",
+DlgCellRowSpan : "Righe occupate",
+DlgCellCollSpan : "Colonne occupate",
+DlgCellBackColor : "Colore sfondo",
+DlgCellBorderColor : "Colore bordo",
+DlgCellBtnSelect : "Scegli...",
+
+// Find Dialog
+DlgFindTitle : "Trova",
+DlgFindFindBtn : "Trova",
+DlgFindNotFoundMsg : "L'elemento cercato non è stato trovato.",
+
+// Replace Dialog
+DlgReplaceTitle : "Sostituisci",
+DlgReplaceFindLbl : "Trova:",
+DlgReplaceReplaceLbl : "Sostituisci con:",
+DlgReplaceCaseChk : "Maiuscole/minuscole",
+DlgReplaceReplaceBtn : "Sostituisci",
+DlgReplaceReplAllBtn : "Sostituisci tutto",
+DlgReplaceWordChk : "Solo parole intere",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Le impostazioni di sicurezza del browser non permettono di tagliare automaticamente il testo. Usa la tastiera (Ctrl+X).",
+PasteErrorCopy : "Le impostazioni di sicurezza del browser non permettono di copiare automaticamente il testo. Usa la tastiera (Ctrl+C).",
+
+PasteAsText : "Incolla come testo semplice",
+PasteFromWord : "Incolla da Word",
+
+DlgPasteMsg2 : "Incolla il testo all'interno dell'area sottostante usando la scorciatoia di tastiere (<STRONG>Ctrl+V</STRONG>) e premi <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignora le definizioni di Font",
+DlgPasteRemoveStyles : "Rimuovi le definizioni di Stile",
+DlgPasteCleanBox : "Svuota area di testo",
+
+// Color Picker
+ColorAutomatic : "Automatico",
+ColorMoreColors : "Altri colori...",
+
+// Document Properties
+DocProps : "Proprietà del Documento",
+
+// Anchor Dialog
+DlgAnchorTitle : "Proprietà ancora",
+DlgAnchorName : "Nome ancora",
+DlgAnchorErrorName : "Inserici il nome dell'ancora",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Non nel dizionario",
+DlgSpellChangeTo : "Cambia in",
+DlgSpellBtnIgnore : "Ignora",
+DlgSpellBtnIgnoreAll : "Ignora tutto",
+DlgSpellBtnReplace : "Cambia",
+DlgSpellBtnReplaceAll : "Cambia tutto",
+DlgSpellBtnUndo : "Annulla",
+DlgSpellNoSuggestions : "- Nessun suggerimento -",
+DlgSpellProgress : "Controllo ortografico in corso",
+DlgSpellNoMispell : "Controllo ortografico completato: nessun errore trovato",
+DlgSpellNoChanges : "Controllo ortografico completato: nessuna parola cambiata",
+DlgSpellOneChange : "Controllo ortografico completato: 1 parola cambiata",
+DlgSpellManyChanges : "Controllo ortografico completato: %1 parole cambiate",
+
+IeSpellDownload : "Contollo ortografico non installato. Lo vuoi scaricare ora?",
+
+// Button Dialog
+DlgButtonText : "Testo (Value)",
+DlgButtonType : "Tipo",
+DlgButtonTypeBtn : "Bottone",
+DlgButtonTypeSbm : "Invio",
+DlgButtonTypeRst : "Annulla",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nome",
+DlgCheckboxValue : "Valore",
+DlgCheckboxSelected : "Selezionato",
+
+// Form Dialog
+DlgFormName : "Nome",
+DlgFormAction : "Azione",
+DlgFormMethod : "Metodo",
+
+// Select Field Dialog
+DlgSelectName : "Nome",
+DlgSelectValue : "Valore",
+DlgSelectSize : "Dimensione",
+DlgSelectLines : "righe",
+DlgSelectChkMulti : "Permetti selezione multipla",
+DlgSelectOpAvail : "Opzioni disponibili",
+DlgSelectOpText : "Testo",
+DlgSelectOpValue : "Valore",
+DlgSelectBtnAdd : "Aggiungi",
+DlgSelectBtnModify : "Modifica",
+DlgSelectBtnUp : "Su",
+DlgSelectBtnDown : "Gi",
+DlgSelectBtnSetValue : "Imposta come predefinito",
+DlgSelectBtnDelete : "Rimuovi",
+
+// Textarea Dialog
+DlgTextareaName : "Nome",
+DlgTextareaCols : "Colonne",
+DlgTextareaRows : "Righe",
+
+// Text Field Dialog
+DlgTextName : "Nome",
+DlgTextValue : "Valore",
+DlgTextCharWidth : "Larghezza",
+DlgTextMaxChars : "Numero massimo di caratteri",
+DlgTextType : "Tipo",
+DlgTextTypeText : "Testo",
+DlgTextTypePass : "Password",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nome",
+DlgHiddenValue : "Valore",
+
+// Bulleted List Dialog
+BulletedListProp : "Proprietà lista puntata",
+NumberedListProp : "Proprietà lista numerata",
+DlgLstStart : "Inizio",
+DlgLstType : "Tipo",
+DlgLstTypeCircle : "Tondo",
+DlgLstTypeDisc : "Disco",
+DlgLstTypeSquare : "Quadrato",
+DlgLstTypeNumbers : "Numeri (1, 2, 3)",
+DlgLstTypeLCase : "Caratteri minuscoli (a, b, c)",
+DlgLstTypeUCase : "Caratteri maiuscoli (A, B, C)",
+DlgLstTypeSRoman : "Numeri Romani minuscoli (i, ii, iii)",
+DlgLstTypeLRoman : "Numeri Romani maiuscoli (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Genarale",
+DlgDocBackTab : "Sfondo",
+DlgDocColorsTab : "Colori e margini",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Titolo pagina",
+DlgDocLangDir : "Direzione scrittura",
+DlgDocLangDirLTR : "Da Sinistra a Destra (LTR)",
+DlgDocLangDirRTL : "Da Destra a Sinistra (RTL)",
+DlgDocLangCode : "Codice Lingua",
+DlgDocCharSet : "Set di caretteri",
+DlgDocCharSetCE : "Europa Centrale",
+DlgDocCharSetCT : "Cinese Tradizionale (Big5)",
+DlgDocCharSetCR : "Cirillico",
+DlgDocCharSetGR : "Greco",
+DlgDocCharSetJP : "Giapponese",
+DlgDocCharSetKR : "Coreano",
+DlgDocCharSetTR : "Turco",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Europa Occidentale",
+DlgDocCharSetOther : "Altro set di caretteri",
+
+DlgDocDocType : "Intestazione DocType",
+DlgDocDocTypeOther : "Altra intestazione DocType",
+DlgDocIncXHTML : "Includi dichiarazione XHTML",
+DlgDocBgColor : "Colore di sfondo",
+DlgDocBgImage : "Immagine di sfondo",
+DlgDocBgNoScroll : "Sfondo fissato",
+DlgDocCText : "Testo",
+DlgDocCLink : "Collegamento",
+DlgDocCVisited : "Collegamento visitato",
+DlgDocCActive : "Collegamento attivo",
+DlgDocMargins : "Margini",
+DlgDocMaTop : "In Alto",
+DlgDocMaLeft : "A Sinistra",
+DlgDocMaRight : "A Destra",
+DlgDocMaBottom : "In Basso",
+DlgDocMeIndex : "Chiavi di indicizzazione documento (separate da virgola)",
+DlgDocMeDescr : "Descrizione documento",
+DlgDocMeAuthor : "Autore",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Anteprima",
+
+// Templates Dialog
+Templates : "Modelli",
+DlgTemplatesTitle : "Contenuto dei modelli",
+DlgTemplatesSelMsg : "Seleziona il modello da aprire nell'editor<br />(il contenuto attuale verrà eliminato):",
+DlgTemplatesLoading : "Caricamento modelli in corso. Attendere prego...",
+DlgTemplatesNoTpl : "(Nessun modello definito)",
+DlgTemplatesReplace : "Cancella il contenuto corrente",
+
+// About Dialog
+DlgAboutAboutTab : "Informazioni",
+DlgAboutBrowserInfoTab : "Informazioni Browser",
+DlgAboutLicenseTab : "Licenza",
+DlgAboutVersion : "versione",
+DlgAboutInfo : "Per maggiori informazioni visitare"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/ja.js b/httemplate/elements/fckeditor/editor/lang/ja.js
new file mode 100644
index 0000000..c567d21
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/ja.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Japanese language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "ツールãƒãƒ¼ã‚’éš ã™",
+ToolbarExpand : "ツールãƒãƒ¼ã‚’表示",
+
+// Toolbar Items and Context Menu
+Save : "ä¿å­˜",
+NewPage : "æ–°ã—ã„ページ",
+Preview : "プレビュー",
+Cut : "切りå–ã‚Š",
+Copy : "コピー",
+Paste : "貼り付ã‘",
+PasteText : "プレーンテキスト貼り付ã‘",
+PasteWord : "ワード文章ã‹ã‚‰è²¼ã‚Šä»˜ã‘",
+Print : "å°åˆ·",
+SelectAll : "ã™ã¹ã¦é¸æŠž",
+RemoveFormat : "フォーマット削除",
+InsertLinkLbl : "リンク",
+InsertLink : "リンク挿入/編集",
+RemoveLink : "リンク削除",
+Anchor : "アンカー挿入/編集",
+InsertImageLbl : "イメージ",
+InsertImage : "イメージ挿入/編集",
+InsertFlashLbl : "Flash",
+InsertFlash : "Flash挿入/編集",
+InsertTableLbl : "テーブル",
+InsertTable : "テーブル挿入/編集",
+InsertLineLbl : "ライン",
+InsertLine : "横罫線",
+InsertSpecialCharLbl: "特殊文字",
+InsertSpecialChar : "特殊文字挿入",
+InsertSmileyLbl : "絵文字",
+InsertSmiley : "絵文字挿入",
+About : "FCKeditorヘルプ",
+Bold : "太字",
+Italic : "斜体",
+Underline : "下線",
+StrikeThrough : "打ã¡æ¶ˆã—ç·š",
+Subscript : "æ·»ãˆå­—",
+Superscript : "上付ã文字",
+LeftJustify : "å·¦æƒãˆ",
+CenterJustify : "中央æƒãˆ",
+RightJustify : "å³æƒãˆ",
+BlockJustify : "両端æƒãˆ",
+DecreaseIndent : "インデント解除",
+IncreaseIndent : "インデント",
+Undo : "å…ƒã«æˆ»ã™",
+Redo : "ã‚„ã‚Šç›´ã—",
+NumberedListLbl : "段è½ç•ªå·",
+NumberedList : "段è½ç•ªå·ã®è¿½åŠ /削除",
+BulletedListLbl : "箇æ¡æ›¸ã",
+BulletedList : "箇æ¡æ›¸ãã®è¿½åŠ /削除",
+ShowTableBorders : "テーブルボーダー表示",
+ShowDetails : "詳細表示",
+Style : "スタイル",
+FontFormat : "フォーマット",
+Font : "フォント",
+FontSize : "サイズ",
+TextColor : "テキスト色",
+BGColor : "背景色",
+Source : "ソース",
+Find : "検索",
+Replace : "ç½®ãæ›ãˆ",
+SpellCheck : "スペルãƒã‚§ãƒƒã‚¯",
+UniversalKeyboard : "ユニãƒãƒ¼ã‚µãƒ«ãƒ»ã‚­ãƒ¼ãƒœãƒ¼ãƒ‰",
+PageBreakLbl : "改ページ",
+PageBreak : "改ページ挿入",
+
+Form : "フォーム",
+Checkbox : "ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹",
+RadioButton : "ラジオボタン",
+TextField : "1行テキスト",
+Textarea : "テキストエリア",
+HiddenField : "ä¸å¯è¦–フィールド",
+Button : "ボタン",
+SelectionField : "é¸æŠžãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰",
+ImageButton : "ç”»åƒãƒœã‚¿ãƒ³",
+
+FitWindow : "エディタサイズを最大ã«ã—ã¾ã™",
+
+// Context Menu
+EditLink : "リンク編集",
+CellCM : "セル",
+RowCM : "行",
+ColumnCM : "カラム",
+InsertRow : "行挿入",
+DeleteRows : "行削除",
+InsertColumn : "列挿入",
+DeleteColumns : "列削除",
+InsertCell : "セル挿入",
+DeleteCells : "セル削除",
+MergeCells : "セルçµåˆ",
+SplitCell : "セル分割",
+TableDelete : "テーブル削除",
+CellProperties : "セル プロパティ",
+TableProperties : "テーブル プロパティ",
+ImageProperties : "イメージ プロパティ",
+FlashProperties : "Flash プロパティ",
+
+AnchorProp : "アンカー プロパティ",
+ButtonProp : "ボタン プロパティ",
+CheckboxProp : "ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ プロパティ",
+HiddenFieldProp : "ä¸å¯è¦–フィールド プロパティ",
+RadioButtonProp : "ラジオボタン プロパティ",
+ImageButtonProp : "ç”»åƒãƒœã‚¿ãƒ³ プロパティ",
+TextFieldProp : "1行テキスト プロパティ",
+SelectionFieldProp : "é¸æŠžãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ プロパティ",
+TextareaProp : "テキストエリア プロパティ",
+FormProp : "フォーム プロパティ",
+
+FontFormats : "標準;書å¼ä»˜ã;アドレス;見出㗠1;見出㗠2;見出㗠3;見出㗠4;見出㗠5;見出㗠6;標準 (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML処ç†ä¸­. ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„...",
+Done : "完了",
+PasteWordConfirm : "貼り付ã‘ã‚’è¡Œã†ãƒ†ã‚­ã‚¹ãƒˆã¯ã€ãƒ¯ãƒ¼ãƒ‰æ–‡ç« ã‹ã‚‰ã‚³ãƒ”ーã•ã‚Œã‚ˆã†ã¨ã—ã¦ã„ã¾ã™ã€‚貼り付ã‘ã‚‹å‰ã«ã‚¯ãƒªãƒ¼ãƒ‹ãƒ³ã‚°ã‚’è¡Œã„ã¾ã™ã‹ï¼Ÿ",
+NotCompatiblePaste : "ã“ã®ã‚³ãƒžãƒ³ãƒ‰ã¯ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆãƒ»ã‚¨ã‚¯ã‚¹ãƒ—ローラーãƒãƒ¼ã‚¸ãƒ§ãƒ³5.5以上ã§åˆ©ç”¨å¯èƒ½ã§ã™ã€‚クリーニングã—ãªã„ã§è²¼ã‚Šä»˜ã‘ã‚’è¡Œã„ã¾ã™ã‹ï¼Ÿ",
+UnknownToolbarItem : "未知ã®ãƒ„ールãƒãƒ¼é …ç›® \"%1\"",
+UnknownCommand : "未知ã®ã‚³ãƒžãƒ³ãƒ‰å \"%1\"",
+NotImplemented : "コマンドã¯ã‚¤ãƒ³ãƒ—リメントã•ã‚Œã¾ã›ã‚“ã§ã—ãŸã€‚",
+UnknownToolbarSet : "ツールãƒãƒ¼è¨­å®š \"%1\" 存在ã—ã¾ã›ã‚“。",
+NoActiveX : "エラーã€è­¦å‘Šãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ãªã©ãŒç™ºç”Ÿã—ãŸå ´åˆã€ãƒ–ラウザーã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨­å®šã«ã‚ˆã‚Šã‚¨ãƒ‡ã‚£ã‚¿ã®ã„ãã¤ã‹ã®æ©Ÿèƒ½ãŒåˆ¶é™ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚セキュリティ設定ã®ã‚ªãƒ—ションã§\"ActiveXコントロールã¨ãƒ—ラグインã®å®Ÿè¡Œ\"を有効ã«ã™ã‚‹ã«ã—ã¦ãã ã•ã„。",
+BrowseServerBlocked : "サーãƒãƒ¼ãƒ–ラウザーを開ãã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ãƒãƒƒãƒ—アップ・ブロック機能ãŒç„¡åŠ¹ã«ãªã£ã¦ã„ã‚‹ã‹ç¢ºèªã—ã¦ãã ã•ã„。",
+DialogBlocked : "ダイアログウィンドウを開ãã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ãƒãƒƒãƒ—アップ・ブロック機能ãŒç„¡åŠ¹ã«ãªã£ã¦ã„ã‚‹ã‹ç¢ºèªã—ã¦ãã ã•ã„。",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "キャンセル",
+DlgBtnClose : "é–‰ã˜ã‚‹",
+DlgBtnBrowseServer : "サーãƒãƒ¼ãƒ–ラウザー",
+DlgAdvancedTag : "高度ãªè¨­å®š",
+DlgOpOther : "<ãã®ä»–>",
+DlgInfoTab : "情報",
+DlgAlertUrl : "URLを挿入ã—ã¦ãã ã•ã„",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ãªã—>",
+DlgGenId : "Id",
+DlgGenLangDir : "文字表記ã®æ–¹å‘",
+DlgGenLangDirLtr : "å·¦ã‹ã‚‰å³ (LTR)",
+DlgGenLangDirRtl : "å³ã‹ã‚‰å·¦ (RTL)",
+DlgGenLangCode : "言語コード",
+DlgGenAccessKey : "アクセスキー",
+DlgGenName : "Name属性",
+DlgGenTabIndex : "タブインデックス",
+DlgGenLongDescr : "longdesc属性(長文説明)",
+DlgGenClass : "スタイルシートクラス",
+DlgGenTitle : "Title属性",
+DlgGenContType : "Content Type属性",
+DlgGenLinkCharset : "リンクcharset属性",
+DlgGenStyle : "スタイルシート",
+
+// Image Dialog
+DlgImgTitle : "イメージ プロパティ",
+DlgImgInfoTab : "イメージ 情報",
+DlgImgBtnUpload : "サーãƒãƒ¼ã«é€ä¿¡",
+DlgImgURL : "URL",
+DlgImgUpload : "アップロード",
+DlgImgAlt : "代替テキスト",
+DlgImgWidth : "å¹…",
+DlgImgHeight : "高ã•",
+DlgImgLockRatio : "ロック比率",
+DlgBtnResetSize : "サイズリセット",
+DlgImgBorder : "ボーダー",
+DlgImgHSpace : "横間隔",
+DlgImgVSpace : "縦間隔",
+DlgImgAlign : "è¡Œæƒãˆ",
+DlgImgAlignLeft : "å·¦",
+DlgImgAlignAbsBottom: "下部(絶対的)",
+DlgImgAlignAbsMiddle: "中央(絶対的)",
+DlgImgAlignBaseline : "ベースライン",
+DlgImgAlignBottom : "下",
+DlgImgAlignMiddle : "中央",
+DlgImgAlignRight : "å³",
+DlgImgAlignTextTop : "テキスト上部",
+DlgImgAlignTop : "上",
+DlgImgPreview : "プレビュー",
+DlgImgAlertUrl : "イメージã®URLを入力ã—ã¦ãã ã•ã„。",
+DlgImgLinkTab : "リンク",
+
+// Flash Dialog
+DlgFlashTitle : "Flash プロパティ",
+DlgFlashChkPlay : "å†ç”Ÿ",
+DlgFlashChkLoop : "ループå†ç”Ÿ",
+DlgFlashChkMenu : "Flashメニューå¯èƒ½",
+DlgFlashScale : "拡大縮å°è¨­å®š",
+DlgFlashScaleAll : "ã™ã¹ã¦è¡¨ç¤º",
+DlgFlashScaleNoBorder : "外ãŒè¦‹ãˆãªã„様ã«æ‹¡å¤§",
+DlgFlashScaleFit : "上下左å³ã«ãƒ•ã‚£ãƒƒãƒˆ",
+
+// Link Dialog
+DlgLnkWindowTitle : "ãƒã‚¤ãƒ‘ーリンク",
+DlgLnkInfoTab : "ãƒã‚¤ãƒ‘ーリンク 情報",
+DlgLnkTargetTab : "ターゲット",
+
+DlgLnkType : "リンクタイプ",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "ã“ã®ãƒšãƒ¼ã‚¸ã®ã‚¢ãƒ³ã‚«ãƒ¼",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "プロトコル",
+DlgLnkProtoOther : "<ãã®ä»–>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "アンカーをé¸æŠž",
+DlgLnkAnchorByName : "アンカーå",
+DlgLnkAnchorById : "エレメントID",
+DlgLnkNoAnchors : "<ドキュメントã«ãŠã„ã¦åˆ©ç”¨å¯èƒ½ãªã‚¢ãƒ³ã‚«ãƒ¼ã¯ã‚ã‚Šã¾ã›ã‚“。>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail アドレス",
+DlgLnkEMailSubject : "件å",
+DlgLnkEMailBody : "本文",
+DlgLnkUpload : "アップロード",
+DlgLnkBtnUpload : "サーãƒãƒ¼ã«é€ä¿¡",
+
+DlgLnkTarget : "ターゲット",
+DlgLnkTargetFrame : "<フレーム>",
+DlgLnkTargetPopup : "<ãƒãƒƒãƒ—アップウィンドウ>",
+DlgLnkTargetBlank : "æ–°ã—ã„ウィンドウ (_blank)",
+DlgLnkTargetParent : "親ウィンドウ (_parent)",
+DlgLnkTargetSelf : "åŒã˜ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ (_self)",
+DlgLnkTargetTop : "最上ä½ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ (_top)",
+DlgLnkTargetFrameName : "目的ã®ãƒ•ãƒ¬ãƒ¼ãƒ å",
+DlgLnkPopWinName : "ãƒãƒƒãƒ—アップウィンドウå",
+DlgLnkPopWinFeat : "ãƒãƒƒãƒ—アップウィンドウ特徴",
+DlgLnkPopResize : "リサイズå¯èƒ½",
+DlgLnkPopLocation : "ロケーションãƒãƒ¼",
+DlgLnkPopMenu : "メニューãƒãƒ¼",
+DlgLnkPopScroll : "スクロールãƒãƒ¼",
+DlgLnkPopStatus : "ステータスãƒãƒ¼",
+DlgLnkPopToolbar : "ツールãƒãƒ¼",
+DlgLnkPopFullScrn : "全画é¢ãƒ¢ãƒ¼ãƒ‰(IE)",
+DlgLnkPopDependent : "é–‹ã„ãŸã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã«é€£å‹•ã—ã¦é–‰ã˜ã‚‹ (Netscape)",
+DlgLnkPopWidth : "å¹…",
+DlgLnkPopHeight : "高ã•",
+DlgLnkPopLeft : "左端ã‹ã‚‰ã®åº§æ¨™ã§æŒ‡å®š",
+DlgLnkPopTop : "上端ã‹ã‚‰ã®åº§æ¨™ã§æŒ‡å®š",
+
+DlnLnkMsgNoUrl : "リンクURLを入力ã—ã¦ãã ã•ã„。",
+DlnLnkMsgNoEMail : "メールアドレスを入力ã—ã¦ãã ã•ã„。",
+DlnLnkMsgNoAnchor : "アンカーをé¸æŠžã—ã¦ãã ã•ã„。",
+DlnLnkMsgInvPopName : "ãƒãƒƒãƒ—・アップåã¯è‹±å­—ã§å§‹ã¾ã‚‹æ–‡å­—ã§æŒ‡å®šã—ã¦ãã ã„。ãƒãƒƒãƒ—・アップåã«ã‚¹ãƒšãƒ¼ã‚¹ã¯å«ã‚ã¾ã›ã‚“",
+
+// Color Dialog
+DlgColorTitle : "色é¸æŠž",
+DlgColorBtnClear : "クリア",
+DlgColorHighlight : "ãƒã‚¤ãƒ©ã‚¤ãƒˆ",
+DlgColorSelected : "é¸æŠžè‰²",
+
+// Smiley Dialog
+DlgSmileyTitle : "顔文字挿入",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "特殊文字é¸æŠž",
+
+// Table Dialog
+DlgTableTitle : "テーブル プロパティ",
+DlgTableRows : "行",
+DlgTableColumns : "列",
+DlgTableBorder : "ボーダーサイズ",
+DlgTableAlign : "キャプションã®æ•´åˆ—",
+DlgTableAlignNotSet : "<ãªã—>",
+DlgTableAlignLeft : "å·¦",
+DlgTableAlignCenter : "中央",
+DlgTableAlignRight : "å³",
+DlgTableWidth : "テーブル幅",
+DlgTableWidthPx : "ピクセル",
+DlgTableWidthPc : "パーセント",
+DlgTableHeight : "テーブル高ã•",
+DlgTableCellSpace : "セル内余白",
+DlgTableCellPad : "セル内間隔",
+DlgTableCaption : "キャプショï¾",
+DlgTableSummary : "テーブル目的/構造",
+
+// Table Cell Dialog
+DlgCellTitle : "セル プロパティ",
+DlgCellWidth : "å¹…",
+DlgCellWidthPx : "ピクセル",
+DlgCellWidthPc : "パーセント",
+DlgCellHeight : "高ã•",
+DlgCellWordWrap : "折り返ã—",
+DlgCellWordWrapNotSet : "<ãªã—>",
+DlgCellWordWrapYes : "Yes",
+DlgCellWordWrapNo : "No",
+DlgCellHorAlign : "セル横ã®æ•´åˆ—",
+DlgCellHorAlignNotSet : "<ãªã—>",
+DlgCellHorAlignLeft : "å·¦",
+DlgCellHorAlignCenter : "中央",
+DlgCellHorAlignRight: "å³",
+DlgCellVerAlign : "セル縦ã®æ•´åˆ—",
+DlgCellVerAlignNotSet : "<ãªã—>",
+DlgCellVerAlignTop : "上",
+DlgCellVerAlignMiddle : "中央",
+DlgCellVerAlignBottom : "下",
+DlgCellVerAlignBaseline : "ベースライン",
+DlgCellRowSpan : "縦幅(行数)",
+DlgCellCollSpan : "横幅(列数)",
+DlgCellBackColor : "背景色",
+DlgCellBorderColor : "ボーダーカラー",
+DlgCellBtnSelect : "é¸æŠž...",
+
+// Find Dialog
+DlgFindTitle : "検索",
+DlgFindFindBtn : "検索",
+DlgFindNotFoundMsg : "指定ã•ã‚ŒãŸæ–‡å­—列ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚",
+
+// Replace Dialog
+DlgReplaceTitle : "ç½®ãæ›ãˆ",
+DlgReplaceFindLbl : "検索ã™ã‚‹æ–‡å­—列:",
+DlgReplaceReplaceLbl : "ç½®æ›ãˆã™ã‚‹æ–‡å­—列:",
+DlgReplaceCaseChk : "部分一致",
+DlgReplaceReplaceBtn : "ç½®æ›ãˆ",
+DlgReplaceReplAllBtn : "ã™ã¹ã¦ç½®æ›ãˆ",
+DlgReplaceWordChk : "å˜èªžå˜ä½ã§ä¸€è‡´",
+
+// Paste Operations / Dialog
+PasteErrorCut : "ブラウザーã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨­å®šã«ã‚ˆã‚Šã‚¨ãƒ‡ã‚£ã‚¿ã®åˆ‡ã‚Šå–ã‚Šæ“作ãŒè‡ªå‹•ã§å®Ÿè¡Œã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。実行ã™ã‚‹ã«ã¯æ‰‹å‹•ã§ã‚­ãƒ¼ãƒœãƒ¼ãƒ‰ã®(Ctrl+X)を使用ã—ã¦ãã ã•ã„。",
+PasteErrorCopy : "ブラウザーã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨­å®šã«ã‚ˆã‚Šã‚¨ãƒ‡ã‚£ã‚¿ã®ã‚³ãƒ”ーæ“作ãŒè‡ªå‹•ã§å®Ÿè¡Œã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。実行ã™ã‚‹ã«ã¯æ‰‹å‹•ã§ã‚­ãƒ¼ãƒœãƒ¼ãƒ‰ã®(Ctrl+C)を使用ã—ã¦ãã ã•ã„。",
+
+PasteAsText : "プレーンテキスト貼り付ã‘",
+PasteFromWord : "ワード文章ã‹ã‚‰è²¼ã‚Šä»˜ã‘",
+
+DlgPasteMsg2 : "キーボード(<STRONG>Ctrl+V</STRONG>)を使用ã—ã¦ã€æ¬¡ã®å…¥åŠ›ã‚¨ãƒªã‚¢å†…ã§è²¼ã£ã¦ã€<STRONG>OK</STRONG>を押ã—ã¦ãã ã•ã„。",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Fontã‚¿ã‚°ã®Face属性を無視ã—ã¾ã™ã€‚",
+DlgPasteRemoveStyles : "スタイル定義を削除ã—ã¾ã™ã€‚",
+DlgPasteCleanBox : "入力エリアクリア",
+
+// Color Picker
+ColorAutomatic : "自動",
+ColorMoreColors : "ãã®ä»–ã®è‰²...",
+
+// Document Properties
+DocProps : "文書 プロパティ",
+
+// Anchor Dialog
+DlgAnchorTitle : "アンカー プロパティ",
+DlgAnchorName : "アンカーå",
+DlgAnchorErrorName : "アンカーåã‚’å¿…ãšå…¥åŠ›ã—ã¦ãã ã•ã„。",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "辞書ã«ã‚ã‚Šã¾ã›ã‚“",
+DlgSpellChangeTo : "変更",
+DlgSpellBtnIgnore : "無視",
+DlgSpellBtnIgnoreAll : "ã™ã¹ã¦ç„¡è¦–",
+DlgSpellBtnReplace : "ç½®æ›",
+DlgSpellBtnReplaceAll : "ã™ã¹ã¦ç½®æ›",
+DlgSpellBtnUndo : "ã‚„ã‚Šç›´ã—",
+DlgSpellNoSuggestions : "- 該当ãªã— -",
+DlgSpellProgress : "スペルãƒã‚§ãƒƒã‚¯å‡¦ç†ä¸­...",
+DlgSpellNoMispell : "スペルãƒã‚§ãƒƒã‚¯å®Œäº†: スペルã®èª¤ã‚Šã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸ",
+DlgSpellNoChanges : "スペルãƒã‚§ãƒƒã‚¯å®Œäº†: 語å¥ã¯å¤‰æ›´ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ",
+DlgSpellOneChange : "スペルãƒã‚§ãƒƒã‚¯å®Œäº†: 1語å¥å¤‰æ›´ã•ã‚Œã¾ã—ãŸ",
+DlgSpellManyChanges : "スペルãƒã‚§ãƒƒã‚¯å®Œäº†: %1 語å¥å¤‰æ›´ã•ã‚Œã¾ã—ãŸ",
+
+IeSpellDownload : "スペルãƒã‚§ãƒƒã‚«ãƒ¼ãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã¾ã›ã‚“。今ã™ãダウンロードã—ã¾ã™ã‹?",
+
+// Button Dialog
+DlgButtonText : "テキスト (値)",
+DlgButtonType : "タイプ",
+DlgButtonTypeBtn : "ボタン",
+DlgButtonTypeSbm : "é€ä¿¡",
+DlgButtonTypeRst : "リセット",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "åå‰",
+DlgCheckboxValue : "値",
+DlgCheckboxSelected : "é¸æŠžæ¸ˆã¿",
+
+// Form Dialog
+DlgFormName : "フォームå",
+DlgFormAction : "アクション",
+DlgFormMethod : "メソッド",
+
+// Select Field Dialog
+DlgSelectName : "åå‰",
+DlgSelectValue : "値",
+DlgSelectSize : "サイズ",
+DlgSelectLines : "行",
+DlgSelectChkMulti : "複数項目é¸æŠžã‚’許å¯",
+DlgSelectOpAvail : "利用å¯èƒ½ãªã‚ªãƒ—ション",
+DlgSelectOpText : "é¸æŠžé …ç›®å",
+DlgSelectOpValue : "é¸æŠžé …目値",
+DlgSelectBtnAdd : "追加",
+DlgSelectBtnModify : "編集",
+DlgSelectBtnUp : "上ã¸",
+DlgSelectBtnDown : "下ã¸",
+DlgSelectBtnSetValue : "é¸æŠžã—ãŸå€¤ã‚’設定",
+DlgSelectBtnDelete : "削除",
+
+// Textarea Dialog
+DlgTextareaName : "åå‰",
+DlgTextareaCols : "列",
+DlgTextareaRows : "行",
+
+// Text Field Dialog
+DlgTextName : "åå‰",
+DlgTextValue : "値",
+DlgTextCharWidth : "サイズ",
+DlgTextMaxChars : "最大長",
+DlgTextType : "タイプ",
+DlgTextTypeText : "テキスト",
+DlgTextTypePass : "パスワード入力",
+
+// Hidden Field Dialog
+DlgHiddenName : "åå‰",
+DlgHiddenValue : "値",
+
+// Bulleted List Dialog
+BulletedListProp : "箇æ¡æ›¸ã プロパティ",
+NumberedListProp : "段è½ç•ªå· プロパティ",
+DlgLstStart : "開始文字",
+DlgLstType : "タイプ",
+DlgLstTypeCircle : "白丸",
+DlgLstTypeDisc : "黒丸",
+DlgLstTypeSquare : "四角",
+DlgLstTypeNumbers : "アラビア数字 (1, 2, 3)",
+DlgLstTypeLCase : "英字å°æ–‡å­— (a, b, c)",
+DlgLstTypeUCase : "英字大文字 (A, B, C)",
+DlgLstTypeSRoman : "ローマ数字å°æ–‡å­— (i, ii, iii)",
+DlgLstTypeLRoman : "ローマ数字大文字 (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "全般",
+DlgDocBackTab : "背景",
+DlgDocColorsTab : "色ã¨ãƒžãƒ¼ã‚¸ãƒ³",
+DlgDocMetaTab : "メタデータ",
+
+DlgDocPageTitle : "ページタイトル",
+DlgDocLangDir : "言語文字表記ã®æ–¹å‘",
+DlgDocLangDirLTR : "å·¦ã‹ã‚‰å³ã«è¡¨è¨˜(LTR)",
+DlgDocLangDirRTL : "å³ã‹ã‚‰å·¦ã«è¡¨è¨˜(RTL)",
+DlgDocLangCode : "言語コード",
+DlgDocCharSet : "文字セット符å·åŒ–",
+DlgDocCharSetCE : "Central European",
+DlgDocCharSetCT : "Chinese Traditional (Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Greek",
+DlgDocCharSetJP : "Japanese",
+DlgDocCharSetKR : "Korean",
+DlgDocCharSetTR : "Turkish",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Western European",
+DlgDocCharSetOther : "ä»–ã®æ–‡å­—セット符å·åŒ–",
+
+DlgDocDocType : "文書タイプヘッダー",
+DlgDocDocTypeOther : "ãã®ä»–文書タイプヘッダー",
+DlgDocIncXHTML : "XHTML宣言をインクルード",
+DlgDocBgColor : "背景色",
+DlgDocBgImage : "èƒŒæ™¯ç”»åƒ URL",
+DlgDocBgNoScroll : "スクロールã—ãªã„背景",
+DlgDocCText : "テキスト",
+DlgDocCLink : "リンク",
+DlgDocCVisited : "アクセス済ã¿ãƒªãƒ³ã‚¯",
+DlgDocCActive : "アクセス中リンク",
+DlgDocMargins : "ページ・マージン",
+DlgDocMaTop : "上部",
+DlgDocMaLeft : "å·¦",
+DlgDocMaRight : "å³",
+DlgDocMaBottom : "下部",
+DlgDocMeIndex : "文書ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰(カンマ区切り)",
+DlgDocMeDescr : "文書ã®æ¦‚è¦",
+DlgDocMeAuthor : "文書ã®ä½œè€…",
+DlgDocMeCopy : "文書ã®è‘—作権",
+DlgDocPreview : "プレビュー",
+
+// Templates Dialog
+Templates : "テンプレート(雛形)",
+DlgTemplatesTitle : "テンプレート内容",
+DlgTemplatesSelMsg : "エディターã§ä½¿ç”¨ã™ã‚‹ãƒ†ãƒ³ãƒ—レートをé¸æŠžã—ã¦ãã ã•ã„。<br>(ç¾åœ¨ã®ã‚¨ãƒ‡ã‚£ã‚¿ã®å†…容ã¯å¤±ã‚ã‚Œã¾ã™):",
+DlgTemplatesLoading : "テンプレート一覧読ã¿è¾¼ã¿ä¸­. ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„...",
+DlgTemplatesNoTpl : "(テンプレートãŒå®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“)",
+DlgTemplatesReplace : "ç¾åœ¨ã®ã‚¨ãƒ‡ã‚£ã‚¿ã®å†…容ã¨ç½®æ›ãˆã‚’ã—ã¾ã™",
+
+// About Dialog
+DlgAboutAboutTab : "ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±",
+DlgAboutBrowserInfoTab : "ブラウザ情報",
+DlgAboutLicenseTab : "ライセンス",
+DlgAboutVersion : "ãƒãƒ¼ã‚¸ãƒ§ãƒ³",
+DlgAboutInfo : "より詳ã—ã„情報ã¯ã“ã¡ã‚‰ã§"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/km.js b/httemplate/elements/fckeditor/editor/lang/km.js
new file mode 100644
index 0000000..e90291f
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/km.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Khmer language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "បង្រួមរបាឧបរកណáŸ",
+ToolbarExpand : "ពង្រីករបាឧបរណáŸ",
+
+// Toolbar Items and Context Menu
+Save : "រក្សាទុក",
+NewPage : "ទំពáŸážšážáŸ’មី",
+Preview : "មើលសាកល្បង",
+Cut : "កាážáŸ‹áž™áž€",
+Copy : "ចំលងយក",
+Paste : "ចំលងដាក់",
+PasteText : "ចំលងដាក់ជាអážáŸ’ážáž”ទធម្មážáž¶",
+PasteWord : "ចំលងដាក់ពី Word",
+Print : "បោះពុម្ភ",
+SelectAll : "ជ្រើសរើសទាំងអស់",
+RemoveFormat : "លប់ចោល ការរចនា",
+InsertLinkLbl : "ឈ្នាប់",
+InsertLink : "បន្ážáŸ‚ម/កែប្រែ ឈ្នាប់",
+RemoveLink : "លប់ឈ្នាប់",
+Anchor : "បន្ážáŸ‚ម/កែប្រែ យុážáŸ’កា",
+InsertImageLbl : "រូបភាព",
+InsertImage : "បន្ážáŸ‚ម/កែប្រែ រូបភាព",
+InsertFlashLbl : "Flash",
+InsertFlash : "បន្ážáŸ‚ម/កែប្រែ Flash",
+InsertTableLbl : "ážáž¶ážšáž¶áž„",
+InsertTable : "បន្ážáŸ‚ម/កែប្រែ ážáž¶ážšáž¶áž„",
+InsertLineLbl : "បន្ទាážáŸ‹",
+InsertLine : "បន្ážáŸ‚មបន្ទាážáŸ‹áž•áŸ’ážáŸáž€",
+InsertSpecialCharLbl: "អក្សរពិសáŸážŸ",
+InsertSpecialChar : "បន្ážáŸ‚មអក្សរពិសáŸážŸ",
+InsertSmileyLbl : "រូបភាព",
+InsertSmiley : "បន្ážáŸ‚ម រូបភាព",
+About : "អំពី FCKeditor",
+Bold : "អក្សរដិážáž’ំ",
+Italic : "អក្សរផ្ážáŸáž€",
+Underline : "ដិážáž”ន្ទាážáŸ‹áž–ីក្រោមអក្សរ",
+StrikeThrough : "ដិážáž”ន្ទាážáŸ‹áž–ាក់កណ្ážáž¶áž›áž¢áž€áŸ’សរ",
+Subscript : "អក្សរážáž¼áž…ក្រោម",
+Superscript : "អក្សរážáž¼áž…លើ",
+LeftJustify : "ážáŸ†ážšáž¹áž˜áž†áŸ’ážœáŸáž„",
+CenterJustify : "ážáŸ†ážšáž¹áž˜áž€ážŽáŸ’ážáž¶áž›",
+RightJustify : "ážáŸ†ážšáž¹áž˜ážŸáŸ’ážáž¶áŸ†",
+BlockJustify : "ážáŸ†ážšáž¹áž˜ážŸáž„ážáž¶áž„",
+DecreaseIndent : "បន្ážáž™áž€áž¶ážšáž…ូលបន្ទាážáŸ‹",
+IncreaseIndent : "បន្ážáŸ‚មការចូលបន្ទាážáŸ‹",
+Undo : "សារឡើងវិញ",
+Redo : "ធ្វើឡើងវិញ",
+NumberedListLbl : "បញ្ជីជាអក្សរ",
+NumberedList : "បន្ážáŸ‚ម/លប់ បញ្ជីជាអក្សរ",
+BulletedListLbl : "បញ្ជីជារង្វង់មូល",
+BulletedList : "បន្ážáŸ‚ម/លប់ បញ្ជីជារង្វង់មូល",
+ShowTableBorders : "បង្ហាញស៊ុមážáž¶ážšáž¶áž„",
+ShowDetails : "បង្ហាញពិស្ážáž¶ážš",
+Style : "ម៉ូáž",
+FontFormat : "រចនា",
+Font : "ហ្វុង",
+FontSize : "ទំហំ",
+TextColor : "ពណ៌អក្សរ",
+BGColor : "ពណ៌ផ្ទៃážáž¶áž„ក្រោយ",
+Source : "កូáž",
+Find : "ស្វែងរក",
+Replace : "ជំនួស",
+SpellCheck : "áž–áž·áž“áž·ážáŸ’យអក្ážážšáž¶ážœáž·ážšáž»áž‘្ធ",
+UniversalKeyboard : "ក្ážáž¶ážšáž–ុម្ភអក្សរសកល",
+PageBreakLbl : "ការផ្ážáž¶áž…់ទំពáŸážš",
+PageBreak : "បន្ážáŸ‚ម ការផ្ážáž¶áž…់ទំពáŸážš",
+
+Form : "បែបបទ",
+Checkbox : "ប្រអប់ជ្រើសរើស",
+RadioButton : "ប៉ូážáž»áž“រង្វង់មូល",
+TextField : "ជួរសរសáŸážšáž¢ážáŸ’ážáž”áž‘",
+Textarea : "ážáŸ†áž”ន់សរសáŸážšáž¢ážáŸ’ážáž”áž‘",
+HiddenField : "ជួរលាក់",
+Button : "ប៉ូážáž»áž“",
+SelectionField : "ជួរជ្រើសរើស",
+ImageButton : "ប៉ូážáž»áž“រូបភាព",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "កែប្រែឈ្នាប់",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "បន្ážáŸ‚មជួរផ្ážáŸáž€",
+DeleteRows : "លប់ជួរផ្ážáŸáž€",
+InsertColumn : "បន្ážáŸ‚មជួរឈរ",
+DeleteColumns : "លប់ជួរឈរ",
+InsertCell : "បន្ážáŸ‚ម សែល",
+DeleteCells : "លប់សែល",
+MergeCells : "បញ្ជូលសែល",
+SplitCell : "ផ្ážáž¶áž…់សែល",
+TableDelete : "លប់ážáž¶ážšáž¶áž„",
+CellProperties : "ការកំណážáŸ‹ážŸáŸ‚áž›",
+TableProperties : "ការកំណážáŸ‹ážáž¶ážšáž¶áž„",
+ImageProperties : "ការកំណážáŸ‹ážšáž¼áž”ភាព",
+FlashProperties : "ការកំណážáŸ‹ Flash",
+
+AnchorProp : "ការកំណážáŸ‹áž™áž»ážáŸ’កា",
+ButtonProp : "ការកំណážáŸ‹ ប៉ូážáž»áž“",
+CheckboxProp : "ការកំណážáŸ‹áž”្រអប់ជ្រើសរើស",
+HiddenFieldProp : "ការកំណážáŸ‹áž‡áž½ážšáž›áž¶áž€áŸ‹",
+RadioButtonProp : "ការកំណážáŸ‹áž”៉ូážáž»áž“រង្វង់",
+ImageButtonProp : "ការកំណážáŸ‹áž”៉ូážáž»áž“រូបភាព",
+TextFieldProp : "ការកំណážáŸ‹áž‡áž½ážšáž¢ážáŸ’ážáž”áž‘",
+SelectionFieldProp : "ការកំណážáŸ‹áž‡áž½ážšáž‡áŸ’រើសរើស",
+TextareaProp : "ការកំណážáŸ‹áž€áž“្លែងសរសáŸážšáž¢ážáŸ’ážáž”áž‘",
+FormProp : "ការកំណážáŸ‹áž”ែបបទ",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "កំពុងដំណើរការ XHTML ។ សូមរងចាំ...",
+Done : "ចប់រួចរាល់",
+PasteWordConfirm : "អážáŸ’ážáž”ទដែលលោកអ្នកបំរុងចំលងដាក់ ហាក់បីដូចជាážáŸ’រូវចំលងមកពីកម្មវិធី​Word​។ ážáž¾áž›áŸ„កអ្នកចង់សំអាážáž˜áž»áž“ចំលងអážáŸ’ážáž”ទដាក់ទáŸ?",
+NotCompatiblePaste : "ពាក្យបញ្ជានáŸáŸ‡áž”្រើបានážáŸ‚ជាមួយ Internet Explorer កំរិហ5.5 រឺ លើសនáŸáŸ‡ ។ ážáž¾áž›áŸ„កអ្នកចង់ចំលងដាក់ដោយមិនចាំបាច់សំអាážáž‘áŸ?",
+UnknownToolbarItem : "ážœážáŸ’ážáž»áž›áž¾ážšáž”ាឧបរកណ០មិនស្គាល់ \"%1\"",
+UnknownCommand : "ឈ្មោះពាក្យបញ្ជា មិនស្គាល់ \"%1\"",
+NotImplemented : "ពាក្យបញ្ជា មិនបានអនុវážáŸ’áž",
+UnknownToolbarSet : "របាឧបរកណ០\"%1\" ពុំមាន ។",
+NoActiveX : "ការកំណážáŸ‹ážŸáž»ážœážáŸ’ážáž—ាពរបស់កម្មវិធីរុករករបស់លោកអ្នក áž“áŸáŸ‡â€‹áž¢áž¶áž…ធ្វើអោយលោកអ្នកមិនអាចប្រើមុážáž„ារážáŸ’លះរបស់កម្មវិធីážáž¶áž€áŸ‹ážáŸ‚ងអážáŸ’ážáž”áž‘áž“áŸáŸ‡ ។ លោកអ្នកážáŸ’រូវកំណážáŸ‹áž¢áŸ„áž™ \"ActiveX និង​កម្មវិធីជំនួយក្នុង (plug-ins)\" អោយដំណើរការ ។ លោកអ្នកអាចជួបប្រទះនឹង បញ្ហា ព្រមជាមួយនឹងការបាážáŸ‹áž”ង់មុážáž„ារណាមួយរបស់កម្មវិធីážáž¶áž€áŸ‹ážáŸ‚ងអážáŸ’ážáž”áž‘áž“áŸáŸ‡ ។",
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "វីនដូវមិនអាចបើកបានទ០។ សូមពិនិážáŸ’យចំពោះកម្មវិធីបិទ វីនដូវលោហ(popup) ážáž¶ážáž¾ážœáž¶ážŠáŸ†ážŽáž¾ážšáž€áž¶ážšážšážºáž‘០។",
+
+// Dialogs
+DlgBtnOK : "យល់ព្រម",
+DlgBtnCancel : "មិនយល់ព្រម",
+DlgBtnClose : "បិទ",
+DlgBtnBrowseServer : "មើល",
+DlgAdvancedTag : "កំរិážážáŸ’ពស់",
+DlgOpOther : "<ផ្សáŸáž„ទៅáž>",
+DlgInfoTab : "áž–ážáŸŒáž˜áž¶áž“",
+DlgAlertUrl : "សូមសរសáŸážš URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<មិនមែន>",
+DlgGenId : "Id",
+DlgGenLangDir : "ទិសដៅភាសា",
+DlgGenLangDirLtr : "ពីឆ្វáŸáž„ទៅស្ážáž¶áŸ†(LTR)",
+DlgGenLangDirRtl : "ពីស្ážáž¶áŸ†áž‘ៅឆ្វáŸáž„(RTL)",
+DlgGenLangCode : "áž›áŸážáž€áž¼ážáž—ាសា",
+DlgGenAccessKey : "ឃី សំរាប់ចូល",
+DlgGenName : "ឈ្មោះ",
+DlgGenTabIndex : "áž›áŸáž Tab",
+DlgGenLongDescr : "អធិប្បាយ URL វែង",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "ចំណងជើង ប្រឹក្សា",
+DlgGenContType : "ប្រភáŸáž‘អážáŸ’ážáž”áž‘ ប្រឹក្សា",
+DlgGenLinkCharset : "áž›áŸážáž€áž¼ážáž¢áž€áŸ’សររបស់ឈ្នាប់",
+DlgGenStyle : "ម៉ូáž",
+
+// Image Dialog
+DlgImgTitle : "ការកំណážáŸ‹ážšáž¼áž”ភាព",
+DlgImgInfoTab : "áž–ážáŸŒáž˜áž¶áž“អំពីរូបភាព",
+DlgImgBtnUpload : "បញ្ជូនទៅកាន់ម៉ាស៊ីនផ្ážáž›áŸ‹ážŸáŸážœáž¶",
+DlgImgURL : "URL",
+DlgImgUpload : "ទាញយក",
+DlgImgAlt : "អážáŸ’ážáž”ទជំនួស",
+DlgImgWidth : "ទទឹង",
+DlgImgHeight : "កំពស់",
+DlgImgLockRatio : "អážáŸ’រាឡុក",
+DlgBtnResetSize : "កំណážáŸ‹áž‘ំហំឡើងវិញ",
+DlgImgBorder : "ស៊ុម",
+DlgImgHSpace : "គំលាážáž‘ទឹង",
+DlgImgVSpace : "គំលាážáž”ណ្ážáŸ„áž™",
+DlgImgAlign : "កំណážáŸ‹áž‘ីážáž¶áŸ†áž„",
+DlgImgAlignLeft : "ážáž¶áž„ឆ្វង",
+DlgImgAlignAbsBottom: "Abs Bottom", //MISSING
+DlgImgAlignAbsMiddle: "Abs Middle", //MISSING
+DlgImgAlignBaseline : "បន្ទាážáŸ‹áž‡áž¶áž˜áž¼áž›ážŠáŸ’ឋាន",
+DlgImgAlignBottom : "ážáž¶áž„ក្រោម",
+DlgImgAlignMiddle : "កណ្ážáž¶áž›",
+DlgImgAlignRight : "ážáž¶áž„ស្ážáž¶áŸ†",
+DlgImgAlignTextTop : "លើអážáŸ’ážáž”áž‘",
+DlgImgAlignTop : "ážáž¶áž„លើ",
+DlgImgPreview : "មើលសាកល្បង",
+DlgImgAlertUrl : "សូមសរសáŸážšáž„ាសáŸáž™ážŠáŸ’ឋានរបស់រូបភាព",
+DlgImgLinkTab : "ឈ្នាប់",
+
+// Flash Dialog
+DlgFlashTitle : "ការកំណážáŸ‹ Flash",
+DlgFlashChkPlay : "áž›áŸáž„ដោយស្វáŸáž™áž”្រវážáŸ’áž",
+DlgFlashChkLoop : "ចំនួនដង",
+DlgFlashChkMenu : "បង្ហាញ មឺនុយរបស់ Flash",
+DlgFlashScale : "ទំហំ",
+DlgFlashScaleAll : "បង្ហាញទាំងអស់",
+DlgFlashScaleNoBorder : "មិនបង្ហាញស៊ុម",
+DlgFlashScaleFit : "ážáŸ’រូវល្មម",
+
+// Link Dialog
+DlgLnkWindowTitle : "ឈ្នាប់",
+DlgLnkInfoTab : "áž–ážáŸŒáž˜áž¶áž“អំពីឈ្នាប់",
+DlgLnkTargetTab : "គោលដៅ",
+
+DlgLnkType : "ប្រភáŸáž‘ឈ្នាប់",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "យុážáŸ’កានៅក្នុងទំពáŸážšáž“áŸáŸ‡",
+DlgLnkTypeEMail : "អ៊ីមែល",
+DlgLnkProto : "ប្រូážáž¼áž€áž¼áž›",
+DlgLnkProtoOther : "<ផ្សáŸáž„ទៀáž>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "ជ្រើសរើសយុážáŸ’កា",
+DlgLnkAnchorByName : "ážáž¶áž˜ážˆáŸ’មោះរបស់យុážáŸ’កា",
+DlgLnkAnchorById : "ážáž¶áž˜ Id",
+DlgLnkNoAnchors : "<ពុំមានយុážáŸ’កានៅក្នុងឯកសារនáŸáŸ‡áž‘áŸ>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "អ៊ីមែល",
+DlgLnkEMailSubject : "ចំណងជើងអážáŸ’ážáž”áž‘",
+DlgLnkEMailBody : "អážáŸ’ážáž”áž‘",
+DlgLnkUpload : "ទាញយក",
+DlgLnkBtnUpload : "ទាញយក",
+
+DlgLnkTarget : "គោលដៅ",
+DlgLnkTargetFrame : "<ហ្វ្រáŸáž˜>",
+DlgLnkTargetPopup : "<វីនដូវ លោáž>",
+DlgLnkTargetBlank : "វីនដូវážáŸ’មី (_blank)",
+DlgLnkTargetParent : "វីនដូវម០(_parent)",
+DlgLnkTargetSelf : "វីនដូវដដែល (_self)",
+DlgLnkTargetTop : "វីនដូវនៅលើគáŸ(_top)",
+DlgLnkTargetFrameName : "ឈ្មោះហ្រ្វáŸáž˜ážŠáŸ‚លជាគោលដៅ",
+DlgLnkPopWinName : "ឈ្មោះវីនដូវលោáž",
+DlgLnkPopWinFeat : "លក្ážážŽáŸ‡ážšáž”ស់វីនដូលលោáž",
+DlgLnkPopResize : "ទំហំអាចផ្លាស់ប្ážáž¼ážš",
+DlgLnkPopLocation : "របា ទីážáž¶áŸ†áž„",
+DlgLnkPopMenu : "របា មឺនុយ",
+DlgLnkPopScroll : "របា ទាញ",
+DlgLnkPopStatus : "របា áž–ážáŸŒáž˜áž¶áž“",
+DlgLnkPopToolbar : "របា ឩបករណáŸ",
+DlgLnkPopFullScrn : "អáŸáž€áŸ’រុងពáŸáž‰(IE)",
+DlgLnkPopDependent : "អាស្រáŸáž™áž›áž¾ (Netscape)",
+DlgLnkPopWidth : "ទទឹង",
+DlgLnkPopHeight : "កំពស់",
+DlgLnkPopLeft : "ទីážáž¶áŸ†áž„ážáž¶áž„ឆ្វáŸáž„",
+DlgLnkPopTop : "ទីážáž¶áŸ†áž„ážáž¶áž„លើ",
+
+DlnLnkMsgNoUrl : "សូមសរសáŸážš អាសáŸáž™ážŠáŸ’ឋាន URL",
+DlnLnkMsgNoEMail : "សូមសរសáŸážš អាសáŸáž™ážŠáŸ’ឋាន អ៊ីមែល",
+DlnLnkMsgNoAnchor : "សូមជ្រើសរើស យុážáŸ’កា",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "ជ្រើសរើស ពណ៌",
+DlgColorBtnClear : "លប់",
+DlgColorHighlight : "ផាážáŸ‹áž–ណ៌",
+DlgColorSelected : "បានជ្រើសរើស",
+
+// Smiley Dialog
+DlgSmileyTitle : "បញ្ជូលរូបភាព",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "ážáž¼áž¢áž€áŸ’សរពិសáŸážŸ",
+
+// Table Dialog
+DlgTableTitle : "ការកំណážáŸ‹ ážáž¶ážšáž¶áž„",
+DlgTableRows : "ជួរផ្ážáŸáž€",
+DlgTableColumns : "ជួរឈរ",
+DlgTableBorder : "ទំហំស៊ុម",
+DlgTableAlign : "ការកំណážáŸ‹áž‘ីážáž¶áŸ†áž„",
+DlgTableAlignNotSet : "<មិនកំណážáŸ‹>",
+DlgTableAlignLeft : "ážáž¶áž„ឆ្វáŸáž„",
+DlgTableAlignCenter : "កណ្ážáž¶áž›",
+DlgTableAlignRight : "ážáž¶áž„ស្ážáž¶áŸ†",
+DlgTableWidth : "ទទឹង",
+DlgTableWidthPx : "ភីកសែល",
+DlgTableWidthPc : "ភាគរយ",
+DlgTableHeight : "កំពស់",
+DlgTableCellSpace : "គំលាážážŸáŸ‚áž›",
+DlgTableCellPad : "គែមសែល",
+DlgTableCaption : "ចំណងជើង",
+DlgTableSummary : "សáŸáž…ក្ážáž¸ážŸáž„្ážáŸáž”",
+
+// Table Cell Dialog
+DlgCellTitle : "ការកំណážáŸ‹ សែល",
+DlgCellWidth : "ទទឹង",
+DlgCellWidthPx : "ភីកសែល",
+DlgCellWidthPc : "ភាគរយ",
+DlgCellHeight : "កំពស់",
+DlgCellWordWrap : "បង្ហាញអážáŸ’ážáž”ទទាំងអស់",
+DlgCellWordWrapNotSet : "<មិនកំណážáŸ‹>",
+DlgCellWordWrapYes : "បាទ(ចា)",
+DlgCellWordWrapNo : "áž‘áŸ",
+DlgCellHorAlign : "ážáŸ†ážšáž¹áž˜áž•áŸ’ážáŸáž€",
+DlgCellHorAlignNotSet : "<មិនកំណážáŸ‹>",
+DlgCellHorAlignLeft : "ážáž¶áž„ឆ្វáŸáž„",
+DlgCellHorAlignCenter : "កណ្ážáž¶áž›",
+DlgCellHorAlignRight: "Right", //MISSING
+DlgCellVerAlign : "ážáŸ†ážšáž¹áž˜ážˆážš",
+DlgCellVerAlignNotSet : "<មិនកណážáŸ‹>",
+DlgCellVerAlignTop : "ážáž¶áž„លើ",
+DlgCellVerAlignMiddle : "កណ្ážáž¶áž›",
+DlgCellVerAlignBottom : "ážáž¶áž„ក្រោម",
+DlgCellVerAlignBaseline : "បន្ទាážáŸ‹áž‡áž¶áž˜áž¼áž›ážŠáŸ’ឋាន",
+DlgCellRowSpan : "បញ្ជូលជួរផ្ážáŸáž€",
+DlgCellCollSpan : "បញ្ជូលជួរឈរ",
+DlgCellBackColor : "ពណ៌ផ្នែកážáž¶áž„ក្រោម",
+DlgCellBorderColor : "ពណ៌ស៊ុម",
+DlgCellBtnSelect : "ជ្រើសរើស...",
+
+// Find Dialog
+DlgFindTitle : "ស្វែងរក",
+DlgFindFindBtn : "ស្វែងរក",
+DlgFindNotFoundMsg : "ពាក្យនáŸáŸ‡ រកមិនឃើញទ០។",
+
+// Replace Dialog
+DlgReplaceTitle : "ជំនួស",
+DlgReplaceFindLbl : "ស្វែងរកអ្វី:",
+DlgReplaceReplaceLbl : "ជំនួសជាមួយ:",
+DlgReplaceCaseChk : "ករណ៉ážáŸ’រូវរក",
+DlgReplaceReplaceBtn : "ជំនួស",
+DlgReplaceReplAllBtn : "ជំនួសទាំងអស់",
+DlgReplaceWordChk : "ážáŸ’រូវពាក្យទាំងអស់",
+
+// Paste Operations / Dialog
+PasteErrorCut : "ការកំណážáŸ‹ážŸáž»ážœážáŸ’ážáž—ាពរបស់កម្មវិធីរុករករបស់លោកអ្នក áž“áŸáŸ‡â€‹áž˜áž·áž“អាចធ្វើកម្មវិធីážáž¶áž€áŸ‹ážáŸ‚ងអážáŸ’ážáž”áž‘ កាážáŸ‹áž¢ážáŸ’ážáž”ទយកដោយស្វáŸáž™áž”្រវážáŸ’ážáž”ានឡើយ ។ សូមប្រើប្រាស់បន្សំ ឃីដូចនáŸáŸ‡ (Ctrl+X) ។",
+PasteErrorCopy : "ការកំណážáŸ‹ážŸáž»ážœážáŸ’ážáž—ាពរបស់កម្មវិធីរុករករបស់លោកអ្នក áž“áŸáŸ‡â€‹áž˜áž·áž“អាចធ្វើកម្មវិធីážáž¶áž€áŸ‹ážáŸ‚ងអážáŸ’ážáž”áž‘ ចំលងអážáŸ’ážáž”ទយកដោយស្វáŸáž™áž”្រវážáŸ’ážáž”ានឡើយ ។ សូមប្រើប្រាស់បន្សំ ឃីដូចនáŸáŸ‡ (Ctrl+C)។",
+
+PasteAsText : "ចំលងដាក់អážáŸ’ážáž”ទធម្មážáž¶",
+PasteFromWord : "ចំលងពាក្យពីកម្មវិធី Word",
+
+DlgPasteMsg2 : "សូមចំលងអážáŸ’ážáž”ទទៅដាក់ក្នុងប្រអប់ដូចážáž¶áž„ក្រោមដោយប្រើប្រាស់ ឃី ​(<STRONG>Ctrl+V</STRONG>) ហើយចុច <STRONG>OK</STRONG> ។",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "មិនគិážáž¢áŸ†áž–ីប្រភáŸáž‘ពុម្ភអក្សរ",
+DlgPasteRemoveStyles : "លប់ម៉ូáž",
+DlgPasteCleanBox : "លប់អážáŸ’ážáž”áž‘áž…áŸáž‰áž–ីប្រអប់",
+
+// Color Picker
+ColorAutomatic : "ស្វáŸáž™áž”្រវážáŸ’áž",
+ColorMoreColors : "ពណ៌ផ្សáŸáž„ទៀáž..",
+
+// Document Properties
+DocProps : "ការកំណážáŸ‹ ឯកសារ",
+
+// Anchor Dialog
+DlgAnchorTitle : "ការកំណážáŸ‹áž…ំណងជើងយុទ្ធážáŸ’កា",
+DlgAnchorName : "ឈ្មោះយុទ្ធážáŸ’កា",
+DlgAnchorErrorName : "សូមសរសáŸážš ឈ្មោះយុទ្ធážáŸ’កា",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "គ្មានក្នុងវចនានុក្រម",
+DlgSpellChangeTo : "ផ្លាស់ប្ážáž¼ážšáž‘ៅ",
+DlgSpellBtnIgnore : "មិនផ្លាស់ប្ážáž¼ážš",
+DlgSpellBtnIgnoreAll : "មិនផ្លាស់ប្ážáž¼ážš ទាំងអស់",
+DlgSpellBtnReplace : "ជំនួស",
+DlgSpellBtnReplaceAll : "ជំនួសទាំងអស់",
+DlgSpellBtnUndo : "សារឡើងវិញ",
+DlgSpellNoSuggestions : "- គ្មានសំណើរ -",
+DlgSpellProgress : "កំពុងពិនិážáŸ’យអក្ážážšáž¶ážœáž·ážšáž»áž‘្ធ...",
+DlgSpellNoMispell : "ការពិនិážáŸ’យអក្ážážšáž¶ážœáž·ážšáž»áž‘្ធបានចប់: គ្មានកំហុស",
+DlgSpellNoChanges : "ការពិនិážáŸ’យអក្ážážšáž¶ážœáž·ážšáž»áž‘្ធបានចប់: ពុំមានផ្លាស់ប្ážáž¼ážš",
+DlgSpellOneChange : "ការពិនិážáŸ’យអក្ážážšáž¶ážœáž·ážšáž»áž‘្ធបានចប់: ពាក្យមួយážáŸ’រូចបានផ្លាស់ប្ážáž¼ážš",
+DlgSpellManyChanges : "ការពិនិážáŸ’យអក្ážážšáž¶ážœáž·ážšáž»áž‘្ធបានចប់: %1 ពាក្យបានផ្លាស់ប្ážáž¼ážš",
+
+IeSpellDownload : "ពុំមានកម្មវិធីពិនិážáŸ’យអក្ážážšáž¶ážœáž·ážšáž»áž‘្ធ ។ ážáž¾áž…ង់ទាញយកពីណា?",
+
+// Button Dialog
+DlgButtonText : "អážáŸ’ážáž”áž‘(ážáŸ†áž›áŸƒ)",
+DlgButtonType : "ប្រភáŸáž‘",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "ឈ្មោះ",
+DlgCheckboxValue : "ážáŸ†áž›áŸƒ",
+DlgCheckboxSelected : "បានជ្រើសរើស",
+
+// Form Dialog
+DlgFormName : "ឈ្មោះ",
+DlgFormAction : "សកម្មភាព",
+DlgFormMethod : "វិធី",
+
+// Select Field Dialog
+DlgSelectName : "ឈ្មោះ",
+DlgSelectValue : "ážáŸ†áž›áŸƒ",
+DlgSelectSize : "ទំហំ",
+DlgSelectLines : "បន្ទាážáŸ‹",
+DlgSelectChkMulti : "អនុញ្ញាážáž¢áŸ„យជ្រើសរើសច្រើន",
+DlgSelectOpAvail : "ការកំណážáŸ‹áž‡áŸ’រើសរើស ដែលអាចកំណážáŸ‹áž”ាន",
+DlgSelectOpText : "ពាក្យ",
+DlgSelectOpValue : "ážáŸ†áž›áŸƒ",
+DlgSelectBtnAdd : "បន្ážáŸ‚ម",
+DlgSelectBtnModify : "ផ្លាស់ប្ážáž¼ážš",
+DlgSelectBtnUp : "លើ",
+DlgSelectBtnDown : "ក្រោម",
+DlgSelectBtnSetValue : "Set as selected value", //MISSING
+DlgSelectBtnDelete : "លប់",
+
+// Textarea Dialog
+DlgTextareaName : "ឈ្មោះ",
+DlgTextareaCols : "ជូរឈរ",
+DlgTextareaRows : "ជូរផ្ážáŸáž€",
+
+// Text Field Dialog
+DlgTextName : "ឈ្មោះ",
+DlgTextValue : "ážáŸ†áž›áŸƒ",
+DlgTextCharWidth : "ទទឹង អក្សរ",
+DlgTextMaxChars : "អក្សរអážáž·áž”រិមា",
+DlgTextType : "ប្រភáŸáž‘",
+DlgTextTypeText : "ពាក្យ",
+DlgTextTypePass : "ពាក្យសំងាážáŸ‹",
+
+// Hidden Field Dialog
+DlgHiddenName : "ឈ្មោះ",
+DlgHiddenValue : "ážáŸ†áž›áŸƒ",
+
+// Bulleted List Dialog
+BulletedListProp : "កំណážáŸ‹áž”ញ្ជីរង្វង់",
+NumberedListProp : "កំណážáŸ‹áž”ញ្áŸáž‡áž¸áž›áŸáž",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "ប្រភáŸáž‘",
+DlgLstTypeCircle : "រង្វង់",
+DlgLstTypeDisc : "Disc",
+DlgLstTypeSquare : "ការáŸ",
+DlgLstTypeNumbers : "áž›áŸáž(1, 2, 3)",
+DlgLstTypeLCase : "អក្សរážáž¼áž…(a, b, c)",
+DlgLstTypeUCase : "អក្សរធំ(A, B, C)",
+DlgLstTypeSRoman : "អក្សរឡាážáž¶áŸ†áž„ážáž¼áž…(i, ii, iii)",
+DlgLstTypeLRoman : "អក្សរឡាážáž¶áŸ†áž„ធំ(I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "ទូទៅ",
+DlgDocBackTab : "ផ្នែកážáž¶áž„ក្រោយ",
+DlgDocColorsTab : "ទំពáŸážšâ€‹áž“áž·áž„ ស៊ុម",
+DlgDocMetaTab : "ទិន្ននáŸáž™áž˜áŸ",
+
+DlgDocPageTitle : "ចំណងជើងទំពáŸážš",
+DlgDocLangDir : "ទិសដៅសរសáŸážšáž—ាសា",
+DlgDocLangDirLTR : "ពីឆ្វáŸáž„ទៅស្ដាំ(LTR)",
+DlgDocLangDirRTL : "ពីស្ដាំទៅឆ្វáŸáž„(RTL)",
+DlgDocLangCode : "áž›áŸážáž€áž¼ážáž—ាសា",
+DlgDocCharSet : "កំណážáŸ‹áž›áŸážáž€áž¼ážáž—ាសា",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "កំណážáŸ‹áž›áŸážáž€áž¼ážáž—ាសាផ្សáŸáž„ទៀáž",
+
+DlgDocDocType : "ប្រភáŸáž‘ក្បាលទំពáŸážš",
+DlgDocDocTypeOther : "ប្រភáŸáž‘ក្បាលទំពáŸážšáž•áŸ’សáŸáž„ទៀáž",
+DlgDocIncXHTML : "បញ្ជូល XHTML",
+DlgDocBgColor : "ពណ៌ážáž¶áž„ក្រោម",
+DlgDocBgImage : "URL របស់រូបភាពážáž¶áž„ក្រោម",
+DlgDocBgNoScroll : "ទំពáŸážšáž€áŸ’រោមមិនប្ážáž¼ážš",
+DlgDocCText : "អážáŸ’ážáž”áž‘",
+DlgDocCLink : "ឈ្នាប់",
+DlgDocCVisited : "ឈ្នាប់មើលហើយ",
+DlgDocCActive : "ឈ្នាប់កំពុងមើល",
+DlgDocMargins : "ស៊ុមទំពáŸážš",
+DlgDocMaTop : "លើ",
+DlgDocMaLeft : "ឆ្វáŸáž„",
+DlgDocMaRight : "ស្ដាំ",
+DlgDocMaBottom : "ក្រោម",
+DlgDocMeIndex : "ពាក្យនៅក្នុងឯកសារ (ផ្ážáž¶áž…់ពីគ្នាដោយក្បៀស)",
+DlgDocMeDescr : "សáŸáž…ក្ážáž¸áž¢ážáŸ’ážáž¶áž’ិប្បាយអំពីឯកសារ",
+DlgDocMeAuthor : "អ្នកនិពន្ធ",
+DlgDocMeCopy : "រក្សាសិទ្ធិáŸ",
+DlgDocPreview : "មើលសាកល្បង",
+
+// Templates Dialog
+Templates : "ឯកសារគំរូ",
+DlgTemplatesTitle : "ឯកសារគំរូ របស់អážáŸ’ážáž“áŸáž™",
+DlgTemplatesSelMsg : "សូមជ្រើសរើសឯកសារគំរូ ដើម្បីបើកនៅក្នុងកម្មវិធីážáž¶áž€áŸ‹ážáŸ‚ងអážáŸ’ážáž”áž‘<br>(អážáŸ’ážáž”ទនឹងបាážáŸ‹áž”ង់):",
+DlgTemplatesLoading : "កំពុងអានបញ្ជីឯកសារគំរូ ។ សូមរងចាំ...",
+DlgTemplatesNoTpl : "(ពុំមានឯកសារគំរូážáŸ’រូវបានកំណážáŸ‹)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "អំពី",
+DlgAboutBrowserInfoTab : "ព៌ážáž˜áž¶áž“កម្មវិធីរុករក",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "ជំនាន់",
+DlgAboutInfo : "សំរាប់ព៌ážáž˜áž¶áž“ផ្សáŸáž„ទៀហសូមទាក់ទង"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/ko.js b/httemplate/elements/fckeditor/editor/lang/ko.js
new file mode 100644
index 0000000..0a2efa6
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/ko.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Korean language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "툴바 ê°ì¶”기",
+ToolbarExpand : "툴바 ë³´ì´ê¸°",
+
+// Toolbar Items and Context Menu
+Save : "저장하기",
+NewPage : "새 문서",
+Preview : "미리보기",
+Cut : "잘ë¼ë‚´ê¸°",
+Copy : "복사하기",
+Paste : "붙여넣기",
+PasteText : "í…스트로 붙여넣기",
+PasteWord : "MS Word 형ì‹ì—ì„œ 붙여넣기",
+Print : "ì¸ì‡„하기",
+SelectAll : "ì „ì²´ì„ íƒ",
+RemoveFormat : "í¬ë§· 지우기",
+InsertLinkLbl : "ë§í¬",
+InsertLink : "ë§í¬ 삽입/변경",
+RemoveLink : "ë§í¬ ì‚­ì œ",
+Anchor : "책갈피 삽입/변경",
+InsertImageLbl : "ì´ë¯¸ì§€",
+InsertImage : "ì´ë¯¸ì§€ 삽입/변경",
+InsertFlashLbl : "플래쉬",
+InsertFlash : "플래쉬 삽입/변경",
+InsertTableLbl : "표",
+InsertTable : "표 삽입/변경",
+InsertLineLbl : "수í‰ì„ ",
+InsertLine : "수í‰ì„  삽입",
+InsertSpecialCharLbl: "íŠ¹ìˆ˜ë¬¸ìž ì‚½ìž…",
+InsertSpecialChar : "íŠ¹ìˆ˜ë¬¸ìž ì‚½ìž…",
+InsertSmileyLbl : "ì•„ì´ì½˜",
+InsertSmiley : "ì•„ì´ì½˜ 삽입",
+About : "FCKeditorì— ëŒ€í•˜ì—¬",
+Bold : "진하게",
+Italic : "ì´í…”릭",
+Underline : "밑줄",
+StrikeThrough : "취소선",
+Subscript : "아래 첨ìž",
+Superscript : "위 첨ìž",
+LeftJustify : "왼쪽 정렬",
+CenterJustify : "ê°€ìš´ë° ì •ë ¬",
+RightJustify : "오른쪽 정렬",
+BlockJustify : "양쪽 맞춤",
+DecreaseIndent : "내어쓰기",
+IncreaseIndent : "들여쓰기",
+Undo : "취소",
+Redo : "재실행",
+NumberedListLbl : "순서있는 목ë¡",
+NumberedList : "순서있는 목ë¡",
+BulletedListLbl : "순서없는 목ë¡",
+BulletedList : "순서없는 목ë¡",
+ShowTableBorders : "í‘œ í…Œë‘리 보기",
+ShowDetails : "문서기호 보기",
+Style : "스타ì¼",
+FontFormat : "í¬ë§·",
+Font : "í°íŠ¸",
+FontSize : "ê¸€ìž í¬ê¸°",
+TextColor : "ê¸€ìž ìƒ‰ìƒ",
+BGColor : "ë°°ê²½ 색ìƒ",
+Source : "소스",
+Find : "찾기",
+Replace : "바꾸기",
+SpellCheck : "ì² ìžê²€ì‚¬",
+UniversalKeyboard : "다국어 입력기",
+PageBreakLbl : "Page Break", //MISSING
+PageBreak : "Insert Page Break", //MISSING
+
+Form : "í¼",
+Checkbox : "ì²´í¬ë°•ìŠ¤",
+RadioButton : "ë¼ë””오버튼",
+TextField : "입력필드",
+Textarea : "ìž…ë ¥ì˜ì—­",
+HiddenField : "숨김필드",
+Button : "버튼",
+SelectionField : "펼침목ë¡",
+ImageButton : "ì´ë¯¸ì§€ë²„튼",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "ë§í¬ 수정",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "가로줄 삽입",
+DeleteRows : "가로줄 삭제",
+InsertColumn : "세로줄 삽입",
+DeleteColumns : "세로줄 삭제",
+InsertCell : "셀 삽입",
+DeleteCells : "셀 삭제",
+MergeCells : "셀 합치기",
+SplitCell : "셀 나누기",
+TableDelete : "Delete Table", //MISSING
+CellProperties : "ì…€ ì†ì„±",
+TableProperties : "í‘œ ì†ì„±",
+ImageProperties : "ì´ë¯¸ì§€ ì†ì„±",
+FlashProperties : "플래쉬 ì†ì„±",
+
+AnchorProp : "책갈피 ì†ì„±",
+ButtonProp : "버튼 ì†ì„±",
+CheckboxProp : "ì²´í¬ë°•ìŠ¤ ì†ì„±",
+HiddenFieldProp : "숨김필드 ì†ì„±",
+RadioButtonProp : "ë¼ë””오버튼 ì†ì„±",
+ImageButtonProp : "ì´ë¯¸ì§€ë²„튼 ì†ì„±",
+TextFieldProp : "입력필드 ì†ì„±",
+SelectionFieldProp : "íŽ¼ì¹¨ëª©ë¡ ì†ì„±",
+TextareaProp : "ìž…ë ¥ì˜ì—­ ì†ì„±",
+FormProp : "í¼ ì†ì„±",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML 처리중. 잠시만 기다려주십시요.",
+Done : "완료",
+PasteWordConfirm : "붙여넣기 í•  í…스트는 MS Wordì—ì„œ 복사한 것입니다. 붙여넣기 ì „ì— MS Word í¬ë©§ì„ 삭제하시겠습니까?",
+NotCompatiblePaste : "ì´ ëª…ë ¹ì€ ì¸í„°ë„·ìµìŠ¤í”Œë¡œëŸ¬ 5.5 버전 ì´ìƒì—서만 ìž‘ë™í•©ë‹ˆë‹¤. í¬ë©§ì„ 삭제하지 ì•Šê³  붙여넣기 하시겠습니까?",
+UnknownToolbarItem : "알수없는 툴바입니다. : \"%1\"",
+UnknownCommand : "알수없는 기능입니다. : \"%1\"",
+NotImplemented : "ê¸°ëŠ¥ì´ ì‹¤í–‰ë˜ì§€ 않았습니다.",
+UnknownToolbarSet : "툴바 ì„¤ì •ì´ ì—†ìŠµë‹ˆë‹¤. : \"%1\"",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "예",
+DlgBtnCancel : "아니오",
+DlgBtnClose : "닫기",
+DlgBtnBrowseServer : "서버 보기",
+DlgAdvancedTag : "ìžì„¸ížˆ",
+DlgOpOther : "<기타>",
+DlgInfoTab : "ì •ë³´",
+DlgAlertUrl : "URLì„ ìž…ë ¥í•˜ì‹­ì‹œìš”",
+
+// General Dialogs Labels
+DlgGenNotSet : "<설정ë˜ì§€ ì•ŠìŒ>",
+DlgGenId : "ID",
+DlgGenLangDir : "쓰기 방향",
+DlgGenLangDirLtr : "왼쪽ì—ì„œ 오른쪽 (LTR)",
+DlgGenLangDirRtl : "오른쪽ì—ì„œ 왼쪽 (RTL)",
+DlgGenLangCode : "언어 코드",
+DlgGenAccessKey : "엑세스 키",
+DlgGenName : "Name",
+DlgGenTabIndex : "탭 순서",
+DlgGenLongDescr : "URL 설명",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "Advisory Title",
+DlgGenContType : "Advisory Content Type",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "ì´ë¯¸ì§€ 설정",
+DlgImgInfoTab : "ì´ë¯¸ì§€ ì •ë³´",
+DlgImgBtnUpload : "서버로 전송",
+DlgImgURL : "URL",
+DlgImgUpload : "업로드",
+DlgImgAlt : "ì´ë¯¸ì§€ 설명",
+DlgImgWidth : "너비",
+DlgImgHeight : "높ì´",
+DlgImgLockRatio : "비율 유지",
+DlgBtnResetSize : "ì›ëž˜ í¬ê¸°ë¡œ",
+DlgImgBorder : "í…Œë‘리",
+DlgImgHSpace : "수í‰ì—¬ë°±",
+DlgImgVSpace : "수ì§ì—¬ë°±",
+DlgImgAlign : "ì •ë ¬",
+DlgImgAlignLeft : "왼쪽",
+DlgImgAlignAbsBottom: "줄아래(Abs Bottom)",
+DlgImgAlignAbsMiddle: "줄중간(Abs Middle)",
+DlgImgAlignBaseline : "기준선",
+DlgImgAlignBottom : "아래",
+DlgImgAlignMiddle : "중간",
+DlgImgAlignRight : "오른쪽",
+DlgImgAlignTextTop : "글ìžìœ„(Text Top)",
+DlgImgAlignTop : "위",
+DlgImgPreview : "미리보기",
+DlgImgAlertUrl : "ì´ë¯¸ì§€ URLì„ ìž…ë ¥í•˜ì‹­ì‹œìš”",
+DlgImgLinkTab : "ë§í¬",
+
+// Flash Dialog
+DlgFlashTitle : "플래쉬 등ë¡ì •ë³´",
+DlgFlashChkPlay : "ìžë™ìž¬ìƒ",
+DlgFlashChkLoop : "반복",
+DlgFlashChkMenu : "플래쉬메뉴 가능",
+DlgFlashScale : "ì˜ì—­",
+DlgFlashScaleAll : "모ë‘보기",
+DlgFlashScaleNoBorder : "경계선없ìŒ",
+DlgFlashScaleFit : "ì˜ì—­ìžë™ì¡°ì ˆ",
+
+// Link Dialog
+DlgLnkWindowTitle : "ë§í¬",
+DlgLnkInfoTab : "ë§í¬ ì •ë³´",
+DlgLnkTargetTab : "타겟",
+
+DlgLnkType : "ë§í¬ 종류",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "책갈피",
+DlgLnkTypeEMail : "ì´ë©”ì¼",
+DlgLnkProto : "프로토콜",
+DlgLnkProtoOther : "<기타>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "책갈피 ì„ íƒ",
+DlgLnkAnchorByName : "책갈피 ì´ë¦„",
+DlgLnkAnchorById : "책갈피 ID",
+DlgLnkNoAnchors : "<ë¬¸ì„œì— ì±…ê°ˆí”¼ê°€ 없습니다.>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ì´ë©”ì¼ ì£¼ì†Œ",
+DlgLnkEMailSubject : "제목",
+DlgLnkEMailBody : "ë‚´ìš©",
+DlgLnkUpload : "업로드",
+DlgLnkBtnUpload : "서버로 전송",
+
+DlgLnkTarget : "타겟",
+DlgLnkTargetFrame : "<프레임>",
+DlgLnkTargetPopup : "<íŒì—…ì°½>",
+DlgLnkTargetBlank : "새 창 (_blank)",
+DlgLnkTargetParent : "부모 창 (_parent)",
+DlgLnkTargetSelf : "현재 창 (_self)",
+DlgLnkTargetTop : "최 ìƒìœ„ ì°½ (_top)",
+DlgLnkTargetFrameName : "타겟 프레임 ì´ë¦„",
+DlgLnkPopWinName : "íŒì—…ì°½ ì´ë¦„",
+DlgLnkPopWinFeat : "íŒì—…ì°½ 설정",
+DlgLnkPopResize : "í¬ê¸°ì¡°ì •",
+DlgLnkPopLocation : "주소표시줄",
+DlgLnkPopMenu : "메뉴바",
+DlgLnkPopScroll : "스í¬ë¡¤ë°”",
+DlgLnkPopStatus : "ìƒíƒœë°”",
+DlgLnkPopToolbar : "툴바",
+DlgLnkPopFullScrn : "전체화면 (IE)",
+DlgLnkPopDependent : "Dependent (Netscape)",
+DlgLnkPopWidth : "너비",
+DlgLnkPopHeight : "높ì´",
+DlgLnkPopLeft : "왼쪽 위치",
+DlgLnkPopTop : "윗쪽 위치",
+
+DlnLnkMsgNoUrl : "ë§í¬ URLì„ ìž…ë ¥í•˜ì‹­ì‹œìš”.",
+DlnLnkMsgNoEMail : "ì´ë©”ì¼ì£¼ì†Œë¥¼ 입력하십시요.",
+DlnLnkMsgNoAnchor : "ì±…ê°ˆí”¼ëª…ì„ ìž…ë ¥í•˜ì‹­ì‹œìš”.",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "ìƒ‰ìƒ ì„ íƒ",
+DlgColorBtnClear : "지우기",
+DlgColorHighlight : "현재",
+DlgColorSelected : "ì„ íƒë¨",
+
+// Smiley Dialog
+DlgSmileyTitle : "ì•„ì´ì½˜ 삽입",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "íŠ¹ìˆ˜ë¬¸ìž ì„ íƒ",
+
+// Table Dialog
+DlgTableTitle : "표 설정",
+DlgTableRows : "가로줄",
+DlgTableColumns : "세로줄",
+DlgTableBorder : "í…Œë‘리 í¬ê¸°",
+DlgTableAlign : "ì •ë ¬",
+DlgTableAlignNotSet : "<설정ë˜ì§€ ì•ŠìŒ>",
+DlgTableAlignLeft : "왼쪽",
+DlgTableAlignCenter : "가운ë°",
+DlgTableAlignRight : "오른쪽",
+DlgTableWidth : "너비",
+DlgTableWidthPx : "픽셀",
+DlgTableWidthPc : "í¼ì„¼íŠ¸",
+DlgTableHeight : "높ì´",
+DlgTableCellSpace : "셀 간격",
+DlgTableCellPad : "셀 여백",
+DlgTableCaption : "캡션",
+DlgTableSummary : "Summary", //MISSING
+
+// Table Cell Dialog
+DlgCellTitle : "셀 설정",
+DlgCellWidth : "너비",
+DlgCellWidthPx : "픽셀",
+DlgCellWidthPc : "í¼ì„¼íŠ¸",
+DlgCellHeight : "높ì´",
+DlgCellWordWrap : "워드랩",
+DlgCellWordWrapNotSet : "<설정ë˜ì§€ ì•ŠìŒ>",
+DlgCellWordWrapYes : "예",
+DlgCellWordWrapNo : "아니오",
+DlgCellHorAlign : "ìˆ˜í‰ ì •ë ¬",
+DlgCellHorAlignNotSet : "<설정ë˜ì§€ ì•ŠìŒ>",
+DlgCellHorAlignLeft : "왼쪽",
+DlgCellHorAlignCenter : "가운ë°",
+DlgCellHorAlignRight: "오른쪽",
+DlgCellVerAlign : "ìˆ˜ì§ ì •ë ¬",
+DlgCellVerAlignNotSet : "<설정ë˜ì§€ ì•ŠìŒ>",
+DlgCellVerAlignTop : "위",
+DlgCellVerAlignMiddle : "중간",
+DlgCellVerAlignBottom : "아래",
+DlgCellVerAlignBaseline : "기준선",
+DlgCellRowSpan : "세로 합치기",
+DlgCellCollSpan : "가로 합치기",
+DlgCellBackColor : "ë°°ê²½ 색ìƒ",
+DlgCellBorderColor : "í…Œë‘리 색ìƒ",
+DlgCellBtnSelect : "ì„ íƒ",
+
+// Find Dialog
+DlgFindTitle : "찾기",
+DlgFindFindBtn : "찾기",
+DlgFindNotFoundMsg : "문ìžì—´ì„ ì°¾ì„ ìˆ˜ 없습니다.",
+
+// Replace Dialog
+DlgReplaceTitle : "바꾸기",
+DlgReplaceFindLbl : "ì°¾ì„ ë¬¸ìžì—´:",
+DlgReplaceReplaceLbl : "바꿀 문ìžì—´:",
+DlgReplaceCaseChk : "ëŒ€ì†Œë¬¸ìž êµ¬ë¶„",
+DlgReplaceReplaceBtn : "바꾸기",
+DlgReplaceReplAllBtn : "ëª¨ë‘ ë°”ê¾¸ê¸°",
+DlgReplaceWordChk : "온전한 단어",
+
+// Paste Operations / Dialog
+PasteErrorCut : "브ë¼ìš°ì €ì˜ ë³´ì•ˆì„¤ì •ë•Œë¬¸ì— ìž˜ë¼ë‚´ê¸° ê¸°ëŠ¥ì„ ì‹¤í–‰í•  수 없습니다. 키보드 ëª…ë ¹ì„ ì‚¬ìš©í•˜ì‹­ì‹œìš”. (Ctrl+X).",
+PasteErrorCopy : "브ë¼ìš°ì €ì˜ ë³´ì•ˆì„¤ì •ë•Œë¬¸ì— ë³µì‚¬í•˜ê¸° ê¸°ëŠ¥ì„ ì‹¤í–‰í•  수 없습니다. 키보드 ëª…ë ¹ì„ ì‚¬ìš©í•˜ì‹­ì‹œìš”. (Ctrl+C).",
+
+PasteAsText : "í…스트로 붙여넣기",
+PasteFromWord : "MS Word 형ì‹ì—ì„œ 붙여넣기",
+
+DlgPasteMsg2 : "í‚¤ë³´ë“œì˜ (<STRONG>Ctrl+V</STRONG>) 를 ì´ìš©í•´ì„œ ìƒìžì•ˆì— 붙여넣고 <STRONG>OK</STRONG> 를 누르세요.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "í°íŠ¸ 설정 무시",
+DlgPasteRemoveStyles : "ìŠ¤íƒ€ì¼ ì •ì˜ ì œê±°",
+DlgPasteCleanBox : "글ìƒìž 제거",
+
+// Color Picker
+ColorAutomatic : "기본색ìƒ",
+ColorMoreColors : "색ìƒì„ íƒ...",
+
+// Document Properties
+DocProps : "문서 ì†ì„±",
+
+// Anchor Dialog
+DlgAnchorTitle : "책갈피 ì†ì„±",
+DlgAnchorName : "책갈피 ì´ë¦„",
+DlgAnchorErrorName : "책갈피 ì´ë¦„ì„ ìž…ë ¥í•˜ì‹­ì‹œìš”.",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "ì‚¬ì „ì— ì—†ëŠ” 단어",
+DlgSpellChangeTo : "변경할 단어",
+DlgSpellBtnIgnore : "건너뜀",
+DlgSpellBtnIgnoreAll : "ëª¨ë‘ ê±´ë„ˆëœ€",
+DlgSpellBtnReplace : "변경",
+DlgSpellBtnReplaceAll : "ëª¨ë‘ ë³€ê²½",
+DlgSpellBtnUndo : "취소",
+DlgSpellNoSuggestions : "- 추천단어 ì—†ìŒ -",
+DlgSpellProgress : "ì² ìžê²€ì‚¬ë¥¼ 진행중입니다...",
+DlgSpellNoMispell : "ì² ìžê²€ì‚¬ 완료: ìž˜ëª»ëœ ì² ìžê°€ 없습니다.",
+DlgSpellNoChanges : "ì² ìžê²€ì‚¬ 완료: ë³€ê²½ëœ ë‹¨ì–´ê°€ 없습니다.",
+DlgSpellOneChange : "ì² ìžê²€ì‚¬ 완료: 단어가 변경ë˜ì—ˆìŠµë‹ˆë‹¤.",
+DlgSpellManyChanges : "ì² ìžê²€ì‚¬ 완료: %1 단어가 변경ë˜ì—ˆìŠµë‹ˆë‹¤.",
+
+IeSpellDownload : "ì² ìž ê²€ì‚¬ê¸°ê°€ 철치ë˜ì§€ 않았습니다. 지금 다운로드하시겠습니까?",
+
+// Button Dialog
+DlgButtonText : "버튼글ìž(ê°’)",
+DlgButtonType : "버튼종류",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "ì´ë¦„",
+DlgCheckboxValue : "ê°’",
+DlgCheckboxSelected : "ì„ íƒë¨",
+
+// Form Dialog
+DlgFormName : "í¼ì´ë¦„",
+DlgFormAction : "실행경로(Action)",
+DlgFormMethod : "방법(Method)",
+
+// Select Field Dialog
+DlgSelectName : "ì´ë¦„",
+DlgSelectValue : "ê°’",
+DlgSelectSize : "세로í¬ê¸°",
+DlgSelectLines : "줄",
+DlgSelectChkMulti : "여러항목 ì„ íƒ í—ˆìš©",
+DlgSelectOpAvail : "ì„ íƒì˜µì…˜",
+DlgSelectOpText : "ì´ë¦„",
+DlgSelectOpValue : "ê°’",
+DlgSelectBtnAdd : "추가",
+DlgSelectBtnModify : "변경",
+DlgSelectBtnUp : "위로",
+DlgSelectBtnDown : "아래로",
+DlgSelectBtnSetValue : "ì„ íƒëœê²ƒìœ¼ë¡œ 설정",
+DlgSelectBtnDelete : "삭제",
+
+// Textarea Dialog
+DlgTextareaName : "ì´ë¦„",
+DlgTextareaCols : "칸수",
+DlgTextareaRows : "줄수",
+
+// Text Field Dialog
+DlgTextName : "ì´ë¦„",
+DlgTextValue : "ê°’",
+DlgTextCharWidth : "ê¸€ìž ë„ˆë¹„",
+DlgTextMaxChars : "최대 글ìžìˆ˜",
+DlgTextType : "종류",
+DlgTextTypeText : "문ìžì—´",
+DlgTextTypePass : "비밀번호",
+
+// Hidden Field Dialog
+DlgHiddenName : "ì´ë¦„",
+DlgHiddenValue : "ê°’",
+
+// Bulleted List Dialog
+BulletedListProp : "순서없는 ëª©ë¡ ì†ì„±",
+NumberedListProp : "순서있는 ëª©ë¡ ì†ì„±",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "종류",
+DlgLstTypeCircle : "ì›(Circle)",
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "네모ì (Square)",
+DlgLstTypeNumbers : "번호 (1, 2, 3)",
+DlgLstTypeLCase : "ì†Œë¬¸ìž (a, b, c)",
+DlgLstTypeUCase : "ëŒ€ë¬¸ìž (A, B, C)",
+DlgLstTypeSRoman : "ë¡œë§ˆìž ìˆ˜ë¬¸ìž (i, ii, iii)",
+DlgLstTypeLRoman : "ë¡œë§ˆìž ëŒ€ë¬¸ìž (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "ì¼ë°˜",
+DlgDocBackTab : "ë°°ê²½",
+DlgDocColorsTab : "ìƒ‰ìƒ ë° ì—¬ë°±",
+DlgDocMetaTab : "메타ë°ì´í„°",
+
+DlgDocPageTitle : "페ì´ì§€ëª…",
+DlgDocLangDir : "ë¬¸ìž ì“°ê¸°ë°©í–¥",
+DlgDocLangDirLTR : "왼쪽ì—ì„œ 오른쪽 (LTR)",
+DlgDocLangDirRTL : "오른쪽ì—ì„œ 왼쪽 (RTL)",
+DlgDocLangCode : "언어코드",
+DlgDocCharSet : "ìºë¦­í„°ì…‹ ì¸ì½”딩",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "다른 ìºë¦­í„°ì…‹ ì¸ì½”딩",
+
+DlgDocDocType : "문서 헤드",
+DlgDocDocTypeOther : "다른 문서헤드",
+DlgDocIncXHTML : "XHTML ë¬¸ì„œì •ì˜ í¬í•¨",
+DlgDocBgColor : "배경색ìƒ",
+DlgDocBgImage : "ë°°ê²½ì´ë¯¸ì§€ URL",
+DlgDocBgNoScroll : "스í¬ë¡¤ë˜ì§€ì•ŠëŠ” ë°°ê²½",
+DlgDocCText : "í…스트",
+DlgDocCLink : "ë§í¬",
+DlgDocCVisited : "방문한 ë§í¬(Visited)",
+DlgDocCActive : "í™œì„±í™”ëœ ë§í¬(Active)",
+DlgDocMargins : "페ì´ì§€ 여백",
+DlgDocMaTop : "위",
+DlgDocMaLeft : "왼쪽",
+DlgDocMaRight : "오른쪽",
+DlgDocMaBottom : "아래",
+DlgDocMeIndex : "문서 키워드 (콤마로 구분)",
+DlgDocMeDescr : "문서 설명",
+DlgDocMeAuthor : "작성ìž",
+DlgDocMeCopy : "저작권",
+DlgDocPreview : "미리보기",
+
+// Templates Dialog
+Templates : "템플릿",
+DlgTemplatesTitle : "내용 템플릿",
+DlgTemplatesSelMsg : "ì—디터ì—ì„œ 사용할 í…œí”Œë¦¿ì„ ì„ íƒí•˜ì‹­ì‹œìš”.<br>(지금까지 ìž‘ì„±ëœ ë‚´ìš©ì€ ì‚¬ë¼ì§‘니다.):",
+DlgTemplatesLoading : "템플릿 목ë¡ì„ 불러오는중입니다. 잠시만 기다려주십시요.",
+DlgTemplatesNoTpl : "(í…œí”Œë¦¿ì´ ì—†ìŠµë‹ˆë‹¤.)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "About",
+DlgAboutBrowserInfoTab : "브ë¼ìš°ì € ì •ë³´",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "버전",
+DlgAboutInfo : "For further information go to"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/lt.js b/httemplate/elements/fckeditor/editor/lang/lt.js
new file mode 100644
index 0000000..db994d0
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/lt.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Lithuanian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Sutraukti mygtukų juostą",
+ToolbarExpand : "Išplėsti mygtukų juostą",
+
+// Toolbar Items and Context Menu
+Save : "IÅ¡saugoti",
+NewPage : "Naujas puslapis",
+Preview : "Peržiūra",
+Cut : "IÅ¡kirpti",
+Copy : "Kopijuoti",
+Paste : "Įdėti",
+PasteText : "Įdėti kaip gryną tekstą",
+PasteWord : "Įdėti iš Word",
+Print : "Spausdinti",
+SelectAll : "Pažymėti viską",
+RemoveFormat : "Panaikinti formatÄ…",
+InsertLinkLbl : "Nuoroda",
+InsertLink : "Įterpti/taisyti nuorodą",
+RemoveLink : "Panaikinti nuorodÄ…",
+Anchor : "Įterpti/modifikuoti žymę",
+InsertImageLbl : "Vaizdas",
+InsertImage : "Įterpti/taisyti vaizdą",
+InsertFlashLbl : "Flash",
+InsertFlash : "Įterpti/taisyti Flash",
+InsertTableLbl : "LentelÄ—",
+InsertTable : "Įterpti/taisyti lentelę",
+InsertLineLbl : "Linija",
+InsertLine : "Įterpti horizontalią liniją",
+InsertSpecialCharLbl: "Spec. simbolis",
+InsertSpecialChar : "Įterpti specialų simbolį",
+InsertSmileyLbl : "Veideliai",
+InsertSmiley : "Įterpti veidelį",
+About : "Apie FCKeditor",
+Bold : "Pusjuodis",
+Italic : "Kursyvas",
+Underline : "Pabrauktas",
+StrikeThrough : "Perbrauktas",
+Subscript : "Apatinis indeksas",
+Superscript : "Viršutinis indeksas",
+LeftJustify : "Lygiuoti kairÄ™",
+CenterJustify : "Centruoti",
+RightJustify : "Lygiuoti dešinę",
+BlockJustify : "Lygiuoti abi puses",
+DecreaseIndent : "Sumažinti įtrauką",
+IncreaseIndent : "Padidinti įtrauką",
+Undo : "Atšaukti",
+Redo : "Atstatyti",
+NumberedListLbl : "Numeruotas sąrašas",
+NumberedList : "Įterpti/Panaikinti numeruotą sąrašą",
+BulletedListLbl : "Suženklintas sąrašas",
+BulletedList : "Įterpti/Panaikinti suženklintą sąrašą",
+ShowTableBorders : "Rodyti lentelÄ—s rÄ—mus",
+ShowDetails : "Rodyti detales",
+Style : "Stilius",
+FontFormat : "Å rifto formatas",
+Font : "Å riftas",
+FontSize : "Å rifto dydis",
+TextColor : "Teksto spalva",
+BGColor : "Fono spalva",
+Source : "Å altinis",
+Find : "Rasti",
+Replace : "Pakeisti",
+SpellCheck : "Rašybos tikrinimas",
+UniversalKeyboard : "Universali klaviatūra",
+PageBreakLbl : "Puslapių skirtukas",
+PageBreak : "Įterpti puslapių skirtuką",
+
+Form : "Forma",
+Checkbox : "Žymimasis langelis",
+RadioButton : "Žymimoji akutė",
+TextField : "Teksto laukas",
+Textarea : "Teksto sritis",
+HiddenField : "Nerodomas laukas",
+Button : "Mygtukas",
+SelectionField : "Atrankos laukas",
+ImageButton : "Vaizdinis mygtukas",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Taisyti nuorodÄ…",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Įterpti eilutę",
+DeleteRows : "Å alinti eilutes",
+InsertColumn : "Įterpti stulpelį",
+DeleteColumns : "Å alinti stulpelius",
+InsertCell : "Įterpti langelį",
+DeleteCells : "Å alinti langelius",
+MergeCells : "Sujungti langelius",
+SplitCell : "Skaidyti langelius",
+TableDelete : "Å alinti lentelÄ™",
+CellProperties : "Langelio savybÄ—s",
+TableProperties : "LentelÄ—s savybÄ—s",
+ImageProperties : "Vaizdo savybÄ—s",
+FlashProperties : "Flash savybÄ—s",
+
+AnchorProp : "Žymės savybės",
+ButtonProp : "Mygtuko savybÄ—s",
+CheckboxProp : "Žymimojo langelio savybės",
+HiddenFieldProp : "Nerodomo lauko savybÄ—s",
+RadioButtonProp : "Žymimosios akutės savybės",
+ImageButtonProp : "Vaizdinio mygtuko savybÄ—s",
+TextFieldProp : "Teksto lauko savybÄ—s",
+SelectionFieldProp : "Atrankos lauko savybÄ—s",
+TextareaProp : "Teksto srities savybÄ—s",
+FormProp : "Formos savybÄ—s",
+
+FontFormats : "Normalus;Formuotas;Kreipinio;Antraštinis 1;Antraštinis 2;Antraštinis 3;Antraštinis 4;Antraštinis 5;Antraštinis 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Apdorojamas XHTML. Prašome palaukti...",
+Done : "Baigta",
+PasteWordConfirm : "Įdedamas tekstas yra panašus į kopiją iš Word. Ar Jūs norite prieš įdėjimą išvalyti jį?",
+NotCompatiblePaste : "Ši komanda yra prieinama tik per Internet Explorer 5.5 ar aukštesnę versiją. Ar Jūs norite įterpti be valymo?",
+UnknownToolbarItem : "Nežinomas mygtukų juosta elementas \"%1\"",
+UnknownCommand : "Nežinomas komandos vardas \"%1\"",
+NotImplemented : "Komanda nėra įgyvendinta",
+UnknownToolbarSet : "Mygtukų juostos rinkinys \"%1\" neegzistuoja",
+NoActiveX : "Jūsų naršyklės saugumo nuostatos gali riboti kai kurias redaktoriaus savybes. Jūs turite aktyvuoti opciją \"Run ActiveX controls and plug-ins\". Kitu atveju Jums bus pranešama apie klaidas ir trūkstamas savybes.",
+BrowseServerBlocked : "Neįmanoma atidaryti naujo narÅ¡yklÄ—s lango. Ä®sitikinkite, kad iÅ¡kylanÄių langų blokavimo programos neveiksnios.",
+DialogBlocked : "Neįmanoma atidaryti dialogo lango. Ä®sitikinkite, kad iÅ¡kylanÄių langų blokavimo programos neveiksnios.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Nutraukti",
+DlgBtnClose : "Uždaryti",
+DlgBtnBrowseServer : "Naršyti po serverį",
+DlgAdvancedTag : "Papildomas",
+DlgOpOther : "<Kita>",
+DlgInfoTab : "Informacija",
+DlgAlertUrl : "Prašome įrašyti URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nÄ—ra nustatyta>",
+DlgGenId : "Id",
+DlgGenLangDir : "Teksto kryptis",
+DlgGenLangDirLtr : "Iš kairės į dešinę (LTR)",
+DlgGenLangDirRtl : "Iš dešinės į kairę (RTL)",
+DlgGenLangCode : "Kalbos kodas",
+DlgGenAccessKey : "Prieigos raktas",
+DlgGenName : "Vardas",
+DlgGenTabIndex : "Tabuliavimo indeksas",
+DlgGenLongDescr : "Ilgas aprašymas URL",
+DlgGenClass : "Stilių lentelės klasės",
+DlgGenTitle : "Konsultacinė antraštė",
+DlgGenContType : "Konsultacinio turinio tipas",
+DlgGenLinkCharset : "Susietų išteklių simbolių lentelė",
+DlgGenStyle : "Stilius",
+
+// Image Dialog
+DlgImgTitle : "Vaizdo savybÄ—s",
+DlgImgInfoTab : "Vaizdo informacija",
+DlgImgBtnUpload : "Siųsti į serverį",
+DlgImgURL : "URL",
+DlgImgUpload : "Nusiųsti",
+DlgImgAlt : "Alternatyvus Tekstas",
+DlgImgWidth : "Plotis",
+DlgImgHeight : "Aukštis",
+DlgImgLockRatio : "IÅ¡laikyti proporcijÄ…",
+DlgBtnResetSize : "Atstatyti dydį",
+DlgImgBorder : "RÄ—melis",
+DlgImgHSpace : "Hor.ErdvÄ—",
+DlgImgVSpace : "Vert.ErdvÄ—",
+DlgImgAlign : "Lygiuoti",
+DlgImgAlignLeft : "KairÄ™",
+DlgImgAlignAbsBottom: "AbsoliuÄiÄ… apaÄiÄ…",
+DlgImgAlignAbsMiddle: "Absoliutų vidurį",
+DlgImgAlignBaseline : "ApatinÄ™ linijÄ…",
+DlgImgAlignBottom : "ApaÄiÄ…",
+DlgImgAlignMiddle : "Vidurį",
+DlgImgAlignRight : "Dešinę",
+DlgImgAlignTextTop : "Teksto viršūnę",
+DlgImgAlignTop : "Viršūnę",
+DlgImgPreview : "Peržiūra",
+DlgImgAlertUrl : "Prašome įvesti vaizdo URL",
+DlgImgLinkTab : "Nuoroda",
+
+// Flash Dialog
+DlgFlashTitle : "Flash savybÄ—s",
+DlgFlashChkPlay : "Automatinis paleidimas",
+DlgFlashChkLoop : "Ciklas",
+DlgFlashChkMenu : "Leisti Flash meniu",
+DlgFlashScale : "Mastelis",
+DlgFlashScaleAll : "Rodyti visÄ…",
+DlgFlashScaleNoBorder : "Be rÄ—melio",
+DlgFlashScaleFit : "Tikslus atitikimas",
+
+// Link Dialog
+DlgLnkWindowTitle : "Nuoroda",
+DlgLnkInfoTab : "Nuorodos informacija",
+DlgLnkTargetTab : "Paskirtis",
+
+DlgLnkType : "Nuorodos tipas",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Žymė šiame puslapyje",
+DlgLnkTypeEMail : "El.paštas",
+DlgLnkProto : "Protokolas",
+DlgLnkProtoOther : "<kitas>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Pasirinkite žymę",
+DlgLnkAnchorByName : "Pagal žymės vardą",
+DlgLnkAnchorById : "Pagal žymės Id",
+DlgLnkNoAnchors : "<Šiame dokumente žymių nėra>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "El.pašto adresas",
+DlgLnkEMailSubject : "Žinutės tema",
+DlgLnkEMailBody : "Žinutės turinys",
+DlgLnkUpload : "Siųsti",
+DlgLnkBtnUpload : "Siųsti į serverį",
+
+DlgLnkTarget : "Paskirties vieta",
+DlgLnkTargetFrame : "<kadras>",
+DlgLnkTargetPopup : "<išskleidžiamas langas>",
+DlgLnkTargetBlank : "Naujas langas (_blank)",
+DlgLnkTargetParent : "Pirminis langas (_parent)",
+DlgLnkTargetSelf : "Tas pats langas (_self)",
+DlgLnkTargetTop : "Svarbiausias langas (_top)",
+DlgLnkTargetFrameName : "Paskirties kadro vardas",
+DlgLnkPopWinName : "Paskirties lango vardas",
+DlgLnkPopWinFeat : "Išskleidžiamo lango savybės",
+DlgLnkPopResize : "KeiÄiamas dydis",
+DlgLnkPopLocation : "Adreso juosta",
+DlgLnkPopMenu : "Meniu juosta",
+DlgLnkPopScroll : "Slinkties juostos",
+DlgLnkPopStatus : "BÅ«senos juosta",
+DlgLnkPopToolbar : "Mygtukų juosta",
+DlgLnkPopFullScrn : "Visas ekranas (IE)",
+DlgLnkPopDependent : "Priklausomas (Netscape)",
+DlgLnkPopWidth : "Plotis",
+DlgLnkPopHeight : "Aukštis",
+DlgLnkPopLeft : "KairÄ— pozicija",
+DlgLnkPopTop : "Viršutinė pozicija",
+
+DlnLnkMsgNoUrl : "Prašome įvesti nuorodos URL",
+DlnLnkMsgNoEMail : "Prašome įvesti el.pašto adresą",
+DlnLnkMsgNoAnchor : "Prašome pasirinkti žymę",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Pasirinkite spalvÄ…",
+DlgColorBtnClear : "Trinti",
+DlgColorHighlight : "Paryškinta",
+DlgColorSelected : "Pažymėta",
+
+// Smiley Dialog
+DlgSmileyTitle : "Įterpti veidelį",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Pasirinkite specialų simbolį",
+
+// Table Dialog
+DlgTableTitle : "LentelÄ—s savybÄ—s",
+DlgTableRows : "EilutÄ—s",
+DlgTableColumns : "Stulpeliai",
+DlgTableBorder : "RÄ—melio dydis",
+DlgTableAlign : "Lygiuoti",
+DlgTableAlignNotSet : "<Nenustatyta>",
+DlgTableAlignLeft : "KairÄ™",
+DlgTableAlignCenter : "CentrÄ…",
+DlgTableAlignRight : "Dešinę",
+DlgTableWidth : "Plotis",
+DlgTableWidthPx : "taškais",
+DlgTableWidthPc : "procentais",
+DlgTableHeight : "Aukštis",
+DlgTableCellSpace : "Tarpas tarp langelių",
+DlgTableCellPad : "Trapas nuo langelio rÄ—mo iki teksto",
+DlgTableCaption : "Antraštė",
+DlgTableSummary : "Santrauka",
+
+// Table Cell Dialog
+DlgCellTitle : "Langelio savybÄ—s",
+DlgCellWidth : "Plotis",
+DlgCellWidthPx : "taškais",
+DlgCellWidthPc : "procentais",
+DlgCellHeight : "Aukštis",
+DlgCellWordWrap : "Teksto laužymas",
+DlgCellWordWrapNotSet : "<Nenustatyta>",
+DlgCellWordWrapYes : "Taip",
+DlgCellWordWrapNo : "Ne",
+DlgCellHorAlign : "Horizontaliai lygiuoti",
+DlgCellHorAlignNotSet : "<Nenustatyta>",
+DlgCellHorAlignLeft : "KairÄ™",
+DlgCellHorAlignCenter : "CentrÄ…",
+DlgCellHorAlignRight: "Dešinę",
+DlgCellVerAlign : "Vertikaliai lygiuoti",
+DlgCellVerAlignNotSet : "<Nenustatyta>",
+DlgCellVerAlignTop : "Viršų",
+DlgCellVerAlignMiddle : "Vidurį",
+DlgCellVerAlignBottom : "ApaÄiÄ…",
+DlgCellVerAlignBaseline : "ApatinÄ™ linijÄ…",
+DlgCellRowSpan : "EiluÄių apjungimas",
+DlgCellCollSpan : "Stulpelių apjungimas",
+DlgCellBackColor : "Fono spalva",
+DlgCellBorderColor : "RÄ—melio spalva",
+DlgCellBtnSelect : "Pažymėti...",
+
+// Find Dialog
+DlgFindTitle : "Paieška",
+DlgFindFindBtn : "Surasti",
+DlgFindNotFoundMsg : "Nurodytas tekstas nerastas.",
+
+// Replace Dialog
+DlgReplaceTitle : "Pakeisti",
+DlgReplaceFindLbl : "Surasti tekstÄ…:",
+DlgReplaceReplaceLbl : "Pakeisti tekstu:",
+DlgReplaceCaseChk : "Skirti didžiąsias ir mažąsias raides",
+DlgReplaceReplaceBtn : "Pakeisti",
+DlgReplaceReplAllBtn : "Pakeisti viskÄ…",
+DlgReplaceWordChk : "Atitikti pilną žodį",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Jūsų naršyklės saugumo nustatymai neleidžia redaktoriui automatiškai įvykdyti iškirpimo operacijų. Tam prašome naudoti klaviatūrą (Ctrl+X).",
+PasteErrorCopy : "Jūsų naršyklės saugumo nustatymai neleidžia redaktoriui automatiškai įvykdyti kopijavimo operacijų. Tam prašome naudoti klaviatūrą (Ctrl+C).",
+
+PasteAsText : "Įdėti kaip gryną tekstą",
+PasteFromWord : "Įdėti iš Word",
+
+DlgPasteMsg2 : "Žemiau esanÄiame įvedimo lauke įdÄ—kite tekstÄ…, naudodami klaviatÅ«rÄ… (<STRONG>Ctrl+V</STRONG>) ir spÅ«stelkite mygtukÄ… <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignoruoti šriftų nustatymus",
+DlgPasteRemoveStyles : "Pašalinti stilių nustatymus",
+DlgPasteCleanBox : "Trinti įvedimo lauką",
+
+// Color Picker
+ColorAutomatic : "Automatinis",
+ColorMoreColors : "Daugiau spalvų...",
+
+// Document Properties
+DocProps : "Dokumento savybÄ—s",
+
+// Anchor Dialog
+DlgAnchorTitle : "Žymės savybės",
+DlgAnchorName : "Žymės vardas",
+DlgAnchorErrorName : "Prašome įvesti žymės vardą",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Žodyne nerastas",
+DlgSpellChangeTo : "Pakeisti į",
+DlgSpellBtnIgnore : "Ignoruoti",
+DlgSpellBtnIgnoreAll : "Ignoruoti visus",
+DlgSpellBtnReplace : "Pakeisti",
+DlgSpellBtnReplaceAll : "Pakeisti visus",
+DlgSpellBtnUndo : "Atšaukti",
+DlgSpellNoSuggestions : "- Nėra pasiūlymų -",
+DlgSpellProgress : "Vyksta rašybos tikrinimas...",
+DlgSpellNoMispell : "Rašybos tikrinimas baigtas: Nerasta rašybos klaidų",
+DlgSpellNoChanges : "Rašybos tikrinimas baigtas: Nėra pakeistų žodžių",
+DlgSpellOneChange : "Rašybos tikrinimas baigtas: Vienas žodis pakeistas",
+DlgSpellManyChanges : "Rašybos tikrinimas baigtas: Pakeista %1 žodžių",
+
+IeSpellDownload : "Rašybos tikrinimas neinstaliuotas. Ar Jūs norite jį dabar atsisiųsti?",
+
+// Button Dialog
+DlgButtonText : "Tekstas (Reikšmė)",
+DlgButtonType : "Tipas",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Vardas",
+DlgCheckboxValue : "Reikšmė",
+DlgCheckboxSelected : "Pažymėtas",
+
+// Form Dialog
+DlgFormName : "Vardas",
+DlgFormAction : "Veiksmas",
+DlgFormMethod : "Metodas",
+
+// Select Field Dialog
+DlgSelectName : "Vardas",
+DlgSelectValue : "Reikšmė",
+DlgSelectSize : "Dydis",
+DlgSelectLines : "eiluÄių",
+DlgSelectChkMulti : "Leisti daugeriopÄ… atrankÄ…",
+DlgSelectOpAvail : "Galimos parinktys",
+DlgSelectOpText : "Tekstas",
+DlgSelectOpValue : "Reikšmė",
+DlgSelectBtnAdd : "Įtraukti",
+DlgSelectBtnModify : "Modifikuoti",
+DlgSelectBtnUp : "Aukštyn",
+DlgSelectBtnDown : "Žemyn",
+DlgSelectBtnSetValue : "Laikyti pažymėta reikšme",
+DlgSelectBtnDelete : "Trinti",
+
+// Textarea Dialog
+DlgTextareaName : "Vardas",
+DlgTextareaCols : "Ilgis",
+DlgTextareaRows : "Plotis",
+
+// Text Field Dialog
+DlgTextName : "Vardas",
+DlgTextValue : "Reikšmė",
+DlgTextCharWidth : "Ilgis simboliais",
+DlgTextMaxChars : "Maksimalus simbolių skaiÄius",
+DlgTextType : "Tipas",
+DlgTextTypeText : "Tekstas",
+DlgTextTypePass : "Slaptažodis",
+
+// Hidden Field Dialog
+DlgHiddenName : "Vardas",
+DlgHiddenValue : "Reikšmė",
+
+// Bulleted List Dialog
+BulletedListProp : "Suženklinto sąrašo savybės",
+NumberedListProp : "Numeruoto sąrašo savybės",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tipas",
+DlgLstTypeCircle : "Apskritimas",
+DlgLstTypeDisc : "Diskas",
+DlgLstTypeSquare : "Kvadratas",
+DlgLstTypeNumbers : "SkaiÄiai (1, 2, 3)",
+DlgLstTypeLCase : "Mažosios raidės (a, b, c)",
+DlgLstTypeUCase : "Didžiosios raidės (A, B, C)",
+DlgLstTypeSRoman : "RomÄ—nų mažieji skaiÄiai (i, ii, iii)",
+DlgLstTypeLRoman : "RomÄ—nų didieji skaiÄiai (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Bendros savybÄ—s",
+DlgDocBackTab : "Fonas",
+DlgDocColorsTab : "Spalvos ir kraštinės",
+DlgDocMetaTab : "Meta duomenys",
+
+DlgDocPageTitle : "Puslapio antraštė",
+DlgDocLangDir : "Kalbos kryptis",
+DlgDocLangDirLTR : "Iš kairės į dešinę (LTR)",
+DlgDocLangDirRTL : "Iš dešinės į kairę (RTL)",
+DlgDocLangCode : "Kalbos kodas",
+DlgDocCharSet : "Simbolių kodavimo lentelė",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Kita simbolių kodavimo lentelė",
+
+DlgDocDocType : "Dokumento tipo antraštė",
+DlgDocDocTypeOther : "Kita dokumento tipo antraštė",
+DlgDocIncXHTML : "Įtraukti XHTML deklaracijas",
+DlgDocBgColor : "Fono spalva",
+DlgDocBgImage : "Fono paveikslÄ—lio nuoroda (URL)",
+DlgDocBgNoScroll : "Neslenkantis fonas",
+DlgDocCText : "Tekstas",
+DlgDocCLink : "Nuoroda",
+DlgDocCVisited : "Aplankyta nuoroda",
+DlgDocCActive : "Aktyvi nuoroda",
+DlgDocMargins : "Puslapio kraštinės",
+DlgDocMaTop : "Viršuje",
+DlgDocMaLeft : "KairÄ—je",
+DlgDocMaRight : "Dešinėje",
+DlgDocMaBottom : "ApaÄioje",
+DlgDocMeIndex : "Dokumento indeksavimo raktiniai žodžiai (atskirti kableliais)",
+DlgDocMeDescr : "Dokumento apibūdinimas",
+DlgDocMeAuthor : "Autorius",
+DlgDocMeCopy : "AutorinÄ—s teisÄ—s",
+DlgDocPreview : "Peržiūra",
+
+// Templates Dialog
+Templates : "Å ablonai",
+DlgTemplatesTitle : "Turinio Å¡ablonai",
+DlgTemplatesSelMsg : "Pasirinkite norimÄ… Å¡ablonÄ…<br>(<b>DÄ—mesio!</b> esamas turinys bus prarastas):",
+DlgTemplatesLoading : "Įkeliamas šablonų sąrašas. Prašome palaukti...",
+DlgTemplatesNoTpl : "(Å ablonų sÄ…raÅ¡as tuÅ¡Äias)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Apie",
+DlgAboutBrowserInfoTab : "Naršyklės informacija",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "versija",
+DlgAboutInfo : "PapildomÄ… informacijÄ… galima gauti"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/lv.js b/httemplate/elements/fckeditor/editor/lang/lv.js
new file mode 100644
index 0000000..6809426
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/lv.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Latvian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "SamazinÄt rÄ«ku joslu",
+ToolbarExpand : "PaplaÅ¡inÄt rÄ«ku joslu",
+
+// Toolbar Items and Context Menu
+Save : "SaglabÄt",
+NewPage : "Jauna lapa",
+Preview : "PÄrskatÄ«t",
+Cut : "Izgriezt",
+Copy : "Kopēt",
+Paste : "Ievietot",
+PasteText : "Ievietot kÄ vienkÄrÅ¡u tekstu",
+PasteWord : "Ievietot no Worda",
+Print : "DrukÄt",
+SelectAll : "Iezīmēt visu",
+RemoveFormat : "Noņemt stilus",
+InsertLinkLbl : "Hipersaite",
+InsertLink : "Ievietot/Labot hipersaiti",
+RemoveLink : "Noņemt hipersaiti",
+Anchor : "Ievietot/Labot iezīmi",
+InsertImageLbl : "Attēls",
+InsertImage : "Ievietot/Labot Attēlu",
+InsertFlashLbl : "Flash",
+InsertFlash : "Ievietot/Labot Flash",
+InsertTableLbl : "Tabula",
+InsertTable : "Ievietot/Labot Tabulu",
+InsertLineLbl : "AtdalÄ«tÄjsvÄ«tra",
+InsertLine : "Ievietot horizontÄlu AtdalÄ«tÄjsvÄ«tru",
+InsertSpecialCharLbl: "Īpašs simbols",
+InsertSpecialChar : "Ievietot speciÄlo simbolu",
+InsertSmileyLbl : "Smaidiņi",
+InsertSmiley : "Ievietot smaidiņu",
+About : "ĪsumÄ par FCKeditor",
+Bold : "Treknu Å¡riftu",
+Italic : "SlÄ«prakstÄ",
+Underline : "Apakšsvītra",
+StrikeThrough : "PÄrsvÄ«trots",
+Subscript : "ZemrakstÄ",
+Superscript : "AugÅ¡rakstÄ",
+LeftJustify : "IzlÄ«dzinÄt pa kreisi",
+CenterJustify : "IzlÄ«dzinÄt pret centru",
+RightJustify : "IzlÄ«dzinÄt pa labi",
+BlockJustify : "IzlÄ«dzinÄt malas",
+DecreaseIndent : "SamazinÄt atkÄpi",
+IncreaseIndent : "PalielinÄt atkÄpi",
+Undo : "Atcelt",
+Redo : "AtkÄrtot",
+NumberedListLbl : "Numurēts saraksts",
+NumberedList : "Ievietot/Noņemt numerēto sarakstu",
+BulletedListLbl : "Izcelts saraksts",
+BulletedList : "Ievietot/Noņemt izceltu sarakstu",
+ShowTableBorders : "ParÄdÄ«t tabulas robežas",
+ShowDetails : "ParÄdÄ«t sÄ«kÄku informÄciju",
+Style : "Stils",
+FontFormat : "FormÄts",
+Font : "Å rifts",
+FontSize : "Izmērs",
+TextColor : "Teksta krÄsa",
+BGColor : "Fona krÄsa",
+Source : "HTML kods",
+Find : "Meklēt",
+Replace : "Nomainīt",
+SpellCheck : "PareizrakstÄ«bas pÄrbaude",
+UniversalKeyboard : "UniversÄla klaviatÅ«ra",
+PageBreakLbl : "Lapas pÄrtraukums",
+PageBreak : "Ievietot lapas pÄrtraukumu",
+
+Form : "Forma",
+Checkbox : "Atzīmēšanas kastīte",
+RadioButton : "Izvēles poga",
+TextField : "Teksta rinda",
+Textarea : "Teksta laukums",
+HiddenField : "Paslēpta teksta rinda",
+Button : "Poga",
+SelectionField : "Iezīmēšanas lauks",
+ImageButton : "Attēlpoga",
+
+FitWindow : "Maksimizēt redaktora izmēru",
+
+// Context Menu
+EditLink : "Labot hipersaiti",
+CellCM : "Å Å«na",
+RowCM : "Rinda",
+ColumnCM : "Kolonna",
+InsertRow : "Ievietot rindu",
+DeleteRows : "Dzēst rindas",
+InsertColumn : "Ievietot kolonnu",
+DeleteColumns : "Dzēst kolonnas",
+InsertCell : "Ievietot rūtiņu",
+DeleteCells : "Dzēst rūtiņas",
+MergeCells : "Apvienot rūtiņas",
+SplitCell : "Sadalīt rūtiņu",
+TableDelete : "Dzēst tabulu",
+CellProperties : "Rūtiņas īpašības",
+TableProperties : "Tabulas īpašības",
+ImageProperties : "Attēla īpašības",
+FlashProperties : "Flash īpašības",
+
+AnchorProp : "Iezīmes īpašības",
+ButtonProp : "Pogas īpašības",
+CheckboxProp : "Atzīmēšanas kastītes īpašības",
+HiddenFieldProp : "PaslÄ“ptÄs teksta rindas Ä«paÅ¡Ä«bas",
+RadioButtonProp : "Izvēles poga īpašības",
+ImageButtonProp : "Attēlpogas īpašības",
+TextFieldProp : "Teksta rindas īpašības",
+SelectionFieldProp : "Iezīmēšanas lauka īpašības",
+TextareaProp : "Teksta laukuma īpašības",
+FormProp : "Formas īpašības",
+
+FontFormats : "NormÄls teksts;FormatÄ“ts teksts;Adrese;Virsraksts 1;Virsraksts 2;Virsraksts 3;Virsraksts 4;Virsraksts 5;Virsraksts 6;Rindkopa (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Tiek apstrÄdÄts XHTML. LÅ«dzu uzgaidiet...",
+Done : "Darīts",
+PasteWordConfirm : "Teksta fragments, kas tiek ievietots, izskatÄs, ka bÅ«tu sagatavots Word'Ä. Vai vÄ“laties to apstrÄdÄt pirms ievietoÅ¡anas?",
+NotCompatiblePaste : "Å Ä« darbÄ«ba ir pieejama Internet Explorer'Ä«, kas jaunÄks par 5.5 versiju. Vai vÄ“laties ievietot bez apstrÄdes?",
+UnknownToolbarItem : "NezinÄms rÄ«ku joslas objekts \"%1\"",
+UnknownCommand : "NezinÄmas darbÄ«bas nosaukums \"%1\"",
+NotImplemented : "Darbība netika paveikta",
+UnknownToolbarSet : "Rīku joslas komplekts \"%1\" neeksistē",
+NoActiveX : "Interneta pÄrlÅ«kprogrammas droÅ¡Ä«bas uzstÄdÄ«jumi varÄ“tu ietekmÄ“t dažas no redaktora Ä«paÅ¡Ä«bÄm. JÄbÅ«t aktivizÄ“tai sadaļai \"Run ActiveX controls and plug-ins\". SavÄdÄk ir iespÄ“jamas kļūdas darbÄ«bÄ un kļūdu paziņojumu parÄdÄ«Å¡anÄs.",
+BrowseServerBlocked : "Resursu pÄrlÅ«ks nevar tikt atvÄ“rts. PÄrliecinieties, ka uznirstoÅ¡o logu bloÄ·Ä“tÄji ir atslÄ“gti.",
+DialogBlocked : "Nav iespÄ“jams atvÄ“rt dialoglogu. PÄrliecinieties, ka uznirstoÅ¡o logu bloÄ·Ä“tÄji ir atslÄ“gti.",
+
+// Dialogs
+DlgBtnOK : "Darīts!",
+DlgBtnCancel : "Atcelt",
+DlgBtnClose : "Aizvērt",
+DlgBtnBrowseServer : "Skatīt servera saturu",
+DlgAdvancedTag : "Izvērstais",
+DlgOpOther : "<Cits>",
+DlgInfoTab : "InformÄcija",
+DlgAlertUrl : "LÅ«dzu, ievietojiet hipersaiti",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nav iestatīts>",
+DlgGenId : "Id",
+DlgGenLangDir : "Valodas lasīšanas virziens",
+DlgGenLangDirLtr : "No kreisÄs uz labo (LTR)",
+DlgGenLangDirRtl : "No labÄs uz kreiso (RTL)",
+DlgGenLangCode : "Valodas kods",
+DlgGenAccessKey : "Pieejas kods",
+DlgGenName : "Nosaukums",
+DlgGenTabIndex : "Ciļņu indekss",
+DlgGenLongDescr : "Gara apraksta Hipersaite",
+DlgGenClass : "Stilu saraksta klases",
+DlgGenTitle : "Konsultatīvs virsraksts",
+DlgGenContType : "Konsultatīvs satura tips",
+DlgGenLinkCharset : "PievienotÄ resursa kodu tabula",
+DlgGenStyle : "Stils",
+
+// Image Dialog
+DlgImgTitle : "Attēla īpašības",
+DlgImgInfoTab : "InformÄcija par attÄ“lu",
+DlgImgBtnUpload : "Nosūtīt serverim",
+DlgImgURL : "URL",
+DlgImgUpload : "AugÅ¡upielÄdÄ“t",
+DlgImgAlt : "Alternatīvais teksts",
+DlgImgWidth : "Platums",
+DlgImgHeight : "Augstums",
+DlgImgLockRatio : "Nemainīga Augstuma/Platuma attiecība",
+DlgBtnResetSize : "Atjaunot sÄkotnÄ“jo izmÄ“ru",
+DlgImgBorder : "RÄmis",
+DlgImgHSpace : "HorizontÄlÄ telpa",
+DlgImgVSpace : "VertikÄlÄ telpa",
+DlgImgAlign : "NolÄ«dzinÄt",
+DlgImgAlignLeft : "Pa kreisi",
+DlgImgAlignAbsBottom: "AbsolÅ«ti apakÅ¡Ä",
+DlgImgAlignAbsMiddle: "AbsolÅ«ti vertikÄli centrÄ“ts",
+DlgImgAlignBaseline : "PamatrindÄ",
+DlgImgAlignBottom : "ApakÅ¡Ä",
+DlgImgAlignMiddle : "VertikÄli centrÄ“ts",
+DlgImgAlignRight : "Pa labi",
+DlgImgAlignTextTop : "Teksta augÅ¡Ä",
+DlgImgAlignTop : "AugÅ¡Ä",
+DlgImgPreview : "PÄrskats",
+DlgImgAlertUrl : "LÅ«dzu norÄdÄ«t attÄ“la hipersaiti",
+DlgImgLinkTab : "Hipersaite",
+
+// Flash Dialog
+DlgFlashTitle : "Flash īpašības",
+DlgFlashChkPlay : "AutomÄtiska atskaņoÅ¡ana",
+DlgFlashChkLoop : "NepÄrtraukti",
+DlgFlashChkMenu : "Atļaut Flash izvēlni",
+DlgFlashScale : "Mainīt izmēru",
+DlgFlashScaleAll : "RÄdÄ«t visu",
+DlgFlashScaleNoBorder : "Bez rÄmja",
+DlgFlashScaleFit : "Precīzs izmērs",
+
+// Link Dialog
+DlgLnkWindowTitle : "Hipersaite",
+DlgLnkInfoTab : "Hipersaites informÄcija",
+DlgLnkTargetTab : "MÄ“rÄ·is",
+
+DlgLnkType : "Hipersaites tips",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "IezÄ«me Å¡ajÄ lapÄ",
+DlgLnkTypeEMail : "E-pasts",
+DlgLnkProto : "Protokols",
+DlgLnkProtoOther : "<cits>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Izvēlēties iezīmi",
+DlgLnkAnchorByName : "Pēc iezīmes nosaukuma",
+DlgLnkAnchorById : "PÄ“c elementa ID",
+DlgLnkNoAnchors : "<Å ajÄ dokumentÄ nav iezÄ«mju>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-pasta adrese",
+DlgLnkEMailSubject : "Ziņas tēma",
+DlgLnkEMailBody : "Ziņas saturs",
+DlgLnkUpload : "AugÅ¡upielÄdÄ“t",
+DlgLnkBtnUpload : "Nosūtīt serverim",
+
+DlgLnkTarget : "MÄ“rÄ·is",
+DlgLnkTargetFrame : "<ietvars>",
+DlgLnkTargetPopup : "<uznirstoÅ¡Ä logÄ>",
+DlgLnkTargetBlank : "JaunÄ logÄ (_blank)",
+DlgLnkTargetParent : "EsoÅ¡ajÄ logÄ (_parent)",
+DlgLnkTargetSelf : "TajÄ paÅ¡Ä logÄ (_self)",
+DlgLnkTargetTop : "VisredzamÄkajÄ logÄ (_top)",
+DlgLnkTargetFrameName : "MÄ“rÄ·a ietvara nosaukums",
+DlgLnkPopWinName : "UznirstoÅ¡Ä loga nosaukums",
+DlgLnkPopWinFeat : "UznirstoÅ¡Ä loga nosaukums Ä«paÅ¡Ä«bas",
+DlgLnkPopResize : "Ar mainÄmu izmÄ“ru",
+DlgLnkPopLocation : "AtraÅ¡anÄs vietas josla",
+DlgLnkPopMenu : "Izvēlnes josla",
+DlgLnkPopScroll : "Ritjoslas",
+DlgLnkPopStatus : "Statusa josla",
+DlgLnkPopToolbar : "RÄ«ku josla",
+DlgLnkPopFullScrn : "PilnÄ ekrÄnÄ (IE)",
+DlgLnkPopDependent : "Atkarīgs (Netscape)",
+DlgLnkPopWidth : "Platums",
+DlgLnkPopHeight : "Augstums",
+DlgLnkPopLeft : "KreisÄ koordinÄte",
+DlgLnkPopTop : "AugÅ¡Ä“jÄ koordinÄte",
+
+DlnLnkMsgNoUrl : "LÅ«dzu norÄdi hipersaiti",
+DlnLnkMsgNoEMail : "LÅ«dzu norÄdi e-pasta adresi",
+DlnLnkMsgNoAnchor : "LÅ«dzu norÄdi iezÄ«mi",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "IzvÄ“lies krÄsu",
+DlgColorBtnClear : "Dzēst",
+DlgColorHighlight : "Izcelt",
+DlgColorSelected : "Iezīmētais",
+
+// Smiley Dialog
+DlgSmileyTitle : "Ievietot smaidiņu",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Ievietot īpašu simbolu",
+
+// Table Dialog
+DlgTableTitle : "Tabulas īpašības",
+DlgTableRows : "Rindas",
+DlgTableColumns : "Kolonnas",
+DlgTableBorder : "RÄmja izmÄ“rs",
+DlgTableAlign : "Novietojums",
+DlgTableAlignNotSet : "<nav norÄdÄ«ts>",
+DlgTableAlignLeft : "Pa kreisi",
+DlgTableAlignCenter : "Centrēti",
+DlgTableAlignRight : "Pa labi",
+DlgTableWidth : "Platums",
+DlgTableWidthPx : "pikseļos",
+DlgTableWidthPc : "procentuÄli",
+DlgTableHeight : "Augstums",
+DlgTableCellSpace : "Rūtiņu atstatums",
+DlgTableCellPad : "Rūtiņu nobīde",
+DlgTableCaption : "Leģenda",
+DlgTableSummary : "AnotÄcija",
+
+// Table Cell Dialog
+DlgCellTitle : "Rūtiņas īpašības",
+DlgCellWidth : "Platums",
+DlgCellWidthPx : "pikseļi",
+DlgCellWidthPc : "procentos",
+DlgCellHeight : "Augstums",
+DlgCellWordWrap : "Teksta pÄrnese",
+DlgCellWordWrapNotSet : "<nav norÄdÄ«ta>",
+DlgCellWordWrapYes : "JÄ",
+DlgCellWordWrapNo : "NÄ“",
+DlgCellHorAlign : "HorizontÄla novietojums",
+DlgCellHorAlignNotSet : "<Nav norÄdÄ«ts>",
+DlgCellHorAlignLeft : "Pa kreisi",
+DlgCellHorAlignCenter : "Centrēti",
+DlgCellHorAlignRight: "Pa labi",
+DlgCellVerAlign : "VertikÄlais novietojums",
+DlgCellVerAlignNotSet : "<nav norÄdÄ«ts>",
+DlgCellVerAlignTop : "Augša",
+DlgCellVerAlignMiddle : "Vidus",
+DlgCellVerAlignBottom : "Apakša",
+DlgCellVerAlignBaseline : "PamatrindÄ",
+DlgCellRowSpan : "Rindu pÄrnese",
+DlgCellCollSpan : "Kolonnu pÄrnese",
+DlgCellBackColor : "Fona krÄsa",
+DlgCellBorderColor : "RÄmja krÄsa",
+DlgCellBtnSelect : "Iezīmē...",
+
+// Find Dialog
+DlgFindTitle : "MeklÄ“tÄjs",
+DlgFindFindBtn : "Meklēt",
+DlgFindNotFoundMsg : "NorÄdÄ«tÄ frÄze netika atrasta.",
+
+// Replace Dialog
+DlgReplaceTitle : "Aizvietošana",
+DlgReplaceFindLbl : "Meklēt:",
+DlgReplaceReplaceLbl : "Nomainīt uz:",
+DlgReplaceCaseChk : "Reģistrjūtīgs",
+DlgReplaceReplaceBtn : "Aizvietot",
+DlgReplaceReplAllBtn : "Aizvietot visu",
+DlgReplaceWordChk : "JÄsakrÄ«t pilnÄ«bÄ",
+
+// Paste Operations / Dialog
+PasteErrorCut : "JÅ«su pÄrlÅ«kprogrammas droÅ¡Ä«bas iestatÄ«jumi nepieļauj editoram automÄtiski veikt izgrieÅ¡anas darbÄ«bu. LÅ«dzu, izmantojiet (Ctrl+X, lai veiktu Å¡o darbÄ«bu.",
+PasteErrorCopy : "JÅ«su pÄrlÅ«kprogrammas droÅ¡Ä«bas iestatÄ«jumi nepieļauj editoram automÄtiski veikt kopÄ“Å¡anas darbÄ«bu. LÅ«dzu, izmantojiet (Ctrl+C), lai veiktu Å¡o darbÄ«bu.",
+
+PasteAsText : "Ievietot kÄ vienkÄrÅ¡u tekstu",
+PasteFromWord : "Ievietot no Worda",
+
+DlgPasteMsg2 : "LÅ«dzu, ievietojiet tekstu Å¡ajÄ laukumÄ, izmantojot klaviatÅ«ru (<STRONG>Ctrl+V</STRONG>) un apstipriniet ar <STRONG>DarÄ«ts!</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "IgnorÄ“t iepriekÅ¡ norÄdÄ«tos fontus",
+DlgPasteRemoveStyles : "Noņemt norÄdÄ«tos stilus",
+DlgPasteCleanBox : "ApstrÄdÄt laukuma saturu",
+
+// Color Picker
+ColorAutomatic : "AutomÄtiska",
+ColorMoreColors : "PlaÅ¡Äka palete...",
+
+// Document Properties
+DocProps : "Dokumenta īpašības",
+
+// Anchor Dialog
+DlgAnchorTitle : "Iezīmes īpašības",
+DlgAnchorName : "Iezīmes nosaukums",
+DlgAnchorErrorName : "LÅ«dzu norÄdiet iezÄ«mes nosaukumu",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Netika atrasts vÄrdnÄ«cÄ",
+DlgSpellChangeTo : "Nomainīt uz",
+DlgSpellBtnIgnore : "Ignorēt",
+DlgSpellBtnIgnoreAll : "Ignorēt visu",
+DlgSpellBtnReplace : "Aizvietot",
+DlgSpellBtnReplaceAll : "Aizvietot visu",
+DlgSpellBtnUndo : "Atcelt",
+DlgSpellNoSuggestions : "- Nav ieteikumu -",
+DlgSpellProgress : "Notiek pareizrakstÄ«bas pÄrbaude...",
+DlgSpellNoMispell : "PareizrakstÄ«bas pÄrbaude pabeigta: kļūdas netika atrastas",
+DlgSpellNoChanges : "PareizrakstÄ«bas pÄrbaude pabeigta: nekas netika labots",
+DlgSpellOneChange : "PareizrakstÄ«bas pÄrbaude pabeigta: 1 vÄrds izmainÄ«ts",
+DlgSpellManyChanges : "PareizrakstÄ«bas pÄrbaude pabeigta: %1 vÄrdi tika mainÄ«ti",
+
+IeSpellDownload : "PareizrakstÄ«bas pÄrbaudÄ«tÄjs nav pievienots. Vai vÄ“laties to lejupielÄdÄ“t tagad?",
+
+// Button Dialog
+DlgButtonText : "Teksts (vērtība)",
+DlgButtonType : "Tips",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nosaukums",
+DlgCheckboxValue : "Vērtība",
+DlgCheckboxSelected : "Iezīmēts",
+
+// Form Dialog
+DlgFormName : "Nosaukums",
+DlgFormAction : "Darbība",
+DlgFormMethod : "Metode",
+
+// Select Field Dialog
+DlgSelectName : "Nosaukums",
+DlgSelectValue : "Vērtība",
+DlgSelectSize : "Izmērs",
+DlgSelectLines : "rindas",
+DlgSelectChkMulti : "Atļaut vairÄkus iezÄ«mÄ“jumus",
+DlgSelectOpAvail : "PieejamÄs iespÄ“jas",
+DlgSelectOpText : "Teksts",
+DlgSelectOpValue : "Vērtība",
+DlgSelectBtnAdd : "Pievienot",
+DlgSelectBtnModify : "Veikt izmaiņas",
+DlgSelectBtnUp : "Augšup",
+DlgSelectBtnDown : "Lejup",
+DlgSelectBtnSetValue : "Noteikt kÄ iezÄ«mÄ“to vÄ“rtÄ«bu",
+DlgSelectBtnDelete : "Dzēst",
+
+// Textarea Dialog
+DlgTextareaName : "Nosaukums",
+DlgTextareaCols : "Kolonnas",
+DlgTextareaRows : "Rindas",
+
+// Text Field Dialog
+DlgTextName : "Nosaukums",
+DlgTextValue : "Vērtība",
+DlgTextCharWidth : "Simbolu platums",
+DlgTextMaxChars : "Simbolu maksimÄlais daudzums",
+DlgTextType : "Tips",
+DlgTextTypeText : "Teksts",
+DlgTextTypePass : "Parole",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nosaukums",
+DlgHiddenValue : "Vērtība",
+
+// Bulleted List Dialog
+BulletedListProp : "Aizzīmju saraksta īpašības",
+NumberedListProp : "NumerÄ“tÄ saraksta Ä«paÅ¡Ä«bas",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tips",
+DlgLstTypeCircle : "Aplis",
+DlgLstTypeDisc : "Disks",
+DlgLstTypeSquare : "KvadrÄts",
+DlgLstTypeNumbers : "Skaitļi (1, 2, 3)",
+DlgLstTypeLCase : "Maziem burtiem (a, b, c)",
+DlgLstTypeUCase : "Lieliem burtiem (A, B, C)",
+DlgLstTypeSRoman : "Maziem romiešu cipariem (i, ii, iii)",
+DlgLstTypeLRoman : "Lieliem romiešu cipariem (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "VispÄrÄ«ga informÄcija",
+DlgDocBackTab : "Fons",
+DlgDocColorsTab : "KrÄsas un robežu nobÄ«des",
+DlgDocMetaTab : "META dati",
+
+DlgDocPageTitle : "Dokumenta virsraksts <Title>",
+DlgDocLangDir : "Valodas lasīšanas virziens",
+DlgDocLangDirLTR : "No kreisÄs uz labo (LTR)",
+DlgDocLangDirRTL : "No labÄs uz kreiso (RTL)",
+DlgDocLangCode : "Valodas kods",
+DlgDocCharSet : "Simbolu kodējums",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Cits simbolu kodējums",
+
+DlgDocDocType : "Dokumenta tips",
+DlgDocDocTypeOther : "Cits dokumenta tips",
+DlgDocIncXHTML : "Ietvert XHTML deklarÄcijas",
+DlgDocBgColor : "Fona krÄsa",
+DlgDocBgImage : "Fona attēla hipersaite",
+DlgDocBgNoScroll : "Fona attēls ir fiksēts",
+DlgDocCText : "Teksts",
+DlgDocCLink : "Hipersaite",
+DlgDocCVisited : "Apmeklēta hipersaite",
+DlgDocCActive : "Aktīva hipersaite",
+DlgDocMargins : "Lapas robežas",
+DlgDocMaTop : "AugÅ¡Ä",
+DlgDocMaLeft : "Pa kreisi",
+DlgDocMaRight : "Pa labi",
+DlgDocMaBottom : "ApakÅ¡Ä",
+DlgDocMeIndex : "Dokumentu aprakstoÅ¡i atslÄ“gvÄrdi (atdalÄ«ti ar komatu)",
+DlgDocMeDescr : "Dokumenta apraksts",
+DlgDocMeAuthor : "Autors",
+DlgDocMeCopy : "Autortiesības",
+DlgDocPreview : "Priekšskats",
+
+// Templates Dialog
+Templates : "Sagataves",
+DlgTemplatesTitle : "Satura sagataves",
+DlgTemplatesSelMsg : "LÅ«dzu, norÄdiet sagatavi, ko atvÄ“rt editorÄ<br>(patreizÄ“jie dati tiks zaudÄ“ti):",
+DlgTemplatesLoading : "Notiek sagatavju saraksta ielÄde. LÅ«dzu, uzgaidiet...",
+DlgTemplatesNoTpl : "(Nav norÄdÄ«tas sagataves)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Par",
+DlgAboutBrowserInfoTab : "InformÄcija par pÄrlÅ«kprogrammu",
+DlgAboutLicenseTab : "Licence",
+DlgAboutVersion : "versija",
+DlgAboutInfo : "Papildus informÄcija ir pieejama"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/mn.js b/httemplate/elements/fckeditor/editor/lang/mn.js
new file mode 100644
index 0000000..ba8f798
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/mn.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Mongolian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Багажны Ñ…ÑÑÑг ÑвдÑÑ…",
+ToolbarExpand : "Багажны Ñ…ÑÑÑг өргөтгөх",
+
+// Toolbar Items and Context Menu
+Save : "Хадгалах",
+NewPage : "Ð¨Ð¸Ð½Ñ Ñ…ÑƒÑƒÐ´Ð°Ñ",
+Preview : "Уридчлан харах",
+Cut : "Хайчлах",
+Copy : "Хуулах",
+Paste : "Буулгах",
+PasteText : "plain text-ÑÑÑ Ð±ÑƒÑƒÐ»Ð³Ð°Ñ…",
+PasteWord : "Word-Ð¾Ð¾Ñ Ð±ÑƒÑƒÐ»Ð³Ð°Ñ…",
+Print : "Ð¥ÑвлÑÑ…",
+SelectAll : "Бүгдийг нь Ñонгох",
+RemoveFormat : "Формат авч хаÑÑ…",
+InsertLinkLbl : "Линк",
+InsertLink : "Линк Оруулах/ЗаÑварлах",
+RemoveLink : "Линк авч хаÑÑ…",
+Anchor : "Insert/Edit Anchor", //MISSING
+InsertImageLbl : "Зураг",
+InsertImage : "Зураг Оруулах/ЗаÑварлах",
+InsertFlashLbl : "Flash", //MISSING
+InsertFlash : "Insert/Edit Flash", //MISSING
+InsertTableLbl : "Ð¥Ò¯ÑнÑгт",
+InsertTable : "Ð¥Ò¯ÑнÑгт Оруулах/ЗаÑварлах",
+InsertLineLbl : "ЗурааÑ",
+InsertLine : "Хөндлөн Ð·ÑƒÑ€Ð°Ð°Ñ Ð¾Ñ€ÑƒÑƒÐ»Ð°Ñ…",
+InsertSpecialCharLbl: "Онцгой Ñ‚ÑмдÑгт",
+InsertSpecialChar : "Онцгой Ñ‚ÑмдÑгт оруулах",
+InsertSmileyLbl : "Тодорхойлолт",
+InsertSmiley : "Тодорхойлолт оруулах",
+About : "FCKeditor-н тухай",
+Bold : "Тод бүдүүн",
+Italic : "Ðалуу",
+Underline : "Доогуур нь зурааÑтай болгох",
+StrikeThrough : "Дундуур нь зурааÑтай болгох",
+Subscript : "Суурь болгох",
+Superscript : "ЗÑÑ€Ñг болгох",
+LeftJustify : "Зүүн талд байрлуулах",
+CenterJustify : "Төвд байрлуулах",
+RightJustify : "Баруун талд байрлуулах",
+BlockJustify : "Блок Ñ…ÑлбÑÑ€ÑÑÑ€ байрлуулах",
+DecreaseIndent : "Догол мөр нÑмÑÑ…",
+IncreaseIndent : "Догол мөр хаÑах",
+Undo : "Хүчингүй болгох",
+Redo : "Өмнөх үйлдлÑÑ ÑÑргÑÑÑ…",
+NumberedListLbl : "ДугаарлагдÑан жагÑаалт",
+NumberedList : "ДугаарлагдÑан жагÑаалт Оруулах/Ðвах",
+BulletedListLbl : "ЦÑгтÑй жагÑаалт",
+BulletedList : "ЦÑгтÑй жагÑаалт Оруулах/Ðвах",
+ShowTableBorders : "Ð¥Ò¯ÑнÑгтийн хүрÑÑг үзүүлÑÑ…",
+ShowDetails : "Деталчлан үзүүлÑÑ…",
+Style : "Загвар",
+FontFormat : "Формат",
+Font : "Фонт",
+FontSize : "Ð¥ÑмжÑÑ",
+TextColor : "Фонтны өнгө",
+BGColor : "Фонны өнгө",
+Source : "Код",
+Find : "Хайх",
+Replace : "Солих",
+SpellCheck : "Check Spelling", //MISSING
+UniversalKeyboard : "Universal Keyboard", //MISSING
+PageBreakLbl : "Page Break", //MISSING
+PageBreak : "Insert Page Break", //MISSING
+
+Form : "Form", //MISSING
+Checkbox : "Checkbox", //MISSING
+RadioButton : "Radio Button", //MISSING
+TextField : "Text Field", //MISSING
+Textarea : "Textarea", //MISSING
+HiddenField : "Hidden Field", //MISSING
+Button : "Button", //MISSING
+SelectionField : "Selection Field", //MISSING
+ImageButton : "Image Button", //MISSING
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Ð¥Ð¾Ð»Ð±Ð¾Ð¾Ñ Ð·Ð°Ñварлах",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Мөр оруулах",
+DeleteRows : "Мөр уÑтгах",
+InsertColumn : "Багана оруулах",
+DeleteColumns : "Багана уÑтгах",
+InsertCell : "Ðүх оруулах",
+DeleteCells : "Ðүх уÑтгах",
+MergeCells : "Ðүх нÑгтÑÑ…",
+SplitCell : "Ðүх туÑгайрлах",
+TableDelete : "Delete Table", //MISSING
+CellProperties : "ХооÑон зайн шинж чанар",
+TableProperties : "Ð¥Ò¯ÑнÑгт",
+ImageProperties : "Зураг",
+FlashProperties : "Flash Properties", //MISSING
+
+AnchorProp : "Anchor Properties", //MISSING
+ButtonProp : "Button Properties", //MISSING
+CheckboxProp : "Checkbox Properties", //MISSING
+HiddenFieldProp : "Hidden Field Properties", //MISSING
+RadioButtonProp : "Radio Button Properties", //MISSING
+ImageButtonProp : "Image Button Properties", //MISSING
+TextFieldProp : "Text Field Properties", //MISSING
+SelectionFieldProp : "Selection Field Properties", //MISSING
+TextareaProp : "Textarea Properties", //MISSING
+FormProp : "Form Properties", //MISSING
+
+FontFormats : "Ð¥Ñвийн;Formatted;ХаÑг;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Paragraph (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML үйл Ñвц Ñвагдаж байна. ХүлÑÑÐ½Ñ Ò¯Ò¯...",
+Done : "Хийх",
+PasteWordConfirm : "Word-Ð¾Ð¾Ñ Ñ…ÑƒÑƒÐ»Ñан текÑÑ‚ÑÑ Ñанаж байгааг нь буулгахыг та Ñ…Ò¯Ñч байна уу. Та текÑÑ‚-ÑÑ Ð±ÑƒÑƒÐ»Ð³Ð°Ñ…Ñ‹Ð½ өмнө цÑвÑрлÑÑ… Ò¯Ò¯?",
+NotCompatiblePaste : "Ð­Ð½Ñ ÐºÐ¾Ð¼Ð¼Ð°Ð½Ð´ Internet Explorer-ын 5.5 буюу түүнÑÑÑ Ð´ÑÑш хувилбарт идвÑхшинÑ. Та цÑвÑрлÑхгүйгÑÑÑ€ буулгахыг Ñ…Ò¯Ñч байна?",
+UnknownToolbarItem : "Багажны Ñ…ÑÑгийн \"%1\" item мÑдÑгдÑхгүй байна",
+UnknownCommand : "\"%1\" комманд нÑÑ€ мÑдагдÑхгүй байна",
+NotImplemented : "Зөвшөөрөгдөхгүй комманд",
+UnknownToolbarSet : "Багажны Ñ…ÑÑÑгт \"%1\" оноох, Ò¯Ò¯ÑÑÑгүй байна",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Болих",
+DlgBtnClose : "Хаах",
+DlgBtnBrowseServer : "Browse Server", //MISSING
+DlgAdvancedTag : "ÐÑмÑлт",
+DlgOpOther : "<Other>", //MISSING
+DlgInfoTab : "Info", //MISSING
+DlgAlertUrl : "Please insert the URL", //MISSING
+
+// General Dialogs Labels
+DlgGenNotSet : "<Оноохгүй>",
+DlgGenId : "Id",
+DlgGenLangDir : "Ð¥Ñлний чиглÑл",
+DlgGenLangDirLtr : "ЗүүнÑÑÑ Ð±Ð°Ñ€ÑƒÑƒÐ½ (LTR)",
+DlgGenLangDirRtl : "Ð‘Ð°Ñ€ÑƒÑƒÐ½Ð°Ð°Ñ Ð·Ò¯Ò¯Ð½ (RTL)",
+DlgGenLangCode : "Ð¥Ñлний код",
+DlgGenAccessKey : "Холбох түлхүүр",
+DlgGenName : "ÐÑÑ€",
+DlgGenTabIndex : "Tab индекÑ",
+DlgGenLongDescr : "URL-ын тайлбар",
+DlgGenClass : "Stylesheet клаÑÑууд",
+DlgGenTitle : "Зөвлөлдөх гарчиг",
+DlgGenContType : "Зөвлөлдөх төрлийн агуулга",
+DlgGenLinkCharset : "ТÑмдÑгт оноох нөөцөд холбогдÑон",
+DlgGenStyle : "Загвар",
+
+// Image Dialog
+DlgImgTitle : "Зураг",
+DlgImgInfoTab : "Зурагны мÑдÑÑлÑл",
+DlgImgBtnUpload : "Үүнийг ÑервÑррүү илгÑÑ",
+DlgImgURL : "URL",
+DlgImgUpload : "Хуулах",
+DlgImgAlt : "Тайлбар текÑÑ‚",
+DlgImgWidth : "Өргөн",
+DlgImgHeight : "Өндөр",
+DlgImgLockRatio : "Lock Ratio",
+DlgBtnResetSize : "Ñ…ÑмжÑÑ Ð´Ð°Ñ…Ð¸Ð½ оноох",
+DlgImgBorder : "ХүрÑÑ",
+DlgImgHSpace : "Хөндлөн зай",
+DlgImgVSpace : "БоÑоо зай",
+DlgImgAlign : "ЭгнÑÑ",
+DlgImgAlignLeft : "Зүүн",
+DlgImgAlignAbsBottom: "Abs доод талд",
+DlgImgAlignAbsMiddle: "Abs Дунд талд",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Доод талд",
+DlgImgAlignMiddle : "Дунд талд",
+DlgImgAlignRight : "Баруун",
+DlgImgAlignTextTop : "ТекÑÑ‚ дÑÑÑ€",
+DlgImgAlignTop : "ДÑÑд талд",
+DlgImgPreview : "Уридчлан харах",
+DlgImgAlertUrl : "Зурагны URL-ын төрлийн Ñонгоно уу",
+DlgImgLinkTab : "Link", //MISSING
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties", //MISSING
+DlgFlashChkPlay : "Auto Play", //MISSING
+DlgFlashChkLoop : "Loop", //MISSING
+DlgFlashChkMenu : "Enable Flash Menu", //MISSING
+DlgFlashScale : "Scale", //MISSING
+DlgFlashScaleAll : "Show all", //MISSING
+DlgFlashScaleNoBorder : "No Border", //MISSING
+DlgFlashScaleFit : "Exact Fit", //MISSING
+
+// Link Dialog
+DlgLnkWindowTitle : "Линк",
+DlgLnkInfoTab : "Линкийн мÑдÑÑлÑл",
+DlgLnkTargetTab : "Байрлал",
+
+DlgLnkType : "Линкийн төрөл",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Ð­Ð½Ñ Ñ…ÑƒÑƒÐ´Ð°Ñандах холбооÑ",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Протокол",
+DlgLnkProtoOther : "<буÑад>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Ð¥Ð¾Ð»Ð±Ð¾Ð¾Ñ Ñонгох",
+DlgLnkAnchorByName : "ХолбооÑын нÑÑ€ÑÑÑ€",
+DlgLnkAnchorById : "ЭлемÑнт Id-гаар",
+DlgLnkNoAnchors : "<Баримт бичиг холбооÑгүй байна>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail ХаÑг",
+DlgLnkEMailSubject : "Message Subject",
+DlgLnkEMailBody : "Message-ийн агуулга",
+DlgLnkUpload : "Хуулах",
+DlgLnkBtnUpload : "Үүнийг Ñерверрүү илгÑÑ",
+
+DlgLnkTarget : "Байрлал",
+DlgLnkTargetFrame : "<Ðгуулах хүрÑÑ>",
+DlgLnkTargetPopup : "<popup цонх>",
+DlgLnkTargetBlank : "Ð¨Ð¸Ð½Ñ Ñ†Ð¾Ð½Ñ… (_blank)",
+DlgLnkTargetParent : "ЭцÑг цонх (_parent)",
+DlgLnkTargetSelf : "ТөÑÑ‚Ñй цонх (_self)",
+DlgLnkTargetTop : "Хамгийн түрүүн байх цонх (_top)",
+DlgLnkTargetFrameName : "Target Frame Name", //MISSING
+DlgLnkPopWinName : "Popup цонхны нÑÑ€",
+DlgLnkPopWinFeat : "Popup цонхны онцлог",
+DlgLnkPopResize : "Ð¥ÑмжÑÑ Ó©Ó©Ñ€Ñ‡Ð»Ó©Ñ…",
+DlgLnkPopLocation : "Location Ñ…ÑÑÑг",
+DlgLnkPopMenu : "Meню Ñ…ÑÑÑг",
+DlgLnkPopScroll : "Скрол Ñ…ÑÑÑгүүд",
+DlgLnkPopStatus : "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ñ…ÑÑÑг",
+DlgLnkPopToolbar : "Багажны Ñ…ÑÑÑг",
+DlgLnkPopFullScrn : "Цонх дүүргÑÑ… (IE)",
+DlgLnkPopDependent : "Хамаатай (Netscape)",
+DlgLnkPopWidth : "Өргөн",
+DlgLnkPopHeight : "Өндөр",
+DlgLnkPopLeft : "Зүүн байрлал",
+DlgLnkPopTop : "ДÑÑд байрлал",
+
+DlnLnkMsgNoUrl : "Линк URL-ÑÑ Ñ‚Ó©Ñ€Ó©Ð»Ð¶Ò¯Ò¯Ð»Ð½Ñ Ò¯Ò¯",
+DlnLnkMsgNoEMail : "Е-mail хаÑгаа Ñ‚Ó©Ñ€Ó©Ð»Ð¶Ò¯Ò¯Ð»Ð½Ñ Ò¯Ò¯",
+DlnLnkMsgNoAnchor : "ХолбооÑоо Ñонгоно уу",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Өнгө Ñонгох",
+DlgColorBtnClear : "ЦÑвÑрлÑÑ…",
+DlgColorHighlight : "Өнгө",
+DlgColorSelected : "СонгогдÑон",
+
+// Smiley Dialog
+DlgSmileyTitle : "Тодорхойлолт оруулах",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Онцгой Ñ‚ÑмдÑгт Ñонгох",
+
+// Table Dialog
+DlgTableTitle : "Ð¥Ò¯ÑнÑгт",
+DlgTableRows : "Мөр",
+DlgTableColumns : "Багана",
+DlgTableBorder : "ХүрÑÑний Ñ…ÑмжÑÑ",
+DlgTableAlign : "ЭгнÑÑ",
+DlgTableAlignNotSet : "<Оноохгүй>",
+DlgTableAlignLeft : "Зүүн талд",
+DlgTableAlignCenter : "Төвд",
+DlgTableAlignRight : "Баруун талд",
+DlgTableWidth : "Өргөн",
+DlgTableWidthPx : "цÑг",
+DlgTableWidthPc : "хувь",
+DlgTableHeight : "Өндөр",
+DlgTableCellSpace : "Ðүх хоорондын зай",
+DlgTableCellPad : "Ðүх доторлох",
+DlgTableCaption : "Тайлбар",
+DlgTableSummary : "Summary", //MISSING
+
+// Table Cell Dialog
+DlgCellTitle : "ХооÑон зайн шинж чанар",
+DlgCellWidth : "Өргөн",
+DlgCellWidthPx : "цÑг",
+DlgCellWidthPc : "хувь",
+DlgCellHeight : "Өндөр",
+DlgCellWordWrap : "Үг таÑлах",
+DlgCellWordWrapNotSet : "<Оноохгүй>",
+DlgCellWordWrapYes : "Тийм",
+DlgCellWordWrapNo : "Үгүй",
+DlgCellHorAlign : "БоÑоо ÑгнÑÑ",
+DlgCellHorAlignNotSet : "<Оноохгүй>",
+DlgCellHorAlignLeft : "Зүүн",
+DlgCellHorAlignCenter : "Төв",
+DlgCellHorAlignRight: "Баруун",
+DlgCellVerAlign : "Хөндлөн ÑгнÑÑ",
+DlgCellVerAlignNotSet : "<Оноохгүй>",
+DlgCellVerAlignTop : "ДÑÑд тал",
+DlgCellVerAlignMiddle : "Дунд",
+DlgCellVerAlignBottom : "Доод тал",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Ðийт мөр",
+DlgCellCollSpan : "Ðийт багана",
+DlgCellBackColor : "Фонны өнгө",
+DlgCellBorderColor : "ХүрÑÑний өнгө",
+DlgCellBtnSelect : "Сонго...",
+
+// Find Dialog
+DlgFindTitle : "Хайх",
+DlgFindFindBtn : "Хайх",
+DlgFindNotFoundMsg : "ХайÑан текÑÑ‚ олÑонгүй.",
+
+// Replace Dialog
+DlgReplaceTitle : "Солих",
+DlgReplaceFindLbl : "Хайх үг/Ò¯ÑÑг:",
+DlgReplaceReplaceLbl : "Солих үг:",
+DlgReplaceCaseChk : "ТÑнцÑÑ… төлөв",
+DlgReplaceReplaceBtn : "Солих",
+DlgReplaceReplAllBtn : "Бүгдийг нь Солих",
+DlgReplaceWordChk : "ТÑнцÑÑ… бүтÑн үг",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Таны browser-ын хамгаалалтын тохиргоо editor-д автоматаар хайчлах үйлдÑлийг зөвшөөрөхгүй байна. (Ctrl+X) товчны хоÑлолыг ашиглана уу.",
+PasteErrorCopy : "Таны browser-ын хамгаалалтын тохиргоо editor-д автоматаар хуулах үйлдÑлийг зөвшөөрөхгүй байна. (Ctrl+C) товчны хоÑлолыг ашиглана уу.",
+
+PasteAsText : "Plain Text-ÑÑÑ Ð±ÑƒÑƒÐ»Ð³Ð°Ñ…",
+PasteFromWord : "Word-Ð¾Ð¾Ñ Ð±ÑƒÑƒÐ»Ð³Ð°Ñ…",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<strong>Ctrl+V</strong>) and hit <strong>OK</strong>.", //MISSING
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignore Font Face definitions", //MISSING
+DlgPasteRemoveStyles : "Remove Styles definitions", //MISSING
+DlgPasteCleanBox : "Clean Up Box", //MISSING
+
+// Color Picker
+ColorAutomatic : "Ðвтоматаар",
+ColorMoreColors : "ÐÑмÑлт өнгөнүүд...",
+
+// Document Properties
+DocProps : "Document Properties", //MISSING
+
+// Anchor Dialog
+DlgAnchorTitle : "Anchor Properties", //MISSING
+DlgAnchorName : "Anchor Name", //MISSING
+DlgAnchorErrorName : "Please type the anchor name", //MISSING
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Not in dictionary", //MISSING
+DlgSpellChangeTo : "Change to", //MISSING
+DlgSpellBtnIgnore : "Ignore", //MISSING
+DlgSpellBtnIgnoreAll : "Ignore All", //MISSING
+DlgSpellBtnReplace : "Replace", //MISSING
+DlgSpellBtnReplaceAll : "Replace All", //MISSING
+DlgSpellBtnUndo : "Undo", //MISSING
+DlgSpellNoSuggestions : "- No suggestions -", //MISSING
+DlgSpellProgress : "Spell check in progress...", //MISSING
+DlgSpellNoMispell : "Spell check complete: No misspellings found", //MISSING
+DlgSpellNoChanges : "Spell check complete: No words changed", //MISSING
+DlgSpellOneChange : "Spell check complete: One word changed", //MISSING
+DlgSpellManyChanges : "Spell check complete: %1 words changed", //MISSING
+
+IeSpellDownload : "Spell checker not installed. Do you want to download it now?", //MISSING
+
+// Button Dialog
+DlgButtonText : "Text (Value)", //MISSING
+DlgButtonType : "Type", //MISSING
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Name", //MISSING
+DlgCheckboxValue : "Value", //MISSING
+DlgCheckboxSelected : "Selected", //MISSING
+
+// Form Dialog
+DlgFormName : "Name", //MISSING
+DlgFormAction : "Action", //MISSING
+DlgFormMethod : "Method", //MISSING
+
+// Select Field Dialog
+DlgSelectName : "Name", //MISSING
+DlgSelectValue : "Value", //MISSING
+DlgSelectSize : "Size", //MISSING
+DlgSelectLines : "lines", //MISSING
+DlgSelectChkMulti : "Allow multiple selections", //MISSING
+DlgSelectOpAvail : "Available Options", //MISSING
+DlgSelectOpText : "Text", //MISSING
+DlgSelectOpValue : "Value", //MISSING
+DlgSelectBtnAdd : "Add", //MISSING
+DlgSelectBtnModify : "Modify", //MISSING
+DlgSelectBtnUp : "Up", //MISSING
+DlgSelectBtnDown : "Down", //MISSING
+DlgSelectBtnSetValue : "Set as selected value", //MISSING
+DlgSelectBtnDelete : "Delete", //MISSING
+
+// Textarea Dialog
+DlgTextareaName : "Name", //MISSING
+DlgTextareaCols : "Columns", //MISSING
+DlgTextareaRows : "Rows", //MISSING
+
+// Text Field Dialog
+DlgTextName : "Name", //MISSING
+DlgTextValue : "Value", //MISSING
+DlgTextCharWidth : "Character Width", //MISSING
+DlgTextMaxChars : "Maximum Characters", //MISSING
+DlgTextType : "Type", //MISSING
+DlgTextTypeText : "Text", //MISSING
+DlgTextTypePass : "Password", //MISSING
+
+// Hidden Field Dialog
+DlgHiddenName : "Name", //MISSING
+DlgHiddenValue : "Value", //MISSING
+
+// Bulleted List Dialog
+BulletedListProp : "Bulleted List Properties", //MISSING
+NumberedListProp : "Numbered List Properties", //MISSING
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Type", //MISSING
+DlgLstTypeCircle : "Circle", //MISSING
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "Square", //MISSING
+DlgLstTypeNumbers : "Numbers (1, 2, 3)", //MISSING
+DlgLstTypeLCase : "Lowercase Letters (a, b, c)", //MISSING
+DlgLstTypeUCase : "Uppercase Letters (A, B, C)", //MISSING
+DlgLstTypeSRoman : "Small Roman Numerals (i, ii, iii)", //MISSING
+DlgLstTypeLRoman : "Large Roman Numerals (I, II, III)", //MISSING
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General", //MISSING
+DlgDocBackTab : "Background", //MISSING
+DlgDocColorsTab : "Colors and Margins", //MISSING
+DlgDocMetaTab : "Meta Data", //MISSING
+
+DlgDocPageTitle : "Page Title", //MISSING
+DlgDocLangDir : "Language Direction", //MISSING
+DlgDocLangDirLTR : "Left to Right (LTR)", //MISSING
+DlgDocLangDirRTL : "Right to Left (RTL)", //MISSING
+DlgDocLangCode : "Language Code", //MISSING
+DlgDocCharSet : "Character Set Encoding", //MISSING
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Other Character Set Encoding", //MISSING
+
+DlgDocDocType : "Document Type Heading", //MISSING
+DlgDocDocTypeOther : "Other Document Type Heading", //MISSING
+DlgDocIncXHTML : "Include XHTML Declarations", //MISSING
+DlgDocBgColor : "Background Color", //MISSING
+DlgDocBgImage : "Background Image URL", //MISSING
+DlgDocBgNoScroll : "Nonscrolling Background", //MISSING
+DlgDocCText : "Text", //MISSING
+DlgDocCLink : "Link", //MISSING
+DlgDocCVisited : "Visited Link", //MISSING
+DlgDocCActive : "Active Link", //MISSING
+DlgDocMargins : "Page Margins", //MISSING
+DlgDocMaTop : "Top", //MISSING
+DlgDocMaLeft : "Left", //MISSING
+DlgDocMaRight : "Right", //MISSING
+DlgDocMaBottom : "Bottom", //MISSING
+DlgDocMeIndex : "Document Indexing Keywords (comma separated)", //MISSING
+DlgDocMeDescr : "Document Description", //MISSING
+DlgDocMeAuthor : "Author", //MISSING
+DlgDocMeCopy : "Copyright", //MISSING
+DlgDocPreview : "Preview", //MISSING
+
+// Templates Dialog
+Templates : "Templates", //MISSING
+DlgTemplatesTitle : "Content Templates", //MISSING
+DlgTemplatesSelMsg : "Please select the template to open in the editor<br />(the actual contents will be lost):", //MISSING
+DlgTemplatesLoading : "Loading templates list. Please wait...", //MISSING
+DlgTemplatesNoTpl : "(No templates defined)", //MISSING
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "About", //MISSING
+DlgAboutBrowserInfoTab : "Browser Info", //MISSING
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "Хувилбар",
+DlgAboutInfo : "ÐœÑдÑÑллÑÑÑ€ туÑлах"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/ms.js b/httemplate/elements/fckeditor/editor/lang/ms.js
new file mode 100644
index 0000000..efe0529
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/ms.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Malay language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Collapse Toolbar",
+ToolbarExpand : "Expand Toolbar",
+
+// Toolbar Items and Context Menu
+Save : "Simpan",
+NewPage : "Helaian Baru",
+Preview : "Prebiu",
+Cut : "Potong",
+Copy : "Salin",
+Paste : "Tampal",
+PasteText : "Tampal sebagai Text Biasa",
+PasteWord : "Tampal dari Word",
+Print : "Cetak",
+SelectAll : "Pilih Semua",
+RemoveFormat : "Buang Format",
+InsertLinkLbl : "Sambungan",
+InsertLink : "Masukkan/Sunting Sambungan",
+RemoveLink : "Buang Sambungan",
+Anchor : "Masukkan/Sunting Pautan",
+InsertImageLbl : "Gambar",
+InsertImage : "Masukkan/Sunting Gambar",
+InsertFlashLbl : "Flash", //MISSING
+InsertFlash : "Insert/Edit Flash", //MISSING
+InsertTableLbl : "Jadual",
+InsertTable : "Masukkan/Sunting Jadual",
+InsertLineLbl : "Garisan",
+InsertLine : "Masukkan Garisan Membujur",
+InsertSpecialCharLbl: "Huruf Istimewa",
+InsertSpecialChar : "Masukkan Huruf Istimewa",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Masukkan Smiley",
+About : "Tentang FCKeditor",
+Bold : "Bold",
+Italic : "Italic",
+Underline : "Underline",
+StrikeThrough : "Strike Through",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Jajaran Kiri",
+CenterJustify : "Jajaran Tengah",
+RightJustify : "Jajaran Kanan",
+BlockJustify : "Jajaran Blok",
+DecreaseIndent : "Kurangkan Inden",
+IncreaseIndent : "Tambahkan Inden",
+Undo : "Batalkan",
+Redo : "Ulangkan",
+NumberedListLbl : "Senarai bernombor",
+NumberedList : "Masukkan/Sunting Senarai bernombor",
+BulletedListLbl : "Senarai tidak bernombor",
+BulletedList : "Masukkan/Sunting Senarai tidak bernombor",
+ShowTableBorders : "Tunjukkan Border Jadual",
+ShowDetails : "Tunjukkan Butiran",
+Style : "Stail",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "Saiz",
+TextColor : "Warna Text",
+BGColor : "Warna Latarbelakang",
+Source : "Sumber",
+Find : "Cari",
+Replace : "Ganti",
+SpellCheck : "Semak Ejaan",
+UniversalKeyboard : "Papan Kekunci Universal",
+PageBreakLbl : "Page Break", //MISSING
+PageBreak : "Insert Page Break", //MISSING
+
+Form : "Borang",
+Checkbox : "Checkbox",
+RadioButton : "Butang Radio",
+TextField : "Text Field",
+Textarea : "Textarea",
+HiddenField : "Field Tersembunyi",
+Button : "Butang",
+SelectionField : "Field Pilihan",
+ImageButton : "Butang Bergambar",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Sunting Sambungan",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Masukkan Baris",
+DeleteRows : "Buangkan Baris",
+InsertColumn : "Masukkan Lajur",
+DeleteColumns : "Buangkan Lajur",
+InsertCell : "Masukkan Sel",
+DeleteCells : "Buangkan Sel-sel",
+MergeCells : "Cantumkan Sel-sel",
+SplitCell : "Bahagikan Sel",
+TableDelete : "Delete Table", //MISSING
+CellProperties : "Ciri-ciri Sel",
+TableProperties : "Ciri-ciri Jadual",
+ImageProperties : "Ciri-ciri Gambar",
+FlashProperties : "Flash Properties", //MISSING
+
+AnchorProp : "Ciri-ciri Pautan",
+ButtonProp : "Ciri-ciri Butang",
+CheckboxProp : "Ciri-ciri Checkbox",
+HiddenFieldProp : "Ciri-ciri Field Tersembunyi",
+RadioButtonProp : "Ciri-ciri Butang Radio",
+ImageButtonProp : "Ciri-ciri Butang Bergambar",
+TextFieldProp : "Ciri-ciri Text Field",
+SelectionFieldProp : "Ciri-ciri Selection Field",
+TextareaProp : "Ciri-ciri Textarea",
+FormProp : "Ciri-ciri Borang",
+
+FontFormats : "Normal;Telah Diformat;Alamat;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Perenggan (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Memproses XHTML. Sila tunggu...",
+Done : "Siap",
+PasteWordConfirm : "Text yang anda hendak tampal adalah berasal dari Word. Adakah anda mahu membuang semua format Word sebelum tampal ke dalam text?",
+NotCompatiblePaste : "Arahan ini bole dilakukan jika anda mempuunyai Internet Explorer version 5.5 atau yang lebih tinggi. Adakah anda hendak tampal text tanpa membuang format Word?",
+UnknownToolbarItem : "Toolbar item tidak diketahui\"%1\"",
+UnknownCommand : "Arahan tidak diketahui \"%1\"",
+NotImplemented : "Arahan tidak terdapat didalam sistem",
+UnknownToolbarSet : "Set toolbar \"%1\" tidak wujud",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Batal",
+DlgBtnClose : "Tutup",
+DlgBtnBrowseServer : "Browse Server",
+DlgAdvancedTag : "Advanced",
+DlgOpOther : "<Lain-lain>",
+DlgInfoTab : "Info", //MISSING
+DlgAlertUrl : "Please insert the URL", //MISSING
+
+// General Dialogs Labels
+DlgGenNotSet : "<tidak di set>",
+DlgGenId : "Id",
+DlgGenLangDir : "Arah Tulisan",
+DlgGenLangDirLtr : "Kiri ke Kanan (LTR)",
+DlgGenLangDirRtl : "Kanan ke Kiri (RTL)",
+DlgGenLangCode : "Kod Bahasa",
+DlgGenAccessKey : "Kunci Akses",
+DlgGenName : "Nama",
+DlgGenTabIndex : "Indeks Tab ",
+DlgGenLongDescr : "Butiran Panjang URL",
+DlgGenClass : "Kelas-kelas Stylesheet",
+DlgGenTitle : "Tajuk Makluman",
+DlgGenContType : "Jenis Kandungan Makluman",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Stail",
+
+// Image Dialog
+DlgImgTitle : "Ciri-ciri Imej",
+DlgImgInfoTab : "Info Imej",
+DlgImgBtnUpload : "Hantar ke Server",
+DlgImgURL : "URL",
+DlgImgUpload : "Muat Naik",
+DlgImgAlt : "Text Alternatif",
+DlgImgWidth : "Lebar",
+DlgImgHeight : "Tinggi",
+DlgImgLockRatio : "Tetapkan Nisbah",
+DlgBtnResetSize : "Saiz Set Semula",
+DlgImgBorder : "Border",
+DlgImgHSpace : "Ruang Melintang",
+DlgImgVSpace : "Ruang Menegak",
+DlgImgAlign : "Jajaran",
+DlgImgAlignLeft : "Kiri",
+DlgImgAlignAbsBottom: "Bawah Mutlak",
+DlgImgAlignAbsMiddle: "Pertengahan Mutlak",
+DlgImgAlignBaseline : "Garis Dasar",
+DlgImgAlignBottom : "Bawah",
+DlgImgAlignMiddle : "Pertengahan",
+DlgImgAlignRight : "Kanan",
+DlgImgAlignTextTop : "Atas Text",
+DlgImgAlignTop : "Atas",
+DlgImgPreview : "Prebiu",
+DlgImgAlertUrl : "Sila taip URL untuk fail gambar",
+DlgImgLinkTab : "Sambungan",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Properties", //MISSING
+DlgFlashChkPlay : "Auto Play", //MISSING
+DlgFlashChkLoop : "Loop", //MISSING
+DlgFlashChkMenu : "Enable Flash Menu", //MISSING
+DlgFlashScale : "Scale", //MISSING
+DlgFlashScaleAll : "Show all", //MISSING
+DlgFlashScaleNoBorder : "No Border", //MISSING
+DlgFlashScaleFit : "Exact Fit", //MISSING
+
+// Link Dialog
+DlgLnkWindowTitle : "Sambungan",
+DlgLnkInfoTab : "Butiran Sambungan",
+DlgLnkTargetTab : "Sasaran",
+
+DlgLnkType : "Jenis Sambungan",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Pautan dalam muka surat ini",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<lain-lain>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Sila pilih pautan",
+DlgLnkAnchorByName : "dengan menggunakan nama pautan",
+DlgLnkAnchorById : "dengan menggunakan ID elemen",
+DlgLnkNoAnchors : "<Tiada pautan terdapat dalam dokumen ini>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Alamat E-Mail",
+DlgLnkEMailSubject : "Subjek Mesej",
+DlgLnkEMailBody : "Isi Kandungan Mesej",
+DlgLnkUpload : "Muat Naik",
+DlgLnkBtnUpload : "Hantar ke Server",
+
+DlgLnkTarget : "Sasaran",
+DlgLnkTargetFrame : "<bingkai>",
+DlgLnkTargetPopup : "<tetingkap popup>",
+DlgLnkTargetBlank : "Tetingkap Baru (_blank)",
+DlgLnkTargetParent : "Tetingkap Parent (_parent)",
+DlgLnkTargetSelf : "Tetingkap yang Sama (_self)",
+DlgLnkTargetTop : "Tetingkap yang paling atas (_top)",
+DlgLnkTargetFrameName : "Nama Bingkai Sasaran",
+DlgLnkPopWinName : "Nama Tetingkap Popup",
+DlgLnkPopWinFeat : "Ciri Tetingkap Popup",
+DlgLnkPopResize : "Saiz bolehubah",
+DlgLnkPopLocation : "Bar Lokasi",
+DlgLnkPopMenu : "Bar Menu",
+DlgLnkPopScroll : "Bar-bar skrol",
+DlgLnkPopStatus : "Bar Status",
+DlgLnkPopToolbar : "Toolbar",
+DlgLnkPopFullScrn : "Skrin Penuh (IE)",
+DlgLnkPopDependent : "Bergantungan (Netscape)",
+DlgLnkPopWidth : "Lebar",
+DlgLnkPopHeight : "Tinggi",
+DlgLnkPopLeft : "Posisi Kiri",
+DlgLnkPopTop : "Posisi Atas",
+
+DlnLnkMsgNoUrl : "Sila taip sambungan URL",
+DlnLnkMsgNoEMail : "Sila taip alamat e-mail",
+DlnLnkMsgNoAnchor : "Sila pilih pautan berkenaaan",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Pilihan Warna",
+DlgColorBtnClear : "Nyahwarna",
+DlgColorHighlight : "Terang",
+DlgColorSelected : "Dipilih",
+
+// Smiley Dialog
+DlgSmileyTitle : "Masukkan Smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Sila pilih huruf istimewa",
+
+// Table Dialog
+DlgTableTitle : "Ciri-ciri Jadual",
+DlgTableRows : "Barisan",
+DlgTableColumns : "Jaluran",
+DlgTableBorder : "Saiz Border",
+DlgTableAlign : "Penjajaran",
+DlgTableAlignNotSet : "<Tidak diset>",
+DlgTableAlignLeft : "Kiri",
+DlgTableAlignCenter : "Tengah",
+DlgTableAlignRight : "Kanan",
+DlgTableWidth : "Lebar",
+DlgTableWidthPx : "piksel-piksel",
+DlgTableWidthPc : "peratus",
+DlgTableHeight : "Tinggi",
+DlgTableCellSpace : "Ruangan Antara Sel",
+DlgTableCellPad : "Tambahan Ruang Sel",
+DlgTableCaption : "Keterangan",
+DlgTableSummary : "Summary", //MISSING
+
+// Table Cell Dialog
+DlgCellTitle : "Ciri-ciri Sel",
+DlgCellWidth : "Lebar",
+DlgCellWidthPx : "piksel-piksel",
+DlgCellWidthPc : "peratus",
+DlgCellHeight : "Tinggi",
+DlgCellWordWrap : "Mengulung Perkataan",
+DlgCellWordWrapNotSet : "<Tidak diset>",
+DlgCellWordWrapYes : "Ya",
+DlgCellWordWrapNo : "Tidak",
+DlgCellHorAlign : "Jajaran Membujur",
+DlgCellHorAlignNotSet : "<Tidak diset>",
+DlgCellHorAlignLeft : "Kiri",
+DlgCellHorAlignCenter : "Tengah",
+DlgCellHorAlignRight: "Kanan",
+DlgCellVerAlign : "Jajaran Menegak",
+DlgCellVerAlignNotSet : "<Tidak diset>",
+DlgCellVerAlignTop : "Atas",
+DlgCellVerAlignMiddle : "Tengah",
+DlgCellVerAlignBottom : "Bawah",
+DlgCellVerAlignBaseline : "Garis Dasar",
+DlgCellRowSpan : "Penggunaan Baris",
+DlgCellCollSpan : "Penggunaan Lajur",
+DlgCellBackColor : "Warna Latarbelakang",
+DlgCellBorderColor : "Warna Border",
+DlgCellBtnSelect : "Pilih...",
+
+// Find Dialog
+DlgFindTitle : "Carian",
+DlgFindFindBtn : "Cari",
+DlgFindNotFoundMsg : "Text yang dicari tidak dijumpai.",
+
+// Replace Dialog
+DlgReplaceTitle : "Gantian",
+DlgReplaceFindLbl : "Perkataan yang dicari:",
+DlgReplaceReplaceLbl : "Diganti dengan:",
+DlgReplaceCaseChk : "Padanan case huruf",
+DlgReplaceReplaceBtn : "Ganti",
+DlgReplaceReplAllBtn : "Ganti semua",
+DlgReplaceWordChk : "Padana Keseluruhan perkataan",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Keselamatan perisian browser anda tidak membenarkan operasi suntingan text/imej. Sila gunakan papan kekunci (Ctrl+X).",
+PasteErrorCopy : "Keselamatan perisian browser anda tidak membenarkan operasi salinan text/imej. Sila gunakan papan kekunci (Ctrl+C).",
+
+PasteAsText : "Tampal sebagai text biasa",
+PasteFromWord : "Tampal dari perisian \"Word\"",
+
+DlgPasteMsg2 : "Please paste inside the following box using the keyboard (<strong>Ctrl+V</strong>) and hit <strong>OK</strong>.", //MISSING
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignore Font Face definitions", //MISSING
+DlgPasteRemoveStyles : "Remove Styles definitions", //MISSING
+DlgPasteCleanBox : "Clean Up Box", //MISSING
+
+// Color Picker
+ColorAutomatic : "Otomatik",
+ColorMoreColors : "Warna lain-lain...",
+
+// Document Properties
+DocProps : "Ciri-ciri dokumen",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ciri-ciri Pautan",
+DlgAnchorName : "Nama Pautan",
+DlgAnchorErrorName : "Sila taip nama pautan",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Tidak terdapat didalam kamus",
+DlgSpellChangeTo : "Tukarkan kepada",
+DlgSpellBtnIgnore : "Biar",
+DlgSpellBtnIgnoreAll : "Biarkan semua",
+DlgSpellBtnReplace : "Ganti",
+DlgSpellBtnReplaceAll : "Gantikan Semua",
+DlgSpellBtnUndo : "Batalkan",
+DlgSpellNoSuggestions : "- Tiada cadangan -",
+DlgSpellProgress : "Pemeriksaan ejaan sedang diproses...",
+DlgSpellNoMispell : "Pemeriksaan ejaan siap: Tiada salah ejaan",
+DlgSpellNoChanges : "Pemeriksaan ejaan siap: Tiada perkataan diubah",
+DlgSpellOneChange : "Pemeriksaan ejaan siap: Satu perkataan telah diubah",
+DlgSpellManyChanges : "Pemeriksaan ejaan siap: %1 perkataan diubah",
+
+IeSpellDownload : "Pemeriksa ejaan tidak dipasang. Adakah anda mahu muat turun sekarang?",
+
+// Button Dialog
+DlgButtonText : "Teks (Nilai)",
+DlgButtonType : "Jenis",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nama",
+DlgCheckboxValue : "Nilai",
+DlgCheckboxSelected : "Dipilih",
+
+// Form Dialog
+DlgFormName : "Nama",
+DlgFormAction : "Tindakan borang",
+DlgFormMethod : "Cara borang dihantar",
+
+// Select Field Dialog
+DlgSelectName : "Nama",
+DlgSelectValue : "Nilai",
+DlgSelectSize : "Saiz",
+DlgSelectLines : "garisan",
+DlgSelectChkMulti : "Benarkan pilihan pelbagai",
+DlgSelectOpAvail : "Pilihan sediada",
+DlgSelectOpText : "Teks",
+DlgSelectOpValue : "Nilai",
+DlgSelectBtnAdd : "Tambah Pilihan",
+DlgSelectBtnModify : "Ubah Pilihan",
+DlgSelectBtnUp : "Naik ke atas",
+DlgSelectBtnDown : "Turun ke bawah",
+DlgSelectBtnSetValue : "Set sebagai nilai terpilih",
+DlgSelectBtnDelete : "Padam",
+
+// Textarea Dialog
+DlgTextareaName : "Nama",
+DlgTextareaCols : "Lajur",
+DlgTextareaRows : "Baris",
+
+// Text Field Dialog
+DlgTextName : "Nama",
+DlgTextValue : "Nilai",
+DlgTextCharWidth : "Lebar isian",
+DlgTextMaxChars : "Isian Maksimum",
+DlgTextType : "Jenis",
+DlgTextTypeText : "Teks",
+DlgTextTypePass : "Kata Laluan",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nama",
+DlgHiddenValue : "Nilai",
+
+// Bulleted List Dialog
+BulletedListProp : "Ciri-ciri senarai berpeluru",
+NumberedListProp : "Ciri-ciri senarai bernombor",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Jenis",
+DlgLstTypeCircle : "Circle",
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "Square",
+DlgLstTypeNumbers : "Nombor-nombor (1, 2, 3)",
+DlgLstTypeLCase : "Huruf-huruf kecil (a, b, c)",
+DlgLstTypeUCase : "Huruf-huruf besar (A, B, C)",
+DlgLstTypeSRoman : "Nombor Roman Kecil (i, ii, iii)",
+DlgLstTypeLRoman : "Nombor Roman Besar (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Umum",
+DlgDocBackTab : "Latarbelakang",
+DlgDocColorsTab : "Warna dan margin",
+DlgDocMetaTab : "Data Meta",
+
+DlgDocPageTitle : "Tajuk Muka Surat",
+DlgDocLangDir : "Arah Tulisan",
+DlgDocLangDirLTR : "Kiri ke Kanan (LTR)",
+DlgDocLangDirRTL : "Kanan ke Kiri (RTL)",
+DlgDocLangCode : "Kod Bahasa",
+DlgDocCharSet : "Enkod Set Huruf",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Enkod Set Huruf yang Lain",
+
+DlgDocDocType : "Jenis Kepala Dokumen",
+DlgDocDocTypeOther : "Jenis Kepala Dokumen yang Lain",
+DlgDocIncXHTML : "Masukkan pemula kod XHTML",
+DlgDocBgColor : "Warna Latarbelakang",
+DlgDocBgImage : "URL Gambar Latarbelakang",
+DlgDocBgNoScroll : "Imej Latarbelakang tanpa Skrol",
+DlgDocCText : "Teks",
+DlgDocCLink : "Sambungan",
+DlgDocCVisited : "Sambungan telah Dilawati",
+DlgDocCActive : "Sambungan Aktif",
+DlgDocMargins : "Margin Muka Surat",
+DlgDocMaTop : "Atas",
+DlgDocMaLeft : "Kiri",
+DlgDocMaRight : "Kanan",
+DlgDocMaBottom : "Bawah",
+DlgDocMeIndex : "Kata Kunci Indeks Dokumen (dipisahkan oleh koma)",
+DlgDocMeDescr : "Keterangan Dokumen",
+DlgDocMeAuthor : "Penulis",
+DlgDocMeCopy : "Hakcipta",
+DlgDocPreview : "Prebiu",
+
+// Templates Dialog
+Templates : "Templat",
+DlgTemplatesTitle : "Templat Kandungan",
+DlgTemplatesSelMsg : "Sila pilih templat untuk dibuka oleh editor<br>(kandungan sebenar akan hilang):",
+DlgTemplatesLoading : "Senarai Templat sedang diproses. Sila Tunggu...",
+DlgTemplatesNoTpl : "(Tiada Templat Disimpan)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Tentang",
+DlgAboutBrowserInfoTab : "Maklumat Perisian Browser",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "versi",
+DlgAboutInfo : "Untuk maklumat lanjut sila pergi ke"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/nb.js b/httemplate/elements/fckeditor/editor/lang/nb.js
new file mode 100644
index 0000000..ae9fa64
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/nb.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Norwegian Bokmål language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Skjul verktøylinje",
+ToolbarExpand : "Vis verktøylinje",
+
+// Toolbar Items and Context Menu
+Save : "Lagre",
+NewPage : "Ny Side",
+Preview : "Forhåndsvis",
+Cut : "Klipp ut",
+Copy : "Kopier",
+Paste : "Lim inn",
+PasteText : "Lim inn som ren tekst",
+PasteWord : "Lim inn fra Word",
+Print : "Skriv ut",
+SelectAll : "Merk alt",
+RemoveFormat : "Fjern format",
+InsertLinkLbl : "Lenke",
+InsertLink : "Sett inn/Rediger lenke",
+RemoveLink : "Fjern lenke",
+Anchor : "Sett inn/Rediger anker",
+InsertImageLbl : "Bilde",
+InsertImage : "Sett inn/Rediger bilde",
+InsertFlashLbl : "Flash",
+InsertFlash : "Sett inn/Rediger Flash",
+InsertTableLbl : "Tabell",
+InsertTable : "Sett inn/Rediger tabell",
+InsertLineLbl : "Linje",
+InsertLine : "Sett inn horisontal linje",
+InsertSpecialCharLbl: "Spesielt tegn",
+InsertSpecialChar : "Sett inn spesielt tegn",
+InsertSmileyLbl : "Smil",
+InsertSmiley : "Sett inn smil",
+About : "Om FCKeditor",
+Bold : "Fet",
+Italic : "Kursiv",
+Underline : "Understrek",
+StrikeThrough : "Gjennomstrek",
+Subscript : "Senket skrift",
+Superscript : "Hevet skrift",
+LeftJustify : "Venstrejuster",
+CenterJustify : "Midtjuster",
+RightJustify : "Høyrejuster",
+BlockJustify : "Blokkjuster",
+DecreaseIndent : "Senk nivå",
+IncreaseIndent : "Øk nivå",
+Undo : "Angre",
+Redo : "Gjør om",
+NumberedListLbl : "Numrert liste",
+NumberedList : "Sett inn/Fjern numrert liste",
+BulletedListLbl : "Uordnet liste",
+BulletedList : "Sett inn/Fjern uordnet liste",
+ShowTableBorders : "Vis tabellrammer",
+ShowDetails : "Vis detaljer",
+Style : "Stil",
+FontFormat : "Format",
+Font : "Skrift",
+FontSize : "Størrelse",
+TextColor : "Tekstfarge",
+BGColor : "Bakgrunnsfarge",
+Source : "Kilde",
+Find : "Finn",
+Replace : "Erstatt",
+SpellCheck : "Stavekontroll",
+UniversalKeyboard : "Universelt tastatur",
+PageBreakLbl : "Sideskift",
+PageBreak : "Sett inn sideskift",
+
+Form : "Skjema",
+Checkbox : "Sjekkboks",
+RadioButton : "Radioknapp",
+TextField : "Tekstfelt",
+Textarea : "Tekstområde",
+HiddenField : "Skjult felt",
+Button : "Knapp",
+SelectionField : "Dropdown meny",
+ImageButton : "Bildeknapp",
+
+FitWindow : "Maksimer størrelsen på redigeringsverktøyet",
+
+// Context Menu
+EditLink : "Rediger lenke",
+CellCM : "Celle",
+RowCM : "Rader",
+ColumnCM : "Kolonne",
+InsertRow : "Sett inn rad",
+DeleteRows : "Slett rader",
+InsertColumn : "Sett inn kolonne",
+DeleteColumns : "Slett kolonner",
+InsertCell : "Sett inn celle",
+DeleteCells : "Slett celler",
+MergeCells : "Slå sammen celler",
+SplitCell : "Splitt celler",
+TableDelete : "Slett tabell",
+CellProperties : "Celleegenskaper",
+TableProperties : "Tabellegenskaper",
+ImageProperties : "Bildeegenskaper",
+FlashProperties : "Flash Egenskaper",
+
+AnchorProp : "Ankeregenskaper",
+ButtonProp : "Knappegenskaper",
+CheckboxProp : "Sjekkboksegenskaper",
+HiddenFieldProp : "Skjult felt egenskaper",
+RadioButtonProp : "Radioknappegenskaper",
+ImageButtonProp : "Bildeknappegenskaper",
+TextFieldProp : "Tekstfeltegenskaper",
+SelectionFieldProp : "Dropdown menyegenskaper",
+TextareaProp : "Tekstfeltegenskaper",
+FormProp : "Skjemaegenskaper",
+
+FontFormats : "Normal;Formatert;Adresse;Tittel 1;Tittel 2;Tittel 3;Tittel 4;Tittel 5;Tittel 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Lager XHTML. Vennligst vent...",
+Done : "Ferdig",
+PasteWordConfirm : "Teksten du prøver å lime inn ser ut som om den kommer fra word , du bør rense den før du limer inn , vil du gjøre dette?",
+NotCompatiblePaste : "Denne kommandoen er tilgjenglig kun for Internet Explorer version 5.5 eller bedre. Vil du fortsette uten å rense?(Du kan lime inn som ren tekst)",
+UnknownToolbarItem : "Ukjent menyvalg \"%1\"",
+UnknownCommand : "Ukjent kommando \"%1\"",
+NotImplemented : "Kommando ikke ennå implimentert",
+UnknownToolbarSet : "Verktøylinjesett \"%1\" finnes ikke",
+NoActiveX : "Din nettleser's sikkerhetsinstillinger kan begrense noen av funksjonene i redigeringsverktøyet. Du må aktivere \"Kjør ActiveXkontroller og plugins\". Du kan oppleve feil og advarsler om manglende funksjoner",
+BrowseServerBlocked : "Kunne ikke åpne dialogboksen for filarkiv. Pass på at du har slått av popupstoppere.",
+DialogBlocked : "Kunne ikke åpne dialogboksen. Pass på at du har slått av popupstoppere.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Avbryt",
+DlgBtnClose : "Lukk",
+DlgBtnBrowseServer : "Bla igjennom server",
+DlgAdvancedTag : "Avansert",
+DlgOpOther : "<Annet>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Vennligst skriv inn URL'en",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ikke satt>",
+DlgGenId : "Id",
+DlgGenLangDir : "Språkretning",
+DlgGenLangDirLtr : "Venstre til høyre (VTH)",
+DlgGenLangDirRtl : "Høyre til venstre (HTV)",
+DlgGenLangCode : "Språk kode",
+DlgGenAccessKey : "Aksessknapp",
+DlgGenName : "Navn",
+DlgGenTabIndex : "Tab Indeks",
+DlgGenLongDescr : "Utvidet beskrivelse",
+DlgGenClass : "Stilarkklasser",
+DlgGenTitle : "Tittel",
+DlgGenContType : "Type",
+DlgGenLinkCharset : "Lenket språkkart",
+DlgGenStyle : "Stil",
+
+// Image Dialog
+DlgImgTitle : "Bildeegenskaper",
+DlgImgInfoTab : "Bildeinformasjon",
+DlgImgBtnUpload : "Send det til serveren",
+DlgImgURL : "URL",
+DlgImgUpload : "Last opp",
+DlgImgAlt : "Alternativ tekst",
+DlgImgWidth : "Bredde",
+DlgImgHeight : "Høyde",
+DlgImgLockRatio : "LÃ¥s forhold",
+DlgBtnResetSize : "Tilbakestill størrelse",
+DlgImgBorder : "Ramme",
+DlgImgHSpace : "HMarg",
+DlgImgVSpace : "VMarg",
+DlgImgAlign : "Juster",
+DlgImgAlignLeft : "Venstre",
+DlgImgAlignAbsBottom: "Abs bunn",
+DlgImgAlignAbsMiddle: "Abs midten",
+DlgImgAlignBaseline : "Bunnlinje",
+DlgImgAlignBottom : "Bunn",
+DlgImgAlignMiddle : "Midten",
+DlgImgAlignRight : "Høyre",
+DlgImgAlignTextTop : "Tekst topp",
+DlgImgAlignTop : "Topp",
+DlgImgPreview : "Forhåndsvis",
+DlgImgAlertUrl : "Vennligst skriv bildeurlen",
+DlgImgLinkTab : "Lenke",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Egenskaper",
+DlgFlashChkPlay : "Auto Spill",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Slå på Flash meny",
+DlgFlashScale : "Skaler",
+DlgFlashScaleAll : "Vis alt",
+DlgFlashScaleNoBorder : "Ingen ramme",
+DlgFlashScaleFit : "Skaler til å passeExact Fit",
+
+// Link Dialog
+DlgLnkWindowTitle : "Lenke",
+DlgLnkInfoTab : "Lenkeinfo",
+DlgLnkTargetTab : "MÃ¥l",
+
+DlgLnkType : "Lenketype",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Lenke til bokmerke i teksten",
+DlgLnkTypeEMail : "E-Post",
+DlgLnkProto : "Protokoll",
+DlgLnkProtoOther : "<annet>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Velg ett anker",
+DlgLnkAnchorByName : "Anker etter navn",
+DlgLnkAnchorById : "Element etter ID",
+DlgLnkNoAnchors : "<Ingen anker i dokumentet>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Post Addresse",
+DlgLnkEMailSubject : "Meldingsemne",
+DlgLnkEMailBody : "Melding",
+DlgLnkUpload : "Last opp",
+DlgLnkBtnUpload : "Send til server",
+
+DlgLnkTarget : "MÃ¥l",
+DlgLnkTargetFrame : "<ramme>",
+DlgLnkTargetPopup : "<popup vindu>",
+DlgLnkTargetBlank : "Nytt vindu (_blank)",
+DlgLnkTargetParent : "Foreldre vindu (_parent)",
+DlgLnkTargetSelf : "Samme vindu (_self)",
+DlgLnkTargetTop : "Hele vindu (_top)",
+DlgLnkTargetFrameName : "MÃ¥lramme",
+DlgLnkPopWinName : "Popup vindus navn",
+DlgLnkPopWinFeat : "Popup vindus egenskaper",
+DlgLnkPopResize : "Endre størrelse",
+DlgLnkPopLocation : "Adresselinje",
+DlgLnkPopMenu : "Menylinje",
+DlgLnkPopScroll : "Scrollbar",
+DlgLnkPopStatus : "Statuslinje",
+DlgLnkPopToolbar : "Verktøylinje",
+DlgLnkPopFullScrn : "Full skjerm (IE)",
+DlgLnkPopDependent : "Avhenging (Netscape)",
+DlgLnkPopWidth : "Bredde",
+DlgLnkPopHeight : "Høyde",
+DlgLnkPopLeft : "Venstre posisjon",
+DlgLnkPopTop : "Topp posisjon",
+
+DlnLnkMsgNoUrl : "Vennligst skriv inn lenkens url",
+DlnLnkMsgNoEMail : "Vennligst skriv inn e-postadressen",
+DlnLnkMsgNoAnchor : "Vennligst velg ett anker",
+DlnLnkMsgInvPopName : "Popup vinduets navn må begynne med en bokstav, og kan ikke inneholde mellomrom",
+
+// Color Dialog
+DlgColorTitle : "Velg farge",
+DlgColorBtnClear : "Tøm",
+DlgColorHighlight : "Marker",
+DlgColorSelected : "Velg",
+
+// Smiley Dialog
+DlgSmileyTitle : "Sett inn smil",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Velg spesielt tegn",
+
+// Table Dialog
+DlgTableTitle : "Tabellegenskaper",
+DlgTableRows : "Rader",
+DlgTableColumns : "Kolonner",
+DlgTableBorder : "Rammestørrelse",
+DlgTableAlign : "Justering",
+DlgTableAlignNotSet : "<Ikke satt>",
+DlgTableAlignLeft : "Venstre",
+DlgTableAlignCenter : "Midtjuster",
+DlgTableAlignRight : "Høyre",
+DlgTableWidth : "Bredde",
+DlgTableWidthPx : "pixler",
+DlgTableWidthPc : "prosent",
+DlgTableHeight : "Høyde",
+DlgTableCellSpace : "Celle marg",
+DlgTableCellPad : "Celle polstring",
+DlgTableCaption : "Tittel",
+DlgTableSummary : "Sammendrag",
+
+// Table Cell Dialog
+DlgCellTitle : "Celle egenskaper",
+DlgCellWidth : "Bredde",
+DlgCellWidthPx : "pixeler",
+DlgCellWidthPc : "prosent",
+DlgCellHeight : "Høyde",
+DlgCellWordWrap : "Tekstbrytning",
+DlgCellWordWrapNotSet : "<Ikke satt>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nei",
+DlgCellHorAlign : "Horisontal justering",
+DlgCellHorAlignNotSet : "<Ikke satt>",
+DlgCellHorAlignLeft : "Venstre",
+DlgCellHorAlignCenter : "Midtjuster",
+DlgCellHorAlignRight: "Høyre",
+DlgCellVerAlign : "Vertikal justering",
+DlgCellVerAlignNotSet : "<Ikke satt>",
+DlgCellVerAlignTop : "Topp",
+DlgCellVerAlignMiddle : "Midten",
+DlgCellVerAlignBottom : "Bunn",
+DlgCellVerAlignBaseline : "Bunnlinje",
+DlgCellRowSpan : "Radspenn",
+DlgCellCollSpan : "Kolonnespenn",
+DlgCellBackColor : "Bakgrunnsfarge",
+DlgCellBorderColor : "Rammefarge",
+DlgCellBtnSelect : "Velg...",
+
+// Find Dialog
+DlgFindTitle : "Finn",
+DlgFindFindBtn : "Finn",
+DlgFindNotFoundMsg : "Den spesifiserte teksten ble ikke funnet.",
+
+// Replace Dialog
+DlgReplaceTitle : "Erstatt",
+DlgReplaceFindLbl : "Finn hva:",
+DlgReplaceReplaceLbl : "Erstatt med:",
+DlgReplaceCaseChk : "Riktig case",
+DlgReplaceReplaceBtn : "Erstatt",
+DlgReplaceReplAllBtn : "Erstatt alle",
+DlgReplaceWordChk : "Finn hele ordet",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Din nettlesers sikkerhetsinstillinger tillater ikke automatisk klipping av tekst. Vennligst brukt snareveien (Ctrl+X).",
+PasteErrorCopy : "Din nettlesers sikkerhetsinstillinger tillater ikke automatisk kopiering av tekst. Vennligst brukt snareveien (Ctrl+C).",
+
+PasteAsText : "Lim inn som ren tekst",
+PasteFromWord : "Lim inn fra word",
+
+DlgPasteMsg2 : "Vennligst lim inn i den følgende boksen med tastaturet (<STRONG>Ctrl+V</STRONG>) og trykk <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Fjern skrifttyper",
+DlgPasteRemoveStyles : "Fjern stildefinisjoner",
+DlgPasteCleanBox : "Tøm boksen",
+
+// Color Picker
+ColorAutomatic : "Automatisk",
+ColorMoreColors : "Flere farger...",
+
+// Document Properties
+DocProps : "Dokumentegenskaper",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ankeregenskaper",
+DlgAnchorName : "Ankernavn",
+DlgAnchorErrorName : "Vennligst skriv inn ankernavnet",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ikke i ordboken",
+DlgSpellChangeTo : "Endre til",
+DlgSpellBtnIgnore : "Ignorer",
+DlgSpellBtnIgnoreAll : "Ignorer alle",
+DlgSpellBtnReplace : "Erstatt",
+DlgSpellBtnReplaceAll : "Erstatt alle",
+DlgSpellBtnUndo : "Angre",
+DlgSpellNoSuggestions : "- ingen forslag -",
+DlgSpellProgress : "Stavekontroll pågår...",
+DlgSpellNoMispell : "Stavekontroll fullført: ingen feilstavinger funnet",
+DlgSpellNoChanges : "Stavekontroll fullført: ingen ord endret",
+DlgSpellOneChange : "Stavekontroll fullført: Ett ord endret",
+DlgSpellManyChanges : "Stavekontroll fullført: %1 ord endret",
+
+IeSpellDownload : "Stavekontroll ikke installert, vil du laste den ned nå?",
+
+// Button Dialog
+DlgButtonText : "Tekst",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Knapp",
+DlgButtonTypeSbm : "Send",
+DlgButtonTypeRst : "Nullstill",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Navn",
+DlgCheckboxValue : "Verdi",
+DlgCheckboxSelected : "Valgt",
+
+// Form Dialog
+DlgFormName : "Navn",
+DlgFormAction : "Handling",
+DlgFormMethod : "Metode",
+
+// Select Field Dialog
+DlgSelectName : "Navn",
+DlgSelectValue : "Verdi",
+DlgSelectSize : "Størrelse",
+DlgSelectLines : "Linjer",
+DlgSelectChkMulti : "Tillat flervalg",
+DlgSelectOpAvail : "Tilgjenglige alternativer",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Verdi",
+DlgSelectBtnAdd : "Legg til",
+DlgSelectBtnModify : "Endre",
+DlgSelectBtnUp : "Opp",
+DlgSelectBtnDown : "Ned",
+DlgSelectBtnSetValue : "Sett som valgt",
+DlgSelectBtnDelete : "Slett",
+
+// Textarea Dialog
+DlgTextareaName : "Navn",
+DlgTextareaCols : "Kolonner",
+DlgTextareaRows : "Rader",
+
+// Text Field Dialog
+DlgTextName : "Navn",
+DlgTextValue : "verdi",
+DlgTextCharWidth : "Tegnbredde",
+DlgTextMaxChars : "Maks antall tegn",
+DlgTextType : "Type",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Passord",
+
+// Hidden Field Dialog
+DlgHiddenName : "Navn",
+DlgHiddenValue : "Verdi",
+
+// Bulleted List Dialog
+BulletedListProp : "Uordnet listeegenskaper",
+NumberedListProp : "Ordnet listeegenskaper",
+DlgLstStart : "Start",
+DlgLstType : "Type",
+DlgLstTypeCircle : "Sirkel",
+DlgLstTypeDisc : "Hel sirkel",
+DlgLstTypeSquare : "Firkant",
+DlgLstTypeNumbers : "Numre(1, 2, 3)",
+DlgLstTypeLCase : "Små bokstaver (a, b, c)",
+DlgLstTypeUCase : "Store bokstaver(A, B, C)",
+DlgLstTypeSRoman : "Små romerske tall(i, ii, iii)",
+DlgLstTypeLRoman : "Store romerske tall(I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Generalt",
+DlgDocBackTab : "Bakgrunn",
+DlgDocColorsTab : "Farger og marginer",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Sidetittel",
+DlgDocLangDir : "Språkretning",
+DlgDocLangDirLTR : "Venstre til høyre (LTR)",
+DlgDocLangDirRTL : "Høyre til venstre (RTL)",
+DlgDocLangCode : "Språkkode",
+DlgDocCharSet : "Tegnsett",
+DlgDocCharSetCE : "Sentraleuropeisk",
+DlgDocCharSetCT : "Tradisonell kinesisk(Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Gresk",
+DlgDocCharSetJP : "Japansk",
+DlgDocCharSetKR : "Koreansk",
+DlgDocCharSetTR : "Tyrkisk",
+DlgDocCharSetUN : "Unikode (UTF-8)",
+DlgDocCharSetWE : "Vesteuropeisk",
+DlgDocCharSetOther : "Annet tegnsett",
+
+DlgDocDocType : "Dokumenttype header",
+DlgDocDocTypeOther : "Annet dokumenttype header",
+DlgDocIncXHTML : "Inkulder XHTML deklarasjon",
+DlgDocBgColor : "Bakgrunnsfarge",
+DlgDocBgImage : "Bakgrunnsbilde url",
+DlgDocBgNoScroll : "Ikke scroll bakgrunnsbilde",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Besøkt lenke",
+DlgDocCActive : "Aktiv lenke",
+DlgDocMargins : "Sidemargin",
+DlgDocMaTop : "Topp",
+DlgDocMaLeft : "Venstre",
+DlgDocMaRight : "Høyre",
+DlgDocMaBottom : "Bunn",
+DlgDocMeIndex : "Dokument nøkkelord (kommaseparert)",
+DlgDocMeDescr : "Dokumentbeskrivelse",
+DlgDocMeAuthor : "Forfatter",
+DlgDocMeCopy : "Kopirett",
+DlgDocPreview : "Forhåndsvising",
+
+// Templates Dialog
+Templates : "Maler",
+DlgTemplatesTitle : "Innholdsmaler",
+DlgTemplatesSelMsg : "Velg malen du vil åpne<br>(innholdet du har skrevet blir tapt!):",
+DlgTemplatesLoading : "Laster malliste. Vennligst vent...",
+DlgTemplatesNoTpl : "(Ingen maler definert)",
+DlgTemplatesReplace : "Erstatt faktisk innold",
+
+// About Dialog
+DlgAboutAboutTab : "Om",
+DlgAboutBrowserInfoTab : "Nettleserinfo",
+DlgAboutLicenseTab : "Lisens",
+DlgAboutVersion : "versjon",
+DlgAboutInfo : "For further information go to" //MISSING
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/nl.js b/httemplate/elements/fckeditor/editor/lang/nl.js
new file mode 100644
index 0000000..f6b26b4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/nl.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Dutch language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Menubalk inklappen",
+ToolbarExpand : "Menubalk uitklappen",
+
+// Toolbar Items and Context Menu
+Save : "Opslaan",
+NewPage : "Nieuwe pagina",
+Preview : "Voorbeeld",
+Cut : "Knippen",
+Copy : "Kopiëren",
+Paste : "Plakken",
+PasteText : "Plakken als platte tekst",
+PasteWord : "Plakken als Word-gegevens",
+Print : "Printen",
+SelectAll : "Alles selecteren",
+RemoveFormat : "Opmaak verwijderen",
+InsertLinkLbl : "Link",
+InsertLink : "Link invoegen/wijzigen",
+RemoveLink : "Link verwijderen",
+Anchor : "Interne link",
+InsertImageLbl : "Afbeelding",
+InsertImage : "Afbeelding invoegen/wijzigen",
+InsertFlashLbl : "Flash",
+InsertFlash : "Flash invoegen/wijzigen",
+InsertTableLbl : "Tabel",
+InsertTable : "Tabel invoegen/wijzigen",
+InsertLineLbl : "Lijn",
+InsertLine : "Invoegen horizontale lijn",
+InsertSpecialCharLbl: "Speciale tekens",
+InsertSpecialChar : "Speciaal teken invoegen",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Smiley invoegen",
+About : "Over FCKeditor",
+Bold : "Vet",
+Italic : "Schuingedrukt",
+Underline : "Onderstreept",
+StrikeThrough : "Doorhalen",
+Subscript : "Subscript",
+Superscript : "Superscript",
+LeftJustify : "Links uitlijnen",
+CenterJustify : "Centreren",
+RightJustify : "Rechts uitlijnen",
+BlockJustify : "Uitvullen",
+DecreaseIndent : "Inspringen verkleinen",
+IncreaseIndent : "Inspringen vergroten",
+Undo : "Ongedaan maken",
+Redo : "Opnieuw uitvoeren",
+NumberedListLbl : "Genummerde lijst",
+NumberedList : "Genummerde lijst invoegen/verwijderen",
+BulletedListLbl : "Opsomming",
+BulletedList : "Opsomming invoegen/verwijderen",
+ShowTableBorders : "Randen tabel weergeven",
+ShowDetails : "Details weergeven",
+Style : "Stijl",
+FontFormat : "Opmaak",
+Font : "Lettertype",
+FontSize : "Grootte",
+TextColor : "Tekstkleur",
+BGColor : "Achtergrondkleur",
+Source : "Code",
+Find : "Zoeken",
+Replace : "Vervangen",
+SpellCheck : "Spellingscontrole",
+UniversalKeyboard : "Universeel toetsenbord",
+PageBreakLbl : "Pagina-einde",
+PageBreak : "Pagina-einde invoegen",
+
+Form : "Formulier",
+Checkbox : "Aanvinkvakje",
+RadioButton : "Selectievakje",
+TextField : "Tekstveld",
+Textarea : "Tekstvak",
+HiddenField : "Verborgen veld",
+Button : "Knop",
+SelectionField : "Selectieveld",
+ImageButton : "Afbeeldingsknop",
+
+FitWindow : "De editor maximaliseren",
+
+// Context Menu
+EditLink : "Link wijzigen",
+CellCM : "Cel",
+RowCM : "Rij",
+ColumnCM : "Kolom",
+InsertRow : "Rij invoegen",
+DeleteRows : "Rijen verwijderen",
+InsertColumn : "Kolom invoegen",
+DeleteColumns : "Kolommen verwijderen",
+InsertCell : "Cel",
+DeleteCells : "Cellen verwijderen",
+MergeCells : "Cellen samenvoegen",
+SplitCell : "Cellen splitsen",
+TableDelete : "Tabel verwijderen",
+CellProperties : "Eigenschappen cel",
+TableProperties : "Eigenschappen tabel",
+ImageProperties : "Eigenschappen afbeelding",
+FlashProperties : "Eigenschappen Flash",
+
+AnchorProp : "Eigenschappen interne link",
+ButtonProp : "Eigenschappen knop",
+CheckboxProp : "Eigenschappen aanvinkvakje",
+HiddenFieldProp : "Eigenschappen verborgen veld",
+RadioButtonProp : "Eigenschappen selectievakje",
+ImageButtonProp : "Eigenschappen afbeeldingsknop",
+TextFieldProp : "Eigenschappen tekstveld",
+SelectionFieldProp : "Eigenschappen selectieveld",
+TextareaProp : "Eigenschappen tekstvak",
+FormProp : "Eigenschappen formulier",
+
+FontFormats : "Normaal;Met opmaak;Adres;Kop 1;Kop 2;Kop 3;Kop 4;Kop 5;Kop 6;Normaal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Bezig met verwerken XHTML. Even geduld aub...",
+Done : "Klaar",
+PasteWordConfirm : "De tekst die je plakte lijkt gekopieerd uit te zijn Word. Wil je de tekst opschonen voordat deze geplakt wordt?",
+NotCompatiblePaste : "Deze opdracht is beschikbaar voor Internet Explorer versie 5.5 of hoger. Wil je plakken zonder op te schonen?",
+UnknownToolbarItem : "Onbekend item op menubalk \"%1\"",
+UnknownCommand : "Onbekende opdrachtnaam: \"%1\"",
+NotImplemented : "Opdracht niet geïmplementeerd.",
+UnknownToolbarSet : "Menubalk \"%1\" bestaat niet.",
+NoActiveX : "De beveilingsinstellingen van je browser zouden sommige functies van de editor kunnen beperken. De optie \"Activeer ActiveX-elementen en plug-ins\" dient ingeschakeld te worden. Het kan zijn dat er nu functies ontbreken of niet werken.",
+BrowseServerBlocked : "De bestandsbrowser kon niet geopend worden. Zorg ervoor dat pop-up-blokkeerders uit staan.",
+DialogBlocked : "Kan het dialoogvenster niet weergeven. Zorg ervoor dat pop-up-blokkeerders uit staan.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Annuleren",
+DlgBtnClose : "Afsluiten",
+DlgBtnBrowseServer : "Bladeren op server",
+DlgAdvancedTag : "Geavanceerd",
+DlgOpOther : "<Anders>",
+DlgInfoTab : "Informatie",
+DlgAlertUrl : "Geef URL op",
+
+// General Dialogs Labels
+DlgGenNotSet : "<niet ingevuld>",
+DlgGenId : "Kenmerk",
+DlgGenLangDir : "Schrijfrichting",
+DlgGenLangDirLtr : "Links naar rechts (LTR)",
+DlgGenLangDirRtl : "Rechts naar links (RTL)",
+DlgGenLangCode : "Taalcode",
+DlgGenAccessKey : "Toegangstoets",
+DlgGenName : "Naam",
+DlgGenTabIndex : "Tabvolgorde",
+DlgGenLongDescr : "Lange URL-omschrijving",
+DlgGenClass : "Stylesheet-klassen",
+DlgGenTitle : "Aanbevolen titel",
+DlgGenContType : "Aanbevolen content-type",
+DlgGenLinkCharset : "Karakterset van gelinkte bron",
+DlgGenStyle : "Stijl",
+
+// Image Dialog
+DlgImgTitle : "Eigenschappen afbeelding",
+DlgImgInfoTab : "Informatie afbeelding",
+DlgImgBtnUpload : "Naar server verzenden",
+DlgImgURL : "URL",
+DlgImgUpload : "Upload",
+DlgImgAlt : "Alternatieve tekst",
+DlgImgWidth : "Breedte",
+DlgImgHeight : "Hoogte",
+DlgImgLockRatio : "Afmetingen vergrendelen",
+DlgBtnResetSize : "Afmetingen resetten",
+DlgImgBorder : "Rand",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Uitlijning",
+DlgImgAlignLeft : "Links",
+DlgImgAlignAbsBottom: "Absoluut-onder",
+DlgImgAlignAbsMiddle: "Absoluut-midden",
+DlgImgAlignBaseline : "Basislijn",
+DlgImgAlignBottom : "Beneden",
+DlgImgAlignMiddle : "Midden",
+DlgImgAlignRight : "Rechts",
+DlgImgAlignTextTop : "Boven tekst",
+DlgImgAlignTop : "Boven",
+DlgImgPreview : "Voorbeeld",
+DlgImgAlertUrl : "Geef de URL van de afbeelding",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Eigenschappen Flash",
+DlgFlashChkPlay : "Automatisch afspelen",
+DlgFlashChkLoop : "Herhalen",
+DlgFlashChkMenu : "Flashmenu\'s inschakelen",
+DlgFlashScale : "Schaal",
+DlgFlashScaleAll : "Alles tonen",
+DlgFlashScaleNoBorder : "Geen rand",
+DlgFlashScaleFit : "Precies passend",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Linkomschrijving",
+DlgLnkTargetTab : "Doel",
+
+DlgLnkType : "Linktype",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Interne link in pagina",
+DlgLnkTypeEMail : "E-mail",
+DlgLnkProto : "Protocol",
+DlgLnkProtoOther : "<anders>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Kies een interne link",
+DlgLnkAnchorByName : "Op naam interne link",
+DlgLnkAnchorById : "Op kenmerk interne link",
+DlgLnkNoAnchors : "(Geen interne links in document gevonden)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-mailadres",
+DlgLnkEMailSubject : "Onderwerp bericht",
+DlgLnkEMailBody : "Inhoud bericht",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Naar de server versturen",
+
+DlgLnkTarget : "Doel",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<popup window>",
+DlgLnkTargetBlank : "Nieuw venster (_blank)",
+DlgLnkTargetParent : "Origineel venster (_parent)",
+DlgLnkTargetSelf : "Zelfde venster (_self)",
+DlgLnkTargetTop : "Hele venster (_top)",
+DlgLnkTargetFrameName : "Naam doelframe",
+DlgLnkPopWinName : "Naam popupvenster",
+DlgLnkPopWinFeat : "Instellingen popupvenster",
+DlgLnkPopResize : "Grootte wijzigen",
+DlgLnkPopLocation : "Locatiemenu",
+DlgLnkPopMenu : "Menubalk",
+DlgLnkPopScroll : "Schuifbalken",
+DlgLnkPopStatus : "Statusbalk",
+DlgLnkPopToolbar : "Menubalk",
+DlgLnkPopFullScrn : "Volledig scherm (IE)",
+DlgLnkPopDependent : "Afhankelijk (Netscape)",
+DlgLnkPopWidth : "Breedte",
+DlgLnkPopHeight : "Hoogte",
+DlgLnkPopLeft : "Positie links",
+DlgLnkPopTop : "Positie boven",
+
+DlnLnkMsgNoUrl : "Geef de link van de URL",
+DlnLnkMsgNoEMail : "Geef een e-mailadres",
+DlnLnkMsgNoAnchor : "Selecteer een interne link",
+DlnLnkMsgInvPopName : "De naam van de popup moet met een alfa-numerieke waarde beginnen, en mag geen spaties bevatten.",
+
+// Color Dialog
+DlgColorTitle : "Selecteer kleur",
+DlgColorBtnClear : "Opschonen",
+DlgColorHighlight : "Accentueren",
+DlgColorSelected : "Geselecteerd",
+
+// Smiley Dialog
+DlgSmileyTitle : "Smiley invoegen",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Selecteer speciaal teken",
+
+// Table Dialog
+DlgTableTitle : "Eigenschappen tabel",
+DlgTableRows : "Rijen",
+DlgTableColumns : "Kolommen",
+DlgTableBorder : "Breedte rand",
+DlgTableAlign : "Uitlijning",
+DlgTableAlignNotSet : "<Niet ingevoerd>",
+DlgTableAlignLeft : "Links",
+DlgTableAlignCenter : "Centreren",
+DlgTableAlignRight : "Rechts",
+DlgTableWidth : "Breedte",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "procent",
+DlgTableHeight : "Hoogte",
+DlgTableCellSpace : "Afstand tussen cellen",
+DlgTableCellPad : "Afstand vanaf rand cel",
+DlgTableCaption : "Naam",
+DlgTableSummary : "Samenvatting",
+
+// Table Cell Dialog
+DlgCellTitle : "Eigenschappen cel",
+DlgCellWidth : "Breedte",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "procent",
+DlgCellHeight : "Hoogte",
+DlgCellWordWrap : "Afbreken woorden",
+DlgCellWordWrapNotSet : "<Niet ingevoerd>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nee",
+DlgCellHorAlign : "Horizontale uitlijning",
+DlgCellHorAlignNotSet : "<Niet ingevoerd>",
+DlgCellHorAlignLeft : "Links",
+DlgCellHorAlignCenter : "Centreren",
+DlgCellHorAlignRight: "Rechts",
+DlgCellVerAlign : "Verticale uitlijning",
+DlgCellVerAlignNotSet : "<Niet ingevoerd>",
+DlgCellVerAlignTop : "Boven",
+DlgCellVerAlignMiddle : "Midden",
+DlgCellVerAlignBottom : "Beneden",
+DlgCellVerAlignBaseline : "Basislijn",
+DlgCellRowSpan : "Overkoepeling rijen",
+DlgCellCollSpan : "Overkoepeling kolommen",
+DlgCellBackColor : "Achtergrondkleur",
+DlgCellBorderColor : "Randkleur",
+DlgCellBtnSelect : "Selecteren...",
+
+// Find Dialog
+DlgFindTitle : "Zoeken",
+DlgFindFindBtn : "Zoeken",
+DlgFindNotFoundMsg : "De opgegeven tekst is niet gevonden.",
+
+// Replace Dialog
+DlgReplaceTitle : "Vervangen",
+DlgReplaceFindLbl : "Zoeken naar:",
+DlgReplaceReplaceLbl : "Vervangen met:",
+DlgReplaceCaseChk : "Hoofdlettergevoelig",
+DlgReplaceReplaceBtn : "Vervangen",
+DlgReplaceReplAllBtn : "Alles vervangen",
+DlgReplaceWordChk : "Hele woord moet voorkomen",
+
+// Paste Operations / Dialog
+PasteErrorCut : "De beveiligingsinstelling van de browser verhinderen het automatisch knippen. Gebruik de sneltoets Ctrl+X van het toetsenbord.",
+PasteErrorCopy : "De beveiligingsinstelling van de browser verhinderen het automatisch kopiëren. Gebruik de sneltoets Ctrl+C van het toetsenbord.",
+
+PasteAsText : "Plakken als platte tekst",
+PasteFromWord : "Plakken als Word-gegevens",
+
+DlgPasteMsg2 : "Plak de tekst in het volgende vak gebruik makend van je toetstenbord (<STRONG>Ctrl+V</STRONG>) en klik op <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Negeer \"Font Face\"-definities",
+DlgPasteRemoveStyles : "Verwijder \"Style\"-definities",
+DlgPasteCleanBox : "Vak opschonen",
+
+// Color Picker
+ColorAutomatic : "Automatisch",
+ColorMoreColors : "Meer kleuren...",
+
+// Document Properties
+DocProps : "Eigenschappen document",
+
+// Anchor Dialog
+DlgAnchorTitle : "Eigenschappen interne link",
+DlgAnchorName : "Naam interne link",
+DlgAnchorErrorName : "Geef de naam van de interne link op",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Niet in het woordenboek",
+DlgSpellChangeTo : "Wijzig in",
+DlgSpellBtnIgnore : "Negeren",
+DlgSpellBtnIgnoreAll : "Alles negeren",
+DlgSpellBtnReplace : "Vervangen",
+DlgSpellBtnReplaceAll : "Alles vervangen",
+DlgSpellBtnUndo : "Ongedaan maken",
+DlgSpellNoSuggestions : "-Geen suggesties-",
+DlgSpellProgress : "Bezig met spellingscontrole...",
+DlgSpellNoMispell : "Klaar met spellingscontrole: geen fouten gevonden",
+DlgSpellNoChanges : "Klaar met spellingscontrole: geen woorden aangepast",
+DlgSpellOneChange : "Klaar met spellingscontrole: één woord aangepast",
+DlgSpellManyChanges : "Klaar met spellingscontrole: %1 woorden aangepast",
+
+IeSpellDownload : "De spellingscontrole niet geïnstalleerd. Wil je deze nu downloaden?",
+
+// Button Dialog
+DlgButtonText : "Tekst (waarde)",
+DlgButtonType : "Soort",
+DlgButtonTypeBtn : "Knop",
+DlgButtonTypeSbm : "Versturen",
+DlgButtonTypeRst : "Leegmaken",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Naam",
+DlgCheckboxValue : "Waarde",
+DlgCheckboxSelected : "Geselecteerd",
+
+// Form Dialog
+DlgFormName : "Naam",
+DlgFormAction : "Actie",
+DlgFormMethod : "Methode",
+
+// Select Field Dialog
+DlgSelectName : "Naam",
+DlgSelectValue : "Waarde",
+DlgSelectSize : "Grootte",
+DlgSelectLines : "Regels",
+DlgSelectChkMulti : "Gecombineerde selecties toestaan",
+DlgSelectOpAvail : "Beschikbare opties",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Waarde",
+DlgSelectBtnAdd : "Toevoegen",
+DlgSelectBtnModify : "Wijzigen",
+DlgSelectBtnUp : "Omhoog",
+DlgSelectBtnDown : "Omlaag",
+DlgSelectBtnSetValue : "Als geselecteerde waarde instellen",
+DlgSelectBtnDelete : "Verwijderen",
+
+// Textarea Dialog
+DlgTextareaName : "Naam",
+DlgTextareaCols : "Kolommen",
+DlgTextareaRows : "Rijen",
+
+// Text Field Dialog
+DlgTextName : "Naam",
+DlgTextValue : "Waarde",
+DlgTextCharWidth : "Breedte (tekens)",
+DlgTextMaxChars : "Maximum aantal tekens",
+DlgTextType : "Soort",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Wachtwoord",
+
+// Hidden Field Dialog
+DlgHiddenName : "Naam",
+DlgHiddenValue : "Waarde",
+
+// Bulleted List Dialog
+BulletedListProp : "Eigenschappen opsommingslijst",
+NumberedListProp : "Eigenschappen genummerde opsommingslijst",
+DlgLstStart : "Start",
+DlgLstType : "Soort",
+DlgLstTypeCircle : "Cirkel",
+DlgLstTypeDisc : "Schijf",
+DlgLstTypeSquare : "Vierkant",
+DlgLstTypeNumbers : "Nummers (1, 2, 3)",
+DlgLstTypeLCase : "Kleine letters (a, b, c)",
+DlgLstTypeUCase : "Hoofdletters (A, B, C)",
+DlgLstTypeSRoman : "Klein Romeins (i, ii, iii)",
+DlgLstTypeLRoman : "Groot Romeins (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Algemeen",
+DlgDocBackTab : "Achtergrond",
+DlgDocColorsTab : "Kleuring en marges",
+DlgDocMetaTab : "META-data",
+
+DlgDocPageTitle : "Paginatitel",
+DlgDocLangDir : "Schrijfrichting",
+DlgDocLangDirLTR : "Links naar rechts",
+DlgDocLangDirRTL : "Rechts naar links",
+DlgDocLangCode : "Taalcode",
+DlgDocCharSet : "Karakterset-encoding",
+DlgDocCharSetCE : "Centraal Europees",
+DlgDocCharSetCT : "Traditioneel Chinees (Big5)",
+DlgDocCharSetCR : "Cyriliaans",
+DlgDocCharSetGR : "Grieks",
+DlgDocCharSetJP : "Japans",
+DlgDocCharSetKR : "Koreaans",
+DlgDocCharSetTR : "Turks",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "West europees",
+DlgDocCharSetOther : "Andere karakterset-encoding",
+
+DlgDocDocType : "Opschrift documentsoort",
+DlgDocDocTypeOther : "Ander opschrift documentsoort",
+DlgDocIncXHTML : "XHTML-declaraties meenemen",
+DlgDocBgColor : "Achtergrondkleur",
+DlgDocBgImage : "URL achtergrondplaatje",
+DlgDocBgNoScroll : "Vaste achtergrond",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Bezochte link",
+DlgDocCActive : "Active link",
+DlgDocMargins : "Afstandsinstellingen document",
+DlgDocMaTop : "Boven",
+DlgDocMaLeft : "Links",
+DlgDocMaRight : "Rechts",
+DlgDocMaBottom : "Onder",
+DlgDocMeIndex : "Trefwoorden betreffende document (kommagescheiden)",
+DlgDocMeDescr : "Beschrijving document",
+DlgDocMeAuthor : "Auteur",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "Voorbeeld",
+
+// Templates Dialog
+Templates : "Sjablonen",
+DlgTemplatesTitle : "Inhoud sjabonen",
+DlgTemplatesSelMsg : "Selecteer het sjabloon dat in de editor geopend moet worden (de actuele inhoud gaat verloren):",
+DlgTemplatesLoading : "Bezig met laden sjabonen. Even geduld alstublieft...",
+DlgTemplatesNoTpl : "(Geen sjablonen gedefinieerd)",
+DlgTemplatesReplace : "Vervang de huidige inhoud",
+
+// About Dialog
+DlgAboutAboutTab : "Over",
+DlgAboutBrowserInfoTab : "Browserinformatie",
+DlgAboutLicenseTab : "Licentie",
+DlgAboutVersion : "Versie",
+DlgAboutInfo : "Voor meer informatie ga naar "
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/no.js b/httemplate/elements/fckeditor/editor/lang/no.js
new file mode 100644
index 0000000..d3b237d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/no.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Norwegian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Skjul verktøylinje",
+ToolbarExpand : "Vis verktøylinje",
+
+// Toolbar Items and Context Menu
+Save : "Lagre",
+NewPage : "Ny Side",
+Preview : "Forhåndsvis",
+Cut : "Klipp ut",
+Copy : "Kopier",
+Paste : "Lim inn",
+PasteText : "Lim inn som ren tekst",
+PasteWord : "Lim inn fra Word",
+Print : "Skriv ut",
+SelectAll : "Merk alt",
+RemoveFormat : "Fjern format",
+InsertLinkLbl : "Lenke",
+InsertLink : "Sett inn/Rediger lenke",
+RemoveLink : "Fjern lenke",
+Anchor : "Sett inn/Rediger anker",
+InsertImageLbl : "Bilde",
+InsertImage : "Sett inn/Rediger bilde",
+InsertFlashLbl : "Flash",
+InsertFlash : "Sett inn/Rediger Flash",
+InsertTableLbl : "Tabell",
+InsertTable : "Sett inn/Rediger tabell",
+InsertLineLbl : "Linje",
+InsertLine : "Sett inn horisontal linje",
+InsertSpecialCharLbl: "Spesielt tegn",
+InsertSpecialChar : "Sett inn spesielt tegn",
+InsertSmileyLbl : "Smil",
+InsertSmiley : "Sett inn smil",
+About : "Om FCKeditor",
+Bold : "Fet",
+Italic : "Kursiv",
+Underline : "Understrek",
+StrikeThrough : "Gjennomstrek",
+Subscript : "Senket skrift",
+Superscript : "Hevet skrift",
+LeftJustify : "Venstrejuster",
+CenterJustify : "Midtjuster",
+RightJustify : "Høyrejuster",
+BlockJustify : "Blokkjuster",
+DecreaseIndent : "Senk nivå",
+IncreaseIndent : "Øk nivå",
+Undo : "Angre",
+Redo : "Gjør om",
+NumberedListLbl : "Numrert liste",
+NumberedList : "Sett inn/Fjern numrert liste",
+BulletedListLbl : "Uordnet liste",
+BulletedList : "Sett inn/Fjern uordnet liste",
+ShowTableBorders : "Vis tabellrammer",
+ShowDetails : "Vis detaljer",
+Style : "Stil",
+FontFormat : "Format",
+Font : "Skrift",
+FontSize : "Størrelse",
+TextColor : "Tekstfarge",
+BGColor : "Bakgrunnsfarge",
+Source : "Kilde",
+Find : "Finn",
+Replace : "Erstatt",
+SpellCheck : "Stavekontroll",
+UniversalKeyboard : "Universelt tastatur",
+PageBreakLbl : "Sideskift",
+PageBreak : "Sett inn sideskift",
+
+Form : "Skjema",
+Checkbox : "Sjekkboks",
+RadioButton : "Radioknapp",
+TextField : "Tekstfelt",
+Textarea : "Tekstområde",
+HiddenField : "Skjult felt",
+Button : "Knapp",
+SelectionField : "Dropdown meny",
+ImageButton : "Bildeknapp",
+
+FitWindow : "Maksimer størrelsen på redigeringsverktøyet",
+
+// Context Menu
+EditLink : "Rediger lenke",
+CellCM : "Celle",
+RowCM : "Rader",
+ColumnCM : "Kolonne",
+InsertRow : "Sett inn rad",
+DeleteRows : "Slett rader",
+InsertColumn : "Sett inn kolonne",
+DeleteColumns : "Slett kolonner",
+InsertCell : "Sett inn celle",
+DeleteCells : "Slett celler",
+MergeCells : "Slå sammen celler",
+SplitCell : "Splitt celler",
+TableDelete : "Slett tabell",
+CellProperties : "Celleegenskaper",
+TableProperties : "Tabellegenskaper",
+ImageProperties : "Bildeegenskaper",
+FlashProperties : "Flash Egenskaper",
+
+AnchorProp : "Ankeregenskaper",
+ButtonProp : "Knappegenskaper",
+CheckboxProp : "Sjekkboksegenskaper",
+HiddenFieldProp : "Skjult felt egenskaper",
+RadioButtonProp : "Radioknappegenskaper",
+ImageButtonProp : "Bildeknappegenskaper",
+TextFieldProp : "Tekstfeltegenskaper",
+SelectionFieldProp : "Dropdown menyegenskaper",
+TextareaProp : "Tekstfeltegenskaper",
+FormProp : "Skjemaegenskaper",
+
+FontFormats : "Normal;Formatert;Adresse;Tittel 1;Tittel 2;Tittel 3;Tittel 4;Tittel 5;Tittel 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Lager XHTML. Vennligst vent...",
+Done : "Ferdig",
+PasteWordConfirm : "Teksten du prøver å lime inn ser ut som om den kommer fra word , du bør rense den før du limer inn , vil du gjøre dette?",
+NotCompatiblePaste : "Denne kommandoen er tilgjenglig kun for Internet Explorer version 5.5 eller bedre. Vil du fortsette uten å rense?(Du kan lime inn som ren tekst)",
+UnknownToolbarItem : "Ukjent menyvalg \"%1\"",
+UnknownCommand : "Ukjent kommando \"%1\"",
+NotImplemented : "Kommando ikke ennå implimentert",
+UnknownToolbarSet : "Verktøylinjesett \"%1\" finnes ikke",
+NoActiveX : "Din nettleser's sikkerhetsinstillinger kan begrense noen av funksjonene i redigeringsverktøyet. Du må aktivere \"Kjør ActiveXkontroller og plugins\". Du kan oppleve feil og advarsler om manglende funksjoner",
+BrowseServerBlocked : "Kunne ikke åpne dialogboksen for filarkiv. Pass på at du har slått av popupstoppere.",
+DialogBlocked : "Kunne ikke åpne dialogboksen. Pass på at du har slått av popupstoppere.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Avbryt",
+DlgBtnClose : "Lukk",
+DlgBtnBrowseServer : "Bla igjennom server",
+DlgAdvancedTag : "Avansert",
+DlgOpOther : "<Annet>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Vennligst skriv inn URL'en",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ikke satt>",
+DlgGenId : "Id",
+DlgGenLangDir : "Språkretning",
+DlgGenLangDirLtr : "Venstre til høyre (VTH)",
+DlgGenLangDirRtl : "Høyre til venstre (HTV)",
+DlgGenLangCode : "Språk kode",
+DlgGenAccessKey : "Aksessknapp",
+DlgGenName : "Navn",
+DlgGenTabIndex : "Tab Indeks",
+DlgGenLongDescr : "Utvidet beskrivelse",
+DlgGenClass : "Stilarkklasser",
+DlgGenTitle : "Tittel",
+DlgGenContType : "Type",
+DlgGenLinkCharset : "Lenket språkkart",
+DlgGenStyle : "Stil",
+
+// Image Dialog
+DlgImgTitle : "Bildeegenskaper",
+DlgImgInfoTab : "Bildeinformasjon",
+DlgImgBtnUpload : "Send det til serveren",
+DlgImgURL : "URL",
+DlgImgUpload : "Last opp",
+DlgImgAlt : "Alternativ tekst",
+DlgImgWidth : "Bredde",
+DlgImgHeight : "Høyde",
+DlgImgLockRatio : "LÃ¥s forhold",
+DlgBtnResetSize : "Tilbakestill størrelse",
+DlgImgBorder : "Ramme",
+DlgImgHSpace : "HMarg",
+DlgImgVSpace : "VMarg",
+DlgImgAlign : "Juster",
+DlgImgAlignLeft : "Venstre",
+DlgImgAlignAbsBottom: "Abs bunn",
+DlgImgAlignAbsMiddle: "Abs midten",
+DlgImgAlignBaseline : "Bunnlinje",
+DlgImgAlignBottom : "Bunn",
+DlgImgAlignMiddle : "Midten",
+DlgImgAlignRight : "Høyre",
+DlgImgAlignTextTop : "Tekst topp",
+DlgImgAlignTop : "Topp",
+DlgImgPreview : "Forhåndsvis",
+DlgImgAlertUrl : "Vennligst skriv bildeurlen",
+DlgImgLinkTab : "Lenke",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Egenskaper",
+DlgFlashChkPlay : "Auto Spill",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Slå på Flash meny",
+DlgFlashScale : "Skaler",
+DlgFlashScaleAll : "Vis alt",
+DlgFlashScaleNoBorder : "Ingen ramme",
+DlgFlashScaleFit : "Skaler til å passeExact Fit",
+
+// Link Dialog
+DlgLnkWindowTitle : "Lenke",
+DlgLnkInfoTab : "Lenkeinfo",
+DlgLnkTargetTab : "MÃ¥l",
+
+DlgLnkType : "Lenketype",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Lenke til bokmerke i teksten",
+DlgLnkTypeEMail : "E-Post",
+DlgLnkProto : "Protokoll",
+DlgLnkProtoOther : "<annet>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Velg ett anker",
+DlgLnkAnchorByName : "Anker etter navn",
+DlgLnkAnchorById : "Element etter ID",
+DlgLnkNoAnchors : "<Ingen anker i dokumentet>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Post Addresse",
+DlgLnkEMailSubject : "Meldingsemne",
+DlgLnkEMailBody : "Melding",
+DlgLnkUpload : "Last opp",
+DlgLnkBtnUpload : "Send til server",
+
+DlgLnkTarget : "MÃ¥l",
+DlgLnkTargetFrame : "<ramme>",
+DlgLnkTargetPopup : "<popup vindu>",
+DlgLnkTargetBlank : "Nytt vindu (_blank)",
+DlgLnkTargetParent : "Foreldre vindu (_parent)",
+DlgLnkTargetSelf : "Samme vindu (_self)",
+DlgLnkTargetTop : "Hele vindu (_top)",
+DlgLnkTargetFrameName : "MÃ¥lramme",
+DlgLnkPopWinName : "Popup vindus navn",
+DlgLnkPopWinFeat : "Popup vindus egenskaper",
+DlgLnkPopResize : "Endre størrelse",
+DlgLnkPopLocation : "Adresselinje",
+DlgLnkPopMenu : "Menylinje",
+DlgLnkPopScroll : "Scrollbar",
+DlgLnkPopStatus : "Statuslinje",
+DlgLnkPopToolbar : "Verktøylinje",
+DlgLnkPopFullScrn : "Full skjerm (IE)",
+DlgLnkPopDependent : "Avhenging (Netscape)",
+DlgLnkPopWidth : "Bredde",
+DlgLnkPopHeight : "Høyde",
+DlgLnkPopLeft : "Venstre posisjon",
+DlgLnkPopTop : "Topp posisjon",
+
+DlnLnkMsgNoUrl : "Vennligst skriv inn lenkens url",
+DlnLnkMsgNoEMail : "Vennligst skriv inn e-postadressen",
+DlnLnkMsgNoAnchor : "Vennligst velg ett anker",
+DlnLnkMsgInvPopName : "Popup vinduets navn må begynne med en bokstav, og kan ikke inneholde mellomrom",
+
+// Color Dialog
+DlgColorTitle : "Velg farge",
+DlgColorBtnClear : "Tøm",
+DlgColorHighlight : "Marker",
+DlgColorSelected : "Velg",
+
+// Smiley Dialog
+DlgSmileyTitle : "Sett inn smil",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Velg spesielt tegn",
+
+// Table Dialog
+DlgTableTitle : "Tabellegenskaper",
+DlgTableRows : "Rader",
+DlgTableColumns : "Kolonner",
+DlgTableBorder : "Rammestørrelse",
+DlgTableAlign : "Justering",
+DlgTableAlignNotSet : "<Ikke satt>",
+DlgTableAlignLeft : "Venstre",
+DlgTableAlignCenter : "Midtjuster",
+DlgTableAlignRight : "Høyre",
+DlgTableWidth : "Bredde",
+DlgTableWidthPx : "pixler",
+DlgTableWidthPc : "prosent",
+DlgTableHeight : "Høyde",
+DlgTableCellSpace : "Celle marg",
+DlgTableCellPad : "Celle polstring",
+DlgTableCaption : "Tittel",
+DlgTableSummary : "Sammendrag",
+
+// Table Cell Dialog
+DlgCellTitle : "Celle egenskaper",
+DlgCellWidth : "Bredde",
+DlgCellWidthPx : "pixeler",
+DlgCellWidthPc : "prosent",
+DlgCellHeight : "Høyde",
+DlgCellWordWrap : "Tekstbrytning",
+DlgCellWordWrapNotSet : "<Ikke satt>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nei",
+DlgCellHorAlign : "Horisontal justering",
+DlgCellHorAlignNotSet : "<Ikke satt>",
+DlgCellHorAlignLeft : "Venstre",
+DlgCellHorAlignCenter : "Midtjuster",
+DlgCellHorAlignRight: "Høyre",
+DlgCellVerAlign : "Vertikal justering",
+DlgCellVerAlignNotSet : "<Ikke satt>",
+DlgCellVerAlignTop : "Topp",
+DlgCellVerAlignMiddle : "Midten",
+DlgCellVerAlignBottom : "Bunn",
+DlgCellVerAlignBaseline : "Bunnlinje",
+DlgCellRowSpan : "Radspenn",
+DlgCellCollSpan : "Kolonnespenn",
+DlgCellBackColor : "Bakgrunnsfarge",
+DlgCellBorderColor : "Rammefarge",
+DlgCellBtnSelect : "Velg...",
+
+// Find Dialog
+DlgFindTitle : "Finn",
+DlgFindFindBtn : "Finn",
+DlgFindNotFoundMsg : "Den spesifiserte teksten ble ikke funnet.",
+
+// Replace Dialog
+DlgReplaceTitle : "Erstatt",
+DlgReplaceFindLbl : "Finn hva:",
+DlgReplaceReplaceLbl : "Erstatt med:",
+DlgReplaceCaseChk : "Riktig case",
+DlgReplaceReplaceBtn : "Erstatt",
+DlgReplaceReplAllBtn : "Erstatt alle",
+DlgReplaceWordChk : "Finn hele ordet",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Din nettlesers sikkerhetsinstillinger tillater ikke automatisk klipping av tekst. Vennligst brukt snareveien (Ctrl+X).",
+PasteErrorCopy : "Din nettlesers sikkerhetsinstillinger tillater ikke automatisk kopiering av tekst. Vennligst brukt snareveien (Ctrl+C).",
+
+PasteAsText : "Lim inn som ren tekst",
+PasteFromWord : "Lim inn fra word",
+
+DlgPasteMsg2 : "Vennligst lim inn i den følgende boksen med tastaturet (<STRONG>Ctrl+V</STRONG>) og trykk <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Fjern skrifttyper",
+DlgPasteRemoveStyles : "Fjern stildefinisjoner",
+DlgPasteCleanBox : "Tøm boksen",
+
+// Color Picker
+ColorAutomatic : "Automatisk",
+ColorMoreColors : "Flere farger...",
+
+// Document Properties
+DocProps : "Dokumentegenskaper",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ankeregenskaper",
+DlgAnchorName : "Ankernavn",
+DlgAnchorErrorName : "Vennligst skriv inn ankernavnet",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ikke i ordboken",
+DlgSpellChangeTo : "Endre til",
+DlgSpellBtnIgnore : "Ignorer",
+DlgSpellBtnIgnoreAll : "Ignorer alle",
+DlgSpellBtnReplace : "Erstatt",
+DlgSpellBtnReplaceAll : "Erstatt alle",
+DlgSpellBtnUndo : "Angre",
+DlgSpellNoSuggestions : "- ingen forslag -",
+DlgSpellProgress : "Stavekontroll pågår...",
+DlgSpellNoMispell : "Stavekontroll fullført: ingen feilstavinger funnet",
+DlgSpellNoChanges : "Stavekontroll fullført: ingen ord endret",
+DlgSpellOneChange : "Stavekontroll fullført: Ett ord endret",
+DlgSpellManyChanges : "Stavekontroll fullført: %1 ord endret",
+
+IeSpellDownload : "Stavekontroll ikke installert, vil du laste den ned nå?",
+
+// Button Dialog
+DlgButtonText : "Tekst",
+DlgButtonType : "Type",
+DlgButtonTypeBtn : "Knapp",
+DlgButtonTypeSbm : "Send",
+DlgButtonTypeRst : "Nullstill",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Navn",
+DlgCheckboxValue : "Verdi",
+DlgCheckboxSelected : "Valgt",
+
+// Form Dialog
+DlgFormName : "Navn",
+DlgFormAction : "Handling",
+DlgFormMethod : "Metode",
+
+// Select Field Dialog
+DlgSelectName : "Navn",
+DlgSelectValue : "Verdi",
+DlgSelectSize : "Størrelse",
+DlgSelectLines : "Linjer",
+DlgSelectChkMulti : "Tillat flervalg",
+DlgSelectOpAvail : "Tilgjenglige alternativer",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Verdi",
+DlgSelectBtnAdd : "Legg til",
+DlgSelectBtnModify : "Endre",
+DlgSelectBtnUp : "Opp",
+DlgSelectBtnDown : "Ned",
+DlgSelectBtnSetValue : "Sett som valgt",
+DlgSelectBtnDelete : "Slett",
+
+// Textarea Dialog
+DlgTextareaName : "Navn",
+DlgTextareaCols : "Kolonner",
+DlgTextareaRows : "Rader",
+
+// Text Field Dialog
+DlgTextName : "Navn",
+DlgTextValue : "verdi",
+DlgTextCharWidth : "Tegnbredde",
+DlgTextMaxChars : "Maks antall tegn",
+DlgTextType : "Type",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Passord",
+
+// Hidden Field Dialog
+DlgHiddenName : "Navn",
+DlgHiddenValue : "Verdi",
+
+// Bulleted List Dialog
+BulletedListProp : "Uordnet listeegenskaper",
+NumberedListProp : "Ordnet listeegenskaper",
+DlgLstStart : "Start",
+DlgLstType : "Type",
+DlgLstTypeCircle : "Sirkel",
+DlgLstTypeDisc : "Hel sirkel",
+DlgLstTypeSquare : "Firkant",
+DlgLstTypeNumbers : "Numre(1, 2, 3)",
+DlgLstTypeLCase : "Små bokstaver (a, b, c)",
+DlgLstTypeUCase : "Store bokstaver(A, B, C)",
+DlgLstTypeSRoman : "Små romerske tall(i, ii, iii)",
+DlgLstTypeLRoman : "Store romerske tall(I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Generalt",
+DlgDocBackTab : "Bakgrunn",
+DlgDocColorsTab : "Farger og marginer",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Sidetittel",
+DlgDocLangDir : "Språkretning",
+DlgDocLangDirLTR : "Venstre til høyre (LTR)",
+DlgDocLangDirRTL : "Høyre til venstre (RTL)",
+DlgDocLangCode : "Språkkode",
+DlgDocCharSet : "Tegnsett",
+DlgDocCharSetCE : "Sentraleuropeisk",
+DlgDocCharSetCT : "Tradisonell kinesisk(Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Gresk",
+DlgDocCharSetJP : "Japansk",
+DlgDocCharSetKR : "Koreansk",
+DlgDocCharSetTR : "Tyrkisk",
+DlgDocCharSetUN : "Unikode (UTF-8)",
+DlgDocCharSetWE : "Vesteuropeisk",
+DlgDocCharSetOther : "Annet tegnsett",
+
+DlgDocDocType : "Dokumenttype header",
+DlgDocDocTypeOther : "Annet dokumenttype header",
+DlgDocIncXHTML : "Inkulder XHTML deklarasjon",
+DlgDocBgColor : "Bakgrunnsfarge",
+DlgDocBgImage : "Bakgrunnsbilde url",
+DlgDocBgNoScroll : "Ikke scroll bakgrunnsbilde",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Besøkt lenke",
+DlgDocCActive : "Aktiv lenke",
+DlgDocMargins : "Sidemargin",
+DlgDocMaTop : "Topp",
+DlgDocMaLeft : "Venstre",
+DlgDocMaRight : "Høyre",
+DlgDocMaBottom : "Bunn",
+DlgDocMeIndex : "Dokument nøkkelord (kommaseparert)",
+DlgDocMeDescr : "Dokumentbeskrivelse",
+DlgDocMeAuthor : "Forfatter",
+DlgDocMeCopy : "Kopirett",
+DlgDocPreview : "Forhåndsvising",
+
+// Templates Dialog
+Templates : "Maler",
+DlgTemplatesTitle : "Innholdsmaler",
+DlgTemplatesSelMsg : "Velg malen du vil åpne<br>(innholdet du har skrevet blir tapt!):",
+DlgTemplatesLoading : "Laster malliste. Vennligst vent...",
+DlgTemplatesNoTpl : "(Ingen maler definert)",
+DlgTemplatesReplace : "Erstatt faktisk innold",
+
+// About Dialog
+DlgAboutAboutTab : "Om",
+DlgAboutBrowserInfoTab : "Nettleserinfo",
+DlgAboutLicenseTab : "Lisens",
+DlgAboutVersion : "versjon",
+DlgAboutInfo : "For further information go to" //MISSING
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/pl.js b/httemplate/elements/fckeditor/editor/lang/pl.js
new file mode 100644
index 0000000..f01994d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/pl.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Polish language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Zwiń pasek narzędzi",
+ToolbarExpand : "Rozwiń pasek narzędzi",
+
+// Toolbar Items and Context Menu
+Save : "Zapisz",
+NewPage : "Nowa strona",
+Preview : "PodglÄ…d",
+Cut : "Wytnij",
+Copy : "Kopiuj",
+Paste : "Wklej",
+PasteText : "Wklej jako czysty tekst",
+PasteWord : "Wklej z Worda",
+Print : "Drukuj",
+SelectAll : "Zaznacz wszystko",
+RemoveFormat : "Usuń formatowanie",
+InsertLinkLbl : "Hiperłącze",
+InsertLink : "Wstaw/edytuj hiperłącze",
+RemoveLink : "Usuń hiperłącze",
+Anchor : "Wstaw/edytuj kotwicÄ™",
+InsertImageLbl : "Obrazek",
+InsertImage : "Wstaw/edytuj obrazek",
+InsertFlashLbl : "Flash",
+InsertFlash : "Dodaj/Edytuj element Flash",
+InsertTableLbl : "Tabela",
+InsertTable : "Wstaw/edytuj tabelÄ™",
+InsertLineLbl : "Linia pozioma",
+InsertLine : "Wstaw poziomÄ… liniÄ™",
+InsertSpecialCharLbl: "Znak specjalny",
+InsertSpecialChar : "Wstaw znak specjalny",
+InsertSmileyLbl : "Emotikona",
+InsertSmiley : "Wstaw emotikonÄ™",
+About : "O programie FCKeditor",
+Bold : "Pogrubienie",
+Italic : "Kursywa",
+Underline : "Podkreślenie",
+StrikeThrough : "Przekreślenie",
+Subscript : "Indeks dolny",
+Superscript : "Indeks górny",
+LeftJustify : "Wyrównaj do lewej",
+CenterJustify : "Wyrównaj do środka",
+RightJustify : "Wyrównaj do prawej",
+BlockJustify : "Wyrównaj do lewej i prawej",
+DecreaseIndent : "Zmniejsz wcięcie",
+IncreaseIndent : "Zwiększ wcięcie",
+Undo : "Cofnij",
+Redo : "Ponów",
+NumberedListLbl : "Lista numerowana",
+NumberedList : "Wstaw/usuń numerowanie listy",
+BulletedListLbl : "Lista wypunktowana",
+BulletedList : "Wstaw/usuń wypunktowanie listy",
+ShowTableBorders : "Pokazuj ramkÄ™ tabeli",
+ShowDetails : "Pokaż szczegóły",
+Style : "Styl",
+FontFormat : "Format",
+Font : "Czcionka",
+FontSize : "Rozmiar",
+TextColor : "Kolor tekstu",
+BGColor : "Kolor tła",
+Source : "Źródło dokumentu",
+Find : "Znajdź",
+Replace : "Zamień",
+SpellCheck : "Sprawdź pisownię",
+UniversalKeyboard : "Klawiatura Uniwersalna",
+PageBreakLbl : "Odstęp",
+PageBreak : "Wstaw odstęp",
+
+Form : "Formularz",
+Checkbox : "Checkbox",
+RadioButton : "Pole wyboru",
+TextField : "Pole tekstowe",
+Textarea : "Obszar tekstowy",
+HiddenField : "Pole ukryte",
+Button : "Przycisk",
+SelectionField : "Lista wyboru",
+ImageButton : "Przycisk obrazek",
+
+FitWindow : "Maksymalizuj rozmiar edytora",
+
+// Context Menu
+EditLink : "Edytuj hiperłącze",
+CellCM : "Komórka",
+RowCM : "Wiersz",
+ColumnCM : "Kolumna",
+InsertRow : "Wstaw wiersz",
+DeleteRows : "Usuń wiersze",
+InsertColumn : "Wstaw kolumnÄ™",
+DeleteColumns : "Usuń kolumny",
+InsertCell : "Wstaw komórkę",
+DeleteCells : "Usuń komórki",
+MergeCells : "Połącz komórki",
+SplitCell : "Podziel komórkę",
+TableDelete : "Usuń tabelę",
+CellProperties : "Właściwości komórki",
+TableProperties : "Właściwości tabeli",
+ImageProperties : "Właściwości obrazka",
+FlashProperties : "Właściwości elementu Flash",
+
+AnchorProp : "Właściwości kotwicy",
+ButtonProp : "Właściwości przycisku",
+CheckboxProp : "Checkbox - właściwości",
+HiddenFieldProp : "Właściwości pola ukrytego",
+RadioButtonProp : "Właściwości pola wyboru",
+ImageButtonProp : "Właściwości przycisku obrazka",
+TextFieldProp : "Właściwości pola tekstowego",
+SelectionFieldProp : "Właściwości listy wyboru",
+TextareaProp : "Właściwości obszaru tekstowego",
+FormProp : "Właściwości formularza",
+
+FontFormats : "Normalny;Tekst sformatowany;Adres;Nagłówek 1;Nagłówek 2;Nagłówek 3;Nagłówek 4;Nagłówek 5;Nagłówek 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Przetwarzanie XHTML. Proszę czekać...",
+Done : "Gotowe",
+PasteWordConfirm : "Tekst, który chcesz wkleić, prawdopodobnie pochodzi z programu Word. Czy chcesz go wyczyścic przed wklejeniem?",
+NotCompatiblePaste : "Ta funkcja jest dostępna w programie Internet Explorer w wersji 5.5 lub wyższej. Czy chcesz wkleić tekst bez czyszczenia?",
+UnknownToolbarItem : "Nieznany element paska narzędzi \"%1\"",
+UnknownCommand : "Nieznana komenda \"%1\"",
+NotImplemented : "Komenda niezaimplementowana",
+UnknownToolbarSet : "Pasek narzędzi \"%1\" nie istnieje",
+NoActiveX : "Ustawienia zabezpieczeń twojej przeglądarki mogą ograniczyć niektóre funkcje edytora. Musisz włączyć opcję \"Uruchamianie formantów Activex i dodatków plugin\". W przeciwnym wypadku mogą pojawiać się błędy.",
+BrowseServerBlocked : "Okno menadżera plików nie może zostać otwarte. Upewnij się, że wszystkie blokady popup są wyłączone.",
+DialogBlocked : "Nie można otworzyć okna dialogowego. Upewnij się, że wszystkie blokady popup są wyłączone.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Anuluj",
+DlgBtnClose : "Zamknij",
+DlgBtnBrowseServer : "PrzeglÄ…daj",
+DlgAdvancedTag : "Zaawansowane",
+DlgOpOther : "<Inny>",
+DlgInfoTab : "Informacje",
+DlgAlertUrl : "Proszę podać URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nieustawione>",
+DlgGenId : "Id",
+DlgGenLangDir : "Kierunek tekstu",
+DlgGenLangDirLtr : "Od lewej do prawej (LTR)",
+DlgGenLangDirRtl : "Od prawej do lewej (RTL)",
+DlgGenLangCode : "Kod języka",
+DlgGenAccessKey : "Klawisz dostępu",
+DlgGenName : "Nazwa",
+DlgGenTabIndex : "Indeks tabeli",
+DlgGenLongDescr : "Long Description URL",
+DlgGenClass : "Stylesheet Classes",
+DlgGenTitle : "Advisory Title",
+DlgGenContType : "Advisory Content Type",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Styl",
+
+// Image Dialog
+DlgImgTitle : "Właściwości obrazka",
+DlgImgInfoTab : "Informacje o obrazku",
+DlgImgBtnUpload : "Syślij",
+DlgImgURL : "Adres URL",
+DlgImgUpload : "Wyślij",
+DlgImgAlt : "Tekst zastępczy",
+DlgImgWidth : "Szerokość",
+DlgImgHeight : "Wysokość",
+DlgImgLockRatio : "Zablokuj proporcje",
+DlgBtnResetSize : "Przywróć rozmiar",
+DlgImgBorder : "Ramka",
+DlgImgHSpace : "Odstęp poziomy",
+DlgImgVSpace : "Odstęp pionowy",
+DlgImgAlign : "Wyrównaj",
+DlgImgAlignLeft : "Do lewej",
+DlgImgAlignAbsBottom: "Do dołu",
+DlgImgAlignAbsMiddle: "Do środka w pionie",
+DlgImgAlignBaseline : "Do linii bazowej",
+DlgImgAlignBottom : "Do dołu",
+DlgImgAlignMiddle : "Do środka",
+DlgImgAlignRight : "Do prawej",
+DlgImgAlignTextTop : "Do góry tekstu",
+DlgImgAlignTop : "Do góry",
+DlgImgPreview : "PodglÄ…d",
+DlgImgAlertUrl : "Podaj adres obrazka.",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Właściwości elementu Flash",
+DlgFlashChkPlay : "Auto Odtwarzanie",
+DlgFlashChkLoop : "Pętla",
+DlgFlashChkMenu : "WÅ‚Ä…cz menu",
+DlgFlashScale : "Skaluj",
+DlgFlashScaleAll : "Pokaż wszystko",
+DlgFlashScaleNoBorder : "Bez Ramki",
+DlgFlashScaleFit : "Dokładne dopasowanie",
+
+// Link Dialog
+DlgLnkWindowTitle : "Hiperłącze",
+DlgLnkInfoTab : "Informacje ",
+DlgLnkTargetTab : "Cel",
+
+DlgLnkType : "Typ hiperłącza",
+DlgLnkTypeURL : "Adres URL",
+DlgLnkTypeAnchor : "Odnośnik wewnątrz strony",
+DlgLnkTypeEMail : "Adres e-mail",
+DlgLnkProto : "Protokół",
+DlgLnkProtoOther : "<inny>",
+DlgLnkURL : "Adres URL",
+DlgLnkAnchorSel : "Wybierz etykietÄ™",
+DlgLnkAnchorByName : "Wg etykiety",
+DlgLnkAnchorById : "Wg identyfikatora elementu",
+DlgLnkNoAnchors : "<W dokumencie nie zdefiniowano żadnych etykiet>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Adres e-mail",
+DlgLnkEMailSubject : "Temat",
+DlgLnkEMailBody : "Treść",
+DlgLnkUpload : "Upload",
+DlgLnkBtnUpload : "Wyślij",
+
+DlgLnkTarget : "Cel",
+DlgLnkTargetFrame : "<ramka>",
+DlgLnkTargetPopup : "<wyskakujÄ…ce okno>",
+DlgLnkTargetBlank : "Nowe okno (_blank)",
+DlgLnkTargetParent : "Okno nadrzędne (_parent)",
+DlgLnkTargetSelf : "To samo okno (_self)",
+DlgLnkTargetTop : "Okno najwyższe w hierarchii (_top)",
+DlgLnkTargetFrameName : "Nazwa Ramki Docelowej",
+DlgLnkPopWinName : "Nazwa wyskakujÄ…cego okna",
+DlgLnkPopWinFeat : "Właściwości wyskakującego okna",
+DlgLnkPopResize : "Możliwa zmiana rozmiaru",
+DlgLnkPopLocation : "Pasek adresu",
+DlgLnkPopMenu : "Pasek menu",
+DlgLnkPopScroll : "Paski przewijania",
+DlgLnkPopStatus : "Pasek statusu",
+DlgLnkPopToolbar : "Pasek narzędzi",
+DlgLnkPopFullScrn : "Pełny ekran (IE)",
+DlgLnkPopDependent : "Okno zależne (Netscape)",
+DlgLnkPopWidth : "Szerokość",
+DlgLnkPopHeight : "Wysokość",
+DlgLnkPopLeft : "Pozycja w poziomie",
+DlgLnkPopTop : "Pozycja w pionie",
+
+DlnLnkMsgNoUrl : "Podaj adres URL",
+DlnLnkMsgNoEMail : "Podaj adres e-mail",
+DlnLnkMsgNoAnchor : "Wybierz etykietÄ™",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Wybierz kolor",
+DlgColorBtnClear : "Wyczyść",
+DlgColorHighlight : "PodglÄ…d",
+DlgColorSelected : "Wybrane",
+
+// Smiley Dialog
+DlgSmileyTitle : "Wstaw emotikonÄ™",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Wybierz znak specjalny",
+
+// Table Dialog
+DlgTableTitle : "Właściwości tabeli",
+DlgTableRows : "Liczba wierszy",
+DlgTableColumns : "Liczba kolumn",
+DlgTableBorder : "Grubość ramki",
+DlgTableAlign : "Wyrównanie",
+DlgTableAlignNotSet : "<brak ustawień>",
+DlgTableAlignLeft : "Do lewej",
+DlgTableAlignCenter : "Do środka",
+DlgTableAlignRight : "Do prawej",
+DlgTableWidth : "Szerokość",
+DlgTableWidthPx : "piksele",
+DlgTableWidthPc : "%",
+DlgTableHeight : "Wysokość",
+DlgTableCellSpace : "Odstęp pomiędzy komórkami",
+DlgTableCellPad : "Margines wewnętrzny komórek",
+DlgTableCaption : "Tytuł",
+DlgTableSummary : "Podsumowanie",
+
+// Table Cell Dialog
+DlgCellTitle : "Właściwości komórki",
+DlgCellWidth : "Szerokość",
+DlgCellWidthPx : "piksele",
+DlgCellWidthPc : "%",
+DlgCellHeight : "Wysokość",
+DlgCellWordWrap : "Zawijanie tekstu",
+DlgCellWordWrapNotSet : "<brak ustawień>",
+DlgCellWordWrapYes : "Tak",
+DlgCellWordWrapNo : "Nie",
+DlgCellHorAlign : "Wyrównanie poziome",
+DlgCellHorAlignNotSet : "<brak ustawień>",
+DlgCellHorAlignLeft : "Do lewej",
+DlgCellHorAlignCenter : "Do środka",
+DlgCellHorAlignRight: "Do prawej",
+DlgCellVerAlign : "Wyrównanie pionowe",
+DlgCellVerAlignNotSet : "<brak ustawień>",
+DlgCellVerAlignTop : "Do góry",
+DlgCellVerAlignMiddle : "Do środka",
+DlgCellVerAlignBottom : "Do dołu",
+DlgCellVerAlignBaseline : "Do linii bazowej",
+DlgCellRowSpan : "Zajętość wierszy",
+DlgCellCollSpan : "Zajętość kolumn",
+DlgCellBackColor : "Kolor tła",
+DlgCellBorderColor : "Kolor ramki",
+DlgCellBtnSelect : "Wybierz...",
+
+// Find Dialog
+DlgFindTitle : "Znajdź",
+DlgFindFindBtn : "Znajdź",
+DlgFindNotFoundMsg : "Nie znaleziono szukanego hasła.",
+
+// Replace Dialog
+DlgReplaceTitle : "Zamień",
+DlgReplaceFindLbl : "Znajdź:",
+DlgReplaceReplaceLbl : "ZastÄ…p przez:",
+DlgReplaceCaseChk : "Uwzględnij wielkość liter",
+DlgReplaceReplaceBtn : "ZastÄ…p",
+DlgReplaceReplAllBtn : "ZastÄ…p wszystko",
+DlgReplaceWordChk : "Całe słowa",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Ustawienia bezpieczeństwa Twojej przeglądarki nie pozwalają na automatyczne wycinanie tekstu. Użyj skrótu klawiszowego Ctrl+X.",
+PasteErrorCopy : "Ustawienia bezpieczeństwa Twojej przeglądarki nie pozwalają na automatyczne kopiowanie tekstu. Użyj skrótu klawiszowego Ctrl+C.",
+
+PasteAsText : "Wklej jako czysty tekst",
+PasteFromWord : "Wklej z Worda",
+
+DlgPasteMsg2 : "Proszę wkleić w poniższym polu używając klawiaturowego skrótu (<STRONG>Ctrl+V</STRONG>) i kliknąć <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignoruj definicje 'Font Face'",
+DlgPasteRemoveStyles : "Usuń definicje Stylów",
+DlgPasteCleanBox : "Wyczyść",
+
+// Color Picker
+ColorAutomatic : "Automatycznie",
+ColorMoreColors : "Więcej kolorów...",
+
+// Document Properties
+DocProps : "Właściwości dokumentu",
+
+// Anchor Dialog
+DlgAnchorTitle : "Właściwości kotwicy",
+DlgAnchorName : "Nazwa kotwicy",
+DlgAnchorErrorName : "Wpisz nazwÄ™ kotwicy",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Słowa nie ma w słowniku",
+DlgSpellChangeTo : "Zmień na",
+DlgSpellBtnIgnore : "Ignoruj",
+DlgSpellBtnIgnoreAll : "Ignoruj wszystkie",
+DlgSpellBtnReplace : "Zmień",
+DlgSpellBtnReplaceAll : "Zmień wszystkie",
+DlgSpellBtnUndo : "Undo",
+DlgSpellNoSuggestions : "- Brak sugestii -",
+DlgSpellProgress : "Trwa sprawdzanie ...",
+DlgSpellNoMispell : "Sprawdzanie zakończone: nie znaleziono błędów",
+DlgSpellNoChanges : "Sprawdzanie zakończone: nie zmieniono żadnego słowa",
+DlgSpellOneChange : "Sprawdzanie zakończone: zmieniono jedno słowo",
+DlgSpellManyChanges : "Sprawdzanie zakończone: zmieniono %l słów",
+
+IeSpellDownload : "Słownik nie jest zainstalowany. Chcesz go ściągnąć?",
+
+// Button Dialog
+DlgButtonText : "Tekst (Wartość)",
+DlgButtonType : "Typ",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nazwa",
+DlgCheckboxValue : "Wartość",
+DlgCheckboxSelected : "Zaznaczony",
+
+// Form Dialog
+DlgFormName : "Nazwa",
+DlgFormAction : "Akcja",
+DlgFormMethod : "Metoda",
+
+// Select Field Dialog
+DlgSelectName : "Nazwa",
+DlgSelectValue : "Wartość",
+DlgSelectSize : "Rozmiar",
+DlgSelectLines : "linii",
+DlgSelectChkMulti : "Wielokrotny wybór",
+DlgSelectOpAvail : "Dostępne opcje",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Wartość",
+DlgSelectBtnAdd : "Dodaj",
+DlgSelectBtnModify : "Zmień",
+DlgSelectBtnUp : "Do góry",
+DlgSelectBtnDown : "Do dołu",
+DlgSelectBtnSetValue : "Ustaw wartość zaznaczoną",
+DlgSelectBtnDelete : "Usuń",
+
+// Textarea Dialog
+DlgTextareaName : "Nazwa",
+DlgTextareaCols : "Kolumnu",
+DlgTextareaRows : "Wiersze",
+
+// Text Field Dialog
+DlgTextName : "Nazwa",
+DlgTextValue : "Wartość",
+DlgTextCharWidth : "Szerokość w znakach",
+DlgTextMaxChars : "Max. szerokość",
+DlgTextType : "Typ",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Hasło",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nazwa",
+DlgHiddenValue : "Wartość",
+
+// Bulleted List Dialog
+BulletedListProp : "Właściwości listy punktowanej",
+NumberedListProp : "Właściwości listy numerowanej",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Typ",
+DlgLstTypeCircle : "Koło",
+DlgLstTypeDisc : "Dysk",
+DlgLstTypeSquare : "Kwadrat",
+DlgLstTypeNumbers : "Cyfry (1, 2, 3)",
+DlgLstTypeLCase : "Małe litery (a, b, c)",
+DlgLstTypeUCase : "Duże litery (A, B, C)",
+DlgLstTypeSRoman : "Numeracja rzymska (i, ii, iii)",
+DlgLstTypeLRoman : "Numeracja rzymska (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Ogólne",
+DlgDocBackTab : "TÅ‚o",
+DlgDocColorsTab : "Kolory i marginesy",
+DlgDocMetaTab : "Meta Dane",
+
+DlgDocPageTitle : "Tytuł strony",
+DlgDocLangDir : "Kierunek pisania",
+DlgDocLangDirLTR : "Od lewej do prawej (LTR)",
+DlgDocLangDirRTL : "Od prawej do lewej (RTL)",
+DlgDocLangCode : "Kod języka",
+DlgDocCharSet : "Kodowanie znaków",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Inne kodowanie znaków",
+
+DlgDocDocType : "Nagłowek typu dokumentu",
+DlgDocDocTypeOther : "Inny typ dokumentu",
+DlgDocIncXHTML : "Dołącz deklarację XHTML",
+DlgDocBgColor : "Kolor tła",
+DlgDocBgImage : "Obrazek tła",
+DlgDocBgNoScroll : "TÅ‚o nieruchome",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Hiperłącze",
+DlgDocCVisited : "Odwiedzane hiperłącze",
+DlgDocCActive : "Aktywne hiperłącze",
+DlgDocMargins : "Marginesy strony",
+DlgDocMaTop : "Górny",
+DlgDocMaLeft : "Lewy",
+DlgDocMaRight : "Prawy",
+DlgDocMaBottom : "Dolny",
+DlgDocMeIndex : "SÅ‚owa kluczowe (oddzielone przecinkami)",
+DlgDocMeDescr : "Opis dokumentu",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Copyright",
+DlgDocPreview : "PodglÄ…d",
+
+// Templates Dialog
+Templates : "Sablony",
+DlgTemplatesTitle : "Szablony zawartości",
+DlgTemplatesSelMsg : "Wybierz szablon do otwarcia w edytorze<br>(obecna zawartość okna edytora zostanie utracona):",
+DlgTemplatesLoading : "Åadowanie listy szablonów. ProszÄ™ czekać...",
+DlgTemplatesNoTpl : "(Brak zdefiniowanych szablonów)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "O ...",
+DlgAboutBrowserInfoTab : "O przeglÄ…darce",
+DlgAboutLicenseTab : "Licencja",
+DlgAboutVersion : "wersja",
+DlgAboutInfo : "Więcej informacji uzyskasz pod adresem"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/pt-br.js b/httemplate/elements/fckeditor/editor/lang/pt-br.js
new file mode 100644
index 0000000..53a2b5d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/pt-br.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Brazilian Portuguese language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Ocultar Barra de Ferramentas",
+ToolbarExpand : "Exibir Barra de Ferramentas",
+
+// Toolbar Items and Context Menu
+Save : "Salvar",
+NewPage : "Novo",
+Preview : "Visualizar",
+Cut : "Recortar",
+Copy : "Copiar",
+Paste : "Colar",
+PasteText : "Colar como Texto sem Formatação",
+PasteWord : "Colar do Word",
+Print : "Imprimir",
+SelectAll : "Selecionar Tudo",
+RemoveFormat : "Remover Formatação",
+InsertLinkLbl : "Hiperlink",
+InsertLink : "Inserir/Editar Hiperlink",
+RemoveLink : "Remover Hiperlink",
+Anchor : "Inserir/Editar Âncora",
+InsertImageLbl : "Figura",
+InsertImage : "Inserir/Editar Figura",
+InsertFlashLbl : "Flash",
+InsertFlash : "Insere/Edita Flash",
+InsertTableLbl : "Tabela",
+InsertTable : "Inserir/Editar Tabela",
+InsertLineLbl : "Linha",
+InsertLine : "Inserir Linha Horizontal",
+InsertSpecialCharLbl: "Caracteres Especiais",
+InsertSpecialChar : "Inserir Caractere Especial",
+InsertSmileyLbl : "Emoticon",
+InsertSmiley : "Inserir Emoticon",
+About : "Sobre FCKeditor",
+Bold : "Negrito",
+Italic : "Itálico",
+Underline : "Sublinhado",
+StrikeThrough : "Tachado",
+Subscript : "Subscrito",
+Superscript : "Sobrescrito",
+LeftJustify : "Alinhar Esquerda",
+CenterJustify : "Centralizar",
+RightJustify : "Alinhar Direita",
+BlockJustify : "Justificado",
+DecreaseIndent : "Diminuir Recuo",
+IncreaseIndent : "Aumentar Recuo",
+Undo : "Desfazer",
+Redo : "Refazer",
+NumberedListLbl : "Numeração",
+NumberedList : "Inserir/Remover Numeração",
+BulletedListLbl : "Marcadores",
+BulletedList : "Inserir/Remover Marcadores",
+ShowTableBorders : "Exibir Bordas da Tabela",
+ShowDetails : "Exibir Detalhes",
+Style : "Estilo",
+FontFormat : "Formatação",
+Font : "Fonte",
+FontSize : "Tamanho",
+TextColor : "Cor do Texto",
+BGColor : "Cor do Plano de Fundo",
+Source : "Código-Fonte",
+Find : "Localizar",
+Replace : "Substituir",
+SpellCheck : "Verificar Ortografia",
+UniversalKeyboard : "Teclado Universal",
+PageBreakLbl : "Quebra de Página",
+PageBreak : "Inserir Quebra de Página",
+
+Form : "Formulário",
+Checkbox : "Caixa de Seleção",
+RadioButton : "Botão de Opção",
+TextField : "Caixa de Texto",
+Textarea : "Ãrea de Texto",
+HiddenField : "Campo Oculto",
+Button : "Botão",
+SelectionField : "Caixa de Listagem",
+ImageButton : "Botão de Imagem",
+
+FitWindow : "Maximizar o tamanho do editor",
+
+// Context Menu
+EditLink : "Editar Hiperlink",
+CellCM : "Célula",
+RowCM : "Linha",
+ColumnCM : "Coluna",
+InsertRow : "Inserir Linha",
+DeleteRows : "Remover Linhas",
+InsertColumn : "Inserir Coluna",
+DeleteColumns : "Remover Colunas",
+InsertCell : "Inserir Células",
+DeleteCells : "Remover Células",
+MergeCells : "Mesclar Células",
+SplitCell : "Dividir Célular",
+TableDelete : "Apagar Tabela",
+CellProperties : "Formatar Célula",
+TableProperties : "Formatar Tabela",
+ImageProperties : "Formatar Figura",
+FlashProperties : "Propriedades Flash",
+
+AnchorProp : "Formatar Âncora",
+ButtonProp : "Formatar Botão",
+CheckboxProp : "Formatar Caixa de Seleção",
+HiddenFieldProp : "Formatar Campo Oculto",
+RadioButtonProp : "Formatar Botão de Opção",
+ImageButtonProp : "Formatar Botão de Imagem",
+TextFieldProp : "Formatar Caixa de Texto",
+SelectionFieldProp : "Formatar Caixa de Listagem",
+TextareaProp : "Formatar Ãrea de Texto",
+FormProp : "Formatar Formulário",
+
+FontFormats : "Normal;Formatado;Endereço;Título 1;Título 2;Título 3;Título 4;Título 5;Título 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Processando XHTML. Por favor, aguarde...",
+Done : "Pronto",
+PasteWordConfirm : "O texto que você deseja colar parece ter sido copiado do Word. Você gostaria de remover a formatação antes de colar?",
+NotCompatiblePaste : "Este comando está disponível para o navegador Internet Explorer 5.5 ou superior. Você gostaria de colar sem remover a formatação?",
+UnknownToolbarItem : "O item da barra de ferramentas \"%1\" não é reconhecido",
+UnknownCommand : "O comando \"%1\" não é reconhecido",
+NotImplemented : "O comando não foi implementado",
+UnknownToolbarSet : "A barra de ferramentas \"%1\" não existe",
+NoActiveX : "As configurações de segurança do seu browser podem limitar algumas características do editor. Você precisa habilitar a opção \"Executar controles e plug-ins ActiveX\". Você pode experimentar erros e alertas de características faltantes.",
+BrowseServerBlocked : "Os recursos do browser não puderam ser abertos. Tenha certeza que todos os bloqueadores de popup estão desabilitados.",
+DialogBlocked : "Não foi possível abrir a janela de diálogo. Tenha certeza que todos os bloqueadores de popup estão desabilitados.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancelar",
+DlgBtnClose : "Fechar",
+DlgBtnBrowseServer : "Localizar no Servidor",
+DlgAdvancedTag : "Avançado",
+DlgOpOther : "<Outros>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Inserir a URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<não ajustado>",
+DlgGenId : "Id",
+DlgGenLangDir : "Direção do idioma",
+DlgGenLangDirLtr : "Esquerda para Direita (LTR)",
+DlgGenLangDirRtl : "Direita para Esquerda (RTL)",
+DlgGenLangCode : "Idioma",
+DlgGenAccessKey : "Chave de Acesso",
+DlgGenName : "Nome",
+DlgGenTabIndex : "Ãndice de Tabulação",
+DlgGenLongDescr : "Descrição da URL",
+DlgGenClass : "Classe de Folhas de Estilo",
+DlgGenTitle : "Título",
+DlgGenContType : "Tipo de Conteúdo",
+DlgGenLinkCharset : "Conjunto de Caracteres do Hiperlink",
+DlgGenStyle : "Estilos",
+
+// Image Dialog
+DlgImgTitle : "Formatar Figura",
+DlgImgInfoTab : "Informações da Figura",
+DlgImgBtnUpload : "Enviar para o Servidor",
+DlgImgURL : "URL",
+DlgImgUpload : "Submeter",
+DlgImgAlt : "Texto Alternativo",
+DlgImgWidth : "Largura",
+DlgImgHeight : "Altura",
+DlgImgLockRatio : "Manter proporções",
+DlgBtnResetSize : "Redefinir para o Tamanho Original",
+DlgImgBorder : "Borda",
+DlgImgHSpace : "Horizontal",
+DlgImgVSpace : "Vertical",
+DlgImgAlign : "Alinhamento",
+DlgImgAlignLeft : "Esquerda",
+DlgImgAlignAbsBottom: "Inferior Absoluto",
+DlgImgAlignAbsMiddle: "Centralizado Absoluto",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "Inferior",
+DlgImgAlignMiddle : "Centralizado",
+DlgImgAlignRight : "Direita",
+DlgImgAlignTextTop : "Superior Absoluto",
+DlgImgAlignTop : "Superior",
+DlgImgPreview : "Visualização",
+DlgImgAlertUrl : "Por favor, digite o URL da figura.",
+DlgImgLinkTab : "Hiperlink",
+
+// Flash Dialog
+DlgFlashTitle : "Propriedades Flash",
+DlgFlashChkPlay : "Tocar Automaticamente",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Habilita Menu Flash",
+DlgFlashScale : "Escala",
+DlgFlashScaleAll : "Mostrar tudo",
+DlgFlashScaleNoBorder : "Sem Borda",
+DlgFlashScaleFit : "Escala Exata",
+
+// Link Dialog
+DlgLnkWindowTitle : "Hiperlink",
+DlgLnkInfoTab : "Informações",
+DlgLnkTargetTab : "Destino",
+
+DlgLnkType : "Tipo de hiperlink",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Âncora nesta página",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocolo",
+DlgLnkProtoOther : "<outro>",
+DlgLnkURL : "URL do hiperlink",
+DlgLnkAnchorSel : "Selecione uma âncora",
+DlgLnkAnchorByName : "Pelo Nome da âncora",
+DlgLnkAnchorById : "Pelo Id do Elemento",
+DlgLnkNoAnchors : "(Não há âncoras disponíveis neste documento)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Endereço E-Mail",
+DlgLnkEMailSubject : "Assunto da Mensagem",
+DlgLnkEMailBody : "Corpo da Mensagem",
+DlgLnkUpload : "Enviar ao Servidor",
+DlgLnkBtnUpload : "Enviar ao Servidor",
+
+DlgLnkTarget : "Destino",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<janela popup>",
+DlgLnkTargetBlank : "Nova Janela (_blank)",
+DlgLnkTargetParent : "Janela Pai (_parent)",
+DlgLnkTargetSelf : "Mesma Janela (_self)",
+DlgLnkTargetTop : "Janela Superior (_top)",
+DlgLnkTargetFrameName : "Nome do Frame de Destino",
+DlgLnkPopWinName : "Nome da Janela Pop-up",
+DlgLnkPopWinFeat : "Atributos da Janela Pop-up",
+DlgLnkPopResize : "Redimensionável",
+DlgLnkPopLocation : "Barra de Endereços",
+DlgLnkPopMenu : "Barra de Menus",
+DlgLnkPopScroll : "Barras de Rolagem",
+DlgLnkPopStatus : "Barra de Status",
+DlgLnkPopToolbar : "Barra de Ferramentas",
+DlgLnkPopFullScrn : "Modo Tela Cheia (IE)",
+DlgLnkPopDependent : "Dependente (Netscape)",
+DlgLnkPopWidth : "Largura",
+DlgLnkPopHeight : "Altura",
+DlgLnkPopLeft : "Esquerda",
+DlgLnkPopTop : "Superior",
+
+DlnLnkMsgNoUrl : "Por favor, digite o endereço do Hiperlink",
+DlnLnkMsgNoEMail : "Por favor, digite o endereço de e-mail",
+DlnLnkMsgNoAnchor : "Por favor, selecione uma âncora",
+DlnLnkMsgInvPopName : "O nome da janela popup deve começar com uma letra ou sublinhado (_) e não pode conter espaços",
+
+// Color Dialog
+DlgColorTitle : "Selecione uma Cor",
+DlgColorBtnClear : "Limpar",
+DlgColorHighlight : "Visualização",
+DlgColorSelected : "Selecionada",
+
+// Smiley Dialog
+DlgSmileyTitle : "Inserir Emoticon",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Selecione um Caractere Especial",
+
+// Table Dialog
+DlgTableTitle : "Formatar Tabela",
+DlgTableRows : "Linhas",
+DlgTableColumns : "Colunas",
+DlgTableBorder : "Borda",
+DlgTableAlign : "Alinhamento",
+DlgTableAlignNotSet : "<Não ajustado>",
+DlgTableAlignLeft : "Esquerda",
+DlgTableAlignCenter : "Centralizado",
+DlgTableAlignRight : "Direita",
+DlgTableWidth : "Largura",
+DlgTableWidthPx : "pixels",
+DlgTableWidthPc : "%",
+DlgTableHeight : "Altura",
+DlgTableCellSpace : "Espaçamento",
+DlgTableCellPad : "Enchimento",
+DlgTableCaption : "Legenda",
+DlgTableSummary : "Resumo",
+
+// Table Cell Dialog
+DlgCellTitle : "Formatar célula",
+DlgCellWidth : "Largura",
+DlgCellWidthPx : "pixels",
+DlgCellWidthPc : "%",
+DlgCellHeight : "Altura",
+DlgCellWordWrap : "Quebra de Linha",
+DlgCellWordWrapNotSet : "<Não ajustado>",
+DlgCellWordWrapYes : "Sim",
+DlgCellWordWrapNo : "Não",
+DlgCellHorAlign : "Alinhamento Horizontal",
+DlgCellHorAlignNotSet : "<Não ajustado>",
+DlgCellHorAlignLeft : "Esquerda",
+DlgCellHorAlignCenter : "Centralizado",
+DlgCellHorAlignRight: "Direita",
+DlgCellVerAlign : "Alinhamento Vertical",
+DlgCellVerAlignNotSet : "<Não ajustado>",
+DlgCellVerAlignTop : "Superior",
+DlgCellVerAlignMiddle : "Centralizado",
+DlgCellVerAlignBottom : "Inferior",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Transpor Linhas",
+DlgCellCollSpan : "Transpor Colunas",
+DlgCellBackColor : "Cor do Plano de Fundo",
+DlgCellBorderColor : "Cor da Borda",
+DlgCellBtnSelect : "Selecionar...",
+
+// Find Dialog
+DlgFindTitle : "Localizar...",
+DlgFindFindBtn : "Localizar",
+DlgFindNotFoundMsg : "O texto especificado não foi encontrado.",
+
+// Replace Dialog
+DlgReplaceTitle : "Substituir",
+DlgReplaceFindLbl : "Procurar por:",
+DlgReplaceReplaceLbl : "Substituir por:",
+DlgReplaceCaseChk : "Coincidir Maiúsculas/Minúsculas",
+DlgReplaceReplaceBtn : "Substituir",
+DlgReplaceReplAllBtn : "Substituir Tudo",
+DlgReplaceWordChk : "Coincidir a palavra inteira",
+
+// Paste Operations / Dialog
+PasteErrorCut : "As configurações de segurança do seu navegador não permitem que o editor execute operações de recortar automaticamente. Por favor, utilize o teclado para recortar (Ctrl+X).",
+PasteErrorCopy : "As configurações de segurança do seu navegador não permitem que o editor execute operações de copiar automaticamente. Por favor, utilize o teclado para copiar (Ctrl+C).",
+
+PasteAsText : "Colar como Texto sem Formatação",
+PasteFromWord : "Colar do Word",
+
+DlgPasteMsg2 : "Transfira o link usado no box usando o teclado com (<STRONG>Ctrl+V</STRONG>) e <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorar definições de fonte",
+DlgPasteRemoveStyles : "Remove definições de estilo",
+DlgPasteCleanBox : "Limpar Box",
+
+// Color Picker
+ColorAutomatic : "Automático",
+ColorMoreColors : "Mais Cores...",
+
+// Document Properties
+DocProps : "Propriedades Documento",
+
+// Anchor Dialog
+DlgAnchorTitle : "Formatar Âncora",
+DlgAnchorName : "Nome da Âncora",
+DlgAnchorErrorName : "Por favor, digite o nome da âncora",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Não encontrada",
+DlgSpellChangeTo : "Alterar para",
+DlgSpellBtnIgnore : "Ignorar uma vez",
+DlgSpellBtnIgnoreAll : "Ignorar Todas",
+DlgSpellBtnReplace : "Alterar",
+DlgSpellBtnReplaceAll : "Alterar Todas",
+DlgSpellBtnUndo : "Desfazer",
+DlgSpellNoSuggestions : "-sem sugestões de ortografia-",
+DlgSpellProgress : "Verificação ortográfica em andamento...",
+DlgSpellNoMispell : "Verificação encerrada: Não foram encontrados erros de ortografia",
+DlgSpellNoChanges : "Verificação ortográfica encerrada: Não houve alterações",
+DlgSpellOneChange : "Verificação ortográfica encerrada: Uma palavra foi alterada",
+DlgSpellManyChanges : "Verificação ortográfica encerrada: %1 foram alteradas",
+
+IeSpellDownload : "A verificação ortográfica não foi instalada. Você gostaria de realizar o download agora?",
+
+// Button Dialog
+DlgButtonText : "Texto (Valor)",
+DlgButtonType : "Tipo",
+DlgButtonTypeBtn : "Botão",
+DlgButtonTypeSbm : "Enviar",
+DlgButtonTypeRst : "Limpar",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nome",
+DlgCheckboxValue : "Valor",
+DlgCheckboxSelected : "Selecionado",
+
+// Form Dialog
+DlgFormName : "Nome",
+DlgFormAction : "Action",
+DlgFormMethod : "Método",
+
+// Select Field Dialog
+DlgSelectName : "Nome",
+DlgSelectValue : "Valor",
+DlgSelectSize : "Tamanho",
+DlgSelectLines : "linhas",
+DlgSelectChkMulti : "Permitir múltiplas seleções",
+DlgSelectOpAvail : "Opções disponíveis",
+DlgSelectOpText : "Texto",
+DlgSelectOpValue : "Valor",
+DlgSelectBtnAdd : "Adicionar",
+DlgSelectBtnModify : "Modificar",
+DlgSelectBtnUp : "Para cima",
+DlgSelectBtnDown : "Para baixo",
+DlgSelectBtnSetValue : "Definir como selecionado",
+DlgSelectBtnDelete : "Remover",
+
+// Textarea Dialog
+DlgTextareaName : "Nome",
+DlgTextareaCols : "Colunas",
+DlgTextareaRows : "Linhas",
+
+// Text Field Dialog
+DlgTextName : "Nome",
+DlgTextValue : "Valor",
+DlgTextCharWidth : "Comprimento (em caracteres)",
+DlgTextMaxChars : "Número Máximo de Caracteres",
+DlgTextType : "Tipo",
+DlgTextTypeText : "Texto",
+DlgTextTypePass : "Senha",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nome",
+DlgHiddenValue : "Valor",
+
+// Bulleted List Dialog
+BulletedListProp : "Formatar Marcadores",
+NumberedListProp : "Formatar Numeração",
+DlgLstStart : "Iniciar",
+DlgLstType : "Tipo",
+DlgLstTypeCircle : "Círculo",
+DlgLstTypeDisc : "Disco",
+DlgLstTypeSquare : "Quadrado",
+DlgLstTypeNumbers : "Números (1, 2, 3)",
+DlgLstTypeLCase : "Letras Minúsculas (a, b, c)",
+DlgLstTypeUCase : "Letras Maiúsculas (A, B, C)",
+DlgLstTypeSRoman : "Números Romanos Minúsculos (i, ii, iii)",
+DlgLstTypeLRoman : "Números Romanos Maiúsculos (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Geral",
+DlgDocBackTab : "Plano de Fundo",
+DlgDocColorsTab : "Cores e Margens",
+DlgDocMetaTab : "Meta Dados",
+
+DlgDocPageTitle : "Título da Página",
+DlgDocLangDir : "Direção do Idioma",
+DlgDocLangDirLTR : "Esquerda para Direita (LTR)",
+DlgDocLangDirRTL : "Direita para Esquerda (RTL)",
+DlgDocLangCode : "Código do Idioma",
+DlgDocCharSet : "Codificação de Caracteres",
+DlgDocCharSetCE : "Europa Central",
+DlgDocCharSetCT : "Chinês Tradicional (Big5)",
+DlgDocCharSetCR : "Cirílico",
+DlgDocCharSetGR : "Grego",
+DlgDocCharSetJP : "Japonês",
+DlgDocCharSetKR : "Coreano",
+DlgDocCharSetTR : "Turco",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Europa Ocidental",
+DlgDocCharSetOther : "Outra Codificação de Caracteres",
+
+DlgDocDocType : "Cabeçalho Tipo de Documento",
+DlgDocDocTypeOther : "Other Document Type Heading",
+DlgDocIncXHTML : "Incluir Declarações XHTML",
+DlgDocBgColor : "Cor do Plano de Fundo",
+DlgDocBgImage : "URL da Imagem de Plano de Fundo",
+DlgDocBgNoScroll : "Plano de Fundo Fixo",
+DlgDocCText : "Texto",
+DlgDocCLink : "Hiperlink",
+DlgDocCVisited : "Hiperlink Visitado",
+DlgDocCActive : "Hiperlink Ativo",
+DlgDocMargins : "Margens da Página",
+DlgDocMaTop : "Superior",
+DlgDocMaLeft : "Inferior",
+DlgDocMaRight : "Direita",
+DlgDocMaBottom : "Inferior",
+DlgDocMeIndex : "Palavras-chave de Indexação do Documento (separadas por vírgula)",
+DlgDocMeDescr : "Descrição do Documento",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Direitos Autorais",
+DlgDocPreview : "Visualizar",
+
+// Templates Dialog
+Templates : "Modelos de layout",
+DlgTemplatesTitle : "Modelo de layout do conteúdo",
+DlgTemplatesSelMsg : "Selecione um modelo de layout para ser aberto no editor<br>(o conteúdo atual será perdido):",
+DlgTemplatesLoading : "Carregando a lista de modelos de layout. Aguarde...",
+DlgTemplatesNoTpl : "(Não foram definidos modelos de layout)",
+DlgTemplatesReplace : "Substituir o conteúdo atual",
+
+// About Dialog
+DlgAboutAboutTab : "Sobre",
+DlgAboutBrowserInfoTab : "Informações do Navegador",
+DlgAboutLicenseTab : "Licença",
+DlgAboutVersion : "versão",
+DlgAboutInfo : "Para maiores informações visite"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/pt.js b/httemplate/elements/fckeditor/editor/lang/pt.js
new file mode 100644
index 0000000..23bab35
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/pt.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Portuguese language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Fechar Barra",
+ToolbarExpand : "Expandir Barra",
+
+// Toolbar Items and Context Menu
+Save : "Guardar",
+NewPage : "Nova Página",
+Preview : "Pré-visualizar",
+Cut : "Cortar",
+Copy : "Copiar",
+Paste : "Colar",
+PasteText : "Colar como texto não formatado",
+PasteWord : "Colar do Word",
+Print : "Imprimir",
+SelectAll : "Seleccionar Tudo",
+RemoveFormat : "Eliminar Formato",
+InsertLinkLbl : "Hiperligação",
+InsertLink : "Inserir/Editar Hiperligação",
+RemoveLink : "Eliminar Hiperligação",
+Anchor : " Inserir/Editar Âncora",
+InsertImageLbl : "Imagem",
+InsertImage : "Inserir/Editar Imagem",
+InsertFlashLbl : "Flash",
+InsertFlash : "Inserir/Editar Flash",
+InsertTableLbl : "Tabela",
+InsertTable : "Inserir/Editar Tabela",
+InsertLineLbl : "Linha",
+InsertLine : "Inserir Linha Horizontal",
+InsertSpecialCharLbl: "Caracter Especial",
+InsertSpecialChar : "Inserir Caracter Especial",
+InsertSmileyLbl : "Emoticons",
+InsertSmiley : "Inserir Emoticons",
+About : "Acerca do FCKeditor",
+Bold : "Negrito",
+Italic : "Itálico",
+Underline : "Sublinhado",
+StrikeThrough : "Rasurado",
+Subscript : "Superior à Linha",
+Superscript : "Inferior à Linha",
+LeftJustify : "Alinhar à Esquerda",
+CenterJustify : "Alinhar ao Centro",
+RightJustify : "Alinhar à Direita",
+BlockJustify : "Justificado",
+DecreaseIndent : "Diminuir Avanço",
+IncreaseIndent : "Aumentar Avanço",
+Undo : "Anular",
+Redo : "Repetir",
+NumberedListLbl : "Numeração",
+NumberedList : "Inserir/Eliminar Numeração",
+BulletedListLbl : "Marcas",
+BulletedList : "Inserir/Eliminar Marcas",
+ShowTableBorders : "Mostrar Limites da Tabelas",
+ShowDetails : "Mostrar Parágrafo",
+Style : "Estilo",
+FontFormat : "Formato",
+Font : "Tipo de Letra",
+FontSize : "Tamanho",
+TextColor : "Cor do Texto",
+BGColor : "Cor de Fundo",
+Source : "Fonte",
+Find : "Procurar",
+Replace : "Substituir",
+SpellCheck : "Verificação Ortográfica",
+UniversalKeyboard : "Teclado Universal",
+PageBreakLbl : "Quebra de Página",
+PageBreak : "Inserir Quebra de Página",
+
+Form : "Formulário",
+Checkbox : "Caixa de Verificação",
+RadioButton : "Botão de Opção",
+TextField : "Campo de Texto",
+Textarea : "Ãrea de Texto",
+HiddenField : "Campo Escondido",
+Button : "Botão",
+SelectionField : "Caixa de Combinação",
+ImageButton : "Botão de Imagem",
+
+FitWindow : "Maximizar o tamanho do editor",
+
+// Context Menu
+EditLink : "Editar Hiperligação",
+CellCM : "Célula",
+RowCM : "Linha",
+ColumnCM : "Coluna",
+InsertRow : "Inserir Linha",
+DeleteRows : "Eliminar Linhas",
+InsertColumn : "Inserir Coluna",
+DeleteColumns : "Eliminar Coluna",
+InsertCell : "Inserir Célula",
+DeleteCells : "Eliminar Célula",
+MergeCells : "Unir Células",
+SplitCell : "Dividir Célula",
+TableDelete : "Eliminar Tabela",
+CellProperties : "Propriedades da Célula",
+TableProperties : "Propriedades da Tabela",
+ImageProperties : "Propriedades da Imagem",
+FlashProperties : "Propriedades do Flash",
+
+AnchorProp : "Propriedades da Âncora",
+ButtonProp : "Propriedades do Botão",
+CheckboxProp : "Propriedades da Caixa de Verificação",
+HiddenFieldProp : "Propriedades do Campo Escondido",
+RadioButtonProp : "Propriedades do Botão de Opção",
+ImageButtonProp : "Propriedades do Botão de imagens",
+TextFieldProp : "Propriedades do Campo de Texto",
+SelectionFieldProp : "Propriedades da Caixa de Combinação",
+TextareaProp : "Propriedades da Ãrea de Texto",
+FormProp : "Propriedades do Formulário",
+
+FontFormats : "Normal;Formatado;Endereço;Título 1;Título 2;Título 3;Título 4;Título 5;Título 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "A Processar XHTML. Por favor, espere...",
+Done : "Concluído",
+PasteWordConfirm : "O texto que deseja parece ter sido copiado do Word. Deseja limpar a formatação antes de colar?",
+NotCompatiblePaste : "Este comando só está disponível para Internet Explorer versão 5.5 ou superior. Deseja colar sem limpar a formatação?",
+UnknownToolbarItem : "Item de barra desconhecido \"%1\"",
+UnknownCommand : "Nome de comando desconhecido \"%1\"",
+NotImplemented : "Comando não implementado",
+UnknownToolbarSet : "Nome de barra \"%1\" não definido",
+NoActiveX : "As definições de segurança do navegador podem limitar algumas potencalidades do editr. Deve activar a opção \"Executar controlos e extensões ActiveX\". Pode ocorrer erros ou verificar que faltam potencialidades.",
+BrowseServerBlocked : "Não foi possível abrir o navegador de recursos. Certifique-se que todos os bloqueadores de popup estão desactivados.",
+DialogBlocked : "Não foi possível abrir a janela de diálogo. Certifique-se que todos os bloqueadores de popup estão desactivados.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Cancelar",
+DlgBtnClose : "Fechar",
+DlgBtnBrowseServer : "Navegar no Servidor",
+DlgAdvancedTag : "Avançado",
+DlgOpOther : "<Outro>",
+DlgInfoTab : "Informação",
+DlgAlertUrl : "Por favor introduza o URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<Não definido>",
+DlgGenId : "Id",
+DlgGenLangDir : "Orientação de idioma",
+DlgGenLangDirLtr : "Esquerda à Direita (LTR)",
+DlgGenLangDirRtl : "Direita a Esquerda (RTL)",
+DlgGenLangCode : "Código de Idioma",
+DlgGenAccessKey : "Chave de Acesso",
+DlgGenName : "Nome",
+DlgGenTabIndex : "Ãndice de Tubulação",
+DlgGenLongDescr : "Descrição Completa do URL",
+DlgGenClass : "Classes de Estilo de Folhas Classes",
+DlgGenTitle : "Título",
+DlgGenContType : "Tipo de Conteúdo",
+DlgGenLinkCharset : "Fonte de caracteres vinculado",
+DlgGenStyle : "Estilo",
+
+// Image Dialog
+DlgImgTitle : "Propriedades da Imagem",
+DlgImgInfoTab : "Informação da Imagem",
+DlgImgBtnUpload : "Enviar para o Servidor",
+DlgImgURL : "URL",
+DlgImgUpload : "Carregar",
+DlgImgAlt : "Texto Alternativo",
+DlgImgWidth : "Largura",
+DlgImgHeight : "Altura",
+DlgImgLockRatio : "Proporcional",
+DlgBtnResetSize : "Tamanho Original",
+DlgImgBorder : "Limite",
+DlgImgHSpace : "Esp.Horiz",
+DlgImgVSpace : "Esp.Vert",
+DlgImgAlign : "Alinhamento",
+DlgImgAlignLeft : "Esquerda",
+DlgImgAlignAbsBottom: "Abs inferior",
+DlgImgAlignAbsMiddle: "Abs centro",
+DlgImgAlignBaseline : "Linha de base",
+DlgImgAlignBottom : "Fundo",
+DlgImgAlignMiddle : "Centro",
+DlgImgAlignRight : "Direita",
+DlgImgAlignTextTop : "Topo do texto",
+DlgImgAlignTop : "Topo",
+DlgImgPreview : "Pré-visualizar",
+DlgImgAlertUrl : "Por favor introduza o URL da imagem",
+DlgImgLinkTab : "Hiperligação",
+
+// Flash Dialog
+DlgFlashTitle : "Propriedades do Flash",
+DlgFlashChkPlay : "Reproduzir automaticamente",
+DlgFlashChkLoop : "Loop",
+DlgFlashChkMenu : "Permitir Menu do Flash",
+DlgFlashScale : "Escala",
+DlgFlashScaleAll : "Mostrar tudo",
+DlgFlashScaleNoBorder : "Sem Limites",
+DlgFlashScaleFit : "Tamanho Exacto",
+
+// Link Dialog
+DlgLnkWindowTitle : "Hiperligação",
+DlgLnkInfoTab : "Informação de Hiperligação",
+DlgLnkTargetTab : "Destino",
+
+DlgLnkType : "Tipo de Hiperligação",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Referência a esta página",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocolo",
+DlgLnkProtoOther : "<outro>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Seleccionar una referência",
+DlgLnkAnchorByName : "Por Nome de Referência",
+DlgLnkAnchorById : "Por ID de elemento",
+DlgLnkNoAnchors : "<Não há referências disponíveis no documento>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Endereço de E-Mail",
+DlgLnkEMailSubject : "Título de Mensagem",
+DlgLnkEMailBody : "Corpo da Mensagem",
+DlgLnkUpload : "Carregar",
+DlgLnkBtnUpload : "Enviar ao Servidor",
+
+DlgLnkTarget : "Destino",
+DlgLnkTargetFrame : "<Frame>",
+DlgLnkTargetPopup : "<Janela de popup>",
+DlgLnkTargetBlank : "Nova Janela(_blank)",
+DlgLnkTargetParent : "Janela Pai (_parent)",
+DlgLnkTargetSelf : "Mesma janela (_self)",
+DlgLnkTargetTop : "Janela primaria (_top)",
+DlgLnkTargetFrameName : "Nome do Frame Destino",
+DlgLnkPopWinName : "Nome da Janela de Popup",
+DlgLnkPopWinFeat : "Características de Janela de Popup",
+DlgLnkPopResize : "Ajustável",
+DlgLnkPopLocation : "Barra de localização",
+DlgLnkPopMenu : "Barra de Menu",
+DlgLnkPopScroll : "Barras de deslocamento",
+DlgLnkPopStatus : "Barra de Estado",
+DlgLnkPopToolbar : "Barra de Ferramentas",
+DlgLnkPopFullScrn : "Janela Completa (IE)",
+DlgLnkPopDependent : "Dependente (Netscape)",
+DlgLnkPopWidth : "Largura",
+DlgLnkPopHeight : "Altura",
+DlgLnkPopLeft : "Posição Esquerda",
+DlgLnkPopTop : "Posição Direita",
+
+DlnLnkMsgNoUrl : "Por favor introduza a hiperligação URL",
+DlnLnkMsgNoEMail : "Por favor introduza o endereço de e-mail",
+DlnLnkMsgNoAnchor : "Por favor seleccione uma referência",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Seleccionar Cor",
+DlgColorBtnClear : "Nenhuma",
+DlgColorHighlight : "Destacado",
+DlgColorSelected : "Seleccionado",
+
+// Smiley Dialog
+DlgSmileyTitle : "Inserir um Emoticon",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Seleccione um caracter especial",
+
+// Table Dialog
+DlgTableTitle : "Propriedades da Tabela",
+DlgTableRows : "Linhas",
+DlgTableColumns : "Colunas",
+DlgTableBorder : "Tamanho do Limite",
+DlgTableAlign : "Alinhamento",
+DlgTableAlignNotSet : "<Não definido>",
+DlgTableAlignLeft : "Esquerda",
+DlgTableAlignCenter : "Centrado",
+DlgTableAlignRight : "Direita",
+DlgTableWidth : "Largura",
+DlgTableWidthPx : "pixeis",
+DlgTableWidthPc : "percentagem",
+DlgTableHeight : "Altura",
+DlgTableCellSpace : "Esp. e/células",
+DlgTableCellPad : "Esp. interior",
+DlgTableCaption : "Título",
+DlgTableSummary : "Sumário",
+
+// Table Cell Dialog
+DlgCellTitle : "Propriedades da Célula",
+DlgCellWidth : "Largura",
+DlgCellWidthPx : "pixeis",
+DlgCellWidthPc : "percentagem",
+DlgCellHeight : "Altura",
+DlgCellWordWrap : "Moldar Texto",
+DlgCellWordWrapNotSet : "<Não definido>",
+DlgCellWordWrapYes : "Sim",
+DlgCellWordWrapNo : "Não",
+DlgCellHorAlign : "Alinhamento Horizontal",
+DlgCellHorAlignNotSet : "<Não definido>",
+DlgCellHorAlignLeft : "Esquerda",
+DlgCellHorAlignCenter : "Centrado",
+DlgCellHorAlignRight: "Direita",
+DlgCellVerAlign : "Alinhamento Vertical",
+DlgCellVerAlignNotSet : "<Não definido>",
+DlgCellVerAlignTop : "Topo",
+DlgCellVerAlignMiddle : "Médio",
+DlgCellVerAlignBottom : "Fundi",
+DlgCellVerAlignBaseline : "Linha de Base",
+DlgCellRowSpan : "Unir Linhas",
+DlgCellCollSpan : "Unir Colunas",
+DlgCellBackColor : "Cor do Fundo",
+DlgCellBorderColor : "Cor do Limite",
+DlgCellBtnSelect : "Seleccione...",
+
+// Find Dialog
+DlgFindTitle : "Procurar",
+DlgFindFindBtn : "Procurar",
+DlgFindNotFoundMsg : "O texto especificado não foi encontrado.",
+
+// Replace Dialog
+DlgReplaceTitle : "Substituir",
+DlgReplaceFindLbl : "Texto a Procurar:",
+DlgReplaceReplaceLbl : "Substituir por:",
+DlgReplaceCaseChk : "Maiúsculas/Minúsculas",
+DlgReplaceReplaceBtn : "Substituir",
+DlgReplaceReplAllBtn : "Substituir Tudo",
+DlgReplaceWordChk : "Coincidir com toda a palavra",
+
+// Paste Operations / Dialog
+PasteErrorCut : "A configuração de segurança do navegador não permite a execução automática de operações de cortar. Por favor use o teclado (Ctrl+X).",
+PasteErrorCopy : "A configuração de segurança do navegador não permite a execução automática de operações de copiar. Por favor use o teclado (Ctrl+C).",
+
+PasteAsText : "Colar como Texto Simples",
+PasteFromWord : "Colar do Word",
+
+DlgPasteMsg2 : "Por favor, cole dentro da seguinte caixa usando o teclado (<STRONG>Ctrl+V</STRONG>) e prima <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorar da definições do Tipo de Letra ",
+DlgPasteRemoveStyles : "Remover as definições de Estilos",
+DlgPasteCleanBox : "Caixa de Limpeza",
+
+// Color Picker
+ColorAutomatic : "Automático",
+ColorMoreColors : "Mais Cores...",
+
+// Document Properties
+DocProps : "Propriedades do Documento",
+
+// Anchor Dialog
+DlgAnchorTitle : "Propriedades da Âncora",
+DlgAnchorName : "Nome da Âncora",
+DlgAnchorErrorName : "Por favor, introduza o nome da âncora",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Não está num directório",
+DlgSpellChangeTo : "Mudar para",
+DlgSpellBtnIgnore : "Ignorar",
+DlgSpellBtnIgnoreAll : "Ignorar Tudo",
+DlgSpellBtnReplace : "Substituir",
+DlgSpellBtnReplaceAll : "Substituir Tudo",
+DlgSpellBtnUndo : "Anular",
+DlgSpellNoSuggestions : "- Sem sugestões -",
+DlgSpellProgress : "Verificação ortográfica em progresso…",
+DlgSpellNoMispell : "Verificação ortográfica completa: não foram encontrados erros",
+DlgSpellNoChanges : "Verificação ortográfica completa: não houve alteração de palavras",
+DlgSpellOneChange : "Verificação ortográfica completa: uma palavra alterada",
+DlgSpellManyChanges : "Verificação ortográfica completa: %1 palavras alteradas",
+
+IeSpellDownload : " Verificação ortográfica não instalada. Quer descarregar agora?",
+
+// Button Dialog
+DlgButtonText : "Texto (Valor)",
+DlgButtonType : "Tipo",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nome",
+DlgCheckboxValue : "Valor",
+DlgCheckboxSelected : "Seleccionado",
+
+// Form Dialog
+DlgFormName : "Nome",
+DlgFormAction : "Acção",
+DlgFormMethod : "Método",
+
+// Select Field Dialog
+DlgSelectName : "Nome",
+DlgSelectValue : "Valor",
+DlgSelectSize : "Tamanho",
+DlgSelectLines : "linhas",
+DlgSelectChkMulti : "Permitir selecções múltiplas",
+DlgSelectOpAvail : "Opções Possíveis",
+DlgSelectOpText : "Texto",
+DlgSelectOpValue : "Valor",
+DlgSelectBtnAdd : "Adicionar",
+DlgSelectBtnModify : "Modificar",
+DlgSelectBtnUp : "Para cima",
+DlgSelectBtnDown : "Para baixo",
+DlgSelectBtnSetValue : "Definir um valor por defeito",
+DlgSelectBtnDelete : "Apagar",
+
+// Textarea Dialog
+DlgTextareaName : "Nome",
+DlgTextareaCols : "Colunas",
+DlgTextareaRows : "Linhas",
+
+// Text Field Dialog
+DlgTextName : "Nome",
+DlgTextValue : "Valor",
+DlgTextCharWidth : "Tamanho do caracter",
+DlgTextMaxChars : "Nr. Máximo de Caracteres",
+DlgTextType : "Tipo",
+DlgTextTypeText : "Texto",
+DlgTextTypePass : "Palavra-chave",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nome",
+DlgHiddenValue : "Valor",
+
+// Bulleted List Dialog
+BulletedListProp : "Propriedades da Marca",
+NumberedListProp : "Propriedades da Numeração",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tipo",
+DlgLstTypeCircle : "Circulo",
+DlgLstTypeDisc : "Disco",
+DlgLstTypeSquare : "Quadrado",
+DlgLstTypeNumbers : "Números (1, 2, 3)",
+DlgLstTypeLCase : "Letras Minúsculas (a, b, c)",
+DlgLstTypeUCase : "Letras Maiúsculas (A, B, C)",
+DlgLstTypeSRoman : "Numeração Romana em Minúsculas (i, ii, iii)",
+DlgLstTypeLRoman : "Numeração Romana em Maiúsculas (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Geral",
+DlgDocBackTab : "Fundo",
+DlgDocColorsTab : "Cores e Margens",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Título da Página",
+DlgDocLangDir : "Orientação de idioma",
+DlgDocLangDirLTR : "Esquerda à Direita (LTR)",
+DlgDocLangDirRTL : "Direita à Esquerda (RTL)",
+DlgDocLangCode : "Código de Idioma",
+DlgDocCharSet : "Codificação de Caracteres",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Outra Codificação de Caracteres",
+
+DlgDocDocType : "Tipo de Cabeçalho do Documento",
+DlgDocDocTypeOther : "Outro Tipo de Cabeçalho do Documento",
+DlgDocIncXHTML : "Incluir Declarações XHTML",
+DlgDocBgColor : "Cor de Fundo",
+DlgDocBgImage : "Caminho para a Imagem de Fundo",
+DlgDocBgNoScroll : "Fundo Fixo",
+DlgDocCText : "Texto",
+DlgDocCLink : "Hiperligação",
+DlgDocCVisited : "Hiperligação Visitada",
+DlgDocCActive : "Hiperligação Activa",
+DlgDocMargins : "Margem das Páginas",
+DlgDocMaTop : "Topo",
+DlgDocMaLeft : "Esquerda",
+DlgDocMaRight : "Direita",
+DlgDocMaBottom : "Fundo",
+DlgDocMeIndex : "Palavras de Indexação do Documento (separadas por virgula)",
+DlgDocMeDescr : "Descrição do Documento",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Direitos de Autor",
+DlgDocPreview : "Pré-visualizar",
+
+// Templates Dialog
+Templates : "Modelos",
+DlgTemplatesTitle : "Modelo de Conteúdo",
+DlgTemplatesSelMsg : "Por favor, seleccione o modelo a abrir no editor<br>(o conteúdo actual será perdido):",
+DlgTemplatesLoading : "A carregar a lista de modelos. Aguarde por favor...",
+DlgTemplatesNoTpl : "(Sem modelos definidos)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Acerca",
+DlgAboutBrowserInfoTab : "Informação do Nevegador",
+DlgAboutLicenseTab : "Licença",
+DlgAboutVersion : "versão",
+DlgAboutInfo : "Para mais informações por favor dirija-se a"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/ro.js b/httemplate/elements/fckeditor/editor/lang/ro.js
new file mode 100644
index 0000000..1f36961
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/ro.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Romanian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Ascunde bara cu opţiuni",
+ToolbarExpand : "Expandează bara cu opţiuni",
+
+// Toolbar Items and Context Menu
+Save : "Salvează",
+NewPage : "Pagină nouă",
+Preview : "Previzualizare",
+Cut : "Taie",
+Copy : "Copiază",
+Paste : "Adaugă",
+PasteText : "Adaugă ca text simplu",
+PasteWord : "Adaugă din Word",
+Print : "Printează",
+SelectAll : "Selectează tot",
+RemoveFormat : "Înlătură formatarea",
+InsertLinkLbl : "Link (Legătură web)",
+InsertLink : "Inserează/Editează link (legătură web)",
+RemoveLink : "Înlătură link (legătură web)",
+Anchor : "Inserează/Editează ancoră",
+InsertImageLbl : "Imagine",
+InsertImage : "Inserează/Editează imagine",
+InsertFlashLbl : "Flash",
+InsertFlash : "Inserează/Editează flash",
+InsertTableLbl : "Tabel",
+InsertTable : "Inserează/Editează tabel",
+InsertLineLbl : "Linie",
+InsertLine : "Inserează linie orizontă",
+InsertSpecialCharLbl: "Caracter special",
+InsertSpecialChar : "Inserează caracter special",
+InsertSmileyLbl : "Figură expresivă (Emoticon)",
+InsertSmiley : "Inserează Figură expresivă (Emoticon)",
+About : "Despre FCKeditor",
+Bold : "ÃŽngroÅŸat (bold)",
+Italic : "ÃŽnclinat (italic)",
+Underline : "Subliniat (underline)",
+StrikeThrough : "Tăiat (strike through)",
+Subscript : "Indice (subscript)",
+Superscript : "Putere (superscript)",
+LeftJustify : "Aliniere la stânga",
+CenterJustify : "Aliniere centrală",
+RightJustify : "Aliniere la dreapta",
+BlockJustify : "Aliniere în bloc (Block Justify)",
+DecreaseIndent : "Scade indentarea",
+IncreaseIndent : "CreÅŸte indentarea",
+Undo : "Starea anterioară (undo)",
+Redo : "Starea ulterioară (redo)",
+NumberedListLbl : "Listă numerotată",
+NumberedList : "Inserează/Şterge listă numerotată",
+BulletedListLbl : "Listă cu puncte",
+BulletedList : "Inserează/Şterge listă cu puncte",
+ShowTableBorders : "Arată marginile tabelului",
+ShowDetails : "Arată detalii",
+Style : "Stil",
+FontFormat : "Formatare",
+Font : "Font",
+FontSize : "Mărime",
+TextColor : "Culoarea textului",
+BGColor : "Coloarea fundalului",
+Source : "Sursa",
+Find : "Găseşte",
+Replace : "ÃŽnlocuieÅŸte",
+SpellCheck : "Verifică text",
+UniversalKeyboard : "Tastatură universală",
+PageBreakLbl : "Separator de pagină (Page Break)",
+PageBreak : "Inserează separator de pagină (Page Break)",
+
+Form : "Formular (Form)",
+Checkbox : "Bifă (Checkbox)",
+RadioButton : "Buton radio (RadioButton)",
+TextField : "Câmp text (TextField)",
+Textarea : "Suprafaţă text (Textarea)",
+HiddenField : "Câmp ascuns (HiddenField)",
+Button : "Buton",
+SelectionField : "Câmp selecţie (SelectionField)",
+ImageButton : "Buton imagine (ImageButton)",
+
+FitWindow : "Maximizează mărimea editorului",
+
+// Context Menu
+EditLink : "Editează Link",
+CellCM : "Celulă",
+RowCM : "Linie",
+ColumnCM : "Coloană",
+InsertRow : "Inserează linie",
+DeleteRows : "Åžterge linii",
+InsertColumn : "Inserează coloană",
+DeleteColumns : "Åžterge celule",
+InsertCell : "Inserează celulă",
+DeleteCells : "Åžterge celule",
+MergeCells : "UneÅŸte celule",
+SplitCell : "Împarte celulă",
+TableDelete : "Åžterge tabel",
+CellProperties : "Proprietăţile celulei",
+TableProperties : "Proprietăţile tabelului",
+ImageProperties : "Proprietăţile imaginii",
+FlashProperties : "Proprietăţile flash-ului",
+
+AnchorProp : "Proprietăţi ancoră",
+ButtonProp : "Proprietăţi buton",
+CheckboxProp : "Proprietăţi bifă (Checkbox)",
+HiddenFieldProp : "Proprietăţi câmp ascuns (Hidden Field)",
+RadioButtonProp : "Proprietăţi buton radio (Radio Button)",
+ImageButtonProp : "Proprietăţi buton imagine (Image Button)",
+TextFieldProp : "Proprietăţi câmp text (Text Field)",
+SelectionFieldProp : "Proprietăţi câmp selecţie (Selection Field)",
+TextareaProp : "Proprietăţi suprafaţă text (Textarea)",
+FormProp : "Proprietăţi formular (Form)",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html //MISSING
+
+// Alerts and Messages
+ProcessingXHTML : "Procesăm XHTML. Vă rugăm aşteptaţi...",
+Done : "Am terminat",
+PasteWordConfirm : "Textul pe care doriţi să-l adăugaţi pare a fi formatat pentru Word. Doriţi să-l curăţaţi de această formatare înainte de a-l adăuga?",
+NotCompatiblePaste : "Această facilitate e disponibilă doar pentru Microsoft Internet Explorer, versiunea 5.5 sau ulterioară. Vreţi să-l adăugaţi fără a-i fi înlăturat formatarea?",
+UnknownToolbarItem : "Obiectul \"%1\" din bara cu opţiuni necunoscut",
+UnknownCommand : "Comanda \"%1\" necunoscută",
+NotImplemented : "Comandă neimplementată",
+UnknownToolbarSet : "Grupul din bara cu opţiuni \"%1\" nu există",
+NoActiveX : "Setările de securitate ale programului dvs. cu care navigaţi pe internet (browser) pot limita anumite funcţionalităţi ale editorului. Pentru a evita asta, trebuie să activaţi opţiunea \"Run ActiveX controls and plug-ins\". Poate veţi întâlni erori sau veţi observa funcţionalităţi lipsă.",
+BrowseServerBlocked : "The resources browser could not be opened. Asiguraţi-vă că nu e activ niciun \"popup blocker\" (funcţionalitate a programului de navigat (browser) sau a unui plug-in al acestuia de a bloca deschiderea unui noi ferestre).",
+DialogBlocked : "Nu a fost posibilă deschiderea unei ferestre de dialog. Asiguraţi-vă că nu e activ niciun \"popup blocker\" (funcţionalitate a programului de navigat (browser) sau a unui plug-in al acestuia de a bloca deschiderea unui noi ferestre).",
+
+// Dialogs
+DlgBtnOK : "Bine",
+DlgBtnCancel : "Anulare",
+DlgBtnClose : "ÃŽnchidere",
+DlgBtnBrowseServer : "Răsfoieşte server",
+DlgAdvancedTag : "Avansat",
+DlgOpOther : "<Altul>",
+DlgInfoTab : "Informaţii",
+DlgAlertUrl : "Vă rugăm să scrieţi URL-ul",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nesetat>",
+DlgGenId : "Id",
+DlgGenLangDir : "Direcţia cuvintelor",
+DlgGenLangDirLtr : "stânga-dreapta (LTR)",
+DlgGenLangDirRtl : "dreapta-stânga (RTL)",
+DlgGenLangCode : "Codul limbii",
+DlgGenAccessKey : "Tasta de acces",
+DlgGenName : "Nume",
+DlgGenTabIndex : "Indexul tabului",
+DlgGenLongDescr : "Descrierea lungă URL",
+DlgGenClass : "Clasele cu stilul paginii (CSS)",
+DlgGenTitle : "Titlul consultativ",
+DlgGenContType : "Tipul consultativ al titlului",
+DlgGenLinkCharset : "Setul de caractere al resursei legate",
+DlgGenStyle : "Stil",
+
+// Image Dialog
+DlgImgTitle : "Proprietăţile imaginii",
+DlgImgInfoTab : "Informaţii despre imagine",
+DlgImgBtnUpload : "Trimite la server",
+DlgImgURL : "URL",
+DlgImgUpload : "Încarcă",
+DlgImgAlt : "Text alternativ",
+DlgImgWidth : "Lăţime",
+DlgImgHeight : "Înălţime",
+DlgImgLockRatio : "Păstrează proporţiile",
+DlgBtnResetSize : "Resetează mărimea",
+DlgImgBorder : "Margine",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Aliniere",
+DlgImgAlignLeft : "Stânga",
+DlgImgAlignAbsBottom: "Jos absolut (Abs Bottom)",
+DlgImgAlignAbsMiddle: "Mijloc absolut (Abs Middle)",
+DlgImgAlignBaseline : "Linia de jos (Baseline)",
+DlgImgAlignBottom : "Jos",
+DlgImgAlignMiddle : "Mijloc",
+DlgImgAlignRight : "Dreapta",
+DlgImgAlignTextTop : "Text sus",
+DlgImgAlignTop : "Sus",
+DlgImgPreview : "Previzualizare",
+DlgImgAlertUrl : "Vă rugăm să scrieţi URL-ul imaginii",
+DlgImgLinkTab : "Link (Legătură web)",
+
+// Flash Dialog
+DlgFlashTitle : "Proprietăţile flash-ului",
+DlgFlashChkPlay : "Rulează automat",
+DlgFlashChkLoop : "Repetă (Loop)",
+DlgFlashChkMenu : "Activează meniul flash",
+DlgFlashScale : "Scală",
+DlgFlashScaleAll : "Arată tot",
+DlgFlashScaleNoBorder : "Fără margini (No border)",
+DlgFlashScaleFit : "PotriveÅŸte",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link (Legătură web)",
+DlgLnkInfoTab : "Informaţii despre link (Legătură web)",
+DlgLnkTargetTab : "Ţintă (Target)",
+
+DlgLnkType : "Tipul link-ului (al legăturii web)",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Ancoră în această pagină",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protocol",
+DlgLnkProtoOther : "<altul>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Selectaţi o ancoră",
+DlgLnkAnchorByName : "după numele ancorei",
+DlgLnkAnchorById : "după Id-ul elementului",
+DlgLnkNoAnchors : "<Nicio ancoră disponibilă în document>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Adresă de e-mail",
+DlgLnkEMailSubject : "Subiectul mesajului",
+DlgLnkEMailBody : "Conţinutul mesajului",
+DlgLnkUpload : "Încarcă",
+DlgLnkBtnUpload : "Trimite la server",
+
+DlgLnkTarget : "Ţintă (Target)",
+DlgLnkTargetFrame : "<frame>",
+DlgLnkTargetPopup : "<fereastra popup>",
+DlgLnkTargetBlank : "Fereastră nouă (_blank)",
+DlgLnkTargetParent : "Fereastra părinte (_parent)",
+DlgLnkTargetSelf : "Aceeaşi fereastră (_self)",
+DlgLnkTargetTop : "Fereastra din topul ierarhiei (_top)",
+DlgLnkTargetFrameName : "Numele frame-ului ţintă",
+DlgLnkPopWinName : "Numele ferestrei popup",
+DlgLnkPopWinFeat : "Proprietăţile ferestrei popup",
+DlgLnkPopResize : "Scalabilă",
+DlgLnkPopLocation : "Bara de locaţie",
+DlgLnkPopMenu : "Bara de meniu",
+DlgLnkPopScroll : "Scroll Bars",
+DlgLnkPopStatus : "Bara de status",
+DlgLnkPopToolbar : "Bara de opţiuni",
+DlgLnkPopFullScrn : "Tot ecranul (Full Screen)(IE)",
+DlgLnkPopDependent : "Dependent (Netscape)",
+DlgLnkPopWidth : "Lăţime",
+DlgLnkPopHeight : "Înălţime",
+DlgLnkPopLeft : "Poziţia la stânga",
+DlgLnkPopTop : "Poziţia la dreapta",
+
+DlnLnkMsgNoUrl : "Vă rugăm să scrieţi URL-ul",
+DlnLnkMsgNoEMail : "Vă rugăm să scrieţi adresa de e-mail",
+DlnLnkMsgNoAnchor : "Vă rugăm să selectaţi o ancoră",
+DlnLnkMsgInvPopName : "Numele 'popup'-ului trebuie să înceapă cu un caracter alfabetic şi trebuie să nu conţină spaţii",
+
+// Color Dialog
+DlgColorTitle : "Selectează culoare",
+DlgColorBtnClear : "Curăţă",
+DlgColorHighlight : "Subliniază (Highlight)",
+DlgColorSelected : "Selectat",
+
+// Smiley Dialog
+DlgSmileyTitle : "Inserează o figură expresivă (Emoticon)",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Selectează caracter special",
+
+// Table Dialog
+DlgTableTitle : "Proprietăţile tabelului",
+DlgTableRows : "Linii",
+DlgTableColumns : "Coloane",
+DlgTableBorder : "Mărimea marginii",
+DlgTableAlign : "Aliniament",
+DlgTableAlignNotSet : "<Nesetat>",
+DlgTableAlignLeft : "Stânga",
+DlgTableAlignCenter : "Centru",
+DlgTableAlignRight : "Dreapta",
+DlgTableWidth : "Lăţime",
+DlgTableWidthPx : "pixeli",
+DlgTableWidthPc : "procente",
+DlgTableHeight : "Înălţime",
+DlgTableCellSpace : "Spaţiu între celule",
+DlgTableCellPad : "Spaţiu în cadrul celulei",
+DlgTableCaption : "Titlu (Caption)",
+DlgTableSummary : "Rezumat",
+
+// Table Cell Dialog
+DlgCellTitle : "Proprietăţile celulei",
+DlgCellWidth : "Lăţime",
+DlgCellWidthPx : "pixeli",
+DlgCellWidthPc : "procente",
+DlgCellHeight : "Înălţime",
+DlgCellWordWrap : "Desparte cuvintele (Wrap)",
+DlgCellWordWrapNotSet : "<Nesetat>",
+DlgCellWordWrapYes : "Da",
+DlgCellWordWrapNo : "Nu",
+DlgCellHorAlign : "Aliniament orizontal",
+DlgCellHorAlignNotSet : "<Nesetat>",
+DlgCellHorAlignLeft : "Stânga",
+DlgCellHorAlignCenter : "Centru",
+DlgCellHorAlignRight: "Dreapta",
+DlgCellVerAlign : "Aliniament vertical",
+DlgCellVerAlignNotSet : "<Nesetat>",
+DlgCellVerAlignTop : "Sus",
+DlgCellVerAlignMiddle : "Mijloc",
+DlgCellVerAlignBottom : "Jos",
+DlgCellVerAlignBaseline : "Linia de jos (Baseline)",
+DlgCellRowSpan : "Lungimea în linii (Span)",
+DlgCellCollSpan : "Lungimea în coloane (Span)",
+DlgCellBackColor : "Culoarea fundalului",
+DlgCellBorderColor : "Culoarea marginii",
+DlgCellBtnSelect : "Selectaţi...",
+
+// Find Dialog
+DlgFindTitle : "Găseşte",
+DlgFindFindBtn : "Găseşte",
+DlgFindNotFoundMsg : "Textul specificat nu a fost găsit.",
+
+// Replace Dialog
+DlgReplaceTitle : "Replace",
+DlgReplaceFindLbl : "Găseşte:",
+DlgReplaceReplaceLbl : "ÃŽnlocuieÅŸte cu:",
+DlgReplaceCaseChk : "DeosebeÅŸte majuscule de minuscule (Match case)",
+DlgReplaceReplaceBtn : "ÃŽnlocuieÅŸte",
+DlgReplaceReplAllBtn : "ÃŽnlocuieÅŸte tot",
+DlgReplaceWordChk : "Doar cuvintele întregi",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Setările de securitate ale navigatorului (browser) pe care îl folosiţi nu permit editorului să execute automat operaţiunea de tăiere. Vă rugăm folosiţi tastatura (Ctrl+X).",
+PasteErrorCopy : "Setările de securitate ale navigatorului (browser) pe care îl folosiţi nu permit editorului să execute automat operaţiunea de copiere. Vă rugăm folosiţi tastatura (Ctrl+C).",
+
+PasteAsText : "Adaugă ca text simplu (Plain Text)",
+PasteFromWord : "Adaugă din Word",
+
+DlgPasteMsg2 : "Vă rugăm adăugaţi în căsuţa următoare folosind tastatura (<STRONG>Ctrl+V</STRONG>) şi apăsaţi <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignoră definiţiile Font Face",
+DlgPasteRemoveStyles : "Şterge definiţiile stilurilor",
+DlgPasteCleanBox : "Şterge căsuţa",
+
+// Color Picker
+ColorAutomatic : "Automatic",
+ColorMoreColors : "Mai multe culori...",
+
+// Document Properties
+DocProps : "Proprietăţile documentului",
+
+// Anchor Dialog
+DlgAnchorTitle : "Proprietăţile ancorei",
+DlgAnchorName : "Numele ancorei",
+DlgAnchorErrorName : "Vă rugăm scrieţi numele ancorei",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Nu e în dicţionar",
+DlgSpellChangeTo : "Schimbă în",
+DlgSpellBtnIgnore : "Ignoră",
+DlgSpellBtnIgnoreAll : "Ignoră toate",
+DlgSpellBtnReplace : "ÃŽnlocuieÅŸte",
+DlgSpellBtnReplaceAll : "ÃŽnlocuieÅŸte tot",
+DlgSpellBtnUndo : "Starea anterioară (undo)",
+DlgSpellNoSuggestions : "- Fără sugestii -",
+DlgSpellProgress : "Verificarea textului în desfăşurare...",
+DlgSpellNoMispell : "Verificarea textului terminată: Nicio greşeală găsită",
+DlgSpellNoChanges : "Verificarea textului terminată: Niciun cuvânt modificat",
+DlgSpellOneChange : "Verificarea textului terminată: Un cuvânt modificat",
+DlgSpellManyChanges : "Verificarea textului terminată: 1% cuvinte modificate",
+
+IeSpellDownload : "Unealta pentru verificat textul (Spell checker) neinstalată. Doriţi să o descărcaţi acum?",
+
+// Button Dialog
+DlgButtonText : "Text (Valoare)",
+DlgButtonType : "Tip",
+DlgButtonTypeBtn : "Button",
+DlgButtonTypeSbm : "Submit",
+DlgButtonTypeRst : "Reset",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Nume",
+DlgCheckboxValue : "Valoare",
+DlgCheckboxSelected : "Selectat",
+
+// Form Dialog
+DlgFormName : "Nume",
+DlgFormAction : "Acţiune",
+DlgFormMethod : "Metodă",
+
+// Select Field Dialog
+DlgSelectName : "Nume",
+DlgSelectValue : "Valoare",
+DlgSelectSize : "Mărime",
+DlgSelectLines : "linii",
+DlgSelectChkMulti : "Permite selecţii multiple",
+DlgSelectOpAvail : "Opţiuni disponibile",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Valoare",
+DlgSelectBtnAdd : "Adaugă",
+DlgSelectBtnModify : "Modifică",
+DlgSelectBtnUp : "Sus",
+DlgSelectBtnDown : "Jos",
+DlgSelectBtnSetValue : "Setează ca valoare selectată",
+DlgSelectBtnDelete : "Åžterge",
+
+// Textarea Dialog
+DlgTextareaName : "Nume",
+DlgTextareaCols : "Coloane",
+DlgTextareaRows : "Linii",
+
+// Text Field Dialog
+DlgTextName : "Nume",
+DlgTextValue : "Valoare",
+DlgTextCharWidth : "Lărgimea caracterului",
+DlgTextMaxChars : "Caractere maxime",
+DlgTextType : "Tip",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Parolă",
+
+// Hidden Field Dialog
+DlgHiddenName : "Nume",
+DlgHiddenValue : "Valoare",
+
+// Bulleted List Dialog
+BulletedListProp : "Proprietăţile listei punctate (Bulleted List)",
+NumberedListProp : "Proprietăţile listei numerotate (Numbered List)",
+DlgLstStart : "Start",
+DlgLstType : "Tip",
+DlgLstTypeCircle : "Cerc",
+DlgLstTypeDisc : "Disc",
+DlgLstTypeSquare : "Pătrat",
+DlgLstTypeNumbers : "Numere (1, 2, 3)",
+DlgLstTypeLCase : "Minuscule-litere mici (a, b, c)",
+DlgLstTypeUCase : "Majuscule (A, B, C)",
+DlgLstTypeSRoman : "Cifre romane mici (i, ii, iii)",
+DlgLstTypeLRoman : "Cifre romane mari (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "General",
+DlgDocBackTab : "Fundal",
+DlgDocColorsTab : "Culori si margini",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Titlul paginii",
+DlgDocLangDir : "Descrierea limbii",
+DlgDocLangDirLTR : "stânga-dreapta (LTR)",
+DlgDocLangDirRTL : "dreapta-stânga (RTL)",
+DlgDocLangCode : "Codul limbii",
+DlgDocCharSet : "Encoding setului de caractere",
+DlgDocCharSetCE : "Central european",
+DlgDocCharSetCT : "Chinezesc tradiţional (Big5)",
+DlgDocCharSetCR : "Chirilic",
+DlgDocCharSetGR : "Grecesc",
+DlgDocCharSetJP : "Japonez",
+DlgDocCharSetKR : "Corean",
+DlgDocCharSetTR : "Turcesc",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Vest european",
+DlgDocCharSetOther : "Alt encoding al setului de caractere",
+
+DlgDocDocType : "Document Type Heading",
+DlgDocDocTypeOther : "Alt Document Type Heading",
+DlgDocIncXHTML : "Include declaraţii XHTML",
+DlgDocBgColor : "Culoarea fundalului (Background Color)",
+DlgDocBgImage : "URL-ul imaginii din fundal (Background Image URL)",
+DlgDocBgNoScroll : "Fundal neflotant, fix (Nonscrolling Background)",
+DlgDocCText : "Text",
+DlgDocCLink : "Link (Legătură web)",
+DlgDocCVisited : "Link (Legătură web) vizitat",
+DlgDocCActive : "Link (Legătură web) activ",
+DlgDocMargins : "Marginile paginii",
+DlgDocMaTop : "Sus",
+DlgDocMaLeft : "Stânga",
+DlgDocMaRight : "Dreapta",
+DlgDocMaBottom : "Jos",
+DlgDocMeIndex : "Cuvinte cheie după care se va indexa documentul (separate prin virgulă)",
+DlgDocMeDescr : "Descrierea documentului",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Drepturi de autor",
+DlgDocPreview : "Previzualizare",
+
+// Templates Dialog
+Templates : "Template-uri (ÅŸabloane)",
+DlgTemplatesTitle : "Template-uri (şabloane) de conţinut",
+DlgTemplatesSelMsg : "Vă rugăm selectaţi template-ul (şablonul) ce se va deschide în editor<br>(conţinutul actual va fi pierdut):",
+DlgTemplatesLoading : "Se încarcă lista cu template-uri (şabloane). Vă rugăm aşteptaţi...",
+DlgTemplatesNoTpl : "(Niciun template (ÅŸablon) definit)",
+DlgTemplatesReplace : "ÃŽnlocuieÅŸte cuprinsul actual",
+
+// About Dialog
+DlgAboutAboutTab : "Despre",
+DlgAboutBrowserInfoTab : "Informaţii browser",
+DlgAboutLicenseTab : "Licenţă",
+DlgAboutVersion : "versiune",
+DlgAboutInfo : "Pentru informaţii amănunţite, vizitaţi"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/ru.js b/httemplate/elements/fckeditor/editor/lang/ru.js
new file mode 100644
index 0000000..fdf151b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/ru.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Russian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Свернуть панель инÑтрументов",
+ToolbarExpand : "Развернуть панель инÑтрументов",
+
+// Toolbar Items and Context Menu
+Save : "Сохранить",
+NewPage : "ÐÐ¾Ð²Ð°Ñ Ñтраница",
+Preview : "Предварительный проÑмотр",
+Cut : "Вырезать",
+Copy : "Копировать",
+Paste : "Ð’Ñтавить",
+PasteText : "Ð’Ñтавить только текÑÑ‚",
+PasteWord : "Ð’Ñтавить из Word",
+Print : "Печать",
+SelectAll : "Выделить вÑе",
+RemoveFormat : "Убрать форматирование",
+InsertLinkLbl : "СÑылка",
+InsertLink : "Ð’Ñтавить/Редактировать ÑÑылку",
+RemoveLink : "Убрать ÑÑылку",
+Anchor : "Ð’Ñтавить/Редактировать Ñкорь",
+InsertImageLbl : "Изображение",
+InsertImage : "Ð’Ñтавить/Редактировать изображение",
+InsertFlashLbl : "Flash",
+InsertFlash : "Ð’Ñтавить/Редактировать Flash",
+InsertTableLbl : "Таблица",
+InsertTable : "Ð’Ñтавить/Редактировать таблицу",
+InsertLineLbl : "ЛиниÑ",
+InsertLine : "Ð’Ñтавить горизонтальную линию",
+InsertSpecialCharLbl: "Специальный Ñимвол",
+InsertSpecialChar : "Ð’Ñтавить Ñпециальный Ñимвол",
+InsertSmileyLbl : "Смайлик",
+InsertSmiley : "Ð’Ñтавить Ñмайлик",
+About : "О FCKeditor",
+Bold : "Жирный",
+Italic : "КурÑив",
+Underline : "Подчеркнутый",
+StrikeThrough : "Зачеркнутый",
+Subscript : "ПодÑтрочный индекÑ",
+Superscript : "ÐадÑтрочный индекÑ",
+LeftJustify : "По левому краю",
+CenterJustify : "По центру",
+RightJustify : "По правому краю",
+BlockJustify : "По ширине",
+DecreaseIndent : "Уменьшить отÑтуп",
+IncreaseIndent : "Увеличить отÑтуп",
+Undo : "Отменить",
+Redo : "Повторить",
+NumberedListLbl : "Ðумерованный ÑпиÑок",
+NumberedList : "Ð’Ñтавить/Удалить нумерованный ÑпиÑок",
+BulletedListLbl : "Маркированный ÑпиÑок",
+BulletedList : "Ð’Ñтавить/Удалить маркированный ÑпиÑок",
+ShowTableBorders : "Показать бордюры таблицы",
+ShowDetails : "Показать детали",
+Style : "Стиль",
+FontFormat : "Форматирование",
+Font : "Шрифт",
+FontSize : "Размер",
+TextColor : "Цвет текÑта",
+BGColor : "Цвет фона",
+Source : "ИÑточник",
+Find : "Ðайти",
+Replace : "Заменить",
+SpellCheck : "Проверить орфографию",
+UniversalKeyboard : "УниверÑÐ°Ð»ÑŒÐ½Ð°Ñ ÐºÐ»Ð°Ð²Ð¸Ð°Ñ‚ÑƒÑ€Ð°",
+PageBreakLbl : "Разрыв Ñтраницы",
+PageBreak : "Ð’Ñтавить разрыв Ñтраницы",
+
+Form : "Форма",
+Checkbox : "Ð¤Ð»Ð°Ð³Ð¾Ð²Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°",
+RadioButton : "Кнопка выбора",
+TextField : "ТекÑтовое поле",
+Textarea : "ТекÑÑ‚Ð¾Ð²Ð°Ñ Ð¾Ð±Ð»Ð°ÑÑ‚ÑŒ",
+HiddenField : "Скрытое поле",
+Button : "Кнопка",
+SelectionField : "СпиÑок",
+ImageButton : "Кнопка Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸ÐµÐ¼",
+
+FitWindow : "Развернуть окно редактора",
+
+// Context Menu
+EditLink : "Ð’Ñтавить ÑÑылку",
+CellCM : "Ячейка",
+RowCM : "Строка",
+ColumnCM : "Колонка",
+InsertRow : "Ð’Ñтавить Ñтроку",
+DeleteRows : "Удалить Ñтроки",
+InsertColumn : "Ð’Ñтавить колонку",
+DeleteColumns : "Удалить колонки",
+InsertCell : "Ð’Ñтавить Ñчейку",
+DeleteCells : "Удалить Ñчейки",
+MergeCells : "Соединить Ñчейки",
+SplitCell : "Разбить Ñчейку",
+TableDelete : "Удалить таблицу",
+CellProperties : "СвойÑтва Ñчейки",
+TableProperties : "СвойÑтва таблицы",
+ImageProperties : "СвойÑтва изображениÑ",
+FlashProperties : "СвойÑтва Flash",
+
+AnchorProp : "СвойÑтва ÑкорÑ",
+ButtonProp : "СвойÑтва кнопки",
+CheckboxProp : "СвойÑтва флаговой кнопки",
+HiddenFieldProp : "СвойÑтва Ñкрытого полÑ",
+RadioButtonProp : "СвойÑтва кнопки выбора",
+ImageButtonProp : "СвойÑтва кнопки Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸ÐµÐ¼",
+TextFieldProp : "СвойÑтва текÑтового полÑ",
+SelectionFieldProp : "СвойÑтва ÑпиÑка",
+TextareaProp : "СвойÑтва текÑтовой облаÑти",
+FormProp : "СвойÑтва формы",
+
+FontFormats : "Ðормальный;Форматированный;ÐдреÑ;Заголовок 1;Заголовок 2;Заголовок 3;Заголовок 4;Заголовок 5;Заголовок 6;Ðормальный (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Обработка XHTML. ПожалуйÑта подождите...",
+Done : "Сделано",
+PasteWordConfirm : "ТекÑÑ‚, который вы хотите вÑтавить, похож на копируемый из Word. Ð’Ñ‹ хотите очиÑтить его перед вÑтавкой?",
+NotCompatiblePaste : "Эта команда доÑтупна Ð´Ð»Ñ Internet Explorer верÑии 5.5 или выше. Ð’Ñ‹ хотите вÑтавить без очиÑтки?",
+UnknownToolbarItem : "Ðе извеÑтный Ñлемент панели инÑтрументов \"%1\"",
+UnknownCommand : "Ðе извеÑтное Ð¸Ð¼Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹ \"%1\"",
+NotImplemented : "Команда не реализована",
+UnknownToolbarSet : "Панель инÑтрументов \"%1\" не ÑущеÑтвует",
+NoActiveX : "ÐаÑтройки безопаÑноÑти вашего браузера могут ограничивать некоторые ÑвойÑтва редактора. Ð’Ñ‹ должны включить опцию \"ЗапуÑкать Ñлементы ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ActiveX и плугины\". Ð’Ñ‹ можете видеть ошибки и замечать отÑутÑтвие возможноÑтей.",
+BrowseServerBlocked : "РеÑурÑÑ‹ браузера не могут быть открыты. Проверьте что блокировки вÑплывающих окон выключены.",
+DialogBlocked : "Ðе возможно открыть окно диалога. Проверьте что блокировки вÑплывающих окон выключены.",
+
+// Dialogs
+DlgBtnOK : "ОК",
+DlgBtnCancel : "Отмена",
+DlgBtnClose : "Закрыть",
+DlgBtnBrowseServer : "ПроÑмотреть на Ñервере",
+DlgAdvancedTag : "РаÑширенный",
+DlgOpOther : "<Другое>",
+DlgInfoTab : "ИнформациÑ",
+DlgAlertUrl : "ПожалуйÑта вÑтавьте URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<не определено>",
+DlgGenId : "Идентификатор",
+DlgGenLangDir : "Ðаправление Ñзыка",
+DlgGenLangDirLtr : "Слева на право (LTR)",
+DlgGenLangDirRtl : "Справа на лево (RTL)",
+DlgGenLangCode : "Язык",
+DlgGenAccessKey : "ГорÑÑ‡Ð°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ°",
+DlgGenName : "ИмÑ",
+DlgGenTabIndex : "ПоÑледовательноÑÑ‚ÑŒ перехода",
+DlgGenLongDescr : "Длинное опиÑание URL",
+DlgGenClass : "КлаÑÑ CSS",
+DlgGenTitle : "Заголовок",
+DlgGenContType : "Тип Ñодержимого",
+DlgGenLinkCharset : "Кодировка",
+DlgGenStyle : "Стиль CSS",
+
+// Image Dialog
+DlgImgTitle : "СвойÑтва изображениÑ",
+DlgImgInfoTab : "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ изображении",
+DlgImgBtnUpload : "ПоÑлать на Ñервер",
+DlgImgURL : "URL",
+DlgImgUpload : "Закачать",
+DlgImgAlt : "Ðльтернативный текÑÑ‚",
+DlgImgWidth : "Ширина",
+DlgImgHeight : "Ð’Ñ‹Ñота",
+DlgImgLockRatio : "СохранÑÑ‚ÑŒ пропорции",
+DlgBtnResetSize : "СброÑить размер",
+DlgImgBorder : "Бордюр",
+DlgImgHSpace : "Горизонтальный отÑтуп",
+DlgImgVSpace : "Вертикальный отÑтуп",
+DlgImgAlign : "Выравнивание",
+DlgImgAlignLeft : "По левому краю",
+DlgImgAlignAbsBottom: "ÐÐ±Ñ Ð¿Ð¾Ð½Ð¸Ð·Ñƒ",
+DlgImgAlignAbsMiddle: "ÐÐ±Ñ Ð¿Ð¾Ñередине",
+DlgImgAlignBaseline : "По базовой линии",
+DlgImgAlignBottom : "Понизу",
+DlgImgAlignMiddle : "ПоÑередине",
+DlgImgAlignRight : "По правому краю",
+DlgImgAlignTextTop : "ТекÑÑ‚ наверху",
+DlgImgAlignTop : "По верху",
+DlgImgPreview : "Предварительный проÑмотр",
+DlgImgAlertUrl : "ПожалуйÑта введите URL изображениÑ",
+DlgImgLinkTab : "СÑылка",
+
+// Flash Dialog
+DlgFlashTitle : "СвойÑтва Flash",
+DlgFlashChkPlay : "Ðвто проигрывание",
+DlgFlashChkLoop : "Повтор",
+DlgFlashChkMenu : "Включить меню Flash",
+DlgFlashScale : "МаÑштабировать",
+DlgFlashScaleAll : "Показывать вÑе",
+DlgFlashScaleNoBorder : "Без бордюра",
+DlgFlashScaleFit : "Точное Ñовпадение",
+
+// Link Dialog
+DlgLnkWindowTitle : "СÑылка",
+DlgLnkInfoTab : "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ ÑÑылки",
+DlgLnkTargetTab : "Цель",
+
+DlgLnkType : "Тип ÑÑылки",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Якорь на Ñту Ñтраницу",
+DlgLnkTypeEMail : "Эл. почта",
+DlgLnkProto : "Протокол",
+DlgLnkProtoOther : "<другое>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Выберите Ñкорь",
+DlgLnkAnchorByName : "По имени ÑкорÑ",
+DlgLnkAnchorById : "По идентификатору Ñлемента",
+DlgLnkNoAnchors : "<Ðет Ñкорей доÑтупных в Ñтом документе>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ÐÐ´Ñ€ÐµÑ Ñл. почты",
+DlgLnkEMailSubject : "Заголовок ÑообщениÑ",
+DlgLnkEMailBody : "Тело ÑообщениÑ",
+DlgLnkUpload : "Закачать",
+DlgLnkBtnUpload : "ПоÑлать на Ñервер",
+
+DlgLnkTarget : "Цель",
+DlgLnkTargetFrame : "<фрейм>",
+DlgLnkTargetPopup : "<вÑплывающее окно>",
+DlgLnkTargetBlank : "Ðовое окно (_blank)",
+DlgLnkTargetParent : "РодительÑкое окно (_parent)",
+DlgLnkTargetSelf : "Тоже окно (_self)",
+DlgLnkTargetTop : "Самое верхнее окно (_top)",
+DlgLnkTargetFrameName : "Ð˜Ð¼Ñ Ñ†ÐµÐ»ÐµÐ²Ð¾Ð³Ð¾ фрейма",
+DlgLnkPopWinName : "Ð˜Ð¼Ñ Ð²Ñплывающего окна",
+DlgLnkPopWinFeat : "СвойÑтва вÑплывающего окна",
+DlgLnkPopResize : "ИзменÑющееÑÑ Ð² размерах",
+DlgLnkPopLocation : "Панель локации",
+DlgLnkPopMenu : "Панель меню",
+DlgLnkPopScroll : "ПолоÑÑ‹ прокрутки",
+DlgLnkPopStatus : "Строка ÑоÑтоÑниÑ",
+DlgLnkPopToolbar : "Панель инÑтрументов",
+DlgLnkPopFullScrn : "Полный Ñкран (IE)",
+DlgLnkPopDependent : "ЗавиÑимый (Netscape)",
+DlgLnkPopWidth : "Ширина",
+DlgLnkPopHeight : "Ð’Ñ‹Ñота",
+DlgLnkPopLeft : "ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ñлева",
+DlgLnkPopTop : "ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ñверху",
+
+DlnLnkMsgNoUrl : "ПожалуйÑта введите URL ÑÑылки",
+DlnLnkMsgNoEMail : "ПожалуйÑта введите Ð°Ð´Ñ€ÐµÑ Ñл. почты",
+DlnLnkMsgNoAnchor : "ПожалуйÑта выберете Ñкорь",
+DlnLnkMsgInvPopName : "Ðазвание вÑпывающего окна должно начинатьÑÑ Ð±ÑƒÐºÐ²Ñ‹ и не может Ñодержать пробелов",
+
+// Color Dialog
+DlgColorTitle : "Выберите цвет",
+DlgColorBtnClear : "ОчиÑтить",
+DlgColorHighlight : "ПодÑвеченный",
+DlgColorSelected : "Выбранный",
+
+// Smiley Dialog
+DlgSmileyTitle : "Ð’Ñтавить Ñмайлик",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Выберите Ñпециальный Ñимвол",
+
+// Table Dialog
+DlgTableTitle : "СвойÑтва таблицы",
+DlgTableRows : "Строки",
+DlgTableColumns : "Колонки",
+DlgTableBorder : "Размер бордюра",
+DlgTableAlign : "Выравнивание",
+DlgTableAlignNotSet : "<Ðе уÑÑ‚.>",
+DlgTableAlignLeft : "Слева",
+DlgTableAlignCenter : "По центру",
+DlgTableAlignRight : "Справа",
+DlgTableWidth : "Ширина",
+DlgTableWidthPx : "пикÑелей",
+DlgTableWidthPc : "процентов",
+DlgTableHeight : "Ð’Ñ‹Ñота",
+DlgTableCellSpace : "Промежуток (spacing)",
+DlgTableCellPad : "ОтÑтуп (padding)",
+DlgTableCaption : "Заголовок",
+DlgTableSummary : "Резюме",
+
+// Table Cell Dialog
+DlgCellTitle : "СвойÑтва Ñчейки",
+DlgCellWidth : "Ширина",
+DlgCellWidthPx : "пикÑелей",
+DlgCellWidthPc : "процентов",
+DlgCellHeight : "Ð’Ñ‹Ñота",
+DlgCellWordWrap : "Заворачивание текÑта",
+DlgCellWordWrapNotSet : "<Ðе уÑÑ‚.>",
+DlgCellWordWrapYes : "Да",
+DlgCellWordWrapNo : "Ðет",
+DlgCellHorAlign : "Гор. выравнивание",
+DlgCellHorAlignNotSet : "<Ðе уÑÑ‚.>",
+DlgCellHorAlignLeft : "Слева",
+DlgCellHorAlignCenter : "По центру",
+DlgCellHorAlignRight: "Справа",
+DlgCellVerAlign : "Верт. выравнивание",
+DlgCellVerAlignNotSet : "<Ðе уÑÑ‚.>",
+DlgCellVerAlignTop : "Сверху",
+DlgCellVerAlignMiddle : "ПоÑередине",
+DlgCellVerAlignBottom : "Снизу",
+DlgCellVerAlignBaseline : "По базовой линии",
+DlgCellRowSpan : "Диапазон Ñтрок (span)",
+DlgCellCollSpan : "Диапазон колонок (span)",
+DlgCellBackColor : "Цвет фона",
+DlgCellBorderColor : "Цвет бордюра",
+DlgCellBtnSelect : "Выберите...",
+
+// Find Dialog
+DlgFindTitle : "Ðайти",
+DlgFindFindBtn : "Ðайти",
+DlgFindNotFoundMsg : "Указанный текÑÑ‚ не найден.",
+
+// Replace Dialog
+DlgReplaceTitle : "Заменить",
+DlgReplaceFindLbl : "Ðайти:",
+DlgReplaceReplaceLbl : "Заменить на:",
+DlgReplaceCaseChk : "Учитывать региÑÑ‚Ñ€",
+DlgReplaceReplaceBtn : "Заменить",
+DlgReplaceReplAllBtn : "Заменить вÑе",
+DlgReplaceWordChk : "Совпадение целых Ñлов",
+
+// Paste Operations / Dialog
+PasteErrorCut : "ÐаÑтройки безопаÑноÑти вашего браузера не позволÑÑŽÑ‚ редактору автоматичеÑки выполнÑÑ‚ÑŒ операции вырезаниÑ. ПожалуйÑта иÑпользуйте клавиатуру Ð´Ð»Ñ Ñтого (Ctrl+X).",
+PasteErrorCopy : "ÐаÑтройки безопаÑноÑти вашего браузера не позволÑÑŽÑ‚ редактору автоматичеÑки выполнÑÑ‚ÑŒ операции копированиÑ. ПожалуйÑта иÑпользуйте клавиатуру Ð´Ð»Ñ Ñтого (Ctrl+C).",
+
+PasteAsText : "Ð’Ñтавить только текÑÑ‚",
+PasteFromWord : "Ð’Ñтавить из Word",
+
+DlgPasteMsg2 : "ПожалуйÑта вÑтавьте текÑÑ‚ в прÑмоугольник иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ñочетание клавиш (<STRONG>Ctrl+V</STRONG>) и нажмите <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Игнорировать Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð³Ð°Ñ€Ð½Ð¸Ñ‚ÑƒÑ€Ñ‹",
+DlgPasteRemoveStyles : "Убрать Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ñтилей",
+DlgPasteCleanBox : "ОчиÑтить",
+
+// Color Picker
+ColorAutomatic : "ÐвтоматичеÑкий",
+ColorMoreColors : "Цвета...",
+
+// Document Properties
+DocProps : "СвойÑтва документа",
+
+// Anchor Dialog
+DlgAnchorTitle : "СвойÑтва ÑкорÑ",
+DlgAnchorName : "Ð˜Ð¼Ñ ÑкорÑ",
+DlgAnchorErrorName : "ПожалуйÑта введите Ð¸Ð¼Ñ ÑкорÑ",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ðет в Ñловаре",
+DlgSpellChangeTo : "Заменить на",
+DlgSpellBtnIgnore : "Игнорировать",
+DlgSpellBtnIgnoreAll : "Игнорировать вÑе",
+DlgSpellBtnReplace : "Заменить",
+DlgSpellBtnReplaceAll : "Заменить вÑе",
+DlgSpellBtnUndo : "Отменить",
+DlgSpellNoSuggestions : "- Ðет предположений -",
+DlgSpellProgress : "Идет проверка орфографии...",
+DlgSpellNoMispell : "Проверка орфографии закончена: ошибок не найдено",
+DlgSpellNoChanges : "Проверка орфографии закончена: ни одного Ñлова не изменено",
+DlgSpellOneChange : "Проверка орфографии закончена: одно Ñлово изменено",
+DlgSpellManyChanges : "Проверка орфографии закончена: 1% Ñлов изменен",
+
+IeSpellDownload : "Модуль проверки орфографии не уÑтановлен. Хотите Ñкачать его ÑейчаÑ?",
+
+// Button Dialog
+DlgButtonText : "ТекÑÑ‚ (Значение)",
+DlgButtonType : "Тип",
+DlgButtonTypeBtn : "Кнопка",
+DlgButtonTypeSbm : "Отправить",
+DlgButtonTypeRst : "СброÑить",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "ИмÑ",
+DlgCheckboxValue : "Значение",
+DlgCheckboxSelected : "ВыбраннаÑ",
+
+// Form Dialog
+DlgFormName : "ИмÑ",
+DlgFormAction : "ДейÑтвие",
+DlgFormMethod : "Метод",
+
+// Select Field Dialog
+DlgSelectName : "ИмÑ",
+DlgSelectValue : "Значение",
+DlgSelectSize : "Размер",
+DlgSelectLines : "линии",
+DlgSelectChkMulti : "Разрешить множеÑтвенный выбор",
+DlgSelectOpAvail : "ДоÑтупные варианты",
+DlgSelectOpText : "ТекÑÑ‚",
+DlgSelectOpValue : "Значение",
+DlgSelectBtnAdd : "Добавить",
+DlgSelectBtnModify : "Модифицировать",
+DlgSelectBtnUp : "Вверх",
+DlgSelectBtnDown : "Вниз",
+DlgSelectBtnSetValue : "УÑтановить как выбранное значение",
+DlgSelectBtnDelete : "Удалить",
+
+// Textarea Dialog
+DlgTextareaName : "ИмÑ",
+DlgTextareaCols : "Колонки",
+DlgTextareaRows : "Строки",
+
+// Text Field Dialog
+DlgTextName : "ИмÑ",
+DlgTextValue : "Значение",
+DlgTextCharWidth : "Ширина",
+DlgTextMaxChars : "МакÑ. кол-во Ñимволов",
+DlgTextType : "Тип",
+DlgTextTypeText : "ТекÑÑ‚",
+DlgTextTypePass : "Пароль",
+
+// Hidden Field Dialog
+DlgHiddenName : "ИмÑ",
+DlgHiddenValue : "Значение",
+
+// Bulleted List Dialog
+BulletedListProp : "СвойÑтва маркированного ÑпиÑка",
+NumberedListProp : "СвойÑтва нумерованного ÑпиÑка",
+DlgLstStart : "Ðачало",
+DlgLstType : "Тип",
+DlgLstTypeCircle : "Круг",
+DlgLstTypeDisc : "ДиÑк",
+DlgLstTypeSquare : "Квадрат",
+DlgLstTypeNumbers : "Ðомера (1, 2, 3)",
+DlgLstTypeLCase : "Буквы нижнего региÑтра (a, b, c)",
+DlgLstTypeUCase : "Буквы верхнего региÑтра (A, B, C)",
+DlgLstTypeSRoman : "Малые римÑкие буквы (i, ii, iii)",
+DlgLstTypeLRoman : "Большие римÑкие буквы (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Общие",
+DlgDocBackTab : "Задний фон",
+DlgDocColorsTab : "Цвета и отÑтупы",
+DlgDocMetaTab : "Мета данные",
+
+DlgDocPageTitle : "Заголовок Ñтраницы",
+DlgDocLangDir : "Ðаправление текÑта",
+DlgDocLangDirLTR : "Слева на право (LTR)",
+DlgDocLangDirRTL : "Справа на лево (RTL)",
+DlgDocLangCode : "Код Ñзыка",
+DlgDocCharSet : "Кодировка набора Ñимволов",
+DlgDocCharSetCE : "Центрально-европейÑкаÑ",
+DlgDocCharSetCT : "КитайÑÐºÐ°Ñ Ñ‚Ñ€Ð°Ð´Ð¸Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ (Big5)",
+DlgDocCharSetCR : "Кириллица",
+DlgDocCharSetGR : "ГречеÑкаÑ",
+DlgDocCharSetJP : "ЯпонÑкаÑ",
+DlgDocCharSetKR : "КорейÑкаÑ",
+DlgDocCharSetTR : "ТурецкаÑ",
+DlgDocCharSetUN : "Юникод (UTF-8)",
+DlgDocCharSetWE : "Западно-европейÑкаÑ",
+DlgDocCharSetOther : "Ð”Ñ€ÑƒÐ³Ð°Ñ ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²ÐºÐ° набора Ñимволов",
+
+DlgDocDocType : "Заголовок типа документа",
+DlgDocDocTypeOther : "Другой заголовок типа документа",
+DlgDocIncXHTML : "Включить XHTML объÑвлениÑ",
+DlgDocBgColor : "Цвет фона",
+DlgDocBgImage : "URL Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ„Ð¾Ð½Ð°",
+DlgDocBgNoScroll : "ÐеÑкроллируемый фон",
+DlgDocCText : "ТекÑÑ‚",
+DlgDocCLink : "СÑылка",
+DlgDocCVisited : "ПоÑÐµÑ‰ÐµÐ½Ð½Ð°Ñ ÑÑылка",
+DlgDocCActive : "ÐÐºÑ‚Ð¸Ð²Ð½Ð°Ñ ÑÑылка",
+DlgDocMargins : "ОтÑтупы Ñтраницы",
+DlgDocMaTop : "Верхний",
+DlgDocMaLeft : "Левый",
+DlgDocMaRight : "Правый",
+DlgDocMaBottom : "Ðижний",
+DlgDocMeIndex : "Ключевые Ñлова документа (разделенные запÑтой)",
+DlgDocMeDescr : "ОпиÑание документа",
+DlgDocMeAuthor : "Ðвтор",
+DlgDocMeCopy : "ÐвторÑкие права",
+DlgDocPreview : "Предварительный проÑмотр",
+
+// Templates Dialog
+Templates : "Шаблоны",
+DlgTemplatesTitle : "Шаблоны Ñодержимого",
+DlgTemplatesSelMsg : "ПожалуйÑта выберете шаблон Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ð² редакторе<br>(текущее Ñодержимое будет потерÑно):",
+DlgTemplatesLoading : "Загрузка ÑпиÑка шаблонов. ПожалуйÑта подождите...",
+DlgTemplatesNoTpl : "(Ðи одного шаблона не определено)",
+DlgTemplatesReplace : "Заменить текущее Ñодержание",
+
+// About Dialog
+DlgAboutAboutTab : "О программе",
+DlgAboutBrowserInfoTab : "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð°",
+DlgAboutLicenseTab : "ЛицензиÑ",
+DlgAboutVersion : "ВерÑиÑ",
+DlgAboutInfo : "Ð”Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐµÐ¹ информации, поÑетите"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/sk.js b/httemplate/elements/fckeditor/editor/lang/sk.js
new file mode 100644
index 0000000..83d00ba
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/sk.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Slovak language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Skryť panel nástrojov",
+ToolbarExpand : "Zobraziť panel nástrojov",
+
+// Toolbar Items and Context Menu
+Save : "Uložit",
+NewPage : "Nová stránka",
+Preview : "Náhľad",
+Cut : "Vystrihnúť",
+Copy : "Kopírovať",
+Paste : "Vložiť",
+PasteText : "VložiÅ¥ ako Äistý text",
+PasteWord : "Vložiť z Wordu",
+Print : "TlaÄ",
+SelectAll : "Vybrať všetko",
+RemoveFormat : "Odstrániť formátovanie",
+InsertLinkLbl : "Odkaz",
+InsertLink : "Vložiť/zmeniť odkaz",
+RemoveLink : "Odstrániť odkaz",
+Anchor : "Vložiť/zmeniť kotvu",
+InsertImageLbl : "Obrázok",
+InsertImage : "Vložiť/zmeniť obrázok",
+InsertFlashLbl : "Flash",
+InsertFlash : "Vložiť/zmeniť Flash",
+InsertTableLbl : "Tabuľka",
+InsertTable : "Vložiť/zmeniť tabuľku",
+InsertLineLbl : "ÄŒiara",
+InsertLine : "VložiÅ¥ vodorovnú Äiaru",
+InsertSpecialCharLbl: "Špeciálne znaky",
+InsertSpecialChar : "Vložiť špeciálne znaky",
+InsertSmileyLbl : "Smajlíky",
+InsertSmiley : "Vložiť smajlíka",
+About : "O aplikáci FCKeditor",
+Bold : "TuÄné",
+Italic : "Kurzíva",
+Underline : "PodÄiarknuté",
+StrikeThrough : "PreÄiarknuté",
+Subscript : "Dolný index",
+Superscript : "Horný index",
+LeftJustify : "Zarovnať vľavo",
+CenterJustify : "Zarovnať na stred",
+RightJustify : "Zarovnať vpravo",
+BlockJustify : "Zarovnať do bloku",
+DecreaseIndent : "Zmenšiť odsadenie",
+IncreaseIndent : "ZväÄÅ¡iÅ¥ odsadenie",
+Undo : "Späť",
+Redo : "Znovu",
+NumberedListLbl : "Číslovanie",
+NumberedList : "VložiÅ¥/odstrániÅ¥ Äíslovaný zoznam",
+BulletedListLbl : "Odrážky",
+BulletedList : "Vložiť/odstraniť odrážky",
+ShowTableBorders : "Zobraziť okraje tabuliek",
+ShowDetails : "Zobraziť podrobnosti",
+Style : "Štýl",
+FontFormat : "Formát",
+Font : "Písmo",
+FontSize : "Veľkosť",
+TextColor : "Farba textu",
+BGColor : "Farba pozadia",
+Source : "Zdroj",
+Find : "Hľadať",
+Replace : "Nahradiť",
+SpellCheck : "Kontrola pravopisu",
+UniversalKeyboard : "Univerzálna klávesnica",
+PageBreakLbl : "OddeľovaÄ stránky",
+PageBreak : "VložiÅ¥ oddeľovaÄ stránky",
+
+Form : "Formulár",
+Checkbox : "ZaÅ¡krtávacie políÄko",
+RadioButton : "PrepínaÄ",
+TextField : "Textové pole",
+Textarea : "Textová oblasť",
+HiddenField : "Skryté pole",
+Button : "TlaÄíidlo",
+SelectionField : "Rozbaľovací zoznam",
+ImageButton : "Obrázkové tlaÄidlo",
+
+FitWindow : "Maximalizovať veľkosť okna editora",
+
+// Context Menu
+EditLink : "Zmeniť odkaz",
+CellCM : "Bunka",
+RowCM : "Riadok",
+ColumnCM : "Stĺpec",
+InsertRow : "Vložiť riadok",
+DeleteRows : "Vymazať riadok",
+InsertColumn : "Vložiť stĺpec",
+DeleteColumns : "Zmazať stĺpec",
+InsertCell : "Vložiť bunku",
+DeleteCells : "Vymazať bunky",
+MergeCells : "ZlúÄiÅ¥ bunky",
+SplitCell : "Rozdeliť bunku",
+TableDelete : "Vymazať tabuľku",
+CellProperties : "Vlastnosti bunky",
+TableProperties : "Vlastnosti tabuľky",
+ImageProperties : "Vlastnosti obrázku",
+FlashProperties : "Vlastnosti Flashu",
+
+AnchorProp : "Vlastnosti kotvy",
+ButtonProp : "Vlastnosti tlaÄidla",
+CheckboxProp : "Vlastnosti zaÅ¡krtávacieho políÄka",
+HiddenFieldProp : "Vlastnosti skrytého poľa",
+RadioButtonProp : "Vlastnosti prepínaÄa",
+ImageButtonProp : "Vlastnosti obrázkového tlaÄidla",
+TextFieldProp : "Vlastnosti textového poľa",
+SelectionFieldProp : "Vlastnosti rozbaľovacieho zoznamu",
+TextareaProp : "Vlastnosti textovej oblasti",
+FormProp : "Vlastnosti formulára",
+
+FontFormats : "Normálny;Formátovaný;Adresa;Nadpis 1;Nadpis 2;Nadpis 3;Nadpis 4;Nadpis 5;Nadpis 6;Odsek (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Prebieha spracovanie XHTML. Čakajte prosím...",
+Done : "DokonÄené.",
+PasteWordConfirm : "Vyzerá to tak, že vkladaný text je kopírovaný z Wordu. Chcete ho pred vložením vyÄistiÅ¥?",
+NotCompatiblePaste : "Tento príkaz je dostupný len v prehliadaÄi Internet Explorer verzie 5.5 alebo vyÅ¡Å¡ej. Chcete vložiÅ¥ text bez vyÄistenia?",
+UnknownToolbarItem : "Neznáma položka panela nástrojov \"%1\"",
+UnknownCommand : "Neznámy príkaz \"%1\"",
+NotImplemented : "Príkaz nie je implementovaný",
+UnknownToolbarSet : "Panel nástrojov \"%1\" neexistuje",
+NoActiveX : "BezpeÄnostné nastavenia vášho prehliadaÄa môžu obmedzovaÅ¥ niektoré funkcie editora. Pre ich plnú funkÄnosÅ¥ musíte zapnúť voľbu \"SpúšťaÅ¥ ActiveX moduly a zásuvné moduly\", inak sa môžete stretnúť s chybami a nefunkÄnosÅ¥ou niektorých funkcií.",
+BrowseServerBlocked : "PrehliadaÄ zdrojových prvkov nebolo možné otvoriÅ¥. Uistite sa, že máte vypnuté vÅ¡etky blokovaÄe vyskakujúcich okien.",
+DialogBlocked : "Dialógové okno nebolo možné otvoriÅ¥. Uistite sa, že máte vypnuté vÅ¡etky blokovaÄe vyskakujúcich okien.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Zrušiť",
+DlgBtnClose : "Zavrieť",
+DlgBtnBrowseServer : "Prechádzať server",
+DlgAdvancedTag : "Rozšírené",
+DlgOpOther : "<Ďalšie>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Prosím vložte URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nenastavené>",
+DlgGenId : "Id",
+DlgGenLangDir : "Orientácia jazyka",
+DlgGenLangDirLtr : "Zľava doprava (LTR)",
+DlgGenLangDirRtl : "Sprava doľava (RTL)",
+DlgGenLangCode : "Kód jazyka",
+DlgGenAccessKey : "Prístupový kľúÄ",
+DlgGenName : "Meno",
+DlgGenTabIndex : "Poradie prvku",
+DlgGenLongDescr : "Dlhý popis URL",
+DlgGenClass : "Trieda štýlu",
+DlgGenTitle : "Pomocný titulok",
+DlgGenContType : "Pomocný typ obsahu",
+DlgGenLinkCharset : "Priradená znaková sada",
+DlgGenStyle : "Štýl",
+
+// Image Dialog
+DlgImgTitle : "Vlastnosti obrázku",
+DlgImgInfoTab : "Informácie o obrázku",
+DlgImgBtnUpload : "Odoslať na server",
+DlgImgURL : "URL",
+DlgImgUpload : "Odoslať",
+DlgImgAlt : "Alternatívny text",
+DlgImgWidth : "Šírka",
+DlgImgHeight : "Výška",
+DlgImgLockRatio : "Zámok",
+DlgBtnResetSize : "Pôvodná veľkosť",
+DlgImgBorder : "Okraje",
+DlgImgHSpace : "H-medzera",
+DlgImgVSpace : "V-medzera",
+DlgImgAlign : "Zarovnanie",
+DlgImgAlignLeft : "Vľavo",
+DlgImgAlignAbsBottom: "Úplne dole",
+DlgImgAlignAbsMiddle: "Do stredu",
+DlgImgAlignBaseline : "Na základňu",
+DlgImgAlignBottom : "Dole",
+DlgImgAlignMiddle : "Na stred",
+DlgImgAlignRight : "Vpravo",
+DlgImgAlignTextTop : "Na horný okraj textu",
+DlgImgAlignTop : "Nahor",
+DlgImgPreview : "Náhľad",
+DlgImgAlertUrl : "Zadajte prosím URL obrázku",
+DlgImgLinkTab : "Odkaz",
+
+// Flash Dialog
+DlgFlashTitle : "Vlastnosti Flashu",
+DlgFlashChkPlay : "Automatické prehrávanie",
+DlgFlashChkLoop : "Opakovanie",
+DlgFlashChkMenu : "Povoliť Flash Menu",
+DlgFlashScale : "Mierka",
+DlgFlashScaleAll : "Zobraziť mierku",
+DlgFlashScaleNoBorder : "Bez okrajov",
+DlgFlashScaleFit : "Roztiahnuť na celé",
+
+// Link Dialog
+DlgLnkWindowTitle : "Odkaz",
+DlgLnkInfoTab : "Informácie o odkaze",
+DlgLnkTargetTab : "Cieľ",
+
+DlgLnkType : "Typ odkazu",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Kotva v tejto stránke",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<iný>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Vybrať kotvu",
+DlgLnkAnchorByName : "Podľa mena kotvy",
+DlgLnkAnchorById : "Podľa Id objektu",
+DlgLnkNoAnchors : "<V stránke nie je definovaná žiadna kotva>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mailová adresa",
+DlgLnkEMailSubject : "Predmet správy",
+DlgLnkEMailBody : "Telo správy",
+DlgLnkUpload : "Odoslať",
+DlgLnkBtnUpload : "Odoslať na server",
+
+DlgLnkTarget : "Cieľ",
+DlgLnkTargetFrame : "<rámec>",
+DlgLnkTargetPopup : "<vyskakovacie okno>",
+DlgLnkTargetBlank : "Nové okno (_blank)",
+DlgLnkTargetParent : "RodiÄovské okno (_parent)",
+DlgLnkTargetSelf : "Rovnaké okno (_self)",
+DlgLnkTargetTop : "Hlavné okno (_top)",
+DlgLnkTargetFrameName : "Meno rámu cieľa",
+DlgLnkPopWinName : "Názov vyskakovacieho okna",
+DlgLnkPopWinFeat : "Vlastnosti vyskakovacieho okna",
+DlgLnkPopResize : "Meniteľná veľkosť",
+DlgLnkPopLocation : "Panel umiestnenia",
+DlgLnkPopMenu : "Panel ponuky",
+DlgLnkPopScroll : "Posuvníky",
+DlgLnkPopStatus : "Stavový riadok",
+DlgLnkPopToolbar : "Panel nástrojov",
+DlgLnkPopFullScrn : "Celá obrazovka (IE)",
+DlgLnkPopDependent : "Závislosť (Netscape)",
+DlgLnkPopWidth : "Šírka",
+DlgLnkPopHeight : "Výška",
+DlgLnkPopLeft : "Ľavý okraj",
+DlgLnkPopTop : "Horný okraj",
+
+DlnLnkMsgNoUrl : "Zadajte prosím URL odkazu",
+DlnLnkMsgNoEMail : "Zadajte prosím e-mailovú adresu",
+DlnLnkMsgNoAnchor : "Vyberte prosím kotvu",
+DlnLnkMsgInvPopName : "Názov vyskakovacieho okna sa musá zaÄínaÅ¥ písmenom a nemôže obsahovaÅ¥ medzery",
+
+// Color Dialog
+DlgColorTitle : "Výber farby",
+DlgColorBtnClear : "Vymazať",
+DlgColorHighlight : "Zvýraznená",
+DlgColorSelected : "Vybraná",
+
+// Smiley Dialog
+DlgSmileyTitle : "Vkladanie smajlíkov",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Výber špeciálneho znaku",
+
+// Table Dialog
+DlgTableTitle : "Vlastnosti tabuľky",
+DlgTableRows : "Riadky",
+DlgTableColumns : "Stĺpce",
+DlgTableBorder : "OhraniÄenie",
+DlgTableAlign : "Zarovnanie",
+DlgTableAlignNotSet : "<nenastavené>",
+DlgTableAlignLeft : "Vľavo",
+DlgTableAlignCenter : "Na stred",
+DlgTableAlignRight : "Vpravo",
+DlgTableWidth : "Šírka",
+DlgTableWidthPx : "pixelov",
+DlgTableWidthPc : "percent",
+DlgTableHeight : "Výška",
+DlgTableCellSpace : "Vzdialenosť buniek",
+DlgTableCellPad : "Odsadenie obsahu",
+DlgTableCaption : "Popis",
+DlgTableSummary : "Prehľad",
+
+// Table Cell Dialog
+DlgCellTitle : "Vlastnosti bunky",
+DlgCellWidth : "Šírka",
+DlgCellWidthPx : "bodov",
+DlgCellWidthPc : "percent",
+DlgCellHeight : "Výška",
+DlgCellWordWrap : "Zalamovannie",
+DlgCellWordWrapNotSet : "<nenastavené>",
+DlgCellWordWrapYes : "Ãno",
+DlgCellWordWrapNo : "Nie",
+DlgCellHorAlign : "Vodorovné zarovnanie",
+DlgCellHorAlignNotSet : "<nenastavené>",
+DlgCellHorAlignLeft : "Vľavo",
+DlgCellHorAlignCenter : "Na stred",
+DlgCellHorAlignRight: "Vpravo",
+DlgCellVerAlign : "Zvislé zarovnanie",
+DlgCellVerAlignNotSet : "<nenastavené>",
+DlgCellVerAlignTop : "Nahor",
+DlgCellVerAlignMiddle : "Doprostred",
+DlgCellVerAlignBottom : "Dole",
+DlgCellVerAlignBaseline : "Na základňu",
+DlgCellRowSpan : "ZlúÄené riadky",
+DlgCellCollSpan : "ZlúÄené stĺpce",
+DlgCellBackColor : "Farba pozadia",
+DlgCellBorderColor : "Farba ohraniÄenia",
+DlgCellBtnSelect : "Výber...",
+
+// Find Dialog
+DlgFindTitle : "Hľadať",
+DlgFindFindBtn : "Hľadať",
+DlgFindNotFoundMsg : "Hľadaný text nebol nájdený.",
+
+// Replace Dialog
+DlgReplaceTitle : "Nahradiť",
+DlgReplaceFindLbl : "Čo hľadať:",
+DlgReplaceReplaceLbl : "Čím nahradiť:",
+DlgReplaceCaseChk : "Rozlišovať malé/veľké písmená",
+DlgReplaceReplaceBtn : "Nahradiť",
+DlgReplaceReplAllBtn : "Nahradiť všetko",
+DlgReplaceWordChk : "Len celé slová",
+
+// Paste Operations / Dialog
+PasteErrorCut : "BezpeÄnostné nastavenie Vášho prehliadaÄa nedovoľujú editoru spustiÅ¥ funkciu pre vystrihnutie zvoleného textu do schránky. Prosím vystrihnite zvolený text do schránky pomocou klávesnice (Ctrl+X).",
+PasteErrorCopy : "BezpeÄnostné nastavenie Vášho prehliadaÄa nedovoľujú editoru spustiÅ¥ funkciu pre kopírovanie zvoleného textu do schránky. Prosím skopírujte zvolený text do schránky pomocou klávesnice (Ctrl+C).",
+
+PasteAsText : "VložiÅ¥ ako Äistý text",
+PasteFromWord : "Vložiť text z Wordu",
+
+DlgPasteMsg2 : "Prosím vložte nasledovný rámÄek použitím klávesnice (<STRONG>Ctrl+V</STRONG>) a stlaÄte <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignorovať nastavenia typu písma",
+DlgPasteRemoveStyles : "Odstrániť formátovanie",
+DlgPasteCleanBox : "VyÄistiÅ¥ schránku",
+
+// Color Picker
+ColorAutomatic : "Automaticky",
+ColorMoreColors : "Viac farieb...",
+
+// Document Properties
+DocProps : "Vlastnosti dokumentu",
+
+// Anchor Dialog
+DlgAnchorTitle : "Vlastnosti kotvy",
+DlgAnchorName : "Meno kotvy",
+DlgAnchorErrorName : "Zadajte prosím meno kotvy",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Nie je v slovníku",
+DlgSpellChangeTo : "Zmeniť na",
+DlgSpellBtnIgnore : "Ignorovať",
+DlgSpellBtnIgnoreAll : "Ignorovať všetko",
+DlgSpellBtnReplace : "Prepísat",
+DlgSpellBtnReplaceAll : "Prepísat všetko",
+DlgSpellBtnUndo : "Späť",
+DlgSpellNoSuggestions : "- Žiadny návrh -",
+DlgSpellProgress : "Prebieha kontrola pravopisu...",
+DlgSpellNoMispell : "Kontrola pravopisu dokonÄená: bez chýb",
+DlgSpellNoChanges : "Kontrola pravopisu dokonÄená: žiadne slová nezmenené",
+DlgSpellOneChange : "Kontrola pravopisu dokonÄená: zmenené jedno slovo",
+DlgSpellManyChanges : "Kontrola pravopisu dokonÄená: zmenených %1 slov",
+
+IeSpellDownload : "Kontrola pravopisu nie je naiÅ¡talovaná. Chcete ju hneÄ stiahnuÅ¥?",
+
+// Button Dialog
+DlgButtonText : "Text",
+DlgButtonType : "Typ",
+DlgButtonTypeBtn : "TlaÄidlo",
+DlgButtonTypeSbm : "Odoslať",
+DlgButtonTypeRst : "Vymazať",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Názov",
+DlgCheckboxValue : "Hodnota",
+DlgCheckboxSelected : "Vybrané",
+
+// Form Dialog
+DlgFormName : "Názov",
+DlgFormAction : "Akcie",
+DlgFormMethod : "Metóda",
+
+// Select Field Dialog
+DlgSelectName : "Názov",
+DlgSelectValue : "Hodnota",
+DlgSelectSize : "Veľkosť",
+DlgSelectLines : "riadkov",
+DlgSelectChkMulti : "Povoliť viacnásobný výber",
+DlgSelectOpAvail : "Dostupné možnosti",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Hodnota",
+DlgSelectBtnAdd : "Pridať",
+DlgSelectBtnModify : "Zmeniť",
+DlgSelectBtnUp : "Hore",
+DlgSelectBtnDown : "Dole",
+DlgSelectBtnSetValue : "Nastaviť ako vybranú hodnotu",
+DlgSelectBtnDelete : "Zmazať",
+
+// Textarea Dialog
+DlgTextareaName : "Názov",
+DlgTextareaCols : "Stĺpce",
+DlgTextareaRows : "Riadky",
+
+// Text Field Dialog
+DlgTextName : "Názov",
+DlgTextValue : "Hodnota",
+DlgTextCharWidth : "Šírka pola (znakov)",
+DlgTextMaxChars : "Maximálny poÄet znakov",
+DlgTextType : "Typ",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Heslo",
+
+// Hidden Field Dialog
+DlgHiddenName : "Názov",
+DlgHiddenValue : "Hodnota",
+
+// Bulleted List Dialog
+BulletedListProp : "Vlastnosti odrážok",
+NumberedListProp : "Vlastnosti Äíslovania",
+DlgLstStart : "Å tart",
+DlgLstType : "Typ",
+DlgLstTypeCircle : "Krúžok",
+DlgLstTypeDisc : "Disk",
+DlgLstTypeSquare : "Å tvorec",
+DlgLstTypeNumbers : "Číslovanie (1, 2, 3)",
+DlgLstTypeLCase : "Malé písmená (a, b, c)",
+DlgLstTypeUCase : "Veľké písmená (A, B, C)",
+DlgLstTypeSRoman : "Malé rímske Äíslice (i, ii, iii)",
+DlgLstTypeLRoman : "Veľké rímske Äíslice (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Všeobecné",
+DlgDocBackTab : "Pozadie",
+DlgDocColorsTab : "Farby a okraje",
+DlgDocMetaTab : "Meta Data",
+
+DlgDocPageTitle : "Titulok",
+DlgDocLangDir : "Orientácie jazyka",
+DlgDocLangDirLTR : "Zľava doprava (LTR)",
+DlgDocLangDirRTL : "Sprava doľava (RTL)",
+DlgDocLangCode : "Kód jazyka",
+DlgDocCharSet : "Kódová stránka",
+DlgDocCharSetCE : "Stredoeurópske",
+DlgDocCharSetCT : "ČínÅ¡tina tradiÄná (Big5)",
+DlgDocCharSetCR : "Cyrillika",
+DlgDocCharSetGR : "GréÄtina",
+DlgDocCharSetJP : "JaponÄina",
+DlgDocCharSetKR : "KorejÄina",
+DlgDocCharSetTR : "TureÄtina",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Západná európa",
+DlgDocCharSetOther : "Iná kódová stránka",
+
+DlgDocDocType : "Typ záhlavia dokumentu",
+DlgDocDocTypeOther : "Iný typ záhlavia dokumentu",
+DlgDocIncXHTML : "Obsahuje deklarácie XHTML",
+DlgDocBgColor : "Farba pozadia",
+DlgDocBgImage : "URL adresa obrázku na pozadí",
+DlgDocBgNoScroll : "Fixné pozadie",
+DlgDocCText : "Text",
+DlgDocCLink : "Odkaz",
+DlgDocCVisited : "Navštívený odkaz",
+DlgDocCActive : "Aktívny odkaz",
+DlgDocMargins : "Okraje stránky",
+DlgDocMaTop : "Horný",
+DlgDocMaLeft : "Ľavý",
+DlgDocMaRight : "Pravý",
+DlgDocMaBottom : "Dolný",
+DlgDocMeIndex : "KľúÄové slová pre indexovanie (oddelené Äiarkou)",
+DlgDocMeDescr : "Popis stránky",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Autorské práva",
+DlgDocPreview : "Náhľad",
+
+// Templates Dialog
+Templates : "Šablóny",
+DlgTemplatesTitle : "Šablóny obsahu",
+DlgTemplatesSelMsg : "Prosím vyberte šablóny na otvorenie v editore<br>(súšasný obsah bude stratený):",
+DlgTemplatesLoading : "Nahrávam zoznam šablón. Čakajte prosím...",
+DlgTemplatesNoTpl : "(žiadne šablóny nenájdené)",
+DlgTemplatesReplace : "Nahradiť aktuálny obsah",
+
+// About Dialog
+DlgAboutAboutTab : "O aplikáci",
+DlgAboutBrowserInfoTab : "Informácie o prehliadaÄi",
+DlgAboutLicenseTab : "Licencia",
+DlgAboutVersion : "verzia",
+DlgAboutInfo : "Viac informácií získate na"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/sl.js b/httemplate/elements/fckeditor/editor/lang/sl.js
new file mode 100644
index 0000000..95cde15
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/sl.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Slovenian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Zloži orodno vrstico",
+ToolbarExpand : "Razširi orodno vrstico",
+
+// Toolbar Items and Context Menu
+Save : "Shrani",
+NewPage : "Nova stran",
+Preview : "Predogled",
+Cut : "Izreži",
+Copy : "Kopiraj",
+Paste : "Prilepi",
+PasteText : "Prilepi kot golo besedilo",
+PasteWord : "Prilepi iz Worda",
+Print : "Natisni",
+SelectAll : "Izberi vse",
+RemoveFormat : "Odstrani oblikovanje",
+InsertLinkLbl : "Povezava",
+InsertLink : "Vstavi/uredi povezavo",
+RemoveLink : "Odstrani povezavo",
+Anchor : "Vstavi/uredi zaznamek",
+InsertImageLbl : "Slika",
+InsertImage : "Vstavi/uredi sliko",
+InsertFlashLbl : "Flash",
+InsertFlash : "Vstavi/Uredi Flash",
+InsertTableLbl : "Tabela",
+InsertTable : "Vstavi/uredi tabelo",
+InsertLineLbl : "ÄŒrta",
+InsertLine : "Vstavi vodoravno Ärto",
+InsertSpecialCharLbl: "Posebni znak",
+InsertSpecialChar : "Vstavi posebni znak",
+InsertSmileyLbl : "Smeško",
+InsertSmiley : "Vstavi smeška",
+About : "O FCKeditorju",
+Bold : "Krepko",
+Italic : "LežeÄe",
+Underline : "PodÄrtano",
+StrikeThrough : "PreÄrtano",
+Subscript : "Podpisano",
+Superscript : "Nadpisano",
+LeftJustify : "Leva poravnava",
+CenterJustify : "Sredinska poravnava",
+RightJustify : "Desna poravnava",
+BlockJustify : "Obojestranska poravnava",
+DecreaseIndent : "Zmanjšaj zamik",
+IncreaseIndent : "PoveÄaj zamik",
+Undo : "Razveljavi",
+Redo : "Ponovi",
+NumberedListLbl : "OÅ¡tevilÄen seznam",
+NumberedList : "Vstavi/odstrani oÅ¡tevilÄevanje",
+BulletedListLbl : "OznaÄen seznam",
+BulletedList : "Vstavi/odstrani oznaÄevanje",
+ShowTableBorders : "Pokaži meje tabele",
+ShowDetails : "Pokaži podrobnosti",
+Style : "Slog",
+FontFormat : "Oblika",
+Font : "Pisava",
+FontSize : "Velikost",
+TextColor : "Barva besedila",
+BGColor : "Barva ozadja",
+Source : "Izvorna koda",
+Find : "Najdi",
+Replace : "Zamenjaj",
+SpellCheck : "Preveri Ärkovanje",
+UniversalKeyboard : "VeÄjeziÄna tipkovnica",
+PageBreakLbl : "Prelom strani",
+PageBreak : "Vstavi prelom strani",
+
+Form : "Obrazec",
+Checkbox : "Potrditveno polje",
+RadioButton : "Izbirno polje",
+TextField : "Vnosno polje",
+Textarea : "Vnosno obmoÄje",
+HiddenField : "Skrito polje",
+Button : "Gumb",
+SelectionField : "Spustni seznam",
+ImageButton : "Gumb s sliko",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Uredi povezavo",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Vstavi vrstico",
+DeleteRows : "Izbriši vrstice",
+InsertColumn : "Vstavi stolpec",
+DeleteColumns : "Izbriši stolpce",
+InsertCell : "Vstavi celico",
+DeleteCells : "Izbriši celice",
+MergeCells : "Združi celice",
+SplitCell : "Razdeli celico",
+TableDelete : "Izbriši tabelo",
+CellProperties : "Lastnosti celice",
+TableProperties : "Lastnosti tabele",
+ImageProperties : "Lastnosti slike",
+FlashProperties : "Lastnosti Flash",
+
+AnchorProp : "Lastnosti zaznamka",
+ButtonProp : "Lastnosti gumba",
+CheckboxProp : "Lastnosti potrditvenega polja",
+HiddenFieldProp : "Lastnosti skritega polja",
+RadioButtonProp : "Lastnosti izbirnega polja",
+ImageButtonProp : "Lastnosti gumba s sliko",
+TextFieldProp : "Lastnosti vnosnega polja",
+SelectionFieldProp : "Lastnosti spustnega seznama",
+TextareaProp : "Lastnosti vnosnega obmoÄja",
+FormProp : "Lastnosti obrazca",
+
+FontFormats : "Navaden;Oblikovan;Napis;Naslov 1;Naslov 2;Naslov 3;Naslov 4;Naslov 5;Naslov 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Obdelujem XHTML. Prosim poÄakajte...",
+Done : "Narejeno",
+PasteWordConfirm : "Izgleda, da želite prilepiti besedilo iz Worda. Ali ga želite oÄistiti, preden ga prilepite?",
+NotCompatiblePaste : "Ta ukaz deluje le v Internet Explorerje razliÄice 5.5 ali viÅ¡je. Ali želite prilepiti brez ÄiÅ¡Äenja?",
+UnknownToolbarItem : "Neznan element orodne vrstice \"%1\"",
+UnknownCommand : "Neznano ime ukaza \"%1\"",
+NotImplemented : "Ukaz ni izdelan",
+UnknownToolbarSet : "Skupina orodnih vrstic \"%1\" ne obstoja",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "V redu",
+DlgBtnCancel : "PrekliÄi",
+DlgBtnClose : "Zapri",
+DlgBtnBrowseServer : "Prebrskaj na strežniku",
+DlgAdvancedTag : "Napredno",
+DlgOpOther : "<Ostalo>",
+DlgInfoTab : "Podatki",
+DlgAlertUrl : "Prosim vpiši spletni naslov",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ni postavljen>",
+DlgGenId : "Id",
+DlgGenLangDir : "Smer jezika",
+DlgGenLangDirLtr : "Od leve proti desni (LTR)",
+DlgGenLangDirRtl : "Od desne proti levi (RTL)",
+DlgGenLangCode : "Oznaka jezika",
+DlgGenAccessKey : "Vstopno geslo",
+DlgGenName : "Ime",
+DlgGenTabIndex : "Å tevilka tabulatorja",
+DlgGenLongDescr : "Dolg opis URL-ja",
+DlgGenClass : "Razred stilne predloge",
+DlgGenTitle : "Predlagani naslov",
+DlgGenContType : "Predlagani tip vsebine (content-type)",
+DlgGenLinkCharset : "Kodna tabela povezanega vira",
+DlgGenStyle : "Slog",
+
+// Image Dialog
+DlgImgTitle : "Lastnosti slike",
+DlgImgInfoTab : "Podatki o sliki",
+DlgImgBtnUpload : "Pošlji na strežnik",
+DlgImgURL : "URL",
+DlgImgUpload : "Pošlji",
+DlgImgAlt : "Nadomestno besedilo",
+DlgImgWidth : "Å irina",
+DlgImgHeight : "Višina",
+DlgImgLockRatio : "Zakleni razmerje",
+DlgBtnResetSize : "Ponastavi velikost",
+DlgImgBorder : "Obroba",
+DlgImgHSpace : "Vodoravni razmik",
+DlgImgVSpace : "NavpiÄni razmik",
+DlgImgAlign : "Poravnava",
+DlgImgAlignLeft : "Levo",
+DlgImgAlignAbsBottom: "Popolnoma na dno",
+DlgImgAlignAbsMiddle: "Popolnoma v sredino",
+DlgImgAlignBaseline : "Na osnovno Ärto",
+DlgImgAlignBottom : "Na dno",
+DlgImgAlignMiddle : "V sredino",
+DlgImgAlignRight : "Desno",
+DlgImgAlignTextTop : "Besedilo na vrh",
+DlgImgAlignTop : "Na vrh",
+DlgImgPreview : "Predogled",
+DlgImgAlertUrl : "Vnesite URL slike",
+DlgImgLinkTab : "Povezava",
+
+// Flash Dialog
+DlgFlashTitle : "Lastnosti Flash",
+DlgFlashChkPlay : "Samodejno predvajaj",
+DlgFlashChkLoop : "Ponavljanje",
+DlgFlashChkMenu : "OmogoÄi Flash Meni",
+DlgFlashScale : "PoveÄava",
+DlgFlashScaleAll : "Pokaži vse",
+DlgFlashScaleNoBorder : "Brez obrobe",
+DlgFlashScaleFit : "NatanÄno prileganje",
+
+// Link Dialog
+DlgLnkWindowTitle : "Povezava",
+DlgLnkInfoTab : "Podatki o povezavi",
+DlgLnkTargetTab : "Cilj",
+
+DlgLnkType : "Vrsta povezave",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Zaznamek na tej strani",
+DlgLnkTypeEMail : "Elektronski naslov",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<drugo>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Izberi zaznamek",
+DlgLnkAnchorByName : "Po imenu zaznamka",
+DlgLnkAnchorById : "Po ID-ju elementa",
+DlgLnkNoAnchors : "<V tem dokumentu ni zaznamkov>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Elektronski naslov",
+DlgLnkEMailSubject : "Predmet sporoÄila",
+DlgLnkEMailBody : "Vsebina sporoÄila",
+DlgLnkUpload : "Prenesi",
+DlgLnkBtnUpload : "Pošlji na strežnik",
+
+DlgLnkTarget : "Cilj",
+DlgLnkTargetFrame : "<okvir>",
+DlgLnkTargetPopup : "<pojavno okno>",
+DlgLnkTargetBlank : "Novo okno (_blank)",
+DlgLnkTargetParent : "Starševsko okno (_parent)",
+DlgLnkTargetSelf : "Isto okno (_self)",
+DlgLnkTargetTop : "Najvišje okno (_top)",
+DlgLnkTargetFrameName : "Ime ciljnega okvirja",
+DlgLnkPopWinName : "Ime pojavnega okna",
+DlgLnkPopWinFeat : "ZnaÄilnosti pojavnega okna",
+DlgLnkPopResize : "Spremenljive velikosti",
+DlgLnkPopLocation : "Naslovna vrstica",
+DlgLnkPopMenu : "Menijska vrstica",
+DlgLnkPopScroll : "Drsniki",
+DlgLnkPopStatus : "Vrstica stanja",
+DlgLnkPopToolbar : "Orodna vrstica",
+DlgLnkPopFullScrn : "Celozaslonska slika (IE)",
+DlgLnkPopDependent : "Podokno (Netscape)",
+DlgLnkPopWidth : "Å irina",
+DlgLnkPopHeight : "Višina",
+DlgLnkPopLeft : "Lega levo",
+DlgLnkPopTop : "Lega na vrhu",
+
+DlnLnkMsgNoUrl : "Vnesite URL povezave",
+DlnLnkMsgNoEMail : "Vnesite elektronski naslov",
+DlnLnkMsgNoAnchor : "Izberite zaznamek",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Izberite barvo",
+DlgColorBtnClear : "PoÄisti",
+DlgColorHighlight : "OznaÄi",
+DlgColorSelected : "Izbrano",
+
+// Smiley Dialog
+DlgSmileyTitle : "Vstavi smeška",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Izberi posebni znak",
+
+// Table Dialog
+DlgTableTitle : "Lastnosti tabele",
+DlgTableRows : "Vrstice",
+DlgTableColumns : "Stolpci",
+DlgTableBorder : "Velikost obrobe",
+DlgTableAlign : "Poravnava",
+DlgTableAlignNotSet : "<Ni nastavljeno>",
+DlgTableAlignLeft : "Levo",
+DlgTableAlignCenter : "Sredinsko",
+DlgTableAlignRight : "Desno",
+DlgTableWidth : "Å irina",
+DlgTableWidthPx : "pik",
+DlgTableWidthPc : "procentov",
+DlgTableHeight : "Višina",
+DlgTableCellSpace : "Razmik med celicami",
+DlgTableCellPad : "Polnilo med celicami",
+DlgTableCaption : "Naslov",
+DlgTableSummary : "Povzetek",
+
+// Table Cell Dialog
+DlgCellTitle : "Lastnosti celice",
+DlgCellWidth : "Å irina",
+DlgCellWidthPx : "pik",
+DlgCellWidthPc : "procentov",
+DlgCellHeight : "Višina",
+DlgCellWordWrap : "Pomikanje besedila",
+DlgCellWordWrapNotSet : "<Ni nastavljeno>",
+DlgCellWordWrapYes : "Da",
+DlgCellWordWrapNo : "Ne",
+DlgCellHorAlign : "Vodoravna poravnava",
+DlgCellHorAlignNotSet : "<Ni nastavljeno>",
+DlgCellHorAlignLeft : "Levo",
+DlgCellHorAlignCenter : "Sredinsko",
+DlgCellHorAlignRight: "Desno",
+DlgCellVerAlign : "NavpiÄna poravnava",
+DlgCellVerAlignNotSet : "<Ni nastavljeno>",
+DlgCellVerAlignTop : "Na vrh",
+DlgCellVerAlignMiddle : "V sredino",
+DlgCellVerAlignBottom : "Na dno",
+DlgCellVerAlignBaseline : "Na osnovno Ärto",
+DlgCellRowSpan : "Spojenih vrstic (row-span)",
+DlgCellCollSpan : "Spojenih stolpcev (col-span)",
+DlgCellBackColor : "Barva ozadja",
+DlgCellBorderColor : "Barva obrobe",
+DlgCellBtnSelect : "Izberi...",
+
+// Find Dialog
+DlgFindTitle : "Najdi",
+DlgFindFindBtn : "Najdi",
+DlgFindNotFoundMsg : "Navedeno besedilo ni bilo najdeno.",
+
+// Replace Dialog
+DlgReplaceTitle : "Zamenjaj",
+DlgReplaceFindLbl : "Najdi:",
+DlgReplaceReplaceLbl : "Zamenjaj z:",
+DlgReplaceCaseChk : "Razlikuj velike in male Ärke",
+DlgReplaceReplaceBtn : "Zamenjaj",
+DlgReplaceReplAllBtn : "Zamenjaj vse",
+DlgReplaceWordChk : "Samo cele besede",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Varnostne nastavitve brskalnika ne dopuÅ¡Äajo samodejnega izrezovanja. Uporabite kombinacijo tipk na tipkovnici (Ctrl+X).",
+PasteErrorCopy : "Varnostne nastavitve brskalnika ne dopuÅ¡Äajo samodejnega kopiranja. Uporabite kombinacijo tipk na tipkovnici (Ctrl+C).",
+
+PasteAsText : "Prilepi kot golo besedilo",
+PasteFromWord : "Prilepi iz Worda",
+
+DlgPasteMsg2 : "Prosim prilepite v sleÄi okvir s pomoÄjo tipkovnice (<STRONG>Ctrl+V</STRONG>) in pritisnite <STRONG>V redu</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Prezri obliko pisave",
+DlgPasteRemoveStyles : "Odstrani nastavitve stila",
+DlgPasteCleanBox : "PoÄisti okvir",
+
+// Color Picker
+ColorAutomatic : "Samodejno",
+ColorMoreColors : "VeÄ barv...",
+
+// Document Properties
+DocProps : "Lastnosti dokumenta",
+
+// Anchor Dialog
+DlgAnchorTitle : "Lastnosti zaznamka",
+DlgAnchorName : "Ime zaznamka",
+DlgAnchorErrorName : "Prosim vnesite ime zaznamka",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ni v slovarju",
+DlgSpellChangeTo : "Spremeni v",
+DlgSpellBtnIgnore : "Prezri",
+DlgSpellBtnIgnoreAll : "Prezri vse",
+DlgSpellBtnReplace : "Zamenjaj",
+DlgSpellBtnReplaceAll : "Zamenjaj vse",
+DlgSpellBtnUndo : "Razveljavi",
+DlgSpellNoSuggestions : "- Ni predlogov -",
+DlgSpellProgress : "Preverjanje Ärkovanja se izvaja...",
+DlgSpellNoMispell : "ÄŒrkovanje je konÄano: Brez napak",
+DlgSpellNoChanges : "ÄŒrkovanje je konÄano: Nobena beseda ni bila spremenjena",
+DlgSpellOneChange : "ÄŒrkovanje je konÄano: Spremenjena je bila ena beseda",
+DlgSpellManyChanges : "ÄŒrkovanje je konÄano: Spremenjenih je bilo %1 besed",
+
+IeSpellDownload : "ÄŒrkovalnik ni nameÅ¡Äen. Ali ga želite prenesti sedaj?",
+
+// Button Dialog
+DlgButtonText : "Besedilo (Vrednost)",
+DlgButtonType : "Tip",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Ime",
+DlgCheckboxValue : "Vrednost",
+DlgCheckboxSelected : "Izbrano",
+
+// Form Dialog
+DlgFormName : "Ime",
+DlgFormAction : "Akcija",
+DlgFormMethod : "Metoda",
+
+// Select Field Dialog
+DlgSelectName : "Ime",
+DlgSelectValue : "Vrednost",
+DlgSelectSize : "Velikost",
+DlgSelectLines : "vrstic",
+DlgSelectChkMulti : "Dovoli izbor veÄih vrstic",
+DlgSelectOpAvail : "Razpoložljive izbire",
+DlgSelectOpText : "Besedilo",
+DlgSelectOpValue : "Vrednost",
+DlgSelectBtnAdd : "Dodaj",
+DlgSelectBtnModify : "Spremeni",
+DlgSelectBtnUp : "Gor",
+DlgSelectBtnDown : "Dol",
+DlgSelectBtnSetValue : "Postavi kot privzeto izbiro",
+DlgSelectBtnDelete : "Izbriši",
+
+// Textarea Dialog
+DlgTextareaName : "Ime",
+DlgTextareaCols : "Stolpcev",
+DlgTextareaRows : "Vrstic",
+
+// Text Field Dialog
+DlgTextName : "Ime",
+DlgTextValue : "Vrednost",
+DlgTextCharWidth : "Dolžina",
+DlgTextMaxChars : "NajveÄje Å¡tevilo znakov",
+DlgTextType : "Tip",
+DlgTextTypeText : "Besedilo",
+DlgTextTypePass : "Geslo",
+
+// Hidden Field Dialog
+DlgHiddenName : "Ime",
+DlgHiddenValue : "Vrednost",
+
+// Bulleted List Dialog
+BulletedListProp : "Lastnosti oznaÄenega seznama",
+NumberedListProp : "Lastnosti oÅ¡tevilÄenega seznama",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tip",
+DlgLstTypeCircle : "Pikica",
+DlgLstTypeDisc : "Kroglica",
+DlgLstTypeSquare : "Kvadratek",
+DlgLstTypeNumbers : "Å tevilke (1, 2, 3)",
+DlgLstTypeLCase : "Male Ärke (a, b, c)",
+DlgLstTypeUCase : "Velike Ärke (A, B, C)",
+DlgLstTypeSRoman : "Male rimske Å¡tevilke (i, ii, iii)",
+DlgLstTypeLRoman : "Velike rimske Å¡tevilke (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Splošno",
+DlgDocBackTab : "Ozadje",
+DlgDocColorsTab : "Barve in zamiki",
+DlgDocMetaTab : "Meta podatki",
+
+DlgDocPageTitle : "Naslov strani",
+DlgDocLangDir : "Smer jezika",
+DlgDocLangDirLTR : "Od leve proti desni (LTR)",
+DlgDocLangDirRTL : "Od desne proti levi (RTL)",
+DlgDocLangCode : "Oznaka jezika",
+DlgDocCharSet : "Kodna tabela",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Druga kodna tabela",
+
+DlgDocDocType : "Glava tipa dokumenta",
+DlgDocDocTypeOther : "Druga glava tipa dokumenta",
+DlgDocIncXHTML : "Vstavi XHTML deklaracije",
+DlgDocBgColor : "Barva ozadja",
+DlgDocBgImage : "URL slike za ozadje",
+DlgDocBgNoScroll : "NepremiÄno ozadje",
+DlgDocCText : "Besedilo",
+DlgDocCLink : "Povezava",
+DlgDocCVisited : "Obiskana povezava",
+DlgDocCActive : "Aktivna povezava",
+DlgDocMargins : "Zamiki strani",
+DlgDocMaTop : "Na vrhu",
+DlgDocMaLeft : "Levo",
+DlgDocMaRight : "Desno",
+DlgDocMaBottom : "Spodaj",
+DlgDocMeIndex : "KljuÄne besede (loÄene z vejicami)",
+DlgDocMeDescr : "Opis strani",
+DlgDocMeAuthor : "Avtor",
+DlgDocMeCopy : "Avtorske pravice",
+DlgDocPreview : "Predogled",
+
+// Templates Dialog
+Templates : "Predloge",
+DlgTemplatesTitle : "Vsebinske predloge",
+DlgTemplatesSelMsg : "Izberite predlogo, ki jo želite odpreti v urejevalniku<br>(trenutna vsebina bo izgubljena):",
+DlgTemplatesLoading : "Nalagam seznam predlog. Prosim poÄakajte...",
+DlgTemplatesNoTpl : "(Ni pripravljenih predlog)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "Vizitka",
+DlgAboutBrowserInfoTab : "Informacije o brskalniku",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "razliÄica",
+DlgAboutInfo : "Za veÄ informacij obiÅ¡Äite"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/sr-latn.js b/httemplate/elements/fckeditor/editor/lang/sr-latn.js
new file mode 100644
index 0000000..5fa8154
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/sr-latn.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Serbian (Latin) language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Smanji liniju sa alatkama",
+ToolbarExpand : "Proiri liniju sa alatkama",
+
+// Toolbar Items and Context Menu
+Save : "SaÄuvaj",
+NewPage : "Nova stranica",
+Preview : "Izgled stranice",
+Cut : "Iseci",
+Copy : "Kopiraj",
+Paste : "Zalepi",
+PasteText : "Zalepi kao neformatiran tekst",
+PasteWord : "Zalepi iz Worda",
+Print : "Å tampa",
+SelectAll : "OznaÄi sve",
+RemoveFormat : "Ukloni formatiranje",
+InsertLinkLbl : "Link",
+InsertLink : "Unesi/izmeni link",
+RemoveLink : "Ukloni link",
+Anchor : "Unesi/izmeni sidro",
+InsertImageLbl : "Slika",
+InsertImage : "Unesi/izmeni sliku",
+InsertFlashLbl : "Fleš",
+InsertFlash : "Unesi/izmeni fleš",
+InsertTableLbl : "Tabela",
+InsertTable : "Unesi/izmeni tabelu",
+InsertLineLbl : "Linija",
+InsertLine : "Unesi horizontalnu liniju",
+InsertSpecialCharLbl: "Specijalni karakteri",
+InsertSpecialChar : "Unesi specijalni karakter",
+InsertSmileyLbl : "Smajli",
+InsertSmiley : "Unesi smajlija",
+About : "O FCKeditoru",
+Bold : "Podebljano",
+Italic : "Kurziv",
+Underline : "PodvuÄeno",
+StrikeThrough : "Precrtano",
+Subscript : "Indeks",
+Superscript : "Stepen",
+LeftJustify : "Levo ravnanje",
+CenterJustify : "Centriran tekst",
+RightJustify : "Desno ravnanje",
+BlockJustify : "Obostrano ravnanje",
+DecreaseIndent : "Smanji levu marginu",
+IncreaseIndent : "Uvećaj levu marginu",
+Undo : "Poni�ti akciju",
+Redo : "Ponovi akciju",
+NumberedListLbl : "Nabrojiva lista",
+NumberedList : "Unesi/ukloni nabrojivu listu",
+BulletedListLbl : "Nenabrojiva lista",
+BulletedList : "Unesi/ukloni nenabrojivu listu",
+ShowTableBorders : "Prikaži okvir tabele",
+ShowDetails : "Prikaži detalje",
+Style : "Stil",
+FontFormat : "Format",
+Font : "Font",
+FontSize : "VeliÄina fonta",
+TextColor : "Boja teksta",
+BGColor : "Boja pozadine",
+Source : "Kôd",
+Find : "Pretraga",
+Replace : "Zamena",
+SpellCheck : "Proveri spelovanje",
+UniversalKeyboard : "Univerzalna tastatura",
+PageBreakLbl : "Page Break", //MISSING
+PageBreak : "Insert Page Break", //MISSING
+
+Form : "Forma",
+Checkbox : "Polje za potvrdu",
+RadioButton : "Radio-dugme",
+TextField : "Tekstualno polje",
+Textarea : "Zona teksta",
+HiddenField : "Skriveno polje",
+Button : "Dugme",
+SelectionField : "Izborno polje",
+ImageButton : "Dugme sa slikom",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Izmeni link",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "Unesi red",
+DeleteRows : "Obriši redove",
+InsertColumn : "Unesi kolonu",
+DeleteColumns : "Obriši kolone",
+InsertCell : "Unesi ćelije",
+DeleteCells : "Obriši ćelije",
+MergeCells : "Spoj celije",
+SplitCell : "Razdvoji celije",
+TableDelete : "Delete Table", //MISSING
+CellProperties : "Osobine celije",
+TableProperties : "Osobine tabele",
+ImageProperties : "Osobine slike",
+FlashProperties : "Osobine fleša",
+
+AnchorProp : "Osobine sidra",
+ButtonProp : "Osobine dugmeta",
+CheckboxProp : "Osobine polja za potvrdu",
+HiddenFieldProp : "Osobine skrivenog polja",
+RadioButtonProp : "Osobine radio-dugmeta",
+ImageButtonProp : "Osobine dugmeta sa slikom",
+TextFieldProp : "Osobine tekstualnog polja",
+SelectionFieldProp : "Osobine izbornog polja",
+TextareaProp : "Osobine zone teksta",
+FormProp : "Osobine forme",
+
+FontFormats : "Normal;Formatirano;Adresa;Naslov 1;Naslov 2;Naslov 3;Naslov 4;Naslov 5;Naslov 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Obradujem XHTML. Malo strpljenja...",
+Done : "Završio",
+PasteWordConfirm : "Tekst koji želite da nalepite kopiran je iz Worda. Da li želite da bude oÄišćen od formata pre lepljenja?",
+NotCompatiblePaste : "Ova komanda je dostupna samo za Internet Explorer od verzije 5.5. Da li želite da nalepim tekst bez Äišćenja?",
+UnknownToolbarItem : "Nepoznata stavka toolbara \"%1\"",
+UnknownCommand : "Nepoznata naredba \"%1\"",
+NotImplemented : "Naredba nije implementirana",
+UnknownToolbarSet : "Toolbar \"%1\" ne postoji",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Otkaži",
+DlgBtnClose : "Zatvori",
+DlgBtnBrowseServer : "Pretraži server",
+DlgAdvancedTag : "Napredni tagovi",
+DlgOpOther : "<Ostali>",
+DlgInfoTab : "Info",
+DlgAlertUrl : "Molimo Vas, unesite URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<nije postavljeno>",
+DlgGenId : "Id",
+DlgGenLangDir : "Smer jezika",
+DlgGenLangDirLtr : "S leva na desno (LTR)",
+DlgGenLangDirRtl : "S desna na levo (RTL)",
+DlgGenLangCode : "Kôd jezika",
+DlgGenAccessKey : "Pristupni taster",
+DlgGenName : "Naziv",
+DlgGenTabIndex : "Tab indeks",
+DlgGenLongDescr : "Pun opis URL",
+DlgGenClass : "Stylesheet klase",
+DlgGenTitle : "Advisory naslov",
+DlgGenContType : "Advisory vrsta sadržaja",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Stil",
+
+// Image Dialog
+DlgImgTitle : "Osobine slika",
+DlgImgInfoTab : "Info slike",
+DlgImgBtnUpload : "Pošalji na server",
+DlgImgURL : "URL",
+DlgImgUpload : "Pošalji",
+DlgImgAlt : "Alternativni tekst",
+DlgImgWidth : "Å irina",
+DlgImgHeight : "Visina",
+DlgImgLockRatio : "ZakljuÄaj odnos",
+DlgBtnResetSize : "Resetuj veliÄinu",
+DlgImgBorder : "Okvir",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Ravnanje",
+DlgImgAlignLeft : "Levo",
+DlgImgAlignAbsBottom: "Abs dole",
+DlgImgAlignAbsMiddle: "Abs sredina",
+DlgImgAlignBaseline : "Bazno",
+DlgImgAlignBottom : "Dole",
+DlgImgAlignMiddle : "Sredina",
+DlgImgAlignRight : "Desno",
+DlgImgAlignTextTop : "Vrh teksta",
+DlgImgAlignTop : "Vrh",
+DlgImgPreview : "Izgled",
+DlgImgAlertUrl : "Unesite URL slike",
+DlgImgLinkTab : "Link",
+
+// Flash Dialog
+DlgFlashTitle : "Osobine fleša",
+DlgFlashChkPlay : "Automatski start",
+DlgFlashChkLoop : "Ponavljaj",
+DlgFlashChkMenu : "UkljuÄi fleÅ¡ meni",
+DlgFlashScale : "Skaliraj",
+DlgFlashScaleAll : "Prikaži sve",
+DlgFlashScaleNoBorder : "Bez ivice",
+DlgFlashScaleFit : "Popuni površinu",
+
+// Link Dialog
+DlgLnkWindowTitle : "Link",
+DlgLnkInfoTab : "Link Info",
+DlgLnkTargetTab : "Meta",
+
+DlgLnkType : "Vrsta linka",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Sidro na ovoj stranici",
+DlgLnkTypeEMail : "E-Mail",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<drugo>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Odaberi sidro",
+DlgLnkAnchorByName : "Po nazivu sidra",
+DlgLnkAnchorById : "Po Id-ju elementa",
+DlgLnkNoAnchors : "<Nema dostupnih sidra>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Mail adresa",
+DlgLnkEMailSubject : "Naslov",
+DlgLnkEMailBody : "Sadržaj poruke",
+DlgLnkUpload : "Pošalji",
+DlgLnkBtnUpload : "Pošalji na server",
+
+DlgLnkTarget : "Meta",
+DlgLnkTargetFrame : "<okvir>",
+DlgLnkTargetPopup : "<popup prozor>",
+DlgLnkTargetBlank : "Novi prozor (_blank)",
+DlgLnkTargetParent : "Roditeljski prozor (_parent)",
+DlgLnkTargetSelf : "Isti prozor (_self)",
+DlgLnkTargetTop : "Prozor na vrhu (_top)",
+DlgLnkTargetFrameName : "Naziv odredišnog frejma",
+DlgLnkPopWinName : "Naziv popup prozora",
+DlgLnkPopWinFeat : "Mogućnosti popup prozora",
+DlgLnkPopResize : "Promenljiva velicina",
+DlgLnkPopLocation : "Lokacija",
+DlgLnkPopMenu : "Kontekstni meni",
+DlgLnkPopScroll : "Scroll bar",
+DlgLnkPopStatus : "Statusna linija",
+DlgLnkPopToolbar : "Toolbar",
+DlgLnkPopFullScrn : "Prikaz preko celog ekrana (IE)",
+DlgLnkPopDependent : "Zavisno (Netscape)",
+DlgLnkPopWidth : "Å irina",
+DlgLnkPopHeight : "Visina",
+DlgLnkPopLeft : "Od leve ivice ekrana (px)",
+DlgLnkPopTop : "Od vrha ekrana (px)",
+
+DlnLnkMsgNoUrl : "Unesite URL linka",
+DlnLnkMsgNoEMail : "Otkucajte adresu elektronske pote",
+DlnLnkMsgNoAnchor : "Odaberite sidro",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Odaberite boju",
+DlgColorBtnClear : "Obriši",
+DlgColorHighlight : "Posvetli",
+DlgColorSelected : "Odaberi",
+
+// Smiley Dialog
+DlgSmileyTitle : "Unesi smajlija",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Odaberite specijalni karakter",
+
+// Table Dialog
+DlgTableTitle : "Osobine tabele",
+DlgTableRows : "Redova",
+DlgTableColumns : "Kolona",
+DlgTableBorder : "VeliÄina okvira",
+DlgTableAlign : "Ravnanje",
+DlgTableAlignNotSet : "<nije postavljeno>",
+DlgTableAlignLeft : "Levo",
+DlgTableAlignCenter : "Sredina",
+DlgTableAlignRight : "Desno",
+DlgTableWidth : "Å irina",
+DlgTableWidthPx : "piksela",
+DlgTableWidthPc : "procenata",
+DlgTableHeight : "Visina",
+DlgTableCellSpace : "Ćelijski prostor",
+DlgTableCellPad : "Razmak ćelija",
+DlgTableCaption : "Naslov tabele",
+DlgTableSummary : "Summary", //MISSING
+
+// Table Cell Dialog
+DlgCellTitle : "Osobine ćelije",
+DlgCellWidth : "Å irina",
+DlgCellWidthPx : "piksela",
+DlgCellWidthPc : "procenata",
+DlgCellHeight : "Visina",
+DlgCellWordWrap : "Deljenje reÄi",
+DlgCellWordWrapNotSet : "<nije postavljeno>",
+DlgCellWordWrapYes : "Da",
+DlgCellWordWrapNo : "Ne",
+DlgCellHorAlign : "Vodoravno ravnanje",
+DlgCellHorAlignNotSet : "<nije postavljeno>",
+DlgCellHorAlignLeft : "Levo",
+DlgCellHorAlignCenter : "Sredina",
+DlgCellHorAlignRight: "Desno",
+DlgCellVerAlign : "Vertikalno ravnanje",
+DlgCellVerAlignNotSet : "<nije postavljeno>",
+DlgCellVerAlignTop : "Gornje",
+DlgCellVerAlignMiddle : "Sredina",
+DlgCellVerAlignBottom : "Donje",
+DlgCellVerAlignBaseline : "Bazno",
+DlgCellRowSpan : "Spajanje redova",
+DlgCellCollSpan : "Spajanje kolona",
+DlgCellBackColor : "Boja pozadine",
+DlgCellBorderColor : "Boja okvira",
+DlgCellBtnSelect : "Odaberi...",
+
+// Find Dialog
+DlgFindTitle : "Pronađi",
+DlgFindFindBtn : "Pronađi",
+DlgFindNotFoundMsg : "Traženi tekst nije pronađen.",
+
+// Replace Dialog
+DlgReplaceTitle : "Zameni",
+DlgReplaceFindLbl : "Pronadi:",
+DlgReplaceReplaceLbl : "Zameni sa:",
+DlgReplaceCaseChk : "Razlikuj mala i velika slova",
+DlgReplaceReplaceBtn : "Zameni",
+DlgReplaceReplAllBtn : "Zameni sve",
+DlgReplaceWordChk : "Uporedi cele reci",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Sigurnosna podeÅ¡avanja VaÅ¡eg pretraživaÄa ne dozvoljavaju operacije automatskog isecanja teksta. Molimo Vas da koristite preÄicu sa tastature (Ctrl+X).",
+PasteErrorCopy : "Sigurnosna podeÅ¡avanja VaÅ¡eg pretraživaÄa ne dozvoljavaju operacije automatskog kopiranja teksta. Molimo Vas da koristite preÄicu sa tastature (Ctrl+C).",
+
+PasteAsText : "Zalepi kao Äist tekst",
+PasteFromWord : "Zalepi iz Worda",
+
+DlgPasteMsg2 : "Molimo Vas da zalepite unutar donje povrine koristeći tastaturnu preÄicu (<STRONG>Ctrl+V</STRONG>) i da pritisnete <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Ignoriši definicije fontova",
+DlgPasteRemoveStyles : "Ukloni definicije stilova",
+DlgPasteCleanBox : "Obriši sve",
+
+// Color Picker
+ColorAutomatic : "Automatski",
+ColorMoreColors : "Više boja...",
+
+// Document Properties
+DocProps : "Osobine dokumenta",
+
+// Anchor Dialog
+DlgAnchorTitle : "Osobine sidra",
+DlgAnchorName : "Ime sidra",
+DlgAnchorErrorName : "Unesite ime sidra",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Nije u reÄniku",
+DlgSpellChangeTo : "Izmeni",
+DlgSpellBtnIgnore : "Ignoriši",
+DlgSpellBtnIgnoreAll : "Ignoriši sve",
+DlgSpellBtnReplace : "Zameni",
+DlgSpellBtnReplaceAll : "Zameni sve",
+DlgSpellBtnUndo : "Vrati akciju",
+DlgSpellNoSuggestions : "- Bez sugestija -",
+DlgSpellProgress : "Provera spelovanja u toku...",
+DlgSpellNoMispell : "Provera spelovanja završena: greške nisu pronadene",
+DlgSpellNoChanges : "Provera spelovanja završena: Nije izmenjena nijedna rec",
+DlgSpellOneChange : "Provera spelovanja zavrÅ¡ena: Izmenjena je jedna reÄ",
+DlgSpellManyChanges : "Provera spelovanja zavrÅ¡ena: %1 reÄ(i) je izmenjeno",
+
+IeSpellDownload : "Provera spelovanja nije instalirana. Da li želite da je skinete sa Interneta?",
+
+// Button Dialog
+DlgButtonText : "Tekst (vrednost)",
+DlgButtonType : "Tip",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Naziv",
+DlgCheckboxValue : "Vrednost",
+DlgCheckboxSelected : "OznaÄeno",
+
+// Form Dialog
+DlgFormName : "Naziv",
+DlgFormAction : "Akcija",
+DlgFormMethod : "Metoda",
+
+// Select Field Dialog
+DlgSelectName : "Naziv",
+DlgSelectValue : "Vrednost",
+DlgSelectSize : "VeliÄina",
+DlgSelectLines : "linija",
+DlgSelectChkMulti : "Dozvoli višestruku selekciju",
+DlgSelectOpAvail : "Dostupne opcije",
+DlgSelectOpText : "Tekst",
+DlgSelectOpValue : "Vrednost",
+DlgSelectBtnAdd : "Dodaj",
+DlgSelectBtnModify : "Izmeni",
+DlgSelectBtnUp : "Gore",
+DlgSelectBtnDown : "Dole",
+DlgSelectBtnSetValue : "Podesi kao oznaÄenu vrednost",
+DlgSelectBtnDelete : "Obriši",
+
+// Textarea Dialog
+DlgTextareaName : "Naziv",
+DlgTextareaCols : "Broj kolona",
+DlgTextareaRows : "Broj redova",
+
+// Text Field Dialog
+DlgTextName : "Naziv",
+DlgTextValue : "Vrednost",
+DlgTextCharWidth : "Å irina (karaktera)",
+DlgTextMaxChars : "Maksimalno karaktera",
+DlgTextType : "Tip",
+DlgTextTypeText : "Tekst",
+DlgTextTypePass : "Lozinka",
+
+// Hidden Field Dialog
+DlgHiddenName : "Naziv",
+DlgHiddenValue : "Vrednost",
+
+// Bulleted List Dialog
+BulletedListProp : "Osobine nenabrojive liste",
+NumberedListProp : "Osobine nabrojive liste",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Tip",
+DlgLstTypeCircle : "Krug",
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "Kvadrat",
+DlgLstTypeNumbers : "Brojevi (1, 2, 3)",
+DlgLstTypeLCase : "mala slova (a, b, c)",
+DlgLstTypeUCase : "VELIKA slova (A, B, C)",
+DlgLstTypeSRoman : "Male rimske cifre (i, ii, iii)",
+DlgLstTypeLRoman : "Velike rimske cifre (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Opšte osobine",
+DlgDocBackTab : "Pozadina",
+DlgDocColorsTab : "Boje i margine",
+DlgDocMetaTab : "Metapodaci",
+
+DlgDocPageTitle : "Naslov stranice",
+DlgDocLangDir : "Smer jezika",
+DlgDocLangDirLTR : "Sleva nadesno (LTR)",
+DlgDocLangDirRTL : "Zdesna nalevo (RTL)",
+DlgDocLangCode : "Å ifra jezika",
+DlgDocCharSet : "Kodiranje skupa karaktera",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "Ostala kodiranja skupa karaktera",
+
+DlgDocDocType : "Zaglavlje tipa dokumenta",
+DlgDocDocTypeOther : "Ostala zaglavlja tipa dokumenta",
+DlgDocIncXHTML : "Ukljuci XHTML deklaracije",
+DlgDocBgColor : "Boja pozadine",
+DlgDocBgImage : "URL pozadinske slike",
+DlgDocBgNoScroll : "Fiksirana pozadina",
+DlgDocCText : "Tekst",
+DlgDocCLink : "Link",
+DlgDocCVisited : "Posećeni link",
+DlgDocCActive : "Aktivni link",
+DlgDocMargins : "Margine stranice",
+DlgDocMaTop : "Gornja",
+DlgDocMaLeft : "Leva",
+DlgDocMaRight : "Desna",
+DlgDocMaBottom : "Donja",
+DlgDocMeIndex : "KljuÄne reci za indeksiranje dokumenta (razdvojene zarezima)",
+DlgDocMeDescr : "Opis dokumenta",
+DlgDocMeAuthor : "Autor",
+DlgDocMeCopy : "Autorska prava",
+DlgDocPreview : "Izgled stranice",
+
+// Templates Dialog
+Templates : "Obrasci",
+DlgTemplatesTitle : "Obrasci za sadržaj",
+DlgTemplatesSelMsg : "Molimo Vas da odaberete obrazac koji ce biti primenjen na stranicu (trenutni sadržaj ce biti obrisan):",
+DlgTemplatesLoading : "UÄitavam listu obrazaca. Malo strpljenja...",
+DlgTemplatesNoTpl : "(Nema definisanih obrazaca)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "O editoru",
+DlgAboutBrowserInfoTab : "Informacije o pretraživacu",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "verzija",
+DlgAboutInfo : "Za više informacija posetite"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/sr.js b/httemplate/elements/fckeditor/editor/lang/sr.js
new file mode 100644
index 0000000..e7aac23
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/sr.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Serbian (Cyrillic) language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Смањи линију Ñа алаткама",
+ToolbarExpand : "Прошири линију Ñа алаткама",
+
+// Toolbar Items and Context Menu
+Save : "Сачувај",
+NewPage : "Ðова Ñтраница",
+Preview : "Изглед Ñтранице",
+Cut : "ИÑеци",
+Copy : "Копирај",
+Paste : "Залепи",
+PasteText : "Залепи као неформатиран текÑÑ‚",
+PasteWord : "Залепи из Worda",
+Print : "Штампа",
+SelectAll : "Означи Ñве",
+RemoveFormat : "Уклони форматирање",
+InsertLinkLbl : "Линк",
+InsertLink : "УнеÑи/измени линк",
+RemoveLink : "Уклони линк",
+Anchor : "УнеÑи/измени Ñидро",
+InsertImageLbl : "Слика",
+InsertImage : "УнеÑи/измени Ñлику",
+InsertFlashLbl : "Флеш елемент",
+InsertFlash : "УнеÑи/измени флеш",
+InsertTableLbl : "Табела",
+InsertTable : "УнеÑи/измени табелу",
+InsertLineLbl : "Линија",
+InsertLine : "УнеÑи хоризонталну линију",
+InsertSpecialCharLbl: "Специјални карактери",
+InsertSpecialChar : "УнеÑи Ñпецијални карактер",
+InsertSmileyLbl : "Смајли",
+InsertSmiley : "УнеÑи Ñмајлија",
+About : "О ФЦКедитору",
+Bold : "Подебљано",
+Italic : "Курзив",
+Underline : "Подвучено",
+StrikeThrough : "Прецртано",
+Subscript : "ИндекÑ",
+Superscript : "Степен",
+LeftJustify : "Лево равнање",
+CenterJustify : "Центриран текÑÑ‚",
+RightJustify : "ДеÑно равнање",
+BlockJustify : "ОбоÑтрано равнање",
+DecreaseIndent : "Смањи леву маргину",
+IncreaseIndent : "Увећај леву маргину",
+Undo : "Поништи акцију",
+Redo : "Понови акцију",
+NumberedListLbl : "Ðабројиву лиÑту",
+NumberedList : "УнеÑи/уклони набројиву лиÑту",
+BulletedListLbl : "Ðенабројива лиÑта",
+BulletedList : "УнеÑи/уклони ненабројиву лиÑту",
+ShowTableBorders : "Прикажи оквир табеле",
+ShowDetails : "Прикажи детаље",
+Style : "Стил",
+FontFormat : "Формат",
+Font : "Фонт",
+FontSize : "Величина фонта",
+TextColor : "Боја текÑта",
+BGColor : "Боја позадине",
+Source : "Kôд",
+Find : "Претрага",
+Replace : "Замена",
+SpellCheck : "Провери Ñпеловање",
+UniversalKeyboard : "Универзална таÑтатура",
+PageBreakLbl : "Page Break", //MISSING
+PageBreak : "Insert Page Break", //MISSING
+
+Form : "Форма",
+Checkbox : "Поље за потврду",
+RadioButton : "Радио-дугме",
+TextField : "ТекÑтуално поље",
+Textarea : "Зона текÑта",
+HiddenField : "Скривено поље",
+Button : "Дугме",
+SelectionField : "Изборно поље",
+ImageButton : "Дугме Ñа Ñликом",
+
+FitWindow : "Maximize the editor size", //MISSING
+
+// Context Menu
+EditLink : "Промени линк",
+CellCM : "Cell", //MISSING
+RowCM : "Row", //MISSING
+ColumnCM : "Column", //MISSING
+InsertRow : "УнеÑи ред",
+DeleteRows : "Обриши редове",
+InsertColumn : "УнеÑи колону",
+DeleteColumns : "Обриши колоне",
+InsertCell : "УнеÑи ћелије",
+DeleteCells : "Обриши ћелије",
+MergeCells : "Спој ћелије",
+SplitCell : "Раздвоји ћелије",
+TableDelete : "Delete Table", //MISSING
+CellProperties : "ОÑобине ћелије",
+TableProperties : "ОÑобине табеле",
+ImageProperties : "ОÑобине Ñлике",
+FlashProperties : "ОÑобине Флеша",
+
+AnchorProp : "ОÑобине Ñидра",
+ButtonProp : "ОÑобине дугмета",
+CheckboxProp : "ОÑобине поља за потврду",
+HiddenFieldProp : "ОÑобине Ñкривеног поља",
+RadioButtonProp : "ОÑобине радио-дугмета",
+ImageButtonProp : "ОÑобине дугмета Ñа Ñликом",
+TextFieldProp : "ОÑобине текÑтуалног поља",
+SelectionFieldProp : "ОÑобине изборног поља",
+TextareaProp : "ОÑобине зоне текÑта",
+FormProp : "ОÑобине форме",
+
+FontFormats : "Normal;Formatirano;Adresa;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Обрађујем XHTML. Maлo Ñтрпљења...",
+Done : "Завршио",
+PasteWordConfirm : "ТекÑÑ‚ који желите да налепите копиран је из Worda. Да ли желите да буде очишћен од формата пре лепљења?",
+NotCompatiblePaste : "Ова команда је доÑтупна Ñамо за Интернет Екплорер од верзије 5.5. Да ли желите да налепим текÑÑ‚ без чишћења?",
+UnknownToolbarItem : "Ðепозната Ñтавка toolbara \"%1\"",
+UnknownCommand : "Ðепозната наредба \"%1\"",
+NotImplemented : "Ðаредба није имплементирана",
+UnknownToolbarSet : "Toolbar \"%1\" не поÑтоји",
+NoActiveX : "Your browser's security settings could limit some features of the editor. You must enable the option \"Run ActiveX controls and plug-ins\". You may experience errors and notice missing features.", //MISSING
+BrowseServerBlocked : "The resources browser could not be opened. Make sure that all popup blockers are disabled.", //MISSING
+DialogBlocked : "It was not possible to open the dialog window. Make sure all popup blockers are disabled.", //MISSING
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Oткажи",
+DlgBtnClose : "Затвори",
+DlgBtnBrowseServer : "Претражи Ñервер",
+DlgAdvancedTag : "Ðапредни тагови",
+DlgOpOther : "<ОÑтали>",
+DlgInfoTab : "Инфо",
+DlgAlertUrl : "Молимо ВаÑ, унеÑите УРЛ",
+
+// General Dialogs Labels
+DlgGenNotSet : "<није поÑтављено>",
+DlgGenId : "Ид",
+DlgGenLangDir : "Смер језика",
+DlgGenLangDirLtr : "С лева на деÑно (LTR)",
+DlgGenLangDirRtl : "С деÑна на лево (RTL)",
+DlgGenLangCode : "Kôд језика",
+DlgGenAccessKey : "ПриÑтупни таÑтер",
+DlgGenName : "Ðазив",
+DlgGenTabIndex : "Таб индекÑ",
+DlgGenLongDescr : "Пун Ð¾Ð¿Ð¸Ñ Ð£Ð Ð›",
+DlgGenClass : "Stylesheet клаÑе",
+DlgGenTitle : "Advisory наÑлов",
+DlgGenContType : "Advisory врÑта Ñадржаја",
+DlgGenLinkCharset : "Linked Resource Charset",
+DlgGenStyle : "Стил",
+
+// Image Dialog
+DlgImgTitle : "ОÑобине Ñлика",
+DlgImgInfoTab : "Инфо Ñлике",
+DlgImgBtnUpload : "Пошаљи на Ñервер",
+DlgImgURL : "УРЛ",
+DlgImgUpload : "Пошаљи",
+DlgImgAlt : "Ðлтернативни текÑÑ‚",
+DlgImgWidth : "Ширина",
+DlgImgHeight : "ВиÑина",
+DlgImgLockRatio : "Закључај одноÑ",
+DlgBtnResetSize : "РеÑетуј величину",
+DlgImgBorder : "Оквир",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Равнање",
+DlgImgAlignLeft : "Лево",
+DlgImgAlignAbsBottom: "Abs доле",
+DlgImgAlignAbsMiddle: "Abs Ñредина",
+DlgImgAlignBaseline : "Базно",
+DlgImgAlignBottom : "Доле",
+DlgImgAlignMiddle : "Средина",
+DlgImgAlignRight : "ДеÑно",
+DlgImgAlignTextTop : "Врх текÑта",
+DlgImgAlignTop : "Врх",
+DlgImgPreview : "Изглед",
+DlgImgAlertUrl : "УнеÑите УРЛ Ñлике",
+DlgImgLinkTab : "Линк",
+
+// Flash Dialog
+DlgFlashTitle : "ОÑобине флеша",
+DlgFlashChkPlay : "ÐутоматÑки Ñтарт",
+DlgFlashChkLoop : "Понављај",
+DlgFlashChkMenu : "Укључи флеш мени",
+DlgFlashScale : "Скалирај",
+DlgFlashScaleAll : "Прикажи Ñве",
+DlgFlashScaleNoBorder : "Без ивице",
+DlgFlashScaleFit : "Попуни површину",
+
+// Link Dialog
+DlgLnkWindowTitle : "Линк",
+DlgLnkInfoTab : "Линк инфо",
+DlgLnkTargetTab : "Мета",
+
+DlgLnkType : "Ð’Ñ€Ñта линка",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Сидро на овој Ñтраници",
+DlgLnkTypeEMail : "EлектронÑка пошта",
+DlgLnkProto : "Протокол",
+DlgLnkProtoOther : "<друго>",
+DlgLnkURL : "УРЛ",
+DlgLnkAnchorSel : "Одабери Ñидро",
+DlgLnkAnchorByName : "По називу Ñидра",
+DlgLnkAnchorById : "Пo Ид-jу елемента",
+DlgLnkNoAnchors : "<Ðема доÑтупних Ñидра>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ÐдреÑа електронÑке поште",
+DlgLnkEMailSubject : "ÐаÑлов",
+DlgLnkEMailBody : "Садржај поруке",
+DlgLnkUpload : "Пошаљи",
+DlgLnkBtnUpload : "Пошаљи на Ñервер",
+
+DlgLnkTarget : "Meтa",
+DlgLnkTargetFrame : "<оквир>",
+DlgLnkTargetPopup : "<иÑкачући прозор>",
+DlgLnkTargetBlank : "Ðови прозор (_blank)",
+DlgLnkTargetParent : "РодитељÑки прозор (_parent)",
+DlgLnkTargetSelf : "ИÑти прозор (_self)",
+DlgLnkTargetTop : "Прозор на врху (_top)",
+DlgLnkTargetFrameName : "Ðазив одредишног фрејма",
+DlgLnkPopWinName : "Ðазив иÑкачућег прозора",
+DlgLnkPopWinFeat : "МогућноÑти иÑкачућег прозора",
+DlgLnkPopResize : "Променљива величина",
+DlgLnkPopLocation : "Локација",
+DlgLnkPopMenu : "КонтекÑтни мени",
+DlgLnkPopScroll : "Скрол бар",
+DlgLnkPopStatus : "СтатуÑна линија",
+DlgLnkPopToolbar : "Toolbar",
+DlgLnkPopFullScrn : "Приказ преко целог екрана (ИE)",
+DlgLnkPopDependent : "ЗавиÑно (Netscape)",
+DlgLnkPopWidth : "Ширина",
+DlgLnkPopHeight : "ВиÑина",
+DlgLnkPopLeft : "Од леве ивице екрана (пикÑела)",
+DlgLnkPopTop : "Од врха екрана (пикÑела)",
+
+DlnLnkMsgNoUrl : "УнеÑите УРЛ линка",
+DlnLnkMsgNoEMail : "Откуцајте адреÑу електронÑке поште",
+DlnLnkMsgNoAnchor : "Одаберите Ñидро",
+DlnLnkMsgInvPopName : "The popup name must begin with an alphabetic character and must not contain spaces", //MISSING
+
+// Color Dialog
+DlgColorTitle : "Одаберите боју",
+DlgColorBtnClear : "Обриши",
+DlgColorHighlight : "ПоÑветли",
+DlgColorSelected : "Одабери",
+
+// Smiley Dialog
+DlgSmileyTitle : "УнеÑи Ñмајлија",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Одаберите Ñпецијални карактер",
+
+// Table Dialog
+DlgTableTitle : "ОÑобине табеле",
+DlgTableRows : "Редова",
+DlgTableColumns : "Kолона",
+DlgTableBorder : "Величина оквира",
+DlgTableAlign : "Равнање",
+DlgTableAlignNotSet : "<није поÑтављено>",
+DlgTableAlignLeft : "Лево",
+DlgTableAlignCenter : "Средина",
+DlgTableAlignRight : "ДеÑно",
+DlgTableWidth : "Ширина",
+DlgTableWidthPx : "пикÑела",
+DlgTableWidthPc : "процената",
+DlgTableHeight : "ВиÑина",
+DlgTableCellSpace : "ЋелијÑки проÑтор",
+DlgTableCellPad : "Размак ћелија",
+DlgTableCaption : "ÐаÑлов табеле",
+DlgTableSummary : "Summary", //MISSING
+
+// Table Cell Dialog
+DlgCellTitle : "ОÑобине ћелије",
+DlgCellWidth : "Ширина",
+DlgCellWidthPx : "пикÑела",
+DlgCellWidthPc : "процената",
+DlgCellHeight : "ВиÑина",
+DlgCellWordWrap : "Дељење речи",
+DlgCellWordWrapNotSet : "<није поÑтављено>",
+DlgCellWordWrapYes : "Да",
+DlgCellWordWrapNo : "Ðе",
+DlgCellHorAlign : "Водоравно равнање",
+DlgCellHorAlignNotSet : "<није поÑтављено>",
+DlgCellHorAlignLeft : "Лево",
+DlgCellHorAlignCenter : "Средина",
+DlgCellHorAlignRight: "ДеÑно",
+DlgCellVerAlign : "Вертикално равнање",
+DlgCellVerAlignNotSet : "<није поÑтављено>",
+DlgCellVerAlignTop : "Горње",
+DlgCellVerAlignMiddle : "Средина",
+DlgCellVerAlignBottom : "Доње",
+DlgCellVerAlignBaseline : "Базно",
+DlgCellRowSpan : "Спајање редова",
+DlgCellCollSpan : "Спајање колона",
+DlgCellBackColor : "Боја позадине",
+DlgCellBorderColor : "Боја оквира",
+DlgCellBtnSelect : "Oдабери...",
+
+// Find Dialog
+DlgFindTitle : "Пронађи",
+DlgFindFindBtn : "Пронађи",
+DlgFindNotFoundMsg : "Тражени текÑÑ‚ није пронађен.",
+
+// Replace Dialog
+DlgReplaceTitle : "Замени",
+DlgReplaceFindLbl : "Пронађи:",
+DlgReplaceReplaceLbl : "Замени Ñа:",
+DlgReplaceCaseChk : "Разликуј велика и мала Ñлова",
+DlgReplaceReplaceBtn : "Замени",
+DlgReplaceReplAllBtn : "Замени Ñве",
+DlgReplaceWordChk : "Упореди целе речи",
+
+// Paste Operations / Dialog
+PasteErrorCut : "СигурноÑна подешавања Вашег претраживача не дозвољавају операције аутоматÑког иÑецања текÑта. Молимо Ð’Ð°Ñ Ð´Ð° кориÑтите пречицу Ñа таÑтатуре (Ctrl+X).",
+PasteErrorCopy : "СигурноÑна подешавања Вашег претраживача не дозвољавају операције аутоматÑког копирања текÑта. Молимо Ð’Ð°Ñ Ð´Ð° кориÑтите пречицу Ñа таÑтатуре (Ctrl+C).",
+
+PasteAsText : "Залепи као чиÑÑ‚ текÑÑ‚",
+PasteFromWord : "Залепи из Worda",
+
+DlgPasteMsg2 : "Молимо Ð’Ð°Ñ Ð´Ð° залепите унутар доње површине кориÑтећи таÑтатурну пречицу (<STRONG>Ctrl+V</STRONG>) и да притиÑнете <STRONG>OK</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Игнориши Font Face дефиниције",
+DlgPasteRemoveStyles : "Уклони дефиниције Ñтилова",
+DlgPasteCleanBox : "Обриши Ñве",
+
+// Color Picker
+ColorAutomatic : "ÐутоматÑки",
+ColorMoreColors : "Више боја...",
+
+// Document Properties
+DocProps : "ОÑобине документа",
+
+// Anchor Dialog
+DlgAnchorTitle : "ОÑобине Ñидра",
+DlgAnchorName : "Име Ñидра",
+DlgAnchorErrorName : "Молимо Ð’Ð°Ñ Ð´Ð° унеÑете име Ñидра",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ðије у речнику",
+DlgSpellChangeTo : "Измени",
+DlgSpellBtnIgnore : "Игнориши",
+DlgSpellBtnIgnoreAll : "Игнориши Ñве",
+DlgSpellBtnReplace : "Замени",
+DlgSpellBtnReplaceAll : "Замени Ñве",
+DlgSpellBtnUndo : "Врати акцију",
+DlgSpellNoSuggestions : "- Без ÑугеÑтија -",
+DlgSpellProgress : "Провера Ñпеловања у току...",
+DlgSpellNoMispell : "Провера Ñпеловања завршена: грешке ниÑу пронађене",
+DlgSpellNoChanges : "Провера Ñпеловања завршена: Ðије измењена ниједна реч",
+DlgSpellOneChange : "Провера Ñпеловања завршена: Измењена је једна реч",
+DlgSpellManyChanges : "Провера Ñпеловања завршена: %1 реч(и) је измењено",
+
+IeSpellDownload : "Провера Ñпеловања није инÑталирана. Да ли желите да је Ñкинете Ñа Интернета?",
+
+// Button Dialog
+DlgButtonText : "ТекÑÑ‚ (вредноÑÑ‚)",
+DlgButtonType : "Tип",
+DlgButtonTypeBtn : "Button", //MISSING
+DlgButtonTypeSbm : "Submit", //MISSING
+DlgButtonTypeRst : "Reset", //MISSING
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Ðазив",
+DlgCheckboxValue : "ВредноÑÑ‚",
+DlgCheckboxSelected : "Означено",
+
+// Form Dialog
+DlgFormName : "Ðазив",
+DlgFormAction : "Aкција",
+DlgFormMethod : "Mетода",
+
+// Select Field Dialog
+DlgSelectName : "Ðазив",
+DlgSelectValue : "ВредноÑÑ‚",
+DlgSelectSize : "Величина",
+DlgSelectLines : "линија",
+DlgSelectChkMulti : "Дозволи вишеÑтруку Ñелекцију",
+DlgSelectOpAvail : "ДоÑтупне опције",
+DlgSelectOpText : "ТекÑÑ‚",
+DlgSelectOpValue : "ВредноÑÑ‚",
+DlgSelectBtnAdd : "Додај",
+DlgSelectBtnModify : "Измени",
+DlgSelectBtnUp : "Горе",
+DlgSelectBtnDown : "Доле",
+DlgSelectBtnSetValue : "ПодеÑи као означену вредноÑÑ‚",
+DlgSelectBtnDelete : "Обриши",
+
+// Textarea Dialog
+DlgTextareaName : "Ðазив",
+DlgTextareaCols : "Број колона",
+DlgTextareaRows : "Број редова",
+
+// Text Field Dialog
+DlgTextName : "Ðазив",
+DlgTextValue : "ВредноÑÑ‚",
+DlgTextCharWidth : "Ширина (карактера)",
+DlgTextMaxChars : "МакÑимално карактера",
+DlgTextType : "Тип",
+DlgTextTypeText : "ТекÑÑ‚",
+DlgTextTypePass : "Лозинка",
+
+// Hidden Field Dialog
+DlgHiddenName : "Ðазив",
+DlgHiddenValue : "ВредноÑÑ‚",
+
+// Bulleted List Dialog
+BulletedListProp : "ОÑобине Bulleted лиÑте",
+NumberedListProp : "ОÑобине набројиве лиÑте",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Тип",
+DlgLstTypeCircle : "Круг",
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "Квадрат",
+DlgLstTypeNumbers : "Бројеви (1, 2, 3)",
+DlgLstTypeLCase : "мала Ñлова (a, b, c)",
+DlgLstTypeUCase : "ВЕЛИКРСЛОВР(A, B, C)",
+DlgLstTypeSRoman : "Мале римÑке цифре (i, ii, iii)",
+DlgLstTypeLRoman : "Велике римÑке цифре (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Опште оÑобине",
+DlgDocBackTab : "Позадина",
+DlgDocColorsTab : "Боје и маргине",
+DlgDocMetaTab : "Метаподаци",
+
+DlgDocPageTitle : "ÐаÑлов Ñтранице",
+DlgDocLangDir : "Смер језика",
+DlgDocLangDirLTR : "Слева надеÑно (LTR)",
+DlgDocLangDirRTL : "ЗдеÑна налево (RTL)",
+DlgDocLangCode : "Шифра језика",
+DlgDocCharSet : "Кодирање Ñкупа карактера",
+DlgDocCharSetCE : "Central European", //MISSING
+DlgDocCharSetCT : "Chinese Traditional (Big5)", //MISSING
+DlgDocCharSetCR : "Cyrillic", //MISSING
+DlgDocCharSetGR : "Greek", //MISSING
+DlgDocCharSetJP : "Japanese", //MISSING
+DlgDocCharSetKR : "Korean", //MISSING
+DlgDocCharSetTR : "Turkish", //MISSING
+DlgDocCharSetUN : "Unicode (UTF-8)", //MISSING
+DlgDocCharSetWE : "Western European", //MISSING
+DlgDocCharSetOther : "ОÑтала кодирања Ñкупа карактера",
+
+DlgDocDocType : "Заглавље типа документа",
+DlgDocDocTypeOther : "ОÑтала заглавља типа документа",
+DlgDocIncXHTML : "Улључи XHTML декларације",
+DlgDocBgColor : "Боја позадине",
+DlgDocBgImage : "УРЛ позадинÑке Ñлике",
+DlgDocBgNoScroll : "ФикÑирана позадина",
+DlgDocCText : "ТекÑÑ‚",
+DlgDocCLink : "Линк",
+DlgDocCVisited : "ПоÑећени линк",
+DlgDocCActive : "Ðктивни линк",
+DlgDocMargins : "Маргине Ñтранице",
+DlgDocMaTop : "Горња",
+DlgDocMaLeft : "Лева",
+DlgDocMaRight : "ДеÑна",
+DlgDocMaBottom : "Доња",
+DlgDocMeIndex : "Кључне речи за индекÑирање документа (раздвојене зарезом)",
+DlgDocMeDescr : "ÐžÐ¿Ð¸Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°",
+DlgDocMeAuthor : "Ðутор",
+DlgDocMeCopy : "ÐуторÑка права",
+DlgDocPreview : "Изглед Ñтранице",
+
+// Templates Dialog
+Templates : "ОбраÑци",
+DlgTemplatesTitle : "ОбраÑци за Ñадржај",
+DlgTemplatesSelMsg : "Молимо Ð’Ð°Ñ Ð´Ð° одаберете образац који ће бити примењен на Ñтраницу (тренутни Ñадржај ће бити обриÑан):",
+DlgTemplatesLoading : "Учитавам лиÑту образаца. Мало Ñтрпљења...",
+DlgTemplatesNoTpl : "(Ðема дефиниÑаних образаца)",
+DlgTemplatesReplace : "Replace actual contents", //MISSING
+
+// About Dialog
+DlgAboutAboutTab : "О едитору",
+DlgAboutBrowserInfoTab : "Информације о претраживачу",
+DlgAboutLicenseTab : "License", //MISSING
+DlgAboutVersion : "верзија",
+DlgAboutInfo : "За више информација поÑетите"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/sv.js b/httemplate/elements/fckeditor/editor/lang/sv.js
new file mode 100644
index 0000000..a301a03
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/sv.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Swedish language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Dölj verktygsfält",
+ToolbarExpand : "Visa verktygsfält",
+
+// Toolbar Items and Context Menu
+Save : "Spara",
+NewPage : "Ny sida",
+Preview : "Förhandsgranska",
+Cut : "Klipp ut",
+Copy : "Kopiera",
+Paste : "Klistra in",
+PasteText : "Klistra in som text",
+PasteWord : "Klistra in från Word",
+Print : "Skriv ut",
+SelectAll : "Markera allt",
+RemoveFormat : "Radera formatering",
+InsertLinkLbl : "Länk",
+InsertLink : "Infoga/Redigera länk",
+RemoveLink : "Radera länk",
+Anchor : "Infoga/Redigera ankarlänk",
+InsertImageLbl : "Bild",
+InsertImage : "Infoga/Redigera bild",
+InsertFlashLbl : "Flash",
+InsertFlash : "Infoga/Redigera Flash",
+InsertTableLbl : "Tabell",
+InsertTable : "Infoga/Redigera tabell",
+InsertLineLbl : "Linje",
+InsertLine : "Infoga horisontal linje",
+InsertSpecialCharLbl: "Utökade tecken",
+InsertSpecialChar : "Klistra in utökat tecken",
+InsertSmileyLbl : "Smiley",
+InsertSmiley : "Infoga Smiley",
+About : "Om FCKeditor",
+Bold : "Fet",
+Italic : "Kursiv",
+Underline : "Understruken",
+StrikeThrough : "Genomstruken",
+Subscript : "Nedsänkta tecken",
+Superscript : "Upphöjda tecken",
+LeftJustify : "Vänsterjustera",
+CenterJustify : "Centrera",
+RightJustify : "Högerjustera",
+BlockJustify : "Justera till marginaler",
+DecreaseIndent : "Minska indrag",
+IncreaseIndent : "Öka indrag",
+Undo : "Ã…ngra",
+Redo : "Gör om",
+NumberedListLbl : "Numrerad lista",
+NumberedList : "Infoga/Radera numrerad lista",
+BulletedListLbl : "Punktlista",
+BulletedList : "Infoga/Radera punktlista",
+ShowTableBorders : "Visa tabellkant",
+ShowDetails : "Visa radbrytningar",
+Style : "Anpassad stil",
+FontFormat : "Teckenformat",
+Font : "Typsnitt",
+FontSize : "Storlek",
+TextColor : "Textfärg",
+BGColor : "Bakgrundsfärg",
+Source : "Källa",
+Find : "Sök",
+Replace : "Ersätt",
+SpellCheck : "Stavningskontroll",
+UniversalKeyboard : "Universellt tangentbord",
+PageBreakLbl : "Sidbrytning",
+PageBreak : "Infoga sidbrytning",
+
+Form : "Formulär",
+Checkbox : "Kryssruta",
+RadioButton : "Alternativknapp",
+TextField : "Textfält",
+Textarea : "Textruta",
+HiddenField : "Dolt fält",
+Button : "Knapp",
+SelectionField : "Flervalslista",
+ImageButton : "Bildknapp",
+
+FitWindow : "Anpassa till fönstrets storlek",
+
+// Context Menu
+EditLink : "Redigera länk",
+CellCM : "Cell",
+RowCM : "Rad",
+ColumnCM : "Kolumn",
+InsertRow : "Infoga rad",
+DeleteRows : "Radera rad",
+InsertColumn : "Infoga kolumn",
+DeleteColumns : "Radera kolumn",
+InsertCell : "Infoga cell",
+DeleteCells : "Radera celler",
+MergeCells : "Sammanfoga celler",
+SplitCell : "Separera celler",
+TableDelete : "Radera tabell",
+CellProperties : "Cellegenskaper",
+TableProperties : "Tabellegenskaper",
+ImageProperties : "Bildegenskaper",
+FlashProperties : "Flashegenskaper",
+
+AnchorProp : "Egenskaper för ankarlänk",
+ButtonProp : "Egenskaper för knapp",
+CheckboxProp : "Egenskaper för kryssruta",
+HiddenFieldProp : "Egenskaper för dolt fält",
+RadioButtonProp : "Egenskaper för alternativknapp",
+ImageButtonProp : "Egenskaper för bildknapp",
+TextFieldProp : "Egenskaper för textfält",
+SelectionFieldProp : "Egenskaper för flervalslista",
+TextareaProp : "Egenskaper för textruta",
+FormProp : "Egenskaper för formulär",
+
+FontFormats : "Normal;Formaterad;Adress;Rubrik 1;Rubrik 2;Rubrik 3;Rubrik 4;Rubrik 5;Rubrik 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Bearbetar XHTML. Var god vänta...",
+Done : "Klar",
+PasteWordConfirm : "Texten du vill klistra in verkar vara kopierad från Word. Vill du rensa innan du klistar in?",
+NotCompatiblePaste : "Denna åtgärd är inte tillgängligt för Internet Explorer version 5.5 eller högre. Vill du klistra in utan att rensa?",
+UnknownToolbarItem : "Okänt verktygsfält \"%1\"",
+UnknownCommand : "Okänt kommando \"%1\"",
+NotImplemented : "Kommandot finns ej",
+UnknownToolbarSet : "Verktygsfält \"%1\" finns ej",
+NoActiveX : "Din webläsares säkerhetsinställningar kan begränsa funktionaliteten. Du bör aktivera \"Kör ActiveX kontroller och plug-ins\". Fel och avsaknad av funktioner kan annars uppstå.",
+BrowseServerBlocked : "Kunde Ej öppna resursfönstret. Var god och avaktivera alla popup-blockerare.",
+DialogBlocked : "Kunde Ej öppna dialogfönstret. Var god och avaktivera alla popup-blockerare.",
+
+// Dialogs
+DlgBtnOK : "OK",
+DlgBtnCancel : "Avbryt",
+DlgBtnClose : "Stäng",
+DlgBtnBrowseServer : "Bläddra på server",
+DlgAdvancedTag : "Avancerad",
+DlgOpOther : "Övrigt",
+DlgInfoTab : "Information",
+DlgAlertUrl : "Var god och ange en URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ej angivet>",
+DlgGenId : "Id",
+DlgGenLangDir : "Språkriktning",
+DlgGenLangDirLtr : "Vänster till Höger (VTH)",
+DlgGenLangDirRtl : "Höger till Vänster (HTV)",
+DlgGenLangCode : "Språkkod",
+DlgGenAccessKey : "Behörighetsnyckel",
+DlgGenName : "Namn",
+DlgGenTabIndex : "Tabindex",
+DlgGenLongDescr : "URL-beskrivning",
+DlgGenClass : "Stylesheet class",
+DlgGenTitle : "Titel",
+DlgGenContType : "Innehållstyp",
+DlgGenLinkCharset : "Teckenuppställning",
+DlgGenStyle : "Style",
+
+// Image Dialog
+DlgImgTitle : "Bildegenskaper",
+DlgImgInfoTab : "Bildinformation",
+DlgImgBtnUpload : "Skicka till server",
+DlgImgURL : "URL",
+DlgImgUpload : "Ladda upp",
+DlgImgAlt : "Alternativ text",
+DlgImgWidth : "Bredd",
+DlgImgHeight : "Höjd",
+DlgImgLockRatio : "Lås höjd/bredd förhållanden",
+DlgBtnResetSize : "Återställ storlek",
+DlgImgBorder : "Kant",
+DlgImgHSpace : "Horis. marginal",
+DlgImgVSpace : "Vert. marginal",
+DlgImgAlign : "Justering",
+DlgImgAlignLeft : "Vänster",
+DlgImgAlignAbsBottom: "Absolut nederkant",
+DlgImgAlignAbsMiddle: "Absolut centrering",
+DlgImgAlignBaseline : "Baslinje",
+DlgImgAlignBottom : "Nederkant",
+DlgImgAlignMiddle : "Mitten",
+DlgImgAlignRight : "Höger",
+DlgImgAlignTextTop : "Text överkant",
+DlgImgAlignTop : "Överkant",
+DlgImgPreview : "Förhandsgranska",
+DlgImgAlertUrl : "Var god och ange bildens URL",
+DlgImgLinkTab : "Länk",
+
+// Flash Dialog
+DlgFlashTitle : "Flashegenskaper",
+DlgFlashChkPlay : "Automatisk uppspelning",
+DlgFlashChkLoop : "Upprepa/Loopa",
+DlgFlashChkMenu : "Aktivera Flashmeny",
+DlgFlashScale : "Skala",
+DlgFlashScaleAll : "Visa allt",
+DlgFlashScaleNoBorder : "Ingen ram",
+DlgFlashScaleFit : "Exakt passning",
+
+// Link Dialog
+DlgLnkWindowTitle : "Länk",
+DlgLnkInfoTab : "Länkinformation",
+DlgLnkTargetTab : "MÃ¥l",
+
+DlgLnkType : "Länktyp",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Ankare i sidan",
+DlgLnkTypeEMail : "E-post",
+DlgLnkProto : "Protokoll",
+DlgLnkProtoOther : "<övrigt>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Välj ett ankare",
+DlgLnkAnchorByName : "efter ankarnamn",
+DlgLnkAnchorById : "efter objektid",
+DlgLnkNoAnchors : "(Inga ankare kunde hittas)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-postadress",
+DlgLnkEMailSubject : "Ämne",
+DlgLnkEMailBody : "Innehåll",
+DlgLnkUpload : "Ladda upp",
+DlgLnkBtnUpload : "Skicka till servern",
+
+DlgLnkTarget : "MÃ¥l",
+DlgLnkTargetFrame : "<ram>",
+DlgLnkTargetPopup : "<popup-fönster>",
+DlgLnkTargetBlank : "Nytt fönster (_blank)",
+DlgLnkTargetParent : "Föregående Window (_parent)",
+DlgLnkTargetSelf : "Detta fönstret (_self)",
+DlgLnkTargetTop : "Översta fönstret (_top)",
+DlgLnkTargetFrameName : "MÃ¥lets ramnamn",
+DlgLnkPopWinName : "Popup-fönstrets namn",
+DlgLnkPopWinFeat : "Popup-fönstrets egenskaper",
+DlgLnkPopResize : "Kan ändra storlek",
+DlgLnkPopLocation : "Adressfält",
+DlgLnkPopMenu : "Menyfält",
+DlgLnkPopScroll : "Scrolllista",
+DlgLnkPopStatus : "Statusfält",
+DlgLnkPopToolbar : "Verktygsfält",
+DlgLnkPopFullScrn : "Helskärm (endast IE)",
+DlgLnkPopDependent : "Beroende (endest Netscape)",
+DlgLnkPopWidth : "Bredd",
+DlgLnkPopHeight : "Höjd",
+DlgLnkPopLeft : "Position från vänster",
+DlgLnkPopTop : "Position från sidans topp",
+
+DlnLnkMsgNoUrl : "Var god ange länkens URL",
+DlnLnkMsgNoEMail : "Var god ange E-postadress",
+DlnLnkMsgNoAnchor : "Var god ange ett ankare",
+DlnLnkMsgInvPopName : "Popup-rutans namn måste börja med en alfabetisk bokstav och får inte innehålla mellanslag",
+
+// Color Dialog
+DlgColorTitle : "Välj färg",
+DlgColorBtnClear : "Rensa",
+DlgColorHighlight : "Markera",
+DlgColorSelected : "Vald",
+
+// Smiley Dialog
+DlgSmileyTitle : "Infoga smiley",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Välj utökat tecken",
+
+// Table Dialog
+DlgTableTitle : "Tabellegenskaper",
+DlgTableRows : "Rader",
+DlgTableColumns : "Kolumner",
+DlgTableBorder : "Kantstorlek",
+DlgTableAlign : "Justering",
+DlgTableAlignNotSet : "<ej angivet>",
+DlgTableAlignLeft : "Vänster",
+DlgTableAlignCenter : "Centrerad",
+DlgTableAlignRight : "Höger",
+DlgTableWidth : "Bredd",
+DlgTableWidthPx : "pixlar",
+DlgTableWidthPc : "procent",
+DlgTableHeight : "Höjd",
+DlgTableCellSpace : "Cellavstånd",
+DlgTableCellPad : "Cellutfyllnad",
+DlgTableCaption : "Rubrik",
+DlgTableSummary : "Sammanfattning",
+
+// Table Cell Dialog
+DlgCellTitle : "Cellegenskaper",
+DlgCellWidth : "Bredd",
+DlgCellWidthPx : "pixlar",
+DlgCellWidthPc : "procent",
+DlgCellHeight : "Höjd",
+DlgCellWordWrap : "Automatisk radbrytning",
+DlgCellWordWrapNotSet : "<Ej angivet>",
+DlgCellWordWrapYes : "Ja",
+DlgCellWordWrapNo : "Nej",
+DlgCellHorAlign : "Horisontal justering",
+DlgCellHorAlignNotSet : "<Ej angivet>",
+DlgCellHorAlignLeft : "Vänster",
+DlgCellHorAlignCenter : "Centrerad",
+DlgCellHorAlignRight: "Höger",
+DlgCellVerAlign : "Vertikal justering",
+DlgCellVerAlignNotSet : "<Ej angivet>",
+DlgCellVerAlignTop : "Topp",
+DlgCellVerAlignMiddle : "Mitten",
+DlgCellVerAlignBottom : "Nederkant",
+DlgCellVerAlignBaseline : "Underst",
+DlgCellRowSpan : "Radomfång",
+DlgCellCollSpan : "Kolumnomfång",
+DlgCellBackColor : "Bakgrundsfärg",
+DlgCellBorderColor : "Kantfärg",
+DlgCellBtnSelect : "Välj...",
+
+// Find Dialog
+DlgFindTitle : "Sök",
+DlgFindFindBtn : "Sök",
+DlgFindNotFoundMsg : "Angiven text kunde ej hittas.",
+
+// Replace Dialog
+DlgReplaceTitle : "Ersätt",
+DlgReplaceFindLbl : "Sök efter:",
+DlgReplaceReplaceLbl : "Ersätt med:",
+DlgReplaceCaseChk : "Skiftläge",
+DlgReplaceReplaceBtn : "Ersätt",
+DlgReplaceReplAllBtn : "Ersätt alla",
+DlgReplaceWordChk : "Inkludera hela ord",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Säkerhetsinställningar i Er webläsare tillåter inte åtgården Klipp ut. Använd (Ctrl+X) istället.",
+PasteErrorCopy : "Säkerhetsinställningar i Er webläsare tillåter inte åtgården Kopiera. Använd (Ctrl+C) istället",
+
+PasteAsText : "Klistra in som vanlig text",
+PasteFromWord : "Klistra in från Word",
+
+DlgPasteMsg2 : "Var god och klistra in Er text i rutan nedan genom att använda (<STRONG>Ctrl+V</STRONG>) klicka sen på <STRONG>OK</STRONG>.",
+DlgPasteSec : "På grund av din webläsares säkerhetsinställningar kan verktyget inte få åtkomst till urklippsdatan. Var god och använd detta fönster istället.",
+DlgPasteIgnoreFont : "Ignorera typsnittsdefinitioner",
+DlgPasteRemoveStyles : "Radera Stildefinitioner",
+DlgPasteCleanBox : "Töm rutans innehåll",
+
+// Color Picker
+ColorAutomatic : "Automatisk",
+ColorMoreColors : "Fler färger...",
+
+// Document Properties
+DocProps : "Dokumentegenskaper",
+
+// Anchor Dialog
+DlgAnchorTitle : "Ankaregenskaper",
+DlgAnchorName : "Ankarnamn",
+DlgAnchorErrorName : "Var god ange ett ankarnamn",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Saknas i ordlistan",
+DlgSpellChangeTo : "Ändra till",
+DlgSpellBtnIgnore : "Ignorera",
+DlgSpellBtnIgnoreAll : "Ignorera alla",
+DlgSpellBtnReplace : "Ersätt",
+DlgSpellBtnReplaceAll : "Ersätt alla",
+DlgSpellBtnUndo : "Ã…ngra",
+DlgSpellNoSuggestions : "- Förslag saknas -",
+DlgSpellProgress : "Stavningskontroll pågår...",
+DlgSpellNoMispell : "Stavningskontroll slutförd: Inga stavfel påträffades.",
+DlgSpellNoChanges : "Stavningskontroll slutförd: Inga ord rättades.",
+DlgSpellOneChange : "Stavningskontroll slutförd: Ett ord rättades.",
+DlgSpellManyChanges : "Stavningskontroll slutförd: %1 ord rättades.",
+
+IeSpellDownload : "Stavningskontrollen är ej installerad. Vill du göra det nu?",
+
+// Button Dialog
+DlgButtonText : "Text (Värde)",
+DlgButtonType : "Typ",
+DlgButtonTypeBtn : "Knapp",
+DlgButtonTypeSbm : "Skicka",
+DlgButtonTypeRst : "Återställ",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Namn",
+DlgCheckboxValue : "Värde",
+DlgCheckboxSelected : "Vald",
+
+// Form Dialog
+DlgFormName : "Namn",
+DlgFormAction : "Funktion",
+DlgFormMethod : "Metod",
+
+// Select Field Dialog
+DlgSelectName : "Namn",
+DlgSelectValue : "Värde",
+DlgSelectSize : "Storlek",
+DlgSelectLines : "Linjer",
+DlgSelectChkMulti : "Tillåt flerval",
+DlgSelectOpAvail : "Befintliga val",
+DlgSelectOpText : "Text",
+DlgSelectOpValue : "Värde",
+DlgSelectBtnAdd : "Lägg till",
+DlgSelectBtnModify : "Redigera",
+DlgSelectBtnUp : "Upp",
+DlgSelectBtnDown : "Ner",
+DlgSelectBtnSetValue : "Markera som valt värde",
+DlgSelectBtnDelete : "Radera",
+
+// Textarea Dialog
+DlgTextareaName : "Namn",
+DlgTextareaCols : "Kolumner",
+DlgTextareaRows : "Rader",
+
+// Text Field Dialog
+DlgTextName : "Namn",
+DlgTextValue : "Värde",
+DlgTextCharWidth : "Teckenbredd",
+DlgTextMaxChars : "Max antal tecken",
+DlgTextType : "Typ",
+DlgTextTypeText : "Text",
+DlgTextTypePass : "Lösenord",
+
+// Hidden Field Dialog
+DlgHiddenName : "Namn",
+DlgHiddenValue : "Värde",
+
+// Bulleted List Dialog
+BulletedListProp : "Egenskaper för punktlista",
+NumberedListProp : "Egenskaper för numrerad lista",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "Typ",
+DlgLstTypeCircle : "Cirkel",
+DlgLstTypeDisc : "Punkt",
+DlgLstTypeSquare : "Ruta",
+DlgLstTypeNumbers : "Nummer (1, 2, 3)",
+DlgLstTypeLCase : "Gemener (a, b, c)",
+DlgLstTypeUCase : "Versaler (A, B, C)",
+DlgLstTypeSRoman : "Små romerska siffror (i, ii, iii)",
+DlgLstTypeLRoman : "Stora romerska siffror (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Allmän",
+DlgDocBackTab : "Bakgrund",
+DlgDocColorsTab : "Färg och marginal",
+DlgDocMetaTab : "Metadata",
+
+DlgDocPageTitle : "Sidtitel",
+DlgDocLangDir : "Språkriktning",
+DlgDocLangDirLTR : "Vänster till Höger",
+DlgDocLangDirRTL : "Höger till Vänster",
+DlgDocLangCode : "Språkkod",
+DlgDocCharSet : "Teckenuppsättningar",
+DlgDocCharSetCE : "Central Europa",
+DlgDocCharSetCT : "Traditionell Kinesisk (Big5)",
+DlgDocCharSetCR : "Kyrillisk",
+DlgDocCharSetGR : "Grekiska",
+DlgDocCharSetJP : "Japanska",
+DlgDocCharSetKR : "Koreanska",
+DlgDocCharSetTR : "Turkiska",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Väst Europa",
+DlgDocCharSetOther : "Övriga teckenuppsättningar",
+
+DlgDocDocType : "Sidhuvud",
+DlgDocDocTypeOther : "Övriga sidhuvuden",
+DlgDocIncXHTML : "Inkludera XHTML deklaration",
+DlgDocBgColor : "Bakgrundsfärg",
+DlgDocBgImage : "Bakgrundsbildens URL",
+DlgDocBgNoScroll : "Fast bakgrund",
+DlgDocCText : "Text",
+DlgDocCLink : "Länk",
+DlgDocCVisited : "Besökt länk",
+DlgDocCActive : "Aktiv länk",
+DlgDocMargins : "Sidmarginal",
+DlgDocMaTop : "Topp",
+DlgDocMaLeft : "Vänster",
+DlgDocMaRight : "Höger",
+DlgDocMaBottom : "Botten",
+DlgDocMeIndex : "Sidans nyckelord",
+DlgDocMeDescr : "Sidans beskrivning",
+DlgDocMeAuthor : "Författare",
+DlgDocMeCopy : "Upphovsrätt",
+DlgDocPreview : "Förhandsgranska",
+
+// Templates Dialog
+Templates : "Sidmallar",
+DlgTemplatesTitle : "Sidmallar",
+DlgTemplatesSelMsg : "Var god välj en mall att använda med editorn<br>(allt nuvarande innehåll raderas):",
+DlgTemplatesLoading : "Laddar mallar. Var god vänta...",
+DlgTemplatesNoTpl : "(Ingen mall är vald)",
+DlgTemplatesReplace : "Ersätt aktuellt innehåll",
+
+// About Dialog
+DlgAboutAboutTab : "Om",
+DlgAboutBrowserInfoTab : "Webläsare",
+DlgAboutLicenseTab : "Licens",
+DlgAboutVersion : "version",
+DlgAboutInfo : "För mer information se"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/th.js b/httemplate/elements/fckeditor/editor/lang/th.js
new file mode 100644
index 0000000..8c4319a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/th.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Thai language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "ซ่อนà¹à¸–บเครื่องมือ",
+ToolbarExpand : "à¹à¸ªà¸”งà¹à¸–บเครื่องมือ",
+
+// Toolbar Items and Context Menu
+Save : "บันทึà¸",
+NewPage : "สร้างหน้าเอà¸à¸ªà¸²à¸£à¹ƒà¸«à¸¡à¹ˆ",
+Preview : "ดูหน้าเอà¸à¸ªà¸²à¸£à¸•à¸±à¸§à¸­à¸¢à¹ˆà¸²à¸‡",
+Cut : "ตัด",
+Copy : "สำเนา",
+Paste : "วาง",
+PasteText : "วางสำเนาจาà¸à¸•à¸±à¸§à¸­à¸±à¸à¸©à¸£à¸˜à¸£à¸£à¸¡à¸”า",
+PasteWord : "วางสำเนาจาà¸à¸•à¸±à¸§à¸­à¸±à¸à¸©à¸£à¹€à¸§à¸´à¸£à¹Œà¸”",
+Print : "สั่งพิมพ์",
+SelectAll : "เลือà¸à¸—ั้งหมด",
+RemoveFormat : "ล้างรูปà¹à¸šà¸š",
+InsertLinkLbl : "ลิงค์เชื่อมโยงเว็บ อีเมล์ รูปภาพ หรือไฟล์อื่นๆ",
+InsertLink : "à¹à¸—รà¸/à¹à¸à¹‰à¹„ข ลิงค์",
+RemoveLink : "ลบ ลิงค์",
+Anchor : "à¹à¸—รà¸/à¹à¸à¹‰à¹„ข Anchor",
+InsertImageLbl : "รูปภาพ",
+InsertImage : "à¹à¸—รà¸/à¹à¸à¹‰à¹„ข รูปภาพ",
+InsertFlashLbl : "ไฟล์ Flash",
+InsertFlash : "à¹à¸—รà¸/à¹à¸à¹‰à¹„ข ไฟล์ Flash",
+InsertTableLbl : "ตาราง",
+InsertTable : "à¹à¸—รà¸/à¹à¸à¹‰à¹„ข ตาราง",
+InsertLineLbl : "เส้นคั่นบรรทัด",
+InsertLine : "à¹à¸—รà¸à¹€à¸ªà¹‰à¸™à¸„ั่นบรรทัด",
+InsertSpecialCharLbl: "ตัวอัà¸à¸©à¸£à¸žà¸´à¹€à¸¨à¸©",
+InsertSpecialChar : "à¹à¸—รà¸à¸•à¸±à¸§à¸­à¸±à¸à¸©à¸£à¸žà¸´à¹€à¸¨à¸©",
+InsertSmileyLbl : "รูปสื่ออารมณ์",
+InsertSmiley : "à¹à¸—รà¸à¸£à¸¹à¸›à¸ªà¸·à¹ˆà¸­à¸­à¸²à¸£à¸¡à¸“์",
+About : "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚ปรà¹à¸à¸£à¸¡ FCKeditor",
+Bold : "ตัวหนา",
+Italic : "ตัวเอียง",
+Underline : "ตัวขีดเส้นใต้",
+StrikeThrough : "ตัวขีดเส้นทับ",
+Subscript : "ตัวห้อย",
+Superscript : "ตัวยà¸",
+LeftJustify : "จัดชิดซ้าย",
+CenterJustify : "จัดà¸à¸¶à¹ˆà¸‡à¸à¸¥à¸²à¸‡",
+RightJustify : "จัดชิดขวา",
+BlockJustify : "จัดพอดีหน้าà¸à¸£à¸°à¸”าษ",
+DecreaseIndent : "ลดระยะย่อหน้า",
+IncreaseIndent : "เพิ่มระยะย่อหน้า",
+Undo : "ยà¸à¹€à¸¥à¸´à¸à¸„ำสั่ง",
+Redo : "ทำซ้ำคำสั่ง",
+NumberedListLbl : "ลำดับรายà¸à¸²à¸£à¹à¸šà¸šà¸•à¸±à¸§à¹€à¸¥à¸‚",
+NumberedList : "à¹à¸—รà¸/à¹à¸à¹‰à¹„ข ลำดับรายà¸à¸²à¸£à¹à¸šà¸šà¸•à¸±à¸§à¹€à¸¥à¸‚",
+BulletedListLbl : "ลำดับรายà¸à¸²à¸£à¹à¸šà¸šà¸ªà¸±à¸à¸¥à¸±à¸à¸©à¸“์",
+BulletedList : "à¹à¸—รà¸/à¹à¸à¹‰à¹„ข ลำดับรายà¸à¸²à¸£à¹à¸šà¸šà¸ªà¸±à¸à¸¥à¸±à¸à¸©à¸“์",
+ShowTableBorders : "à¹à¸ªà¸”งขอบของตาราง",
+ShowDetails : "à¹à¸ªà¸”งรายละเอียด",
+Style : "ลัà¸à¸©à¸“ะ",
+FontFormat : "รูปà¹à¸šà¸š",
+Font : "à¹à¸šà¸šà¸­à¸±à¸à¸©à¸£",
+FontSize : "ขนาด",
+TextColor : "สีตัวอัà¸à¸©à¸£",
+BGColor : "สีพื้นหลัง",
+Source : "ดูรหัส HTML",
+Find : "ค้นหา",
+Replace : "ค้นหาà¹à¸¥à¸°à¹à¸—นที่",
+SpellCheck : "ตรวจà¸à¸²à¸£à¸ªà¸°à¸à¸”คำ",
+UniversalKeyboard : "คีย์บอร์ดหลาà¸à¸ à¸²à¸©à¸²",
+PageBreakLbl : "ใส่ตัวà¹à¸šà¹ˆà¸‡à¸«à¸™à¹‰à¸² Page Break",
+PageBreak : "à¹à¸—รà¸à¸•à¸±à¸§à¹à¸šà¹ˆà¸‡à¸«à¸™à¹‰à¸² Page Break",
+
+Form : "à¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡",
+Checkbox : "เช็คบ๊อà¸",
+RadioButton : "เรดิโอบัตตอน",
+TextField : "เท็à¸à¸‹à¹Œà¸Ÿà¸´à¸¥à¸”์",
+Textarea : "เท็à¸à¸‹à¹Œà¹à¸­à¹€à¸£à¸µà¸¢",
+HiddenField : "ฮิดเดนฟิลด์",
+Button : "ปุ่ม",
+SelectionField : "à¹à¸–บตัวเลือà¸",
+ImageButton : "ปุ่มà¹à¸šà¸šà¸£à¸¹à¸›à¸ à¸²à¸ž",
+
+FitWindow : "ขยายขนาดตัวอีดิตเตอร์",
+
+// Context Menu
+EditLink : "à¹à¸à¹‰à¹„ข ลิงค์",
+CellCM : "ช่องตาราง",
+RowCM : "à¹à¸–ว",
+ColumnCM : "คอลัมน์",
+InsertRow : "à¹à¸—รà¸à¹à¸–ว",
+DeleteRows : "ลบà¹à¸–ว",
+InsertColumn : "à¹à¸—รà¸à¸ªà¸”มน์",
+DeleteColumns : "ลบสดมน์",
+InsertCell : "à¹à¸—รà¸à¸Šà¹ˆà¸­à¸‡",
+DeleteCells : "ลบช่อง",
+MergeCells : "ผสานช่อง",
+SplitCell : "à¹à¸¢à¸à¸Šà¹ˆà¸­à¸‡",
+TableDelete : "ลบตาราง",
+CellProperties : "คุณสมบัติของช่อง",
+TableProperties : "คุณสมบัติของตาราง",
+ImageProperties : "คุณสมบัติของรูปภาพ",
+FlashProperties : "คุณสมบัติของไฟล์ Flash",
+
+AnchorProp : "รายละเอียด Anchor",
+ButtonProp : "รายละเอียดของ ปุ่ม",
+CheckboxProp : "คุณสมบัติของ เช็คบ๊อà¸",
+HiddenFieldProp : "คุณสมบัติของ ฮิดเดนฟิลด์",
+RadioButtonProp : "คุณสมบัติของ เรดิโอบัตตอน",
+ImageButtonProp : "คุณสมบัติของ ปุ่มà¹à¸šà¸šà¸£à¸¹à¸›à¸ à¸²à¸ž",
+TextFieldProp : "คุณสมบัติของ เท็à¸à¸‹à¹Œà¸Ÿà¸´à¸¥à¸”์",
+SelectionFieldProp : "คุณสมบัติของ à¹à¸–บตัวเลือà¸",
+TextareaProp : "คุณสมบัติของ เท็à¸à¹à¸­à¹€à¸£à¸µà¸¢",
+FormProp : "คุณสมบัติของ à¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Paragraph (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "โปรà¹à¸à¸£à¸¡à¸à¸³à¸¥à¸±à¸‡à¸—ำงานด้วยเทคโนโลยี XHTML à¸à¸£à¸¸à¸“ารอสัà¸à¸„รู่...",
+Done : "โปรà¹à¸à¸£à¸¡à¸—ำงานเสร็จสมบูรณ์",
+PasteWordConfirm : "ข้อมูลที่ท่านต้องà¸à¸²à¸£à¸§à¸²à¸‡à¸¥à¸‡à¹ƒà¸™à¹à¸œà¹ˆà¸™à¸‡à¸²à¸™ ถูà¸à¸ˆà¸±à¸”รูปà¹à¸šà¸šà¸ˆà¸²à¸à¹‚ปรà¹à¸à¸£à¸¡à¹€à¸§à¸´à¸£à¹Œà¸”. ท่านต้องà¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸£à¸¹à¸›à¹à¸šà¸šà¸—ี่มาจาà¸à¹‚ปรà¹à¸à¸£à¸¡à¹€à¸§à¸´à¸£à¹Œà¸”หรือไม่?",
+NotCompatiblePaste : "คำสั่งนี้ทำงานในโปรà¹à¸à¸£à¸¡à¸—่องเว็บ Internet Explorer version รุ่น 5.5 หรือใหม่à¸à¸§à¹ˆà¸²à¹€à¸—่านั้น. ท่านต้องà¸à¸²à¸£à¸§à¸²à¸‡à¸•à¸±à¸§à¸­à¸±à¸à¸©à¸£à¹‚ดยไม่ล้างรูปà¹à¸šà¸šà¸—ี่มาจาà¸à¹‚ปรà¹à¸à¸£à¸¡à¹€à¸§à¸´à¸£à¹Œà¸”หรือไม่?",
+UnknownToolbarItem : "ไม่สามารถระบุปุ่มเครื่องมือได้ \"%1\"",
+UnknownCommand : "ไม่สามารถระบุชื่อคำสั่งได้ \"%1\"",
+NotImplemented : "ไม่สามารถใช้งานคำสั่งได้",
+UnknownToolbarSet : "ไม่มีà¸à¸²à¸£à¸•à¸´à¸”ตั้งชุดคำสั่งในà¹à¸–บเครื่องมือ \"%1\" à¸à¸£à¸¸à¸“าติดต่อผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š",
+NoActiveX : "โปรà¹à¸à¸£à¸¡à¸—่องอินเตอร์เน็ตของท่านไม่อนุà¸à¸²à¸•à¸´à¹ƒà¸«à¹‰à¸­à¸µà¸”ิตเตอร์ทำงาน \"Run ActiveX controls and plug-ins\". หาà¸à¹„ม่อนุà¸à¸²à¸•à¸´à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ ActiveX controls ท่านจะไม่สามารถใช้งานได้อย่างเต็มประสิทธิภาพ.",
+BrowseServerBlocked : "เปิดหน้าต่างป๊อบอัพเพื่อทำงานต่อไม่ได้ à¸à¸£à¸¸à¸“าปิดเครื่องมือป้องà¸à¸±à¸™à¸›à¹Šà¸­à¸šà¸­à¸±à¸žà¹ƒà¸™à¹‚ปรà¹à¸à¸£à¸¡à¸—่องอินเตอร์เน็ตของท่านด้วย",
+DialogBlocked : "เปิดหน้าต่างป๊อบอัพเพื่อทำงานต่อไม่ได้ à¸à¸£à¸¸à¸“าปิดเครื่องมือป้องà¸à¸±à¸™à¸›à¹Šà¸­à¸šà¸­à¸±à¸žà¹ƒà¸™à¹‚ปรà¹à¸à¸£à¸¡à¸—่องอินเตอร์เน็ตของท่านด้วย",
+
+// Dialogs
+DlgBtnOK : "ตà¸à¸¥à¸‡",
+DlgBtnCancel : "ยà¸à¹€à¸¥à¸´à¸",
+DlgBtnClose : "ปิด",
+DlgBtnBrowseServer : "เปิดหน้าต่างจัดà¸à¸²à¸£à¹„ฟล์อัพโหลด",
+DlgAdvancedTag : "ขั้นสูง",
+DlgOpOther : "<อื่นๆ>",
+DlgInfoTab : "อินโฟ",
+DlgAlertUrl : "à¸à¸£à¸¸à¸“าระบุ URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<ไม่ระบุ>",
+DlgGenId : "ไอดี",
+DlgGenLangDir : "à¸à¸²à¸£à¹€à¸‚ียน-อ่านภาษา",
+DlgGenLangDirLtr : "จาà¸à¸‹à¹‰à¸²à¸¢à¹„ปขวา (LTR)",
+DlgGenLangDirRtl : "จาà¸à¸‚วามาซ้าย (RTL)",
+DlgGenLangCode : "รหัสภาษา",
+DlgGenAccessKey : "à¹à¸­à¸„เซส คีย์",
+DlgGenName : "ชื่อ",
+DlgGenTabIndex : "ลำดับของ à¹à¸—็บ",
+DlgGenLongDescr : "คำอธิบายประà¸à¸­à¸š URL",
+DlgGenClass : "คลาสของไฟล์à¸à¸³à¸«à¸™à¸”ลัà¸à¸©à¸“ะà¸à¸²à¸£à¹à¸ªà¸”งผล",
+DlgGenTitle : "คำเà¸à¸£à¸´à¹ˆà¸™à¸™à¸³",
+DlgGenContType : "ชนิดของคำเà¸à¸£à¸´à¹ˆà¸™à¸™à¸³",
+DlgGenLinkCharset : "ลิงค์เชื่อมโยงไปยังชุดตัวอัà¸à¸©à¸£",
+DlgGenStyle : "ลัà¸à¸©à¸“ะà¸à¸²à¸£à¹à¸ªà¸”งผล",
+
+// Image Dialog
+DlgImgTitle : "คุณสมบัติของ รูปภาพ",
+DlgImgInfoTab : "ข้อมูลของรูปภาพ",
+DlgImgBtnUpload : "อัพโหลดไฟล์ไปเà¸à¹‡à¸šà¹„ว้ที่เครื่องà¹à¸¡à¹ˆà¸‚่าย (เซิร์ฟเวอร์)",
+DlgImgURL : "ที่อยู่อ้างอิง URL",
+DlgImgUpload : "อัพโหลดไฟล์",
+DlgImgAlt : "คำประà¸à¸­à¸šà¸£à¸¹à¸›à¸ à¸²à¸ž",
+DlgImgWidth : "ความà¸à¸§à¹‰à¸²à¸‡",
+DlgImgHeight : "ความสูง",
+DlgImgLockRatio : "à¸à¸³à¸«à¸™à¸”อัตราส่วน à¸à¸§à¹‰à¸²à¸‡-สูง à¹à¸šà¸šà¸„งที่",
+DlgBtnResetSize : "à¸à¸³à¸«à¸™à¸”รูปเท่าขนาดจริง",
+DlgImgBorder : "ขนาดขอบรูป",
+DlgImgHSpace : "ระยะà¹à¸™à¸§à¸™à¸­à¸™",
+DlgImgVSpace : "ระยะà¹à¸™à¸§à¸•à¸±à¹‰à¸‡",
+DlgImgAlign : "à¸à¸²à¸£à¸ˆà¸±à¸”วาง",
+DlgImgAlignLeft : "ชิดซ้าย",
+DlgImgAlignAbsBottom: "ชิดด้านล่างสุด",
+DlgImgAlignAbsMiddle: "à¸à¸¶à¹ˆà¸‡à¸à¸¥à¸²à¸‡",
+DlgImgAlignBaseline : "ชิดบรรทัด",
+DlgImgAlignBottom : "ชิดด้านล่าง",
+DlgImgAlignMiddle : "à¸à¸¶à¹ˆà¸‡à¸à¸¥à¸²à¸‡à¹à¸™à¸§à¸•à¸±à¹‰à¸‡",
+DlgImgAlignRight : "ชิดขวา",
+DlgImgAlignTextTop : "ใต้ตัวอัà¸à¸©à¸£",
+DlgImgAlignTop : "บนสุด",
+DlgImgPreview : "หน้าเอà¸à¸ªà¸²à¸£à¸•à¸±à¸§à¸­à¸¢à¹ˆà¸²à¸‡",
+DlgImgAlertUrl : "à¸à¸£à¸¸à¸“าระบุที่อยู่อ้างอิงออนไลน์ของไฟล์รูปภาพ (URL)",
+DlgImgLinkTab : "ลิ้งค์",
+
+// Flash Dialog
+DlgFlashTitle : "คุณสมบัติของไฟล์ Flash",
+DlgFlashChkPlay : "เล่นอัตโนมัติ Auto Play",
+DlgFlashChkLoop : "เล่นวนรอบ Loop",
+DlgFlashChkMenu : "ให้ใช้งานเมนูของ Flash",
+DlgFlashScale : "อัตราส่วน Scale",
+DlgFlashScaleAll : "à¹à¸ªà¸”งให้เห็นทั้งหมด Show all",
+DlgFlashScaleNoBorder : "ไม่à¹à¸ªà¸”งเส้นขอบ No Border",
+DlgFlashScaleFit : "à¹à¸ªà¸”งให้พอดีà¸à¸±à¸šà¸žà¸·à¹‰à¸™à¸—ี่ Exact Fit",
+
+// Link Dialog
+DlgLnkWindowTitle : "ลิงค์เชื่อมโยงเว็บ อีเมล์ รูปภาพ หรือไฟล์อื่นๆ",
+DlgLnkInfoTab : "รายละเอียด",
+DlgLnkTargetTab : "à¸à¸²à¸£à¹€à¸›à¸´à¸”หน้าจอ",
+
+DlgLnkType : "ประเภทของลิงค์",
+DlgLnkTypeURL : "ที่อยู่อ้างอิงออนไลน์ (URL)",
+DlgLnkTypeAnchor : "จุดเชื่อมโยง (Anchor)",
+DlgLnkTypeEMail : "ส่งอีเมล์ (E-Mail)",
+DlgLnkProto : "โปรโตคอล",
+DlgLnkProtoOther : "<อื่นๆ>",
+DlgLnkURL : "ที่อยู่อ้างอิงออนไลน์ (URL)",
+DlgLnkAnchorSel : "ระบุข้อมูลของจุดเชื่อมโยง (Anchor)",
+DlgLnkAnchorByName : "ชื่อ",
+DlgLnkAnchorById : "ไอดี",
+DlgLnkNoAnchors : "(ยังไม่มีจุดเชื่อมโยงภายในหน้าเอà¸à¸ªà¸²à¸£à¸™à¸µà¹‰)", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "อีเมล์ (E-Mail)",
+DlgLnkEMailSubject : "หัวเรื่อง",
+DlgLnkEMailBody : "ข้อความ",
+DlgLnkUpload : "อัพโหลดไฟล์",
+DlgLnkBtnUpload : "บันทึà¸à¹„ฟล์ไว้บนเซิร์ฟเวอร์",
+
+DlgLnkTarget : "à¸à¸²à¸£à¹€à¸›à¸´à¸”หน้าลิงค์",
+DlgLnkTargetFrame : "<เปิดในเฟรม>",
+DlgLnkTargetPopup : "<เปิดหน้าจอเล็ภ(Pop-up)>",
+DlgLnkTargetBlank : "เปิดหน้าจอใหม่ (_blank)",
+DlgLnkTargetParent : "เปิดในหน้าหลัภ(_parent)",
+DlgLnkTargetSelf : "เปิดในหน้าปัจจุบัน (_self)",
+DlgLnkTargetTop : "เปิดในหน้าบนสุด (_top)",
+DlgLnkTargetFrameName : "ชื่อทาร์เà¸à¹‡à¸•à¹€à¸Ÿà¸£à¸¡",
+DlgLnkPopWinName : "ระบุชื่อหน้าจอเล็ภ(Pop-up)",
+DlgLnkPopWinFeat : "คุณสมบัติของหน้าจอเล็ภ(Pop-up)",
+DlgLnkPopResize : "ปรับขนาดหน้าจอ",
+DlgLnkPopLocation : "à¹à¸ªà¸”งที่อยู่ของไฟล์",
+DlgLnkPopMenu : "à¹à¸ªà¸”งà¹à¸–บเมนู",
+DlgLnkPopScroll : "à¹à¸ªà¸”งà¹à¸–บเลื่อน",
+DlgLnkPopStatus : "à¹à¸ªà¸”งà¹à¸–บสถานะ",
+DlgLnkPopToolbar : "à¹à¸ªà¸”งà¹à¸–บเครื่องมือ",
+DlgLnkPopFullScrn : "à¹à¸ªà¸”งเต็มหน้าจอ (IE5.5++ เท่านั้น)",
+DlgLnkPopDependent : "à¹à¸ªà¸”งเต็มหน้าจอ (Netscape)",
+DlgLnkPopWidth : "à¸à¸§à¹‰à¸²à¸‡",
+DlgLnkPopHeight : "สูง",
+DlgLnkPopLeft : "พิà¸à¸±à¸”ซ้าย (Left Position)",
+DlgLnkPopTop : "พิà¸à¸±à¸”บน (Top Position)",
+
+DlnLnkMsgNoUrl : "à¸à¸£à¸¸à¸“าระบุที่อยู่อ้างอิงออนไลน์ (URL)",
+DlnLnkMsgNoEMail : "à¸à¸£à¸¸à¸“าระบุอีเมล์ (E-mail)",
+DlnLnkMsgNoAnchor : "à¸à¸£à¸¸à¸“าระบุจุดเชื่อมโยง (Anchor)",
+DlnLnkMsgInvPopName : "ชื่อของหน้าต่างป๊อบอัพ จะต้องขึ้นต้นด้วยตัวอัà¸à¸©à¸£à¹€à¸—่านั้น à¹à¸¥à¸°à¸•à¹‰à¸­à¸‡à¹„ม่มีช่องว่างในชื่อ",
+
+// Color Dialog
+DlgColorTitle : "เลือà¸à¸ªà¸µ",
+DlgColorBtnClear : "ล้างค่ารหัสสี",
+DlgColorHighlight : "ตัวอย่างสี",
+DlgColorSelected : "สีที่เลือà¸",
+
+// Smiley Dialog
+DlgSmileyTitle : "à¹à¸—รà¸à¸ªà¸±à¸à¸¥à¸±à¸à¸©à¸“์สื่ออารมณ์",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "à¹à¸—รà¸à¸•à¸±à¸§à¸­à¸±à¸à¸©à¸£à¸žà¸´à¹€à¸¨à¸©",
+
+// Table Dialog
+DlgTableTitle : "คุณสมบัติของ ตาราง",
+DlgTableRows : "à¹à¸–ว",
+DlgTableColumns : "สดมน์",
+DlgTableBorder : "ขนาดเส้นขอบ",
+DlgTableAlign : "à¸à¸²à¸£à¸ˆà¸±à¸”ตำà¹à¸«à¸™à¹ˆà¸‡",
+DlgTableAlignNotSet : "<ไม่ระบุ>",
+DlgTableAlignLeft : "ชิดซ้าย",
+DlgTableAlignCenter : "à¸à¸¶à¹ˆà¸‡à¸à¸¥à¸²à¸‡",
+DlgTableAlignRight : "ชิดขวา",
+DlgTableWidth : "à¸à¸§à¹‰à¸²à¸‡",
+DlgTableWidthPx : "จุดสี",
+DlgTableWidthPc : "เปอร์เซ็น",
+DlgTableHeight : "สูง",
+DlgTableCellSpace : "ระยะà¹à¸™à¸§à¸™à¸­à¸™à¸™",
+DlgTableCellPad : "ระยะà¹à¸™à¸§à¸•à¸±à¹‰à¸‡",
+DlgTableCaption : "หัวเรื่องของตาราง",
+DlgTableSummary : "สรุปความ",
+
+// Table Cell Dialog
+DlgCellTitle : "คุณสมบัติของ ช่อง",
+DlgCellWidth : "à¸à¸§à¹‰à¸²à¸‡",
+DlgCellWidthPx : "จุดสี",
+DlgCellWidthPc : "เปอร์เซ็น",
+DlgCellHeight : "สูง",
+DlgCellWordWrap : "ตัดบรรทัดอัตโนมัติ",
+DlgCellWordWrapNotSet : "<ไม่ระบุ>",
+DlgCellWordWrapYes : "ใ่ช่",
+DlgCellWordWrapNo : "ไม่",
+DlgCellHorAlign : "à¸à¸²à¸£à¸ˆà¸±à¸”วางà¹à¸™à¸§à¸™à¸­à¸™",
+DlgCellHorAlignNotSet : "<ไม่ระบุ>",
+DlgCellHorAlignLeft : "ชิดซ้าย",
+DlgCellHorAlignCenter : "à¸à¸¶à¹ˆà¸‡à¸à¸¥à¸²à¸‡",
+DlgCellHorAlignRight: "ชิดขวา",
+DlgCellVerAlign : "à¸à¸²à¸£à¸ˆà¸±à¸”วางà¹à¸™à¸§à¸•à¸±à¹‰à¸‡",
+DlgCellVerAlignNotSet : "<ไม่ระบุ>",
+DlgCellVerAlignTop : "บนสุด",
+DlgCellVerAlignMiddle : "à¸à¸¶à¹ˆà¸‡à¸à¸¥à¸²à¸‡",
+DlgCellVerAlignBottom : "ล่างสุด",
+DlgCellVerAlignBaseline : "อิงบรรทัด",
+DlgCellRowSpan : "จำนวนà¹à¸–วที่คร่อมà¸à¸±à¸™",
+DlgCellCollSpan : "จำนวนสดมน์ที่คร่อมà¸à¸±à¸™",
+DlgCellBackColor : "สีพื้นหลัง",
+DlgCellBorderColor : "สีเส้นขอบ",
+DlgCellBtnSelect : "เลือà¸..",
+
+// Find Dialog
+DlgFindTitle : "ค้นหา",
+DlgFindFindBtn : "ค้นหา",
+DlgFindNotFoundMsg : "ไม่พบคำที่ค้นหา.",
+
+// Replace Dialog
+DlgReplaceTitle : "ค้นหาà¹à¸¥à¸°à¹à¸—นที่",
+DlgReplaceFindLbl : "ค้นหาคำว่า:",
+DlgReplaceReplaceLbl : "à¹à¸—นที่ด้วย:",
+DlgReplaceCaseChk : "ตัวโหà¸à¹ˆ-เล็ภต้องตรงà¸à¸±à¸™",
+DlgReplaceReplaceBtn : "à¹à¸—นที่",
+DlgReplaceReplAllBtn : "à¹à¸—นที่ทั้งหมดที่พบ",
+DlgReplaceWordChk : "ต้องตรงà¸à¸±à¸™à¸—ุà¸à¸„ำ",
+
+// Paste Operations / Dialog
+PasteErrorCut : "ไม่สามารถตัดข้อความที่เลือà¸à¹„ว้ได้เนื่องจาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าระดับความปลอดภัย. à¸à¸£à¸¸à¸“าใช้ปุ่มลัดเพื่อวางข้อความà¹à¸—น (à¸à¸”ปุ่ม Ctrl à¹à¸¥à¸°à¸•à¸±à¸§ X พร้อมà¸à¸±à¸™).",
+PasteErrorCopy : "ไม่สามารถสำเนาข้อความที่เลือà¸à¹„ว้ได้เนื่องจาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าระดับความปลอดภัย. à¸à¸£à¸¸à¸“าใช้ปุ่มลัดเพื่อวางข้อความà¹à¸—น (à¸à¸”ปุ่ม Ctrl à¹à¸¥à¸°à¸•à¸±à¸§ C พร้อมà¸à¸±à¸™).",
+
+PasteAsText : "วางà¹à¸šà¸šà¸•à¸±à¸§à¸­à¸±à¸à¸©à¸£à¸˜à¸£à¸£à¸¡à¸”า",
+PasteFromWord : "วางà¹à¸šà¸šà¸•à¸±à¸§à¸­à¸±à¸à¸©à¸£à¸ˆà¸²à¸à¹‚ปรà¹à¸à¸£à¸¡à¹€à¸§à¸´à¸£à¹Œà¸”",
+
+DlgPasteMsg2 : "à¸à¸£à¸¸à¸“าใช้คีย์บอร์ดเท่านั้น โดยà¸à¸”ปุ๋ม (<strong>Ctrl à¹à¸¥à¸° V</strong>)พร้อมๆà¸à¸±à¸™ à¹à¸¥à¸°à¸à¸” <strong>OK</strong>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "ไม่สนใจ Font Face definitions",
+DlgPasteRemoveStyles : "ลบ Styles definitions",
+DlgPasteCleanBox : "ล้างข้อมูลใน Box",
+
+// Color Picker
+ColorAutomatic : "สีอัตโนมัติ",
+ColorMoreColors : "เลือà¸à¸ªà¸µà¸­à¸·à¹ˆà¸™à¹†...",
+
+// Document Properties
+DocProps : "คุณสมบัติของเอà¸à¸ªà¸²à¸£",
+
+// Anchor Dialog
+DlgAnchorTitle : "คุณสมบัติของ Anchor",
+DlgAnchorName : "ชื่อ Anchor",
+DlgAnchorErrorName : "à¸à¸£à¸¸à¸“าระบุชื่อของ Anchor",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "ไม่พบในดิà¸à¸Šà¸±à¸™à¸™à¸²à¸£à¸µ",
+DlgSpellChangeTo : "à¹à¸à¹‰à¹„ขเป็น",
+DlgSpellBtnIgnore : "ยà¸à¹€à¸§à¹‰à¸™",
+DlgSpellBtnIgnoreAll : "ยà¸à¹€à¸§à¹‰à¸™à¸—ั้งหมด",
+DlgSpellBtnReplace : "à¹à¸—นที่",
+DlgSpellBtnReplaceAll : "à¹à¸—นที่ทั้งหมด",
+DlgSpellBtnUndo : "ยà¸à¹€à¸¥à¸´à¸",
+DlgSpellNoSuggestions : "- ไม่มีคำà¹à¸™à¸°à¸™à¸³à¹ƒà¸”ๆ -",
+DlgSpellProgress : "à¸à¸³à¸¥à¸±à¸‡à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸šà¸„ำสะà¸à¸”...",
+DlgSpellNoMispell : "ตรวจสอบคำสะà¸à¸”เสร็จสิ้น: ไม่พบคำสะà¸à¸”ผิด",
+DlgSpellNoChanges : "ตรวจสอบคำสะà¸à¸”เสร็จสิ้น: ไม่มีà¸à¸²à¸£à¹à¸à¹‰à¸„ำใดๆ",
+DlgSpellOneChange : "ตรวจสอบคำสะà¸à¸”เสร็จสิ้น: à¹à¸à¹‰à¹„ข1คำ",
+DlgSpellManyChanges : "ตรวจสอบคำสะà¸à¸”เสร็จสิ้น:: à¹à¸à¹‰à¹„ข %1 คำ",
+
+IeSpellDownload : "ไม่ได้ติดตั้งระบบตรวจสอบคำสะà¸à¸”. ต้องà¸à¸²à¸£à¸•à¸´à¸”ตั้งไหมครับ?",
+
+// Button Dialog
+DlgButtonText : "ข้อความ (ค่าตัวà¹à¸›à¸£)",
+DlgButtonType : "ข้อความ",
+DlgButtonTypeBtn : "Button",
+DlgButtonTypeSbm : "Submit",
+DlgButtonTypeRst : "Reset",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "ชื่อ",
+DlgCheckboxValue : "ค่าตัวà¹à¸›à¸£",
+DlgCheckboxSelected : "เลือà¸à¹€à¸›à¹‡à¸™à¸„่าเริ่มต้น",
+
+// Form Dialog
+DlgFormName : "ชื่อ",
+DlgFormAction : "à¹à¸­à¸„ชั่น",
+DlgFormMethod : "เมธอด",
+
+// Select Field Dialog
+DlgSelectName : "ชื่อ",
+DlgSelectValue : "ค่าตัวà¹à¸›à¸£",
+DlgSelectSize : "ขนาด",
+DlgSelectLines : "บรรทัด",
+DlgSelectChkMulti : "เลือà¸à¸«à¸¥à¸²à¸¢à¸„่าได้",
+DlgSelectOpAvail : "รายà¸à¸²à¸£à¸•à¸±à¸§à¹€à¸¥à¸·à¸­à¸",
+DlgSelectOpText : "ข้อความ",
+DlgSelectOpValue : "ค่าตัวà¹à¸›à¸£",
+DlgSelectBtnAdd : "เพิ่ม",
+DlgSelectBtnModify : "à¹à¸à¹‰à¹„ข",
+DlgSelectBtnUp : "บน",
+DlgSelectBtnDown : "ล่าง",
+DlgSelectBtnSetValue : "เลือà¸à¹€à¸›à¹‡à¸™à¸„่าเริ่มต้น",
+DlgSelectBtnDelete : "ลบ",
+
+// Textarea Dialog
+DlgTextareaName : "ชื่อ",
+DlgTextareaCols : "สดมภ์",
+DlgTextareaRows : "à¹à¸–ว",
+
+// Text Field Dialog
+DlgTextName : "ชื่อ",
+DlgTextValue : "ค่าตัวà¹à¸›à¸£",
+DlgTextCharWidth : "ความà¸à¸§à¹‰à¸²à¸‡",
+DlgTextMaxChars : "จำนวนตัวอัà¸à¸©à¸£à¸ªà¸¹à¸‡à¸ªà¸¸à¸”",
+DlgTextType : "ชนิด",
+DlgTextTypeText : "ข้อความ",
+DlgTextTypePass : "รหัสผ่าน",
+
+// Hidden Field Dialog
+DlgHiddenName : "ชื่อ",
+DlgHiddenValue : "ค่าตัวà¹à¸›à¸£",
+
+// Bulleted List Dialog
+BulletedListProp : "คุณสมบัติของ บูลเล็ตลิสต์",
+NumberedListProp : "คุณสมบัติของ นัมเบอร์ลิสต์",
+DlgLstStart : "Start", //MISSING
+DlgLstType : "ชนิด",
+DlgLstTypeCircle : "รูปวงà¸à¸¥à¸¡",
+DlgLstTypeDisc : "Disc", //MISSING
+DlgLstTypeSquare : "รูปสี่เหลี่ยม",
+DlgLstTypeNumbers : "หมายเลข (1, 2, 3)",
+DlgLstTypeLCase : "ตัวพิมพ์เล็ภ(a, b, c)",
+DlgLstTypeUCase : "ตัวพิมพ์ใหà¸à¹ˆ (A, B, C)",
+DlgLstTypeSRoman : "เลขโรมันพิมพ์เล็ภ(i, ii, iii)",
+DlgLstTypeLRoman : "เลขโรมันพิมพ์ใหà¸à¹ˆ (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "ลัà¸à¸©à¸“ะทั่วไปของเอà¸à¸ªà¸²à¸£",
+DlgDocBackTab : "พื้นหลัง",
+DlgDocColorsTab : "สีà¹à¸¥à¸°à¸£à¸°à¸¢à¸°à¸‚อบ",
+DlgDocMetaTab : "ข้อมูลสำหรับเสิร์ชเอนจิ้น",
+
+DlgDocPageTitle : "ชื่อไตเติ้ล",
+DlgDocLangDir : "à¸à¸²à¸£à¸­à¹ˆà¸²à¸™à¸ à¸²à¸©à¸²",
+DlgDocLangDirLTR : "จาà¸à¸‹à¹‰à¸²à¸¢à¹„ปขวา (LTR)",
+DlgDocLangDirRTL : "จาà¸à¸‚วาไปซ้าย (RTL)",
+DlgDocLangCode : "รหัสภาษา",
+DlgDocCharSet : "ชุดตัวอัà¸à¸©à¸£",
+DlgDocCharSetCE : "Central European",
+DlgDocCharSetCT : "Chinese Traditional (Big5)",
+DlgDocCharSetCR : "Cyrillic",
+DlgDocCharSetGR : "Greek",
+DlgDocCharSetJP : "Japanese",
+DlgDocCharSetKR : "Korean",
+DlgDocCharSetTR : "Turkish",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Western European",
+DlgDocCharSetOther : "ชุดตัวอัà¸à¸©à¸£à¸­à¸·à¹ˆà¸™à¹†",
+
+DlgDocDocType : "ประเภทของเอà¸à¸ªà¸²à¸£",
+DlgDocDocTypeOther : "ประเภทเอà¸à¸ªà¸²à¸£à¸­à¸·à¹ˆà¸™à¹†",
+DlgDocIncXHTML : "รวมเอา XHTML Declarations ไว้ด้วย",
+DlgDocBgColor : "สีพื้นหลัง",
+DlgDocBgImage : "ที่อยู่อ้างอิงออนไลน์ของรูปพื้นหลัง (Image URL)",
+DlgDocBgNoScroll : "พื้นหลังà¹à¸šà¸šà¹„ม่มีà¹à¸–บเลื่อน",
+DlgDocCText : "ข้อความ",
+DlgDocCLink : "ลิงค์",
+DlgDocCVisited : "ลิงค์ที่เคยคลิ้à¸à¹à¸¥à¹‰à¸§ Visited Link",
+DlgDocCActive : "ลิงค์ที่à¸à¸³à¸¥à¸±à¸‡à¸„ลิ้ภActive Link",
+DlgDocMargins : "ระยะขอบของหน้าเอà¸à¸ªà¸²à¸£",
+DlgDocMaTop : "ด้านบน",
+DlgDocMaLeft : "ด้านซ้าย",
+DlgDocMaRight : "ด้านขวา",
+DlgDocMaBottom : "ด้านล่าง",
+DlgDocMeIndex : "คำสำคัà¸à¸­à¸˜à¸´à¸šà¸²à¸¢à¹€à¸­à¸à¸ªà¸²à¸£ (คั่นคำด้วย คอมม่า)",
+DlgDocMeDescr : "ประโยคอธิบายเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹€à¸­à¸à¸ªà¸²à¸£",
+DlgDocMeAuthor : "ผู้สร้างเอà¸à¸ªà¸²à¸£",
+DlgDocMeCopy : "สงวนลิขสิทธิ์",
+DlgDocPreview : "ตัวอย่างหน้าเอà¸à¸ªà¸²à¸£",
+
+// Templates Dialog
+Templates : "เทมเพลต",
+DlgTemplatesTitle : "เทมเพลตของส่วนเนื้อหาเว็บไซต์",
+DlgTemplatesSelMsg : "à¸à¸£à¸¸à¸“าเลือภเทมเพลต เพื่อนำไปà¹à¸à¹‰à¹„ขในอีดิตเตอร์<br />(เนื้อหาส่วนนี้จะหายไป):",
+DlgTemplatesLoading : "à¸à¸³à¸¥à¸±à¸‡à¹‚หลดรายà¸à¸²à¸£à¹€à¸—มเพลตทั้งหมด...",
+DlgTemplatesNoTpl : "(ยังไม่มีà¸à¸²à¸£à¸à¸³à¸«à¸™à¸”เทมเพลต)",
+DlgTemplatesReplace : "à¹à¸—นที่เนื้อหาเว็บไซต์ที่เลือà¸",
+
+// About Dialog
+DlgAboutAboutTab : "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚ปรà¹à¸à¸£à¸¡",
+DlgAboutBrowserInfoTab : "โปรà¹à¸à¸£à¸¡à¸—่องเว็บที่ท่านใช้",
+DlgAboutLicenseTab : "ลิขสิทธิ์",
+DlgAboutVersion : "รุ่น",
+DlgAboutInfo : "For further information go to" //MISSING
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/tr.js b/httemplate/elements/fckeditor/editor/lang/tr.js
new file mode 100644
index 0000000..53b371e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/tr.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Turkish language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Araç Çubuğunu Kapat",
+ToolbarExpand : "Araç Çubuğunu Aç",
+
+// Toolbar Items and Context Menu
+Save : "Kaydet",
+NewPage : "Yeni Sayfa",
+Preview : "Ön İzleme",
+Cut : "Kes",
+Copy : "Kopyala",
+Paste : "Yapıştır",
+PasteText : "Düzyazı Olarak Yapıştır",
+PasteWord : "Word'den Yapıştır",
+Print : "Yazdır",
+SelectAll : "Tümünü Seç",
+RemoveFormat : "Biçimi Kaldır",
+InsertLinkLbl : "Köprü",
+InsertLink : "Köprü Ekle/Düzenle",
+RemoveLink : "Köprü Kaldır",
+Anchor : "Çapa Ekle/Düzenle",
+InsertImageLbl : "Resim",
+InsertImage : "Resim Ekle/Düzenle",
+InsertFlashLbl : "Flash",
+InsertFlash : "Flash Ekle/Düzenle",
+InsertTableLbl : "Tablo",
+InsertTable : "Tablo Ekle/Düzenle",
+InsertLineLbl : "Satır",
+InsertLine : "Yatay Satır Ekle",
+InsertSpecialCharLbl: "Özel Karakter",
+InsertSpecialChar : "Özel Karakter Ekle",
+InsertSmileyLbl : "Ä°fade",
+InsertSmiley : "Ä°fade Ekle",
+About : "FCKeditor Hakkında",
+Bold : "Kalın",
+Italic : "Ä°talik",
+Underline : "Altı Çizgili",
+StrikeThrough : "Üstü Çizgili",
+Subscript : "Alt Simge",
+Superscript : "Ãœst Simge",
+LeftJustify : "Sola Dayalı",
+CenterJustify : "Ortalanmış",
+RightJustify : "Sağa Dayalı",
+BlockJustify : "İki Kenara Yaslanmış",
+DecreaseIndent : "Sekme Azalt",
+IncreaseIndent : "Sekme Arttır",
+Undo : "Geri Al",
+Redo : "Tekrarla",
+NumberedListLbl : "Numaralı Liste",
+NumberedList : "Numaralı Liste Ekle/Kaldır",
+BulletedListLbl : "Simgeli Liste",
+BulletedList : "Simgeli Liste Ekle/Kaldır",
+ShowTableBorders : "Tablo Kenarlarını Göster",
+ShowDetails : "Detayları Göster",
+Style : "Biçem",
+FontFormat : "Biçim",
+Font : "Yazı Türü",
+FontSize : "Boyut",
+TextColor : "Yazı Rengi",
+BGColor : "Arka Renk",
+Source : "Kaynak",
+Find : "Bul",
+Replace : "DeÄŸiÅŸtir",
+SpellCheck : "Yazım Denetimi",
+UniversalKeyboard : "Evrensel Klavye",
+PageBreakLbl : "Sayfa sonu",
+PageBreak : "Sayfa Sonu Ekle",
+
+Form : "Form",
+Checkbox : "Onay Kutusu",
+RadioButton : "Seçenek Düğmesi",
+TextField : "Metin GiriÅŸi",
+Textarea : "Çok Satırlı Metin",
+HiddenField : "Gizli Veri",
+Button : "Düğme",
+SelectionField : "Seçim Menüsü",
+ImageButton : "Resimli Düğme",
+
+FitWindow : "Düzenleyici boyutunu büyüt",
+
+// Context Menu
+EditLink : "Köprü Düzenle",
+CellCM : "Hücre",
+RowCM : "Satır",
+ColumnCM : "Sütun",
+InsertRow : "Satır Ekle",
+DeleteRows : "Satır Sil",
+InsertColumn : "Sütun Ekle",
+DeleteColumns : "Sütun Sil",
+InsertCell : "Hücre Ekle",
+DeleteCells : "Hücre Sil",
+MergeCells : "Hücreleri Birleştir",
+SplitCell : "Hücre Böl",
+TableDelete : "Tabloyu Sil",
+CellProperties : "Hücre Özellikleri",
+TableProperties : "Tablo Özellikleri",
+ImageProperties : "Resim Özellikleri",
+FlashProperties : "Flash Özellikleri",
+
+AnchorProp : "Çapa Özellikleri",
+ButtonProp : "Düğme Özellikleri",
+CheckboxProp : "Onay Kutusu Özellikleri",
+HiddenFieldProp : "Gizli Veri Özellikleri",
+RadioButtonProp : "Seçenek Düğmesi Özellikleri",
+ImageButtonProp : "Resimli Düğme Özellikleri",
+TextFieldProp : "Metin Girişi Özellikleri",
+SelectionFieldProp : "Seçim Menüsü Özellikleri",
+TextareaProp : "Çok Satırlı Metin Özellikleri",
+FormProp : "Form Özellikleri",
+
+FontFormats : "Normal;Biçimli;Adres;Başlık 1;Başlık 2;Başlık 3;Başlık 4;Başlık 5;Başlık 6;Paragraf (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "XHTML işleniyor. Lütfen bekleyin...",
+Done : "Bitti",
+PasteWordConfirm : "Yapıştırdığınız yazı Word'den gelmişe benziyor. Yapıştırmadan önce gereksiz eklentileri silmek ister misiniz?",
+NotCompatiblePaste : "Bu komut Internet Explorer 5.5 ve ileriki sürümleri için mevcuttur. Temizlenmeden yapıştırılmasını ister misiniz ?",
+UnknownToolbarItem : "Bilinmeyen araç çubugu öğesi \"%1\"",
+UnknownCommand : "Bilinmeyen komut \"%1\"",
+NotImplemented : "Komut uyarlanamadı",
+UnknownToolbarSet : "\"%1\" araç çubuğu öğesi mevcut değil",
+NoActiveX : "Kullandığınız tarayıcının güvenlik ayarları bazı özelliklerin kullanılmasını engelliyor. Bu özelliklerin çalışması için \"Run ActiveX controls and plug-ins (Activex ve eklentileri çalıştır)\" seçeneğinin aktif yapılması gerekiyor. Kullanılamayan eklentiler ve hatalar konusunda daha fazla bilgi sahibi olun.",
+BrowseServerBlocked : "Kaynak tarayıcısı açılamadı. Tüm \"popup blocker\" programlarının devre dışı olduğundan emin olun. (Yahoo toolbar, Msn toolbar, Google toolbar gibi)",
+DialogBlocked : "Diyalog açmak mümkün olmadı. Tüm \"Popup Blocker\" programlarının devre dışı olduğundan emin olun.",
+
+// Dialogs
+DlgBtnOK : "Tamam",
+DlgBtnCancel : "Ä°ptal",
+DlgBtnClose : "Kapat",
+DlgBtnBrowseServer : "Sunucuyu Gez",
+DlgAdvancedTag : "GeliÅŸmiÅŸ",
+DlgOpOther : "<DiÄŸer>",
+DlgInfoTab : "Bilgi",
+DlgAlertUrl : "Lütfen URL girin",
+
+// General Dialogs Labels
+DlgGenNotSet : "<tanımlanmamış>",
+DlgGenId : "Kimlik",
+DlgGenLangDir : "Dil Yönü",
+DlgGenLangDirLtr : "Soldan SaÄŸa (LTR)",
+DlgGenLangDirRtl : "SaÄŸdan Sola (RTL)",
+DlgGenLangCode : "Dil Kodlaması",
+DlgGenAccessKey : "EriÅŸim TuÅŸu",
+DlgGenName : "Ad",
+DlgGenTabIndex : "Sekme Ä°ndeksi",
+DlgGenLongDescr : "Uzun Tanımlı URL",
+DlgGenClass : "Biçem Sayfası Sınıfları",
+DlgGenTitle : "Danışma Başlığı",
+DlgGenContType : "Danışma İçerik Türü",
+DlgGenLinkCharset : "Bağlı Kaynak Karakter Gurubu",
+DlgGenStyle : "Biçem",
+
+// Image Dialog
+DlgImgTitle : "Resim Özellikleri",
+DlgImgInfoTab : "Resim Bilgisi",
+DlgImgBtnUpload : "Sunucuya Yolla",
+DlgImgURL : "URL",
+DlgImgUpload : "Karşıya Yükle",
+DlgImgAlt : "Alternatif Yazı",
+DlgImgWidth : "GeniÅŸlik",
+DlgImgHeight : "Yükseklik",
+DlgImgLockRatio : "Oranı Kilitle",
+DlgBtnResetSize : "Boyutu Başa Döndür",
+DlgImgBorder : "Kenar",
+DlgImgHSpace : "Yatay BoÅŸluk",
+DlgImgVSpace : "Dikey BoÅŸluk",
+DlgImgAlign : "Hizalama",
+DlgImgAlignLeft : "Sol",
+DlgImgAlignAbsBottom: "Tam Altı",
+DlgImgAlignAbsMiddle: "Tam Ortası",
+DlgImgAlignBaseline : "Taban Çizgisi",
+DlgImgAlignBottom : "Alt",
+DlgImgAlignMiddle : "Orta",
+DlgImgAlignRight : "SaÄŸ",
+DlgImgAlignTextTop : "Yazı Tepeye",
+DlgImgAlignTop : "Tepe",
+DlgImgPreview : "Ön İzleme",
+DlgImgAlertUrl : "Lütfen resmin URL'sini yazınız",
+DlgImgLinkTab : "Köprü",
+
+// Flash Dialog
+DlgFlashTitle : "Flash Özellikleri",
+DlgFlashChkPlay : "Otomatik Oynat",
+DlgFlashChkLoop : "Döngü",
+DlgFlashChkMenu : "Flash Menüsünü Kullan",
+DlgFlashScale : "Boyutlandır",
+DlgFlashScaleAll : "Hepsini Göster",
+DlgFlashScaleNoBorder : "Kenar Yok",
+DlgFlashScaleFit : "Tam Sığdır",
+
+// Link Dialog
+DlgLnkWindowTitle : "Köprü",
+DlgLnkInfoTab : "Köprü Bilgisi",
+DlgLnkTargetTab : "Hedef",
+
+DlgLnkType : "Köprü Türü",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Bu sayfada çapa",
+DlgLnkTypeEMail : "E-Posta",
+DlgLnkProto : "Protokol",
+DlgLnkProtoOther : "<diÄŸer>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Çapa Seç",
+DlgLnkAnchorByName : "Çapa Adı ile",
+DlgLnkAnchorById : "Eleman Kimlik Numarası ile",
+DlgLnkNoAnchors : "<Bu belgede hiç çapa yok>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "E-Posta Adresi",
+DlgLnkEMailSubject : "Ä°leti Konusu",
+DlgLnkEMailBody : "İleti Gövdesi",
+DlgLnkUpload : "Karşıya Yükle",
+DlgLnkBtnUpload : "Sunucuya Gönder",
+
+DlgLnkTarget : "Hedef",
+DlgLnkTargetFrame : "<çerçeve>",
+DlgLnkTargetPopup : "<yeni açılan pencere>",
+DlgLnkTargetBlank : "Yeni Pencere(_blank)",
+DlgLnkTargetParent : "Anne Pencere (_parent)",
+DlgLnkTargetSelf : "Kendi Penceresi (_self)",
+DlgLnkTargetTop : "En Ãœst Pencere (_top)",
+DlgLnkTargetFrameName : "Hedef Çerçeve Adı",
+DlgLnkPopWinName : "Yeni Açılan Pencere Adı",
+DlgLnkPopWinFeat : "Yeni Açılan Pencere Özellikleri",
+DlgLnkPopResize : "Boyutlandırılabilir",
+DlgLnkPopLocation : "Yer Çubuğu",
+DlgLnkPopMenu : "Menü Çubuğu",
+DlgLnkPopScroll : "Kaydırma Çubukları",
+DlgLnkPopStatus : "Durum Çubuğu",
+DlgLnkPopToolbar : "Araç Çubuğu",
+DlgLnkPopFullScrn : "Tam Ekran (IE)",
+DlgLnkPopDependent : "Bağımlı (Netscape)",
+DlgLnkPopWidth : "GeniÅŸlik",
+DlgLnkPopHeight : "Yükseklik",
+DlgLnkPopLeft : "Sola Göre Konum",
+DlgLnkPopTop : "Yukarıya Göre Konum",
+
+DlnLnkMsgNoUrl : "Lütfen köprü URL'sini yazın",
+DlnLnkMsgNoEMail : "Lütfen E-posta adresini yazın",
+DlnLnkMsgNoAnchor : "Lütfen bir çapa seçin",
+DlnLnkMsgInvPopName : "Açılır pencere adı abecesel bir karakterle başlamalı ve boşluk içermemelidir",
+
+// Color Dialog
+DlgColorTitle : "Renk Seç",
+DlgColorBtnClear : "Temizle",
+DlgColorHighlight : "Vurgula",
+DlgColorSelected : "Seçilmiş",
+
+// Smiley Dialog
+DlgSmileyTitle : "Ä°fade Ekle",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Özel Karakter Seç",
+
+// Table Dialog
+DlgTableTitle : "Tablo Özellikleri",
+DlgTableRows : "Satırlar",
+DlgTableColumns : "Sütunlar",
+DlgTableBorder : "Kenar Kalınlığı",
+DlgTableAlign : "Hizalama",
+DlgTableAlignNotSet : "<Tanımlanmamış>",
+DlgTableAlignLeft : "Sol",
+DlgTableAlignCenter : "Merkez",
+DlgTableAlignRight : "SaÄŸ",
+DlgTableWidth : "GeniÅŸlik",
+DlgTableWidthPx : "piksel",
+DlgTableWidthPc : "yüzde",
+DlgTableHeight : "Yükseklik",
+DlgTableCellSpace : "Izgara kalınlığı",
+DlgTableCellPad : "Izgara yazı arası",
+DlgTableCaption : "Başlık",
+DlgTableSummary : "Özet",
+
+// Table Cell Dialog
+DlgCellTitle : "Hücre Özellikleri",
+DlgCellWidth : "GeniÅŸlik",
+DlgCellWidthPx : "piksel",
+DlgCellWidthPc : "yüzde",
+DlgCellHeight : "Yükseklik",
+DlgCellWordWrap : "Sözcük Kaydır",
+DlgCellWordWrapNotSet : "<Tanımlanmamış>",
+DlgCellWordWrapYes : "Evet",
+DlgCellWordWrapNo : "Hayır",
+DlgCellHorAlign : "Yatay Hizalama",
+DlgCellHorAlignNotSet : "<Tanımlanmamış>",
+DlgCellHorAlignLeft : "Sol",
+DlgCellHorAlignCenter : "Merkez",
+DlgCellHorAlignRight: "SaÄŸ",
+DlgCellVerAlign : "Dikey Hizalama",
+DlgCellVerAlignNotSet : "<Tanımlanmamış>",
+DlgCellVerAlignTop : "Tepe",
+DlgCellVerAlignMiddle : "Orta",
+DlgCellVerAlignBottom : "Alt",
+DlgCellVerAlignBaseline : "Taban Çizgisi",
+DlgCellRowSpan : "Satır Kapla",
+DlgCellCollSpan : "Sütun Kapla",
+DlgCellBackColor : "Arka Plan Rengi",
+DlgCellBorderColor : "Kenar Rengi",
+DlgCellBtnSelect : "Seç...",
+
+// Find Dialog
+DlgFindTitle : "Bul",
+DlgFindFindBtn : "Bul",
+DlgFindNotFoundMsg : "Belirtilen yazı bulunamadı.",
+
+// Replace Dialog
+DlgReplaceTitle : "DeÄŸiÅŸtir",
+DlgReplaceFindLbl : "Aranan:",
+DlgReplaceReplaceLbl : "Bununla deÄŸiÅŸtir:",
+DlgReplaceCaseChk : "Büyük/küçük harf duyarlı",
+DlgReplaceReplaceBtn : "DeÄŸiÅŸtir",
+DlgReplaceReplAllBtn : "Tümünü Değiştir",
+DlgReplaceWordChk : "Kelimenin tamamı uysun",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Gezgin yazılımınızın güvenlik ayarları düzenleyicinin otomatik kesme işlemine izin vermiyor. İşlem için (Ctrl+X) tuşlarını kullanın.",
+PasteErrorCopy : "Gezgin yazılımınızın güvenlik ayarları düzenleyicinin otomatik kopyalama işlemine izin vermiyor. İşlem için (Ctrl+C) tuşlarını kullanın.",
+
+PasteAsText : "Düz Metin Olarak Yapıştır",
+PasteFromWord : "Word'den yapıştır",
+
+DlgPasteMsg2 : "Lütfen aşağıdaki kutunun içine yapıştırın. (<STRONG>Ctrl+V</STRONG>) ve <STRONG>Tamam</STRONG> butonunu tıklayın.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Yazı Tipi tanımlarını yoksay",
+DlgPasteRemoveStyles : "Biçem Tanımlarını çıkar",
+DlgPasteCleanBox : "Temizlik Kutusu",
+
+// Color Picker
+ColorAutomatic : "Otomatik",
+ColorMoreColors : "DiÄŸer renkler...",
+
+// Document Properties
+DocProps : "Belge Özellikleri",
+
+// Anchor Dialog
+DlgAnchorTitle : "Çapa Özellikleri",
+DlgAnchorName : "Çapa Adı",
+DlgAnchorErrorName : "Lütfen çapa için ad giriniz",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Sözlükte Yok",
+DlgSpellChangeTo : "Åžuna deÄŸiÅŸtir:",
+DlgSpellBtnIgnore : "Yoksay",
+DlgSpellBtnIgnoreAll : "Tümünü Yoksay",
+DlgSpellBtnReplace : "DeÄŸiÅŸtir",
+DlgSpellBtnReplaceAll : "Tümünü Değiştir",
+DlgSpellBtnUndo : "Geri Al",
+DlgSpellNoSuggestions : "- Öneri Yok -",
+DlgSpellProgress : "Yazım denetimi işlemde...",
+DlgSpellNoMispell : "Yazım denetimi tamamlandı: Yanlış yazıma rastlanmadı",
+DlgSpellNoChanges : "Yazım denetimi tamamlandı: Hiçbir kelime değiştirilmedi",
+DlgSpellOneChange : "Yazım denetimi tamamlandı: Bir kelime değiştirildi",
+DlgSpellManyChanges : "Yazım denetimi tamamlandı: %1 kelime değiştirildi",
+
+IeSpellDownload : "Yazım denetimi yüklenmemiş. Şimdi yüklemek ister misiniz?",
+
+// Button Dialog
+DlgButtonText : "Metin (DeÄŸer)",
+DlgButtonType : "Tip",
+DlgButtonTypeBtn : "Düğme",
+DlgButtonTypeSbm : "Gönder",
+DlgButtonTypeRst : "Sıfırla",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Ad",
+DlgCheckboxValue : "DeÄŸer",
+DlgCheckboxSelected : "Seçili",
+
+// Form Dialog
+DlgFormName : "Ad",
+DlgFormAction : "Ä°ÅŸlem",
+DlgFormMethod : "Yöntem",
+
+// Select Field Dialog
+DlgSelectName : "Ad",
+DlgSelectValue : "DeÄŸer",
+DlgSelectSize : "Boyut",
+DlgSelectLines : "satır",
+DlgSelectChkMulti : "Çoklu seçime izin ver",
+DlgSelectOpAvail : "Mevcut Seçenekler",
+DlgSelectOpText : "Metin",
+DlgSelectOpValue : "DeÄŸer",
+DlgSelectBtnAdd : "Ekle",
+DlgSelectBtnModify : "Düzenle",
+DlgSelectBtnUp : "Yukarı",
+DlgSelectBtnDown : "Aşağı",
+DlgSelectBtnSetValue : "Seçili değer olarak ata",
+DlgSelectBtnDelete : "Sil",
+
+// Textarea Dialog
+DlgTextareaName : "Ad",
+DlgTextareaCols : "Sütunlar",
+DlgTextareaRows : "Satırlar",
+
+// Text Field Dialog
+DlgTextName : "Ad",
+DlgTextValue : "DeÄŸer",
+DlgTextCharWidth : "Karakter GeniÅŸliÄŸi",
+DlgTextMaxChars : "En Fazla Karakter",
+DlgTextType : "Tür",
+DlgTextTypeText : "Metin",
+DlgTextTypePass : "Parola",
+
+// Hidden Field Dialog
+DlgHiddenName : "Ad",
+DlgHiddenValue : "DeÄŸer",
+
+// Bulleted List Dialog
+BulletedListProp : "Simgeli Liste Özellikleri",
+NumberedListProp : "Numaralı Liste Özellikleri",
+DlgLstStart : "Başlangıç",
+DlgLstType : "Tip",
+DlgLstTypeCircle : "Çember",
+DlgLstTypeDisc : "Disk",
+DlgLstTypeSquare : "Kare",
+DlgLstTypeNumbers : "Sayılar (1, 2, 3)",
+DlgLstTypeLCase : "Küçük Harfler (a, b, c)",
+DlgLstTypeUCase : "Büyük Harfler (A, B, C)",
+DlgLstTypeSRoman : "Küçük Romen Rakamları (i, ii, iii)",
+DlgLstTypeLRoman : "Büyük Romen Rakamları (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Genel",
+DlgDocBackTab : "Arka Plan",
+DlgDocColorsTab : "Renkler ve Kenar Boşlukları",
+DlgDocMetaTab : "Tanım Bilgisi (Meta)",
+
+DlgDocPageTitle : "Sayfa Başlığı",
+DlgDocLangDir : "Dil Yönü",
+DlgDocLangDirLTR : "Soldan SaÄŸa (LTR)",
+DlgDocLangDirRTL : "SaÄŸdan Sola (RTL)",
+DlgDocLangCode : "Dil Kodu",
+DlgDocCharSet : "Karakter Kümesi Kodlaması",
+DlgDocCharSetCE : "Orta Avrupa",
+DlgDocCharSetCT : "Geleneksel Çince (Big5)",
+DlgDocCharSetCR : "Kiril",
+DlgDocCharSetGR : "Yunanca",
+DlgDocCharSetJP : "Japonca",
+DlgDocCharSetKR : "Korece",
+DlgDocCharSetTR : "Türkçe",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Batı Avrupa",
+DlgDocCharSetOther : "Diğer Karakter Kümesi Kodlaması",
+
+DlgDocDocType : "Belge Türü Başlığı",
+DlgDocDocTypeOther : "Diğer Belge Türü Başlığı",
+DlgDocIncXHTML : "XHTML Bildirimlerini Dahil Et",
+DlgDocBgColor : "Arka Plan Rengi",
+DlgDocBgImage : "Arka Plan Resim URLsi",
+DlgDocBgNoScroll : "Sabit Arka Plan",
+DlgDocCText : "Metin",
+DlgDocCLink : "Köprü",
+DlgDocCVisited : "Ziyaret Edilmiş Köprü",
+DlgDocCActive : "Etkin Köprü",
+DlgDocMargins : "Kenar Boşlukları",
+DlgDocMaTop : "Tepe",
+DlgDocMaLeft : "Sol",
+DlgDocMaRight : "SaÄŸ",
+DlgDocMaBottom : "Alt",
+DlgDocMeIndex : "Belge Dizinleme Anahtar Kelimeleri (virgülle ayrılmış)",
+DlgDocMeDescr : "Belge Tanımı",
+DlgDocMeAuthor : "Yazar",
+DlgDocMeCopy : "Telif",
+DlgDocPreview : "Ön İzleme",
+
+// Templates Dialog
+Templates : "Åžablonlar",
+DlgTemplatesTitle : "İçerik Şablonları",
+DlgTemplatesSelMsg : "Düzenleyicide açmak için lütfen bir şablon seçin.<br>(hali hazırdaki içerik kaybolacaktır.):",
+DlgTemplatesLoading : "Şablon listesi yüklenmekte. Lütfen bekleyiniz...",
+DlgTemplatesNoTpl : "(Belirli bir şablon seçilmedi)",
+DlgTemplatesReplace : "Mevcut içerik ile değiştir",
+
+// About Dialog
+DlgAboutAboutTab : "Hakkında",
+DlgAboutBrowserInfoTab : "Gezgin Bilgisi",
+DlgAboutLicenseTab : "Lisans",
+DlgAboutVersion : "sürüm",
+DlgAboutInfo : "Daha fazla bilgi için:"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/uk.js b/httemplate/elements/fckeditor/editor/lang/uk.js
new file mode 100644
index 0000000..1defaac
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/uk.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Ukrainian language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Згорнути панель інÑтрументів",
+ToolbarExpand : "Розгорнути панель інÑтрументів",
+
+// Toolbar Items and Context Menu
+Save : "Зберегти",
+NewPage : "Ðова Ñторінка",
+Preview : "Попередній переглÑд",
+Cut : "Вирізати",
+Copy : "Копіювати",
+Paste : "Ð’Ñтавити",
+PasteText : "Ð’Ñтавити тільки текÑÑ‚",
+PasteWord : "Ð’Ñтавити з Word",
+Print : "Друк",
+SelectAll : "Виділити вÑе",
+RemoveFormat : "Прибрати форматуваннÑ",
+InsertLinkLbl : "ПоÑиланнÑ",
+InsertLink : "Ð’Ñтавити/Редагувати поÑиланнÑ",
+RemoveLink : "Знищити поÑиланнÑ",
+Anchor : "Ð’Ñтавити/Редагувати Ñкір",
+InsertImageLbl : "ЗображеннÑ",
+InsertImage : "Ð’Ñтавити/Редагувати зображеннÑ",
+InsertFlashLbl : "Flash",
+InsertFlash : "Ð’Ñтавити/Редагувати Flash",
+InsertTableLbl : "ТаблицÑ",
+InsertTable : "Ð’Ñтавити/Редагувати таблицю",
+InsertLineLbl : "ЛініÑ",
+InsertLine : "Ð’Ñтавити горизонтальну лінію",
+InsertSpecialCharLbl: "Спеціальний Ñимвол",
+InsertSpecialChar : "Ð’Ñтавити Ñпеціальний Ñимвол",
+InsertSmileyLbl : "Смайлик",
+InsertSmiley : "Ð’Ñтавити Ñмайлик",
+About : "Про FCKeditor",
+Bold : "Жирний",
+Italic : "КурÑив",
+Underline : "ПідкреÑлений",
+StrikeThrough : "ЗакреÑлений",
+Subscript : "ПідрÑдковий індекÑ",
+Superscript : "ÐадрÑдковий индекÑ",
+LeftJustify : "По лівому краю",
+CenterJustify : "По центру",
+RightJustify : "По правому краю",
+BlockJustify : "По ширині",
+DecreaseIndent : "Зменшити відÑтуп",
+IncreaseIndent : "Збільшити відÑтуп",
+Undo : "Повернути",
+Redo : "Повторити",
+NumberedListLbl : "Ðумерований ÑпиÑок",
+NumberedList : "Ð’Ñтавити/Видалити нумерований ÑпиÑок",
+BulletedListLbl : "Маркований ÑпиÑок",
+BulletedList : "Ð’Ñтавити/Видалити маркований ÑпиÑок",
+ShowTableBorders : "Показати бордюри таблиці",
+ShowDetails : "Показати деталі",
+Style : "Стиль",
+FontFormat : "ФорматуваннÑ",
+Font : "Шрифт",
+FontSize : "Розмір",
+TextColor : "Колір текÑту",
+BGColor : "Колір фону",
+Source : "Джерело",
+Find : "Пошук",
+Replace : "Заміна",
+SpellCheck : "Перевірити орфографію",
+UniversalKeyboard : "УніверÑальна клавіатура",
+PageBreakLbl : "Розривши Ñторінки",
+PageBreak : "Ð’Ñтавити розривши Ñторінки",
+
+Form : "Форма",
+Checkbox : "Флагова кнопка",
+RadioButton : "Кнопка вибору",
+TextField : "ТекÑтове поле",
+Textarea : "ТекÑтова облаÑÑ‚ÑŒ",
+HiddenField : "Приховане поле",
+Button : "Кнопка",
+SelectionField : "СпиÑок",
+ImageButton : "Кнопка із зображеннÑм",
+
+FitWindow : "Розвернути вікно редактора",
+
+// Context Menu
+EditLink : "Ð’Ñтавити поÑиланнÑ",
+CellCM : "ОÑередок",
+RowCM : "РÑдок",
+ColumnCM : "Колонка",
+InsertRow : "Ð’Ñтавити Ñтроку",
+DeleteRows : "Видалити Ñтроки",
+InsertColumn : "Ð’Ñтавити колонку",
+DeleteColumns : "Видалити колонки",
+InsertCell : "Ð’Ñтавити комірку",
+DeleteCells : "Видалити комірки",
+MergeCells : "Об'єднати комірки",
+SplitCell : "Роз'єднати комірку",
+TableDelete : "Видалити таблицю",
+CellProperties : "ВлаÑтивоÑÑ‚Ñ– комірки",
+TableProperties : "ВлаÑтивоÑÑ‚Ñ– таблиці",
+ImageProperties : "ВлаÑтивоÑÑ‚Ñ– зображеннÑ",
+FlashProperties : "ВлаÑтивоÑÑ‚Ñ– Flash",
+
+AnchorProp : "ВлаÑтивоÑÑ‚Ñ– ÑкорÑ",
+ButtonProp : "ВлаÑтивоÑÑ‚Ñ– кнопки",
+CheckboxProp : "ВлаÑтивоÑÑ‚Ñ– флагової кнопки",
+HiddenFieldProp : "ВлаÑтивоÑÑ‚Ñ– прихованого полÑ",
+RadioButtonProp : "ВлаÑтивоÑÑ‚Ñ– кнопки вибору",
+ImageButtonProp : "ВлаÑтивоÑÑ‚Ñ– кнопки із зображеннÑм",
+TextFieldProp : "ВлаÑтивоÑÑ‚Ñ– текÑтового полÑ",
+SelectionFieldProp : "ВлаÑтивоÑÑ‚Ñ– ÑпиÑку",
+TextareaProp : "ВлаÑтивоÑÑ‚Ñ– текÑтової облаÑÑ‚Ñ–",
+FormProp : "ВлаÑтивоÑÑ‚Ñ– форми",
+
+FontFormats : "Ðормальний;Форматований;ÐдреÑа;Заголовок 1;Заголовок 2;Заголовок 3;Заголовок 4;Заголовок 5;Заголовок 6;Ðормальний (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Обробка XHTML. Зачекайте, будь лаÑка...",
+Done : "Зроблено",
+PasteWordConfirm : "ТекÑÑ‚, що ви хочете вÑтавити, Ñхожий на копійований з Word. Ви хочете очиÑтити його перед вÑтавкою?",
+NotCompatiblePaste : "Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° доÑтупна Ð´Ð»Ñ Internet Explorer верÑÑ–Ñ— 5.5 або вище. Ви хочете вÑтавити без очищеннÑ?",
+UnknownToolbarItem : "Ðевідомий елемент панелі інÑтрументів \"%1\"",
+UnknownCommand : "Ðевідоме ім'Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¸ \"%1\"",
+NotImplemented : "Команда не реалізована",
+UnknownToolbarSet : "Панель інÑтрументів \"%1\" не Ñ–Ñнує",
+NoActiveX : "ÐаÑтройки безпеки вашого браузера можуть обмежувати деÑкі влаÑтивоÑÑ‚Ñ– редактора. Ви повинні включити опцію \"ЗапуÑкати елементи ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ ACTIVEX Ñ– плугіни\". Ви можете бачити помилки Ñ– помічати відÑутніÑÑ‚ÑŒ можливоÑтей.",
+BrowseServerBlocked : "РеÑурÑи браузера не можуть бути відкриті. Перевірте що Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñпливаючих вікон вимкнені.",
+DialogBlocked : "Ðе можливо відкрити вікно діалогу. Перевірте що Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñпливаючих вікон вимкнені.",
+
+// Dialogs
+DlgBtnOK : "ОК",
+DlgBtnCancel : "СкаÑувати",
+DlgBtnClose : "Зачинити",
+DlgBtnBrowseServer : "ПередивитиÑÑ Ð½Ð° Ñервері",
+DlgAdvancedTag : "Розширений",
+DlgOpOther : "<Інше>",
+DlgInfoTab : "Інфо",
+DlgAlertUrl : "Ð’Ñтавте, будь-лаÑка, URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<не визначено>",
+DlgGenId : "Ідентифікатор",
+DlgGenLangDir : "ÐапрÑмок мови",
+DlgGenLangDirLtr : "Зліва на право (LTR)",
+DlgGenLangDirRtl : "Зправа на ліво (RTL)",
+DlgGenLangCode : "Мова",
+DlgGenAccessKey : "ГарÑча клавіша",
+DlgGenName : "Им'Ñ",
+DlgGenTabIndex : "ПоÑлідовніÑÑ‚ÑŒ переходу",
+DlgGenLongDescr : "Довгий Ð¾Ð¿Ð¸Ñ URL",
+DlgGenClass : "ÐšÐ»Ð°Ñ CSS",
+DlgGenTitle : "Заголовок",
+DlgGenContType : "Тип вміÑту",
+DlgGenLinkCharset : "Кодировка",
+DlgGenStyle : "Стиль CSS",
+
+// Image Dialog
+DlgImgTitle : "ВлаÑтивоÑÑ‚Ñ– зображеннÑ",
+DlgImgInfoTab : "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ изображении",
+DlgImgBtnUpload : "ÐадіÑлати на Ñервер",
+DlgImgURL : "URL",
+DlgImgUpload : "Закачати",
+DlgImgAlt : "Ðльтернативний текÑÑ‚",
+DlgImgWidth : "Ширина",
+DlgImgHeight : "ВиÑота",
+DlgImgLockRatio : "Зберегти пропорції",
+DlgBtnResetSize : "Скинути розмір",
+DlgImgBorder : "Бордюр",
+DlgImgHSpace : "Горизонтальний відÑтуп",
+DlgImgVSpace : "Вертикальний відÑтуп",
+DlgImgAlign : "ВирівнюваннÑ",
+DlgImgAlignLeft : "По лівому краю",
+DlgImgAlignAbsBottom: "ÐÐ±Ñ Ð¿Ð¾ низу",
+DlgImgAlignAbsMiddle: "ÐÐ±Ñ Ð¿Ð¾ Ñередині",
+DlgImgAlignBaseline : "По базовій лінії",
+DlgImgAlignBottom : "По низу",
+DlgImgAlignMiddle : "По Ñередині",
+DlgImgAlignRight : "По правому краю",
+DlgImgAlignTextTop : "ТекÑÑ‚ на верху",
+DlgImgAlignTop : "По верху",
+DlgImgPreview : "Попередній переглÑд",
+DlgImgAlertUrl : "Будь лаÑка, введіть URL зображеннÑ",
+DlgImgLinkTab : "ПоÑиланнÑ",
+
+// Flash Dialog
+DlgFlashTitle : "ВлаÑтивоÑÑ‚Ñ– Flash",
+DlgFlashChkPlay : "Ðвто програваннÑ",
+DlgFlashChkLoop : "Зациклити",
+DlgFlashChkMenu : "Дозволити меню Flash",
+DlgFlashScale : "МаÑштаб",
+DlgFlashScaleAll : "Показати вÑÑ–",
+DlgFlashScaleNoBorder : "Без рамки",
+DlgFlashScaleFit : "ДійÑний розмір",
+
+// Link Dialog
+DlgLnkWindowTitle : "ПоÑиланнÑ",
+DlgLnkInfoTab : "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ÑиланнÑ",
+DlgLnkTargetTab : "Ціль",
+
+DlgLnkType : "Тип поÑиланнÑ",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Якір на цю Ñторінку",
+DlgLnkTypeEMail : "Эл. пошта",
+DlgLnkProto : "Протокол",
+DlgLnkProtoOther : "<інше>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Оберіть Ñкір",
+DlgLnkAnchorByName : "За ім'Ñм ÑкорÑ",
+DlgLnkAnchorById : "За ідентифікатором елемента",
+DlgLnkNoAnchors : "<Ðемає Ñкорів доÑтупних в цьому документі>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "ÐдреÑа ел. пошти",
+DlgLnkEMailSubject : "Тема лиÑта",
+DlgLnkEMailBody : "Тіло повідомленнÑ",
+DlgLnkUpload : "Закачати",
+DlgLnkBtnUpload : "ПереÑлати на Ñервер",
+
+DlgLnkTarget : "Ціль",
+DlgLnkTargetFrame : "<фрейм>",
+DlgLnkTargetPopup : "<Ñпливаюче вікно>",
+DlgLnkTargetBlank : "Ðове вікно (_blank)",
+DlgLnkTargetParent : "БатьківÑьке вікно (_parent)",
+DlgLnkTargetSelf : "Теж вікно (_self)",
+DlgLnkTargetTop : "Ðайвище вікно (_top)",
+DlgLnkTargetFrameName : "Ім'Ñ Ñ†ÐµÐ»ÐµÐ²Ð¾Ð³Ð¾ фрейма",
+DlgLnkPopWinName : "Ім'Ñ Ñпливаючого вікна",
+DlgLnkPopWinFeat : "ВлаÑтивоÑÑ‚Ñ– Ñпливаючого вікна",
+DlgLnkPopResize : "ЗмінюєтьÑÑ Ð² розмірах",
+DlgLnkPopLocation : "Панель локації",
+DlgLnkPopMenu : "Панель меню",
+DlgLnkPopScroll : "ПолоÑи прокрутки",
+DlgLnkPopStatus : "Строка ÑтатуÑу",
+DlgLnkPopToolbar : "Панель інÑтрументів",
+DlgLnkPopFullScrn : "Повний екран (IE)",
+DlgLnkPopDependent : "Залежний (Netscape)",
+DlgLnkPopWidth : "Ширина",
+DlgLnkPopHeight : "ВиÑота",
+DlgLnkPopLeft : "ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð·Ð»Ñ–Ð²Ð°",
+DlgLnkPopTop : "ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð·Ð²ÐµÑ€Ñ…Ñƒ",
+
+DlnLnkMsgNoUrl : "Будь лаÑка, занеÑÑ–Ñ‚ÑŒ URL поÑиланнÑ",
+DlnLnkMsgNoEMail : "Будь лаÑка, занеÑÑ–Ñ‚ÑŒ Ð°Ð´Ñ€ÐµÑ Ñл. почты",
+DlnLnkMsgNoAnchor : "Будь лаÑка, оберіть Ñкір",
+DlnLnkMsgInvPopName : "Ðазва Ñпливаючого вікна повинна починатиÑÑ Ð±ÑƒÐºÐ²Ð¸ Ñ– не може міÑтити пропуÑків",
+
+// Color Dialog
+DlgColorTitle : "Оберіть колір",
+DlgColorBtnClear : "ОчиÑтити",
+DlgColorHighlight : "ПідÑвічений",
+DlgColorSelected : "Обраний",
+
+// Smiley Dialog
+DlgSmileyTitle : "Ð’Ñтавити Ñмайлик",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Оберіть Ñпеціальний Ñимвол",
+
+// Table Dialog
+DlgTableTitle : "ВлаÑтивоÑÑ‚Ñ– таблиці",
+DlgTableRows : "Строки",
+DlgTableColumns : "Колонки",
+DlgTableBorder : "Розмір бордюра",
+DlgTableAlign : "ВирівнюваннÑ",
+DlgTableAlignNotSet : "<Ðе вÑÑ‚.>",
+DlgTableAlignLeft : "Зліва",
+DlgTableAlignCenter : "По центру",
+DlgTableAlignRight : "Зправа",
+DlgTableWidth : "Ширина",
+DlgTableWidthPx : "пікÑелів",
+DlgTableWidthPc : "відÑотків",
+DlgTableHeight : "ВиÑота",
+DlgTableCellSpace : "Проміжок (spacing)",
+DlgTableCellPad : "ВідÑтуп (padding)",
+DlgTableCaption : "Заголовок",
+DlgTableSummary : "Резюме",
+
+// Table Cell Dialog
+DlgCellTitle : "ВлаÑтивоÑÑ‚Ñ– комірки",
+DlgCellWidth : "Ширина",
+DlgCellWidthPx : "пікÑелів",
+DlgCellWidthPc : "відÑотків",
+DlgCellHeight : "ВиÑота",
+DlgCellWordWrap : "Ð—Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñ‚ÐµÐºÑта",
+DlgCellWordWrapNotSet : "<Ðе вÑÑ‚.>",
+DlgCellWordWrapYes : "Так",
+DlgCellWordWrapNo : "ÐÑ–",
+DlgCellHorAlign : "Горизонтальне вирівнюваннÑ",
+DlgCellHorAlignNotSet : "<Ðе вÑÑ‚.>",
+DlgCellHorAlignLeft : "Зліва",
+DlgCellHorAlignCenter : "По центру",
+DlgCellHorAlignRight: "Зправа",
+DlgCellVerAlign : "Вертикальное вирівнюваннÑ",
+DlgCellVerAlignNotSet : "<Ðе вÑÑ‚.>",
+DlgCellVerAlignTop : "Зверху",
+DlgCellVerAlignMiddle : "ПоÑередині",
+DlgCellVerAlignBottom : "Знизу",
+DlgCellVerAlignBaseline : "По базовій лінії",
+DlgCellRowSpan : "Діапазон Ñтрок (span)",
+DlgCellCollSpan : "Діапазон колонок (span)",
+DlgCellBackColor : "Колір фона",
+DlgCellBorderColor : "Колір бордюра",
+DlgCellBtnSelect : "Оберіть...",
+
+// Find Dialog
+DlgFindTitle : "Пошук",
+DlgFindFindBtn : "Пошук",
+DlgFindNotFoundMsg : "Вказаний текÑÑ‚ не знайдений.",
+
+// Replace Dialog
+DlgReplaceTitle : "Замінити",
+DlgReplaceFindLbl : "Шукати:",
+DlgReplaceReplaceLbl : "Замінити на:",
+DlgReplaceCaseChk : "Учитывать региÑÑ‚Ñ€",
+DlgReplaceReplaceBtn : "Замінити",
+DlgReplaceReplAllBtn : "Замінити вÑе",
+DlgReplaceWordChk : "Збіг цілих Ñлів",
+
+// Paste Operations / Dialog
+PasteErrorCut : "ÐаÑтройки безпеки вашого браузера не дозволÑÑŽÑ‚ÑŒ редактору автоматично виконувати операції вирізуваннÑ. Будь лаÑка, викориÑтовуйте клавіатуру Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ (Ctrl+X).",
+PasteErrorCopy : "ÐаÑтройки безпеки вашого браузера не дозволÑÑŽÑ‚ÑŒ редактору автоматично виконувати операції копіюваннÑ. Будь лаÑка, викориÑтовуйте клавіатуру Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ (Ctrl+C).",
+
+PasteAsText : "Ð’Ñтавити тільки текÑÑ‚",
+PasteFromWord : "Ð’Ñтавити з Word",
+
+DlgPasteMsg2 : "Будь-лаÑка, вÑтавте з буфера обміну в цю облаÑÑ‚ÑŒ, кориÑтуючиÑÑŒ комбінацією клавіш (<STRONG>Ctrl+V</STRONG>) та натиÑніть <STRONG>OK</STRONG>.",
+DlgPasteSec : "Редактор не може отримати прÑмий доÑтуп до буферу обміну у зв'Ñзку з налаштуваннÑми вашого браузера. Вам потрібно вÑтавити інформацію повторно в це вікно.",
+DlgPasteIgnoreFont : "Ігнорувати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñ–Ð²",
+DlgPasteRemoveStyles : "Видалити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñтилів",
+DlgPasteCleanBox : "ОчиÑтити облаÑÑ‚ÑŒ",
+
+// Color Picker
+ColorAutomatic : "Ðвтоматичний",
+ColorMoreColors : "Кольори...",
+
+// Document Properties
+DocProps : "ВлаÑтивоÑÑ‚Ñ– документа",
+
+// Anchor Dialog
+DlgAnchorTitle : "ВлаÑтивоÑÑ‚Ñ– ÑкорÑ",
+DlgAnchorName : "Ім'Ñ ÑкорÑ",
+DlgAnchorErrorName : "Будь лаÑка, занеÑÑ–Ñ‚ÑŒ ім'Ñ ÑкорÑ",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Ðе має в Ñловнику",
+DlgSpellChangeTo : "Замінити на",
+DlgSpellBtnIgnore : "Ігнорувати",
+DlgSpellBtnIgnoreAll : "Ігнорувати вÑе",
+DlgSpellBtnReplace : "Замінити",
+DlgSpellBtnReplaceAll : "Замінити вÑе",
+DlgSpellBtnUndo : "Ðазад",
+DlgSpellNoSuggestions : "- Ðемає припущень -",
+DlgSpellProgress : "ВиконуєтьÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ° орфографії...",
+DlgSpellNoMispell : "Перевірку орфографії завершено: помилок не знайдено",
+DlgSpellNoChanges : "Перевірку орфографії завершено: жодне Ñлово не змінено",
+DlgSpellOneChange : "Перевірку орфографії завершено: змінено одно Ñлово",
+DlgSpellManyChanges : "Перевірку орфографії завершено: 1% Ñлів змінено",
+
+IeSpellDownload : "Модуль перевірки орфографії не вÑтановлено. Бажаєтн завантажити його зараз?",
+
+// Button Dialog
+DlgButtonText : "ТекÑÑ‚ (ЗначеннÑ)",
+DlgButtonType : "Тип",
+DlgButtonTypeBtn : "Кнопка",
+DlgButtonTypeSbm : "Відправити",
+DlgButtonTypeRst : "Скинути",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Ім'Ñ",
+DlgCheckboxValue : "ЗначеннÑ",
+DlgCheckboxSelected : "Обрана",
+
+// Form Dialog
+DlgFormName : "Ім'Ñ",
+DlgFormAction : "ДіÑ",
+DlgFormMethod : "Метод",
+
+// Select Field Dialog
+DlgSelectName : "Ім'Ñ",
+DlgSelectValue : "ЗначеннÑ",
+DlgSelectSize : "Розмір",
+DlgSelectLines : "лінії",
+DlgSelectChkMulti : "Дозволити Ð¾Ð±Ñ€Ð°Ð½Ð½Ñ Ð´ÐµÐºÑ–Ð»ÑŒÐºÐ¾Ñ… позицій",
+DlgSelectOpAvail : "ДоÑтупні варіанти",
+DlgSelectOpText : "ТекÑÑ‚",
+DlgSelectOpValue : "ЗначеннÑ",
+DlgSelectBtnAdd : "Добавити",
+DlgSelectBtnModify : "Змінити",
+DlgSelectBtnUp : "Вгору",
+DlgSelectBtnDown : "Вниз",
+DlgSelectBtnSetValue : "Ð’Ñтановити Ñк вибране значеннÑ",
+DlgSelectBtnDelete : "Видалити",
+
+// Textarea Dialog
+DlgTextareaName : "Ім'Ñ",
+DlgTextareaCols : "Колонки",
+DlgTextareaRows : "Строки",
+
+// Text Field Dialog
+DlgTextName : "Ім'Ñ",
+DlgTextValue : "ЗначеннÑ",
+DlgTextCharWidth : "Ширина",
+DlgTextMaxChars : "МакÑ. кіл-Ñ‚ÑŒ Ñимволів",
+DlgTextType : "Тип",
+DlgTextTypeText : "ТекÑÑ‚",
+DlgTextTypePass : "Пароль",
+
+// Hidden Field Dialog
+DlgHiddenName : "Ім'Ñ",
+DlgHiddenValue : "ЗначеннÑ",
+
+// Bulleted List Dialog
+BulletedListProp : "ВлаÑтивоÑÑ‚Ñ– маркованого ÑпиÑка",
+NumberedListProp : "ВлаÑтивоÑÑ‚Ñ– нумерованного ÑпиÑка",
+DlgLstStart : "Початок",
+DlgLstType : "Тип",
+DlgLstTypeCircle : "Коло",
+DlgLstTypeDisc : "ДиÑк",
+DlgLstTypeSquare : "Квадрат",
+DlgLstTypeNumbers : "Ðомери (1, 2, 3)",
+DlgLstTypeLCase : "Літери нижнього регіÑтра(a, b, c)",
+DlgLstTypeUCase : "Букви верхнього регіÑтра (A, B, C)",
+DlgLstTypeSRoman : "Малі римÑькі літери (i, ii, iii)",
+DlgLstTypeLRoman : "Великі римÑькі літери (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Загальні",
+DlgDocBackTab : "Заднє тло",
+DlgDocColorsTab : "Кольори та відÑтупи",
+DlgDocMetaTab : "Мета дані",
+
+DlgDocPageTitle : "Заголовок Ñторінки",
+DlgDocLangDir : "ÐапрÑмок текÑту",
+DlgDocLangDirLTR : "Зліва на право (LTR)",
+DlgDocLangDirRTL : "Зправа на лево (RTL)",
+DlgDocLangCode : "Код мови",
+DlgDocCharSet : "ÐšÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð°Ð±Ð¾Ñ€Ñƒ Ñимволів",
+DlgDocCharSetCE : "Центрально-європейÑька",
+DlgDocCharSetCT : "КитайÑька традиційна (Big5)",
+DlgDocCharSetCR : "КирилицÑ",
+DlgDocCharSetGR : "Грецька",
+DlgDocCharSetJP : "ЯпонÑька",
+DlgDocCharSetKR : "КорейÑька",
+DlgDocCharSetTR : "Турецька",
+DlgDocCharSetUN : "Юнікод (UTF-8)",
+DlgDocCharSetWE : "Західно-европейÑкаÑ",
+DlgDocCharSetOther : "Інше ÐºÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð°Ð±Ð¾Ñ€Ñƒ Ñимволів",
+
+DlgDocDocType : "Заголовок типу документу",
+DlgDocDocTypeOther : "Інший заголовок типу документу",
+DlgDocIncXHTML : "Ввімкнути XHTML оголошеннÑ",
+DlgDocBgColor : "Колір тла",
+DlgDocBgImage : "URL Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð»Ð°",
+DlgDocBgNoScroll : "Тло без прокрутки",
+DlgDocCText : "ТекÑÑ‚",
+DlgDocCLink : "ПоÑиланнÑ",
+DlgDocCVisited : "Відвідане поÑиланнÑ",
+DlgDocCActive : "Ðктивне поÑиланнÑ",
+DlgDocMargins : "ВідÑтупи Ñторінки",
+DlgDocMaTop : "Верхній",
+DlgDocMaLeft : "Лівий",
+DlgDocMaRight : "Правий",
+DlgDocMaBottom : "Ðижній",
+DlgDocMeIndex : "Ключові Ñлова документа (розділені комами)",
+DlgDocMeDescr : "ÐžÐ¿Ð¸Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°",
+DlgDocMeAuthor : "Ðвтор",
+DlgDocMeCopy : "ÐвторÑькі права",
+DlgDocPreview : "Попередній переглÑд",
+
+// Templates Dialog
+Templates : "Шаблони",
+DlgTemplatesTitle : "Шаблони зміÑту",
+DlgTemplatesSelMsg : "Оберіть, будь лаÑка, шаблон Ð´Ð»Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð² редакторі<br>(поточний зміÑÑ‚ буде втрачено):",
+DlgTemplatesLoading : "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÑпиÑку шаблонів. Зачекайте, будь лаÑка...",
+DlgTemplatesNoTpl : "(Ðе визначено жодного шаблону)",
+DlgTemplatesReplace : "Замінити поточний вміÑÑ‚",
+
+// About Dialog
+DlgAboutAboutTab : "Про програму",
+DlgAboutBrowserInfoTab : "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð°",
+DlgAboutLicenseTab : "ЛіцензіÑ",
+DlgAboutVersion : "ВерÑÑ–Ñ",
+DlgAboutInfo : "Додаткову інформацію дивітьÑÑ Ð½Ð° "
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/vi.js b/httemplate/elements/fckeditor/editor/lang/vi.js
new file mode 100644
index 0000000..5c2c608
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/vi.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Vietnamese language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "Thu gá»n Thanh công cụ",
+ToolbarExpand : "Mở rộng Thanh công cụ",
+
+// Toolbar Items and Context Menu
+Save : "LÆ°u",
+NewPage : "Trang má»›i",
+Preview : "Xem trÆ°á»›c",
+Cut : "Cắt",
+Copy : "Sao chép",
+Paste : "Dán",
+PasteText : "Dán theo dạng văn bản thuần",
+PasteWord : "Dán với định dạng Word",
+Print : "In",
+SelectAll : "Chá»n Tất cả",
+RemoveFormat : "Xoá Äịnh dạng",
+InsertLinkLbl : "Liên kết",
+InsertLink : "Chèn/Sửa Liên kết",
+RemoveLink : "Xoá Liên kết",
+Anchor : "Chèn/Sửa Neo",
+InsertImageLbl : "Hình ảnh",
+InsertImage : "Chèn/Sửa Hình ảnh",
+InsertFlashLbl : "Flash",
+InsertFlash : "Chèn/Sửa Flash",
+InsertTableLbl : "Bảng",
+InsertTable : "Chèn/Sửa Bảng",
+InsertLineLbl : "ÄÆ°á»ng phân cách ngang",
+InsertLine : "Chèn ÄÆ°á»ng phân cách ngang",
+InsertSpecialCharLbl: "Ký tự đặc biệt",
+InsertSpecialChar : "Chèn Ký tự đặc biệt",
+InsertSmileyLbl : "Hình biểu lá»™ cảm xúc (mặt cÆ°á»i)",
+InsertSmiley : "Chèn Hình biểu lá»™ cảm xúc (mặt cÆ°á»i)",
+About : "Giới thiệu vỠFCKeditor",
+Bold : "Äậm",
+Italic : "Nghiêng",
+Underline : "Gạch chân",
+StrikeThrough : "Gạch xuyên ngang",
+Subscript : "Chỉ số dưới",
+Superscript : "Chỉ số trên",
+LeftJustify : "Canh trái",
+CenterJustify : "Canh giữa",
+RightJustify : "Canh phải",
+BlockJustify : "Canh Ä‘á»u",
+DecreaseIndent : "Dịch ra ngoài",
+IncreaseIndent : "Dịch vào trong",
+Undo : "Khôi phục thao tác",
+Redo : "Làm lại thao tác",
+NumberedListLbl : "Danh sách có thứ tự",
+NumberedList : "Chèn/Xoá Danh sách có thứ tự",
+BulletedListLbl : "Danh sách không thứ tự",
+BulletedList : "Chèn/Xoá Danh sách không thứ tự",
+ShowTableBorders : "Hiển thị ÄÆ°á»ng viá»n bảng",
+ShowDetails : "Hiển thị Chi tiết",
+Style : "Mẫu",
+FontFormat : "Äịnh dạng",
+Font : "Phông",
+FontSize : "Cỡ chữ",
+TextColor : "Màu chữ",
+BGColor : "Màu ná»n",
+Source : "Mã HTML",
+Find : "Tìm kiếm",
+Replace : "Thay thế",
+SpellCheck : "Kiểm tra Chính tả",
+UniversalKeyboard : "Bàn phím Quốc tế",
+PageBreakLbl : "Ngắt trang",
+PageBreak : "Chèn Ngắt trang",
+
+Form : "Biểu mẫu",
+Checkbox : "Nút kiểm",
+RadioButton : "Nút chá»n",
+TextField : "TrÆ°á»ng văn bản",
+Textarea : "Vùng văn bản",
+HiddenField : "TrÆ°á»ng ẩn",
+Button : "Nút",
+SelectionField : "Ô chá»n",
+ImageButton : "Nút hình ảnh",
+
+FitWindow : "Mở rộng tối đa kích thước trình biên tập",
+
+// Context Menu
+EditLink : "Sửa Liên kết",
+CellCM : "Ô",
+RowCM : "Hàng",
+ColumnCM : "Cá»™t",
+InsertRow : "Chèn Hàng",
+DeleteRows : "Xoá Hàng",
+InsertColumn : "Chèn Cột",
+DeleteColumns : "Xoá Cột",
+InsertCell : "Chèn Ô",
+DeleteCells : "Xoá Ô",
+MergeCells : "Trộn Ô",
+SplitCell : "Chia Ô",
+TableDelete : "Xóa Bảng",
+CellProperties : "Thuộc tính Ô",
+TableProperties : "Thuộc tính Bảng",
+ImageProperties : "Thuộc tính Hình ảnh",
+FlashProperties : "Thuộc tính Flash",
+
+AnchorProp : "Thuộc tính Neo",
+ButtonProp : "Thuộc tính Nút",
+CheckboxProp : "Thuộc tính Nút kiểm",
+HiddenFieldProp : "Thuá»™c tính TrÆ°á»ng ẩn",
+RadioButtonProp : "Thuá»™c tính Nút chá»n",
+ImageButtonProp : "Thuộc tính Nút hình ảnh",
+TextFieldProp : "Thuá»™c tính TrÆ°á»ng văn bản",
+SelectionFieldProp : "Thuá»™c tính Ô chá»n",
+TextareaProp : "Thuộc tính Vùng văn bản",
+FormProp : "Thuộc tính Biểu mẫu",
+
+FontFormats : "Normal;Formatted;Address;Heading 1;Heading 2;Heading 3;Heading 4;Heading 5;Heading 6;Normal (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "Äang xá»­ lý XHTML. Vui lòng đợi trong giây lát...",
+Done : "Äã hoàn thành",
+PasteWordConfirm : "Văn bản bạn muốn dán có kèm định dạng của Word. Bạn có muốn loại bỠđịnh dạng Word trước khi dán?",
+NotCompatiblePaste : "Lệnh này chỉ được hỗ trợ từ trình duyệt Internet Explorer phiên bản 5.5 hoặc mới hơn. Bạn có muốn dán nguyên mẫu?",
+UnknownToolbarItem : "Không rõ mục trên thanh công cụ \"%1\"",
+UnknownCommand : "Không rõ lệnh \"%1\"",
+NotImplemented : "Lệnh không được thực hiện",
+UnknownToolbarSet : "Thanh công cụ \"%1\" không tồn tại",
+NoActiveX : "Các thiết lập bảo mật của trình duyệt có thể giá»›i hạn má»™t số chức năng của trình biên tập. Bạn phải bật tùy chá»n \"Run ActiveX controls and plug-ins\". Bạn có thể gặp má»™t số lá»—i và thấy thiếu Ä‘i má»™t số chức năng.",
+BrowseServerBlocked : "Không thể mở được bộ duyệt tài nguyên. Hãy đảm bảo chức năng chặn popup đã bị vô hiệu hóa.",
+DialogBlocked : "Không thể mở được cửa sổ hộp thoại. Hãy đảm bảo chức năng chặn popup đã bị vô hiệu hóa.",
+
+// Dialogs
+DlgBtnOK : "Äồng ý",
+DlgBtnCancel : "Bá» qua",
+DlgBtnClose : "Äóng",
+DlgBtnBrowseServer : "Duyệt trên máy chủ",
+DlgAdvancedTag : "Mở rộng",
+DlgOpOther : "<Khác>",
+DlgInfoTab : "Thông tin",
+DlgAlertUrl : "Hãy nhập vào một URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<không thiết lập>",
+DlgGenId : "Äịnh danh",
+DlgGenLangDir : "ÄÆ°á»ng dẫn Ngôn ngữ",
+DlgGenLangDirLtr : "Trái sang Phải (LTR)",
+DlgGenLangDirRtl : "Phải sang Trái (RTL)",
+DlgGenLangCode : "Mã Ngôn ngữ",
+DlgGenAccessKey : "Phím Hỗ trợ truy cập",
+DlgGenName : "Tên",
+DlgGenTabIndex : "Chỉ số của Tab",
+DlgGenLongDescr : "Mô tả URL",
+DlgGenClass : "Lá»›p Stylesheet",
+DlgGenTitle : "Advisory Title",
+DlgGenContType : "Advisory Content Type",
+DlgGenLinkCharset : "Bảng mã của tài nguyên được liên kết đến",
+DlgGenStyle : "Mẫu",
+
+// Image Dialog
+DlgImgTitle : "Thuộc tính Hình ảnh",
+DlgImgInfoTab : "Thông tin Hình ảnh",
+DlgImgBtnUpload : "Tải lên Máy chủ",
+DlgImgURL : "URL",
+DlgImgUpload : "Tải lên",
+DlgImgAlt : "Chú thích Hình ảnh",
+DlgImgWidth : "Rá»™ng",
+DlgImgHeight : "Cao",
+DlgImgLockRatio : "Giữ tỷ lệ",
+DlgBtnResetSize : "Kích thước gốc",
+DlgImgBorder : "ÄÆ°á»ng viá»n",
+DlgImgHSpace : "HSpace",
+DlgImgVSpace : "VSpace",
+DlgImgAlign : "Vị trí",
+DlgImgAlignLeft : "Trái",
+DlgImgAlignAbsBottom: "Dưới tuyệt đối",
+DlgImgAlignAbsMiddle: "Giữa tuyệt đối",
+DlgImgAlignBaseline : "Baseline",
+DlgImgAlignBottom : "DÆ°á»›i",
+DlgImgAlignMiddle : "Giữa",
+DlgImgAlignRight : "Phải",
+DlgImgAlignTextTop : "Phía trên chữ",
+DlgImgAlignTop : "Trên",
+DlgImgPreview : "Xem trÆ°á»›c",
+DlgImgAlertUrl : "Hãy đưa vào URL của hình ảnh",
+DlgImgLinkTab : "Liên kết",
+
+// Flash Dialog
+DlgFlashTitle : "Thuộc tính Flash",
+DlgFlashChkPlay : "Tự động chạy",
+DlgFlashChkLoop : "Lặp",
+DlgFlashChkMenu : "Cho phép bật Menu của Flash",
+DlgFlashScale : "Tỷ lệ",
+DlgFlashScaleAll : "Hiển thị tất cả",
+DlgFlashScaleNoBorder : "Không Ä‘Æ°á»ng viá»n",
+DlgFlashScaleFit : "Vừa vặn",
+
+// Link Dialog
+DlgLnkWindowTitle : "Liên kết",
+DlgLnkInfoTab : "Thông tin Liên kết",
+DlgLnkTargetTab : "Äích",
+
+DlgLnkType : "Kiểu Liên kết",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "Neo trong trang này",
+DlgLnkTypeEMail : "Thư điện tử",
+DlgLnkProto : "Giao thức",
+DlgLnkProtoOther : "<khác>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "Chá»n má»™t Neo",
+DlgLnkAnchorByName : "Theo Tên Neo",
+DlgLnkAnchorById : "Theo Äịnh danh Element",
+DlgLnkNoAnchors : "<Không có Neo nào trong tài liệu>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "Thư điện tử",
+DlgLnkEMailSubject : "Tiêu đỠThông điệp",
+DlgLnkEMailBody : "Nội dung Thông điệp",
+DlgLnkUpload : "Tải lên",
+DlgLnkBtnUpload : "Tải lên Máy chủ",
+
+DlgLnkTarget : "Äích",
+DlgLnkTargetFrame : "<khung>",
+DlgLnkTargetPopup : "<cửa sổ popup>",
+DlgLnkTargetBlank : "Cửa sổ mới (_blank)",
+DlgLnkTargetParent : "Cửa sổ cha (_parent)",
+DlgLnkTargetSelf : "Cùng cửa sổ (_self)",
+DlgLnkTargetTop : "Cửa sổ trên cùng(_top)",
+DlgLnkTargetFrameName : "Tên Khung đích",
+DlgLnkPopWinName : "Tên Cửa sổ Popup",
+DlgLnkPopWinFeat : "Äặc Ä‘iểm của Cá»­a sổ Popup",
+DlgLnkPopResize : "Kích thước thay đổi",
+DlgLnkPopLocation : "Thanh vị trí",
+DlgLnkPopMenu : "Thanh Menu",
+DlgLnkPopScroll : "Thanh cuá»™n",
+DlgLnkPopStatus : "Thanh trạng thái",
+DlgLnkPopToolbar : "Thanh công cụ",
+DlgLnkPopFullScrn : "Toàn màn hình (IE)",
+DlgLnkPopDependent : "Phụ thuộc (Netscape)",
+DlgLnkPopWidth : "Rá»™ng",
+DlgLnkPopHeight : "Cao",
+DlgLnkPopLeft : "Vị trí Trái",
+DlgLnkPopTop : "Vị trí Trên",
+
+DlnLnkMsgNoUrl : "Hãy đưa vào Liên kết URL",
+DlnLnkMsgNoEMail : "Hãy đưa vào địa chỉ thư điện tử",
+DlnLnkMsgNoAnchor : "Hãy chá»n má»™t Neo",
+DlnLnkMsgInvPopName : "Tên của cửa sổ Popup phải bắt đầu bằng một ký tự và không được chứa khoảng trắng",
+
+// Color Dialog
+DlgColorTitle : "Chá»n màu",
+DlgColorBtnClear : "Xoá",
+DlgColorHighlight : "Tô sáng",
+DlgColorSelected : "Äã chá»n",
+
+// Smiley Dialog
+DlgSmileyTitle : "Chèn Hình biểu lá»™ cảm xúc (mặt cÆ°á»i)",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "Hãy chá»n Ký tá»± đặc biệt",
+
+// Table Dialog
+DlgTableTitle : "Thuộc tính bảng",
+DlgTableRows : "Hàng",
+DlgTableColumns : "Cá»™t",
+DlgTableBorder : "Cỡ ÄÆ°á»ng viá»n",
+DlgTableAlign : "Canh lá»",
+DlgTableAlignNotSet : "<Chưa thiết lập>",
+DlgTableAlignLeft : "Trái",
+DlgTableAlignCenter : "Giữa",
+DlgTableAlignRight : "Phải",
+DlgTableWidth : "Rá»™ng",
+DlgTableWidthPx : "điểm (px)",
+DlgTableWidthPc : "%",
+DlgTableHeight : "Cao",
+DlgTableCellSpace : "Khoảng cách Ô",
+DlgTableCellPad : "Äệm Ô",
+DlgTableCaption : "Äầu Ä‘á»",
+DlgTableSummary : "Tóm lược",
+
+// Table Cell Dialog
+DlgCellTitle : "Thuộc tính Ô",
+DlgCellWidth : "Rá»™ng",
+DlgCellWidthPx : "điểm (px)",
+DlgCellWidthPc : "%",
+DlgCellHeight : "Cao",
+DlgCellWordWrap : "Bá»c từ",
+DlgCellWordWrapNotSet : "<Chưa thiết lập>",
+DlgCellWordWrapYes : "Äồng ý",
+DlgCellWordWrapNo : "Không",
+DlgCellHorAlign : "Canh theo Chiá»u ngang",
+DlgCellHorAlignNotSet : "<Chưa thiết lập>",
+DlgCellHorAlignLeft : "Trái",
+DlgCellHorAlignCenter : "Giữa",
+DlgCellHorAlignRight: "Phải",
+DlgCellVerAlign : "Canh theo Chiá»u dá»c",
+DlgCellVerAlignNotSet : "<Chưa thiết lập>",
+DlgCellVerAlignTop : "Trên",
+DlgCellVerAlignMiddle : "Giữa",
+DlgCellVerAlignBottom : "DÆ°á»›i",
+DlgCellVerAlignBaseline : "Baseline",
+DlgCellRowSpan : "Nối Hàng",
+DlgCellCollSpan : "Nối Cột",
+DlgCellBackColor : "Màu ná»n",
+DlgCellBorderColor : "Màu viá»n",
+DlgCellBtnSelect : "Chá»n...",
+
+// Find Dialog
+DlgFindTitle : "Tìm kiếm",
+DlgFindFindBtn : "Tìm kiếm",
+DlgFindNotFoundMsg : "Không tìm thấy chuỗi cần tìm.",
+
+// Replace Dialog
+DlgReplaceTitle : "Thay thế",
+DlgReplaceFindLbl : "Tìm chuỗi:",
+DlgReplaceReplaceLbl : "Thay bằng:",
+DlgReplaceCaseChk : "Phân biệt chữ hoa/thÆ°á»ng",
+DlgReplaceReplaceBtn : "Thay thế",
+DlgReplaceReplAllBtn : "Thay thế Tất cả",
+DlgReplaceWordChk : "Äúng toàn bá»™ từ",
+
+// Paste Operations / Dialog
+PasteErrorCut : "Các thiết lập bảo mật của trình duyệt không cho phép trình biên tập tự động thực thi lệnh cắt. Hãy sử dụng bàn phím cho lệnh này (Ctrl+X).",
+PasteErrorCopy : "Các thiết lập bảo mật của trình duyệt không cho phép trình biên tập tự động thực thi lệnh sao chép. Hãy sử dụng bàn phím cho lệnh này (Ctrl+C).",
+
+PasteAsText : "Dán theo định dạng văn bản thuần",
+PasteFromWord : "Dán với định dạng Word",
+
+DlgPasteMsg2 : "Hãy dán ná»™i dung vào trong khung bên dÆ°á»›i, sá»­ dụng tổ hợp phím (<STRONG>Ctrl+V</STRONG>) và nhấn vào nút <STRONG>Äồng ý</STRONG>.",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "Chấp nhận các định dạng phông",
+DlgPasteRemoveStyles : "Gỡ bỠcác định dạng Styles",
+DlgPasteCleanBox : "Xóa nội dung",
+
+// Color Picker
+ColorAutomatic : "Tá»± Ä‘á»™ng",
+ColorMoreColors : "Màu khác...",
+
+// Document Properties
+DocProps : "Thuộc tính Tài liệu",
+
+// Anchor Dialog
+DlgAnchorTitle : "Thuộc tính Neo",
+DlgAnchorName : "Tên của Neo",
+DlgAnchorErrorName : "Hãy đưa vào tên của Neo",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "Không có trong từ điển",
+DlgSpellChangeTo : "Chuyển thành",
+DlgSpellBtnIgnore : "Bá» qua",
+DlgSpellBtnIgnoreAll : "BỠqua Tất cả",
+DlgSpellBtnReplace : "Thay thế",
+DlgSpellBtnReplaceAll : "Thay thế Tất cả",
+DlgSpellBtnUndo : "Phục hồi lại",
+DlgSpellNoSuggestions : "- Không đưa ra gợi ý vỠtừ -",
+DlgSpellProgress : "Äang tiến hành kiểm tra chính tả...",
+DlgSpellNoMispell : "Hoàn tất kiểm tra chính tả: Không có lỗi chính tả",
+DlgSpellNoChanges : "Hoàn tất kiểm tra chính tả: Không có từ nào được thay đổi",
+DlgSpellOneChange : "Hoàn tất kiểm tra chính tả: Một từ đã được thay đổi",
+DlgSpellManyChanges : "Hoàn tất kiểm tra chính tả: %1 từ đã được thay đổi",
+
+IeSpellDownload : "Chức năng kiểm tra chính tả chưa được cài đặt. Bạn có muốn tải vỠngay bây gi�",
+
+// Button Dialog
+DlgButtonText : "Chuỗi hiển thị (Giá trị)",
+DlgButtonType : "Kiểu",
+DlgButtonTypeBtn : "Nút Bấm",
+DlgButtonTypeSbm : "Nút Gửi",
+DlgButtonTypeRst : "Nút Nhập lại",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "Tên",
+DlgCheckboxValue : "Giá trị",
+DlgCheckboxSelected : "Äược chá»n",
+
+// Form Dialog
+DlgFormName : "Tên",
+DlgFormAction : "Hành động",
+DlgFormMethod : "Phương thức",
+
+// Select Field Dialog
+DlgSelectName : "Tên",
+DlgSelectValue : "Giá trị",
+DlgSelectSize : "Kích cỡ",
+DlgSelectLines : "dòng",
+DlgSelectChkMulti : "Cho phép chá»n nhiá»u",
+DlgSelectOpAvail : "Các tùy chá»n có thể sá»­ dụng",
+DlgSelectOpText : "Văn bản",
+DlgSelectOpValue : "Giá trị",
+DlgSelectBtnAdd : "Thêm",
+DlgSelectBtnModify : "Thay đổi",
+DlgSelectBtnUp : "Lên",
+DlgSelectBtnDown : "Xuống",
+DlgSelectBtnSetValue : "Giá trị được chá»n",
+DlgSelectBtnDelete : "Xoá",
+
+// Textarea Dialog
+DlgTextareaName : "Tên",
+DlgTextareaCols : "Cá»™t",
+DlgTextareaRows : "Hàng",
+
+// Text Field Dialog
+DlgTextName : "Tên",
+DlgTextValue : "Giá trị",
+DlgTextCharWidth : "Rá»™ng",
+DlgTextMaxChars : "Số Ký tự tối đa",
+DlgTextType : "Kiểu",
+DlgTextTypeText : "Ký tự",
+DlgTextTypePass : "Mật khẩu",
+
+// Hidden Field Dialog
+DlgHiddenName : "Tên",
+DlgHiddenValue : "Giá trị",
+
+// Bulleted List Dialog
+BulletedListProp : "Thuộc tính Danh sách không thứ tự",
+NumberedListProp : "Thuộc tính Danh sách có thứ tự",
+DlgLstStart : "Bắt đầu",
+DlgLstType : "Kiểu",
+DlgLstTypeCircle : "Hình tròn",
+DlgLstTypeDisc : "Hình đĩa",
+DlgLstTypeSquare : "Hình vuông",
+DlgLstTypeNumbers : "Số thứ tự (1, 2, 3)",
+DlgLstTypeLCase : "Chữ cái thÆ°á»ng (a, b, c)",
+DlgLstTypeUCase : "Chữ cái hoa (A, B, C)",
+DlgLstTypeSRoman : "Số La Mã thÆ°á»ng (i, ii, iii)",
+DlgLstTypeLRoman : "Số La Mã hoa (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "Toàn thể",
+DlgDocBackTab : "Ná»n",
+DlgDocColorsTab : "Màu sắc và ÄÆ°á»ng biên",
+DlgDocMetaTab : "Siêu dữ liệu",
+
+DlgDocPageTitle : "Tiêu đỠTrang",
+DlgDocLangDir : "ÄÆ°á»ng dẫn Ngôn ngữ",
+DlgDocLangDirLTR : "Trái sang Phải (LTR)",
+DlgDocLangDirRTL : "Phải sang Trái (RTL)",
+DlgDocLangCode : "Mã Ngôn ngữ",
+DlgDocCharSet : "Bảng mã ký tự",
+DlgDocCharSetCE : "Trung Âu",
+DlgDocCharSetCT : "Tiếng Trung Quốc (Big5)",
+DlgDocCharSetCR : "Tiếng Kirin",
+DlgDocCharSetGR : "Tiếng Hy Lạp",
+DlgDocCharSetJP : "Tiếng Nhật",
+DlgDocCharSetKR : "Tiếng Hàn",
+DlgDocCharSetTR : "Tiếng Thổ Nhĩ Kỳ",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "Tây Âu",
+DlgDocCharSetOther : "Bảng mã ký tự khác",
+
+DlgDocDocType : "Kiểu Äá» mục Tài liệu",
+DlgDocDocTypeOther : "Kiểu Äá» mục Tài liệu khác",
+DlgDocIncXHTML : "Bao gồm cả định nghĩa XHTML",
+DlgDocBgColor : "Màu ná»n",
+DlgDocBgImage : "URL của Hình ảnh ná»n",
+DlgDocBgNoScroll : "Không cuá»™n ná»n",
+DlgDocCText : "Văn bản",
+DlgDocCLink : "Liên kết",
+DlgDocCVisited : "Liên kết Äã ghé thăm",
+DlgDocCActive : "Liên kết Hiện hành",
+DlgDocMargins : "ÄÆ°á»ng biên của Trang",
+DlgDocMaTop : "Trên",
+DlgDocMaLeft : "Trái",
+DlgDocMaRight : "Phải",
+DlgDocMaBottom : "DÆ°á»›i",
+DlgDocMeIndex : "Các từ khóa chỉ mục tài liệu (phân cách bởi dấu phẩy)",
+DlgDocMeDescr : "Mô tả tài liệu",
+DlgDocMeAuthor : "Tác giả",
+DlgDocMeCopy : "Bản quyá»n",
+DlgDocPreview : "Xem trÆ°á»›c",
+
+// Templates Dialog
+Templates : "Mẫu dựng sẵn",
+DlgTemplatesTitle : "Nội dung Mẫu dựng sẵn",
+DlgTemplatesSelMsg : "Hãy chá»n Mẫu dá»±ng sẵn để mở trong trình biên tập<br>(ná»™i dung hiện tại sẽ bị mất):",
+DlgTemplatesLoading : "Äang nạp Danh sách Mẫu dá»±ng sẵn. Vui lòng đợi trong giây lát...",
+DlgTemplatesNoTpl : "(Không có Mẫu dựng sẵn nào được định nghĩa)",
+DlgTemplatesReplace : "Thay thế nội dung hiện tại",
+
+// About Dialog
+DlgAboutAboutTab : "Giới thiệu",
+DlgAboutBrowserInfoTab : "Thông tin trình duyệt",
+DlgAboutLicenseTab : "Giấy phép",
+DlgAboutVersion : "phiên bản",
+DlgAboutInfo : "Äể biết thêm thông tin, hãy truy cập"
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/zh-cn.js b/httemplate/elements/fckeditor/editor/lang/zh-cn.js
new file mode 100644
index 0000000..6d6f4f4
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/zh-cn.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Chinese Simplified language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "折å å·¥å…·æ ",
+ToolbarExpand : "展开工具æ ",
+
+// Toolbar Items and Context Menu
+Save : "ä¿å­˜",
+NewPage : "新建",
+Preview : "预览",
+Cut : "剪切",
+Copy : "å¤åˆ¶",
+Paste : "粘贴",
+PasteText : "粘贴为无格å¼æ–‡æœ¬",
+PasteWord : "从 MS Word 粘贴",
+Print : "打å°",
+SelectAll : "全选",
+RemoveFormat : "清除格å¼",
+InsertLinkLbl : "超链接",
+InsertLink : "æ’å…¥/编辑超链接",
+RemoveLink : "å–消超链接",
+Anchor : "æ’å…¥/编辑锚点链接",
+InsertImageLbl : "图象",
+InsertImage : "æ’å…¥/编辑图象",
+InsertFlashLbl : "Flash",
+InsertFlash : "æ’å…¥/编辑 Flash",
+InsertTableLbl : "表格",
+InsertTable : "æ’å…¥/编辑表格",
+InsertLineLbl : "水平线",
+InsertLine : "æ’入水平线",
+InsertSpecialCharLbl: "特殊符å·",
+InsertSpecialChar : "æ’入特殊符å·",
+InsertSmileyLbl : "表情符",
+InsertSmiley : "æ’入表情图标",
+About : "关于 FCKeditor",
+Bold : "加粗",
+Italic : "倾斜",
+Underline : "下划线",
+StrikeThrough : "删除线",
+Subscript : "下标",
+Superscript : "上标",
+LeftJustify : "左对é½",
+CenterJustify : "居中对é½",
+RightJustify : "å³å¯¹é½",
+BlockJustify : "两端对é½",
+DecreaseIndent : "å‡å°‘缩进é‡",
+IncreaseIndent : "增加缩进é‡",
+Undo : "撤消",
+Redo : "é‡åš",
+NumberedListLbl : "ç¼–å·åˆ—表",
+NumberedList : "æ’å…¥/删除编å·åˆ—表",
+BulletedListLbl : "项目列表",
+BulletedList : "æ’å…¥/删除项目列表",
+ShowTableBorders : "显示表格边框",
+ShowDetails : "显示详细资料",
+Style : "æ ·å¼",
+FontFormat : "æ ¼å¼",
+Font : "字体",
+FontSize : "大å°",
+TextColor : "文本颜色",
+BGColor : "背景颜色",
+Source : "æºä»£ç ",
+Find : "查找",
+Replace : "替æ¢",
+SpellCheck : "拼写检查",
+UniversalKeyboard : "软键盘",
+PageBreakLbl : "分页符",
+PageBreak : "æ’入分页符",
+
+Form : "表å•",
+Checkbox : "å¤é€‰æ¡†",
+RadioButton : "å•é€‰æŒ‰é’®",
+TextField : "å•è¡Œæ–‡æœ¬",
+Textarea : "多行文本",
+HiddenField : "éšè—域",
+Button : "按钮",
+SelectionField : "列表/èœå•",
+ImageButton : "图åƒåŸŸ",
+
+FitWindow : "å…¨å±ç¼–辑",
+
+// Context Menu
+EditLink : "编辑超链接",
+CellCM : "å•å…ƒæ ¼",
+RowCM : "行",
+ColumnCM : "列",
+InsertRow : "æ’入行",
+DeleteRows : "删除行",
+InsertColumn : "æ’入列",
+DeleteColumns : "删除列",
+InsertCell : "æ’å…¥å•å…ƒæ ¼",
+DeleteCells : "删除å•å…ƒæ ¼",
+MergeCells : "åˆå¹¶å•å…ƒæ ¼",
+SplitCell : "拆分å•å…ƒæ ¼",
+TableDelete : "删除表格",
+CellProperties : "å•å…ƒæ ¼å±žæ€§",
+TableProperties : "表格属性",
+ImageProperties : "图象属性",
+FlashProperties : "Flash 属性",
+
+AnchorProp : "锚点链接属性",
+ButtonProp : "按钮属性",
+CheckboxProp : "å¤é€‰æ¡†å±žæ€§",
+HiddenFieldProp : "éšè—域属性",
+RadioButtonProp : "å•é€‰æŒ‰é’®å±žæ€§",
+ImageButtonProp : "图åƒåŸŸå±žæ€§",
+TextFieldProp : "å•è¡Œæ–‡æœ¬å±žæ€§",
+SelectionFieldProp : "èœå•/列表属性",
+TextareaProp : "多行文本属性",
+FormProp : "表å•å±žæ€§",
+
+FontFormats : "普通;已编排格å¼;地å€;标题 1;标题 2;标题 3;标题 4;标题 5;标题 6;段è½(DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "æ­£åœ¨å¤„ç† XHTML,请ç¨ç­‰...",
+Done : "完æˆ",
+PasteWordConfirm : "您è¦ç²˜è´´çš„内容好åƒæ˜¯æ¥è‡ª MS Word,是å¦è¦æ¸…除 MS Word æ ¼å¼åŽå†ç²˜è´´ï¼Ÿ",
+NotCompatiblePaste : "è¯¥å‘½ä»¤éœ€è¦ Internet Explorer 5.5 或更高版本的支æŒï¼Œæ˜¯å¦æŒ‰å¸¸è§„粘贴进行?",
+UnknownToolbarItem : "未知工具æ é¡¹ç›® \"%1\"",
+UnknownCommand : "未知命令å称 \"%1\"",
+NotImplemented : "命令无法执行",
+UnknownToolbarSet : "工具æ è®¾ç½® \"%1\" ä¸å­˜åœ¨",
+NoActiveX : "æµè§ˆå™¨å®‰å…¨è®¾ç½®é™åˆ¶äº†æœ¬ç¼–辑器的æŸäº›åŠŸèƒ½ã€‚您必须å¯ç”¨å®‰å…¨è®¾ç½®ä¸­çš„“è¿è¡Œ ActiveX 控件和æ’件â€ï¼Œå¦åˆ™å°†å‡ºçŽ°æŸäº›é”™è¯¯å¹¶ç¼ºå°‘功能。",
+BrowseServerBlocked : "无法打开资æºæµè§ˆå™¨ï¼Œè¯·ç¡®è®¤æ˜¯å¦å¯ç”¨äº†ç¦æ­¢å¼¹å‡ºçª—å£ã€‚",
+DialogBlocked : "无法打开对è¯æ¡†çª—å£ï¼Œè¯·ç¡®è®¤æ˜¯å¦å¯ç”¨äº†ç¦æ­¢å¼¹å‡ºçª—å£æˆ–网页对è¯æ¡†ï¼ˆIE)。",
+
+// Dialogs
+DlgBtnOK : "确定",
+DlgBtnCancel : "å–消",
+DlgBtnClose : "关闭",
+DlgBtnBrowseServer : "æµè§ˆæœåŠ¡å™¨",
+DlgAdvancedTag : "高级",
+DlgOpOther : "<其它>",
+DlgInfoTab : "ä¿¡æ¯",
+DlgAlertUrl : "请æ’å…¥ URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<没有设置>",
+DlgGenId : "ID",
+DlgGenLangDir : "语言方å‘",
+DlgGenLangDirLtr : "ä»Žå·¦åˆ°å³ (LTR)",
+DlgGenLangDirRtl : "从å³åˆ°å·¦ (RTL)",
+DlgGenLangCode : "语言代ç ",
+DlgGenAccessKey : "访问键",
+DlgGenName : "å称",
+DlgGenTabIndex : "Tab 键次åº",
+DlgGenLongDescr : "详细说明地å€",
+DlgGenClass : "æ ·å¼ç±»å称",
+DlgGenTitle : "标题",
+DlgGenContType : "内容类型",
+DlgGenLinkCharset : "字符编ç ",
+DlgGenStyle : "行内样å¼",
+
+// Image Dialog
+DlgImgTitle : "图象属性",
+DlgImgInfoTab : "图象",
+DlgImgBtnUpload : "å‘é€åˆ°æœåŠ¡å™¨ä¸Š",
+DlgImgURL : "æºæ–‡ä»¶",
+DlgImgUpload : "上传",
+DlgImgAlt : "替æ¢æ–‡æœ¬",
+DlgImgWidth : "宽度",
+DlgImgHeight : "高度",
+DlgImgLockRatio : "é”定比例",
+DlgBtnResetSize : "æ¢å¤å°ºå¯¸",
+DlgImgBorder : "边框大å°",
+DlgImgHSpace : "水平间è·",
+DlgImgVSpace : "åž‚ç›´é—´è·",
+DlgImgAlign : "对é½æ–¹å¼",
+DlgImgAlignLeft : "左对é½",
+DlgImgAlignAbsBottom: "ç»å¯¹åº•è¾¹",
+DlgImgAlignAbsMiddle: "ç»å¯¹å±…中",
+DlgImgAlignBaseline : "基线",
+DlgImgAlignBottom : "底边",
+DlgImgAlignMiddle : "居中",
+DlgImgAlignRight : "å³å¯¹é½",
+DlgImgAlignTextTop : "文本上方",
+DlgImgAlignTop : "顶端",
+DlgImgPreview : "预览",
+DlgImgAlertUrl : "请输入图象地å€",
+DlgImgLinkTab : "链接",
+
+// Flash Dialog
+DlgFlashTitle : "Flash 属性",
+DlgFlashChkPlay : "自动播放",
+DlgFlashChkLoop : "循环",
+DlgFlashChkMenu : "å¯ç”¨ Flash èœå•",
+DlgFlashScale : "缩放",
+DlgFlashScaleAll : "全部显示",
+DlgFlashScaleNoBorder : "无边框",
+DlgFlashScaleFit : "严格匹é…",
+
+// Link Dialog
+DlgLnkWindowTitle : "超链接",
+DlgLnkInfoTab : "超链接信æ¯",
+DlgLnkTargetTab : "目标",
+
+DlgLnkType : "超链接类型",
+DlgLnkTypeURL : "超链接",
+DlgLnkTypeAnchor : "页内锚点链接",
+DlgLnkTypeEMail : "电å­é‚®ä»¶",
+DlgLnkProto : "åè®®",
+DlgLnkProtoOther : "<其它>",
+DlgLnkURL : "地å€",
+DlgLnkAnchorSel : "选择一个锚点",
+DlgLnkAnchorByName : "按锚点å称",
+DlgLnkAnchorById : "按锚点 ID",
+DlgLnkNoAnchors : "<此文档没有å¯ç”¨çš„锚点>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "地å€",
+DlgLnkEMailSubject : "主题",
+DlgLnkEMailBody : "内容",
+DlgLnkUpload : "上传",
+DlgLnkBtnUpload : "å‘é€åˆ°æœåŠ¡å™¨ä¸Š",
+
+DlgLnkTarget : "目标",
+DlgLnkTargetFrame : "<框架>",
+DlgLnkTargetPopup : "<弹出窗å£>",
+DlgLnkTargetBlank : "æ–°çª—å£ (_blank)",
+DlgLnkTargetParent : "çˆ¶çª—å£ (_parent)",
+DlgLnkTargetSelf : "æœ¬çª—å£ (_self)",
+DlgLnkTargetTop : "整页 (_top)",
+DlgLnkTargetFrameName : "目标框架å称",
+DlgLnkPopWinName : "弹出窗å£å称",
+DlgLnkPopWinFeat : "弹出窗å£å±žæ€§",
+DlgLnkPopResize : "调整大å°",
+DlgLnkPopLocation : "地å€æ ",
+DlgLnkPopMenu : "èœå•æ ",
+DlgLnkPopScroll : "滚动æ¡",
+DlgLnkPopStatus : "状æ€æ ",
+DlgLnkPopToolbar : "工具æ ",
+DlgLnkPopFullScrn : "å…¨å± (IE)",
+DlgLnkPopDependent : "ä¾é™„ (NS)",
+DlgLnkPopWidth : "宽",
+DlgLnkPopHeight : "高",
+DlgLnkPopLeft : "å·¦",
+DlgLnkPopTop : "å³",
+
+DlnLnkMsgNoUrl : "请输入超链接地å€",
+DlnLnkMsgNoEMail : "请输入电å­é‚®ä»¶åœ°å€",
+DlnLnkMsgNoAnchor : "请选择一个锚点",
+DlnLnkMsgInvPopName : "弹出窗å£å称必须以字æ¯å¼€å¤´ï¼Œå¹¶ä¸”ä¸èƒ½å«æœ‰ç©ºæ ¼ã€‚",
+
+// Color Dialog
+DlgColorTitle : "选择颜色",
+DlgColorBtnClear : "清除",
+DlgColorHighlight : "预览",
+DlgColorSelected : "选择",
+
+// Smiley Dialog
+DlgSmileyTitle : "æ’入表情图标",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "选择特殊符å·",
+
+// Table Dialog
+DlgTableTitle : "表格属性",
+DlgTableRows : "行数",
+DlgTableColumns : "列数",
+DlgTableBorder : "边框",
+DlgTableAlign : "对é½",
+DlgTableAlignNotSet : "<没有设置>",
+DlgTableAlignLeft : "左对é½",
+DlgTableAlignCenter : "居中",
+DlgTableAlignRight : "å³å¯¹é½",
+DlgTableWidth : "宽度",
+DlgTableWidthPx : "åƒç´ ",
+DlgTableWidthPc : "百分比",
+DlgTableHeight : "高度",
+DlgTableCellSpace : "é—´è·",
+DlgTableCellPad : "è¾¹è·",
+DlgTableCaption : "标题",
+DlgTableSummary : "摘è¦",
+
+// Table Cell Dialog
+DlgCellTitle : "å•å…ƒæ ¼å±žæ€§",
+DlgCellWidth : "宽度",
+DlgCellWidthPx : "åƒç´ ",
+DlgCellWidthPc : "百分比",
+DlgCellHeight : "高度",
+DlgCellWordWrap : "自动æ¢è¡Œ",
+DlgCellWordWrapNotSet : "<没有设置>",
+DlgCellWordWrapYes : "是",
+DlgCellWordWrapNo : "å¦",
+DlgCellHorAlign : "水平对é½",
+DlgCellHorAlignNotSet : "<没有设置>",
+DlgCellHorAlignLeft : "左对é½",
+DlgCellHorAlignCenter : "居中",
+DlgCellHorAlignRight: "å³å¯¹é½",
+DlgCellVerAlign : "垂直对é½",
+DlgCellVerAlignNotSet : "<没有设置>",
+DlgCellVerAlignTop : "顶端",
+DlgCellVerAlignMiddle : "居中",
+DlgCellVerAlignBottom : "底部",
+DlgCellVerAlignBaseline : "基线",
+DlgCellRowSpan : "纵跨行数",
+DlgCellCollSpan : "横跨列数",
+DlgCellBackColor : "背景颜色",
+DlgCellBorderColor : "边框颜色",
+DlgCellBtnSelect : "选择...",
+
+// Find Dialog
+DlgFindTitle : "查找",
+DlgFindFindBtn : "查找",
+DlgFindNotFoundMsg : "指定文本没有找到。",
+
+// Replace Dialog
+DlgReplaceTitle : "替æ¢",
+DlgReplaceFindLbl : "查找:",
+DlgReplaceReplaceLbl : "替æ¢:",
+DlgReplaceCaseChk : "区分大å°å†™",
+DlgReplaceReplaceBtn : "替æ¢",
+DlgReplaceReplAllBtn : "全部替æ¢",
+DlgReplaceWordChk : "全字匹é…",
+
+// Paste Operations / Dialog
+PasteErrorCut : "您的æµè§ˆå™¨å®‰å…¨è®¾ç½®ä¸å…许编辑器自动执行剪切æ“作,请使用键盘快æ·é”®(Ctrl+X)æ¥å®Œæˆã€‚",
+PasteErrorCopy : "您的æµè§ˆå™¨å®‰å…¨è®¾ç½®ä¸å…许编辑器自动执行å¤åˆ¶æ“作,请使用键盘快æ·é”®(Ctrl+C)æ¥å®Œæˆã€‚",
+
+PasteAsText : "粘贴为无格å¼æ–‡æœ¬",
+PasteFromWord : "从 MS Word 粘贴",
+
+DlgPasteMsg2 : "请使用键盘快æ·é”®(<STRONG>Ctrl+V</STRONG>)把内容粘贴到下é¢çš„方框里,å†æŒ‰ <STRONG>确定</STRONG>。",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "忽略 Font 标签",
+DlgPasteRemoveStyles : "æ¸…ç† CSS æ ·å¼",
+DlgPasteCleanBox : "清空上é¢å†…容",
+
+// Color Picker
+ColorAutomatic : "自动",
+ColorMoreColors : "其它颜色...",
+
+// Document Properties
+DocProps : "页é¢å±žæ€§",
+
+// Anchor Dialog
+DlgAnchorTitle : "命å锚点",
+DlgAnchorName : "锚点å称",
+DlgAnchorErrorName : "请输入锚点å称",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "没有在字典里",
+DlgSpellChangeTo : "更改为",
+DlgSpellBtnIgnore : "忽略",
+DlgSpellBtnIgnoreAll : "全部忽略",
+DlgSpellBtnReplace : "替æ¢",
+DlgSpellBtnReplaceAll : "全部替æ¢",
+DlgSpellBtnUndo : "撤消",
+DlgSpellNoSuggestions : "- 没有建议 -",
+DlgSpellProgress : "正在进行拼写检查...",
+DlgSpellNoMispell : "拼写检查完æˆï¼šæ²¡æœ‰å‘现拼写错误",
+DlgSpellNoChanges : "拼写检查完æˆï¼šæ²¡æœ‰æ›´æ”¹ä»»ä½•å•è¯",
+DlgSpellOneChange : "拼写检查完æˆï¼šæ›´æ”¹äº†ä¸€ä¸ªå•è¯",
+DlgSpellManyChanges : "拼写检查完æˆï¼šæ›´æ”¹äº† %1 个å•è¯",
+
+IeSpellDownload : "拼写检查æ’件还没安装,你是å¦æƒ³çŽ°åœ¨å°±ä¸‹è½½ï¼Ÿ",
+
+// Button Dialog
+DlgButtonText : "标签(值)",
+DlgButtonType : "类型",
+DlgButtonTypeBtn : "按钮",
+DlgButtonTypeSbm : "æ交",
+DlgButtonTypeRst : "é‡è®¾",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "å称",
+DlgCheckboxValue : "选定值",
+DlgCheckboxSelected : "已勾选",
+
+// Form Dialog
+DlgFormName : "å称",
+DlgFormAction : "动作",
+DlgFormMethod : "方法",
+
+// Select Field Dialog
+DlgSelectName : "å称",
+DlgSelectValue : "选定",
+DlgSelectSize : "高度",
+DlgSelectLines : "行",
+DlgSelectChkMulti : "å…许多选",
+DlgSelectOpAvail : "列表值",
+DlgSelectOpText : "标签",
+DlgSelectOpValue : "值",
+DlgSelectBtnAdd : "新增",
+DlgSelectBtnModify : "修改",
+DlgSelectBtnUp : "上移",
+DlgSelectBtnDown : "下移",
+DlgSelectBtnSetValue : "设为åˆå§‹åŒ–时选定",
+DlgSelectBtnDelete : "删除",
+
+// Textarea Dialog
+DlgTextareaName : "å称",
+DlgTextareaCols : "字符宽度",
+DlgTextareaRows : "行数",
+
+// Text Field Dialog
+DlgTextName : "å称",
+DlgTextValue : "åˆå§‹å€¼",
+DlgTextCharWidth : "字符宽度",
+DlgTextMaxChars : "最多字符数",
+DlgTextType : "类型",
+DlgTextTypeText : "文本",
+DlgTextTypePass : "密ç ",
+
+// Hidden Field Dialog
+DlgHiddenName : "å称",
+DlgHiddenValue : "åˆå§‹å€¼",
+
+// Bulleted List Dialog
+BulletedListProp : "项目列表属性",
+NumberedListProp : "ç¼–å·åˆ—表属性",
+DlgLstStart : "开始åºå·",
+DlgLstType : "列表类型",
+DlgLstTypeCircle : "圆圈",
+DlgLstTypeDisc : "圆点",
+DlgLstTypeSquare : "æ–¹å—",
+DlgLstTypeNumbers : "æ•°å­— (1, 2, 3)",
+DlgLstTypeLCase : "å°å†™å­—æ¯ (a, b, c)",
+DlgLstTypeUCase : "å¤§å†™å­—æ¯ (A, B, C)",
+DlgLstTypeSRoman : "å°å†™ç½—马数字 (i, ii, iii)",
+DlgLstTypeLRoman : "大写罗马数字 (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "常规",
+DlgDocBackTab : "背景",
+DlgDocColorsTab : "颜色和边è·",
+DlgDocMetaTab : "Meta æ•°æ®",
+
+DlgDocPageTitle : "页é¢æ ‡é¢˜",
+DlgDocLangDir : "语言方å‘",
+DlgDocLangDirLTR : "ä»Žå·¦åˆ°å³ (LTR)",
+DlgDocLangDirRTL : "从å³åˆ°å·¦ (RTL)",
+DlgDocLangCode : "语言代ç ",
+DlgDocCharSet : "字符编ç ",
+DlgDocCharSetCE : "中欧",
+DlgDocCharSetCT : "ç¹ä½“中文 (Big5)",
+DlgDocCharSetCR : "西里尔文",
+DlgDocCharSetGR : "希腊文",
+DlgDocCharSetJP : "日文",
+DlgDocCharSetKR : "韩文",
+DlgDocCharSetTR : "土耳其文",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "西欧",
+DlgDocCharSetOther : "其它字符编ç ",
+
+DlgDocDocType : "文档类型",
+DlgDocDocTypeOther : "其它文档类型",
+DlgDocIncXHTML : "åŒ…å« XHTML 声明",
+DlgDocBgColor : "背景颜色",
+DlgDocBgImage : "背景图åƒ",
+DlgDocBgNoScroll : "ä¸æ»šåŠ¨èƒŒæ™¯å›¾åƒ",
+DlgDocCText : "文本",
+DlgDocCLink : "超链接",
+DlgDocCVisited : "已访问的超链接",
+DlgDocCActive : "活动超链接",
+DlgDocMargins : "页é¢è¾¹è·",
+DlgDocMaTop : "上",
+DlgDocMaLeft : "å·¦",
+DlgDocMaRight : "å³",
+DlgDocMaBottom : "下",
+DlgDocMeIndex : "页é¢ç´¢å¼•å…³é”®å­— (用åŠè§’逗å·[,]分隔)",
+DlgDocMeDescr : "页é¢è¯´æ˜Ž",
+DlgDocMeAuthor : "作者",
+DlgDocMeCopy : "版æƒ",
+DlgDocPreview : "预览",
+
+// Templates Dialog
+Templates : "模æ¿",
+DlgTemplatesTitle : "内容模æ¿",
+DlgTemplatesSelMsg : "请选择编辑器内容模æ¿<br>(当å‰å†…容将会被清除替æ¢):",
+DlgTemplatesLoading : "正在加载模æ¿åˆ—表,请ç¨ç­‰...",
+DlgTemplatesNoTpl : "(没有模æ¿)",
+DlgTemplatesReplace : "替æ¢å½“å‰å†…容",
+
+// About Dialog
+DlgAboutAboutTab : "关于",
+DlgAboutBrowserInfoTab : "æµè§ˆå™¨ä¿¡æ¯",
+DlgAboutLicenseTab : "许å¯è¯",
+DlgAboutVersion : "版本",
+DlgAboutInfo : "è¦èŽ·å¾—更多信æ¯è¯·è®¿é—® "
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/lang/zh.js b/httemplate/elements/fckeditor/editor/lang/zh.js
new file mode 100644
index 0000000..b5cd239
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/lang/zh.js
@@ -0,0 +1,504 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Chinese Traditional language file.
+ */
+
+var FCKLang =
+{
+// Language direction : "ltr" (left to right) or "rtl" (right to left).
+Dir : "ltr",
+
+ToolbarCollapse : "éš±è—é¢æ¿",
+ToolbarExpand : "顯示é¢æ¿",
+
+// Toolbar Items and Context Menu
+Save : "儲存",
+NewPage : "開新檔案",
+Preview : "é è¦½",
+Cut : "剪下",
+Copy : "複製",
+Paste : "貼上",
+PasteText : "貼為純文字格å¼",
+PasteWord : "自 Word 貼上",
+Print : "列å°",
+SelectAll : "å…¨é¸",
+RemoveFormat : "清除格å¼",
+InsertLinkLbl : "超連çµ",
+InsertLink : "æ’å…¥/編輯超連çµ",
+RemoveLink : "移除超連çµ",
+Anchor : "æ’å…¥/編輯錨點",
+InsertImageLbl : "å½±åƒ",
+InsertImage : "æ’å…¥/編輯影åƒ",
+InsertFlashLbl : "Flash",
+InsertFlash : "æ’å…¥/編輯 Flash",
+InsertTableLbl : "表格",
+InsertTable : "æ’å…¥/編輯表格",
+InsertLineLbl : "水平線",
+InsertLine : "æ’入水平線",
+InsertSpecialCharLbl: "特殊符號",
+InsertSpecialChar : "æ’入特殊符號",
+InsertSmileyLbl : "表情符號",
+InsertSmiley : "æ’入表情符號",
+About : "關於 FCKeditor",
+Bold : "ç²—é«”",
+Italic : "斜體",
+Underline : "底線",
+StrikeThrough : "刪除線",
+Subscript : "下標",
+Superscript : "上標",
+LeftJustify : "é å·¦å°é½Š",
+CenterJustify : "置中",
+RightJustify : "é å³å°é½Š",
+BlockJustify : "å·¦å³å°é½Š",
+DecreaseIndent : "減少縮排",
+IncreaseIndent : "增加縮排",
+Undo : "復原",
+Redo : "é‡è¤‡",
+NumberedListLbl : "編號清單",
+NumberedList : "æ’å…¥/移除編號清單",
+BulletedListLbl : "項目清單",
+BulletedList : "æ’å…¥/移除項目清單",
+ShowTableBorders : "顯示表格邊框",
+ShowDetails : "顯示詳細資料",
+Style : "樣å¼",
+FontFormat : "æ ¼å¼",
+Font : "å­—é«”",
+FontSize : "大å°",
+TextColor : "文字é¡è‰²",
+BGColor : "背景é¡è‰²",
+Source : "原始碼",
+Find : "尋找",
+Replace : "å–代",
+SpellCheck : "拼字檢查",
+UniversalKeyboard : "è¬åœ‹éµç›¤",
+PageBreakLbl : "分é ç¬¦è™Ÿ",
+PageBreak : "æ’入分é ç¬¦è™Ÿ",
+
+Form : "表單",
+Checkbox : "æ ¸å–方塊",
+RadioButton : "é¸é …按鈕",
+TextField : "文字方塊",
+Textarea : "文字å€åŸŸ",
+HiddenField : "éš±è—欄ä½",
+Button : "按鈕",
+SelectionField : "清單/é¸å–®",
+ImageButton : "å½±åƒæŒ‰éˆ•",
+
+FitWindow : "編輯器最大化",
+
+// Context Menu
+EditLink : "編輯超連çµ",
+CellCM : "儲存格",
+RowCM : "列",
+ColumnCM : "欄",
+InsertRow : "æ’入列",
+DeleteRows : "刪除列",
+InsertColumn : "æ’入欄",
+DeleteColumns : "刪除欄",
+InsertCell : "æ’入儲存格",
+DeleteCells : "刪除儲存格",
+MergeCells : "åˆä½µå„²å­˜æ ¼",
+SplitCell : "分割儲存格",
+TableDelete : "刪除表格",
+CellProperties : "儲存格屬性",
+TableProperties : "表格屬性",
+ImageProperties : "å½±åƒå±¬æ€§",
+FlashProperties : "Flash 屬性",
+
+AnchorProp : "錨點屬性",
+ButtonProp : "按鈕屬性",
+CheckboxProp : "æ ¸å–方塊屬性",
+HiddenFieldProp : "éš±è—欄ä½å±¬æ€§",
+RadioButtonProp : "é¸é …按鈕屬性",
+ImageButtonProp : "å½±åƒæŒ‰éˆ•å±¬æ€§",
+TextFieldProp : "文字方塊屬性",
+SelectionFieldProp : "清單/é¸å–®å±¬æ€§",
+TextareaProp : "文字å€åŸŸå±¬æ€§",
+FormProp : "表單屬性",
+
+FontFormats : "本文;已格å¼åŒ–;ä½å€;標題 1;標題 2;標題 3;標題 4;標題 5;標題 6;本文 (DIV)", //REVIEW : Check _getfontformat.html
+
+// Alerts and Messages
+ProcessingXHTML : "è™•ç† XHTML 中,請ç¨å€™â€¦",
+Done : "完æˆ",
+PasteWordConfirm : "您想貼上的文字似乎是自 Word 複製而來,請å•æ‚¨æ˜¯å¦è¦å…ˆæ¸…除 Word çš„æ ¼å¼å¾Œå†è¡Œè²¼ä¸Šï¼Ÿ",
+NotCompatiblePaste : "此指令僅在 Internet Explorer 5.5 或以上的版本有效。請å•æ‚¨æ˜¯å¦åŒæ„ä¸æ¸…除格å¼å³è²¼ä¸Šï¼Ÿ",
+UnknownToolbarItem : "未知工具列項目 \"%1\"",
+UnknownCommand : "未知指令å稱 \"%1\"",
+NotImplemented : "尚未安è£æ­¤æŒ‡ä»¤",
+UnknownToolbarSet : "工具列設定 \"%1\" ä¸å­˜åœ¨",
+NoActiveX : "ç€è¦½å™¨çš„安全性設定é™åˆ¶äº†æœ¬ç·¨è¼¯å™¨çš„æŸäº›åŠŸèƒ½ã€‚您必須啟用安全性設定中的「執行ActiveX控制項與外掛程å¼ã€é …目,å¦å‰‡æœ¬ç·¨è¼¯å™¨å°‡æœƒå‡ºç¾éŒ¯èª¤ä¸¦ç¼ºå°‘æŸäº›åŠŸèƒ½",
+BrowseServerBlocked : "無法開啟資æºç€è¦½å™¨ï¼Œè«‹ç¢ºå®šæ‰€æœ‰å¿«é¡¯è¦–窗å°éŽ–程å¼æ˜¯å¦é—œé–‰",
+DialogBlocked : "無法開啟å°è©±è¦–窗,請確定所有快顯視窗å°éŽ–程å¼æ˜¯å¦é—œé–‰",
+
+// Dialogs
+DlgBtnOK : "確定",
+DlgBtnCancel : "å–消",
+DlgBtnClose : "關閉",
+DlgBtnBrowseServer : "ç€è¦½ä¼ºæœå™¨ç«¯",
+DlgAdvancedTag : "進階",
+DlgOpOther : "<其他>",
+DlgInfoTab : "資訊",
+DlgAlertUrl : "è«‹æ’å…¥ URL",
+
+// General Dialogs Labels
+DlgGenNotSet : "<尚未設定>",
+DlgGenId : "ID",
+DlgGenLangDir : "語言方å‘",
+DlgGenLangDirLtr : "ç”±å·¦è€Œå³ (LTR)",
+DlgGenLangDirRtl : "ç”±å³è€Œå·¦ (RTL)",
+DlgGenLangCode : "語言代碼",
+DlgGenAccessKey : "å­˜å–éµ",
+DlgGenName : "å稱",
+DlgGenTabIndex : "定ä½é †åº",
+DlgGenLongDescr : "詳細 URL",
+DlgGenClass : "樣å¼è¡¨é¡žåˆ¥",
+DlgGenTitle : "標題",
+DlgGenContType : "內容類型",
+DlgGenLinkCharset : "連çµè³‡æºä¹‹ç·¨ç¢¼",
+DlgGenStyle : "樣å¼",
+
+// Image Dialog
+DlgImgTitle : "å½±åƒå±¬æ€§",
+DlgImgInfoTab : "å½±åƒè³‡è¨Š",
+DlgImgBtnUpload : "上傳至伺æœå™¨",
+DlgImgURL : "URL",
+DlgImgUpload : "上傳",
+DlgImgAlt : "替代文字",
+DlgImgWidth : "寬度",
+DlgImgHeight : "高度",
+DlgImgLockRatio : "等比例",
+DlgBtnResetSize : "é‡è¨­ç‚ºåŽŸå¤§å°",
+DlgImgBorder : "邊框",
+DlgImgHSpace : "æ°´å¹³è·é›¢",
+DlgImgVSpace : "åž‚ç›´è·é›¢",
+DlgImgAlign : "å°é½Š",
+DlgImgAlignLeft : "é å·¦å°é½Š",
+DlgImgAlignAbsBottom: "絕å°ä¸‹æ–¹",
+DlgImgAlignAbsMiddle: "絕å°ä¸­é–“",
+DlgImgAlignBaseline : "基準線",
+DlgImgAlignBottom : "é ä¸‹å°é½Š",
+DlgImgAlignMiddle : "置中å°é½Š",
+DlgImgAlignRight : "é å³å°é½Š",
+DlgImgAlignTextTop : "文字上方",
+DlgImgAlignTop : "é ä¸Šå°é½Š",
+DlgImgPreview : "é è¦½",
+DlgImgAlertUrl : "è«‹è¼¸å…¥å½±åƒ URL",
+DlgImgLinkTab : "超連çµ",
+
+// Flash Dialog
+DlgFlashTitle : "Flash 屬性",
+DlgFlashChkPlay : "自動播放",
+DlgFlashChkLoop : "é‡è¤‡",
+DlgFlashChkMenu : "é–‹å•Ÿé¸å–®",
+DlgFlashScale : "縮放",
+DlgFlashScaleAll : "全部顯示",
+DlgFlashScaleNoBorder : "無邊框",
+DlgFlashScaleFit : "精確符åˆ",
+
+// Link Dialog
+DlgLnkWindowTitle : "超連çµ",
+DlgLnkInfoTab : "超連çµè³‡è¨Š",
+DlgLnkTargetTab : "目標",
+
+DlgLnkType : "超連接類型",
+DlgLnkTypeURL : "URL",
+DlgLnkTypeAnchor : "本é éŒ¨é»ž",
+DlgLnkTypeEMail : "é›»å­éƒµä»¶",
+DlgLnkProto : "通訊å”定",
+DlgLnkProtoOther : "<其他>",
+DlgLnkURL : "URL",
+DlgLnkAnchorSel : "è«‹é¸æ“‡éŒ¨é»ž",
+DlgLnkAnchorByName : "ä¾éŒ¨é»žå稱",
+DlgLnkAnchorById : "ä¾å…ƒä»¶ ID",
+DlgLnkNoAnchors : "<本文件尚無å¯ç”¨ä¹‹éŒ¨é»ž>", //REVIEW : Change < and > with ( and )
+DlgLnkEMail : "é›»å­éƒµä»¶",
+DlgLnkEMailSubject : "郵件主旨",
+DlgLnkEMailBody : "郵件內容",
+DlgLnkUpload : "上傳",
+DlgLnkBtnUpload : "傳é€è‡³ä¼ºæœå™¨",
+
+DlgLnkTarget : "目標",
+DlgLnkTargetFrame : "<框架>",
+DlgLnkTargetPopup : "<快顯視窗>",
+DlgLnkTargetBlank : "新視窗 (_blank)",
+DlgLnkTargetParent : "父視窗 (_parent)",
+DlgLnkTargetSelf : "本視窗 (_self)",
+DlgLnkTargetTop : "最上層視窗 (_top)",
+DlgLnkTargetFrameName : "目標框架å稱",
+DlgLnkPopWinName : "快顯視窗å稱",
+DlgLnkPopWinFeat : "快顯視窗屬性",
+DlgLnkPopResize : "å¯èª¿æ•´å¤§å°",
+DlgLnkPopLocation : "網å€åˆ—",
+DlgLnkPopMenu : "é¸å–®åˆ—",
+DlgLnkPopScroll : "æ²è»¸",
+DlgLnkPopStatus : "狀態列",
+DlgLnkPopToolbar : "工具列",
+DlgLnkPopFullScrn : "全螢幕 (IE)",
+DlgLnkPopDependent : "從屬 (NS)",
+DlgLnkPopWidth : "寬",
+DlgLnkPopHeight : "高",
+DlgLnkPopLeft : "å·¦",
+DlgLnkPopTop : "å³",
+
+DlnLnkMsgNoUrl : "請輸入欲連çµçš„ URL",
+DlnLnkMsgNoEMail : "請輸入電å­éƒµä»¶ä½å€",
+DlnLnkMsgNoAnchor : "è«‹é¸æ“‡éŒ¨é»ž",
+DlnLnkMsgInvPopName : "快顯å稱必須以「英文字æ¯ã€ç‚ºé–‹é ­ï¼Œä¸”ä¸å¾—å«æœ‰ç©ºç™½",
+
+// Color Dialog
+DlgColorTitle : "è«‹é¸æ“‡é¡è‰²",
+DlgColorBtnClear : "清除",
+DlgColorHighlight : "é è¦½",
+DlgColorSelected : "é¸æ“‡",
+
+// Smiley Dialog
+DlgSmileyTitle : "æ’入表情符號",
+
+// Special Character Dialog
+DlgSpecialCharTitle : "è«‹é¸æ“‡ç‰¹æ®Šç¬¦è™Ÿ",
+
+// Table Dialog
+DlgTableTitle : "表格屬性",
+DlgTableRows : "列數",
+DlgTableColumns : "欄數",
+DlgTableBorder : "邊框",
+DlgTableAlign : "å°é½Š",
+DlgTableAlignNotSet : "<未設定>",
+DlgTableAlignLeft : "é å·¦å°é½Š",
+DlgTableAlignCenter : "置中",
+DlgTableAlignRight : "é å³å°é½Š",
+DlgTableWidth : "寬度",
+DlgTableWidthPx : "åƒç´ ",
+DlgTableWidthPc : "百分比",
+DlgTableHeight : "高度",
+DlgTableCellSpace : "é–“è·",
+DlgTableCellPad : "å…§è·",
+DlgTableCaption : "標題",
+DlgTableSummary : "摘è¦",
+
+// Table Cell Dialog
+DlgCellTitle : "儲存格屬性",
+DlgCellWidth : "寬度",
+DlgCellWidthPx : "åƒç´ ",
+DlgCellWidthPc : "百分比",
+DlgCellHeight : "高度",
+DlgCellWordWrap : "自動æ›è¡Œ",
+DlgCellWordWrapNotSet : "<尚未設定>",
+DlgCellWordWrapYes : "是",
+DlgCellWordWrapNo : "å¦",
+DlgCellHorAlign : "æ°´å¹³å°é½Š",
+DlgCellHorAlignNotSet : "<尚未設定>",
+DlgCellHorAlignLeft : "é å·¦å°é½Š",
+DlgCellHorAlignCenter : "置中",
+DlgCellHorAlignRight: "é å³å°é½Š",
+DlgCellVerAlign : "åž‚ç›´å°é½Š",
+DlgCellVerAlignNotSet : "<尚未設定>",
+DlgCellVerAlignTop : "é ä¸Šå°é½Š",
+DlgCellVerAlignMiddle : "置中",
+DlgCellVerAlignBottom : "é ä¸‹å°é½Š",
+DlgCellVerAlignBaseline : "基準線",
+DlgCellRowSpan : "åˆä½µåˆ—數",
+DlgCellCollSpan : "åˆä½µæ¬„æ•°",
+DlgCellBackColor : "背景é¡è‰²",
+DlgCellBorderColor : "邊框é¡è‰²",
+DlgCellBtnSelect : "è«‹é¸æ“‡â€¦",
+
+// Find Dialog
+DlgFindTitle : "尋找",
+DlgFindFindBtn : "尋找",
+DlgFindNotFoundMsg : "未找到指定的文字。",
+
+// Replace Dialog
+DlgReplaceTitle : "å–代",
+DlgReplaceFindLbl : "尋找:",
+DlgReplaceReplaceLbl : "å–代:",
+DlgReplaceCaseChk : "大å°å¯«é ˆç›¸ç¬¦",
+DlgReplaceReplaceBtn : "å–代",
+DlgReplaceReplAllBtn : "全部å–代",
+DlgReplaceWordChk : "全字相符",
+
+// Paste Operations / Dialog
+PasteErrorCut : "ç€è¦½å™¨çš„安全性設定ä¸å…許編輯器自動執行剪下動作。請使用快æ·éµ (Ctrl+X) 剪下。",
+PasteErrorCopy : "ç€è¦½å™¨çš„安全性設定ä¸å…許編輯器自動執行複製動作。請使用快æ·éµ (Ctrl+C) 複製。",
+
+PasteAsText : "貼為純文字格å¼",
+PasteFromWord : "自 Word 貼上",
+
+DlgPasteMsg2 : "請使用快æ·éµ (<strong>Ctrl+V</strong>) 貼到下方å€åŸŸä¸­ä¸¦æŒ‰ä¸‹ <strong>確定</strong>",
+DlgPasteSec : "Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.", //MISSING
+DlgPasteIgnoreFont : "移除字型設定",
+DlgPasteRemoveStyles : "移除樣å¼è¨­å®š",
+DlgPasteCleanBox : "清除文字å€åŸŸ",
+
+// Color Picker
+ColorAutomatic : "自動",
+ColorMoreColors : "更多é¡è‰²â€¦",
+
+// Document Properties
+DocProps : "文件屬性",
+
+// Anchor Dialog
+DlgAnchorTitle : "命å錨點",
+DlgAnchorName : "錨點å稱",
+DlgAnchorErrorName : "請輸入錨點å稱",
+
+// Speller Pages Dialog
+DlgSpellNotInDic : "ä¸åœ¨å­—典中",
+DlgSpellChangeTo : "更改為",
+DlgSpellBtnIgnore : "忽略",
+DlgSpellBtnIgnoreAll : "全部忽略",
+DlgSpellBtnReplace : "å–代",
+DlgSpellBtnReplaceAll : "全部å–代",
+DlgSpellBtnUndo : "復原",
+DlgSpellNoSuggestions : "- 無建議值 -",
+DlgSpellProgress : "進行拼字檢查中…",
+DlgSpellNoMispell : "拼字檢查完æˆï¼šæœªç™¼ç¾æ‹¼å­—錯誤",
+DlgSpellNoChanges : "拼字檢查完æˆï¼šæœªæ›´æ”¹ä»»ä½•å–®å­—",
+DlgSpellOneChange : "拼字檢查完æˆï¼šæ›´æ”¹äº† 1 個單字",
+DlgSpellManyChanges : "拼字檢查完æˆï¼šæ›´æ”¹äº† %1 個單字",
+
+IeSpellDownload : "尚未安è£æ‹¼å­—檢查元件。您是å¦æƒ³è¦ç¾åœ¨ä¸‹è¼‰ï¼Ÿ",
+
+// Button Dialog
+DlgButtonText : "顯示文字 (值)",
+DlgButtonType : "é¡žåž‹",
+DlgButtonTypeBtn : "按鈕 (Button)",
+DlgButtonTypeSbm : "é€å‡º (Submit)",
+DlgButtonTypeRst : "é‡è¨­ (Reset)",
+
+// Checkbox and Radio Button Dialogs
+DlgCheckboxName : "å稱",
+DlgCheckboxValue : "é¸å–值",
+DlgCheckboxSelected : "å·²é¸å–",
+
+// Form Dialog
+DlgFormName : "å稱",
+DlgFormAction : "動作",
+DlgFormMethod : "方法",
+
+// Select Field Dialog
+DlgSelectName : "å稱",
+DlgSelectValue : "é¸å–值",
+DlgSelectSize : "大å°",
+DlgSelectLines : "行",
+DlgSelectChkMulti : "å¯å¤šé¸",
+DlgSelectOpAvail : "å¯ç”¨é¸é …",
+DlgSelectOpText : "顯示文字",
+DlgSelectOpValue : "值",
+DlgSelectBtnAdd : "新增",
+DlgSelectBtnModify : "修改",
+DlgSelectBtnUp : "上移",
+DlgSelectBtnDown : "下移",
+DlgSelectBtnSetValue : "設為é è¨­å€¼",
+DlgSelectBtnDelete : "刪除",
+
+// Textarea Dialog
+DlgTextareaName : "å稱",
+DlgTextareaCols : "字元寬度",
+DlgTextareaRows : "列數",
+
+// Text Field Dialog
+DlgTextName : "å稱",
+DlgTextValue : "值",
+DlgTextCharWidth : "字元寬度",
+DlgTextMaxChars : "最多字元數",
+DlgTextType : "é¡žåž‹",
+DlgTextTypeText : "文字",
+DlgTextTypePass : "密碼",
+
+// Hidden Field Dialog
+DlgHiddenName : "å稱",
+DlgHiddenValue : "值",
+
+// Bulleted List Dialog
+BulletedListProp : "項目清單屬性",
+NumberedListProp : "編號清單屬性",
+DlgLstStart : "起始編號",
+DlgLstType : "清單類型",
+DlgLstTypeCircle : "圓圈",
+DlgLstTypeDisc : "圓點",
+DlgLstTypeSquare : "方塊",
+DlgLstTypeNumbers : "數字 (1, 2, 3)",
+DlgLstTypeLCase : "å°å¯«å­—æ¯ (a, b, c)",
+DlgLstTypeUCase : "å¤§å¯«å­—æ¯ (A, B, C)",
+DlgLstTypeSRoman : "å°å¯«ç¾…馬數字 (i, ii, iii)",
+DlgLstTypeLRoman : "大寫羅馬數字 (I, II, III)",
+
+// Document Properties Dialog
+DlgDocGeneralTab : "一般",
+DlgDocBackTab : "背景",
+DlgDocColorsTab : "顯色與邊界",
+DlgDocMetaTab : "Meta 資料",
+
+DlgDocPageTitle : "é é¢æ¨™é¡Œ",
+DlgDocLangDir : "語言方å‘",
+DlgDocLangDirLTR : "ç”±å·¦è€Œå³ (LTR)",
+DlgDocLangDirRTL : "ç”±å³è€Œå·¦ (RTL)",
+DlgDocLangCode : "語言代碼",
+DlgDocCharSet : "字元編碼",
+DlgDocCharSetCE : "中æ­èªžç³»",
+DlgDocCharSetCT : "正體中文 (Big5)",
+DlgDocCharSetCR : "斯拉夫文",
+DlgDocCharSetGR : "希臘文",
+DlgDocCharSetJP : "日文",
+DlgDocCharSetKR : "韓文",
+DlgDocCharSetTR : "土耳其文",
+DlgDocCharSetUN : "Unicode (UTF-8)",
+DlgDocCharSetWE : "西æ­èªžç³»",
+DlgDocCharSetOther : "其他字元編碼",
+
+DlgDocDocType : "文件類型",
+DlgDocDocTypeOther : "其他文件類型",
+DlgDocIncXHTML : "åŒ…å« XHTML 定義",
+DlgDocBgColor : "背景é¡è‰²",
+DlgDocBgImage : "背景影åƒ",
+DlgDocBgNoScroll : "浮水å°",
+DlgDocCText : "文字",
+DlgDocCLink : "超連çµ",
+DlgDocCVisited : "å·²ç€è¦½éŽçš„超連çµ",
+DlgDocCActive : "作用中的超連çµ",
+DlgDocMargins : "é é¢é‚Šç•Œ",
+DlgDocMaTop : "上",
+DlgDocMaLeft : "å·¦",
+DlgDocMaRight : "å³",
+DlgDocMaBottom : "下",
+DlgDocMeIndex : "文件索引關éµå­— (用åŠå½¢é€—號[,]分隔)",
+DlgDocMeDescr : "文件說明",
+DlgDocMeAuthor : "作者",
+DlgDocMeCopy : "版權所有",
+DlgDocPreview : "é è¦½",
+
+// Templates Dialog
+Templates : "樣版",
+DlgTemplatesTitle : "內容樣版",
+DlgTemplatesSelMsg : "è«‹é¸æ“‡æ¬²é–‹å•Ÿçš„樣版<br> (原有的內容將會被清除):",
+DlgTemplatesLoading : "讀å–樣版清單中,請ç¨å€™â€¦",
+DlgTemplatesNoTpl : "(無樣版)",
+DlgTemplatesReplace : "å–代原有內容",
+
+// About Dialog
+DlgAboutAboutTab : "關於",
+DlgAboutBrowserInfoTab : "ç€è¦½å™¨è³‡è¨Š",
+DlgAboutLicenseTab : "許å¯è­‰",
+DlgAboutVersion : "版本",
+DlgAboutInfo : "想ç²å¾—更多資訊請至 "
+}; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/plugins/autogrow/fckplugin.js b/httemplate/elements/fckeditor/editor/plugins/autogrow/fckplugin.js
new file mode 100644
index 0000000..7ce1c1c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/autogrow/fckplugin.js
@@ -0,0 +1,92 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Plugin: automatically resizes the editor until a configurable maximun
+ * height (FCKConfig.AutoGrowMax), based on its contents.
+ */
+
+var FCKAutoGrow_Min = window.frameElement.offsetHeight ;
+
+function FCKAutoGrow_Check()
+{
+ var oInnerDoc = FCK.EditorDocument ;
+
+ var iFrameHeight, iInnerHeight ;
+
+ if ( FCKBrowserInfo.IsIE )
+ {
+ iFrameHeight = FCK.EditorWindow.frameElement.offsetHeight ;
+ iInnerHeight = oInnerDoc.body.scrollHeight ;
+ }
+ else
+ {
+ iFrameHeight = FCK.EditorWindow.innerHeight ;
+ iInnerHeight = oInnerDoc.body.offsetHeight ;
+ }
+
+ var iDiff = iInnerHeight - iFrameHeight ;
+
+ if ( iDiff != 0 )
+ {
+ var iMainFrameSize = window.frameElement.offsetHeight ;
+
+ if ( iDiff > 0 && iMainFrameSize < FCKConfig.AutoGrowMax )
+ {
+ iMainFrameSize += iDiff ;
+ if ( iMainFrameSize > FCKConfig.AutoGrowMax )
+ iMainFrameSize = FCKConfig.AutoGrowMax ;
+ }
+ else if ( iDiff < 0 && iMainFrameSize > FCKAutoGrow_Min )
+ {
+ iMainFrameSize += iDiff ;
+ if ( iMainFrameSize < FCKAutoGrow_Min )
+ iMainFrameSize = FCKAutoGrow_Min ;
+ }
+ else
+ return ;
+
+ window.frameElement.height = iMainFrameSize ;
+ }
+}
+
+FCK.AttachToOnSelectionChange( FCKAutoGrow_Check ) ;
+
+function FCKAutoGrow_SetListeners()
+{
+ if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
+ return ;
+
+ FCK.EditorWindow.attachEvent( 'onscroll', FCKAutoGrow_Check ) ;
+ FCK.EditorDocument.attachEvent( 'onkeyup', FCKAutoGrow_Check ) ;
+}
+
+if ( FCKBrowserInfo.IsIE )
+{
+// FCKAutoGrow_SetListeners() ;
+ FCK.Events.AttachEvent( 'OnAfterSetHTML', FCKAutoGrow_SetListeners ) ;
+}
+
+function FCKAutoGrow_CheckEditorStatus( sender, status )
+{
+ if ( status == FCK_STATUS_COMPLETE )
+ FCKAutoGrow_Check() ;
+}
+
+FCK.Events.AttachEvent( 'OnStatusChange', FCKAutoGrow_CheckEditorStatus ) ; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/fck_placeholder.html b/httemplate/elements/fckeditor/editor/plugins/placeholder/fck_placeholder.html
new file mode 100644
index 0000000..a334206
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/fck_placeholder.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Placeholder Plugin.
+-->
+<html>
+ <head>
+ <title>Placeholder Properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta content="noindex, nofollow" name="robots">
+ <script language="javascript">
+
+var oEditor = window.parent.InnerDialogLoaded() ;
+var FCKLang = oEditor.FCKLang ;
+var FCKPlaceholders = oEditor.FCKPlaceholders ;
+
+window.onload = function ()
+{
+ // First of all, translate the dialog box texts
+ oEditor.FCKLanguageManager.TranslatePage( document ) ;
+
+ LoadSelected() ;
+
+ // Show the "Ok" button.
+ window.parent.SetOkButton( true ) ;
+}
+
+var eSelected = oEditor.FCKSelection.GetSelectedElement() ;
+
+function LoadSelected()
+{
+ if ( !eSelected )
+ return ;
+
+ if ( eSelected.tagName == 'SPAN' && eSelected._fckplaceholder )
+ document.getElementById('txtName').value = eSelected._fckplaceholder ;
+ else
+ eSelected == null ;
+}
+
+function Ok()
+{
+ var sValue = document.getElementById('txtName').value ;
+
+ if ( eSelected && eSelected._fckplaceholder == sValue )
+ return true ;
+
+ if ( sValue.length == 0 )
+ {
+ alert( FCKLang.PlaceholderErrNoName ) ;
+ return false ;
+ }
+
+ if ( FCKPlaceholders.Exist( sValue ) )
+ {
+ alert( FCKLang.PlaceholderErrNameInUse ) ;
+ return false ;
+ }
+
+ FCKPlaceholders.Add( sValue ) ;
+ return true ;
+}
+
+ </script>
+ </head>
+ <body scroll="no" style="OVERFLOW: hidden">
+ <table height="100%" cellSpacing="0" cellPadding="0" width="100%" border="0">
+ <tr>
+ <td>
+ <table cellSpacing="0" cellPadding="0" align="center" border="0">
+ <tr>
+ <td>
+ <span fckLang="PlaceholderDlgName">Placeholder Name</span><br>
+ <input id="txtName" type="text">
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html> \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/fckplugin.js b/httemplate/elements/fckeditor/editor/plugins/placeholder/fckplugin.js
new file mode 100644
index 0000000..26489b8
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/fckplugin.js
@@ -0,0 +1,187 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Plugin to insert "Placeholders" in the editor.
+ */
+
+// Register the related command.
+FCKCommands.RegisterCommand( 'Placeholder', new FCKDialogCommand( 'Placeholder', FCKLang.PlaceholderDlgTitle, FCKPlugins.Items['placeholder'].Path + 'fck_placeholder.html', 340, 170 ) ) ;
+
+// Create the "Plaholder" toolbar button.
+var oPlaceholderItem = new FCKToolbarButton( 'Placeholder', FCKLang.PlaceholderBtn ) ;
+oPlaceholderItem.IconPath = FCKPlugins.Items['placeholder'].Path + 'placeholder.gif' ;
+
+FCKToolbarItems.RegisterItem( 'Placeholder', oPlaceholderItem ) ;
+
+
+// The object used for all Placeholder operations.
+var FCKPlaceholders = new Object() ;
+
+// Add a new placeholder at the actual selection.
+FCKPlaceholders.Add = function( name )
+{
+ var oSpan = FCK.CreateElement( 'SPAN' ) ;
+ this.SetupSpan( oSpan, name ) ;
+}
+
+FCKPlaceholders.SetupSpan = function( span, name )
+{
+ span.innerHTML = '[[ ' + name + ' ]]' ;
+
+ span.style.backgroundColor = '#ffff00' ;
+ span.style.color = '#000000' ;
+
+ if ( FCKBrowserInfo.IsGecko )
+ span.style.cursor = 'default' ;
+
+ span._fckplaceholder = name ;
+ span.contentEditable = false ;
+
+ // To avoid it to be resized.
+ span.onresizestart = function()
+ {
+ FCK.EditorWindow.event.returnValue = false ;
+ return false ;
+ }
+}
+
+// On Gecko we must do this trick so the user select all the SPAN when clicking on it.
+FCKPlaceholders._SetupClickListener = function()
+{
+ FCKPlaceholders._ClickListener = function( e )
+ {
+ if ( e.target.tagName == 'SPAN' && e.target._fckplaceholder )
+ FCKSelection.SelectNode( e.target ) ;
+ }
+
+ FCK.EditorDocument.addEventListener( 'click', FCKPlaceholders._ClickListener, true ) ;
+}
+
+// Open the Placeholder dialog on double click.
+FCKPlaceholders.OnDoubleClick = function( span )
+{
+ if ( span.tagName == 'SPAN' && span._fckplaceholder )
+ FCKCommands.GetCommand( 'Placeholder' ).Execute() ;
+}
+
+FCK.RegisterDoubleClickHandler( FCKPlaceholders.OnDoubleClick, 'SPAN' ) ;
+
+// Check if a Placholder name is already in use.
+FCKPlaceholders.Exist = function( name )
+{
+ var aSpans = FCK.EditorDocument.getElementsByTagName( 'SPAN' ) ;
+
+ for ( var i = 0 ; i < aSpans.length ; i++ )
+ {
+ if ( aSpans[i]._fckplaceholder == name )
+ return true ;
+ }
+
+ return false ;
+}
+
+if ( FCKBrowserInfo.IsIE )
+{
+ FCKPlaceholders.Redraw = function()
+ {
+ if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
+ return ;
+
+ var aPlaholders = FCK.EditorDocument.body.innerText.match( /\[\[[^\[\]]+\]\]/g ) ;
+ if ( !aPlaholders )
+ return ;
+
+ var oRange = FCK.EditorDocument.body.createTextRange() ;
+
+ for ( var i = 0 ; i < aPlaholders.length ; i++ )
+ {
+ if ( oRange.findText( aPlaholders[i] ) )
+ {
+ var sName = aPlaholders[i].match( /\[\[\s*([^\]]*?)\s*\]\]/ )[1] ;
+ oRange.pasteHTML( '<span style="color: #000000; background-color: #ffff00" contenteditable="false" _fckplaceholder="' + sName + '">' + aPlaholders[i] + '</span>' ) ;
+ }
+ }
+ }
+}
+else
+{
+ FCKPlaceholders.Redraw = function()
+ {
+ if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
+ return ;
+
+ var oInteractor = FCK.EditorDocument.createTreeWalker( FCK.EditorDocument.body, NodeFilter.SHOW_TEXT, FCKPlaceholders._AcceptNode, true ) ;
+
+ var aNodes = new Array() ;
+
+ while ( ( oNode = oInteractor.nextNode() ) )
+ {
+ aNodes[ aNodes.length ] = oNode ;
+ }
+
+ for ( var n = 0 ; n < aNodes.length ; n++ )
+ {
+ var aPieces = aNodes[n].nodeValue.split( /(\[\[[^\[\]]+\]\])/g ) ;
+
+ for ( var i = 0 ; i < aPieces.length ; i++ )
+ {
+ if ( aPieces[i].length > 0 )
+ {
+ if ( aPieces[i].indexOf( '[[' ) == 0 )
+ {
+ var sName = aPieces[i].match( /\[\[\s*([^\]]*?)\s*\]\]/ )[1] ;
+
+ var oSpan = FCK.EditorDocument.createElement( 'span' ) ;
+ FCKPlaceholders.SetupSpan( oSpan, sName ) ;
+
+ aNodes[n].parentNode.insertBefore( oSpan, aNodes[n] ) ;
+ }
+ else
+ aNodes[n].parentNode.insertBefore( FCK.EditorDocument.createTextNode( aPieces[i] ) , aNodes[n] ) ;
+ }
+ }
+
+ aNodes[n].parentNode.removeChild( aNodes[n] ) ;
+ }
+
+ FCKPlaceholders._SetupClickListener() ;
+ }
+
+ FCKPlaceholders._AcceptNode = function( node )
+ {
+ if ( /\[\[[^\[\]]+\]\]/.test( node.nodeValue ) )
+ return NodeFilter.FILTER_ACCEPT ;
+ else
+ return NodeFilter.FILTER_SKIP ;
+ }
+}
+
+FCK.Events.AttachEvent( 'OnAfterSetHTML', FCKPlaceholders.Redraw ) ;
+
+// We must process the SPAN tags to replace then with the real resulting value of the placeholder.
+FCKXHtml.TagProcessors['span'] = function( node, htmlNode )
+{
+ if ( htmlNode._fckplaceholder )
+ node = FCKXHtml.XML.createTextNode( '[[' + htmlNode._fckplaceholder + ']]' ) ;
+ else
+ FCKXHtml._AppendChildNodes( node, htmlNode, false ) ;
+
+ return node ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/de.js b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/de.js
new file mode 100644
index 0000000..a666f8b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/de.js
@@ -0,0 +1,27 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Placholder German language file.
+ */
+FCKLang.PlaceholderBtn = 'Einfügen/editieren Platzhalter' ;
+FCKLang.PlaceholderDlgTitle = 'Platzhalter Eigenschaften' ;
+FCKLang.PlaceholderDlgName = 'Platzhalter Name' ;
+FCKLang.PlaceholderErrNoName = 'Bitte den Namen des Platzhalters schreiben' ;
+FCKLang.PlaceholderErrNameInUse = 'Der angegebene Namen ist schon in Gebrauch' ; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/en.js b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/en.js
new file mode 100644
index 0000000..290a3fb
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/en.js
@@ -0,0 +1,27 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Placholder English language file.
+ */
+FCKLang.PlaceholderBtn = 'Insert/Edit Placeholder' ;
+FCKLang.PlaceholderDlgTitle = 'Placeholder Properties' ;
+FCKLang.PlaceholderDlgName = 'Placeholder Name' ;
+FCKLang.PlaceholderErrNoName = 'Please type the placeholder name' ;
+FCKLang.PlaceholderErrNameInUse = 'The specified name is already in use' ; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/fr.js b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/fr.js
new file mode 100644
index 0000000..f5ac26e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/fr.js
@@ -0,0 +1,27 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Placeholder French language file.
+ */
+FCKLang.PlaceholderBtn = "Insérer/Modifier l'Espace réservé" ;
+FCKLang.PlaceholderDlgTitle = "Propriétés de l'Espace réservé" ;
+FCKLang.PlaceholderDlgName = "Nom de l'Espace réservé" ;
+FCKLang.PlaceholderErrNoName = "Veuillez saisir le nom de l'Espace réservé" ;
+FCKLang.PlaceholderErrNameInUse = "Ce nom est déjà utilisé" ;
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/it.js b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/it.js
new file mode 100644
index 0000000..51d75c0
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/it.js
@@ -0,0 +1,27 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Placholder Italian language file.
+ */
+FCKLang.PlaceholderBtn = 'Aggiungi/Modifica Placeholder' ;
+FCKLang.PlaceholderDlgTitle = 'Proprietà del Placeholder' ;
+FCKLang.PlaceholderDlgName = 'Nome del Placeholder' ;
+FCKLang.PlaceholderErrNoName = 'Digitare il nome del placeholder' ;
+FCKLang.PlaceholderErrNameInUse = 'Il nome inserito è già in uso' ;
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/pl.js b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/pl.js
new file mode 100644
index 0000000..bc55b38
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/lang/pl.js
@@ -0,0 +1,27 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Placholder Polish language file.
+ */
+FCKLang.PlaceholderBtn = 'Wstaw/Edytuj nagłówek' ;
+FCKLang.PlaceholderDlgTitle = 'Właśności nagłówka' ;
+FCKLang.PlaceholderDlgName = 'Nazwa nagłówka' ;
+FCKLang.PlaceholderErrNoName = 'Proszę wprowadzić nazwę nagłówka' ;
+FCKLang.PlaceholderErrNameInUse = 'Podana nazwa jest już w użyciu' ; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/plugins/placeholder/placeholder.gif b/httemplate/elements/fckeditor/editor/plugins/placeholder/placeholder.gif
new file mode 100644
index 0000000..c07078c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/placeholder/placeholder.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/plugins/simplecommands/fckplugin.js b/httemplate/elements/fckeditor/editor/plugins/simplecommands/fckplugin.js
new file mode 100644
index 0000000..cd25b6a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/simplecommands/fckplugin.js
@@ -0,0 +1,29 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This plugin register Toolbar items for the combos modifying the style to
+ * not show the box.
+ */
+
+FCKToolbarItems.RegisterItem( 'SourceSimple' , new FCKToolbarButton( 'Source', FCKLang.Source, null, FCK_TOOLBARITEM_ONLYICON, true, true, 1 ) ) ;
+FCKToolbarItems.RegisterItem( 'StyleSimple' , new FCKToolbarStyleCombo( null, FCK_TOOLBARITEM_ONLYTEXT ) ) ;
+FCKToolbarItems.RegisterItem( 'FontNameSimple' , new FCKToolbarFontsCombo( null, FCK_TOOLBARITEM_ONLYTEXT ) ) ;
+FCKToolbarItems.RegisterItem( 'FontSizeSimple' , new FCKToolbarFontSizeCombo( null, FCK_TOOLBARITEM_ONLYTEXT ) ) ;
+FCKToolbarItems.RegisterItem( 'FontFormatSimple', new FCKToolbarFontFormatCombo( null, FCK_TOOLBARITEM_ONLYTEXT ) ) ;
diff --git a/httemplate/elements/fckeditor/editor/plugins/tablecommands/fckplugin.js b/httemplate/elements/fckeditor/editor/plugins/tablecommands/fckplugin.js
new file mode 100644
index 0000000..88dac9c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/plugins/tablecommands/fckplugin.js
@@ -0,0 +1,32 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This plugin register the required Toolbar items to be able to insert the
+ * toolbar commands in the toolbar.
+ */
+
+FCKToolbarItems.RegisterItem( 'TableInsertRow' , new FCKToolbarButton( 'TableInsertRow' , FCKLang.InsertRow, null, null, null, null, 62 ) ) ;
+FCKToolbarItems.RegisterItem( 'TableDeleteRows' , new FCKToolbarButton( 'TableDeleteRows' , FCKLang.DeleteRows, null, null, null, null, 63 ) ) ;
+FCKToolbarItems.RegisterItem( 'TableInsertColumn' , new FCKToolbarButton( 'TableInsertColumn' , FCKLang.InsertColumn, null, null, null, null, 64 ) ) ;
+FCKToolbarItems.RegisterItem( 'TableDeleteColumns' , new FCKToolbarButton( 'TableDeleteColumns', FCKLang.DeleteColumns, null, null, null, null, 65 ) ) ;
+FCKToolbarItems.RegisterItem( 'TableInsertCell' , new FCKToolbarButton( 'TableInsertCell' , FCKLang.InsertCell, null, null, null, null, 58 ) ) ;
+FCKToolbarItems.RegisterItem( 'TableDeleteCells' , new FCKToolbarButton( 'TableDeleteCells' , FCKLang.DeleteCells, null, null, null, null, 59 ) ) ;
+FCKToolbarItems.RegisterItem( 'TableMergeCells' , new FCKToolbarButton( 'TableMergeCells' , FCKLang.MergeCells, null, null, null, null, 60 ) ) ;
+FCKToolbarItems.RegisterItem( 'TableSplitCell' , new FCKToolbarButton( 'TableSplitCell' , FCKLang.SplitCell, null, null, null, null, 61 ) ) ; \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/skins/_fckviewstrips.html b/httemplate/elements/fckeditor/editor/skins/_fckviewstrips.html
new file mode 100644
index 0000000..b28b941
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/_fckviewstrips.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Useful page that enumerates all icons in the skins strips.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>FCKeditor - View Icons Strips</title>
+ <style type="text/css">
+ .TB_Button_Image
+ {
+ overflow: hidden;
+ width: 16px;
+ height: 16px;
+ margin: 3px;
+ background-repeat: no-repeat;
+ }
+
+ .TB_Button_Image img
+ {
+ position: relative;
+ }
+ </style>
+ <script type="text/javascript">
+
+window.onload = function()
+{
+ var eImg1 = document.createElement( 'img' ) ;
+ eImg1.onload = Img_OnLoad ;
+ eImg1.src = 'default/fck_strip.gif' ;
+
+ var eImg2 = document.createElement( 'img' ) ;
+ eImg2.onload = Img_OnLoad ;
+ eImg2.src = 'office2003/fck_strip.gif' ;
+
+ var eImg3 = document.createElement( 'img' ) ;
+ eImg3.onload = Img_OnLoad ;
+ eImg3.src = 'silver/fck_strip.gif' ;
+}
+
+var iTotalStrips = 3 ;
+var iMaxHeight = 0 ;
+
+function Img_OnLoad()
+{
+ if ( iMaxHeight < this.height )
+ iMaxHeight = this.height ;
+
+ iTotalStrips-- ;
+
+ if ( iTotalStrips == 0 )
+ LoadIcons( iMaxHeight / 16 ) ;
+}
+
+function LoadIcons( total )
+{
+ var xIconsTable = document.getElementById( 'xIconsTable' ) ;
+
+ for ( var i = 0 ; i < total ; i++ )
+ {
+ var eRow = xIconsTable.insertRow(-1) ;
+
+ var eCell = eRow.insertCell(-1) ;
+ eCell.innerHTML = i + 1 ;
+
+ eCell = eRow.insertCell(-1) ;
+ eCell.align = 'center' ;
+ eCell.style.border = '#dcdcdc 1px solid' ;
+ eCell.innerHTML = '<div class="TB_Button_Image"><img src="default/fck_strip.gif" style="top:-' + ( i * 16 ) + 'px;"><\/div>' ;
+
+ eCell = eRow.insertCell(-1) ;
+ eCell.align = 'center' ;
+ eCell.style.border = '#dcdcdc 1px solid' ;
+ eCell.innerHTML = '<div class="TB_Button_Image"><img src="office2003/fck_strip.gif" style="top:-' + ( i * 16 ) + 'px;"><\/div>' ;
+
+ eCell = eRow.insertCell(-1) ;
+ eCell.align = 'center' ;
+ eCell.style.border = '#dcdcdc 1px solid' ;
+ eCell.innerHTML = '<div class="TB_Button_Image"><img src="silver/fck_strip.gif" style="top:-' + ( i * 16 ) + 'px;"><\/div>' ;
+ }
+}
+
+ </script>
+</head>
+<body>
+ <table id="xIconsTable">
+ <tr>
+ <td rowspan="2">
+ Index</td>
+ <td align="center" colspan="3">
+ Skins</td>
+ </tr>
+ <tr>
+ <td width="80" align="center">
+ default</td>
+ <td width="80" align="center">
+ office2003</td>
+ <td width="80" align="center">
+ silver</td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/httemplate/elements/fckeditor/editor/skins/default/fck_dialog.css b/httemplate/elements/fckeditor/editor/skins/default/fck_dialog.css
new file mode 100644
index 0000000..9725f6c
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/fck_dialog.css
@@ -0,0 +1,137 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Styles used by the dialog boxes.
+ */
+
+body
+{
+ margin: 0px;
+ padding: 10px;
+}
+
+body, td, input, select, textarea
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana;
+}
+
+body, .BackColor
+{
+ background-color: #f1f1e3;
+}
+
+.PopupBody
+{
+ margin: 0px;
+ padding: 0px;
+}
+
+.PopupTitle
+{
+ font-weight: bold;
+ font-size: 14pt;
+ color: #737357;
+ background-color: #e3e3c7;
+ padding: 3px 10px 3px 10px;
+}
+
+.PopupButtons
+{
+ border-top: #d5d59d 1px solid;
+ background-color: #e3e3c7;
+ padding: 7px 10px 7px 10px;
+}
+
+.Button
+{
+ border: #737357 1px solid;
+ color: #3b3b1f;
+ background-color: #c7c78f;
+}
+
+#btnOk
+{
+ width: 100px;
+}
+
+.DarkBackground
+{
+ background-color: #d7d79f;
+}
+
+.LightBackground
+{
+ background-color: #ffffbe;
+}
+
+.PopupTitleBorder
+{
+ border-bottom: #d5d59d 1px solid;
+}
+
+.PopupTabArea
+{
+ color: #737357;
+ background-color: #e3e3c7;
+}
+
+.PopupTabEmptyArea
+{
+ padding-left: 10px ;
+ border-bottom: #d5d59d 1px solid;
+}
+
+.PopupTab, .PopupTabSelected
+{
+ border-right: #d5d59d 1px solid;
+ border-top: #d5d59d 1px solid;
+ border-left: #d5d59d 1px solid;
+ padding-right: 5px;
+ padding-left: 5px;
+ padding-bottom: 3px;
+ padding-top: 3px;
+ color: #737357;
+}
+
+.PopupTab
+{
+ margin-top: 1px;
+ border-bottom: #d5d59d 1px solid;
+ cursor: pointer;
+ cursor: hand;
+}
+
+.PopupTabSelected
+{
+ font-weight:bold;
+ cursor: default;
+ padding-top: 4px;
+ border-bottom: #f1f1e3 1px solid;
+ background-color: #f1f1e3;
+}
+
+.PopupSelectionBox
+{
+ border: #ff9933 1px solid !important;
+ background-color: #fffacd !important;
+ cursor: pointer;
+ cursor: hand;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/skins/default/fck_editor.css b/httemplate/elements/fckeditor/editor/skins/default/fck_editor.css
new file mode 100644
index 0000000..b849cca
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/fck_editor.css
@@ -0,0 +1,464 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Styles used by the editor IFRAME and Toolbar.
+ */
+
+/*
+ ### Basic Editor IFRAME Styles.
+*/
+
+body
+{
+ padding: 1px 1px 1px 1px;
+ margin: 0px 0px 0px 0px;
+}
+
+#xEditingArea
+{
+ border: #696969 1px solid;
+}
+
+.SourceField
+{
+ padding: 5px;
+ margin: 0px;
+ font-family: Monospace;
+}
+
+/*
+ Toolbar
+*/
+
+.TB_ToolbarSet, .TB_Expand, .TB_Collapse
+{
+ cursor: default;
+ background-color: #efefde;
+}
+
+.TB_ToolbarSet
+{
+ border-top: #efefde 1px outset;
+ border-bottom: #efefde 1px outset;
+}
+
+.TB_ToolbarSet TD
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.TB_Toolbar
+{
+ height: 24px;
+ display: inline-table; /* inline = Opera jumping buttons bug */
+}
+
+.TB_Separator
+{
+ width: 1px;
+ height: 16px;
+ margin: 2px;
+ background-color: #999966;
+}
+
+.TB_Start
+{
+ background-image: url(images/toolbar.start.gif);
+ margin: 2px;
+ width: 3px;
+ background-repeat: no-repeat;
+ height: 16px;
+}
+
+.TB_End
+{
+ display: none;
+}
+
+.TB_ExpandImg
+{
+ background-image: url(images/toolbar.expand.gif);
+ background-repeat: no-repeat;
+}
+
+.TB_CollapseImg
+{
+ background-image: url(images/toolbar.collapse.gif);
+ background-repeat: no-repeat;
+}
+
+.TB_SideBorder
+{
+ background-color: #696969;
+}
+
+.TB_Expand, .TB_Collapse
+{
+ padding: 2px 2px 2px 2px;
+ border: #efefde 1px outset;
+}
+
+.TB_Collapse
+{
+ width: 5px;
+}
+
+.TB_Break
+{
+ height: 24px; /* IE needs the height to be set, otherwise no break */
+}
+
+/*
+ Toolbar Button
+*/
+
+.TB_Button_On, .TB_Button_Off, .TB_Button_On_Over, .TB_Button_Off_Over, .TB_Button_Disabled
+{
+ border: #efefde 1px solid; /* This is the default border */
+ height: 22px; /* The height is necessary, otherwise IE will not apply the alpha */
+}
+
+.TB_Button_On
+{
+ border: #316ac5 1px solid;
+ background-color: #c1d2ee;
+}
+
+.TB_Button_On_Over, .TB_Button_Off_Over
+{
+ border: #316ac5 1px solid;
+ background-color: #dff1ff;
+}
+
+.TB_Button_Off
+{
+ filter: alpha(opacity=70); /* IE */
+ opacity: 0.70; /* Safari, Opera and Mozilla */
+}
+
+.TB_Button_Disabled
+{
+ filter: gray() alpha(opacity=30); /* IE */
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+}
+
+.TB_Button_Padding
+{
+ visibility: hidden;
+ width: 3px;
+ height: 22px;
+}
+
+.TB_Button_Image
+{
+ overflow: hidden;
+ width: 16px;
+ height: 16px;
+ margin: 3px;
+ background-repeat: no-repeat;
+}
+
+.TB_Button_Image img
+{
+ position: relative;
+}
+
+.TB_Button_Off .TB_Button_Text
+{
+ background-color: #efefde; /* Needed because of a bug on Clear Type */
+}
+
+.TB_ConnectionLine
+{
+ background-color: #ffffff;
+ height: 1px;
+ margin-left: 1px; /* ltr */
+ margin-right: 1px; /* rtl */
+}
+
+.TB_Text
+{
+ height: 22px;
+}
+
+.TB_Button_Off .TB_Text
+{
+ background-color: #efefde ; /* Needed because of a bug on ClearType */
+}
+
+.TB_Button_On_Over .TB_Text
+{
+ background-color: #dff1ff ; /* Needed because of a bug on ClearType */
+}
+
+/*
+ Menu
+*/
+
+.MN_Menu
+{
+ border: 1px solid #8f8f73;
+ padding: 2px;
+ background-color: #ffffff;
+ cursor: default;
+}
+
+.MN_Menu, .MN_Menu .MN_Label
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.MN_Item_Padding
+{
+ visibility: hidden;
+ width: 3px;
+ height: 20px;
+}
+
+.MN_Icon
+{
+ background-color: #e3e3c7;
+ text-align: center;
+ height: 20px;
+}
+
+.MN_Label
+{
+ padding-left: 3px;
+ padding-right: 3px;
+}
+
+.MN_Separator
+{
+ height: 3px;
+}
+
+.MN_Separator_Line
+{
+ border-top: #b9b99d 1px solid;
+}
+
+.MN_Item .MN_Icon IMG
+{
+ filter: alpha(opacity=70);
+ opacity: 0.70;
+}
+
+.MN_Item_Over
+{
+ color: #ffffff;
+ background-color: #8f8f73;
+}
+
+.MN_Item_Over .MN_Icon
+{
+ background-color: #737357;
+}
+
+.MN_Item_Disabled IMG
+{
+ filter: gray() alpha(opacity=30); /* IE */
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+}
+
+.MN_Item_Disabled .MN_Label
+{
+ color: #b7b7b7;
+}
+
+.MN_Arrow
+{
+ padding-right: 3px;
+ padding-left: 3px;
+}
+
+.MN_ConnectionLine
+{
+ background-color: #ffffff;
+}
+
+.Menu .TB_Button_On, .Menu .TB_Button_On_Over
+{
+ border: #8f8f73 1px solid;
+ background-color: #ffffff;
+}
+
+/*
+ ### Panel Styles
+*/
+
+.FCK_Panel
+{
+ border: #8f8f73 1px solid;
+ padding: 2px;
+ background-color: #ffffff;
+}
+
+.FCK_Panel, .FCK_Panel TD
+{
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+ font-size: 11px;
+}
+
+/*
+ ### Special Combos
+*/
+
+.SC_Panel
+{
+ overflow: auto;
+ white-space: nowrap;
+ cursor: default;
+ border: 1px solid #8f8f73;
+ padding-left: 2px;
+ padding-right: 2px;
+ background-color: #ffffff;
+}
+
+.SC_Panel, .SC_Panel TD
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.SC_Item, .SC_ItemSelected
+{
+ margin-top: 2px;
+ margin-bottom: 2px;
+ background-position: left center;
+ padding-left: 11px;
+ padding-right: 3px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ background-repeat: no-repeat;
+ border: #dddddd 1px solid;
+}
+
+.SC_Item *, .SC_ItemSelected *
+{
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+.SC_ItemSelected
+{
+ border: #9a9afb 1px solid;
+ background-image: url(images/toolbar.arrowright.gif);
+}
+
+.SC_ItemOver
+{
+ border: #316ac5 1px solid;
+}
+
+.SC_Field
+{
+ border: #b7b7a6 1px solid;
+ cursor: default;
+}
+
+.SC_FieldCaption
+{
+ overflow: visible;
+ padding-right: 5px;
+ padding-left: 5px;
+ opacity: 0.75; /* Safari, Opera and Mozilla */
+ filter: alpha(opacity=70); /* IE */ /* -moz-opacity: 0.75; Mozilla (Old) */
+ height: 23px;
+ background-color: #efefde;
+}
+
+.SC_FieldLabel
+{
+ white-space: nowrap;
+ padding: 2px;
+ width: 100%;
+ cursor: default;
+ background-color: #ffffff;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.SC_FieldButton
+{
+ background-position: center center;
+ background-image: url(images/toolbar.buttonarrow.gif);
+ border-left: #b7b7a6 1px solid;
+ width: 14px;
+ background-repeat: no-repeat;
+}
+
+.SC_FieldDisabled .SC_FieldButton, .SC_FieldDisabled .SC_FieldCaption
+{
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+ filter: gray() alpha(opacity=30); /* IE */ /* -moz-opacity: 0.30; Mozilla (Old) */
+}
+
+.SC_FieldOver
+{
+ border: #316ac5 1px solid;
+}
+
+.SC_FieldOver .SC_FieldButton
+{
+ border-left: #316ac5 1px solid;
+}
+
+/*
+ ### Color Selector Panel
+*/
+
+.ColorBoxBorder
+{
+ border: #808080 1px solid;
+ position: static;
+}
+
+.ColorBox
+{
+ font-size: 1px;
+ width: 10px;
+ position: static;
+ height: 10px;
+}
+
+.ColorDeselected, .ColorSelected
+{
+ cursor: default;
+}
+
+.ColorDeselected
+{
+ border: #ffffff 1px solid;
+ padding: 2px;
+ float: left;
+}
+
+.ColorSelected
+{
+ border: #330066 1px solid;
+ padding: 2px;
+ float: left;
+ background-color: #c4cdd6;
+}
diff --git a/httemplate/elements/fckeditor/editor/skins/default/fck_strip.gif b/httemplate/elements/fckeditor/editor/skins/default/fck_strip.gif
new file mode 100644
index 0000000..d5ba74e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/fck_strip.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.arrowright.gif b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.arrowright.gif
new file mode 100644
index 0000000..6843c8d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.arrowright.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.buttonarrow.gif b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.buttonarrow.gif
new file mode 100644
index 0000000..ea60995
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.buttonarrow.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.collapse.gif b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.collapse.gif
new file mode 100644
index 0000000..87aa56d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.collapse.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.end.gif b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.end.gif
new file mode 100644
index 0000000..5bfd67a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.end.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.expand.gif b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.expand.gif
new file mode 100644
index 0000000..79075e7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.expand.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.separator.gif b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.separator.gif
new file mode 100644
index 0000000..eaed04a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.separator.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.start.gif b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.start.gif
new file mode 100644
index 0000000..1774246
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/default/images/toolbar.start.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/fck_dialog.css b/httemplate/elements/fckeditor/editor/skins/office2003/fck_dialog.css
new file mode 100644
index 0000000..54a958b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/fck_dialog.css
@@ -0,0 +1,138 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Styles used by the dialog boxes.
+ */
+
+body
+{
+ margin: 0px;
+ padding: 10px;
+ background-color: #f7f8fd;
+}
+
+body, td, input, select, textarea
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana;
+}
+
+body, .BackColor
+{
+ background-color: #f7f8fd;
+}
+
+.PopupBody
+{
+ margin: 0px;
+ padding: 0px;
+}
+
+.PopupTitle
+{
+ font-weight: bold;
+ font-size: 14pt;
+ color: #0e3460;
+ background-color: #8cb2fd;
+ padding: 3px 10px 3px 10px;
+}
+
+.PopupButtons
+{
+ border-top: #466ca6 1px solid;
+ background-color: #8cb2fd;
+ padding: 7px 10px 7px 10px;
+}
+
+.Button
+{
+ border: #1c3460 1px solid;
+ color: #000a28;
+ background-color: #7096d3;
+}
+
+#btnOk
+{
+ width: 100px;
+}
+
+.DarkBackground
+{
+ background-color: #d7d79f;
+}
+
+.LightBackground
+{
+ background-color: #ffffbe;
+}
+
+.PopupTitleBorder
+{
+ border-bottom: #d5d59d 1px solid;
+}
+
+.PopupTabArea
+{
+ color: #0e3460;
+ background-color: #8cb2fd;
+}
+
+.PopupTabEmptyArea
+{
+ padding-left: 10px ;
+ border-bottom: #466ca6 1px solid;
+}
+
+.PopupTab, .PopupTabSelected
+{
+ border-right: #466ca6 1px solid;
+ border-top: #466ca6 1px solid;
+ border-left: #466ca6 1px solid;
+ padding-right: 5px;
+ padding-left: 5px;
+ padding-bottom: 3px;
+ padding-top: 3px;
+ color: #0e3460;
+}
+
+.PopupTab
+{
+ margin-top: 1px;
+ border-bottom: #466ca6 1px solid;
+ cursor: pointer;
+ cursor: hand;
+}
+
+.PopupTabSelected
+{
+ font-weight:bold;
+ cursor: default;
+ padding-top: 4px;
+ border-bottom: #f7f8fd 1px solid;
+ background-color: #f7f8fd;
+}
+
+.PopupSelectionBox
+{
+ border: #1e90ff 1px solid !important;
+ background-color: #add8e6 !important;
+ cursor: pointer;
+ cursor: hand;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/fck_editor.css b/httemplate/elements/fckeditor/editor/skins/office2003/fck_editor.css
new file mode 100644
index 0000000..f68c06f
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/fck_editor.css
@@ -0,0 +1,476 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Styles used by the editor IFRAME and Toolbar.
+ */
+
+/*
+ ### Basic Editor IFRAME Styles.
+*/
+
+body
+{
+ padding: 1px 1px 1px 1px;
+ margin: 0px 0px 0px 0px;
+}
+
+#xEditingArea
+{
+ border: #696969 1px solid;
+}
+
+.SourceField
+{
+ padding: 5px;
+ margin: 0px;
+ font-family: Monospace;
+}
+
+/*
+ Toolbar
+*/
+
+.TB_ToolbarSet, .TB_Expand, .TB_Collapse
+{
+ cursor: default;
+ background-color: #f7f8fd;
+}
+
+.TB_ToolbarSet
+{
+ border-top: #f7f8fd 1px outset;
+ border-bottom: #f7f8fd 1px outset;
+}
+
+.TB_ToolbarSet TD
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.TB_Toolbar
+{
+ background-color: #d6dff7;
+ background-image: url(images/toolbar.bg.gif);
+ background-repeat: repeat-x;
+ display: inline-table;
+}
+
+.TB_Separator
+{
+ width: 1px;
+ height: 16px;
+ margin: 2px;
+ background-color: #B2CBFF;
+}
+
+.TB_Start
+{
+ background-image: url(images/toolbar.start.gif);
+ background-repeat: no-repeat;
+ background-position: center center;
+ margin: 0px;
+ width: 7px;
+ height: 24px;
+}
+
+.TB_End
+{
+ background-image: url(images/toolbar.end.gif);
+ background-repeat: no-repeat;
+ background-position: center left;
+ height: 24px;
+ width: 4px;
+}
+
+.TB_ExpandImg
+{
+ background-image: url(images/toolbar.expand.gif);
+ background-repeat: no-repeat;
+}
+
+.TB_CollapseImg
+{
+ background-image: url(images/toolbar.collapse.gif);
+ background-repeat: no-repeat;
+}
+
+.TB_SideBorder
+{
+ background-color: #696969;
+}
+
+.TB_Expand, .TB_Collapse
+{
+ padding: 2px 2px 2px 2px;
+ border: #f7f8fd 1px outset;
+}
+
+.TB_Collapse
+{
+ width: 5px;
+}
+
+.TB_Break
+{
+ height: 24px; /* IE needs the height to be set, otherwise no break */
+}
+
+/*
+ Toolbar Button
+*/
+
+.TB_Button_On, .TB_Button_Off, .TB_Button_On_Over, .TB_Button_Off_Over, .TB_Button_Disabled
+{
+ margin: 1px;
+ height: 22px; /* The height is necessary, otherwise IE will not apply the alpha */
+}
+
+.TB_Button_On
+{
+ margin: 0px;
+ border: #316ac5 1px solid;
+ background-color: #c1d2ee;
+}
+
+.TB_Button_On_Over, .TB_Button_Off_Over
+{
+ margin: 0px ;
+ border: #316ac5 1px solid;
+ background-color: #dff1ff;
+}
+
+.TB_Button_Off
+{
+ filter: alpha(opacity=70); /* IE */
+ opacity: 0.70; /* Safari, Opera and Mozilla */
+}
+
+.TB_Button_Disabled
+{
+ filter: gray() alpha(opacity=30); /* IE */
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+}
+
+.TB_Button_Padding
+{
+ visibility: hidden;
+ width: 3px;
+ height: 22px;
+}
+
+.TB_Button_Image
+{
+ overflow: hidden;
+ width: 16px;
+ height: 16px;
+ margin: 3px;
+ background-repeat: no-repeat;
+}
+
+.TB_Button_Image img
+{
+ position: relative;
+}
+
+.TB_Button_Off .TB_Button_Text
+{
+ background-color: #d6dff7; /* Needed because of a bug on ClearType */
+ background-image: url(images/toolbar.bg.gif);
+ background-repeat: repeat-x;
+}
+
+.TB_ConnectionLine
+{
+ background-color: #f7f8fd;
+ height: 1px;
+ margin-left: 1px; /* ltr */
+ margin-right: 1px; /* rtl */
+}
+
+.TB_Button_Off .TB_Text
+{
+ background-color: #d6dff7; /* Needed because of a bug on ClearType */
+ background-image: url(images/toolbar.bg.gif);
+ background-repeat: repeat-x;
+}
+
+.TB_Button_On_Over .TB_Text
+{
+ background-color: #dff1ff ; /* Needed because of a bug on ClearType */
+}
+
+/*
+ Menu
+*/
+
+.MN_Menu
+{
+ border: 1px solid #8f8f73;
+ padding: 2px;
+ background-color: #f7f8fd;
+ cursor: default;
+}
+
+.MN_Menu, .MN_Menu .MN_Label
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.MN_Item_Padding
+{
+ visibility: hidden;
+ width: 3px;
+ height: 20px;
+}
+
+.MN_Icon
+{
+ background-color: #d6dff7;
+ text-align: center;
+ height: 20px;
+}
+
+.MN_Label
+{
+ padding-left: 3px;
+ padding-right: 3px;
+}
+
+.MN_Separator
+{
+ height: 3px;
+}
+
+.MN_Separator_Line
+{
+ border-top: #b9b99d 1px solid;
+}
+
+.MN_Item .MN_Icon IMG
+{
+ filter: alpha(opacity=70);
+ opacity: 0.70;
+}
+
+.MN_Item_Over
+{
+ color: #ffffff;
+ background-color: #7096FA;
+}
+
+.MN_Item_Over .MN_Icon
+{
+ background-color: #466ca6;
+}
+
+.MN_Item_Disabled IMG
+{
+ filter: gray() alpha(opacity=30); /* IE */
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+}
+
+.MN_Item_Disabled .MN_Label
+{
+ color: #b7b7b7;
+}
+
+.MN_Arrow
+{
+ padding-right: 3px;
+ padding-left: 3px;
+}
+
+.MN_ConnectionLine
+{
+ background-color: #f7f8fd;
+}
+
+.Menu .TB_Button_On, .Menu .TB_Button_On_Over
+{
+ border: #8f8f73 1px solid;
+ background-color: #f7f8fd;
+}
+
+/*
+ ### Panel Styles
+*/
+
+.FCK_Panel
+{
+ border: #8f8f73 1px solid;
+ padding: 2px;
+ background-color: #f7f8fd;
+}
+
+.FCK_Panel, .FCK_Panel TD
+{
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+ font-size: 11px;
+}
+
+/*
+ ### Special Combos
+*/
+
+.SC_Panel
+{
+ overflow: auto;
+ white-space: nowrap;
+ cursor: default;
+ border: 1px solid #8f8f73;
+ padding-left: 2px;
+ padding-right: 2px;
+ background-color: #ffffff;
+}
+
+.SC_Panel, .SC_Panel TD
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.SC_Item, .SC_ItemSelected
+{
+ margin-top: 2px;
+ margin-bottom: 2px;
+ background-position: left center;
+ padding-left: 11px;
+ padding-right: 3px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ background-repeat: no-repeat;
+ border: #dddddd 1px solid;
+}
+
+.SC_Item *, .SC_ItemSelected *
+{
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+.SC_ItemSelected
+{
+ border: #9a9afb 1px solid;
+ background-image: url(images/toolbar.arrowright.gif);
+}
+
+.SC_ItemOver
+{
+ border: #316ac5 1px solid;
+}
+
+.SC_Field
+{
+ margin-top: 2px ;
+ border: #b7b7a6 1px solid;
+ cursor: default;
+}
+
+.SC_FieldCaption
+{
+ overflow: visible;
+ padding-right: 5px;
+ padding-left: 5px;
+ opacity: 0.75; /* Safari, Opera and Mozilla */
+ filter: alpha(opacity=70); /* IE */ /* -moz-opacity: 0.75; Mozilla (Old) */
+ height: 23px;
+ background-color: #d6dff7; /* Needed because of a bug on ClearType */
+ background-image: url(images/toolbar.bg.gif);
+ background-repeat: repeat-x;
+/* background-color: inherit; Maybe this is needed wait to check */
+}
+
+.SC_FieldLabel
+{
+ white-space: nowrap;
+ padding: 2px;
+ width: 100%;
+ cursor: default;
+ background-color: #ffffff;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.SC_FieldButton
+{
+ background-position: center center;
+ background-image: url(images/toolbar.buttonarrow.gif);
+ border-left: #b7b7a6 1px solid;
+ width: 14px;
+ background-repeat: no-repeat;
+}
+
+.SC_FieldDisabled .SC_FieldButton, .SC_FieldDisabled .SC_FieldCaption
+{
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+ filter: gray() alpha(opacity=30); /* IE */ /* -moz-opacity: 0.30; Mozilla (Old) */
+}
+
+.SC_FieldOver
+{
+ border: #316ac5 1px solid;
+}
+
+.SC_FieldOver .SC_FieldButton
+{
+ border-left: #316ac5 1px solid;
+}
+
+/*
+ ### Color Selector Panel
+*/
+
+.ColorBoxBorder
+{
+ border: #808080 1px solid;
+ position: static;
+}
+
+.ColorBox
+{
+ font-size: 1px;
+ width: 10px;
+ position: static;
+ height: 10px;
+}
+
+.ColorDeselected, .ColorSelected
+{
+ cursor: default;
+}
+
+.ColorDeselected
+{
+ border: #ffffff 1px solid;
+ padding: 2px;
+ float: left;
+}
+
+.ColorSelected
+{
+ border: #330066 1px solid;
+ padding: 2px;
+ float: left;
+ background-color: #c4cdd6;
+}
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/fck_strip.gif b/httemplate/elements/fckeditor/editor/skins/office2003/fck_strip.gif
new file mode 100644
index 0000000..a7282f2
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/fck_strip.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.arrowright.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.arrowright.gif
new file mode 100644
index 0000000..6843c8d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.arrowright.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.bg.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.bg.gif
new file mode 100644
index 0000000..b03960b
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.bg.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.buttonarrow.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.buttonarrow.gif
new file mode 100644
index 0000000..ea60995
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.buttonarrow.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.collapse.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.collapse.gif
new file mode 100644
index 0000000..d549166
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.collapse.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.end.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.end.gif
new file mode 100644
index 0000000..7ff599d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.end.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.expand.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.expand.gif
new file mode 100644
index 0000000..c4a7326
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.expand.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.separator.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.separator.gif
new file mode 100644
index 0000000..27db9c3
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.separator.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.start.gif b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.start.gif
new file mode 100644
index 0000000..41f1241
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/office2003/images/toolbar.start.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/fck_dialog.css b/httemplate/elements/fckeditor/editor/skins/silver/fck_dialog.css
new file mode 100644
index 0000000..e05c173
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/fck_dialog.css
@@ -0,0 +1,141 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Styles used by the dialog boxes.
+ */
+
+body
+{
+ margin: 0px;
+ padding: 10px;
+ background-color: #f7f7f7;
+}
+
+body, td, input, select, textarea
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana;
+}
+
+body, .BackColor
+{
+ background-color: #f7f7f7;
+}
+
+.PopupBody
+{
+ margin: 0px;
+ padding: 0px;
+}
+
+.PopupTitle
+{
+ padding-right: 10px;
+ padding-left: 10px;
+ font-weight: bold;
+ font-size: 14pt;
+ padding-bottom: 3px;
+ color: #504845;
+ padding-top: 3px;
+ background-color: #dedede;
+}
+
+.PopupButtons
+{
+ border-top: #cec6b5 1px solid;
+ background-color: #DEDEDE;
+ padding: 7px 10px 7px 10px;
+}
+
+.Button
+{
+ border: #7a7261 1px solid;
+ color: #504845;
+ background-color: #cec6b5;
+}
+
+#btnOk
+{
+ width: 100px;
+}
+
+.DarkBackground
+{
+ background-color: #d7d79f;
+}
+
+.LightBackground
+{
+ background-color: #ffffbe;
+}
+
+.PopupTitleBorder
+{
+ border-bottom: #cec6b5 1px solid;
+}
+
+.PopupTabArea
+{
+ color: #504845;
+ background-color: #DEDEDE;
+}
+
+.PopupTabEmptyArea
+{
+ padding-left: 10px ;
+ border-bottom: #cec6b5 1px solid;
+}
+
+.PopupTab, .PopupTabSelected
+{
+ border-right: #cec6b5 1px solid;
+ border-top: #cec6b5 1px solid;
+ border-left: #cec6b5 1px solid;
+ padding-right: 5px;
+ padding-left: 5px;
+ padding-bottom: 3px;
+ padding-top: 3px;
+ color: #504845;
+}
+
+.PopupTab
+{
+ margin-top: 1px;
+ border-bottom: #cec6b5 1px solid;
+ cursor: pointer;
+ cursor: hand;
+}
+
+.PopupTabSelected
+{
+ font-weight:bold;
+ cursor: default;
+ padding-top: 4px;
+ border-bottom: #f1f1e3 1px solid;
+ background-color: #f7f7f7;
+}
+
+.PopupSelectionBox
+{
+ border: #a9a9a9 1px solid !important;
+ background-color: #dcdcdc !important;
+ cursor: pointer;
+ cursor: hand;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/fck_editor.css b/httemplate/elements/fckeditor/editor/skins/silver/fck_editor.css
new file mode 100644
index 0000000..656dcdb
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/fck_editor.css
@@ -0,0 +1,473 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Styles used by the editor IFRAME and Toolbar.
+ */
+
+/*
+ ### Basic Editor IFRAME Styles.
+*/
+
+body
+{
+ padding: 1px 1px 1px 1px;
+ margin: 0px 0px 0px 0px;
+}
+
+#xEditingArea
+{
+ border: #696969 1px solid;
+}
+
+.SourceField
+{
+ padding: 5px;
+ margin: 0px;
+ font-family: Monospace;
+}
+
+/*
+ Toolbar
+*/
+
+.TB_ToolbarSet, .TB_Expand, .TB_Collapse
+{
+ cursor: default;
+ background-color: #f7f7f7;
+}
+
+.TB_ToolbarSet
+{
+ padding: 1px;
+ border-top: #efefde 1px outset;
+ border-bottom: #efefde 1px outset;
+}
+
+.TB_ToolbarSet TD
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.TB_Toolbar
+{
+ display: inline-table;
+}
+
+.TB_Separator
+{
+ width: 1px;
+ height: 21px;
+ margin: 2px;
+ background-color: #C6C3BD;
+}
+
+.TB_Start
+{
+ background-image: url(images/toolbar.start.gif);
+ margin-left: 2px;
+ margin-right: 2px;
+ width: 3px;
+ background-repeat: no-repeat;
+ height: 27px;
+ background-position: center center;
+}
+
+.TB_End
+{
+ display: none;
+}
+
+.TB_ExpandImg
+{
+ background-image: url(images/toolbar.expand.gif);
+ background-repeat: no-repeat;
+}
+
+.TB_CollapseImg
+{
+ background-image: url(images/toolbar.collapse.gif);
+ background-repeat: no-repeat;
+}
+
+.TB_SideBorder
+{
+ background-color: #696969;
+}
+
+.TB_Expand, .TB_Collapse
+{
+ padding: 2px 2px 2px 2px;
+ border: #efefde 1px outset;
+}
+
+.TB_Collapse
+{
+ border: #efefde 1px outset;
+ width: 5px;
+}
+
+.TB_Break
+{
+ height: 27px;
+}
+
+/*
+ Toolbar Button
+*/
+
+.TB_Button_On, .TB_Button_Off, .TB_Button_On_Over, .TB_Button_Off_Over, .TB_Button_Disabled
+{
+ padding: 1px ;
+ margin:1px;
+ height: 21px;
+}
+
+.TB_Button_On, .TB_Button_Off, .TB_Button_On_Over, .TB_Button_Off_Over, .TB_Button_Disabled
+{
+ border: #cec6b5 1px solid;
+}
+
+.TB_Button_On
+{
+ border-color: #316ac5;
+ background-color: #c1d2ee;
+}
+
+.TB_Button_On_Over, .TB_Button_Off_Over
+{
+ border: #316ac5 1px solid;
+ background-color: #dff1ff;
+}
+
+.TB_Button_Off
+{
+ background: #efefef url(images/toolbar.buttonbg.gif) repeat-x;
+}
+
+.TB_Button_Off, .TB_Combo_Off
+{
+ opacity: 0.70; /* Safari, Opera and Mozilla */
+ filter: alpha(opacity=70); /* IE */
+ /* -moz-opacity: 0.70; Mozilla (Old) */
+}
+
+.TB_Button_Disabled
+{
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+ filter: gray() alpha(opacity=30); /* IE */
+}
+
+.TB_Button_Padding
+{
+ visibility: hidden;
+ width: 3px;
+ height: 21px;
+}
+
+.TB_Button_Image
+{
+ overflow: hidden;
+ width: 16px;
+ height: 16px;
+ margin: 3px;
+ margin-top: 4px;
+ margin-bottom: 2px;
+ background-repeat: no-repeat;
+}
+
+/* For composed button ( icon + text, icon + arrow ), we must compensate the table */
+.TB_Button_On TABLE .TB_Button_Image,
+.TB_Button_Off TABLE .TB_Button_Image,
+.TB_Button_On_Over TABLE .TB_Button_Image,
+.TB_Button_Off_Over TABLE .TB_Button_Image,
+.TB_Button_Disabled TABLE .TB_Button_Image
+{
+ margin-top: 3px;
+}
+
+.TB_Button_Image img
+{
+ position: relative;
+}
+
+.TB_ConnectionLine
+{
+ background-color: #ffffff;
+ height: 1px;
+ margin-left: 1px; /* ltr */
+ margin-right: 1px; /* rtl */
+}
+
+/*
+ Menu
+*/
+
+.MN_Menu
+{
+ border: 1px solid #8f8f73;
+ padding: 2px;
+ background-color: #f7f7f7;
+ cursor: default;
+}
+
+.MN_Menu, .MN_Menu .MN_Label
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.MN_Item_Padding
+{
+ visibility: hidden;
+ width: 3px;
+ height: 20px;
+}
+
+.MN_Icon
+{
+ background-color: #dedede;
+ text-align: center;
+ height: 20px;
+}
+
+.MN_Label
+{
+ padding-left: 3px;
+ padding-right: 3px;
+}
+
+.MN_Separator
+{
+ height: 3px;
+}
+
+.MN_Separator_Line
+{
+ border-top: #b9b99d 1px solid;
+}
+
+.MN_Item .MN_Icon IMG
+{
+ filter: alpha(opacity=70);
+ opacity: 0.70;
+}
+
+.MN_Item_Over
+{
+ color: #ffffff;
+ background-color: #8a857d;
+}
+
+.MN_Item_Over .MN_Icon
+{
+ background-color: #6c6761;
+}
+
+.MN_Item_Disabled IMG
+{
+ filter: gray() alpha(opacity=30); /* IE */
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+}
+
+.MN_Item_Disabled .MN_Label
+{
+ color: #b7b7b7;
+}
+
+.MN_Arrow
+{
+ padding-right: 3px;
+ padding-left: 3px;
+}
+
+.MN_ConnectionLine
+{
+ background-color: #ffffff;
+}
+
+.Menu .TB_Button_On, .Menu .TB_Button_On_Over
+{
+ border: #8f8f73 1px solid;
+ background-color: #ffffff;
+}
+
+/*
+ ### Panel Styles
+*/
+
+.FCK_Panel
+{
+ border: #8f8f73 1px solid;
+ padding: 2px;
+ background-color: #ffffff;
+}
+
+.FCK_Panel, .FCK_Panel TD
+{
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+ font-size: 11px;
+}
+
+/*
+ ### Special Combos
+*/
+
+.SC_Panel
+{
+ overflow: auto;
+ white-space: nowrap;
+ cursor: default;
+ border: 1px solid #8f8f73;
+ padding-left: 2px;
+ padding-right: 2px;
+ background-color: #ffffff;
+}
+
+.SC_Panel, .SC_Panel TD
+{
+ font-size: 11px;
+ font-family: 'Microsoft Sans Serif' , Tahoma, Arial, Verdana, Sans-Serif;
+}
+
+.SC_Item, .SC_ItemSelected
+{
+ margin-top: 2px;
+ margin-bottom: 2px;
+ background-position: left center;
+ padding-left: 11px;
+ padding-right: 3px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ background-repeat: no-repeat;
+ border: #dddddd 1px solid;
+}
+
+.SC_Item *, .SC_ItemSelected *
+{
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+.SC_ItemSelected
+{
+ border: #9a9afb 1px solid;
+ background-image: url(images/toolbar.arrowright.gif);
+}
+
+.SC_ItemOver
+{
+ border: #316ac5 1px solid;
+}
+
+.SC_Field
+{
+ margin-top:1px ;
+ border: #b7b7a6 1px solid;
+ cursor: default;
+}
+
+.SC_FieldCaption
+{
+ padding-top: 1px ;
+ overflow: visible;
+ padding-right: 5px;
+ padding-left: 5px;
+ opacity: 0.75; /* Safari, Opera and Mozilla */
+ filter: alpha(opacity=70); /* IE */ /* -moz-opacity: 0.75; Mozilla (Old) */
+ height: 23px;
+ background-color: #f7f7f7;
+}
+
+.SC_FieldLabel
+{
+ white-space: nowrap;
+ padding: 2px;
+ width: 100%;
+ cursor: default;
+ background-color: #ffffff;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.SC_FieldButton
+{
+ background-position: center center;
+ background-image: url(images/toolbar.buttonarrow.gif);
+ border-left: #b7b7a6 1px solid;
+ width: 14px;
+ background-repeat: no-repeat;
+}
+
+.SC_FieldDisabled .SC_FieldButton, .SC_FieldDisabled .SC_FieldCaption
+{
+ opacity: 0.30; /* Safari, Opera and Mozilla */
+ filter: gray() alpha(opacity=30); /* IE */ /* -moz-opacity: 0.30; Mozilla (Old) */
+}
+
+.SC_FieldOver
+{
+ border: #316ac5 1px solid;
+}
+
+.SC_FieldOver .SC_FieldButton
+{
+ border-left: #316ac5 1px solid;
+}
+
+/*
+ ### Color Selector Panel
+*/
+
+.ColorBoxBorder
+{
+ border: #808080 1px solid;
+ position: static;
+}
+
+.ColorBox
+{
+ font-size: 1px;
+ width: 10px;
+ position: static;
+ height: 10px;
+}
+
+.ColorDeselected, .ColorSelected
+{
+ cursor: default;
+}
+
+.ColorDeselected
+{
+ border: #ffffff 1px solid;
+ padding: 2px;
+ float: left;
+}
+
+.ColorSelected
+{
+ border: #316ac5 1px solid;
+ padding: 2px;
+ float: left;
+ background-color: #c1d2ee;
+}
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/fck_strip.gif b/httemplate/elements/fckeditor/editor/skins/silver/fck_strip.gif
new file mode 100644
index 0000000..d5ba74e
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/fck_strip.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.arrowright.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.arrowright.gif
new file mode 100644
index 0000000..6843c8d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.arrowright.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonarrow.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonarrow.gif
new file mode 100644
index 0000000..ea60995
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonarrow.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonbg.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonbg.gif
new file mode 100644
index 0000000..a93ffca
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.buttonbg.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.collapse.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.collapse.gif
new file mode 100644
index 0000000..87aa56d
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.collapse.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.end.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.end.gif
new file mode 100644
index 0000000..5bfd67a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.end.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.expand.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.expand.gif
new file mode 100644
index 0000000..79075e7
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.expand.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.separator.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.separator.gif
new file mode 100644
index 0000000..eaed04a
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.separator.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.start.gif b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.start.gif
new file mode 100644
index 0000000..1774246
--- /dev/null
+++ b/httemplate/elements/fckeditor/editor/skins/silver/images/toolbar.start.gif
Binary files differ
diff --git a/httemplate/elements/fckeditor/fckconfig.js b/httemplate/elements/fckeditor/fckconfig.js
new file mode 100644
index 0000000..215bc0a
--- /dev/null
+++ b/httemplate/elements/fckeditor/fckconfig.js
@@ -0,0 +1,245 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Editor configuration settings.
+ *
+ * Follow this link for more information:
+ * http://wiki.fckeditor.net/Developer%27s_Guide/Configuration/Configurations_Settings
+ */
+
+// Disable the custom Enter Key Handler. This option will be removed in version 2.5.
+FCKConfig.DisableEnterKeyHandler = false ;
+
+FCKConfig.CustomConfigurationsPath = '' ;
+
+FCKConfig.EditorAreaCSS = FCKConfig.BasePath + 'css/fck_editorarea.css' ;
+FCKConfig.ToolbarComboPreviewCSS = '' ;
+
+FCKConfig.DocType = '' ;
+
+FCKConfig.BaseHref = '' ;
+
+FCKConfig.FullPage = false ;
+
+FCKConfig.Debug = false ;
+FCKConfig.AllowQueryStringDebug = true ;
+
+FCKConfig.SkinPath = FCKConfig.BasePath + 'skins/default/' ;
+//FCKConfig.SkinPath = FCKConfig.BasePath + 'editor/skins/silver/' ;
+FCKConfig.PreloadImages = [ FCKConfig.SkinPath + 'images/toolbar.start.gif', FCKConfig.SkinPath + 'images/toolbar.buttonarrow.gif' ] ;
+
+FCKConfig.PluginsPath = FCKConfig.BasePath + 'plugins/' ;
+
+// FCKConfig.Plugins.Add( 'autogrow' ) ;
+FCKConfig.AutoGrowMax = 400 ;
+
+// FCKConfig.ProtectedSource.Add( /<%[\s\S]*?%>/g ) ; // ASP style server side code <%...%>
+// FCKConfig.ProtectedSource.Add( /<\?[\s\S]*?\?>/g ) ; // PHP style server side code
+// FCKConfig.ProtectedSource.Add( /(<asp:[^\>]+>[\s|\S]*?<\/asp:[^\>]+>)|(<asp:[^\>]+\/>)/gi ) ; // ASP.Net style tags <asp:control>
+
+FCKConfig.AutoDetectLanguage = true ;
+FCKConfig.DefaultLanguage = 'en' ;
+FCKConfig.ContentLangDirection = 'ltr' ;
+
+FCKConfig.ProcessHTMLEntities = true ;
+FCKConfig.IncludeLatinEntities = true ;
+FCKConfig.IncludeGreekEntities = true ;
+
+FCKConfig.ProcessNumericEntities = false ;
+
+FCKConfig.AdditionalNumericEntities = '' ; // Single Quote: "'"
+
+FCKConfig.FillEmptyBlocks = true ;
+
+FCKConfig.FormatSource = true ;
+FCKConfig.FormatOutput = true ;
+FCKConfig.FormatIndentator = ' ' ;
+
+FCKConfig.ForceStrongEm = true ;
+FCKConfig.GeckoUseSPAN = false ;
+FCKConfig.StartupFocus = false ;
+FCKConfig.ForcePasteAsPlainText = false ;
+FCKConfig.AutoDetectPasteFromWord = true ; // IE only.
+FCKConfig.ForceSimpleAmpersand = false ;
+FCKConfig.TabSpaces = 0 ;
+FCKConfig.ShowBorders = true ;
+FCKConfig.SourcePopup = false ;
+FCKConfig.ToolbarStartExpanded = true ;
+FCKConfig.ToolbarCanCollapse = true ;
+FCKConfig.IgnoreEmptyParagraphValue = true ;
+FCKConfig.PreserveSessionOnFileBrowser = false ;
+FCKConfig.FloatingPanelsZIndex = 10000 ;
+
+FCKConfig.TemplateReplaceAll = true ;
+FCKConfig.TemplateReplaceCheckbox = true ;
+
+FCKConfig.ToolbarLocation = 'In' ;
+
+//FCKConfig.ToolbarSets["Default"] = [
+// ['Source','DocProps','-','Save','NewPage','Preview','-','Templates'],
+// ['Cut','Copy','Paste','PasteText','PasteWord','-','Print','SpellCheck'],
+// ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
+// ['Form','Checkbox','Radio','TextField','Textarea','Select','Button','ImageButton','HiddenField'],
+// '/',
+// ['Bold','Italic','Underline','StrikeThrough','-','Subscript','Superscript'],
+// ['OrderedList','UnorderedList','-','Outdent','Indent'],
+// ['JustifyLeft','JustifyCenter','JustifyRight','JustifyFull'],
+// ['Link','Unlink','Anchor'],
+// ['Image','Flash','Table','Rule','Smiley','SpecialChar','PageBreak'],
+// '/',
+// ['Style','FontFormat','FontName','FontSize'],
+// ['TextColor','BGColor'],
+// ['FitWindow','-','About']
+//] ;
+FCKConfig.ToolbarSets["Default"] = [
+ ['Source','DocProps','-','Save','Preview','-'],
+ ['Cut','Copy','Paste','PasteText','PasteWord','-','Print','SpellCheck'],
+ ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
+ //['Form','Checkbox','Radio','TextField','Textarea','Select','Button','ImageButton','HiddenField'],
+ '/',
+ ['Bold','Italic','Underline','StrikeThrough','-','Subscript','Superscript'],
+ ['OrderedList','UnorderedList','-','Outdent','Indent'],
+ ['JustifyLeft','JustifyCenter','JustifyRight','JustifyFull'],
+ ['Link','Unlink','Anchor'],
+ ['Image','Flash','Table','Rule','Smiley','SpecialChar','PageBreak'],
+ '/',
+ ['Style','FontFormat','FontName','FontSize'],
+ ['TextColor','BGColor'],
+ ['FitWindow','-','About']
+] ;
+
+FCKConfig.ToolbarSets["Basic"] = [
+ ['Bold','Italic','-','OrderedList','UnorderedList','-','Link','Unlink','-','About']
+] ;
+
+FCKConfig.EnterMode = 'p' ; // p | div | br
+FCKConfig.ShiftEnterMode = 'br' ; // p | div | br
+
+FCKConfig.Keystrokes = [
+ [ CTRL + 65 /*A*/, true ],
+ [ CTRL + 67 /*C*/, true ],
+ [ CTRL + 70 /*F*/, true ],
+ [ CTRL + 83 /*S*/, true ],
+ [ CTRL + 88 /*X*/, true ],
+ [ CTRL + 86 /*V*/, 'Paste' ],
+ [ SHIFT + 45 /*INS*/, 'Paste' ],
+ [ CTRL + 90 /*Z*/, 'Undo' ],
+ [ CTRL + 89 /*Y*/, 'Redo' ],
+ [ CTRL + SHIFT + 90 /*Z*/, 'Redo' ],
+ [ CTRL + 76 /*L*/, 'Link' ],
+ [ CTRL + 66 /*B*/, 'Bold' ],
+ [ CTRL + 73 /*I*/, 'Italic' ],
+ [ CTRL + 85 /*U*/, 'Underline' ],
+ [ CTRL + SHIFT + 83 /*S*/, 'Save' ],
+ [ CTRL + ALT + 13 /*ENTER*/, 'FitWindow' ],
+ [ CTRL + 9 /*TAB*/, 'Source' ]
+] ;
+
+FCKConfig.ContextMenu = ['Generic','Link','Anchor','Image','Flash','Select','Textarea','Checkbox','Radio','TextField','HiddenField','ImageButton','Button','BulletedList','NumberedList','Table','Form'] ;
+FCKConfig.BrowserContextMenuOnCtrl = false ;
+
+FCKConfig.FontColors = '000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,808080,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF' ;
+
+FCKConfig.FontNames = 'Arial;Comic Sans MS;Courier New;Tahoma;Times New Roman;Verdana' ;
+FCKConfig.FontSizes = '1/xx-small;2/x-small;3/small;4/medium;5/large;6/x-large;7/xx-large' ;
+FCKConfig.FontFormats = 'p;div;pre;address;h1;h2;h3;h4;h5;h6' ;
+
+FCKConfig.StylesXmlPath = FCKConfig.EditorPath + 'fckstyles.xml' ;
+FCKConfig.TemplatesXmlPath = FCKConfig.EditorPath + 'fcktemplates.xml' ;
+
+FCKConfig.SpellChecker = 'ieSpell' ; // 'ieSpell' | 'SpellerPages'
+FCKConfig.IeSpellDownloadUrl = 'http://www.iespell.com/download.php' ;
+FCKConfig.SpellerPagesServerScript = 'server-scripts/spellchecker.php' ; // Available extension: .php .cfm .pl
+FCKConfig.FirefoxSpellChecker = false ;
+
+FCKConfig.MaxUndoLevels = 15 ;
+
+FCKConfig.DisableObjectResizing = false ;
+FCKConfig.DisableFFTableHandles = true ;
+
+FCKConfig.LinkDlgHideTarget = false ;
+FCKConfig.LinkDlgHideAdvanced = false ;
+
+FCKConfig.ImageDlgHideLink = false ;
+FCKConfig.ImageDlgHideAdvanced = false ;
+
+FCKConfig.FlashDlgHideAdvanced = false ;
+
+FCKConfig.ProtectedTags = '' ;
+
+// This will be applied to the body element of the editor
+FCKConfig.BodyId = '' ;
+FCKConfig.BodyClass = '' ;
+
+FCKConfig.DefaultLinkTarget = '' ;
+
+// The option switches between trying to keep the html structure or do the changes so the content looks like it was in Word
+FCKConfig.CleanWordKeepsStructure = false ;
+
+// The following value defines which File Browser connector and Quick Upload
+// "uploader" to use. It is valid for the default implementaion and it is here
+// just to make this configuration file cleaner.
+// It is not possible to change this value using an external file or even
+// inline when creating the editor instance. In that cases you must set the
+// values of LinkBrowserURL, ImageBrowserURL and so on.
+// Custom implementations should just ignore it.
+var _FileBrowserLanguage = 'asp' ; // asp | aspx | cfm | lasso | perl | php | py
+var _QuickUploadLanguage = 'asp' ; // asp | aspx | cfm | lasso | php
+
+
+// Don't care about the following line. It just calculates the correct connector
+// extension to use for the default File Browser (Perl uses "cgi").
+var _FileBrowserExtension = _FileBrowserLanguage == 'perl' ? 'cgi' : _FileBrowserLanguage ;
+
+FCKConfig.LinkBrowser = true ;
+FCKConfig.LinkBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Connector=connectors/' + _FileBrowserLanguage + '/connector.' + _FileBrowserExtension ;
+FCKConfig.LinkBrowserWindowWidth = FCKConfig.ScreenWidth * 0.7 ; // 70%
+FCKConfig.LinkBrowserWindowHeight = FCKConfig.ScreenHeight * 0.7 ; // 70%
+
+FCKConfig.ImageBrowser = true ;
+FCKConfig.ImageBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Type=Image&Connector=connectors/' + _FileBrowserLanguage + '/connector.' + _FileBrowserExtension ;
+FCKConfig.ImageBrowserWindowWidth = FCKConfig.ScreenWidth * 0.7 ; // 70% ;
+FCKConfig.ImageBrowserWindowHeight = FCKConfig.ScreenHeight * 0.7 ; // 70% ;
+
+FCKConfig.FlashBrowser = true ;
+FCKConfig.FlashBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Type=Flash&Connector=connectors/' + _FileBrowserLanguage + '/connector.' + _FileBrowserExtension ;
+FCKConfig.FlashBrowserWindowWidth = FCKConfig.ScreenWidth * 0.7 ; //70% ;
+FCKConfig.FlashBrowserWindowHeight = FCKConfig.ScreenHeight * 0.7 ; //70% ;
+
+FCKConfig.LinkUpload = true ;
+FCKConfig.LinkUploadURL = FCKConfig.BasePath + 'filemanager/upload/' + _QuickUploadLanguage + '/upload.' + _QuickUploadLanguage ;
+FCKConfig.LinkUploadAllowedExtensions = "" ; // empty for all
+FCKConfig.LinkUploadDeniedExtensions = ".(html|htm|php|php2|php3|php4|php5|phtml|pwml|inc|asp|aspx|ascx|jsp|cfm|cfc|pl|bat|exe|com|dll|vbs|js|reg|cgi|htaccess|asis|sh|shtml|shtm|phtm)$" ; // empty for no one
+
+FCKConfig.ImageUpload = true ;
+FCKConfig.ImageUploadURL = FCKConfig.BasePath + 'filemanager/upload/' + _QuickUploadLanguage + '/upload.' + _QuickUploadLanguage + '?Type=Image' ;
+FCKConfig.ImageUploadAllowedExtensions = ".(jpg|gif|jpeg|png|bmp)$" ; // empty for all
+FCKConfig.ImageUploadDeniedExtensions = "" ; // empty for no one
+
+FCKConfig.FlashUpload = true ;
+FCKConfig.FlashUploadURL = FCKConfig.BasePath + 'filemanager/upload/' + _QuickUploadLanguage + '/upload.' + _QuickUploadLanguage + '?Type=Flash' ;
+FCKConfig.FlashUploadAllowedExtensions = ".(swf|fla)$" ; // empty for all
+FCKConfig.FlashUploadDeniedExtensions = "" ; // empty for no one
+
+FCKConfig.SmileyPath = FCKConfig.BasePath + 'images/smiley/msn/' ;
+FCKConfig.SmileyImages = ['regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','confused_smile.gif','tounge_smile.gif','embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angry_smile.gif','angel_smile.gif','shades_smile.gif','devil_smile.gif','cry_smile.gif','lightbulb.gif','thumbs_down.gif','thumbs_up.gif','heart.gif','broken_heart.gif','kiss.gif','envelope.gif'] ;
+FCKConfig.SmileyColumns = 8 ;
+FCKConfig.SmileyWindowWidth = 320 ;
+FCKConfig.SmileyWindowHeight = 240 ;
diff --git a/httemplate/elements/fckeditor/fckeditor.js b/httemplate/elements/fckeditor/fckeditor.js
new file mode 100644
index 0000000..63ec41f
--- /dev/null
+++ b/httemplate/elements/fckeditor/fckeditor.js
@@ -0,0 +1,214 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This is the integration file for JavaScript.
+ *
+ * It defines the FCKeditor class that can be used to create editor
+ * instances in a HTML page in the client side. For server side
+ * operations, use the specific integration system.
+ */
+
+// FCKeditor Class
+var FCKeditor = function( instanceName, width, height, toolbarSet, value )
+{
+ // Properties
+ this.InstanceName = instanceName ;
+ this.Width = width || '100%' ;
+ this.Height = height || '200' ;
+ this.ToolbarSet = toolbarSet || 'Default' ;
+ this.Value = value || '' ;
+ this.BasePath = '/fckeditor/' ;
+ this.CheckBrowser = true ;
+ this.DisplayErrors = true ;
+ this.EnableSafari = false ; // This is a temporary property, while Safari support is under development.
+ this.EnableOpera = false ; // This is a temporary property, while Opera support is under development.
+
+ this.Config = new Object() ;
+
+ // Events
+ this.OnError = null ; // function( source, errorNumber, errorDescription )
+}
+
+FCKeditor.prototype.Version = '2.4.3' ;
+FCKeditor.prototype.VersionBuild = '15657' ;
+
+FCKeditor.prototype.Create = function()
+{
+ document.write( this.CreateHtml() ) ;
+}
+
+FCKeditor.prototype.CreateHtml = function()
+{
+ // Check for errors
+ if ( !this.InstanceName || this.InstanceName.length == 0 )
+ {
+ this._ThrowError( 701, 'You must specify an instance name.' ) ;
+ return '' ;
+ }
+
+ var sHtml = '<div>' ;
+
+ if ( !this.CheckBrowser || this._IsCompatibleBrowser() )
+ {
+ sHtml += '<input type="hidden" id="' + this.InstanceName + '" name="' + this.InstanceName + '" value="' + this._HTMLEncode( this.Value ) + '" style="display:none" />' ;
+ sHtml += this._GetConfigHtml() ;
+ sHtml += this._GetIFrameHtml() ;
+ }
+ else
+ {
+ var sWidth = this.Width.toString().indexOf('%') > 0 ? this.Width : this.Width + 'px' ;
+ var sHeight = this.Height.toString().indexOf('%') > 0 ? this.Height : this.Height + 'px' ;
+ sHtml += '<textarea name="' + this.InstanceName + '" rows="4" cols="40" style="width:' + sWidth + ';height:' + sHeight + '">' + this._HTMLEncode( this.Value ) + '<\/textarea>' ;
+ }
+
+ sHtml += '</div>' ;
+
+ return sHtml ;
+}
+
+FCKeditor.prototype.ReplaceTextarea = function()
+{
+ if ( !this.CheckBrowser || this._IsCompatibleBrowser() )
+ {
+ // We must check the elements firstly using the Id and then the name.
+ var oTextarea = document.getElementById( this.InstanceName ) ;
+ var colElementsByName = document.getElementsByName( this.InstanceName ) ;
+ var i = 0;
+ while ( oTextarea || i == 0 )
+ {
+ if ( oTextarea && oTextarea.tagName.toLowerCase() == 'textarea' )
+ break ;
+ oTextarea = colElementsByName[i++] ;
+ }
+
+ if ( !oTextarea )
+ {
+ alert( 'Error: The TEXTAREA with id or name set to "' + this.InstanceName + '" was not found' ) ;
+ return ;
+ }
+
+ oTextarea.style.display = 'none' ;
+ this._InsertHtmlBefore( this._GetConfigHtml(), oTextarea ) ;
+ this._InsertHtmlBefore( this._GetIFrameHtml(), oTextarea ) ;
+ }
+}
+
+FCKeditor.prototype._InsertHtmlBefore = function( html, element )
+{
+ if ( element.insertAdjacentHTML ) // IE
+ element.insertAdjacentHTML( 'beforeBegin', html ) ;
+ else // Gecko
+ {
+ var oRange = document.createRange() ;
+ oRange.setStartBefore( element ) ;
+ var oFragment = oRange.createContextualFragment( html );
+ element.parentNode.insertBefore( oFragment, element ) ;
+ }
+}
+
+FCKeditor.prototype._GetConfigHtml = function()
+{
+ var sConfig = '' ;
+ for ( var o in this.Config )
+ {
+ if ( sConfig.length > 0 ) sConfig += '&amp;' ;
+ sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.Config[o] ) ;
+ }
+
+ return '<input type="hidden" id="' + this.InstanceName + '___Config" value="' + sConfig + '" style="display:none" />' ;
+}
+
+FCKeditor.prototype._GetIFrameHtml = function()
+{
+ var sFile = 'fckeditor.html' ;
+
+ try
+ {
+ if ( (/fcksource=true/i).test( window.top.location.search ) )
+ sFile = 'fckeditor.original.html' ;
+ }
+ catch (e) { /* Ignore it. Much probably we are inside a FRAME where the "top" is in another domain (security error). */ }
+
+ var sLink = this.BasePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.InstanceName ) ;
+ if (this.ToolbarSet) sLink += '&amp;Toolbar=' + this.ToolbarSet ;
+
+ return '<iframe id="' + this.InstanceName + '___Frame" src="' + sLink + '" width="' + this.Width + '" height="' + this.Height + '" frameborder="0" scrolling="no"></iframe>' ;
+}
+
+FCKeditor.prototype._IsCompatibleBrowser = function()
+{
+ return FCKeditor_IsCompatibleBrowser( this.EnableSafari, this.EnableOpera ) ;
+}
+
+FCKeditor.prototype._ThrowError = function( errorNumber, errorDescription )
+{
+ this.ErrorNumber = errorNumber ;
+ this.ErrorDescription = errorDescription ;
+
+ if ( this.DisplayErrors )
+ {
+ document.write( '<div style="COLOR: #ff0000">' ) ;
+ document.write( '[ FCKeditor Error ' + this.ErrorNumber + ': ' + this.ErrorDescription + ' ]' ) ;
+ document.write( '</div>' ) ;
+ }
+
+ if ( typeof( this.OnError ) == 'function' )
+ this.OnError( this, errorNumber, errorDescription ) ;
+}
+
+FCKeditor.prototype._HTMLEncode = function( text )
+{
+ if ( typeof( text ) != "string" )
+ text = text.toString() ;
+
+ text = text.replace(
+ /&/g, "&amp;").replace(
+ /"/g, "&quot;").replace(
+ /</g, "&lt;").replace(
+ />/g, "&gt;") ;
+
+ return text ;
+}
+
+function FCKeditor_IsCompatibleBrowser( enableSafari, enableOpera )
+{
+ var sAgent = navigator.userAgent.toLowerCase() ;
+
+ // Internet Explorer
+ if ( sAgent.indexOf("msie") != -1 && sAgent.indexOf("mac") == -1 && sAgent.indexOf("opera") == -1 )
+ {
+ var sBrowserVersion = navigator.appVersion.match(/MSIE (.\..)/)[1] ;
+ return ( sBrowserVersion >= 5.5 ) ;
+ }
+
+ // Gecko (Opera 9 tries to behave like Gecko at this point).
+ if ( navigator.product == "Gecko" && navigator.productSub >= 20030210 && !( typeof(opera) == 'object' && opera.postError ) )
+ return true ;
+
+ // Opera
+ if ( enableOpera && sAgent.indexOf( 'opera' ) == 0 && parseInt( navigator.appVersion, 10 ) >= 9 )
+ return true ;
+
+ // Safari
+ if ( enableSafari && sAgent.indexOf( 'safari' ) != -1 )
+ return ( sAgent.match( /safari\/(\d+)/ )[1] >= 312 ) ; // Build must be at least 312 (1.3)
+
+ return false ;
+} \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/fckpackager.xml b/httemplate/elements/fckeditor/fckpackager.xml
new file mode 100644
index 0000000..3cae595
--- /dev/null
+++ b/httemplate/elements/fckeditor/fckpackager.xml
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This is the configuration file to be used with FCKpackager to generate the
+ * compressed code files in the "js" folder.
+ *
+ * Please check http://www.fckeditor.net for more info.
+-->
+<Package>
+ <Header><![CDATA[/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This file has been compressed for better performance. The original source
+ * can be found at "editor/_source".
+ */
+]]></Header>
+ <Constants removeDeclaration="false">
+ <Constant name="FCK_STATUS_NOTLOADED" value="0" />
+ <Constant name="FCK_STATUS_ACTIVE" value="1" />
+ <Constant name="FCK_STATUS_COMPLETE" value="2" />
+ <Constant name="FCK_TRISTATE_OFF" value="0" />
+ <Constant name="FCK_TRISTATE_ON" value="1" />
+ <Constant name="FCK_TRISTATE_DISABLED" value="-1" />
+ <Constant name="FCK_UNKNOWN" value="-9" />
+ <Constant name="FCK_TOOLBARITEM_ONLYICON" value="0" />
+ <Constant name="FCK_TOOLBARITEM_ONLYTEXT" value="1" />
+ <Constant name="FCK_TOOLBARITEM_ICONTEXT" value="2" />
+ <Constant name="FCK_EDITMODE_WYSIWYG" value="0" />
+ <Constant name="FCK_EDITMODE_SOURCE" value="1" />
+ </Constants>
+ <PackageFile path="editor/js/fckeditorcode_ie.js">
+ <File path="editor/_source/fckconstants.js" />
+ <File path="editor/_source/fckjscoreextensions.js" />
+ <File path="editor/_source/classes/fckiecleanup.js" />
+ <File path="editor/_source/internals/fckbrowserinfo.js" />
+ <File path="editor/_source/internals/fckurlparams.js" />
+ <File path="editor/_source/classes/fckevents.js" />
+ <File path="editor/_source/internals/fck.js" />
+ <File path="editor/_source/internals/fck_ie.js" />
+ <File path="editor/_source/internals/fckconfig.js" />
+ <File path="editor/_source/internals/fckdebug.js" />
+ <File path="editor/_source/internals/fckdomtools.js" />
+ <File path="editor/_source/internals/fcktools.js" />
+ <File path="editor/_source/internals/fcktools_ie.js" />
+ <File path="editor/_source/fckeditorapi.js" />
+ <File path="editor/_source/classes/fckimagepreloader.js" />
+
+ <File path="editor/_source/internals/fckregexlib.js" />
+ <File path="editor/_source/internals/fcklistslib.js" />
+ <File path="editor/_source/internals/fcklanguagemanager.js" />
+ <File path="editor/_source/internals/fckxhtmlentities.js" />
+ <File path="editor/_source/internals/fckxhtml.js" />
+ <File path="editor/_source/internals/fckxhtml_ie.js" />
+ <File path="editor/_source/internals/fckcodeformatter.js" />
+ <File path="editor/_source/internals/fckundo_ie.js" />
+ <File path="editor/_source/classes/fckeditingarea.js" />
+ <File path="editor/_source/classes/fckkeystrokehandler.js" />
+
+ <File path="editor/_source/internals/fcklisthandler.js" />
+ <File path="editor/_source/classes/fckelementpath.js" />
+ <File path="editor/_source/classes/fckdomrange.js" />
+ <File path="editor/_source/classes/fckdomrange_ie.js" />
+ <File path="editor/_source/classes/fckdocumentfragment_ie.js" />
+ <File path="editor/_source/classes/fckw3crange.js" />
+ <File path="editor/_source/classes/fckenterkey.js" />
+
+ <File path="editor/_source/internals/fckdocumentprocessor.js" />
+ <File path="editor/_source/internals/fckselection.js" />
+ <File path="editor/_source/internals/fckselection_ie.js" />
+
+ <File path="editor/_source/internals/fcktablehandler.js" />
+ <File path="editor/_source/internals/fcktablehandler_ie.js" />
+ <File path="editor/_source/classes/fckxml_ie.js" />
+ <File path="editor/_source/classes/fckstyledef.js" />
+ <File path="editor/_source/classes/fckstyledef_ie.js" />
+ <File path="editor/_source/classes/fckstylesloader.js" />
+
+ <File path="editor/_source/commandclasses/fcknamedcommand.js" />
+ <File path="editor/_source/commandclasses/fck_othercommands.js" />
+ <File path="editor/_source/commandclasses/fckspellcheckcommand_ie.js" />
+ <File path="editor/_source/commandclasses/fcktextcolorcommand.js" />
+ <File path="editor/_source/commandclasses/fckpasteplaintextcommand.js" />
+ <File path="editor/_source/commandclasses/fckpastewordcommand.js" />
+ <File path="editor/_source/commandclasses/fcktablecommand.js" />
+ <File path="editor/_source/commandclasses/fckstylecommand.js" />
+ <File path="editor/_source/commandclasses/fckfitwindow.js" />
+ <File path="editor/_source/internals/fckcommands.js" />
+
+ <File path="editor/_source/classes/fckpanel.js" />
+ <File path="editor/_source/classes/fckicon.js" />
+ <File path="editor/_source/classes/fcktoolbarbuttonui.js" />
+ <File path="editor/_source/classes/fcktoolbarbutton.js" />
+ <File path="editor/_source/classes/fckspecialcombo.js" />
+ <File path="editor/_source/classes/fcktoolbarspecialcombo.js" />
+ <File path="editor/_source/classes/fcktoolbarfontscombo.js" />
+ <File path="editor/_source/classes/fcktoolbarfontsizecombo.js" />
+ <File path="editor/_source/classes/fcktoolbarfontformatcombo.js" />
+ <File path="editor/_source/classes/fcktoolbarstylecombo.js" />
+ <File path="editor/_source/classes/fcktoolbarpanelbutton.js" />
+ <File path="editor/_source/internals/fcktoolbaritems.js" />
+ <File path="editor/_source/classes/fcktoolbar.js" />
+ <File path="editor/_source/classes/fcktoolbarbreak_ie.js" />
+ <File path="editor/_source/internals/fcktoolbarset.js" />
+ <File path="editor/_source/internals/fckdialog.js" />
+ <File path="editor/_source/internals/fckdialog_ie.js" />
+
+ <File path="editor/_source/classes/fckmenuitem.js" />
+ <File path="editor/_source/classes/fckmenublock.js" />
+ <File path="editor/_source/classes/fckmenublockpanel.js" />
+ <File path="editor/_source/classes/fckcontextmenu.js" />
+ <File path="editor/_source/internals/fck_contextmenu.js" />
+
+ <File path="editor/_source/classes/fckplugin.js" />
+ <File path="editor/_source/internals/fckplugins.js" />
+ </PackageFile>
+
+ <PackageFile path="editor/js/fckeditorcode_gecko.js">
+ <File path="editor/_source/fckconstants.js" />
+ <File path="editor/_source/fckjscoreextensions.js" />
+ <File path="editor/_source/internals/fckbrowserinfo.js" />
+ <File path="editor/_source/internals/fckurlparams.js" />
+ <File path="editor/_source/classes/fckevents.js" />
+ <File path="editor/_source/internals/fck.js" />
+ <File path="editor/_source/internals/fck_gecko.js" />
+ <File path="editor/_source/internals/fckconfig.js" />
+ <File path="editor/_source/internals/fckdebug.js" />
+ <File path="editor/_source/internals/fckdomtools.js" />
+ <File path="editor/_source/internals/fcktools.js" />
+ <File path="editor/_source/internals/fcktools_gecko.js" />
+ <File path="editor/_source/fckeditorapi.js" />
+ <File path="editor/_source/classes/fckimagepreloader.js" />
+
+ <File path="editor/_source/internals/fckregexlib.js" />
+ <File path="editor/_source/internals/fcklistslib.js" />
+ <File path="editor/_source/internals/fcklanguagemanager.js" />
+ <File path="editor/_source/internals/fckxhtmlentities.js" />
+ <File path="editor/_source/internals/fckxhtml.js" />
+ <File path="editor/_source/internals/fckxhtml_gecko.js" />
+ <File path="editor/_source/internals/fckcodeformatter.js" />
+ <File path="editor/_source/internals/fckundo_gecko.js" />
+ <File path="editor/_source/classes/fckeditingarea.js" />
+ <File path="editor/_source/classes/fckkeystrokehandler.js" />
+
+ <File path="editor/_source/internals/fcklisthandler.js" />
+ <File path="editor/_source/classes/fckelementpath.js" />
+ <File path="editor/_source/classes/fckdomrange.js" />
+ <File path="editor/_source/classes/fckdomrange_gecko.js" />
+ <File path="editor/_source/classes/fckdocumentfragment_gecko.js" />
+ <File path="editor/_source/classes/fckw3crange.js" />
+ <File path="editor/_source/classes/fckenterkey.js" />
+
+ <File path="editor/_source/internals/fckdocumentprocessor.js" />
+ <File path="editor/_source/internals/fckselection.js" />
+ <File path="editor/_source/internals/fckselection_gecko.js" />
+
+ <File path="editor/_source/internals/fcktablehandler.js" />
+ <File path="editor/_source/internals/fcktablehandler_gecko.js" />
+ <File path="editor/_source/classes/fckxml_gecko.js" />
+ <File path="editor/_source/classes/fckstyledef.js" />
+ <File path="editor/_source/classes/fckstyledef_gecko.js" />
+ <File path="editor/_source/classes/fckstylesloader.js" />
+
+ <File path="editor/_source/commandclasses/fcknamedcommand.js" />
+ <File path="editor/_source/commandclasses/fck_othercommands.js" />
+ <File path="editor/_source/commandclasses/fckspellcheckcommand_gecko.js" />
+ <File path="editor/_source/commandclasses/fcktextcolorcommand.js" />
+ <File path="editor/_source/commandclasses/fckpasteplaintextcommand.js" />
+ <File path="editor/_source/commandclasses/fckpastewordcommand.js" />
+ <File path="editor/_source/commandclasses/fcktablecommand.js" />
+ <File path="editor/_source/commandclasses/fckstylecommand.js" />
+ <File path="editor/_source/commandclasses/fckfitwindow.js" />
+ <File path="editor/_source/internals/fckcommands.js" />
+
+ <File path="editor/_source/classes/fckpanel.js" />
+ <File path="editor/_source/classes/fckicon.js" />
+ <File path="editor/_source/classes/fcktoolbarbuttonui.js" />
+ <File path="editor/_source/classes/fcktoolbarbutton.js" />
+ <File path="editor/_source/classes/fckspecialcombo.js" />
+ <File path="editor/_source/classes/fcktoolbarspecialcombo.js" />
+ <File path="editor/_source/classes/fcktoolbarfontscombo.js" />
+ <File path="editor/_source/classes/fcktoolbarfontsizecombo.js" />
+ <File path="editor/_source/classes/fcktoolbarfontformatcombo.js" />
+ <File path="editor/_source/classes/fcktoolbarstylecombo.js" />
+ <File path="editor/_source/classes/fcktoolbarpanelbutton.js" />
+ <File path="editor/_source/internals/fcktoolbaritems.js" />
+ <File path="editor/_source/classes/fcktoolbar.js" />
+ <File path="editor/_source/classes/fcktoolbarbreak_gecko.js" />
+ <File path="editor/_source/internals/fcktoolbarset.js" />
+ <File path="editor/_source/internals/fckdialog.js" />
+ <File path="editor/_source/internals/fckdialog_gecko.js" />
+
+ <File path="editor/_source/classes/fckmenuitem.js" />
+ <File path="editor/_source/classes/fckmenublock.js" />
+ <File path="editor/_source/classes/fckmenublockpanel.js" />
+ <File path="editor/_source/classes/fckcontextmenu.js" />
+ <File path="editor/_source/internals/fck_contextmenu.js" />
+
+ <File path="editor/_source/classes/fckplugin.js" />
+ <File path="editor/_source/internals/fckplugins.js" />
+ </PackageFile>
+
+</Package> \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/fckstyles.xml b/httemplate/elements/fckeditor/fckstyles.xml
new file mode 100644
index 0000000..bfd80e7
--- /dev/null
+++ b/httemplate/elements/fckeditor/fckstyles.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This is the sample style definitions file. It makes the styles combo
+ * completely customizable.
+ *
+ * See FCKConfig.StylesXmlPath in the configuration file.
+-->
+<Styles>
+ <Style name="Image on Left" element="img">
+ <Attribute name="style" value="padding: 5px; margin-right: 5px" />
+ <Attribute name="border" value="2" />
+ <Attribute name="align" value="left" />
+ </Style>
+ <Style name="Image on Right" element="img">
+ <Attribute name="style" value="padding: 5px; margin-left: 5px" />
+ <Attribute name="border" value="2" />
+ <Attribute name="align" value="right" />
+ </Style>
+ <Style name="Custom Bold" element="span">
+ <Attribute name="style" value="font-weight: bold;" />
+ </Style>
+ <Style name="Custom Italic" element="em" />
+ <Style name="Title" element="span">
+ <Attribute name="class" value="Title" />
+ </Style>
+ <Style name="Code" element="span">
+ <Attribute name="class" value="Code" />
+ </Style>
+ <Style name="Title H3" element="h3" />
+ <Style name="Custom Ruler" element="hr">
+ <Attribute name="size" value="1" />
+ <Attribute name="color" value="#ff0000" />
+ </Style>
+</Styles> \ No newline at end of file
diff --git a/httemplate/elements/fckeditor/fcktemplates.xml b/httemplate/elements/fckeditor/fcktemplates.xml
new file mode 100644
index 0000000..8761062
--- /dev/null
+++ b/httemplate/elements/fckeditor/fcktemplates.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * This is the sample templates definitions file. It makes the "templates"
+ * command completely customizable.
+ *
+ * See FCKConfig.TemplatesXmlPath in the configuration file.
+-->
+<Templates imagesBasePath="fck_template/images/">
+ <Template title="Image and Title" image="template1.gif">
+ <Description>One main image with a title and text that surround the image.</Description>
+ <Html>
+ <![CDATA[
+ <img style="MARGIN-RIGHT: 10px" height="100" alt="" width="100" align="left"/>
+ <h3>Type the title here</h3>
+ Type the text here
+ ]]>
+ </Html>
+ </Template>
+ <Template title="Strange Template" image="template2.gif">
+ <Description>A template that defines two colums, each one with a title, and some text.</Description>
+ <Html>
+ <![CDATA[
+ <table cellspacing="0" cellpadding="0" width="100%" border="0">
+ <tbody>
+ <tr>
+ <td width="50%">
+ <h3>Title 1</h3>
+ </td>
+ <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </td>
+ <td width="50%">
+ <h3>Title 2</h3>
+ </td>
+ </tr>
+ <tr>
+ <td>Text 1</td>
+ <td>&nbsp;</td>
+ <td>Text 2</td>
+ </tr>
+ </tbody>
+ </table>
+ More text goes here.
+ ]]>
+ </Html>
+ </Template>
+ <Template title="Text and Table" image="template3.gif">
+ <Description>A title with some text and a table.</Description>
+ <Html>
+ <![CDATA[
+ <table align="left" width="80%" border="0" cellspacing="0" cellpadding="0"><tr><td>
+ <h3>Title goes here</h3>
+ <p>
+ <table style="FLOAT: right" cellspacing="0" cellpadding="0" width="150" border="1">
+ <tbody>
+ <tr>
+ <td align="center" colspan="3"><strong>Table title</strong></td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ </tr>
+ </tbody>
+ </table>
+ Type the text here</p>
+ </td></tr></table>
+ ]]>
+ </Html>
+ </Template>
+</Templates>
diff --git a/httemplate/elements/file-upload.html b/httemplate/elements/file-upload.html
new file mode 100644
index 0000000..c8b026d
--- /dev/null
+++ b/httemplate/elements/file-upload.html
@@ -0,0 +1,74 @@
+<SCRIPT TYPE="text/javascript">
+
+ function doUpload(form, callback) {
+ var name = 'form' + Math.floor(Math.random() * 99999); // perlize?
+ var d = document.createElement('DIV');
+ d.innerHTML = '<iframe style="display:none" src="about:blank" ' +
+ 'id="' + name + '" ' +
+ 'name="' + name + '" ' +
+ 'onload="uploadComplete(\'' + name + '\')">' +
+ '</iframe>';
+ document.body.appendChild(d);
+
+ var i = document.getElementById(name);
+ if (callback && typeof(callback) == 'function') {
+ i.onComplete = callback;
+ }
+
+ form.setAttribute('target', name);
+ return true;
+ }
+
+ function uploadComplete(id) {
+ var i = document.getElementById(id);
+ if (i.contentDocument) {
+ var d = i.contentDocument;
+ } else if (i.contentWindow) {
+ var d = i.contentWindow.document;
+ } else {
+ var d = window.frames[id].document;
+ }
+ if (d.location.href == "about:blank") {
+ return;
+ }
+
+ document.getElementById('r').innerHTML = d.body.innerHTML;
+ if (typeof(i.onComplete) == 'function') {
+ var p;
+ if (p = d.body.innerHTML.indexOf("File Upload Successful ") >= 0) {
+ var v = d.body.innerHTML.substr(p+24);
+ var u = document.getElementById('uploaded_files');
+ v = v.substr(0, v.indexOf(';'));
+ u.value = v;
+ i.onComplete(true, '');
+ }else{
+ i.onComplete(false, d.body.innerHTML);
+ }
+ }
+ }
+
+</SCRIPT>
+
+<INPUT TYPE="hidden" NAME="uploaded_files" ID="uploaded_files" VALUE="" />
+
+<INPUT TYPE="hidden" NAME="upload_fields" VALUE="<% join(',', @field) %>" />
+
+% foreach (@field) {
+ <TR>
+ <TH ALIGN="right"><% shift @label %></TH>
+ <TD><INPUT TYPE="file" NAME="<% $_ %>" /></TD>
+ </TR>
+% }
+
+<DIV STYLE="display:<% $param{debug} ? 'visible' : 'none' %>">
+ Debugging: <PRE ID="r"></PRE>
+</DIV>
+
+<%init>
+
+my %param = @_;
+
+my @label = ref($param{'label'}) ? @{$param{'label'}} : ($param{'label'});
+my @field = ref($param{'field'}) ? @{$param{'field'}} : ($param{'field'});
+
+</%init>
diff --git a/httemplate/elements/footer.html b/httemplate/elements/footer.html
new file mode 100644
index 0000000..32d1219
--- /dev/null
+++ b/httemplate/elements/footer.html
@@ -0,0 +1,5 @@
+ </TD>
+ </TR>
+ </TABLE>
+ </BODY>
+</HTML>
diff --git a/httemplate/elements/form-file_upload.html b/httemplate/elements/form-file_upload.html
new file mode 100644
index 0000000..4ab70ad
--- /dev/null
+++ b/httemplate/elements/form-file_upload.html
@@ -0,0 +1,93 @@
+<%doc>
+
+Example:
+
+ <% include( '/elements/form-file_upload.html',
+
+ 'name' => 'form_name',
+ 'action' => 'process/target.cgi', #progress-init target
+ 'fields' => [ 'other', 'form', 'fields' ],
+ 'num_files' => 1, #or more
+
+ 'url' => $url
+ #AND/OR
+ 'message' => 'Message',
+
+ #optional
+ 'key' => 'unique_key', #for using more than once on a page
+ )
+
+% #...
+
+% # num_files=>1
+ include( '/elements/file-upload.html',
+ 'field' => 'element',
+ 'label' => 'Label',
+ )
+
+% # OR
+
+% # num_files=>2 # or more
+ include( '/elements/file-upload.html',
+ 'field' => [ 'element', 'element2', ], #etc.
+ 'label' => [ 'Label', 'Label2', ], #etc.
+ )
+
+
+%>
+
+</%doc>
+
+<% include( '/elements/progress-init.html',
+ $opt{name},
+ $opt{fields},
+ $opt{action},
+ $msg_or_url,
+ $opt{key},
+ )
+%>
+
+<SCRIPT>
+
+ function <% $opt{key} %>gotUploaded(success, message) {
+
+ var uploaded = document.getElementById('uploaded_files');
+ var a = uploaded.value.split(',');
+ if (success && uploaded.value.split(',').length == <% $opt{num_files} %>){
+ process();
+ }else{
+ var p = document.getElementById('uploadError');
+ p.innerHTML='<FONT SIZE="+1" COLOR="#ff0000">Error: '+message+'</FONT><BR><BR>';
+ p.style='display:visible';
+ return false;
+ }
+
+ }
+
+</SCRIPT>
+
+<div style="display:none:" id="uploadError"></div>
+
+<FORM NAME = "<% $opt{name} %>"
+ ACTION = "<% $fsurl %>misc/file-upload.html"
+ METHOD = "POST"
+ ENCTYPE = "multipart/form-data"
+ onSubmit = "return doUpload(this, <% $opt{key} %>gotUploaded)"
+>
+
+<%init>
+
+#my( $formname, $fields, $action, $url_or_message, $key ) = @_;
+my %opt = ref($_[0]) ? %{ $_[0] } : @_;
+
+my $key = exists $opt{key} ? $opt{key} : '';
+
+push @{ $opt{fields} }, 'uploaded_files';
+
+my $msg_or_url = $opt{message}
+ ? { 'message' => $opt{message},
+ 'url' => $opt{url},
+ }
+ : $opt{url};
+
+</%init>
diff --git a/httemplate/elements/freeside.css b/httemplate/elements/freeside.css
new file mode 100644
index 0000000..c310e2f
--- /dev/null
+++ b/httemplate/elements/freeside.css
@@ -0,0 +1,16 @@
+* {
+ font-family: Arial, Verdana, Helvetica, sans-serif;
+ /* font-family: Verdana, Arial, Helvetica, sans-serif; */
+}
+
+A:link IMG, A:visited { border-style: none }
+/* A:focus {text-decoration: underline } */
+
+a:link, a:visited {
+ /* text-decoration: none; */
+ color: #000000;
+}
+/* a:hover { text-decoration: underline } */
+
+/* a:focus { background-color: #ccccee } */
+
diff --git a/httemplate/elements/header-minimal.html b/httemplate/elements/header-minimal.html
new file mode 100644
index 0000000..f74a9cc
--- /dev/null
+++ b/httemplate/elements/header-minimal.html
@@ -0,0 +1,19 @@
+%
+% my($title, $menubar) = ( shift, shift ); #$menubar is unused here though
+% my $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
+% my $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section
+% my $conf = new FS::Conf;
+%
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+ <HEAD>
+ <TITLE>
+ <% $title %>
+ </TITLE>
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+ <% $head %>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8" <% $etc %>>
diff --git a/httemplate/elements/header-popup.html b/httemplate/elements/header-popup.html
new file mode 100644
index 0000000..68be108
--- /dev/null
+++ b/httemplate/elements/header-popup.html
@@ -0,0 +1,24 @@
+%
+% my($title, $menubar) = ( shift, shift ); #$menubar is unused here though
+% my $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
+% my $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section
+% my $conf = new FS::Conf;
+%
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+ <HEAD>
+ <TITLE>
+ <% $title %>
+ </TITLE>
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+ <% $head %>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8" <% $etc %>>
+ <link href="<%$fsurl%>elements/freeside.css" type="text/css" rel="stylesheet">
+ <FONT SIZE=6>
+ <CENTER><% $title %></CENTER>
+ </FONT>
+ <BR><!--<BR>-->
diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html
new file mode 100644
index 0000000..8e902f0
--- /dev/null
+++ b/httemplate/elements/header.html
@@ -0,0 +1,299 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+ <HEAD>
+ <TITLE>
+ <% $title %>
+ </TITLE>
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+
+ <% include('menu.html', 'freeside_baseurl' => $fsurl,
+ 'position' => $menu_position,
+ ) |n
+ %>
+
+ <% include('init_overlib.html') |n %>
+
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_cust (what) {
+ if ( what.value == '(cust #, name, company or phone)' )
+ what.value = '';
+ }
+
+ function clearhint_search_address2 (what) {
+ if ( what.value == '(Unit #)' )
+ what.value = '';
+ }
+
+ function clearhint_search_invoice (what) {
+ if ( what.value == '(inv #)' )
+ what.value = '';
+ }
+
+ function clearhint_search_svc (what) {
+ if ( what.value == '(user, email, ip, mac, or domain)' )
+ what.value = '';
+ }
+
+ function clearhint_search_ticket (what) {
+ if ( what.value == '(ticket #, subject, email or fulltext:text)' )
+ what.value = '';
+ }
+ </SCRIPT>
+
+ <% $head |n %>
+
+ </HEAD>
+ <BODY <% $menu_position eq 'left' ? qq( BACKGROUND="${fsurl}images/background-cheat.png" ) : ' BGCOLOR="#e8e8e8" ' %> <% $etc |n %> STYLE="margin-top:0; margin-bottom:0; margin-left:0; margin-right:0">
+ <table width="100%" CELLPADDING=0 CELLSPACING=0 STYLE="padding-left:0; padding-right:4">
+ <tr>
+ <td rowspan=2 BGCOLOR="#ffffff"><IMG BORDER=0 ALT="freeside" SRC="<%$fsurl%>view/REAL_logo.cgi"></td>
+ <td align=left rowspan=2 BGCOLOR="#ffffff"> <!-- valign="top" -->
+ <font size=6><% $company_name || 'ExampleCo' %></font>
+ </td>
+ <td align=right valign=top BGCOLOR="#ffffff"><FONT SIZE="-1">Logged in as <b><% getotaker %>&nbsp;</b><br></FONT><FONT SIZE="-2"><a href="<%$fsurl%>pref/pref.html">Preferences</a>&nbsp;<BR></FONT>
+ </td>
+ </tr>
+ <tr>
+ <td align=right valign=bottom BGCOLOR="#ffffff">
+
+ <table>
+ <tr>
+ <td align=right BGCOLOR="#ffffff">
+ <FONT SIZE="-2">
+ <% include('/elements/popup_link.html',
+ 'action' => $fsurl.'docs/about.html',
+ 'label' => 'Freeside',
+ 'actionlabel' => 'About',
+ 'width' => 300,
+ 'height' => 360,
+ 'color' => '#7e0079',
+ 'scrolling' => 'no',
+ ) |n
+ %>&nbsp;v<% $FS::VERSION %><BR>
+ <A HREF="<% $conf->config('support-key') ? "http://www.freeside.biz/mediawiki/index.php/Supported:Documentation" : "http://www.freeside.biz/mediawiki/index.php/Freeside:1.9:Documentation" %>" TARGET="_blank">Documentation</A><BR>
+ </FONT>
+ </td>
+% if ( $conf->config('ticket_system') eq 'RT_Internal' ) {
+% eval "use RT;";
+
+ <td bgcolor=#000000></td>
+ <td align=left>
+ <FONT SIZE="-2">
+ <A HREF="http://www.bestpractical.com/rt" TARGET="_blank">RT<A>&nbsp;v<% $RT::VERSION %><BR>
+ <A HREF="http://wiki.bestpractical.com/" TARGET="_blank">Documentation</A><BR>
+ </FONT>
+ </td>
+% }
+
+
+ </tr>
+ </table>
+
+ </td>
+ </tr>
+ </table>
+
+<style type="text/css">
+input.fsblackbutton {
+ background-color:#333333;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff333333',EndColorStr='#ff666666')
+}
+
+input.fsblackbuttonselected {
+ background-color:#7e0079;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff330033',EndColorStr='#ff7e0079')
+}
+</style>
+
+ <TABLE WIDTH="100%" CELLSPACING=0 CELLPADDING=0>
+ <TR>
+ <TD COLSPAN=6 WIDTH="100%" STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gradient.png" HEIGHT="13" WIDTH="100%"></TD>
+ </TR>
+
+% if ( $menu_position eq 'top' ) {
+
+ <TR>
+
+ <TD COLSPAN="6" WIDTH="100%" STYLE="padding:0">
+ <SCRIPT TYPE="text/javascript">
+ document.write(myBar);
+ </SCRIPT>
+ </TD>
+
+ </TR>
+
+ <TR>
+ <TD COLSPAN="6" WIDTH="100%" HEIGHT="2px" STYLE="padding:0" BGCOLOR="#000000">
+ </TD>
+ </TR>
+
+ <TR>
+ <TD COLSPAN="6" WIDTH="100%" HEIGHT="4px" STYLE="padding:0" BGCOLOR="#000000">
+ </TD>
+ </TR>
+
+% }
+
+ <TR>
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+ <FORM ACTION="<%$fsurl%>edit/cust_main.cgi" METHOD="GET" STYLE="margin:0">
+ <INPUT TYPE="submit" VALUE="New customer" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="vertical-align:bottom; font-size:100%">
+ </FORM>
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+% if ( $curuser->access_right('List customers') ) {
+ <FORM ACTION="<%$fsurl%>search/cust_main.cgi" METHOD="GET" STYLE="margin:0">
+ <INPUT NAME="search_cust" TYPE="text" VALUE="(cust #, name, company or phone)" SIZE="28" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
+ <A HREF="<%$fsurl%>search/report_cust_main.html" STYLE="color: #ffffff; font-size: 70%">Advanced</A>
+ <INPUT TYPE="submit" VALUE="Search customers" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+ </FORM>
+% }
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="center">
+% if ( $conf->exists('address2-search') ) {
+ <FORM ACTION="<%$fsurl%>search/cust_main.cgi" METHOD="GET" STYLE="margin:0;display:inline">
+ <INPUT TYPE="hidden" NAME="address2_on" VALUE="1">
+ <INPUT NAME="address2_text" TYPE="text" VALUE="(Unit #)" SIZE="4" onFocus="clearhint_search_address2(this);" onClick="clearhint_search_address2(this);" STYLE="vertical-align:bottom;text-align:right;margin-bottom:1px">
+ <BR>
+ <INPUT TYPE="submit" VALUE="Search units" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%;padding-left:2px;padding-right:2px">
+ </FORM>
+% }
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+% if ( $curuser->access_right('View invoices') ) {
+
+ <FORM ACTION="<%$fsurl%>search/cust_bill.html" METHOD="GET" STYLE="margin:0;display:inline">
+ <INPUT NAME="invnum" TYPE="text" VALUE="(inv #)" SIZE="4" onFocus="clearhint_search_invoice(this);" onClick="clearhint_search_invoice(this);" STYLE="vertical-align:bottom;text-align:right;margin-bottom:1px">
+% if ( $curuser->access_right('List invoices') ) {
+
+ <A HREF="<%$fsurl%>search/report_cust_bill.html" STYLE="color: #ffffff; font-size: 70%">Advanced</A>
+% }
+
+ <BR>
+ <INPUT TYPE="submit" VALUE="Search invoices" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+ </FORM>
+% }
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right">
+ <FORM ACTION="<%$fsurl%>search/cust_svc.html" METHOD="GET" STYLE="margin:0">
+ <INPUT NAME="search_svc" TYPE="text" VALUE="(user, email, ip, mac, or domain)" SIZE="26" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
+ <A NOTYET="<%$fsurl%>search/svc_Smarter.html" STYLE="color: #000000; font-size: 70%">Advanced</A>
+ <INPUT TYPE="submit" VALUE="Search services" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+ </FORM>
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right" STYLE="padding-right:4px">
+% if ( $conf->config("ticket_system") ) {
+ <FORM ACTION="<% FS::TicketSystem->baseurl %>index.html" METHOD="GET" STYLE="margin:0">
+ <INPUT NAME="q" TYPE="text" VALUE="(ticket #, subject, email or fulltext:text)" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" STYLE="vertical-align:bottom;text-align:right"><BR>
+ <A HREF="<% FS::TicketSystem->baseurl %>Search/Build.html" STYLE="color: #ffffff; font-size: 70%">Advanced</A>
+ <INPUT TYPE="submit" VALUE="Search tickets" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%;padding-left:2px;padding-right:2px">
+ </FORM>
+% }
+ </TD>
+
+ </TR>
+ </TABLE>
+
+ <TABLE WIDTH="100%" HEIGHT="100%" CELLSPACING=0 CELLPADDING=4>
+
+ <TR>
+
+% if ( $menu_position eq 'left' ) {
+
+ <TD BGCOLOR="#000000" STYLE="padding:0" WIDTH="154"></TD>
+ <TD STYLE="padding:0" WIDTH="13"><IMG BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gray-corner.png"></TD>
+
+% }
+
+ <TD STYLE="padding:0" WIDTH="100%"><IMG BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gray-top.png" HEIGHT="13" WIDTH="100%"></TD>
+
+ </TR>
+
+ <TR HEIGHT="100%">
+
+% if ( $menu_position eq 'left' ) {
+
+ <TD BGCOLOR="#000000" ALIGN="left" HEIGHT="100%" WIDTH="154" VALIGN="top" ALIGN="right">
+ <SCRIPT TYPE="text/javascript">
+ document.write(myBar);
+ </SCRIPT>
+ <BR>
+ <IMG SRC="<%$fsurl%>images/32clear.gif" HEIGHT="1" WIDTH="154">
+
+ </TD>
+ <TD STYLE="padding:0" HEIGHT="100%" WIDTH=13 VALIGN="top"><IMG WIDTH="13" HEIGHT="100%" BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gray-side.png"></TD>
+
+% }
+
+ <TD BGCOLOR="#e8e8e8" HEIGHT="100%" VALIGN="top"> <!-- WIDTH="100%"> -->
+
+ <FONT SIZE=6>
+ <% $title %>
+ </FONT>
+
+% unless ( $nobr ) {
+ <BR><BR>
+% }
+
+ <% $menubar !~ /^\s*$/ ? "$menubar<BR><BR>" : '' %>
+<%init>
+
+my( $title, $menubar, $etc, $head ) = ( '', '', '', '' );
+my( $nobr ) = ( 0 );
+if ( ref($_[0]) ) {
+ my $opt = shift;
+ $title = $opt->{title};
+ $menubar = $opt->{menubar};
+ $etc = $opt->{etc};
+ $head = $opt->{head};
+ $nobr = $opt->{nobr};
+} else {
+ ($title, $menubar) = ( shift, shift );
+ $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
+ $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section
+}
+
+my $conf = new FS::Conf;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $menu_position = $curuser->option('menu_position')
+ || 'top'; #new default for 1.9
+
+my $company_name;
+my @agentnums = $curuser->agentnums;
+if ( scalar(@agentnums) == 1 ) {
+ $company_name = $conf->config('company_name', $agentnums[0] );
+} else {
+ $company_name = $conf->config('company_name');
+}
+
+</%init>
diff --git a/httemplate/elements/hidden.html b/httemplate/elements/hidden.html
new file mode 100644
index 0000000..8311081
--- /dev/null
+++ b/httemplate/elements/hidden.html
@@ -0,0 +1,11 @@
+<INPUT TYPE = "hidden"
+ NAME = "<% $opt{field} %>"
+ ID = "<% $opt{field} %>"
+ VALUE = "<% $opt{curr_value} || $opt{value} |h %>"
+>
+
+<%init>
+
+my %opt = @_;
+
+</%init>
diff --git a/httemplate/elements/htmlarea.html b/httemplate/elements/htmlarea.html
new file mode 100644
index 0000000..f27c4b5
--- /dev/null
+++ b/httemplate/elements/htmlarea.html
@@ -0,0 +1,36 @@
+<%doc>
+
+Example:
+
+ include('/elements/htmlarea.html',
+ 'field' => 'fieldname',
+ 'curr_value' => $curr_value,
+ 'height' => 800,
+ );
+
+</%doc>
+
+% #init
+<SCRIPT TYPE="text/javascript" src="<% $p %>elements/fckeditor/fckeditor.js">
+</SCRIPT>
+
+% #editor
+<SCRIPT TYPE="text/javascript">
+
+ var oFCKeditor = new FCKeditor('<% $opt{'field'} %>');
+ oFCKeditor.Value = <% $opt{'curr_value'} |js_string %>;
+
+ oFCKeditor.BasePath = '<% $p %>elements/fckeditor/';
+ oFCKeditor.Config['SkinPath'] = '<% $p %>elements/fckeditor/editor/skins/silver/';
+ oFCKeditor.Height = '<% $opt{'height'} || 420 %>';
+ oFCKeditor.Config['StartupFocus'] = true;
+
+ oFCKeditor.Create();
+
+</SCRIPT>
+
+<%init>
+
+my %opt = @_;
+
+</%init>
diff --git a/httemplate/elements/iframecontentmws.js b/httemplate/elements/iframecontentmws.js
new file mode 100644
index 0000000..f2a91d2
--- /dev/null
+++ b/httemplate/elements/iframecontentmws.js
@@ -0,0 +1,59 @@
+/*
+ iframecontentmws.js - Foteos Macrides (author and copyright holder)
+ Initial: October 10, 2004 - Last Revised: January 26, 2008
+ Scripts for using HTML documents as iframe content in overlibmws popups.
+
+ See http://www.macridesweb.com/oltest/IFRAME.html
+ and http://www.macridesweb.com/oltest/AJAX.html#ajaxex3
+ for more information.
+*/
+
+/*
+ Use as lead argument in overlib or overlb2 calls. Include WRAP and
+ TEXTPADDING,0 in the call to ensure that the width arg is respected (unless
+ the CAPTION plus CLOSETEXT widths add up to more than the width arg, in which
+ case you should increase the width arg). The name arg should be a unique
+ string for each popup with iframe content in the document. The frameborder
+ arg should be 1 (browser default if omitted) or 0. The scrolling arg should
+ be 'auto' (default if omitted), 'yes' or 'no'.
+*/
+function OLiframeContent(src, width, height, name, frameborder, scrolling) {
+
+ /* stupid safari iframe location caching... */
+ var d = new Date();
+ var unique = d.getTime() + '' + Math.floor(1000 * Math.random());
+ name = name + '' + unique;
+
+ return ('<iframe src="'+src+'" width="'+width+'" height="'+height+'"'
+ +(name!=null?' name="'+name+'" id="'+name+'"':'')
+ +(frameborder!=null?' frameborder="'+frameborder+'"':'')
+ +' scrolling="'+(scrolling!=null?scrolling:'auto')
+ +'"><div>[iframe not supported]</div></iframe>');
+}
+
+/*
+ Swap the src if we are iframe content. The name arg should be the same
+ string as in the OLiframeContent function for the popup. The src arg is
+ a partial, relative, or complete URL for the document to be swapped in.
+*/
+function OLswapIframeSrc(name, src){
+ if(parent==self){
+ alert(src+'\n\n is only for iframe content');
+ return;
+ }
+ var o=parent.OLgetRef(name);
+ if(o)o.src=src;
+ else alert(src+'\n\n is not available');
+}
+
+/*
+ Emulate the Back button if we are iframe content. Use only in documents
+ which are swapped in by using the OLswapIframeSrc function.
+*/
+function OLiframeBack(){
+ if(parent==self){
+ alert('This feature is only for iframe content');
+ return;
+ }
+ history.back();
+}
diff --git a/httemplate/elements/init_calendar.html b/httemplate/elements/init_calendar.html
new file mode 100644
index 0000000..04b0135
--- /dev/null
+++ b/httemplate/elements/init_calendar.html
@@ -0,0 +1,5 @@
+<LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2">
+
+% foreach (qw( _stripped -en -setup )) {
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar<%$_%>.js"></SCRIPT>
+% }
diff --git a/httemplate/elements/init_overlib.html b/httemplate/elements/init_overlib.html
new file mode 100644
index 0000000..d27ca3b
--- /dev/null
+++ b/httemplate/elements/init_overlib.html
@@ -0,0 +1,9 @@
+% for my $file (@files) {
+ <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/<%$file%>.js"></SCRIPT>
+% }
+<%init>
+
+my @files = map "overlibmws$_", ( '', qw( _iframe _draggable _crossframe ) );
+push @files, map { "${_}contentmws" } qw( iframe ajax );
+
+</%init>
diff --git a/httemplate/elements/input-text.html b/httemplate/elements/input-text.html
new file mode 100644
index 0000000..9db0643
--- /dev/null
+++ b/httemplate/elements/input-text.html
@@ -0,0 +1,44 @@
+<% $opt{'prefix'} %><INPUT TYPE = "<% $opt{type} || 'text' %>"
+ NAME = "<% $opt{field} %>"
+ ID = "<% $opt{id} %>"
+ VALUE = "<% $value |h %>"
+ <% $size %>
+ <% $maxlength %>
+ <% $style %>
+ <% $opt{disabled} %>
+ <% $onchange %>
+ ><% $opt{'postfix'} %>
+<%init>
+
+my %opt = @_;
+
+my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value};
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $size = $opt{'size'}
+ ? 'SIZE="'. $opt{'size'}. '"'
+ : '';
+
+my $maxlength = $opt{'maxlength'}
+ ? 'MAXLENGTH="'. $opt{'maxlength'}. '"'
+ : '';
+
+$opt{'disabled'} = &{ $opt{'disabled'} }( \%opt )
+ if ref($opt{'disabled'}) eq 'CODE';
+$opt{'disabled'} = 'DISABLED'
+ if $opt{'disabled'} && $opt{'disabled'} !~ /disabled/i; # uuh... yeah?
+
+my @style = ();
+
+push @style, 'text-align: '. $opt{'text-align'}
+ if $opt{'text-align'};
+
+push @style, 'background-color: #dddddd'
+ if $opt{'disabled'};
+
+my $style = scalar(@style) ? 'STYLE="'. join(';', @style). '"' : '';
+
+</%init>
diff --git a/httemplate/elements/jsrsClient.js b/httemplate/elements/jsrsClient.js
new file mode 100644
index 0000000..3a2572c
--- /dev/null
+++ b/httemplate/elements/jsrsClient.js
@@ -0,0 +1,356 @@
+//
+// jsrsClient.js - javascript remote scripting client include
+//
+// Author: Brent Ashley [jsrs@megahuge.com]
+//
+// make asynchronous remote calls to server without client page refresh
+//
+// see license.txt for copyright and license information
+
+/*
+see history.txt for full history
+2.0 26 Jul 2001 - added POST capability for IE/MOZ
+2.2 10 Aug 2003 - added Opera support
+2.3(beta) 10 Oct 2003 - added Konqueror support - **needs more testing**
+*/
+
+// callback pool needs global scope
+var jsrsContextPoolSize = 0;
+var jsrsContextMaxPool = 10;
+var jsrsContextPool = new Array();
+var jsrsBrowser = jsrsBrowserSniff();
+var jsrsPOST = true;
+var containerName;
+
+// constructor for context object
+function jsrsContextObj( contextID ){
+
+ // properties
+ this.id = contextID;
+ this.busy = true;
+ this.callback = null;
+ this.container = contextCreateContainer( contextID );
+
+ // methods
+ this.GET = contextGET;
+ this.POST = contextPOST;
+ this.getPayload = contextGetPayload;
+ this.setVisibility = contextSetVisibility;
+}
+
+// method functions are not privately scoped
+// because Netscape's debugger chokes on private functions
+function contextCreateContainer( containerName ){
+ // creates hidden container to receive server data
+ var container;
+ switch( jsrsBrowser ) {
+ case 'NS':
+ container = new Layer(100);
+ container.name = containerName;
+ container.visibility = 'hidden';
+ container.clip.width = 100;
+ container.clip.height = 100;
+ break;
+
+ case 'IE':
+ document.body.insertAdjacentHTML( "afterBegin", '<span id="SPAN' + containerName + '"></span>' );
+ var span = document.all( "SPAN" + containerName );
+ var html = '<iframe name="' + containerName + '" src=""></iframe>';
+ span.innerHTML = html;
+ span.style.display = 'none';
+ container = window.frames[ containerName ];
+ break;
+
+ case 'MOZ':
+ var span = document.createElement('SPAN');
+ span.id = "SPAN" + containerName;
+ document.body.appendChild( span );
+ var iframe = document.createElement('IFRAME');
+ iframe.name = containerName;
+ iframe.id = containerName;
+ span.appendChild( iframe );
+ container = iframe;
+ break;
+
+ case 'OPR':
+ var span = document.createElement('SPAN');
+ span.id = "SPAN" + containerName;
+ document.body.appendChild( span );
+ var iframe = document.createElement('IFRAME');
+ iframe.name = containerName;
+ iframe.id = containerName;
+ span.appendChild( iframe );
+ container = iframe;
+ break;
+
+ case 'KONQ':
+ var span = document.createElement('SPAN');
+ span.id = "SPAN" + containerName;
+ document.body.appendChild( span );
+ var iframe = document.createElement('IFRAME');
+ iframe.name = containerName;
+ iframe.id = containerName;
+ span.appendChild( iframe );
+ container = iframe;
+
+ // Needs to be hidden for Konqueror, otherwise it'll appear on the page
+ span.style.display = none;
+ iframe.style.display = none;
+ iframe.style.visibility = hidden;
+ iframe.height = 0;
+ iframe.width = 0;
+
+ break;
+ }
+ return container;
+}
+
+function contextPOST( rsPage, func, parms ){
+
+ var d = new Date();
+ var unique = d.getTime() + '' + Math.floor(1000 * Math.random());
+ var doc = (jsrsBrowser == "IE" ) ? this.container.document : this.container.contentDocument;
+ doc.open();
+ doc.write('<html><body>');
+ doc.write('<form name="jsrsForm" method="post" target="" ');
+ doc.write(' action="' + rsPage + '?U=' + unique + '">');
+ doc.write('<input type="hidden" name="C" value="' + this.id + '">');
+
+ // func and parms are optional
+ if (func != null){
+ doc.write('<input type="hidden" name="F" value="' + func + '">');
+
+ if (parms != null){
+ if (typeof(parms) == "string"){
+ // single parameter
+ doc.write( '<input type="hidden" name="P0" '
+ + 'value="[' + jsrsEscapeQQ(parms) + ']">');
+ } else {
+ // assume parms is array of strings
+ for( var i=0; i < parms.length; i++ ){
+ doc.write( '<input type="hidden" name="P' + i + '" '
+ + 'value="[' + jsrsEscapeQQ(parms[i]) + ']">');
+ }
+ } // parm type
+ } // parms
+ } // func
+
+ doc.write('</form></body></html>');
+ doc.close();
+ doc.forms['jsrsForm'].submit();
+}
+
+function contextGET( rsPage, func, parms ){
+
+ // build URL to call
+ var URL = rsPage;
+
+ // always send context
+ URL += "?C=" + this.id;
+
+ // func and parms are optional
+ if (func != null){
+ URL += "&F=" + escape(func);
+
+ if (parms != null){
+ if (typeof(parms) == "string"){
+ // single parameter
+ URL += "&P0=[" + escape(parms+'') + "]";
+ } else {
+ // assume parms is array of strings
+ for( var i=0; i < parms.length; i++ ){
+ URL += "&P" + i + "=[" + escape(parms[i]+'') + "]";
+ }
+ } // parm type
+ } // parms
+ } // func
+
+ // unique string to defeat cache
+ var d = new Date();
+ URL += "&U=" + d.getTime();
+
+ // make the call
+ switch( jsrsBrowser ) {
+ case 'NS':
+ this.container.src = URL;
+ break;
+ case 'IE':
+ this.container.document.location.replace(URL);
+ break;
+ case 'MOZ':
+ this.container.src = '';
+ this.container.src = URL;
+ break;
+ case 'OPR':
+ this.container.src = '';
+ this.container.src = URL;
+ break;
+ case 'KONQ':
+ this.container.src = '';
+ this.container.src = URL;
+ break;
+ }
+}
+
+function contextGetPayload(){
+ switch( jsrsBrowser ) {
+ case 'NS':
+ return this.container.document.forms['jsrs_Form'].elements['jsrs_Payload'].value;
+ case 'IE':
+ return this.container.document.forms['jsrs_Form']['jsrs_Payload'].value;
+ case 'MOZ':
+ return window.frames[this.container.name].document.forms['jsrs_Form']['jsrs_Payload'].value;
+ case 'OPR':
+ var textElement = window.frames[this.container.name].document.getElementById("jsrs_Payload");
+ case 'KONQ':
+ var textElement = window.frames[this.container.name].document.getElementById("jsrs_Payload");
+ return textElement.value;
+ }
+}
+
+function contextSetVisibility( vis ){
+ switch( jsrsBrowser ) {
+ case 'NS':
+ this.container.visibility = (vis)? 'show' : 'hidden';
+ break;
+ case 'IE':
+ document.all("SPAN" + this.id ).style.display = (vis)? '' : 'none';
+ break;
+ case 'MOZ':
+ document.getElementById("SPAN" + this.id).style.visibility = (vis)? '' : 'hidden';
+ case 'OPR':
+ document.getElementById("SPAN" + this.id).style.visibility = (vis)? '' : 'hidden';
+ this.container.width = (vis)? 250 : 0;
+ this.container.height = (vis)? 100 : 0;
+ break;
+ }
+}
+
+// end of context constructor
+
+function jsrsGetContextID(){
+ var contextObj;
+ for (var i = 1; i <= jsrsContextPoolSize; i++){
+ contextObj = jsrsContextPool[ 'jsrs' + i ];
+ if ( !contextObj.busy ){
+ contextObj.busy = true;
+ return contextObj.id;
+ }
+ }
+ // if we got here, there are no existing free contexts
+ if ( jsrsContextPoolSize <= jsrsContextMaxPool ){
+ // create new context
+ var contextID = "jsrs" + (jsrsContextPoolSize + 1);
+ jsrsContextPool[ contextID ] = new jsrsContextObj( contextID );
+ jsrsContextPoolSize++;
+ return contextID;
+ } else {
+ alert( "jsrs Error: context pool full" );
+ return null;
+ }
+}
+
+function jsrsExecute( rspage, callback, func, parms, visibility ){
+ // call a server routine from client code
+ //
+ // rspage - href to asp file
+ // callback - function to call on return
+ // or null if no return needed
+ // (passes returned string to callback)
+ // func - sub or function name to call
+ // parm - string parameter to function
+ // or array of string parameters if more than one
+ // visibility - optional boolean to make container visible for debugging
+
+ // get context
+ var contextObj = jsrsContextPool[ jsrsGetContextID() ];
+ contextObj.callback = callback;
+
+ var vis = (visibility == null)? false : visibility;
+ contextObj.setVisibility( vis );
+
+ if ( jsrsPOST && ((jsrsBrowser == 'IE') || (jsrsBrowser == 'MOZ'))){
+ contextObj.POST( rspage, func, parms );
+ } else {
+ contextObj.GET( rspage, func, parms );
+ }
+
+ return contextObj.id;
+}
+
+function jsrsLoaded( contextID ){
+ // get context object and invoke callback
+ var contextObj = jsrsContextPool[ contextID ];
+ if( contextObj.callback != null){
+ contextObj.callback( jsrsUnescape( contextObj.getPayload() ), contextID );
+ }
+ // clean up and return context to pool
+ contextObj.callback = null;
+ contextObj.busy = false;
+}
+
+function jsrsError( contextID, str ){
+ alert( unescape(str) );
+ jsrsContextPool[ contextID ].busy = false
+}
+
+function jsrsEscapeQQ( thing ){
+ return thing.replace(/'"'/g, '\\"');
+}
+
+function jsrsUnescape( str ){
+ // payload has slashes escaped with whacks
+ return str.replace( /\\\//g, "/" );
+}
+
+function jsrsBrowserSniff(){
+ if (document.layers) return "NS";
+ if (document.all) {
+ // But is it really IE?
+ // convert all characters to lowercase to simplify testing
+ var agt=navigator.userAgent.toLowerCase();
+ var is_opera = (agt.indexOf("opera") != -1);
+ var is_konq = (agt.indexOf("konqueror") != -1);
+ if(is_opera) {
+ return "OPR";
+ } else {
+ if(is_konq) {
+ return "KONQ";
+ } else {
+ // Really is IE
+ return "IE";
+ }
+ }
+ }
+ if (document.getElementById) return "MOZ";
+ return "OTHER";
+}
+
+/////////////////////////////////////////////////
+//
+// user functions
+
+function jsrsArrayFromString( s, delim ){
+ // rebuild an array returned from server as string
+ // optional delimiter defaults to ~
+ var d = (delim == null)? '~' : delim;
+ return s.split(d);
+}
+
+function jsrsDebugInfo(){
+ // use for debugging by attaching to f1 (works with IE)
+ // with onHelp = "return jsrsDebugInfo();" in the body tag
+ var doc = window.open().document;
+ doc.open;
+ doc.write( 'Pool Size: ' + jsrsContextPoolSize + '<br><font face="arial" size="2"><b>' );
+ for( var i in jsrsContextPool ){
+ var contextObj = jsrsContextPool[i];
+ doc.write( '<hr>' + contextObj.id + ' : ' + (contextObj.busy ? 'busy' : 'available') + '<br>');
+ doc.write( contextObj.container.document.location.pathname + '<br>');
+ doc.write( contextObj.container.document.location.search + '<br>');
+ doc.write( '<table border="1"><tr><td>' + contextObj.container.document.body.innerHTML + '</td></tr></table>' );
+ }
+ doc.write('</table>');
+ doc.close();
+ return false;
+}
diff --git a/httemplate/elements/jsrsServer.html b/httemplate/elements/jsrsServer.html
new file mode 100644
index 0000000..f37b0aa
--- /dev/null
+++ b/httemplate/elements/jsrsServer.html
@@ -0,0 +1,4 @@
+%
+% my $server = new FS::UI::Web::JSRPC '', $cgi;
+%
+<% $server->process %>
diff --git a/httemplate/elements/location.html b/httemplate/elements/location.html
new file mode 100644
index 0000000..d7b73a2
--- /dev/null
+++ b/httemplate/elements/location.html
@@ -0,0 +1,154 @@
+<%doc>
+
+Example:
+
+ include( '/elements/location.html',
+ 'object' => $cust_main, # or $cust_location
+ 'prefix' => $pre, #only for cust_main objects
+ 'onchange' => $javascript,
+ 'disabled' => $disabled,
+ 'same_checked' => $same_checked,
+ 'geocode' => $geocode, #passed through
+ 'no_asterisks' => 0, #set true to disable the red asterisks next
+ #to required fields
+ )
+
+</%doc>
+
+<TR>
+ <TH ALIGN="right"><%$r%>Address</TH>
+ <TD COLSPAN=7>
+ <INPUT TYPE = "text"
+ NAME = "<%$pre%>address1"
+ ID = "<%$pre%>address1"
+ VALUE = "<% $object->get($pre.'address1') |h %>"
+ SIZE = 58
+ onChange = "<% $onchange %>"
+ <% $disabled %>
+ <% $style %>
+ >
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right"><FONT ID="<% $pre %>address2_required" color="#ff0000" <% $address2_label_style %>>*</FONT>&nbsp;<FONT ID="<% $pre %>address2_label" <% $address2_label_style %>><B>Unit&nbsp;#</B></FONT></TD>
+ <TD COLSPAN=7>
+ <INPUT TYPE = "text"
+ NAME = "<%$pre%>address2"
+ ID = "<%$pre%>address2"
+ VALUE = "<% $object->get($pre.'address2') |h %>"
+ SIZE = 58
+ onChange = "<% $onchange %>"
+ <% $disabled %>
+ <% $style %>
+ >
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right"><%$r%>City</TH>
+ <TD>
+ <INPUT TYPE = "text"
+ NAME = "<%$pre%>city"
+ ID = "<%$pre%>city"
+ VALUE = "<% $object->get($pre.'city') |h %>"
+ onChange = "<% $onchange %>"
+ <% $disabled %>
+ <% $style %>
+ >
+ </TD>
+ <TH ALIGN="right" ID="<%$pre%>countylabel" <%$county_style%>><%$r%>County</TH>
+ <TD>
+ <% include('/elements/select-county.html', %select_hash ) %>
+ </TD>
+ <TH ALIGN="right"><%$r%>State</TH>
+ <TD>
+ <% include('/elements/select-state.html', %select_hash ) %>
+ </TD>
+ <TH><%$r%>Zip</TH>
+ <TD>
+ <INPUT TYPE = "text"
+ NAME = "<%$pre%>zip"
+ ID = "<%$pre%>zip"
+ VALUE = "<% $object->get($pre.'zip') |h %>"
+ SIZE = 10
+ onChange = "<% $onchange %>"
+ <% $disabled %>
+ <% $style %>
+ >
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right"><%$r%>Country</TH>
+ <TD COLSPAN=5><% include('/elements/select-country.html', %select_hash ) %></TD>
+</TR>
+
+% if ( !$pre ) {
+ <INPUT TYPE="hidden" NAME="geocode" VALUE="<% $opt{geocode} %>">
+% }
+
+<%init>
+
+my %opt = @_;
+
+my $pre = $opt{'prefix'};
+my $object = $opt{'object'};
+my $onchange = $opt{'onchange'};
+my $disabled = $opt{'disabled'};
+
+my $conf = new FS::Conf;
+
+my $r = $opt{'no_asterisks'} ? '' : qq!<font color="#ff0000">*</font>&nbsp;!;
+
+#false laziness with ship state
+my $countrydefault = $conf->config('countrydefault') || 'US';
+$object->set($pre.'country', $countrydefault )
+ unless $object->get($pre.'country');
+
+my $statedefault = $conf->config('statedefault')
+ || ($countrydefault eq 'US' ? 'CA' : '');
+$object->set($pre.'state', $statedefault )
+ unless $object->get($pre.'state')
+ || $object->get($pre.'country') ne $countrydefault;
+
+my @style = ();
+push @style, 'background-color: #dddddd"' if $disabled;
+
+my @address2_label_style = ();
+push @address2_label_style, 'visibility:hidden'
+ if $disabled
+ || ! $conf->exists('cust_main-require_address2')
+ || ( !$pre && !$opt{'same_checked'} );
+
+my @counties = counties( $object->get($pre.'state'),
+ $object->get($pre.'country'),
+ );
+my @county_style = ();
+push @county_style, 'visibility:hidden'
+ unless scalar(@counties) > 1;
+
+my $style =
+ scalar(@style)
+ ? 'STYLE="'. join(';', @style). '"'
+ : '';
+my $address2_label_style =
+ scalar(@address2_label_style)
+ ? 'STYLE="'. join(';', @address2_label_style). '"'
+ : '';
+my $county_style =
+ scalar(@county_style)
+ ? 'STYLE="'. join(';', @county_style). '"'
+ : '';
+
+my %select_hash = (
+ 'county' => $object->get($pre.'county'),
+ 'state' => $object->get($pre.'state'),
+ 'country' => $object->get($pre.'country'),
+ 'prefix' => $pre,
+ 'onchange' => $onchange,
+ 'disabled' => $disabled,
+ 'style' => \@style,
+);
+
+</%init>
diff --git a/httemplate/elements/mcp_lint.html b/httemplate/elements/mcp_lint.html
new file mode 100644
index 0000000..161415e
--- /dev/null
+++ b/httemplate/elements/mcp_lint.html
@@ -0,0 +1,40 @@
+% foreach my $lint (@lint) {
+% my $color = ( $lint =~ /unchecked$/ ? '#FF9900' : '#FF0000' );
+ <FONT COLOR="<% $color %>"><% $lint %></FONT><BR>
+% }
+
+<%init>
+
+my(%opt) = @_;
+
+my $conf = new FS::Conf;
+
+my @svc = ();
+if ( $opt{svc} ) {
+ @svc = ref($opt{svc}) ? @{ $opt{svc} } : ( $opt{svc} );
+} elsif ( $opt{cust_main} ) {
+ my $custnum = $opt{cust_main}->custnum;
+ @svc = qsearchs({
+ 'table' => 'cust_svc',
+ 'addl_from' => ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'svcpart' => $conf->config('mcp_svcpart') },
+ 'extra_sql' => " AND custnum = $custnum ",
+ });
+} else {
+ die 'neither svc nor cust_main options passed to mcp_lint';
+}
+
+
+my @lint = ();
+push @lint, 'unchecked' unless @svc;
+foreach my $svc ( @svc ) {
+ my @svc_lint = tron_lint($svc);
+ if ( scalar(@svc) > 1 ) {
+ push @lint, map $svc->title.": $_", @svc_lint;
+ } else {
+ push @lint, @svc_lint;
+ }
+}
+
+</%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
new file mode 100644
index 0000000..627f9c8
--- /dev/null
+++ b/httemplate/elements/menu.html
@@ -0,0 +1,427 @@
+<script type="text/javascript" src="<%$fsurl%>elements/cssexpr.js"></script>
+
+% if ( $opt{'position'} eq 'top' ) {
+
+ <script type="text/javascript" src="<%$fsurl%>elements/xmenu.top.js"></script>
+ <link href="<%$fsurl%>elements/xmenu.top.css" type="text/css" rel="stylesheet">
+
+% } else { # elsif ( $opt{'position'} eq 'left' ) {
+
+ <script type="text/javascript" src="<%$fsurl%>elements/xmenu.js"></script>
+ <link href="<%$fsurl%>elements/xmenu.css" type="text/css" rel="stylesheet">
+
+% }
+
+<link href="<%$fsurl%>elements/freeside.css" type="text/css" rel="stylesheet">
+
+<SCRIPT TYPE="text/javascript">
+
+ webfxMenuImagePath = "<%$fsurl%>images/";
+ webfxMenuUseHover = 1;
+ webfxMenuShowTime = 300;
+ webfxMenuHideTime = 500;
+
+ var myBar = new WebFXMenuBar;
+
+% foreach my $item ( keys %menu ) {
+%
+% my( $url_or_submenu, $tooltip ) = @{ $menu{$item} };
+%
+% if ( ref($url_or_submenu) ) {
+%
+% #warn $item;
+%
+% my( $subhtml, $submenuname ) = submenu($url_or_submenu, $item);
+
+ <% $subhtml %>
+ myBar.add(new WebFXMenuButton("<% $item %>", null, "<% $tooltip %>", <% $submenuname %> ));
+
+% } else {
+
+ myBar.add(new WebFXMenuButton("<% $item %>", "<% $url_or_submenu %>", "<% $tooltip %>" ));
+
+% }
+%
+% }
+
+ myBar.show( null, 'vertical' );
+ myBar.width = 154;
+
+</SCRIPT>
+
+<%init>
+my( %opt ) = @_;
+my $conf = new FS::Conf;
+my $fsurl = $opt{'freeside_baseurl'};
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+#Active tickets not assigned to a customer
+
+tie my %report_customers_lists, 'Tie::IxHash',
+ 'by customer number' => [ $fsurl. 'search/cust_main.cgi?browse=custnum', '' ],
+ 'by last name' => [ $fsurl. 'search/cust_main.cgi?browse=last', '' ],
+ 'by company name' => [ $fsurl. 'search/cust_main.cgi?browse=company', '' ],
+;
+$report_customers_lists{'by active trouble tickets'} = [ $fsurl. 'search/cust_main.cgi?browse=tickets', '' ]
+ if $conf->config('ticket_system');
+
+tie my %report_customers_search, 'Tie::IxHash';
+$report_customers_search{'by ordering employee'} = [ $fsurl. 'search/cust_main-otaker.cgi' ]
+ if $curuser->access_right('Configuration');
+
+tie my %report_customers, 'Tie::IxHash';
+$report_customers{'List customers'} = [ \%report_customers_lists, 'List customers' ]
+ if $curuser->access_right('List customers');
+$report_customers{'Search customers'} = [ \%report_customers_search, 'Search customers' ]
+ if keys %report_customers_search;
+$report_customers{'Zip code distribution'} = [ $fsurl. 'search/report_cust_main-zip.html', 'Zip codes by number of customers' ];
+$report_customers{'Advanced customer reports'} = [ $fsurl. 'search/report_cust_main.html', 'by status, signup date, agent, etc.' ]
+ if $curuser->access_right('List customers')
+ && $curuser->access_right('List packages');
+
+tie my %report_invoices_open, 'Tie::IxHash',
+ 'All open invoices' => [ $fsurl.'search/cust_bill.html?OPEN_date', 'All invoices with an unpaid balance' ],
+ '15 day open invoices' => [ $fsurl.'search/cust_bill.html?OPEN15_date', 'Invoices 15 days or older with an unpaid balance' ],
+ '30 day open invoices' => [ $fsurl.'search/cust_bill.html?OPEN30_date', 'Invoices 30 days or older with an unpaid balance' ],
+ '60 day open invoices' => [ $fsurl.'search/cust_bill.html?OPEN60_date', 'Invoices 60 days or older with an unpaid balance' ],
+ '90 day open invoices' => [ $fsurl.'search/cust_bill.html?OPEN90_date', 'Invoices 90 days or older with an unpaid balance' ],
+ '120 day open invoices' => [ $fsurl.'search/cust_bill.html?OPEN120_date', 'Invoices 120 days or older with an unpaid balance' ],
+;
+
+tie my %report_invoices, 'Tie::IxHash',
+ 'Open invoices' => [ \%report_invoices_open, 'Open invoices' ],
+ 'All invoices' => [ $fsurl. 'search/cust_bill.html?date', 'List all invoices' ],
+ 'Advanced invoice reports' => [ $fsurl.'search/report_cust_bill.html', 'by agent, date range, etc.' ],
+;
+
+tie my %report_services, 'Tie::IxHash';
+if ( $curuser->access_right('Configuration') ) {
+ $report_services{'Service definitions'} = [ $fsurl.'browse/part_svc.cgi?orderby=active', 'Service definitions by number of active packages' ];
+ $report_services{'separator'} = '';
+}
+foreach my $svcdb ( FS::part_svc->svc_tables() ) {
+
+ my $name = "FS::$svcdb"->table_info->{'name_plural'}
+ || PL( "FS::$svcdb"->table_info->{'name'} );
+ my $lcname = lc($name);
+ my $lcsname = lc("FS::$svcdb"->table_info->{'name'});
+ my $longname = "FS::$svcdb"->table_info->{'longname_plural'} || $name;
+ my $lclongname = lc($longname);
+ my $sorts = "FS::$svcdb"->table_info->{'sorts'} || [ 'svcnum' ];
+ $sorts = [ $sorts ] unless ref($sorts);
+ my %svc_url = ( 'm' => $m,
+ 'action' => 'search',
+ 'svcdb' => $svcdb,
+ );
+
+ tie my %report_svc, 'Tie::IxHash';
+
+ foreach my $sort ( @$sorts ) {
+
+ my $field_info = FS::part_svc->svc_table_fields($svcdb)->{$sort};
+ my $label = $field_info->{'label_sort'} || 'by '.$field_info->{'label'};
+
+ my $title = "All $lcname";
+ $title .= " $label"
+ if scalar(@$sorts) > 1;
+
+ $report_svc{$title} =
+ [ svc_url( %svc_url, 'query' => "magic=all;sortby=$sort" ),
+ '',
+ ];
+ }
+
+ if ( $svcdb eq 'svc_acct' ) {
+ $report_svc{"All $lcname never logged in"} =
+ [ svc_url( %svc_url, 'query' => "magic=nologin;sortby=svcnum" ),
+ '',
+ ];
+ }
+
+ if ( $curuser->access_right('View/link unlinked services') ) {
+ $report_svc{"Unlinked $lcname"} =
+ [ svc_url( %svc_url, 'query' => "magic=unlinked;sortby=". $sorts->[0] ),
+ "Pre-Freeside $lcname without a customer record",
+ ];
+ }
+
+ if ( $svcdb eq 'svc_acct' ) {
+ $report_svc{"Advanced $lcsname reports"} =
+ [ $fsurl."search/report_$svcdb.html", '' ];
+ }
+
+ $report_services{$name} = [ \%report_svc, $longname ];
+
+}
+
+tie my %report_packages, 'Tie::IxHash';
+if ( $curuser->access_right('Edit package definitions')
+ || $curuser->access_right('Edit global package definitions')
+ )
+{
+ $report_packages{'Package definitions'} = [ $fsurl.'browse/part_pkg.cgi?active=1', 'Package definitions by number of active packages' ];
+ $report_packages{'separator'} = '';
+}
+if ( $curuser->access_right('Financial reports') ) {
+ $report_packages{'Package churn'} = [ $fsurl.'graph/report_cust_pkg.html', 'Orders, suspensions and cancellations summary graph' ];
+ $report_packages{'separator2'} = '';
+}
+$report_packages{'All customer packages'} = [ $fsurl.'search/cust_pkg.cgi?pkgnum', 'List all customer packages', ];
+$report_packages{'Suspended customer packages'} = [ $fsurl.'search/cust_pkg.cgi?magic=suspended', 'List suspended packages' ];
+$report_packages{'Customer packages with unconfigured services'} = [ $fsurl.'search/cust_pkg.cgi?APKG_pkgnum', 'List packages which have provisionable services' ];
+$report_packages{'Advanced package reports'} = [ $fsurl.'search/report_cust_pkg.html', 'by agent, date range, status, package definition' ];
+
+tie my %report_rating, 'Tie::IxHash',
+ 'RADIUS sessions' => [ $fsurl.'search/sqlradius.html', '' ],
+ 'Call Detail Records (CDRs)' => [ $fsurl.'search/report_cdr.html', '' ],
+ 'Time worked' => [ $fsurl.'search/report_rt_transaction.html', '' ],
+;
+
+tie my %report_bill_event, 'Tie::IxHash',
+ 'All billing events' => [ $fsurl.'search/report_cust_event.html', 'All billing events for a date range' ],
+ 'Billing event errors' => [ $fsurl.'search/report_cust_event.html?failed=1', 'Failed credit cards, processor or printer problems, etc.' ],
+ 'All invoice events' => [ $fsurl.'search/cust_bill_event.html', 'Reports on deprecated, old-style invoice events for a date range' ],
+ 'Invoice event errors' => [ $fsurl.'search/cust_bill_event.html?failed=1', 'Reports on deprecated, old-style events for failed credit cards, processor or printer problems, etc.' ],
+;
+
+tie my %report_financial, 'Tie::IxHash',
+ 'Sales, Credits and Receipts' => [ $fsurl.'graph/report_money_time.html', 'Sales, credits and receipts summary graph' ],
+ 'Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg.html', 'Sales report and graph (by agent, package class and/or date range)' ],
+ 'Credit Report' => [ $fsurl.'search/report_cust_credit.html', 'Credit report (by employee and/or date range)' ],
+ 'Payment Report' => [ $fsurl.'search/report_cust_pay.html', 'Payment report (by type and/or date range)' ],
+;
+$report_financial{'Pending Payment Report'} = [ $fsurl.'search/cust_pay_pending.html?magic=_date;statusNOT=done', 'Pending real-time payments' ]
+ if $curuser->access_right('View customer pending payments');
+$report_financial{'Payment Batch Report'} = [ $fsurl.'search/pay_batch.html', 'Payment batches (by status and/or date range)' ]
+ if $conf->exists('batch-enable') || $conf->config('batch-enable_payby');
+$report_financial{'A/R Aging'} = [ $fsurl.'search/report_receivables.html', 'Accounts Receivable Aging report' ];
+$report_financial{'Prepaid Income'} = [ $fsurl.'search/report_prepaid_income.html', 'Prepaid income (unearned revenue) report' ];
+$report_financial{'Sales Tax Liability'} = [ $fsurl.'search/report_tax.html', 'Sales tax liability report (old taxclass system)' ];
+$report_financial{'Tax Liability'} = [ $fsurl.'search/report_newtax.html', 'Tax liability report (new tax products system)' ]
+ if $conf->exists('enable_taxproducts');
+;
+
+tie my %report_menu, 'Tie::IxHash';
+$report_menu{'Customers'} = [ \%report_customers, 'Customer reports' ]
+ if $curuser->access_right('List customers');
+$report_menu{'Invoices'} = [ \%report_invoices, 'Invoice reports' ]
+ if $curuser->access_right('List invoices');
+$report_menu{'Packages'} = [ \%report_packages, 'Package reports' ]
+ if $curuser->access_right('List packages');
+$report_menu{'Services'} = [ \%report_services, 'Services reports' ]
+ if $curuser->access_right('List services');
+$report_menu{'Usage'} = [ \%report_rating, 'Usage reports' ]
+ if $curuser->access_right('List rating data');
+$report_menu{'Billing events'} = [ \%report_bill_event, 'Billing events' ]
+ if $curuser->access_right('Billing event reports');
+$report_menu{'Financial'} = [ \%report_financial, 'Financial reports' ]
+ if $curuser->access_right('Financial reports');
+$report_menu{'SQL Query'} = [ $fsurl.'search/report_sql.html', 'SQL Query' ]
+ if $curuser->access_right('Raw SQL');
+
+tie my %tools_importing, 'Tie::IxHash',
+ 'Import customers' => [ $fsurl.'misc/cust_main-import.cgi', '' ],
+ 'Import customer comments from CSV file' => [ $fsurl.'misc/cust_main_note-import.html', '' ],
+ 'Import one-time charges from CSV file' => [ $fsurl.'misc/cust_main-import_charges.cgi', '' ],
+ 'Import payments from CSV file' => [ $fsurl.'misc/cust_pay-import.cgi', '' ],
+ 'Import phone numbers (DIDs)' => [ $fsurl.'misc/phone_avail-import.html', '' ],
+ 'Import Call Detail Records (CDRs) from CSV file' => [ $fsurl.'misc/cdr-import.html', '' ],
+ 'Import tax rates from CSV files' => [ $fsurl.'misc/tax-import.cgi', '' ],
+;
+
+tie my %tools_exporting, 'Tie::IxHash',
+ 'Download database dump' => [ $fsurl. 'misc/dump.cgi', '' ],
+;
+
+# <!-- <BR>View active NAS ports:
+# <A HREF="browse/nas.cgi">session server</A> -->
+# <!-- or <A HREF="browse/nas-sqlradius.cgi">RADIUS</A>
+# <BR> -->
+
+tie my %tools_menu, 'Tie::IxHash', ();
+$tools_menu{'Quick payment entry'} = [ $fsurl.'misc/batch-cust_pay.html', 'Enter multiple payments in a batch' ]
+ if $curuser->access_right('Post payment batch');
+$tools_menu{'Process payment batches'} = [ $fsurl.'search/pay_batch.cgi?magic=_date;open=1;intransit=1', 'Process credit card and electronic check batches' ]
+ if ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
+ && $curuser->access_right('Process batches');
+$tools_menu{'Job Queue'} = [ $fsurl.'search/queue.html', 'View pending job queue' ]
+ if $curuser->access_right('Job queue');
+$tools_menu{'Time Queue'} = [ $fsurl.'search/timeworked.html', 'View pending support time' ]
+ if $curuser->access_right('Time queue');
+$tools_menu{'Importing'} = [ \%tools_importing, 'Import tools' ]
+ if $curuser->access_right('Import');
+$tools_menu{'Exporting'} = [ \%tools_exporting, 'Export tools' ]
+ if $curuser->access_right('Export');
+
+tie my %config_employees, 'Tie::IxHash',
+ 'View/Edit employees' => [ $fsurl.'browse/access_user.html', 'Setup internal users' ],
+ 'View/Edit employee groups' => [ $fsurl.'browse/access_group.html', 'Employee groups allow you to control access to the backend' ],
+;
+
+tie my %config_export_svc_pkg, 'Tie::IxHash', ();
+if ( $curuser->access_right('Configuration') ) {
+ $config_export_svc_pkg{'View/Edit exports'} = [ $fsurl.'browse/part_export.cgi', 'Provisioning services to external machines, databases and APIs' ];
+ $config_export_svc_pkg{'View/Edit service definitions'} = [ $fsurl.'browse/part_svc.cgi', 'Services are items you offer to your customers' ];
+}
+$config_export_svc_pkg{'View/Edit package definitions'} = [ $fsurl.'browse/part_pkg.cgi', 'One or more services are grouped together into a package and given pricing information. Customers purchase packages, not services' ]
+ if $curuser->access_right('Edit package definitions')
+ || $curuser->access_right('Edit global package definitions');
+if ( $curuser->access_right('Configuration') ) {
+ $config_export_svc_pkg{'View/Edit package categories'} = [ $fsurl.'browse/pkg_category.html', 'Package categories define groups of package classes, for reporting and convenience purposes.' ];
+ $config_export_svc_pkg{'View/Edit package classes'} = [ $fsurl.'browse/pkg_class.html', 'Package classes define groups of packages, for reporting and convenience purposes.' ];
+ $config_export_svc_pkg{'View/Edit cancel reason types'} = [ $fsurl.'browse/reason_type.html?class=C', 'Cancel reason types define groups of reasons, for reporting and convenience purposes.' ];
+ $config_export_svc_pkg{'View/Edit cancel reasons'} = [ $fsurl.'browse/reason.html?class=C', 'Cancel reasons explain why a service was cancelled.' ];
+ $config_export_svc_pkg{'View/Edit suspend reason types'} = [ $fsurl.'browse/reason_type.html?class=S', 'Suspend reason types define groups of reasons, for reporting and convenience purposes.' ];
+ $config_export_svc_pkg{'View/Edit suspend reasons'} = [ $fsurl.'browse/reason.html?class=S', 'Suspend reasons explain why a service was suspended.' ];
+}
+
+tie my %config_agent, 'Tie::IxHash',
+ 'View/Edit agent types' => [ $fsurl.'browse/agent_type.cgi', 'Agent types define groups of package definitions that you can then assign to particular agents' ],
+ 'View/Edit agents' => [ $fsurl.'browse/agent.cgi', 'Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their type)' ],
+ 'View/Edit agent payment gateways' => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors for agent overrides' ];
+;
+
+tie my %config_billing_rates, 'Tie::IxHash',
+ 'View/Edit rate plans' => [ $fsurl.'browse/rate.cgi', 'Manage rate plans' ],
+ 'View/Edit regions and prefixes' => [ $fsurl.'browse/rate_region.html', 'Manage regions and prefixes' ],
+ 'View/Edit usage classes' => [ $fsurl.'browse/usage_class.html', 'Usage classes define groups of usage for taxation purposes.' ],
+;
+
+tie my %config_billing, 'Tie::IxHash';
+# 'View/Edit payment gateways' => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors' ];
+$config_billing{'View/Edit billing events'} = [ $fsurl.'browse/part_event.html', 'Billing actions for customers, invoices and packages' ]
+ if $curuser->access_right('Edit billing events')
+ || $curuser->access_right('Edit global billing events');
+if ( $curuser->access_right('Configuration') ) {
+ $config_billing{'View/Edit invoice events'} = [ $fsurl.'browse/part_bill_event.cgi', 'Deprecated, old-style actions for overdue invoices' ];
+ $config_billing{'View/Edit invoice templates'} = [ $fsurl.'browse/invoice_template.html', 'Edit templates for HTML, plaintext and typeset invoices' ];
+ $config_billing{'View/Edit prepaid cards'} = [ $fsurl.'search/prepay_credit.html', 'View outstanding cards, generate new cards' ];
+ $config_billing{'View/Edit call rates and regions'} = [ \%config_billing_rates, 'Manage rate plans, regions and prefixes for VoIP and call billing' ];
+ $config_billing{'View/Edit locales and tax rates (old tax class system)'} = [ $fsurl.'browse/cust_main_county.cgi', 'Change tax rates, or break down a country into states, or a state into counties and assign different tax rates to each' ];
+ $config_billing{'View/Edit tax rates (new tax products system)'} = [ $fsurl.'browse/tax_rate.cgi', 'Edit tax rates for the new tax products system' ];
+ $config_billing{'View/Edit credit reason types'} = [ $fsurl.'browse/reason_type.html?class=R', 'Credit reason types define groups of reasons, for reporting and convenience purposes.' ];
+ $config_billing{'View/Edit credit reasons'} = [ $fsurl.'browse/reason.html?class=R', 'Credit reasons explain why a credit was issued.' ];
+}
+
+tie my %config_dialup, 'Tie::IxHash',
+ 'View/Edit access numbers' => [ $fsurl.'browse/svc_acct_pop.cgi', 'Points of Presence' ],
+;
+
+tie my %config_broadband, 'Tie::IxHash',
+ 'View/Edit routers' => [ $fsurl.'browse/router.cgi', 'Broadband access routers' ],
+ 'View/Edit address blocks' => [ $fsurl.'browse/addr_block.cgi', 'Manage address blocks and block assignments to broadband routers' ],
+;
+
+tie my %config_misc, 'Tie::IxHash';
+$config_misc{'View/Edit advertising sources'} = [ $fsurl.'browse/part_referral.html', 'Where a customer heard about your service. Tracked for informational purposes' ]
+ if $curuser->access_right('Edit advertising sources')
+ || $curuser->access_right('Edit global advertising sources');
+if ( $curuser->access_right('Configuration') ) {
+ $config_misc{'View/Edit virtual fields'} = [ $fsurl.'browse/part_virtual_field.cgi', 'Locally defined fields', ];
+ $config_misc{'View/Edit message catalog'} = [ $fsurl.'browse/msgcat.cgi', 'Change error messages and other customizable labels' ];
+ $config_misc{'View/Edit inventory classes and inventory'} = [ $fsurl.'browse/inventory_class.html', 'Setup inventory classes and stock inventory' ];
+}
+
+tie my %config_menu, 'Tie::IxHash';
+if ( $curuser->access_right('Configuration' ) ) {
+ %config_menu = (
+ 'Settings' => [ $fsurl.'config/config-view.cgi', '' ],
+ 'separator' => '', #its a separator!
+ 'Employees' => [ \%config_employees, '' ],
+ );
+}
+$config_menu{'Provisioning, services and packages'} =
+ [ \%config_export_svc_pkg, '' ]
+ if $curuser->access_right('Configuration' )
+ || $curuser->access_right('Edit package definitions')
+ || $curuser->access_right('Edit global package definitions');
+$config_menu{'Resellers'} = [ \%config_agent, '' ]
+ if $curuser->access_right('Configuration');
+$config_menu{'Billing'} = [ \%config_billing, '' ]
+ if $curuser->access_right('Edit billing events')
+ || $curuser->access_right('Edit global billing events');
+$config_menu{'Dialup'} = [ \%config_dialup, '' ]
+ if ( $curuser->access_right('Dialup configuration') );
+$config_menu{'Fixed (username-less) broadband'} = [ \%config_broadband, '' ]
+ if ( $curuser->access_right('Broadband configuration') );
+$config_menu{'Miscellaneous'} = [ \%config_misc, '' ]
+ if $curuser->access_right('Edit advertising sources')
+ || $curuser->access_right('Edit global advertising sources');
+
+tie my %menu, 'Tie::IxHash',
+ 'Billing Main' => [ $fsurl, 'Billing start page', ],
+;
+if ( $conf->config('ticket_system') ) {
+ $menu{'Ticketing Main'} =
+ [
+ ( $conf->config('ticket_system') eq 'RT_External'
+ ? FS::TicketSystem->baseurl()
+ : $fsurl.'rt/'
+ ),
+ 'Ticketing start page',
+ ],
+}
+$menu{'Reports'} = [ \%report_menu, 'Lists, reporting and graphing' ]
+ if keys %report_menu;
+$menu{'Tools'} = [ \%tools_menu, 'Tools' ]
+ if keys %tools_menu;
+$menu{'Configuration'} = [ \%config_menu, 'Configuraiton and setup' ]
+ if $curuser->access_right('Configuration')
+ || $curuser->access_right('Edit package definitions')
+ || $curuser->access_right('Edit global package definitions')
+ || $curuser->access_right('Edit billing events')
+ || $curuser->access_right('Edit global billing events')
+ || $curuser->access_right('Dialup configuration')
+ || $curuser->access_right('Broadband configuration')
+ || $curuser->access_right('Edit advertising sources')
+ || $curuser->access_right('Edit global advertising sources');
+
+use vars qw($gmenunum);
+$gmenunum = 0;
+
+sub submenu {
+ my($submenu, $title) = @_;
+ my $menunum = $gmenunum++;
+
+ #return two args: html, menuname
+
+ "var myMenu$menunum = new WebFXMenu;\n".
+ #"myMenu$menunum.useAutoPosition = true;\n".
+ "myMenu$menunum.emptyText = '$title';\n".
+
+ (
+ join("\n", map {
+
+ if ( !ref( $submenu->{$_} ) ) {
+
+ "myMenu$menunum.add(new WebFXMenuSeparator());";
+
+ } else {
+
+ my($url_or_submenu, $tooltip ) = @{ $submenu->{$_} };
+ if ( ref($url_or_submenu) ) {
+
+ my($subhtml, $submenuname ) = submenu($url_or_submenu, $_); #mmm, recursion
+
+ "$subhtml\n".
+ "myMenu$menunum.add(new WebFXMenuItem(\"$_\", null, \"$tooltip\", $submenuname ));";
+
+ } else {
+
+ "myMenu$menunum.add(new WebFXMenuItem(\"$_\", \"$url_or_submenu\", \"$tooltip\" ));";
+
+ }
+
+ }
+
+ } keys %$submenu )
+ ). "\n".
+ "myMenu$menunum.width = 280;\n",
+
+ "myMenu$menunum";
+
+}
+
+</%init>
+
diff --git a/httemplate/elements/menubar.html b/httemplate/elements/menubar.html
new file mode 100644
index 0000000..ec6c13f
--- /dev/null
+++ b/httemplate/elements/menubar.html
@@ -0,0 +1,10 @@
+%
+% my($item, $url, @html);
+% while (@_) {
+% ($item, $url) = splice(@_,0,2);
+% next if $item =~ /^\s*Main\s+Menu\s*$/i;
+% push @html, qq!<A HREF="$url">$item</A>!;
+% }
+%
+
+<% join(' | ', @html) %>
diff --git a/httemplate/elements/overlibmws.js b/httemplate/elements/overlibmws.js
new file mode 100644
index 0000000..df2bd1d
--- /dev/null
+++ b/httemplate/elements/overlibmws.js
@@ -0,0 +1,620 @@
+/*
+ Do not remove or change this notice.
+ overlibmws.js core module - Copyright Foteos Macrides 2002-2008. All rights reserved.
+ Initial: August 18, 2002 - Last Revised: March 22, 2008
+ This module is subject to the same terms of usage as for Erik Bosrup's overLIB,
+ though only a minority of the code and API now correspond with Erik's version.
+ See the overlibmws Change History and Command Reference via:
+
+ http://www.macridesweb.com/oltest/
+
+ Published under an open source license: http://www.macridesweb.com/oltest/license.html
+ Give credit on sites that use overlibmws and submit changes so others can use them as well.
+ You can get Erik's version via: http://www.bosrup.com/web/overlib/
+*/
+
+// PRE-INIT -- Ignore these lines, configuration is below.
+var OLloaded=0,OLbubblePI=0,OLcrossframePI=0,OLdebugPI=0,OLdraggablePI=0,OLexclusivePI=0,OLfilterPI=0,
+OLfunctionPI=0,OLhidePI=0,OLiframePI=0,OLmodalPI=0,OLovertwoPI=0,OLscrollPI=0,OLshadowPI=0,OLprintPI=0,
+pmCnt=1,pMtr=new Array(),OLcmdLine=new Array(),OLrunTime=new Array(),OLv,OLudf,OLrefXY,
+OLpct=new Array("83%","67%","83%","100%","117%","150%","200%","267%");if(typeof OLgateOK=='undefined')var OLgateOK=1;
+var OLp1or2c='inarray,caparray,caption,closetext,right,left,center,autostatuscap,padx,pady,below,above,vcenter,donothing',
+OLp1or2co='nofollow,background,offsetx,offsety,fgcolor,bgcolor,cgcolor,textcolor,capcolor,width,wrap,wrapmax,height,border,'
++'base,status,autostatus,snapx,snapy,fixx,fixy,relx,rely,midx,midy,ref,refc,refp,refx,refy,fgbackground,bgbackground,'
++'cgbackground,fullhtml,capicon,textfont,captionfont,textsize,captionsize,timeout,delay,hauto,vauto,nojustx,nojusty,fgclass,'
++'bgclass,cgclass,capbelow,textpadding,textfontclass,captionpadding,captionfontclass,sticky,noclose,mouseoff,offdelay,'
++'closecolor,closefont,closesize,closeclick,closetitle,closefontclass,decode',OLp1or2o='text,cap,close,hpos,vpos,padxl,'
++'padxr,padyt,padyb',OLp1co='label',OLp1or2=OLp1or2co+','+OLp1or2o,OLp1=OLp1co+','+'frame';
+OLregCmds(OLp1or2c+','+OLp1or2co+','+OLp1co);
+function OLud(v){return eval('typeof ol_'+v+'=="undefined"')?1:0;}
+
+// DEFAULT CONFIGURATION -- See overlibConfig.txt for descriptions
+if(OLud('fgcolor'))var ol_fgcolor="#ccccff";
+if(OLud('bgcolor'))var ol_bgcolor="#333399";
+if(OLud('cgcolor'))var ol_cgcolor="#333399";
+if(OLud('textcolor'))var ol_textcolor="#000000";
+if(OLud('capcolor'))var ol_capcolor="#ffffff";
+if(OLud('closecolor'))var ol_closecolor="#eeeeff";
+if(OLud('textfont'))var ol_textfont="Verdana,Arial,Helvetica";
+if(OLud('captionfont'))var ol_captionfont="Verdana,Arial,Helvetica";
+if(OLud('closefont'))var ol_closefont="Verdana,Arial,Helvetica";
+if(OLud('textsize'))var ol_textsize=1;
+if(OLud('captionsize'))var ol_captionsize=1;
+if(OLud('closesize'))var ol_closesize=1;
+if(OLud('fgclass'))var ol_fgclass="";
+if(OLud('bgclass'))var ol_bgclass="";
+if(OLud('cgclass'))var ol_cgclass="";
+if(OLud('textpadding'))var ol_textpadding=2;
+if(OLud('textfontclass'))var ol_textfontclass="";
+if(OLud('captionpadding'))var ol_captionpadding=2;
+if(OLud('captionfontclass'))var ol_captionfontclass="";
+if(OLud('closefontclass'))var ol_closefontclass="";
+if(OLud('close'))var ol_close="Close";
+if(OLud('closeclick'))var ol_closeclick=0;
+if(OLud('closetitle'))var ol_closetitle="Click to Close";
+if(OLud('text'))var ol_text="Default Text";
+if(OLud('cap'))var ol_cap="";
+if(OLud('capbelow'))var ol_capbelow=0;
+if(OLud('background'))var ol_background="";
+if(OLud('width'))var ol_width=200;
+if(OLud('wrap'))var ol_wrap=0;
+if(OLud('wrapmax'))var ol_wrapmax=0;
+if(OLud('height'))var ol_height= -1;
+if(OLud('border'))var ol_border=1;
+if(OLud('base'))var ol_base=0;
+if(OLud('offsetx'))var ol_offsetx=10;
+if(OLud('offsety'))var ol_offsety=10;
+if(OLud('sticky'))var ol_sticky=0;
+if(OLud('nofollow'))var ol_nofollow=0;
+if(OLud('noclose'))var ol_noclose=0;
+if(OLud('mouseoff'))var ol_mouseoff=0;
+if(OLud('offdelay'))var ol_offdelay=300;
+if(OLud('hpos'))var ol_hpos=RIGHT;
+if(OLud('vpos'))var ol_vpos=BELOW;
+if(OLud('status'))var ol_status="";
+if(OLud('autostatus'))var ol_autostatus=0;
+if(OLud('snapx'))var ol_snapx=0;
+if(OLud('snapy'))var ol_snapy=0;
+if(OLud('fixx'))var ol_fixx= -1;
+if(OLud('fixy'))var ol_fixy= -1;
+if(OLud('relx'))var ol_relx=null;
+if(OLud('rely'))var ol_rely=null;
+if(OLud('midx'))var ol_midx=null;
+if(OLud('midy'))var ol_midy=null;
+if(OLud('ref'))var ol_ref="";
+if(OLud('refc'))var ol_refc='UL';
+if(OLud('refp'))var ol_refp='UL';
+if(OLud('refx'))var ol_refx=0;
+if(OLud('refy'))var ol_refy=0;
+if(OLud('fgbackground'))var ol_fgbackground="";
+if(OLud('bgbackground'))var ol_bgbackground="";
+if(OLud('cgbackground'))var ol_cgbackground="";
+if(OLud('padxl'))var ol_padxl=1;
+if(OLud('padxr'))var ol_padxr=1;
+if(OLud('padyt'))var ol_padyt=1;
+if(OLud('padyb'))var ol_padyb=1;
+if(OLud('fullhtml'))var ol_fullhtml=0;
+if(OLud('capicon'))var ol_capicon="";
+if(OLud('frame'))var ol_frame=self;
+if(OLud('timeout'))var ol_timeout=0;
+if(OLud('delay'))var ol_delay=0;
+if(OLud('hauto'))var ol_hauto=0;
+if(OLud('vauto'))var ol_vauto=0;
+if(OLud('nojustx'))var ol_nojustx=0;
+if(OLud('nojusty'))var ol_nojusty=0;
+if(OLud('label'))var ol_label="";
+if(OLud('decode'))var ol_decode=0;
+// ARRAY CONFIGURATION - See overlibConfig.txt for descriptions.
+if(OLud('texts'))var ol_texts=new Array("Text 0","Text 1");
+if(OLud('caps'))var ol_caps=new Array("Caption 0","Caption 1");
+// END CONFIGURATION -- Don't change anything below, all configuration is above.
+
+// INIT -- Runtime variables.
+var o3_text="",o3_cap="",o3_sticky=0,o3_nofollow=0,o3_background="",o3_noclose=0,o3_mouseoff=0,o3_offdelay=300,o3_hpos=RIGHT,
+o3_offsetx=10,o3_offsety=10,o3_fgcolor="",o3_bgcolor="",o3_cgcolor="",o3_textcolor="",o3_capcolor="",o3_closecolor="",
+o3_width=200,o3_wrap=0,o3_wrapmax=0,o3_height= -1,o3_border=1,o3_base=0,o3_status="",o3_autostatus=0,o3_snapx=0,o3_snapy=0,
+o3_fixx= -1,o3_fixy= -1,o3_relx=null,o3_rely=null,o3_midx=null,o3_midy=null,o3_ref="",o3_refc='UL',o3_refp='UL',o3_refx=0,
+o3_refy=0,o3_fgbackground="",o3_bgbackground="",o3_cgbackground="",o3_padxl=0,o3_padxr=0,o3_padyt=0,o3_padyb=0,o3_fullhtml=0,
+o3_vpos=BELOW,o3_capicon="",o3_textfont="Verdana,Arial,Helvetica",o3_captionfont="",o3_closefont="",o3_textsize=1,OLcC=null,
+o3_captionsize=1,o3_closesize=1,o3_frame=self,o3_timeout=0,o3_delay=0,o3_hauto=0,o3_vauto=0,o3_nojustx=0,o3_nojusty=0,
+o3_close="",o3_closeclick=0,o3_closetitle="",o3_fgclass="",o3_bgclass="",o3_cgclass="",o3_textpadding=2,o3_textfontclass="",
+o3_captionpadding=2,o3_captionfontclass="",o3_closefontclass="",o3_capbelow=0,o3_label="",o3_decode=0,
+CSSOFF=DONOTHING,CSSCLASS=DONOTHING,over=null,OLdelayid=0,OLtimerid=0,OLshowid=0,OLndt=0,OLfnRef="",OLhover=0,OLx=0,OLy=0,
+OLshowingsticky=0,OLallowmove=0,OLoverHTML="",OLover2HTML="",OLifRef="",OLo2Ref="",OLifX=0,OLifY=0,
+OLua=(OLv=navigator.userAgent)?OLv.toLowerCase():'',
+OLns4=(navigator.appName=='Netscape'&&parseInt(navigator.appVersion)==4)?1:0,
+OLns6=(document.getElementById)?1:0,
+OLie4=(document.all)?1:0,
+OLgek=(OLv=OLua.match(/gecko\/(\d{8})/i))?parseInt(OLv[1]):0,
+OLmac=(OLua.indexOf('mac')>=0)?1:0,
+OLsaf=(OLua.indexOf('safari')>=0)?1:0,
+OLkon=(OLua.indexOf('konqueror')>=0)?1:0,
+OLkht=(OLsaf||OLkon)?1:0,
+OLopr=(OLua.indexOf('opera')>=0)?1:0,
+OLop7=(OLopr&&document.createTextNode)?1:0;
+if(OLopr){OLns4=OLns6=OLgek=0;OLie4=(OLop7)?1:0;}
+var OLieM=((OLie4&&OLmac)&&!(OLkht||OLopr))?1:0,
+OLie5=0,OLie55=0;OLie7=0;if(OLie4&&!OLop7){
+if((OLv=OLua.match(/msie (\d\.\d+)\.*/i))&&(OLv=parseFloat(OLv[1]))>=5.0){
+OLie5=1;OLns6=0;if(OLv>=5.5)OLie55=1;if(OLv>=7.0)OLie7=1;}if(OLns6)OLie4=0;}
+if(OLns4)window.onresize=function(){location.reload();};var OLchkMh=1,OLdw;
+if(OLns4||OLie4||OLns6){OLmh();if(window.addEventListener)window.addEventListener("unload",
+OLulCl,false);}else{overlib=nd=cClick=OLpageDefaults=no_overlib;}
+function OLulCl(){if(over)cClick();window.removeEventListener("unload",OLulCl,false);}
+
+/*
+ PUBLIC FUNCTIONS
+*/
+// Loads defaults then args into runtime variables.
+function overlib(){
+if(!(OLloaded&&OLgateOK))return;if((OLexclusivePI)&&OLisExclusive(arguments))return true;if(OLchkMh)OLmh();
+if(OLndt&&!OLtimerid)OLndt=0;if(over)cClick();if(parent!=self){if(parent.OLo2Ref){parent.OLeval(parent.OLo2Ref);
+parent.OLo2Ref="";}if(parent.OLifRef){parent.OLeval(parent.OLifRef);parent.OLifRef="";}}if(OLo2Ref){eval(OLo2Ref);
+OLo2Ref="";}if(OLifRef){eval(OLifRef);OLifRef="";}OLload(OLp1or2);OLload(OLp1);OLfnRef="";OLifX=0;OLifY=0;OLhover=0;
+OLsetRunTimeVar();OLparseTokens('o3_',arguments);if(!(over=OLmkLyr()))return false;if(o3_decode)OLdecode();if(OLprintPI)
+OLchkPrint();if(OLbubblePI)OLchkForBubbleEffect();if(OLdebugPI)OLsetDebugCanShow();if(OLshadowPI)OLinitShadow();
+if(OLiframePI)OLinitIfs();if(OLfilterPI)OLinitFilterLyr();if(OLexclusivePI&&o3_exclusive&&o3_exclusivestatus!="")
+o3_status=o3_exclusivestatus;else if(o3_autostatus==2&&o3_cap!="")o3_status=o3_cap;else if(o3_autostatus==1&&o3_text!="")
+o3_status=o3_text;if(!o3_delay){return OLmain();}else{OLdelayid=setTimeout("OLmain()",o3_delay);if(o3_status!=""){
+self.status=o3_status;return true;}else if(!(OLop7&&event&&event.type=='mouseover'))return false;}
+}
+function OLeval(s){eval(s);}
+
+// Clears popups if appropriate
+function nd(time){
+if(OLloaded&&OLgateOK){if(!((OLexclusivePI)&&OLisExclusive())){if(time&&over&&!o3_delay){
+if(OLtimerid>0)clearTimeout(OLtimerid);OLtimerid=(OLhover&&o3_frame==self&&!OLcursorOff())?0:
+setTimeout("cClick()",(o3_timeout=OLndt=time));}else{if(!OLshowingsticky){OLallowmove=0;
+if(over)OLhideObject(over);}}}}return false;
+}
+
+// Close function for stickies
+function cClick(){
+if(OLloaded&&OLgateOK){OLhover=0;if(over){if(OLo2Ref){eval(OLo2Ref);OLo2Ref="";}if(OLovertwoPI&&over==over2)cClick2();
+OLhideObject(over);OLshowingsticky=0;OLallowmove=0;}if(OLmodalPI)OLclearModal();}return false;
+}
+
+// Sets page-specific defaults.
+function OLpageDefaults(){
+OLparseTokens('ol_',arguments);
+}
+
+// Gets object referenced by its id or name
+function OLgetRef(l,d){var r=OLgetRefById(l,d);return (r)?r:OLgetRefByName(l,d);}
+
+// For unsupported browsers.
+function no_overlib(){return false;}
+
+/*
+ OVERLIB MAIN FUNCTION SET
+*/
+function OLmain(){
+o3_delay=0;if(parent!=self&&o3_frame==parent&&parent.OLscrollPI&&parent.over)parent.OLclearScroll();if(o3_frame==self){
+if(o3_noclose)OLoptMOUSEOFF(0);else if(o3_mouseoff)OLoptMOUSEOFF(1);}if(o3_sticky){OLshowingsticky=1;if(OLfnRef&&
+parent!=self&&o3_frame==parent&&parent.overlib){parent.OLifRef=OLfnRef+'cClick()';}}OLdoLyr();OLallowmove=0;if(o3_timeout>0){
+if(OLtimerid>0)clearTimeout(OLtimerid);OLtimerid=setTimeout("cClick()",o3_timeout);}OLchkRef();OLdisp(o3_status);
+if(OLdraggablePI)OLcheckDrag();if(o3_status!="")return true;else if(!(OLop7&&event&&event.type=='mouseover'))return false;
+}
+function OLchkRef(){
+if(o3_ref){OLrefXY=OLgetRefXY(o3_ref);if(OLrefXY[0]==null&&OLcrossframePI)OLchkIfRef();
+if(OLrefXY[0]==null){o3_ref="";o3_midx=0;o3_midy=0;}}
+}
+
+// Loads o3_ variables
+function OLload(c){var i,m=c.split(',');for(i=0;i<m.length;i++)eval('o3_'+m[i]+'=ol_'+m[i]);}
+
+// Chooses LGF
+function OLdoLGF(){
+return (o3_background!=''||o3_fullhtml)?OLcontentBackground(o3_text,o3_background,o3_fullhtml):(o3_cap=="")?
+OLcontentSimple(o3_text):(o3_sticky)?OLcontentCaption(o3_text,o3_cap,o3_close):OLcontentCaption(o3_text,o3_cap,'');
+}
+
+// Makes Layer
+function OLmkLyr(id,f,z){
+id=(id||'overDiv');f=(f||o3_frame);z=(z||1000);var fd=f.document,d=OLgetRefById(id,fd);
+if(!d){if(OLns4)d=fd.layers[id]=new Layer(1024,f);else if(OLie4&&!OLop7){
+fd.body.insertAdjacentHTML('AfterBegin','<div id="'+id+'"></div>');d=fd.all[id];}else{d=fd.createElement('div');
+if(d){d.id=id;fd.body.appendChild(d);}}if(!d)return null;if(OLns4)d.zIndex=z;else{var o=d.style;o.position='absolute';
+o.visibility='hidden';o.zIndex=z;}}return d;
+}
+
+// Creates and writes layer content
+function OLdoLyr(){
+if(o3_sticky&&OLtimerid>0){clearTimeout(OLtimerid);OLtimerid=0;}if(o3_background==''&&!o3_fullhtml){
+if(o3_fgbackground!='')o3_fgbackground=' background="'+o3_fgbackground+'"';
+if(o3_bgbackground!='')o3_bgbackground=' background="'+o3_bgbackground+'"';
+if(o3_cgbackground!='')o3_cgbackground=' background="'+o3_cgbackground+'"';
+if(o3_fgcolor!='')o3_fgcolor=' bgcolor="'+o3_fgcolor+'"';if(o3_bgcolor!='')o3_bgcolor=' bgcolor="'+o3_bgcolor+'"';
+if(o3_cgcolor!='')o3_cgcolor=' bgcolor="'+o3_cgcolor+'"';if(o3_height>0)o3_height=' height="'+o3_height+'"';
+else o3_height='';}if(!OLns4)OLrepositionTo(over,(OLns6?20:0),0);var lyrHtml=OLdoLGF();
+if(o3_wrap&&!o3_fullhtml){OLlayerWrite(lyrHtml);o3_width=(OLns4?over.clip.width:over.offsetWidth);if(OLie4){
+var w=OLfd().clientWidth;if(o3_width>=w){if(OLop7){if(OLovertwoPI&&over==over2){var z=over2.style.zIndex;
+o3_frame.document.body.removeChild(over);over2=OLmkLyr('overDiv2',o3_frame,z);over=over2;}else{
+o3_frame.document.body.removeChild(over);over=OLmkLyr();}}o3_width=w-20;}}
+if(o3_wrapmax<1&&o3_frame.innerWidth)o3_wrapmax=o3_frame.innerWidth-40;
+if(o3_wrapmax>0&&o3_width>o3_wrapmax)o3_width=o3_wrapmax;o3_wrap=0;lyrHtml=OLdoLGF();}OLlayerWrite(lyrHtml);
+o3_width=(OLns4?over.clip.width:over.offsetWidth);if(OLbubblePI)OLgenerateBubble(lyrHtml);
+}
+
+/*
+ LAYER GENERATION FUNCTIONS
+*/
+// Makes simple table without caption
+function OLcontentSimple(txt){
+var t=OLbgLGF()+OLfgLGF(txt)+OLbaseLGF();OLsetBackground('');return t;
+}
+
+// Makes table with caption and optional close link
+function OLcontentCaption(txt,title,close){
+var closing=(OLprintPI?OLprintCapLGF():''),closeevent='onmouseover',caption,t,cC='javascript:return '+OLfnRef
++(OLovertwoPI&&over==over2?'cClick2();':'cClick();');if(o3_closeclick)closeevent=(o3_closetitle?'title="'
++o3_closetitle+'" ':'')+'onclick';if(o3_capicon!=''&&o3_capicon.indexOf('<img')!=0)o3_capicon='<img src="'+o3_capicon
++'" /> ';if(close){closing+='<td align="right"><a href="'+cC+'" '+closeevent+'="'+cC+'"'+(o3_closefontclass?' class="'
++o3_closefontclass+'">':(OLns4?'><':'')+OLlgfUtil(0,1,'','a',o3_closecolor,o3_closefont,o3_closesize))+close+
+(o3_closefontclass?'':(OLns4?OLlgfUtil(1,1,'','a'):''))+'</a></td>';}caption='<table id="overCap'
++(OLovertwoPI&&over==over2?'2':'')+'"'+OLwd(0)+' border="0" cellpadding="'+o3_captionpadding+'" cellspacing="0"'
++(o3_cgclass?' class="'+o3_cgclass+'"':o3_cgcolor+o3_cgbackground)+'><tr><td'+OLwd(0)+(o3_cgclass?' class="'
++o3_cgclass+'">':'>')+(o3_captionfontclass?'<div'+OLhL(1)+' class="'+o3_captionfontclass+'">':OLlgfUtil(0,1,'','div',
+o3_capcolor,o3_captionfont,o3_captionsize))+o3_capicon+title+OLlgfUtil(1,1,'','div')+'</td>'+closing+'</tr></table>';
+t=OLbgLGF()+(o3_capbelow?OLfgLGF(txt)+caption:caption+OLfgLGF(txt))+OLbaseLGF();OLsetBackground('');return t;
+}
+
+// For BACKGROUND and FULLHTML commands
+function OLcontentBackground(txt,image,hasfullhtml){
+var t;if(hasfullhtml){t=txt;}else{t='<table'+OLwd(1)+' border="0" cellpadding="0" '+'cellspacing="0" '+'height="'
++o3_height+'"><tr><td colspan="3" height="'+o3_padyt+'"></td></tr><tr><td width="'+o3_padxl+'"></td><td valign="top"'
++OLwd(2)+'>'+OLlgfUtil(0,0,o3_textfontclass,'div',o3_textcolor,o3_textfont,o3_textsize)+txt+OLlgfUtil(1,0,'','div')
++'</td><td width="'+o3_padxr+'"></td></tr><tr><td colspan="3" height="'+o3_padyb+'"></td></tr></table>';}
+OLsetBackground(image);return t;
+}
+
+// LGF utilities
+function OLbgLGF(){
+return '<table'+OLwd(1)+o3_height+' border="0" cellpadding="'+o3_border+'" cellspacing="0"'+(o3_bgclass?' class="'
++o3_bgclass+'"':o3_bgcolor+o3_bgbackground)+'><tr><td>';
+}
+function OLfgLGF(t){
+return '<table'+OLwd(0)+o3_height+' border="0" cellpadding="'+o3_textpadding+'" cellspacing="0"'+(o3_fgclass?' class="'
++o3_fgclass+'"':o3_fgcolor+o3_fgbackground)+'><tr><td valign="top"'+(o3_fgclass?' class="'+o3_fgclass+'"':'')+'>'
++OLlgfUtil(0,0,o3_textfontclass,'div',o3_textcolor,o3_textfont,o3_textsize)+t+(OLprintPI?OLprintFgLGF():'')
++OLlgfUtil(1,0,'','div')+'</td></tr></table>';
+}
+function OLlgfUtil(end,stg,tfc,ele,col,fac,siz){
+if(end)return('</'+(OLns4?'font'+(stg?'></strong':''):ele)+'>');else return(tfc?'<div'+OLhL(1)+' class="'+tfc+'">':
+((ele=='a'?'':'<')+(OLns4?(stg?'strong><':'')+'font color="'+col+'" face="'+OLquoteMultiNameFonts(fac)+'" size="'
++siz:(ele=='a'?'':ele)+' style="'+((ele=='div')?OLhL(0):'')+'color:'+col+(stg?';font-weight:bold':'')+';font-family:'
++OLquoteMultiNameFonts(fac)+';font-size:'+siz+';'+(ele=='span'?'text-decoration:underline;':''))+'">'));
+}
+function OLquoteMultiNameFonts(f){
+var i,v,pM=f.split(',');for(i=0;i<pM.length;i++){v=pM[i];v=v.replace(/^\s+/,'').replace(/\s+$/,'');
+if(/\s/.test(v) && !/['"]/.test(v)){v="\'"+v+"\'";pM[i]=v;}}return pM.join();
+}
+function OLbaseLGF(){
+return ((o3_base>0&&!o3_wrap)?('<table width="100%" border="0" cellpadding="0" cellspacing="0"'+(o3_bgclass?' class="'
++o3_bgclass+'"':'')+'><tr><td height="'+o3_base+'"></td></tr></table>'):'')+'</td></tr></table>';
+}
+function OLwd(a){return(o3_wrap?'':' width="'+(!a?'100%':(a==1?o3_width:(o3_width-o3_padxl-o3_padxr)))+'"');}
+function OLhL(s){return(s?' style="width:100%;"':'width:100%;');}
+
+// Loads image into the div.
+function OLsetBackground(i){
+if(i==''){if(OLns4)over.background.src=null;else{if(OLns6)over.style.width='';over.style.backgroundImage='none';}}
+else{if(OLns4)over.background.src=i;else{if(OLns6)over.style.width=o3_width+'px';over.style.backgroundImage='url('+i+')';}}
+}
+
+/*
+ HANDLING FUNCTIONS
+*/
+// Displays layer
+function OLdisp(s){
+if(OLmodalPI&&!o3_modalscroll)OLchkModal();if(!OLallowmove){if(OLshadowPI)OLdispShadow();if(OLiframePI)OLdispIfs();
+OLplaceLayer();if(OLmodalPI&&o3_modalscroll)OLchkModal();if(OLndt)OLshowObject(over);
+else OLshowid=setTimeout("OLshowObject(over)",1);OLallowmove=(o3_sticky||o3_nofollow)?0:1;}OLndt=0;if(s!="")self.status=s;
+}
+
+// Decides placement of layer.
+function OLplaceLayer(){
+var snp,X,Y,pgLeft,pgTop,pWd=o3_width,pHt,iWd=100,iHt=100,SB=0,LM=0,CX=0,TM=0,BM=0,CY=0,o=OLfd(),
+nsb=(OLgek>=20010505&&!o3_frame.scrollbars.visible)?1:0;
+if(!OLkht&&o&&o.clientWidth)iWd=o.clientWidth;
+else if(o3_frame.innerWidth){SB=Math.ceil(1.4*(o3_frame.outerWidth-o3_frame.innerWidth));
+if(SB>20)SB=20;iWd=o3_frame.innerWidth;}
+pgLeft=(OLie4)?o.scrollLeft:o3_frame.pageXOffset;
+if(OLie55&&OLfilterPI&&o3_filter&&o3_filtershadow)SB=CX=5;else
+if((OLshadowPI)&&bkdrop&&o3_shadow&&o3_shadowx){SB+=((o3_shadowx>0)?o3_shadowx:0);
+LM=((o3_shadowx<0)?Math.abs(o3_shadowx):0);CX=Math.abs(o3_shadowx);}
+if(o3_ref!=""||o3_fixx> -1||o3_relx!=null||o3_midx!=null){
+if(o3_ref!=""){X=OLrefXY[0];if(OLie55&&OLfilterPI&&o3_filter&&o3_filtershadow){
+if(o3_refp=='UR'||o3_refp=='LR')X-=5;}
+else if((OLshadowPI)&&bkdrop&&o3_shadow&&o3_shadowx){
+if(o3_shadowx<0&&(o3_refp=='UL'||o3_refp=='LL'))X-=o3_shadowx;else
+if(o3_shadowx>0&&(o3_refp=='UR'||o3_refp=='LR'))X-=o3_shadowx;}
+}else{if(o3_midx!=null){
+X=parseInt(pgLeft+((iWd-pWd-SB-LM)/2)+o3_midx);
+}else{if(o3_relx!=null){
+if(o3_relx>=0)X=pgLeft+o3_relx+LM;else X=pgLeft+o3_relx+iWd-pWd-SB;
+}else{X=o3_fixx+LM;}}}
+}else{
+if(o3_hauto){
+if(o3_hpos==LEFT&&OLx-pgLeft+OLifX<iWd/2&&OLx-pWd-o3_offsetx+OLifX<pgLeft+LM)o3_hpos=RIGHT;else
+if(o3_hpos==RIGHT&&OLx-pgLeft+OLifX>iWd/2&&OLx+pWd+o3_offsetx+OLifX>pgLeft+iWd-SB)o3_hpos=LEFT;}
+X=(o3_hpos==CENTER)?parseInt(OLx-((pWd+CX)/2)+o3_offsetx):
+(o3_hpos==LEFT)?OLx-o3_offsetx-pWd:OLx+o3_offsetx;
+if(o3_snapx>1){
+snp=X % o3_snapx;
+if(o3_hpos==LEFT){X=X-(o3_snapx+snp);}else{X=X+(o3_snapx-snp);}}X+=OLifX;}
+if(!o3_nojustx&&X+pWd>pgLeft+iWd-SB)
+X=iWd+pgLeft-pWd-SB;if(!o3_nojustx&&X-LM<pgLeft)X=pgLeft+LM;
+pgTop=OLie4?o.scrollTop:o3_frame.pageYOffset;
+if(!OLkht&&!nsb&&o&&o.clientHeight)iHt=o.clientHeight;
+else if(o3_frame.innerHeight)iHt=o3_frame.innerHeight;
+if(OLbubblePI&&o3_bubble)pHt=OLbubbleHt;else pHt=OLns4?over.clip.height:over.offsetHeight;
+if((OLshadowPI)&&bkdrop&&o3_shadow&&o3_shadowy){TM=(o3_shadowy<0)?Math.abs(o3_shadowy):0;
+if(OLie55&&OLfilterPI&&o3_filter&&o3_filtershadow)BM=CY=5;else
+BM=(o3_shadowy>0)?o3_shadowy:0;CY=Math.abs(o3_shadowy);}
+if(o3_ref!=""||o3_fixy> -1||o3_rely!=null||o3_midy!=null){
+if(o3_ref!=""){Y=OLrefXY[1];if(OLie55&&OLfilterPI&&o3_filter&&o3_filtershadow){
+if(o3_refp=='LL'||o3_refp=='LR')Y-=5;}else if((OLshadowPI)&&bkdrop&&o3_shadow&&o3_shadowy){
+if(o3_shadowy<0&&(o3_refp=='UL'||o3_refp=='UR'))Y-=o3_shadowy;else
+if(o3_shadowy>0&&(o3_refp=='LL'||o3_refp=='LR'))Y-=o3_shadowy;}
+}else{if(o3_midy!=null){
+Y=parseInt(pgTop+((iHt-pHt-CY)/2)+o3_midy);
+}else{if(o3_rely!=null){
+if(o3_rely>=0)Y=pgTop+o3_rely+TM;else Y=pgTop+o3_rely+iHt-pHt-BM;}else{
+Y=o3_fixy+TM;}}}
+}else{
+if(o3_vauto){
+if(o3_vpos==ABOVE&&OLy-pgTop+OLifY<iHt/2&&OLy-pHt-o3_offsety+OLifY<pgTop)o3_vpos=BELOW;else
+if(o3_vpos==BELOW&&OLy-pgTop+OLifY>iHt/2&&OLy+pHt+o3_offsety+((OLns4||OLkht)?17:0)+OLifY>pgTop+iHt-BM)
+o3_vpos=ABOVE;}Y=(o3_vpos==VCENTER)?parseInt(OLy-((pHt+CY)/2)+o3_offsety):
+(o3_vpos==ABOVE)?OLy-(pHt+o3_offsety+BM):OLy+o3_offsety+TM;
+if(o3_snapy>1){
+snp=Y % o3_snapy;
+if(pHt>0&&o3_vpos==ABOVE){Y=Y-(o3_snapy+snp);}else{Y=Y+(o3_snapy-snp);}}Y+=OLifY;}
+if(!o3_nojusty&&Y+pHt+BM>pgTop+iHt)Y=pgTop+iHt-pHt-BM;if(!o3_nojusty&&Y-TM<pgTop)Y=pgTop+TM;
+OLrepositionTo(over,X,Y);
+if(OLshadowPI)OLrepositionShadow(X,Y);if(OLiframePI)OLrepositionIfs(X,Y);
+if(OLns6&&o3_frame.innerHeight){iHt=o3_frame.innerHeight;OLrepositionTo(over,X,Y);}
+if(OLscrollPI)OLchkScroll(X-pgLeft,Y-pgTop);
+}
+
+// Chooses body or documentElement
+function OLfd(f){
+var fd=((f)?f:o3_frame).document,fdc=fd.compatMode,fdd=fd.documentElement;
+return (!OLop7&&fdc&&fdc!='BackCompat'&&fdd&&fdd.clientWidth)?fd.documentElement:fd.body;
+}
+
+// Gets location of REFerence object
+function OLgetRefXY(r,d){
+var o=OLgetRef(r,d),ob=o,rXY=[o3_refx,o3_refy],of;if(!o)return [null,null];if(OLns4){
+if(typeof o.length!='undefined'&&o.length>1){ob=o[0];rXY[0]+=o[0].x+o[1].pageX;rXY[1]+=o[0].y+o[1].pageY;}else{
+if((o.toString().indexOf('Image')!= -1)||(o.toString().indexOf('Anchor')!= -1)){rXY[0]+=o.x;rXY[1]+=o.y;}
+else{rXY[0]+=o.pageX;rXY[1]+=o.pageY;}}}else{rXY[0]+=OLpageLoc(o,'Left');rXY[1]+=OLpageLoc(o,'Top');}
+of=OLgetRefOffsets(ob);rXY[0]+=of[0];rXY[1]+=of[1];return rXY;
+}
+
+// Seeks REFerence by id
+function OLgetRefById(l,d){
+l=(l||'overDiv');d=(d||o3_frame.document);var j,r;if(d.getElementById)return d.getElementById(l);
+if(OLie4&&d.all)return d.all[l];if(d.layers&&d.layers.length>0){if(d.layers[l])return d.layers[l];
+for(j=0;j<d.layers.length;j++){r=OLgetRefById(l,d.layers[j].document);if(r)return r;}}return null;
+}
+
+// Seeks REFerence by name
+function OLgetRefByName(l,d){
+d=(d||o3_frame.document);var j,r,v=OLie4?d.all.tags('iframe'):OLns6?d.getElementsByTagName('iframe'):null;
+if(typeof d.images!='undefined'&&d.images[l])return d.images[l];
+if(typeof d.anchors!='undefined'&&d.anchors[l])return d.anchors[l];
+if(v)for(j=0;j<v.length;j++)if(v[j].name==l)return v[j];if(d.layers&&d.layers.length>0)for(j=0;j<d.layers.length;j++){
+r=OLgetRefByName(l,d.layers[j].document);if(r&&r.length>0)return r;else if(r)return [r,d.layers[j]];}return null;
+}
+
+// Gets layer vs REFerence offsets
+function OLgetRefOffsets(o){
+var c=o3_refc.toUpperCase(),p=o3_refp.toUpperCase(),W=0,H=0,pW=0,pH=0,of=[0,0];pW=(OLbubblePI&&o3_bubble)?
+o3_width:OLns4?over.clip.width:over.offsetWidth;pH=(OLbubblePI&&o3_bubble)?OLbubbleHt:OLns4?
+over.clip.height:over.offsetHeight;if((!OLop7)&&o.toString().indexOf('Image')!= -1){W=o.width;H=o.height;}
+else if((!OLop7)&&o.toString().indexOf('Anchor')!= -1){c=o3_refc='UL';}else{W=(OLns4)?o.clip.width:o.offsetWidth;
+H=(OLns4)?o.clip.height:o.offsetHeight;}if((OLns4||(OLns6&&OLgek))&&o.border){W+=2*parseInt(o.border);
+H+=2*parseInt(o.border);}if(c=='UL'){of=(p=='UR')?[-pW,0]:(p=='LL')?[0,-pH]:(p=='LR')?[-pW,-pH]:[0,0];}else if(c=='UR'){
+of=(p=='UR')?[W-pW,0]:(p=='LL')?[W,-pH]:(p=='LR')?[W-pW,-pH]:[W,0];}else if(c=='LL'){of=(p=='UR')?[-pW,H]:(p=='LL')?[0,H-pH]:
+(p=='LR')?[-pW,H-pH]:[0,H];}else if(c=='LR'){of=(p=='UR')?[W-pW,H]:(p=='LL')?[W,H-pH]:(p=='LR')?[W-pW,H-pH]:[W,H];}return of;
+}
+
+// Gets x or y location of object
+function OLpageLoc(o,t){
+var l=0,s=o;while(o.offsetParent&&o.offsetParent.tagName.toLowerCase()!='html'){l+=o['offset'+t];o=o.offsetParent;}
+l+=o['offset'+t];while(s=s.parentNode){if((s['scroll'+t]>0)&&s.tagName.toLowerCase()=='div')l-=s['scroll'+t];}return l;
+}
+
+// Moves layer
+function OLmouseMove(e){
+var e=(e||event);OLcC=(OLovertwoPI&&over2&&over==over2?cClick2:cClick);OLx=(e.pageX||e.clientX+OLfd().scrollLeft);
+OLy=(e.pageY||e.clientY+OLfd().scrollTop);if((OLallowmove&&over)&&(o3_frame==self||over==OLgetRefById()||(OLovertwoPI&&
+over2==over&&over==OLgetRefById('overDiv2')))){OLplaceLayer();if(OLhidePI)OLhideUtil(0,1,1,0,0,0);}if(OLhover&&over&&
+o3_frame==self&&OLcursorOff())if(o3_offdelay<1)OLcC();else{if(OLtimerid>0)clearTimeout(OLtimerid);
+OLtimerid=setTimeout("OLcC()",o3_offdelay);}
+}
+
+// Capture mouse and chain other scripts.
+function OLmh(){
+var fN,f,j,k,s,mh=OLmouseMove,w=(OLns4&&window.onmousemove),re=/function[ ]*(\w*)\(/;OLdw=document;if(document.onmousemove||
+w){if(w)OLdw=window;f=OLdw.onmousemove.toString();fN=f.match(re);if(!fN||fN[1]=='anonymous'||fN[1]=='OLmouseMove'){OLchkMh=0;
+return;}if(fN[1])s=fN[1]+'(e)';else{j=f.indexOf('{');k=f.lastIndexOf('}')+1;s=f.substring(j,k);}s+=';OLmouseMove(e);';
+mh=new Function('e',s);}OLdw.onmousemove=mh;if(OLns4)OLdw.captureEvents(Event.MOUSEMOVE);
+}
+
+/*
+ PARSING
+*/
+function OLparseTokens(pf,ar){
+var i,v,md= -1,par=(pf!='ol_'),p=OLpar,q=OLparQuo,t=OLtoggle;OLudf=(par&&!ar.length?1:0);
+for(i=0;i<ar.length;i++){if(md<0){if(typeof ar[i]=='number'){OLudf=(par?1:0);i--;}
+else{switch(pf){case 'ol_':ol_text=ar[i];break;default:o3_text=ar[i];}}md=0;}else{
+if(ar[i]==INARRAY){OLudf=0;eval(pf+'text=ol_texts['+ar[++i]+']');continue;}
+if(ar[i]==CAPARRAY){eval(pf+'cap=ol_caps['+ar[++i]+']');continue;}
+if(ar[i]==CAPTION){q(ar[++i],pf+'cap');continue;}
+if(Math.abs(ar[i])==STICKY){t(ar[i],pf+'sticky');continue;}
+if(Math.abs(ar[i])==NOFOLLOW){t(ar[i],pf+'nofollow');continue;}
+if(ar[i]==BACKGROUND){q(ar[++i],pf+'background');continue;}
+if(Math.abs(ar[i])==NOCLOSE){t(ar[i],pf+'noclose');continue;}
+if(Math.abs(ar[i])==MOUSEOFF){t(ar[i],pf+'mouseoff');continue;}
+if(ar[i]==OFFDELAY){p(ar[++i],pf+'offdelay');continue;}
+if(ar[i]==RIGHT||ar[i]==LEFT||ar[i]==CENTER){p(ar[i],pf+'hpos');continue;}
+if(ar[i]==OFFSETX){p(ar[++i],pf+'offsetx');continue;}
+if(ar[i]==OFFSETY){p(ar[++i],pf+'offsety');continue;}
+if(ar[i]==FGCOLOR){q(ar[++i],pf+'fgcolor');continue;}
+if(ar[i]==BGCOLOR){q(ar[++i],pf+'bgcolor');continue;}
+if(ar[i]==CGCOLOR){q(ar[++i],pf+'cgcolor');continue;}
+if(ar[i]==TEXTCOLOR){q(ar[++i],pf+'textcolor');continue;}
+if(ar[i]==CAPCOLOR){q(ar[++i],pf+'capcolor');continue;}
+if(ar[i]==CLOSECOLOR){q(ar[++i],pf+'closecolor');continue;}
+if(ar[i]==WIDTH){p(ar[++i],pf+'width');continue;}
+if(Math.abs(ar[i])==WRAP){t(ar[i],pf+'wrap');continue;}
+if(ar[i]==WRAPMAX){p(ar[++i],pf+'wrapmax');continue;}
+if(ar[i]==HEIGHT){p(ar[++i],pf+'height');continue;}
+if(ar[i]==BORDER){p(ar[++i],pf+'border');continue;}
+if(ar[i]==BASE){p(ar[++i],pf+'base');continue;}
+if(ar[i]==STATUS){q(ar[++i],pf+'status');continue;}
+if(Math.abs(ar[i])==AUTOSTATUS){v=pf+'autostatus';
+eval(v+'=('+ar[i]+'<0)?('+v+'==2?2:0):('+v+'==1?0:1)');continue;}
+if(Math.abs(ar[i])==AUTOSTATUSCAP){v=pf+'autostatus';
+eval(v+'=('+ar[i]+'<0)?('+v+'==1?1:0):('+v+'==2?0:2)');continue;}
+if(ar[i]==CLOSETEXT){q(ar[++i],pf+'close');continue;}
+if(ar[i]==SNAPX){p(ar[++i],pf+'snapx');continue;}
+if(ar[i]==SNAPY){p(ar[++i],pf+'snapy');continue;}
+if(ar[i]==FIXX){p(ar[++i],pf+'fixx');continue;}
+if(ar[i]==FIXY){p(ar[++i],pf+'fixy');continue;}
+if(ar[i]==RELX){p(ar[++i],pf+'relx');continue;}
+if(ar[i]==RELY){p(ar[++i],pf+'rely');continue;}
+if(ar[i]==MIDX){p(ar[++i],pf+'midx');continue;}
+if(ar[i]==MIDY){p(ar[++i],pf+'midy');continue;}
+if(ar[i]==REF){q(ar[++i],pf+'ref');continue;}
+if(ar[i]==REFC){q(ar[++i],pf+'refc');continue;}
+if(ar[i]==REFP){q(ar[++i],pf+'refp');continue;}
+if(ar[i]==REFX){p(ar[++i],pf+'refx');continue;}
+if(ar[i]==REFY){p(ar[++i],pf+'refy');continue;}
+if(ar[i]==FGBACKGROUND){q(ar[++i],pf+'fgbackground');continue;}
+if(ar[i]==BGBACKGROUND){q(ar[++i],pf+'bgbackground');continue;}
+if(ar[i]==CGBACKGROUND){q(ar[++i],pf+'cgbackground');continue;}
+if(ar[i]==PADX){p(ar[++i],pf+'padxl');p(ar[++i],pf+'padxr');continue;}
+if(ar[i]==PADY){p(ar[++i],pf+'padyt');p(ar[++i],pf+'padyb');continue;}
+if(Math.abs(ar[i])==FULLHTML){t(ar[i],pf+'fullhtml');continue;}
+if(ar[i]==BELOW||ar[i]==ABOVE||ar[i]==VCENTER){p(ar[i],pf+'vpos');continue;}
+if(ar[i]==CAPICON){q(ar[++i],pf+'capicon');continue;}
+if(ar[i]==TEXTFONT){q(ar[++i],pf+'textfont');continue;}
+if(ar[i]==CAPTIONFONT){q(ar[++i],pf+'captionfont');continue;}
+if(ar[i]==CLOSEFONT){q(ar[++i],pf+'closefont');continue;}
+if(ar[i]==TEXTSIZE){q(ar[++i],pf+'textsize');continue;}
+if(ar[i]==CAPTIONSIZE){q(ar[++i],pf+'captionsize');continue;}
+if(ar[i]==CLOSESIZE){q(ar[++i],pf+'closesize');continue;}
+if(ar[i]==TIMEOUT){p(ar[++i],pf+'timeout');continue;}
+if(ar[i]==DELAY){p(ar[++i],pf+'delay');continue;}
+if(Math.abs(ar[i])==HAUTO){t(ar[i],pf+'hauto');continue;}
+if(Math.abs(ar[i])==VAUTO){t(ar[i],pf+'vauto');continue;}
+if(Math.abs(ar[i])==NOJUSTX){t(ar[i],pf+'nojustx');continue;}
+if(Math.abs(ar[i])==NOJUSTY){t(ar[i],pf+'nojusty');continue;}
+if(Math.abs(ar[i])==CLOSECLICK){t(ar[i],pf+'closeclick');continue;}
+if(ar[i]==CLOSETITLE){q(ar[++i],pf+'closetitle');continue;}
+if(ar[i]==FGCLASS){q(ar[++i],pf+'fgclass');continue;}
+if(ar[i]==BGCLASS){q(ar[++i],pf+'bgclass');continue;}
+if(ar[i]==CGCLASS){q(ar[++i],pf+'cgclass');continue;}
+if(ar[i]==TEXTPADDING){p(ar[++i],pf+'textpadding');continue;}
+if(ar[i]==TEXTFONTCLASS){q(ar[++i],pf+'textfontclass');continue;}
+if(ar[i]==CAPTIONPADDING){p(ar[++i],pf+'captionpadding');continue;}
+if(ar[i]==CAPTIONFONTCLASS){q(ar[++i],pf+'captionfontclass');continue;}
+if(ar[i]==CLOSEFONTCLASS){q(ar[++i],pf+'closefontclass');continue;}
+if(Math.abs(ar[i])==CAPBELOW){t(ar[i],pf+'capbelow');continue;}
+if(ar[i]==LABEL){q(ar[++i],pf+'label');continue;}
+if(Math.abs(ar[i])==DECODE){t(ar[i],pf+'decode');continue;}
+if(ar[i]==DONOTHING){continue;}
+i=OLparseCmdLine(pf,i,ar);}}
+if((OLfunctionPI)&&OLudf&&o3_function)o3_text=o3_function();
+if(pf=='o3_')OLfontSize();
+}
+function OLpar(a,v){eval(v+'='+a);}
+function OLparQuo(a,v){eval(v+"='"+OLescSglQt(a)+"'");}
+function OLescSglQt(s){return s.toString().replace(/\\/g,"\\\\").replace(/'/g,"\\'");}
+function OLtoggle(a,v){eval(v+'=('+v+'==0&&'+a+'>=0)?1:0');}
+function OLhasDims(s){return /[%\-a-z]+$/.test(s);}
+function OLfontSize(){
+var i;if(OLhasDims(o3_textsize)){if(OLns4)o3_textsize="2";}else
+if(!OLns4){i=parseInt(o3_textsize);o3_textsize=(i>0&&i<8)?OLpct[i]:OLpct[0];}
+if(OLhasDims(o3_captionsize)){if(OLns4)o3_captionsize="2";}else
+if(!OLns4){i=parseInt(o3_captionsize);o3_captionsize=(i>0&&i<8)?OLpct[i]:OLpct[0];}
+if(OLhasDims(o3_closesize)){if(OLns4)o3_closesize="2";}else
+if(!OLns4){i=parseInt(o3_closesize);o3_closesize=(i>0&&i<8)?OLpct[i]:OLpct[0];}
+if(OLprintPI)OLprintDims();
+}
+function OLdecode(){
+var re=/%[0-9A-Fa-f]{2,}/,t=o3_text,c=o3_cap,u=unescape,d=!OLns4&&(!OLgek||OLgek>=20020826)&&typeof decodeURIComponent?
+decodeURIComponent:u;if(typeof(window.TypeError)=='function'){if(re.test(t)){eval(new Array('try{','o3_text=d(t);',
+'}catch(e){','o3_text=u(t);','}').join('\n'))};if(c&&re.test(c)){eval(new Array('try{','o3_cap=d(c);','}catch(e){',
+'o3_cap=u(c);','}').join('\n'))}}else{if(re.test(t))o3_text=u(t);if(c&&re.test(c))o3_cap=u(c);}
+}
+
+/*
+ LAYER FUNCTIONS
+*/
+// Writes to layer
+function OLlayerWrite(t){
+t+="\n";if(OLns4){over.document.write(t);over.document.close();}else if(typeof over.innerHTML!='undefined'){
+if(OLieM)over.innerHTML='';over.innerHTML=t;}else{var range=o3_frame.document.createRange();range.setStartAfter(over);
+var domfrag=range.createContextualFragment(t);while(over.hasChildNodes()){over.removeChild(over.lastChild);}
+over.appendChild(domfrag);}if(OLovertwoPI&&over==over2)OLover2HTML=t;else OLoverHTML=t;
+if(OLprintPI)over.print=o3_print?t:null;
+}
+
+// Makes object visible
+function OLshowObject(o){
+OLshowid=0;o=(OLns4)?o:o.style;if(((OLfilterPI)&&!OLchkFilter(o))||!OLfilterPI)o.visibility="visible";
+if(OLshadowPI)OLshowShadow();if(OLiframePI)OLshowIfs();if(OLhidePI)OLhideUtil(1,1,0);
+}
+
+// Hides object
+function OLhideObject(o){
+if(OLshowid>0){clearTimeout(OLshowid);OLshowid=0;}if(OLtimerid>0)clearTimeout(OLtimerid);
+if(OLdelayid>0)clearTimeout(OLdelayid);OLtimerid=0;OLdelayid=0;self.status="";o3_label=ol_label;
+if(o3_frame!=self)o=OLgetRefById();if(o){if(o.onmouseover)o.onmouseover=null;if(OLscrollPI&&o==over)OLclearScroll();
+if(OLdraggablePI)OLclearDrag();if(OLfilterPI)OLcleanupFilter(o);if(OLshadowPI)OLhideShadow();var os=(OLns4)?o:o.style;
+if(((OLfilterPI)&&!OLchkFadeOut(os))||!OLfilterPI){os.visibility="hidden";if(!OLie55||!OLfilterPI||!o3_filter||
+o3_fadeout<0)o.innerHTML='';}if(OLhidePI&&o==over)OLhideUtil(0,0,1);if(OLiframePI)OLhideIfs(o);}
+}
+
+// Moves layer
+function OLrepositionTo(o,xL,yL){
+o=(OLns4)?o:o.style;o.left=(OLns4?xL:xL+'px');o.top=(OLns4?yL:yL+'px');
+}
+
+// Handle NOCLOSE-MOUSEOFF
+function OLoptMOUSEOFF(c){
+if(!c)o3_close="";
+over.onmouseover=function(){OLhover=1;if(OLtimerid>0){clearTimeout(OLtimerid);OLtimerid=0;}}
+}
+function OLcursorOff(){
+var o=(OLns4?over:over.style),pHt=OLns4?over.clip.height:over.offsetHeight,left=parseInt(o.left),top=parseInt(o.top),
+right=left+o3_width,bottom=top+((OLbubblePI&&o3_bubble)?OLbubbleHt:pHt);
+if(OLx<left||OLx>right||OLy<top||OLy>bottom)return true;return false;
+}
+
+/*
+ REGISTRATION
+*/
+function OLsetRunTimeVar(){
+if(OLrunTime.length)for(var k=0;k<OLrunTime.length;k++)OLrunTime[k]();
+}
+function OLparseCmdLine(pf,i,ar){
+if(OLcmdLine.length){for(var k=0;k<OLcmdLine.length;k++){var j=OLcmdLine[k](pf,i,ar);if(j>-1){i=j;break;}}}return i;
+}
+function OLregCmds(c){
+if(typeof c!='string')return;var pM=c.split(',');pMtr=pMtr.concat(pM);
+for(var i=0;i<pM.length;i++)eval(pM[i].toUpperCase()+'='+pmCnt++);
+}
+function OLregRunTimeFunc(f){
+if(typeof f=='object')OLrunTime=OLrunTime.concat(f);else OLrunTime[OLrunTime.length++]=f;
+}
+function OLregCmdLineFunc(f){
+if(typeof f=='object')OLcmdLine=OLcmdLine.concat(f);else OLcmdLine[OLcmdLine.length++]=f;
+}
+
+OLloaded=1;
diff --git a/httemplate/elements/overlibmws_crossframe.js b/httemplate/elements/overlibmws_crossframe.js
new file mode 100644
index 0000000..dd64223
--- /dev/null
+++ b/httemplate/elements/overlibmws_crossframe.js
@@ -0,0 +1,53 @@
+/*
+ overlibmws_crossframe.js plug-in module - Copyright Foteos Macrides 2003-2008. All rights reserved.
+ For support of FRAME.
+ Initial: August 3, 2003 - Last Revised: January 16, 2008
+ See the Change History and Command Reference for overlibmws via:
+
+ http://www.macridesweb.com/oltest/
+
+ Published under an open source license: http://www.macridesweb.com/oltest/license.html
+*/
+
+OLloaded=0;
+OLregCmds('frame');
+
+function OLparseCrossframe(pf,i,ar){
+var k=i,v;
+if(k<ar.length){
+if(ar[k]==FRAME){v=ar[++k];if(pf=='ol_')ol_frame=v;else OLoptFRAME(v);return k;}}
+return -1;
+}
+
+function OLgetFrameRef(thisFrame,ofrm){
+var i,v,retVal='';for(i=0;i<thisFrame.length;i++){if((((thisFrame[i].length>0)))&&(((OLns4))||
+((OLie4)&&(v=thisFrame[i].document.all.tags('iframe'))!=null&&v.length==0)||
+((OLns6)&&(v=thisFrame[i].document.getElementsByTagName('iframe'))!=null&&v.length==0))){
+retVal=OLgetFrameRef(thisFrame[i],ofrm);if(retVal=='')continue;}
+else if(thisFrame[i]!=ofrm)continue;retVal='['+i+']'+retVal;break;}
+return retVal;
+}
+
+function OLoptFRAME(frm){
+o3_frame=OLmkLyr('overDiv',frm)?frm:self;if(o3_frame!=self){var l,tFrm=OLgetFrameRef(top.frames,o3_frame),
+sFrm=OLgetFrameRef(top.frames,ol_frame);if(sFrm.length==tFrm.length) {l=tFrm.lastIndexOf('[');if(l){
+while(sFrm.substring(0,l)!=tFrm.substring(0,l))l=tFrm.lastIndexOf('[',l-1);tFrm=tFrm.substr(l);sFrm=sFrm.substr(l);}}
+var i,k,cnt=0,p='',str=tFrm;while((k=str.lastIndexOf('['))!= -1){cnt++;str=str.substring(0,k);}
+for(i=0;i<cnt;i++)p=p+'parent.';OLfnRef=p+'frames'+sFrm+'.';var n=window.name,o;
+if((n&&parent!=self&&o3_frame==parent)&&(o=OLgetRef(n,parent.document))){if(OLie4&&!OLop7){
+OLx=event.clientX+OLfd().scrollLeft;OLy=event.clientY+OLfd().scrollTop;}
+OLifX=OLpageLoc(o,'Left')-(OLie4&&!OLop7?OLfd().scrollLeft:self.pageXOffset);
+OLifY=OLpageLoc(o,'Top')-(OLie4&&!OLop7?OLfd().scrollTop:self.pageYOffset);}}
+}
+
+function OLchkIfRef(){
+var n=(parent!=self&&o3_frame==parent)?window.name:'',o=n?OLgetRef(n):null;
+if(o){var oR=OLgetRef(o3_ref,document);if(oR){OLrefXY=OLgetRefXY(o3_ref,document);
+OLrefXY[0]+=(OLpageLoc(o,'Left')-(OLie4&&!OLop7?OLfd(self).scrollLeft:self.pageXOffset));
+OLrefXY[1]+=(OLpageLoc(o,'Top')-(OLie4&&!OLop7?OLfd(self).scrollTop:self.pageYOffset));}}
+}
+
+OLregCmdLineFunc(OLparseCrossframe);
+
+OLcrossframePI=1;
+OLloaded=1;
diff --git a/httemplate/elements/overlibmws_draggable.js b/httemplate/elements/overlibmws_draggable.js
new file mode 100644
index 0000000..1bf0ecf
--- /dev/null
+++ b/httemplate/elements/overlibmws_draggable.js
@@ -0,0 +1,85 @@
+/*
+ overlibmws_draggable.js plug-in module - Copyright Foteos Macrides 2002-2008. All rights reserved.
+ For support of the DRAGGABLE feature.
+ Initial: August 24, 2002 - Last Revised: January 26, 2008
+ See the Change History and Command Reference for overlibmws via:
+
+ http://www.macridesweb.com/oltest/
+
+ Published under an open source license: http://www.macridesweb.com/oltest/license.html
+*/
+
+OLloaded=0;
+var OLdraggableCmds='draggable,dragcap,dragid';
+OLregCmds(OLdraggableCmds);
+
+// DEFAULT CONFIGURATION
+if(OLud('draggable'))var ol_draggable=0;
+if(OLud('dragcap'))var ol_dragcap=0;
+if(OLud('dragid'))var ol_dragid='';
+// END CONFIGURATION
+
+var o3_draggable=0,o3_dragcap=0,o3_dragid='',o3_dragging=0,OLdrg=null,OLmMv,
+OLcX,OLcY,OLcbX,OLcbY;function OLloadDraggable(){OLload(OLdraggableCmds);}
+function OLparseDraggable(pf,i,ar){var t=OLtoggle,k=i;if(k<ar.length){
+if(Math.abs(ar[k])==DRAGGABLE){t(ar[k],pf+'draggable');return k;}
+if(Math.abs(ar[k])==DRAGCAP){t(ar[k],pf+'dragcap');return k;}
+if(ar[k]==DRAGID){OLparQuo(ar[++k],pf+'dragid');return k;}}return -1;
+}
+
+function OLcheckDrag(){
+if(o3_draggable){if(o3_sticky&&(o3_frame==self))OLinitDrag();else o3_draggable=0;}
+}
+function OLinitDrag(){
+OLmMv=OLdw.onmousemove;o3_dragging=0;
+if(OLns4){document.captureEvents(Event.MOUSEDOWN|Event.CLICK);
+document.onmousedown=OLgrabEl;document.onclick=function(e){return routeEvent(e);}}
+else{var dvido=(o3_dragid)?OLgetRef(o3_dragid):null,capid=(OLovertwoPI&&over==over2?
+'overCap2':'overCap');if(dvido)dvido.onscroll=function(){OLdw.onmousemove=OLmMv;
+OLinitDrag();};OLdrg=(o3_cap&&o3_dragcap)?OLgetRef(capid):over;
+if(!OLdrg||!OLdrg.style)OLdrg=over;OLdrg.onmousedown=OLgrabEl;OLsetDrgCur(1);}
+}
+function OLsetDrgCur(d){if(!OLns4&&OLdrg)OLdrg.style.cursor=(d?'move':'auto');}
+
+function OLgrabEl(e){
+var e=(e||event);
+var cKy=(OLns4?e.modifiers&Event.ALT_MASK:(e.altKey||(OLop7&&e.ctrlKey)));o3_dragging=1;
+if(cKy){OLsetDrgCur(0);document.onmouseup=function(){OLsetDrgCur(1);o3_dragging=0;}
+return(OLns4?routeEvent(e):true);}
+OLx=(e.pageX||e.clientX+OLfd().scrollLeft);OLy=(e.pageY||e.clientY+OLfd().scrollTop);
+if(OLie4)over.onselectstart=function(){return false;}
+if(OLns4){OLcX=OLx;OLcY=OLy;document.captureEvents(Event.MOUSEUP)}else{
+OLcX=OLx-(OLns4?over.left:parseInt(over.style.left));
+OLcY=OLy-(OLns4?over.top:parseInt(over.style.top));
+if((OLshadowPI)&&bkdrop&&o3_shadow){OLcbX=OLx-(parseInt(bkdrop.style.left));
+OLcbY=OLy-(parseInt(bkdrop.style.top));}}OLdw.onmousemove=OLmoveEl;
+document.onmouseup=function(){
+if(OLie4)over.onselectstart=null;o3_dragging=0;OLdw.onmousemove=OLmMv;}
+return(OLns4?routeEvent(e):false);
+}
+
+function OLmoveEl(e){
+var e=(e||event);
+OLx=(e.pageX||e.clientX+OLfd().scrollLeft);OLy=(e.pageY||e.clientY+OLfd().scrollTop);
+if(o3_dragging){if(OLns4){over.moveBy(OLx-OLcX,OLy-OLcY);
+if(OLshadowPI&&bkdrop&&o3_shadow)bkdrop.moveBy(OLx-OLcX,OLy-OLcY);}
+else{OLrepositionTo(over,OLx-OLcX,OLy-OLcY);
+if((OLiframePI)&&OLie55&&OLifsP1)OLrepositionTo(OLifsP1,OLx-OLcX,OLy-OLcY);
+if((OLshadowPI)&&bkdrop&&o3_shadow){OLrepositionTo(bkdrop,OLx-OLcbX,OLy-OLcbY);
+if((OLiframePI)&&OLie55&&OLifsSh)OLrepositionTo(OLifsSh,OLx-OLcbX,OLy-OLcbY);}}
+if(OLhidePI)OLhideUtil(0,1,1,0,0,0);}if(OLns4){OLcX=OLx;OLcY=OLy;}
+return false;
+}
+
+function OLclearDrag(){
+if(OLns4){document.releaseEvents(Event.MOUSEDOWN|Event.MOUSEUP|Event.CLICK);
+document.onmousedown=document.onclick=null;}else{
+if(OLdrg)OLdrg.onmousedown=null;over.onmousedown=null;OLsetDrgCur(0);}
+document.onmouseup=null;o3_dragging=0;
+}
+
+OLregRunTimeFunc(OLloadDraggable);
+OLregCmdLineFunc(OLparseDraggable);
+
+OLdraggablePI=1;
+OLloaded=1;
diff --git a/httemplate/elements/overlibmws_iframe.js b/httemplate/elements/overlibmws_iframe.js
new file mode 100644
index 0000000..4c937d3
--- /dev/null
+++ b/httemplate/elements/overlibmws_iframe.js
@@ -0,0 +1,93 @@
+/*
+ overlibmws_iframe.js plug-in module - Copyright Foteos Macrides 2003-2008. All rights reserved.
+ Masks system controls to prevent obscuring of popops for IE v5.5 or higher.
+ Initial: October 19, 2003 - Last Revised: January 26, 2008
+ See the Change History and Command Reference for overlibmws via:
+
+ http://www.macridesweb.com/oltest/
+
+ Published under an open source license: http://www.macridesweb.com/oltest/license.html
+*/
+
+OLloaded=0;
+
+var OLifsP1=null,OLifsSh=null,OLifsP2=null;
+
+// IFRAME SHIM SUPPORT FUNCTIONS
+function OLinitIfs(){
+if(!OLie55)return;
+if((OLovertwoPI)&&over2&&over==over2){
+var o=o3_frame.document.all['overIframeOvertwo'];
+if(!o||OLifsP2!=o){OLifsP2=null;OLgetIfsP2Ref();}return;}
+o=o3_frame.document.all['overIframe'];
+if(!o||OLifsP1!=o){OLifsP1=null;OLgetIfsRef();}
+if((OLshadowPI)&&o3_shadow){o=o3_frame.document.all['overIframeShadow'];
+if(!o||OLifsSh!=o){OLifsSh=null;OLgetIfsShRef();}}
+}
+
+function OLsetIfsRef(o,i,z){
+o.id=i;o.src='javascript:false;';o.scrolling='no';var os=o.style;os.position='absolute';
+os.top='0px';os.left='0px';os.width='1px';os.height='1px';os.visibility='hidden';
+os.zIndex=over.style.zIndex-z;os.filter='Alpha(style=0,opacity=0)';
+}
+
+function OLgetIfsRef(){
+if(OLifsP1||!OLie55)return;
+OLifsP1=o3_frame.document.createElement('iframe');
+OLsetIfsRef(OLifsP1,'overIframe',2);
+o3_frame.document.body.appendChild(OLifsP1);
+}
+
+function OLgetIfsShRef(){
+if(OLifsSh||!OLie55)return;
+OLifsSh=o3_frame.document.createElement('iframe');
+OLsetIfsRef(OLifsSh,'overIframeShadow',3);
+o3_frame.document.body.appendChild(OLifsSh);
+}
+
+function OLgetIfsP2Ref(){
+if(OLifsP2||!OLie55)return;
+OLifsP2=o3_frame.document.createElement('iframe');
+OLsetIfsRef(OLifsP2,'overIframeOvertwo',1);
+o3_frame.document.body.appendChild(OLifsP2);
+}
+
+function OLsetDispIfs(o,w,h){
+var os=o.style;
+os.width=w+'px';os.height=h+'px';os.clip='rect(0px '+w+'px '+h+'px 0px)';
+o.filters.alpha.enabled=true;
+}
+
+function OLdispIfs(){
+if(!OLie55)return;
+var wd=over.offsetWidth,ht=over.offsetHeight;
+if(OLfilterPI&&o3_filter&&o3_filtershadow){wd+=5;ht+=5;}
+if((OLovertwoPI)&&over2&&over==over2){
+if(!OLifsP2)return;
+OLsetDispIfs(OLifsP2,wd,ht);return;}
+if(!OLifsP1)return;
+OLsetDispIfs(OLifsP1,wd,ht);
+if((!OLshadowPI)||!o3_shadow||!OLifsSh)return;
+OLsetDispIfs(OLifsSh,wd,ht);
+}
+
+function OLshowIfs(){
+if(OLifsP1){OLifsP1.style.visibility="visible";
+if((OLshadowPI)&&o3_shadow&&OLifsSh)OLifsSh.style.visibility="visible";}
+}
+
+function OLhideIfs(o){
+if(!OLie55||o!=over)return;
+if(OLifsP1)OLifsP1.style.visibility="hidden";
+if((OLshadowPI)&&o3_shadow&&OLifsSh)OLifsSh.style.visibility="hidden";
+}
+
+function OLrepositionIfs(X,Y){
+if(OLie55){if((OLovertwoPI)&&over2&&over==over2){
+if(OLifsP2)OLrepositionTo(OLifsP2,X,Y);}
+else{if(OLifsP1){OLrepositionTo(OLifsP1,X,Y);if((OLshadowPI)&&o3_shadow&&OLifsSh)
+OLrepositionTo(OLifsSh,X+o3_shadowx,Y+o3_shadowy);}}}
+}
+
+OLiframePI=1;
+OLloaded=1;
diff --git a/httemplate/elements/pager.html b/httemplate/elements/pager.html
new file mode 100644
index 0000000..a53300f
--- /dev/null
+++ b/httemplate/elements/pager.html
@@ -0,0 +1,55 @@
+% my %opt = @_;
+% my $pager = '';
+%
+% if ( $opt{'total'} != $opt{'num_rows'} && $opt{'maxrecords'} ) {
+%
+% unless ( $opt{'offset'} == 0 ) {
+% $cgi->param('offset', $opt{'offset'} - $opt{'maxrecords'});
+
+ <A HREF="<% $cgi->self_url %>"><B><FONT SIZE="+1">Previous</FONT></B></A>
+
+% }
+%
+% my $page = 0;
+% my $prevpage = 0;
+% my $over = 0;
+% my $step = $opt{total} / 10; # 10 evenly spaced
+% for ( my $poff = 0; $poff < $opt{total}; $poff += $opt{maxrecords} ) {
+% $page++;
+%
+% next unless
+% $page <= 4 #first four
+% || $page >= ( $opt{total} / $opt{maxrecords} ) - 3 #last four
+% || abs( ($opt{offset}-$poff) / $opt{maxrecords} ) <= 3 #w/i 3 of current
+% || $poff > $over # evenly spaced
+% ;
+%
+% $over += $step if $poff > $over;
+%
+% if ( $opt{'offset'} == $poff ) {
+
+ <FONT SIZE="+2"><% $page %></FONT>
+
+% } else {
+% $cgi->param('offset', $poff);
+%
+% if ( $page > $prevpage+1 ) {
+ ...
+% }
+
+ <A HREF="<% $cgi->self_url %>"><% $page %></A>
+
+% }
+%
+% $prevpage = $page;
+%
+% }
+%
+% unless ( $opt{'offset'} + $opt{'maxrecords'} > $opt{'total'} ) {
+% $cgi->param('offset', $opt{'offset'} + $opt{'maxrecords'});
+
+ <A HREF="<% $cgi->self_url %>"><B><FONT SIZE="+1">Next</FONT></B></A>
+%
+% }
+%
+% }
diff --git a/httemplate/elements/phonenumber.html b/httemplate/elements/phonenumber.html
new file mode 100644
index 0000000..60414a6
--- /dev/null
+++ b/httemplate/elements/phonenumber.html
@@ -0,0 +1,40 @@
+<% include('/elements/init_overlib.html') %>
+
+% if ( length($number) ) {
+
+ <% $number %>
+
+% if ( $opt{'callable'} && $curuser->option('vonage-username') ) {
+
+ <% include('/elements/popup_link.html',
+ 'action' =>
+ 'https://secure.click2callu.com/tpcc/makecall'.
+ '?username='. uri_escape($curuser->option('vonage-username')).
+ '&password='. uri_escape($curuser->option('vonage-password')).
+ "&fromnumber=$vonage_number".
+ "&tonumber=$snumber",
+ 'width' => 240,
+ 'height' => 64,
+ 'actionlabel' => 'Initiating call',
+ 'label' => qq!<IMG SRC="${fsurl}images/red_telephone_mimooh_01.png" BORDER=0 ALT="Call this number">!,
+ )
+ %>
+
+% }
+%
+% } else {
+
+ &nbsp;
+
+% }
+<%init>
+
+my( $number, %opt ) = @_;
+( my $snumber = $number ) =~ s/\D//g;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+( my $vonage_number = $curuser->option('vonage-fromnumber') ) =~ s/\D//g;
+$vonage_number =~ /^1/ or $vonage_number = "1$vonage_number";
+
+</%init>
diff --git a/httemplate/elements/popup_link-cust_main.html b/httemplate/elements/popup_link-cust_main.html
new file mode 100644
index 0000000..6d92301
--- /dev/null
+++ b/httemplate/elements/popup_link-cust_main.html
@@ -0,0 +1,42 @@
+<%doc>
+
+Example:
+
+ include('/elements/init_overlib.html')
+
+ include( '/elements/cust_popup_link.html', { #hashref or a list, either way
+
+ #required
+ 'action' => 'content.html', # uri for content of popup which should
+ # be suitable for appending keywords
+ 'label' => 'click me', # text of <A> tag
+ 'cust_main' => $cust_main # a FS::cust_main object
+
+ #strongly recommended (you want a title, right?)
+ 'actionlabel => 'You clicked', # popup title
+
+ #opt
+ 'width' => '540',
+ 'color' => '#ff0000',
+ 'closetext' => 'Go Away', # the value '' removes the link
+ )
+
+</%doc>
+% if ( $params->{'cust_main'} ) {
+<% include('/elements/popup_link.html', $params ) %>\
+% }
+<%init>
+
+my $params = { 'closetext' => 'Close' };
+
+if (ref($_[0]) eq 'HASH') {
+ $params = { %$params, %{ $_[0] } };
+} else {
+ $params = { %$params, @_ };
+}
+
+$params->{'action'} .=
+ ( $params->{'action'} =~ /\?/ ? ';' : '?' ).
+ 'custnum='. $params->{'cust_main'}->custnum;
+
+</%init>
diff --git a/httemplate/elements/popup_link-cust_pkg.html b/httemplate/elements/popup_link-cust_pkg.html
new file mode 100644
index 0000000..cd8d5c0
--- /dev/null
+++ b/httemplate/elements/popup_link-cust_pkg.html
@@ -0,0 +1,47 @@
+<%doc>
+
+Example:
+
+ include('/elements/init_overlib.html')
+
+ include( '/elements/pkg_popup_link.html', { #hashref or a list, either way
+
+ #required
+ 'action' => 'content.html', # uri for content of popup which should
+ # be suitable for appending '&stuff...'
+ 'label' => 'click me', # text of <A> tag
+ 'cust_pkg' => $cust_pkg # a FS::cust_pkg object
+
+ #strongly recommended (you want a title, right?)
+ 'actionlabel => 'You clicked', # popup title
+
+ #opt
+ 'width' => '540',
+ 'color' => '#ff0000',
+ 'closetext' => 'Go Away', # the value '' removes the link
+ )
+
+</%doc>
+% if ( $params->{'cust_pkg'} ) {
+<% include('/elements/popup_link.html', $params ) %>\
+% }
+<%init>
+
+my $params = { 'closetext' => 'Close',
+ 'width' => 768,
+ };
+
+if (ref($_[0]) eq 'HASH') {
+ $params = { %$params, %{ $_[0] } };
+} else {
+ $params = { %$params, @_ };
+}
+
+$params->{'action'} .=
+ ( $params->{'action'} =~ /\?/ ? ';' : '?' ).
+ 'pkgnum='. $params->{'cust_pkg'}->pkgnum;
+
+$params->{'actionlabel'} .=
+ ' package '. $params->{'cust_pkg'}->pkgnum; #XXX pkgnum? really?
+
+</%init>
diff --git a/httemplate/elements/popup_link-cust_svc.html b/httemplate/elements/popup_link-cust_svc.html
new file mode 100644
index 0000000..8255ffc
--- /dev/null
+++ b/httemplate/elements/popup_link-cust_svc.html
@@ -0,0 +1,47 @@
+<%doc>
+
+Example:
+
+ include('/elements/init_overlib.html')
+
+ include( '/elements/svc_popup_link.html', { #hashref or a list, either way
+
+ #required
+ 'action' => 'content.html', # uri for content of popup which should
+ # be suitable for appending '?svcnum='
+ 'label' => 'click me', # text of <A> tag
+ 'cust_svc' => $cust_svc # a FS::cust_svc object
+
+ #strongly recommended (you want a title, right?)
+ 'actionlabel => 'You clicked', # popup title
+
+ #opt
+ 'width' => '540',
+ 'color' => '#ff0000',
+ 'closetext' => 'Go Away', # the value '' removes the link
+ )
+
+</%doc>
+% if ( $params->{'cust_svc'} ) {
+<% include( '/elements/popup_link.html', $params ) %>\
+% }
+<%init>
+
+my $params = { 'closetext' => 'Close',
+ 'width' => 392,
+ };
+
+if (ref($_[0]) eq 'HASH') {
+ $params = { %$params, %{ $_[0] } };
+} else {
+ $params = { %$params, @_ };
+}
+
+$params->{'action'} .=
+ ( $params->{'action'} =~ /\?/ ? ';' : '?' ).
+ 'svcnum='. $params->{'cust_svc'}->svcnum;
+
+$params->{'actionlabel'} .=
+ ' service '. $params->{'cust_svc'}->svcnum; #XXX svcnum? really?
+
+</%init>
diff --git a/httemplate/elements/popup_link.html b/httemplate/elements/popup_link.html
new file mode 100644
index 0000000..2019387
--- /dev/null
+++ b/httemplate/elements/popup_link.html
@@ -0,0 +1,49 @@
+<%doc>
+
+Example:
+
+ include('/elements/init_overlib.html')
+
+ include( '/elements/popup_link.html', { #hashref or a list, either way is fine
+
+ #required
+ 'action' => 'content.html', # uri for content of popup
+ 'label' => 'click me', # text of <A> tag
+
+ #strongly recommended
+ 'actionlabel => 'You clicked', # popup title
+
+ #opt
+ 'width' => 540,
+ 'height' => 336,
+ 'color' => '#ff0000',
+ 'closetext' => 'Go Away', # the value '' removes the link
+
+ #uncommon opt
+ 'aname' => "target", # link NAME= value, useful for #targets
+ 'target' => '_parent',
+ } )
+
+</%doc>
+% if ($params->{'action'} && $label) {
+<A HREF="javascript:void(0);"
+ onClick="<% $onclick %>"
+ <% $params->{'aname'} ? 'NAME="'. $params->{'aname'}. '"' : '' %>
+ <% $params->{'target'} ? 'TARGET="'. $params->{'target'}. '"' : '' %>
+><% $label %></A>\
+% }
+<%init>
+
+my $params;
+if (ref($_[0]) eq 'HASH') {
+ #$params = { %$params, %{ $_[0] } };
+ $params = shift;
+} else {
+ #$params = { %$params, @_ };
+ $params = { @_ };
+}
+
+my $label = $params->{'label'};
+my $onclick = include('/elements/popup_link_onclick.html', $params);
+
+</%init>
diff --git a/httemplate/elements/popup_link_onclick.html b/httemplate/elements/popup_link_onclick.html
new file mode 100644
index 0000000..f539f4b
--- /dev/null
+++ b/httemplate/elements/popup_link_onclick.html
@@ -0,0 +1,74 @@
+<%doc>
+
+Example:
+
+ include('/elements/init_overlib.html')
+
+ include( '/elements/popup_link_onclick.html', { #hashref or a list, either way
+
+ #required
+ 'action' => 'content.html', # uri for content of popup
+
+ #strongly recommended
+ 'actionlabel => 'You clicked', # popup title
+
+ #opt
+ 'width' => 540,
+ 'height' => 336,
+ 'color' => '#ff0000',
+ 'closetext' => 'Go Away', # the value '' removes the link
+
+ #uncommon opt
+ 'frame' => 0, #bool
+ 'scrolling' => 'yes', #scrollbars:
+ # 'auto' (default if omitted), 'yes' or 'no'.
+ 'nofalse' => 0, #bool, eliminates "return false;"
+
+ } )
+
+</%doc>
+% if ($action) {
+<% $onclick %>\
+% }
+<%init>
+
+my( $action, $actionlabel, $frame ) = ( '', '', '' );
+my( $width, $height ) = ( 540, 336 );
+my $closetext = 'Close';
+my $color = '#333399';
+my $scrolling = 'auto';
+
+my $params;
+if (ref($_[0]) eq 'HASH') {
+ #$params = { %$params, %{ $_[0] } };
+ $params = shift;
+} else {
+ #$params = { %$params, @_ };
+ $params = { @_ };
+}
+
+$action = $params->{'action'} if exists $params->{'action'};
+$actionlabel = $params->{'actionlabel'} if exists $params->{'actionlabel'};
+$width = $params->{'width'} if exists $params->{'width'};
+$height = $params->{'height'} if exists $params->{'height'};
+$color = $params->{'color'} if exists $params->{'color'};
+$closetext = $params->{'closetext'} if exists $params->{'closetext'};
+$frame = $params->{'frame'} if exists $params->{'frame'};
+$scrolling = $params->{'scrolling'} if exists $params->{'scrolling'};
+
+#stupid safari is caching the "location" of popup iframs, and submitting them
+#instead of displaying them. this should prevent that.
+my $popup_name = 'popup-'.time. "-$$-". rand() * 2**32;
+
+my $onclick =
+ "overlib( OLiframeContent('$action', $width, $height, '$popup_name', 0, '$scrolling' ), ".
+ "CAPTION, '$actionlabel', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, ".
+ "DRAGGABLE, CLOSECLICK, ".
+ "BGCOLOR, '$color', CGCOLOR, '$color', CLOSETEXT, '$closetext'".
+ ( $frame ? ", FRAME, $frame" : '' ).
+ ");";
+
+$onclick .= " return false;"
+ unless $params->{'nofalse'};
+
+</%init>
diff --git a/httemplate/elements/progress-init.html b/httemplate/elements/progress-init.html
new file mode 100644
index 0000000..194fc74
--- /dev/null
+++ b/httemplate/elements/progress-init.html
@@ -0,0 +1,87 @@
+<% include('/elements/xmlhttp.html',
+ 'method' => 'POST',
+ 'url' => $action,
+ 'subs' => [ 'start_job' ],
+ 'key' => $key,
+ )
+%>
+
+<% include('/elements/init_overlib.html') %>
+
+<SCRIPT TYPE="text/javascript">
+
+function <%$key%>process () {
+
+ //alert('<%$key%>process for form <%$formname%>');
+
+ if ( document.<%$formname%>.submit.disabled == false ) {
+ document.<%$formname%>.submit.disabled=true;
+ }
+
+ overlib( 'Submitting job to server...', WIDTH, 444, HEIGHT, 168, CAPTION, 'Please wait...', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', CLOSECLICK, MIDX, 0, MIDY, 0 );
+
+ var Hash = new Array();
+ var x = 0;
+ var fieldName;
+ for (var i = 0; i<document.<%$formname%>.elements.length; i++) {
+ field = document.<%$formname%>.elements[i];
+ if ( <% join(' || ', map { "(field.name.indexOf('$_') > -1)" } @$fields ) %>
+ )
+ {
+ if ( field.type == 'select-multiple' ) {
+ //alert('select-multiple ' + field.name);
+ for (var j=0; j < field.options.length; j++) {
+ if ( field.options[j].selected ) {
+ //alert(field.name + ' => ' + field.options[j].value);
+ Hash[x++] = field.name;
+ Hash[x++] = field.options[j].value;
+ }
+ }
+ } else if ( ( field.type != 'radio' && field.type != 'checkbox' )
+ || ( ( field.type == 'radio' || field.type == 'checkbox' )
+ && document.<%$formname%>.elements[i].checked
+ )
+ )
+ {
+ Hash[x++] = field.name;
+ Hash[x++] = field.value;
+ }
+ }
+ }
+
+ // jsrsPOST = true;
+ // jsrsExecute( '<% $action %>', <%$key%>myCallback, 'start_job', Hash );
+
+ //alert('start_job( ' + Hash + ', <%$key%>myCallback )' );
+ //alert('start_job()' );
+ <%$key%>start_job( Hash, <%$key%>myCallback );
+
+}
+
+function <%$key%>myCallback( jobnum ) {
+
+ overlib( OLiframeContent('<%$p%>elements/progress-popup.html?jobnum=' + jobnum + ';<%$url_or_message_link%>;formname=<%$formname%>' , 444, 168, '<% $popup_name %>'), CAPTION, 'Please wait...', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', CLOSECLICK, MIDX, 0, MIDY, 0 );
+
+}
+
+</SCRIPT>
+
+<%init>
+
+my( $formname, $fields, $action, $url_or_message, $key ) = @_;
+$key = '' unless defined $key;
+
+my $url_or_message_link;
+if ( ref($url_or_message) ) { #its a message or something
+ $url_or_message_link = 'message='. uri_escape( $url_or_message->{'message'} );
+ $url_or_message_link .= ';url='. uri_escape( $url_or_message->{'url'} )
+ if $url_or_message->{'url'};
+} else {
+ $url_or_message_link = "url=$url_or_message";
+}
+
+#stupid safari is caching the "location" of popup iframs, and submitting them
+#instead of displaying them. this should prevent that.
+my $popup_name = 'popup-'.time. "-$$-". rand() * 2**32;
+
+</%init>
diff --git a/httemplate/elements/progress-popup.html b/httemplate/elements/progress-popup.html
new file mode 100644
index 0000000..0bd71ff
--- /dev/null
+++ b/httemplate/elements/progress-popup.html
@@ -0,0 +1,114 @@
+%
+% my $jobnum = $cgi->param('jobnum');
+% my $url = $cgi->param('url');
+% my $message = $cgi->param('message');
+% my $formname = scalar($cgi->param('formname'));
+%
+
+<HTML>
+ <HEAD>
+ <TITLE></TITLE>
+ </HEAD>
+ <BODY BGCOLOR="#ccccff" onLoad="refreshStatus()">
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p.'elements/jsrsServer.html',
+ 'subs' => [ 'job_status' ],
+ )
+%>
+<SCRIPT TYPE="text/javascript" src="<%$fsurl%>elements/qlib/control.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" src="<%$fsurl%>elements/qlib/imagelist.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" src="<%$fsurl%>elements/qlib/progress.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript">
+function refreshStatus () {
+ //jsrsExecute( '<%$p%>elements/jsrsServer.html', updateStatus, 'job_status', '<% $jobnum %>' );
+
+ job_status( '<% $jobnum %>', updateStatus );
+}
+function updateStatus( status_statustext ) {
+
+ //var Array = status_statustext.split("\n");
+ var statusArray = eval('(' + status_statustext + ')');
+ var status = statusArray[0];
+ var statustext = statusArray[1];
+
+ //if ( status == 'progress' ) {
+ //IE workaround, no i have no idea why
+ if ( status.indexOf('progress') > -1 ) {
+ document.getElementById("progress_percent").innerHTML = statustext + '%';
+ bar1.set(statustext);
+ bar1.update;
+ //jsrsExecute( '<%$p%>elements/jsrsServer.html', updateStatus, 'job_status', '<% $jobnum %>' );
+ job_status( '<% $jobnum %>', updateStatus );
+ } else if ( status.indexOf('complete') > -1 ) {
+% if ( $message ) {
+%
+% my $onClick = $url
+% ? "window.top.location.href = \\'$url\\';"
+% : 'parent.nd(1);';
+
+ document.getElementById("progress_message").innerHTML = "<% $message %>";
+ document.getElementById("progress_bar").innerHTML = '';
+ document.getElementById("progress_percent").innerHTML =
+ '<INPUT TYPE="button" VALUE="OK" onClick="<% $onClick %>">';
+ document.getElementById("progress_jobnum").innerHTML = '';
+
+% unless ( $url ) {
+ if ( parent.document.<%$formname%>.submit.disabled == true ) {
+ parent.document.<%$formname%>.submit.disabled=false;
+ }
+% }
+
+% } elsif ( $url ) {
+
+ window.top.location.href = '<% $url %>';
+% } else {
+
+ alert('job done but no url or message specified');
+% }
+
+ } else if ( status.indexOf('error') > -1 ) {
+ document.getElementById("progress_message").innerHTML = '<FONT SIZE="+1" COLOR="#FF0000">Error: ' + statustext + '</FONT>';
+ document.getElementById("progress_bar").innerHTML = '';
+ document.getElementById("progress_percent").innerHTML = '<INPUT TYPE="button" VALUE="OK" onClick="parent.nd(1);">';
+ document.getElementById("progress_jobnum").innerHTML = '';
+ if ( parent.document.<%$formname%>.submit.disabled == true ) {
+ parent.document.<%$formname%>.submit.disabled=false;
+ }
+ } else {
+ alert('XXX unknown status returned from server: ' + status);
+ }
+
+}
+</SCRIPT>
+
+ <TABLE WIDTH="100%">
+ <TR>
+ <TD ALIGN="center" ID="progress_message">
+ Server processing job...
+ </TD>
+ </TR><TR>
+ <TD ALIGN="center" ID="progress_bar">
+ <SCRIPT TYPE="text/javascript">
+ // Create imagelist
+ SEGS = new QImageList(4, 23, "<%$fsurl%>images/progressbar-empty.png", "<%$fsurl%>images/progressbar-full.png");
+ // Create bars
+ bar1 = new QProgress(null, "bar1", SEGS, 100);
+ // bar1.set(0);
+ // bar1.update;
+ </SCRIPT>
+ </TD>
+ </TR><TR>
+ <TD ALIGN="center">
+ <DIV ID="progress_percent">%</DIV>
+ </TD>
+ </TR><TR>
+ <TD ALIGN="center" ID="progress_jobnum">
+ (progress of job #<% $jobnum %>)
+ </TD>
+ </TR>
+ </TABLE>
+
+ </BODY>
+</HTML>
+
diff --git a/httemplate/elements/qlib/box.js b/httemplate/elements/qlib/box.js
new file mode 100644
index 0000000..537aac4
--- /dev/null
+++ b/httemplate/elements/qlib/box.js
@@ -0,0 +1,29 @@
+/**
+ * QLIB 1.0 Box Control
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QBox(parent, name, res, x, y, width, height, body, visible, effects, opacity, zindex) {
+ this.init(parent, name);
+ if (this.res = res) {
+ this.x = x - 0;
+ this.y = y - 0;
+ this.width = width - 0;
+ this.height = (typeof(height) == "number") ? height : null;
+ this.body = body || "&nbsp;";
+ var j = QBox.arguments.length;
+ this.visible = (j > 8) ? visible : true;
+ this.effects = (j > 9) ? effects : (res.effects || 0);
+ this.opacity = (j > 10) ? opacity : (res.opacity != null ? res.opacity : 100);
+ this.zindex = (j > 11) ? zindex : null;
+ this.create();
+ } else {
+ this.document.write("invalid resource");
+ }
+}
+QBox.prototype = new QBoxCtrl();
diff --git a/httemplate/elements/qlib/boxctrl.js b/httemplate/elements/qlib/boxctrl.js
new file mode 100644
index 0000000..417b204
--- /dev/null
+++ b/httemplate/elements/qlib/boxctrl.js
@@ -0,0 +1,48 @@
+/**
+ * QLIB 1.0 Box Abstraction
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QBoxCtrl_content() {
+ with (this) {
+ if (res) {
+ this.cwidth = width - res.L - res.R - 8;
+ this.cheight = height && (height - res.T - res.B - 8);
+ var ec = '"><table border="0" cellspacing="0" cellpadding="0"><tr><td></td></tr></table></td>';
+ document.write('<table class="qbox" border="0" cellspacing="0" cellpadding="0" width="' +
+ (width - 8) + (height != null ? '" height="' + (height - 8) : '') + '"><tr><td width="' +
+ res.L + '" height="' + res.T + '"><img src="' + res.TL.src + '" border="0" width="' +
+ res.L + '" height="' + res.T + '"></td><td width="' + cwidth + '" height="' + res.T +
+ '" background="' + res.TC.src + ec + '<td width="' + res.R + '" height="' + res.T +
+ '"><img src="' + res.TR.src + '" border="0" width="' + res.R + '" height="' + res.T +
+ '"></td></tr><tr><td width="' + res.L + (cheight != null ? '" height="' + cheight : '') +
+ '" background="' + res.ML.src + ec + '<td width="' + cwidth + '" bgcolor="' + res.bgcolor +
+ (cheight != null ? '" height="' + cheight : '') + (res.bgtile ? '" background="' +
+ res.bgtile.src : '') + '" align="left" valign="top" class="body" unselectable="on">');
+ if (typeof(body) == "function") {
+ this.body();
+ } else {
+ document.write(body);
+ }
+ document.write('</td><td width="' + res.R + (cheight != null ? '" height="' + cheight : '') +
+ '" background="' + res.MR.src + ec + '</tr><tr><td width="' + res.L + '" height="' + res.B +
+ '"><img src="' + res.BL.src + '" border="0" width="' + res.L + '" height="' + res.B +
+ '"></td><td width="' + cwidth + '" height="' + res.B + '" background="' + res.BC.src + ec +
+ '<td width="' + res.R + '" height="' + res.B + '"><img src="' + res.BR.src +
+ '" border="0" width="' + res.R + '" height="' + res.B + '"></td></tr></table><br>');
+ }
+ }
+}
+
+function QBoxCtrl() {
+ this.res = false;
+ this.body = "&nbsp;";
+ this.cwidth = this.cheight = 0;
+ this.content = QBoxCtrl_content;
+}
+QBoxCtrl.prototype = new QWndCtrl();
diff --git a/httemplate/elements/qlib/boxres.js b/httemplate/elements/qlib/boxres.js
new file mode 100644
index 0000000..0878172
--- /dev/null
+++ b/httemplate/elements/qlib/boxres.js
@@ -0,0 +1,42 @@
+/**
+ * QLIB 1.0 Box Resource
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QBoxRes(t, r, b, l, tc, tr, mr, br, bc, bl, ml, tl, bgcolor, bgtile, effects, opacity) {
+ var args = QBoxRes.arguments.length;
+ this.T = t;
+ this.R = r;
+ this.B = b;
+ this.L = l;
+ this.TC = new Image();
+ this.TC.src = tc;
+ this.TR = new Image(r, t);
+ this.TR.src = tr;
+ this.MR = new Image();
+ this.MR.src = mr;
+ this.BR = new Image(r, b);
+ this.BR.src = br;
+ this.BC = new Image();
+ this.BC.src = bc;
+ this.BL = new Image(l, b);
+ this.BL.src = bl;
+ this.ML = new Image();
+ this.ML.src = ml;
+ this.TL = new Image(l, t);
+ this.TL.src = tl;
+ this.bgcolor = bgcolor || "#FFFFFF";
+ if (bgtile) {
+ this.bgtile = new Image();
+ this.bgtile.src = bgtile;
+ } else {
+ this.bgtile = false;
+ }
+ this.effects = (args > 13) ? effects : null;
+ this.opacity = (args > 14) ? opacity : null;
+}
diff --git a/httemplate/elements/qlib/button.js b/httemplate/elements/qlib/button.js
new file mode 100644
index 0000000..05247d5
--- /dev/null
+++ b/httemplate/elements/qlib/button.js
@@ -0,0 +1,74 @@
+/**
+ * QLIB 1.0 Button Control
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QButton_update() {
+ with (this) {
+ image.src = ((!enabled && res.imgD) || (value ? res.imgP : res.imgN)).src;
+ }
+}
+
+function QButton_doEvent() {
+ with (this) {
+ if (enabled) {
+ if (res.style == 1) {
+ this.value = value ? 0 : 1;
+ update();
+ }
+ onClick(value, tag);
+ }
+ }
+ return false;
+}
+
+function QButton_enable(state) {
+ this.enabled = state;
+ this.update();
+}
+
+function QButton_set(value) {
+ if (this.enabled) {
+ this.value = value ? 1 : 0;
+ this.update();
+ }
+ return true;
+}
+
+function QButton(parent, name, res, tooltip) {
+ this.init(parent, name);
+ if (res) {
+ this.res = res;
+ this.tip = tooltip || "";
+ this.enabled = true;
+ this.value = 0;
+ this.set = QButton_set;
+ this.enable = QButton_enable;
+ this.update = QButton_update;
+ this.doEvent = QButton_doEvent;
+ this.onClick = QControl.event;
+ with (this) {
+ document.write('<a href="#" hidefocus="true" unselectable="on"' +
+ (tip ? ' title="' + tip + '"' : '') + ' onClick="return ' + name +
+ '.doEvent()" onMouseOver="' + (res.style == 2 ? name + '.set(1);' : '') +
+ 'window.top.status=' + name + '.tip;return true" onMouseOut="' +
+ (!res.style || (res.style == 2) ? name + '.set();' : '') + 'window.top.status=\'\'"' +
+ (!res.style ? ' onMouseDown="return ' + name + '.set(1)" onMouseUp="return ' + name + '.set()"' : '') +
+ '><img class="qbutton" name="' + id + '" src="' + res.imgN.src + '" border="0" width="' +
+ res.width + '" height="' + res.height + '"></a>');
+ this.image = document.images[id] || new Image(1, 1);
+ }
+ } else {
+ this.document.write("invalid resource");
+ }
+}
+QButton.prototype = new QControl();
+QButton.NORMAL = 0;
+QButton.CHECKBOX = 1;
+QButton.WEB = 2;
+QButton.SIGNAL = 3;
diff --git a/httemplate/elements/qlib/buttonres.js b/httemplate/elements/qlib/buttonres.js
new file mode 100644
index 0000000..97f6dfc
--- /dev/null
+++ b/httemplate/elements/qlib/buttonres.js
@@ -0,0 +1,23 @@
+/**
+ * QLIB 1.0 Button Resource
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QButtonRes(style, width, height, normal, pressed, disabled) {
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.imgN = new Image(width, height);
+ this.imgN.src = normal;
+ this.imgP = new Image(width, height);
+ this.imgP.src = pressed;
+ if (disabled) {
+ this.imgD = new Image(width, height);
+ this.imgD.src = disabled;
+ }
+}
diff --git a/httemplate/elements/qlib/control.js b/httemplate/elements/qlib/control.js
new file mode 100644
index 0000000..f50206e
--- /dev/null
+++ b/httemplate/elements/qlib/control.js
@@ -0,0 +1,51 @@
+/**
+ * QLIB 1.0 Base Abstract Control
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QControl_init(parent, name) {
+ this.parent = parent || self;
+ this.window = (parent && parent.window) || self;
+ this.document = (parent && parent.document) || self.document;
+ this.name = (parent && parent.name) ? (parent.name + "." + name) : ("self." + name);
+ this.id = "Q";
+ var h = this.hash(this.name);
+ for (var j=0; j<8; j++) {
+ this.id += QControl.HEXTABLE.charAt(h & 15);
+ h >>>= 4;
+ }
+}
+
+function QControl_hash(str) {
+ var h = 0;
+ if (str) {
+ for (var j=str.length-1; j>=0; j--) {
+ h ^= QControl.ANTABLE.indexOf(str.charAt(j)) + 1;
+ for (var i=0; i<3; i++) {
+ var m = (h = h<<7 | h>>>25) & 150994944;
+ h ^= m ? (m == 150994944 ? 1 : 0) : 1;
+ }
+ }
+ }
+ return h;
+}
+
+function QControl_nop() {
+}
+
+function QControl() {
+ this.init = QControl_init;
+ this.hash = QControl_hash;
+ this.window = self;
+ this.document = self.document;
+ this.tag = null;
+}
+QControl.ANTABLE = "w5Q2KkFts3deLIPg8Nynu_JAUBZ9YxmH1XW47oDpa6lcjMRfi0CrhbGSOTvqzEV";
+QControl.HEXTABLE = "0123456789ABCDEF";
+QControl.nop = QControl_nop;
+QControl.event = QControl_nop;
diff --git a/httemplate/elements/qlib/counter.js b/httemplate/elements/qlib/counter.js
new file mode 100644
index 0000000..72aeddb
--- /dev/null
+++ b/httemplate/elements/qlib/counter.js
@@ -0,0 +1,81 @@
+/**
+ * QLIB 1.0 Animated Digital Counter
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QCounter_update() {
+ with (this) {
+ var v = Math.max(value, 0);
+ var mod;
+ for (var j=0; j<size; j++) {
+ mod = Math.floor(v % 10);
+ images[j].src = (v >= 1) || (!j) ? res.list[mod].src : res.list[10].src;
+ v /= 10;
+ }
+ }
+}
+
+function QCounter_count(value, step) {
+ this._cntt = false;
+ this.value += step;
+ if ((step * (this.value - value)) >= 0) {
+ this.value = value - 0; // convert to number
+ } else {
+ this._cntt = setTimeout(this.name + ".count(" + value + "," + step + ")", 50);
+ }
+ this.update();
+}
+
+function QCounter_set(value) {
+ this.setval = value;
+ if (value != this.value) {
+ if (this._cntt) {
+ clearTimeout(this._cntt);
+ this._cntt = false;
+ }
+ var dv = value - this.value;
+ if (this.effect == 2) {
+ dv = dv / Math.min(10, Math.abs(dv));
+ } else if (this.effect == 3) {
+ dv = dv / Math.abs(dv);
+ }
+ this.count(value, dv);
+ }
+}
+
+function QCounter(parent, name, res, size, effect) {
+ this.init(parent, name);
+ if (res) {
+ this.res = res;
+ this.setval = this.value = 0;
+ this.size = size || 4;
+ this.effect = effect || 2;
+ this._cntt = false;
+ this.images = new Array(this.size);
+ this.set = QCounter_set;
+ this.update = QCounter_update;
+ this.count = QCounter_count;
+ with (this) {
+ document.write('<table class="qcounter" width="' + (res.width * size) + '" height="' + res.height +
+ '" border="0" cellspacing="0" cellpadding="0" unselectable="on"><tr>');
+ for (var j=(size - 1); j>=0; j--) {
+ document.write('<td width="' + res.width + '" height="' + res.height +
+ '" unselectable="on"><img name="' + id + j + '" src="' + (j ? res.list[10].src : res.list[0].src) +
+ '" border="0" width="' + res.width + '" height="' + res.height + '"></td>');
+ images[j] = document.images[id + j] || new Image(1, 1);
+ }
+ document.write('</tr></table>');
+ }
+ } else {
+ this.document.write("invalid resource");
+ }
+}
+QCounter.prototype = new QControl();
+QCounter.INSTANT = 1;
+QCounter.FAST = 2;
+QCounter.SLOW = 3;
diff --git a/httemplate/elements/qlib/imagelist.js b/httemplate/elements/qlib/imagelist.js
new file mode 100644
index 0000000..9f12de0
--- /dev/null
+++ b/httemplate/elements/qlib/imagelist.js
@@ -0,0 +1,25 @@
+/**
+ * QLIB 1.0 ImageList Resource
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QImageList(width, height) {
+ var len = QImageList.arguments.length - 2;
+ if (len > 0) {
+ this.list = new Array(len);
+ this.length = len;
+ this.width = width;
+ this.height = height;
+ var im;
+ for (var j=0; j<len; j++) {
+ im = new Image(width, height);
+ im.src = QImageList.arguments[j + 2];
+ this.list[j] = im;
+ }
+ }
+} \ No newline at end of file
diff --git a/httemplate/elements/qlib/label.js b/httemplate/elements/qlib/label.js
new file mode 100644
index 0000000..2d8b1e7
--- /dev/null
+++ b/httemplate/elements/qlib/label.js
@@ -0,0 +1,72 @@
+/**
+ * QLIB 1.0 Text Label
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QLabel_set_ie(value) {
+ this.label.innerText = (this.value = value) || "\xA0";
+}
+
+function QLabel_set_dom2(value) {
+ with (this.label) {
+ replaceChild(this.document.createTextNode((this.value = value) || "\xA0"), firstChild);
+ }
+}
+
+function QLabel_set_ns4(value) {
+ this.value = value || "";
+ with (this) {
+ document.open();
+ document.write('<div class="qlabel">' + (clickable ? '<a href="#" title="' + tooltip + '" onClick="return ' +
+ name + '.doEvent()" onMouseOut="window.top.status=\'\'" onMouseOver="window.top.status=' + name +
+ '.tooltip;return true">' + value + '</a>' : value) + '</div>');
+ document.close();
+ }
+}
+
+function QLabel_doEvent() {
+ this.onClick(this.value, this.tag);
+ return false;
+}
+
+function QLabel(parent, name, value, clickable, tooltip) {
+ this.init(parent, name);
+ this.value = value || "";
+ this.clickable = clickable || false;
+ this.tooltip = tooltip || "";
+ this.doEvent = QLabel_doEvent;
+ this.onClick = QControl.event;
+ with (this) {
+ if (document.getElementById || document.all) {
+ document.write(clickable ? '<div class="qlabel" unselectable="on"><a id="' + id + '" href="#" title="' +
+ tooltip + '" onClick="return ' + name + '.doEvent()" onMouseOver="window.top.status=' + name +
+ '.tooltip;return true" onMouseOut="window.top.status=\'\'" hidefocus="true" unselectable="on">' +
+ (value || '&nbsp;') + '</a></div>' : '<div id="' + id + '" class="qlabel" unselectable="on">' +
+ (value || '&nbsp;') + '</div>');
+ this.label = document.getElementById ? document.getElementById(id) :
+ (document.all.item ? document.all.item(id) : document.all[id]);
+ this.set = (label && (label.innerText ? QLabel_set_ie :
+ (label.replaceChild && QLabel_set_dom2))) || QControl.nop;
+ } else if (document.layers) {
+ var suffix = "";
+ for (var j=value.length; j<QLabel.TEXTQUOTA; j++) suffix += " &nbsp;";
+ document.write('<div><ilayer id="i' + id + '"><layer id="' + id + '"><div class="qlabel">' +
+ (clickable ? '<a href="#" title="' + tooltip + '" onClick="return ' + name +
+ '.doEvent()" onMouseOver="window.top.status=' + name +
+ '.tooltip;return true" onMouseOut="window.top.status=\'\'">' + value + suffix + '</a>' :
+ value + suffix) + '</div></layer></ilayer></div>');
+ this.label = (this.label = document.layers["i" + id]) && label.document.layers[id];
+ this.document = label && label.document;
+ this.set = (label && document) ? QLabel_set_ns4 : QControl.nop;
+ } else {
+ document.write("Object is not supported");
+ }
+ }
+}
+QLabel.prototype = new QControl();
+QLabel.TEXTQUOTA = 50;
diff --git a/httemplate/elements/qlib/messagebox.js b/httemplate/elements/qlib/messagebox.js
new file mode 100644
index 0000000..2e45839
--- /dev/null
+++ b/httemplate/elements/qlib/messagebox.js
@@ -0,0 +1,57 @@
+/**
+ * QLIB 1.0 Message Box Control
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QMessageBox_alert(msg) {
+ if (typeof(msg) == "string") {
+ this.label.set(this.value = msg);
+ }
+ this.center();
+ this.focus();
+ this.show(true);
+}
+
+function QMessageBox_close() {
+ with (this.parent) {
+ if (!onClose(tag)) show(false);
+ }
+}
+
+function QMessageBox_body() {
+ with (this) {
+ document.write('<table border="0" width="' + cwidth + '"><tr><td align="left" valign="top" unselectable="on">');
+ this.label = new QLabel(this, "label", value);
+ document.write('</td></tr><tr><td height="' + (bres.height + 14) + '" align="center" valign="bottom" unselectable="on">');
+ this.button = new QButton(this, "button", bres, "Close");
+ document.write('</td></tr></table>');
+ button.onClick = QMessageBox_close;
+ }
+}
+
+function QMessageBox(parent, name, box, btn, msg, effects, opacity) {
+ this.init(parent, name);
+ if ((this.res = box) && (this.bres = btn)) {
+ this.value = typeof(msg) == "string" ? msg : "";
+ this.width = Math.max(200, Math.floor(Math.sqrt(555 * this.value.length)));
+ this.height = null;
+ this.x = this.y = 0;
+ this.visible = false;
+ this.zindex = null;
+ this.body = QMessageBox_body;
+ var j = QMessageBox.arguments.length;
+ this.effects = j > 5 ? effects : (box.effects != null ? box.effects : 0);
+ this.opacity = j > 6 ? opacity : (box.opacity != null ? box.opacity : 100);
+ this.create();
+ this.alert = QMessageBox_alert;
+ this.onClose = QControl.event;
+ } else {
+ this.document.write("invalid resource");
+ }
+}
+QMessageBox.prototype = new QBoxCtrl();
diff --git a/httemplate/elements/qlib/progress.js b/httemplate/elements/qlib/progress.js
new file mode 100644
index 0000000..2de077e
--- /dev/null
+++ b/httemplate/elements/qlib/progress.js
@@ -0,0 +1,73 @@
+/**
+ * QLIB 1.0 Progress Control
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QProgress_update() {
+ with (this) {
+ var i = low;
+ for (var j=0; j<size; j++) {
+ images[j].src = i < value ? imgsrc1 : imgsrc0;
+ i += delta;
+ }
+ }
+}
+
+function QProgress_set(value) {
+ this.value = value - 0;
+ this.update();
+}
+
+function QProgress_setBounds(low, high) {
+ this.low = Math.min(low, high);
+ this.high = Math.max(low, high);
+ this.delta = (this.high - this.low) / this.size;
+ this.update();
+}
+
+function QProgress(parent, name, res, size, style) {
+ this.init(parent, name);
+ if (res) {
+ this.res = res;
+ this.value = 0;
+ this.low = 0;
+ this.high = 100;
+ this.size = size || 10;
+ this.delta = 100 / this.size;
+ this.style = style || 0;
+ this.images = new Array(this.size);
+ this.imgsrc0 = res.list[0] && res.list[0].src;
+ this.imgsrc1 = res.list[1] && res.list[1].src;
+ this.set = QProgress_set;
+ this.update = QProgress_update;
+ this.setBounds = QProgress_setBounds;
+ with (this) {
+ var hor = this.style < 2;
+ var rev = this.style % 2;
+ document.write('<table class="qprogress" border="0" cellspacing="0" cellpadding="0" unselectable="on" ' +
+ (hor ? 'width="' + (size * res.width) + '" height="' + res.height + '"><tr>' : 'width="' + res.width +
+ '" height="' + (size * res.height) + '">'));
+ for (var j=0; j<size; j++) {
+ document.write((hor ? '' : '<tr>') + '<td width="' + res.width + '" height="' + res.height +
+ '" unselectable="on"><img name="' + id + (rev ? size - j - 1 : j) + '" src="' + res.list[0].src +
+ '" border="0" width="' + res.width + '" height="' + res.height + '"></td>' + (hor ? '' : '</tr>'));
+ }
+ document.write((hor ? '</tr>' : '') + '</table>');
+ for (var j=0; j<size; j++) {
+ images[j] = document.images[id + j] || new Image(1, 1);
+ }
+ }
+ } else {
+ this.document.write("invalid resource");
+ }
+}
+QProgress.prototype = new QControl();
+QProgress.NORMAL = 0;
+QProgress.REVERSE = 1;
+QProgress.FALL = 2;
+QProgress.RISE = 3;
diff --git a/httemplate/elements/qlib/sound.js b/httemplate/elements/qlib/sound.js
new file mode 100644
index 0000000..3d1aaf6
--- /dev/null
+++ b/httemplate/elements/qlib/sound.js
@@ -0,0 +1,47 @@
+/**
+ * QLIB 1.0 Preloaded Sound
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QSound_play(loop) {
+ this._out.loop = loop || 0;
+ this._out.src = this._buf.src;
+}
+
+function QSound_stop() {
+ this._out.loop = 0;
+ this._out.src = "";
+}
+
+function QSound_setVolume(volume) {
+ this._out.volume = this.volume = volume;
+}
+
+function QSound(parent, name, src, volume) {
+ this.init(parent, name);
+ this.volume = volume || 0;
+ this.play = this.stop = this.setVolume = QControl.nop;
+ with (this) {
+ document.write('<bgsound id="' + id + '" src="" volume="' + volume + '">');
+ if (document.all && document.all.item) {
+ this._out = document.all.item(id);
+ if (_out && (typeof _out.src != "undefined") && (_out.volume === volume)) {
+ document.write('<bgsound id="b' + id + '" src="' + src + '" volume="-10000">');
+ this._buf = document.all.item("b" + id);
+ if (_buf) {
+ this.play = QSound_play;
+ this.stop = QSound_stop;
+ this.setVolume = QSound_setVolume;
+
+ _out.onreadystatechange = new Function("alert(0)");
+ }
+ }
+ }
+ }
+}
+QSound.prototype = new QControl();
diff --git a/httemplate/elements/qlib/sprite.js b/httemplate/elements/qlib/sprite.js
new file mode 100644
index 0000000..72a68fb
--- /dev/null
+++ b/httemplate/elements/qlib/sprite.js
@@ -0,0 +1,125 @@
+/**
+ * QLIB 1.0 Sprite Object
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QSprite_load(src) {
+ if (src) {
+ this.face = new Image(this.cwidth, this.cheight);
+ this.face.src = src;
+ this.valid = false;
+ }
+}
+
+function QSprite_show(show) {
+ if (show && !this.valid && this.face.complete) {
+ this._img.src = this.face.src;
+ this.valid = true;
+ }
+ this._show(show);
+}
+
+function QSprite_moveTo(x, y) {
+ this.stop();
+ this._move(x, y);
+}
+
+function QSprite_slideTo(x, y) {
+ this.stop();
+ if (this.visible) {
+ this.doSlide(++this._spro, x, y);
+ } else {
+ this.moveTo(x, y);
+ }
+}
+
+function QSprite_shake() {
+ this.stop();
+ if (this.visible) {
+ this.doShake(++this._spro, 0, this.x, this.y);
+ }
+}
+
+function QSprite_stop() {
+ this._spro++;
+ if (this._sprt) {
+ clearTimeout(this._sprt);
+ this._sprt = false;
+ }
+}
+
+function QSprite_doSlide(id, x, y) {
+ if (this._spro == id) {
+ this._sprt = false;
+ var dx = Math.round(x - this.x);
+ var dy = Math.round(y - this.y);
+ if (dx || dy) {
+ if (dx) dx = dx > 0 ? Math.ceil(dx/4) : Math.floor(dx/4);
+ if (dy) dy = dy > 0 ? Math.ceil(dy/4) : Math.floor(dy/4);
+ this._move(this.x + dx, this.y + dy);
+ this._sprt = setTimeout(this.name + ".doSlide(" + id + "," + x + "," + y + ")", 30);
+ } else {
+ this._move(x, y);
+ }
+ }
+}
+
+function QSprite_doShake(id, phase, x, y) {
+ if (this._spro == id) {
+ this._sprt = false;
+ if (phase < 20) {
+ var m = 3 * Math.sin(.16 * phase);
+ this._move(x + m * Math.sin(phase), y + m * Math.cos(phase));
+ this._sprt = setTimeout(this.name + ".doShake(" + id + "," + (++phase) + "," + x + "," + y + ")", 20);
+ } else {
+ this._move(x, y);
+ }
+ }
+}
+
+function QSprite_doClick() {
+ if (!this._sprt) {
+ this.onClick(this.tag);
+ }
+ return false;
+}
+
+function QSprite(parent, name, x, y, width, height, src, visible, effects, opacity, zindex) {
+ this.init(parent, name);
+ this.x = x - 0;
+ this.y = y - 0;
+ this.width = (this.cwidth = width - 0) + 8;
+ this.height = (this.cheight = height - 0) + 8;
+ var j = QSprite.arguments.length;
+ this.visible = (j > 7) ? visible : true;
+ this.effects = (j > 8) ? effects : 0;
+ this.opacity = (j > 9) ? opacity : 100;
+ this.zindex = (j > 10) ? zindex : null;
+ this.valid = !!src;
+ this.content = '<a href="#" title="" onclick="return false" onmousedown="return ' + this.name +
+ '.doClick()" onmouseover="window.top.status=\'\';return true" hidefocus="true" unselectable="on"><img name="' +
+ this.id + '" src="' + (src || '') + '" border="0" width="' + this.cwidth + '" height="' + this.cheight +
+ '" alt="" unselectable="on"></a>';
+ this.doClick = QSprite_doClick;
+ this.doSlide = QSprite_doSlide;
+ this.doShake = QSprite_doShake;
+ this.onClick = QControl.event;
+ this.create();
+ this.face = this._img = this.document.images[this.id] || new Image(1, 1);
+ this._spro = 0;
+ this._sprt = false;
+ this._show = this.show;
+ this._move = this.moveTo;
+ this.load = QSprite_load;
+ this.show = QSprite_show;
+ this.moveTo = QSprite_moveTo;
+ this.slideTo = QSprite_slideTo;
+ this.shake = QSprite_shake;
+ this.stop = QSprite_stop;
+}
+QSprite.prototype = new QWndCtrl();
diff --git a/httemplate/elements/qlib/window.js b/httemplate/elements/qlib/window.js
new file mode 100644
index 0000000..6056fda
--- /dev/null
+++ b/httemplate/elements/qlib/window.js
@@ -0,0 +1,25 @@
+/**
+ * QLIB 1.0 Window Control
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QWindow(parent, name, x, y, width, height, content, visible, effects, opacity, zindex) {
+ this.init(parent, name);
+ this.x = x - 0;
+ this.y = y - 0;
+ this.width = width - 0;
+ this.height = (typeof(height) == "number") ? height : null;
+ this.content = content;
+ var j = QWindow.arguments.length;
+ this.visible = (j > 7) ? visible : true;
+ this.effects = (j > 8) ? effects : 0;
+ this.opacity = (j > 9) ? opacity : 100;
+ this.zindex = (j > 10) ? zindex : null;
+ this.create();
+}
+QWindow.prototype = new QWndCtrl();
diff --git a/httemplate/elements/qlib/wndctrl.js b/httemplate/elements/qlib/wndctrl.js
new file mode 100644
index 0000000..b3bde4e
--- /dev/null
+++ b/httemplate/elements/qlib/wndctrl.js
@@ -0,0 +1,322 @@
+/**
+ * QLIB 1.0 Window Abstraction
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * http://qlib.quazzle.com
+ */
+
+function QWndCtrl_center_ie4() {
+ var b = this.document.body;
+ this.moveTo(b.scrollLeft + Math.max(0, Math.floor((b.clientWidth -
+ this.width) / 2)), b.scrollTop + 100);
+}
+
+function QWndCtrl_center_moz() {
+ this.moveTo(self.pageXOffset + Math.max(0, Math.floor((self.innerWidth -
+ this.width) / 2)), self.pageYOffset + 100);
+}
+
+function QWndCtrl_setEffects_ie4(fx) {
+ this.effects = fx;
+ with (this.wnd) {
+ filters[0].enabled = (fx & 256) != 0;
+ filters[1].enabled = (fx & 512) != 0;
+ filters[2].enabled = (fx & 1024) != 0;
+ filters[4].enabled = (fx & 2048) != 0;
+ }
+}
+
+function QWndCtrl_setEffects_moz(fx) {
+ this.effects = fx;
+}
+
+function QWndCtrl_setOpacity_ie4(op) {
+ this.opacity = Math.max(0, Math.min(100, Math.floor(op - 0)));
+ this.wnd.filters[3].opacity = this.opacity;
+ this.wnd.filters[3].enabled = (this.opacity < 100);
+}
+
+function QWndCtrl_setOpacity_moz(op) {
+ this.opacity = Math.max(0, Math.min(100, Math.floor(op - 0)));
+ this.wnd.style.MozOpacity = this.opacity + "%";
+}
+
+function QWndCtrl_setSize_css(w, h) {
+ this.wnd.style.width = (this.width = Math.floor(w - 0)) + "px";
+ this.wnd.style.height = typeof(h) == "number" ? (this.height = Math.floor(h)) + "px" : "auto";
+}
+
+function QWndCtrl_setSize_ns4(w, h) {
+ this.wnd.clip.width = this.width = Math.floor(w - 0);
+ if (typeof(h) == "number") {
+ this.wnd.clip.height = this.height = Math.floor(h);
+ }
+}
+
+function QWndCtrl_focus() {
+ this.setZIndex(QWndCtrl.TOPZINDEX++);
+}
+
+function QWndCtrl_setZIndex_css(z) {
+ this.wnd.style.zIndex = this.zindex = z || 0;
+}
+
+function QWndCtrl_setZIndex_ns4(z) {
+ this.wnd.zIndex = this.zindex = z || 0;
+}
+
+function QWndCtrl_moveTo_css(x, y) {
+ this.wnd.style.left = (this.x = Math.floor(x - 0)) + "px";
+ this.wnd.style.top = (this.y = Math.floor(y - 0)) + "px";
+}
+
+function QWndCtrl_moveTo_ns4(x, y) {
+ this.wnd.moveTo(this.x = Math.floor(x - 0), this.y = Math.floor(y - 0));
+}
+
+function QWndCtrl_fxhandler() {
+ this.fxhandler = QControl.nop;
+ this.onShow(this.visible, this.tag);
+}
+
+function QWndCtrl_show_ie4(show) {
+ if (this.visible != show) {
+ var fx = false;
+ switch (show ? this.effects & 15 : (this.effects & 240) >>> 4) {
+ case 1:
+ fx = this.wnd.filters[5];
+ break;
+ case 2:
+ (fx = this.wnd.filters[6]).transition = show ? 1 : 0;
+ break;
+ case 3:
+ (fx = this.wnd.filters[6]).transition = show ? 3 : 2;
+ break;
+ case 4:
+ (fx = this.wnd.filters[6]).transition = show ? 5 : 4;
+ break;
+ case 5:
+ (fx = this.wnd.filters[6]).transition = show ? 14 : 13;
+ break;
+ case 6:
+ (fx = this.wnd.filters[6]).transition = show ? 16 : 15;
+ break;
+ case 7:
+ (fx = this.wnd.filters[6]).transition = 12;
+ break;
+ case 8:
+ (fx = this.wnd.filters[6]).transition = 8;
+ break;
+ case 9:
+ (fx = this.wnd.filters[6]).transition = 9;
+ }
+ if (fx) {
+ fx.apply();
+ this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";
+ this.fxhandler = QWndCtrl_fxhandler;
+ fx.play(0.3);
+ } else {
+ this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";
+ this.onShow(show, this.tag);
+ }
+ }
+}
+
+function QWndCtrl_fade_moz(op, step) {
+ this._wndt = false;
+ if (step) {
+ op += step;
+ if ((op > 0) && (op < this.opacity)) {
+ this.wnd.style.MozOpacity = op + "%";
+ this._wndt = setTimeout(this.name + ".fade(" + op + "," + step + ")", 50);
+ } else {
+ if (op <= 0) {
+ this.wnd.style.visibility = "hidden";
+ this.visible = false;
+ }
+ this.wnd.style.MozOpacity = this.opacity + "%";
+ this.onShow(this.visible, this.tag);
+ }
+ }
+}
+
+function QWndCtrl_show_moz(show) {
+ if (this.visible != show) {
+ if (this._wndt) {
+ clearTimeout(this._wndt);
+ this._wndt = false;
+ }
+ var step = show ? ((this.effects & 15) == 1) && Math.floor(this.opacity / 5) :
+ ((this.effects & 240) == 16) && -Math.floor(this.opacity / 5);
+ if (step) {
+ if (this.visible) {
+ this.fade(this.opacity - 0, step);
+ } else {
+ this.wnd.style.MozOpacity = "0%";
+ this.wnd.style.visibility = "visible";
+ this.visible = true;
+ this.fade(0, step);
+ }
+ } else {
+ this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";
+ this.onShow(show, this.tag);
+ }
+ }
+}
+
+function QWndCtrl_show_css(show) {
+ if (this.visible != show) {
+ this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";
+ this.onShow(show, this.tag);
+ }
+}
+
+function QWndCtrl_show_ns4(show) {
+ if (this.visible != show) {
+ this.wnd.visibility = (this.visible = show) ? "show" : "hidden";
+ this.onShow(show, this.tag);
+ }
+}
+
+function QWndCtrl_create_dom2() {
+ with (this) {
+ this.fxhandler = QControl.nop;
+ var ie4 = document.body && document.body.filters;
+ var moz = document.body && document.body.style &&
+ typeof(document.body.style.MozOpacity) == "string";
+ document.write('<div unselectable="on" id="' + id +
+ (ie4 ? '" onfilterchange="' + name + '.fxhandler()': '') +
+ '" style="position:absolute;left:' + x + 'px;top:' + y +
+ 'px;width:' + width + (height != null ? 'px;height:' + height : '') +
+ 'px;visibility:' + (visible ? 'visible' : 'hidden') +
+ ';overflow:hidden' + (zindex ? ';z-index:' + zindex : '') +
+ (ie4 ? ';filter:Gray(enabled=' + (effects & 256 ? '1' : '0') +
+ ') Xray(enabled=' + (effects & 512 ? '1' : '0') +
+ ') Invert(enabled=' + (effects & 1024 ? '1' : '0') +
+ ') alpha(enabled=' + (opacity < 100 ? '1' : '0') + ',opacity=' + opacity +
+ ') shadow(enabled=' + (effects & 2048 ? '1' : '0') +
+ ',direction=135) BlendTrans(enabled=0) RevealTrans(enabled=0)' : '') +
+ (moz && (opacity < 100) ? ';-moz-opacity:' + opacity + '%' : '') +
+ '"><div unselectable="on" class="qwindow">');
+ if (typeof(content) == "function") {
+ this.content();
+ } else {
+ document.write(content);
+ }
+ document.write('</div></div>');
+ if (this.wnd = document.getElementById ? document.getElementById(id) :
+ (document.all.item ? document.all.item(id) : document.all[id])) {
+ if (wnd.style) {
+ ie4 = ie4 && wnd.filters;
+ moz = moz && typeof(wnd.style.MozOpacity) == "string";
+ this.moveTo = QWndCtrl_moveTo_css;
+ this.setZIndex = QWndCtrl_setZIndex_css;
+ this.focus = QWndCtrl_focus;
+ this.setSize = QWndCtrl_setSize_css;
+ this.show = ie4 ? QWndCtrl_show_ie4 : (moz ? QWndCtrl_show_moz : QWndCtrl_show_css);
+ this.fade = moz ? QWndCtrl_fade_moz : QControl.nop;
+ this.setOpacity = ie4 ? QWndCtrl_setOpacity_ie4 : (moz ? QWndCtrl_setOpacity_moz : QControl.nop);
+ this.setEffects = ie4 ? QWndCtrl_setEffects_ie4 : (moz ? QWndCtrl_setEffects_moz : QControl.nop);
+ this.center = self.innerWidth ? QWndCtrl_center_moz :
+ (document.body && document.body.clientWidth ? QWndCtrl_center_ie4 : QControl.nop);
+ }
+ }
+ }
+}
+
+function QWndCtrl_create_ns4(finalize) {
+ with (this) {
+ if (finalize) {
+ if (_wnde) {
+ parent.window.onload = _wnde;
+ parent.window.onload();
+ }
+ document.open();
+ document.write('<div class="qwindow">');
+ this.content();
+ document.write('</div>');
+ document.close();
+ } else {
+ document.write('<layer id="' + id + '" left="' + x + '" top="' + y +
+ '" width="' + width + '" visibility="' + (visible ? 'show' : 'hidden') +
+ (height != null ? '" height="' + height + '" clip="' + width + ',' + height : '') +
+ (zindex ? '" z-index="' + zindex : '') + (typeof(content) != "function" ?
+ '"><div class="qwindow">' + content + '</div></layer>' : '">&nbsp;</layer>'));
+ if (this.window = this.wnd = document.layers[id]) {
+ if (this.document = wnd.document) {
+ this.show = QWndCtrl_show_ns4;
+ this.moveTo = QWndCtrl_moveTo_ns4;
+ this.setZIndex = QWndCtrl_setZIndex_ns4;
+ this.focus = QWndCtrl_focus;
+ this.center = QWndCtrl_center_moz;
+ this.setSize = QWndCtrl_setSize_ns4;
+ if (typeof(content) == "function") {
+ this._wnde = parent.window.onload;
+ parent.window.onload = new Function(name + ".create(true)");
+ }
+ }
+ }
+ }
+ }
+}
+
+function QWndCtrl_create_na() {
+ this.document.write('Object is not supported.');
+ this.wnd = null;
+}
+
+function QWndCtrl_create() {
+ with (this) {
+ this.create = (document.getElementById || document.all) ? QWndCtrl_create_dom2 :
+ (document.layers ? QWndCtrl_create_ns4 : QWndCtrl_create_na);
+ create();
+ }
+}
+
+function QWndCtrl() {
+ this.x = this.y = 0;
+ this.width = this.height = 0;
+ this.content = "";
+ this.visible = true;
+ this.effects = 0;
+ this.opacity = 100;
+ this.zindex = null;
+ this._wndt = this._wnde = false;
+ this.create = QWndCtrl_create;
+ this.show = QControl.nop;
+ this.focus = QControl.nop;
+ this.center = QControl.nop;
+ this.moveTo = QControl.nop;
+ this.setSize = QControl.nop;
+ this.setOpacity = QControl.nop;
+ this.setEffects = QControl.nop;
+ this.setZIndex = QControl.nop;
+ this.onShow = QControl.event;
+}
+QWndCtrl.prototype = new QControl();
+QWndCtrl.TOPZINDEX = 1000;
+QWndCtrl.GRAY = 256;
+QWndCtrl.XRAY = 512;
+QWndCtrl.INVERT = 1024;
+QWndCtrl.SHADOW = 2048;
+QWndCtrl.FADEIN = 1;
+QWndCtrl.FADEOUT = 16;
+QWndCtrl.BOXIN = 2;
+QWndCtrl.BOXOUT = 32;
+QWndCtrl.CIRCLEIN = 3;
+QWndCtrl.CIRCLEOUT = 48;
+QWndCtrl.WIPEIN = 4;
+QWndCtrl.WIPEOUT = 64;
+QWndCtrl.HBARNIN = 5;
+QWndCtrl.HBARNOUT = 80;
+QWndCtrl.VBARNIN = 6;
+QWndCtrl.VBARNOUT = 96;
+QWndCtrl.DISSOLVEIN = 7;
+QWndCtrl.DISSOLVEOUT = 112;
+QWndCtrl.HBLINDSIN = 8;
+QWndCtrl.HBLINDSOUT = 128;
+QWndCtrl.VBLINDSIN = 9;
+QWndCtrl.VBLINDSOUT = 144;
diff --git a/httemplate/elements/search-cust_main.html b/httemplate/elements/search-cust_main.html
new file mode 100644
index 0000000..dbcc2ed
--- /dev/null
+++ b/httemplate/elements/search-cust_main.html
@@ -0,0 +1,181 @@
+<INPUT TYPE="hidden" NAME="<% $opt{'field_name'} %>" VALUE="<% $value %>">
+
+<!-- some false laziness w/ misc/batch-cust_pay.html, though not as bad as i'd thought at first... -->
+
+<INPUT TYPE = "text"
+ NAME = "<% $opt{'field_name'} %>_search"
+ ID = "<% $opt{'field_name'} %>_search"
+ SIZE = "32"
+ VALUE="<% $cust_main ? $cust_main->name : '(cust #, name or company)' %>"
+ onFocus="clearhint_<% $opt{'field_name'} %>_search(this);"
+ onClick="clearhint_<% $opt{'field_name'} %>_search(this);"
+ onChange="smart_<% $opt{'field_name'} %>_search(this);"
+>
+
+% if ( $opt{'find_button'} ) {
+ <INPUT TYPE = "button"
+ VALUE = 'Find',
+ NAME = "<% $opt{'field_name'} %>_findbutton"
+ onClick = "smart_<% $opt{'field_name'} %>_search(this.form.<% $opt{'field_name'} %>_search);"
+ >
+% }
+
+<SELECT NAME="<% $opt{'field_name'} %>_select" ID="<% $opt{'field_name'} %>_select" STYLE="color:#ff0000; display:none" onChange="select_<% $opt{'field_name'} %>(this);">
+</SELECT>
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
+ 'subs' => [ 'smart_search' ],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ function clearhint_<% $opt{'field_name'} %>_search (what) {
+
+ what.style.color = '#000000';
+
+ if ( what.value == '(cust #, name or company)' )
+ what.value = '';
+
+ if ( what.value.indexOf('Customer not found: ') == 0 )
+ what.value = what.value.substr(20);
+
+ }
+
+ function smart_<% $opt{'field_name'} %>_search(what) {
+
+ var customer = what.value;
+
+ if ( customer == 'searching...' || customer == ''
+ || customer.indexOf('Customer not found: ') == 0 )
+ return;
+
+ if ( what.getAttribute('magic') == 'nosearch' ) {
+ what.setAttribute('magic', '');
+ return;
+ }
+
+ //what.value = 'searching...'
+ what.disabled = true;
+ what.style.color= '#000000';
+ what.style.backgroundColor = '#dddddd';
+
+ var customer_select = document.getElementById('<% $opt{'field_name'} %>_select');
+
+ //alert("search for customer " + customer);
+
+ function <% $opt{'field_name'} %>_search_update(customers) {
+
+ //alert('customers returned: ' + customers);
+
+ var customerArray = eval('(' + customers + ')');
+
+ what.disabled = false;
+ what.style.backgroundColor = '#ffffff';
+
+ if ( customerArray.length == 0 ) {
+
+ what.form.<% $opt{'field_name'} %>.value = '';
+
+ what.value = 'Customer not found: ' + what.value;
+ what.style.color = '#ff0000';
+
+ what.style.display = '';
+ customer_select.style.display = 'none';
+
+ } else if ( customerArray.length == 1 ) {
+
+ //alert('one customer found: ' + customerArray[0]);
+
+ what.form.<% $opt{'field_name'} %>.value = customerArray[0][0];
+ what.value = customerArray[0][1];
+
+ what.style.display = '';
+ customer_select.style.display = 'none';
+
+ } else {
+
+ //alert('multiple customers found, have to create select dropdown');
+
+ //blank the current list
+ for ( var i = customer_select.length; i >= 0; i-- )
+ customer_select.options[i] = null;
+
+ opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
+
+ //add the multiple customers
+ for ( var s = 0; s < customerArray.length; s++ )
+ opt(customer_select, customerArray[s][0], customerArray[s][1], '#000000');
+
+ opt(customer_select, 'cancel', '(Edit search string)', '#000000');
+
+ what.style.display = 'none';
+ customer_select.style.display = '';
+
+ }
+
+ }
+
+ smart_search( customer, <% $opt{'field_name'} %>_search_update );
+
+
+ }
+
+ function select_<% $opt{'field_name'} %> (what) {
+
+ var custnum = what.options[what.selectedIndex].value;
+ var customer = what.options[what.selectedIndex].text;
+
+ var customer_obj = document.getElementById('<% $opt{'field_name'} %>_search');
+
+ if ( custnum == '' ) {
+ //what.style.color = '#ff0000';
+
+ } else if ( custnum == 'cancel' ) {
+
+ customer_obj.style.color = '#000000';
+
+ what.style.display = 'none';
+ customer_obj.style.display = '';
+ customer_obj.focus();
+
+ } else {
+
+ what.form.<% $opt{'field_name'} %>.value = custnum;
+
+ customer_obj.value = customer;
+ customer_obj.style.color = '#000000';
+
+ what.style.display = 'none';
+ customer_obj.style.display = '';
+
+ }
+
+ }
+
+ function opt(what,value,text,color) {
+ var optionName = new Option(text, value, false, false);
+ optionName.style.color = color;
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+</SCRIPT>
+<%init>
+
+my( %opt ) = @_;
+$opt{'field_name'} ||= 'custnum';
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+my $cust_main = '';
+if ( $value ) {
+ $cust_main = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $value },
+ 'extra_sql' => " AND ". $FS::CurrentUser::CurrentUser->agentnums_sql,
+ });
+}
+
+</%init>
diff --git a/httemplate/elements/select-access_group.html b/httemplate/elements/select-access_group.html
new file mode 100644
index 0000000..299a66a
--- /dev/null
+++ b/httemplate/elements/select-access_group.html
@@ -0,0 +1,16 @@
+%
+% my( $groupnum, %opt ) = @_;
+%
+% %opt{'records'} = delete $opt{'access_group'}
+% if $opt{'access_group'};
+%
+%
+<% include( '/elements/select-table.html',
+ 'table' => 'access_group',
+ 'name_col' => 'groupname',
+ 'value' => $groupnum,
+ 'empty_label' => '(none)',
+ #'hashref' => { 'disabled' => '' },
+ %opt,
+ )
+%>
diff --git a/httemplate/elements/select-agent.html b/httemplate/elements/select-agent.html
new file mode 100644
index 0000000..897c982
--- /dev/null
+++ b/httemplate/elements/select-agent.html
@@ -0,0 +1,31 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'agent',
+ 'name_col' => 'agent',
+ 'value' => $agentnum || '',
+ 'agent_virt' => 1,
+ 'empty_label' => 'all',
+ 'hashref' => { 'disabled' => '' },
+ 'order_by' => ' ORDER BY agent',
+ 'disable_empty' => $disable_empty,
+ %opt,
+ )
+%>
+<%init>
+
+my %opt = @_;
+my $agentnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'records'} = delete $opt{'agents'}
+ if $opt{'agents'};
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+my $disable_empty = 0;
+if ( $opt{'agent_null_right'} ) {
+ if ( $curuser->access_right($opt{'agent_null_right'}) ) {
+ $disable_empty = 0;
+ } else {
+ $disable_empty = 1;
+ }
+}
+
+</%init>
diff --git a/httemplate/elements/select-agent_type.html b/httemplate/elements/select-agent_type.html
new file mode 100644
index 0000000..ba59cd3
--- /dev/null
+++ b/httemplate/elements/select-agent_type.html
@@ -0,0 +1,21 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'agent_type',
+ 'name_col' => 'atype',
+ 'value' => $typenum || '',
+ 'empty_label' => 'all',
+ 'hashref' => { 'disabled' => '' },
+ #'extra_sql' => ' AND '.
+ # $FS::CurrentUser::CurrentUser->agentnums_sql.
+ # ' ORDER BY agent',
+ %opt,
+ )
+%>
+<%init>
+
+my %opt = @_;
+my $typenum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'records'} = delete $opt{'agent_types'}
+ if $opt{'agent_types'};
+
+</%init>
diff --git a/httemplate/elements/select-agent_types.html b/httemplate/elements/select-agent_types.html
new file mode 100644
index 0000000..400b453
--- /dev/null
+++ b/httemplate/elements/select-agent_types.html
@@ -0,0 +1,30 @@
+%# if ( $cgi->param('clone') ) { #XXX
+% if ( $opt{'disabled'} ) {
+
+ <INPUT TYPE="hidden" NAME="agent_type" VALUE="">
+
+% } elsif ( scalar(@all_agent_types) == 1) {
+
+ <INPUT TYPE="hidden" NAME="agent_type" VALUE="<% $all_agent_types[0] %>">
+
+% } else {
+
+ <% include( 'select-table.html',
+ 'element_name' => 'agent_type',
+ 'table' => 'agent_type',
+ 'name_col' => 'atype',
+ #'value' => \@agent_type,
+ 'element_etc' => 'size="10"',
+ %opt,
+ 'multiple' => '1', #cause edit.html is dum
+ )
+ %>
+
+% }
+<%init>
+
+my %opt = @_;
+
+my @all_agent_types = map {$_->typenum} qsearch('agent_type',{});
+
+</%init>
diff --git a/httemplate/elements/select-areacode.html b/httemplate/elements/select-areacode.html
new file mode 100644
index 0000000..aa2d73b
--- /dev/null
+++ b/httemplate/elements/select-areacode.html
@@ -0,0 +1,91 @@
+<% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/areacodes.cgi',
+ 'subs' => [ $opt{'prefix'}. 'get_areacodes' ],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function <% $opt{'prefix'} %>state_changed(what, callback) {
+
+ what.form.<% $opt{'prefix'} %>areacode.disabled = 'disabled';
+ what.form.<% $opt{'prefix'} %>areacode.style.display = 'none';
+ var areacodewait = document.getElementById('<% $opt{'prefix'} %>areacodewait');
+ areacodewait.style.display = '';
+ var areacodeerror = document.getElementById('<% $opt{'prefix'} %>areacodeerror');
+ areacodeerror.style.display = 'none';
+
+ what.form.<% $opt{'prefix'} %>exchange.disabled = 'disabled';
+ what.form.<% $opt{'prefix'} %>phonenum.disabled = 'disabled';
+
+ state = what.options[what.selectedIndex].value;
+
+ function <% $opt{'prefix'} %>update_areacodes(areacodes) {
+
+ // blank the current areacode
+ for ( var i = what.form.<% $opt{'prefix'} %>areacode.length; i >= 0; i-- )
+ what.form.<% $opt{'prefix'} %>areacode.options[i] = null;
+ // blank the current exchange too
+ for ( var i = what.form.<% $opt{'prefix'} %>exchange.length; i >= 0; i-- )
+ what.form.<% $opt{'prefix'} %>exchange.options[i] = null;
+ opt(what.form.<% $opt{'prefix'} %>exchange, '', 'Select city / exchange');
+ // blank the current phonenum too
+ for ( var i = what.form.<% $opt{'prefix'} %>phonenum.length; i >= 0; i-- )
+ what.form.<% $opt{'prefix'} %>phonenum.options[i] = null;
+ opt(what.form.<% $opt{'prefix'} %>phonenum, '', 'Select phone number');
+
+% if ($opt{empty}) {
+ opt(what.form.<% $opt{'prefix'} %>areacode, '', '<% $opt{empty} %>');
+% }
+
+ // add the new areacodes
+ var areacodeArray = eval('(' + areacodes + ')' );
+ for ( var s = 0; s < areacodeArray.length; s++ ) {
+ var areacodeLabel = areacodeArray[s];
+ if ( areacodeLabel == "" )
+ areacodeLabel = '(n/a)';
+ opt(what.form.<% $opt{'prefix'} %>areacode, areacodeArray[s], areacodeLabel);
+ }
+
+ areacodewait.style.display = 'none';
+ if ( areacodeArray.length >= 1 ) {
+ what.form.<% $opt{'prefix'} %>areacode.disabled = '';
+ what.form.<% $opt{'prefix'} %>areacode.style.display = '';
+ } else {
+ var areacodeerror = document.getElementById('<% $opt{'prefix'} %>areacodeerror');
+ areacodeerror.style.display = '';
+ }
+
+ //run the callback
+ if ( callback != null )
+ callback();
+ }
+
+ // go get the new areacodes
+ <% $opt{'prefix'} %>get_areacodes( state, <% $opt{'svcpart'} %>, <% $opt{'prefix'} %>update_areacodes );
+
+ }
+
+</SCRIPT>
+
+<DIV ID="areacodewait" STYLE="display:none"><IMG SRC="<%$fsurl%>images/wait-orange.gif"> <B>Finding area codes</B></DIV>
+
+<DIV ID="areacodeerror" STYLE="display:none"><IMG SRC="<%$fsurl%>images/cross.png"> <B>Select a different state</B></DIV>
+
+<SELECT NAME="<% $opt{'prefix'} %>areacode" onChange="<% $opt{'prefix'} %>areacode_changed(this); <% $opt{'onchange'} %>" <% $opt{'disabled'} %>>
+ <OPTION VALUE="">Select area code</OPTION>
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+
+$opt{disabled} = 'disabled' unless exists $opt{disabled};
+
+</%init>
diff --git a/httemplate/elements/select-cdrbatch.html b/httemplate/elements/select-cdrbatch.html
new file mode 100644
index 0000000..866ba25
--- /dev/null
+++ b/httemplate/elements/select-cdrbatch.html
@@ -0,0 +1,38 @@
+% if ( scalar(@{ $opt{'cdrbatches'} }) ) {
+
+ <SELECT NAME="<% $opt{'name'} || 'cdrbatch' %>">
+
+ <OPTION VALUE="__ALL__">All
+ <OPTION VALUE="">(blank)
+
+% foreach my $cdrbatch ( @{ $opt{'cdrbatches'} } ) {
+ <OPTION VALUE="<% $cdrbatch %>"<% $cdrbatch eq $selected_cdrbatch ? ' SELECTED' : '' %>><% $cdrbatch %>
+% }
+
+ </SELECT>
+
+% } else {
+
+ <INPUT TYPE="hidden" NAME="cdrbatch" VALUE="__ALL__">
+
+% }
+
+<%init>
+
+my %opt = @_;
+my $selected_cdrbatch = $opt{'curr_value'}; # || $opt{'value'} necessary?
+
+my $conf = new FS::Conf;
+
+unless ( $opt{'cdrbatches'} ) {
+
+ my $sth = dbh->prepare('SELECT DISTINCT cdrbatch FROM cdr')
+ or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my %cdrbatches = map { $_->[0] => 1 } @{$sth->fetchall_arrayref};
+ @{ $opt{'cdrbatches'} } = grep $_, keys %cdrbatches;
+
+}
+
+</%init>
+
diff --git a/httemplate/elements/select-country.html b/httemplate/elements/select-country.html
new file mode 100644
index 0000000..45b0ccd
--- /dev/null
+++ b/httemplate/elements/select-country.html
@@ -0,0 +1,127 @@
+<%doc>
+
+Example:
+
+ include( '/elements/select-country.html',
+ #recommended
+ country => $current_country,
+
+ #optional
+ prefix => $optional_unique_prefix,
+ onchange => $javascript,
+ disabled => 0, #bool
+ disable_empty => 1, #defaults to 1, disable the empty option
+ empty_label => 'all', #label for empty option
+ disable_stateupdate => 0, #bool - disabled update of the select-state.html
+ style => [ 'attribute:value', 'another:value' ],
+ );
+
+</%doc>
+% unless ( $opt{'disable_stateupdate'} ) {
+
+ <% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/states.cgi',
+ 'subs' => [ $pre. 'get_states' ],
+ )
+ %>
+
+ <SCRIPT TYPE="text/javascript">
+
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function <% $pre %>country_changed(what, callback) {
+
+ country = what.options[what.selectedIndex].value;
+
+ function <% $pre %>update_states(states) {
+
+ // blank the current state list
+ for ( var i = what.form.<% $pre %>state.length; i >= 0; i-- )
+ what.form.<% $pre %>state.options[i] = null;
+
+ // add the new states
+ var statesArray = eval('(' + states + ')' );
+ for ( var s = 0; s < statesArray.length; s=s+2 ) {
+ var stateLabel = statesArray[s+1];
+ if ( stateLabel == "" )
+ stateLabel = '(n/a)';
+ opt(what.form.<% $pre %>state, statesArray[s], stateLabel);
+ }
+
+ //run the callback
+ if ( callback != null )
+ callback();
+ }
+
+ // go get the new states
+ <% $pre %>get_states( country, <% $pre %>update_states );
+
+ }
+
+ </SCRIPT>
+
+% }
+
+<SELECT NAME = "<% $pre %>country"
+ ID = "<% $pre %>country"
+ onChange = "<% $onchange %>"
+ <% $opt{'disabled'} %>
+ <% $style %>
+>
+
+% unless ( $opt{'disable_empty'} ) {
+ <OPTION VALUE=""><% $opt{'empty_label'} || '(all)' %>
+% }
+
+% foreach my $country ( @all_countries ) {
+
+ <OPTION VALUE="<% $country |h %>"
+ <% $country eq $opt{'country'} ? ' SELECTED' : '' %>
+ ><% code2country($country). " ($country)" %>
+
+% }
+
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+foreach my $opt (qw( country prefix onchange disabled disable_stateupdate )) {
+ $opt{$opt} = '' unless exists($opt{$opt}) && defined($opt{$opt});
+}
+
+$opt{'disable_empty'} = 1 unless exists($opt{'disable_empty'});
+
+my $pre = $opt{'prefix'};
+
+my $onchange =
+ ( $opt{'disable_stateupdate'} ? '' : $pre.'country_changed(this); ' ).
+ $opt{'onchange'};
+
+$opt{'style'} ||= [];
+my $style =
+ scalar(@{$opt{style}})
+ ? 'STYLE="'. join(';', @{$opt{style}}). '"'
+ : '';
+
+my $conf = new FS::Conf;
+my $default = $conf->config('countrydefault') || 'US';
+
+my @all_countries = (
+ sort { ($b eq $default) <=> ($a eq $default)
+ or code2country($a) cmp code2country($b)
+ }
+ map { $_->country }
+ qsearch({
+ 'select' => 'country',
+ 'table' => 'cust_main_county',
+ 'hashref' => {},
+ 'extra_sql' => 'GROUP BY country',
+ })
+ );
+
+</%init>
diff --git a/httemplate/elements/select-county.html b/httemplate/elements/select-county.html
new file mode 100644
index 0000000..59f235a
--- /dev/null
+++ b/httemplate/elements/select-county.html
@@ -0,0 +1,160 @@
+<%doc>
+
+Example:
+
+ include( '/elements/select-county.html',
+ #recommended
+ country => $current_country,
+ state => $current_state,
+ county => $current_county,
+
+ #optional
+ prefix => $optional_unique_prefix,
+ onchange => $javascript,
+ disabled => 0, #bool
+ disable_empty => 1, #defaults to 1, disable the empty option
+ empty_label => 'all', #label for empty option
+ style => [ 'attribute:value', 'another:value' ],
+ );
+
+</%doc>
+% if ( $countyflag ) {
+
+ <% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/counties.cgi',
+ 'subs' => [ $pre. 'get_counties' ],
+ )
+ %>
+
+ <SCRIPT TYPE="text/javascript">
+
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function <% $pre %>state_changed(what, callback) {
+
+ state = what.options[what.selectedIndex].value;
+ country = what.form.<% $pre %>country.options[what.form.<% $pre %>country.selectedIndex].value;
+
+ function <% $pre %>update_counties(counties) {
+
+ // blank the current county list
+ for ( var i = what.form.<% $pre %>county.length; i >= 0; i-- )
+ what.form.<% $pre %>county.options[i] = null;
+
+ // add the new counties
+ var countiesArray = eval('(' + counties + ')' );
+ for ( var s = 0; s < countiesArray.length; s++ ) {
+ var countyLabel = countiesArray[s];
+ if ( countyLabel == "" )
+ countyLabel = '(n/a)';
+ opt(what.form.<% $pre %>county, countiesArray[s], countyLabel);
+ }
+
+ var countyFormLabel = document.getElementById('<% $pre %>countylabel');
+
+ if ( countiesArray.length > 1 ) {
+ what.form.<% $pre %>county.style.display = '';
+ countyFormLabel.style.visibility = 'visible';
+ } else {
+ what.form.<% $pre %>county.style.display = 'none';
+ countyFormLabel.style.visibility = 'hidden';
+ }
+
+ //run the callback
+ if ( callback != null )
+ callback();
+ }
+
+ // go get the new counties
+ <% $pre %>get_counties( state, country, <% $pre %>update_counties );
+
+ }
+
+ </SCRIPT>
+
+ <SELECT NAME = "<% $pre %>county"
+ ID = "<% $pre %>county"
+ onChange= "<% $opt{'onchange'} %>"
+ <% $opt{'disabled'} %>
+ <% $style %>
+ >
+
+% unless ( $opt{'disable_empty'} ) {
+ <OPTION VALUE="" <% $opt{county} eq '' ? 'SELECTED' : '' %>><% $opt{empty_label} %>
+% }
+
+% foreach my $county ( @counties ) {
+
+ <OPTION VALUE="<% $county |h %>"
+ <% $county eq $opt{'county'} ? 'SELECTED' : '' %>
+ ><% $county eq $opt{'empty_data_value'} ? $opt{'empty_data_label'} : $county %>
+
+% }
+
+ </SELECT>
+
+% } else {
+
+ <SCRIPT TYPE="text/javascript">
+ function <% $pre %>state_changed(what) {
+ }
+ </SCRIPT>
+
+ <SELECT NAME = "<% $pre %>county"
+ ID = "<% $pre %>county"
+ STYLE = "display:none"
+ >
+ <OPTION SELECTED VALUE="<% $opt{'county'} |h %>">
+ </SELECT>
+
+% }
+
+<%init>
+
+my %opt = @_;
+foreach my $opt (qw( county state country prefix onchange disabled
+ empty_value )) {
+ $opt{$opt} = '' unless exists($opt{$opt}) && defined($opt{$opt});
+}
+
+$opt{'disable_empty'} = 1 unless exists($opt{'disable_empty'});
+
+my $pre = $opt{'prefix'};
+
+$opt{'style'} ||= [];
+my $style =
+ scalar(@{$opt{style}})
+ ? 'STYLE="'. join(';', @{$opt{style}}). '"'
+ : '';
+
+my @counties = ();
+if ( $countyflag ) {
+
+ @counties = map { length($_) ? $_ : $opt{'empty_data_value'} }
+ counties( $opt{'state'}, $opt{'country'} );
+
+ # this is very hacky
+ unless ( scalar(@counties) > 1 ) {
+ if ( $opt{'disabled'} =~ /STYLE=/i ) {
+ $opt{'disabled'} =~ s/STYLE="([^"]+)"/STYLE="$1; display:none"/i;
+ } else {
+ $opt{'disabled'} .= ' STYLE="display:none"';
+ }
+ }
+
+}
+
+</%init>
+<%once>
+
+my $sql = "SELECT COUNT(*) FROM cust_main_county".
+ " WHERE county IS NOT NULL AND county != ''";
+my $sth = dbh->prepare($sql) or die dbh->errstr;
+$sth->execute or die $sth->errstr;
+my $countyflag = $sth->fetchrow_arrayref->[0];
+
+</%once>
diff --git a/httemplate/elements/select-cust-fields.html b/httemplate/elements/select-cust-fields.html
new file mode 100644
index 0000000..98feaf8
--- /dev/null
+++ b/httemplate/elements/select-cust-fields.html
@@ -0,0 +1,24 @@
+%
+% my( $cust_fields, %opt ) = @_;
+%
+% use FS::ConfDefaults;
+% $opt{'avail_fields'} ||= [ FS::ConfDefaults->cust_fields_avail() ];
+%
+% tie my %hash, 'Tie::IxHash', @{ $opt{'avail_fields'} };
+%
+%
+
+
+<SELECT NAME="cust_fields">
+
+ <OPTION VALUE="">(configured default)
+%
+% foreach my $value ( keys %hash ) {
+
+
+ <OPTION VALUE="<% $value %>"><% $hash{$value} %>
+% }
+
+
+</SELECT>
+
diff --git a/httemplate/elements/select-cust-part_pkg.html b/httemplate/elements/select-cust-part_pkg.html
new file mode 100644
index 0000000..2926629
--- /dev/null
+++ b/httemplate/elements/select-cust-part_pkg.html
@@ -0,0 +1,41 @@
+<%doc>
+
+Example:
+
+ include( '/elements/select-cust-part_pkg.html',
+
+ #required
+ 'cust_main' => $cust_main, #or 'custnum' ?
+
+ #strongly recommended (you want your forms to be "sticky" on errors, right?)
+ 'curr_value' => 'current_value',
+
+ #opt
+ 'part_pkg' => \@records,
+
+ #select-table.html options
+ )
+
+</%doc>
+
+<% include( '/elements/select-part_pkg.html',
+ 'empty_label' => 'Select package', #? need here in case removed
+ #from select-part_pkg ??
+ %opt,
+ )
+%>
+<%init>
+
+my( %opt ) = @_;
+
+my $cust_main = $opt{'cust_main'}
+ or die "cust_main not specified";
+
+$opt{'extra_sql'} .=
+ ' AND ( agentnum IS NOT NULL '.
+ ' OR 0 < ( SELECT COUNT(*) FROM type_pkgs '.
+ ' WHERE typenum = '. $cust_main->agent->typenum.
+ ' AND type_pkgs.pkgpart = part_pkg.pkgpart )'.
+ ' )';
+
+</%init>
diff --git a/httemplate/elements/select-cust_main-status.html b/httemplate/elements/select-cust_main-status.html
new file mode 100644
index 0000000..2e0b6cb
--- /dev/null
+++ b/httemplate/elements/select-cust_main-status.html
@@ -0,0 +1,30 @@
+<SELECT NAME="<% $opt{'field'} || 'status' %>"
+ <% $opt{'multiple'} ? 'MULTIPLE' : '' %>
+ <% $onchange %>
+>
+
+ <OPTION VALUE="">all
+
+% foreach my $option ( @{ $opt{'statuses'} } ) {
+
+ <OPTION VALUE="<% $option %>"
+ <% $option eq $curr_value ? 'SELECTED' : '' %>
+ ><% $option %>
+
+% }
+
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+
+$opt{'statuses'} ||= [ FS::cust_main->statuses() ]; # { disabled=>'' } )
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+</%init>
diff --git a/httemplate/elements/select-cust_pkg-status.html b/httemplate/elements/select-cust_pkg-status.html
new file mode 100644
index 0000000..2d545c0
--- /dev/null
+++ b/httemplate/elements/select-cust_pkg-status.html
@@ -0,0 +1,30 @@
+<SELECT NAME="<% $opt{'field'} || 'status' %>"
+ <% $opt{'multiple'} ? 'MULTIPLE' : '' %>
+ <% $onchange %>
+>
+
+ <OPTION VALUE="">all
+
+% foreach my $option ( @{ $opt{'statuses'} } ) {
+
+ <OPTION VALUE="<% $option %>"
+ <% $option eq $curr_value ? 'SELECTED' : '' %>
+ ><% $option %>
+
+% }
+
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+
+$opt{'statuses'} ||= [ FS::cust_pkg->statuses() ]; # { disabled=>'' } )
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+</%init>
diff --git a/httemplate/elements/select-did.html b/httemplate/elements/select-did.html
new file mode 100644
index 0000000..0695164
--- /dev/null
+++ b/httemplate/elements/select-did.html
@@ -0,0 +1,84 @@
+<%doc>
+
+Example:
+
+ include('/elements/select-did.html',
+ 'field' => 'phonenum',
+
+ 'svcpart' => 5,
+ #OR
+ 'object' => $svc_phone,
+ );
+
+</%doc>
+% if ( $use_selector ) {
+
+ <TABLE>
+
+ <TR>
+ <TD>
+ <% include('/elements/select-state.html',
+ 'country' => $country,
+ 'disable_empty' => 0,
+ 'empty_label' => 'Select state',
+ )
+ %>
+ </TD>
+ <TD>
+ <% include('/elements/select-areacode.html',
+ 'svcpart' => $svcpart,
+ 'empty' => 'Select area code',
+ )
+ %>
+ </TD>
+ <TD>
+ <% include('/elements/select-exchange.html',
+ 'svcpart' => $svcpart,
+ 'empty' => 'Select exchange',
+ )
+ %>
+ </TD>
+ <TD>
+ <% include('/elements/select-phonenum.html',
+ 'svcpart' => $svcpart,
+ 'empty' => 'Select phone number',
+ )
+ %>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD><FONT SIZE="-1">State</FONT></TD>
+ <TD><FONT SIZE="-1">Area code</FONT></TD>
+ <TD><FONT SIZE="-1">City / Exchange</FONT></TD>
+ <TD><FONT SIZE="-1">Phone number</FONT></TD>
+ </TR>
+
+ </TABLE>
+
+% } else {
+
+ <% include( '/elements/input-text.html', %opt, 'type'=>'text' ) %>
+
+% }
+<%init>
+
+my %opt = @_;
+
+my $conf = new FS::Conf;
+my $country = $conf->config('countrydefault') || 'US';
+
+#XXX make sure this comes through on errors too
+my $svcpart = $opt{'svcpart'} || $opt{'object'}->svcpart;
+
+my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
+die "unknown svcpart $svcpart" unless $part_svc;
+
+my @exports = $part_svc->part_export_did;
+if ( scalar(@exports) > 1 ) {
+ die "more than one DID-providing export attached to svcpart $svcpart";
+}
+
+my $use_selector = scalar(@exports) ? 1 : 0;
+
+</%init>
diff --git a/httemplate/elements/select-domain.html b/httemplate/elements/select-domain.html
new file mode 100644
index 0000000..a9998da
--- /dev/null
+++ b/httemplate/elements/select-domain.html
@@ -0,0 +1,13 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'svc_domain',
+ 'name_col' => 'domain',
+ 'empty_label' => 'all',
+ 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ #' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ',
+ 'agent_virt' => 1,
+ 'agent_null-right' => 'View/link unlinked services',
+ @_,
+ )
+%>
diff --git a/httemplate/elements/select-exchange.html b/httemplate/elements/select-exchange.html
new file mode 100644
index 0000000..012e7c6
--- /dev/null
+++ b/httemplate/elements/select-exchange.html
@@ -0,0 +1,86 @@
+<% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/exchanges.cgi',
+ 'subs' => [ $opt{'prefix'}. 'get_exchanges' ],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function <% $opt{'prefix'} %>areacode_changed(what, callback) {
+
+ what.form.<% $opt{'prefix'} %>exchange.disabled = 'disabled';
+ what.form.<% $opt{'prefix'} %>exchange.style.display = 'none';
+ var exchangewait = document.getElementById('<% $opt{'prefix'} %>exchangewait');
+ exchangewait.style.display = '';
+ var exchangeerror = document.getElementById('<% $opt{'prefix'} %>exchangeerror');
+ exchangeerror.style.display = 'none';
+
+ what.form.<% $opt{'prefix'} %>phonenum.disabled = 'disabled';
+
+ areacode = what.options[what.selectedIndex].value;
+
+ function <% $opt{'prefix'} %>update_exchanges(exchanges) {
+
+ // blank the current exchange
+ for ( var i = what.form.<% $opt{'prefix'} %>exchange.length; i >= 0; i-- )
+ what.form.<% $opt{'prefix'} %>exchange.options[i] = null;
+ // blank the current phonenum too
+ for ( var i = what.form.<% $opt{'prefix'} %>phonenum.length; i >= 0; i-- )
+ what.form.<% $opt{'prefix'} %>phonenum.options[i] = null;
+ opt(what.form.<% $opt{'prefix'} %>phonenum, '', 'Select phone number');
+
+% if ($opt{empty}) {
+ opt(what.form.<% $opt{'prefix'} %>exchange, '', '<% $opt{empty} %>');
+% }
+
+ // add the new exchanges
+ var exchangeArray = eval('(' + exchanges + ')' );
+ for ( var s = 0; s < exchangeArray.length; s++ ) {
+ var exchangeLabel = exchangeArray[s];
+ if ( exchangeLabel == "" )
+ exchangeLabel = '(n/a)';
+ opt(what.form.<% $opt{'prefix'} %>exchange, exchangeArray[s], exchangeLabel);
+ }
+
+ exchangewait.style.display = 'none';
+ if ( exchangeArray.length >= 1 ) {
+ what.form.<% $opt{'prefix'} %>exchange.disabled = '';
+ what.form.<% $opt{'prefix'} %>exchange.style.display = '';
+ } else {
+ var exchangeerror = document.getElementById('<% $opt{'prefix'} %>exchangeerror');
+ exchangeerror.style.display = '';
+ }
+
+ //run the callback
+ if ( callback != null )
+ callback();
+ }
+
+ // go get the new exchanges
+ <% $opt{'prefix'} %>get_exchanges( areacode, <% $opt{'svcpart'} %>, <% $opt{'prefix'} %>update_exchanges );
+
+ }
+
+</SCRIPT>
+
+<DIV ID="exchangewait" STYLE="display:none"><IMG SRC="<%$fsurl%>images/wait-orange.gif"> <B>Finding cities / exchanges</B></DIV>
+
+<DIV ID="exchangeerror" STYLE="display:none"><IMG SRC="<%$fsurl%>images/cross.png"> <B>Select a different area code</B></DIV>
+
+<SELECT NAME="<% $opt{'prefix'} %>exchange" onChange="<% $opt{'prefix'} %>exchange_changed(this); <% $opt{'onchange'} %>" <% $opt{'disabled'} %>>
+ <OPTION VALUE="">Select city / exchange</OPTION>
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+
+$opt{disabled} = 'disabled' unless exists $opt{disabled};
+
+</%init>
diff --git a/httemplate/elements/select-month_year.html b/httemplate/elements/select-month_year.html
new file mode 100644
index 0000000..34476bc
--- /dev/null
+++ b/httemplate/elements/select-month_year.html
@@ -0,0 +1,62 @@
+%
+%
+% my %opt = @_;
+%
+% my $prefix = $opt{'prefix'} || '';
+% my $disabled = $opt{'disabled'} || '';
+% my $empty = $opt{'empty_option'} || '';
+% my $start_year = $opt{'start_year'};
+% my $end_year = $opt{'end_year'} || '2037';
+%
+% my @mon;
+% if ( $opt{'show_month_abbr'} ) {
+% @mon = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+% } else {
+% @mon = ( 1 .. 12 );
+% }
+%
+% my $date = $opt{'selected_date'} || '';
+% $date = '' if $date eq '-';
+% #$date ||= '01-2000' unless $empty;
+%
+% my $mon = $opt{'selected_mon'} || 0;
+% my $year = $opt{'selected_year'} || 0;
+% if ( $date ) {
+% if ( $date =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
+% ( $mon, $year ) = ( $2, $1 );
+% } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+% ( $mon, $year ) = ( $1, $3 );
+% } else {
+% die "unrecognized expiration date format: $date";
+% }
+% }
+%
+% unless ( $start_year ) {
+% my @t = localtime;
+% $start_year = $t[5] + 1900;
+% }
+% $start_year = $year if $start_year > $year && $year > 0;
+%
+%
+
+
+<SELECT NAME="<% $prefix %>_month" SIZE="1" <% $disabled%>>
+
+<% $empty ? '<OPTION VALUE="">' : '' %>
+% foreach ( 1 .. 12 ) {
+
+ <OPTION<% $_ == $mon ? ' SELECTED' : '' %> VALUE="<% $_ %>"><% $mon[$_-1] %>
+% }
+
+
+</SELECT>/<SELECT NAME="<% $prefix %>_year" SIZE="1" <% $disabled%>>
+
+<% $empty ? '<OPTION VALUE="">' : '' %>
+% for ( $start_year .. $end_year ) {
+
+ <OPTION<% $_ == $year ? ' SELECTED' : '' %> VALUE="<% $_ %>"><% $_ %>
+% }
+
+
+</SELECT>
+
diff --git a/httemplate/elements/select-otaker.html b/httemplate/elements/select-otaker.html
new file mode 100644
index 0000000..2a689f3
--- /dev/null
+++ b/httemplate/elements/select-otaker.html
@@ -0,0 +1,27 @@
+<SELECT NAME="otaker">
+
+% unless ( $opt{'multiple'} || $opt{'disable_empty'} ) {
+ <OPTION VALUE="">all</OPTION>
+% }
+
+% foreach my $otaker ( @{ $opt{'otakers'} } ) {
+ <OPTION VALUE="<% $otaker %>"><% $otaker %></OPTION>
+% }
+
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+
+unless ( $opt{'otakers'} ) {
+
+ my $sth = dbh->prepare("SELECT username FROM access_user".
+ " WHERE disabled = '' or disabled IS NULL")
+ or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ $opt{'otakers'} = [ map { $_->[0] } @{$sth->fetchall_arrayref} ];
+
+}
+
+</%init>
diff --git a/httemplate/elements/select-part_pkg.html b/httemplate/elements/select-part_pkg.html
new file mode 100644
index 0000000..52b1cca
--- /dev/null
+++ b/httemplate/elements/select-part_pkg.html
@@ -0,0 +1,38 @@
+<%doc>
+
+Example:
+
+ include( '/elements/select-part_pkg.html',
+
+ #strongly recommended (you want your forms to be "sticky" on errors, right?)
+ 'curr_value' => 'current_value',
+
+ #opt
+ 'part_pkg' => \@records,
+
+ #select-table.html options
+ )
+
+</%doc>
+
+<% include( '/elements/select-table.html',
+ 'table' => 'part_pkg',
+ 'agent_virt' => 1,
+ 'agent_null' => 1,
+ 'name_col' => 'pkg',
+ 'empty_label' => 'Select package', #should this be the default?
+ 'label_callback' => sub { shift->pkg_comment },
+ 'hashref' => { 'disabled' => '' },
+ %opt,
+ )
+%>
+<%init>
+
+my( %opt ) = @_;
+
+$opt{'records'} = delete $opt{'part_pkg'}
+ if $opt{'part_pkg'};
+
+$opt{'extra_sql'} .= ' AND '. FS::part_pkg->curuser_pkgs_sql;
+
+</%init>
diff --git a/httemplate/elements/select-part_referral.html b/httemplate/elements/select-part_referral.html
new file mode 100644
index 0000000..c4b8829
--- /dev/null
+++ b/httemplate/elements/select-part_referral.html
@@ -0,0 +1,20 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'part_referral',
+ 'name_col' => 'referral',
+ 'value' => $refnum,
+ 'empty_label' => 'Select advertising source',
+ 'hashref' => { 'disabled' => '' },
+ 'extra_sql' => ' AND '.
+ FS::part_referral->acl_agentnum_sql(1),
+ %opt,
+ )
+%>
+<%init>
+
+my %opt = @_;
+my $refnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'records'} = delete $opt{'part_referrals'}
+ if $opt{'part_referrals'};
+
+</%init>
diff --git a/httemplate/elements/select-payby.html b/httemplate/elements/select-payby.html
new file mode 100644
index 0000000..3f19cb9
--- /dev/null
+++ b/httemplate/elements/select-payby.html
@@ -0,0 +1,40 @@
+<SELECT NAME="<% $opt{'field'} || 'payby' %>"
+ <% $opt{'multiple'} ? 'MULTIPLE' : '' %>
+ <% $onchange %>
+>
+
+% unless ( $opt{'multiple'} ) {
+ <OPTION VALUE="" <% '' eq $value ? 'SELECTED' : '' %> >all
+% }
+
+% foreach my $option ( keys %{ $opt{'paybys'} } ) {
+% my $sel = ( ref($value) && $value->{$option} ) || $option eq $value;
+
+ <OPTION VALUE="<% $option %>"
+ <% $sel ? 'SELECTED' : '' %>
+ ><% $opt{'paybys'}->{$option} %>
+
+% }
+
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+
+my $method = 'payby';
+$method = 'cust_payby' if $opt{'payby_type'} eq 'cust';
+#$method = 'event_payby' if $opt{'payby_type'} eq 'event';
+#$method = 'pay_payby' if $opt{'payby_type'} eq 'pay';
+
+unless ( $opt{'paybys'} ) {
+ tie %{ $opt{'paybys'} }, 'Tie::IxHash', FS::payby->$method();
+}
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+</%init>
diff --git a/httemplate/elements/select-phonenum.html b/httemplate/elements/select-phonenum.html
new file mode 100644
index 0000000..b98d140
--- /dev/null
+++ b/httemplate/elements/select-phonenum.html
@@ -0,0 +1,84 @@
+<% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/phonenums.cgi',
+ 'subs' => [ $opt{'prefix'}. 'get_phonenums' ],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function <% $opt{'prefix'} %>exchange_changed(what, callback) {
+
+ what.form.<% $opt{'prefix'} %>phonenum.disabled = 'disabled';
+ what.form.<% $opt{'prefix'} %>phonenum.style.display = 'none';
+ var phonenumwait = document.getElementById('<% $opt{'prefix'} %>phonenumwait');
+ phonenumwait.style.display = '';
+ var phonenumerror = document.getElementById('<% $opt{'prefix'} %>phonenumerror');
+ phonenumerror.style.display = 'none';
+
+ exchange = what.options[what.selectedIndex].value;
+
+ function <% $opt{'prefix'} %>update_phonenums(phonenums) {
+
+ // blank the current phonenum
+ for ( var i = what.form.<% $opt{'prefix'} %>phonenum.length; i >= 0; i-- )
+ what.form.<% $opt{'prefix'} %>phonenum.options[i] = null;
+
+% if ($opt{empty}) {
+ opt(what.form.<% $opt{'prefix'} %>phonenum, '', '<% $opt{empty} %>');
+% }
+
+ // add the new phonenums
+ var phonenumArray = eval('(' + phonenums + ')' );
+ for ( var s = 0; s < phonenumArray.length; s++ ) {
+ var phonenumLabel = phonenumArray[s];
+ if ( phonenumLabel == "" )
+ phonenumLabel = '(n/a)';
+ opt(what.form.<% $opt{'prefix'} %>phonenum, phonenumArray[s], phonenumLabel);
+ }
+
+ //var phonenumFormLabel = document.getElementById('<% $opt{'prefix'} %>phonenumlabel');
+
+ what.form.<% $opt{'prefix'} %>phonenum.disabled = '';
+
+ phonenumwait.style.display = 'none';
+ if ( phonenumArray.length >= 1 ) {
+ what.form.<% $opt{'prefix'} %>phonenum.disabled = '';
+ what.form.<% $opt{'prefix'} %>phonenum.style.display = '';
+ } else {
+ var phonenumerror = document.getElementById('<% $opt{'prefix'} %>phonenumerror');
+ phonenumerror.style.display = '';
+ }
+
+ //run the callback
+ if ( callback != null )
+ callback();
+ }
+
+ // go get the new phonenums
+ <% $opt{'prefix'} %>get_phonenums( exchange, <% $opt{'svcpart'} %>, <% $opt{'prefix'} %>update_phonenums );
+
+ }
+
+</SCRIPT>
+
+<DIV ID="phonenumwait" STYLE="display:none"><IMG SRC="<%$fsurl%>images/wait-orange.gif"> <B>Finding phone numbers</B></DIV>
+
+<DIV ID="phonenumerror" STYLE="display:none"><IMG SRC="<%$fsurl%>images/cross.png"> <B>Select a different city/exchange</B></DIV>
+
+<SELECT NAME="<% $opt{'prefix'} %>phonenum" notonChange="<% $opt{'prefix'} %>phonenum_changed(this); <% $opt{'onchange'} %>" <% $opt{'disabled'} %>>
+ <OPTION VALUE="">Select phone number</OPTION>
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+
+$opt{disabled} = 'disabled' unless exists $opt{disabled};
+
+</%init>
diff --git a/httemplate/elements/select-pkg_class.html b/httemplate/elements/select-pkg_class.html
new file mode 100644
index 0000000..f30259e
--- /dev/null
+++ b/httemplate/elements/select-pkg_class.html
@@ -0,0 +1,18 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'pkg_class',
+ 'name_col' => 'classname',
+ 'value' => $classnum,
+ 'empty_label' => '(none)',
+ 'hashref' => { 'disabled' => '' },
+ %opt,
+ )
+%>
+<%init>
+
+my %opt = @_;
+my $classnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'records'} = delete $opt{'pkg_class'}
+ if $opt{'pkg_class'};
+
+</%init>
diff --git a/httemplate/elements/select-rate.html b/httemplate/elements/select-rate.html
new file mode 100644
index 0000000..83a7add
--- /dev/null
+++ b/httemplate/elements/select-rate.html
@@ -0,0 +1,9 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'rate',
+ 'name_col' => 'ratename',
+ 'empty_label' => 'Select rate plan',
+ #'hashref' => { 'disabled' => '' },
+ 'order_by' => ' ORDER BY ratenum', #ratename ?
+ @_,
+ )
+%>
diff --git a/httemplate/elements/select-state.html b/httemplate/elements/select-state.html
new file mode 100644
index 0000000..9b358e2
--- /dev/null
+++ b/httemplate/elements/select-state.html
@@ -0,0 +1,66 @@
+<%doc>
+
+Example:
+
+ include( '/elements/select-state.html',
+ #recommended
+ country => $current_country,
+ state => $current_state,
+
+ #optional
+ prefix => $optional_unique_prefix,
+ onchange => $javascript,
+ disabled => 0, #bool
+ disable_empty => 1, #defaults to 1, disable the empty option
+ empty_label => 'all', #label for empty option
+ disable_countyupdate => 0, #bool - disabled update of the select-state.html
+ style => [ 'attribute:value', 'another:value' ],
+ );
+
+</%doc>
+
+<SELECT NAME = "<% $pre %>state"
+ ID = "<% $pre %>state"
+ onChange = "<% $onchange %>"
+ <% $opt{'disabled'} %>
+ <% $style %>
+>
+
+% unless ( $opt{'disable_empty'} ) {
+ <OPTION VALUE=""<% $opt{state} eq '' ? ' SELECTED' : '' %>><% $opt{empty_label} %>
+% }
+
+% foreach my $state ( keys %states ) {
+
+ <OPTION VALUE="<% $state |h %>"<% $state eq $opt{'state'} ? ' SELECTED' : '' %>><% $states{$state} || '(n/a)' %>
+
+% }
+
+
+</SELECT>
+
+<%init>
+
+my %opt = @_;
+foreach my $opt (qw( state country prefix onchange disabled empty_label )) {
+ $opt{$opt} = '' unless exists($opt{$opt}) && defined($opt{$opt});
+}
+
+$opt{'disable_empty'} = 1 unless exists($opt{'disable_empty'});
+
+my $pre = $opt{'prefix'};
+
+my $onchange =
+ ( $opt{'disable_countyupdate'} ? '' : $pre.'state_changed(this); ' ).
+ $opt{'onchange'};
+
+$opt{'style'} ||= [];
+my $style =
+ scalar(@{$opt{style}})
+ ? 'STYLE="'. join(';', @{$opt{style}}). '"'
+ : '';
+
+tie my %states, 'Tie::IxHash', states_hash( $opt{'country'} );
+
+</%init>
+
diff --git a/httemplate/elements/select-table.html b/httemplate/elements/select-table.html
new file mode 100644
index 0000000..4efbcba
--- /dev/null
+++ b/httemplate/elements/select-table.html
@@ -0,0 +1,164 @@
+<%doc>
+
+Example:
+
+ include( '/elements/select-table.html',
+
+ ##
+ # required
+ ##
+ 'table' => 'table_name',
+ 'name_col' => 'name_column',
+
+ #strongly recommended (you want your forms to be "sticky" on errors, right?)
+ 'curr_value' => 'current_value',
+ #'value' => #deprecated form of 'curr_value',
+
+ ##
+ # optional
+ ##
+
+ #search params
+ 'hashref' => {},
+ 'addl_from' => '',
+ 'extra_sql' => '',
+ 'agent_virt' => 0, #set true and make sure the result is JOINed to
+ #something with agentnum (usually cust_main)
+ 'agent_null' => 0, #set true to always show un-agented entries
+ 'agent_null_right' => '', #right to see un-agented entries
+ #or
+ 'records' => \@records, #instead of search params
+
+ #basic params controlling the resulting <SELECT>
+ 'pre_options' => [ 'value' => 'option' ], #before normal options
+ 'empty_label' => '', #better specify it though, the default might change
+ 'multiple' => 0, # bool
+ 'disable_empty' => 0, # bool (implied by multiple)
+ 'label_callback' => sub { my $record = shift; return "label"; },
+
+ #more params controlling HTML stuff about the <SELECT>
+ 'element_name' => '', #HTML element name, defaults to the name of
+ # the primary key column
+ 'field' => '', #synonym for element_name
+ 'element_etc' => '', #additional attributes (i.e. "DISABLED") for the
+ #<SELECT> element
+ 'onchange' => '', #javascript code
+
+ #special return options
+ 'js_only' => 0, #set true to return only the JS portions (i.e. nothing)
+ 'html_only' => 0, #set true to return only the HTML portions (no-op, i.e. return everything)
+
+ #debugging
+ 'debug' => 0, #set true to enable
+
+ )
+
+</%doc>
+% unless ( $opt{'js_only'} ) {
+
+<SELECT <% $opt{'multiple'} ? 'MULTIPLE' : '' %>
+ NAME = "<% $opt{'element_name'} || $opt{'field'} || $key %>"
+ ID = "<% $opt{'id'} || $key %>"
+ <% $onchange %>
+ <% $opt{'element_etc'} %>
+>
+
+% while ( @pre_options ) {
+ <OPTION VALUE="<% shift(@pre_options) %>"><% shift(@pre_options) %>
+
+% }
+
+% unless ( $opt{'multiple'} || $opt{'disable_empty'} ) {
+ <OPTION VALUE=""><% $opt{'empty_label'} || 'all' %>
+% }
+
+% foreach my $record ( sort { $a->$name_col() cmp $b->$name_col() } @records ) {
+% my $recvalue = $record->$key();
+ <OPTION VALUE="<% $recvalue %>"
+ <% ref($value) && $value->{$recvalue} || $value == $recvalue
+ ? ' SELECTED' : ''
+ %>
+ ><% $opt{'label_callback'}
+ ? &{ $opt{'label_callback'} }( $record )
+ : $record->$name_col()
+ %>
+% }
+
+</SELECT>
+
+%}
+<%init>
+
+my( %opt ) = @_;
+
+warn "elements/select-table.html: \n". Dumper(%opt)
+ if exists $opt{debug} && $opt{debug};
+
+my $onchange = '';
+if ( $opt{'onchange'} ) {
+ $onchange = $opt{'onchange'};
+ $onchange .= '(this)' unless $onchange =~ /\(\w*\);?$/;
+ $onchange =~ s/\(what\);/\(this\);/g; #ugh, terrible hack. all onchange
+ #callbacks should act the same
+ $onchange = 'onChange="'. $onchange. '"';
+}
+
+my $dbdef_table = dbdef->table($opt{'table'})
+ or die "can't find dbdef for ". $opt{'table'}. " table\n";
+
+my $key = $dbdef_table->primary_key; #? $opt{'primary_key'} ||
+
+my $name_col = $opt{'name_col'};
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+$value = [ split(/\s*,\s*/, $value) ] if $opt{'multiple'} && $value =~ /,/;
+
+#my $addl_from = $opt{'addl_from'} || '';
+my $extra_sql = $opt{'extra_sql'} || '';
+my $hashref = $opt{'hashref'} || {};
+
+if ( $opt{'agent_virt'} ) {
+ $extra_sql .=
+ ( $extra_sql =~ /WHERE/i || scalar(keys %$hashref ) ? ' AND ' : ' WHERE ' ).
+ $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null' => $opt{'agent_null'},
+ 'null_right' => $opt{'agent_null_right'},
+ );
+}
+
+my @records = ();
+if ( $opt{'records'} ) {
+ @records = @{ $opt{'records'} };
+} else {
+ @records = qsearch( {
+ 'table' => $opt{'table'},
+ 'addl_from' => $opt{'addl_from'},
+ 'hashref' => $hashref,
+ 'extra_sql' => $extra_sql,
+ 'order_by' => ( $opt{'order_by'} || "ORDER BY $name_col" ),
+ });
+}
+
+unless ( ! $value
+ or ref($value)
+ or ! exists( $opt{hashref}->{disabled} ) #??
+ or grep { $value == $_->$key() } @records
+ ) {
+ delete $opt{hashref}->{disabled};
+ $opt{hashref}->{$key} = $value;
+ my $record = qsearchs( {
+ 'table' => $opt{table},
+ 'addl_from' => $opt{'addl_from'},
+ 'hashref' => $hashref,
+ 'extra_sql' => $extra_sql,
+ });
+ push @records, $record if $record;
+}
+
+if ( ref( $value ) eq 'ARRAY' ) {
+ $value = { map { $_ => 1 } @$value };
+}
+
+my @pre_options = $opt{pre_options} ? @{ $opt{pre_options} } : ();
+
+</%init>
diff --git a/httemplate/elements/select-taxclass.html b/httemplate/elements/select-taxclass.html
new file mode 100644
index 0000000..2504a5b
--- /dev/null
+++ b/httemplate/elements/select-taxclass.html
@@ -0,0 +1,41 @@
+% if ( $conf->exists('enable_taxclasses') ) {
+
+ <SELECT NAME="<% $opt{'name'} || 'taxclass' %>">
+
+% if ( $conf->exists('require_taxclasses') ) {
+ <OPTION VALUE="(select)">Select tax class
+% } else {
+ <OPTION VALUE="">
+% }
+
+% foreach my $taxclass ( @{ $opt{'taxclasses'} } ) {
+ <OPTION VALUE="<% $taxclass %>"<% $taxclass eq $selected_taxclass ? ' SELECTED' : '' %>><% $taxclass %>
+% }
+
+ </SELECT>
+
+% } else {
+
+ <INPUT TYPE="hidden" NAME="taxclass" VALUE="<% $selected_taxclass %>">
+
+% }
+
+<%init>
+
+my %opt = @_;
+my $selected_taxclass = $opt{'curr_value'}; # || $opt{'value'} necessary?
+
+my $conf = new FS::Conf;
+
+unless ( $opt{'taxclasses'} ) {
+
+ #my $sth = dbh->prepare('SELECT DISTINCT taxclass FROM cust_main_county')
+ my $sth = dbh->prepare('SELECT taxclass FROM part_pkg_taxclass')
+ or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my %taxclasses = map { $_->[0] => 1 } @{$sth->fetchall_arrayref};
+ @{ $opt{'taxclasses'} } = grep $_, keys %taxclasses;
+
+}
+
+</%init>
diff --git a/httemplate/elements/select-taxoverride.html b/httemplate/elements/select-taxoverride.html
new file mode 100644
index 0000000..8b1c528
--- /dev/null
+++ b/httemplate/elements/select-taxoverride.html
@@ -0,0 +1,28 @@
+ <INPUT NAME = "<% $name %>"
+ ID = "<% $name %>"
+ TYPE = "hidden"
+ VALUE = "<% $value %>"
+ >
+ <A href="javascript:void(0)" onclick="<% $onclick %>">
+ <% $value ? "Edit $class tax overrides" : "Override $class taxes" %>
+ </A>
+<%init>
+
+my %opt = @_;
+my $name = $opt{element_name} || $opt{field} || 'tax_override';
+my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value};
+
+my %usage_class = map { ($_->classnum => $_->classname) }
+ qsearch('usage_class', {});
+$usage_class{setup} = 'Setup';
+$usage_class{recur} = 'Recurring';
+
+my $usage;
+$name =~ /^tax_override_(\w+)$/ && ( $usage = $1 );
+
+my $class = lc($usage_class{$usage} || "Usage class $usage")
+ if $usage;
+
+my $onclick = $opt{onclick} || "overlib( OLiframeContent('part_pkg_taxoverride.html?element_name=$name;selected='+document.getElementById('$name').value, 1100, 600, 'tax_product_popup'), CAPTION, 'Edit $class product tax overrides', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK); return false;";
+
+</%init>
diff --git a/httemplate/elements/select-taxproduct.html b/httemplate/elements/select-taxproduct.html
new file mode 100644
index 0000000..0f6ef55
--- /dev/null
+++ b/httemplate/elements/select-taxproduct.html
@@ -0,0 +1,28 @@
+<% $opt{'prefix'} %><INPUT NAME = "<% $name %>"
+ ID = "<% $name %>"
+ TYPE = "hidden"
+ VALUE = "<% $value |h %>"
+ >
+ <INPUT NAME = "<% $name %>_description"
+ ID = "<% $name %>_description"
+ TYPE = "text"
+ VALUE = "<% $description %>"
+ SIZE = "12"
+ onClick = "<% $onclick %>"><% $opt{'postfix'} %>
+<%init>
+
+my %opt = @_;
+my $name = $opt{element_name} || $opt{field} || 'taxproductnum';
+my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value};
+my $description = $opt{'taxproduct_description'};
+
+unless ( $description || ! $value ) {
+ my $part_pkg_taxproduct =
+ qsearchs( 'part_pkg_taxproduct', { 'taxproductnum'=> $value } );
+ $description = $part_pkg_taxproduct->description
+ if $part_pkg_taxproduct;
+}
+
+my $onclick = $opt{onclick} || "overlib( OLiframeContent('${p}/browse/part_pkg_taxproduct.cgi?_type=select&id=${name}&taxproductnum='+document.getElementById('${name}').value, 1000, 400, 'tax_product_popup'), CAPTION, 'Select product', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK); return false;";
+
+</%init>
diff --git a/httemplate/elements/selectlayers.html b/httemplate/elements/selectlayers.html
new file mode 100644
index 0000000..82f5dd1
--- /dev/null
+++ b/httemplate/elements/selectlayers.html
@@ -0,0 +1,216 @@
+<%doc>
+
+Example:
+
+ include( '/elements/selectlayers.html',
+ 'field' => $key, # SELECT element NAME (passed as form field)
+ # also used as ID and a unique key for layers and
+ # functions
+ 'curr_value' => $selected_layer,
+ 'options' => [ 'option1', 'option2' ],
+ 'labels' => { 'option1' => 'Option 1 Label',
+ 'option2' => 'Option 2 Label',
+ },
+
+ #XXX put this handling it its own selectlayers-fields.html element?
+ 'layer_prefix' => 'prefix_', #optional prefix for fieldnames
+ 'layer_fields' => [ 'layer' => [ 'fieldname',
+ { label => 'fieldname2',
+ type => 'text', #implemented:
+ # text, money, fixed,
+ # hidden, checkbox,
+ # checkbox-multiple,
+ # select, select-agent,
+ # select-pkg_class,
+ # select-part_referral,
+ # select-table,
+ #XXX tbd:
+ # more?
+ },
+ ...
+ ],
+ 'layer2' => [ 'l2fieldname',
+ ...
+ ],
+
+ #current values for layer fields above
+ 'layer_values' => { 'layer' => { 'fieldname' => 'current_value',
+ 'fieldname2' => 'field2value',
+ ...
+ },
+ 'layer2' => { 'l2fieldname' => 'l2value',
+ ...
+ },
+ ...
+ },
+
+ #or manual control, instead of layer_fields and layer_values above
+ #called with args: my( $layer, $layer_fields, $layer_values, $layer_prefix )
+ 'layer_callback' =>
+
+ 'html_between => '', #optional HTML displayed between the SELECT and the
+ #layers, scalar or coderef ('field' passed as a param)
+ 'onchange' => '', #javascript code run when the SELECT changes
+ # ("what" is the element)
+ 'js_only' => 0, #set true to return only the JS portions
+ 'html_only' => 0, #set true to return only the HTML portions
+ 'select_only' => 0, #set true to return only the <SELECT> HTML
+ 'layers_only' => 0, #set true to return only the layers <DIV> HTML
+ )
+
+</%doc>
+% unless ( grep $opt{$_}, qw(html_only js_only select_only layers_only) ) {
+ <SCRIPT TYPE="text/javascript">
+% }
+% unless ( grep $opt{$_}, qw(html_only select_only layers_only) ) {
+ //alert('start function define');
+ function <% $key %>changed(what) {
+
+ <% $opt{'onchange'} %>
+
+ var <% $key %>layer = what.options[what.selectedIndex].value;
+
+% foreach my $layer ( keys %$options ) {
+
+ if (<% $key %>layer == "<% $layer %>" ) {
+
+% foreach my $not ( grep { $_ ne $layer } keys %$options ) {
+% my $element = "document.getElementById('${key}d$not').style";
+ <% $element %>.display = "none";
+ <% $element %>.zIndex = 0;
+% }
+
+% my $element = "document.getElementById('${key}d$layer').style";
+ <% $element %>.display = "";
+ <% $element %>.zIndex = 1;
+
+ }
+% }
+
+ //<% $opt{'onchange'} %>
+
+ }
+ //alert('end function define');
+% }
+% unless ( grep $opt{$_}, qw(html_only js_only select_only layers_only) ) {
+ </SCRIPT>
+% }
+%
+% unless ( grep $opt{$_}, qw(js_only layers_only) ) {
+
+ <SELECT NAME = "<% $key %>"
+ ID = "<% $key %>"
+ previousValue = "<% $selected %>"
+ previousText = "<% $options{$selected} %>"
+ onChange="<% $key %>changed(this);"
+ >
+
+% foreach my $option ( keys %$options ) {
+
+ <OPTION VALUE="<% $option %>"
+ <% $option eq $selected ? ' SELECTED' : '' %>
+ ><% $options->{$option} %></OPTION>
+
+% }
+
+ </SELECT>
+
+% }
+% unless ( grep $opt{$_}, qw(js_only select_only layers_only) ) {
+
+<% ref($between) ? &{$between}($key) : $between %>
+
+% }
+%
+% unless ( grep $opt{$_}, qw(js_only select_only) ) {
+
+% foreach my $layer ( keys %$options ) {
+
+ <DIV ID="<% $key %>d<% $layer %>"
+ STYLE="<% $layer eq $selected
+ ? 'display: "" ; z-index: 1'
+ : 'display: none; z-index: 0'
+ %>"
+ >
+
+ <% &{$layer_callback}($layer, $layer_fields, $layer_values, $layer_prefix) %>
+
+ </DIV>
+
+% }
+
+% }
+<%once>
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%once>
+<%init>
+
+my %opt = @_;
+
+#use Data::Dumper;
+#warn Dumper(%opt);
+
+my $key = $opt{field}; # || 'generate_one' #?
+
+tie my %options, 'Tie::IxHash',
+ map { $_ => $opt{'labels'}->{$_} }
+ @{ $opt{'options'} }; #just arrayref for now
+
+my $between = exists($opt{html_between}) ? $opt{html_between} : '';
+my $options = \%options;
+
+my $selected = exists($opt{curr_value}) ? $opt{curr_value} : '';
+
+#XXX eek. also eek $layer_fields in the layer_callback() call...
+my $layer_fields = $opt{layer_fields};
+my $layer_values = $opt{layer_values};
+my $layer_prefix = $opt{layer_prefix};
+
+my $layer_callback = $opt{layer_callback} || \&layer_callback;
+
+sub layer_callback {
+ my( $layer, $layer_fields, $layer_values, $layer_prefix ) = @_;
+
+ return '' unless $layer && exists $layer_fields->{$layer};
+ tie my %fields, 'Tie::IxHash', @{ $layer_fields->{$layer} };
+
+ #XXX this should become an element itself... (false laziness w/edit.html)
+ # but at least all the elements inside are the shared mason elements now
+
+ return '' unless keys %fields;
+ my $html = "<TABLE>";
+
+ foreach my $field ( keys %fields ) {
+
+ my $lf = ref($fields{$field})
+ ? $fields{$field}
+ : { 'label'=>$fields{$field} };
+
+ my $value = $layer_values->{$layer}{$field};
+
+ my $type = $lf->{type} || 'text';
+
+ my $include = $type;
+ $include = "input-$include" if $include =~ /^(text|money)$/;
+ $include = "tr-$include" unless $include eq 'hidden';
+
+ $html .= include( "/elements/$include.html",
+ %$lf,
+ 'field' => "$layer_prefix$field",
+ 'id' => "$layer_prefix$field", #separate?
+ #don't want field0_label0...?
+ 'label_id' => $layer_prefix.$field."_label",
+
+ 'value' => ( $lf->{'value'} || $value ), #hmm.
+ 'curr_value' => $value,
+ );
+
+ }
+ $html .= '</TABLE>';
+ return $html;
+}
+
+</%init>
diff --git a/httemplate/elements/small_custview.html b/httemplate/elements/small_custview.html
new file mode 100644
index 0000000..9060d89
--- /dev/null
+++ b/httemplate/elements/small_custview.html
@@ -0,0 +1,3 @@
+% my $conf = new FS::Conf;
+
+<% small_custview( shift, shift || scalar($conf->config('countrydefault')), @_ ) %>
diff --git a/httemplate/elements/table-grid.html b/httemplate/elements/table-grid.html
new file mode 100644
index 0000000..e1e6c36
--- /dev/null
+++ b/httemplate/elements/table-grid.html
@@ -0,0 +1,25 @@
+<STYLE TYPE="text/css">
+
+.grid table { border: solid; empty-cells: show }
+.grid TH { padding-left: 3px; padding-right: 3px; border: 1px solid #dddddd; border-bottom: dashed 1px black; border-right: none }
+.grid TD { padding-left: 3px; padding-right: 3px; empty-cells: show; border: 1px solid #cccccc; border-bottom: none; border-right: none }
+
+.inv table { border: none }
+.inv TH { border: none }
+.inv TD { border: none }
+
+</STYLE>
+
+<TABLE CLASS="grid" CELLSPACING=<% $opt{cellspacing} %> CELLPADDING=<% $opt{cellpadding} %> BORDER=1 BORDERCOLOR="#000000" <% $opt{bgcolor} %> STYLE="border: solid 1px black; empty-cells: show">
+
+<%init>
+
+my %opt = @_;
+$opt{cellspacing} ||= 0;
+$opt{cellpadding} ||= 0;
+
+$opt{bgcolor} =~ s/^#//;
+$opt{bgcolor} = 'BGCOLOR="#'. $opt{bgcolor}. '"' if length($opt{bgcolor});
+
+</%init>
+
diff --git a/httemplate/elements/table.html b/httemplate/elements/table.html
new file mode 100644
index 0000000..8152b65
--- /dev/null
+++ b/httemplate/elements/table.html
@@ -0,0 +1,11 @@
+%
+% my $color = shift;
+% if ( $color ) {
+%
+
+ <TABLE BGCOLOR="<% $color %>" BORDER=1 WIDTH="100%" CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">
+% } else {
+
+ <TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">
+% }
+
diff --git a/httemplate/elements/tablebreak-tr-title.html b/httemplate/elements/tablebreak-tr-title.html
new file mode 100644
index 0000000..ee28312
--- /dev/null
+++ b/httemplate/elements/tablebreak-tr-title.html
@@ -0,0 +1,14 @@
+</TABLE>
+
+<TABLE <% $id %> BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+
+<% include('tr-title.html', @_ ) %>
+
+<%init>
+
+my %opt = @_;
+
+my $id = '';
+$id = 'ID="'. $opt{'table_id'}. '"' if $opt{'table_id'};
+
+</%init>
diff --git a/httemplate/elements/tr-checkbox-multiple.html b/httemplate/elements/tr-checkbox-multiple.html
new file mode 100644
index 0000000..bb90a82
--- /dev/null
+++ b/httemplate/elements/tr-checkbox-multiple.html
@@ -0,0 +1,40 @@
+<% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+
+% foreach my $option ( @{ $opt{options} } ) { #just arrayref for now
+
+ <INPUT TYPE = "checkbox"
+ NAME = "<% $opt{field} %>"
+ ID = "<% $opt{id}.'_'.$option %>"
+ VALUE = "<% $option %>"
+ <% ref($value) && $value->{$option} || $value eq $option
+ ? ' CHECKED' : ''
+ %>
+ <% $onchange %>
+
+ >&nbsp;<% $labels->{$option} %>
+
+ <BR>
+
+% }
+
+ </TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+my $labels = $opt{'option_labels'} || $opt{'labels'};
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-checkbox.html b/httemplate/elements/tr-checkbox.html
new file mode 100644
index 0000000..2e6d1f1
--- /dev/null
+++ b/httemplate/elements/tr-checkbox.html
@@ -0,0 +1,25 @@
+<% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+ <INPUT TYPE = "checkbox"
+ NAME = "<% $opt{field} %>"
+ ID = "<% $opt{id} %>"
+ VALUE = "<% $opt{value} %>"
+ <% $opt{curr_value} eq $opt{value} ? ' CHECKED' : '' %>
+ <% $onchange %>
+ >
+ </TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-checkboxes-table.html b/httemplate/elements/tr-checkboxes-table.html
new file mode 100644
index 0000000..0099427
--- /dev/null
+++ b/httemplate/elements/tr-checkboxes-table.html
@@ -0,0 +1,20 @@
+% unless ( $opt{'js_only'} ) {
+
+ <% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+% }
+
+ <% include( '/elements/checkboxes-table.html', %opt ) %>
+
+% unless ( $opt{'js_only'} ) {
+ </TD>
+ </TR>
+% }
+<%init>
+
+my( %opt ) = @_;
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-fixed-country.html b/httemplate/elements/tr-fixed-country.html
new file mode 100644
index 0000000..806d92c
--- /dev/null
+++ b/httemplate/elements/tr-fixed-country.html
@@ -0,0 +1,10 @@
+<% include('tr-fixed.html', %opt ) %>
+<%init>
+
+my %opt = @_;
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'formatted_value'} = code2country($value). " ($value)";
+
+</%init>
diff --git a/httemplate/elements/tr-fixed-state.html b/httemplate/elements/tr-fixed-state.html
new file mode 100644
index 0000000..eea30dd
--- /dev/null
+++ b/httemplate/elements/tr-fixed-state.html
@@ -0,0 +1,10 @@
+<% include('tr-fixed.html', %opt ) %>
+<%init>
+
+my %opt = @_;
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'formatted_value'} = state_label($value, $opt{'object'}->country);
+
+</%init>
diff --git a/httemplate/elements/tr-fixed.html b/httemplate/elements/tr-fixed.html
new file mode 100644
index 0000000..095e1bc
--- /dev/null
+++ b/httemplate/elements/tr-fixed.html
@@ -0,0 +1,15 @@
+<% include('tr-td-label.html', @_ ) %>
+
+ <TD BGCOLOR="#dddddd" <% $style %>><% $opt{'formatted_value'} || $opt{'curr_value'} || $opt{'value'} |h %></TD>
+
+</TR>
+
+<% include('hidden.html', %opt ) %>
+
+<%init>
+
+my %opt = @_;
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-freq.html b/httemplate/elements/tr-freq.html
new file mode 100644
index 0000000..cb58bf6
--- /dev/null
+++ b/httemplate/elements/tr-freq.html
@@ -0,0 +1,54 @@
+<% include('tr-td-label.html', @_) %>
+
+ <TD <% $style %>>
+
+ <INPUT TYPE = "text"
+ SIZE = "<% $opt{'size'} || 4 %>"
+ NAME = "<% $opt{'field'} || 'freq' %>"
+ ID = "<% $opt{'id'} %>"
+ VALUE = "<% $curr_value %>"
+ >
+
+ <SELECT NAME = "<% $opt{'field'} || 'freq' %>_units">
+% foreach my $freq ( keys %freq ) {
+ <OPTION VALUE="<% $freq %>"
+ <% $freq eq $units ? 'SELECTED' : '' %>
+ ><% $freq{$freq} %>
+% }
+ </SELECT>
+
+ </TD>
+
+</TR>
+
+<%once>
+
+ tie my %freq, 'Tie::IxHash',
+ #'y' => 'years',
+ 'm' => 'months',
+ 'w' => 'weeks',
+ 'd' => 'days',
+ 'h' => 'hours',
+ ;
+
+</%once>
+<%init>
+
+my %opt = @_;
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+my $units = 'm';
+
+if ( $curr_value =~ /^(\d*)([mwdh])$/i ) {
+ $curr_value = $1;
+ $units = lc($2);
+}
+
+</%init>
+
diff --git a/httemplate/elements/tr-input-beginning_ending.html b/httemplate/elements/tr-input-beginning_ending.html
new file mode 100644
index 0000000..8a1dd62
--- /dev/null
+++ b/httemplate/elements/tr-input-beginning_ending.html
@@ -0,0 +1,75 @@
+% unless ( $m->count == $previous_request_count ) {
+ <LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT>
+% }
+
+<TR>
+ <TD ALIGN="right">From date: </TD>
+ <TD><INPUT TYPE="text" NAME="<% $opt{prefix} %>beginning" ID="<% $opt{prefix} %>beginning_text" VALUE="" SIZE=<%$size%> MAXLENGTH=<%$maxlength%>> <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $opt{prefix} %>beginning_button" STYLE="cursor: pointer" TITLE="Select date"><IMG SRC="<%$fsurl%>images/calendar-disabled.png" ID="<% $opt{prefix} %>beginning_disabled" STYLE="display:none"><BR><i>m/d/y<% $time_hint %></i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "<% $opt{prefix} %>beginning_text",
+ ifFormat: "%m/%d/%Y<% $time_format %>",
+ button: "<% $opt{prefix} %>beginning_button",
+ align: "BR"
+ <% $input_time %>
+ });
+</SCRIPT>
+
+% unless ( $opt{layout} =~ /^h/i ) { #horizontal
+
+</TR>
+<TR>
+
+% }
+
+ <TD ALIGN="right">To date: </TD>
+ <TD><INPUT TYPE="text" NAME="<% $opt{prefix} %>ending" ID="<% $opt{prefix} %>ending_text" VALUE="" SIZE=<%$size%> MAXLENGTH=<%$maxlength%>> <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $opt{prefix} %>ending_button" STYLE="cursor: pointer" TITLE="Select date"><IMG SRC="<%$fsurl%>images/calendar-disabled.png" ID="<% $opt{prefix} %>ending_disabled" STYLE="display:none"><BR><i>m/d/y<% $time_hint %></i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "<% $opt{prefix} %>ending_text",
+ ifFormat: "%m/%d/%Y<% $time_format %>",
+ button: "<% $opt{prefix} %>ending_button",
+ align: "BR"
+ <% $input_time %>
+ });
+</SCRIPT>
+</TR>
+
+<TR>
+ <TD></TD>
+ <TD COLSPAN=<% $opt{layout} =~ /^h/i ? 3 : 1 %>>
+ <FONT SIZE="-1">(leave one or both dates blank for an open-ended search)</FONT>
+ </TD>
+</TR>
+
+<%once>
+
+my $previous_request_count = '';
+
+</%once>
+<%init>
+
+my %opt = @_;
+
+$opt{prefix} = '' unless defined $opt{prefix};
+$opt{prefix} .= '_' if $opt{prefix};
+
+my( $input_time, $time_format, $time_hint ) = ( '', '', '' );
+my( $size, $maxlength ) = ( 11, 10 );
+if ( $opt{'input_time'} ) {
+ $input_time = ', showsTime: true, timeFormat: "12"'; # http://www.dynarch.com/demos/jscalendar/doc/html/reference.html#node_sec_2.3
+ $time_format = ' %k:%M:%S'; # http://www.dynarch.com/demos/jscalendar/doc/html/reference.html#node_sec_5.3.5
+ $time_hint = ' h:m:s';
+ $size = 21;
+ $maxlength = 27;
+}
+
+</%init>
+<%cleanup>
+
+$previous_request_count = $m->count;
+
+</%cleanup>
diff --git a/httemplate/elements/tr-input-date-field.html b/httemplate/elements/tr-input-date-field.html
new file mode 100644
index 0000000..11581d5
--- /dev/null
+++ b/httemplate/elements/tr-input-date-field.html
@@ -0,0 +1,40 @@
+
+<LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2">
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar_stripped.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-en.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT>
+
+<TR>
+ <TD ALIGN="right"><% $label %></TD>
+ <TD>
+ <INPUT TYPE="text" NAME="<% $name %>" ID="<% $name %>_text" VALUE="<% $value %>">
+ <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $name %>_button" STYLE="cursor: pointer" TITLE="Select date">
+ </TD>
+</TR>
+
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "<% $name %>_text",
+ ifFormat: "<% $format %>",
+ button: "<% $name %>_button",
+ align: "BR"
+ });
+</SCRIPT>
+
+
+<%init>
+my($name, $value, $label, $format, $usedatetime) = @_;
+
+$format = "%m/%d/%Y" unless $format;
+$label = $name unless $label;
+
+if ($usedatetime) {
+ my $dt = DateTime->from_epoch(epoch => $value, time_zone => 'floating');
+ $value = $dt->strftime($format)
+ unless $value eq '';
+}else{
+ $value = time2str($format, $value);
+}
+
+</%init>
+
diff --git a/httemplate/elements/tr-input-lessthan_greaterthan.html b/httemplate/elements/tr-input-lessthan_greaterthan.html
new file mode 100644
index 0000000..16c2ed9
--- /dev/null
+++ b/httemplate/elements/tr-input-lessthan_greaterthan.html
@@ -0,0 +1,13 @@
+<TR>
+ <TD ALIGN="right"><% $opt{label} %> less than: </TD>
+ <TD><INPUT TYPE="text" NAME="<% $opt{field} %>_lt" SIZE=7></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right"><% $opt{label} %> greater than: </TD>
+ <TD><INPUT TYPE="text" NAME="<% $opt{field} %>_gt" SIZE=7></TD>
+</TR>
+
+<%init>
+ my %opt = @_;
+</%init>
diff --git a/httemplate/elements/tr-input-money.html b/httemplate/elements/tr-input-money.html
new file mode 100644
index 0000000..8801419
--- /dev/null
+++ b/httemplate/elements/tr-input-money.html
@@ -0,0 +1,13 @@
+<% include('tr-input-text.html', @_,
+ 'type' => 'text',
+ 'prefix' => $money_char,
+ 'size' => 8,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%once>
+
diff --git a/httemplate/elements/tr-input-percentage.html b/httemplate/elements/tr-input-percentage.html
new file mode 100644
index 0000000..ae553a9
--- /dev/null
+++ b/httemplate/elements/tr-input-percentage.html
@@ -0,0 +1,8 @@
+<% include('tr-input-text.html', @_,
+ 'type' => 'text',
+ 'postfix' => '%',
+ 'size' => 5, #6? check in IE (not a big deal)
+ 'maxlength' => 7,
+ 'text-align' => 'right',
+ )
+%>
diff --git a/httemplate/elements/tr-input-text.html b/httemplate/elements/tr-input-text.html
new file mode 100644
index 0000000..14f1425
--- /dev/null
+++ b/httemplate/elements/tr-input-text.html
@@ -0,0 +1,13 @@
+<% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $cell_style %>><% include('input-text.html', @_ ) %></TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-justtitle.html b/httemplate/elements/tr-justtitle.html
new file mode 100644
index 0000000..7839a8c
--- /dev/null
+++ b/httemplate/elements/tr-justtitle.html
@@ -0,0 +1,11 @@
+<TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left">
+ <FONT SIZE="+1"><% $opt{value} %></FONT>
+ </TH>
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+</%init>
diff --git a/httemplate/elements/tr-part_pkg_freq.html b/httemplate/elements/tr-part_pkg_freq.html
new file mode 100644
index 0000000..649f8a2
--- /dev/null
+++ b/httemplate/elements/tr-part_pkg_freq.html
@@ -0,0 +1,24 @@
+<% include('tr-select.html', @_,
+ 'field' => 'freq',
+ 'options' => \@freq,
+ 'labels' => \%freq,
+ 'curr_value' => $curr_value,
+ )
+%>
+<%init>
+
+my %opt = @_;
+
+my $curr_value = $opt{'curr_value'} || $opt{'freq'};
+
+tie my %freq, 'Tie::IxHash', %{FS::part_pkg->freqs_href()};
+if ( dbdef->table('part_pkg')->column('freq')->type =~ /(int)/i ) {
+ delete $freq{$_} foreach grep { ! /^\d+$/ } keys %freq;
+}
+
+my @freq = keys %freq;
+@freq = grep { /^\d+$/ } @freq
+ if $opt{'month_increments_only'};
+# if exists($plans{$layer}->{'freq'}) && $plans{$layer}->{'freq'} eq 'm';
+
+</%init>
diff --git a/httemplate/elements/tr-password.html b/httemplate/elements/tr-password.html
new file mode 100644
index 0000000..bbc624d
--- /dev/null
+++ b/httemplate/elements/tr-password.html
@@ -0,0 +1,4 @@
+<% include('tr-input-text.html', @_,
+ 'type' => 'password',
+ )
+%>
diff --git a/httemplate/elements/tr-pkg_svc.html b/httemplate/elements/tr-pkg_svc.html
new file mode 100644
index 0000000..a9561a1
--- /dev/null
+++ b/httemplate/elements/tr-pkg_svc.html
@@ -0,0 +1,93 @@
+<TR>
+ <TD BGCOLOR="#e8e8e8" COLSPAN=99>
+
+<% itable('', 4, 1) %><TR><TD VALIGN="top">
+<% $thead %>
+
+%foreach my $part_svc ( @part_svc ) {
+% my $svcpart = $part_svc->svcpart;
+% my $pkg_svc = $pkg_svc{$svcpart}
+% || new FS::pkg_svc ( {
+% 'pkgpart' => $pkgpart,
+% 'svcpart' => $svcpart,
+% 'quantity' => 0,
+% 'primary_svc' => '',
+% } );
+% if ( $cgi->param('error') ) {
+% my $primary_svc = ( $pkg_svc->primary_svc =~ /^Y/i );
+% my $pkg_svc_primary = scalar($cgi->param('pkg_svc_primary'));
+% $pkg_svc->primary_svc('')
+% if $primary_svc && $pkg_svc_primary != $svcpart;
+% $pkg_svc->primary_svc('Y')
+% if ! $primary_svc && $pkg_svc_primary == $svcpart;
+% }
+%
+% push @fixups, "pkg_svc$svcpart";
+%
+% my $quan = 0;
+% if ( $cgi->param("pkg_svc$svcpart") =~ /^\s*(\d+)\s*$/ ) {
+% $quan = $1;
+% } elsif ( $pkg_svc->quantity ) {
+% $quan = $pkg_svc->quantity;
+% }
+
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="pkg_svc<% $svcpart %>" SIZE=7 MAXLENGTH=6 VALUE="<% $quan %>">
+ </TD>
+
+ <TD ALIGN="center">
+ <INPUT TYPE="radio" NAME="pkg_svc_primary" VALUE="<% $svcpart %>" <% $pkg_svc->primary_svc =~ /^Y/i ? ' CHECKED' : '' %>>
+ </TD>
+
+ <TD>
+ <A HREF="part_svc.cgi?<% $part_svc->svcpart %>"><% $part_svc->svc %></A> <% $part_svc->disabled =~ /^Y/i ? ' (DISABLED' : '' %>
+ </TD>
+ </TR>
+% foreach ( 1 .. $columns-1 ) {
+% if ( $count == int( $_ * scalar(@part_svc) / $columns ) ) {
+%
+
+ </TABLE></TD><TD VALIGN="top"><% $thead %>
+% }
+% }
+% $count++;
+%
+% }
+
+</TR></TABLE></TD></TR></TABLE>
+
+ </TD>
+</TR>
+
+<%init>
+
+my %opt = @_;
+my $cgi = $opt{'cgi'};
+
+my $thead = "\n\n". ntable('#cccccc', 2).
+ '<TR><TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Quan.</FONT></TH>'.
+ '<TH BGCOLOR="#dcdcdc"><FONT SIZE=-2>Primary</FONT></TH>'.
+ '<TH BGCOLOR="#dcdcdc">Service</TH></TR>';
+
+my $part_pkg = $opt{'object'};
+my $pkgpart = $part_pkg->pkgpart;
+
+my $where = "WHERE disabled IS NULL OR disabled = ''";
+if ( $pkgpart ) {
+ $where .= " OR 0 < ( SELECT quantity FROM pkg_svc
+ WHERE pkg_svc.svcpart = part_svc.svcpart
+ AND pkgpart = $pkgpart
+ )";
+}
+my @part_svc = qsearch('part_svc', {}, '', $where);
+
+#my $q_part_pkg = $clone_part_pkg || $part_pkg;
+#my %pkg_svc = map { $_->svcpart => $_ } $q_part_pkg->pkg_svc;
+my %pkg_svc = map { $_->svcpart => $_ } $part_pkg->pkg_svc;
+
+my @fixups = ();
+my $count = 0;
+my $columns = 3;
+
+</%init>
diff --git a/httemplate/elements/tr-select-access_group.html b/httemplate/elements/tr-select-access_group.html
new file mode 100644
index 0000000..e443ad2
--- /dev/null
+++ b/httemplate/elements/tr-select-access_group.html
@@ -0,0 +1,22 @@
+%
+% my( $groupnum, %opt ) = @_;
+%
+% $opt{'access_group'} ||= [ qsearch( 'access_group', {} ) ]; # { disabled=>'' } )
+%
+% #warn "***** tr-select-access_group: \n". Dumper(%opt);
+%
+% if ( scalar(@{ $opt{'access_group'} }) == 0 ) {
+
+
+ <INPUT TYPE="hidden" NAME="groupnum" VALUE="">
+% } else {
+
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Access group' %></TD>
+ <TD>
+ <% include( '/elements/select-access_group.html', $groupnum, %opt ) %>
+ </TD>
+ </TR>
+% }
+
diff --git a/httemplate/elements/tr-select-agent.html b/httemplate/elements/tr-select-agent.html
new file mode 100644
index 0000000..fcfa9f3
--- /dev/null
+++ b/httemplate/elements/tr-select-agent.html
@@ -0,0 +1,33 @@
+% if ( scalar(@agents) == 1 ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'field'} || 'agentnum' %>" VALUE="<% $agents[0]->agentnum %>">
+
+%# YUCK. empty row so we don't throw g_row in edit.html off :/
+ <TR>
+ </TR>
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Agent' %></TD>
+ <TD>
+ <% include( '/elements/select-agent.html',
+ 'curr_value' => $agentnum,
+ 'agents' => \@agents,
+ %opt,
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+
+<%init>
+
+my %opt = @_;
+my $agentnum = $opt{'curr_value'} || $opt{'value'};
+
+my @agents = $opt{'agents'}
+ ? @{ $opt{'agents'} }
+ : $FS::CurrentUser::CurrentUser->agents;
+
+</%init>
diff --git a/httemplate/elements/tr-select-agent_type.html b/httemplate/elements/tr-select-agent_type.html
new file mode 100644
index 0000000..1b0dfd4
--- /dev/null
+++ b/httemplate/elements/tr-select-agent_type.html
@@ -0,0 +1,39 @@
+% if ( scalar(@agent_types) == 1 ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'field'} || 'typenum' %>" VALUE="<% $agent_types[0]->typenum %>">
+
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Agent Type' %></TD>
+ <TD>
+ <% include( '/elements/select-agent_type.html',
+ 'curr_value' => $typenum,
+ 'agent_types' => \@agent_types,
+ %opt,
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+
+<%init>
+
+my %opt = @_;
+my $typenum = $opt{'curr_value'} || $opt{'value'};
+
+my @agent_types = ();
+if ( $opt{'agent_types'} ) {
+ #@agents = @{ $opt{'agents'} };
+
+ #here is the agent virtualization
+# my $agentnums_href = $FS::CurrentUser::CurrentUser->agentnums_href;
+# @agent_types = grep $agentnums_href->{$_->agentnum}, @{ $opt{'agent_types'} };
+
+ delete $opt{'agent_types'};
+} else {
+# @agents = $FS::CurrentUser::CurrentUser->agents;
+}
+
+</%init>
diff --git a/httemplate/elements/tr-select-agent_types.html b/httemplate/elements/tr-select-agent_types.html
new file mode 100644
index 0000000..efbf386
--- /dev/null
+++ b/httemplate/elements/tr-select-agent_types.html
@@ -0,0 +1,19 @@
+% unless ( $opt{'disabled'} || scalar(@all_agent_types) == 1 ) {
+
+<% include('/elements/tr-justtitle.html', value=>'Agent (reseller) types') %>
+
+% }
+
+<TR>
+ <TD COLSPAN=2>
+ <% include('select-agent_types.html', %opt) %>
+ </TD>
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my @all_agent_types = map {$_->typenum} qsearch('agent_type',{});
+
+</%init>
diff --git a/httemplate/elements/tr-select-cdrbatch.html b/httemplate/elements/tr-select-cdrbatch.html
new file mode 100644
index 0000000..21cd004
--- /dev/null
+++ b/httemplate/elements/tr-select-cdrbatch.html
@@ -0,0 +1,32 @@
+% if ( ! scalar(@{ $opt{'cdrbatches'} }) ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'cdrbatch' %>" VALUE="<% $selected_cdrbatch %>">
+
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'cdrbatch'} || 'CDR Batch: ' %></TD>
+ <TD>
+ <% include( '/elements/select-cdrbatch.html', 'curr_value' => $selected_cdrbatch, %opt ) %>
+ </TD>
+ </TR>
+
+% }
+<%init>
+
+my( %opt ) = @_;
+my $conf = new FS::Conf;
+my $selected_cdrbatch = $opt{'curr_value'}; # || $opt{'value'} necessary?
+
+unless ( $opt{'cdrbatches'} ) {
+
+ my $sth = dbh->prepare('SELECT cdrbatch FROM cdr')
+ or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my %cdrbatches = map { $_->[0] => 1 } @{$sth->fetchall_arrayref};
+ @{ $opt{'cdrbatches'} } = grep $_, keys %cdrbatches;
+
+}
+
+</%init>
+
diff --git a/httemplate/elements/tr-select-cust-fields.html b/httemplate/elements/tr-select-cust-fields.html
new file mode 100644
index 0000000..80562fe
--- /dev/null
+++ b/httemplate/elements/tr-select-cust-fields.html
@@ -0,0 +1,15 @@
+%
+% my( $cust_fields, %opt ) = @_;
+%
+% use FS::ConfDefaults;
+% $opt{'avail_fields'} ||= [ FS::ConfDefaults->cust_fields_avail() ];
+%
+%
+
+
+<TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Customer fields' %></TD>
+ <TD>
+ <% include( '/elements/select-cust-fields.html', $cust_fields, %opt ) %>
+ </TD>
+</TR>
diff --git a/httemplate/elements/tr-select-cust_location.html b/httemplate/elements/tr-select-cust_location.html
new file mode 100644
index 0000000..da16dfe
--- /dev/null
+++ b/httemplate/elements/tr-select-cust_location.html
@@ -0,0 +1,178 @@
+<%doc>
+
+Example:
+
+ include('/elements/tr-select-cust_location.html',
+ 'cgi' => $cgi,
+ 'cust_main' => $cust_main,
+ )
+
+</%doc>
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/location.cgi',
+ 'subs' => [ 'get_location' ],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ function locationnum_changed(what) {
+ var locationnum = what.options[what.selectedIndex].value;
+ if ( locationnum == -1 ) {
+
+% for (@location_fields) {
+ what.form.<%$_%>.disabled = false;
+ what.form.<%$_%>.style.backgroundColor = '#ffffff';
+% }
+
+ what.form.address1.value = '';
+ what.form.address2.value = '';
+ what.form.city.value = '';
+ what.form.zip.value = '';
+
+ changeSelect(what.form.country, <% $countrydefault |js_string %>);
+
+ country_changed( what.form.country,
+ fix_state_factory( <% $statedefault |js_string %>,
+ ''
+ )
+ );
+
+ } else {
+
+ if ( locationnum == 0 ) {
+ what.form.address1.value = <% $cust_main->get($prefix.'address1') |js_string %>;
+ what.form.address2.value = <% $cust_main->get($prefix.'address2') |js_string %>;
+ what.form.city.value = <% $cust_main->get($prefix.'city') |js_string %>;
+ what.form.zip.value = <% $cust_main->get($prefix.'zip') |js_string %>;
+
+ changeSelect(what.form.country, <% $cust_main->get($prefix.'country') | js_string %> );
+
+ country_changed( what.form.country,
+ fix_state_factory( <% $cust_main->get($prefix.'state') | js_string %>,
+ <% $cust_main->get($prefix.'county') | js_string %>
+ )
+ );
+
+ } else {
+ get_location( locationnum, update_location );
+ }
+
+%#sleep/wait until dropdowns are updated?
+% for (@location_fields) {
+ what.form.<%$_%>.disabled = true;
+ what.form.<%$_%>.style.backgroundColor = '#dddddd';
+% }
+
+ }
+ }
+
+ function fix_state_factory (state, county) {
+ function fix_state() {
+ var state_el = document.getElementById('state');
+ changeSelect(state_el, state);
+ state_changed(state_el, fix_county_factory(county) );
+ }
+ return fix_state;
+ }
+
+ function fix_county_factory(county) {
+ function fix_county() {
+ var county_el = document.getElementById('county');
+ if ( county.length > 0 ) {
+ changeSelect(county_el, county );
+ } else {
+ county_el.selectedIndex = 0;
+ }
+ }
+ return fix_county;
+ }
+
+ function changeSelect(what, value) {
+ for ( var i=0; i<what.length; i++) {
+ if ( what.options[i].value == value ) {
+ what.selectedIndex = i;
+ }
+ }
+ }
+
+ function update_location( string ) {
+ var hash = eval('('+string+')');
+ document.getElementById('address1').value = hash['address1'];
+ document.getElementById('address2').value = hash['address2'];
+ document.getElementById('city').value = hash['city'];
+ document.getElementById('zip').value = hash['zip'];
+
+ var country_el = document.getElementById('country');
+
+ changeSelect( country_el, hash['country'] );
+
+ country_changed( country_el,
+ fix_state_factory( hash['state'],
+ hash['county']
+ )
+ );
+ }
+
+</SCRIPT>
+
+<TR>
+ <TH ALIGN="right">Service&nbsp;location</TH>
+ <TD COLSPAN=7>
+ <SELECT NAME="locationnum" onChange="locationnum_changed(this);">
+ <OPTION VALUE="">(default service address)
+% foreach my $loc ( $cust_main->cust_location ) {
+ <OPTION VALUE="<% $loc->locationnum %>"
+ <% $locationnum == $loc->locationnum ? 'SELECTED' : '' %>
+ ><% $loc->line |h %>
+% }
+ <OPTION VALUE="-1"
+ <% $locationnum == -1 ? 'SELECTED' : '' %>
+ >Add new location
+ </SELECT>
+ </TD>
+</TR>
+
+<% include('/elements/location.html',
+ 'object' => $cust_location,
+ #'onchange' ? probably not
+ 'disabled' => ( $locationnum == -1 ? '' : 'DISABLED' ),
+ 'no_asterisks' => 1,
+ )
+%>
+
+<%once>
+
+my @location_fields = qw( address1 address2 city county state zip country );
+
+</%once>
+<%init>
+
+my $conf = new FS::Conf;
+my $countrydefault = $conf->config('countrydefault') || 'US';
+my $statedefault = $conf->config('statedefault')
+ || ($countrydefault eq 'US' ? 'CA' : '');
+
+my %opt = @_;
+my $cgi = $opt{'cgi'};
+my $cust_main = $opt{'cust_main'};
+
+my $prefix = length($cust_main->ship_last) ? 'ship_' : '';
+
+$cgi->param('locationnum') =~ /^(\-?\d*)$/ or die "illegal locationnum";
+my $locationnum = $1;
+my $cust_location;
+if ( $locationnum && $locationnum != -1 ) {
+ $cust_location = qsearchs('cust_location', { 'locationnum' => $locationnum } )
+ or die "unknown locationnum";
+} else {
+ $cust_location = new FS::cust_location;
+ if ( $locationnum == -1 ) {
+ $cust_location->$_( $cgi->param($_) ) foreach @location_fields;
+ } else {
+ $cust_location->$_( $cust_main->get($prefix.$_) ) foreach @location_fields;
+ }
+}
+
+</%init>
diff --git a/httemplate/elements/tr-select-cust_main-status.html b/httemplate/elements/tr-select-cust_main-status.html
new file mode 100644
index 0000000..6700d7e
--- /dev/null
+++ b/httemplate/elements/tr-select-cust_main-status.html
@@ -0,0 +1,29 @@
+<% include ('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+
+ <% include( '/elements/select-cust_main-status.html',
+ 'curr_value' => $curr_value,
+ %opt
+ )
+ %>
+
+ </TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+#my $onchange = $opt{'onchange'}
+# ? 'onChange="'. $opt{'onchange'}. '(this)"'
+# : '';
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+$opt{'statuses'} ||= [ FS::cust_main->statuses() ]; # { disabled=>'' } )
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+</%init>
diff --git a/httemplate/elements/tr-select-cust_pkg-status.html b/httemplate/elements/tr-select-cust_pkg-status.html
new file mode 100644
index 0000000..6cc29d0
--- /dev/null
+++ b/httemplate/elements/tr-select-cust_pkg-status.html
@@ -0,0 +1,29 @@
+<% include ('tr-td-label.html', 'label' => 'Status', @_ ) %>
+
+ <TD <% $style %>>
+
+ <% include( '/elements/select-cust_pkg-status.html',
+ 'curr_value' => $curr_value,
+ %opt
+ )
+ %>
+
+ </TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+#my $onchange = $opt{'onchange'}
+# ? 'onChange="'. $opt{'onchange'}. '(this)"'
+# : '';
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+$opt{'statuses'} ||= [ FS::cust_pkg->statuses() ]; # { disabled=>'' } )
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+</%init>
diff --git a/httemplate/elements/tr-select-did.html b/httemplate/elements/tr-select-did.html
new file mode 100644
index 0000000..c784033
--- /dev/null
+++ b/httemplate/elements/tr-select-did.html
@@ -0,0 +1,25 @@
+<% include('tr-td-label.html', @_ ) %>
+
+% if ( $opt{'curr_value'} ne '' ) {
+
+ <TD BGCOLOR="#dddddd" <% $cell_style %>><% $opt{'formatted_value'} || $opt{'curr_value'} || $opt{'value'} |h %></TD>
+
+ <% include('hidden.html', %opt ) %>
+
+% } else {
+
+ <TD <% $cell_style %>>
+ <% include('/elements/select-did.html', %opt ) %>
+ </TD>
+
+% }
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-select-domain.html b/httemplate/elements/tr-select-domain.html
new file mode 100644
index 0000000..5b8d237
--- /dev/null
+++ b/httemplate/elements/tr-select-domain.html
@@ -0,0 +1,12 @@
+% #if ( scalar(@domains) < 2 ) {
+% #} else {
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Domain' %></TD>
+ <TD>
+ <% include( '/elements/select-domain.html', %opt) %>
+ </TD>
+ </TR>
+% #}
+<%init>
+ my %opt = @_;
+</%init>
diff --git a/httemplate/elements/tr-select-from_to.html b/httemplate/elements/tr-select-from_to.html
new file mode 100644
index 0000000..083243d
--- /dev/null
+++ b/httemplate/elements/tr-select-from_to.html
@@ -0,0 +1,52 @@
+%
+%
+% #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+% my ($curmon,$curyear) = (localtime(time))[4,5];
+%
+% #find first month
+% my $syear = 1899+$curyear;
+% my $smonth = $curmon+1;
+%
+% #want 12 month by default, not 13
+% $smonth++;
+% if ( $smonth > 12 ) { $smonth-=12; $syear++ }
+%
+% #find last month
+% my $eyear = 1900+$curyear;
+% my $emonth = $curmon+1;
+%
+% my %hash = (
+% 'show_month_abbr' => 1,
+% 'start_year' => '1999',
+% 'end_year' => '2012', #haha, well...
+% @_,
+% );
+%
+%
+
+
+<TR>
+ <TD ALIGN="right">From: </TD>
+ <TD>
+ <% include('/elements/select-month_year.html',
+ 'prefix' => 'start',
+ 'selected_mon' => $smonth,
+ 'selected_year' => $syear,
+ %hash,
+ )
+ %>
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">To: </TD>
+ <TD>
+ <% include('/elements/select-month_year.html',
+ 'prefix' => 'end',
+ 'selected_mon' => $emonth,
+ 'selected_year' => $eyear,
+ %hash,
+ )
+ %>
+ </TD>
+</TR>
diff --git a/httemplate/elements/tr-select-invoice_template.html b/httemplate/elements/tr-select-invoice_template.html
new file mode 100644
index 0000000..7ba8d99
--- /dev/null
+++ b/httemplate/elements/tr-select-invoice_template.html
@@ -0,0 +1,39 @@
+<% include('tr-td-label.html', @_) %>
+
+ <TD <% $style %>>
+
+ <SELECT NAME = "<% $opt{'field'} || 'templatename' %>"
+ ID = "<% $opt{'id'} %>"
+ >
+
+% foreach my $templatename ( '', @templatenames ) {
+
+ <OPTION VALUE="<% $templatename %>"
+ <% $templatename eq $curr_value ? 'SELECTED' : '' %>
+ ><% $templatename || '(Default)' %>
+
+% }
+
+ </SELECT>
+
+ </TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+my $conf = new FS::Conf;
+
+my @templatenames = $conf->invoice_templatenames;
+
+</%init>
diff --git a/httemplate/elements/tr-select-otaker.html b/httemplate/elements/tr-select-otaker.html
new file mode 100644
index 0000000..edf62dc
--- /dev/null
+++ b/httemplate/elements/tr-select-otaker.html
@@ -0,0 +1,10 @@
+<TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Employee: ' %></TD>
+ <TD><% include('select-otaker.html', %opt) %></TD>
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+</%init>
diff --git a/httemplate/elements/tr-select-part_pkg.html b/httemplate/elements/tr-select-part_pkg.html
new file mode 100644
index 0000000..db9afd2
--- /dev/null
+++ b/httemplate/elements/tr-select-part_pkg.html
@@ -0,0 +1,39 @@
+% if ( $opt{'part_pkg'} && scalar(@{ $opt{'part_pkg'} }) == 0 ) {
+% unless ( $opt{'js_only'} ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'field'} || 'pkgpart' %>" VALUE="">
+
+% }
+%
+% } else {
+%
+% unless ( $opt{'js_only'} ) {
+
+ <% include('tr-td-label.html', %opt) %>
+ <TD <% $cell_style %>>
+
+% }
+%
+ <% include( '/elements/select-part_pkg.html', %opt ) %>
+%
+% unless ( $opt{'js_only'} ) {
+
+ </TD>
+ </TR>
+
+% }
+%
+% }
+<%init>
+
+my( %opt ) = @_;
+
+my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+$opt{'label'} ||= 'Package definition';
+
+#taken care of (better) in select-part_pkg now (is there anything using this
+# that needs to override the disabed=>'' ??)
+#$opt{'part_pkg'} ||= [ qsearch( 'part_pkg', {} ) ]; # { disabled=>'' } )
+
+</%init>
diff --git a/httemplate/elements/tr-select-part_referral.html b/httemplate/elements/tr-select-part_referral.html
new file mode 100644
index 0000000..a589528
--- /dev/null
+++ b/httemplate/elements/tr-select-part_referral.html
@@ -0,0 +1,37 @@
+% if ( scalar( @{$opt{'part_referrals'}} ) == 0 ) {
+ <P><FONT SIZE="+1" COLOR="#ff0000">You have not created any advertising sources. You must create at least one advertising source before adding a customer. Go to <A HREF="<% popurl(2) %>browse/part_referral.html">advertising source listing</A> and create one or more advertising sources.</FONT>
+% } elsif ( scalar( @{$opt{'part_referrals'}} ) == 1 ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'refnum' %>" VALUE="<% $opt{'part_referrals'}->[0]->refnum %>">
+
+% } else {
+
+ <TR>
+% if ( $opt{'label'} ) {
+ <TD ALIGN="right"><% $opt{'label'} %></TD>
+% } else {
+ <TH ALIGN="right"><%$r%>Advertising source</TH>
+% }
+ <TD COLSPAN="<% $colspan %>">
+ <% include( '/elements/select-part_referral.html',
+ 'curr_value' => $refnum,
+ %opt
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+<%init>
+
+my %opt = @_;
+my $refnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'part_referrals'} ||=
+ [ FS::part_referral->all_part_referral( 1 ) ]; #1: include global
+
+my $colspan = delete($opt{'colspan'}) || 1;
+
+my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
+
+</%init>
diff --git a/httemplate/elements/tr-select-part_svc.html b/httemplate/elements/tr-select-part_svc.html
new file mode 100644
index 0000000..0274ef1
--- /dev/null
+++ b/httemplate/elements/tr-select-part_svc.html
@@ -0,0 +1,29 @@
+% if ( scalar(@{ $opt{'part_svc'} }) == 0 ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'field'} || 'svcpart' %>" VALUE="">
+
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Package definition' %></TD>
+ <TD>
+ <% include( '/elements/select-table.html',
+ 'table' => 'part_svc',
+ 'name_col' => 'svc',
+ 'multiple' => 1,
+ #N/A 'empty_label' => '(none)',
+ %opt,
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+
+<%init>
+
+my( %opt ) = @_;
+
+$opt{'part_svc'} ||= [ qsearch( 'part_svc', {} ) ]; # { disabled=>'' } )
+
+</%init>
diff --git a/httemplate/elements/tr-select-payby.html b/httemplate/elements/tr-select-payby.html
new file mode 100644
index 0000000..354eb55
--- /dev/null
+++ b/httemplate/elements/tr-select-payby.html
@@ -0,0 +1,37 @@
+<% include ('tr-td-label.html', 'label' => 'Payment type', @_ ) %>
+
+ <TD <% $style %>>
+
+ <% include( '/elements/select-payby.html',
+ 'curr_value' => $curr_value,
+ %opt
+ )
+ %>
+
+ </TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+#my $onchange = $opt{'onchange'}
+# ? 'onChange="'. $opt{'onchange'}. '(this)"'
+# : '';
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+my $method = 'payby2longname';
+$method = 'cust_payby2longname' if $opt{'payby_type'} eq 'cust';
+#$method = 'event_payby2longname' if $opt{'payby_type'} eq 'event';
+#$method = 'pay_payby2longname' if $opt{'payby_type'} eq 'pay';
+
+unless ( $opt{'paybys'} ) {
+ tie %{ $opt{'paybys'} }, 'Tie::IxHash', FS::payby->$method();
+}
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+</%init>
+
diff --git a/httemplate/elements/tr-select-pkg_class.html b/httemplate/elements/tr-select-pkg_class.html
new file mode 100644
index 0000000..aa27609
--- /dev/null
+++ b/httemplate/elements/tr-select-pkg_class.html
@@ -0,0 +1,27 @@
+% if ( scalar(@{ $opt{'pkg_class'} }) == 0 ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'field'} || 'classnum' %>" VALUE="">
+
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Package class' %></TD>
+ <TD>
+ <% include( '/elements/select-pkg_class.html',
+ 'curr_value' => $classnum,
+ %opt
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+
+<%init>
+
+my %opt = @_;
+my $classnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'pkg_class'} ||= [ qsearch( 'pkg_class', {} ) ]; # { disabled=>'' } )
+
+</%init>
diff --git a/httemplate/elements/tr-select-rate.html b/httemplate/elements/tr-select-rate.html
new file mode 100644
index 0000000..27f2645
--- /dev/null
+++ b/httemplate/elements/tr-select-rate.html
@@ -0,0 +1,21 @@
+% unless ( $opt{'js_only'} ) {
+
+ <% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+% }
+
+ <% include( '/elements/select-rate.html', %opt ) %>
+
+% unless ( $opt{'js_only'} ) {
+ </TD>
+ </TR>
+% }
+<%init>
+
+my( %opt ) = @_;
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
+
diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html
new file mode 100755
index 0000000..d85538f
--- /dev/null
+++ b/httemplate/elements/tr-select-reason.html
@@ -0,0 +1,189 @@
+<%doc>
+
+Example:
+
+ include( '/elements/tr-select-reason.html',
+
+ #required
+ 'field' => 'reasonnum',
+ 'reason_class' => 'C', # currently 'C', 'R', or 'S'
+ # for cancel, credit, or suspend
+
+ #recommended
+ 'cgi' => $cgi, #easiest way for things to be properly "sticky" on errors
+
+ #optional
+ 'control_button' => 'element_name', #button to be enabled when a reason is
+ #selected
+ 'id' => 'element_id',
+
+ #deprecated ways to keep things "sticky" on errors
+ # (requires duplicate code in each using file to parse cgi params)
+ 'curr_value' => $curr_value,
+ 'curr_value' => {
+ 'typenum' => $typenum,
+ 'reason' => $reason,
+ },
+
+ )
+
+</%doc>
+
+<SCRIPT TYPE="text/javascript">
+ function sh_add<% $func_suffix %>()
+ {
+
+ if (document.getElementById('<% $id %>').selectedIndex == 0){
+ <% $controlledbutton ? $controlledbutton.'.disabled = true;' : ';' %>
+ }else{
+ <% $controlledbutton ? $controlledbutton.'.disabled = false;' : ';' %>
+ }
+
+%if ($curuser->access_right($add_access_right)){
+
+ if (document.getElementById('<% $id %>').selectedIndex ==
+ (document.getElementById('<% $id %>').length - 1)) {
+ document.getElementById('new<% $id %>').disabled = false;
+ document.getElementById('new<% $id %>').style.display = 'inline';
+ document.getElementById('new<% $id %>Label').style.display = 'inline';
+ document.getElementById('new<% $id %>T').disabled = false;
+ document.getElementById('new<% $id %>T').style.display = 'inline';
+ document.getElementById('new<% $id %>TLabel').style.display = 'inline';
+ } else {
+ document.getElementById('new<% $id %>').disabled = true;
+ document.getElementById('new<% $id %>').style.display = 'none';
+ document.getElementById('new<% $id %>Label').style.display = 'none';
+ document.getElementById('new<% $id %>T').disabled = true;
+ document.getElementById('new<% $id %>T').style.display = 'none';
+ document.getElementById('new<% $id %>TLabel').style.display = 'none';
+ }
+
+%}
+
+ }
+</SCRIPT>
+
+<TR>
+ <TD ALIGN="right">Reason</TD>
+ <TD>
+ <SELECT id="<% $id %>" name="<% $name %>" onFocus="sh_add<% $func_suffix %>()" onChange="sh_add<% $func_suffix %>()">
+ <OPTION VALUE="" <% ($init_reason eq '') ? 'SELECTED' : '' %>>Select Reason...</OPTION>
+% foreach my $reason (@reasons) {
+ <OPTION VALUE="<% $reason->reasonnum %>" <% ($init_reason == $reason->reasonnum) ? 'SELECTED' : '' %>><% $reason->reasontype->type %> : <% $reason->reason %></OPTION>
+% }
+% if ($curuser->access_right($add_access_right)) {
+ <OPTION VALUE="-1" <% ($init_reason == -1) ? 'SELECTED' : '' %>>Add new reason</OPTION>
+% }
+%
+ </SELECT>
+ </TD>
+</TR>
+
+% my @types = qsearch( 'reason_type', { 'class' => $class } );
+% if (scalar(@types) < 1) { # we should never reach this
+<TR>
+ <TD ALIGN="right">
+ <P>No reason types. Go add some. </P>
+ </TD>
+</TR>
+% }elsif (scalar(@types) == 1) {
+<TR>
+ <TD ALIGN="right">
+ <P id="new<% $name %>TLabel" style="display:<% $display %>">Reason Type</P>
+ </TD>
+ <TD>
+ <P id="new<% $name %>T" disabled="<% $disabled %>" style="display:<% $display %>"><% $types[0]->type %>
+ <INPUT type="hidden" name="new<% $name %>T" value="<% $types[0]->typenum %>">
+ </TD>
+</TR>
+
+% }else{
+
+<TR>
+ <TD ALIGN="right">
+ <P id="new<% $id %>TLabel" style="display:<% $display %>">Reason Type</P>
+ </TD>
+ <TD>
+ <SELECT id="new<% $id %>T" name="new<% $name %>T" "<% $disabled %>" style="display:<% $display %>">
+% for my $type (@types) {
+ <OPTION VALUE="<% $type->typenum %>" <% ($init_type == $type->typenum) ? 'SELECTED' : '' %>><% $type->type %></OPTION>
+% }
+ </SELECT>
+ </TD>
+</TR>
+% }
+
+<TR>
+ <TD ALIGN="right">
+ <P id="new<% $id %>Label" style="display:<% $display %>">New Reason</P>
+ </TD>
+ <TD><INPUT id="new<% $id %>" name="new<% $name %>" type="text" value="<% $init_newreason |h %>" "<% $disabled %>" style="display:<% $display %>"></TD>
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my $name = $opt{'field'};
+my $class = $opt{'reason_class'};
+
+my $init_reason;
+if ( $opt{'cgi'} ) {
+ $init_reason = $opt{'cgi'}->param($name);
+} else {
+ $init_reason = $opt{'curr_value'};
+}
+
+my $controlledbutton = $opt{'control_button'};
+
+( my $func_suffix = $name ) =~ s/\./_/g;
+
+my $id = $opt{'id'} || $func_suffix;
+
+my( $add_access_right, $access_right );
+if ($class eq 'C') {
+ $access_right = 'Cancel customer';
+ $add_access_right = 'Add on-the-fly cancel reason';
+} elsif ($class eq 'S') {
+ $access_right = 'Suspend customer package';
+ $add_access_right = 'Add on-the-fly suspend reason';
+} elsif ($class eq 'R') {
+ $access_right = 'Post credit';
+ $add_access_right = 'Add on-the-fly credit reason';
+} else {
+ die "illegal class: $class";
+}
+
+my( $display, $disabled ) = ( 'none', 'DISABLED' );
+my( $init_type, $init_newreason ) = ( '', '' );
+if ($init_reason == -1 || ref($init_reason) ) {
+
+ $display = 'inline';
+ $disabled = '';
+
+ if ( ref($init_reason) ) {
+ $init_type = $init_reason->{'typenum'};
+ $init_newreason = $init_reason->{'reason'};
+ $init_reason = -1;
+ } elsif ( $opt{'cgi'} ) {
+ $init_type = $opt{'cgi'}->param( "new${name}T" );
+ $init_newreason = $opt{'cgi'}->param( "new$name" );
+ }
+
+}
+
+my $extra_sql =
+ "WHERE class = '$class' and (disabled = '' OR disabled is NULL)";
+
+my @reasons = qsearch({
+ table => 'reason',
+ hashref => {},
+ extra_sql => $extra_sql,
+ addl_from => 'LEFT JOIN reason_type '.
+ ' ON reason_type.typenum = reason.reason_type',
+ order_by => 'ORDER BY reason_type.type ASC, reason.reason ASC',
+});
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+</%init>
diff --git a/httemplate/elements/tr-select-table.html b/httemplate/elements/tr-select-table.html
new file mode 100644
index 0000000..6ac7487
--- /dev/null
+++ b/httemplate/elements/tr-select-table.html
@@ -0,0 +1,20 @@
+% unless ( $opt{'js_only'} ) {
+
+ <% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+% }
+
+ <% include( '/elements/select-table.html', %opt ) %>
+
+% unless ( $opt{'js_only'} ) {
+ </TD>
+ </TR>
+% }
+<%init>
+
+my( %opt ) = @_;
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-select-taxclass.html b/httemplate/elements/tr-select-taxclass.html
new file mode 100644
index 0000000..981c1a5
--- /dev/null
+++ b/httemplate/elements/tr-select-taxclass.html
@@ -0,0 +1,34 @@
+% if ( ! $conf->exists('enable_taxclasses')
+% || scalar(@{ $opt{'taxclasses'} }) == 0
+% ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'taxclass' %>" VALUE="<% $selected_taxclass %>">
+
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Tax class: ' %></TD>
+ <TD>
+ <% include( '/elements/select-taxclass.html', 'curr_value' => $selected_taxclass, %opt ) %>
+ </TD>
+ </TR>
+
+% }
+<%init>
+
+my( %opt ) = @_;
+my $conf = new FS::Conf;
+my $selected_taxclass = $opt{'curr_value'}; # || $opt{'value'} necessary?
+
+unless ( $opt{'taxclasses'} ) {
+
+ #my $sth = dbh->prepare('SELECT DISTINCT taxclass FROM cust_main_county')
+ my $sth = dbh->prepare('SELECT taxclass FROM part_pkg_taxclass')
+ or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+ my %taxclasses = map { $_->[0] => 1 } @{$sth->fetchall_arrayref};
+ @{ $opt{'taxclasses'} } = grep $_, keys %taxclasses;
+
+}
+
+</%init>
diff --git a/httemplate/elements/tr-select-taxoverride.html b/httemplate/elements/tr-select-taxoverride.html
new file mode 100644
index 0000000..e20d37e
--- /dev/null
+++ b/httemplate/elements/tr-select-taxoverride.html
@@ -0,0 +1,18 @@
+% if ( $conf->exists('enable_taxproducts') ) {
+ <%include('tr-td-label.html', @_) %>
+ <TD <% $cell_style %>><% include('select-taxoverride.html', @_) %></TD>
+ </TR>
+
+% } else {
+ <INPUT TYPE="hidden" NAME="<% $name %>" VALUE="<% $opt{value} %>">
+% }
+
+<%init>
+
+my $conf = new FS::Conf;
+
+my %opt = @_;
+my $cell_style = $opt{'cell_style'}? 'STYLE="'. $opt{cell_style}. '"' : '';
+my $name = $opt{element_name} || $opt{field} || 'tax_override';
+
+</%init>
diff --git a/httemplate/elements/tr-select-taxproduct.html b/httemplate/elements/tr-select-taxproduct.html
new file mode 100644
index 0000000..759d0c01
--- /dev/null
+++ b/httemplate/elements/tr-select-taxproduct.html
@@ -0,0 +1,18 @@
+% if ( $conf->exists('enable_taxproducts') ) {
+ <%include('tr-td-label.html', @_) %>
+ <TD <% $cell_style %>><% include('select-taxproduct.html', @_) %></TD>
+ </TR>
+
+% } else {
+ <INPUT TYPE="hidden" NAME="<% $name %>" VALUE="<% $opt{value} %>">
+% }
+
+<%init>
+
+my $conf = new FS::Conf;
+
+my %opt = @_;
+my $cell_style = $opt{cell_style} ? 'STYLE="'. $opt{cell_style}. '"' : '';
+my $name = $opt{element_name} || $opt{field} || 'taxproductnum';
+
+</%init>
diff --git a/httemplate/elements/tr-select.html b/httemplate/elements/tr-select.html
new file mode 100644
index 0000000..07b0a01
--- /dev/null
+++ b/httemplate/elements/tr-select.html
@@ -0,0 +1,61 @@
+<% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+
+ <SELECT NAME = "<% $opt{field} %>"
+ ID = "<% $opt{id} %>"
+ previousValue = "<% $curr_value %>"
+ previousText = "<% $labels->{$curr_value} || $curr_value %>"
+ <% $onchange %>
+ >
+
+% if ( $opt{options} ) {
+%
+% foreach my $option ( @{ $opt{options} } ) { #just arrayref for now
+
+ <OPTION VALUE="<% $option %>"
+ <% $opt{curr_value} eq $option ? 'SELECTED' : '' %>
+ >
+ <% $labels->{$option} || $option %>
+ </OPTION>
+
+% }
+%
+% } else { #deprecated weird value hashref used only by reason.html
+%
+% my $aref = $opt{'value'}->{'values'};
+% my $vkey = $opt{'value'}->{'vcolumn'};
+% my $ckey = $opt{'value'}->{'ccolumn'};
+% foreach my $v (@$aref) {
+
+ <OPTION VALUE="<% $v->$vkey %>"
+ <% ($opt{curr_value} eq $v->$vkey) ? 'SELECTED' : '' %>
+ >
+ <% $v->$ckey %>
+ </OPTION>
+
+% }
+%
+% }
+
+ </SELECT>
+
+ </TD>
+
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+my $onchange = $opt{'onchange'}
+ ? 'onChange="'. $opt{'onchange'}. '(this)"'
+ : '';
+
+my $labels = $opt{'option_labels'} || $opt{'labels'};
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+my $curr_value = $opt{'curr_value'};
+
+</%init>
diff --git a/httemplate/elements/tr-selectlayers-select.html b/httemplate/elements/tr-selectlayers-select.html
new file mode 100644
index 0000000..af81336
--- /dev/null
+++ b/httemplate/elements/tr-selectlayers-select.html
@@ -0,0 +1 @@
+<% include('tr-selectlayers.html', @_, 'select_only'=>1 ) %>
diff --git a/httemplate/elements/tr-selectlayers.html b/httemplate/elements/tr-selectlayers.html
new file mode 100644
index 0000000..865d822
--- /dev/null
+++ b/httemplate/elements/tr-selectlayers.html
@@ -0,0 +1,25 @@
+% unless ( $opt{js_only} ) {
+
+ <% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+
+% }
+
+ <% include('selectlayers.html', @_ ) %>
+
+% unless ( $opt{js_only} ) {
+
+ </TD>
+
+ </TR>
+
+% }
+
+<%init>
+
+my %opt = @_;
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/tr-selectmultiple-part_pkg.html b/httemplate/elements/tr-selectmultiple-part_pkg.html
new file mode 100644
index 0000000..455038d
--- /dev/null
+++ b/httemplate/elements/tr-selectmultiple-part_pkg.html
@@ -0,0 +1,20 @@
+<TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Packages' %></TD>
+ <TD>
+ <% include( '/elements/select-table.html',
+ 'table' => 'part_pkg',
+ 'name_col' => 'pkg',
+ 'value' => '',
+ 'empty_label' => '(none)',
+ 'element_etc' => 'multiple',
+ %opt,
+ )
+ %>
+ </TD>
+</TR>
+
+<%init>
+
+my %opt = @_;
+
+</%init>
diff --git a/httemplate/elements/tr-td-label.html b/httemplate/elements/tr-td-label.html
new file mode 100644
index 0000000..77c0484
--- /dev/null
+++ b/httemplate/elements/tr-td-label.html
@@ -0,0 +1,17 @@
+<TR>
+
+ <TD ALIGN="right" VALIGN="top" STYLE="<% $style %>" ID="<% $opt{label_id} || $opt{id}. '_label0' %>">
+
+ <% $opt{label} %>
+
+ </TD>
+
+<%init>
+
+my %opt = @_;
+
+my $style = 'padding-top: 3px';
+$style .= '; '. $opt{'cell_style'}
+ if $opt{'cell_style'};
+
+</%init>
diff --git a/httemplate/elements/tr-title.html b/httemplate/elements/tr-title.html
new file mode 100644
index 0000000..8517737
--- /dev/null
+++ b/httemplate/elements/tr-title.html
@@ -0,0 +1,5 @@
+<TR>
+ <TD BGCOLOR="#e8e8e8" COLSPAN=2>&nbsp;</TD>
+</TR>
+
+<% include('tr-justtitle.html', @_) %>
diff --git a/httemplate/elements/xmenu.css b/httemplate/elements/xmenu.css
new file mode 100644
index 0000000..97c7da8
--- /dev/null
+++ b/httemplate/elements/xmenu.css
@@ -0,0 +1,196 @@
+
+.webfx-menu, .webfx-menu * {
+ /*
+ Set the box sizing to content box
+ in the future when IE6 supports box-sizing
+ there will be an issue to fix the sizes
+
+ There is probably an issue with IE5 mac now
+ because IE5 uses content-box but the script
+ assumes all versions of IE uses border-box.
+
+ At the time of this writing mozilla did not support
+ box-sizing for absolute positioned element.
+
+ Opera only supports content-box
+ */
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+}
+
+.webfx-menu {
+ position: absolute;
+ z-index: 100;
+ visibility: hidden;
+ border: 1px solid black;
+ padding: 1px;
+ background: white;
+ filter: progid:DXImageTransform.Microsoft.Shadow(color="#777777", Direction=135, Strength=4)
+ alpha(Opacity=95);
+ -moz-opacity: 0.95;
+ /* a drop shadow would be nice in moz/others too... */
+}
+
+.webfx-menu-empty {
+ display: block;
+ border: 1px solid white;
+ padding: 2px 5px 2px 5px;
+ font-size: 11px;
+ /* font-family: Tahoma, Verdan, Helvetica, Sans-Serif; */
+ color: black;
+}
+
+.webfx-menu a {
+ display: block;
+ /* width: expression(constExpression(ieBox ? "100%": "auto")); /* should be ignored by mz and op */
+ width: expression(constExpression(ie ? "98%": "auto")); /* should be ignored by mz and op */
+ overflow: visible;
+ /* padding: 2px 0px 2px 5px; */
+ padding: 1px 0px 1px 5px;
+ font-size: 14px;
+/* font-family: Verdana, Arial, Helvetica, sans-serif; */
+ font-weight: bold;
+ text-decoration: none;
+ vertical-align: center;
+ color: black;
+ border: 1px solid white;
+}
+
+.webfx-menu a:visited {
+ color: black;
+ border: 1px solid white;
+}
+
+.webfx-menu a:hover {
+ color: black;
+ border: 1px solid #7e0079;
+}
+
+.webfx-menu a:hover {
+ color: black;
+ /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */
+ /* background: #ffe6fe; */
+ /* background: #ffc2fe; */
+ background: #fff2fe;
+ border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/
+}
+
+.webfx-menu a .arrow {
+ float: right;
+ border: 0;
+ width: 3px;
+ margin-right: 3px;
+ margin-top: 4px;
+}
+
+/* separtor */
+.webfx-menu div {
+ height: 0;
+ height: expression(constExpression(ieBox ? "2px" : "0"));
+ border-top: 1px solid #7e0079; /* rgb(120,172,255); */
+ border-bottom: 1px solid rgb(234,242,255);
+ overflow: hidden;
+ margin: 2px 0px 2px 0px;
+ font-size: 0mm;
+}
+
+.webfx-menu-bar {
+ /* i want a vertical bar */
+ display: block;
+
+ /* background: rgb(120,172,255);/*rgb(255,128,0);*/
+ /* background: #a097ed; */
+ background: #000000;
+ /* border: 1px solid #7E0079; */
+ /* border: 1px solid #000000; */
+ /* border: none */
+ color: white;
+
+ padding: 2px;
+
+ /* IE5.0 has the wierdest box model for inline elements */
+ padding: expression(constExpression(ie50 ? "0px" : "2px"));
+}
+
+.webfx-menu-bar a,
+.webfx-menu-bar a:visited {
+ /* i want a vertical bar */
+ display: block;
+
+ /* border: 1px solid black; /*rgb(0,0,0);/*rgb(255,128,0);*/
+ /* border: 1px solid black; /* #ffffff; */
+ /* border-bottom: 1px solid black; */
+ border-bottom: 1px solid white;
+ /* border-bottom: 1px solid rgb(0,66,174);
+ /* border-bottom: 1px solid black;
+ border-bottom: 1px solid black;
+ border-bottom: 1px solid black; */
+
+ padding: 1px 5px 1px 5px;
+
+ /* color: black; */
+ color: white;
+ text-decoration: none;
+
+ /* IE5.0 Does not paint borders and padding on inline elements without a height/width */
+ height: expression(constExpression(ie50 ? "17px" : "auto"));
+}
+
+.webfx-menu-bar a:link {
+ color: white;
+}
+
+.webfx-menu-bar a:hover {
+ /* color: black; */
+ color: white;
+ /* background: rgb(120,172,255); */
+ /* background: #BC79B8; */
+ background: #7e0079;
+ /* border-left: 1px solid rgb(234,242,255);
+ border-right: 1px solid rgb(0,66,174);
+ border-top: 1px solid rgb(234,242,255);
+ border-bottom: 1px solid rgb(0,66,174); */
+}
+
+.webfx-menu-bar a .arrow {
+ float: right;
+ border: 0;
+/* vertical-align: top; */
+ width: 3px;
+ margin-right: 3px;
+ margin-top: 4px;
+}
+
+.webfx-menu-bar a:active, .webfx-menu-bar a:focus {
+ -moz-outline: none;
+ outline: none;
+ /*
+ ie does not support outline but ie55 can hide the outline using
+ a proprietary property on HTMLElement. Did I say that IE sucks at CSS?
+ */
+ ie-dummy: expression(this.hideFocus=true);
+
+/* border-left: 1px solid rgb(0,66,174); */
+/* border-right: 1px solid rgb(234,242,255); */
+/* border-top: 1px solid rgb(0,66,174); */
+/* border-bottom: 1px solid rgb(234,242,255); */
+}
+
+.webfx-menu-title {
+ color: black;
+ /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */
+ background: #7e0079;
+/* border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/
+ /* padding: 3px 1px 3px 6px; */
+ padding: 3px 1px 3px 5px;
+ display: block;
+ font-size: 16px;
+/* font-family: Verdana, Arial, Helvetica, sans-serif; */
+ font-weight: bold;
+ text-decoration: none;
+ color: white;
+/* border: 1px solid white; */
+ border-bottom: 1px solid white;
+ width: expression(constExpression(ie ? "98%": "auto")); /* should be ignored by mz and op */
+}
+
diff --git a/httemplate/elements/xmenu.js b/httemplate/elements/xmenu.js
new file mode 100644
index 0000000..134265f
--- /dev/null
+++ b/httemplate/elements/xmenu.js
@@ -0,0 +1,668 @@
+//<script>
+/*
+ * This script was created by Erik Arvidsson (erik@eae.net)
+ * for WebFX (http://webfx.eae.net)
+ * Copyright 2001
+ *
+ * For usage see license at http://webfx.eae.net/license.html
+ *
+ * Created: 2001-01-12
+ * Updates: 2001-11-20 Added hover mode support and removed Opera focus hacks
+ * 2001-12-20 Added auto positioning and some properties to support this
+ * 2002-08-13 toString used ' for attributes. Changed to " to allow in args
+ */
+
+// check browsers
+var ua = navigator.userAgent;
+var opera = /opera [56789]|opera\/[56789]/i.test(ua);
+var ie = !opera && /MSIE/.test(ua);
+var ie50 = ie && /MSIE 5\.[01234]/.test(ua);
+var ie6 = ie && /MSIE [6789]/.test(ua);
+var ieBox = ie && (document.compatMode == null || document.compatMode != "CSS1Compat");
+var moz = !opera && /gecko/i.test(ua);
+var nn6 = !opera && /netscape.*6\./i.test(ua);
+var khtml = /KHTML/i.test(ua);
+
+// define the default values
+
+webfxMenuDefaultWidth = 154;
+
+webfxMenuDefaultBorderLeft = 1;
+webfxMenuDefaultBorderRight = 1;
+webfxMenuDefaultBorderTop = 1;
+webfxMenuDefaultBorderBottom = 1;
+
+webfxMenuDefaultPaddingLeft = 1;
+webfxMenuDefaultPaddingRight = 1;
+webfxMenuDefaultPaddingTop = 1;
+webfxMenuDefaultPaddingBottom = 1;
+
+webfxMenuDefaultShadowLeft = 0;
+webfxMenuDefaultShadowRight = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 :0;
+webfxMenuDefaultShadowTop = 0;
+webfxMenuDefaultShadowBottom = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 : 0;
+
+
+webfxMenuItemDefaultHeight = 18;
+webfxMenuItemDefaultText = "Untitled";
+webfxMenuItemDefaultHref = "javascript:void(0)";
+
+webfxMenuSeparatorDefaultHeight = 6;
+
+webfxMenuDefaultEmptyText = "Empty";
+
+webfxMenuDefaultUseAutoPosition = nn6 ? false : true;
+
+
+
+// other global constants
+
+webfxMenuImagePath = "";
+
+webfxMenuUseHover = opera ? true : false;
+webfxMenuHideTime = 500;
+webfxMenuShowTime = 200;
+
+
+
+var webFXMenuHandler = {
+ idCounter : 0,
+ idPrefix : "webfx-menu-object-",
+ all : {},
+ getId : function () { return this.idPrefix + this.idCounter++; },
+ overMenuItem : function (oItem) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+ var jsItem = this.all[oItem.id];
+ if (webfxMenuShowTime <= 0)
+ this._over(jsItem);
+ else if ( jsItem )
+ //this.showTimeout = window.setTimeout(function () { webFXMenuHandler._over(jsItem) ; }, webfxMenuShowTime);
+ // I hate IE5.0 because the piece of shit crashes when using setTimeout with a function object
+ this.showTimeout = window.setTimeout("webFXMenuHandler._over(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuShowTime);
+ },
+ outMenuItem : function (oItem) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+ var jsItem = this.all[oItem.id];
+ if (webfxMenuHideTime <= 0)
+ this._out(jsItem);
+ else if ( jsItem )
+ //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime);
+ this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime);
+ },
+ blurMenu : function (oMenuItem) {
+ window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime);
+ },
+ _over : function (jsItem) {
+ if (jsItem.subMenu) {
+ jsItem.parentMenu.hideAllSubs();
+ jsItem.subMenu.show();
+ }
+ else
+ jsItem.parentMenu.hideAllSubs();
+ },
+ _out : function (jsItem) {
+ // find top most menu
+ var root = jsItem;
+ var m;
+ if (root instanceof WebFXMenuButton)
+ m = root.subMenu;
+ else {
+ m = jsItem.parentMenu;
+ while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar))
+ m = m.parentMenu;
+ }
+ if (m != null)
+ m.hide();
+ },
+ hideMenu : function (menu) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+
+ this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime);
+ },
+ showMenu : function (menu, src, dir) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+
+ if (arguments.length < 3)
+ dir = "vertical";
+
+ menu.show(src, dir);
+ }
+};
+
+function WebFXMenu() {
+ this._menuItems = [];
+ this._subMenus = [];
+ this.id = webFXMenuHandler.getId();
+ this.top = 0;
+ this.left = 0;
+ this.shown = false;
+ this.parentMenu = null;
+ webFXMenuHandler.all[this.id] = this;
+}
+
+WebFXMenu.prototype.width = webfxMenuDefaultWidth;
+WebFXMenu.prototype.emptyText = webfxMenuDefaultEmptyText;
+WebFXMenu.prototype.useAutoPosition = webfxMenuDefaultUseAutoPosition;
+
+WebFXMenu.prototype.borderLeft = webfxMenuDefaultBorderLeft;
+WebFXMenu.prototype.borderRight = webfxMenuDefaultBorderRight;
+WebFXMenu.prototype.borderTop = webfxMenuDefaultBorderTop;
+WebFXMenu.prototype.borderBottom = webfxMenuDefaultBorderBottom;
+
+WebFXMenu.prototype.paddingLeft = webfxMenuDefaultPaddingLeft;
+WebFXMenu.prototype.paddingRight = webfxMenuDefaultPaddingRight;
+WebFXMenu.prototype.paddingTop = webfxMenuDefaultPaddingTop;
+WebFXMenu.prototype.paddingBottom = webfxMenuDefaultPaddingBottom;
+
+WebFXMenu.prototype.shadowLeft = webfxMenuDefaultShadowLeft;
+WebFXMenu.prototype.shadowRight = webfxMenuDefaultShadowRight;
+WebFXMenu.prototype.shadowTop = webfxMenuDefaultShadowTop;
+WebFXMenu.prototype.shadowBottom = webfxMenuDefaultShadowBottom;
+
+
+
+WebFXMenu.prototype.add = function (menuItem) {
+ this._menuItems[this._menuItems.length] = menuItem;
+ if (menuItem.subMenu) {
+ this._subMenus[this._subMenus.length] = menuItem.subMenu;
+ menuItem.subMenu.parentMenu = this;
+ }
+
+ menuItem.parentMenu = this;
+};
+
+WebFXMenu.prototype.show = function (relObj, sDir) {
+ if (this.useAutoPosition)
+ this.position(relObj, sDir);
+
+ var divElement = document.getElementById(this.id);
+ if ( divElement ) {
+
+ divElement.style.left = opera ? this.left : this.left + "px";
+ divElement.style.top = opera ? this.top : this.top + "px";
+ divElement.style.visibility = "visible";
+
+ if ( ie ) {
+ var shimElement = document.getElementById(this.id + "Shim");
+ if ( shimElement ) {
+ shimElement.style.width = divElement.offsetWidth;
+ shimElement.style.height = divElement.offsetHeight;
+ shimElement.style.top = divElement.style.top;
+ shimElement.style.left = divElement.style.left;
+ /*shimElement.style.zIndex = divElement.style.zIndex - 1; */
+ shimElement.style.display = "block";
+ shimElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
+ }
+ }
+
+ }
+
+ this.shown = true;
+
+ if (this.parentMenu)
+ this.parentMenu.show();
+};
+
+WebFXMenu.prototype.hide = function () {
+ this.hideAllSubs();
+ var divElement = document.getElementById(this.id);
+ if ( divElement ) {
+ divElement.style.visibility = "hidden";
+ if ( ie ) {
+ var shimElement = document.getElementById(this.id + "Shim");
+ if ( shimElement ) {
+ shimElement.style.display = "none";
+ }
+ }
+ }
+
+ this.shown = false;
+};
+
+WebFXMenu.prototype.hideAllSubs = function () {
+ for (var i = 0; i < this._subMenus.length; i++) {
+ if (this._subMenus[i].shown)
+ this._subMenus[i].hide();
+ }
+};
+
+WebFXMenu.prototype.toString = function () {
+ var top = this.top + this.borderTop + this.paddingTop;
+ var str = "<div id='" + this.id + "' class='webfx-menu' style='" +
+ "width:" + (!ieBox ?
+ this.width - this.borderLeft - this.paddingLeft - this.borderRight - this.paddingRight :
+ this.width) + "px;" +
+ (this.useAutoPosition ?
+ "left:" + this.left + "px;" + "top:" + this.top + "px;" :
+ "") +
+ (ie50 ? "filter: none;" : "") +
+ "'>";
+
+ if (this._menuItems.length == 0) {
+ str += "<span class='webfx-menu-empty'>" + this.emptyText + "</span>";
+ }
+ else {
+ str += '<span class="webfx-menu-title" onmouseover="webFXMenuHandler.overMenuItem(this)"' +
+ (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+ '>' + this.emptyText + '</span>';
+ // str += '<div id="' + this.id + '-title">' + this.emptyText + '</div>';
+ // loop through all menuItems
+ for (var i = 0; i < this._menuItems.length; i++) {
+ var mi = this._menuItems[i];
+ str += mi;
+ if (!this.useAutoPosition) {
+ if (mi.subMenu && !mi.subMenu.useAutoPosition)
+ mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop;
+ top += mi.height;
+ }
+ }
+
+ }
+
+ str += "</div>";
+
+ if ( ie ) {
+ str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>";
+ }
+
+ for (var i = 0; i < this._subMenus.length; i++) {
+ this._subMenus[i].left = this.left + this.width - this._subMenus[i].borderLeft;
+ str += this._subMenus[i];
+ }
+
+ return str;
+};
+// WebFXMenu.prototype.position defined later
+
+function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) {
+ this.text = sText || webfxMenuItemDefaultText;
+ this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref;
+ this.subMenu = oSubMenu;
+ if (oSubMenu)
+ oSubMenu.parentMenuItem = this;
+ this.toolTip = sToolTip;
+ this.id = webFXMenuHandler.getId();
+ webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight;
+WebFXMenuItem.prototype.toString = function () {
+ return "<a" +
+ " id='" + this.id + "'" +
+ " href=\"" + this.href + "\"" +
+ (this.toolTip ? " title=\"" + this.toolTip + "\"" : "") +
+ " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+ (this.subMenu ? " unselectable='on' tabindex='-1'" : "") +
+ ">" +
+ (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") +
+ this.text +
+ "</a>";
+};
+
+
+function WebFXMenuSeparator() {
+ this.id = webFXMenuHandler.getId();
+ webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight;
+WebFXMenuSeparator.prototype.toString = function () {
+ return "<div" +
+ " id='" + this.id + "'" +
+ (webfxMenuUseHover ?
+ " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ " onmouseout='webFXMenuHandler.outMenuItem(this)'"
+ :
+ "") +
+ "></div>"
+};
+
+function WebFXMenuBar() {
+ this._parentConstructor = WebFXMenu;
+ this._parentConstructor();
+}
+WebFXMenuBar.prototype = new WebFXMenu;
+WebFXMenuBar.prototype.toString = function () {
+ var str = "<div id='" + this.id + "' class='webfx-menu-bar'>";
+
+ // loop through all menuButtons
+ for (var i = 0; i < this._menuItems.length; i++)
+ str += this._menuItems[i];
+
+ str += "</div>";
+
+ for (var i = 0; i < this._subMenus.length; i++)
+ str += this._subMenus[i];
+
+ return str;
+};
+
+function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) {
+ this._parentConstructor = WebFXMenuItem;
+ this._parentConstructor(sText, sHref, sToolTip, oSubMenu);
+}
+WebFXMenuButton.prototype = new WebFXMenuItem;
+WebFXMenuButton.prototype.toString = function () {
+ return "<a" +
+ " id='" + this.id + "'" +
+ " href='" + this.href + "'" +
+ (this.toolTip ? " title='" + this.toolTip + "'" : "") +
+ (webfxMenuUseHover ?
+ (" onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ " onmouseout='webFXMenuHandler.outMenuItem(this)'") :
+ (
+ " onfocus='webFXMenuHandler.overMenuItem(this)'" +
+ (this.subMenu ?
+ " onblur='webFXMenuHandler.blurMenu(this)'" :
+ ""
+ )
+ )) +
+ ">" +
+ (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.right.png'>" : "") +
+ this.text +
+ "</a>";
+};
+
+
+
+
+
+/* Position functions */
+
+
+function getInnerLeft(el) {
+
+ if (el == null) return 0;
+
+ if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+ return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) );
+
+}
+
+
+
+function getLeft(el, debug) {
+
+ if (el == null) return 0;
+
+ //if ( debug )
+ // alert ( el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) );
+
+ return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) );
+
+}
+
+
+
+function getInnerTop(el) {
+
+ if (el == null) return 0;
+
+ if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+ return parseInt( getTop(el) + parseInt(getBorderTop(el)) );
+
+}
+
+
+
+function getTop(el) {
+
+ if (el == null) return 0;
+
+ return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) );
+
+}
+
+
+
+function getBorderLeft(el) {
+
+ return ie ?
+
+ el.clientLeft :
+
+ ( khtml
+ ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ );
+
+}
+
+
+
+function getBorderTop(el) {
+
+ return ie ?
+
+ el.clientTop :
+
+ ( khtml
+ ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width"))
+ );
+
+}
+
+
+
+function opera_getLeft(el) {
+
+ if (el == null) return 0;
+
+ return el.offsetLeft + opera_getLeft(el.offsetParent);
+
+}
+
+
+
+function opera_getTop(el) {
+
+ if (el == null) return 0;
+
+ return el.offsetTop + opera_getTop(el.offsetParent);
+
+}
+
+
+
+function getOuterRect(el, debug) {
+
+ return {
+
+ left: (opera ? opera_getLeft(el) : getLeft(el, debug)),
+
+ top: (opera ? opera_getTop(el) : getTop(el)),
+
+ width: el.offsetWidth,
+
+ height: el.offsetHeight
+
+ };
+
+}
+
+
+
+// mozilla bug! scrollbars not included in innerWidth/height
+
+function getDocumentRect(el) {
+
+ return {
+
+ left: 0,
+
+ top: 0,
+
+ width: (ie ?
+
+ (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) :
+
+ window.innerWidth
+
+ ),
+
+ height: (ie ?
+
+ (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) :
+
+ window.innerHeight
+
+ )
+
+ };
+
+}
+
+
+
+function getScrollPos(el) {
+
+ return {
+
+ left: (ie ?
+
+ (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) :
+
+ window.pageXOffset
+
+ ),
+
+ top: (ie ?
+
+ (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) :
+
+ window.pageYOffset
+
+ )
+
+ };
+
+}
+
+
+/* end position functions */
+
+WebFXMenu.prototype.position = function (relEl, sDir) {
+ var dir = sDir;
+ // find parent item rectangle, piRect
+ var piRect;
+ if (!relEl) {
+ var pi = this.parentMenuItem;
+ if (!this.parentMenuItem)
+ return;
+
+ relEl = document.getElementById(pi.id);
+ if (dir == null)
+ dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal";
+ //alert('created RelEl from parent: ' + pi.id);
+ piRect = getOuterRect(relEl, 1);
+ }
+ else if (relEl.left != null && relEl.top != null && relEl.width != null && relEl.height != null) { // got a rect
+ //alert('passed a Rect as RelEl: ' + typeof(relEl));
+
+ piRect = relEl;
+ }
+ else {
+ //alert('passed an element as RelEl: ' + typeof(relEl));
+ piRect = getOuterRect(relEl);
+ }
+
+ var menuEl = document.getElementById(this.id);
+ var menuRect = getOuterRect(menuEl);
+ var docRect = getDocumentRect();
+ var scrollPos = getScrollPos();
+ var pMenu = this.parentMenu;
+
+ if (dir == "vertical") {
+ if (piRect.left + menuRect.width - scrollPos.left <= docRect.width) {
+ //alert('piRect.left: ' + piRect.left);
+ this.left = piRect.left;
+ if ( ! ie )
+ this.left = this.left + 138;
+ } else if (docRect.width >= menuRect.width) {
+ //konq (not safari though) winds up here by accident and positions the menus all weird
+ //alert('docRect.width + scrollPos.left - menuRect.width');
+
+ this.left = docRect.width + scrollPos.left - menuRect.width;
+ } else {
+ //alert('scrollPos.left: ' + scrollPos.left);
+ this.left = scrollPos.left;
+ }
+
+ if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top)
+
+ this.top = piRect.top + piRect.height;
+
+ else if (piRect.top - menuRect.height >= scrollPos.top)
+
+ this.top = piRect.top - menuRect.height;
+
+ else if (docRect.height >= menuRect.height)
+
+ this.top = docRect.height + scrollPos.top - menuRect.height;
+
+ else
+
+ this.top = scrollPos.top;
+ }
+ else {
+ if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top)
+
+ this.top = piRect.top - this.borderTop - this.paddingTop;
+
+ else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0)
+
+ this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom;
+
+ else if (docRect.height >= menuRect.height)
+
+ this.top = docRect.height + scrollPos.top - menuRect.height;
+
+ else
+
+ this.top = scrollPos.top;
+
+
+
+ var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0;
+
+ var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0;
+
+ var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0;
+
+ var pMenuBorderRight = pMenu ? pMenu.borderRight : 0;
+
+
+
+ if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight +
+
+ pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left)
+
+ this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft;
+
+ else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0)
+
+ this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight;
+
+ else if (docRect.width >= menuRect.width)
+
+ this.left = docRect.width + scrollPos.left - menuRect.width;
+
+ else
+
+ this.left = scrollPos.left;
+ }
+};
diff --git a/httemplate/elements/xmenu.top.css b/httemplate/elements/xmenu.top.css
new file mode 100644
index 0000000..7591703
--- /dev/null
+++ b/httemplate/elements/xmenu.top.css
@@ -0,0 +1,211 @@
+
+.webfx-menu, .webfx-menu * {
+ /*
+ Set the box sizing to content box
+ in the future when IE6 supports box-sizing
+ there will be an issue to fix the sizes
+
+ There is probably an issue with IE5 mac now
+ because IE5 uses content-box but the script
+ assumes all versions of IE uses border-box.
+
+ At the time of this writing mozilla did not support
+ box-sizing for absolute positioned element.
+
+ Opera only supports content-box
+ */
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+}
+
+.webfx-menu {
+ position: absolute;
+ z-index: 100;
+ visibility: hidden;
+ border: 1px solid black;
+ padding: 1px;
+ background: white;
+ filter: progid:DXImageTransform.Microsoft.Shadow(color="#777777", Direction=135, Strength=4)
+ alpha(Opacity=95);
+ -moz-opacity: 0.95;
+ /* a drop shadow would be nice in moz/others too... */
+}
+
+.webfx-menu-empty {
+ display: block;
+ border: 1px solid white;
+ padding: 2px 5px 2px 5px;
+ font-size: 11px;
+ /* font-family: Tahoma, Verdan, Helvetica, Sans-Serif; */
+ color: black;
+}
+
+.webfx-menu a {
+ display: block;
+ /* width: expression(constExpression(ieBox ? "100%": "auto")); /* should be ignored by mz and op */
+ width: expression(constExpression(ie ? "98%": "auto")); /* should be ignored by mz and op */
+ overflow: visible;
+ /* padding: 2px 0px 2px 5px; */
+ padding: 1px 0px 1px 5px;
+ font-size: 14px;
+/* font-family: Verdana, Arial, Helvetica, sans-serif; */
+ font-weight: bold;
+ text-decoration: none;
+ vertical-align: center;
+ color: black;
+ border: 1px solid white;
+}
+
+.webfx-menu a:visited {
+ color: black;
+ border: 1px solid white;
+}
+
+.webfx-menu a:hover {
+ color: black;
+ border: 1px solid #7e0079;
+}
+
+.webfx-menu a:hover {
+ color: black;
+ /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */
+ /* background: #ffe6fe; */
+ /* background: #ffc2fe; */
+ background: #fff2fe;
+ border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/
+}
+
+.webfx-menu a .arrow {
+ float: right;
+ border: 0;
+ width: 3px;
+ margin-right: 3px;
+ margin-top: 4px;
+}
+
+/* separtor */
+.webfx-menu div {
+ height: 0;
+ height: expression(constExpression(ieBox ? "2px" : "0"));
+ border-top: 1px solid #7e0079; /* rgb(120,172,255); */
+ border-bottom: 1px solid rgb(234,242,255);
+ overflow: hidden;
+ margin: 2px 0px 2px 0px;
+ font-size: 0mm;
+}
+
+.webfx-menu-bar {
+ /* background: rgb(120,172,255);/*rgb(255,128,0);*/
+ /* background: #a097ed; */
+ background: #000000;
+ /* border: 1px solid #7E0079; */
+ /* border: 1px solid #000000; */
+ /* border: none */
+ color: white;
+
+ padding: 2px;
+
+ /* IE5.0 has the wierdest box model for inline elements */
+ padding: expression(constExpression(ie50 ? "0px" : "2px"));
+}
+
+.webfx-menu-bar a,
+.webfx-menu-bar a:visited {
+ /* i want a vertical bar */
+ /* display: block; */
+
+ /* border: 1px solid black; /*rgb(0,0,0);/*rgb(255,128,0);*/
+ /* border: 1px solid black; /* #ffffff; */
+ /* border-bottom: 1px solid black; */
+ /* border-bottom: 1px solid white; */
+ /* border-bottom: 1px solid rgb(0,66,174);
+ /* border-bottom: 1px solid black;
+ border-bottom: 1px solid black;
+ border-bottom: 1px solid black; */
+
+ padding: 1px 5px 1px 5px;
+
+ /* color: black; */
+ color: white;
+ text-decoration: none;
+
+ /* IE5.0 Does not paint borders and padding on inline elements without a height/width */
+ height: expression(constExpression(ie50 ? "17px" : "auto"));
+
+ background-color:#333333;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+
+ margin-right: 4px
+
+}
+
+.webfx-menu-bar a:link {
+ color: white;
+}
+
+.webfx-menu-bar a:hover {
+ /* color: black; */
+ color: white;
+ /* background: rgb(120,172,255); */
+ /* background: #BC79B8; */
+ background: #7e0079;
+ /* border-left: 1px solid rgb(234,242,255);
+ border-right: 1px solid rgb(0,66,174);
+ border-top: 1px solid rgb(234,242,255);
+ border-bottom: 1px solid rgb(0,66,174); */
+
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+
+}
+
+.webfx-menu-bar a .arrow {
+ /* float: right; */
+ border: 0;
+/* vertical-align: top; */
+/* width: 3px; */
+/* margin-right: 3px; */
+ margin-bottom: 2px;
+
+}
+
+.webfx-menu-bar a:active, .webfx-menu-bar a:focus {
+ -moz-outline: none;
+ outline: none;
+ /*
+ ie does not support outline but ie55 can hide the outline using
+ a proprietary property on HTMLElement. Did I say that IE sucks at CSS?
+ */
+ ie-dummy: expression(this.hideFocus=true);
+
+/* border-left: 1px solid rgb(0,66,174); */
+/* border-right: 1px solid rgb(234,242,255); */
+/* border-top: 1px solid rgb(0,66,174); */
+/* border-bottom: 1px solid rgb(234,242,255); */
+}
+
+.webfx-menu-title {
+ color: black;
+ /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */
+ background: #7e0079;
+/* border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/
+ /* padding: 3px 1px 3px 6px; */
+ padding: 3px 1px 3px 5px;
+ display: block;
+ font-size: 16px;
+/* font-family: Verdana, Arial, Helvetica, sans-serif; */
+ font-weight: bold;
+ text-decoration: none;
+ color: white;
+/* border: 1px solid white; */
+ border-bottom: 1px solid white;
+ width: expression(constExpression(ie ? "98%": "auto")); /* should be ignored by mz and op */
+}
+
diff --git a/httemplate/elements/xmenu.top.js b/httemplate/elements/xmenu.top.js
new file mode 100644
index 0000000..8d81035
--- /dev/null
+++ b/httemplate/elements/xmenu.top.js
@@ -0,0 +1,671 @@
+//<script>
+/*
+ * This script was created by Erik Arvidsson (erik@eae.net)
+ * for WebFX (http://webfx.eae.net)
+ * Copyright 2001
+ *
+ * For usage see license at http://webfx.eae.net/license.html
+ *
+ * Created: 2001-01-12
+ * Updates: 2001-11-20 Added hover mode support and removed Opera focus hacks
+ * 2001-12-20 Added auto positioning and some properties to support this
+ * 2002-08-13 toString used ' for attributes. Changed to " to allow in args
+ */
+
+// check browsers
+var ua = navigator.userAgent;
+var opera = /opera [56789]|opera\/[56789]/i.test(ua);
+var ie = !opera && /MSIE/.test(ua);
+var ie50 = ie && /MSIE 5\.[01234]/.test(ua);
+var ie6 = ie && /MSIE [6789]/.test(ua);
+var ieBox = ie && (document.compatMode == null || document.compatMode != "CSS1Compat");
+var moz = !opera && /gecko/i.test(ua);
+var nn6 = !opera && /netscape.*6\./i.test(ua);
+var khtml = /KHTML/i.test(ua);
+
+// define the default values
+
+webfxMenuDefaultWidth = 154;
+
+webfxMenuDefaultBorderLeft = 1;
+webfxMenuDefaultBorderRight = 1;
+webfxMenuDefaultBorderTop = 1;
+webfxMenuDefaultBorderBottom = 1;
+
+webfxMenuDefaultPaddingLeft = 1;
+webfxMenuDefaultPaddingRight = 1;
+webfxMenuDefaultPaddingTop = 1;
+webfxMenuDefaultPaddingBottom = 1;
+
+webfxMenuDefaultShadowLeft = 0;
+webfxMenuDefaultShadowRight = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 :0;
+webfxMenuDefaultShadowTop = 0;
+webfxMenuDefaultShadowBottom = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 : 0;
+
+
+webfxMenuItemDefaultHeight = 18;
+webfxMenuItemDefaultText = "Untitled";
+webfxMenuItemDefaultHref = "javascript:void(0)";
+
+webfxMenuSeparatorDefaultHeight = 6;
+
+webfxMenuDefaultEmptyText = "Empty";
+
+webfxMenuDefaultUseAutoPosition = nn6 ? false : true;
+
+
+
+// other global constants
+
+webfxMenuImagePath = "";
+
+webfxMenuUseHover = opera ? true : false;
+webfxMenuHideTime = 500;
+webfxMenuShowTime = 200;
+
+
+
+var webFXMenuHandler = {
+ idCounter : 0,
+ idPrefix : "webfx-menu-object-",
+ all : {},
+ getId : function () { return this.idPrefix + this.idCounter++; },
+ overMenuItem : function (oItem) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+ var jsItem = this.all[oItem.id];
+ if (webfxMenuShowTime <= 0)
+ this._over(jsItem);
+ else if ( jsItem )
+ //this.showTimeout = window.setTimeout(function () { webFXMenuHandler._over(jsItem) ; }, webfxMenuShowTime);
+ // I hate IE5.0 because the piece of shit crashes when using setTimeout with a function object
+ this.showTimeout = window.setTimeout("webFXMenuHandler._over(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuShowTime);
+ },
+ outMenuItem : function (oItem) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+ var jsItem = this.all[oItem.id];
+ if (webfxMenuHideTime <= 0)
+ this._out(jsItem);
+ else if ( jsItem )
+ //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime);
+ this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime);
+ },
+ blurMenu : function (oMenuItem) {
+ window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime);
+ },
+ _over : function (jsItem) {
+ if (jsItem.subMenu) {
+ jsItem.parentMenu.hideAllSubs();
+ jsItem.subMenu.show();
+ }
+ else
+ jsItem.parentMenu.hideAllSubs();
+ },
+ _out : function (jsItem) {
+ // find top most menu
+ var root = jsItem;
+ var m;
+ if (root instanceof WebFXMenuButton)
+ m = root.subMenu;
+ else {
+ m = jsItem.parentMenu;
+ while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar))
+ m = m.parentMenu;
+ }
+ if (m != null)
+ m.hide();
+ },
+ hideMenu : function (menu) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+
+ this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime);
+ },
+ showMenu : function (menu, src, dir) {
+ if (this.showTimeout != null)
+ window.clearTimeout(this.showTimeout);
+ if (this.hideTimeout != null)
+ window.clearTimeout(this.hideTimeout);
+
+ if (arguments.length < 3)
+ dir = "vertical";
+
+ menu.show(src, dir);
+ }
+};
+
+function WebFXMenu() {
+ this._menuItems = [];
+ this._subMenus = [];
+ this.id = webFXMenuHandler.getId();
+ this.top = 0;
+ this.left = 0;
+ this.shown = false;
+ this.parentMenu = null;
+ webFXMenuHandler.all[this.id] = this;
+}
+
+WebFXMenu.prototype.width = webfxMenuDefaultWidth;
+WebFXMenu.prototype.emptyText = webfxMenuDefaultEmptyText;
+WebFXMenu.prototype.useAutoPosition = webfxMenuDefaultUseAutoPosition;
+
+WebFXMenu.prototype.borderLeft = webfxMenuDefaultBorderLeft;
+WebFXMenu.prototype.borderRight = webfxMenuDefaultBorderRight;
+WebFXMenu.prototype.borderTop = webfxMenuDefaultBorderTop;
+WebFXMenu.prototype.borderBottom = webfxMenuDefaultBorderBottom;
+
+WebFXMenu.prototype.paddingLeft = webfxMenuDefaultPaddingLeft;
+WebFXMenu.prototype.paddingRight = webfxMenuDefaultPaddingRight;
+WebFXMenu.prototype.paddingTop = webfxMenuDefaultPaddingTop;
+WebFXMenu.prototype.paddingBottom = webfxMenuDefaultPaddingBottom;
+
+WebFXMenu.prototype.shadowLeft = webfxMenuDefaultShadowLeft;
+WebFXMenu.prototype.shadowRight = webfxMenuDefaultShadowRight;
+WebFXMenu.prototype.shadowTop = webfxMenuDefaultShadowTop;
+WebFXMenu.prototype.shadowBottom = webfxMenuDefaultShadowBottom;
+
+
+
+WebFXMenu.prototype.add = function (menuItem) {
+ this._menuItems[this._menuItems.length] = menuItem;
+ if (menuItem.subMenu) {
+ this._subMenus[this._subMenus.length] = menuItem.subMenu;
+ menuItem.subMenu.parentMenu = this;
+ }
+
+ menuItem.parentMenu = this;
+};
+
+WebFXMenu.prototype.show = function (relObj, sDir) {
+ if (this.useAutoPosition)
+ this.position(relObj, sDir);
+
+ var divElement = document.getElementById(this.id);
+ if ( divElement ) {
+
+ divElement.style.left = opera ? this.left : this.left + "px";
+ divElement.style.top = opera ? this.top : this.top + "px";
+ divElement.style.visibility = "visible";
+
+ if ( ie ) {
+ var shimElement = document.getElementById(this.id + "Shim");
+ if ( shimElement ) {
+ shimElement.style.width = divElement.offsetWidth;
+ shimElement.style.height = divElement.offsetHeight;
+ shimElement.style.top = divElement.style.top;
+ shimElement.style.left = divElement.style.left;
+ /*shimElement.style.zIndex = divElement.style.zIndex - 1; */
+ shimElement.style.display = "block";
+ shimElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
+ }
+ }
+
+ }
+
+ this.shown = true;
+
+ if (this.parentMenu)
+ this.parentMenu.show();
+};
+
+WebFXMenu.prototype.hide = function () {
+ this.hideAllSubs();
+ var divElement = document.getElementById(this.id);
+ if ( divElement ) {
+ divElement.style.visibility = "hidden";
+ if ( ie ) {
+ var shimElement = document.getElementById(this.id + "Shim");
+ if ( shimElement ) {
+ shimElement.style.display = "none";
+ }
+ }
+ }
+
+ this.shown = false;
+};
+
+WebFXMenu.prototype.hideAllSubs = function () {
+ for (var i = 0; i < this._subMenus.length; i++) {
+ if (this._subMenus[i].shown)
+ this._subMenus[i].hide();
+ }
+};
+
+WebFXMenu.prototype.toString = function () {
+ var top = this.top + this.borderTop + this.paddingTop;
+ var str = "<div id='" + this.id + "' class='webfx-menu' style='" +
+ "width:" + (!ieBox ?
+ this.width - this.borderLeft - this.paddingLeft - this.borderRight - this.paddingRight :
+ this.width) + "px;" +
+ (this.useAutoPosition ?
+ "left:" + this.left + "px;" + "top:" + this.top + "px;" :
+ "") +
+ (ie50 ? "filter: none;" : "") +
+ "'>";
+
+ if (this._menuItems.length == 0) {
+ str += "<span class='webfx-menu-empty'>" + this.emptyText + "</span>";
+ }
+ else {
+ str += '<span class="webfx-menu-title" onmouseover="webFXMenuHandler.overMenuItem(this)"' +
+ (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+ '>' + this.emptyText + '</span>';
+ // str += '<div id="' + this.id + '-title">' + this.emptyText + '</div>';
+ // loop through all menuItems
+ for (var i = 0; i < this._menuItems.length; i++) {
+ var mi = this._menuItems[i];
+ str += mi;
+ if (!this.useAutoPosition) {
+ if (mi.subMenu && !mi.subMenu.useAutoPosition)
+ mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop;
+ top += mi.height;
+ }
+ }
+
+ }
+
+ str += "</div>";
+
+ if ( ie ) {
+ str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>";
+ }
+
+ for (var i = 0; i < this._subMenus.length; i++) {
+ this._subMenus[i].left = this.left + this.width - this._subMenus[i].borderLeft;
+ str += this._subMenus[i];
+ }
+
+ return str;
+};
+// WebFXMenu.prototype.position defined later
+
+function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) {
+ this.text = sText || webfxMenuItemDefaultText;
+ this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref;
+ this.subMenu = oSubMenu;
+ if (oSubMenu)
+ oSubMenu.parentMenuItem = this;
+ this.toolTip = sToolTip;
+ this.id = webFXMenuHandler.getId();
+ webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight;
+WebFXMenuItem.prototype.toString = function () {
+ return "<a" +
+ " id='" + this.id + "'" +
+ " href=\"" + this.href + "\"" +
+ (this.toolTip ? " title=\"" + this.toolTip + "\"" : "") +
+ " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") +
+ (this.subMenu ? " unselectable='on' tabindex='-1'" : "") +
+ ">" +
+ (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") +
+ this.text +
+ "</a>";
+};
+
+
+function WebFXMenuSeparator() {
+ this.id = webFXMenuHandler.getId();
+ webFXMenuHandler.all[this.id] = this;
+};
+WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight;
+WebFXMenuSeparator.prototype.toString = function () {
+ return "<div" +
+ " id='" + this.id + "'" +
+ (webfxMenuUseHover ?
+ " onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ " onmouseout='webFXMenuHandler.outMenuItem(this)'"
+ :
+ "") +
+ "></div>"
+};
+
+function WebFXMenuBar() {
+ this._parentConstructor = WebFXMenu;
+ this._parentConstructor();
+}
+WebFXMenuBar.prototype = new WebFXMenu;
+WebFXMenuBar.prototype.toString = function () {
+ var str = "<div id='" + this.id + "' class='webfx-menu-bar'>";
+
+ // loop through all menuButtons
+ for (var i = 0; i < this._menuItems.length; i++)
+ str += this._menuItems[i];
+
+ str += "</div>";
+
+ for (var i = 0; i < this._subMenus.length; i++)
+ str += this._subMenus[i];
+
+ return str;
+};
+
+function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) {
+ this._parentConstructor = WebFXMenuItem;
+ this._parentConstructor(sText, sHref, sToolTip, oSubMenu);
+}
+WebFXMenuButton.prototype = new WebFXMenuItem;
+WebFXMenuButton.prototype.toString = function () {
+ return "<a" +
+ " id='" + this.id + "'" +
+ " href='" + this.href + "'" +
+ (this.toolTip ? " title='" + this.toolTip + "'" : "") +
+ (webfxMenuUseHover ?
+ (" onmouseover='webFXMenuHandler.overMenuItem(this)'" +
+ " onmouseout='webFXMenuHandler.outMenuItem(this)'") :
+ (
+ " onfocus='webFXMenuHandler.overMenuItem(this)'" +
+ (this.subMenu ?
+ " onblur='webFXMenuHandler.blurMenu(this)'" :
+ ""
+ )
+ )) +
+ ">" +
+ this.text +
+ (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.down.png'>" : "") +
+ "</a>";
+};
+
+
+
+
+
+/* Position functions */
+
+
+function getInnerLeft(el, debug) {
+
+ if (el == null) return 0;
+
+ if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+ //if ( debug )
+ // alert ( 'getInnerLeft: ' + getLeft(el) + ' - ' + getBorderLeft(el) );
+
+ return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) );
+
+}
+
+
+
+function getLeft(el, debug) {
+
+ if (el == null) return 0;
+
+ //if ( debug )
+ // alert ( el + ': ' + el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) );
+
+ return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) );
+
+}
+
+
+
+function getInnerTop(el) {
+
+ if (el == null) return 0;
+
+ if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0;
+
+ return parseInt( getTop(el) + parseInt(getBorderTop(el)) );
+
+}
+
+
+
+function getTop(el) {
+
+ if (el == null) return 0;
+
+ return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) );
+
+}
+
+
+
+function getBorderLeft(el) {
+
+ return ie ?
+
+ el.clientLeft :
+
+ ( khtml
+ ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ );
+
+}
+
+
+
+function getBorderTop(el) {
+
+ return ie ?
+
+ el.clientTop :
+
+ ( khtml
+ ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width"))
+ : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width"))
+ );
+
+}
+
+
+
+function opera_getLeft(el) {
+
+ if (el == null) return 0;
+
+ return el.offsetLeft + opera_getLeft(el.offsetParent);
+
+}
+
+
+
+function opera_getTop(el) {
+
+ if (el == null) return 0;
+
+ return el.offsetTop + opera_getTop(el.offsetParent);
+
+}
+
+
+
+function getOuterRect(el, debug) {
+
+ return {
+
+ left: (opera ? opera_getLeft(el) : getLeft(el, debug)),
+
+ top: (opera ? opera_getTop(el) : getTop(el)),
+
+ width: el.offsetWidth,
+
+ height: el.offsetHeight
+
+ };
+
+}
+
+
+
+// mozilla bug! scrollbars not included in innerWidth/height
+
+function getDocumentRect(el) {
+
+ return {
+
+ left: 0,
+
+ top: 0,
+
+ width: (ie ?
+
+ (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) :
+
+ window.innerWidth
+
+ ),
+
+ height: (ie ?
+
+ (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) :
+
+ window.innerHeight
+
+ )
+
+ };
+
+}
+
+
+
+function getScrollPos(el) {
+
+ return {
+
+ left: (ie ?
+
+ (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) :
+
+ window.pageXOffset
+
+ ),
+
+ top: (ie ?
+
+ (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) :
+
+ window.pageYOffset
+
+ )
+
+ };
+
+}
+
+
+/* end position functions */
+
+WebFXMenu.prototype.position = function (relEl, sDir) {
+ var dir = sDir;
+ // find parent item rectangle, piRect
+ var piRect;
+ if (!relEl) {
+ var pi = this.parentMenuItem;
+ if (!this.parentMenuItem)
+ return;
+
+ relEl = document.getElementById(pi.id);
+ if (dir == null)
+ dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal";
+ //alert('created RelEl from parent: ' + pi.id);
+ piRect = getOuterRect(relEl, 1);
+ }
+ else if (relEl.left != null && relEl.top != null && relEl.width != null && relEl.height != null) { // got a rect
+ //alert('passed a Rect as RelEl: ' + typeof(relEl));
+
+ piRect = relEl;
+ }
+ else {
+ //alert('passed an element as RelEl: ' + typeof(relEl));
+ piRect = getOuterRect(relEl);
+ }
+
+ var menuEl = document.getElementById(this.id);
+ var menuRect = getOuterRect(menuEl);
+ var docRect = getDocumentRect();
+ var scrollPos = getScrollPos();
+ var pMenu = this.parentMenu;
+
+ if (dir == "vertical") {
+ if (piRect.left + menuRect.width - scrollPos.left <= docRect.width) {
+ //alert('piRect.left: ' + piRect.left);
+ this.left = piRect.left;
+// if ( ! ie )
+// this.left = this.left + 138;
+ } else if (docRect.width >= menuRect.width) {
+ //konq (not safari though) winds up here by accident and positions the menus all weird
+ //alert('docRect.width + scrollPos.left - menuRect.width');
+
+ this.left = docRect.width + scrollPos.left - menuRect.width;
+ } else {
+ //alert('scrollPos.left: ' + scrollPos.left);
+ this.left = scrollPos.left;
+ }
+
+ if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top)
+
+ this.top = piRect.top + piRect.height;
+
+ else if (piRect.top - menuRect.height >= scrollPos.top)
+
+ this.top = piRect.top - menuRect.height;
+
+ else if (docRect.height >= menuRect.height)
+
+ this.top = docRect.height + scrollPos.top - menuRect.height;
+
+ else
+
+ this.top = scrollPos.top;
+ }
+ else {
+ if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top)
+
+ this.top = piRect.top - this.borderTop - this.paddingTop;
+
+ else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0)
+
+ this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom;
+
+ else if (docRect.height >= menuRect.height)
+
+ this.top = docRect.height + scrollPos.top - menuRect.height;
+
+ else
+
+ this.top = scrollPos.top;
+
+
+
+ var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0;
+
+ var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0;
+
+ var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0;
+
+ var pMenuBorderRight = pMenu ? pMenu.borderRight : 0;
+
+
+
+ if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight +
+
+ pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left)
+
+ this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft;
+
+ else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0)
+
+ this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight;
+
+ else if (docRect.width >= menuRect.width)
+
+ this.left = docRect.width + scrollPos.left - menuRect.width;
+
+ else
+
+ this.left = scrollPos.left;
+ }
+};
diff --git a/httemplate/elements/xmlhttp.html b/httemplate/elements/xmlhttp.html
new file mode 100644
index 0000000..d0c7990
--- /dev/null
+++ b/httemplate/elements/xmlhttp.html
@@ -0,0 +1,125 @@
+<%doc>
+
+Example:
+
+ include( '/elements/xmlhttp.html',
+ # required
+ 'url' => $p.'misc/something.html',
+ 'subs' => [ 'subroutine' ],
+
+ # optional
+ 'method' => 'GET', #defaults to GET, could specify POST
+ 'key' => 'unique', #unique key
+
+ );
+
+</%doc>
+<SCRIPT TYPE="text/javascript">
+
+ function rs_init_object() {
+ var A;
+ try {
+ A=new ActiveXObject("Msxml2.XMLHTTP");
+ } catch (e) {
+ try {
+ A=new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (oc) {
+ A=null;
+ }
+ }
+ if(!A && typeof XMLHttpRequest != "undefined")
+ A = new XMLHttpRequest();
+ if (!A)
+ alert("Can't create XMLHttpRequest object");
+ return A;
+
+ }
+% foreach my $func ( @{$opt{'subs'}} ) {
+%
+% my $furl = $url;
+% $furl =~ s/\"/\\\\\"/; #javascript escape
+%
+%
+
+
+ function <%$key%><%$func%>() {
+ // count args; build URL
+ var url = "<%$furl%>";
+ var a = <%$key%><%$func%>.arguments;
+
+ var args;
+ var len;
+ var content = 'sub=<% uri_escape($func) %>';
+ if ( a && typeof a == 'object' && a[0].constructor == Array ) {
+ args = a[0];
+ len = args.length
+ } else {
+ args = a;
+ len = args.length - 1;
+ }
+ for (var i = 0; i < len; i++)
+ content = content + "&arg=" + escape(args[i]);
+ content = content.replace( /[+]/g, '%2B'); // fix unescaped plus signs
+
+ if ( '<%$method%>' == 'GET' ) {
+ url = url + content;
+ }
+
+ //alert('<%$method%> ' + url);
+
+ var xmlhttp = rs_init_object();
+ xmlhttp.open("<%$method%>", url, true);
+
+ xmlhttp.onreadystatechange = function() {
+ if (xmlhttp.readyState != 4)
+ return;
+
+ if (xmlhttp.status != 200) {
+ alert(xmlhttp.status + " status connecting to " + url);
+ } else {
+ var data = xmlhttp.responseText;
+ //alert('received response: ' + data);
+ a[a.length-1](data);
+ if ( data.indexOf("<b>System error</b>") > -1 ) {
+ var w;
+ if ( w = window.open("about:blank") ) {
+ w.document.write(data);
+ } else {
+ // popup blocking? should use an overlib popup instead
+ alert("Error popup disabled; try disabling popup blocking to see");
+ }
+ }
+ }
+ }
+
+ if ( '<%$method%>' == 'POST' ) {
+
+ xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+ xmlhttp.send(content);
+
+ } else {
+
+ xmlhttp.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
+ xmlhttp.send(null);
+
+ }
+
+ //rs_debug("x_$func_name url = " + url);
+ //rs_debug("x_$func_name waiting..");
+ }
+% }
+
+
+</SCRIPT>
+<%init>
+my ( %opt ) = @_;
+
+my $url = $opt{'url'};
+my $method = exists($opt{'method'}) ? $opt{'method'} : 'GET';
+#my @subs = @{ $opt{'subs'};
+my $key = exists($opt{'key'}) ? $opt{'key'} : '';
+
+$url .= ( ($url =~ /\?/) ? '&' : '?' )
+ if $method eq 'GET';
+
+</%init>
diff --git a/httemplate/graph/cust_bill_pkg.cgi b/httemplate/graph/cust_bill_pkg.cgi
new file mode 100644
index 0000000..d7cae80
--- /dev/null
+++ b/httemplate/graph/cust_bill_pkg.cgi
@@ -0,0 +1,109 @@
+<% include('elements/monthly.html',
+ 'title' => $title. 'Sales Report (Gross)',
+ 'graph_type' => 'Mountain',
+ 'items' => \@items,
+ 'params' => \@params,
+ 'labels' => \@labels,
+ 'graph_labels' => \@labels,
+ 'colors' => \@colors,
+ 'links' => \@links,
+ 'remove_empty' => 1,
+ 'bottom_total' => 1,
+ 'bottom_link' => "$link;",
+ 'agentnum' => $agentnum,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+#XXX or virtual
+my( $agentnum, $sel_agent ) = ('', '');
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agentnum = $1;
+ $sel_agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+ die "agentnum $agentnum not found!" unless $sel_agent;
+}
+my $title = $sel_agent ? $sel_agent->agent.' ' : '';
+
+#false lazinessish w/search/cust_pkg.cgi
+my $classnum = 0;
+my @pkg_class = ();
+if ( $cgi->param('classnum') =~ /^(\d*)$/ ) {
+ $classnum = $1;
+ if ( $classnum ) {
+ @pkg_class = ( qsearchs('pkg_class', { 'classnum' => $classnum } ) );
+ die "classnum $classnum not found!" unless $pkg_class[0];
+ $title .= $pkg_class[0]->classname.' ';
+ } elsif ( $classnum eq '' ) {
+ $title .= 'Empty class ';
+ @pkg_class = ( '(empty class)' );
+ } elsif ( $classnum eq '0' ) {
+ @pkg_class = qsearch('pkg_class', {} ); # { 'disabled' => '' } );
+ push @pkg_class, '(empty class)';
+ }
+}
+#eslaf
+
+my $hue = 0;
+#my $hue_increment = 170;
+#my $hue_increment = 145;
+my $hue_increment = 125;
+
+my @items = ();
+my @params = ();
+my @labels = ();
+my @colors = ();
+my @links = ();
+
+my $link = "${p}search/cust_bill_pkg.cgi?nottax=1;include_comp_cust=1";
+
+foreach my $agent ( $sel_agent || qsearch('agent', { 'disabled' => '' } ) ) {
+
+ my $col_scheme = Color::Scheme->new
+ ->from_hue($hue) #->from_hex($agent->color)
+ ->scheme('analogic')
+ ;
+ my @recur_colors = ();
+ my @onetime_colors = ();
+
+ ### fixup the color handling for package classes...
+ my $n = 0;
+
+ foreach my $pkg_class ( @pkg_class ) {
+
+ push @items, 'cust_bill_pkg';
+
+
+ push @labels,
+ ( $sel_agent ? '' : $agent->agent.' ' ).
+ ( $classnum eq '0'
+ ? ( ref($pkg_class) ? $pkg_class->classname : $pkg_class )
+ : ''
+ );
+
+ my $row_classnum = ref($pkg_class) ? $pkg_class->classnum : 0;
+ my $row_agentnum = $agent->agentnum;
+ push @params, [ 'classnum' => $row_classnum,
+ 'agentnum' => $row_agentnum,
+ ];
+
+ push @links, "$link;agentnum=$row_agentnum;classnum=$row_classnum;";
+
+ @recur_colors = ($col_scheme->colors)[0,4,8,1,5,9]
+ unless @recur_colors;
+ @onetime_colors = ($col_scheme->colors)[2,6,10,3,7,11]
+ unless @onetime_colors;
+ push @colors, shift @recur_colors;
+
+ }
+
+ $hue += $hue_increment;
+
+}
+
+#use Data::Dumper;
+#warn Dumper(\@items);
+
+</%init>
diff --git a/httemplate/graph/cust_pkg.cgi b/httemplate/graph/cust_pkg.cgi
new file mode 100644
index 0000000..21ce07d
--- /dev/null
+++ b/httemplate/graph/cust_pkg.cgi
@@ -0,0 +1,63 @@
+<% include('elements/monthly.html',
+ 'title' => $agentname. 'Package Churn',
+ 'items' => \@items,
+ 'labels' => \%label,
+ 'graph_labels' => \%graph_label,
+ 'colors' => \%color,
+ 'links' => \%link,
+ 'agentnum' => $agentnum,
+ 'sprintf' => '%u',
+ 'disable_money' => 1,
+ )
+%>
+<%init>
+
+#XXX use a different ACL for package churn?
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+#false laziness w/money_time.cgi, cust_bill_pkg.cgi
+
+#XXX or virtual
+my( $agentnum, $agent ) = ('', '');
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agentnum = $1;
+ $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+ die "agentnum $agentnum not found!" unless $agent;
+}
+
+my $agentname = $agent ? $agent->agent.' ' : '';
+
+my @items = qw( setup_pkg susp_pkg cancel_pkg );
+
+my %label = (
+ 'setup_pkg' => 'New orders',
+ 'susp_pkg' => 'Suspensions',
+# 'unsusp' => 'Unsuspensions',
+ 'cancel_pkg' => 'Cancellations',
+);
+my %graph_label = %label;
+
+my %color = (
+ 'setup_pkg' => '00cc00', #green
+ 'susp_pkg' => 'ff9900', #yellow
+ #'unsusp' => '', #light green?
+ 'cancel_pkg' => 'cc0000', #red ? 'ff0000'
+);
+
+my %link = (
+ 'setup_pkg' => { 'link' => "${p}search/cust_pkg.cgi?agentnum=$agentnum;",
+ 'fromparam' => 'setup_begin',
+ 'toparam' => 'setup_end',
+ },
+ 'susp_pkg' => { 'link' => "${p}search/cust_pkg.cgi?agentnum=$agentnum;",
+ 'fromparam' => 'susp_begin',
+ 'toparam' => 'susp_end',
+ },
+ 'cancel_pkg' => { 'link' => "${p}search/cust_pkg.cgi?agentnum=$agentnum;",
+ 'fromparam' => 'cancel_begin',
+ 'toparam' => 'cancel_end',
+ },
+);
+
+</%init>
diff --git a/httemplate/graph/elements/monthly.html b/httemplate/graph/elements/monthly.html
new file mode 100644
index 0000000..7039bfe
--- /dev/null
+++ b/httemplate/graph/elements/monthly.html
@@ -0,0 +1,351 @@
+<%doc>
+
+Example:
+
+ include('elements/monthly.html',
+ #required
+ 'title' => 'Page title',
+ 'items' => \@items,
+ 'labels' => \@labels, # or \%labels (keys are items)
+
+ #required?
+ 'colors' => \@colors, # or \%colors,
+
+ #recommended
+ 'graph_labels' => \@graph_labels, # or \%graph_labels,
+
+ #optional
+ 'params' => \@params, # opt,
+ 'links' => \@links, # or \%link, #opt
+ 'link_fromparam' => 'param_from', #defaults to 'begin'
+ 'link_toparam' => 'param_to', #defaults to 'end'
+
+ #optional, pulled from CGI params if not specified
+ 'start_month' => $smonth,
+ 'start_year' => $syear,
+ 'end_month' => $emonth,
+ 'end_year' => $eyear,
+
+ #optional
+ 'agentnum' => $agentnum,
+ 'nototal' => 1,
+ 'graph_type' => 'LinesPoints',
+ 'remove_empty' => 1,
+ 'bottom_total' => 1,
+ 'sprintf' => '%u', #sprintf format, overrides default %.2f
+ 'disable_money' => 1,
+ );
+
+</%doc>
+% if ( $cgi->param('_type') =~ /^(csv)$/ ) {
+%
+% #http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes
+% http_header('Content-Type' => 'text/plain' );
+%
+% my $csv = new Text::CSV_XS { 'always_quote' => 1,
+% 'eol' => "\n", #"\015\012", #"\012"
+% };
+%
+% $csv->combine(map { my $m=$_; $m =~ s/^(\d+)\//$mon[$1-1] /; $m; }
+% ('', @{$data->{label}}, $opt{'nototal'} ? () : 'Total')
+% );
+%
+<% $csv->string %>
+%
+% my @bottom_total = ();
+% foreach ( @{ $data->{'items'} } ) {
+%
+% my $col = 0;
+% my $total = 0;
+% $csv->combine(
+% shift( @{ $data->{'item_labels'} } ),
+% map { $total += $_; $bottom_total[$col++] += $_; sprintf($sprintf, $_); }
+% ( @{ shift( @{$data->{data}} ) } ),
+% ( $opt{'nototal'} ? () : sprintf($sprintf, $total) ),
+% );
+% unless ( $opt{'nototal'} ) {
+% $bottom_total[$col++] += $total;
+% }
+%
+<% $csv->string %>
+%
+% }
+%
+% if ( $opt{'bottom_total'} ) {
+% $csv->combine(
+% 'Total',
+% map { sprintf($sprintf, $_) } @bottom_total,
+% );
+%
+<% $csv->string %>
+%
+% }
+%
+% } elsif ( $cgi->param('_type') =~ /(\.xls)$/ ) {
+%
+% #http_header('Content-Type' => 'application/excel' ); #eww
+% http_header('Content-Type' => 'application/vnd.ms-excel' );
+% #http_header('Content-Type' => 'application/msexcel' ); #alas
+%
+% my $output = '';
+% my $XLS = new IO::Scalar \$output;
+% my $workbook = Spreadsheet::WriteExcel->new($XLS)
+% or die "Error opening .xls file: $!";
+%
+% my $worksheet = $workbook->add_worksheet(substr($opt{'title'},0,31));
+%
+% my($r,$c) = (0,0);
+%
+% foreach ('', @{$data->{label}}, ($opt{'nototal'} ? () : 'Total') ) {
+% my $header = $_;
+% $header =~ s/^(\d+)\//$mon[$1-1] /;
+% $worksheet->write($r, $c++, $header)
+% }
+%
+% my @bottom_total = ();
+% foreach ( @{ $data->{'items'} } ) {
+% $r++;
+% $c = 0;
+% my $total = 0;
+% $worksheet->write( $r, $c++, shift( @{ $data->{'item_labels'} } ) );
+% foreach ( @{ shift( @{$data->{data}} ) } ) {
+% $total += $_;
+% $bottom_total[$c] += $_;
+% $worksheet->write($r, $c++, sprintf($sprintf, $_) );
+% }
+% unless ( $opt{'nototal'} ) {
+% $bottom_total[$c] += $total;
+% $worksheet->write($r, $c++, sprintf($sprintf, $total) );
+% }
+% }
+%
+% $c = 0;
+% if ( $opt{'bottom_total'} ) {
+% $r++;
+% $worksheet->write($r, $c++, 'Total');
+% $worksheet->write($r, $c++, sprintf($sprintf, $_)) foreach @bottom_total;
+% }
+%
+% $workbook->close();# or die "Error creating .xls file: $!";
+%
+% http_header('Content-Length' => length($output) );
+%
+<% $output %>
+% } elsif ( $cgi->param('_type') =~ /^(png)$/ ) {
+%
+% #my $chart = Chart::LinesPoints->new(1024,480);
+% #my $chart = Chart::LinesPoints->new(768,480);
+%
+% my $graph_type = 'LinesPoints';
+% if ( $opt{'graph_type'} =~ /^(LinesPoints|Mountain)$/ ) {
+% $graph_type = $1;
+% }
+% my $class = "Chart::$graph_type";
+%
+% my $chart = $class->new(976,384);
+%
+% my $d = 0;
+% $chart->set(
+% #'min_val' => 0,
+% 'legend' => 'bottom',
+% 'colors' => { (
+% map { my $color = $_;
+% 'dataset'.$d++ =>
+% [ map hex($_), unpack 'a2a2a2', $color ]
+% }
+% #@{ $opt{'colors'} }
+% @{ $data->{'colors'} }
+% ),
+% #'grey_background' => [ 211, 211, 211 ],
+% 'grey_background' => 'white',
+% 'background' => [ 0xe8, 0xe8, 0xe8 ], #grey
+% },
+% #'grey_background' => 'false',
+% 'legend_labels' => $data->{'item_labels'},
+% 'brush_size' => 4,
+% #'pt_size' => 12,
+% );
+%
+% #my @data = map { $data->{$_} } ( 'label', @items );
+% my @data = @{ $data->{data} };
+% unshift @data, $data->{'label'};
+%
+% http_header('Content-Type' => 'image/png' );
+%
+% $chart->_set_colors();
+%
+<% $chart->scalar_png(\@data) %>
+%
+% } else {
+%
+<% include('/elements/header.html', $opt{'title'} ) %>
+% $cgi->param('_type', 'png');
+
+<IMG SRC="<% $cgi->self_url %>" WIDTH="976" HEIGHT="384">
+<P ALIGN="right">
+
+% unless ( $opt{'disable_download'} ) {
+% $cgi->param('_type', "monthly.xls" );
+ Download full results<BR>
+ as <A HREF="<% $cgi->self_url %>">Excel spreadsheet</A><BR>
+% $cgi->param('_type', 'csv');
+ as <A HREF="<% $cgi->self_url %>">CSV file</A></P>
+% $cgi->param('_type', "html" );
+% }
+%
+</P>
+<% include('/elements/table.html', 'e8e8e8') %>
+
+<TR>
+
+ <TD></TD>
+
+% foreach my $column ( @{$data->{label}} ) {
+% #$column =~ s/^(\d+)\//$mon[$1-1]<BR>/e;
+% $column =~ s/^(\d+)\//$mon[$1-1]<BR>/;
+ <TH><% $column %></TH>
+% }
+
+% unless ( $opt{'nototal'} ) {
+ <TH>Total</TH>
+% }
+
+</TR>
+
+% my @bottom_total = ();
+% foreach my $row ( @{ $data->{'items'} } ) {
+%
+% #my $color = shift( @{ $opt{'colors'} } );
+% my $color = shift( @{ $data->{'colors'} } );
+% my $link = shift( @{ $data->{'links'} } );
+% my ( $begin, $end ) = ( $fromparam, $toparam );
+% if ( ref($link) ) {
+% my $ref = $link;
+% $link = $ref->{link};
+% $begin = $ref->{fromparam};
+% $end = $ref->{toparam};
+% }
+% $link = $link ? qq(<A HREF="$link) : '';
+% my $label = shift( @{ $data->{'item_labels'} } );
+
+ <TR>
+
+ <TH>
+ <FONT COLOR="#<% $color %>"><% $label %></FONT>
+ </TH>
+
+% #my $link = exists($opt{'links'}{$row})
+% # ? qq(<A HREF="$opt{'links'}{$row})
+% # : '';
+% my @speriod = @{$data->{speriod}};
+% my @eperiod = @{$data->{eperiod}};
+% my $total = 0;
+%
+% my $col = 0;
+% foreach my $column ( @{ shift( @{$data->{data}} ) } ) {
+
+ <TD ALIGN="right" BGCOLOR="#ffffff">
+ <% $link ? $link. "$begin=". shift(@speriod). ";$end=". shift(@eperiod). '">' : '' %><FONT COLOR="#<% $color %>"><% $money_char %><% sprintf($sprintf,, $column) %></FONT><% $link ? '</A>' : '' %>
+ </TD>
+%
+% $total += $column;
+% $bottom_total[$col++] += $column;
+%
+% }
+
+% unless ( $opt{'nototal'} ) {
+ <TD ALIGN="right" BGCOLOR="#f5f6be">
+ <% $link ? $link. "$begin=". ${$data->{speriod}}[0]. ";$end=". ${$data->{eperiod}}[-1]. '">' : '' %><FONT COLOR="#<% $color %>"><% $money_char %><% sprintf($sprintf, $total) %></FONT><% $link ? '</A>' : '' %>
+ </TD>
+% $bottom_total[$col++] += $total;
+% }
+
+ </TR>
+
+% }
+
+% if ( $opt{'bottom_total'} ) {
+% my @speriod = ( @{$data->{speriod}}, ${$data->{speriod}}[0] );
+% my @eperiod = ( @{$data->{eperiod}}, ${$data->{eperiod}}[-1] );
+
+ <TR>
+ <TH>Total</TH>
+
+% foreach my $total ( @bottom_total ) {
+
+ <TD ALIGN="right" BGCOLOR="#f5f6be">
+ <% $opt{'bottom_link'}
+ ? '<A HREF="'. $opt{'bottom_link'}.
+ "$fromparam=". shift(@speriod).
+ ";$toparam=". shift(@eperiod). '">'
+ : ''
+ %>$<% sprintf($sprintf, $total) %><% $opt{'bottom_link'} ? '</A>' : '' %>
+
+ </TD>
+
+% }
+
+ </TR>
+
+% }
+
+</TABLE>
+
+<% include('/elements/footer.html') %>
+% }
+<%once>
+
+</%once>
+<%init>
+
+my(%opt) = @_;
+
+my $sprintf = $opt{'sprintf'} || '%.2f';
+my $fromparam = $opt{'link_fromparam'} || 'begin';
+my $toparam = $opt{'link_toparam'} || 'end';
+
+my $conf = new FS::Conf;
+my $money_char = $opt{'disable_money'} ? '' : $conf->config('money_char');
+
+my @items = @{ $opt{'items'} };
+
+foreach my $other (qw( labels graph_labels colors links )) {
+#foreach my $other (qw( labels graph_labels colors )) {
+ if ( ref($opt{$other}) eq 'HASH' ) {
+ $opt{$other} = [ map $opt{$other}{$_}, @items ];
+ }
+}
+
+my @mon = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+
+#find first month
+$opt{'start_month'} ||= $cgi->param('start_month'); # || $curmon+1;
+$opt{'start_year'} ||= $cgi->param('start_year'); # || 1899+$curyear;
+
+#find last month
+$opt{'end_month'} ||= $cgi->param('end_month'); # || $curmon+1;
+$opt{'end_year'} ||= $cgi->param('end_year'); # || 1900+$curyear;
+
+my $report = new FS::Report::Table::Monthly (
+
+ #'items' => $opt{'items'},
+ 'items' => \@items,
+ 'params' => $opt{'params'},
+ 'item_labels' => ( $cgi->param('_type') =~ /^(png)$/
+ ? $opt{'graph_labels'}
+ : $opt{'labels'}
+ ),
+ 'colors' => $opt{'colors'},
+ 'links' => $opt{'links'},
+
+ 'start_month' => $opt{'start_month'},
+ 'start_year' => $opt{'start_year'},
+ 'end_month' => $opt{'end_month'},
+ 'end_year' => $opt{'end_year'},
+
+ 'agentnum' => $opt{'agentnum'},
+ 'remove_empty' => $opt{'remove_empty'},
+);
+my $data = $report->data;
+
+</%init>
diff --git a/httemplate/graph/money_time.cgi b/httemplate/graph/money_time.cgi
new file mode 100644
index 0000000..4e4157e
--- /dev/null
+++ b/httemplate/graph/money_time.cgi
@@ -0,0 +1,98 @@
+<% include('elements/monthly.html',
+ 'title' => $agentname.
+ 'Sales, Credits and Receipts Summary',
+ 'items' => \@items,
+ 'labels' => \%label,
+ 'graph_labels' => \%graph_label,
+ 'colors' => \%color,
+ 'links' => \%link,
+ 'agentnum' => $agentnum,
+ 'nototal' => scalar($cgi->param('12mo')),
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+#XXX or virtual
+my( $agentnum, $agent ) = ('', '');
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agentnum = $1;
+ $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+ die "agentnum $agentnum not found!" unless $agent;
+}
+
+my $agentname = $agent ? $agent->agent.' ' : '';
+
+my @items = qw( invoiced netsales
+ credits netcredits
+ payments receipts
+ refunds netrefunds
+ cashflow netcashflow
+ );
+if ( $cgi->param('12mo') == 1 ) {
+ @items = map $_.'_12mo', @items;
+}
+
+my %label = (
+ 'invoiced' => 'Gross Sales',
+ 'netsales' => 'Net Sales',
+ 'credits' => 'Gross Credits',
+ 'netcredits' => 'Net Credits',
+ 'payments' => 'Gross Receipts',
+ 'receipts' => 'Net Receipts',
+ 'refunds' => 'Gross Refunds',
+ 'netrefunds' => 'Net Refunds',
+ 'cashflow' => 'Gross Cashflow',
+ 'netcashflow' => 'Net Cashflow',
+);
+
+my %graph_suffix = (
+ 'invoiced' => ' (invoiced)',
+ 'netsales' => ' (invoiced - applied credits)',
+ 'credits' => ' (credited)',
+ 'netcredits' => ' (applied credits)',
+ 'payments' => ' (payments)',
+ 'receipts' => ' (applied payments)',
+ 'refunds' => ' (refunds)',
+ 'netrefunds' => ' (applied refunds)',
+ 'cashflow' => ' (payments - refunds)',
+ 'netcashflow' => ' (applied payments - applied refunds)',
+);
+my %graph_label = map { $_ => $label{$_}.$graph_suffix{$_} } keys %label;
+
+$label{$_.'_12mo'} = $label{$_}. " (prev 12 months)"
+ foreach keys %label;
+
+$graph_label{$_.'_12mo'} = $graph_label{$_}. " (prev 12 months)"
+ foreach keys %graph_label;
+
+my %color = (
+ 'invoiced' => '9999ff', #light blue
+ 'netsales' => '0000cc', #blue
+ 'credits' => 'ff9999', #light red
+ 'netcredits' => 'cc0000', #red
+ 'payments' => '99cc99', #light green
+ 'receipts' => '00cc00', #green
+ 'refunds' => 'ffcc99', #light orange
+ 'netrefunds' => 'ff9900', #orange
+ 'cashflow' => '99cc33', #light olive
+ 'netcashflow' => '339900', #olive
+);
+$color{$_.'_12mo'} = $color{$_}
+ foreach keys %color;
+
+my %link = (
+ 'invoiced' => "${p}search/cust_bill.html?agentnum=$agentnum;",
+ 'netsales' => "${p}search/cust_bill.html?agentnum=$agentnum;net=1;",
+ 'credits' => "${p}search/cust_credit.html?agentnum=$agentnum;",
+ 'netcredits' => "${p}search/cust_credit_bill.html?agentnum=$agentnum;",
+ 'payments' => "${p}search/cust_pay.cgi?magic=_date;agentnum=$agentnum;",
+ 'receipts' => "${p}search/cust_bill_pay.html?agentnum=$agentnum;",
+ 'refunds' => "${p}search/cust_refund.html?magic=_date;agentnum=$agentnum;",
+ 'netrefunds' => "${p}search/cust_credit_refund.html?agentnum=$agentnum;",
+);
+# XXX link 12mo?
+
+</%init>
diff --git a/httemplate/graph/report_cust_bill_pkg.html b/httemplate/graph/report_cust_bill_pkg.html
new file mode 100644
index 0000000..5193bf4
--- /dev/null
+++ b/httemplate/graph/report_cust_bill_pkg.html
@@ -0,0 +1,39 @@
+<% include('/elements/header.html', 'Sales Report' ) %>
+
+<FORM ACTION="cust_bill_pkg.cgi" METHOD="GET">
+
+<TABLE>
+
+<% include('/elements/tr-select-from_to.html' ) %>
+
+<% include('/elements/tr-select-agent.html',
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ )
+%>
+
+<% include('/elements/tr-select-pkg_class.html',
+ 'pre_options' => [ '0' => 'all' ],
+ 'empty_label' => '(empty class)',
+ )
+%>
+
+<!--
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="separate_0freq" VALUE="1"></TD>
+ <TD>Separate one-time vs. recurring sales</TD>
+</TR>
+-->
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Display">
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/graph/report_cust_pkg.html b/httemplate/graph/report_cust_pkg.html
new file mode 100644
index 0000000..22ccd5d
--- /dev/null
+++ b/httemplate/graph/report_cust_pkg.html
@@ -0,0 +1,28 @@
+<% include('/elements/header.html', 'Package Churn Summary' ) %>
+
+<FORM ACTION="cust_pkg.cgi" METHOD="GET">
+
+<TABLE>
+
+<% include('/elements/tr-select-from_to.html' ) %>
+
+<% include('/elements/tr-select-agent.html',
+ 'curr_value' => scalar($cgi->param('agentnum')),
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ )
+%>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Display">
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+#XXX use a different ACL for package churn?
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/graph/report_money_time.html b/httemplate/graph/report_money_time.html
new file mode 100644
index 0000000..b85bb65
--- /dev/null
+++ b/httemplate/graph/report_money_time.html
@@ -0,0 +1,43 @@
+<% include('/elements/header.html', 'Sales, Credits and Receipts Summary' ) %>
+
+<FORM ACTION="money_time.cgi" METHOD="GET">
+
+<!--
+<INPUT TYPE="checkbox" NAME="ar">
+ Accounts receivable (invoices - applied credits)<BR>
+<INPUT TYPE="checkbox" NAME="charged">
+ Just Invoices<BR>
+<INPUT TYPE="checkbox" NAME="defer">
+ Accounts receivable, with deferred revenue (invoices - applied credits, with charges for annual/semi-annual/quarterly/etc. services deferred over applicable time period) (there has got to be a shorter description for this)<BR>
+<INPUT TYPE="checkbox" NAME="cash">
+ Cashflow (payments - refunds)<BR>
+<BR>
+-->
+
+<TABLE>
+
+<% include('/elements/tr-select-from_to.html' ) %>
+
+<% include('/elements/tr-select-agent.html',
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ )
+%>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="12mo" VALUE="1"></TD>
+ <TD>Show 12 month totals instead of monthly values</TD>
+</TR>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Display">
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/images/32clear.gif b/httemplate/images/32clear.gif
new file mode 100644
index 0000000..5fdcea2
--- /dev/null
+++ b/httemplate/images/32clear.gif
Binary files differ
diff --git a/httemplate/images/ach.png b/httemplate/images/ach.png
new file mode 100644
index 0000000..fdcd5e6
--- /dev/null
+++ b/httemplate/images/ach.png
Binary files differ
diff --git a/httemplate/images/arrow.down.png b/httemplate/images/arrow.down.png
new file mode 100644
index 0000000..34cb028
--- /dev/null
+++ b/httemplate/images/arrow.down.png
Binary files differ
diff --git a/httemplate/images/arrow.right.black.png b/httemplate/images/arrow.right.black.png
new file mode 100644
index 0000000..933c258
--- /dev/null
+++ b/httemplate/images/arrow.right.black.png
Binary files differ
diff --git a/httemplate/images/arrow.right.png b/httemplate/images/arrow.right.png
new file mode 100644
index 0000000..60bcb76
--- /dev/null
+++ b/httemplate/images/arrow.right.png
Binary files differ
diff --git a/httemplate/images/background-cheat.png b/httemplate/images/background-cheat.png
new file mode 100644
index 0000000..ad332f6
--- /dev/null
+++ b/httemplate/images/background-cheat.png
Binary files differ
diff --git a/httemplate/images/black-gradient.png b/httemplate/images/black-gradient.png
new file mode 100644
index 0000000..225732d
--- /dev/null
+++ b/httemplate/images/black-gradient.png
Binary files differ
diff --git a/httemplate/images/black-gray-corner.png b/httemplate/images/black-gray-corner.png
new file mode 100644
index 0000000..17954cd
--- /dev/null
+++ b/httemplate/images/black-gray-corner.png
Binary files differ
diff --git a/httemplate/images/black-gray-gradient.png b/httemplate/images/black-gray-gradient.png
new file mode 100644
index 0000000..f5c318f
--- /dev/null
+++ b/httemplate/images/black-gray-gradient.png
Binary files differ
diff --git a/httemplate/images/black-gray-side.png b/httemplate/images/black-gray-side.png
new file mode 100644
index 0000000..f7a98a4
--- /dev/null
+++ b/httemplate/images/black-gray-side.png
Binary files differ
diff --git a/httemplate/images/black-gray-top.png b/httemplate/images/black-gray-top.png
new file mode 100644
index 0000000..ed07075
--- /dev/null
+++ b/httemplate/images/black-gray-top.png
Binary files differ
diff --git a/httemplate/images/calendar-disabled.png b/httemplate/images/calendar-disabled.png
new file mode 100644
index 0000000..81816bc
--- /dev/null
+++ b/httemplate/images/calendar-disabled.png
Binary files differ
diff --git a/httemplate/images/calendar.png b/httemplate/images/calendar.png
new file mode 100644
index 0000000..1632661
--- /dev/null
+++ b/httemplate/images/calendar.png
Binary files differ
diff --git a/httemplate/images/cross.png b/httemplate/images/cross.png
new file mode 100644
index 0000000..1514d51
--- /dev/null
+++ b/httemplate/images/cross.png
Binary files differ
diff --git a/httemplate/images/cvv2.png b/httemplate/images/cvv2.png
new file mode 100644
index 0000000..48c58d5
--- /dev/null
+++ b/httemplate/images/cvv2.png
Binary files differ
diff --git a/httemplate/images/cvv2_amex.png b/httemplate/images/cvv2_amex.png
new file mode 100644
index 0000000..82d1f47
--- /dev/null
+++ b/httemplate/images/cvv2_amex.png
Binary files differ
diff --git a/httemplate/images/error.png b/httemplate/images/error.png
new file mode 100644
index 0000000..628cf2d
--- /dev/null
+++ b/httemplate/images/error.png
Binary files differ
diff --git a/httemplate/images/menu-left-example.png b/httemplate/images/menu-left-example.png
new file mode 100644
index 0000000..375725c
--- /dev/null
+++ b/httemplate/images/menu-left-example.png
Binary files differ
diff --git a/httemplate/images/menu-top-example.png b/httemplate/images/menu-top-example.png
new file mode 100644
index 0000000..bd9bea8
--- /dev/null
+++ b/httemplate/images/menu-top-example.png
Binary files differ
diff --git a/httemplate/images/progressbar-empty.png b/httemplate/images/progressbar-empty.png
new file mode 100644
index 0000000..318219c
--- /dev/null
+++ b/httemplate/images/progressbar-empty.png
Binary files differ
diff --git a/httemplate/images/progressbar-full.png b/httemplate/images/progressbar-full.png
new file mode 100644
index 0000000..863d8e1
--- /dev/null
+++ b/httemplate/images/progressbar-full.png
Binary files differ
diff --git a/httemplate/images/red_telephone_mimooh_01.png b/httemplate/images/red_telephone_mimooh_01.png
new file mode 100644
index 0000000..2212ff0
--- /dev/null
+++ b/httemplate/images/red_telephone_mimooh_01.png
Binary files differ
diff --git a/httemplate/images/small-logo.png b/httemplate/images/small-logo.png
new file mode 100644
index 0000000..1e415e6
--- /dev/null
+++ b/httemplate/images/small-logo.png
Binary files differ
diff --git a/httemplate/images/tick.png b/httemplate/images/tick.png
new file mode 100644
index 0000000..a9925a0
--- /dev/null
+++ b/httemplate/images/tick.png
Binary files differ
diff --git a/httemplate/images/wait-orange.gif b/httemplate/images/wait-orange.gif
new file mode 100644
index 0000000..92c7f34
--- /dev/null
+++ b/httemplate/images/wait-orange.gif
Binary files differ
diff --git a/httemplate/index.html b/httemplate/index.html
new file mode 100644
index 0000000..c813991
--- /dev/null
+++ b/httemplate/index.html
@@ -0,0 +1,54 @@
+% my $conf = new FS::Conf;
+
+<% include('/elements/header.html', 'Billing Main' ) %>
+
+<% include('/elements/dashboard-toplist.html') %>
+
+% my $sth = dbh->prepare(
+% #"SELECT DISTINCT custnum FROM h_cust_main JOIN cust_main USING ( custnum )
+% "SELECT custnum FROM h_cust_main JOIN cust_main USING ( custnum )
+% WHERE ( history_action = 'insert' OR history_action = 'replace_new' )
+% AND history_user = ?
+% ORDER BY history_date desc" # LIMIT 10
+% ) or die dbh->errstr;
+%
+% $sth->execute( getotaker() ) or die $sth->errstr;
+%
+% my %saw = ();
+% my @custnums = grep { !$saw{$_}++ } map $_->[0], @{ $sth->fetchall_arrayref };
+%
+% @custnums = splice(@custnums, 0, 10);
+%
+% if ( @custnums ) {
+
+ <% include('/elements/table-grid.html') %>
+
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = $bgcolor2;
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=1>Customers I recently added or modified</TH>
+ </TR>
+
+% foreach my $custnum ( @custnums ) {
+% my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+% next unless $cust_main;
+
+ <TR>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><A HREF="view/cust_main.cgi?<% $custnum %>"><% $cust_main->display_custnum %>: <% $cust_main->name %></A></TD>
+ </TR>
+
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% }
+
+ </TABLE>
+
+% }
+
+<% include('/elements/footer.html') %>
diff --git a/httemplate/misc/areacodes.cgi b/httemplate/misc/areacodes.cgi
new file mode 100644
index 0000000..69c9573
--- /dev/null
+++ b/httemplate/misc/areacodes.cgi
@@ -0,0 +1,24 @@
+%# [ <% join(', ', map { qq("$_") } @areacodes) %> ]
+<% objToJson(\@areacodes) %>
+<%init>
+
+my( $state, $svcpart ) = $cgi->param('arg');
+
+my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
+die "unknown svcpart $svcpart" unless $part_svc;
+
+my @exports = $part_svc->part_export_did;
+if ( scalar(@exports) > 1 ) {
+ die "more than one DID-providing export attached to svcpart $svcpart";
+} elsif ( ! @exports ) {
+ die "no DID providing export attached to svcpart $svcpart";
+}
+my $export = $exports[0];
+
+my $something = $export->get_dids('state'=>$state);
+
+#warn Dumper($something);
+
+my @areacodes = @{ $something };
+
+</%init>
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
new file mode 100644
index 0000000..e10a5f6
--- /dev/null
+++ b/httemplate/misc/batch-cust_pay.html
@@ -0,0 +1,38 @@
+<% include('/elements/header.html', 'Quick payment entry') %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="process/batch-cust_pay.cgi" NAME="OneTrueForm" METHOD="POST" onsubmit="document.OneTrueForm.submit.disabled=true;">
+
+<!-- <B>Batch</B> <INPUT TYPE="text" NAME="paybatch"><BR><BR> -->
+
+<% include( "/elements/customer-table.html",
+ name_singular => 'payment',
+ header => [ '', 'Amount', 'Check #', '' ],
+ fields => [ sub {'$'}, 'paid', 'payinfo', 'error', ],
+ types => [ 'immutable', '', '', 'immutable', ],
+ align => [ 'c', 'r', 'r', 'l' ],
+ sizes => [ 0, 8, 10, 0, ],
+ colors => [ '', '', '', '#ff0000' ],
+ param => { () },
+ footer => [ '$', '_TOTAL', '', '' ],
+ footer_align => [ 'c', 'r', 'r', '' ],
+ )
+%>
+
+<!-- <BR>
+<INPUT TYPE="button" VALUE="TEST addrow" onclick="addRow()"> -->
+
+<BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="Post payment batch">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Post payment batch');
+
+</%init>
diff --git a/httemplate/misc/bill.cgi b/httemplate/misc/bill.cgi
new file mode 100755
index 0000000..3c3c48c
--- /dev/null
+++ b/httemplate/misc/bill.cgi
@@ -0,0 +1,45 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect(popurl(2). "view/cust_main.cgi?$custnum") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Bill customer now');
+
+#untaint custnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d*)$/;
+my $custnum = $1;
+my $cust_main = qsearchs('cust_main',{'custnum'=>$custnum});
+die "Can't find customer!\n" unless $cust_main;
+
+my $conf = new FS::Conf;
+
+my $error = $cust_main->bill(
+# 'time'=>$time
+ );
+
+unless ( $error ) {
+ $error = $cust_main->apply_payments_and_credits
+ || $cust_main->collect(
+ #'invoice-time'=>$time,
+ #'batch_card'=> 'yes',
+ #'batch_card'=> 'no',
+ #'report_badcard'=> 'yes',
+ #'retry_card' => 'yes',
+
+ 'retry' => 'yes',
+
+ #this is used only by cust_main::batch_card
+ #need to pick & create an actual config
+ #value if we're going to turn this on
+ #("realtime-backend" doesn't exist,
+ # "backend-realtime" is for something
+ # entirely different)
+ #'realtime' => $conf->exists('realtime-backend'),
+ );
+}
+
+</%init>
diff --git a/httemplate/misc/bulk_change_pkg.cgi b/httemplate/misc/bulk_change_pkg.cgi
new file mode 100755
index 0000000..9334985
--- /dev/null
+++ b/httemplate/misc/bulk_change_pkg.cgi
@@ -0,0 +1,59 @@
+<% include('/elements/header-popup.html', "Change Packages") %>
+
+% if ( $cgi->param('error') ) {
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $cgi->param('error') %></FONT>
+ <BR><BR>
+% }
+
+<FORM ACTION="<% $p %>misc/process/bulk_change_pkg.cgi" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="query" VALUE="<% $cgi->keywords %>">
+% for my $param (qw(agentnum magic status classnum pkgpart)) {
+<INPUT TYPE="hidden" NAME="<% $param %>" VALUE="<% $cgi->param($param) %>">
+% }
+%
+% foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
+%
+ <INPUT TYPE="hidden" NAME="<% $field %>begin" VALUE="<% $cgi->param("${field}.begin") %>">
+ <INPUT TYPE="hidden" NAME="<% $field %>beginning" VALUE="<% $cgi->param("${field}beginning") %>">
+ <INPUT TYPE="hidden" NAME="<% $field %>end" VALUE="<% $cgi->param("${field}.end") %>">
+ <INPUT TYPE="hidden" NAME="<% $field %>ending" VALUE="<% $cgi->param("${field}.ending") %>">
+% }
+
+<% ntable('#cccccc') %>
+
+ <TR>
+ <TD>New package: </TD>
+ <TD><% include('/elements/select-table.html',
+ 'table' => 'part_pkg',
+ 'name_col' => 'pkg',
+ 'empty_label' => 'Select package',
+ 'label_callback' => sub { $_[0]->pkgpart. ': '.
+ $_[0]->pkg. ' - '.
+ $_[0]->comment;
+ },
+ 'element_name' => 'new_pkgpart',
+ 'curr_value' => ( $cgi->param('error')
+ ? scalar($cgi->param('new_pkgpart'))
+ : ''
+ ),
+ )
+ %>
+ </TD>
+ </TR>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Change packages">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages');
+
+</%init>
diff --git a/httemplate/misc/cancel-unaudited.cgi b/httemplate/misc/cancel-unaudited.cgi
new file mode 100755
index 0000000..4919c66
--- /dev/null
+++ b/httemplate/misc/cancel-unaudited.cgi
@@ -0,0 +1,33 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect(popurl(2)) %>
+%}
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unprovision customer service')
+ && $FS::CurrentUser::CurrentUser->access_right('View/link unlinked services');
+
+#untaint svcnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+
+#my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$svcnum});
+#die "Unknown svcnum!" unless $svc_acct;
+
+my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+die "Unknown svcnum!" unless $cust_svc;
+my $cust_pkg = $cust_svc->cust_pkg;
+if ( $cust_pkg ) {
+ errorpage( 'This account has already been audited. Cancel the '.
+ qq!<A HREF="${p}view/cust_main.cgi?!. $cust_pkg->custnum.
+ '#cust_pkg'. $cust_pkg->pkgnum. '">'.
+ 'package</A> instead.');
+}
+
+my $error = $cust_svc->cancel;
+
+</%init>
diff --git a/httemplate/misc/cancel_cust.html b/httemplate/misc/cancel_cust.html
new file mode 100644
index 0000000..12c37eb
--- /dev/null
+++ b/httemplate/misc/cancel_cust.html
@@ -0,0 +1,63 @@
+<% include('/elements/header-popup.html', 'Cancel customer' ) %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="cust_cancel_popup" ACTION="<% popurl(1) %>cust_main-cancel.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+
+ <P ALIGN="center"><B>Permanently delete all services and cancel this customer?</B>
+
+ <% $ban %>
+
+<BR><BR>
+
+<% ntable("#cccccc", 2) %>
+
+<% include('/elements/tr-select-reason.html',
+ 'field' => 'reasonnum',
+ 'reason_class' => 'C',
+ 'cgi' => $cgi,
+ 'control_button' => "document.getElementById('confirm_cancel_cust_button')",
+ )
+%>
+
+</TABLE>
+
+<BR>
+<P ALIGN="CENTER">
+<INPUT TYPE="submit" NAME="submit" ID="confirm_cancel_cust_button" VALUE="Cancel customer" DISABLED> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT TYPE="BUTTON" VALUE="Don't cancel" onClick="parent.cClick();">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
+my $custnum = $1;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied" unless $curuser->access_right('Cancel customer');
+
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+} );
+die "No customer # $custnum" unless $cust_main;
+
+my $ban = '';
+if ( $cust_main->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/ ) {
+ $ban = '<BR><P ALIGN="center">'.
+ '<INPUT TYPE="checkbox" NAME="ban" VALUE="1"> Ban this customer\'s ';
+ if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
+ $ban .= 'credit card';
+ } elsif ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
+ $ban .= 'ACH account';
+ }
+}
+
+</%init>
+
diff --git a/httemplate/misc/cancel_pkg.html b/httemplate/misc/cancel_pkg.html
new file mode 100755
index 0000000..e0e5fd1
--- /dev/null
+++ b/httemplate/misc/cancel_pkg.html
@@ -0,0 +1,109 @@
+%# if ( $link eq 'popup' ) {
+ <% include('/elements/header-popup.html', $title ) %>
+%# } else {
+%# <% include("/elements/header.html", $title, '') %>
+%# }
+
+<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+<SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="sc_popup" ACTION="<% popurl(1) %>process/cancel_pkg.html" METHOD=POST>
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+<INPUT TYPE="hidden" NAME="method" VALUE="<% $method %>">
+
+
+<BR><BR>
+<% ucfirst($method) . " $pkgnum: " .$part_pkg->pkg. ' - ' .$part_pkg->comment %>
+<% ntable("#cccccc", 2) %>
+
+% if ($method eq 'expire' || $method eq 'adjourn') {
+<TR>
+ <TD><% $submit =~ /^(\w*)\s/ %> package on </TD>
+ <TD><INPUT TYPE="text" NAME="date" ID="expire_date" VALUE="<% $date |h %>">
+ <IMG SRC="<% $p %>images/calendar.png" ID="expire_button" STYLE="cursor:pointer" TITLE="Select date">
+ <BR><I>m/d/y</I>
+ </TD>
+</TR>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "expire_date",
+ ifFormat: "%m/%d/%Y",
+ button: "expire_button",
+ align: "BR"
+ });
+</SCRIPT>
+%}
+%
+
+<% include('/elements/tr-select-reason.html',
+ 'field' => 'reasonnum',
+ 'reason_class' => $class,
+ 'curr_value' => $reasonnum,
+ 'control_button' => "document.getElementById('confirm_cancel_pkg_button')",
+ )
+%>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" NAME="submit" ID="confirm_cancel_pkg_button" VALUE="<% $submit %>" DISABLED>
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+my $date = time2str("%m/%d/%Y", time);
+
+my($pkgnum, $reasonnum);
+if ( $cgi->param('error') ) {
+ $pkgnum = $cgi->param('pkgnum');
+ $reasonnum = $cgi->param('reasonnum');
+ $date = $cgi->param('date');
+} elsif ( $cgi->param('pkgnum') =~ /^(\d+)$/ ) {
+ $pkgnum = $1;
+ $reasonnum = '';
+} else {
+ die "illegal query ". $cgi->keywords;
+}
+
+$cgi->param('method') =~ /^(\w+)$/ or die 'illegal method';
+my $method = $1;
+
+my($class, $submit, $right);
+if ($method eq 'cancel') {
+ $class = 'C';
+ $submit = 'Cancel Now';
+ $right = 'Cancel customer package immediately';
+} elsif ($method eq 'expire') {
+ $class = 'C';
+ $submit = 'Cancel Later';
+ $right = 'Cancel customer package later';
+} elsif ($method eq 'suspend') {
+ $class = 'S';
+ $submit = 'Suspend Now';
+ $right = 'Suspend customer package';
+} elsif ($method eq 'adjourn') {
+ $class = 'S';
+ $submit = "Suspend Later";
+ $right = 'Suspend customer package later';
+} else {
+ die 'illegal query (unknown method param)';
+}
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied" unless $curuser->access_right($right);
+
+my $title = ucfirst($method) . ' Package';
+
+my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum})
+ or die "Unknown pkgnum: $pkgnum";
+
+my $part_pkg = $cust_pkg->part_pkg;
+
+</%init>
diff --git a/httemplate/misc/catchall.cgi b/httemplate/misc/catchall.cgi
new file mode 100755
index 0000000..240f34d
--- /dev/null
+++ b/httemplate/misc/catchall.cgi
@@ -0,0 +1,118 @@
+<% include('/elements/header.html', 'Domain Catchall Edit') %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<%$p1%>process/catchall.cgi" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum |h %>">
+Service #<FONT SIZE=+1><B><% $svcnum ? $svcnum : ' (NEW)' |h %></B></FONT>
+<BR><BR>
+
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum |h %>">
+
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
+
+% my $domain = $svc_domain->domain;
+% my $catchall = $svc_domain->catchall;
+
+<INPUT TYPE="hidden" NAME="domain" VALUE="<% $domain |h %>">
+
+Mail to <I>(anything)</I>@<B><% $domain |h %></B> forwards to <SELECT NAME="catchall" SIZE=1>
+% foreach $_ (keys %email) {
+ <OPTION<% $_ eq $catchall ? ' SELECTED' : '' %> VALUE="<% $_ %>"><% $email{$_} %>
+% }
+</SELECT>
+<BR><BR>
+
+<INPUT TYPE="submit" VALUE="Submit">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit domain catchall');
+
+my $conf = new FS::Conf;
+
+my($svc_domain, $svcnum, $pkgnum, $svcpart, $part_svc);
+if ( $cgi->param('error') ) {
+ $svc_domain = new FS::svc_domain ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_domain')
+ } );
+ $svcnum = $svc_domain->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+} else {
+ my($query) = $cgi->keywords;
+ if ( $query =~ /^(\d+)$/ ) { #editing
+ $svcnum=$1;
+ $svc_domain=qsearchs('svc_domain',{'svcnum'=>$svcnum})
+ or die "Unknown (svc_domain) svcnum!";
+
+ my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum=$cust_svc->pkgnum;
+ $svcpart=$cust_svc->svcpart;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ } else {
+
+ die "Invalid (svc_domain) svcnum!";
+
+ }
+}
+
+my %email;
+if ($pkgnum) {
+
+ #find all possible user svcnums (and emails)
+
+ #starting with that currently attached
+ if ($svc_domain->catchall) {
+ my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$svc_domain->catchall});
+ $email{$svc_domain->catchall} = $svc_acct->email;
+ }
+
+ #and including the rest for this customer
+ my($u_part_svc,@u_acct_svcparts);
+ foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) {
+ push @u_acct_svcparts,$u_part_svc->getfield('svcpart');
+ }
+
+ my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ my($custnum)=$cust_pkg->getfield('custnum');
+ my($i_cust_pkg);
+ foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
+ my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
+ my($acct_svcpart);
+ foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding
+ #record(s) in cust_svc ( for this
+ #pkgnum ! )
+ my($i_cust_svc);
+ foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) {
+ my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')});
+ $email{$svc_acct->getfield('svcnum')}=$svc_acct->email;
+ }
+ }
+ }
+
+} else {
+
+ my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$svc_domain->catchall});
+ $email{$svc_domain->catchall} = $svc_acct->email;
+}
+
+# add an absence of a catchall
+$email{''} = "(none)";
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/misc/cdr-import.html b/httemplate/misc/cdr-import.html
new file mode 100644
index 0000000..7af6c52
--- /dev/null
+++ b/httemplate/misc/cdr-import.html
@@ -0,0 +1,61 @@
+<% include("/elements/header.html",'Call Detail Record Import') %>
+
+<% include( '/elements/form-file_upload.html',
+ 'name' => 'CDRImportForm',
+ 'action' => 'process/cdr-import.html',
+ 'num_files' => 1,
+ 'fields' => [ 'format', 'cdrbatch', ],
+ 'message' => 'CDR import successful',
+ 'url' => $p."search/cdr.html?cdrbatch=$cdrbatch",
+ )
+%>
+
+Import a file containing Call Detail Records (CDRs).<BR><BR>
+
+<INPUT TYPE="hidden" NAME="cdrbatch" VALUE="<% $cdrbatch %>"%>
+
+<% ntable('#cccccc', 2) %>
+
+ <TR>
+ <TD>CDR Format</TD>
+ <TD>
+ <SELECT NAME="format">
+% foreach my $format ( keys %formats ) {
+ <OPTION VALUE="<% $format %>"><% $formats{$format} %></OPTION>
+% }
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/file-upload.html',
+ 'field' => 'file',
+ 'label' => 'Filename',
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ ID = "submit"
+ VALUE = "Import file"
+ onClick = "document.InventoryItemImportForm.submit.disabled=true;"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+tie my %formats, 'Tie::IxHash', FS::cdr->import_formats;
+
+my $cdrbatch = time2str('webimport-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+</%init>
diff --git a/httemplate/misc/cdr.cgi b/httemplate/misc/cdr.cgi
new file mode 100644
index 0000000..d2ee773
--- /dev/null
+++ b/httemplate/misc/cdr.cgi
@@ -0,0 +1,48 @@
+%# <% $cgi->redirect(popurl(2). "search/cdr.html") %>
+%# i should be a popup and reload my parent... until then, this will do
+<% include('/elements/header.html','CDR update successful') %>
+<% include('/elements/footer.html') %>
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit rating data');
+
+$cgi->param('action') =~ /^(new|del|(reprocess|delete) selected)$/
+ or die "Illegal action";
+my $action = $1;
+
+my $cdr;
+if ( $action eq 'new' || $action eq 'del' ) {
+ $cgi->param('acctid') =~ /^(\d+)$/ or die "Illegal acctid";
+ my $acctid = $1;
+ $cdr = qsearchs('cdr', { 'acctid' => $1 })
+ or die "unknown acctid $acctid";
+}
+
+if ( $action eq 'new' ) {
+ my %hash = $cdr->hash;
+ $hash{'freesidestatus'} = '';
+ my $new = new FS::cdr \%hash;
+ my $error = $new->replace($cdr);
+ die $error if $error;
+} elsif ( $action eq 'del' ) {
+ my $error = $cdr->delete;
+ die $error if $error;
+} elsif ( $action =~ /^(reprocess|delete) selected$/ ) {
+ foreach my $acctid (
+ map { /^acctid(\d+)$/; $1; } grep /^acctid\d+$/, $cgi->param
+ ) {
+ my $cdr = qsearchs('cdr', { 'acctid' => $acctid });
+ if ( $action eq 'reprocess selected' && $cdr ) { #new
+ my %hash = $cdr->hash;
+ $hash{'freesidestatus'} = '';
+ my $new = new FS::cdr \%hash;
+ my $error = $new->replace($cdr);
+ die $error if $error;
+ } elsif ( $action eq 'delete selected' && $cdr ) { #del
+ my $error = $cdr->delete;
+ die $error if $error;
+ }
+ }
+}
+
+</%init>
diff --git a/httemplate/misc/change_pkg.cgi b/httemplate/misc/change_pkg.cgi
new file mode 100755
index 0000000..c4dfca2
--- /dev/null
+++ b/httemplate/misc/change_pkg.cgi
@@ -0,0 +1,72 @@
+<% include('/elements/header-popup.html', "Change Package") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p %>edit/process/change-cust_pkg.html" METHOD=POST>
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+
+<% ntable('#cccccc') %>
+
+ <TR>
+ <TH ALIGN="right">Current package</TH>
+ <TD COLSPAN=7>
+ <% $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : '' %><B><% $part_pkg->pkg |h %></B> - <% $part_pkg->comment |h %>
+ </TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">New package</TH>
+ <TD COLSPAN=7>
+ <% include('/elements/select-cust-part_pkg.html',
+ 'cust_main' => $cust_main,
+ 'element_name' => 'pkgpart',
+ #'extra_sql' => ' AND pkgpart != '. $cust_pkg->pkgpart,
+ 'curr_value' => scalar($cgi->param('pkgpart')),
+ )
+ %>
+ </TD>
+ </TR>
+
+ <% include('/elements/tr-select-cust_location.html',
+ 'cgi' => $cgi,
+ 'cust_main' => $cust_main,
+ )
+ %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Change package">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+my $conf = new FS::Conf;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Change customer package');
+
+my $pkgnum = scalar($cgi->param('pkgnum'));
+$pkgnum =~ /^(\d+)$/ or die "illegal pkgnum $pkgnum";
+$pkgnum = $1;
+
+my $cust_pkg =
+ qsearchs({
+ 'table' => 'cust_pkg',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'pkgnum' => $pkgnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+ }) or die "unknown pkgnum $pkgnum";
+
+my $cust_main = $cust_pkg->cust_main
+ or die "can't get cust_main record for custnum ". $cust_pkg->custnum.
+ " ( pkgnum ". cust_pkg->pkgnum. ")";
+
+my $part_pkg = $cust_pkg->part_pkg;
+
+</%init>
diff --git a/httemplate/misc/copy-rate_detail.html b/httemplate/misc/copy-rate_detail.html
new file mode 100644
index 0000000..3d328ce
--- /dev/null
+++ b/httemplate/misc/copy-rate_detail.html
@@ -0,0 +1,61 @@
+<% include( '/elements/header.html', 'Copy rates between plans', menubar(
+ 'View all rate plans' => "${p}browse/rate.cgi",
+ ))
+%>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="process/copy-rate_detail.html">
+
+<% ntable('#cccccc') %>
+
+ <% include( '/elements/tr-justtitle.html', 'value' => 'Copy rates' ) %>
+
+ <% include( '/elements/tr-select-rate.html',
+ 'label' => 'From rate plan',
+ 'element_name' => 'src_ratenum',
+ )
+ %>
+
+ <% include( '/elements/tr-select-rate.html',
+ 'label' => 'To rate plan',
+ 'element_name' => 'dst_ratenum',
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN=2>Copy country codes</TD>
+ </TR>
+
+ <TR>
+ <TD COLSPAN=2>
+
+ <% include( '/elements/checkboxes.html',
+ 'names_list' => [ FS::rate_prefix->all_countrycodes ],
+ 'element_name_prefix' => 'countrycode',
+ )
+ %>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center">
+ <INPUT TYPE="submit" VALUE="Copy rates">
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+#should have some javascript that enables submit button only when both src & dst
+#rates are chosen
+
+</%init>
diff --git a/httemplate/misc/counties.cgi b/httemplate/misc/counties.cgi
new file mode 100644
index 0000000..c022a27
--- /dev/null
+++ b/httemplate/misc/counties.cgi
@@ -0,0 +1,7 @@
+[ <% join(', ', map { qq("$_") } @counties) %> ]
+<%init>
+
+my( $state, $country ) = $cgi->param('arg');
+my @counties = counties($state, $country);
+
+</%init>
diff --git a/httemplate/misc/cust_main-cancel.cgi b/httemplate/misc/cust_main-cancel.cgi
new file mode 100755
index 0000000..009a7d4
--- /dev/null
+++ b/httemplate/misc/cust_main-cancel.cgi
@@ -0,0 +1,57 @@
+<% header("Customer cancelled") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Cancel customer');
+
+my $custnum;
+my $ban = '';
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $custnum = $1;
+ $ban = $cgi->param('ban');
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ || die "Illegal custnum";
+ $custnum = $1;
+}
+
+#false laziness w/process/cancel_pkg.html
+
+#untaint reasonnum
+my $reasonnum = $cgi->param('reasonnum');
+$reasonnum =~ /^(-?\d+)$/ || die "Illegal reasonnum";
+$reasonnum = $1;
+
+if ($reasonnum == -1) {
+ $reasonnum = {
+ 'typenum' => scalar( $cgi->param('newreasonnumT') ),
+ 'reason' => scalar( $cgi->param('newreasonnum' ) ),
+ };
+}
+
+#eslaf
+
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+} );
+
+warn "cancelling $cust_main";
+my @errors = $cust_main->cancel(
+ 'ban' => $ban,
+ 'reason' => $reasonnum,
+);
+my $error = join(' / ', @errors) if scalar(@errors);
+
+if ( $error ) {
+ $cgi->param('error', $error);
+ print $cgi->redirect(popurl(1). "cancel_cust.html?". $cgi->query_string );
+}
+
+</%init>
diff --git a/httemplate/misc/cust_main-import.cgi b/httemplate/misc/cust_main-import.cgi
new file mode 100644
index 0000000..b822c5d
--- /dev/null
+++ b/httemplate/misc/cust_main-import.cgi
@@ -0,0 +1,148 @@
+<% include("/elements/header.html",'Batch Customer Import') %>
+
+Import a file containing customer records.
+<BR><BR>
+
+<% include( '/elements/form-file_upload.html',
+ 'name' => 'CustomerImportForm',
+ 'action' => 'process/cust_main-import.cgi',
+ 'num_files' => 1,
+ 'fields' => [ 'agentnum', 'custbatch', 'format' ],
+ 'message' => 'Customer import successful',
+ 'url' => $p."search/cust_main.html?custbatch=$custbatch",
+ )
+%>
+
+<% &ntable("#cccccc", 2) %>
+
+ <% include( '/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+ %>
+
+ <INPUT TYPE="hidden" NAME="custbatch" VALUE="<% $custbatch %>"%>
+
+ <TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <!-- <OPTION VALUE="simple">Simple -->
+ <OPTION VALUE="extended" SELECTED>Extended
+ <OPTION VALUE="extended-plus_company">Extended plus company
+ <OPTION VALUE="svc_external">External service
+ <OPTION VALUE="svc_external_svc_phone">External service and phone service
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/file-upload.html',
+ 'field' => 'file',
+ 'label' => 'Filename',
+ )
+ %>
+
+
+% #include('/elements/tr-select-part_referral.html')
+%
+
+
+<!--
+<TR>
+ <TH>First package</TH>
+ <TD>
+ This needs to be agent-virtualized if it gets used!
+ <SELECT NAME="pkgpart"><OPTION VALUE="">(none)</OPTION>
+% foreach my $part_pkg ( qsearch('part_pkg',{'disabled'=>'' }) ) {
+
+ <OPTION VALUE="<% $part_pkg->pkgpart %>"><% $part_pkg->pkg. ' - '. $part_pkg->comment %></OPTION>
+% }
+
+ </SELECT>
+ </TD>
+</TR>
+-->
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ ID = "submit"
+ VALUE = "Import file"
+ onClick = "document.CustomerImportForm.submit.disabled=true;"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+
+<!-- Simple file format is CSV, with the following field order: <i>cust_pkg.setup, dayphone, first, last, address1, address2, city, state, zip, comments</i>
+<BR><BR> -->
+
+Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets. The file should have a .CSV or .XLS extension.
+<BR><BR>
+
+<b>Extended</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password</i>
+<BR><BR>
+
+<b>Extended plus company</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password</i>
+<BR><BR>
+
+<b>External service</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, next_bill_date, id, title</i>
+<BR><BR>
+
+<b>External service and phone service</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, next_bill_date, id, title, countrycode, phonenum, sip_password, pin</i>
+<BR><BR>
+
+<%$req%> Required fields
+<BR><BR>
+
+Field information:
+
+<ul>
+
+ <li><i>agent_custid</i>: This is the reseller's idea of the customer number or identifier. It may be left blank. If specified, it must be unique per-agent.
+
+ <li><i>refnum</i>: Advertising source number - where a customer heard about your service. Configuration -&gt; Miscellaneous -&gt; View/Edit advertising sources. This field has special treatment upon import: If a string is passed instead
+of an integer, the string is searched for and if necessary auto-created in the
+advertising source table.
+
+ <li><i>payinfo</i>: Credit card number, or leave this, <i>paycvv</i> and <i>paydate</i> blank for email/paper invoicing.
+
+ <li><i>paycvv</i>: CVV2 number (three digits on the back of the credit card)
+
+ <li><i>paydate</i>: Credit card expiration date, MM/YYYY or MM/YY (M/YY and M/YYYY are also accepted).
+
+ <li><i>invoicing_list</i>: Email address for invoices, or POST for postal invoices.
+
+ <li><i>pkgpart</i>: Package definition. Configuration -&gt; Provisioning, services and packages -&gt; View/Edit package definitions
+
+ <li><i>username</i> and <i>_password</i> are required if <i>pkgpart</i> is specified. (Extended and Extended plus company formats)
+
+ <li><i>id</i>: External service id, integer
+
+ <li><i>title</i>: External service identifier, text
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+my $req = qq!<font color="#ff0000">*</font>!;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $custbatch = time2str('webimport-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+</%init>
diff --git a/httemplate/misc/cust_main-import_charges.cgi b/httemplate/misc/cust_main-import_charges.cgi
new file mode 100644
index 0000000..3801929
--- /dev/null
+++ b/httemplate/misc/cust_main-import_charges.cgi
@@ -0,0 +1,22 @@
+<% include('/elements/header.html', 'Batch Customer Charge') %>
+
+<FORM ACTION="process/cust_main-import_charges.cgi" METHOD="post" ENCTYPE="multipart/form-data">
+
+Import a CSV file containing customer charges.<BR><BR>
+Default file format is CSV, with the following field order: <i>custnum, amount, description</i><BR><BR>
+If <i>amount</i> is negative, a credit will be applied instead.<BR><BR>
+<BR><BR>
+
+CSV Filename: <INPUT TYPE="file" NAME="csvfile"><BR><BR>
+<INPUT TYPE="submit" VALUE="Import">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+</%init>
diff --git a/httemplate/misc/cust_main_note-import.cgi b/httemplate/misc/cust_main_note-import.cgi
new file mode 100644
index 0000000..b93c5c1
--- /dev/null
+++ b/httemplate/misc/cust_main_note-import.cgi
@@ -0,0 +1,207 @@
+<% include("/elements/header.html", 'Batch Customer Note Import') %>
+%
+
+<FORM ACTION="process/cust_main_note-import.cgi" METHOD="POST">
+
+
+<SCRIPT TYPE="text/javascript">
+
+ function clearhint_custnum() {
+
+ if ( this.value == 'Not found' ) {
+ this.value = '';
+ this.style.color = '#000000';
+ }
+
+ }
+
+ function search_custnum() {
+
+ this.style.color = '#000000'
+
+ var custnum_obj = this;
+ var searchrow = this.getAttribute('rownum');
+ var custnum = this.value;
+ var name_obj = document.getElementById('name'+searchrow);
+
+ if ( custnum == 'searching...' || custnum == 'Not found' )
+ return;
+
+ var customer_select = document.getElementById('cust_select'+searchrow);
+
+ if ( custnum == '' ) {
+ customer_select.selectedIndex = 0;
+ return;
+ }
+
+ custnum_obj.value = 'searching...';
+ custnum_obj.disabled = true;
+ custnum_obj.style.backgroundColor = '#dddddd';
+
+
+ //alert('search for custnum ' + custnum + ', row#' + searchrow );
+
+ function search_custnum_update(name) {
+
+ var name = eval('(' + name + ')' );
+
+ custnum_obj.disabled = false;
+ custnum_obj.style.backgroundColor = '#ffffff';
+
+ if ( name.length > 0 ) {
+ //alert('custnum found: ' + name);
+ opt(customer_select,custnum,name,'#000000');
+ customer_select.selectedIndex = customer_select.length - 1;
+ custnum_obj.value = custnum;
+ name_obj.value = name;
+ } else {
+ custnum_obj.value = 'Not found';
+ custnum_obj.style.color = '#ff0000';
+ }
+
+ }
+
+ custnum_search( custnum, search_custnum_update );
+
+ }
+
+ function select_customer() {
+
+ var custnum = this.options[this.selectedIndex].value;
+ var name = this.options[this.selectedIndex].text;
+
+ var searchrow = this.getAttribute('rownum');
+ var custnum_obj = document.getElementById('custnum'+searchrow);
+ var name_obj = document.getElementById('name'+searchrow);
+
+ custnum_obj.value = custnum;
+ custnum_obj.style.color = '#000000';
+
+ name_obj.value = name;
+
+ }
+
+ function opt(what,value,text,color) {
+ var optionName = new Option(text, value, false, false);
+ optionName.style.color = color;
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function previewChanged(what) {
+ var submit_obj = document.getElementById('importsubmit');
+ if (what.checked) {
+ submit_obj.value = 'Preview note import';
+ }else{
+ submit_obj.value = 'Import notes';
+ }
+ }
+
+</SCRIPT>
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
+ 'subs' => [qw( custnum_search )],
+ )
+%>
+
+% my $fh = $cgi->upload('csvfile');
+% my $csv = new Text::CSV_XS;
+% my $skip_fuzzies = $cgi->param('fuzzies') ? 0 : 1;
+%
+% if ( defined($fh) ) {
+ <TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+ <TR>
+ <TH>Cust #</TH>
+ <TH>Customer</TH>
+ <TH>Last</TH>
+ <TH>First</TH>
+ <TH>Note to be added</TH>
+ </TR>
+% my $agentnum => scalar($cgi->param('agentnum')),
+% my $line;
+% my $row = 0;
+% while ( defined($line=<$fh>) ) {
+% $line =~ s/(\S*)\s*$/$1/;
+% $line =~ s/^(.*)(#!).*/$1/;
+%
+% $csv->parse($line) or die "can't parse line: " . $csv->error_input();
+% my $custnum = 0;
+% my @values = $csv->fields();
+% my $last = shift @values;
+% if ($last =~ /^\s*(\d+)\s*$/ ) {
+% $custnum = $1;
+% $last = shift @values;
+% }
+% my $first = shift @values;
+% my $note = join ' ', @values;
+% next unless ( $last || $first || $note );
+% my @cust_main = ();
+% warn "searching for: $last, $first" if ($first || $last);
+% if ($custnum) {
+% @cust_main = qsearch('cust_main', { 'custnum' => $custnum });
+% } else {
+% @cust_main = FS::cust_main::smart_search(
+% 'search' => "$last, $first",
+% 'no_fuzzy_on_exact' => $skip_fuzzies,
+% )
+% if ($first || $last);
+% }
+%
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="custnum<% $row %>" ID="custnum<% $row %>" SIZE=8 MAXLENGTH=12 VALUE="<% $cust_main[0] ? $cust_main[0]->custnum : '' %>" rownum="<% $row %>">
+ <SCRIPT TYPE="text/javascript">
+ var custnum_input<% $row %> = document.getElementById("custnum<% $row %>");
+ custnum_input<% $row %>.onfocus = clearhint_custnum;
+ custnum_input<% $row %>.onchange = search_custnum;
+ </SCRIPT>
+ </TD>
+ <TD>
+ <SELECT NAME="cust_select<% $row %>" ID="cust_select<% $row %>" rownum="<% $row %>">
+ <OPTION VALUE="">---</OPTION>
+% my $i=0;
+% foreach (@cust_main) {
+ <OPTION <% $i ? '' : 'SELECTED' %> VALUE="<% $_->custnum %>"><% $_->name %></OPTION>
+% $i++;
+% }
+ </SELECT>
+ <SCRIPT TYPE="text/javascript">
+ var customer_select<% $row %> = document.getElementById("cust_select<% $row %>");
+ customer_select<% $row %>.onchange = select_customer;
+ </SCRIPT>
+ <INPUT TYPE="hidden" NAME="name<% $row %>" ID="name<% $row %>" VALUE="<% $i ? $cust_main[0]->name : '' %>">
+ </TD>
+ <TD>
+ <% $first %>
+ <INPUT TYPE="hidden" NAME="first<% $row %>" VALUE="<% $first %>">
+ </TD>
+ <TD>
+ <% $last %>
+ <INPUT TYPE="hidden" NAME="last<% $row %>" VALUE="<% $last %>">
+ </TD>
+ <TD>
+ <% $note %>
+ <INPUT TYPE="hidden" NAME="note<% $row %>" VALUE="<% $note %>">
+ </TD>
+ </TR>
+% $row++;
+% }
+ </TABLE>
+ <INPUT TYPE="submit" NAME="submit" ID="importsubmit" VALUE="Import notes">
+ <INPUT TYPE="checkbox" NAME="preview" onchange="previewChanged(this);">
+ Preview mode
+% } else {
+ No file supplied
+% }
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+</%init>
diff --git a/httemplate/misc/cust_main_note-import.html b/httemplate/misc/cust_main_note-import.html
new file mode 100644
index 0000000..d8fefa7
--- /dev/null
+++ b/httemplate/misc/cust_main_note-import.html
@@ -0,0 +1,39 @@
+<% include("/elements/header.html",'Batch Customer Note Import') %>
+
+<FORM ACTION="cust_main_note-import.cgi" METHOD="post" ENCTYPE="multipart/form-data">
+
+Import a CSV file containing customer notes records.
+<BR><BR>
+
+File format is CSV, with the following field order: <i>[custnum,] last, first, notefield1, notefield2, notefield3...</i>
+<BR>
+The optional custnum field is identified by being numeric.
+Anything after the character sequence #! is ignored.
+<BR><BR>
+
+<% &ntable("#cccccc") %>
+
+<TR>
+ <TH ALIGN="right">CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="csvfile"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right">Include additional possibilites when exact match is found</TH>
+ <TD><INPUT TYPE="checkbox" NAME="fuzzies"></TD>
+</TR>
+
+</TABLE>
+<BR><BR>
+
+<INPUT TYPE="submit" VALUE="Load and match">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+</%init>
+
diff --git a/httemplate/misc/cust_pay-import.cgi b/httemplate/misc/cust_pay-import.cgi
new file mode 100644
index 0000000..849a25b
--- /dev/null
+++ b/httemplate/misc/cust_pay-import.cgi
@@ -0,0 +1,62 @@
+<% include("/elements/header.html",'Batch Payment Import') %>
+
+Import a CSV file containing customer payments.
+<BR><BR>
+
+<FORM ACTION="process/cust_pay-import.cgi" METHOD="post" ENCTYPE="multipart/form-data">
+
+<% &ntable("#cccccc", 2) %>
+
+<% include('/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+%>
+
+<TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <OPTION VALUE="simple">Simple
+<!-- <OPTION VALUE="extended" SELECTED>Extended -->
+ </SELECT>
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="csvfile"></TD>
+</TR>
+
+<TR><TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"><INPUT TYPE="submit" VALUE="Import CSV file"></TD></TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+
+Simple file format is CSV, with the following field order: <i>custnum, agent_custid, amount, checknum</i>
+<BR><BR>
+
+<!-- Extended file format is not yet defined</i>
+<BR><BR> -->
+
+Field information:
+
+<ul>
+
+ <li><i>custnum</i>: This is the freeside customer number. It may be left blank. If specified, agent_custid must be blank.
+
+ <li><i>agent_custid</i>: This is the reseller's idea of the customer number or identifier. It may be left blank. If specified, custnum must be blank.
+
+ <li><i>amount</i>: A positive numeric value with at most two digits after the decimal point.
+
+ <li><i>checknum</i>: A sequences of digits. May be left blank.
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
diff --git a/httemplate/misc/delay_susp_pkg.html b/httemplate/misc/delay_susp_pkg.html
new file mode 100755
index 0000000..1158a35
--- /dev/null
+++ b/httemplate/misc/delay_susp_pkg.html
@@ -0,0 +1,70 @@
+%# if ( $link eq 'popup' ) {
+ <% include('/elements/header-popup.html', $title ) %>
+%# } else {
+%# <% include("/elements/header.html", $title, '') %>
+%# }
+
+<% include('/elements/init_calendar.html') %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="ds_popup" ACTION="<% popurl(1) %>process/delay_susp_pkg.html" METHOD=POST>
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+
+<BR><BR>
+<% "Delay automatic suspension of $pkgnum: " .$part_pkg->pkg. ' - ' .$part_pkg->comment %>
+<% ntable("#cccccc", 2) %>
+
+<TR>
+ <TD>Delay until</TD>
+ <TD><INPUT TYPE="text" NAME="date" ID="dun_date" VALUE="<% $date |h %>">
+ <IMG SRC="<% $p %>images/calendar.png" ID="dun_button" STYLE="cursor:pointer" TITLE="Select date">
+ <BR><I>m/d/y</I>
+ </TD>
+</TR>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "dun_date",
+ ifFormat: "%m/%d/%Y",
+ button: "dun_button",
+ align: "BR"
+ });
+</SCRIPT>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="<% $submit %>">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+my $date = time2str("%m/%d/%Y", time);
+
+my($pkgnum);
+if ( $cgi->param('error') ) {
+ $pkgnum = $cgi->param('pkgnum');
+ $date = $cgi->param('date');
+} elsif ( $cgi->param('pkgnum') =~ /^(\d+)$/ ) {
+ $pkgnum = $1;
+} else {
+ die "illegal query ". $cgi->keywords;
+}
+
+my $submit = 'Delay Suspension';
+my $right = 'Delay suspension events';
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied" unless $curuser->access_right($right);
+
+my $title = 'Delay Suspension of Package';
+
+my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum})
+ or die "Unknown pkgnum: $pkgnum";
+
+my $part_pkg = $cust_pkg->part_pkg;
+
+</%init>
diff --git a/httemplate/misc/delete-agent_payment_gateway.cgi b/httemplate/misc/delete-agent_payment_gateway.cgi
new file mode 100644
index 0000000..20a202e
--- /dev/null
+++ b/httemplate/misc/delete-agent_payment_gateway.cgi
@@ -0,0 +1,15 @@
+% die "you don't have the 'Configuration' access right"
+% unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+%
+% my($query) = $cgi->keywords;
+% $query =~ /^(\d+)$/ || die "Illegal agentgatewaynum";
+% my $agentgatewaynum = $1;
+%
+% my $agent_payment_gateway = qsearchs('agent_payment_gateway', {
+% 'agentgatewaynum' => $agentgatewaynum,
+% });
+%
+% my $error = $agent_payment_gateway->delete;
+% errorpage($error) if $error;
+%
+% print $cgi->redirect($p. "browse/agent.cgi");
diff --git a/httemplate/misc/delete-cust_credit.cgi b/httemplate/misc/delete-cust_credit.cgi
new file mode 100755
index 0000000..03eb472
--- /dev/null
+++ b/httemplate/misc/delete-cust_credit.cgi
@@ -0,0 +1,21 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Delete credit');
+
+#untaint crednum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal crednum";
+my $crednum = $1;
+
+my $cust_credit = qsearchs('cust_credit',{'crednum'=>$crednum});
+my $custnum = $cust_credit->custnum;
+
+my $error = $cust_credit->delete;
+
+</%init>
diff --git a/httemplate/misc/delete-cust_pay.cgi b/httemplate/misc/delete-cust_pay.cgi
new file mode 100755
index 0000000..38e7e4b
--- /dev/null
+++ b/httemplate/misc/delete-cust_pay.cgi
@@ -0,0 +1,21 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Delete payment');
+
+#untaint paynum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal paynum";
+my $paynum = $1;
+
+my $cust_pay = qsearchs('cust_pay',{'paynum'=>$paynum});
+my $custnum = $cust_pay->custnum;
+
+my $error = $cust_pay->delete;
+
+</%init>
diff --git a/httemplate/misc/delete-cust_refund.cgi b/httemplate/misc/delete-cust_refund.cgi
new file mode 100755
index 0000000..983a79d
--- /dev/null
+++ b/httemplate/misc/delete-cust_refund.cgi
@@ -0,0 +1,21 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Delete refund');
+
+#untaint refundnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal refundnum";
+my $refundnum = $1;
+
+my $cust_refund = qsearchs('cust_refund',{'refundnum'=>$refundnum});
+my $custnum = $cust_refund->custnum;
+
+my $error = $cust_refund->delete;
+
+</%init>
diff --git a/httemplate/misc/delete-customer.cgi b/httemplate/misc/delete-customer.cgi
new file mode 100755
index 0000000..203ed36
--- /dev/null
+++ b/httemplate/misc/delete-customer.cgi
@@ -0,0 +1,64 @@
+<% include('/elements/header.html', 'Delete customer') %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% popurl(1) %>process/delete-customer.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum |h %>">
+
+%if ( qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } ) ) {
+ Move uncancelled packages to customer number
+ <INPUT TYPE="text" NAME="new_custnum" VALUE="<% $new_custnum |h %>"><BR><BR>
+%}
+
+This will <B>completely remove</B> all traces of this customer record. This
+is <B>not</B> what you want if this is a real customer who has simply
+canceled service with you. For that, cancel all of the customer's packages.
+(you can optionally hide cancelled customers with the <A HREF="../config/config-view.cgi#hidecancelledcustomers">hidecancelledcustomers</A> configuration option)
+<BR>
+<BR>Are you <B>absolutely sure</B> you want to delete this customer?
+<BR><INPUT TYPE="submit" VALUE="Yes">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+%#Deleting a customer you have financial records on (i.e. credits) is
+%#typically considered fraudulant bookkeeping. Remember, deleting
+%#customers should ONLY be used for completely bogus records. You should
+%#NOT delete real customers who simply discontinue service.
+%#
+%#For real customers who simply discontinue service, cancel all of the
+%#customer's packages. Customers with all cancelled packages are not
+%#billed. There is no need to take further action to prevent billing on
+%#customers with all cancelled packages.
+%#
+%#Also see the "hidecancelledcustomers" and "hidecancelledpackages"
+%#configuration options, which will allow you to surpress the display of
+%#cancelled customers and packages, respectively.
+
+<%init>
+
+my $conf = new FS::Conf;
+die "Customer deletions not enabled in configuration"
+ unless $conf->exists('deletecustomers');
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Delete customer');
+
+my($custnum, $new_custnum);
+if ( $cgi->param('error') ) {
+ $custnum = $cgi->param('custnum');
+ $new_custnum = $cgi->param('new_custnum');
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "Illegal query: $query";
+ $custnum = $1;
+ $new_custnum = '';
+}
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+} )
+ or die 'Unknown custnum';
+
+</%init>
diff --git a/httemplate/misc/delete-domain_record.cgi b/httemplate/misc/delete-domain_record.cgi
new file mode 100755
index 0000000..08eedde
--- /dev/null
+++ b/httemplate/misc/delete-domain_record.cgi
@@ -0,0 +1,20 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect($p. "view/svc_domain.cgi?". $domain_record->svcnum) %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice');
+
+#untaint recnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal recnum";
+my $recnum = $1;
+
+my $domain_record = qsearchs('domain_record',{'recnum'=>$recnum});
+
+my $error = $domain_record->delete;
+
+</%init>
diff --git a/httemplate/misc/delete-part_export.cgi b/httemplate/misc/delete-part_export.cgi
new file mode 100755
index 0000000..52404e0
--- /dev/null
+++ b/httemplate/misc/delete-part_export.cgi
@@ -0,0 +1,20 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect($p. "browse/part_export.cgi") %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+#untaint exportnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal exportnum";
+my $exportnum = $1;
+
+my $part_export = qsearchs('part_export',{'exportnum'=>$exportnum});
+
+my $error = $part_export->delete;
+
+</%init>
diff --git a/httemplate/misc/disable-payment_gateway.cgi b/httemplate/misc/disable-payment_gateway.cgi
new file mode 100644
index 0000000..13e1f92
--- /dev/null
+++ b/httemplate/misc/disable-payment_gateway.cgi
@@ -0,0 +1,25 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+%#<% $cgi->redirect(popurl(2). "browse/payment_gateway.html?showdiabled=$showdisabled") %>
+<% $cgi->redirect(popurl(2). "browse/payment_gateway.html") %>
+%}
+<%init>
+
+die "access deined"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+#my $showdisabled = 0;
+#$cgi->param('showdisabled') =~ /^(\d+)$/ and $showdisabled = $1;
+
+#$cgi->param('gatewaynum') =~ /^(\d+)$/ or die 'illegal gatewaynum';
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ or die 'illegal gatewaynum';
+my $gatewaynum = $1;
+
+my $payment_gateway =
+ qsearchs('payment_gateway', { 'gatewaynum' => $gatewaynum } );
+
+my $error = $payment_gateway->disable;
+
+</%init>
diff --git a/httemplate/misc/download-batch.cgi b/httemplate/misc/download-batch.cgi
new file mode 100644
index 0000000..57905da
--- /dev/null
+++ b/httemplate/misc/download-batch.cgi
@@ -0,0 +1,213 @@
+%if ($format eq "BoM") {
+%
+% my($origid,$datacenter,$typecode,$shortname,$longname,$mybank,$myacct) =
+% $conf->config("batchconfig-$format");
+%
+<% sprintf( "A%10s%04u%06u%05u%54s\n",$origid,$pay_batch->batchnum,$jdate,$datacenter,"").
+ sprintf( "XD%03u%06u%-15s%-30s%09u%-12s \n",$typecode,$jdate,$shortname,$longname,$mybank,$myacct )
+ %>
+%
+%}elsif ($format eq "PAP"){
+%
+% my($origid,$datacenter,$typecode,$shortname,$longname,$mybank,$myacct) =
+% $conf->config("batchconfig-$format");
+%
+<% sprintf( "H%10sD%3s%06u%-15s%09u%-12s%04u%19s\n",$origid,$typecode,$cdate,$shortname,$mybank,$myacct,$pay_batch->batchnum,"") %>
+%
+%
+%}elsif ($format eq "csv-td_canada_trust-merchant_pc_batch"){
+%# 1;
+%}elsif ($format eq "csv-chase_canada-E-xactBatch"){
+%
+% my($origid) = $conf->config("batchconfig-$format");
+<% sprintf( '$$E-xactBatchFileV1.0$$%s:%03u$$%s',$sdate,$pay_batch->batchnum, $origid)
+ %>
+%
+%}elsif ($format eq "ach-spiritone"){
+%# 1;
+%}else{
+% die "Unknown format for batch in batchconfig. \n";
+%}
+%
+%
+%for my $cust_pay_batch ( sort { $a->paybatchnum <=> $b->paybatchnum }
+% qsearch('cust_pay_batch',
+% {'batchnum'=>$pay_batch->batchnum} )
+%) {
+%
+% $cust_pay_batch->exp =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+% my( $mon, $y ) = ( $2, $1 );
+% if ( $conf->exists('batch-increment_expiration') ) {
+% my( $curmon, $curyear ) = (localtime(time))[4,5];
+% $curmon++; $curyear-=100;
+% $y++ while $y < $curyear || ( $y == $curyear && $mon < $curmon );
+% }
+% $mon = "0$mon" if $mon =~ /^\d$/;
+% $y = "0$y" if $y =~ /^\d$/;
+% my $exp = "$mon$y";
+%
+% if ( $first_download ) {
+% my $balance = $cust_pay_batch->cust_main->balance;
+% if ( $balance <= 0 ) {
+% my $error = $cust_pay_batch->delete;
+% if ( $error ) {
+% $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+% die $error;
+% }
+% next;
+% } elsif ( $balance < $cust_pay_batch->amount ) {
+% $cust_pay_batch->amount($balance);
+% my $error = $cust_pay_batch->replace;
+% if ( $error ) {
+% $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+% die $error;
+% }
+% #} elsif ( $balance > $cust_pay_batch->amount ) {
+% }
+% }
+%
+% $batchcount++;
+% $batchtotal += $cust_pay_batch->amount;
+%
+% if ($format eq "BoM") {
+%
+% my( $account, $aba ) = split( '@', $cust_pay_batch->payinfo );
+%
+<% sprintf( "D%010.0f%09u%-12s%-29s%-19s\n",$cust_pay_batch->amount*100,$aba,$account,$cust_pay_batch->payname,$cust_pay_batch->paybatchnum) %>
+%
+%
+% } elsif ($format eq "PAP"){
+%
+% my( $account, $aba ) = split( '@', $cust_pay_batch->payinfo );
+%
+<% sprintf( "D%-23s%06u%-19s%09u%-12s%010.0f\n",$cust_pay_batch->payname,$cdate,$cust_pay_batch->paybatchnum,$aba,$account,$cust_pay_batch->amount*100) %>
+%
+%
+% } elsif ($format eq "csv-td_canada_trust-merchant_pc_batch") {
+%
+%
+,,,,<% $cust_pay_batch->payinfo %>,<% $exp %>,<% $cust_pay_batch->amount %>,<% $cust_pay_batch->paybatchnum %>
+%
+%
+% } elsif ($format eq "csv-chase_canada-E-xactBatch"){
+%
+% my $payname=$cust_pay_batch->payname; $payname =~ tr/",/ /; #payinfo too? :P
+<% $cust_pay_batch->paybatchnum %>,<% $cust_pay_batch->custnum %>,<% $cust_pay_batch->invnum %>,"<% $payname %>",00,<% $cust_pay_batch->payinfo %>,<% $cust_pay_batch->amount %>,<% $exp %>,,
+%
+%
+% }elsif ($format eq "ach-spiritone"){
+%
+% my( $account, $aba ) = split( '@', $cust_pay_batch->payinfo );
+% my $payname=$cust_pay_batch->first. " ". $cust_pay_batch->last;
+% $payname =~ tr/",/ /; #payinfo too?
+% my $batchline = qq!"$payname","!.$cust_pay_batch->paybatchnum.
+% qq!","$aba","$account","27","!.$cust_pay_batch->amount.
+% qq!","27","0.00"!;
+% push @batchlines, $batchline;
+<% $batchline %>
+%
+% } else {
+% die "I'm already dead, but you did not know that.\n";
+% }
+%
+%}
+%
+%if ($format eq "BoM") {
+%
+%
+<% sprintf( "YD%08u%014.0f%56s\n",$batchcount,$batchtotal*100,"" ).
+ sprintf( "Z%014u%05u%014u%05u%41s\n",$batchtotal*100,$batchcount,"0","0","" ) %>
+%
+%
+%} elsif ($format eq "PAP"){
+%
+%
+<% sprintf( "T%08u%014.0f%57s\n",$batchcount,$batchtotal*100,"" ) %>
+%
+%
+%} elsif ($format eq "csv-td_canada_trust-merchant_pc_batch"){
+% #1;
+%} elsif ($format eq "csv-chase_canada-E-xactBatch"){
+% #1;
+%} elsif ($format eq "ach-spiritone"){
+% #1;
+%} else {
+% die "I'm already dead (again), but you did not know that.\n";
+%}
+%
+<%init>
+
+my $conf=new FS::Conf;
+
+#http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes
+http_header('Content-Type' => 'text/plain' );
+
+my $batchnum;
+if ( $cgi->param('batchnum') =~ /^(\d+)$/ ) {
+ $batchnum = $1;
+} else {
+ die "No batch number (bad URL) \n";
+}
+
+my $format;
+if ( $cgi->param('format') =~ /^([\w\- ]+)$/ ) {
+ $format = $1;
+} else {
+ $format = $conf->config('batch-default_format');
+}
+
+my $autopost;
+if ( $format eq 'ach-spiritone' ) {
+ $autopost = 1;
+}else{
+ $autopost = 0;
+}
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+my $dbh = dbh;
+
+my $pay_batch = qsearchs('pay_batch', {'batchnum'=>$batchnum, 'status'=>'O'} );
+my $first_download = 1;
+unless ($pay_batch) {
+ $pay_batch = qsearchs('pay_batch', {'batchnum'=>$batchnum, 'status'=>'I'} )
+ if $FS::CurrentUser::CurrentUser->access_right('Reprocess batches');
+ $first_download = 0;
+}
+die "No pending batch. \n" unless $pay_batch;
+
+my $error = $pay_batch->set_status('I');
+die "error updating batch status: $error\n" if $error;
+
+my $batchtotal=0;
+my $batchcount=0;
+
+my (@date)=localtime($pay_batch->download);
+my $jdate = sprintf("%03d", $date[5] % 100).sprintf("%03d", $date[7] + 1);
+my $cdate = sprintf("%02d", $date[3]).sprintf("%02d", $date[4] + 1).
+ sprintf("%02d", $date[5] % 100);
+my $sdate = sprintf("%02d", $date[5] % 100).'/'.sprintf("%02d", $date[4] + 1).
+ '/'.sprintf("%02d", $date[3]);
+
+my @batchlines = ();
+</%init>
+<%cleanup>
+if ($autopost) {
+ my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ my $fh = new File::Temp(
+ TEMPLATE => 'paybatch.'. $batchnum .'.XXXXXXXX',
+ DIR => $dir,
+ ) or die "can't open temp file: $!\n";
+
+ print $fh map{ "$_\n" } @batchlines;
+ seek $fh, 0, 0;
+
+ $error = $pay_batch->import_results( 'filehandle' => $fh,
+ 'format' => $format,
+ );
+ die $error if $error;
+}
+
+$dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+</%cleanup>
diff --git a/httemplate/misc/dump.cgi b/httemplate/misc/dump.cgi
new file mode 100644
index 0000000..3b60b20
--- /dev/null
+++ b/httemplate/misc/dump.cgi
@@ -0,0 +1,20 @@
+% die "access denied"
+% unless $FS::CurrentUser::CurrentUser->access_right('Export');
+%
+% if ( driver_name =~ /^Pg$/ ) {
+% my $dbname = (split(':', datasrc))[2];
+% if ( $dbname =~ /[;=]/ ) {
+% my %elements = map { /^(\w+)=(.*)$/; $1=>$2 } split(';', $dbname);
+% $dbname = $elements{'dbname'};
+% }
+% open(DUMP,"pg_dump $dbname |");
+% } else {
+% errorpage("don't (yet) know how to dump ". driver_name. " databases");
+% }
+%
+% http_header('Content-Type' => 'text/plain' );
+%
+% while (<DUMP>) {
+% print $_;
+% }
+% close DUMP;
diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html
new file mode 100644
index 0000000..0d3d622
--- /dev/null
+++ b/httemplate/misc/email-customers.html
@@ -0,0 +1,145 @@
+<% include('/elements/header.html', $title) %>
+
+<FORM NAME="OneTrueForm" ACTION="email-customers.html" METHOD="POST">
+% foreach my $key ( keys %search ) {
+% my @values = ref($search{$key}) ? @{$search{$key}} : ( $search{$key} );
+% foreach my $value ( @values ) {
+ <INPUT TYPE="hidden" NAME="<% $key |h %>" VALUE="<% $value |h %>">
+% }
+% }
+
+% if ( $cgi->param('magic') eq 'send' ) {
+
+ <FONT SIZE="+2">Sending notice</FONT>
+
+ <% include('/elements/progress-init.html',
+ 'OneTrueForm',
+ [ keys(%search), qw( from subject html_body text_body ) ],
+ 'process/email-customers.html',
+ { 'message' => "Notice sent" }, #would be nice to show #, but..
+ )
+ %>
+
+% } elsif ( $cgi->param('magic') eq 'preview' ) {
+
+ <FONT SIZE="+2">Preview notice</FONT>
+
+% }
+
+% if ( $cgi->param('magic') ) {
+
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <% include('/elements/tr-fixed.html',
+ 'field' => 'from',
+ 'label' => 'From:',
+ 'value' => scalar( $cgi->param('from') ),
+ )
+ %>
+
+ <% include('/elements/tr-fixed.html',
+ 'field' => 'subject',
+ 'label' => 'Subject:',
+ 'value' => scalar( $cgi->param('subject') ),
+ )
+ %>
+
+ <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $cgi->param('html_body') |h %>">
+ <TR>
+ <TD ALIGN="right" VALIGN="top">Message (HTML display): </TD>
+ <TD BGCOLOR="#e8e8e8" ALIGN="left"><% $cgi->param('html_body') %></TD>
+ </TR>
+
+% my $text_body = HTML::FormatText->new(leftmargin=>0)->format(
+% HTML::TreeBuilder->new_from_content(
+% $cgi->param('html_body')
+% )
+% );
+ <INPUT TYPE="hidden" NAME="text_body" VALUE="<% $text_body |h %>">
+ <TR>
+ <TD ALIGN="right" VALIGN="top">Message (Text display): </TD>
+ <TD BGCOLOR="#e8e8e8" ALIGN="left"><PRE><% $text_body %></PRE></TD>
+ </TR>
+
+ </TABLE>
+
+% if ( $cgi->param('magic') eq 'preview' ) {
+
+ <SCRIPT>
+ function areyousure(href) {
+ return confirm("Send this notice to <% $num_cust %> customers?");
+ }
+ </SCRIPT>
+
+ <BR>
+ <INPUT TYPE="hidden" NAME="magic" VALUE="send">
+ <INPUT TYPE="submit" VALUE="Send notice" onClick="return areyousure()">
+
+% }
+
+% } else {
+
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%">
+
+ <% include('/elements/tr-input-text.html',
+ 'field' => 'from',
+ 'label' => 'From:',
+ )
+ %>
+
+ <% include('/elements/tr-input-text.html',
+ 'field' => 'subject',
+ 'label' => 'Subject:',
+ )
+ %>
+
+ <TR>
+ <TD ALIGN="right" VALIGN="top">Message: </TD>
+ <TD><% include('/elements/htmlarea.html', 'field'=>'html_body') %></TD>
+ </TR>
+
+ </TABLE>
+
+%#Substitution vars:
+
+ <BR><BR>
+ <INPUT TYPE="hidden" NAME="magic" VALUE="preview">
+ <INPUT TYPE="submit" VALUE="Preview notice">
+
+% }
+
+</FORM>
+
+% if ( $cgi->param('magic') eq 'send' ) {
+ <SCRIPT TYPE="text/javascript">
+ process();
+ </SCRIPT>
+% }
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
+
+my %search = $cgi->Vars;
+delete $search{$_} for qw( magic from subject html_body text_body );
+$search{$_} = [ split(/\0/, $search{$_}) ]
+ foreach grep $search{$_} =~ /\0/, keys %search;
+
+my $title = 'Bulk send customer notices';
+
+my $num_cust;
+if ( $cgi->param('magic') eq 'preview' ) {
+ my $sql_query = FS::cust_main->search_sql(\%search);
+ my $count_query = delete($sql_query->{'count_query'});
+ my $count_sth = dbh->prepare($count_query)
+ or die "Error preparing $count_query: ". dbh->errstr;
+ $count_sth->execute
+ or die "Error executing $count_query: ". $count_sth->errstr;
+ my $count_arrayref = $count_sth->fetchrow_arrayref;
+ $num_cust = $count_arrayref->[0];
+}
+
+</%init>
diff --git a/httemplate/misc/email-invoice.cgi b/httemplate/misc/email-invoice.cgi
new file mode 100755
index 0000000..269722f
--- /dev/null
+++ b/httemplate/misc/email-invoice.cgi
@@ -0,0 +1,19 @@
+<% $cgi->redirect("${p}view/cust_main.cgi?$custnum") %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^((.+)-)?(\d+)$/;
+my $template = $2;
+my $invnum = $3;
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Can't find invoice!\n" unless $cust_bill;
+
+$cust_bill->email($template);
+
+my $custnum = $cust_bill->getfield('custnum');
+
+</%init>
diff --git a/httemplate/misc/email_events.cgi b/httemplate/misc/email_events.cgi
new file mode 100644
index 0000000..e7a0e77
--- /dev/null
+++ b/httemplate/misc/email_events.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_event::process_reemail', $cgi;
+
+</%init>
diff --git a/httemplate/misc/email_invoice_events.cgi b/httemplate/misc/email_invoice_events.cgi
new file mode 100644
index 0000000..d65fe17
--- /dev/null
+++ b/httemplate/misc/email_invoice_events.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill_event::process_reemail', $cgi;
+
+</%init>
diff --git a/httemplate/misc/email_invoices.cgi b/httemplate/misc/email_invoices.cgi
new file mode 100644
index 0000000..78ca0f6
--- /dev/null
+++ b/httemplate/misc/email_invoices.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill::process_reemail', $cgi;
+
+</%init>
diff --git a/httemplate/misc/enable_or_disable_tax.html b/httemplate/misc/enable_or_disable_tax.html
new file mode 100755
index 0000000..0efd07d
--- /dev/null
+++ b/httemplate/misc/enable_or_disable_tax.html
@@ -0,0 +1,37 @@
+<% include('/elements/header-popup.html', ucfirst($action). ' Tax Rates') %>
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% popurl(1) %>process/enable_or_disable_tax.html" METHOD=POST>
+<INPUT TYPE="hidden" NAME="action" VALUE="<% $action %>">
+<INPUT TYPE="hidden" NAME="data_vendor" VALUE="<% $cgi->param('data_vendor') %>">
+<INPUT TYPE="hidden" NAME="geocode" VALUE="<% $cgi->param('geocode') %>">
+<INPUT TYPE="hidden" NAME="taxclassnum" VALUE="<% $cgi->param('taxclassnum') %>">
+<INPUT TYPE="hidden" NAME="tax_type" VALUE="<% $cgi->param('tax_type') %>">
+<INPUT TYPE="hidden" NAME="tax_cat" VALUE="<% $cgi->param('tax_cat') %>">
+<INPUT TYPE="hidden" NAME="showdisabled" VALUE="<% $cgi->param('showdisabled') |h %>">
+
+This will <B><% $action %></B> <% $count %> tax
+<% $count == 1 ? 'rate' : 'rates' %>. Are you <B>certain</B> you want to do
+this?
+<BR><BR><INPUT TYPE="submit" VALUE="Yes">
+</FORM>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $action = '';
+if ( $cgi->param('action') =~ /^(\w+)$/ ) {
+ $action = $1;
+}
+
+my ($query, $count_query) = FS::tax_rate::browse_queries(scalar($cgi->Vars));
+
+my $count_sth = dbh->prepare($count_query)
+ or die "Error preparing $count_query: ". dbh->errstr;
+$count_sth->execute
+ or die "Error executing $count_query: ". $count_sth->errstr;
+my $count = $count_sth->fetchrow_arrayref->[0];
+
+</%init>
diff --git a/httemplate/misc/exchanges.cgi b/httemplate/misc/exchanges.cgi
new file mode 100644
index 0000000..f5860cf
--- /dev/null
+++ b/httemplate/misc/exchanges.cgi
@@ -0,0 +1,24 @@
+%# [ <% join(', ', map { qq("$_") } @exchanges) %> ]
+<% objToJson(\@exchanges) %>
+<%init>
+
+my( $areacode, $svcpart ) = $cgi->param('arg');
+
+my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
+die "unknown svcpart $svcpart" unless $part_svc;
+
+my @exports = $part_svc->part_export_did;
+if ( scalar(@exports) > 1 ) {
+ die "more than one DID-providing export attached to svcpart $svcpart";
+} elsif ( ! @exports ) {
+ die "no DID providing export attached to svcpart $svcpart";
+}
+my $export = $exports[0];
+
+my $something = $export->get_dids('areacode'=>$areacode);
+
+#warn Dumper($something);
+
+my @exchanges = @{ $something };
+
+</%init>
diff --git a/httemplate/misc/fax-invoice.cgi b/httemplate/misc/fax-invoice.cgi
new file mode 100755
index 0000000..2591fce
--- /dev/null
+++ b/httemplate/misc/fax-invoice.cgi
@@ -0,0 +1,19 @@
+<% $cgi->redirect("${p}view/cust_main.cgi?$custnum") %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^((.+)-)?(\d+)$/;
+my $template = $2;
+my $invnum = $3;
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Can't find invoice!\n" unless $cust_bill;
+
+$cust_bill->fax_invoice($template);
+
+my $custnum = $cust_bill->getfield('custnum');
+
+</%init>
diff --git a/httemplate/misc/fax_events.cgi b/httemplate/misc/fax_events.cgi
new file mode 100644
index 0000000..39cba07
--- /dev/null
+++ b/httemplate/misc/fax_events.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_event::process_refax', $cgi;
+
+</%init>
diff --git a/httemplate/misc/fax_invoice_events.cgi b/httemplate/misc/fax_invoice_events.cgi
new file mode 100644
index 0000000..05420ee
--- /dev/null
+++ b/httemplate/misc/fax_invoice_events.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill_event::process_refax', $cgi;
+
+</%init>
diff --git a/httemplate/misc/fax_invoices.cgi b/httemplate/misc/fax_invoices.cgi
new file mode 100644
index 0000000..a843523
--- /dev/null
+++ b/httemplate/misc/fax_invoices.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill::process_refax', $cgi;
+
+</%init>
diff --git a/httemplate/misc/file-upload.html b/httemplate/misc/file-upload.html
new file mode 100644
index 0000000..469274c
--- /dev/null
+++ b/httemplate/misc/file-upload.html
@@ -0,0 +1,53 @@
+<% include('/elements/header-minimal.html', 'File Upload') %>
+% if ($error) {
+Error: <% $error %>
+% }else{
+File Upload Successful <% join(',', @filenames) %>;
+% }
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import'); #?
+
+my @filenames = ();
+my $error = ''; # could be extended to the access control
+
+$cgi->param('upload_fields') =~ /^([,\w]+)$/
+ or $error = "invalid upload_fields";
+my $fields = $1;
+
+my $dir = $FS::UID::cache_dir. "/cache.". $FS::UID::datasrc;
+
+foreach my $field (split /,/, $fields) {
+ next if $error;
+
+ my $fh = $cgi->upload($field)
+ or $error = "No valid file was provided.";
+
+ my $suffix = '';
+ if ( $cgi->param($field) =~ /(\.\w+)$/i ) {
+ $suffix = lc($1);
+ }
+
+ my $sh = new File::Temp( TEMPLATE => 'upload.XXXXXXXX',
+ SUFFIX => $suffix,
+ DIR => $dir,
+ UNLINK => 0,
+ )
+ or $error ||= "can't open temporary file to store upload: $!\n";
+
+ unless ($error) {
+ while(<$fh>) {
+ print $sh $_;
+ }
+ $sh->filename =~ m!.*/([.\w]+)$!;
+ push @filenames, "$field:$1";
+ close $sh
+ }
+
+}
+
+$error = "No files" unless scalar(@filenames);
+
+</%init>
diff --git a/httemplate/misc/ftp_invoices.cgi b/httemplate/misc/ftp_invoices.cgi
new file mode 100644
index 0000000..9a072b9
--- /dev/null
+++ b/httemplate/misc/ftp_invoices.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill::process_reftp', $cgi;
+
+</%init>
diff --git a/httemplate/misc/inventory_item-import.html b/httemplate/misc/inventory_item-import.html
new file mode 100644
index 0000000..65a123e
--- /dev/null
+++ b/httemplate/misc/inventory_item-import.html
@@ -0,0 +1,67 @@
+<% include("/elements/header.html", PL($inventory_class->classname)) %>
+
+Import a file containing <% PL($inventory_class->classname) %>, one per line.
+<BR><BR>
+
+<% include( '/elements/form-file_upload.html',
+ 'name' => 'InventoryItemImportForm',
+ 'action' => 'process/inventory_item-import.html',
+ 'num_files' => 1,
+ #'fields' => [ 'format', 'itembatch', 'classnum', ],
+ 'fields' => [ 'format', 'classnum', ],
+ 'message' => 'Inventory import successful',
+ #XXX redirect via $itembatch? or just back to class browse?
+ 'url' => $p."search/inventory_item.html?classnum=$classnum;avail=1",
+ )
+%>
+
+<% &ntable("#cccccc", 2) %>
+
+ <INPUT TYPE="hidden" NAME="format" VALUE="default">
+
+ <INPUT TYPE="hidden" NAME="classnum" VALUE="<% $classnum %>">
+
+%# <INPUT TYPE="hidden" NAME="itembatch" VALUE="<% $itembatch %>">
+
+ <% include( '/elements/file-upload.html',
+ 'field' => 'file',
+ 'label' => 'Filename',
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ ID = "submit"
+ VALUE = "Import file"
+ onClick = "document.InventoryItemImportForm.submit.disabled=true;"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+
+Upload file can be a text file or Excel spreadsheet. If an Excel spreadsheet,
+ should have an .XLS extension.
+<BR><BR>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+$cgi->param =~ /^(\d+)$/ or errorpage("illegal classnum");
+my $classnum = $1;
+my $inventory_class = qsearchs('inventory_class', { 'classnum' => $classnum } );
+
+#my $conf = new FS::Conf;
+#my $itembatch =
+# time2str('webimport-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+</%init>
diff --git a/httemplate/misc/link.cgi b/httemplate/misc/link.cgi
new file mode 100755
index 0000000..748eaa1
--- /dev/null
+++ b/httemplate/misc/link.cgi
@@ -0,0 +1,84 @@
+<% include("/elements/header.html","Link to existing $svc") %>
+
+<FORM ACTION="<% popurl(1) %>process/link.cgi" METHOD=POST>
+% if ( $link_field ) {
+
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="">
+ <INPUT TYPE="hidden" NAME="link_field" VALUE="<% $link_field %>">
+ <% $link_field %> of existing service: <INPUT TYPE="text" NAME="link_value">
+ <BR>
+% if ( $link_field2 ) {
+
+ <INPUT TYPE="hidden" NAME="link_field2" VALUE="<% $link_field2->{field} %>">
+ <% $link_field2->{'label'} %> of existing service:
+% if ( $link_field2->{'type'} eq 'select' ) {
+% if ( $link_field2->{'select_table'} ) {
+
+ <SELECT NAME="link_value2">
+ <OPTION> </OPTION>
+% foreach my $r ( qsearch( $link_field2->{'select_table'}, {})) {
+% my $key = $link_field2->{'select_key'};
+% my $label = $link_field2->{'select_label'};
+
+ <OPTION VALUE="<% $r->$key() %>"><% $r->$label() %></OPTION>
+% }
+
+ </SELECT>
+% } else {
+
+ Don't know how to process secondary link field for <% $svcdb %>
+ (type=>select but no select_table)
+% }
+% } else {
+
+ Don't know how to process secondary link field for <% $svcdb %>
+ (unknown type <% $link_field2->{'type'} %>)
+% }
+
+ <BR>
+% }
+% } else {
+
+ Service # of existing service: <INPUT TYPE="text" NAME="svcnum" VALUE="">
+% }
+
+
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
+<BR><INPUT TYPE="submit" VALUE="Link">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View/link unlinked services');
+
+my %link_field = (
+ 'svc_acct' => 'username',
+ 'svc_domain' => 'domain',
+);
+
+my %link_field2 = (
+ 'svc_acct' => { label => 'Domain',
+ field => 'domsvc',
+ type => 'select',
+ select_table => 'svc_domain',
+ select_key => 'svcnum',
+ select_label => 'domain'
+ },
+);
+
+$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum';
+my $pkgnum = $1;
+$cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart';
+my $svcpart = $1;
+
+my $part_svc = qsearchs('part_svc',{'svcpart'=>$svcpart});
+my $svc = $part_svc->getfield('svc');
+my $svcdb = $part_svc->getfield('svcdb');
+my $link_field = $link_field{$svcdb};
+my $link_field2 = $link_field2{$svcdb};
+
+</%init>
diff --git a/httemplate/misc/location.cgi b/httemplate/misc/location.cgi
new file mode 100644
index 0000000..419c59f
--- /dev/null
+++ b/httemplate/misc/location.cgi
@@ -0,0 +1,19 @@
+<% objToJson(\%hash) %>
+<%init>
+
+my $locationnum = $cgi->param('arg');
+
+my $cust_location = qsearchs({
+ 'select' => 'cust_location.*',
+ 'table' => 'cust_location',
+ 'hashref' => { 'locationnum' => $locationnum },
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+
+my %hash = ();
+%hash = map { $_ => $cust_location->$_() }
+ qw( address1 address2 city county state zip country )
+ if $cust_location;
+
+</%init>
diff --git a/httemplate/misc/meta-import.cgi b/httemplate/misc/meta-import.cgi
new file mode 100644
index 0000000..5b3470c
--- /dev/null
+++ b/httemplate/misc/meta-import.cgi
@@ -0,0 +1,79 @@
+<% include('/elements/header.html', 'Import') %>
+
+<FORM ACTION="process/meta-import.cgi" METHOD="post" ENCTYPE="multipart/form-data">
+Import data from a DBI data source<BR><BR>
+%
+% #false laziness with edit/cust_main.cgi
+% my @agents = qsearch( 'agent', {} );
+% die "No agents created!" unless @agents;
+% my $agentnum = $agents[0]->agentnum; #default to first
+%
+% if ( scalar(@agents) == 1 ) {
+%
+
+ <INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agentnum %>">
+% } else {
+
+ <BR><BR>Agent <SELECT NAME="agentnum" SIZE="1">
+% foreach my $agent (sort { $a->agent cmp $b->agent } @agents) {
+
+ <OPTION VALUE="<% $agent->agentnum %>" <% " SELECTED"x($agent->agentnum==$agentnum) %>><% $agent->agent %></OPTION>
+% }
+
+ </SELECT><BR><BR>
+% }
+%
+% my @referrals = qsearch('part_referral',{});
+% die "No advertising sources created!" unless @referrals;
+% my $refnum = $referrals[0]->refnum; #default to first
+%
+% if ( scalar(@referrals) == 1 ) {
+%
+
+ <INPUT TYPE="hidden" NAME="refnum" VALUE="<% $refnum %>">
+% } else {
+
+ <BR><BR>Advertising source <SELECT NAME="refnum" SIZE="1">
+% foreach my $referral ( sort { $a->referral <=> $b->referral } @referrals) {
+
+ <OPTION VALUE="<% $referral->refnum %>" <% " SELECTED"x($referral->refnum==$refnum) %>><% $referral->refnum %>: <% $referral->referral %></OPTION>
+% }
+
+ </SELECT><BR><BR>
+% }
+
+
+ First package: <SELECT NAME="pkgpart"><OPTION VALUE="">(none)</OPTION>
+% foreach my $part_pkg ( qsearch('part_pkg',{'disabled'=>'' }) ) {
+
+ <OPTION VALUE="<% $part_pkg->pkgpart %>"><% $part_pkg->pkg. ' - '. $part_pkg->comment %></OPTION>
+% }
+
+</SELECT><BR><BR>
+
+ <table>
+ <tr>
+ <td align="right">DBI data source: </td>
+ <td><INPUT TYPE="text" NAME="data_source"></td>
+ </tr>
+ <tr>
+ <td align="right">DBI username: </td>
+ <td><INPUT TYPE="text" NAME="username"></td>
+ </tr>
+ <tr>
+ <td align="right">DBI password: </td>
+ <td><INPUT TYPE="text" NAME="password"></td>
+ </tr>
+ </table>
+ <INPUT TYPE="submit" VALUE="Import">
+
+ </FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+#there's no ACL for this... haven't used in ages
+die 'meta-import not enabled; remove this if you want to use it';
+
+</%init>
diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html
new file mode 100644
index 0000000..2c83351
--- /dev/null
+++ b/httemplate/misc/order_pkg.html
@@ -0,0 +1,75 @@
+<% include('/elements/header-popup.html', 'Order new package' ) %>
+
+<SCRIPT TYPE="text/javascript">
+
+ function enable_order_pkg () {
+ if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) {
+ document.OrderPkgForm.submit.disabled = false;
+ } else {
+ document.OrderPkgForm.submit.disabled = true;
+ }
+ }
+
+</SCRIPT>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="OrderPkgForm" ACTION="<% $p %>edit/process/quick-cust_pkg.cgi" METHOD="POST">
+
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $cust_main->custnum %>">
+
+<% ntable("#cccccc", 2) %>
+<TR>
+ <TH ALIGN="right">Package</TH>
+ <TD COLSPAN=7>
+ <% include('/elements/select-cust-part_pkg.html',
+ 'curr_value' => $pkgpart,
+ 'cust_main' => $cust_main,
+ 'onchange' => 'enable_order_pkg',
+ )
+ %>
+ </TD>
+</TR>
+
+% if ( $conf->exists('pkg_referral') ) {
+ <% include('/elements/tr-select-part_referral.html',
+ 'curr_value' => scalar( $cgi->param('refnum') ), #get rid of empty_label first# || $cust_main->refnum,
+ 'disable_empty' => 1,
+ 'multiple' => $conf->exists('pkg_referral-multiple'),
+ 'colspan' => 7,
+ )
+ %>
+% }
+
+<% include('/elements/tr-select-cust_location.html',
+ 'cgi' => $cgi,
+ 'cust_main' => $cust_main,
+ )
+%>
+
+</TABLE>
+
+<BR>
+<INPUT NAME="submit" TYPE="submit" VALUE="Order Package" <% $pkgpart ? '' : 'DISABLED' %>>
+
+</FORM>
+</BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Order customer package');
+
+my $conf = new FS::Conf;
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die "no custnum";
+my $custnum = $1;
+my $cust_main = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+
+my $pkgpart = scalar($cgi->param('pkgpart'));
+
+</%init>
diff --git a/httemplate/misc/payment.cgi b/httemplate/misc/payment.cgi
new file mode 100644
index 0000000..0047004
--- /dev/null
+++ b/httemplate/misc/payment.cgi
@@ -0,0 +1,285 @@
+<% include( '/elements/header.html', "Process $type{$payby} payment" ) %>
+<% include( '/elements/small_custview.html', $cust_main, '', '', popurl(2) . "view/cust_main.cgi" ) %>
+<FORM NAME="OneTrueForm" ACTION="process/payment.cgi" METHOD="POST" onSubmit="document.OneTrueForm.process.disabled=true">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>">
+<INPUT TYPE="hidden" NAME="payunique" VALUE="<% $payunique %>">
+<INPUT TYPE="hidden" NAME="balance" VALUE="<% $balance %>">
+
+<% include('/elements/init_overlib.html') %>
+
+% #include( '/elements/table.html', '#cccccc' )
+
+<% ntable('#cccccc') %>
+ <TR>
+ <TD ALIGN="right">Payment amount</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<INPUT TYPE="text" NAME="amount" SIZE=8 VALUE="<% $balance > 0 ? sprintf("%.2f", $balance) : '' %>">
+ </TD></TR></TABLE>
+ </TD>
+ </TR>
+
+% if ( $payby eq 'CARD' ) {
+%
+% my( $payinfo, $paycvv, $month, $year ) = ( '', '', '', '' );
+% my $payname = $cust_main->first. ' '. $cust_main->getfield('last');
+% my $address1 = $cust_main->address1;
+% my $address2 = $cust_main->address2;
+% my $city = $cust_main->city;
+% my $state = $cust_main->state;
+% my $zip = $cust_main->zip;
+% if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
+% $payinfo = $cust_main->paymask;
+% $paycvv = $cust_main->paycvv;
+% ( $month, $year ) = $cust_main->paydate_monthyear;
+% $payname = $cust_main->payname if $cust_main->payname;
+% }
+
+ <TR>
+ <TD ALIGN="right">Card&nbsp;number</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="payinfo" SIZE=20 MAXLENGTH=19 VALUE="<%$payinfo%>"> </TD>
+ <TD>Exp.</TD>
+ <TD>
+ <SELECT NAME="month">
+% for ( ( map "0$_", 1 .. 9 ), 10 .. 12 ) {
+
+ <OPTION<% $_ == $month ? ' SELECTED' : '' %>><% $_ %>
+% }
+
+ </SELECT>
+ </TD>
+ <TD> / </TD>
+ <TD>
+ <SELECT NAME="year">
+% my @a = localtime; for ( $a[5]+1900 .. $a[5]+1915 ) {
+
+ <OPTION<% $_ == $year ? ' SELECTED' : '' %>><% $_ %>
+% }
+
+ </SELECT>
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">CVV2</TD>
+ <TD><INPUT TYPE="text" NAME="paycvv" VALUE="<% $paycvv %>" SIZE=4 MAXLENGTH=4>
+ (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/cvv2.html', 480, 352, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Exact&nbsp;name&nbsp;on&nbsp;card</TD>
+ <TD><INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="payname" VALUE="<%$payname%>"></TD>
+ </TR><TR>
+ <TD ALIGN="right">Card&nbsp;billing&nbsp;address</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address1" VALUE="<%$address1%>">
+ </TD>
+ </TR><TR>
+ <TD ALIGN="right">Address&nbsp;line&nbsp;2</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address2" VALUE="<%$address2%>">
+ </TD>
+ </TR><TR>
+ <TD ALIGN="right">City</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="city" SIZE="12" MAXLENGTH=80 VALUE="<%$city%>">
+ </TD>
+ <TD>State</TD>
+ <TD>
+ <SELECT NAME="state">
+% for ( @states ) {
+
+ <OPTION<% $_ eq $state ? ' SELECTED' : '' %>><% $_ %>
+% }
+
+ </SELECT>
+ </TD>
+ <TD>Zip</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="zip" SIZE=11 MAXLENGTH=10 VALUE="<%$zip%>">
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+ </TR>
+
+% } elsif ( $payby eq 'CHEK' ) {
+%
+% my( $payinfo1, $payinfo2, $payname, $ss, $paytype, $paystate,
+% $stateid, $stateid_state )
+% = ( '', '', '', '', '', '', '', '' );
+% if ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
+% $cust_main->paymask =~ /^([\dx]+)\@([\dx]+)$/i
+% or die "unparsable payinfo ". $cust_main->payinfo;
+% ($payinfo1, $payinfo2) = ($1, $2);
+% $payname = $cust_main->payname;
+% $ss = $cust_main->ss;
+% $paytype = $cust_main->getfield('paytype');
+% $paystate = $cust_main->getfield('paystate');
+% $stateid = $cust_main->getfield('stateid');
+% $stateid_state = $cust_main->getfield('stateid_state');
+% }
+
+ <INPUT TYPE="hidden" NAME="month" VALUE="12">
+ <INPUT TYPE="hidden" NAME="year" VALUE="2037">
+ <TR>
+ <TD ALIGN="right">Account&nbsp;number</TD>
+ <TD><INPUT TYPE="text" SIZE=10 NAME="payinfo1" VALUE="<%$payinfo1%>"></TD>
+ <TD ALIGN="right">Type</TD>
+ <TD><SELECT NAME="paytype"><% join('', map { qq!<OPTION VALUE="$_" !.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>" } @FS::cust_main::paytypes) %></SELECT></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">ABA/Routing&nbsp;number</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=10 MAXLENGTH=9 NAME="payinfo2" VALUE="<%$payinfo2%>">
+ (<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/ach.html', 380, 240, 'ach_popup' ), CAPTION, 'ACH Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Bank&nbsp;name</TD>
+ <TD><INPUT TYPE="text" NAME="payname" VALUE="<%$payname%>"></TD>
+ </TR>
+
+% if ( $conf->exists('show_bankstate') ) {
+ <TR>
+ <TD ALIGN="right">Bank&nbsp;state</TD>
+ <TD><% include('/elements/select-state.html',
+ 'disable_empty' => 0,
+ 'empty_label' => '(choose)',
+ 'state' => $paystate,
+ 'country' => $cust_main->country,
+ 'prefix' => 'pay',
+ )
+ %>
+ </TD>
+ </TR>
+% } else {
+ <INPUT TYPE="hidden" NAME="paystate" VALUE="<% $paystate %>">
+% }
+
+% if ( $conf->exists('show_ss') ) {
+ <TR>
+ <TD ALIGN="right">
+ Account&nbsp;holder<BR>
+ Social&nbsp;security&nbsp;or&nbsp;tax&nbsp;ID&nbsp;#
+ </TD>
+ <TD><INPUT TYPE="text" NAME="ss" VALUE="<% $ss %>"></TD>
+ </TR>
+% } else {
+ <INPUT TYPE="hidden" NAME="ss" VALUE="<% $ss %>"></TD>
+% }
+
+% if ( $conf->exists('show_stateid') ) {
+ <TR>
+ <TD ALIGN="right">
+ Account&nbsp;holder<BR>
+ Driver&rsquo;s&nbsp;license&nbsp;or&nbsp;state&nbsp;ID&nbsp;#
+ </TD>
+ <TD><INPUT TYPE="text" NAME="stateid" VALUE="<% $stateid %>"></TD>
+ <TD ALIGN="right">State</TD>
+ <TD><% include('/elements/select-state.html',
+ 'disable_empty' => 0,
+ 'empty_label' => '(choose)',
+ 'state' => $stateid_state,
+ 'country' => $cust_main->country,
+ 'prefix' => 'stateid_',
+ )
+ %>
+ </TD>
+ </TR>
+% } else {
+ <INPUT TYPE="hidden" NAME="stateid" VALUE="<% $stateid %>">
+ <INPUT TYPE="hidden" NAME="stateid_state" VALUE="<% $stateid_state %>">
+% }
+
+% } #end CARD/CHEK-specific section
+
+
+<TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox" CHECKED NAME="save" VALUE="1">
+ Remember this information
+ </TD>
+</TR>
+
+% if ( $conf->exists("batch-enable")
+% || grep $payby eq $_, $conf->config('batch-enable_payby')
+% ) {
+%
+% if ( grep $payby eq $_, $conf->config('realtime-disable_payby') ) {
+
+ <INPUT TYPE="hidden" NAME="batch" VALUE="1">
+
+% } else {
+
+ <TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox" NAME="batch" VALUE="1">
+ Add to current batch
+ </TD>
+ </TR>
+
+% }
+% }
+
+<TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox"<% ( ( $payby eq 'CARD' && $cust_main->payby ne 'DCRD' ) || ( $payby eq 'CHEK' && $cust_main->payby eq 'CHEK' ) ) ? ' CHECKED' : '' %> NAME="auto" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.save.checked=true; }">
+ Charge future payments to this <% $type{$payby} %> automatically
+ </TD>
+</TR>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" NAME="process" VALUE="Process payment">
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Process payment');
+
+my %type = ( 'CARD' => 'credit card',
+ 'CHEK' => 'electronic check (ACH)',
+ );
+
+$cgi->param('payby') =~ /^(CARD|CHEK)$/
+ or die "unknown payby ". $cgi->param('payby');
+my $payby = $1;
+
+$cgi->param('custnum') =~ /^(\d+)$/
+ or die "illegal custnum ". $cgi->param('custnum');
+my $custnum = $1;
+
+my $cust_main = qsearchs( 'cust_main', { 'custnum'=>$custnum } );
+die "unknown custnum $custnum" unless $cust_main;
+
+my $balance = $cust_main->balance;
+
+my $payinfo = '';
+
+#false laziness w/selfservice make_payment.html shortcut for one-country
+my $conf = new FS::Conf;
+my %states = map { $_->state => 1 }
+ qsearch('cust_main_county', {
+ 'country' => $conf->config('countrydefault') || 'US'
+ } );
+my @states = sort { $a cmp $b } keys %states;
+
+my $payunique = "webui-payment-". time. "-$$-". rand() * 2**32;
+
+</%init>
+
+
diff --git a/httemplate/misc/phone_avail-import.html b/httemplate/misc/phone_avail-import.html
new file mode 100644
index 0000000..1f4d8ca
--- /dev/null
+++ b/httemplate/misc/phone_avail-import.html
@@ -0,0 +1,88 @@
+<% include('/elements/header.html', 'Phone number (DID) import') %>
+
+Import a file containing phone numbers (DIDs).
+<BR><BR>
+
+<% include( '/elements/form-file_upload.html',
+ 'name' => 'PhonenumImportForm',
+ 'action' => 'process/phone_avail-import.html',
+ 'num_files' => 1,
+ 'fields' => [ 'format', 'availbatch', 'exportnum', 'countrycode' ],
+ 'message' => 'DID import successful',
+ 'url' => $p."search/phone_avail.html?availbatch=$availbatch",
+ )
+%>
+
+<% &ntable("#cccccc", 2) %>
+
+ <INPUT TYPE="hidden" NAME="format" VALUE="default">
+
+ <INPUT TYPE="hidden" NAME="availbatch" VALUE="<% $availbatch %>">
+
+ <% include( '/elements/tr-select-table.html',
+ 'table' => 'part_export',
+ 'name_col' => 'machine',
+ 'label' => 'Export',
+ 'empty_label' => 'Select export',
+ 'hashref' => { 'exporttype' => 'internal_diddb', },
+ #'label_callback' =>
+ )
+ %>
+
+ <TR>
+ <TH ALIGN="right">Country code</TH>
+ <TD>
+ <INPUT TYPE = "text"
+ NAME = "countrycode"
+ VALUE = "<% $conf->config('default_phone_countrycode') || 1 %>"
+ >
+ </TD>
+ </TR>
+
+ <% include( '/elements/file-upload.html',
+ 'field' => 'file',
+ 'label' => 'Filename',
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ ID = "submit"
+ VALUE = "Import file"
+ onClick = "document.PhonenumImportForm.submit.disabled=true;"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+
+Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets. The file should have a .CSV or .XLS extension.
+<BR><BR>
+
+<b>Default</b> format has the following field order: <i>state, number<i></i>
+<BR><BR>
+
+Field information:
+<ul>
+ <li><i>state</i>: Two-letter state code, i.e. "CA"
+ <li><i>number</i>: Phone number
+</ul>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $conf = new FS::Conf;
+
+my $availbatch =
+ time2str('webimport-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+</%init>
diff --git a/httemplate/misc/phonenums.cgi b/httemplate/misc/phonenums.cgi
new file mode 100644
index 0000000..2ed0f61
--- /dev/null
+++ b/httemplate/misc/phonenums.cgi
@@ -0,0 +1,29 @@
+%# [ <% join(', ', map { qq("$_") } @exchanges) %> ]
+<% objToJson(\@exchanges) %>
+<%init>
+
+my( $exchangestring, $svcpart ) = $cgi->param('arg');
+
+$exchangestring =~ /\((\d{3})-(\d{3})-XXXX\)\s*$/i
+ or die "unparsable exchange: $exchangestring";
+my( $areacode, $exchange ) = ( $1, $2 );
+my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
+die "unknown svcpart $svcpart" unless $part_svc;
+
+my @exports = $part_svc->part_export_did;
+if ( scalar(@exports) > 1 ) {
+ die "more than one DID-providing export attached to svcpart $svcpart";
+} elsif ( ! @exports ) {
+ die "no DID providing export attached to svcpart $svcpart";
+}
+my $export = $exports[0];
+
+my $something = $export->get_dids('areacode'=>$areacode,
+ 'exchange'=>$exchange,
+ );
+
+#warn Dumper($something);
+
+my @exchanges = @{ $something };
+
+</%init>
diff --git a/httemplate/misc/print-invoice.cgi b/httemplate/misc/print-invoice.cgi
new file mode 100755
index 0000000..aeef687
--- /dev/null
+++ b/httemplate/misc/print-invoice.cgi
@@ -0,0 +1,19 @@
+<% $cgi->redirect("${p}view/cust_main.cgi?$custnum") %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^((.+)-)?(\d+)$/;
+my $template = $2;
+my $invnum = $3;
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Can't find invoice!\n" unless $cust_bill;
+
+$cust_bill->print($template);
+
+my $custnum = $cust_bill->getfield('custnum');
+
+</%init>
diff --git a/httemplate/misc/print_events.cgi b/httemplate/misc/print_events.cgi
new file mode 100644
index 0000000..8d83d3d
--- /dev/null
+++ b/httemplate/misc/print_events.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_event::process_reprint', $cgi;
+
+</%init>
diff --git a/httemplate/misc/print_invoice_events.cgi b/httemplate/misc/print_invoice_events.cgi
new file mode 100644
index 0000000..c974d5f
--- /dev/null
+++ b/httemplate/misc/print_invoice_events.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill_event::process_reprint', $cgi;
+
+</%init>
diff --git a/httemplate/misc/print_invoices.cgi b/httemplate/misc/print_invoices.cgi
new file mode 100644
index 0000000..f859f6d
--- /dev/null
+++ b/httemplate/misc/print_invoices.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill::process_reprint', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/batch-cust_pay.cgi b/httemplate/misc/process/batch-cust_pay.cgi
new file mode 100644
index 0000000..058a225
--- /dev/null
+++ b/httemplate/misc/process/batch-cust_pay.cgi
@@ -0,0 +1,47 @@
+% die "access denied"
+% unless $FS::CurrentUser::CurrentUser->access_right('Post payment batch');
+%
+% my $param = $cgi->Vars;
+%
+% #my $paybatch = $param->{'paybatch'};
+% my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+%
+% my @cust_pay = ();
+% #my $row = 0;
+% #while ( exists($param->{"custnum$row"}) ) {
+% for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+% push @cust_pay, new FS::cust_pay {
+% 'custnum' => $param->{"custnum$row"},
+% 'paid' => $param->{"paid$row"},
+% 'payby' => 'BILL',
+% 'payinfo' => $param->{"payinfo$row"},
+% 'paybatch' => $paybatch,
+% }
+% if $param->{"custnum$row"}
+% || $param->{"paid$row"}
+% || $param->{"payinfo$row"};
+% #$row++;
+% }
+%
+% my @errors = FS::cust_pay->batch_insert(@cust_pay);
+% my $num_errors = scalar(grep $_, @errors);
+%
+% if ( $num_errors ) {
+%
+% $cgi->param('error', "$num_errors error". ($num_errors>1 ? 's' : '').
+% ' - Batch not processed, correct and resubmit'
+% );
+%
+% my $erow=0;
+% $cgi->param('error'. $erow++, shift @errors) while @errors;
+%
+%
+<% $cgi->redirect($p.'batch-cust_pay.html?'. $cgi->query_string)
+
+ %>
+% } else {
+%
+%
+<% $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %>
+% }
+
diff --git a/httemplate/misc/process/bulk_change_pkg.cgi b/httemplate/misc/process/bulk_change_pkg.cgi
new file mode 100755
index 0000000..d2ab4bf
--- /dev/null
+++ b/httemplate/misc/process/bulk_change_pkg.cgi
@@ -0,0 +1,56 @@
+% if ($error) {
+<% $cgi->redirect(popurl(2)."/bulk_change_pkg.cgi?".$cgi->query_string ) %>
+% }
+<% include('/elements/header-popup.html', "Packages Changed") %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages');
+
+my %search_hash = ();
+
+$search_hash{'query'} = $cgi->param('query');
+
+for my $param (qw(agentnum magic status classnum pkgpart)) {
+ $search_hash{$param} = $cgi->param($param)
+ if $cgi->param($param);
+}
+
+###
+# parse dates
+###
+
+#false laziness w/report_cust_pkg.html
+my %disable = (
+ 'all' => {},
+ 'one-time charge' => { 'last_bill'=>1, 'bill'=>1, 'adjourn'=>1, 'susp'=>1, 'expire'=>1, 'cancel'=>1, },
+ 'active' => { 'susp'=>1, 'cancel'=>1 },
+ 'suspended' => { 'cancel' => 1 },
+ 'cancelled' => {},
+ '' => {},
+);
+
+foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
+
+ my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
+
+ next if $beginning == 0 && $ending == 4294967295
+ or $disable{$cgi->param('status')}->{$field};
+
+ $search_hash{$field} = [ $beginning, $ending ];
+
+}
+
+my $sql_query = FS::cust_pkg->search_sql(\%search_hash);
+$sql_query->{'select'} = 'cust_pkg.pkgnum';
+
+my $error = FS::cust_pkg::bulk_change( [ $cgi->param('new_pkgpart') ],
+ [ map { $_->pkgnum } qsearch($sql_query) ],
+ );
+
+$cgi->param("error", substr($error, 0, 512)); # arbitrary length believed
+ # suited for all supported
+ # browsers
+
+
+</%init>
diff --git a/httemplate/misc/process/cancel_pkg.html b/httemplate/misc/process/cancel_pkg.html
new file mode 100755
index 0000000..669af9c
--- /dev/null
+++ b/httemplate/misc/process/cancel_pkg.html
@@ -0,0 +1,72 @@
+<% header("Package $past{$method}") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+</HTML>
+<%once>
+
+my %past = ( 'cancel' => 'cancelled',
+ 'expire' => 'expired',
+ 'suspend' => 'suspended',
+ 'adjourn' => 'adjourned',
+ );
+
+#i'm sure this is false laziness with somewhere, at least w/misc/cancel_pkg.html
+my %right = ( 'cancel' => 'Cancel customer package immediately',
+ 'expire' => 'Cancel customer package later',
+ 'suspend' => 'Suspend customer package',
+ 'adjourn' => 'Suspend customer package later',
+ );
+
+</%once>
+<%init>
+
+#untaint method
+my $method = $cgi->param('method');
+$method =~ /^(cancel|expire|suspend|adjourn)$/ or die "Illegal method";
+$method = $1;
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right($right{$method});
+
+#untaint pkgnum
+my $pkgnum = $cgi->param('pkgnum');
+$pkgnum =~ /^(\d+)$/ or die "Illegal pkgnum";
+$pkgnum = $1;
+
+#untaint reasonnum
+my $reasonnum = $cgi->param('reasonnum');
+$reasonnum =~ /^(-?\d+)$/ or die "Illegal reasonnum";
+$reasonnum = $1;
+
+my $date = time;
+if ($method eq 'expire' || $method eq 'adjourn'){
+ #untaint date
+ $date = $cgi->param('date');
+ str2time($cgi->param('date')) =~ /^(\d+)$/ or die "Illegal date";
+ $date = $1;
+ $method = ($method eq 'expire') ? 'cancel' : 'suspend';
+}
+
+my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
+
+#my $otaker = $FS::CurrentUser::CurrentUser->name;
+#$otaker = $FS::CurrentUser::CurrentUser->username
+# if ($otaker eq "User, Legacy");
+
+if ($reasonnum == -1) {
+ $reasonnum = {
+ 'typenum' => scalar( $cgi->param('newreasonnumT') ),
+ 'reason' => scalar( $cgi->param('newreasonnum' ) ),
+ };
+}
+
+my $error = $cust_pkg->$method( 'reason' => $reasonnum, 'date' => $date );
+
+if ($error) {
+ $cgi->param('error', $error);
+ print $cgi->redirect(popurl(2). "cancel_pkg.html?". $cgi->query_string );
+}
+
+</%init>
diff --git a/httemplate/misc/process/catchall.cgi b/httemplate/misc/process/catchall.cgi
new file mode 100755
index 0000000..0dda2ea
--- /dev/null
+++ b/httemplate/misc/process/catchall.cgi
@@ -0,0 +1,35 @@
+%if ($error) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "catchall.cgi?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit domain catchall');
+
+$FS::svc_domain::whois_hack=1;
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum =$1;
+
+my $old = qsearchs('svc_domain',{'svcnum'=>$svcnum}) if $svcnum;
+
+my $new = new FS::svc_domain ( {
+ map {
+ ($_, scalar($cgi->param($_)));
+ } ( fields('svc_domain'), qw( pkgnum svcpart ) )
+} );
+
+$new->setfield('action' => 'M');
+
+my $error;
+if ( $svcnum ) {
+ $error = $new->replace($old);
+} else {
+ $error = $new->insert;
+ $svcnum = $new->getfield('svcnum');
+}
+
+</%init>
diff --git a/httemplate/misc/process/cdr-import.html b/httemplate/misc/process/cdr-import.html
new file mode 100644
index 0000000..edc441e
--- /dev/null
+++ b/httemplate/misc/process/cdr-import.html
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cdr::process_batch_import', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/copy-rate_detail.html b/httemplate/misc/process/copy-rate_detail.html
new file mode 100644
index 0000000..87a6745
--- /dev/null
+++ b/httemplate/misc/process/copy-rate_detail.html
@@ -0,0 +1,61 @@
+%# if ( $error ) {
+%# <% $cgi->redirect(popurl(2).'copy-rate_detail.html?'. $cgi->query_string ) %>
+%# } else {
+<% include('/elements/header.html', 'Rates copied',
+ menubar( 'View all rate plans' => popurl(3).'browse/rate.cgi' ),
+ ) %>
+%# }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+$cgi->param('src_ratenum') =~ /^(\d+)$/ or die 'Illegal src_ratenum';
+my $src_ratenum = $1;
+
+$cgi->param('dst_ratenum') =~ /^(\d+)$/ or die 'Illegal src_ratenum';
+my $dst_ratenum = $1;
+
+my @countrycodes = map { /^countrycode(\d+)$/ or die; $1 }
+ grep { /^countrycode(\d+)$/ && $cgi->param($_) }
+ $cgi->param;
+
+foreach my $countrycode ( @countrycodes ) {
+
+ my @src_rate_detail = qsearch({
+ 'table' => 'rate_detail',
+ 'addl_from' => 'JOIN rate_region'.
+ ' ON ( rate_detail.dest_regionnum = rate_region.regionnum )',
+ 'hashref' => { 'ratenum' => $src_ratenum },
+ 'extra_sql' =>
+ "AND 0 < ( SELECT COUNT(*) FROM rate_prefix
+ WHERE rate_prefix.regionnum = rate_region.regionnum
+ AND countrycode = '$countrycode'
+ )
+ ",
+ });
+
+ foreach my $src_rate_detail ( @src_rate_detail ) {
+
+ my %hash = (
+ 'ratenum' => $dst_ratenum,
+ map { $_ => $src_rate_detail->get($_) }
+ qw( orig_regionnum dest_regionnum )
+ );
+
+ my $dst_rate_detail = qsearchs( 'rate_detail', \%hash)
+ || new FS::rate_detail \%hash;
+
+ $dst_rate_detail->$_( $src_rate_detail->get($_) )
+ foreach qw( min_included min_charge sec_granularity classnum );
+
+ my $method = $dst_rate_detail->ratedetailnum ? 'replace' : 'insert';
+
+ my $error = $dst_rate_detail->$method();
+
+ die $error if $error; # "shouldn't" happen
+
+ }
+}
+
+</%init>
diff --git a/httemplate/misc/process/cust_main-import.cgi b/httemplate/misc/process/cust_main-import.cgi
new file mode 100644
index 0000000..2b705a6
--- /dev/null
+++ b/httemplate/misc/process/cust_main-import.cgi
@@ -0,0 +1,10 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $server =
+ new FS::UI::Web::JSRPC 'FS::cust_main::Import::process_batch_import', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/cust_main-import_charges.cgi b/httemplate/misc/process/cust_main-import_charges.cgi
new file mode 100644
index 0000000..3ca6894
--- /dev/null
+++ b/httemplate/misc/process/cust_main-import_charges.cgi
@@ -0,0 +1,23 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+ <% include('/elements/header.html','Import successful') %>
+ <% include('/elements/footer.html') %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $fh = $cgi->upload('csvfile');
+#warn $cgi;
+#warn $fh;
+
+my $error = defined($fh)
+ ? FS::cust_main::batch_charge( {
+ filehandle => $fh,
+ 'fields' => [qw( custnum amount pkg )],
+ } )
+ : 'No file';
+
+</%init>
diff --git a/httemplate/misc/process/cust_main_note-import.cgi b/httemplate/misc/process/cust_main_note-import.cgi
new file mode 100644
index 0000000..6aa8b1d
--- /dev/null
+++ b/httemplate/misc/process/cust_main_note-import.cgi
@@ -0,0 +1,82 @@
+<% include("/elements/header.html", "Batch Customer Note Import $op") %>
+
+The following items <% $op eq 'Preview' ? 'would not be' : 'were not' %> imported. (See below for imported items)
+<PRE>
+% foreach my $row (@uninserted) {
+% $csv->combine( (map{ $row->{$_} } qw(last first note) ),
+% $row->{error} ? ('#!', $row->{error}) : (),
+% );
+<% $csv->string %>
+% }
+</PRE>
+
+The following items <% $op eq 'Preview' ? 'would be' : 'were' %> imported. (See above for unimported items)
+
+<PRE>
+% foreach my $row (@inserted) {
+% $csv->combine( (map{ $row->{$_} } qw(custnum last first note) ),
+% ('#!', $row->{name}),
+% );
+<% $csv->string %>
+% }
+</PRE>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $date = time;
+my $otaker = $FS::CurrentUser::CurrentUser->username;
+my $csv = new Text::CSV_XS;
+
+my $param = $cgi->Vars;
+
+my $op = $param->{preview} ? "Preview" : "Results";
+
+my @inserted = ();
+my @uninserted = ();
+for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+ if ( $param->{"custnum$row"} ) {
+# my $cust_main_note = new FS::cust_main_note {
+# 'custnum' => $param->{"custnum$row"},
+# '_date' => $date,
+# 'otaker' => $otaker,
+# 'comments' => $param->{"note$row"},
+# };
+# my $error = '';
+# $error = $cust_main_note->insert unless ($op eq "Preview");
+ my $cust_main = qsearchs('cust_main',
+ { 'custnum' => $param->{"custnum$row"} }
+ );
+ my $error;
+ if ($cust_main) {
+ $cust_main->comments
+ ? $cust_main->comments($cust_main->comments. " ". $param->{"note$row"})
+ : $cust_main->comments($param->{"note$row"});
+ $error = $cust_main->replace;
+ }else{
+ $error = "Can't find customer " . $param->{"custnum$row"};
+ }
+ my $result = { 'custnum' => $param->{"custnum$row"},
+ 'last' => $param->{"last$row"},
+ 'first' => $param->{"first$row"},
+ 'note' => $param->{"note$row"},
+ 'name' => $param->{"name$row"},
+ 'error' => $error,
+ };
+ if ($error) {
+ push @uninserted, $result;
+ }else{
+ push @inserted, $result;
+ }
+ }else{
+ push @uninserted, { 'custnum' => '',
+ 'last' => $param->{"last$row"},
+ 'first' => $param->{"first$row"},
+ 'note' => $param->{"note$row"},
+ 'error' => '',
+ };
+ }
+}
+</%init>
diff --git a/httemplate/misc/process/cust_pay-import.cgi b/httemplate/misc/process/cust_pay-import.cgi
new file mode 100644
index 0000000..d4ff226
--- /dev/null
+++ b/httemplate/misc/process/cust_pay-import.cgi
@@ -0,0 +1,21 @@
+<% $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %>
+<%init>
+
+my $fh = $cgi->upload('csvfile');
+
+# webbatch? I suppose
+my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+my $error = defined($fh)
+ ? FS::cust_pay::batch_import( {
+ 'filehandle' => $fh,
+ 'agentnum' => scalar($cgi->param('agentnum')),
+ 'format' => scalar($cgi->param('format')),
+ 'paybatch' => $paybatch,
+ } )
+ : 'No file';
+
+errorpage($error)
+ if ( $error );
+
+</%init>
diff --git a/httemplate/misc/process/delay_susp_pkg.html b/httemplate/misc/process/delay_susp_pkg.html
new file mode 100755
index 0000000..c7cc7de
--- /dev/null
+++ b/httemplate/misc/process/delay_susp_pkg.html
@@ -0,0 +1,41 @@
+<% header("Package suspension delayed") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+</HTML>
+<%once>
+
+my $right = 'Delay suspension events';
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right($right);
+
+my ($pkgnum, $date, $cust_pkg, $cust_main, $error);
+
+#untaint pkgnum
+$cgi->param('pkgnum') =~ /^(\d+)$/ or die "Illegal pkgnum";
+$pkgnum = $1;
+
+#untaint date
+str2time($cgi->param('date')) =~ /^(\d+)$/ or die "Illegal date";
+my $date = $1;
+
+$cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
+if ($cust_pkg) {
+ $cust_main = $cust_pkg->cust_main;
+ $cust_main->dundate( $date );
+ $error = $cust_main->replace;
+} else {
+ $error = "Invalid pkgnum";
+}
+
+if ($error) {
+ $cgi->param('error', $error);
+ print $cgi->redirect(popurl(2). "cancel_pkg.html?". $cgi->query_string );
+}
+
+</%init>
diff --git a/httemplate/misc/process/delete-customer.cgi b/httemplate/misc/process/delete-customer.cgi
new file mode 100755
index 0000000..d509a5e
--- /dev/null
+++ b/httemplate/misc/process/delete-customer.cgi
@@ -0,0 +1,33 @@
+%if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "delete-customer.cgi?". $cgi->query_string ) %>
+%} elsif ( $new_custnum ) {
+<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$new_custnum") %>
+%} else {
+<% $cgi->redirect(popurl(3)) %>
+%}
+<%init>
+
+my $conf = new FS::Conf;
+die "Customer deletions not enabled in configuration"
+ unless $conf->exists('deletecustomers');
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Delete customer');
+
+$cgi->param('custnum') =~ /^(\d+)$/;
+my $custnum = $1;
+my $new_custnum;
+if ( $cgi->param('new_custnum') ) {
+ $cgi->param('new_custnum') =~ /^(\d+)$/
+ or die "Illegal new customer number: ". $cgi->param('new_custnum');
+ $new_custnum = $1;
+} else {
+ $new_custnum = '';
+}
+my $cust_main = qsearchs( 'cust_main', { 'custnum' => $custnum } )
+ or die "Customer not found: $custnum";
+
+my $error = $cust_main->delete($new_custnum);
+
+</%init>
diff --git a/httemplate/misc/process/email-customers.html b/httemplate/misc/process/email-customers.html
new file mode 100644
index 0000000..d254cfe
--- /dev/null
+++ b/httemplate/misc/process/email-customers.html
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_main::process_email_search_sql', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/enable_or_disable_tax.html b/httemplate/misc/process/enable_or_disable_tax.html
new file mode 100755
index 0000000..9b7324b
--- /dev/null
+++ b/httemplate/misc/process/enable_or_disable_tax.html
@@ -0,0 +1,41 @@
+%if ($error) {
+<% $cgi->redirect(popurl(2).'enable_or_disable_tax.html?'.$cgi->query_string) %>
+%}else{
+ <% include('/elements/header-popup.html', $title) %>
+
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+ </BODY>
+ </HTML>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $action = '';
+if ( $cgi->param('action') =~ /^(\w+)$/ ) {
+ $action = $1;
+}
+
+my ($query, $count_query) = FS::tax_rate::browse_queries(scalar($cgi->Vars));
+my @tax_rate = qsearch( $query );
+
+#transaction?
+my $error;
+$error = "Invalid action" unless ($action =~ /enable|disable/);
+
+foreach my $tax_rate (@tax_rate) {
+ $action eq 'enable' ? $tax_rate->disabled('') : $tax_rate->disabled('Y');
+ # $tax_rate->manual('Y');
+ $error ||= $tax_rate->replace;
+ last if $error;
+}
+$cgi->param('error', $error) if $error;
+
+my $title = scalar(@tax_rate) == 1 ? 'Tax rate ' : 'Tax rates ';
+$title .= lc($action). 'd';
+
+</%init>
diff --git a/httemplate/misc/process/inventory_item-import.html b/httemplate/misc/process/inventory_item-import.html
new file mode 100644
index 0000000..377943f
--- /dev/null
+++ b/httemplate/misc/process/inventory_item-import.html
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $server = new FS::UI::Web::JSRPC 'FS::inventory_item::process_batch_import', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/link.cgi b/httemplate/misc/process/link.cgi
new file mode 100755
index 0000000..df15dca
--- /dev/null
+++ b/httemplate/misc/process/link.cgi
@@ -0,0 +1,72 @@
+%unless ($error) {
+% #no errors, so let's view this customer.
+% my $custnum = $new->cust_pkg->custnum;
+<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum#cust_pkg$pkgnum" ) %>
+%} else {
+% errorpage($error);
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View/link unlinked services');
+
+my $DEBUG = 0;
+
+$cgi->param('pkgnum') =~ /^(\d+)$/;
+my $pkgnum = $1;
+$cgi->param('svcpart') =~ /^(\d+)$/;
+my $svcpart = $1;
+$cgi->param('svcnum') =~ /^(\d*)$/;
+my $svcnum = $1;
+
+unless ( $svcnum ) {
+ my $part_svc = qsearchs('part_svc',{'svcpart'=>$svcpart});
+ my $svcdb = $part_svc->getfield('svcdb');
+ $cgi->param('link_field') =~ /^(\w+)$/;
+ my $link_field = $1;
+ my %search = ( $link_field => $cgi->param('link_value') );
+ if ( $cgi->param('link_field2') =~ /^(\w+)$/ ) {
+ $search{$1} = $cgi->param('link_value2');
+ }
+
+ my @svc_x = ( sort { ($a->cust_svc->pkgnum > 0) <=> ($b->cust_svc->pkgnum > 0)
+ or ($b->cust_svc->svcpart == $svcpart)
+ <=> ($a->cust_svc->svcpart == $svcpart)
+ }
+ qsearch( $svcdb, \%search )
+ );
+
+ if ( $DEBUG ) {
+ warn scalar(@svc_x). " candidate accounts found for linking ".
+ "(svcpart $svcpart):\n";
+ foreach my $svc_x ( @svc_x ) {
+ warn " ". $svc_x->email.
+ " (svcnum ". $svc_x->svcnum. ",".
+ " pkgnum ". $svc_x->cust_svc->pkgnum. ",".
+ " svcpart ". $svc_x->cust_svc->svcpart. ")\n";
+ }
+ }
+
+ my $svc_x = $svc_x[0];
+
+ errorpage("$link_field not found!") unless $svc_x;
+
+ $svcnum = $svc_x->svcnum;
+
+}
+
+my $old = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+die "svcnum not found!" unless $old;
+my $conf = new FS::Conf;
+my($error, $new);
+if ( $old->pkgnum && ! $conf->exists('legacy_link-steal') ) {
+ $error = "svcnum $svcnum already linked to package ". $old->pkgnum;
+} else {
+ $new = new FS::cust_svc { $old->hash };
+ $new->pkgnum($pkgnum);
+ $new->svcpart($svcpart);
+
+ $error = $new->replace($old);
+}
+
+</%init>
diff --git a/httemplate/misc/process/meta-import.cgi b/httemplate/misc/process/meta-import.cgi
new file mode 100644
index 0000000..68ae49c
--- /dev/null
+++ b/httemplate/misc/process/meta-import.cgi
@@ -0,0 +1,190 @@
+<% include("/elements/header.html",'Map tables') %>
+
+<SCRIPT>
+var gSafeOnload = new Array();
+var gSafeOnsubmit = new Array();
+window.onload = SafeOnload;
+function SafeAddOnLoad(f) {
+ gSafeOnload[gSafeOnload.length] = f;
+}
+function SafeOnload() {
+ for (var i=0;i<gSafeOnload.length;i++)
+ gSafeOnload[i]();
+}
+function SafeAddOnSubmit(f) {
+ gSafeOnsubmit[gSafeOnsubmit.length] = f;
+}
+function SafeOnsubmit() {
+ for (var i=0;i<gSafeOnsubmit.length;i++)
+ gSafeOnsubmit[i]();
+}
+</SCRIPT>
+
+<FORM NAME="OneTrueForm" METHOD="POST" ACTION="meta-import.cgi">
+%
+% #use DBIx::DBSchema;
+% my $schema = new_native DBIx::DBSchema
+% map { $cgi->param($_) } qw( data_source username password );
+% foreach my $field (qw( data_source username password )) {
+
+ <INPUT TYPE="hidden" NAME=<% $field %> VALUE="<% $cgi->param($field) %>">
+% }
+%
+% my %schema;
+% use Tie::DxHash;
+% tie %schema, 'Tie::DxHash';
+% if ( $cgi->param('schema') ) {
+% my $schema_string = $cgi->param('schema');
+%
+ <INPUT TYPE="hidden" NAME="schema" VALUE="<%$schema_string%>">
+%
+% %schema = map { /^\s*(\w+)\s*=>\s*(\w+)\s*$/
+% or die "guru meditation #420: $_";
+% ( $1 => $2 );
+% }
+% split( /\n/, $schema_string );
+% }
+%
+% #first page
+% unless ( $cgi->param('magic') ) {
+
+
+ <INPUT TYPE="hidden" NAME="magic" VALUE="process">
+ <% hashmaker('schema', [ $schema->tables ],
+ [ grep !/^h_/, dbdef->tables ], ) %>
+ <br><INPUT TYPE="submit" VALUE="done">
+%
+%
+% #second page
+% } elsif ( $cgi->param('magic') eq 'process' ) {
+
+
+ <INPUT TYPE="hidden" NAME="magic" VALUE="process2">
+%
+%
+% my %unique;
+% foreach my $table ( keys %schema ) {
+%
+% my @from_columns = $schema->table($table)->columns;
+% my @fs_columns = dbdef->table($schema{$table})->columns;
+%
+%
+
+ <% hashmaker( $table.'__'.$unique{$table}++,
+ \@from_columns => \@fs_columns,
+ $table => $schema{$table}, ) %>
+ <br><hr><br>
+%
+%
+% }
+%
+%
+
+ <br><INPUT TYPE="submit" VALUE="done">
+%
+%
+% #third (results)
+% } elsif ( $cgi->param('magic') eq 'process2' ) {
+%
+% print "<pre>\n";
+%
+% my %unique;
+% foreach my $table ( keys %schema ) {
+% ( my $spaces = $table ) =~ s/./ /g;
+% print "'$table' => { 'table' => '$schema{$table}',\n".
+% #(length($table) x ' '). " 'map' => {\n";
+% "$spaces 'map' => {\n";
+% my %map = map { /^\s*(\w+)\s*=>\s*(\w+)\s*$/
+% or die "guru meditation #420: $_";
+% ( $1 => $2 );
+% }
+% split( /\n/, $cgi->param($table.'__'.$unique{$table}++) );
+% foreach ( keys %map ) {
+% print "$spaces '$_' => '$map{$_}',\n";
+% }
+% print "$spaces },\n";
+% print "$spaces },\n";
+%
+% }
+% print "\n</pre>";
+%
+% } else {
+% warn "unrecognized magic: ". $cgi->param('magic');
+% }
+%
+%
+
+</FORM>
+</BODY>
+</HTML>
+%
+% #hashmaker widget
+% sub hashmaker {
+% my($name, $from, $to, $labelfrom, $labelto) = @_;
+% my $fromsize = scalar(@$from);
+% my $tosize = scalar(@$to);
+% "<TABLE><TR><TH>$labelfrom</TH><TH>$labelto</TH></TR><TR><TD>".
+% qq!<SELECT NAME="${name}_from" SIZE=$fromsize>\n!.
+% join("\n", map { qq!<OPTION VALUE="$_">$_</OPTION>! } sort { $a cmp $b } @$from ).
+% "</SELECT>\n<BR>".
+% qq!<INPUT TYPE="button" VALUE="refill" onClick="repack_${name}_from()">!.
+% '</TD><TD>'.
+% qq!<SELECT NAME="${name}_to" SIZE=$tosize>\n!.
+% join("\n", map { qq!<OPTION VALUE="$_">$_</OPTION>! } sort { $a cmp $b } @$to ).
+% "</SELECT>\n<BR>".
+% qq!<INPUT TYPE="button" VALUE="refill" onClick="repack_${name}_to()">!.
+% '</TD></TR>'.
+% '<TR><TD COLSPAN=2>'.
+% qq!<INPUT TYPE="button" VALUE="map" onClick="toke_$name(this.form)">!.
+% '</TD></TR><TR><TD COLSPAN=2>'.
+% qq!<TEXTAREA NAME="$name" COLS=80 ROWS=8></TEXTAREA>!.
+% '</TD></TR></TABLE>'.
+% "<script>
+% function toke_$name() {
+% fromObject = document.OneTrueForm.${name}_from;
+% for (var i=fromObject.options.length-1;i>-1;i--) {
+% if (fromObject.options[i].selected)
+% fromname = deleteOption_$name(fromObject,i);
+% }
+% toObject = document.OneTrueForm.${name}_to;
+% for (var i=toObject.options.length-1;i>-1;i--) {
+% if (toObject.options[i].selected)
+% toname = deleteOption_$name(toObject,i);
+% }
+% document.OneTrueForm.$name.value = document.OneTrueForm.$name.value + fromname + ' => ' + toname + '\\n';
+% }
+% function deleteOption_$name(object,index) {
+% value = object.options[index].value;
+% object.options[index] = null;
+% return value;
+% }
+% function repack_${name}_from() {
+% var object = document.OneTrueForm.${name}_from;
+% object.options.length = 0;
+% ". join("\n",
+% map { "addOption_$name(object, '$_');\n" }
+% ( sort { $a cmp $b } @$from ) ). "
+% }
+% function repack_${name}_to() {
+% var object = document.OneTrueForm.${name}_to;
+% object.options.length = 0;
+% ". join("\n",
+% map { "addOption_$name(object, '$_');\n" }
+% ( sort { $a cmp $b } @$to ) ). "
+% }
+% function addOption_$name(object,value) {
+% var length = object.length;
+% object.options[length] = new Option(value, value, false, false);
+% }
+% </script>".
+% '';
+% }
+%
+%
+<%init>
+
+#there's no ACL for this... haven't used in ages
+#make XSS-safe if this is used for more than just admins to import data....
+die 'meta-import not enabled; remove this if you want to use it';
+
+</%init>
diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi
new file mode 100644
index 0000000..2baca1e
--- /dev/null
+++ b/httemplate/misc/process/payment.cgi
@@ -0,0 +1,183 @@
+% if ( $cgi->param('batch') ) {
+
+ <% include( '/elements/header.html', ucfirst($type{$payby}). ' processing successful',
+ include('/elements/menubar.html'),
+
+ )
+ %>
+
+ <% include( '/elements/small_custview.html', $cust_main, '', '', popurl(3). "view/cust_main.cgi" ) %>
+
+ <% include('/elements/footer.html') %>
+
+% } else {
+<% $cgi->redirect(popurl(3). "view/cust_pay.html?paynum=$paynum" ) %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Process payment');
+
+#some false laziness w/MyAccount::process_payment
+
+$cgi->param('custnum') =~ /^(\d+)$/
+ or die "illegal custnum ". $cgi->param('custnum');
+my $custnum = $1;
+
+my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+die "unknown custnum $custnum" unless $cust_main;
+
+$cgi->param('amount') =~ /^\s*(\d*(\.\d\d)?)\s*$/
+ or errorpage("illegal amount ". $cgi->param('amount'));
+my $amount = $1;
+errorpage("amount <= 0") unless $amount > 0;
+
+$cgi->param('year') =~ /^(\d+)$/
+ or errorpage("illegal year ". $cgi->param('year'));
+my $year = $1;
+
+$cgi->param('month') =~ /^(\d+)$/
+ or errorpage("illegal month ". $cgi->param('month'));
+my $month = $1;
+
+$cgi->param('payby') =~ /^(CARD|CHEK)$/
+ or errorpage("illegal payby ". $cgi->param('payby'));
+my $payby = $1;
+my %payby2fields = (
+ 'CARD' => [ qw( address1 address2 city state zip ) ],
+ 'CHEK' => [ qw( ss paytype paystate stateid stateid_state ) ],
+);
+my %type = ( 'CARD' => 'credit card',
+ 'CHEK' => 'electronic check (ACH)',
+ );
+
+$cgi->param('payname') =~ /^([\w \,\.\-\']+)$/
+ or errorpage(gettext('illegal_name'). " payname: ". $cgi->param('payname'));
+my $payname = $1;
+
+$cgi->param('payunique') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]*)$/
+ or errorpage(gettext('illegal_text'). " payunique: ". $cgi->param('payunique'));
+my $payunique = $1;
+
+$cgi->param('balance') =~ /^\s*(\-?\s*\d*(\.\d\d)?)\s*$/
+ or errorpage("illegal balance");
+my $balance = $1;
+
+my $payinfo;
+my $paycvv = '';
+if ( $payby eq 'CHEK' ) {
+
+ if ($cgi->param('payinfo1') =~ /xx/i || $cgi->param('payinfo2') =~ /xx/i ) {
+ $payinfo = $cust_main->payinfo;
+ } else {
+ $cgi->param('payinfo1') =~ /^(\d+)$/
+ or errorpage("illegal account number ". $cgi->param('payinfo1'));
+ my $payinfo1 = $1;
+ $cgi->param('payinfo2') =~ /^(\d+)$/
+ or errorpage("illegal ABA/routing number ". $cgi->param('payinfo2'));
+ my $payinfo2 = $1;
+ $payinfo = $payinfo1. '@'. $payinfo2;
+ }
+
+} elsif ( $payby eq 'CARD' ) {
+
+ $payinfo = $cgi->param('payinfo');
+ if ($payinfo eq $cust_main->paymask) {
+ $payinfo = $cust_main->payinfo;
+ }
+ $payinfo =~ s/\D//g;
+ $payinfo =~ /^(\d{13,16})$/
+ or errorpage(gettext('invalid_card')); # . ": ". $self->payinfo;
+ $payinfo = $1;
+ validate($payinfo)
+ or errorpage(gettext('invalid_card')); # . ": ". $self->payinfo;
+ errorpage(gettext('unknown_card_type'))
+ if cardtype($payinfo) eq "Unknown";
+
+ if ( defined $cust_main->dbdef_table->column('paycvv') ) {
+ if ( length($cgi->param('paycvv') ) ) {
+ if ( cardtype($payinfo) eq 'American Express card' ) {
+ $cgi->param('paycvv') =~ /^(\d{4})$/
+ or errorpage("CVV2 (CID) for American Express cards is four digits.");
+ $paycvv = $1;
+ } else {
+ $cgi->param('paycvv') =~ /^(\d{3})$/
+ or errorpage("CVV2 (CVC2/CID) is three digits.");
+ $paycvv = $1;
+ }
+ }
+ }
+
+} else {
+ die "unknown payby $payby";
+}
+
+my $error = '';
+my $paynum = '';
+if ( $cgi->param('batch') ) {
+
+ $error = $cust_main->batch_card(
+ 'payby' => $payby,
+ 'amount' => $amount,
+ 'payinfo' => $payinfo,
+ 'paydate' => "$year-$month-01",
+ 'payname' => $payname,
+ map { $_ => $cgi->param($_) }
+ @{$payby2fields{$payby}}
+ );
+ errorpage($error) if $error;
+
+} else {
+
+ $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $amount,
+ 'quiet' => 1,
+ 'manual' => 1,
+ 'balance' => $balance,
+ 'payinfo' => $payinfo,
+ 'paydate' => "$year-$month-01",
+ 'payname' => $payname,
+ 'payunique' => $payunique,
+ 'paycvv' => $paycvv,
+ 'paynum_ref' => \$paynum,
+ map { $_ => $cgi->param($_) } @{$payby2fields{$payby}}
+ );
+ errorpage($error) if $error;
+
+ $cust_main->apply_payments;
+
+}
+
+if ( $cgi->param('save') ) {
+ my $new = new FS::cust_main { $cust_main->hash };
+ if ( $payby eq 'CARD' ) {
+ $new->set( 'payby' => ( $cgi->param('auto') ? 'CARD' : 'DCRD' ) );
+ } elsif ( $payby eq 'CHEK' ) {
+ $new->set( 'payby' => ( $cgi->param('auto') ? 'CHEK' : 'DCHK' ) );
+ } else {
+ die "unknown payby $payby";
+ }
+ $new->set( 'payinfo' => $payinfo );
+ $new->set( 'paydate' => "$year-$month-01" );
+ $new->set( 'payname' => $payname );
+
+ #false laziness w/FS:;cust_main::realtime_bop - check both to make sure
+ # working correctly
+ my $conf = new FS::Conf;
+ if ( $payby eq 'CARD' &&
+ grep { $_ eq cardtype($payinfo) } $conf->config('cvv-save') ) {
+ $new->set( 'paycvv' => $paycvv );
+ } else {
+ $new->set( 'paycvv' => '');
+ }
+
+ $new->set( $_ => $cgi->param($_) ) foreach @{$payby2fields{$payby}};
+
+ my $error = $new->replace($cust_main);
+ errorpage("payment processed successfully, but error saving info: $error")
+ if $error;
+ $cust_main = $new;
+}
+
+#success!
+
+</%init>
diff --git a/httemplate/misc/process/phone_avail-import.html b/httemplate/misc/process/phone_avail-import.html
new file mode 100644
index 0000000..f1a2f24
--- /dev/null
+++ b/httemplate/misc/process/phone_avail-import.html
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $server = new FS::UI::Web::JSRPC 'FS::phone_avail::process_batch_import', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/recharge_svc.html b/httemplate/misc/process/recharge_svc.html
new file mode 100755
index 0000000..147b953
--- /dev/null
+++ b/httemplate/misc/process/recharge_svc.html
@@ -0,0 +1,92 @@
+%unless ($error) {
+%
+% my ($amount, $seconds, $up, $down, $total) = (0, 0, 0, 0, 0);
+% #should probably use payby.pm but whatever
+% if ($payby eq 'PREP') {
+% $error = $cust_main->get_prepay($prepaid, \$amount, \$seconds, \$up, \$down, \$total)
+% || $svc_acct->increment_seconds($seconds)
+% || $svc_acct->increment_upbytes($up)
+% || $svc_acct->increment_downbytes($down)
+% || $svc_acct->increment_totalbytes($total)
+% || $cust_main->insert_cust_pay_prepay( $amount, $prepaid );
+% } elsif ( $payby =~ /^(CARD|DCRD|CHEK|DCHK|LECB|BILL|COMP)$/ ) {
+% my $part_pkg = $svc_acct->cust_svc->cust_pkg->part_pkg;
+% $amount = $part_pkg->option('recharge_amount', 1);
+% my %rhash = map { $_ =~ /^recharge_(.*)$/; $1, $part_pkg->option($_) }
+% grep { $part_pkg->option($_, 1) }
+% qw ( recharge_seconds recharge_upbytes recharge_downbytes
+% recharge_totalbytes );
+%
+% my $description = "Recharge";
+% $description .= " $rhash{seconds}s" if $rhash{seconds};
+% $description .= " $rhash{upbytes} up" if $rhash{upbytes};
+% $description .= " $rhash{downbytes} down" if $rhash{downbytes};
+% $description .= " $rhash{totalbytes} total" if $rhash{totalbytes};
+%
+% $error = $cust_main->charge($amount, "Recharge " . $svc_acct->label,
+% $description, $part_pkg->taxclass);
+%
+% if ($part_pkg->option('recharge_reset', 1)) {
+% $error ||= $svc_acct->set_usage(\%rhash);
+% }else{
+% $error ||= $svc_acct->recharge(\%rhash);
+% }
+%
+% my $old_balance = $cust_main->balance;
+% $error ||= $cust_main->bill;
+% $error ||= $cust_main->apply_payments_and_credits;
+% my $bill_error = $cust_main->collect('realtime' => 1) unless $error;
+% $error ||= "Failed to collect - $bill_error"
+% if $cust_main->balance > $old_balance && $cust_main->balance > 0
+% && $payby ne 'BILL';
+%
+% } else {
+% $error = "fatal error - unknown payby: $payby";
+% }
+%}
+%
+%if ($error) {
+% $cgi->param('error', $error);
+% $dbh->rollback if $oldAutoCommit;
+% print $cgi->redirect(popurl(2). "recharge_svc.html?". $cgi->query_string );
+%}
+%$dbh->commit or die $dbh->errstr if $oldAutoCommit;
+%
+<% header("Package recharged") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY></HTML>
+<%init>
+
+my $conf = new FS::Conf;
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Recharge customer service');
+
+#untaint svcnum
+my $svcnum = $cgi->param('svcnum');
+$svcnum =~ /^(\d+)$/ || die "Illegal svcnum";
+$svcnum = $1;
+
+#untaint prepaid
+my $prepaid = $cgi->param('prepaid');
+$prepaid =~ /^(\w*)$/;
+$prepaid = $1;
+
+#untaint payby
+my $payby = $cgi->param('payby');
+$payby =~ /^([A-Z]*)$/;
+$payby = $1;
+
+my $error = '';
+my $svc_acct = qsearchs( 'svc_acct', {'svcnum'=>$svcnum} );
+$error = "Can't recharge service $svcnum. " unless $svc_acct;
+
+my $cust_main = $svc_acct->cust_svc->cust_pkg->cust_main;
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+my $dbh = dbh;
+
+</%init>
diff --git a/httemplate/misc/process/recharge_svc.new b/httemplate/misc/process/recharge_svc.new
new file mode 100755
index 0000000..bc916e5
--- /dev/null
+++ b/httemplate/misc/process/recharge_svc.new
@@ -0,0 +1,85 @@
+%
+%
+%#untaint svcnum
+%my $svcnum = $cgi->param('svcnum');
+%$svcnum =~ /^(\d+)$/ || die "Illegal svcnum";
+%$svcnum = $1;
+%
+%#untaint prepaid
+%my $prepaid = $cgi->param('prepaid');
+%$prepaid =~ /^(\w*)$/;
+%$prepaid = $1;
+
+%#untaint payby
+%my $payby = $cgi->param('payby');
+%$payby =~ /^([A-Z]*)$/;
+%$payby = $1;
+%
+%my $error = '';
+%my $svc_acct = qsearchs( 'svc_acct', {'svcnum'=>$svcnum} );
+%$error = "Can't recharge service $svcnum. " unless $svc_acct;
+%
+%my $cust_main = $svc_acct->cust_svc->cust_pkg->cust_main;
+%
+%my $oldAutoCommit = $FS::UID::AutoCommit;
+%local $FS::UID::AutoCommit = 0;
+%my $dbh = dbh;
+%
+%
+%unless ($error) {
+%
+% my ($amount, $seconds, $up, $down, $total) = (0, 0, 0, 0, 0);
+% #should probably use payby.pm but whatever
+% if ($payby eq 'PREP') {
+% $error = $cust_main->get_prepay($prepaid, \$amount, \$seconds, \$up, \$down, \$total)
+% || $svc_acct->increment_seconds($seconds)
+% || $svc_acct->increment_upbytes($up)
+% || $svc_acct->increment_downbytes($down)
+% || $svc_acct->increment_totalbytes($total)
+% || $cust_main->insert_cust_pay_prepay( $amount, $prepaid );
+% } elsif ( $payby =~ /^(CARD|DCRD|CHEK|DCHK|LECB|BILL|COMP)$/ ) {
+% my $part_pkg = $svc_acct->cust_svc->cust_pkg->part_pkg;
+% $amount = $part_pkg->option('recharge_amount', 1);
+% my %rhash = map { $_ =~ /^recharge_(.*)$/; $1, $part_pkg->option($_, 1) }
+% qw ( recharge_seconds recharge_upbytes recharge_downbytes
+% recharge_totalbytes );
+%
+% my $description = "Recharge";
+% $description .= " $rhash{seconds}s" if $rhash{seconds};
+% $description .= " $rhash{upbytes} up" if $rhash{upbytes};
+% $description .= " $rhash{downbytes} down" if $rhash{downbytes};
+% $description .= " $rhash{totalbytes} total" if $rhash{totalbytes};
+%
+% $error = $cust_main->charge($amount, "Recharge " . $svc_acct->label,
+% $description, $part_pkg->taxclass);
+%
+% $error ||= $svc_acct->recharge(\%rhash);
+%
+% my $old_balance = $cust_main->balance;
+% $error ||= $cust_main->bill;
+% $error ||= $cust_main->apply_payments_and_credits;
+% my $bill_error = $cust_main->collect('realtime' => 1) unless $error;
+% $error ||= "Failed to collect - $bill_error"
+% if $cust_main->balance > $old_balance && $cust_main->balance > 0
+% && $payby ne 'BILL';
+%
+% } else {
+% $error = "fatal error - unknown payby: $payby";
+% }
+%}
+%
+%if ($error) {
+% $cgi->param('error', $error);
+% $dbh->rollback if $oldAutoCommit;
+% print $cgi->redirect(popurl(2). "recharge_svc.html?". $cgi->query_string );
+%}
+%$dbh->commit or die $dbh->errstr if $oldAutoCommit;
+%
+<% header("Package recharged") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY></HTML>
+<%init>
+my $conf = new FS::Conf;
+</%init>
diff --git a/httemplate/misc/process/tax-import.cgi b/httemplate/misc/process/tax-import.cgi
new file mode 100644
index 0000000..016d4b6
--- /dev/null
+++ b/httemplate/misc/process/tax-import.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::tax_rate::process_batch_import', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/tax-upgrade.cgi b/httemplate/misc/process/tax-upgrade.cgi
new file mode 100644
index 0000000..8782282
--- /dev/null
+++ b/httemplate/misc/process/tax-upgrade.cgi
@@ -0,0 +1,147 @@
+% if ( $error ) {
+% warn $error;
+% errorpage($error);
+% } else {
+ <% include('/elements/header.html','Import successful') %>
+ <% include('/elements/footer.html') %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $cfh = $cgi->upload('codefile');
+my $zfh = $cgi->upload('plus4file');
+my $tfh = $cgi->upload('txmatrix');
+my $dfh = $cgi->upload('detail');
+#warn $cgi;
+#warn $fh;
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+my $dbh = dbh;
+
+my $error = '';
+
+my ($cifh, $cdfh, $zifh, $zdfh, $tifh, $tdfh);
+
+if (defined($cfh)) {
+ $cifh = new File::Temp( TEMPLATE => 'code.insert.XXXXXXXX',
+ DIR => $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc,
+ ) or die "can't open temp file: $!\n";
+
+ $cdfh = new File::Temp( TEMPLATE => 'code.insert.XXXXXXXX',
+ DIR => $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc,
+ ) or die "can't open temp file: $!\n";
+
+ while(<$cfh>) {
+ my $fh = '';
+ $fh = $cifh if $_ =~ /"I"\s*$/;
+ $fh = $cdfh if $_ =~ /"D"\s*$/;
+ die "bad input line: $_" unless $fh;
+ print $fh $_;
+ }
+ seek $cifh, 0, 0;
+ seek $cdfh, 0, 0;
+
+}else{
+ $error = 'No code file';
+}
+
+$error ||= FS::tax_class::batch_import( {
+ filehandle => $cifh,
+ 'format' => scalar($cgi->param('format')),
+ } );
+
+close $cifh if $cifh;
+
+if (defined($zfh)) {
+ $zifh = new File::Temp( TEMPLATE => 'plus4.insert.XXXXXXXX',
+ DIR => $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc,
+ ) or die "can't open temp file: $!\n";
+
+ $zdfh = new File::Temp( TEMPLATE => 'plus4.insert.XXXXXXXX',
+ DIR => $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc,
+ ) or die "can't open temp file: $!\n";
+
+ while(<$zfh>) {
+ my $fh = '';
+ $fh = $zifh if $_ =~ /"I"\s*$/;
+ $fh = $zdfh if $_ =~ /"D"\s*$/;
+ die "bad input line: $_" unless $fh;
+ print $fh $_;
+ }
+ seek $zifh, 0, 0;
+ seek $zdfh, 0, 0;
+
+}else{
+ $error = 'No plus4 file';
+}
+
+$error ||= FS::cust_tax_location::batch_import( {
+ filehandle => $zifh,
+ 'format' => scalar($cgi->param('format')),
+ } );
+close $zifh if $zifh;
+
+if (defined($tfh)) {
+ $tifh = new File::Temp( TEMPLATE => 'txmatrix.insert.XXXXXXXX',
+ DIR => $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc,
+ ) or die "can't open temp file: $!\n";
+
+ $tdfh = new File::Temp( TEMPLATE => 'txmatrix.insert.XXXXXXXX',
+ DIR => $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc,
+ ) or die "can't open temp file: $!\n";
+
+ while(<$tfh>) {
+ my $fh = '';
+ $fh = $tifh if $_ =~ /"I"\s*$/;
+ $fh = $tdfh if $_ =~ /"D"\s*$/;
+ die "bad input line: $_" unless $fh;
+ print $fh $_;
+ }
+ seek $tifh, 0, 0;
+ seek $tdfh, 0, 0;
+
+}else{
+ $error = 'No tax matrix file';
+}
+
+$error ||= FS::part_pkg_taxrate::batch_import( {
+ filehandle => $tifh,
+ 'format' => scalar($cgi->param('format')),
+ } );
+close $tifh if $tifh;
+
+$error ||= defined($dfh)
+ ? FS::tax_rate::batch_update( {
+ filehandle => $dfh,
+ 'format' => scalar($cgi->param('format')),
+ } )
+ : 'No tax detail file';
+
+$error ||= FS::part_pkg_taxrate::batch_import( {
+ filehandle => $tdfh,
+ 'format' => scalar($cgi->param('format')),
+ } );
+close $tdfh if $tdfh;
+
+$error ||= FS::cust_tax_location::batch_import( {
+ filehandle => $zdfh,
+ 'format' => scalar($cgi->param('format')),
+ } );
+close $zdfh if $zdfh;
+
+$error ||= FS::tax_class::batch_import( {
+ filehandle => $cdfh,
+ 'format' => scalar($cgi->param('format')),
+ } );
+close $cdfh if $cdfh;
+
+if ($error) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+}else{
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+}
+
+</%init>
diff --git a/httemplate/misc/process/timeworked.html b/httemplate/misc/process/timeworked.html
new file mode 100644
index 0000000..860118e
--- /dev/null
+++ b/httemplate/misc/process/timeworked.html
@@ -0,0 +1,57 @@
+% if ($error) {
+<% $cgi->redirect(popurl(2). "timeworked.html?". $cgi->query_string) %>
+% } else {
+<% $cgi->redirect(popurl(3). "search/timeworked.html") %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Time queue');
+
+my @acct_rt_transaction;
+foreach my $transaction (
+ map { /^transactionid(\d+)$/; $1; } grep /^transactionid\d+$/, $cgi->param
+) {
+ my $s = "multiplier${transaction}_";
+ my %multipliers = map { /^$s(\d+)$/; $1 => $cgi->param("$s$1"); }
+ grep /^$s\d+$/, $cgi->param;
+ my $msum = 0;
+ foreach(values %multipliers) {$msum += $_};
+
+ my $seconds = $cgi->param("seconds$transaction");
+ my %seconds =
+ map { $_ => sprintf("%.0f", $seconds * $multipliers{$_} / $msum) }
+ (keys %multipliers);
+ my $sum = 0;
+ my $count = 0;
+ foreach (values %seconds) {
+ $sum += $_;
+ $count++;
+ }
+
+ #fudge in some time if we're close
+ if (abs($seconds-$sum) <= $count) {
+ my $adjustment = $seconds-$sum;
+ foreach (keys %seconds) { # explicitly choose one?
+ $seconds{$_} += $adjustment;
+ last;
+ }
+ } else {
+ die "unexpectedly cannot apportion time";
+ }
+
+ foreach my $customer ( grep {$seconds{$_}} keys %seconds ) {
+ push @acct_rt_transaction, new FS::acct_rt_transaction {
+ 'custnum' => $customer,
+ 'transaction_id' => $transaction,
+ 'seconds' => $seconds{$customer},
+ 'support' => int( $seconds{$customer} * $msum ),
+ };
+ }
+
+}
+
+my $error = FS::acct_rt_transaction->batch_insert(@acct_rt_transaction);
+$cgi->param('error', $error) if $error;
+
+</%init>
diff --git a/httemplate/misc/queue.cgi b/httemplate/misc/queue.cgi
new file mode 100644
index 0000000..5dee29b
--- /dev/null
+++ b/httemplate/misc/queue.cgi
@@ -0,0 +1,49 @@
+<% $cgi->redirect(popurl(2). "search/queue.html") %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Job queue');
+
+$cgi->param('action') =~ /^(new|del|(retry|remove) selected)$/
+ or die "Illegal action";
+my $action = $1;
+
+my $job;
+if ( $action eq 'new' || $action eq 'del' ) {
+ $cgi->param('jobnum') =~ /^(\d+)$/ or die "Illegal jobnum";
+ my $jobnum = $1;
+ $job = qsearchs('queue', { 'jobnum' => $1 })
+ or die "unknown jobnum $jobnum - ".
+ "it probably completed normally or was removed by another user";
+}
+
+if ( $action eq 'new' ) {
+ my %hash = $job->hash;
+ $hash{'status'} = 'new';
+ $hash{'statustext'} = '';
+ my $new = new FS::queue \%hash;
+ my $error = $new->replace($job);
+ die $error if $error;
+} elsif ( $action eq 'del' ) {
+ my $error = $job->delete;
+ die $error if $error;
+} elsif ( $action =~ /^(retry|remove) selected$/ ) {
+ foreach my $jobnum (
+ map { /^jobnum(\d+)$/; $1; } grep /^jobnum\d+$/, $cgi->param
+ ) {
+ my $job = qsearchs('queue', { 'jobnum' => $jobnum });
+ if ( $action eq 'retry selected' && $job ) { #new
+ my %hash = $job->hash;
+ $hash{'status'} = 'new';
+ $hash{'statustext'} = '';
+ my $new = new FS::queue \%hash;
+ my $error = $new->replace($job);
+ die $error if $error;
+ } elsif ( $action eq 'remove selected' && $job ) { #del
+ my $error = $job->delete;
+ die $error if $error;
+ }
+ }
+}
+
+</%init>
diff --git a/httemplate/misc/recharge_svc.html b/httemplate/misc/recharge_svc.html
new file mode 100755
index 0000000..d8a8faa
--- /dev/null
+++ b/httemplate/misc/recharge_svc.html
@@ -0,0 +1,105 @@
+<% include('/elements/header-popup.html', 'Recharge Service' ) %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="recharge_popup" ACTION="<% popurl(1) %>process/recharge_svc.html" METHOD=POST>
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
+
+<BR><BR>
+<% "Recharge $svcnum: $label - $value" %>
+<% ntable("#cccccc", 2) %>
+
+<SCRIPT>
+ function toggle_prep(what) {
+ if (what.value == "PREP"){
+ what.form.prepaid.disabled = false;
+ }else{
+ what.form.prepaid.disabled = true;
+ }
+ }
+</SCRIPT>
+<TR>
+ <TD><INPUT TYPE="radio" NAME="payby" onchange="toggle_prep(this)" VALUE="PREP" <% $payby eq "PREP" ? 'checked' : '' %> <% $recharge_label ? '' : 'disabled' %>></TD>
+ <TD>Prepaid Card</TD>
+% if ($recharge_label) {
+ <TD><INPUT TYPE="radio" NAME="payby" onchange="toggle_prep(this)" VALUE="<% $cust_svc->cust_pkg->cust_main->payby %>" <% $payby eq "PREP" ? '' : 'checked' %>></TD>
+ <TD><% $recharge_label %></TD>
+% }
+</TR>
+<TR>
+ <TD>Enter prepaid card: </TD>
+ <TD><INPUT TYPE="text" NAME="prepaid" VALUE="<% $prepaid |h %>" <% $payby eq "PREP" ? '' : 'disabled' %>></TD>
+</TR>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="Recharge">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Recharge customer service');
+
+my($svcnum, $prepaid, $payby);
+if ( $cgi->param('error') ) {
+ $svcnum = $cgi->param('svcnum');
+ $prepaid = $cgi->param('prepaid');
+ $payby = $cgi->param('payby');
+} elsif ( $cgi->param('svcnum') =~ /^(\d+)$/ ) {
+ $svcnum = $1;
+ $prepaid = '';
+} else {
+ die "illegal query ". $cgi->keywords;
+}
+
+my $title = 'Recharge Service';
+
+my $cust_svc = qsearchs('cust_svc', {'svcnum' => $svcnum});
+die "No such service: $svcnum" unless $cust_svc;
+
+my($label, $value) = $cust_svc->label;
+
+$payby = $cust_svc->cust_pkg->cust_main->payby unless $payby;
+my $part_pkg = $cust_svc->cust_pkg->part_pkg;
+my $amount = $part_pkg->option('recharge_amount', 1) || 0;
+
+my $recharge_label = "Charge $money_char$amount for ";
+
+$recharge_label .= $part_pkg->option('recharge_seconds', 1) . 's '
+ if $part_pkg->option('recharge_seconds', 1);
+
+
+$recharge_label .= FS::UI::bytecount::display_bytecount(
+ $part_pkg->option('recharge_upbytes', 1) )
+ . ' up '
+ if $part_pkg->option('recharge_upbytes', 1);
+
+
+$recharge_label .= FS::UI::bytecount::display_bytecount(
+ $part_pkg->option('recharge_downbytes', 1) )
+ . ' down '
+ if $part_pkg->option('recharge_downbytes', 1);
+
+
+$recharge_label .= FS::UI::bytecount::display_bytecount(
+ $part_pkg->option('recharge_totalbytes', 1) )
+ . ' total '
+ if $part_pkg->option('recharge_totalbytes', 1);
+
+
+$recharge_label = ''
+ unless ($recharge_label ne "Charge $money_char$amount for ");
+
+</%init>
+
diff --git a/httemplate/misc/spool_invoices.cgi b/httemplate/misc/spool_invoices.cgi
new file mode 100644
index 0000000..bfe24e6
--- /dev/null
+++ b/httemplate/misc/spool_invoices.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Resend invoices');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_bill::process_respool', $cgi;
+
+</%init>
diff --git a/httemplate/misc/states.cgi b/httemplate/misc/states.cgi
new file mode 100644
index 0000000..cf2b46e
--- /dev/null
+++ b/httemplate/misc/states.cgi
@@ -0,0 +1,7 @@
+%
+%
+% my $country = $cgi->param('arg');
+% my @output = states_hash($country);
+%
+%
+[ <% join(', ', map { qq("$_") } @output) %> ]
diff --git a/httemplate/misc/svc_acct-domains.cgi b/httemplate/misc/svc_acct-domains.cgi
new file mode 100644
index 0000000..5734574
--- /dev/null
+++ b/httemplate/misc/svc_acct-domains.cgi
@@ -0,0 +1,31 @@
+[ <% join(', ', map { qq("$_->[0]", "$_->[1]") } @svc_domain) %> ]
+<%init>
+
+my $conf = new FS::Conf;
+
+my $pkgpart_svcpart = $cgi->param('arg');
+$pkgpart_svcpart =~ /^\d+_(\d+)$/;
+my $part_svc = qsearchs('part_svc', { 'svcpart' => $1 }) if $1;
+my $part_svc_column = $part_svc->part_svc_column('domsvc') if $part_svc;
+
+my @output = split /,/, $part_svc_column->columnvalue if $part_svc_column;
+my $columnflag = $part_svc_column->columnflag if $part_svc_column;
+my @svc_domain = ();
+my %seen = ();
+
+foreach (@output) {
+ my $svc_domain = qsearchs('svc_domain', { 'svcnum' => $_ })
+ or warn "unknown svc_domain.svcnum $_ for part_svc_column domsvc; ".
+ "svcpart = " . $part_svc->svcpart;
+ push @svc_domain, [ $_ => $svc_domain->domain ];
+ $seen{$_}++;
+}
+if ($conf->exists('svc_acct-alldomains')
+ && ( $columnflag eq 'D' || $columnflag eq '' )
+ ) {
+ foreach (grep { $_->svcnum ne $output[0] } qsearch('svc_domain', {}) ){
+ push @svc_domain, [ $_->svcnum => $_->domain ];
+ }
+}
+
+</%init>
diff --git a/httemplate/misc/tax-import.cgi b/httemplate/misc/tax-import.cgi
new file mode 100644
index 0000000..a695e97
--- /dev/null
+++ b/httemplate/misc/tax-import.cgi
@@ -0,0 +1,65 @@
+<% include("/elements/header.html",'Batch Tax Rate Import') %>
+
+Import a CSV file set containing tax rate records.
+<BR><BR>
+
+<% include( '/elements/form-file_upload.html',
+ 'name' => 'TaxRateUpload',
+ 'action' => 'process/tax-import.cgi',
+ 'num_files' => 5,
+ 'fields' => [ 'format', ],
+ 'message' => 'Tax rates imported',
+ )
+%>
+
+<% &ntable("#cccccc", 2) %>
+
+ <TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <OPTION VALUE="cch-update" SELECTED>CCH update (CSV)
+ <OPTION VALUE="cch">CCH initial import (CSV)
+ <OPTION VALUE="cch-fixed-update">CCH update (fixed length)
+ <OPTION VALUE="cch-fixed">CCH initial import (fixed length)
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/file-upload.html',
+ 'field' => [ 'codefile',
+ 'plus4file',
+ 'zipfile',
+ 'txmatrix',
+ 'detail',
+ ],
+ 'label' => [ 'code filename',
+ 'plus4 filename',
+ 'zip filename',
+ 'txmatrix filename',
+ 'detail filename',
+ ],
+ 'debug' => 0,
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ VALUE = "Import CSV files"
+ onClick = "document.TaxRateUpload.submit.disabled=true;"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+</%init>
diff --git a/httemplate/misc/timeworked.html b/httemplate/misc/timeworked.html
new file mode 100755
index 0000000..db4b64c
--- /dev/null
+++ b/httemplate/misc/timeworked.html
@@ -0,0 +1,135 @@
+<% include('/elements/header.html', $title, '' ) %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="timeworked_form" ACTION="<% popurl(1) %>process/timeworked.html" METHOD=POST>
+
+<TABLE CELLSPACING="2" CELLPADDING="2" RULES="groups" FRAME="hsides">
+
+ <THEAD>
+ <TR>
+ <TH>Trans</TH>
+ <TH COLSPAN="2">Ticket</TH>
+ <TH>Time</TH>
+ <TH COLSPAN="2">Customer</TH>
+ <TH>Multiplier</TH>
+ </TR>
+
+ <TR>
+ <TH>#</TH>
+ <TH>#</TH>
+ <TH>Subject</TH>
+ <TH>hours</TH>
+ <TH>#</TH>
+ <TH>Name</TH>
+ <TH></TH>
+ </TR>
+ </THEAD>
+
+ <TBODY>
+
+% foreach my $tr_id ( keys %ticketmap ) {
+% my (@customers) = @{$customers{$ticketmap{$tr_id}}};
+% next unless @customers;
+% my $default_multiplier = sprintf("%.2f", 1/@customers);
+% my ($custnum, $name) = split(':', pop @customers, 2);
+% my $link = $p. 'rt/Ticket/Display.html?id='. $ticketmap{$tr_id}.
+% '#txn-'. $tr_id;
+
+ <TR>
+ <TD><a href="<% $link %>"><% $tr_id %></a></TD>
+ <TD><a href="<% $link %>"><% $ticketmap{$tr_id} %></a></TD>
+ <TD><a href="<% $link %>"><% $ticket{$ticketmap{$tr_id}} |h %></a></TD>
+
+% my $seconds = 0;
+% if ( $cgi->param("seconds$tr_id") =~ /^(\d+)$/ ) {
+% $seconds = $1;
+% }
+
+ <TD><% sprintf("%0.2f", $seconds/3600) %></TD>
+ <TD ALIGN="right"><% $custnum %></TD>
+ <TD ALIGN="right"><% $name %></TD>
+ <TD>
+ <INPUT TYPE="hidden" NAME="transactionid<%$tr_id%>" VALUE="1" >
+ <INPUT TYPE="hidden" NAME="seconds<%$tr_id%>" VALUE="<% $seconds %>" >
+
+% my $multiplier = $default_multiplier;
+% my $mult_paramname = "multiplier${tr_id}_$custnum";
+% if ( $cgi->param($mult_paramname) =~ /^\s*([\d\.]+)\s*$/ ) {
+% $multiplier = $1;
+% }
+
+ <INPUT TYPE="text" NAME="<% $mult_paramname %>" SIZE="5" VALUE="<% $multiplier %>" >
+ </TD>
+ </TR>
+
+% foreach ( @customers ) {
+% ($custnum, $name) = split(':', $_, 2);
+
+ <TR>
+ <TD ALIGN="right" COLSPAN="5" ><% $custnum %></TD>
+ <TD ALIGN="right"><% $name %></TD>
+ <TD>
+
+% $multiplier = $default_multiplier;
+% $mult_paramname = "multiplier${tr_id}_$custnum";
+% if ( $cgi->param($mult_paramname) =~ /^\s*([\d\.]+)\s*$/ ) {
+% $multiplier = $1;
+% }
+
+ <INPUT TYPE="text" NAME="<% $mult_paramname %>" SIZE="5" VALUE="<% $multiplier %>" >
+
+ </TD>
+
+ </TR>
+
+% }
+% }
+
+ </TBODY>
+
+</TABLE>
+
+<BR>
+
+<INPUT TYPE="submit" NAME="submit" VALUE="<% $title %>">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Time queue');
+
+my(%ticketmap, %ticket, %customers);
+my $title = 'Assign Time Worked';
+tie %ticketmap, 'Tie::IxHash';
+
+RT::Init();
+
+my $CurrentUser = RT::CurrentUser->new();
+$CurrentUser->LoadByName($FS::CurrentUser::CurrentUser->username);
+
+foreach my $id ( map { /^transactionid(\d+)$/; $1; }
+ grep /^transactionid\d+$/, $cgi->param) {
+ my $transaction = new RT::Transaction($CurrentUser);
+ $transaction->Load($id);
+ $ticketmap{$id} = $transaction->ObjectId;
+ unless(exists($ticket{$ticketmap{$id}})) {
+ my $ticket = new RT::Ticket($CurrentUser);
+ $ticket->Load($ticketmap{$id});
+ $ticket{$ticketmap{$id}} = $ticket->Subject;
+ $customers{$ticketmap{$id}} =
+ [ map { $_->Resolver->AsString }
+ grep { $_->Resolver->{'fstable'} eq 'cust_main' }
+ grep { $_->Scheme eq 'freeside' }
+ map { $_->TargetURI }
+ @{ $ticket->_Links('Base')->ItemsArrayRef }
+ ];
+
+ }
+}
+
+</%init>
+
diff --git a/httemplate/misc/unadjourn_pkg.cgi b/httemplate/misc/unadjourn_pkg.cgi
new file mode 100755
index 0000000..356b49c
--- /dev/null
+++ b/httemplate/misc/unadjourn_pkg.cgi
@@ -0,0 +1,17 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect(popurl(2). "view/cust_main.cgi?".$cust_pkg->getfield('custnum')) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Suspend customer package later');
+
+my ($pkgnum) = $cgi->keywords;
+my $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
+my $error = "No package $pkgnum" unless $cust_pkg;
+
+$error ||= $cust_pkg->unadjourn;
+
+</%init>
diff --git a/httemplate/misc/unapply-cust_credit.cgi b/httemplate/misc/unapply-cust_credit.cgi
new file mode 100755
index 0000000..ed739ac
--- /dev/null
+++ b/httemplate/misc/unapply-cust_credit.cgi
@@ -0,0 +1,20 @@
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unapply credit');
+
+#untaint crednum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal crednum";
+my $crednum = $1;
+
+my $cust_credit = qsearchs('cust_credit', { 'crednum' => $crednum } );
+my $custnum = $cust_credit->custnum;
+
+foreach my $cust_credit_bill ( $cust_credit->cust_credit_bill ) {
+ my $error = $cust_credit_bill->delete;
+ errorpage($error) if $error;
+}
+
+</%init>
diff --git a/httemplate/misc/unapply-cust_pay.cgi b/httemplate/misc/unapply-cust_pay.cgi
new file mode 100755
index 0000000..8cdac18
--- /dev/null
+++ b/httemplate/misc/unapply-cust_pay.cgi
@@ -0,0 +1,20 @@
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unapply payment');
+
+#untaint paynum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal paynum";
+my $paynum = $1;
+
+my $cust_pay = qsearchs('cust_pay', { 'paynum' => $paynum } );
+my $custnum = $cust_pay->custnum;
+
+foreach my $cust_bill_pay ( $cust_pay->cust_bill_pay ) {
+ my $error = $cust_bill_pay->delete;
+ errorpage($error) if $error;
+}
+
+</%init>
diff --git a/httemplate/misc/unexpire_pkg.cgi b/httemplate/misc/unexpire_pkg.cgi
new file mode 100755
index 0000000..4450255
--- /dev/null
+++ b/httemplate/misc/unexpire_pkg.cgi
@@ -0,0 +1,17 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect(popurl(2). "view/cust_main.cgi?".$cust_pkg->getfield('custnum')) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Cancel customer package later');
+
+my ($pkgnum) = $cgi->keywords;
+my $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
+my $error = "No package $pkgnum" unless $cust_pkg;
+
+$error ||= $cust_pkg->unexpire;
+
+</%init>
diff --git a/httemplate/misc/unprovision.cgi b/httemplate/misc/unprovision.cgi
new file mode 100755
index 0000000..4ab15fd
--- /dev/null
+++ b/httemplate/misc/unprovision.cgi
@@ -0,0 +1,26 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect(popurl(2)."view/cust_main.cgi?$custnum") %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unprovision customer service');
+
+#untaint svcnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+
+#my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$svcnum});
+#die "Unknown svcnum!" unless $svc_acct;
+
+my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+die "Unknown svcnum!" unless $cust_svc;
+
+my $custnum = $cust_svc->cust_pkg->custnum;
+
+my $error = $cust_svc->cancel;
+
+</%init>
diff --git a/httemplate/misc/unsusp_pkg.cgi b/httemplate/misc/unsusp_pkg.cgi
new file mode 100755
index 0000000..b350693
--- /dev/null
+++ b/httemplate/misc/unsusp_pkg.cgi
@@ -0,0 +1,20 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect(popurl(2). "view/cust_main.cgi?".$cust_pkg->getfield('custnum')) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unsuspend customer package');
+
+#untaint pkgnum
+my ($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal pkgnum";
+my $pkgnum = $1;
+
+my $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+
+my $error = $cust_pkg->unsuspend;
+
+</%init>
diff --git a/httemplate/misc/unvoid-cust_pay_void.cgi b/httemplate/misc/unvoid-cust_pay_void.cgi
new file mode 100755
index 0000000..91fe1c2
--- /dev/null
+++ b/httemplate/misc/unvoid-cust_pay_void.cgi
@@ -0,0 +1,21 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unvoid');
+
+#untaint paynum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal paynum";
+my $paynum = $1;
+
+my $cust_pay_void = qsearchs('cust_pay_void', { 'paynum' => $paynum } );
+my $custnum = $cust_pay_void->custnum;
+
+my $error = $cust_pay_void->unvoid;
+
+</%init>
diff --git a/httemplate/misc/upload-batch.cgi b/httemplate/misc/upload-batch.cgi
new file mode 100644
index 0000000..d1a84fd
--- /dev/null
+++ b/httemplate/misc/upload-batch.cgi
@@ -0,0 +1,36 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+ <% include('/elements/header.html','Batch results upload successful') %>
+ <% include('/elements/footer.html') %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Process batches');
+
+my $error;
+
+my $fh = $cgi->upload('batch_results');
+$error = 'No file uploaded' unless defined($fh);
+
+unless ( $error ) {
+
+ $cgi->param('batchnum') =~ /^(\d+)$/;
+ my $batchnum = $1;
+
+ my $pay_batch = qsearchs( 'pay_batch', { 'batchnum' => $batchnum } );
+ if ( ! $pay_batch ) {
+ $error = "batchnum $batchnum not found";
+ } elsif ( $pay_batch->status ne 'I' ) {
+ $error = "batch $batchnum is not in transit";
+ } else {
+ $error = $pay_batch->import_results(
+ 'filehandle' => $fh,
+ 'format' => $cgi->param('format'),
+ );
+ }
+
+}
+
+</%init>
diff --git a/httemplate/misc/void-cust_pay.cgi b/httemplate/misc/void-cust_pay.cgi
new file mode 100755
index 0000000..7b484e9
--- /dev/null
+++ b/httemplate/misc/void-cust_pay.cgi
@@ -0,0 +1,26 @@
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+%}
+<%init>
+
+#untaint paynum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal paynum";
+my $paynum = $1;
+
+my $cust_pay = qsearchs('cust_pay',{'paynum'=>$paynum});
+
+my $right = 'Regular void';
+$right = 'Credit card void' if $cust_pay->payby eq 'CARD';
+$right = 'Echeck void' if $cust_pay->payby eq 'CHEK';
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right($right);
+
+my $custnum = $cust_pay->custnum;
+
+my $error = $cust_pay->void;
+
+</%init>
diff --git a/httemplate/misc/whois.cgi b/httemplate/misc/whois.cgi
new file mode 100644
index 0000000..533b2d7
--- /dev/null
+++ b/httemplate/misc/whois.cgi
@@ -0,0 +1,33 @@
+<% include("/elements/header.html","Whois $domain", menubar(
+ ( $custnum
+ ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ : ()
+ ),
+ "View this domain (#$svcnum)" => "${p}view/svc_domain.cgi?$svcnum",
+)) %>
+
+<PRE><% $whois %></PRE>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+my $svcnum = $cgi->param('svcnum');
+my $custnum = $cgi->param('custnum');
+my $domain = $cgi->param('domain');
+
+my $display_custnum;
+if ( $custnum ) {
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+ $display_custnum = $cust_main->display_custnum;
+}
+
+my $whois = eval { whois($domain) };
+ if ( $@ ) {
+ ( $whois = $@ ) =~ s/ at \/.*Net\/Whois\/Raw\.pm line \d+.*$//s;
+ } else {
+ $whois =~ s/^\n+//;
+ }
+
+</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-address_standardize.html b/httemplate/misc/xmlhttp-cust_main-address_standardize.html
new file mode 100644
index 0000000..72fa4a4
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_main-address_standardize.html
@@ -0,0 +1,89 @@
+<% objToJson($return) %>
+<%init>
+
+my $DEBUG = 0;
+
+my $conf = new FS::Conf;
+
+my $sub = $cgi->param('sub');
+
+my $return = {};
+
+if ( $sub eq 'address_standardize' ) {
+
+ my %arg = $cgi->param('arg');
+ $return = \%arg;
+ warn join('', map "$_: $arg{$_}\n", keys %arg )
+ if $DEBUG;
+
+ my $userid = $conf->config('usps_webtools-userid');
+ my $password = $conf->config('usps_webtools-password');
+
+ if ( length($userid) && length($password) ) {
+
+ my $verifier = Business::US::USPS::WebTools::AddressStandardization->new( {
+ UserID => $userid, #$ENV{USPS_WEBTOOLS_USERID},
+ Password => $password, #$ENV{USPS_WEBTOOLS_PASSWORD},
+ #Testing => 1,
+ } );
+
+ foreach my $pre ( '', 'ship_' ) {
+
+ my($zip5, $zip4) = split('-',$arg{$pre.'zip'});
+
+ my %usps_args = (
+ FirmName => $arg{$pre.'company'},
+ Address2 => $arg{$pre.'address1'},
+ Address1 => $arg{$pre.'address2'},
+ City => $arg{$pre.'city'},
+ State => $arg{$pre.'state'},
+ Zip5 => $zip5,
+ Zip4 => $zip4,
+ );
+ warn join('', map "$_: $usps_args{$_}\n", keys %usps_args )
+ if $DEBUG;
+
+ my $hash = $verifier->verify_address( %usps_args );
+
+ warn $verifier->response
+ if $DEBUG;
+
+ unless ( $verifier->is_error ) {
+
+ $return = {
+ %$return,
+ "new_$pre".'company' => $hash->{FirmName},
+ "new_$pre".'address1' => $hash->{Address2},
+ "new_$pre".'address2' => $hash->{Address1},
+ "new_$pre".'city' => $hash->{City},
+ "new_$pre".'state' => $hash->{State},
+ "new_$pre".'zip' => $hash->{Zip5}. '-'. $hash->{Zip4},
+ };
+
+ my @fields = (qw( company address1 address2 city state zip )); #hmm
+
+ my $changed =
+ scalar( grep { $return->{$pre.$_} ne $return->{"new_$pre$_"} }
+ @fields
+ )
+ ? 1 : 0;
+
+ $return->{$pre.'address_standardized'} = $changed;
+
+ } else {
+
+ $return->{$pre.'error'} = "USPS WebTools error: ".
+ $verifier->{error}{description};
+
+
+ }
+
+ }
+
+ }
+
+ $return;
+
+}
+
+</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-search.cgi b/httemplate/misc/xmlhttp-cust_main-search.cgi
new file mode 100644
index 0000000..26e68b5
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_main-search.cgi
@@ -0,0 +1,36 @@
+% if ( $sub eq 'custnum_search' ) {
+%
+% my $custnum = $cgi->param('arg');
+% my $cust_main = '';
+% if ( $custnum <= 2147483647 ) {
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'custnum' => $custnum },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+% }
+% if ( ! $cust_main ) {
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'agent_custid' => $custnum },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+% }
+%
+"<% $cust_main ? $cust_main->name : '' %>"
+%
+% } elsif ( $sub eq 'smart_search' ) {
+%
+% my $string = $cgi->param('arg');
+% my @cust_main = smart_search( 'search' => $string );
+% my $return = [ map [ $_->custnum, $_->name ], @cust_main ];
+%
+<% objToJson($return) %>
+% }
+<%init>
+
+my $conf = new FS::Conf;
+
+my $sub = $cgi->param('sub');
+
+</%init>
diff --git a/httemplate/misc/xmlrpc.cgi b/httemplate/misc/xmlrpc.cgi
new file mode 100644
index 0000000..1d0383f
--- /dev/null
+++ b/httemplate/misc/xmlrpc.cgi
@@ -0,0 +1,18 @@
+%
+%
+% my $request_xml = $cgi->param('POSTDATA');
+%
+% #$r->log_error($request_xml);
+%
+% my $fsxmlrpc = new FS::XMLRPC;
+% my ($error, $response_xml) = $fsxmlrpc->serve($request_xml);
+%
+% #$r->log_error($error) if $error;
+%
+% http_header('Content-Type' => 'text/xml',
+% 'Content-Length' => length($response_xml));
+%
+% print $response_xml;
+%
+%
+
diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html
new file mode 100644
index 0000000..9661516
--- /dev/null
+++ b/httemplate/pref/pref-process.html
@@ -0,0 +1,58 @@
+% my $error = '';
+%
+% my $access_user;
+% if ( grep { $cgi->param($_) !~ /^\s*$/ }
+% qw(_password new_password new_password2)
+% ) {
+%
+% $access_user = qsearchs( 'access_user', {
+% 'username' => getotaker,
+% '_password' => $cgi->param('_password'),
+% } );
+%
+% $error = 'Current password incorrect; password not changed'
+% unless $access_user;
+%
+% $error ||= "New passwords don't match"
+% unless $cgi->param('new_password') eq $cgi->param('new_password2');
+%
+% $error ||= "No new password entered"
+% unless length($cgi->param('new_password'));
+%
+% $access_user->_password($cgi->param('new_password')) unless $error;
+%
+% } else {
+%
+% $access_user = $FS::CurrentUser::CurrentUser;
+%
+% }
+%
+% my %param = $access_user->options;
+%
+% #XXX autogen
+% my @paramlist = qw( menu_position
+% email_address
+% vonage-fromnumber vonage-username vonage-password
+% show_pkgnum show_db_profile save_db_profile
+% height width availHeight availWidth colorDepth
+% );
+%
+% foreach (@paramlist) {
+% scalar($cgi->param($_)) =~ /^[,.\-\@\w]*$/ && next;
+% $error ||= "Illegal value for parameter $_";
+% last;
+% }
+%
+% foreach (@paramlist) {
+% $param{$_} = scalar($cgi->param($_));
+% }
+%
+% $error ||= $access_user->replace( \%param );
+%
+% if ( $error ) {
+% $cgi->param('error', $error);
+% print $cgi->redirect(popurl(1). "pref.html?". $cgi->query_string );
+% } else {
+<% include('/elements/header.html', 'Preferences updated') %>
+<% include('/elements/footer.html') %>
+% }
diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html
new file mode 100644
index 0000000..57e22b3
--- /dev/null
+++ b/httemplate/pref/pref.html
@@ -0,0 +1,124 @@
+<% include('/elements/header.html', 'Preferences for '. getotaker ) %>
+
+<FORM METHOD="POST" NAME="pref_form" ACTION="pref-process.html">
+
+<% include('/elements/error.html') %>
+
+
+Change password (leave blank for no change)
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TH ALIGN="right">Current password: </TH>
+ <TD><INPUT TYPE="password" NAME="_password"></TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">New password: </TH>
+ <TD><INPUT TYPE="password" NAME="new_password"></TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">Re-enter new password: </TH>
+ <TD><INPUT TYPE="password" NAME="new_password2"></TD>
+ </TR>
+
+</TABLE>
+<BR>
+
+
+Interface
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TH>Menu location: </TH>
+ <TD>
+ <INPUT TYPE="radio" NAME="menu_position" VALUE="left" onClick="document.images['menu_example'].src='../images/menu-left-example.png';" <% $menu_position eq 'left' ? ' CHECKED' : ''%>> Left<BR>
+ <INPUT TYPE="radio" NAME="menu_position" VALUE="top"onClick="document.images['menu_example'].src='../images/menu-top-example.png';" <% $menu_position eq 'top' ? ' CHECKED' : ''%>> Top <BR>
+ </TD>
+ <TD><IMG NAME="menu_example" SRC="../images/menu-<% $menu_position %>-example.png"></TD>
+ </TR>
+
+</TABLE>
+<BR>
+
+
+Email Address
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TH>Email Address(es) (comma separated) </TH>
+ <TD>
+ <TD><INPUT TYPE="text" NAME="email_address" VALUE="<% $email_address %>">
+ </TD>
+ </TR>
+
+</TABLE>
+<BR>
+
+
+Development
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TH>Show internal package numbers: </TH>
+ <TD><INPUT TYPE="checkbox" NAME="show_pkgnum" VALUE="1" <% $curuser->option('show_pkgnum') ? 'CHECKED' : '' %>></TD>
+ </TR>
+ <TR>
+ <TH>Show database profiling (when available): </TH>
+ <TD><INPUT TYPE="checkbox" NAME="show_db_profile" VALUE="1" <% $curuser->option('show_db_profile') ? 'CHECKED' : '' %>></TD>
+ </TR>
+ <TR>
+ <TH>Save database profiling logs (when available): </TH>
+ <TD><INPUT TYPE="checkbox" NAME="save_db_profile" VALUE="1" <% $curuser->option('save_db_profile') ? 'CHECKED' : '' %>></TD>
+ </TR>
+
+</TABLE>
+<BR>
+
+
+Vonage integration (see <a href="https://secure.click2callu.com/">Click2Call</a>)
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TH ALIGN="right">Vonage phone number</TH>
+ <TD><INPUT TYPE="text" NAME="vonage-fromnumber" VALUE="<% $curuser->option('vonage-fromnumber') %>"></TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">Vonage username</TH>
+ <TD><INPUT TYPE="text" NAME="vonage-username" VALUE="<% $curuser->option('vonage-username') %>"></TD>
+ </TR>
+
+ <TR>
+ <TH ALIGN="right">Vonage password</TH>
+ <TD><INPUT TYPE="password" NAME="vonage-password" VALUE="<% $curuser->option('vonage-password') %>"></TD>
+ </TR>
+
+</TABLE>
+<BR>
+
+
+% foreach my $prop (qw( height width availHeight availWidth colorDepth )) {
+ <INPUT TYPE="hidden" NAME="<% $prop %>" VALUE="">
+ <SCRIPT TYPE="text/javascript">
+ document.pref_form.<% $prop %>.value = screen.<% $prop %>;
+ </script>
+% }
+
+<INPUT TYPE="submit" VALUE="Update preferences">
+
+<% include('/elements/footer.html') %>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+# XSS via your own preferences? seems unlikely, but nice try anyway...
+( $curuser->option('menu_position') || 'left' )
+ =~ /^(\w+)$/ or die "illegal menu_position";
+my $menu_position = $1;
+( $curuser->option('email_address') )
+ =~ /^([,\w\@.]*)$/ or die "illegal email_address"; #too late
+my $email_address = $1;
+
+</%init>
diff --git a/httemplate/search/cdr.html b/httemplate/search/cdr.html
new file mode 100644
index 0000000..852eeba
--- /dev/null
+++ b/httemplate/search/cdr.html
@@ -0,0 +1,159 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+ 'name' => 'call detail records',
+
+ 'query' => { 'table' => 'cdr',
+ 'hashref' => $hashref,
+ 'extra_sql' => $qsearch,
+ 'order_by' => 'ORDER BY calldate',
+ },
+ 'count_query' => $count_query,
+ 'header' => [
+ '', # checkbox column
+ fields('cdr'), #XXX fill in some nice names
+ ],
+ 'fields' => [
+ sub {
+ return '' unless $edit_data;
+ $areboxes = 1;
+ my $cdr = shift;
+ my $acctid = $cdr->acctid;
+ qq!<INPUT NAME="acctid$acctid" TYPE="checkbox" VALUE="1">!;
+ },
+ fields('cdr'), #XXX fill in some pretty-print
+ #processing, etc.
+ ],
+
+ 'html_form' => qq!<FORM NAME="cdrForm" ACTION="$p/misc/cdr.cgi" METHOD="POST">!,
+ #false laziness w/queue.html
+ 'html_foot' => sub {
+ if ( $areboxes ) {
+ '<BR><INPUT TYPE="button" VALUE="select all" onClick="setAll(true)">'.
+ '<INPUT TYPE="button" VALUE="unselect all" onClick="setAll(false)">'.
+ qq!<BR><INPUT TYPE="submit" NAME="action" VALUE="reprocess selected" onClick="return confirm('Are you sure you want to reprocess the selected CDRs?')">!.
+ qq!<INPUT TYPE="submit" NAME="action" VALUE="delete selected" onClick="return confirm('Are you sure you want to delete the selected CDRs?')"><BR>!.
+ '<SCRIPT TYPE="text/javascript">'.
+ ' function setAll(setTo) { '.
+ ' theForm = document.cdrForm;'.
+ ' for (i=0,n=theForm.elements.length;i<n;i++)'.
+ ' if (theForm.elements[i].name.indexOf("acctid") != -1)'.
+ ' theForm.elements[i].checked = setTo;'.
+ ' }'.
+ '</SCRIPT>';
+ } else {
+ '';
+ }
+ },
+
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+
+my $edit_data = $FS::CurrentUser::CurrentUser->access_right('Edit rating data');
+
+my $areboxes = 0;
+
+my $title = 'Call Detail Records';
+my $hashref = {};
+
+#process params for CDR search, populate $hashref...
+# and fixup $count_query
+
+my @search = ();
+
+###
+# freesidestatus
+###
+
+if ( $cgi->param('freesidestatus') eq 'NULL' ) {
+
+ my $title = "Unprocessed $title";
+ $hashref->{'freesidestatus'} = ''; # Record.pm will take care of it
+ push @search, "( freesidestatus IS NULL OR freesidestatus = '' )";
+
+} elsif ( $cgi->param('freesidestatus') =~ /^([\w ]+)$/ ) {
+
+ my $title = "Processed $title";
+ $hashref->{'freesidestatus'} = $1;
+ push @search, "freesidestatus = '$1'";
+
+}
+
+###
+# dates
+###
+
+my $str2time_sql = str2time_sql;
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "$str2time_sql calldate) >= $beginning ",
+ "$str2time_sql calldate) <= $ending";
+
+###
+# duration / billsec
+###
+
+push @search, FS::UI::Web::parse_lt_gt($cgi, 'duration');
+push @search, FS::UI::Web::parse_lt_gt($cgi, 'billsec');
+
+###
+# src/dest/charged_party
+###
+
+my @qsearch = @search;
+
+if ( $cgi->param('src') =~ /^\s*([\d\-\+\ ]+)\s*$/ ) {
+ ( my $src = $1 ) =~ s/\D//g;
+ $hashref->{'src'} = $src;
+ push @search, "src = '$src'";
+}
+
+if ( $cgi->param('dst') =~ /^\s*([\d\-\+ ]+)\s*$/ ) {
+ ( my $dst = $1 ) =~ s/\D//g;
+ $hashref->{'dst'} = $dst;
+ push @search, "dst = '$dst'";
+}
+
+if ( $cgi->param('charged_party') =~ /^\s*([\d\-\+\ ]+)\s*$/ ) {
+ ( my $charged_party = $1 ) =~ s/\D//g;
+ #$hashref->{'charged_party'} = $charged_party;
+ #push @search, "charged_party = '$charged_party'";
+ #XXX countrycode
+ push @search, " ( charged_party = '$charged_party'
+ OR charged_party = '1$charged_party' ) ";
+ push @qsearch, " ( charged_party = '$charged_party'
+ OR charged_party = '1$charged_party' ) ";
+}
+
+###
+# cdrbatch
+###
+
+if ( $cgi->param('cdrbatch') ne '__ALL__' ) {
+ if ( $cgi->param('cdrbatch') eq '' ) {
+ my $search = "( cdrbatch IS NULL OR cdrbatch = '' )";
+ push @qsearch, $search;
+ push @search, $search;
+ } else {
+ $hashref->{cdrbatch} = $cgi->param('cdrbatch');
+ push @search, 'cdrbatch = '. dbh->quote($cgi->param('cdrbatch'));
+ }
+}
+
+###
+# finish it up
+###
+
+my $search = join(' AND ', @search);
+$search = "WHERE $search" if $search;
+
+my $count_query = "SELECT COUNT(*) FROM cdr $search";
+
+my $qsearch = join(' AND ', @qsearch);
+$qsearch = ( scalar(keys %$hashref) ? ' AND ' : ' WHERE ' ) . $qsearch
+ if $qsearch;
+
+</%init>
diff --git a/httemplate/search/cust_bill.html b/httemplate/search/cust_bill.html
new file mode 100755
index 0000000..6f440db
--- /dev/null
+++ b/httemplate/search/cust_bill.html
@@ -0,0 +1,251 @@
+<% include( 'elements/search.html',
+ 'title' => 'Invoice Search Results',
+ 'html_init' => $html_init,
+ 'menubar' => $menubar,
+ 'name' => 'invoices',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'count_addl' => $count_addl,
+ 'redirect' => $link,
+ 'header' => [ 'Invoice #',
+ 'Balance',
+ 'Net Amount',
+ 'Gross Amount',
+ 'Date',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ 'invnum',
+ sub { sprintf($money_char.'%.2f', shift->get('owed') ) },
+ sub { sprintf($money_char.'%.2f', shift->get('net') ) },
+ sub { sprintf($money_char.'%.2f', shift->charged ) },
+ sub { time2str('%b %d %Y', shift->_date ) },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'align' => 'rrrr'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ $link,
+ $link,
+ $link,
+ $link,
+ $link,
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+
+
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List invoices');
+
+my $join_cust_main = 'LEFT JOIN cust_main USING ( custnum )';
+#here is the agent virtualization
+my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my( $count_query, $sql_query );
+my $count_addl = '';
+#my $distinct = '';
+my %search;
+
+if ( $cgi->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/ ) {
+
+ $count_query =
+ "SELECT COUNT(*) FROM cust_bill $join_cust_main".
+ " WHERE invnum = $2 AND $agentnums_sql"; #agent virtualization
+ $sql_query = {
+ 'table' => 'cust_bill',
+ 'addl_from' => $join_cust_main,
+ 'hashref' => { 'invnum' => $2 },
+ #'select' => '*',
+ 'extra_sql' => " AND $agentnums_sql", #agent virtualization
+ };
+
+} else {
+
+ #some false laziness w/cust_bill::re_X
+ my @where;
+ my $orderby = 'ORDER BY cust_bill._date';
+
+ if ( $cgi->param('beginning')
+ && $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/ ) {
+ $search{'begin'} = str2time($1);
+ }
+ if ( $cgi->param('ending')
+ && $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/ ) {
+ $search{'end'} = str2time($1) + 86399;
+ }
+
+ if ( $cgi->param('begin') =~ /^(\d+)$/ ) {
+ $search{'begin'} = $1;
+ }
+ if ( $cgi->param('end') =~ /^(\d+)$/ ) {
+ $search{'end'} = $1;
+ }
+
+ if ( $cgi->param('invnum_min') =~ /^\s*(\d+)\s*$/ ) {
+ $search{'invnum_min'} = $1;
+ }
+ if ( $cgi->param('invnum_max') =~ /^\s*(\d+)\s*$/ ) {
+ $search{'invnum_max'} = $1;
+ }
+
+ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $search{'agentnum'} = $1;
+ }
+
+ $search{'open'} = 1 if $cgi->param('open');
+ $search{'net'} = 1 if $cgi->param('net' );
+
+ my($query) = $cgi->keywords;
+ if ( $query =~ /^(OPEN(\d*)_)?(invnum|date|custnum)$/ ) {
+ $search{'open'} = 1 if $1;
+ ($search{'days'}, my $field) = ($2, $3);
+ $field = "_date" if $field eq 'date';
+ $orderby = "ORDER BY cust_bill.$field";
+ }
+
+ if ( $cgi->param('newest_percust') ) {
+ $search{'newest_percust'} = 1;
+ $count_query = "SELECT COUNT(DISTINCT cust_bill.custnum), 'N/A', 'N/A'";
+ }
+
+ my $extra_sql = ' WHERE '. FS::cust_bill->search_sql( \%search );
+
+ unless ( $count_query ) {
+ $count_query = 'SELECT COUNT(*), '. join(', ',
+ map "SUM($_)",
+ ( 'charged',
+ FS::cust_bill->net_sql,
+ FS::cust_bill->owed_sql,
+ )
+ );
+ $count_addl = [ '$%.2f invoiced (gross)',
+ '$%.2f invoiced (net)',
+ '$%.2f outstanding balance',
+ ];
+ }
+ $count_query .= " FROM cust_bill $join_cust_main $extra_sql";
+
+ $sql_query = {
+ 'table' => 'cust_bill',
+ 'addl_from' => $join_cust_main,
+ 'hashref' => {},
+ #'select' => "$distinct ". join(', ',
+ 'select' => join(', ',
+ 'cust_bill.*',
+ #( map "cust_main.$_", qw(custnum last first company) ),
+ 'cust_main.custnum as cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ FS::cust_bill->owed_sql. ' AS owed',
+ FS::cust_bill->net_sql. ' AS net',
+ ),
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $orderby,
+ };
+
+}
+
+my $link = [ "${p}view/cust_bill.cgi?", 'invnum', ];
+my $clink = sub {
+ my $cust_bill = shift;
+ $cust_bill->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'custnum' ]
+ : '';
+};
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $html_init = join("\n", map {
+ ( my $action = $_ ) =~ s/_$//;
+ include('/elements/progress-init.html',
+ $_.'form',
+ [ keys %search ],
+ "../misc/${_}invoices.cgi",
+ { 'message' => "Invoices re-${action}ed" }, #would be nice to show the number of them, but...
+ $_, #key
+ ),
+ qq!<FORM NAME="${_}form">!,
+ ( map qq!<INPUT TYPE="hidden" NAME="$_" VALUE="$search{$_}">!, keys %search ),
+ qq!</FORM>!
+} qw( print_ email_ fax_ ftp_ spool_ ) ).
+
+'<SCRIPT TYPE="text/javascript">
+
+function confirm_print_process() {
+ if ( ! confirm("Are you sure you want to reprint these invoices?") ) {
+ return;
+ }
+ print_process();
+}
+function confirm_email_process() {
+ if ( ! confirm("Are you sure you want to re-email these invoices?") ) {
+ return;
+ }
+ email_process();
+}
+function confirm_fax_process() {
+ if ( ! confirm("Are you sure you want to re-fax these invoices?") ) {
+ return;
+ }
+ fax_process();
+}
+function confirm_ftp_process() {
+ if ( ! confirm("Are you sure you want to re-FTP these invoices?") ) {
+ return;
+ }
+ ftp_process();
+}
+function confirm_spool_process() {
+ if ( ! confirm("Are you sure you want to re-spool these invoices?") ) {
+ return;
+ }
+ spool_process();
+}
+
+</SCRIPT>';
+
+my $menubar = [];
+
+if ( $FS::CurrentUser::CurrentUser->access_right('Resend invoices') ) {
+
+ push @$menubar, 'Print these invoices' =>
+ "javascript:confirm_print_process()",
+ 'Email these invoices' =>
+ "javascript:confirm_email_process()";
+
+ push @$menubar, 'Fax these invoices' =>
+ "javascript:confirm_fax_process()"
+ if $conf->exists('hylafax');
+
+ push @$menubar, 'FTP these invoices' =>
+ "javascript:confirm_ftp_process()"
+ if $conf->exists('cust_bill-ftpformat');
+
+ push @$menubar, 'Spool these invoices' =>
+ "javascript:confirm_spool_process()"
+ if $conf->exists('cust_bill-spoolformat');
+
+}
+
+</%init>
diff --git a/httemplate/search/cust_bill_event.cgi b/httemplate/search/cust_bill_event.cgi
new file mode 100644
index 0000000..ff4168d
--- /dev/null
+++ b/httemplate/search/cust_bill_event.cgi
@@ -0,0 +1,166 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+ 'html_init' => $html_init,
+ 'menubar' => $menubar,
+ 'name' => 'billing events',
+ 'query' => $sql_query,
+ 'count_query' => $count_sql,
+ 'header' => [ 'Event',
+ 'Date',
+ 'Status',
+ #'Inv #', 'Inv Date', 'Cust #',
+ 'Invoice',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ 'event',
+ sub { time2str("%b %d %Y %T", $_[0]->_date) },
+ sub {
+ #my $cust_bill_event = shift;
+ my $status = $_[0]->status;
+ $status .= ': '.$_[0]->statustext
+ if $_[0]->statustext;
+ $status;
+ },
+ sub {
+ #my $cust_bill_event = shift;
+ 'Invoice #'. $_[0]->invnum.
+ ' ('.
+ time2str("%D", $_[0]->cust_bill_date).
+ ')';
+ },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'align' => 'lrlr'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ '',
+ '',
+ sub {
+ my $part_bill_event = shift;
+ my $template = $part_bill_event->templatename;
+ $template .= '-' if $template;
+ [ "${p}view/cust_bill.cgi?$template", 'invnum'];
+ },
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Billing event reports')
+ or $curuser->access_right('View customer billing events')
+ && $cgi->param('invnum') =~ /^(\d+)$/;
+
+my $title = $cgi->param('failed')
+ ? 'Failed invoice events'
+ : 'Invoice events';
+
+my %search = ();
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $search{agentnum} = $1;
+}
+
+($search{beginning}, $search{ending})
+ = FS::UI::Web::parse_beginning_ending($cgi);
+
+if ( $cgi->param('failed') ) {
+ $search{failed} = '1';
+}
+
+if ( $cgi->param('part_bill_event.payby') =~ /^(\w+)$/ ) {
+ $search{payby} = $1;
+}
+
+if ( $cgi->param('invnum') =~ /^(\d+)$/ ) {
+ $search{invnum} = $1;
+}
+
+my $where = 'WHERE '. FS::cust_bill_event->search_sql( \%search );
+
+my $join = 'LEFT JOIN part_bill_event USING ( eventpart ) '.
+ 'LEFT JOIN cust_bill USING ( invnum ) '.
+ 'LEFT JOIN cust_main USING ( custnum ) ';
+
+my $sql_query = {
+ 'table' => 'cust_bill_event',
+ 'select' => join(', ',
+ 'cust_bill_event.*',
+ 'part_bill_event.event',
+ 'cust_bill.custnum',
+ 'cust_bill._date AS cust_bill_date',
+ 'cust_main.custnum AS cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'hashref' => {},
+ 'extra_sql' => "$where ORDER BY _date ASC",
+ 'addl_from' => $join,
+};
+
+my $count_sql = "SELECT COUNT(*) FROM cust_bill_event $join $where";
+
+my $conf = new FS::Conf;
+
+my $html_init = '
+ <FONT SIZE="+1">Invoice events are the deprecated, old-style actions taken o
+n open invoices. See Reports-&gt;Billing events-&gt;Billing events for current event reports.</FONT><BR><BR>';
+
+$html_init .= join("\n", map {
+ ( my $action = $_ ) =~ s/_$//;
+ include('/elements/progress-init.html',
+ $_.'form',
+ [ keys(%search) ],
+ "../misc/${_}invoice_events.cgi",
+ { 'message' => "Invoices re-${action}ed" }, #would be nice to show the number of them, but...
+ $_, #key
+ ),
+ qq!<FORM NAME="${_}form">!,
+ qq!<INPUT TYPE="hidden" NAME="action" VALUE="$_">!, #not used though
+ (map {qq!<INPUT TYPE="hidden" NAME="$_" VALUE="$search{$_}">!} keys(%search)),
+ qq!</FORM>!
+} qw( print_ email_ fax_ ) );
+
+my $menubar = [];
+
+if ( $curuser->access_right('Resend invoices') ) {
+
+ push @$menubar, 'Re-print these events' =>
+ "javascript:print_process()",
+ 'Re-email these events' =>
+ "javascript:email_process()",
+ ;
+
+ push @$menubar, 'Re-fax these events' =>
+ "javascript:fax_process()"
+ if $conf->exists('hylafax');
+
+}
+
+my $link_cust = sub {
+ my $cust_bill_event = shift;
+ $cust_bill_event->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'custnum' ]
+ : '';
+};
+
+</%init>
diff --git a/httemplate/search/cust_bill_event.html b/httemplate/search/cust_bill_event.html
new file mode 100755
index 0000000..0f84a55
--- /dev/null
+++ b/httemplate/search/cust_bill_event.html
@@ -0,0 +1,67 @@
+<% include(
+ '/elements/header.html',
+ ( $cgi->param('failed') ? 'Failed invoice events' : 'Invoice events' ),
+ )
+%>
+
+ <FONT SIZE="+1">Invoice events are the deprecated, old-style actions taken
+ on open invoices. See Reports-&gt;Billing events-&gt;Billing events for current event reports.</FONT><BR><BR>
+
+ <FORM ACTION="cust_bill_event.cgi" METHOD="GET">
+ <INPUT TYPE="hidden" NAME="failed" VALUE="<% $cgi->param('failed') ? 1 : 0 %>">
+ <TABLE>
+
+ <% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %>
+
+ <!--<TR>
+ <TD ALIGN="right">Customer type</TD>
+ <TD><SELECT MULTIPLE NAME="perhaps_payby">
+ <OPTION SELECTED VALUE="CARD">Credit card (automatic)
+ <OPTION SELECTED VALUE="CHEK">E-check (automatic)
+ <OPTION SELECTED VALUE="LECB">Phone bill billing
+ <OPTION SELECTED VALUE="BILL">Billing
+ <OPTION SELECTED VALUE="DCRD">Credit card (on-demand)
+ <OPTION SELECTED VALUE="DCHK">E-check (on-demand)
+ </TD>
+ </TR>
+ -->
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+ <!--
+ <TR>
+ <TD ALIGN="right">Events: </TD>
+ <TD>
+ <SELECT NAME="eventpart">
+ <OPTION SELECTED VALUE=""><% $cgi->param('failed') ? '(all failed events)' : '(all events)' %>
+% #foreach my $part_bill_event ( qsearch( 'part_bill_event', {} ) ) {
+% #}
+
+ </SELECT>
+ </TD>
+ </TR>
+ -->
+ <TR>
+ <TD ALIGN="right">Events for payment type: </TD>
+ <TD>
+ <SELECT NAME="part_bill_event.payby">
+ <OPTION SELECTED VALUE="">(all)
+ <OPTION VALUE="CARD">Credit card (automatic)
+ <OPTION VALUE="BILL">Billing
+ <OPTION VALUE="CHEK">Electronic check (automatic)
+ <OPTION VALUE="DCRD">Credit card (on-demand)
+ <OPTION VALUE="DCHK">Electronic check (on-demand)
+ <OPTION VALUE="LECB">Phone bill billing
+ <OPTION VALUE="COMP">Complimentary
+ </SELECT>
+ </TD>
+ </TR>
+ </TABLE>
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
+ </FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Billing event reports');
+
+</%init>
diff --git a/httemplate/search/cust_bill_pay.html b/httemplate/search/cust_bill_pay.html
new file mode 100644
index 0000000..3c390e7
--- /dev/null
+++ b/httemplate/search/cust_bill_pay.html
@@ -0,0 +1,131 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+ 'name' => 'net payments',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ '$%.2f total paid (net)', ],
+ 'header' => [ 'Net applied',
+ 'to Invoice',
+ 'Payment',
+ 'By',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ sub { $money_char. sprintf('%.2f', shift->amount ) },
+ sub { my $cbp = shift;
+ '#'.$cbp->invnum. ' '.
+ time2str('%b %d %Y', $cbp->cust_bill_date ).
+ " ($money_char".
+ sprintf('%.2f', $cbp->cust_bill_amount).
+ ")"
+ },
+ sub { my $cbp = shift;
+ $cbp->cust_pay->payby_payinfo_pretty. ' '.
+ time2str('%b %d %Y', $cbp->_date ).
+ " ($money_char".
+ sprintf('%.2f', $cbp->cust_pay_paid ).
+ ")"
+ },
+ sub { shift->cust_pay->otaker },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'align' => 'rrrl'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ $cust_bill_link,
+ $cust_pay_link,
+ '',
+ ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $title = 'Net Payment Search Results';
+
+my @search = ();
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "agentnum = $1";
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "unknown agentnum $1" unless $agent;
+ $title = $agent->agent. " $title";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "cust_bill._date >= $beginning ",
+ "cust_bill._date <= $ending";
+
+#here is the agent virtualization
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $where = 'WHERE '. join(' AND ', @search);
+#
+my $count_query = 'SELECT COUNT(*), SUM(amount)
+ FROM cust_bill_pay
+ LEFT JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum ) '.
+ $where;
+
+my $sql_query = {
+ 'table' => 'cust_bill_pay',
+ 'select' => join(', ',
+ 'cust_bill_pay.*',
+ 'cust_pay.paid AS cust_pay_paid',
+ 'cust_bill._date AS cust_bill_date',
+ #'cust_bill.charged AS cust_bill_charged',
+ 'cust_pay.custnum AS custnum',
+ 'cust_main.custnum AS cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'hashref' => {},
+ 'extra_sql' => $where,
+ 'addl_from' => 'LEFT JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_pay USING ( paynum )
+ LEFT JOIN cust_main ON ( cust_bill.custnum = cust_main.custnum )',
+};
+
+my $cust_bill_link = sub {
+ my $cust_bill_pay = shift;
+ $cust_bill_pay->invnum
+ ? [ "${p}view/cust_bill.cgi?", 'invnum' ]
+ : '';
+};
+
+my $cust_pay_link = sub {
+ my $cust_bill_pay = shift;
+ $cust_bill_pay->paynum
+ ? [ "${p}view/cust_pay.html?paynum=", 'paynum' ]
+ : '';
+};
+
+my $cust_link = sub {
+ my $cust_credit_bill = shift;
+ $cust_credit_bill->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'cust_main_custnum' ]
+ : '';
+};
+
+</%init>
diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi
new file mode 100644
index 0000000..89901ac
--- /dev/null
+++ b/httemplate/search/cust_bill_pkg.cgi
@@ -0,0 +1,335 @@
+<% include( 'elements/search.html',
+ 'title' => 'Line items',
+ 'name' => 'line items',
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ $money_char. '%.2f total', ],
+ 'header' => [
+ '#',
+ 'Description',
+ 'Setup charge',
+ 'Recurring charge',
+ 'Invoice',
+ 'Date',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ 'billpkgnum',
+ sub { $_[0]->pkgnum > 0
+ ? $_[0]->get('pkg')
+ : $_[0]->get('itemdesc')
+ },
+ #strikethrough or "N/A ($amount)" or something these when
+ # they're not applicable to pkg_tax search
+ sub { sprintf($money_char.'%.2f', shift->setup ) },
+ sub { sprintf($money_char.'%.2f', shift->recur ) },
+ 'invnum',
+ sub { time2str('%b %d %Y', shift->_date ) },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [
+ '',
+ '',
+ '',
+ '',
+ $ilink,
+ $ilink,
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rlrrrc'.FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+#here is the agent virtualization
+my $agentnums_sql =
+ $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my @where = ( $agentnums_sql );
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @where, "_date >= $beginning",
+ "_date <= $ending";
+
+push @where , " payby != 'COMP' "
+ unless $cgi->param('include_comp_cust');
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.agentnum = $1";
+}
+
+if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
+ if ( $1 == 0 ) {
+ push @where, "classnum IS NULL";
+ } else {
+ push @where, "classnum = $1";
+ }
+}
+
+#sub _where {
+# my $table = shift;
+# my $prefix = @_ ? shift : '';
+# "
+# ( cust_main_county.county = $table.${prefix}.county
+# OR ( cust_main_county.county IS NULL AND $table.${prefix}.county = '' )
+# OR ( cust_main_county.county = '' AND $table.${prefix}.county IS NULL)
+# OR ( cust_main_county.county IS NULL AND $table.${prefix}.county IS NULL)
+# )
+# AND ( cust_main_county.state = $table.${prefix}.state
+# OR ( cust_main_county.state IS NULL AND $table.${prefix}.state = '' )
+# OR ( cust_main_county.state = '' AND $table.${prefix}.state IS NULL )
+# OR ( cust_main_county.state IS NULL AND $table.${prefix}.state IS NULL )
+# )
+# AND cust_main_county.country = $table.${prefix}.country
+# ";
+#
+#}
+
+if ( $cgi->param('out') ) {
+
+ my ( $loc_sql, @param ) = FS::cust_pkg->location_sql( 'ornull' => 1 );
+ while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution
+ $loc_sql =~ s/\?/'cust_main_county.'.shift(@param)/e;
+ }
+
+ $loc_sql =~ s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g
+ if $cgi->param('istax');
+
+ push @where, "
+ 0 = (
+ SELECT COUNT(*) FROM cust_main_county
+ WHERE cust_main_county.tax > 0
+ AND $loc_sql
+ )
+ ";
+
+ #not linked to by anything, but useful for debugging "out of taxable region"
+ if ( grep $cgi->param($_), qw( county state country ) ) {
+
+ my %ph = map { $_ => dbh->quote( $cgi->param($_) ) }
+ qw( county state country );
+
+ my ( $loc_sql, @param ) = FS::cust_pkg->location_sql;
+ while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution
+ $loc_sql =~ s/\?/$ph{shift(@param)}/e;
+ }
+
+ push @where, $loc_sql;
+
+ }
+
+} elsif ( $cgi->param('country' ) ) {
+
+ my %ph = map { $_ => dbh->quote( $cgi->param($_) ) }
+ qw( county state country );
+
+ my ( $loc_sql, @param ) = FS::cust_pkg->location_sql;
+ while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution
+ $loc_sql =~ s/\?/$ph{shift(@param)}/e;
+ }
+
+ push @where, $loc_sql;
+
+ if ( $cgi->param('istax') ) {
+ if ( $cgi->param('taxname') ) {
+ push @where, 'itemdesc = '. dbh->quote( $cgi->param('taxname') );
+ #} elsif ( $cgi->param('taxnameNULL') {
+ } else {
+ push @where, "( itemdesc IS NULL OR itemdesc = '' OR itemdesc = 'Tax' )";
+ }
+ } elsif ( $cgi->param('nottax') ) {
+ #what can we usefully do with "taxname" ???? look up a class???
+ } else {
+ #warn "neither nottax nor istax parameters specified";
+ }
+
+ push @where, ' taxclass = '. dbh->quote( $cgi->param('taxclass') )
+ if $cgi->param('taxclass')
+ && ! $cgi->param('istax'); #no part_pkg.taxclass in this case
+ #(should we save a taxclass or a link to taxnum
+ # in cust_bill_pkg or something like
+ # cust_bill_pkg_tax_location?)
+
+ if ( $cgi->param('taxclassNULL') ) {
+
+ my %hash = ( 'country' => scalar($cgi->param('country')) );
+ foreach (qw( state county )) {
+ $hash{$_} = scalar($cgi->param($_)) if $cgi->param($_);
+ }
+ my $cust_main_county = qsearchs('cust_main_county', \%hash);
+ die "unknown base region for empty taxclass" unless $cust_main_county;
+
+ my $same_sql = $cust_main_county->sql_taxclass_sameregion;
+ push @where, $same_sql if $same_sql;
+
+ }
+
+}
+
+if ($cgi->param('itemdesc')) {
+ if ($cgi->param('itemdesc') eq 'Tax') {
+ push @where, "(itemdesc='Tax' OR itemdesc is null)";
+ }else{
+ push @where, 'itemdesc='. dbh->quote($cgi->param('itemdesc'));
+ }
+}
+push @where, 'cust_bill_pkg.pkgnum != 0' if $cgi->param('nottax');
+push @where, 'cust_bill_pkg.pkgnum = 0' if $cgi->param('istax');
+
+push @where, " tax = 'Y' " if $cgi->param('cust_tax');
+
+my $count_query;
+if ( $cgi->param('pkg_tax') ) {
+
+ $count_query =
+ "SELECT COUNT(*),
+ SUM(
+ ( CASE WHEN part_pkg.setuptax = 'Y'
+ THEN cust_bill_pkg.setup
+ ELSE 0
+ END
+ )
+ +
+ ( CASE WHEN part_pkg.recurtax = 'Y'
+ THEN cust_bill_pkg.recur
+ ELSE 0
+ END
+ )
+ )
+ ";
+
+ push @where, "( ( part_pkg.setuptax = 'Y' AND cust_bill_pkg.setup > 0 )
+ OR ( part_pkg.recurtax = 'Y' AND cust_bill_pkg.recur > 0 ) )",
+ "( tax != 'Y' OR tax IS NULL )";
+
+} elsif ( $cgi->param('taxable') ) {
+
+ my $setup_taxable = "(
+ CASE WHEN part_pkg.setuptax = 'Y'
+ THEN 0
+ ELSE cust_bill_pkg.setup
+ END
+ )";
+
+ my $recur_taxable = "(
+ CASE WHEN part_pkg.recurtax = 'Y'
+ THEN 0
+ ELSE cust_bill_pkg.recur
+ END
+ )";
+
+ my $exempt = "(
+ SELECT COALESCE( SUM(amount), 0 ) FROM cust_tax_exempt_pkg
+ WHERE cust_tax_exempt_pkg.billpkgnum = cust_bill_pkg.billpkgnum
+ )";
+
+ $count_query =
+ "SELECT COUNT(*), SUM( $setup_taxable + $recur_taxable - $exempt )";
+
+ push @where,
+ #not tax-exempt package (setup or recur)
+ "(
+ ( ( part_pkg.setuptax != 'Y' OR part_pkg.setuptax IS NULL )
+ AND cust_bill_pkg.setup > 0 )
+ OR
+ ( ( part_pkg.recurtax != 'Y' OR part_pkg.recurtax IS NULL )
+ AND cust_bill_pkg.recur > 0 )
+ )",
+ #not a tax_exempt customer
+ "( tax != 'Y' OR tax IS NULL )";
+ #not covered in full by a monthly tax exemption (texas tax)
+ "0 < ( $setup_taxable + $recur_taxable - $exempt )",
+
+} else {
+
+ $count_query =
+ "SELECT COUNT(*), SUM(cust_bill_pkg.setup + cust_bill_pkg.recur)";
+
+}
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+my $join_cust = ' JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum ) ';
+
+
+my $join_pkg;
+if ( $cgi->param('nottax') ) {
+
+ $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart ) ';
+ $join_pkg .= ' LEFT JOIN cust_location USING ( locationnum ) '
+ if $conf->exists('tax-pkg_address');
+
+} elsif ( $cgi->param('istax') ) {
+
+ #false laziness w/report_tax.cgi $taxfromwhere
+ if ( $conf->exists('tax-pkg_address') ) {
+ $join_pkg .= ' LEFT JOIN cust_bill_pkg_tax_location USING ( billpkgnum )
+ LEFT JOIN cust_location USING ( locationnum ) ';
+
+ #quelle kludge, false laziness w/report_tax.cgi
+ $where =~ s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g;
+ }
+
+} else {
+
+ #die?
+ warn "neiether nottax nor istax parameters specified";
+ #same as before?
+ $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart ) ';
+
+}
+
+$count_query .= " FROM cust_bill_pkg $join_cust $join_pkg $where";
+
+my @select = (
+ 'cust_bill_pkg.*',
+ 'cust_bill._date',
+ );
+push @select, 'part_pkg.pkg' unless $cgi->param('istax');
+push @select, 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields();
+
+my $query = {
+ 'table' => 'cust_bill_pkg',
+ 'addl_from' => "$join_cust $join_pkg",
+ 'hashref' => {},
+ 'select' => join(', ', @select ),
+ 'extra_sql' => $where,
+ 'order_by' => 'ORDER BY _date, billpkgnum',
+};
+
+my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/search/cust_credit.html b/httemplate/search/cust_credit.html
new file mode 100755
index 0000000..9a14dce
--- /dev/null
+++ b/httemplate/search/cust_credit.html
@@ -0,0 +1,104 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+ 'name' => 'credits',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ '$%.2f total credited (gross)', ],
+ #'redirect' => $link,
+ 'header' => [ 'Amount',
+ 'Date',
+ 'By',
+ 'Reason',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ #'crednum',
+ sub { sprintf('$%.2f', shift->amount ) },
+ sub { time2str('%b %d %Y', shift->_date ) },
+ 'otaker',
+ 'reason',
+ \&FS::UI::Web::cust_fields,
+ ],
+ #'align' => 'rrrllll',
+ 'align' => 'rrll'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ '',
+ '',
+ '',
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $title = 'Credit Search Results';
+#my( $count_query, $sql_query );
+
+my @search = ();
+
+if ( $cgi->param('otaker') && $cgi->param('otaker') =~ /^([\w\.\-]+)$/ ) {
+ push @search, "cust_credit.otaker = '$1'";
+}
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "agentnum = $1";
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "unknown agentnum $1" unless $agent;
+ $title = $agent->agent. " $title";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "_date >= $beginning ",
+ "_date <= $ending";
+
+push @search, FS::UI::Web::parse_lt_gt($cgi, 'amount' );
+
+#here is the agent virtualization
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $where = 'WHERE '. join(' AND ', @search);
+
+my $count_query = 'SELECT COUNT(*), SUM(amount) '.
+ 'FROM cust_credit LEFT JOIN cust_main USING ( custnum ) '.
+ $where;
+
+my $sql_query = {
+ 'table' => 'cust_credit',
+ 'select' => join(', ',
+ 'cust_credit.*',
+ 'cust_main.custnum as cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'hashref' => {},
+ 'extra_sql' => $where,
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+};
+
+ my $clink = sub {
+ my $cust_bill = shift;
+ $cust_bill->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'custnum' ]
+ : '';
+ };
+
+</%init>
diff --git a/httemplate/search/cust_credit_bill.html b/httemplate/search/cust_credit_bill.html
new file mode 100644
index 0000000..818e603
--- /dev/null
+++ b/httemplate/search/cust_credit_bill.html
@@ -0,0 +1,135 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+ 'name' => 'net credits',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ '$%.2f total credited (net)', ],
+ 'header' => [ 'Net applied',
+ 'to Invoice',
+ 'Credit',
+ 'By',
+ 'Reason',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ sub { $money_char. sprintf('%.2f', shift->amount ) },
+ sub { my $ccb = shift;
+ '#'.$ccb->invnum. ' '.
+ time2str('%b %d %Y', $ccb->cust_bill_date ).
+ " ($money_char".
+ sprintf('%.2f', $ccb->cust_bill_amount).
+ ")"
+ },
+ sub { my $ccb = shift;
+ time2str('%b %d %Y', $ccb->_date ).
+ " ($money_char".
+ sprintf('%.2f', $ccb->cust_credit_amount ).
+ ")"
+ },
+ sub { shift->cust_credit->otaker },
+ sub { shift->cust_credit->reason },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'align' => 'rrrll'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ $cust_bill_link,
+ '',
+ '',
+ '',
+ ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $title = 'Net Credit Search Results';
+
+my @search = ();
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "agentnum = $1";
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "unknown agentnum $1" unless $agent;
+ $title = $agent->agent. " $title";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "cust_bill._date >= $beginning ",
+ "cust_bill._date <= $ending";
+
+#here is the agent virtualization
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $where = 'WHERE '. join(' AND ', @search);
+#
+my $count_query = 'SELECT COUNT(*), SUM(amount)
+ FROM cust_credit_bill
+ LEFT JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum ) '.
+ $where;
+
+my $sql_query = {
+ 'table' => 'cust_credit_bill',
+ 'select' => join(', ',
+ 'cust_credit_bill.*',
+ 'cust_credit.amount AS cust_credit_amount',
+ 'cust_bill._date AS cust_bill_date',
+ 'cust_bill.charged AS cust_bill_charged',
+ 'cust_credit.custnum AS custnum',
+ 'cust_main.custnum AS cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'hashref' => {},
+ 'extra_sql' => $where,
+ 'addl_from' => 'LEFT JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_credit USING ( crednum )
+ LEFT JOIN cust_main ON ( cust_bill.custnum = cust_main.custnum )',
+};
+
+my $cust_bill_link = sub {
+ my $cust_credit_bill = shift;
+ $cust_credit_bill->invnum
+ ? [ "${p}view/cust_bill.cgi?", 'invnum' ]
+ : '';
+};
+
+#my $cust_credit_link = sub {
+# my $cust_credit_bill = shift;
+# $cust_credit_bill->crednum
+# ? [ "${p}view/cust_credit.cgi?", 'crednum' ]
+# : '';
+#};
+
+my $cust_link = sub {
+ my $cust_credit_bill = shift;
+ $cust_credit_bill->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'cust_main_custnum' ]
+ : '';
+};
+
+</%init>
diff --git a/httemplate/search/cust_credit_refund.html b/httemplate/search/cust_credit_refund.html
new file mode 100644
index 0000000..d9abe2e
--- /dev/null
+++ b/httemplate/search/cust_credit_refund.html
@@ -0,0 +1,130 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+ 'name' => 'net refunds',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ '$%.2f total refunded (net)', ],
+ 'header' => [ 'Net applied',
+ 'to Credit',
+ 'Refund',
+ 'By',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ sub { $money_char. sprintf('%.2f', shift->amount ) },
+ sub { my $ccr = shift;
+ '#'.$ccr->crednum. ' '.
+ time2str('%b %d %Y', $ccr->cust_credit_date ).
+ " ($money_char".
+ sprintf('%.2f', $ccr->cust_credit_amount).
+ ")"
+ },
+ sub { my $ccr = shift;
+ time2str('%b %d %Y', $ccr->_date ).
+ " ($money_char".
+ sprintf('%.2f', $ccr->cust_refund_refund ).
+ ")"
+ },
+ sub { shift->cust_refund->otaker },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'align' => 'rrrl'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ '',
+ '',
+ '',
+ ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $title = 'Net Refund Search Results';
+
+my @search = ();
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "agentnum = $1";
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "unknown agentnum $1" unless $agent;
+ $title = $agent->agent. " $title";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "cust_credit._date >= $beginning ",
+ "cust_credit._date <= $ending";
+
+#here is the agent virtualization
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $where = 'WHERE '. join(' AND ', @search);
+#
+my $count_query = 'SELECT COUNT(*), SUM(cust_credit_refund.amount)
+ FROM cust_credit_refund
+ LEFT JOIN cust_credit USING ( crednum )
+ LEFT JOIN cust_main USING ( custnum ) '.
+ $where;
+
+my $sql_query = {
+ 'table' => 'cust_credit_refund',
+ 'select' => join(', ',
+ 'cust_credit_refund.*',
+ 'cust_refund.refund AS cust_refund_refund',
+ 'cust_credit._date AS cust_credit_date',
+ 'cust_credit.amount AS cust_credit_amnount',
+ 'cust_refund.custnum AS custnum',
+ 'cust_main.custnum AS cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'hashref' => {},
+ 'extra_sql' => $where,
+ 'addl_from' => 'LEFT JOIN cust_credit USING ( crednum )
+ LEFT JOIN cust_refund USING ( refundnum )
+ LEFT JOIN cust_main ON ( cust_credit.custnum = cust_main.custnum )',
+};
+
+#my $cust_credit_link = sub {
+# my $cust_credit_refund = shift;
+# $cust_credit_refund->crednum
+# ? [ "${p}view/cust_credit.cgi?", 'credum' ]
+# : '';
+#};
+
+#my $cust_refund_link = sub {
+# my $cust_credit_refund = shift;
+# $cust_credit_refund->refundnum
+# ? [ "${p}view/cust_refund.cgi?", 'refundnum' ]
+# : '';
+#};
+
+my $cust_link = sub {
+ my $cust_credit_refund = shift;
+ $cust_credit_refund->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'cust_main_custnum' ]
+ : '';
+};
+
+</%init>
diff --git a/httemplate/search/cust_event.html b/httemplate/search/cust_event.html
new file mode 100644
index 0000000..d55b5c6
--- /dev/null
+++ b/httemplate/search/cust_event.html
@@ -0,0 +1,285 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+ 'html_init' => $html_init,
+ 'menubar' => $menubar,
+ 'name' => 'billing events',
+ 'query' => $sql_query,
+ 'count_query' => $count_sql,
+ 'header' => [ 'Event',
+ 'Date',
+ 'Status',
+ 'Trigger',
+ #'Inv #', 'Inv Date', 'Cust #',
+ #'Invoice',
+
+ FS::UI::Web::cust_header(), #'cust_main_custnum',
+ ],
+ 'fields' => [
+ 'event',
+ sub { time2str("%b %d %Y %T", $_[0]->_date) },
+ $status_sub,
+ $trigger_sub,
+ #sub {
+ # #my $cust_event = shift;
+ # 'Invoice #'. $_[0]->invnum.
+ # ' ('.
+ # time2str("%D", $_[0]->cust_bill_date).
+ # ')';
+ # },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'align' => 'lrll'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ '',
+ '',
+ $trigger_link,
+ #sub {
+ # my $part_event = shift;
+ # #XXX
+ # my $template = $part_event->templatename;
+ # $template .= '-' if $template;
+ # [ "${p}view/cust_bill.cgi?$template", 'invnum'];
+ #},
+
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ #'',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ #'',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%once>
+
+my $status_sub = sub {
+ my $cust_event = shift;
+
+ my $status = $cust_event->status;
+ $status .= ': '.$cust_event->statustext
+ if $cust_event->statustext;
+
+ my $part_event = $cust_event->part_event;
+
+ if ( $part_event->eventtable eq 'cust_bill' && $part_event->templatename ) {
+ my $alt_templatename = $part_event->templatename;
+ my $alt_link = "$alt_templatename-". $cust_event->tablenum;
+
+ my $conf = new FS::Conf;
+ my $cust_bill = $cust_event->cust_X;
+
+ $status .= qq{
+ ( <A HREF="${p}view/cust_bill.cgi?$alt_link">view</A>
+ | <A HREF="${p}view/cust_bill-pdf.cgi?$alt_link.pdf">view
+ typeset</A>
+ | <A HREF="${p}misc/print-invoice.cgi?$alt_link">re-print</A>
+ };
+
+ if ( grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ) {
+ $status .= qq{
+ | <A HREF="${p}misc/email-invoice.cgi?$alt_link">re-email</A>
+ };
+ }
+
+ if ( $conf->exists('hylafax') && length($cust_bill->cust_main->fax) ) {
+ $status .= qq{
+ | <A HREF="${p}misc/fax-invoice.cgi?$alt_link">re-fax</A>
+ }
+ }
+
+ $status .= ' ) ';
+
+ }
+
+ $status;
+};
+
+my $trigger_sub = sub {
+ my $cust_event = shift;
+ my $eventtable = $cust_event->eventtable;
+ my $label = FS::part_event->eventtable_labels->{$eventtable};
+ #if ( $eventtable eq 'cust_pkg' || $eventtable eq 'cust_bill' ) {
+ "$label #". $cust_event->tablenum;
+ #} else {
+ # $label;
+ #}
+};
+
+my $trigger_link = sub {
+ my $cust_event = shift;
+ my $eventtable = $cust_event->eventtable;
+ if ( $eventtable eq 'cust_pkg' ) {
+ my $custnum = $cust_event->cust_main_custnum;
+ [ "${p}view/cust_main.cgi?$custnum#cust_pkg", 'tablenum' ];
+ } else {
+ [ "${p}view/$eventtable.cgi?", 'tablenum' ];
+ }
+};
+
+</%once>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Billing event reports')
+ or $curuser->access_right('View customer billing events')
+ && ( $cgi->param('custnum') =~ /^(\d+)$/
+ || $cgi->param('invnum') =~ /^(\d+)$/
+ || $cgi->param('pkgnum') =~ /^(\d+)$/
+ );
+
+
+my $title = $cgi->param('failed')
+ ? 'Failed billing events'
+ : 'Billing events';
+
+my @search = ();
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "cust_main.agentnum = $1";
+ #my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ #die "unknown agentnum $1" unless $agent;
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "cust_event._date >= $beginning",
+ "cust_event._date <= $ending";
+
+if ( $cgi->param('failed') ) {
+ push @search, "statustext != ''",
+ "statustext IS NOT NULL",
+ "statustext != 'N/A'";
+}
+
+#if ( $cgi->param('part_event.payby') =~ /^(\w+)$/ ) {
+# push @search, "part_event.payby = '$1'";
+#}
+
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ push @search, "cust_main.custnum = '$1'";
+}
+if ( $cgi->param('invnum') =~ /^(\d+)$/ ) {
+ push @search, "part_event.eventtable = 'cust_bill'",
+ "tablenum = '$1'";
+}
+if ( $cgi->param('pkgnum') =~ /^(\d+)$/ ) {
+ push @search, "part_event.eventtable = 'cust_pkg'",
+ "tablenum = '$1'";
+}
+
+#here is the agent virtualization
+push @search, $curuser->agentnums_sql( 'table' => 'cust_main' );
+
+my $where = 'WHERE '. join(' AND ', @search );
+
+my $join = "
+ JOIN part_event USING ( eventpart )
+ LEFT JOIN cust_bill ON ( eventtable = 'cust_bill' AND tablenum = invnum )
+ LEFT JOIN cust_pkg ON ( eventtable = 'cust_pkg' AND tablenum = pkgnum )
+ LEFT JOIN cust_main ON ( ( eventtable = 'cust_main' AND tablenum = cust_main.custnum )
+ OR ( eventtable = 'cust_bill' AND cust_bill.custnum = cust_main.custnum )
+ OR ( eventtable = 'cust_pkg' AND cust_pkg.custnum = cust_main.custnum )
+ )
+";
+ #'LEFT JOIN cust_main USING ( custnum ) ';
+
+my $sql_query = {
+ 'table' => 'cust_event',
+ 'select' => join(', ',
+ 'cust_event.*',
+ 'part_event.*',
+ #'cust_bill.custnum',
+ #'cust_bill._date AS cust_bill_date',
+ 'cust_main.custnum AS cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'hashref' => {},
+ 'extra_sql' => "$where ORDER BY _date ASC",
+ 'addl_from' => $join,
+};
+
+my $count_sql = "SELECT COUNT(*) FROM cust_event $join $where";
+
+my $conf = new FS::Conf;
+
+my $failed = $cgi->param('failed');
+
+my $html_init = join("\n", map {
+ ( my $action = $_ ) =~ s/_$//;
+ include('/elements/progress-init.html',
+ $_.'form',
+ [ 'action', 'beginning', 'ending', 'failed' ],
+ "../misc/${_}events.cgi",
+ { 'message' => "Invoices re-${action}ed" }, #would be nice to show the number of them, but...
+ $_, #key
+ ),
+ qq!<FORM NAME="${_}form">!,
+ qq!<INPUT TYPE="hidden" NAME="action" VALUE="$_">!, #not used though
+ qq!<INPUT TYPE="hidden" NAME="beginning" VALUE="$beginning">!,
+ qq!<INPUT TYPE="hidden" NAME="ending" VALUE="$ending">!,
+ qq!<INPUT TYPE="hidden" NAME="failed" VALUE="$failed">!,
+ qq!</FORM>!
+} qw( print_ email_ fax_ ) ).
+
+'<SCRIPT TYPE="text/javascript">
+
+function confirm_print_process() {
+ if ( ! confirm("Are you sure you want to reprint these invoices?") ) {
+ return;
+ }
+ print_process();
+}
+function confirm_email_process() {
+ if ( ! confirm("Are you sure you want to re-email these invoices?") ) {
+ return;
+ }
+ email_process();
+}
+function confirm_fax_process() {
+ if ( ! confirm("Are you sure you want to re-fax these invoices?") ) {
+ return;
+ }
+ fax_process();
+}
+
+</SCRIPT>';
+
+my $menubar = [];
+
+if ( $curuser->access_right('Resend invoices') ) {
+
+ push @$menubar, 'Re-print these events' =>
+ "javascript:confirm_print_process()",
+ 'Re-email these events' =>
+ "javascript:confirm_email_process()",
+ ;
+
+ push @$menubar, 'Re-fax these events' =>
+ "javascript:confirm_fax_process()"
+ if $conf->exists('hylafax');
+
+}
+
+my $link_cust = sub {
+ my $cust_event = shift;
+ $cust_event->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'cust_main_custnum' ]
+ : '';
+};
+
+</%init>
diff --git a/httemplate/search/cust_main-otaker.cgi b/httemplate/search/cust_main-otaker.cgi
new file mode 100755
index 0000000..0c252e4
--- /dev/null
+++ b/httemplate/search/cust_main-otaker.cgi
@@ -0,0 +1,31 @@
+<% include('/elements/header.html', 'Customer Search' ) %>
+
+<FORM ACTION="cust_main.cgi" METHOD="GET">
+
+Search for <B>Order taker</B>:
+ <INPUT TYPE="hidden" NAME="otaker_on" VALUE="TRUE">
+% my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_main")
+% or die dbh->errstr;
+% $sth->execute() or die $sth->errstr;
+% #my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+%
+
+<SELECT NAME="otaker">
+% my $otaker; while ( $otaker = $sth->fetchrow_arrayref ) {
+
+ <OPTION><% $otaker->[0] %>
+% }
+
+</SELECT>
+
+<P><INPUT TYPE="submit" VALUE="Search">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/search/cust_main-zip.html b/httemplate/search/cust_main-zip.html
new file mode 100644
index 0000000..56df924
--- /dev/null
+++ b/httemplate/search/cust_main-zip.html
@@ -0,0 +1,99 @@
+<% include( 'elements/search.html',
+ 'title' => 'Zip code Search Results',
+ 'name' => 'zip codes',
+ 'query' => $sql_query,
+ 'count_query' => $count_sql,
+ 'header' => [ 'Zip code', 'Customers', ],
+ #'fields' => [ 'zip', 'num_cust', ],
+ 'links' => [ '', sub { 'somewhere'; } ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List zip codes');
+
+# XXX link to customers
+
+my @where = ();
+
+# select status
+
+if ( $cgi->param('status') =~ /^(prospect|uncancel|active|susp|cancel)$/ ) {
+ my $method = $1.'_sql';
+ push @where, FS::cust_main->$method();
+}
+
+# select agent
+# XXX this needs to be virtualized by agent too (like lots of stuff)
+
+my $agentnum = '';
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agentnum = $1;
+ push @where, "cust_main.agentnum = $agentnum";
+}
+my $where = scalar(@where) ? 'WHERE '. join(' AND ', @where) : '';
+
+# bill zip vs ship zip
+
+sub fieldorempty {
+ my $field = shift;
+ "CASE WHEN $field IS NULL THEN '' ELSE $field END";
+}
+
+sub strip_plus4 {
+ my $field = shift;
+ "CASE WHEN $field is NULL
+ THEN ''
+ ELSE CASE WHEN $field LIKE '_____-____'
+ THEN SUBSTRING($field FROM 1 FOR 5)
+ ELSE $field
+ END
+ END";
+}
+
+my( $zip, $czip);
+if ( $cgi->param('column') eq 'ship_zip' ) {
+
+ my $casewhen_noship =
+ "CASE WHEN ( ship_last IS NULL OR ship_last = '' ) THEN ";
+
+ $czip = "$casewhen_noship zip ELSE ship_zip END";
+
+ if ( $cgi->param('ignore_plus4') ) {
+ $zip = $casewhen_noship. strip_plus4('zip').
+ " ELSE ". strip_plus4('ship_zip'). ' END';
+
+ } else {
+ $zip = $casewhen_noship. fieldorempty('zip').
+ " ELSE ". fieldorempty('ship_zip'). ' END';
+ }
+
+} else {
+
+ $czip = 'zip';
+
+ if ( $cgi->param('ignore_plus4') ) {
+ $zip = strip_plus4('zip');
+ } else {
+ $zip = fieldorempty('zip');
+ }
+
+}
+
+# construct the queries and send 'em off
+
+my $sql_query =
+ "SELECT $zip AS zipcode,
+ COUNT(*) AS num_cust
+ FROM cust_main
+ $where
+ GROUP BY zipcode
+ ORDER BY num_cust DESC
+ ";
+
+my $count_sql = "select count(distinct $czip) from cust_main $where";
+
+# XXX should link...
+
+</%init>
diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi
new file mode 100755
index 0000000..36e4374
--- /dev/null
+++ b/httemplate/search/cust_main.cgi
@@ -0,0 +1,736 @@
+%die "access denied"
+% unless $FS::CurrentUser::CurrentUser->access_right('List customers');
+%
+%my $conf = new FS::Conf;
+%my $maxrecords = $conf->config('maxsearchrecordsperpage');
+%
+%#my $cache;
+%
+%#my $monsterjoin = <<END;
+%#cust_main left outer join (
+%# ( cust_pkg left outer join part_pkg using(pkgpart)
+%# ) left outer join (
+%# (
+%# (
+%# ( cust_svc left outer join part_svc using (svcpart)
+%# ) left outer join svc_acct using (svcnum)
+%# ) left outer join svc_domain using(svcnum)
+%# ) left outer join svc_forward using(svcnum)
+%# ) using (pkgnum)
+%#) using (custnum)
+%#END
+%
+%#my $monsterjoin = <<END;
+%#cust_main left outer join (
+%# ( cust_pkg left outer join part_pkg using(pkgpart)
+%# ) left outer join (
+%# (
+%# (
+%# ( cust_svc left outer join part_svc using (svcpart)
+%# ) left outer join (
+%# svc_acct left outer join (
+%# select svcnum, domain, catchall from svc_domain
+%# ) as svc_acct_domsvc (
+%# svc_acct_svcnum, svc_acct_domain, svc_acct_catchall
+%# ) on svc_acct.domsvc = svc_acct_domsvc.svc_acct_svcnum
+%# ) using (svcnum)
+%# ) left outer join svc_domain using(svcnum)
+%# ) left outer join svc_forward using(svcnum)
+%# ) using (pkgnum)
+%#) using (custnum)
+%#END
+%
+%my $limit = '';
+%$limit .= "LIMIT $maxrecords" if $maxrecords;
+%
+%my $offset = $cgi->param('offset') || 0;
+%$limit .= " OFFSET $offset" if $offset;
+%
+%my $total = 0;
+%
+%my(@cust_main, $sortby, $orderby);
+%my @select = ();
+%my @addl_headers = ();
+%my @addl_cols = ();
+%if ( $cgi->param('browse')
+% || $cgi->param('otaker_on')
+% || $cgi->param('agentnum_on')
+%) {
+%
+% my %search = ();
+%
+% if ( $cgi->param('browse') ) {
+% my $query = $cgi->param('browse');
+% if ( $query eq 'custnum' ) {
+% if ( $conf->exists('cust_main-default_agent_custid') ) {
+% $sortby=\*display_custnum_sort;
+% $orderby = "ORDER BY CASE WHEN agent_custid IS NOT NULL AND agent_custid != '' THEN CAST(agent_custid AS BIGINT) ELSE custnum END";
+% } else {
+% $sortby=\*custnum_sort;
+% $orderby = "ORDER BY custnum";
+% }
+% } elsif ( $query eq 'last' ) {
+% $sortby=\*last_sort;
+% $orderby = "ORDER BY LOWER(last || ' ' || first)";
+% } elsif ( $query eq 'company' ) {
+% $sortby=\*company_sort;
+% $orderby = "ORDER BY LOWER(company || ' ' || last || ' ' || first )";
+% } elsif ( $query eq 'tickets' ) {
+% $sortby = \*tickets_sort;
+% $orderby = "ORDER BY tickets DESC";
+% push @select, FS::TicketSystem->sql_num_customer_tickets. " as tickets";
+% push @addl_headers, 'Tickets';
+% push @addl_cols, 'tickets';
+% } else {
+% die "unknown browse field $query";
+% }
+% } else {
+% $sortby = \*last_sort; #??
+% $orderby = "ORDER BY LOWER(last || ' ' || first)"; #??
+% }
+%
+% if ( $cgi->param('otaker_on') ) {
+% die "access denied"
+% unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+% $cgi->param('otaker') =~ /^(\w{1,32})$/ or errorpage("Illegal otaker");
+% $search{otaker} = $1;
+% } elsif ( $cgi->param('agentnum_on') ) {
+% $cgi->param('agentnum') =~ /^(\d+)$/ or errorpage("Illegal agentnum");
+% $search{agentnum} = $1;
+%# } else {
+%# die "unknown query...";
+% }
+%
+% my @qual = ();
+%
+% my $ncancelled = '';
+%
+% if ( $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
+% || ( $conf->exists('hidecancelledcustomers')
+% && ! $cgi->param('showcancelledcustomers') )
+% ) {
+% #grep { $_->ncancelled_pkgs || ! $_->all_pkgs }
+% push @qual, FS::cust_main->uncancel_sql;
+%
+% }
+%
+% push @qual, FS::cust_main->cancel_sql if $cgi->param('cancelled');
+% push @qual, FS::cust_main->prospect_sql if $cgi->param('prospect');
+% push @qual, FS::cust_main->active_sql if $cgi->param('active');
+% push @qual, FS::cust_main->inactive_sql if $cgi->param('inactive');
+% push @qual, FS::cust_main->susp_sql if $cgi->param('suspended');
+%
+% #EWWWWWW
+% my $qual = join(' AND ',
+% map { "$_ = ". dbh->quote($search{$_}) } keys %search );
+%
+% my $addl_qual = join(' AND ', @qual);
+%
+% #here is the agent virtualization
+% $addl_qual .= ( $addl_qual ? ' AND ' : '' ).
+% $FS::CurrentUser::CurrentUser->agentnums_sql;
+%
+% if ( $addl_qual ) {
+% $qual .= ' AND ' if $qual;
+% $qual .= $addl_qual;
+% }
+%
+% $qual = " WHERE $qual" if $qual;
+% my $statement = "SELECT COUNT(*) FROM cust_main $qual";
+% my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
+% $sth->execute or die "Error executing \"$statement\": ". $sth->errstr;
+%
+% $total = $sth->fetchrow_arrayref->[0];
+%
+% if ( $addl_qual ) {
+% if ( %search ) {
+% $addl_qual = " AND $addl_qual";
+% } else {
+% $addl_qual = " WHERE $addl_qual";
+% }
+% }
+%
+% my $select;
+% if ( @select ) {
+% $select = 'cust_main.*, '. join (', ', @select);
+% } else {
+% $select = '*';
+% }
+%
+% @cust_main = qsearch('cust_main', \%search, $select,
+% "$addl_qual $orderby $limit" );
+%
+%# foreach my $cust_main ( @just_cust_main ) {
+%#
+%# my @one_cust_main;
+%# $FS::Record::DEBUG=1;
+%# ( $cache, @one_cust_main ) = jsearch(
+%# "$monsterjoin",
+%# { 'custnum' => $cust_main->custnum },
+%# '',
+%# '',
+%# 'cust_main',
+%# 'custnum',
+%# );
+%# push @cust_main, @one_cust_main;
+%# }
+%
+%} else {
+% @cust_main=();
+% $sortby = \*last_sort;
+%
+% push @cust_main, @{&custnumsearch}
+% if $cgi->param('custnum_on') && $cgi->param('custnum_text');
+% push @cust_main, @{&cardsearch}
+% if $cgi->param('card_on') && $cgi->param('card');
+% push @cust_main, @{&lastsearch}
+% if $cgi->param('last_on') && $cgi->param('last_text');
+% push @cust_main, @{&companysearch}
+% if $cgi->param('company_on') && $cgi->param('company_text');
+% push @cust_main, @{&address2search}
+% if $cgi->param('address2_on') && $cgi->param('address2_text');
+% push @cust_main, @{&phonesearch}
+% if $cgi->param('phone_on') && $cgi->param('phone_text');
+% push @cust_main, @{&referralsearch}
+% if $cgi->param('referral_custnum');
+%
+% if ( $cgi->param('company_on') && $cgi->param('company_text') ) {
+% $sortby = \*company_sort;
+% push @cust_main, @{&companysearch};
+% }
+%
+% if ( $cgi->param('search_cust') ) {
+% $sortby = \*company_sort;
+% $orderby = "ORDER BY LOWER(company || ' ' || last || ' ' || first )";
+% push @cust_main, smart_search( 'search' => $cgi->param('search_cust') );
+% }
+%
+% @cust_main = grep { $_->ncancelled_pkgs || ! $_->all_pkgs } @cust_main
+% if ! $cgi->param('cancelled')
+% && (
+% $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
+% || ( $conf->exists('hidecancelledcustomers')
+% && ! $cgi->param('showcancelledcustomers') )
+% );
+%
+% my %saw = ();
+% @cust_main = grep { !$saw{$_->custnum}++ } @cust_main;
+%}
+%
+%my %all_pkgs;
+%if ( $conf->exists('hidecancelledpackages' ) ) {
+% %all_pkgs = map { $_->custnum => [ $_->ncancelled_pkgs ] } @cust_main;
+%} else {
+% %all_pkgs = map { $_->custnum => [ $_->all_pkgs ] } @cust_main;
+%}
+%#%all_pkgs = ();
+%
+%if ( scalar(@cust_main) == 1 && ! $cgi->param('referral_custnum') ) {
+% if ( $cgi->param('quickpay') eq 'yes' ) {
+% print $cgi->redirect(popurl(2). "edit/cust_pay.cgi?quickpay=yes;custnum=". $cust_main[0]->custnum);
+% } else {
+% print $cgi->redirect(popurl(2). "view/cust_main.cgi?". $cust_main[0]->custnum);
+% }
+% #exit;
+%} elsif ( scalar(@cust_main) == 0 ) {
+%
+
+<!-- mason kludge -->
+%
+% errorpage("No matching customers found!");
+%} else {
+%
+
+<% include('/elements/header.html', "Customer Search Results", '' ) %>
+% $total ||= scalar(@cust_main);
+
+
+ <% $total %> matching customers found
+
+% my $pager = include( '/elements/pager.html',
+% 'offset' => $offset,
+% 'num_rows' => scalar(@cust_main),
+% 'total' => $total,
+% 'maxrecords' => $maxrecords,
+% );
+%
+% unless ( $cgi->param('cancelled') ) {
+% if ( $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
+% || ( $conf->exists('hidecancelledcustomers')
+% && ! $cgi->param('showcancelledcustomers')
+% )
+% ) {
+% $cgi->param('showcancelledcustomers', 1);
+% $cgi->param('offset', 0);
+% print qq!( <a href="!. $cgi->self_url. qq!">show!;
+% } else {
+% $cgi->param('showcancelledcustomers', 0);
+% $cgi->param('offset', 0);
+% print qq!( <a href="!. $cgi->self_url. qq!">hide!;
+% }
+% print ' cancelled customers</a> )';
+% }
+%
+% if ( $cgi->param('referral_custnum') ) {
+% $cgi->param('referral_custnum') =~ /^(\d+)$/
+% or errorpage("Illegal referral_custnum");
+% my $referral_custnum = $1;
+% my $cust_main = qsearchs('cust_main', { custnum => $referral_custnum } );
+% print '<FORM METHOD="GET">'.
+% qq!<INPUT TYPE="hidden" NAME="referral_custnum" VALUE="$referral_custnum">!.
+% 'referrals of <A HREF="'. popurl(2).
+% "view/cust_main.cgi?$referral_custnum\">$referral_custnum: ".
+% ( $cust_main->company
+% || $cust_main->last. ', '. $cust_main->first ).
+% '</A>';
+% print "\n",<<END;
+% <SCRIPT>
+% function changed(what) {
+% what.form.submit();
+% }
+% </SCRIPT>
+%END
+% print ' <SELECT NAME="referral_depth" SIZE="1" onChange="changed(this)">';
+% my $max = 8; #config file
+% $cgi->param('referral_depth') =~ /^(\d*)$/
+% or errorpage("Illegal referral_depth");
+% my $referral_depth = $1;
+%
+% foreach my $depth ( 1 .. $max ) {
+% print '<OPTION',
+% ' SELECTED'x($depth == $referral_depth),
+% ">$depth";
+% }
+% print "</SELECT> levels deep".
+% '<NOSCRIPT> <INPUT TYPE="submit" VALUE="change"></NOSCRIPT>'.
+% '</FORM>';
+% }
+%
+% my @custom_priorities = ();
+% if ( $conf->config('ticket_system-custom_priority_field')
+% && @{[ $conf->config('ticket_system-custom_priority_field-values') ]} ) {
+% @custom_priorities =
+% $conf->config('ticket_system-custom_priority_field-values');
+% }
+%
+% print "<BR><BR>". $pager. include('/elements/table-grid.html'). <<END;
+% <TR>
+% <TH CLASS="grid" BGCOLOR="#cccccc">#</TH>
+% <TH CLASS="grid" BGCOLOR="#cccccc">Status</TH>
+% <TH CLASS="grid" BGCOLOR="#cccccc">(bill) name</TH>
+% <TH CLASS="grid" BGCOLOR="#cccccc">company</TH>
+%END
+%
+%if ( defined dbdef->table('cust_main')->column('ship_last') ) {
+% print <<END;
+% <TH CLASS="grid" BGCOLOR="#cccccc">(service) name</TH>
+% <TH CLASS="grid" BGCOLOR="#cccccc">company</TH>
+%END
+%}
+%
+%foreach my $addl_header ( @addl_headers ) {
+% print '<TH CLASS="grid" BGCOLOR="#cccccc">'. "$addl_header</TH>";
+%}
+%
+%print <<END;
+% <TH CLASS="grid" BGCOLOR="#cccccc">Packages</TH>
+% <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Services</TH>
+% </TR>
+%END
+%
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor;
+%
+% my(%saw,$cust_main);
+% foreach $cust_main (
+% sort $sortby grep(!$saw{$_->custnum}++, @cust_main)
+% ) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% my($custnum,$last,$first,$company)=(
+% $cust_main->custnum,
+% $cust_main->getfield('last'),
+% $cust_main->getfield('first'),
+% $cust_main->company,
+% );
+%
+% my(@lol_cust_svc);
+% my($rowspan)=0;#scalar( @{$all_pkgs{$custnum}} );
+% foreach ( @{$all_pkgs{$custnum}} ) {
+% #my(@cust_svc) = qsearch( 'cust_svc', { 'pkgnum' => $_->pkgnum } );
+% my @cust_svc = $_->cust_svc;
+% push @lol_cust_svc, \@cust_svc;
+% $rowspan += scalar(@cust_svc) || 1;
+% }
+%
+% #my($rowspan) = scalar(@{$all_pkgs{$custnum}});
+% my $view;
+% if ( defined $cgi->param('quickpay') && $cgi->param('quickpay') eq 'yes' ) {
+% $view = $p. 'edit/cust_pay.cgi?quickpay=yes;custnum='. $custnum;
+% } else {
+% $view = $p. 'view/cust_main.cgi?'. $custnum;
+% }
+% my $pcompany = $company
+% ? qq!<A HREF="$view"><FONT SIZE=-1>$company</FONT></A>!
+% : '<FONT SIZE=-1>&nbsp;</FONT>';
+%
+% my $status = $cust_main->status;
+% my $statuscol = $cust_main->statuscolor;
+
+ <TR>
+ <TD CLASS="grid" ALIGN="right" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %>><A HREF="<% $view %>"><FONT SIZE=-1><% $cust_main->display_custnum %></FONT></A></TD>
+ <TD CLASS="grid" ALIGN="center" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %>><FONT SIZE="-1" COLOR="#<% $statuscol %>"><B><% ucfirst($status) %></B></FONT></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %>><A HREF="<% $view %>"><FONT SIZE=-1><% "$last, $first" %></FONT></A></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %>><% $pcompany %></TD>
+%
+% if ( defined dbdef->table('cust_main')->column('ship_last') ) {
+% my($ship_last,$ship_first,$ship_company)=(
+% $cust_main->ship_last || $cust_main->getfield('last'),
+% $cust_main->ship_last ? $cust_main->ship_first : $cust_main->first,
+% $cust_main->ship_last ? $cust_main->ship_company : $cust_main->company,
+% );
+% my $pship_company = $ship_company
+% ? qq!<A HREF="$view"><FONT SIZE=-1>$ship_company</FONT></A>!
+% : '<FONT SIZE=-1>&nbsp;</FONT>';
+%
+
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %>><A HREF="<% $view %>"><FONT SIZE=-1><% "$ship_last, $ship_first" %></FONT></A></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %>><% $pship_company %></A></TD>
+% }
+%
+% foreach my $addl_col ( @addl_cols ) {
+% if ( $addl_col eq 'tickets' ) {
+% if ( @custom_priorities ) {
+
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %> ALIGN=right><FONT SIZE=-1>
+
+ <TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
+% foreach my $priority ( @custom_priorities, '' ) {
+%
+% my $num =
+% FS::TicketSystem->num_customer_tickets($custnum,$priority);
+% my $ahref = '';
+% $ahref= '<A HREF="'.
+% FS::TicketSystem->href_customer_tickets($custnum,$priority).
+% '">'
+% if $num;
+%
+
+
+ <TR>
+ <TD ALIGN=right>
+ <FONT SIZE=-1><% $ahref.$num %></A></FONT>
+ </TD>
+ <TD ALIGN=left>
+ <FONT SIZE=-1><% $ahref %><% $priority || '<i>(none)</i>' %></A></FONT>
+ </TD>
+ </TR>
+% }
+
+
+ <TR>
+ <TH ALIGN=right STYLE="border-top: dashed 1px black">
+ <FONT SIZE=-1>
+% } else {
+
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %> ALIGN=right><FONT SIZE=-1>
+% }
+%
+% my $ahref = '';
+% $ahref = '<A HREF="'.
+% FS::TicketSystem->href_customer_tickets($custnum).
+% '">'
+% if $cust_main->get($addl_col);
+%
+
+
+ <% $ahref %><% $cust_main->get($addl_col) %></A>
+% if ( @custom_priorities ) {
+
+
+ </FONT></TH>
+ <TH ALIGN=left STYLE="border-top: dashed 1px black">
+ <FONT SIZE=-1><% ${ahref} %>Total</A><FONT>
+ </TH>
+ </TR>
+ </TABLE>
+% }
+
+
+ </FONT></TD>
+% } else {
+
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ROWSPAN=<% $rowspan || 1 %> ALIGN=right><FONT SIZE=-1>
+ <% $cust_main->get($addl_col) %>
+ </FONT></TD>
+%
+% }
+% }
+%
+% my($n1)='';
+% foreach ( @{$all_pkgs{$custnum}} ) {
+% my $pkgnum = $_->pkgnum;
+%# my $part_pkg = qsearchs( 'part_pkg', { pkgpart => $_->pkgpart } );
+% my $part_pkg = $_->part_pkg;
+%
+% my $pkg = $part_pkg->pkg;
+% my $comment = $part_pkg->comment;
+% my $pkgview = "${p}view/cust_main.cgi?$custnum#cust_pkg$pkgnum";
+% my @cust_svc = @{shift @lol_cust_svc};
+% #my(@cust_svc) = qsearch( 'cust_svc', { 'pkgnum' => $_->pkgnum } );
+% my $rowspan = scalar(@cust_svc) || 1;
+%
+% print $n1, qq!<TD CLASS="grid" BGCOLOR="$bgcolor" ROWSPAN=$rowspan><A HREF="$pkgview"><FONT SIZE=-1>$pkg - $comment</FONT></A></TD>!;
+%
+% my($n2)='';
+% foreach my $cust_svc ( @cust_svc ) {
+% my($label, $value, $svcdb) = $cust_svc->label;
+% my($svcnum) = $cust_svc->svcnum;
+% my($sview) = $p.'view';
+% print $n2,
+% qq!<TD CLASS="grid" BGCOLOR="$bgcolor" >!. FS::UI::Web::svc_link($m, $cust_svc->part_svc, $cust_svc) . qq!</TD> !.
+% qq!<TD CLASS="grid" BGCOLOR="$bgcolor" >!. FS::UI::Web::svc_label_link($m, $cust_svc->part_svc, $cust_svc) . qq!</TD> !;
+% $n2="</TR><TR>";
+% }
+%
+% unless ( @cust_svc ) {
+% print qq!<TD CLASS="grid" BGCOLOR="$bgcolor" COLSPAN=2>&nbsp;</TD>!;
+% }
+%
+% #print qq!</TR><TR>\n!;
+% $n1="</TR><TR>";
+% }
+%
+% unless ( @{$all_pkgs{$custnum}} ) {
+% print qq!<TD CLASS="grid" BGCOLOR="$bgcolor" COLSPAN=3>&nbsp;</TD>!;
+% }
+%
+% print "</TR>";
+% }
+%
+%
+
+
+ </TABLE><% $pager %>
+
+ <% include('/elements/footer.html') %>
+% }
+%
+%#undef $cache; #does this help?
+%
+%#
+%
+%sub last_sort {
+% lc($a->getfield('last')) cmp lc($b->getfield('last'))
+% || lc($a->first) cmp lc($b->first);
+%}
+%
+%sub company_sort {
+% return -1 if $a->company && ! $b->company;
+% return 1 if ! $a->company && $b->company;
+% lc($a->company) cmp lc($b->company)
+% || lc($a->getfield('last')) cmp lc($b->getfield('last'))
+% || lc($a->first) cmp lc($b->first);;
+%}
+%
+%sub display_custnum_sort {
+% $a->display_custnum <=> $b->display_custnum;
+%}
+%
+%sub custnum_sort {
+% $a->getfield('custnum') <=> $b->getfield('custnum');
+%}
+%
+%sub tickets_sort {
+% $b->getfield('tickets') <=> $a->getfield('tickets');
+%}
+%
+%sub custnumsearch {
+%
+% my $custnum = $cgi->param('custnum_text');
+% $custnum =~ s/\D//g;
+% $custnum =~ /^(\d{1,23})$/ or errorpage("Illegal customer number");
+% $custnum = $1;
+%
+% [ qsearchs('cust_main', { 'custnum' => $custnum } ) ];
+%}
+%
+%sub cardsearch {
+%
+% my($card)=$cgi->param('card');
+% $card =~ s/\D//g;
+% $card =~ /^(\d{13,16})$/ or errorpage("Illegal card number");
+% my($payinfo)=$1;
+%
+% [ qsearch('cust_main',{'payinfo'=>$payinfo, 'payby'=>'CARD'}),
+% qsearch('cust_main',{'payinfo'=>$payinfo, 'payby'=>'DCRD'})
+% ];
+%}
+%
+%sub referralsearch {
+% $cgi->param('referral_custnum') =~ /^(\d+)$/
+% or errorpage("Illegal referral_custnum");
+% my $cust_main = qsearchs('cust_main', { 'custnum' => $1 } )
+% or errorpage("Customer $1 not found");
+% my $depth;
+% if ( $cgi->param('referral_depth') ) {
+% $cgi->param('referral_depth') =~ /^(\d+)$/
+% or errorpage("Illegal referral_depth");
+% $depth = $1;
+% } else {
+% $depth = 1;
+% }
+% [ $cust_main->referral_cust_main($depth) ];
+%}
+%
+%sub lastsearch {
+% my(%last_type);
+% my @cust_main;
+% foreach ( $cgi->param('last_type') ) {
+% $last_type{$_}++;
+% }
+%
+% $cgi->param('last_text') =~ /^([\w \,\.\-\']*)$/
+% or errorpage("Illegal last name");
+% my($last)=$1;
+%
+% if ( $last_type{'Exact'} || $last_type{'Fuzzy'} ) {
+% push @cust_main, qsearch( 'cust_main',
+% { 'last' => { 'op' => 'ILIKE',
+% 'value' => $last } } );
+%
+% push @cust_main, qsearch( 'cust_main',
+% { 'ship_last' => { 'op' => 'ILIKE',
+% 'value' => $last } } )
+% if defined dbdef->table('cust_main')->column('ship_last');
+% }
+%
+% if ( $last_type{'Substring'} || $last_type{'All'} ) {
+%
+% push @cust_main, qsearch( 'cust_main',
+% { 'last' => { 'op' => 'ILIKE',
+% 'value' => "%$last%" } } );
+%
+% push @cust_main, qsearch( 'cust_main',
+% { 'ship_last' => { 'op' => 'ILIKE',
+% 'value' => "%$last%" } } )
+% if defined dbdef->table('cust_main')->column('ship_last');
+%
+% }
+%
+% if ( $last_type{'Fuzzy'} || $last_type{'All'} ) {
+% push @cust_main, FS::cust_main->fuzzy_search( { 'last' => $last } );
+% }
+%
+% #if ($last_type{'Sound-alike'}) {
+% #}
+%
+% \@cust_main;
+%}
+%
+%sub companysearch {
+%
+% my(%company_type);
+% my @cust_main;
+% foreach ( $cgi->param('company_type') ) {
+% $company_type{$_}++
+% };
+%
+% $cgi->param('company_text') =~
+% /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]*)$/
+% or errorpage("Illegal company");
+% my $company = $1;
+%
+% if ( $company_type{'Exact'} || $company_type{'Fuzzy'} ) {
+% push @cust_main, qsearch( 'cust_main',
+% { 'company' => { 'op' => 'ILIKE',
+% 'value' => $company } } );
+%
+% push @cust_main, qsearch( 'cust_main',
+% { 'ship_company' => { 'op' => 'ILIKE',
+% 'value' => $company } } )
+% if defined dbdef->table('cust_main')->column('ship_last');
+% }
+%
+% if ( $company_type{'Substring'} || $company_type{'All'} ) {
+%
+% push @cust_main, qsearch( 'cust_main',
+% { 'company' => { 'op' => 'ILIKE',
+% 'value' => "%$company%" } } );
+%
+% push @cust_main, qsearch( 'cust_main',
+% { 'ship_company' => { 'op' => 'ILIKE',
+% 'value' => "%$company%" } })
+% if defined dbdef->table('cust_main')->column('ship_last');
+%
+% }
+%
+% if ( $company_type{'Fuzzy'} || $company_type{'All'} ) {
+% push @cust_main, FS::cust_main->fuzzy_search( { 'company' => $company } );
+% }
+%
+% if ($company_type{'Sound-alike'}) {
+% }
+%
+% \@cust_main;
+%}
+%
+%sub address2search {
+% my @cust_main;
+%
+% $cgi->param('address2_text') =~
+% /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]*)$/
+% or errorpage("Illegal address2");
+% my $address2 = $1;
+%
+% push @cust_main, qsearch( 'cust_main',
+% { 'address2' => { 'op' => 'ILIKE',
+% 'value' => $address2 } } );
+% push @cust_main, qsearch( 'cust_main',
+% { 'ship_address2' => { 'op' => 'ILIKE',
+% 'value' => $address2 } } );
+%
+% \@cust_main;
+%}
+%
+%sub phonesearch {
+% my @cust_main;
+%
+% my $phone = $cgi->param('phone_text');
+%
+% #(no longer really) false laziness with Record::ut_phonen
+% #only works with US/CA numbers...
+% $phone =~ s/\D//g;
+% if ( $phone =~ /^(\d{3})(\d{3})(\d{4})(\d*)$/ ) {
+% $phone = "$1-$2-$3";
+% $phone .= " x$4" if $4;
+% } elsif ( $phone =~ /^(\d{3})(\d{4})$/ ) {
+% $phone = "$1-$2";
+% } elsif ( $phone =~ /^(\d{3,4})$/ ) {
+% $phone = $1;
+% } else {
+% errorpage(gettext('illegal_phone'). ": $phone");
+% }
+%
+% my @fields = qw(daytime night fax);
+% push @fields, qw(ship_daytime ship_night ship_fax)
+% if defined dbdef->table('cust_main')->column('ship_last');
+%
+% for my $field ( @fields ) {
+% push @cust_main, qsearch ( 'cust_main',
+% { $field => { 'op' => 'LIKE',
+% 'value' => "%$phone%" } } );
+% }
+%
+% \@cust_main;
+%}
diff --git a/httemplate/search/cust_main.html b/httemplate/search/cust_main.html
new file mode 100755
index 0000000..3282f0f
--- /dev/null
+++ b/httemplate/search/cust_main.html
@@ -0,0 +1,109 @@
+<% include( 'elements/search.html',
+ 'title' => 'Customer Search Results',
+ 'menubar' => $menubar,
+ 'name' => 'customers',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'header' => [ FS::UI::Web::cust_header(
+ $cgi->param('cust_fields')
+ ),
+ @extra_headers,
+ ],
+ 'fields' => [
+ \&FS::UI::Web::cust_fields,
+ @extra_fields,
+ ],
+ 'color' => [ FS::UI::Web::cust_colors(),
+ map '', @extra_fields
+ ],
+ 'style' => [ FS::UI::Web::cust_styles(),
+ map '', @extra_fields
+ ],
+ 'align' => [ FS::UI::Web::cust_aligns(),
+ map '', @extra_fields
+ ],
+ 'links' => [ ( map { $_ ne 'Cust. Status' ? $link : '' }
+ FS::UI::Web::cust_header(
+ $cgi->param('cust_fields')
+ )
+ ),
+ map '', @extra_fields
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless ( $FS::CurrentUser::CurrentUser->access_right('List customers') &&
+ $FS::CurrentUser::CurrentUser->access_right('List packages')
+ );
+
+my %search_hash = ();
+
+#$search_hash{'query'} = $cgi->keywords;
+
+#scalars
+for my $param (qw(
+ agentnum status cancelled_pkgs cust_fields flattened_pkgs custbatch
+)) {
+ $search_hash{$param} = scalar( $cgi->param($param) )
+ if $cgi->param($param);
+}
+
+#lists
+for my $param (qw( payby )) {
+ $search_hash{$param} = [ $cgi->param($param) ]
+ if $cgi->param($param);
+}
+
+###
+# parse dates
+###
+
+foreach my $field (qw( signupdate )) {
+
+ my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
+
+ next if $beginning == 0 && $ending == 4294967295;
+ #or $disable{$cgi->param('status')}->{$field};
+
+ $search_hash{$field} = [ $beginning, $ending ];
+
+}
+
+##
+# amounts
+##
+
+$search_hash{'current_balance'} =
+ [ FS::UI::Web::parse_lt_gt($cgi, 'current_balance') ];
+
+###
+# etc
+###
+
+my $sql_query = FS::cust_main->search_sql(\%search_hash);
+my $count_query = delete($sql_query->{'count_query'});
+my @extra_headers = @{ delete($sql_query->{'extra_headers'}) };
+my @extra_fields = @{ delete($sql_query->{'extra_fields'}) };
+
+my $link = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+###
+# email links
+###
+
+my $menubar = [];
+
+if ( $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices') ) {
+
+ my $uri = new URI::URL;
+ $uri->query_form( \%search_hash );
+ my $query = $uri->query;
+
+ push @$menubar, 'Email a notice to these customers' =>
+ "${p}misc/email-customers.html?$query",
+
+}
+
+</%init>
diff --git a/httemplate/search/cust_pay.cgi b/httemplate/search/cust_pay.cgi
new file mode 100755
index 0000000..65bd39e
--- /dev/null
+++ b/httemplate/search/cust_pay.cgi
@@ -0,0 +1,7 @@
+<% include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'pay',
+ 'amount_field' => 'paid',
+ 'name_singular' => 'payment',
+ 'name_verb' => 'paid',
+ )
+%>
diff --git a/httemplate/search/cust_pay_batch.cgi b/httemplate/search/cust_pay_batch.cgi
new file mode 100755
index 0000000..1576963
--- /dev/null
+++ b/httemplate/search/cust_pay_batch.cgi
@@ -0,0 +1,193 @@
+<% include('elements/search.html',
+ 'title' => 'Batch payment details',
+ 'name' => 'batch details',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'html_init' => $pay_batch ? $html_init : '',
+ 'header' => [ '#',
+ 'Inv #',
+ 'Customer',
+ 'Customer',
+ 'Card Name',
+ 'Card',
+ 'Exp',
+ 'Amount',
+ 'Status',
+ ],
+ 'fields' => [ sub {
+ shift->[0];
+ },
+ sub {
+ shift->[1];
+ },
+ sub {
+ shift->[2];
+ },
+ sub {
+ my $cpb = shift;
+ $cpb->[3] . ', ' . $cpb->[4];
+ },
+ sub {
+ shift->[5];
+ },
+ sub {
+ my $cardnum = shift->[6];
+ 'x'x(length($cardnum)-4). substr($cardnum,(length($cardnum)-4));
+ },
+ sub {
+ shift->[7] =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ my( $mon, $year ) = ( $2, $1 );
+ $mon = "0$mon" if length($mon) == 1;
+ "$mon/$year";
+ },
+ sub {
+ shift->[8];
+ },
+ sub {
+ shift->[9];
+ },
+ ],
+ 'align' => 'lllllllrl',
+ 'links' => [ ['', sub{'#';}],
+ ["${p}view/cust_bill.cgi?", sub{shift->[1];},],
+ ["${p}view/cust_main.cgi?", sub{shift->[2];},],
+ ["${p}view/cust_main.cgi?", sub{shift->[2];},],
+ ],
+ )
+%>
+<%init>
+
+my $conf = new FS::Conf;
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports')
+ || $FS::CurrentUser::CurrentUser->access_right('Process batches')
+ || ( $cgi->param('custnum')
+ && ( $conf->exists('batch-enable')
+ || $conf->config('batch-enable_payby')
+ )
+ #&& $FS::CurrentUser::CurrentUser->access_right('View customer batched payments')
+ );
+
+my( $count_query, $sql_query );
+my $hashref = {};
+my @search = ();
+my $orderby = 'paybatchnum';
+
+my( $pay_batch, $batchnum ) = ( '', '');
+if ( $cgi->param('batchnum') && $cgi->param('batchnum') =~ /^(\d+)$/ ) {
+ push @search, "batchnum = $1";
+ $pay_batch = qsearchs('pay_batch', { 'batchnum' => $1 } );
+ die "Batch $1 not found!" unless $pay_batch;
+ $batchnum = $pay_batch->batchnum;
+}
+
+if ( $cgi->param('custnum') && $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ push @search, "custnum = $1";
+}
+
+if ( $cgi->param('status') && $cgi->param('status') =~ /^(\w)$/ ) {
+ push @search, "pay_batch.status = '$1'";
+}
+
+if ( $cgi->param('payby') ) {
+ $cgi->param('payby') =~ /^(CARD|CHEK)$/
+ or die "illegal payby " . $cgi->param('payby');
+
+ push @search, "cust_pay_batch.payby = '$1'";
+}
+
+if ( not $cgi->param('dcln') ) {
+ push @search, "cpb.status IS DISTINCT FROM 'Approved'";
+}
+
+my ($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+unless ($pay_batch){
+ push @search, "pay_batch.upload >= $beginning" if ($beginning);
+ push @search, "pay_batch.upload <= $ending" if ($ending < 4294967295);#2^32-1
+ $orderby = "pay_batch.download,paybatchnum";
+}
+
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+my $search = ' WHERE ' . join(' AND ', @search);
+
+$count_query = 'SELECT COUNT(*) FROM cust_pay_batch AS cpb ' .
+ 'LEFT JOIN cust_main USING ( custnum ) ' .
+ 'LEFT JOIN pay_batch USING ( batchnum )' .
+ $search;
+
+#grr
+$sql_query = "SELECT paybatchnum,invnum,custnum,cpb.last,cpb.first," .
+ "cpb.payname,cpb.payinfo,cpb.exp,amount,cpb.status " .
+ "FROM cust_pay_batch AS cpb " .
+ 'LEFT JOIN cust_main USING ( custnum ) ' .
+ 'LEFT JOIN pay_batch USING ( batchnum ) ' .
+ "$search ORDER BY $orderby";
+
+my $html_init = '';
+if ( $pay_batch ) {
+ my $fixed = $conf->config('batch-fixed_format-'. $pay_batch->payby);
+ if (
+ $pay_batch->status eq 'O'
+ || ( $pay_batch->status eq 'I'
+ && $FS::CurrentUser::CurrentUser->access_right('Reprocess batches')
+ )
+ ) {
+ $html_init .= qq!<FORM ACTION="$p/misc/download-batch.cgi" METHOD="POST">!;
+ if ( $fixed ) {
+ $html_init .= qq!<INPUT TYPE="hidden" NAME="format" VALUE="$fixed">!;
+ } else {
+ $html_init .= qq!Download batch in format <SELECT NAME="format">!.
+ qq!<OPTION VALUE="">Default batch mode</OPTION>!.
+ qq!<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV file for TD Canada Trust Merchant PC Batch</OPTION>!.
+ qq!<OPTION VALUE="csv-chase_canada-E-xactBatch">CSV file for Chase Canada E-xactBatch</OPTION>!.
+ qq!<OPTION VALUE="PAP">80 byte file for TD Canada Trust PAP Batch</OPTION>!.
+ qq!<OPTION VALUE="BoM">Bank of Montreal ECA batch</OPTION>!.
+ qq!<OPTION VALUE="ach-spiritone">Spiritone ACH batch</OPTION>!.
+ qq!</SELECT>!;
+ }
+ $html_init .= qq!<INPUT TYPE="hidden" NAME="batchnum" VALUE="$batchnum"><INPUT TYPE="submit" VALUE="Download"></FORM><BR>!;
+ }
+
+ if (
+ $pay_batch->status eq 'I'
+ || ( $pay_batch->status eq 'R'
+ && $FS::CurrentUser::CurrentUser->access_right('Reprocess batches')
+ )
+ ) {
+ $html_init .= qq!<FORM ACTION="$p/misc/upload-batch.cgi" METHOD="POST" ENCTYPE="multipart/form-data">!.
+ qq!Upload results<BR>!.
+ qq!Filename <INPUT TYPE="file" NAME="batch_results"><BR>!;
+ if ( $fixed ) {
+ $html_init .= qq!<INPUT TYPE="hidden" NAME="format" VALUE="$fixed">!;
+ } else {
+ $html_init .= qq!Format <SELECT NAME="format">!.
+ qq!<OPTION VALUE="">Default batch mode</OPTION>!.
+ qq!<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV results from TD Canada Trust Merchant PC Batch</OPTION>!.
+ qq!<OPTION VALUE="csv-chase_canada-E-xactBatch">CSV file for Chase Canada E-xactBatch</OPTION>!.
+ qq!<OPTION VALUE="PAP">264 byte results for TD Canada Trust PAP Batch</OPTION>!.
+ qq!<OPTION VALUE="BoM">Bank of Montreal ECA results</OPTION>!.
+ qq!<OPTION VALUE="ach-spiritone">Spiritone ACH batch</OPTION>!.
+ qq!</SELECT><BR>!;
+ }
+ $html_init .= qq!<INPUT TYPE="hidden" NAME="batchnum" VALUE="$batchnum">!;
+ $html_init .= '<INPUT TYPE="submit" VALUE="Upload"></FORM><BR>';
+ }
+
+}
+
+if ($pay_batch) {
+ my $sth = dbh->prepare($count_query) or die dbh->errstr. "doing $count_query";
+ $sth->execute or die "Error executing \"$count_query\": ". $sth->errstr;
+ my $cards = $sth->fetchrow_arrayref->[0];
+
+ my $st = "SELECT SUM(amount) from cust_pay_batch WHERE batchnum=". $batchnum;
+ $sth = dbh->prepare($st) or die dbh->errstr. "doing $st";
+ $sth->execute or die "Error executing \"$st\": ". $sth->errstr;
+ my $total = $sth->fetchrow_arrayref->[0];
+
+ $html_init .= "$cards credit card payments batched<BR>\$" .
+ sprintf("%.2f", $total) ." total in batch<BR>";
+}
+
+</%init>
diff --git a/httemplate/search/cust_pay_pending.html b/httemplate/search/cust_pay_pending.html
new file mode 100755
index 0000000..f46e08a
--- /dev/null
+++ b/httemplate/search/cust_pay_pending.html
@@ -0,0 +1,57 @@
+<% include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'pay_pending',
+ 'amount_field' => 'paid',
+ 'name_singular' => 'pending payment',
+ 'name_verb' => 'pending',
+ 'disable_link' => 1,
+ 'disable_by' => 1, #add otaker to cust_pay_pending?
+ 'html_init' => include('/elements/init_overlib.html'),
+ 'addl_header' => [ 'Time', 'Payment Status', ],
+ 'addl_fields' => [ sub { time2str('%r', shift->_date ) },
+ $status_sub,
+ ],
+ 'redirect_empty' => $redirect_empty,
+ )
+%>
+<%init>
+
+my %statusaction = (
+ 'new' => 'delete',
+ 'pending' => 'complete',
+ #'authorized' => '',
+ #'captured' => '',
+ #'declined' => '',
+ #wouldn't need to take action on a done state#'done'
+);
+
+my $edit_pending =
+ $FS::CurrentUser::CurrentUser->access_right('Edit customer pending payments');
+
+my $status_sub = sub {
+ my $pending = shift;
+ my $return = $pending->status;
+ my $action = $statusaction{$pending->status};
+ return $return unless $action && $edit_pending;
+ my $link = include('/elements/popup_link.html',
+ 'action' => $p. 'edit/cust_pay_pending.html'.
+ '?paypendingnum='. $pending->paypendingnum.
+ ";action=$action",
+ 'label' => $action,
+ 'color' => '#ff0000',
+ 'width' => 655,
+ 'height' => ( $action eq 'delete' ? 480 : 575 ),
+ 'actionlabel' => ucfirst($action). ' pending payment',
+ );
+ $return. qq! <FONT SIZE="-1">($link)</FONT>!;
+};
+
+my $redirect_empty = sub {
+ my $cgi = shift;
+ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $p. "view/cust_main.cgi?$1";
+ } else {
+ '';
+ }
+};
+
+</%init>
diff --git a/httemplate/search/cust_pkg.cgi b/httemplate/search/cust_pkg.cgi
new file mode 100755
index 0000000..bd4a946
--- /dev/null
+++ b/httemplate/search/cust_pkg.cgi
@@ -0,0 +1,233 @@
+<% include( 'elements/search.html',
+ 'html_init' => $html_init,
+ 'title' => 'Package Search Results',
+ 'name' => 'packages',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ #'redirect' => $link,
+ 'header' => [ '#',
+ 'Quan.',
+ 'Package',
+ 'Class',
+ 'Status',
+ 'Freq.',
+ 'Setup',
+ 'Last bill',
+ 'Next bill',
+ 'Adjourn',
+ 'Susp.',
+ 'Expire',
+ 'Cancel',
+ 'Reason',
+ FS::UI::Web::cust_header(
+ $cgi->param('cust_fields')
+ ),
+ 'Services',
+ ],
+ 'fields' => [
+ 'pkgnum',
+ 'quantity',
+ sub { #my $part_pkg = $part_pkg{shift->pkgpart};
+ #$part_pkg->pkg; # ' - '. $part_pkg->comment;
+ $_[0]->pkg; # ' - '. $_[0]->comment;
+ },
+ 'classname',
+ sub { ucfirst(shift->status); },
+ sub { #shift->part_pkg->freq_pretty;
+
+ #my $part_pkg = $part_pkg{shift->pkgpart};
+ #$part_pkg->freq_pretty;
+
+ FS::part_pkg::freq_pretty(shift);
+ },
+
+ #sub { time2str('%b %d %Y', shift->setup); },
+ #sub { time2str('%b %d %Y', shift->last_bill); },
+ #sub { time2str('%b %d %Y', shift->bill); },
+ #sub { time2str('%b %d %Y', shift->susp); },
+ #sub { time2str('%b %d %Y', shift->expire); },
+ #sub { time2str('%b %d %Y', shift->get('cancel')); },
+ ( map { time_or_blank($_) }
+ qw( setup last_bill bill adjourn susp expire cancel ) ),
+
+ sub { my $self = shift;
+ my $return = '';
+ foreach my $action ( qw ( cancel susp ) ) {
+ my $reason = $self->last_reason($action);
+ $return = $reason->reason if $reason;
+ last if $return;
+ }
+ $return;
+ },
+
+ \&FS::UI::Web::cust_fields,
+ #sub { '<table border=0 cellspacing=0 cellpadding=0 STYLE="border:none">'.
+ # join('', map { '<tr><td align="right" style="border:none">'. $_->[0].
+ # ':</td><td style="border:none">'. $_->[1]. '</td></tr>' }
+ # shift->labels
+ # ).
+ # '</table>';
+ # },
+ sub {
+ [ map {
+ [
+ { 'data' => $_->[0]. ':',
+ 'align'=> 'right',
+ },
+ { 'data' => $_->[1],
+ 'align'=> 'left',
+ 'link' => $p. 'view/' .
+ $_->[2]. '.cgi?'. $_->[3],
+ },
+ ];
+ } shift->labels
+ ];
+ },
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ sub { shift->statuscolor; },
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ '',
+ ],
+ 'style' => [ '', '', '', '', 'b', '', '', '', '', '', '', '', '', '',
+ FS::UI::Web::cust_styles() ],
+ 'size' => [ '', '', '', '', '-1' ],
+ 'align' => 'rrlcclrrrrrrrl'. FS::UI::Web::cust_aligns(). 'r',
+ 'links' => [
+ $link,
+ $link,
+ $link,
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header(
+ $cgi->param('cust_fields')
+ )
+ ),
+ '',
+ ],
+ 'extra_choices_callback'=> $extra_choices,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List packages');
+
+# my %part_pkg = map { $_->pkgpart => $_ } qsearch('part_pkg', {});
+
+ my %search_hash = ();
+
+ $search_hash{'query'} = $cgi->keywords;
+
+ for my $param (qw(agentnum magic status classnum pkgpart)) {
+ $search_hash{$param} = $cgi->param($param)
+ if $cgi->param($param);
+ }
+
+###
+# parse dates
+###
+
+#false laziness w/report_cust_pkg.html
+my %disable = (
+ 'all' => {},
+ 'one-time charge' => { 'last_bill'=>1, 'bill'=>1, 'adjourn'=>1, 'susp'=>1, 'expire'=>1, 'cancel'=>1, },
+ 'active' => { 'susp'=>1, 'cancel'=>1 },
+ 'suspended' => { 'cancel' => 1 },
+ 'cancelled' => {},
+ '' => {},
+);
+
+foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
+
+ my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
+
+ next if $beginning == 0 && $ending == 4294967295
+ or $disable{$cgi->param('status')}->{$field};
+
+ $search_hash{$field} = [ $beginning, $ending ];
+
+}
+
+my $sql_query = FS::cust_pkg->search_sql(\%search_hash);
+my $count_query = delete($sql_query->{'count_query'});
+
+my $link = sub {
+ [ "${p}view/cust_main.cgi?".shift->custnum.'#cust_pkg', 'pkgnum' ];
+};
+
+my $clink = sub {
+ my $cust_pkg = shift;
+ $cust_pkg->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'custnum' ]
+ : '';
+};
+
+#if ( scalar(@cust_pkg) == 1 ) {
+# print $cgi->redirect("${p}view/cust_main.cgi?". $cust_pkg[0]->custnum.
+# "#cust_pkg". $cust_pkg[0]->pkgnum );
+
+# my @cust_svc = qsearch( 'cust_svc', { 'pkgnum' => $pkgnum } );
+# my $rowspan = scalar(@cust_svc) || 1;
+
+# my $n2 = '';
+# foreach my $cust_svc ( @cust_svc ) {
+# my($label, $value, $svcdb) = $cust_svc->label;
+# my $svcnum = $cust_svc->svcnum;
+# my $sview = $p. "view";
+# print $n2,qq!<TD><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$label</FONT></A></TD>!,
+# qq!<TD><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$value</FONT></A></TD>!;
+# $n2="</TR><TR>";
+# }
+
+sub time_or_blank {
+ my $column = shift;
+ return sub {
+ my $record = shift;
+ my $value = $record->get($column); #mmm closures
+ $value ? time2str('%b %d %Y', $value ) : '';
+ };
+}
+
+my $html_init = include('/elements/init_overlib.html');
+
+my $extra_choices = sub {
+ my $query = shift;
+
+ return '' unless
+ $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages');
+
+ '<BR><BR>'.
+ include( '/elements/popup_link.html',
+ 'label' => 'Change these packages',
+ 'action' => "${p}misc/bulk_change_pkg.cgi?$query",
+ 'actionlabel' => 'Change Packages',
+ 'width' => 763,
+ 'height' => 336,
+ );
+};
+
+</%init>
diff --git a/httemplate/search/cust_refund.html b/httemplate/search/cust_refund.html
new file mode 100644
index 0000000..e31e088
--- /dev/null
+++ b/httemplate/search/cust_refund.html
@@ -0,0 +1,7 @@
+<% include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'refund',
+ 'amount_field' => 'refund',
+ 'name_singular' => 'refund',
+ 'name_verb' => 'refunded',
+ )
+%>
diff --git a/httemplate/search/cust_svc.html b/httemplate/search/cust_svc.html
new file mode 100644
index 0000000..3beca4d
--- /dev/null
+++ b/httemplate/search/cust_svc.html
@@ -0,0 +1,138 @@
+<% include( 'elements/search.html',
+ 'title' => 'Service search results',
+ 'name' => 'services',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => [ '#',
+ 'Service',
+ # package?
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [ 'svcnum',
+ sub {
+ #$_[0]->svc. ': '. $_[0]->label;
+ my($label, $value, $svcdb) = $_[0]->label;
+ "$label: $value";
+ },
+ # package?
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ $link,
+ # package?
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rl'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $addl_from = ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+my @extra_sql = ();
+my $orderby = 'ORDER BY svcnum'; #has to be ordered by something
+ #for pagination to work
+if ( length( $cgi->param('search_svc') ) ) {
+
+ my $string = $cgi->param('search_svc');
+ $string =~ s/(^\s+|\s+$)//; #trim leading & trailing whitespace
+
+ # implement fuzzy searching in subclasses too at some point?
+ # service searching maybe shouldn't be fuzzy...
+
+ push @extra_sql,
+ ' ( '. join(' OR ',
+ map { my $table = $_;
+ my $search_sql = "FS::$table"->search_sql($string);
+ " ( svcdb = '$table'
+ AND 0 < ( SELECT COUNT(*) FROM $table
+ WHERE $table.svcnum = cust_svc.svcnum
+ AND $search_sql
+ )
+ ) ";
+ }
+ FS::part_svc->svc_tables
+ ). ' ) ';
+
+} elsif ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ $cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unknown svcdb";
+ push @extra_sql, "svcdb = '$1'";
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $orderby = "ORDER BY $sortby";
+ }
+
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+
+ push @extra_sql, "svcpart = $1";
+
+} else {
+ errorpage("No search term specified");
+}
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $extra_sql = ' WHERE '. join(' AND ', @extra_sql );
+
+my $sql_query = {
+ 'select' => join(', ',
+ 'cust_svc.*',
+ 'part_svc.*',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'table' => 'cust_svc',
+ 'addl_from' => $addl_from,
+ 'hashref' => {},
+ 'extra_sql' => "$extra_sql $orderby",
+};
+
+my $count_query = "SELECT COUNT(*) FROM cust_svc $addl_from $extra_sql";
+
+my $link = sub {
+ my $cust_svc = shift;
+ my $url = svc_url(
+ 'm' => $m,
+ 'action' => 'view',
+ #'part_svc' => $cust_svc->part_svc,
+ 'svcdb' => $cust_svc->svcdb, #we have it from the joined search
+ #'svc' => $cust_svc, #redundant
+ 'query' => '',
+ );
+ [ $url, 'svcnum' ];
+};
+
+my $link_cust = sub {
+ my $cust_svc = shift;
+ if ( $cust_svc->custnum ) {
+ [ "${p}view/cust_main.cgi?", 'custnum' ];
+ } else {
+ '';
+ }
+};
+
+</%init>
diff --git a/httemplate/search/cust_tax_exempt.cgi b/httemplate/search/cust_tax_exempt.cgi
new file mode 100644
index 0000000..3704b20
--- /dev/null
+++ b/httemplate/search/cust_tax_exempt.cgi
@@ -0,0 +1,139 @@
+<% include( 'elements/search.html',
+ 'title' => 'Legacy tax exemptions',
+ 'name' => 'legacy tax exemptions',
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ $money_char. '%.2f total', ],
+ 'header' => [
+ '#',
+ 'Month',
+ 'Inserted',
+ 'Amount',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ 'exemptnum',
+ sub { $_[0]->month. '/'. $_[0]->year; },
+ sub { my $h = $_[0]->h_search('insert');
+ $h ? time2str('%L/%d/%Y', $h->history_date ) : ''
+ },
+ sub { $money_char. $_[0]->amount; },
+
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [
+ '',
+ '',
+ '',
+ '',
+
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rrrr'.FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+my $join_cust = "
+ LEFT JOIN cust_main USING ( custnum )
+";
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer tax exemptions');
+
+my @where = ();
+
+#my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+#if ( $beginning || $ending ) {
+# push @where, "_date >= $beginning",
+# "_date <= $ending";
+# #"payby != 'COMP';
+#}
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @where, "agentnum = $1";
+}
+
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.custnum = $1";
+}
+
+#prospect active inactive suspended cancelled
+if ( grep { $cgi->param('status') eq $_ } FS::cust_main->statuses() ) {
+ my $method = $cgi->param('status'). '_sql';
+ #push @where, $class->$method();
+ push @where, FS::cust_main->$method();
+}
+
+if ( $cgi->param('out') ) {
+
+ push @where, "
+ 0 = (
+ SELECT COUNT(*) FROM cust_main_county AS county_out
+ WHERE ( county_out.county = cust_main.county
+ OR ( county_out.county IS NULL AND cust_main.county = '' )
+ OR ( county_out.county = '' AND cust_main.county IS NULL)
+ OR ( county_out.county IS NULL AND cust_main.county IS NULL)
+ )
+ AND ( county_out.state = cust_main.state
+ OR ( county_out.state IS NULL AND cust_main.state = '' )
+ OR ( county_out.state = '' AND cust_main.state IS NULL )
+ OR ( county_out.state IS NULL AND cust_main.state IS NULL )
+ )
+ AND county_out.country = cust_main.country
+ AND county_out.tax > 0
+ )
+ ";
+
+} elsif ( $cgi->param('country' ) ) {
+
+ my $county = dbh->quote( $cgi->param('county') );
+ my $state = dbh->quote( $cgi->param('state') );
+ my $country = dbh->quote( $cgi->param('country') );
+ push @where, "( county = $county OR $county = '' )",
+ "( state = $state OR $state = '' )",
+ " country = $country";
+ push @where, 'taxclass = '. dbh->quote( $cgi->param('taxclass') )
+ if $cgi->param('taxclass');
+
+}
+
+my $where = scalar(@where) ? 'WHERE '.join(' AND ', @where) : '';
+
+my $count_query = "SELECT COUNT(*), SUM(amount)".
+ " FROM cust_tax_exempt $join_cust $where";
+
+my $query = {
+ 'table' => 'cust_tax_exempt',
+ 'addl_from' => $join_cust,
+ 'hashref' => {},
+ 'select' => join(', ',
+ 'cust_tax_exempt.*',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => $where,
+};
+
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/search/cust_tax_exempt.html b/httemplate/search/cust_tax_exempt.html
new file mode 100644
index 0000000..612ad7e
--- /dev/null
+++ b/httemplate/search/cust_tax_exempt.html
@@ -0,0 +1,31 @@
+<% include('/elements/header.html', 'Legacy tax exemption report' ) %>
+
+<FORM ACTION="cust_tax_exempt.cgi" METHOD="GET">
+
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Search options</FONT></TH>
+ </TR>
+
+ <% include( '/elements/tr-select-cust_main-status.html',
+ 'label' => 'Customer Status'
+ )
+ %>
+
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer tax exemptions');
+
+</%init>
+
diff --git a/httemplate/search/cust_tax_exempt_pkg.cgi b/httemplate/search/cust_tax_exempt_pkg.cgi
new file mode 100644
index 0000000..3a5155a
--- /dev/null
+++ b/httemplate/search/cust_tax_exempt_pkg.cgi
@@ -0,0 +1,182 @@
+<% include( 'elements/search.html',
+ 'title' => 'Tax exemptions',
+ 'name' => 'tax exemptions',
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ $money_char. '%.2f total', ],
+ 'header' => [
+ '#',
+ 'Month',
+ 'Amount',
+ 'Line item',
+ 'Invoice',
+ 'Date',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ 'exemptpkgnum',
+ sub { $_[0]->month. '/'. $_[0]->year; },
+ sub { $money_char. $_[0]->amount; },
+
+ sub {
+ $_[0]->billpkgnum. ': '.
+ ( $_[0]->pkgnum > 0
+ ? $_[0]->get('pkg')
+ : $_[0]->get('itemdesc')
+ ).
+ ' ('.
+ ( $_[0]->setup > 0
+ ? $money_char. $_[0]->setup. ' setup'
+ : ''
+ ).
+ ( $_[0]->setup > 0 && $_[0]->recur > 0
+ ? ' / '
+ : ''
+ ).
+ ( $_[0]->recur > 0
+ ? $money_char. $_[0]->recur. ' recur'
+ : ''
+ ).
+ ')';
+ },
+
+ 'invnum',
+ sub { time2str('%b %d %Y', shift->_date ) },
+
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [
+ '',
+ '',
+ '',
+
+ '',
+ $ilink,
+ $ilink,
+
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rrrlrc'.FS::UI::Web::cust_aligns(), # 'rlrrrc',
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%once>
+
+my $join_cust = "
+ JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum )
+";
+
+my $join_pkg = "
+ LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart )
+";
+
+my $join = "
+ JOIN cust_bill_pkg USING ( billpkgnum )
+ $join_cust
+ $join_pkg
+";
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer tax exemptions');
+
+my @where = ();
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+if ( $beginning || $ending ) {
+ push @where, "_date >= $beginning",
+ "_date <= $ending";
+ #"payby != 'COMP';
+}
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.agentnum = $1";
+}
+
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.custnum = $1";
+}
+
+if ( $cgi->param('out') ) {
+
+ push @where, "
+ 0 = (
+ SELECT COUNT(*) FROM cust_main_county AS county_out
+ WHERE ( county_out.county = cust_main.county
+ OR ( county_out.county IS NULL AND cust_main.county = '' )
+ OR ( county_out.county = '' AND cust_main.county IS NULL)
+ OR ( county_out.county IS NULL AND cust_main.county IS NULL)
+ )
+ AND ( county_out.state = cust_main.state
+ OR ( county_out.state IS NULL AND cust_main.state = '' )
+ OR ( county_out.state = '' AND cust_main.state IS NULL )
+ OR ( county_out.state IS NULL AND cust_main.state IS NULL )
+ )
+ AND county_out.country = cust_main.country
+ AND county_out.tax > 0
+ )
+ ";
+
+} elsif ( $cgi->param('country' ) ) {
+
+ my $county = dbh->quote( $cgi->param('county') );
+ my $state = dbh->quote( $cgi->param('state') );
+ my $country = dbh->quote( $cgi->param('country') );
+ push @where, "( county = $county OR $county = '' )",
+ "( state = $state OR $state = '' )",
+ " country = $country";
+ push @where, 'taxclass = '. dbh->quote( $cgi->param('taxclass') )
+ if $cgi->param('taxclass');
+
+}
+
+my $where = scalar(@where) ? 'WHERE '.join(' AND ', @where) : '';
+
+my $count_query = "SELECT COUNT(*), SUM(amount)".
+ " FROM cust_tax_exempt_pkg $join $where";
+
+my $query = {
+ 'table' => 'cust_tax_exempt_pkg',
+ 'addl_from' => $join,
+ 'hashref' => {},
+ 'select' => join(', ',
+ 'cust_tax_exempt_pkg.*',
+ 'cust_bill_pkg.*',
+ 'cust_bill.*',
+ 'part_pkg.pkg',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => $where,
+};
+
+my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html
new file mode 100755
index 0000000..add8427
--- /dev/null
+++ b/httemplate/search/elements/cust_pay_or_refund.html
@@ -0,0 +1,301 @@
+<%doc>
+
+Examples:
+
+ include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'pay',
+ 'amount_field' => 'paid',
+ 'name_singular' => 'payment',
+ 'name_verb' => 'paid',
+ )
+
+ include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'refund',
+ 'amount_field' => 'refund',
+ 'name_singular' => 'refund',
+ 'name_verb' => 'refunded',
+ )
+
+ include( 'elements/cust_pay_or_refund.html',
+ 'thing' => 'pay_pending',
+ 'amount_field' => 'paid',
+ 'name_singular' => 'pending payment',
+ 'name_verb' => 'pending',
+ 'disable_link' => 1,
+ 'disable_by' => 1,
+ 'html_init' => '',
+ 'addl_header' => [],
+ 'addl_fields' => [],
+ 'redirect_empty' => $redirect_empty,
+ )
+
+</%doc>
+<% include( 'search.html',
+ 'title' => $title,
+ 'name_singular' => $name_singular,
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ '$%.2f total '.$opt{name_verb}, ],
+ 'redirect_empty' => $opt{'redirect_empty'},
+ 'header' => [ "\u$name_singular",
+ 'Amount',
+ 'Date',
+ @header,
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ 'payby_payinfo_pretty',
+ sub { sprintf('$%.2f', shift->$amount_field() ) },
+ sub { time2str('%b %d %Y', shift->_date ) },
+ @fields,
+ \&FS::UI::Web::cust_fields,
+ ],
+ #'align' => 'lrrrll',
+ 'align' => 'rrr'.
+ join('', map 'c', @fields ).
+ FS::UI::Web::cust_aligns(),
+ 'links' => [
+ $link,
+ $link,
+ $link,
+ ( map '', @fields ),
+ ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ ( map '', @fields ),
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ ( map '', @fields ),
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+my %opt = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('Financial reports');
+
+my $thing = $opt{'thing'};
+my $amount_field = $opt{'amount_field'};
+my $name_singular = $opt{'name_singular'};
+
+my $title = "\u$name_singular Search Results";
+
+my @header = ();
+my @fields = ();
+unless ( $opt{'disable_by'} ) {
+ push @header, 'By';
+ push @fields, sub { my $o = shift->otaker;
+ $o = 'auto billing' if $o eq 'fs_daily';
+ $o = 'customer self-service' if $o eq 'fs_selfservice';
+ $o;
+ };
+}
+
+push @header, @{ $opt{'addl_header'} }
+ if $opt{'addl_header'};
+push @fields, @{ $opt{'addl_fields'} }
+ if $opt{'addl_fields'};
+
+my( $count_query, $sql_query );
+if ( $cgi->param('magic') ) {
+
+ my @search = ();
+ my $orderby;
+ if ( $cgi->param('magic') eq '_date' ) {
+
+
+ if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "agentnum = $1"; # $search{'agentnum'} = $1;
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "unknown agentnum $1" unless $agent;
+ $title = $agent->agent. " $title";
+ }
+
+ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ push @search, "custnum = $1";
+ }
+
+ if ( $cgi->param('payby') ) {
+ $cgi->param('payby') =~
+ /^(CARD|CHEK|BILL|PREP|CASH|WEST|MCRD)(-(VisaMC|Amex|Discover|Maestro))?$/
+ or die "illegal payby ". $cgi->param('payby');
+ push @search, "cust_$thing.payby = '$1'";
+ if ( $3 ) {
+
+ my $cardtype = $3;
+
+ my $search;
+ if ( $cardtype eq 'VisaMC' ) {
+ #avoid posix regexes for portability
+ $search =
+ " ( ( substring(cust_$thing.payinfo from 1 for 1) = '4' ".
+ " AND substring(cust_$thing.payinfo from 1 for 4) != '4936' ".
+ " AND substring(cust_$thing.payinfo from 1 for 6) ".
+ " NOT SIMILAR TO '49030[2-9]' ".
+ " AND substring(cust_$thing.payinfo from 1 for 6) ".
+ " NOT SIMILAR TO '49033[5-9]' ".
+ " AND substring(cust_$thing.payinfo from 1 for 6) ".
+ " NOT SIMILAR TO '49110[1-2]' ".
+ " AND substring(cust_$thing.payinfo from 1 for 6) ".
+ " NOT SIMILAR TO '49117[4-9]' ".
+ " AND substring(cust_$thing.payinfo from 1 for 6) ".
+ " NOT SIMILAR TO '49118[1-2]' ".
+ " )".
+ " OR substring(cust_$thing.payinfo from 1 for 2) = '51' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2) = '52' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2) = '53' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2) = '54' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2) = '54' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2) = '55' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2) = '36' ". #Diner's int'l processed as Visa/MC inside US
+ " ) ";
+ } elsif ( $cardtype eq 'Amex' ) {
+ $search =
+ " ( substring(cust_$thing.payinfo from 1 for 2 ) = '34' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2 ) = '37' ".
+ " ) ";
+ } elsif ( $cardtype eq 'Discover' ) {
+ $search =
+ " ( substring(cust_$thing.payinfo from 1 for 4 ) = '6011' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2 ) = '65' ".
+ " OR substring(cust_$thing.payinfo from 1 for 3 ) = '622' ". #China Union Pay processed as Discover outside CN
+ " ) ";
+ } elsif ( $cardtype eq 'Maestro' ) {
+ $search =
+ " ( substring(cust_$thing.payinfo from 1 for 2 ) = '63' ".
+ " OR substring(cust_$thing.payinfo from 1 for 2 ) = '67' ".
+ " OR substring(cust_$thing.payinfo from 1 for 6 ) = '564182' ".
+ " OR substring(cust_$thing.payinfo from 1 for 4 ) = '4936' ".
+ " OR substring(cust_$thing.payinfo from 1 for 6 ) ".
+ " SIMILAR TO '49030[2-9]' ".
+ " OR substring(cust_$thing.payinfo from 1 for 6 ) ".
+ " SIMILAR TO '49033[5-9]' ".
+ " OR substring(cust_$thing.payinfo from 1 for 6 ) ".
+ " SIMILAR TO '49110[1-2]' ".
+ " OR substring(cust_$thing.payinfo from 1 for 6 ) ".
+ " SIMILAR TO '49117[4-9]' ".
+ " OR substring(cust_$thing.payinfo from 1 for 6 ) ".
+ " SIMILAR TO '49118[1-2]' ".
+ " ) ";
+ } else {
+ die "unknown card type $cardtype";
+ }
+
+ my $masksearch = $search;
+ $masksearch =~ s/cust_$thing\.payinfo/cust_$thing.paymask/gi;
+
+ push @search,
+ "( $search OR ( cust_$thing.paymask IS NOT NULL AND $masksearch ) )";
+
+ }
+ }
+
+ if ( $cgi->param('payinfo') ) {
+ $cgi->param('payinfo') =~ /^\s*(\d+)\s*$/
+ or die "illegal payinfo ". $cgi->param('payinfo');
+ push @search, "cust_$thing.payinfo = '$1'";
+ }
+
+ #for cust_pay_pending... statusNOT=done
+ if ( $cgi->param('statusNOT') =~ /^(\w+)$/ ) {
+ push @search, "status != '$1'";
+ }
+
+ my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+ push @search, "_date >= $beginning ",
+ "_date <= $ending";
+
+ push @search, FS::UI::Web::parse_lt_gt($cgi, $amount_field );
+
+ $orderby = '_date';
+
+ } elsif ( $cgi->param('magic') eq 'paybatch' ) {
+
+ $cgi->param('paybatch') =~ /^([\w\/\:\-\.]+)$/
+ or die "illegal paybatch: ". $cgi->param('paybatch');
+
+ push @search, "paybatch = '$1'";
+
+ $orderby = "LOWER(company || ' ' || last || ' ' || first )";
+
+ } else {
+ die "unknown search magic: ". $cgi->param('magic');
+ }
+
+ #here is the agent virtualization
+ push @search, $curuser->agentnums_sql;
+
+ my $search = ' WHERE '. join(' AND ', @search);
+
+ $count_query = "SELECT COUNT(*), SUM($amount_field) ".
+ "FROM cust_$thing LEFT JOIN cust_main USING ( custnum )".
+ $search;
+
+ $sql_query = {
+ 'table' => "cust_$thing",
+ 'select' => join(', ',
+ "cust_$thing.*",
+ 'cust_main.custnum as cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'hashref' => {},
+ 'extra_sql' => "$search ORDER BY $orderby",
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ };
+
+} else {
+
+ #hmm... is this still used?
+
+ $cgi->param('payinfo') =~ /^\s*(\d+)\s*$/ or die "illegal payinfo";
+ my $payinfo = $1;
+
+ $cgi->param('payby') =~ /^(\w+)$/ or die "illegal payby";
+ my $payby = $1;
+
+ $count_query = "SELECT COUNT(*), SUM($amount_field) FROM cust_$thing".
+ " WHERE payinfo = '$payinfo' AND payby = '$payby'".
+ " AND ". $curuser->agentnums_sql;
+
+ $sql_query = {
+ 'table' => "cust_$thing",
+ 'hashref' => { 'payinfo' => $payinfo,
+ 'payby' => $payby },
+ 'extra_sql' => $curuser->agentnums_sql.
+ " ORDER BY _date",
+ };
+
+}
+
+my $link = '';
+if ( ( $curuser->access_right('View invoices') #XXX for now
+ || $curuser->access_right('View customer payments')
+ )
+ && ! $opt{'disable_link'}
+ )
+{
+ $link = [ "${p}view/cust_$thing.html?${thing}num=", $thing.'num' ]
+}
+
+my $cust_link = sub {
+ my $cust_thing = shift;
+ $cust_thing->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'custnum' ]
+ : '';
+};
+
+</%init>
diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html
new file mode 100644
index 0000000..8835f8c
--- /dev/null
+++ b/httemplate/search/elements/search.html
@@ -0,0 +1,912 @@
+<%doc>
+
+Example:
+
+ include( 'elements/search.html',
+
+ ###
+ # required
+ ###
+
+ 'title' => 'Page title',
+
+ 'name_singular' => 'item', #singular name for the records returned
+ #OR# # (preferred, will be pluralized automatically)
+ 'name' => 'items', #plural name for the records returned
+ # (deprecated, will be singularlized
+ # simplisticly)
+
+ #literal SQL query string (deprecated?) or qsearch hashref
+ 'query' => {
+ 'table' => 'tablename',
+ #everything else is optional...
+ 'hashref' => { 'field' => 'value',
+ 'field' => { 'op' => '<',
+ 'value' => '54',
+ },
+ },
+ 'select' => '*',
+ 'addl_from' => '', #'LEFT JOIN othertable USING ( key )',
+ 'extra_sql' => '', #'AND otherstuff', #'WHERE onlystuff',
+ 'order_by' => 'ORDER BY something',
+
+ },
+ # "select * from tablename";
+
+ #required unless 'query' is an SQL query string (shouldn't be...)
+ 'count_query' => 'SELECT COUNT(*) FROM tablename',
+
+ ###
+ # recommended / common
+ ###
+
+ #listref of column labels, <TH>
+ #recommended unless 'query' is an SQL query string
+ # (if not specified the database column names will be used)
+ 'header' => [ '#',
+ 'Item',
+ { 'label' => 'Another Item',
+
+ },
+ ],
+
+ #listref - each item is a literal column name (or method) or coderef
+ #if not specified all columns will be shown
+ 'fields' => [
+ 'column',
+ sub { my $row = shift; $row->column; },
+ ],
+
+ #redirect if there's only one item...
+ # listref of URL base and column name (or method)
+ # or a coderef that returns the same
+ 'redirect' => sub { my( $record, $cgi ) = @_;
+ [ popurl(2).'view/item.html', 'primary_key' ];
+ },
+
+ #redirect if there's no items
+ # scalar URL or a coderef that returns a URL
+ 'redirect_empty' => sub { my( $cgi ) = @_;
+ popurl(2).'view/item.html';
+ },
+
+ ###
+ # optional
+ ###
+
+ # some HTML callbacks...
+ 'menubar' => '', #menubar arrayref
+ 'html_init' => '', #after the header/menubar and before the pager
+ 'html_form' => '', #after the pager, right before the results
+ # (only shown if there are results)
+ # (use this for any form-opening tag rather than
+ # html_init, to avoid a nested form)
+ 'html_foot' => '', #at the bottom
+ 'html_posttotal' => '', #at the bottom
+ # (these three can be strings or coderefs)
+
+ 'count_addl' => [], #additional count fields listref of sprintf strings or coderefs
+ # [ $money_char.'%.2f total paid', ],
+
+ #second (smaller) header line, currently only for HTML
+ 'header2 => [ '#',
+ 'Item',
+ { 'label' => 'Another Item',
+
+ },
+ ],
+
+ #listref of column footers
+ 'footer' => [],
+
+ #disabling things
+ 'disable_download' => '', # set true to hide the CSV/Excel download links
+ 'disable_total' => '', # set true to hide the total"
+ 'disable_maxselect' => '', # set true to disable record/page selection
+ 'disable_nonefound' => '', # set true to disable the "No matching Xs found"
+ # message
+
+ #handling "disabled" fields in the records
+ 'disableable' => 1, # set set to 1 (or column position for "disabled"
+ # status col) to enable if this table has a "disabled"
+ # field, to hide disabled records & have
+ # "show disabled/hide disabled" links
+ #(can't be used with a literal query)
+ 'disabled_statuspos' => 3, #optional position (starting from 0) to insert
+ #a Status column when showing disabled records
+ #(query needs to be a qsearch hashref and
+ # header & fields need to be defined)
+
+ #handling agent virtualization
+ 'agent_virt' => 1, # set true if this search should be
+ # agent-virtualized
+ 'agent_null_right' => 'Access Right', # optional right to view global
+ # records
+ 'agent_null_right_link' => 'Access Right' # optional right to link to
+ # global records; defaults to
+ # same as agent_null_right
+ 'agent_pos' => 3, # optional position (starting from 0) to
+ # insert an Agent column (query needs to be a
+ # qsearch hashref and header & fields need to
+ # be defined)
+
+ # link & display properties for fields
+
+ #listref - each item is the empty string,
+ # or a listref of link and method name to append,
+ # or a listref of link and coderef to run and append
+ # or a coderef that returns such a listref
+ 'links' => [],`
+
+ #listref - each item is the empty string,
+ # or a string onClick handler for the corresponding link
+ # or a coderef that returns string onClick handler
+ 'link_onclicks' => [],
+
+ #one letter for each column, left/right/center/none
+ # or pass a listref with full values: [ 'left', 'right', 'center', '' ]
+ 'align' => 'lrc.',
+
+ #listrefs of ( scalars or coderefs )
+ #currently only HTML, maybe eventually Excel too
+ 'color' => [],
+ 'size' => [],
+ 'style' => [], #<B> or <I>, etc.
+ 'cell_style' => [], #STYLE= attribute of TR, very HTML-specific...
+
+ );
+
+</%doc>
+% if ( $type eq 'csv' ) {
+%
+% #http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes
+% http_header('Content-Type' => 'text/plain' );
+%
+% my $csv = new Text::CSV_XS { 'always_quote' => 1,
+% 'eol' => "\n", #"\015\012", #"\012"
+% };
+%
+% $csv->combine(@$header); #or die $csv->status;
+%
+<% $csv->string %>
+%
+%
+% foreach my $row ( @$rows ) {
+%
+% if ( $opt{'fields'} ) {
+%
+% my @line = ();
+%
+% foreach my $field ( @{$opt{'fields'}} ) {
+% if ( ref($field) eq 'CODE' ) {
+% push @line, map {
+% ref($_) eq 'ARRAY'
+% ? '(N/A)' #unimplemented
+% : $_;
+% }
+% &{$field}($row);
+% } else {
+% push @line, $row->$field();
+% }
+% }
+%
+% $csv->combine(@line); #or die $csv->status;
+%
+% } else {
+% $csv->combine(@$row); #or die $csv->status;
+% }
+%
+%
+<% $csv->string %>
+%
+%
+% }
+%
+% #} elsif ( $type eq 'excel' ) {
+% } elsif ( $type =~ /\.xls$/ ) {
+%
+% #http_header('Content-Type' => 'application/excel' ); #eww
+% #http_header('Content-Type' => 'application/msexcel' ); #alas
+% #http_header('Content-Type' => 'application/x-msexcel' ); #?
+%
+% #http://support.microsoft.com/kb/199841
+% http_header('Content-Type' => 'application/vnd.ms-excel' );
+%
+% #http://support.microsoft.com/kb/812935
+% #http://support.microsoft.com/kb/323308
+% $HTML::Mason::Commands::r->headers_out->{'Cache-control'} = 'max-age=0';
+%
+% my $data = '';
+% my $XLS = new IO::Scalar \$data;
+% my $workbook = Spreadsheet::WriteExcel->new($XLS)
+% or die "Error opening .xls file: $!";
+%
+% my $worksheet = $workbook->add_worksheet(substr($opt{'title'},0,31));
+%
+% my($r,$c) = (0,0);
+%
+% $worksheet->write($r, $c++, $_) foreach @$header;
+%
+% foreach my $row ( @$rows ) {
+% $r++;
+% $c = 0;
+%
+% if ( $opt{'fields'} ) {
+%
+% #my $links = $opt{'links'} ? [ @{$opt{'links'}} ] : '';
+% #my $aligns = $opt{'align'} ? [ @{$opt{'align'}} ] : '';
+%
+% foreach my $field ( @{$opt{'fields'}} ) {
+% #my $align = $aligns ? shift @$aligns : '';
+% #$align = " ALIGN=$align" if $align;
+% #my $a = '';
+% #if ( $links ) {
+% # my $link = shift @$links;
+% # $link = &{$link}($row) if ref($link) eq 'CODE';
+% # if ( $link ) {
+% # my( $url, $method ) = @{$link};
+% # if ( ref($method) eq 'CODE' ) {
+% # $a = $url. &{$method}($row);
+% # } else {
+% # $a = $url. $row->$method();
+% # }
+% # $a = qq(<A HREF="$a">);
+% # }
+% #}
+% if ( ref($field) eq 'CODE' ) {
+% foreach my $value ( &{$field}($row) ) {
+% if ( ref($value) eq 'ARRAY' ) {
+% $worksheet->write($r, $c++, '(N/A)' ); #unimplemented
+% } else {
+% $worksheet->write($r, $c++, $value );
+% }
+% }
+% } else {
+% $worksheet->write($r, $c++, $row->$field() );
+% }
+% }
+%
+% } else {
+% $worksheet->write($r, $c++, $_) foreach @$row;
+% }
+%
+% }
+%
+% $workbook->close();# or die "Error creating .xls file: $!";
+%
+% http_header('Content-Length' => length($data) );
+%
+<% $data %>
+%
+%
+% } else { # regular HTML
+%
+% if ( exists($opt{'redirect'}) && scalar(@$rows) == 1 && $total == 1
+% && $type ne 'html-print'
+% ) {
+% my $redirect = $opt{'redirect'};
+% $redirect = &{$redirect}($rows->[0], $cgi) if ref($redirect) eq 'CODE';
+% my( $url, $method ) = @$redirect;
+% redirect( $url. $rows->[0]->$method() );
+% } elsif ( exists($opt{'redirect_empty'}) && ! scalar(@$rows) && $total == 0
+% && $type ne 'html-print'
+% && $opt{'redirect_empty'}
+% && ( ref($opt{'redirect_empty'}) ne 'CODE'
+% || &{$opt{'redirect_empty'}}($cgi) )
+% ) {
+% my $redirect = $opt{'redirect_empty'};
+% $redirect = &{$redirect}($cgi) if ref($redirect) eq 'CODE';
+% redirect( $redirect );
+% } else {
+% if ( $opt{'name_singular'} ) {
+% $opt{'name'} = PL($opt{'name_singular'});
+% }
+% ( my $xlsname = $opt{'name'} ) =~ s/\W//g;
+% if ( $total == 1 ) {
+% if ( $opt{'name_singular'} ) {
+% $opt{'name'} = $opt{'name_singular'}
+% } else {
+% #$opt{'name'} =~ s/s$// if $total == 1;
+% $opt{'name'} =~ s/((s)e)?s$/$2/ if $total == 1;
+% }
+% }
+%
+% if ( $type eq 'html-print' ) {
+
+ <% include( '/elements/header-popup.html', $opt{'title'} ) %>
+
+% } elsif ( $type eq 'select' ) {
+
+ <% include( '/elements/header-popup.html', $opt{'title'} ) %>
+ <% defined($opt{'html_init'})
+ ? ( ref($opt{'html_init'})
+ ? &{$opt{'html_init'}}()
+ : $opt{'html_init'}
+ )
+ : ''
+ %>
+
+% } else {
+%
+% my @menubar = ();
+% if ( $opt{'menubar'} ) {
+% @menubar = @{ $opt{'menubar'} };
+% #} else {
+% # @menubar = ( 'Main menu' => $p );
+% }
+
+ <% include( '/elements/header.html', $opt{'title'},
+ include( '/elements/menubar.html', @menubar )
+ )
+ %>
+
+ <% defined($opt{'html_init'})
+ ? ( ref($opt{'html_init'})
+ ? &{$opt{'html_init'}}()
+ : $opt{'html_init'}
+ )
+ : ''
+ %>
+
+% }
+
+% unless ( $total ) {
+% unless ( $opt{'disable_nonefound'} ) {
+ No matching <% $opt{'name'} %> found.<BR>
+% }
+% }
+%
+% if ( $total || $opt{'disableable'} ) { #hmm... and there *are* ones to show??
+
+ <TABLE>
+ <TR>
+
+ <TD VALIGN="bottom">
+
+ <FORM>
+
+% if (! $opt{'disable_total'}) {
+ <% $total %> total <% $opt{'name'} %>
+% }
+
+% if ( $confmax && $total > $confmax
+% && ! $opt{'disable_maxselect'}
+% && $type ne 'html-print' )
+% {
+% $cgi->delete('maxrecords');
+% $cgi->param('_dummy', 1);
+
+ ( show <SELECT NAME="maxrecords" onChange="window.location = '<% $cgi->self_url %>;maxrecords=' + this.options[this.selectedIndex].value;">
+
+% foreach my $max ( map { $_ * $confmax } qw( 1 5 10 25 ) ) {
+ <OPTION VALUE="<% $max %>" <% ( $maxrecords == $max ) ? 'SELECTED' : '' %>><% $max %></OPTION>
+% }
+
+ </SELECT> per page )
+
+% $cgi->param('maxrecords', $maxrecords);
+% }
+
+% if ( defined($opt{'html_posttotal'}) && $type ne 'html-print' ) {
+ <% ref($opt{'html_posttotal'})
+ ? &{$opt{'html_posttotal'}}()
+ : $opt{'html_posttotal'}
+ %>
+% }
+ <BR>
+
+% if ( $opt{'count_addl'} ) {
+% my $n=0;
+% foreach my $count ( @{$opt{'count_addl'}} ) {
+% my $data = $count_arrayref->[++$n];
+% if ( ref($count) ) {
+ <% &{ $count }( $data ) %>
+% } else {
+ <% sprintf( $count, $data ) %><BR>
+% }
+% }
+% }
+ </FORM>
+
+ </TD>
+
+% unless ( $opt{'disable_download'} || $type eq 'html-print' ) {
+
+ <TD ALIGN="right">
+
+ Download full results<BR>
+
+% $cgi->param('_type', "$xlsname.xls" );
+ as <A HREF="<% $cgi->self_url %>">Excel spreadsheet</A><BR>
+
+% $cgi->param('_type', 'csv');
+ as <A HREF="<% $cgi->self_url %>">CSV file</A><BR>
+
+% $cgi->param('_type', 'html-print');
+ as <A HREF="<% $cgi->self_url %>">printable copy</A>
+
+ <% $opt{'extra_choices_callback'}
+ ? &{$opt{'extra_choices_callback'}}($cgi->query_string)
+ : ''
+ %>
+
+ </TD>
+% $cgi->param('_type', "html" );
+% }
+
+ </TR>
+ <TR>
+ <TD COLSPAN=2>
+
+% my $pager = '';
+% unless ( $type eq 'html_print' ) {
+
+ <% $pager = include( '/elements/pager.html',
+ 'offset' => $offset,
+ 'num_rows' => scalar(@$rows),
+ 'total' => $total,
+ 'maxrecords' => $maxrecords,
+ )
+ %>
+
+ <% defined($opt{'html_form'})
+ ? ( ref($opt{'html_form'})
+ ? &{$opt{'html_form'}}()
+ : $opt{'html_form'}
+ )
+ : ''
+ %>
+
+% }
+
+ <% include('/elements/table-grid.html') %>
+
+ <TR>
+% my $h2 = 0;
+% foreach my $header ( @{ $opt{header} } ) {
+% my $label = ref($header) ? $header->{label} : $header;
+% my $rowspan = 1;
+% my $style = '';
+% if ( $opt{header2} ) {
+% if ( !length($opt{header2}->[$h2]) ) {
+% $rowspan = 2;
+% splice @{ $opt{header2} }, $h2, 1;
+% } else {
+% $h2++;
+% $style = 'STYLE="border-bottom: none"'
+% }
+% }
+ <TH CLASS = "grid"
+ BGCOLOR = "#cccccc"
+ ROWSPAN = "<% $rowspan %>"
+ <% $style %>
+
+ >
+ <% $label %>
+ </TH>
+% }
+ </TR>
+
+% if ( $opt{header2} ) {
+ <TR>
+% foreach my $header ( @{ $opt{header2} } ) {
+% my $label = ref($header) ? $header->{label} : $header;
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE="-1"><% $label %></FONT>
+ </TH>
+% }
+ </TR>
+% }
+
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor;
+%
+% foreach my $row ( @$rows ) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+ <TR>
+
+% if ( $opt{'fields'} ) {
+%
+% my $links = $opt{'links'} ? [ @{$opt{'links'}} ] : '';
+% my $onclicks = $opt{'link_onclicks'} ? [ @{$opt{'link_onclicks'}} ] : [];
+% my $aligns = $opt{'align'} ? [ @{$opt{'align'}} ] : '';
+% my $colors = $opt{'color'} ? [ @{$opt{'color'}} ] : [];
+% my $sizes = $opt{'size'} ? [ @{$opt{'size'}} ] : [];
+% my $styles = $opt{'style'} ? [ @{$opt{'style'}} ] : [];
+% my $cstyles = $opt{'cell_style'} ? [ @{$opt{'cell_style'}} ] : [];
+%
+% foreach my $field (
+%
+% map {
+% if ( ref($_) eq 'ARRAY' ) {
+%
+% my $tableref = $_;
+%
+% '<TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0 WIDTH="100%">'.
+%
+% join('', map {
+%
+% my $rowref = $_;
+%
+% '<tr>'.
+%
+% join('', map {
+%
+% my $e = $_;
+%
+% '<TD '.
+% join(' ', map {
+% uc($_).'="'. $e->{$_}. '"';
+% }
+% grep exists($e->{$_}),
+% qw( align bgcolor colspan rowspan
+% style valign width )
+% ).
+% '>'.
+%
+% ( $e->{'link'}
+% ? '<A HREF="'. $e->{'link'}. '">'
+% : ''
+% ).
+% ( $e->{'size'}
+% ? '<FONT SIZE="'.uc($e->{'size'}).'">'
+% : ''
+% ).
+% ( $e->{'data_style'}
+% ? '<'. uc($e->{'data_style'}). '>'
+% : ''
+% ).
+% $e->{'data'}.
+% ( $e->{'data_style'}
+% ? '</'. uc($e->{'data_style'}). '>'
+% : ''
+% ).
+% ( $e->{'size'} ? '</FONT>' : '' ).
+% ( $e->{'link'} ? '</A>' : '' ).
+% '</td>';
+%
+% } @$rowref ).
+%
+% '</tr>';
+% } @$tableref ).
+%
+% '</table>';
+%
+% } else {
+% $_;
+% }
+% }
+%
+% map {
+% if ( ref($_) eq 'CODE' ) {
+% &{$_}($row);
+% } else {
+% $row->$_();
+% }
+% }
+% @{$opt{'fields'}}
+%
+% ) {
+%
+% my $class = ( $field =~ /^<TABLE/i ) ? 'inv' : 'grid';
+%
+% my $align = $aligns ? shift @$aligns : '';
+% $align = " ALIGN=$align" if $align;
+%
+% my $a = '';
+% if ( $links ) {
+% my $link = shift @$links;
+% my $onclick = shift @$onclicks;
+%
+% if ( ! $opt{'agent_virt'}
+% || ( $null_link && ! $row->agentnum )
+% || grep { $row->agentnum == $_ }
+% @link_agentnums
+% ) {
+%
+% $link = &{$link}($row)
+% if ref($link) eq 'CODE';
+%
+% $onclick = &{$onclick}($row)
+% if ref($onclick) eq 'CODE';
+% $onclick = qq( onClick="$onclick") if $onclick;
+%
+% if ( $link ) {
+% my( $url, $method ) = @{$link};
+% if ( ref($method) eq 'CODE' ) {
+% $a = $url. &{$method}($row);
+% } else {
+% $a = $url. $row->$method();
+% }
+% $a = qq(<A HREF="$a"$onclick>);
+% }
+%
+% }
+%
+% }
+%
+% my $font = '';
+% my $color = shift @$colors;
+% $color = &{$color}($row) if ref($color) eq 'CODE';
+% my $size = shift @$sizes;
+% $size = &{$size}($row) if ref($size) eq 'CODE';
+% if ( $color || $size ) {
+% $font = '<FONT '.
+% ( $color ? "COLOR=#$color " : '' ).
+% ( $size ? qq(SIZE="$size" ) : '' ).
+% '>';
+% }
+%
+% my($s, $es) = ( '', '' );
+% my $style = shift @$styles;
+% $style = &{$style}($row) if ref($style) eq 'CODE';
+% if ( $style ) {
+% $s = join( '', map "<$_>", split('', $style) );
+% $es = join( '', map "</$_>", split('', $style) );
+% }
+%
+% my $cstyle = shift @$cstyles;
+% $cstyle = &{$cstyle}($row) if ref($cstyle) eq 'CODE';
+% $cstyle = qq(STYLE="$cstyle")
+% if $cstyle;
+
+ <TD CLASS="<% $class %>" BGCOLOR="<% $bgcolor %>" <% $align %> <% $cstyle %>><% $font %><% $a %><% $s %><% $field %><% $es %><% $a ? '</A>' : '' %><% $font ? '</FONT>' : '' %></TD>
+
+% }
+%
+% } else {
+%
+% foreach ( @$row ) {
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $_ %></TD>
+% }
+%
+% }
+
+ </TR>
+
+% }
+
+% if ( $opt{'footer'} ) {
+
+ <TR>
+
+% foreach my $footer ( @{ $opt{'footer'} } ) {
+ <TD CLASS="grid" BGCOLOR="#dddddd" STYLE="border-top: dashed 1px black;"><i><% $footer %></i></TD>
+% }
+
+ </TR>
+% }
+
+ </TABLE>
+
+ <% $pager %>
+
+ </TD>
+ </TR>
+ </TABLE>
+% }
+
+% if ( $type eq 'html-print' ) {
+
+ </BODY></HTML>
+
+% } else {
+
+ <% defined($opt{'html_foot'})
+ ? ( ref($opt{'html_foot'})
+ ? &{$opt{'html_foot'}}()
+ : $opt{'html_foot'}
+ )
+ : ''
+ %>
+
+ <% include( '/elements/footer.html' ) %>
+
+% }
+
+% }
+%
+% }
+<%init>
+
+my(%opt) = @_;
+#warn join(' / ', map { "$_ => $opt{$_}" } keys %opt ). "\n";
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my %align = (
+ 'l' => 'left',
+ 'r' => 'right',
+ 'c' => 'center',
+ ' ' => '',
+ '.' => '',
+);
+$opt{align} = [ map $align{$_}, split(//, $opt{align}) ],
+ unless !$opt{align} || ref($opt{align});
+
+$opt{disable_download} = 0
+ if $opt{disable_download} && $curuser->access_right('Configuration download');
+
+my @link_agentnums = ();
+my $null_link = '';
+if ( $opt{'agent_virt'} ) {
+
+ @link_agentnums = $curuser->agentnums;
+ $null_link = $curuser->access_right( $opt{'agent_null_right_link'}
+ || $opt{'agent_null_right'} );
+
+ my $agentnums_sql = $curuser->agentnums_sql(
+ 'null_right' => $opt{'agent_null_right'}
+ );
+
+ $opt{'query'}{'extra_sql'} .=
+ ( $opt{'query'}{'extra_sql'} =~ /WHERE/i || keys %{$opt{'query'}{'hashref'}}
+ ? ' AND '
+ : ' WHERE ' ). $agentnums_sql;
+
+ $opt{'count_query'} .=
+ ( $opt{'count_query'} =~ /WHERE/i ? ' AND ' : ' WHERE ' ). $agentnums_sql;
+
+ if ( $opt{'agent_pos'} || $opt{'agent_pos'} eq '0'
+ and scalar($curuser->agentnums) > 1 ) {
+ #false laziness w/statuspos above
+ my $pos = $opt{'agent_pos'};
+
+ foreach my $att (qw( align style color size )) {
+ $opt{$att} ||= [ map '', @{ $opt{'fields'} } ];
+ }
+
+ splice @{ $opt{'header'} }, $pos, 0, 'Agent';
+ splice @{ $opt{'align'} }, $pos, 0, 'c';
+ splice @{ $opt{'style'} }, $pos, 0, '';
+ splice @{ $opt{'size'} }, $pos, 0, '';
+ splice @{ $opt{'fields'} }, $pos, 0,
+ sub { $_[0]->agentnum ? $_[0]->agent->agent : '(global)'; };
+ splice @{ $opt{'color'} }, $pos, 0, '';
+ splice @{ $opt{'links'} }, $pos, 0, '' #[ 'agent link?', 'agentnum' ]
+ if $opt{'links'};
+ splice @{ $opt{'link_onclicks'} }, $pos, 0, ''
+ if $opt{'link_onclicks'};
+
+ }
+
+}
+
+if ( $opt{'disableable'} ) {
+
+ unless ( $cgi->param('showdisabled') ) { #modify searches
+
+ $opt{'query'}{'hashref'}{'disabled'} = '';
+ $opt{'query'}{'extra_sql'} =~ s/^\s*WHERE/ AND/i;
+
+ $opt{'count_query'} .=
+ ( $opt{'count_query'} =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ "( disabled = '' OR disabled IS NULL )";
+
+ } elsif ( $opt{'disabled_statuspos'}
+ || $opt{'disabled_statuspos'} eq '0' ) { #add status column
+
+ my $pos = $opt{'disabled_statuspos'};
+
+ foreach my $att (qw( align style color size )) {
+ $opt{$att} ||= [ map '', @{ $opt{'fields'} } ];
+ }
+
+ splice @{ $opt{'header'} }, $pos, 0, 'Status';
+ splice @{ $opt{'align'} }, $pos, 0, 'c';
+ splice @{ $opt{'style'} }, $pos, 0, 'b';
+ splice @{ $opt{'size'} }, $pos, 0, '';
+ splice @{ $opt{'fields'} }, $pos, 0,
+ sub { shift->disabled ? 'DISABLED' : 'Active'; };
+ splice @{ $opt{'color'} }, $pos, 0,
+ sub { shift->disabled ? 'FF0000' : '00CC00'; };
+ splice @{ $opt{'links'} }, $pos, 0, ''
+ if $opt{'links'};
+ splice @{ $opt{'link_onlicks'} }, $pos, 0, ''
+ if $opt{'link_onlicks'};
+ }
+
+ #add show/hide disabled links
+ my $items = $opt{'name'} || PL($opt{'name_singular'});
+ if ( $cgi->param('showdisabled') ) {
+ $cgi->param('showdisabled', 0);
+ $opt{'html_posttotal'} .=
+ '( <a href="'. $cgi->self_url. qq!">hide disabled $items</a> )!;
+ $cgi->param('showdisabled', 1);
+ } else {
+ $cgi->param('showdisabled', 1);
+ $opt{'html_posttotal'} .=
+ '( <a href="'. $cgi->self_url. qq!">show disabled $items</a> )!;
+ $cgi->param('showdisabled', 0);
+ }
+
+}
+
+my $type = $cgi->param('_type') =~ /^(csv|\w*\.xls|select|html(-print)?)$/
+ ? $1 : 'html';
+
+my $limit = '';
+my($confmax, $maxrecords, $total, $offset, $count_arrayref);
+
+unless ( $type =~ /^(csv|\w*\.xls)$/ ) {
+
+ unless (exists($opt{count_query}) && length($opt{count_query})) {
+ ( $opt{count_query} = $opt{query} ) =~
+ s/^\s*SELECT\s*(.*?)\s+FROM\s/SELECT COUNT(*) FROM /i; #silly vim:/
+ }
+
+ if ( $opt{disableable} && ! $cgi->param('showdisabled') ) {
+ $opt{count_query} .=
+ ( ( $opt{count_query} =~ /WHERE/i ) ? ' AND ' : ' WHERE ' ).
+ "( disabled = '' OR disabled IS NULL )";
+ }
+
+ unless ( $type eq 'html-print' ) {
+
+ #setup some pagination things if we're in html mode
+
+ my $conf = new FS::Conf;
+ $confmax = $conf->config('maxsearchrecordsperpage');
+ if ( $cgi->param('maxrecords') =~ /^(\d+)$/ ) {
+ $maxrecords = $1;
+ } else {
+ $maxrecords ||= $confmax;
+ }
+
+ $limit = $maxrecords ? "LIMIT $maxrecords" : '';
+
+ $offset = $cgi->param('offset') =~ /^(\d+)$/ ? $1 : 0;
+ $limit .= " OFFSET $offset" if $offset;
+
+ }
+
+ my $count_sth = dbh->prepare($opt{'count_query'})
+ or die "Error preparing $opt{'count_query'}: ". dbh->errstr;
+ $count_sth->execute
+ or die "Error executing $opt{'count_query'}: ". $count_sth->errstr;
+ $count_arrayref = $count_sth->fetchrow_arrayref;
+ $total = $count_arrayref->[0];
+
+}
+
+# run the query
+
+my $header = [ map { ref($_) ? $_->{'label'} : $_ } @{$opt{header}} ];
+my $rows;
+if ( ref($opt{query}) ) {
+
+ if ( $opt{disableable} && ! $cgi->param('showdisabled') ) {
+ #%search = ( 'disabled' => '' );
+ $opt{'query'}->{'hashref'}->{'disabled'} = '';
+ $opt{'query'}->{'extra_sql'} =~ s/^\s*WHERE/ AND/i;
+ }
+
+ #eval "use FS::$opt{'query'};";
+ $rows = [ qsearch({
+ 'select' => $opt{'query'}->{'select'},
+ 'table' => $opt{'query'}->{'table'},
+ 'addl_from' => (exists($opt{'query'}->{'addl_from'}) ? $opt{'query'}->{'addl_from'} : ''),
+ 'hashref' => $opt{'query'}->{'hashref'} || {},
+ 'extra_sql' => $opt{'query'}->{'extra_sql'},
+ 'order_by' => $opt{'query'}->{'order_by'}. " $limit",
+ }) ];
+} else {
+ my $sth = dbh->prepare("$opt{'query'} $limit")
+ or die "Error preparing $opt{'query'}: ". dbh->errstr;
+ $sth->execute
+ or die "Error executing $opt{'query'}: ". $sth->errstr;
+
+ #can get # of rows without fetching them all?
+ $rows = $sth->fetchall_arrayref;
+
+ $header ||= $sth->{NAME};
+}
+
+</%init>
diff --git a/httemplate/search/inventory_item.html b/httemplate/search/inventory_item.html
new file mode 100644
index 0000000..cd37e26
--- /dev/null
+++ b/httemplate/search/inventory_item.html
@@ -0,0 +1,125 @@
+<% include( 'elements/search.html',
+ 'title' => $title,
+
+ #less lame to use Lingua:: something to pluralize
+ 'name' => $inventory_class->classname. 's',
+
+ 'query' => {
+ 'table' => 'inventory_item',
+ 'hashref' => { 'classnum' => $classnum },
+ 'select' => join(', ',
+ 'inventory_item.*',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => $extra_sql,
+ 'addl_from' => $addl_from,
+ },
+
+ 'count_query' => $count_query,
+
+ 'header' => [
+ '#',
+ $inventory_class->classname,
+ 'Service',
+ FS::UI::Web::cust_header(),
+ ],
+
+ 'fields' => [
+ 'itemnum',
+ 'item',
+ #'svcnum', #XXX proper full service customer link ala svc_acct
+ # "unallocated" ? "available" ?
+ sub {
+ #this could be way more efficient with a mixin
+ # like cust_main_Mixin that let us all all the methods
+ # on data we already have...
+ my $inventory_item = shift;
+ my $cust_svc = $inventory_item->cust_svc;
+ if ( $cust_svc ) {
+ my($label, $value) = $cust_svc->label;
+ "$label: $value";
+ } else {
+ '(available)';
+ }
+ },
+
+ \&FS::UI::Web::cust_fields,
+
+ ],
+ 'align' => 'rll'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ '',
+ $link,
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $classnum = $cgi->param('classnum');
+$classnum =~ /^(\d+)$/ or errorpage("illegal classnum $classnum");
+$classnum = $1;
+
+my $inventory_class = qsearchs( {
+ 'table' => 'inventory_class',
+ 'hashref' => { 'classnum' => $classnum },
+} );
+
+my $title = $inventory_class->classname. ' Inventory';
+
+#little false laziness with SQL fragments in inventory_class.pm
+my $extra_sql = '';
+if ( $cgi->param('avail') ) {
+ $extra_sql = 'AND ( svcnum IS NULL OR svcnum = 0 )';
+ $title .= ' - Available';
+} elsif ( $cgi->param('used') ) {
+ $extra_sql = 'AND svcnum IS NOT NULL AND svcnum > 0';
+ $title .= ' - In use';
+}
+
+my $count_query =
+ "SELECT COUNT(*) FROM inventory_item WHERE classnum = $classnum $extra_sql";
+
+my $link = sub {
+ my $inventory_item = shift;
+ if ( $inventory_item->svcnum ) {
+ [ "${p}view/svc_acct.cgi?", 'svcnum' ];
+ } else {
+ '';
+ }
+};
+my $link_cust = sub {
+ my $inventory_item = shift;
+ if ( $inventory_item->custnum ) {
+ [ "${p}view/cust_main.cgi?", 'custnum' ];
+ } else {
+ '';
+ }
+};
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+</%init>
diff --git a/httemplate/search/pay_batch.cgi b/httemplate/search/pay_batch.cgi
new file mode 100755
index 0000000..fb45287
--- /dev/null
+++ b/httemplate/search/pay_batch.cgi
@@ -0,0 +1,130 @@
+<% include( 'elements/search.html',
+ 'title' => 'Payment Batches',
+ 'name_singular' => 'batch',
+ 'query' => { 'table' => 'pay_batch',
+ 'hashref' => $hashref,
+ 'extra_sql' => "$extra_sql ORDER BY batchnum DESC",
+ },
+ 'count_query' => "$count_query $extra_sql",
+ 'header' => [ 'Batch',
+ 'Type',
+ 'First Download',
+ 'Last Upload',
+ 'Item Count',
+ 'Amount',
+ 'Status',
+ ],
+ 'align' => 'rcllrrc',
+ 'fields' => [ 'batchnum',
+ sub {
+ FS::payby->shortname(shift->payby);
+ },
+ sub {
+ my $self = shift;
+ my $_date = $self->download;
+ if ( $_date ) {
+ time2str("%a %b %e %T %Y", $_date);
+ } elsif ( $self->status eq 'O' ) {
+ 'Download batch';
+ } else {
+ '';
+ }
+ },
+ sub {
+ my $self = shift;
+ my $_date = $self->upload;
+ if ( $_date ) {
+ time2str("%a %b %e %T %Y", $_date);
+ } elsif ( $self->status eq 'I' ) {
+ 'Upload results';
+ } else {
+ '';
+ }
+ },
+ sub {
+ my $st = "SELECT COUNT(*) from cust_pay_batch WHERE batchnum=" . shift->batchnum;
+ my $sth = dbh->prepare($st)
+ or die dbh->errstr. "doing $st";
+ $sth->execute
+ or die "Error executing \"$st\": ". $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+ },
+ sub {
+ my $st = "SELECT SUM(amount) from cust_pay_batch WHERE batchnum=" . shift->batchnum;
+ my $sth = dbh->prepare($st)
+ or die dbh->errstr. "doing $st";
+ $sth->execute
+ or die "Error executing \"$st\": ". $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+ },
+ sub {
+ $statusmap{shift->status};
+ },
+ ],
+ 'links' => [
+ $link,
+ '',
+ sub { shift->status eq 'O' ? $link : '' },
+ sub { shift->status eq 'I' ? $link : '' },
+ ],
+ 'size' => [
+ '',
+ '',
+ sub { shift->status eq 'O' ? "+1" : '' },
+ sub { shift->status eq 'I' ? "+1" : '' },
+ ],
+ 'style' => [
+ '',
+ '',
+ sub { shift->status eq 'O' ? "b" : '' },
+ sub { shift->status eq 'I' ? "b" : '' },
+ ],
+ )
+
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports')
+ || $FS::CurrentUser::CurrentUser->access_right('Process batches');
+
+my %statusmap = ('I'=>'In Transit', 'O'=>'Open', 'R'=>'Resolved');
+my $hashref = {};
+my $count_query = 'SELECT COUNT(*) FROM pay_batch';
+
+my($begin, $end) = ( '', '' );
+
+my @where;
+if ( $cgi->param('beginning')
+ && $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/ ) {
+ $begin = str2time($1);
+ push @where, "download >= $begin";
+}
+if ( $cgi->param('ending')
+ && $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/ ) {
+ $end = str2time($1) + 86399;
+ push @where, "download < $end";
+}
+
+my @status;
+if ( $cgi->param('open') ) {
+ push @status, "O";
+}
+
+if ( $cgi->param('intransit') ) {
+ push @status, "I";
+}
+
+if ( $cgi->param('resolved') ) {
+ push @status, "R";
+}
+
+push @where,
+ scalar(@status) ? q!(status='! . join(q!' OR status='!, @status) . q!')!
+ : q!status='X'!; # kludgy, X is unused at present
+
+my $extra_sql = scalar(@where) ? 'WHERE ' . join(' AND ', @where) : '';
+
+my $link = [ "${p}search/cust_pay_batch.cgi?dcln=1;batchnum=", 'batchnum' ];
+
+</%init>
diff --git a/httemplate/search/pay_batch.html b/httemplate/search/pay_batch.html
new file mode 100644
index 0000000..5907169
--- /dev/null
+++ b/httemplate/search/pay_batch.html
@@ -0,0 +1,33 @@
+<% include('/elements/header.html', 'Batch criteria' ) %>
+
+<FORM ACTION="pay_batch.cgi" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE>
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="open" VALUE="1" CHECKED></TD>
+ <TD>Show open batches</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="intransit" VALUE="1" CHECKED></TD>
+ <TD>Show in-transit batches</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="resolved" VALUE="1" CHECKED></TD>
+ <TD>Show resolved batches</TD>
+ </TR>
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Batches">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/phone_avail.html b/httemplate/search/phone_avail.html
new file mode 100644
index 0000000..2388d25
--- /dev/null
+++ b/httemplate/search/phone_avail.html
@@ -0,0 +1,102 @@
+<% include( 'elements/search.html',
+ 'title' => 'Phone Number (DID) Search Results',
+ 'name_singular' => 'phone number',
+ 'query' => {
+ 'table' => 'phone_avail',
+ 'hashref' => {},
+ 'select' => join(', ',
+ 'phone_avail.*',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => $search,
+ 'addl_from' => $addl_from,
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'State',
+ 'Phone Number',
+ 'Export',
+ 'Service',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ 'availnum',
+ 'state',
+ sub { my $pn = shift;
+ '+'. $pn->countrycode. ' '.
+ $pn->npa. ' '. $pn->nxx. '-'. $pn->station;
+ },
+ 'exportnum', #XXX
+ #sub { },
+ 'svcnum', #XXX
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'align' => 'rllll'.FS::UI::Web::cust_aligns(),
+ 'links' => [
+ '',
+ '',
+ '',
+ '', #XXX #$export_link
+ '', #XXX #$svc_link
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my @search = ();
+
+if ( $cgi->param('availbatch') =~ /^([\w\/\:\-\.]+)$/ ) {
+ push @search, "availbatch = '$1'";
+}
+
+# #here is the agent virtualization
+# push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $search = scalar(@search)
+ ? ' WHERE '. join(' AND ', @search)
+ : '';
+
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ #' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+my $count_query = "SELECT COUNT(*) FROM phone_avail $search"; #$addl_from?
+
+my $link_cust = sub {
+ my $phone_avail = shift;
+ if ( $phone_avail->svcnum ) {
+ my $cust_svc = $phone_avail->svc_phone->cust_svc;
+ if ( $cust_svc->pkgnum ) {
+ #my $cust_main = $cust_svc->cust_pkg->cust_main;
+ return [ "${p}view/cust_main.cgi?", 'custnum' ];
+ }
+ }
+ '';
+};
+
+</%init>
diff --git a/httemplate/search/prepay_credit.html b/httemplate/search/prepay_credit.html
new file mode 100644
index 0000000..96391fc
--- /dev/null
+++ b/httemplate/search/prepay_credit.html
@@ -0,0 +1,67 @@
+<% include( 'elements/search.html',
+ 'title' => 'Unused Prepaid Cards'.
+ ($agent ? ' for '. $agent->agent : ''),
+ 'menubar' => [
+ 'Generate cards' => $p.'edit/prepay_credit.cgi',
+ ],
+ 'name' => 'prepaid cards',
+ 'query' => { 'table' => 'prepay_credit',
+ 'hashref' => $hashref,
+ },
+ 'count_query' => $count_query,
+ #'redirect' => $link,
+ 'header' => [ '#', qw(Amount Time Upload Download Total Agent) ],
+ 'fields' => [
+ 'identifier',
+ sub { sprintf('$%.2f', shift->amount ) },
+ sub { my $c = shift;
+ $c->seconds ? duration_exact($c->seconds) : ''
+ },
+ sub { my $c = shift;
+ $c->upbytes
+ ? FS::UI::bytecount::bytecount_unexact($c->upbytes)
+ : ''
+ },
+ sub { my $c = shift;
+ $c->downbytes
+ ? FS::UI::bytecount::bytecount_unexact($c->downbytes)
+ : ''
+ },
+ sub { my $c = shift;
+ $c->totalbytes
+ ? FS::UI::bytecount::bytecount_unexact($c->totalbytes)
+ : ''
+ },
+ sub { my $agent = shift->agent;
+ $agent ? $agent->agent : '';
+ },
+ ],
+ 'links' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ sub { my $agent = shift->agent;
+ $agent ? [ "${p}view/agent.cgi?", 'agentnum' ] : '';
+ },
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $agent = '';
+my $hashref = {};
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+$hashref->{agentnum} = $1;
+$agent = qsearchs('agent', { 'agentnum' => $1 } );
+}
+
+my $count_query = 'SELECT COUNT(*) FROM prepay_credit';
+$count_query .= ' WHERE agentnum = '. $agent->agentnum if $agent;
+
+</%init>
diff --git a/httemplate/search/queue.html b/httemplate/search/queue.html
new file mode 100644
index 0000000..125a6f7
--- /dev/null
+++ b/httemplate/search/queue.html
@@ -0,0 +1,138 @@
+<% include( 'elements/search.html',
+ 'title' => 'Job Queue',
+ 'name' => 'jobs',
+ 'html_form' => qq!<FORM NAME="jobForm" ACTION="$p/misc/queue.cgi" METHOD="POST">!,
+ 'query' => { 'table' => 'queue',
+ 'hashref' => $hashref,
+ 'extra_sql' => 'ORDER BY jobnum',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Job',
+ 'Args',
+ 'Date',
+ 'Status',
+ 'Account', # unless $hashref->{'svcnum'}
+ '', # checkbox column
+ ],
+ 'fields' => [
+ 'jobnum',
+ 'job',
+ sub {
+ my $queue = shift;
+ if ( $dangerous
+ || $queue->job !~ /^FS::part_export::/
+ || !$noactions
+ )
+ {
+ encode_entities( join(' ', $queue->args) );
+ } else {
+ '';
+ }
+ },
+ sub {
+ time2str( "%a %b %e %T %Y", shift->_date );
+ },
+ sub {
+ my $queue = shift;
+ my $jobnum = $queue->jobnum;
+ my $status = $queue->status;
+ $status .= ': '. $queue->statustext
+ if $queue->statustext;
+ my @queue_depend = $queue->queue_depend;
+ $status .= ' (waiting for '.
+ join(', ', map { $_->depend_jobnum }
+ @queue_depend
+ ).
+ ')'
+ if @queue_depend;
+ my $changable = $dangerous
+ || ( ! $noactions
+ && $status =~ /^failed/
+ || $status =~ /^locked/
+ );
+ if ( $changable ) {
+ $status .=
+ qq! (&nbsp;<A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=new">retry</A>&nbsp;|!.
+ qq!&nbsp;<A HREF="$p/misc/queue.cgi?jobnum=$jobnum&action=del">remove</A>&nbsp;)!;
+ }
+ $status;
+ },
+ sub {
+ my $queue = shift;
+ # return '' if $hashref->{'svcnum'}
+ my $cust_svc = $queue->cust_svc;
+ my $account;
+ if ( $cust_svc ) {
+ my $table = $cust_svc->part_svc->svcdb;
+ my $label = ( $cust_svc->label )[1];
+ qq!<A HREF="../view/$table.cgi?!. $queue->svcnum.
+ qq!">$label</A>!;
+ } else {
+ '';
+ }
+ },
+ sub {
+ my $queue = shift;
+ my $jobnum = $queue->jobnum;
+ my $status = $queue->status;
+ my $changable = $dangerous
+ || ( ! $noactions
+ && $status eq 'failed'
+ || $status eq 'locked'
+ );
+ if ( $changable ) {
+ $areboxes = 1;
+ qq!<INPUT NAME="jobnum$jobnum" TYPE="checkbox" VALUE="1">!;
+ } else {
+ '';
+ }
+ },
+ ],
+ #'links' => [
+ # '',
+ # '',
+ # '',
+ # '',
+ # '',
+ # '', #$acct_link,
+ # '',
+ # ],
+ 'html_foot' => sub {
+ if ( $areboxes ) {
+ '<BR><INPUT TYPE="button" VALUE="select all" onClick="setAll(true)">'.
+ '<INPUT TYPE="button" VALUE="unselect all" onClick="setAll(false)">'.
+ '<BR><INPUT TYPE="submit" NAME="action" VALUE="retry selected">'.
+ '<INPUT TYPE="submit" NAME="action" VALUE="remove selected"><BR>'.
+ '<SCRIPT TYPE="text/javascript">'.
+ ' function setAll(setTo) { '.
+ ' theForm = document.jobForm;'.
+ ' for (i=0,n=theForm.elements.length;i<n;i++)'.
+ ' if (theForm.elements[i].name.indexOf("jobnum") != -1)'.
+ ' theForm.elements[i].checked = setTo;'.
+ ' }'.
+ '</SCRIPT>';
+ } else {
+ '';
+ }
+ },
+ )
+
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Job queue');
+
+my $hashref = {};
+
+my $conf = new FS::Conf;
+my $dangerous = $conf->exists('queue_dangerous_controls');
+
+my $noactions = 0;
+
+my $count_query = 'SELECT COUNT(*) FROM queue'; # + $hashref
+
+my $areboxes = 0;
+
+</%init>
diff --git a/httemplate/search/reg_code.html b/httemplate/search/reg_code.html
new file mode 100644
index 0000000..f65b00d
--- /dev/null
+++ b/httemplate/search/reg_code.html
@@ -0,0 +1,40 @@
+<% include( 'elements/search.html',
+ 'title' => 'Unused Registration Codes for '.
+ $agent->agent,
+ 'name' => 'registration codes',
+ 'query' => { 'table' => 'reg_code',
+ 'hashref' => { 'agentnum' => $agentnum, },
+ },
+ 'count_query' => $count_query,
+ #'redirect' => $link,
+ 'header' => [ qw(Code Packages) ],
+ 'fields' => [
+ 'code',
+ sub {
+ map {
+ qq!<A HREF="${p}edit/part_pkg.cgi?!. $_->pkgpart. '">'.
+ $_->pkg. ' - '. $_->comment.
+ '</A><BR>'
+ } $_[0]->part_pkg
+ },
+ ],
+ 'links' => [
+ '',
+ #$plink,
+ '',
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $agentnum = $cgi->param('agentnum');
+$agentnum =~ /^(\d+)$/ or errorpage("illegal agentnum $agentnum");
+$agentnum = $1;
+my $agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+
+my $count_query = "SELECT COUNT(*) FROM reg_code WHERE agentnum = $agentnum";
+
+</%init>
diff --git a/httemplate/search/report_cdr.html b/httemplate/search/report_cdr.html
new file mode 100644
index 0000000..2851631
--- /dev/null
+++ b/httemplate/search/report_cdr.html
@@ -0,0 +1,58 @@
+<% include('/elements/header.html', 'Call Detail Record Search' ) %>
+
+<FORM ACTION="cdr.html" METHOD="GET">
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+ <TR>
+ <TD ALIGN="right">Status: </TD>
+ <TD>
+ <SELECT NAME="freesidestatus">
+ <OPTION VALUE="">(all)
+ <OPTION VALUE="NULL">unprocessed
+ <OPTION VALUE="done">processed
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include ( '/elements/tr-input-beginning_ending.html' ) %>
+
+ <TR>
+ <TD ALIGN="right">Source #: </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="src">
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Destination #: </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="dst">
+ </TD>
+ </TR>
+
+ <% include( '/elements/tr-input-lessthan_greaterthan.html',
+ 'label' => 'Duration (sec)',
+ 'field' => 'duration',
+ )
+ %>
+
+ <% include( '/elements/tr-input-lessthan_greaterthan.html',
+ 'label' => 'Billable duration (sec)',
+ 'field' => 'billsec',
+ )
+ %>
+
+ <% include( '/elements/tr-select-cdrbatch.html' ) %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Search Call Detail Records">
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+
+</%init>
diff --git a/httemplate/search/report_cust_bill.html b/httemplate/search/report_cust_bill.html
new file mode 100644
index 0000000..96cf492
--- /dev/null
+++ b/httemplate/search/report_cust_bill.html
@@ -0,0 +1,36 @@
+<% include('/elements/header.html', 'Invoice Report' ) %>
+
+<FORM ACTION="cust_bill.html" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'label' => 'Invoices for agent: ',
+ 'disable_empty' => 0,
+ )
+ %>
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="open" VALUE="1" CHECKED></TD>
+ <TD>Show only open invoices</TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="newest_percust" VALUE="1"></TD>
+ <TD>Show only the single most recent invoice per-customer</TD>
+ </TR>
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List invoices');
+
+</%init>
diff --git a/httemplate/search/report_cust_credit.html b/httemplate/search/report_cust_credit.html
new file mode 100644
index 0000000..9c719b7
--- /dev/null
+++ b/httemplate/search/report_cust_credit.html
@@ -0,0 +1,48 @@
+<% include('/elements/header.html', 'Credit report' ) %>
+
+<FORM ACTION="cust_credit.html" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE>
+
+ <% include( '/elements/tr-select-otaker.html',
+ 'label' => 'Credits by employee: ',
+ 'otakers' => \@otakers,
+ )
+ %>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'label' => 'for agent: ',
+ 'disable_empty' => 0,
+ )
+ %>
+
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+
+ <% include( '/elements/tr-input-lessthan_greaterthan.html',
+ 'label' => 'Amount',
+ 'field' => 'amount',
+ )
+ %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_credit")
+ or die dbh->errstr;
+$sth->execute or die $sth->errstr;
+my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+
+</%init>
diff --git a/httemplate/search/report_cust_event.html b/httemplate/search/report_cust_event.html
new file mode 100644
index 0000000..e63b637
--- /dev/null
+++ b/httemplate/search/report_cust_event.html
@@ -0,0 +1,65 @@
+<% include(
+ '/elements/header.html',
+ ( $cgi->param('failed') ? 'Failed billing events' : 'Billing events' ),
+ )
+%>
+
+ <FORM ACTION="cust_event.html" METHOD="GET">
+ <INPUT TYPE="hidden" NAME="failed" VALUE="<% $cgi->param('failed') ? 1 : 0 %>">
+ <TABLE>
+
+ <% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %>
+
+ <!--<TR>
+ <TD ALIGN="right">Customer type</TD>
+ <TD><SELECT MULTIPLE NAME="perhaps_payby">
+ <OPTION SELECTED VALUE="CARD">Credit card (automatic)
+ <OPTION SELECTED VALUE="CHEK">E-check (automatic)
+ <OPTION SELECTED VALUE="LECB">Phone bill billing
+ <OPTION SELECTED VALUE="BILL">Billing
+ <OPTION SELECTED VALUE="DCRD">Credit card (on-demand)
+ <OPTION SELECTED VALUE="DCHK">E-check (on-demand)
+ </TD>
+ </TR>
+ -->
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+ <!--
+ <TR>
+ <TD ALIGN="right">Events: </TD>
+ <TD>
+ <SELECT NAME="eventpart">
+ <OPTION SELECTED VALUE=""><% $cgi->param('failed') ? '(all failed events)' : '(all events)' %>
+% #foreach my $part_bill_event ( qsearch( 'part_bill_event', {} ) ) {
+% #}
+
+ </SELECT>
+ </TD>
+ </TR>
+ -->
+<!-- <TR>
+ <TD ALIGN="right">Events for payment type: </TD>
+ <TD>
+ <SELECT NAME="part_bill_event.payby">
+ <OPTION SELECTED VALUE="">(all)
+ <OPTION VALUE="CARD">Credit card (automatic)
+ <OPTION VALUE="BILL">Billing
+ <OPTION VALUE="CHEK">Electronic check (automatic)
+ <OPTION VALUE="DCRD">Credit card (on-demand)
+ <OPTION VALUE="DCHK">Electronic check (on-demand)
+ <OPTION VALUE="LECB">Phone bill billing
+ <OPTION VALUE="COMP">Complimentary
+ </SELECT>
+ </TD>
+ </TR>
+-->
+ </TABLE>
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
+ </FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Billing event reports');
+
+</%init>
diff --git a/httemplate/search/report_cust_main-zip.html b/httemplate/search/report_cust_main-zip.html
new file mode 100644
index 0000000..aa802f3
--- /dev/null
+++ b/httemplate/search/report_cust_main-zip.html
@@ -0,0 +1,53 @@
+<% include('/elements/header.html', 'Zip code report') %>
+
+ <FORM ACTION="cust_main-zip.html" METHOD="GET">
+
+ <TABLE>
+
+ <TR>
+ <TD ALIGN="right">Billing or service zip</TD>
+ <TD>
+ <SELECT NAME="column">
+ <OPTION VALUE="zip">Billing zip
+ <OPTION VALUE="ship_zip">Service zip
+ </SELECT>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Ignore +4 for US zip codes</TD>
+ <TD><INPUT TYPE="checkbox" NAME="ignore_plus4" VALUE="yes" CHECKED> </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Show customers with status:</TD>
+ <TD>
+ <SELECT NAME="status">
+ <OPTION VALUE="">all
+ <OPTION VALUE="prospect">prospect (no packages ever)
+ <OPTION SELECTED VALUE="uncancel">all except cancelled
+ <OPTION VALUE="active">active recurring packages
+ <OPTION VALUE="susp">suspended
+ <OPTION VALUE="cancel">cancelled
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ )
+ %>
+
+ </TABLE>
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
+ </FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List zip codes');
+
+</%init>
diff --git a/httemplate/search/report_cust_main.html b/httemplate/search/report_cust_main.html
new file mode 100755
index 0000000..b0c5fde
--- /dev/null
+++ b/httemplate/search/report_cust_main.html
@@ -0,0 +1,94 @@
+<% include('/elements/header.html', 'Customer Report' ) %>
+
+<FORM ACTION="cust_main.html" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="bill">
+
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Search options</FONT></TH>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar($cgi->param('agentnum')),
+ 'disable_empty' => 0,
+ )
+ %>
+
+ <% include( '/elements/tr-select-cust_main-status.html',
+ 'label' => 'Status'
+ )
+ %>
+
+
+% foreach my $field (qw( signupdate )) {
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center"><% $label{$field} %></TD>
+ <TD>
+ <TABLE>
+ <% include( '/elements/tr-input-beginning_ending.html',
+ prefix => $field,
+ layout => 'horiz',
+ )
+ %>
+ </TABLE>
+ </TD>
+ </TR>
+
+% }
+
+ <% include( '/elements/tr-select-payby.html',
+ 'payby_type' => 'cust',
+ 'multiple' => 1,
+ 'curr_value' => { map { $_ => 1 } FS::payby->cust_payby },
+ )
+ %>
+
+ <% include( '/elements/tr-input-lessthan_greaterthan.html',
+ label => 'Current balance',
+ field => 'current_balance',
+ )
+ %>
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center">Include cancelled packages</TD>
+ <TD><INPUT TYPE="checkbox" NAME="cancelled_pkgs"></TD>
+ </TR>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2>&nbsp;</TH>
+ </TR>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Display options</FONT></TH>
+ </TR>
+ <% include( '/elements/tr-select-cust-fields.html' ) %>
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center">Add package columns</TD>
+ <TD><INPUT TYPE="checkbox" NAME="flattened_pkgs"></TD>
+ </TR>
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless ( $FS::CurrentUser::CurrentUser->access_right('List customers') &&
+ $FS::CurrentUser::CurrentUser->access_right('List packages')
+ );;
+
+</%init>
+<%once>
+
+my %label = (
+ 'signupdate' => 'Signup date',
+);
+
+</%once>
diff --git a/httemplate/search/report_cust_pay.html b/httemplate/search/report_cust_pay.html
new file mode 100644
index 0000000..0627131
--- /dev/null
+++ b/httemplate/search/report_cust_pay.html
@@ -0,0 +1,79 @@
+<% include('/elements/header.html', 'Payment report' ) %>
+
+<FORM ACTION="cust_pay.cgi" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+
+<TABLE>
+
+ <TR>
+ <TD ALIGN="right">Payments of type: </TD>
+ <TD>
+ <SELECT NAME="payby" onChange="payby_changed(this)">
+ <OPTION VALUE="">all</OPTION>
+ <OPTION VALUE="CARD">credit card (all)</OPTION>
+ <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION>
+ <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION>
+ <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION>
+ <OPTION VALUE="CARD-Maestro">credit card (Maestro/Switch/Solo)</OPTION>
+ <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
+ <OPTION VALUE="BILL">check</OPTION>
+ <OPTION VALUE="PREP">prepaid card</OPTION>
+ <OPTION VALUE="CASH">cash</OPTION>
+ <OPTION VALUE="WEST">Western Union</OPTION>
+ <OPTION VALUE="MCRD">manual credit card</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <SCRIPT TYPE="text/javascript">
+
+ function payby_changed(what) {
+ if ( what.options[what.selectedIndex].value == 'BILL' ) {
+ document.getElementById('checkno_caption').style.color = '#000000';
+ what.form.payinfo.disabled = false;
+ what.form.payinfo.style.backgroundColor = '#ffffff';
+ } else {
+ document.getElementById('checkno_caption').style.color = '#bbbbbb';
+ what.form.payinfo.disabled = true;
+ what.form.payinfo.style.backgroundColor = '#dddddd';
+ }
+ }
+
+ </SCRIPT>
+
+ <TR>
+ <TD ALIGN="right"><FONT ID="checkno_caption" COLOR="#bbbbbb">Check #: </FONT></TD>
+ <TD>
+ <INPUT TYPE="text" NAME="payinfo" DISABLED STYLE="background-color: #dddddd">
+ </TD>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar($cgi->param('agentnum')),
+ 'label' => 'for agent: ',
+ 'disable_empty' => 0,
+ )
+ %>
+
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+
+ <% include( '/elements/tr-input-lessthan_greaterthan.html',
+ 'label' => 'Amount',
+ 'field' => 'paid',
+ )
+ %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/report_cust_pay_batch.html b/httemplate/search/report_cust_pay_batch.html
new file mode 100644
index 0000000..2d3ef06
--- /dev/null
+++ b/httemplate/search/report_cust_pay_batch.html
@@ -0,0 +1,44 @@
+<% include('/elements/header.html', 'Batch payment report' ) %>
+
+<FORM ACTION="cust_pay_batch.cgi" METHOD="GET">
+
+<TABLE>
+
+ <TR>
+ <TD ALIGN="right">Payments of type: </TD>
+ <TD>
+ <SELECT NAME="payby">
+ <OPTION VALUE="">all</OPTION>
+ <OPTION VALUE="CARD">credit card</OPTION>
+ <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0
+ )
+ %>
+
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="dcln" VALUE="1" CHECKED></TD>
+ <TD>Include approved items</TD>
+ </TR>
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/report_cust_pkg.html b/httemplate/search/report_cust_pkg.html
new file mode 100755
index 0000000..aef1c24
--- /dev/null
+++ b/httemplate/search/report_cust_pkg.html
@@ -0,0 +1,149 @@
+<% include('/elements/header.html', 'Package Report' ) %>
+
+<FORM ACTION="cust_pkg.cgi" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="bill">
+
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left">
+ <FONT SIZE="+1">Search options</FONT>
+ </TH>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'disable_empty' => 0,
+ )
+ %>
+
+ <% include( '/elements/tr-select-cust_pkg-status.html',
+ 'onchange' => 'status_changed(this);',
+ )
+ %>
+
+ <SCRIPT TYPE="text/javascript">
+
+ function status_changed(what) {
+
+% foreach my $status ( '', FS::cust_pkg->statuses() ) {
+
+ if ( what.options[what.selectedIndex].value == '<% $status %>' ) {
+
+% foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
+% if ( $disable{$status}->{$field} ) {
+
+ what.form.<% $field %>_beginning_text.disabled = true;
+ what.form.<% $field %>_ending_text.disabled = true;
+ what.form.<% $field %>_beginning_text.style.backgroundColor = '#dddddd';
+ what.form.<% $field %>_ending_text.style.backgroundColor = '#dddddd';
+
+ what.form.<% $field %>_beginning_button.style.display = 'none';
+ what.form.<% $field %>_ending_button.style.display = 'none';
+ what.form.<% $field %>_beginning_disabled.style.display = '';
+ what.form.<% $field %>_ending_disabled.style.display = '';
+
+% } else {
+
+ what.form.<% $field %>_beginning_text.disabled = false;
+ what.form.<% $field %>_ending_text.disabled = false;
+ what.form.<% $field %>_beginning_text.style.backgroundColor = '#ffffff';
+ what.form.<% $field %>_ending_text.style.backgroundColor = '#ffffff';
+
+ what.form.<% $field %>_beginning_button.style.display = '';
+ what.form.<% $field %>_ending_button.style.display = '';
+ what.form.<% $field %>_beginning_disabled.style.display = 'none';
+ what.form.<% $field %>_ending_disabled.style.display = 'none';
+
+% }
+% }
+
+ }
+
+% }
+
+ }
+
+ </SCRIPT>
+
+ <% include( '/elements/tr-select-pkg_class.html',
+ 'pre_options' => [ '0' => 'all' ],
+ 'empty_label' => '(empty class)',
+ )
+ %>
+
+% foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center"><% $label{$field} %></TD>
+ <TD>
+ <TABLE>
+ <% include( '/elements/tr-input-beginning_ending.html',
+ prefix => $field,
+ layout => 'horiz',
+ )
+ %>
+ </TABLE>
+ </TD>
+ </TR>
+
+% }
+
+ <% include( '/elements/tr-selectmultiple-part_pkg.html' ) %>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2>&nbsp;</TH>
+ </TR>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Display options</FONT></TH>
+ </TR>
+ <% include( '/elements/tr-select-cust-fields.html' ) %>
+
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List packages');
+
+</%init>
+<%once>
+
+my %label = (
+ 'setup' => 'Setup',
+ 'last_bill' => 'Last bill',
+ 'bill' => 'Next bill',
+ 'adjourn' => 'Adjourns',
+ 'susp' => 'Suspended',
+ 'expire' => 'Expires',
+ 'cancel' => 'Cancelled',
+);
+
+#false laziness w/cust_pkg.cgi
+my %disable = (
+ 'all' => {},
+ 'one-time charge' => { 'last_bill'=>1, 'bill'=>1, 'adjourn'=>1, 'susp'=>1, 'expire'=>1, 'cancel'=>1, },
+ 'active' => { 'susp'=>1, 'cancel'=>1 },
+ 'suspended' => { 'cancel' => 1 },
+ 'cancelled' => {},
+ '' => {},
+);
+
+#hmm?
+my %checkbox = (
+ 'setup' => 0,
+ 'last_bill' => 0,
+ 'bill' => 0,
+ 'susp' => 1,
+ 'expire' => 1,
+ 'cancel' => 1,
+);
+
+</%once>
diff --git a/httemplate/search/report_newtax.cgi b/httemplate/search/report_newtax.cgi
new file mode 100755
index 0000000..586fddd
--- /dev/null
+++ b/httemplate/search/report_newtax.cgi
@@ -0,0 +1,158 @@
+<% include("/elements/header.html", "$agentname Tax Report - ".
+ ( $beginning
+ ? time2str('%h %o %Y ', $beginning )
+ : ''
+ ).
+ 'through '.
+ ( $ending == 4294967295
+ ? 'now'
+ : time2str('%h %o %Y', $ending )
+ )
+ )
+%>
+
+<% include('/elements/table-grid.html') %>
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Tax collected</TH>
+ </TR>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor;
+%
+% foreach my $tax ( @taxes ) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% my $link = '';
+% if ( $tax->{'label'} ne 'Total' ) {
+% $link = ';'. $tax->{'url_param'};
+% }
+%
+
+ <TR>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $tax->{'label'} %></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="right">
+ <A HREF="<% $baselink. $link %>;istax=1"><% $money_char %><% sprintf('%.2f', $tax->{'tax'} ) %></A>
+ </TD>
+ </TR>
+% }
+
+</TABLE>
+
+</BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+
+my $join_cust = "
+ JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum )
+";
+my $from_join_cust = "
+ FROM cust_bill_pkg
+ $join_cust
+";
+
+my $where = "WHERE _date >= $beginning AND _date <= $ending ";
+
+my $agentname = '';
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "agent not found" unless $agent;
+ $agentname = $agent->agent;
+ $where .= ' AND cust_main.agentnum = '. $agent->agentnum;
+}
+
+my $tax = 0;
+my %taxes = ();
+foreach my $t (qsearch({ table => 'cust_bill_pkg',
+ hashref => { pkgpart => 0 },
+ addl_from => $join_cust,
+ extra_sql => $where,
+ })
+ )
+{
+ #warn $t->itemdesc. "\n";
+
+ my $label = $t->itemdesc;
+ $label ||= 'Tax';
+ $taxes{$label}->{'label'} = $label;
+ $taxes{$label}->{'url_param'} = "itemdesc=$label";
+
+ # calculate total for this tax
+ # calculate customer-exemption for this tax
+ # calculate package-exemption for this tax
+ # calculate monthly exemption (texas tax) for this tax
+ # count up all the cust_tax_exempt_pkg records associated with
+ # the actual line items.
+}
+
+
+foreach my $t (qsearch({ table => 'cust_bill_pkg',
+ select => 'DISTINCT itemdesc',
+ hashref => { pkgpart => 0 },
+ addl_from => $join_cust,
+ extra_sql => $where,
+ })
+ )
+{
+
+ my $label = $t->itemdesc;
+ $label ||= 'Tax';
+ my @taxparam = ( 'itemdesc' );
+ my $taxwhere = "$from_join_cust $where AND payby != 'COMP' ".
+ "AND itemdesc = ?" ;
+
+ my $sql = "SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur) ".
+ " $taxwhere AND pkgnum = 0";
+
+ my $x = scalar_sql($t, \@taxparam, $sql );
+ $tax += $x;
+ $taxes{$label}->{'tax'} += $x;
+
+}
+
+#ordering
+my @taxes =
+ map $taxes{$_},
+ sort { ($b cmp $a) }
+ keys %taxes;
+
+push @taxes, {
+ 'label' => 'Total',
+ 'url_param' => '',
+ 'tax' => $tax,
+};
+
+#--
+
+#false laziness w/FS::Report::Table::Monthly (sub should probably be moved up
+#to FS::Report or FS::Record or who the fuck knows where)
+sub scalar_sql {
+ my( $r, $param, $sql ) = @_;
+ #warn "$sql\n";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute( map $r->$_(), @$param )
+ or die "Unexpected error executing statement $sql: ". $sth->errstr;
+ $sth->fetchrow_arrayref->[0] || 0;
+}
+
+my $dateagentlink = "begin=$beginning;end=$ending";
+$dateagentlink .= ';agentnum='. $cgi->param('agentnum')
+ if length($agentname);
+my $baselink = $p. "search/cust_bill_pkg.cgi?$dateagentlink";
+
+</%init>
diff --git a/httemplate/search/report_newtax.html b/httemplate/search/report_newtax.html
new file mode 100755
index 0000000..daf2d23
--- /dev/null
+++ b/httemplate/search/report_newtax.html
@@ -0,0 +1,23 @@
+<% include('/elements/header.html', 'Tax Report' ) %>
+
+<FORM ACTION="report_newtax.cgi" METHOD="GET">
+
+<TABLE>
+
+ <% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %>
+
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/report_prepaid_income.cgi b/httemplate/search/report_prepaid_income.cgi
new file mode 100644
index 0000000..27dbcbf
--- /dev/null
+++ b/httemplate/search/report_prepaid_income.cgi
@@ -0,0 +1,87 @@
+<% include("/elements/header.html", 'Prepaid Income (Unearned Revenue) Report') %>
+
+<% table() %>
+ <TR>
+ <TH>Actual Unearned Revenue</TH>
+ <TH>Legacy Unearned Revenue</TH>
+ </TR>
+ <TR>
+ <TD ALIGN="right">$<% $total %>
+ <TD ALIGN="right">
+ <% $now == $time ? "\$$total_legacy" : '<i>N/A</i>'%>
+ </TD>
+ </TR>
+
+</TABLE>
+<BR>
+Actual unearned revenue is the amount of unearned revenue Freeside has
+actually invoiced for packages with longer-than monthly terms.
+<BR><BR>
+Legacy unearned revenue is the amount of unearned revenue represented by
+customer packages. This number may be larger than actual unearned
+revenue if you have imported longer-than monthly customer packages from
+a previous billing system.
+</BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+#doesn't yet deal with daily/weekly packages
+
+#needs to be re-written in sql for efficiency
+
+my $time = time;
+
+my $now = $cgi->param('date') && str2time($cgi->param('date')) || $time;
+$now =~ /^(\d+)$/ or die "unparsable date?";
+$now = $1;
+
+my( $total, $total_legacy ) = ( 0, 0 );
+
+my @cust_bill_pkg =
+ grep { $_->cust_pkg && $_->cust_pkg->part_pkg->freq !~ /^([01]|\d+[dw])$/ }
+ qsearch( 'cust_bill_pkg', {
+ 'recur' => { op=>'!=', value=>0 },
+ 'edate' => { op=>'>', value=>$now },
+ }, );
+
+my @cust_pkg =
+ grep { $_->part_pkg->recur != 0
+ && $_->part_pkg->freq !~ /^([01]|\d+[dw])$/
+ }
+ qsearch ( 'cust_pkg', {
+ 'bill' => { op=>'>', value=>$now }
+ } );
+
+foreach my $cust_bill_pkg ( @cust_bill_pkg) {
+ my $period = $cust_bill_pkg->edate - $cust_bill_pkg->sdate;
+
+ my $elapsed = $now - $cust_bill_pkg->sdate;
+ $elapsed = 0 if $elapsed < 0;
+
+ my $remaining = 1 - $elapsed/$period;
+
+ my $unearned = $remaining * $cust_bill_pkg->recur;
+ $total += $unearned;
+
+}
+
+foreach my $cust_pkg ( @cust_pkg ) {
+ my $period = $cust_pkg->bill - $cust_pkg->last_bill;
+
+ my $elapsed = $now - $cust_pkg->last_bill;
+ $elapsed = 0 if $elapsed < 0;
+
+ my $remaining = 1 - $elapsed/$period;
+
+ my $unearned = $remaining * $cust_pkg->part_pkg->recur; #!! only works for flat/legacy
+ $total_legacy += $unearned;
+
+}
+
+$total = sprintf('%.2f', $total);
+$total_legacy = sprintf('%.2f', $total_legacy);
+
+</%init>
diff --git a/httemplate/search/report_prepaid_income.html b/httemplate/search/report_prepaid_income.html
new file mode 100644
index 0000000..81adb64
--- /dev/null
+++ b/httemplate/search/report_prepaid_income.html
@@ -0,0 +1,43 @@
+<% include('/elements/header.html', 'Prepaid Income (Unearned Revenue) Report',
+ '',
+ '',
+ '<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
+ '
+) %>
+
+ <FORM ACTION="report_prepaid_income.cgi" METHOD="GET">
+ <TABLE>
+ <TR>
+ <TD>Prepaid income (unearned revenue) as of </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="date" ID="date_text" VALUE="now">
+ <IMG SRC="../images/calendar.png" ID="date_button" STYLE="cursor: pointer" TITLE="Select date">
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ </TD>
+ <TD><i>m/d/y</i></TD>
+ </TR>
+ </TABLE>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "date_text",
+ ifFormat: "%m/%d/%Y",
+ button: "date_button",
+ align: "BR"
+ });
+</SCRIPT>
+
+<INPUT TYPE="submit" VALUE="Generate report">
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/report_receivables.cgi b/httemplate/search/report_receivables.cgi
new file mode 100755
index 0000000..58d87fa
--- /dev/null
+++ b/httemplate/search/report_receivables.cgi
@@ -0,0 +1,197 @@
+<% include( 'elements/search.html',
+ 'title' => 'Accounts Receivable Aging Summary',
+ 'name' => 'customers',
+ 'query' => $sql_query,
+ 'count_query' => $count_sql,
+ 'header' => [
+ FS::UI::Web::cust_header(),
+ '0-30',
+ '30-60',
+ '60-90',
+ '90+',
+ 'Total',
+ ],
+ 'footer' => [
+ 'Total',
+ ( map '',
+ ( 1 ..
+ scalar(FS::UI::Web::cust_header()-1)
+ )
+ ),
+ sprintf( $money_char.'%.2f',
+ $row->{'balance_0_30'} ),
+ sprintf( $money_char.'%.2f',
+ $row->{'balance_30_60'} ),
+ sprintf( $money_char.'%.2f',
+ $row->{'balance_60_90'} ),
+ sprintf( $money_char.'%.2f',
+ $row->{'balance_90_0'} ),
+ sprintf( '<b>'. $money_char.'%.2f'. '</b>',
+ $row->{'balance_0_0'} ),
+ ],
+ 'fields' => [
+ \&FS::UI::Web::cust_fields,
+ format_balance('0_30'),
+ format_balance('30_60'),
+ format_balance('60_90'),
+ format_balance('90_0'),
+ format_balance('0_0'),
+ ],
+ 'links' => [
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ '',
+ '',
+ '',
+ '',
+ '',
+ ],
+ #'align' => 'rlccrrrrr',
+ 'align' => FS::UI::Web::cust_aligns(). 'rrrrr',
+ #'size' => [ '', '', '-1', '-1', '', '', '', '', '', ],
+ #'style' => [ '', '', 'b', 'b', '', '', '', '', 'b', ],
+ 'size' => [ ( map '', FS::UI::Web::cust_header() ),
+ #'-1', '', '', '', '', '', ],
+ '', '', '', '', '', ],
+ 'style' => [ FS::UI::Web::cust_styles(),
+ #'b', '', '', '', '', 'b', ],
+ '', '', '', '', 'b', ],
+ 'color' => [
+ FS::UI::Web::cust_colors(),
+ '',
+ '',
+ '',
+ '',
+ '',
+ ],
+
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my @ranges = (
+ [ 0, 30 ],
+ [ 30, 60 ],
+ [ 60, 90 ],
+ [ 90, 0 ],
+ [ 0, 0 ],
+);
+
+my $owed_cols = join(',', map balance( @$_ ), @ranges );
+
+my $select_count_pkgs = FS::cust_main->select_count_pkgs_sql;
+
+my $active_sql = FS::cust_pkg->active_sql;
+my $inactive_sql = FS::cust_pkg->inactive_sql;
+my $suspended_sql = FS::cust_pkg->suspended_sql;
+my $cancelled_sql = FS::cust_pkg->cancelled_sql;
+
+my $packages_cols = <<END;
+ ( $select_count_pkgs ) AS num_pkgs_sql,
+ ( $select_count_pkgs AND $active_sql ) AS active_pkgs,
+ ( $select_count_pkgs AND $inactive_sql ) AS inactive_pkgs,
+ ( $select_count_pkgs AND $suspended_sql ) AS suspended_pkgs,
+ ( $select_count_pkgs AND $cancelled_sql ) AS cancelled_pkgs
+END
+
+my @where = ();
+
+unless ( $cgi->param('all_customers') ) {
+
+ my $days = 0;
+ if ( $cgi->param('days') =~ /^\s*(\d+)\s*$/ ) {
+ $days = $1;
+ }
+
+ push @where, balance($days, 0, 'no_as'=>1). ' > 0'; # != 0';
+
+}
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ my $agentnum = $1;
+ push @where, "agentnum = $agentnum";
+}
+
+#here is the agent virtualization
+push @where, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $where = join(' AND ', @where);
+$where = "WHERE $where" if $where;
+
+my $count_sql = "select count(*) from cust_main $where";
+
+my $sql_query = {
+ 'table' => 'cust_main',
+ 'hashref' => {},
+ 'select' => join(',',
+ #'cust_main.*',
+ 'custnum',
+ $owed_cols,
+ $packages_cols,
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => $where,
+ 'order_by' => "order by coalesce(lower(company), ''), lower(last)",
+};
+
+my $total_sql = "SELECT ". join(',', map balance( @$_, 'sum'=>1 ), @ranges).
+ " FROM cust_main $where";
+
+my $total_sth = dbh->prepare($total_sql) or die dbh->errstr;
+$total_sth->execute or die "error executing $total_sql: ". $total_sth->errstr;
+my $row = $total_sth->fetchrow_hashref();
+
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+</%init>
+<%once>
+
+my $conf = new FS::Conf;
+
+my $money_char = $conf->config('money_char') || '$';
+
+#Example:
+#
+# my $balance = balance(
+# $start, $end,
+# 'no_as' => 1, #set to true when using in a WHERE clause (supress AS clause)
+# #or 0 / omit when using in a SELECT clause as a column
+# # ("AS balance_$start_$end")
+# 'sum' => 1, #set to true to get a SUM() of the values, for totals
+#
+# #obsolete? options for totals (passed to cust_main::balance_date_sql)
+# 'total' => 1, #set to true to remove all customer comparison clauses
+# 'join' => $join, #JOIN clause
+# 'where' => \@where, #WHERE clause hashref (elements "AND"ed together)
+# )
+
+sub balance {
+ my($start, $end, %opt) = @_;
+
+ my $as = $opt{'no_as'} ? '' : " AS balance_${start}_$end";
+
+ #handle start and end ranges (86400 = 24h * 60m * 60s)
+ my $str2time = str2time_sql;
+ my $closing = str2time_sql_closing;
+ $start = $start ? "( $str2time now() $closing - ".($start * 86400). ' )' : '';
+ $end = $end ? "( $str2time now() $closing - ".($end * 86400). ' )' : '';
+
+ $opt{'unapplied_date'} = 1;
+
+ ( $opt{sum} ? 'SUM( ' : '' ).
+ FS::cust_main->balance_date_sql( $start, $end, %opt ).
+ ( $opt{sum} ? ' )' : '' ).
+ $as;
+
+}
+
+sub format_balance { #closures help alot
+ my $range = shift;
+ sub { sprintf( $money_char.'%.2f', shift->get("balance_$range") ) };
+}
+
+</%once>
diff --git a/httemplate/search/report_receivables.html b/httemplate/search/report_receivables.html
new file mode 100755
index 0000000..19b1ee7
--- /dev/null
+++ b/httemplate/search/report_receivables.html
@@ -0,0 +1,35 @@
+<% include('/elements/header.html', 'Accounts Receivable Aging Summary' ) %>
+
+<FORM NAME="OneTrueForm" ACTION="report_receivables.cgi" METHOD="GET">
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left">
+ <FONT SIZE="+1">Search options</FONT>
+ </TH>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %>
+
+ <TR>
+ <TD ALIGN="right">Customers</TD>
+ <TD>
+ <INPUT TYPE="radio" NAME="all_customers" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.days.disabled=true; document.OneTrueForm.days.style.backgroundColor = '#dddddd'; } else { document.OneTrueForm.days.disabled=false; document.OneTrueForm.days.style.backgroundColor = '#ffffff'; }">All customers (even those without an outstanding balance)<BR>
+ <INPUT TYPE="radio" NAME="all_customers" VALUE="0" CHECKED onClick="if ( ! this.checked ) { document.OneTrueForm.days.disabled=true; document.OneTrueForm.days.style.backgroundColor = '#dddddd'; } else { document.OneTrueForm.days.disabled=false; document.OneTrueForm.days.style.backgroundColor = '#ffffff'; }">Customers with a balance over <INPUT NAME="days" TYPE="text" SIZE=4 MAXLENGTH=3 VALUE="0"> days old
+ </TD>
+ </TR>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Get Report">
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/report_rt_transaction.html b/httemplate/search/report_rt_transaction.html
new file mode 100644
index 0000000..9b7b7cb
--- /dev/null
+++ b/httemplate/search/report_rt_transaction.html
@@ -0,0 +1,24 @@
+<% include('/elements/header.html', 'Time worked report criteria' ) %>
+
+<FORM ACTION="rt_transaction.html" METHOD="GET">
+
+<TABLE>
+
+ <% include ( '/elements/tr-input-beginning_ending.html' ) %>
+
+ <% include ( '/elements/tr-select-otaker.html' ) %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Search">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+
+</%init>
diff --git a/httemplate/search/report_sql.html b/httemplate/search/report_sql.html
new file mode 100644
index 0000000..9953308
--- /dev/null
+++ b/httemplate/search/report_sql.html
@@ -0,0 +1,23 @@
+<% include('/elements/header.html', 'SQL Query' ) %>
+
+<FORM ACTION="sql.html" METHOD="GET">
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+ <TR>
+ <TD ALIGN="right" VALIGN="top">SELECT </TD>
+ <TD><TEXTAREA NAME="sql"></TEXTAREA></TD>
+ </TR>
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Query">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Raw SQL');
+
+</%init>
diff --git a/httemplate/search/report_svc_acct.html b/httemplate/search/report_svc_acct.html
new file mode 100755
index 0000000..59fd1f8
--- /dev/null
+++ b/httemplate/search/report_svc_acct.html
@@ -0,0 +1,105 @@
+<% include('/elements/header.html', 'Account Report' ) %>
+
+<FORM ACTION="svc_acct.cgi" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="advanced">
+
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Search options</FONT></TH>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'disable_empty' => 0,
+ )
+ %>
+
+ <% include( '/elements/tr-select-domain.html',
+ 'element_name' => 'domsvc',
+ 'curr_value' => scalar( $cgi->param('domsvc') ),
+ 'disable_empty' => 0,
+ )
+ %>
+
+
+ <SCRIPT type="text/javascript">
+ function toggle(what) {
+ label = document.getElementById (what + '_label');
+ field = document.getElementById ( what + '_invert');
+ if (field.value == 1) {
+ field.value = 0;
+ } else {
+ field.value = 1;
+ }
+ if (field.value == 1) {
+ label.firstChild.nodeValue = 'Did not ' + label.firstChild.nodeValue;
+ }else{
+ text = label.firstChild.nodeValue;
+ label.firstChild.nodeValue = text.replace(/Did not /, '');
+ }
+ }
+ </SCRIPT>
+% foreach my $field (qw( last_login last_logout )) {
+% my $invert = $field."_invert";
+
+ <TR>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD ALIGN="right" VALIGN="center" ID="<% $field."_label" %>">
+ <% $label{$field} %>
+ </TD>
+ <TD>
+ <INPUT NAME="<% $invert %>" ID="<% $invert %>" TYPE="hidden">
+ <A HREF="javascript:void(0)" onClick="toggle('<% $field %>'); return false;">Invert</A>
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+ <TD>
+ <TABLE>
+ <% include( '/elements/tr-input-beginning_ending.html',
+ prefix => $field,
+ layout => 'horiz',
+ )
+ %>
+ </TABLE>
+ </TD>
+ </TR>
+
+% }
+
+ <% include( '/elements/tr-selectmultiple-part_pkg.html' ) %>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2>&nbsp;</TH>
+ </TR>
+
+ <TR>
+ <TH BGCOLOR="#e8e8e8" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Display options</FONT></TH>
+ </TR>
+ <% include( '/elements/tr-select-cust-fields.html' ) %>
+
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List packages');
+
+</%init>
+<%once>
+
+my %label = (
+ 'last_login' => 'Last login',
+ 'last_logout' => 'Last logout',
+);
+
+</%once>
diff --git a/httemplate/search/report_tax.cgi b/httemplate/search/report_tax.cgi
new file mode 100755
index 0000000..a7630dd
--- /dev/null
+++ b/httemplate/search/report_tax.cgi
@@ -0,0 +1,612 @@
+<% include("/elements/header.html", "$agentname Sales Tax Report - ".
+ ( $beginning
+ ? time2str('%h %o %Y ', $beginning )
+ : ''
+ ).
+ 'through '.
+ ( $ending == 4294967295
+ ? 'now'
+ : time2str('%h %o %Y', $ending )
+ )
+ )
+%>
+
+<% include('/elements/table-grid.html') %>
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=9>Sales</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2>Rate</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2>Tax owed</TH>
+% unless ( $cgi->param('show_taxclasses') ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2>Tax invoiced</TH>
+% }
+ </TR>
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Total</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Non-taxable<BR><FONT SIZE=-1>(tax-exempt customer)</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Non-taxable<BR><FONT SIZE=-1>(tax-exempt package)</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Non-taxable<BR><FONT SIZE=-1>(monthly exemption)</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Taxable</TH>
+ </TR>
+
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor;
+%
+% foreach my $region ( @regions ) {
+%
+% my $link = '';
+% if ( $region->{'label'} ne 'Total' ) {
+% if ( $region->{'label'} eq $out ) {
+% $link = ';out=1';
+% } else {
+% $link = ';'. $region->{'url_param'};
+% }
+% }
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% #my $diff = 0;
+% my $hicolor = $bgcolor;
+% unless ( $cgi->param('show_taxclasses') ) {
+% my $diff = abs( sprintf( '%.2f', $region->{'owed'} )
+% - sprintf( '%.2f', $region->{'tax'} )
+% );
+% if ( $diff > 0.02 ) {
+% # $hicolor = $hicolor eq '#eeeeee' ? '#eeee66' : '#ffff99';
+% #} elsif ( $diff ) {
+% $hicolor = $hicolor eq '#eeeeee' ? '#eeee99' : '#ffffcc';
+% }
+% }
+%
+%
+% my $td = qq(TD CLASS="grid" BGCOLOR="$bgcolor");
+% my $tdh = qq(TD CLASS="grid" BGCOLOR="$hicolor");
+% my $bigmath = '<FONT FACE="sans-serif" SIZE="+1"><B>';
+% my $bme = '</B></FONT>';
+
+ <TR>
+ <<%$td%>><% $region->{'label'} %></TD>
+ <<%$td%> ALIGN="right">
+ <A HREF="<% $baselink. $link %>;nottax=1"
+ ><% &$money_sprintf( $region->{'total'} ) %></A>
+ </TD>
+ <<%$td%>><FONT SIZE="+1"><B> - </B></FONT></TD>
+ <<%$td%> ALIGN="right">
+ <A HREF="<% $baselink. $link %>;nottax=1;cust_tax=Y"
+ ><% &$money_sprintf( $region->{'exempt_cust'} ) %></A>
+ </TD>
+ <<%$td%>><FONT SIZE="+1"><B> - </B></FONT></TD>
+ <<%$td%> ALIGN="right">
+ <A HREF="<% $baselink. $link %>;nottax=1;pkg_tax=Y"
+ ><% &$money_sprintf( $region->{'exempt_pkg'} ) %></A>
+ </TD>
+ <<%$td%>><FONT SIZE="+1"><B> - </B></FONT></TD>
+ <<%$td%> ALIGN="right">
+ <A HREF="<% $exemptlink. $link %>"
+ ><% &$money_sprintf( $region->{'exempt_monthly'} ) %></A>
+ </TD>
+ <<%$td%>><FONT SIZE="+1"><B> = </B></FONT></TD>
+ <<%$td%> ALIGN="right">
+ <A HREF="<% $baselink. $link %>;nottax=1;taxable=1"
+ ><% &$money_sprintf( $region->{'taxable'} ) %></A>
+ </TD>
+ <<%$td%>><% $region->{'label'} eq 'Total' ? '' : "$bigmath X $bme" %></TD>
+ <<%$td%> ALIGN="right"><% $region->{'rate'} %></TD>
+ <<%$td%>><% $region->{'label'} eq 'Total' ? '' : "$bigmath = $bme" %></TD>
+ <<%$tdh%> ALIGN="right">
+ <% &$money_sprintf( $region->{'owed'} ) %>
+ </TD>
+
+% unless ( $cgi->param('show_taxclasses') ) {
+ <<%$tdh%> ALIGN="right">
+ <A HREF="<% $baselink. $link %>;istax=1"
+ ><% &$money_sprintf( $region->{'tax'} ) %></A>
+ </TD>
+% }
+
+ </TR>
+% }
+
+</TABLE>
+
+% if ( $cgi->param('show_taxclasses') ) {
+
+ <BR>
+ <% include('/elements/table-grid.html') %>
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Tax invoiced</TH>
+ </TR>
+
+% #some false laziness w/above
+% $bgcolor1 = '#eeeeee';
+% $bgcolor2 = '#ffffff';
+%
+% foreach my $region ( @base_regions ) {
+%
+% my $link = '';
+% #if ( $region->{'label'} ne 'Total' ) {
+% if ( $region->{'label'} eq $out ) {
+% $link = ';out=1';
+% } else {
+% $link = ';'. $region->{'url_param'};
+% }
+% #}
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+% my $td = qq(TD CLASS="grid" BGCOLOR="$bgcolor");
+
+ <TR>
+ <<%$td%>><% $region->{'label'} %></TD>
+ <<%$td%> ALIGN="right">
+ <A HREF="<% $baselink. $link %>;istax=1"
+ ><% &$money_sprintf( $region->{'tax'} ) %></A>
+ </TD>
+ </TR>
+
+% }
+
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+% my $td = qq(TD CLASS="grid" BGCOLOR="$bgcolor");
+
+ <TR>
+ <<%$td%>>Total</TD>
+ <<%$td%> ALIGN="right">
+ <A HREF="<% $baselink %>;istax=1"
+ ><% &$money_sprintf( $tax ) %></A>
+ </TD>
+ </TR>
+
+ </TABLE>
+
+% }
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+my $user = getotaker;
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+
+my $join_cust = ' JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum ) ';
+my $join_cust_pkg = $join_cust.
+ ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart ) ';
+$join_cust_pkg .= ' LEFT JOIN cust_location USING ( locationnum )'
+ if $conf->exists('tax-pkg_address');
+
+my $from_join_cust_pkg = " FROM cust_bill_pkg $join_cust_pkg ";
+
+my $where = "WHERE _date >= $beginning AND _date <= $ending ";
+
+my( $location_sql, @base_param ) = FS::cust_pkg->location_sql;
+$where .= " AND $location_sql ";
+
+my $agentname = '';
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "agent not found" unless $agent;
+ $agentname = $agent->agent;
+ $where .= ' AND cust_main.agentnum = '. $agent->agentnum;
+}
+
+sub gotcust {
+ my $table = shift;
+ my $prefix = @_ ? shift : '';
+ "
+ ( $table.${prefix}county = cust_main_county.county
+ OR cust_main_county.county = ''
+ OR cust_main_county.county IS NULL )
+ AND ( $table.${prefix}state = cust_main_county.state
+ OR cust_main_county.state = ''
+ OR cust_main_county.state IS NULL )
+ AND ( $table.${prefix}country = cust_main_county.country )
+ ";
+}
+
+my $gotcust;
+if ( $conf->exists('tax-ship_address') ) {
+
+ $gotcust = "
+ ( cust_main_county.country = cust_main.country
+ OR cust_main_county.country = cust_main.ship_country
+ )
+
+ AND
+
+ (
+ ( ( ship_last IS NULL OR ship_last = '' )
+ AND ". gotcust('cust_main'). "
+ )
+ OR
+ ( ship_last IS NOT NULL AND ship_last != ''
+ AND ". gotcust('cust_main', 'ship_'). "
+ )
+ )
+ ";
+
+} else {
+
+ $gotcust = gotcust('cust_main');
+
+}
+if ( $conf->exists('tax-pkg_address') ) {
+ $gotcust = "
+ ( cust_pkg.locationnum IS NULL AND $gotcust)
+ OR ( cust_pkg.locationnum IS NOT NULL AND ". gotcust('cust_location'). " )";
+ $gotcust =
+ "WHERE 0 < ( SELECT COUNT(*) FROM cust_pkg
+ LEFT JOIN cust_main USING ( custnum )
+ LEFT JOIN cust_location USING ( locationnum )
+ WHERE $gotcust
+ LIMIT 1
+ )
+ ";
+} else {
+ $gotcust =
+ "WHERE 0 < ( SELECT COUNT(*) FROM cust_main WHERE $gotcust LIMIT 1 )";
+}
+
+my($total, $tot_taxable, $owed, $tax) = ( 0, 0, 0, 0 );
+my( $exempt_cust, $exempt_pkg, $exempt_monthly ) = ( 0, 0, 0 );
+my $out = 'Out of taxable region(s)';
+my %regions = ();
+
+foreach my $r ( qsearch({ 'table' => 'cust_main_county',
+ 'extra_sql' => $gotcust,
+ })
+ )
+{
+ #warn $r->county. ' '. $r->state. ' '. $r->country. "\n";
+
+ my $label = getlabel($r);
+ $regions{$label}->{'label'} = $label;
+ $regions{$label}->{'url_param'} =
+ join(';', map "$_=".uri_escape($r->$_()),
+ qw( county state country taxname )
+ );
+
+ my @param = @base_param;
+ my $mywhere = $where;
+
+ if ( $r->taxclass ) {
+
+ $mywhere .= " AND taxclass = ? ";
+ push @param, 'taxclass';
+ $regions{$label}->{'url_param'} .= ';taxclass='. uri_escape($r->taxclass);
+ #no, always# if $cgi->param('show_taxclasses');
+
+ } else {
+
+ $regions{$label}->{'url_param'} .= ';taxclassNULL=1'
+ if $cgi->param('show_taxclasses');
+
+ my $same_sql = $r->sql_taxclass_sameregion;
+ $mywhere .= " AND $same_sql" if $same_sql;
+
+ }
+
+ my $fromwhere = "$from_join_cust_pkg $mywhere"; # AND payby != 'COMP' ";
+
+# my $label = getlabel($r);
+# $regions{$label}->{'label'} = $label;
+
+ my $nottax = 'pkgnum != 0';
+
+ ## calculate total for this region
+
+ my $t_sql =
+ "SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur) $fromwhere AND $nottax";
+ my $t = scalar_sql($r, \@param, $t_sql);
+ $total += $t;
+ $regions{$label}->{'total'} += $t;
+
+ #if ( $label eq $out ) {# && $t ) {
+ # warn "adding $t for ".
+ # join('/', map $r->$_, qw( taxclass county state country ) ). "\n";
+ # #warn $t_sql if $r->state eq 'FL';
+ #}
+
+ ## calculate customer-exemption for this region
+
+## my $taxable = $t;
+
+# my($taxable, $x_cust) = (0, 0);
+# foreach my $e ( grep { $r->get($_.'tax') !~ /^Y/i }
+# qw( cust_bill_pkg.setup cust_bill_pkg.recur ) ) {
+# $taxable += scalar_sql($r, \@param,
+# "SELECT SUM($e) $fromwhere AND $nottax AND ( tax != 'Y' OR tax IS NULL )"
+# );
+#
+# $x_cust += scalar_sql($r, \@param,
+# "SELECT SUM($e) $fromwhere AND $nottax AND tax = 'Y'"
+# );
+# }
+
+ my $x_cust = scalar_sql($r, \@param,
+ "SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur)
+ $fromwhere AND $nottax AND tax = 'Y' "
+ );
+
+ $exempt_cust += $x_cust;
+ $regions{$label}->{'exempt_cust'} += $x_cust;
+
+ ## calculate package-exemption for this region
+
+ my $x_pkg = scalar_sql($r, \@param,
+ "SELECT SUM(
+ ( CASE WHEN part_pkg.setuptax = 'Y'
+ THEN cust_bill_pkg.setup
+ ELSE 0
+ END
+ )
+ +
+ ( CASE WHEN part_pkg.recurtax = 'Y'
+ THEN cust_bill_pkg.recur
+ ELSE 0
+ END
+ )
+ )
+ $fromwhere
+ AND $nottax
+ AND (
+ ( part_pkg.setuptax = 'Y' AND cust_bill_pkg.setup > 0 )
+ OR ( part_pkg.recurtax = 'Y' AND cust_bill_pkg.recur > 0 )
+ )
+ AND ( tax != 'Y' OR tax IS NULL )
+ "
+ );
+ $exempt_pkg += $x_pkg;
+ $regions{$label}->{'exempt_pkg'} += $x_pkg;
+
+ ## calculate monthly exemption (texas tax) for this region
+
+ # count up all the cust_tax_exempt_pkg records associated with
+ # the actual line items.
+
+ my $x_monthly = scalar_sql($r, \@param,
+ "SELECT SUM(amount)
+ FROM cust_tax_exempt_pkg
+ JOIN cust_bill_pkg USING ( billpkgnum )
+ $join_cust_pkg
+ $mywhere"
+ );
+# if ( $x_monthly ) {
+# #warn $r->taxnum(). ": $x_monthly\n";
+# $taxable -= $x_monthly;
+# }
+
+ $exempt_monthly += $x_monthly;
+ $regions{$label}->{'exempt_monthly'} += $x_monthly;
+
+ my $taxable = $t - $x_cust - $x_pkg - $x_monthly;
+
+ $tot_taxable += $taxable;
+ $regions{$label}->{'taxable'} += $taxable;
+
+ $owed += $taxable * ($r->tax/100);
+ $regions{$label}->{'owed'} += $taxable * ($r->tax/100);
+
+ if ( defined($regions{$label}->{'rate'})
+ && $regions{$label}->{'rate'} != $r->tax.'%' ) {
+ $regions{$label}->{'rate'} = 'variable';
+ } else {
+ $regions{$label}->{'rate'} = $r->tax.'%';
+ }
+
+}
+
+my $distinct = "country, state, county,
+ CASE WHEN taxname IS NULL THEN '' ELSE taxname END AS taxname";
+my $taxclass_distinct =
+ #a little bit unsure of this part... test?
+ #ah, it looks like it winds up being irrelevant as ->{'tax'}
+ # from $regions is not displayed when show_taxclasses is on
+ ( $cgi->param('show_taxclasses')
+ ? " CASE WHEN taxclass IS NULL THEN '' ELSE taxclass END "
+ : " '' "
+ )." AS taxclass";
+
+
+my %qsearch = (
+ 'select' => "DISTINCT $distinct, $taxclass_distinct",
+ 'table' => 'cust_main_county',
+ 'hashref' => {},
+ 'extra_sql' => $gotcust,
+);
+
+my $taxfromwhere = " FROM cust_bill_pkg $join_cust ";
+my $taxwhere = $where;
+if ( $conf->exists('tax-pkg_address') ) {
+
+ $taxfromwhere .= 'LEFT JOIN cust_bill_pkg_tax_location USING ( billpkgnum )
+ LEFT JOIN cust_location USING ( locationnum ) ';
+
+ #quelle kludge
+ $taxwhere =~ s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g;
+
+}
+$taxfromwhere .= " $taxwhere "; #AND payby != 'COMP' ";
+my @taxparam = @base_param;
+
+#should i be a cust_main_county method or something
+#need to pass in $taxfromwhere & @taxparam???
+my $_taxamount_sub = sub {
+ my $r = shift;
+
+ #match itemdesc if necessary!
+ my $named_tax =
+ $r->taxname
+ ? 'AND itemdesc = '. dbh->quote($r->taxname)
+ : "AND ( itemdesc IS NULL OR itemdesc = '' OR itemdesc = 'Tax' )";
+
+ my $sql = "SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur) ".
+ " $taxfromwhere AND cust_bill_pkg.pkgnum = 0 $named_tax";
+
+ scalar_sql($r, \@taxparam, $sql );
+};
+
+#foreach my $label ( keys %regions ) {
+foreach my $r ( qsearch(\%qsearch) ) {
+
+ #warn join('-', map { $r->$_() } qw( country state county taxname ) )."\n";
+
+ my $label = getlabel($r);
+
+ #my $fromwhere = $join_pkg. $where. " AND payby != 'COMP' ";
+ #my @param = @base_param;
+
+ my $x = &{$_taxamount_sub}($r);
+
+ $tax += $x unless $cgi->param('show_taxclasses');
+ $regions{$label}->{'tax'} += $x;
+
+}
+
+my %base_regions = ();
+if ( $cgi->param('show_taxclasses') ) {
+
+ $qsearch{'select'} = "DISTINCT $distinct";
+ foreach my $r ( qsearch(\%qsearch) ) {
+
+ my $x = &{$_taxamount_sub}($r);
+
+ my $base_label = getlabel($r, 'no_taxclass'=>1 );
+ $base_regions{$base_label}->{'label'} = $base_label;
+
+ $base_regions{$base_label}->{'url_param'} =
+ join(';', map "$_=". uri_escape($r->$_()),
+ qw( county state country taxname )
+ );
+
+ $base_regions{$base_label}->{'tax'} += $x;
+ $tax += $x;
+ }
+
+}
+
+
+#ordering
+my @regions =
+ map $regions{$_},
+ sort { ( ($a eq $out) cmp ($b eq $out) ) || ($b cmp $a) }
+ keys %regions;
+
+my @base_regions =
+ map $base_regions{$_},
+ sort { ( ($a eq $out) cmp ($b eq $out) ) || ($b cmp $a) }
+ keys %base_regions;
+
+push @regions, {
+ 'label' => 'Total',
+ 'url_param' => '',
+ 'total' => $total,
+ 'exempt_cust' => $exempt_cust,
+ 'exempt_pkg' => $exempt_pkg,
+ 'exempt_monthly' => $exempt_monthly,
+ 'taxable' => $tot_taxable,
+ 'rate' => '',
+ 'owed' => $owed,
+ 'tax' => $tax,
+};
+
+#--
+
+my $money_char = $conf->config('money_char') || '$';
+my $money_sprintf = sub {
+ $money_char. sprintf('%.2f', shift );
+};
+
+sub getlabel {
+ my $r = shift;
+ my %opt = @_;
+
+ my $label;
+ if (
+ $r->tax == 0
+ && ! scalar( qsearch('cust_main_county', { 'state' => $r->state,
+ 'county' => $r->county,
+ 'country' => $r->country,
+ 'tax' => { op=>'>', value=>0 },
+ }
+ )
+ )
+
+ ) {
+ #kludge to avoid "will not stay shared" warning
+ my $out = 'Out of taxable region(s)';
+ $label = $out;
+# } elsif ( $r->taxname && count_taxname($r->taxname) == 1 ) {
+# $label = $r->taxname;
+## $regions{$label}->{'taxname'} = $label;
+## push @{$regions{$label}->{$_}}, $r->$_() foreach qw( county state country );
+ } else {
+ $label = $r->country;
+ $label = $r->state.", $label" if $r->state;
+ $label = $r->county." county, $label" if $r->county;
+ $label = "$label (". $r->taxclass. ")"
+ if $r->taxclass
+ && $cgi->param('show_taxclasses')
+ && ! $opt{'no_taxclass'};
+ $label = $r->taxname. " ($label)" if $r->taxname;
+ }
+ return $label;
+}
+
+#my %count_taxname = (); #cache
+#sub count_taxname {
+# my $taxname = shift;
+# return $count_taxname{$taxname} if exists $count_taxname{$taxname};
+# my $sql = 'SELECT COUNT(*) FROM cust_main_county WHERE taxname = ?';
+# my $sth = dbh->prepare($sql) or die dbh->errstr;
+# $sth->execute( $taxname )
+# or die "Unexpected error executing statement $sql: ". $sth->errstr;
+# $count_taxname{$taxname} = $sth->fetchrow_arrayref->[0];
+#}
+
+#false laziness w/FS::Report::Table::Monthly (sub should probably be moved up
+#to FS::Report or FS::Record or who the fuck knows where)
+sub scalar_sql {
+ my( $r, $param, $sql ) = @_;
+ #warn "$sql\n";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute( map $r->$_(), @$param )
+ or die "Unexpected error executing statement $sql: ". $sth->errstr;
+ $sth->fetchrow_arrayref->[0] || 0;
+}
+
+my $dateagentlink = "begin=$beginning;end=$ending";
+$dateagentlink .= ';agentnum='. $cgi->param('agentnum')
+ if length($agentname);
+my $baselink = $p. "search/cust_bill_pkg.cgi?$dateagentlink";
+my $exemptlink = $p. "search/cust_tax_exempt_pkg.cgi?$dateagentlink";
+
+</%init>
diff --git a/httemplate/search/report_tax.html b/httemplate/search/report_tax.html
new file mode 100755
index 0000000..e5ffa9a
--- /dev/null
+++ b/httemplate/search/report_tax.html
@@ -0,0 +1,42 @@
+<% include('/elements/header.html', 'Tax Report' ) %>
+
+<FORM ACTION="report_tax.cgi" METHOD="GET">
+
+<TABLE>
+
+ <% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %>
+
+ <% include( '/elements/tr-input-beginning_ending.html' ) %>
+% my $conf = new FS::Conf;
+% if ( $conf->exists('enable_taxclasses') ) {
+%
+
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_taxclasses" VALUE="1"></TD>
+ <TD>Show tax classes</TD>
+ </TR>
+% }
+% my @pkg_class = qsearch('pkg_class', {});
+% if ( @pkg_class ) {
+%
+
+ <TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_pkgclasses" VALUE="1"></TD>
+ <TD>Show package classes</TD>
+ </TR>
+% }
+
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/rt_transaction.html b/httemplate/search/rt_transaction.html
new file mode 100644
index 0000000..651f289
--- /dev/null
+++ b/httemplate/search/rt_transaction.html
@@ -0,0 +1,96 @@
+<% include('elements/search.html',
+ 'title' => 'Time worked',
+ 'name_singular' => 'transaction',
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ $format_seconds_sub, ],
+ 'header' => [ 'Ticket #',
+ 'Ticket',
+ 'Date',
+ 'Time',
+ ],
+ 'fields' => [ 'ticketid',
+ sub { encode_entities(shift->get('subject')) },
+ 'created',
+ sub { my $seconds = shift->get('transaction_time');
+ &{ $format_seconds_sub }( $seconds );
+ },
+ ],
+ 'links' => [
+ $link,
+ $link,
+ '',
+ '',
+ ],
+ )
+%>
+<%once>
+
+my $format_seconds_sub = sub {
+ my $seconds = shift;
+ (($seconds < 0) ? '-' : '') . concise(duration($seconds));
+};
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+
+#some amount of false laziness w/timeworked.html...
+
+my $transactiontime = "
+ CASE transactions.type when 'Set'
+ THEN (to_number(newvalue,'999999')-to_number(oldvalue, '999999')) * 60
+ ELSE timetaken*60
+ END
+";
+
+my $join = 'JOIN Tickets ON Transactions.ObjectId = Tickets.Id '.
+ 'JOIN Users ON Transactions.Creator = Users.Id ';
+
+my $where = "
+ WHERE objecttype='RT::Ticket'
+ AND ( ( Transactions.Type = 'Set'
+ AND Transactions.Field = 'TimeWorked'
+ AND Transactions.NewValue != Transactions.OldValue )
+ OR ( ( Transactions.Type='Create' OR Transactions.Type='Comment' OR Transactions.Type='Correspond' )
+ AND Transactions.TimeTaken > 0
+ )
+ )
+";
+#AND transaction_time != 0
+#AND $wheretimeleft
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+# TIMESTAMP is Pg-specific... ?
+if ( $beginning > 0 ) {
+ $beginning = "TIMESTAMP '". time2str('%Y-%m-%d %X', $beginning). "'";
+ $where .= " AND Transactions.Created >= $beginning ";
+}
+if ( $ending < 4294967295 ) {
+ $ending = "TIMESTAMP '". time2str('%Y-%m-%d %X', $ending). "'";
+ $where .= " AND Transactions.Created <= $ending ";
+}
+
+if ( $cgi->param('otaker') && $cgi->param('otaker') =~ /^([\w\.\-]+)$/ ) {
+ $where .= " AND Users.name = '$1' ";
+}
+
+my $query = {
+ 'select' => "Transactions.*, Tickets.Id AS ticketid, Tickets.Subject, Users.name as otaker, $transactiontime AS transaction_time",
+ #'table' => 'Transactions',
+ 'table' => 'transactions',
+ 'addl_from' => $join.
+ 'LEFT JOIN acct_rt_transaction '.
+ ' ON Transactions.Id = acct_rt_transaction.transaction_id',
+ 'extra_sql' => $where,
+ 'order by' => 'ORDER BY Created',
+};
+
+my $count_query =
+ "SELECT COUNT(*), SUM($transactiontime) FROM Transactions $join $where";
+
+my $link = [ "${p}rt/Ticket/Display.html?id=", sub { shift->get('id'); } ];
+
+</%init>
diff --git a/httemplate/search/sql.html b/httemplate/search/sql.html
new file mode 100644
index 0000000..df9b8cd
--- /dev/null
+++ b/httemplate/search/sql.html
@@ -0,0 +1,13 @@
+<% include( 'elements/search.html',
+ 'title' => 'Query Results',
+ 'name' => 'rows',
+ 'query' => 'SELECT '. ( $cgi->param('sql')
+ || errorpage('Empty query') ),
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Raw SQL');
+
+</%init>
diff --git a/httemplate/search/sqlradius.cgi b/httemplate/search/sqlradius.cgi
new file mode 100644
index 0000000..29f3602
--- /dev/null
+++ b/httemplate/search/sqlradius.cgi
@@ -0,0 +1,328 @@
+<% include( '/elements/header.html', 'RADIUS Sessions') %>
+
+% ###
+% # and finally, display the thing
+% ###
+%
+% foreach my $part_export (
+% #grep $_->can('usage_sessions'), qsearch( 'part_export' )
+% qsearch( 'part_export', { 'exporttype' => 'sqlradius' } ),
+% qsearch( 'part_export', { 'exporttype' => 'sqlradius_withdomain' } )
+% ) {
+% %user2svc_acct = ();
+%
+% my $efields = tie my %efields, 'Tie::IxHash', %fields;
+% delete $efields{'framedipaddress'} if $part_export->option('hide_ip');
+% if ( $part_export->option('hide_data') ) {
+% delete $efields{$_} foreach qw(acctinputoctets acctoutputoctets);
+% }
+% if ( $part_export->option('show_called_station') ) {
+% $efields->Splice(1, 0,
+% 'calledstationid' => {
+% 'name' => 'Destination',
+% 'attrib' => 'Called-Station-ID',
+% 'fmt' =>
+% sub { length($_[0]) ? shift : '&nbsp'; },
+% 'align' => 'left',
+% },
+% );
+% }
+%
+%
+
+ <% $part_export->exporttype %> to <% $part_export->machine %><BR>
+ <% include( '/elements/table-grid.html' ) %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor;
+
+ <TR>
+% foreach my $field ( keys %efields ) {
+
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <% $efields{$field}->{name} %><BR>
+ <FONT SIZE=-2><% $efields{$field}->{attrib} %></FONT>
+ </TH>
+
+% }
+ </TR>
+
+% foreach my $session (
+% @{ $part_export->usage_sessions( {
+% 'stoptime_start' => $beginning,
+% 'stoptime_end' => $ending,
+% 'open_sessions' => $open_sessions,
+% 'starttime_start' => $starttime_beginning,
+% 'starttime_end' => $starttime_ending,
+% 'svc_acct' => $cgi_svc_acct,
+% 'ip' => $ip,
+% 'prefix' => $prefix,
+% } )
+% }
+% ) {
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+ <TR>
+% foreach my $field ( keys %efields ) {
+% my $html = &{ $efields{$field}->{fmt} }( $session->{$field},
+% $session,
+% $part_export,
+% );
+% my $class = ( $html =~ /<TABLE/ ? 'inv' : 'grid' );
+
+ <TD CLASS="<%$class%>" BGCOLOR="<% $bgcolor %>" ALIGN="<% $efields{$field}->{align} %>">
+ <% $html %>
+ </TD>
+% }
+ </TR>
+
+% }
+
+</TABLE>
+<BR><BR>
+
+% }
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+
+###
+# parse cgi params
+###
+
+#sort of false laziness w/cust_pay.cgi
+my( $beginning, $ending ) = ( '', '' );
+if ( $cgi->param('stoptime_beginning')
+ && $cgi->param('stoptime_beginning') =~ /^([ 0-9\-\/\:\w]{0,54})$/ ) {
+ $beginning = str2time($1);
+}
+if ( $cgi->param('stoptime_ending')
+ && $cgi->param('stoptime_ending') =~ /^([ 0-9\-\/\:\w]{0,54})$/ ) {
+ $ending = str2time($1); # + 86399;
+}
+if ( $cgi->param('begin') && $cgi->param('begin') =~ /^(\d+)$/ ) {
+ $beginning = $1;
+}
+if ( $cgi->param('end') && $cgi->param('end') =~ /^(\d+)$/ ) {
+ $ending = $1;
+}
+
+my $open_sessions = '';
+if ( $cgi->param('open_sessions') =~ /^(\d*)$/ ) {
+ $open_sessions = $1;
+}
+
+my( $starttime_beginning, $starttime_ending ) = ( '', '' );
+if ( $cgi->param('starttime_beginning')
+ && $cgi->param('starttime_beginning') =~ /^([ 0-9\-\/\:\w]{0,54})$/ ) {
+ $starttime_beginning = str2time($1);
+}
+if ( $cgi->param('starttime_ending')
+ && $cgi->param('starttime_ending') =~ /^([ 0-9\-\/\:\w]{0,54})$/ ) {
+ $starttime_ending = str2time($1); # + 86399;
+}
+
+my $cgi_svc_acct = '';
+if ( $cgi->param('svcnum') =~ /^(\d+)$/ ) {
+ $cgi_svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $1 } );
+} elsif ( $cgi->param('username') =~ /^([^@]+)\@([^@]+)$/ ) {
+ my %search = { 'username' => $1 };
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $2 } );
+ if ( $svc_domain ) {
+ $search{'domsvc'} = $svc_domain->svcnum;
+ } else {
+ delete $search{'username'};
+ }
+ $cgi_svc_acct = qsearchs( 'svc_acct', \%search )
+ if keys %search;
+} elsif ( $cgi->param('username') =~ /^(.+)$/ ) {
+ $cgi_svc_acct = qsearchs( 'svc_acct', { 'username' => $1 } );
+}
+
+my $ip = '';
+if ( $cgi->param('ip') =~ /^((\d+\.){3}\d+)$/ ) {
+ $ip = $1;
+}
+
+my $prefix = $cgi->param('prefix');
+$prefix =~ s/\D//g;
+if ( $prefix =~ /^(\d+)$/ ) {
+ $prefix = $1;
+ $prefix = "011$prefix" unless $prefix =~ /^1/;
+} else {
+ $prefix = '';
+}
+
+###
+# field formatting subroutines
+###
+
+my %user2svc_acct = ();
+my $user_format = sub {
+ my ( $user, $session, $part_export ) = @_;
+
+ my $svc_acct = '';
+ if ( exists $user2svc_acct{$user} ) {
+ $svc_acct = $user2svc_acct{$user};
+ } else {
+ my %search = ();
+ if ( $part_export->exporttype eq 'sqlradius_withdomain' ) {
+ my $domain;
+ if ( $user =~ /^([^@]+)\@([^@]+)$/ ) {
+ $search{'username'} = $1;
+ $domain = $2;
+ } else {
+ $search{'username'} = $user;
+ $domain = $session->{'realm'};
+ }
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } );
+ if ( $svc_domain ) {
+ $search{'domsvc'} = $svc_domain->svcnum;
+ } else {
+ delete $search{'username'};
+ }
+ } elsif ( $part_export->exporttype eq 'sqlradius' ) {
+ $search{'username'} = $user;
+ } else {
+ die 'unknown export type '. $part_export->exporttype.
+ " for $part_export\n";
+ }
+ if ( keys %search ) {
+ my @svc_acct =
+ grep { qsearchs( 'export_svc', {
+ 'exportnum' => $part_export->exportnum,
+ 'svcpart' => $_->cust_svc->svcpart,
+ } )
+ } qsearch( 'svc_acct', \%search );
+ if ( @svc_acct ) {
+ warn 'multiple svc_acct records for user $user found; '.
+ 'using first arbitrarily'
+ if scalar(@svc_acct) > 1;
+ $user2svc_acct{$user} = $svc_acct = shift @svc_acct;
+ }
+ }
+ }
+
+ if ( $svc_acct ) {
+ my $svcnum = $svc_acct->svcnum;
+ qq(<A HREF="${p}view/svc_acct.cgi?$svcnum"><B>$user</B></A>);
+ } else {
+ "<B>$user</B>";
+ }
+
+};
+
+my $customer_format = sub {
+ my( $unused, $session ) = @_;
+ return '&nbsp;' unless exists $user2svc_acct{$session->{'username'}};
+ my $svc_acct = $user2svc_acct{$session->{'username'}};
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ return '&nbsp;' unless $cust_pkg;
+ my $cust_main = $cust_pkg->cust_main;
+
+ qq!<A HREF="${p}view/cust_main.cgi?!. $cust_main->custnum. '">'.
+ $cust_pkg->cust_main->name. '</A>';
+};
+
+my $time_format = sub {
+ my $time = shift;
+ return '&nbsp;' if $time == 0;
+ my $pretty = time2str('%T%P %a&nbsp;%b&nbsp;%o&nbsp;%Y', $time );
+ $pretty =~ s/ (\d)(st|dn|rd|th)/$1$2/;
+ $pretty;
+};
+
+my $duration_format = sub {
+ my $seconds = shift;
+ my $hour = int($seconds/3600);
+ my $min = int( ($seconds%3600) / 60 );
+ my $sec = $seconds%60;
+ '<TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0>'.
+ '<TR><TD CLASS="inv" ALIGN="right">'.
+ ( $hour ? "<B>$hour</B>h" : '&nbsp;' ).
+ '</TD><TD CLASS="inv" ALIGN="right">'.
+ ( ( $hour || $min ) ? "<B>$min</B>m" : '&nbsp;' ).
+ '</TD><TD CLASS="inv" ALIGN="right">'.
+ "<B>$sec</B>s".
+ '</TD></TR></TABLE>';
+};
+
+my $octets_format = sub {
+ my $octets = shift;
+ my $megs = $octets / 1048576;
+ sprintf('<B>%.3f</B>&nbsp;megs', $megs);
+ #my $gigs = $octets / 1073741824
+ #sprintf('<B>%.3f</B> gigabytes', $gigs);
+};
+
+###
+# the fields
+###
+
+tie my %fields, 'Tie::IxHash',
+ 'username' => {
+ name => 'User',
+ attrib => 'UserName',
+ fmt => $user_format,
+ align => 'left',
+ },
+ 'realm' => {
+ name => 'Realm',
+ attrib => 'Realm',
+ align => 'left',
+ },
+ 'dummy' => {
+ name => 'Customer',
+ attrib => '',
+ fmt => $customer_format,
+ align => 'left',
+ },
+ 'framedipaddress' => {
+ name => 'IP&nbsp;Address',
+ attrib => 'Framed-IP-Address',
+ fmt => sub { my $ip = shift;
+ length($ip) ? $ip : '&nbsp';
+ },
+ align => 'right',
+ },
+ 'acctstarttime' => {
+ name => 'Start&nbsp;time',
+ attrib => 'Acct-Start-Time',
+ fmt => $time_format,
+ align => 'left',
+ },
+ 'acctstoptime' => {
+ name => 'End&nbsp;time',
+ attrib => 'Acct-Stop-Time',
+ fmt => $time_format,
+ align => 'left',
+ },
+ 'acctsessiontime' => {
+ name => 'Duration',
+ attrib => 'Acct-Session-Time',
+ fmt => $duration_format,
+ align => 'right',
+ },
+ 'acctinputoctets' => {
+ name => 'Upload', # (from user)',
+ attrib => 'Acct-Input-Octets',
+ fmt => $octets_format,
+ align => 'right',
+ },
+ 'acctoutputoctets' => {
+ name => 'Download', # (to user)',
+ attrib => 'Acct-Output-Octets',
+ fmt => $octets_format,
+ align => 'right',
+ },
+;
+$fields{$_}->{fmt} ||= sub { length($_[0]) ? shift : '&nbsp'; }
+ foreach keys %fields;
+
+</%init>
diff --git a/httemplate/search/sqlradius.html b/httemplate/search/sqlradius.html
new file mode 100644
index 0000000..8c40598
--- /dev/null
+++ b/httemplate/search/sqlradius.html
@@ -0,0 +1,123 @@
+<% include( '/elements/header.html', 'Search RADIUS sessions' ) %>
+
+<FORM NAME="OneTrueForm" ACTION="sqlradius.cgi" METHOD="GET">
+% #include( '/elements/table.html' )
+
+<% ntable('#cccccc') %>
+<TR>
+ <TD ALIGN="right">Username: </TD>
+ <TD><INPUT TYPE="text" NAME="username"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD><FONT SIZE="-1"><I>(leave blank to show all users)</I></FONT></TD>
+</TR>
+% my @part_export = qsearch( 'part_export', { 'exporttype' => 'sqlradius' } );
+% push @part_export,
+% qsearch( 'part_export', { 'exporttype' => 'sqlradius_withdomain' } );
+%
+% if ( grep { ! $_->option('hide_ip') } @part_export ) {
+
+ <TR>
+ <TD ALIGN="right">IP address: </TD>
+ <TD><INPUT TYPE="text" NAME="ip"></TD>
+ </TR>
+ <TR>
+ <TD></TD>
+ <TD><FONT SIZE="-1"><I>(leave blank to show all IPs)</I></FONT></TD>
+ </TR>
+% }
+% if ( grep { $_->option('show_called_station') } @part_export ) {
+
+ <TR>
+ <TD ALIGN="right">Destination prefix:</TD>
+ <TD><INPUT TYPE="text" NAME="prefix"></TD>
+ </TR>
+ <TR>
+ <TD></TD>
+ <TD><FONT SIZE="-1"><I>(country code or country code and prefix)</I></FONT></TD>
+ </TR>
+ <TR>
+ <TD></TD>
+ <TD><FONT SIZE="-1"><I>(leave blank to show all destinations)</I></FONT></TD>
+ </TR>
+% }
+
+ <TR>
+ <TD>Show:</TD>
+ <TD>
+ <INPUT TYPE="radio" NAME="open_sessions" VALUE="0" onClick="open_changed(this);" CHECKED>Completed sessions<BR>
+ <INPUT TYPE="radio" NAME="open_sessions" VALUE="1" onClick="open_changed(this);">Open sessions
+ </TD>
+ </TR>
+
+ <TR>
+ <TH COLSPAN=2>Session start</TD>
+ </TR>
+
+ <% include( '/elements/tr-input-beginning_ending.html',
+ 'prefix' => 'starttime',
+ 'input_time' => 1,
+ )
+ %>
+
+ <SCRIPT TYPE="text/javascript">
+
+ function open_changed(what) {
+
+ var value=get_open_value(what);
+ if ( value == '1' ) {
+ what.form.stoptime_beginning_text.disabled = true;
+ what.form.stoptime_ending_text.disabled = true;
+ what.form.stoptime_beginning_text.style.backgroundColor = '#dddddd';
+ what.form.stoptime_ending_text.style.backgroundColor = '#dddddd';
+ what.form.stoptime_beginning_button.style.display = 'none';
+ what.form.stoptime_ending_button.style.display = 'none';
+ what.form.stoptime_beginning_disabled.style.display = '';
+ what.form.stoptime_ending_disabled.style.display = '';
+ } else if ( value == '0' ) {
+ what.form.stoptime_beginning_text.disabled = false;
+ what.form.stoptime_ending_text.disabled = false;
+ what.form.stoptime_beginning_text.style.backgroundColor = '#ffffff';
+ what.form.stoptime_ending_text.style.backgroundColor = '#ffffff';
+ what.form.stoptime_beginning_button.style.display = '';
+ what.form.stoptime_ending_button.style.display = '';
+ what.form.stoptime_beginning_disabled.style.display = 'none';
+ what.form.stoptime_ending_disabled.style.display = 'none';
+ }
+
+ }
+
+ function get_open_value(what) {
+ var rad_val = '';
+ for (var i=0; i < what.form.open_sessions.length; i++) {
+ if (what.form.open_sessions[i].checked) {
+ var rad_val = what.form.open_sessions[i].value;
+ }
+ }
+ return rad_val;
+ }
+
+ </SCRIPT>
+
+ <TR>
+ <TH COLSPAN=2>Session end</TD>
+ </TR>
+
+ <% include( '/elements/tr-input-beginning_ending.html',
+ 'prefix' => 'stoptime',
+ 'input_time' => 1,
+ )
+ %>
+
+</TABLE>
+<BR><INPUT TYPE="submit" VALUE="View sessions">
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+
+</%init>
diff --git a/httemplate/search/svc_acct.cgi b/httemplate/search/svc_acct.cgi
new file mode 100755
index 0000000..2324399
--- /dev/null
+++ b/httemplate/search/svc_acct.cgi
@@ -0,0 +1,296 @@
+<% include( 'elements/search.html',
+ 'title' => 'Account Search Results',
+ 'name' => 'accounts',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'links' => \@links,
+ 'align' => $align,
+ 'color' => \@color,
+ 'style' => \@style,
+ )
+%>
+<%once>
+
+#false laziness w/ClientAPI/MyAccount.pm
+sub format_time {
+ my $support = shift;
+ (($support < 0) ? '-' : '' ). int(abs($support)/3600)."h".sprintf("%02d",(abs($support)%3600)/60)."m";
+}
+
+sub timelast {
+ my( $svc_acct, $last, $permonth ) = @_;
+
+ my $sql = "
+ SELECT SUM(support) FROM acct_rt_transaction
+ LEFT JOIN Transactions
+ ON Transactions.Id = acct_rt_transaction.transaction_id
+ WHERE svcnum = ?
+ AND Transactions.Created >= ?
+ ";
+
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute( $svc_acct->svcnum,
+ time2str('%Y-%m-%d %X', time - $last*86400 )
+ )
+ or die $sth->errstr;
+
+ my $seconds = $sth->fetchrow_arrayref->[0];
+
+ my $return = (($seconds < 0) ? '-' : '') . concise(duration($seconds));
+
+ $return .= sprintf(' (%.2fx)', $seconds / $permonth ) if $permonth;
+
+ $return;
+
+}
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $link = [ "${p}view/svc_acct.cgi?", 'svcnum' ];
+my $link_cust = sub {
+ my $svc_acct = shift;
+ if ( $svc_acct->custnum ) {
+ [ "${p}view/cust_main.cgi?", 'custnum' ];
+ } else {
+ '';
+ }
+};
+
+my @extra_sql = ();
+
+my @header = ( '#', 'Service', 'Account', 'UID', 'Last Login' );
+my @fields = ( 'svcnum', 'svc', 'email', 'uid', 'last_login_text' );
+my @links = ( $link, $link, $link, $link, $link );
+my $align = 'rlllr';
+my @color = ( '', '', '', '', '' );
+my @style = ( '', '', '', '', '' );
+
+if ( $cgi->param('domain') ) {
+ my $svc_domain =
+ qsearchs('svc_domain', { 'domain' => $cgi->param('domain') } );
+ unless ( $svc_domain ) {
+ #it would be nice if this looked more like the other "not found"
+ #errors, but this will do for now.
+ errorpage("Domain ". $cgi->param('domain'). " not found at all");
+ } else {
+ push @extra_sql, 'domsvc = '. $svc_domain->svcnum;
+ }
+}
+if ( $cgi->param('domsvc') =~ /^(\d+)$/ ) {
+ push @extra_sql, "domsvc = $1";
+}
+
+my $timepermonth = '';
+
+my $orderby = 'ORDER BY svcnum';
+if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ my $sortby = '';
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ $sortby = $1;
+ $sortby = "LOWER($sortby)"
+ if $sortby eq 'username';
+ push @extra_sql, "$sortby IS NOT NULL"
+ if $sortby eq 'uid' || $sortby eq 'seconds' || $sortby eq 'last_login';
+ $orderby = "ORDER BY $sortby";
+ }
+
+ if ( $sortby eq 'seconds' ) {
+ #push @header, 'Time remaining';
+ push @header, 'Time';
+ push @fields, sub { my $svc_acct = shift; format_time($svc_acct->seconds) };
+ push @links, '';
+ $align .= 'r';
+ push @color, '';
+ push @style, '';
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('svc_acct-display_paid_time_remaining') ) {
+ push @header, 'Paid time', 'Last 30', 'Last 60', 'Last 90';
+ push @fields,
+ sub {
+ my $svc_acct = shift;
+ my $seconds = $svc_acct->seconds;
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ my $part_pkg = $cust_pkg->part_pkg;
+
+ #my $timepermonth = $part_pkg->option('seconds');
+ $timepermonth = $part_pkg->option('seconds');
+ $timepermonth = $timepermonth / $part_pkg->freq
+ if $part_pkg->freq =~ /^\d+$/ && $part_pkg->freq != 0;
+
+ #my $recur = $part_pkg->calc_recur($cust_pkg);
+ my $recur = $part_pkg->base_recur($cust_pkg);
+
+ return format_time($seconds) unless $timepermonth && $recur;
+
+ my $balance = $cust_pkg->cust_main->balance;
+ my $months_unpaid = $balance / $recur;
+ my $time_unpaid = $months_unpaid * $timepermonth;
+ format_time($seconds-$time_unpaid).
+ sprintf(' (%.2fx monthly)', ( $seconds-$time_unpaid ) / $timepermonth );
+ },
+ sub { timelast( shift, 30, $timepermonth ); },
+ sub { timelast( shift, 60, $timepermonth ); },
+ sub { timelast( shift, 90, $timepermonth ); },
+ ;
+ push @links, '', '', '', '';
+ $align .= 'rrrr';
+ push @color, '', '', '', '';
+ push @style, '', '', '', '';
+ }
+
+ }
+
+} elsif ( $cgi->param('magic') =~ /^nologin$/ ) {
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $sortby = "LOWER($sortby)"
+ if $sortby eq 'username';
+ push @extra_sql, "last_login IS NULL";
+ $orderby = "ORDER BY $sortby";
+ }
+
+} elsif ( $cgi->param('magic') =~ /^advanced$/ ) {
+ $orderby = "";
+
+ if ( $cgi->param('agentnum') =~ /^(\d+)$/ and $1 ) {
+ push @extra_sql, "agentnum = $1";
+ }
+
+ my $pkgpart = join (' OR cust_pkg.pkgpart=',
+ grep {$_} map { /^(\d+)$/; } ($cgi->param('pkgpart')));
+ push @extra_sql, '(cust_pkg.pkgpart=' . $pkgpart . ')' if $pkgpart;
+
+ foreach my $field (qw( last_login last_logout )) {
+
+ my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
+
+ next if $beginning == 0 && $ending == 4294967295;
+
+ if ($cgi->param($field."_invert")) {
+ push @extra_sql,
+ "(svc_acct.$field IS NULL OR ".
+ "svc_acct.$field < $beginning AND ".
+ "svc_acct.$field > $ending)";
+ } else {
+ push @extra_sql,
+ "svc_acct.$field IS NOT NULL",
+ "svc_acct.$field >= $beginning",
+ "svc_acct.$field <= $ending";
+ }
+
+ $orderby ||= "ORDER BY svc_acct.$field" .
+ ($cgi->param($field."_invert") ? ' DESC' : '');
+
+ }
+
+ $orderby ||= "ORDER BY svcnum";
+
+} elsif ( $cgi->param('popnum') =~ /^(\d+)$/ ) {
+ push @extra_sql, "popnum = $1";
+ $orderby = "ORDER BY LOWER(username)";
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ push @extra_sql, "svcpart = $1";
+ $orderby = "ORDER BY uid";
+ #$orderby = "ORDER BY svcnum";
+} else {
+ $orderby = "ORDER BY uid";
+
+ my @username_sql;
+
+ my %username_type;
+ foreach ( $cgi->param('username_type') ) {
+ $username_type{$_}++;
+ }
+
+ $cgi->param('username') =~ /^([\w\-\.\&]+)$/; #untaint username_text
+ my $username = $1;
+
+ push @username_sql, "username ILIKE '$username'"
+ if $username_type{'Exact'}
+ || $username_type{'Fuzzy'};
+
+ push @username_sql, "username ILIKE '\%$username\%'"
+ if $username_type{'Substring'}
+ || $username_type{'All'};
+
+ if ( $username_type{'Fuzzy'} || $username_type{'All'} ) {
+ &FS::svc_acct::check_and_rebuild_fuzzyfiles;
+ my $all_username = &FS::svc_acct::all_username;
+
+ my %username;
+ if ( $username_type{'Fuzzy'} || $username_type{'All'} ) {
+ foreach ( amatch($username, [ qw(i) ], @$all_username) ) {
+ $username{$_}++;
+ }
+ }
+
+ #if ($username_type{'Sound-alike'}) {
+ #}
+
+ push @username_sql, "username = '$_'"
+ foreach (keys %username);
+
+ }
+
+ push @extra_sql, '( '. join( ' OR ', @username_sql). ' )';
+
+}
+
+push @header, FS::UI::Web::cust_header($cgi->param('cust_fields'));
+push @fields, \&FS::UI::Web::cust_fields,
+push @links, map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header($cgi->param('cust_fields'));
+$align .= FS::UI::Web::cust_aligns();
+push @color, FS::UI::Web::cust_colors();
+push @style, FS::UI::Web::cust_styles();
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+
+my $extra_sql =
+ scalar(@extra_sql)
+ ? ' WHERE '. join(' AND ', @extra_sql )
+ : '';
+
+my $count_query = "SELECT COUNT(*) FROM svc_acct $addl_from $extra_sql";
+#if ( keys %svc_acct ) {
+# $count_query .= ' WHERE '.
+# join(' AND ', map "$_ = ". dbh->quote($svc_acct{$_}),
+# keys %svc_acct
+# );
+#}
+
+my $sql_query = {
+ 'table' => 'svc_acct',
+ 'hashref' => {}, # \%svc_acct,
+ 'select' => join(', ',
+ 'svc_acct.*',
+ 'part_svc.svc',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => "$extra_sql $orderby",
+ 'addl_from' => $addl_from,
+};
+
+</%init>
diff --git a/httemplate/search/svc_broadband.cgi b/httemplate/search/svc_broadband.cgi
new file mode 100755
index 0000000..2cb0c1e
--- /dev/null
+++ b/httemplate/search/svc_broadband.cgi
@@ -0,0 +1,123 @@
+<% include( 'elements/search.html',
+ 'title' => 'Broadband Search Results',
+ 'name' => 'broadband services',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => [ popurl(2). "view/svc_broadband.cgi?", 'svcnum' ],
+ 'header' => [ '#',
+ 'Service',
+ 'Router',
+ 'IP Address',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [ 'svcnum',
+ 'svc',
+ sub { $routerbyblock{shift->blocknum}->routername; },
+ 'ip_addr',
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ $link,
+ $link_router,
+ $link,
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rllr'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $conf = new FS::Conf;
+
+my $orderby = 'ORDER BY svcnum';
+my %svc_broadband = ();
+my @extra_sql = ();
+if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $orderby = "ORDER BY $sortby";
+ }
+
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ push @extra_sql, "svcpart = $1";
+} elsif ( $cgi->param('ip_addr') =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ ) {
+ push @extra_sql, "ip_addr = '$1'";
+}
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+
+my $extra_sql = '';
+if ( @extra_sql ) {
+ $extra_sql = ( keys(%svc_broadband) ? ' AND ' : ' WHERE ' ).
+ join(' AND ', @extra_sql );
+}
+
+my $count_query = "SELECT COUNT(*) FROM svc_broadband $addl_from ";
+#if ( keys %svc_broadband ) {
+# $count_query .= ' WHERE '.
+# join(' AND ', map "$_ = ". dbh->quote($svc_broadband{$_}),
+# keys %svc_broadband
+# );
+#}
+$count_query .= $extra_sql;
+
+my $sql_query = {
+ 'table' => 'svc_broadband',
+ 'hashref' => {}, #\%svc_broadband,
+ 'select' => join(', ',
+ 'svc_broadband.*',
+ 'part_svc.svc',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => $extra_sql,
+ 'addl_from' => $addl_from,
+};
+
+my %routerbyblock = ();
+foreach my $router (qsearch('router', {})) {
+ foreach ($router->addr_block) {
+ $routerbyblock{$_->blocknum} = $router;
+ }
+}
+
+my $link = [ $p.'view/svc_broadband.cgi', 'svcnum' ];
+
+#XXX get the router link working
+my $link_router = sub { my $routernum = $routerbyblock{shift->blocknum}->routernum;
+ [ $p.'view/router.cgi?'.$routernum, 'routernum' ];
+ };
+
+my $link_cust = [ $p.'view/cust_main.cgi', 'custnum' ];
+
+</%init>
diff --git a/httemplate/search/svc_domain.cgi b/httemplate/search/svc_domain.cgi
new file mode 100755
index 0000000..08ffdba
--- /dev/null
+++ b/httemplate/search/svc_domain.cgi
@@ -0,0 +1,112 @@
+<% include( 'elements/search.html',
+ 'title' => "Domain Search Results",
+ 'name' => 'domains',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => [ '#',
+ 'Service',
+ 'Domain',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [ 'svcnum',
+ 'svc',
+ 'domain',
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ $link,
+ $link,
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rll'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $conf = new FS::Conf;
+
+my $orderby = 'ORDER BY svcnum';
+my %svc_domain = ();
+my @extra_sql = ();
+if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $orderby = "ORDER BY $sortby";
+ }
+
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ push @extra_sql, "svcpart = $1";
+} else {
+ $cgi->param('domain') =~ /^([\w\-\.]+)$/;
+ $svc_domain{'domain'} = $1;
+}
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+
+my $extra_sql = '';
+if ( @extra_sql ) {
+ $extra_sql = ( keys(%svc_domain) ? ' AND ' : ' WHERE ' ).
+ join(' AND ', @extra_sql );
+}
+
+my $count_query = "SELECT COUNT(*) FROM svc_domain $addl_from ";
+if ( keys %svc_domain ) {
+ $count_query .= ' WHERE '.
+ join(' AND ', map "$_ = ". dbh->quote($svc_domain{$_}),
+ keys %svc_domain
+ );
+}
+$count_query .= $extra_sql;
+
+my $sql_query = {
+ 'table' => 'svc_domain',
+ 'hashref' => \%svc_domain,
+ 'select' => join(', ',
+ 'svc_domain.*',
+ 'part_svc.svc',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => "$extra_sql $orderby",
+ 'addl_from' => $addl_from,
+};
+
+my $link = [ "${p}view/svc_domain.cgi?", 'svcnum' ];
+
+#smaller false laziness w/svc_*.cgi here
+my $link_cust = sub {
+ my $svc_x = shift;
+ $svc_x->custnum ? [ "${p}view/cust_main.cgi?", 'custnum' ] : '';
+};
+
+</%init>
diff --git a/httemplate/search/svc_external.cgi b/httemplate/search/svc_external.cgi
new file mode 100755
index 0000000..2710d75
--- /dev/null
+++ b/httemplate/search/svc_external.cgi
@@ -0,0 +1,153 @@
+%die "access denied"
+% unless $FS::CurrentUser::CurrentUser->access_right('List services');
+%
+%my $conf = new FS::Conf;
+%
+%my @svc_external = ();
+%my @h_svc_external = ();
+%my $sortby=\*svcnum_sort;
+%if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+%
+% @svc_external=qsearch('svc_external',{});
+%
+% if ( $cgi->param('magic') eq 'unlinked' ) {
+% @svc_external = grep { qsearchs('cust_svc', {
+% 'svcnum' => $_->svcnum,
+% 'pkgnum' => '',
+% }
+% )
+% }
+% @svc_external;
+% }
+%
+% if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+% my $sortby = $1;
+% if ( $sortby eq 'id' ) {
+% $sortby = \*id_sort;
+% }
+% }
+%
+%} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+%
+% @svc_external =
+% qsearch( 'svc_external', {}, '',
+% " WHERE $1 = ( SELECT svcpart FROM cust_svc ".
+% " WHERE cust_svc.svcnum = svc_external.svcnum ) "
+% );
+%
+%} elsif ( $cgi->param('title') =~ /^(.*)$/ ) {
+% $sortby=\*id_sort;
+% @svc_external=qsearch('svc_external',{ title => $1 });
+% if( $cgi->param('history') == 1 ) {
+% @h_svc_external=qsearch('h_svc_external',{ title => $1 });
+% }
+%} elsif ( $cgi->param('id') =~ /^([\w\-\.]+)$/ ) {
+% my $id = $1;
+% @svc_external = qsearchs('svc_external',{'id'=>$id});
+%}
+%
+%if ( scalar(@svc_external) == 1 ) {
+%
+%
+<% $cgi->redirect(popurl(2). "view/svc_external.cgi?". $svc_external[0]->svcnum) %>
+%
+%
+%} elsif ( scalar(@svc_external) == 0 ) {
+%
+%
+<% include('/elements/header.html', 'External Search Results' ) %>
+
+ No matching external services found
+% } else {
+%
+%
+<% include('/elements/header.html', 'External Search Results', '') %>
+
+ <% scalar(@svc_external) %> matching external services found
+ <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
+ <TR>
+ <TH>Service #</TH>
+ <TH><% FS::Msgcat::_gettext('svc_external-id') || 'External&nbsp;ID' %></TH>
+ <TH><% FS::Msgcat::_gettext('svc_external-title') || 'Title' %></TH>
+ </TR>
+%
+% foreach my $svc_external (
+% sort $sortby (@svc_external)
+% ) {
+% my($svcnum, $id, $title)=(
+% $svc_external->svcnum,
+% $svc_external->id,
+% $svc_external->title,
+% );
+%
+% my $rowspan = 1;
+%
+% print <<END;
+% <TR>
+% <TD ROWSPAN=$rowspan><A HREF="${p}view/svc_external.cgi?$svcnum">$svcnum</A></TD>
+% <TD ROWSPAN=$rowspan><A HREF="${p}view/svc_external.cgi?$svcnum">$id</A></TD>
+% <TD ROWSPAN=$rowspan><A HREF="${p}view/svc_external.cgi?$svcnum">$title</A></TD>
+%END
+%
+% #print @rows;
+% print "</TR>";
+%
+% }
+% if( scalar(@h_svc_external) > 0 ) {
+% print <<HTML;
+% </TABLE>
+% <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
+% <TR>
+% <TH>Freeside ID</TH>
+% <TH>Service #</TH>
+% <TH>Title</TH>
+% <TH>Date</TH>
+% </TR>
+%HTML
+%
+% foreach my $h_svc ( @h_svc_external ) {
+% my($svcnum, $id, $title, $user, $date)=(
+% $h_svc->svcnum,
+% $h_svc->id,
+% $h_svc->title,
+% $h_svc->history_user,
+% $h_svc->history_date,
+% );
+% my $rowspan = 1;
+% my ($h_cust_svc) = qsearchs( 'h_cust_svc', {
+% svcnum => $svcnum,
+% });
+% my $cust_pkg = qsearchs( 'cust_pkg', {
+% pkgnum => $h_cust_svc->pkgnum,
+% });
+% my $custnum = $cust_pkg->custnum;
+%
+% print <<END;
+% <TR>
+% <TD ROWSPAN=$rowspan><A HREF="${p}view/cust_main.cgi?$custnum">$custnum</A></TD>
+% <TD ROWSPAN=$rowspan><A HREF="${p}view/cust_main.cgi?$custnum">$svcnum</A></TD>
+% <TD ROWSPAN=$rowspan><A HREF="${p}view/cust_main.cgi?$custnum">$title</A></TD>
+% <TD ROWSPAN=$rowspan><A HREF="${p}view/cust_main.cgi?$custnum">$date</A></TD>
+% </TR>
+%END
+% }
+% }
+%
+% print <<END;
+% </TABLE>
+% </BODY>
+%</HTML>
+%END
+%
+%}
+%
+%sub svcnum_sort {
+% $a->getfield('svcnum') <=> $b->getfield('svcnum');
+%}
+%
+%sub id_sort {
+% $a->getfield('id') <=> $b->getfield('id');
+%}
+%
+%
+
diff --git a/httemplate/search/svc_forward.cgi b/httemplate/search/svc_forward.cgi
new file mode 100755
index 0000000..2bcd0c8
--- /dev/null
+++ b/httemplate/search/svc_forward.cgi
@@ -0,0 +1,146 @@
+<% include( 'elements/search.html',
+ 'title' => "Mail forward Search Results",
+ 'name' => 'mail forwards',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => [ '#',
+ 'Service',
+ 'Mail to',
+ 'Forwards to',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [ 'svcnum',
+ 'svc',
+ $format_src,
+ $format_dst,
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ $link,
+ $link_src,
+ $link_dst,
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rlll'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $conf = new FS::Conf;
+
+my $orderby = 'ORDER BY svcnum';
+my @extra_sql = ();
+if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $orderby = "ORDER BY $sortby";
+ }
+
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ push @extra_sql, "svcpart = $1";
+}
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+
+my $extra_sql =
+ scalar(@extra_sql)
+ ? ' WHERE '. join(' AND ', @extra_sql )
+ : '';
+
+my $count_query = "SELECT COUNT(*) FROM svc_forward $addl_from $extra_sql";
+my $sql_query = {
+ 'table' => 'svc_forward',
+ 'hashref' => {},
+ 'select' => join(', ',
+ 'svc_forward.*',
+ 'part_svc.svc',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => "$extra_sql $orderby",
+ 'addl_from' => $addl_from,
+};
+
+# <TH>Service #<BR><FONT SIZE=-1>(click to view forward)</FONT></TH>
+# <TH>Mail to<BR><FONT SIZE=-1>(click to view account)</FONT></TH>
+# <TH>Forwards to<BR><FONT SIZE=-1>(click to view account)</FONT></TH>
+
+my $link = [ "${p}view/svc_forward.cgi?", 'svcnum' ];
+
+my $format_src = sub {
+ my $svc_forward = shift;
+ if ( $svc_forward->srcsvc_acct ) {
+ $svc_forward->srcsvc_acct->email;
+ } else {
+ my $src = $svc_forward->src;
+ $src = "<I>(anything)</I>$src" if $src =~ /^@/;
+ $src;
+ }
+};
+
+my $link_src = sub {
+ my $svc_forward = shift;
+ if ( $svc_forward->srcsvc_acct ) {
+ [ "${p}view/svc_acct.cgi?", 'srcsvc' ];
+ } else {
+ '';
+ }
+};
+
+my $format_dst = sub {
+ my $svc_forward = shift;
+ if ( $svc_forward->dstsvc_acct ) {
+ $svc_forward->dstsvc_acct->email;
+ } else {
+ $svc_forward->dst;
+ }
+};
+
+my $link_dst = sub {
+ my $svc_forward = shift;
+ if ( $svc_forward->dstsvc_acct ) {
+ [ "${p}view/svc_acct.cgi?", 'dstsvc' ];
+ } else {
+ '';
+ }
+};
+
+#smaller false laziness w/svc_*.cgi here
+my $link_cust = sub {
+ my $svc_x = shift;
+ $svc_x->custnum ? [ "${p}view/cust_main.cgi?", 'custnum' ] : '';
+};
+
+</%init>
diff --git a/httemplate/search/svc_phone.cgi b/httemplate/search/svc_phone.cgi
new file mode 100644
index 0000000..49340c6
--- /dev/null
+++ b/httemplate/search/svc_phone.cgi
@@ -0,0 +1,117 @@
+<% include( 'elements/search.html',
+ 'title' => "Phone number search results",
+ 'name' => 'phone numbers',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => [ '#',
+ 'Service',
+ 'Country code',
+ 'Phone number',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [ 'svcnum',
+ 'svc',
+ 'countrycode',
+ 'phonenum',
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ $link,
+ $link,
+ $link,
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rlrr'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $conf = new FS::Conf;
+
+my $orderby = 'ORDER BY svcnum';
+my %svc_phone = ();
+my @extra_sql = ();
+if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $orderby = "ORDER BY $sortby";
+ }
+
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ push @extra_sql, "svcpart = $1";
+} else {
+ $cgi->param('phonenum') =~ /^([\d\- ]+)$/;
+ ( $svc_phone{'phonenum'} = $1 ) =~ s/\D//g;
+}
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+
+my $extra_sql = '';
+if ( @extra_sql ) {
+ $extra_sql = ( keys(%svc_phone) ? ' AND ' : ' WHERE ' ).
+ join(' AND ', @extra_sql );
+}
+
+my $count_query = "SELECT COUNT(*) FROM svc_phone $addl_from ";
+if ( keys %svc_phone ) {
+ $count_query .= ' WHERE '.
+ join(' AND ', map "$_ = ". dbh->quote($svc_phone{$_}),
+ keys %svc_phone
+ );
+}
+$count_query .= $extra_sql;
+
+my $sql_query = {
+ 'table' => 'svc_phone',
+ 'hashref' => \%svc_phone,
+ 'select' => join(', ',
+ 'svc_phone.*',
+ 'part_svc.svc',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => "$extra_sql $orderby",
+ 'addl_from' => $addl_from,
+};
+
+my $link = [ "${p}view/svc_phone.cgi?", 'svcnum' ];
+
+#smaller false laziness w/svc_*.cgi here
+my $link_cust = sub {
+ my $svc_x = shift;
+ $svc_x->custnum ? [ "${p}view/cust_main.cgi?", 'custnum' ] : '';
+};
+
+</%init>
diff --git a/httemplate/search/svc_www.cgi b/httemplate/search/svc_www.cgi
new file mode 100755
index 0000000..2e3c461
--- /dev/null
+++ b/httemplate/search/svc_www.cgi
@@ -0,0 +1,113 @@
+<% include( 'elements/search.html',
+ 'title' => 'Virtual Host Search Results',
+ 'name' => 'virtual hosts',
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => [ '#',
+ 'Service',
+ 'Zone',
+ 'User',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [ 'svcnum',
+ 'svc',
+ sub { $_[0]->domain_record->zone },
+ sub {
+ my $svc_www = shift;
+ my $svc_acct = $svc_www->svc_acct;
+ $svc_acct
+ ? $svc_acct->email
+ : '';
+ },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ $ulink,
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rlll'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+#my $conf = new FS::Conf;
+
+my $orderby = 'ORDER BY svcnum';
+my @extra_sql = ();
+if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $orderby = "ORDER BY $sortby";
+ }
+
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ push @extra_sql, "svcpart = $1";
+}
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+
+my $extra_sql =
+ scalar(@extra_sql)
+ ? ' WHERE '. join(' AND ', @extra_sql )
+ : '';
+
+
+my $count_query = "SELECT COUNT(*) FROM svc_www $addl_from $extra_sql";
+my $sql_query = {
+ 'table' => 'svc_www',
+ 'hashref' => {},
+ 'select' => join(', ',
+ 'svc_www.*',
+ 'part_svc.svc',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ ),
+ 'extra_sql' => "$extra_sql $orderby",
+ 'addl_from' => $addl_from,
+};
+
+my $link = [ "${p}view/svc_www.cgi?", 'svcnum', ];
+#my $dlink = [ "${p}view/svc_www.cgi?", 'svcnum', ];
+my $ulink = [ "${p}view/svc_acct.cgi?", 'usersvc', ];
+
+#smaller false laziness w/svc_*.cgi here
+my $link_cust = sub {
+ my $svc_x = shift;
+ $svc_x->custnum ? [ "${p}view/cust_main.cgi?", 'custnum' ] : '';
+};
+
+</%init>
diff --git a/httemplate/search/timeworked.html b/httemplate/search/timeworked.html
new file mode 100644
index 0000000..b72dd0e
--- /dev/null
+++ b/httemplate/search/timeworked.html
@@ -0,0 +1,117 @@
+<% include( 'elements/search.html',
+ 'title' => 'Time Worked',
+ 'name' => 'time',
+ 'html_form' => qq!<FORM NAME="timeForm" ACTION="${p}misc/timeworked.html" METHOD="POST">!,
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Ticket',
+ 'Date',
+ 'Time',
+ '', # checkbox column
+ ],
+ 'fields' => [ sub { shift->[0] },
+ sub { encode_entities(shift->[1]) },
+ sub { shift->[2] },
+ sub { my $seconds = shift->[3];
+ (($seconds < 0) ? '-' : '') .
+ concise(duration($seconds));
+ },
+ sub {
+ my $row = shift;
+ my $seconds = $row->[3];
+ my $id = $row->[4];
+ qq!<INPUT NAME="transactionid$id" TYPE="checkbox" VALUE="1">!.
+ qq!<INPUT NAME="seconds$id" TYPE="hidden" VALUE="$seconds">!;
+ },
+ ],
+ 'links' => [
+ $link,
+ $link,
+ '',
+ '',
+ '',
+ ],
+ 'html_foot' => sub {
+ '<BR><INPUT TYPE="button" VALUE="select all" onClick="setAll(true)">'.
+ '<INPUT TYPE="button" VALUE="unselect all" onClick="setAll(false)">'.
+ '<BR><INPUT TYPE="submit" NAME="action" VALUE="Assign to accounts"><BR>'.
+ '<SCRIPT TYPE="text/javascript">'.
+ ' function setAll(setTo) { '.
+ ' theForm = document.timeForm;'.
+ ' for (i=0,n=theForm.elements.length;i<n;i++)'.
+ ' if (theForm.elements[i].name.indexOf("transactionid") != -1)'.
+ ' theForm.elements[i].checked = setTo;'.
+ ' }'.
+ '</SCRIPT>';
+ },
+ )
+
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Time queue');
+
+my @groupby = ();
+
+my $transactiontime = "
+ CASE Transactions.Type WHEN 'Set'
+ THEN (TO_NUMBER(NewValue,'999999')-TO_NUMBER(OldValue, '999999')) * 60
+ ELSE TimeTaken*60
+ END
+";
+
+push @groupby, qw( transactions.type newvalue oldvalue timetaken );
+
+my $appliedtimeclause = "COALESCE (SUM(acct_rt_transaction.seconds), 0)";
+
+my $appliedtimeselect = "
+ COALESCE(
+ ( SELECT SUM(seconds) FROM acct_rt_transaction
+ WHERE transaction_id = Transactions.id
+ ),
+ 0
+ )
+";
+
+push @groupby, "Transactions.id";
+
+my $wheretimeleft = "$transactiontime != $appliedtimeselect";
+
+push @groupby, "Tickets.id";
+push @groupby, "Tickets.Subject";
+push @groupby, "Transactions.Created";
+
+my $groupby = join(',', @groupby);
+
+my $where = "
+ WHERE ObjectType='RT::Ticket'
+ AND ( ( Transactions.Type='Set' AND Field='TimeWorked' )
+ OR Transactions.Type='Create'
+ OR Transactions.Type='Comment'
+ OR Transactions.Type='Correspond'
+ )
+ AND $wheretimeleft
+";
+ #AND $wheretimeleft
+
+my $query = "
+ SELECT Tickets.id, Tickets.Subject,
+ TO_CHAR(Transactions.Created, 'Dy Mon DD HH24:MI:SS YYYY'),
+ $transactiontime-$appliedtimeclause,
+ Transactions.id
+ FROM Transactions
+ JOIN Tickets ON Transactions.ObjectId = Tickets.id
+ LEFT JOIN acct_rt_transaction
+ ON Transactions.id = acct_rt_transaction.transaction_id
+ $where
+ GROUP BY $groupby
+ ORDER BY Transactions.Created
+";
+
+my $count_query = "SELECT COUNT(*) FROM Transactions $where";
+
+my $link = [ "${p}rt/Ticket/Display.html?id=", sub { shift->[0]; } ];
+
+</%init>
diff --git a/httemplate/view/REAL_logo.cgi b/httemplate/view/REAL_logo.cgi
new file mode 100755
index 0000000..c269c7d
--- /dev/null
+++ b/httemplate/view/REAL_logo.cgi
@@ -0,0 +1,14 @@
+<% $conf->config_binary("logo.png", $agentnum) %>
+<%init>
+
+my $conf = new FS::Conf;
+
+my $agentnum = '';
+my @agentnums = $FS::CurrentUser::CurrentUser->agentnums;
+if ( scalar(@agentnums) == 1 ) {
+ $agentnum = $agentnums[0];
+}
+
+http_header('Content-Type' => 'image/png' );
+
+</%init>
diff --git a/httemplate/view/cust_bill-logo.cgi b/httemplate/view/cust_bill-logo.cgi
new file mode 100755
index 0000000..09ac9a7
--- /dev/null
+++ b/httemplate/view/cust_bill-logo.cgi
@@ -0,0 +1,31 @@
+<% $conf->config_binary("logo$templatename.png", $agentnum) %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View invoices')
+ or $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+my $templatename;
+my $agentnum = '';
+if ( $cgi->param('invnum') ) {
+ $templatename = $cgi->param('templatename');
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $cgi->param('invnum') } )
+ or die 'unknown invnum';
+ $agentnum = $cust_bill->cust_main->agentnum;
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^([^\.\/]*)$/ or die 'illegal query';
+ $templatename = $1;
+}
+
+if ( $templatename && $conf->exists("logo_$templatename.png") ) {
+ $templatename = "_$templatename";
+} else {
+ $templatename = '';
+}
+
+http_header('Content-Type' => 'image/png' );
+
+</%init>
diff --git a/httemplate/view/cust_bill-pdf.cgi b/httemplate/view/cust_bill-pdf.cgi
new file mode 100755
index 0000000..f09e1b7
--- /dev/null
+++ b/httemplate/view/cust_bill-pdf.cgi
@@ -0,0 +1,28 @@
+<% $pdf %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View invoices');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^((.+)-)?(\d+)(.pdf)?$/;
+my $templatename = $2;
+my $invnum = $3;
+
+my $cust_bill = qsearchs({
+ 'select' => 'cust_bill.*',
+ 'table' => 'cust_bill',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'invnum' => $invnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die "Invoice #$invnum not found!" unless $cust_bill;
+
+my $pdf = $cust_bill->print_pdf( '', $templatename);
+
+http_header('Content-Type' => 'application/pdf' );
+http_header('Content-Length' => length($pdf) );
+http_header('Cache-control' => 'max-age=60' );
+
+</%init>
diff --git a/httemplate/view/cust_bill-ps.cgi b/httemplate/view/cust_bill-ps.cgi
new file mode 100755
index 0000000..5313dbf
--- /dev/null
+++ b/httemplate/view/cust_bill-ps.cgi
@@ -0,0 +1,24 @@
+<% $cust_bill->print_ps( '', $templatename) %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View invoices');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^((.+)-)?(\d+)$/;
+my $templatename = $2;
+my $invnum = $3;
+
+my $cust_bill = qsearchs({
+ 'select' => 'cust_bill.*',
+ 'table' => 'cust_bill',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'invnum' => $invnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die "Invoice #$invnum not found!" unless $cust_bill;
+
+http_header('Content-Type' => 'application/postscript' );
+
+</%init>
diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi
new file mode 100755
index 0000000..450c74e
--- /dev/null
+++ b/httemplate/view/cust_bill.cgi
@@ -0,0 +1,120 @@
+<% include("/elements/header.html",'Invoice View', menubar(
+ "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+)) %>
+
+
+% if ( $cust_bill->owed > 0
+% && scalar( grep $payby{$_}, qw(BILL CASH WEST MCRD) )
+% && $FS::CurrentUser::CurrentUser->access_right('Post payment')
+% )
+% {
+% my $s = 0;
+
+ Post
+
+% if ( $payby{'BILL'} ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_pay.cgi?payby=BILL;invnum=<% $invnum %>">check</A>
+% }
+
+% if ( $payby{'CASH'} ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_pay.cgi?payby=CASH;invnum=<% $invnum %>">cash</A>
+% }
+
+% if ( $payby{'WEST'} ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_pay.cgi?payby=WEST;invnum=<% $invnum %>">Western Union</A>
+% }
+
+% if ( $payby{'MCRD'} ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_pay.cgi?payby=MCRD;invnum=<% $invnum %>">manual credit card</A>
+% }
+
+ payment against this invoice<BR><BR>
+
+% }
+
+
+% if ( $FS::CurrentUser::CurrentUser->access_right('Resend invoices') ) {
+
+ <A HREF="<% $p %>misc/print-invoice.cgi?<% $link %>">Re-print this invoice</A>
+
+% if ( grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ) {
+ | <A HREF="<% $p %>misc/email-invoice.cgi?<% $link %>">Re-email this invoice</A>
+% }
+
+% if ( $conf->exists('hylafax') && length($cust_bill->cust_main->fax) ) {
+ | <A HREF="<% $p %>misc/fax-invoice.cgi?<% $link %>">Re-fax this invoice</A>
+% }
+
+ <BR><BR>
+
+% }
+
+
+% if ( $conf->exists('invoice_latex') ) {
+
+ <A HREF="<% $p %>view/cust_bill-pdf.cgi?<% $link %>.pdf">View typeset invoice</A>
+ <BR><BR>
+% }
+
+% my $br = 0;
+% if ( $cust_bill->num_cust_event ) { $br++;
+<A HREF="<%$p%>search/cust_event.html?invnum=<% $cust_bill->invnum %>">(&nbsp;View invoice events&nbsp;)</A>
+% }
+
+% if ( $cust_bill->num_cust_bill_event ) { $br++;
+<A HREF="<%$p%>search/cust_bill_event.cgi?invnum=<% $cust_bill->invnum %>">(&nbsp;View deprecated, old-style invoice events&nbsp;)</A>
+% }
+
+<% $br ? '<BR><BR>' : '' %>
+
+% if ( $conf->exists('invoice_html') ) {
+
+ <% join('', $cust_bill->print_html('', $templatename) ) %>
+% } else {
+
+ <PRE><% join('', $cust_bill->print_text('', $templatename) ) %></PRE>
+% }
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View invoices');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^((.+)-)?(\d+)$/;
+my $templatename = $2;
+my $invnum = $3;
+
+my $conf = new FS::Conf;
+
+my @payby = grep /\w/, $conf->config('payby');
+#@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
+@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
+ unless @payby;
+my %payby = map { $_=>1 } @payby;
+
+my $cust_bill = qsearchs({
+ 'select' => 'cust_bill.*',
+ 'table' => 'cust_bill',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'invnum' => $invnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die "Invoice #$invnum not found!" unless $cust_bill;
+
+my $custnum = $cust_bill->custnum;
+my $display_custnum = $cust_bill->cust_main->display_custnum;
+
+#my $printed = $cust_bill->printed;
+
+my $link = $templatename ? "$templatename-$invnum" : $invnum;
+
+</%init>
+
+
diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi
new file mode 100755
index 0000000..2231d41
--- /dev/null
+++ b/httemplate/view/cust_main.cgi
@@ -0,0 +1,158 @@
+<% include("/elements/header.html","Customer View: ". $cust_main->name ) %>
+
+% if ( $curuser->access_right('Edit customer') ) {
+ <A HREF="<% $p %>edit/cust_main.cgi?<% $custnum %>">Edit this customer</A> |
+% }
+
+<% include('/elements/init_overlib.html') %>
+
+<SCRIPT TYPE="text/javascript">
+function areyousure(href, message) {
+ if (confirm(message) == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
+% if ( $curuser->access_right('Cancel customer')
+% && $cust_main->ncancelled_pkgs
+% ) {
+
+ <% include( '/elements/popup_link-cust_main.html',
+ { 'action' => $p. 'misc/cancel_cust.html',
+ 'label' => 'Cancel&nbsp;this&nbsp;customer',
+ 'actionlabel' => 'Confirm Cancellation',
+ 'color' => '#ff0000',
+ 'cust_main' => $cust_main,
+ }
+ )
+ %> |
+
+% }
+
+% if ( $conf->exists('deletecustomers')
+% && $curuser->access_right('Delete customer')
+% ) {
+ <A HREF="<% $p %>misc/delete-customer.cgi?<% $custnum%>">Delete this customer</A> |
+% }
+
+% unless ( $conf->exists('disable_customer_referrals') ) {
+ <A HREF="<% $p %>edit/cust_main.cgi?referral_custnum=<% $custnum %>">Refer a new customer</A> |
+ <A HREF="<% $p %>search/cust_main.cgi?referral_custnum=<% $custnum %>">View this customer's referrals</A>
+% }
+
+<BR><BR>
+
+% if ( $curuser->access_right('Billing event reports')
+% || $curuser->access_right('View customer billing events')
+% ) {
+
+ <A HREF="<% $p %>search/cust_event.html?custnum=<% $custnum %>">View billing events for this customer</A>
+ <BR><BR>
+
+% }
+
+%my $signupurl = $conf->config('signupurl');
+%if ( $signupurl ) {
+ This customer's signup URL: <A HREF="<% $signupurl %>?ref=<% $custnum %>"><% $signupurl %>?ref=<% $custnum %></A><BR><BR>
+% }
+
+
+<A NAME="cust_main"></A>
+<TABLE BORDER=0>
+<TR>
+ <TD VALIGN="top">
+ <% include('cust_main/contacts.html', $cust_main ) %>
+ </TD>
+ <TD VALIGN="top" STYLE="padding-left: 54px">
+ <% include('cust_main/misc.html', $cust_main ) %>
+% if ( $conf->config('payby-default') ne 'HIDE' ) {
+
+ <BR>
+ <% include('cust_main/billing.html', $cust_main ) %>
+% }
+
+ </TD>
+</TR>
+</TABLE>
+%
+%if ( $cust_main->comments =~ /[^\s\n\r]/ ) {
+%
+
+<BR>
+Comments
+<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+<TR>
+ <TD BGCOLOR="#ffffff">
+ <PRE><% encode_entities($cust_main->comments) %></PRE>
+ </TD>
+</TR>
+</TABLE></TABLE>
+% }
+<BR><BR>
+% my $notecount = scalar($cust_main->notes());
+% if ( ! $conf->exists('cust_main-disable_notes') || $notecount) {
+
+<A NAME="cust_main_note"><FONT SIZE="+2">Notes</FONT></A><BR>
+% if ( $curuser->access_right('Add customer note') &&
+% ! $conf->exists('cust_main-disable_notes')
+% ) {
+
+ <% include( '/elements/popup_link-cust_main.html',
+ 'label' => 'Add customer note',
+ 'action' => $p. 'edit/cust_main_note.cgi',
+ 'actionlabel' => 'Enter customer note',
+ 'cust_main' => $cust_main,
+ 'width' => 616,
+ 'height' => 408,
+ )
+ %>
+
+% }
+
+<BR>
+
+<% include('cust_main/notes.html', 'custnum' => $cust_main->custnum ) %>
+
+% }
+
+
+% if ( $conf->config('ticket_system') ) {
+
+ <BR><BR>
+ <% include('cust_main/tickets.html', $cust_main ) %>
+% }
+
+
+<BR><BR>
+
+% #XXX enable me# if ( $curuser->access_right('View customer packages') {
+<% include('cust_main/packages.html', $cust_main ) %>
+% #}
+
+% if ( $conf->config('payby-default') ne 'HIDE' ) {
+ <% include('cust_main/payment_history.html', $cust_main ) %>
+% }
+
+
+<% include('/elements/footer.html') %>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('View customer');
+
+my $conf = new FS::Conf;
+
+die "No customer specified (bad URL)!" unless $cgi->keywords;
+my($query) = $cgi->keywords; # needs parens with my, ->keywords returns array
+$query =~ /^(\d+)$/;
+my $custnum = $1;
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+die "Customer not found!" unless $cust_main;
+
+</%init>
diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html
new file mode 100644
index 0000000..aea90e8
--- /dev/null
+++ b/httemplate/view/cust_main/billing.html
@@ -0,0 +1,220 @@
+Billing information
+%# If we can't see the unencrypted card, then bill now is an exercise in
+%# frustration (without some sort of job queue magic to send it to a secure
+%# machine, anyway)
+%if ( $FS::CurrentUser::CurrentUser->access_right('Bill customer now')
+% && ! $cust_main->is_encrypted($cust_main->payinfo)
+% ) {
+ (<A HREF="<% $p %>misc/bill.cgi?<% $cust_main->custnum %>">Bill now</A>)
+% }
+
+<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+
+%( my $balance = $cust_main->balance )
+% =~ s/^(\-?)(.*)$/<FONT SIZE=+1>$1<\/FONT>$money_char$2/;
+
+<TR>
+ <TD ALIGN="right">Balance due</TD>
+ <TD BGCOLOR="#ffffff"><B><% $balance %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Billing&nbsp;type</TD>
+ <TD BGCOLOR="#ffffff">
+% if ( $cust_main->payby eq 'CARD' || $cust_main->payby eq 'DCRD' ) {
+
+
+ Credit&nbsp;card&nbsp;<% $cust_main->payby eq 'CARD' ? '(automatic)' : '(on-demand)' %>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Card number</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->paymask %></TD>
+</TR>
+%
+%#false laziness w/elements/select-month_year.html & edit/cust_main/billing.html
+%my( $mon, $year );
+%my $date = $cust_main->paydate || '12-2037';
+%if ( $date =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
+% ( $mon, $year ) = ( $2, $1 );
+%} elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+% ( $mon, $year ) = ( $1, $3 );
+%} else {
+% warn "unrecognized expiration date format: $date";
+% ( $mon, $year ) = ( '', '' );
+%}
+%
+
+<TR>
+ <TD ALIGN="right">Expiration</TD>
+ <TD BGCOLOR="#ffffff"><% "$mon/$year" %></TD>
+</TR>
+% if ( $cust_main->paystart_month ) {
+
+ <TR>
+ <TD ALIGN="right">Start date</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->paystart_month. '/'. $cust_main->paystart_year %>
+ </TR>
+% } elsif ( $cust_main->payissue ) {
+
+ <TR>
+ <TD ALIGN="right">Issue #</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->payissue %>
+ </TR>
+% }
+
+
+<TR>
+ <TD ALIGN="right">Name on card</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->payname %></TD>
+</TR>
+% } elsif ( $cust_main->payby eq 'CHEK' || $cust_main->payby eq 'DCHK') {
+% my( $account, $aba ) = split('@', $cust_main->paymask );
+
+
+ Electronic&nbsp;check&nbsp;<% $cust_main->payby eq 'CHEK' ? '(automatic)' : '(on-demand)' %>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">ABA/Routing code</TD>
+ <TD BGCOLOR="#ffffff"><% $aba %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Account number</TD>
+ <TD BGCOLOR="#ffffff"><% $account %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Account type</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->paytype %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Bank name</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->payname %></TD>
+</TR>
+% if ( $conf->exists('show_bankstate') ) {
+<TR>
+ <TD ALIGN="right"><% $paystate_label %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->paystate || '&nbsp;&nbsp;&nbsp;' %></TD>
+</TR>
+% }
+% } elsif ( $cust_main->payby eq 'LECB' ) {
+% $cust_main->payinfo =~ /^(\d{3})(\d{3})(\d{4})$/;
+% my $payinfo = "$1-$2-$3";
+%
+
+
+ Phone&nbsp;bill&nbsp;billing
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Phone number</TD>
+ <TD BGCOLOR="#ffffff"><% $payinfo %></TD>
+</TR>
+% } elsif ( $cust_main->payby eq 'BILL' ) {
+
+
+ Billing
+ </TD>
+</TR>
+% if ( $cust_main->payinfo ) {
+
+<TR>
+ <TD ALIGN="right">P.O. </TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->payinfo %></TD>
+</TR>
+% }
+
+
+<TR>
+ <TD ALIGN="right">Attention</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->payname %></TD>
+</TR>
+% } elsif ( $cust_main->payby eq 'COMP' ) {
+
+
+ Complimentary
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Authorized&nbsp;by</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->payinfo %></TD>
+</TR>
+%
+%#false laziness w/above etc.
+%my( $mon, $year );
+%my $date = $cust_main->paydate || '12-2037';
+%if ( $date =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format
+% ( $mon, $year ) = ( $2, $1 );
+%} elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+% ( $mon, $year ) = ( $1, $3 );
+%} else {
+% warn "unrecognized expiration date format: $date";
+% ( $mon, $year ) = ( '', '' );
+%}
+%
+
+<TR>
+ <TD ALIGN="right">Expiration</TD>
+ <TD BGCOLOR="#ffffff"><% "$mon/$year" %></TD>
+</TR>
+% }
+
+
+<TR>
+ <TD ALIGN="right">Tax&nbsp;exempt</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->tax ? 'yes' : 'no' %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Postal&nbsp;invoices</TD>
+ <TD BGCOLOR="#ffffff">
+ <% ( grep { $_ eq 'POST' } @invoicing_list ) ? 'yes' : 'no' %>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">FAX&nbsp;invoices</TD>
+ <TD BGCOLOR="#ffffff">
+ <% ( grep { $_ eq 'FAX' } @invoicing_list ) ? 'yes' : 'no' %>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Email&nbsp;invoices</TD>
+ <TD BGCOLOR="#ffffff">
+ <% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) || 'no' %>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Invoice&nbsp;terms</TD>
+ <TD BGCOLOR="#ffffff">
+ <% $cust_main->invoice_terms || 'Default ('. ( $conf->config('invoice_default_terms') || 'Payable upon receipt' ). ')' %>
+ </TD>
+</TR>
+
+% if ( $conf->exists('voip-cust_cdr_spools') ) {
+ <TR>
+ <TD ALIGN="right">Spool&nbsp;CDRs</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->spool_cdr ? 'yes' : 'no' %></TD>
+ </TR>
+% }
+
+% if ( $conf->exists('voip-cust_cdr_squelch') ) {
+ <TR>
+ <TD ALIGN="right">Print&nbsp;CDRs</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->squelch_cdr ? 'no' : 'yes' %></TD>
+ </TR>
+% }
+
+</TABLE></TD></TR></TABLE>
+<%once>
+
+my $paystate_label = FS::Msgcat::_gettext('paystate');
+$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/;
+
+</%once>
+<%init>
+
+my( $cust_main ) = @_;
+my @invoicing_list = $cust_main->invoicing_list;
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/view/cust_main/contacts.html b/httemplate/view/cust_main/contacts.html
new file mode 100644
index 0000000..e88c02e
--- /dev/null
+++ b/httemplate/view/cust_main/contacts.html
@@ -0,0 +1,122 @@
+% my %which = (
+% '' => 'Billing',
+% 'ship_' => 'Service',
+% );
+% foreach my $which ( '', 'ship_' ) {
+% my $pre = $cust_main->get("${which}last") ? $which : '';
+
+<% $which{$which} %> address
+<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+<TR>
+ <TD ALIGN="right">Contact name</TD>
+ <TD COLSPAN=5 BGCOLOR="#ffffff">
+ <% $cust_main->get("${pre}last"). ', '. $cust_main->get("${pre}first") %>
+ </TD>
+% if ( $which eq '' && $conf->exists('show_ss') ) {
+ <TD ALIGN="right">SS#</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->masked('ss') || '&nbsp' %></TD>
+% }
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}company") %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Address</TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address1") %></TD>
+</TR>
+
+% if ( $cust_main->get("${pre}address2") ) {
+% my $address2_label =
+% ( $conf->exists('cust_main-require_address2')
+% && ! ( $pre xor $cust_main->has_ship_address )
+% )
+% ? 'Unit&nbsp;#'
+% : '&nbsp;';
+
+ <TR>
+ <TD ALIGN="right"><% $address2_label %></TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address2") %></TD>
+ </TR>
+
+% }
+
+<TR>
+ <TD ALIGN="right">City</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}city") %></TD>
+% if ( $cust_main->get("${pre}county") ) {
+ <TD ALIGN="right">County</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}county") %></TD>
+% }
+ <TD ALIGN="right">State</TD>
+ <TD BGCOLOR="#ffffff"><% state_label( $cust_main->get("${pre}state"), $cust_main->get("${pre}country") ) %></TD>
+ <TD ALIGN="right">Zip</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}zip") %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Country</TD>
+ <TD BGCOLOR="#ffffff"><% code2country( $cust_main->get("${pre}country") ) %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right"><% $daytime_label %></TD>
+ <TD COLSPAN=3 BGCOLOR="#ffffff">
+ <% include('/elements/phonenumber.html',
+ $cust_main->get("${pre}daytime"),
+ 'callable'=>1
+ )
+ %>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right"><% $night_label %></TD>
+ <TD COLSPAN=3 BGCOLOR="#ffffff">
+ <% include('/elements/phonenumber.html',
+ $cust_main->get("${pre}night"),
+ 'callable'=>1
+ )
+ %>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=3 BGCOLOR="#ffffff">
+ <% $cust_main->get("${pre}fax") || '&nbsp' %>
+ </TD>
+</TR>
+% if ( $which eq '' && $conf->exists('show_stateid') ) {
+ <TR>
+ <TD ALIGN="right"><% $stateid_label %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->masked('stateid') || '&nbsp' %></TD>
+ <TD ALIGN="right"><% $stateid_state_label %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->stateid_state || '&nbsp' %></TD>
+ </TR>
+% }
+</TABLE></TD></TR></TABLE>
+% if ( $which ne 'ship_' ) {
+<BR>
+% }
+% }
+
+<%once>
+
+my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/
+ ? 'Day&nbsp;Phone'
+ : FS::Msgcat::_gettext('daytime');
+my $night_label = FS::Msgcat::_gettext('night') =~ /^(night)?$/
+ ? 'Night&nbsp;Phone'
+ : FS::Msgcat::_gettext('night');
+my $stateid_label = FS::Msgcat::_gettext('stateid') =~ /^(stateid)?$/
+ ? 'Driver&rsquo;s&nbsp;License'
+ : FS::Msgcat::_gettext('stateid');
+my $stateid_state_label = FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_state)?$/
+ ? 'Driver&rsquo;s&nbsp;License State'
+ : FS::Msgcat::_gettext('stateid_state');
+
+</%once>
+<%init>
+
+my( $cust_main ) = @_;
+my $conf = new FS::Conf;
+
+</%init>
+
diff --git a/httemplate/view/cust_main/misc.html b/httemplate/view/cust_main/misc.html
new file mode 100644
index 0000000..060da87
--- /dev/null
+++ b/httemplate/view/cust_main/misc.html
@@ -0,0 +1,110 @@
+<% ntable("#cccccc") %><TR><TD><% &ntable("#cccccc",2) %>
+
+<TR>
+ <TD ALIGN="right">Customer&nbsp;number</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->display_custnum %></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Status</TD>
+ <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_main->statuscolor %>"><B><% ucfirst($cust_main->status) %></B></FONT></TD>
+</TR>
+
+%my $agent;
+%if ( $num_agents == 1 ) {
+% my @agents = qsearchs( 'agent', {} );
+% $agent = $agents[0];
+%} else {
+% $agent = qsearchs('agent',{ 'agentnum' => $cust_main->agentnum } );
+ <TR>
+ <TD ALIGN="right">Agent</TD>
+ <TD BGCOLOR="#ffffff"><% $agent->agentnum %>: <% $agent->agent %></TD>
+ </TR>
+% }
+
+% if ( $cust_main->agent_custid
+% && ! $conf->exists('cust_main-default_agent_custid') ) {
+
+<TR>
+ <TD ALIGN="right">Agent customer ref#</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->agent_custid %></TD>
+</TR>
+%
+% }
+%
+% unless ( FS::part_referral->num_part_referral == 1 ) {
+% my $referral = qsearchs('part_referral', {
+% 'refnum' => $cust_main->refnum
+% } );
+%
+
+
+<TR>
+ <TD ALIGN="right">Advertising&nbsp;source</TD>
+ <TD BGCOLOR="#ffffff"><% $referral->refnum %>: <% $referral->referral%></TD>
+</TR>
+% }
+
+
+<TR>
+ <TD ALIGN="right">Referring&nbsp;Customer</TD>
+ <TD BGCOLOR="#ffffff">
+%
+% my $referring_cust_main = '';
+% if ( $cust_main->referral_custnum
+% && ( $referring_cust_main =
+% qsearchs('cust_main', { custnum => $cust_main->referral_custnum } )
+% )
+% ) {
+%
+
+
+<A HREF="<% popurl(1) %>cust_main.cgi?<% $cust_main->referral_custnum %>"><%$cust_main->referral_custnum %>:
+<%
+ ( $referring_cust_main->company
+ ? $referring_cust_main->company. ' ('.
+ $referring_cust_main->last. ', '. $referring_cust_main->first.
+ ')'
+ : $referring_cust_main->last. ', '. $referring_cust_main->first
+ )
+%></A>
+% }
+
+
+ </TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Order taker</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->otaker %></TD>
+</TR>
+
+ <TR>
+ <TD ALIGN="right">Signup Date</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->signupdate ? time2str($date_format, $cust_main->signupdate) : '' %></TD>
+ </TR>
+
+% if ( $conf->exists('cust_main-enable_birthdate') ) {
+% my $dt = DateTime->from_epoch(epoch => $cust_main->birthdate,
+% time_zone=>'floating',
+% );
+
+ <TR>
+ <TD ALIGN="right">Date of Birth</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->birthdate ne '' ? $dt->strftime($date_format) : '' %></TD>
+ </TR>
+
+% }
+
+</TABLE></TD></TR></TABLE>
+<%init>
+
+my( $cust_main ) = @_;
+my $conf = new FS::Conf;
+my $date_format = ($conf->config('date_format') || "%m/%d/%Y");
+
+my $sth = dbh->prepare('SELECT COUNT(*) FROM agent') or die dbh->errstr;
+$sth->execute or die $sth->errstr;
+my $num_agents = $sth->fetchrow_arrayref->[0];
+
+</%init>
diff --git a/httemplate/view/cust_main/notes.html b/httemplate/view/cust_main/notes.html
new file mode 100755
index 0000000..833c92e
--- /dev/null
+++ b/httemplate/view/cust_main/notes.html
@@ -0,0 +1,88 @@
+% if ( scalar(@notes) ) {
+
+ <% include('/elements/init_overlib.html') %>
+
+ <% include("/elements/table-grid.html") %>
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Date</TH>
+% if ( $conf->exists('cust_main_note-display_times') ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc">Time</TH>
+% }
+ <TH CLASS="grid" BGCOLOR="#cccccc">Person</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Note</TH>
+ </TR>
+
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+%
+% foreach my $note (@notes) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% my $pop = popurl(3);
+% my $notenum = $note->notenum;
+% my $onclick = include( '/elements/popup_link_onclick.html',
+% 'action' => popurl(2).
+% 'edit/cust_main_note.cgi'.
+% "?custnum=$custnum".
+% ";notenum=$notenum",
+% 'actionlabel' => 'Edit customer note',
+% 'width' => 616,
+% 'height' => 408,
+% 'frame' => 'top',
+% );
+% my $clickjs = qq!onclick="$onclick"!;
+%
+% my $edit = '';
+% if ($curuser->access_right('Edit customer note') ) {
+% $edit = qq! <A HREF="javascript:void(0);" $clickjs>(edit)</A>!;
+% }
+
+ <TR>
+ <% note_datestr($note,$conf,$bgcolor) %>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ &nbsp;<% $note->otaker%>
+ </TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ &nbsp;<%$note->comments%><% $edit %>
+ </TD>
+ </TR>
+
+% } #end display notes
+
+</TABLE>
+
+% }
+<%init>
+
+my $conf = new FS::Conf;
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my(%opt) = @_;
+
+my $custnum = $opt{'custnum'};
+
+my $cust_main = qsearchs('cust_main', {'custnum' => $custnum} );
+die "Custimer not found!" unless $cust_main;
+
+my (@notes) = $cust_main->notes();
+
+#subroutines
+
+sub note_datestr {
+ my($note, $conf, $bgcolor) = @_ or return '';
+ my $td = qq{<TD CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">};
+ my $format = "$td%b&nbsp;%o,&nbsp;%Y</TD>";
+ $format .= "$td%l:%M%P</TD>"
+ if $conf->exists('cust_main_note-display_times');
+ ( my $strip = time2str($format, $note->_date) ) =~ s/ (\d)/$1/g;
+ $strip;
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html
new file mode 100755
index 0000000..2c25888
--- /dev/null
+++ b/httemplate/view/cust_main/packages.html
@@ -0,0 +1,241 @@
+<A NAME="cust_pkg"><FONT SIZE="+2">Packages</FONT></A><BR>
+
+% if ( $curuser->access_right('One-time charge') ) {
+
+<SCRIPT TYPE="text/javascript">
+
+function taxproductmagic(which) {
+ var str = '';
+ var elements = which.form.elements;
+ for (var i = 0; i<elements.length; i++) {
+ if (elements[i].name == 'taxproductnum'){
+ document.getElementById('taxproductnum').value = elements[i].value;
+ continue;
+ }
+ if (elements[i].name == 'taxproductnum_description'){
+ continue;
+ }
+ if (str.length){str += ';';}
+ str += elements[i].name + '=' + escape(elements[i].value);
+ }
+ document.getElementById('charge_storage').value = str;
+ cClick();
+ overlib( OLiframeContent('<% $p %>/browse/part_pkg_taxproduct.cgi?_type=select&id=taxproductnum&onclick=taxproductquickchargemagic&taxproductnum='+document.getElementById('taxproductnum').value, 1000, 400, 'tax_product_popup'), CAPTION, 'Select product', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK);
+}
+
+function taxproductquickchargemagic() {
+ var str = document.getElementById('charge_storage').value;
+ if (str.length){str += ';';}
+ str += 'magic=taxproductnum;taxproductnum=';
+ str += escape(document.getElementById('taxproductnum').value);
+ cClick();
+ overlib( OLiframeContent('<% $p %>/edit/quick-charge.html?'+str, 545, 336, 'One-time charge'), CAPTION, 'One-time charge', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK, BGCOLOR, '#333399', CGCOLOR, '#333399', CLOSETEXT, 'Close');
+
+}
+
+function taxoverridemagic(which) {
+ var str = '';
+ var elements = which.ownerDocument.QuickChargeForm.elements;
+ for (var i = 0; i<elements.length; i++) {
+ if (elements[i].name == 'tax_override'){
+ document.getElementById('tax_override').value = elements[i].value;
+ continue;
+ }
+ if (str.length){str += ';';}
+ str += elements[i].name + '=' + escape(elements[i].value);
+ }
+ document.getElementById('charge_storage').value = str;
+ cClick();
+ overlib( OLiframeContent('<% $p %>/edit/part_pkg_taxoverride.html?element_name=tax_override;onclick=taxoverridequickchargemagic;selected='+document.getElementById('tax_override').value, 1100, 600, 'tax_product_popup'), CAPTION, 'Edit product tax overrides', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK);
+}
+
+function taxoverridequickchargemagic() {
+ var str = document.getElementById('charge_storage').value;
+ if (str.length){str += ';';}
+ str += 'magic=taxoverride;tax_override=';
+ str += document.getElementById('tax_override').value;
+ cClick();
+ overlib( OLiframeContent('<% $p %>/edit/quick-charge.html?'+str, 545, 336, 'One-time charge'), CAPTION, 'One-time charge', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK, BGCOLOR, '#333399', CGCOLOR, '#333399', CLOSETEXT, 'Close');
+
+}
+
+</SCRIPT>
+<FORM NAME='quickcharge'>
+ <INPUT NAME="taxproductnum" ID="taxproductnum" TYPE="hidden">
+ <INPUT NAME="tax_override" ID="tax_override" TYPE="hidden">
+ <INPUT NAME="charge_storage" ID="charge_storage" TYPE="hidden">
+ <INPUT NAME="taxproductnum_description" ID="taxproductnum_description" TYPE="hidden">
+</FORM>
+% }
+
+% my $s = 0;
+% if ( $curuser->access_right('Order customer package') ) {
+ <% $s++ ? ' | ' : '' %>
+ <% order_pkg_link($cust_main) %>
+% }
+
+% if ( $curuser->access_right('One-time charge')
+% && $conf->config('payby-default') ne 'HIDE'
+% ) {
+%
+ <% $s++ ? ' | ' : '' %>
+ <% include('/elements/popup_link.html',
+ {
+ 'action' => $p. 'edit/quick-charge.html?custnum='. $cust_main->custnum,
+ 'label' => 'One-time charge',
+ 'actionlabel' => 'One-time charge',
+ 'color' => '#333399',
+ 'width' => 763,
+ 'height' => 408,
+ })
+ %>
+% }
+
+% if ( $curuser->access_right('Bulk change customer packages') ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_pkg.cgi?<% $cust_main->custnum %>">Bulk order and cancel packages</A> (preserves services)
+% }
+
+
+<BR><BR>
+% if ( @$packages ) {
+
+Current packages
+% }
+% if ( $cust_main->num_cancelled_pkgs ) {
+% if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me
+% || ( $conf->exists('hidecancelledpackages')
+% && ! $cgi->param('showcancelledpackages')
+% )
+% )
+% {
+% $cgi->param('showcancelledpackages', 1);
+%
+
+ ( <a href="<% $cgi->self_url %>">show
+% } else {
+% $cgi->param('showcancelledpackages', 0);
+%
+
+ ( <a href="<% $cgi->self_url %>">hide
+% }
+
+ cancelled packages</a> )
+% }
+% if ( @$packages ) {
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Package</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Status</TH>
+% if ( $show_location ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc">Location</TH>
+% }
+ <TH CLASS="grid" BGCOLOR="#cccccc">Services</TH>
+</TR>
+
+% foreach my $cust_pkg (@$packages) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% my $countrydefault = scalar($conf->config('countrydefault')) || 'US';
+% my %iopt = (
+% 'bgcolor' => $bgcolor,
+% 'cust_pkg' => $cust_pkg,
+% 'part_pkg' => $cust_pkg->part_pkg,
+%
+% #for services.html and status.html
+% 'cust_pkg-display_times' => $conf->exists('cust_pkg-display_times'),
+%
+% #for location.html
+% 'countrydefault' => $countrydefault,
+% 'statedefault' => ( scalar($conf->config('statedefault'))
+% || ($countrydefault eq 'US' ? 'CA' : '') ),
+%
+% #for services.html
+% 'svc_external-skip_manual' => $conf->exists('svc_external-skip_manual'),
+% 'legacy_link' => $conf->exists('legacy_link'),
+%
+% );
+
+ <!--pkgnum: <% $cust_pkg->pkgnum %>-->
+ <TR>
+ <% include('packages/package.html', %iopt) %>
+ <% include('packages/status.html', %iopt) %>
+% if ( $show_location ) {
+ <% include('packages/location.html', %iopt) %>
+% }
+ <% include('packages/services.html', %iopt) %>
+ </TR>
+
+% }
+
+</TABLE>
+
+% } else {
+<BR>
+% }
+
+% if ( $cgi->param('fragment') =~ /^cust_pkg(\d+)$/ ) {
+ <SCRIPT>
+ // IE-specific hack. other browsers listen to #fragments
+ // is this even working? or is the #target redirection just working cause
+ // we set the URL params differently?
+ var el = document.getElementById( 'cust_pkg<% $1 %>' );
+ if ( el ) el.scrollIntoView(true);
+ </SCRIPT>
+% }
+<%init>
+
+my( $cust_main ) = @_;
+my $conf = new FS::Conf;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $packages = get_packages($cust_main, $conf);
+
+my $show_location = $conf->exists('cust_pkg-always_show_location')
+ || ( grep $_->locationnum, @$packages ); # ? '1' : '0';
+
+#subroutines
+
+sub get_packages {
+ my $cust_main = shift or return undef;
+ my $conf = shift;
+
+ my @packages = ();
+ my $method;
+ if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me
+ || ( $conf->exists('hidecancelledpackages')
+ && ! $cgi->param('showcancelledpackages') )
+ )
+ {
+ $method = 'ncancelled_pkgs';
+ } else {
+ $method = 'all_pkgs';
+ }
+
+ [ $cust_main->$method() ];
+}
+
+sub order_pkg_link {
+ include( '/elements/popup_link-cust_main.html',
+ 'action' => $p. 'misc/order_pkg.html',
+ 'label' => 'Order&nbsp;new&nbsp;package',
+ 'actionlabel' => 'Order new package',
+ 'color' => '#333399',
+ 'cust_main' => shift,
+ 'closetext' => 'Close',
+ 'width' => 763,
+ )
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/packages/location.html b/httemplate/view/cust_main/packages/location.html
new file mode 100644
index 0000000..59efce1
--- /dev/null
+++ b/httemplate/view/cust_main/packages/location.html
@@ -0,0 +1,60 @@
+<TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+
+% unless ( $cust_pkg->locationnum ) {
+ <I><FONT SIZE=-1>(default service address)</FONT><BR>
+% }
+
+ <% $loc->get($prefix.'address1') |h %><BR>
+
+% if ( $loc->get($prefix.'address2') !~ /^\s*$/ ) {
+ <% $loc->get($prefix.'address2') |h %><BR>
+% }
+
+ <% $loc->get($prefix.'city') |h %><% $loc->get($prefix.'county') ? ' ('.$loc->get($prefix.'county').' county)' : '' |h %>,
+ <% $loc->get($prefix.'state') |h %> &nbsp; <% $loc->get($prefix.'zip') |h %><BR>
+
+% if ( $loc->get($prefix.'country') ne $countrydefault ) {
+ <% code2country( $loc->get($prefix.'country') ) %>
+% }
+
+ </I>
+
+% if ( ! $cust_pkg->get('cancel')
+% && $FS::CurrentUser::CurrentUser->access_right('Change customer package')
+% )
+% {
+ <FONT SIZE=-1>
+ (&nbsp;<%pkg_change_location_link($cust_pkg)%>&nbsp;)
+ </FONT>
+% }
+
+</TD>
+<%init>
+
+my %opt = @_;
+
+my $bgcolor = $opt{'bgcolor'};
+my $cust_pkg = $opt{'cust_pkg'};
+my $part_pkg = $opt{'part_pkg'};
+my $countrydefault = $opt{'countrydefault'} || 'US';
+my $statedefault = $opt{'statedefault'}
+ || ($countrydefault eq 'US' ? 'CA' : '');
+
+my $loc = $cust_pkg->cust_location_or_main;
+my $prefix =
+ ( $loc->table eq 'cust_main' && length($loc->ship_last) ) ? 'ship_' : ''; #doh
+
+sub pkg_change_location_link {
+ my $cust_pkg = shift;
+ my $pkgpart = $cust_pkg->pkgpart;
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. "misc/change_pkg.cgi?locationnum=-1;pkgpart=$pkgpart;".
+ "address1=;address2=;city=;county=;state=$statedefault;".
+ "zip=;country=$countrydefault",
+ 'label' => 'Change&nbsp;location',
+ 'actionlabel' => 'Change',
+ 'cust_pkg' => $cust_pkg,
+ );
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html
new file mode 100644
index 0000000..b07e1af
--- /dev/null
+++ b/httemplate/view/cust_main/packages/package.html
@@ -0,0 +1,215 @@
+<TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+ <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
+ <TR>
+ <TD COLSPAN=2>
+ <A NAME="cust_pkg<% $cust_pkg->pkgnum %>"
+ ID ="cust_pkg<% $cust_pkg->pkgnum %>"
+ ><% $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : '' %><B><% $part_pkg->pkg |h %></B></A>
+ -
+ <% $part_pkg->comment |h %>
+ </TD>
+ </TR>
+
+% if ( $cust_pkg->quantity > 1 ) {
+ <TR>
+ <TD COLSPAN=2>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quantity:
+ <B><% $cust_pkg->quantity %></B>
+ </TD>
+ </TR>
+% }
+
+ <TR>
+ <TD COLSPAN=2>
+ <FONT SIZE=-1>
+
+% unless ( $cust_pkg->get('cancel') ) {
+%
+% my $br = 0;
+% if ( $curuser->access_right('Change customer package') ) {
+% $br=1;
+ (&nbsp;<%pkg_change_link($cust_pkg)%>&nbsp;)
+% }
+%
+% if ( $curuser->access_right('Edit customer package dates') ) {
+% $br=1;
+ (&nbsp;<%pkg_dates_link($cust_pkg)%>&nbsp;)
+% }
+%
+% if ( $curuser->access_right('Customize customer package') ) {
+% $br=1;
+ (&nbsp;<%pkg_customize_link($cust_pkg,$cust_pkg->custnum)%>&nbsp;)
+% }
+%
+ <% $br ? '<BR>' : '' %>
+% }
+
+% if ( $cust_pkg->num_cust_event
+% && ( $curuser->access_right('Billing event reports')
+% || $curuser->access_right('View customer billing events')
+% )
+% ) {
+ (&nbsp;<%pkg_event_link($cust_pkg)%>&nbsp;)
+% }
+
+ </FONT>
+ </TD>
+ </TR>
+
+% my $editi = $curuser->access_right('Edit customer package invoice details');
+% my $editc = $curuser->access_right('Edit customer package comments');
+%
+% if ( $cust_pkg->cust_pkg_detail('I')
+% || $cust_pkg->cust_pkg_detail('C')
+% || $editi
+% || $editc ) {
+%
+% my $editlink = $p. 'edit/cust_pkg_detail?pkgnum='. $cust_pkg->pkgnum.
+% ';detailtype=';
+
+ <TR>
+
+% if ( $cust_pkg->cust_pkg_detail('I') ) {
+ <TD VALIGN="top">
+ <% include('/elements/table-grid.html') %>
+ <TR>
+ <TH BGCOLOR="#dddddd" STYLE="border-bottom: dashed 1px black; padding-bottom: 1px">
+ <FONT SIZE="-1">
+ Invoice details
+% if ( $editi && ! $cust_pkg->get('cancel') ) {
+ (<% include('/elements/popup_link.html', {
+ 'action' => $editlink. 'I',
+ 'label' => 'edit',
+ 'actionlabel' => 'Edit invoice details',
+ 'color' => '#333399',
+ 'width' => 763,
+ })
+ %>)
+% }
+ </FONT>
+ </TH>
+ </TR>
+% foreach my $cust_pkg_detail ( $cust_pkg->cust_pkg_detail('I') ) {
+ <TR>
+ <TD><FONT SIZE="-1">&nbsp;-&nbsp;<% $cust_pkg_detail->detail |h %></FONT></TD>
+ </TR>
+% }
+ </TABLE>
+ </TD>
+% } else {
+ <TD>
+% if ( $editi && ! $cust_pkg->get('cancel') ) {
+ <FONT SIZE="-1">
+ (&nbsp;<% include('/elements/popup_link.html', {
+ 'action' => $editlink. 'I',
+ 'label' => 'Add&nbsp;invoice&nbsp;details',
+ 'actionlabel' => 'Add invoice details',
+ 'color' => '#333399',
+ 'width' => 763,
+ })
+ %>&nbsp;)
+ </FONT>
+% }
+ </TD>
+% }
+
+% if ( $cust_pkg->cust_pkg_detail('C') ) {
+ <TD VALIGN="top">
+ <% include('/elements/table-grid.html') %>
+ <TR>
+ <TH BGCOLOR="#dddddd" STYLE="border-bottom: dashed 1px black; padding-bottom: 1px">
+ <FONT SIZE="-1">
+ Comments
+% if ( $editc ) {
+ (<% include('/elements/popup_link.html', {
+ 'action' => $editlink. 'C',
+ 'label' => 'edit',
+ 'actionlabel' => 'Edit comments',
+ 'color' => '#333399',
+ 'width' => 763,
+ })
+ %>)
+% }
+ </FONT>
+ </TH>
+ </TR>
+% foreach my $cust_pkg_detail ( $cust_pkg->cust_pkg_detail('C') ) {
+ <TR>
+ <TD><FONT SIZE="-1">&nbsp;-&nbsp;<% $cust_pkg_detail->detail |h %></FONT></TD>
+ </TR>
+% }
+ </TABLE>
+ </TD>
+% } else {
+ <TD>
+% if ( $editc ) {
+ <FONT SIZE="-1">
+ (&nbsp;<% include('/elements/popup_link.html', {
+ 'action' => $editlink. 'C',
+ 'label' => 'Add&nbsp;comments',
+ 'actionlabel' => 'Add comments',
+ 'color' => '#333399',
+ 'width' => 763,
+ })
+ %>&nbsp;)
+ </FONT>
+% }
+ </TD>
+% }
+
+ </TR>
+% }
+
+ </TABLE>
+
+</TD>
+
+<%init>
+
+my %opt = @_;
+
+my $bgcolor = $opt{'bgcolor'};
+my $cust_pkg = $opt{'cust_pkg'};
+my $part_pkg = $opt{'part_pkg'};
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+#subroutines
+
+#false laziness w/status.html
+sub pkg_link {
+ my($action, $label, $cust_pkg) = @_;
+ return '' unless $cust_pkg;
+ qq!<a href="$p$action.cgi?!. $cust_pkg->pkgnum. qq!">$label</a>!;
+}
+
+sub pkg_change_link {
+ my $cust_pkg = shift;
+ my $locationnum = $cust_pkg->locationnum;
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. "misc/change_pkg.cgi?locationnum=$locationnum",
+ 'label' => 'Change&nbsp;package',
+ 'actionlabel' => 'Change',
+ 'cust_pkg' => $cust_pkg,
+ );
+}
+
+sub pkg_dates_link { pkg_link('edit/REAL_cust_pkg', 'Edit&nbsp;dates', @_ ); }
+
+sub pkg_customize_link {
+ my $cust_pkg = shift or return '';
+ my $custnum = $cust_pkg->custnum;
+ qq!<A HREF="${p}edit/part_pkg.cgi?!.
+ "clone=". $cust_pkg->part_pkg->pkgpart. ';'.
+ "pkgnum=". $cust_pkg->pkgnum.
+ qq!">Customize</A>!;
+}
+
+sub pkg_event_link {
+ my($cust_pkg) = @_;
+ qq!<a href="${p}search/cust_event.html?pkgnum=!. $cust_pkg->pkgnum. qq!">!.
+ 'View package events'.
+ '</a>';
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html
new file mode 100644
index 0000000..1e47373
--- /dev/null
+++ b/httemplate/view/cust_main/packages/services.html
@@ -0,0 +1,119 @@
+% ###
+% # Services
+% ###
+
+ <TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+ <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
+
+% #foreach my $svcpart (sort {$a->{svcpart} <=> $b->{svcpart}} @{$pkg->{svcparts}}) {
+% foreach my $part_svc ( $cust_pkg->part_svc ) {
+
+% #foreach my $service (@{$svcpart->{services}}) {
+% foreach my $cust_svc ( @{ $part_svc->cust_pkg_svc } ) {
+
+ <TR>
+ <TD ALIGN="right" VALIGN="top"><% FS::UI::Web::svc_link($m, $part_svc, $cust_svc) %></TD>
+ <TD STYLE="padding-bottom:0px"><B><% FS::UI::Web::svc_label_link($m, $part_svc, $cust_svc) %></B></TD>
+ <TD><% FS::UI::Web::svc_export_links($m, $part_svc, $cust_svc) %></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right" COLSPAN="3" VALIGN="top" STYLE="padding-bottom:1px;padding-top:0px"><FONT SIZE="-2" COLOR="#FFD000">
+
+ <% $cust_svc->overlimit ? "Overlimit: ". time2str('%b %o %Y' . ($opt{'cust_pkg-display_times'} ? ' %l:%M %P' : ''), $cust_svc->overlimit) : '' %>
+ </FONT></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right" VALIGN="top" STYLE="padding-bottom:5px;padding-top:0px"><FONT SIZE="-2">
+
+% if ( $curuser->access_right('Recharge customer service')
+% && $part_svc->svcdb eq 'svc_acct'
+% && ( $cust_svc->svc_x->seconds ne ''
+% || $cust_svc->svc_x->upbytes ne ''
+% || $cust_svc->svc_x->downbytes ne ''
+% || $cust_svc->svc_x->totalbytes ne ''
+% )
+% ) {
+ (&nbsp;<%svc_recharge_link($cust_svc)%>&nbsp;)
+% }
+ </FONT></TD>
+
+ <TD ALIGN="right" VALIGN="top" STYLE="padding-bottom:5px;padding-top:0px"><FONT SIZE="-2">
+
+% if ( $curuser->access_right('Unprovision customer service') ) {
+ (&nbsp;<%svc_unprovision_link($cust_svc)%>&nbsp;)
+% }
+ </FONT></TD>
+ </TR>
+% }
+
+% if ( ! $cust_pkg->get('cancel')
+% && $curuser->access_right('Provision customer service')
+% && $part_svc->num_avail
+% ) {
+
+ <TR>
+ <TD COLSPAN=3 ALIGN="center" STYLE="padding-bottom:4px;padding-top:0px">
+ <B><% svc_provision_link($cust_pkg, $part_svc, \%opt, $curuser) %></B>
+ </TD>
+ </TR>
+
+% }
+
+% }
+
+ </TABLE>
+ </TD>
+
+<%init>
+
+my %opt = @_;
+
+my $bgcolor = $opt{'bgcolor'};
+my $cust_pkg = $opt{'cust_pkg'};
+my $part_pkg = $opt{'part_pkg'};
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+sub svc_provision_link {
+ my ($cust_pkg, $part_svc, $opt, $curuser) = @_;
+ ( my $svc_nbsp = $part_svc->svc ) =~ s/\s+/&nbsp;/g;
+ my $num_avail = $part_svc->num_avail;
+ my $pkgnum_svcpart = "pkgnum=". $cust_pkg->pkgnum. ';'.
+ "svcpart=". $part_svc->svcpart;
+ my $url;
+ if ( $part_svc->svcdb eq 'svc_external' #could be generalized
+ && $opt->{'svc_external-skip_manual'}
+ ) {
+ $url = "${p}edit/process/". $part_svc->svcdb. ".cgi?$pkgnum_svcpart";
+ } else {
+ $url = svc_url(
+ 'm' => $m,
+ 'action' => 'edit',
+ 'part_svc' => $part_svc,
+ 'query' => $pkgnum_svcpart,
+ );
+ #$url = "${p}edit/$svcpart->{svcdb}.cgi?$pkgnum_svcpart";
+ }
+
+ my $link = qq!<A CLASS="provision" HREF="$url">!.
+ "Provision&nbsp;$svc_nbsp&nbsp;($num_avail)</A>";
+ if ( $opt->{'legacy_link'}
+ && $curuser->access_right('View/link unlinked services')
+ )
+ {
+ $link .= '<BR>'.
+ qq!<A CLASS="provision" HREF="${p}misc/link.cgi?!.
+ qq!$pkgnum_svcpart">!.
+ "Link&nbsp;to&nbsp;legacy&nbsp;$svc_nbsp&nbsp;($num_avail)</A>";
+ }
+ $link;
+}
+
+sub svc_unprovision_link {
+ my $cust_svc = shift or return '';
+ qq!<A HREF="javascript:areyousure('${p}misc/unprovision.cgi?!. $cust_svc->svcnum.
+ qq!', 'Permanently unprovision and delete this service?')">Unprovision</A>!;
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
new file mode 100644
index 0000000..106137b
--- /dev/null
+++ b/httemplate/view/cust_main/packages/status.html
@@ -0,0 +1,379 @@
+<TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+ <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
+
+%#this should use cust_pkg->status and cust_pkg->statuscolor eventually
+
+% if ( $cust_pkg->get('cancel') ) { #status: cancelled
+% my $cpr = $cust_pkg->last_cust_pkg_reason('cancel');
+
+ <% pkg_status_row($cust_pkg, 'Cancelled', 'cancel', 'color'=>'FF0000', %opt ) %>
+
+ <% pkg_status_row_colspan(
+ ( $cpr ? $cpr->reasontext. ' by '. $cpr->otaker : '' ), '',
+ 'align' => 'right', 'color' => 'ff0000', 'size' => '-2',
+ )
+ %>
+
+% unless ( $cust_pkg->get('setup') ) {
+
+ <% pkg_status_row_colspan('Never billed') %>
+
+% } else {
+
+ <% pkg_status_row( $cust_pkg, 'Setup', 'setup', %opt ) %>
+ <% pkg_status_row_changed( $cust_pkg, %opt ) %>
+ <% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
+ <% pkg_status_row_if( $cust_pkg, 'Suspended', 'susp', %opt, curuser=>$curuser ) %>
+
+% }
+%
+% } else {
+%
+% if ( $cust_pkg->get('susp') ) { #status: suspended
+% my $cpr = $cust_pkg->last_cust_pkg_reason('susp');
+
+ <% pkg_status_row( $cust_pkg, 'Suspended', 'susp', 'color'=>'FF9900', %opt ) %>
+
+ <% pkg_status_row_colspan(
+ ( $cpr ? $cpr->reasontext. ' by '. $cpr->otaker : '' ), '',
+ 'align' => 'right', 'color' => 'FF9900', 'size' => '-2',
+ )
+ %>
+
+% unless ( $cust_pkg->get('setup') ) {
+ <% pkg_status_row_colspan('Never billed') %>
+% } else {
+ <% pkg_status_row($cust_pkg, 'Setup', 'setup', %opt ) %>
+% }
+
+ <% pkg_status_row_changed( $cust_pkg, %opt ) %>
+ <% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
+% # pkg_status_row($cust_pkg, 'Next bill', 'bill', %opt)
+ <% pkg_status_row_if( $cust_pkg, 'Expires', 'expire', %opt, curuser=>$curuser ) %>
+
+ <TR>
+ <TD COLSPAN=<%$colspan%>>
+ <FONT SIZE=-1>
+% if ( $curuser->access_right('Unsuspend customer package') ) {
+ (&nbsp;<% pkg_unsuspend_link($cust_pkg) %>&nbsp;)
+% }
+% if ( $curuser->access_right('Cancel customer package immediately') ) {
+ (&nbsp;<% pkg_cancel_link($cust_pkg) %>&nbsp;)
+% }
+ </FONT>
+ </TD>
+ </TR>
+
+% } else { #status: active
+%
+% unless ( $cust_pkg->get('setup') ) { #not setup
+%
+% unless ( $part_pkg->freq ) {
+
+ <% pkg_status_row_colspan('Not&nbsp;yet&nbsp;billed&nbsp;(one-time&nbsp;charge)') %>
+
+ <TR>
+ <TD COLSPAN=<%$colspan%>>
+ <FONT SIZE=-1>
+% if ( $curuser->access_right('Cancel customer package immediately') ) {
+ (&nbsp;<% pkg_cancel_link($cust_pkg) %>&nbsp;)
+% }
+ </FONT>
+ </TD>
+ </TR>
+
+% } else {
+
+ <% pkg_status_row_colspan("Not&nbsp;yet&nbsp;billed&nbsp;($billed_or_prepaid&nbsp;". myfreq($part_pkg). ')' ) %>
+
+% }
+%
+% } else { #setup
+%
+% unless ( $part_pkg->freq ) {
+
+ <% pkg_status_row_colspan('One-time&nbsp;charge') %>
+
+ <% pkg_status_row($cust_pkg, 'Billed', 'setup', %opt) %>
+
+% } else {
+%
+% if (scalar($cust_pkg->overlimit)) {
+
+ <% pkg_status_row_colspan(
+ 'Overlimit',
+ $billed_or_prepaid. '&nbsp;'. myfreq($part_pkg),
+ 'color' => 'FFD000',
+ )
+ %>
+
+% } else {
+ <% pkg_status_row_colspan(
+ 'Active',
+ $billed_or_prepaid. '&nbsp;'. myfreq($part_pkg),
+ 'color' => '00CC00',
+ )
+ %>
+% }
+
+ <% pkg_status_row($cust_pkg, 'Setup', 'setup', %opt) %>
+
+% }
+%
+% }
+%
+% if ( $conf->exists('cust_pkg-show_autosuspend') ) {
+% my $autosuspend = pkg_autosuspend_time( $cust_pkg );
+% $cust_pkg->set('autosuspend', $autosuspend) if $autosuspend;
+% }
+
+ <% pkg_status_row_changed( $cust_pkg, %opt ) %>
+ <% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
+ <% pkg_status_row_if( $cust_pkg, $next_bill_or_prepaid_until, 'bill', %opt, curuser=>$curuser ) %>
+ <% pkg_status_row_if($cust_pkg, 'Will automatically suspend by', 'autosuspend', %opt) %>
+ <% pkg_status_row_if( $cust_pkg, 'Will suspend on', 'adjourn', %opt, curuser=>$curuser ) %>
+ <% pkg_status_row_if( $cust_pkg, 'Expires', 'expire', %opt, curuser=>$curuser ) %>
+
+% if ( $part_pkg->freq ) {
+
+ <TR>
+ <TD COLSPAN=<%$colspan%>>
+ <FONT SIZE=-1>
+% if ( $curuser->access_right('Suspend customer package') ) {
+ (&nbsp;<% pkg_suspend_link($cust_pkg) %>&nbsp;)
+% }
+% if ( $curuser->access_right('Suspend customer package later') ) {
+ (&nbsp;<% pkg_adjourn_link($cust_pkg) %>&nbsp;)
+% }
+% if ( $curuser->access_right('Delay suspension events') ) {
+ (&nbsp;<% pkg_delay_link($cust_pkg) %>&nbsp;)
+% }
+% if ( $curuser->access_right('Cancel customer package immediately') ) {
+ (&nbsp;<% pkg_cancel_link($cust_pkg) %>&nbsp;)
+% }
+% if ( $curuser->access_right('Cancel customer package later') ) {
+ (&nbsp;<% pkg_expire_link($cust_pkg) %>&nbsp;)
+% }
+
+ <FONT>
+ </TD>
+ </TR>
+% }
+%
+% }
+% }
+
+ </TABLE>
+</TD>
+
+<%init>
+
+my %opt = @_;
+
+my $conf = new FS::Conf;
+
+my $bgcolor = $opt{'bgcolor'};
+my $cust_pkg = $opt{'cust_pkg'};
+my $part_pkg = $opt{'part_pkg'};
+my $curuser = $FS::CurrentUser::CurrentUser;
+my $colspan = $opt{'cust_pkg-display_times'} ? 8 : 4;
+my $width = $opt{'cust_pkg-display_times'} ? '38%' : '56%';
+
+#false laziness w/edit/REAL_cust_pkg.cgi
+my( $billed_or_prepaid, $last_bill_or_renewed, $next_bill_or_prepaid_until );
+unless ( $part_pkg->is_prepaid ) {
+ $billed_or_prepaid = 'billed';
+ $last_bill_or_renewed = 'Last&nbsp;bill';
+ $next_bill_or_prepaid_until = 'Next&nbsp;bill';
+} else {
+ $billed_or_prepaid = 'prepaid';
+ $last_bill_or_renewed = 'Renewed';
+ $next_bill_or_prepaid_until = 'Prepaid&nbsp;until';
+}
+
+#subroutines
+
+sub myfreq {
+ my $part_pkg = shift;
+ my $freq = $part_pkg->freq_pretty;
+ $freq =~ s/ /&nbsp;/g;
+ $freq;
+}
+
+#false laziness w/package.html
+sub pkg_link {
+ my($action, $label, $cust_pkg) = @_;
+ return '' unless $cust_pkg;
+ qq!<a href="$p$action.cgi?!. $cust_pkg->pkgnum. qq!">$label</a>!;
+}
+
+sub pkg_status_row {
+ my( $cust_pkg, $title, $field, %opt ) = @_;
+
+ my $color = $opt{'color'};
+
+ my $html = qq(<TR><TD WIDTH="<%$width%>" ALIGN="right">);
+ $html .= qq(<FONT COLOR="#$color"><B>) if length($color);
+ $html .= qq($title&nbsp;);
+ $html .= qq(</B></FONT>) if length($color);
+ $html .= qq(</TD>);
+ $html .= pkg_datestr($cust_pkg, $field, %opt).'</TR>';
+
+ $html;
+}
+
+sub pkg_status_row_if {
+ my( $cust_pkg, $title, $field, %opt ) = @_;
+
+ $title = '<FONT SIZE=-1>(&nbsp;'. pkg_unadjourn_link($cust_pkg). '&nbsp;)&nbsp;</FONT>'. $title
+ if ( $field eq 'adjourn' &&
+ $opt{curuser}->access_right('Suspend customer package later')
+ );
+
+ $title = '<FONT SIZE=-1>(&nbsp;'. pkg_unexpire_link($cust_pkg). '&nbsp;)&nbsp;</FONT>'. $title
+ if ( $field eq 'expire' &&
+ $opt{curuser}->access_right('Cancel customer package later')
+ );
+
+ $cust_pkg->get($field) ? pkg_status_row($cust_pkg, $title, $field, %opt) : '';
+}
+
+sub pkg_status_row_changed {
+ my( $cust_pkg, %opt ) = @_;
+ return '' unless $cust_pkg->change_date;
+ my $html = pkg_status_row( $cust_pkg, 'Package&nbsp;changed', 'change_date', %opt );
+ my $old = $cust_pkg->old_cust_pkg;
+ if ( $old ) {
+ my $part_pkg = $old->part_pkg;
+ my $label = 'Changed from '. $cust_pkg->change_pkgnum. ': '.
+ $part_pkg->pkg. ' - '. $part_pkg->comment;
+ $html .= pkg_status_row_colspan( $label, '', size=>'-1', align=>'right' );
+ }
+ $html;
+}
+
+sub pkg_status_row_colspan {
+ my($title, $addl, %opt) = @_;
+
+ my $align = $opt{'align'} ? 'ALIGN="'. $opt{'align'}.'"' : '';
+ my $color = $opt{'color'} ? 'COLOR="#'.$opt{'color'}.'"' : '';
+ my $size = $opt{'size'} ? 'SIZE="'. $opt{'size'}. '"' : '';
+
+ my $html = qq(<TR><TD COLSPAN=$colspan $align>);
+ $html .= qq(<FONT $color $size>) if length($color) || $size;
+ $html .= qq(<B>) if $color && !$size;
+ $html .= $title;
+ $html .= qq(</B>) if $color && !$size;
+ $html .= qq(</FONT>) if length($color) || $size;
+ $html .= ",&nbsp;$addl" if length($addl);
+ $html .= qq(</TD></TR>);
+
+ $html;
+
+}
+
+sub pkg_datestr {
+ my($cust_pkg, $field, %opt) = @_ or return '';
+ return '&nbsp;' unless $cust_pkg->get($field);
+ my $format = '<TD align="left"><B>%b</B></TD>'.
+ '<TD align="right"><B>&nbsp;%o,</B></TD>'.
+ '<TD align="right"><B>&nbsp;%Y</B></TD>';
+ #$format .= '&nbsp;<FONT SIZE=-3>%l:%M:%S%P&nbsp;%z</FONT>'
+ $format .= '<TD ALIGN="right"><B>&nbsp;%l</TD>'.
+ '<TD ALIGN="center"><B>:</B></TD>'.
+ '<TD ALIGN="left"><B>%M</B></TD>'.
+ '<TD ALIGN="left"><B>&nbsp;%P</B></TD>'
+ if $opt{'cust_pkg-display_times'};
+ my $strip = time2str($format, $cust_pkg->get($field) );
+ $strip =~ s/ (\d)/$1/g;
+ $strip;
+}
+
+sub pkg_suspend_link {
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. 'misc/cancel_pkg.html?method=suspend',
+ 'label' => 'Suspend&nbsp;now',
+ 'actionlabel' => 'Suspend',
+ 'color' => '#FF9900',
+ 'cust_pkg' => shift,
+ )
+}
+
+sub pkg_adjourn_link {
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. 'misc/cancel_pkg.html?method=adjourn',
+ 'label' => 'Suspend&nbsp;later',
+ 'actionlabel' => 'Adjourn',
+ 'color' => '#CC6600',
+ 'cust_pkg' => shift,
+ )
+}
+
+sub pkg_delay_link {
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. 'misc/delay_susp_pkg.html',
+ 'label' => 'Delay&nbsp;suspend',
+ 'actionlabel' => 'Delay suspend for',
+ 'cust_pkg' => shift,
+ )
+}
+
+sub pkg_unsuspend_link { pkg_link('misc/unsusp_pkg', 'Unsuspend', @_ ); }
+sub pkg_unadjourn_link { pkg_link('misc/unadjourn_pkg', 'Abort', @_ ); }
+sub pkg_unexpire_link { pkg_link('misc/unexpire_pkg', 'Abort', @_ ); }
+
+sub pkg_cancel_link {
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. 'misc/cancel_pkg.html?method=cancel',
+ 'label' => 'Cancel&nbsp;now',
+ 'actionlabel' => 'Cancel',
+ 'color' => '#ff0000',
+ 'cust_pkg' => shift,
+ )
+}
+
+sub pkg_expire_link {
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. 'misc/cancel_pkg.html?method=expire',
+ 'label' => 'Cancel&nbsp;later',
+ 'actionlabel' => 'Expire', #"Cancel package $num later"
+ 'color' => '#CC0000',
+ 'cust_pkg' => shift,
+ )
+}
+
+sub svc_recharge_link {
+ include( '/elements/popup_link-cust_svc.html',
+ 'action' => $p. 'misc/recharge_svc.html',
+ 'label' => 'Recharge',
+ 'actionlabel' => 'Recharge',
+ 'color' => '#333399',
+ 'cust_svc' => shift,
+ )
+}
+
+sub pkg_autosuspend_time {
+ my $cust_pkg = shift or return '';
+ my $days = 7;
+ my $time = time;
+ my $pending_suspend = 0;
+ #this seems to be extremely inefficient... and is slowing down all customer
+ #views
+ while ( $days > 0 &&
+ scalar(
+ grep { $_->part_event->action eq 'suspend' }
+ @{$cust_pkg->cust_main->due_cust_event( time => $time + 86400*$days,
+ testonly => 1,
+ ) }
+ )
+ )
+ {
+ $pending_suspend = 1;
+ $days--;
+ }
+
+ $pending_suspend ? time + ($days + 1) * 86400 : '';
+
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
new file mode 100644
index 0000000..335ce24
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history.html
@@ -0,0 +1,413 @@
+<BR><BR><A NAME="history"><FONT SIZE="+2">Payment History</FONT></A><BR>
+
+%# payment links
+
+% my $s = 0;
+% if ( $payby{'BILL'} && $curuser->access_right('Post payment') ) {
+ <% $s++ ? ' | ' : '' %>
+ <% include('/elements/popup_link-cust_main.html',
+ 'label' => 'Enter check payment',
+ 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=BILL",
+ 'cust_main' => $cust_main,
+ 'actionlabel' => 'Enter check payment',
+ 'width' => 392,
+ #default# 'height' => 336,
+ )
+ %>
+% }
+
+% if ( $payby{'CASH'} && $curuser->access_right('Post payment') ) {
+ <% $s++ ? ' | ' : '' %>
+ <% include('/elements/popup_link-cust_main.html',
+ 'label' => 'Enter cash payment',
+ 'action' => "${p}edit/cust_pay.cgi?popup=1;payby=CASH",
+ 'cust_main' => $cust_main,
+ 'actionlabel' => 'Enter cash payment',
+ 'width' => 392,
+ #default# 'height' => 336,
+ )
+ %>
+% }
+
+% if ( $payby{'WEST'} && $curuser->access_right('Post payment') ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_pay.cgi?payby=WEST;custnum=<% $custnum %>">Enter Western Union payment</A>
+% }
+
+% if ( ( $payby{'CARD'} || $payby{'DCRD'} )
+% && $curuser->access_right('Process payment')
+% && ! $cust_main->is_encrypted($cust_main->payinfo)
+% ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>misc/payment.cgi?payby=CARD;custnum=<% $custnum %>">Process credit card payment</A>
+% }
+
+% if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
+% && $curuser->access_right('Process payment')
+% && ! $cust_main->is_encrypted($cust_main->payinfo)
+% ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>misc/payment.cgi?payby=CHEK;custnum=<% $custnum %>">Process electronic check (ACH) payment</A>
+% }
+
+% if ( $payby{'MCRD'} && $curuser->access_right('Post payment') ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_pay.cgi?payby=MCRD;custnum=<% $custnum %>">Post manual (offline/POS) credit card payment</A>
+% }
+
+<BR>
+
+%# credit link
+
+% if ( $curuser->access_right('Post credit') ) {
+ <% include('/elements/popup_link-cust_main.html',
+ 'label' => 'Enter credit',
+ 'action' => "${p}edit/cust_credit.cgi",
+ 'cust_main' => $cust_main,
+ 'actionlabel' => 'Enter credit',
+ 'width' => 392,
+ #default# 'height' => 336,
+ )
+ %>
+ <BR>
+% }
+
+%# refund links
+
+% $s = 0;
+% if ( $payby{'BILL'} && $curuser->access_right('Post refund') ) {
+ <% $s++ ? ' | ' : '' %>
+ <% include('/elements/popup_link-cust_main.html',
+ 'label' => 'Enter check refund',
+ 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=BILL",
+ 'cust_main' => $cust_main,
+ 'actionlabel' => 'Enter check refund',
+ 'width' => 392,
+ #default# 'height' => 336,
+ )
+ %>
+% }
+
+% if ( $payby{'CASH'} && $curuser->access_right('Post refund') ) {
+ <% $s++ ? ' | ' : '' %>
+ <% include('/elements/popup_link-cust_main.html',
+ 'label' => 'Enter cash refund',
+ 'action' => "${p}edit/cust_refund.cgi?popup=1;payby=CASH",
+ 'cust_main' => $cust_main,
+ 'actionlabel' => 'Enter cash refund',
+ 'width' => 392,
+ #default# 'height' => 336,
+ )
+ %>
+% }
+
+%# someday, perhaps. very few gateways let you do unlinked refunds at all.
+%# Authorize.net makes you sign a special form
+%#
+%# % if ( ( $payby{'CARD'} || $payby{'DCRD'} )
+%# % && $curuser->access_right('Process refund')
+%# % && ! $cust_main->is_encrypted($cust_main->payinfo)
+%# % ) {
+%# <% $s++ ? ' | ' : '' %>
+%# <A HREF="<% $p %>misc/refund.cgi?payby=CARD;custnum=<% $custnum %>">Process credit card refund</A>
+%# % }
+%#
+%# % if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
+%# % && $curuser->access_right('Process refund')
+%# % && ! $cust_main->is_encrypted($cust_main->payinfo)
+%# % ) {
+%# <% $s++ ? ' | ' : '' %>
+%# <A HREF="<% $p %>misc/refund.cgi?payby=CHEK;custnum=<% $custnum %>">Process electronic check (ACH) refund</A>
+%# % }
+
+% if ( $payby{'MCRD'} && $curuser->access_right('Post refund') ) {
+ <% $s++ ? ' | ' : '' %>
+ <A HREF="<% $p %>edit/cust_refund.cgi?payby=MCRD;custnum=<% $custnum %>">Post manual (offline/POS) credit card refund</A>
+% }
+
+<BR>
+
+%# tax exemption link
+
+% if ( $curuser->access_right('View customer tax exemptions') ) {
+ <A HREF="<% $p %>search/cust_tax_exempt_pkg.cgi?custnum=<% $custnum %>">View tax exemptions</A>
+ <BR>
+% }
+
+%# batched payment links
+
+% if ( ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
+% && $curuser->access_right('View customer batched payments')
+% )
+% {
+ View batched payments:
+% foreach my $status (qw( Queued In-transit Complete All )) {
+ <A HREF="<% $p %>search/cust_pay_batch.cgi?status=<% $status{$status} %>;custnum=<% $custnum %>"><% $status %></A>
+ <% $status ne 'All' ? '|' : '' %>
+% }
+ <BR>
+% }
+
+%# pending payment links
+
+% if ( $curuser->access_right('View customer pending payments')
+% && scalar($cust_main->cust_pay_pending)
+% )
+% {
+ <A HREF="<% $p %>search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=<% $custnum %>">View pending payments</A><BR>
+% }
+
+%# and now the table
+
+<% include("/elements/table-grid.html") %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Date</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Description</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Invoice</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Payment</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>In-house<BR>Credit</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Refund</FONT></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>Balance</FONT></TH>
+</TR>
+
+%#display payment history
+
+%my $money_char = $conf->config('money_char') || '$';
+%
+%sub balance_forward_row {
+% my( $b, $date, $money_char ) = @_;
+% ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/-&nbsp;\$/;
+
+ <TR ID="balance_forward_row">
+ <TD CLASS="grid" BGCOLOR="#dddddd">
+ <% time2str("%D",$date) %>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="#dddddd">
+ <I>Starting balance on <% time2str("%D",$date) %></I>
+ (<A HREF="javascript:void(0);" onClick="show_history();">show prior history</A>)
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
+ <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
+ <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
+ <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
+ <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD>
+
+ </TR>
+%}
+%
+%my $balance = 0;
+%my %target = ();
+%
+%my $years = $conf->config('payment_history-years') || 2;
+%my $older_than = time - $years * 31556736; #60*60*24*365.24
+%my $hidden = 0;
+%my $seen = 0;
+%my $old_history = 0;
+%my $lastdate = 0;
+%
+%foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) {
+%
+% $lastdate = $item->{'date'};
+%
+% my $display;
+% if ( $item->{'date'} < $older_than ) {
+% $display = ' STYLE="display:none" ';
+% $hidden = 1;
+% } else {
+%
+% $display = '';
+%
+% if ( $hidden && ! $seen++ ) {
+% balance_forward_row($balance, $item->{'date'}, $money_char);
+% }
+%
+% }
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+%
+% my $charge = exists($item->{'charge'})
+% ? sprintf("$money_char\%.2f", $item->{'charge'})
+% : '';
+%
+% my $payment = exists($item->{'payment'})
+% ? sprintf("-&nbsp;$money_char\%.2f", $item->{'payment'})
+% : '';
+%
+% $payment ||= sprintf( "<DEL>-&nbsp;$money_char\%.2f</DEL>",
+% $item->{'void_payment'}
+% )
+% if exists($item->{'void_payment'});
+%
+% my $credit = exists($item->{'credit'})
+% ? sprintf("-&nbsp;$money_char\%.2f", $item->{'credit'})
+% : '';
+%
+% my $refund = exists($item->{'refund'})
+% ? sprintf("$money_char\%.2f", $item->{'refund'})
+% : '';
+%
+% my $target = exists($item->{'target'}) ? $item->{'target'} : '';
+%
+% $balance += $item->{'charge'} if exists $item->{'charge'};
+% $balance -= $item->{'payment'} if exists $item->{'payment'};
+% $balance -= $item->{'credit'} if exists $item->{'credit'};
+% $balance += $item->{'refund'} if exists $item->{'refund'};
+% $balance = sprintf("%.2f", $balance);
+% $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
+% ( my $showbalance = $money_char. $balance ) =~ s/^\$\-/-&nbsp;\$/;
+%
+%
+
+
+ <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% unless ( !$target || $target{$target}++ ) {
+
+ <A NAME="<% $target %>">
+% }
+
+ <% time2str("%D",$item->{'date'}) %>
+% if ( $target && $target{$target} == 1 ) {
+
+ </A>
+% }
+
+ </FONT>
+ </TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $item->{'desc'} %>
+ </TD>
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $charge %>
+ </TD>
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $payment %>
+ </TD>
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $credit %>
+ </TD>
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $refund %>
+ </TD>
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $showbalance %>
+ </TD>
+ </TR>
+% }
+
+%if ( scalar(@history) && $hidden && ! $seen++ ) {
+% balance_forward_row($balance, $lastdate, $money_char);
+%}
+
+</TABLE>
+
+<SCRIPT TYPE="text/javascript">
+
+function show_history () {
+ //alert('showing history!');
+
+ var balance_forward_row = document.getElementById('balance_forward_row');
+
+ balance_forward_row.style.display = 'none';
+ for ( var i = 0; i < <% $old_history %>; i++ ) {
+ var oldRow = document.getElementById('old_history'+i);
+ oldRow.style.display = '';
+ }
+
+}
+
+</SCRIPT>
+
+<%init>
+
+my( $cust_main ) = @_;
+my $custnum = $cust_main->custnum;
+
+my $conf = new FS::Conf;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my @payby = grep /\w/, $conf->config('payby');
+#@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
+@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
+ unless @payby;
+my %payby = map { $_=>1 } @payby;
+
+my %status = (
+ 'Queued' => 'O', #Open
+ 'In-transit' => 'I',
+ 'Complete' => 'R', #Resolved
+ 'All' => '',
+);
+
+#get payment history
+my @history = ();
+
+my %opt =
+ ( map { $_ => scalar($conf->config($_)) }
+ qw( card_refund-days )
+ ),
+ ( map { $_ => $conf->exists($_) }
+ qw( deletepayments deleterefunds )
+ );
+
+#invoices
+foreach my $cust_bill ($cust_main->cust_bill) {
+ push @history, {
+ 'date' => $cust_bill->_date,
+ 'desc' => include('payment_history/invoice.html', $cust_bill, %opt ),
+ 'charge' => $cust_bill->charged,
+ };
+}
+
+#payments (some false laziness w/credits)
+foreach my $cust_pay ($cust_main->cust_pay) {
+ push @history, {
+ 'date' => $cust_pay->_date,
+ 'desc' => include('payment_history/payment.html', $cust_pay, %opt ),
+ 'payment' => $cust_pay->paid,
+ #'target' => $target, #XXX
+ };
+}
+
+#voided payments
+foreach my $cust_pay_void ($cust_main->cust_pay_void) {
+ push @history, {
+ 'date' => $cust_pay_void->_date,
+ 'desc' => include('payment_history/voided_payment.html', $cust_pay_void),
+ 'void_payment' => $cust_pay_void->paid,
+ };
+
+}
+
+#credits (some false laziness w/payments)
+foreach my $cust_credit ($cust_main->cust_credit) {
+ push @history, {
+ 'date' => $cust_credit->_date,
+ 'desc' => include('payment_history/credit.html', $cust_credit),
+ 'credit' => $cust_credit->amount,
+ };
+
+}
+
+#refunds
+foreach my $cust_refund ($cust_main->cust_refund) {
+ push @history, {
+ 'date' => $cust_refund->_date,
+ 'desc' => include('payment_history/refund.html', $cust_refund),
+ 'refund' => $cust_refund->refund,
+ };
+
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history/credit.html b/httemplate/view/cust_main/payment_history/credit.html
new file mode 100644
index 0000000..2deb275
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history/credit.html
@@ -0,0 +1,140 @@
+<% $pre %>Credit<% $post %>
+by <% $cust_credit->otaker %><% "$reason$desc$apply$delete$unapply" %>
+<%init>
+
+my( $cust_credit, %opt ) = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my @cust_credit_bill = $cust_credit->cust_credit_bill;
+my @cust_credit_refund = $cust_credit->cust_credit_refund;
+
+my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
+if ( scalar(@cust_credit_bill) == 0
+ && scalar(@cust_credit_refund) == 0 ) {
+ #completely unapplied
+ $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
+ $post = '</FONT></B>';
+ if ( $curuser->access_right('Apply credit') ) {
+ if ( $cust_credit->cust_main->total_owed > 0 ) {
+ $apply = ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply',
+ 'action' => "${p}edit/cust_credit_bill.cgi?".
+ $cust_credit->crednum,
+ 'actionlabel' => 'Apply credit',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ if ( $cust_credit->cust_main->total_unapplied_refunds > 0 ) {
+ $apply.= ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply to refund',
+ 'action' => "${p}edit/cust_credit_refund.cgi?".
+ $cust_credit->crednum,
+ 'actionlabel' => 'Apply credit to refund',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ }
+} elsif ( scalar(@cust_credit_bill) == 1
+ && scalar(@cust_credit_refund) == 0
+ && $cust_credit->credited == 0 ) {
+ #applied to one invoice, the usual situation
+ $desc = ' '. $cust_credit_bill[0]->applied_to_invoice;
+} elsif ( scalar(@cust_credit_bill) == 0
+ && scalar(@cust_credit_refund) == 1
+ && $cust_credit->credited == 0 ) {
+ #applied to one refund
+ $desc = ' refunded on '. time2str("%D", $cust_credit_refund[0]->_date);
+} else {
+ #complicated
+ $desc = '<BR>';
+ foreach my $app ( sort { $a->_date <=> $b->_date }
+ ( @cust_credit_bill, @cust_credit_refund ) ) {
+ if ( $app->isa('FS::cust_credit_bill') ) {
+ $desc .= '&nbsp;&nbsp;'.
+ '$'. $app->amount.
+ ' '. $app->applied_to_invoice.
+ '<BR>';
+ #' on '. time2str("%D", $app->_date).
+ } elsif ( $app->isa('FS::cust_credit_refund') ) {
+ $desc .= '&nbsp;&nbsp;'.
+ '$'. $app->amount.
+ ' refunded on '. time2str("%D", $app->_date).
+ '<BR>';
+ } else {
+ die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund";
+ }
+ }
+ if ( $cust_credit->credited > 0 ) {
+ $desc .= '&nbsp;&nbsp;<B><FONT COLOR="#FF0000">$'.
+ $cust_credit->credited. ' unapplied</FONT></B>';
+ if ( $curuser->access_right('Apply credit') ) {
+ if ( $cust_credit->cust_main->total_owed > 0 ) {
+ $apply = ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply',
+ 'action' => "${p}edit/cust_credit_bill.cgi?".
+ $cust_credit->crednum,
+ 'actionlabel' => 'Apply credit',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ if ( $cust_credit->cust_main->total_unapplied_refunds > 0 ) {
+ $apply.= ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply to refund',
+ 'action' => "${p}edit/cust_credit_refund.cgi?".
+ $cust_credit->crednum,
+ 'actionlabel' => 'Apply credit to refund',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ }
+ $desc .= '<BR>';
+ }
+}
+#
+my $delete = '';
+if ( $cust_credit->closed !~ /^Y/i
+
+ #s'pose deleting a credit isn't bad like deleting a payment
+ # and this needs to be generally available until we have credit voiding..
+ #&& $conf->exists('deletecredits')
+
+ && $curuser->access_right('Delete credit')
+ )
+{
+ $delete = qq! (<A HREF="javascript:areyousure('!.
+ qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum.
+ qq!', 'Are you sure you want to delete this credit?')">!.
+ qq!delete</A>)!;
+}
+
+my $unapply = '';
+if ( $cust_credit->closed !~ /^Y/i
+ && scalar(@cust_credit_bill)
+ && $curuser->access_right('Unapply credit')
+ )
+{
+ $unapply = qq! (<A HREF="javascript:areyousure('!.
+ qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum.
+ qq!', 'Are you sure you want to unapply this credit?')">!.
+ qq!unapply</A>)!;
+}
+
+my $reason = $cust_credit->reason
+ ? ' ('. $cust_credit->reason. ')'
+ : '';
+
+</%init>
+
diff --git a/httemplate/view/cust_main/payment_history/invoice.html b/httemplate/view/cust_main/payment_history/invoice.html
new file mode 100644
index 0000000..39c6739
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history/invoice.html
@@ -0,0 +1,34 @@
+<% $link %><% $pre %>Invoice #<% $invnum %>
+(Balance $ <% $cust_bill->owed %>)<% $post %><% $link ? '</A>' : '' %><% $events %>
+<%init>
+
+my( $cust_bill, %opt ) = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my($pre, $post) = ('', '');
+if ( $cust_bill->owed > 0 ) {
+ $pre = '<B><FONT SIZE="+1" COLOR="#FF0000">Open ';
+ $post = '</FONT></B>';
+}
+
+my $invnum = $cust_bill->invnum;
+
+my $link = $curuser->access_right('View invoices')
+ ? qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!
+ : '';
+
+my $events = '';
+#1.9
+if ( $cust_bill->num_cust_event
+ && ( $curuser->access_right('Billing event reports')
+ || $curuser->access_right('View customer billing events')
+ )
+ ) {
+ $events =
+ qq!<BR><FONT SIZE="-1"><A HREF="${p}search/cust_event.html?invnum=!.
+ $cust_bill->invnum. '">(&nbsp;View invoice events&nbsp;)</A></FONT>';
+}
+#
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history/payment.html b/httemplate/view/cust_main/payment_history/payment.html
new file mode 100644
index 0000000..2e24b17
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history/payment.html
@@ -0,0 +1,209 @@
+<% $pre %>Payment<% $post %> by <% $otaker %>
+<% "$info$desc$view$apply$refund$void$delete$unapply" %>
+<%init>
+
+my( $cust_pay, %opt ) = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $payby = $cust_pay->payby;
+
+my $payinfo;
+if ( $payby eq 'CARD' ) {
+ $payinfo = $cust_pay->paymask;
+} elsif ( $payby eq 'CHEK' ) {
+ my( $account, $aba ) = split('@', $cust_pay->paymask );
+ $payinfo = "ABA $aba, Acct #$account";
+} else {
+ $payinfo = $cust_pay->payinfo;
+}
+my @cust_bill_pay = $cust_pay->cust_bill_pay;
+my @cust_pay_refund = $cust_pay->cust_pay_refund;
+
+my $target = "$payby$payinfo";
+$payby =~ s/^BILL$/Check #/ if $payinfo;
+$payby =~ s/^CHEK$/Electronic check /;
+$payby =~ s/^PREP$/Prepaid card /;
+$payby =~ s/^CARD$/Credit card #/;
+$payby =~ s/^COMP$/Complimentary by /;
+$payby =~ s/^CASH$/Cash/;
+$payby =~ s/^WEST$/Western Union/;
+$payby =~ s/^MCRD$/Manual credit card/;
+$payby =~ s/^BILL$//;
+my $info = $payby ? "($payby$payinfo)" : '';
+
+my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' );
+if ( scalar(@cust_bill_pay) == 0
+ && scalar(@cust_pay_refund) == 0 ) {
+ #completely unapplied
+ $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
+ $post = '</FONT></B>';
+ if ( $curuser->access_right('Apply payment') ) {
+ if ( $cust_pay->cust_main->total_owed > 0 ) {
+ $apply = ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply',
+ 'action' => "${p}edit/cust_bill_pay.cgi?".
+ $cust_pay->paynum,
+ 'actionlabel' => 'Apply payment',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ if ( $cust_pay->cust_main->total_unapplied_refunds > 0 ) {
+ $apply.= ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply to refund',
+ 'action' => "${p}edit/cust_pay_refund.cgi?".
+ $cust_pay->paynum,
+ 'actionlabel' => 'Apply payment to refund',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ }
+} elsif ( scalar(@cust_bill_pay) == 1
+ && scalar(@cust_pay_refund) == 0
+ && $cust_pay->unapplied == 0 ) {
+ #applied to one invoice, the usual situation
+ $desc = ' '. $cust_bill_pay[0]->applied_to_invoice;
+} elsif ( scalar(@cust_bill_pay) == 0
+ && scalar(@cust_pay_refund) == 1
+ && $cust_pay->unapplied == 0 ) {
+ #applied to one refund
+ $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date);
+} else {
+ #complicated
+ $desc = '<BR>';
+ foreach my $app ( sort { $a->_date <=> $b->_date }
+ ( @cust_bill_pay, @cust_pay_refund ) ) {
+ if ( $app->isa('FS::cust_bill_pay') ) {
+ $desc .= '&nbsp;&nbsp;'.
+ '$'. $app->amount.
+ ' '. $app->applied_to_invoice.
+ '<BR>';
+ #' on '. time2str("%D", $cust_bill_pay->_date).
+ } elsif ( $app->isa('FS::cust_pay_refund') ) {
+ $desc .= '&nbsp;&nbsp;'.
+ '$'. $app->amount.
+ ' refunded on '. time2str("%D", $app->_date).
+ '<BR>';
+ } else {
+ die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund";
+ }
+ }
+ if ( $cust_pay->unapplied > 0 ) {
+ $desc .= '&nbsp;&nbsp;'.
+ '<B><FONT COLOR="#FF0000">$'.
+ $cust_pay->unapplied. ' unapplied</FONT></B>';
+ if ( $curuser->access_right('Apply payment') ) {
+ if ( $cust_pay->cust_main->total_owed > 0 ) {
+ $apply = ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply',
+ 'action' => "${p}edit/cust_bill_pay.cgi?".
+ $cust_pay->paynum,
+ 'actionlabel' => 'Apply payment',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ if ( $cust_pay->cust_main->total_unapplied_refunds > 0 ) {
+ $apply.= ' ('.
+ include( '/elements/popup_link.html',
+ 'label' => 'apply to refund',
+ 'action' => "${p}edit/cust_pay_refund.cgi?".
+ $cust_pay->paynum,
+ 'actionlabel' => 'Apply payment to refund',
+ 'width' => 392,
+ #default# 'height' => 336,
+ ).
+ ')';
+ }
+ }
+ $desc .= '<BR>';
+ }
+}
+
+my $view =
+ ' ('. include('/elements/popup_link.html',
+ 'label' => 'view receipt',
+ 'action' => "${p}view/cust_pay.html?link=popup;paynum=".
+ $cust_pay->paynum,
+ 'actionlabel' => 'Payment Receipt',
+ ).
+ ')';
+
+my $refund = '';
+my $refund_days = $opt{'card_refund-days'} || 120;
+if ( $cust_pay->closed !~ /^Y/i
+ && $cust_pay->payby =~ /^(CARD|CHEK)$/
+ && time-$cust_pay->_date < $refund_days*86400
+ && $cust_pay->unrefunded > 0
+ && $curuser->access_right('Refund payment')
+) {
+ $refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!.
+ qq!paynum=!. $cust_pay->paynum. '"'.
+ qq! TITLE="Send a refund for this payment to the payment gateway"!.
+ qq!>refund</A>)!;
+}
+
+my $void = '';
+if ( $cust_pay->closed !~ /^Y/i
+ && ( ( $cust_pay->payby eq 'CARD'
+ && $curuser->access_right('Credit card void')
+ )
+ || ( $cust_pay->payby eq 'CHEK'
+ && $curuser->access_right('Echeck void')
+ )
+ || ( $cust_pay->payby !~ /^(CARD|CHEK)$/
+ && $curuser->access_right('Regular void')
+ )
+ )
+ )
+{
+ $void = qq! (<A HREF="javascript:areyousure('!.
+ qq!${p}misc/void-cust_pay.cgi?!. $cust_pay->paynum.
+ qq!', 'Are you sure you want to void this payment?')"!.
+ qq! TITLE="Void this payment from the database!.
+ ( $cust_pay->payby =~ /^(CARD|CHEK)$/
+ ? ' (do not send anything to the payment gateway)'
+ : ''
+ ). '"'.
+ qq!>void</A>)!;
+}
+
+my $delete = '';
+if ( $cust_pay->closed !~ /^Y/i
+ && $opt{'deletepayments'}
+ && $curuser->access_right('Delete payment')
+ )
+{
+ $delete = qq! (<A HREF="javascript:areyousure('!.
+ qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum.
+ qq!', 'Are you sure you want to delete this payment?')"!.
+ qq! TITLE="Delete this payment from the database completely - not recommended"!.
+ qq!>delete</A>)!;
+}
+
+my $unapply = '';
+if ( $cust_pay->closed !~ /^Y/i
+ && scalar(@cust_bill_pay)
+ && $curuser->access_right('Unapply payment')
+ )
+{
+ $unapply = qq! (<A HREF="javascript:areyousure('!.
+ qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum.
+ qq!', 'Are you sure you want to unapply this payment?')"!.
+ qq! TITLE="Keep this payment, but dissociate it from the invoices it is currently applied against"!.
+ qq!>unapply</A>)!;
+}
+
+my $otaker = $cust_pay->otaker;
+$otaker = '<i>auto billing</i>' if $otaker eq 'fs_daily';
+$otaker = '<i>customer self-service</i>' if $otaker eq 'fs_selfservice';
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history/refund.html b/httemplate/view/cust_main/payment_history/refund.html
new file mode 100644
index 0000000..4a48fea
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history/refund.html
@@ -0,0 +1,50 @@
+<% $pre %>Refund<% $post %>
+(<% $payby. $payinfo %>)
+by <% $cust_refund->otaker %><% $view %><% $delete %>
+<%init>
+
+my( $cust_refund, %opt ) = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $payby = $cust_refund->payby;
+my $payinfo = $payby eq 'CARD'
+ ? $cust_refund->paymask
+ : $cust_refund->payinfo;
+
+$payby =~ s/^BILL$/Check #/ if $payinfo;
+$payby =~ s/^BILL$/Check/;
+$payby =~ s/^CHEK$/Electronic check /;
+$payby =~ s/^(CARD|COMP)$/$1 /;
+
+my($pre, $post) = ('', '');
+if ( $cust_refund->unapplied > 0 ) {
+ $pre = '<B><FONT COLOR="#FF0000">Unapplied ';
+ $post = '</FONT></B>';
+}
+
+my $view =
+ ' ('. include('/elements/popup_link.html',
+ 'label' => 'view receipt',
+ 'action' => "${p}view/cust_refund.html?link=popup;".
+ 'refundnum='. $cust_refund->refundnum,
+ 'actionlabel' => 'Payment Receipt',
+ ).
+ ')';
+
+
+my $delete = '';
+if ( $cust_refund->closed !~ /^Y/i
+ && $opt{'deleterefunds'}
+ && $curuser->access_right('Delete refund')
+ )
+{
+ $delete = qq! (<A HREF="javascript:areyousure('!.
+ qq!${p}misc/delete-cust_refund.cgi?!. $cust_refund->refundnum.
+ qq!', 'Are you sure you want to delete this refund?')"!.
+ qq! TITLE="Delete this refund from the database completely - not recommended"!.
+ qq!>delete</A>)!;
+}
+
+</%init>
+
diff --git a/httemplate/view/cust_main/payment_history/voided_payment.html b/httemplate/view/cust_main/payment_history/voided_payment.html
new file mode 100644
index 0000000..9cbc47b
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history/voided_payment.html
@@ -0,0 +1,37 @@
+<DEL>Payment <% $info %></DEL>
+<I>voided <% time2str("%D", $cust_pay_void->void_date) %>
+by <% $cust_pay_void->otaker %></I><% $unvoid %>
+<%init>
+
+my( $cust_pay_void, %opt ) = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $payby = $cust_pay_void->payby;
+my $payinfo = $payby eq 'CARD'
+ ? $cust_pay_void->paymask
+ : $cust_pay_void->payinfo;
+
+$payby =~ s/^BILL$/Check #/ if $payinfo;
+$payby =~ s/^CHEK$/Electronic check /;
+$payby =~ s/^BILL$//;
+$payby =~ s/^(CARD|COMP)$/$1 /;
+my $info = $payby ? " ($payby$payinfo)" : '';
+
+my $unvoid = '';
+if ( $cust_pay_void->closed !~ /^Y/i
+ && $curuser->access_right('Unvoid')
+ )
+{
+ $unvoid = qq! (<A HREF="javascript:areyousure('!.
+ qq!${p}misc/unvoid-cust_pay_void.cgi?!. $cust_pay_void->paynum.
+ qq!', 'Are you sure you want to unvoid this payment?')"!.
+ qq! TITLE="Unvoid this payment from the database!.
+ ( $cust_pay_void->payby =~ /^(CARD|CHEK)$/
+ ? ' (do not send anything to the payment gateway)'
+ : ''
+ ). '"'.
+ qq!>unvoid</A>)!;
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/tickets.html b/httemplate/view/cust_main/tickets.html
new file mode 100644
index 0000000..b5d581d
--- /dev/null
+++ b/httemplate/view/cust_main/tickets.html
@@ -0,0 +1,84 @@
+<A NAME="tickets"><FONT SIZE="+2">Tickets</FONT></A>
+<BR>
+
+(<A HREF="<% $open_link %>">View <% $openlabel %> tickets for this customer</A>)
+(<A HREF="<% $res_link %>">View resolved tickets for this customer</A>)
+<BR>
+(<A HREF="<% $new_link %>">Create new ticket for this customer</A>)
+
+<% include("/elements/table-grid.html") %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">#</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Subject</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Status</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Queue</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Owner</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Priority</TH>
+</TR>
+
+% foreach my $ticket ( @tickets ) {
+% my $href = FS::TicketSystem->href_ticket($ticket->{id});
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF=<%$href%>><% $ticket->{id} %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF=<%$href%>><% $ticket->{subject} %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{status} %>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{queue} %>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{owner} %>
+ </TD>
+
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{content}
+ ? $ticket->{content}.' ('.$ticket->{priority}.')'
+ : $ticket->{priority}
+ %>
+ </TD>
+
+ </TR>
+
+% }
+
+</TABLE>
+
+<%init>
+
+my( $cust_main ) = @_;
+my( @tickets ) = $cust_main->tickets;
+
+my $open_link = FS::TicketSystem->href_customer_tickets($cust_main->custnum);
+my $openlabel = join('/', FS::TicketSystem->statuses );
+
+my $res_link = FS::TicketSystem->href_customer_tickets(
+ $cust_main->custnum,
+ { 'statuses' => [ 'resolved' ] }
+ );
+
+my $new_link = FS::TicketSystem->href_new_ticket(
+ $cust_main,
+ join(', ', $cust_main->invoicing_list_emailonly )
+ );
+
+</%init>
diff --git a/httemplate/view/cust_pay.html b/httemplate/view/cust_pay.html
new file mode 100644
index 0000000..c36d769
--- /dev/null
+++ b/httemplate/view/cust_pay.html
@@ -0,0 +1,135 @@
+% if ( $link eq 'popup' ) {
+
+ <% include('/elements/header-popup.html', "Payment Receipt" ) %>
+
+ <CENTER><A HREF="javascript:self.parent.location = '<% $pr_link %>'">Print</A></CENTER><BR>
+
+% } elsif ( $link eq 'print' ) {
+
+ <% include('/elements/header-popup.html', "Payment Receipt" ) %>
+
+% #it would be nice if the menubar could be hidden for print, but better to
+% # have it available than not, otherwise the user winds up at a dead end
+ <% menubar(
+ "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ %>
+ <BR><BR>
+
+% } else {
+
+ <% include('/elements/header.html', "Payment Receipt", menubar(
+ "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ 'Print receipt' => $pr_link,
+ ))
+ %>
+
+% }
+
+% unless ($link eq 'popup' ) {
+ <% include('/elements/small_custview.html',
+ $custnum,
+ scalar($conf->config('countrydefault')),
+ 1, #no balance
+ )
+ %>
+ <BR><BR>
+% }
+
+<% ntable("#cccccc", 2) %>
+
+<TR>
+ <TD ALIGN="right">Payment#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->paynum %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Date</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% time2str"%a&nbsp;%b&nbsp;%o,&nbsp;%Y&nbsp;%r", $cust_pay->_date %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $money_char. $cust_pay->paid %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Payment method</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->payby_name %> #<% $cust_pay->paymask %></B></TD>
+</TR>
+
+% if ( $cust_pay->payby =~ /^(CARD|CHEK|LECB)$/ && $cust_pay->paybatch ) {
+
+ <TR>
+ <TD ALIGN="right">Processor</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->processor %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Authorization#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->authorization %></B></TD>
+ </TR>
+
+% if ( $cust_pay->order_number ) {
+ <TR>
+ <TD ALIGN="right">Order#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->order_number %></B></TD>
+ </TR>
+% }
+
+% }
+
+</TABLE>
+
+% if ( $link eq 'print' ) {
+
+ <SCRIPT TYPE="text/javascript">
+ window.print();
+ </SCRIPT>
+
+% }
+
+% if ( $link =~ /^(popup|print)$/ ) {
+ </BODY>
+ </HTML>
+% } else {
+ <% include('/elements/footer.html') %>
+% }
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('View invoices') #remove this in 1.9 EVENTUALLY
+ || $curuser->access_right('View customer payments');
+
+$cgi->param('paynum') =~ /^(\d+)$/ or die "no paynum";
+my $paynum = $1;
+
+my $link = '';
+if ( $cgi->param('link') =~ /^(\w+)$/ ) {
+ $link = $1;
+}
+
+my $cust_pay = qsearchs({
+ 'select' => 'cust_pay.*',
+ 'table' => 'cust_pay',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'paynum' => $paynum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die "Payment #$paynum not found!" unless $cust_pay;
+
+my $pr_link = "${p}view/cust_pay.html?link=print;paynum=$paynum";
+
+my $custnum = $cust_pay->custnum;
+my $display_custnum = $cust_pay->cust_main->display_custnum;
+
+my $conf = new FS::Conf;
+
+my $money_char = $conf->config('money_char') || '$';
+
+tie my %payby, 'Tie::IxHash', FS::payby->payby2longname;
+
+</%init>
diff --git a/httemplate/view/cust_refund.html b/httemplate/view/cust_refund.html
new file mode 100644
index 0000000..138c878
--- /dev/null
+++ b/httemplate/view/cust_refund.html
@@ -0,0 +1,142 @@
+% if ( $link eq 'popup' ) {
+
+ <% include('/elements/header-popup.html', "Refund Receipt" ) %>
+
+ <CENTER><A HREF="javascript:self.parent.location = '<% $pr_link %>'">Print</A></CENTER><BR>
+
+% } elsif ( $link eq 'print' ) {
+
+ <% include('/elements/header-popup.html', "Refund Receipt" ) %>
+
+% #it would be nice if the menubar could be hidden for print, but better to
+% # have it available than not, otherwise the user winds up at a dead end
+ <% menubar(
+ "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ %>
+ <BR><BR>
+
+% } else {
+
+ <% include('/elements/header.html', "Refund Receipt", menubar(
+ "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ 'Print receipt' => $pr_link,
+ ))
+ %>
+
+% }
+
+% unless ($link eq 'popup' ) {
+ <% include('/elements/small_custview.html',
+ $custnum,
+ scalar($conf->config('countrydefault')),
+ 1, #no balance
+ )
+ %>
+ <BR><BR>
+% }
+
+<% ntable("#cccccc", 2) %>
+
+<TR>
+ <TD ALIGN="right">Refund#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_refund->refundnum %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Date</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% time2str"%a&nbsp;%b&nbsp;%o,&nbsp;%Y&nbsp;%r", $cust_refund->_date %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Amount</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $money_char. $cust_refund->refund %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Reason</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_refund->reason %></B></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Refund method</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_refund->payby_name %><% $cust_refund->paymask ? ' #'.$cust_refund->paymask : '' %></B></TD>
+</TR>
+
+% if ( $cust_refund->payby =~ /^(CARD|CHEK|LECB)$/ && $cust_refund->paybatch ) {
+
+ <TR>
+ <TD ALIGN="right">Processor</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_refund->processor %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Authorization#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_refund->authorization %></B></TD>
+ </TR>
+
+% if ( $cust_refund->order_number ) {
+ <TR>
+ <TD ALIGN="right">Order#</TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_refund->order_number %></B></TD>
+ </TR>
+% }
+
+% }
+
+</TABLE>
+
+% if ( $link eq 'print' ) {
+
+ <SCRIPT TYPE="text/javascript">
+ window.print();
+ </SCRIPT>
+
+% }
+
+% if ( $link =~ /^(popup|print)$/ ) {
+ </BODY>
+ </HTML>
+% } else {
+ <% include('/elements/footer.html') %>
+% }
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('View invoices') #remove this in 1.9 EVENTUALLY
+ || $curuser->access_right('View customer payments');
+ #'View customer refunds' ???
+
+
+$cgi->param('refundnum') =~ /^(\d+)$/ or die "no refundnum";
+my $refundnum = $1;
+
+my $link = '';
+if ( $cgi->param('link') =~ /^(\w+)$/ ) {
+ $link = $1;
+}
+
+my $cust_refund = qsearchs({
+ 'select' => 'cust_refund.*',
+ 'table' => 'cust_refund',
+ 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'refundnum' => $refundnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die "Refund #$refundnum not found!" unless $cust_refund;
+
+my $pr_link = "${p}view/cust_refund.html?link=print;refundnum=$refundnum";
+
+my $custnum = $cust_refund->custnum;
+my $display_custnum = $cust_refund->cust_main->display_custnum;
+
+my $conf = new FS::Conf;
+
+my $money_char = $conf->config('money_char') || '$';
+
+tie my %payby, 'Tie::IxHash', FS::payby->payby2longname;
+
+</%init>
diff --git a/httemplate/view/elements/svc_Common.html b/httemplate/view/elements/svc_Common.html
new file mode 100644
index 0000000..a0b4e37
--- /dev/null
+++ b/httemplate/view/elements/svc_Common.html
@@ -0,0 +1,152 @@
+% # options example...
+% #
+% # 'table' => 'svc_something'
+% #
+% # 'labels' => {
+% # 'column' => 'Label',
+% # },
+% #
+% # listref - each item is a literal column name (or method) or (notyet) coderef
+% # if not specified all columns (except for the primary key) will be viewable
+% # 'fields' => [
+% # ]
+% #
+% # # defaults to "edit/$table.cgi?", will have svcnum appended
+% # 'edit_url' =>
+%
+%
+% if ( $custnum ) {
+
+
+ <% include("/elements/header.html","View $label: $value") %>
+
+ <% include( '/elements/small_custview.html', $custnum, '', 1,
+ "${p}view/cust_main.cgi") %>
+ <BR>
+% } else {
+
+
+ <SCRIPT>
+ function areyousure(href) {
+ if (confirm("Permanently delete this <% $label %>?") == true)
+ window.location.href = href;
+ }
+ </SCRIPT>
+
+ <% include("/elements/header.html","View $label: $value", menubar(
+ "Cancel this (unaudited) $label" =>
+ "javascript:areyousure(\'${p}misc/cancel-unaudited.cgi?$svcnum\')"
+ )) %>
+% }
+
+
+Service #<B><% $svcnum %></B>
+% my $url = $opt{'edit_url'} || $p. 'edit/'. $opt{'table'}. '.cgi?';
+| <A HREF="<%$url%><%$svcnum%>">Edit this <% $label %></A>
+<BR>
+
+<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+
+% foreach my $f ( @$fields ) {
+%
+% my($field, $type);
+% if ( ref($f) ) {
+% $field = $f->{'field'},
+% $type = $f->{'type'} || 'text',
+% } else {
+% $field = $f;
+% $type = 'text';
+% }
+%
+% my $columndef = $part_svc->part_svc_column($field);
+% unless ($columndef->columnflag eq 'F' && !length($columndef->columnvalue)) {
+
+ <TR>
+ <TD ALIGN="right">
+ <% ( $opt{labels} && exists $opt{labels}->{$field} )
+ ? $opt{labels}->{$field}
+ : $field
+ %>
+ </TD>
+
+% #eventually more options for <SELECT>, etc. fields
+
+ <TD BGCOLOR="#ffffff"><% $svc_x->$field %><TD>
+
+ </TR>
+
+% }
+%
+% }
+
+% foreach (sort { $a cmp $b } $svc_x->virtual_fields) {
+ <% $svc_x->pvf($_)->widget('HTML', 'view', $svc_x->getfield($_)) %>
+% }
+
+
+</TABLE></TD></TR></TABLE>
+
+<BR>
+
+% if ( defined($opt{'html_foot'}) ) {
+
+ <% ref($opt{'html_foot'})
+ ? &{ $opt{'html_foot'} }($svc_x)
+ : $opt{'html_foot'}
+ %>
+ <BR>
+
+% }
+
+<% joblisting({'svcnum'=>$svcnum}, 1) %>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+my(%opt) = @_;
+
+my $table = $opt{'table'};
+
+my $fields = $opt{'fields'}
+ #|| [ grep { $_ ne 'svcnum' } dbdef->table($table)->columns ];
+ || [ grep { $_ ne 'svcnum' } fields($table) ];
+
+my $svcnum;
+if ( $cgi->param('svcnum') ) {
+ $cgi->param('svcnum') =~ /^(\d+)$/ or die "unparsable svcnum";
+ $svcnum = $1;
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die "no svcnum";
+ $svcnum = $1;
+}
+my $svc_x = qsearchs({
+ 'select' => $opt{'table'}.'.*',
+ 'table' => $opt{'table'},
+ 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $svcnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+}) or die "Unknown svcnum $svcnum in ". $opt{'table'}. " table\n";
+
+my $cust_svc = $svc_x->cust_svc;
+my($label, $value, $svcdb) = $cust_svc->label;
+
+my $part_svc = $cust_svc->part_svc;
+
+my $pkgnum = $cust_svc->pkgnum;
+
+my($cust_pkg, $custnum);
+if ($pkgnum) {
+ $cust_pkg = $cust_svc->cust_pkg;
+ $custnum = $cust_pkg->custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+
+</%init>
diff --git a/httemplate/view/logo.cgi b/httemplate/view/logo.cgi
new file mode 100644
index 0000000..aeca0f3
--- /dev/null
+++ b/httemplate/view/logo.cgi
@@ -0,0 +1,47 @@
+<% $data %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+my $type;
+if ( $cgi->param('type') eq 'png' ) {
+ $type = 'png';
+} elsif ( $cgi->param('type') eq 'eps' ) {
+ $type = 'eps';
+} else {
+ die "unknown logo type ". $cgi->param('type');
+}
+
+my $data;
+if ( $cgi->param('preview_session') =~ /^(\w+)$/ ) {
+
+ my $session = $1;
+ my $curuser = $FS::CurrentUser::CurrentUser;
+ $data = decode_base64( $curuser->option("logo_preview$session") );
+
+} elsif ( $cgi->param('name') =~ /^([^\.\/]*)$/ ) {
+
+ my $templatename = $1;
+ if ( $templatename && $conf->exists("logo_$templatename.$type") ) {
+ $templatename = "_$templatename";
+ } else {
+ $templatename = '';
+ }
+
+ if ( $type eq 'png' ) {
+ $data = $conf->config_binary("logo$templatename.png");
+ } elsif ( $type eq 'eps' ) {
+ #convert EPS to a png... punting on that for now
+ }
+
+} else {
+ die "neither a valid name nor a valid preview_session specified";
+}
+
+http_header('Content-Type' => 'image/png' );
+
+</%init>
+
diff --git a/httemplate/view/svc_Common.html b/httemplate/view/svc_Common.html
new file mode 100644
index 0000000..bb3a6dd
--- /dev/null
+++ b/httemplate/view/svc_Common.html
@@ -0,0 +1,29 @@
+<%init>
+
+# false laziness w/edit/svc_Common.html
+
+$cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unparsable svcdb";
+my $table = $1;
+require "FS/$table.pm";
+
+my %opt;
+if ( UNIVERSAL::can("FS::$table", 'table_info') ) {
+ $opt{'name'} = "FS::$table"->table_info->{'name'};
+
+ my $fields = "FS::$table"->table_info->{'fields'};
+ my %labels = map { $_ => ( ref($fields->{$_})
+ ? $fields->{$_}{'label'}
+ : $fields->{$_}
+ );
+ }
+ keys %$fields;
+ $opt{'labels'} = \%labels;
+}
+
+</%init>
+<% include('elements/svc_Common.html',
+ 'table' => $table,
+ 'edit_url' => $p."edit/svc_Common.html?svcdb=$table;svcnum=",
+ %opt,
+ )
+%>
diff --git a/httemplate/view/svc_acct.cgi b/httemplate/view/svc_acct.cgi
new file mode 100755
index 0000000..e87a8ee
--- /dev/null
+++ b/httemplate/view/svc_acct.cgi
@@ -0,0 +1,401 @@
+% if ( $custnum ) {
+
+ <% include("/elements/header.html","View $svc account") %>
+ <% include( '/elements/small_custview.html', $custnum, '', 1,
+ "${p}view/cust_main.cgi") %>
+ <BR>
+
+% } else {
+
+ <SCRIPT>
+ function areyousure(href) {
+ if (confirm("Permanently delete this account?") == true)
+ window.location.href = href;
+ }
+ </SCRIPT>
+
+ <% include("/elements/header.html",'Account View', menubar(
+ "Cancel this (unaudited) account" =>
+ "javascript:areyousure(\'${p}misc/cancel-unaudited.cgi?$svcnum\')",
+ )) %>
+
+% }
+
+% if ( $part_svc->part_export_usage ) {
+%
+% my $last_bill;
+% my %plandata;
+% if ( $cust_pkg ) {
+% #false laziness w/httemplate/edit/part_pkg... this stuff doesn't really
+% #belong in plan data
+% %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }
+% split("\n", $cust_pkg->part_pkg->plandata );
+%
+% $last_bill = $cust_pkg->last_bill;
+% } else {
+% $last_bill = 0;
+% %plandata = ();
+% }
+%
+% my $seconds = $svc_acct->seconds_since_sqlradacct( $last_bill, time );
+% my $hour = int($seconds/3600);
+% my $min = int( ($seconds%3600) / 60 );
+% my $sec = $seconds%60;
+%
+% my $input = $svc_acct->attribute_since_sqlradacct(
+% $last_bill, time, 'AcctInputOctets'
+% ) / 1048576;
+% my $output = $svc_acct->attribute_since_sqlradacct(
+% $last_bill, time, 'AcctOutputOctets'
+% ) / 1048576;
+%
+%
+
+
+ RADIUS session information<BR>
+ <% ntable('#cccccc',2) %>
+ <TR><TD BGCOLOR="#ffffff">
+% if ( $seconds ) {
+
+ Online <B><% $hour %></B>h <B><% $min %></B>m <B><% $sec %></B>s
+% } else {
+
+ Has not logged on
+% }
+% if ( $cust_pkg ) {
+
+ since last bill (<% time2str('%a %b %o %Y', $last_bill) %>)
+% if ( length($plandata{recur_included_hours}) ) {
+
+ - <% $plandata{recur_included_hours} %> total hours in plan
+% }
+
+ <BR>
+% } else {
+
+ (no billing cycle available for unaudited account)<BR>
+% }
+
+
+ Upload: <B><% sprintf("%.3f", $input) %></B> megabytes<BR>
+ Download: <B><% sprintf("%.3f", $output) %></B> megabytes<BR>
+ Last Login: <B><% $svc_acct->last_login_text %></B><BR>
+% my $href = qq!<A HREF="${p}search/sqlradius.cgi?svcnum=$svcnum!;
+
+ View session detail:
+ <% $href %>;begin=<% $last_bill %>">this billing cycle</A>
+ | <% $href %>;begin=<% time-15552000 %>">past six months</A>
+ | <% $href %>">all sessions</A>
+
+ </TD></TR></TABLE><BR>
+% }
+
+% my @part_svc = ();
+% if ($FS::CurrentUser::CurrentUser->access_right('Change customer service')) {
+
+ <SCRIPT TYPE="text/javascript">
+ function enable_change () {
+ if ( document.OneTrueForm.svcpart.selectedIndex > 1 ) {
+ document.OneTrueForm.submit.disabled = false;
+ } else {
+ document.OneTrueForm.submit.disabled = true;
+ }
+ }
+ </SCRIPT>
+
+ <FORM NAME="OneTrueForm" ACTION="<%$p%>edit/process/cust_svc.cgi">
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
+ <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+
+% #print qq!<BR><A HREF="../misc/sendconfig.cgi?$svcnum">Send account information</A>!;
+%
+% if ( $pkgnum ) {
+% @part_svc = grep { $_->svcdb eq 'svc_acct'
+% && $_->svcpart != $part_svc->svcpart }
+% $cust_pkg->available_part_svc;
+% } else {
+% @part_svc = qsearch('part_svc', {
+% svcdb => 'svc_acct',
+% disabled => '',
+% svcpart => { op=>'!=', value=>$part_svc->svcpart },
+% } );
+% }
+%
+% }
+
+Service #<B><% $svcnum %></B>
+| <A HREF="<%$p%>edit/svc_acct.cgi?<%$svcnum%>">Edit this service</A>
+
+% if ( @part_svc ) {
+
+| <SELECT NAME="svcpart" onChange="enable_change()">
+ <OPTION VALUE="">Change service</OPTION>
+ <OPTION VALUE="">--------------</OPTION>
+% foreach my $opt_part_svc ( @part_svc ) {
+
+ <OPTION VALUE="<% $opt_part_svc->svcpart %>"><% $opt_part_svc->svc %></OPTION>
+% }
+
+ </SELECT>
+ <INPUT NAME="submit" TYPE="submit" VALUE="Change" disabled>
+
+% }
+
+
+<% &ntable("#cccccc") %><TR><TD><% &ntable("#cccccc",2) %>
+
+<TR>
+ <TD ALIGN="right">Service</TD>
+ <TD BGCOLOR="#ffffff"><% $part_svc->svc %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->username %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Domain</TD>
+ <TD BGCOLOR="#ffffff"><% $domain %></TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD BGCOLOR="#ffffff">
+% my $password = $svc_acct->_password;
+% if ( $password =~ /^\*\w+\* (.*)$/ ) {
+% $password = $1;
+%
+
+ <I>(login disabled)</I>
+% }
+% if ( $conf->exists('showpasswords') ) {
+
+ <PRE><% encode_entities($password) %></PRE>
+% } else {
+
+ <I>(hidden)</I>
+% }
+
+
+ </TD>
+</TR>
+% $password = '';
+% if ( $conf->exists('security_phrase') ) {
+% my $sec_phrase = $svc_acct->sec_phrase;
+%
+
+ <TR>
+ <TD ALIGN="right">Security phrase</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->sec_phrase %></TD>
+ </TR>
+% }
+% if ( $svc_acct->popnum ) {
+% my $svc_acct_pop = qsearchs('svc_acct_pop',{'popnum'=>$svc_acct->popnum});
+%
+
+ <TR>
+ <TD ALIGN="right">Access number</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct_pop->text %></TD>
+ </TR>
+% }
+% if ($svc_acct->uid ne '') {
+
+ <TR>
+ <TD ALIGN="right">UID</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->uid %></TD>
+ </TR>
+% }
+% if ($svc_acct->gid ne '') {
+
+ <TR>
+ <TD ALIGN="right">GID</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->gid %></TD>
+ </TR>
+% }
+% if ($svc_acct->finger ne '') {
+
+ <TR>
+ <TD ALIGN="right">GECOS</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->finger %></TD>
+ </TR>
+% }
+% if ($svc_acct->dir ne '') {
+
+ <TR>
+ <TD ALIGN="right">Home directory</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->dir %></TD>
+ </TR>
+% }
+% if ($svc_acct->shell ne '') {
+
+ <TR>
+ <TD ALIGN="right">Shell</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->shell %></TD>
+ </TR>
+% }
+% if ($svc_acct->quota ne '') {
+
+ <TR>
+ <TD ALIGN="right">Quota</TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->quota %></TD>
+ </TR>
+% }
+% if ($svc_acct->slipip) {
+
+ <TR>
+ <TD ALIGN="right">IP address</TD>
+ <TD BGCOLOR="#ffffff">
+ <% ( $svc_acct->slipip eq "0.0.0.0" || $svc_acct->slipip eq '0e0' )
+ ? "<I>(Dynamic)</I>"
+ : $svc_acct->slipip
+ %>
+ </TD>
+ </TR>
+% }
+% my %ulabel = ( seconds => 'Time',
+% upbytes => 'Upload bytes',
+% downbytes => 'Download bytes',
+% totalbytes => 'Total bytes',
+% );
+% foreach my $uf ( keys %ulabel ) {
+% my $tf = $uf . "_threshold";
+% if ( $svc_acct->$uf ne '' ) {
+% my $v = $uf eq 'seconds'
+% #? (($svc_acct->$uf < 0 ? '-' : ''). duration_exact($svc_acct->$uf) )
+% ? ($svc_acct->$uf < 0 ? '-' : '').
+% int(abs($svc_acct->$uf)/3600). "hr ".
+% sprintf("%02d",(abs($svc_acct->$uf)%3600)/60). "min"
+% : FS::UI::bytecount::display_bytecount($svc_acct->$uf);
+ <TR>
+ <TD ALIGN="right"><% $ulabel{$uf} %> remaining</TD>
+ <TD BGCOLOR="#ffffff"><% $v %></TD>
+ </TR>
+
+% }
+% }
+% foreach my $attribute ( grep /^radius_/, $svc_acct->fields ) {
+% $attribute =~ /^radius_(.*)$/;
+% my $pattribute = $FS::raddb::attrib{$1};
+%
+
+ <TR>
+ <TD ALIGN="right">Radius (reply) <% $pattribute %></TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->getfield($attribute) %></TD>
+ </TR>
+% }
+% foreach my $attribute ( grep /^rc_/, $svc_acct->fields ) {
+% $attribute =~ /^rc_(.*)$/;
+% my $pattribute = $FS::raddb::attrib{$1};
+%
+
+ <TR>
+ <TD ALIGN="right">Radius (check) <% $pattribute %></TD>
+ <TD BGCOLOR="#ffffff"><% $svc_acct->getfield($attribute) %></TD>
+ </TR>
+% }
+
+
+<TR>
+ <TD ALIGN="right">RADIUS groups</TD>
+ <TD BGCOLOR="#ffffff"><% join('<BR>', $svc_acct->radius_groups) %></TD>
+</TR>
+%
+%# Can this be abstracted further? Maybe a library function like
+%# widget('HTML', 'view', $svc_acct) ? It would definitely make UI
+%# style management easier.
+%
+% foreach (sort { $a cmp $b } $svc_acct->virtual_fields) {
+
+ <% $svc_acct->pvf($_)->widget('HTML', 'view', $svc_acct->getfield($_)) %>
+% }
+
+
+</TABLE></TD></TR></TABLE>
+</FORM>
+<BR><BR>
+
+% if ( @svc_www ) {
+ Hosting
+ <% &ntable("#cccccc") %><TR><TD><% &ntable("#cccccc",2) %>
+% foreach my $svc_www (@svc_www) {
+% my($label, $value) = $svc_www->cust_svc->label;
+% my $link = $p. 'view/svc_www.cgi?'. $svc_www->svcnum;
+ <TR>
+ <TD BGCOLOR="#ffffff">
+ <A HREF="<% $link %>"><% "$label: $value" %></A>
+ </TD>
+ </TR>
+% }
+ </TABLE></TD></TR></TABLE>
+ <BR><BR>
+% }
+
+<% join("<BR>", $conf->config('svc_acct-notes') ) %>
+<BR><BR>
+
+<% joblisting({'svcnum'=>$svcnum}, 1) %>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+my $conf = new FS::Conf;
+
+my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_acct = qsearchs({
+ 'select' => 'svc_acct.*',
+ 'table' => 'svc_acct',
+ 'addl_from' => $addl_from,
+ 'hashref' => { 'svcnum' => $svcnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ ),
+});
+die "Unknown svcnum" unless $svc_acct;
+
+#false laziness w/all svc_*.cgi
+my $cust_svc = qsearchs( 'cust_svc' , { 'svcnum' => $svcnum } );
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum);
+if ($pkgnum) {
+ $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
+ $custnum = $cust_pkg->custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+#eofalse
+
+my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } );
+die "Unknown svcpart" unless $part_svc;
+my $svc = $part_svc->svc;
+
+die 'Empty domsvc for svc_acct.svcnum '. $svc_acct->svcnum
+ unless $svc_acct->domsvc;
+my $svc_domain = qsearchs('svc_domain', { 'svcnum' => $svc_acct->domsvc } );
+die 'Unknown domain (domsvc '. $svc_acct->domsvc.
+ ' for svc_acct.svcnum '. $svc_acct->svcnum. ')'
+ unless $svc_domain;
+my $domain = $svc_domain->domain;
+
+my @svc_www = qsearch({
+ 'select' => 'svc_www.*',
+ 'table' => 'svc_www',
+ 'addl_from' => $addl_from,
+ 'hashref' => { 'usersvc' => $svcnum },
+ #XXX shit outta luck if you somehow got them linked across agents
+ # maybe we should show but not link to them? kinda makes sense...
+ # (maybe a specific ACL for this situation???)
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ ),
+});
+
+</%init>
diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi
new file mode 100644
index 0000000..145d341
--- /dev/null
+++ b/httemplate/view/svc_broadband.cgi
@@ -0,0 +1,212 @@
+<%include("/elements/header.html",'Broadband Service View', menubar(
+ ( ( $custnum )
+ ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ : ( "Cancel this (unaudited) website" =>
+ "${p}misc/cancel-unaudited.cgi?$svcnum" )
+ )
+))
+%>
+
+<A HREF="<%${p}%>edit/svc_broadband.cgi?<%$svcnum%>">Edit this information</A>
+<BR>
+<%ntable("#cccccc")%>
+ <TR>
+ <TD>
+ <%ntable("#cccccc",2)%>
+ <TR>
+ <TD ALIGN="right">Service number</TD>
+ <TD BGCOLOR="#ffffff"><%$svcnum%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Description</TD>
+ <TD BGCOLOR="#ffffff"><%$description%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Router</TD>
+ <TD BGCOLOR="#ffffff"><%$routernum%>: <%$routername%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Download Speed</TD>
+ <TD BGCOLOR="#ffffff"><%$speed_down%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Upload Speed</TD>
+ <TD BGCOLOR="#ffffff"><%$speed_up%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">IP Address</TD>
+ <TD BGCOLOR="#ffffff"><%$ip_addr%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">IP Netmask</TD>
+ <TD BGCOLOR="#ffffff"><%$ip_netmask%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">IP Gateway</TD>
+ <TD BGCOLOR="#ffffff"><%$ip_gateway%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">MAC Address</TD>
+ <TD BGCOLOR="#ffffff"><%$mac_addr%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Latitude</TD>
+ <TD BGCOLOR="#ffffff"><%$latitude%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Longitude</TD>
+ <TD BGCOLOR="#ffffff"><%$longitude%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Altitude</TD>
+ <TD BGCOLOR="#ffffff"><%$altitude%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">VLAN Profile</TD>
+ <TD BGCOLOR="#ffffff"><%$vlan_profile%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Authentication Key</TD>
+ <TD BGCOLOR="#ffffff"><%$auth_key%></TD>
+ </TR>
+ <TR COLSPAN="2"><TD></TD></TR>
+%
+%foreach (sort { $a cmp $b } $svc_broadband->virtual_fields) {
+% print $svc_broadband->pvf($_)->widget('HTML', 'view',
+% $svc_broadband->getfield($_)), "\n";
+%}
+%
+%
+
+ </TABLE>
+ </TD>
+ </TR>
+</TABLE>
+
+<BR>
+<%ntable("#cccccc", 2)%>
+%
+% my $sb_router = qsearchs('router', { svcnum => $svcnum });
+% if ($sb_router) {
+%
+
+ <B>Router associated: <%$sb_router->routername%> </B>
+ <A HREF="<%popurl(2)%>edit/router.cgi?<%$sb_router->routernum%>">
+ (details)
+ </A>
+ <BR>
+% my @sb_addr_block;
+% if (@sb_addr_block = $sb_router->addr_block) {
+%
+
+ <B>Address space </B>
+ <A HREF="<%popurl(2)%>browse/addr_block.cgi">
+ (edit)
+ </A>
+ <BR>
+% print ntable("#cccccc", 1);
+% foreach (@sb_addr_block) {
+
+ <TR>
+ <TD><%$_->ip_gateway%>/<%$_->ip_netmask%></TD>
+ </TR>
+% }
+
+ </TABLE>
+% } else {
+
+ <B>No address space allocated.</B>
+% }
+
+ <BR>
+%
+% } else {
+%
+
+
+<FORM METHOD="GET" ACTION="<%popurl(2)%>edit/router.cgi">
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
+Add router named
+ <INPUT TYPE="text" NAME="routername" SIZE="32" VALUE="Broadband router (<%$svcnum%>)">
+ <INPUT TYPE="submit" VALUE="Add router">
+</FORM>
+%
+%}
+%
+
+
+<BR>
+<%joblisting({'svcnum'=>$svcnum}, 1)%>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_broadband = qsearchs({
+ 'select' => 'svc_broadband.*',
+ 'table' => 'svc_broadband',
+ 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $svcnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+}) or die "svc_broadband: Unknown svcnum $svcnum";
+
+#false laziness w/all svc_*.cgi
+my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $svcnum } );
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum, $display_custnum);
+if ($pkgnum) {
+ $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
+ $custnum = $cust_pkg->custnum;
+ $display_custnum = $cust_pkg->cust_main->display_custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+#eofalse
+
+my $addr_block = $svc_broadband->addr_block;
+my $router = $addr_block->router;
+
+if (not $router) { die "Could not lookup router for svc_broadband (svcnum $svcnum)" };
+
+my (
+ $routername,
+ $routernum,
+ $speed_down,
+ $speed_up,
+ $ip_addr,
+ $ip_gateway,
+ $ip_netmask,
+ $mac_addr,
+ $latitude,
+ $longitude,
+ $altitude,
+ $vlan_profile,
+ $auth_key,
+ $description,
+ ) = (
+ $router->getfield('routername'),
+ $router->getfield('routernum'),
+ $svc_broadband->getfield('speed_down'),
+ $svc_broadband->getfield('speed_up'),
+ $svc_broadband->getfield('ip_addr'),
+ $addr_block->ip_gateway,
+ $addr_block->NetAddr->mask,
+ $svc_broadband->mac_addr,
+ $svc_broadband->latitude,
+ $svc_broadband->longitude,
+ $svc_broadband->altitude,
+ $svc_broadband->vlan_profile,
+ $svc_broadband->auth_key,
+ $svc_broadband->description,
+ );
+
+</%init>
diff --git a/httemplate/view/svc_domain.cgi b/httemplate/view/svc_domain.cgi
new file mode 100755
index 0000000..8c1f4ce
--- /dev/null
+++ b/httemplate/view/svc_domain.cgi
@@ -0,0 +1,161 @@
+<% include("/elements/header.html",'Domain View', menubar(
+ ( ( $pkgnum || $custnum )
+ ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ : ( "Delete this (unaudited) domain" =>
+ "javascript:areyousure('${p}misc/cancel-unaudited.cgi?$svcnum', 'Delete $domain and all records?' )" )
+ )
+)) %>
+
+Service #<% $svcnum %>
+<BR>Service: <B><% $part_svc->svc %></B>
+<BR>Domain name: <B><% $domain %></B>
+% if ( $FS::CurrentUser::CurrentUser->access_right('Edit domain catchall') ) {
+ <BR>Catch all email <A HREF="<% ${p} %>misc/catchall.cgi?<% $svcnum %>">(change)</A>:
+% } else {
+ <BR>Catch all email:
+% }
+
+<% $email ? "<B>$email</B>" : "<I>(none)<I>" %>
+<BR><BR><A HREF="<% ${p} %>misc/whois.cgi?custnum=<%$custnum%>;svcnum=<%$svcnum%>;domain=<%$domain%>">View whois information.</A>
+<BR><BR>
+<SCRIPT>
+ function areyousure(href, message) {
+ if ( confirm(message) == true )
+ window.location.href = href;
+ }
+ function slave_areyousure() {
+ return confirm("Remove all records and slave from " + document.SlaveForm.recdata.value + "?");
+ }
+</SCRIPT>
+
+% my @records; if ( @records = $svc_domain->domain_record ) {
+
+ <% include('/elements/table-grid.html') %>
+
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = $bgcolor2;
+
+ <tr>
+ <th CLASS="grid" BGCOLOR="#cccccc">Zone</th>
+ <th CLASS="grid" BGCOLOR="#cccccc">Type</th>
+ <th CLASS="grid" BGCOLOR="#cccccc">Data</th>
+ </tr>
+
+% foreach my $domain_record ( @records ) {
+% my $type = $domain_record->rectype eq '_mstr'
+% ? "(slave)"
+% : $domain_record->recaf. ' '. $domain_record->rectype;
+
+
+ <tr>
+ <td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $domain_record->reczone %></td>
+ <td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $type %></td>
+ <td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $domain_record->recdata %>
+
+% unless ( $domain_record->rectype eq 'SOA'
+% || ! $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice')
+% ) {
+% ( my $recdata = $domain_record->recdata ) =~ s/"/\\'\\'/g;
+ (<A HREF="javascript:areyousure('<%$p%>misc/delete-domain_record.cgi?<%$domain_record->recnum%>', 'Delete \'<% $domain_record->reczone %> <% $type %> <% $recdata %>\' ?' )">delete</A>)
+% }
+ </td>
+ </tr>
+
+
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+% }
+
+ </table>
+% }
+
+% if ( $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice') ) {
+ <BR>
+ <FORM METHOD="POST" ACTION="<%$p%>edit/process/domain_record.cgi">
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
+ <INPUT TYPE="text" NAME="reczone">
+ <INPUT TYPE="hidden" NAME="recaf" VALUE="IN"> IN
+ <SELECT NAME="rectype">
+% foreach (qw( A NS CNAME MX PTR TXT) ) {
+ <OPTION VALUE="<%$_%>"><%$_%></OPTION>
+% }
+ </SELECT>
+ <INPUT TYPE="text" NAME="recdata">
+ <INPUT TYPE="submit" VALUE="Add record">
+ </FORM>
+
+ <BR><BR>
+ or
+ <BR><BR>
+
+ <FORM NAME="SlaveForm" METHOD="POST" ACTION="<%$p%>edit/process/domain_record.cgi">
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
+% if ( @records ) {
+ Delete all records and
+% }
+ Slave from nameserver IP
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
+ <INPUT TYPE="hidden" NAME="reczone" VALUE="@">
+ <INPUT TYPE="hidden" NAME="recaf" VALUE="IN">
+ <INPUT TYPE="hidden" NAME="rectype" VALUE="_mstr">
+ <INPUT TYPE="text" NAME="recdata">
+ <INPUT TYPE="submit" VALUE="Slave domain" onClick="return slave_areyousure()">
+ </FORM>
+
+% }
+
+<BR><BR>
+
+<% joblisting({'svcnum'=>$svcnum}, 1) %>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_domain = qsearchs({
+ 'select' => 'svc_domain.*',
+ 'table' => 'svc_domain',
+ 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => {'svcnum'=>$svcnum},
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die "Unknown svcnum" unless $svc_domain;
+
+my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum, $display_custnum);
+if ($pkgnum) {
+ $cust_pkg =qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ $custnum = $cust_pkg->custnum;
+ $custnum = $cust_pkg->cust_main->display_custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+
+my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } );
+die "Unknown svcpart" unless $part_svc;
+
+my $email = '';
+if ($svc_domain->catchall) {
+ my $svc_acct = qsearchs('svc_acct',{'svcnum'=> $svc_domain->catchall } );
+ die "Unknown svcpart" unless $svc_acct;
+ $email = $svc_acct->email;
+}
+
+my $domain = $svc_domain->domain;
+
+</%init>
diff --git a/httemplate/view/svc_external.cgi b/httemplate/view/svc_external.cgi
new file mode 100644
index 0000000..222f36a
--- /dev/null
+++ b/httemplate/view/svc_external.cgi
@@ -0,0 +1,63 @@
+<% include("/elements/header.html",'External Service View', menubar(
+ ( ( $custnum )
+ ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ : ( "Cancel this (unaudited) external service" =>
+ "${p}misc/cancel-unaudited.cgi?$svcnum" )
+ ),
+)) %>
+
+<A HREF="<%$p%>edit/svc_external.cgi?<%$svcnum%>">Edit this information</A><BR>
+<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+
+<TR><TD ALIGN="right">Service number</TD>
+ <TD BGCOLOR="#ffffff"><% $svcnum %></TD></TR>
+<TR><TD ALIGN="right"><% FS::Msgcat::_gettext('svc_external-id') || 'External&nbsp;ID' %></TD>
+ <TD BGCOLOR="#ffffff"><% $conf->config('svc_external-display_type') eq 'artera_turbo' ? sprintf('%010d', $svc_external->id) : $svc_external->id %></TD></TR>
+<TR><TD ALIGN="right"><% FS::Msgcat::_gettext('svc_external-title') || 'Title' %></TD>
+ <TD BGCOLOR="#ffffff"><% $svc_external->title %></TD></TR>
+% foreach (sort { $a cmp $b } $svc_external->virtual_fields) {
+
+ <% $svc_external->pvf($_)->widget('HTML', 'view', $svc_external->getfield($_)) %>
+% }
+
+
+</TABLE></TD></TR></TABLE>
+<BR><% joblisting({'svcnum'=>$svcnum}, 1) %>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_external = qsearchs({
+ 'select' => 'svc_external.*',
+ 'table' => 'svc_external',
+ 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $svcnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+}) or die "svc_external: Unknown svcnum $svcnum";
+
+my $conf = new FS::Conf;
+
+#false laziness w/all svc_*.cgi
+my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $svcnum } );
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum, $display_custnum);
+if ($pkgnum) {
+ $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
+ $custnum = $cust_pkg->custnum;
+ $display_custnum = $cust_pkg->cust_main->display_custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+#eofalse
+
+</%init>
diff --git a/httemplate/view/svc_forward.cgi b/httemplate/view/svc_forward.cgi
new file mode 100755
index 0000000..ff84a28
--- /dev/null
+++ b/httemplate/view/svc_forward.cgi
@@ -0,0 +1,105 @@
+<% include('/elements/header.html', 'Mail Forward View', menubar(
+ ( ( $pkgnum || $custnum )
+ ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ : ( "Cancel this (unaudited) mail forward" =>
+ "${p}misc/cancel-unaudited.cgi?$svcnum" )
+ )
+))
+%>
+
+<A HREF="<% $p %>edit/svc_forward.cgi?<% $svcnum %>">Edit this information</A>
+
+<% ntable("#cccccc",2) %>
+
+ <TR>
+ <TD ALIGN="right">Service number</TD>
+ <TD BGCOLOR="#ffffff"><% $svcnum %></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Service</TD>
+ <TD BGCOLOR="#ffffff"><% $svc %></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Email to</TD>
+ <TD BGCOLOR="#ffffff"><% $source %></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Forwards to </TD>
+ <TD BGCOLOR="#ffffff"><% $destination %></TD>
+ </TR>
+
+% foreach (sort { $a cmp $b } $svc_forward->virtual_fields) {
+ <% $svc_forward->pvf($_)->widget('HTML', 'view', $svc_forward->getfield($_)) %>
+% }
+
+</TABLE>
+
+<BR>
+<% joblisting({'svcnum'=>$svcnum}, 1) %>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+my $conf = new FS::Conf;
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_forward = qsearchs({
+ 'select' => 'svc_forward.*',
+ 'table' => 'svc_forward',
+ 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => {'svcnum'=>$svcnum},
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+});
+die "Unknown svcnum" unless $svc_forward;
+
+my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum, $display_custnum);
+if ($pkgnum) {
+ $cust_pkg=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ $custnum=$cust_pkg->getfield('custnum');
+ $display_custnum = $cust_pkg->cust_main->display_custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+
+my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } )
+ or die "Unknown svcpart";
+
+my($srcsvc,$dstsvc,$dst) = (
+ $svc_forward->srcsvc,
+ $svc_forward->dstsvc,
+ $svc_forward->dst,
+);
+my $src = $svc_forward->dbdef_table->column('src') ? $svc_forward->src : '';
+
+my $svc = $part_svc->svc;
+
+my $source;
+if ($srcsvc) {
+ my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$srcsvc})
+ or die "Corrupted database: no svc_acct.svcnum matching srcsvc $srcsvc";
+ $source = $svc_acct->email;
+} else {
+ $source = $src;
+}
+
+my $destination;
+if ($dstsvc) {
+ my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$dstsvc})
+ or die "Corrupted database: no svc_acct.svcnum matching dstsvc $dstsvc";
+ $destination = $svc_acct->email;
+} else {
+ $destination = $dst;
+}
+
+</%init>
diff --git a/httemplate/view/svc_phone.cgi b/httemplate/view/svc_phone.cgi
new file mode 100644
index 0000000..f604daa
--- /dev/null
+++ b/httemplate/view/svc_phone.cgi
@@ -0,0 +1,54 @@
+<% include('elements/svc_Common.html',
+ 'table' => 'svc_phone',
+ 'fields' => [qw(
+ countrycode
+ phonenum
+ sip_password
+ pin
+ phone_name
+ )],
+ 'labels' => {
+ 'countrycode' => 'Country code',
+ 'phonenum' => 'Phone number',
+ 'sip_password' => 'SIP password',
+ 'pin' => 'PIN',
+ 'phone_name' => 'Name',
+ },
+ 'html_foot' => $html_foot,
+ )
+%>
+<%init>
+
+my $html_foot = sub {
+ my $svc_phone = shift;
+
+ tie my %what, 'Tie::IxHash',
+ 'pending' => 'NULL',
+ 'billed' => 'done',
+ ;
+
+ #XXX src & charged party (& default prefix) as per voip_cdr.pm
+ #XXX handle toll free too
+
+ my $number = $svc_phone->phonenum;
+ $number = $svc_phone->countrycode. $number
+ unless $svc_phone->countrycode eq '1';
+
+ #my @links = map {
+ # qq(<A HREF="${p}search/cdr.html?src=$number;freesidestatus=$what{$_}">).
+ # "View $_ CDRs</A>";
+ #} keys(%what);
+ my @links = map {
+ qq(<A HREF="${p}search/cdr.html?cdrbatch=__ALL__;charged_party=$number;freesidestatus=$what{$_}">).
+ "View $_ CDRs</A>";
+ } keys(%what);
+
+ my @ilinks = ( qq(<A HREF="${p}search/cdr.html?dst=$number">).
+ 'View incoming CDRs</A>' );
+
+ join(' | ', @links ). '<BR>'.
+ join(' | ', @ilinks). '<BR>';
+
+};
+
+</%init>
diff --git a/httemplate/view/svc_www.cgi b/httemplate/view/svc_www.cgi
new file mode 100644
index 0000000..cb1a3bb
--- /dev/null
+++ b/httemplate/view/svc_www.cgi
@@ -0,0 +1,104 @@
+<% include("/elements/header.html", "Website View", menubar(
+ ( ( $custnum )
+ ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ : ( "Cancel this (unaudited) website" =>
+ "${p}misc/cancel-unaudited.cgi?$svcnum" )
+ ),
+ ))
+%>
+
+<A HREF="<% $p %>edit/svc_www.cgi?<% $svcnum %>">Edit this information</A><BR>
+
+<% ntable("#cccccc", 2) %>
+
+ <TR>
+ <TD ALIGN="right">Service number</TD>
+ <TD BGCOLOR="#ffffff"><% $svcnum %></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Website name</TD>
+ <TD BGCOLOR="#ffffff"><A HREF="http://<% $www %>"><% $www %></A></TD>
+ </TR>
+
+% if ( $part_svc->part_svc_column('usersvc')->columnflag ne 'F'
+% || $part_svc->part_svc_column('usersvc')->columnvalue !~ /^\s*$/) {
+
+ <TR>
+ <TD ALIGN="right">Account</TD>
+ <TD BGCOLOR="#ffffff">
+% if ( $usersvc ) {
+ <A HREF="<% $p %>view/svc_acct.cgi?<% $usersvc %>"><% $email %></A>
+% } else {
+ </i>(none)</i>
+% }
+ </TD>
+ </TR>
+
+% }
+
+ <TR>
+ <TD ALIGN="right">Config lines</TD>
+ <TD BGCOLOR="#ffffff"><PRE><% join("\n", $svc_www->config) |h %></PRE></TD>
+ </TR>
+
+% foreach (sort { $a cmp $b } $svc_www->virtual_fields) {
+ <% $svc_www->pvf($_)->widget('HTML', 'view', $svc_www->getfield($_)) %>
+% }
+
+</TABLE>
+
+<BR>
+<% joblisting({'svcnum'=>$svcnum}, 1) %>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_www = qsearchs({
+ 'select' => 'svc_www.*',
+ 'table' => 'svc_www',
+ 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $svcnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+}) or die "svc_www: Unknown svcnum $svcnum";
+
+#false laziness w/all svc_*.cgi
+my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $svcnum } );
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum, $display_custnum);
+if ($pkgnum) {
+ $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
+ $custnum = $cust_pkg->custnum;
+ $display_custnum = $cust_pkg->cust_main->display_custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+#eofalse
+
+my $part_svc=qsearchs('part_svc',{'svcpart'=>$cust_svc->svcpart})
+ or die "svc_www: Unknown svcpart" . $cust_svc->svcpart;
+
+my $usersvc = $svc_www->usersvc;
+my $svc_acct = '';
+my $email = '';
+if ( $usersvc ) {
+ $svc_acct = qsearchs('svc_acct', { 'svcnum' => $usersvc } )
+ or die "svc_www: Unknown usersvc $usersvc";
+ $email = $svc_acct->email;
+}
+
+my $domain_record = qsearchs('domain_record', { 'recnum' => $svc_www->recnum } )
+ or die "svc_www: Unknown recnum ". $svc_www->recnum;
+
+my $www = $domain_record->zone;
+
+</%init>
diff --git a/init.d/freeside-init b/init.d/freeside-init
new file mode 100644
index 0000000..c9bcebe
--- /dev/null
+++ b/init.d/freeside-init
@@ -0,0 +1,110 @@
+#!/bin/sh
+#
+# chkconfig: 345 86 16
+# description: Freeside daemons
+
+QUEUED_USER=%%%QUEUED_USER%%%
+
+SELFSERVICE_USER=%%%SELFSERVICE_USER%%%
+SELFSERVICE_MACHINES="%%%SELFSERVICE_MACHINES%%%"
+
+#INSTALLSCRIPT/INSTALLSITEBIN from Makefile.PL
+PATH="$PATH:/usr/local/bin"
+export PATH
+
+[ -r /etc/default/freeside ] && . /etc/default/freeside
+
+case "$1" in
+ start)
+ # Start daemons.
+ echo -n "Starting freeside-queued: "
+ freeside-queued $QUEUED_USER
+ echo "done."
+
+ echo -n "Starting freeside-sqlradius-radacctd: "
+ freeside-sqlradius-radacctd $QUEUED_USER
+ echo "done."
+
+ echo -n "Starting freeside-prepaidd: "
+ freeside-prepaidd $QUEUED_USER
+ echo "done."
+
+ echo -n "Starting freeside-cdrrewrited: "
+ freeside-cdrrewrited $QUEUED_USER
+ echo "done."
+
+ echo -n "Starting freeside-cdrd: "
+ freeside-cdrd $QUEUED_USER
+ echo "done."
+
+ for MACHINE in $SELFSERVICE_MACHINES; do
+ echo -n "Starting freeside-selfservice-server to $MACHINE: "
+ freeside-selfservice-server $SELFSERVICE_USER $MACHINE
+ echo "done."
+ done
+
+ ;;
+ stop)
+ # Stop daemons.
+ echo -n "Stopping freeside-queued: "
+ [ -e /var/run/freeside-queued.pid ] && kill `cat /var/run/freeside-queued.pid`
+ #and
+ sleep 2;
+ killall freeside-queued
+ echo "done."
+
+ if [ -e /var/run/freeside-sqlradius-radacctd.pid ]; then
+ echo -n "Stopping freeside-sqlradius-radacctd: "
+ kill `cat /var/run/freeside-sqlradius-radacctd.pid`
+ echo "done."
+ fi
+
+ if [ -e /var/run/freeside-prepaidd.pid ]; then
+ echo -n "Stopping freeside-prepaidd: "
+ kill `cat /var/run/freeside-prepaidd.pid`
+ echo "done."
+ fi
+
+ if [ -e /var/run/freeside-cdrd.pid ]; then
+ echo -n "Stopping freeside-cdrd: "
+ kill `cat /var/run/freeside-cdrd.pid`
+ echo "done."
+ fi
+
+ if [ -e /var/run/freeside-cdrrewrited.pid ]; then
+ echo -n "Stopping freeside-cdrrewrited: "
+ kill `cat /var/run/freeside-cdrrewrited.pid`
+ echo "done."
+ fi
+
+ if [ -e /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid ]
+ then
+ echo -n "Stopping (old) freeside-selfservice-server: "
+ kill `cat /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid`
+ rm /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid
+ echo "done."
+ fi
+
+ if [ -z "$SELFSERVICE_MACHINES" ]; then SELFSERVICE_MACHINES='localhost'; fi
+ for MACHINE in $SELFSERVICE_MACHINES; do
+ if [ -e /var/run/freeside-selfservice-server.$SELFSERVICE_USER.$MACHINE.pid ]
+ then
+ echo -n "Stopping freeside-selfservice-server to $MACHINE: "
+ kill `cat /var/run/freeside-selfservice-server.$SELFSERVICE_USER.$MACHINE.pid`
+ echo "done."
+ fi
+ done
+
+ ;;
+
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "Usage: freeside {start|stop|restart}"
+ exit 1
+esac
+
+exit 0
+
diff --git a/rpm/INSTALL b/rpm/INSTALL
new file mode 100644
index 0000000..d39bf70
--- /dev/null
+++ b/rpm/INSTALL
@@ -0,0 +1,4 @@
+See httemplates/docs/install-rpm.html and documentation on the wiki.
+
+This directory contains files that are RPM-specific and are referenced by the spec file during a build of the Freeside RPMs.
+
diff --git a/rpm/freeside-selfservice.conf b/rpm/freeside-selfservice.conf
new file mode 100644
index 0000000..f2c103b
--- /dev/null
+++ b/rpm/freeside-selfservice.conf
@@ -0,0 +1,11 @@
+ScriptAlias /selfservice %%%FREESIDE_SELFSERVICE_DOCUMENT_ROOT%%%/cgi
+
+<Directory %%%FREESIDE_SELFSERVICE_DOCUMENT_ROOT%%%/cgi>
+SSLRequireSSL
+DirectoryIndex selfservice.cgi
+AllowOverride None
+Options +ExecCGI -Includes
+Order deny,allow
+Allow from all
+SetHandler cgi-script
+</Directory>
diff --git a/rpm/freeside.spec b/rpm/freeside.spec
new file mode 100644
index 0000000..77b5061
--- /dev/null
+++ b/rpm/freeside.spec
@@ -0,0 +1,459 @@
+%{!?_initrddir:%define _initrddir /etc/rc.d/init.d}
+%{!?version:%define version 1.9}
+%{!?release:%define release 6}
+
+Summary: Freeside ISP Billing System
+Name: freeside
+Version: %{version}
+Release: %{release}
+License: AGPLv3
+Group: Applications/Internet
+URL: http://www.sisd.com/freeside/
+Vendor: Freeside
+Source: http://www.sisd.com/freeside/%{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+BuildArch: noarch
+Requires: %{name}-frontend
+Requires: %{name}-backend
+%if "%{_vendor}" != "suse"
+Requires: tetex-latex
+%else
+Requires: te_latex
+%endif
+Requires: perl-Fax-Hylafax-Client
+
+%if "%{_vendor}" != "suse"
+%define apache_conffile /etc/httpd/conf/httpd.conf
+%define apache_confdir /etc/httpd/conf.d
+%define apache_version 2
+%define freeside_document_root /var/www/freeside
+%define freeside_selfservice_document_root /var/www/freeside-selfservice
+%else
+%define apache_conffile /etc/apache2/uid.conf
+%define apache_confdir /etc/apache2/conf.d
+%define apache_version 2
+%define freeside_document_root /srv/www/freeside
+%define freeside_selfservice_document_root /srv/www/freeside-selfservice
+%endif
+# Can change this back to /var/cache/subsys/freeside when cache relocation is fixed and released
+%define freeside_cache /etc/freeside
+%define freeside_conf /etc/freeside
+%define freeside_export /etc/freeside
+%define freeside_lock /var/lock/freeside
+%define freeside_log /var/log/freeside
+%define freeside_socket /etc/freeside
+%define rt_enabled 0
+%define fs_queue_user fs_queue
+%define fs_selfservice_user fs_selfservice
+%define fs_cron_user fs_daily
+%define db_types Pg mysql
+
+%define _rpmlibdir /usr/lib/rpm
+%define rpmfiles rpm
+
+%description
+Freeside is a flexible ISP billing system written by Ivan Kohler
+
+%package mason
+Summary: HTML::Mason interface for %{name}
+Group: Applications/Internet
+Prefix: %{freeside_document_root}
+%if "%{_vendor}" != "suse"
+Requires: mod_ssl
+%endif
+Requires: perl-Apache-DBI
+Provides: %{name}-frontend = %{version}
+BuildArch: noarch
+
+%description mason
+This package includes the HTML::Mason web interface for %{name}.
+You should install only one %{name} web interface.
+
+%package postgresql
+Summary: PostgreSQL backend for %{name}
+Group: Applications/Internet
+Requires: perl-DBI
+Requires: perl-DBD-Pg >= 1.32
+Requires: %{name}
+Conflicts: %{name}-mysql
+Provides: %{name}-backend = %{version}
+
+%description postgresql
+This package includes the PostgreSQL database backend for %{name}.
+You should install only one %{name} database backend.
+Please note that this RPM does not create the database or database user; it only installs the required drivers.
+
+%package mysql
+Summary: MySQL database backend for %{name}
+Group: Applications/Internet
+Requires: perl-DBI
+Requires: perl-DBD-MySQL
+Requires: %{name}
+Conflicts: %{name}-postgresql
+Provides: %{name}-backend = %{version}
+
+%description mysql
+This package includes the MySQL database backend for %{name}.
+You should install only one %{name} database backend.
+Please note that this RPM does not create the database or database user; it only installs the required drivers.
+
+%package selfservice
+Summary: Self-service interface for %{name}
+Group: Applications/Internet
+Requires: %{name}-selfservice-cgi
+
+%description selfservice
+This package installs the Perl modules and CGI scripts for the self-service interface for %{name}.
+For security reasons, it is set to conflict with %{name} as you should not install the billing system and self-service interface on the same computer.
+
+%package selfservice-core
+Summary: Core Perl libraries for the self-service interface for %{name}
+Group: Applications/Internet
+
+%description selfservice-core
+This package installs the Perl modules and client daemon for the self-service interface for %{name}. It does not install the CGI interface and can be used with a different front-end.
+For security reasons, it is set to conflict with %{name} as you should not install the billing system and self-service interface on the same computer.
+
+%package selfservice-cgi
+Summary: CGI scripts for the self-service interface for %{name}
+Group: Applications/Internet
+Requires: %{name}-selfservice-core
+Prefix: %{freeside_selfservice_document_root}
+
+%description selfservice-cgi
+This package installs the CGI scripts for the self-service interface for %{name}. The scripts use some core libraries packaged in a separate RPM.
+For security reasons, it is set to conflict with %{name} as you should not install the billing system and self-service interface on the same computer.
+
+%package selfservice-php
+Summary: Sample PHP files for the self-service interface for %{name}
+Group: Applications/Internet
+Prefix: %{freeside_selfservice_document_root}
+
+%description selfservice-php
+This package installs the sample PHP scripts for the self-service interface for %{name}.
+For security reasons, it is set to conflict with %{name} as you should not install the billing system and self-service interface on the same computer.
+
+%prep
+%setup -q
+%{__rm} bin/pod2x # Only useful to Ivan Kohler now
+perl -pi -e 's|/usr/local/bin|%{_bindir}|g' FS/Makefile.PL
+# RPM handles changing file ownership, so Makefile shouldn't
+perl -pi -e 's/\s+-o\s+(freeside|root)(\s+-g\s+\$\{\w+\})?\s+/ /g' Makefile
+perl -ni -e 'print if !/\s+chown\s+/;' Makefile
+
+# Fix-ups for self-service. Should merge this into Makefile
+perl -pi -e 's|/usr/local/sbin|%{_sbindir}|g' FS/bin/freeside-selfservice-server
+perl -pi -e 's|/usr/local/bin|%{_bindir}|g' fs_selfservice/FS-SelfService/Makefile.PL
+perl -pi -e 's|/usr/local/freeside|%{freeside_socket}|g' fs_selfservice/FS-SelfService/*.pm
+perl -pi -e 's|socket\s*=\s*"/usr/local/freeside|socket = "%{freeside_socket}|g' fs_selfservice/FS-SelfService/freeside-selfservice-*
+perl -pi -e 's|log_file\s*=\s*"/usr/local/freeside|log_file = "%{freeside_log}|g' fs_selfservice/FS-SelfService/freeside-selfservice-*
+perl -pi -e 's|lock_file\s*=\s*"/usr/local/freeside|lock_file = "%{freeside_lock}|g' fs_selfservice/FS-SelfService/freeside-selfservice-*
+
+# Fix-ups for SuSE
+%if "%{_vendor}" == "suse"
+perl -pi -e 's|htpasswd|/usr/sbin/htpasswd2|g if /system/;' FS/FS/access_user.pm
+perl -pi -e 'print "Order deny,allow\nAllow from all\n" if /<Files/i;' htetc/freeside*.conf
+%endif
+
+# Override find-requires/find-provides to supplement Perl requires for HTML::Mason file handler.pl
+cat << \EOF > %{name}-req
+#!/bin/sh
+tee %{_tmppath}/filelist | %{_rpmlibdir}/rpmdeps --requires | grep -v -E '^perl\(the\)$' \
+| grep -v -E '^perl\((lib|strict|vars|RT)\)$' \
+| grep -v -E '^perl\(RT::' \
+| sort -u
+grep handler.pl %{_tmppath}/filelist | xargs %{_rpmlibdir}/perldeps.pl --requires \
+| grep -v -E '^perl\((lib|strict|vars|RT)\)$' \
+| grep -v -E '^perl\(RT::' \
+| sort -u
+EOF
+
+%define __find_provides %{_rpmlibdir}/rpmdeps --provides
+%define __find_requires %{_builddir}/%{name}-%{version}/%{name}-req
+%{__chmod} +x %{__find_requires}
+%define _use_internal_dependency_generator 0
+
+%build
+
+# False laziness...
+# The htmlman target now makes wiki documentation. Let's pretend we made it.
+touch htmlman
+%{__make} alldocs
+
+#perl -pi -e 's|%%%%%%VERSION%%%%%%|%{version}|g' FS/bin/*
+cd FS
+if [ "%{_vendor}" = "suse" ]; then
+ CFLAGS="$RPM_OPT_FLAGS" perl Makefile.PL
+else
+ CFLAGS="$RPM_OPT_FLAGS" perl Makefile.PL PREFIX=$RPM_BUILD_ROOT%{_prefix} SITELIBEXP=$RPM_BUILD_ROOT%{perl_sitelib} SITEARCHEXP=$RPM_BUILD_ROOT%{perl_sitearch} INSTALLSCRIPT=$RPM_BUILD_ROOT%{_bindir}
+fi
+%{__make} OPTIMIZE="$RPM_OPT_FLAGS"
+cd ..
+%{__make} perl-modules VERSION='%{version}-%{release}' RT_ENABLED=%{rt_enabled} FREESIDE_CACHE=%{freeside_cache} FREESIDE_CONF=%{freeside_conf} FREESIDE_EXPORT=%{freeside_export} FREESIDE_LOCK=%{freeside_lock} FREESIDE_LOG=%{freeside_log}
+touch perl-modules
+
+cd fs_selfservice/FS-SelfService
+if [ "%{_vendor}" = "suse" ]; then
+ CFLAGS="$RPM_OPT_FLAGS" perl Makefile.PL
+else
+ CFLAGS="$RPM_OPT_FLAGS" perl Makefile.PL PREFIX=$RPM_BUILD_ROOT%{_prefix} SITELIBEXP=$RPM_BUILD_ROOT%{perl_sitelib} SITEARCHEXP=$RPM_BUILD_ROOT%{perl_sitearch} INSTALLSCRIPT=$RPM_BUILD_ROOT%{_sbindir}
+fi
+%{__make} OPTIMIZE="$RPM_OPT_FLAGS"
+cd ../..
+
+%install
+%{__rm} -rf %{buildroot}
+
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_document_root}
+
+touch install-perl-modules perl-modules
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_cache}
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_conf}
+#%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_export}
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_lock}
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_log}
+for DBTYPE in %{db_types}; do
+ %{__mkdir_p} $RPM_BUILD_ROOT/tmp
+ [ -d $RPM_BUILD_ROOT%{freeside_conf}/default_conf ] && %{__rm} -rf $RPM_BUILD_ROOT%{freeside_conf}/default_conf
+ %{__make} create-config DB_TYPE=$DBTYPE DATASOURCE=DBI:$DBTYPE:dbname=%{name} RT_ENABLED=%{rt_enabled} FREESIDE_CACHE=$RPM_BUILD_ROOT%{freeside_cache} FREESIDE_CONF=$RPM_BUILD_ROOT/tmp FREESIDE_EXPORT=$RPM_BUILD_ROOT%{freeside_export} FREESIDE_LOCK=$RPM_BUILD_ROOT%{freeside_lock} FREESIDE_LOG=$RPM_BUILD_ROOT%{freeside_log}
+ %{__mv} $RPM_BUILD_ROOT/tmp/* $RPM_BUILD_ROOT%{freeside_conf}
+ /bin/rmdir $RPM_BUILD_ROOT/tmp
+done
+%{__rm} install-perl-modules perl-modules $RPM_BUILD_ROOT%{freeside_conf}/conf*/ticket_system
+
+touch docs
+%{__perl} -pi -e "s|%%%%%%FREESIDE_DOCUMENT_ROOT%%%%%%|%{freeside_document_root}|g" htetc/handler.pl
+%{__make} install-docs RT_ENABLED=%{rt_enabled} PREFIX=$RPM_BUILD_ROOT%{_prefix} TEMPLATE=mason FREESIDE_DOCUMENT_ROOT=$RPM_BUILD_ROOT%{freeside_document_root} MASON_HANDLER=$RPM_BUILD_ROOT%{freeside_conf}/handler.pl MASONDATA=$RPM_BUILD_ROOT%{freeside_cache}/masondata
+%{__perl} -pi -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{freeside_conf}/handler.pl
+%{__rm} docs
+
+# Install the init script
+%{__mkdir_p} $RPM_BUILD_ROOT%{_initrddir}
+%{__make} install-init INSTALLGROUP=root INIT_FILE=$RPM_BUILD_ROOT%{_initrddir}/%{name} QUEUED_USER=%{fs_queue_user} SELFSERVICE_USER=%{fs_selfservice_user} SELFSERVICE_MACHINES= INIT_INSTALL=
+%{__perl} -pi -e "\
+ s|/etc/default|/etc/sysconfig|g;\
+ " $RPM_BUILD_ROOT%{_initrddir}/%{name}
+
+# Install the HTTPD configuration snippet for HTML::Mason
+%{__mkdir_p} $RPM_BUILD_ROOT%{apache_confdir}
+%{__make} install-apache FREESIDE_DOCUMENT_ROOT=%{freeside_document_root} RT_ENABLED=%{rt_enabled} APACHE_CONF=$RPM_BUILD_ROOT%{apache_confdir} APACHE_VERSION=%{apache_version} FREESIDE_CONF=%{freeside_conf} MASON_HANDLER=%{freeside_conf}/handler.pl
+%{__perl} -pi -e 'print "Alias /%{name} %{freeside_document_root}\n\n" if /^<Directory/;' $RPM_BUILD_ROOT%{apache_confdir}/freeside-*.conf
+%{__perl} -pi -e 'print "SSLRequireSSL\n" if /^AuthName/i;' $RPM_BUILD_ROOT%{apache_confdir}/freeside-*.conf
+
+# Make lists of the database-specific configuration files
+for DBTYPE in %{db_types}; do
+ echo "%%attr(600,freeside,freeside) %{freeside_conf}/secrets" > %{name}-%{version}-%{release}-$DBTYPE-filelist
+ for DIR in `echo -e "%{freeside_conf}\n%{freeside_cache}\n%{freeside_export}\n" | sort | uniq`; do
+ find $RPM_BUILD_ROOT$DIR -type f -print | \
+ grep ":$DBTYPE:" | \
+ sed "s@^$RPM_BUILD_ROOT@%%attr(640,freeside,freeside) %%config(noreplace) @g" >> %{name}-%{version}-%{release}-$DBTYPE-filelist
+ find $RPM_BUILD_ROOT$DIR -type d -print | \
+ grep ":$DBTYPE:" | \
+ sed "s@^$RPM_BUILD_ROOT@%%attr(711,freeside,freeside) %%dir @g" >> %{name}-%{version}-%{release}-$DBTYPE-filelist
+ done
+ if [ "$(cat %{name}-%{version}-%{release}-$DBTYPE-filelist)X" = "X" ] ; then
+ echo "ERROR: EMPTY FILE LIST"
+ exit 1
+ fi
+done
+
+# Make a list of the Mason files before adding self-service, etc.
+echo "%attr(-,freeside,freeside) %{freeside_conf}/handler.pl" > %{name}-%{version}-%{release}-mason-filelist
+find $RPM_BUILD_ROOT%{freeside_document_root} -type f -print | \
+ sed "s@^$RPM_BUILD_ROOT@@g" >> %{name}-%{version}-%{release}-mason-filelist
+if [ "$(cat %{name}-%{version}-%{release}-mason-filelist)X" = "X" ] ; then
+ echo "ERROR: EMPTY FILE LIST"
+ exit 1
+fi
+
+# Install all the miscellaneous binaries into /usr/share or similar
+%{__mkdir_p} $RPM_BUILD_ROOT%{_datadir}/%{name}-%{version}/bin
+%{__install} bin/* $RPM_BUILD_ROOT%{_datadir}/%{name}-%{version}/bin
+
+%{__mkdir_p} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig
+%{__install} %{rpmfiles}/freeside.sysconfig $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/%{name}
+
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_selfservice_document_root}
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/cgi
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/cgi/images
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/cgi/misc
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/php
+%{__mkdir_p} $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/templates
+%{__install} fs_selfservice/FS-SelfService/cgi/{*.cgi,*.html,*.gif} $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/cgi
+%{__install} fs_selfservice/FS-SelfService/cgi/images/* $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/cgi/images
+%{__install} fs_selfservice/FS-SelfService/cgi/misc/* $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/cgi/misc
+%{__install} fs_selfservice/php/* $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/php
+%{__install} fs_selfservice/FS-SelfService/*.template $RPM_BUILD_ROOT%{freeside_selfservice_document_root}/templates
+
+# Install the main billing server Perl files
+cd FS
+eval `perl '-V:installarchlib'`
+%{__mkdir_p} $RPM_BUILD_ROOT$installarchlib
+%makeinstall PREFIX=$RPM_BUILD_ROOT%{_prefix}
+%{__rm} -f `find $RPM_BUILD_ROOT -type f -name perllocal.pod -o -name .packlist`
+
+[ -x %{_rpmlibdir}/brp-compress ] && %{_rpmlibdir}/brp-compress
+
+find $RPM_BUILD_ROOT%{_prefix} -type f -print | \
+ grep -v '/etc/freeside/conf' | \
+ grep -v '/etc/freeside/secrets' | \
+ sed "s@^$RPM_BUILD_ROOT@@g" > %{name}-%{version}-%{release}-filelist
+if [ "$(cat %{name}-%{version}-%{release}-filelist)X" = "X" ] ; then
+ echo "ERROR: EMPTY FILE LIST"
+ exit 1
+fi
+cd ..
+
+# Install the self-service interface Perl files
+cd fs_selfservice/FS-SelfService
+%{__mkdir_p} $RPM_BUILD_ROOT%{_prefix}/local/bin
+%makeinstall
+%{__rm} -f `find $RPM_BUILD_ROOT -type f -name perllocal.pod -o -name .packlist`
+
+[ -x %{_rpmlibdir}/brp-compress ] && %{_rpmlibdir}/brp-compress
+
+find $RPM_BUILD_ROOT%{_prefix} -type f -print | \
+ grep -v '/etc/freeside/conf' | \
+ grep -v '/etc/freeside/secrets' | \
+ sed "s@^$RPM_BUILD_ROOT@@g" > %{name}-%{version}-%{release}-temp-filelist
+cat ../../FS/%{name}-%{version}-%{release}-filelist %{name}-%{version}-%{release}-temp-filelist | sort | uniq -u > %{name}-%{version}-%{release}-selfservice-core-filelist
+if [ "$(cat %{name}-%{version}-%{release}-selfservice-core-filelist)X" = "X" ] ; then
+ echo "ERROR: EMPTY FILE LIST"
+ exit 1
+fi
+cd ../..
+
+# Install the Apache configuration file for self-service
+%{__install} %{rpmfiles}/freeside-selfservice.conf $RPM_BUILD_ROOT%{apache_confdir}/%{name}-selfservice.conf
+%{__perl} -pi -e "s|%%%%%%FREESIDE_SELFSERVICE_DOCUMENT_ROOT%%%%%%|%{freeside_selfservice_document_root}|g" $RPM_BUILD_ROOT%{apache_confdir}/%{name}-selfservice.conf
+
+%pre
+if ! %{__id} freeside &>/dev/null; then
+ /usr/sbin/useradd freeside
+fi
+
+%pre mason
+if ! %{__id} freeside &>/dev/null; then
+ /usr/sbin/useradd freeside
+fi
+
+%pre postgresql
+if ! %{__id} freeside &>/dev/null; then
+ /usr/sbin/useradd freeside
+fi
+
+%pre mysql
+if ! %{__id} freeside &>/dev/null; then
+ /usr/sbin/useradd freeside
+fi
+
+%pre selfservice-cgi
+if ! %{__id} freeside &>/dev/null; then
+ /usr/sbin/useradd freeside
+fi
+
+%post
+if [ -x /sbin/chkconfig ]; then
+ /sbin/chkconfig --add freeside
+fi
+#if [ $1 -eq 2 -a -x /usr/bin/freeside-upgrade ]; then
+# /usr/bin/freeside-upgrade
+#fi
+
+%post postgresql
+if [ -f %{freeside_conf}/secrets ]; then
+ perl -p -i.fsbackup -e 's/^DBI:.*?:/DBI:Pg:/' %{freeside_conf}/secrets
+fi
+
+%post mysql
+if [ -f %{freeside_conf}/secrets ]; then
+ perl -p -i.fsbackup -e 's/^DBI:.*?:/DBI:mysql:/' %{freeside_conf}/secrets
+fi
+
+%post mason
+# Make local httpd run with User/Group = freeside
+if [ -f %{apache_conffile} ]; then
+%if "%{_vendor}" != "suse"
+ perl -p -i.fsbackup -e 's/^(User|Group) .*/$1 freeside/' %{apache_conffile}
+%else
+ perl -p -i.fsbackup -e 's/^(User) .*/$1 freeside/' %{apache_conffile}
+%endif
+fi
+# Fix up environment so pslatex will run
+%if "%{_vendor}" == "suse"
+if ! %{__grep} TEXINPUTS /etc/profile.local >/dev/null; then
+ echo "unset TEXINPUTS" >>/etc/profile.local
+fi
+if ! %{__grep} TEXINPUTS /etc/init.d/apache2 >/dev/null; then
+ perl -p -i.fsbackup -e 'print "unset TEXINPUTS\n\n" if /^httpd_conf\s*=\s*/;' /etc/init.d/apache2
+fi
+%endif
+
+%clean
+%{__rm} -rf %{buildroot}
+
+%files -f FS/%{name}-%{version}-%{release}-filelist
+%attr(0711,root,root) %{_initrddir}/%{name}
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/sysconfig/%{name}
+%defattr(-,freeside,freeside,-)
+%doc README INSTALL CREDITS AGPL
+%attr(-,freeside,freeside) %dir %{freeside_conf}
+%attr(-,freeside,freeside) %dir %{freeside_lock}
+%attr(-,freeside,freeside) %dir %{freeside_log}
+%attr(0644,freeside,freeside) %config(noreplace) %{freeside_conf}/default_conf
+
+%files mason -f %{name}-%{version}-%{release}-mason-filelist
+%defattr(-, freeside, freeside, 0755)
+%attr(-,freeside,freeside) %{freeside_cache}/masondata
+%attr(0644,root,root) %config(noreplace) %{apache_confdir}/%{name}-base%{apache_version}.conf
+
+%files postgresql -f %{name}-%{version}-%{release}-Pg-filelist
+
+%files mysql -f %{name}-%{version}-%{release}-mysql-filelist
+
+%files selfservice
+%defattr(-, freeside, freeside, 0644)
+%attr(0644,root,root) %config(noreplace) %{apache_confdir}/%{name}-selfservice.conf
+
+%files selfservice-core -f fs_selfservice/FS-SelfService/%{name}-%{version}-%{release}-selfservice-core-filelist
+%defattr(-, freeside, freeside, 0644)
+%attr(-,freeside,freeside) %dir %{freeside_socket}
+%attr(-,freeside,freeside) %dir %{freeside_lock}
+%attr(-,freeside,freeside) %dir %{freeside_log}
+
+%files selfservice-cgi
+%defattr(-, freeside, freeside, 0644)
+%attr(0711,freeside,freeside) %{freeside_selfservice_document_root}/cgi
+%attr(0644,freeside,freeside) %{freeside_selfservice_document_root}/templates
+
+%files selfservice-php
+%defattr(-, freeside, freeside, 0644)
+%attr(0755,freeside,freeside) %{freeside_selfservice_document_root}/php
+
+%changelog
+* Mon Dec 22 2008 Richard Siddall <richard.siddall@elirion.net> - 1.9-5
+- Modifications to make self-service work if you really insist on installing it on the same machine as Freeside
+
+* Tue Dec 9 2008 Richard Siddall <richard.siddall@elirion.net> - 1.9-4
+- Cleaning up after rpmlint
+
+* Tue Aug 26 2008 Richard Siddall <richard.siddall@elirion.net> - 1.9-3
+- More revisions for self-service interface
+
+* Sat Aug 23 2008 Richard Siddall <richard.siddall@elirion.net> - 1.7.3-2
+- Revisions for self-service interface
+- RT support is still missing
+
+* Sun Jul 8 2007 Richard Siddall <richard.siddall@elirion.net> - 1.7.3
+- Updated for upcoming Freeside 1.7.3
+- RT support is still missing
+
+* Fri Jun 29 2007 Richard Siddall <richard.siddall@elirion.net> - 1.7.2
+- Updated for Freeside 1.7.2
+- Removed support for Apache::ASP
+
+* Wed Oct 12 2005 Richard Siddall <richard.siddall@elirion.net> - 1.5.7
+- Added self-service package
+
+* Sun Feb 06 2005 Richard Siddall <richard.siddall@elirion.net> - 1.5.0pre6-1
+- Initial package
diff --git a/rpm/freeside.sysconfig b/rpm/freeside.sysconfig
new file mode 100644
index 0000000..baa0462
--- /dev/null
+++ b/rpm/freeside.sysconfig
@@ -0,0 +1,5 @@
+QUEUED_USER=fs_queue
+#RADACCTD_USER=
+#
+SELFSERVICE_USER=fs_selfservice
+#SELFSERVICE_MACHINES=
diff --git a/rpm/rpm2Bundle b/rpm/rpm2Bundle
new file mode 100755
index 0000000..1bc8771
--- /dev/null
+++ b/rpm/rpm2Bundle
@@ -0,0 +1,111 @@
+#!/usr/bin/perl -Tw
+#
+# Make a bundle file from an RPM
+#
+use strict;
+
+$ENV{PATH} = '/bin:/usr/bin/';
+
+my $verbose = 0;
+
+# These are Perl dependencies that should be ignored/suppressed
+my %suppress;
+
+foreach (qw/strict subs vars base lib warnings FS/) {
+ $suppress{$_} = $_;
+}
+
+# These are Perl modules corresponding to RPM names.
+# Add entries when the mapping isn't simply "remove leading 'perl-' and replace - with ::"
+my %rpm2mod=(
+ 'DBD-MySQL' => 'DBD::mysql',
+);
+
+## These are root packages that shouldn't be cited multiple times
+## Should figure this out with CPAN
+#my %rootpkgs;
+#
+#foreach (qw/FS/) {
+# $rootpkgs{$_} = 1;
+#}
+
+foreach my $rawrpm (@ARGV) {
+ $rawrpm =~ /^([-\.a-z0-9\/]+)\s*$/i;
+ my $rpm = $1 or next;
+ my @parts = split '/', $rpm;
+ my $name = pop @parts;
+ my $version = 0.01;
+ if ($name =~ m<([^/]+?)[-._]?v?-?([-_.\d]+[a-z]*?\d*)\.\w+\.rpm$>) {
+ $name = $1;
+ $version = $2;
+ }
+ print STDERR "rpm: $rpm ($name, $version)\n";
+ my @deps = sort `rpm -qp --requires $rpm`;
+
+ my %mods;
+
+ foreach (@deps) {
+ if (/^perl\((.*?)\)\s*((>=|=|<=)\s+([\d\.]+))?$/
+ || /^perl-(.*?)\s*((>=|=|<=)\s+([\d\.]+))?$/) {
+ my ($mod, $rel, $ver) = ($1, $3, $4);
+ if (/^perl-/) {
+ print STDERR "\"$mod\"\n" if $verbose;
+ $mod = $rpm2mod{$mod} if exists($rpm2mod{$mod});
+ $mod =~ s/-/::/g
+ }
+ next if exists($suppress{$mod});
+ my @parts = split /::/, $mod;
+ if (scalar @parts > 1) {
+ next if exists($suppress{$parts[0]});
+ }
+ if ($verbose) {
+ print STDERR "$mod";
+ print STDERR " $rel $ver" if $ver;
+ print STDERR "\n";
+ }
+ $mods{$mod} = $ver ? $ver : undef; # Should also save $rel
+ }
+ }
+
+ my $hdr =<<END;
+# -*- perl -*-
+
+package Bundle::$name;
+
+\$VERSION = '$version';
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bundle::$name - A bundle to install prerequisites for the $name package
+
+=head1 SYNOPSIS
+
+C<perl -MCPAN -e 'install Bundle::$name'>
+
+=head1 CONTENTS
+
+END
+
+ my $ftr =<<END;
+=head1 DESCRIPTION
+
+This bundle includes all prerequisites needed by the $name package.
+
+=cut
+END
+
+ print $hdr;
+ foreach (sort keys %mods) {
+ print "$_";
+ print " $mods{$_}" if exists($mods{$_}) && $mods{$_};
+ print " -\n\n";
+ }
+ print $ftr;
+}
+
+1;
+
diff --git a/GPL b/rt/COPYING
index e77696a..e77696a 100644..100755
--- a/GPL
+++ b/rt/COPYING
diff --git a/rt/Changelog b/rt/Changelog
new file mode 100644
index 0000000..1ffc374
--- /dev/null
+++ b/rt/Changelog
@@ -0,0 +1,23227 @@
+------------------------------------------------------------------------
+r10425 | jesse | 2008-01-22 11:49:13 -0500 (Tue, 22 Jan 2008) | 1 line
+Changed paths:
+ A /rt/tags/3.6.6 (from /rt/branches/3.6-RELEASE:10424)
+
+Tagged as 3.6.6 by svn RelEng 1.0
+------------------------------------------------------------------------
+r10424 | jesse | 2008-01-22 11:45:37 -0500 (Tue, 22 Jan 2008) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r75532@pinglin: jesse | 2008-01-22 11:43:46 -0500
+ 3.6.6 final
+
+------------------------------------------------------------------------
+r10352 | jesse | 2008-01-15 10:22:58 -0500 (Tue, 15 Jan 2008) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r75058@pinglin: jesse | 2008-01-15 10:22:51 -0500
+ * bump to rc3
+
+------------------------------------------------------------------------
+r10351 | jesse | 2008-01-15 10:18:19 -0500 (Tue, 15 Jan 2008) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+ r75056@pinglin: jesse | 2008-01-15 10:17:55 -0500
+ * Fixed an issue where RT would flub the content-type on mail messages containing non-ascii characters
+ (Bug introduced after 3.6.5 and reported by Sven Sternberger)
+
+------------------------------------------------------------------------
+r10231 | jesse | 2008-01-03 09:47:40 -0500 (Thu, 03 Jan 2008) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+ r74540@pinglin: jesse | 2008-02-11 15:31:54 +0000
+ SetupSessionCookie - enable "Transaction" property for better support on
+ Oracle or File sessions (From Alexandr Ciornii)
+
+------------------------------------------------------------------------
+r10183 | jesse | 2007-12-29 15:47:12 -0500 (Sat, 29 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+3.6.6rc2
+
+
+------------------------------------------------------------------------
+r10129 | jesse | 2007-12-26 16:01:11 -0500 (Wed, 26 Dec 2007) | 13 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r74208@pinglin: jesse | 2007-12-26 16:00:34 -0500
+ RT-Ticket: 8781
+ RT-Status: resolved
+ RT-Update: correspond
+
+ I've encountered a problem with sender identification in the mailgate. If
+ a user sends an e-mail where 'Reply-To' is present but empty (i.e there is
+ 'Reply-To: ' in the e-mail headers), RT does not associate the e-mail
+ with a RT user even if the user exists in the system and the sender
+ address is specified in the 'Sender' or 'From' fields.
+ - Patch from ondrasej@centrum.cz
+
+
+------------------------------------------------------------------------
+r9963 | jesse | 2007-12-15 02:11:28 -0500 (Sat, 15 Dec 2007) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+
+ r73110@pinglin: jesse | 2007-12-14 13:54:52 -0500
+ * Fix for a copy and paste bug when RT's config file was unreadable.
+ Thanks to Maxime Henrion <mux@freebsd.org>
+
+
+------------------------------------------------------------------------
+r9962 | jesse | 2007-12-15 02:11:20 -0500 (Sat, 15 Dec 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/index.html
+ M /rt/branches/3.6-RELEASE/html/Approvals/index.html
+ M /rt/branches/3.6-RELEASE/html/Search/Results.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Offline.html
+
+ r72962@pinglin: jesse | 2007-12-10 15:05:42 -0500
+ * Canonicalize a bunch of "Go" buttons to "Go!"
+ * Switched the approvals button from "Show Approvals" to "Go"
+
+------------------------------------------------------------------------
+r9961 | jesse | 2007-12-15 02:11:14 -0500 (Sat, 15 Dec 2007) | 7 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+ M /rt/branches/3.6-RELEASE/lib/t/regression/06-mime_decoding.t
+
+ r72961@pinglin: jesse | 2007-12-10 14:58:08 -0500
+ RT-Ticket: 8710
+ RT-Status: resolved
+ RT-Update correspond
+
+ * Attached patch fixes a bug in decoding content-type headers that contain encoded file names.
+
+------------------------------------------------------------------------
+r9960 | jesse | 2007-12-15 02:10:50 -0500 (Sat, 15 Dec 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+
+ r72958@pinglin: jesse | 2007-12-10 13:57:04 -0500
+ * Switch the REST interface to always show ticket times in minutes, rather than lose data
+
+
+------------------------------------------------------------------------
+r9923 | ruz | 2007-12-12 15:32:51 -0500 (Wed, 12 Dec 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Groups_Overlay.pm
+
+* add definitions of new methods we have to share between Groups and Users classes
+------------------------------------------------------------------------
+r9922 | ruz | 2007-12-12 15:31:15 -0500 (Wed, 12 Dec 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23cfsort.t
+
+backport from 3.7
+
+* add more tests to show that sorting by CF fails mark them as todo
+
+------------------------------------------------------------------------
+r9884 | sartak | 2007-12-11 08:58:39 -0500 (Tue, 11 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Simple.html
+
+ r48891@onn: sartak | 2007-12-11 08:58:32 -0500
+ Add some callbacks to Simple Search
+
+------------------------------------------------------------------------
+r9883 | sartak | 2007-12-11 08:41:42 -0500 (Tue, 11 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/Googleish.pm
+
+ r48889@onn: sartak | 2007-12-11 08:41:35 -0500
+ Allow the caller of Googleish searches to choose what queues go into the query
+
+------------------------------------------------------------------------
+r9882 | sartak | 2007-12-11 08:37:35 -0500 (Tue, 11 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Widgets/SelectionBox
+
+ r48885@onn: sartak | 2007-12-11 08:37:30 -0500
+ Add an optional Clear button to SelectionBox
+
+------------------------------------------------------------------------
+r9879 | sartak | 2007-12-11 08:13:27 -0500 (Tue, 11 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Widgets/SelectionBox
+
+ r48883@onn: sartak | 2007-12-11 08:13:12 -0500
+ Add a NoArrows option to selectionbox, for when order doesn't matter
+
+------------------------------------------------------------------------
+r9871 | jesse | 2007-12-10 13:45:46 -0500 (Mon, 10 Dec 2007) | 7 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/Modify.html
+
+ r72955@pinglin: jesse | 2007-12-10 13:45:10 -0500
+ RT-Ticket: 8702
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a small html fix (missing </td>) from Steve Turner
+
+------------------------------------------------------------------------
+r9870 | jesse | 2007-12-10 13:45:31 -0500 (Mon, 10 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScripAction
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectTemplate
+
+ r72954@pinglin: jesse | 2007-12-10 13:42:06 -0500
+ * Sort Templates, Actions and Conditions by Name, as suggested by Steve Turner
+
+------------------------------------------------------------------------
+r9869 | jesse | 2007-12-10 13:01:05 -0500 (Mon, 10 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fr.po
+
+ r72886@pinglin: jesse | 2007-12-10 13:00:49 -0500
+ * Updated french translation from Emmanuel Lacour
+
+------------------------------------------------------------------------
+r9853 | audreyt | 2007-12-09 00:19:10 -0500 (Sun, 09 Dec 2007) | 9 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/etc/initialdata
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/DisplayOptions
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/SelfService/index.html
+ M /rt/branches/3.6-RELEASE/html/Tools/MyDay.html
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_tw.po
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets.pm
+
+ r18145@T (orig r9848): audreyt | 2007-12-08 07:35:37 +0800
+ * Massive internationalization fixes.
+ r18146@T (orig r9849): audreyt | 2007-12-08 07:39:06 +0800
+ * zh_tw.po cleanup
+ r18148@T (orig r9851): audreyt | 2007-12-09 01:21:40 +0800
+ * zh_tw.po: More L10N
+ r18149@T (orig r9852): audreyt | 2007-12-09 13:08:38 +0800
+ * Full translation of zh_tw and zh_cn.
+
+------------------------------------------------------------------------
+r9807 | jesse | 2007-12-03 16:05:08 -0500 (Mon, 03 Dec 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+* 3.6.6rc1
+
+------------------------------------------------------------------------
+r9806 | jesse | 2007-12-03 12:06:57 -0500 (Mon, 03 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+
+ r72604@pinglin: jesse | 2007-12-03 12:06:40 -0500
+ * Japanese translation updates from Shinji Yamane
+
+------------------------------------------------------------------------
+r9784 | jesse | 2007-12-01 21:41:06 -0500 (Sat, 01 Dec 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/EmailInput
+ M /rt/branches/3.6-RELEASE/html/Helpers/EmailAutocomplete
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/controls.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/effects.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/prototype.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/scriptaculous.js
+ M /rt/branches/3.6-RELEASE/html/SelfService/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Update.html
+
+ r72598@pinglin: jesse | 2007-12-01 21:40:32 -0500
+ * reverting audrey's changes which were just pulled to the aberdeen branch
+
+------------------------------------------------------------------------
+r9781 | audreyt | 2007-12-01 21:31:45 -0500 (Sat, 01 Dec 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Helpers/EmailAutocomplete
+ M /rt/branches/3.6-RELEASE/html/SelfService/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Update.html
+
+* Convert SelfService and Ticket Create/Update to EmailAutocomplete.
+------------------------------------------------------------------------
+r9780 | audreyt | 2007-12-01 21:30:10 -0500 (Sat, 01 Dec 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/EmailInput
+
+* EmailInput: First cut at email completion widget.
+------------------------------------------------------------------------
+r9779 | audreyt | 2007-12-01 21:27:18 -0500 (Sat, 01 Dec 2007) | 1 line
+Changed paths:
+ A /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous
+ A /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/controls.js
+ A /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/effects.js
+ A /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/prototype.js
+ A /rt/branches/3.6-RELEASE/html/NoAuth/js/scriptaculous/scriptaculous.js
+
+* Add the parts of scriptaculous we need for the autocompleter.
+------------------------------------------------------------------------
+r9778 | audreyt | 2007-12-01 21:17:03 -0500 (Sat, 01 Dec 2007) | 1 line
+Changed paths:
+ A /rt/branches/3.6-RELEASE/html/NoAuth/images/css/fieldbg-autocomplete.gif
+
+* Picture of autocompleted-field-background from jifty
+------------------------------------------------------------------------
+r9777 | audreyt | 2007-12-01 21:15:21 -0500 (Sat, 01 Dec 2007) | 1 line
+Changed paths:
+ A /rt/branches/3.6-RELEASE/html/Helpers/EmailAutocomplete
+
+* Email Autocompletion element.
+------------------------------------------------------------------------
+r9776 | audreyt | 2007-12-01 18:09:56 -0500 (Sat, 01 Dec 2007) | 1 line
+Changed paths:
+ A /rt/branches/3.6-RELEASE/html/Elements/EmailInput
+
+* Elements/EmailInput - Beginning of refactoring toward Ajax mail
+------------------------------------------------------------------------
+r9768 | jesse | 2007-11-30 14:14:25 -0500 (Fri, 30 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r72537@pinglin: jesse | 2007-11-30 14:13:55 -0500
+ * Applied http://page.mi.fu-berlin.de/pape/rt3/patches/rt/3.6.5/less_warnings_in_error_log.patch from Dirk Pape to quiet some warnings
+
+------------------------------------------------------------------------
+r9767 | jesse | 2007-11-30 14:08:42 -0500 (Fri, 30 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/nl.po
+
+ r72527@pinglin: jesse | 2007-11-30 14:07:42 -0500
+ * Updated dutch .po file from Marcel Kolkman
+
+------------------------------------------------------------------------
+r9742 | ruz | 2007-11-23 20:27:52 -0500 (Fri, 23 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* bump required version of SB so people will get a fix that
+ affect the latest change
+------------------------------------------------------------------------
+r9741 | ruz | 2007-11-23 20:25:55 -0500 (Fri, 23 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Users_Overlay.pm
+
+* split WhoHaveRoleRights into more queries so buggy MySQL's
+ optimizer can do the right thing
+------------------------------------------------------------------------
+r9733 | ruz | 2007-11-23 17:34:45 -0500 (Fri, 23 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+
+* oops, missed space during refactoring
+------------------------------------------------------------------------
+r9730 | falcone | 2007-11-23 17:15:54 -0500 (Fri, 23 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/search/ticket
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+ r26864@ketch: falcone | 2007-11-23 17:04:49 -0500
+ * handle spaces in field names when doing rt ls
+
+------------------------------------------------------------------------
+r9727 | ruz | 2007-11-21 19:38:24 -0500 (Wed, 21 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Widgets/SavedSearch
+
+* add handling of arguments with multiple values into Widgets/SavedSearch
+------------------------------------------------------------------------
+r9715 | ruz | 2007-11-20 20:27:25 -0500 (Tue, 20 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ConfigureMyRT
+ M /rt/branches/3.6-RELEASE/html/Widgets/SelectionBox
+
+* revert rev9654 as it breaks charts on home page
+------------------------------------------------------------------------
+r9695 | ruz | 2007-11-18 16:56:52 -0500 (Sun, 18 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+
+* add caching based on short keys
+* split _HasRight into _HasGroupRight and _HasRoleRight
+* use more queries for roles
+------------------------------------------------------------------------
+r9693 | ruz | 2007-11-16 17:42:23 -0500 (Fri, 16 Nov 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+
+* unshift more global objects instead of pushing them,
+ so we'll get them earlier in an upcoming refactoring
+* do all things related to EquivObjects in HasRight and
+ leave only checks in _HasRight
+------------------------------------------------------------------------
+r9692 | ruz | 2007-11-16 17:37:59 -0500 (Fri, 16 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+
+* minor
+------------------------------------------------------------------------
+r9659 | falcone | 2007-11-13 19:09:02 -0500 (Tue, 13 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/Modify.html
+
+ r26553@ketch: falcone | 2007-11-13 19:07:28 -0500
+ * set the prefix so that CFs can be added during Group Creation
+
+------------------------------------------------------------------------
+r9654 | ruz | 2007-11-13 12:59:32 -0500 (Tue, 13 Nov 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ConfigureMyRT
+ M /rt/branches/3.6-RELEASE/html/Widgets/SelectionBox
+
+* add labeling of portlets that are based on components,
+ it doesn't support localization, but it's better than empty label
+* allow to manage many portlets that are based on the same
+ comp by adding optional 'id' key
+------------------------------------------------------------------------
+r9653 | ruz | 2007-11-13 12:45:26 -0500 (Tue, 13 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Widgets/TitleBoxStart
+
+* we don't use some vars anymore so don't fool people
+ by defining them in the %ARGS block
+------------------------------------------------------------------------
+r9649 | audreyt | 2007-11-12 15:48:07 -0500 (Mon, 12 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+* RT::Action::SendEmail - Support for multipart/* emails; we now
+ textualify all MIME parts, instead of collapsing the whole multipart.
+------------------------------------------------------------------------
+r9648 | audreyt | 2007-11-12 15:46:09 -0500 (Mon, 12 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+
+* RT::Transaction->ContentObj - Prefer the first part that
+ matches $PreferredContentType, instead of always 'text/plain'.
+------------------------------------------------------------------------
+r9647 | audreyt | 2007-11-12 03:03:52 -0500 (Mon, 12 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+
+* RT::Transaction_Overlay - use RT::I18N::IsTextualContentType.
+------------------------------------------------------------------------
+r9646 | audreyt | 2007-11-12 03:02:35 -0500 (Mon, 12 Nov 2007) | 10 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachment_Overlay.pm
+
+* RT::Attachment_Overlay - Fix several broken logic in ->OriginalContent:
+
+ - When $self->ContentEncoding eq 'quoted-printable', a premature
+ "return" prevented any encoding conversion from taking place.
+
+ - A bogus Encode::_utf8_on($content) later prevented any encoding
+ conversino from taking place _anyway_.
+
+* $attachment->Quote now respects RT::I18N::IsTextualContentType.
+
+------------------------------------------------------------------------
+r9645 | audreyt | 2007-11-12 02:58:56 -0500 (Mon, 12 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+* RT::Record - _DecodeLOB now respects RT::I18N::IsTextualContentType.
+------------------------------------------------------------------------
+r9644 | audreyt | 2007-11-12 02:56:43 -0500 (Mon, 12 Nov 2007) | 13 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+
+* RT::I18N - Introduce a new utility function, IsTextualContentType($type),
+ that determines whether $type can be sensibly converted to Unicode text.
+
+ Currently it uses this regex (case-insensitively):
+
+ ^(?:text/(?:plain|html)|message/rfc822)\b
+
+ The idea is to unify all the inconsistent uses all over RT's code
+ (some tested for text/*, some for text/plain|message/rfc822, some
+ for text/plain|text/html|message/*) to use this function instead.
+
+* Minor POD glitch - Say "function" when it said "method" but really wasn't.
+
+------------------------------------------------------------------------
+r9643 | audreyt | 2007-11-12 02:52:35 -0500 (Mon, 12 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+* RT::Action::SendEmail - Minor comment typo: "use" ne "sue"
+------------------------------------------------------------------------
+r9642 | audreyt | 2007-11-11 15:05:50 -0500 (Sun, 11 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+
+* RT::Transaction_Overlay: Be saner and allows explicitly
+ calling $txn->Content(Type => 'text/html'), instead of
+ relying on action-at-a-distance $PreferredContentType.
+------------------------------------------------------------------------
+r9641 | audreyt | 2007-11-11 14:58:21 -0500 (Sun, 11 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+
+* RT.pm.in: Add support for loading RT_Vendor.pm by default
+ before RT_Local, for greater consistency.
+------------------------------------------------------------------------
+r9640 | audreyt | 2007-11-11 14:07:43 -0500 (Sun, 11 Nov 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+* RT::Action::SendEmail: Allow user-specified Content-Type
+ in outgoing templates. Currently, only text/* is supported
+ for security reasons. Eventually maybe multipart/* with
+ all textual subparts might be allowed, too.
+------------------------------------------------------------------------
+r9639 | audreyt | 2007-11-11 14:06:06 -0500 (Sun, 11 Nov 2007) | 9 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+
+* RT::Transaction_Overlay: Unify the idea of "textual" MIME types
+ to text/plain, text/html and message/*. (Previously it was defined
+ differently for e.g. the first part and the other parts.)
+
+* RT::Transaction_Overlay: Introduce the variable
+ $RT::Transaction::PreferredContentType to control the context
+ where ->Content() is evaluated in. If not set, it defaults
+ to text/plain.
+
+------------------------------------------------------------------------
+r9628 | sartak | 2007-11-09 15:49:01 -0500 (Fri, 09 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+
+ r44879@onn: sartak | 2007-11-09 15:48:26 -0500
+ If there are no tickets found in a search, give an error-message image instead of silently failing to load.
+
+------------------------------------------------------------------------
+r9627 | audreyt | 2007-11-09 15:23:38 -0500 (Fri, 09 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+* _DecodeLOB: All text/* are treated as UTF-8, not only text/plain.
+------------------------------------------------------------------------
+r9626 | audreyt | 2007-11-09 15:06:09 -0500 (Fri, 09 Nov 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+
+* RT::Transaction_Overlay: Signatures in text/html content objects should be
+ removed in a way that doesn't depend on whitespace.
+ Also, remove an unneccessary regex capture.
+
+------------------------------------------------------------------------
+r9625 | ruz | 2007-11-09 14:57:37 -0500 (Fri, 09 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+
+* delete double width definition
+* don't escape html
+------------------------------------------------------------------------
+r9623 | audreyt | 2007-11-09 14:07:05 -0500 (Fri, 09 Nov 2007) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+* MakeMIMEEntity now takes an optional "Type" field to denote
+ the MIME Type of the body. (Default to 'text/plain'.)
+
+* CreateTicket() now supports $ARGS{ContentType}, and
+ ProcessUpdateMessage() now supports $ARGS{UpdateContentType}.
+------------------------------------------------------------------------
+r9620 | ruz | 2007-11-08 18:08:32 -0500 (Thu, 08 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/Members.html
+ M /rt/branches/3.6-RELEASE/html/User/Groups/Members.html
+
+* In this case the <ul> was opened inside an IF block,
+ but </ul> closed outside the IF block.
+ Thanks to Jason Long.
+------------------------------------------------------------------------
+r9619 | ruz | 2007-11-08 18:05:04 -0500 (Thu, 08 Nov 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+
+* The <nobr> opening tag came before the IF statement,
+ and the </nobr> closing tag was inside the IF statement.
+ Thanks to Jason Long.
+------------------------------------------------------------------------
+r9618 | ruz | 2007-11-08 17:55:41 -0500 (Thu, 08 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+* RFC dictates to encode by chars, so if char is two octets then
+ it MUST be in one chunk
+------------------------------------------------------------------------
+r9617 | ruz | 2007-11-08 17:38:33 -0500 (Thu, 08 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+* add max-age along to Expires
+------------------------------------------------------------------------
+r9616 | audreyt | 2007-11-08 15:06:00 -0500 (Thu, 08 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* Add new dep: CSS::Squish 0.06
+------------------------------------------------------------------------
+r9615 | ruz | 2007-11-08 12:19:48 -0500 (Thu, 08 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/autohandler
+
+* align comments
+------------------------------------------------------------------------
+r9610 | ruz | 2007-11-06 16:30:42 -0500 (Tue, 06 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowBasics
+ A /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowQueue
+
+* add Ticket/Elements/ShowQueue and use it on create and in ShowBasics,
+ tiny comp for overriding in extensions
+------------------------------------------------------------------------
+r9560 | ruz | 2007-11-06 05:03:32 -0500 (Tue, 06 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/Header
+ A /rt/branches/3.6-RELEASE/html/NoAuth/css/dhandler
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* squish our CSS into one big thing use CSS::Squish and dhandler
+------------------------------------------------------------------------
+r9559 | ruz | 2007-11-06 04:59:01 -0500 (Tue, 06 Nov 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+* don't send Last-Modified field in a response
+* make cache public
+------------------------------------------------------------------------
+r9558 | ruz | 2007-11-06 04:40:57 -0500 (Tue, 06 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/forms.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/main.css
+
+* revert rev. 9532 as we now more interested in mason comments instead of css comments
+------------------------------------------------------------------------
+r9532 | ruz | 2007-11-01 10:07:17 -0400 (Thu, 01 Nov 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/forms.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/main.css
+
+* convert mason comments into css comments
+------------------------------------------------------------------------
+r9471 | tla | 2007-10-26 10:36:52 -0400 (Fri, 26 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+get rid of overly verbose debug statement
+------------------------------------------------------------------------
+r9462 | sunnavy | 2007-10-25 16:04:40 -0400 (Thu, 25 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/Tabs
+
+add a callback for SelfService/Elements/Tabs
+------------------------------------------------------------------------
+r9460 | tla | 2007-10-25 10:39:42 -0400 (Thu, 25 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+change comment
+------------------------------------------------------------------------
+r9457 | tla | 2007-10-25 05:33:58 -0400 (Thu, 25 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+refrain from shifting args off the argument array
+------------------------------------------------------------------------
+r9447 | tla | 2007-10-24 22:20:11 -0400 (Wed, 24 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+don't want RecordOutgoingMailTransaction to be fatal to the scrip.
+------------------------------------------------------------------------
+r9446 | tla | 2007-10-24 22:04:45 -0400 (Wed, 24 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+store the outgoing txn ID in for later access
+------------------------------------------------------------------------
+r9445 | tla | 2007-10-24 18:03:16 -0400 (Wed, 24 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+Save the transaction ID of the outgoing message transaction
+------------------------------------------------------------------------
+r9429 | ruz | 2007-10-23 20:16:06 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMembers
+
+* oops, forgot commit one line, there is no $member anymore
+------------------------------------------------------------------------
+r9428 | ruz | 2007-10-23 20:12:43 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMembers
+
+* switch to /Elements/ShowLink
+------------------------------------------------------------------------
+r9426 | ruz | 2007-10-23 19:05:52 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/EditLinks
+
+* delete unused variable
+------------------------------------------------------------------------
+r9425 | ruz | 2007-10-23 19:03:34 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMembers
+
+* add missing </li>
+------------------------------------------------------------------------
+r9424 | ruz | 2007-10-23 19:02:34 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMembers
+
+* move general if block into <%INIT>
+------------------------------------------------------------------------
+r9423 | ruz | 2007-10-23 18:44:53 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+* update docs
+------------------------------------------------------------------------
+r9422 | ruz | 2007-10-23 18:43:38 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyLinks.html
+
+* pass a reference to @results into a callback, so extensions can fill it
+------------------------------------------------------------------------
+r9421 | ruz | 2007-10-23 16:41:07 -0400 (Tue, 23 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowLinks
+
+* add closing tags for elements of lists
+------------------------------------------------------------------------
+r9418 | sartak | 2007-10-23 14:14:23 -0400 (Tue, 23 Oct 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Template_Overlay.pm
+
+ r44066@onn: sartak | 2007-10-23 14:14:02 -0400
+ Allow Template->ParseContent to not require Ticket and Txn
+
+------------------------------------------------------------------------
+r9380 | sunnavy | 2007-10-20 03:06:24 -0400 (Sat, 20 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+
+don't overwrite %ARGS; don't add cloned ticket to parents by default
+------------------------------------------------------------------------
+r9365 | sunnavy | 2007-10-19 15:42:07 -0400 (Fri, 19 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldFreeform
+
+we shouldn't replace "\n" with whitespace if it's multiple type
+------------------------------------------------------------------------
+r9362 | sunnavy | 2007-10-19 15:23:08 -0400 (Fri, 19 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+
+Ticket/Create.html can handle clone function now
+------------------------------------------------------------------------
+r9271 | jesse | 2007-10-11 11:40:59 -0400 (Thu, 11 Oct 2007) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+
+ r68059@pinglin: jesse | 2007-10-11 11:40:39 -0400
+ * We used the wrong variable name in html/Search/Chart (for fonts)
+ Thanks to elacour at easter-eggs.com
+
+
+
+------------------------------------------------------------------------
+r9270 | sunnavy | 2007-10-11 01:22:26 -0400 (Thu, 11 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/History.html
+
+added AfterShowHistory Callback in Ticket/History.html
+------------------------------------------------------------------------
+r9268 | jesse | 2007-10-10 21:35:37 -0400 (Wed, 10 Oct 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r68037@h46055e4a: jesse | 2007-10-10 19:45:05 -0500
+ * A small fix to stop RT from complaining about categories when updating custom fields via the web ui. Thanks to Brian Gallew
+
+------------------------------------------------------------------------
+r9258 | sunnavy | 2007-10-09 16:44:47 -0400 (Tue, 09 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+added current_tab arg for AfterShowHistory Callback in Ticket/Display.html
+------------------------------------------------------------------------
+r9245 | sunnavy | 2007-10-08 14:11:19 -0400 (Mon, 08 Oct 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/PageLayout
+ M /rt/branches/3.6-RELEASE/html/Elements/Tabs
+
+added show_menu arg for Elements/Tabs and Elements/PageLayout, mainly for RTx-TicketBottomTabs
+------------------------------------------------------------------------
+r9214 | jesse | 2007-10-03 10:54:53 -0400 (Wed, 03 Oct 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/Modify.html
+
+ r27337@hualien: jesse | 2007-10-03 10:54:11 -0400
+ New callbacks from Dirk Pape: http://page.mi.fu-berlin.de/pape/rt3/patches/rt/3.6.4/add_callbacks_to_admin_us
+ ers.patch
+
+------------------------------------------------------------------------
+r9192 | jesse | 2007-10-02 14:09:30 -0400 (Tue, 02 Oct 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditScrips
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditTemplates
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/DisplayOptions
+ M /rt/branches/3.6-RELEASE/html/Ticket/Update.html
+
+ r27297@hualien: jesse | 2007-10-02 14:08:53 -0400
+ * HTML Table fixes from Dirk Pape (http://page.mi.fu-berlin.de/pape/rt3/patches/rt/3.6.4/rt3.6-xhtml-2.patch)
+
+------------------------------------------------------------------------
+r9190 | jesse | 2007-10-01 17:09:35 -0400 (Mon, 01 Oct 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r27278@hualien: jesse | 2007-10-01 17:08:31 -0400
+ 3.6.5 releng
+
+------------------------------------------------------------------------
+r9152 | jesse | 2007-09-26 00:43:10 -0400 (Wed, 26 Sep 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/EmailParser.pm
+
+ r67594@pinglin: jesse | 2007-09-26 00:42:44 -0400
+ MIME::Tools removed a method from their public API. this works around it
+
+------------------------------------------------------------------------
+r9148 | sunnavy | 2007-09-25 16:42:57 -0400 (Tue, 25 Sep 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+revert lib/t/regression/26command_line.t to drop multiple value cf tests
+------------------------------------------------------------------------
+r9145 | sunnavy | 2007-09-25 12:05:09 -0400 (Tue, 25 Sep 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+
+revert back for multiple cf values bug in cli, we need a branch for that
+------------------------------------------------------------------------
+r9139 | jesse | 2007-09-24 11:42:34 -0400 (Mon, 24 Sep 2007) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+
+ r67533@pinglin: jesse | 2007-09-24 11:41:58 -0400
+ RT-Ticket: 8563
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Updated Japanese translation from y-iida@secom.co.jp
+
+
+------------------------------------------------------------------------
+r9122 | sunnavy | 2007-09-23 00:34:33 -0400 (Sun, 23 Sep 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+
+updated vsplit in accordance with our change to multiple value CF
+------------------------------------------------------------------------
+r9121 | sunnavy | 2007-09-23 00:20:30 -0400 (Sun, 23 Sep 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+do the right way for view and modification of multiple value CF for cli
+------------------------------------------------------------------------
+r9119 | sunnavy | 2007-09-21 16:24:07 -0400 (Fri, 21 Sep 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+single value cf can be set rightly through bin/rt.
+------------------------------------------------------------------------
+r9100 | sunnavy | 2007-09-20 02:55:33 -0400 (Thu, 20 Sep 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+
+remove buggy whitespaces following a comma
+------------------------------------------------------------------------
+r9099 | sunnavy | 2007-09-20 02:05:48 -0400 (Thu, 20 Sep 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+A multi-valued custom field should end up with multiple rows in
+ObjectCustomFieldValues
+------------------------------------------------------------------------
+r9082 | sunnavy | 2007-09-18 03:02:09 -0400 (Tue, 18 Sep 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/21query-builder.t
+
+cleaned up a useless custom field for following tests
+------------------------------------------------------------------------
+r9081 | sunnavy | 2007-09-18 01:13:09 -0400 (Tue, 18 Sep 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+added tests for set/add/del values for custom field with multiple
+values in command line
+------------------------------------------------------------------------
+r9076 | sunnavy | 2007-09-17 16:52:39 -0400 (Mon, 17 Sep 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+
+fix bug 10273.
+https://tickets.bestpractical.com/Ticket/Display.html?id=10273
+we need `delete' the old values before setting new values.
+
+------------------------------------------------------------------------
+r8942 | jesse | 2007-09-05 16:57:32 -0400 (Wed, 05 Sep 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/SelfService/Display.html
+
+ r66993@pinglin: jesse | 2007-09-05 16:44:11 -0400
+ Fix to not update tickets multiple times when doing a selfservice posting.
+
+------------------------------------------------------------------------
+r8674 | jesse | 2007-08-21 13:49:46 -0400 (Tue, 21 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r66194@pinglin: jesse | 2007-08-21 13:49:09 -0400
+ 3.6.5 RC2
+
+------------------------------------------------------------------------
+r8672 | ruz | 2007-08-21 12:03:53 -0400 (Tue, 21 Aug 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/titlebox.css
+
+* switch back to using paddings instead of margins,
+ tables in title-boxes are screwed in IE
+------------------------------------------------------------------------
+r8671 | nicholas | 2007-08-21 11:30:26 -0400 (Tue, 21 Aug 2007) | 11 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomFields
+
+svn merge -r8090:8094 http://code.bestpractical.com/bps-public/rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/EditCustomFields html/Ticket/Elements/EditCustomFields
+
+ r8091 | ruz | 2007-07-10 21:44:12 +0000 (Tue, 10 Jul 2007) | 2 lines
+
+ * get rid of Count call, use counter to figure out if we have even or odd number of CFs
+ * use less nested tables
+
+ r8093 | ruz | 2007-07-10 21:59:45 +0000 (Tue, 10 Jul 2007) | 1 line
+
+ * skip a CF if user has no right to change it
+
+------------------------------------------------------------------------
+r8670 | nicholas | 2007-08-21 10:28:28 -0400 (Tue, 21 Aug 2007) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransactionAttachments
+
+<div class="messagebody"> is opened within
+ foreach my $message ( grep { $_->Parent == $Parent } @$Attachments ) {
+but was being closed (erroneously) just beyond the foreach. This could result
+in lots of orphaned </div> tags.
+
+------------------------------------------------------------------------
+r8669 | nicholas | 2007-08-21 09:39:15 -0400 (Tue, 21 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/Row
+
+For a long time html/Elements/CollectionAsTable/ParseFormat has been parsing
+/CLASS. Fix html/Elements/CollectionAsTable/Row to put that class on the <td>
+
+------------------------------------------------------------------------
+r8550 | falcone | 2007-08-14 13:54:27 -0400 (Tue, 14 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/StyleGuide.pod
+
+ r23791@ketch: falcone | 2007-08-14 13:53:58 -0400
+ * we no longer support 5.6.1
+
+------------------------------------------------------------------------
+r8540 | falcone | 2007-08-14 12:23:38 -0400 (Tue, 14 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+ r23772@ketch: falcone | 2007-08-14 12:22:58 -0400
+ * document how to use the Update command on Records
+
+------------------------------------------------------------------------
+r8471 | jesse | 2007-08-09 11:26:00 -0400 (Thu, 09 Aug 2007) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFields
+
+ r65323@pinglin: jesse | 2007-08-09 11:25:41 -0400
+ *( Fix a bug (?) where a 0 was displayed after a value for multiple-values
+ custom fields. - Quentin Garnier
+
+
+
+------------------------------------------------------------------------
+r8454 | sartak | 2007-08-08 17:20:43 -0400 (Wed, 08 Aug 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+
+Reword $ChartFont description
+------------------------------------------------------------------------
+r8453 | ruz | 2007-08-08 17:04:31 -0400 (Wed, 08 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+
+* add new $ChartFont option to the config which allow admin to
+ select a tru type font RT uses in charts
+
+------------------------------------------------------------------------
+r8451 | ruz | 2007-08-08 16:26:30 -0400 (Wed, 08 Aug 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ru.po
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+
+* fix ru.po
+* simplify translators' work by using phrases with the same case
+------------------------------------------------------------------------
+r8445 | jesse | 2007-08-08 13:25:30 -0400 (Wed, 08 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r65296@pinglin: jesse | 2007-08-08 13:24:39 -0400
+ * 3.6.5rc1
+
+------------------------------------------------------------------------
+r8443 | falcone | 2007-08-08 09:19:21 -0400 (Wed, 08 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransaction
+
+ r23542@ketch: falcone | 2007-08-08 09:18:04 -0400
+ * remove $aid var that never gets used.
+
+------------------------------------------------------------------------
+r8433 | ruz | 2007-08-07 16:44:52 -0400 (Tue, 07 Aug 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+
+* fix performance regression in searches by watchers joined
+ with other conditions using OR
+------------------------------------------------------------------------
+r8432 | ruz | 2007-08-07 16:43:05 -0400 (Tue, 07 Aug 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+
+* update doc
+------------------------------------------------------------------------
+r8431 | ruz | 2007-08-07 16:38:47 -0400 (Tue, 07 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+* call CanonicalizeEmailAddress as class method instead of
+ passing undef as first argument
+
+------------------------------------------------------------------------
+r8430 | ruz | 2007-08-07 16:37:21 -0400 (Tue, 07 Aug 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/21query-builder.t
+
+* add debug info into test
+------------------------------------------------------------------------
+r8428 | ruz | 2007-08-07 15:36:17 -0400 (Tue, 07 Aug 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/00-mason-syntax.t
+
+* fix compatibility of a test with new version of mason
+------------------------------------------------------------------------
+r8409 | jesse | 2007-08-06 17:33:00 -0400 (Mon, 06 Aug 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+ r65236@pinglin: jesse | 2007-08-06 17:17:56 -0400
+ * better canonicalization of multiple addresses in the cc/bcc box on correspondence and comment
+
+------------------------------------------------------------------------
+r8344 | ruz | 2007-07-31 19:32:21 -0400 (Tue, 31 Jul 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+* we don't exit on errors in data, but go to next object
+------------------------------------------------------------------------
+r8280 | ruz | 2007-07-25 10:34:34 -0400 (Wed, 25 Jul 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/22search_tix_by_watcher.t
+
+* mark only one query as todo instead of many which really should pass
+------------------------------------------------------------------------
+r8271 | jesse | 2007-07-24 19:06:29 -0400 (Tue, 24 Jul 2007) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearch.pm
+
+ r61183@pinglin: jesse | 2007-07-24 16:05:28 -0700
+ RT-Ticket: 8492
+ RT-Update: correspond
+ RT-Status: resolved
+
+ * James Bunch pointed out a missing $ that would cause a failed method call in an error scenario
+
+
+------------------------------------------------------------------------
+r8237 | jesse | 2007-07-20 16:37:33 -0400 (Fri, 20 Jul 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+ r60980@pinglin: jesse | 2007-07-20 16:36:52 -0400
+ * Some custom fields (mixed case ones) didn't work properly on case-sensitive databases from the CLI client
+
+------------------------------------------------------------------------
+r8233 | jesse | 2007-07-19 15:30:01 -0400 (Thu, 19 Jul 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r60965@pinglin: jesse | 2007-07-19 15:29:22 -0400
+ * Backported 6766 to RT 3.6, with additional cleanups
+
+------------------------------------------------------------------------
+r8228 | jesse | 2007-07-19 13:40:36 -0400 (Thu, 19 Jul 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/drop.Oracle
+
+ r60951@pinglin: jesse | 2007-07-19 13:39:57 -0400
+ * Oracle drop schema typo fixes. Thanks to JoopvandeWege@mococo.nl
+
+------------------------------------------------------------------------
+r8215 | jesse | 2007-07-18 19:12:52 -0400 (Wed, 18 Jul 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/dhandler
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/REST.pm
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+ r60938@pinglin: jesse | 2007-07-18 19:12:15 -0400
+ * Fixed RT REST API and commandline client to support custom fields with spaces in their names
+
+
+------------------------------------------------------------------------
+r8186 | ruz | 2007-07-17 12:27:11 -0400 (Tue, 17 Jul 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+* clear duplications recipient lists, based on patch from Dirk Pape
+------------------------------------------------------------------------
+r8182 | ruz | 2007-07-17 07:11:12 -0400 (Tue, 17 Jul 2007) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Base.pm
+
+::Base::CurrentUser
+* allow passing RT::User as argument
+* check that the object is really RT::CurrentUser
+ or its sub-class
+* verbose error message
+* on error return undef instead of 0(zero)
+------------------------------------------------------------------------
+r8162 | jesse | 2007-07-14 00:03:08 -0400 (Sat, 14 Jul 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+
+ r60623@pinglin: jesse | 2007-07-13 15:58:45 -0400
+ * Quiet a warning. Thanks to Vlad <marchenko@gmail.com>
+
+------------------------------------------------------------------------
+r8063 | jesse | 2007-07-05 16:59:04 -0400 (Thu, 05 Jul 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r59918@pinglin: jesse | 2007-07-05 16:58:53 -0400
+ * Someone left in a call to Data::Dumper
+
+------------------------------------------------------------------------
+r8061 | jesse | 2007-07-05 13:46:42 -0400 (Thu, 05 Jul 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r59914@pinglin: jesse | 2007-07-05 13:45:10 -0400
+ Bumping to 3.6.4
+
+------------------------------------------------------------------------
+r8060 | jesse | 2007-07-05 13:45:54 -0400 (Thu, 05 Jul 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/22search_tix_by_watcher.t
+
+ r59913@pinglin: jesse | 2007-07-05 13:10:32 -0400
+ * todoing a test for a future feature
+
+------------------------------------------------------------------------
+r8025 | falcone | 2007-06-25 10:41:49 -0400 (Mon, 25 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyAll.html
+
+ r22168@ketch: falcone | 2007-06-25 09:41:08 -0500
+ * There is no $TicketObj here, just $Ticket
+
+------------------------------------------------------------------------
+r8024 | nicholas | 2007-06-25 08:56:54 -0400 (Mon, 25 Jun 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/Error
+
+Add a ) that was missing from r8012.
+
+------------------------------------------------------------------------
+r8023 | nicholas | 2007-06-25 08:55:26 -0400 (Mon, 25 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/00-mason-syntax.t
+
+Need to skip all files in .svn directories when searching for files to test
+load into Mason.
+
+------------------------------------------------------------------------
+r8016 | jesse | 2007-06-15 16:24:51 -0400 (Fri, 15 Jun 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ A /rt/branches/3.6-RELEASE/lib/RT/I18N/tr.po
+
+ r58409@pinglin: jesse | 2007-06-15 16:24:18 -0400
+ * First cut Turkish translation from burakgursoy@gmx.net
+
+
+------------------------------------------------------------------------
+r8012 | jesse | 2007-06-15 12:31:08 -0400 (Fri, 15 Jun 2007) | 9 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/Error
+
+ r58404@pinglin: jesse | 2007-06-15 12:30:51 -0400
+ html/Element/Errors generates uninitialized value warnings if the parameter
+ $Details is not passed in, and if the SessionType is undefined. The following
+ patch quietens it. - Nicholas Clark.
+
+ (slightly modified to be more masonic)
+
+
+
+------------------------------------------------------------------------
+r8010 | falcone | 2007-06-14 18:12:42 -0400 (Thu, 14 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+ r21978@ketch: falcone | 2007-06-14 18:12:25 -0400
+ * be more chatty about errors so you can figure out your initialdata mistakes
+
+------------------------------------------------------------------------
+r8009 | falcone | 2007-06-14 16:44:10 -0400 (Thu, 14 Jun 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyAll.html
+
+ r21941@ketch: falcone | 2007-06-14 16:41:33 -0400
+ * reload the ticket after scrips run, so if one of your scrips
+ changes ticket data it'll be reflected in the new display
+
+------------------------------------------------------------------------
+r8008 | falcone | 2007-06-14 16:43:48 -0400 (Thu, 14 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+
+ r18561@ketch: falcone | 2007-05-11 14:19:35 -0400
+ * don't parse undef for SQL (quiets a warning on new searches)
+
+------------------------------------------------------------------------
+r8007 | falcone | 2007-06-14 16:43:40 -0400 (Thu, 14 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r18560@ketch: falcone | 2007-05-11 14:18:46 -0400
+ * stop a warning when we don't have any saved search
+
+------------------------------------------------------------------------
+r8006 | falcone | 2007-06-14 16:42:27 -0400 (Thu, 14 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/link
+
+ r18127@ketch: falcone | 2007-04-18 17:08:59 -0400
+ * make bin/rt link work and fix the associated tests
+
+------------------------------------------------------------------------
+r7999 | jesse | 2007-06-08 15:01:31 -0400 (Fri, 08 Jun 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.po
+
+ r58163@pinglin: jesse | 2007-06-08 15:00:29 -0400
+ * Czech translation updates from Daniel Kastner
+
+
+------------------------------------------------------------------------
+r7993 | jesse | 2007-06-07 13:20:44 -0400 (Thu, 07 Jun 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+* 3.6.4.rc2
+
+------------------------------------------------------------------------
+r7992 | jesse | 2007-06-07 13:16:48 -0400 (Thu, 07 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+
+ r58101@pinglin: jesse | 2007-06-07 13:15:56 -0400
+ * Now honor RT::Timezone
+
+------------------------------------------------------------------------
+r7989 | ruz | 2007-06-06 23:07:12 -0400 (Wed, 06 Jun 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+
+* revert fix for searches like 'Requestor = X AND Requestor = Y',
+ unless we have correct bundling of conditions searches by requestors
+ with OR aggregator are very slow.
+------------------------------------------------------------------------
+r7935 | jesse | 2007-05-25 16:16:34 -0400 (Fri, 25 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+ r57122@pinglin: jesse | 2007-05-25 16:15:54 -0400
+ * Log a notice when we choose not to redistribute autogenerated messages. Suggested by John Bartelt.
+
+------------------------------------------------------------------------
+r7934 | jesse | 2007-05-25 16:00:19 -0400 (Fri, 25 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/MyRequests
+
+ r57120@pinglin: jesse | 2007-05-25 15:59:54 -0400
+ Minor cleanup from Nicholas Clark
+
+------------------------------------------------------------------------
+r7905 | ruz | 2007-05-22 02:35:58 -0400 (Tue, 22 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* we uploaded a new version of Module::Versions::Report to the CPAN
+ which fix long standing issue. Bump deps.
+
+------------------------------------------------------------------------
+r7904 | ruz | 2007-05-22 02:30:28 -0400 (Tue, 22 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* mysql 5.x are a little bit crazy about joins dependencies,
+ we've fixed things in SB 1.48
+
+------------------------------------------------------------------------
+r7900 | jesse | 2007-05-21 15:59:12 -0400 (Mon, 21 May 2007) | 7 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+ r57019@pinglin: jesse | 2007-05-21 15:58:43 -0400
+ RT-Ticket: 8387
+ Update: correspond
+
+
+ * Better debugging info when schema files are missing. - Nicholas Clark <nick@ccl4.org>
+
+------------------------------------------------------------------------
+r7897 | ruz | 2007-05-18 01:07:19 -0400 (Fri, 18 May 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+
+* if we cache aliases then we should not apply limits multiple times
+
+------------------------------------------------------------------------
+r7895 | jesse | 2007-05-17 15:26:07 -0400 (Thu, 17 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r56946@pinglin: root | 2007-05-17 15:23:33 -0400
+ * 3.6.4rc1
+
+------------------------------------------------------------------------
+r7893 | ruz | 2007-05-17 00:08:14 -0400 (Thu, 17 May 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+* revert revision 7555 as it may result in attachments loosing
+------------------------------------------------------------------------
+r7892 | ruz | 2007-05-16 23:11:33 -0400 (Wed, 16 May 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE_Overlay.pm
+
+* revert change as it's causing regressions
+------------------------------------------------------------------------
+r7891 | ruz | 2007-05-16 21:31:35 -0400 (Wed, 16 May 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+* revert jesse's patch that slurps uploads into session,
+ this may hurt badly with multiple attachments.
+------------------------------------------------------------------------
+r7890 | ruz | 2007-05-16 19:57:27 -0400 (Wed, 16 May 2007) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+* calc id of a session in $SessionCookie var
+* add $session_properties variable
+** use it in both attempts to tie
+
+this should fix potential relogin problems
+
+------------------------------------------------------------------------
+r7887 | ruz | 2007-05-16 13:19:55 -0400 (Wed, 16 May 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE_Overlay.pm
+
+* we get principal's type as argument, but that's really
+ wrong as we have id of the pricipal and each principal
+ record has type property. Use info from the record and
+ log an error.
+------------------------------------------------------------------------
+r7886 | ruz | 2007-05-16 13:17:08 -0400 (Wed, 16 May 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE_Overlay.pm
+
+* method must return object and type, not status and message
+------------------------------------------------------------------------
+r7884 | jesse | 2007-05-16 12:49:03 -0400 (Wed, 16 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/TicketList
+
+ r56926@pinglin: jesse | 2007-05-16 12:47:06 -0400
+ * Fix for "Page 1 of 0" . Thanks to Nicholas Clark <nick@ccl4.org>
+
+------------------------------------------------------------------------
+r7859 | jesse | 2007-05-14 13:02:41 -0400 (Mon, 14 May 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+
+ r56818@pinglin: jesse | 2007-05-14 13:02:21 -0400
+ * Fix an inaccurate error message. Thanks to Nicholas Clark <nick@ccl4.org>
+
+
+------------------------------------------------------------------------
+r7855 | falcone | 2007-05-14 11:35:45 -0400 (Mon, 14 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+
+ r18561@ketch: falcone | 2007-05-11 14:19:35 -0400
+ * don't parse undef for SQL (quiets a warning on new searches)
+
+------------------------------------------------------------------------
+r7854 | falcone | 2007-05-14 11:35:33 -0400 (Mon, 14 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r18560@ketch: falcone | 2007-05-11 14:18:46 -0400
+ * stop a warning when we don't have any saved search
+
+------------------------------------------------------------------------
+r7853 | falcone | 2007-05-14 11:34:39 -0400 (Mon, 14 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/link
+
+ r18127@ketch: falcone | 2007-04-18 17:08:59 -0400
+ * make bin/rt link work and fix the associated tests
+
+------------------------------------------------------------------------
+r7775 | jesse | 2007-05-06 02:35:28 -0400 (Sun, 06 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowSummary
+
+ r56726@pinglin: jesse | 2007-05-06 02:35:04 -0400
+ * Evil hack to make the Reminders box render like most of the others
+
+------------------------------------------------------------------------
+r7774 | jesse | 2007-05-06 02:35:19 -0400 (Sun, 06 May 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/titlebox.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/transactions.css
+
+ r56725@pinglin: jesse | 2007-05-06 02:33:17 -0400
+ * Minor 3.6 style cleanups to tighten up the display a bit
+
+------------------------------------------------------------------------
+r7770 | ruz | 2007-05-04 21:20:07 -0400 (Fri, 04 May 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/ScrubHTML
+
+* we scrub format strings and should allow to __WebXXX__ things
+ in the beginning
+------------------------------------------------------------------------
+r7769 | ruz | 2007-05-04 21:13:47 -0400 (Fri, 04 May 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+* apply CF as global only if it hasn't been applied to a queue
+------------------------------------------------------------------------
+r7687 | jesse | 2007-04-25 09:22:08 -0400 (Wed, 25 Apr 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/drop.Oracle
+
+ r56038@pinglin: jesse | 2007-04-25 09:21:30 -0400
+ The drop.Oracle script was incomplete, drop statements were incorrect for version 3.6.3
+ -Reported by Christophe Nowicki cscm@meuh.dyndns.org
+
+------------------------------------------------------------------------
+r7673 | jesse | 2007-04-24 13:21:42 -0400 (Tue, 24 Apr 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/Makefile.in
+ M /rt/branches/3.6-RELEASE/README
+ M /rt/branches/3.6-RELEASE/bin/mason_handler.fcgi.in
+ M /rt/branches/3.6-RELEASE/bin/mason_handler.scgi.in
+ M /rt/branches/3.6-RELEASE/bin/mason_handler.svc.in
+ M /rt/branches/3.6-RELEASE/bin/rt-crontool.in
+ M /rt/branches/3.6-RELEASE/bin/rt-mailgate.in
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+ M /rt/branches/3.6-RELEASE/bin/standalone_httpd.in
+ M /rt/branches/3.6-RELEASE/bin/webmux.pl.in
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/Objects.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ConfigureMyRT
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/CustomFieldTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditCustomField
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditScrip
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditScrips
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditTemplates
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditUserComments
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/GlobalCustomFieldTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/GroupTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/Header
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ModifyTemplate
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ObjectCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/PickCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/PickObjects
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/QueueTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectCustomFieldLookupType
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectGroups
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectModifyQueue
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectModifyUser
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectRights
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScrip
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScripAction
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectSingleOrMultiple
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectStage
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectTemplate
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectUsers
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SystemTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ToolTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/UserTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Groups.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Queue-Tickets.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Queue-Transactions.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Users.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/MyRT.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Scrip.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Scrips.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Template.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Templates.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/CustomFields.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/History.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/Members.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/CustomField.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/CustomFields.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/People.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Scrip.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Scrips.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Template.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Templates.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Tools/Configuration.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Tools/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/CustomFields.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/History.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/Memberships.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/MyRT.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/autohandler
+ M /rt/branches/3.6-RELEASE/html/Admin/index.html
+ M /rt/branches/3.6-RELEASE/html/Approvals/Display.html
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/Approve
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/ShowDependency
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Approvals/index.html
+ M /rt/branches/3.6-RELEASE/html/Download/CustomFieldValue/dhandler
+ M /rt/branches/3.6-RELEASE/html/Download/Tabular/dhandler
+ M /rt/branches/3.6-RELEASE/html/Elements/BevelBoxRaisedEnd
+ M /rt/branches/3.6-RELEASE/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/3.6-RELEASE/html/Elements/Callback
+ M /rt/branches/3.6-RELEASE/html/Elements/Checkbox
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/Header
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/Row
+ M /rt/branches/3.6-RELEASE/html/Elements/CreateTicket
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomField
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldBinary
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldCombobox
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldImage
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldText
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldWikitext
+ M /rt/branches/3.6-RELEASE/html/Elements/EditLinks
+ M /rt/branches/3.6-RELEASE/html/Elements/Error
+ M /rt/branches/3.6-RELEASE/html/Elements/Footer
+ M /rt/branches/3.6-RELEASE/html/Elements/GotoTicket
+ M /rt/branches/3.6-RELEASE/html/Elements/Header
+ M /rt/branches/3.6-RELEASE/html/Elements/ListActions
+ M /rt/branches/3.6-RELEASE/html/Elements/Login
+ M /rt/branches/3.6-RELEASE/html/Elements/Logo
+ M /rt/branches/3.6-RELEASE/html/Elements/Menu
+ M /rt/branches/3.6-RELEASE/html/Elements/MessageBox
+ M /rt/branches/3.6-RELEASE/html/Elements/MyAdminQueues
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+ M /rt/branches/3.6-RELEASE/html/Elements/MyReminders
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRequests
+ M /rt/branches/3.6-RELEASE/html/Elements/MySupportQueues
+ M /rt/branches/3.6-RELEASE/html/Elements/MyTickets
+ M /rt/branches/3.6-RELEASE/html/Elements/PageLayout
+ M /rt/branches/3.6-RELEASE/html/Elements/QueryString
+ M /rt/branches/3.6-RELEASE/html/Elements/QueueSummary
+ M /rt/branches/3.6-RELEASE/html/Elements/QuickCreate
+ M /rt/branches/3.6-RELEASE/html/Elements/Quicksearch
+ M /rt/branches/3.6-RELEASE/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/3.6-RELEASE/html/Elements/Refresh
+ M /rt/branches/3.6-RELEASE/html/Elements/RefreshHomepage
+ M /rt/branches/3.6-RELEASE/html/Elements/ScrubHTML
+ M /rt/branches/3.6-RELEASE/html/Elements/Section
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectAttachmentField
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectBoolean
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectCustomFieldValue
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectDate
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectDateRelation
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectDateType
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectEqualityOperator
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectGroups
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectLang
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectLinkType
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectMatch
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectNewTicketQueue
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectOwner
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectQueue
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectResultsPerPage
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectSortOrder
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectStatus
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectTicketSortBy
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectTicketTypes
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectTimeUnits
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectUsers
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectWatcherType
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldBinary
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldImage
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFields
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowLink
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowLinks
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowMemberships
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+ M /rt/branches/3.6-RELEASE/html/Elements/SimpleSearch
+ M /rt/branches/3.6-RELEASE/html/Elements/Submit
+ M /rt/branches/3.6-RELEASE/html/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Elements/TicketList
+ M /rt/branches/3.6-RELEASE/html/Elements/TitleBox
+ M /rt/branches/3.6-RELEASE/html/Elements/TitleBoxEnd
+ M /rt/branches/3.6-RELEASE/html/Elements/TitleBoxStart
+ M /rt/branches/3.6-RELEASE/html/Elements/ValidateCustomFields
+ M /rt/branches/3.6-RELEASE/html/Helpers/CalPopup.html
+ M /rt/branches/3.6-RELEASE/html/NoAuth/Logout.html
+ M /rt/branches/3.6-RELEASE/html/NoAuth/Reminder.html
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/body.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/footer.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/forms.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/header.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/login.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/main.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/misc.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/nav.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/quickbar.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/ticket.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/titlebox.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/transactions.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/approvals.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/body.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/footer.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/forms.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/header.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/login.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/logo.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/main.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/misc.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/nav.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/quickbar.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/ticket.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/titlebox.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/transactions.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/autohandler
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/print.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/ahah.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/autohandler
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/cascaded.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/class.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/combobox.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/list.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/titlebox-state.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/util.js
+ M /rt/branches/3.6-RELEASE/html/Prefs/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+ M /rt/branches/3.6-RELEASE/html/Prefs/Quicksearch.html
+ M /rt/branches/3.6-RELEASE/html/Prefs/Search.html
+ M /rt/branches/3.6-RELEASE/html/Prefs/SearchOptions.html
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/queue/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/queue/ns
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/comment
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/links
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/merge
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/take
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/transaction/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/user/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/user/ns
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/autohandler
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/dhandler
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/logout
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/search/dhandler
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/search/ticket
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/comment
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/link
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/merge
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+ M /rt/branches/3.6-RELEASE/html/Search/Bulk.html
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+ M /rt/branches/3.6-RELEASE/html/Search/Edit.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/BuildFormatString
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/DisplayOptions
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditFormat
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditQuery
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/NewListActions
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/PickBasics
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/PickCFs
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/PickCriteria
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SearchPrivacy
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SearchesForObject
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectAndOr
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectChartType
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectGroup
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectGroupBy
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectLinks
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectPersonType
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectSearchObject
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectSearchesForObjects
+ M /rt/branches/3.6-RELEASE/html/Search/Results.html
+ M /rt/branches/3.6-RELEASE/html/Search/Results.rdf
+ M /rt/branches/3.6-RELEASE/html/Search/Results.tsv
+ M /rt/branches/3.6-RELEASE/html/Search/Simple.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Attachment/dhandler
+ M /rt/branches/3.6-RELEASE/html/SelfService/Closed.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Create.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/CreateTicketInQueue.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Display.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/GotoTicket
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/Header
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/MyRequests
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/SelfService/Error.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Prefs.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Update.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/index.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Attachment/dhandler
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/AddWatchers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/BulkLinks
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditBasics
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomField
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditDates
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditPeople
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditWatchers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/FindAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Reminders
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowBasics
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowCustomFields
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowDates
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowDependencies
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowHistory
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMembers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMessageHeaders
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMessageStanza
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowPeople
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTime
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowUserEntry
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Ticket/History.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyAll.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyDates.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyLinks.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyPeople.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Reminders.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Update.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Tools/MyDay.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Offline.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/CreatedByDates.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/ResolvedByDates.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/ResolvedByOwner.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/index.html
+ M /rt/branches/3.6-RELEASE/html/Tools/index.html
+ M /rt/branches/3.6-RELEASE/html/User/Delegation.html
+ M /rt/branches/3.6-RELEASE/html/User/Elements/DelegateRights
+ M /rt/branches/3.6-RELEASE/html/User/Elements/GroupTabs
+ M /rt/branches/3.6-RELEASE/html/User/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/User/Groups/Members.html
+ M /rt/branches/3.6-RELEASE/html/User/Groups/Modify.html
+ M /rt/branches/3.6-RELEASE/html/User/Groups/index.html
+ M /rt/branches/3.6-RELEASE/html/User/Prefs.html
+ M /rt/branches/3.6-RELEASE/html/Widgets/ComboBox
+ M /rt/branches/3.6-RELEASE/html/Widgets/SavedSearch
+ M /rt/branches/3.6-RELEASE/html/Widgets/SelectionBox
+ M /rt/branches/3.6-RELEASE/html/Widgets/TitleBox
+ M /rt/branches/3.6-RELEASE/html/Widgets/TitleBoxEnd
+ M /rt/branches/3.6-RELEASE/html/Widgets/TitleBoxStart
+ M /rt/branches/3.6-RELEASE/html/autohandler
+ M /rt/branches/3.6-RELEASE/html/index.html
+ M /rt/branches/3.6-RELEASE/html/l
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ACL.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ACL_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/AutoOpen.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Autoreply.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/EscalatePriority.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Notify.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/NotifyAsComment.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/RecordComment.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/RecordCorrespondence.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/ResolveMembers.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SetPriority.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/UserDefined.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachment.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachments.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attribute.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attributes.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Base.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMember.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMembers.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/AnyTransaction.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/BeforeDue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/Overdue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/OwnerChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/PriorityChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/PriorityExceeds.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/QueueChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/StatusChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/UserDefined.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CurrentUser.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValues.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValues_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFields.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Date.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/EmailParser.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Group.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMember.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMembers.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Group_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Groups.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Groups_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Handle.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/i_default.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/CLI.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email/Auth/GnuPG.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/REST.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Menu/Item.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Menu.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/QueryBuilder/Tree.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/QueryBuilder.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Standalone.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Link.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Link_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Links.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Links_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomField.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValues.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomField_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFields.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFields_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principals.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principals_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queues.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queues_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Reminders.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets/Entry.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearch.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearches.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrip.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripAction.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripActions.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripCondition.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripConditions.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrips.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/ActiveTicketsInQueue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/FromSQL.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/Googleish.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SearchBuilder.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/System.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Template.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Template_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Templates.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Templates_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transactions.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/base.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/t.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/User.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Users.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Users_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+ M /rt/branches/3.6-RELEASE/sbin/extract-message-catalog
+ M /rt/branches/3.6-RELEASE/sbin/extract_pod_tests
+ M /rt/branches/3.6-RELEASE/sbin/factory
+ M /rt/branches/3.6-RELEASE/sbin/license_tag
+ M /rt/branches/3.6-RELEASE/sbin/regression_harness
+ M /rt/branches/3.6-RELEASE/sbin/rt-dump-database.in
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r56008@pinglin: jesse | 2007-04-24 13:17:46 -0400
+ * license-tagger update
+
+------------------------------------------------------------------------
+r7669 | jesse | 2007-04-24 09:28:24 -0400 (Tue, 24 Apr 2007) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/DisplayOptions
+
+ r55983@pinglin: jesse | 2007-04-24 09:24:25 -0400
+ The selected option of the "OrderBy"-selectbox cannot be saved in /rt/Prefs/SearchOptions.html if only one (of possibly 4) sort field is selected and other are set to none.
+ patch: <http://page.mi.fu-berlin.de/~pape/rt3/patches/rt/search_prefs_order_cannot_be_saved.patch>
+
+ -Dr. Dirk Pape
+
+------------------------------------------------------------------------
+r7560 | jesse | 2007-04-18 20:53:34 -0400 (Wed, 18 Apr 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+
+ r55725@241: jesse | 2007-04-18 16:28:17 -0400
+ * We can't use encoding functions without using Encode
+
+------------------------------------------------------------------------
+r7559 | jesse | 2007-04-18 20:53:25 -0400 (Wed, 18 Apr 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Tools/MyDay.html
+
+ r55724@241: jesse | 2007-04-18 16:28:00 -0400
+ * Made MyDay.html actually work. Based in spirit on a patch from Chris Hobbs
+
+------------------------------------------------------------------------
+r7558 | jesse | 2007-04-18 20:52:37 -0400 (Wed, 18 Apr 2007) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+
+ r55723@241: jesse | 2007-04-18 15:54:59 -0400
+ RT-Ticket: 8160
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied patch from jedik to enable UTF8 Passwords
+
+
+------------------------------------------------------------------------
+r7557 | jesse | 2007-04-18 20:52:25 -0400 (Wed, 18 Apr 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/history
+
+ r55722@241: jesse | 2007-04-18 15:52:43 -0400
+ * Patch from Philip Kime at Shopzilla to prevent accidental display of transactions from the wrong ticket when you explicitly specify a ticket id and a transaction id.
+
+------------------------------------------------------------------------
+r7556 | jesse | 2007-04-18 20:52:14 -0400 (Wed, 18 Apr 2007) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ A /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/transaction
+ A /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/transaction/default
+
+ r55721@241: jesse | 2007-04-18 15:47:20 -0400
+ Patch from Philip Kime to allow transactions to be displayed by range
+
+
+
+
+------------------------------------------------------------------------
+r7555 | jesse | 2007-04-18 20:51:20 -0400 (Wed, 18 Apr 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+ r55720@241: jesse | 2007-04-18 15:37:35 -0400
+ * Attaching files could sometimes eat the messages in ticket replies.
+ Patch from Harry.Bochner@biogenidec.com
+
+------------------------------------------------------------------------
+r7547 | falcone | 2007-04-18 17:10:45 -0400 (Wed, 18 Apr 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/link
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+ r18127@ketch: falcone | 2007-04-18 17:08:59 -0400
+ * make bin/rt link work and fix the associated tests
+
+------------------------------------------------------------------------
+r7546 | falcone | 2007-04-18 17:10:18 -0400 (Wed, 18 Apr 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/02basic_web.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23-web_attachments.t
+
+ r18126@ketch: falcone | 2007-04-18 15:40:09 -0400
+ * WWW::Mechanize 1.22 removed the form() method
+ 02basic_web and 21query_builder have different problems with 1.22
+
+------------------------------------------------------------------------
+r7472 | jesse | 2007-04-05 21:23:33 -0400 (Thu, 05 Apr 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+
+ r54515@dhcp207: jesse | 2007-04-06 10:22:47 +0900
+ * Typo in a debug message. Thanks to philip kime
+
+------------------------------------------------------------------------
+r7341 | jesse | 2007-03-26 03:26:10 -0400 (Mon, 26 Mar 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+ r54212@pinglin: jesse | 2007-03-26 00:24:20 -0700
+ * Allow merging resolved tickets
+
+------------------------------------------------------------------------
+r7314 | jesse | 2007-03-21 23:18:19 -0400 (Wed, 21 Mar 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+ r53781@pinglin: jesse | 2007-03-21 23:17:52 -0400
+ * Mismerge
+
+------------------------------------------------------------------------
+r7313 | jesse | 2007-03-21 23:16:05 -0400 (Wed, 21 Mar 2007) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r53777@pinglin: jesse | 2007-03-21 23:15:06 -0400
+ RT-Ticket: 8256
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * New API from pere@hungry.com to Avoid mail loop when using RT-Extension-CommandByMail
+
+
+------------------------------------------------------------------------
+r7312 | jesse | 2007-03-21 23:15:38 -0400 (Wed, 21 Mar 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+
+------------------------------------------------------------------------
+r7310 | ruz | 2007-03-21 14:08:00 -0400 (Wed, 21 Mar 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+* revert not intentional commit
+------------------------------------------------------------------------
+r7309 | jesse | 2007-03-21 13:27:10 -0400 (Wed, 21 Mar 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+
+ r53758@pinglin: jesse | 2007-03-21 11:16:26 -0400
+ * Danish PO nits.
+
+------------------------------------------------------------------------
+r7308 | falcone | 2007-03-20 17:49:03 -0400 (Tue, 20 Mar 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+ r17520@ketch: falcone | 2007-03-20 17:47:46 -0400
+ * if you set $RT::WebSessionClass, %backends will be empty and the
+ tie will fail. This lets you use Apache::Session::Oracle
+
+------------------------------------------------------------------------
+r7307 | jesse | 2007-03-20 13:19:25 -0400 (Tue, 20 Mar 2007) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/sv.po
+
+ r53731@pinglin: jesse | 2007-03-20 13:18:35 -0400
+ * New Swedish translation and updated Danish translation contributed by
+ Brian Kjelin Olsen on behalf of Schilling A/S
+
+
+------------------------------------------------------------------------
+r7264 | jesse | 2007-03-19 01:23:48 -0400 (Mon, 19 Mar 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectGroups
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectUsers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditPeople
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditWatchers
+
+ r53625@pinglin: jesse | 2007-03-19 01:21:53 -0400
+ * XHTML cleanup from Dirk Pape
+
+
+------------------------------------------------------------------------
+r7254 | jesse | 2007-03-15 18:52:21 -0400 (Thu, 15 Mar 2007) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+
+ r53492@124: jesse | 2007-03-15 18:52:01 -0400
+ * RT now complains when you try to start the application server with
+ either too-old a perl or a broken version of Scalar::Util.
+
+
+------------------------------------------------------------------------
+r7253 | jesse | 2007-03-15 17:24:46 -0400 (Thu, 15 Mar 2007) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+ r53457@124: jesse | 2007-03-15 17:18:44 -0400
+ RT-Ticket: 8186
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch for a possible race condition in the "SetOwner" routine that could be triggered when two users tried to take a ticket at the same time. Thanks to Todd Chapman!
+
+
+------------------------------------------------------------------------
+r7251 | jesse | 2007-03-14 14:23:59 -0400 (Wed, 14 Mar 2007) | 11 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r53425@pinglin: jesse | 2007-03-14 13:40:45 -0400
+ RT-Ticket: 8252
+ RT-Status: resolved
+ RT-Update: correspond
+
+
+ * Include subject of bounced messages when mailing the RT owner about a mail error <pere@hungry.com>
+
+
+
+
+------------------------------------------------------------------------
+r7250 | jesse | 2007-03-14 14:23:00 -0400 (Wed, 14 Mar 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r48450@pinglin: jesse | 2007-02-21 13:56:09 -0500
+ * Move RT's detection of incoming mail that may be a loop or autogenerated to better catch such cases before certain "Permission denied" messages are generated.
+
+------------------------------------------------------------------------
+r7051 | ruz | 2007-02-22 18:37:12 -0500 (Thu, 22 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Template_Overlay.pm
+
+* use local during filling the T:: namespace for a template processing,
+ otherwise we have a global reference to a ticket and destruction is
+ delayed which hurts TransactionBatch stage
+------------------------------------------------------------------------
+r7046 | falcone | 2007-02-21 14:47:16 -0500 (Wed, 21 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r16829@ketch: falcone | 2007-02-21 14:46:40 -0500
+ * 1.10 has been bumped to 2.02 because of version and packaging problems
+
+------------------------------------------------------------------------
+r7043 | ruz | 2007-02-21 12:31:37 -0500 (Wed, 21 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/QueueSummary
+
+* fix problems in Quicksearch when queue name has the ampersand char
+------------------------------------------------------------------------
+r7040 | ruz | 2007-02-20 20:30:13 -0500 (Tue, 20 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+* fix a bug with saved searches on rt at glance when summary rows preference
+ and rows per page of the search are different values
+* run callback a little bit earlier
+------------------------------------------------------------------------
+r7039 | ruz | 2007-02-20 19:58:29 -0500 (Tue, 20 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+
+* backport 'RT at glance with one column' feature from 3.7
+------------------------------------------------------------------------
+r7031 | ruz | 2007-02-19 03:48:57 -0500 (Mon, 19 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+
+* better error propagation, thanks to Pholip Kime.
+------------------------------------------------------------------------
+r7028 | ruz | 2007-02-18 20:57:48 -0500 (Sun, 18 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+* fix errors propagation. Thanks to Philip Kime.
+------------------------------------------------------------------------
+r7027 | ruz | 2007-02-18 20:47:24 -0500 (Sun, 18 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+* resolve a clobbered regex-match $1 by stashing the principal ID in a
+ temporary variable. Thanks to Richard Harman.
+* type was set incorrect in a case of multiple parameters
+------------------------------------------------------------------------
+r7023 | jesse | 2007-02-18 16:35:45 -0500 (Sun, 18 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ A /rt/branches/3.6-RELEASE/lib/RT/I18N/sv.po
+
+ r48373@237: jesse | 2007-02-18 13:36:32 -0600
+ * Swedish translation from Brian Kjelin Olsen and Schilling A/S
+
+------------------------------------------------------------------------
+r7019 | ruz | 2007-02-15 14:01:12 -0500 (Thu, 15 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransaction
+
+* use a local array of attachments we have instead of $Transaction->Attachments call
+------------------------------------------------------------------------
+r7018 | ruz | 2007-02-15 12:00:52 -0500 (Thu, 15 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/RT__Ticket/ColumnMap
+
+* add forgoten ; after anon subs
+------------------------------------------------------------------------
+r7009 | ruz | 2007-02-14 16:03:14 -0500 (Wed, 14 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/RT__Ticket/ColumnMap
+
+* get rid of the latest named functions in our comps
+------------------------------------------------------------------------
+r7008 | ruz | 2007-02-14 13:39:59 -0500 (Wed, 14 Feb 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+* run callback before redirect as people are using it for updates
+ when redirect hides all arguments we had
+------------------------------------------------------------------------
+r7007 | falcone | 2007-02-14 13:09:29 -0500 (Wed, 14 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r16747@ketch: falcone | 2007-02-14 13:07:58 -0500
+ * bump version requirement since we've fixed a bunch of bugs since 1.3
+
+------------------------------------------------------------------------
+r6999 | ruz | 2007-02-13 14:37:01 -0500 (Tue, 13 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+
+* fix all failing tests for searches by watchers
+------------------------------------------------------------------------
+r6998 | ruz | 2007-02-13 12:03:06 -0500 (Tue, 13 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/22search_tix_by_watcher.t
+
+* improve tests for lookups by watchers
+------------------------------------------------------------------------
+r6981 | ruz | 2007-02-09 23:52:57 -0500 (Fri, 09 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+* oops, we use ISA later
+------------------------------------------------------------------------
+r6966 | ruz | 2007-02-07 22:45:12 -0500 (Wed, 07 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Attributes_Overlay.pm
+
+* typo
+------------------------------------------------------------------------
+r6965 | ruz | 2007-02-07 20:45:29 -0500 (Wed, 07 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+* we depend on perl 5.8 so we don't need Encode::compat at all
+------------------------------------------------------------------------
+r6964 | ruz | 2007-02-07 20:20:57 -0500 (Wed, 07 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* we don't depend on MLDBM anymore and I don't recall we did.
+------------------------------------------------------------------------
+r6948 | ruz | 2007-02-06 18:27:46 -0500 (Tue, 06 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* backport from 3.7: convert 'sub _' to 'sub text_to_hash'
+------------------------------------------------------------------------
+r6947 | ruz | 2007-02-06 17:41:59 -0500 (Tue, 06 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* delete we don't use from dependencies, I've checked that Mason 1.23 depends
+ on this modules and versions it requires are equal or greater than we had
+
+------------------------------------------------------------------------
+r6920 | kevinr | 2007-02-04 19:09:35 -0500 (Sun, 04 Feb 2007) | 9 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Simple.html
+
+ r18330@sad-girl-in-snow: kevinr | 2007-02-04 19:08:22 -0500
+ RT-Ticket: 8169
+ RT-Status: open
+ RT-Update: comment
+
+ Added a missing </div> to the Simple Search page. Thanks to Arran Cudbard-Bell
+ for the catch.
+
+
+------------------------------------------------------------------------
+r6919 | ruz | 2007-02-03 17:30:24 -0500 (Sat, 03 Feb 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+* don't push transactions into batch unless CommitScrips is true,
+ this fixes a bug: users click Reply button and we fire a correspond
+ scrip that is in the batch stage
+------------------------------------------------------------------------
+r6881 | ruz | 2007-02-01 22:43:12 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* make defaults actually defaults
+------------------------------------------------------------------------
+r6880 | ruz | 2007-02-01 22:28:09 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* actually skip sections user didn't request
+------------------------------------------------------------------------
+r6879 | ruz | 2007-02-01 22:11:18 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* we don't use FreezeThaw module for a long time
+------------------------------------------------------------------------
+r6878 | ruz | 2007-02-01 22:07:26 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* update mailgate's deps
+------------------------------------------------------------------------
+r6877 | ruz | 2007-02-01 22:04:49 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* update CLI deps
+------------------------------------------------------------------------
+r6876 | ruz | 2007-02-01 22:00:20 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/configure.ac
+
+* add --with-standalone option to configure script
+------------------------------------------------------------------------
+r6875 | ruz | 2007-02-01 21:59:03 -0500 (Thu, 01 Feb 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* add --with-standalone option
+* as well fill defaults for dev mode and standalone within a configure script
+------------------------------------------------------------------------
+r6874 | ruz | 2007-02-01 21:57:03 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* Regexp::Common is required by core(QB/TicketSQL)
+------------------------------------------------------------------------
+r6873 | ruz | 2007-02-01 21:55:59 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* we don't use this module
+------------------------------------------------------------------------
+r6872 | ruz | 2007-02-01 21:55:19 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* Term::ReadKey is used in CLI only
+------------------------------------------------------------------------
+r6871 | ruz | 2007-02-01 21:54:28 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* Test::Inline is only dev dependancy
+------------------------------------------------------------------------
+r6870 | ruz | 2007-02-01 21:51:36 -0500 (Thu, 01 Feb 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* typo
+------------------------------------------------------------------------
+r6869 | kevinr | 2007-02-01 19:36:56 -0500 (Thu, 01 Feb 2007) | 7 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/MyRequests
+
+ r18262@sad-girl-in-snow: kevinr | 2007-02-01 19:34:35 -0500
+ RT-Ticket: 8159
+ RT-Status: resolved
+ RT-Update: respond
+
+ Use Watcher instead for better performance. Thanks to Dirk Pape for the catch.
+
+------------------------------------------------------------------------
+r6868 | kevinr | 2007-02-01 19:35:48 -0500 (Thu, 01 Feb 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Tabs
+
+ r18261@sad-girl-in-snow: kevinr | 2007-02-01 19:29:44 -0500
+ * Put the search navigation in the right submenu. Thanks to Dirk Pape
+ for the patch.
+
+------------------------------------------------------------------------
+r6813 | ruz | 2007-01-26 10:35:53 -0500 (Fri, 26 Jan 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/22search_tix_by_watcher.t
+
+* add test that fails due to bug in searches by watcher
+------------------------------------------------------------------------
+r6812 | ruz | 2007-01-26 10:34:57 -0500 (Fri, 26 Jan 2007) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+* if we have subject line regexp then we should use it during mail sending.
+ This will allow us to send email with any token we want that matches the
+ regexp, by defining it in a template. So now it's possible to use:
+
+ Subject: [not-rtname-token-that-match-re #{ $Ticket->id }] ...
+
+------------------------------------------------------------------------
+r6811 | jesse | 2007-01-26 07:21:23 -0500 (Fri, 26 Jan 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r21377@hualien: jesse | 2007-01-26 20:20:50 +0800
+ * Switch web-form based email attachments to in-core storage for better persistence across http hits
+
+------------------------------------------------------------------------
+r6809 | jesse | 2007-01-26 04:15:02 -0500 (Fri, 26 Jan 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/22search_tix_by_txn.t
+
+ r21344@hualien: jesse | 2007-01-26 17:14:09 +0800
+ * Test script 22: search tix by txn had no plan and failed in asian timezones due to a startup precedence bug
+
+------------------------------------------------------------------------
+r6795 | clkao | 2007-01-23 10:42:03 -0500 (Tue, 23 Jan 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField_Overlay.pm
+
+Fix a bug in CustomField->Create where assigned Queue is not properly
+associated with the newly created CF. This was previously not exposed
+because rt-setup-database is always supplying queue id to this method.
+
+------------------------------------------------------------------------
+r6794 | ruz | 2007-01-23 05:48:07 -0500 (Tue, 23 Jan 2007) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/MyRequests
+
+SelfService/Elements/MyRequests
+* backport refactoring from 3.7
+------------------------------------------------------------------------
+r6789 | ruz | 2007-01-20 23:50:29 -0500 (Sat, 20 Jan 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+* that was wrong idea to run next mail plugins for some action
+ if the current plugin said that everything was done (returned
+ status -2).
+------------------------------------------------------------------------
+r6788 | ruz | 2007-01-20 22:11:00 -0500 (Sat, 20 Jan 2007) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearch.pm
+
+* add warnings
+------------------------------------------------------------------------
+r6783 | jesse | 2007-01-19 14:33:56 -0500 (Fri, 19 Jan 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+
+ r21119@hualien: root | 2007-01-19 14:31:43 -0500
+ * Updates to search unlimited issue from todd chapman
+
+------------------------------------------------------------------------
+r6782 | jesse | 2007-01-19 14:16:40 -0500 (Fri, 19 Jan 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Results.html
+
+ r21117@hualien: jesse | 2007-01-19 14:16:22 -0500
+ * Todd chapman pointed out a typo
+
+------------------------------------------------------------------------
+r6781 | jesse | 2007-01-19 14:05:37 -0500 (Fri, 19 Jan 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Results.html
+
+ r21115@hualien: jesse | 2007-01-19 14:04:50 -0500
+ * A fix for "unlimited rows" searches from James Bunch
+
+------------------------------------------------------------------------
+r6729 | jesse | 2007-01-10 01:53:50 -0500 (Wed, 10 Jan 2007) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r46883@pinglin: jesse | 2007-01-10 01:53:34 -0500
+ * If the user submits two "add watcher" records with the same http arg name, treat them as separate.
+
+
+------------------------------------------------------------------------
+r6712 | jesse | 2007-01-08 15:55:52 -0500 (Mon, 08 Jan 2007) | 7 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/autohandler
+
+ r46859@pinglin: jesse | 2007-01-08 15:55:35 -0500
+ The autohandler should *redirect* unprivileged users from
+ Ticket/Display.html to the Self-Service interface. Current code displays
+ SS page under non-SS URL, which causes the "Reply" links to go to
+ Ticket/Update.html, which in turn displays the SS home page.
+ -David Chandek-Stark <david.chandek.stark@duke.edu>
+
+------------------------------------------------------------------------
+r6703 | jesse | 2007-01-02 22:30:46 -0500 (Tue, 02 Jan 2007) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/SelfService/Display.html
+
+ r46723@pinglin: jesse | 2007-01-02 22:30:09 -0500
+ * Forward port the fix from 3.4 that lets users create tickets with custom field values in SelfService
+
+------------------------------------------------------------------------
+r6691 | ruz | 2006-12-23 21:19:47 -0500 (Sat, 23 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+
+* adjust error message so it's clear that we report an user's id
+------------------------------------------------------------------------
+r6690 | ruz | 2006-12-22 19:23:37 -0500 (Fri, 22 Dec 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/SelfService/Display.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+* Copy rediect feature from Ticket/Display to SelfService/Display
+ Thanks to doogles and Todd.
+------------------------------------------------------------------------
+r6689 | ruz | 2006-12-22 19:05:47 -0500 (Fri, 22 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/QueueSummary
+
+* get rid of 1 query per queue in html/Elements/QueueSummary component
+------------------------------------------------------------------------
+r6687 | ruz | 2006-12-21 13:32:54 -0500 (Thu, 21 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+* bump version, 3.6.3
+------------------------------------------------------------------------
+r6683 | ruz | 2006-12-20 23:10:24 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+
+* de.po update
+------------------------------------------------------------------------
+r6681 | ruz | 2006-12-20 17:00:19 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+* bum version, 3.6.3rc4
+------------------------------------------------------------------------
+r6680 | ruz | 2006-12-20 16:57:52 -0500 (Wed, 20 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/en.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/es.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fi.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fr.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/he.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/hu.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/id.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/it.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/nl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/no.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ru.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_tw.po
+
+I18N
+* regenerate *.po files
+* several messages gone, several new
+------------------------------------------------------------------------
+r6679 | ruz | 2006-12-20 16:46:49 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/bin/rt-crontool.in
+
+* fix a typo
+------------------------------------------------------------------------
+r6678 | ruz | 2006-12-20 16:40:14 -0500 (Wed, 20 Dec 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fi.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fr.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/he.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/hu.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/id.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/it.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/nl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_tw.po
+
+I18N
+* drop newlines in messages
+* get rid of some errors
+** msgfmt --statistics ... is now almost happy
+------------------------------------------------------------------------
+r6677 | ruz | 2006-12-20 16:37:35 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Search/Simple.html
+
+* remove newlines from text that should be localized, we couldn't track this well :(
+------------------------------------------------------------------------
+r6676 | ruz | 2006-12-20 16:06:39 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/es.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fi.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fr.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/he.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/hu.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/id.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/it.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/nl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_tw.po
+
+* delete crap from *.po files
+------------------------------------------------------------------------
+r6675 | ruz | 2006-12-20 15:33:33 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/en.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/es.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/he.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/hu.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/it.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/nl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/no.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_tw.po
+
+* use 'UTF-8' as it's standard name
+------------------------------------------------------------------------
+r6674 | ruz | 2006-12-20 15:30:38 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/en.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/es.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fi.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fr.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/he.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/hu.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/id.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/it.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/nl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/no.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ru.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_tw.po
+
+* lists.bestpractical.com is prefered
+------------------------------------------------------------------------
+r6673 | ruz | 2006-12-20 15:27:15 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.po
+
+* fix issues in cs.po
+------------------------------------------------------------------------
+r6672 | ruz | 2006-12-20 15:20:02 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/no.po
+
+* no.po fixes, thanks to Petter Reinholdtsen
+------------------------------------------------------------------------
+r6670 | ruz | 2006-12-20 14:28:50 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+* bump version, 3.6.3rc3
+------------------------------------------------------------------------
+r6669 | ruz | 2006-12-20 13:59:40 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+* don't write an email record txn if there were no recipients
+------------------------------------------------------------------------
+r6668 | ruz | 2006-12-20 13:03:52 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+
+* fix de.po
+------------------------------------------------------------------------
+r6667 | ruz | 2006-12-20 12:47:16 -0500 (Wed, 20 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+
+* load module before using
+------------------------------------------------------------------------
+r6660 | ruz | 2006-12-19 17:28:14 -0500 (Tue, 19 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ru.po
+
+* update russian translation. Thanks to Andrew.
+------------------------------------------------------------------------
+r6658 | jesse | 2006-12-19 16:33:18 -0500 (Tue, 19 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r46287@pinglin: jesse | 2006-12-19 16:32:47 -0500
+ * rc2
+
+------------------------------------------------------------------------
+r6657 | ruz | 2006-12-19 16:22:29 -0500 (Tue, 19 Dec 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+* add %Override option to html/Elements/ShowSearch
+* html/Elements/MyRT should limit results according to user's preferences
+------------------------------------------------------------------------
+r6655 | jesse | 2006-12-19 15:28:16 -0500 (Tue, 19 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r46282@pinglin: jesse | 2006-12-19 15:24:48 -0500
+ * RT 3.6.3rc1
+
+------------------------------------------------------------------------
+r6654 | ruz | 2006-12-19 15:12:17 -0500 (Tue, 19 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/TicketList
+
+* workaround problems with Page = '' or undef
+------------------------------------------------------------------------
+r6653 | ruz | 2006-12-19 14:59:18 -0500 (Tue, 19 Dec 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/DisplayOptions
+
+* always select(mark an option as selected) ASC or DESC value
+* drop aninit warn
+------------------------------------------------------------------------
+r6652 | ruz | 2006-12-19 14:49:02 -0500 (Tue, 19 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectResultsPerPage
+
+* if %ARGS has a key but value is undefefined then default values
+ in the <%ARGS> section are ignored, but we want 50 rows per page
+ by default
+------------------------------------------------------------------------
+r6651 | ruz | 2006-12-19 14:40:03 -0500 (Tue, 19 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+* fix issue with saved searches on home page after upgrade from previouse version of RT
+------------------------------------------------------------------------
+r6650 | jesse | 2006-12-19 12:24:06 -0500 (Tue, 19 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/index.html
+
+ r46270@pinglin: jesse | 2006-12-19 12:23:55 -0500
+ * The clickable link to manage "RT at a glance" had the wrong path. Thanks to John Arends
+
+------------------------------------------------------------------------
+r6648 | jesse | 2006-12-18 17:32:04 -0500 (Mon, 18 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r46246@pinglin: jesse | 2006-12-18 17:28:32 -0500
+ * 3.6.2 final release
+
+------------------------------------------------------------------------
+r6647 | jesse | 2006-12-18 17:29:40 -0500 (Mon, 18 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+
+ r46168@pinglin: jesse | 2006-12-14 23:38:55 -0500
+ * Beter quoting on indexes for oracle schema. Should make 9i happier
+
+------------------------------------------------------------------------
+r6646 | falcone | 2006-12-18 16:14:12 -0500 (Mon, 18 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+ r15224@ketch: falcone | 2006-12-18 16:13:31 -0500
+ * use a proper path to the bplogo.gif file
+
+------------------------------------------------------------------------
+r6633 | falcone | 2006-12-15 10:44:27 -0500 (Fri, 15 Dec 2006) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/attachments
+ A /rt/branches/3.6-RELEASE/lib/t/data/lorem-ipsum
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+ r15177@ketch: falcone | 2006-12-15 10:43:44 -0500
+ * make the command line client remove the trailing newline if we're given non-text content
+ * if we're returning just a non-text attachment from REST, set the ContentType
+ * un-TODO tests of adding attachments through the command line interface
+ * lorem-ipsum simple test attachment text
+
+------------------------------------------------------------------------
+r6632 | jesse | 2006-12-14 23:39:19 -0500 (Thu, 14 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/schema.Oracle
+
+ r46168@dhcp64-134-35-110: jesse | 2006-12-14 20:38:55 -0800
+ * Beter quoting on indexes for oracle schema. Should make 9i happier
+
+------------------------------------------------------------------------
+r6618 | jesse | 2006-12-11 11:30:29 -0500 (Mon, 11 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r46101@245: jesse | 2006-12-11 11:30:08 -0500
+ * rc5
+
+------------------------------------------------------------------------
+r6595 | clkao | 2006-12-07 20:51:31 -0500 (Thu, 07 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/MyRT.html
+
+Carry current user id in the reset form for Admin/Users/MyRT.html.
+------------------------------------------------------------------------
+r6594 | clkao | 2006-12-07 20:28:29 -0500 (Thu, 07 Dec 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/aclocal.m4
+
+Merge r6433 from 3.7 branch. This fixes inplace docroot problem.
+------------------------------------------------------------------------
+r6592 | jesse | 2006-12-06 15:39:13 -0500 (Wed, 06 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r45881@pinglin: jesse | 2006-12-06 15:38:58 -0500
+ * rc4
+
+------------------------------------------------------------------------
+r6591 | jesse | 2006-12-06 14:18:48 -0500 (Wed, 06 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+ r45870@114: jesse | 2006-12-06 14:17:20 -0500
+ * Let users add non-predefined searches saved by the superuser to RT::System's saved searches to their homepages
+
+------------------------------------------------------------------------
+r6590 | jesse | 2006-12-06 14:18:35 -0500 (Wed, 06 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/Modify.html
+
+ r45869@114: jesse | 2006-12-06 14:16:40 -0500
+ * Allow admins to set custom field values on user create.
+
+------------------------------------------------------------------------
+r6589 | jesse | 2006-12-06 14:17:52 -0500 (Wed, 06 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+
+ r45868@114: jesse | 2006-12-06 13:29:38 -0500
+ * perltidy
+
+------------------------------------------------------------------------
+r6588 | jesse | 2006-12-06 14:17:36 -0500 (Wed, 06 Dec 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectBoolean
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/DisplayOptions
+
+ r45867@114: jesse | 2006-12-06 13:29:07 -0500
+ * Warning avoidance
+
+
+------------------------------------------------------------------------
+r6582 | jesse | 2006-12-04 13:20:08 -0500 (Mon, 04 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r45802@pinglin: jesse | 2006-12-04 13:19:38 -0500
+ * rc3
+
+------------------------------------------------------------------------
+r6581 | jesse | 2006-12-01 14:45:53 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/index.html
+
+ r45701@pinglin: jesse | 2006-12-01 14:45:41 -0500
+ * Added a listing for "MyRT" to the actual displayed global list
+
+------------------------------------------------------------------------
+r6580 | jesse | 2006-12-01 14:19:52 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/00-mason-syntax.t
+
+ r45699@pinglin: jesse | 2006-12-01 14:12:46 -0500
+ * skip backup and .rej files when testing mason syntax
+
+------------------------------------------------------------------------
+r6579 | jesse | 2006-12-01 14:01:52 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/User/Prefs.html
+
+ r45679@pinglin: jesse | 2006-12-01 13:53:39 -0500
+ * Added a new "end of form" callback to user preferences
+
+------------------------------------------------------------------------
+r6578 | jesse | 2006-12-01 14:00:39 -0500 (Fri, 01 Dec 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+ r45543@pinglin (orig r6545): jesse | 2006-11-28 18:34:51 -0500
+ r45542@64: jesse | 2006-11-28 18:34:07 -0500
+ * Refactoring to add a method to just _send_ a MIME::Entity as email
+
+
+------------------------------------------------------------------------
+r6577 | jesse | 2006-12-01 14:00:23 -0500 (Fri, 01 Dec 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+ r45540@pinglin (orig r6544): jesse | 2006-11-28 17:16:13 -0500
+ r45539@pinglin: jesse | 2006-11-28 17:15:57 -0500
+ * Refactor the recording of outgoing messages so we can massage the one we send but not the one we record
+
+
+------------------------------------------------------------------------
+r6576 | jesse | 2006-12-01 13:59:58 -0500 (Fri, 01 Dec 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ M /rt/branches/3.6-RELEASE/html/Widgets/SavedSearch
+
+ r45536@pinglin (orig r6543): jesse | 2006-11-28 15:30:14 -0500
+ r45535@pinglin: jesse | 2006-11-28 15:29:55 -0500
+ * Allow a title on the saved searches dropdown
+
+
+------------------------------------------------------------------------
+r6575 | jesse | 2006-12-01 13:59:26 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+ r45534@pinglin (orig r6542): clkao | 2006-11-28 10:56:23 -0500
+ Enable object custom field by default from rt-setup-database.
+
+------------------------------------------------------------------------
+r6574 | jesse | 2006-12-01 13:59:10 -0500 (Fri, 01 Dec 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+ r45533@pinglin (orig r6541): clkao | 2006-11-28 08:38:46 -0500
+ Allow initdb to insert non-queue custom fields. The logic for
+ looking up queue is in CustomField->Create already.
+
+
+------------------------------------------------------------------------
+r6573 | jesse | 2006-12-01 13:58:44 -0500 (Fri, 01 Dec 2006) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearch.pm
+
+ r45314@pinglin (orig r6511): clkao | 2006-11-23 06:28:14 -0500
+ Save searches in SaveSearch, not "Search - ..." for RT::System.
+ Note that users must have ShowSavedSearch for the things they have
+ on portlet to work.
+
+
+------------------------------------------------------------------------
+r6572 | jesse | 2006-12-01 13:58:09 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Widgets/SavedSearch
+
+ r45312@pinglin (orig r6509): clkao | 2006-11-23 05:45:58 -0500
+ remove extra button
+
+------------------------------------------------------------------------
+r6571 | jesse | 2006-12-01 13:57:54 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ A /rt/branches/3.6-RELEASE/html/Widgets/SavedSearch
+
+ r45311@pinglin (orig r6508): clkao | 2006-11-23 05:24:36 -0500
+ Support load and save for charts, in a separate widget SavedSearch.
+
+------------------------------------------------------------------------
+r6570 | jesse | 2006-12-01 13:57:27 -0500 (Fri, 01 Dec 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+
+ r45310@pinglin (orig r6507): clkao | 2006-11-23 03:50:33 -0500
+ Cluster the processing of saved search related management together.
+
+
+------------------------------------------------------------------------
+r6569 | jesse | 2006-12-01 13:56:55 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectSearchesForObjects
+
+ r45294@pinglin (orig r6504): clkao | 2006-11-22 14:23:23 -0500
+ simple saved chart ui.
+
+------------------------------------------------------------------------
+r6568 | jesse | 2006-12-01 13:56:25 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/UserTabs
+ A /rt/branches/3.6-RELEASE/html/Admin/Users/MyRT.html
+
+ r45293@pinglin (orig r6503): clkao | 2006-11-22 12:39:59 -0500
+ Allow editing myrt prefs for individual user.
+
+------------------------------------------------------------------------
+r6567 | jesse | 2006-12-01 13:56:07 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+
+ r45292@pinglin (orig r6502): clkao | 2006-11-22 09:18:33 -0500
+ Fix missing >
+
+------------------------------------------------------------------------
+r6566 | jesse | 2006-12-01 13:55:27 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+
+ r45290@pinglin (orig r6500): clkao | 2006-11-22 07:27:01 -0500
+ Use RT::SavedSearch for Chart saving as well.
+
+------------------------------------------------------------------------
+r6565 | jesse | 2006-12-01 13:55:01 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearch.pm
+
+ r45289@pinglin (orig r6499): clkao | 2006-11-22 07:21:00 -0500
+ Refactor Search/Build.html to save searches using RT::SavedSearch.
+
+------------------------------------------------------------------------
+r6564 | jesse | 2006-12-01 13:54:28 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+
+ r45288@pinglin (orig r6498): clkao | 2006-11-22 06:57:24 -0500
+ Must save searches on RT::System with 'Search - ' convention.
+
+------------------------------------------------------------------------
+r6563 | jesse | 2006-12-01 13:54:14 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+
+ r45287@pinglin (orig r6497): clkao | 2006-11-22 06:33:42 -0500
+ Allow resetting myrt.
+
+------------------------------------------------------------------------
+r6562 | jesse | 2006-12-01 13:53:42 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SystemTabs
+ A /rt/branches/3.6-RELEASE/html/Admin/Global/MyRT.html
+
+ r45286@pinglin (orig r6496): clkao | 2006-11-22 06:17:20 -0500
+ Global/Admin/MyRT.html for configure global myrt.
+
+------------------------------------------------------------------------
+r6561 | jesse | 2006-12-01 13:53:07 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ A /rt/branches/3.6-RELEASE/html/Admin/Elements/ConfigureMyRT
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+
+ r45285@pinglin (orig r6495): clkao | 2006-11-22 06:04:43 -0500
+ Refactor MyRT configuration to an element.
+
+------------------------------------------------------------------------
+r6560 | jesse | 2006-12-01 13:52:28 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+
+ r45284@pinglin (orig r6494): clkao | 2006-11-22 05:28:14 -0500
+ Display search type as part of the portlet name in MyRT prefs.
+
+------------------------------------------------------------------------
+r6559 | jesse | 2006-12-01 13:51:59 -0500 (Fri, 01 Dec 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r45283@pinglin (orig r6493): clkao | 2006-11-22 04:56:50 -0500
+ Fix chart saved in rt::system for selection in MyRT preferences.
+
+
+------------------------------------------------------------------------
+r6558 | jesse | 2006-12-01 13:51:45 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearch.pm
+
+ r45282@pinglin (orig r6492): clkao | 2006-11-22 04:44:13 -0500
+ Move load_container_object into RT::SavedSearch.
+
+------------------------------------------------------------------------
+r6557 | jesse | 2006-12-01 13:51:26 -0500 (Fri, 01 Dec 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearches.pm
+
+ r45281@pinglin (orig r6491): clkao | 2006-11-22 04:38:43 -0500
+ RT::SavedSearches::_GetObject is the same as RT::SavedSearch::_GetObject,
+ apart from error messages.
+
+------------------------------------------------------------------------
+r6556 | jesse | 2006-12-01 13:51:12 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearches.pm
+
+ r45280@pinglin (orig r6490): clkao | 2006-11-22 03:57:46 -0500
+ refactor the code for loading saved search privacies to RT::SavedSearches.
+
+------------------------------------------------------------------------
+r6555 | jesse | 2006-12-01 13:50:35 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+ r45278@pinglin (orig r6488): clkao | 2006-11-22 03:08:27 -0500
+ Don't show edit link when there's no customize url
+
+------------------------------------------------------------------------
+r6554 | jesse | 2006-12-01 13:49:56 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+ r45277@pinglin (orig r6487): clkao | 2006-11-22 03:00:53 -0500
+ portlet link should respect searchtype.
+
+------------------------------------------------------------------------
+r6553 | jesse | 2006-12-01 13:49:44 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+
+ r45276@pinglin (orig r6486): clkao | 2006-11-22 02:22:33 -0500
+ Make the style of chart in myrt consistent.
+
+------------------------------------------------------------------------
+r6552 | jesse | 2006-12-01 13:49:08 -0500 (Fri, 01 Dec 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+ r45274@pinglin (orig r6484): clkao | 2006-11-21 15:20:33 -0500
+ No we don't really want to abort.
+
+------------------------------------------------------------------------
+r6551 | jesse | 2006-12-01 13:48:55 -0500 (Fri, 01 Dec 2006) | 11 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+
+ r45273@pinglin (orig r6483): clkao | 2006-11-21 14:31:42 -0500
+ First cut of savable chart.
+
+ * Search/Chart.html: build form for saving current chart into
+ saved search, with type "Chart". This makes the saved search
+ shows up in MyRT prefs.
+
+ * Elements/ShowSearch: when showing a saved search that is not
+ of type "Ticket", dispatch to different handler.
+
+
+------------------------------------------------------------------------
+r6548 | jesse | 2006-11-30 12:27:18 -0500 (Thu, 30 Nov 2006) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrip_Overlay.pm
+
+ r45595@pinglin: jesse | 2006-11-30 12:26:55 -0500
+ RT-Ticket: 8049
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Thanks. Applied
+
+
+------------------------------------------------------------------------
+r6547 | jesse | 2006-11-28 21:27:35 -0500 (Tue, 28 Nov 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/ahah.js
+
+ r45558@pinglin: jesse | 2006-11-28 21:26:43 -0500
+ * When doing an AHAH replace of a page section, don't show a "loading" message. (makes things feel smoother)
+
+------------------------------------------------------------------------
+r6546 | jesse | 2006-11-28 21:27:23 -0500 (Tue, 28 Nov 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Template_Overlay.pm
+
+ r45544@pinglin: jesse | 2006-11-28 18:37:57 -0500
+ * Typo fix
+
+------------------------------------------------------------------------
+r6515 | ruz | 2006-11-24 16:22:32 -0500 (Fri, 24 Nov 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pl.po
+
+* Polish translation, thanks to Arkadiusz Miskiewicz
+------------------------------------------------------------------------
+r6514 | ruz | 2006-11-24 10:11:12 -0500 (Fri, 24 Nov 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ru.po
+
+* update of russian translation
+------------------------------------------------------------------------
+r6513 | ruz | 2006-11-24 07:42:09 -0500 (Fri, 24 Nov 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditScrip
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Scrip.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Scrip.html
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrip_Overlay.pm
+
+Scrip management
+* error messages
+* preserve values of the fields between calls
+* correctly report id in the tab
+* fix #7445
+------------------------------------------------------------------------
+r6477 | clkao | 2006-11-21 07:17:23 -0500 (Tue, 21 Nov 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+
+Allow arguments from component porlets.
+------------------------------------------------------------------------
+r6468 | ruz | 2006-11-20 22:56:05 -0500 (Mon, 20 Nov 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/04send_email.t
+
+* correct number of test
+------------------------------------------------------------------------
+r6467 | ruz | 2006-11-20 21:29:46 -0500 (Mon, 20 Nov 2006) | 1 line
+Changed paths:
+ A /rt/branches/3.6-RELEASE/lib/t/data/very-long-subject
+ M /rt/branches/3.6-RELEASE/lib/t/regression/04send_email.t
+
+* add a test for processing of emails with a long subject
+------------------------------------------------------------------------
+r6466 | ruz | 2006-11-20 18:26:54 -0500 (Mon, 20 Nov 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+ A /rt/branches/3.6-RELEASE/lib/t/data/subject-with-folding-ws
+ M /rt/branches/3.6-RELEASE/lib/t/regression/04send_email.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/06-mime_decoding.t
+
+* tests and fix for folding white spaces in headers
+* see also #5248 at rt3.fsck.com
+------------------------------------------------------------------------
+r6464 | clkao | 2006-11-20 15:03:46 -0500 (Mon, 20 Nov 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/license_tag
+
+FSF has changed their address.
+------------------------------------------------------------------------
+r6458 | ruz | 2006-11-20 00:54:02 -0500 (Mon, 20 Nov 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+
+* minor changes
+------------------------------------------------------------------------
+r6453 | ruz | 2006-11-18 15:08:31 -0500 (Sat, 18 Nov 2006) | 18 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/autohandler
+ M /rt/branches/3.6-RELEASE/lib/t/regression/21query-builder.t
+
+* fix problems in building queries with CF conditions based on
+ CFs with not-ascii names
+** bug fix for #8012 at rt3.fsck.com and may be other bugs related
+ to not-ascii keys in the %ARGS.
+** add a test
+
+Additional info:
+
+Use $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS );
+instead of $m->call_next to avoid problems with UTF8 keys in arguments.
+The call_next method pass through original arguments and if you have
+an argument with unicode key then in a next component you'll get two
+records in the args hash: one with key without UTF8 flag and another
+with the flag, which may result into errors. "{ base_comp => $m->request_comp }"
+is copied from mason's source to get the same results as we get from
+call_next method, this feature is not documented, so we just leave it
+here to avoid possible side effects.
+
+------------------------------------------------------------------------
+r6450 | ruz | 2006-11-16 17:07:58 -0500 (Thu, 16 Nov 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+ M /rt/branches/3.6-RELEASE/lib/t/regression/21query-builder.t
+
+* fix TODO tests in query builder and add new tests
+** all tests are related to building queries like 'C1 OR ( C2 AND C3 )',
+ QB was changing all entry aggregators to AND
+------------------------------------------------------------------------
+r6449 | ruz | 2006-11-16 17:03:05 -0500 (Thu, 16 Nov 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/21query-builder.t
+
+* make the test file re-entrant
+------------------------------------------------------------------------
+r6446 | ruz | 2006-11-16 11:28:28 -0500 (Thu, 16 Nov 2006) | 1 line
+Changed paths:
+ A /rt/branches/3.6-RELEASE/lib/t/regression/00-mason-syntax.t
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+* test syntax of comps with a mason compiler
+------------------------------------------------------------------------
+r6432 | jesse | 2006-11-14 15:53:31 -0500 (Tue, 14 Nov 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Handler.pm
+
+ r44838@pinglin: jesse | 2006-11-14 15:53:18 -0500
+ * Move Text::Quoted back to being a run-time require. So that it's possible to turn off the feature if it causes your perl to segfault. (Text::Tabs is...not robust in the face of perl bugs)
+
+------------------------------------------------------------------------
+r6400 | jesse | 2006-11-09 23:46:17 -0500 (Thu, 09 Nov 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+
+ r44740@pinglin: jesse | 2006-11-09 23:46:01 -0500
+ * Added a callback to let extensions massage custom homepage portlet searches
+
+
+------------------------------------------------------------------------
+r6399 | jesse | 2006-11-09 23:41:15 -0500 (Thu, 09 Nov 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+
+ r44732@pinglin: jesse | 2006-11-09 23:41:00 -0500
+ * The RT homepage no longer explodes if you upgrade from 3.4. (Error proof the "Portlets" attribute handling)
+
+
+------------------------------------------------------------------------
+r6380 | jesse | 2006-11-07 14:22:15 -0500 (Tue, 07 Nov 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/SelfService/Closed.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/MyRequests
+ M /rt/branches/3.6-RELEASE/html/SelfService/index.html
+
+ r40520@pinglin: jesse | 2006-11-07 14:15:50 -0500
+ * SelfService cleanup and regularization patches from
+ David Chandek-Stark <david.chandek.stark@duke.edu>
+
+------------------------------------------------------------------------
+r6379 | jesse | 2006-11-07 14:16:57 -0500 (Tue, 07 Nov 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r29654@pinglin: jesse | 2006-10-31 17:10:10 -0500
+ 3.6.2rc2
+
+------------------------------------------------------------------------
+r6363 | falcone | 2006-11-06 14:09:32 -0500 (Mon, 06 Nov 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+
+ r14256@ketch: falcone | 2006-11-06 14:08:54 -0500
+ Make LoadConfig a lot more vocal about why it can't read your config files
+ and offer some hints and warnings about fixing it
+
+------------------------------------------------------------------------
+r6362 | falcone | 2006-11-06 14:09:21 -0500 (Mon, 06 Nov 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/README
+
+ r14255@ketch: falcone | 2006-11-06 14:07:58 -0500
+ You can't have FastCgiIpcDir and FastCgiServer in the VirtualHost block
+
+------------------------------------------------------------------------
+r6302 | ruz | 2006-10-25 23:18:11 -0400 (Wed, 25 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+
+ r4038@cubic-pc: cubic | 2006-10-26 07:29:27 +0400
+ * add doc
+
+------------------------------------------------------------------------
+r6243 | jesse | 2006-10-20 12:37:05 -0400 (Fri, 20 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r28972@119: jesse | 2006-10-20 12:36:50 -0400
+ * Backed out the rest of kevin's accidental commit
+
+------------------------------------------------------------------------
+r6242 | kevinr | 2006-10-20 01:13:43 -0400 (Fri, 20 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r28708@SAD-GIRL-IN-SNOW: kevinr | 2006-10-20 01:13:16 -0400
+ * Reverted
+
+------------------------------------------------------------------------
+r6241 | kevinr | 2006-10-20 00:05:59 -0400 (Fri, 20 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+
+ r28703@sad-girl-in-snow: kevinr | 2006-10-20 00:02:48 -0400
+ * Pulled User::WatchedQueues in from RT::Extension::rt_cpan_org
+
+------------------------------------------------------------------------
+r6240 | kevinr | 2006-10-20 00:05:03 -0400 (Fri, 20 Oct 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+
+------------------------------------------------------------------------
+r6211 | jesse | 2006-10-16 11:49:02 -0400 (Mon, 16 Oct 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r28886@pinglin: jesse | 2006-10-16 11:48:54 -0400
+ * Added a workaround for parsing headers from broken MUAs that send headers like:
+
+ From: ""Vincent, Jesse"" <jesse@fsck.com>
+
+
+
+------------------------------------------------------------------------
+r6201 | ruz | 2006-10-12 22:33:22 -0400 (Thu, 12 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+ r3951@cubic-pc: cubic | 2006-10-13 06:43:19 +0400
+ * we have $OldOwnerObj, so use it
+
+------------------------------------------------------------------------
+r6200 | ruz | 2006-10-12 22:33:13 -0400 (Thu, 12 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Tabs
+
+ r3950@cubic-pc: cubic | 2006-10-13 06:07:28 +0400
+ * fine tune [Take] and [Steal] actions according to ACL
+
+------------------------------------------------------------------------
+r6199 | ruz | 2006-10-12 22:33:05 -0400 (Thu, 12 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Tabs
+
+ r3949@cubic-pc: cubic | 2006-10-13 06:02:30 +0400
+ * cache results of rights checks locally
+
+------------------------------------------------------------------------
+r6198 | ruz | 2006-10-12 17:54:23 -0400 (Thu, 12 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+* skip -Category part as -Magic [rt3.fsck.com #7903]
+* drop $cfid as it's not used
+
+------------------------------------------------------------------------
+r6172 | jesse | 2006-10-06 12:42:45 -0400 (Fri, 06 Oct 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+ r28065@101: jesse | 2006-10-06 12:42:52 -0400
+ * After RT 3.6.1, we broke the ""send an error message" email routines to require that they have an incoming message. Fixed now
+ - Spotted by Jason A. Diegmueller
+
+
+------------------------------------------------------------------------
+r6167 | jesse | 2006-10-05 12:06:49 -0400 (Thu, 05 Oct 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r28050@pinglin: jesse | 2006-10-05 12:06:47 -0400
+ * 3.6.2rc1
+
+------------------------------------------------------------------------
+r6063 | jesse | 2006-09-27 18:43:54 -0400 (Wed, 27 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/Approve
+
+ r27846@253: jesse | 2006-09-27 18:43:53 -0400
+ * removed a stray font tag
+
+------------------------------------------------------------------------
+r6055 | ruz | 2006-09-26 22:25:59 -0400 (Tue, 26 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+
+* convert de.po back to UTF-8 from latin-1
+------------------------------------------------------------------------
+r6054 | ruz | 2006-09-26 20:32:59 -0400 (Tue, 26 Sep 2006) | 48 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+
+merge 3.4 -> QUEBEC -> CHALDEA -> 3.6
+
+ r3890@cubic-pc (orig r6053): ruz | 2006-09-27 04:31:42 +0400
+ merge QUEBEC -> CHALDEA
+
+ r3883@cubic-pc (orig r6046): ruz | 2006-09-27 03:29:31 +0400
+ r3671@cubic-pc (orig r5829): ruz | 2006-08-31 00:12:48 +0400
+ * typo
+
+ r3884@cubic-pc (orig r6047): ruz | 2006-09-27 03:29:39 +0400
+ r3694@cubic-pc (orig r5849): ruz | 2006-09-04 20:32:29 +0400
+ * drop unused variable
+ * init index($i) with 0 to avoid warnings
+
+ r3885@cubic-pc (orig r6048): ruz | 2006-09-27 03:29:51 +0400
+ r3695@cubic-pc (orig r5850): ruz | 2006-09-04 20:49:12 +0400
+ * drop uninit warning
+
+ r3886@cubic-pc (orig r6049): ruz | 2006-09-27 04:15:12 +0400
+ r3696@cubic-pc (orig r5856): ruz | 2006-09-04 23:17:14 +0400
+ * split function ProcessObjectCustomFieldUpdates into two
+ * call RedoSearch on object's custom fields values collection
+ after {Add,Delete}CustomFieldValues operations
+
+ r3887@cubic-pc (orig r6050): ruz | 2006-09-27 04:15:22 +0400
+ r3697@cubic-pc (orig r5857): ruz | 2006-09-04 23:30:28 +0400
+ * redo search if we have deleted entries in collection
+
+ r3888@cubic-pc (orig r6051): ruz | 2006-09-27 04:15:29 +0400
+ r3698@cubic-pc (orig r5858): ruz | 2006-09-05 02:09:30 +0400
+ ::OldValue and ::NewValue
+ * don't load object if {Old,New}Reference is false value
+ ** this is hitting some cache issues and could return "random" result
+ which brakes tickets' history.
+
+ Thanks to Joshua Speicher.
+
+
+ r3889@cubic-pc (orig r6052): ruz | 2006-09-27 04:15:58 +0400
+ r3759@cubic-pc (orig r5943): jesse | 2006-09-15 23:31:25 +0400
+ r27507@pinglin: jesse | 2006-09-15 20:30:47 +0100
+ [mail gateway] Todd Chapman discovered a case where RT's mail gateway would default to the RT::SystemUser if no valid from header were found. This could allow a malicious user to create tickets or reply to tickets, but not to gain access to data.
+
+
+
+
+
+
+------------------------------------------------------------------------
+r6045 | ruz | 2006-09-26 18:59:02 -0400 (Tue, 26 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+
+* drop unused code
+------------------------------------------------------------------------
+r6044 | ruz | 2006-09-26 18:47:02 -0400 (Tue, 26 Sep 2006) | 1 line
+Changed paths:
+ D /rt/branches/3.6-RELEASE/html/NoAuth/ahah.js
+ D /rt/branches/3.6-RELEASE/html/NoAuth/printrt.css
+
+* remove files that have been added by merge
+------------------------------------------------------------------------
+r6043 | ruz | 2006-09-26 18:34:35 -0400 (Tue, 26 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditBasics
+
+* drop duplicated code (result of the merge)
+------------------------------------------------------------------------
+r6042 | ruz | 2006-09-26 18:24:16 -0400 (Tue, 26 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/autohandler
+
+* parsing fails after merge, fix it
+------------------------------------------------------------------------
+r6041 | ruz | 2006-09-26 18:23:05 -0400 (Tue, 26 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFields
+
+* fix merge bug
+------------------------------------------------------------------------
+r6040 | ruz | 2006-09-26 17:49:01 -0400 (Tue, 26 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+* revert wrong merge part
+------------------------------------------------------------------------
+r6039 | ruz | 2006-09-26 11:06:31 -0400 (Tue, 26 Sep 2006) | 2254 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/README
+ M /rt/branches/3.6-RELEASE/bin/rt-crontool.in
+ M /rt/branches/3.6-RELEASE/bin/rt-mailgate.in
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+ M /rt/branches/3.6-RELEASE/bin/webmux.pl.in
+ M /rt/branches/3.6-RELEASE/configure.ac
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/etc/RT_SiteConfig.pm
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditScrips
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.6-RELEASE/html/Elements/Callback
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/Row
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomField
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.6-RELEASE/html/Elements/Footer
+ M /rt/branches/3.6-RELEASE/html/Elements/Header
+ M /rt/branches/3.6-RELEASE/html/Elements/Menu
+ M /rt/branches/3.6-RELEASE/html/Elements/PageLayout
+ M /rt/branches/3.6-RELEASE/html/Elements/QueryString
+ M /rt/branches/3.6-RELEASE/html/Elements/QuickCreate
+ M /rt/branches/3.6-RELEASE/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/3.6-RELEASE/html/Elements/ScrubHTML
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectNewTicketQueue
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldBinary
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFields
+ M /rt/branches/3.6-RELEASE/html/Elements/ValidateCustomFields
+ A /rt/branches/3.6-RELEASE/html/NoAuth/ahah.js
+ A /rt/branches/3.6-RELEASE/html/NoAuth/printrt.css
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/search/ticket
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/BuildFormatString
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ M /rt/branches/3.6-RELEASE/html/Search/Results.html
+ M /rt/branches/3.6-RELEASE/html/Search/Results.rdf
+ M /rt/branches/3.6-RELEASE/html/Search/Results.tsv
+ M /rt/branches/3.6-RELEASE/html/SelfService/Create.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Display.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditBasics
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Reminders
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowBasics
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowDates
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowHistory
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTime
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyAll.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyDates.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyLinks.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyPeople.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Update.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Tools/Offline.html
+ M /rt/branches/3.6-RELEASE/html/User/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/autohandler
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Notify.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Base.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValues.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Date.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/EmailParser.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Group_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Groups_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Handle.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/da.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/de.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/en.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/es.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fi.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/fr.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/he.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/hu.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/id.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/it.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ja.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/nl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/no.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pl.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/ru.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/zh_tw.po
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Standalone.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Link_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Links_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SearchBuilder.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Template_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/base.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Users_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+ A /rt/branches/3.6-RELEASE/lib/t/regression/06-mime_decoding.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/06mailgateway.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/07acl.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/07rights.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/12-search.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/14linking.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/20-sort-by-requestor.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23-batch-upload-csv.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23-web_attachments.t
+ M /rt/branches/3.6-RELEASE/releng.cnf
+ M /rt/branches/3.6-RELEASE/sbin/extract-message-catalog
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+merge CHALDEA -> 3.6
+
+ r2964@cubic-pc (orig r3308): jesse | 2005-07-04 02:06:31 +0400
+ r22123@hualien: jesse | 2005-07-03 17:46:54 -0400
+ r20060@hualien: jesse | 2005-06-14 08:57:51 -0400
+ * Moving the search qyery into the search body
+
+
+ r2965@cubic-pc (orig r3309): jesse | 2005-07-04 02:06:41 +0400
+ r22124@hualien: jesse | 2005-07-03 17:46:59 -0400
+ r20076@hualien: jesse | 2005-06-14 15:01:31 -0400
+ r20064@hualien (orig r3143): alexmv | 2005-06-13 20:15:22 -0400
+ r4193@zoq-fot-pik: chmrr | 2005-06-13 20:14:53 -0400
+ * Generalize to work with arrays in %ARGS
+
+ r20066@hualien (orig r3145): alexmv | 2005-06-13 21:02:36 -0400
+ r4197@zoq-fot-pik: chmrr | 2005-06-13 21:01:50 -0400
+ * Don't assume that ->Resolver is a valid object
+
+ r20068@hualien (orig r3147): alexmv | 2005-06-14 06:56:06 -0400
+ r4209@zoq-fot-pik: chmrr | 2005-06-14 06:55:43 -0400
+ * We should actually let people *download* these uploads..
+
+
+
+
+ r2966@cubic-pc (orig r3310): jesse | 2005-07-04 02:06:53 +0400
+ r22125@hualien: jesse | 2005-07-03 17:47:04 -0400
+ r20540@hualien: jesse | 2005-06-15 14:46:19 -0400
+ r20162@hualien (orig r3156): alexmv | 2005-06-14 23:24:19 -0400
+ r4230@zoq-fot-pik: chmrr | 2005-06-14 23:22:31 -0400
+ * Allow filtering of custom fields
+
+ r20163@hualien (orig r3157): alexmv | 2005-06-14 23:24:23 -0400
+
+ r20165@hualien (orig r3159): alexmv | 2005-06-15 00:47:27 -0400
+ r4236@zoq-fot-pik: chmrr | 2005-06-15 00:46:57 -0400
+ * <input> tags don't like newlines in them
+
+
+
+
+ r2967@cubic-pc (orig r3311): jesse | 2005-07-04 02:07:03 +0400
+ r22126@hualien: jesse | 2005-07-03 17:47:09 -0400
+ r20543@hualien: jesse | 2005-06-15 15:58:07 -0400
+ r19701@hualien: jesse | 2005-06-09 00:58:09 -0400
+ * Bumping to 3.4.3pre1
+
+
+
+
+ r2968@cubic-pc (orig r3312): jesse | 2005-07-04 02:07:17 +0400
+ r22127@hualien: jesse | 2005-07-03 17:47:14 -0400
+ r20544@hualien: jesse | 2005-06-15 15:58:11 -0400
+ r20078@hualien: jesse | 2005-06-14 16:57:41 -0400
+ * First draft Indonesian message catalog from James Briggs
+
+
+
+
+ r2969@cubic-pc (orig r3313): jesse | 2005-07-04 02:07:35 +0400
+ r22128@hualien: jesse | 2005-07-03 17:47:18 -0400
+ r20545@hualien: jesse | 2005-06-15 15:58:15 -0400
+ r20542@hualien: jesse | 2005-06-15 15:56:43 -0400
+ * Made search-by-any-customfield work
+
+
+
+
+ r2970@cubic-pc (orig r3314): jesse | 2005-07-04 02:07:47 +0400
+ r22129@hualien: jesse | 2005-07-03 17:47:23 -0400
+ r20551@hualien: jesse | 2005-06-15 16:12:50 -0400
+ * Reenabled testing redirect on article update
+
+
+ r2971@cubic-pc (orig r3315): jesse | 2005-07-04 02:08:00 +0400
+ r22130@hualien: jesse | 2005-07-03 17:47:27 -0400
+ r20559@hualien: jesse | 2005-06-15 16:33:09 -0400
+ * Style cleanups for custom field list filtering
+
+
+ r2972@cubic-pc (orig r3316): jesse | 2005-07-04 02:08:11 +0400
+ r22131@hualien: jesse | 2005-07-03 17:47:32 -0400
+ r20562@hualien: jesse | 2005-06-15 18:59:20 -0400
+ * When updating articles, we want to say "Updated Article" rather than "Updated FM"
+
+
+
+
+ r2973@cubic-pc (orig r3317): jesse | 2005-07-04 02:08:21 +0400
+ r22132@hualien: jesse | 2005-07-03 17:47:37 -0400
+ r20726@hualien: jesse | 2005-06-16 12:43:06 -0400
+ r20706@hualien: jesse | 2005-06-16 12:12:07 -0400
+
+
+
+
+ r2974@cubic-pc (orig r3318): jesse | 2005-07-04 02:08:35 +0400
+ r22133@hualien: jesse | 2005-07-03 17:47:41 -0400
+ r20727@hualien: jesse | 2005-06-16 12:43:11 -0400
+ r20707@hualien: jesse | 2005-06-16 12:12:16 -0400
+ r19708@hualien (orig r3120): kevinr | 2005-06-09 01:33:18 -0400
+ r4110@SAD-GIRL-IN-SNOW: kevinr | 2005-06-09 01:30:22 -0400
+ * fixed the mis-typed perldoc tags
+
+
+
+
+
+ r2975@cubic-pc (orig r3319): jesse | 2005-07-04 02:08:50 +0400
+ r22134@hualien: jesse | 2005-07-03 17:47:46 -0400
+ r20728@hualien: jesse | 2005-06-16 12:43:18 -0400
+ r20708@hualien: jesse | 2005-06-16 12:12:29 -0400
+ r20063@hualien (orig r3142): kevinr | 2005-06-13 15:43:08 -0400
+ r4210@SAD-GIRL-IN-SNOW: kevinr | 2005-06-13 15:39:50 -0400
+ * fixed a couple small issues with the perldoc format
+
+
+
+
+
+ r2976@cubic-pc (orig r3320): jesse | 2005-07-04 02:09:01 +0400
+ r22135@hualien: jesse | 2005-07-03 17:47:56 -0400
+ r20730@hualien: jesse | 2005-06-16 12:43:33 -0400
+ r20714@hualien: jesse | 2005-06-16 12:20:30 -0400
+ r20705@hualien: jesse | 2005-06-16 12:11:06 -0400
+ r19183@hualien (orig r3063): pdh | 2005-06-03 01:01:55 -0400
+ Misleading error referred to IsApplicable, not Commit
+
+
+
+
+
+
+
+ r2977@cubic-pc (orig r3321): jesse | 2005-07-04 02:09:13 +0400
+ r22136@hualien: jesse | 2005-07-03 17:48:00 -0400
+ r20731@hualien: jesse | 2005-06-16 12:43:44 -0400
+ r20715@hualien: jesse | 2005-06-16 12:20:34 -0400
+ r20712@hualien: jesse | 2005-06-16 12:16:22 -0400
+ r20710@hualien (orig r3203): alexmv | 2005-06-16 04:29:14 -0400
+ r4306@zoq-fot-pik: chmrr | 2005-06-16 04:29:06 -0400
+ * Move /^$foo$/ regexes to use eq instead, to prevent regex insertion
+ exploits
+
+
+
+
+
+
+ r2978@cubic-pc (orig r3322): jesse | 2005-07-04 02:09:25 +0400
+ r22137@hualien: jesse | 2005-07-03 17:48:05 -0400
+ r20732@hualien: jesse | 2005-06-16 12:43:57 -0400
+ r20716@hualien: jesse | 2005-06-16 12:23:58 -0400
+ r20713@hualien: jesse | 2005-06-16 12:16:28 -0400
+ r20711@hualien (orig r3204): alexmv | 2005-06-16 04:31:40 -0400
+ r4308@zoq-fot-pik: chmrr | 2005-06-16 04:31:30 -0400
+ * Missed an lc
+
+
+
+
+
+
+ r2979@cubic-pc (orig r3323): jesse | 2005-07-04 02:09:35 +0400
+ r22138@hualien: jesse | 2005-07-03 17:48:10 -0400
+ r20733@hualien: jesse | 2005-06-16 12:44:07 -0400
+ r20717@hualien: jesse | 2005-06-16 12:24:50 -0400
+ * Message catalog updates
+
+
+
+ r2980@cubic-pc (orig r3324): jesse | 2005-07-04 02:10:10 +0400
+ r22139@hualien: jesse | 2005-07-03 17:49:33 -0400
+ r20761@hualien: jesse | 2005-06-17 19:38:02 -0400
+ * Now statistical reports include total # of tickets found
+
+
+ r2981@cubic-pc (orig r3325): jesse | 2005-07-04 02:10:23 +0400
+ r22140@hualien: jesse | 2005-07-03 17:49:37 -0400
+ r21273@hualien: jesse | 2005-06-20 17:57:29 -0400
+ r20804@hualien (orig r3230): alexmv | 2005-06-20 14:57:03 -0400
+ r4370@zoq-fot-pik: chmrr | 2005-06-20 14:58:14 -0400
+ * Added callback
+
+
+
+
+ r2982@cubic-pc (orig r3326): jesse | 2005-07-04 02:10:33 +0400
+ r22141@hualien: jesse | 2005-07-03 17:49:42 -0400
+ r21955@hualien: jesse | 2005-06-27 11:40:56 -0400
+ r21288@hualien (orig r3235): alexmv | 2005-06-20 18:54:00 -0400
+ r4386@zoq-fot-pik: chmrr | 2005-06-20 18:56:20 -0400
+ * Attempt to prevent footer from showing up on redirects for some servers (?)
+
+
+
+
+ r2983@cubic-pc (orig r3327): jesse | 2005-07-04 02:10:46 +0400
+ r22142@hualien: jesse | 2005-07-03 17:49:50 -0400
+ r21959@hualien: jesse | 2005-06-27 14:15:10 -0400
+ * Moved "Owner" to right after queue in the list of basics to edit.
+
+
+ r2984@cubic-pc (orig r3328): jesse | 2005-07-04 02:11:05 +0400
+ r22143@hualien: jesse | 2005-07-03 17:49:55 -0400
+ r22037@hualien: jesse | 2005-07-01 19:25:05 -0400
+ r22031@hualien (orig r3273): alexmv | 2005-07-01 00:46:49 -0400
+ r4557@zoq-fot-pik: chmrr | 2005-06-30 20:44:33 -0400
+ * Fractional units
+
+
+
+
+ r2985@cubic-pc (orig r3329): jesse | 2005-07-04 02:11:22 +0400
+ r22144@hualien: jesse | 2005-07-03 17:50:04 -0400
+ r22096@hualien: jesse | 2005-07-03 16:25:14 -0400
+ r20757@hualien: jesse | 2005-06-17 19:34:37 -0400
+ * Updated mailgateway documentation
+
+
+
+ r2986@cubic-pc (orig r3330): jesse | 2005-07-04 02:11:35 +0400
+ r22145@hualien: jesse | 2005-07-03 17:50:08 -0400
+ r22097@hualien: jesse | 2005-07-03 16:25:18 -0400
+ r21520@hualien: jesse | 2005-06-23 19:49:06 -0400
+ * Test suite improvements and cleanups
+
+
+
+ r2987@cubic-pc (orig r3331): jesse | 2005-07-04 02:11:45 +0400
+ r22146@hualien: jesse | 2005-07-03 17:50:13 -0400
+ r22098@hualien: jesse | 2005-07-03 16:26:12 -0400
+ r21521@hualien: jesse | 2005-06-23 19:49:30 -0400
+ * Custom field edit widget bullet proofing
+
+
+
+ r2988@cubic-pc (orig r3332): jesse | 2005-07-04 02:11:55 +0400
+ r22147@hualien: jesse | 2005-07-03 17:50:17 -0400
+ r22099@hualien: jesse | 2005-07-03 16:26:16 -0400
+ r21522@hualien: jesse | 2005-06-23 19:49:51 -0400
+ * Some warning avoidance in Action/SendEmail.pm
+
+
+
+ r2989@cubic-pc (orig r3333): jesse | 2005-07-04 02:12:12 +0400
+ r22148@hualien: jesse | 2005-07-03 17:50:22 -0400
+ r22100@hualien: jesse | 2005-07-03 16:26:21 -0400
+ r21656@hualien: jesse | 2005-06-24 13:53:26 -0400
+ * New Indonesian translation from James Briggs
+
+
+
+ r2990@cubic-pc (orig r3334): jesse | 2005-07-04 02:12:31 +0400
+ r22150@hualien: jesse | 2005-07-03 17:50:31 -0400
+ r22102@hualien: jesse | 2005-07-03 16:26:30 -0400
+ r21954@hualien: jesse | 2005-06-27 11:40:16 -0400
+ r20564@hualien (orig r3181): alexmv | 2005-06-15 17:52:14 -0400
+
+ r20798@hualien (orig r3224): kevinr | 2005-06-18 22:26:40 -0400
+ r4487@sad-girl-in-snow: kevinr | 2005-06-18 21:23:04 -0500
+ * Fixed a typo
+ * now use $RT::WebAddress because it's a good idea
+ * now localize 'Scrip' and number, instead of only the former
+
+ r20799@hualien (orig r3225): kevinr | 2005-06-18 22:26:51 -0400
+
+ r20800@hualien (orig r3226): kevinr | 2005-06-18 22:37:51 -0400
+ r4496@sad-girl-in-snow: kevinr | 2005-06-18 21:37:27 -0500
+ * Fixes possible cross-site scripting bug.
+
+ r20801@hualien (orig r3227): jesse | 2005-06-18 23:47:04 -0400
+ * Fixed typo in kevin's fix (/l is a filter. it needs the |)
+
+ r21566@hualien (orig r3240): alexmv | 2005-06-23 16:57:51 -0400
+ r4466@zoq-fot-pik: chmrr | 2005-06-23 16:58:19 -0400
+ * Document Set() a little better
+
+ r21567@hualien (orig r3241): alexmv | 2005-06-23 16:57:54 -0400
+ r4467@zoq-fot-pik: chmrr | 2005-06-23 16:58:52 -0400
+ * Doc that CanonicalizeEmailAddress may be called as static
+
+ r21568@hualien (orig r3242): alexmv | 2005-06-23 16:58:02 -0400
+ r4468@zoq-fot-pik: chmrr | 2005-06-23 16:59:51 -0400
+ * Remove possible closure issue (my $foo = .... if $bar;)
+
+ r21569@hualien (orig r3243): alexmv | 2005-06-23 16:58:14 -0400
+ r4469@zoq-fot-pik: chmrr | 2005-06-23 17:00:09 -0400
+ * Escape queue name properly
+
+ r21570@hualien (orig r3244): alexmv | 2005-06-23 17:37:31 -0400
+ r4481@zoq-fot-pik: chmrr | 2005-06-23 17:39:53 -0400
+ * Actually fix the escaping bug
+
+ r21571@hualien (orig r3245): alexmv | 2005-06-23 17:43:57 -0400
+ r4483@zoq-fot-pik: chmrr | 2005-06-23 17:46:18 -0400
+ RT-Ticket: 6782
+ RT-Update: correspond
+ RT-Status: resolved
+
+ * Fix perl invocation; thanks to Kim Toms <kim.toms@gmail.com>
+
+ r21572@hualien (orig r3246): alexmv | 2005-06-23 17:48:26 -0400
+ r4485@zoq-fot-pik: chmrr | 2005-06-23 17:50:48 -0400
+ RT-Ticket: 6781
+ RT-Update: correspond
+ RT-Status: resolved
+
+ * _AddWatcher takes Email, not Person; thanks to Andreas Jakum <ajakum@inode.at>
+
+
+
+
+
+ r2991@cubic-pc (orig r3335): jesse | 2005-07-04 02:12:44 +0400
+ r22151@hualien: jesse | 2005-07-03 17:50:36 -0400
+ r22103@hualien: jesse | 2005-07-03 16:27:27 -0400
+ r22038@hualien: jesse | 2005-07-01 19:28:24 -0400
+ r21984@hualien (orig r3261): kevinr | 2005-06-28 18:47:38 -0400
+ r5752@sad-girl-in-snow: kevinr | 2005-06-28 17:45:36 -0500
+ RT-Ticket: 6745
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Applied Seph's patch to add a BeforeShowHistory callback.
+
+ r22028@hualien (orig r3270): alexmv | 2005-07-01 00:32:06 -0400
+ r4555@zoq-fot-pik: chmrr | 2005-06-30 17:36:05 -0400
+ * WikiText updates to auto-link http:// style links
+
+ r22029@hualien (orig r3271): alexmv | 2005-07-01 00:32:10 -0400
+ r4556@zoq-fot-pik: chmrr | 2005-06-30 17:58:57 -0400
+ * Don't assume link is happy on creation
+
+ r22030@hualien (orig r3272): alexmv | 2005-07-01 00:32:15 -0400
+
+
+
+
+
+ r2992@cubic-pc (orig r3336): jesse | 2005-07-04 02:12:59 +0400
+ r22152@hualien: jesse | 2005-07-03 17:50:41 -0400
+ r22104@hualien: jesse | 2005-07-03 16:28:54 -0400
+ r22039@hualien: jesse | 2005-07-01 21:05:38 -0400
+ * Refactoring of how RT::Record deals with custom fields for greater consistency
+
+
+
+
+ r2993@cubic-pc (orig r3337): jesse | 2005-07-04 02:13:11 +0400
+ r22153@hualien: jesse | 2005-07-03 17:50:46 -0400
+ r22105@hualien: jesse | 2005-07-03 16:28:58 -0400
+ r22091@hualien: jesse | 2005-07-03 16:13:10 -0400
+ * Disabling a new feature that might be dangerous; test fixes; cf fixes
+
+
+
+ r2994@cubic-pc (orig r3338): jesse | 2005-07-04 02:13:21 +0400
+ r22154@hualien: jesse | 2005-07-03 17:50:55 -0400
+ r22109@hualien: jesse | 2005-07-03 17:06:06 -0400
+ * Test fixups
+
+
+ r2995@cubic-pc (orig r3339): jesse | 2005-07-04 02:13:33 +0400
+
+ r2996@cubic-pc (orig r3418): autrijus | 2005-07-08 03:56:54 +0400
+ * Fix misvalidation for single input fields.
+ r2997@cubic-pc (orig r3421): autrijus | 2005-07-08 05:37:26 +0400
+ * When deleting the value of a single-value field, we need to validate that
+ empty string is a valid value for it.
+ * Also adds regression tests for this.
+ r2998@cubic-pc (orig r3467): autrijus | 2005-07-13 07:34:47 +0400
+ * invalid CFs in ticket creation times now warns.
+ r2999@cubic-pc (orig r3468): glasser | 2005-07-13 21:18:09 +0400
+ Add Calendar::Simple to dependencies. (thanks to Kevin Falcone)
+ r3000@cubic-pc (orig r3481): trs | 2005-07-16 23:14:14 +0400
+ r5007@wintermute: tom | 2005-07-16 15:11:39 -0400
+ RT-Ticket: 6846
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Applied Kevin Falcone's patch to fix the calendar display.
+
+ r3001@cubic-pc (orig r3673): jesse | 2005-08-19 00:29:25 +0400
+ r13195@hualien: jesse | 2005-08-18 14:19:57 -0400
+ r4892@hualien: jesse | 2005-07-18 11:47:55 -0400
+
+
+
+ r3002@cubic-pc (orig r3674): jesse | 2005-08-19 00:29:51 +0400
+ r13196@hualien: jesse | 2005-08-18 14:23:48 -0400
+ r4894@hualien: jesse | 2005-07-18 13:44:44 -0400
+ * releng.cnf bump to rc1
+
+
+ r3003@cubic-pc (orig r3675): jesse | 2005-08-19 00:30:13 +0400
+ r13197@hualien: jesse | 2005-08-18 14:23:55 -0400
+ r7152@hualien: jesse | 2005-07-30 11:06:46 -0400
+ * German and Danish translation header fixes.
+ besides that just a message catalog regenration
+
+
+
+ r3004@cubic-pc (orig r3676): jesse | 2005-08-19 00:30:55 +0400
+ r13198@hualien: jesse | 2005-08-18 14:26:49 -0400
+ r7153@hualien: jesse | 2005-07-30 11:07:37 -0400
+ 3.4.3rc2
+
+
+ r3005@cubic-pc (orig r3677): jesse | 2005-08-19 00:31:09 +0400
+ r13199@hualien: jesse | 2005-08-18 14:26:56 -0400
+ r7158@hualien: jesse | 2005-07-30 11:08:03 -0400
+ r4909@hualien (orig r3501): alexmv | 2005-07-18 15:00:38 -0400
+ r5198@zoq-fot-pik: chmrr | 2005-07-18 14:59:07 -0400
+ * Fix TransactionBatch / DESTROY bug (backport from QUEBEC)
+
+ r7124@hualien (orig r3546): kevinr | 2005-07-28 14:51:34 -0400
+ r6713@SAD-GIRL-IN-SNOW: kevinr | 2005-07-28 14:50:47 -0400
+ RT-Ticket: 6892
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied patch from Hsin-Chan Chien to fix a bug with attachment uploading
+ in SelfService mode.
+
+
+
+
+ r3006@cubic-pc (orig r3678): jesse | 2005-08-19 00:31:32 +0400
+ r13200@hualien: jesse | 2005-08-18 14:27:07 -0400
+ r7398@hualien: jesse | 2005-08-05 15:39:10 -0400
+ r7317@hualien (orig r3576): robert | 2005-08-02 00:23:36 -0400
+ r3582@woof: rspier | 2005-08-01 21:20:12 -0700
+ allow arbitrary https?: urls in the menus
+
+ r7333@hualien (orig r3579): kevinr | 2005-08-02 14:22:27 -0400
+ r6837@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 14:20:51 -0400
+ RT-Ticket: 6897
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Incorporated the updated Italian translation from Angelo Turetta
+
+ r7335@hualien (orig r3581): glasser | 2005-08-02 15:08:03 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r7336@hualien (orig r3582): glasser | 2005-08-02 15:10:05 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r7366@hualien (orig r3585): kevinr | 2005-08-02 20:18:54 -0400
+ r6849@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 20:17:38 -0400
+ * POD formatting fixes
+
+ r7370@hualien (orig r3588): kevinr | 2005-08-03 01:20:35 -0400
+ r6868@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:19:47 -0400
+ * Fixed an outdated bit of POD
+
+ r7371@hualien (orig r3589): kevinr | 2005-08-03 01:51:55 -0400
+ r6872@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:51:33 -0400
+ * More minor perldoc cleanup
+
+
+
+
+ r3007@cubic-pc (orig r3679): jesse | 2005-08-19 00:32:07 +0400
+ r13201@hualien: jesse | 2005-08-18 14:27:20 -0400
+ r7399@hualien: jesse | 2005-08-05 19:16:58 -0400
+ * Cleaned up searching by ticket or txn date.
+
+
+ r3008@cubic-pc (orig r3680): jesse | 2005-08-19 00:32:32 +0400
+ r13202@hualien: jesse | 2005-08-18 14:27:29 -0400
+ r7634@hualien: jesse | 2005-08-10 15:25:31 -0400
+ * This is 3.4.3
+
+
+
+ r3009@cubic-pc (orig r3681): jesse | 2005-08-19 00:32:53 +0400
+ r13203@hualien: jesse | 2005-08-18 14:27:36 -0400
+ r12938@hualien: jesse | 2005-08-11 13:46:14 -0400
+ * French localization had lost its header
+
+
+
+ r3010@cubic-pc (orig r3682): jesse | 2005-08-19 00:33:18 +0400
+ r13204@hualien: jesse | 2005-08-18 14:27:47 -0400
+ r13154@hualien: jesse | 2005-08-17 19:14:26 -0400
+ * Fixes to standalone webserver for mason 1.30
+
+
+ r3011@cubic-pc (orig r3683): jesse | 2005-08-19 00:33:46 +0400
+ r13205@hualien: jesse | 2005-08-18 14:27:56 -0400
+ r13155@hualien: jesse | 2005-08-17 19:15:11 -0400
+ * Note dependency on a current HSSM
+
+
+ r3012@cubic-pc (orig r3684): jesse | 2005-08-19 00:34:26 +0400
+ r13208@hualien: jesse | 2005-08-18 14:39:15 -0400
+ r7377@hualien: jesse | 2005-08-03 15:11:21 -0400
+
+
+
+ r3013@cubic-pc (orig r3772): jesse | 2005-09-02 19:52:23 +0400
+ r14057@hualien: jesse | 2005-08-30 05:48:48 -0400
+ r13699@hualien: jesse | 2005-08-22 14:46:07 -0400
+ r13255@hualien (orig r3700): trs | 2005-08-18 22:39:02 -0400
+ r6002@wintermute: tom | 2005-08-18 22:37:53 -0400
+ Fixed typo
+
+
+
+
+ r3014@cubic-pc (orig r3773): jesse | 2005-09-02 19:53:08 +0400
+ r14058@hualien: jesse | 2005-08-30 05:49:00 -0400
+ r13720@hualien: jesse | 2005-08-23 17:54:29 -0400
+ * Bugfixes to the German translation file
+
+
+ r3015@cubic-pc (orig r3774): jesse | 2005-09-02 19:53:49 +0400
+ r14059@hualien: jesse | 2005-08-30 05:49:11 -0400
+ r13723@hualien: jesse | 2005-08-23 17:55:47 -0400
+ r13701@hualien (orig r3706): glasser | 2005-08-22 15:55:57 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r13721@hualien (orig r3707): glasser | 2005-08-22 16:04:25 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r13722@hualien (orig r3708): glasser | 2005-08-22 16:05:46 -0400
+ r40177@tin-foil: glasser | 2005-08-18 18:49:13 -0400
+ Updates to the standalone server to not dupe as much code, but may be rolled back if HSSM needs to be.
+
+
+
+
+ r3016@cubic-pc (orig r3775): jesse | 2005-09-02 19:54:25 +0400
+ r14060@hualien: jesse | 2005-08-30 05:49:21 -0400
+ r13725@hualien: jesse | 2005-08-23 22:10:42 -0400
+ * Added support for mod_perl 2.0
+
+
+ r3017@cubic-pc (orig r3776): jesse | 2005-09-02 19:55:10 +0400
+ r14061@hualien: jesse | 2005-08-30 05:51:23 -0400
+ r13728@hualien: jesse | 2005-08-23 22:37:12 -0400
+ * Bumped to 3.4.4pre1
+
+
+ r3018@cubic-pc (orig r3777): jesse | 2005-09-02 19:55:53 +0400
+ r14062@hualien: jesse | 2005-08-30 05:51:32 -0400
+ r13732@hualien: jesse | 2005-08-23 23:38:28 -0400
+ * Applied tom's callbacks fix
+
+
+
+ r3019@cubic-pc (orig r3778): jesse | 2005-09-02 19:56:30 +0400
+ r14063@hualien: jesse | 2005-08-30 05:51:41 -0400
+ r13733@hualien: jesse | 2005-08-23 23:38:39 -0400
+ * bumped to pre2
+
+
+ r3020@cubic-pc (orig r3779): jesse | 2005-09-02 19:57:07 +0400
+ r14064@hualien: jesse | 2005-08-30 05:51:50 -0400
+ r13743@hualien: jesse | 2005-08-24 02:00:08 -0400
+ RT-Ticket: 6957
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from ams to restore his byline to bin/rt
+
+
+ r3021@cubic-pc (orig r3780): jesse | 2005-09-02 19:57:46 +0400
+ r14065@hualien: jesse | 2005-08-30 05:51:59 -0400
+ r13753@hualien: jesse | 2005-08-25 14:53:06 -0400
+ * Removing warnings about mod_perl2
+
+
+ r3022@cubic-pc (orig r3781): jesse | 2005-09-02 19:58:20 +0400
+ r14066@hualien: jesse | 2005-08-30 05:52:07 -0400
+ r13754@hualien: jesse | 2005-08-25 14:55:32 -0400
+ * EquivObjects support for acl queries, from Todd Chapman
+
+
+
+ r3023@cubic-pc (orig r3782): jesse | 2005-09-02 19:59:02 +0400
+ r14067@hualien: jesse | 2005-08-30 05:52:15 -0400
+ r13757@hualien: jesse | 2005-08-25 15:23:18 -0400
+ RT-Ticket: 6934
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * A couple of patches to improve how localization extraction works from jfenal
+
+
+
+ r3024@cubic-pc (orig r3783): jesse | 2005-09-02 19:59:38 +0400
+ r14068@hualien: jesse | 2005-08-30 05:52:24 -0400
+ r13759@hualien: jesse | 2005-08-25 15:31:56 -0400
+ * Fixes to IsRTAddress processing. Spotted by Travis Campbell.
+
+
+
+ r3025@cubic-pc (orig r3784): jesse | 2005-09-02 20:00:18 +0400
+ r14069@hualien: jesse | 2005-08-30 05:52:32 -0400
+ r13760@hualien: jesse | 2005-08-25 15:51:57 -0400
+ RT-Ticket: 6855
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from Andreas Jakum to improve RT::Ticket->Import's handling
+ of watchers
+
+
+
+ r3026@cubic-pc (orig r3785): jesse | 2005-09-02 20:00:55 +0400
+ r14070@hualien: jesse | 2005-08-30 05:52:43 -0400
+ r13763@hualien: jesse | 2005-08-25 16:04:29 -0400
+ * 3.4.4rc3
+
+
+ r3027@cubic-pc (orig r3786): jesse | 2005-09-02 20:01:35 +0400
+ r14071@hualien: jesse | 2005-08-30 05:52:51 -0400
+ r13804@hualien: jesse | 2005-08-27 21:25:50 -0400
+ * Updated French translation from jfenal
+
+
+
+ r3028@cubic-pc (orig r3787): jesse | 2005-09-02 20:02:15 +0400
+ r14072@hualien: jesse | 2005-08-30 05:54:33 -0400
+ r13812@hualien: jesse | 2005-08-28 15:43:29 -0400
+ * bumping to 3.4.4
+
+
+ r3029@cubic-pc (orig r3788): jesse | 2005-09-02 20:02:55 +0400
+ r14073@hualien: jesse | 2005-08-30 05:55:38 -0400
+ r13543@hualien: jesse | 2005-08-19 10:40:13 -0400
+ r7386@hualien (orig r3596): alexmv | 2005-08-03 17:48:18 -0400
+ r5615@zoq-fot-pik: chmrr | 2005-08-03 17:46:45 -0400
+ * Cutsom field values in RT::Tickets are always on RT::Ticket objects
+ * Remove CSS that was causing <span class="label"> to be small, unused CSS
+
+
+
+
+ r3030@cubic-pc (orig r3789): jesse | 2005-09-02 20:04:12 +0400
+ r14074@hualien: jesse | 2005-08-30 05:55:48 -0400
+ r13544@hualien: jesse | 2005-08-19 10:40:27 -0400
+ r7387@hualien (orig r3597): alexmv | 2005-08-03 18:08:02 -0400
+ r5627@zoq-fot-pik: chmrr | 2005-08-03 18:00:41 -0400
+ * Attempt to fix WebExternalAuth problem with images
+
+
+
+
+ r3031@cubic-pc (orig r3790): jesse | 2005-09-02 20:04:52 +0400
+ r14075@hualien: jesse | 2005-08-30 05:56:28 -0400
+ r13545@hualien: jesse | 2005-08-19 10:40:41 -0400
+ r7388@hualien (orig r3598): alexmv | 2005-08-03 18:39:17 -0400
+ r5629@zoq-fot-pik: chmrr | 2005-08-03 18:37:50 -0400
+ * Show time in hours or days when needed
+
+
+
+
+ r3032@cubic-pc (orig r3791): jesse | 2005-09-02 20:05:32 +0400
+ r14076@hualien: jesse | 2005-08-30 05:56:38 -0400
+ r13546@hualien: jesse | 2005-08-19 10:40:57 -0400
+ r7391@hualien (orig r3601): alexmv | 2005-08-04 13:41:11 -0400
+ r5643@zoq-fot-pik: chmrr | 2005-08-04 13:38:31 -0400
+ * Don't display "days" -- often conntes work days, not 24-hour periods
+
+
+
+
+ r3033@cubic-pc (orig r3792): jesse | 2005-09-02 20:06:12 +0400
+ r14077@hualien: jesse | 2005-08-30 05:56:46 -0400
+ r13547@hualien: jesse | 2005-08-19 10:41:11 -0400
+ r7392@hualien (orig r3602): alexmv | 2005-08-04 13:41:33 -0400
+ r5644@zoq-fot-pik: chmrr | 2005-08-04 13:39:54 -0400
+ * Replace %FIELDS (which has pseudohash meaning) with %FIELD_METADATA
+ * Make join for sorting watchers be a left join
+
+
+
+
+ r3034@cubic-pc (orig r3793): jesse | 2005-09-02 20:06:53 +0400
+ r14078@hualien: jesse | 2005-08-30 05:56:57 -0400
+ r13548@hualien: jesse | 2005-08-19 10:41:25 -0400
+ r7393@hualien (orig r3603): alexmv | 2005-08-04 16:11:05 -0400
+ r5647@zoq-fot-pik: chmrr | 2005-08-04 16:04:03 -0400
+ * Add $RT::WikiImplicitLinks option (defaults to false)
+ * Support for Wiki links doing useful things
+
+
+
+
+ r3035@cubic-pc (orig r3794): jesse | 2005-09-02 20:07:35 +0400
+ r14079@hualien: jesse | 2005-08-30 05:57:09 -0400
+ r13549@hualien: jesse | 2005-08-19 10:41:40 -0400
+ r7397@hualien (orig r3607): alexmv | 2005-08-05 14:59:21 -0400
+ r5653@zoq-fot-pik: chmrr | 2005-08-05 14:57:28 -0400
+ * Display "save" even if search isn't dirty (can be a rename)
+
+
+
+
+ r3036@cubic-pc (orig r3795): jesse | 2005-09-02 20:08:16 +0400
+ r14080@hualien: jesse | 2005-08-30 05:57:19 -0400
+ r13550@hualien: jesse | 2005-08-19 10:41:53 -0400
+ r12931@hualien (orig r3634): alexmv | 2005-08-10 20:28:31 -0400
+ r5784@zoq-fot-pik: chmrr | 2005-08-10 20:29:42 -0400
+ * Fix custom fields formatting in IE
+
+
+
+
+ r3037@cubic-pc (orig r3796): jesse | 2005-09-02 20:09:02 +0400
+ r14081@hualien: jesse | 2005-08-30 05:57:29 -0400
+ r13551@hualien: jesse | 2005-08-19 10:42:07 -0400
+ r12965@hualien (orig r3648): alexmv | 2005-08-12 16:30:49 -0400
+ r5826@zoq-fot-pik: chmrr | 2005-08-12 16:29:24 -0400
+ * r3602 didn't go far enough; be happy we are using DBIx::SB::Unique,
+ because we got us a lot of possible duplice rows with all of these
+ left joins.
+
+
+
+
+ r3038@cubic-pc (orig r3797): jesse | 2005-09-02 20:09:52 +0400
+ r14082@hualien: jesse | 2005-08-30 05:57:40 -0400
+ r13552@hualien: jesse | 2005-08-19 10:42:22 -0400
+ r12966@hualien (orig r3649): alexmv | 2005-08-12 16:30:55 -0400
+ r5827@zoq-fot-pik: chmrr | 2005-08-12 16:33:16 -0400
+ * Revert r3597
+
+
+
+
+ r3039@cubic-pc (orig r3798): jesse | 2005-09-02 20:10:33 +0400
+ r14083@hualien: jesse | 2005-08-30 05:57:49 -0400
+ r13553@hualien: jesse | 2005-08-19 10:42:36 -0400
+ r13127@hualien (orig r3658): alexmv | 2005-08-15 18:38:56 -0400
+ r5873@zoq-fot-pik: chmrr | 2005-08-15 18:42:04 -0400
+ * Require that the object not only be defined but also valid, to pull
+ values from it
+
+
+
+
+ r3040@cubic-pc (orig r3799): jesse | 2005-09-02 20:11:16 +0400
+ r14084@hualien: jesse | 2005-08-30 05:57:58 -0400
+ r13741@hualien: jesse | 2005-08-24 01:28:59 -0400
+ * Added support for named custom fields in templates
+
+
+
+ r3041@cubic-pc (orig r3800): jesse | 2005-09-02 20:12:05 +0400
+ r15105@hualien: jesse | 2005-09-02 11:11:38 -0400
+ Merge forward from RT 3.4.4
+
+
+ r14193@hualien: jesse | 2005-09-01 13:40:23 -0400
+ r4892@hualien: jesse | 2005-07-18 11:47:55 -0400
+
+
+ r14194@hualien: jesse | 2005-09-01 13:41:24 -0400
+ r4894@hualien: jesse | 2005-07-18 13:44:44 -0400
+ * releng.cnf bump to rc1
+
+ r14195@hualien: jesse | 2005-09-01 13:41:32 -0400
+ r7152@hualien: jesse | 2005-07-30 11:06:46 -0400
+ * German and Danish translation header fixes.
+ besides that just a message catalog regenration
+
+
+ r14196@hualien: jesse | 2005-09-01 13:42:01 -0400
+ r7153@hualien: jesse | 2005-07-30 11:07:37 -0400
+ 3.4.3rc2
+
+ r14197@hualien: jesse | 2005-09-01 13:42:09 -0400
+ r7158@hualien: jesse | 2005-07-30 11:08:03 -0400
+ r4909@hualien (orig r3501): alexmv | 2005-07-18 15:00:38 -0400
+ r5198@zoq-fot-pik: chmrr | 2005-07-18 14:59:07 -0400
+ * Fix TransactionBatch / DESTROY bug (backport from QUEBEC)
+
+ r7124@hualien (orig r3546): kevinr | 2005-07-28 14:51:34 -0400
+ r6713@SAD-GIRL-IN-SNOW: kevinr | 2005-07-28 14:50:47 -0400
+ RT-Ticket: 6892
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied patch from Hsin-Chan Chien to fix a bug with attachment uploading
+ in SelfService mode.
+
+
+
+ r14198@hualien: jesse | 2005-09-01 13:42:19 -0400
+ r7398@hualien: jesse | 2005-08-05 15:39:10 -0400
+ r7317@hualien (orig r3576): robert | 2005-08-02 00:23:36 -0400
+ r3582@woof: rspier | 2005-08-01 21:20:12 -0700
+ allow arbitrary https?: urls in the menus
+
+ r7333@hualien (orig r3579): kevinr | 2005-08-02 14:22:27 -0400
+ r6837@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 14:20:51 -0400
+ RT-Ticket: 6897
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Incorporated the updated Italian translation from Angelo Turetta
+
+ r7335@hualien (orig r3581): glasser | 2005-08-02 15:08:03 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r7336@hualien (orig r3582): glasser | 2005-08-02 15:10:05 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r7366@hualien (orig r3585): kevinr | 2005-08-02 20:18:54 -0400
+ r6849@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 20:17:38 -0400
+ * POD formatting fixes
+
+ r7370@hualien (orig r3588): kevinr | 2005-08-03 01:20:35 -0400
+ r6868@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:19:47 -0400
+ * Fixed an outdated bit of POD
+
+ r7371@hualien (orig r3589): kevinr | 2005-08-03 01:51:55 -0400
+ r6872@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:51:33 -0400
+ * More minor perldoc cleanup
+
+
+
+ r14199@hualien: jesse | 2005-09-01 13:42:33 -0400
+ r7399@hualien: jesse | 2005-08-05 19:16:58 -0400
+ * Cleaned up searching by ticket or txn date.
+
+ r14200@hualien: jesse | 2005-09-01 13:42:43 -0400
+ r7634@hualien: jesse | 2005-08-10 15:25:31 -0400
+ * This is 3.4.3
+
+
+ r14201@hualien: jesse | 2005-09-01 13:42:52 -0400
+ r12938@hualien: jesse | 2005-08-11 13:46:14 -0400
+ * French localization had lost its header
+
+
+ r14202@hualien: jesse | 2005-09-01 13:43:02 -0400
+ r13154@hualien: jesse | 2005-08-17 19:14:26 -0400
+ * Fixes to standalone webserver for mason 1.30
+
+ r14203@hualien: jesse | 2005-09-01 13:43:10 -0400
+ r13155@hualien: jesse | 2005-08-17 19:15:11 -0400
+ * Note dependency on a current HSSM
+
+ r14204@hualien: jesse | 2005-09-01 13:43:19 -0400
+ r13164@hualien: jesse | 2005-08-17 22:03:19 -0400
+ r13131@hualien (orig r3662): alexmv | 2005-08-17 13:29:25 -0400
+ r5914@zoq-fot-pik: chmrr | 2005-08-17 13:32:00 -0400
+ * We love escaping! We need to escape quotes and slashes in
+ TicketSQL, then URI escape, then HTML escape. And the query comes
+ from the TicketSQL-escaped one, not the URI and HTML-escaped one. Oh,
+ yeah, and don't forget to turn off the automatic HTML escaping that
+ Mason does, or it'll be TicketSQL-HTML-URI-HTML escaped, which is
+ Right Out.
+
+ r13132@hualien (orig r3663): alexmv | 2005-08-17 13:29:32 -0400
+
+ r13163@hualien (orig r3666): alexmv | 2005-08-17 13:41:38 -0400
+ r5920@zoq-fot-pik: chmrr | 2005-08-17 13:44:59 -0400
+ * My memory was faulty; HTML escaping is not needed
+
+
+
+ r14205@hualien: jesse | 2005-09-01 13:46:31 -0400
+ r13699@hualien: jesse | 2005-08-22 14:46:07 -0400
+ r13255@hualien (orig r3700): trs | 2005-08-18 22:39:02 -0400
+ r6002@wintermute: tom | 2005-08-18 22:37:53 -0400
+ Fixed typo
+
+
+
+ r14206@hualien: jesse | 2005-09-01 13:46:41 -0400
+ r13720@hualien: jesse | 2005-08-23 17:54:29 -0400
+ * Bugfixes to the German translation file
+
+ r14207@hualien: jesse | 2005-09-01 13:46:51 -0400
+ r13723@hualien: jesse | 2005-08-23 17:55:47 -0400
+ r13701@hualien (orig r3706): glasser | 2005-08-22 15:55:57 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r13721@hualien (orig r3707): glasser | 2005-08-22 16:04:25 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r13722@hualien (orig r3708): glasser | 2005-08-22 16:05:46 -0400
+ r40177@tin-foil: glasser | 2005-08-18 18:49:13 -0400
+ Updates to the standalone server to not dupe as much code, but may be rolled back if HSSM needs to be.
+
+
+
+ r14208@hualien: jesse | 2005-09-01 13:47:00 -0400
+ r13725@hualien: jesse | 2005-08-23 22:10:42 -0400
+ * Added support for mod_perl 2.0
+
+ r14209@hualien: jesse | 2005-09-01 13:47:09 -0400
+ r13728@hualien: jesse | 2005-08-23 22:37:12 -0400
+ * Bumped to 3.4.4pre1
+
+ r14210@hualien: jesse | 2005-09-01 13:47:17 -0400
+ r13732@hualien: jesse | 2005-08-23 23:38:28 -0400
+ * Applied tom's callbacks fix
+
+
+ r14211@hualien: jesse | 2005-09-01 13:47:26 -0400
+ r13733@hualien: jesse | 2005-08-23 23:38:39 -0400
+ * bumped to pre2
+
+ r14212@hualien: jesse | 2005-09-01 13:47:35 -0400
+ r13743@hualien: jesse | 2005-08-24 02:00:08 -0400
+ RT-Ticket: 6957
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from ams to restore his byline to bin/rt
+
+ r14213@hualien: jesse | 2005-09-01 13:47:43 -0400
+ r13753@hualien: jesse | 2005-08-25 14:53:06 -0400
+ * Removing warnings about mod_perl2
+
+ r14214@hualien: jesse | 2005-09-01 13:47:53 -0400
+ r13754@hualien: jesse | 2005-08-25 14:55:32 -0400
+ * EquivObjects support for acl queries, from Todd Chapman
+
+
+ r14215@hualien: jesse | 2005-09-01 13:48:01 -0400
+ r13757@hualien: jesse | 2005-08-25 15:23:18 -0400
+ RT-Ticket: 6934
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * A couple of patches to improve how localization extraction works from jfenal
+
+
+ r14216@hualien: jesse | 2005-09-01 13:48:11 -0400
+ r13759@hualien: jesse | 2005-08-25 15:31:56 -0400
+ * Fixes to IsRTAddress processing. Spotted by Travis Campbell.
+
+
+ r14217@hualien: jesse | 2005-09-01 13:48:19 -0400
+ r13760@hualien: jesse | 2005-08-25 15:51:57 -0400
+ RT-Ticket: 6855
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from Andreas Jakum to improve RT::Ticket->Import's handling
+ of watchers
+
+
+ r14218@hualien: jesse | 2005-09-01 13:48:28 -0400
+ r13763@hualien: jesse | 2005-08-25 16:04:29 -0400
+ * 3.4.4rc3
+
+ r14219@hualien: jesse | 2005-09-01 13:48:37 -0400
+ r13804@hualien: jesse | 2005-08-27 21:25:50 -0400
+ * Updated French translation from jfenal
+
+
+ r14220@hualien: jesse | 2005-09-01 13:48:47 -0400
+ r13812@hualien: jesse | 2005-08-28 15:43:29 -0400
+ * bumping to 3.4.4
+
+ r14221@hualien: jesse | 2005-09-01 13:48:56 -0400
+ r14192@hualien: jesse | 2005-09-01 13:37:34 -0400
+ r14130@hualien (orig r3739): robert | 2005-08-31 16:46:16 -0400
+ r3748@woof: rspier | 2005-08-31 13:41:53 -0700
+ Check for invalid character (-) in mysql database names and prevent RT from allowing it to be configured.
+
+
+
+
+
+ r3042@cubic-pc (orig r3913): jesse | 2005-10-05 22:48:43 +0400
+ r17065@hualien: jesse | 2005-10-05 10:39:00 -0400
+ r15942@hualien: jesse | 2005-09-21 23:48:22 -0400
+ * CreateTickets now handles custom fields
+
+
+ r3043@cubic-pc (orig r3914): jesse | 2005-10-05 22:48:55 +0400
+ r17066@hualien: jesse | 2005-10-05 10:39:06 -0400
+ r15969@hualien: jesse | 2005-09-23 15:36:13 -0400
+ * Algorithm for picking transaction content didn't have a reasonable enough fallback case.
+
+
+ r3044@cubic-pc (orig r3915): jesse | 2005-10-05 22:49:11 +0400
+ r17067@hualien: jesse | 2005-10-05 10:39:13 -0400
+ r17053@hualien: jesse | 2005-10-05 10:00:45 -0400
+ r15946@hualien (orig r3872): alexmv | 2005-09-22 12:38:17 -0400
+ r6181@zoq-fot-pik: chmrr | 2005-09-02 12:09:41 -0400
+ * Additional tests for no requestor (should still sort correctly)
+
+ r15947@hualien (orig r3873): alexmv | 2005-09-22 12:38:34 -0400
+ r6451@zoq-fot-pik: chmrr | 2005-09-22 12:37:30 -0400
+ * Statement logging
+
+ r17047@hualien (orig r3903): alexmv | 2005-10-03 15:57:38 -0400
+ r6572@zoq-fot-pik: chmrr | 2005-10-03 15:47:52 -0400
+ * Make test text reflect the number of tickets the test is looking for
+
+ r17048@hualien (orig r3904): alexmv | 2005-10-03 15:57:45 -0400
+
+
+
+
+ r3045@cubic-pc (orig r3916): jesse | 2005-10-05 22:49:27 +0400
+ r17068@hualien: jesse | 2005-10-05 10:39:19 -0400
+ r17054@hualien: jesse | 2005-10-05 10:07:50 -0400
+ r15723@hualien: jesse | 2005-09-13 12:05:40 -0400
+ * When pulling data out of the database, we need to be more careful
+ about whether it's utf8 or not. Thanks to Ruslan Zakirov
+
+
+
+
+ r3046@cubic-pc (orig r3917): jesse | 2005-10-05 22:49:50 +0400
+ r17069@hualien: jesse | 2005-10-05 10:39:25 -0400
+ r17055@hualien: jesse | 2005-10-05 10:07:58 -0400
+ r15749@hualien: jesse | 2005-09-15 11:14:56 -0400
+ * It was possible to get into an infinite loop when removing a member from a group
+
+
+
+ r3047@cubic-pc (orig r3918): jesse | 2005-10-05 22:50:07 +0400
+ r17070@hualien: jesse | 2005-10-05 10:39:31 -0400
+ r17056@hualien: jesse | 2005-10-05 10:08:04 -0400
+ r15752@hualien: jesse | 2005-09-15 11:41:41 -0400
+ r14236@hualien (orig r3754): robert | 2005-09-01 17:47:36 -0400
+ r3800@bear: rspier | 2005-09-01 14:46:59 -0700
+ RT-Ticket: 6986
+ RT-Status: resolved
+ RT-Update: correspond
+
+ If we didn't generate any SQL, don't pass it to FromSQL which will reset the dirty flag and then SB won't actually run anything.
+
+ Also, tests.
+
+
+ r15713@hualien (orig r3847): glasser | 2005-09-12 18:11:43 -0400
+ r41532@maclaurin-seven-twelve: glasser | 2005-09-12 18:04:55 -0400
+ Defining subs in Mason components is dangerous, since they clash with subs defined
+ in every other component.
+
+
+
+
+
+ r3048@cubic-pc (orig r3919): jesse | 2005-10-05 22:50:32 +0400
+ r17071@hualien: jesse | 2005-10-05 10:39:37 -0400
+ r17057@hualien: jesse | 2005-10-05 10:08:11 -0400
+ r15770@hualien: jesse | 2005-09-16 12:23:15 -0400
+ * The RSS feeds should come with a default subject, as feeds really want to have article titles in some clients
+
+
+
+ r3049@cubic-pc (orig r3920): jesse | 2005-10-05 22:50:54 +0400
+ r17072@hualien: jesse | 2005-10-05 10:39:48 -0400
+ r17059@hualien: jesse | 2005-10-05 10:08:39 -0400
+ r17052@hualien: jesse | 2005-10-05 09:37:42 -0400
+ r15958@hualien (orig r3877): alexmv | 2005-09-22 15:09:22 -0400
+ r6458@zoq-fot-pik: chmrr | 2005-09-22 15:08:37 -0400
+ * Add where the faulty caller was in deprecated warnings
+
+ r16168@hualien (orig r3892): robert | 2005-09-28 12:16:03 -0400
+ r3945@bear: rspier | 2005-09-28 09:15:08 -0700
+ Performance Improvement when Sending Email using sendmailpipe -
+
+ MIME::Entity would bog down in certain cases because of it's use of IO::Scalar during stringification. MIME::Entity will be switching to IO::ScalarArray, which will help... but RT was causing it to store into a temporary string anyway, which was silly.
+
+ This change has MIME::Entity write directly to the pipe, which is a lot more efficient. Seems to cut out ~33% of user time. (Because we don't need to have a temporary IO::Scalar thingy around.) Also will reduce peak memory usage.
+
+
+ r16169@hualien (orig r3893): jesse | 2005-09-28 13:27:29 -0400
+ Switch from ->CustomFields to ->TicketCustomFields to stop using a deprecated API.
+ Thanks to T.J. Maciak
+
+ r17038@hualien (orig r3894): alexmv | 2005-09-30 15:19:46 -0400
+ r6554@zoq-fot-pik: chmrr | 2005-09-30 15:16:47 -0400
+ * Remove unused and deprecated code path (bugs 6605, 7008)
+
+ r17039@hualien (orig r3895): alexmv | 2005-09-30 15:19:57 -0400
+ r6555@zoq-fot-pik: chmrr | 2005-09-30 15:18:22 -0400
+ * Link to the *other* end of the link, not the one that is us
+
+ r17040@hualien (orig r3896): alexmv | 2005-09-30 15:56:31 -0400
+ r6558@zoq-fot-pik: chmrr | 2005-09-30 15:56:06 -0400
+ RT-Ticket: 7029
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied missing limit for AdminCcs, from Todd Chapman
+
+
+ r17044@hualien (orig r3900): alexmv | 2005-10-03 13:32:45 -0400
+ r6566@zoq-fot-pik: chmrr | 2005-10-03 13:28:24 -0400
+ * Updated spanish translation, thanks to Carlos Velasco
+
+ r17045@hualien (orig r3901): alexmv | 2005-10-03 14:15:35 -0400
+ r6568@zoq-fot-pik: chmrr | 2005-10-03 14:14:49 -0400
+ * Header fixes in PO files to include correct RT version
+
+
+
+
+
+ r3050@cubic-pc (orig r3968): jesse | 2005-10-15 03:48:12 +0400
+ r17373@hualien: jesse | 2005-10-14 15:34:42 -0400
+ r17359@hualien: jesse | 2005-10-14 15:21:10 -0400
+ * Perltidy
+
+
+ r3051@cubic-pc (orig r3969): jesse | 2005-10-15 03:48:19 +0400
+ r17374@hualien: jesse | 2005-10-14 15:34:43 -0400
+ r17361@hualien: jesse | 2005-10-14 15:22:39 -0400
+ r17358@hualien: jesse | 2005-10-14 15:06:26 -0400
+ r17219@hualien (orig r3938): robert | 2005-10-07 00:20:15 -0400
+ r3995@bear: rspier | 2005-10-06 21:19:24 -0700
+ [fsck.com #7067] - If we can't find a customfield that the user is allowed to see on a ticket, don't return any values, (when specifying a custom field)
+
+ r17275@hualien (orig r3944): ruz | 2005-10-10 15:27:36 -0400
+ backport of the 3.5-TESTING@3943
+ Changes
+ * fix for search by owner's fields, now owner is WATCHERFIELD instead of ENUM
+ * added backward compatible variant for Owner, next searches should work
+ ** Owner = '<id>'
+ ** Owner != '<id>'
+ ** Owner = '<name>'
+ ** Owner != '<name>'
+ ** for other operators or if subfield(subkey) is specified search works
+ as for other watchers
+ * Fix for searches like "Cc.Name <> 'SomeBody'", was skipping tickets
+ with empty Cc list.
+ * get rid of some unint warnings
+ * test suite for all corner cases
+
+ r17276@hualien (orig r3945): ruz | 2005-10-10 15:47:29 -0400
+ backport of the 3.5-TESTING@3543
+ Changes:
+ * fix attachments ordering
+
+ r17313@hualien (orig r3948): ruz | 2005-10-10 20:01:50 -0400
+ * get rid of "not a number" warning
+ r17339@hualien (orig r3957): ruz | 2005-10-13 08:37:47 -0400
+ * code comments
+ r17340@hualien (orig r3958): ruz | 2005-10-13 08:40:24 -0400
+ * new callback in html/User/Elements/Tabs
+
+ r17360@hualien: jesse | 2005-10-14 15:21:46 -0400
+ * Perltidy
+
+
+
+ r3052@cubic-pc (orig r3970): jesse | 2005-10-15 03:48:29 +0400
+ r17375@hualien: jesse | 2005-10-14 15:34:45 -0400
+ r17362@hualien: jesse | 2005-10-14 15:56:53 -0400
+ * Merge fixups
+
+
+ r3053@cubic-pc (orig r3971): jesse | 2005-10-15 03:48:42 +0400
+ r17376@hualien: jesse | 2005-10-14 15:34:47 -0400
+ r17363@hualien: jesse | 2005-10-14 15:57:06 -0400
+ * Todo test no longer failing
+
+
+ r3054@cubic-pc (orig r3972): jesse | 2005-10-15 03:48:49 +0400
+ r17377@hualien: jesse | 2005-10-14 15:34:48 -0400
+ r17371@hualien: jesse | 2005-10-14 17:13:07 -0400
+ Pull up from 3.4
+
+
+ r3055@cubic-pc (orig r4177): jesse | 2005-12-01 00:26:20 +0300
+ r19594@truegrounds: jesse | 2005-11-30 16:04:46 -0500
+ r18978@truegrounds: jesse | 2005-11-18 17:16:11 -0500
+ * Minor refactoring of CreateTickets.pm (It needs a more complete rototill)
+
+ * more flexible support for custom fields whose names contain dashes and spaces in createtickets templates
+
+
+
+ r3056@cubic-pc (orig r4178): jesse | 2005-12-01 00:26:34 +0300
+ r19595@truegrounds: jesse | 2005-11-30 16:04:52 -0500
+ r19551@truegrounds: jesse | 2005-11-30 15:24:09 -0500
+ r19350@truegrounds (orig r4145): alexmv | 2005-11-23 16:32:25 -0500
+ r7341@zoq-fot-pik: chmrr | 2005-11-23 16:31:44 -0500
+ * Limiting based on CFs should make sure that the CFs in question are
+ on the right queue, otherwise negative searches might be wrong.
+ * Removed debugging lines from 20-sort-by-requestor.t
+
+ r19521@truegrounds (orig r4149): alexmv | 2005-11-28 15:00:53 -0500
+ r7368@zoq-fot-pik: chmrr | 2005-11-28 15:00:18 -0500
+ * Search/Build.html passes Rows not RowsPerPage -- don't hard-force
+ to 50 all the time!
+
+
+
+
+ r3057@cubic-pc (orig r4179): jesse | 2005-12-01 00:26:50 +0300
+ r19596@truegrounds: jesse | 2005-11-30 16:05:00 -0500
+ r19553@truegrounds: jesse | 2005-11-30 15:26:01 -0500
+ r19552@truegrounds (orig r4155): alexmv | 2005-11-30 15:26:08 -0500
+ r7395@zoq-fot-pik: chmrr | 2005-11-30 15:25:32 -0500
+ * Sort by requestor tests fail on some database backends because they
+ sort nulls differently than Perl does; only compare non-nulls
+
+
+
+
+ r3058@cubic-pc (orig r4180): jesse | 2005-12-01 00:27:04 +0300
+ r19597@truegrounds: jesse | 2005-11-30 16:05:07 -0500
+ r19554@truegrounds: jesse | 2005-11-30 15:27:53 -0500
+ r18121@truegrounds: jesse | 2005-11-02 22:40:02 -0500
+ r17958@truegrounds (orig r3989): alexmv | 2005-10-24 17:26:18 -0400
+ r6881@zoq-fot-pik: chmrr | 2005-10-24 17:25:14 -0400
+ * Ensure custom fields keep correct fallback values; for instance, if
+ "add another attachment" is clicked
+
+ r18110@truegrounds (orig r4010): pdh | 2005-10-31 19:21:57 -0500
+ Make $RT::MaxInlineBody work properly.
+
+
+ r18111@truegrounds (orig r4011): pdh | 2005-11-01 00:43:02 -0500
+ Add a missing space, before the Style Police come after me.
+
+
+
+
+
+
+ r3059@cubic-pc (orig r4181): jesse | 2005-12-01 00:27:18 +0300
+ r19598@truegrounds: jesse | 2005-11-30 16:06:36 -0500
+ r19555@truegrounds: jesse | 2005-11-30 15:28:02 -0500
+ r18409@truegrounds: jesse | 2005-11-06 17:11:57 -0500
+ * Fix to attachment ordering when you ask for a txn's attachments.
+ (Postgres doesn't default to ordering by id, so we were getting the wrong txn content)
+
+
+
+ r3060@cubic-pc (orig r4182): jesse | 2005-12-01 00:27:32 +0300
+ r19599@truegrounds: jesse | 2005-11-30 16:06:44 -0500
+ r19556@truegrounds: jesse | 2005-11-30 15:28:09 -0500
+ r18411@truegrounds: jesse | 2005-11-06 17:13:33 -0500
+ * Patch to significantly improve performance on "WhoHaveRight" from Ruslan.
+
+
+
+ r3061@cubic-pc (orig r4183): jesse | 2005-12-01 00:27:48 +0300
+ r19600@truegrounds: jesse | 2005-11-30 16:06:53 -0500
+ r19557@truegrounds: jesse | 2005-11-30 15:28:15 -0500
+ r18412@truegrounds: jesse | 2005-11-06 17:13:58 -0500
+ * Bumped to 3.4.5pre1
+
+
+
+ r3062@cubic-pc (orig r4184): jesse | 2005-12-01 00:28:03 +0300
+ r19601@truegrounds: jesse | 2005-11-30 16:07:00 -0500
+ r19558@truegrounds: jesse | 2005-11-30 15:28:23 -0500
+ r18716@truegrounds: jesse | 2005-11-11 00:10:08 -0500
+ * fix from ruslan for fallout from his WhoHaveRight refactoring
+
+
+
+ r3063@cubic-pc (orig r4185): jesse | 2005-12-01 00:28:18 +0300
+ r19602@truegrounds: jesse | 2005-11-30 16:07:07 -0500
+ r19559@truegrounds: jesse | 2005-11-30 15:28:45 -0500
+ r18722@truegrounds: jesse | 2005-11-11 15:26:34 -0500
+ * SB 1.35 dependency
+
+
+
+ r3064@cubic-pc (orig r4186): jesse | 2005-12-01 00:28:41 +0300
+ r19603@truegrounds: jesse | 2005-11-30 16:07:14 -0500
+ r19560@truegrounds: jesse | 2005-11-30 15:29:29 -0500
+ r18876@truegrounds: jesse | 2005-11-14 12:32:25 -0500
+ r18739@truegrounds (orig r4061): robert | 2005-11-13 00:14:57 -0500
+ r4124@bear: rspier | 2005-11-12 21:08:45 -0800
+ Undefined Warning Elimination:
+ - index.html passes in $session{'home_refresh_interval'} which can be null.
+
+ r4125@bear: rspier | 2005-11-12 21:14:28 -0800
+ Undefined Warning Elimination:
+ GetHeader will return undefined when the header doesn't exist. (This is _good_, as that is different than empty.)
+ But.. =~ warns.
+
+
+
+
+
+ r3065@cubic-pc (orig r4187): jesse | 2005-12-01 00:28:55 +0300
+ r19604@truegrounds: jesse | 2005-11-30 16:07:22 -0500
+ r19561@truegrounds: jesse | 2005-11-30 15:29:35 -0500
+ r18877@truegrounds: jesse | 2005-11-14 12:37:37 -0500
+ RT-Ticket: 7087
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Displayed linked tickets in search results were inverted
+
+
+
+
+ r3066@cubic-pc (orig r4188): jesse | 2005-12-01 00:29:09 +0300
+ r19605@truegrounds: jesse | 2005-11-30 16:07:28 -0500
+ r19562@truegrounds: jesse | 2005-11-30 15:29:41 -0500
+ r18880@truegrounds: jesse | 2005-11-14 12:42:48 -0500
+ RT-Ticket: 7081
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Added a note to the readme warning users to clean out the
+ mason cache on upgrades - Ruslan
+
+
+
+
+ r3067@cubic-pc (orig r4189): jesse | 2005-12-01 00:29:25 +0300
+ r19606@truegrounds: jesse | 2005-11-30 16:07:35 -0500
+ r19563@truegrounds: jesse | 2005-11-30 15:29:47 -0500
+ r18888@truegrounds: jesse | 2005-11-14 12:54:25 -0500
+ RT-Ticket: 7048
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Akos Torok pointed out that our HTML scrubber removed "PRE" tags from HTML
+
+
+
+ r3068@cubic-pc (orig r4190): jesse | 2005-12-01 00:29:40 +0300
+ r19607@truegrounds: jesse | 2005-11-30 16:07:42 -0500
+ r19564@truegrounds: jesse | 2005-11-30 15:29:54 -0500
+ r18892@truegrounds: jesse | 2005-11-14 13:07:15 -0500
+ r18881@truegrounds (orig r4064): alexmv | 2005-11-14 12:43:06 -0500
+ r7122@zoq-fot-pik: chmrr | 2005-11-14 12:42:37 -0500
+ * Updated russian translation from Andrew Kornilov <andy@eva.dp.ua>
+
+
+
+
+
+ r3069@cubic-pc (orig r4191): jesse | 2005-12-01 00:29:56 +0300
+ r19608@truegrounds: jesse | 2005-11-30 16:09:07 -0500
+ r19565@truegrounds: jesse | 2005-11-30 15:30:00 -0500
+ r18893@truegrounds: jesse | 2005-11-14 13:19:52 -0500
+ RT-Ticket: 7128
+ RT-Status: resolved
+ RT-Update: correspond
+
+ A big patch from Todd Chapman (with lots of juicy tests) to optionally
+ create two transactions when you create a link. (Also, this means that we'll
+ run scrips twice). This is off by default in RT 3.4
+
+
+
+
+ r3070@cubic-pc (orig r4192): jesse | 2005-12-01 00:30:14 +0300
+ r19609@truegrounds: jesse | 2005-11-30 16:09:14 -0500
+ r19566@truegrounds: jesse | 2005-11-30 15:30:08 -0500
+ r18895@truegrounds: jesse | 2005-11-14 13:35:29 -0500
+ RT-Ticket: 7136
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Stuart Knight reports:
+
+ As part of the "initdb" processing, the scripts went through and created a new database user, in my case called RT3.
+
+ When it came time to create the tables, the script was still logged on as the dba user "system", so all of tables/sequences were created under "system"'s schema.
+
+ I followed through the rt-setup-database script, and spotted that there was a database disconnect, followed by an immediate reconnect, as the same user. (in the case of Oracle this still being the "dba" account)
+
+ Putting an extra validation check in here for Oracle, and then connecting as the intended database user fixed up the issue.
+
+
+
+
+ r3071@cubic-pc (orig r4193): jesse | 2005-12-01 00:30:29 +0300
+ r19610@truegrounds: jesse | 2005-11-30 16:09:20 -0500
+ r19567@truegrounds: jesse | 2005-11-30 15:30:15 -0500
+ r18897@truegrounds: jesse | 2005-11-14 13:35:44 -0500
+ r18896@truegrounds (orig r4072): alexmv | 2005-11-14 13:33:43 -0500
+ r7135@zoq-fot-pik: chmrr | 2005-11-14 13:32:23 -0500
+ RT-Ticket: 7101
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Don't modify EquivObjects arrayref, thanks to Todd Chapman
+
+
+
+
+
+
+ r3072@cubic-pc (orig r4194): jesse | 2005-12-01 00:30:44 +0300
+ r19611@truegrounds: jesse | 2005-11-30 16:09:27 -0500
+ r19568@truegrounds: jesse | 2005-11-30 15:30:22 -0500
+ r18899@truegrounds: jesse | 2005-11-14 13:40:24 -0500
+ RT-Ticket: 7121
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Todd Chapman to make the web based acl tests honor RT::WebPath
+
+
+
+
+ r3073@cubic-pc (orig r4195): jesse | 2005-12-01 00:31:00 +0300
+ r19612@truegrounds: jesse | 2005-11-30 16:09:34 -0500
+ r19569@truegrounds: jesse | 2005-11-30 15:37:06 -0500
+ r18900@truegrounds: jesse | 2005-11-14 13:57:34 -0500
+ RT-Ticket: 7122
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Todd Chapman to honor changed a $rtname variable when running
+ the test suite
+
+
+
+ r3074@cubic-pc (orig r4196): jesse | 2005-12-01 00:31:18 +0300
+ r19613@truegrounds: jesse | 2005-11-30 16:09:40 -0500
+ r19570@truegrounds: jesse | 2005-11-30 15:37:12 -0500
+ r18904@truegrounds: jesse | 2005-11-14 14:49:25 -0500
+ RT-Ticket: 7105
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Updated French translation from Jerome Fenal
+
+
+
+ r3075@cubic-pc (orig r4197): jesse | 2005-12-01 00:31:35 +0300
+ r19614@truegrounds: jesse | 2005-11-30 16:09:47 -0500
+ r19571@truegrounds: jesse | 2005-11-30 15:37:19 -0500
+ r19545@truegrounds: jesse | 2005-11-29 18:51:07 -0500
+ * A pair of new callbacks to make it easier to hide away a custom field on ticket display/edit
+
+
+
+ r3076@cubic-pc (orig r4198): jesse | 2005-12-01 00:31:49 +0300
+ r19615@truegrounds: jesse | 2005-11-30 16:09:54 -0500
+ r19572@truegrounds: jesse | 2005-11-30 15:37:26 -0500
+ r19547@truegrounds: jesse | 2005-11-29 18:54:41 -0500
+ r18901@truegrounds (orig r4074): alexmv | 2005-11-14 13:52:00 -0500
+ r7140@zoq-fot-pik: chmrr | 2005-11-14 13:51:14 -0500
+ * Better bounce handling, from Abhijit Menon-Sen <ams@oryx.com>
+
+ r18905@truegrounds (orig r4077): alexmv | 2005-11-14 14:20:49 -0500
+ r7146@zoq-fot-pik: chmrr | 2005-11-14 14:20:03 -0500
+ RT-Ticket: 7090
+ RT-Status: resolved
+ RT-Update: correspond
+ * New Japanese .po, from Daisuke Maki <daisuke@wafu.ne.jp>
+
+ r18940@truegrounds (orig r4079): alexmv | 2005-11-14 14:52:57 -0500
+ r7148@zoq-fot-pik: chmrr | 2005-11-14 14:51:58 -0500
+ RT-Ticket: 6559
+ RT-Status: resolved
+ RT-Update: correspond
+ * Tests from Todd Chapman for loading CF from a wrong queue
+
+ r18941@truegrounds (orig r4080): alexmv | 2005-11-14 14:55:17 -0500
+ r7152@zoq-fot-pik: chmrr | 2005-11-14 14:54:43 -0500
+ * Restore rightful .po headers on new french translation
+
+ r18942@truegrounds (orig r4081): alexmv | 2005-11-14 14:59:42 -0500
+ r7155@zoq-fot-pik: chmrr | 2005-11-14 14:59:06 -0500
+ RT-Ticket: 7020
+ RT-Status: resolved
+ RT-Update: correspond
+ * Actually make use of 'style' if it is provided; thanks to Kelly
+ F. Hickel <kfh@mqsoftware.com>
+
+ r18944@truegrounds (orig r4083): alexmv | 2005-11-14 15:43:24 -0500
+ r7159@zoq-fot-pik: chmrr | 2005-11-14 15:42:48 -0500
+ RT-Ticket: 6457
+ RT-Status: resolved
+ RT-Update: correspond
+ * Typo in Ticket_Overlay.pm, found by Todd Chapman <todd@chaka.net>
+
+ r18945@truegrounds (orig r4084): alexmv | 2005-11-14 15:51:27 -0500
+ r7161@zoq-fot-pik: chmrr | 2005-11-14 15:50:56 -0500
+ RT-Ticket: 6458
+ RT-Status: resolved
+ RT-Update: correspond
+ * Removed extra return argument from _AddLink, thanks to Todd Chapman
+ <todd@chaka.net>
+
+ r18946@truegrounds (orig r4085): alexmv | 2005-11-14 16:30:12 -0500
+ r7163@zoq-fot-pik: chmrr | 2005-11-14 16:29:36 -0500
+ RT-Ticket: 6507
+ RT-Status: resolved
+ RT-Update: correspond
+ * Standardize fonts to "Verdana, Arial, Helvetica, sans-serif";
+ variant of patch from Maxime Henrion <mux@FreeBSD.org>
+
+ r18947@truegrounds (orig r4086): alexmv | 2005-11-14 16:49:33 -0500
+ r7165@zoq-fot-pik: chmrr | 2005-11-14 16:49:07 -0500
+ RT-Ticket: 7131
+ RT-Status: resolved
+ RT-Update: correspond
+ * The $RT::rtname regex should be case insensitive for matching
+ subjects; thanks to Phil Smith III <psmith@levanta.com> for the
+ catch
+
+ r18948@truegrounds (orig r4087): ruz | 2005-11-14 16:50:12 -0500
+ * fix: really hide hidden paths from callbacks
+ * fix: fetch data from the %cache by one key when store data with other
+ r18950@truegrounds (orig r4089): ruz | 2005-11-14 16:57:36 -0500
+ * revert back mysql.schema, commited by accident
+ r18951@truegrounds (orig r4090): ruz | 2005-11-14 17:02:36 -0500
+ * /Elements/QueryString now supports ARRAY refs, this allow us to handle
+ multiple arguments with the same name, this behaviour is consistent with
+ how HTML::Mason handle arguments
+ r18953@truegrounds (orig r4092): alexmv | 2005-11-14 17:35:40 -0500
+ r7175@zoq-fot-pik: chmrr | 2005-11-14 17:35:03 -0500
+ RT-Ticket: 7010
+ RT-Status: resolved
+ RT-Update: correspond
+ * Treat our email addresses as case-insensitive
+
+ r18957@truegrounds (orig r4096): alexmv | 2005-11-14 18:34:44 -0500
+ r7182@zoq-fot-pik: chmrr | 2005-11-14 18:34:13 -0500
+ RT-Ticket: 6994
+ RT-Status: resolved
+ RT-Update: correspond
+ * Sort custom vield values by SortOrder, then *Name*, then id; patch
+ from Troy Davis <troy@nack.net>
+
+ r18992@truegrounds (orig r4120): robert | 2005-11-19 22:52:28 -0500
+ r4186@bear: rspier | 2005-11-19 19:51:38 -0800
+ typo fix: s/load/Load/
+
+
+
+
+
+ r3077@cubic-pc (orig r4199): jesse | 2005-12-01 00:32:08 +0300
+ r19616@truegrounds: jesse | 2005-11-30 16:11:33 -0500
+ * Reminders typo
+
+ r3078@cubic-pc (orig r4221): jesse | 2005-12-03 02:07:20 +0300
+ r19696@truegrounds: jesse | 2005-12-02 17:59:08 -0500
+ * typo fox
+
+ r3079@cubic-pc (orig r4222): jesse | 2005-12-03 02:07:34 +0300
+ r19698@truegrounds: jesse | 2005-12-02 18:00:25 -0500
+ r19693@truegrounds: jesse | 2005-12-02 17:48:24 -0500
+ r19588@truegrounds: jesse | 2005-11-30 16:00:10 -0500
+ * Bump to 3.4.5rc1
+ r19674@truegrounds: jesse | 2005-12-01 23:13:50 -0500
+ * Added a print stylesheet from Koos van den Hout
+ r19688@truegrounds: jesse | 2005-12-02 17:01:28 -0500
+ RT-Ticket: 6962
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Rolf Grossmann to fix some bogosity in the query builder
+
+
+
+
+ r3080@cubic-pc (orig r4267): jesse | 2005-12-08 08:44:10 +0300
+ r19796@truegrounds: jesse | 2005-12-08 00:41:36 -0500
+ * ModifyAll.html has two copies of Owner. Disambiguate.
+
+ r3081@cubic-pc (orig r4268): jesse | 2005-12-08 08:45:05 +0300
+ r19797@truegrounds: jesse | 2005-12-08 00:42:14 -0500
+ *Also, deal with the case where the user doesn't change either value
+
+ r3082@cubic-pc (orig r4409): jesse | 2006-01-19 18:14:40 +0300
+ r22718@truegrounds: jesse | 2006-01-19 09:01:31 -0500
+ Merge forward from RT 3.4 and Quebec
+
+ r22386@truegrounds: jesse | 2006-01-13 12:50:56 -0500
+ r19822@truegrounds (orig r4253): alexmv | 2005-12-06 18:29:47 +0100
+ r7565@zoq-fot-pik: chmrr | 2005-12-06 12:28:41 -0500
+ * Tailing test -- paging, ORDER BY, JOIN, and DISTINCT collude to
+ produce incorrect results, unless the DISTINCT is done very carefully.
+ See the latest DBIx-SearchBuilder.
+
+ r20865@truegrounds (orig r4330): alexmv | 2005-12-19 20:48:21 +0100
+ r7854@zoq-fot-pik: chmrr | 2005-12-19 14:47:41 -0500
+ * Squish out duplicate SortOrders in ObjectCustomFields if they
+ exist; this should be impossible, so the O(n) updates is mostly
+ justified.
+
+ r20866@truegrounds (orig r4331): alexmv | 2005-12-19 21:15:17 +0100
+ r7856@zoq-fot-pik: chmrr | 2005-12-19 15:14:45 -0500
+ * Checking Principal isn't enough to know if this is the current user
+ -- we could be being passed our own email address, instead of our own
+ principal.
+
+ r22376@truegrounds (orig r4387): alexmv | 2006-01-12 17:24:24 +0100
+ r8635@zoq-fot-pik: chmrr | 2006-01-12 11:23:39 -0500
+ * Parse embedded newlines
+ * Anchor column alias matches at beginning and end so they don't pick
+ up things like 'CustomField-Summary Status' as =~ /Summary/
+
+
+ r22387@truegrounds: jesse | 2006-01-13 12:56:51 -0500
+ * Pulled up changes from RT 3.4.5
+
+ r19695@truegrounds: jesse | 2005-12-02 23:58:50 +0100
+ * RC2
+ r20429@truegrounds: jesse | 2005-12-13 21:25:39 +0100
+ r19996@truegrounds (orig r4303): alexmv | 2005-12-13 13:58:20 -0500
+ r7707@zoq-fot-pik: chmrr | 2005-12-13 13:54:45 -0500
+ * I don't think this join to Attachments is needed or useful -- it means you don't see changes with no attachments
+
+
+ r20435@truegrounds: jesse | 2005-12-13 22:51:06 +0100
+ * warning silencing for a log message
+ r20436@truegrounds: jesse | 2005-12-13 22:51:41 +0100
+ * 3.4.5rc3
+ r22357@truegrounds: jesse | 2006-01-11 18:20:01 +0100
+ RT-Ticket: 7222
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * German translation update from Dirk Pape
+ r22369@truegrounds: jesse | 2006-01-12 16:23:48 +0100
+ * Forced timezone for a date test to GMT, since it's searching on subjective dates
+ r22370@truegrounds: jesse | 2006-01-12 16:25:19 +0100
+ * Silence a warning introduced by a patch to fix oracle installs
+ r22371@truegrounds: jesse | 2006-01-12 16:25:39 +0100
+ * This be 3.4.5
+
+ r22715@truegrounds: jesse | 2006-01-19 08:49:53 -0500
+ r22708@truegrounds (orig r4400): alexmv | 2006-01-18 16:35:37 -0500
+ r8683@zoq-fot-pik: chmrr | 2006-01-18 16:34:42 -0500
+ * Correctly deal with 0-length field values; now passes test suite
+ again. Mea culpa.
+
+ r22709@truegrounds (orig r4401): alexmv | 2006-01-18 17:59:16 -0500
+ r8689@zoq-fot-pik: chmrr | 2006-01-18 17:58:29 -0500
+ * Support multiple requestors, ccs, adminccs, or links on one line
+
+ r22710@truegrounds (orig r4402): alexmv | 2006-01-18 23:15:31 -0500
+ r8691@zoq-fot-pik: chmrr | 2006-01-18 23:14:54 -0500
+ * Document and standardize UpdateType
+ * Fix another possible infinite loop bug
+
+ r22711@truegrounds (orig r4403): alexmv | 2006-01-19 00:10:27 -0500
+ r8693@zoq-fot-pik: chmrr | 2006-01-19 00:09:49 -0500
+ * ForceOwner to force ownership
+
+ r22712@truegrounds (orig r4404): alexmv | 2006-01-19 00:13:52 -0500
+ r8695@zoq-fot-pik: chmrr | 2006-01-19 00:13:12 -0500
+ * Removed debugging statements
+
+
+ r22717@truegrounds: jesse | 2006-01-19 08:59:52 -0500
+ * Merged forward from RT 3.4
+
+
+ r3083@cubic-pc (orig r4456): jesse | 2006-02-01 04:16:39 +0300
+ r23030@truegrounds: jesse | 2006-01-31 18:51:02 -0500
+ * Added the ability to make custom fields link to and include content from other systems.
+
+ r3084@cubic-pc (orig r4457): jesse | 2006-02-01 04:16:50 +0300
+ r23031@truegrounds: jesse | 2006-01-31 18:52:13 -0500
+ * Added a note about the AHAH support
+
+ r3085@cubic-pc (orig r4496): jesse | 2006-02-04 07:13:43 +0300
+ r23050@truegrounds: jesse | 2006-02-03 13:34:17 -0500
+ * Added a bunch more callbacks for great justice.
+
+ r3086@cubic-pc (orig r4518): jesse | 2006-02-09 22:20:22 +0300
+ r23555@truegrounds: jesse | 2006-02-09 14:19:23 -0500
+ * missing _ in regex
+
+ r3087@cubic-pc (orig r4519): jesse | 2006-02-09 22:59:12 +0300
+ r23569@truegrounds: jesse | 2006-02-09 14:48:54 -0500
+ * open links in new windows
+
+ r3088@cubic-pc (orig r4524): jesse | 2006-02-10 07:14:17 +0300
+ r23587@truegrounds: jesse | 2006-02-09 23:13:22 -0500
+ * Reminder editing updates
+
+
+ r3089@cubic-pc (orig r4525): jesse | 2006-02-10 22:50:21 +0300
+ r23593@truegrounds: jesse | 2006-02-10 14:47:08 -0500
+ * Force the content type inside a js comment
+
+ r3090@cubic-pc (orig r4526): jesse | 2006-02-10 22:50:33 +0300
+ r23594@truegrounds: jesse | 2006-02-10 14:49:27 -0500
+ * wrap the mason directive for setting the content type in a js comment
+
+
+ r3091@cubic-pc (orig r4541): jesse | 2006-02-16 22:45:36 +0300
+ r23812@truegrounds: jesse | 2006-02-16 11:42:23 -0800
+ * Chaldea release engineering
+
+ r3092@cubic-pc (orig r4543): jesse | 2006-02-17 00:34:46 +0300
+ r23814@truegrounds: jesse | 2006-02-16 13:33:40 -0800
+ * try to make rt.js do the right thing with headers
+
+ r3093@cubic-pc (orig r4551): jesse | 2006-02-17 05:29:47 +0300
+ r23826@truegrounds: jesse | 2006-02-16 18:25:05 -0800
+ * Backport tickets_overlay from 3.5. Fix a bug that stopped search on global custom fields
+
+ r3094@cubic-pc (orig r4552): jesse | 2006-02-17 21:03:41 +0300
+ r23835@truegrounds: jesse | 2006-02-17 10:02:28 -0800
+ * Chaldea r7
+
+ r3095@cubic-pc (orig r4559): jesse | 2006-02-21 22:13:42 +0300
+ r24473@truegrounds: jesse | 2006-02-21 14:05:29 -0500
+ r24470@truegrounds: jesse | 2006-02-21 13:50:15 -0500
+ r23039@truegrounds (orig r4459): alexmv | 2006-01-31 21:33:58 -0500
+ r8863@zoq-fot-pik: chmrr | 2006-01-31 21:33:12 -0500
+ * Collapse ForceOwner and Owner
+ * Fix updating of (Admin)Ccs
+ * Default UpdateType to 'correspond'
+ * Default content-type to 'text/plain'
+
+ r23040@truegrounds (orig r4460): alexmv | 2006-01-31 23:42:36 -0500
+ r8868@zoq-fot-pik: chmrr | 2006-01-31 22:24:18 -0500
+ * Untabify and adjust indenting in one or two places
+
+ r23041@truegrounds (orig r4461): alexmv | 2006-01-31 23:42:41 -0500
+ r8869@zoq-fot-pik: chmrr | 2006-01-31 23:41:57 -0500
+ * Custom field updating during processing
+ * Better error handling when ticket id doesn't exist during update
+
+ r23082@truegrounds (orig r4473): alexmv | 2006-02-03 14:32:54 -0500
+ r8895@zoq-fot-pik: chmrr | 2006-02-03 14:32:05 -0500
+ * 'id' is not longer required to be the first column. In fact, it's
+ not required at all. Blank or non-existant 'id' columns will cause it
+ to assume the ticket is new, and come up with an automatic template id
+ for the row.
+
+ r23515@truegrounds (orig r4504): alexmv | 2006-02-08 15:01:17 -0500
+ r8969@zoq-fot-pik: chmrr | 2006-02-08 15:00:00 -0500
+ * Whitespace fixes in Results.tsv
+ * Spit out custom fields as CF-...
+ * During offline upload parsing, try to treat date as ISO first,
+ falling back to unknown
+ * Keep offline upload form from tacking on an extra newline each
+ submit
+ * Minimize yo-yo-ing of status if possible
+ * Allow false values as possible values, as long as they're defined
+ * Cc, AdminCc, and Requestor may be either email addresses or users
+
+ r23628@truegrounds (orig r4527): alexmv | 2006-02-10 19:10:22 -0500
+ r9038@zoq-fot-pik: chmrr | 2006-02-10 19:09:21 -0500
+ * Accept either singular or plural forms of requestor, cc, admincc
+
+
+ r24472@truegrounds: jesse | 2006-02-21 13:57:01 -0500
+ r22894@truegrounds: jesse | 2006-01-24 07:44:05 -0500
+ * Note that our SQLite dependency is 1.0
+ r22958@truegrounds: jesse | 2006-01-25 07:08:34 -0500
+ From: Joop van de Wege <JoopvandeWege@mococo.nl>
+ Message-Id: <20060125125248.1A97.JOOPVANDEWEGE@mococo.nl>
+
+ > There is atleast one problem that I have spotted and that is that
+ > schema.Oracle contains two empty lines in CREATE TABLE
+ > ObjectCustomFieldValues which don't belong there.
+ > That is the second set of errors you get. The first is an indication
+ > that an sequence with that name already exists in the schema of that
+ > Oracle users you're RT installing in.
+
+ r22960@truegrounds: jesse | 2006-01-25 07:47:07 -0500
+ * a couple added lines of docs to the cli
+ r22962@truegrounds: jesse | 2006-01-25 08:18:09 -0500
+ * Updated mandatory fields for ticket creation forms
+ r24471@truegrounds: jesse | 2006-02-21 13:51:18 -0500
+ r23026@truegrounds (orig r4448): kevinr | 2006-01-30 19:25:47 -0500
+ r10537@SAD-GIRL-IN-SNOW: kevinr | 2006-01-30 19:20:52 -0500
+ RT-Ticket: 7289
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Updated German translation (thanks to Thorsten Brumm)
+
+ r23132@truegrounds (orig r4497): alexmv | 2006-02-04 18:34:45 -0500
+ r8936@zoq-fot-pik: chmrr | 2006-02-04 18:24:38 -0500
+ * Only rmtree if we have something to rm; keeps rmtree from
+ complaining about 'Not root path(s) specified'
+
+ r23133@truegrounds (orig r4498): alexmv | 2006-02-04 18:34:51 -0500
+ r8937@zoq-fot-pik: chmrr | 2006-02-04 18:33:57 -0500
+ RT-Ticket: 7329
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Use SelectNewTicketQueue instead of SelectQueue
+
+
+
+
+
+
+ r3096@cubic-pc (orig r5258): ruz | 2006-05-19 06:13:05 +0400
+ merge from 3.4
+
+ r2079@cubic-pc (orig r4686): jesse | 2006-03-05 01:47:46 +0300
+ r22962@truegrounds: jesse | 2006-01-25 05:18:09 -0800
+ * Updated mandatory fields for ticket creation forms
+
+ r2190@cubic-pc (orig r4799): jesse | 2006-03-23 09:37:59 +0300
+ r30313@truegrounds: jesse | 2006-03-23 01:36:27 -0500
+ * Better mp2 bulletproofing
+
+ r2191@cubic-pc (orig r4814): jesse | 2006-03-24 06:40:37 +0300
+ r10436@hualien: jesse | 2006-03-23 22:40:25 -0500
+ * It helps when there aren't typos
+
+ r2262@cubic-pc (orig r4847): alexmv | 2006-03-29 00:50:07 +0400
+ r11918@zoq-fot-pik: chmrr | 2006-03-28 15:49:56 -0500
+ * Backport TXN fixes from 3.7 and 3.5
+
+ r2321@cubic-pc (orig r4924): jesse | 2006-03-31 06:07:23 +0400
+ r10636@hualien: jesse | 2006-03-31 11:06:57 +0900
+ RT-Ticket: 7398
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Added a "RH" RedHat layout option to config.layout -- Paulo Matos
+
+ r2396@cubic-pc (orig r5047): ruz | 2006-04-18 04:40:06 +0400
+ * check and report error to the logs
+ r2421@cubic-pc (orig r5060): jesse | 2006-04-24 18:49:40 +0400
+ r11842@hualien: jesse | 2006-04-24 10:49:13 -0400
+ The following patch adds the useful LastUpdated field to the fields
+ returned through the REST interface.
+ David - who starts to wonder if his patches are actually read by someone :-)
+ --
+ David Schweikert | phone: +41 44 632 7019
+ System manager ISG.EE | walk: ETH Zentrum, ETL F24.1
+ ETH Zurich, Switzerland | web: http://people.ee.ethz.ch/dws
+
+
+ r2422@cubic-pc (orig r5064): ruz | 2006-04-25 00:42:43 +0400
+ * max subject is 200 character long
+ r2423@cubic-pc (orig r5065): ruz | 2006-04-25 01:25:15 +0400
+ * convert only if $enc'oding contains something
+ r2424@cubic-pc (orig r5066): ruz | 2006-04-25 04:21:32 +0400
+ * simple tests for Attachments manipulation from web interface
+ r2548@cubic-pc (orig r5115): ruz | 2006-04-26 03:24:45 +0400
+ * (cond) && 'selected' outputs 0 if condition fails on my system
+ r2549@cubic-pc (orig r5116): ruz | 2006-04-26 03:35:09 +0400
+ * get queue ID from page
+ r2658@cubic-pc (orig r5118): jesse | 2006-04-26 06:43:31 +0400
+ r11882@hualien: jesse | 2006-04-25 22:43:11 -0400
+ * Mark Eichin picked up that http://lists.fsck.com/pipermail/rt-devel/2004-August/006216.html had never been applied.
+
+ rt ls -l broke because of it, if your RT server wasn't at /
+
+ r2862@cubic-pc (orig r5183): jesse | 2006-05-09 06:31:56 +0400
+ r13313@hualien: jesse | 2006-05-08 12:01:55 -0400
+ * Finding disabled groups should actually find them, now
+
+ r2863@cubic-pc (orig r5184): jesse | 2006-05-09 06:32:10 +0400
+ r13314@hualien: jesse | 2006-05-08 12:14:26 -0400
+ * Minor reformatting
+
+ r2864@cubic-pc (orig r5185): jesse | 2006-05-09 06:32:19 +0400
+ r13315@hualien: jesse | 2006-05-08 22:31:30 -0400
+ * Mail gateway refactoring to make added functioanlity a bit easier.
+ No (intentional) functional changes.
+
+ r2865@cubic-pc (orig r5186): jesse | 2006-05-09 06:56:20 +0400
+ r13330@hualien: jesse | 2006-05-08 22:55:56 -0400
+ * Reed Loden caught a perltidy error that, somewhat terrifiyingly, was still a valid mason page
+
+ r2866@cubic-pc (orig r5187): jesse | 2006-05-09 08:48:10 +0400
+ r13332@hualien: jesse | 2006-05-09 00:47:49 -0400
+ * Mismatched parens
+
+ r2867@cubic-pc (orig r5206): ruz | 2006-05-12 00:48:53 +0400
+ * return values checking and more logging on errors
+ r2868@cubic-pc (orig r5207): ruz | 2006-05-12 00:56:24 +0400
+ * more checks on attachments processing
+ r2869@cubic-pc (orig r5208): ruz | 2006-05-12 02:24:17 +0400
+ * if ( not $xxx || $xxx->foo ) is equivalent to
+ if ( not ( $xxx || $xxx->foo ) ) due to perl5 rules
+ which is not expected behaviour
+ r2870@cubic-pc (orig r5209): ruz | 2006-05-12 02:31:58 +0400
+ * user do next steps:
+ 1) open ticket #1
+ 2) click reply
+ 3) upload attachment
+ 4) open ticket #2 in another browser window
+ 5) send reply to the ticket #1
+ RT looses uploaded attachment due to step 4) as RT tries
+ to add attchement to the ticket #2 and drops them from session.
+ As solution don't ProcessTicketMessage if there is attachments,
+ but only if there is real update message.
+ r2885@cubic-pc (orig r5216): ruz | 2006-05-13 00:54:41 +0400
+ * add Timezone argument in SetToMidnight
+ r2886@cubic-pc (orig r5217): ruz | 2006-05-13 01:02:54 +0400
+ * use SetToMidnight( Timezone => 'server' ) to calc start and end of the day
+ r2888@cubic-pc (orig r5218): ruz | 2006-05-13 04:31:33 +0400
+ * get rid of "masks earlier declaration" warnings
+ r2889@cubic-pc (orig r5238): ruz | 2006-05-17 02:39:59 +0400
+ * really noisy warning
+ *NOTE* that option we use is not described in config
+ r2895@cubic-pc (orig r5249): ruz | 2006-05-18 20:17:47 +0400
+ * add bug comment
+ r2896@cubic-pc (orig r5256): ruz | 2006-05-19 05:45:58 +0400
+ * allow to complete actions in mail plugins
+ r2897@cubic-pc (orig r5257): ruz | 2006-05-19 05:53:40 +0400
+ * minor
+
+ r3098@cubic-pc (orig r5260): ruz | 2006-05-19 06:37:34 +0400
+ QUEBEC->CHALDEA
+
+ r1950@cubic-pc (orig r4560): jesse | 2006-02-21 22:14:19 +0300
+ r24472@truegrounds: jesse | 2006-02-21 13:57:01 -0500
+ r22894@truegrounds: jesse | 2006-01-24 07:44:05 -0500
+ * Note that our SQLite dependency is 1.0
+ r22958@truegrounds: jesse | 2006-01-25 07:08:34 -0500
+ From: Joop van de Wege <JoopvandeWege@mococo.nl>
+ Message-Id: <20060125125248.1A97.JOOPVANDEWEGE@mococo.nl>
+
+ > There is atleast one problem that I have spotted and that is that
+ > schema.Oracle contains two empty lines in CREATE TABLE
+ > ObjectCustomFieldValues which don't belong there.
+ > That is the second set of errors you get. The first is an indication
+ > that an sequence with that name already exists in the schema of that
+ > Oracle users you're RT installing in.
+
+ r22960@truegrounds: jesse | 2006-01-25 07:47:07 -0500
+ * a couple added lines of docs to the cli
+ r22962@truegrounds: jesse | 2006-01-25 08:18:09 -0500
+ * Updated mandatory fields for ticket creation forms
+ r24471@truegrounds: jesse | 2006-02-21 13:51:18 -0500
+ r23026@truegrounds (orig r4448): kevinr | 2006-01-30 19:25:47 -0500
+ r10537@SAD-GIRL-IN-SNOW: kevinr | 2006-01-30 19:20:52 -0500
+ RT-Ticket: 7289
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Updated German translation (thanks to Thorsten Brumm)
+
+ r23132@truegrounds (orig r4497): alexmv | 2006-02-04 18:34:45 -0500
+ r8936@zoq-fot-pik: chmrr | 2006-02-04 18:24:38 -0500
+ * Only rmtree if we have something to rm; keeps rmtree from
+ complaining about 'Not root path(s) specified'
+
+ r23133@truegrounds (orig r4498): alexmv | 2006-02-04 18:34:51 -0500
+ r8937@zoq-fot-pik: chmrr | 2006-02-04 18:33:57 -0500
+ RT-Ticket: 7329
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Use SelectNewTicketQueue instead of SelectQueue
+
+
+
+
+
+ r2172@cubic-pc (orig r4721): alexmv | 2006-03-10 00:53:17 +0300
+ r8469@zoq-fot-pik: chmrr | 2006-03-09 16:52:49 -0500
+ * Include Cc and AdminCc in CSV download
+
+ r2173@cubic-pc (orig r4722): alexmv | 2006-03-10 02:13:22 +0300
+ r8473@zoq-fot-pik: chmrr | 2006-03-09 18:13:13 -0500
+ * Backport from 3.7 for CustomFields fixes
+
+ r2325@cubic-pc (orig r4931): alexmv | 2006-04-01 02:51:38 +0400
+ r12069@zoq-fot-pik: chmrr | 2006-03-31 17:50:29 -0500
+ * Bump SearchBuilder dependency
+
+ r2907@cubic-pc (orig r4931): svm | 2006-05-19 06:11:25 +0400
+ SVM: initializing mirror for /mirrors/branches/QUEBEC-EXPERIMENTAL
+ r3097@cubic-pc (orig r5259): ruz | 2006-05-19 06:18:42 +0400
+ merge from 3.4
+
+ r2079@cubic-pc (orig r4686): jesse | 2006-03-05 01:47:46 +0300
+ r22962@truegrounds: jesse | 2006-01-25 05:18:09 -0800
+ * Updated mandatory fields for ticket creation forms
+
+ r2190@cubic-pc (orig r4799): jesse | 2006-03-23 09:37:59 +0300
+ r30313@truegrounds: jesse | 2006-03-23 01:36:27 -0500
+ * Better mp2 bulletproofing
+
+ r2191@cubic-pc (orig r4814): jesse | 2006-03-24 06:40:37 +0300
+ r10436@hualien: jesse | 2006-03-23 22:40:25 -0500
+ * It helps when there aren't typos
+
+ r2262@cubic-pc (orig r4847): alexmv | 2006-03-29 00:50:07 +0400
+ r11918@zoq-fot-pik: chmrr | 2006-03-28 15:49:56 -0500
+ * Backport TXN fixes from 3.7 and 3.5
+
+ r2321@cubic-pc (orig r4924): jesse | 2006-03-31 06:07:23 +0400
+ r10636@hualien: jesse | 2006-03-31 11:06:57 +0900
+ RT-Ticket: 7398
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Added a "RH" RedHat layout option to config.layout -- Paulo Matos
+
+ r2396@cubic-pc (orig r5047): ruz | 2006-04-18 04:40:06 +0400
+ * check and report error to the logs
+ r2421@cubic-pc (orig r5060): jesse | 2006-04-24 18:49:40 +0400
+ r11842@hualien: jesse | 2006-04-24 10:49:13 -0400
+ The following patch adds the useful LastUpdated field to the fields
+ returned through the REST interface.
+ David - who starts to wonder if his patches are actually read by someone :-)
+ --
+ David Schweikert | phone: +41 44 632 7019
+ System manager ISG.EE | walk: ETH Zentrum, ETL F24.1
+ ETH Zurich, Switzerland | web: http://people.ee.ethz.ch/dws
+
+
+ r2422@cubic-pc (orig r5064): ruz | 2006-04-25 00:42:43 +0400
+ * max subject is 200 character long
+ r2423@cubic-pc (orig r5065): ruz | 2006-04-25 01:25:15 +0400
+ * convert only if $enc'oding contains something
+ r2424@cubic-pc (orig r5066): ruz | 2006-04-25 04:21:32 +0400
+ * simple tests for Attachments manipulation from web interface
+ r2548@cubic-pc (orig r5115): ruz | 2006-04-26 03:24:45 +0400
+ * (cond) && 'selected' outputs 0 if condition fails on my system
+ r2549@cubic-pc (orig r5116): ruz | 2006-04-26 03:35:09 +0400
+ * get queue ID from page
+ r2658@cubic-pc (orig r5118): jesse | 2006-04-26 06:43:31 +0400
+ r11882@hualien: jesse | 2006-04-25 22:43:11 -0400
+ * Mark Eichin picked up that http://lists.fsck.com/pipermail/rt-devel/2004-August/006216.html had never been applied.
+
+ rt ls -l broke because of it, if your RT server wasn't at /
+
+ r2862@cubic-pc (orig r5183): jesse | 2006-05-09 06:31:56 +0400
+ r13313@hualien: jesse | 2006-05-08 12:01:55 -0400
+ * Finding disabled groups should actually find them, now
+
+ r2863@cubic-pc (orig r5184): jesse | 2006-05-09 06:32:10 +0400
+ r13314@hualien: jesse | 2006-05-08 12:14:26 -0400
+ * Minor reformatting
+
+ r2864@cubic-pc (orig r5185): jesse | 2006-05-09 06:32:19 +0400
+ r13315@hualien: jesse | 2006-05-08 22:31:30 -0400
+ * Mail gateway refactoring to make added functioanlity a bit easier.
+ No (intentional) functional changes.
+
+ r2865@cubic-pc (orig r5186): jesse | 2006-05-09 06:56:20 +0400
+ r13330@hualien: jesse | 2006-05-08 22:55:56 -0400
+ * Reed Loden caught a perltidy error that, somewhat terrifiyingly, was still a valid mason page
+
+ r2866@cubic-pc (orig r5187): jesse | 2006-05-09 08:48:10 +0400
+ r13332@hualien: jesse | 2006-05-09 00:47:49 -0400
+ * Mismatched parens
+
+ r2867@cubic-pc (orig r5206): ruz | 2006-05-12 00:48:53 +0400
+ * return values checking and more logging on errors
+ r2868@cubic-pc (orig r5207): ruz | 2006-05-12 00:56:24 +0400
+ * more checks on attachments processing
+ r2869@cubic-pc (orig r5208): ruz | 2006-05-12 02:24:17 +0400
+ * if ( not $xxx || $xxx->foo ) is equivalent to
+ if ( not ( $xxx || $xxx->foo ) ) due to perl5 rules
+ which is not expected behaviour
+ r2870@cubic-pc (orig r5209): ruz | 2006-05-12 02:31:58 +0400
+ * user do next steps:
+ 1) open ticket #1
+ 2) click reply
+ 3) upload attachment
+ 4) open ticket #2 in another browser window
+ 5) send reply to the ticket #1
+ RT looses uploaded attachment due to step 4) as RT tries
+ to add attchement to the ticket #2 and drops them from session.
+ As solution don't ProcessTicketMessage if there is attachments,
+ but only if there is real update message.
+ r2885@cubic-pc (orig r5216): ruz | 2006-05-13 00:54:41 +0400
+ * add Timezone argument in SetToMidnight
+ r2886@cubic-pc (orig r5217): ruz | 2006-05-13 01:02:54 +0400
+ * use SetToMidnight( Timezone => 'server' ) to calc start and end of the day
+ r2888@cubic-pc (orig r5218): ruz | 2006-05-13 04:31:33 +0400
+ * get rid of "masks earlier declaration" warnings
+ r2889@cubic-pc (orig r5238): ruz | 2006-05-17 02:39:59 +0400
+ * really noisy warning
+ *NOTE* that option we use is not described in config
+ r2895@cubic-pc (orig r5249): ruz | 2006-05-18 20:17:47 +0400
+ * add bug comment
+ r2896@cubic-pc (orig r5256): ruz | 2006-05-19 05:45:58 +0400
+ * allow to complete actions in mail plugins
+ r2897@cubic-pc (orig r5257): ruz | 2006-05-19 05:53:40 +0400
+ * minor
+
+
+ r3131@cubic-pc (orig r5314): ruz | 2006-05-28 03:34:03 +0400
+ * revert wrong merge from quebec branch
+ r3264@cubic-pc (orig r5398): ruz | 2006-06-17 00:16:45 +0400
+ merge 3.4 -> QUEBEC -> CHALDEA
+
+ r3263@cubic-pc (orig r5397): ruz | 2006-06-17 00:15:18 +0400
+ merge 3.4 -> QUEBEC
+
+ r3099@cubic-pc (orig r5268): jesse | 2006-05-20 01:17:41 +0400
+ r13935@hualien: jesse | 2006-05-19 17:17:27 -0400
+ * There were divergent copies of this code. The EmailParser code was more correct
+
+ r3130@cubic-pc (orig r5310): ruz | 2006-05-27 04:39:49 +0400
+ * when we could parse URI, for example object doesn't exist
+ fallback to RT::URI::base resolver, so $uri->IsLocal and
+ other methods wouldn't die but return undef
+ r3132@cubic-pc (orig r5315): ruz | 2006-05-28 15:19:20 +0400
+ * Use "Requestor.id = $requestor->id" search instead of search by email address
+ as latter is not indexed
+ r3133@cubic-pc (orig r5317): ruz | 2006-05-31 00:13:02 +0400
+ * If current user changes owner from somebody else to nobody user,
+ the action fails with "You can only reassign tickets that you own
+ or that are unowned", but we must change owner if he has no right
+ to own tickets in dest queue. Do it with Force and with SystemUser
+ context.
+
+
+ r3498@cubic-pc (orig r5486): ruz | 2006-06-29 02:39:35 +0400
+ merge QUEBEC -> CHALDEA
+
+ r3492@cubic-pc (orig r5484): ruz | 2006-06-29 01:39:57 +0400
+ merge 3.4->QUEBEC
+
+ r3273@cubic-pc (orig r5400): ruz | 2006-06-17 04:40:24 +0400
+ * not default mail plugins has been broken during last refactoring
+ ** move a code back into its scope
+ ** don't forget to store $_ in $Class when $_ matches ^RT::Interface::Email
+ r3310@cubic-pc (orig r5476): ruz | 2006-06-28 01:21:07 +0400
+ * nothing special, small changes I'd changed during
+ the hunt over a bug
+ r3311@cubic-pc (orig r5480): ruz | 2006-06-28 04:05:49 +0400
+ * minor formatting
+ r3491@cubic-pc (orig r5482): ruz | 2006-06-29 01:25:18 +0400
+ * add tests for unsafe mailgate commands
+ * fix bugs that were introduced during Email.pm refactoring
+
+
+ r3668@cubic-pc (orig r5826): ruz | 2006-08-30 22:13:47 +0400
+ merge QUEBEC -> CHALDEA
+
+ r3667@cubic-pc (orig r5825): ruz | 2006-08-30 19:47:20 +0400
+ merge 3.4->QUEBEC
+
+ r3523@cubic-pc (orig r5496): ruz | 2006-07-01 00:09:08 +0400
+ Changes:
+ * new config option $OldestTransactionsFirst that allow
+ administrator to reverse order of transactions on
+ history page
+
+ r3524@cubic-pc (orig r5520): ruz | 2006-07-04 09:36:46 +0400
+ * forgot to add option to config
+ r3525@cubic-pc (orig r5521): ruz | 2006-07-04 09:38:03 +0400
+ * report error when couldn't create CF
+ r3526@cubic-pc (orig r5522): ruz | 2006-07-04 09:39:21 +0400
+ * we never should call exit from libs
+ r3527@cubic-pc (orig r5534): ruz | 2006-07-06 19:19:46 +0400
+ rt-crontool
+ * add --transaction argument with two possible values: 'first' and 'last'
+ * add --transaction-type argument to allow users select type of transactions
+ ** these transactions would be passed to scrips for processing, so users
+ can use conditions, actions and templates that check or use properties of
+ transaction
+
+ * also some existant actions, conditions and templates require scrip or
+ scrip action objects to process normally, as we have no these objects
+ available we now pass void (not loaded) objects. This change would allow
+ users to use notify actions with crontool.
+ r3528@cubic-pc (orig r5570): kevinr | 2006-07-14 00:21:31 +0400
+ r14836@sad-girl-in-snow: kevinr | 2006-07-13 16:17:43 -0400
+ * The RT::Condition::Generic docs were wrong... fixed.
+
+ r3529@cubic-pc (orig r5616): jesse | 2006-07-20 04:40:57 +0400
+ r14217@pinglin: jesse | 2006-07-19 17:39:08 -0700
+ * crit was being called on the wrong object. Thanks to Todd Chapman
+
+ r3530@cubic-pc (orig r5624): jesse | 2006-07-20 21:48:07 +0400
+ r14229@pinglin: jesse | 2006-07-20 10:47:51 -0700
+ * The new history ordering feature was backported backwards.
+
+ r3569@cubic-pc (orig r5669): ruz | 2006-07-28 20:06:56 +0400
+ * bump 3.4.6rc1
+ r3653@cubic-pc (orig r5777): ruz | 2006-08-17 02:55:37 +0400
+ * add LeftColumn and RightColumn callbacks to the summary element
+ r3654@cubic-pc (orig r5778): ruz | 2006-08-17 02:56:58 +0400
+ * display one value without html lists
+ r3655@cubic-pc (orig r5779): ruz | 2006-08-17 02:58:46 +0400
+ * allow user to add zero CF values
+ r3661@cubic-pc (orig r5818): ruz | 2006-08-27 20:34:52 +0400
+ * add additional classes to records in tables with ticket's properties
+ ** so it's possible to change visiual appearance of different fields
+ like dates, queue, priority status and other
+ r3662@cubic-pc (orig r5819): ruz | 2006-08-29 20:59:30 +0400
+ * Code:
+ $cf->Load(...)
+ $field = $cf->id;
+ unless( $field =~ /^\d+$/ )...
+ may produce unint warning if field wasn't loaded,
+ use "unless( $cf->id )" instead as we do everywhere
+
+ r3663@cubic-pc (orig r5820): ruz | 2006-08-29 21:03:36 +0400
+ * default title to empty string as we compare it with 'NEWLINE' string
+ in several places, drops uninit warnings
+ r3664@cubic-pc (orig r5821): ruz | 2006-08-29 21:07:03 +0400
+ * get rid of unint warnings
+ * make a log message shorter to be more readable
+ r3665@cubic-pc (orig r5822): ruz | 2006-08-29 23:10:01 +0400
+ * fix decoding of the MIME fields, this should fix:
+ ** problems with non-ascii names of attachments
+ ** problems with partly encoded fields with '=' chars
+ in not encoded parts, for example:
+
+ X-MyHeader: key="plain"; key="=?encoded?="
+ X-MyHeader: key="=?encoded?="; key="plain"
+
+
+
+ r3669@cubic-pc (orig r5827): ruz | 2006-08-31 00:11:30 +0400
+ * typo fix
+ r3670@cubic-pc (orig r5828): ruz | 2006-08-31 00:12:00 +0400
+ * fix merge bugs
+
+------------------------------------------------------------------------
+r6034 | jesse | 2006-09-25 22:31:11 -0400 (Mon, 25 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+ r27827@pinglin: jesse | 2006-09-25 22:30:55 -0400
+ * New callback 'BeforeShowSummary' on ticket display; handling of people changes on ticket display
+
+------------------------------------------------------------------------
+r6033 | jesse | 2006-09-25 22:31:05 -0400 (Mon, 25 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+
+ r27826@pinglin: jesse | 2006-09-25 22:30:02 -0400
+ * Added the ability to search on TimeEstimated
+
+------------------------------------------------------------------------
+r6030 | jesse | 2006-09-25 11:57:01 -0400 (Mon, 25 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+ r27804@pinglin: jesse | 2006-09-25 11:56:46 -0400
+ * That new callback was misnamed
+
+------------------------------------------------------------------------
+r6028 | jesse | 2006-09-25 10:58:40 -0400 (Mon, 25 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+ r27799@pinglin: jesse | 2006-09-25 10:57:18 -0400
+ * New callback in Ticket/Display.html
+
+------------------------------------------------------------------------
+r6027 | jesse | 2006-09-25 10:58:34 -0400 (Mon, 25 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23cfsort.t
+
+ r27798@pinglin: jesse | 2006-09-25 10:57:01 -0400
+ * The custom field tests that have been dying for the last 4 months were miscoded. fixed
+
+------------------------------------------------------------------------
+r6026 | jesse | 2006-09-25 10:58:29 -0400 (Mon, 25 Sep 2006) | 10 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/es.po
+
+ r27796@pinglin: jesse | 2006-09-24 15:42:56 -0400
+ RT-Ticket: 7802
+ RT-Update: correspond
+ RT-Status: resolved
+
+ I18N
+
+ * Updated Spanish translation from Carlos Velasco
+
+
+------------------------------------------------------------------------
+r6004 | jesse | 2006-09-20 14:46:37 -0400 (Wed, 20 Sep 2006) | 9 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/upgrade/3.5.1/content
+
+ r27685@191: jesse | 2006-09-20 20:46:27 +0200
+ RT-Ticket: 7883
+ RT-Status: resolved
+ RT-Update: correspond
+
+ initialdata was fixed already. This needs to be fixed
+ for people who will upgrade from 3.4 to 3.6.2+. --Todd Chapman
+
+
+------------------------------------------------------------------------
+r5991 | ruz | 2006-09-19 07:22:31 -0400 (Tue, 19 Sep 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Tools/MyDay.html
+
+MyDay.html
+* there is no need in $thispage argument
+* merge two %INIT blocks
+* drop unused code
+------------------------------------------------------------------------
+r5990 | ruz | 2006-09-19 07:18:01 -0400 (Tue, 19 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Tools/Elements/Tabs
+
+* drop XEmacs metadata
+------------------------------------------------------------------------
+r5987 | jesse | 2006-09-19 03:40:52 -0400 (Tue, 19 Sep 2006) | 12 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Tools/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Tools/MyDay.html
+
+ r27648@pinglin: jesse | 2006-09-19 09:40:40 +0200
+ RT-Ticket: 7876
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Thanks! Applied:
+
+ From Brian Gallew <geek@burri.to>:
+ As shipped, RT-3.6.1 doesn't have a useful Tools/MyDays.html. I've
+ fixed it up so that it works seemingly correctly. Here is the patch that
+ takes care of it.
+
+------------------------------------------------------------------------
+r5938 | jesse | 2006-09-15 09:50:43 -0400 (Fri, 15 Sep 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldFreeform
+
+ r27502@pinglin: jesse | 2006-09-15 14:50:38 +0100
+ Give Freeform form elements an id. This will be useful
+ in combination with the EditComponentName callback.
+ With a few local mods, date CFs will be able to use the
+ date picker. Without an id the date picker doesn't work.
+ -Todd Chapman
+
+------------------------------------------------------------------------
+r5913 | jesse | 2006-09-11 19:55:41 -0400 (Mon, 11 Sep 2006) | 6 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+
+ r27407@pinglin: jesse | 2006-09-12 00:55:29 +0100
+ RT Essentials mistakenly suggests that users use '/' as an RT::WebPath.
+
+ Do what they mean if they screw up and do that.
+
+
+------------------------------------------------------------------------
+r5888 | jesse | 2006-09-08 16:51:01 -0400 (Fri, 08 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+ r27291@pinglin: jesse | 2006-09-08 14:19:50 -0400
+ * Support for marking RT's HTTP cookie as "secure." Patch from Pavel Ruzicka.
+
+------------------------------------------------------------------------
+r5887 | jesse | 2006-09-08 16:50:50 -0400 (Fri, 08 Sep 2006) | 16 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+
+ r27290@pinglin: jesse | 2006-09-08 13:50:04 -0400
+ Patch from Kevin Murphy to support the following features:
+
+ # If $SuppressInlineTextFiles is set to a true value, then uploaded
+ # text files (text-type attachments with file names) are prevented
+ # from being displayed in-line when viewing a ticket's history.
+
+ Set($SuppressInlineTextFiles, undef);
+
+ # If $DontSearchFileAttachments is set to a true value, then uploaded
+ # files (attachments with file names) are not searched during full-content
+ # ticket searches.
+
+ Set($DontSearchFileAttachments, undef);
+
+
+------------------------------------------------------------------------
+r5886 | jesse | 2006-09-08 16:50:39 -0400 (Fri, 08 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/Approve
+
+ r27286@pinglin: jesse | 2006-09-08 12:51:22 -0400
+ * Finer-grained control of what's displayed on ticket approval from Drew Taylor.
+
+------------------------------------------------------------------------
+r5873 | jesse | 2006-09-06 15:09:06 -0400 (Wed, 06 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/RT__Ticket/ColumnMap
+
+ r27206@pinglin: jesse | 2006-09-06 15:09:02 -0400
+ * Remove trailing '<br/>' from custom fields shown in ticket search results
+
+------------------------------------------------------------------------
+r5872 | jesse | 2006-09-06 15:07:22 -0400 (Wed, 06 Sep 2006) | 11 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r27203@pinglin: jesse | 2006-09-06 15:07:04 -0400
+ RT-Ticket: 7848
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * POSIX::strftime on Solaris isn't robust enough to deal with our Date: header generation.
+ Switched to Date::Format::strftime instead. - ADDED DEPENDENCY: Date::Format
+ Fix thanks to Tim Bishop
+
+
+
+------------------------------------------------------------------------
+r5871 | jesse | 2006-09-06 15:07:16 -0400 (Wed, 06 Sep 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+
+ r27202@pinglin: jesse | 2006-09-06 14:59:10 -0400
+ * Handle Apache::Session database errors a bit more gracefully
+
+------------------------------------------------------------------------
+r5869 | jesse | 2006-09-06 10:26:13 -0400 (Wed, 06 Sep 2006) | 9 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+ r27194@pinglin: jesse | 2006-09-06 10:26:01 -0400
+ RT-Ticket:7852
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Tobias Kremer pointed out a typo in Ticket_Overlay.pm that caused
+ an error when a user got a certain permission denied message
+
+
+------------------------------------------------------------------------
+r5862 | ruz | 2006-09-05 12:55:33 -0400 (Tue, 05 Sep 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+* don't concat $args{'Resolved'} when its value is not true
+------------------------------------------------------------------------
+r5859 | ruz | 2006-09-04 18:29:59 -0400 (Mon, 04 Sep 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+
+Ticket creation
+* /Elements/SelectDate has no argument 'Value'
+* use argument 'Default' to:
+** safe values when custom field validation fails
+** allow users/code to pre-define dates in URL
+------------------------------------------------------------------------
+r5802 | kevinr | 2006-08-23 17:44:22 -0400 (Wed, 23 Aug 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r27527@sad-girl-in-snow: kevinr | 2006-08-23 17:42:09 -0400
+ * We need to append a colon to non-standard headers so MIME::Entity will
+ include them.
+
+------------------------------------------------------------------------
+r5793 | jesse | 2006-08-22 12:44:05 -0400 (Tue, 22 Aug 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/initialdata
+
+ r20626@pinglin: jesse | 2006-08-22 12:43:48 -0400
+ Fix from Jim Meyer to correct a hardcoded RT::WebPath in initialdata. (This doesn't fix users who've already installed)
+
+
+
+------------------------------------------------------------------------
+r5782 | jesse | 2006-08-21 13:17:08 -0400 (Mon, 21 Aug 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+
+ r20599@pinglin: jesse | 2006-08-21 13:17:00 -0400
+ * Fix for "Search by requestor" appearing to break in 3.6.1
+ (Really, an older, deeper bug was unmasked)
+
+
+------------------------------------------------------------------------
+r5776 | jesse | 2006-08-16 02:14:14 -0400 (Wed, 16 Aug 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23cfsort.t
+
+ r20440@pinglin: jesse | 2006-08-16 02:13:52 -0400
+ * Todd Chapman spotted that a missing plan was masking test failures.
+
+
+------------------------------------------------------------------------
+r5771 | jesse | 2006-08-15 00:01:54 -0400 (Tue, 15 Aug 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/Chart
+
+ r20425@pinglin: jesse | 2006-08-15 00:01:34 -0400
+ * We now sort chart results before displaying them. Reported by Petter Reinholdtsen
+
+------------------------------------------------------------------------
+r5752 | jesse | 2006-08-11 20:31:03 -0400 (Fri, 11 Aug 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/MyRequests
+
+ r20345@pinglin: jesse | 2006-08-11 20:30:54 -0400
+ * Small HTML column balancing bug (Missing </td> found by Erik Peterson)
+
+------------------------------------------------------------------------
+r5747 | ruz | 2006-08-09 16:57:41 -0400 (Wed, 09 Aug 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/CurrentUser.pm
+
+* skip test if french locale is not loaded
+------------------------------------------------------------------------
+r5746 | ruz | 2006-08-09 16:32:00 -0400 (Wed, 09 Aug 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/bin/rt-mailgate.in
+ M /rt/branches/3.6-RELEASE/lib/t/regression/06mailgateway.t
+
+* mailgate without --action argument were failing
+* tests
+------------------------------------------------------------------------
+r5745 | ruz | 2006-08-09 15:37:28 -0400 (Wed, 09 Aug 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+* CullRTAddresses in RT::Interface::Email was using reversed filter logic
+------------------------------------------------------------------------
+r5744 | ruz | 2006-08-09 15:34:29 -0400 (Wed, 09 Aug 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+
+* report error if action is empty in mailgate
+------------------------------------------------------------------------
+r5740 | ruz | 2006-08-09 02:48:35 -0400 (Wed, 09 Aug 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+
+* drop uninit wornings
+* "... eq undef" is bad code
+------------------------------------------------------------------------
+r5728 | kevinr | 2006-08-08 21:00:32 -0400 (Tue, 08 Aug 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Tabs
+
+ r17938@sad-girl-in-snow: kevinr | 2006-08-08 20:59:23 -0400
+ * Fixed a logic bug in how we were positioning the separator.
+
+------------------------------------------------------------------------
+r5721 | kevinr | 2006-08-08 14:16:31 -0400 (Tue, 08 Aug 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/autohandler
+
+ r17906@sad-girl-in-snow: kevinr | 2006-08-08 14:15:30 -0400
+ * Added FailedLogin and SuccessfulLogin callbacks to the autohandler
+
+------------------------------------------------------------------------
+r5719 | jesse | 2006-08-08 12:21:37 -0400 (Tue, 08 Aug 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r15134@pinglin: jesse | 2006-08-08 12:21:13 -0400
+ * RT 3.6.1
+
+------------------------------------------------------------------------
+r5718 | jesse | 2006-08-08 12:18:57 -0400 (Tue, 08 Aug 2006) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/es.po
+
+ r15132@pinglin: jesse | 2006-08-08 12:17:58 -0400
+ RT-Ticket: 7757
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Thanks! applied
+
+
+------------------------------------------------------------------------
+r5717 | ruz | 2006-08-07 23:57:37 -0400 (Mon, 07 Aug 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/06mailgateway.t
+
+* cleanup test file, less warnings
+------------------------------------------------------------------------
+r5716 | ruz | 2006-08-07 23:56:36 -0400 (Mon, 07 Aug 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23-batch-upload-csv.t
+
+* DB may return records in random order(Pg for example),
+ use OrderBy when test depends on order of a collection.
+------------------------------------------------------------------------
+r5714 | ruz | 2006-08-07 23:08:21 -0400 (Mon, 07 Aug 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+* use $principal object we created before instead of args{PrincipalId}
+ as user may provide email address
+* use == instead of eq for id comparisions
+------------------------------------------------------------------------
+r5713 | ruz | 2006-08-07 23:02:52 -0400 (Mon, 07 Aug 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+* get rid of uninit warnings
+* id is an integer so use == instead of eq to compare
+------------------------------------------------------------------------
+r5712 | ruz | 2006-08-07 22:52:43 -0400 (Mon, 07 Aug 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/AutoOpen.pm
+
+* get rid of uninit warnings
+* refactor
+* don't call $self->TransactionObj->Message->First twice
+------------------------------------------------------------------------
+r5711 | ruz | 2006-08-07 22:41:22 -0400 (Mon, 07 Aug 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/NoAuth/mail-gateway
+
+* get rid of unint warnings
+------------------------------------------------------------------------
+r5658 | ruz | 2006-07-26 13:14:42 -0400 (Wed, 26 Jul 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+
+* docs update
+------------------------------------------------------------------------
+r5657 | ruz | 2006-07-26 11:28:30 -0400 (Wed, 26 Jul 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/SelfService/Display.html
+
+* drop uninit warning
+------------------------------------------------------------------------
+r5656 | ruz | 2006-07-26 11:03:38 -0400 (Wed, 26 Jul 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+
+* http://rt3.fsck.com/Ticket/Display.html?id=7133
+ * @actions -> @$actions to handle parser errors
+ * don't blame users on CF.{} conditions
+ Thanks to Todd and Rolf Grossmann.
+------------------------------------------------------------------------
+r5653 | kevinr | 2006-07-25 22:05:36 -0400 (Tue, 25 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+
+ r15411@sad-girl-in-snow: kevinr | 2006-07-25 20:04:26 -0400
+ * Added a BeforeProcessArguments callback
+
+------------------------------------------------------------------------
+r5652 | ruz | 2006-07-25 20:21:32 -0400 (Tue, 25 Jul 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/REST.pm
+
+* add support for spaces in names of custom fields in the REST,
+ based on patch from Dmitri Tikhonov.
+------------------------------------------------------------------------
+r5651 | ruz | 2006-07-25 19:44:13 -0400 (Tue, 25 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+* If a ticket is created with _RecordTransaction set to
+ zero, it's not an error when zero is returned as the
+ transaction id. Thanks, Todd.
+------------------------------------------------------------------------
+r5650 | jesse | 2006-07-25 14:06:32 -0400 (Tue, 25 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r14405@pinglin: jesse | 2006-07-25 11:05:54 -0700
+ * RC 2
+
+------------------------------------------------------------------------
+r5649 | jesse | 2006-07-25 14:06:06 -0400 (Tue, 25 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Simple.html
+
+ r14404@pinglin: jesse | 2006-07-25 11:05:41 -0700
+ * Restore "Go to ticket #" search functionality accidentally removed from RT 3.6.0
+
+------------------------------------------------------------------------
+r5642 | jesse | 2006-07-21 14:18:04 -0400 (Fri, 21 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets.pm
+
+ r14319@pinglin: jesse | 2006-07-21 11:17:54 -0700
+ * more updates from jason
+
+------------------------------------------------------------------------
+r5641 | jesse | 2006-07-21 12:16:07 -0400 (Fri, 21 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets.pm
+
+ r14309@pinglin: jesse | 2006-07-21 09:15:58 -0700
+ * Loc bug reported by doogles
+
+------------------------------------------------------------------------
+r5629 | kevinr | 2006-07-20 15:42:05 -0400 (Thu, 20 Jul 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+ r15197@sad-girl-in-snow: kevinr | 2006-07-20 15:36:45 -0400
+ * Sending e-mail falls back to the username if a RealName can't be found. We
+ shouldn't be sending messages with "From: via RT" any more.
+
+------------------------------------------------------------------------
+r5628 | jesse | 2006-07-20 15:16:09 -0400 (Thu, 20 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditWatchers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowUserEntry
+
+ r14235@pinglin: jesse | 2006-07-20 12:15:45 -0700
+ * Better display of squelched ticket recipients
+
+------------------------------------------------------------------------
+r5627 | jesse | 2006-07-20 15:16:01 -0400 (Thu, 20 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets.pm
+
+ r14234@pinglin: jesse | 2006-07-20 12:15:31 -0700
+ * explicitly use the libraries we're using (Rather than depend on them to be used elsewhere)
+
+------------------------------------------------------------------------
+r5626 | jesse | 2006-07-20 13:52:03 -0400 (Thu, 20 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowHistory
+
+ r14231@pinglin: jesse | 2006-07-20 10:49:41 -0700
+ Forward porting a history ordering fix from 3.4
+
+------------------------------------------------------------------------
+r5625 | jesse | 2006-07-20 13:51:37 -0400 (Thu, 20 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFields
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomFields
+
+ r14228@pinglin: jesse | 2006-07-20 10:38:00 -0700
+ * Better semantic html for custom field labels from Todd Chapman
+
+------------------------------------------------------------------------
+r5623 | jesse | 2006-07-20 13:32:59 -0400 (Thu, 20 Jul 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/autohandler
+
+ r14225@pinglin: jesse | 2006-07-20 10:32:41 -0700
+ * Removed not-quite-right outdated cachinng header generation
+
+
+------------------------------------------------------------------------
+r5618 | jesse | 2006-07-19 20:45:36 -0400 (Wed, 19 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectGroupBy
+ M /rt/branches/3.6-RELEASE/html/Search/Results.html
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets.pm
+
+ r14219@pinglin: jesse | 2006-07-19 17:45:03 -0700
+ * Backport the ability to chart by custom field from RT 3.7
+
+------------------------------------------------------------------------
+r5617 | jesse | 2006-07-19 20:45:26 -0400 (Wed, 19 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Group_Overlay.pm
+
+ r14216@pinglin: jesse | 2006-07-19 17:38:40 -0700
+ * crit was being called on the wrong object. Thanks to Todd Chapman
+
+------------------------------------------------------------------------
+r5615 | jesse | 2006-07-19 18:46:28 -0400 (Wed, 19 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+ r14214@pinglin: jesse | 2006-07-19 15:46:15 -0700
+ * bumping to pre2
+
+------------------------------------------------------------------------
+r5614 | jesse | 2006-07-19 18:37:43 -0400 (Wed, 19 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransactionAttachments
+
+ r14208@pinglin: jesse | 2006-07-19 15:33:26 -0700
+ * Allow sites to disable the display of inline ticket images.
+
+------------------------------------------------------------------------
+r5613 | jesse | 2006-07-19 18:37:32 -0400 (Wed, 19 Jul 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowPeople
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowUserEntry
+
+ r14207@pinglin: jesse | 2006-07-19 15:33:02 -0700
+ * Inline display of which ticket watchers are "squelched"
+
+
+------------------------------------------------------------------------
+r5609 | jesse | 2006-07-19 15:19:27 -0400 (Wed, 19 Jul 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+
+------------------------------------------------------------------------
+r5608 | jesse | 2006-07-19 15:18:52 -0400 (Wed, 19 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r14179@pinglin: jesse | 2006-07-18 21:27:41 -0700
+ * better help on dependency install
+
+------------------------------------------------------------------------
+r5607 | jesse | 2006-07-19 15:18:09 -0400 (Wed, 19 Jul 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+
+------------------------------------------------------------------------
+r5600 | jesse | 2006-07-18 17:51:46 -0400 (Tue, 18 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/23-web_attachments.t
+
+ r37204@truegrounds: jesse | 2006-07-18 16:21:51 -0400
+ * Tests updated for 3.6
+
+------------------------------------------------------------------------
+r5599 | jesse | 2006-07-18 17:51:35 -0400 (Tue, 18 Jul 2006) | 166 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/bin/rt-crontool.in
+ M /rt/branches/3.6-RELEASE/config.layout
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/index.html
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/3.6-RELEASE/html/SelfService/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowHistory
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Date.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Link_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Links_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/base.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI.pm
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+ M /rt/branches/3.6-RELEASE/lib/t/regression/06mailgateway.t
+ M /rt/branches/3.6-RELEASE/lib/t/regression/22search_tix_by_txn.t
+ A /rt/branches/3.6-RELEASE/lib/t/regression/23-web_attachments.t
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+ r37203@truegrounds: jesse | 2006-07-18 13:08:57 -0400
+ r30313@truegrounds: jesse | 2006-03-23 01:36:27 -0500
+ * Better mp2 bulletproofing
+ r30314@truegrounds: jesse | 2006-03-23 01:36:42 -0500
+
+ r37202@truegrounds: jesse | 2006-07-18 12:49:01 -0400
+ r31084@truegrounds (orig r4814): jesse | 2006-03-23 22:40:37 -0500
+ r10436@hualien: jesse | 2006-03-23 22:40:25 -0500
+ * It helps when there aren't typos
+
+ r31117@truegrounds (orig r4847): alexmv | 2006-03-28 15:50:07 -0500
+ r11918@zoq-fot-pik: chmrr | 2006-03-28 15:49:56 -0500
+ * Backport TXN fixes from 3.7 and 3.5
+
+ r31194@truegrounds (orig r4924): jesse | 2006-03-30 21:07:23 -0500
+ r10636@hualien: jesse | 2006-03-31 11:06:57 +0900
+ RT-Ticket: 7398
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Added a "RH" RedHat layout option to config.layout -- Paulo Matos
+
+ r31317@truegrounds (orig r5047): ruz | 2006-04-17 20:40:06 -0400
+ * check and report error to the logs
+ r31330@truegrounds (orig r5060): jesse | 2006-04-24 10:49:40 -0400
+ r11842@hualien: jesse | 2006-04-24 10:49:13 -0400
+ The following patch adds the useful LastUpdated field to the fields
+ returned through the REST interface.
+ David - who starts to wonder if his patches are actually read by someone :-)
+ --
+ David Schweikert | phone: +41 44 632 7019
+ System manager ISG.EE | walk: ETH Zentrum, ETL F24.1
+ ETH Zurich, Switzerland | web: http://people.ee.ethz.ch/dws
+
+
+ r31334@truegrounds (orig r5064): ruz | 2006-04-24 16:42:43 -0400
+ * max subject is 200 character long
+ r31335@truegrounds (orig r5065): ruz | 2006-04-24 17:25:15 -0400
+ * convert only if $enc'oding contains something
+ r31336@truegrounds (orig r5066): ruz | 2006-04-24 20:21:32 -0400
+ * simple tests for Attachments manipulation from web interface
+ r31385@truegrounds (orig r5115): ruz | 2006-04-25 19:24:45 -0400
+ * (cond) && 'selected' outputs 0 if condition fails on my system
+ r31386@truegrounds (orig r5116): ruz | 2006-04-25 19:35:09 -0400
+ * get queue ID from page
+ r31388@truegrounds (orig r5118): jesse | 2006-04-25 22:43:31 -0400
+ r11882@hualien: jesse | 2006-04-25 22:43:11 -0400
+ * Mark Eichin picked up that http://lists.fsck.com/pipermail/rt-devel/2004-August/006216.html had never been applied.
+
+ rt ls -l broke because of it, if your RT server wasn't at /
+
+ r31453@truegrounds (orig r5183): jesse | 2006-05-08 22:31:56 -0400
+ r13313@hualien: jesse | 2006-05-08 12:01:55 -0400
+ * Finding disabled groups should actually find them, now
+
+ r31454@truegrounds (orig r5184): jesse | 2006-05-08 22:32:10 -0400
+ r13314@hualien: jesse | 2006-05-08 12:14:26 -0400
+ * Minor reformatting
+
+ r31455@truegrounds (orig r5185): jesse | 2006-05-08 22:32:19 -0400
+ r13315@hualien: jesse | 2006-05-08 22:31:30 -0400
+ * Mail gateway refactoring to make added functioanlity a bit easier.
+ No (intentional) functional changes.
+
+ r31456@truegrounds (orig r5186): jesse | 2006-05-08 22:56:20 -0400
+ r13330@hualien: jesse | 2006-05-08 22:55:56 -0400
+ * Reed Loden caught a perltidy error that, somewhat terrifiyingly, was still a valid mason page
+
+ r31457@truegrounds (orig r5187): jesse | 2006-05-09 00:48:10 -0400
+ r13332@hualien: jesse | 2006-05-09 00:47:49 -0400
+ * Mismatched parens
+
+ r31476@truegrounds (orig r5206): ruz | 2006-05-11 16:48:53 -0400
+ * return values checking and more logging on errors
+ r31477@truegrounds (orig r5207): ruz | 2006-05-11 16:56:24 -0400
+ * more checks on attachments processing
+ r31478@truegrounds (orig r5208): ruz | 2006-05-11 18:24:17 -0400
+ * if ( not $xxx || $xxx->foo ) is equivalent to
+ if ( not ( $xxx || $xxx->foo ) ) due to perl5 rules
+ which is not expected behaviour
+ r31479@truegrounds (orig r5209): ruz | 2006-05-11 18:31:58 -0400
+ * user do next steps:
+ 1) open ticket #1
+ 2) click reply
+ 3) upload attachment
+ 4) open ticket #2 in another browser window
+ 5) send reply to the ticket #1
+ RT looses uploaded attachment due to step 4) as RT tries
+ to add attchement to the ticket #2 and drops them from session.
+ As solution don't ProcessTicketMessage if there is attachments,
+ but only if there is real update message.
+ r31486@truegrounds (orig r5216): ruz | 2006-05-12 16:54:41 -0400
+ * add Timezone argument in SetToMidnight
+ r31487@truegrounds (orig r5217): ruz | 2006-05-12 17:02:54 -0400
+ * use SetToMidnight( Timezone => 'server' ) to calc start and end of the day
+ r31488@truegrounds (orig r5218): ruz | 2006-05-12 20:31:33 -0400
+ * get rid of "masks earlier declaration" warnings
+ r31508@truegrounds (orig r5238): ruz | 2006-05-16 18:39:59 -0400
+ * really noisy warning
+ *NOTE* that option we use is not described in config
+ r31519@truegrounds (orig r5249): ruz | 2006-05-18 12:17:47 -0400
+ * add bug comment
+ r31526@truegrounds (orig r5256): ruz | 2006-05-18 21:45:58 -0400
+ * allow to complete actions in mail plugins
+ r31527@truegrounds (orig r5257): ruz | 2006-05-18 21:53:40 -0400
+ * minor
+ r31538@truegrounds (orig r5268): jesse | 2006-05-19 17:17:41 -0400
+ r13935@hualien: jesse | 2006-05-19 17:17:27 -0400
+ * There were divergent copies of this code. The EmailParser code was more correct
+
+ r31580@truegrounds (orig r5310): ruz | 2006-05-26 20:39:49 -0400
+ * when we could parse URI, for example object doesn't exist
+ fallback to RT::URI::base resolver, so $uri->IsLocal and
+ other methods wouldn't die but return undef
+ r31585@truegrounds (orig r5315): ruz | 2006-05-28 07:19:20 -0400
+ * Use "Requestor.id = $requestor->id" search instead of search by email address
+ as latter is not indexed
+ r31587@truegrounds (orig r5317): ruz | 2006-05-30 16:13:02 -0400
+ * If current user changes owner from somebody else to nobody user,
+ the action fails with "You can only reassign tickets that you own
+ or that are unowned", but we must change owner if he has no right
+ to own tickets in dest queue. Do it with Force and with SystemUser
+ context.
+ r31670@truegrounds (orig r5400): ruz | 2006-06-16 20:40:24 -0400
+ * not default mail plugins has been broken during last refactoring
+ ** move a code back into its scope
+ ** don't forget to store $_ in $Class when $_ matches ^RT::Interface::Email
+ r35569@truegrounds (orig r5476): ruz | 2006-06-27 17:21:07 -0400
+ * nothing special, small changes I'd changed during
+ the hunt over a bug
+ r35573@truegrounds (orig r5480): ruz | 2006-06-27 20:05:49 -0400
+ * minor formatting
+ r35575@truegrounds (orig r5482): ruz | 2006-06-28 17:25:18 -0400
+ * add tests for unsafe mailgate commands
+ * fix bugs that were introduced during Email.pm refactoring
+ r35589@truegrounds (orig r5496): ruz | 2006-06-30 16:09:08 -0400
+ Changes:
+ * new config option $OldestTransactionsFirst that allow
+ administrator to reverse order of transactions on
+ history page
+
+ r36191@truegrounds (orig r5520): ruz | 2006-07-04 01:36:46 -0400
+ * forgot to add option to config
+ r36192@truegrounds (orig r5521): ruz | 2006-07-04 01:38:03 -0400
+ * report error when couldn't create CF
+ r36193@truegrounds (orig r5522): ruz | 2006-07-04 01:39:21 -0400
+ * we never should call exit from libs
+ r36205@truegrounds (orig r5534): ruz | 2006-07-06 11:19:46 -0400
+ rt-crontool
+ * add --transaction argument with two possible values: 'first' and 'last'
+ * add --transaction-type argument to allow users select type of transactions
+ ** these transactions would be passed to scrips for processing, so users
+ can use conditions, actions and templates that check or use properties of
+ transaction
+
+ * also some existant actions, conditions and templates require scrip or
+ scrip action objects to process normally, as we have no these objects
+ available we now pass void (not loaded) objects. This change would allow
+ users to use notify actions with crontool.
+ r36241@truegrounds (orig r5570): kevinr | 2006-07-13 16:21:31 -0400
+ r14836@sad-girl-in-snow: kevinr | 2006-07-13 16:17:43 -0400
+ * The RT::Condition::Generic docs were wrong... fixed.
+
+
+
+
+------------------------------------------------------------------------
+r5573 | jesse | 2006-07-17 12:41:35 -0400 (Mon, 17 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+ M /rt/branches/3.6-RELEASE/html/index.html
+
+ r14054@pinglin: jesse | 2006-07-17 11:28:21 -0400
+ * Switch our redirects to use the new RT::Interface::Web::Redirect method
+
+------------------------------------------------------------------------
+r5572 | jesse | 2006-07-17 12:41:09 -0400 (Mon, 17 Jul 2006) | 13 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+
+ r14053@pinglin: jesse | 2006-07-17 11:28:00 -0400
+ * Added "Redirect" and "StaticFileHeaders" methods to RT::Interface::Web.
+
+ * Redirect is careful to redirect the browser to the same base RT url they're coming from
+ and to close the user's Apache::Session, lest that module try to open two copies
+ of the same prepared session database handle at the same time and fall over.
+
+ * StaticFileHeaders tells the user's browser that the file being served
+ was last modified at last server start and should be cached for approximately
+ a month. Better would be to use the Heuristics that Jifty::View::Static provides.
+
+
+
+------------------------------------------------------------------------
+r5571 | jesse | 2006-07-17 12:40:48 -0400 (Mon, 17 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ D /rt/branches/3.6-RELEASE/html/NoAuth/ahah.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/autohandler
+ M /rt/branches/3.6-RELEASE/html/NoAuth/images/autohandler
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/autohandler
+
+ r14052@pinglin: jesse | 2006-07-17 11:23:30 -0400
+ * Started generating better caching headers for css, image and js files
+
+------------------------------------------------------------------------
+r5528 | trs | 2006-07-04 17:59:19 -0400 (Tue, 04 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/autohandler
+
+ r13934@zot: tom | 2006-07-04 17:59:07 -0400
+ Add an HTTP "Expires" header to CSS files so they expire in a year (and aren't reloaded on every request)
+
+------------------------------------------------------------------------
+r5524 | robert | 2006-07-04 02:37:00 -0400 (Tue, 04 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/NoAuth/mail-gateway
+
+ r5611@bear: rspier | 2006-07-03 23:36:20 -0700
+ Add Pre hook to mail-gateway
+
+------------------------------------------------------------------------
+r5523 | robert | 2006-07-04 02:36:40 -0400 (Tue, 04 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/Row
+ M /rt/branches/3.6-RELEASE/html/Elements/QueryString
+ M /rt/branches/3.6-RELEASE/html/Ticket/Update.html
+
+ r5607@bear: rspier | 2006-07-03 20:30:15 -0700
+ Hide a handful of warnings in a quick and dirty way
+
+------------------------------------------------------------------------
+r5519 | robert | 2006-07-03 21:31:03 -0400 (Mon, 03 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/etc/RT_Config.pm.in
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+ A /rt/branches/3.6-RELEASE/lib/t/regression/27verp.t
+
+ r5600@bear: rspier | 2006-07-03 18:27:29 -0700
+ Initial support for configurable VERP
+
+------------------------------------------------------------------------
+r5518 | robert | 2006-07-03 21:30:50 -0400 (Mon, 03 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+
+ r5599@bear: rspier | 2006-07-03 18:03:10 -0700
+ Add a Date: header to outbound email messages
+
+------------------------------------------------------------------------
+r5517 | robert | 2006-07-03 21:30:36 -0400 (Mon, 03 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/User/Prefs.html
+
+ r5598@bear: rspier | 2006-07-03 17:45:55 -0700
+ Add id's to the TitleBox wrappers so we can easily make them invisible from the stylesheet
+
+------------------------------------------------------------------------
+r5497 | robert | 2006-07-03 13:17:51 -0400 (Mon, 03 Jul 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/configure.ac
+
+ r5577@bear: rspier | 2006-07-03 10:17:32 -0700
+ Update version string
+
+------------------------------------------------------------------------
+r5489 | kevinr | 2006-06-29 14:41:08 -0400 (Thu, 29 Jun 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/lib/t/regression/26command_line.t
+
+ r14127@sad-girl-in-snow: kevinr | 2006-06-29 14:40:28 -0400
+ * Added todo_skip tests for linking via the rt command-line tool.
+
+------------------------------------------------------------------------
+r5485 | ruz | 2006-06-28 18:15:28 -0400 (Wed, 28 Jun 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/sbin/rt-dump-database.in
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+
+* every RT script that uses the API must use local libs
+------------------------------------------------------------------------
+r5481 | ruz | 2006-06-28 13:55:11 -0400 (Wed, 28 Jun 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/REST.pm
+
+Based on patch from Philip Kime.
+* sort bt numbers first then by letters in expand_list
+* get rid of warnings
+
+------------------------------------------------------------------------
+r5418 | ruz | 2006-06-23 17:22:53 -0400 (Fri, 23 Jun 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/Googleish.pm
+
+* typo, package is Googleish, we load local/vendor Googlish
+------------------------------------------------------------------------
+r5408 | jesse | 2006-06-19 18:45:12 -0400 (Mon, 19 Jun 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/html/Elements/Footer
+
+ r12787@pinglin: jesse | 2006-06-19 18:43:43 -0400
+ * Set the visible copyright string to 2006
+
+------------------------------------------------------------------------
+r5407 | jesse | 2006-06-19 18:44:04 -0400 (Mon, 19 Jun 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/Makefile.in
+ M /rt/branches/3.6-RELEASE/README
+ M /rt/branches/3.6-RELEASE/bin/mason_handler.fcgi.in
+ M /rt/branches/3.6-RELEASE/bin/mason_handler.scgi.in
+ M /rt/branches/3.6-RELEASE/bin/mason_handler.svc.in
+ M /rt/branches/3.6-RELEASE/bin/rt-crontool.in
+ M /rt/branches/3.6-RELEASE/bin/rt-mailgate.in
+ M /rt/branches/3.6-RELEASE/bin/rt.in
+ M /rt/branches/3.6-RELEASE/bin/standalone_httpd.in
+ M /rt/branches/3.6-RELEASE/bin/webmux.pl.in
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/Objects.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/CustomFields/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/CustomFieldTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditCustomField
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditScrip
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditScrips
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditTemplates
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/EditUserComments
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/GlobalCustomFieldTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/GroupTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/Header
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ModifyTemplate
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ObjectCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/PickCustomFields
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/PickObjects
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/QueueTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectCustomFieldLookupType
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectGroups
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectModifyQueue
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectModifyUser
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectRights
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScrip
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScripAction
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectSingleOrMultiple
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectStage
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectTemplate
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SelectUsers
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/SystemTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/ToolTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Elements/UserTabs
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Groups.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Queue-Tickets.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Queue-Transactions.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/Users.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/CustomFields/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Scrip.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Scrips.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Template.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/Templates.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Global/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/CustomFields.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/History.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/Members.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Groups/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/CustomField.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/CustomFields.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/GroupRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/People.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Scrip.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Scrips.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Template.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/Templates.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/UserRights.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Queues/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Tools/Configuration.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Tools/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/CustomFields.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/History.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/Memberships.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Admin/Users/index.html
+ M /rt/branches/3.6-RELEASE/html/Admin/autohandler
+ M /rt/branches/3.6-RELEASE/html/Admin/index.html
+ M /rt/branches/3.6-RELEASE/html/Approvals/Display.html
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/Approve
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/ShowDependency
+ M /rt/branches/3.6-RELEASE/html/Approvals/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Approvals/index.html
+ M /rt/branches/3.6-RELEASE/html/Download/CustomFieldValue/dhandler
+ M /rt/branches/3.6-RELEASE/html/Download/Tabular/dhandler
+ M /rt/branches/3.6-RELEASE/html/Elements/BevelBoxRaisedEnd
+ M /rt/branches/3.6-RELEASE/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/3.6-RELEASE/html/Elements/Callback
+ M /rt/branches/3.6-RELEASE/html/Elements/Checkbox
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/Header
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/3.6-RELEASE/html/Elements/CollectionAsTable/Row
+ M /rt/branches/3.6-RELEASE/html/Elements/CreateTicket
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomField
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldBinary
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldCombobox
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldImage
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldText
+ M /rt/branches/3.6-RELEASE/html/Elements/EditCustomFieldWikitext
+ M /rt/branches/3.6-RELEASE/html/Elements/EditLinks
+ M /rt/branches/3.6-RELEASE/html/Elements/Error
+ M /rt/branches/3.6-RELEASE/html/Elements/Footer
+ M /rt/branches/3.6-RELEASE/html/Elements/GotoTicket
+ M /rt/branches/3.6-RELEASE/html/Elements/Header
+ M /rt/branches/3.6-RELEASE/html/Elements/ListActions
+ M /rt/branches/3.6-RELEASE/html/Elements/Login
+ M /rt/branches/3.6-RELEASE/html/Elements/Logo
+ M /rt/branches/3.6-RELEASE/html/Elements/Menu
+ M /rt/branches/3.6-RELEASE/html/Elements/MessageBox
+ M /rt/branches/3.6-RELEASE/html/Elements/MyAdminQueues
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRT
+ M /rt/branches/3.6-RELEASE/html/Elements/MyReminders
+ M /rt/branches/3.6-RELEASE/html/Elements/MyRequests
+ M /rt/branches/3.6-RELEASE/html/Elements/MySupportQueues
+ M /rt/branches/3.6-RELEASE/html/Elements/MyTickets
+ M /rt/branches/3.6-RELEASE/html/Elements/PageLayout
+ M /rt/branches/3.6-RELEASE/html/Elements/QueryString
+ M /rt/branches/3.6-RELEASE/html/Elements/QueueSummary
+ M /rt/branches/3.6-RELEASE/html/Elements/QuickCreate
+ M /rt/branches/3.6-RELEASE/html/Elements/Quicksearch
+ M /rt/branches/3.6-RELEASE/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/3.6-RELEASE/html/Elements/Refresh
+ M /rt/branches/3.6-RELEASE/html/Elements/RefreshHomepage
+ M /rt/branches/3.6-RELEASE/html/Elements/ScrubHTML
+ M /rt/branches/3.6-RELEASE/html/Elements/Section
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectAttachmentField
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectBoolean
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectCustomFieldValue
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectDate
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectDateRelation
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectDateType
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectEqualityOperator
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectGroups
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectLang
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectLinkType
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectMatch
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectNewTicketQueue
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectOwner
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectQueue
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectResultsPerPage
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectSortOrder
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectStatus
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectTicketSortBy
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectTicketTypes
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectTimeUnits
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectUsers
+ M /rt/branches/3.6-RELEASE/html/Elements/SelectWatcherType
+ M /rt/branches/3.6-RELEASE/html/Elements/SetupSessionCookie
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldBinary
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldImage
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowCustomFields
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowLink
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowLinks
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowMemberships
+ M /rt/branches/3.6-RELEASE/html/Elements/ShowSearch
+ M /rt/branches/3.6-RELEASE/html/Elements/SimpleSearch
+ M /rt/branches/3.6-RELEASE/html/Elements/Submit
+ M /rt/branches/3.6-RELEASE/html/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Elements/TicketList
+ M /rt/branches/3.6-RELEASE/html/Elements/TitleBox
+ M /rt/branches/3.6-RELEASE/html/Elements/TitleBoxEnd
+ M /rt/branches/3.6-RELEASE/html/Elements/TitleBoxStart
+ M /rt/branches/3.6-RELEASE/html/Elements/ValidateCustomFields
+ M /rt/branches/3.6-RELEASE/html/Helpers/CalPopup.html
+ M /rt/branches/3.6-RELEASE/html/NoAuth/Logout.html
+ M /rt/branches/3.6-RELEASE/html/NoAuth/Reminder.html
+ M /rt/branches/3.6-RELEASE/html/NoAuth/ahah.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/body.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/footer.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/forms.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/header.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/login.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/main.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/misc.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/nav.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/quickbar.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/ticket.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/titlebox.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.4-compat/transactions.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/approvals.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/body.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/footer.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/forms.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/header.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/login.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/logo.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/main.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/misc.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/nav.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/quickbar.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/ticket.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/titlebox.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/3.5-default/transactions.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/autohandler
+ M /rt/branches/3.6-RELEASE/html/NoAuth/css/print.css
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/ahah.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/autohandler
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/cascaded.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/class.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/combobox.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/list.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/titlebox-state.js
+ M /rt/branches/3.6-RELEASE/html/NoAuth/js/util.js
+ M /rt/branches/3.6-RELEASE/html/Prefs/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Prefs/MyRT.html
+ M /rt/branches/3.6-RELEASE/html/Prefs/Quicksearch.html
+ M /rt/branches/3.6-RELEASE/html/Prefs/Search.html
+ M /rt/branches/3.6-RELEASE/html/Prefs/SearchOptions.html
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/queue/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/queue/ns
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/comment
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/links
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/merge
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/ticket/take
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/user/default
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/Forms/user/ns
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/autohandler
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/dhandler
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/logout
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/search/dhandler
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/search/ticket
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/comment
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/link
+ M /rt/branches/3.6-RELEASE/html/REST/1.0/ticket/merge
+ M /rt/branches/3.6-RELEASE/html/Search/Build.html
+ M /rt/branches/3.6-RELEASE/html/Search/Bulk.html
+ M /rt/branches/3.6-RELEASE/html/Search/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Chart.html
+ M /rt/branches/3.6-RELEASE/html/Search/Edit.html
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/BuildFormatString
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/Chart
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/DisplayOptions
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditFormat
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditQuery
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/EditSearches
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/NewListActions
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/PickBasics
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/PickCFs
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/PickCriteria
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SearchPrivacy
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SearchesForObject
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectAndOr
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectChartType
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectGroup
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectGroupBy
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectLinks
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectPersonType
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectSearchObject
+ M /rt/branches/3.6-RELEASE/html/Search/Elements/SelectSearchesForObjects
+ M /rt/branches/3.6-RELEASE/html/Search/Results.html
+ M /rt/branches/3.6-RELEASE/html/Search/Results.rdf
+ M /rt/branches/3.6-RELEASE/html/Search/Results.tsv
+ M /rt/branches/3.6-RELEASE/html/Search/Simple.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Attachment/dhandler
+ M /rt/branches/3.6-RELEASE/html/SelfService/Closed.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Create.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/CreateTicketInQueue.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Display.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/GotoTicket
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/Header
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/MyRequests
+ M /rt/branches/3.6-RELEASE/html/SelfService/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/SelfService/Error.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Prefs.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/Update.html
+ M /rt/branches/3.6-RELEASE/html/SelfService/index.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Attachment/dhandler
+ M /rt/branches/3.6-RELEASE/html/Ticket/Create.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Display.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/AddWatchers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/BulkLinks
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditBasics
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomField
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditDates
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditPeople
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/EditWatchers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/FindAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Reminders
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowBasics
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowCustomFields
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowDates
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowDependencies
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowHistory
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMembers
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMessageHeaders
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowMessageStanza
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowPeople
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTime
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/ShowUserEntry
+ M /rt/branches/3.6-RELEASE/html/Ticket/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Ticket/History.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Modify.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyAll.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyDates.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyLinks.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ModifyPeople.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Reminders.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/3.6-RELEASE/html/Ticket/Update.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Tools/MyDay.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Offline.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/CreatedByDates.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/ResolvedByDates.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/ResolvedByOwner.html
+ M /rt/branches/3.6-RELEASE/html/Tools/Reports/index.html
+ M /rt/branches/3.6-RELEASE/html/Tools/index.html
+ M /rt/branches/3.6-RELEASE/html/User/Delegation.html
+ M /rt/branches/3.6-RELEASE/html/User/Elements/DelegateRights
+ M /rt/branches/3.6-RELEASE/html/User/Elements/GroupTabs
+ M /rt/branches/3.6-RELEASE/html/User/Elements/Tabs
+ M /rt/branches/3.6-RELEASE/html/User/Groups/Members.html
+ M /rt/branches/3.6-RELEASE/html/User/Groups/Modify.html
+ M /rt/branches/3.6-RELEASE/html/User/Groups/index.html
+ M /rt/branches/3.6-RELEASE/html/User/Prefs.html
+ M /rt/branches/3.6-RELEASE/html/Widgets/ComboBox
+ M /rt/branches/3.6-RELEASE/html/Widgets/SelectionBox
+ M /rt/branches/3.6-RELEASE/html/Widgets/TitleBox
+ M /rt/branches/3.6-RELEASE/html/Widgets/TitleBoxEnd
+ M /rt/branches/3.6-RELEASE/html/Widgets/TitleBoxStart
+ M /rt/branches/3.6-RELEASE/html/autohandler
+ M /rt/branches/3.6-RELEASE/html/index.html
+ M /rt/branches/3.6-RELEASE/html/l
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ACE_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ACL.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ACL_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/AutoOpen.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Autoreply.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/EscalatePriority.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/Notify.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/NotifyAsComment.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/RecordComment.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/RecordCorrespondence.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/ResolveMembers.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/SetPriority.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Action/UserDefined.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachment.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachments.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attribute.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attributes.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Base.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMember.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMembers.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/AnyTransaction.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/BeforeDue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/Overdue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/OwnerChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/PriorityChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/PriorityExceeds.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/QueueChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/StatusChange.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Condition/UserDefined.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CurrentUser.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValues.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFieldValues_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFields.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Date.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/EmailParser.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Group.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMember.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMembers.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Group_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Groups.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Groups_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Handle.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/cs.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N/i_default.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/I18N.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/CLI.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email/Auth/GnuPG.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Email.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/REST.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Menu/Item.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Menu.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/QueryBuilder/Tree.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/QueryBuilder.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web/Standalone.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Interface/Web.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Link.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Link_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Links.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Links_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomField.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValues.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomField_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFields.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ObjectCustomFields_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principal_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principals.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Principals_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queue_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queues.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Queues_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Record.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Reminders.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets/Entry.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Report/Tickets.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearch.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SavedSearches.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrip.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripAction.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripActions.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripCondition.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripConditions.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrips.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/ActiveTicketsInQueue.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/FromSQL.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/Generic.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Search/Googleish.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/SearchBuilder.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/System.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Template.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Template_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Templates.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Templates_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transactions.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/base.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI/t.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/URI.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/User.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/User_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Users.pm
+ M /rt/branches/3.6-RELEASE/lib/RT/Users_Overlay.pm
+ M /rt/branches/3.6-RELEASE/lib/RT.pm.in
+ M /rt/branches/3.6-RELEASE/sbin/extract-message-catalog
+ M /rt/branches/3.6-RELEASE/sbin/extract_pod_tests
+ M /rt/branches/3.6-RELEASE/sbin/factory
+ M /rt/branches/3.6-RELEASE/sbin/license_tag
+ M /rt/branches/3.6-RELEASE/sbin/regression_harness
+ M /rt/branches/3.6-RELEASE/sbin/rt-dump-database.in
+ M /rt/branches/3.6-RELEASE/sbin/rt-setup-database.in
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r12786@pinglin: jesse | 2006-06-19 18:42:15 -0400
+ * Ran the copyright tagger, updated for 2006.
+
+------------------------------------------------------------------------
+r5393 | jesse | 2006-06-16 09:01:26 -0400 (Fri, 16 Jun 2006) | 8 lines
+Changed paths:
+ M /rt/branches/3.6-RELEASE
+ M /rt/branches/3.6-RELEASE/sbin/rt-test-dependencies.in
+
+ r12704@pinglin: jesse | 2006-06-16 15:00:26 +0200
+ RT-Ticket: 7608
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * removed redundant Calendar::Simple from the dependencies
+
+
+------------------------------------------------------------------------
+r5367 | jesse | 2006-06-15 03:52:50 -0400 (Thu, 15 Jun 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.6-RELEASE/releng.cnf
+
+* Releng.cnf tweaks
+------------------------------------------------------------------------
+r5366 | jesse | 2006-06-15 03:49:42 -0400 (Thu, 15 Jun 2006) | 1 line
+Changed paths:
+ D /rt/branches/3.5-TESTING
+ A /rt/branches/3.6-RELEASE (from /rt/branches/3.5-TESTING:5361)
+
+Moving to the 3.6 release branch
+------------------------------------------------------------------------
+r5361 | jesse | 2006-06-14 18:34:59 -0400 (Wed, 14 Jun 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r12610@pinglin: jesse | 2006-06-15 00:33:56 +0200
+ * Bumping for 3.6.0 release
+
+------------------------------------------------------------------------
+r5338 | kevinr | 2006-06-05 15:41:19 -0400 (Mon, 05 Jun 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r13080@sad-girl-in-snow: kevinr | 2006-06-05 15:40:36 -0400
+ * Added docs for 'rt <take|untake|steal>'
+ * Some small doc clean-up
+
+------------------------------------------------------------------------
+r5337 | kevinr | 2006-06-05 15:41:08 -0400 (Mon, 05 Jun 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r13079@sad-girl-in-snow: kevinr | 2006-06-05 15:23:21 -0400
+ * Updated the CLI test suite so it doesn't depend on any other tests running
+ before it.
+
+------------------------------------------------------------------------
+r5316 | jesse | 2006-05-29 18:38:53 -0400 (Mon, 29 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+
+ r9889@pinglin: jesse | 2006-05-29 18:37:55 -0400
+ * The german translation had been corrupted
+
+------------------------------------------------------------------------
+r5308 | jesse | 2006-05-26 13:33:55 -0400 (Fri, 26 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r9855@jesse-vincents-computer-2: jesse | 2006-05-26 13:33:19 -0400
+ *RC3
+
+------------------------------------------------------------------------
+r5307 | ruz | 2006-05-26 12:42:33 -0400 (Fri, 26 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/default
+
+* vsplit Requestor, Cc and AdminCc field on create
+------------------------------------------------------------------------
+r5306 | ruz | 2006-05-26 12:40:22 -0400 (Fri, 26 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+* drop old bits
+------------------------------------------------------------------------
+r5304 | ruz | 2006-05-26 11:50:44 -0400 (Fri, 26 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+* add untake action
+------------------------------------------------------------------------
+r5303 | ruz | 2006-05-26 11:15:46 -0400 (Fri, 26 May 2006) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/take
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+* take/steal actions in CLI
+** pass 'take|steal|untake' as Action argument to REST
+** don't check rights in REST interface, SetOwner do that for us
+** return more descriptive message when we try to take ticket from
+ other user
+** all tests pass
+------------------------------------------------------------------------
+r5302 | jesse | 2006-05-26 00:12:03 -0400 (Fri, 26 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r9835@jesse-vincents-computer-2: jesse | 2006-05-26 00:07:45 -0400
+ * dont
+
+------------------------------------------------------------------------
+r5301 | jesse | 2006-05-26 00:11:58 -0400 (Fri, 26 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/search/ticket
+
+ r9834@jesse-vincents-computer-2: jesse | 2006-05-26 00:07:24 -0400
+ * Back out mistaken change
+
+------------------------------------------------------------------------
+r5300 | jesse | 2006-05-26 00:11:51 -0400 (Fri, 26 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/dhandler
+
+ r9833@jesse-vincents-computer-2: jesse | 2006-05-26 00:07:09 -0400
+ * Typo fix
+
+------------------------------------------------------------------------
+r5299 | jesse | 2006-05-26 00:11:37 -0400 (Fri, 26 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/take
+
+ r9832@jesse-vincents-computer-2: jesse | 2006-05-26 00:06:54 -0400
+ *Fix compilation errors on wip to help make tests not bomb out
+
+------------------------------------------------------------------------
+r5298 | jesse | 2006-05-26 00:11:28 -0400 (Fri, 26 May 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/search/ticket
+
+ r9831@jesse-vincents-computer-2: jesse | 2006-05-25 23:20:36 -0400
+ * Added custom summary "ticket lists" using the commandline (combining -f and -s) lists.
+ * Fixed a couple small bugs
+
+------------------------------------------------------------------------
+r5297 | jesse | 2006-05-26 00:11:22 -0400 (Fri, 26 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/default
+
+ r9830@jesse-vincents-computer-2: jesse | 2006-05-25 23:18:44 -0400
+ Stopped using a deprecated API
+
+------------------------------------------------------------------------
+r5296 | jesse | 2006-05-25 21:15:12 -0400 (Thu, 25 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Transaction_Overlay.pm
+
+ r9824@jesse-vincents-computer-2: jesse | 2006-05-25 21:13:11 -0400
+ * Actually do a credible job of showing html-only email messages in ticket history by downsampling them to plain text.
+
+------------------------------------------------------------------------
+r5295 | jesse | 2006-05-25 21:15:05 -0400 (Thu, 25 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/07rights.t
+
+ r9823@jesse-vincents-computer-2: jesse | 2006-05-25 21:12:39 -0400
+ * Quieting down some redefinition warnings
+
+------------------------------------------------------------------------
+r5294 | jesse | 2006-05-25 21:14:59 -0400 (Thu, 25 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/3.5-TESTING/html/REST/1.0/dhandler
+
+ r9822@jesse-vincents-computer-2: jesse | 2006-05-25 21:12:23 -0400
+ * Small fixes to ticket history display
+
+------------------------------------------------------------------------
+r5293 | ruz | 2006-05-25 18:25:21 -0400 (Thu, 25 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField_Overlay.pm
+
+* return results of the Delete method as it may fail too
+------------------------------------------------------------------------
+r5292 | ruz | 2006-05-25 18:12:05 -0400 (Thu, 25 May 2006) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/14linking.t
+
+* New option StrictLinkACL
+ # When this feature is enabled an user need ModifyTicket right on both
+ # tickets to link them together, otherwise he can have right on any of
+ # two.
+** update Create, _?AddLink, DeleteLink methods
+* fix: we created transaction if some tries to create link that allready
+ exists
+* move all acl checks out from _AddLink to AddLink method
+* cover with every change with tests
+------------------------------------------------------------------------
+r5291 | jesse | 2006-05-25 17:56:01 -0400 (Thu, 25 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField_Overlay.pm
+
+ r9817@jesse-vincents-computer-2: jesse | 2006-05-25 17:55:26 -0400
+ * Cleaned up a couple of warnings for redeclared variables
+
+------------------------------------------------------------------------
+r5290 | ruz | 2006-05-24 17:12:02 -0400 (Wed, 24 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/t/regression/13-attribute-tests.t
+
+* Order attributes as we depend on ordering in tests
+------------------------------------------------------------------------
+r5287 | jesse | 2006-05-23 17:30:33 -0400 (Tue, 23 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ D /rt/branches/3.5-TESTING/etc/upgrade/QUEBEC
+
+ r14034@hualien: jesse | 2006-05-23 17:28:31 -0400
+ * the 3.5 upgrade scripts trump the quebec scripts. we can ignore quebec
+
+------------------------------------------------------------------------
+r5286 | jesse | 2006-05-23 17:30:04 -0400 (Tue, 23 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r13785@hualien: jesse | 2006-05-14 21:34:01 -0400
+ * 3.6.0rc2
+
+------------------------------------------------------------------------
+r5285 | jesse | 2006-05-23 17:29:50 -0400 (Tue, 23 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r13781@hualien: jesse | 2006-05-14 21:08:33 -0400
+ * fixed the test count for the cli tests
+
+------------------------------------------------------------------------
+r5284 | jesse | 2006-05-23 17:29:34 -0400 (Tue, 23 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r13780@hualien: jesse | 2006-05-14 21:08:21 -0400
+ * When using the quick ticket creation widget, set the requestor to the current user
+
+------------------------------------------------------------------------
+r5283 | jesse | 2006-05-23 17:29:21 -0400 (Tue, 23 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+ r13779@hualien: jesse | 2006-05-14 21:04:13 -0400
+ By popular demand, adding a ticket link doesn't check ACLs on the second ticket
+
+------------------------------------------------------------------------
+r5282 | ruz | 2006-05-23 17:05:16 -0400 (Tue, 23 May 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+
+* add back CustomField metafield
+** this should fix empty results when sorting by CF
+------------------------------------------------------------------------
+r5232 | trs | 2006-05-15 23:10:13 -0400 (Mon, 15 May 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+ r12307@zot: tom | 2006-05-15 23:09:54 -0400
+ * When adding watchers and checking a user-supplied email address against the current user's, we want to canonicalize it and then compare case-insensitively.
+
+ * Typo fix
+
+------------------------------------------------------------------------
+r5231 | trs | 2006-05-15 21:13:44 -0400 (Mon, 15 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/configure.ac
+
+ r12305@zot: tom | 2006-05-15 21:13:20 -0400
+ Revert the group-finding fix as it doesn't work on all platforms
+
+------------------------------------------------------------------------
+r5228 | kevinr | 2006-05-15 14:43:13 -0400 (Mon, 15 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/default
+ A /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/take
+
+ r12583@SAD-GIRL-IN-SNOW: kevinr | 2006-05-15 14:42:02 -0400
+ * First (*BROKEN*) pass at take/steal/etc.
+
+------------------------------------------------------------------------
+r5227 | jesse | 2006-05-15 14:02:23 -0400 (Mon, 15 May 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+* The change to AddLink ACLs was a bit premature. It was actually a functionality change relative to 3.4
+
+------------------------------------------------------------------------
+r5222 | jesse | 2006-05-14 21:37:17 -0400 (Sun, 14 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r13785@hualien: jesse | 2006-05-14 21:34:01 -0400
+ * 3.6.0rc2
+
+------------------------------------------------------------------------
+r5221 | jesse | 2006-05-14 21:31:30 -0400 (Sun, 14 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r13781@hualien: jesse | 2006-05-14 21:08:33 -0400
+ * fixed the test count for the cli tests
+
+------------------------------------------------------------------------
+r5220 | jesse | 2006-05-14 21:31:19 -0400 (Sun, 14 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/index.html
+
+ r13780@hualien: jesse | 2006-05-14 21:08:21 -0400
+ * When using the quick ticket creation widget, set the requestor to the current user
+
+------------------------------------------------------------------------
+r5219 | jesse | 2006-05-14 21:31:10 -0400 (Sun, 14 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+ r13779@hualien: jesse | 2006-05-14 21:04:13 -0400
+ By popular demand, adding a ticket link doesn't check ACLs on the second ticket
+
+------------------------------------------------------------------------
+r5202 | ruz | 2006-05-11 03:45:48 -0400 (Thu, 11 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/t/regression/12-search.t
+
+* add test for CF.{cfname} search format
+------------------------------------------------------------------------
+r5201 | ruz | 2006-05-11 03:34:50 -0400 (Thu, 11 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+
+* fixes in searches by CFs
+** CustomFields alias could be undef, we not always join to this table
+** cache both aliases we use (ObjectCustomFieldValues and CustomFields)
+------------------------------------------------------------------------
+r5200 | ruz | 2006-05-11 03:31:30 -0400 (Thu, 11 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/t/regression/12-search.t
+
+* add several CF searches that are failing
+------------------------------------------------------------------------
+r5199 | ruz | 2006-05-11 03:30:35 -0400 (Thu, 11 May 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+* drop noisy unint warning
+------------------------------------------------------------------------
+r5182 | trs | 2006-05-08 22:08:15 -0400 (Mon, 08 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/misc.css
+
+ r10382@zot: tom | 2006-05-08 22:07:05 -0400
+ Some fixes for horizontal scrolling problems (the query builder will still cause horizontal scroll if the window isn't wide enough, however, as a result of it's layout)
+
+------------------------------------------------------------------------
+r5181 | trs | 2006-05-08 21:07:04 -0400 (Mon, 08 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/configure.ac
+
+ r10380@zot: tom | 2006-05-08 21:06:34 -0400
+ More robust way of figuring out the user's group which doesn't rely on order
+
+------------------------------------------------------------------------
+r5180 | kevinr | 2006-05-08 19:20:16 -0400 (Mon, 08 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r12580@SAD-GIRL-IN-SNOW: kevinr | 2006-05-08 19:18:46 -0400
+ * Spec'd out tests for 'take' and 'steal' via the CLI
+
+------------------------------------------------------------------------
+r5179 | kevinr | 2006-05-08 19:19:27 -0400 (Mon, 08 May 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/comment
+
+ r12465@SAD-GIRL-IN-SNOW: kevinr | 2006-05-01 19:24:23 -0400
+ * Err, actually changed the warnings to debugs, as opposed to commenting them
+ out. Oops.
+
+------------------------------------------------------------------------
+r5177 | jesse | 2006-05-08 11:38:03 -0400 (Mon, 08 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r13308@hualien: jesse | 2006-05-08 11:36:46 -0400
+ * RC 1
+
+------------------------------------------------------------------------
+r5176 | jesse | 2006-05-08 11:37:46 -0400 (Mon, 08 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/cs.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/da.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/en.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/es.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fi.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/he.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/hu.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/nl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/no.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_tw.po
+
+ r13307@hualien: jesse | 2006-05-08 11:36:06 -0400
+ * Message catalogs updated
+
+------------------------------------------------------------------------
+r5175 | jesse | 2006-05-08 11:37:40 -0400 (Mon, 08 May 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomField
+ M /rt/branches/3.5-TESTING/html/Elements/ShowCustomFields
+
+ r13302@hualien: jesse | 2006-05-08 11:20:08 -0400
+ RT-Ticket: 7511
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Todd Chapman to add callbacks to customize custom field entry widgets
+
+------------------------------------------------------------------------
+r5147 | kevinr | 2006-05-01 18:31:18 -0400 (Mon, 01 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r12460@sad-girl-in-snow: kevinr | 2006-05-01 18:30:24 -0400
+ * Added tests for merge via the CLI
+
+------------------------------------------------------------------------
+r5146 | kevinr | 2006-05-01 18:31:06 -0400 (Mon, 01 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ A /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/merge
+
+ r12459@sad-girl-in-snow: kevinr | 2006-05-01 18:12:40 -0400
+ * Made merging tickets via the REST interface and the CLI tool work
+
+------------------------------------------------------------------------
+r5145 | kevinr | 2006-05-01 18:30:57 -0400 (Mon, 01 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/comment
+
+ r12458@sad-girl-in-snow: kevinr | 2006-05-01 17:37:01 -0400
+ * Changed a couple warning messages to debugs in the REST comment code
+
+------------------------------------------------------------------------
+r5143 | kevinr | 2006-05-01 15:57:50 -0400 (Mon, 01 May 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r12456@sad-girl-in-snow: kevinr | 2006-05-01 15:57:14 -0400
+ * Applied David Schweikert's patch to only invoke Term::ReadLine when we're
+ actually going to be using it.
+
+------------------------------------------------------------------------
+r5142 | kevinr | 2006-05-01 15:18:02 -0400 (Mon, 01 May 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r12449@sad-girl-in-snow: kevinr | 2006-05-01 15:15:43 -0400
+ * Updated the CLI test suite to check that commenting and replying actually
+ work now
+
+------------------------------------------------------------------------
+r5141 | kevinr | 2006-05-01 15:17:28 -0400 (Mon, 01 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/links
+
+ r12277@sad-girl-in-snow: kevinr | 2006-04-24 21:12:50 -0400
+ * Changed the other REST Forms to use <%INIT> instead of <%perl>
+
+------------------------------------------------------------------------
+r5140 | kevinr | 2006-05-01 15:17:18 -0400 (Mon, 01 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/comment
+
+ r12276@sad-girl-in-snow: kevinr | 2006-04-24 21:00:07 -0400
+ * Fixed the error-out after commenting via the CLI tool
+
+------------------------------------------------------------------------
+r5139 | kevinr | 2006-05-01 15:17:01 -0400 (Mon, 01 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/comment
+ M /rt/branches/3.5-TESTING/html/REST/1.0/dhandler
+
+ r12275@sad-girl-in-snow: kevinr | 2006-04-24 20:44:24 -0400
+ * Cleaned up the REST comment form and the dhandler a bit.
+
+------------------------------------------------------------------------
+r5138 | kevinr | 2006-05-01 15:16:52 -0400 (Mon, 01 May 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/comment
+
+ r12274@sad-girl-in-snow: kevinr | 2006-04-24 20:03:15 -0400
+ * Commenting via the REST interface works now!
+
+------------------------------------------------------------------------
+r5137 | kevinr | 2006-05-01 15:16:39 -0400 (Mon, 01 May 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/comment
+
+ r12273@sad-girl-in-snow: kevinr | 2006-04-24 19:03:22 -0400
+ * Commenting via the REST interface now doesn't error (although it doesn't
+ actually /do/ anything, either).
+
+------------------------------------------------------------------------
+r5133 | jesse | 2006-04-28 12:29:01 -0400 (Fri, 28 Apr 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Search/Results.tsv
+
+ r12020@hualien: jesse | 2006-04-28 12:28:09 -0400
+ David Schweikert pointed out that the new sorting behaviour can break the Bulk update page (and by extension, the spreadsheet download)
+
+------------------------------------------------------------------------
+r5132 | jesse | 2006-04-28 12:28:48 -0400 (Fri, 28 Apr 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Chart
+
+ r12019@hualien: jesse | 2006-04-28 12:27:03 -0400
+ * If the system GD library can't display PNGs, display GIFs
+
+------------------------------------------------------------------------
+r5119 | ruz | 2006-04-26 16:14:47 -0400 (Wed, 26 Apr 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/sbin/rt-setup-database.in
+
+* intend&style
+* change some die with exits
+------------------------------------------------------------------------
+r5111 | ruz | 2006-04-25 15:45:46 -0400 (Tue, 25 Apr 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditFormat
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditQuery
+ M /rt/branches/3.5-TESTING/html/Widgets/SelectionBox
+
+* add spaces around Left, Right, Up, Down arrows because bug in WWW::Mechanize
+** http://rt.cpan.org/Ticket/Display.html?id=18921
+------------------------------------------------------------------------
+r5061 | jesse | 2006-04-24 11:07:22 -0400 (Mon, 24 Apr 2006) | 12 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowRequestor
+
+ r11845@hualien: jesse | 2006-04-24 11:06:51 -0400
+ On the Ticket page, if you click on the "More about Requestor" link, you
+ get to the Admin/Users/Modify.html page, but if you don't have the
+ AdminUsers privilege, you only get a blank page.
+
+ This patch removes the link if the user doesn't have that privilege.
+
+ David Schweikert | phone: +41 44 632 7019
+ System manager ISG.EE | walk: ETH Zentrum, ETL F24.1
+ ETH Zurich, Switzerland | web: http://people.ee.ethz.ch/dws
+
+
+------------------------------------------------------------------------
+r5048 | ruz | 2006-04-18 19:06:57 -0400 (Tue, 18 Apr 2006) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+* install modules after all checks
+* test that CPAN.pm is configured and exit otherwise
+* use eval with CPAN.pm
+* output some suggestions if CPAN shell fails terribly
+** this happens for me with CPAN-1.87, syntax error in FirstTime.pm
+
+------------------------------------------------------------------------
+r5026 | trs | 2006-04-12 20:41:25 -0400 (Wed, 12 Apr 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTime
+
+ r9771@wintermute: tom | 2006-04-12 20:39:24 -0400
+ Rodney Rindels pointed out that nothing is displayed if the time values are too large. r3818 should have made the 'elsif' an 'else' when it removed the original 'else'.
+
+------------------------------------------------------------------------
+r5025 | trs | 2006-04-12 19:27:16 -0400 (Wed, 12 Apr 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/SelectQueue
+
+ r9767@wintermute: tom | 2006-04-12 19:26:09 -0400
+ Cache in the session instead of the mason cache
+
+------------------------------------------------------------------------
+r5006 | kevinr | 2006-04-11 01:10:01 -0400 (Tue, 11 Apr 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/comment
+
+ r12018@sad-girl-in-snow: kevinr | 2006-04-11 01:08:53 -0400
+ * Copied the old comment code over to the place where the new codepath is
+ looking for it, and started to look at making it work. It's not there yet,
+ but it will be soon.
+
+------------------------------------------------------------------------
+r5005 | kevinr | 2006-04-11 01:09:29 -0400 (Tue, 11 Apr 2006) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/html/REST/1.0/dhandler
+
+ r12015@sad-girl-in-snow: kevinr | 2006-04-10 21:53:13 -0400
+ * Added a comment to dhandler to reflect one of the duties it *should* have
+ * Fixed the default server location to point to RT's default location
+ * Made the command-line tool default to shell mode if no arguments are passed
+ to it
+
+------------------------------------------------------------------------
+r5004 | kevinr | 2006-04-10 22:25:24 -0400 (Mon, 10 Apr 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.5-TESTING/html/REST/1.0/dhandler
+ M /rt/branches/3.5-TESTING/html/REST/1.0/ticket/comment
+
+ r12014@sad-girl-in-snow: kevinr | 2006-04-10 21:25:57 -0400
+ * Fixed a couple minor bits of ugly code and a comment
+
+------------------------------------------------------------------------
+r5003 | kevinr | 2006-04-10 22:24:50 -0400 (Mon, 10 Apr 2006) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r12013@sad-girl-in-snow: kevinr | 2006-04-10 20:45:06 -0400
+ * Discovered that, when attempting to comment via the command-line tool, the
+ RT instance thought it was being asked to find a ticket with an id of
+ 'comment'. Flipped the order of the terms in the URL we're requesting so
+ that they fit the canonical order.
+
+------------------------------------------------------------------------
+r5002 | kevinr | 2006-04-10 19:02:25 -0400 (Mon, 10 Apr 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r12011@sad-girl-in-snow: kevinr | 2006-04-10 19:00:46 -0400
+ * Made the command-line tool not spew the entire help text if you give it an
+ unrecognized command
+ * Made the command-line tool ignore a leading 'rt' in a command in shell mode
+ (eg. 'rt create' does the same thing as just 'create')
+ * Added a test for the latter
+
+------------------------------------------------------------------------
+r4997 | jesse | 2006-04-09 22:28:55 -0400 (Sun, 09 Apr 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditFormat
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditQuery
+ M /rt/branches/3.5-TESTING/html/Widgets/SelectionBox
+
+ r11490@hualien: jesse | 2006-04-09 22:28:32 -0400
+ * HTML arrow cleanup from Joshua Colson
+
+------------------------------------------------------------------------
+r4995 | jesse | 2006-04-07 10:37:40 -0400 (Fri, 07 Apr 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r11132@hualien: jesse | 2006-04-07 10:35:32 -0400
+ * 3.6.0pre1
+
+
+------------------------------------------------------------------------
+r4923 | jesse | 2006-03-30 21:03:20 -0500 (Thu, 30 Mar 2006) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/23cfsort.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/24pawsort.t
+
+ r10634@hualien: jesse | 2006-03-31 11:03:02 +0900
+ RT-Ticket: 7425
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Test fixes from Todd Chapman
+
+
+------------------------------------------------------------------------
+r4922 | jesse | 2006-03-30 20:41:00 -0500 (Thu, 30 Mar 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/autohandler
+
+ r10630@hualien: jesse | 2006-03-31 10:40:29 +0900
+ * perltidied the autohandler per Jim Meyer.
+
+
+------------------------------------------------------------------------
+r4786 | kevinr | 2006-03-20 21:18:44 -0500 (Mon, 20 Mar 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11586@sad-girl-in-snow: kevinr | 2006-03-20 20:11:58 -0500
+ * Fixed the problem where the RT CLI wouldn't let you create a new object if
+ you weren't 'add'ing something to it (ie. if you were only 'set'ing values).
+ Turns out that it wasn't grabbing a form from the server when creating a new
+ object unless you 'add'ed something, so default values like queue weren't
+ getting filled in.
+
+------------------------------------------------------------------------
+r4785 | kevinr | 2006-03-20 21:18:29 -0500 (Mon, 20 Mar 2006) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11585@sad-girl-in-snow: kevinr | 2006-03-20 18:18:11 -0500
+ * Fixed the RT command-line tool to not spew the entire help text for the
+ command you just typed if you screw something up, so you don't have to scroll
+ up to see the actual error message.
+ * Changed most of the todo_skip CLI tests to TODO tests, so I can see when they
+ start passing, since I no longer have to wade through many many screens worth
+ of help text. :)
+
+------------------------------------------------------------------------
+r4783 | ruz | 2006-03-20 19:26:00 -0500 (Mon, 20 Mar 2006) | 2 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/Prefs/Search.html
+ M /rt/branches/3.5-TESTING/html/Prefs/SearchOptions.html
+
+* fix for multiple OrderBy on edit
+** backport r4622 from 3.7
+------------------------------------------------------------------------
+r4782 | ruz | 2006-03-20 18:00:12 -0500 (Mon, 20 Mar 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyDates.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyPeople.html
+
+* return back components calls dropped by rev 4562
+------------------------------------------------------------------------
+r4772 | trs | 2006-03-19 00:09:25 -0500 (Sun, 19 Mar 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r9240@wintermute: tom | 2006-03-19 00:08:22 -0500
+ RT-Ticket: 7415
+ RT-Status: resolved
+ RT-Action: correspond
+
+ Removed invalid slash (/) character from titlebox IDs.
+
+------------------------------------------------------------------------
+r4770 | alexmv | 2006-03-16 17:10:00 -0500 (Thu, 16 Mar 2006) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Submit
+
+ r8588@zoq-fot-pik: chmrr | 2006-03-16 17:09:31 -0500
+ RT-Ticket: 7424
+ RT-Update: correspond
+ RT-Status: resolved
+
+ * Patch from Todd Chapman to remove duplicate class="..."
+
+
+------------------------------------------------------------------------
+r4755 | kevinr | 2006-03-13 21:49:47 -0500 (Mon, 13 Mar 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11461@SAD-GIRL-IN-SNOW: kevinr | 2006-03-13 21:20:26 -0500
+ * Tweaked my new CLI tests to handle servers and ports which are not localhost
+ and 80, respectively, a la Todd Chapman's patch.
+
+------------------------------------------------------------------------
+r4754 | kevinr | 2006-03-13 21:49:21 -0500 (Mon, 13 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11460@SAD-GIRL-IN-SNOW: kevinr | 2006-03-13 19:57:10 -0500
+ * Gave the RT CLI tests a plan
+
+------------------------------------------------------------------------
+r4753 | kevinr | 2006-03-13 21:48:45 -0500 (Mon, 13 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r11459@SAD-GIRL-IN-SNOW: kevinr | 2006-03-13 19:54:07 -0500
+ * Bumped RT 3.5's DBIx::SearchBuilder dependency from 1.35 to 1.39
+
+------------------------------------------------------------------------
+r4752 | kevinr | 2006-03-13 21:47:52 -0500 (Mon, 13 Mar 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/02basic_web.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/03web_compiliation_errors.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/06mailgateway.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/07acl.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/08web_cf_access.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/17custom_search.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/18custom_frontpage.t
+
+ r11458@SAD-GIRL-IN-SNOW: kevinr | 2006-03-13 19:28:36 -0500
+ * Applied patch from Todd Chapman to make the RT 3.5 test-suite obey the
+ configuration directives for server and port, so you don't have to be
+ testing on localhost and port 80.
+
+------------------------------------------------------------------------
+r4744 | kevinr | 2006-03-13 18:42:01 -0500 (Mon, 13 Mar 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11456@SAD-GIRL-IN-SNOW: kevinr | 2006-03-13 18:41:41 -0500
+ * Added tests for a basic (not-yet-implemented) CLI mechanism to create and
+ modify custom fields
+
+------------------------------------------------------------------------
+r4743 | kevinr | 2006-03-13 18:28:23 -0500 (Mon, 13 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11452@SAD-GIRL-IN-SNOW: kevinr | 2006-03-13 18:27:41 -0500
+ * Tests for modification of users, groups, and queues from the CLI
+
+------------------------------------------------------------------------
+r4740 | kevinr | 2006-03-13 16:52:02 -0500 (Mon, 13 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11449@SAD-GIRL-IN-SNOW: kevinr | 2006-03-13 16:50:42 -0500
+ * Added tests for listing and showing tickets from the command line
+
+------------------------------------------------------------------------
+r4739 | kevinr | 2006-03-13 16:51:18 -0500 (Mon, 13 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r11363@SAD-GIRL-IN-SNOW: kevinr | 2006-03-06 23:33:15 -0500
+ * Added basic Term::ReadLine support to the CLI
+
+------------------------------------------------------------------------
+r4726 | jesse | 2006-03-13 01:29:18 -0500 (Mon, 13 Mar 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+
+------------------------------------------------------------------------
+r4725 | jesse | 2006-03-13 01:28:53 -0500 (Mon, 13 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+
+ r26050@truegrounds: jesse | 2006-03-12 20:05:18 -0800
+ * Switched the LinkTransactionsRunOneScrip default, tx to Todd Chapman
+
+------------------------------------------------------------------------
+r4706 | kevinr | 2006-03-06 21:46:17 -0500 (Mon, 06 Mar 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r11357@SAD-GIRL-IN-SNOW: kevinr | 2006-03-06 21:46:02 -0500
+ * Added most of the 'frob this database field on the ticket' tests I think the
+ CLI is going to want
+
+------------------------------------------------------------------------
+r4701 | kevinr | 2006-03-06 18:24:39 -0500 (Mon, 06 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r11355@SAD-GIRL-IN-SNOW: kevinr | 2006-03-06 18:23:38 -0500
+ * Added the beginnings of CLI tests
+
+------------------------------------------------------------------------
+r4688 | jesse | 2006-03-04 17:51:25 -0500 (Sat, 04 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+
+ r25526@truegrounds: jesse | 2006-03-04 14:49:56 -0800
+ * If a given txn is on something other than a ticket, don't try to update its TimeTaken
+
+------------------------------------------------------------------------
+r4687 | jesse | 2006-03-04 17:51:08 -0500 (Sat, 04 Mar 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+
+ r24842@truegrounds: jesse | 2006-03-01 13:13:35 -0800
+ * that extra slash causes breakage
+
+------------------------------------------------------------------------
+r4566 | jesse | 2006-02-21 17:35:15 -0500 (Tue, 21 Feb 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r24495@truegrounds: jesse | 2006-02-21 17:33:52 -0500
+ * 3.6.0pre0
+
+
+------------------------------------------------------------------------
+r4565 | jesse | 2006-02-21 17:31:37 -0500 (Tue, 21 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/html/Elements/TitleBoxEnd
+ A /rt/branches/3.5-TESTING/html/Elements/TitleBoxStart
+
+ r24493@truegrounds: jesse | 2006-02-21 17:30:01 -0500
+ * Wrappers for 3.4 compatibility
+
+------------------------------------------------------------------------
+r4564 | jesse | 2006-02-21 16:32:09 -0500 (Tue, 21 Feb 2006) | 2 lines
+Changed paths:
+ A /rt/branches/3.5-TESTING/html/Elements/TitleBox
+
+* Titlebox component for compatibility with 3.4
+
+------------------------------------------------------------------------
+r4563 | jesse | 2006-02-21 16:25:00 -0500 (Tue, 21 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/ahah.js
+
+ r24478@truegrounds: jesse | 2006-02-21 14:43:51 -0500
+ * merge touchups
+
+------------------------------------------------------------------------
+r4562 | jesse | 2006-02-21 16:23:06 -0500 (Tue, 21 Feb 2006) | 115 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/bin/webmux.pl.in
+ M /rt/branches/3.5-TESTING/etc/schema.Oracle
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/Modify.html
+ M /rt/branches/3.5-TESTING/html/Elements/QuickCreate
+ M /rt/branches/3.5-TESTING/html/Elements/ShowCustomFields
+ A /rt/branches/3.5-TESTING/html/NoAuth/ahah.js
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/3.5-TESTING/html/Search/Results.tsv
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Modify.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyAll.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyDates.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyLinks.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyPeople.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Update.html
+ M /rt/branches/3.5-TESTING/html/Tools/Offline.html
+ M /rt/branches/3.5-TESTING/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Web.pm
+ M /rt/branches/3.5-TESTING/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r24477@truegrounds: jesse | 2006-02-21 14:15:55 -0500
+ r23030@truegrounds: jesse | 2006-01-31 18:51:02 -0500
+ * Added the ability to make custom fields link to and include content from other systems.
+ r23031@truegrounds: jesse | 2006-01-31 18:52:13 -0500
+ * Added a note about the AHAH support
+ r23050@truegrounds: jesse | 2006-02-03 13:34:17 -0500
+ * Added a bunch more callbacks for great justice.
+ r23555@truegrounds: jesse | 2006-02-09 14:19:23 -0500
+ * missing _ in regex
+ r23569@truegrounds: jesse | 2006-02-09 14:48:54 -0500
+ * open links in new windows
+ r23587@truegrounds: jesse | 2006-02-09 23:13:22 -0500
+ * Reminder editing updates
+
+ r23593@truegrounds: jesse | 2006-02-10 14:47:08 -0500
+ * Force the content type inside a js comment
+ r23594@truegrounds: jesse | 2006-02-10 14:49:27 -0500
+ * wrap the mason directive for setting the content type in a js comment
+
+ r23812@truegrounds: jesse | 2006-02-16 14:42:23 -0500
+ * Chaldea release engineering
+ r23814@truegrounds: jesse | 2006-02-16 16:33:40 -0500
+ * try to make rt.js do the right thing with headers
+ r23826@truegrounds: jesse | 2006-02-16 21:25:05 -0500
+ * Backport tickets_overlay from 3.5. Fix a bug that stopped search on global custom fields
+ r23835@truegrounds: jesse | 2006-02-17 13:02:28 -0500
+ * Chaldea r7
+ r24473@truegrounds: jesse | 2006-02-21 14:05:29 -0500
+ r24470@truegrounds: jesse | 2006-02-21 13:50:15 -0500
+ r23039@truegrounds (orig r4459): alexmv | 2006-01-31 21:33:58 -0500
+ r8863@zoq-fot-pik: chmrr | 2006-01-31 21:33:12 -0500
+ * Collapse ForceOwner and Owner
+ * Fix updating of (Admin)Ccs
+ * Default UpdateType to 'correspond'
+ * Default content-type to 'text/plain'
+
+ r23040@truegrounds (orig r4460): alexmv | 2006-01-31 23:42:36 -0500
+ r8868@zoq-fot-pik: chmrr | 2006-01-31 22:24:18 -0500
+ * Untabify and adjust indenting in one or two places
+
+ r23041@truegrounds (orig r4461): alexmv | 2006-01-31 23:42:41 -0500
+ r8869@zoq-fot-pik: chmrr | 2006-01-31 23:41:57 -0500
+ * Custom field updating during processing
+ * Better error handling when ticket id doesn't exist during update
+
+ r23082@truegrounds (orig r4473): alexmv | 2006-02-03 14:32:54 -0500
+ r8895@zoq-fot-pik: chmrr | 2006-02-03 14:32:05 -0500
+ * 'id' is not longer required to be the first column. In fact, it's
+ not required at all. Blank or non-existant 'id' columns will cause it
+ to assume the ticket is new, and come up with an automatic template id
+ for the row.
+
+ r23515@truegrounds (orig r4504): alexmv | 2006-02-08 15:01:17 -0500
+ r8969@zoq-fot-pik: chmrr | 2006-02-08 15:00:00 -0500
+ * Whitespace fixes in Results.tsv
+ * Spit out custom fields as CF-...
+ * During offline upload parsing, try to treat date as ISO first,
+ falling back to unknown
+ * Keep offline upload form from tacking on an extra newline each
+ submit
+ * Minimize yo-yo-ing of status if possible
+ * Allow false values as possible values, as long as they're defined
+ * Cc, AdminCc, and Requestor may be either email addresses or users
+
+ r23628@truegrounds (orig r4527): alexmv | 2006-02-10 19:10:22 -0500
+ r9038@zoq-fot-pik: chmrr | 2006-02-10 19:09:21 -0500
+ * Accept either singular or plural forms of requestor, cc, admincc
+
+
+ r24472@truegrounds: jesse | 2006-02-21 13:57:01 -0500
+ r22894@truegrounds: jesse | 2006-01-24 07:44:05 -0500
+ * Note that our SQLite dependency is 1.0
+ r22958@truegrounds: jesse | 2006-01-25 07:08:34 -0500
+ From: Joop van de Wege <JoopvandeWege@mococo.nl>
+ Message-Id: <20060125125248.1A97.JOOPVANDEWEGE@mococo.nl>
+
+ > There is atleast one problem that I have spotted and that is that
+ > schema.Oracle contains two empty lines in CREATE TABLE
+ > ObjectCustomFieldValues which don't belong there.
+ > That is the second set of errors you get. The first is an indication
+ > that an sequence with that name already exists in the schema of that
+ > Oracle users you're RT installing in.
+
+ r22960@truegrounds: jesse | 2006-01-25 07:47:07 -0500
+ * a couple added lines of docs to the cli
+ r22962@truegrounds: jesse | 2006-01-25 08:18:09 -0500
+ * Updated mandatory fields for ticket creation forms
+ r24471@truegrounds: jesse | 2006-02-21 13:51:18 -0500
+ r23026@truegrounds (orig r4448): kevinr | 2006-01-30 19:25:47 -0500
+ r10537@SAD-GIRL-IN-SNOW: kevinr | 2006-01-30 19:20:52 -0500
+ RT-Ticket: 7289
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Updated German translation (thanks to Thorsten Brumm)
+
+ r23132@truegrounds (orig r4497): alexmv | 2006-02-04 18:34:45 -0500
+ r8936@zoq-fot-pik: chmrr | 2006-02-04 18:24:38 -0500
+ * Only rmtree if we have something to rm; keeps rmtree from
+ complaining about 'Not root path(s) specified'
+
+ r23133@truegrounds (orig r4498): alexmv | 2006-02-04 18:34:51 -0500
+ r8937@zoq-fot-pik: chmrr | 2006-02-04 18:33:57 -0500
+ RT-Ticket: 7329
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Use SelectNewTicketQueue instead of SelectQueue
+
+
+
+
+
+
+
+------------------------------------------------------------------------
+r4561 | trs | 2006-02-21 15:20:58 -0500 (Tue, 21 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/misc.css
+
+ r8719@wintermute: tom | 2006-02-21 15:19:10 -0500
+ We expect a white background
+
+------------------------------------------------------------------------
+r4558 | kevinr | 2006-02-20 22:24:26 -0500 (Mon, 20 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r11045@sad-girl-in-snow: kevinr | 2006-02-20 22:23:27 -0500
+ * Added basic documentation for the shell
+
+------------------------------------------------------------------------
+r4557 | kevinr | 2006-02-20 21:50:53 -0500 (Mon, 20 Feb 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r11043@sad-girl-in-snow: kevinr | 2006-02-20 21:49:38 -0500
+ * Added a 'quit' command (also 'exit') to exit from the shell. This makes
+ testing via Test::Expect a *lot* easier.
+
+------------------------------------------------------------------------
+r4556 | kevinr | 2006-02-20 21:42:03 -0500 (Mon, 20 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r10929@sad-girl-in-snow: kevinr | 2006-02-13 20:15:37 -0500
+ * Fixed a typo in the help for 'rt edit'
+
+------------------------------------------------------------------------
+r4555 | kevinr | 2006-02-20 21:41:46 -0500 (Mon, 20 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/lib/t/regression/26command_line.t
+
+ r10928@sad-girl-in-snow: kevinr | 2006-02-13 19:44:30 -0500
+ * Laid out a skeleton for the CLI tests
+
+------------------------------------------------------------------------
+r4554 | kevinr | 2006-02-20 21:41:28 -0500 (Mon, 20 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/25scrip_order.t
+
+ r10927@sad-girl-in-snow: kevinr | 2006-02-13 19:20:37 -0500
+ * Added a plan to the scrip-ordering tests
+
+------------------------------------------------------------------------
+r4550 | jesse | 2006-02-16 21:29:14 -0500 (Thu, 16 Feb 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+
+ r23828@truegrounds: jesse | 2006-02-16 18:27:41 -0800
+ * Fix for searching on global custom fields
+
+
+------------------------------------------------------------------------
+r4549 | jesse | 2006-02-16 21:28:52 -0500 (Thu, 16 Feb 2006) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r23511@truegrounds: jesse | 2006-02-08 10:23:27 -0800
+ RT-Ticket: 7324
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Bumped Pg dependency. Joby Walker at University of Washington discovered
+ incorrect ordering behaviour with DBD::Pg 1.42 and earlier
+
+
+------------------------------------------------------------------------
+r4503 | ruz | 2006-02-08 05:29:33 -0500 (Wed, 08 Feb 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay_SQL.pm
+
+* make tests reintrant in lib/RT/Tickets_Overlay_SQL.pm
+* queries changes broke several things, for example
+ "id = 123 OR MemberOf = 123", all breakages covered allready
+ in tests.
+
+------------------------------------------------------------------------
+r4502 | trs | 2006-02-07 16:15:21 -0500 (Tue, 07 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r8432@wintermute: tom | 2006-02-07 16:11:01 -0500
+ Fix undefined warnings
+
+------------------------------------------------------------------------
+r4500 | ruz | 2006-02-06 14:47:28 -0500 (Mon, 06 Feb 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/01ticket_link_searching.t
+
+* backport from 3.7
+* add support for optional OPERATOR argument in LimitLinked(From|To)
+* and add this to methods Limit(HasMemeber|DependsOn|...)
+* in _LinkLimit:
+** support for IS NOT NULL queries ala "has at least one link of defined type"
+** operator '!=' didn't work at all, now it works with meanning "has no link with defined ticket"
+
+------------------------------------------------------------------------
+r4499 | ruz | 2006-02-06 14:35:37 -0500 (Mon, 06 Feb 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/NoAuth/images/autohandler
+
+* opened FILE, but reading from <file>, use $fh instead
+** result off tidy script
+* turn on binmode on handle
+* flush buffers, otherwise we load all file into memmory
+
+------------------------------------------------------------------------
+r4463 | kevinr | 2006-02-02 00:04:27 -0500 (Thu, 02 Feb 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/UPGRADING
+
+ r10741@RANDOM-THREE-NINETY-TWO: kevinr | 2006-02-02 00:03:25 -0500
+ * Updated the UPGRADING document to reflect the new scrip ordering
+
+------------------------------------------------------------------------
+r4462 | kevinr | 2006-02-01 22:27:10 -0500 (Wed, 01 Feb 2006) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditScrips
+ M /rt/branches/3.5-TESTING/lib/RT/Scrips_Overlay.pm
+ A /rt/branches/3.5-TESTING/lib/t/regression/25scrip_order.t
+
+ r10739@RANDOM-ONE-NINETY-THREE: kevinr | 2006-02-01 22:26:27 -0500
+ RT-Ticket: 7295
+ RT-Status: resolved
+ RT-Update: correspond
+
+ We now order scrips by their description, so you can force them to run in a
+ particular order by prepending numbers to their descriptions. Updated the
+ perldoc and EditScrips UI element to reflect this and wrote tests for it.
+
+------------------------------------------------------------------------
+r4458 | kevinr | 2006-01-31 21:12:46 -0500 (Tue, 31 Jan 2006) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/cascaded.js
+
+ r10725@SAD-GIRL-IN-SNOW: kevinr | 2006-01-31 21:08:57 -0500
+ * Reverted cascaded.js to the previous version -- r4390 seems to have
+ refactored the code in a way which doesn't work, or doesn't work in all
+ browsers.
+
+------------------------------------------------------------------------
+r4452 | kevinr | 2006-01-31 18:14:51 -0500 (Tue, 31 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+
+ r10546@SAD-GIRL-IN-SNOW: kevinr | 2006-01-31 18:13:50 -0500
+ * Fixed a typo.
+
+------------------------------------------------------------------------
+r4446 | jesse | 2006-01-25 08:35:46 -0500 (Wed, 25 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r22963@truegrounds: jesse | 2006-01-25 14:32:59 +0100
+ * Re-add the warning message
+
+------------------------------------------------------------------------
+r4434 | jesse | 2006-01-23 08:59:20 -0500 (Mon, 23 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/REST/1.0/Forms/ticket/default
+
+ r22883@truegrounds: jesse | 2006-01-23 11:01:51 +0100
+ * CLI support for custom fields from lwang at Cluster FS
+
+------------------------------------------------------------------------
+r4433 | jesse | 2006-01-23 08:59:01 -0500 (Mon, 23 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r22882@truegrounds: jesse | 2006-01-23 11:00:23 +0100
+ * removed notice that the CLI is beta only
+
+------------------------------------------------------------------------
+r4427 | kevinr | 2006-01-20 17:31:36 -0500 (Fri, 20 Jan 2006) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/Members.html
+
+ r10418@STRATTON-FIVE-HUNDRED: kevinr | 2006-01-20 17:28:00 -0500
+ RT-Ticket: 7148
+ RT-Status: update
+ RT-Update: correspond
+
+ Applied Todd Chapman's patch, which:
+ 1. Orders user members of a group by Name
+ 2. If a user/group is already a member, doesn't list them in the select
+ 3. Doesn't list the group itself because a group can't have itself
+ as a member
+
+------------------------------------------------------------------------
+r4416 | kevinr | 2006-01-19 22:52:04 -0500 (Thu, 19 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/ListActions
+
+ r10392@sad-girl-in-snow: kevinr | 2006-01-19 22:47:37 -0500
+ * Cleaned up html/Elements/ListActions (based on a patch from Ruslan Zakirov)
+
+------------------------------------------------------------------------
+r4415 | kevinr | 2006-01-19 22:51:48 -0500 (Thu, 19 Jan 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+
+ r10391@sad-girl-in-snow: kevinr | 2006-01-18 16:32:59 -0500
+ RT-Ticket: 7143
+ RT-Status: open
+ RT-Update: correspond
+
+ Made minor cleanups to RT::Record (thanks to Ruslan Zakirov)
+
+------------------------------------------------------------------------
+r4414 | kevinr | 2006-01-19 22:51:23 -0500 (Thu, 19 Jan 2006) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Attachment_Overlay.pm
+
+ r10390@sad-girl-in-snow: kevinr | 2006-01-18 16:31:03 -0500
+ RT-Ticket: 7149
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Cleaned up attachment creation in lib/RT/Attachment_Overlay.pm (thanks to
+ Ruslan Zakirov)
+
+------------------------------------------------------------------------
+r4413 | kevinr | 2006-01-19 22:50:58 -0500 (Thu, 19 Jan 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+
+ r10386@sad-girl-in-snow: kevinr | 2006-01-18 15:59:51 -0500
+ RT-Ticket: 7154
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Silenced warnings in Search/Results.html (thanks to Jim Meyer)
+
+------------------------------------------------------------------------
+r4411 | jesse | 2006-01-19 10:17:30 -0500 (Thu, 19 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r22726@truegrounds: jesse | 2006-01-19 10:15:16 -0500
+ * 3.5.7
+
+------------------------------------------------------------------------
+r4408 | jesse | 2006-01-19 10:11:39 -0500 (Thu, 19 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/20-sort-by-requestor.t
+
+ r22721@truegrounds: jesse | 2006-01-19 10:08:18 -0500
+ * mergeup fixes
+
+------------------------------------------------------------------------
+r4407 | jesse | 2006-01-19 10:11:21 -0500 (Thu, 19 Jan 2006) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/schema.mysql
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomFields
+ M /rt/branches/3.5-TESTING/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyAll.html
+ M /rt/branches/3.5-TESTING/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Template_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/20-sort-by-requestor.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/22search_tix_by_txn.t
+ M /rt/branches/3.5-TESTING/sbin/rt-setup-database.in
+
+ r22720@truegrounds: jesse | 2006-01-19 09:15:00 -0500
+ * merge up from chaldea, quebec, 3.4
+
+------------------------------------------------------------------------
+r4394 | kevinr | 2006-01-17 00:48:17 -0500 (Tue, 17 Jan 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+
+ r10382@SAD-GIRL-IN-SNOW: kevinr | 2006-01-17 00:28:53 -0500
+ * Applied the bits of Jim Meyer's 'fixes' patch to
+ Elements/EditCustomFieldSelect which seemed still relevant
+
+------------------------------------------------------------------------
+r4393 | kevinr | 2006-01-17 00:48:10 -0500 (Tue, 17 Jan 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+
+ r10381@SAD-GIRL-IN-SNOW: kevinr | 2006-01-17 00:23:29 -0500
+ * Applied Jim Meyer's patch to fix the indentation of
+ Elements/EditCustomFieldSelect
+
+------------------------------------------------------------------------
+r4390 | ruz | 2006-01-13 06:41:20 -0500 (Fri, 13 Jan 2006) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/Elements/Submit
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/cascaded.js
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/combobox.js
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+* addClass and delClass functions
+* setVisibility util
+* get rid of style.display=[none,block]
+* setCheckbox(form, name, state) function that set all checkbox
+ inputs under the form element with defined name to the state.
+* update Submit element according to new changes
+
+------------------------------------------------------------------------
+r4389 | kevinr | 2006-01-13 02:42:26 -0500 (Fri, 13 Jan 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditSearches
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/AddWatchers
+
+
+------------------------------------------------------------------------
+r4388 | kevinr | 2006-01-13 02:40:52 -0500 (Fri, 13 Jan 2006) | 23 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/CollectionAsTable/Header
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/MessageBox
+ M /rt/branches/3.5-TESTING/html/Elements/SelectDate
+ M /rt/branches/3.5-TESTING/html/Elements/SelectWatcherType
+ M /rt/branches/3.5-TESTING/html/Elements/TicketList
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Search/Elements/BuildFormatString
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditSearches
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/AddWatchers
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyAll.html
+ M /rt/branches/3.5-TESTING/html/Tools/Offline.html
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBox
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Web/Menu/Item.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Web/Menu.pm
+
+ r10349@SAD-GIRL-IN-SNOW: kevinr | 2006-01-13 02:15:36 -0500
+ RT-Ticket: 7150, 7151, 7152, 7153, 7155, 7156, 7157, 7158, 7159, 7160, 7161, 7162, 7164, 7165, 7166
+ RT-Status: resolved
+ RT-Action: correspond
+
+ Fixed Mason warnings in:
+ Elements/CollectionAsTable/Header
+ Elements/Menu
+ Elements/MessageBox
+ Elements/SelectDate
+ Elements/SelectWatcherType
+ Elements/TicketList
+ Search/Bulk.html
+ Search/Elements/BuildFormatString
+ Search/Elements/EditSearches
+ Ticket/Elements/AddWatchers
+ Ticket/Elements/ShowTransactionAttachments
+ Ticket/ModifyAll.html
+ Ticket/Reminders.html
+ Tools/Offline.html
+ Widgets/TitleBox
+ (thanks to Jim Meyer)
+
+------------------------------------------------------------------------
+r4374 | ruz | 2006-01-11 22:04:46 -0500 (Wed, 11 Jan 2006) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/Objects.html
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomField
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomFields
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditScrip
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditScrips
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditTemplates
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditUserComments
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/ModifyTemplate
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickCustomFields
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickObjects
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectCustomFieldLookupType
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectGroups
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectModifyQueue
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectModifyUser
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectRights
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectScrip
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectScripAction
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectStage
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectTemplate
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectUsers
+ M /rt/branches/3.5-TESTING/html/Admin/Global/CustomFields/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Global/GroupRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Global/Template.html
+ M /rt/branches/3.5-TESTING/html/Admin/Global/UserRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Global/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/GroupRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/Members.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/UserRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/GroupRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/People.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/Scrips.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/Template.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/UserRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Tools/Configuration.html
+ M /rt/branches/3.5-TESTING/html/Admin/Users/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Users/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/index.html
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/ShowDependency
+ M /rt/branches/3.5-TESTING/html/Elements/Checkbox
+ M /rt/branches/3.5-TESTING/html/Elements/CollectionAsTable/Row
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomField
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldBinary
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldCombobox
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldImage
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldText
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldWikitext
+ M /rt/branches/3.5-TESTING/html/Elements/EditLinks
+ M /rt/branches/3.5-TESTING/html/Elements/Error
+ M /rt/branches/3.5-TESTING/html/Elements/Footer
+ M /rt/branches/3.5-TESTING/html/Elements/GotoTicket
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/Elements/Logo
+ M /rt/branches/3.5-TESTING/html/Elements/MessageBox
+ M /rt/branches/3.5-TESTING/html/Elements/MyRT
+ M /rt/branches/3.5-TESTING/html/Elements/QueueSummary
+ M /rt/branches/3.5-TESTING/html/Elements/QuickCreate
+ M /rt/branches/3.5-TESTING/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/3.5-TESTING/html/Elements/Refresh
+ M /rt/branches/3.5-TESTING/html/Elements/RefreshHomepage
+ M /rt/branches/3.5-TESTING/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/3.5-TESTING/html/Elements/SelectCustomFieldValue
+ M /rt/branches/3.5-TESTING/html/Elements/SelectEqualityOperator
+ M /rt/branches/3.5-TESTING/html/Elements/SelectGroups
+ M /rt/branches/3.5-TESTING/html/Elements/SelectUsers
+ M /rt/branches/3.5-TESTING/html/Elements/ShowCustomFieldImage
+ M /rt/branches/3.5-TESTING/html/NoAuth/Logout.html
+ M /rt/branches/3.5-TESTING/html/NoAuth/images/autohandler
+ M /rt/branches/3.5-TESTING/html/Prefs/MyRT.html
+ M /rt/branches/3.5-TESTING/html/Prefs/Quicksearch.html
+ M /rt/branches/3.5-TESTING/html/Prefs/SearchOptions.html
+ M /rt/branches/3.5-TESTING/html/Search/Build.html
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Search/Edit.html
+ M /rt/branches/3.5-TESTING/html/Search/Elements/Chart
+ M /rt/branches/3.5-TESTING/html/Search/Elements/DisplayOptions
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditFormat
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditQuery
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditSearches
+ M /rt/branches/3.5-TESTING/html/Search/Elements/NewListActions
+ M /rt/branches/3.5-TESTING/html/Search/Elements/PickBasics
+ M /rt/branches/3.5-TESTING/html/Search/Elements/PickCriteria
+ M /rt/branches/3.5-TESTING/html/Search/Elements/SelectAndOr
+ M /rt/branches/3.5-TESTING/html/Search/Elements/SelectGroup
+ M /rt/branches/3.5-TESTING/html/Search/Elements/SelectLinks
+ M /rt/branches/3.5-TESTING/html/Search/Elements/SelectPersonType
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+ M /rt/branches/3.5-TESTING/html/Search/Simple.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Create.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Display.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Elements/GotoTicket
+ M /rt/branches/3.5-TESTING/html/SelfService/Elements/MyRequests
+ M /rt/branches/3.5-TESTING/html/SelfService/Error.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Prefs.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Update.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/AddWatchers
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/BulkLinks
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditBasics
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditPeople
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditWatchers
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowDates
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowDependencies
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowMembers
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/3.5-TESTING/html/Ticket/History.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Modify.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyAll.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyDates.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyLinks.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyPeople.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Update.html
+ M /rt/branches/3.5-TESTING/html/Tools/MyDay.html
+ M /rt/branches/3.5-TESTING/html/Tools/Offline.html
+ M /rt/branches/3.5-TESTING/html/Tools/Reports/CreatedByDates.html
+ M /rt/branches/3.5-TESTING/html/Tools/Reports/ResolvedByDates.html
+ M /rt/branches/3.5-TESTING/html/User/Elements/DelegateRights
+ M /rt/branches/3.5-TESTING/html/User/Groups/Members.html
+ M /rt/branches/3.5-TESTING/html/User/Groups/Modify.html
+ M /rt/branches/3.5-TESTING/html/User/Groups/index.html
+ M /rt/branches/3.5-TESTING/html/User/Prefs.html
+ M /rt/branches/3.5-TESTING/html/Widgets/SelectionBox
+ M /rt/branches/3.5-TESTING/html/index.html
+
+* apply tidy_html script
+------------------------------------------------------------------------
+r4371 | trs | 2006-01-08 12:27:50 -0500 (Sun, 08 Jan 2006) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/titlebox.css
+
+ r8179@wintermute: tom | 2006-01-08 12:20:56 -0500
+ * Fixed rendering issues in Opera and (I think) Safari/Shiira/any Webkit browser
+ * Fixed background of login box issue in IE
+
+------------------------------------------------------------------------
+r4313 | jesse | 2005-12-15 15:09:00 -0500 (Thu, 15 Dec 2005) | 20 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+
+ r20493@truegrounds: jesse | 2005-12-15 15:07:44 -0500
+ On Thu, Dec 15, 2005 at 01:49:55PM -0600, Roedel, Mark wrote:
+ >
+ > The attached patch (to html/Ticket/Elements/Reminders from RT-3.5.6)
+ > corrects the following behaviors:
+ >
+ > (1) When maintaining reminders from /Ticket/Display.html, all reminder
+ > subjects for that ticket were being reset to blank when "Save" was
+ > pressed, and
+ >
+ > (2) When maintaining reminders from /Ticket/Reminders.html, only one
+ > field per reminder would actually be updated when "Save" was pressed
+ >
+ >
+ > --
+ > Mark Roedel
+ > Web Programmer / Analyst
+ > LeTourneau University
+
+
+------------------------------------------------------------------------
+r4252 | trs | 2005-12-05 18:29:26 -0500 (Mon, 05 Dec 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+
+ r6730@wintermute: tom | 2005-12-05 18:28:43 -0500
+ Fix
+
+------------------------------------------------------------------------
+r4251 | trs | 2005-12-05 16:54:40 -0500 (Mon, 05 Dec 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r6558@wintermute: tom | 2005-11-24 09:00:09 -0500
+ Aren't used for hackish CSS calcs anymore
+
+------------------------------------------------------------------------
+r4228 | jesse | 2005-12-02 18:43:54 -0500 (Fri, 02 Dec 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ D /rt/branches/3.5-TESTING/html/NoAuth/printrt.css
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r19706@truegrounds: jesse | 2005-12-02 18:36:02 -0500
+ * merge fixups, 3.5.6
+
+------------------------------------------------------------------------
+r4227 | jesse | 2005-12-02 18:43:35 -0500 (Fri, 02 Dec 2005) | 18 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/html/NoAuth/printrt.css
+ M /rt/branches/3.5-TESTING/html/Search/Build.html
+
+ r19702@truegrounds: jesse | 2005-12-02 18:16:21 -0500
+ r19698@truegrounds: jesse | 2005-12-02 18:00:25 -0500
+ r19693@truegrounds: jesse | 2005-12-02 17:48:24 -0500
+ r19588@truegrounds: jesse | 2005-11-30 16:00:10 -0500
+ * Bump to 3.4.5rc1
+ r19674@truegrounds: jesse | 2005-12-01 23:13:50 -0500
+ * Added a print stylesheet from Koos van den Hout
+ r19688@truegrounds: jesse | 2005-12-02 17:01:28 -0500
+ RT-Ticket: 6962
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Rolf Grossmann to fix some bogosity in the query builder
+
+
+
+
+
+------------------------------------------------------------------------
+r4218 | jesse | 2005-12-02 17:41:33 -0500 (Fri, 02 Dec 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+
+ r19690@truegrounds: jesse | 2005-12-02 17:39:24 -0500
+ * Merge fixups
+
+------------------------------------------------------------------------
+r4217 | jesse | 2005-12-02 17:41:00 -0500 (Fri, 02 Dec 2005) | 326 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/README
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+ M /rt/branches/3.5-TESTING/html/Elements/Callback
+ M /rt/branches/3.5-TESTING/html/Elements/CollectionAsTable/Row
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomField
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Elements/QueryString
+ M /rt/branches/3.5-TESTING/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/3.5-TESTING/html/Elements/ScrubHTML
+ M /rt/branches/3.5-TESTING/html/Elements/ShowCustomFields
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/3.5-TESTING/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/CustomFieldValues.pm
+ M /rt/branches/3.5-TESTING/lib/RT/EmailParser.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Groups_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Email.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Principal_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Users_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/06mailgateway.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/07acl.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/07rights.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/09record_cf_api.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/12-search.t
+ A /rt/branches/3.5-TESTING/lib/t/regression/14linking.t
+ A /rt/branches/3.5-TESTING/lib/t/regression/23-batch-upload-csv.t
+ M /rt/branches/3.5-TESTING/sbin/rt-setup-database.in
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r19661@truegrounds: jesse | 2005-12-01 22:53:35 -0500
+ r19594@truegrounds: jesse | 2005-11-30 16:04:46 -0500
+ r18978@truegrounds: jesse | 2005-11-18 17:16:11 -0500
+ * Minor refactoring of CreateTickets.pm (It needs a more complete rototill)
+
+ * more flexible support for custom fields whose names contain dashes and spaces in createtickets templates
+
+
+ r19595@truegrounds: jesse | 2005-11-30 16:04:52 -0500
+ r19551@truegrounds: jesse | 2005-11-30 15:24:09 -0500
+ r19350@truegrounds (orig r4145): alexmv | 2005-11-23 16:32:25 -0500
+ r7341@zoq-fot-pik: chmrr | 2005-11-23 16:31:44 -0500
+ * Limiting based on CFs should make sure that the CFs in question are
+ on the right queue, otherwise negative searches might be wrong.
+ * Removed debugging lines from 20-sort-by-requestor.t
+
+ r19521@truegrounds (orig r4149): alexmv | 2005-11-28 15:00:53 -0500
+ r7368@zoq-fot-pik: chmrr | 2005-11-28 15:00:18 -0500
+ * Search/Build.html passes Rows not RowsPerPage -- don't hard-force
+ to 50 all the time!
+
+
+
+ r19596@truegrounds: jesse | 2005-11-30 16:05:00 -0500
+ r19553@truegrounds: jesse | 2005-11-30 15:26:01 -0500
+ r19552@truegrounds (orig r4155): alexmv | 2005-11-30 15:26:08 -0500
+ r7395@zoq-fot-pik: chmrr | 2005-11-30 15:25:32 -0500
+ * Sort by requestor tests fail on some database backends because they
+ sort nulls differently than Perl does; only compare non-nulls
+
+
+
+ r19597@truegrounds: jesse | 2005-11-30 16:05:07 -0500
+ r19554@truegrounds: jesse | 2005-11-30 15:27:53 -0500
+ r18121@truegrounds: jesse | 2005-11-02 22:40:02 -0500
+ r17958@truegrounds (orig r3989): alexmv | 2005-10-24 17:26:18 -0400
+ r6881@zoq-fot-pik: chmrr | 2005-10-24 17:25:14 -0400
+ * Ensure custom fields keep correct fallback values; for instance, if
+ "add another attachment" is clicked
+
+ r18110@truegrounds (orig r4010): pdh | 2005-10-31 19:21:57 -0500
+ Make $RT::MaxInlineBody work properly.
+
+
+ r18111@truegrounds (orig r4011): pdh | 2005-11-01 00:43:02 -0500
+ Add a missing space, before the Style Police come after me.
+
+
+
+
+
+ r19598@truegrounds: jesse | 2005-11-30 16:06:36 -0500
+ r19555@truegrounds: jesse | 2005-11-30 15:28:02 -0500
+ r18409@truegrounds: jesse | 2005-11-06 17:11:57 -0500
+ * Fix to attachment ordering when you ask for a txn's attachments.
+ (Postgres doesn't default to ordering by id, so we were getting the wrong txn content)
+
+
+ r19599@truegrounds: jesse | 2005-11-30 16:06:44 -0500
+ r19556@truegrounds: jesse | 2005-11-30 15:28:09 -0500
+ r18411@truegrounds: jesse | 2005-11-06 17:13:33 -0500
+ * Patch to significantly improve performance on "WhoHaveRight" from Ruslan.
+
+
+ r19600@truegrounds: jesse | 2005-11-30 16:06:53 -0500
+ r19557@truegrounds: jesse | 2005-11-30 15:28:15 -0500
+ r18412@truegrounds: jesse | 2005-11-06 17:13:58 -0500
+ * Bumped to 3.4.5pre1
+
+
+ r19601@truegrounds: jesse | 2005-11-30 16:07:00 -0500
+ r19558@truegrounds: jesse | 2005-11-30 15:28:23 -0500
+ r18716@truegrounds: jesse | 2005-11-11 00:10:08 -0500
+ * fix from ruslan for fallout from his WhoHaveRight refactoring
+
+
+ r19602@truegrounds: jesse | 2005-11-30 16:07:07 -0500
+ r19559@truegrounds: jesse | 2005-11-30 15:28:45 -0500
+ r18722@truegrounds: jesse | 2005-11-11 15:26:34 -0500
+ * SB 1.35 dependency
+
+
+ r19603@truegrounds: jesse | 2005-11-30 16:07:14 -0500
+ r19560@truegrounds: jesse | 2005-11-30 15:29:29 -0500
+ r18876@truegrounds: jesse | 2005-11-14 12:32:25 -0500
+ r18739@truegrounds (orig r4061): robert | 2005-11-13 00:14:57 -0500
+ r4124@bear: rspier | 2005-11-12 21:08:45 -0800
+ Undefined Warning Elimination:
+ - index.html passes in $session{'home_refresh_interval'} which can be null.
+
+ r4125@bear: rspier | 2005-11-12 21:14:28 -0800
+ Undefined Warning Elimination:
+ GetHeader will return undefined when the header doesn't exist. (This is _good_, as that is different than empty.)
+ But.. =~ warns.
+
+
+
+
+ r19604@truegrounds: jesse | 2005-11-30 16:07:22 -0500
+ r19561@truegrounds: jesse | 2005-11-30 15:29:35 -0500
+ r18877@truegrounds: jesse | 2005-11-14 12:37:37 -0500
+ RT-Ticket: 7087
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Displayed linked tickets in search results were inverted
+
+
+
+ r19605@truegrounds: jesse | 2005-11-30 16:07:28 -0500
+ r19562@truegrounds: jesse | 2005-11-30 15:29:41 -0500
+ r18880@truegrounds: jesse | 2005-11-14 12:42:48 -0500
+ RT-Ticket: 7081
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Added a note to the readme warning users to clean out the
+ mason cache on upgrades - Ruslan
+
+
+
+ r19606@truegrounds: jesse | 2005-11-30 16:07:35 -0500
+ r19563@truegrounds: jesse | 2005-11-30 15:29:47 -0500
+ r18888@truegrounds: jesse | 2005-11-14 12:54:25 -0500
+ RT-Ticket: 7048
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Akos Torok pointed out that our HTML scrubber removed "PRE" tags from HTML
+
+
+ r19607@truegrounds: jesse | 2005-11-30 16:07:42 -0500
+ r19564@truegrounds: jesse | 2005-11-30 15:29:54 -0500
+ r18892@truegrounds: jesse | 2005-11-14 13:07:15 -0500
+ r18881@truegrounds (orig r4064): alexmv | 2005-11-14 12:43:06 -0500
+ r7122@zoq-fot-pik: chmrr | 2005-11-14 12:42:37 -0500
+ * Updated russian translation from Andrew Kornilov <andy@eva.dp.ua>
+
+
+
+
+ r19608@truegrounds: jesse | 2005-11-30 16:09:07 -0500
+ r19565@truegrounds: jesse | 2005-11-30 15:30:00 -0500
+ r18893@truegrounds: jesse | 2005-11-14 13:19:52 -0500
+ RT-Ticket: 7128
+ RT-Status: resolved
+ RT-Update: correspond
+
+ A big patch from Todd Chapman (with lots of juicy tests) to optionally
+ create two transactions when you create a link. (Also, this means that we'll
+ run scrips twice). This is off by default in RT 3.4
+
+
+
+ r19609@truegrounds: jesse | 2005-11-30 16:09:14 -0500
+ r19566@truegrounds: jesse | 2005-11-30 15:30:08 -0500
+ r18895@truegrounds: jesse | 2005-11-14 13:35:29 -0500
+ RT-Ticket: 7136
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Stuart Knight reports:
+
+ As part of the "initdb" processing, the scripts went through and created a new database user, in my case called RT3.
+
+ When it came time to create the tables, the script was still logged on as the dba user "system", so all of tables/sequences were created under "system"'s schema.
+
+ I followed through the rt-setup-database script, and spotted that there was a database disconnect, followed by an immediate reconnect, as the same user. (in the case of Oracle this still being the "dba" account)
+
+ Putting an extra validation check in here for Oracle, and then connecting as the intended database user fixed up the issue.
+
+
+
+ r19610@truegrounds: jesse | 2005-11-30 16:09:20 -0500
+ r19567@truegrounds: jesse | 2005-11-30 15:30:15 -0500
+ r18897@truegrounds: jesse | 2005-11-14 13:35:44 -0500
+ r18896@truegrounds (orig r4072): alexmv | 2005-11-14 13:33:43 -0500
+ r7135@zoq-fot-pik: chmrr | 2005-11-14 13:32:23 -0500
+ RT-Ticket: 7101
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Don't modify EquivObjects arrayref, thanks to Todd Chapman
+
+
+
+
+
+ r19611@truegrounds: jesse | 2005-11-30 16:09:27 -0500
+ r19568@truegrounds: jesse | 2005-11-30 15:30:22 -0500
+ r18899@truegrounds: jesse | 2005-11-14 13:40:24 -0500
+ RT-Ticket: 7121
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Todd Chapman to make the web based acl tests honor RT::WebPath
+
+
+
+ r19612@truegrounds: jesse | 2005-11-30 16:09:34 -0500
+ r19569@truegrounds: jesse | 2005-11-30 15:37:06 -0500
+ r18900@truegrounds: jesse | 2005-11-14 13:57:34 -0500
+ RT-Ticket: 7122
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Todd Chapman to honor changed a $rtname variable when running
+ the test suite
+
+
+ r19613@truegrounds: jesse | 2005-11-30 16:09:40 -0500
+ r19570@truegrounds: jesse | 2005-11-30 15:37:12 -0500
+ r18904@truegrounds: jesse | 2005-11-14 14:49:25 -0500
+ RT-Ticket: 7105
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Updated French translation from Jerome Fenal
+
+
+ r19614@truegrounds: jesse | 2005-11-30 16:09:47 -0500
+ r19571@truegrounds: jesse | 2005-11-30 15:37:19 -0500
+ r19545@truegrounds: jesse | 2005-11-29 18:51:07 -0500
+ * A pair of new callbacks to make it easier to hide away a custom field on ticket display/edit
+
+
+ r19615@truegrounds: jesse | 2005-11-30 16:09:54 -0500
+ r19572@truegrounds: jesse | 2005-11-30 15:37:26 -0500
+ r19547@truegrounds: jesse | 2005-11-29 18:54:41 -0500
+ r18901@truegrounds (orig r4074): alexmv | 2005-11-14 13:52:00 -0500
+ r7140@zoq-fot-pik: chmrr | 2005-11-14 13:51:14 -0500
+ * Better bounce handling, from Abhijit Menon-Sen <ams@oryx.com>
+
+ r18905@truegrounds (orig r4077): alexmv | 2005-11-14 14:20:49 -0500
+ r7146@zoq-fot-pik: chmrr | 2005-11-14 14:20:03 -0500
+ RT-Ticket: 7090
+ RT-Status: resolved
+ RT-Update: correspond
+ * New Japanese .po, from Daisuke Maki <daisuke@wafu.ne.jp>
+
+ r18940@truegrounds (orig r4079): alexmv | 2005-11-14 14:52:57 -0500
+ r7148@zoq-fot-pik: chmrr | 2005-11-14 14:51:58 -0500
+ RT-Ticket: 6559
+ RT-Status: resolved
+ RT-Update: correspond
+ * Tests from Todd Chapman for loading CF from a wrong queue
+
+ r18941@truegrounds (orig r4080): alexmv | 2005-11-14 14:55:17 -0500
+ r7152@zoq-fot-pik: chmrr | 2005-11-14 14:54:43 -0500
+ * Restore rightful .po headers on new french translation
+
+ r18942@truegrounds (orig r4081): alexmv | 2005-11-14 14:59:42 -0500
+ r7155@zoq-fot-pik: chmrr | 2005-11-14 14:59:06 -0500
+ RT-Ticket: 7020
+ RT-Status: resolved
+ RT-Update: correspond
+ * Actually make use of 'style' if it is provided; thanks to Kelly
+ F. Hickel <kfh@mqsoftware.com>
+
+ r18944@truegrounds (orig r4083): alexmv | 2005-11-14 15:43:24 -0500
+ r7159@zoq-fot-pik: chmrr | 2005-11-14 15:42:48 -0500
+ RT-Ticket: 6457
+ RT-Status: resolved
+ RT-Update: correspond
+ * Typo in Ticket_Overlay.pm, found by Todd Chapman <todd@chaka.net>
+
+ r18945@truegrounds (orig r4084): alexmv | 2005-11-14 15:51:27 -0500
+ r7161@zoq-fot-pik: chmrr | 2005-11-14 15:50:56 -0500
+ RT-Ticket: 6458
+ RT-Status: resolved
+ RT-Update: correspond
+ * Removed extra return argument from _AddLink, thanks to Todd Chapman
+ <todd@chaka.net>
+
+ r18946@truegrounds (orig r4085): alexmv | 2005-11-14 16:30:12 -0500
+ r7163@zoq-fot-pik: chmrr | 2005-11-14 16:29:36 -0500
+ RT-Ticket: 6507
+ RT-Status: resolved
+ RT-Update: correspond
+ * Standardize fonts to "Verdana, Arial, Helvetica, sans-serif";
+ variant of patch from Maxime Henrion <mux@FreeBSD.org>
+
+ r18947@truegrounds (orig r4086): alexmv | 2005-11-14 16:49:33 -0500
+ r7165@zoq-fot-pik: chmrr | 2005-11-14 16:49:07 -0500
+ RT-Ticket: 7131
+ RT-Status: resolved
+ RT-Update: correspond
+ * The $RT::rtname regex should be case insensitive for matching
+ subjects; thanks to Phil Smith III <psmith@levanta.com> for the
+ catch
+
+ r18948@truegrounds (orig r4087): ruz | 2005-11-14 16:50:12 -0500
+ * fix: really hide hidden paths from callbacks
+ * fix: fetch data from the %cache by one key when store data with other
+ r18950@truegrounds (orig r4089): ruz | 2005-11-14 16:57:36 -0500
+ * revert back mysql.schema, commited by accident
+ r18951@truegrounds (orig r4090): ruz | 2005-11-14 17:02:36 -0500
+ * /Elements/QueryString now supports ARRAY refs, this allow us to handle
+ multiple arguments with the same name, this behaviour is consistent with
+ how HTML::Mason handle arguments
+ r18953@truegrounds (orig r4092): alexmv | 2005-11-14 17:35:40 -0500
+ r7175@zoq-fot-pik: chmrr | 2005-11-14 17:35:03 -0500
+ RT-Ticket: 7010
+ RT-Status: resolved
+ RT-Update: correspond
+ * Treat our email addresses as case-insensitive
+
+ r18957@truegrounds (orig r4096): alexmv | 2005-11-14 18:34:44 -0500
+ r7182@zoq-fot-pik: chmrr | 2005-11-14 18:34:13 -0500
+ RT-Ticket: 6994
+ RT-Status: resolved
+ RT-Update: correspond
+ * Sort custom vield values by SortOrder, then *Name*, then id; patch
+ from Troy Davis <troy@nack.net>
+
+ r18992@truegrounds (orig r4120): robert | 2005-11-19 22:52:28 -0500
+ r4186@bear: rspier | 2005-11-19 19:51:38 -0800
+ typo fix: s/load/Load/
+
+
+
+
+ r19616@truegrounds: jesse | 2005-11-30 16:11:33 -0500
+ * Reminders typo
+
+
+------------------------------------------------------------------------
+r4144 | trs | 2005-11-23 15:57:32 -0500 (Wed, 23 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Tabs
+
+ r6548@wintermute: tom | 2005-11-23 15:56:59 -0500
+ Minor nit: fixed jumbo link
+
+------------------------------------------------------------------------
+r4143 | trs | 2005-11-23 15:39:13 -0500 (Wed, 23 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_SiteConfig.pm
+
+ r6546@wintermute: tom | 2005-11-23 15:38:39 -0500
+ My mistake, sorry
+
+------------------------------------------------------------------------
+r4142 | trs | 2005-11-23 15:33:25 -0500 (Wed, 23 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/User/Prefs.html
+
+ r6512@wintermute: tom | 2005-11-23 15:23:50 -0500
+ Added a few callbacks to make extending Prefs cleaner
+
+------------------------------------------------------------------------
+r4141 | trs | 2005-11-23 15:32:54 -0500 (Wed, 23 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_SiteConfig.pm
+ M /rt/branches/3.5-TESTING/html/User/Prefs.html
+
+ r6511@wintermute: tom | 2005-11-23 13:49:29 -0500
+ Minor nit: unneeded <br>
+
+------------------------------------------------------------------------
+r4131 | ruz | 2005-11-21 22:54:02 -0500 (Mon, 21 Nov 2005) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/Elements/SelectTimeUnits
+ M /rt/branches/3.5-TESTING/html/Search/Elements/PickBasics
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditBasics
+ M /rt/branches/3.5-TESTING/html/Ticket/Update.html
+
+ r1332@cubic-pc (orig r4124): ruz | 2005-11-21 22:30:53 +0300
+ r1329@cubic-pc: cubic | 2005-11-21 17:38:22 +0300
+ * Name sufix -TimeUnits is optional in Elements/SelectTimeUnits
+ * get rid of sufix in the callers
+
+
+------------------------------------------------------------------------
+r4130 | ruz | 2005-11-21 22:52:05 -0500 (Mon, 21 Nov 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/autohandler
+
+ r1331@cubic-pc (orig r4123): ruz | 2005-11-21 22:30:40 +0300
+ r1328@cubic-pc: cubic | 2005-11-21 17:29:03 +0300
+ * fix time units handling, '1/8' didn't work
+ * we don't filter args with grep to filter them again in loop
+ * tidy and code paths cleanup
+
+
+------------------------------------------------------------------------
+r4129 | trs | 2005-11-21 19:59:58 -0500 (Mon, 21 Nov 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/Elements/QueueSummary
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/login.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/main.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/misc.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/quickbar.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/titlebox.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/titlebox.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Reminders.html
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxEnd
+
+ r6509@wintermute: tom | 2005-11-21 19:58:59 -0500
+ * Couple of merge/typo bug fixes
+ * Improved 3.4-compat styles, still need IE testing though
+
+------------------------------------------------------------------------
+r4128 | alexmv | 2005-11-21 16:59:46 -0500 (Mon, 21 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Elements/PickBasics
+
+ r7297@zoq-fot-pik: chmrr | 2005-11-21 16:59:01 -0500
+ * Removed duplicated line
+
+------------------------------------------------------------------------
+r4127 | trs | 2005-11-21 16:53:31 -0500 (Mon, 21 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r6505@wintermute: tom | 2005-11-21 16:51:50 -0500
+ Overzealous s/document\.getElementById/$/g
+
+------------------------------------------------------------------------
+r4126 | trs | 2005-11-21 16:11:32 -0500 (Mon, 21 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r6501@wintermute: tom | 2005-11-21 16:10:38 -0500
+ Better compat. at the suggestion of Jesse
+
+------------------------------------------------------------------------
+r4125 | trs | 2005-11-21 15:58:42 -0500 (Mon, 21 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r6486@wintermute: tom | 2005-11-21 15:35:10 -0500
+ Utility function
+
+------------------------------------------------------------------------
+r4119 | trs | 2005-11-18 19:39:43 -0500 (Fri, 18 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav.css
+
+ r7023@wintermute: tom | 2005-11-18 19:35:25 -0500
+ Better menu style, should elimination menu wrapping
+
+------------------------------------------------------------------------
+r4095 | trs | 2005-11-14 18:30:46 -0500 (Mon, 14 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowHistory
+
+ r6955@wintermute: tom | 2005-11-14 18:29:51 -0500
+ Note about (ab)use
+
+------------------------------------------------------------------------
+r4094 | trs | 2005-11-14 18:30:41 -0500 (Mon, 14 Nov 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/History.html
+ M /rt/branches/3.5-TESTING/html/Admin/Users/History.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowHistory
+
+ r6954@wintermute: tom | 2005-11-14 18:16:02 -0500
+ * Cleaned up logic in ShowHistory
+ * Fixed params in the Group/User histories
+
+------------------------------------------------------------------------
+r4093 | alexmv | 2005-11-14 17:58:13 -0500 (Mon, 14 Nov 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r7177@zoq-fot-pik: chmrr | 2005-11-14 17:46:20 -0500
+ RT-Ticket: 7063
+ RT-Status: resolved
+ RT-Update: correspond
+ * Ability to shell out to outside program to install deps; variant of
+ patch from Ruz.
+
+------------------------------------------------------------------------
+r4091 | trs | 2005-11-14 17:24:24 -0500 (Mon, 14 Nov 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ D /rt/branches/3.5-TESTING/html/CalPopup.html
+ D /rt/branches/3.5-TESTING/html/NoAuth/cascaded.js
+ D /rt/branches/3.5-TESTING/html/NoAuth/class.js
+ D /rt/branches/3.5-TESTING/html/NoAuth/combobox.js
+ D /rt/branches/3.5-TESTING/html/NoAuth/list.js
+ D /rt/branches/3.5-TESTING/html/rt.js
+
+ r6952@wintermute: tom | 2005-11-14 17:23:00 -0500
+ Deleting old remanents from a merge bug
+
+------------------------------------------------------------------------
+r4088 | alexmv | 2005-11-14 16:57:12 -0500 (Mon, 14 Nov 2005) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+
+ r7169@zoq-fot-pik: chmrr | 2005-11-14 16:55:38 -0500
+ RT-Ticket: 7106
+ RT-Status: resolved
+ RT-Update: correspond
+ * Updated french translation from Jerome Fenal <jfenal@gmail.com>
+
+------------------------------------------------------------------------
+r4082 | alexmv | 2005-11-14 15:15:00 -0500 (Mon, 14 Nov 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/SelfService/Elements/Tabs
+
+ r7157@zoq-fot-pik: chmrr | 2005-11-14 15:14:13 -0500
+ RT-Ticket: 6762
+ RT-Status: resolved
+ RT-Update: correspond
+ * "Create" in SelfService goes straight to the only queue if they
+ only have one; thanks to Kenneth Marshall <ktm@it.is.rice.edu>
+
+------------------------------------------------------------------------
+r4070 | jesse | 2005-11-14 13:04:05 -0500 (Mon, 14 Nov 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/titlebox.css
+
+ r18890@truegrounds: jesse | 2005-11-14 13:02:38 -0500
+ RT-Ticket: 7053
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * IE titlebox style cleanup from Peter Popovics
+
+------------------------------------------------------------------------
+r4068 | jesse | 2005-11-14 12:54:13 -0500 (Mon, 14 Nov 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r18884@truegrounds: jesse | 2005-11-14 12:51:29 -0500
+ RT-Ticket: 7056
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch from Peter Popovics to improve calendar popup browser compatibility
+
+------------------------------------------------------------------------
+r4067 | jesse | 2005-11-14 12:54:01 -0500 (Mon, 14 Nov 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Prefs/Quicksearch.html
+
+ r18883@truegrounds: jesse | 2005-11-14 12:46:30 -0500
+ RT-Ticket: 7082
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Patch to show queue descriptions in quicksearch preferences from
+ Joby Walker
+
+
+------------------------------------------------------------------------
+r3979 | ruz | 2005-10-19 16:57:11 -0400 (Wed, 19 Oct 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r1156@cubic-pc (orig r3977): trs | 2005-10-19 02:22:43 +0400
+ r6654@wintermute: tom | 2005-10-18 08:44:56 -0400
+ Fixed up the 3.4-compat styles to account for HTML and CSS changes
+
+ r1157@cubic-pc (orig r3978): trs | 2005-10-19 02:22:47 +0400
+ r6655@wintermute: tom | 2005-10-18 08:53:18 -0400
+ Bolded ticket values to match 3.4
+
+
+------------------------------------------------------------------------
+r3978 | trs | 2005-10-18 18:22:47 -0400 (Tue, 18 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/main.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/ticket.css
+
+ r6655@wintermute: tom | 2005-10-18 08:53:18 -0400
+ Bolded ticket values to match 3.4
+
+------------------------------------------------------------------------
+r3977 | trs | 2005-10-18 18:22:43 -0400 (Tue, 18 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/main.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/nav.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/quickbar.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/titlebox.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/transactions.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/quickbar.css
+
+ r6654@wintermute: tom | 2005-10-18 08:44:56 -0400
+ Fixed up the 3.4-compat styles to account for HTML and CSS changes
+
+------------------------------------------------------------------------
+r3973 | jesse | 2005-10-14 23:57:52 -0400 (Fri, 14 Oct 2005) | 70 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/User/Elements/Tabs
+ M /rt/branches/3.5-TESTING/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/12-search.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/13-attribute-tests.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/20-sort-by-requestor.t
+
+ r17383@hualien: jesse | 2005-10-14 16:12:53 -0400
+ * perltidy
+ r17385@hualien: jesse | 2005-10-14 16:33:19 -0400
+ r17373@hualien: jesse | 2005-10-14 15:34:42 -0400
+ r17359@hualien: jesse | 2005-10-14 15:21:10 -0400
+ * Perltidy
+
+
+ r17386@hualien: jesse | 2005-10-14 16:33:41 -0400
+ r17374@hualien: jesse | 2005-10-14 15:34:43 -0400
+ r17361@hualien: jesse | 2005-10-14 15:22:39 -0400
+ r17358@hualien: jesse | 2005-10-14 15:06:26 -0400
+ r17219@hualien (orig r3938): robert | 2005-10-07 00:20:15 -0400
+ r3995@bear: rspier | 2005-10-06 21:19:24 -0700
+ [fsck.com #7067] - If we can't find a customfield that the user is allowed to see on a ticket, don't return any values, (when specifying a custom field)
+
+ r17275@hualien (orig r3944): ruz | 2005-10-10 15:27:36 -0400
+ backport of the 3.5-TESTING@3943
+ Changes
+ * fix for search by owner's fields, now owner is WATCHERFIELD instead of ENUM
+ * added backward compatible variant for Owner, next searches should work
+ ** Owner = '<id>'
+ ** Owner != '<id>'
+ ** Owner = '<name>'
+ ** Owner != '<name>'
+ ** for other operators or if subfield(subkey) is specified search works
+ as for other watchers
+ * Fix for searches like "Cc.Name <> 'SomeBody'", was skipping tickets
+ with empty Cc list.
+ * get rid of some unint warnings
+ * test suite for all corner cases
+
+ r17276@hualien (orig r3945): ruz | 2005-10-10 15:47:29 -0400
+ backport of the 3.5-TESTING@3543
+ Changes:
+ * fix attachments ordering
+
+ r17313@hualien (orig r3948): ruz | 2005-10-10 20:01:50 -0400
+ * get rid of "not a number" warning
+ r17339@hualien (orig r3957): ruz | 2005-10-13 08:37:47 -0400
+ * code comments
+ r17340@hualien (orig r3958): ruz | 2005-10-13 08:40:24 -0400
+ * new callback in html/User/Elements/Tabs
+
+ r17360@hualien: jesse | 2005-10-14 15:21:46 -0400
+ * Perltidy
+
+
+
+ r17387@hualien: jesse | 2005-10-14 16:34:33 -0400
+ r17375@hualien: jesse | 2005-10-14 15:34:45 -0400
+ r17362@hualien: jesse | 2005-10-14 15:56:53 -0400
+ * Merge fixups
+
+
+ r17388@hualien: jesse | 2005-10-14 16:34:35 -0400
+ r17376@hualien: jesse | 2005-10-14 15:34:47 -0400
+ r17363@hualien: jesse | 2005-10-14 15:57:06 -0400
+ * Todo test no longer failing
+
+
+ r17389@hualien: jesse | 2005-10-14 16:34:36 -0400
+ r17377@hualien: jesse | 2005-10-14 15:34:48 -0400
+ r17371@hualien: jesse | 2005-10-14 17:13:07 -0400
+ Pull up from 3.4
+
+
+ r17390@hualien: jesse | 2005-10-14 23:45:35 -0400
+ * Tickets_Overlay.pm merging; untodoed passing tests
+
+------------------------------------------------------------------------
+r3954 | trs | 2005-10-12 19:43:40 -0400 (Wed, 12 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav.css
+
+ r6574@wintermute: tom | 2005-10-12 13:45:46 -0400
+ Fix stacked menu spacing in IE
+
+------------------------------------------------------------------------
+r3950 | trs | 2005-10-11 20:38:54 -0400 (Tue, 11 Oct 2005) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/Elements/Logo
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/header.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/logo.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/main.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/quickbar.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/images/bplogo.gif
+
+ r6571@wintermute: tom | 2005-10-11 20:28:25 -0400
+ Reorganized the whole page header to squash some bugs and make it function
+ better. Lots of CSS cleanups and the stacked menus should generally behave
+ now. There is a very minor, cosmetic spacing issue in IE, but I'll address
+ that later. I also moved the logo div out of the quickbar and on its own.
+
+------------------------------------------------------------------------
+r3949 | trs | 2005-10-11 20:38:49 -0400 (Tue, 11 Oct 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/main.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav-left.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/quickbar.css
+
+ r6570@wintermute: tom | 2005-10-10 21:07:35 -0400
+ Removed the old right-style menu CSS and made the left-style the default
+ so it's easier to fiddle with.
+
+------------------------------------------------------------------------
+r3943 | ruz | 2005-10-10 13:15:15 -0400 (Mon, 10 Oct 2005) | 16 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/20-sort-by-requestor.t
+ A /rt/branches/3.5-TESTING/lib/t/regression/22search_tix_by_watcher.t
+
+Changes
+* fix for ordering by watcher's fields(workaround)
+* fix for search by owner's fields, now owner is WATCHERFIELD instead of ENUM
+* added backward compatible variant for Owner, next searches should work
+** Owner = '<id>'
+** Owner != '<id>'
+** Owner = '<name>'
+** Owner != '<name>'
+** for other operators or if subfield(subkey) is specified search works
+ as for other watchers
+* Fix for searches like "Cc.Name <> 'SomeBody'", was skipping tickets
+ with empty Cc list.
+* get rid of some unint warnings
+* test suite for all corner cases
+* one TODO test block
+
+------------------------------------------------------------------------
+r3942 | ruz | 2005-10-07 12:40:57 -0400 (Fri, 07 Oct 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/Action/SendEmail.pm
+
+* get rid of uninit warnings
+------------------------------------------------------------------------
+r3936 | ruz | 2005-10-05 21:52:51 -0400 (Wed, 05 Oct 2005) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/t/regression/04send_email.t
+
+* added some util functions in .t file:
+ first_txn, count_xns, first_attach and count_attachs
+ this functions should be moved later into standalone
+ test helper file
+* use simple file_content function instead of `cat`
+
+------------------------------------------------------------------------
+r3935 | ruz | 2005-10-05 21:39:58 -0400 (Wed, 05 Oct 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+ M /rt/branches/3.5-TESTING/lib/RT.pm.in
+
+* new config option LogStackTraces
+------------------------------------------------------------------------
+r3934 | ruz | 2005-10-05 20:41:56 -0400 (Wed, 05 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/t/regression/03web_compiliation_errors.t
+
+* tests cleanup, print diag messages only if we $ENV{TEST_VERBOSE} is true
+ prove -v set this
+* print diag message with URL we test
+------------------------------------------------------------------------
+r3933 | ruz | 2005-10-05 20:26:21 -0400 (Wed, 05 Oct 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r1101@cubic-pc (orig r3931): svm | 2005-10-06 04:25:58 +0400
+ SVM: initializing mirror for /mirrors/branches/3.5-TESTING
+ r1102@cubic-pc (orig r3932): jesse | 2005-10-05 23:45:05 +0400
+ * Alex points out that the PO merges are wrong
+
+
+
+------------------------------------------------------------------------
+r3932 | jesse | 2005-10-05 15:45:05 -0400 (Wed, 05 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/cs.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/da.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/en.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/es.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fi.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/he.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/hu.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/nl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/no.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_tw.po
+
+* Alex points out that the PO merges are wrong
+
+
+------------------------------------------------------------------------
+r3931 | jesse | 2005-10-05 15:25:32 -0400 (Wed, 05 Oct 2005) | 51 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.5-TESTING/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/3.5-TESTING/html/Search/Elements/BuildFormatString
+ M /rt/branches/3.5-TESTING/html/Search/Results.tsv
+ M /rt/branches/3.5-TESTING/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/EmailParser.pm
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/cs.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/da.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/en.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/es.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fi.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/he.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/hu.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/nl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/no.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_tw.po
+ M /rt/branches/3.5-TESTING/lib/RT/Link_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Queue_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Transactions_Overlay.pm
+
+ r17089@hualien: jesse | 2005-10-05 11:11:26 -0400
+ r17072@hualien: jesse | 2005-10-05 10:39:48 -0400
+ r17059@hualien: jesse | 2005-10-05 10:08:39 -0400
+ r17052@hualien: jesse | 2005-10-05 09:37:42 -0400
+ r15958@hualien (orig r3877): alexmv | 2005-09-22 15:09:22 -0400
+ r6458@zoq-fot-pik: chmrr | 2005-09-22 15:08:37 -0400
+ * Add where the faulty caller was in deprecated warnings
+
+ r16168@hualien (orig r3892): robert | 2005-09-28 12:16:03 -0400
+ r3945@bear: rspier | 2005-09-28 09:15:08 -0700
+ Performance Improvement when Sending Email using sendmailpipe -
+
+ MIME::Entity would bog down in certain cases because of it's use of IO::Scalar during stringification. MIME::Entity will be switching to IO::ScalarArray, which will help... but RT was causing it to store into a temporary string anyway, which was silly.
+
+ This change has MIME::Entity write directly to the pipe, which is a lot more efficient. Seems to cut out ~33% of user time. (Because we don't need to have a temporary IO::Scalar thingy around.) Also will reduce peak memory usage.
+
+
+ r16169@hualien (orig r3893): jesse | 2005-09-28 13:27:29 -0400
+ Switch from ->CustomFields to ->TicketCustomFields to stop using a deprecated API.
+ Thanks to T.J. Maciak
+
+ r17038@hualien (orig r3894): alexmv | 2005-09-30 15:19:46 -0400
+ r6554@zoq-fot-pik: chmrr | 2005-09-30 15:16:47 -0400
+ * Remove unused and deprecated code path (bugs 6605, 7008)
+
+ r17039@hualien (orig r3895): alexmv | 2005-09-30 15:19:57 -0400
+ r6555@zoq-fot-pik: chmrr | 2005-09-30 15:18:22 -0400
+ * Link to the *other* end of the link, not the one that is us
+
+ r17040@hualien (orig r3896): alexmv | 2005-09-30 15:56:31 -0400
+ r6558@zoq-fot-pik: chmrr | 2005-09-30 15:56:06 -0400
+ RT-Ticket: 7029
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied missing limit for AdminCcs, from Todd Chapman
+
+
+ r17044@hualien (orig r3900): alexmv | 2005-10-03 13:32:45 -0400
+ r6566@zoq-fot-pik: chmrr | 2005-10-03 13:28:24 -0400
+ * Updated spanish translation, thanks to Carlos Velasco
+
+ r17045@hualien (orig r3901): alexmv | 2005-10-03 14:15:35 -0400
+ r6568@zoq-fot-pik: chmrr | 2005-10-03 14:14:49 -0400
+ * Header fixes in PO files to include correct RT version
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3930 | jesse | 2005-10-05 15:25:11 -0400 (Wed, 05 Oct 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Results.rdf
+
+ r17088@hualien: jesse | 2005-10-05 11:11:20 -0400
+ r17071@hualien: jesse | 2005-10-05 10:39:37 -0400
+ r17057@hualien: jesse | 2005-10-05 10:08:11 -0400
+ r15770@hualien: jesse | 2005-09-16 12:23:15 -0400
+ * The RSS feeds should come with a default subject, as feeds really want to have article titles in some clients
+
+
+
+
+------------------------------------------------------------------------
+r3929 | jesse | 2005-10-05 15:24:46 -0400 (Wed, 05 Oct 2005) | 25 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Build.html
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+
+ r17087@hualien: jesse | 2005-10-05 11:09:10 -0400
+ r17070@hualien: jesse | 2005-10-05 10:39:31 -0400
+ r17056@hualien: jesse | 2005-10-05 10:08:04 -0400
+ r15752@hualien: jesse | 2005-09-15 11:41:41 -0400
+ r14236@hualien (orig r3754): robert | 2005-09-01 17:47:36 -0400
+ r3800@bear: rspier | 2005-09-01 14:46:59 -0700
+ RT-Ticket: 6986
+ RT-Status: resolved
+ RT-Update: correspond
+
+ If we didn't generate any SQL, don't pass it to FromSQL which will reset the dirty flag and then SB won't actually run anything.
+
+ Also, tests.
+
+
+ r15713@hualien (orig r3847): glasser | 2005-09-12 18:11:43 -0400
+ r41532@maclaurin-seven-twelve: glasser | 2005-09-12 18:04:55 -0400
+ Defining subs in Mason components is dangerous, since they clash with subs defined
+ in every other component.
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3928 | jesse | 2005-10-05 15:24:28 -0400 (Wed, 05 Oct 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/ACE_Overlay.pm
+
+ r17086@hualien: jesse | 2005-10-05 11:09:03 -0400
+ r17069@hualien: jesse | 2005-10-05 10:39:25 -0400
+ r17055@hualien: jesse | 2005-10-05 10:07:58 -0400
+ r15749@hualien: jesse | 2005-09-15 11:14:56 -0400
+ * It was possible to get into an infinite loop when removing a member from a group
+
+
+
+
+------------------------------------------------------------------------
+r3927 | jesse | 2005-10-05 15:24:11 -0400 (Wed, 05 Oct 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+
+ r17085@hualien: jesse | 2005-10-05 11:08:57 -0400
+ r17068@hualien: jesse | 2005-10-05 10:39:19 -0400
+ r17054@hualien: jesse | 2005-10-05 10:07:50 -0400
+ r15723@hualien: jesse | 2005-09-13 12:05:40 -0400
+ * When pulling data out of the database, we need to be more careful
+ about whether it's utf8 or not. Thanks to Ruslan Zakirov
+
+
+
+
+
+------------------------------------------------------------------------
+r3926 | jesse | 2005-10-05 15:23:50 -0400 (Wed, 05 Oct 2005) | 21 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+ M /rt/branches/3.5-TESTING/html/autohandler
+ M /rt/branches/3.5-TESTING/lib/RT.pm.in
+ M /rt/branches/3.5-TESTING/lib/t/regression/20-sort-by-requestor.t
+
+ r17084@hualien: jesse | 2005-10-05 11:08:50 -0400
+ r17067@hualien: jesse | 2005-10-05 10:39:13 -0400
+ r17053@hualien: jesse | 2005-10-05 10:00:45 -0400
+ r15946@hualien (orig r3872): alexmv | 2005-09-22 12:38:17 -0400
+ r6181@zoq-fot-pik: chmrr | 2005-09-02 12:09:41 -0400
+ * Additional tests for no requestor (should still sort correctly)
+
+ r15947@hualien (orig r3873): alexmv | 2005-09-22 12:38:34 -0400
+ r6451@zoq-fot-pik: chmrr | 2005-09-22 12:37:30 -0400
+ * Statement logging
+
+ r17047@hualien (orig r3903): alexmv | 2005-10-03 15:57:38 -0400
+ r6572@zoq-fot-pik: chmrr | 2005-10-03 15:47:52 -0400
+ * Make test text reflect the number of tickets the test is looking for
+
+ r17048@hualien (orig r3904): alexmv | 2005-10-03 15:57:45 -0400
+
+
+
+
+
+------------------------------------------------------------------------
+r3925 | jesse | 2005-10-05 15:23:30 -0400 (Wed, 05 Oct 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Transaction_Overlay.pm
+
+ r17083@hualien: jesse | 2005-10-05 11:08:43 -0400
+ r17066@hualien: jesse | 2005-10-05 10:39:06 -0400
+ r15969@hualien: jesse | 2005-09-23 15:36:13 -0400
+ * Algorithm for picking transaction content didn't have a reasonable enough fallback case.
+
+
+
+------------------------------------------------------------------------
+r3924 | jesse | 2005-10-05 15:23:07 -0400 (Wed, 05 Oct 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Action/CreateTickets.pm
+
+ r17082@hualien: jesse | 2005-10-05 11:08:36 -0400
+ r17065@hualien: jesse | 2005-10-05 10:39:00 -0400
+ r15942@hualien: jesse | 2005-09-21 23:48:22 -0400
+ * CreateTickets now handles custom fields
+
+
+
+------------------------------------------------------------------------
+r3923 | jesse | 2005-10-05 15:22:37 -0400 (Wed, 05 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r15936@hualien: jesse | 2005-09-21 15:36:00 -0400
+ Bumping to 3.5.4
+
+------------------------------------------------------------------------
+r3922 | jesse | 2005-10-05 15:19:14 -0400 (Wed, 05 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r15931@hualien: jesse | 2005-09-20 17:14:57 -0400
+ * Merge bug in "PickObjects" - Spotted by Ruslan
+
+------------------------------------------------------------------------
+r3921 | jesse | 2005-10-05 15:19:00 -0400 (Wed, 05 Oct 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r15929@hualien: jesse | 2005-09-20 15:14:40 -0400
+ * Now javascript files get processed by mason
+
+
+------------------------------------------------------------------------
+r3902 | alexmv | 2005-10-03 14:34:49 -0400 (Mon, 03 Oct 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/cs.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/da.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/en.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/es.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fi.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/he.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/hu.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/nl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/no.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_tw.po
+
+ r6570@zoq-fot-pik: chmrr | 2005-10-03 14:33:25 -0400
+ * Fix version number in PO files
+
+------------------------------------------------------------------------
+r3899 | alexmv | 2005-09-30 16:32:52 -0400 (Fri, 30 Sep 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+
+ r6564@zoq-fot-pik: chmrr | 2005-09-30 16:32:22 -0400
+ RT-Ticket: 6976
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied new french localization from Jerome Fenal <jfenal@gmail.com>
+
+------------------------------------------------------------------------
+r3898 | alexmv | 2005-09-30 16:27:36 -0400 (Fri, 30 Sep 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+
+------------------------------------------------------------------------
+r3897 | alexmv | 2005-09-30 16:27:27 -0400 (Fri, 30 Sep 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Tools/Reports/index.html
+
+ r6560@zoq-fot-pik: chmrr | 2005-09-30 16:25:00 -0400
+ RT-Ticket: 7001
+ RT-Status: resolved
+ RT-Update: comment
+
+ * Localize "Reports", from Jerome Fenal <jfenal@gmail.com>
+
+------------------------------------------------------------------------
+r3870 | robert | 2005-09-22 02:01:28 -0400 (Thu, 22 Sep 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/TicketList
+ M /rt/branches/3.5-TESTING/html/Search/Build.html
+ M /rt/branches/3.5-TESTING/html/Search/Elements/DisplayOptions
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ A /rt/branches/3.5-TESTING/lib/t/regression/23cfsort.t
+ A /rt/branches/3.5-TESTING/lib/t/regression/24pawsort.t
+
+ r3904@bear: rspier | 2005-09-21 22:27:01 -0700
+ local branch for 3.5
+ r3905@bear: rspier | 2005-09-21 23:01:01 -0700
+
+ - Sort By CustomField
+ - Sort by multiple fields in UI
+ - Ownership/PAW support
+
+ and tests for both
+
+
+------------------------------------------------------------------------
+r3867 | jesse | 2005-09-21 16:52:22 -0400 (Wed, 21 Sep 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+* merge bug fix from Joe Micciche 3.5.5
+------------------------------------------------------------------------
+r3865 | jesse | 2005-09-21 15:36:36 -0400 (Wed, 21 Sep 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r15936@hualien: jesse | 2005-09-21 15:36:00 -0400
+ Bumping to 3.5.4
+
+------------------------------------------------------------------------
+r3864 | jesse | 2005-09-20 17:16:05 -0400 (Tue, 20 Sep 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickObjects
+
+ r15931@hualien: jesse | 2005-09-20 17:14:57 -0400
+ * Merge bug in "PickObjects" - Spotted by Ruslan
+
+------------------------------------------------------------------------
+r3863 | jesse | 2005-09-20 15:15:19 -0400 (Tue, 20 Sep 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/webmux.pl.in
+
+ r15929@hualien: jesse | 2005-09-20 15:14:40 -0400
+ * Now javascript files get processed by mason
+
+
+------------------------------------------------------------------------
+r3846 | jesse | 2005-09-12 09:09:44 -0400 (Mon, 12 Sep 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditBasics
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.5-TESTING/html/Ticket/Reminders.html
+
+ r15702@hualien: jesse | 2005-09-12 09:08:37 -0400
+ * Merge bugfixes from Jeff Voskamp
+
+------------------------------------------------------------------------
+r3828 | jesse | 2005-09-02 13:05:30 -0400 (Fri, 02 Sep 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/cs.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/da.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/en.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/es.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fi.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/he.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/hu.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/nl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/no.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_tw.po
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r15163@hualien: jesse | 2005-09-02 13:04:23 -0400
+ * 3.5.3
+
+------------------------------------------------------------------------
+r3827 | jesse | 2005-09-02 13:02:04 -0400 (Fri, 02 Sep 2005) | 243 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/configure.ac
+
+ r15135@hualien: jesse | 2005-09-02 12:28:08 -0400
+ r15105@hualien: jesse | 2005-09-02 11:11:38 -0400
+ Merge forward from RT 3.4.4
+
+
+ r14193@hualien: jesse | 2005-09-01 13:40:23 -0400
+ r4892@hualien: jesse | 2005-07-18 11:47:55 -0400
+
+
+ r14194@hualien: jesse | 2005-09-01 13:41:24 -0400
+ r4894@hualien: jesse | 2005-07-18 13:44:44 -0400
+ * releng.cnf bump to rc1
+
+ r14195@hualien: jesse | 2005-09-01 13:41:32 -0400
+ r7152@hualien: jesse | 2005-07-30 11:06:46 -0400
+ * German and Danish translation header fixes.
+ besides that just a message catalog regenration
+
+
+ r14196@hualien: jesse | 2005-09-01 13:42:01 -0400
+ r7153@hualien: jesse | 2005-07-30 11:07:37 -0400
+ 3.4.3rc2
+
+ r14197@hualien: jesse | 2005-09-01 13:42:09 -0400
+ r7158@hualien: jesse | 2005-07-30 11:08:03 -0400
+ r4909@hualien (orig r3501): alexmv | 2005-07-18 15:00:38 -0400
+ r5198@zoq-fot-pik: chmrr | 2005-07-18 14:59:07 -0400
+ * Fix TransactionBatch / DESTROY bug (backport from QUEBEC)
+
+ r7124@hualien (orig r3546): kevinr | 2005-07-28 14:51:34 -0400
+ r6713@SAD-GIRL-IN-SNOW: kevinr | 2005-07-28 14:50:47 -0400
+ RT-Ticket: 6892
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied patch from Hsin-Chan Chien to fix a bug with attachment uploading
+ in SelfService mode.
+
+
+
+ r14198@hualien: jesse | 2005-09-01 13:42:19 -0400
+ r7398@hualien: jesse | 2005-08-05 15:39:10 -0400
+ r7317@hualien (orig r3576): robert | 2005-08-02 00:23:36 -0400
+ r3582@woof: rspier | 2005-08-01 21:20:12 -0700
+ allow arbitrary https?: urls in the menus
+
+ r7333@hualien (orig r3579): kevinr | 2005-08-02 14:22:27 -0400
+ r6837@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 14:20:51 -0400
+ RT-Ticket: 6897
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Incorporated the updated Italian translation from Angelo Turetta
+
+ r7335@hualien (orig r3581): glasser | 2005-08-02 15:08:03 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r7336@hualien (orig r3582): glasser | 2005-08-02 15:10:05 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r7366@hualien (orig r3585): kevinr | 2005-08-02 20:18:54 -0400
+ r6849@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 20:17:38 -0400
+ * POD formatting fixes
+
+ r7370@hualien (orig r3588): kevinr | 2005-08-03 01:20:35 -0400
+ r6868@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:19:47 -0400
+ * Fixed an outdated bit of POD
+
+ r7371@hualien (orig r3589): kevinr | 2005-08-03 01:51:55 -0400
+ r6872@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:51:33 -0400
+ * More minor perldoc cleanup
+
+
+
+ r14199@hualien: jesse | 2005-09-01 13:42:33 -0400
+ r7399@hualien: jesse | 2005-08-05 19:16:58 -0400
+ * Cleaned up searching by ticket or txn date.
+
+ r14200@hualien: jesse | 2005-09-01 13:42:43 -0400
+ r7634@hualien: jesse | 2005-08-10 15:25:31 -0400
+ * This is 3.4.3
+
+
+ r14201@hualien: jesse | 2005-09-01 13:42:52 -0400
+ r12938@hualien: jesse | 2005-08-11 13:46:14 -0400
+ * French localization had lost its header
+
+
+ r14202@hualien: jesse | 2005-09-01 13:43:02 -0400
+ r13154@hualien: jesse | 2005-08-17 19:14:26 -0400
+ * Fixes to standalone webserver for mason 1.30
+
+ r14203@hualien: jesse | 2005-09-01 13:43:10 -0400
+ r13155@hualien: jesse | 2005-08-17 19:15:11 -0400
+ * Note dependency on a current HSSM
+
+ r14204@hualien: jesse | 2005-09-01 13:43:19 -0400
+ r13164@hualien: jesse | 2005-08-17 22:03:19 -0400
+ r13131@hualien (orig r3662): alexmv | 2005-08-17 13:29:25 -0400
+ r5914@zoq-fot-pik: chmrr | 2005-08-17 13:32:00 -0400
+ * We love escaping! We need to escape quotes and slashes in
+ TicketSQL, then URI escape, then HTML escape. And the query comes
+ from the TicketSQL-escaped one, not the URI and HTML-escaped one. Oh,
+ yeah, and don't forget to turn off the automatic HTML escaping that
+ Mason does, or it'll be TicketSQL-HTML-URI-HTML escaped, which is
+ Right Out.
+
+ r13132@hualien (orig r3663): alexmv | 2005-08-17 13:29:32 -0400
+
+ r13163@hualien (orig r3666): alexmv | 2005-08-17 13:41:38 -0400
+ r5920@zoq-fot-pik: chmrr | 2005-08-17 13:44:59 -0400
+ * My memory was faulty; HTML escaping is not needed
+
+
+
+ r14205@hualien: jesse | 2005-09-01 13:46:31 -0400
+ r13699@hualien: jesse | 2005-08-22 14:46:07 -0400
+ r13255@hualien (orig r3700): trs | 2005-08-18 22:39:02 -0400
+ r6002@wintermute: tom | 2005-08-18 22:37:53 -0400
+ Fixed typo
+
+
+
+ r14206@hualien: jesse | 2005-09-01 13:46:41 -0400
+ r13720@hualien: jesse | 2005-08-23 17:54:29 -0400
+ * Bugfixes to the German translation file
+
+ r14207@hualien: jesse | 2005-09-01 13:46:51 -0400
+ r13723@hualien: jesse | 2005-08-23 17:55:47 -0400
+ r13701@hualien (orig r3706): glasser | 2005-08-22 15:55:57 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r13721@hualien (orig r3707): glasser | 2005-08-22 16:04:25 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r13722@hualien (orig r3708): glasser | 2005-08-22 16:05:46 -0400
+ r40177@tin-foil: glasser | 2005-08-18 18:49:13 -0400
+ Updates to the standalone server to not dupe as much code, but may be rolled back if HSSM needs to be.
+
+
+
+ r14208@hualien: jesse | 2005-09-01 13:47:00 -0400
+ r13725@hualien: jesse | 2005-08-23 22:10:42 -0400
+ * Added support for mod_perl 2.0
+
+ r14209@hualien: jesse | 2005-09-01 13:47:09 -0400
+ r13728@hualien: jesse | 2005-08-23 22:37:12 -0400
+ * Bumped to 3.4.4pre1
+
+ r14210@hualien: jesse | 2005-09-01 13:47:17 -0400
+ r13732@hualien: jesse | 2005-08-23 23:38:28 -0400
+ * Applied tom's callbacks fix
+
+
+ r14211@hualien: jesse | 2005-09-01 13:47:26 -0400
+ r13733@hualien: jesse | 2005-08-23 23:38:39 -0400
+ * bumped to pre2
+
+ r14212@hualien: jesse | 2005-09-01 13:47:35 -0400
+ r13743@hualien: jesse | 2005-08-24 02:00:08 -0400
+ RT-Ticket: 6957
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from ams to restore his byline to bin/rt
+
+ r14213@hualien: jesse | 2005-09-01 13:47:43 -0400
+ r13753@hualien: jesse | 2005-08-25 14:53:06 -0400
+ * Removing warnings about mod_perl2
+
+ r14214@hualien: jesse | 2005-09-01 13:47:53 -0400
+ r13754@hualien: jesse | 2005-08-25 14:55:32 -0400
+ * EquivObjects support for acl queries, from Todd Chapman
+
+
+ r14215@hualien: jesse | 2005-09-01 13:48:01 -0400
+ r13757@hualien: jesse | 2005-08-25 15:23:18 -0400
+ RT-Ticket: 6934
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * A couple of patches to improve how localization extraction works from jfenal
+
+
+ r14216@hualien: jesse | 2005-09-01 13:48:11 -0400
+ r13759@hualien: jesse | 2005-08-25 15:31:56 -0400
+ * Fixes to IsRTAddress processing. Spotted by Travis Campbell.
+
+
+ r14217@hualien: jesse | 2005-09-01 13:48:19 -0400
+ r13760@hualien: jesse | 2005-08-25 15:51:57 -0400
+ RT-Ticket: 6855
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from Andreas Jakum to improve RT::Ticket->Import's handling
+ of watchers
+
+
+ r14218@hualien: jesse | 2005-09-01 13:48:28 -0400
+ r13763@hualien: jesse | 2005-08-25 16:04:29 -0400
+ * 3.4.4rc3
+
+ r14219@hualien: jesse | 2005-09-01 13:48:37 -0400
+ r13804@hualien: jesse | 2005-08-27 21:25:50 -0400
+ * Updated French translation from jfenal
+
+
+ r14220@hualien: jesse | 2005-09-01 13:48:47 -0400
+ r13812@hualien: jesse | 2005-08-28 15:43:29 -0400
+ * bumping to 3.4.4
+
+ r14221@hualien: jesse | 2005-09-01 13:48:56 -0400
+ r14192@hualien: jesse | 2005-09-01 13:37:34 -0400
+ r14130@hualien (orig r3739): robert | 2005-08-31 16:46:16 -0400
+ r3748@woof: rspier | 2005-08-31 13:41:53 -0700
+ Check for invalid character (-) in mysql database names and prevent RT from allowing it to be configured.
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3826 | jesse | 2005-09-02 13:01:17 -0400 (Fri, 02 Sep 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Footer
+ M /rt/branches/3.5-TESTING/html/Elements/MyRT
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+
+ r14191@hualien: jesse | 2005-09-01 12:35:09 -0400
+ * HTML merge bugs fixed
+
+------------------------------------------------------------------------
+r3825 | jesse | 2005-09-02 13:00:29 -0400 (Fri, 02 Sep 2005) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Action/CreateTickets.pm
+
+ r14111@hualien: jesse | 2005-08-30 06:18:30 -0400
+ r14084@hualien: jesse | 2005-08-30 05:57:58 -0400
+ r13741@hualien: jesse | 2005-08-24 01:28:59 -0400
+ * Added support for named custom fields in templates
+
+
+
+
+------------------------------------------------------------------------
+r3824 | jesse | 2005-09-02 12:59:46 -0400 (Fri, 02 Sep 2005) | 12 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomField
+
+ r14110@hualien: jesse | 2005-08-30 06:18:20 -0400
+ r14083@hualien: jesse | 2005-08-30 05:57:49 -0400
+ r13553@hualien: jesse | 2005-08-19 10:42:36 -0400
+ r13127@hualien (orig r3658): alexmv | 2005-08-15 18:38:56 -0400
+ r5873@zoq-fot-pik: chmrr | 2005-08-15 18:42:04 -0400
+ * Require that the object not only be defined but also valid, to pull
+ values from it
+
+
+
+
+
+------------------------------------------------------------------------
+r3823 | jesse | 2005-09-02 12:59:05 -0400 (Fri, 02 Sep 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/webmux.pl.in
+
+ r14109@hualien: jesse | 2005-08-30 06:18:10 -0400
+ r14082@hualien: jesse | 2005-08-30 05:57:40 -0400
+ r13552@hualien: jesse | 2005-08-19 10:42:22 -0400
+ r12966@hualien (orig r3649): alexmv | 2005-08-12 16:30:55 -0400
+ r5827@zoq-fot-pik: chmrr | 2005-08-12 16:33:16 -0400
+ * Revert r3597
+
+
+
+
+
+------------------------------------------------------------------------
+r3822 | jesse | 2005-09-02 12:58:21 -0400 (Fri, 02 Sep 2005) | 13 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+
+ r14108@hualien: jesse | 2005-08-30 06:18:00 -0400
+ r14081@hualien: jesse | 2005-08-30 05:57:29 -0400
+ r13551@hualien: jesse | 2005-08-19 10:42:07 -0400
+ r12965@hualien (orig r3648): alexmv | 2005-08-12 16:30:49 -0400
+ r5826@zoq-fot-pik: chmrr | 2005-08-12 16:29:24 -0400
+ * r3602 didn't go far enough; be happy we are using DBIx::SB::Unique,
+ because we got us a lot of possible duplice rows with all of these
+ left joins.
+
+
+
+
+
+------------------------------------------------------------------------
+r3821 | jesse | 2005-09-02 12:57:39 -0400 (Fri, 02 Sep 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditSearches
+
+ r14107@hualien: jesse | 2005-08-30 06:17:42 -0400
+ r14079@hualien: jesse | 2005-08-30 05:57:09 -0400
+ r13549@hualien: jesse | 2005-08-19 10:41:40 -0400
+ r7397@hualien (orig r3607): alexmv | 2005-08-05 14:59:21 -0400
+ r5653@zoq-fot-pik: chmrr | 2005-08-05 14:57:28 -0400
+ * Display "save" even if search isn't dirty (can be a rename)
+
+
+
+
+
+------------------------------------------------------------------------
+r3820 | jesse | 2005-09-02 12:56:48 -0400 (Fri, 02 Sep 2005) | 12 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+ M /rt/branches/3.5-TESTING/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/3.5-TESTING/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+
+ r14106@hualien: jesse | 2005-08-30 06:17:32 -0400
+ r14078@hualien: jesse | 2005-08-30 05:56:57 -0400
+ r13548@hualien: jesse | 2005-08-19 10:41:25 -0400
+ r7393@hualien (orig r3603): alexmv | 2005-08-04 16:11:05 -0400
+ r5647@zoq-fot-pik: chmrr | 2005-08-04 16:04:03 -0400
+ * Add $RT::WikiImplicitLinks option (defaults to false)
+ * Support for Wiki links doing useful things
+
+
+
+
+
+------------------------------------------------------------------------
+r3819 | jesse | 2005-09-02 12:56:00 -0400 (Fri, 02 Sep 2005) | 12 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay_SQL.pm
+
+ r14105@hualien: jesse | 2005-08-30 06:17:22 -0400
+ r14077@hualien: jesse | 2005-08-30 05:56:46 -0400
+ r13547@hualien: jesse | 2005-08-19 10:41:11 -0400
+ r7392@hualien (orig r3602): alexmv | 2005-08-04 13:41:33 -0400
+ r5644@zoq-fot-pik: chmrr | 2005-08-04 13:39:54 -0400
+ * Replace %FIELDS (which has pseudohash meaning) with %FIELD_METADATA
+ * Make join for sorting watchers be a left join
+
+
+
+
+
+------------------------------------------------------------------------
+r3818 | jesse | 2005-09-02 12:55:10 -0400 (Fri, 02 Sep 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTime
+
+ r14104@hualien: jesse | 2005-08-30 06:17:13 -0400
+ r14076@hualien: jesse | 2005-08-30 05:56:38 -0400
+ r13546@hualien: jesse | 2005-08-19 10:40:57 -0400
+ r7391@hualien (orig r3601): alexmv | 2005-08-04 13:41:11 -0400
+ r5643@zoq-fot-pik: chmrr | 2005-08-04 13:38:31 -0400
+ * Don't display "days" -- often conntes work days, not 24-hour periods
+
+
+
+
+
+------------------------------------------------------------------------
+r3817 | jesse | 2005-09-02 12:54:26 -0400 (Fri, 02 Sep 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowBasics
+ A /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTime
+
+ r14103@hualien: jesse | 2005-08-30 06:17:02 -0400
+ r14075@hualien: jesse | 2005-08-30 05:56:28 -0400
+ r13545@hualien: jesse | 2005-08-19 10:40:41 -0400
+ r7388@hualien (orig r3598): alexmv | 2005-08-03 18:39:17 -0400
+ r5629@zoq-fot-pik: chmrr | 2005-08-03 18:37:50 -0400
+ * Show time in hours or days when needed
+
+
+
+
+
+------------------------------------------------------------------------
+r3816 | jesse | 2005-09-02 12:53:45 -0400 (Fri, 02 Sep 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/webmux.pl.in
+
+ r14102@hualien: jesse | 2005-08-30 06:16:53 -0400
+ r14074@hualien: jesse | 2005-08-30 05:55:48 -0400
+ r13544@hualien: jesse | 2005-08-19 10:40:27 -0400
+ r7387@hualien (orig r3597): alexmv | 2005-08-03 18:08:02 -0400
+ r5627@zoq-fot-pik: chmrr | 2005-08-03 18:00:41 -0400
+ * Attempt to fix WebExternalAuth problem with images
+
+
+
+
+
+------------------------------------------------------------------------
+r3815 | jesse | 2005-09-02 12:53:03 -0400 (Fri, 02 Sep 2005) | 12 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+
+ r14101@hualien: jesse | 2005-08-30 06:16:43 -0400
+ r14073@hualien: jesse | 2005-08-30 05:55:38 -0400
+ r13543@hualien: jesse | 2005-08-19 10:40:13 -0400
+ r7386@hualien (orig r3596): alexmv | 2005-08-03 17:48:18 -0400
+ r5615@zoq-fot-pik: chmrr | 2005-08-03 17:46:45 -0400
+ * Cutsom field values in RT::Tickets are always on RT::Ticket objects
+ * Remove CSS that was causing <span class="label"> to be small, unused CSS
+
+
+
+
+
+------------------------------------------------------------------------
+r3814 | jesse | 2005-09-02 12:51:43 -0400 (Fri, 02 Sep 2005) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+
+ r14099@hualien: jesse | 2005-08-30 06:15:59 -0400
+ r14071@hualien: jesse | 2005-08-30 05:52:51 -0400
+ r13804@hualien: jesse | 2005-08-27 21:25:50 -0400
+ * Updated French translation from jfenal
+
+
+
+
+------------------------------------------------------------------------
+r3813 | jesse | 2005-09-02 12:50:35 -0400 (Fri, 02 Sep 2005) | 13 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+ r14097@hualien: jesse | 2005-08-30 06:15:28 -0400
+ r14069@hualien: jesse | 2005-08-30 05:52:32 -0400
+ r13760@hualien: jesse | 2005-08-25 15:51:57 -0400
+ RT-Ticket: 6855
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from Andreas Jakum to improve RT::Ticket->Import's handling
+ of watchers
+
+
+
+
+------------------------------------------------------------------------
+r3812 | jesse | 2005-09-02 12:49:53 -0400 (Fri, 02 Sep 2005) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/EmailParser.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Email.pm
+
+ r14096@hualien: jesse | 2005-08-30 06:15:19 -0400
+ r14068@hualien: jesse | 2005-08-30 05:52:24 -0400
+ r13759@hualien: jesse | 2005-08-25 15:31:56 -0400
+ * Fixes to IsRTAddress processing. Spotted by Travis Campbell.
+
+
+
+
+------------------------------------------------------------------------
+r3811 | jesse | 2005-09-02 12:49:07 -0400 (Fri, 02 Sep 2005) | 12 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Footer
+
+ r14095@hualien: jesse | 2005-08-30 06:14:21 -0400
+ r14067@hualien: jesse | 2005-08-30 05:52:15 -0400
+ r13757@hualien: jesse | 2005-08-25 15:23:18 -0400
+ RT-Ticket: 6934
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * A couple of patches to improve how localization extraction works from jfenal
+
+
+
+
+------------------------------------------------------------------------
+r3810 | jesse | 2005-09-02 12:48:26 -0400 (Fri, 02 Sep 2005) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Groups_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Users_Overlay.pm
+
+ r14094@hualien: jesse | 2005-08-30 06:14:12 -0400
+ r14066@hualien: jesse | 2005-08-30 05:52:07 -0400
+ r13754@hualien: jesse | 2005-08-25 14:55:32 -0400
+ * EquivObjects support for acl queries, from Todd Chapman
+
+
+
+
+------------------------------------------------------------------------
+r3809 | jesse | 2005-09-02 12:47:45 -0400 (Fri, 02 Sep 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r14093@hualien: jesse | 2005-08-30 06:14:01 -0400
+ r14065@hualien: jesse | 2005-08-30 05:51:59 -0400
+ r13753@hualien: jesse | 2005-08-25 14:53:06 -0400
+ * Removing warnings about mod_perl2
+
+
+
+------------------------------------------------------------------------
+r3808 | jesse | 2005-09-02 12:43:47 -0400 (Fri, 02 Sep 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/etc/upgrade/QUEBEC
+ A /rt/branches/3.5-TESTING/etc/upgrade/QUEBEC/content
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickObjects
+ A /rt/branches/3.5-TESTING/html/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/Elements/MyRT
+ A /rt/branches/3.5-TESTING/html/NoAuth/cascaded.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/class.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/combobox.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/list.js
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Search/Elements/PickBasics
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditBasics
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.5-TESTING/html/Ticket/Reminders.html
+ M /rt/branches/3.5-TESTING/html/Tools/Reports/index.html
+ A /rt/branches/3.5-TESTING/html/rt.js
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+
+ r14085@hualien: jesse | 2005-08-30 05:59:38 -0400
+ r14057@hualien: jesse | 2005-08-30 05:48:48 -0400
+ r13699@hualien: jesse | 2005-08-22 14:46:07 -0400
+ r13255@hualien (orig r3700): trs | 2005-08-18 22:39:02 -0400
+ r6002@wintermute: tom | 2005-08-18 22:37:53 -0400
+ Fixed typo
+
+
+
+
+
+------------------------------------------------------------------------
+r3807 | jesse | 2005-09-02 12:43:09 -0400 (Fri, 02 Sep 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+
+ r13752@hualien: jesse | 2005-08-24 16:23:43 -0400
+ r13743@hualien: jesse | 2005-08-24 02:00:08 -0400
+ RT-Ticket: 6957
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied a patch from ams to restore his byline to bin/rt
+
+
+------------------------------------------------------------------------
+r3806 | jesse | 2005-09-02 12:42:06 -0400 (Fri, 02 Sep 2005) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Callback
+
+ r13750@hualien: jesse | 2005-08-24 16:21:25 -0400
+ r13732@hualien: jesse | 2005-08-23 23:38:28 -0400
+ * Applied tom's callbacks fix
+
+
+
+------------------------------------------------------------------------
+r3805 | jesse | 2005-09-02 12:41:09 -0400 (Fri, 02 Sep 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/README
+ M /rt/branches/3.5-TESTING/bin/webmux.pl.in
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Web/Handler.pm
+
+ r13748@hualien: jesse | 2005-08-24 16:19:08 -0400
+ r13725@hualien: jesse | 2005-08-23 22:10:42 -0400
+ * Added support for mod_perl 2.0
+
+
+------------------------------------------------------------------------
+r3804 | jesse | 2005-09-02 12:40:29 -0400 (Fri, 02 Sep 2005) | 25 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Web/Standalone.pm
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r13747@hualien: jesse | 2005-08-24 16:18:51 -0400
+ r13723@hualien: jesse | 2005-08-23 17:55:47 -0400
+ r13701@hualien (orig r3706): glasser | 2005-08-22 15:55:57 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r13721@hualien (orig r3707): glasser | 2005-08-22 16:04:25 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r13722@hualien (orig r3708): glasser | 2005-08-22 16:05:46 -0400
+ r40177@tin-foil: glasser | 2005-08-18 18:49:13 -0400
+ Updates to the standalone server to not dupe as much code, but may be rolled back if HSSM needs to be.
+
+
+
+
+------------------------------------------------------------------------
+r3803 | jesse | 2005-09-02 12:39:47 -0400 (Fri, 02 Sep 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+
+ r13746@hualien: jesse | 2005-08-24 16:18:34 -0400
+ r13720@hualien: jesse | 2005-08-23 17:54:29 -0400
+ * Bugfixes to the German translation file
+
+
+------------------------------------------------------------------------
+r3802 | jesse | 2005-09-02 12:38:43 -0400 (Fri, 02 Sep 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+
+
+------------------------------------------------------------------------
+r3801 | jesse | 2005-09-02 12:37:40 -0400 (Fri, 02 Sep 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+
+
+------------------------------------------------------------------------
+r3702 | trs | 2005-08-18 22:39:31 -0400 (Thu, 18 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Queue_Overlay.pm
+
+ r6005@wintermute: tom | 2005-08-18 22:38:38 -0400
+ Fixed typo
+
+------------------------------------------------------------------------
+r3701 | trs | 2005-08-18 22:39:27 -0400 (Thu, 18 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickObjects
+ D /rt/branches/3.5-TESTING/html/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+ D /rt/branches/3.5-TESTING/html/NoAuth/cascaded.js
+ D /rt/branches/3.5-TESTING/html/NoAuth/combobox.js
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+ D /rt/branches/3.5-TESTING/html/rt.js
+
+ r6001@wintermute: tom | 2005-08-18 22:19:32 -0400
+ Fixed merge bugs from r3685
+
+------------------------------------------------------------------------
+r3694 | jesse | 2005-08-18 17:55:41 -0400 (Thu, 18 Aug 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Build.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+
+ r13221@hualien: jesse | 2005-08-18 15:44:15 -0400
+ r13208@hualien: jesse | 2005-08-18 14:39:15 -0400
+ r7377@hualien: jesse | 2005-08-03 15:11:21 -0400
+
+
+
+
+------------------------------------------------------------------------
+r3693 | jesse | 2005-08-18 17:55:19 -0400 (Thu, 18 Aug 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r13220@hualien: jesse | 2005-08-18 15:43:58 -0400
+ r13205@hualien: jesse | 2005-08-18 14:27:56 -0400
+ r13155@hualien: jesse | 2005-08-17 19:15:11 -0400
+ * Note dependency on a current HSSM
+
+
+
+------------------------------------------------------------------------
+r3692 | jesse | 2005-08-18 17:54:57 -0400 (Thu, 18 Aug 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Web/Standalone.pm
+
+ r13219@hualien: jesse | 2005-08-18 15:43:47 -0400
+ r13204@hualien: jesse | 2005-08-18 14:27:47 -0400
+ r13154@hualien: jesse | 2005-08-17 19:14:26 -0400
+ * Fixes to standalone webserver for mason 1.30
+
+
+
+------------------------------------------------------------------------
+r3691 | jesse | 2005-08-18 17:54:33 -0400 (Thu, 18 Aug 2005) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+
+ r13218@hualien: jesse | 2005-08-18 15:43:37 -0400
+ r13203@hualien: jesse | 2005-08-18 14:27:36 -0400
+ r12938@hualien: jesse | 2005-08-11 13:46:14 -0400
+ * French localization had lost its header
+
+
+
+
+------------------------------------------------------------------------
+r3690 | jesse | 2005-08-18 17:53:13 -0400 (Thu, 18 Aug 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ A /rt/branches/3.5-TESTING/lib/t/regression/22search_tix_by_txn.t
+
+ r13216@hualien: jesse | 2005-08-18 15:43:06 -0400
+ r13201@hualien: jesse | 2005-08-18 14:27:20 -0400
+ r7399@hualien: jesse | 2005-08-05 19:16:58 -0400
+ * Cleaned up searching by ticket or txn date.
+
+
+
+------------------------------------------------------------------------
+r3689 | jesse | 2005-08-18 17:52:33 -0400 (Thu, 18 Aug 2005) | 47 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Base.pm
+ M /rt/branches/3.5-TESTING/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/EmailParser.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Group_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Groups_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Handle.pm
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Email.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Queue_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+ M /rt/branches/3.5-TESTING/lib/RT/SearchBuilder.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Template_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/User_Overlay.pm
+
+ r13215@hualien: jesse | 2005-08-18 15:42:24 -0400
+ r13200@hualien: jesse | 2005-08-18 14:27:07 -0400
+ r7398@hualien: jesse | 2005-08-05 15:39:10 -0400
+ r7317@hualien (orig r3576): robert | 2005-08-02 00:23:36 -0400
+ r3582@woof: rspier | 2005-08-01 21:20:12 -0700
+ allow arbitrary https?: urls in the menus
+
+ r7333@hualien (orig r3579): kevinr | 2005-08-02 14:22:27 -0400
+ r6837@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 14:20:51 -0400
+ RT-Ticket: 6897
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Incorporated the updated Italian translation from Angelo Turetta
+
+ r7335@hualien (orig r3581): glasser | 2005-08-02 15:08:03 -0400
+ r36571@tin-foil: glasser | 2005-07-08 16:53:26 -0400
+ Fix from Tom Yu; if the Handle will give us a server-side function to lowercase
+ values, use that.
+
+ r7336@hualien (orig r3582): glasser | 2005-08-02 15:10:05 -0400
+ r38371@tin-foil: glasser | 2005-08-02 15:05:25 -0400
+ RT-Ticket: 6899
+ RT-Status: resolved
+ RT-Update: correspond
+
+ When limiting based on a whole date, make sure you're not being pushed to
+ another if you happen to be east of GMT.
+
+ Patch by Ruslan Zakirov, problem found by Olivier Horec.
+
+ r7366@hualien (orig r3585): kevinr | 2005-08-02 20:18:54 -0400
+ r6849@SAD-GIRL-IN-SNOW: kevinr | 2005-08-02 20:17:38 -0400
+ * POD formatting fixes
+
+ r7370@hualien (orig r3588): kevinr | 2005-08-03 01:20:35 -0400
+ r6868@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:19:47 -0400
+ * Fixed an outdated bit of POD
+
+ r7371@hualien (orig r3589): kevinr | 2005-08-03 01:51:55 -0400
+ r6872@SAD-GIRL-IN-SNOW: kevinr | 2005-08-03 01:51:33 -0400
+ * More minor perldoc cleanup
+
+
+
+
+
+------------------------------------------------------------------------
+r3688 | jesse | 2005-08-18 17:52:10 -0400 (Thu, 18 Aug 2005) | 20 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/SelfService/Display.html
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+ r13214@hualien: jesse | 2005-08-18 15:42:14 -0400
+ r13199@hualien: jesse | 2005-08-18 14:26:56 -0400
+ r7158@hualien: jesse | 2005-07-30 11:08:03 -0400
+ r4909@hualien (orig r3501): alexmv | 2005-07-18 15:00:38 -0400
+ r5198@zoq-fot-pik: chmrr | 2005-07-18 14:59:07 -0400
+ * Fix TransactionBatch / DESTROY bug (backport from QUEBEC)
+
+ r7124@hualien (orig r3546): kevinr | 2005-07-28 14:51:34 -0400
+ r6713@SAD-GIRL-IN-SNOW: kevinr | 2005-07-28 14:50:47 -0400
+ RT-Ticket: 6892
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Applied patch from Hsin-Chan Chien to fix a bug with attachment uploading
+ in SelfService mode.
+
+
+
+
+
+------------------------------------------------------------------------
+r3687 | jesse | 2005-08-18 17:48:44 -0400 (Thu, 18 Aug 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/cs.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/da.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/es.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fi.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/he.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/hu.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/nl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/no.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_tw.po
+
+ r13212@hualien: jesse | 2005-08-18 15:41:30 -0400
+ r13197@hualien: jesse | 2005-08-18 14:23:55 -0400
+ r7152@hualien: jesse | 2005-07-30 11:06:46 -0400
+ * German and Danish translation header fixes.
+ besides that just a message catalog regenration
+
+
+
+
+------------------------------------------------------------------------
+r3686 | jesse | 2005-08-18 17:46:53 -0400 (Thu, 18 Aug 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt.in
+ M /rt/branches/3.5-TESTING/html/Admin/Users/Modify.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Display.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowHistory
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/3.5-TESTING/lib/RT/ACE_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Action/Generic.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Action/SendEmail.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField.pm
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Email.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Web.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Principal_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+ M /rt/branches/3.5-TESTING/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Template_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/User_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT.pm.in
+ M /rt/branches/3.5-TESTING/lib/t/regression/06mailgateway.t
+ A /rt/branches/3.5-TESTING/lib/t/regression/07rights.t
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r13210@hualien: jesse | 2005-08-18 15:36:15 -0400
+ r13195@hualien: jesse | 2005-08-18 14:19:57 -0400
+ r4892@hualien: jesse | 2005-07-18 11:47:55 -0400
+
+
+
+
+------------------------------------------------------------------------
+r3685 | jesse | 2005-08-18 17:46:36 -0400 (Thu, 18 Aug 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickObjects
+ A /rt/branches/3.5-TESTING/html/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.5-TESTING/html/Elements/ValidateCustomFields
+ A /rt/branches/3.5-TESTING/html/NoAuth/cascaded.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/combobox.js
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+ A /rt/branches/3.5-TESTING/html/rt.js
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/15cf_pattern.t
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r13209@hualien: jesse | 2005-08-18 14:56:02 -0400
+ r13194@hualien: jesse | 2005-08-18 14:18:36 -0400
+
+
+
+------------------------------------------------------------------------
+r3660 | trs | 2005-08-16 10:39:31 -0400 (Tue, 16 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowMessageStanza
+
+ r5899@wintermute: tom | 2005-08-16 10:37:44 -0400
+ Fixed line breaking regex. (Alex pointed out it's brokenness a few days ago.)
+
+------------------------------------------------------------------------
+r3647 | trs | 2005-08-11 23:55:21 -0400 (Thu, 11 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+
+ r5846@wintermute: tom | 2005-08-11 23:54:44 -0400
+ Added callbacks before and after form so we can use them
+
+------------------------------------------------------------------------
+r3646 | trs | 2005-08-11 23:12:35 -0400 (Thu, 11 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+
+ r5835@wintermute: tom | 2005-08-11 23:09:53 -0400
+ Cleanup of code to determine form action and actually use $goto
+
+------------------------------------------------------------------------
+r3639 | alexmv | 2005-08-11 13:02:03 -0400 (Thu, 11 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Transaction_Overlay.pm
+
+ r5794@zoq-fot-pik: chmrr | 2005-08-11 13:02:43 -0400
+ * Now, with more localization!
+
+------------------------------------------------------------------------
+r3638 | alexmv | 2005-08-11 12:47:08 -0400 (Thu, 11 Aug 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Transaction_Overlay.pm
+
+ r5792@zoq-fot-pik: chmrr | 2005-08-11 12:47:05 -0400
+ * Pet peeve -- actually quote things the way sane people do. I fixed
+ this in 3.0, but it didn't get ported forward, so here it is again.
+
+------------------------------------------------------------------------
+r3621 | trs | 2005-08-08 16:03:35 -0400 (Mon, 08 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ D /rt/branches/3.5-TESTING/html/NoAuth/common.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/webrt.css
+
+ r5735@wintermute: tom | 2005-08-08 15:58:35 -0400
+ Obsolete
+
+------------------------------------------------------------------------
+r3620 | trs | 2005-08-08 11:54:11 -0400 (Mon, 08 Aug 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/transactions.css
+
+ r5719@wintermute: tom | 2005-08-08 11:48:30 -0400
+ Different styles to try and fix the "disjointed history" problem...
+
+ I left the striping in, but I'm not sure I like it.
+
+------------------------------------------------------------------------
+r3619 | trs | 2005-08-07 11:49:20 -0400 (Sun, 07 Aug 2005) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowMessageHeaders
+
+ r5714@wintermute: tom | 2005-08-07 11:46:58 -0400
+ * Fixed parsing of message headers (though maybe we shoud
+ use an established module to do this?)
+
+ * Cleaned up the component a little
+
+------------------------------------------------------------------------
+r3618 | trs | 2005-08-06 19:55:38 -0400 (Sat, 06 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+
+ r5712@wintermute: tom | 2005-08-06 19:55:22 -0400
+ Fixed empty menu bug
+
+------------------------------------------------------------------------
+r3617 | trs | 2005-08-06 19:48:42 -0400 (Sat, 06 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/misc.css
+
+ r5710@wintermute: tom | 2005-08-06 19:48:25 -0400
+ Eliminate extra margin whitespace around action results list
+
+------------------------------------------------------------------------
+r3616 | trs | 2005-08-06 19:40:33 -0400 (Sat, 06 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/main.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/ticket.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+
+ r5708@wintermute: tom | 2005-08-06 19:39:04 -0400
+ Styles for the CF values for a ticket
+
+------------------------------------------------------------------------
+r3615 | trs | 2005-08-06 19:25:59 -0400 (Sat, 06 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/transactions.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowMessageStanza
+
+ r5706@wintermute: tom | 2005-08-06 19:25:38 -0400
+ Fixed the problem with newlines not being output as <br /> and added some padding
+
+------------------------------------------------------------------------
+r3614 | trs | 2005-08-06 18:53:00 -0400 (Sat, 06 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/titlebox-state.js
+
+ r5704@wintermute: tom | 2005-08-06 18:51:05 -0400
+ When I made a change to the JS a while back I broke the titlebox statefulness. Now it's fixed.
+
+------------------------------------------------------------------------
+r3613 | trs | 2005-08-05 21:28:18 -0400 (Fri, 05 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/titlebox.css
+
+ r5683@wintermute: tom | 2005-08-05 21:25:46 -0400
+ Slight CSS fix
+
+------------------------------------------------------------------------
+r3609 | trs | 2005-08-05 18:00:23 -0400 (Fri, 05 Aug 2005) | 6 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/sbin/extract-message-catalog
+
+ r5667@wintermute: tom | 2005-08-05 17:56:34 -0400
+ Applied two updates from Jerome Fenal (sorry, svk doesn't like the accents):
+
+ * Patch to make extract-message-catalog play nice with svn checkouts
+ * Updated French I18N file
+
+------------------------------------------------------------------------
+r3600 | trs | 2005-08-03 23:48:43 -0400 (Wed, 03 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/titlebox.css
+
+ r5614@wintermute: tom | 2005-08-03 23:45:06 -0400
+ Include two more IDs and use better formatting
+
+------------------------------------------------------------------------
+r3599 | trs | 2005-08-03 23:48:39 -0400 (Wed, 03 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/autohandler
+
+ r5613@wintermute: tom | 2005-08-03 23:44:18 -0400
+ Nicer indenting
+
+------------------------------------------------------------------------
+r3593 | trs | 2005-08-03 13:47:45 -0400 (Wed, 03 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/Logout.html
+
+ r5597@wintermute: tom | 2005-08-03 13:42:57 -0400
+ Added callback hooks
+
+------------------------------------------------------------------------
+r3575 | robert | 2005-08-01 23:50:51 -0400 (Mon, 01 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/configure.ac
+
+ r3580@woof: rspier | 2005-08-01 20:47:59 -0700
+ SVK version of 3.5 should say 3.5.HEAD (as it does now)
+
+------------------------------------------------------------------------
+r3572 | trs | 2005-08-01 12:31:46 -0400 (Mon, 01 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+
+ r5516@wintermute: tom | 2005-08-01 12:30:56 -0400
+ Applied patch for cleaner XHTML by Steve Peters and cleaned up a few related things myself.
+
+------------------------------------------------------------------------
+r3571 | trs | 2005-08-01 12:31:41 -0400 (Mon, 01 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/index.html
+
+ r5515@wintermute: tom | 2005-08-01 12:24:42 -0400
+ Only show the Edit action if user can ModifySelf
+
+------------------------------------------------------------------------
+r3570 | trs | 2005-08-01 12:31:35 -0400 (Mon, 01 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+
+ r5512@wintermute: tom | 2005-08-01 00:30:02 -0400
+ Fixed copy-and-pasted doc
+
+------------------------------------------------------------------------
+r3569 | trs | 2005-08-01 12:31:29 -0400 (Mon, 01 Aug 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/forms.css
+
+ r5502@wintermute: tom | 2005-07-31 15:11:44 -0400
+ Small visual fix
+
+------------------------------------------------------------------------
+r3568 | trs | 2005-07-31 15:05:29 -0400 (Sun, 31 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/transactions.css
+
+ r5500@wintermute: tom | 2005-07-31 14:31:58 -0400
+ Fixed message stanza coloring off-by-one numbering
+
+------------------------------------------------------------------------
+r3567 | trs | 2005-07-31 12:27:38 -0400 (Sun, 31 Jul 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/SelectNewTicketQueue
+ M /rt/branches/3.5-TESTING/html/Elements/SelectQueue
+
+ r5491@wintermute: tom | 2005-07-31 12:25:15 -0400
+ * Cleaned up SelectQueue and added caching for performance
+ * Made SelectNewTicketQueue use SelectQueue to be standard
+
+------------------------------------------------------------------------
+r3566 | trs | 2005-07-31 12:27:33 -0400 (Sun, 31 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Simple.html
+
+ r5484@wintermute: tom | 2005-07-29 22:50:18 -0400
+ Fixed annoying lack of a space
+
+------------------------------------------------------------------------
+r3560 | trs | 2005-07-29 17:38:57 -0400 (Fri, 29 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Simple.html
+
+ r5474@wintermute: tom | 2005-07-29 17:31:53 -0400
+ Added a callback to allow modification of the query before the Googleish search gets it.
+
+------------------------------------------------------------------------
+r3543 | trs | 2005-07-27 22:10:16 -0400 (Wed, 27 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Action/SendEmail.pm
+
+ r5432@wintermute: tom | 2005-07-27 22:07:34 -0400
+ Applied patch by Ruslan Zakirov to fix attachments ordering.
+
+------------------------------------------------------------------------
+r3530 | trs | 2005-07-25 06:26:34 -0400 (Mon, 25 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/titlebox.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/transactions.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/titlebox-state.js
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r5389@wintermute: tom | 2005-07-24 23:46:32 -0400
+ * Made class "rolled-up" attached to a more semantic element
+ * Fixed styles that made transaction subjects look threaded
+ * Styled titleboxes with a border and background to separate them a little better from each other (rt-devel users noted there seemed too much whitespace and not enough distinction)
+
+------------------------------------------------------------------------
+r3529 | trs | 2005-07-25 06:26:26 -0400 (Mon, 25 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/Users/Modify.html
+
+ r5388@wintermute: tom | 2005-07-24 23:42:04 -0400
+ Fix misplaced titlebox end
+
+------------------------------------------------------------------------
+r3528 | trs | 2005-07-25 06:26:12 -0400 (Mon, 25 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Tools/Reports/index.html
+
+ r5387@wintermute: tom | 2005-07-24 23:17:54 -0400
+ Fixed to show title
+
+------------------------------------------------------------------------
+r3527 | trs | 2005-07-25 06:26:07 -0400 (Mon, 25 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r5386@wintermute: tom | 2005-07-24 22:52:11 -0400
+ Tag the last ul in the system menu with a class for planned future use in re-structuring the menu CSS
+
+------------------------------------------------------------------------
+r3526 | trs | 2005-07-25 06:26:02 -0400 (Mon, 25 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+
+ r5385@wintermute: tom | 2005-07-24 22:46:48 -0400
+ Per component CSS IDs for very specific styling
+
+------------------------------------------------------------------------
+r3525 | trs | 2005-07-25 06:25:56 -0400 (Mon, 25 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Simple.html
+
+ r5384@wintermute: tom | 2005-07-24 22:42:13 -0400
+ Fixed menu bug
+
+------------------------------------------------------------------------
+r3524 | trs | 2005-07-25 06:25:52 -0400 (Mon, 25 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/User/Prefs.html
+
+ r5351@wintermute: tom | 2005-07-22 21:50:30 -0400
+ Fixed HTML typos
+
+------------------------------------------------------------------------
+r3512 | trs | 2005-07-20 18:25:39 -0400 (Wed, 20 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/User_Overlay.pm
+
+ r5318@wintermute: tom | 2005-07-20 18:23:57 -0400
+ Two methods were duplicated. Discovery and patch by Joby Walker.
+
+------------------------------------------------------------------------
+r3510 | trs | 2005-07-20 15:27:14 -0400 (Wed, 20 Jul 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+
+------------------------------------------------------------------------
+r3509 | trs | 2005-07-20 15:27:10 -0400 (Wed, 20 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/quickbar.css
+
+ r5299@wintermute: tom | 2005-07-20 10:49:49 -0400
+ Slight spacing bug (make it less prone to wrapping)
+
+------------------------------------------------------------------------
+r3508 | trs | 2005-07-20 15:26:46 -0400 (Wed, 20 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r5180@wintermute: tom | 2005-07-18 14:11:35 -0400
+ Fix button text size
+
+------------------------------------------------------------------------
+r3504 | jesse | 2005-07-18 18:42:24 -0400 (Mon, 18 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r4913@hualien: jesse | 2005-07-18 18:41:04 -0400
+ * This is 3.5.2
+
+------------------------------------------------------------------------
+r3502 | kevinr | 2005-07-18 15:56:33 -0400 (Mon, 18 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/autohandler
+
+ r6429@SAD-GIRL-IN-SNOW: kevinr | 2005-07-18 15:33:39 -0400
+ * Rolled the functionality from RT::Extension::RedirectUnprivilegedUsers in
+ -- this sends unprivileged users who request Ticket/Display.html addresses to
+ the equivalent SelfService/Display.html address instead.
+
+------------------------------------------------------------------------
+r3500 | trs | 2005-07-18 14:14:34 -0400 (Mon, 18 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/forms.css
+
+ r5180@wintermute: tom | 2005-07-18 14:11:35 -0400
+ Fix button text size
+
+------------------------------------------------------------------------
+r3496 | trs | 2005-07-17 19:23:18 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Submit
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/nav.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/quickbar.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/forms.css
+
+ r5169@wintermute: tom | 2005-07-17 19:15:33 -0400
+ Submit bars and blue top bar
+
+------------------------------------------------------------------------
+r3495 | trs | 2005-07-17 19:23:12 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/body.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/footer.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/forms.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/main.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/nav.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/quickbar.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/titlebox.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/transactions.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTransaction
+
+ r5168@wintermute: tom | 2005-07-17 18:17:59 -0400
+ Most of the 3.4 compatibility style sheet is done. I still have to do the dark blue bar across the top and try to get the menu to extend to the page bottom. Oh, and style /Elements/Submit to match 3.4. Otherwise, I think it's a pretty good imitation.
+
+------------------------------------------------------------------------
+r3494 | trs | 2005-07-17 19:23:07 -0400 (Sun, 17 Jul 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Helpers/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/misc.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r5167@wintermute: tom | 2005-07-17 18:12:47 -0400
+ * Fixed titlebox hide/show widget state-ness and reverted back to an 'X'.
+ * Changed CalPopup.html to be a better size and have no footer
+
+------------------------------------------------------------------------
+r3493 | trs | 2005-07-17 19:22:53 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Elements/PickBasics
+
+ r5166@wintermute: tom | 2005-07-17 16:35:15 -0400
+ Alignment consistancy bug
+
+------------------------------------------------------------------------
+r3492 | trs | 2005-07-17 19:22:47 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r5165@wintermute: tom | 2005-07-17 14:53:39 -0400
+ Changes that should have been included with the addition of $WebDefaultStylesheet
+
+------------------------------------------------------------------------
+r3491 | trs | 2005-07-17 19:22:41 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r5164@wintermute: tom | 2005-07-17 14:35:04 -0400
+ Make the state of rollup links more accessible to users without CSS
+
+------------------------------------------------------------------------
+r3490 | trs | 2005-07-17 19:22:37 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+
+ r5163@wintermute: tom | 2005-07-17 14:34:21 -0400
+ New config option for the default stylesheet to use
+
+------------------------------------------------------------------------
+r3489 | trs | 2005-07-17 19:22:31 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/main.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/nav.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.4-compat/quickbar.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/approvals.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/body.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/footer.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/forms.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/header.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/login.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/main.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/misc.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav-left.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/nav.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/quickbar.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/titlebox.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/3.5-default/transactions.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/footer.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/header.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/main.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/nav-left.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/nav.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/css/transactions.css
+
+ r5162@wintermute: tom | 2005-07-17 14:31:19 -0400
+ Lots of moving CSS around
+
+------------------------------------------------------------------------
+r3488 | trs | 2005-07-17 19:22:27 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/main.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+
+ r5161@wintermute: tom | 2005-07-17 14:28:21 -0400
+ Moved CSS around into different files
+
+------------------------------------------------------------------------
+r3487 | trs | 2005-07-17 10:43:46 -0400 (Sun, 17 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/print.css
+
+ r5015@wintermute: tom | 2005-07-17 10:40:28 -0400
+ Preliminary print styles
+
+------------------------------------------------------------------------
+r3483 | trs | 2005-07-16 17:16:15 -0400 (Sat, 16 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/combobox.js
+ M /rt/branches/3.5-TESTING/html/Widgets/ComboBox
+
+ r5013@wintermute: tom | 2005-07-16 17:14:50 -0400
+ RT-Ticket: 6821
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Pulled most of the styles out of the JS and into the forms CSS. I had a few classes to the HTML.
+
+ The combo box should probably be revamped at some point to use CSS positioning instead of JS, but that's a really minor nit.
+
+------------------------------------------------------------------------
+r3482 | trs | 2005-07-16 15:38:00 -0400 (Sat, 16 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Helpers/CalPopup.html
+
+ r5011@wintermute: tom | 2005-07-16 15:34:30 -0400
+ Fixed CHALDEA bug from ticket #6846. Slightly different fix than Kevin Falcone's in the ticket.
+
+------------------------------------------------------------------------
+r3473 | trs | 2005-07-14 06:46:52 -0400 (Thu, 14 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Approvals/Display.html
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/Approve
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+
+ r4986@wintermute: tom | 2005-07-13 21:45:29 -0400
+ Styled approvals and made the approval display much more semantic
+
+------------------------------------------------------------------------
+r3462 | trs | 2005-07-11 20:05:47 -0400 (Mon, 11 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ D /rt/branches/3.5-TESTING/html/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/Elements/SelectDate
+ A /rt/branches/3.5-TESTING/html/Helpers
+ A /rt/branches/3.5-TESTING/html/Helpers/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r4983@wintermute: tom | 2005-07-11 20:02:25 -0400
+ Moved CalPopup.html around and reworked it from the HTML to the CSS to the JS to the UI wording.
+
+------------------------------------------------------------------------
+r3461 | trs | 2005-07-11 20:05:38 -0400 (Mon, 11 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r4982@wintermute: tom | 2005-07-11 19:13:21 -0400
+ Unmangling CalPopup
+
+------------------------------------------------------------------------
+r3460 | trs | 2005-07-10 23:46:28 -0400 (Sun, 10 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+
+ r4979@wintermute: tom | 2005-07-10 10:27:42 -0400
+ Fixed positioning of hide/show widget in IE6...
+
+------------------------------------------------------------------------
+r3458 | trs | 2005-07-09 22:22:14 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r4973@wintermute: tom | 2005-07-09 22:21:10 -0400
+ Added Robert's nice onload hook mechanism, though in the future we may wish to extend it to accepting anonymous functions as well.
+
+------------------------------------------------------------------------
+r3457 | trs | 2005-07-09 22:22:10 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/transactions.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowMessageStanza
+
+ r4972@wintermute: tom | 2005-07-09 21:56:29 -0400
+ Semantic message stanza coloring
+
+------------------------------------------------------------------------
+r3456 | trs | 2005-07-09 22:22:05 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/SimpleSearch
+ M /rt/branches/3.5-TESTING/html/index.html
+
+ r4971@wintermute: tom | 2005-07-09 21:28:48 -0400
+ Update the SimpleSearch form and index.html to use the true simple search in Search/Simple.html
+
+------------------------------------------------------------------------
+r3455 | trs | 2005-07-09 22:22:01 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Footer
+
+ r4970@wintermute: tom | 2005-07-09 21:26:04 -0400
+ Jesse: To do it right, you really need to localize
+
+------------------------------------------------------------------------
+r3454 | trs | 2005-07-09 22:21:58 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/transactions.css
+
+ r4969@wintermute: tom | 2005-07-09 15:19:30 -0400
+ Color changes
+
+------------------------------------------------------------------------
+r3453 | trs | 2005-07-09 14:52:41 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/transactions.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowHistory
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTransaction
+
+ r4964@wintermute: tom | 2005-07-09 14:38:55 -0400
+ A nicer looking history display, lots of cosmetic changes.
+
+------------------------------------------------------------------------
+r3452 | trs | 2005-07-09 14:52:35 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/transactions.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTransactionAttachments
+
+ r4963@wintermute: tom | 2005-07-09 13:56:31 -0400
+ Transaction style updates (spacing, alignment)
+
+------------------------------------------------------------------------
+r3451 | trs | 2005-07-09 14:52:30 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r4962@wintermute: tom | 2005-07-09 13:25:28 -0400
+ Fixed focus on username field for login form
+
+------------------------------------------------------------------------
+r3450 | trs | 2005-07-09 14:52:26 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+
+ r4961@wintermute: tom | 2005-07-09 12:43:02 -0400
+ Some formatting cleaning up
+
+------------------------------------------------------------------------
+r3449 | trs | 2005-07-09 12:12:35 -0400 (Sat, 09 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/main.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/transactions.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowHistory
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowTransaction
+
+ r4952@wintermute: tom | 2005-07-09 12:10:22 -0400
+ Transaction and ticket info colorization.
+
+ Think it's obvious enough? It's kinda to mimic how it's shown in history.
+
+------------------------------------------------------------------------
+r3448 | trs | 2005-07-09 12:12:31 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+
+ r4951@wintermute: tom | 2005-07-09 12:05:54 -0400
+ Better positioning
+
+------------------------------------------------------------------------
+r3447 | trs | 2005-07-09 12:12:27 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/nav.css
+
+ r4950@wintermute: tom | 2005-07-09 12:05:22 -0400
+ Bigger is better
+
+------------------------------------------------------------------------
+r3446 | trs | 2005-07-09 12:12:24 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+
+ r4949@wintermute: tom | 2005-07-09 08:15:02 -0400
+ #body header styles
+
+------------------------------------------------------------------------
+r3445 | trs | 2005-07-09 12:12:19 -0400 (Sat, 09 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/Elements/Error
+ M /rt/branches/3.5-TESTING/html/Elements/Footer
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+
+ r4948@wintermute: tom | 2005-07-09 07:38:05 -0400
+ Various cleanups and link styles
+
+------------------------------------------------------------------------
+r3441 | trs | 2005-07-08 14:31:51 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowHistory
+
+ r4943@wintermute: tom | 2005-07-08 14:26:53 -0400
+ Styled up the Brief/Full headers links
+
+------------------------------------------------------------------------
+r3440 | trs | 2005-07-08 14:31:44 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowRequestor
+
+ r4942@wintermute: tom | 2005-07-08 14:25:41 -0400
+ Should create the link with the normal arguments (which fixes the unable to hide/show problem)
+
+------------------------------------------------------------------------
+r3439 | trs | 2005-07-08 14:31:38 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/nav.css
+
+ r4941@wintermute: tom | 2005-07-08 13:09:27 -0400
+ IE should now have the menu arrows
+
+------------------------------------------------------------------------
+r3437 | trs | 2005-07-08 12:55:03 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/nav-left.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/nav.css
+
+ r4939@wintermute: tom | 2005-07-08 12:54:16 -0400
+ Some CSS doc for the various tricks used
+
+------------------------------------------------------------------------
+r3436 | trs | 2005-07-08 12:33:38 -0400 (Fri, 08 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ A /rt/branches/3.5-TESTING/html/Elements/Logo
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ M /rt/branches/3.5-TESTING/html/NoAuth/images/bplogo.gif
+ D /rt/branches/3.5-TESTING/html/NoAuth/images/space.gif
+
+ r4937@wintermute: tom | 2005-07-08 12:32:09 -0400
+ * Cropped a unnecessary little whitespace from the logo
+ * Got rid of an unused spacer image
+ * Pulled the logo out to a separate element since it's being used in two places now
+
+------------------------------------------------------------------------
+r3435 | trs | 2005-07-08 12:01:48 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r4934@wintermute: tom | 2005-07-08 12:00:43 -0400
+ Allow TitleBoxes to not be hideable (and make this true for the Login titleboxes)
+
+------------------------------------------------------------------------
+r3433 | trs | 2005-07-08 11:46:51 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r4930@wintermute: tom | 2005-07-08 11:26:31 -0400
+ TitleBoxes now do the right side titles as well
+
+------------------------------------------------------------------------
+r3432 | trs | 2005-07-08 11:46:45 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+
+ r4929@wintermute: tom | 2005-07-08 10:31:26 -0400
+ /Elements/Login styles fixed (haven't checked it out in IE yet, though)
+
+------------------------------------------------------------------------
+r3430 | trs | 2005-07-08 10:03:25 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditSearches
+
+ r4926@wintermute: tom | 2005-07-08 10:00:41 -0400
+ Move attribute to end of element so as not to get in the way of a test
+
+------------------------------------------------------------------------
+r3429 | trs | 2005-07-08 10:03:18 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/autohandler
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/footer.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/main.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/nav-left.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/nav.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/autohandler
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+
+ r4925@wintermute: tom | 2005-07-08 09:54:15 -0400
+ Cleanup how the content-type is set for CSS and JS
+
+------------------------------------------------------------------------
+r3428 | jesse | 2005-07-08 01:43:34 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/images/autohandler
+
+ r22398@hualien: jesse | 2005-07-08 01:37:45 -0400
+ * A regexp-based search and replace broke the autohandler that serves out images on misconfigured or downlevel platforms.
+
+------------------------------------------------------------------------
+r3427 | trs | 2005-07-08 00:40:31 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r4866@wintermute: tom | 2005-07-08 00:38:36 -0400
+ Bullet-proofed the ID generation
+
+------------------------------------------------------------------------
+r3426 | trs | 2005-07-08 00:40:27 -0400 (Fri, 08 Jul 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/titlebox-state.js
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r4858@wintermute: tom | 2005-07-08 00:14:27 -0400
+ Stateful titleboxes using JS and cookies. The generated titlebox IDs should
+ be unique.
+
+------------------------------------------------------------------------
+r3425 | trs | 2005-07-08 00:40:23 -0400 (Fri, 08 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+
+ r4857@wintermute: tom | 2005-07-08 00:03:33 -0400
+ Spacing fixes
+
+------------------------------------------------------------------------
+r3417 | trs | 2005-07-07 18:37:20 -0400 (Thu, 07 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/header.css
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r4854@wintermute: tom | 2005-07-07 18:35:26 -0400
+ Ironed out a bunch of cosmetic IE bugs...
+
+ * Width of #page-menu
+ * Rounded ends of #page-menu
+ * .button size (padding issue)
+ * Hide/show arrows on titleboxes
+
+
+------------------------------------------------------------------------
+r3415 | trs | 2005-07-07 16:24:52 -0400 (Thu, 07 Jul 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+
+ r4851@wintermute: tom | 2005-07-07 16:20:29 -0400
+ A few cosmetic bugs fixed...
+
+ * IE: #header padding-top
+ * All: /Elements/Submit padding/margin-bottom
+ * IE: Top action submit buttons + font size
+
+------------------------------------------------------------------------
+r3413 | trs | 2005-07-06 22:07:12 -0400 (Wed, 06 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+
+ r4844@wintermute: tom | 2005-07-06 22:05:41 -0400
+ The Show details/Show basics links are now in the actions/subactions menu and
+ work via Javascript and CSS (much like hideshow, actually). For users who don't
+ have JS enabled, both basics and details will be displayed (a JS onload event is
+ what hides the details at first).
+
+ I had pushed the necessary JS and CSS earlier, but forgot to commit this component
+ change.
+
+------------------------------------------------------------------------
+r3412 | trs | 2005-07-06 22:07:05 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/CreateTicket
+ M /rt/branches/3.5-TESTING/html/Elements/SimpleSearch
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditSearches
+
+ r4843@wintermute: tom | 2005-07-06 21:48:24 -0400
+ Style changes and class-ifying a few missed inputs
+
+------------------------------------------------------------------------
+r3411 | trs | 2005-07-06 22:06:47 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/People.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Users/index.html
+ M /rt/branches/3.5-TESTING/html/Elements/CreateTicket
+ M /rt/branches/3.5-TESTING/html/Elements/GotoTicket
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/Elements/QuickCreate
+ M /rt/branches/3.5-TESTING/html/Elements/RefreshHomepage
+ M /rt/branches/3.5-TESTING/html/Elements/SimpleSearch
+ M /rt/branches/3.5-TESTING/html/Elements/Submit
+ M /rt/branches/3.5-TESTING/html/Prefs/MyRT.html
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditFormat
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditQuery
+ M /rt/branches/3.5-TESTING/html/Search/Elements/EditSearches
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+ M /rt/branches/3.5-TESTING/html/Search/Simple.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Elements/GotoTicket
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditPeople
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/ShowSummary
+ M /rt/branches/3.5-TESTING/html/Ticket/Update.html
+ M /rt/branches/3.5-TESTING/html/Tools/Offline.html
+ M /rt/branches/3.5-TESTING/html/Widgets/SelectionBox
+
+ r4842@wintermute: tom | 2005-07-06 20:18:26 -0400
+ Added class="button" to all submit and reset inputs
+
+------------------------------------------------------------------------
+r3410 | trs | 2005-07-06 22:06:40 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomField
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditScrips
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditTemplates
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickCustomFields
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/PickObjects
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectRights
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/Members.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/index.html
+ M /rt/branches/3.5-TESTING/html/Admin/Users/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Users/index.html
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/3.5-TESTING/html/Elements/Checkbox
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldBinary
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldCombobox
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldImage
+ M /rt/branches/3.5-TESTING/html/Elements/EditLinks
+ M /rt/branches/3.5-TESTING/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/3.5-TESTING/html/Prefs/Quicksearch.html
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Update.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditWatchers
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/3.5-TESTING/html/Ticket/Update.html
+ M /rt/branches/3.5-TESTING/html/User/Groups/Members.html
+ M /rt/branches/3.5-TESTING/html/User/Groups/Modify.html
+
+ r4841@wintermute: tom | 2005-07-06 20:12:04 -0400
+ Added class="checkbox" to all checkbox inputs
+
+------------------------------------------------------------------------
+r3409 | trs | 2005-07-06 22:06:33 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Approvals/Elements/Approve
+ M /rt/branches/3.5-TESTING/html/Search/Elements/SelectAndOr
+
+ r4840@wintermute: tom | 2005-07-06 20:11:29 -0400
+ Added class="radio" to all radio inputs
+
+------------------------------------------------------------------------
+r3408 | trs | 2005-07-06 22:06:26 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/Objects.html
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomField
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditCustomFields
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditScrip
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditScrips
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditTemplates
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditUserComments
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/SelectRights
+ M /rt/branches/3.5-TESTING/html/Admin/Global/Template.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/GroupRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/Members.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Groups/UserRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/GroupRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/Modify.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/People.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/Template.html
+ M /rt/branches/3.5-TESTING/html/Admin/Queues/UserRights.html
+ M /rt/branches/3.5-TESTING/html/Admin/Users/Modify.html
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomField
+ M /rt/branches/3.5-TESTING/html/Elements/Login
+ M /rt/branches/3.5-TESTING/html/Elements/QuickCreate
+ M /rt/branches/3.5-TESTING/html/Prefs/SearchOptions.html
+ M /rt/branches/3.5-TESTING/html/Search/Build.html
+ M /rt/branches/3.5-TESTING/html/Search/Bulk.html
+ M /rt/branches/3.5-TESTING/html/Search/Edit.html
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Create.html
+ M /rt/branches/3.5-TESTING/html/SelfService/Update.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+ M /rt/branches/3.5-TESTING/html/Ticket/Modify.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyAll.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyDates.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyLinks.html
+ M /rt/branches/3.5-TESTING/html/Ticket/ModifyPeople.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Update.html
+ M /rt/branches/3.5-TESTING/html/User/Elements/DelegateRights
+ M /rt/branches/3.5-TESTING/html/User/Groups/Members.html
+ M /rt/branches/3.5-TESTING/html/User/Groups/Modify.html
+ M /rt/branches/3.5-TESTING/html/User/Prefs.html
+ M /rt/branches/3.5-TESTING/html/Widgets/SelectionBox
+
+ r4839@wintermute: tom | 2005-07-06 20:10:29 -0400
+ Added class="hidden" to all hidden inputs
+
+------------------------------------------------------------------------
+r3407 | trs | 2005-07-06 19:09:00 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/ListActions
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ M /rt/branches/3.5-TESTING/html/Prefs/Search.html
+
+ r4837@wintermute: tom | 2005-07-06 19:08:04 -0400
+ Miscellaneous minor changes
+
+------------------------------------------------------------------------
+r3406 | trs | 2005-07-06 18:44:22 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/rolldown-arrow.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/rolldown-arrow.png
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/rollup-arrow.gif
+ D /rt/branches/3.5-TESTING/html/NoAuth/images/css/rollup-arrow.png
+ M /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r4833@wintermute: tom | 2005-07-06 18:41:54 -0400
+ JS improvements and better TitleBox rollup/down
+
+------------------------------------------------------------------------
+r3403 | trs | 2005-07-06 12:25:08 -0400 (Wed, 06 Jul 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+
+------------------------------------------------------------------------
+r3402 | trs | 2005-07-06 12:24:59 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ D /rt/branches/3.5-TESTING/html/NoAuth/cascaded.js
+ D /rt/branches/3.5-TESTING/html/NoAuth/class.js
+ D /rt/branches/3.5-TESTING/html/NoAuth/combobox.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/cascaded.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/class.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/combobox.js
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/list.js
+ D /rt/branches/3.5-TESTING/html/NoAuth/list.js
+ M /rt/branches/3.5-TESTING/html/Widgets/ComboBox
+
+ r4824@wintermute: tom | 2005-07-06 12:20:57 -0400
+ Moved JS to a central location.
+
+------------------------------------------------------------------------
+r3401 | trs | 2005-07-06 12:24:56 -0400 (Wed, 06 Jul 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/html/NoAuth/js
+ A /rt/branches/3.5-TESTING/html/NoAuth/js/util.js
+ D /rt/branches/3.5-TESTING/html/rt.js
+
+ r4823@wintermute: tom | 2005-07-06 12:11:28 -0400
+ Moved JS to a better place (eventually we should keep all JS in the
+ same dir) and renamed it to be more descriptive.
+
+------------------------------------------------------------------------
+r3400 | jesse | 2005-07-06 12:11:13 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_SiteConfig.pm
+
+ r22316@hualien: jesse | 2005-07-06 12:10:06 -0400
+ * Cleaning up SiteConfig.pm
+
+------------------------------------------------------------------------
+r3397 | glasser | 2005-07-06 09:47:11 -0400 (Wed, 06 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_SiteConfig.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/13-attribute-tests.t
+
+ r36395@tin-foil: glasser | 2005-07-06 10:43:15 -0300
+ Clarify documentation and add tests for RT::Record->FirstAttribute (suggested by Todd Chapman)
+
+------------------------------------------------------------------------
+r3396 | trs | 2005-07-05 21:55:21 -0400 (Tue, 05 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+
+ r4811@wintermute: tom | 2005-07-05 21:50:30 -0400
+ Oops, should have included this with the previous commit
+
+------------------------------------------------------------------------
+r3395 | trs | 2005-07-05 21:55:15 -0400 (Tue, 05 Jul 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+ M /rt/branches/3.5-TESTING/html/rt.js
+
+ r4810@wintermute: tom | 2005-07-05 21:49:45 -0400
+ Fixed hideshow() to make it more generalized (I'll be using it in other
+ places than the title boxes) as well as more cross-browser.
+
+------------------------------------------------------------------------
+r3387 | trs | 2005-07-04 21:48:58 -0400 (Mon, 04 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/rt.js
+
+ r4803@wintermute: tom | 2005-07-04 21:44:58 -0400
+ Should return false so click isn't passed through after capture and page doesn't jump to the top
+
+------------------------------------------------------------------------
+r3386 | trs | 2005-07-04 21:48:54 -0400 (Mon, 04 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+
+ r4802@wintermute: tom | 2005-07-04 21:43:16 -0400
+ Put styles where they should be
+
+------------------------------------------------------------------------
+r3385 | trs | 2005-07-04 21:48:43 -0400 (Mon, 04 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r4796@wintermute: tom | 2005-07-04 16:31:28 -0400
+ Fixed conditional display of actions-menu
+
+------------------------------------------------------------------------
+r3384 | trs | 2005-07-04 21:48:40 -0400 (Mon, 04 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r4795@wintermute: tom | 2005-07-04 16:25:11 -0400
+ Proper separators in the menus
+
+------------------------------------------------------------------------
+r3383 | glasser | 2005-07-04 17:35:33 -0400 (Mon, 04 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Search/Results.html
+ M /rt/branches/3.5-TESTING/html/Search/Results.rdf
+
+ r36250@tin-foil: glasser | 2005-07-04 18:33:35 -0300
+ * RSS AutoDiscovery. (Also, put some more /> into header links, since we're
+ claiming to be XHTML. And use a more accurate Content-Type for the RSS file
+ (which probably wants to be renamed, anyway).)
+
+------------------------------------------------------------------------
+r3382 | trs | 2005-07-04 16:33:23 -0400 (Mon, 04 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r4796@wintermute: tom | 2005-07-04 16:31:28 -0400
+ Fixed conditional display of actions-menu
+
+------------------------------------------------------------------------
+r3381 | trs | 2005-07-04 16:33:18 -0400 (Mon, 04 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Tabs
+
+ r4795@wintermute: tom | 2005-07-04 16:25:11 -0400
+ Proper separators in the menus
+
+------------------------------------------------------------------------
+r3380 | glasser | 2005-07-04 15:45:57 -0400 (Mon, 04 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/sbin/rt-test-dependencies.in
+
+ r36248@tin-foil: glasser | 2005-07-04 15:51:03 -0300
+ Dependency on Calendar::Simple, from CalPopup.html
+
+------------------------------------------------------------------------
+r3377 | jesse | 2005-07-03 19:00:53 -0400 (Sun, 03 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+
+ r22231@hualien: jesse | 2005-07-03 18:53:24 -0400
+ * Merge broke the css pointer. fixed
+
+------------------------------------------------------------------------
+r3376 | jesse | 2005-07-03 19:00:45 -0400 (Sun, 03 Jul 2005) | 26 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/webmux.pl.in
+ A /rt/branches/3.5-TESTING/html/CalPopup.html
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Elements/SelectDate
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+ M /rt/branches/3.5-TESTING/html/Ticket/Reminders.html
+ A /rt/branches/3.5-TESTING/html/rt.js
+ M /rt/branches/3.5-TESTING/lib/RT/Date.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Reminders.pm
+
+ r22230@hualien: jesse | 2005-07-03 18:25:54 -0400
+ r22155@hualien: jesse | 2005-07-03 17:56:30 -0400
+ r22026@hualien (orig r3268): alexmv | 2005-07-01 00:29:51 -0400
+
+ r22027@hualien (orig r3269): alexmv | 2005-07-01 00:29:58 -0400
+
+ r22032@hualien (orig r3274): alexmv | 2005-07-01 14:48:21 -0400
+ r4600@zoq-fot-pik: chmrr | 2005-07-01 14:53:11 -0400
+ * Revert 3268
+
+ r22035@hualien (orig r3277): alexmv | 2005-07-01 16:12:56 -0400
+ r4602@zoq-fot-pik: chmrr | 2005-07-01 16:17:13 -0400
+ * Fix syntax error in html/Ticket/Elements/Reminders
+ * Fix missing 'Obj' on TicketObj in lib/RT/Reminders.pm
+ * Move JS into separate file
+ * Calendar popups
+
+ r22036@hualien (orig r3278): alexmv | 2005-07-01 18:53:15 -0400
+ r4606@zoq-fot-pik: chmrr | 2005-07-01 18:57:33 -0400
+ * Editing of Reminders
+ * RT::Date has ->Date and ->Time methods now
+ * Calendar popup is always yyyy-mm-dd
+
+
+
+
+------------------------------------------------------------------------
+r3375 | jesse | 2005-07-03 19:00:29 -0400 (Sun, 03 Jul 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/3.5-TESTING/html/NoAuth/webrt.css
+ M /rt/branches/3.5-TESTING/lib/RT/SearchBuilder.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/07acl.t
+
+ r22229@hualien: jesse | 2005-07-03 18:25:48 -0400
+ r22154@hualien: jesse | 2005-07-03 17:50:55 -0400
+ r22109@hualien: jesse | 2005-07-03 17:06:06 -0400
+ * Test fixups
+
+
+
+------------------------------------------------------------------------
+r3374 | jesse | 2005-07-03 19:00:21 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt-mailgate.in
+ M /rt/branches/3.5-TESTING/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Interface/Email.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+ M /rt/branches/3.5-TESTING/lib/t/regression/06mailgateway.t
+
+ r22228@hualien: jesse | 2005-07-03 18:25:42 -0400
+ r22153@hualien: jesse | 2005-07-03 17:50:46 -0400
+ r22105@hualien: jesse | 2005-07-03 16:28:58 -0400
+ r22091@hualien: jesse | 2005-07-03 16:13:10 -0400
+ * Disabling a new feature that might be dangerous; test fixes; cf fixes
+
+
+
+
+------------------------------------------------------------------------
+r3373 | jesse | 2005-07-03 19:00:13 -0400 (Sun, 03 Jul 2005) | 10 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+
+ r22227@hualien: jesse | 2005-07-03 18:25:38 -0400
+ r22152@hualien: jesse | 2005-07-03 17:50:41 -0400
+ r22104@hualien: jesse | 2005-07-03 16:28:54 -0400
+ r22039@hualien: jesse | 2005-07-01 21:05:38 -0400
+ * Refactoring of how RT::Record deals with custom fields for greater consistency
+
+
+
+
+
+------------------------------------------------------------------------
+r3372 | jesse | 2005-07-03 19:00:03 -0400 (Sun, 03 Jul 2005) | 27 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+ r22226@hualien: jesse | 2005-07-03 18:25:33 -0400
+ r22151@hualien: jesse | 2005-07-03 17:50:36 -0400
+ r22103@hualien: jesse | 2005-07-03 16:27:27 -0400
+ r22038@hualien: jesse | 2005-07-01 19:28:24 -0400
+ r21984@hualien (orig r3261): kevinr | 2005-06-28 18:47:38 -0400
+ r5752@sad-girl-in-snow: kevinr | 2005-06-28 17:45:36 -0500
+ RT-Ticket: 6745
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Applied Seph's patch to add a BeforeShowHistory callback.
+
+ r22028@hualien (orig r3270): alexmv | 2005-07-01 00:32:06 -0400
+ r4555@zoq-fot-pik: chmrr | 2005-06-30 17:36:05 -0400
+ * WikiText updates to auto-link http:// style links
+
+ r22029@hualien (orig r3271): alexmv | 2005-07-01 00:32:10 -0400
+ r4556@zoq-fot-pik: chmrr | 2005-06-30 17:58:57 -0400
+ * Don't assume link is happy on creation
+
+ r22030@hualien (orig r3272): alexmv | 2005-07-01 00:32:15 -0400
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3371 | jesse | 2005-07-03 18:59:47 -0400 (Sun, 03 Jul 2005) | 62 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_SiteConfig.pm
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/EditScrips
+ M /rt/branches/3.5-TESTING/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT/User_Overlay.pm
+ M /rt/branches/3.5-TESTING/lib/RT.pm.in
+
+ r22225@hualien: jesse | 2005-07-03 18:24:46 -0400
+ r22150@hualien: jesse | 2005-07-03 17:50:31 -0400
+ r22102@hualien: jesse | 2005-07-03 16:26:30 -0400
+ r21954@hualien: jesse | 2005-06-27 11:40:16 -0400
+ r20564@hualien (orig r3181): alexmv | 2005-06-15 17:52:14 -0400
+
+ r20798@hualien (orig r3224): kevinr | 2005-06-18 22:26:40 -0400
+ r4487@sad-girl-in-snow: kevinr | 2005-06-18 21:23:04 -0500
+ * Fixed a typo
+ * now use $RT::WebAddress because it's a good idea
+ * now localize 'Scrip' and number, instead of only the former
+
+ r20799@hualien (orig r3225): kevinr | 2005-06-18 22:26:51 -0400
+
+ r20800@hualien (orig r3226): kevinr | 2005-06-18 22:37:51 -0400
+ r4496@sad-girl-in-snow: kevinr | 2005-06-18 21:37:27 -0500
+ * Fixes possible cross-site scripting bug.
+
+ r20801@hualien (orig r3227): jesse | 2005-06-18 23:47:04 -0400
+ * Fixed typo in kevin's fix (/l is a filter. it needs the |)
+
+ r21566@hualien (orig r3240): alexmv | 2005-06-23 16:57:51 -0400
+ r4466@zoq-fot-pik: chmrr | 2005-06-23 16:58:19 -0400
+ * Document Set() a little better
+
+ r21567@hualien (orig r3241): alexmv | 2005-06-23 16:57:54 -0400
+ r4467@zoq-fot-pik: chmrr | 2005-06-23 16:58:52 -0400
+ * Doc that CanonicalizeEmailAddress may be called as static
+
+ r21568@hualien (orig r3242): alexmv | 2005-06-23 16:58:02 -0400
+ r4468@zoq-fot-pik: chmrr | 2005-06-23 16:59:51 -0400
+ * Remove possible closure issue (my $foo = .... if $bar;)
+
+ r21569@hualien (orig r3243): alexmv | 2005-06-23 16:58:14 -0400
+ r4469@zoq-fot-pik: chmrr | 2005-06-23 17:00:09 -0400
+ * Escape queue name properly
+
+ r21570@hualien (orig r3244): alexmv | 2005-06-23 17:37:31 -0400
+ r4481@zoq-fot-pik: chmrr | 2005-06-23 17:39:53 -0400
+ * Actually fix the escaping bug
+
+ r21571@hualien (orig r3245): alexmv | 2005-06-23 17:43:57 -0400
+ r4483@zoq-fot-pik: chmrr | 2005-06-23 17:46:18 -0400
+ RT-Ticket: 6782
+ RT-Update: correspond
+ RT-Status: resolved
+
+ * Fix perl invocation; thanks to Kim Toms <kim.toms@gmail.com>
+
+ r21572@hualien (orig r3246): alexmv | 2005-06-23 17:48:26 -0400
+ r4485@zoq-fot-pik: chmrr | 2005-06-23 17:50:48 -0400
+ RT-Ticket: 6781
+ RT-Update: correspond
+ RT-Status: resolved
+
+ * _AddWatcher takes Email, not Person; thanks to Andreas Jakum <ajakum@inode.at>
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3370 | jesse | 2005-07-03 18:59:35 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+
+ r22224@hualien: jesse | 2005-07-03 18:24:36 -0400
+ r22148@hualien: jesse | 2005-07-03 17:50:22 -0400
+ r22100@hualien: jesse | 2005-07-03 16:26:21 -0400
+ r21656@hualien: jesse | 2005-06-24 13:53:26 -0400
+ * New Indonesian translation from James Briggs
+
+
+
+
+------------------------------------------------------------------------
+r3369 | jesse | 2005-07-03 18:59:27 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Action/SendEmail.pm
+
+ r22223@hualien: jesse | 2005-07-03 18:24:31 -0400
+ r22147@hualien: jesse | 2005-07-03 17:50:17 -0400
+ r22099@hualien: jesse | 2005-07-03 16:26:16 -0400
+ r21522@hualien: jesse | 2005-06-23 19:49:51 -0400
+ * Some warning avoidance in Action/SendEmail.pm
+
+
+
+
+------------------------------------------------------------------------
+r3368 | jesse | 2005-07-03 18:59:19 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomField
+
+ r22222@hualien: jesse | 2005-07-03 18:24:27 -0400
+ r22146@hualien: jesse | 2005-07-03 17:50:13 -0400
+ r22098@hualien: jesse | 2005-07-03 16:26:12 -0400
+ r21521@hualien: jesse | 2005-06-23 19:49:30 -0400
+ * Custom field edit widget bullet proofing
+
+
+
+
+------------------------------------------------------------------------
+r3367 | jesse | 2005-07-03 18:59:05 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/07acl.t
+ M /rt/branches/3.5-TESTING/lib/t/regression/09record_cf_api.t
+
+ r22221@hualien: jesse | 2005-07-03 18:24:23 -0400
+ r22145@hualien: jesse | 2005-07-03 17:50:08 -0400
+ r22097@hualien: jesse | 2005-07-03 16:25:18 -0400
+ r21520@hualien: jesse | 2005-06-23 19:49:06 -0400
+ * Test suite improvements and cleanups
+
+
+
+
+------------------------------------------------------------------------
+r3366 | jesse | 2005-07-03 18:58:57 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/bin/rt-mailgate.in
+
+ r22220@hualien: jesse | 2005-07-03 18:24:18 -0400
+ r22144@hualien: jesse | 2005-07-03 17:50:04 -0400
+ r22096@hualien: jesse | 2005-07-03 16:25:14 -0400
+ r20757@hualien: jesse | 2005-06-17 19:34:37 -0400
+ * Updated mailgateway documentation
+
+
+
+
+------------------------------------------------------------------------
+r3365 | jesse | 2005-07-03 18:58:49 -0400 (Sun, 03 Jul 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/autohandler
+
+ r22219@hualien: jesse | 2005-07-03 18:24:14 -0400
+ r22143@hualien: jesse | 2005-07-03 17:49:55 -0400
+ r22037@hualien: jesse | 2005-07-01 19:25:05 -0400
+ r22031@hualien (orig r3273): alexmv | 2005-07-01 00:46:49 -0400
+ r4557@zoq-fot-pik: chmrr | 2005-06-30 20:44:33 -0400
+ * Fractional units
+
+
+
+
+
+------------------------------------------------------------------------
+r3364 | jesse | 2005-07-03 18:58:34 -0400 (Sun, 03 Jul 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/EditBasics
+
+ r22218@hualien: jesse | 2005-07-03 18:24:10 -0400
+ r22142@hualien: jesse | 2005-07-03 17:49:50 -0400
+ r21959@hualien: jesse | 2005-06-27 14:15:10 -0400
+ * Moved "Owner" to right after queue in the list of basics to edit.
+
+
+
+------------------------------------------------------------------------
+r3363 | jesse | 2005-07-03 18:58:27 -0400 (Sun, 03 Jul 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Create.html
+
+ r22217@hualien: jesse | 2005-07-03 18:24:05 -0400
+ r22141@hualien: jesse | 2005-07-03 17:49:42 -0400
+ r21955@hualien: jesse | 2005-06-27 11:40:56 -0400
+ r21288@hualien (orig r3235): alexmv | 2005-06-20 18:54:00 -0400
+ r4386@zoq-fot-pik: chmrr | 2005-06-20 18:56:20 -0400
+ * Attempt to prevent footer from showing up on redirects for some servers (?)
+
+
+
+
+
+------------------------------------------------------------------------
+r3362 | jesse | 2005-07-03 18:58:20 -0400 (Sun, 03 Jul 2005) | 11 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Tools/Elements/Tabs
+
+ r22216@hualien: jesse | 2005-07-03 18:24:00 -0400
+ r22140@hualien: jesse | 2005-07-03 17:49:37 -0400
+ r21273@hualien: jesse | 2005-06-20 17:57:29 -0400
+ r20804@hualien (orig r3230): alexmv | 2005-06-20 14:57:03 -0400
+ r4370@zoq-fot-pik: chmrr | 2005-06-20 14:58:14 -0400
+ * Added callback
+
+
+
+
+
+------------------------------------------------------------------------
+r3361 | jesse | 2005-07-03 18:58:11 -0400 (Sun, 03 Jul 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Elements/Chart
+
+ r22215@hualien: jesse | 2005-07-03 18:23:55 -0400
+ r22139@hualien: jesse | 2005-07-03 17:49:33 -0400
+ r20761@hualien: jesse | 2005-06-17 19:38:02 -0400
+ * Now statistical reports include total # of tickets found
+
+
+
+------------------------------------------------------------------------
+r3360 | jesse | 2005-07-03 18:57:54 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/cs.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/da.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/de.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/en.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/es.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fi.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/fr.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/he.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/hu.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/it.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ja.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/nl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/no.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pl.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/pt_br.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/ru.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_cn.po
+ M /rt/branches/3.5-TESTING/lib/RT/I18N/zh_tw.po
+
+ r22214@hualien: jesse | 2005-07-03 18:23:46 -0400
+ r22138@hualien: jesse | 2005-07-03 17:48:10 -0400
+ r20733@hualien: jesse | 2005-06-16 12:44:07 -0400
+ r20717@hualien: jesse | 2005-06-16 12:24:50 -0400
+ * Message catalog updates
+
+
+
+
+------------------------------------------------------------------------
+r3359 | jesse | 2005-07-03 18:57:46 -0400 (Sun, 03 Jul 2005) | 15 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Action/Notify.pm
+
+ r22213@hualien: jesse | 2005-07-03 18:23:42 -0400
+ r22137@hualien: jesse | 2005-07-03 17:48:05 -0400
+ r20732@hualien: jesse | 2005-06-16 12:43:57 -0400
+ r20716@hualien: jesse | 2005-06-16 12:23:58 -0400
+ r20713@hualien: jesse | 2005-06-16 12:16:28 -0400
+ r20711@hualien (orig r3204): alexmv | 2005-06-16 04:31:40 -0400
+ r4308@zoq-fot-pik: chmrr | 2005-06-16 04:31:30 -0400
+ * Missed an lc
+
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3358 | jesse | 2005-07-03 18:57:37 -0400 (Sun, 03 Jul 2005) | 16 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Action/Notify.pm
+ M /rt/branches/3.5-TESTING/lib/RT/EmailParser.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Queue_Overlay.pm
+
+ r22212@hualien: jesse | 2005-07-03 18:23:37 -0400
+ r22136@hualien: jesse | 2005-07-03 17:48:00 -0400
+ r20731@hualien: jesse | 2005-06-16 12:43:44 -0400
+ r20715@hualien: jesse | 2005-06-16 12:20:34 -0400
+ r20712@hualien: jesse | 2005-06-16 12:16:22 -0400
+ r20710@hualien (orig r3203): alexmv | 2005-06-16 04:29:14 -0400
+ r4306@zoq-fot-pik: chmrr | 2005-06-16 04:29:06 -0400
+ * Move /^$foo$/ regexes to use eq instead, to prevent regex insertion
+ exploits
+
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3357 | jesse | 2005-07-03 18:57:27 -0400 (Sun, 03 Jul 2005) | 15 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Scrip_Overlay.pm
+
+ r22211@hualien: jesse | 2005-07-03 18:23:32 -0400
+ r22135@hualien: jesse | 2005-07-03 17:47:56 -0400
+ r20730@hualien: jesse | 2005-06-16 12:43:33 -0400
+ r20714@hualien: jesse | 2005-06-16 12:20:30 -0400
+ r20705@hualien: jesse | 2005-06-16 12:11:06 -0400
+ r19183@hualien (orig r3063): pdh | 2005-06-03 01:01:55 -0400
+ Misleading error referred to IsApplicable, not Commit
+
+
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3356 | jesse | 2005-07-03 18:57:20 -0400 (Sun, 03 Jul 2005) | 13 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/User_Overlay.pm
+
+ r22210@hualien: jesse | 2005-07-03 18:23:26 -0400
+ r22134@hualien: jesse | 2005-07-03 17:47:46 -0400
+ r20728@hualien: jesse | 2005-06-16 12:43:18 -0400
+ r20708@hualien: jesse | 2005-06-16 12:12:29 -0400
+ r20063@hualien (orig r3142): kevinr | 2005-06-13 15:43:08 -0400
+ r4210@SAD-GIRL-IN-SNOW: kevinr | 2005-06-13 15:39:50 -0400
+ * fixed a couple small issues with the perldoc format
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3355 | jesse | 2005-07-03 18:57:05 -0400 (Sun, 03 Jul 2005) | 13 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/ScripAction_Overlay.pm
+
+ r22209@hualien: jesse | 2005-07-03 18:23:21 -0400
+ r22133@hualien: jesse | 2005-07-03 17:47:41 -0400
+ r20727@hualien: jesse | 2005-06-16 12:43:11 -0400
+ r20707@hualien: jesse | 2005-06-16 12:12:16 -0400
+ r19708@hualien (orig r3120): kevinr | 2005-06-09 01:33:18 -0400
+ r4110@SAD-GIRL-IN-SNOW: kevinr | 2005-06-09 01:30:22 -0400
+ * fixed the mis-typed perldoc tags
+
+
+
+
+
+
+------------------------------------------------------------------------
+r3354 | jesse | 2005-07-03 18:56:50 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/ScripAction_Overlay.pm
+
+ r22208@hualien: jesse | 2005-07-03 18:23:15 -0400
+ r22132@hualien: jesse | 2005-07-03 17:47:37 -0400
+ r20726@hualien: jesse | 2005-06-16 12:43:06 -0400
+ r20706@hualien: jesse | 2005-06-16 12:12:07 -0400
+
+
+
+
+
+------------------------------------------------------------------------
+r3353 | jesse | 2005-07-03 18:56:42 -0400 (Sun, 03 Jul 2005) | 9 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/Record.pm
+
+ r22207@hualien: jesse | 2005-07-03 18:23:10 -0400
+ r22131@hualien: jesse | 2005-07-03 17:47:32 -0400
+ r20562@hualien: jesse | 2005-06-15 18:59:20 -0400
+ * When updating articles, we want to say "Updated Article" rather than "Updated FM"
+
+
+
+
+
+------------------------------------------------------------------------
+r3352 | jesse | 2005-07-03 18:56:34 -0400 (Sun, 03 Jul 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/index.html
+
+ r22206@hualien: jesse | 2005-07-03 18:23:05 -0400
+ r22130@hualien: jesse | 2005-07-03 17:47:27 -0400
+ r20559@hualien: jesse | 2005-06-15 16:33:09 -0400
+ * Style cleanups for custom field list filtering
+
+
+
+------------------------------------------------------------------------
+r3351 | jesse | 2005-07-03 18:56:27 -0400 (Sun, 03 Jul 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+
+ r22205@hualien: jesse | 2005-07-03 18:23:01 -0400
+ r22129@hualien: jesse | 2005-07-03 17:47:23 -0400
+ r20551@hualien: jesse | 2005-06-15 16:12:50 -0400
+ * Reenabled testing redirect on article update
+
+
+
+------------------------------------------------------------------------
+r3350 | jesse | 2005-07-03 18:56:11 -0400 (Sun, 03 Jul 2005) | 10 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/RT/SearchBuilder.pm
+
+ r22204@hualien: jesse | 2005-07-03 18:22:56 -0400
+ r22128@hualien: jesse | 2005-07-03 17:47:18 -0400
+ r20545@hualien: jesse | 2005-06-15 15:58:15 -0400
+ r20542@hualien: jesse | 2005-06-15 15:56:43 -0400
+ * Made search-by-any-customfield work
+
+
+
+
+
+------------------------------------------------------------------------
+r3349 | jesse | 2005-07-03 18:55:59 -0400 (Sun, 03 Jul 2005) | 10 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/lib/RT/I18N/id.po
+
+ r22203@hualien: jesse | 2005-07-03 18:22:52 -0400
+ r22127@hualien: jesse | 2005-07-03 17:47:14 -0400
+ r20544@hualien: jesse | 2005-06-15 15:58:11 -0400
+ r20078@hualien: jesse | 2005-06-14 16:57:41 -0400
+ * First draft Indonesian message catalog from James Briggs
+
+
+
+
+
+------------------------------------------------------------------------
+r3348 | jesse | 2005-07-03 18:55:42 -0400 (Sun, 03 Jul 2005) | 17 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Admin/CustomFields/index.html
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldFreeform
+
+ r22201@hualien: jesse | 2005-07-03 18:21:44 -0400
+ r22125@hualien: jesse | 2005-07-03 17:47:04 -0400
+ r20540@hualien: jesse | 2005-06-15 14:46:19 -0400
+ r20162@hualien (orig r3156): alexmv | 2005-06-14 23:24:19 -0400
+ r4230@zoq-fot-pik: chmrr | 2005-06-14 23:22:31 -0400
+ * Allow filtering of custom fields
+
+ r20163@hualien (orig r3157): alexmv | 2005-06-14 23:24:23 -0400
+
+ r20165@hualien (orig r3159): alexmv | 2005-06-15 00:47:27 -0400
+ r4236@zoq-fot-pik: chmrr | 2005-06-15 00:46:57 -0400
+ * <input> tags don't like newlines in them
+
+
+
+
+
+------------------------------------------------------------------------
+r3347 | jesse | 2005-07-03 18:55:34 -0400 (Sun, 03 Jul 2005) | 19 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/QueryString
+ A /rt/branches/3.5-TESTING/html/Elements/ShowCustomFieldBinary
+ M /rt/branches/3.5-TESTING/lib/RT/Ticket_Overlay.pm
+
+ r22200@hualien: jesse | 2005-07-03 18:21:39 -0400
+ r22124@hualien: jesse | 2005-07-03 17:46:59 -0400
+ r20076@hualien: jesse | 2005-06-14 15:01:31 -0400
+ r20064@hualien (orig r3143): alexmv | 2005-06-13 20:15:22 -0400
+ r4193@zoq-fot-pik: chmrr | 2005-06-13 20:14:53 -0400
+ * Generalize to work with arrays in %ARGS
+
+ r20066@hualien (orig r3145): alexmv | 2005-06-13 21:02:36 -0400
+ r4197@zoq-fot-pik: chmrr | 2005-06-13 21:01:50 -0400
+ * Don't assume that ->Resolver is a valid object
+
+ r20068@hualien (orig r3147): alexmv | 2005-06-14 06:56:06 -0400
+ r4209@zoq-fot-pik: chmrr | 2005-06-14 06:55:43 -0400
+ * We should actually let people *download* these uploads..
+
+
+
+
+
+------------------------------------------------------------------------
+r3346 | jesse | 2005-07-03 18:55:25 -0400 (Sun, 03 Jul 2005) | 7 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Search/Chart
+ M /rt/branches/3.5-TESTING/html/Search/Chart.html
+ M /rt/branches/3.5-TESTING/html/Search/Elements/Chart
+
+ r22199@hualien: jesse | 2005-07-03 18:21:35 -0400
+ r22123@hualien: jesse | 2005-07-03 17:46:54 -0400
+ r20060@hualien: jesse | 2005-06-14 08:57:51 -0400
+ * Moving the search qyery into the search body
+
+
+
+------------------------------------------------------------------------
+r3345 | jesse | 2005-07-03 18:55:17 -0400 (Sun, 03 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+ M /rt/branches/3.5-TESTING/lib/RT/Reminders.pm
+ M /rt/branches/3.5-TESTING/lib/RT/SearchBuilder.pm
+ M /rt/branches/3.5-TESTING/lib/RT/Transaction_Overlay.pm
+
+ r22198@hualien: jesse | 2005-07-03 18:21:29 -0400
+ r22022@hualien: jesse | 2005-06-30 01:29:44 -0400
+ * Reminders fixes
+
+
+------------------------------------------------------------------------
+r3344 | jesse | 2005-07-03 18:55:10 -0400 (Sun, 03 Jul 2005) | 18 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldCombobox
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Reminders
+
+ r22197@hualien: jesse | 2005-07-03 18:21:24 -0400
+ r21988@hualien: jesse | 2005-06-29 10:18:11 -0400
+ r21985@hualien (orig r3262): alexmv | 2005-06-29 01:25:04 -0400
+ r4492@zoq-fot-pik: chmrr | 2005-06-28 21:22:25 -0400
+ * Only update reminders if we actually were submitting on them
+ * Don't open reminders if we didn't have a checkbox for them
+
+ r21986@hualien (orig r3263): alexmv | 2005-06-29 01:25:11 -0400
+ r4493@zoq-fot-pik: chmrr | 2005-06-28 21:32:36 -0400
+ * Label entry field better
+
+ r21987@hualien (orig r3264): alexmv | 2005-06-29 01:25:19 -0400
+ r4494@zoq-fot-pik: chmrr | 2005-06-28 22:03:22 -0400
+ * Comboboxes are just one value, not multiple; s/Values/Value/
+
+
+
+
+------------------------------------------------------------------------
+r3343 | jesse | 2005-07-03 18:54:54 -0400 (Sun, 03 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditLinks
+
+ r22196@hualien: jesse | 2005-07-03 18:21:19 -0400
+ r21981@hualien: jesse | 2005-06-29 10:15:07 -0400
+ * fixed a bug in link editing.
+
+
+------------------------------------------------------------------------
+r3342 | jesse | 2005-07-03 18:54:46 -0400 (Sun, 03 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/lib/t/regression/03web_compiliation_errors.t
+
+ r22195@hualien: jesse | 2005-07-03 18:21:15 -0400
+ r21980@hualien: jesse | 2005-06-29 10:14:47 -0400
+ * Small testsuite fixes
+
+
+------------------------------------------------------------------------
+r3341 | jesse | 2005-07-03 18:54:37 -0400 (Sun, 03 Jul 2005) | 5 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r22194@hualien: jesse | 2005-07-03 18:21:11 -0400
+ r21957@hualien: jesse | 2005-06-27 11:45:13 -0400
+ * Fixing broken callback
+
+
+------------------------------------------------------------------------
+r3340 | jesse | 2005-07-03 18:54:30 -0400 (Sun, 03 Jul 2005) | 12 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/EditCustomFieldSelect
+ M /rt/branches/3.5-TESTING/html/NoAuth/cascaded.js
+ M /rt/branches/3.5-TESTING/html/NoAuth/combobox.js
+
+ r22193@hualien: jesse | 2005-07-03 18:21:06 -0400
+ r21950@hualien: jesse | 2005-06-27 10:10:33 -0400
+ r18006@hualien (orig r2983): autrijus | 2005-05-30 01:51:30 -0400
+ * empty categories are now treated as empty, not as /same as above/.
+ * selecting an empty catagory now means /show all/, not /show uncategorised/.
+ r19182@hualien (orig r3062): autrijus | 2005-06-02 23:03:22 -0400
+ * fix Safari borkenness with ComboBox. Saf segfaults with
+ .styles.display='none'; switched to the less pretty but
+ still workable .styles.visibility='invisible';.
+
+
+
+------------------------------------------------------------------------
+r3291 | trs | 2005-07-02 20:11:07 -0400 (Sat, 02 Jul 2005) | 15 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ M /rt/branches/3.5-TESTING/html/Elements/Submit
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/body.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/footer.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/forms.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/header.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/main.css
+ M /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/cbr-b2lb.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/ctr-b2lb.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/rollup-arrow.png
+ M /rt/branches/3.5-TESTING/html/Ticket/Display.html
+ M /rt/branches/3.5-TESTING/html/Ticket/Elements/Tabs
+ M /rt/branches/3.5-TESTING/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/3.5-TESTING/html/Widgets/SelectionBox
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBox
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxEnd
+ M /rt/branches/3.5-TESTING/html/Widgets/TitleBoxStart
+
+ r4694@wintermute: tom | 2005-07-02 20:09:14 -0400
+ LOTS of miscellaneous changes...
+
+ I've mostly styled the /Widget/TitleBoxes, but they still need
+ to be colorized according to type (and this needs to be synced with
+ history).
+
+ A lot of form elements still need styling because they don't have appropriate
+ classes. These would be done already if I could use CSS3 attribute selectors,
+ but alas, I can't.
+
+ A few JS improvements plus general cleanup of HTML as I go along.
+
+ I still have to correct for a few IE bugs.
+
+------------------------------------------------------------------------
+r3290 | trs | 2005-07-01 23:23:31 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Footer
+
+ r4690@wintermute: tom | 2005-07-01 23:21:18 -0400
+ Moved conditional outside of #legal
+
+------------------------------------------------------------------------
+r3289 | trs | 2005-07-01 23:23:27 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r4689@wintermute: tom | 2005-07-01 22:20:01 -0400
+ Get rid of the pesky horizontal scrollbar
+
+------------------------------------------------------------------------
+r3288 | trs | 2005-07-01 23:23:23 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Prefs/SearchOptions.html
+
+ r4688@wintermute: tom | 2005-07-01 22:11:30 -0400
+ Added missing page title
+
+------------------------------------------------------------------------
+r3287 | trs | 2005-07-01 21:25:00 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+
+ r4686@wintermute: tom | 2005-07-01 21:24:32 -0400
+ Trivial commit :)
+
+------------------------------------------------------------------------
+r3286 | trs | 2005-07-01 21:13:05 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ D /rt/branches/3.5-TESTING/html/NoAuth/ossf.css
+
+ r4659@wintermute: tom | 2005-07-01 21:11:37 -0400
+ Unneeded CSS file
+
+------------------------------------------------------------------------
+r3285 | trs | 2005-07-01 21:13:02 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+
+ r4658@wintermute: tom | 2005-07-01 21:10:21 -0400
+ Fixed merge artifacts
+
+------------------------------------------------------------------------
+r3284 | trs | 2005-07-01 21:12:51 -0400 (Fri, 01 Jul 2005) | 1 line
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ A /rt/branches/3.5-TESTING/html/NoAuth/ossf.css
+
+
+------------------------------------------------------------------------
+r3283 | trs | 2005-07-01 21:12:45 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/html/Elements/CreateTicket
+ M /rt/branches/3.5-TESTING/html/Elements/Footer
+ M /rt/branches/3.5-TESTING/html/Elements/Header
+ M /rt/branches/3.5-TESTING/html/Elements/Menu
+ M /rt/branches/3.5-TESTING/html/Elements/PageLayout
+ M /rt/branches/3.5-TESTING/html/Elements/SelectNewTicketQueue
+ M /rt/branches/3.5-TESTING/html/Elements/SimpleSearch
+ A /rt/branches/3.5-TESTING/html/NoAuth/css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/footer.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/header.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/main.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/misc.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/nav-left.css
+ A /rt/branches/3.5-TESTING/html/NoAuth/css/nav.css
+ D /rt/branches/3.5-TESTING/html/NoAuth/ossf.css
+
+ r4631@wintermute: tom | 2005-07-01 20:30:23 -0400
+ Integrated the top actions, menu, logo, footer, and some other stuff into the new CSS layout. Menu system was a total pain to retrofit, and it may still have kinks.
+
+------------------------------------------------------------------------
+r3282 | trs | 2005-07-01 21:12:41 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r4630@wintermute: tom | 2005-07-01 14:07:13 -0400
+ Added var, local, and share to svn:ignore
+
+------------------------------------------------------------------------
+r3281 | trs | 2005-07-01 21:12:35 -0400 (Fri, 01 Jul 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/cb-light.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/cb.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/cbr-b2g.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/cbr-gray.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/cbr-trans.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/cbr.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/ct-light.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/ct.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/ctr-b2g.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/ctr-gray.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/ctr-trans.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/ctr.gif
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/dark-arrow-up.png
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/dark-arrow.png
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/light-arrow-up.png
+ A /rt/branches/3.5-TESTING/html/NoAuth/images/css/light-arrow.png
+
+ r4629@wintermute: tom | 2005-07-01 14:05:58 -0400
+ All the little images used by the CSS
+
+------------------------------------------------------------------------
+r3280 | trs | 2005-07-01 21:12:32 -0400 (Fri, 01 Jul 2005) | 8 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+
+ r4366@wintermute: tom | 2005-06-22 16:07:17 -0400
+ r4256@wintermute (orig r3221): pdh | 2005-06-16 23:37:58 -0400
+ Default value for $RedistributeAutoGeneratedMessages is now
+ "privileged".
+
+
+
+
+------------------------------------------------------------------------
+r3279 | trs | 2005-07-01 21:12:28 -0400 (Fri, 01 Jul 2005) | 54 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+
+ r4119@wintermute: tom | 2005-06-13 18:37:20 -0400
+ r4105@wintermute (orig r3129): kevinr | 2005-06-09 20:30:01 -0400
+ r4148@SAD-GIRL-IN-SNOW: kevinr | 2005-06-09 20:29:52 -0400
+ * Cleaned up the tests and fixed the issue with the local RT tag.
+
+ r4106@wintermute (orig r3130): kevinr | 2005-06-10 15:36:10 -0400
+ r4163@STRATTON-ONE-THIRTY-THREE: kevinr | 2005-06-10 13:50:15 -0400
+ * Added a plan to the second test and made sure the tests will get distributed.
+
+ r4107@wintermute (orig r3131): kevinr | 2005-06-10 15:36:25 -0400
+ r4170@STRATTON-ONE-THIRTY-THREE: kevinr | 2005-06-10 15:36:00 -0400
+ * Included the relevant information about setting template headers in the
+ README
+ * moved scrip creation into initialdata so the Makefile will do it automagically
+
+ r4108@wintermute (orig r3132): kevinr | 2005-06-10 16:11:03 -0400
+ * This is more properly an extension, since it creates a scrip.
+ r4109@wintermute (orig r3133): kevinr | 2005-06-10 18:37:42 -0400
+ r4179@STRATTON-ONE-THIRTY-THREE: kevinr | 2005-06-10 16:43:14 -0400
+ * Moved in an extension file to use as a stub.
+
+ r4110@wintermute (orig r3134): kevinr | 2005-06-10 18:37:58 -0400
+ r4180@STRATTON-ONE-THIRTY-THREE: kevinr | 2005-06-10 18:37:31 -0400
+ * Modified files in the hopes of making this an extension instead of an action.
+
+ r4111@wintermute (orig r3135): kevinr | 2005-06-10 18:44:38 -0400
+ r4183@STRATTON-ONE-THIRTY-THREE: kevinr | 2005-06-10 18:44:33 -0400
+ * s/::/-/, which makes it not fail because '::' means something in Makefiles.
+
+ r4112@wintermute (orig r3136): kevinr | 2005-06-10 18:47:49 -0400
+ r4185@STRATTON-ONE-THIRTY-THREE: kevinr | 2005-06-10 18:47:44 -0400
+ * now actually includes the extension file
+
+ r4113@wintermute (orig r3137): glasser | 2005-06-10 22:42:19 -0400
+ r34816@tin-foil: glasser | 2005-06-10 22:41:58 -0400
+ Support DEFAULT in schema.
+
+ r4114@wintermute (orig r3138): jesse | 2005-06-12 22:52:55 -0400
+ Branching RT 3.5 for testing
+ r4115@wintermute (orig r3139): jesse | 2005-06-12 22:53:47 -0400
+ Removing disused platano-experimental branch
+
+ r4116@wintermute (orig r3140): jesse | 2005-06-12 23:32:42 -0400
+ r20056@hualien: jesse | 2005-06-12 23:32:17 -0400
+ * 3.5.1
+
+ r4117@wintermute (orig r3141): jesse | 2005-06-12 23:34:35 -0400
+ Tagged as 3.5.1 by svn RelEng 1.0
+ r4118@wintermute (orig r3142): kevinr | 2005-06-13 15:43:08 -0400
+ r4210@SAD-GIRL-IN-SNOW: kevinr | 2005-06-13 15:39:50 -0400
+ * fixed a couple small issues with the perldoc format
+
+
+
+------------------------------------------------------------------------
+r3221 | pdh | 2005-06-16 23:37:58 -0400 (Thu, 16 Jun 2005) | 4 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING/UPGRADING
+ M /rt/branches/3.5-TESTING/etc/RT_Config.pm.in
+
+Default value for $RedistributeAutoGeneratedMessages is now
+"privileged".
+
+
+------------------------------------------------------------------------
+r3140 | jesse | 2005-06-12 23:32:42 -0400 (Sun, 12 Jun 2005) | 3 lines
+Changed paths:
+ M /rt/branches/3.5-TESTING
+ M /rt/branches/3.5-TESTING/configure.ac
+ A /rt/branches/3.5-TESTING/etc/upgrade/3.5.1
+ A /rt/branches/3.5-TESTING/etc/upgrade/3.5.1/content
+ D /rt/branches/3.5-TESTING/etc/upgrade/QUEBEC
+ M /rt/branches/3.5-TESTING/html/NoAuth/webrt.css
+ M /rt/branches/3.5-TESTING/releng.cnf
+
+ r20056@hualien: jesse | 2005-06-12 23:32:17 -0400
+ * 3.5.1
+
+------------------------------------------------------------------------
+r3138 | jesse | 2005-06-12 22:52:55 -0400 (Sun, 12 Jun 2005) | 1 line
+Changed paths:
+ A /rt/branches/3.5-TESTING (from /rt/branches/PLATANO-EXPERIMENTAL-CSS:3125)
+
+Branching RT 3.5 for testing
+------------------------------------------------------------------------
+r3125 | jesse | 2005-06-09 02:36:55 -0400 (Thu, 09 Jun 2005) | 3 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyReminders
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Reminders.html
+
+ r19711@hualien: jesse | 2005-06-09 02:35:57 -0400
+ * Fixes from chaldea mergeup
+
+------------------------------------------------------------------------
+r3124 | jesse | 2005-06-09 02:36:24 -0400 (Thu, 09 Jun 2005) | 3 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.svc.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-mailgate.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_SiteConfig.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/initialdata
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/QUEBEC/content
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldCombobox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldSelect
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Error
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyReminders
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ValidateCustomFields
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/cascaded.js
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/combobox.js
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Chart
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Chart.png
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/Chart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/PreviewScrips
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Reminders
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMemberOf
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowReferences
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Reminders.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/CreatedByDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/ResolvedByOwner.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/index.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/ComboBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/AutoOpen.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/EscalatePriority.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CurrentUser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Date.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/EmailParser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Handle.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/da.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/de.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/es.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fi.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fr.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/he.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/hu.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/it.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ja.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/nl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/no.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pt_br.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ru.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_cn.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_tw.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/QueryBuilder
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/QueryBuilder/Tree.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/QueryBuilder.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Reminders.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Report/Tickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SavedSearch.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SavedSearches.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/ActiveTicketsInQueue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/FromSQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SearchBuilder.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/System.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/t.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/02basic_web.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/03web_compiliation_errors.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/04send_email.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/06mailgateway.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/09record_cf_api.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/10merge.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/11-template-insert.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/12-search.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/13-attribute-tests.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/15cf_single_values_are_single.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/20savedsearch.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/21query-builder.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r19710@hualien: jesse | 2005-06-09 01:48:50 -0400
+ * Merging up from chaldea
+
+------------------------------------------------------------------------
+r2929 | jesse | 2005-05-22 18:40:11 -0400 (Sun, 22 May 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+
+ r17005@hualien: jesse | 2005-05-22 17:15:48 -0400
+ r16996@hualien: jesse | 2005-05-22 17:01:22 -0400
+ r16989@hualien: jesse | 2005-05-22 16:58:45 -0400
+ r16893@hualien: jesse | 2005-05-16 18:04:08 -0400
+ * Better creation of in-reply-to/references headers on web-ased reply
+
+
+
+
+------------------------------------------------------------------------
+r2928 | jesse | 2005-05-22 18:39:49 -0400 (Sun, 22 May 2005) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+
+ r17004@hualien: jesse | 2005-05-22 17:15:44 -0400
+ r16995@hualien: jesse | 2005-05-22 17:01:13 -0400
+ r16988@hualien: jesse | 2005-05-22 16:58:36 -0400
+ r16891@hualien: jesse | 2005-05-16 17:48:02 -0400
+ * Adding a new "top of page" callback
+
+
+
+
+
+------------------------------------------------------------------------
+r2927 | jesse | 2005-05-22 18:39:12 -0400 (Sun, 22 May 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+
+ r17003@hualien: jesse | 2005-05-22 17:15:39 -0400
+ r16994@hualien: jesse | 2005-05-22 17:00:41 -0400
+ r16987@hualien: jesse | 2005-05-22 16:58:28 -0400
+ r16761@hualien: jesse | 2005-05-13 10:22:22 -0400
+ * Ruslan found a debugging statement we had accidentally committed
+
+
+
+
+------------------------------------------------------------------------
+r2926 | jesse | 2005-05-22 18:38:49 -0400 (Sun, 22 May 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+
+ r17002@hualien: jesse | 2005-05-22 17:15:31 -0400
+ r16993@hualien: jesse | 2005-05-22 17:00:24 -0400
+ r16986@hualien: jesse | 2005-05-22 16:58:17 -0400
+ r16510@hualien: jesse | 2005-05-12 12:44:00 -0400
+ * Fix on the plane was wrong. sigh
+
+
+
+
+------------------------------------------------------------------------
+r2925 | jesse | 2005-05-22 18:37:54 -0400 (Sun, 22 May 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTimeUnits
+
+ r17001@hualien: jesse | 2005-05-22 17:15:24 -0400
+ r16992@hualien: jesse | 2005-05-22 17:00:09 -0400
+ r16915@hualien: jesse | 2005-05-18 14:32:50 -0400
+ * missing file
+
+
+
+------------------------------------------------------------------------
+r2924 | jesse | 2005-05-22 18:37:32 -0400 (Sun, 22 May 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/02basic_web.t
+
+ r17000@hualien: jesse | 2005-05-22 17:15:03 -0400
+ r16991@hualien: jesse | 2005-05-22 16:59:53 -0400
+ r16903@hualien: jesse | 2005-05-18 14:29:56 -0400
+ * Reimplemented "Hours/Minutes" selects for time worked/estimated/spent
+
+
+
+------------------------------------------------------------------------
+r2923 | jesse | 2005-05-22 18:37:11 -0400 (Sun, 22 May 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+
+ r16999@hualien: jesse | 2005-05-22 17:14:59 -0400
+ r16990@hualien: jesse | 2005-05-22 16:59:47 -0400
+ r16902@hualien: jesse | 2005-05-17 23:18:36 -0400
+ * Added Owner to the ticket basics page
+
+
+
+------------------------------------------------------------------------
+r2922 | jesse | 2005-05-22 18:35:47 -0400 (Sun, 22 May 2005) | 231 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_SiteConfig.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/initialdata
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFieldValues
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldCascaded
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldSelect
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldWikitext
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFieldWikitext
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Tabs
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ValidateCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Chart.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Chart.png
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/Chart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectChartType
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectGroupBy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Elements/Tabs
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/CreatedByDates.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/Elements
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/Elements/Tabs
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/ResolvedByDates.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/ResolvedByOwner.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Reports/index.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/da.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/de.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/en.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/es.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fi.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fr.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/he.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/hu.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/it.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ja.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/nl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/no.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pt_br.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ru.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_cn.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_tw.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Report/Tickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SearchBuilder.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/01ticket_link_searching.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/15cf_combo_cascade.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/15cf_pattern.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/19-rtname.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r16998@hualien: jesse | 2005-05-22 17:12:47 -0400
+ r16975@hualien: jesse | 2005-05-22 15:08:34 -0400
+ r16904@hualien (orig r2888): autrijus | 2005-05-18 06:26:04 -0400
+ r17753@not (orig r2830): jesse | 2005-05-04 12:19:34 +0800
+ r15713@hualien: jesse | 2005-05-04 00:17:54 -0400
+ * Graphing and charting
+
+ r17757@not (orig r2834): jesse | 2005-05-07 01:59:35 +0800
+ r15824@hualien: jesse | 2005-05-06 13:57:04 -0400
+ * First cut at new reports
+
+ r17758@not (orig r2835): jesse | 2005-05-07 02:01:51 +0800
+ r15827@hualien: jesse | 2005-05-06 14:01:08 -0400
+ * Testdeps fixes for the new code
+
+ r17759@not (orig r2836): jesse | 2005-05-07 04:39:11 +0800
+ r15830@hualien: jesse | 2005-05-06 16:34:11 -0400
+ r13240@hualien: jesse | 2005-04-18 01:07:43 -0400
+ * Added Wikitext custom field type
+
+
+ r17760@not (orig r2837): jesse | 2005-05-07 04:40:10 +0800
+ r15831@hualien: jesse | 2005-05-06 16:34:16 -0400
+ r13251@hualien: jesse | 2005-04-18 01:54:02 -0400
+ r13242@hualien (orig r2768): jesse | 2005-04-18 00:16:42 -0400
+ 3.4.2rc1
+
+
+
+ r17761@not (orig r2838): jesse | 2005-05-07 04:40:27 +0800
+ r15832@hualien: jesse | 2005-05-06 16:34:19 -0400
+ r13264@hualien: jesse | 2005-04-19 14:15:54 -0400
+ RT-Ticket: 6616
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Added a new HasPassword method to the RT user object
+
+
+
+ r17762@not (orig r2839): jesse | 2005-05-07 04:40:45 +0800
+ r15833@hualien: jesse | 2005-05-06 16:34:24 -0400
+ r13834@hualien: jesse | 2005-04-19 17:03:40 -0400
+ RT-Ticket: 6617
+ RT-Status: resolve
+ RT-Update: correspond
+
+ Removed some spurious use lib lines from the test suite. Thanks to Dave Rolsky.
+
+
+
+
+ r17763@not (orig r2840): jesse | 2005-05-07 04:41:00 +0800
+ r15834@hualien: jesse | 2005-05-06 16:34:27 -0400
+ r13990@hualien: jesse | 2005-04-19 22:00:14 -0400
+ * Canonicalized => to , in the configuration file
+ They mean the same thing, but consistency is good.
+
+
+ r17764@not (orig r2841): jesse | 2005-05-07 04:41:15 +0800
+ r15835@hualien: jesse | 2005-05-06 16:34:31 -0400
+ r13994@hualien: jesse | 2005-04-20 11:31:44 -0400
+ RT-Ticket: 6620
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Hungarian translation had an incorrect Content-Type. Reported upstream from
+ Debian.
+
+
+
+ r17765@not (orig r2842): jesse | 2005-05-07 04:41:50 +0800
+ r15836@hualien: jesse | 2005-05-06 16:34:35 -0400
+ r14017@hualien: jesse | 2005-04-21 21:07:47 -0400
+ Loc updates
+
+
+ r17766@not (orig r2843): jesse | 2005-05-07 04:42:47 +0800
+ r15837@hualien: jesse | 2005-05-06 16:35:02 -0400
+ r14525@hualien: jesse | 2005-04-26 14:42:28 -0400
+ r13265@hualien (orig r2779): tla | 2005-04-19 00:45:48 -0400
+ fix the perldoc
+
+ r14524@hualien (orig r2807): jesse | 2005-04-25 22:33:14 -0400
+ * Correct comments in config file. "RTLogoURL" is actually LogoURL. --Allison Randal
+
+
+
+
+ r17767@not (orig r2844): jesse | 2005-05-07 04:43:03 +0800
+ r15838@hualien: jesse | 2005-05-06 16:35:06 -0400
+ r14616@hualien: jesse | 2005-04-28 22:56:19 -0400
+ * Added a todo notice
+
+
+
+ r17768@not (orig r2845): jesse | 2005-05-07 04:43:17 +0800
+ r15839@hualien: jesse | 2005-05-06 16:35:10 -0400
+ r14617@hualien: jesse | 2005-04-28 22:56:56 -0400
+ * Enabled proper message threading with RT
+
+
+
+ r17769@not (orig r2846): jesse | 2005-05-07 04:43:33 +0800
+ r15840@hualien: jesse | 2005-05-06 16:35:17 -0400
+ r14626@hualien: jesse | 2005-04-29 13:06:57 -0400
+ * Message threading improvements suggested by Florian Weimer
+
+
+ r17770@not (orig r2847): jesse | 2005-05-07 04:44:08 +0800
+ r15841@hualien: jesse | 2005-05-06 16:35:23 -0400
+ r14637@hualien: jesse | 2005-05-02 13:24:05 -0400
+ * Patch to SelfService UI to not display a preferences page unless the current user (ie all unprivileged users) has the right to do so.
+
+
+
+ r17771@not (orig r2848): jesse | 2005-05-07 04:44:24 +0800
+ r15842@hualien: jesse | 2005-05-06 16:35:28 -0400
+ r14639@hualien: jesse | 2005-05-02 13:30:18 -0400
+ * This be 3.4.2rc2
+
+
+ r17772@not (orig r2849): jesse | 2005-05-07 04:44:40 +0800
+ r15843@hualien: jesse | 2005-05-06 16:35:33 -0400
+ r15791@hualien: jesse | 2005-05-04 13:11:37 -0400
+ r15714@hualien (orig r2829): robert | 2005-05-03 23:52:32 -0400
+ r2835@bear: rspier | 2005-05-04T03:51:48.597852Z
+ fix typo in initialdata related to approval rejection
+
+
+
+
+
+ r17773@not (orig r2850): jesse | 2005-05-07 04:44:56 +0800
+ r15844@hualien: jesse | 2005-05-06 16:35:38 -0400
+ r15792@hualien: jesse | 2005-05-04 13:14:49 -0400
+ * This is 3.4.2
+
+
+ r17774@not (orig r2851): jesse | 2005-05-07 04:45:10 +0800
+ r15845@hualien: jesse | 2005-05-06 16:35:42 -0400
+ r15829@hualien: jesse | 2005-05-06 16:06:12 -0400
+ * Make sure that disabled custom fields aren't listed in Queue CF listings.
+
+
+
+ r17775@not (orig r2852): jesse | 2005-05-09 11:33:18 +0800
+ r16156@hualien: jesse | 2005-05-08 23:31:32 -0400
+ * Better reporting functionality; group by day, month, year
+
+ r17788@not (orig r2865): jesse | 2005-05-12 08:08:05 +0800
+ r16463@hualien: jesse | 2005-05-11 15:31:30 +0100
+ r16375@hualien: jesse | 2005-05-09 21:47:25 -0400
+ r15825@hualien (orig r2833): glasser | 2005-05-05 22:44:56 -0400
+ Require modern version of XML::RSS (versions prior to 1.02 do not
+ encode their output properly; 1.02 and 1.05 have the same prereqs
+ but 1.05 fixes some other bugs)
+
+
+
+ r17789@not (orig r2866): jesse | 2005-05-12 08:08:28 +0800
+ r16464@hualien: jesse | 2005-05-11 15:31:34 +0100
+ r16376@hualien: jesse | 2005-05-09 21:51:14 -0400
+ r16373@hualien: jesse | 2005-05-09 21:43:59 -0400
+ * Queue name case changes were accidentally generating a "Name Conflict" error
+
+
+
+
+ r17790@not (orig r2867): jesse | 2005-05-12 08:08:43 +0800
+ r16465@hualien: jesse | 2005-05-11 15:31:39 +0100
+ r16392@hualien: jesse | 2005-05-10 02:10:18 -0400
+ r16391@hualien (orig r2861): robert | 2005-05-10 01:54:35 -0400
+ r2875@bear: rspier | 2005-05-10T05:54:13.875334Z
+ quick and dirty fix (ok, not so quick, and not really so dirty)
+ for the recursive CreateTickets dataloss issue.
+
+
+
+
+
+ r17791@not (orig r2868): jesse | 2005-05-12 08:08:56 +0800
+ r16466@hualien: jesse | 2005-05-11 15:31:43 +0100
+ r16461@hualien: jesse | 2005-05-11 10:23:33 -0400
+ * Custom field values can now be integers
+
+
+ r17792@not (orig r2869): jesse | 2005-05-12 08:09:08 +0800
+ r16467@hualien: jesse | 2005-05-11 15:31:47 +0100
+ r16462@hualien: jesse | 2005-05-11 10:30:30 -0400
+ * Try harder to set a proper In-reply-to: header
+
+
+ r17793@not (orig r2870): jesse | 2005-05-12 08:09:21 +0800
+ r16469@hualien: jesse | 2005-05-11 15:35:20 +0100
+
+
+ r17794@not (orig r2871): jesse | 2005-05-12 08:09:34 +0800
+ r16470@hualien: jesse | 2005-05-11 17:55:59 +0100
+ * Fixed some bulk custom field deletion issues
+
+ r17805@not (orig r2882): jesse | 2005-05-14 08:42:08 +0800
+ r16738@hualien: jesse | 2005-05-13 01:40:25 +0100
+ * Clicking through saved search titlebars on the homepage now preserves format and rows per page
+
+ r17806@not (orig r2883): jesse | 2005-05-14 08:42:22 +0800
+ r16763@hualien: jesse | 2005-05-14 01:35:43 +0100
+ * Refactoring broke "group by id" in reports. Fixed
+
+
+ r16905@hualien (orig r2889): autrijus | 2005-05-18 08:18:58 -0400
+ * Refactor Cascaded type back to apply to all Select-ish custom fields,
+ by introducing a "Category" member field for a CFV.
+ r16906@hualien (orig r2890): autrijus | 2005-05-18 08:36:58 -0400
+ * stub for $cfv->Category and $cfv->SetCategory, as well as $cfv->Create
+ that accepts Category as key.
+ r16907@hualien (orig r2891): autrijus | 2005-05-18 08:42:36 -0400
+ * first cut at Category Admin UI -- User-side UI coming RSN
+ r16908@hualien (orig r2892): autrijus | 2005-05-18 08:47:05 -0400
+ * $cfv->Category and $cfv->SetCategory, via attributes.
+ r16909@hualien (orig r2893): autrijus | 2005-05-18 09:22:30 -0400
+ * cascaded select lands.
+ r16910@hualien (orig r2894): autrijus | 2005-05-18 10:23:00 -0400
+ * creation of CF with pattern
+ r16911@hualien (orig r2895): autrijus | 2005-05-18 13:32:21 -0400
+ * feedback for invalid CFs.
+ r16912@hualien (orig r2896): autrijus | 2005-05-18 13:40:02 -0400
+ * improved diagnostics on sanity tests.
+
+
+
+------------------------------------------------------------------------
+r2921 | jesse | 2005-05-22 18:35:09 -0400 (Sun, 22 May 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditTemplates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Checkbox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldCascaded
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldCombobox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueueSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SetupSessionCookie
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/combobox.js
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/SearchOptions.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Chart.png
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/DelegateRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/da.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/es.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ru.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Report
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Report/Tickets
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Report/Tickets/Entry.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Report/Tickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SavedSearch.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Googleish.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/07acl.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/12-search.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/15cf_pattern.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/19quicksearch.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r16997@hualien: jesse | 2005-05-22 17:04:18 -0400
+ r16883@hualien: jesse | 2005-05-16 17:06:12 -0400
+
+
+
+------------------------------------------------------------------------
+r2805 | jesse | 2005-04-24 17:18:35 -0400 (Sun, 24 Apr 2005) | 3 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+
+ r14466@hualien: jesse | 2005-04-24 17:16:41 -0400
+ * Small merge fixes from gugod
+
+------------------------------------------------------------------------
+r2804 | jesse | 2005-04-24 17:17:46 -0400 (Sun, 24 Apr 2005) | 102 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Objects.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/People.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/Configuration.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Checkbox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CreateTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MessageBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRT
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueueSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QuickCreate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Refresh
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RefreshHomepage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Section
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectAttachmentField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectBoolean
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateRelation
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectEqualityOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLang
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLinkType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectMatch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectResultsPerPage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectSortOrder
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectStatus
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketSortBy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketTypes
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectWatcherType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFieldImage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Submit
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Logout.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Quicksearch.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Search.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/SearchOptions.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Edit.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Error.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyPeople.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/MyDay.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Offline.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+
+ r14457@hualien: jesse | 2005-04-24 12:22:55 -0400
+
+ ==== Patch <Platano> level 1
+ Source: 5dbbeda1-96ee-0310-ab23-efcf45cb33fb:/rt/branches/PLATANO-EXPERIMENTAL-CSS:17302
+ Target: e417ac7c-1bcc-0310-8ffa-8f5827389a85:/rt/branches/PLATANO-EXPERIMENTAL-CSS:2736
+ (svn://svn.bestpractical.com/rt)
+ Log:
+ r15592@gugod: gugod | 2005-03-31 18:20:07 +0800
+ make local branch again
+ r15850@gugod: gugod | 2005-04-02 15:26:27 +0800
+ * indentation of some mess body :/
+ * Tweak on the div.title and div.mainbody
+ r15851@gugod: gugod | 2005-04-02 16:10:59 +0800
+ Make platano "HOME" screen has the same layout as 3.4R
+ r15856@gugod: gugod | 2005-04-02 17:39:59 +0800
+ remove <font> tag
+ r15857@gugod: gugod | 2005-04-02 17:40:48 +0800
+ TitleBox now use <div> instead of <table>
+ r15858@gugod: gugod | 2005-04-02 18:28:09 +0800
+ * wrap a div#all right inside <body> to avoid margin problem on IE
+ * use div.right-column and div.left-column to split columns.
+ r16237@gugod: gugod | 2005-04-08 12:27:57 +0800
+ setup login form semantic
+ r16238@gugod: gugod | 2005-04-08 12:39:25 +0800
+ So, semantics.
+ r16239@gugod: gugod | 2005-04-08 12:41:51 +0800
+ tweak css to match semantic of "HOME" page
+ r16240@gugod: gugod | 2005-04-08 12:45:55 +0800
+ put ossf new style into css linkn
+ r16298@gugod: gugod | 2005-04-10 09:37:10 +0800
+ Move TitleBox to Widgets/
+ r16299@gugod: gugod | 2005-04-10 09:53:44 +0800
+ /Elements/TitleBox -> /Widgets/TitleBox
+ r16300@gugod: gugod | 2005-04-10 09:55:50 +0800
+ /Elements/TitleBox -> /Widgets/TitleBox
+ r16431@gugod: gugod | 2005-04-11 01:11:44 +0800
+ use component calls with content, instead of TitleBoxStart + TitleBoxEnd
+ r16432@gugod: gugod | 2005-04-11 01:14:33 +0800
+ Use component calls with content instead of TitleBoxStart + TitleBoxEnd
+ r16438@gugod: gugod | 2005-04-11 08:36:11 +0800
+ deprecate TitleBoxStart + TitleBoxEnd
+ r16439@gugod: gugod | 2005-04-11 09:36:13 +0800
+ Use component call with contents to "TitleBox" instead of TitleBoxStart + TitleBoxEnd
+ r16662@gugod: gugod | 2005-04-13 22:16:49 +0800
+ * fix syntax error
+ r16680@gugod: gugod | 2005-04-15 13:40:19 +0800
+ fix syntax error
+ r16824@gugod: gugod | 2005-04-17 14:00:10 +0800
+ make it more xhtml-ish
+ r16825@gugod: gugod | 2005-04-17 14:01:55 +0800
+ use lowercase tag name
+ r16826@gugod: gugod | 2005-04-17 14:11:32 +0800
+ more xhtml-ish thing
+ r16827@gugod: gugod | 2005-04-17 14:54:09 +0800
+ lowercase A tags
+ r16828@gugod: gugod | 2005-04-17 14:55:21 +0800
+ lowercase tag
+ r16829@gugod: gugod | 2005-04-17 14:55:37 +0800
+ lowercase tag
+ r16830@gugod: gugod | 2005-04-17 14:56:07 +0800
+ indentation and SimpleSearchForm css
+ r16831@gugod: gugod | 2005-04-17 14:58:33 +0800
+ lowercase A tag
+ r16832@gugod: gugod | 2005-04-17 15:09:04 +0800
+ Massive Change
+
+ lowercase all html tags with this script:
+
+ #!/usr/bin/perl -i.bak -p
+ s{(</\w+>)}{lc($1)}eg;
+ s{(<\w+[\s>])}{lc($1)}eg;
+
+
+ r17292@gugod: gugod | 2005-04-24 18:30:56 +0800
+ * lowercase the attribute names
+ * double-quote attribute values
+ r17293@gugod: gugod | 2005-04-24 18:32:08 +0800
+ * lowercase attribute name
+ * double-quote attribute value
+ r17294@gugod: gugod | 2005-04-24 18:33:12 +0800
+ * lowercase attribute name
+ * double-quote attribute value
+ r17295@gugod: gugod | 2005-04-24 18:34:06 +0800
+ <b> -> <strong>
+ r17296@gugod: gugod | 2005-04-24 18:39:07 +0800
+ remove <font>
+ r17297@gugod: gugod | 2005-04-24 18:39:46 +0800
+ This should be more proper
+ r17298@gugod: gugod | 2005-04-24 18:44:06 +0800
+ * <b> -> <strong>
+ * <font> -> <span>
+ r17299@gugod: gugod | 2005-04-24 18:45:58 +0800
+ * <br> -> <br/>
+ * <i> -> <em>
+ r17300@gugod: gugod | 2005-04-24 18:48:28 +0800
+ * more xhtml tidy
+ r17301@gugod: gugod | 2005-04-24 18:52:32 +0800
+ * more xhtml tidy
+ r17302@gugod: gugod | 2005-04-24 18:55:06 +0800
+ xhtml tidy
+
+
+------------------------------------------------------------------------
+r2736 | jesse | 2005-04-17 16:14:08 -0400 (Sun, 17 Apr 2005) | 3 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Objects.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditTemplates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditUserComments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ModifyTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldLookupType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripAction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectStage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/People.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Scrips.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/ShowDependency
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Checkbox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CreateTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Error
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Menu
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRT
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueueSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Refresh
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Section
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectAttachmentField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectBoolean
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateRelation
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectEqualityOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLang
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLinkType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectMatch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectResultsPerPage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectSortOrder
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectStatus
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketSortBy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketTypes
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectWatcherType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLink
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Submit
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Logout.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/ossf.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Quicksearch.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Search.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/SearchOptions.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/NewListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectPersonType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Error.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/AddWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/BulkLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditPeople
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDependencies
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMemberOf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowReferences
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyPeople.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/MyDay.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Offline.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/TitleBoxStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+
+ r13180@hualien: jesse | 2005-04-17 16:12:50 -0400
+ * XHTML overhaul from Gugod
+
+------------------------------------------------------------------------
+r2735 | jesse | 2005-04-16 04:20:58 -0400 (Sat, 16 Apr 2005) | 3 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyAdminQueues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MySupportQueues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+
+ r13124@hualien: jesse | 2005-04-16 04:12:52 -0400
+ * Compilation fixes from the forward merge
+
+------------------------------------------------------------------------
+r2734 | jesse | 2005-04-16 04:20:52 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchesForObjects
+
+ r13123@hualien: jesse | 2005-04-16 03:13:10 -0400
+ r13022@hualien: jesse | 2005-04-16 02:28:15 -0400
+ r12964@hualien: jesse | 2005-04-15 18:46:40 -0400
+ * Merge fixes
+
+
+
+------------------------------------------------------------------------
+r2733 | jesse | 2005-04-16 04:20:45 -0400 (Sat, 16 Apr 2005) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SavedSearch.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/20savedsearch.t
+
+ r13122@hualien: jesse | 2005-04-16 03:13:07 -0400
+ r13021@hualien: jesse | 2005-04-16 02:28:10 -0400
+ r12963@hualien: jesse | 2005-04-15 17:46:02 -0400
+ r12958@hualien: jesse | 2005-04-15 17:30:30 -0400
+ r12957@hualien (orig r2634): tla | 2005-04-15 17:21:25 -0400
+ Added ability to update name as well as search parameters.
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2732 | jesse | 2005-04-16 04:20:31 -0400 (Sat, 16 Apr 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SavedSearch.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/12-search.t
+
+ r13121@hualien: jesse | 2005-04-16 03:13:04 -0400
+ r13020@hualien: jesse | 2005-04-16 02:28:04 -0400
+ r12962@hualien: jesse | 2005-04-15 17:45:57 -0400
+ r12950@hualien: jesse | 2005-04-15 16:47:32 -0400
+ * Cleaned up search tests
+
+
+
+
+------------------------------------------------------------------------
+r2731 | jesse | 2005-04-16 04:20:22 -0400 (Sat, 16 Apr 2005) | 80 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/config.layout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldText
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchesForObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SavedSearch.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SavedSearches.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/t.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/20savedsearch.t
+
+ r13120@hualien: jesse | 2005-04-16 03:12:59 -0400
+ r13019@hualien: jesse | 2005-04-16 02:27:57 -0400
+ r12961@hualien: jesse | 2005-04-15 17:43:00 -0400
+ r12947@hualien: jesse | 2005-04-15 15:31:30 -0400
+ r10706@hualien (orig r2541): tla | 2005-03-30 19:20:44 -0500
+ Fixes so that custom fields will pay attention to defaults, when the defaults
+ are specified.
+
+ r10782@hualien (orig r2542): robert | 2005-03-30 23:03:13 -0500
+ r2579@dog: rspier | 2005-03-30 19:58:06 -0800
+ RT-Ticket: 6572
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Remove second FHS layout
+
+
+ r11405@hualien (orig r2557): tla | 2005-04-01 23:16:13 -0500
+ A scheme which allows "t:123" URIs, for ticket specification.
+
+ r11785@hualien (orig r2582): tla | 2005-04-03 15:35:23 -0400
+ Get rid of LocalURIPrefix test. Would make this DTRT if I knew what TRT was.
+
+ r11854@hualien (orig r2584): tla | 2005-04-04 04:18:22 -0400
+ Add a simple sub to return the object class with the RT::Lib:: prefix
+ stripped off. Useful for displaying, e.g., "Ticket #35" or "Article #3".
+
+ r11856@hualien (orig r2586): tla | 2005-04-04 04:28:02 -0400
+ Add a callback for the use of RTFM
+
+ r12528@hualien (orig r2601): tla | 2005-04-10 19:49:31 -0400
+ User_Overlay: added OwnGroups method to get a collection of groups of which
+ the user is a member.
+ Record: revamped ObjectTypeStr according to suggestions.
+
+
+ r12529@hualien (orig r2602): tla | 2005-04-10 19:51:16 -0400
+ Upped version dependency of DBIx::SearchBuilder to 1.24 for unique records
+ feature in CustomFields.
+
+ r12530@hualien (orig r2603): tla | 2005-04-10 19:51:56 -0400
+ UNTESTED first cut at a saved search lib wrapper.
+
+ r12549@hualien (orig r2607): tla | 2005-04-11 14:47:03 -0400
+ Semi-tested, semi-working checkpoint. Formal tests to follow shortly.
+
+ r12760@hualien (orig r2608): tla | 2005-04-12 15:29:25 -0400
+ SavedSearch now shinily tested. API also changed somewhat.
+
+ r12761@hualien (orig r2609): tla | 2005-04-12 15:59:07 -0400
+ Made SavedSearches work, added error messages to SavedSearch, updated tests
+
+ r12779@hualien (orig r2617): tla | 2005-04-12 21:52:54 -0400
+ Added delete method & localized strings in SavedSearch
+ Made SavedSearches::LimitByPrivacy safe to call multiple times
+
+ r12781@hualien (orig r2619): autrijus | 2005-04-13 08:13:01 -0400
+ * VALUE="1" does not work on checkboxes; this renders the
+ default True setting on the "Set to Privliged" checkbox
+ in the "New User" screen useless, so new users are defaulted
+ to unprivileged even though the code shows that they are
+ intended to be so.
+ r12782@hualien (orig r2620): autrijus | 2005-04-13 11:19:54 -0400
+ * Never mind my last change -- I saw "VALUE = 1" and trigger-happily
+ inferred that it should default to CHECKED. Which is, of course,
+ not the case. Reverted.
+ r12784@hualien (orig r2622): tla | 2005-04-13 12:27:38 -0400
+ Lost a curly brace somewhere. Also noted location of tests for both files.
+
+ r12785@hualien (orig r2623): tla | 2005-04-13 13:32:41 -0400
+ Skeletal inline tests, to make autogen happy.
+
+ r12786@hualien (orig r2624): tla | 2005-04-13 13:42:51 -0400
+ Change to exclude saved searches meant for things other than tickets.
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2730 | jesse | 2005-04-16 04:20:15 -0400 (Sat, 16 Apr 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Standalone.pm
+
+ r13119@hualien: jesse | 2005-04-16 03:12:56 -0400
+ r13018@hualien: jesse | 2005-04-16 02:27:54 -0400
+ r12960@hualien: jesse | 2005-04-15 17:42:55 -0400
+ r12944@hualien: jesse | 2005-04-15 15:28:01 -0400
+ * Fixed Module::Refresh handling for standalone_httpd
+
+
+
+
+------------------------------------------------------------------------
+r2729 | jesse | 2005-04-16 04:20:09 -0400 (Sat, 16 Apr 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+
+ r13118@hualien: jesse | 2005-04-16 03:12:53 -0400
+ r13017@hualien: jesse | 2005-04-16 02:27:49 -0400
+ r12959@hualien: jesse | 2005-04-15 17:42:51 -0400
+ r12790@hualien: jesse | 2005-04-14 15:07:03 -0400
+ * Fix support for uploading attachmends in selfservice. -- HC Chien
+
+
+
+
+------------------------------------------------------------------------
+r2728 | jesse | 2005-04-16 04:19:57 -0400 (Sat, 16 Apr 2005) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/t.pm
+
+ r13117@hualien: jesse | 2005-04-16 03:12:50 -0400
+ r13016@hualien: jesse | 2005-04-16 02:27:44 -0400
+ r12956@hualien: jesse | 2005-04-15 17:09:33 -0400
+ r11404@hualien (orig r2556): tla | 2005-04-01 23:14:29 -0500
+ A scheme which allows "t:123" URIs, for ticket specification.
+
+
+
+
+
+------------------------------------------------------------------------
+r2727 | jesse | 2005-04-16 04:19:51 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+
+ r13116@hualien: jesse | 2005-04-16 03:12:47 -0400
+ r13015@hualien: jesse | 2005-04-16 02:27:39 -0400
+ r12954@hualien: jesse | 2005-04-15 17:06:11 -0400
+ * Simple search typo fix
+
+
+
+------------------------------------------------------------------------
+r2726 | jesse | 2005-04-16 04:19:45 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/12-search.t
+
+ r13115@hualien: jesse | 2005-04-16 03:12:43 -0400
+ r13014@hualien: jesse | 2005-04-16 02:27:35 -0400
+ r12788@hualien: jesse | 2005-04-13 14:25:49 -0400
+ Pulling forward from 3.4
+
+
+
+------------------------------------------------------------------------
+r2725 | jesse | 2005-04-16 04:19:38 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r13114@hualien: jesse | 2005-04-16 03:12:40 -0400
+ r13013@hualien: jesse | 2005-04-16 02:27:31 -0400
+ r12778@hualien: jesse | 2005-04-13 14:12:24 -0400
+ * SB dep bumped
+
+
+
+------------------------------------------------------------------------
+r2724 | jesse | 2005-04-16 04:19:22 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Googleish.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/19quicksearch.t
+
+ r13113@hualien: jesse | 2005-04-16 03:12:37 -0400
+ r13012@hualien: jesse | 2005-04-16 02:27:27 -0400
+ r12759@hualien: jesse | 2005-04-12 20:23:10 -0400
+ * Googleish simple search fixes
+
+
+
+------------------------------------------------------------------------
+r2723 | jesse | 2005-04-16 04:19:15 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Googleish.pm
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Quick.pm
+
+ r13112@hualien: jesse | 2005-04-16 03:12:33 -0400
+ r13011@hualien: jesse | 2005-04-16 02:27:23 -0400
+ r12752@hualien: jesse | 2005-04-12 09:51:37 -0400
+ Renamed "quick" to "googlish"
+
+
+
+------------------------------------------------------------------------
+r2722 | jesse | 2005-04-16 04:19:08 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Quick.pm
+
+ r13111@hualien: jesse | 2005-04-16 03:12:30 -0400
+ r13010@hualien: jesse | 2005-04-16 02:27:19 -0400
+ r12751@hualien: jesse | 2005-04-12 09:50:05 -0400
+ checkpoint
+
+
+
+------------------------------------------------------------------------
+r2721 | jesse | 2005-04-16 04:18:54 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/Tabs
+
+ r13110@hualien: jesse | 2005-04-16 03:12:27 -0400
+ r13009@hualien: jesse | 2005-04-16 02:27:15 -0400
+ r12750@hualien: jesse | 2005-04-12 09:48:51 -0400
+ * Tabs for SearchOptions
+
+
+
+------------------------------------------------------------------------
+r2720 | jesse | 2005-04-16 04:18:48 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Quick.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Simple.html
+
+ r13109@hualien: jesse | 2005-04-16 03:12:24 -0400
+ r13008@hualien: jesse | 2005-04-16 02:27:11 -0400
+ r12749@hualien: jesse | 2005-04-12 08:55:47 -0400
+ * Renamed quick search to simple search
+
+
+
+------------------------------------------------------------------------
+r2719 | jesse | 2005-04-16 04:18:41 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/SearchOptions.html
+
+ r13108@hualien: jesse | 2005-04-16 03:12:21 -0400
+ r13007@hualien: jesse | 2005-04-16 02:27:07 -0400
+ r12748@hualien: jesse | 2005-04-12 08:54:10 -0400
+ * Added support for search display preferences
+
+
+
+------------------------------------------------------------------------
+r2718 | jesse | 2005-04-16 04:18:27 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/02basic_web.t
+
+ r13107@hualien: jesse | 2005-04-16 03:12:17 -0400
+ r13006@hualien: jesse | 2005-04-16 02:27:03 -0400
+ r12747@hualien: jesse | 2005-04-12 07:10:14 -0400
+ * Allow click-to-sort on requestor/cc/admincc
+
+
+
+------------------------------------------------------------------------
+r2717 | jesse | 2005-04-16 04:18:18 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/02basic_web.t
+
+ r13106@hualien: jesse | 2005-04-16 03:12:13 -0400
+ r13005@hualien: jesse | 2005-04-16 02:26:58 -0400
+ r12532@hualien: jesse | 2005-04-11 10:47:38 -0400
+ * When entering time worked/left/etc, you can select minutes or hours
+
+
+
+------------------------------------------------------------------------
+r2716 | jesse | 2005-04-16 04:18:10 -0400 (Sat, 16 Apr 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+
+ r13105@hualien: jesse | 2005-04-16 03:12:10 -0400
+ r13004@hualien: jesse | 2005-04-16 02:26:55 -0400
+ r12256@hualien: jesse | 2005-04-08 16:51:49 -0400
+ * Removed "Search by group membership" from search UI since
+ it hit horrible scaling issues.
+
+
+
+------------------------------------------------------------------------
+r2715 | jesse | 2005-04-16 04:18:05 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+
+ r13104@hualien: jesse | 2005-04-16 03:12:07 -0400
+ r13003@hualien: jesse | 2005-04-16 02:26:51 -0400
+ r11941@hualien: jesse | 2005-04-06 15:26:40 -0400
+ * Search "Order By" needed a more greedy matching operation; too many values were selected
+
+
+
+------------------------------------------------------------------------
+r2714 | jesse | 2005-04-16 04:17:53 -0400 (Sat, 16 Apr 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+
+ r13103@hualien: jesse | 2005-04-16 03:12:03 -0400
+ r13002@hualien: jesse | 2005-04-16 02:26:47 -0400
+ r11938@hualien: jesse | 2005-04-06 15:03:57 -0400
+ Merging forward a clicky-headers fix from 3.4
+
+
+
+
+------------------------------------------------------------------------
+r2713 | jesse | 2005-04-16 04:17:46 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/20-sort-by-requestor.t
+
+ r13102@hualien: jesse | 2005-04-16 03:11:59 -0400
+ r13001@hualien: jesse | 2005-04-16 02:26:39 -0400
+ r11663@hualien: jesse | 2005-04-03 05:11:09 -0400
+ * Added support for sort by requestor/watcher
+
+
+
+------------------------------------------------------------------------
+r2712 | jesse | 2005-04-16 04:17:39 -0400 (Sat, 16 Apr 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+
+ r13101@hualien: jesse | 2005-04-16 03:11:56 -0400
+ r13000@hualien: jesse | 2005-04-16 02:26:36 -0400
+ r11468@hualien: jesse | 2005-04-02 04:41:10 -0500
+ r10907@hualien: jesse | 2005-04-01 17:15:55 +0800
+ * Added "make depends" as an alias for "make fixdeps" for Autrijus
+
+
+
+
+------------------------------------------------------------------------
+r2711 | jesse | 2005-04-16 04:17:32 -0400 (Sat, 16 Apr 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+
+ r13100@hualien: jesse | 2005-04-16 03:11:52 -0400
+ r12999@hualien: jesse | 2005-04-16 02:26:32 -0400
+ r11467@hualien: jesse | 2005-04-02 04:40:59 -0500
+ r10781@hualien: jesse | 2005-03-31 16:49:00 +0800
+ * Fixes to seph's rtname regex
+
+
+
+
+------------------------------------------------------------------------
+r2710 | jesse | 2005-04-16 04:17:18 -0400 (Sat, 16 Apr 2005) | 15 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/19-rtname.t
+
+ r13099@hualien: jesse | 2005-04-16 03:11:48 -0400
+ r12998@hualien: jesse | 2005-04-16 02:26:28 -0400
+ r11466@hualien: jesse | 2005-04-02 04:40:44 -0500
+ r10696@hualien: jesse | 2005-03-30 18:19:21 +0800
+ RT-Ticket: 6544
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * More graceful handling for historical $rtname tags
+ -- From seph
+
+
+
+
+
+------------------------------------------------------------------------
+r2709 | jesse | 2005-04-16 04:17:12 -0400 (Sat, 16 Apr 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueueSummary
+
+ r13098@hualien: jesse | 2005-04-16 03:11:45 -0400
+ r12997@hualien: jesse | 2005-04-16 02:26:24 -0400
+ r11461@hualien: jesse | 2005-04-02 00:53:31 -0500
+ * Made overview search for Stalled in addition to open and new. Sometime,
+ we should make this code smarter
+
+
+
+------------------------------------------------------------------------
+r2708 | jesse | 2005-04-16 04:17:06 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+
+ r13097@hualien: jesse | 2005-04-16 03:10:51 -0400
+ r12996@hualien: jesse | 2005-04-16 02:26:02 -0400
+ r11354@hualien: jesse | 2005-04-01 07:43:14 -0500
+ * Updated webrt.css to resture the black text color for titleboxright text
+
+
+
+------------------------------------------------------------------------
+r2707 | jesse | 2005-04-16 04:17:00 -0400 (Sat, 16 Apr 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+
+ r13096@hualien: jesse | 2005-04-16 03:10:48 -0400
+ r12995@hualien: jesse | 2005-04-16 02:25:59 -0400
+ r11323@hualien: jesse | 2005-04-01 04:52:16 -0500
+ r9577@hualien (orig r2498): clkao | 2005-03-23 17:03:02 +0800
+ Don't html-escape loc() when building FormatString.
+
+
+
+
+------------------------------------------------------------------------
+r2706 | jesse | 2005-04-16 04:16:52 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Quick.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Quick.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/19quicksearch.t
+
+ r13095@hualien: jesse | 2005-04-16 03:10:43 -0400
+ r12994@hualien: jesse | 2005-04-16 02:25:55 -0400
+ r10695@hualien: jesse | 2005-03-30 05:14:59 -0500
+ * First version of B4 quicksearch from simon
+
+
+
+------------------------------------------------------------------------
+r2705 | jesse | 2005-04-16 04:16:37 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/02basic_web.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/03web_compiliation_errors.t
+
+ r13094@hualien: jesse | 2005-04-16 03:10:38 -0400
+ r12993@hualien: jesse | 2005-04-16 02:23:51 -0400
+ r10286@hualien: jesse | 2005-03-28 05:49:49 -0500
+ * Merge forward from 3.4-RELEASE
+
+
+
+------------------------------------------------------------------------
+r2704 | jesse | 2005-04-16 04:16:31 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditQuery
+
+ r13093@hualien: jesse | 2005-04-16 03:08:13 -0400
+ r12992@hualien: jesse | 2005-04-16 02:23:47 -0400
+ r10273@hualien: jesse | 2005-03-28 03:43:00 -0500
+ * Completed deliverable B3; Significant cleanup of search UI
+
+
+
+------------------------------------------------------------------------
+r2703 | jesse | 2005-04-16 04:16:25 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+
+ r13092@hualien: jesse | 2005-04-16 03:08:05 -0400
+ r12989@hualien: jesse | 2005-04-16 02:23:35 -0400
+ r10001@hualien: jesse | 2005-03-24 23:56:04 -0500
+ Fixed a "Save" label
+
+
+
+------------------------------------------------------------------------
+r2702 | jesse | 2005-04-16 04:16:19 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+
+ r13091@hualien: jesse | 2005-04-16 03:08:02 -0400
+ r12988@hualien: jesse | 2005-04-16 02:23:31 -0400
+ r9579@hualien: jesse | 2005-03-23 06:15:02 -0500
+ * Typo fixes
+
+
+
+------------------------------------------------------------------------
+r2701 | jesse | 2005-04-16 04:16:12 -0400 (Sat, 16 Apr 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldSelect
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+
+ r13090@hualien: jesse | 2005-04-16 03:07:58 -0400
+ r12987@hualien: jesse | 2005-04-16 02:23:26 -0400
+ r9576@hualien: jesse | 2005-03-23 05:51:06 -0500
+ * Added support for bulk update of custom fields
+ * Added support for search paging in the bulk update UI
+
+
+
+------------------------------------------------------------------------
+r2700 | jesse | 2005-04-16 04:15:57 -0400 (Sat, 16 Apr 2005) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/list.js
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18custom_frontpage.t
+
+ r13089@hualien: jesse | 2005-04-16 03:07:53 -0400
+ r12986@hualien: jesse | 2005-04-16 02:23:22 -0400
+ r9575@hualien: jesse | 2005-03-23 03:46:50 -0500
+ r9573@hualien (orig r2496): clkao | 2005-03-23 13:22:08 +0800
+ Fix js (still disabled for now).
+ Make test pass.
+ r9574@hualien (orig r2497): clkao | 2005-03-23 14:58:16 +0800
+ Fix the regexp injecting checkbox into bulk edit format.
+
+
+
+
+------------------------------------------------------------------------
+r2699 | jesse | 2005-04-16 04:15:49 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+
+ r13088@hualien: jesse | 2005-04-16 03:01:59 -0400
+ r12985@hualien: jesse | 2005-04-16 02:23:17 -0400
+ r9563@hualien: jesse | 2005-03-22 12:12:39 -0500
+ Refactor bulk update to use more standard components
+
+
+
+------------------------------------------------------------------------
+r2698 | jesse | 2005-04-16 04:15:42 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+
+ r13087@hualien: jesse | 2005-04-16 03:01:56 -0400
+ r12984@hualien: jesse | 2005-04-16 02:23:14 -0400
+ r9554@hualien: jesse | 2005-03-22 09:01:26 -0500
+ * When creating a ticket, the footer sometimes wouldn't be displayed
+
+
+
+------------------------------------------------------------------------
+r2697 | jesse | 2005-04-16 04:15:34 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+
+ r13086@hualien: jesse | 2005-04-16 03:01:53 -0400
+ r12983@hualien: jesse | 2005-04-16 02:23:10 -0400
+ r9553@hualien: jesse | 2005-03-22 09:00:21 -0500
+ Certain search-related actions wouldn't display the page footer due to calls to $m->abort();
+
+
+
+------------------------------------------------------------------------
+r2696 | jesse | 2005-04-16 04:15:21 -0400 (Sat, 16 Apr 2005) | 13 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18custom_frontpage.t
+
+ r13085@hualien: jesse | 2005-04-16 03:01:49 -0400
+ r12982@hualien: jesse | 2005-04-16 02:23:06 -0400
+ r8817@hualien: jesse | 2005-03-16 23:59:40 -0500
+ r8804@hualien (orig r2472): clkao | 2005-03-16 01:31:43 -0500
+ Make the error more readable and prevent side-effect on failed
+ savesearch.
+ r8805@hualien (orig r2473): clkao | 2005-03-16 01:47:42 -0500
+ Test if saved search is listed in available items in RT at a glance
+ preferences.
+
+
+
+
+------------------------------------------------------------------------
+r2695 | jesse | 2005-04-16 04:15:15 -0400 (Sat, 16 Apr 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+
+ r13084@hualien: jesse | 2005-04-16 03:01:46 -0400
+ r12981@hualien: jesse | 2005-04-16 02:23:03 -0400
+ r8816@hualien: jesse | 2005-03-16 23:58:56 -0500
+ Minor cleanups based on customer feedback
+
+
+
+------------------------------------------------------------------------
+r2694 | jesse | 2005-04-16 04:15:07 -0400 (Sat, 16 Apr 2005) | 29 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/initialdata
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/QUEBEC
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/QUEBEC/content
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18custom_frontpage.t
+
+ r13083@hualien: jesse | 2005-04-16 03:01:40 -0400
+ r12980@hualien: jesse | 2005-04-16 02:17:57 -0400
+ r8728@hualien: jesse | 2005-03-14 12:29:07 -0500
+ r8416@hualien (orig r2349): clkao | 2005-03-13 02:20:34 -0500
+ Do SeeQueue permission filtering in AddRecord so both Next() and
+ ItemsArrayRef work.
+ r8452@hualien (orig r2351): jesse | 2005-03-14 01:32:53 -0500
+ r8450@hualien: jesse | 2005-03-14 01:30:59 -0500
+ Slight style cleanup to page actions.
+
+ r8454@hualien (orig r2352): jesse | 2005-03-14 01:37:23 -0500
+ r8453@hualien: jesse | 2005-03-14 01:37:00 -0500
+ Hilighted menu options are now the right size
+
+ r8718@hualien (orig r2465): clkao | 2005-03-14 11:35:59 -0500
+ Localise format in predefined search with __l{text}__.
+ r8719@hualien (orig r2466): clkao | 2005-03-14 11:37:56 -0500
+ Disable js for now.
+ r8720@hualien (orig r2467): clkao | 2005-03-14 11:48:37 -0500
+ * Rename "My Requests" => "Unowned Tickets".
+ * __l{text}__ => __loc(text)__
+ r8721@hualien (orig r2468): clkao | 2005-03-14 12:01:08 -0500
+ Upgrade path to QUEBEC.
+ r8722@hualien (orig r2469): clkao | 2005-03-14 12:17:02 -0500
+ Allow AutoSave mode for SelectionBox.
+
+
+
+
+------------------------------------------------------------------------
+r2693 | jesse | 2005-04-16 04:14:56 -0400 (Sat, 16 Apr 2005) | 15 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+
+ r13082@hualien: jesse | 2005-04-16 03:01:26 -0400
+ r12976@hualien: jesse | 2005-04-16 02:17:39 -0400
+ r6768@hualien: jesse | 2005-03-11 18:59:52 -0500
+ r6766@hualien (orig r2341): clkao | 2005-03-11 18:58:39 -0500
+ r2469@ab: clkao | 2005-03-12 07:41:47 +0800
+ Properly check permission with the correct object on savesearch.
+
+ r6767@hualien (orig r2342): clkao | 2005-03-11 18:59:06 -0500
+ r2470@ab: clkao | 2005-03-12 07:56:47 +0800
+ Move _parse_saved_search and _load_container_object to RT::Interface::Web.
+
+
+
+
+
+------------------------------------------------------------------------
+r2692 | jesse | 2005-04-16 04:14:42 -0400 (Sat, 16 Apr 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Quicksearch.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+
+ r13081@hualien: jesse | 2005-04-16 03:01:21 -0400
+ r12975@hualien: jesse | 2005-04-16 02:16:37 -0400
+ r6764@hualien: jesse | 2005-03-11 18:54:06 -0500
+ A bunch of small UI cleanups per customer request.
+
+
+
+
+------------------------------------------------------------------------
+r2691 | jesse | 2005-04-16 04:14:35 -0400 (Sat, 16 Apr 2005) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRT
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+
+ r13080@hualien: jesse | 2005-04-16 03:01:17 -0400
+ r12974@hualien: jesse | 2005-04-16 02:16:34 -0400
+ r6745@hualien: jesse | 2005-03-10 17:13:01 -0500
+ r6660@hualien (orig r2333): clkao | 2005-03-10 09:52:26 -0500
+ * Make AllowedComponent configurable in RT_Config.
+ * Check selected are within given Available ones.
+ * Check permission when saving searches for rt::system.
+
+
+
+
+
+------------------------------------------------------------------------
+r2690 | jesse | 2005-04-16 04:14:26 -0400 (Sat, 16 Apr 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Elements
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Elements/Tabs
+
+ r13079@hualien: jesse | 2005-04-16 03:01:14 -0400
+ r12973@hualien: jesse | 2005-04-16 02:16:31 -0400
+ r6650@hualien: jesse | 2005-03-09 14:13:09 -0500
+ r6649@hualien (orig r2331): clkao | 2005-03-09 13:37:15 -0500
+ Missing tabs.
+
+
+
+
+------------------------------------------------------------------------
+r2689 | jesse | 2005-04-16 04:14:17 -0400 (Sat, 16 Apr 2005) | 19 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/initialdata
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/list.js
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Quicksearch.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Search.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18custom_frontpage.t
+
+ r13078@hualien: jesse | 2005-04-16 03:01:09 -0400
+ r12972@hualien: jesse | 2005-04-16 02:16:27 -0400
+ r6648@hualien: jesse | 2005-03-09 13:32:18 -0500
+ r6638@hualien (orig r2321): clkao | 2005-03-08 23:44:47 -0500
+ Kill all referers and "go back"s.
+ r6639@hualien (orig r2322): clkao | 2005-03-09 00:15:21 -0500
+ Move selectAll to form onSubmit.
+ r6640@hualien (orig r2323): clkao | 2005-03-09 00:20:13 -0500
+ Oops.
+ r6641@hualien (orig r2324): clkao | 2005-03-09 00:44:38 -0500
+ More SelectionBox UI tweaks.
+ r6646@hualien (orig r2329): clkao | 2005-03-09 13:04:48 -0500
+ Make SummaryRow a user preference.
+ r6647@hualien (orig r2330): clkao | 2005-03-09 13:30:32 -0500
+ MyRT SubTabs.
+
+
+
+
+------------------------------------------------------------------------
+r2688 | jesse | 2005-04-16 04:14:08 -0400 (Sat, 16 Apr 2005) | 21 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/initialdata
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRT
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MySupportQueues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Quicksearch.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Search.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/17custom_search.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18custom_frontpage.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r13077@hualien: jesse | 2005-04-16 03:01:01 -0400
+ r12971@hualien: jesse | 2005-04-16 02:16:09 -0400
+ r6625@hualien: jesse | 2005-03-08 23:18:58 -0500
+ r6570@hualien (orig r2307): clkao | 2005-03-07 11:59:06 -0500
+ Require newer Test::WWW::Mechanize.
+ r6571@hualien (orig r2308): clkao | 2005-03-07 12:33:59 -0500
+ merge down
+ r6572@hualien (orig r2309): clkao | 2005-03-07 13:30:20 -0500
+ Saving search to RT::System now works.
+ r6573@hualien (orig r2310): clkao | 2005-03-07 13:35:18 -0500
+ Restore the original ordering after tests.
+ r6574@hualien (orig r2311): clkao | 2005-03-07 13:47:39 -0500
+ Proper locs.
+ r6605@hualien (orig r2314): clkao | 2005-03-08 12:11:43 -0500
+ Misc UI changes, and according test tweaks.
+ r6606@hualien (orig r2315): clkao | 2005-03-08 13:05:59 -0500
+ oops
+
+
+
+
+------------------------------------------------------------------------
+r2687 | jesse | 2005-04-16 04:14:01 -0400 (Sat, 16 Apr 2005) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18custom_frontpage.t
+
+ r13076@hualien: jesse | 2005-04-16 03:00:58 -0400
+ r12970@hualien: jesse | 2005-04-16 02:16:06 -0400
+ r6495@hualien: jesse | 2005-03-06 17:46:04 -0500
+ r6412@hualien (orig r2294): clkao | 2005-03-06 10:50:23 -0500
+ r2419@ab: clkao | 2005-03-06 23:48:51 +0800
+ Minimum regression tests for frontpage customization.
+
+
+
+
+
+------------------------------------------------------------------------
+r2686 | jesse | 2005-04-16 04:13:51 -0400 (Sat, 16 Apr 2005) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/list.js
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/SelectionBox
+
+ r13075@hualien: jesse | 2005-04-16 03:00:50 -0400
+ r12969@hualien: jesse | 2005-04-16 02:16:02 -0400
+ r6494@hualien: jesse | 2005-03-06 17:46:01 -0500
+ r6411@hualien (orig r2293): clkao | 2005-03-06 10:10:26 -0500
+ Land SelectionBox Widget, which works for both js and cgi.
+ Make the frontpage customization UI use SelectionBox.
+
+
+
+
+------------------------------------------------------------------------
+r2685 | jesse | 2005-04-16 04:13:38 -0400 (Sat, 16 Apr 2005) | 31 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/initialdata
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyAdminQueues
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRT
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRequests
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MySupportQueues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyTickets
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueueSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RefreshHomepage
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowSearch
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/class.js
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/list.js
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/MyRT.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Quicksearch.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Prefs/Search.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditSearches
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SearchesForObject
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchesForObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/System.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/06mailgateway.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/07acl.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/08web_cf_access.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/17custom_search.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+
+ r13074@hualien: jesse | 2005-04-16 02:55:49 -0400
+ r12968@hualien: jesse | 2005-04-16 02:13:13 -0400
+ r6370@hualien: jesse | 2005-03-05 19:28:47 -0500
+ r6273@hualien (orig r2277): clkao | 2005-03-05 01:12:50 -0500
+ * Make regression does not require being root anymore.
+ * Global pre-defined searches are now attributes of RT::System.
+ * Users can now override display options for pre-defined searches.
+ * Users can now configure Quicksearch portlet.
+ * New portlets: MyAdminQueues, MySupportQueues.
+ * Users can now configure RT at a glance with pre-defined searches,
+ allowed components, and saves searches.
+
+ * $MyTicketsLength and $MyRequestsLength is no longer needed in RT::Config.
+ * Clean up Search/Build.html for reduce duplicated code.
+ * Fix title_right_href in Titlebox.
+ * Cleanup original Quicksearch to Use QueueSummary for summary portlets.
+
+ TODO:
+ * Saving search for RT::System needs to do 'Saved Search - name' instead
+ of SavedSearch
+
+ r6274@hualien (orig r2278): clkao | 2005-03-05 09:31:05 -0500
+ r2397@ab: clkao | 2005-03-05 22:29:18 +0800
+ Fix a bug where MyRT gets only one component in a pane.
+
+ r6275@hualien (orig r2279): clkao | 2005-03-05 09:31:25 -0500
+
+
+
+
+
+------------------------------------------------------------------------
+r2628 | jesse | 2005-04-15 13:13:42 -0400 (Fri, 15 Apr 2005) | 3 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+
+ r12803@hualien: jesse | 2005-04-15 13:11:33 -0400
+ * Titlebox fixes from Gugod
+
+------------------------------------------------------------------------
+r2621 | autrijus | 2005-04-13 11:35:31 -0400 (Wed, 13 Apr 2005) | 1 line
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+
+* typo - syntax fixes
+------------------------------------------------------------------------
+r2604 | jesse | 2005-04-11 10:11:27 -0400 (Mon, 11 Apr 2005) | 43 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Error
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QuickCreate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditQuery
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditSearches
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCriteria
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Error.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyLinks.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyPeople.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+
+ r12526@hualien: jesse | 2005-04-11 09:45:37 -0400
+ ==== Patch <Platano> level 6
+ Source: 5dbbeda1-96ee-0310-ab23-efcf45cb33fb:/rt/branches/PLATANO-EXPERIMENTAL-CSS:16439
+ Target: e417ac7c-1bcc-0310-8ffa-8f5827389a85:/rt/branches/PLATANO-EXPERIMENTAL-CSS:2599
+ (svn://svn.bestpractical.com/rt)
+ Log:
+ r15592@DHCP-21126: gugod | 2005-03-31 18:20:07 +0800
+ make local branch again
+ r15850@DHCP-21126: gugod | 2005-04-02 15:26:27 +0800
+ * indentation of some mess body :/
+ * Tweak on the div.title and div.mainbody
+ r15851@DHCP-21126: gugod | 2005-04-02 16:10:59 +0800
+ Make platano "HOME" screen has the same layout as 3.4R
+ r15856@DHCP-21126: gugod | 2005-04-02 17:39:59 +0800
+ remove <font> tag
+ r15857@DHCP-21126: gugod | 2005-04-02 17:40:48 +0800
+ TitleBox now use <div> instead of <table>
+ r15858@DHCP-21126: gugod | 2005-04-02 18:28:09 +0800
+ * wrap a div#all right inside <body> to avoid margin problem on IE
+ * use div.right-column and div.left-column to split columns.
+ r16237@DHCP-21126: gugod | 2005-04-08 12:27:57 +0800
+ setup login form semantic
+ r16238@DHCP-21126: gugod | 2005-04-08 12:39:25 +0800
+ So, semantics.
+ r16239@DHCP-21126: gugod | 2005-04-08 12:41:51 +0800
+ tweak css to match semantic of "HOME" page
+ r16240@DHCP-21126: gugod | 2005-04-08 12:45:55 +0800
+ put ossf new style into css linkn
+ r16298@DHCP-21126: gugod | 2005-04-10 09:37:10 +0800
+ Move TitleBox to Widgets/
+ r16299@DHCP-21126: gugod | 2005-04-10 09:53:44 +0800
+ /Elements/TitleBox -> /Widgets/TitleBox
+ r16300@DHCP-21126: gugod | 2005-04-10 09:55:50 +0800
+ /Elements/TitleBox -> /Widgets/TitleBox
+ r16431@DHCP-21126: gugod | 2005-04-11 01:11:44 +0800
+ use component calls with content, instead of TitleBoxStart + TitleBoxEnd
+ r16432@DHCP-21126: gugod | 2005-04-11 01:14:33 +0800
+ Use component calls with content instead of TitleBoxStart + TitleBoxEnd
+ r16438@DHCP-21126: gugod | 2005-04-11 08:36:11 +0800
+ deprecate TitleBoxStart + TitleBoxEnd
+ r16439@DHCP-21126: gugod | 2005-04-11 09:36:13 +0800
+ Use component call with contents to "TitleBox" instead of TitleBoxStart + TitleBoxEnd
+
+------------------------------------------------------------------------
+r2599 | jesse | 2005-04-10 15:50:41 -0400 (Sun, 10 Apr 2005) | 34 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/ShowDependency
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Error
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyTickets
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QuickCreate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBox
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxEnd
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxStart
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/common.css
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/ossf.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditQuery
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditSearches
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCriteria
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Error.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyLinks.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyPeople.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/TitleBox
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/TitleBoxEnd
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Widgets/TitleBoxStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+
+ r12352@hualien: jesse | 2005-04-10 15:48:42 -0400
+ * Updates from Gugod
+
+ r15592@kybristedi: gugod | 2005-03-31 18:20:07 +0800
+ make local branch again
+ r15850@kybristedi: gugod | 2005-04-02 15:26:27 +0800
+ * indentation of some mess body :/
+ * Tweak on the div.title and div.mainbody
+ r15851@kybristedi: gugod | 2005-04-02 16:10:59 +0800
+ Make platano "HOME" screen has the same layout as 3.4R
+ r15856@kybristedi: gugod | 2005-04-02 17:39:59 +0800
+ remove <font> tag
+ r15857@kybristedi: gugod | 2005-04-02 17:40:48 +0800
+ TitleBox now use <div> instead of <table>
+ r15858@kybristedi: gugod | 2005-04-02 18:28:09 +0800
+ * wrap a div#all right inside <body> to avoid margin problem on IE
+ * use div.right-column and div.left-column to split columns.
+ r16237@kybristedi: gugod | 2005-04-08 12:27:57 +0800
+ setup login form semantic
+ r16238@kybristedi: gugod | 2005-04-08 12:39:25 +0800
+ So, semantics.
+ r16239@kybristedi: gugod | 2005-04-08 12:41:51 +0800
+ tweak css to match semantic of "HOME" page
+ r16240@kybristedi: gugod | 2005-04-08 12:45:55 +0800
+ put ossf new style into css linkn
+ r16298@kybristedi: gugod | 2005-04-10 09:37:10 +0800
+ Move TitleBox to Widgets/
+ r16299@kybristedi: gugod | 2005-04-10 09:53:44 +0800
+ /Elements/TitleBox -> /Widgets/TitleBox
+ r16300@kybristedi: gugod | 2005-04-10 09:55:50 +0800
+ /Elements/TitleBox -> /Widgets/TitleBox
+
+
+
+------------------------------------------------------------------------
+r2544 | jesse | 2005-03-31 04:58:31 -0500 (Thu, 31 Mar 2005) | 3 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+
+ r10801@hualien: jesse | 2005-03-31 17:57:46 +0800
+ * bad paren from bogus merge
+
+------------------------------------------------------------------------
+r2535 | jesse | 2005-03-28 05:34:36 -0500 (Mon, 28 Mar 2005) | 82 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_SiteConfig.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MessageBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CurrentUser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Standalone.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/02basic_web.t
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/03basic_web.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/03web_compiliation_errors.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r10275@hualien: jesse | 2005-03-28 16:51:44 +0800
+ r10274@hualien: jesse | 2005-03-28 16:49:31 +0800
+ r8746@hualien: jesse | 2005-03-15 14:07:30 +0800
+ Refactored the top-level admin menu to make it easier to add and remove options
+ r8807@hualien: jesse | 2005-03-16 16:03:29 +0800
+ r8806@hualien (orig r2474): alexmv | 2005-03-16 02:53:59 -0500
+ * Users and groups can have transactions; ShowTicket isn't needed to
+ see these
+
+
+ r8808@hualien: jesse | 2005-03-16 16:07:57 +0800
+ Ticket: 6537
+ RT-Status: resolved
+
+ Explicit transaction description for changed passwords
+
+ r9502@hualien: jesse | 2005-03-22 01:07:59 +0800
+ * Refactored standalone_httpd to use HTTP::Server::Simple::Mason
+ * Moved some configuration from webmux.pl to RT::Interface::Web::Handler
+ * Split apart some of the web tests for better isolation
+ r9555@hualien: jesse | 2005-03-22 22:15:24 +0800
+ * Properly set binmode for the standalone_httpd server. This makes
+ UTF8 text that could be misinterpreted as Latin1 behave correctly.
+
+ r9584@hualien: jesse | 2005-03-24 10:28:28 +0800
+ Ticket: 6558
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Removed an extra </td> tag. Thanks to Steve Turner
+
+ r10203@hualien: jesse | 2005-03-27 13:01:43 +0800
+ * Corrected HTTP::Server::Simple dependencies
+ r10204@hualien: jesse | 2005-03-27 13:08:01 +0800
+ * Improved generic messages returned by RT::Record->_Set()
+ * Corrected RT::Record->_Set to return a Class::ReturnValue, rather than butchering it
+ into an array. (This means that RT::Ticket->SetPriority can now be evaluated in boolean
+ or list context, like it was supposed to be)
+ r10205@hualien: jesse | 2005-03-27 13:10:21 +0800
+ * Slightly better introductory text on user and queue listings
+ r10206@hualien: jesse | 2005-03-27 13:11:53 +0800
+ Improvements to big textareas to make sure they don't force browser windows to scroll.
+ r10207@hualien: jesse | 2005-03-27 13:13:26 +0800
+ * Bugfixes to CachedGroupMember->SetDisabled unmasked by the RT::Record fixes
+ r10208@hualien: jesse | 2005-03-27 13:14:57 +0800
+ * Fixing tests to not depend on a deprecated API
+ r10209@hualien: jesse | 2005-03-27 13:16:31 +0800
+ * Updated RT::User regression tests to not expect a pristine database
+ * Updated RT::User->_Set to return more apropriate results messages (no more listing the actor)
+ r10210@hualien: jesse | 2005-03-27 13:17:02 +0800
+ * Updated RT::Ticket->_Set to return more appropriate status messages
+ r10211@hualien: jesse | 2005-03-27 13:18:07 +0800
+ * Updated RT::Transaction to return better Foo changed from "bar" to "baz" messages
+ r10221@hualien: jesse | 2005-03-27 14:15:36 +0800
+ Ticket: 6565
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Overhauled README to be more correct, friendly and a bit cleaner
+ r10223@hualien: jesse | 2005-03-27 14:33:12 +0800
+ Ticket: 6566
+ RT-Status: resolved
+ RT-Update: correspond
+
+ * Added a bit of documentation to RT_SiteConfig.pm -- Thanks to David Glasser
+
+ r10225@hualien: jesse | 2005-03-27 14:43:25 +0800
+ * SiteConfig typofix
+ r10227@hualien: jesse | 2005-03-27 15:00:51 +0800
+ * Explicitly give the login box an id/name so passwords are not saved on user edit pages
+ r10229@hualien: jesse | 2005-03-27 15:39:30 +0800
+ * Added ALT text for BPS corporate logo -- Suggested by Jedi during YAPC.TW 2005
+
+ r10231@hualien: jesse | 2005-03-27 17:12:44 +0800
+ * More fixes for status messages broken in this morning's commit
+ r10258@hualien: jesse | 2005-03-28 01:05:27 +0800
+ * Record Message-Id when creating attachment records
+ r10271@hualien: jesse | 2005-03-28 15:49:42 +0800
+ * A bunch of minor search query builder cleanup and loc improvements
+
+
+
+------------------------------------------------------------------------
+r2464 | jesse | 2005-03-14 03:05:12 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Groups.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Tickets.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Transactions.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Users.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/CustomFields.html
+
+ r8625@hualien: jesse | 2005-03-14 02:47:00 -0500
+ r8513@hualien: jesse | 2005-03-14 02:29:16 -0500
+ r8456@hualien: jesse | 2005-03-14 02:20:50 -0500
+ Overhaul of custom field editing code to make it easier to add custom fields
+ for new object types
+
+
+
+------------------------------------------------------------------------
+r2463 | jesse | 2005-03-14 03:05:03 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+
+ r8623@hualien: jesse | 2005-03-14 02:43:35 -0500
+ r8511@hualien: jesse | 2005-03-14 02:29:11 -0500
+ r8450@hualien: jesse | 2005-03-14 01:30:59 -0500
+ Slight style cleanup to page actions.
+
+
+
+------------------------------------------------------------------------
+r2462 | jesse | 2005-03-14 03:04:49 -0500 (Mon, 14 Mar 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips_Overlay.pm
+
+ r8622@hualien: jesse | 2005-03-14 02:43:33 -0500
+ r8510@hualien: jesse | 2005-03-14 02:29:08 -0500
+ r6769@hualien: jesse | 2005-03-11 20:16:42 -0500
+ Now when running scrips on a disabled queue, run global scrips as well as per-queue scrips.
+
+
+
+
+
+------------------------------------------------------------------------
+r2461 | jesse | 2005-03-14 03:04:43 -0500 (Mon, 14 Mar 2005) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+
+ r8621@hualien: jesse | 2005-03-14 02:43:31 -0500
+ r8509@hualien: jesse | 2005-03-14 02:29:04 -0500
+ r6651@hualien: jesse | 2005-03-09 17:23:54 -0500
+ RT-Ticket: 6459
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Sanity fixes for _AddLink's API from Todd Chapman
+
+
+
+
+------------------------------------------------------------------------
+r2460 | jesse | 2005-03-14 03:04:38 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Objects.html
+
+ r8620@hualien: jesse | 2005-03-14 02:43:29 -0500
+ r8508@hualien: jesse | 2005-03-14 02:29:01 -0500
+ r6568@hualien: jesse | 2005-03-07 16:01:44 -0500
+ Adding support for selecting custom fields for RTFM objects
+
+
+
+------------------------------------------------------------------------
+r2459 | jesse | 2005-03-14 03:04:27 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+
+ r8619@hualien: jesse | 2005-03-14 02:43:27 -0500
+ r8507@hualien: jesse | 2005-03-14 02:28:57 -0500
+ r6473@hualien: jesse | 2005-03-06 16:04:46 -0500
+ Small cleanups to print out less hardcoded style information (introduced by the pull-up from 3.2)
+
+
+
+------------------------------------------------------------------------
+r2458 | jesse | 2005-03-14 03:04:14 -0500 (Mon, 14 Mar 2005) | 13 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+
+ r8618@hualien: jesse | 2005-03-14 02:43:25 -0500
+ r8506@hualien: jesse | 2005-03-14 02:28:54 -0500
+ r6465@hualien: jesse | 2005-03-06 15:00:29 -0500
+ r6450@hualien: jesse | 2005-03-06 12:44:27 -0500
+ RT-Ticket: 6496
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Content-Transfer-Encoding should have been '8bit' not '8-bit'
+
+
+
+
+------------------------------------------------------------------------
+r2457 | jesse | 2005-03-14 03:04:07 -0500 (Mon, 14 Mar 2005) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/aclocal.m4
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+
+ r8617@hualien: jesse | 2005-03-14 02:43:21 -0500
+ r8505@hualien: jesse | 2005-03-14 02:28:50 -0500
+ r6464@hualien: jesse | 2005-03-06 15:00:23 -0500
+ r6371@hualien: jesse | 2005-03-05 19:29:35 -0500
+ r3963@hualien (orig r2142): jesse | 2005-01-30 12:45:15 -0500
+ When we can't load a user by email address, make sure we can't load that user by name before returning an error
+
+
+
+
+
+------------------------------------------------------------------------
+r2456 | jesse | 2005-03-14 03:04:01 -0500 (Mon, 14 Mar 2005) | 14 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+
+ r8616@hualien: jesse | 2005-03-14 02:43:19 -0500
+ r8504@hualien: jesse | 2005-03-14 02:28:45 -0500
+ r6462@hualien: jesse | 2005-03-06 14:57:07 -0500
+ r4715@hualien: jesse | 2005-02-15 12:34:54 -0500
+ RT-Ticket: 6443
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Ticket Searches on AdminCc would fail on non-mysql databases due to a case error
+
+
+
+
+
+------------------------------------------------------------------------
+r2455 | jesse | 2005-03-14 03:03:55 -0500 (Mon, 14 Mar 2005) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pl.po
+
+ r8615@hualien: jesse | 2005-03-14 02:43:17 -0500
+ r8503@hualien: jesse | 2005-03-14 02:28:43 -0500
+ r6461@hualien: jesse | 2005-03-06 14:57:03 -0500
+ r4070@hualien: jesse | 2005-02-03 11:22:59 -0500
+ Polish Translation from Piotr Sliwa
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2454 | jesse | 2005-03-14 03:03:42 -0500 (Mon, 14 Mar 2005) | 17 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+
+ r8614@hualien: jesse | 2005-03-14 02:43:14 -0500
+ r8502@hualien: jesse | 2005-03-14 02:28:38 -0500
+ r6459@hualien: jesse | 2005-03-06 14:55:49 -0500
+ r3935@hualien: jesse | 2005-01-26 12:15:42 -0500
+ RT-Ticket: 6378
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Added a flag to allow tools to use the RT API to search for deleted tickets.
+ (Ruslan)
+
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2453 | jesse | 2005-03-14 03:03:32 -0500 (Mon, 14 Mar 2005) | 21 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+
+ r8613@hualien: jesse | 2005-03-14 02:43:12 -0500
+ r8501@hualien: jesse | 2005-03-14 02:28:32 -0500
+ r6457@hualien: jesse | 2005-03-06 14:55:20 -0500
+ r2575@hualien: jesse | 2005-01-06 17:15:39 -0500
+ RT-Ticket: 6327
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Users who had "DeleteTicket" but not "ModifyTicket" could not delete tickets.
+
+ This is introduced because "Ticket::SetStatus" checks the ACL but
+ forgets to tell "Ticket::_Set" NOT to check (which does check again, but
+ only against the 'ModifyTicket' right, and thus denies the change).
+
+ Thanks to Ruediger Riediger
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2452 | jesse | 2005-03-14 03:03:25 -0500 (Mon, 14 Mar 2005) | 15 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fr.po
+
+ r8612@hualien: jesse | 2005-03-14 02:43:09 -0500
+ r8500@hualien: jesse | 2005-03-14 02:28:28 -0500
+ r6456@hualien: jesse | 2005-03-06 14:55:05 -0500
+ r2573@hualien: jesse | 2005-01-06 17:10:02 -0500
+ RT-Ticket: 6322
+ RT-Status: resolved
+ RT-Update: correspond
+
+ New French translation from robitail@iro.umontreal.ca
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2451 | jesse | 2005-03-14 03:03:10 -0500 (Mon, 14 Mar 2005) | 23 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+
+ r8611@hualien: jesse | 2005-03-14 02:43:06 -0500
+ r8499@hualien: jesse | 2005-03-14 02:28:22 -0500
+ r6454@hualien: jesse | 2005-03-06 14:53:48 -0500
+ r2568@hualien: jesse | 2005-01-06 16:57:40 -0500
+ RT-Ticket: 6336
+ RT-Status: resolved
+ RT-Update: correspond
+
+
+ The attribute "align" in COLUMN_MAP wasn't usable, because it could not be
+ used for a complete column, so I added a /ALIGN:.. section in
+ CollectionAsTable/ParseFormat and use it in Row.
+
+ There also was a minor error to be corrected, since the old variant of
+ ParseFormat did not allow more than one /ANYTHING:... to be use (in my
+ example /TITLE:.../ALIGN:...) --Dirk Pape
+
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2450 | jesse | 2005-03-14 03:03:05 -0500 (Mon, 14 Mar 2005) | 15 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/autohandler
+
+ r8610@hualien: jesse | 2005-03-14 02:43:04 -0500
+ r8498@hualien: jesse | 2005-03-14 02:28:19 -0500
+ r6453@hualien: jesse | 2005-03-06 14:53:44 -0500
+ r2567@hualien: jesse | 2005-01-06 16:37:01 -0500
+ RT-Ticket: 6338
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Force UTF8 content type on replies from RT's REST interface (Thanks to Dirk Pape)
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2449 | jesse | 2005-03-14 03:02:59 -0500 (Mon, 14 Mar 2005) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+
+ r8609@hualien: jesse | 2005-03-14 02:43:03 -0500
+ r8497@hualien: jesse | 2005-03-14 02:28:17 -0500
+ r6452@hualien: jesse | 2005-03-06 14:53:41 -0500
+ r2514@hualien: jesse | 2004-12-29 14:43:06 -0500
+ Updated Attachment import code to take database-specific lob encoding into account
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2448 | jesse | 2005-03-14 03:02:54 -0500 (Mon, 14 Mar 2005) | 20 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/17multiple_deleg_revocation.t
+
+ r8608@hualien: jesse | 2005-03-14 02:43:00 -0500
+ r8496@hualien: jesse | 2005-03-14 02:28:14 -0500
+ r6372@hualien: jesse | 2005-03-05 19:29:51 -0500
+ r6268@hualien (orig r2274): mwhitson | 2005-03-03 12:34:13 -0500
+ rt-ticket: 6450
+ rt-status: resolved
+ rt-update: correspond
+ Correct inaccurate POD for RT::Group::MembersObj and DeepMembersObj.
+
+ r6269@hualien (orig r2275): mwhitson | 2005-03-03 13:55:06 -0500
+ RT-Ticket: 6482
+ RT-Status: resolved
+ RT-Update: correspond
+ Fix incorrect loop exit test in RT::ACE::_Delete such that multiply delegated ACEs are all
+ cleaned up when the parent ACE is revoked.
+
+
+
+
+
+------------------------------------------------------------------------
+r2447 | jesse | 2005-03-14 03:02:37 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/docs/design_docs/realflow.txt
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/docs/design_docs/rt-mvc
+
+ r8607@hualien: jesse | 2005-03-14 02:42:55 -0500
+ r8495@hualien: jesse | 2005-03-14 02:28:11 -0500
+ r6290@hualien: jesse | 2005-03-05 19:06:54 -0500
+ random design docs
+
+
+
+------------------------------------------------------------------------
+r2446 | jesse | 2005-03-14 03:02:28 -0500 (Mon, 14 Mar 2005) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18stale_delegations_cleanup.t
+
+ r8606@hualien: jesse | 2005-03-14 02:42:52 -0500
+ r8494@hualien: jesse | 2005-03-14 02:28:08 -0500
+ r6277@hualien: jesse | 2005-03-05 17:06:08 -0500
+ RT-Ticket: 6184
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Much better coverage of delegation revocation when users' group memberships are changed - Mike Whitson (BPS)
+
+
+
+
+------------------------------------------------------------------------
+r2445 | jesse | 2005-03-14 03:02:23 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r8605@hualien: jesse | 2005-03-14 02:42:49 -0500
+ r8493@hualien: jesse | 2005-03-14 02:28:04 -0500
+ r6272@hualien: jesse | 2005-03-05 16:08:36 -0500
+ Better support for loading custom fields by name
+
+
+
+------------------------------------------------------------------------
+r2444 | jesse | 2005-03-14 03:02:17 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+
+ r8604@hualien: jesse | 2005-03-14 02:42:47 -0500
+ r8492@hualien: jesse | 2005-03-14 02:28:00 -0500
+ r6108@hualien: jesse | 2005-03-01 15:20:28 -0500
+ * 'LimitLinkedFrom' in RT::Tickets didn't work due to a typo. Fixed.
+
+
+
+------------------------------------------------------------------------
+r2443 | jesse | 2005-03-14 03:02:12 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+
+ r8603@hualien: jesse | 2005-03-14 02:42:45 -0500
+ r8491@hualien: jesse | 2005-03-14 02:27:57 -0500
+ r6061@hualien: jesse | 2005-02-27 18:58:48 -0500
+ RT::Transaction->Delete implemented. (for RTFM)
+
+
+
+------------------------------------------------------------------------
+r2442 | jesse | 2005-03-14 03:01:59 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+
+ r8602@hualien: jesse | 2005-03-14 02:42:43 -0500
+ r8490@hualien: jesse | 2005-03-14 02:27:53 -0500
+ r5948@hualien: jesse | 2005-02-24 16:47:52 -0500
+ Custom Field API extension and cleanup to allow new objects to use custom fields. (No backwards-incompat changes)
+
+
+
+------------------------------------------------------------------------
+r2441 | jesse | 2005-03-14 03:01:53 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/index.html
+
+ r8601@hualien: jesse | 2005-03-14 02:42:41 -0500
+ r8489@hualien: jesse | 2005-03-14 02:27:51 -0500
+ r5947@hualien: jesse | 2005-02-24 16:46:11 -0500
+ Refactoring Custom Field admin UI for extensibility
+
+
+
+------------------------------------------------------------------------
+r2440 | jesse | 2005-03-14 03:01:45 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ObjectCustomFields
+
+ r8600@hualien: jesse | 2005-03-14 02:42:39 -0500
+ r8488@hualien: jesse | 2005-03-14 02:27:47 -0500
+ r5946@hualien: jesse | 2005-02-24 16:44:28 -0500
+ Refactoring Custom Field admin UI for extensibility
+
+
+
+------------------------------------------------------------------------
+r2439 | jesse | 2005-03-14 03:01:27 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+
+ r8599@hualien: jesse | 2005-03-14 02:42:37 -0500
+ r8487@hualien: jesse | 2005-03-14 02:27:45 -0500
+ r5945@hualien: jesse | 2005-02-24 16:43:36 -0500
+ Minor stylistic cleanups for web components
+
+
+
+------------------------------------------------------------------------
+r2438 | jesse | 2005-03-14 03:01:22 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+
+ r8598@hualien: jesse | 2005-03-14 02:42:34 -0500
+ r8486@hualien: jesse | 2005-03-14 02:27:42 -0500
+ r5944@hualien: jesse | 2005-02-24 16:43:05 -0500
+ Minor stylistic cleanups for web components
+
+
+
+------------------------------------------------------------------------
+r2437 | jesse | 2005-03-14 03:01:16 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/16-transaction_cf_tests.t
+
+ r8597@hualien: jesse | 2005-03-14 02:42:30 -0500
+ r8485@hualien: jesse | 2005-03-14 02:27:37 -0500
+ r5943@hualien: jesse | 2005-02-24 16:42:01 -0500
+ Upgraded a private _LookupTypes method to a public CustomFieldUpdateTypes method to make it easier to ad custom fields to other objects
+
+
+
+
+------------------------------------------------------------------------
+r2436 | jesse | 2005-03-14 03:01:10 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+
+ r8596@hualien: jesse | 2005-03-14 02:42:27 -0500
+ r8484@hualien: jesse | 2005-03-14 02:27:35 -0500
+ r5942@hualien: jesse | 2005-02-24 16:31:48 -0500
+ * Have the configure script default to the web group for the RT group if no rt group exists or is specified
+
+
+
+------------------------------------------------------------------------
+r2435 | jesse | 2005-03-14 03:01:04 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+
+ r8595@hualien: jesse | 2005-03-14 02:42:25 -0500
+ r8483@hualien: jesse | 2005-03-14 02:27:32 -0500
+ r5932@hualien: jesse | 2005-02-24 15:09:27 -0500
+ Robert added new functionality to support autodetection of RT and Web users in ./configure
+
+
+
+
+------------------------------------------------------------------------
+r2434 | jesse | 2005-03-14 03:00:51 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/16-transaction_cf_tests.t
+
+ r8594@hualien: jesse | 2005-03-14 02:42:22 -0500
+ r8482@hualien: jesse | 2005-03-14 02:27:29 -0500
+ r5747@hualien: jesse | 2005-02-22 16:33:09 -0500
+ Better transaction UpdateCustomFields API
+
+
+
+
+------------------------------------------------------------------------
+r2433 | jesse | 2005-03-14 03:00:45 -0500 (Mon, 14 Mar 2005) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+
+ r8593@hualien: jesse | 2005-03-14 02:42:17 -0500
+ r8481@hualien: jesse | 2005-03-14 02:27:26 -0500
+ r4714@hualien: jesse | 2005-02-15 12:34:26 -0500
+ RT-Ticket: 6443
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Ticket Searches on AdminCc would fail on non-mysql databases due to a case error
+
+
+
+
+------------------------------------------------------------------------
+r2432 | jesse | 2005-03-14 03:00:37 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+
+ r8592@hualien: jesse | 2005-03-14 02:42:15 -0500
+ r8480@hualien: jesse | 2005-03-14 02:27:24 -0500
+ r4711@hualien: jesse | 2005-02-15 11:19:52 -0500
+ Bumping to 3.4.1
+
+
+
+
+------------------------------------------------------------------------
+r2431 | jesse | 2005-03-14 03:00:31 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+
+ r8591@hualien: jesse | 2005-03-14 02:42:13 -0500
+ r8479@hualien: jesse | 2005-03-14 02:27:21 -0500
+ r4710@hualien: jesse | 2005-02-15 11:18:22 -0500
+ Updated 2.0 upgrade instructions
+
+
+
+------------------------------------------------------------------------
+r2430 | jesse | 2005-03-14 03:00:16 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/en_malkovich.po
+
+ r8590@hualien: jesse | 2005-03-14 02:42:08 -0500
+ r8477@hualien: jesse | 2005-03-14 02:26:23 -0500
+ r4703@hualien: jesse | 2005-02-14 10:56:23 -0500
+ Removed broken Malkovich translation
+
+
+
+
+------------------------------------------------------------------------
+r2429 | jesse | 2005-03-14 03:00:09 -0500 (Mon, 14 Mar 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/de.po
+
+ r8589@hualien: jesse | 2005-03-14 02:42:04 -0500
+ r8476@hualien: jesse | 2005-03-14 02:26:18 -0500
+ r4702@hualien: jesse | 2005-02-14 10:56:02 -0500
+ Updated German translation from Torsten Brumm
+
+
+
+
+
+------------------------------------------------------------------------
+r2428 | jesse | 2005-03-14 03:00:03 -0500 (Mon, 14 Mar 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+
+ r8588@hualien: jesse | 2005-03-14 02:42:02 -0500
+ r8475@hualien: jesse | 2005-03-14 02:26:15 -0500
+ r4697@hualien: jesse | 2005-02-13 23:42:05 -0500
+ * Standalone HTTP Daemon now deals properly with unicode input/output
+
+
+
+
+
+------------------------------------------------------------------------
+r2427 | jesse | 2005-03-14 02:59:46 -0500 (Mon, 14 Mar 2005) | 13 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/it.po
+
+ r8587@hualien: jesse | 2005-03-14 02:41:59 -0500
+ r8474@hualien: jesse | 2005-03-14 02:26:09 -0500
+ r4696@hualien: jesse | 2005-02-13 23:06:07 -0500
+ RT-Ticket: 6429
+ RT-Update: correspond
+ RT-Status: resolved
+
+ Updated Italian translation from Angelo Turetta. Thanks!
+
+
+
+
+
+------------------------------------------------------------------------
+r2426 | jesse | 2005-03-14 02:59:41 -0500 (Mon, 14 Mar 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+
+ r8586@hualien: jesse | 2005-03-14 02:41:57 -0500
+ r8473@hualien: jesse | 2005-03-14 02:26:01 -0500
+ r4695@hualien: jesse | 2005-02-13 23:04:07 -0500
+ Fix for issues with words with Umlauts in search result listings
+
+
+
+
+
+------------------------------------------------------------------------
+r2425 | jesse | 2005-03-14 02:59:36 -0500 (Mon, 14 Mar 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+
+ r8585@hualien: jesse | 2005-03-14 02:41:55 -0500
+ r8472@hualien: jesse | 2005-03-14 02:25:58 -0500
+ r4692@hualien: jesse | 2005-02-13 22:38:09 -0500
+ Disabled automatic clearing of mason component cache on start with FastCGI. It doesn't work properly in the multiprocess
+ environment.
+
+
+
+
+------------------------------------------------------------------------
+r2424 | jesse | 2005-03-14 02:59:31 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-crontool.in
+
+ r8584@hualien: jesse | 2005-03-14 02:41:54 -0500
+ r8471@hualien: jesse | 2005-03-14 02:25:56 -0500
+ r4691@hualien: jesse | 2005-02-13 22:37:15 -0500
+ The crontool was using local and system libs in the wrong order
+
+
+
+
+------------------------------------------------------------------------
+r2423 | jesse | 2005-03-14 02:59:26 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/UserRights.html
+
+ r8583@hualien: jesse | 2005-03-14 02:41:52 -0500
+ r8470@hualien: jesse | 2005-03-14 02:25:53 -0500
+ r4689@hualien: jesse | 2005-02-13 22:30:20 -0500
+ A typo prevented users from being able to revoke other users' rights on custom fields
+
+
+
+
+------------------------------------------------------------------------
+r2422 | jesse | 2005-03-14 02:59:11 -0500 (Mon, 14 Mar 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/attachments
+
+ r8582@hualien: jesse | 2005-03-14 02:41:50 -0500
+ r8469@hualien: jesse | 2005-03-14 02:25:51 -0500
+ r4683@hualien: jesse | 2005-02-10 22:00:33 -0500
+ Use the "OriginalContent" rather than our encoded one. this might make cli attachment downloads work better
+
+
+
+
+
+------------------------------------------------------------------------
+r2421 | jesse | 2005-03-14 02:59:06 -0500 (Mon, 14 Mar 2005) | 14 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+
+ r8581@hualien: jesse | 2005-03-14 02:41:48 -0500
+ r8468@hualien: jesse | 2005-03-14 02:25:48 -0500
+ r4678@hualien: jesse | 2005-02-10 17:10:29 -0500
+ RT-Ticket: 6406
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Query builder fixes for sites with multiple identically named custom fields
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2420 | jesse | 2005-03-14 02:58:57 -0500 (Mon, 14 Mar 2005) | 13 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.po
+
+ r8580@hualien: jesse | 2005-03-14 02:41:45 -0500
+ r8467@hualien: jesse | 2005-03-14 02:25:44 -0500
+ r4676@hualien: jesse | 2005-02-10 16:33:19 -0500
+ RT-Ticket: 6364
+ RT-Status: resolved
+ RT-Update: corerspond
+
+ Updated Czech translation
+
+
+
+
+
+------------------------------------------------------------------------
+r2419 | jesse | 2005-03-14 02:58:32 -0500 (Mon, 14 Mar 2005) | 15 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/docs/design_docs/cvs_integration
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/docs/design_docs/link-definitions.txt
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/da.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/de.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/en_malkovich.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/es.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fi.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fr.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/he.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/hu.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/it.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ja.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/nl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/no.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pt_br.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ru.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_cn.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_tw.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT.pm.in
+
+ r8579@hualien: jesse | 2005-03-14 02:41:07 -0500
+ r8466@hualien: jesse | 2005-03-14 02:25:18 -0500
+ r4673@hualien: jesse | 2005-02-10 16:24:02 -0500
+ RT-Ticket: 6379
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Correct a common spelling error:
+ perl -pi.bak -e's/seperat/separat/g; s/Seperat/Separat/g;' $(find .)
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2418 | jesse | 2005-03-14 02:58:12 -0500 (Mon, 14 Mar 2005) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+
+ r8578@hualien: jesse | 2005-03-14 02:41:05 -0500
+ r8465@hualien: jesse | 2005-03-14 02:25:15 -0500
+ r4672@hualien: jesse | 2005-02-10 16:19:40 -0500
+ Fixes to handle large text custom fields gracefully
+
+
+
+
+
+------------------------------------------------------------------------
+r2417 | jesse | 2005-03-14 02:58:07 -0500 (Mon, 14 Mar 2005) | 13 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/hu.po
+
+ r8577@hualien: jesse | 2005-03-14 02:41:01 -0500
+ r8464@hualien: jesse | 2005-03-14 02:25:12 -0500
+ r4669@hualien: jesse | 2005-02-10 16:11:24 -0500
+ RT-Ticket: 6418
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Applied new hungarian translation
+
+
+
+
+
+------------------------------------------------------------------------
+r2416 | jesse | 2005-03-14 02:58:01 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/history
+
+ r8576@hualien: jesse | 2005-03-14 02:40:58 -0500
+ r8463@hualien: jesse | 2005-03-14 02:25:10 -0500
+ r4668@hualien: jesse | 2005-02-10 15:54:30 -0500
+ removed effective ticket from history listing
+
+
+
+------------------------------------------------------------------------
+r2415 | jesse | 2005-03-14 02:57:55 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+
+ r8575@hualien: jesse | 2005-03-14 02:40:56 -0500
+ r8462@hualien: jesse | 2005-03-14 02:25:07 -0500
+ r4644@hualien: jesse | 2005-02-08 12:26:39 -0500
+ "Status" in ticket listings is now localized
+
+
+
+
+------------------------------------------------------------------------
+r2414 | jesse | 2005-03-14 02:57:42 -0500 (Mon, 14 Mar 2005) | 13 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectStage
+
+ r8574@hualien: jesse | 2005-03-14 02:40:54 -0500
+ r8461@hualien: jesse | 2005-03-14 02:25:05 -0500
+ r4103@hualien: jesse | 2005-02-07 12:21:04 -0500
+ RT-Ticket: 6417
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Only show the TransactionBatch scrip stage if TransactionBatch
+ scrips are enabled. --Dave Rolsky
+
+
+
+
+------------------------------------------------------------------------
+r2413 | jesse | 2005-03-14 02:57:36 -0500 (Mon, 14 Mar 2005) | 14 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripAction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Submit
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMessageStanza
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r8573@hualien: jesse | 2005-03-14 02:40:49 -0500
+ r8460@hualien: jesse | 2005-03-14 02:25:00 -0500
+ r4102@hualien: jesse | 2005-02-07 12:20:01 -0500
+ RT-Ticket: 6415
+ RT-Status: resolved
+ RT-Update: correspond
+
+ A bunch of small fixes to avoid minor warnings in the RT code. --Dave Rolsky
+
+
+
+
+
+
+------------------------------------------------------------------------
+r2412 | jesse | 2005-03-14 02:57:30 -0500 (Mon, 14 Mar 2005) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowMemberships
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMemberOf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowReferences
+
+ r8572@hualien: jesse | 2005-03-14 02:40:43 -0500
+ r8459@hualien: jesse | 2005-03-14 02:24:57 -0500
+ r4082@hualien: jesse | 2005-02-04 11:03:07 -0500
+ Fixing some links that broke when RT was not installed at the server root
+
+
+
+------------------------------------------------------------------------
+r2411 | jesse | 2005-03-14 02:57:22 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+
+ r8571@hualien: jesse | 2005-03-14 02:40:41 -0500
+ r8458@hualien: jesse | 2005-03-14 02:24:54 -0500
+ r4075@hualien: jesse | 2005-02-03 12:27:08 -0500
+ SelectOwner was displaying "Nobody" twice due to smarter backend code that broke the not-so-smart frontend. Fixed the frontend.
+
+
+
+
+------------------------------------------------------------------------
+r2410 | jesse | 2005-03-14 02:57:17 -0500 (Mon, 14 Mar 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Menu
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Menu/Item.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Menu.pm
+
+ r8570@hualien: jesse | 2005-03-14 02:40:38 -0500
+ r6762@hualien: jesse | 2005-03-11 16:20:03 -0500
+ Started to sketch out a new API to replace RT's menuing components.
+
+ Comments greatly appreciated.
+
+
+
+------------------------------------------------------------------------
+r2190 | jesse | 2005-02-02 11:36:14 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+
+ r4027@hualien: jesse | 2005-02-02T15:47:58.569530Z
+ r3972@hualien: jesse | 2005-02-01T14:36:00.190124Z
+ 3.4.0
+
+
+------------------------------------------------------------------------
+r2189 | jesse | 2005-02-02 11:35:35 -0500 (Wed, 02 Feb 2005) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r4026@hualien: jesse | 2005-02-02T15:47:57.808370Z
+ r3971@hualien: jesse | 2005-02-01T14:35:45.436824Z
+ Bumping SearchBuilder dependency to 1.21
+
+
+
+------------------------------------------------------------------------
+r2188 | jesse | 2005-02-02 11:34:53 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+
+ r4025@hualien: jesse | 2005-02-02T15:47:57.027989Z
+ r3970@hualien: jesse | 2005-02-01T14:21:00.328018Z
+ Merge reverted a date in the README
+
+
+------------------------------------------------------------------------
+r2187 | jesse | 2005-02-02 11:33:58 -0500 (Wed, 02 Feb 2005) | 26 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/create_data.pl
+
+ r4024@hualien: jesse | 2005-02-02T15:47:28.154721Z
+ r3969@hualien: jesse | 2005-02-01T14:20:17.033746Z
+ r2696@hualien (orig r2069): zev | 2005-01-11T23:49:19.545552Z
+ Fixed spelling and grammar mistakes in etc/RT_Config.pm.in
+ Added some documentation and fixed whitespace in Makefile.in
+
+ r2697@hualien (orig r2070): jesse | 2005-01-11T23:58:03.208655Z
+ Readme update to note that manual installation of Apache::DBI may be required.
+
+
+ r2698@hualien (orig r2071): jesse | 2005-01-12T23:39:03.787078Z
+ RT3.4 schema diagram corrected and updated.
+
+
+ r2699@hualien (orig r2072): jesse | 2005-01-13T07:02:25.740524Z
+ Removed a reference to NIS
+
+
+ r3878@hualien (orig r2109): tla | 2005-01-18T01:53:10.044267Z
+ A script to automate testing data creation. Requires a new CPAN module,
+ Text::Lorem. Work in progress.
+
+
+
+
+
+------------------------------------------------------------------------
+r2186 | jesse | 2005-02-02 11:32:49 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.fcgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.scgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.svc.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-crontool.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-mailgate.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Objects.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CustomFieldTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditTemplates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditUserComments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/GlobalCustomFieldTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/GroupTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ModifyTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ObjectCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldLookupType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripAction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectSingleOrMultiple
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectStage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SystemTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ToolTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/UserTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Groups.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Tickets.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Transactions.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Users.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Scrip.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Scrips.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Templates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/CustomField.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/People.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Scrip.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Scrips.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Templates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/Configuration.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Memberships.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/Approve
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/ShowDependency
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Download/CustomFieldValue/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Download/Tabular/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Callback
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Checkbox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CreateTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldBinary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldImage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldSelect
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldText
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Error
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Menu
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MessageBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyTickets
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueryString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QuickCreate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Refresh
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ScrubHTML
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Section
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectAttachmentField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectBoolean
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateRelation
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectEqualityOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLang
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLinkType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectMatch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectResultsPerPage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectSortOrder
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectStatus
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketSortBy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketTypes
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectWatcherType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SetupSessionCookie
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFieldImage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLink
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowMemberships
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SimpleSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Submit
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Logout.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Reminder.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/queue/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/queue/ns
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/links
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/user/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/user/ns
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/logout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/search/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/search/ticket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/comment
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/link
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/merge
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Edit.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditQuery
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditSearches
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/NewListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCFs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCriteria
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SearchPrivacy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectAndOr
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectPersonType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchObject
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchesForObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.rdf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.tsv
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Attachment/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Closed.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/CreateTicketInQueue.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Error.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Attachment/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/AddWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/BulkLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditPeople
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/FindAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDependencies
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMemberOf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMessageHeaders
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMessageStanza
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowPeople
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowReferences
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowUserEntry
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyLinks.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyPeople.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/MyDay.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Offline.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Delegation.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/DelegateRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/GroupTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/l
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/AutoOpen.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Autoreply.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/EscalatePriority.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Notify.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/NotifyAsComment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/RecordComment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/RecordCorrespondence.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/ResolveMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SetPriority.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/UserDefined.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Base.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/AnyTransaction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/BeforeDue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/Overdue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/OwnerChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/PriorityChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/PriorityExceeds.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/QueueChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/StatusChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/UserDefined.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CurrentUser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Date.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/EmailParser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Handle.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/i_default.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/CLI.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Auth/GnuPG.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/REST.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/ActiveTicketsInQueue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/FromSQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SearchBuilder.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/System.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/base.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/extract-message-catalog
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/extract_pod_tests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/factory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/regression_harness
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-dump-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r4023@hualien: jesse | 2005-02-02T15:46:27.472832Z
+ r3962@hualien: jesse | 2005-02-01T14:05:26.980011Z
+ reran license tagger
+
+
+------------------------------------------------------------------------
+r2185 | jesse | 2005-02-02 11:31:42 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/da.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/de.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/en_malkovich.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/es.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fi.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fr.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/he.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/hu.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/it.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ja.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/nl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/no.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pt_br.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ru.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_cn.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_tw.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/license_tag
+
+ r4022@hualien: jesse | 2005-02-02T15:46:16.166257Z
+ r3961@hualien: jesse | 2005-02-01T14:03:42.493372Z
+ Message catalogs updated
+
+
+------------------------------------------------------------------------
+r2184 | jesse | 2005-02-02 11:30:49 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+
+ r4021@hualien: jesse | 2005-02-02T15:40:41.986030Z
+ r3960@hualien: jesse | 2005-02-01T13:59:42.556416Z
+ Tiny readme style fixes
+
+
+------------------------------------------------------------------------
+r2183 | jesse | 2005-02-02 11:29:54 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.0/schema.Oracle
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.0/schema.Pg
+
+ r4020@hualien: jesse | 2005-02-02T15:40:41.142335Z
+ r3959@hualien: jesse | 2005-02-01T13:55:00.555573Z
+ Removing index changes from upgrade process that could break upgrades for some users
+
+
+------------------------------------------------------------------------
+r2182 | jesse | 2005-02-02 11:28:50 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+
+ r4019@hualien: jesse | 2005-02-02T15:40:40.315621Z
+ r3924@hualien: jesse | 2005-01-22T19:34:52.453359Z
+ Bumped to 3.4.0rc6
+
+
+------------------------------------------------------------------------
+r2181 | jesse | 2005-02-02 11:27:43 -0500 (Wed, 02 Feb 2005) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+
+ r4018@hualien: jesse | 2005-02-02T15:40:39.178927Z
+ r3910@hualien: jesse | 2005-01-22T16:06:58.731752Z
+ Custom field searches should only search on values that haven't been deleted
+
+
+
+------------------------------------------------------------------------
+r2180 | jesse | 2005-02-02 11:26:41 -0500 (Wed, 02 Feb 2005) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/15cf_single_values_are_single.t
+
+ r4017@hualien: jesse | 2005-02-02T15:40:38.080093Z
+ r3909@hualien: jesse | 2005-01-22T15:42:46.936767Z
+ Bullet-proofing for custom fields with a set but limited number of values
+
+
+------------------------------------------------------------------------
+r2179 | jesse | 2005-02-02 11:25:46 -0500 (Wed, 02 Feb 2005) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+
+ r4016@hualien: jesse | 2005-02-02T15:40:36.888212Z
+ r3907@hualien: jesse | 2005-01-22T14:48:14.100308Z
+ TicketSQL search fixes, especially to Custom field operations
+
+
+
+------------------------------------------------------------------------
+r2178 | jesse | 2005-02-02 11:24:48 -0500 (Wed, 02 Feb 2005) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+
+ r4015@hualien: jesse | 2005-02-02T15:40:36.083067Z
+ r3904@hualien: jesse | 2005-01-22T13:00:19.775653Z
+ The column map callback was broken by performance work
+
+
+
+------------------------------------------------------------------------
+r2177 | jesse | 2005-02-02 11:23:45 -0500 (Wed, 02 Feb 2005) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.0/content
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/acl.Oracle
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/acl.Pg
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/acl.SQLite
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/acl.mysql
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/content
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/schema.Oracle
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/schema.SQLite
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+
+ r4014@hualien: jesse | 2005-02-02T15:40:34.961988Z
+ r3902@hualien: jesse | 2005-01-22T11:21:43.212477Z
+ RT-Ticket: 6366
+ RT-Update: correspond
+
+ Cleaned up the upgrade procedure to give better messages on success. Added content so it won't bomb out on errors for the 3.4 upgrades.
+ (We could have altered the script to not error out on missing content, but that would obscure errors where the user flubbed the path.
+
+
+
+
+------------------------------------------------------------------------
+r2176 | jesse | 2005-02-02 11:22:48 -0500 (Wed, 02 Feb 2005) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+
+ r4013@hualien: jesse | 2005-02-02T15:40:34.227393Z
+ r3893@hualien: jesse | 2005-01-19T18:39:39.850646Z
+ Updated version number used by development builds to note that this is the 3.4 series
+
+
+
+------------------------------------------------------------------------
+r2175 | jesse | 2005-02-02 11:21:57 -0500 (Wed, 02 Feb 2005) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+
+ r4012@hualien: jesse | 2005-02-02T15:40:33.418827Z
+ r3892@hualien: jesse | 2005-01-19T18:38:38.233900Z
+ Added better error checking for attachment insertion failure. (This unmasked more instances of oracle brokenness)
+
+
+
+------------------------------------------------------------------------
+r2174 | jesse | 2005-02-02 11:20:56 -0500 (Wed, 02 Feb 2005) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/03basic_web.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/06mailgateway.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/09record_cf_api.t
+
+ r4011@hualien: jesse | 2005-02-02T15:40:32.524514Z
+ r3891@hualien: jesse | 2005-01-19T18:31:15.604733Z
+ Minor formatting cleanups to tests
+
+
+
+------------------------------------------------------------------------
+r2173 | jesse | 2005-02-02 11:19:38 -0500 (Wed, 02 Feb 2005) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users_Overlay.pm
+
+ r4010@hualien: jesse | 2005-02-02T15:40:31.594185Z
+ r3885@hualien: jesse | 2005-01-18T15:02:02.574134Z
+ Reimplemented RT::Users->WhoHaveRights to remove O(n^2) SQL calls. (That means it's faster now)
+ Reimplemented SelectOwner widget to take advantage of the new WhoHaveRights
+
+
+
+
+------------------------------------------------------------------------
+r2172 | jesse | 2005-02-02 11:19:03 -0500 (Wed, 02 Feb 2005) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+
+ r4009@hualien: jesse | 2005-02-02T15:40:30.672747Z
+ r3884@hualien: jesse | 2005-01-18T14:59:19.630164Z
+ RT-Ticket: 6352
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Fix an invalid custom field acl check
+
+
+
+------------------------------------------------------------------------
+r2171 | jesse | 2005-02-02 11:16:35 -0500 (Wed, 02 Feb 2005) | 4 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/HOWTO
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/UPGRADING
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.fcgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.scgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.svc.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/docs/design_docs/3.3-schema-redesign.txt
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/docs/rt3-schema-relationships.dot
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.Oracle
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.0/schema.Oracle
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.0/schema.mysql
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/schema.Pg
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/schema.mysql
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Objects.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CustomFieldTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Callback
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldSelect
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldText
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/back_home.gif
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/head_requestracker.gif
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/rt.jpg
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/spacer.gif
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/squares_blue.gif
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/search/ticket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Create.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/CreateTicketInQueue.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/MyDay.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Handle.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/da.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/de.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/en.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/en_malkovich.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/es.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fi.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fr.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/he.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/hu.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/it.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ja.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/nl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/no.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pt_br.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ru.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_cn.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_tw.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue.pm
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue_Overlay.pm
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues.pm
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/00smoke.t
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/00smoke.t.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/01harness.t.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/02regression.t.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/03web.pl.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/04_send_email.pl.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/05cronsupport.pl.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/06mailgateway.pl.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/07acl.pl.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/08web_cf.pl.in
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/00placeholder
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/01ticket_link_searching.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/03basic_web.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/04send_email.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/05cronsupport.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/06mailgateway.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/07acl.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/08web_cf_access.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/09record_cf_api.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/10merge.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/11-template-insert.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/12-search.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/13-attribute-tests.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/14merge.t
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/setup_regression.t
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/extract_pod_tests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/factory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-dump-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r4008@hualien: jesse | 2005-02-02T15:18:09.179761Z
+
+
+
+------------------------------------------------------------------------
+r1874 | jesse | 2004-11-11 03:55:55 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r9136@tinbook: jesse | 2004-11-11T04:51:39.612228Z
+ r9062@tinbook: jesse | 2004-11-10T13:49:32.150537Z
+ r9055@tinbook (orig r1796): autrijus | 2004-11-10T13:15:51.955295Z
+ r3588@not: autrijus | 2004-11-10T13:16:02.416034Z
+ * Module::Refresh is now enabled by default for all HTTPD handlers
+ when $RT::DevelMode is set to true.
+
+
+
+
+
+------------------------------------------------------------------------
+r1873 | jesse | 2004-11-11 03:55:34 -0500 (Thu, 11 Nov 2004) | 14 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+
+ r9135@tinbook: jesse | 2004-11-11T04:51:37.057295Z
+ r9043@tinbook: jesse | 2004-11-10T12:55:28.022711Z
+ r9040@tinbook: jesse | 2004-11-10T12:52:28.271911Z
+ RT-Ticket: 4624
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Provide more control over how autogenerated mail gets sent out via RT.
+
+
+
+
+
+
+------------------------------------------------------------------------
+r1872 | jesse | 2004-11-11 03:55:14 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+
+ r9134@tinbook: jesse | 2004-11-11T04:51:34.877785Z
+ r9042@tinbook: jesse | 2004-11-10T12:55:27.433932Z
+ r8971@tinbook: jesse | 2004-11-10T02:24:26.255370Z
+ Fixed the MaxAttachmentSize variable name in comments. Thanks to Graham Dunn
+
+
+
+------------------------------------------------------------------------
+r1871 | jesse | 2004-11-11 03:54:54 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+
+ r9133@tinbook: jesse | 2004-11-11T04:51:32.885635Z
+ r9037@tinbook: jesse | 2004-11-10T12:38:52.925002Z
+ * $ was missing on the DevelMode option
+
+
+------------------------------------------------------------------------
+r1870 | jesse | 2004-11-11 03:54:27 -0500 (Thu, 11 Nov 2004) | 20 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Callback
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CurrentUser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r9132@tinbook: jesse | 2004-11-11T04:51:29.581756Z
+ r9028@tinbook: jesse | 2004-11-10T10:25:42.564475Z
+ r9024@tinbook (orig r1777): autrijus | 2004-11-10T10:08:17.041433Z
+ r3565@not: autrijus | 2004-11-10T10:08:34.482570Z
+ * Various warning avoidance patches.
+
+
+ r9025@tinbook (orig r1778): autrijus | 2004-11-10T10:08:41.221082Z
+ r3566@not: autrijus | 2004-11-10T10:08:56.876914Z
+ * Under developer mode, do not let Mason cache object files on var/.
+
+
+ r9026@tinbook (orig r1779): autrijus | 2004-11-10T10:09:04.430896Z
+ r3567@not: autrijus | 2004-11-10T10:09:08.775089Z
+ * Add Module::Refresh as a dependency.
+
+
+
+
+
+------------------------------------------------------------------------
+r1869 | jesse | 2004-11-11 03:54:03 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+
+ r9131@tinbook: jesse | 2004-11-11T04:51:27.703409Z
+ r8969@tinbook: jesse | 2004-11-10T02:01:53.054185Z
+ Fixing DeveloperlMode -> DevelMode. Thanks to Reed Lowden for the catch.
+
+
+
+------------------------------------------------------------------------
+r1868 | jesse | 2004-11-11 03:53:18 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.fcgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.scgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.svc.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-crontool.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-mailgate.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Objects.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CustomFieldTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditTemplates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditUserComments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/GlobalCustomFieldTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/GroupTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ModifyTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ObjectCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldLookupType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripAction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectSingleOrMultiple
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectStage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SystemTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ToolTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/UserTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Groups.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Tickets.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Transactions.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Users.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Scrip.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Scrips.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Templates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/CustomField.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/People.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Scrip.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Scrips.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Templates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/Configuration.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Memberships.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/Approve
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/ShowDependency
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Download/CustomFieldValue/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Download/Tabular/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Callback
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Checkbox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/ParseFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CreateTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldBinary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldImage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldSelect
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldText
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Error
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Menu
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MessageBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyTickets
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueryString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QuickCreate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Refresh
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ScrubHTML
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Section
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectAttachmentField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectBoolean
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateRelation
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectEqualityOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLang
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLinkType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectMatch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectResultsPerPage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectSortOrder
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectStatus
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketSortBy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketTypes
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectWatcherType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SetupSessionCookie
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFieldImage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLink
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowMemberships
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SimpleSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Submit
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Logout.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Reminder.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/queue/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/queue/ns
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/links
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/user/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/user/ns
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/logout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/search/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/search/ticket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/comment
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/link
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/merge
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Edit.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditQuery
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditSearches
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/NewListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCFs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCriteria
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SearchPrivacy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectAndOr
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectPersonType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchObject
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchesForObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.rdf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.tsv
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Attachment/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Closed.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Error.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Attachment/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/AddWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/BulkLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditPeople
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/FindAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDependencies
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMemberOf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMessageHeaders
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMessageStanza
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowPeople
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowReferences
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowUserEntry
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyLinks.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyPeople.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/MyDay.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Offline.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Delegation.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/DelegateRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/GroupTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/l
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/AutoOpen.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Autoreply.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/EscalatePriority.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Notify.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/NotifyAsComment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/RecordComment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/RecordCorrespondence.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/ResolveMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SetPriority.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/UserDefined.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Base.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/AnyTransaction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/BeforeDue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/Overdue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/OwnerChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/PriorityChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/PriorityExceeds.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/QueueChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/StatusChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/UserDefined.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CurrentUser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Date.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/EmailParser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Handle.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/i_default.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/CLI.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Auth/GnuPG.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/REST.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/ActiveTicketsInQueue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/FromSQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SearchBuilder.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/System.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/base.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/06mailgateway.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/extract-message-catalog
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/extract_pod_tests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/factory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/license_tag
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/regression_harness
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-dump-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r9130@tinbook: jesse | 2004-11-11T04:08:03.094469Z
+ r8965@tinbook: jesse | 2004-11-09T08:25:30.422838Z
+ Updated license tagging and folding for license tagging
+
+ * No functionality changes
+
+
+
+
+
+------------------------------------------------------------------------
+r1867 | jesse | 2004-11-11 03:52:24 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+
+ r9129@tinbook: jesse | 2004-11-11T04:08:00.711759Z
+ r8963@tinbook: jesse | 2004-11-09T07:55:26.294077Z
+ Just formatting changes. ran perltidy on HasRight.
+
+
+
+
+------------------------------------------------------------------------
+r1866 | jesse | 2004-11-11 03:52:05 -0500 (Thu, 11 Nov 2004) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r9128@tinbook: jesse | 2004-11-11T04:07:58.364185Z
+ r8959@tinbook: jesse | 2004-11-09T05:47:13.371897Z
+ Switched to using Cache::Simple::TimedExpiry for our ACL cache, rather
+ than our overly convoluted (and slower) ACL cache code in Prinicpal_Overlay.pm
+
+ SearchBuilder already depends on C::S::TE, so this isn't a new dep
+
+
+
+------------------------------------------------------------------------
+r1865 | jesse | 2004-11-11 03:51:46 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+
+ r9127@tinbook: jesse | 2004-11-11T04:07:56.058756Z
+ r8951@tinbook: jesse | 2004-11-09T04:59:07.816904Z
+ Fixed a comment about the static_source directive for mason. Thanks to matthewd
+
+
+
+------------------------------------------------------------------------
+r1864 | jesse | 2004-11-11 03:51:25 -0500 (Thu, 11 Nov 2004) | 18 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+
+ r9126@tinbook: jesse | 2004-11-11T04:07:52.654642Z
+ r8942@tinbook: jesse | 2004-11-08T17:22:08.032229Z
+ Web:
+ * Switched back to manual buffer flushing, as this will
+ improve performance and help future-proof RT against a mason update
+ * Added several manual buffer flushing points to help with the user
+ experience during page display
+ * Switched to using mason's "static_source" directive, which stops mason
+ from trying to guess about which pages need to be reload from disk.
+ (Now you need to restart the webserver to see changes)
+ Core:
+ * New --with-developer-mode configuration flag. Right now, it just
+ turns mason static_source off, but in the future, it will enable other
+ developer-only features.
+
+
+
+
+------------------------------------------------------------------------
+r1863 | jesse | 2004-11-11 03:51:03 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Base.pm
+
+ r9125@tinbook: jesse | 2004-11-11T04:07:50.357009Z
+ r8941@tinbook: jesse | 2004-11-08T17:18:03.357715Z
+ Added a bit of documentation to RT::Base; removed an unused line of code
+
+
+
+------------------------------------------------------------------------
+r1862 | jesse | 2004-11-11 03:47:18 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+
+ r9124@tinbook: jesse | 2004-11-11T04:07:47.464978Z
+ r8935@tinbook: jesse | 2004-11-08T13:18:04.774918Z
+ * Turning off the forking in standalone_httpd. It was never a win
+
+
+------------------------------------------------------------------------
+r1861 | jesse | 2004-11-11 03:47:00 -0500 (Thu, 11 Nov 2004) | 28 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/docs/design_docs/3.3-schema-redesign.txt
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/spec
+
+ r9123@tinbook: jesse | 2004-11-11T04:07:44.681509Z
+ r8934@tinbook: jesse | 2004-11-08T13:12:15.840028Z
+ r8929@tinbook (orig r1748): autrijus | 2004-11-08T12:52:03.511429Z
+ r1746@not: autrijus | 2004-11-08T08:27:45.676219Z
+ * V7 of redesign schema, incoporating many more comments and
+ the new, clkao-inspired fields.
+
+
+ r8930@tinbook (orig r1749): autrijus | 2004-11-08T12:52:36.289718Z
+ r1747@not: autrijus | 2004-11-08T08:28:53.206839Z
+ * move the design spec from spec/ under the docs/design_docs/ space where it belongs.
+
+
+ r8931@tinbook (orig r1750): autrijus | 2004-11-08T12:53:00.482208Z
+ r1748@not: autrijus | 2004-11-08T08:29:17.162697Z
+ * remove the now-empty spec/ directory.
+
+
+ r8932@tinbook (orig r1751): autrijus | 2004-11-08T12:53:30.365957Z
+
+ r8933@tinbook (orig r1752): autrijus | 2004-11-08T12:53:51.280973Z
+ r3280@not: autrijus | 2004-11-08T12:13:28.529608Z
+ * make kill -HUP work sanely.
+
+
+
+
+
+------------------------------------------------------------------------
+r1860 | jesse | 2004-11-11 03:46:38 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/02regression.t.in
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/08web_cf.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r9122@tinbook: jesse | 2004-11-11T04:07:42.162101Z
+ r6237@tinbook: jesse | 2004-11-07T16:27:42.200967Z
+ New web-based testing for image custom field creation
+ (upload on create, display in search result listings)
+
+ -Autrijus Tang
+
+
+
+
+------------------------------------------------------------------------
+r1859 | jesse | 2004-11-11 03:46:17 -0500 (Thu, 11 Nov 2004) | 89 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.Oracle
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.Pg
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.SQLite
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.mysql
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/schema.Pg
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.11/schema.mysql
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditQuery
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditSearches
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCriteria
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectAndOr
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectPersonType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/factory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+
+ r9121@tinbook: jesse | 2004-11-11T04:07:32.925469Z
+ r6227@tinbook: jesse | 2004-11-07T14:48:07.801297Z
+ r6202@tinbook (orig r1718): autrijus | 2004-11-05T08:56:10.440277Z
+ * Log::Dispatch wants ->warning, not ->warn.
+
+
+ r6205@tinbook (orig r1721): autrijus | 2004-11-05T11:43:18.999313Z
+ r10186@not (orig r1718): autrijus | 2004-11-05T08:56:10.440277Z
+ * Log::Dispatch wants ->warning, not ->warn.
+
+ r10196@not: autrijus | 2004-11-05T11:41:58.476189Z
+ * Fixes to standalone_httpd: 'protocol' was not defined, and query_string
+ may be undefined too. (Supposedly fixed in HTTP::Server::Simple.)
+
+ r10197@not: autrijus | 2004-11-05T11:42:20.621065Z
+ * Remove outdated comment.
+
+ r10198@not: autrijus | 2004-11-05T11:43:55.045510Z
+ * Refactor getting-an-uploaded-file-into-cf into _UploadFile call
+ in Interface::Web; this unbreaks uploading a file cf on ticket creation.
+ * The "CustomField-x" keys in $Ticket->Create now take hash references
+ that contain CFV fields, eg. LargeContent, ContentType, Value.
+
+
+
+ r6208@tinbook (orig r1723): autrijus | 2004-11-07T14:16:15.830464Z
+ r10242@not: autrijus | 2004-11-06T06:59:58.274625Z
+ * Schema changes for Pg, SQLite, mysql and Oracle.
+
+
+ r6209@tinbook (orig r1724): autrijus | 2004-11-07T14:17:03.232005Z
+ r10242@not: autrijus | 2004-11-06T06:59:58.274625Z
+ * Schema changes for Pg, SQLite, mysql and Oracle.
+
+ r10243@not: autrijus | 2004-11-06T07:00:15.865682Z
+ * schema updater for Pg and mysql; Oracle comes later and SQLite is nontrivial.
+
+
+ r6212@tinbook (orig r1725): autrijus | 2004-11-07T14:22:06.654667Z
+ r10251@not: autrijus | 2004-11-06T10:17:29.202899Z
+ * fallouts from the OCFV schema change.
+
+
+
+ r6217@tinbook (orig r1726): autrijus | 2004-11-07T14:26:14.894843Z
+ r10252@not: autrijus | 2004-11-06T10:17:41.433720Z
+ * "make dropdb" on SQLite now works.
+
+
+
+ r6218@tinbook (orig r1727): autrijus | 2004-11-07T14:28:27.582072Z
+ r10253@not: autrijus | 2004-11-06T10:17:49.792085Z
+ * "make initdb" now an alias of "make initialize-database".
+
+
+
+ r6219@tinbook (orig r1728): autrijus | 2004-11-07T14:29:51.869060Z
+ r10256@not: autrijus | 2004-11-06T10:35:22.448152Z
+ * Escape ColumnMap return values properly to prevent cross-site scripting
+ attack. All HTML snippets are now returned as scalar references.
+
+
+ r6220@tinbook (orig r1729): autrijus | 2004-11-07T14:30:06.090844Z
+ r10260@not: autrijus | 2004-11-07T08:54:20.330259Z
+ * Query builder now acts on multiple clauses.
+ * Close a potential XSS bug by escaping the clause naems.
+ * Refactor the clause display component.
+
+
+ r6221@tinbook (orig r1730): autrijus | 2004-11-07T14:30:22.730305Z
+ r10261@not: autrijus | 2004-11-07T08:55:02.919657Z
+ * loc and layout fixed.
+
+
+ r6222@tinbook (orig r1731): autrijus | 2004-11-07T14:32:12.241052Z
+ r10263@not: autrijus | 2004-11-07T14:15:05.153388Z
+ * one loc fix.
+
+
+ r6226@tinbook (orig r1734): autrijus | 2004-11-07T14:38:43.706169Z
+ * In query builder, parse custom fields by name.
+ * Generate all Link-type result cell callbacks from %LINKTYPEMAP.
+ * Display custom field contents, separated by newlines.
+ * For Image custom fields we also show a thumbnail.
+
+
+
+
+
+------------------------------------------------------------------------
+r1858 | jesse | 2004-11-11 03:45:56 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+
+ r9120@tinbook: jesse | 2004-11-11T04:07:29.821876Z
+ r6225@tinbook: jesse | 2004-11-07T14:44:15.591492Z
+ Web
+ * Refactore BuildFormatString to simplify logic and improve reliability
+
+
+
+------------------------------------------------------------------------
+r1857 | jesse | 2004-11-11 03:45:39 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+
+ r9119@tinbook: jesse | 2004-11-11T04:07:26.321289Z
+ r6201@tinbook: jesse | 2004-11-07T05:35:22.316024Z
+ Refactoring and restyling the Ticket List to present prettier tables with more human-readable names
+
+
+
+------------------------------------------------------------------------
+r1856 | jesse | 2004-11-11 03:45:19 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectLinks
+
+ r9118@tinbook: jesse | 2004-11-11T04:07:23.596293Z
+ r6200@tinbook: jesse | 2004-11-07T05:32:56.068727Z
+ Web: Fixed SelectLinks component to contain the right options and present the right human-readable names
+
+
+
+
+------------------------------------------------------------------------
+r1855 | jesse | 2004-11-11 03:45:00 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditFormat
+
+ r9117@tinbook: jesse | 2004-11-11T04:07:21.090324Z
+ r6199@tinbook: jesse | 2004-11-07T05:30:58.330526Z
+ Web UI: Refactored the logic which builds format strings to simplify code and present better human-readable names
+
+
+
+------------------------------------------------------------------------
+r1854 | jesse | 2004-11-11 03:44:40 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+
+ r9116@tinbook: jesse | 2004-11-11T03:02:54.980441Z
+ r6198@tinbook: jesse | 2004-11-07T04:18:22.934311Z
+ Devel: Changed severity of calling Principal->HasRight without a Right
+
+
+------------------------------------------------------------------------
+r1853 | jesse | 2004-11-11 03:44:19 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+
+ r9115@tinbook: jesse | 2004-11-11T03:02:19.638154Z
+ r6197@tinbook: jesse | 2004-11-07T03:38:25.380100Z
+ Switching Quicksearch to use most standard CSS styles
+
+
+
+------------------------------------------------------------------------
+r1852 | jesse | 2004-11-11 03:44:01 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+
+ r9114@tinbook: jesse | 2004-11-11T03:02:17.607069Z
+ r6196@tinbook: jesse | 2004-11-07T03:37:32.303504Z
+ Squash warnings when trying to check acls on nonexisten custom fields
+
+
+
+------------------------------------------------------------------------
+r1851 | jesse | 2004-11-11 03:43:44 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r9113@tinbook: jesse | 2004-11-11T03:02:15.074540Z
+ r6195@tinbook: jesse | 2004-11-06T15:14:59.241373Z
+ Better handling of Record deletion confirmation messages. (To go with new searchbuilder fixes)
+
+
+
+------------------------------------------------------------------------
+r1850 | jesse | 2004-11-11 03:43:27 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r9112@tinbook: jesse | 2004-11-11T03:02:12.914087Z
+ r6175@tinbook: jesse | 2004-11-05T02:17:24.427390Z
+ Improved comments and folds in RT::Record
+
+
+
+
+------------------------------------------------------------------------
+r1849 | jesse | 2004-11-11 03:43:09 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+
+ r9111@tinbook: jesse | 2004-11-11T03:02:10.088168Z
+ r6174@tinbook: jesse | 2004-11-05T02:17:10.144816Z
+ When uploading image custom fields, they're handled as file descriptors, rather than scalars. Our old logic assumed that any ref was an array. Now we check for array refs explicitly.
+
+
+
+------------------------------------------------------------------------
+r1848 | jesse | 2004-11-11 03:42:50 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+
+ r9110@tinbook: jesse | 2004-11-11T03:02:08.247722Z
+ r6173@tinbook: jesse | 2004-11-05T02:14:56.442006Z
+ Added a HUP handler for the standalone httpd (so you can kill -HUP it to reload)
+
+
+
+------------------------------------------------------------------------
+r1847 | jesse | 2004-11-11 03:42:31 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFields
+
+ r9109@tinbook: jesse | 2004-11-11T03:02:06.365889Z
+ r6172@tinbook: jesse | 2004-11-05T02:14:19.720658Z
+ Improved list format for multiple custom field values
+
+
+
+------------------------------------------------------------------------
+r1846 | jesse | 2004-11-11 03:42:10 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CreateTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+
+ r9108@tinbook: jesse | 2004-11-11T03:00:31.422471Z
+ r6171@tinbook: jesse | 2004-11-05T02:14:02.424154Z
+ More form Names to make testing easier
+
+
+
+------------------------------------------------------------------------
+r1845 | jesse | 2004-11-11 03:41:49 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+
+ r9107@tinbook: jesse | 2004-11-11T03:00:29.366770Z
+ r6170@tinbook: jesse | 2004-11-04T08:33:12.626307Z
+ Added a couple of form NAME elements, to ease testing
+
+
+
+------------------------------------------------------------------------
+r1844 | jesse | 2004-11-11 03:41:21 -0500 (Thu, 11 Nov 2004) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r9106@tinbook: jesse | 2004-11-11T03:00:27.170066Z
+ r6169@tinbook: jesse | 2004-11-04T08:32:13.034112Z
+ Added a Delete method to RT::Record so we get proper messages on object deletion
+ --Autrijus
+
+
+
+
+------------------------------------------------------------------------
+r1843 | jesse | 2004-11-11 03:40:58 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+
+ r9105@tinbook: jesse | 2004-11-11T03:00:24.963220Z
+ r6157@tinbook: jesse | 2004-11-01T04:52:49.078521Z
+ A patch from 3.2 accidentally cascaded forward
+
+
+------------------------------------------------------------------------
+r1842 | jesse | 2004-11-11 03:40:37 -0500 (Thu, 11 Nov 2004) | 13 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+
+ r9104@tinbook: jesse | 2004-11-11T03:00:21.588955Z
+ r6156@tinbook: jesse | 2004-11-01T04:47:06.225531Z
+ r6152@tinbook: jesse | 2004-11-01T04:43:38.864654Z
+ RT-Ticket: 6230
+ RT-Status: resolved
+ RT-Update: correspond
+
+ New "batch transactions" core from Ruslan. This update makes it much easier to use stock conditions for bulk updates
+
+
+
+
+
+------------------------------------------------------------------------
+r1841 | jesse | 2004-11-11 03:40:15 -0500 (Thu, 11 Nov 2004) | 14 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+
+ r9103@tinbook: jesse | 2004-11-11T03:00:19.163116Z
+ r6155@tinbook: jesse | 2004-11-01T04:47:05.144200Z
+ r6151@tinbook: jesse | 2004-11-01T04:42:20.034865Z
+ RT-Ticket: 6242
+ RT-Status: resolved
+ RT-Update: correspond
+
+ There were a couple of bugs in the bulk update page that broke ticket linking.
+ (Among other things, the custom fields update functionality was completely broken. It's been disabled pending a new implementation)
+
+
+
+
+
+------------------------------------------------------------------------
+r1840 | jesse | 2004-11-11 03:39:53 -0500 (Thu, 11 Nov 2004) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+
+ r9102@tinbook: jesse | 2004-11-11T03:00:17.230099Z
+ r6149@tinbook: jesse | 2004-11-01T03:50:49.511535Z
+ RT-Ticket: 6245
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Global custom field editing in 3.3 had the wrong (hardcoded) URL target.
+
+
+
+
+
+------------------------------------------------------------------------
+r1839 | jesse | 2004-11-11 03:39:27 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+
+ r9101@tinbook: jesse | 2004-11-11T03:00:14.757629Z
+ r6148@tinbook: jesse | 2004-11-01T03:15:59.580167Z
+
+
+
+------------------------------------------------------------------------
+r1838 | jesse | 2004-11-11 03:39:09 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+
+ r9100@tinbook: jesse | 2004-11-11T03:00:12.926506Z
+ r6127@tinbook: jesse | 2004-10-27T22:02:28.387934Z
+ Bumping to 3.3.10
+
+
+
+
+------------------------------------------------------------------------
+r1837 | jesse | 2004-11-11 03:33:52 -0500 (Thu, 11 Nov 2004) | 14 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+
+ r9099@tinbook: jesse | 2004-11-11T03:00:10.407857Z
+ r6121@tinbook: jesse | 2004-10-27T07:55:33.542549Z
+ r6115@tinbook: jesse | 2004-10-27T07:51:39.957035Z
+ RT-Ticket: 6219
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Rebuilt RT::Tickets' watcher search logic to only use _one_ join to the users/groups table so as to create joins that can be computed in finite time. Theoretically, this makes watcher searching less powerful. In practice, it makes watcher searching a lot more possible.
+
+
+
+
+
+
+------------------------------------------------------------------------
+r1836 | jesse | 2004-11-11 03:33:34 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+
+ r9098@tinbook: jesse | 2004-11-11T03:00:08.493264Z
+ r6120@tinbook: jesse | 2004-10-27T07:55:32.882088Z
+ r6109@tinbook: jesse | 2004-10-26T20:02:02.857695Z
+ Additional fixes for display of unlimited search results
+
+
+
+------------------------------------------------------------------------
+r1835 | jesse | 2004-11-11 03:33:03 -0500 (Thu, 11 Nov 2004) | 12 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+
+ r9097@tinbook: jesse | 2004-11-11T03:00:06.212366Z
+ r6119@tinbook: jesse | 2004-10-27T07:55:32.408440Z
+ r6046@tinbook: jesse | 2004-10-19T05:21:18.567580Z
+ RT-Ticket: 6121
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Fixed the name of the template used to notify non-privilged users when their password is changed
+
+
+
+
+------------------------------------------------------------------------
+r1834 | jesse | 2004-11-11 03:32:34 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.Pg
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.1.0/schema.Pg
+
+ r9096@tinbook: jesse | 2004-11-11T03:00:04.296789Z
+ r6118@tinbook: jesse | 2004-10-27T07:55:31.895658Z
+ r6007@tinbook: jesse | 2004-10-14T21:47:50.297291Z
+ RT-Ticket: 6197
+ RT-Status: resolved
+
+ Removed a comment from the PostgreSQL schema files, as some versions of postgres fail to parse it well.
+
+
+
+
+------------------------------------------------------------------------
+r1833 | jesse | 2004-11-11 03:32:11 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+
+ r9095@tinbook: jesse | 2004-11-11T03:00:02.589971Z
+ r6117@tinbook: jesse | 2004-10-27T07:55:31.185793Z
+ r1994@tinbook: jesse | 2004-10-12T23:37:37.775608Z
+ Improved "stickiness" of queries.
+ Next/Prev navigation should be more consistently available now.
+ Most recent query should be available more consistently.
+
+
+
+
+------------------------------------------------------------------------
+r1832 | jesse | 2004-11-11 03:31:11 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+
+ r9094@tinbook: jesse | 2004-11-11T02:59:58.447349Z
+ r6113@tinbook: jesse | 2004-10-27T04:31:13.448231Z
+ Added support for recording transaction custom fields on ticket creation.
+
+
+
+------------------------------------------------------------------------
+r1831 | jesse | 2004-11-11 03:30:21 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+
+ r9093@tinbook: jesse | 2004-11-11T02:59:56.622603Z
+ r6101@tinbook: jesse | 2004-10-23T22:05:06.396023Z
+ Bumping to 3.3.9
+
+
+------------------------------------------------------------------------
+r1830 | jesse | 2004-11-11 03:30:02 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectEqualityOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectStatus
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectGroup
+
+ r9092@tinbook: jesse | 2004-11-11T02:59:53.704366Z
+ r6093@tinbook: jesse | 2004-10-23T21:47:53.224532Z
+ A bunch of tiny fixes to the web ui to avoid warnings about uninitialized variables.
+
+
+------------------------------------------------------------------------
+r1829 | jesse | 2004-11-11 03:29:43 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.SQLite
+
+ r9091@tinbook: jesse | 2004-11-11T02:59:52.024824Z
+ r6092@tinbook: jesse | 2004-10-23T21:46:47.129210Z
+ Updated SQLite schema to current RT 3.3 versions
+
+
+------------------------------------------------------------------------
+r1828 | jesse | 2004-11-11 03:29:18 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
+
+ r9090@tinbook: jesse | 2004-11-11T02:59:50.183743Z
+ r6091@tinbook: jesse | 2004-10-23T21:46:04.315427Z
+ Better checking for ACL queries on undefined objects (to avoid a warning).
+
+
+------------------------------------------------------------------------
+r1827 | jesse | 2004-11-11 03:28:55 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r9089@tinbook: jesse | 2004-11-11T02:59:48.064760Z
+ r6090@tinbook: jesse | 2004-10-23T21:44:41.851286Z
+ Fixed a warning when objects were created with no names
+
+
+
+------------------------------------------------------------------------
+r1826 | jesse | 2004-11-11 03:28:34 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+
+ r9088@tinbook: jesse | 2004-11-11T02:59:45.857398Z
+ r6089@tinbook: jesse | 2004-10-23T21:43:19.939096Z
+ Fixed message returned on password change to not include md5 hash of password
+
+
+
+------------------------------------------------------------------------
+r1825 | jesse | 2004-11-11 03:28:16 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r9087@tinbook: jesse | 2004-11-11T02:59:43.762275Z
+ r6088@tinbook: jesse | 2004-10-23T21:42:23.542584Z
+ Bumped DBIx::SearchBuilder required version, for new SQLite support
+
+
+------------------------------------------------------------------------
+r1824 | jesse | 2004-11-11 03:27:55 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/factory
+
+ r9086@tinbook: jesse | 2004-11-11T02:59:36.539036Z
+ r6077@tinbook: jesse | 2004-10-21T17:20:26.237576Z
+ Core: integrated new factory code to allow more datamodel metadata to be collected
+
+
+------------------------------------------------------------------------
+r1823 | jesse | 2004-11-11 03:27:35 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+
+ r9085@tinbook: jesse | 2004-11-11T02:59:34.761573Z
+ r6076@tinbook: jesse | 2004-10-21T16:46:20.466296Z
+ ACL: Fixed logic in an ACL query so that it returns failure when handed an invalid object, rather than trying a bogus SQL query and failing.
+
+
+------------------------------------------------------------------------
+r1822 | jesse | 2004-11-11 03:27:16 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r9084@tinbook: jesse | 2004-11-11T02:59:32.464211Z
+ r6074@tinbook: jesse | 2004-10-20T23:26:48.112240Z
+ RT-Ticket: 5701
+ RT-Status: resolved
+ RT-Update: correspond
+
+ RT now prevents users from creating objects with 'Name' attributes that are numreric.
+
+
+
+------------------------------------------------------------------------
+r1821 | jesse | 2004-11-11 03:26:56 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+
+ r9083@tinbook: jesse | 2004-11-11T02:59:30.423030Z
+ r6071@tinbook: jesse | 2004-10-20T22:56:43.545883Z
+ RT-Ticket: 6192
+ RT-Status: update
+
+
+ Updated RT::Attachement::OriginalContent to use a better regexp for determining what's encoded and what's not.
+
+
+
+
+------------------------------------------------------------------------
+r1820 | jesse | 2004-11-11 03:26:38 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/nl.po
+
+ r9082@tinbook: jesse | 2004-11-11T02:59:27.664780Z
+ r6070@tinbook: jesse | 2004-10-20T22:51:44.854897Z
+ RT-Ticket: 6123
+ RT-Status: resolved
+ RT-Update: correspond
+
+
+ Updated the dutch translation. (As the translation hadn't been applied for a bit, some translations didn't apply cleanly.)
+
+
+
+------------------------------------------------------------------------
+r1819 | jesse | 2004-11-11 03:26:15 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/Configuration.html
+
+ r9081@tinbook: jesse | 2004-11-11T02:59:25.937757Z
+ r6064@tinbook: jesse | 2004-10-20T22:08:13.953728Z
+ Fixed a typo
+
+
+
+------------------------------------------------------------------------
+r1818 | jesse | 2004-11-11 03:25:52 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+
+ r9080@tinbook: jesse | 2004-11-11T02:59:23.705558Z
+ r6063@tinbook: jesse | 2004-10-20T19:54:02.885732Z
+ RT-Ticket: 6180
+ RT-Status: resolved
+ RT-Update: correspond
+
+ If you have changed $MinimumPasswordLength to be greater than 6, RT's "GenerateRandomPassword" method now takes that into account
+
+
+
+
+------------------------------------------------------------------------
+r1817 | jesse | 2004-11-11 03:25:33 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/Configuration.html
+
+ r9079@tinbook: jesse | 2004-11-11T02:59:21.916469Z
+ r6062@tinbook: jesse | 2004-10-20T19:46:00.816618Z
+ RT-Ticket: 6188
+ RT-Status: resolved
+ RT-Update: correspond
+
+ RT squelches passwords in the configuration summary page. It no longer
+ squleches "RT::MinimumPasswordLength"
+
+
+
+------------------------------------------------------------------------
+r1816 | jesse | 2004-11-11 03:25:14 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+
+ r9078@tinbook: jesse | 2004-11-11T02:59:19.778677Z
+ r6061@tinbook: jesse | 2004-10-20T19:27:22.698792Z
+ Minor cleanups to record.pm
+
+
+------------------------------------------------------------------------
+r1815 | jesse | 2004-11-11 03:24:52 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/EmailParser.pm
+
+ r9077@tinbook: jesse | 2004-11-11T02:59:17.907274Z
+ r6056@tinbook: jesse | 2004-10-20T19:20:39.940786Z
+ RT-Ticket: 5594
+ RT-Status: resolved
+ RT-UpdateType: correspond
+
+ EmailParser updates applied to RT 3.3 to fix subject lines that are encoded and multi-line
+
+
+
+------------------------------------------------------------------------
+r1814 | jesse | 2004-11-11 03:24:34 -0500 (Thu, 11 Nov 2004) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+
+ r9076@tinbook: jesse | 2004-11-11T02:59:15.685769Z
+ r6055@tinbook: jesse | 2004-10-20T18:56:31.219026Z
+ RT-Ticket: 5569
+ RT-Status: resolved
+
+ Status is nowdefinablein the RT_Config file.
+
+
+
+------------------------------------------------------------------------
+r1813 | jesse | 2004-11-11 03:16:42 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+
+ r9075@tinbook: jesse | 2004-11-11T02:55:01.141993Z
+ r6051@tinbook: jesse | 2004-10-20T05:39:54.597284Z
+ RT-Ticket: 5837
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Moved more ticket listing style to the CSS and out of the html, per the suggestion of Dirk Pape
+
+
+
+
+------------------------------------------------------------------------
+r1812 | jesse | 2004-11-11 03:16:24 -0500 (Thu, 11 Nov 2004) | 11 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+
+ r9074@tinbook: jesse | 2004-11-11T02:54:58.891576Z
+ r6050@tinbook: jesse | 2004-10-20T05:33:26.027966Z
+ RT-Ticket: 5729
+ RT-Status: resolved
+ RT-Action: correspond
+
+ Patch fixes issue with multibyte characters escaping in URLs. --Ruslan
+
+
+
+
+------------------------------------------------------------------------
+r1811 | jesse | 2004-11-11 03:16:05 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/config.layout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/m4/rt_layout.m4
+
+ r9073@tinbook: jesse | 2004-11-11T02:54:57.035812Z
+ r6048@tinbook: jesse | 2004-10-19T05:24:15.256721Z
+ RT-Ticket: 6198
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Applied a patch to add an fhs-compliant RT layout and fix some of the layout parsing.
+
+
+
+------------------------------------------------------------------------
+r1810 | jesse | 2004-11-11 03:15:47 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+
+ r9072@tinbook: jesse | 2004-11-11T02:54:55.160063Z
+ r6044@tinbook: jesse | 2004-10-19T05:18:31.025176Z
+ RT-Ticket: 6154
+ RT-Status: resolved
+ RT-Update: reply
+
+ Owner listings now sort alphabetically
+
+
+
+------------------------------------------------------------------------
+r1809 | jesse | 2004-11-11 03:15:22 -0500 (Thu, 11 Nov 2004) | 10 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT.pm.in
+
+ r9071@tinbook: jesse | 2004-11-11T02:54:53.317739Z
+ r6042@tinbook: jesse | 2004-10-19T05:10:45.782060Z
+ RT-Ticket: 6199
+ RT-Status: resolved
+ RT-Update: correspond
+
+ Formatting fixes to RT.pm
+
+
+
+------------------------------------------------------------------------
+r1808 | jesse | 2004-11-11 03:15:04 -0500 (Thu, 11 Nov 2004) | 9 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/initialdata
+
+ r9070@tinbook: jesse | 2004-11-11T02:54:51.524660Z
+ r6039@tinbook: jesse | 2004-10-18T17:48:14.238206Z
+ RT-Ticket: 5336
+ RT-Status: resolved
+
+ Approval template corrected
+
+
+
+------------------------------------------------------------------------
+r1807 | jesse | 2004-11-11 03:14:32 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+
+ r9069@tinbook: jesse | 2004-11-11T02:54:49.799723Z
+ r6014@tinbook: jesse | 2004-10-15T18:26:19.439204Z
+ Bumping to 3.3.8
+
+
+------------------------------------------------------------------------
+r1806 | jesse | 2004-11-11 03:09:20 -0500 (Thu, 11 Nov 2004) | 6 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/GlobalCustomFieldTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/GroupTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ObjectCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SystemTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/UserTabs
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Groups.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Tickets.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Queue-Transactions.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/Users.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/index.html
+
+ r9068@tinbook: jesse | 2004-11-11T02:54:47.265494Z
+ r6009@tinbook: jesse | 2004-10-15T17:57:36.162346Z
+ Administrative menu overhaul to sanitize custom field configuration.
+
+
+
+------------------------------------------------------------------------
+r1805 | jesse | 2004-11-11 03:08:54 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+
+ r9067@tinbook: jesse | 2004-11-11T02:54:44.745160Z
+ r1992@tinbook: jesse | 2004-10-12T21:00:33.006657Z
+ RT-Ticket: 6182
+ RT-Status: resolved
+
+
+
+------------------------------------------------------------------------
+r1804 | jesse | 2004-11-11 03:08:26 -0500 (Thu, 11 Nov 2004) | 5 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+
+ r9066@tinbook: jesse | 2004-11-11T02:54:42.174913Z
+ r1985@tinbook: jesse | 2004-10-11T20:26:31.046336Z
+ Fixed a bug in the merge from 3.2 that broke ticket transaction searching; added support for Ticket "Updated" as a synonym for TransactionDate as a search criterion
+
+
+------------------------------------------------------------------------
+r1803 | jesse | 2004-11-11 03:08:01 -0500 (Thu, 11 Nov 2004) | 7 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.tsv
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+
+ r9065@tinbook: jesse | 2004-11-11T02:54:36.542352Z
+ r1652@tinbook: jesse | 2004-10-10T04:30:49.336195Z
+ r1650@tinbook: jesse | 2004-10-10T04:23:58.456281Z
+
+
+
+
+------------------------------------------------------------------------
+r1802 | jesse | 2004-11-11 03:03:05 -0500 (Thu, 11 Nov 2004) | 8 lines
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/Makefile.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/README
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/UPGRADING
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.fcgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.scgi.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/mason_handler.svc.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-crontool.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt-mailgate.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/rt.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/standalone_httpd.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/bin/webmux.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/RT_Config.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/drop.Oracle
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/schema.Sybase
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.1.0/schema.Pg
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade/3.3.0/schema.mysql
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/Objects.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/CustomFields/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CreateUserCalled
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/CustomFieldTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditTemplates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/EditUserComments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/GroupTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ListGlobalScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ModifyTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ObjectCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/PickObjects
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/QueueTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldLookupType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectModifyUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScrip
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripAction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectSingleOrMultiple
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectStage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectTemplate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/SystemTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/ToolTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Elements/UserTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Scrip.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Scrips.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/Templates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Global/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/CustomField.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/GroupRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/People.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Scrip.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Scrips.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Template.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/Templates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/UserRights.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Queues/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/Configuration.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Tools/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/CustomFields.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Memberships.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/Users/index.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Admin/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/Approve
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/ShowDependency
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Approvals/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Download/CustomFieldValue/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Download/Tabular/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Callback
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Checkbox
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Header
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/ParseFormat
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CollectionAsTable/Row
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CreateTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldBinary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldFreeform
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldImage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldSelect
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditCustomFieldText
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/EditLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Error
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Menu
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MessageBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyTickets
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QueryString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QuickCreate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/RT__Ticket/ColumnMap
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Refresh
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ScrubHTML
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Section
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectAttachmentField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectBoolean
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectCustomFieldValue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateRelation
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectDateType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectEqualityOperator
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectGroups
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLang
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectLinkType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectMatch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectOwner
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectResultsPerPage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectSortOrder
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectStatus
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketSortBy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectTicketTypes
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectUsers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectWatcherType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SetupSessionCookie
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFieldImage
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLink
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ShowMemberships
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SimpleSearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Submit
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TicketList
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBox
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxEnd
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxStart
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/ViewUser
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Logout.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/Reminder.html
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/images/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/queue/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/queue/ns
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/ticket/links
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/user/default
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/Forms/user/ns
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/logout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/search/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/search/ticket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/comment
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/link
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/REST/1.0/ticket/merge
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Build.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Bulk.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Edit.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/BuildFormatString
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/DisplayOptions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditFormat
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/EditSearches
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/NewListActions
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCFs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/PickCriteria
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SearchPrivacy
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectAndOr
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectGroup
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectPersonType
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchObject
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/SelectSearchesForObjects
+ D /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Elements/TicketRow
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.rdf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Search/Results.tsv
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Attachment/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Closed.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/GotoTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Error.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/SelfService/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Attachment/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Create.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Display.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/AddWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/BulkLinks
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomField
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditPeople
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/EditWatchers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/FindAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowBasics
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowCustomFields
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDates
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowDependencies
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowHistory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMemberOf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMembers
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMessageHeaders
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowMessageStanza
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowPeople
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowReferences
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowSummary
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/ShowUserEntry
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/History.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyAll.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyDates.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyLinks.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ModifyPeople.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Update.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/MyDay.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Tools/Offline.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Delegation.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/DelegateRights
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/GroupTabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Members.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/Modify.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Groups/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/User/Prefs.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/autohandler
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/l
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/AutoOpen.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Autoreply.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/EscalatePriority.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/Notify.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/NotifyAsComment.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/RecordComment.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/RecordCorrespondence.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/ResolveMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SendEmail.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/SetPriority.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Action/UserDefined.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Attributes_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Base.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/AnyTransaction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/BeforeDue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/Overdue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/OwnerChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/PriorityChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/PriorityExceeds.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/QueueChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/StatusChange.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Condition/UserDefined.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CurrentUser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Date.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/EmailParser.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Groups_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Handle.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/cs.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/da.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/de.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/en_malkovich.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/es.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fi.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/fr.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/he.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/hu.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/i_default.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/it.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ja.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/nl.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/no.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/pt_br.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/ru.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_cn.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N/zh_tw.po
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/I18N.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/CLI.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Auth/GnuPG.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Email.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/REST.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web/Handler.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Interface/Web.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Link_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Links_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomField_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ObjectCustomFields_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principals_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Queues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Record.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Scrips_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/ActiveTicketsInQueue.pm
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/FromSQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Search/Generic.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/SearchBuilder.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/StyleGuide.pod
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/System.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Template_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Templates_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValue_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/TicketCustomFieldValues_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/base.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/URI.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Users_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT.pm.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/02regression.t.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/03web.pl.in
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/05cronsupport.pl.in
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/06mailgateway.pl.in
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/07acl.pl.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/releng.cnf
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/extract-message-catalog
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/extract_pod_tests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/factory
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/license_tag
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/regression_harness
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-dump-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-setup-database.in
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/sbin/rt-test-dependencies.in
+
+ r9063@tinbook: jesse | 2004-11-11T02:43:02.429915Z
+
+
+ r9064@tinbook: jesse | 2004-11-11T02:43:31.814891Z
+ r1651@tinbook: jesse | 2004-10-10T04:24:17.077128Z
+
+
+
+------------------------------------------------------------------------
+r1718 | autrijus | 2004-11-05 03:56:10 -0500 (Fri, 05 Nov 2004) | 3 lines
+Changed paths:
+ M /rt
+ M /rt/branches
+ M /rt/branches/3.2-RELEASE
+ M /rt/branches/3.2-SYBASE-TESTING
+ M /rt/branches/3.3-TESTING
+ M /rt/branches/3.3-TESTING/lib/RT/Queue_Overlay.pm
+ M /rt/branches/3.3-TESTING/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/etc/upgrade
+
+* Log::Dispatch wants ->warning, not ->warn.
+
+
+------------------------------------------------------------------------
+r1637 | jesse | 2004-10-07 17:02:52 -0400 (Thu, 07 Oct 2004) | 1 line
+Changed paths:
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/configure.ac
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/CreateTicket
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Footer
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Header
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Login
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Menu
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyRequests
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/MyTickets
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/PageLayout
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/QuickCreate
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/Quicksearch
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/SelectNewTicketQueue
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Elements/TitleBoxStart
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/NoAuth/webrt.css
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/Ticket/Elements/Tabs
+ M /rt/branches/PLATANO-EXPERIMENTAL-CSS/html/index.html
+
+Start of CSSification of the RT UI
+------------------------------------------------------------------------
+r1636 | jesse | 2004-10-07 16:42:35 -0400 (Thu, 07 Oct 2004) | 1 line
+Changed paths:
+ A /rt/branches/PLATANO-EXPERIMENTAL-CSS (from /rt/branches/PLATANO-EXPERIMENTAL:1630)
+
+
+------------------------------------------------------------------------
+r1264 | autrijus | 2004-07-19 22:56:01 -0400 (Mon, 19 Jul 2004) | 8 lines
+Changed paths:
+ M /rt
+ M /rt/branches/3.2-RELEASE/html/Elements/Header
+ M /rt/branches/3.2-RELEASE/html/Elements/SetupSessionCookie
+ M /rt/branches/3.2-RELEASE/html/NoAuth/webrt.css
+ M /rt/branches/3.3-TESTING/html/Download/Tabular/dhandler
+ M /rt/branches/PLATANO-EXPERIMENTAL/docs/design_docs/ruleset-workflow.txt
+
+
+ r6188@not: autrijus | 2004-07-20T02:52:39.146361Z
+
+
+
+
+
+
+------------------------------------------------------------------------
+r1259 | autrijus | 2004-07-16 17:52:53 -0400 (Fri, 16 Jul 2004) | 2 lines
+Changed paths:
+ M /rt
+ A /rt/branches/PLATANO-EXPERIMENTAL/docs/design_docs/ruleset-workflow.txt
+
+* design docs for ruleset/workflows
+
+------------------------------------------------------------------------
+r1227 | jesse | 2004-07-13 15:15:10 -0400 (Tue, 13 Jul 2004) | 1 line
+Changed paths:
+ A /rt/branches/PLATANO-EXPERIMENTAL (from /rt/branches/3.3-TESTING:1226)
+
+Branching PLATANO for hacking (This will be 3.5)
+------------------------------------------------------------------------
+r1224 | jesse | 2004-07-13 14:41:42 -0400 (Tue, 13 Jul 2004) | 1 line
+Changed paths:
+ A /rt/branches/3.3-TESTING (from /rt/branches/rt-3.3:1223)
+ D /rt/branches/rt-3.3
+
+Moving RT 3.0 from "head" to a 3.0-MAINT branch
+------------------------------------------------------------------------
+r1209 | autrijus | 2004-07-12 16:22:48 -0400 (Mon, 12 Jul 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.1/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/etc/upgrade/3.1.0/schema.SQLite
+
+ ----------------------------------------------------------------------
+ r5990@not: autrijus | 2004-07-12T20:22:03.588615Z
+
+ * Let the cf param in Ticket->CustomFieldValues be optional,
+ so we can get Atom tests to work.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1205 | autrijus | 2004-07-12 13:30:03 -0400 (Mon, 12 Jul 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/sbin/rt-setup-database.in
+
+ ----------------------------------------------------------------------
+ r5976@not: autrijus | 2004-07-12T17:29:13.392510Z
+
+ * The warning on missing ACL files used $RT::EtcPath even if it's
+ reading it fro other paths. Fixed.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1180 | autrijus | 2004-07-05 15:43:17 -0400 (Mon, 05 Jul 2004) | 6 lines
+Changed paths:
+ M /rt
+ A /rt/branches/rt-3.3/sbin/rt-dump-database.in
+
+ ----------------------------------------------------------------------
+ r5913@not: autrijus | 2004-07-05T19:41:37.720057Z
+
+ * add rt-dump-database, a tool to dump config data to initialdata format.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1179 | autrijus | 2004-07-05 12:56:22 -0400 (Mon, 05 Jul 2004) | 8 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+
+ ----------------------------------------------------------------------
+ r5899@not: autrijus | 2004-07-05T15:13:55.850880Z
+
+ * correct the documentation for CreateTickets; it was still referring
+ to the archaic "Approvals" queue and "Approval" type, whereas now we're
+ using "___Approvals" and "approval" now.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1160 | jesse | 2004-06-27 23:10:54 -0400 (Sun, 27 Jun 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/releng.cnf
+
+ ----------------------------------------------------------------------
+ r1997@tinbook: jesse | 2004-06-28T03:10:36.434329Z
+
+ Bumping to 3.3.4
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1159 | jesse | 2004-06-27 23:08:01 -0400 (Sun, 27 Jun 2004) | 24 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/Makefile.in
+ M /rt/branches/rt-3.3/UPGRADING
+ M /rt/branches/rt-3.3/bin/mason_handler.fcgi.in
+ M /rt/branches/rt-3.3/bin/mason_handler.scgi.in
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/configure.ac
+ M /rt/branches/rt-3.3/etc/RT_Config.pm.in
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditCustomField
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditScrip
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditTemplates
+ M /rt/branches/rt-3.3/html/Admin/Elements/UserTabs
+ M /rt/branches/rt-3.3/html/Admin/Global/GroupRights.html
+ M /rt/branches/rt-3.3/html/Admin/Global/Template.html
+ M /rt/branches/rt-3.3/html/Admin/Global/UserRights.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/GroupRights.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/Members.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/Modify.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/UserRights.html
+ M /rt/branches/rt-3.3/html/Admin/Queues/GroupRights.html
+ M /rt/branches/rt-3.3/html/Admin/Queues/Modify.html
+ M /rt/branches/rt-3.3/html/Admin/Queues/Template.html
+ M /rt/branches/rt-3.3/html/Admin/Queues/UserRights.html
+ M /rt/branches/rt-3.3/html/Admin/Users/Modify.html
+ M /rt/branches/rt-3.3/html/Approvals/index.html
+ M /rt/branches/rt-3.3/html/Search/Bulk.html
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+ M /rt/branches/rt-3.3/html/SelfService/Prefs.html
+ M /rt/branches/rt-3.3/html/Ticket/Display.html
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/rt-3.3/html/Ticket/ModifyAll.html
+ M /rt/branches/rt-3.3/html/Ticket/ModifyDates.html
+ M /rt/branches/rt-3.3/html/Ticket/ModifyLinks.html
+ M /rt/branches/rt-3.3/html/Ticket/Update.html
+ M /rt/branches/rt-3.3/html/User/Delegation.html
+ M /rt/branches/rt-3.3/html/User/Groups/Members.html
+ M /rt/branches/rt-3.3/html/User/Groups/Modify.html
+ M /rt/branches/rt-3.3/html/User/Prefs.html
+ M /rt/branches/rt-3.3/html/autohandler
+ M /rt/branches/rt-3.3/lib/RT/I18N/da.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/de.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/en_malkovich.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/es.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fi.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fr.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/he.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/it.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ja.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/nl.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/no.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/pt_br.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ru.po
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/SearchBuilder.pm
+ M /rt/branches/rt-3.3/lib/RT/Templates_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+
+ ----------------------------------------------------------------------
+ r1769@tinbook: jesse | 2004-06-01T20:22:38.640731Z
+
+
+ ----------------------------------------------------------------------
+ r1902@tinbook: jesse | 2004-06-17T01:08:53.996154Z
+
+
+ ----------------------------------------------------------------------
+ r1991@tinbook: jesse | 2004-06-28T02:03:57.669851Z
+
+
+ ----------------------------------------------------------------------
+ r1992@tinbook: jesse | 2004-06-28T02:19:36.386707Z
+
+ Pulling forward from RT 3.1
+
+
+ ----------------------------------------------------------------------
+ r1993@tinbook: jesse | 2004-06-28T03:06:56.930396Z
+
+ Merging forward from 3.1
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1153 | autrijus | 2004-06-27 00:02:47 -0400 (Sun, 27 Jun 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/Tools/Offline.html
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+
+ ----------------------------------------------------------------------
+ r5788@not: autrijus | 2004-06-27T04:02:12.644291Z
+
+ * import 3.1 again due to glitches in Offline.html
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1150 | autrijus | 2004-06-26 16:31:56 -0400 (Sat, 26 Jun 2004) | 14 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/Makefile.in
+ M /rt/branches/rt-3.3/README
+ M /rt/branches/rt-3.3/UPGRADING
+ M /rt/branches/rt-3.3/bin/mason_handler.fcgi.in
+ D /rt/branches/rt-3.3/bin/rt-commit-handler.in
+ M /rt/branches/rt-3.3/bin/rt-crontool.in
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/configure.ac
+ M /rt/branches/rt-3.3/etc/RT_Config.pm.in
+ M /rt/branches/rt-3.3/etc/acl.mysql
+ M /rt/branches/rt-3.3/etc/initialdata
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.17
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.17/content
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditScrip
+ D /rt/branches/rt-3.3/html/Admin/Elements/ModifyQueue
+ D /rt/branches/rt-3.3/html/Admin/Elements/ModifyUser
+ M /rt/branches/rt-3.3/html/Admin/Queues/People.html
+ M /rt/branches/rt-3.3/html/Admin/Users/Modify.html
+ D /rt/branches/rt-3.3/html/Admin/Users/Prefs.html
+ D /rt/branches/rt-3.3/html/Developer
+ M /rt/branches/rt-3.3/html/Elements/EditLinks
+ M /rt/branches/rt-3.3/html/Elements/Header
+ M /rt/branches/rt-3.3/html/Elements/ScrubHTML
+ M /rt/branches/rt-3.3/html/Elements/SelectOwner
+ M /rt/branches/rt-3.3/html/Elements/SelectStatus
+ M /rt/branches/rt-3.3/html/Elements/TicketList
+ M /rt/branches/rt-3.3/html/Elements/TitleBoxStart
+ M /rt/branches/rt-3.3/html/NoAuth/webrt.css
+ M /rt/branches/rt-3.3/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/rt-3.3/html/REST/1.0/ticket/link
+ M /rt/branches/rt-3.3/html/Search/Build.html
+ M /rt/branches/rt-3.3/html/Search/Bulk.html
+ M /rt/branches/rt-3.3/html/Search/Edit.html
+ M /rt/branches/rt-3.3/html/Search/Elements/BuildFormatString
+ M /rt/branches/rt-3.3/html/Search/Elements/EditFormat
+ M /rt/branches/rt-3.3/html/Search/Elements/EditSearches
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+ M /rt/branches/rt-3.3/html/Search/Results.html
+ M /rt/branches/rt-3.3/html/SelfService/Display.html
+ M /rt/branches/rt-3.3/html/SelfService/Update.html
+ M /rt/branches/rt-3.3/html/Ticket/Create.html
+ M /rt/branches/rt-3.3/html/Ticket/Elements/EditBasics
+ M /rt/branches/rt-3.3/html/Ticket/Elements/EditPeople
+ M /rt/branches/rt-3.3/html/Ticket/Elements/EditWatchers
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowDates
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowGroupMembers
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowHistory
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowPeople
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowSummary
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowUserEntry
+ M /rt/branches/rt-3.3/html/Ticket/Elements/Tabs
+ M /rt/branches/rt-3.3/html/Ticket/ModifyAll.html
+ M /rt/branches/rt-3.3/html/Ticket/ModifyLinks.html
+ M /rt/branches/rt-3.3/html/Ticket/Update.html
+ M /rt/branches/rt-3.3/html/Tools/MyDay.html
+ M /rt/branches/rt-3.3/html/Tools/Offline.html
+ M /rt/branches/rt-3.3/html/User/Prefs.html
+ M /rt/branches/rt-3.3/html/index.html
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/EscalatePriority.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/PriorityChange.pm
+ M /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+ M /rt/branches/rt-3.3/lib/RT/EmailParser.pm
+ M /rt/branches/rt-3.3/lib/RT/I18N/cs.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/da.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/de.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/en_malkovich.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/es.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fi.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fr.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/he.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/it.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ja.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/nl.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/no.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/pt_br.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ru.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_cn.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_tw.po
+ M /rt/branches/rt-3.3/lib/RT/Interface/Email.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/Queue_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT.pm.in
+ M /rt/branches/rt-3.3/lib/t/00smoke.t.in
+ M /rt/branches/rt-3.3/lib/t/02regression.t.in
+ M /rt/branches/rt-3.3/lib/t/03web.pl.in
+ M /rt/branches/rt-3.3/sbin/rt-test-dependencies.in
+
+ ----------------------------------------------------------------------
+ r5774@not: autrijus | 2004-06-26T10:40:48.767820Z
+
+ * incremental merge from 3.1 to 3.3
+ ----------------------------------------------------------------------
+ r5778@not: autrijus | 2004-06-26T11:28:26.143291Z
+
+ * solve utf8 quoting problem on "Take".
+ ----------------------------------------------------------------------
+ r5781@not: autrijus | 2004-06-26T20:27:29.184033Z
+
+ * restore hu.po
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1121 | autrijus | 2004-06-20 13:59:07 -0400 (Sun, 20 Jun 2004) | 10 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/html/Elements/Header
+ M /rt/branches/rt-3.3/html/Elements/SetupSessionCookie
+ M /rt/branches/rt-3.3/html/NoAuth/webrt.css
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+
+ ----------------------------------------------------------------------
+ r5682@not: autrijus | 2004-06-20T17:57:48.360088Z
+
+ * Supports native MasonX::Apache2Handler.
+ * Changed header_out to use headers_out instead (which should be
+ used in 1.0 as well). Cf.:
+ http://perl.apache.planetmirror.com/docs/2.0/user/porting/compat.html
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1082 | alexmv | 2004-06-16 10:53:17 -0400 (Wed, 16 Jun 2004) | 11 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+
+RT-Ticket: 5732
+RT-Status: stalled
+RT-Update: correspond
+
+ * POD spacing mistake in Ticket_Overlay
+
+ * Ticket_Overlay now overrides RT::Record's CustomFieldValues method,
+ to intercept calls where the field is passed by name. I don't have
+ a way of testing this patch, so this bug is getting marked stalled
+ until this patch is verified.
+
+------------------------------------------------------------------------
+r1033 | leira | 2004-06-08 14:26:18 -0400 (Tue, 08 Jun 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+
+Pass along 'Type' argument in CreateTickets (replicates a patch that was applied to 3.1, but needed for a customer in this branch now).
+
+------------------------------------------------------------------------
+r1031 | jesse | 2004-06-08 01:05:46 -0400 (Tue, 08 Jun 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/bin/standalone_httpd.in
+
+ ----------------------------------------------------------------------
+ r1752@debian: jesse | 2004-06-08T13:50:52.709840Z
+
+ standalone httpd is now forking rather than singleprocess
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r1023 | alexmv | 2004-06-07 16:03:46 -0400 (Mon, 07 Jun 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/I18N/he.po
+
+RT-Ticket: 5674
+RT-Action: comment
+RT-Status: resolved
+
+ * Apply Ilan Rabinovitch's changes
+
+------------------------------------------------------------------------
+r1022 | alexmv | 2004-06-07 14:47:18 -0400 (Mon, 07 Jun 2004) | 7 lines
+Changed paths:
+ M /rt/branches/rt-3.3/etc/schema.mysql
+
+RT-Ticket: 5648
+RT-Action: comment
+RT-Status: resolved
+
+ * The last hunk of the ticket no longer applies, as those indexes
+ were reshuffled because they were too long.
+
+------------------------------------------------------------------------
+r986 | jesse | 2004-05-31 21:54:38 -0400 (Mon, 31 May 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/configure.ac
+ A /rt/branches/rt-3.3/etc/acl.Sybase
+ A /rt/branches/rt-3.3/etc/schema.Sybase
+ M /rt/branches/rt-3.3/html/Search/Build.html
+ M /rt/branches/rt-3.3/html/Search/Bulk.html
+ M /rt/branches/rt-3.3/lib/RT/EmailParser.pm
+ M /rt/branches/rt-3.3/lib/RT/Handle.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/rt-3.3/lib/RT.pm.in
+ M /rt/branches/rt-3.3/sbin/rt-setup-database.in
+ M /rt/branches/rt-3.3/sbin/rt-test-dependencies.in
+
+Merging forward from RT 3.1
+------------------------------------------------------------------------
+r981 | jesse | 2004-05-30 03:01:04 -0400 (Sun, 30 May 2004) | 7 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/etc/schema.mysql
+ M /rt/branches/rt-3.3/etc/upgrade/3.3.0/schema.mysql
+
+
+ ----------------------------------------------------------------------
+ r1721@debian: jesse | 2004-05-30T16:04:25.431679Z
+
+ Mysql on some platforms (built with unicode?) has a shorter max index length. adjusted indexes
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r966 | jesse | 2004-05-27 23:30:59 -0400 (Thu, 27 May 2004) | 35 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/configure.ac
+ M /rt/branches/rt-3.3/etc/acl.Pg
+ D /rt/branches/rt-3.3/etc/upgrade/2.1.71
+ M /rt/branches/rt-3.3/html/Admin/Elements/ToolTabs
+ D /rt/branches/rt-3.3/html/Developer/Perldoc.html
+ M /rt/branches/rt-3.3/html/Elements/EditLinks
+ M /rt/branches/rt-3.3/html/Elements/Footer
+ D /rt/branches/rt-3.3/html/Elements/ShadedBox
+ D /rt/branches/rt-3.3/html/Elements/ShadedInputRow
+ D /rt/branches/rt-3.3/html/Elements/ShadedRow
+ M /rt/branches/rt-3.3/html/Elements/ShowLinks
+ M /rt/branches/rt-3.3/html/Elements/TicketList
+ D /rt/branches/rt-3.3/html/Projects
+ D /rt/branches/rt-3.3/html/Scope
+ M /rt/branches/rt-3.3/html/Search/Build.html
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+ D /rt/branches/rt-3.3/html/Search/Elements/PickRestriction
+ D /rt/branches/rt-3.3/html/Search/Elements/TicketHeader
+ D /rt/branches/rt-3.3/html/Search/Elements/TicketHeaderCell
+ D /rt/branches/rt-3.3/html/Search/Listing.html
+ D /rt/branches/rt-3.3/html/Ticket/Elements/EditLinks
+ D /rt/branches/rt-3.3/html/Ticket/Elements/ShowLink
+ D /rt/branches/rt-3.3/html/Ticket/Elements/ShowLinks
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/Tabs
+ M /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+
+ ----------------------------------------------------------------------
+ r1710@tinbook: jesse | 2004-05-28T02:46:23.907073Z
+
+
+ ----------------------------------------------------------------------
+ r1712@tinbook: jesse | 2004-05-28T03:15:12.330227Z
+
+ ----------------------------------------------------------------------
+ r1669@tinbook: jesse | 2004-05-24T02:17:41.959105Z
+
+
+ ----------------------------------------------------------------------
+ r1672@tinbook: jesse | 2004-05-24T03:09:05.156817Z
+
+
+ ----------------------------------------------------------------------
+ r1673@tinbook: jesse | 2004-05-24T03:59:43.181766Z
+
+ Bumping to 3.1.16
+ ----------------------------------------------------------------------
+ r1706@tinbook: jesse | 2004-05-28T02:10:56.309297Z
+
+
+ ----------------------------------------------------------------------
+ r1711@tinbook: jesse | 2004-05-28T02:47:49.277131Z
+
+
+ ----------------------------------------------------------------------
+
+ ----------------------------------------------------------------------
+ r1713@tinbook: jesse | 2004-05-28T03:27:19.127190Z
+
+ Cleaning out things split out into other products
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r965 | jesse | 2004-05-27 22:44:48 -0400 (Thu, 27 May 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/bin/standalone_httpd.in
+ M /rt/branches/rt-3.3/html/Search/Edit.html
+ M /rt/branches/rt-3.3/html/Search/Results.html
+ M /rt/branches/rt-3.3/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/rt-3.3/lib/RT/Interface/Email/Auth/GnuPG.pm
+
+
+------------------------------------------------------------------------
+r954 | autrijus | 2004-05-26 06:12:38 -0400 (Wed, 26 May 2004) | 20 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.1/lib/RT/I18N/zh_cn.po
+ M /rt/branches/rt-3.1/lib/RT/I18N/zh_tw.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_cn.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_tw.po
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+
+ ----------------------------------------------------------------------
+ r4757@not: autrijus | 2004-05-11T06:13:28.139589Z
+
+ * Chinese translation for 3.2 final.
+ ----------------------------------------------------------------------
+ r5211@not: autrijus | 2004-05-26T10:07:40.959072Z
+
+ * FreezeThaw fails badly when encoding structures with mixed unicode
+ and byte string contents. Switch to Storable+Base64 which deals
+ with this gracefully.
+
+ ----------------------------------------------------------------------
+ r5212@not: autrijus | 2004-05-26T10:09:36.398305Z
+
+ * EscapeUTF8 should preserve the byte/unicode flag of the original
+ string. Otherwise, silent promotion on AutoFlush=>0 can corrupt
+ pages with both byte and unicode string contents.
+ (Reported by: LCamel and jihuang from foundry)
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r941 | jesse | 2004-05-23 22:29:51 -0400 (Sun, 23 May 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/html/Search/Build.html
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+ M /rt/branches/rt-3.3/html/Ticket/Create.html
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Groups_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Queue_Overlay.pm
+ A /rt/branches/rt-3.3/lib/t/data/rt-send-cc
+
+Merge forward from 3.1
+
+------------------------------------------------------------------------
+r934 | leira | 2004-05-19 12:23:49 -0400 (Wed, 19 May 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+
+Move file from 3.1 so that CF searching will work.
+
+------------------------------------------------------------------------
+r933 | leira | 2004-05-19 02:34:22 -0400 (Wed, 19 May 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Link_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+
+Actually pass along link error messages. Resolves bps #2210.
+
+------------------------------------------------------------------------
+r930 | leira | 2004-05-19 00:48:20 -0400 (Wed, 19 May 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Ticket/Create.html
+
+Remove Debug field.
+
+------------------------------------------------------------------------
+r920 | jesse | 2004-05-17 22:18:53 -0400 (Mon, 17 May 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3
+
+
+------------------------------------------------------------------------
+r918 | jesse | 2004-05-17 22:02:24 -0400 (Mon, 17 May 2004) | 10 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/html/Admin/Groups/index.html
+ M /rt/branches/rt-3.3/html/Elements/MyTickets
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+ M /rt/branches/rt-3.3/html/Tools/Offline.html
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/rt-3.3/lib/t/04_send_email.pl.in
+
+ ----------------------------------------------------------------------
+ r1191@Jesse-Vincents-Computer: jesse | 2004-05-18T00:35:23.362237Z
+
+
+ ----------------------------------------------------------------------
+ r1192@Jesse-Vincents-Computer: jesse | 2004-05-18T02:01:52.198022Z
+
+ Merged forward from rt 3.1
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r901 | leira | 2004-05-14 17:35:35 -0400 (Fri, 14 May 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Elements/MyTickets
+
+MyTickets doesn't require a Priority to be set.
+
+------------------------------------------------------------------------
+r890 | leira | 2004-05-14 00:28:27 -0400 (Fri, 14 May 2004) | 4 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Admin/Groups/index.html
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+
+If there isn't a QueueObj to begin with, it can't have a Name.
+
+Replaced a newline that had gone astray.
+
+------------------------------------------------------------------------
+r864 | jesse | 2004-05-11 01:25:36 -0400 (Tue, 11 May 2004) | 8 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/html/Admin/Elements/CustomFieldTabs
+ M /rt/branches/rt-3.3/html/SelfService/Closed.html
+ M /rt/branches/rt-3.3/html/autohandler
+ M /rt/branches/rt-3.3/lib/RT/I18N/cs.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/da.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/de.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/en.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/en_malkovich.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/es.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fi.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fr.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/he.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/hu.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/it.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ja.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/nl.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/no.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/pt_br.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ru.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_cn.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_tw.po
+ M /rt/branches/rt-3.3/sbin/extract-message-catalog
+
+
+
+ ----------------------------------------------------------------------
+ r1528@tinbook: jesse | 2004-05-11T04:57:31.357768Z
+
+ Updated translations, linted them, updated translation extraction software
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r859 | jesse | 2004-05-09 21:29:48 -0400 (Sun, 09 May 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/releng.cnf
+
+ ----------------------------------------------------------------------
+ r1517@tinbook: jesse | 2004-05-10T01:29:37.932237Z
+
+ Bumping to 3.3.3
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r858 | jesse | 2004-05-09 21:27:33 -0400 (Sun, 09 May 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/Makefile.in
+ M /rt/branches/rt-3.3/etc/upgrade/3.3.0/schema.Pg
+ M /rt/branches/rt-3.3/etc/upgrade/3.3.0/schema.mysql
+
+ ----------------------------------------------------------------------
+ r1515@tinbook: jesse | 2004-05-10T01:26:38.232023Z
+
+ Improved 3.1->3.3 migration support
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r851 | jesse | 2004-05-06 21:03:05 -0400 (Thu, 06 May 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/html/Admin/Queues/Scrip.html
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT.pm.in
+
+Merging forward from rt-3.1
+------------------------------------------------------------------------
+r848 | jesse | 2004-05-06 20:33:00 -0400 (Thu, 06 May 2004) | 5 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/HOWTO/README
+ M /rt/branches/rt-3.3/HOWTO/change.txt
+ M /rt/branches/rt-3.3/HOWTO/release.txt
+ M /rt/branches/rt-3.3/HOWTO/version-control.txt
+ M /rt/branches/rt-3.3/Makefile.in
+ M /rt/branches/rt-3.3/UPGRADING
+ M /rt/branches/rt-3.3/aclocal.m4
+ M /rt/branches/rt-3.3/bin/mason_handler.fcgi.in
+ M /rt/branches/rt-3.3/bin/mason_handler.scgi.in
+ M /rt/branches/rt-3.3/bin/mason_handler.svc.in
+ M /rt/branches/rt-3.3/bin/rt-commit-handler.in
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/configure.ac
+ M /rt/branches/rt-3.3/docs/Security
+ M /rt/branches/rt-3.3/docs/design_docs/acls
+ M /rt/branches/rt-3.3/docs/design_docs/approval_notices
+ M /rt/branches/rt-3.3/docs/design_docs/approval_template
+ M /rt/branches/rt-3.3/docs/design_docs/cf_search
+ M /rt/branches/rt-3.3/docs/design_docs/cli_spec
+ M /rt/branches/rt-3.3/docs/design_docs/cvs_integration
+ M /rt/branches/rt-3.3/docs/design_docs/delegation
+ M /rt/branches/rt-3.3/docs/design_docs/evil_plans
+ M /rt/branches/rt-3.3/docs/design_docs/groups_notes
+ M /rt/branches/rt-3.3/docs/design_docs/link-definitions.txt
+ M /rt/branches/rt-3.3/docs/design_docs/recursive_group_membership_algorithm
+ M /rt/branches/rt-3.3/docs/design_docs/rql_parser_machine.graphviz
+ M /rt/branches/rt-3.3/docs/design_docs/string-extraction-guide.txt
+ M /rt/branches/rt-3.3/docs/design_docs/ticket_templates
+ M /rt/branches/rt-3.3/docs/design_docs/users
+ M /rt/branches/rt-3.3/docs/rt3-schema-relationships.dot
+ M /rt/branches/rt-3.3/etc/RT_Config.pm.in
+ M /rt/branches/rt-3.3/etc/RT_SiteConfig.pm
+ M /rt/branches/rt-3.3/etc/acl.Informix
+ M /rt/branches/rt-3.3/etc/acl.Oracle
+ M /rt/branches/rt-3.3/etc/constraints.mysql
+ M /rt/branches/rt-3.3/etc/drop.Informix
+ M /rt/branches/rt-3.3/etc/drop.Oracle
+ M /rt/branches/rt-3.3/etc/initialdata
+ M /rt/branches/rt-3.3/etc/rt.spec
+ M /rt/branches/rt-3.3/etc/schema.SQLite
+ M /rt/branches/rt-3.3/html/Admin/Elements/AddCustomFieldValue
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditCustomField
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditCustomFieldValues
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditCustomFields
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditQueueWatchers
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditScrips
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditTemplates
+ M /rt/branches/rt-3.3/html/Admin/Elements/ListGlobalCustomFields
+ M /rt/branches/rt-3.3/html/Admin/Elements/QueueRightsForUser
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectCustomFieldType
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectGroups
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectModifyGroup
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectNewGroupMembers
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectRights
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectScripAction
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectScripCondition
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectSingleOrMultiple
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectUsers
+ M /rt/branches/rt-3.3/html/Admin/Global/Scrip.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/GroupRights.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/Members.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/Modify.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/UserRights.html
+ M /rt/branches/rt-3.3/html/Admin/Groups/index.html
+ M /rt/branches/rt-3.3/html/Admin/Queues/CustomField.html
+ M /rt/branches/rt-3.3/html/Admin/Queues/CustomFields.html
+ M /rt/branches/rt-3.3/html/Admin/Queues/Scrip.html
+ M /rt/branches/rt-3.3/html/Approvals/Display.html
+ M /rt/branches/rt-3.3/html/Approvals/Elements/Approve
+ M /rt/branches/rt-3.3/html/Approvals/Elements/PendingMyApproval
+ M /rt/branches/rt-3.3/html/Approvals/Elements/ShowDependency
+ M /rt/branches/rt-3.3/html/Approvals/Elements/Tabs
+ M /rt/branches/rt-3.3/html/Approvals/index.html
+ M /rt/branches/rt-3.3/html/Elements/BevelBoxRaisedEnd
+ M /rt/branches/rt-3.3/html/Elements/BevelBoxRaisedStart
+ M /rt/branches/rt-3.3/html/Elements/CreateTicket
+ M /rt/branches/rt-3.3/html/Elements/GotoTicket
+ M /rt/branches/rt-3.3/html/Elements/Menu
+ M /rt/branches/rt-3.3/html/Elements/MyRequests
+ M /rt/branches/rt-3.3/html/Elements/MyTickets
+ M /rt/branches/rt-3.3/html/Elements/PageLayout
+ M /rt/branches/rt-3.3/html/Elements/Quicksearch
+ M /rt/branches/rt-3.3/html/Elements/Refresh
+ M /rt/branches/rt-3.3/html/Elements/SelectAttachmentField
+ M /rt/branches/rt-3.3/html/Elements/SelectCustomFieldOperator
+ M /rt/branches/rt-3.3/html/Elements/SelectCustomFieldValue
+ M /rt/branches/rt-3.3/html/Elements/SelectGroups
+ M /rt/branches/rt-3.3/html/Elements/SelectLinkType
+ M /rt/branches/rt-3.3/html/Elements/SelectMatch
+ M /rt/branches/rt-3.3/html/Elements/SelectNewTicketQueue
+ M /rt/branches/rt-3.3/html/Elements/SelectQueue
+ M /rt/branches/rt-3.3/html/Elements/SelectResultsPerPage
+ M /rt/branches/rt-3.3/html/Elements/SelectSortOrder
+ M /rt/branches/rt-3.3/html/Elements/SelectTicketSortBy
+ M /rt/branches/rt-3.3/html/Elements/SelectTicketTypes
+ M /rt/branches/rt-3.3/html/Elements/ViewUser
+ M /rt/branches/rt-3.3/html/NoAuth/images/back_home.gif
+ M /rt/branches/rt-3.3/html/NoAuth/images/bplogo.gif
+ M /rt/branches/rt-3.3/html/NoAuth/images/favicon.png
+ M /rt/branches/rt-3.3/html/NoAuth/images/head_requestracker.gif
+ M /rt/branches/rt-3.3/html/NoAuth/images/rt.jpg
+ M /rt/branches/rt-3.3/html/NoAuth/images/space.gif
+ M /rt/branches/rt-3.3/html/NoAuth/images/spacer.gif
+ M /rt/branches/rt-3.3/html/NoAuth/images/squares_blue.gif
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/queue/default
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/queue/ns
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/links
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/user/default
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/user/ns
+ M /rt/branches/rt-3.3/html/REST/1.0/NoAuth/mail-gateway
+ M /rt/branches/rt-3.3/html/REST/1.0/logout
+ M /rt/branches/rt-3.3/html/REST/1.0/search/dhandler
+ M /rt/branches/rt-3.3/html/REST/1.0/ticket/comment
+ M /rt/branches/rt-3.3/html/REST/1.0/ticket/link
+ M /rt/branches/rt-3.3/html/REST/1.0/ticket/merge
+ M /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+ M /rt/branches/rt-3.3/html/Search/Elements/TicketRow
+ M /rt/branches/rt-3.3/html/SelfService/Attachment/dhandler
+ M /rt/branches/rt-3.3/html/SelfService/Closed.html
+ M /rt/branches/rt-3.3/html/SelfService/Elements/Tabs
+ M /rt/branches/rt-3.3/html/SelfService/index.html
+ M /rt/branches/rt-3.3/html/Ticket/Create.html
+ M /rt/branches/rt-3.3/html/Ticket/Display.html
+ M /rt/branches/rt-3.3/html/Ticket/Elements/BulkLinks
+ M /rt/branches/rt-3.3/html/Ticket/Elements/EditCustomField
+ M /rt/branches/rt-3.3/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowCustomFields
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowHistory
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowMessageHeaders
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/rt-3.3/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/rt-3.3/html/Tools/Offline.html
+ M /rt/branches/rt-3.3/html/User/Delegation.html
+ M /rt/branches/rt-3.3/html/User/Elements/DelegateRights
+ M /rt/branches/rt-3.3/html/User/Elements/GroupTabs
+ M /rt/branches/rt-3.3/html/User/Elements/Tabs
+ M /rt/branches/rt-3.3/html/User/Groups/Members.html
+ M /rt/branches/rt-3.3/html/User/Groups/Modify.html
+ M /rt/branches/rt-3.3/html/User/Groups/index.html
+ M /rt/branches/rt-3.3/html/index.html
+ M /rt/branches/rt-3.3/html/l
+ M /rt/branches/rt-3.3/install-sh
+ M /rt/branches/rt-3.3/lib/RT/ACE_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/ACL_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/EscalatePriority.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/ResolveMembers.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/SendEmail.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/SetPriority.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/UserDefined.pm
+ M /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/CachedGroupMember.pm
+ M /rt/branches/rt-3.3/lib/RT/CachedGroupMember_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/CachedGroupMembers.pm
+ M /rt/branches/rt-3.3/lib/RT/CachedGroupMembers_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/AnyTransaction.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/BeforeDue.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/Overdue.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/OwnerChange.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/PriorityExceeds.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/QueueChange.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/StatusChange.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/UserDefined.pm
+ M /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomField.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomFieldValue.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomFieldValues.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomFieldValues_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomFields.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Date.pm
+ M /rt/branches/rt-3.3/lib/RT/EmailParser.pm
+ M /rt/branches/rt-3.3/lib/RT/GroupMember_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/GroupMembers_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Group_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Groups_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Handle.pm
+ M /rt/branches/rt-3.3/lib/RT/I18N/cs.pm
+ M /rt/branches/rt-3.3/lib/RT/I18N/cs.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/de.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/en.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/es.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fr.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/he.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/i_default.pm
+ M /rt/branches/rt-3.3/lib/RT/I18N/it.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ja.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/nl.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/no.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/pt_br.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/ru.po
+ M /rt/branches/rt-3.3/lib/RT/Interface/CLI.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Email.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/REST.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/Link.pm
+ M /rt/branches/rt-3.3/lib/RT/Link_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Links.pm
+ M /rt/branches/rt-3.3/lib/RT/Links_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Principal.pm
+ M /rt/branches/rt-3.3/lib/RT/Principal_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Principals.pm
+ M /rt/branches/rt-3.3/lib/RT/Principals_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Queue_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Queues_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/ScripAction_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/ScripActions_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/ScripCondition_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/ScripConditions_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Scrip_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Search/ActiveTicketsInQueue.pm
+ M /rt/branches/rt-3.3/lib/RT/Search/Generic.pm
+ M /rt/branches/rt-3.3/lib/RT/System.pm
+ M /rt/branches/rt-3.3/lib/RT/Template_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Templates_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValue.pm
+ M /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValue_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValues.pm
+ M /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValues_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/rt-3.3/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/URI/base.pm
+ M /rt/branches/rt-3.3/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/rt-3.3/lib/RT/User_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT.pm.in
+ M /rt/branches/rt-3.3/lib/t/00smoke.t.in
+ M /rt/branches/rt-3.3/lib/t/01harness.t.in
+ M /rt/branches/rt-3.3/lib/t/02regression.t.in
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/dir
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg1
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg2
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg3
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg4
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg5
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg6
+ M /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg7
+ M /rt/branches/rt-3.3/lib/t/data/multipart-alternative-with-umlaut
+ M /rt/branches/rt-3.3/lib/t/data/multipart-report
+ M /rt/branches/rt-3.3/lib/t/data/nested-mime-sample
+ M /rt/branches/rt-3.3/lib/t/data/nested-rfc-822
+ M /rt/branches/rt-3.3/lib/t/data/new-ticket-from-iso-8859-1
+ M /rt/branches/rt-3.3/lib/t/data/new-ticket-from-iso-8859-1-full
+ M /rt/branches/rt-3.3/lib/t/data/russian-subject-no-content-type
+ M /rt/branches/rt-3.3/lib/t/data/text-html-in-russian
+ M /rt/branches/rt-3.3/lib/t/data/text-html-with-umlaut
+ M /rt/branches/rt-3.3/lib/t/regression/00placeholder
+ M /rt/branches/rt-3.3/lib/t/regression/mime_tests
+ M /rt/branches/rt-3.3/m4/rt_enable_layout.m4
+ M /rt/branches/rt-3.3/m4/rt_expand_var.m4
+ M /rt/branches/rt-3.3/m4/rt_layout.m4
+ M /rt/branches/rt-3.3/m4/rt_subst_expanded_arg.m4
+ M /rt/branches/rt-3.3/sbin/extract-message-catalog
+ M /rt/branches/rt-3.3/sbin/extract_pod_tests
+ M /rt/branches/rt-3.3/sbin/factory
+ M /rt/branches/rt-3.3/sbin/regression_harness
+ M /rt/branches/rt-3.3/sbin/rt-setup-database.in
+ M /rt/branches/rt-3.3/sbin/rt-test-dependencies.in
+
+ r1484@tinbook: jesse | 2004-05-07T00:31:24.624807Z
+
+ Merging forward from 3.1
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r843 | autrijus | 2004-05-06 04:13:18 -0400 (Thu, 06 May 2004) | 10 lines
+Changed paths:
+ M /rt
+ D /rt/branches/rt-3.3/docs/design_docs/atom-api.txt
+ M /rt/branches/rt-3.3/lib/RT/CustomField_Overlay.pm
+
+ ----------------------------------------------------------------------
+ r4415@not: autrijus | 2004-05-03T06:42:06.584504Z
+
+ * move atom-api.txt to RTx::Atom space.
+ ----------------------------------------------------------------------
+ r4510@not: autrijus | 2004-05-06T07:44:35.710915Z
+
+ * stub undef CustomField->QueueObj to make BuildFormatString happy.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r833 | jesse | 2004-05-05 14:18:03 -0400 (Wed, 05 May 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+
+A new merge ticket
+
+------------------------------------------------------------------------
+r810 | autrijus | 2004-05-02 21:00:48 -0400 (Sun, 02 May 2004) | 8 lines
+Changed paths:
+ M /rt
+ D /rt/branches/rt-3.3/html/REST/2.0
+
+ ----------------------------------------------------------------------
+ r4394@not: autrijus | 2004-05-03T01:00:51.890023Z
+
+ * 301 Moved Permanently
+ * Location: /RTx-Atom/html/Atom/0.3
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r808 | autrijus | 2004-05-02 20:21:29 -0400 (Sun, 02 May 2004) | 15 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/docs/design_docs/atom-api.txt
+ A /rt/branches/rt-3.3/html/REST/2.0/Add
+ A /rt/branches/rt-3.3/html/REST/2.0/Add/index
+ D /rt/branches/rt-3.3/html/REST/2.0/Create
+ D /rt/branches/rt-3.3/html/REST/2.0/Delete
+ A /rt/branches/rt-3.3/html/REST/2.0/Remove
+ A /rt/branches/rt-3.3/html/REST/2.0/Remove/index
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+ M /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+ M /rt/branches/rt-3.3/lib/RT/Date.pm
+
+ ----------------------------------------------------------------------
+ r4385@not: autrijus | 2004-05-02T21:16:04.349680Z
+
+ * Correct POD for W3CDTF
+ ----------------------------------------------------------------------
+ r4388@not: autrijus | 2004-05-03T00:17:22.934822Z
+
+ * massive redesign based on discussion with obra.
+ ----------------------------------------------------------------------
+ r4389@not: autrijus | 2004-05-03T00:21:25.960300Z
+
+ * Rename "Create" to "Add"; "Delete" to "Remove".
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r805 | autrijus | 2004-05-02 14:03:32 -0400 (Sun, 02 May 2004) | 10 lines
+Changed paths:
+ M /rt
+ A /rt/branches/rt-3.3/docs/design_docs/atom-api.txt
+ A /rt/branches/rt-3.3/html/REST/2.0/Create/index
+ A /rt/branches/rt-3.3/html/REST/2.0/Delete/index
+ A /rt/branches/rt-3.3/html/REST/2.0/Describe
+ A /rt/branches/rt-3.3/html/REST/2.0/Describe/index
+ M /rt/branches/rt-3.3/html/REST/2.0/Elements/Link
+ A /rt/branches/rt-3.3/html/REST/2.0/Get/index
+ M /rt/branches/rt-3.3/html/REST/2.0/NoAuth/feed.css
+ A /rt/branches/rt-3.3/html/REST/2.0/Put
+ A /rt/branches/rt-3.3/html/REST/2.0/Put/index
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+ A /rt/branches/rt-3.3/html/REST/2.0/Update/index
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+ M /rt/branches/rt-3.3/html/REST/2.0/index
+
+ ----------------------------------------------------------------------
+ r4382@not: autrijus | 2004-05-02T18:02:39.870947Z
+
+ * Describes RT/REST 2.0, Atom-compatible API.
+ ----------------------------------------------------------------------
+ r4383@not: autrijus | 2004-05-02T18:03:08.473736Z
+
+ * Put stubs to all the unimplemented verbs.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r804 | autrijus | 2004-05-02 11:58:17 -0400 (Sun, 02 May 2004) | 14 lines
+Changed paths:
+ M /rt
+ A /rt/branches/rt-3.3/html/REST/2.0/Elements/Error
+ A /rt/branches/rt-3.3/html/REST/2.0/Elements/Introspect
+ D /rt/branches/rt-3.3/html/REST/2.0/Error
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+ M /rt/branches/rt-3.3/html/REST/2.0/index
+
+ ----------------------------------------------------------------------
+ r4379@not: autrijus | 2004-05-02T15:26:00.873038Z
+
+ * Do content negotiation with the browser and fallback on text/xml.
+ * Clean up namespaces a little.
+ ----------------------------------------------------------------------
+ r4380@not: autrijus | 2004-05-02T15:58:03.262688Z
+
+ * Move Error/index to Elements/Error since Error is not a verb.
+ * Refactor the Introspect part away to Elements/.
+ * Correctly redirects /Templates/ and /template/ to /templates/.
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r802 | autrijus | 2004-05-02 09:14:02 -0400 (Sun, 02 May 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Elements/Link
+ M /rt/branches/rt-3.3/html/REST/2.0/NoAuth/feed.css
+ M /rt/branches/rt-3.3/html/REST/2.0/NoAuth/index.css
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+
+ ----------------------------------------------------------------------
+ r4375@not: autrijus | 2004-05-02T13:13:38.884936Z
+
+ * use ->URI correctly.
+ * use <a xmlns="...html..."> so things can correct render in nongecko browsers.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r801 | autrijus | 2004-05-01 19:14:04 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/index
+
+ ----------------------------------------------------------------------
+ r4365@not: autrijus | 2004-05-01T23:14:05.342098Z
+
+ * Cross-introspection logic to eliminate subordinate classes from main index.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r800 | autrijus | 2004-05-01 17:39:50 -0400 (Sat, 01 May 2004) | 8 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Elements/Link
+ M /rt/branches/rt-3.3/html/REST/2.0/NoAuth/feed.css
+ A /rt/branches/rt-3.3/html/REST/2.0/NoAuth/index.css
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+ M /rt/branches/rt-3.3/html/REST/2.0/autohandler
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+ M /rt/branches/rt-3.3/html/REST/2.0/index
+
+ ----------------------------------------------------------------------
+ r4363@not: autrijus | 2004-05-01T21:39:51.608519Z
+
+ * Index page implemented using multiple "Feed" services.
+ * Lots of cute, little icons placed with CSS.
+ * Much better preparation for autodiscovery; PostURI is now handled with "/type.new".
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r799 | autrijus | 2004-05-01 14:54:56 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/NoAuth/feed.css
+
+ ----------------------------------------------------------------------
+ r4361@not: autrijus | 2004-05-01T18:55:05.234036Z
+
+ * Even more CSS tricks.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r798 | autrijus | 2004-05-01 14:49:18 -0400 (Sat, 01 May 2004) | 14 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/Admin/Elements/Header
+ M /rt/branches/rt-3.3/html/Admin/Global/Templates.html
+ M /rt/branches/rt-3.3/html/Elements/Header
+ M /rt/branches/rt-3.3/html/Elements/TicketList
+ A /rt/branches/rt-3.3/html/REST/2.0/Elements
+ A /rt/branches/rt-3.3/html/REST/2.0/Elements/Link
+ M /rt/branches/rt-3.3/html/REST/2.0/NoAuth/feed.css
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+ M /rt/branches/rt-3.3/html/REST/2.0/autohandler
+
+ ----------------------------------------------------------------------
+ r4357@not: autrijus | 2004-05-01T18:47:10.055533Z
+
+ * Various CSS tricks.
+ ----------------------------------------------------------------------
+ r4358@not: autrijus | 2004-05-01T18:47:49.553353Z
+
+ * Fixed an off-by-one error in ticket listing.
+ ----------------------------------------------------------------------
+ r4359@not: autrijus | 2004-05-01T18:48:06.789268Z
+
+ * support for Atom autodiscovery.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r797 | autrijus | 2004-05-01 12:11:01 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Error/index
+
+ ----------------------------------------------------------------------
+ r4353@not: autrijus | 2004-05-01T16:11:10.795277Z
+
+ * finally Error works for all three supported carriers.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r796 | autrijus | 2004-05-01 12:07:33 -0400 (Sat, 01 May 2004) | 17 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/bin/standalone_httpd.in
+ M /rt/branches/rt-3.3/html/REST/2.0/Error/index
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+
+ ----------------------------------------------------------------------
+ r4349@not: autrijus | 2004-05-01T15:36:00.221832Z
+
+ * Properly clean up leftover HTTP_* environment variables.
+ * Pass authorization header to Mason.
+ * Send out correct HTTP status code instead of blindly assuming 200.
+ ----------------------------------------------------------------------
+ r4350@not: autrijus | 2004-05-01T15:59:39.046584Z
+
+ * RT::Date->new also takes currentuser.
+ ----------------------------------------------------------------------
+ r4351@not: autrijus | 2004-05-01T16:00:38.530367Z
+
+ * Correctly handle nonce disposal.
+ * $m->abort is needed for mod_perl2.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r795 | autrijus | 2004-05-01 09:58:07 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+
+ ----------------------------------------------------------------------
+ r4347@not: autrijus | 2004-05-01T13:58:14.427055Z
+
+ * it's Queues, not Queue
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r794 | autrijus | 2004-05-01 09:55:57 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+
+ ----------------------------------------------------------------------
+ r4345@not: autrijus | 2004-05-01T13:56:03.778530Z
+
+ * use absolute URIs, not relative ones, per the Atom spec
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r793 | autrijus | 2004-05-01 09:51:25 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+
+ ----------------------------------------------------------------------
+ r4343@not: autrijus | 2004-05-01T13:51:31.406464Z
+
+ * link semantics now agrees with AtomAPI.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r792 | autrijus | 2004-05-01 09:35:28 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Search/index
+
+ ----------------------------------------------------------------------
+ r4341@not: autrijus | 2004-05-01T13:35:35.544895Z
+
+ * Make <id> slightly more unique
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r791 | autrijus | 2004-05-01 09:33:03 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/Error/index
+ M /rt/branches/rt-3.3/html/REST/2.0/autohandler
+
+ ----------------------------------------------------------------------
+ r4339@not: autrijus | 2004-05-01T13:32:59.502832Z
+
+ * adapt for FastCGI's needs
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r790 | autrijus | 2004-05-01 09:22:21 -0400 (Sat, 01 May 2004) | 9 lines
+Changed paths:
+ M /rt
+ A /rt/branches/rt-3.3/html/REST/2.0/Auth
+ A /rt/branches/rt-3.3/html/REST/2.0/Create
+ A /rt/branches/rt-3.3/html/REST/2.0/Delete
+ A /rt/branches/rt-3.3/html/REST/2.0/Error
+ A /rt/branches/rt-3.3/html/REST/2.0/Error/index
+ A /rt/branches/rt-3.3/html/REST/2.0/Get
+ A /rt/branches/rt-3.3/html/REST/2.0/Search
+ A /rt/branches/rt-3.3/html/REST/2.0/Search/index
+ A /rt/branches/rt-3.3/html/REST/2.0/Update
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+ A /rt/branches/rt-3.3/html/REST/2.0/index
+
+ ----------------------------------------------------------------------
+ r4335@not: autrijus | 2004-05-01T13:20:56.570615Z
+
+ * Establish directory layout.
+ * Error handling.
+ * Refactor out the 'Search' verb.
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r788 | autrijus | 2004-05-01 07:44:58 -0400 (Sat, 01 May 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+
+ ----------------------------------------------------------------------
+ r4331@not: autrijus | 2004-05-01T11:44:31.355329Z
+
+ * do not offer WSSE auth to non-atom clients, and vice versa,
+ to avoid spurious auth warnings.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r785 | autrijus | 2004-05-01 07:16:25 -0400 (Sat, 01 May 2004) | 10 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+ M /rt/branches/rt-3.3/lib/RT/Date.pm
+
+ ----------------------------------------------------------------------
+ r4323@not: autrijus | 2004-05-01T11:14:28.377660Z
+
+ * implement ->W3CDTF.
+ ----------------------------------------------------------------------
+ r4324@not: autrijus | 2004-05-01T11:16:31.456041Z
+
+ * we are now a valid Atom feed.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r784 | autrijus | 2004-05-01 06:52:15 -0400 (Sat, 01 May 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+
+ ----------------------------------------------------------------------
+ r4321@not: autrijus | 2004-05-01T10:52:23.225100Z
+
+ * add fastcgi support.
+ * make proper use of X-WSSE header instead of demanding Authorization
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r783 | autrijus | 2004-05-01 06:50:30 -0400 (Sat, 01 May 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/I18N.pm
+
+ ----------------------------------------------------------------------
+ r4319@not: autrijus | 2004-05-01T10:50:15.580296Z
+
+ * glob local/*.po too for consistency and intuitiveness.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r782 | autrijus | 2004-05-01 06:35:23 -0400 (Sat, 01 May 2004) | 8 lines
+Changed paths:
+ M /rt
+ A /rt/branches/rt-3.3/html/REST/2.0/NoAuth
+ A /rt/branches/rt-3.3/html/REST/2.0/NoAuth/feed.css
+ M /rt/branches/rt-3.3/html/REST/2.0/autohandler
+ M /rt/branches/rt-3.3/html/REST/2.0/dhandler
+
+ ----------------------------------------------------------------------
+ r4317@not: autrijus | 2004-05-01T10:35:00.584748Z
+
+ * now does Basic and Digest authentication, too.
+ * CSSify the example feed
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r781 | autrijus | 2004-04-30 23:24:12 -0400 (Fri, 30 Apr 2004) | 7 lines
+Changed paths:
+ M /rt
+ A /rt/branches/rt-3.3/html/REST/2.0
+ A /rt/branches/rt-3.3/html/REST/2.0/autohandler
+ A /rt/branches/rt-3.3/html/REST/2.0/dhandler
+ M /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+
+ ----------------------------------------------------------------------
+ r4309@not: autrijus | 2004-05-01T03:22:53.730384Z
+
+ * initial commit of RT/REST 2.0 API and WSSE authentication.
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r766 | jesse | 2004-04-28 14:26:02 -0400 (Wed, 28 Apr 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3/etc/upgrade
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0/acl.Informix
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0/acl.Oracle
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0/acl.Pg
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0/acl.SQLite
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0/acl.mysql
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0/schema.Pg
+ A /rt/branches/rt-3.3/etc/upgrade/3.3.0/schema.mysql
+
+ ----------------------------------------------------------------------
+ r2206@tinbook: jesse | 2004-04-28T18:24:56.988760Z
+
+ Added the beginnings of upgrade 3.1->3.3 functionality. has not yet been tested
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r763 | autrijus | 2004-04-28 02:05:14 -0400 (Wed, 28 Apr 2004) | 8 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/Base.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+
+ ----------------------------------------------------------------------
+ r4223@not: autrijus | 2004-04-28T06:00:27.794797Z
+
+ * correctly put loc_fuzzy in Base.
+ * do not double-encode EscapeUTF8 for Mason. this resolves the weird
+ standalong-httpd bug.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r761 | jesse | 2004-04-27 23:16:35 -0400 (Tue, 27 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/etc/schema.Pg
+
+Updating schema for 3.3 to use 'integer' on postgres rather than int2 on Andrew Sullivan's recommendation
+
+------------------------------------------------------------------------
+r758 | autrijus | 2004-04-27 16:12:38 -0400 (Tue, 27 Apr 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/Elements/Footer
+
+ ----------------------------------------------------------------------
+ r4214@not: autrijus | 2004-04-27T20:12:39.658238Z
+
+ * rework Footer l10n into one line so not to confuse the old string extractor
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r757 | autrijus | 2004-04-27 16:09:45 -0400 (Tue, 27 Apr 2004) | 6 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_cn.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_tw.po
+
+ ----------------------------------------------------------------------
+ r4211@not: autrijus | 2004-04-27T19:55:32.087502Z
+
+ * Chinese translations.
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r756 | autrijus | 2004-04-27 04:46:57 -0400 (Tue, 27 Apr 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/README
+
+ ----------------------------------------------------------------------
+ r4195@not: autrijus | 2004-04-27T08:46:36.958817Z
+
+ * now mod_perl2 only needs one line of extra setup.
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r746 | autrijus | 2004-04-26 15:11:33 -0400 (Mon, 26 Apr 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.1/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/User_Overlay.pm
+
+ ----------------------------------------------------------------------
+ r4176@not: autrijus | 2004-04-26T19:09:53.594445Z
+
+ * switch password format from base64 to hex; maintaining legacy
+ compatibility by upgrading passwords in-place upon successful auth
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r745 | autrijus | 2004-04-26 15:02:48 -0400 (Mon, 26 Apr 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/SearchBuilder.pm
+
+ ----------------------------------------------------------------------
+ r4171@not: autrijus | 2004-04-26T18:59:54.608186Z
+
+ * correct EMPTY and NULL support for $Tickets->LimitAttribute().
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r744 | autrijus | 2004-04-26 15:01:49 -0400 (Mon, 26 Apr 2004) | 8 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Template_Overlay.pm
+
+ ----------------------------------------------------------------------
+ r4170@not: autrijus | 2004-04-26T18:56:10.826541Z
+
+ * Do not crash the application when Template parsing failed; instead
+ capture the error and handle it with $RT::Logger->error().
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r743 | autrijus | 2004-04-26 15:01:05 -0400 (Mon, 26 Apr 2004) | 8 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+
+ ----------------------------------------------------------------------
+ r4169@not: autrijus | 2004-04-26T18:52:39.442581Z
+
+ * allow subkeys limit like $Tickets->Limit( FIELD => 'Requestor.Id' );
+ previously it was only available from "CF.*" keys.
+
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r741 | autrijus | 2004-04-26 14:48:52 -0400 (Mon, 26 Apr 2004) | 3 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+
+* freeze() forgot to take account of the look_at_* indices, resulting
+ in incorrect object after thaw()ing
+
+------------------------------------------------------------------------
+r733 | autrijus | 2004-04-23 15:09:40 -0400 (Fri, 23 Apr 2004) | 5 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/Groups_Overlay.pm
+
+ r4083@not: autrijus | 2004-04-23T19:08:59.061919Z
+
+ * ENTRY_AGGREGATOR is a misspelling (for ENTRYAGGREGATOR), and it
+ defaults to OR anyway, so simply eliminate this bogus parameter.
+
+------------------------------------------------------------------------
+r722 | autrijus | 2004-04-21 18:32:19 -0400 (Wed, 21 Apr 2004) | 10 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.1/lib/RT/SearchBuilder.pm
+ M /rt/branches/rt-3.3/lib/RT/SearchBuilder.pm
+
+ r4037@not: autrijus | 2004-04-21T22:30:37.269510Z
+
+ * It turns out that for EMPTY => 1 to work, IS NULL needs to be OR'ed
+ to the other columns as well...
+
+ ----------------------------------------------------------------------
+ r4038@not: autrijus | 2004-04-21T22:31:56.271421Z
+
+ * merge previous commit from 3.3 to 3.1.
+
+------------------------------------------------------------------------
+r719 | autrijus | 2004-04-21 14:59:55 -0400 (Wed, 21 Apr 2004) | 4 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/SearchBuilder.pm
+
+ r4027@not: autrijus | 2004-04-21T18:59:19.548165Z
+
+ * LimitAttribute( EMPTY => 1 ) is much more correctly handled with a IS NULL.
+
+------------------------------------------------------------------------
+r717 | jesse | 2004-04-21 01:25:01 -0400 (Wed, 21 Apr 2004) | 6 lines
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/Makefile.in
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/configure.ac
+ M /rt/branches/rt-3.3/etc/RT_Config.pm.in
+ M /rt/branches/rt-3.3/etc/schema.mysql
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/acl.Informix
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/acl.Oracle
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/acl.Pg
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/acl.SQLite
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/acl.mysql
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/content
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/schema.Informix
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/schema.Oracle
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/schema.Pg
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/schema.SQLite
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.0/schema.mysql
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectNewGroupMembers
+ A /rt/branches/rt-3.3/html/Admin/Elements/ToolTabs
+ A /rt/branches/rt-3.3/html/Admin/Tools
+ A /rt/branches/rt-3.3/html/Admin/Tools/Configuration.html
+ A /rt/branches/rt-3.3/html/Admin/Tools/index.html
+ A /rt/branches/rt-3.3/html/Elements/QueryString
+ A /rt/branches/rt-3.3/html/Elements/ScrubHTML
+ M /rt/branches/rt-3.3/html/Elements/Tabs
+ M /rt/branches/rt-3.3/html/Elements/TicketList
+ M /rt/branches/rt-3.3/html/NoAuth/webrt.css
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/rt-3.3/html/Scope/Search.html
+ M /rt/branches/rt-3.3/html/Search/Build.html
+ M /rt/branches/rt-3.3/html/Search/Edit.html
+ M /rt/branches/rt-3.3/html/Search/Elements/BuildFormatString
+ M /rt/branches/rt-3.3/html/Search/Elements/DisplayOptions
+ M /rt/branches/rt-3.3/html/Search/Elements/EditFormat
+ M /rt/branches/rt-3.3/html/Search/Elements/EditSearches
+ M /rt/branches/rt-3.3/html/Search/Results.html
+ M /rt/branches/rt-3.3/html/Search/Results.rdf
+ M /rt/branches/rt-3.3/html/Search/Results.tsv
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransaction
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransactionAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/Tabs
+ A /rt/branches/rt-3.3/html/Ticket/ShowEmailRecord.html
+ M /rt/branches/rt-3.3/html/Ticket/Update.html
+ A /rt/branches/rt-3.3/html/Tools/Elements
+ A /rt/branches/rt-3.3/html/Tools/Elements/Tabs
+ A /rt/branches/rt-3.3/html/Tools/Offline.html
+ M /rt/branches/rt-3.3/html/autohandler
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Group_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/I18N.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/Queue_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Users_Overlay.pm
+ M /rt/branches/rt-3.3/sbin/rt-test-dependencies.in
+
+ ----------------------------------------------------------------------
+ r2113@tinbook: jesse | 2004-04-21T05:24:17.573962Z
+
+ Remerging from RT 3.1.x
+ ----------------------------------------------------------------------
+
+------------------------------------------------------------------------
+r714 | autrijus | 2004-04-20 23:49:45 -0400 (Tue, 20 Apr 2004) | 7 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldImage
+ A /rt/branches/rt-3.3/html/Elements/ShowCustomFieldImage
+ M /rt/branches/rt-3.3/html/Elements/ShowCustomFields
+
+r4005@not: autrijus | 2004-04-21T03:49:17.533641Z
+
+RT-Ticket: 5365
+RT-Status: resolved
+
+Make image display inline on ticket info screen.
+
+------------------------------------------------------------------------
+r712 | leira | 2004-04-20 22:49:09 -0400 (Tue, 20 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Admin/Groups/index.html
+ M /rt/branches/rt-3.3/html/Admin/Users/index.html
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldImage
+
+fix WebPath urls
+
+------------------------------------------------------------------------
+r711 | leira | 2004-04-20 22:43:57 -0400 (Tue, 20 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Admin/Groups/index.html
+ M /rt/branches/rt-3.3/html/Admin/Users/index.html
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldBinary
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldImage
+
+Dowload links need the WebPath.
+
+------------------------------------------------------------------------
+r710 | leira | 2004-04-20 22:37:34 -0400 (Tue, 20 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValue_Overlay.pm
+
+decode_utf8, not decode_utf_8
+
+------------------------------------------------------------------------
+r709 | leira | 2004-04-20 22:16:04 -0400 (Tue, 20 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValue_Overlay.pm
+
+"decode_utf_8", not "decode_utf 8"
+
+------------------------------------------------------------------------
+r708 | leira | 2004-04-20 22:15:39 -0400 (Tue, 20 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/CustomField_Overlay.pm
+
+CFs are no longer attached to queues.
+
+------------------------------------------------------------------------
+r707 | jesse | 2004-04-20 21:21:11 -0400 (Tue, 20 Apr 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+
+Adding support for encoding LOBs for the new custom field behaviour
+------------------------------------------------------------------------
+r703 | jesse | 2004-04-19 22:26:50 -0400 (Mon, 19 Apr 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/etc/schema.Pg
+ M /rt/branches/rt-3.3/html/Ticket/Elements/FindAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/rt-3.3/lib/RT/Transaction_Overlay.pm
+
+"Another pass at trying to fix the schema"
+------------------------------------------------------------------------
+r702 | jesse | 2004-04-19 22:12:32 -0400 (Mon, 19 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/etc/acl.Pg
+ M /rt/branches/rt-3.3/etc/schema.Pg
+
+Fixing conflicts from fixing the same bugs as linda.
+
+------------------------------------------------------------------------
+r701 | leira | 2004-04-19 14:35:01 -0400 (Mon, 19 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/etc/acl.Pg
+
+One more acl fix.
+
+------------------------------------------------------------------------
+r700 | leira | 2004-04-19 14:16:08 -0400 (Mon, 19 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/etc/acl.Pg
+ M /rt/branches/rt-3.3/etc/schema.Pg
+
+Schema & acl fixes for Postgres.
+
+------------------------------------------------------------------------
+r695 | autrijus | 2004-04-16 15:33:06 -0400 (Fri, 16 Apr 2004) | 4 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldSelect
+
+r3928@not: autrijus | 2004-04-16T19:32:35.970968Z
+
+Fix select CF display bug as reported by leira
+
+------------------------------------------------------------------------
+r685 | autrijus | 2004-04-14 05:29:23 -0400 (Wed, 14 Apr 2004) | 5 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldBinary
+
+r3851@not: autrijus | 2004-04-14T09:27:57.550568Z
+
+* make EditCustomFieldBinary's condition style agree with other
+ elements -- also avoids comparing an uninitialized value.
+
+------------------------------------------------------------------------
+r682 | leira | 2004-04-12 02:07:01 -0400 (Mon, 12 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Admin/Groups/Modify.html
+ M /rt/branches/rt-3.3/html/Admin/Users/Modify.html
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldBinary
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldImage
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldSelect
+ M /rt/branches/rt-3.3/html/Elements/EditCustomFieldText
+ M /rt/branches/rt-3.3/html/Search/Elements/SelectPersonType
+ M /rt/branches/rt-3.3/html/Ticket/Elements/EditCustomFields
+ M /rt/branches/rt-3.3/html/Ticket/Modify.html
+ M /rt/branches/rt-3.3/html/Ticket/ModifyAll.html
+ M /rt/branches/rt-3.3/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomFields_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Transaction_Overlay.pm
+
+Fixes from autrijus.
+
+------------------------------------------------------------------------
+r665 | jesse | 2004-04-02 16:54:23 -0500 (Fri, 02 Apr 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3/html/Admin/Elements/PickObjects
+
+Allowing the custom field admin screens to add custom fields to all users
+------------------------------------------------------------------------
+r664 | jesse | 2004-04-02 16:39:27 -0500 (Fri, 02 Apr 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+ M /rt/branches/rt-3.3/lib/RT/Transaction_Overlay.pm
+
+Pulling forward relationships fixes from 3.1; updating a transaction-related assertion for 3.3
+------------------------------------------------------------------------
+r662 | jesse | 2004-04-02 10:54:22 -0500 (Fri, 02 Apr 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3/etc/acl.Pg
+ M /rt/branches/rt-3.3/html/Admin/Elements/ModifyQueue
+ M /rt/branches/rt-3.3/html/Admin/Queues/Modify.html
+ A /rt/branches/rt-3.3/html/Elements/EditLinks
+ M /rt/branches/rt-3.3/html/Elements/MessageBox
+ M /rt/branches/rt-3.3/html/Elements/SelectMatch
+ A /rt/branches/rt-3.3/html/Elements/ShowLink
+ A /rt/branches/rt-3.3/html/Elements/ShowLinks
+ M /rt/branches/rt-3.3/html/Scope/Elements/ShowSummary
+ M /rt/branches/rt-3.3/html/Search/Build.html
+ M /rt/branches/rt-3.3/html/Search/Bulk.html
+ M /rt/branches/rt-3.3/html/Search/Elements/SelectPersonType
+ M /rt/branches/rt-3.3/html/SelfService/Display.html
+ M /rt/branches/rt-3.3/html/Ticket/Create.html
+ M /rt/branches/rt-3.3/html/Ticket/Display.html
+ A /rt/branches/rt-3.3/html/Ticket/Elements/LoadTextAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowHistory
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowSummary
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/rt-3.3/html/Ticket/History.html
+ M /rt/branches/rt-3.3/html/Ticket/ModifyAll.html
+ M /rt/branches/rt-3.3/html/Ticket/ModifyLinks.html
+ M /rt/branches/rt-3.3/html/Ticket/Update.html
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/SendEmail.pm
+ M /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Attribute_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+ M /rt/branches/rt-3.3/lib/RT/EmailParser.pm
+ A /rt/branches/rt-3.3/lib/RT/I18N/da.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fr.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/hu.po
+ M /rt/branches/rt-3.3/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/rt-3.3/lib/RT/User_Overlay.pm
+
+Took a hand-merging pass at 3.1->3.3 after autrijus manual baseless merge.
+------------------------------------------------------------------------
+r661 | autrijus | 2004-04-02 08:57:08 -0500 (Fri, 02 Apr 2004) | 3 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+
+* put Ticket_Overlay back to where it was.
+
+
+------------------------------------------------------------------------
+r660 | autrijus | 2004-04-02 08:43:44 -0500 (Fri, 02 Apr 2004) | 1 line
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3
+ M /rt/branches/rt-3.3/Makefile.in
+ A /rt/branches/rt-3.3/README.Oracle
+ M /rt/branches/rt-3.3/UPGRADING
+ M /rt/branches/rt-3.3/bin/mason_handler.fcgi.in
+ M /rt/branches/rt-3.3/bin/mason_handler.scgi.in
+ M /rt/branches/rt-3.3/bin/rt-crontool.in
+ M /rt/branches/rt-3.3/bin/rt-mailgate.in
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/config.layout
+ M /rt/branches/rt-3.3/configure.ac
+ M /rt/branches/rt-3.3/etc/initialdata
+ M /rt/branches/rt-3.3/etc/schema.Informix
+ M /rt/branches/rt-3.3/etc/schema.Oracle
+ M /rt/branches/rt-3.3/etc/schema.SQLite
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.15
+ A /rt/branches/rt-3.3/etc/upgrade/3.1.15/content
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditScrip
+ M /rt/branches/rt-3.3/html/Admin/Elements/SelectStage
+ M /rt/branches/rt-3.3/html/Elements/Callback
+ M /rt/branches/rt-3.3/html/Elements/MessageBox
+ M /rt/branches/rt-3.3/html/Elements/SelectLang
+ M /rt/branches/rt-3.3/html/Elements/SelectMatch
+ M /rt/branches/rt-3.3/html/Elements/SelectWatcherType
+ M /rt/branches/rt-3.3/html/Elements/SetupSessionCookie
+ M /rt/branches/rt-3.3/html/Elements/SimpleSearch
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/default
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/history
+ M /rt/branches/rt-3.3/html/REST/1.0/autohandler
+ M /rt/branches/rt-3.3/html/REST/1.0/dhandler
+ M /rt/branches/rt-3.3/html/REST/1.0/search/ticket
+ M /rt/branches/rt-3.3/html/Scope/Elements/ShowHistory
+ M /rt/branches/rt-3.3/html/Scope/Elements/ShowTransaction
+ M /rt/branches/rt-3.3/html/Search/Elements/PickRestriction
+ M /rt/branches/rt-3.3/html/SelfService/Elements/MyRequests
+ M /rt/branches/rt-3.3/html/Ticket/Attachment/dhandler
+ M /rt/branches/rt-3.3/html/Ticket/Display.html
+ M /rt/branches/rt-3.3/html/Ticket/Elements/FindAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowHistory
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowMessageStanza
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowRequestor
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/rt-3.3/html/Ticket/Elements/Tabs
+ M /rt/branches/rt-3.3/html/index.html
+ M /rt/branches/rt-3.3/lib/RT/Action/AutoOpen.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Attachments_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Base.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/EmailParser.pm
+ M /rt/branches/rt-3.3/lib/RT/Group_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Handle.pm
+ M /rt/branches/rt-3.3/lib/RT/I18N/fi.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/fr.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_cn.po
+ M /rt/branches/rt-3.3/lib/RT/I18N/zh_tw.po
+ M /rt/branches/rt-3.3/lib/RT/I18N.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Email/Auth/MailFrom.pm
+ M /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ M /rt/branches/rt-3.3/lib/RT/Principal_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Queue_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/StyleGuide.pod
+ M /rt/branches/rt-3.3/lib/RT/Template_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Tickets_Overlay_SQL.pm
+ M /rt/branches/rt-3.3/lib/RT/Transaction_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Transactions_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/URI.pm
+ M /rt/branches/rt-3.3/lib/RT/User_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Users_Overlay.pm
+ M /rt/branches/rt-3.3/lib/t/03web.pl.in
+ M /rt/branches/rt-3.3/lib/t/04_send_email.pl.in
+ M /rt/branches/rt-3.3/sbin/license_tag
+ M /rt/branches/rt-3.3/sbin/rt-test-dependencies.in
+
+MERGE: 3.1 to 3.3
+------------------------------------------------------------------------
+r659 | jesse | 2004-04-01 21:23:57 -0500 (Thu, 01 Apr 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3/releng.cnf
+
+Updated releng.cnf
+------------------------------------------------------------------------
+r656 | jesse | 2004-04-01 16:54:22 -0500 (Thu, 01 Apr 2004) | 2 lines
+Changed paths:
+ M /rt/branches/rt-3.3/html/Admin/CustomFields/Objects.html
+ M /rt/branches/rt-3.3/html/Admin/CustomFields/index.html
+ M /rt/branches/rt-3.3/html/Admin/Elements/EditCustomFields
+ M /rt/branches/rt-3.3/html/Admin/Elements/PickCustomFields
+ M /rt/branches/rt-3.3/html/Admin/Elements/PickObjects
+ M /rt/branches/rt-3.3/html/Admin/Elements/QueueTabs
+
+UI updates to remove unneeded options and clarify some language
+
+------------------------------------------------------------------------
+r655 | jesse | 2004-04-01 16:53:29 -0500 (Thu, 01 Apr 2004) | 3 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/CustomField_Overlay.pm
+
+Updated how RT generates friendly name for which object a Custom Field applies to
+
+
+------------------------------------------------------------------------
+r654 | jesse | 2004-04-01 16:52:31 -0500 (Thu, 01 Apr 2004) | 1 line
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/Transactions_Overlay.pm
+
+Added support for RT 3.0 compatible Transactions->Limit
+------------------------------------------------------------------------
+r653 | autrijus | 2004-04-01 11:37:33 -0500 (Thu, 01 Apr 2004) | 4 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/html/Admin/Elements/QueueTabs
+
+r3597@not: autrijus | 2004-04-01T16:36:37.019460Z
+
+* put global Cf editing back.
+
+------------------------------------------------------------------------
+r648 | autrijus | 2004-03-31 11:12:04 -0500 (Wed, 31 Mar 2004) | 4 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/I18N.pm
+
+r3556@not: autrijus | 2004-03-31T16:11:11.225511Z
+
+* lexicon path may contain spaces, esp. on win32.
+
+------------------------------------------------------------------------
+r645 | autrijus | 2004-03-31 01:20:10 -0500 (Wed, 31 Mar 2004) | 5 lines
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/etc/schema.Informix
+ M /rt/branches/rt-3.3/etc/schema.Oracle
+ M /rt/branches/rt-3.3/etc/schema.Pg
+ M /rt/branches/rt-3.3/etc/schema.SQLite
+ M /rt/branches/rt-3.3/etc/schema.mysql
+
+r3541@not: autrijus | 2004-03-31T06:19:36.740253Z
+
+* port mysql schema to Pg.
+* other database are partially ported -- OCF and OCFV currently not there
+
+------------------------------------------------------------------------
+r643 | autrijus | 2004-03-31 00:16:07 -0500 (Wed, 31 Mar 2004) | 1 line
+Changed paths:
+ M /rt
+ M /rt/branches/rt-3.3/lib/RT/CustomFields_Overlay.pm
+
+hint to self: don't refactor when you're tired.
+------------------------------------------------------------------------
+r612 | autrijus | 2004-03-22 13:46:04 -0500 (Mon, 22 Mar 2004) | 2 lines
+Changed paths:
+ D /rt/branches/rt-3.3/html/Admin/Global/CustomField.html
+ D /rt/branches/rt-3.3/html/Admin/Global/CustomFields.html
+
+* remove unused "Global" CF admin pages.
+
+------------------------------------------------------------------------
+r611 | autrijus | 2004-03-20 10:40:58 -0500 (Sat, 20 Mar 2004) | 7 lines
+Changed paths:
+ M /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+ M /rt/branches/rt-3.3/lib/RT/CustomField_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/User_Overlay.pm
+
+* merge Jesse's $RT::SystemUser->LanguageHandle fix; corrected a
+ bug caused by it in setup database stage where there's no SystemUser.
+* fixed two tests in RT::CustomField that incorrectly expected old-style
+ ->Type return values; they now test ->Type and ->MaxValues separately.
+* change some ok() tests into is() and like() tests.
+
+
+------------------------------------------------------------------------
+r597 | autrijus | 2004-03-17 13:12:25 -0500 (Wed, 17 Mar 2004) | 2 lines
+Changed paths:
+ M /rt/branches
+ A /rt/branches/rt-3.3/html/Developer
+ A /rt/branches/rt-3.3/html/Developer/Log.html
+ A /rt/branches/rt-3.3/html/Developer/Perldoc.html
+ A /rt/branches/rt-3.3/html/Developer/autohandler
+
+* Developer tools.
+
+------------------------------------------------------------------------
+r585 | autrijus | 2004-03-16 14:22:32 -0500 (Tue, 16 Mar 2004) | 3 lines
+Changed paths:
+ M /rt/branches
+ M /rt/branches/rt-3.3/bin/webmux.pl.in
+ M /rt/branches/rt-3.3/etc/acl.mysql
+ M /rt/branches/rt-3.3/lib/RT.pm.in
+
+* move class loading to RT::InitClasses.
+* do not do acl if mysql is in skip-grant-tables mode.
+
+------------------------------------------------------------------------
+r584 | autrijus | 2004-03-16 14:07:50 -0500 (Tue, 16 Mar 2004) | 3 lines
+Changed paths:
+ M /rt/branches
+ M /rt/branches/rt-3.3/Makefile.in
+ M /rt/branches/rt-3.3/etc/RT_Config.pm.in
+
+* it is possible that the DESTDIR is the current directory.
+* also, hint on how to use MasonX::Profiler more concisely.
+
+------------------------------------------------------------------------
+r571 | autrijus | 2004-03-15 10:22:17 -0500 (Mon, 15 Mar 2004) | 2 lines
+Changed paths:
+ M /rt/branches
+ M /rt/branches/rt-3.3/html/User/Prefs.html
+
+* backport lang handle setting from -elixus
+
+------------------------------------------------------------------------
+r560 | autrijus | 2004-03-12 21:44:05 -0500 (Fri, 12 Mar 2004) | 2 lines
+Changed paths:
+ M /rt/branches
+ M /rt/branches/rt-3.3/README
+ M /rt/branches/rt-3.3/bin/rt-mailgate.in
+ M /rt/branches/rt-3.3/bin/rt.in
+ M /rt/branches/rt-3.3/etc/RT_Config.pm.in
+ M /rt/branches/rt-3.3/etc/schema.Oracle
+ M /rt/branches/rt-3.3/html/Admin/Users/Modify.html
+ M /rt/branches/rt-3.3/html/Elements/Footer
+ M /rt/branches/rt-3.3/html/Elements/Header
+ M /rt/branches/rt-3.3/html/Elements/Login
+ M /rt/branches/rt-3.3/html/Elements/SelectQueue
+ M /rt/branches/rt-3.3/html/Elements/TicketList
+ M /rt/branches/rt-3.3/html/NoAuth/webrt.css
+ M /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/attachments
+ M /rt/branches/rt-3.3/html/REST/1.0/ticket/comment
+ M /rt/branches/rt-3.3/html/Search/Build.html
+ M /rt/branches/rt-3.3/html/Search/Elements/BuildFormatString
+ M /rt/branches/rt-3.3/html/Search/Elements/PickBasics
+ M /rt/branches/rt-3.3/html/Search/Elements/PickRestriction
+ M /rt/branches/rt-3.3/html/Search/Results.html
+ M /rt/branches/rt-3.3/html/Search/Results.rdf
+ M /rt/branches/rt-3.3/html/Ticket/Display.html
+ A /rt/branches/rt-3.3/html/Ticket/Elements/FindAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/PreviewScrips
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowAttachments
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowHistory
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowSummary
+ M /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransaction
+ M /rt/branches/rt-3.3/html/Ticket/Elements/Tabs
+ M /rt/branches/rt-3.3/html/Ticket/History.html
+ M /rt/branches/rt-3.3/html/autohandler
+ M /rt/branches/rt-3.3/lib/RT/Action/Autoreply.pm
+ M /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ M /rt/branches/rt-3.3/lib/RT/Condition/OwnerChange.pm
+ M /rt/branches/rt-3.3/lib/RT/Handle.pm
+ M /rt/branches/rt-3.3/lib/RT/Link_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/Record.pm
+ M /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ M /rt/branches/rt-3.3/lib/RT/URI/fsck_com_rt.pm
+ M /rt/branches/rt-3.3/lib/RT/User_Overlay.pm
+ M /rt/branches/rt-3.3/releng.cnf
+
+* smerge rt-3.1 to rt-3.3.
+
+------------------------------------------------------------------------
+r559 | autrijus | 2004-03-12 21:22:26 -0500 (Fri, 12 Mar 2004) | 2 lines
+Changed paths:
+ M /rt/branches
+ D /rt/branches/autrijus-3.1
+ A /rt/branches/rt-3.3
+ A /rt/branches/rt-3.3/COPYING
+ A /rt/branches/rt-3.3/HOWTO
+ A /rt/branches/rt-3.3/HOWTO/README
+ A /rt/branches/rt-3.3/HOWTO/change.txt
+ A /rt/branches/rt-3.3/HOWTO/release.txt
+ A /rt/branches/rt-3.3/HOWTO/version-control.txt
+ A /rt/branches/rt-3.3/Makefile.in
+ A /rt/branches/rt-3.3/README
+ A /rt/branches/rt-3.3/UPGRADING
+ A /rt/branches/rt-3.3/aclocal.m4
+ A /rt/branches/rt-3.3/bin
+ A /rt/branches/rt-3.3/bin/mason_handler.fcgi.in
+ A /rt/branches/rt-3.3/bin/mason_handler.scgi.in
+ A /rt/branches/rt-3.3/bin/mason_handler.svc.in
+ A /rt/branches/rt-3.3/bin/rt-commit-handler.in
+ A /rt/branches/rt-3.3/bin/rt-crontool.in
+ A /rt/branches/rt-3.3/bin/rt-mailgate.in
+ A /rt/branches/rt-3.3/bin/rt.in
+ A /rt/branches/rt-3.3/bin/standalone_httpd.in
+ A /rt/branches/rt-3.3/bin/webmux.pl.in
+ A /rt/branches/rt-3.3/config.layout
+ A /rt/branches/rt-3.3/configure.ac
+ A /rt/branches/rt-3.3/docs
+ A /rt/branches/rt-3.3/docs/README.docs
+ A /rt/branches/rt-3.3/docs/Security
+ A /rt/branches/rt-3.3/docs/design_docs
+ A /rt/branches/rt-3.3/docs/design_docs/CARS
+ A /rt/branches/rt-3.3/docs/design_docs/TransactionTypes.txt
+ A /rt/branches/rt-3.3/docs/design_docs/acls
+ A /rt/branches/rt-3.3/docs/design_docs/approval_notices
+ A /rt/branches/rt-3.3/docs/design_docs/approval_template
+ A /rt/branches/rt-3.3/docs/design_docs/cf_search
+ A /rt/branches/rt-3.3/docs/design_docs/cli_spec
+ A /rt/branches/rt-3.3/docs/design_docs/cvs_integration
+ A /rt/branches/rt-3.3/docs/design_docs/delegation
+ A /rt/branches/rt-3.3/docs/design_docs/evil_plans
+ A /rt/branches/rt-3.3/docs/design_docs/groups_notes
+ A /rt/branches/rt-3.3/docs/design_docs/link-definitions.txt
+ A /rt/branches/rt-3.3/docs/design_docs/recursive_group_membership_algorithm
+ A /rt/branches/rt-3.3/docs/design_docs/rql_parser_machine.graphviz
+ A /rt/branches/rt-3.3/docs/design_docs/string-extraction-guide.txt
+ A /rt/branches/rt-3.3/docs/design_docs/subscription-definitions.txt
+ A /rt/branches/rt-3.3/docs/design_docs/ticket_templates
+ A /rt/branches/rt-3.3/docs/design_docs/users
+ A /rt/branches/rt-3.3/docs/rt3-schema-relationships.dot
+ A /rt/branches/rt-3.3/etc
+ A /rt/branches/rt-3.3/etc/RT_Config.pm.in
+ A /rt/branches/rt-3.3/etc/RT_SiteConfig.pm
+ A /rt/branches/rt-3.3/etc/acl.Informix
+ A /rt/branches/rt-3.3/etc/acl.Oracle
+ A /rt/branches/rt-3.3/etc/acl.Pg
+ A /rt/branches/rt-3.3/etc/acl.mysql
+ A /rt/branches/rt-3.3/etc/constraints.mysql
+ A /rt/branches/rt-3.3/etc/drop.Informix
+ A /rt/branches/rt-3.3/etc/drop.Oracle
+ A /rt/branches/rt-3.3/etc/initialdata
+ A /rt/branches/rt-3.3/etc/rt.spec
+ A /rt/branches/rt-3.3/etc/schema.Informix
+ A /rt/branches/rt-3.3/etc/schema.Oracle
+ A /rt/branches/rt-3.3/etc/schema.Pg
+ A /rt/branches/rt-3.3/etc/schema.SQLite
+ A /rt/branches/rt-3.3/etc/schema.mysql
+ A /rt/branches/rt-3.3/etc/upgrade
+ A /rt/branches/rt-3.3/etc/upgrade/2.1.71
+ A /rt/branches/rt-3.3/html
+ A /rt/branches/rt-3.3/html/Admin
+ A /rt/branches/rt-3.3/html/Admin/CustomFields
+ A /rt/branches/rt-3.3/html/Admin/CustomFields/GroupRights.html
+ A /rt/branches/rt-3.3/html/Admin/CustomFields/Modify.html
+ A /rt/branches/rt-3.3/html/Admin/CustomFields/Objects.html
+ A /rt/branches/rt-3.3/html/Admin/CustomFields/UserRights.html
+ A /rt/branches/rt-3.3/html/Admin/CustomFields/index.html
+ A /rt/branches/rt-3.3/html/Admin/Elements
+ A /rt/branches/rt-3.3/html/Admin/Elements/AddCustomFieldValue
+ A /rt/branches/rt-3.3/html/Admin/Elements/CreateUserCalled
+ A /rt/branches/rt-3.3/html/Admin/Elements/CustomFieldTabs
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditCustomField
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditCustomFieldValues
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditCustomFields
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditQueueWatchers
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditScrip
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditScrips
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditTemplates
+ A /rt/branches/rt-3.3/html/Admin/Elements/EditUserComments
+ A /rt/branches/rt-3.3/html/Admin/Elements/GroupTabs
+ A /rt/branches/rt-3.3/html/Admin/Elements/Header
+ A /rt/branches/rt-3.3/html/Admin/Elements/ListGlobalCustomFields
+ A /rt/branches/rt-3.3/html/Admin/Elements/ListGlobalScrips
+ A /rt/branches/rt-3.3/html/Admin/Elements/ModifyQueue
+ A /rt/branches/rt-3.3/html/Admin/Elements/ModifyTemplate
+ A /rt/branches/rt-3.3/html/Admin/Elements/ModifyUser
+ A /rt/branches/rt-3.3/html/Admin/Elements/ObjectCustomFields
+ A /rt/branches/rt-3.3/html/Admin/Elements/PickCustomFields
+ A /rt/branches/rt-3.3/html/Admin/Elements/PickObjects
+ A /rt/branches/rt-3.3/html/Admin/Elements/QueueRightsForUser
+ A /rt/branches/rt-3.3/html/Admin/Elements/QueueTabs
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectCustomFieldLookupType
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectCustomFieldType
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectGroups
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectModifyGroup
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectModifyQueue
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectModifyUser
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectNewGroupMembers
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectRights
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectScrip
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectScripAction
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectScripCondition
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectSingleOrMultiple
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectStage
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectTemplate
+ A /rt/branches/rt-3.3/html/Admin/Elements/SelectUsers
+ A /rt/branches/rt-3.3/html/Admin/Elements/SystemTabs
+ A /rt/branches/rt-3.3/html/Admin/Elements/Tabs
+ A /rt/branches/rt-3.3/html/Admin/Elements/UserTabs
+ A /rt/branches/rt-3.3/html/Admin/Global
+ A /rt/branches/rt-3.3/html/Admin/Global/CustomField.html
+ A /rt/branches/rt-3.3/html/Admin/Global/CustomFields.html
+ A /rt/branches/rt-3.3/html/Admin/Global/GroupRights.html
+ A /rt/branches/rt-3.3/html/Admin/Global/Scrip.html
+ A /rt/branches/rt-3.3/html/Admin/Global/Scrips.html
+ A /rt/branches/rt-3.3/html/Admin/Global/Template.html
+ A /rt/branches/rt-3.3/html/Admin/Global/Templates.html
+ A /rt/branches/rt-3.3/html/Admin/Global/UserRights.html
+ A /rt/branches/rt-3.3/html/Admin/Global/index.html
+ A /rt/branches/rt-3.3/html/Admin/Groups
+ A /rt/branches/rt-3.3/html/Admin/Groups/CustomFields.html
+ A /rt/branches/rt-3.3/html/Admin/Groups/GroupRights.html
+ A /rt/branches/rt-3.3/html/Admin/Groups/History.html
+ A /rt/branches/rt-3.3/html/Admin/Groups/Members.html
+ A /rt/branches/rt-3.3/html/Admin/Groups/Modify.html
+ A /rt/branches/rt-3.3/html/Admin/Groups/UserRights.html
+ A /rt/branches/rt-3.3/html/Admin/Groups/index.html
+ A /rt/branches/rt-3.3/html/Admin/Queues
+ A /rt/branches/rt-3.3/html/Admin/Queues/CustomField.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/CustomFields.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/GroupRights.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/Modify.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/People.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/Scrip.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/Scrips.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/Template.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/Templates.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/UserRights.html
+ A /rt/branches/rt-3.3/html/Admin/Queues/index.html
+ A /rt/branches/rt-3.3/html/Admin/Users
+ A /rt/branches/rt-3.3/html/Admin/Users/CustomFields.html
+ A /rt/branches/rt-3.3/html/Admin/Users/History.html
+ A /rt/branches/rt-3.3/html/Admin/Users/Memberships.html
+ A /rt/branches/rt-3.3/html/Admin/Users/Modify.html
+ A /rt/branches/rt-3.3/html/Admin/Users/Prefs.html
+ A /rt/branches/rt-3.3/html/Admin/Users/index.html
+ A /rt/branches/rt-3.3/html/Admin/index.html
+ A /rt/branches/rt-3.3/html/Approvals
+ A /rt/branches/rt-3.3/html/Approvals/Display.html
+ A /rt/branches/rt-3.3/html/Approvals/Elements
+ A /rt/branches/rt-3.3/html/Approvals/Elements/Approve
+ A /rt/branches/rt-3.3/html/Approvals/Elements/PendingMyApproval
+ A /rt/branches/rt-3.3/html/Approvals/Elements/ShowDependency
+ A /rt/branches/rt-3.3/html/Approvals/Elements/Tabs
+ A /rt/branches/rt-3.3/html/Approvals/index.html
+ A /rt/branches/rt-3.3/html/Download
+ A /rt/branches/rt-3.3/html/Download/CustomFieldValue
+ A /rt/branches/rt-3.3/html/Download/CustomFieldValue/dhandler
+ A /rt/branches/rt-3.3/html/Download/Tabular
+ A /rt/branches/rt-3.3/html/Download/Tabular/dhandler
+ A /rt/branches/rt-3.3/html/Elements
+ A /rt/branches/rt-3.3/html/Elements/BevelBoxRaisedEnd
+ A /rt/branches/rt-3.3/html/Elements/BevelBoxRaisedStart
+ A /rt/branches/rt-3.3/html/Elements/Callback
+ A /rt/branches/rt-3.3/html/Elements/Checkbox
+ A /rt/branches/rt-3.3/html/Elements/CreateTicket
+ A /rt/branches/rt-3.3/html/Elements/EditCustomField
+ A /rt/branches/rt-3.3/html/Elements/EditCustomFieldBinary
+ A /rt/branches/rt-3.3/html/Elements/EditCustomFieldFreeform
+ A /rt/branches/rt-3.3/html/Elements/EditCustomFieldImage
+ A /rt/branches/rt-3.3/html/Elements/EditCustomFieldSelect
+ A /rt/branches/rt-3.3/html/Elements/EditCustomFieldText
+ A /rt/branches/rt-3.3/html/Elements/Error
+ A /rt/branches/rt-3.3/html/Elements/Footer
+ A /rt/branches/rt-3.3/html/Elements/GotoTicket
+ A /rt/branches/rt-3.3/html/Elements/Header
+ A /rt/branches/rt-3.3/html/Elements/ListActions
+ A /rt/branches/rt-3.3/html/Elements/Login
+ A /rt/branches/rt-3.3/html/Elements/Menu
+ A /rt/branches/rt-3.3/html/Elements/MessageBox
+ A /rt/branches/rt-3.3/html/Elements/MyRequests
+ A /rt/branches/rt-3.3/html/Elements/MyTickets
+ A /rt/branches/rt-3.3/html/Elements/PageLayout
+ A /rt/branches/rt-3.3/html/Elements/QuickCreate
+ A /rt/branches/rt-3.3/html/Elements/Quicksearch
+ A /rt/branches/rt-3.3/html/Elements/Refresh
+ A /rt/branches/rt-3.3/html/Elements/Section
+ A /rt/branches/rt-3.3/html/Elements/SelectAttachmentField
+ A /rt/branches/rt-3.3/html/Elements/SelectBoolean
+ A /rt/branches/rt-3.3/html/Elements/SelectCustomFieldOperator
+ A /rt/branches/rt-3.3/html/Elements/SelectCustomFieldValue
+ A /rt/branches/rt-3.3/html/Elements/SelectDate
+ A /rt/branches/rt-3.3/html/Elements/SelectDateRelation
+ A /rt/branches/rt-3.3/html/Elements/SelectDateType
+ A /rt/branches/rt-3.3/html/Elements/SelectEqualityOperator
+ A /rt/branches/rt-3.3/html/Elements/SelectGroups
+ A /rt/branches/rt-3.3/html/Elements/SelectLang
+ A /rt/branches/rt-3.3/html/Elements/SelectLinkType
+ A /rt/branches/rt-3.3/html/Elements/SelectMatch
+ A /rt/branches/rt-3.3/html/Elements/SelectNewTicketQueue
+ A /rt/branches/rt-3.3/html/Elements/SelectOwner
+ A /rt/branches/rt-3.3/html/Elements/SelectQueue
+ A /rt/branches/rt-3.3/html/Elements/SelectResultsPerPage
+ A /rt/branches/rt-3.3/html/Elements/SelectSortOrder
+ A /rt/branches/rt-3.3/html/Elements/SelectStatus
+ A /rt/branches/rt-3.3/html/Elements/SelectTicketSortBy
+ A /rt/branches/rt-3.3/html/Elements/SelectTicketTypes
+ A /rt/branches/rt-3.3/html/Elements/SelectUsers
+ A /rt/branches/rt-3.3/html/Elements/SelectWatcherType
+ A /rt/branches/rt-3.3/html/Elements/SetupSessionCookie
+ A /rt/branches/rt-3.3/html/Elements/ShadedBox
+ A /rt/branches/rt-3.3/html/Elements/ShadedInputRow
+ A /rt/branches/rt-3.3/html/Elements/ShadedRow
+ A /rt/branches/rt-3.3/html/Elements/ShowCustomFields
+ A /rt/branches/rt-3.3/html/Elements/ShowMemberships
+ A /rt/branches/rt-3.3/html/Elements/SimpleSearch
+ A /rt/branches/rt-3.3/html/Elements/Submit
+ A /rt/branches/rt-3.3/html/Elements/Tabs
+ A /rt/branches/rt-3.3/html/Elements/TicketList
+ A /rt/branches/rt-3.3/html/Elements/TitleBox
+ A /rt/branches/rt-3.3/html/Elements/TitleBoxEnd
+ A /rt/branches/rt-3.3/html/Elements/TitleBoxStart
+ A /rt/branches/rt-3.3/html/Elements/ViewUser
+ A /rt/branches/rt-3.3/html/NoAuth
+ A /rt/branches/rt-3.3/html/NoAuth/Logout.html
+ A /rt/branches/rt-3.3/html/NoAuth/Reminder.html
+ A /rt/branches/rt-3.3/html/NoAuth/images
+ A /rt/branches/rt-3.3/html/NoAuth/images/back_home.gif
+ A /rt/branches/rt-3.3/html/NoAuth/images/bplogo.gif
+ A /rt/branches/rt-3.3/html/NoAuth/images/favicon.png
+ A /rt/branches/rt-3.3/html/NoAuth/images/head_requestracker.gif
+ A /rt/branches/rt-3.3/html/NoAuth/images/rt.jpg
+ A /rt/branches/rt-3.3/html/NoAuth/images/space.gif
+ A /rt/branches/rt-3.3/html/NoAuth/images/spacer.gif
+ A /rt/branches/rt-3.3/html/NoAuth/images/squares_blue.gif
+ A /rt/branches/rt-3.3/html/NoAuth/webrt.css
+ A /rt/branches/rt-3.3/html/Projects
+ A /rt/branches/rt-3.3/html/Projects/Create.html
+ A /rt/branches/rt-3.3/html/Projects/Edit.html
+ A /rt/branches/rt-3.3/html/Projects/Elements
+ A /rt/branches/rt-3.3/html/Projects/Elements/CreateTask
+ A /rt/branches/rt-3.3/html/Projects/Elements/EditTask
+ A /rt/branches/rt-3.3/html/Projects/Elements/OverviewEntry
+ A /rt/branches/rt-3.3/html/Projects/Elements/SelectProjectTask
+ A /rt/branches/rt-3.3/html/Projects/Elements/SelectProjectTaskEntry
+ A /rt/branches/rt-3.3/html/Projects/Elements/Tabs
+ A /rt/branches/rt-3.3/html/Projects/Elements/TicketTabs
+ A /rt/branches/rt-3.3/html/Projects/Gantt.html
+ A /rt/branches/rt-3.3/html/Projects/History.html
+ A /rt/branches/rt-3.3/html/Projects/Overview.html
+ A /rt/branches/rt-3.3/html/Projects/Schedule.html
+ A /rt/branches/rt-3.3/html/Projects/ScheduleByActor.html
+ A /rt/branches/rt-3.3/html/Projects/index.html
+ A /rt/branches/rt-3.3/html/REST
+ A /rt/branches/rt-3.3/html/REST/1.0
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/queue
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/queue/default
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/queue/ns
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/attachments
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/default
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/history
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/ticket/links
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/user
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/user/default
+ A /rt/branches/rt-3.3/html/REST/1.0/Forms/user/ns
+ A /rt/branches/rt-3.3/html/REST/1.0/NoAuth
+ A /rt/branches/rt-3.3/html/REST/1.0/NoAuth/mail-gateway
+ A /rt/branches/rt-3.3/html/REST/1.0/autohandler
+ A /rt/branches/rt-3.3/html/REST/1.0/dhandler
+ A /rt/branches/rt-3.3/html/REST/1.0/logout
+ A /rt/branches/rt-3.3/html/REST/1.0/search
+ A /rt/branches/rt-3.3/html/REST/1.0/search/dhandler
+ A /rt/branches/rt-3.3/html/REST/1.0/search/ticket
+ A /rt/branches/rt-3.3/html/REST/1.0/ticket
+ A /rt/branches/rt-3.3/html/REST/1.0/ticket/comment
+ A /rt/branches/rt-3.3/html/REST/1.0/ticket/link
+ A /rt/branches/rt-3.3/html/REST/1.0/ticket/merge
+ A /rt/branches/rt-3.3/html/Scope
+ A /rt/branches/rt-3.3/html/Scope/Action.html
+ A /rt/branches/rt-3.3/html/Scope/Elements
+ A /rt/branches/rt-3.3/html/Scope/Elements/Header
+ A /rt/branches/rt-3.3/html/Scope/Elements/MessageBox
+ A /rt/branches/rt-3.3/html/Scope/Elements/ShowHistory
+ A /rt/branches/rt-3.3/html/Scope/Elements/ShowHistoryView
+ A /rt/branches/rt-3.3/html/Scope/Elements/ShowMessageHeaders
+ A /rt/branches/rt-3.3/html/Scope/Elements/ShowMessageStanza
+ A /rt/branches/rt-3.3/html/Scope/Elements/ShowSummary
+ A /rt/branches/rt-3.3/html/Scope/Elements/ShowTransaction
+ A /rt/branches/rt-3.3/html/Scope/History.html
+ A /rt/branches/rt-3.3/html/Scope/Search.html
+ A /rt/branches/rt-3.3/html/Scope/Update.html
+ A /rt/branches/rt-3.3/html/Scope/View.html
+ A /rt/branches/rt-3.3/html/Scope/Work.html
+ A /rt/branches/rt-3.3/html/Scope/index.html
+ A /rt/branches/rt-3.3/html/Search
+ A /rt/branches/rt-3.3/html/Search/Build.html
+ A /rt/branches/rt-3.3/html/Search/Bulk.html
+ A /rt/branches/rt-3.3/html/Search/Edit.html
+ A /rt/branches/rt-3.3/html/Search/Elements
+ A /rt/branches/rt-3.3/html/Search/Elements/BuildFormatString
+ A /rt/branches/rt-3.3/html/Search/Elements/DisplayOptions
+ A /rt/branches/rt-3.3/html/Search/Elements/EditFormat
+ A /rt/branches/rt-3.3/html/Search/Elements/EditSearches
+ A /rt/branches/rt-3.3/html/Search/Elements/NewListActions
+ A /rt/branches/rt-3.3/html/Search/Elements/PickBasics
+ A /rt/branches/rt-3.3/html/Search/Elements/PickCFs
+ A /rt/branches/rt-3.3/html/Search/Elements/PickCriteria
+ A /rt/branches/rt-3.3/html/Search/Elements/PickRestriction
+ A /rt/branches/rt-3.3/html/Search/Elements/SearchPrivacy
+ A /rt/branches/rt-3.3/html/Search/Elements/SelectAndOr
+ A /rt/branches/rt-3.3/html/Search/Elements/SelectGroup
+ A /rt/branches/rt-3.3/html/Search/Elements/SelectLinks
+ A /rt/branches/rt-3.3/html/Search/Elements/SelectPersonType
+ A /rt/branches/rt-3.3/html/Search/Elements/SelectSearchObject
+ A /rt/branches/rt-3.3/html/Search/Elements/SelectSearchesForObjects
+ A /rt/branches/rt-3.3/html/Search/Elements/TicketHeader
+ A /rt/branches/rt-3.3/html/Search/Elements/TicketHeaderCell
+ A /rt/branches/rt-3.3/html/Search/Elements/TicketRow
+ A /rt/branches/rt-3.3/html/Search/Listing.html
+ A /rt/branches/rt-3.3/html/Search/Results.html
+ A /rt/branches/rt-3.3/html/Search/Results.rdf
+ A /rt/branches/rt-3.3/html/Search/Results.tsv
+ A /rt/branches/rt-3.3/html/SelfService
+ A /rt/branches/rt-3.3/html/SelfService/Attachment
+ A /rt/branches/rt-3.3/html/SelfService/Attachment/dhandler
+ A /rt/branches/rt-3.3/html/SelfService/Closed.html
+ A /rt/branches/rt-3.3/html/SelfService/Create.html
+ A /rt/branches/rt-3.3/html/SelfService/Display.html
+ A /rt/branches/rt-3.3/html/SelfService/Elements
+ A /rt/branches/rt-3.3/html/SelfService/Elements/GotoTicket
+ A /rt/branches/rt-3.3/html/SelfService/Elements/Header
+ A /rt/branches/rt-3.3/html/SelfService/Elements/MyRequests
+ A /rt/branches/rt-3.3/html/SelfService/Elements/Tabs
+ A /rt/branches/rt-3.3/html/SelfService/Error.html
+ A /rt/branches/rt-3.3/html/SelfService/Prefs.html
+ A /rt/branches/rt-3.3/html/SelfService/Update.html
+ A /rt/branches/rt-3.3/html/SelfService/index.html
+ A /rt/branches/rt-3.3/html/Ticket
+ A /rt/branches/rt-3.3/html/Ticket/Attachment
+ A /rt/branches/rt-3.3/html/Ticket/Attachment/dhandler
+ A /rt/branches/rt-3.3/html/Ticket/Create.html
+ A /rt/branches/rt-3.3/html/Ticket/Display.html
+ A /rt/branches/rt-3.3/html/Ticket/Elements
+ A /rt/branches/rt-3.3/html/Ticket/Elements/AddWatchers
+ A /rt/branches/rt-3.3/html/Ticket/Elements/BulkLinks
+ A /rt/branches/rt-3.3/html/Ticket/Elements/EditBasics
+ A /rt/branches/rt-3.3/html/Ticket/Elements/EditCustomField
+ A /rt/branches/rt-3.3/html/Ticket/Elements/EditCustomFields
+ A /rt/branches/rt-3.3/html/Ticket/Elements/EditDates
+ A /rt/branches/rt-3.3/html/Ticket/Elements/EditLinks
+ A /rt/branches/rt-3.3/html/Ticket/Elements/EditPeople
+ A /rt/branches/rt-3.3/html/Ticket/Elements/EditWatchers
+ A /rt/branches/rt-3.3/html/Ticket/Elements/PreviewScrips
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowAttachments
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowBasics
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowCustomFields
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowDates
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowDependencies
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowHistory
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowLink
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowLinks
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowMemberOf
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowMembers
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowMessageHeaders
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowMessageStanza
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowPeople
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowReferences
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowRequestor
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowSummary
+ A /rt/branches/rt-3.3/html/Ticket/Elements/ShowTransaction
+ A /rt/branches/rt-3.3/html/Ticket/Elements/Tabs
+ A /rt/branches/rt-3.3/html/Ticket/History.html
+ A /rt/branches/rt-3.3/html/Ticket/Modify.html
+ A /rt/branches/rt-3.3/html/Ticket/ModifyAll.html
+ A /rt/branches/rt-3.3/html/Ticket/ModifyDates.html
+ A /rt/branches/rt-3.3/html/Ticket/ModifyLinks.html
+ A /rt/branches/rt-3.3/html/Ticket/ModifyPeople.html
+ A /rt/branches/rt-3.3/html/Ticket/Update.html
+ A /rt/branches/rt-3.3/html/Tools
+ A /rt/branches/rt-3.3/html/Tools/MyDay.html
+ A /rt/branches/rt-3.3/html/User
+ A /rt/branches/rt-3.3/html/User/Delegation.html
+ A /rt/branches/rt-3.3/html/User/Elements
+ A /rt/branches/rt-3.3/html/User/Elements/DelegateRights
+ A /rt/branches/rt-3.3/html/User/Elements/GroupTabs
+ A /rt/branches/rt-3.3/html/User/Elements/Tabs
+ A /rt/branches/rt-3.3/html/User/Groups
+ A /rt/branches/rt-3.3/html/User/Groups/Members.html
+ A /rt/branches/rt-3.3/html/User/Groups/Modify.html
+ A /rt/branches/rt-3.3/html/User/Groups/index.html
+ A /rt/branches/rt-3.3/html/User/Prefs.html
+ A /rt/branches/rt-3.3/html/autohandler
+ A /rt/branches/rt-3.3/html/index.html
+ A /rt/branches/rt-3.3/html/l
+ A /rt/branches/rt-3.3/install-sh
+ A /rt/branches/rt-3.3/lib
+ A /rt/branches/rt-3.3/lib/RT
+ A /rt/branches/rt-3.3/lib/RT/ACE.pm
+ A /rt/branches/rt-3.3/lib/RT/ACE_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ACL.pm
+ A /rt/branches/rt-3.3/lib/RT/ACL_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Action
+ A /rt/branches/rt-3.3/lib/RT/Action/AutoOpen.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/Autoreply.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/CreateTickets.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/EscalatePriority.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/Generic.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/Notify.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/NotifyAsComment.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/ResolveMembers.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/SendEmail.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/SetPriority.pm
+ A /rt/branches/rt-3.3/lib/RT/Action/UserDefined.pm
+ A /rt/branches/rt-3.3/lib/RT/Attachment.pm
+ A /rt/branches/rt-3.3/lib/RT/Attachment_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Attachments.pm
+ A /rt/branches/rt-3.3/lib/RT/Attachments_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Attribute.pm
+ A /rt/branches/rt-3.3/lib/RT/Attribute_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Attributes.pm
+ A /rt/branches/rt-3.3/lib/RT/Attributes_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Base.pm
+ A /rt/branches/rt-3.3/lib/RT/CachedGroupMember.pm
+ A /rt/branches/rt-3.3/lib/RT/CachedGroupMember_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/CachedGroupMembers.pm
+ A /rt/branches/rt-3.3/lib/RT/CachedGroupMembers_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition
+ A /rt/branches/rt-3.3/lib/RT/Condition/AnyTransaction.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/BeforeDue.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/Generic.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/Overdue.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/OwnerChange.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/PriorityExceeds.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/QueueChange.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/StatusChange.pm
+ A /rt/branches/rt-3.3/lib/RT/Condition/UserDefined.pm
+ A /rt/branches/rt-3.3/lib/RT/CurrentUser.pm
+ A /rt/branches/rt-3.3/lib/RT/CustomField.pm
+ A /rt/branches/rt-3.3/lib/RT/CustomFieldValue.pm
+ A /rt/branches/rt-3.3/lib/RT/CustomFieldValues.pm
+ A /rt/branches/rt-3.3/lib/RT/CustomFieldValues_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/CustomField_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/CustomFields.pm
+ A /rt/branches/rt-3.3/lib/RT/CustomFields_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Date.pm
+ A /rt/branches/rt-3.3/lib/RT/EmailParser.pm
+ A /rt/branches/rt-3.3/lib/RT/Group.pm
+ A /rt/branches/rt-3.3/lib/RT/GroupMember.pm
+ A /rt/branches/rt-3.3/lib/RT/GroupMember_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/GroupMembers.pm
+ A /rt/branches/rt-3.3/lib/RT/GroupMembers_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Group_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Groups.pm
+ A /rt/branches/rt-3.3/lib/RT/Groups_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Handle.pm
+ A /rt/branches/rt-3.3/lib/RT/I18N
+ A /rt/branches/rt-3.3/lib/RT/I18N/cs.pm
+ A /rt/branches/rt-3.3/lib/RT/I18N/cs.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/de.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/en.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/en_malkovich.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/es.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/fi.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/fr.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/he.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/i_default.pm
+ A /rt/branches/rt-3.3/lib/RT/I18N/it.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/ja.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/nl.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/no.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/pt_br.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/ru.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/zh_cn.po
+ A /rt/branches/rt-3.3/lib/RT/I18N/zh_tw.po
+ A /rt/branches/rt-3.3/lib/RT/I18N.pm
+ A /rt/branches/rt-3.3/lib/RT/Interface
+ A /rt/branches/rt-3.3/lib/RT/Interface/CLI.pm
+ A /rt/branches/rt-3.3/lib/RT/Interface/Email
+ A /rt/branches/rt-3.3/lib/RT/Interface/Email/Auth
+ A /rt/branches/rt-3.3/lib/RT/Interface/Email/Auth/GnuPG.pm
+ A /rt/branches/rt-3.3/lib/RT/Interface/Email/Auth/MailFrom.pm
+ A /rt/branches/rt-3.3/lib/RT/Interface/Email/Filter
+ A /rt/branches/rt-3.3/lib/RT/Interface/Email/Filter/SpamAssassin.pm
+ A /rt/branches/rt-3.3/lib/RT/Interface/Email.pm
+ A /rt/branches/rt-3.3/lib/RT/Interface/REST.pm
+ A /rt/branches/rt-3.3/lib/RT/Interface/Web.pm
+ A /rt/branches/rt-3.3/lib/RT/Link.pm
+ A /rt/branches/rt-3.3/lib/RT/Link_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Links.pm
+ A /rt/branches/rt-3.3/lib/RT/Links_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomField.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValue.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValue_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValues.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomFieldValues_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomField_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomFields.pm
+ A /rt/branches/rt-3.3/lib/RT/ObjectCustomFields_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Principal.pm
+ A /rt/branches/rt-3.3/lib/RT/Principal_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Principals.pm
+ A /rt/branches/rt-3.3/lib/RT/Principals_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Queue.pm
+ A /rt/branches/rt-3.3/lib/RT/Queue_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Queues.pm
+ A /rt/branches/rt-3.3/lib/RT/Queues_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Record.pm
+ A /rt/branches/rt-3.3/lib/RT/Scrip.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripAction.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripAction_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripActions.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripActions_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripCondition.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripCondition_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripConditions.pm
+ A /rt/branches/rt-3.3/lib/RT/ScripConditions_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Scrip_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Scrips.pm
+ A /rt/branches/rt-3.3/lib/RT/Scrips_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Search
+ A /rt/branches/rt-3.3/lib/RT/Search/ActiveTicketsInQueue.pm
+ A /rt/branches/rt-3.3/lib/RT/Search/Generic.pm
+ A /rt/branches/rt-3.3/lib/RT/SearchBuilder.pm
+ A /rt/branches/rt-3.3/lib/RT/StyleGuide.pod
+ A /rt/branches/rt-3.3/lib/RT/System.pm
+ A /rt/branches/rt-3.3/lib/RT/Template.pm
+ A /rt/branches/rt-3.3/lib/RT/Template_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Templates.pm
+ A /rt/branches/rt-3.3/lib/RT/Templates_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Ticket.pm
+ A /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValue.pm
+ A /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValue_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValues.pm
+ A /rt/branches/rt-3.3/lib/RT/TicketCustomFieldValues_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Ticket_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Tickets.pm
+ A /rt/branches/rt-3.3/lib/RT/Tickets_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Tickets_Overlay_SQL.pm
+ A /rt/branches/rt-3.3/lib/RT/Transaction.pm
+ A /rt/branches/rt-3.3/lib/RT/Transaction_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Transactions.pm
+ A /rt/branches/rt-3.3/lib/RT/Transactions_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/URI
+ A /rt/branches/rt-3.3/lib/RT/URI/base.pm
+ A /rt/branches/rt-3.3/lib/RT/URI/fsck_com_rt.pm
+ A /rt/branches/rt-3.3/lib/RT/URI.pm
+ A /rt/branches/rt-3.3/lib/RT/User.pm
+ A /rt/branches/rt-3.3/lib/RT/User_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT/Users.pm
+ A /rt/branches/rt-3.3/lib/RT/Users_Overlay.pm
+ A /rt/branches/rt-3.3/lib/RT.pm.in
+ A /rt/branches/rt-3.3/lib/t
+ A /rt/branches/rt-3.3/lib/t/00smoke.t.in
+ A /rt/branches/rt-3.3/lib/t/01harness.t.in
+ A /rt/branches/rt-3.3/lib/t/02regression.t.in
+ A /rt/branches/rt-3.3/lib/t/03web.pl.in
+ A /rt/branches/rt-3.3/lib/t/04_send_email.pl.in
+ A /rt/branches/rt-3.3/lib/t/data
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/dir
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg1
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg2
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg3
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg4
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg5
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg6
+ A /rt/branches/rt-3.3/lib/t/data/8859-15-message-series/msg7
+ A /rt/branches/rt-3.3/lib/t/data/crashes-file-based-parser
+ A /rt/branches/rt-3.3/lib/t/data/multipart-alternative-with-umlaut
+ A /rt/branches/rt-3.3/lib/t/data/multipart-report
+ A /rt/branches/rt-3.3/lib/t/data/nested-mime-sample
+ A /rt/branches/rt-3.3/lib/t/data/nested-rfc-822
+ A /rt/branches/rt-3.3/lib/t/data/new-ticket-from-iso-8859-1
+ A /rt/branches/rt-3.3/lib/t/data/new-ticket-from-iso-8859-1-full
+ A /rt/branches/rt-3.3/lib/t/data/notes-uuencoded
+ A /rt/branches/rt-3.3/lib/t/data/russian-subject-no-content-type
+ A /rt/branches/rt-3.3/lib/t/data/text-html-in-russian
+ A /rt/branches/rt-3.3/lib/t/data/text-html-with-umlaut
+ A /rt/branches/rt-3.3/lib/t/regression
+ A /rt/branches/rt-3.3/lib/t/regression/00placeholder
+ A /rt/branches/rt-3.3/lib/t/regression/mime_tests
+ A /rt/branches/rt-3.3/m4
+ A /rt/branches/rt-3.3/m4/rt_enable_layout.m4
+ A /rt/branches/rt-3.3/m4/rt_expand_var.m4
+ A /rt/branches/rt-3.3/m4/rt_layout.m4
+ A /rt/branches/rt-3.3/m4/rt_subst_expanded_arg.m4
+ A /rt/branches/rt-3.3/releng.cnf
+ A /rt/branches/rt-3.3/sbin
+ A /rt/branches/rt-3.3/sbin/extract-message-catalog
+ A /rt/branches/rt-3.3/sbin/extract_pod_tests
+ A /rt/branches/rt-3.3/sbin/factory
+ A /rt/branches/rt-3.3/sbin/license_tag
+ A /rt/branches/rt-3.3/sbin/regression_harness
+ A /rt/branches/rt-3.3/sbin/rt-setup-database.in
+ A /rt/branches/rt-3.3/sbin/rt-test-dependencies.in
+ A /rt/branches/rt-3.3/spec
+ A /rt/branches/rt-3.3/spec/schema.txt
+
+* mv autrijus-3.1 to rt-3.3
+
+------------------------------------------------------------------------
diff --git a/rt/FREESIDE_MODIFIED b/rt/FREESIDE_MODIFIED
new file mode 100644
index 0000000..6691779
--- /dev/null
+++ b/rt/FREESIDE_MODIFIED
@@ -0,0 +1,34 @@
+ sbin/rt-setup-database.in
+config.layout
+config.layout.in
+ etc/RT_SiteConfig.pm
+lib/RT/Interface/Web_Vendor.pm
+lib/RT/SearchBuilder.pm #need DBIx::SearchBuilder >= 1.36 for Pg 8.1+
+lib/RT/URI/freeside.pm
+lib/RT/URI/freeside/Internal.pm
+lib/RT/URI/freeside/XMLRPC.pm
+ html/Elements/Header
+ html/Elements/Menu
+ html/Elements/PageLayout
+ html/Elements/QuickCreate
+ html/Elements/SimpleSearch
+ html/Elements/Tabs
+ html/Elements/Footer
+ html/Elements/CollectionAsTable/Row #backport from 3.3-TESTING
+html/Ticket/Elements/AddCustomers
+html/Ticket/Elements/EditCustomers
+html/Ticket/Elements/ShowCustomers
+ html/Ticket/Elements/ShowSummary
+ html/Ticket/Elements/Tabs
+html/Ticket/ModifyCustomers.html
+html/NoAuth/images/small-logo.png
+ html/NoAuth/css/3.5-default/main.css
+html/NoAuth/css/3.5-default/freeside.css
+
+html/Widgets/TitleBoxStart
+
+html/Elements/FreesideNewCust
+html/Elements/FreesideSearch
+html/Elements/FreesideSvcSearch
+
+
diff --git a/rt/Makefile b/rt/Makefile
new file mode 100644
index 0000000..e6a5dde
--- /dev/null
+++ b/rt/Makefile
@@ -0,0 +1,494 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+#
+# DO NOT HAND-EDIT the file named 'Makefile'. This file is autogenerated.
+# Have a look at "configure" and "Makefile.in" instead
+#
+
+
+PERL = /usr/bin/perl
+
+CONFIG_FILE_PATH = /opt/rt3/etc
+CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_Config.pm
+SITE_CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_SiteConfig.pm
+
+
+RT_VERSION_MAJOR = 3
+RT_VERSION_MINOR = 6
+RT_VERSION_PATCH = 4
+
+RT_VERSION = $(RT_VERSION_MAJOR).$(RT_VERSION_MINOR).$(RT_VERSION_PATCH)
+TAG = rt-$(RT_VERSION_MAJOR)-$(RT_VERSION_MINOR)-$(RT_VERSION_PATCH)
+
+
+# This is the group that all of the installed files will be chgrp'ed to.
+RTGROUP = freeside
+
+
+# User which should own rt binaries.
+BIN_OWNER = root
+
+# User that should own all of RT's libraries, generally root.
+LIBS_OWNER = root
+
+# Group that should own all of RT's libraries, generally root.
+LIBS_GROUP = bin
+
+WEB_USER = freeside
+WEB_GROUP = freeside
+
+
+APACHECTL = /usr/sbin/apachectl
+
+# {{{ Files and directories
+
+# DESTDIR allows you to specify that RT be installed somewhere other than
+# where it will eventually reside
+
+DESTDIR =
+
+
+RT_PATH = /opt/rt3
+RT_ETC_PATH = /opt/rt3/etc
+RT_BIN_PATH = /opt/rt3/bin
+RT_SBIN_PATH = /opt/rt3/sbin
+RT_LIB_PATH = /opt/rt3/lib
+RT_MAN_PATH = /opt/rt3/man
+RT_VAR_PATH = /opt/rt3/var
+RT_DOC_PATH = /opt/rt3/share/doc
+RT_LOCAL_PATH = /opt/rt3/local
+LOCAL_ETC_PATH = /opt/rt3/local/etc
+LOCAL_LIB_PATH = /opt/rt3/local/lib
+LOCAL_LEXICON_PATH = /opt/rt3/local/po
+MASON_HTML_PATH = /var/www/freeside/rt
+MASON_LOCAL_HTML_PATH = /opt/rt3/local/html
+MASON_DATA_PATH = /usr/local/etc/freeside/masondata
+MASON_SESSION_PATH = /opt/rt3/var/session_data
+RT_LOG_PATH = /opt/rt3/var/log
+
+# RT_READABLE_DIR_MODE is the mode of directories that are generally meant
+# to be accessable
+RT_READABLE_DIR_MODE = 0755
+
+
+
+
+# {{{ all these define the places that RT's binaries should get installed
+
+# RT_MODPERL_HANDLER is the mason handler script for mod_perl
+RT_MODPERL_HANDLER = $(RT_BIN_PATH)/webmux.pl
+# RT_STANDALONE_SERVER is a stand-alone HTTP server
+RT_STANDALONE_SERVER = $(RT_BIN_PATH)/standalone_httpd
+# RT_SPEEDYCGI_HANDLER is the mason handler script for SpeedyCGI
+RT_SPEEDYCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.scgi
+# RT_FASTCGI_HANDLER is the mason handler script for FastCGI
+RT_FASTCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.fcgi
+# RT_WIN32_FASTCGI_HANDLER is the mason handler script for FastCGI
+RT_WIN32_FASTCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.svc
+# RT's CLI
+RT_CLI_BIN = $(RT_BIN_PATH)/rt
+# RT's mail gateway
+RT_MAILGATE_BIN = $(RT_BIN_PATH)/rt-mailgate
+# RT's cron tool
+RT_CRON_BIN = $(RT_BIN_PATH)/rt-crontool
+
+# }}}
+
+
+BINARIES = $(DESTDIR)/$(RT_MODPERL_HANDLER) \
+ $(DESTDIR)/$(RT_MAILGATE_BIN) \
+ $(DESTDIR)/$(RT_CLI_BIN) \
+ $(DESTDIR)/$(RT_CRON_BIN) \
+ $(DESTDIR)/$(RT_STANDALONE_SERVER) \
+ $(DESTDIR)/$(RT_SPEEDYCGI_HANDLER) \
+ $(DESTDIR)/$(RT_FASTCGI_HANDLER) \
+ $(DESTDIR)/$(RT_WIN32_FASTCGI_HANDLER)
+SYSTEM_BINARIES = $(DESTDIR)/$(RT_SBIN_PATH)/
+
+# }}}
+
+# {{{ Database setup
+
+#
+# DB_TYPE defines what sort of database RT trys to talk to
+# "mysql" is known to work.
+# "Pg" is known to work
+# "Informix" is known to work
+
+DB_TYPE = Pg
+
+# Set DBA to the name of a unix account with the proper permissions and
+# environment to run your commandline SQL sbin
+
+# Set DB_DBA to the name of a DB user with permission to create new databases
+
+# For mysql, you probably want 'root'
+# For Pg, you probably want 'postgres'
+# For Oracle, you want 'system'
+# For Informix, you want 'informix'
+
+DB_DBA = freeside
+
+DB_HOST = localhost
+
+# If you're not running your database server on its default port,
+# specifiy the port the database server is running on below.
+# It's generally safe to leave this blank
+
+DB_PORT =
+
+
+
+
+#
+# Set this to the canonical name of the interface RT will be talking to the
+# database on. If you said that the RT_DB_HOST above was "localhost," this
+# should be too. This value will be used to grant rt access to the database.
+# If you want to access the RT database from multiple hosts, you'll need
+# to grant those database rights by hand.
+#
+
+DB_RT_HOST = localhost
+
+# set this to the name you want to give to the RT database in
+# your database server. For Oracle, this should be the name of your sid
+
+DB_DATABASE = freeside
+DB_RT_USER = freeside
+DB_RT_PASS =
+
+# }}}
+
+
+####################################################################
+
+all: default
+
+default:
+ @echo "Please read RT's readme before installing. Not doing so could"
+ @echo "be dangerous."
+
+
+
+instruct:
+ @echo "Congratulations. RT has been installed. "
+ @echo ""
+ @echo ""
+ @echo "You must now configure RT by editing $(SITE_CONFIG_FILE)."
+ @echo ""
+ @echo "(You will definitely need to set RT's database password in "
+ @echo "$(SITE_CONFIG_FILE) before continuing. Not doing so could be "
+ @echo "very dangerous. Note that you do not have to manually add a "
+ @echo "database user or set up a database for RT. These actions will be "
+ @echo "taken care of in the next step.)"
+ @echo ""
+ @echo "After that, you need to initialize RT's database by running"
+ @echo " 'make initialize-database'"
+
+# @echo " or by executing "
+# @echo " '$(RT_SBIN_PATH)/rt-setup-database --action init \ "
+# @echo " --dba $(DB_DBA) --prompt-for-dba-password'"
+
+
+
+upgrade-instruct:
+ @echo "Congratulations. RT has been upgraded. You should now check-over"
+ @echo "$(CONFIG_FILE) for any necessary site customization. Additionally,"
+ @echo "you should update RT's system database objects by running "
+ @echo " ls etc/upgrade"
+ @echo ""
+ @echo "For each item in that directory whose name is greater than"
+ @echo "your previously installed RT version, run:"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action schema --datadir etc/upgrade/<version>"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action acl --datadir etc/upgrade/<version>"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action insert --datadir etc/upgrade/<version>"
+
+
+upgrade: config-install dirs files-install fixperms upgrade-instruct
+
+upgrade-noclobber: config-install libs-install html-install bin-install local-install doc-install fixperms
+
+
+# {{{ dependencies
+testdeps:
+ $(PERL) ./sbin/rt-test-dependencies --verbose --with-$(DB_TYPE)
+
+depends: fixdeps
+
+fixdeps:
+ $(PERL) ./sbin/rt-test-dependencies --verbose --install --with-$(DB_TYPE)
+
+#}}}
+
+# {{{ fixperms
+fixperms:
+ # Make the libraries readable
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_PATH)
+ chown -R $(LIBS_OWNER) $(DESTDIR)/$(RT_LIB_PATH)
+ chgrp -R $(LIBS_GROUP) $(DESTDIR)/$(RT_LIB_PATH)
+ chmod -R u+rwX,go-w,go+rX $(DESTDIR)/$(RT_LIB_PATH)
+
+
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_BIN_PATH)
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_BIN_PATH)
+
+ chmod 0755 $(DESTDIR)/$(RT_ETC_PATH)
+ chmod 0500 $(DESTDIR)/$(RT_ETC_PATH)/*
+
+ #TODO: the config file should probably be able to have its
+ # owner set separately from the binaries.
+ chown -R $(BIN_OWNER) $(DESTDIR)/$(RT_ETC_PATH)
+ chgrp -R $(RTGROUP) $(DESTDIR)/$(RT_ETC_PATH)
+
+ chmod 0550 $(DESTDIR)/$(CONFIG_FILE)
+ chmod 0550 $(DESTDIR)/$(SITE_CONFIG_FILE)
+
+ # Make the interfaces executable
+ chown $(BIN_OWNER) $(BINARIES)
+ chgrp $(RTGROUP) $(BINARIES)
+ chmod 0755 $(BINARIES)
+
+ # Make the web ui readable by all.
+ chmod -R u+rwX,go-w,go+rX $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH) \
+ $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+ chown -R $(LIBS_OWNER) $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ chgrp -R $(LIBS_GROUP) $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+
+ # Make the web ui's data dir writable
+ chmod 0770 $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+ chown -R $(WEB_USER) $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+ chgrp -R $(WEB_GROUP) $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+# }}}
+
+# {{{ dirs
+dirs:
+ mkdir -p $(DESTDIR)/$(RT_LOG_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/cache
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/etc
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/obj
+ mkdir -p $(DESTDIR)/$(MASON_SESSION_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_HTML_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_ETC_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_LIB_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+# }}}
+
+install: config-install dirs files-install fixperms instruct
+
+files-install: libs-install etc-install bin-install sbin-install html-install local-install doc-install
+
+config-install:
+ mkdir -p $(DESTDIR)/$(CONFIG_FILE_PATH)
+ -cp etc/RT_Config.pm $(DESTDIR)/$(CONFIG_FILE)
+ [ -f $(DESTDIR)/$(SITE_CONFIG_FILE) ] || cp etc/RT_SiteConfig.pm $(DESTDIR)/$(SITE_CONFIG_FILE)
+
+ chgrp $(RTGROUP) $(DESTDIR)/$(CONFIG_FILE)
+ chown $(BIN_OWNER) $(DESTDIR)/$(CONFIG_FILE)
+
+ chgrp $(RTGROUP) $(DESTDIR)/$(SITE_CONFIG_FILE)
+ chown $(BIN_OWNER) $(DESTDIR)/$(SITE_CONFIG_FILE)
+
+ @echo "Installed configuration. about to install rt in $(RT_PATH)"
+
+test:
+ $(PERL) -Ilib lib/t/00smoke.t
+
+regression-install: config-install
+ $(PERL) -pi -e 's/Set\(\$$DatabaseName.*\);/Set\(\$$DatabaseName, "rt3regression"\);/' $(DESTDIR)/$(CONFIG_FILE)
+
+regression: regression-install dirs files-install libs-install sbin-install bin-install regression-instruct regression-reset-db testify-pods fixperms apachectl run-regression
+
+run-regression:
+ prove -Ilib lib/t/setup_regression.t lib/t/autogen/ lib/t/regression/
+
+
+regression-noapache: regression-install dirs files-install libs-install sbin-install bin-install regression-instruct regression-reset-db testify-pods fixperms start-httpd run-regression
+
+regression-quiet:
+ $(PERL) sbin/regression_harness
+
+regression-instruct:
+ @echo "About to wipe your database for a regression test. ABORT NOW with Control-C"
+
+
+# {{{ database-installation
+
+regression-reset-db:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action drop --dba $(DB_DBA) --dba-password '' --force
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action init --dba $(DB_DBA) --dba-password ''
+
+initdb :: initialize-database
+
+initialize-database:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action init --dba $(DB_DBA) --prompt-for-dba-password
+
+dropdb:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action drop --dba $(DB_DBA) --prompt-for-dba-password
+
+insert-approval-data:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/insert_approval_scrips
+# }}}
+
+# {{{ libs-install
+libs-install:
+ [ -d $(DESTDIR)/$(RT_LIB_PATH) ] || mkdir -p $(DESTDIR)/$(RT_LIB_PATH)
+ -cp -rp lib/* $(DESTDIR)/$(RT_LIB_PATH)
+# }}}
+
+# {{{ html-install
+html-install:
+ [ -d $(DESTDIR)/$(MASON_HTML_PATH) ] || mkdir -p $(DESTDIR)/$(MASON_HTML_PATH)
+ -cp -rp ./html/* $(DESTDIR)/$(MASON_HTML_PATH)
+# }}}
+
+# {{{ doc-install
+doc-install:
+ # RT 3.0.0 - RT 3.0.2 would accidentally create a file instead of a dir
+ -[ -f $(DESTDIR)/$(RT_DOC_PATH) ] && rm $(DESTDIR)/$(RT_DOC_PATH)
+ [ -d $(DESTDIR)/$(RT_DOC_PATH) ] || mkdir -p $(DESTDIR)/$(RT_DOC_PATH)
+ -cp -rp ./README $(DESTDIR)/$(RT_DOC_PATH)
+# }}}
+
+# {{{ etc-install
+
+etc-install:
+ mkdir -p $(DESTDIR)/$(RT_ETC_PATH)
+ -cp -rp \
+ etc/acl.* \
+ etc/initialdata \
+ etc/schema.* \
+ $(DESTDIR)/$(RT_ETC_PATH)
+# }}}
+
+# {{{ sbin-install
+
+sbin-install:
+ mkdir -p $(DESTDIR)/$(RT_SBIN_PATH)
+ chmod +x \
+ sbin/rt-dump-database \
+ sbin/rt-setup-database \
+ sbin/rt-test-dependencies
+ -cp -rp \
+ sbin/rt-dump-database \
+ sbin/rt-setup-database \
+ sbin/rt-test-dependencies \
+ $(DESTDIR)/$(RT_SBIN_PATH)
+
+# }}}
+
+# {{{ bin-install
+
+bin-install:
+ mkdir -p $(DESTDIR)/$(RT_BIN_PATH)
+ chmod +x bin/rt-mailgate \
+ bin/rt-crontool
+ -cp -rp \
+ bin/rt-mailgate \
+ bin/mason_handler.fcgi \
+ bin/mason_handler.scgi \
+ bin/standalone_httpd \
+ bin/mason_handler.svc \
+ bin/rt \
+ bin/webmux.pl \
+ bin/rt-crontool \
+ $(DESTDIR)/$(RT_BIN_PATH)
+# }}}
+
+# {{{ local-install
+local-install:
+ -cp -rp ./local/html/* $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ -cp -rp ./local/po/* $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+ -cp -rp ./local/etc/* $(DESTDIR)/$(LOCAL_ETC_PATH)
+# }}}
+
+# {{{ Best Practical Build targets -- no user servicable parts inside
+
+
+POD2TEST_EXE = sbin/extract_pod_tests
+
+testify-pods:
+ [ -d lib/t/autogen ] || mkdir lib/t/autogen
+ find lib -name \*pm |grep -v .svn | grep -v \*.in |xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find bin -type f |grep -v .svn | grep -v \~ | grep -v "\.in" | xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find lib -name \*pm |grep -v .svn | grep -v \*.in |xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find bin -type f |grep -v .svn | grep -v \~ | grep -v "\.in" | xargs -n 1 $(PERL) $(POD2TEST_EXE)
+
+
+
+regenerate-catalogs:
+ $(PERL) sbin/extract-message-catalog
+
+license-tag:
+ $(PERL) sbin/license_tag
+
+factory: initialize-database
+ cd lib; $(PERL) ../sbin/factory $(DB_DATABASE) RT
+
+reconfigure:
+ aclocal -I m4
+ autoconf
+ chmod 755 ./configure
+ ./configure
+
+start-httpd:
+ $(PERL) bin/standalone_httpd &
+
+apachectl:
+ $(APACHECTL) stop
+ sleep 10
+ $(APACHECTL) start
+ sleep 5
+# }}}
diff --git a/rt/Makefile.in b/rt/Makefile.in
new file mode 100644
index 0000000..2e2f305
--- /dev/null
+++ b/rt/Makefile.in
@@ -0,0 +1,494 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+#
+# DO NOT HAND-EDIT the file named 'Makefile'. This file is autogenerated.
+# Have a look at "configure" and "Makefile.in" instead
+#
+
+
+PERL = @PERL@
+
+CONFIG_FILE_PATH = @CONFIG_FILE_PATH@
+CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_Config.pm
+SITE_CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_SiteConfig.pm
+
+
+RT_VERSION_MAJOR = @RT_VERSION_MAJOR@
+RT_VERSION_MINOR = @RT_VERSION_MINOR@
+RT_VERSION_PATCH = @RT_VERSION_PATCH@
+
+RT_VERSION = $(RT_VERSION_MAJOR).$(RT_VERSION_MINOR).$(RT_VERSION_PATCH)
+TAG = rt-$(RT_VERSION_MAJOR)-$(RT_VERSION_MINOR)-$(RT_VERSION_PATCH)
+
+
+# This is the group that all of the installed files will be chgrp'ed to.
+RTGROUP = @RTGROUP@
+
+
+# User which should own rt binaries.
+BIN_OWNER = @BIN_OWNER@
+
+# User that should own all of RT's libraries, generally root.
+LIBS_OWNER = @LIBS_OWNER@
+
+# Group that should own all of RT's libraries, generally root.
+LIBS_GROUP = @LIBS_GROUP@
+
+WEB_USER = @WEB_USER@
+WEB_GROUP = @WEB_GROUP@
+
+
+APACHECTL = @APACHECTL@
+
+# {{{ Files and directories
+
+# DESTDIR allows you to specify that RT be installed somewhere other than
+# where it will eventually reside
+
+DESTDIR =
+
+
+RT_PATH = @RT_PATH@
+RT_ETC_PATH = @RT_ETC_PATH@
+RT_BIN_PATH = @RT_BIN_PATH@
+RT_SBIN_PATH = @RT_SBIN_PATH@
+RT_LIB_PATH = @RT_LIB_PATH@
+RT_MAN_PATH = @RT_MAN_PATH@
+RT_VAR_PATH = @RT_VAR_PATH@
+RT_DOC_PATH = @RT_DOC_PATH@
+RT_LOCAL_PATH = @RT_LOCAL_PATH@
+LOCAL_ETC_PATH = @LOCAL_ETC_PATH@
+LOCAL_LIB_PATH = @LOCAL_LIB_PATH@
+LOCAL_LEXICON_PATH = @LOCAL_LEXICON_PATH@
+MASON_HTML_PATH = @MASON_HTML_PATH@
+MASON_LOCAL_HTML_PATH = @MASON_LOCAL_HTML_PATH@
+MASON_DATA_PATH = @MASON_DATA_PATH@
+MASON_SESSION_PATH = @MASON_SESSION_PATH@
+RT_LOG_PATH = @RT_LOG_PATH@
+
+# RT_READABLE_DIR_MODE is the mode of directories that are generally meant
+# to be accessable
+RT_READABLE_DIR_MODE = 0755
+
+
+
+
+# {{{ all these define the places that RT's binaries should get installed
+
+# RT_MODPERL_HANDLER is the mason handler script for mod_perl
+RT_MODPERL_HANDLER = $(RT_BIN_PATH)/webmux.pl
+# RT_STANDALONE_SERVER is a stand-alone HTTP server
+RT_STANDALONE_SERVER = $(RT_BIN_PATH)/standalone_httpd
+# RT_SPEEDYCGI_HANDLER is the mason handler script for SpeedyCGI
+RT_SPEEDYCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.scgi
+# RT_FASTCGI_HANDLER is the mason handler script for FastCGI
+RT_FASTCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.fcgi
+# RT_WIN32_FASTCGI_HANDLER is the mason handler script for FastCGI
+RT_WIN32_FASTCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.svc
+# RT's CLI
+RT_CLI_BIN = $(RT_BIN_PATH)/rt
+# RT's mail gateway
+RT_MAILGATE_BIN = $(RT_BIN_PATH)/rt-mailgate
+# RT's cron tool
+RT_CRON_BIN = $(RT_BIN_PATH)/rt-crontool
+
+# }}}
+
+
+BINARIES = $(DESTDIR)/$(RT_MODPERL_HANDLER) \
+ $(DESTDIR)/$(RT_MAILGATE_BIN) \
+ $(DESTDIR)/$(RT_CLI_BIN) \
+ $(DESTDIR)/$(RT_CRON_BIN) \
+ $(DESTDIR)/$(RT_STANDALONE_SERVER) \
+ $(DESTDIR)/$(RT_SPEEDYCGI_HANDLER) \
+ $(DESTDIR)/$(RT_FASTCGI_HANDLER) \
+ $(DESTDIR)/$(RT_WIN32_FASTCGI_HANDLER)
+SYSTEM_BINARIES = $(DESTDIR)/$(RT_SBIN_PATH)/
+
+# }}}
+
+# {{{ Database setup
+
+#
+# DB_TYPE defines what sort of database RT trys to talk to
+# "mysql" is known to work.
+# "Pg" is known to work
+# "Informix" is known to work
+
+DB_TYPE = @DB_TYPE@
+
+# Set DBA to the name of a unix account with the proper permissions and
+# environment to run your commandline SQL sbin
+
+# Set DB_DBA to the name of a DB user with permission to create new databases
+
+# For mysql, you probably want 'root'
+# For Pg, you probably want 'postgres'
+# For Oracle, you want 'system'
+# For Informix, you want 'informix'
+
+DB_DBA = @DB_DBA@
+
+DB_HOST = @DB_HOST@
+
+# If you're not running your database server on its default port,
+# specifiy the port the database server is running on below.
+# It's generally safe to leave this blank
+
+DB_PORT = @DB_PORT@
+
+
+
+
+#
+# Set this to the canonical name of the interface RT will be talking to the
+# database on. If you said that the RT_DB_HOST above was "localhost," this
+# should be too. This value will be used to grant rt access to the database.
+# If you want to access the RT database from multiple hosts, you'll need
+# to grant those database rights by hand.
+#
+
+DB_RT_HOST = @DB_RT_HOST@
+
+# set this to the name you want to give to the RT database in
+# your database server. For Oracle, this should be the name of your sid
+
+DB_DATABASE = @DB_DATABASE@
+DB_RT_USER = @DB_RT_USER@
+DB_RT_PASS = @DB_RT_PASS@
+
+# }}}
+
+
+####################################################################
+
+all: default
+
+default:
+ @echo "Please read RT's readme before installing. Not doing so could"
+ @echo "be dangerous."
+
+
+
+instruct:
+ @echo "Congratulations. RT has been installed. "
+ @echo ""
+ @echo ""
+ @echo "You must now configure RT by editing $(SITE_CONFIG_FILE)."
+ @echo ""
+ @echo "(You will definitely need to set RT's database password in "
+ @echo "$(SITE_CONFIG_FILE) before continuing. Not doing so could be "
+ @echo "very dangerous. Note that you do not have to manually add a "
+ @echo "database user or set up a database for RT. These actions will be "
+ @echo "taken care of in the next step.)"
+ @echo ""
+ @echo "After that, you need to initialize RT's database by running"
+ @echo " 'make initialize-database'"
+
+# @echo " or by executing "
+# @echo " '$(RT_SBIN_PATH)/rt-setup-database --action init \ "
+# @echo " --dba $(DB_DBA) --prompt-for-dba-password'"
+
+
+
+upgrade-instruct:
+ @echo "Congratulations. RT has been upgraded. You should now check-over"
+ @echo "$(CONFIG_FILE) for any necessary site customization. Additionally,"
+ @echo "you should update RT's system database objects by running "
+ @echo " ls etc/upgrade"
+ @echo ""
+ @echo "For each item in that directory whose name is greater than"
+ @echo "your previously installed RT version, run:"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action schema --datadir etc/upgrade/<version>"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action acl --datadir etc/upgrade/<version>"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action insert --datadir etc/upgrade/<version>"
+
+
+upgrade: config-install dirs files-install fixperms upgrade-instruct
+
+upgrade-noclobber: config-install libs-install html-install bin-install local-install doc-install fixperms
+
+
+# {{{ dependencies
+testdeps:
+ $(PERL) ./sbin/rt-test-dependencies --verbose --with-$(DB_TYPE)
+
+depends: fixdeps
+
+fixdeps:
+ $(PERL) ./sbin/rt-test-dependencies --verbose --install --with-$(DB_TYPE)
+
+#}}}
+
+# {{{ fixperms
+fixperms:
+ # Make the libraries readable
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_PATH)
+ chown -R $(LIBS_OWNER) $(DESTDIR)/$(RT_LIB_PATH)
+ chgrp -R $(LIBS_GROUP) $(DESTDIR)/$(RT_LIB_PATH)
+ chmod -R u+rwX,go-w,go+rX $(DESTDIR)/$(RT_LIB_PATH)
+
+
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_BIN_PATH)
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_BIN_PATH)
+
+ chmod 0755 $(DESTDIR)/$(RT_ETC_PATH)
+ chmod 0500 $(DESTDIR)/$(RT_ETC_PATH)/*
+
+ #TODO: the config file should probably be able to have its
+ # owner set separately from the binaries.
+ chown -R $(BIN_OWNER) $(DESTDIR)/$(RT_ETC_PATH)
+ chgrp -R $(RTGROUP) $(DESTDIR)/$(RT_ETC_PATH)
+
+ chmod 0550 $(DESTDIR)/$(CONFIG_FILE)
+ chmod 0550 $(DESTDIR)/$(SITE_CONFIG_FILE)
+
+ # Make the interfaces executable
+ chown $(BIN_OWNER) $(BINARIES)
+ chgrp $(RTGROUP) $(BINARIES)
+ chmod 0755 $(BINARIES)
+
+ # Make the web ui readable by all.
+ chmod -R u+rwX,go-w,go+rX $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH) \
+ $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+ chown -R $(LIBS_OWNER) $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ chgrp -R $(LIBS_GROUP) $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+
+ # Make the web ui's data dir writable
+ chmod 0770 $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+ chown -R $(WEB_USER) $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+ chgrp -R $(WEB_GROUP) $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+# }}}
+
+# {{{ dirs
+dirs:
+ mkdir -p $(DESTDIR)/$(RT_LOG_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/cache
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/etc
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/obj
+ mkdir -p $(DESTDIR)/$(MASON_SESSION_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_HTML_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_ETC_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_LIB_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+# }}}
+
+install: config-install dirs files-install fixperms instruct
+
+files-install: libs-install etc-install bin-install sbin-install html-install local-install doc-install
+
+config-install:
+ mkdir -p $(DESTDIR)/$(CONFIG_FILE_PATH)
+ -cp etc/RT_Config.pm $(DESTDIR)/$(CONFIG_FILE)
+ [ -f $(DESTDIR)/$(SITE_CONFIG_FILE) ] || cp etc/RT_SiteConfig.pm $(DESTDIR)/$(SITE_CONFIG_FILE)
+
+ chgrp $(RTGROUP) $(DESTDIR)/$(CONFIG_FILE)
+ chown $(BIN_OWNER) $(DESTDIR)/$(CONFIG_FILE)
+
+ chgrp $(RTGROUP) $(DESTDIR)/$(SITE_CONFIG_FILE)
+ chown $(BIN_OWNER) $(DESTDIR)/$(SITE_CONFIG_FILE)
+
+ @echo "Installed configuration. about to install rt in $(RT_PATH)"
+
+test:
+ $(PERL) -Ilib lib/t/00smoke.t
+
+regression-install: config-install
+ $(PERL) -pi -e 's/Set\(\$$DatabaseName.*\);/Set\(\$$DatabaseName, "rt3regression"\);/' $(DESTDIR)/$(CONFIG_FILE)
+
+regression: regression-install dirs files-install libs-install sbin-install bin-install regression-instruct regression-reset-db testify-pods fixperms apachectl run-regression
+
+run-regression:
+ prove -Ilib lib/t/setup_regression.t lib/t/autogen/ lib/t/regression/
+
+
+regression-noapache: regression-install dirs files-install libs-install sbin-install bin-install regression-instruct regression-reset-db testify-pods fixperms start-httpd run-regression
+
+regression-quiet:
+ $(PERL) sbin/regression_harness
+
+regression-instruct:
+ @echo "About to wipe your database for a regression test. ABORT NOW with Control-C"
+
+
+# {{{ database-installation
+
+regression-reset-db:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action drop --dba $(DB_DBA) --dba-password '' --force
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action init --dba $(DB_DBA) --dba-password ''
+
+initdb :: initialize-database
+
+initialize-database:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action init --dba $(DB_DBA) --prompt-for-dba-password
+
+dropdb:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action drop --dba $(DB_DBA) --prompt-for-dba-password
+
+insert-approval-data:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/insert_approval_scrips
+# }}}
+
+# {{{ libs-install
+libs-install:
+ [ -d $(DESTDIR)/$(RT_LIB_PATH) ] || mkdir -p $(DESTDIR)/$(RT_LIB_PATH)
+ -cp -rp lib/* $(DESTDIR)/$(RT_LIB_PATH)
+# }}}
+
+# {{{ html-install
+html-install:
+ [ -d $(DESTDIR)/$(MASON_HTML_PATH) ] || mkdir -p $(DESTDIR)/$(MASON_HTML_PATH)
+ -cp -rp ./html/* $(DESTDIR)/$(MASON_HTML_PATH)
+# }}}
+
+# {{{ doc-install
+doc-install:
+ # RT 3.0.0 - RT 3.0.2 would accidentally create a file instead of a dir
+ -[ -f $(DESTDIR)/$(RT_DOC_PATH) ] && rm $(DESTDIR)/$(RT_DOC_PATH)
+ [ -d $(DESTDIR)/$(RT_DOC_PATH) ] || mkdir -p $(DESTDIR)/$(RT_DOC_PATH)
+ -cp -rp ./README $(DESTDIR)/$(RT_DOC_PATH)
+# }}}
+
+# {{{ etc-install
+
+etc-install:
+ mkdir -p $(DESTDIR)/$(RT_ETC_PATH)
+ -cp -rp \
+ etc/acl.* \
+ etc/initialdata \
+ etc/schema.* \
+ $(DESTDIR)/$(RT_ETC_PATH)
+# }}}
+
+# {{{ sbin-install
+
+sbin-install:
+ mkdir -p $(DESTDIR)/$(RT_SBIN_PATH)
+ chmod +x \
+ sbin/rt-dump-database \
+ sbin/rt-setup-database \
+ sbin/rt-test-dependencies
+ -cp -rp \
+ sbin/rt-dump-database \
+ sbin/rt-setup-database \
+ sbin/rt-test-dependencies \
+ $(DESTDIR)/$(RT_SBIN_PATH)
+
+# }}}
+
+# {{{ bin-install
+
+bin-install:
+ mkdir -p $(DESTDIR)/$(RT_BIN_PATH)
+ chmod +x bin/rt-mailgate \
+ bin/rt-crontool
+ -cp -rp \
+ bin/rt-mailgate \
+ bin/mason_handler.fcgi \
+ bin/mason_handler.scgi \
+ bin/standalone_httpd \
+ bin/mason_handler.svc \
+ bin/rt \
+ bin/webmux.pl \
+ bin/rt-crontool \
+ $(DESTDIR)/$(RT_BIN_PATH)
+# }}}
+
+# {{{ local-install
+local-install:
+ -cp -rp ./local/html/* $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ -cp -rp ./local/po/* $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+ -cp -rp ./local/etc/* $(DESTDIR)/$(LOCAL_ETC_PATH)
+# }}}
+
+# {{{ Best Practical Build targets -- no user servicable parts inside
+
+
+POD2TEST_EXE = sbin/extract_pod_tests
+
+testify-pods:
+ [ -d lib/t/autogen ] || mkdir lib/t/autogen
+ find lib -name \*pm |grep -v .svn | grep -v \*.in |xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find bin -type f |grep -v .svn | grep -v \~ | grep -v "\.in" | xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find lib -name \*pm |grep -v .svn | grep -v \*.in |xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find bin -type f |grep -v .svn | grep -v \~ | grep -v "\.in" | xargs -n 1 $(PERL) $(POD2TEST_EXE)
+
+
+
+regenerate-catalogs:
+ $(PERL) sbin/extract-message-catalog
+
+license-tag:
+ $(PERL) sbin/license_tag
+
+factory: initialize-database
+ cd lib; $(PERL) ../sbin/factory $(DB_DATABASE) RT
+
+reconfigure:
+ aclocal -I m4
+ autoconf
+ chmod 755 ./configure
+ ./configure
+
+start-httpd:
+ $(PERL) bin/standalone_httpd &
+
+apachectl:
+ $(APACHECTL) stop
+ sleep 10
+ $(APACHECTL) start
+ sleep 5
+# }}}
diff --git a/rt/README b/rt/README
new file mode 100755
index 0000000..ca88d2e
--- /dev/null
+++ b/rt/README
@@ -0,0 +1,365 @@
+RT is an enterprise-grade issue tracking system. It allows organizations
+to keep track of what needs to get done, who is working on which tasks,
+what's already been done, and when tasks were (or weren't) completed.
+
+RT doesn't cost anything to use, no matter how much you use it; it
+is freely available under the terms of Version 2 of the GNU General
+Public License.
+
+RT is commercially-supported software. To purchase support, training,
+custom development, or professional services, please get in touch with
+us at sales@bestpractical.com.
+
+ Jesse Vincent
+ Best Practical Solutions, LLC
+ March, 2005
+
+
+REQUIRED PACKAGES:
+------------------
+
+o Perl 5.8.3 or later (http://www.perl.com).
+
+ Perl versions prior to 5.8.3 contain bugs that could result
+ in data corruption. We recommend strongly that you use 5.8.3
+ or newer.
+
+o A supported SQL database
+
+ Currently supported: Mysql 4.0.13 or later with InnoDB support.
+ Postgres 7.2 or later.
+ Oracle 9iR2 or later.
+ SQLite 3.0. (Not recommended for production)
+
+o Apache version 1.3.x or 2.x (http://httpd.apache.org)
+ with mod_perl -- (http://perl.apache.org )
+ or a webserver with FastCGI support (www.fastcgi.com)
+
+ Compiling mod_perl on Apache 1.3.x as a DSO has been known
+ to have massive stability problems and is not recommended.
+
+ mod_perl 1.x must be built with EVERYTHING=1
+
+ RT's FastCGI handler needs to access RT's configuration file.
+
+o Various and sundry perl modules
+ A tool included with RT takes care of the installation of
+ most of these automatically during the install process.
+
+ The tool supplied with RT uses Perl's CPAN system
+ (http://www.cpan.org) to install modules. Some operating
+ systems package all or some of the modules required, and
+ you may be better off installing the modules that way.
+
+
+GENERAL INSTALLATION
+--------------------
+
+This is a rough guide to installing RT. For more detail, you'll
+want to read a more comprehensive installation guide at:
+
+ http://wiki.bestpractical.com/index.cgi?InstallationGuides
+
+1 Unpack this distribution other than where you want to install RT
+
+ To do this cleanly, run the following command:
+
+ tar xzvf rt.tar.gz -C /tmp
+
+2 Run the "configure" script.
+
+ ./configure --help to see the list of options
+ ./configure (with the flags you want)
+
+ RT defaults to installing in /opt/rt3 with MySQL as its database. It
+ tries to guess which of www-data, www, apache or nobody your webserver
+ will run as, but you can override that behavior.
+
+3 Make sure that RT has everything it needs to run.
+
+ Check for missing dependencies by running:
+
+ make testdeps
+
+4 If the script reports any missing dependencies, install them by hand
+ or run the following command as a user who has permission to install perl
+ modules on your system:
+
+ make fixdeps
+
+5 Check to make sure everything was installed properly.
+
+ make testdeps
+
+ It might sometimes be necessary to run "make fixdeps" several times
+ to install all necessary perl modules.
+
+6 If this is a new installation:
+
+ As a user with permission to install RT in your chosen directory, type:
+
+ make install
+
+ Set up etc/RT_SiteConfig.pm in your RT installation directory.
+ You'll need to add any values you need to change from the defaults
+ in etc/RT_Config.pm
+
+ As a user with permission to read RT's configuration file, type:
+
+ make initialize-database
+
+ If the make fails, type:
+
+ make dropdb
+
+ and start over from step 6
+
+7 If you're upgrading from RT 3.0 or newer:
+
+ Read through the UPGRADING document included in this distribution.
+
+ It includes special upgrade instructions that will help you get this
+ new version of RT up and running smoothly.
+
+ As a user with permission to install RT in your chosen installation
+ directory, type:
+
+ make upgrade
+
+ This will install new binaries, config files and libraries without
+ overwriting your RT database.
+
+ Update etc/RT_SiteConfig.pm in your RT installation directory.
+ You'll need to add any new values you need to change from the defaults
+ in etc/RT_Config.pm
+
+ You may also need to update RT's database. To find out, type:
+
+ ls etc/upgrade
+
+ For each item in that directory whose name is greater than
+ your previously installed RT version, run:
+
+ /opt/rt3/sbin/rt-setup-database --action schema \
+ --datadir etc/upgrade/<version>
+ /opt/rt3/sbin/rt-setup-database --action acl \
+ --datadir etc/upgrade/<version>
+ /opt/rt3/sbin/rt-setup-database --action insert \
+ --datadir etc/upgrade/<version>
+
+ Clear mason cache dir:
+
+ rm -fr /opt/rt3/var/mason_data/obj
+
+ Stop and start web-server.
+
+
+8 If you're upgrading from RT 2.0:
+
+ Please upgrade from RT 2.0 to RT 3.2 and then follow the instructions
+ for section 7.
+
+9 Configure the email and web gateways, as described below.
+
+ NOTE: root's password for the web interface is "password"
+ (without the quotes). Not changing this is a SECURITY risk!
+
+10 Set up users, groups, queues, scrips and access control.
+
+ Until you do this, RT will not be able to send or receive email,
+ nor will it be more than marginally functional. This is not an
+ optional step.
+
+
+SETTING UP THE WEB INTERFACE
+----------------------------
+
+RT's web interface is based around HTML::Mason, which works well with
+the mod_perl perl interpreter within Apache httpd and FastCGI
+
+mod_perl
+--------
+
+To install RT with mod_perl, you'll need to install the
+apache database connection cache. To make sure it's installed, run
+the following command:
+
+ perl -MCPAN -e'install Apache::DBI'
+
+Next, add a few lines to your Apache configuration file, so that
+it knows where to find RT:
+
+<VirtualHost your.ip.address>
+ ServerName your.rt.server.hostname
+ DocumentRoot /opt/rt3/share/html
+ AddDefaultCharset UTF-8
+
+ PerlModule Apache::DBI
+ PerlRequire /opt/rt3/bin/webmux.pl
+
+ <Location />
+ SetHandler perl-script
+ PerlHandler RT::Mason
+ </Location>
+</VirtualHost>
+
+FastCGI
+-------
+
+Installation with FastCGI is a little bit more complex and is documented
+in detail at http://wiki.bestpractical.com/index.cgi?FastCGIConfiguration
+
+In the most basic configuration, you can set up your webserver to run
+as a user who is a member of the "rt" unix group so that the FastCGI script
+can read RT's configuration file. It's important to understand the security
+implications of this configuration, which are discussed in the document
+mentioned above.
+
+To install RT with FastCGI, you'll need to add a few lines to your
+Apache configuration file telling it about RT:
+
+
+# Tell FastCGI to put its temporary files somewhere sane.
+FastCgiIpcDir /tmp
+
+FastCgiServer /opt/rt3/bin/mason_handler.fcgi -idle-timeout 120
+
+<VirtualHost rt.example.com>
+
+ # Pass through requests to display images
+ Alias /NoAuth/images/ /opt/rt3/share/html/NoAuth/images/
+
+ AddHandler fastcgi-script fcgi
+ ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/
+
+</VirtualHost>
+
+
+
+SETTING UP THE MAIL GATEWAY
+---------------------------
+
+To let email flow to your RT server, you need to add a few lines of
+configuration to your mail server's "aliases" file. These lines "pipe"
+incoming email messages from your mail server to RT.
+
+Add the following lines to /etc/aliases (or your local equivalent) on your mail server:
+
+rt: "|/opt/rt3/bin/rt-mailgate --queue general --action correspond --url http://rt.example.com/"
+rt-comment: "|/opt/rt3/bin/rt-mailgate --queue general --action comment --url http://rt.example.com/"
+
+You'll need to add similar lines for each queue you want to be able
+to send email to. To find out more about how to configure RT's email
+gateway, type:
+
+ perldoc /opt/rt3/bin/rt-mailgate
+
+
+
+GETTING HELP
+------------
+
+If RT is mission-critical for you or if you use it heavily, we recommend that
+you purchase a commercial support contract. Details on support contracts
+are available at http://www.bestpractical.com or by writing to
+<sales@bestpractical.com>.
+
+If you're interested in having RT extended or customized or would like more
+information about commercial support options, please send email to
+<sales@bestpractical.com> to discuss rates and availability.
+
+
+
+RT WEBSITE
+----------
+
+For current information about RT, check out the RT website at
+ http://www.bestpractical.com/
+
+You'll find screenshots, a pointer to the current version of RT, contributed
+patches, and lots of other great stuff.
+
+
+
+RT-USERS MAILING LIST
+--------------------
+
+To keep up to date on the latest RT tips, techniques and extensions,
+you probably want to join the rt-users mailing list. Send a message to:
+
+ rt-users-request@lists.bestpractical.com
+
+with the body of the message consisting of only the word:
+
+ subscribe
+
+If you're interested in hacking on RT, you'll want to subscribe to
+<rt-devel@lists.bestpractical.com>. Subscribe to it with instructions
+similar to those above.
+
+Address questions about the stable release to the rt-users list, and
+questions about the development version to the rt-devel list. If you feel
+your questions are best not asked publicly, send them personally to
+<jesse@bestpractical.com>.
+
+
+
+BUGS
+----
+
+RT's a pretty complex application, and as you get up to speed, you might
+run into some trouble. Generally, it's best to ask about things you
+run into on the rt-users mailinglist (or pick up a commercial support
+contract from Best Practical). But, sometimes people do run into bugs. In
+the exceedingly unlikely event that you hit a bug in RT, please report
+it! We'd love to hear about problems you have with RT, so we can fix them.
+To report a bug, send email to rt-bugs@fsck.com.
+
+
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
diff --git a/rt/README.Oracle b/rt/README.Oracle
new file mode 100644
index 0000000..41bec82
--- /dev/null
+++ b/rt/README.Oracle
@@ -0,0 +1,37 @@
+In order to install RT with Oracle, the database must first be
+prepared. Ports of RT to other databases will automatically create
+the RT schema. This is not done for Oracle because most sites wishing
+to deploy RT on Oracle will have choose to make specific configuration
+of the RT user, for example to select the appropriate tablespace or to
+set up a resource profile. The RT user must have appropriate
+privileges similar to the resource and connect roles and must have the
+"query rewrite" system privilege.
+ Here is an example of commands to create an RT user called "RT" with
+a password of "rt".
+
+ create user rt identified by rt default tablespace users temporary
+ tablespace temp;
+ grant resource, connect, query rewrite to rt;
+
+
+RT should not run its schema creation as the Oracle DBA; instead the
+schema creation should be run as the RT user. To accomplish this set
+the --with-rt-dba configuration parameter to the RT user, not to the
+Oracle DBA. As an example, the following might be appropriate to
+configure RT for the example.com Oracle database.
+
+ ./configure --prefix /usr/local/rt --with-db-type=Oracle \
+ --with-db-dba=rt --with-db-database=example.com \
+ --with-db-rt-user=rt \
+ --with-db-rt-pass=rt
+
+
+As with all databases it is important to analyze the Schema and get
+current statistics after any significant dataset change. Oracle's
+cost-based optimizer can provide particularly bad performance when the
+schema statistics are significantly inaccurate. To analyze the schema
+of a user called rt, execute the following from withing Sqlplus.
+
+ execute dbms_utility.analyze_schema( 'RT', 'estimate');
+
+
diff --git a/rt/UPGRADING b/rt/UPGRADING
new file mode 100644
index 0000000..aca9bb0
--- /dev/null
+++ b/rt/UPGRADING
@@ -0,0 +1,222 @@
+UPGRADING
+
+Detailed information about upgrading can be found in the README file.
+This document is intended to supplement the instructions in that file.
+
+Additional information about upgrading from specific versions of RT is
+contained below.
+
+*******
+WARNING
+*******
+
+Before making any changes to your database, always ensure that you have a
+complete current backup. If you don't have a current backup, you could
+accidentally damage your database and lose data or worse.
+
+*******
+
+UPGRADING FROM 3.5.7 and earlier - Changes:
+
+Scrips are now prepared and committed in order alphanumerically by description.
+This means that you can prepend a number (00, 07, 15, 24) to the beginning of
+each scrip's description, and they will run in that order. Depending on your
+database, the old ordering may have been by scrip id number -- if that is the
+case, simply prepend the scrip id number to the beginning of its description.
+
+
+UPGRADING FROM 3.5.1 and earlier - Changes:
+
+The default for $RedistributeAutoGeneratedMessages has changed to
+'privileged', to make out-of-the-box installations more resistant
+to mail loops. If you rely on the old default of redistributing to
+all watchers, you'll need to set it explicitly now.
+
+
+UPGRADING FROM 3.3.14 and earlier - Changes:
+
+The "ModifyObjectCustomFieldValues" right name was too long. It's been changed to
+"ModifyCustomField"
+
+
+UPGRADING FROM 3.3.11 and earlier - Changes:
+
+= Rights Changes =
+
+Custom Fields now have an additional right "ModifyCustomField".
+This right governs whether a user can modify an object's custom field values
+for a particular custom field. This includes adding, deleting and changing values.
+
+
+UPGRADING FROM 3.2 and earlier - Changes:
+
+= Rights changes =
+
+Now, if you want any user to be able to access the Admin tools (a.k.a.
+the Configuration tab), you must grant that user the "ShowConfigTab"
+right. Making the user a privileged user is no longer sufficient.
+
+"SuperUser" users are no longer automatically added to the list of users who can own tickets in a queue. You now need to explicitly give them the "own tickets" right.
+
+
+
+UPGRADING FROM 3.0.x - Changes:
+
+= Installation =
+
+We recommend you move your existing /opt/rt3 tree completely out
+of the way before installating the newversion of RT, to make sure
+that you don't inadvertently leave old files hanging around.
+
+= Rights changes =
+
+Now, if you want RT to automatically create new users upon ticket
+submission, you MUST grant 'Everyone' the right to create tickets.
+Granting this right only to "Unprivileged Users" is now insufficient.
+
+
+= FastCGI configuration =
+
+This section is a snapshot of the documentation available at:
+
+http://wiki.bestpractical.com/index.cgi?FastCGIConfiguration
+
+It's worth checking out that resource if these instructions don't
+work right for you
+
+
+RT 3.2 includes a signficant change to the FastCGI handler. It is
+no longer "setgid" to the RT group. Perl's setid support has been
+deprecated for the last several releases and a number of platforms
+don't bundle the "sperl" or "suidperl" executable by default.
+Additionally, when perl is run SetUID or SetGID, the interpreter
+is automatically switched into /taint mode/, in which all incoming
+data, no matter the source is considered suspect. At first, this
+seems like a great idea. But perl's taint mode is a big sledgehammer
+used to hit small nails. Many perl libraries aren't tested in taint
+mode and will fail when least expected. Moving away from a SetGID
+FastCGI handler will enable more users to have a smoother RT
+experience. It does require some changes in how you set up and
+configure RT.
+
+Beginning with RT 3.2, you have several choices about how to configure
+RT to run as a FastCGI:
+
+
+== Install RT as the user your webserver runs as ==
+
+Pros: Very easy to configure
+
+Cons: Your webserver has access to RT's private database password
+
+
+=== How To
+
+When installing RT, run:
+
+ ./configure --with-web-user="webuser" --with-web-group="webgroup" \
+ --with-rt-user="webuser" --with-rt-group="webgroup"
+
+(Don't forget to include other configuration options that matter to you)
+
+If you're using apache, you'll want to add something like the following
+to your httpd.conf:
+
+ <VirtualHost rt.example.com>
+
+ # Pass through requests to display images
+ Alias /NoAuth/images/ /opt/rt3/share/html/NoAuth/images/
+
+ # Tell FastCGI to put its temporary files somewhere sane.
+ FastCgiIpcDir /tmp
+
+ FastCgiServer /opt/rt3/bin/mason_handler.fcgi -idle-timeout 120
+
+ AddHandler fastcgi-script fcgi
+ ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/
+
+ </VirtualHost>
+
+
+== Make your webserver user a member of the "rt" group ==
+
+Pros: Easy to configure
+
+Cons: Your webserver has access to RT's private database password
+
+
+=== How To
+
+Install RT normally. Add whichever user your webserver runs as
+(whatever you set --with-web-user to) to the "rt" group (whatever
+you set --with-rt-group to) in /etc/groups.
+
+To find out what user your webserver runs as, look for the line
+
+ User some-user-name
+
+in your apache httpd.conf. Common values are www, www-data, web and nobody.
+
+
+
+== Run RT using _suexec_ or a similar mechanism
+
+
+Pros: More secure
+
+Cons: Sometimes very difficult to configure
+
+Apache's _suexec_ utility allows you run CGI programs as specific
+users. Because that's a relatively heavy responsibility, it's very,
+very conservative about what it's willing to do for you. On top of
+that, Apache's mod_fastcgi plugin doesn't respect all of suexec's
+features. While suexec is designed to execute CGI scripts in a
+given virtual host's !DocumentRoot, It can only execute FastCGI
+scripts in the system's *main* !DocumentRoot.
+
+This means you have to copy the RT FastCGI handler into your main
+!DocumentRoot
+
+The following example !VirtualHost will run RT as a FastCGI on
+Apache 1.3 on a Debian Linux server.
+
+
+ <VirtualHost rt.example.com>
+
+ DocumentRoot /opt/rt3/share/html
+
+ # Set the rt user and group as the executing user for this virtual host
+ User rt
+ Group rt
+
+
+ # Pass through requests to display images
+ Alias /NoAuth/images/ /opt/rt3/share/html/NoAuth/images/
+
+ # Tell FastCGI to put its temporary files somewhere sane.
+ FastCgiIpcDir /tmp
+
+ # Tell FastCGI that it should use apache's "suexec" binary to call any
+ # FastCGI script.
+ # This is a GLOBAL setting
+ FastCgiWrapper /usr/lib/apache/suexec
+
+ # You need to copy the rt mason_handler.fcgi into a directory inside
+ # the main server DocumentRoot
+ # That directory must be owned by the user and group that will execute
+ # the FastCGI script
+ # In this case, that directory is /var/www/rt
+
+ # To find the local DocumentRoot, run "suexec -V" as root and look for the
+ # -D DOC_ROOT parameter.
+
+ # Apache 1.3 discards the user and group parameters on the FastCgiServer
+ # line. Apache 2.0 requires them.
+
+ FastCgiServer /var/www/rt/mason_handler.fcgi -idle-timeout 120 -user rt -group rt
+
+ AddHandler fastcgi-script fcgi
+ ScriptAlias / /var/www/rt/mason_handler.fcgi/
+
+ </VirtualHost>
+
diff --git a/rt/aclocal.m4 b/rt/aclocal.m4
new file mode 100644
index 0000000..9c6b641
--- /dev/null
+++ b/rt/aclocal.m4
@@ -0,0 +1,158 @@
+dnl aclocal.m4 generated automatically by aclocal 1.4-p6
+
+dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+dnl PARTICULAR PURPOSE.
+
+dnl
+dnl @synopsis RT_ENABLE_LAYOUT()
+dnl
+dnl Enable a specific directory layout for the installation to use.
+dnl This configures a command-line parameter that can be specified
+dnl at ./configure invocation.
+dnl
+dnl The use of this feature in this way is a little hackish, but
+dnl better than a heap of options for every directory.
+dnl
+dnl This code is heavily borrowed *cough* from the Apache 2 code.
+dnl
+
+AC_DEFUN([RT_ENABLE_LAYOUT],[
+AC_ARG_ENABLE(layout,
+ AC_HELP_STRING([--enable-layout=LAYOUT],
+ [Use a specific directory layout (Default: RT3)]),
+ LAYOUT=$enableval)
+
+if test "x$LAYOUT" = "x"; then
+ LAYOUT="RT3"
+fi
+RT_LAYOUT($srcdir/config.layout, $LAYOUT)
+AC_MSG_CHECKING(for chosen layout)
+if test "x$rt_layout_name" = "xno"; then
+ if test "x$LAYOUT" = "xno"; then
+ AC_MSG_RESULT(none)
+ else
+ AC_MSG_RESULT($LAYOUT)
+ fi
+ AC_MSG_ERROR([a valid layout must be specified (or the default used)])
+else
+ AC_SUBST(rt_layout_name)
+ AC_MSG_RESULT($rt_layout_name)
+fi
+])
+
+dnl
+dnl @synopsis RT_LAYOUT(configlayout, layoutname)
+dnl
+dnl This macro reads an Apache-style layout file (specified as the
+dnl configlayout parameter), and searches for a specific layout
+dnl (named using the layoutname parameter).
+dnl
+dnl The entries for a given layout are then inserted into the
+dnl environment such that they become available as substitution
+dnl variables. In addition, the rt_layout_name variable is set
+dnl (but not exported) if the layout is valid.
+dnl
+dnl This code is heavily borrowed *cough* from the Apache 2 codebase.
+dnl
+
+AC_DEFUN([RT_LAYOUT],[
+ if test ! -f $srcdir/config.layout; then
+ AC_MSG_WARN([Layout file $srcdir/config.layout not found])
+ rt_layout_name=no
+ else
+ pldconf=./config.pld
+ $PERL -0777 -p -e "\$layout = '$2';" -e '
+ s/.*<Layout\s+$layout>//gims;
+ s/\<\/Layout\>.*//s;
+ s/^#.*$//m;
+ s/^\s+//gim;
+ s/\s+$/\n/gim;
+ s/\+$/\/rt3/gim;
+ # m4 will not let us just use $1, we need @S|@1
+ s/^\s*((?:bin|sbin|libexec|data|sysconf|sharedstate|localstate|lib|include|oldinclude|info|man|html)dir)\s*:\s*(.*)$/@S|@1=@S|@2/gim;
+ s/^\s*(.*?)\s*:\s*(.*)$/\(test "x\@S|@@S|@1" = "xNONE" || test "x\@S|@@S|@1" = "x") && @S|@1=@S|@2/gim;
+ ' < $1 > $pldconf
+
+ if test -s $pldconf; then
+ rt_layout_name=$2
+ . $pldconf
+ changequote({,})
+ for var in prefix exec_prefix bindir sbindir \
+ sysconfdir mandir libdir datadir htmldir \
+ localstatedir logfiledir masonstatedir \
+ sessionstatedir customdir custometcdir customhtmldir \
+ customlexdir customlibdir manualdir; do
+ eval "val=\"\$$var\""
+ val=`echo $val | sed -e 's:\(.\)/*$:\1:'`
+ val=`echo $val |
+ sed -e 's:[\$]\([a-z_]*\):${\1}:g'`
+ eval "$var='$val'"
+ done
+ changequote([,])
+ else
+ rt_layout_name=no
+ fi
+ #rm $pldconf
+ fi
+ RT_SUBST_EXPANDED_ARG(prefix)
+ RT_SUBST_EXPANDED_ARG(exec_prefix)
+ RT_SUBST_EXPANDED_ARG(bindir)
+ RT_SUBST_EXPANDED_ARG(sbindir)
+ RT_SUBST_EXPANDED_ARG(sysconfdir)
+ RT_SUBST_EXPANDED_ARG(mandir)
+ RT_SUBST_EXPANDED_ARG(libdir)
+ RT_SUBST_EXPANDED_ARG(datadir)
+ RT_SUBST_EXPANDED_ARG(htmldir)
+ RT_SUBST_EXPANDED_ARG(manualdir)
+ RT_SUBST_EXPANDED_ARG(localstatedir)
+ RT_SUBST_EXPANDED_ARG(logfiledir)
+ RT_SUBST_EXPANDED_ARG(masonstatedir)
+ RT_SUBST_EXPANDED_ARG(sessionstatedir)
+ RT_SUBST_EXPANDED_ARG(customdir)
+ RT_SUBST_EXPANDED_ARG(custometcdir)
+ RT_SUBST_EXPANDED_ARG(customhtmldir)
+ RT_SUBST_EXPANDED_ARG(customlexdir)
+ RT_SUBST_EXPANDED_ARG(customlibdir)
+])dnl
+
+dnl
+dnl @synopsis RT_SUBST_EXPANDED_ARG(var)
+dnl
+dnl Export (via AC_SUBST) a given variable, along with an expanded
+dnl version of the variable (same name, but with exp_ prefix).
+dnl
+dnl This code is heavily borrowed *cough* from the Apache 2 source.
+dnl
+
+AC_DEFUN([RT_SUBST_EXPANDED_ARG],[
+ RT_EXPAND_VAR(exp_$1, [$]$1)
+ AC_SUBST($1)
+ AC_SUBST(exp_$1)
+])
+
+dnl
+dnl @synopsis RT_EXPAND_VAR(baz, $fraz)
+dnl
+dnl Iteratively expands the second parameter, until successive iterations
+dnl yield no change. The result is then assigned to the first parameter.
+dnl
+dnl This code is heavily borrowed from the Apache 2 codebase.
+dnl
+
+AC_DEFUN([RT_EXPAND_VAR],[
+ ap_last=''
+ ap_cur='$2'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ $1="${ap_cur}"
+])
+
diff --git a/rt/autom4te.cache/output.0 b/rt/autom4te.cache/output.0
new file mode 100644
index 0000000..3d27db9
--- /dev/null
+++ b/rt/autom4te.cache/output.0
@@ -0,0 +1,2771 @@
+@%:@! /bin/sh
+@%:@ From configure.ac Revision: 1.1 .
+@%:@ Guess values for system-dependent variables and create Makefiles.
+@%:@ Generated by GNU Autoconf 2.53 for RT 3.0.9.
+@%:@
+@%:@ Report bugs to <rt-3.0-bugs@fsck.com>.
+@%:@
+@%:@ Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
+@%:@ Free Software Foundation, Inc.
+@%:@ This configure script is free software; the Free Software Foundation
+@%:@ gives unlimited permission to copy, distribute and modify it.
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+
+# NLS nuisances.
+# Support unset when possible.
+if (FOO=FOO; unset FOO) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+(set +x; test -n "`(LANG=C; export LANG) 2>&1`") &&
+ { $as_unset LANG || test "${LANG+set}" != set; } ||
+ { LANG=C; export LANG; }
+(set +x; test -n "`(LC_ALL=C; export LC_ALL) 2>&1`") &&
+ { $as_unset LC_ALL || test "${LC_ALL+set}" != set; } ||
+ { LC_ALL=C; export LC_ALL; }
+(set +x; test -n "`(LC_TIME=C; export LC_TIME) 2>&1`") &&
+ { $as_unset LC_TIME || test "${LC_TIME+set}" != set; } ||
+ { LC_TIME=C; export LC_TIME; }
+(set +x; test -n "`(LC_CTYPE=C; export LC_CTYPE) 2>&1`") &&
+ { $as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set; } ||
+ { LC_CTYPE=C; export LC_CTYPE; }
+(set +x; test -n "`(LANGUAGE=C; export LANGUAGE) 2>&1`") &&
+ { $as_unset LANGUAGE || test "${LANGUAGE+set}" != set; } ||
+ { LANGUAGE=C; export LANGUAGE; }
+(set +x; test -n "`(LC_COLLATE=C; export LC_COLLATE) 2>&1`") &&
+ { $as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set; } ||
+ { LC_COLLATE=C; export LC_COLLATE; }
+(set +x; test -n "`(LC_NUMERIC=C; export LC_NUMERIC) 2>&1`") &&
+ { $as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set; } ||
+ { LC_NUMERIC=C; export LC_NUMERIC; }
+(set +x; test -n "`(LC_MESSAGES=C; export LC_MESSAGES) 2>&1`") &&
+ { $as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set; } ||
+ { LC_MESSAGES=C; export LC_MESSAGES; }
+
+
+# Name of the executable.
+as_me=`(basename "$0") 2>/dev/null ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conftest.sh
+ echo "exit 0" >>conftest.sh
+ chmod +x conftest.sh
+ if (PATH=".;."; conftest.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conftest.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=$PATH_SEPARATOR; export CDPATH; }
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='RT'
+PACKAGE_TARNAME='rt'
+PACKAGE_VERSION='3.0.9'
+PACKAGE_STRING='RT 3.0.9'
+PACKAGE_BUGREPORT='rt-3.0-bugs@fsck.com'
+
+ac_unique_file="lib/RT.pm.in"
+ac_default_prefix=/opt/rt3
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_PERL_set=${PERL+set}
+ac_env_PERL_value=$PERL
+ac_cv_env_PERL_set=${PERL+set}
+ac_cv_env_PERL_value=$PERL
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures RT 3.0.9 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of RT 3.0.9:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-layout=LAYOUT Use a specific directory layout (Default: RT3)
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-speedycgi=/path/to/speedy
+ path to your speedycgi binary, if it exists
+ --with-rt-group=GROUP group to own all files (default: rt)
+ --with-bin-owner=OWNER user that will own rt binaries (default root)
+ --with-libs-owner=OWNER user that will own RT libraries (default root)
+ --with-libs-group=GROUP group that will own rt binaries (default bin)
+ --with-db-type=TYPE sort of database RT will use (default: mysql)
+ (mysql, Pg, Oracle and Informix are valid)
+ --with-db-host=HOSTNAME FQDN of database server (default: localhost)
+ --with-db-port=PORT port on which the database listens on
+ --with-db-rt-host=HOSTNAME
+ FQDN of RT server which talks to the database server
+ (default: localhost)
+ --with-db-dba=DBA name of database administrator (default: root)
+ --with-db-database=DBNAME
+ name of the database to use (default: rt3)
+ --with-db-rt-user=DBUSER
+ name of database user (default: rt_user)
+ --with-db-rt-pass=PASSWORD
+ password for database user (default: rt_pass)
+ --with-web-user=USER user the web server runs as (default: www)
+ --with-web-group=GROUP group the web server runs as (default: www)
+ --with-my-user-group set all users and groups to current user/group
+
+Some influential environment variables:
+ PERL Perl interpreter command
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <rt-3.0-bugs@fsck.com>.
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be
+# absolute.
+ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd`
+ac_abs_top_builddir=`cd "$ac_dir" && cd $ac_top_builddir && pwd`
+ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd`
+ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd`
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd $ac_popdir
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+RT configure 3.0.9
+generated by GNU Autoconf 2.53
+
+Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
+Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by RT $as_me 3.0.9, which was
+generated by GNU Autoconf 2.53. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+@%:@@%:@ --------- @%:@@%:@
+@%:@@%:@ Platform. @%:@@%:@
+@%:@@%:@ --------- @%:@@%:@
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+@%:@@%:@ ----------- @%:@@%:@
+@%:@@%:@ Core tests. @%:@@%:@
+@%:@@%:@ ----------- @%:@@%:@
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell meta-characters.
+ac_configure_args=
+ac_sep=
+for ac_arg
+do
+ case $ac_arg in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n ) continue ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ ac_sep=" " ;;
+ esac
+ # Get rid of the leading space.
+done
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+ cat <<\_ASBOX
+@%:@@%:@ ---------------- @%:@@%:@
+@%:@@%:@ Cache variables. @%:@@%:@
+@%:@@%:@ ---------------- @%:@@%:@
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+@%:@@%:@ ----------- @%:@@%:@
+@%:@@%:@ confdefs.h. @%:@@%:@
+@%:@@%:@ ----------- @%:@@%:@
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core core.* *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+rt_version_major=3
+
+rt_version_minor=0
+
+rt_version_patch=9
+
+test "x$rt_version_major" = 'x' && rt_version_major=0
+test "x$rt_version_minor" = 'x' && rt_version_minor=0
+test "x$rt_version_patch" = 'x' && rt_version_patch=0
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f $ac_dir/shtool; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL=$ac_install_sh
+ fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+# Extract the first word of "perl", so it can be a program name with args.
+set dummy perl; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_PERL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $PERL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PERL="$PERL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="not found"
+ ;;
+esac
+fi
+PERL=$ac_cv_path_PERL
+
+if test -n "$PERL"; then
+ echo "$as_me:$LINENO: result: $PERL" >&5
+echo "${ECHO_T}$PERL" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+if test "$PERL" = 'not found'; then
+ { { echo "$as_me:$LINENO: error: cannot use $PACKAGE_NAME without perl" >&5
+echo "$as_me: error: cannot use $PACKAGE_NAME without perl" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+# Check whether --with-speedycgi or --without-speedycgi was given.
+if test "${with_speedycgi+set}" = set; then
+ withval="$with_speedycgi"
+ SPEEDY_BIN=$withval
+else
+ SPEEDY_BIN=/usr/local/bin/speedy
+fi;
+
+
+
+
+
+# Check whether --enable-layout or --disable-layout was given.
+if test "${enable_layout+set}" = set; then
+ enableval="$enable_layout"
+ LAYOUT=$enableval
+fi;
+
+if test "x$LAYOUT" = "x"; then
+ LAYOUT="RT3"
+fi
+
+ if test ! -f $srcdir/config.layout; then
+ { echo "$as_me:$LINENO: WARNING: Layout file $srcdir/config.layout not found" >&5
+echo "$as_me: WARNING: Layout file $srcdir/config.layout not found" >&2;}
+ rt_layout_name=no
+ else
+ pldconf=./config.pld
+ $PERL -0777 -p -e "\$layout = '$LAYOUT';" -e '
+ s/.*<Layout\s+$layout>//gims;
+ s/\<\/Layout\>.*//s;
+ s/^#.*$//m;
+ s/^\s+//gim;
+ s/\s+$/\n/gim;
+ s/\+$/\/rt3/gim;
+ # m4 will not let us just use $srcdir/config.layout, we need @S|@1
+ s/^\s*((?:bin|sbin|libexec|data|sysconf|sharedstate|localstate|lib|include|oldinclude|info|man)dir)\s*:\s*(.*)$/@S|@1=@S|@2/gim;
+ s/^\s*(.*?)\s*:\s*(.*)$/\(test "x\@S|@@S|@1" = "xNONE" || test "x\@S|@@S|@1" = "x") && @S|@1=@S|@2/gim;
+ ' < $srcdir/config.layout > $pldconf
+
+ if test -s $pldconf; then
+ rt_layout_name=$LAYOUT
+ . $pldconf
+
+ for var in prefix exec_prefix bindir sbindir \
+ sysconfdir mandir libdir datadir htmldir \
+ localstatedir logfiledir masonstatedir \
+ sessionstatedir customdir custometcdir customhtmldir \
+ customlexdir customlibdir manualdir; do
+ eval "val=\"\$$var\""
+ val=`echo $val | sed -e 's:\(.\)/*$:\1:'`
+ val=`echo $val |
+ sed -e 's:[\$]\([a-z_]*\):$\1:g'`
+ eval "$var='$val'"
+ done
+
+ else
+ rt_layout_name=no
+ fi
+ #rm $pldconf
+ fi
+
+
+ ap_last=''
+ ap_cur='$prefix'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_prefix="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$exec_prefix'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_exec_prefix="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$bindir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_bindir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$sbindir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_sbindir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$sysconfdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_sysconfdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$mandir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_mandir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$libdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_libdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$datadir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_datadir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$htmldir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_htmldir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$manualdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_manualdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$localstatedir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_localstatedir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$logfiledir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_logfiledir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$masonstatedir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_masonstatedir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$sessionstatedir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_sessionstatedir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$custometcdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_custometcdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customhtmldir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customhtmldir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customlexdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customlexdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customlibdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customlibdir="${ap_cur}"
+
+
+
+
+
+echo "$as_me:$LINENO: checking for chosen layout" >&5
+echo $ECHO_N "checking for chosen layout... $ECHO_C" >&6
+if test "x$rt_layout_name" = "xno"; then
+ if test "x$LAYOUT" = "xno"; then
+ echo "$as_me:$LINENO: result: none" >&5
+echo "${ECHO_T}none" >&6
+ else
+ echo "$as_me:$LINENO: result: $LAYOUT" >&5
+echo "${ECHO_T}$LAYOUT" >&6
+ fi
+ { { echo "$as_me:$LINENO: error: a valid layout must be specified (or the default used)" >&5
+echo "$as_me: error: a valid layout must be specified (or the default used)" >&2;}
+ { (exit 1); exit 1; }; }
+else
+
+ echo "$as_me:$LINENO: result: $rt_layout_name" >&5
+echo "${ECHO_T}$rt_layout_name" >&6
+fi
+
+
+
+# Check whether --with-rt-group or --without-rt-group was given.
+if test "${with_rt_group+set}" = set; then
+ withval="$with_rt_group"
+ RTGROUP=$withval
+else
+ RTGROUP=rt
+fi;
+
+
+
+# Check whether --with-bin-owner or --without-bin-owner was given.
+if test "${with_bin_owner+set}" = set; then
+ withval="$with_bin_owner"
+ BIN_OWNER=$withval
+else
+ BIN_OWNER=root
+fi;
+
+
+
+# Check whether --with-libs-owner or --without-libs-owner was given.
+if test "${with_libs_owner+set}" = set; then
+ withval="$with_libs_owner"
+ LIBS_OWNER=$withval
+else
+ LIBS_OWNER=root
+fi;
+
+
+
+# Check whether --with-libs-group or --without-libs-group was given.
+if test "${with_libs_group+set}" = set; then
+ withval="$with_libs_group"
+ LIBS_GROUP=$withval
+else
+ LIBS_GROUP=bin
+fi;
+
+
+
+# Check whether --with-db-type or --without-db-type was given.
+if test "${with_db_type+set}" = set; then
+ withval="$with_db_type"
+ DB_TYPE=$withval
+else
+ DB_TYPE=mysql
+fi;
+if test "$DB_TYPE" != 'mysql' -a "$DB_TYPE" != 'Pg' -a "$DB_TYPE" != 'SQLite' -a "$DB_TYPE" != 'Oracle' -a "$DB_TYPE" != 'Informix' ; then
+ { { echo "$as_me:$LINENO: error: Only Oracle, Informix, Pg and mysql are valid db types" >&5
+echo "$as_me: error: Only Oracle, Informix, Pg and mysql are valid db types" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+if test "$DB_TYPE" = 'Oracle'; then
+ test "x$ORACLE_HOME" = 'x' && { { echo "$as_me:$LINENO: error: Please declare the ORACLE_HOME environment variable" >&5
+echo "$as_me: error: Please declare the ORACLE_HOME environment variable" >&2;}
+ { (exit 1); exit 1; }; }
+ ORACLE_ENV_PREF="\$ENV{'ORACLE_HOME'} = '$ORACLE_HOME';"
+fi
+
+
+
+# Check whether --with-db-host or --without-db-host was given.
+if test "${with_db_host+set}" = set; then
+ withval="$with_db_host"
+ DB_HOST=$withval
+else
+ DB_HOST=localhost
+fi;
+
+
+
+# Check whether --with-db-port or --without-db-port was given.
+if test "${with_db_port+set}" = set; then
+ withval="$with_db_port"
+ DB_PORT=$withval
+else
+ DB_PORT=
+fi;
+
+
+
+# Check whether --with-db-rt-host or --without-db-rt-host was given.
+if test "${with_db_rt_host+set}" = set; then
+ withval="$with_db_rt_host"
+ DB_RT_HOST=$withval
+else
+ DB_RT_HOST=localhost
+fi;
+
+
+
+# Check whether --with-db-dba or --without-db-dba was given.
+if test "${with_db_dba+set}" = set; then
+ withval="$with_db_dba"
+ DB_DBA=$withval
+else
+ DB_DBA=root
+fi;
+
+
+
+# Check whether --with-db-database or --without-db-database was given.
+if test "${with_db_database+set}" = set; then
+ withval="$with_db_database"
+ DB_DATABASE=$withval
+else
+ DB_DATABASE=rt3
+fi;
+
+
+
+# Check whether --with-db-rt-user or --without-db-rt-user was given.
+if test "${with_db_rt_user+set}" = set; then
+ withval="$with_db_rt_user"
+ DB_RT_USER=$withval
+else
+ DB_RT_USER=rt_user
+fi;
+
+
+
+# Check whether --with-db-rt-pass or --without-db-rt-pass was given.
+if test "${with_db_rt_pass+set}" = set; then
+ withval="$with_db_rt_pass"
+ DB_RT_PASS=$withval
+else
+ DB_RT_PASS=rt_pass
+fi;
+
+
+
+# Check whether --with-web-user or --without-web-user was given.
+if test "${with_web_user+set}" = set; then
+ withval="$with_web_user"
+ WEB_USER=$withval
+else
+ WEB_USER=www
+fi;
+
+
+
+# Check whether --with-web-group or --without-web-group was given.
+if test "${with_web_group+set}" = set; then
+ withval="$with_web_group"
+ WEB_GROUP=$withval
+else
+ WEB_GROUP=www
+fi;
+
+
+my_group=$(groups|cut -f1 -d' ')
+
+# Check whether --with-my-user-group or --without-my-user-group was given.
+if test "${with_my_user_group+set}" = set; then
+ withval="$with_my_user_group"
+ RTGROUP=$my_group
+ BIN_OWNER=$USER
+ LIBS_OWNER=$USER
+ LIBS_GROUP=$my_group
+ WEB_USER=$USER
+ WEB_GROUP=$my_group
+fi;
+
+
+RT_VERSION_MAJOR=${rt_version_major}
+
+RT_VERSION_MINOR=${rt_version_minor}
+
+RT_VERSION_PATCH=${rt_version_patch}
+
+
+RT_PATH=${exp_prefix}
+
+RT_DOC_PATH=${exp_manualdir}
+
+RT_LOCAL_PATH=${exp_customdir}
+
+RT_LIB_PATH=${exp_libdir}
+
+RT_ETC_PATH=${exp_sysconfdir}
+
+CONFIG_FILE_PATH=${exp_sysconfdir}
+
+RT_BIN_PATH=${exp_bindir}
+
+RT_SBIN_PATH=${exp_sbindir}
+
+RT_VAR_PATH=${exp_localstatedir}
+
+RT_MAN_PATH=${exp_mandir}
+
+MASON_DATA_PATH=${exp_masonstatedir}
+
+MASON_SESSION_PATH=${exp_sessionstatedir}
+
+MASON_HTML_PATH=${exp_htmldir}
+
+LOCAL_ETC_PATH=${exp_custometcdir}
+
+MASON_LOCAL_HTML_PATH=${exp_customhtmldir}
+
+LOCAL_LEXICON_PATH=${exp_customlexdir}
+
+LOCAL_LIB_PATH=${exp_customlibdir}
+
+DESTDIR=${exp_prefix}
+
+RT_LOG_PATH=${exp_logfiledir}
+
+
+
+ac_config_files="$ac_config_files sbin/rt-setup-database sbin/rt-test-dependencies Makefile etc/RT_Config.pm lib/RT.pm lib/t/00smoke.t lib/t/01harness.t lib/t/02regression.t lib/t/03web.pl lib/t/04_send_email.pl bin/mason_handler.fcgi bin/mason_handler.scgi bin/mason_handler.svc bin/rt-commit-handler bin/rt-crontool bin/rt-mailgate bin/rt bin/webmux.pl"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overriden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if cmp -s $cache_file confcache; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g
+t quote
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[ `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output. A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+
+# NLS nuisances.
+# Support unset when possible.
+if (FOO=FOO; unset FOO) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+(set +x; test -n "`(LANG=C; export LANG) 2>&1`") &&
+ { $as_unset LANG || test "${LANG+set}" != set; } ||
+ { LANG=C; export LANG; }
+(set +x; test -n "`(LC_ALL=C; export LC_ALL) 2>&1`") &&
+ { $as_unset LC_ALL || test "${LC_ALL+set}" != set; } ||
+ { LC_ALL=C; export LC_ALL; }
+(set +x; test -n "`(LC_TIME=C; export LC_TIME) 2>&1`") &&
+ { $as_unset LC_TIME || test "${LC_TIME+set}" != set; } ||
+ { LC_TIME=C; export LC_TIME; }
+(set +x; test -n "`(LC_CTYPE=C; export LC_CTYPE) 2>&1`") &&
+ { $as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set; } ||
+ { LC_CTYPE=C; export LC_CTYPE; }
+(set +x; test -n "`(LANGUAGE=C; export LANGUAGE) 2>&1`") &&
+ { $as_unset LANGUAGE || test "${LANGUAGE+set}" != set; } ||
+ { LANGUAGE=C; export LANGUAGE; }
+(set +x; test -n "`(LC_COLLATE=C; export LC_COLLATE) 2>&1`") &&
+ { $as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set; } ||
+ { LC_COLLATE=C; export LC_COLLATE; }
+(set +x; test -n "`(LC_NUMERIC=C; export LC_NUMERIC) 2>&1`") &&
+ { $as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set; } ||
+ { LC_NUMERIC=C; export LC_NUMERIC; }
+(set +x; test -n "`(LC_MESSAGES=C; export LC_MESSAGES) 2>&1`") &&
+ { $as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set; } ||
+ { LC_MESSAGES=C; export LC_MESSAGES; }
+
+
+# Name of the executable.
+as_me=`(basename "$0") 2>/dev/null ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conftest.sh
+ echo "exit 0" >>conftest.sh
+ chmod +x conftest.sh
+ if (PATH=".;."; conftest.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conftest.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=$PATH_SEPARATOR; export CDPATH; }
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../@%:@@%:@ /;s/...$/ @%:@@%:@/;p;x;p;x' <<_ASBOX
+@%:@@%:@ Running $as_me. @%:@@%:@
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by RT $as_me 3.0.9, which was
+generated by GNU Autoconf 2.53. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+RT config.status 3.0.9
+configured by $0, generated by GNU Autoconf 2.53,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ shift
+ set dummy "$ac_option" "$ac_optarg" ${1+"$@"}
+ shift
+ ;;
+ -*);;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_need_defaults=false;;
+ esac
+
+ case $1 in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running $SHELL $0 " $ac_configure_args " --no-create --no-recursion"
+ exec $SHELL $0 $ac_configure_args --no-create --no-recursion ;;
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ shift
+ CONFIG_FILES="$CONFIG_FILES $1"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $1"
+ ac_need_defaults=false;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "sbin/rt-setup-database" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-setup-database" ;;
+ "sbin/rt-test-dependencies" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-test-dependencies" ;;
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "etc/RT_Config.pm" ) CONFIG_FILES="$CONFIG_FILES etc/RT_Config.pm" ;;
+ "lib/RT.pm" ) CONFIG_FILES="$CONFIG_FILES lib/RT.pm" ;;
+ "lib/t/00smoke.t" ) CONFIG_FILES="$CONFIG_FILES lib/t/00smoke.t" ;;
+ "lib/t/01harness.t" ) CONFIG_FILES="$CONFIG_FILES lib/t/01harness.t" ;;
+ "lib/t/02regression.t" ) CONFIG_FILES="$CONFIG_FILES lib/t/02regression.t" ;;
+ "lib/t/03web.pl" ) CONFIG_FILES="$CONFIG_FILES lib/t/03web.pl" ;;
+ "lib/t/04_send_email.pl" ) CONFIG_FILES="$CONFIG_FILES lib/t/04_send_email.pl" ;;
+ "bin/mason_handler.fcgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.fcgi" ;;
+ "bin/mason_handler.scgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.scgi" ;;
+ "bin/mason_handler.svc" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.svc" ;;
+ "bin/rt-commit-handler" ) CONFIG_FILES="$CONFIG_FILES bin/rt-commit-handler" ;;
+ "bin/rt-crontool" ) CONFIG_FILES="$CONFIG_FILES bin/rt-crontool" ;;
+ "bin/rt-mailgate" ) CONFIG_FILES="$CONFIG_FILES bin/rt-mailgate" ;;
+ "bin/rt" ) CONFIG_FILES="$CONFIG_FILES bin/rt" ;;
+ "bin/webmux.pl" ) CONFIG_FILES="$CONFIG_FILES bin/webmux.pl" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+: ${TMPDIR=/tmp}
+{
+ tmp=`(umask 077 && mktemp -d -q "$TMPDIR/csXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=$TMPDIR/cs$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in $TMPDIR" >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@rt_version_major@,$rt_version_major,;t t
+s,@rt_version_minor@,$rt_version_minor,;t t
+s,@rt_version_patch@,$rt_version_patch,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@PERL@,$PERL,;t t
+s,@SPEEDY_BIN@,$SPEEDY_BIN,;t t
+s,@exp_prefix@,$exp_prefix,;t t
+s,@exp_exec_prefix@,$exp_exec_prefix,;t t
+s,@exp_bindir@,$exp_bindir,;t t
+s,@exp_sbindir@,$exp_sbindir,;t t
+s,@exp_sysconfdir@,$exp_sysconfdir,;t t
+s,@exp_mandir@,$exp_mandir,;t t
+s,@exp_libdir@,$exp_libdir,;t t
+s,@exp_datadir@,$exp_datadir,;t t
+s,@htmldir@,$htmldir,;t t
+s,@exp_htmldir@,$exp_htmldir,;t t
+s,@manualdir@,$manualdir,;t t
+s,@exp_manualdir@,$exp_manualdir,;t t
+s,@exp_localstatedir@,$exp_localstatedir,;t t
+s,@logfiledir@,$logfiledir,;t t
+s,@exp_logfiledir@,$exp_logfiledir,;t t
+s,@masonstatedir@,$masonstatedir,;t t
+s,@exp_masonstatedir@,$exp_masonstatedir,;t t
+s,@sessionstatedir@,$sessionstatedir,;t t
+s,@exp_sessionstatedir@,$exp_sessionstatedir,;t t
+s,@customdir@,$customdir,;t t
+s,@exp_customdir@,$exp_customdir,;t t
+s,@custometcdir@,$custometcdir,;t t
+s,@exp_custometcdir@,$exp_custometcdir,;t t
+s,@customhtmldir@,$customhtmldir,;t t
+s,@exp_customhtmldir@,$exp_customhtmldir,;t t
+s,@customlexdir@,$customlexdir,;t t
+s,@exp_customlexdir@,$exp_customlexdir,;t t
+s,@customlibdir@,$customlibdir,;t t
+s,@exp_customlibdir@,$exp_customlibdir,;t t
+s,@rt_layout_name@,$rt_layout_name,;t t
+s,@RTGROUP@,$RTGROUP,;t t
+s,@BIN_OWNER@,$BIN_OWNER,;t t
+s,@LIBS_OWNER@,$LIBS_OWNER,;t t
+s,@LIBS_GROUP@,$LIBS_GROUP,;t t
+s,@DB_TYPE@,$DB_TYPE,;t t
+s,@ORACLE_ENV_PREF@,$ORACLE_ENV_PREF,;t t
+s,@DB_HOST@,$DB_HOST,;t t
+s,@DB_PORT@,$DB_PORT,;t t
+s,@DB_RT_HOST@,$DB_RT_HOST,;t t
+s,@DB_DBA@,$DB_DBA,;t t
+s,@DB_DATABASE@,$DB_DATABASE,;t t
+s,@DB_RT_USER@,$DB_RT_USER,;t t
+s,@DB_RT_PASS@,$DB_RT_PASS,;t t
+s,@WEB_USER@,$WEB_USER,;t t
+s,@WEB_GROUP@,$WEB_GROUP,;t t
+s,@RT_VERSION_MAJOR@,$RT_VERSION_MAJOR,;t t
+s,@RT_VERSION_MINOR@,$RT_VERSION_MINOR,;t t
+s,@RT_VERSION_PATCH@,$RT_VERSION_PATCH,;t t
+s,@RT_PATH@,$RT_PATH,;t t
+s,@RT_DOC_PATH@,$RT_DOC_PATH,;t t
+s,@RT_LOCAL_PATH@,$RT_LOCAL_PATH,;t t
+s,@RT_LIB_PATH@,$RT_LIB_PATH,;t t
+s,@RT_ETC_PATH@,$RT_ETC_PATH,;t t
+s,@CONFIG_FILE_PATH@,$CONFIG_FILE_PATH,;t t
+s,@RT_BIN_PATH@,$RT_BIN_PATH,;t t
+s,@RT_SBIN_PATH@,$RT_SBIN_PATH,;t t
+s,@RT_VAR_PATH@,$RT_VAR_PATH,;t t
+s,@RT_MAN_PATH@,$RT_MAN_PATH,;t t
+s,@MASON_DATA_PATH@,$MASON_DATA_PATH,;t t
+s,@MASON_SESSION_PATH@,$MASON_SESSION_PATH,;t t
+s,@MASON_HTML_PATH@,$MASON_HTML_PATH,;t t
+s,@LOCAL_ETC_PATH@,$LOCAL_ETC_PATH,;t t
+s,@MASON_LOCAL_HTML_PATH@,$MASON_LOCAL_HTML_PATH,;t t
+s,@LOCAL_LEXICON_PATH@,$LOCAL_LEXICON_PATH,;t t
+s,@LOCAL_LIB_PATH@,$LOCAL_LIB_PATH,;t t
+s,@DESTDIR@,$DESTDIR,;t t
+s,@RT_LOG_PATH@,$RT_LOG_PATH,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { case "$ac_dir" in
+ [\\/]* | ?:[\\/]* ) as_incr_dir=;;
+ *) as_incr_dir=.;;
+esac
+as_dummy="$ac_dir"
+for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do
+ case $as_mkdir_dir in
+ # Skip DOS drivespec
+ ?:) as_incr_dir=$as_mkdir_dir ;;
+ *)
+ as_incr_dir=$as_incr_dir/$as_mkdir_dir
+ test -d "$as_incr_dir" ||
+ mkdir "$as_incr_dir" ||
+ { { echo "$as_me:$LINENO: error: cannot create \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ esac
+done; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be
+# absolute.
+ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd`
+ac_abs_top_builddir=`cd "$ac_dir" && cd $ac_top_builddir && pwd`
+ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd`
+ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd`
+
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+ esac
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo $f;;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo $f
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo $srcdir/$f
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
diff --git a/rt/autom4te.cache/requests b/rt/autom4te.cache/requests
new file mode 100644
index 0000000..fad7b54
--- /dev/null
+++ b/rt/autom4te.cache/requests
@@ -0,0 +1,94 @@
+# This file was created by autom4te.
+# It contains the lists of macros which have been traced.
+# It can be safely removed.
+
+@request = (
+ bless( [
+ '0',
+ 1,
+ [
+ '/usr/share/autoconf'
+ ],
+ [
+ '--reload-state=/usr/share/autoconf/autoconf/autoconf.m4f',
+ 'aclocal.m4',
+ 'configure.ac'
+ ],
+ {
+ 'AC_HEADER_STAT' => 1,
+ 'AC_FUNC_STRFTIME' => 1,
+ 'AC_PROG_RANLIB' => 1,
+ 'AC_FUNC_WAIT3' => 1,
+ 'AC_FUNC_SETPGRP' => 1,
+ 'AC_HEADER_TIME' => 1,
+ 'AC_FUNC_SETVBUF_REVERSED' => 1,
+ 'AC_HEADER_SYS_WAIT' => 1,
+ 'AC_TYPE_UID_T' => 1,
+ 'AM_CONDITIONAL' => 1,
+ 'AC_CHECK_LIB' => 1,
+ 'AC_PROG_LN_S' => 1,
+ 'AC_FUNC_MEMCMP' => 1,
+ 'AC_FUNC_FORK' => 1,
+ 'AC_FUNC_GETGROUPS' => 1,
+ 'AC_HEADER_MAJOR' => 1,
+ 'AC_FUNC_STRTOD' => 1,
+ 'AC_HEADER_DIRENT' => 1,
+ 'AC_FUNC_UTIME_NULL' => 1,
+ 'AC_CONFIG_FILES' => 1,
+ 'AC_FUNC_ALLOCA' => 1,
+ 'AC_C_CONST' => 1,
+ 'include' => 1,
+ 'AC_FUNC_OBSTACK' => 1,
+ 'AC_FUNC_LSTAT' => 1,
+ 'AC_STRUCT_TIMEZONE' => 1,
+ 'AC_FUNC_GETPGRP' => 1,
+ 'AC_DEFINE_TRACE_LITERAL' => 1,
+ 'AC_CHECK_HEADERS' => 1,
+ 'AC_TYPE_MODE_T' => 1,
+ 'AC_CHECK_TYPES' => 1,
+ 'AC_PROG_YACC' => 1,
+ 'AC_TYPE_PID_T' => 1,
+ 'AC_FUNC_STRERROR_R' => 1,
+ 'AC_STRUCT_ST_BLOCKS' => 1,
+ 'AC_PROG_GCC_TRADITIONAL' => 1,
+ 'AC_TYPE_SIGNAL' => 1,
+ 'AC_FUNC_FNMATCH' => 1,
+ 'AC_PROG_CPP' => 1,
+ 'AM_PROG_LIBTOOL' => 1,
+ 'AC_FUNC_STAT' => 1,
+ 'AC_PROG_INSTALL' => 1,
+ 'AM_GNU_GETTEXT' => 1,
+ 'AC_FUNC_STRCOLL' => 1,
+ 'AC_LIBSOURCE' => 1,
+ 'AC_C_INLINE' => 1,
+ 'AC_FUNC_CHOWN' => 1,
+ 'AC_PROG_LEX' => 1,
+ 'AH_OUTPUT' => 1,
+ 'AC_HEADER_STDC' => 1,
+ 'AC_FUNC_GETLOADAVG' => 1,
+ 'AC_CHECK_FUNCS' => 1,
+ 'AC_TYPE_SIZE_T' => 1,
+ 'AC_DECL_SYS_SIGLIST' => 1,
+ 'AC_FUNC_MKTIME' => 1,
+ 'AC_PROG_MAKE_SET' => 1,
+ 'AC_PROG_CXX' => 1,
+ 'm4_pattern_allow' => 1,
+ 'm4_include' => 1,
+ 'm4_pattern_forbid' => 1,
+ 'AC_PROG_AWK' => 1,
+ 'AC_FUNC_VPRINTF' => 1,
+ 'AC_CONFIG_HEADERS' => 1,
+ 'AC_PATH_X' => 1,
+ 'AC_TYPE_OFF_T' => 1,
+ 'AC_FUNC_MALLOC' => 1,
+ 'AC_FUNC_ERROR_AT_LINE' => 1,
+ 'AC_FUNC_FSEEKO' => 1,
+ 'AC_FUNC_MMAP' => 1,
+ 'AC_STRUCT_TM' => 1,
+ 'AC_SUBST' => 1,
+ 'AC_PROG_CC' => 1,
+ 'AC_PROG_LIBTOOL' => 1
+ }
+ ], 'Request' )
+ );
+
diff --git a/rt/autom4te.cache/traces.0 b/rt/autom4te.cache/traces.0
new file mode 100644
index 0000000..f132762
--- /dev/null
+++ b/rt/autom4te.cache/traces.0
@@ -0,0 +1,158 @@
+m4trace:configure.ac:9: -1- m4_pattern_forbid([^_?A[CHUM]_])
+m4trace:configure.ac:9: -1- m4_pattern_forbid([_AC_])
+m4trace:configure.ac:9: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs. LIBOBJS'])
+m4trace:configure.ac:9: -1- m4_pattern_allow([^AS_FLAGS$])
+m4trace:configure.ac:9: -1- m4_pattern_forbid([^_?m4_])
+m4trace:configure.ac:9: -1- m4_pattern_forbid([^dnl$])
+m4trace:configure.ac:9: -1- m4_pattern_forbid([^_?AS_])
+m4trace:configure.ac:9: -1- AC_SUBST([SHELL], [${CONFIG_SHELL-/bin/sh}])
+m4trace:configure.ac:9: -1- AC_SUBST([PATH_SEPARATOR])
+m4trace:configure.ac:9: -1- AC_SUBST([PACKAGE_NAME], [m4_ifdef([AC_PACKAGE_NAME], ['AC_PACKAGE_NAME'])])
+m4trace:configure.ac:9: -1- AC_SUBST([PACKAGE_TARNAME], [m4_ifdef([AC_PACKAGE_TARNAME], ['AC_PACKAGE_TARNAME'])])
+m4trace:configure.ac:9: -1- AC_SUBST([PACKAGE_VERSION], [m4_ifdef([AC_PACKAGE_VERSION], ['AC_PACKAGE_VERSION'])])
+m4trace:configure.ac:9: -1- AC_SUBST([PACKAGE_STRING], [m4_ifdef([AC_PACKAGE_STRING], ['AC_PACKAGE_STRING'])])
+m4trace:configure.ac:9: -1- AC_SUBST([PACKAGE_BUGREPORT], [m4_ifdef([AC_PACKAGE_BUGREPORT], ['AC_PACKAGE_BUGREPORT'])])
+m4trace:configure.ac:9: -1- AC_SUBST([exec_prefix], [NONE])
+m4trace:configure.ac:9: -1- AC_SUBST([prefix], [NONE])
+m4trace:configure.ac:9: -1- AC_SUBST([program_transform_name], [s,x,x,])
+m4trace:configure.ac:9: -1- AC_SUBST([bindir], ['${exec_prefix}/bin'])
+m4trace:configure.ac:9: -1- AC_SUBST([sbindir], ['${exec_prefix}/sbin'])
+m4trace:configure.ac:9: -1- AC_SUBST([libexecdir], ['${exec_prefix}/libexec'])
+m4trace:configure.ac:9: -1- AC_SUBST([datadir], ['${prefix}/share'])
+m4trace:configure.ac:9: -1- AC_SUBST([sysconfdir], ['${prefix}/etc'])
+m4trace:configure.ac:9: -1- AC_SUBST([sharedstatedir], ['${prefix}/com'])
+m4trace:configure.ac:9: -1- AC_SUBST([localstatedir], ['${prefix}/var'])
+m4trace:configure.ac:9: -1- AC_SUBST([libdir], ['${exec_prefix}/lib'])
+m4trace:configure.ac:9: -1- AC_SUBST([includedir], ['${prefix}/include'])
+m4trace:configure.ac:9: -1- AC_SUBST([oldincludedir], ['/usr/include'])
+m4trace:configure.ac:9: -1- AC_SUBST([infodir], ['${prefix}/info'])
+m4trace:configure.ac:9: -1- AC_SUBST([mandir], ['${prefix}/man'])
+m4trace:configure.ac:9: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_NAME])
+m4trace:configure.ac:9: -1- AH_OUTPUT([PACKAGE_NAME], [/* Define to the full name of this package. */
+#undef PACKAGE_NAME])
+m4trace:configure.ac:9: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_TARNAME])
+m4trace:configure.ac:9: -1- AH_OUTPUT([PACKAGE_TARNAME], [/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME])
+m4trace:configure.ac:9: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_VERSION])
+m4trace:configure.ac:9: -1- AH_OUTPUT([PACKAGE_VERSION], [/* Define to the version of this package. */
+#undef PACKAGE_VERSION])
+m4trace:configure.ac:9: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_STRING])
+m4trace:configure.ac:9: -1- AH_OUTPUT([PACKAGE_STRING], [/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING])
+m4trace:configure.ac:9: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_BUGREPORT])
+m4trace:configure.ac:9: -1- AH_OUTPUT([PACKAGE_BUGREPORT], [/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT])
+m4trace:configure.ac:9: -1- AC_SUBST([build_alias])
+m4trace:configure.ac:9: -1- AC_SUBST([host_alias])
+m4trace:configure.ac:9: -1- AC_SUBST([target_alias])
+m4trace:configure.ac:9: -1- AC_SUBST([DEFS])
+m4trace:configure.ac:9: -1- AC_SUBST([ECHO_C])
+m4trace:configure.ac:9: -1- AC_SUBST([ECHO_N])
+m4trace:configure.ac:9: -1- AC_SUBST([ECHO_T])
+m4trace:configure.ac:9: -1- AC_SUBST([LIBS])
+m4trace:configure.ac:14: -1- AC_SUBST([rt_version_major], [3])
+m4trace:configure.ac:16: -1- AC_SUBST([rt_version_minor], [0])
+m4trace:configure.ac:18: -1- AC_SUBST([rt_version_patch], [9])
+m4trace:configure.ac:24: -1- AC_PROG_INSTALL
+m4trace:configure.ac:24: -1- AC_SUBST([INSTALL_PROGRAM])
+m4trace:configure.ac:24: -1- AC_SUBST([INSTALL_SCRIPT])
+m4trace:configure.ac:24: -1- AC_SUBST([INSTALL_DATA])
+m4trace:configure.ac:25: -1- AC_SUBST([PERL])
+m4trace:configure.ac:26: -1- AC_SUBST([PERL], [$ac_cv_path_PERL])
+m4trace:configure.ac:36: -1- AC_SUBST([SPEEDY_BIN])
+m4trace:configure.ac:41: -1- AC_SUBST([prefix])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_prefix])
+m4trace:configure.ac:41: -1- AC_SUBST([exec_prefix])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_exec_prefix])
+m4trace:configure.ac:41: -1- AC_SUBST([bindir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_bindir])
+m4trace:configure.ac:41: -1- AC_SUBST([sbindir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_sbindir])
+m4trace:configure.ac:41: -1- AC_SUBST([sysconfdir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_sysconfdir])
+m4trace:configure.ac:41: -1- AC_SUBST([mandir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_mandir])
+m4trace:configure.ac:41: -1- AC_SUBST([libdir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_libdir])
+m4trace:configure.ac:41: -1- AC_SUBST([datadir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_datadir])
+m4trace:configure.ac:41: -1- AC_SUBST([htmldir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_htmldir])
+m4trace:configure.ac:41: -1- AC_SUBST([manualdir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_manualdir])
+m4trace:configure.ac:41: -1- AC_SUBST([localstatedir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_localstatedir])
+m4trace:configure.ac:41: -1- AC_SUBST([logfiledir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_logfiledir])
+m4trace:configure.ac:41: -1- AC_SUBST([masonstatedir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_masonstatedir])
+m4trace:configure.ac:41: -1- AC_SUBST([sessionstatedir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_sessionstatedir])
+m4trace:configure.ac:41: -1- AC_SUBST([customdir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_customdir])
+m4trace:configure.ac:41: -1- AC_SUBST([custometcdir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_custometcdir])
+m4trace:configure.ac:41: -1- AC_SUBST([customhtmldir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_customhtmldir])
+m4trace:configure.ac:41: -1- AC_SUBST([customlexdir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_customlexdir])
+m4trace:configure.ac:41: -1- AC_SUBST([customlibdir])
+m4trace:configure.ac:41: -1- AC_SUBST([exp_customlibdir])
+m4trace:configure.ac:41: -1- AC_SUBST([rt_layout_name])
+m4trace:configure.ac:49: -1- AC_SUBST([RTGROUP])
+m4trace:configure.ac:57: -1- AC_SUBST([BIN_OWNER])
+m4trace:configure.ac:65: -1- AC_SUBST([LIBS_OWNER])
+m4trace:configure.ac:73: -1- AC_SUBST([LIBS_GROUP])
+m4trace:configure.ac:84: -1- AC_SUBST([DB_TYPE])
+m4trace:configure.ac:91: -1- AC_SUBST([ORACLE_ENV_PREF])
+m4trace:configure.ac:99: -1- AC_SUBST([DB_HOST])
+m4trace:configure.ac:107: -1- AC_SUBST([DB_PORT])
+m4trace:configure.ac:115: -1- AC_SUBST([DB_RT_HOST])
+m4trace:configure.ac:123: -1- AC_SUBST([DB_DBA])
+m4trace:configure.ac:131: -1- AC_SUBST([DB_DATABASE])
+m4trace:configure.ac:139: -1- AC_SUBST([DB_RT_USER])
+m4trace:configure.ac:147: -1- AC_SUBST([DB_RT_PASS])
+m4trace:configure.ac:155: -1- AC_SUBST([WEB_USER])
+m4trace:configure.ac:163: -1- AC_SUBST([WEB_GROUP])
+m4trace:configure.ac:182: -1- AC_SUBST([RT_VERSION_MAJOR], [${rt_version_major}])
+m4trace:configure.ac:183: -1- AC_SUBST([RT_VERSION_MINOR], [${rt_version_minor}])
+m4trace:configure.ac:184: -1- AC_SUBST([RT_VERSION_PATCH], [${rt_version_patch}])
+m4trace:configure.ac:187: -1- AC_SUBST([RT_PATH], [${exp_prefix}])
+m4trace:configure.ac:188: -1- AC_SUBST([RT_DOC_PATH], [${exp_manualdir}])
+m4trace:configure.ac:189: -1- AC_SUBST([RT_LOCAL_PATH], [${exp_customdir}])
+m4trace:configure.ac:190: -1- AC_SUBST([RT_LIB_PATH], [${exp_libdir}])
+m4trace:configure.ac:191: -1- AC_SUBST([RT_ETC_PATH], [${exp_sysconfdir}])
+m4trace:configure.ac:192: -1- AC_SUBST([CONFIG_FILE_PATH], [${exp_sysconfdir}])
+m4trace:configure.ac:193: -1- AC_SUBST([RT_BIN_PATH], [${exp_bindir}])
+m4trace:configure.ac:194: -1- AC_SUBST([RT_SBIN_PATH], [${exp_sbindir}])
+m4trace:configure.ac:195: -1- AC_SUBST([RT_VAR_PATH], [${exp_localstatedir}])
+m4trace:configure.ac:196: -1- AC_SUBST([RT_MAN_PATH], [${exp_mandir}])
+m4trace:configure.ac:197: -1- AC_SUBST([MASON_DATA_PATH], [${exp_masonstatedir}])
+m4trace:configure.ac:198: -1- AC_SUBST([MASON_SESSION_PATH], [${exp_sessionstatedir}])
+m4trace:configure.ac:199: -1- AC_SUBST([MASON_HTML_PATH], [${exp_htmldir}])
+m4trace:configure.ac:200: -1- AC_SUBST([LOCAL_ETC_PATH], [${exp_custometcdir}])
+m4trace:configure.ac:201: -1- AC_SUBST([MASON_LOCAL_HTML_PATH], [${exp_customhtmldir}])
+m4trace:configure.ac:202: -1- AC_SUBST([LOCAL_LEXICON_PATH], [${exp_customlexdir}])
+m4trace:configure.ac:203: -1- AC_SUBST([LOCAL_LIB_PATH], [${exp_customlibdir}])
+m4trace:configure.ac:204: -1- AC_SUBST([DESTDIR], [${exp_prefix}])
+m4trace:configure.ac:205: -1- AC_SUBST([RT_LOG_PATH], [${exp_logfiledir}])
+m4trace:configure.ac:228: -1- AC_CONFIG_FILES([
+ sbin/rt-setup-database
+ sbin/rt-test-dependencies
+ Makefile
+ etc/RT_Config.pm
+ lib/RT.pm
+ lib/t/00smoke.t
+ lib/t/01harness.t
+ lib/t/02regression.t
+ lib/t/03web.pl
+ lib/t/04_send_email.pl
+ bin/mason_handler.fcgi
+ bin/mason_handler.scgi
+ bin/mason_handler.svc
+ bin/rt-commit-handler
+ bin/rt-crontool
+ bin/rt-mailgate
+ bin/rt
+ bin/webmux.pl
+ ])
diff --git a/rt/bin/mason_handler.fcgi b/rt/bin/mason_handler.fcgi
new file mode 100755
index 0000000..38f5901
--- /dev/null
+++ b/rt/bin/mason_handler.fcgi
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Mason;
+
+use strict;
+use vars '$Handler';
+use File::Basename;
+require ('/opt/rt3/bin/webmux.pl');
+
+# Enter CGI::Fast mode, which should also work as a vanilla CGI script.
+require CGI::Fast;
+
+RT::Init();
+
+while ( my $cgi = CGI::Fast->new ) {
+ # the whole point of fastcgi requires the env to get reset here..
+ # So we must squash it again
+ $ENV{'PATH'} = '/bin:/usr/bin';
+ $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
+ $ENV{'SHELL'} = '/bin/sh' if defined $ENV{'SHELL'};
+ $ENV{'ENV'} = '' if defined $ENV{'ENV'};
+ $ENV{'IFS'} = '' if defined $ENV{'IFS'};
+
+ Module::Refresh->refresh if $RT::DevelMode;
+ RT::ConnectToDatabase();
+
+ if ( ( !$Handler->interp->comp_exists( $cgi->path_info ) )
+ && ( $Handler->interp->comp_exists( $cgi->path_info . "/index.html" ) ) ) {
+ $cgi->path_info( $cgi->path_info . "/index.html" );
+ }
+
+ eval { $Handler->handle_cgi_object($cgi); };
+ if ($@) {
+ $RT::Logger->crit($@);
+ }
+ RT::Interface::Web::Handler->CleanupRequest();
+
+}
+
+1;
diff --git a/rt/bin/mason_handler.fcgi.in b/rt/bin/mason_handler.fcgi.in
new file mode 100644
index 0000000..aefe6d8
--- /dev/null
+++ b/rt/bin/mason_handler.fcgi.in
@@ -0,0 +1,86 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Mason;
+
+use strict;
+use vars '$Handler';
+use File::Basename;
+require ('@RT_BIN_PATH@/webmux.pl');
+
+# Enter CGI::Fast mode, which should also work as a vanilla CGI script.
+require CGI::Fast;
+
+RT::Init();
+
+while ( my $cgi = CGI::Fast->new ) {
+ # the whole point of fastcgi requires the env to get reset here..
+ # So we must squash it again
+ $ENV{'PATH'} = '/bin:/usr/bin';
+ $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
+ $ENV{'SHELL'} = '/bin/sh' if defined $ENV{'SHELL'};
+ $ENV{'ENV'} = '' if defined $ENV{'ENV'};
+ $ENV{'IFS'} = '' if defined $ENV{'IFS'};
+
+ Module::Refresh->refresh if $RT::DevelMode;
+ RT::ConnectToDatabase();
+
+ if ( ( !$Handler->interp->comp_exists( $cgi->path_info ) )
+ && ( $Handler->interp->comp_exists( $cgi->path_info . "/index.html" ) ) ) {
+ $cgi->path_info( $cgi->path_info . "/index.html" );
+ }
+
+ eval { $Handler->handle_cgi_object($cgi); };
+ if ($@) {
+ $RT::Logger->crit($@);
+ }
+ RT::Interface::Web::Handler->CleanupRequest();
+
+}
+
+1;
diff --git a/rt/bin/mason_handler.scgi b/rt/bin/mason_handler.scgi
new file mode 100755
index 0000000..faff8a5
--- /dev/null
+++ b/rt/bin/mason_handler.scgi
@@ -0,0 +1,67 @@
+#!/usr/local/bin/speedy
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Mason;
+
+use strict;
+use vars '$Handler';
+require ('/opt/rt3/bin/webmux.pl');
+
+require CGI;
+
+RT::Init();
+
+my $cgi = CGI->new;
+if ( ( !$Handler->interp->comp_exists( $cgi->path_info ) )
+ && ( $Handler->interp->comp_exists( $cgi->path_info . "/index.html" ) ) ) {
+ $cgi->path_info( $cgi->path_info . "/index.html" );
+}
+
+$Handler->handle_cgi_object($cgi);
+RT::Interface::Web::Handler->CleanupRequest();
+1;
diff --git a/rt/bin/mason_handler.scgi.in b/rt/bin/mason_handler.scgi.in
new file mode 100644
index 0000000..0af0ccd
--- /dev/null
+++ b/rt/bin/mason_handler.scgi.in
@@ -0,0 +1,67 @@
+#!@SPEEDY_BIN@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Mason;
+
+use strict;
+use vars '$Handler';
+require ('@RT_BIN_PATH@/webmux.pl');
+
+require CGI;
+
+RT::Init();
+
+my $cgi = CGI->new;
+if ( ( !$Handler->interp->comp_exists( $cgi->path_info ) )
+ && ( $Handler->interp->comp_exists( $cgi->path_info . "/index.html" ) ) ) {
+ $cgi->path_info( $cgi->path_info . "/index.html" );
+}
+
+$Handler->handle_cgi_object($cgi);
+RT::Interface::Web::Handler->CleanupRequest();
+1;
diff --git a/rt/bin/mason_handler.svc b/rt/bin/mason_handler.svc
new file mode 100644
index 0000000..fc97da9
--- /dev/null
+++ b/rt/bin/mason_handler.svc
@@ -0,0 +1,260 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+mason_handler.svc - Win32 IIS Service handler for RT
+
+=head1 SYNOPSIS
+
+ perl mason_handler.svc --install # install as service
+ perl mason_handler.svc --deinstall # deinstall this service
+ perl mason_handler.svc --help # show this help
+ perl mason_handler.svc # launch handler from command line
+
+=head1 DESCRIPTION
+
+This script manages a stand-alone FastCGI server, and populates the necessary
+registry settings to run it with Microsoft IIS Server 4.0 or above.
+
+Before running it, you need to install the B<FCGI> module from CPAN, as well as
+B<Win32::Daemon> from L<http://www.roth.net/perl/Daemon/> if you want to install
+itself as a service.
+
+This script will automatically create a virtual directory under the IIS root;
+its name is taken from C<$WebPath> in the F<RT_Config.pm> file. Additionally,
+please install the ISAPI binary from L<http://www.caraveo.com/fastcgi/> and set
+up an ISAPI Script Map that maps F<.html> files to F<isapi_fcgi.dll>.
+
+Once the service is launched (either via C<net start RTFastCGI> or by running
+C<perl mason_handler.svc>), a FCGI server will start and bind to port C<8284>
+(mnemonics: the ASCII value of C<R> and C<T>); the ISAPI handler's C<BindPath>
+registry setting will also be automatically populated.
+
+=cut
+
+package RT::Mason;
+
+use strict;
+use File::Basename;
+use vars '$Handler';
+require (dirname(__FILE__) . '/webmux.pl');
+
+use Cwd;
+use File::Spec;
+
+use Win32;
+use Win32::Process;
+use Win32::Service;
+use Win32::TieRegistry;
+
+my $ProcessObj;
+
+BEGIN {
+ my $runsvc = sub {
+ Win32::Process::Create(
+ $ProcessObj, $^X, "$^X $0 --run", 0, NORMAL_PRIORITY_CLASS, "."
+ ) or do {
+ die Win32::FormatMessage( Win32::GetLastError() );
+ };
+
+ chdir File::Basename::dirname($0);
+ my $path = Cwd::cwd();
+ $path =~ s|/|\\|g;
+ $path =~ s|bin$|share\\html|;
+
+ $Win32::TieRegistry::Registry->{
+ 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\\'.
+ 'W3SVC\Parameters\Virtual Roots\\'
+ }->{$RT::WebPath || '/'} = "$path,,205";
+
+ $Win32::TieRegistry::Registry->{
+ 'HKEY_LOCAL_MACHINE\Software\FASTCGI\.html\\'
+ }->{'BindPath'} = $ENV{'FCGI_SOCKET_PATH'};
+
+ Win32::Service::StartService(Win32::NodeName, 'W3SVC');
+ };
+
+ if ($ARGV[0] eq '--deinstall') {
+ chdir File::Basename::dirname($0);
+ my $path = Cwd::cwd();
+ $path =~ s|/|\\|g;
+
+ require Win32::Daemon;
+ Win32::Daemon::DeleteService('RTFastCGI');
+ warn "Service 'RTFastCGI' successfully deleted.\n";
+ exit;
+ }
+ elsif ($ARGV[0] eq '--install') {
+ chdir File::Basename::dirname($0);
+ my $path = Cwd::cwd();
+ $path =~ s|/|\\|g;
+
+ require Win32::Daemon;
+ Win32::Daemon::DeleteService('RTFastCGI');
+
+ my $rv = Win32::Daemon::CreateService( {
+ machine => '',
+ name => 'RTFastCGI',
+ display => 'RT FastCGI Handler',
+ path => $^X,
+ user => '',
+ pwd => $path,
+ description => 'Enables port 8284 as the RT FastCGI handler.',
+ parameters => File::Spec->catfile(
+ $path, File::Basename::basename($0)
+ ) . ' --service',
+ } );
+
+ if ($rv) {
+ warn "Service 'RTFastCGI' successfully created.\n";
+ }
+ else {
+ warn "Failed to add service: " . Win32::FormatMessage(
+ Win32::Daemon::GetLastError()
+ ) . "\n";
+ }
+ exit;
+ }
+ elsif ($ARGV[0] eq '--service') {
+ require Win32::Daemon;
+
+ my $PrevState = Win32::Daemon::SERVICE_START_PENDING();
+ Win32::Daemon::StartService() or die $^E;
+
+ while ( 1 ) {
+ my $State = Win32::Daemon::State();
+ last if $State == Win32::Daemon::SERVICE_STOPPED();
+
+ if ( $State == Win32::Daemon::SERVICE_START_PENDING() ) {
+ $runsvc->();
+ Win32::Daemon::State( Win32::Daemon::SERVICE_RUNNING() );
+ $PrevState = Win32::Daemon::SERVICE_RUNNING();
+ }
+ elsif ( $State == Win32::Daemon::SERVICE_CONTINUE_PENDING() ) {
+ $ProcessObj->Resume;
+ Win32::Daemon::State( Win32::Daemon::SERVICE_RUNNING() );
+ $PrevState = Win32::Daemon::SERVICE_RUNNING();
+ }
+ elsif ( $State == Win32::Daemon::SERVICE_STOP_PENDING() ) {
+ $ProcessObj->Kill(0);
+ Win32::Daemon::State( Win32::Daemon::SERVICE_STOPPED() );
+ $PrevState = Win32::Daemon::SERVICE_STOPPED();
+ }
+ elsif ( $State == Win32::Daemon::SERVICE_RUNNING() ) {
+ my $Message = Win32::Daemon::QueryLastMessage(1);
+ if ( $Message == Win32::Daemon::SERVICE_CONTROL_INTERROGATE() ) {
+ Win32::Daemon::State( $PrevState );
+ }
+ elsif ( $Message == Win32::Daemon::SERVICE_CONTROL_SHUTDOWN() ) {
+ Win32::Daemon::State( Win32::Daemon::SERVICE_STOP_PENDING(), 15000 );
+ }
+ elsif ( $Message != Win32::Daemon::SERVICE_CONTROL_NONE() ) {
+ Win32::Daemon::State( $PrevState );
+ }
+ }
+
+ Win32::Sleep( 1000 );
+ }
+
+ Win32::Daemon::StopService();
+ exit;
+ }
+ elsif ($ARGV[0] eq '--help') {
+ system("perldoc $0");
+ exit;
+ }
+ elsif ($ARGV[0] ne '--run') {
+ $SIG{__DIE__} = sub { $ProcessObj->Kill(0) if $ProcessObj };
+ $runsvc->();
+ warn "RT FastCGI Handler launched. Press [Enter] to terminate...\n";
+ <STDIN>;
+ exit;
+ }
+}
+
+###############################################################################
+
+warn "Begin listening on $ENV{'FCGI_SOCKET_PATH'}\n";
+
+require CGI::Fast;
+
+RT::Init();
+
+# Response loop
+while( my $cgi = CGI::Fast->new ) {
+ my $comp = $ENV{'PATH_INFO'};
+
+ $comp = $1 if ($comp =~ /^(.*)$/);
+ $comp =~ s|^$RT::WebPath\b||i;
+ $comp .= "index.html" if ($comp =~ /\/$/);
+ $comp =~ s/.pl$/.html/g;
+
+ warn "Serving $comp\n";
+
+ $Handler->handle_cgi($comp);
+ RT::Interface::Web::Handler->CleanupRequest();
+ # _should_ always be tied
+}
+
+1;
+
+=head1 AUTHORS
+
+Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002 by Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
diff --git a/rt/bin/mason_handler.svc.in b/rt/bin/mason_handler.svc.in
new file mode 100644
index 0000000..bda998a
--- /dev/null
+++ b/rt/bin/mason_handler.svc.in
@@ -0,0 +1,260 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+mason_handler.svc - Win32 IIS Service handler for RT
+
+=head1 SYNOPSIS
+
+ perl mason_handler.svc --install # install as service
+ perl mason_handler.svc --deinstall # deinstall this service
+ perl mason_handler.svc --help # show this help
+ perl mason_handler.svc # launch handler from command line
+
+=head1 DESCRIPTION
+
+This script manages a stand-alone FastCGI server, and populates the necessary
+registry settings to run it with Microsoft IIS Server 4.0 or above.
+
+Before running it, you need to install the B<FCGI> module from CPAN, as well as
+B<Win32::Daemon> from L<http://www.roth.net/perl/Daemon/> if you want to install
+itself as a service.
+
+This script will automatically create a virtual directory under the IIS root;
+its name is taken from C<$WebPath> in the F<RT_Config.pm> file. Additionally,
+please install the ISAPI binary from L<http://www.caraveo.com/fastcgi/> and set
+up an ISAPI Script Map that maps F<.html> files to F<isapi_fcgi.dll>.
+
+Once the service is launched (either via C<net start RTFastCGI> or by running
+C<perl mason_handler.svc>), a FCGI server will start and bind to port C<8284>
+(mnemonics: the ASCII value of C<R> and C<T>); the ISAPI handler's C<BindPath>
+registry setting will also be automatically populated.
+
+=cut
+
+package RT::Mason;
+
+use strict;
+use File::Basename;
+use vars '$Handler';
+require (dirname(__FILE__) . '/webmux.pl');
+
+use Cwd;
+use File::Spec;
+
+use Win32;
+use Win32::Process;
+use Win32::Service;
+use Win32::TieRegistry;
+
+my $ProcessObj;
+
+BEGIN {
+ my $runsvc = sub {
+ Win32::Process::Create(
+ $ProcessObj, $^X, "$^X $0 --run", 0, NORMAL_PRIORITY_CLASS, "."
+ ) or do {
+ die Win32::FormatMessage( Win32::GetLastError() );
+ };
+
+ chdir File::Basename::dirname($0);
+ my $path = Cwd::cwd();
+ $path =~ s|/|\\|g;
+ $path =~ s|bin$|share\\html|;
+
+ $Win32::TieRegistry::Registry->{
+ 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\\'.
+ 'W3SVC\Parameters\Virtual Roots\\'
+ }->{$RT::WebPath || '/'} = "$path,,205";
+
+ $Win32::TieRegistry::Registry->{
+ 'HKEY_LOCAL_MACHINE\Software\FASTCGI\.html\\'
+ }->{'BindPath'} = $ENV{'FCGI_SOCKET_PATH'};
+
+ Win32::Service::StartService(Win32::NodeName, 'W3SVC');
+ };
+
+ if ($ARGV[0] eq '--deinstall') {
+ chdir File::Basename::dirname($0);
+ my $path = Cwd::cwd();
+ $path =~ s|/|\\|g;
+
+ require Win32::Daemon;
+ Win32::Daemon::DeleteService('RTFastCGI');
+ warn "Service 'RTFastCGI' successfully deleted.\n";
+ exit;
+ }
+ elsif ($ARGV[0] eq '--install') {
+ chdir File::Basename::dirname($0);
+ my $path = Cwd::cwd();
+ $path =~ s|/|\\|g;
+
+ require Win32::Daemon;
+ Win32::Daemon::DeleteService('RTFastCGI');
+
+ my $rv = Win32::Daemon::CreateService( {
+ machine => '',
+ name => 'RTFastCGI',
+ display => 'RT FastCGI Handler',
+ path => $^X,
+ user => '',
+ pwd => $path,
+ description => 'Enables port 8284 as the RT FastCGI handler.',
+ parameters => File::Spec->catfile(
+ $path, File::Basename::basename($0)
+ ) . ' --service',
+ } );
+
+ if ($rv) {
+ warn "Service 'RTFastCGI' successfully created.\n";
+ }
+ else {
+ warn "Failed to add service: " . Win32::FormatMessage(
+ Win32::Daemon::GetLastError()
+ ) . "\n";
+ }
+ exit;
+ }
+ elsif ($ARGV[0] eq '--service') {
+ require Win32::Daemon;
+
+ my $PrevState = Win32::Daemon::SERVICE_START_PENDING();
+ Win32::Daemon::StartService() or die $^E;
+
+ while ( 1 ) {
+ my $State = Win32::Daemon::State();
+ last if $State == Win32::Daemon::SERVICE_STOPPED();
+
+ if ( $State == Win32::Daemon::SERVICE_START_PENDING() ) {
+ $runsvc->();
+ Win32::Daemon::State( Win32::Daemon::SERVICE_RUNNING() );
+ $PrevState = Win32::Daemon::SERVICE_RUNNING();
+ }
+ elsif ( $State == Win32::Daemon::SERVICE_CONTINUE_PENDING() ) {
+ $ProcessObj->Resume;
+ Win32::Daemon::State( Win32::Daemon::SERVICE_RUNNING() );
+ $PrevState = Win32::Daemon::SERVICE_RUNNING();
+ }
+ elsif ( $State == Win32::Daemon::SERVICE_STOP_PENDING() ) {
+ $ProcessObj->Kill(0);
+ Win32::Daemon::State( Win32::Daemon::SERVICE_STOPPED() );
+ $PrevState = Win32::Daemon::SERVICE_STOPPED();
+ }
+ elsif ( $State == Win32::Daemon::SERVICE_RUNNING() ) {
+ my $Message = Win32::Daemon::QueryLastMessage(1);
+ if ( $Message == Win32::Daemon::SERVICE_CONTROL_INTERROGATE() ) {
+ Win32::Daemon::State( $PrevState );
+ }
+ elsif ( $Message == Win32::Daemon::SERVICE_CONTROL_SHUTDOWN() ) {
+ Win32::Daemon::State( Win32::Daemon::SERVICE_STOP_PENDING(), 15000 );
+ }
+ elsif ( $Message != Win32::Daemon::SERVICE_CONTROL_NONE() ) {
+ Win32::Daemon::State( $PrevState );
+ }
+ }
+
+ Win32::Sleep( 1000 );
+ }
+
+ Win32::Daemon::StopService();
+ exit;
+ }
+ elsif ($ARGV[0] eq '--help') {
+ system("perldoc $0");
+ exit;
+ }
+ elsif ($ARGV[0] ne '--run') {
+ $SIG{__DIE__} = sub { $ProcessObj->Kill(0) if $ProcessObj };
+ $runsvc->();
+ warn "RT FastCGI Handler launched. Press [Enter] to terminate...\n";
+ <STDIN>;
+ exit;
+ }
+}
+
+###############################################################################
+
+warn "Begin listening on $ENV{'FCGI_SOCKET_PATH'}\n";
+
+require CGI::Fast;
+
+RT::Init();
+
+# Response loop
+while( my $cgi = CGI::Fast->new ) {
+ my $comp = $ENV{'PATH_INFO'};
+
+ $comp = $1 if ($comp =~ /^(.*)$/);
+ $comp =~ s|^$RT::WebPath\b||i;
+ $comp .= "index.html" if ($comp =~ /\/$/);
+ $comp =~ s/.pl$/.html/g;
+
+ warn "Serving $comp\n";
+
+ $Handler->handle_cgi($comp);
+ RT::Interface::Web::Handler->CleanupRequest();
+ # _should_ always be tied
+}
+
+1;
+
+=head1 AUTHORS
+
+Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002 by Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
diff --git a/rt/bin/rt-commit-handler b/rt/bin/rt-commit-handler
new file mode 100644
index 0000000..bf23a6c
--- /dev/null
+++ b/rt/bin/rt-commit-handler
@@ -0,0 +1,846 @@
+#!/usr/bin/perl -w
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+
+# {{{ Docs
+# -*-Perl-*-
+#
+#ident "@(#)ccvs/contrib:$Name: $:$Id: rt-commit-handler,v 1.2 2007-08-01 22:20:32 ivan Exp $"
+#
+# Perl filter to handle the log messages from the checkin of files in multiple
+# directories. This script will group the lists of files by log message, and
+# send one piece of mail per unique message, no matter how many files are
+# committed.
+
+=head1 NAME rt-commit-handler
+
+=head1 USAGE
+
+
+
+=head2 Regular use
+
+Stick the following in in CVSROOT/commitinfo
+
+ ALL /opt/rt3/bin/rt-commit-handler --record-last-dir
+
+Stick the following in CVSROOT/loginfo
+
+ ALL /opt/rt3/bin/rt-commit-handler --cvs-root /pathtocvs/root --rt %{Vvts}
+
+=head2 Invocation (advanced use)
+
+rt-commit-handler --cvs-root /path/to/cvs/root [-d] [-D] [-r] [-M module] \
+ [[-m mailto] ...] [[-R replyto] ...] [-f logfile]
+
+
+ -d - turn on debugging
+ -m mailto - send mail to "mailto" (multiple)
+ -R replyto - set the "Reply-To:" to "replyto" (multiple)
+ -M modulename - set module name to "modulename"
+ -f logfile - write commit messages to logfile too
+ -D - generate diff commands
+ --rt - invoke RT commit handler
+ --cvs-root - specify your CVS root
+
+ --record-last-dir - Record the last directory with changes in
+ pre-commit (commitinfo) mode
+
+
+=cut
+
+# }}}
+
+use strict;
+use Carp;
+use Getopt::Long;
+use Text::Wrap;
+use Digest::MD5;
+use MIME::Entity;
+
+use lib ("/opt/rt3/lib", "/opt/rt3/local/lib");
+
+use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
+
+use vars
+ qw(@MAILER $TMPDIR $FILE_PREFIX $LASTDIR_FILE $HASH_FILE $VERSION_FILE $MESSAGE_FILE $MAIL_FILE $DEBUG $MAILTO $REPLYTO $id $MODULE_NAME
+ $LOGIN $COMMITLOG $CVS_ROOT $RT_HANDLER);
+
+#Clean out all the nasties from the environment
+CleanEnv();
+
+#Load etc/config.pm and drop privs
+RT::LoadConfig();
+
+#Drop setgid permissions
+RT::DropSetGIDPermissions();
+
+# {{{ Variable setup
+$TMPDIR = '/tmp';
+$FILE_PREFIX = $TMPDIR . '/#cvs.';
+
+# The root of your CVS install. we should get this from some smarter place.
+# It needs a trailing /
+
+$LASTDIR_FILE = $FILE_PREFIX . "lastdir";
+$HASH_FILE = $FILE_PREFIX . "hash";
+$VERSION_FILE = $FILE_PREFIX . "version";
+$MESSAGE_FILE = $FILE_PREFIX . "message";
+$MAIL_FILE = $FILE_PREFIX . "mail";
+
+$DEBUG = 0;
+$RT_HANDLER = 1;
+
+$MAILTO = '';
+
+my @files = ();
+my (@log_lines);
+my $do_diff = 0;
+my $id = getpgrp(); # note, you *must* use a shell which does setpgrp()
+$LOGIN = getpwuid($<);
+
+# }}}
+
+die "User could not be found" unless ($LOGIN);
+
+# {{{ parse command line arguments (file list is seen as one arg)
+#
+while ( my $arg = shift @ARGV ) {
+
+ if ( $arg eq '-d' ) {
+ $DEBUG = 1;
+ warn "Debug turned on...\n";
+ }
+ elsif ( $arg =~ /^--record-last-dir$/i ) {
+ record_last_dir( $id, $ARGV[0] );
+ exit(0);
+ }
+ elsif ( $arg eq '-m' ) {
+ $MAILTO .= ", " if $MAILTO;
+ $MAILTO .= shift @ARGV;
+ }
+ elsif ( $arg eq '--rt' ) {
+ $RT_HANDLER = 1;
+ }
+ elsif ( $arg eq '-R' ) {
+ $REPLYTO .= ", " if $REPLYTO;
+ $REPLYTO .= shift @ARGV;
+ }
+ elsif ( $arg eq '-M' ) {
+ die ("too many '-M' args\n") if $MODULE_NAME;
+ $MODULE_NAME = shift @ARGV;
+ }
+ elsif ( $arg eq '--cvs-root' ) {
+ $CVS_ROOT = shift @ARGV;
+ $CVS_ROOT .= "/" unless ( $CVS_ROOT =~ /\/$/ );
+ }
+ elsif ( $arg eq '-f' ) {
+ die ("too many '-f' args\n") if $COMMITLOG;
+ $COMMITLOG = shift @ARGV;
+
+ # This is a disgusting hack to untaint $COMMITLOG if we're running from
+ # setgid cvs.
+ $COMMITLOG = untaint($COMMITLOG);
+ }
+ elsif ( $arg eq '-D' ) {
+ $do_diff = 1;
+ }
+ else {
+ @files = split ( ' ', $arg );
+ last;
+ }
+}
+
+# }}}
+
+$REPLYTO = $LOGIN unless ($REPLYTO);
+
+# for now, the first "file" is the repository directory being committed,
+# relative to the $CVSROOT location
+#
+my $dir = shift @files;
+
+# XXX there are some ugly assumptions in here about module names and
+# XXX directories relative to the $CVSROOT location -- really should
+# XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since
+# XXX we have to parse it backwards.
+#
+# XXX For now we set the `module' name to the top-level directory name.
+#
+unless ($MODULE_NAME) {
+ ($MODULE_NAME) = split ( '/', $dir, 2 );
+}
+
+if ($DEBUG) {
+ warn "module - ", $MODULE_NAME, "\n";
+ warn "dir - ", $dir, "\n";
+ warn "files - ", join ( " ", @files ), "\n";
+ warn "id - ", $id, "\n";
+}
+
+# {{{ Check for a new directory or an import command.
+
+#
+# files[0] - "-"
+# files[1] - "New"
+# files[2] - "directory"
+#
+# files[0] - "-"
+# files[1] - "Imported"
+# files[2] - "sources"
+#
+if ( $files[0] eq "-" ) {
+
+ #we just don't care about New Directory notes
+ unless ( $files[1] eq "New" && $files[2] eq "directory" ) {
+
+ my @text = ();
+
+ push @text, build_header();
+ push @text, "";
+
+ while ( my $line = <STDIN> ) {
+ chop $line; # Drop the newline
+ push @text, $line;
+ }
+
+ append_logfile( $COMMITLOG, @text ) if ($COMMITLOG);
+
+ mail_notification( $id, @text );
+ }
+
+ exit 0;
+}
+
+# }}}
+
+# {{{ Collect just the log message from stdin.
+#
+
+while ( my $line = <STDIN> ) {
+ chop $line; # strip the newline
+ last if ( $line =~ /^Log Message:$/ );
+}
+while ( my $line = <STDIN> ) {
+ chop $line; # strip the newline
+ $line =~ s/\s+$//; # strip trailing white space
+ push @log_lines, $line;
+}
+
+my $md5 = Digest::MD5->new();
+foreach my $line (@log_lines) {
+ $md5->add( $line . "\n" );
+}
+my $hash = $md5->hexdigest();
+
+warn "hash = $hash\n" if ($DEBUG);
+
+if ( !-e "$MESSAGE_FILE.$id.$hash" ) {
+ append_logfile( "$HASH_FILE.$id", $hash );
+ write_file( "$MESSAGE_FILE.$id.$hash", @log_lines );
+}
+
+# }}}
+
+# Spit out the information gathered in this pass.
+
+append_logfile( "$VERSION_FILE.$id.$hash", $dir . '/', @files );
+
+# {{{ Check whether this is the last directory. If not, quit.
+
+warn "Checking current dir against last dir $LASTDIR_FILE.$id\n" if ($DEBUG);
+
+my @last_dir = read_file("$LASTDIR_FILE.$id");
+
+unless ($CVS_ROOT) {
+ die "No cvs root specified with --cvs-root. Can't continue.";
+}
+
+if ( $last_dir[0] ne $CVS_ROOT . $dir ) {
+ warn "Current directory $CVS_ROOT$dir is not last directory $last_dir[0].\n"
+ if ($DEBUG);
+ exit 0;
+}
+
+# }}}
+
+# {{{ End Of Commits!
+#
+
+# This is it. The commits are all finished. Lump everything together
+# into a single message, fire a copy off to the mailing list, and drop
+# it on the end of the Changes file.
+#
+
+#
+# Produce the final compilation of the log messages
+#
+
+my @hashes = read_file("$HASH_FILE.$id");
+my (@text);
+
+push @text, build_header();
+push @text, "";
+
+my ( @added_files, @modified_files, @removed_files );
+
+foreach my $hash (@hashes) {
+
+ # In case we're running setgid, make sure the hash file hasn't been hacked.
+ $hash =~ m/([a-z0-9]*)/ || die "*** Hacking attempt detected\n";
+ $hash = $1;
+
+ my @files = read_file("$VERSION_FILE.$id.$hash");
+ my @log_lines = read_file("$MESSAGE_FILE.$id.$hash");
+
+ my $working_on_dir; # gets set as we iterate through the files.
+ foreach my $file (@files) {
+
+ #If we've entered a new directory, make a note of that and remove the trailing /
+
+ if ( $file =~ s'\/$'' ) {
+ $working_on_dir = $file;
+ next;
+ }
+
+ my @file_entry = ( split ( ',', $file, 4 ), $working_on_dir );
+
+ # file_entry looks like ths:
+
+ # 0 1 2 3 4
+ # Old rev : new rev : tag: file :directory
+ my $entry = {};
+ $entry->{'old'} = $file_entry[0];
+ $entry->{'new'} = $file_entry[1];
+ $entry->{'tag'} = $file_entry[2];
+ $entry->{'file'} = $file_entry[3];
+ $entry->{'dir'} = $file_entry[4];
+
+ if ( $file_entry[0] eq 'NONE' ) {
+ $entry->{'old'} = '0';
+ push @added_files, $entry;
+ }
+ elsif ( $file_entry[1] eq 'NONE' ) {
+ $entry->{'new'} = '0';
+ push @removed_files, $entry;
+ }
+ else {
+ push @modified_files, $entry;
+ }
+ }
+}
+
+# }}}
+
+# {{{ start building up the body
+
+# Strip leading and trailing blank lines from the log message. Also
+# compress multiple blank lines in the body of the message down to a
+# single blank line.
+#
+
+my $blank = 1;
+@log_lines = map {
+ my $wasblank = $blank;
+ $blank = $_ eq '';
+ $blank && $wasblank ? () : $_;
+} @log_lines;
+
+pop @log_lines if $blank;
+
+@modified_files = order_and_summarize_diffs(@modified_files);
+@added_files = order_and_summarize_diffs(@added_files);
+@removed_files = order_and_summarize_diffs(@removed_files);
+
+push @text, "Modified Files:", format_lists(@modified_files)
+ if (@modified_files);
+
+push @text, "Added Files:", format_lists(@added_files) if (@added_files);
+
+push @text, "Removed Files:", format_lists(@removed_files) if (@removed_files);
+
+push @text, "", "Log Message", @log_lines if (@log_lines);
+
+push @text, "";
+
+if ($RT_HANDLER) {
+ rt_handler(
+ @log_lines, "\n",
+ loc("To generate a diff of this commit:\n"), "\n",
+ format_diffs( @modified_files, @added_files, @removed_files )
+ );
+}
+
+if ($COMMITLOG) {
+ append_logfile( $COMMITLOG, @text );
+}
+
+if ($do_diff) {
+ push @text, "";
+ push @text, loc("To generate a diff of this commit:");
+ push @text, format_diffs( @modified_files, @added_files, @removed_files );
+ push @text, "";
+}
+
+# }}}
+
+# {{{ Mail out the notification.
+
+mail_notification( $id, @text );
+
+# }}}
+
+# {{{ clean up
+
+unless ($DEBUG) {
+ $hash = untaint($hash);
+ $id = untaint($id);
+ unlink "$VERSION_FILE.$id.$hash";
+ unlink "$MESSAGE_FILE.$id.$hash";
+ unlink "$MAIL_FILE.$id";
+ unlink "$LASTDIR_FILE.$id";
+ unlink "$HASH_FILE.$id";
+}
+
+# }}}
+
+exit 0;
+
+# {{{ Subroutines
+#
+
+# {{{ append_logfile
+sub append_logfile {
+ my $filename = shift;
+ my (@lines) = @_;
+
+ $filename = untaint($filename);
+
+ open( FILE, ">>$filename" )
+ || die ("Cannot open file $filename for append.\n");
+ foreach my $line (@lines) {
+ print FILE $line . "\n";
+ }
+ close(FILE);
+}
+
+# }}}
+
+# {{{ write_file
+sub write_file {
+ my $filename = shift;
+ my (@lines) = @_;
+
+ $filename = untaint($filename);
+
+ open( FILE, ">$filename" )
+ || die ("Cannot open file $filename for write.\n");
+ foreach my $line (@lines) {
+ print FILE $line . "\n";
+ }
+ close(FILE);
+}
+
+# }}}
+
+# {{{ read_file
+sub read_file {
+ my $filename = shift;
+ my (@lines);
+
+ open( FILE, "<$filename" )
+ || die ("Cannot open file $filename for read.\n");
+ while ( my $line = <FILE> ) {
+ chop $line;
+ push @lines, $line;
+ }
+ close(FILE);
+
+ return (@lines);
+}
+
+# }}}
+
+# {{{ sub format_lists
+
+sub format_lists {
+ my @items = (@_);
+
+ my $files = "";
+ map {
+ $_->{'files'} && ( $files .= ' ' . join ( ' ', @{ $_->{'files'} } ) );
+ } @items;
+
+ my @lines = wrap( "\t", "\t\t", $files );
+ return (@lines);
+
+}
+
+# }}}
+
+# {{{ sub format_diffs
+
+sub format_diffs {
+ my @items = (@_);
+
+ my @lines;
+ foreach my $item (@items) {
+ next unless ( $item->{'files'} );
+ push ( @lines,
+ "cvs diff -r"
+ . $item->{'old'} . " -r"
+ . $item->{'new'} . " "
+ . join ( " ", @{ $item->{'files'} } ) . "\n" );
+
+ }
+
+ @lines = fill( "\t", "\t\t", @lines );
+
+ return (@lines);
+}
+
+# }}}
+
+# {{{ sub order_and_summarize_diffs {
+
+# takes an array of file items
+# returns a sorted array of fileset items, which are like file items, except they can have an array of files, rather than
+# a singleton file.
+
+sub order_and_summarize_diffs {
+
+ my @files = (@_);
+
+ # Sort by tag, dir, file.
+ @files = sort {
+ $a->{'tag'} cmp $b->{'tag'}
+ || $a->{'dir'} cmp $b->{'dir'}
+ || $a->{'file'} cmp $b->{'file'};
+ } @files;
+
+ # Combine adjacent rows that are the same modulo the file name.
+
+ my @items = (undef);
+
+ foreach my $file (@files) {
+ if ( $#items == -1 #if it's empty
+ || ( !defined $items[-1]->{'old'}
+ || $items[-1]->{'old'} ne $file->{'old'} )
+ || ( !defined $items[-1]->{'new'}
+ || $items[-1]->{'new'} ne $file->{'new'} )
+ || ( !defined $items[-1]->{'tag'}
+ || $items[-1]->{'tag'} ne $file->{'tag'} ) )
+ {
+
+ push ( @items, $file );
+ }
+ push ( @{ $items[-1]->{'files'} },
+ $file->{'dir'} . "/" . $file->{'file'} );
+ }
+
+ return (@items);
+}
+
+# }}}
+
+# {{{ build_header
+
+sub build_header {
+ my $now = gmtime;
+ my $header =
+ sprintf( "Module Name:\t%s\nCommitted By:\t%s\nDate:\t\t%s %s %s",
+ $MODULE_NAME, $LOGIN, substr( $now, 0, 19 ), "UTC",
+ substr( $now, 20, 4 ) );
+ return ($header);
+}
+
+# }}}
+
+# {{{ mail_notification
+sub mail_notification {
+ my $id = shift;
+ my (@text) = @_;
+ write_file( "$MAIL_FILE.$id", "From: " . $LOGIN,
+ "Subject: CVS commit: " . $MODULE_NAME, "To: " . $MAILTO,
+ "Reply-To: " . $REPLYTO, "", "", @text );
+
+ my $entity = MIME::Entity->build(
+ From => $LOGIN,
+ To => $MAILTO,
+ Subject => "CVS commit: " . $MODULE_NAME,
+ 'Reply-To' => $REPLYTO,
+ Data => join ( "\n", @text )
+ );
+ if ( $RT::MailCommand eq 'sendmailpipe' ) {
+ open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" )
+ || die "Couldn't send mail: " . $@ . "\n";
+ print MAIL $entity->as_string;
+ close(MAIL);
+ }
+ else {
+ $entity->send( $RT::MailCommand, $RT::MailParams );
+ }
+
+}
+
+# }}}
+
+# {{{ sub record_last_dir
+
+sub record_last_dir {
+ my $id = shift;
+ my $dir = shift;
+
+ # make a note of this directory. later, we'll use this to
+ # figure out if we've gone through the whole commit,
+ # for something that is a bad mockery of attomic commits.
+
+ warn "about to write $dir to $LASTDIR_FILE.$id" if ($DEBUG);
+
+ write_file( "$LASTDIR_FILE.$id", $dir );
+}
+
+# }}}
+
+# {{{ Get the RT stuff set up
+
+# {{{ sub rt_handler
+
+sub rt_handler {
+ my (@LogMessage) = (@_);
+
+ #Connect to the database and get RT::SystemUser and RT::Nobody loaded
+ RT::Init;
+
+ require RT::Ticket;
+
+ #Get the current user all loaded
+ my $CurrentUser = GetCurrentUser();
+
+ if ( !$CurrentUser->Id ) {
+ print
+loc("No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\n");
+ return;
+ }
+
+ my (@commands) = find_commands( \@LogMessage );
+
+ my ( @tickets, @errors );
+
+ # Get the list of tickets we're working with out of commands
+ grep { $_ =~ /^RT-Ticket:\s*(.*?)$/i && push ( @tickets, $1 ) } @commands;
+
+ my $message = new MIME::Entity;
+ $message->build(
+ From => $CurrentUser->EmailAddress,
+ Subject => 'CVS Commit',
+ Data => \@LogMessage
+ );
+
+ # {{{ comment or correspond, as needed
+
+ foreach my $ticket (@tickets) {
+ my $TicketObj = RT::Ticket->new($CurrentUser);
+ $TicketObj->Load($ticket);
+ my ( $id, $msg );
+ unless ( $TicketObj->Id ) {
+ push ( @errors,
+"Couldn't load ticket #$ticket. Not adding commit log to ticket history.\n"
+ );
+ }
+
+ if ( $LogMessage[0] =~ /^(comment|private)$/ ) {
+ ( $id, $msg ) = $TicketObj->Comment( MIMEObj => $message );
+
+ }
+ else {
+ ( $id, $msg ) = $TicketObj->Correspond( MIMEObj => $message );
+ }
+
+ push ( @errors, ">> Log message",
+ "Ticket #" . $TicketObj->Id . ": " . $msg );
+
+ }
+
+ # }}}
+
+ my ($reply) = ActOnPseudoHeaders( $CurrentUser, @commands );
+ print "$reply\n" if ($reply);
+ print join ( "\n", @errors );
+ print "\n";
+
+}
+
+# }}}
+
+# {{{ sub find_commands
+
+sub find_commands {
+ my $lines = shift;
+ my (@pseudoheaders);
+
+ while ( my $line = shift @{$lines} ) {
+ next if $line =~ /^\s*?$/;
+ if ( $line =~ /^RT-/i ) {
+
+ push ( @pseudoheaders, $line );
+ }
+
+ #If we find a line that's not a command, get out.
+ else {
+ unshift ( @{$lines}, $line );
+ last;
+ }
+ }
+
+ return (@pseudoheaders);
+
+}
+
+# }}}
+
+# {{{ sub ActOnPseudoHeaders
+
+=item ActOnPseudoHeaders $PseudoHeaders
+
+Takes a string of pseudo-headers, iterates through them and does what they tell it to.
+
+=cut
+
+sub ActOnPseudoHeaders {
+ my $CurrentUser = shift;
+ my (@actions) = (@_);
+
+ my $ResultsMessage = '';
+ my $Ticket = RT::Ticket->new($CurrentUser);
+
+ foreach my $action (@actions) {
+ my ($val);
+ my $msg = '';
+
+ $ResultsMessage .= ">>> $action\n";
+
+ if ( $action =~ /^RT-(.*?):\s*(.*)$/i ) {
+ my $command = $1;
+ my $args = $2;
+
+ if ( $command =~ /^ticket$/i ) {
+
+ $val = $Ticket->Load($args);
+ unless ($val) {
+ $ResultsMessage .=
+ loc("ERROR: Couldn't load ticket '[_1]': [_2].\n", $1, $msg);
+ . loc("Aborting to avoid unintended ticket modifications.\n")
+ . loc("The following commands were not proccessed:\n\n")
+ . join ( "\n", @actions );
+ return ($ResultsMessage);
+ }
+ $ResultsMessage .= loc("Ticket [_1] loaded\n", $Ticket->Id);
+ }
+ else {
+ unless ( $Ticket->Id ) {
+ $ResultsMessage .= loc("No Ticket specified. Aborting ticket ")
+ . loc("modifications\n\n")
+ . loc("The following commands were not proccessed:\n\n")
+ . join ( "\n", @actions );
+ return ($ResultsMessage);
+ }
+
+ # Deal with the basics
+ if ( $command =~ /^(Subject|Owner|Status|Queue)$/i ) {
+ my $method = 'Set' . ucfirst( lc($1) );
+ ( $val, $msg ) = $Ticket->$method($args);
+ }
+
+ # Deal with the dates
+ elsif ( $command =~ /^(due|starts|started|resolved)$/i ) {
+ my $method = 'Set' . ucfirst( lc($1) );
+ my $date = new RT::Date($CurrentUser);
+ $date->Set( Format => 'unknown', Value => $args );
+ ( $val, $msg ) = $Ticket->$method( $date->ISO );
+ }
+
+ # Deal with the watchers
+ elsif ( $command =~ /^(requestor|requestors|cc|admincc)$/i ) {
+ my $operator = "+";
+ my ($type);
+ if ( $args =~ /^(\+|\-)(.*)$/ ) {
+ $operator = $1;
+ $args = $2;
+ }
+ $type = 'Requestor' if ( $command =~ /^requestor/i );
+ $type = 'Cc' if ( $command =~ /^cc/i );
+ $type = 'AdminCc' if ( $command =~ /^admincc/i );
+
+ my $user = RT::User->new($CurrentUser);
+ $user->Load($args);
+
+ if ($operator eq '+') {
+ ($val, $msg) = $Ticket->AddWatcher( Type => $type,
+ PrincipalId => $user->PrincipalId);
+ } elsif ($operator eq '-') {
+ ($val, $msg) = $Ticket->DeleteWatcher( Type => $type,
+ PrincipalId => $user->PrincipalId);
+ }
+
+ }
+ $ResultsMessage .= $msg . "\n";
+ }
+
+ }
+ return ($ResultsMessage);
+
+}
+
+# }}}
+
+# {{{ sub untaint
+sub untaint {
+ my $val = shift;
+
+ if ( $val =~ /^([-\#\/\w.]+)$/ ) {
+ $val = $1; # $data now untainted
+ }
+ else {
+ die loc("Bad data in [_1]", $val); # log this somewhere
+ }
+ return ($val);
+}
+
+# }}}
+
+=head1 AUTHOR
+
+
+
+ rt-commit-handler is a rewritten version of the NetBSD commit handler,
+ which was placed in the public domain by Charles Hannum. It bore the following
+ authors statement:
+
+ Contributed by David Hampton <hampton@cisco.com>
+ Hacked greatly by Greg A. Woods <woods@planix.com>
+ Rewritten by Charles M. Hannum <mycroft@netbsd.org>
+
+=cut
+
diff --git a/rt/bin/rt-crontool b/rt/bin/rt-crontool
new file mode 100644
index 0000000..8fcd631
--- /dev/null
+++ b/rt/bin/rt-crontool
@@ -0,0 +1,298 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use Carp;
+
+use lib ("/opt/rt3/local/lib", "/opt/rt3/lib");
+
+package RT;
+
+use Getopt::Long;
+
+use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
+use RT::Tickets;
+use RT::Template;
+
+#Clean out all the nasties from the environment
+CleanEnv();
+
+# Load the config file
+RT::LoadConfig();
+
+#Connect to the database and get RT::SystemUser and RT::Nobody loaded
+RT::Init();
+
+#Get the current user all loaded
+my $CurrentUser = GetCurrentUser();
+
+unless ( $CurrentUser->Id ) {
+ print loc("No RT user found. Please consult your RT administrator.\n");
+ exit(1);
+}
+
+my ( $search, $condition, $action, $search_arg, $condition_arg, $action_arg,
+ $template_id, $transaction, $transaction_type, $help, $verbose );
+GetOptions( "search=s" => \$search,
+ "search-arg=s" => \$search_arg,
+ "condition=s" => \$condition,
+ "condition-arg=s" => \$condition_arg,
+ "action-arg=s" => \$action_arg,
+ "action=s" => \$action,
+ "template-id=s" => \$template_id,
+ "transaction=s" => \$transaction,
+ "transaction-type=s" => \$transaction_type,
+ "help" => \$help,
+ "verbose|v" => \$verbose );
+
+help() if $help or not $search or not $action;
+
+$transaction ||= 'first';
+unless ( $transaction =~ /^(first|last)$/i ) {
+ print STDERR loc("--transaction argument could be only 'first' or 'last'");
+ exit 1;
+}
+$transaction = lc($transaction) eq 'first'? 'ASC': 'DESC';
+
+# We _must_ have a search object
+load_module($search);
+load_module($action) if ($action);
+load_module($condition) if ($condition);
+
+# load template if specified
+my $template_obj;
+if ($template_id) {
+ $template_obj = RT::Template->new($CurrentUser);
+ $template_obj->Load($template_id);
+}
+my $void_scrip = RT::Scrip->new( $CurrentUser );
+my $void_scrip_action = RT::ScripAction->new( $CurrentUser );
+
+#At the appointed time:
+
+#find a bunch of tickets
+my $tickets = RT::Tickets->new($CurrentUser);
+my $search = $search->new(
+ TicketsObj => $tickets,
+ Argument => $search_arg,
+ CurrentUser => $CurrentUser
+);
+
+$search->Prepare();
+
+# TicketsFound is an RT::Tickets object
+my $tickets = $search->TicketsObj;
+
+#for each ticket we've found
+while ( my $ticket = $tickets->Next() ) {
+ print $ticket->Id() . ": " if ($verbose);
+
+ my $transaction = get_transaction($ticket);
+ print loc("Using transaction #[_1]...", $transaction->id)
+ if $verbose && $transaction;
+
+ # perform some more advanced check
+ if ($condition) {
+ my $condition_obj = $condition->new(
+ TransactionObj => $transaction,
+ TicketObj => $ticket,
+ ScripObj => $void_scrip,
+ TemplateObj => $template_obj,
+ Argument => $condition_arg,
+ CurrentUser => $CurrentUser,
+ );
+
+ # if the condition doesn't apply, get out of here
+
+ next unless ( $condition_obj->IsApplicable );
+ print loc("Condition matches...") if ($verbose);
+ }
+
+ #prepare our action
+ my $action_obj = $action->new(
+ TicketObj => $ticket,
+ TransactionObj => $transaction,
+ TemplateObj => $template_obj,
+ Argument => $action_arg,
+ ScripObj => $void_scrip,
+ ScripActionObj => $void_scrip_action,
+ CurrentUser => $CurrentUser,
+ );
+
+ #if our preparation, move onto the next ticket
+ next unless ( $action_obj->Prepare );
+ print loc("Action prepared...") if ($verbose);
+
+ #commit our action.
+ next unless ( $action_obj->Commit );
+ print loc("Action committed.\n") if ($verbose);
+}
+
+=head2 get_transaction
+
+Takes ticket and returns its transaction acording to command
+line arguments C<--transaction> and <--transaction-type>.
+
+=cut
+
+sub get_transaction {
+ my $ticket = shift;
+ my $txns = $ticket->Transactions;
+ $txns->OrderByCols(
+ { FIELD => 'Created', ORDER => $transaction },
+ { FIELD => 'id', ORDER => $transaction },
+ );
+ $txns->Limit( FIELD => 'Type', VALUE => $transaction_type )
+ if $transaction_type;
+ $txns->RowsPerPage(1);
+ return $txns->First;
+}
+
+# {{{ load_module
+
+=head2 load_module
+
+Loads a perl module, dying nicely if it can't find it.
+
+=cut
+
+sub load_module {
+ my $modname = shift;
+ eval "require $modname";
+ if ($@) {
+ die loc( "Failed to load module [_1]. ([_2])", $modname, $@ );
+ }
+
+}
+
+# }}}
+
+# {{{ loc
+
+=head2 loc LIST
+
+Localize this string, with the current user's currentuser object
+
+=cut
+
+sub loc {
+ $CurrentUser->loc(@_);
+}
+
+# }}}
+
+sub help {
+
+ print loc( "[_1] is a tool to act on tickets from an external scheduling tool, such as cron.", $0 )
+ . "\n";
+ print loc("It takes several arguments:") . "\n\n";
+
+ print " "
+ . loc( "[_1] - Specify the search module you want to use", "--search" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--search-argument", "--search" )
+ . "\n";
+
+ print " "
+ . loc( "[_1] - Specify the condition module you want to use", "--condition" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--condition-argument", "--condition" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify the action module you want to use", "--action" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--action-argument", "--action" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify id of the template you want to use", "--template-id" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify if you want to use either 'first' or 'last' transaction", "--transaction" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify the type of a transaction you want to use", "--transaction-type" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Output status updates to STDOUT", "--verbose" ) . "\n";
+ print "\n";
+ print "\n";
+ print loc("Security:")."\n";
+ print loc("This tool allows the user to run arbitrary perl modules from within RT.")." ".
+ loc("If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT.")." ".
+ loc("It is incredibly important that nonprivileged users not be allowed to run this tool."). " " .
+ loc("It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool.")."\n";
+ print "\n";
+ print loc("Example:");
+ print "\n";
+ print " "
+ . loc( "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+ )
+ . "\n\n";
+
+ print " bin/rt-crontool \\\n";
+ print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
+ print " --condition RT::Condition::UntouchedInHours --condition-arg 4 \\\n";
+ print " --action RT::Action::SetPriority --action-arg 99 \\\n";
+ print " --verbose\n";
+
+ print "\n";
+ print loc("Escalate tickets"). "\n";
+ print " bin/rt-crontool \\\n";
+ print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
+ print " --action RT::Action::EscalatePriority\n";
+
+
+
+
+
+
+ exit(0);
+}
diff --git a/rt/bin/rt-crontool.in b/rt/bin/rt-crontool.in
new file mode 100644
index 0000000..9881120
--- /dev/null
+++ b/rt/bin/rt-crontool.in
@@ -0,0 +1,298 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use Carp;
+
+use lib ("@LOCAL_LIB_PATH@", "@RT_LIB_PATH@");
+
+package RT;
+
+use Getopt::Long;
+
+use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
+use RT::Tickets;
+use RT::Template;
+
+#Clean out all the nasties from the environment
+CleanEnv();
+
+# Load the config file
+RT::LoadConfig();
+
+#Connect to the database and get RT::SystemUser and RT::Nobody loaded
+RT::Init();
+
+#Get the current user all loaded
+my $CurrentUser = GetCurrentUser();
+
+unless ( $CurrentUser->Id ) {
+ print loc("No RT user found. Please consult your RT administrator.\n");
+ exit(1);
+}
+
+my ( $search, $condition, $action, $search_arg, $condition_arg, $action_arg,
+ $template_id, $transaction, $transaction_type, $help, $verbose );
+GetOptions( "search=s" => \$search,
+ "search-arg=s" => \$search_arg,
+ "condition=s" => \$condition,
+ "condition-arg=s" => \$condition_arg,
+ "action-arg=s" => \$action_arg,
+ "action=s" => \$action,
+ "template-id=s" => \$template_id,
+ "transaction=s" => \$transaction,
+ "transaction-type=s" => \$transaction_type,
+ "help" => \$help,
+ "verbose|v" => \$verbose );
+
+help() if $help or not $search or not $action;
+
+$transaction ||= 'first';
+unless ( $transaction =~ /^(first|last)$/i ) {
+ print STDERR loc("--transaction argument could be only 'first' or 'last'");
+ exit 1;
+}
+$transaction = lc($transaction) eq 'first'? 'ASC': 'DESC';
+
+# We _must_ have a search object
+load_module($search);
+load_module($action) if ($action);
+load_module($condition) if ($condition);
+
+# load template if specified
+my $template_obj;
+if ($template_id) {
+ $template_obj = RT::Template->new($CurrentUser);
+ $template_obj->Load($template_id);
+}
+my $void_scrip = RT::Scrip->new( $CurrentUser );
+my $void_scrip_action = RT::ScripAction->new( $CurrentUser );
+
+#At the appointed time:
+
+#find a bunch of tickets
+my $tickets = RT::Tickets->new($CurrentUser);
+my $search = $search->new(
+ TicketsObj => $tickets,
+ Argument => $search_arg,
+ CurrentUser => $CurrentUser
+);
+
+$search->Prepare();
+
+# TicketsFound is an RT::Tickets object
+my $tickets = $search->TicketsObj;
+
+#for each ticket we've found
+while ( my $ticket = $tickets->Next() ) {
+ print $ticket->Id() . ": " if ($verbose);
+
+ my $transaction = get_transaction($ticket);
+ print loc("Using transaction #[_1]...", $transaction->id)
+ if $verbose && $transaction;
+
+ # perform some more advanced check
+ if ($condition) {
+ my $condition_obj = $condition->new(
+ TransactionObj => $transaction,
+ TicketObj => $ticket,
+ ScripObj => $void_scrip,
+ TemplateObj => $template_obj,
+ Argument => $condition_arg,
+ CurrentUser => $CurrentUser,
+ );
+
+ # if the condition doesn't apply, get out of here
+
+ next unless ( $condition_obj->IsApplicable );
+ print loc("Condition matches...") if ($verbose);
+ }
+
+ #prepare our action
+ my $action_obj = $action->new(
+ TicketObj => $ticket,
+ TransactionObj => $transaction,
+ TemplateObj => $template_obj,
+ Argument => $action_arg,
+ ScripObj => $void_scrip,
+ ScripActionObj => $void_scrip_action,
+ CurrentUser => $CurrentUser,
+ );
+
+ #if our preparation, move onto the next ticket
+ next unless ( $action_obj->Prepare );
+ print loc("Action prepared...") if ($verbose);
+
+ #commit our action.
+ next unless ( $action_obj->Commit );
+ print loc("Action committed.\n") if ($verbose);
+}
+
+=head2 get_transaction
+
+Takes ticket and returns its transaction acording to command
+line arguments C<--transaction> and <--transaction-type>.
+
+=cut
+
+sub get_transaction {
+ my $ticket = shift;
+ my $txns = $ticket->Transactions;
+ $txns->OrderByCols(
+ { FIELD => 'Created', ORDER => $transaction },
+ { FIELD => 'id', ORDER => $transaction },
+ );
+ $txns->Limit( FIELD => 'Type', VALUE => $transaction_type )
+ if $transaction_type;
+ $txns->RowsPerPage(1);
+ return $txns->First;
+}
+
+# {{{ load_module
+
+=head2 load_module
+
+Loads a perl module, dying nicely if it can't find it.
+
+=cut
+
+sub load_module {
+ my $modname = shift;
+ eval "require $modname";
+ if ($@) {
+ die loc( "Failed to load module [_1]. ([_2])", $modname, $@ );
+ }
+
+}
+
+# }}}
+
+# {{{ loc
+
+=head2 loc LIST
+
+Localize this string, with the current user's currentuser object
+
+=cut
+
+sub loc {
+ $CurrentUser->loc(@_);
+}
+
+# }}}
+
+sub help {
+
+ print loc( "[_1] is a tool to act on tickets from an external scheduling tool, such as cron.", $0 )
+ . "\n";
+ print loc("It takes several arguments:") . "\n\n";
+
+ print " "
+ . loc( "[_1] - Specify the search module you want to use", "--search" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--search-argument", "--search" )
+ . "\n";
+
+ print " "
+ . loc( "[_1] - Specify the condition module you want to use", "--condition" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--condition-argument", "--condition" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify the action module you want to use", "--action" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--action-argument", "--action" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify id of the template you want to use", "--template-id" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify if you want to use either 'first' or 'last' transaction", "--transaction" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify the type of a transaction you want to use", "--transaction-type" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Output status updates to STDOUT", "--verbose" ) . "\n";
+ print "\n";
+ print "\n";
+ print loc("Security:")."\n";
+ print loc("This tool allows the user to run arbitrary perl modules from within RT.")." ".
+ loc("If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT.")." ".
+ loc("It is incredibly important that nonprivileged users not be allowed to run this tool."). " " .
+ loc("It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool.")."\n";
+ print "\n";
+ print loc("Example:");
+ print "\n";
+ print " "
+ . loc( "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+ )
+ . "\n\n";
+
+ print " bin/rt-crontool \\\n";
+ print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
+ print " --condition RT::Condition::UntouchedInHours --condition-arg 4 \\\n";
+ print " --action RT::Action::SetPriority --action-arg 99 \\\n";
+ print " --verbose\n";
+
+ print "\n";
+ print loc("Escalate tickets"). "\n";
+ print " bin/rt-crontool \\\n";
+ print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
+ print " --action RT::Action::EscalatePriority\n";
+
+
+
+
+
+
+ exit(0);
+}
diff --git a/rt/bin/rt-mailgate b/rt/bin/rt-mailgate
new file mode 100755
index 0000000..8db26db
--- /dev/null
+++ b/rt/bin/rt-mailgate
@@ -0,0 +1,323 @@
+#!/usr/bin/perl -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+rt-mailgate - Mail interface to RT3.
+
+=cut
+
+
+use strict;
+use warnings;
+use Getopt::Long;
+use LWP::UserAgent;
+
+use constant EX_TEMPFAIL => 75;
+
+my %opts;
+GetOptions( \%opts, "queue=s", "action=s", "url=s", "jar=s", "help", "debug", "extension=s", "timeout=i" );
+
+if ( $opts{help} ) {
+ require Pod::Usage;
+ import Pod::Usage;
+ pod2usage("RT Mail Gateway\n");
+ exit 1; # Don't want to succeed if this is really an email!
+}
+
+for (qw(url)) {
+ die "$0 invoked improperly\n\nNo $_ provided to mail gateway!\n" unless $opts{$_};
+}
+
+my $ua = LWP::UserAgent->new();
+$ua->cookie_jar( { file => $opts{jar} } );
+
+my %args = (
+ SessionType => 'REST', # Surpress login box
+);
+foreach ( qw(queue action) ) {
+ $args{$_} = $opts{$_} if defined $opts{$_};
+};
+
+# Read the message in from STDIN
+$args{'message'} = do { local (@ARGV, $/); <> };
+
+unless ( $args{message} =~ /\S/ ) {
+ print STDERR "$0: no message passed on STDIN!\n";
+ exit 0;
+}
+
+if ($opts{'extension'}) {
+ $args{$opts{'extension'}} = $ENV{'EXTENSION'};
+}
+
+# Set up cookie here.
+
+my $full_url = $opts{'url'}. "/REST/1.0/NoAuth/mail-gateway";
+warn "Connecting to $full_url" if $opts{'debug'};
+
+
+
+$ua->timeout(exists($opts{'timeout'}) ? $opts{'timeout'} : 180);
+my $r = $ua->post( $full_url, {%args} );
+check_failure($r);
+
+my $content = $r->content;
+warn $content if ($opts{debug});
+
+if ( $content !~ /^(ok|not ok)/ ) {
+
+ # It's not the server's fault if the mail is bogus. We just want to know that
+ # *something* came out of the server.
+ warn <<EOF;
+RT server error.
+
+The RT server which handled your email did not behave as expected. It
+said:
+
+$content
+EOF
+
+exit EX_TEMPFAIL;
+
+}
+
+exit;
+
+
+sub check_failure {
+ my $r = shift;
+ return if $r->is_success();
+
+ # This ordinarily oughtn't to be able to happen, suggests a bug in RT.
+ # So only load these heavy modules when they're needed.
+ require HTML::TreeBuilder;
+ require HTML::FormatText;
+
+ my $error = $r->error_as_HTML;
+ my $tree = HTML::TreeBuilder->new->parse($error);
+ $tree->eof;
+
+ # It'll be a cold day in hell before RT sends out bounces in HTML
+ my $formatter = HTML::FormatText->new( leftmargin => 0,
+ rightmargin => 50 );
+ warn $formatter->format($tree);
+ warn "This is $0 exiting because of an undefined server error" if ($opts{debug});
+ exit EX_TEMPFAIL;
+}
+
+
+=head1 SYNOPSIS
+
+ rt-mailgate --help : this text
+
+Usual invocation (from MTA):
+
+ rt-mailgate --action (correspond|comment|...) --queue queuename
+ --url http://your.rt.server/
+ [ --debug ]
+ [ --extension (queue|action|ticket) ]
+ [ --timeout seconds ]
+
+
+
+See C<man rt-mailgate> for more.
+
+=head1 OPTIONS
+
+=over 3
+
+=item C<--action>
+
+Specifies what happens to email sent to this alias. The avaliable
+basic actions are: C<correspond>, C<comment>.
+
+
+If you've set the RT configuration variable B<$RT::UnsafeEmailCommands>,
+C<take> and C<resolve> are also available. You can execute two or more
+actions on a single message using a C<-> separated list. RT will execute
+the actions in the listed order. For example you can use C<take-comment>,
+C<correspond-resolve> or C<take-comment-resolve> as actions.
+
+Note that C<take> and C<resolve> actions ignore message text if used
+alone. Include a C<comment> or C<correspond> action if you want RT
+to record the incoming message.
+
+The default action is C<correspond>.
+
+=item C<--queue>
+
+This flag determines which queue this alias should create a ticket in if no ticket identifier
+is found.
+
+=item C<--url>
+
+This flag tells the mail gateway where it can find your RT server. You should
+probably use the same URL that users use to log into RT.
+
+
+=item C<--extension> OPTIONAL
+
+Some MTAs will route mail sent to user-foo@host or user+foo@host to user@host
+and present "foo" in the environment variable $EXTENSION. By specifying
+the value "queue" for this parameter, the queue this message should be
+submitted to will be set to the value of $EXTENSION. By specifying
+"ticket", $EXTENSION will be interpreted as the id of the ticket this message
+is related to. "action" will allow the user to specify either "comment" or
+"correspond" in the address extension.
+
+=item C<--debug> OPTIONAL
+
+Print debugging output to standard error
+
+
+=item C<--timeout> OPTIONAL
+
+Configure the timeout for posting the message to the web server. The
+default timeout is 3 minutes (180 seconds).
+
+
+=head1 DESCRIPTION
+
+The RT mail gateway is the primary mechanism for communicating with RT
+via email. This program simply directs the email to the RT web server,
+which handles filing correspondence and sending out any required mail.
+It is designed to be run as part of the mail delivery process, either
+called directly by the MTA or C<procmail>, or in a F<.forward> or
+equivalent.
+
+=head1 SETUP
+
+Much of the set up of the mail gateway depends on your MTA and mail
+routing configuration. However, you will need first of all to create an
+RT user for the mail gateway and assign it a password; this helps to
+ensure that mail coming into the web server did originate from the
+gateway.
+
+Next, you need to route mail to C<rt-mailgate> for the queues you're
+monitoring. For instance, if you're using F</etc/aliases> and you have a
+"bugs" queue, you will want something like this:
+
+ bugs: "|/opt/rt3/bin/rt-mailgate --queue bugs --action correspond
+ --url http://rt.mycorp.com/"
+
+ bugs-comment: "|/opt/rt3/bin/rt-mailgate --queue bugs --action comment
+ --url http://rt.mycorp.com/"
+
+Note that you don't have to run your RT server on your mail server, as
+the mail gateway will happily relay to a different machine.
+
+=head1 CUSTOMIZATION
+
+By default, the mail gateway will accept mail from anyone. However,
+there are situations in which you will want to authenticate users
+before allowing them to communicate with the system. You can do this
+via a plug-in mechanism in the RT configuration.
+
+You can set the array C<@RT::MailPlugins> to be a list of plugins. The
+default plugin, if this is not given, is C<Auth::MailFrom> - that is,
+authentication of the person is done based on the C<From> header of the
+email. If you have additional filters or authentication mechanisms, you
+can list them here and they will be called in order:
+
+ @RT::MailPlugins = (
+ "Filter::SpamAssassin",
+ "Auth::LDAP",
+ # ...
+ );
+
+See the documentation for any additional plugins you have.
+
+You may also put Perl subroutines into the C<@RT::MailPlugins> array, if
+they behave as described below.
+
+=head1 WRITING PLUGINS
+
+What's actually going on in the above is that C<@RT::MailPlugins> is a
+list of Perl modules; RT prepends C<RT::Interface::Email::> to the name,
+to form a package name, and then C<use>'s this module. The module is
+expected to provide a C<GetCurrentUser> subroutine, which takes a hash of
+several parameters:
+
+=over 4
+
+=item Message
+
+A C<MIME::Entity> object representing the email
+
+=item CurrentUser
+
+An C<RT::CurrentUser> object
+
+=item AuthStat
+
+The authentication level returned from the previous plugin.
+
+=item Ticket [OPTIONAL]
+
+The ticket under discussion
+
+=item Queue [OPTIONAL]
+
+If we don't already have a ticket id, we need to know which queue we're talking about
+
+=item Action
+
+The action being performed. At the moment, it's one of "comment" or "correspond"
+
+=back 4
+
+It returns two values, the new C<RT::CurrentUser> object, and the new
+authentication level. The authentication level can be zero, not allowed
+to communicate with RT at all, (a "permission denied" error is mailed to
+the correspondent) or one, which is the normal mode of operation.
+Additionally, if C<-1> is returned, then the processing of the plug-ins
+stops immediately and the message is ignored.
+
+=cut
+
diff --git a/rt/bin/rt-mailgate.in b/rt/bin/rt-mailgate.in
new file mode 100644
index 0000000..6264d43
--- /dev/null
+++ b/rt/bin/rt-mailgate.in
@@ -0,0 +1,323 @@
+#!@PERL@ -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+rt-mailgate - Mail interface to RT3.
+
+=cut
+
+
+use strict;
+use warnings;
+use Getopt::Long;
+use LWP::UserAgent;
+
+use constant EX_TEMPFAIL => 75;
+
+my %opts;
+GetOptions( \%opts, "queue=s", "action=s", "url=s", "jar=s", "help", "debug", "extension=s", "timeout=i" );
+
+if ( $opts{help} ) {
+ require Pod::Usage;
+ import Pod::Usage;
+ pod2usage("RT Mail Gateway\n");
+ exit 1; # Don't want to succeed if this is really an email!
+}
+
+for (qw(url)) {
+ die "$0 invoked improperly\n\nNo $_ provided to mail gateway!\n" unless $opts{$_};
+}
+
+my $ua = LWP::UserAgent->new();
+$ua->cookie_jar( { file => $opts{jar} } );
+
+my %args = (
+ SessionType => 'REST', # Surpress login box
+);
+foreach ( qw(queue action) ) {
+ $args{$_} = $opts{$_} if defined $opts{$_};
+};
+
+# Read the message in from STDIN
+$args{'message'} = do { local (@ARGV, $/); <> };
+
+unless ( $args{message} =~ /\S/ ) {
+ print STDERR "$0: no message passed on STDIN!\n";
+ exit 0;
+}
+
+if ($opts{'extension'}) {
+ $args{$opts{'extension'}} = $ENV{'EXTENSION'};
+}
+
+# Set up cookie here.
+
+my $full_url = $opts{'url'}. "/REST/1.0/NoAuth/mail-gateway";
+warn "Connecting to $full_url" if $opts{'debug'};
+
+
+
+$ua->timeout(exists($opts{'timeout'}) ? $opts{'timeout'} : 180);
+my $r = $ua->post( $full_url, {%args} );
+check_failure($r);
+
+my $content = $r->content;
+warn $content if ($opts{debug});
+
+if ( $content !~ /^(ok|not ok)/ ) {
+
+ # It's not the server's fault if the mail is bogus. We just want to know that
+ # *something* came out of the server.
+ warn <<EOF;
+RT server error.
+
+The RT server which handled your email did not behave as expected. It
+said:
+
+$content
+EOF
+
+exit EX_TEMPFAIL;
+
+}
+
+exit;
+
+
+sub check_failure {
+ my $r = shift;
+ return if $r->is_success();
+
+ # This ordinarily oughtn't to be able to happen, suggests a bug in RT.
+ # So only load these heavy modules when they're needed.
+ require HTML::TreeBuilder;
+ require HTML::FormatText;
+
+ my $error = $r->error_as_HTML;
+ my $tree = HTML::TreeBuilder->new->parse($error);
+ $tree->eof;
+
+ # It'll be a cold day in hell before RT sends out bounces in HTML
+ my $formatter = HTML::FormatText->new( leftmargin => 0,
+ rightmargin => 50 );
+ warn $formatter->format($tree);
+ warn "This is $0 exiting because of an undefined server error" if ($opts{debug});
+ exit EX_TEMPFAIL;
+}
+
+
+=head1 SYNOPSIS
+
+ rt-mailgate --help : this text
+
+Usual invocation (from MTA):
+
+ rt-mailgate --action (correspond|comment|...) --queue queuename
+ --url http://your.rt.server/
+ [ --debug ]
+ [ --extension (queue|action|ticket) ]
+ [ --timeout seconds ]
+
+
+
+See C<man rt-mailgate> for more.
+
+=head1 OPTIONS
+
+=over 3
+
+=item C<--action>
+
+Specifies what happens to email sent to this alias. The avaliable
+basic actions are: C<correspond>, C<comment>.
+
+
+If you've set the RT configuration variable B<$RT::UnsafeEmailCommands>,
+C<take> and C<resolve> are also available. You can execute two or more
+actions on a single message using a C<-> separated list. RT will execute
+the actions in the listed order. For example you can use C<take-comment>,
+C<correspond-resolve> or C<take-comment-resolve> as actions.
+
+Note that C<take> and C<resolve> actions ignore message text if used
+alone. Include a C<comment> or C<correspond> action if you want RT
+to record the incoming message.
+
+The default action is C<correspond>.
+
+=item C<--queue>
+
+This flag determines which queue this alias should create a ticket in if no ticket identifier
+is found.
+
+=item C<--url>
+
+This flag tells the mail gateway where it can find your RT server. You should
+probably use the same URL that users use to log into RT.
+
+
+=item C<--extension> OPTIONAL
+
+Some MTAs will route mail sent to user-foo@host or user+foo@host to user@host
+and present "foo" in the environment variable $EXTENSION. By specifying
+the value "queue" for this parameter, the queue this message should be
+submitted to will be set to the value of $EXTENSION. By specifying
+"ticket", $EXTENSION will be interpreted as the id of the ticket this message
+is related to. "action" will allow the user to specify either "comment" or
+"correspond" in the address extension.
+
+=item C<--debug> OPTIONAL
+
+Print debugging output to standard error
+
+
+=item C<--timeout> OPTIONAL
+
+Configure the timeout for posting the message to the web server. The
+default timeout is 3 minutes (180 seconds).
+
+
+=head1 DESCRIPTION
+
+The RT mail gateway is the primary mechanism for communicating with RT
+via email. This program simply directs the email to the RT web server,
+which handles filing correspondence and sending out any required mail.
+It is designed to be run as part of the mail delivery process, either
+called directly by the MTA or C<procmail>, or in a F<.forward> or
+equivalent.
+
+=head1 SETUP
+
+Much of the set up of the mail gateway depends on your MTA and mail
+routing configuration. However, you will need first of all to create an
+RT user for the mail gateway and assign it a password; this helps to
+ensure that mail coming into the web server did originate from the
+gateway.
+
+Next, you need to route mail to C<rt-mailgate> for the queues you're
+monitoring. For instance, if you're using F</etc/aliases> and you have a
+"bugs" queue, you will want something like this:
+
+ bugs: "|/opt/rt3/bin/rt-mailgate --queue bugs --action correspond
+ --url http://rt.mycorp.com/"
+
+ bugs-comment: "|/opt/rt3/bin/rt-mailgate --queue bugs --action comment
+ --url http://rt.mycorp.com/"
+
+Note that you don't have to run your RT server on your mail server, as
+the mail gateway will happily relay to a different machine.
+
+=head1 CUSTOMIZATION
+
+By default, the mail gateway will accept mail from anyone. However,
+there are situations in which you will want to authenticate users
+before allowing them to communicate with the system. You can do this
+via a plug-in mechanism in the RT configuration.
+
+You can set the array C<@RT::MailPlugins> to be a list of plugins. The
+default plugin, if this is not given, is C<Auth::MailFrom> - that is,
+authentication of the person is done based on the C<From> header of the
+email. If you have additional filters or authentication mechanisms, you
+can list them here and they will be called in order:
+
+ @RT::MailPlugins = (
+ "Filter::SpamAssassin",
+ "Auth::LDAP",
+ # ...
+ );
+
+See the documentation for any additional plugins you have.
+
+You may also put Perl subroutines into the C<@RT::MailPlugins> array, if
+they behave as described below.
+
+=head1 WRITING PLUGINS
+
+What's actually going on in the above is that C<@RT::MailPlugins> is a
+list of Perl modules; RT prepends C<RT::Interface::Email::> to the name,
+to form a package name, and then C<use>'s this module. The module is
+expected to provide a C<GetCurrentUser> subroutine, which takes a hash of
+several parameters:
+
+=over 4
+
+=item Message
+
+A C<MIME::Entity> object representing the email
+
+=item CurrentUser
+
+An C<RT::CurrentUser> object
+
+=item AuthStat
+
+The authentication level returned from the previous plugin.
+
+=item Ticket [OPTIONAL]
+
+The ticket under discussion
+
+=item Queue [OPTIONAL]
+
+If we don't already have a ticket id, we need to know which queue we're talking about
+
+=item Action
+
+The action being performed. At the moment, it's one of "comment" or "correspond"
+
+=back 4
+
+It returns two values, the new C<RT::CurrentUser> object, and the new
+authentication level. The authentication level can be zero, not allowed
+to communicate with RT at all, (a "permission denied" error is mailed to
+the correspondent) or one, which is the normal mode of operation.
+Additionally, if C<-1> is returned, then the processing of the plug-ins
+stops immediately and the message is ignored.
+
+=cut
+
diff --git a/rt/bin/rt.in b/rt/bin/rt.in
new file mode 100644
index 0000000..09b52ae
--- /dev/null
+++ b/rt/bin/rt.in
@@ -0,0 +1,2060 @@
+#!@PERL@ -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+# Designed and implemented for Best Practical Solutions, LLC by
+# Abhijit Menon-Sen <ams@wiw.org>
+
+use strict;
+
+# This program is intentionally written to have as few non-core module
+# dependencies as possible. It should stay that way.
+
+use Cwd;
+use LWP;
+use Text::ParseWords;
+use HTTP::Request::Common;
+use Term::ReadLine;
+
+# 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 => undef,
+ orderby => undef,
+ ),
+ config_from_file($ENV{RTCONFIG} || ".rtrc"),
+ config_from_env()
+);
+my $session = new Session("$HOME/.rt_sessions");
+my $REST = "$config{server}/REST/1.0";
+
+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 $field = '(?:[a-zA-Z](?:[a-zA-Z0-9_-]|\s+)*)';
+my $label = '[a-zA-Z0-9@_.+-]+';
+my $labels = "(?:$label,)*$label";
+my $idlist = '(?:(?:\d+-)?\d+,)*(?:\d+-)?\d+';
+
+# Our command line looks like this:
+#
+# rt <action> [options] [arguments]
+#
+# We'll parse just enough of it to decide upon an action to perform, and
+# leave the rest to per-action handlers to interpret appropriately.
+
+my %handlers = (
+# handler => [ ...aliases... ],
+ version => ["version", "ver"],
+ shell => ["shell"],
+ logout => ["logout"],
+ help => ["help", "man"],
+ show => ["show", "cat"],
+ edit => ["create", "edit", "new", "ed"],
+ list => ["search", "list", "ls"],
+ comment => ["comment", "correspond"],
+ link => ["link", "ln"],
+ merge => ["merge"],
+ grant => ["grant", "revoke"],
+ take => ["take", "steal", "untake"],
+ quit => ["quit", "exit"],
+);
+
+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;
+ $actions{$action}->($action);
+ }
+ else {
+ print STDERR "rt: Unknown command '@ARGV'.\n";
+ print STDERR "rt: For help, run 'rt help'.\n";
+ }
+}
+
+handler();
+exit;
+
+# Handler functions.
+# ------------------
+#
+# The following subs are handlers for each entry in %actions.
+
+sub shell {
+ $|=1;
+ my $term = new Term::ReadLine 'RT CLI';
+ while ( defined ($_ = $term->readline($prompt)) ) {
+ next if /^#/ || /^\s*$/;
+
+ @ARGV = shellwords($_);
+ handler();
+ }
+}
+
+sub version {
+ print "rt $VERSION\n";
+}
+
+sub logout {
+ submit("$REST/logout") if defined $session->cookie;
+}
+
+sub quit {
+ logout();
+ exit;
+}
+
+my %help;
+sub help {
+ my ($action, $type) = @_;
+ my $key;
+
+ # What help topics do we know about?
+ if (!%help) {
+ local $/ = undef;
+ foreach my $item (@{ Form::parse(<DATA>) }) {
+ my $title = $item->[2]{Title};
+ my @titles = ref $title eq 'ARRAY' ? @$title : $title;
+
+ foreach $title (grep $_, @titles) {
+ $help{$title} = $item->[2]{Text};
+ }
+ }
+ }
+
+ # What does the user want help with?
+ undef $action if ($action && $actions{$action} eq \&help);
+ unless ($action || $type) {
+ # If we don't know, we'll look for clues in @ARGV.
+ foreach (@ARGV) {
+ if (exists $help{$_}) { $key = $_; last; }
+ }
+ unless ($key) {
+ # Tolerate possibly plural words.
+ foreach (@ARGV) {
+ if ($_ =~ s/s$// && exists $help{$_}) { $key = $_; last; }
+ }
+ }
+ }
+
+ if ($type && $action) {
+ $key = "$type.$action";
+ }
+ $key ||= $type || $action || "introduction";
+
+ # Find a suitable topic to display.
+ while (!exists $help{$key}) {
+ if ($type && $action) {
+ if ($key eq "$type.$action") { $key = $action; }
+ elsif ($key eq $action) { $key = $type; }
+ else { $key = "introduction"; }
+ }
+ else {
+ $key = "introduction";
+ }
+ }
+
+ print STDERR $help{$key}, "\n\n";
+}
+
+# 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;
+
+ 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;
+ }
+ elsif (/^-f$/) {
+ if ($ARGV[0] !~ /^(?:(?:$field,)*$field)$/) {
+ whine "No valid field list in '-f $ARGV[0]'.";
+ $bad = 1; last;
+ }
+ $data{fields} = shift @ARGV;
+ }
+ elsif (!defined $q && !/^-/) {
+ $q = $_;
+ }
+ else {
+ my $datum = /^-/ ? "option" : "argument";
+ whine "Unrecognised $datum '$_'.";
+ $bad = 1; last;
+ }
+ }
+ if (!defined $q) {
+ $q = $config{query};
+ }
+
+ $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) if $bad;
+
+ my $r = submit("$REST/search/$type", { query => $q, %data });
+ print $r->content;
+}
+
+# Displays selected information about a single object.
+
+sub show {
+ my ($type, @objects, %data);
+ my $slurped = 0;
+ my $bad = 0;
+
+ 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 (/^-([isl])$/) {
+ $data{format} = $1;
+ }
+ elsif (/^-$/ && !$slurped) {
+ chomp(my @lines = <STDIN>);
+ foreach (@lines) {
+ unless (is_object_spec($_, $type)) {
+ whine "Invalid object on STDIN: '$_'.";
+ $bad = 1; last;
+ }
+ push @objects, $_;
+ }
+ $slurped = 1;
+ }
+ elsif (/^-f$/) {
+ if ($ARGV[0] !~ /^(?:(?:$field,)*$field)$/) {
+ whine "No valid field list in '-f $ARGV[0]'.";
+ $bad = 1; last;
+ }
+ $data{fields} = shift @ARGV;
+ }
+ elsif (my $spec = is_object_spec($_, $type)) {
+ push @objects, $spec;
+ }
+ else {
+ my $datum = /^-/ ? "option" : "argument";
+ whine "Unrecognised $datum '$_'.";
+ $bad = 1; last;
+ }
+ }
+
+ unless (@objects) {
+ whine "No objects specified.";
+ $bad = 1;
+ }
+ #return help("show", $type) if $bad;
+ return suggest_help("show", $type) 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);
+ }
+ print $c;
+}
+
+# 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;
+
+ if (/^-e$/) { $edit = 1 }
+ elsif (/^-i$/) { $input = 1 }
+ elsif (/^-o$/) { $output = 1 }
+ elsif (/^-t$/) {
+ $bad = 1, last unless defined($type = get_type_argument());
+ }
+ elsif (/^-S$/) {
+ $bad = 1, last unless get_var_argument(\%data);
+ }
+ elsif (/^-$/ && !($slurped || $input)) {
+ chomp(my @lines = <STDIN>);
+ foreach (@lines) {
+ unless (is_object_spec($_, $type)) {
+ whine "Invalid object on STDIN: '$_'.";
+ $bad = 1; last;
+ }
+ push @objects, $_;
+ }
+ $slurped = 1;
+ }
+ elsif (/^set$/i) {
+ my $vars = 0;
+
+ while (@ARGV && $ARGV[0] =~ /^($field)([+-]?=)(.*)$/) {
+ 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)=(.*)$/) {
+ 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 (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");
+ }
+ #return help($action, $type) if $bad;
+ return suggest_help($action, $type) if $bad;
+
+ # We need a form to make changes to. We usually ask the server for
+ # one, but we can avoid that if we are fed one on STDIN, or if the
+ # user doesn't want to edit the form by hand, and the command line
+ # specifies only simple variable assignments. We *should* get a
+ # form if we're creating a new ticket, so that the default values
+ # get filled in properly.
+
+ my @new_objects = grep /\/new$/, @objects;
+
+ if ($input) {
+ local $/ = undef;
+ $text = <STDIN>;
+ }
+ elsif ($edit || %add || %del || !$cl || @new_objects) {
+ my $r = submit("$REST/show", { id => \@objects, format => 'l' });
+ $text = $r->content;
+ }
+
+ # If any changes were specified on the command line, apply them.
+ if ($cl) {
+ if ($text) {
+ # We're updating forms from the server.
+ my $forms = Form::parse($text);
+
+ foreach my $form (@$forms) {
+ my ($c, $o, $k, $e) = @$form;
+ my ($key, $val);
+
+ next if ($e || !@$o);
+
+ local %add = %add;
+ local %del = %del;
+ local %set = %set;
+
+ # Make changes to existing fields.
+ foreach $key (@$o) {
+ if (exists $add{lc $key}) {
+ $val = delete $add{lc $key};
+ vpush($k, $key, $val);
+ $k->{$key} = vsplit($k->{$key}) if $val =~ /[,\n]/;
+ }
+ if (exists $del{lc $key}) {
+ $val = delete $del{lc $key};
+ my %val = map {$_=>1} @{ vsplit($val) };
+ $k->{$key} = vsplit($k->{$key});
+ @{$k->{$key}} = grep {!exists $val{$_}} @{$k->{$key}};
+ }
+ if (exists $set{lc $key}) {
+ $k->{$key} = delete $set{lc $key};
+ }
+ }
+
+ # Then update the others.
+ foreach $key (keys %set) { vpush($k, $key, $set{$key}) }
+ foreach $key (keys %add) {
+ vpush($k, $key, $add{$key});
+ $k->{$key} = vsplit($k->{$key});
+ }
+ push @$o, (keys %add, keys %set);
+ }
+
+ $text = Form::compose($forms);
+ }
+ else {
+ # We're rolling our own set of forms.
+ my @forms;
+ foreach (@objects) {
+ my ($type, $ids, $args) =
+ m{^($name)/($idlist|$labels)(?:(/.*))?$}o;
+
+ $args ||= "";
+ foreach my $obj (expand_list($ids)) {
+ my %set = (%set, id => "$type/$obj$args");
+ push @forms, ["", [keys %set], \%set];
+ }
+ }
+ $text = Form::compose(\@forms);
+ }
+ }
+
+ if ($output) {
+ print $text;
+ return;
+ }
+
+ 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;
+ }
+ }
+ print $r->content;
+ }
+}
+
+# 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;
+ }
+ push @files, shift @ARGV;
+ }
+ elsif (/-([bc])/) {
+ my $a = $_ eq "-b" ? \@bcc : \@cc;
+ @$a = split /\s*,\s*/, shift @ARGV;
+ }
+ elsif (/-m/) {
+ $msg = shift @ARGV;
+ if ( $msg =~ /^-$/ ) {
+ undef $msg;
+ while (<STDIN>) { $msg .= $_ }
+ }
+ }
+
+ elsif (/-w/) { $wtime = shift @ARGV }
+ }
+ elsif (!$id && m|^(?:ticket/)?($idlist)$|) {
+ $id = $1;
+ }
+ else {
+ my $datum = /^-/ ? "option" : "argument";
+ whine "Unrecognised $datum '$_'.";
+ $bad = 1; last;
+ }
+ }
+
+ unless ($id) {
+ whine "No object specified.";
+ $bad = 1;
+ }
+ #return help($action, "ticket") if $bad;
+ return suggest_help($action, "ticket") if $bad;
+
+ my $form = [
+ "",
+ [ "Ticket", "Action", "Cc", "Bcc", "Attachment", "TimeWorked", "Text" ],
+ {
+ Ticket => $id,
+ Action => $action,
+ Cc => [ @cc ],
+ Bcc => [ @bcc ],
+ Attachment => [ @files ],
+ TimeWorked => $wtime || '',
+ Text => $msg || '',
+ Status => ''
+ }
+ ];
+
+ my $text = Form::compose([ $form ]);
+
+ if ($edit || !$msg) {
+ my $error = 0;
+ my ($c, $o, $k, $e);
+
+ do {
+ my $ntext = vi($text);
+ return if ($error && $ntext eq $text);
+ $text = $ntext;
+ $form = Form::parse($text);
+ $error = 0;
+
+ ($c, $o, $k, $e) = @{ $form->[0] };
+ if ($e) {
+ $error = 1;
+ $c = "# Syntax error.";
+ goto NEXT;
+ }
+ elsif (!@$o) {
+ return;
+ }
+ @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;
+}
+
+# Merge one ticket into another.
+
+sub merge {
+ my @id;
+ my $bad = 0;
+
+ while (@ARGV) {
+ $_ = shift @ARGV;
+
+ 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") if $bad;
+
+ my $r = submit("$REST/ticket/$id[0]/merge/$id[1]");
+ print $r->content;
+}
+
+# Link one ticket to another.
+
+sub link {
+ my ($bad, $del, %data) = (0, 0, ());
+ my %ltypes = map { lc $_ => $_ } qw(DependsOn DependedOnBy RefersTo
+ ReferredToBy HasMember MemberOf);
+
+ while (@ARGV && $ARGV[0] =~ /^-/) {
+ $_ = shift @ARGV;
+
+ if (/^-d$/) {
+ $del = 1;
+ }
+ else {
+ whine "Unrecognised option: '$_'.";
+ $bad = 1; last;
+ }
+ }
+
+ if (@ARGV == 3) {
+ my ($from, $rel, $to) = @ARGV;
+ if ($from !~ /^\d+$/ || $to !~ /^\d+$/) {
+ my $bad = $from =~ /^\d+$/ ? $to : $from;
+ whine "Invalid ticket ID '$bad' specified.";
+ $bad = 1;
+ }
+ unless (exists $ltypes{lc $rel}) {
+ whine "Invalid link '$rel' 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 help("link", "ticket") if $bad;
+ return suggest_help("link", "ticket") if $bad;
+
+ my $r = submit("$REST/ticket/link", \%data);
+ print $r->content;
+}
+
+# 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") if $bad;
+
+ my $r = submit("$REST/ticket/$id/take", \%data);
+ print $r->content;
+}
+
+# Grant/revoke a user's rights.
+
+sub grant {
+ my ($cmd) = @_;
+
+ my $revoke = 0;
+ while (@ARGV) {
+ }
+
+ $revoke = 1 if $cmd->{action} eq 'revoke';
+}
+
+# Client <-> Server communication.
+# --------------------------------
+#
+# This function composes and sends an HTTP request to the RT server, and
+# interprets the response. It takes a request URI, and optional request
+# data (a string, or a reference to a set of key-value pairs).
+
+sub submit {
+ my ($uri, $content) = @_;
+ my ($req, $data);
+ my $ua = new LWP::UserAgent(agent => "RT/3.0b", env_proxy => 1);
+
+ # 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?
+ if (!defined $session->cookie) {
+ 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);
+
+ # Then we send the request and parse the response.
+ DEBUG(3, $req->as_string);
+ my $res = $ua->request($req);
+ DEBUG(3, $res->as_string);
+
+ if ($res->is_success) {
+ # The content of the response we get from the RT server consists
+ # of an HTTP-like status line followed by optional header lines,
+ # a blank line, and arbitrary text.
+
+ my ($head, $text) = split /\n\n/, $res->content, 2;
+ my ($status, @headers) = split /\n/, $head;
+ $text =~ s/\n*$/\n/ if ($text);
+
+ # "RT/3.0.1 401 Credentials required"
+ if ($status !~ m#^RT/\d+(?:\S+) (\d+) ([\w\s]+)$#) {
+ warn "rt: Malformed RT response from $config{server}.\n";
+ warn "(Rerun with RTDEBUG=3 for details.)\n" if $config{debug} < 3;
+ exit -1;
+ }
+
+ # Our caller can pretend that the server returned a custom HTTP
+ # response code and message. (Doing that directly is apparently
+ # not sufficiently portable and uncomplicated.)
+ $res->code($1);
+ $res->message($2);
+ $res->content($text);
+ $session->update($res) if ($res->is_success || $res->code != 401);
+
+ if (!$res->is_success) {
+ # We can deal with authentication failures ourselves. Either
+ # we sent invalid credentials, or our session has expired.
+ if ($res->code == 401) {
+ my %d = @$data;
+ if (exists $d{user}) {
+ warn "rt: Incorrect username or password.\n";
+ exit -1;
+ }
+ elsif ($req->header("Cookie")) {
+ # We'll retry the request with credentials, unless
+ # we only wanted to logout in the first place.
+ $session->delete;
+ return submit(@_) unless $uri eq "$REST/logout";
+ }
+ }
+ # Conflicts should be dealt with by the handler and user.
+ # For anything else, we just die.
+ elsif ($res->code != 409) {
+ warn "rt: ", $res->content;
+ #exit;
+ }
+ }
+ }
+ else {
+ warn "rt: Server error: ", $res->message, " (", $res->code, ")\n";
+ exit -1;
+ }
+
+ return $res;
+}
+
+# Session management.
+# -------------------
+#
+# Maintains a list of active sessions in the ~/.rt_sessions file.
+{
+ package Session;
+ my ($s, $u);
+
+ # Initialises the session cache.
+ sub new {
+ my ($class, $file) = @_;
+ my $self = {
+ file => $file || "$HOME/.rt_sessions",
+ sids => { }
+ };
+
+ # The current session is identified by the currently configured
+ # server and user.
+ ($s, $u) = @config{"server", "user"};
+
+ bless $self, $class;
+ $self->load();
+
+ return $self;
+ }
+
+ # Returns the current session cookie.
+ sub cookie {
+ my ($self) = @_;
+ my $cookie = $self->{sids}{$s}{$u};
+ return defined $cookie ? "RT_SID_$cookie" : undef;
+ }
+
+ # Deletes the current session cookie.
+ sub delete {
+ my ($self) = @_;
+ delete $self->{sids}{$s}{$u};
+ }
+
+ # Adds a Cookie header to an outgoing HTTP request.
+ sub add_cookie_header {
+ my ($self, $request) = @_;
+ my $cookie = $self->cookie();
+
+ $request->header(Cookie => $cookie) if defined $cookie;
+ }
+
+ # Extracts the Set-Cookie header from an HTTP response, and updates
+ # session information accordingly.
+ sub update {
+ my ($self, $response) = @_;
+ my $cookie = $response->header("Set-Cookie");
+
+ if (defined $cookie && $cookie =~ /^RT_SID_(.[^;,\s]+=[0-9A-Fa-f]+);/) {
+ $self->{sids}{$s}{$u} = $1;
+ }
+ }
+
+ # Loads the session cache from the specified file.
+ sub load {
+ my ($self, $file) = @_;
+ $file ||= $self->{file};
+ local *F;
+
+ open(F, $file) && do {
+ $self->{file} = $file;
+ my $sids = $self->{sids} = {};
+ while (<F>) {
+ chomp;
+ next if /^$/ || /^#/;
+ next unless m#^https?://[^ ]+ \w+ [^;,\s]+=[0-9A-Fa-f]+$#;
+ my ($server, $user, $cookie) = split / /, $_;
+ $sids->{$server}{$user} = $cookie;
+ }
+ return 1;
+ };
+ return 0;
+ }
+
+ # Writes the current session cache to the specified file.
+ sub save {
+ my ($self, $file) = shift;
+ $file ||= $self->{file};
+ local *F;
+
+ open(F, ">$file") && do {
+ my $sids = $self->{sids};
+ foreach my $server (keys %$sids) {
+ foreach my $user (keys %{ $sids->{$server} }) {
+ my $sid = $sids->{$server}{$user};
+ if (defined $sid) {
+ print F "$server $user $sid\n";
+ }
+ }
+ }
+ close(F);
+ chmod 0600, $file;
+ return 1;
+ };
+ return 0;
+ }
+
+ sub DESTROY {
+ my $self = shift;
+ $self->save;
+ }
+}
+
+# Form handling.
+# --------------
+#
+# Forms are RFC822-style sets of (field, value) specifications with some
+# initial comments and interspersed blank lines allowed for convenience.
+# Sets of forms are separated by --\n (in a cheap parody of MIME).
+#
+# Each form is parsed into an array with four elements: commented text
+# at the start of the form, an array with the order of keys, a hash with
+# key/value pairs, and optional error text if the form syntax was wrong.
+
+# Returns a reference to an array of parsed forms.
+sub Form::parse {
+ my $state = 0;
+ my @forms = ();
+ my @lines = split /\n/, $_[0];
+ 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 ("DEBUG", "USER", "PASSWD", "SERVER", "QUERY", "ORDERBY") {
+ if (exists $ENV{"RT$k"}) {
+ $env{lc $k} = $ENV{"RT$k"};
+ }
+ }
+
+ return %env;
+}
+
+# Finds a suitable configuration file and returns information from it.
+sub config_from_file {
+ my ($rc) = @_;
+
+ if ($rc =~ m#^/#) {
+ # We'll use an absolute path if we were given one.
+ return parse_config_file($rc);
+ }
+ else {
+ # Otherwise we'll use the first file we can find in the current
+ # directory, or in one of its (increasingly distant) ancestors.
+
+ my @dirs = split /\//, cwd;
+ while (@dirs) {
+ my $file = join('/', @dirs, $rc);
+ if (-r $file) {
+ return parse_config_file($file);
+ }
+
+ # Remove the last directory component each time.
+ pop @dirs;
+ }
+
+ # Still nothing? We'll fall back to some likely defaults.
+ for ("$HOME/$rc", "/etc/rt.conf") {
+ return parse_config_file($_) if (-r $_);
+ }
+ }
+
+ return ();
+}
+
+# Makes a hash of the specified configuration file.
+sub parse_config_file {
+ my %cfg;
+ my ($file) = @_;
+ local $_; # $_ may be aliased to a constant, from line 1163
+
+ open(CFG, $file) && do {
+ while (<CFG>) {
+ chomp;
+ next if (/^#/ || /^\s*$/);
+
+ if (/^(user|passwd|server|query|orderby)\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;
+}
+
+sub read_passwd {
+ eval 'require Term::ReadKey';
+ if ($@) {
+ die "No password specified (and Term::ReadKey not installed).\n";
+ }
+
+ print "Password: ";
+ Term::ReadKey::ReadMode('noecho');
+ chomp(my $passwd = Term::ReadKey::ReadLine(0));
+ Term::ReadKey::ReadMode('restore');
+ print "\n";
+
+ return $passwd;
+}
+
+sub vi {
+ my ($text) = @_;
+ my $file = "/tmp/rt.form.$$";
+ my $editor = $ENV{EDITOR} || $ENV{VISUAL} || "vi";
+
+ local *F;
+ local $/ = undef;
+
+ open(F, ">$file") || die "$file: $!\n"; print F $text; close(F);
+ system($editor, $file) && die "Couldn't run $editor.\n";
+ open(F, $file) || die "$file: $!\n"; $text = <F>; close(F);
+ unlink($file);
+
+ return $text;
+}
+
+# Add a value to a (possibly multi-valued) hash key.
+sub vpush {
+ my ($hash, $key, $val) = @_;
+ my @val = ref $val eq 'ARRAY' ? @$val : $val;
+
+ if (exists $hash->{$key}) {
+ unless (ref $hash->{$key} eq 'ARRAY') {
+ my @v = $hash->{$key} ne '' ? $hash->{$key} : ();
+ $hash->{$key} = \@v;
+ }
+ push @{ $hash->{$key} }, @val;
+ }
+ else {
+ $hash->{$key} = $val;
+ }
+}
+
+# "Normalise" a hash key that's known to be multi-valued.
+sub vsplit {
+ my ($val) = @_;
+ my ($word, @words);
+ my @values = ref $val eq 'ARRAY' ? @$val : $val;
+
+ foreach my $line (map {split /\n/} @values) {
+ # XXX: This should become a real parser, à la Text::ParseWords.
+ $line =~ s/^\s+//;
+ $line =~ s/\s+$//;
+ push @words, split /\s*,\s*/, $line;
+ }
+
+ return \@words;
+}
+
+# WARN: this code is duplicated in lib/RT/Interface/REST.pm
+# change both functions at once
+sub expand_list {
+ my ($list) = @_;
+
+ my @elts;
+ foreach (split /,/, $list) {
+ push @elts, /^(\d+)-(\d+)$/? ($1..$2): $_;
+ }
+
+ return map $_->[0], # schwartzian transform
+ sort {
+ defined $a->[1] && defined $b->[1]?
+ # both numbers
+ $a->[1] <=> $b->[1]
+ :!defined $a->[1] && !defined $b->[1]?
+ # both letters
+ $a->[2] cmp $b->[2]
+ # mix, number must be first
+ :defined $a->[1]? -1: 1
+ }
+ map [ $_, (defined( /^(\d+)$/ )? $1: undef), lc($_) ],
+ @elts;
+}
+
+sub get_type_argument {
+ my $type;
+
+ if (@ARGV) {
+ $type = shift @ARGV;
+ unless ($type =~ /^[A-Za-z0-9_.-]+$/) {
+ # We want whine to mention our caller, not us.
+ @_ = ("Invalid type '$type' specified.");
+ goto &whine;
+ }
+ }
+ else {
+ @_ = ("No type argument specified with -t.");
+ goto &whine;
+ }
+
+ $type =~ s/s$//; # "Plural". Ugh.
+ return $type;
+}
+
+sub get_var_argument {
+ my ($data) = @_;
+
+ if (@ARGV) {
+ my $kv = shift @ARGV;
+ if (my ($k, $v) = $kv =~ /^($field)=(.*)$/) {
+ push @{ $data->{$k} }, $v;
+ }
+ else {
+ @_ = ("Invalid variable specification: '$kv'.");
+ goto &whine;
+ }
+ }
+ else {
+ @_ = ("No variable argument specified with -S.");
+ goto &whine;
+ }
+}
+
+sub is_object_spec {
+ my ($spec, $type) = @_;
+
+ $spec =~ s|^(?:$type/)?|$type/| if defined $type;
+ return $spec if ($spec =~ m{^$name/(?:$idlist|$labels)(?:/.*)?$}o);
+ return;
+}
+
+sub suggest_help {
+ my ($action, $type) = @_;
+
+ 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;
+}
+
+__DATA__
+
+Title: intro
+Title: introduction
+Text:
+
+ ** THIS IS AN UNSUPPORTED PREVIEW RELEASE **
+ ** PLEASE REPORT BUGS TO rt-bugs@bestpractical.com **
+
+ This is a command-line interface to RT 3.0 or newer
+
+ It allows you to interact with an RT server over HTTP, and offers an
+ interface to RT's functionality that is better-suited to automation
+ and integration with other tools.
+
+ In general, each invocation of this program should specify an action
+ to perform on one or more objects, and any other arguments required
+ to complete the desired action.
+
+ For more information:
+
+ - rt help usage (syntax information)
+ - rt help objects (how to specify objects)
+ - rt help actions (a list of possible actions)
+ - rt help types (a list of object types)
+
+ - rt help config (configuration details)
+ - rt help examples (a few useful examples)
+ - rt help topics (a list of help topics)
+
+--
+
+Title: usage
+Title: syntax
+Text:
+
+ Syntax:
+
+ rt <action> [options] [arguments]
+ or
+ rt shell
+
+ Each invocation of this program must specify an action (e.g. "edit",
+ "create"), options to modify behaviour, and other arguments required
+ by the specified action. (For example, most actions expect a list of
+ numeric object IDs to act upon.)
+
+ The details of the syntax and arguments for each action are given by
+ "rt help <action>". Some actions may be referred to by more than one
+ name ("create" is the same as "new", for example).
+
+ You may also call "rt shell", which will give you an 'rt>' prompt at
+ which you can issue commands of the form "<action> [options]
+ [arguments]". See "rt help shell" for details.
+
+ Objects are identified by a type and an ID (which can be a name or a
+ number, depending on the type). For some actions, the object type is
+ implied (you can only comment on tickets); for others, the user must
+ specify it explicitly. See "rt help objects" for details.
+
+ In syntax descriptions, mandatory arguments that must be replaced by
+ appropriate value are enclosed in <>, and optional arguments are
+ indicated by [] (for example, <action> and [options] above).
+
+ For more information:
+
+ - rt help objects (how to specify objects)
+ - rt help actions (a list of actions)
+ - rt help types (a list of object types)
+ - rt help shell (how to use the shell)
+
+--
+
+Title: conf
+Title: config
+Title: configuration
+Text:
+
+ This program has two major sources of configuration information: its
+ configuration files, and the environment.
+
+ The program looks for configuration directives in a file named .rtrc
+ (or $RTCONFIG; see below) in the current directory, and then in more
+ distant ancestors, until it reaches /. If no suitable configuration
+ files are found, it will also check for ~/.rtrc and /etc/rt.conf.
+
+ Configuration directives:
+
+ The following directives may occur, one per line:
+
+ - server <URL> URL to RT server.
+ - user <username> RT username.
+ - passwd <passwd> RT user's password.
+ - query <RT Query> Default RT Query for list action
+ - orderby <order> Default RT order for list action
+
+ Blank and #-commented lines are ignored.
+
+ Environment variables:
+
+ The following environment variables override any corresponding
+ values defined in configuration files:
+
+ - RTUSER
+ - RTPASSWD
+ - RTSERVER
+ - RTDEBUG Numeric debug level. (Set to 3 for full logs.)
+ - RTCONFIG Specifies a name other than ".rtrc" for the
+ configuration file.
+ - RTQUERY Default RT Query for rt list
+ - RTORDERBY Default order for rt list
+
+--
+
+Title: objects
+Text:
+
+ Syntax:
+
+ <type>/<id>[/<attributes>]
+
+ Every object in RT has a type (e.g. "ticket", "queue") and a numeric
+ ID. Some types of objects can also be identified by name (like users
+ and queues). Furthermore, objects may have named attributes (such as
+ "ticket/1/history").
+
+ An object specification is like a path in a virtual filesystem, with
+ object types as top-level directories, object IDs as subdirectories,
+ and named attributes as further subdirectories.
+
+ A comma-separated list of names, numeric IDs, or numeric ranges can
+ be used to specify more than one object of the same type. Note that
+ the list must be a single argument (i.e., no spaces). For example,
+ "user/root,1-3,5,7-10,ams" is a list of ten users; the same list
+ can also be written as "user/ams,root,1,2,3,5,7,8-10".
+
+ Examples:
+
+ ticket/1
+ ticket/1/attachments
+ ticket/1/attachments/3
+ ticket/1/attachments/3/content
+ ticket/1-3/links
+ ticket/1-3,5-7/history
+
+ user/ams
+ user/ams/rights
+ user/ams,rai,1/rights
+
+ For more information:
+
+ - rt help <action> (action-specific details)
+ - rt help <type> (type-specific details)
+
+--
+
+Title: actions
+Title: commands
+Text:
+
+ You can currently perform the following actions on all objects:
+
+ - list (list objects matching some condition)
+ - show (display object details)
+ - edit (edit object details)
+ - create (create a new object)
+
+ Each type may define actions specific to itself; these are listed in
+ the help item about that type.
+
+ For more information:
+
+ - rt help <action> (action-specific details)
+ - rt help types (a list of possible types)
+
+--
+
+Title: types
+Text:
+
+ You can currently operate on the following types of objects:
+
+ - tickets
+ - users
+ - groups
+ - queues
+
+ For more information:
+
+ - rt help <type> (type-specific details)
+ - rt help objects (how to specify objects)
+ - rt help actions (a list of possible actions)
+
+--
+
+Title: ticket
+Text:
+
+ Tickets are identified by a numeric ID.
+
+ The following generic operations may be performed upon tickets:
+
+ - list
+ - show
+ - edit
+ - create
+
+ In addition, the following ticket-specific actions exist:
+
+ - link
+ - merge
+ - comment
+ - correspond
+
+ Attributes:
+
+ The following attributes can be used with "rt show" or "rt edit"
+ to retrieve or edit other information associated with tickets:
+
+ links A ticket's relationships with others.
+ history All of a ticket's transactions.
+ history/type/<type> Only a particular type of transaction.
+ history/id/<id> Only the transaction of the specified id.
+ attachments A list of attachments.
+ attachments/<id> The metadata for an individual attachment.
+ attachments/<id>/content The content of an individual attachment.
+
+--
+
+Title: user
+Title: group
+Text:
+
+ Users and groups are identified by name or numeric ID.
+
+ The following generic operations may be performed upon them:
+
+ - list
+ - show
+ - edit
+ - create
+
+ In addition, the following type-specific actions exist:
+
+ - grant
+ - revoke
+
+ Attributes:
+
+ The following attributes can be used with "rt show" or "rt edit"
+ to retrieve or edit other information associated with users and
+ groups:
+
+ rights Global rights granted to this user.
+ rights/<queue> Queue rights for this user.
+
+--
+
+Title: queue
+Text:
+
+ Queues are identified by name or numeric ID.
+
+ Currently, they can be subjected to the following actions:
+
+ - show
+ - edit
+ - create
+
+--
+
+Title: logout
+Text:
+
+ Syntax:
+
+ rt logout
+
+ Terminates the currently established login session. You will need to
+ provide authentication credentials before you can continue using the
+ server. (See "rt help config" for details about authentication.)
+
+--
+
+Title: ls
+Title: list
+Title: search
+Text:
+
+ Syntax:
+
+ rt <ls|list|search> [options] "query string"
+
+ Displays a list of objects matching the specified conditions.
+ ("ls", "list", and "search" are synonyms.)
+
+ Conditions are expressed in the SQL-like syntax used internally by
+ RT3. (For more information, see "rt help query".) The query string
+ must be supplied as one argument.
+
+ (Right now, the server doesn't support listing anything but tickets.
+ Other types will be supported in future; this client will be able to
+ take advantage of that support without any changes.)
+
+ Options:
+
+ The following options control how much information is displayed
+ about each matching object:
+
+ -i Numeric IDs only. (Useful for |rt edit -; see examples.)
+ -s Short description.
+ -l Longer description.
+
+ In addition,
+
+ -o +/-<field> Orders the returned list by the specified field.
+ -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]%'"
+
+--
+
+Title: show
+Text:
+
+ Syntax:
+
+ rt show [options] <object-ids>
+
+ Displays details of the specified objects.
+
+ For some types, object information is further classified into named
+ attributes (for example, "1-3/links" is a valid ticket specification
+ that refers to the links for tickets 1-3). Consult "rt help <type>"
+ and "rt help objects" for further details.
+
+ This command writes a set of forms representing the requested object
+ data to STDOUT.
+
+ Options:
+
+ - 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.
+ -v Verbose display
+ 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 -v ticket/3/history
+ rt show -t user 2
+
+--
+
+Title: new
+Title: edit
+Title: create
+Text:
+
+ Syntax:
+
+ rt edit [options] <object-ids> set field=value [field=value] ...
+ add field=value [field=value] ...
+ del field=value [field=value] ...
+
+ Edits information corresponding to the specified objects.
+
+ If, instead of "edit", an action of "new" or "create" is specified,
+ then a new object is created. In this case, no numeric object IDs
+ may be specified, but the syntax and behaviour remain otherwise
+ unchanged.
+
+ This command typically starts an editor to allow you to edit object
+ data in a form for submission. If you specified enough information
+ on the command-line, however, it will make the submission directly.
+
+ The command line may specify field-values in three different ways.
+ "set" sets the named field to the given value, "add" adds a value
+ to a multi-valued field, and "del" deletes the corresponding value.
+ Each "field=value" specification must be given as a single argument.
+
+ For some types, object information is further classified into named
+ attributes (for example, "1-3/links" is a valid ticket specification
+ that refers to the links for tickets 1-3). These attributes may also
+ be edited. Consult "rt help <type>" and "rt help object" for further
+ details.
+
+ Options:
+
+ - Read numeric IDs from STDIN instead of the command-line.
+ (Useful with rt ls ... | rt edit -; see examples below.)
+ -i Read a completed form from STDIN before submitting.
+ -o Dump the completed form to STDOUT instead of submitting.
+ -e Allows you to edit the form even if the command-line has
+ enough information to make a submission directly.
+ -S var=val
+ Submits the specified variable with the request.
+ -t type Specifies object type.
+
+ Examples:
+
+ # Interactive (starts $EDITOR with a form).
+ rt edit ticket/3
+ rt create -t ticket
+
+ # Non-interactive.
+ rt edit ticket/1-3 add cc=foo@example.com set priority=3
+ rt ls -t tickets -i 'Priority > 5' | rt edit - set status=resolved
+ rt edit ticket/4 set priority=3 owner=bar@example.com \
+ add cc=foo@example.com bcc=quux@example.net
+ rt create -t ticket set subject='new ticket' priority=10 \
+ add cc=foo@example.com
+
+--
+
+Title: comment
+Title: correspond
+Text:
+
+ Syntax:
+
+ rt <comment|correspond> [options] <ticket-id>
+
+ Adds a comment (or correspondence) to the specified ticket (the only
+ difference being that comments aren't sent to the requestors.)
+
+ This command will typically start an editor and allow you to type a
+ comment into a form. If, however, you specified all the necessary
+ information on the command line, it submits the comment directly.
+
+ (See "rt help forms" for more information about forms.)
+
+ Options:
+
+ -m <text> Specify comment text.
+ -a <file> Attach a file to the comment. (May be used more
+ than once to attach multiple files.)
+ -c <addrs> A comma-separated list of Cc addresses.
+ -b <addrs> A comma-separated list of Bcc addresses.
+ -w <time> Specify the time spent working on this ticket.
+ -e Starts an editor before the submission, even if
+ arguments from the command line were sufficient.
+
+ Examples:
+
+ rt comment -m 'Not worth fixing.' -a stddisclaimer.h 23
+
+--
+
+Title: merge
+Text:
+
+ Syntax:
+
+ rt merge <from-id> <to-id>
+
+ Merges the first ticket specified into the second ticket specified.
+
+--
+
+Title: link
+Text:
+
+ Syntax:
+
+ rt link [-d] <id-A> <link> <id-B>
+
+ Creates (or, with -d, deletes) a link between the specified tickets.
+ The link can (irrespective of case) be any of:
+
+ DependsOn/DependedOnBy: A depends upon B (or vice versa).
+ RefersTo/ReferredToBy: A refers to B (or vice versa).
+ MemberOf/HasMember: A is a member of B (or vice versa).
+
+ To view a ticket's links, use "rt show ticket/3/links". (See
+ "rt help ticket" and "rt help show".)
+
+ Options:
+
+ -d Deletes the specified link.
+
+ Examples:
+
+ rt link 2 dependson 3
+ rt link -d 4 referredtoby 6 # 6 no longer refers to 4
+
+--
+
+Title: grant
+Title: revoke
+Text:
+
+--
+
+Title: query
+Text:
+
+ RT3 uses an SQL-like syntax to specify object selection constraints.
+ See the <RT:...> documentation for details.
+
+ (XXX: I'm going to have to write it, aren't I?)
+
+--
+
+Title: form
+Title: forms
+Text:
+
+ This program uses RFC822 header-style forms to represent object data
+ in a form that's suitable for processing both by humans and scripts.
+
+ A form is a set of (field, value) specifications, with some initial
+ commented text and interspersed blank lines allowed for convenience.
+ Field names may appear more than once in a form; a comma-separated
+ list of multiple field values may also be specified directly.
+
+ Field values can be wrapped as in RFC822, with leading whitespace.
+ The longest sequence of leading whitespace common to all the lines
+ is removed (preserving further indentation). There is no limit on
+ the length of a value.
+
+ Multiple forms are separated by a line containing only "--\n".
+
+ (XXX: A more detailed specification will be provided soon. For now,
+ the server-side syntax checking will suffice.)
+
+--
+
+Title: topics
+Text:
+
+ Syntax:
+
+ rt help <topic>
+
+ Get help on any of the following subjects:
+
+ - tickets, users, groups, queues.
+ - show, edit, ls/list/search, new/create.
+
+ - query (search query syntax)
+ - forms (form specification)
+
+ - objects (how to specify objects)
+ - types (a list of object types)
+ - actions/commands (a list of actions)
+ - usage/syntax (syntax details)
+ - conf/config/configuration (configuration details)
+ - examples (a few useful examples)
+
+--
+
+Title: example
+Title: examples
+Text:
+
+ This section will be filled in with useful examples, once it becomes
+ more clear what examples may be useful.
+
+ For the moment, please consult examples provided with each action.
+
+--
+
+Title: shell
+Text:
+
+ Syntax:
+
+ rt shell
+
+ Opens an interactive shell, at which you can issue commands of
+ the form "<action> [options] [arguments]".
+
+ To exit the shell, type "quit" or "exit".
+
+ Commands can be given at the shell in the same form as they would
+ be given at the command line without the leading 'rt' invocation.
+
+ Example:
+ $ rt shell
+ rt> create -t ticket set subject='new' add cc=foo@example.com
+ # Ticket 8 created.
+ rt> quit
+ $
+
+--
+
+Title: take
+Title: untake
+Title: steal
+Text:
+
+ Syntax:
+
+ rt <take|untake|steal> <ticket-id>
+
+ Sets the owner of the specified ticket to the current user,
+ assuming said user has the bits to do so, or releases the
+ ticket.
+
+ 'Take' is used on tickets which are not currently owned
+ (Owner: Nobody), 'steal' is used on tickets which *are*
+ currently owned, and 'untake' is used to "release" a ticket
+ (reset its Owner to Nobody). 'Take' cannot be used on
+ tickets which are currently owned.
+
+ Example:
+ alice$ rt create -t ticket set subject="New ticket"
+ # Ticket 7 created.
+ alice$ rt take 7
+ # Owner changed from Nobody to alice
+ alice$ su bob
+ bob$ rt steal 7
+ # Owner changed from alice to bob
+ bob$ rt untake 7
+ # Owner changed from bob to Nobody
+
+--
+
+Title: quit
+Title: exit
+Text:
+
+ Use "quit" or "exit" to leave the shell. Only valid within shell
+ mode.
+
+ Example:
+ $ rt shell
+ rt> quit
+ $
diff --git a/rt/bin/standalone_httpd.in b/rt/bin/standalone_httpd.in
new file mode 100755
index 0000000..c26e2a5
--- /dev/null
+++ b/rt/bin/standalone_httpd.in
@@ -0,0 +1,67 @@
+#!@PERL@ -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use warnings;
+use strict;
+
+BEGIN {
+ use lib( "@LOCAL_LIB_PATH@", "@RT_LIB_PATH@");
+ use RT;
+ RT::LoadConfig();
+ if ($RT::DevelMode) { require Module::Refresh; }
+}
+
+RT::Init();
+
+my $port = shift @ARGV || $RT::WebPort || '8080';
+use RT::Interface::Web::Standalone;
+my $server = RT::Interface::Web::Standalone->new;
+$server->port($port);
+$server->run();
+
+
diff --git a/rt/bin/webmux.pl.in b/rt/bin/webmux.pl.in
new file mode 100644
index 0000000..f9b792f
--- /dev/null
+++ b/rt/bin/webmux.pl.in
@@ -0,0 +1,137 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+
+BEGIN {
+ $ENV{'PATH'} = '/bin:/usr/bin'; # or whatever you need
+ $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
+ $ENV{'SHELL'} = '/bin/sh' if defined $ENV{'SHELL'};
+ $ENV{'ENV'} = '' if defined $ENV{'ENV'};
+ $ENV{'IFS'} = '' if defined $ENV{'IFS'};
+
+ use CGI qw(-private_tempfiles); #bring this in before mason, to make sure we
+ #set private_tempfiles
+
+ die "RT does not support mod_perl 1.99. Please upgrade to mod_perl 2.0"
+ if $ENV{'MOD_PERL'}
+ and $ENV{'MOD_PERL'} =~ m{mod_perl/(?:1\.9)};
+
+}
+
+use lib ( "@LOCAL_LIB_PATH@", "@RT_LIB_PATH@" );
+use RT;
+
+package RT::Mason;
+
+use vars qw($Nobody $SystemUser $Handler $r);
+
+#This drags in RT's config.pm
+BEGIN {
+ RT::LoadConfig();
+ if ($RT::DevelMode) { require Module::Refresh; }
+}
+
+
+{
+
+ package HTML::Mason::Commands;
+ use vars qw(%session);
+}
+
+use RT::Interface::Web;
+use RT::Interface::Web::Handler;
+$Handler = RT::Interface::Web::Handler->new(@RT::MasonParameters);
+
+if ($ENV{'MOD_PERL'} && !$RT::DevelMode) {
+ # Under static_source, we need to purge the component cache
+ # each time we restart, so newer components may be reloaded.
+ #
+ # We can't do this in FastCGI or we'll blow away the component root _every_ time a new server starts
+ # which happens every few hits.
+
+ use File::Path qw( rmtree );
+ use File::Glob qw( bsd_glob );
+ my @files = bsd_glob("$RT::MasonDataDir/obj/*");
+ rmtree([ @files ], 0, 1) if @files;
+}
+
+sub handler {
+ ($r) = @_;
+
+ local $SIG{__WARN__};
+ local $SIG{__DIE__};
+
+ if ($r->content_type =~ m/^httpd\b.*\bdirectory/i) {
+ use File::Spec::Unix;
+ # Our DirectoryIndex is always index.html, regardless of httpd settings
+ $r->filename( File::Spec::Unix->catfile( $r->filename, 'index.html' ) );
+ }
+# elsif (defined( $r->content_type )) {
+ #$r->content_type !~ m!(^text/|\bxml\b)!i or return -1;
+# }
+
+ Module::Refresh->refresh if $RT::DevelMode;
+
+ RT::Init();
+
+ my %session;
+ my $status;
+ eval { $status = $Handler->handle_request($r) };
+ if ($@) {
+ $RT::Logger->crit($@);
+ }
+
+ undef(%session);
+
+ RT::Interface::Web::Handler->CleanupRequest();
+
+ return $status;
+}
+
+1;
diff --git a/rt/config.layout.in b/rt/config.layout.in
new file mode 100644
index 0000000..a08f489
--- /dev/null
+++ b/rt/config.layout.in
@@ -0,0 +1,127 @@
+##
+## config.layout -- Pre-defined Installation Path Layouts
+##
+## Hints:
+## - layouts can be loaded with configure's --enable-layout=ID option
+## - when no --enable-layout option is given, the default layout is `RT'
+## - a trailing plus character (`+') on paths is replaced with a
+## `/<target>' suffix where <target> is currently hardcoded to 'rt3'.
+## (This may become a configurable parameter at some point.)
+##
+## The following variables must _all_ be set:
+## prefix exec_prefix bindir sbindir sysconfdir mandir libdir
+## datadir htmldir localstatedir logfiledir masonstatedir
+## sessionstatedir customdir customhtmldir customlexdir
+## (This can be seen in m4/rt_layout.m4.)
+##
+
+# Default RT3 path layout.
+<Layout RT3>
+ prefix: /opt/rt3
+ exec_prefix: ${prefix}
+ bindir: ${exec_prefix}/bin
+ sbindir: ${exec_prefix}/sbin
+ sysconfdir: ${prefix}/etc
+ mandir: ${prefix}/man
+ libdir: ${prefix}/lib
+ datadir: ${prefix}/share
+ htmldir: ${datadir}/html
+ manualdir: ${datadir}/doc
+ localstatedir: ${prefix}/var
+ logfiledir: ${localstatedir}/log
+ masonstatedir: ${localstatedir}/mason_data
+ sessionstatedir: ${localstatedir}/session_data
+ customdir: ${prefix}/local
+ custometcdir: ${customdir}/etc
+ customhtmldir: ${customdir}/html
+ customlexdir: ${customdir}/po
+ customlibdir: ${customdir}/lib
+</Layout>
+<Layout inplace>
+ prefix: `pwd`
+ exec_prefix: ${prefix}
+ bindir: ${exec_prefix}/bin
+ sbindir: ${exec_prefix}/sbin
+ sysconfdir: ${prefix}/etc
+ mandir: ${prefix}/man
+ libdir: ${prefix}/lib
+ datadir: ${prefix}/share
+ htmldir: ${prefix}/html
+ manualdir: ${datadir}/doc
+ localstatedir: ${prefix}/var
+ logfiledir: ${localstatedir}/log
+ masonstatedir: ${localstatedir}/mason_data
+ sessionstatedir: ${localstatedir}/session_data
+ customdir: ${prefix}/local
+ custometcdir: ${customdir}/etc
+ customhtmldir: ${customdir}/html
+ customlexdir: ${customdir}/po
+ customlibdir: ${customdir}/lib
+</Layout>
+
+<Layout FreeBSD>
+ prefix: /usr/local
+ exec_prefix: ${prefix}
+ bindir: ${exec_prefix}/bin
+ sbindir: ${exec_prefix}/sbin
+ sysconfdir: ${prefix}/etc+
+ mandir: ${prefix}/man
+ libdir: ${prefix}/lib+
+ datadir: ${prefix}/share+
+ htmldir: ${datadir}/html
+ manualdir: ${prefix}/share/doc+
+ logfiledir: /var/log
+ localstatedir: /var/run+
+ masonstatedir: ${localstatedir}/mason_data
+ sessionstatedir: ${localstatedir}/session_data
+ customdir: ${prefix}/share+
+ custometcdir: ${customdir}/local/etc
+ customhtmldir: ${customdir}/local/html
+ customlexdir: ${customdir}/local/po
+ customlibdir: ${customdir}/local/lib
+</Layout>
+
+<Layout Win32>
+ prefix: C:/Program Files/Request Tracker
+ exec_prefix: ${prefix}
+ bindir: ${exec_prefix}/bin
+ sbindir: ${exec_prefix}/sbin
+ sysconfdir: ${prefix}/etc
+ mandir: ${prefix}/man
+ libdir: ${prefix}/lib
+ datadir: ${prefix}
+ htmldir: ${datadir}/html
+ manualdir: ${datadir}/doc
+ localstatedir: ${prefix}/var
+ logfiledir: ${localstatedir}/log
+ masonstatedir: ${localstatedir}/mason_data
+ sessionstatedir: ${localstatedir}/session_data
+ customdir: ${prefix}/local
+ custometcdir: ${customdir}/etc
+ customhtmldir: ${customdir}/html
+ customlexdir: ${customdir}/po
+ customlibdir: ${customdir}/lib
+</Layout>
+
+<Layout Freeside>
+ prefix: /opt/rt3
+ exec_prefix: ${prefix}
+ bindir: ${exec_prefix}/bin
+ sbindir: ${exec_prefix}/sbin
+ sysconfdir: ${prefix}/etc
+ mandir: ${prefix}/man
+ libdir: ${prefix}/lib
+ datadir: ${prefix}/share
+ htmldir: %%%FREESIDE_DOCUMENT_ROOT%%%/rt
+ manualdir: ${datadir}/doc
+ localstatedir: ${prefix}/var
+ logfiledir: ${localstatedir}/log
+ masonstatedir: %%%MASONDATA%%%
+ sessionstatedir: ${localstatedir}/session_data
+ customdir: ${prefix}/local
+ custometcdir: ${customdir}/etc
+ customhtmldir: ${customdir}/html
+ customlexdir: ${customdir}/po
+ customlibdir: ${customdir}/lib
+</Layout>
+
diff --git a/rt/config.log b/rt/config.log
new file mode 100644
index 0000000..ab4b65c
--- /dev/null
+++ b/rt/config.log
@@ -0,0 +1,226 @@
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by RT configure 3.6.4, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ ./configure --enable-layout=Freeside --with-db-type=Pg --with-db-dba=freeside --with-db-database=freeside --with-db-rt-user=freeside --with-db-rt-pass= --with-web-user=freeside --with-web-group=freeside --with-rt-group=freeside
+
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = rootwood
+uname -m = x86_64
+uname -r = 2.6.21-1-amd64
+uname -s = Linux
+uname -v = #1 SMP Sat May 26 17:22:54 CEST 2007
+
+/usr/bin/uname -p = unknown
+/bin/uname -X = unknown
+
+/bin/arch = unknown
+/usr/bin/arch -k = unknown
+/usr/convex/getsysinfo = unknown
+hostinfo = unknown
+/bin/machine = unknown
+/usr/bin/oslevel = unknown
+/bin/universe = unknown
+
+PATH: /usr/local/sbin
+PATH: /usr/local/bin
+PATH: /usr/sbin
+PATH: /usr/bin
+PATH: /sbin
+PATH: /bin
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+configure:1331: checking for a BSD-compatible install
+configure:1386: result: /usr/bin/install -c
+configure:1401: checking for gawk
+configure:1417: found /usr/bin/gawk
+configure:1427: result: gawk
+configure:1440: checking for perl
+configure:1458: found /usr/bin/perl
+configure:1471: result: /usr/bin/perl
+configure:1795: checking for chosen layout
+configure:1810: result: Freeside
+configure:2272: creating ./config.status
+
+## ---------------------- ##
+## Running config.status. ##
+## ---------------------- ##
+
+This file was extended by RT config.status 3.6.4, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES =
+ CONFIG_HEADERS =
+ CONFIG_LINKS =
+ CONFIG_COMMANDS =
+ $ ./config.status
+
+on rootwood
+
+config.status:760: creating sbin/rt-dump-database
+config.status:760: creating sbin/rt-setup-database
+config.status:760: creating sbin/rt-test-dependencies
+config.status:760: creating bin/mason_handler.fcgi
+config.status:760: creating bin/mason_handler.scgi
+config.status:760: creating bin/standalone_httpd
+config.status:760: creating bin/rt-crontool
+config.status:760: creating bin/rt-mailgate
+config.status:760: creating bin/rt
+config.status:760: creating Makefile
+config.status:760: creating etc/RT_Config.pm
+config.status:760: creating lib/RT.pm
+config.status:760: creating bin/mason_handler.svc
+config.status:760: creating bin/webmux.pl
+
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+
+ac_cv_env_PERL_set=
+ac_cv_env_PERL_value=
+ac_cv_env_build_alias_set=
+ac_cv_env_build_alias_value=
+ac_cv_env_host_alias_set=
+ac_cv_env_host_alias_value=
+ac_cv_env_target_alias_set=
+ac_cv_env_target_alias_value=
+ac_cv_path_PERL=/usr/bin/perl
+ac_cv_path_install='/usr/bin/install -c'
+ac_cv_prog_AWK=gawk
+
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+
+APACHECTL='/usr/sbin/apachectl'
+AWK='gawk'
+BIN_OWNER='root'
+CONFIG_FILE_PATH='/opt/rt3/etc'
+DATABASE_ENV_PREF=''
+DB_DATABASE='freeside'
+DB_DBA='freeside'
+DB_HOST='localhost'
+DB_PORT=''
+DB_RT_HOST='localhost'
+DB_RT_PASS=''
+DB_RT_USER='freeside'
+DB_TYPE='Pg'
+DEFS='-DPACKAGE_NAME=\"RT\" -DPACKAGE_TARNAME=\"rt\" -DPACKAGE_VERSION=\"3.6.4\" -DPACKAGE_STRING=\"RT\ 3.6.4\" -DPACKAGE_BUGREPORT=\"rt-bugs@bestpractical.com\" '
+DESTDIR='/opt/rt3'
+ECHO_C=''
+ECHO_N='-n'
+ECHO_T=''
+INSTALL_DATA='${INSTALL} -m 644'
+INSTALL_PROGRAM='${INSTALL}'
+INSTALL_SCRIPT='${INSTALL}'
+LIBOBJS=''
+LIBS=''
+LIBS_GROUP='bin'
+LIBS_OWNER='root'
+LOCAL_ETC_PATH='/opt/rt3/local/etc'
+LOCAL_LEXICON_PATH='/opt/rt3/local/po'
+LOCAL_LIB_PATH='/opt/rt3/local/lib'
+LTLIBOBJS=''
+MASON_DATA_PATH='/usr/local/etc/freeside/masondata'
+MASON_HTML_PATH='/var/www/freeside/rt'
+MASON_LOCAL_HTML_PATH='/opt/rt3/local/html'
+MASON_SESSION_PATH='/opt/rt3/var/session_data'
+PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
+PACKAGE_NAME='RT'
+PACKAGE_STRING='RT 3.6.4'
+PACKAGE_TARNAME='rt'
+PACKAGE_VERSION='3.6.4'
+PATH_SEPARATOR=':'
+PERL='/usr/bin/perl'
+RTGROUP='freeside'
+RT_BIN_PATH='/opt/rt3/bin'
+RT_DEVEL_MODE='0'
+RT_DOC_PATH='/opt/rt3/share/doc'
+RT_ETC_PATH='/opt/rt3/etc'
+RT_LIB_PATH='/opt/rt3/lib'
+RT_LOCAL_PATH='/opt/rt3/local'
+RT_LOG_PATH='/opt/rt3/var/log'
+RT_MAN_PATH='/opt/rt3/man'
+RT_PATH='/opt/rt3'
+RT_SBIN_PATH='/opt/rt3/sbin'
+RT_STANDALONE='0'
+RT_VAR_PATH='/opt/rt3/var'
+RT_VERSION_MAJOR='3'
+RT_VERSION_MINOR='6'
+RT_VERSION_PATCH='4'
+SHELL='/bin/sh'
+SPEEDY_BIN='/usr/local/bin/speedy'
+WEB_GROUP='freeside'
+WEB_USER='freeside'
+bindir='/opt/rt3/bin'
+build_alias=''
+customdir='/opt/rt3/local'
+custometcdir='/opt/rt3/local/etc'
+customhtmldir='/opt/rt3/local/html'
+customlexdir='/opt/rt3/local/po'
+customlibdir='/opt/rt3/local/lib'
+datadir='/opt/rt3/share'
+exec_prefix='/opt/rt3'
+exp_bindir='/opt/rt3/bin'
+exp_customdir='/opt/rt3/local'
+exp_custometcdir='/opt/rt3/local/etc'
+exp_customhtmldir='/opt/rt3/local/html'
+exp_customlexdir='/opt/rt3/local/po'
+exp_customlibdir='/opt/rt3/local/lib'
+exp_datadir='/opt/rt3/share'
+exp_exec_prefix='/opt/rt3'
+exp_htmldir='/var/www/freeside/rt'
+exp_libdir='/opt/rt3/lib'
+exp_localstatedir='/opt/rt3/var'
+exp_logfiledir='/opt/rt3/var/log'
+exp_mandir='/opt/rt3/man'
+exp_manualdir='/opt/rt3/share/doc'
+exp_masonstatedir='/usr/local/etc/freeside/masondata'
+exp_prefix='/opt/rt3'
+exp_sbindir='/opt/rt3/sbin'
+exp_sessionstatedir='/opt/rt3/var/session_data'
+exp_sysconfdir='/opt/rt3/etc'
+host_alias=''
+htmldir='/var/www/freeside/rt'
+includedir='${prefix}/include'
+infodir='${prefix}/info'
+libdir='/opt/rt3/lib'
+libexecdir='${exec_prefix}/libexec'
+localstatedir='/opt/rt3/var'
+logfiledir='/opt/rt3/var/log'
+mandir='/opt/rt3/man'
+manualdir='/opt/rt3/share/doc'
+masonstatedir='/usr/local/etc/freeside/masondata'
+oldincludedir='/usr/include'
+prefix='/opt/rt3'
+program_transform_name='s,x,x,'
+rt_layout_name='Freeside'
+rt_version_major='3'
+rt_version_minor='6'
+rt_version_patch='4'
+sbindir='/opt/rt3/sbin'
+sessionstatedir='/opt/rt3/var/session_data'
+sharedstatedir='${prefix}/com'
+sysconfdir='/opt/rt3/etc'
+target_alias=''
+
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+
+#define PACKAGE_BUGREPORT "rt-bugs@bestpractical.com"
+#define PACKAGE_NAME "RT"
+#define PACKAGE_STRING "RT 3.6.4"
+#define PACKAGE_TARNAME "rt"
+#define PACKAGE_VERSION "3.6.4"
+
+configure: exit 0
diff --git a/rt/config.status b/rt/config.status
new file mode 100755
index 0000000..06c562a
--- /dev/null
+++ b/rt/config.status
@@ -0,0 +1,817 @@
+#! /bin/sh
+# Generated by configure.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=${CONFIG_SHELL-/bin/sh}
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by RT $as_me 3.6.4, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+config_files=" sbin/rt-dump-database sbin/rt-setup-database sbin/rt-test-dependencies bin/mason_handler.fcgi bin/mason_handler.scgi bin/standalone_httpd bin/rt-crontool bin/rt-mailgate bin/rt Makefile etc/RT_Config.pm lib/RT.pm bin/mason_handler.svc bin/webmux.pl"
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+ac_cs_version="\
+RT config.status 3.6.4
+configured by ./configure, generated by GNU Autoconf 2.59,
+ with options \"'--enable-layout=Freeside' '--with-db-type=Pg' '--with-db-dba=freeside' '--with-db-database=freeside' '--with-db-rt-user=freeside' '--with-db-rt-pass=' '--with-web-user=freeside' '--with-web-group=freeside' '--with-rt-group=freeside'\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=.
+INSTALL="/usr/bin/install -c"
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+if $ac_cs_recheck; then
+ echo "running /bin/sh ./configure " '--enable-layout=Freeside' '--with-db-type=Pg' '--with-db-dba=freeside' '--with-db-database=freeside' '--with-db-rt-user=freeside' '--with-db-rt-pass=' '--with-web-user=freeside' '--with-web-group=freeside' '--with-rt-group=freeside' $ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec /bin/sh ./configure '--enable-layout=Freeside' '--with-db-type=Pg' '--with-db-dba=freeside' '--with-db-database=freeside' '--with-db-rt-user=freeside' '--with-db-rt-pass=' '--with-web-user=freeside' '--with-web-group=freeside' '--with-rt-group=freeside' $ac_configure_extra_args --no-create --no-recursion
+fi
+
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "sbin/rt-dump-database" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-dump-database" ;;
+ "sbin/rt-setup-database" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-setup-database" ;;
+ "sbin/rt-test-dependencies" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-test-dependencies" ;;
+ "bin/mason_handler.fcgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.fcgi" ;;
+ "bin/mason_handler.scgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.scgi" ;;
+ "bin/standalone_httpd" ) CONFIG_FILES="$CONFIG_FILES bin/standalone_httpd" ;;
+ "bin/rt-crontool" ) CONFIG_FILES="$CONFIG_FILES bin/rt-crontool" ;;
+ "bin/rt-mailgate" ) CONFIG_FILES="$CONFIG_FILES bin/rt-mailgate" ;;
+ "bin/rt" ) CONFIG_FILES="$CONFIG_FILES bin/rt" ;;
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "etc/RT_Config.pm" ) CONFIG_FILES="$CONFIG_FILES etc/RT_Config.pm" ;;
+ "lib/RT.pm" ) CONFIG_FILES="$CONFIG_FILES lib/RT.pm" ;;
+ "bin/mason_handler.svc" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.svc" ;;
+ "bin/webmux.pl" ) CONFIG_FILES="$CONFIG_FILES bin/webmux.pl" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t$/@;t t/; /@;t t$/s/[\\&,]/\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t$/,;t t/' >$tmp/subs.sed <<\CEOF
+s,@SHELL@,/bin/sh,;t t
+s,@PATH_SEPARATOR@,:,;t t
+s,@PACKAGE_NAME@,RT,;t t
+s,@PACKAGE_TARNAME@,rt,;t t
+s,@PACKAGE_VERSION@,3.6.4,;t t
+s,@PACKAGE_STRING@,RT 3.6.4,;t t
+s,@PACKAGE_BUGREPORT@,rt-bugs@bestpractical.com,;t t
+s,@exec_prefix@,/opt/rt3,;t t
+s,@prefix@,/opt/rt3,;t t
+s,@program_transform_name@,s,x,x,,;t t
+s,@bindir@,/opt/rt3/bin,;t t
+s,@sbindir@,/opt/rt3/sbin,;t t
+s,@libexecdir@,${exec_prefix}/libexec,;t t
+s,@datadir@,/opt/rt3/share,;t t
+s,@sysconfdir@,/opt/rt3/etc,;t t
+s,@sharedstatedir@,${prefix}/com,;t t
+s,@localstatedir@,/opt/rt3/var,;t t
+s,@libdir@,/opt/rt3/lib,;t t
+s,@includedir@,${prefix}/include,;t t
+s,@oldincludedir@,/usr/include,;t t
+s,@infodir@,${prefix}/info,;t t
+s,@mandir@,/opt/rt3/man,;t t
+s,@build_alias@,,;t t
+s,@host_alias@,,;t t
+s,@target_alias@,,;t t
+s,@DEFS@,-DPACKAGE_NAME=\"RT\" -DPACKAGE_TARNAME=\"rt\" -DPACKAGE_VERSION=\"3.6.4\" -DPACKAGE_STRING=\"RT\ 3.6.4\" -DPACKAGE_BUGREPORT=\"rt-bugs@bestpractical.com\" ,;t t
+s,@ECHO_C@,,;t t
+s,@ECHO_N@,-n,;t t
+s,@ECHO_T@,,;t t
+s,@LIBS@,,;t t
+s,@rt_version_major@,3,;t t
+s,@rt_version_minor@,6,;t t
+s,@rt_version_patch@,4,;t t
+s,@INSTALL_PROGRAM@,${INSTALL},;t t
+s,@INSTALL_SCRIPT@,${INSTALL},;t t
+s,@INSTALL_DATA@,${INSTALL} -m 644,;t t
+s,@AWK@,gawk,;t t
+s,@PERL@,/usr/bin/perl,;t t
+s,@SPEEDY_BIN@,/usr/local/bin/speedy,;t t
+s,@exp_prefix@,/opt/rt3,;t t
+s,@exp_exec_prefix@,/opt/rt3,;t t
+s,@exp_bindir@,/opt/rt3/bin,;t t
+s,@exp_sbindir@,/opt/rt3/sbin,;t t
+s,@exp_sysconfdir@,/opt/rt3/etc,;t t
+s,@exp_mandir@,/opt/rt3/man,;t t
+s,@exp_libdir@,/opt/rt3/lib,;t t
+s,@exp_datadir@,/opt/rt3/share,;t t
+s,@htmldir@,/var/www/freeside/rt,;t t
+s,@exp_htmldir@,/var/www/freeside/rt,;t t
+s,@manualdir@,/opt/rt3/share/doc,;t t
+s,@exp_manualdir@,/opt/rt3/share/doc,;t t
+s,@exp_localstatedir@,/opt/rt3/var,;t t
+s,@logfiledir@,/opt/rt3/var/log,;t t
+s,@exp_logfiledir@,/opt/rt3/var/log,;t t
+s,@masonstatedir@,/usr/local/etc/freeside/masondata,;t t
+s,@exp_masonstatedir@,/usr/local/etc/freeside/masondata,;t t
+s,@sessionstatedir@,/opt/rt3/var/session_data,;t t
+s,@exp_sessionstatedir@,/opt/rt3/var/session_data,;t t
+s,@customdir@,/opt/rt3/local,;t t
+s,@exp_customdir@,/opt/rt3/local,;t t
+s,@custometcdir@,/opt/rt3/local/etc,;t t
+s,@exp_custometcdir@,/opt/rt3/local/etc,;t t
+s,@customhtmldir@,/opt/rt3/local/html,;t t
+s,@exp_customhtmldir@,/opt/rt3/local/html,;t t
+s,@customlexdir@,/opt/rt3/local/po,;t t
+s,@exp_customlexdir@,/opt/rt3/local/po,;t t
+s,@customlibdir@,/opt/rt3/local/lib,;t t
+s,@exp_customlibdir@,/opt/rt3/local/lib,;t t
+s,@rt_layout_name@,Freeside,;t t
+s,@BIN_OWNER@,root,;t t
+s,@LIBS_OWNER@,root,;t t
+s,@LIBS_GROUP@,bin,;t t
+s,@DB_TYPE@,Pg,;t t
+s,@DATABASE_ENV_PREF@,,;t t
+s,@DB_HOST@,localhost,;t t
+s,@DB_PORT@,,;t t
+s,@DB_RT_HOST@,localhost,;t t
+s,@DB_DBA@,freeside,;t t
+s,@DB_DATABASE@,freeside,;t t
+s,@DB_RT_USER@,freeside,;t t
+s,@DB_RT_PASS@,,;t t
+s,@WEB_USER@,freeside,;t t
+s,@WEB_GROUP@,freeside,;t t
+s,@RTGROUP@,freeside,;t t
+s,@APACHECTL@,/usr/sbin/apachectl,;t t
+s,@RT_STANDALONE@,0,;t t
+s,@RT_DEVEL_MODE@,0,;t t
+s,@RT_VERSION_MAJOR@,3,;t t
+s,@RT_VERSION_MINOR@,6,;t t
+s,@RT_VERSION_PATCH@,4,;t t
+s,@RT_PATH@,/opt/rt3,;t t
+s,@RT_DOC_PATH@,/opt/rt3/share/doc,;t t
+s,@RT_LOCAL_PATH@,/opt/rt3/local,;t t
+s,@RT_LIB_PATH@,/opt/rt3/lib,;t t
+s,@RT_ETC_PATH@,/opt/rt3/etc,;t t
+s,@CONFIG_FILE_PATH@,/opt/rt3/etc,;t t
+s,@RT_BIN_PATH@,/opt/rt3/bin,;t t
+s,@RT_SBIN_PATH@,/opt/rt3/sbin,;t t
+s,@RT_VAR_PATH@,/opt/rt3/var,;t t
+s,@RT_MAN_PATH@,/opt/rt3/man,;t t
+s,@MASON_DATA_PATH@,/usr/local/etc/freeside/masondata,;t t
+s,@MASON_SESSION_PATH@,/opt/rt3/var/session_data,;t t
+s,@MASON_HTML_PATH@,/var/www/freeside/rt,;t t
+s,@LOCAL_ETC_PATH@,/opt/rt3/local/etc,;t t
+s,@MASON_LOCAL_HTML_PATH@,/opt/rt3/local/html,;t t
+s,@LOCAL_LEXICON_PATH@,/opt/rt3/local/po,;t t
+s,@LOCAL_LIB_PATH@,/opt/rt3/local/lib,;t t
+s,@DESTDIR@,/opt/rt3,;t t
+s,@RT_LOG_PATH@,/opt/rt3/var/log,;t t
+s,@LIBOBJS@,,;t t
+s,@LTLIBOBJS@,,;t t
+CEOF
+
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+ esac
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ sed "/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}
+
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+ # Run the commands associated with the file.
+ case $ac_file in
+ sbin/rt-dump-database ) chmod ug+x $ac_file
+ ;;
+ sbin/rt-setup-database ) chmod ug+x $ac_file
+ ;;
+ sbin/rt-test-dependencies ) chmod ug+x $ac_file
+ ;;
+ bin/mason_handler.fcgi ) chmod ug+x $ac_file
+ ;;
+ bin/mason_handler.scgi ) chmod ug+x $ac_file
+ ;;
+ bin/standalone_httpd ) chmod ug+x $ac_file
+ ;;
+ bin/rt-crontool ) chmod ug+x $ac_file
+ ;;
+ bin/rt-mailgate ) chmod ug+x $ac_file
+ ;;
+ bin/rt ) chmod ug+x $ac_file
+ ;;
+ esac
+done
+
+{ (exit 0); exit 0; }
diff --git a/rt/configure b/rt/configure
new file mode 100755
index 0000000..18c5577
--- /dev/null
+++ b/rt/configure
@@ -0,0 +1,3164 @@
+#! /bin/sh
+# From configure.ac Revision: 6876 .
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for RT 3.6.6.
+#
+# Report bugs to <rt-bugs@bestpractical.com>.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='RT'
+PACKAGE_TARNAME='rt'
+PACKAGE_VERSION='3.6.6'
+PACKAGE_STRING='RT 3.6.6'
+PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
+
+ac_unique_file="lib/RT.pm.in"
+ac_default_prefix=/opt/rt3
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS rt_version_major rt_version_minor rt_version_patch INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA AWK PERL SPEEDY_BIN exp_prefix exp_exec_prefix exp_bindir exp_sbindir exp_sysconfdir exp_mandir exp_libdir exp_datadir htmldir exp_htmldir manualdir exp_manualdir exp_localstatedir logfiledir exp_logfiledir masonstatedir exp_masonstatedir sessionstatedir exp_sessionstatedir customdir exp_customdir custometcdir exp_custometcdir customhtmldir exp_customhtmldir customlexdir exp_customlexdir customlibdir exp_customlibdir rt_layout_name BIN_OWNER LIBS_OWNER LIBS_GROUP DB_TYPE DATABASE_ENV_PREF DB_HOST DB_PORT DB_RT_HOST DB_DBA DB_DATABASE DB_RT_USER DB_RT_PASS WEB_USER WEB_GROUP RTGROUP APACHECTL RT_STANDALONE RT_DEVEL_MODE RT_VERSION_MAJOR RT_VERSION_MINOR RT_VERSION_PATCH RT_PATH RT_DOC_PATH RT_LOCAL_PATH RT_LIB_PATH RT_ETC_PATH CONFIG_FILE_PATH RT_BIN_PATH RT_SBIN_PATH RT_VAR_PATH RT_MAN_PATH MASON_DATA_PATH MASON_SESSION_PATH MASON_HTML_PATH LOCAL_ETC_PATH MASON_LOCAL_HTML_PATH LOCAL_LEXICON_PATH LOCAL_LIB_PATH DESTDIR RT_LOG_PATH LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_PERL_set=${PERL+set}
+ac_env_PERL_value=$PERL
+ac_cv_env_PERL_set=${PERL+set}
+ac_cv_env_PERL_value=$PERL
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures RT 3.6.6 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of RT 3.6.6:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-layout=LAYOUT Use a specific directory layout (Default: RT3)
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-speedycgi=/path/to/speedy
+ path to your speedycgi binary, if it exists
+ --with-bin-owner=OWNER user that will own RT binaries (default root)
+ --with-libs-owner=OWNER user that will own RT libraries (default root)
+ --with-libs-group=GROUP group that will own RT binaries (default bin)
+ --with-db-type=TYPE sort of database RT will use (default: mysql)
+ (mysql, Pg, Oracle, Informix and SQLite are valid)
+ --with-db-host=HOSTNAME FQDN of database server (default: localhost)
+ --with-db-port=PORT port on which the database listens on
+ --with-db-rt-host=HOSTNAME
+ FQDN of RT server which talks to the database server
+ (default: localhost)
+ --with-db-dba=DBA name of database administrator (default: root)
+ --with-db-database=DBNAME
+ name of the database to use (default: rt3)
+ --with-db-rt-user=DBUSER
+ name of database user (default: rt_user)
+ --with-db-rt-pass=PASSWORD
+ password for database user (default: rt_pass)
+ --with-web-user=USER user the web server runs as (default: www)
+ --with-web-group=GROUP group the web server runs as (default: www)
+ --with-rt-group=GROUP group to own all files (default: rt)
+ --with-my-user-group set all users and groups to current user/group
+ --with-apachectl instruct RT where to find your apachectl
+ --with-standalone Install modules for pure perl standalone server
+ --with-devel-mode Turn on development aids that might hurt you in
+ production
+
+Some influential environment variables:
+ PERL Perl interpreter command
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <rt-bugs@bestpractical.com>.
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd "$ac_popdir"
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+RT configure 3.6.6
+generated by GNU Autoconf 2.59
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by RT $as_me 3.6.6, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+rt_version_major=3
+
+rt_version_minor=6
+
+rt_version_patch=6
+
+test "x$rt_version_major" = 'x' && rt_version_major=0
+test "x$rt_version_minor" = 'x' && rt_version_minor=0
+test "x$rt_version_patch" = 'x' && rt_version_patch=0
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f $ac_dir/shtool; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL=$ac_install_sh
+ fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AWK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AWK="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$AWK" && break
+done
+
+
+# Extract the first word of "perl", so it can be a program name with args.
+set dummy perl; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_PERL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $PERL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PERL="$PERL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="not found"
+ ;;
+esac
+fi
+PERL=$ac_cv_path_PERL
+
+if test -n "$PERL"; then
+ echo "$as_me:$LINENO: result: $PERL" >&5
+echo "${ECHO_T}$PERL" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+if test "$PERL" = 'not found'; then
+ { { echo "$as_me:$LINENO: error: cannot use $PACKAGE_NAME without perl" >&5
+echo "$as_me: error: cannot use $PACKAGE_NAME without perl" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+# Check whether --with-speedycgi or --without-speedycgi was given.
+if test "${with_speedycgi+set}" = set; then
+ withval="$with_speedycgi"
+ SPEEDY_BIN=$withval
+else
+ SPEEDY_BIN=/usr/local/bin/speedy
+fi;
+
+
+
+
+
+# Check whether --enable-layout or --disable-layout was given.
+if test "${enable_layout+set}" = set; then
+ enableval="$enable_layout"
+ LAYOUT=$enableval
+fi;
+
+if test "x$LAYOUT" = "x"; then
+ LAYOUT="RT3"
+fi
+
+ if test ! -f $srcdir/config.layout; then
+ { echo "$as_me:$LINENO: WARNING: Layout file $srcdir/config.layout not found" >&5
+echo "$as_me: WARNING: Layout file $srcdir/config.layout not found" >&2;}
+ rt_layout_name=no
+ else
+ pldconf=./config.pld
+ $PERL -0777 -p -e "\$layout = '$LAYOUT';" -e '
+ s/.*<Layout\s+$layout>//gims;
+ s/\<\/Layout\>.*//s;
+ s/^#.*$//m;
+ s/^\s+//gim;
+ s/\s+$/\n/gim;
+ s/\+$/\/rt3/gim;
+ # m4 will not let us just use $srcdir/config.layout, we need $1
+ s/^\s*((?:bin|sbin|libexec|data|sysconf|sharedstate|localstate|lib|include|oldinclude|info|man|html)dir)\s*:\s*(.*)$/$1=$2/gim;
+ s/^\s*(.*?)\s*:\s*(.*)$/\(test "x\$$1" = "xNONE" || test "x\$$1" = "x") && $1=$2/gim;
+ ' < $srcdir/config.layout > $pldconf
+
+ if test -s $pldconf; then
+ rt_layout_name=$LAYOUT
+ . $pldconf
+
+ for var in prefix exec_prefix bindir sbindir \
+ sysconfdir mandir libdir datadir htmldir \
+ localstatedir logfiledir masonstatedir \
+ sessionstatedir customdir custometcdir customhtmldir \
+ customlexdir customlibdir manualdir; do
+ eval "val=\"\$$var\""
+ val=`echo $val | sed -e 's:\(.\)/*$:\1:'`
+ val=`echo $val |
+ sed -e 's:[\$]\([a-z_]*\):$\1:g'`
+ eval "$var='$val'"
+ done
+
+ else
+ rt_layout_name=no
+ fi
+ #rm $pldconf
+ fi
+
+
+ ap_last=''
+ ap_cur='$prefix'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_prefix="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$exec_prefix'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_exec_prefix="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$bindir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_bindir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$sbindir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_sbindir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$sysconfdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_sysconfdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$mandir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_mandir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$libdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_libdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$datadir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_datadir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$htmldir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_htmldir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$manualdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_manualdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$localstatedir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_localstatedir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$logfiledir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_logfiledir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$masonstatedir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_masonstatedir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$sessionstatedir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_sessionstatedir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$custometcdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_custometcdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customhtmldir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customhtmldir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customlexdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customlexdir="${ap_cur}"
+
+
+
+
+
+
+ ap_last=''
+ ap_cur='$customlibdir'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ exp_customlibdir="${ap_cur}"
+
+
+
+
+
+echo "$as_me:$LINENO: checking for chosen layout" >&5
+echo $ECHO_N "checking for chosen layout... $ECHO_C" >&6
+if test "x$rt_layout_name" = "xno"; then
+ if test "x$LAYOUT" = "xno"; then
+ echo "$as_me:$LINENO: result: none" >&5
+echo "${ECHO_T}none" >&6
+ else
+ echo "$as_me:$LINENO: result: $LAYOUT" >&5
+echo "${ECHO_T}$LAYOUT" >&6
+ fi
+ { { echo "$as_me:$LINENO: error: a valid layout must be specified (or the default used)" >&5
+echo "$as_me: error: a valid layout must be specified (or the default used)" >&2;}
+ { (exit 1); exit 1; }; }
+else
+
+ echo "$as_me:$LINENO: result: $rt_layout_name" >&5
+echo "${ECHO_T}$rt_layout_name" >&6
+fi
+
+
+
+# ACRT_USER_EXISTS( users, variable, default )
+# - users is a list of users [www apache www-docs]
+# from highest to lowest priority to high priority (i.e. first match)
+# - variable is what you set with the result
+#
+
+
+
+
+
+# Check whether --with-bin-owner or --without-bin-owner was given.
+if test "${with_bin_owner+set}" = set; then
+ withval="$with_bin_owner"
+ BIN_OWNER=$withval
+else
+ BIN_OWNER=root
+fi;
+
+
+
+# Check whether --with-libs-owner or --without-libs-owner was given.
+if test "${with_libs_owner+set}" = set; then
+ withval="$with_libs_owner"
+ LIBS_OWNER=$withval
+else
+ LIBS_OWNER=root
+fi;
+
+
+
+# Check whether --with-libs-group or --without-libs-group was given.
+if test "${with_libs_group+set}" = set; then
+ withval="$with_libs_group"
+ LIBS_GROUP=$withval
+else
+ LIBS_GROUP=bin
+fi;
+
+
+
+# Check whether --with-db-type or --without-db-type was given.
+if test "${with_db_type+set}" = set; then
+ withval="$with_db_type"
+ DB_TYPE=$withval
+else
+ DB_TYPE=mysql
+fi;
+if test "$DB_TYPE" != 'mysql' -a "$DB_TYPE" != 'Pg' -a "$DB_TYPE" != 'SQLite' -a "$DB_TYPE" != 'Oracle' -a "$DB_TYPE" != 'Informix' -a "$DB_TYPE" != 'Sybase' ; then
+ { { echo "$as_me:$LINENO: error: Only Oracle, Informix, Pg, mysql and SQLite are valid db types" >&5
+echo "$as_me: error: Only Oracle, Informix, Pg, mysql and SQLite are valid db types" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+if test "$DB_TYPE" = 'Oracle'; then
+ test "x$ORACLE_HOME" = 'x' && { { echo "$as_me:$LINENO: error: Please declare the ORACLE_HOME environment variable" >&5
+echo "$as_me: error: Please declare the ORACLE_HOME environment variable" >&2;}
+ { (exit 1); exit 1; }; }
+ DATABASE_ENV_PREF="\$ENV{'ORACLE_HOME'} = '$ORACLE_HOME';"
+fi
+
+if test "$DB_TYPE" = 'Sybase'; then
+ test "x$SYBASE" = 'x' && { { echo "$as_me:$LINENO: error: Please declare the SYBASE_HOME environment variable" >&5
+echo "$as_me: error: Please declare the SYBASE_HOME environment variable" >&2;}
+ { (exit 1); exit 1; }; }
+ DATABASE_ENV_PREF="\$ENV{'SYBASE'} = '$SYBASE';"
+fi
+
+
+
+# Check whether --with-db-host or --without-db-host was given.
+if test "${with_db_host+set}" = set; then
+ withval="$with_db_host"
+ DB_HOST=$withval
+else
+ DB_HOST=localhost
+fi;
+
+
+
+# Check whether --with-db-port or --without-db-port was given.
+if test "${with_db_port+set}" = set; then
+ withval="$with_db_port"
+ DB_PORT=$withval
+else
+ DB_PORT=
+fi;
+
+
+
+# Check whether --with-db-rt-host or --without-db-rt-host was given.
+if test "${with_db_rt_host+set}" = set; then
+ withval="$with_db_rt_host"
+ DB_RT_HOST=$withval
+else
+ DB_RT_HOST=localhost
+fi;
+
+
+
+# Check whether --with-db-dba or --without-db-dba was given.
+if test "${with_db_dba+set}" = set; then
+ withval="$with_db_dba"
+ DB_DBA=$withval
+else
+ DB_DBA=root
+fi;
+
+
+
+# Check whether --with-db-database or --without-db-database was given.
+if test "${with_db_database+set}" = set; then
+ withval="$with_db_database"
+ DB_DATABASE=$withval
+else
+ DB_DATABASE=rt3
+fi;
+
+
+
+# Check whether --with-db-rt-user or --without-db-rt-user was given.
+if test "${with_db_rt_user+set}" = set; then
+ withval="$with_db_rt_user"
+ DB_RT_USER=$withval
+else
+ DB_RT_USER=rt_user
+fi;
+
+
+
+# Check whether --with-db-rt-pass or --without-db-rt-pass was given.
+if test "${with_db_rt_pass+set}" = set; then
+ withval="$with_db_rt_pass"
+ DB_RT_PASS=$withval
+else
+ DB_RT_PASS=rt_pass
+fi;
+
+
+
+# Check whether --with-web-user or --without-web-user was given.
+if test "${with_web_user+set}" = set; then
+ withval="$with_web_user"
+ WEB_USER=$withval
+else
+
+ WEB_USER=www
+ for x in www www-data apache httpd nobody; do
+ echo "$as_me:$LINENO: checking if user $x exists" >&5
+echo $ECHO_N "checking if user $x exists... $ECHO_C" >&6
+ if $PERL -e"exit( defined getpwnam('$x') ? 0 : 1)" ; then
+ echo "$as_me:$LINENO: result: found" >&5
+echo "${ECHO_T}found" >&6; WEB_USER=$x ; break
+else
+ echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+fi
+
+ done
+
+
+fi;
+
+
+
+# Check whether --with-web-group or --without-web-group was given.
+if test "${with_web_group+set}" = set; then
+ withval="$with_web_group"
+ WEB_GROUP=$withval
+else
+
+ WEB_GROUP=www
+ for x in www www-data apache httpd nogroup nobody; do
+ echo "$as_me:$LINENO: checking if group $x exists" >&5
+echo $ECHO_N "checking if group $x exists... $ECHO_C" >&6
+ if $PERL -e"exit( defined getgrnam('$x') ? 0 : 1)" ; then
+ echo "$as_me:$LINENO: result: found" >&5
+echo "${ECHO_T}found" >&6; WEB_GROUP=$x ; break
+else
+ echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+fi
+
+ done
+
+fi;
+
+
+
+# Check whether --with-rt-group or --without-rt-group was given.
+if test "${with_rt_group+set}" = set; then
+ withval="$with_rt_group"
+ RTGROUP=$withval
+else
+
+ RTGROUP=rt
+ for x in rt3 rt $WEB_GROUP; do
+ echo "$as_me:$LINENO: checking if group $x exists" >&5
+echo $ECHO_N "checking if group $x exists... $ECHO_C" >&6
+ if $PERL -e"exit( defined getgrnam('$x') ? 0 : 1)" ; then
+ echo "$as_me:$LINENO: result: found" >&5
+echo "${ECHO_T}found" >&6; RTGROUP=$x ; break
+else
+ echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+fi
+
+ done
+
+fi;
+
+
+# XXX TODO: The command below to figure out the group brokenly relies on
+# output order (and "id -gn" doesn't work on all platforms).
+my_group=$(groups|cut -f1 -d' ')
+my_user=${USER:-$LOGNAME}
+
+# Check whether --with-my-user-group or --without-my-user-group was given.
+if test "${with_my_user_group+set}" = set; then
+ withval="$with_my_user_group"
+ RTGROUP=$my_group
+ BIN_OWNER=$my_user
+ LIBS_OWNER=$my_user
+ LIBS_GROUP=$my_group
+ WEB_USER=$my_user
+ WEB_GROUP=$my_group
+fi;
+
+# Test for valid database names
+if test "$DB_TYPE" == "mysql" ; then
+ echo "$as_me:$LINENO: checking if database name is valid" >&5
+echo $ECHO_N "checking if database name is valid... $ECHO_C" >&6
+ if echo $DB_DATABASE | $AWK '/-/ { exit 1 }' ; then
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+ { { echo "$as_me:$LINENO: error: no. database name ($DB_DATABASE) contains '-' which is not valid for mysql" >&5
+echo "$as_me: error: no. database name ($DB_DATABASE) contains '-' which is not valid for mysql" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+
+
+
+fi
+
+
+
+
+# Check whether --with-apachectl or --without-apachectl was given.
+if test "${with_apachectl+set}" = set; then
+ withval="$with_apachectl"
+ APACHECTL=$withval
+else
+ APACHECTL=`which apachectl`
+fi;
+
+
+
+# Check whether --with-devel-mode or --without-devel-mode was given.
+if test "${with_devel_mode+set}" = set; then
+ withval="$with_devel_mode"
+ RT_STANDALONE="1"
+else
+ RT_STANDALONE="0"
+fi;
+
+
+
+# Check whether --with-devel-mode or --without-devel-mode was given.
+if test "${with_devel_mode+set}" = set; then
+ withval="$with_devel_mode"
+ RT_DEVEL_MODE="1"
+else
+ RT_DEVEL_MODE="0"
+fi;
+
+
+RT_VERSION_MAJOR=${rt_version_major}
+
+RT_VERSION_MINOR=${rt_version_minor}
+
+RT_VERSION_PATCH=${rt_version_patch}
+
+
+RT_PATH=${exp_prefix}
+
+RT_DOC_PATH=${exp_manualdir}
+
+RT_LOCAL_PATH=${exp_customdir}
+
+RT_LIB_PATH=${exp_libdir}
+
+RT_ETC_PATH=${exp_sysconfdir}
+
+CONFIG_FILE_PATH=${exp_sysconfdir}
+
+RT_BIN_PATH=${exp_bindir}
+
+RT_SBIN_PATH=${exp_sbindir}
+
+RT_VAR_PATH=${exp_localstatedir}
+
+RT_MAN_PATH=${exp_mandir}
+
+MASON_DATA_PATH=${exp_masonstatedir}
+
+MASON_SESSION_PATH=${exp_sessionstatedir}
+
+MASON_HTML_PATH=${exp_htmldir}
+
+LOCAL_ETC_PATH=${exp_custometcdir}
+
+MASON_LOCAL_HTML_PATH=${exp_customhtmldir}
+
+LOCAL_LEXICON_PATH=${exp_customlexdir}
+
+LOCAL_LIB_PATH=${exp_customlibdir}
+
+DESTDIR=${exp_prefix}
+
+RT_LOG_PATH=${exp_logfiledir}
+
+
+
+ ac_config_files="$ac_config_files sbin/rt-dump-database sbin/rt-setup-database sbin/rt-test-dependencies bin/mason_handler.fcgi bin/mason_handler.scgi bin/standalone_httpd bin/rt-crontool bin/rt-mailgate bin/rt"
+
+
+ ac_config_files="$ac_config_files Makefile etc/RT_Config.pm lib/RT.pm bin/mason_handler.svc bin/webmux.pl"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g
+t quote
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[ `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output. A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by RT $as_me 3.6.6, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+RT config.status 3.6.6
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "sbin/rt-dump-database" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-dump-database" ;;
+ "sbin/rt-setup-database" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-setup-database" ;;
+ "sbin/rt-test-dependencies" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-test-dependencies" ;;
+ "bin/mason_handler.fcgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.fcgi" ;;
+ "bin/mason_handler.scgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.scgi" ;;
+ "bin/standalone_httpd" ) CONFIG_FILES="$CONFIG_FILES bin/standalone_httpd" ;;
+ "bin/rt-crontool" ) CONFIG_FILES="$CONFIG_FILES bin/rt-crontool" ;;
+ "bin/rt-mailgate" ) CONFIG_FILES="$CONFIG_FILES bin/rt-mailgate" ;;
+ "bin/rt" ) CONFIG_FILES="$CONFIG_FILES bin/rt" ;;
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "etc/RT_Config.pm" ) CONFIG_FILES="$CONFIG_FILES etc/RT_Config.pm" ;;
+ "lib/RT.pm" ) CONFIG_FILES="$CONFIG_FILES lib/RT.pm" ;;
+ "bin/mason_handler.svc" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.svc" ;;
+ "bin/webmux.pl" ) CONFIG_FILES="$CONFIG_FILES bin/webmux.pl" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@rt_version_major@,$rt_version_major,;t t
+s,@rt_version_minor@,$rt_version_minor,;t t
+s,@rt_version_patch@,$rt_version_patch,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@AWK@,$AWK,;t t
+s,@PERL@,$PERL,;t t
+s,@SPEEDY_BIN@,$SPEEDY_BIN,;t t
+s,@exp_prefix@,$exp_prefix,;t t
+s,@exp_exec_prefix@,$exp_exec_prefix,;t t
+s,@exp_bindir@,$exp_bindir,;t t
+s,@exp_sbindir@,$exp_sbindir,;t t
+s,@exp_sysconfdir@,$exp_sysconfdir,;t t
+s,@exp_mandir@,$exp_mandir,;t t
+s,@exp_libdir@,$exp_libdir,;t t
+s,@exp_datadir@,$exp_datadir,;t t
+s,@htmldir@,$htmldir,;t t
+s,@exp_htmldir@,$exp_htmldir,;t t
+s,@manualdir@,$manualdir,;t t
+s,@exp_manualdir@,$exp_manualdir,;t t
+s,@exp_localstatedir@,$exp_localstatedir,;t t
+s,@logfiledir@,$logfiledir,;t t
+s,@exp_logfiledir@,$exp_logfiledir,;t t
+s,@masonstatedir@,$masonstatedir,;t t
+s,@exp_masonstatedir@,$exp_masonstatedir,;t t
+s,@sessionstatedir@,$sessionstatedir,;t t
+s,@exp_sessionstatedir@,$exp_sessionstatedir,;t t
+s,@customdir@,$customdir,;t t
+s,@exp_customdir@,$exp_customdir,;t t
+s,@custometcdir@,$custometcdir,;t t
+s,@exp_custometcdir@,$exp_custometcdir,;t t
+s,@customhtmldir@,$customhtmldir,;t t
+s,@exp_customhtmldir@,$exp_customhtmldir,;t t
+s,@customlexdir@,$customlexdir,;t t
+s,@exp_customlexdir@,$exp_customlexdir,;t t
+s,@customlibdir@,$customlibdir,;t t
+s,@exp_customlibdir@,$exp_customlibdir,;t t
+s,@rt_layout_name@,$rt_layout_name,;t t
+s,@BIN_OWNER@,$BIN_OWNER,;t t
+s,@LIBS_OWNER@,$LIBS_OWNER,;t t
+s,@LIBS_GROUP@,$LIBS_GROUP,;t t
+s,@DB_TYPE@,$DB_TYPE,;t t
+s,@DATABASE_ENV_PREF@,$DATABASE_ENV_PREF,;t t
+s,@DB_HOST@,$DB_HOST,;t t
+s,@DB_PORT@,$DB_PORT,;t t
+s,@DB_RT_HOST@,$DB_RT_HOST,;t t
+s,@DB_DBA@,$DB_DBA,;t t
+s,@DB_DATABASE@,$DB_DATABASE,;t t
+s,@DB_RT_USER@,$DB_RT_USER,;t t
+s,@DB_RT_PASS@,$DB_RT_PASS,;t t
+s,@WEB_USER@,$WEB_USER,;t t
+s,@WEB_GROUP@,$WEB_GROUP,;t t
+s,@RTGROUP@,$RTGROUP,;t t
+s,@APACHECTL@,$APACHECTL,;t t
+s,@RT_STANDALONE@,$RT_STANDALONE,;t t
+s,@RT_DEVEL_MODE@,$RT_DEVEL_MODE,;t t
+s,@RT_VERSION_MAJOR@,$RT_VERSION_MAJOR,;t t
+s,@RT_VERSION_MINOR@,$RT_VERSION_MINOR,;t t
+s,@RT_VERSION_PATCH@,$RT_VERSION_PATCH,;t t
+s,@RT_PATH@,$RT_PATH,;t t
+s,@RT_DOC_PATH@,$RT_DOC_PATH,;t t
+s,@RT_LOCAL_PATH@,$RT_LOCAL_PATH,;t t
+s,@RT_LIB_PATH@,$RT_LIB_PATH,;t t
+s,@RT_ETC_PATH@,$RT_ETC_PATH,;t t
+s,@CONFIG_FILE_PATH@,$CONFIG_FILE_PATH,;t t
+s,@RT_BIN_PATH@,$RT_BIN_PATH,;t t
+s,@RT_SBIN_PATH@,$RT_SBIN_PATH,;t t
+s,@RT_VAR_PATH@,$RT_VAR_PATH,;t t
+s,@RT_MAN_PATH@,$RT_MAN_PATH,;t t
+s,@MASON_DATA_PATH@,$MASON_DATA_PATH,;t t
+s,@MASON_SESSION_PATH@,$MASON_SESSION_PATH,;t t
+s,@MASON_HTML_PATH@,$MASON_HTML_PATH,;t t
+s,@LOCAL_ETC_PATH@,$LOCAL_ETC_PATH,;t t
+s,@MASON_LOCAL_HTML_PATH@,$MASON_LOCAL_HTML_PATH,;t t
+s,@LOCAL_LEXICON_PATH@,$LOCAL_LEXICON_PATH,;t t
+s,@LOCAL_LIB_PATH@,$LOCAL_LIB_PATH,;t t
+s,@DESTDIR@,$DESTDIR,;t t
+s,@RT_LOG_PATH@,$RT_LOG_PATH,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+ esac
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+ # Run the commands associated with the file.
+ case $ac_file in
+ sbin/rt-dump-database ) chmod ug+x $ac_file
+ ;;
+ sbin/rt-setup-database ) chmod ug+x $ac_file
+ ;;
+ sbin/rt-test-dependencies ) chmod ug+x $ac_file
+ ;;
+ bin/mason_handler.fcgi ) chmod ug+x $ac_file
+ ;;
+ bin/mason_handler.scgi ) chmod ug+x $ac_file
+ ;;
+ bin/standalone_httpd ) chmod ug+x $ac_file
+ ;;
+ bin/rt-crontool ) chmod ug+x $ac_file
+ ;;
+ bin/rt-mailgate ) chmod ug+x $ac_file
+ ;;
+ bin/rt ) chmod ug+x $ac_file
+ ;;
+ esac
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
diff --git a/rt/configure.ac b/rt/configure.ac
new file mode 100644
index 0000000..2a3271d
--- /dev/null
+++ b/rt/configure.ac
@@ -0,0 +1,310 @@
+autoconf; exec ./configure $@
+dnl
+dnl Process this file with autoconf to produce a configure script
+dnl
+dnl Embed in generated ./configure script the following CVS info:
+AC_REVISION($Revision: 1.1.1.9 $)dnl
+
+dnl Setup autoconf
+AC_PREREQ(2.53)
+AC_INIT(RT, [3.6.6], [rt-bugs@bestpractical.com])
+AC_CONFIG_SRCDIR([lib/RT.pm.in])
+
+dnl Extract RT version number components
+AC_SUBST([rt_version_major],
+ m4_bregexp(AC_PACKAGE_VERSION,[^\(\w+\)\.\(\w+\)\(\.\(\w+\)\)?],[\1]))
+AC_SUBST([rt_version_minor],
+ m4_bregexp(AC_PACKAGE_VERSION,[^\(\w+\)\.\(\w+\)\(\.\(\w+\)\)?],[\2]))
+AC_SUBST([rt_version_patch],
+ m4_bregexp(AC_PACKAGE_VERSION,[^\(\w+\)\.\(\w+\)\(\.\(\w+\)\)?],[\4]))
+test "x$rt_version_major" = 'x' && rt_version_major=0
+test "x$rt_version_minor" = 'x' && rt_version_minor=0
+test "x$rt_version_patch" = 'x' && rt_version_patch=0
+
+dnl Check for programs
+AC_PROG_INSTALL
+AC_PROG_AWK
+AC_ARG_VAR([PERL],[Perl interpreter command])
+AC_PATH_PROG([PERL], [perl], [not found])
+if test "$PERL" = 'not found'; then
+ AC_MSG_ERROR([cannot use $PACKAGE_NAME without perl])
+fi
+
+
+dnl SPEED_BIN
+AC_ARG_WITH(speedycgi,
+ AC_HELP_STRING([--with-speedycgi=/path/to/speedy],
+ [path to your speedycgi binary, if it exists]),
+ SPEEDY_BIN=$withval,
+ SPEEDY_BIN=/usr/local/bin/speedy)
+AC_SUBST(SPEEDY_BIN)
+
+
+dnl Defaults paths for installation
+AC_PREFIX_DEFAULT([/opt/rt3])
+RT_ENABLE_LAYOUT
+
+
+# ACRT_USER_EXISTS( users, variable, default )
+# - users is a list of users [www apache www-docs]
+# from highest to lowest priority to high priority (i.e. first match)
+# - variable is what you set with the result
+#
+
+AC_DEFUN([ACRT_USER_GUESS],
+ [
+ $2=$3
+ for x in $1; do
+ AC_MSG_CHECKING([if user $x exists])
+ AS_IF([ $PERL -e"exit( defined getpwnam('$x') ? 0 : 1)" ],
+ [ AC_MSG_RESULT([found]); $2=$x ; break],
+ [ AC_MSG_RESULT([not found]) ])
+ done
+ ])
+AC_DEFUN([ACRT_GROUP_GUESS],
+ [
+ $2=$3
+ for x in $1; do
+ AC_MSG_CHECKING([if group $x exists])
+ AS_IF([ $PERL -e"exit( defined getgrnam('$x') ? 0 : 1)" ],
+ [ AC_MSG_RESULT([found]); $2=$x ; break],
+ [ AC_MSG_RESULT([not found]) ])
+ done
+ ])
+
+dnl BIN_OWNER
+AC_ARG_WITH(bin-owner,
+ AC_HELP_STRING([--with-bin-owner=OWNER],
+ [user that will own RT binaries (default root)]),
+ BIN_OWNER=$withval,
+ BIN_OWNER=root)
+AC_SUBST(BIN_OWNER)
+
+dnl LIBS_OWNER
+AC_ARG_WITH(libs-owner,
+ AC_HELP_STRING([--with-libs-owner=OWNER],
+ [user that will own RT libraries (default root)]),
+ LIBS_OWNER=$withval,
+ LIBS_OWNER=root)
+AC_SUBST(LIBS_OWNER)
+
+dnl LIBS_GROUP
+AC_ARG_WITH(libs-group,
+ AC_HELP_STRING([--with-libs-group=GROUP],
+ [group that will own RT binaries (default bin)]),
+ LIBS_GROUP=$withval,
+ LIBS_GROUP=bin)
+AC_SUBST(LIBS_GROUP)
+
+dnl DB_TYPE
+AC_ARG_WITH(db-type,
+ AC_HELP_STRING([--with-db-type=TYPE],
+ [sort of database RT will use (default: mysql) (mysql, Pg, Oracle, Informix and SQLite are valid)]),
+ DB_TYPE=$withval,
+ DB_TYPE=mysql)
+if test "$DB_TYPE" != 'mysql' -a "$DB_TYPE" != 'Pg' -a "$DB_TYPE" != 'SQLite' -a "$DB_TYPE" != 'Oracle' -a "$DB_TYPE" != 'Informix' -a "$DB_TYPE" != 'Sybase' ; then
+ AC_MSG_ERROR([Only Oracle, Informix, Pg, mysql and SQLite are valid db types])
+fi
+AC_SUBST(DB_TYPE)
+
+dnl DATABASE_ENV_PREF
+if test "$DB_TYPE" = 'Oracle'; then
+ test "x$ORACLE_HOME" = 'x' && AC_MSG_ERROR([Please declare the ORACLE_HOME environment variable])
+ DATABASE_ENV_PREF="\$ENV{'ORACLE_HOME'} = '$ORACLE_HOME';"
+fi
+
+dnl DATABASE_ENV_PREF
+if test "$DB_TYPE" = 'Sybase'; then
+ test "x$SYBASE" = 'x' && AC_MSG_ERROR([Please declare the SYBASE_HOME environment variable])
+ DATABASE_ENV_PREF="\$ENV{'SYBASE'} = '$SYBASE';"
+fi
+AC_SUBST(DATABASE_ENV_PREF)
+
+dnl DB_HOST
+AC_ARG_WITH(db-host,
+ AC_HELP_STRING([--with-db-host=HOSTNAME],
+ [FQDN of database server (default: localhost)]),
+ DB_HOST=$withval,
+ DB_HOST=localhost)
+AC_SUBST(DB_HOST)
+
+dnl DB_PORT
+AC_ARG_WITH(db-port,
+ AC_HELP_STRING([--with-db-port=PORT],
+ [port on which the database listens on]),
+ DB_PORT=$withval,
+ DB_PORT=)
+AC_SUBST(DB_PORT)
+
+dnl DB_RT_HOST
+AC_ARG_WITH(db-rt-host,
+ AC_HELP_STRING([--with-db-rt-host=HOSTNAME],
+ [FQDN of RT server which talks to the database server (default: localhost)]),
+ DB_RT_HOST=$withval,
+ DB_RT_HOST=localhost)
+AC_SUBST(DB_RT_HOST)
+
+dnl DB_DATABASE_ADMIN
+AC_ARG_WITH(db-dba,
+ AC_HELP_STRING([--with-db-dba=DBA],
+ [name of database administrator (default: root)]),
+ DB_DBA=$withval,
+ DB_DBA=root)
+AC_SUBST(DB_DBA)
+
+dnl DB_DATABASE
+AC_ARG_WITH(db-database,
+ AC_HELP_STRING([--with-db-database=DBNAME],
+ [name of the database to use (default: rt3)]),
+ DB_DATABASE=$withval,
+ DB_DATABASE=rt3)
+AC_SUBST(DB_DATABASE)
+
+dnl DB_RT_USER
+AC_ARG_WITH(db-rt-user,
+ AC_HELP_STRING([--with-db-rt-user=DBUSER],
+ [name of database user (default: rt_user)]),
+ DB_RT_USER=$withval,
+ DB_RT_USER=rt_user)
+AC_SUBST(DB_RT_USER)
+
+dnl DB_RT_PASS
+AC_ARG_WITH(db-rt-pass,
+ AC_HELP_STRING([--with-db-rt-pass=PASSWORD],
+ [password for database user (default: rt_pass)]),
+ DB_RT_PASS=$withval,
+ DB_RT_PASS=rt_pass)
+AC_SUBST(DB_RT_PASS)
+
+dnl WEB_USER
+AC_ARG_WITH(web-user,
+ AC_HELP_STRING([--with-web-user=USER],
+ [user the web server runs as (default: www)]),
+ WEB_USER=$withval,
+ ACRT_USER_GUESS([www www-data apache httpd nobody],[WEB_USER],[www])
+)
+AC_SUBST(WEB_USER)
+
+dnl WEB_GROUP
+AC_ARG_WITH(web-group,
+ AC_HELP_STRING([--with-web-group=GROUP],
+ [group the web server runs as (default: www)]),
+ WEB_GROUP=$withval,
+ ACRT_GROUP_GUESS([www www-data apache httpd nogroup nobody],[WEB_GROUP], [www]))
+AC_SUBST(WEB_GROUP)
+
+dnl RTGROUP
+AC_ARG_WITH(rt-group,
+ AC_HELP_STRING([--with-rt-group=GROUP],
+ [group to own all files (default: rt)]),
+ RTGROUP=$withval,
+ ACRT_GROUP_GUESS([rt3 rt $WEB_GROUP],[RTGROUP], [rt]))
+AC_SUBST(RTGROUP)
+
+dnl INSTALL AS ME
+# XXX TODO: The command below to figure out the group brokenly relies on
+# output order (and "id -gn" doesn't work on all platforms).
+my_group=$(groups|cut -f1 -d' ')
+my_user=${USER:-$LOGNAME}
+AC_ARG_WITH(my-user-group,
+ AC_HELP_STRING([--with-my-user-group],
+ [set all users and groups to current user/group]),
+ RTGROUP=$my_group
+ BIN_OWNER=$my_user
+ LIBS_OWNER=$my_user
+ LIBS_GROUP=$my_group
+ WEB_USER=$my_user
+ WEB_GROUP=$my_group)
+
+# Test for valid database names
+AS_IF([ test "$DB_TYPE" == "mysql" ],
+ [ AC_MSG_CHECKING([if database name is valid])
+ AS_IF([ echo $DB_DATABASE | $AWK '/-/ { exit 1 }' ],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_ERROR([no. database name ($DB_DATABASE) contains '-' which is not valid for mysql]) ]
+ )
+ ]
+ )
+
+
+dnl Set the value of apachectl
+AC_ARG_WITH(apachectl,
+ AC_HELP_STRING([--with-apachectl],
+ [instruct RT where to find your apachectl]),
+
+ APACHECTL=$withval,
+ APACHECTL=`which apachectl`)
+AC_SUBST(APACHECTL)
+
+dnl RT's standalone pure perl server
+AC_ARG_WITH(devel-mode,
+ AC_HELP_STRING([--with-standalone],
+ [Install modules for pure perl standalone server]),
+
+ RT_STANDALONE="1",
+ RT_STANDALONE="0")
+AC_SUBST(RT_STANDALONE)
+
+dnl RT's "maintainer mode"
+AC_ARG_WITH(devel-mode,
+ AC_HELP_STRING([--with-devel-mode],
+ [Turn on development aids that might hurt you in production]),
+
+ RT_DEVEL_MODE="1",
+ RT_DEVEL_MODE="0")
+AC_SUBST(RT_DEVEL_MODE)
+dnl This section maps the variable names this script 'natively' generates
+dnl to their existing names. They should be removed from here as the .in
+dnl files are changed to use the new names.
+
+dnl version numbers
+AC_SUBST(RT_VERSION_MAJOR, ${rt_version_major})
+AC_SUBST(RT_VERSION_MINOR, ${rt_version_minor})
+AC_SUBST(RT_VERSION_PATCH, ${rt_version_patch})
+
+dnl layout paths
+AC_SUBST([RT_PATH], ${exp_prefix})
+AC_SUBST([RT_DOC_PATH], ${exp_manualdir})
+AC_SUBST([RT_LOCAL_PATH], ${exp_customdir})
+AC_SUBST([RT_LIB_PATH], ${exp_libdir})
+AC_SUBST([RT_ETC_PATH], ${exp_sysconfdir})
+AC_SUBST([CONFIG_FILE_PATH], ${exp_sysconfdir})
+AC_SUBST([RT_BIN_PATH], ${exp_bindir})
+AC_SUBST([RT_SBIN_PATH], ${exp_sbindir})
+AC_SUBST([RT_VAR_PATH], ${exp_localstatedir})
+AC_SUBST([RT_MAN_PATH], ${exp_mandir})
+AC_SUBST([MASON_DATA_PATH], ${exp_masonstatedir})
+AC_SUBST([MASON_SESSION_PATH], ${exp_sessionstatedir})
+AC_SUBST([MASON_HTML_PATH], ${exp_htmldir})
+AC_SUBST([LOCAL_ETC_PATH], ${exp_custometcdir})
+AC_SUBST([MASON_LOCAL_HTML_PATH], ${exp_customhtmldir})
+AC_SUBST([LOCAL_LEXICON_PATH], ${exp_customlexdir})
+AC_SUBST([LOCAL_LIB_PATH], ${exp_customlibdir})
+AC_SUBST([DESTDIR], ${exp_prefix})
+AC_SUBST([RT_LOG_PATH], ${exp_logfiledir})
+
+dnl Configure the output files, and generate them.
+
+dnl Binaries that should be +x
+AC_CONFIG_FILES([
+ sbin/rt-dump-database
+ sbin/rt-setup-database
+ sbin/rt-test-dependencies
+ bin/mason_handler.fcgi
+ bin/mason_handler.scgi
+ bin/standalone_httpd
+ bin/rt-crontool
+ bin/rt-mailgate
+ bin/rt],
+ [chmod ug+x $ac_file]
+ )
+
+dnl All other generated files
+AC_CONFIG_FILES([
+ Makefile
+ etc/RT_Config.pm
+ lib/RT.pm
+ bin/mason_handler.svc
+ bin/webmux.pl],
+ )
+AC_OUTPUT
diff --git a/rt/docs/README.docs b/rt/docs/README.docs
new file mode 100755
index 0000000..38866b3
--- /dev/null
+++ b/rt/docs/README.docs
@@ -0,0 +1,2 @@
+Questions about docs should be sent to the RT Documentation Team (rt-docs@fsck.com)
+which is led by Meri.
diff --git a/rt/docs/Security b/rt/docs/Security
new file mode 100644
index 0000000..c9787ac
--- /dev/null
+++ b/rt/docs/Security
@@ -0,0 +1,25 @@
+RT2 runs setgid to some group (it defaults to 'rt').
+
+rt's configuration file, 'config.pm', is not world readable because it
+contains rt's database password. If a user gets access to this file, he
+can arbitrarily manipulate the RT database. This is bad. You don't want
+this to happen. config.pm is mode 550. No users should be members of
+the 'rt' group unless you want them to be able to obtain your rt password.
+
+If you're running the web interface, you'll need to make sure your webserver
+has access to config.pm. You could do this by letting your webserver's user
+be a member of the 'rt' group. This has the disadvantage of letting
+any mod_perl code on your web server have access to your RT password.
+
+Alternatively, you can run RT2 on its own apache instance bound to a high
+port on 127.0.0.1
+which runs as a non-priviledged user which is a member of the group 'rt'.
+
+Configure your webserver to proxy requests to RT's
+virtual directory to the apache instance you just set up.
+
+TODO: doc the apache configs needed to do this.
+
+The same technique can be used to run multiple RT2 instances on the same host.
+
+
diff --git a/rt/docs/design_docs/3.3-schema-redesign.txt b/rt/docs/design_docs/3.3-schema-redesign.txt
new file mode 100644
index 0000000..518eccd
--- /dev/null
+++ b/rt/docs/design_docs/3.3-schema-redesign.txt
@@ -0,0 +1,57 @@
+-- --------------------------------------- --
+-- RT 3.3 Schema redesign v7: 2004-11-08 --
+-- --------------------------------------- --
+
+TABLE CustomFields (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(200) NULL ,
+ Type varchar(200) NULL , -- Changed: see MaxValues below
+ MaxValues integer, -- New: 1 = Single, 0 = Multiple
+ Pattern varchar(255) NULL , -- New: regex to validate against
+ Repeated int2 NOT NULL DEFAULT 0 , -- New: repeated table entry
+ LookupType varchar(255) NOT NULL, -- New: "RT::Queue-RT::Ticket"
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 , -- only used on "pick CF" screen
+)
+
+-- This table replaces the "Queue" field in CustomFields
+TABLE ObjectCustomFields (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ CustomField int NOT NULL , -- CustomField ID
+ ObjectId integer NOT NULL, -- Final id of toplevel parent, or
+ -- the object itself if ParentType
+ -- is empty; 0 means global as usual
+ SortOrder integer NOT NULL DEFAULT 0 , -- this is used to sort the CFs
+);
+
+-- This table replaces TicketCustomFieldValues
+TABLE ObjectCustomFieldValues (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ CustomField int NOT NULL ,
+ ObjectType varchar(255) NOT NULL, -- Final target of the Object
+ ObjectId int NOT NULL , -- New: replaces the "Ticket" field
+ SortOrder integer NOT NULL DEFAULT 0 , -- New: for Repeated fields
+
+ Content varchar(255) NULL ,
+ LargeContent LONGTEXT NULL, -- New: data longer than 255 bytes
+ ContentType varchar(80) NULL, -- New: MIME type of LargeContent
+ ContentEncoding varchar(80) NULL , -- New: for binary LargeContent
+ Disabled int2 NOT NULL DEFAULT 0 , -- New: whether this is deleted
+)
+
+TABLE Transactions (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ ObjectType varchar(255) NULL, -- Final target of the Object
+ ObjectId integer NOT NULL DEFAULT 0 , -- New: replaces the "Ticket" field
+ TimeTaken integer NOT NULL DEFAULT 0 ,
+ Type varchar(20) NULL ,
+ Field varchar(40) NULL ,
+ OldValue varchar(255) NULL ,
+ NewValue varchar(255) NULL ,
+ ReferenceType varchar(255) NULL, -- NeW: Currently "RT::OCFV" only
+ OldReference integer NULL , -- New: Id of ReferenceType
+ NewReference integer NULL , -- New: Id of ReferenceType
+ Data varchar(255) NULL ,
+)
+
+-- vim: filetype=mysql shiftwidth=4 expandtab
diff --git a/rt/docs/design_docs/CARS b/rt/docs/design_docs/CARS
new file mode 100755
index 0000000..a4d2a78
--- /dev/null
+++ b/rt/docs/design_docs/CARS
@@ -0,0 +1,66 @@
+Conditional Automated Request Shuffler
+Initial Design. <jesse@fsck.com> 9 Nov 99
+
+#Try to find out what queue the incoming ticket is in
+#Try to find out the default action for this invocation
+#Read the ticket from STDIN
+#Obtain the actor
+#Obtain the serial # if we have one
+#If the ticket has a ticket-id
+ #if this is a 'comment'
+ #add the current mime objects as a 'comment'
+
+ #if this is 'correspondence'
+ #add the current mime object as 'correspondence'
+
+
+#if this ticket does not yet have a ticket id
+
+ #For now:
+ #Create a new ticket
+
+ #In the distant future
+
+ #load the regexp table matching this queue
+ #check the message agains the regexp table, ordered by precedence
+ #when we get a match
+ #get the ruleset for that regexp from the actions table
+ #evaluate the ruleset in order of precedence.
+ #if we get an 'exit' stop proccesing ALL rulesets
+wpw #if we get a 'forward,' forward it to 'value'.
+
+ #if we get a 'create,' create a request in 'value'
+ #elseif we get a 'map', add this as additional correspondence on ticket 'value'
+
+
+ #if we get an 'associate', associate the ticket number returned from the
+ 'create' or 'map' with the master ticket from 'value'
+
+ #if we get a 'reply',
+ #load the reply template with id 'value'
+ #replace strings in the template
+ #send the template
+
+
+
+
+CREATE TABLE Rules {
+ID int AUTO_INCREMENT,
+Desc varchar(120),
+Regexp varchar(80),
+Precedence int,
+MatchField varchar(20), #Can be a headername or 'any' all header names
+ #end in :
+
+
+CREATE TABLE Actions {
+Rule int,
+Action varchar(20), # Create, Forward, Squelch, Owner, Area, Associate
+Value varchar(20), #queue or email address
+Desc varchar(120)
+}
+
+CREATE TABLE Autoreplies {
+ID int AUTO_INCREMENT,
+Content text
+); \ No newline at end of file
diff --git a/rt/docs/design_docs/TransactionTypes.txt b/rt/docs/design_docs/TransactionTypes.txt
new file mode 100755
index 0000000..942b723
--- /dev/null
+++ b/rt/docs/design_docs/TransactionTypes.txt
@@ -0,0 +1,48 @@
+This is some loose scrabbling made by TobiX, they might eventually be relevant
+for 2.1.
+
+INTERFACES, in general
+
+should:
+
+- provide the user (client?) with a list of possible actions (methods).
+- let the user execute those actions (methods).
+- Return information to the user/client.
+
+There are two kind of actions/methods:
+
+- Information retrieval
+- Transactions
+
+For the first, I think the best thing is to just provide a lot of
+methods for it in the libraries, and let it be an Interface Design
+Issue what to show and how to show it.
+
+For the second, I think we can win in the long run on having a
+generalized methods for
+
+- listing transaction types.
+- creating & committing transactions.
+
+..with the possibility of just deploying new custom-developed modules
+when new transaction types are needed.
+
+
+$RT::TransactionTypes ...and...
+%RT::TransactionTypes
+ - global object which contains all TransactionTypes
+ - used by all UIs to create menues of possible (user) actions (one TransactionType is a user action)
+
+The UIs should call sth like
+$Ticket->AddTransaction($TransactionName), which should be equivalent
+with i.e. $Ticket->Correspond when $TransactionName is 'Correspond'
+(AUTOLOAD should call the do-sub if exists
+$RT::TransactionTypes{$TransactionName})
+
+The RT::Ticket::AddTransaction will create a new transaction of the
+right TransactionClass (maybe via a sub
+RT::TransactionTypes::NewTransaction). Then $Transaction->do is
+called.
+
+TransactionType->do initializes a new object of the right TransactionClass, and
+
diff --git a/rt/docs/design_docs/acls b/rt/docs/design_docs/acls
new file mode 100644
index 0000000..bb093ad
--- /dev/null
+++ b/rt/docs/design_docs/acls
@@ -0,0 +1,50 @@
+
+
+Does principal baz have right foo for object bar
+
+What rights does user baz have for object bar
+
+# {{{ Which principals have right foo for object bar
+
+
+if ($args{'ObjectType'} eq 'Ticket') {
+ $or_check_ticket_roles = " OR ( Groups.Domain = 'TicketRole' AND Groups.Instance = '".$args{'ObjectId'}."') ";
+ # If we're looking at ticket rights, we also want to look at the associated queue rights.
+ # this is a little bit hacky, but basically, now that we've done the ticket roles magic, we load the queue object
+ # and ask all the rest of our questions about the queue.
+ my $tick = RT::Ticket->new($RT::SystemUser);
+ $tick->Load($args{'ObjectId'});
+ $args{'ObjectType'} = 'Queue';
+ $args{'ObjectId'} = $tick->QueueObj->Id();
+
+}
+if ($args{'ObjectType'} eq 'Queue') {
+ $or_check_roles = " OR ( ( (Groups.Domain = 'QueueRole' AND Groups.Instance = '".$args{'ObjectId'}."') $or_check_ticket_roles )
+ AND Groups.Type = ACL.PrincipalType AND Groups.Id = Principals.ObjectId AND Principals.PrincipalType = 'Group') ";
+}
+
+if (defined $args{'ObjectType'} ) {
+ $or_look_at_object_rights = " OR (ACL.ObjectType = '".$args{'ObjectType'}."' AND ACL.ObjectId = '".$args{'ObjectId'}."') ";
+
+}
+
+my $query = "SELECT Users.* from ACL, Groups, Users, Principals, Principals UserPrinc, CachedGroupMembers WHERE
+ Users.id = UserPrinc.ObjectId AND UserPrinc.PrincipalType = 'User' AND
+ Principals.Id = CachedGroupMembers.GroupId AND
+ CachedGroupMembers.MemberId = UserPrinc.ObjectId AND
+ UserPrinc.PrincipalType = 'User' AND
+ (ACL.RightName = 'SuperUser' OR ACL.RightName = '$right') AND
+ (ACL.ObjectType = 'System' $or_look_at_object_rights) AND
+ (
+ (ACL.PrincipalId = Principals.Id AND
+ Principals.ObjectId = Groups.Id AND
+ ACL.PrincipalType = 'Group' AND
+ (Groups.Domain = 'SystemInternal' OR Groups.Domain = 'UserDefined' OR Groups.Domain = 'ACLEquivalence')
+ )
+ $or_check_roles
+ )";
+
+# }}}
+
+What objects does principal baz have right foo for
+;
diff --git a/rt/docs/design_docs/approval_notices b/rt/docs/design_docs/approval_notices
new file mode 100644
index 0000000..5e76119
--- /dev/null
+++ b/rt/docs/design_docs/approval_notices
@@ -0,0 +1,8 @@
+Notification on "your request approved by"
+Notification on "your request approved by all approvers"
+Notification on "your request denied by"
+Reject ticket on rejection of any approval
+
+"Ticket N is pending your approval"
+
+
diff --git a/rt/docs/design_docs/approval_template b/rt/docs/design_docs/approval_template
new file mode 100644
index 0000000..16a988c
--- /dev/null
+++ b/rt/docs/design_docs/approval_template
@@ -0,0 +1,25 @@
+===Create-Ticket: approval
+ { my $name = "HR";
+ my $groups = RT::Groups->new($RT::SystemUser);
+ $groups->LimitToUserDefinedGroups();
+ $groups->Limit(FIELD => 'Name', OPERATOR => '=', VALUE => "$name");
+ $groups->WithMember($TransactionObj->CreatorObj->Id);
+
+ my $groupid = $groups->First->Id;
+
+ my $adminccs = RT::Users->new($RT::SystemUser);
+ $adminccs->WhoHaveRight(Right => 'AdminGroup', IncludeSystemRights => undef, IncludeSuperusers => 0, IncludeSubgroupMembers => 0, Object => $groups->First);
+
+ my @admins;
+ while (my $admin = $adminccs->Next) {
+ push (@admins, $admin->Name);
+ }
+ }
+ Queue: Approvals
+ Type: Approval
+ AdminCcs: {join (", ",@admins) }
+ Depended-On-By: {$tickets{'TOP'}->Id}
+ Refers-To: {$tickets{'TOP'}->Id}
+ Due: {time + 86400}
+ Content-Type: text/plain
+ Content: Your approval is requested for the ticket {%$tickets{'TOP'}->Id}: {$tickets{'TOP'}->Subject}
diff --git a/rt/docs/design_docs/cf_search b/rt/docs/design_docs/cf_search
new file mode 100644
index 0000000..456a9fe
--- /dev/null
+++ b/rt/docs/design_docs/cf_search
@@ -0,0 +1,72 @@
+find all tickets where:
+
+
+ CF Foo
+ Has values (talk or read) AND
+ Has values (bar and baz) AND
+ doesn't have values (bing or bong)
+
+
+LimitCustomFieldValues {
+ my %args = ( CustomField => undef,
+ ClauseId => 'CustomFields',
+ OPERATOR => undef,
+ ENTRYAGGREGATOR => undef,
+ VALUES => undef,
+ @_) ;
+
+ unless ( $self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField'} ) {
+ $self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField'} = $self->NewAlias('CustomFields');
+ $self->Join(TABLE1 =>$self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField' },
+ FIELD1 => 'QueueId',
+ TABLE2 => 'main', FIELD2 => 'QueueId');
+
+ if ($args{'OPERATOR'} =~ /!=|IS/i) {
+ }
+ else {
+ }
+
+}
+ # {{{ if it's a keyword
+ elsif ( $TYPES{ $restriction->{'FIELD'} } eq 'CUSTOMFIELD' ) {
+
+ my $null_columns_ok;
+ my $TicketCFs = $self->Join( TYPE => 'left',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'TicketCustomFieldValues',
+ FIELD2 => 'Ticket' );
+
+ foreach my $value ( @{ $restriction->{'VALUES'} } ) {
+ $self->SUPER::Limit( ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => $restriction->{'OPERATOR'},
+ VALUE => $value,
+ QUOTEVALUE => $restriction->{'QUOTEVALUE'},
+ ENTRYAGGREGATOR => 'AND', );
+ }
+ if ( ( $restriction->{'OPERATOR'} =~ /^IS$/i ) or ( $restriction->{'OPERATOR'} eq '!=' ) ) {
+ $null_columns_ok = 1;
+ }
+
+ #If we're trying to find tickets where the keyword isn't somethng, also check ones where it _IS_ null
+ if ( $restriction->{'OPERATOR'} eq '!=' ) {
+ $self->SUPER::Limit( ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'OR', );
+ }
+
+ $self->SUPER::Limit( LEFTJOIN => $TicketCFs,
+ FIELD => 'CustomField',
+ VALUE => $restriction->{'CUSTOMFIELD'},
+ ENTRYAGGREGATOR => 'OR' );
+
+ }
+
+ # }}}
+
+ }
+
diff --git a/rt/docs/design_docs/cli_spec b/rt/docs/design_docs/cli_spec
new file mode 100644
index 0000000..ae5f29f
--- /dev/null
+++ b/rt/docs/design_docs/cli_spec
@@ -0,0 +1,31 @@
+
+Things the cli must do
+ create ticket
+ comment
+ reply
+ update ticket metadata
+ search for tickets
+ update a bunch of tickets.
+ list tickets
+ login/logout
+
+
+should support multiple rt servers
+
+create/edit/update should use EDITOR or take from a file or stdin
+
+should be able to update ticket sttributes from a commandline without invoking an editor or needing to use stdin.
+
+login/logout should store RT session cookies rather than constantly transmitting the username/password combo.
+
+rtserver and rt username should come from env variables. but should be able to be overridden by commandline options.
+
+rt password should be able to be specified on the commandline (say from a script) or, failing that be prompted for within the application (as rt's sbin/initdb script does) ...or maybe able to be read from a stash file on disk.
+
+must be able to dowaload attachments from cli.
+
+ it might also be cool to be able to generate session-length urls for attavhments so you can use a browser. but that's not necessary.
+
+
+I'm envisioning this as similar to the subversion cli, actually.
+
diff --git a/rt/docs/design_docs/cvs_integration b/rt/docs/design_docs/cvs_integration
new file mode 100644
index 0000000..45a758f
--- /dev/null
+++ b/rt/docs/design_docs/cvs_integration
@@ -0,0 +1,164 @@
+jesse@FSCK.COM: ok. anyone here
+ interested in having RT as a bug tracker integrated with CVS? ()
+
+marc: in principle, sure. ()
+
+jesse@FSCK.COM: want to write up your
+ ideal of how such a beast would work? ()
+
+alex_c: what sort of integration are you thinking of, Jesse? ()
+
+jesse@FSCK.COM: well, that's what I want
+ to know, alex. lots of people want their bug trackers tied to their
+ version control. I want to know what people want it to _do_ ;) ()
+
+alex_c: weird. :) ()
+
+marc: similarly to what the debian bts does.
+ you put a magic string ("rt-closes#123") and it causes the bug in rt to
+ be closed (or appended with a different magic string) with the commit
+ message. also nice would be if rt would then generate links to a
+ webcvs server. ()
+
+jhawk: Hrmm. cvs front-end that strips 'em out?
+ Perhaps with RT: lines instead of CVS: lines in the commit
+ interaction? ()
+
+marc: the magic string goes in the commit
+ message, that is. no, use one of the magic post-commit scripts. ()
+
+
+jesse@FSCK.COM: well, there's also the
+ pre-commit script to lock out commits wihtout a ticket id ()
+
+jhawk: Personally, I don't want to force special
+ magic strings to the bug-tracking system, some of which may be
+ confidential, to appear in the cvs logs. ()
+
+marc: I could see wanting that on a release
+ branch. ()
+
+jhawk: I also think it would be cool to supply
+ template stuff for you to edit. ()
+
+jesse@FSCK.COM: I'm not sure cvs can be
+ made to do that. can it? (generate templates) ()
+
+jhawk: It would be reasonable, in my model, to
+ turn some kinds of RT: lines into things that fell in the commit
+ message, but not all kinds. ()
+
+marc: I don't quite see jhawk's objection. ()
+
+ghudson: In my observation, locking out commits
+ without a ticket ID is usually an impediment to development, and leads
+ to developers having the one bug which all commits cite. ()
+
+jhawk: If you had a CVS frontend, it could geneate
+ the template and feed it to 'cvs commit -m' ()
+
+ghudson: CVS can generate templates and verify
+ that they have been filled in. ()
+
+jhawk: What Greg says sounds cool; greg, what do
+ you mean? marc: one sec. ()
+
+marc: I think assuming a frontend is a terrible
+ idea. ()
+
+jesse@FSCK.COM: greg: agreed. but people
+ seem to want it. the idea would be only for a locked down release
+ branch. ()
+
+jhawk: marc: So, I might want to close an open
+ ticket as part of a commit message without that showing up in the
+ coommit message. Or to insert a splufty long comment into a ticket
+ while I do the commit but not close or really change the state, and
+ that comment might want to ramble a lot but not include that ramble in
+ the commit message. ()
+
+jesse@FSCK.COM: well, then arguably, you
+ might want to not use the commit message for that update, but instead
+ just go straight to the bts ()
+
+marc: I think the idea is to force you to
+ mention the ticket closing in the commit message. ()
+
+jesse@FSCK.COM: but yeah, state changing
+ and 'update messages' are separate concepts that should both be
+ supported. ()
+
+jesse@FSCK.COM: part of the idea is to
+ drag the commit message into the BTS ()
+
+jhawk: Err, I think it quite frequent that I want
+ to put separate info in both the commit message and the ticket system,
+ and entering them at the same time seems cool. ()
+
+jesse@FSCK.COM: ok. noted. I'll see if
+ that's doable, when i get around to this. ()
+
+marc: so I think you want a custom front-end,
+ but I don't think what you want is what jesse is talking about. ()
+
+jesse@FSCK.COM: the thing that would be
+ really cool that scare the pants off me is tracking which branches bugs
+ exist in / are fixed in ()
+
+jesse@FSCK.COM: what jhawk wants should
+ be doable, now that I understand his reqts. ()
+
+marc: that would require the bts to understand
+ branches in some fundamental way. ()
+
+jesse@FSCK.COM: yes. see above, about
+ the pants. ()
+
+sly: uh oh, not more people losing their pants... ..
+
+
+ghudson: RT needs to know the names of branches
+ and their structure (so that you can tell it "fixed in foo" and it
+ knows that the bug is still fixed in anything that branches off of foo,
+ but not necessarily in other new branches), but nothing more than that.
+
+jhawk: So, note that what I'm describing is how
+ I'd like the UI to be, from a generic architectural level, and not
+ really thinking terribly specific. Greg, can you explain the CVS
+ template thing? ()
+
+jesse@FSCK.COM: and it needs to know
+ exactly "when" a branch happens. because "fixed in foo" won't fix
+ something that branched off foo yesterday ()
+
+marc: jesse was talking about integrating rt
+ with cvs. building a new developent+repository+bts from scratch would
+ be a problem with larger scope :-) ()
+
+jesse@FSCK.COM: marc: was that in
+ response to jhawk? ()
+
+ghudson: CVS and templates: "rcsinfo" lets you
+ specify a template for log messages, and "commitinfo" lets you check
+ them. ()
+
+ghudson: Er, sorry, my bad.
+ s/commitinfo/verifymsg/ ()
+
+marc: with cvs, if you have the revision number
+ of the fix (which you should). you can use the branch version number to
+ get a date and see when the branch happened relative to the fix. ()
+
+marc: jesse: yes. ()
+
+jesse@FSCK.COM: Ok. would people
+ consider "integration with CVS" to be subpar or incomplete if it didn't
+ deal with tracking branches? ()
+
+marc: incomplete relative to an ideal, but not
+ subpar, as it would still be useful. ()
+
+allbery@CS.CMU.EDU: CVS's branch
+ support sucks so much that failure to work with it is hardly a bug ()
+
+
diff --git a/rt/docs/design_docs/delegation b/rt/docs/design_docs/delegation
new file mode 100644
index 0000000..0e57059
--- /dev/null
+++ b/rt/docs/design_docs/delegation
@@ -0,0 +1,115 @@
+Group ACLs
+
+ the rights:
+
+
+ CreatePersonalGroup
+ CreateGroup
+
+ AdminGroup
+ * Update group metadata and access control list
+ AdminGroupMembers
+ * Add ad delete members of this group
+ ModifyOwnMembership
+ * Join and quit this group
+
+
+ the primitives:
+
+In user.pm
+
+=item HasRight { Right => 'somerightname', ObjectType => 'Group', ObjectId => 'GroupId'
+
+ Returns true if this user has the right 'somerightname' for
+the group with id 'Id'
+
+=cut
+
+
+=item RightsForObject { ObjectType => 'Group', ObjectId =>'GroupId' }
+
+in users.pm
+
+=item WhoHaveRight { Right =>'somerightname', ObjectType => 'Group', ObjectId => 'GroupId' }
+
+
+ Finds all users who have the right 'somerightname' for the group
+in question.
+
+ If a user has "AdminGroupMembers" globally and we ask about
+ group 23, that user should be found.
+
+=cut
+
+Users must be able to delegate individual rights
+
+ * Is it that users can delegate any and all rights but it's
+ only rights they _have_ which actually grant rights.
+
+rights must not be redelegated
+
+users must be able to create groups to which rights can be delegated.
+
+Only users who have the "delegate rights" right can delegate rights.
+
+
+When a user's right to do something is revoked, the delegation must
+be revoked
+
+ * For any delegated ACL check, the delegator's right must be
+ checked immediately after the delegatee's right.
+ If a user has had a right delegated by multiple parties,
+ this may mean that we need to actually loop through and check
+ a bunch of possible delegations. Or can we craft a "has delegated
+ right" ACL check.
+
+
+
+
+
+
+
+ACL 1 Group Q has the right to Frob ObjectI.
+ACL 2 User A has the right "DelegateRights"
+
+Group Q has the member Group S
+Group S has the member Group R
+Group S has the member Group T
+Group R has the member user A
+Group T has the member user A
+
+User A delegates to Group P the right to Frob ObjectI
+
+ New ACL rule:
+
+ ACL 3: Group P has the right to Frob ObjectI
+ as delegated from ACL1 by User A
+
+
+In the case where ACL1 is revoked:
+
+ find all acls which are delegated from ACL1.
+ Delete them
+
+In the case where User A is removed from group R
+
+ Get the list of all groups that A was in by way of group R before the removal
+ Get the list of all groups that A is in _after_ the removal.
+
+ Find all the ACEs granted to each group that A is no longer in.
+ For each ACE in that list, find all the rights that A has delegated.
+ Whack them.
+
+In the case where Group S is removed from group Q
+
+
+ Get a list of all groups that S was in by way of Q before the removal
+ Call this list O.
+
+ For each user X who's a member of S (directly or indirectly):
+ Get a list of all groups that X is in after removal.
+ For each group in O that X is no longer a member of:
+ Find all ACEs granted to O
+ For each ACE, look up all the delegations that X has made.
+ For each delegation
+ WHACK IT
diff --git a/rt/docs/design_docs/evil_plans b/rt/docs/design_docs/evil_plans
new file mode 100644
index 0000000..5b5cc58
--- /dev/null
+++ b/rt/docs/design_docs/evil_plans
@@ -0,0 +1,162 @@
+Current planned 2.2 feature list. subject to change.
+
+
+Core
+
+
+
+Web UI
+
+Should New "Tools" top level menu
+Should "This week in RT" at a glance.
+Nice "RT Stats" overview.
+Nice recent and favorite items
+
+
+per-user configuration
+
+Must Saveable user preferences.
+
+ The ideal implementation would be "saveable user metadata",
+ including things like "Alternate Email Addresses". To
+ do this right, not all user metadata would be directly
+ editable by the user who has "ModifySelf" it may be that
+ this is a "system" datastore that gets accessed by various
+ functions, some of which the user has access to modify and
+ some of which only the system does.
+
+ API: Set field "FOO" to value "BAR" for user BAZ
+ What values does field "FOO" have for user BAZ?
+ Clear all values of "FOO" for user BAZ
+ What users have value "BAR" for field "FOO"
+
+ Example usages:
+
+ What users have the alternative email address matching
+ "boo@fsck.com"
+ What custom searches does user BAZ have defined?
+ What is baz's default queue?
+
+ Actually, I feel a little sketchy about Alternative Email
+ Addresses in there. I'm not quite sure why yet.
+
+ The same would really be useful for queues. Damn it. I think
+ I want a registry.
+
+
+
+Searching
+
+Must Ability to define search result format.
+should Saveable user searches.
+nice Sharable searches.
+
+
+Scrips
+
+must Include more Conditions; at least those contributed so far
+ that make sense in my grand scheme of things
+
+should The name should change to something that people don't think is
+ spelled wrong. ("I will not invent words\n" x 1000)
+
+nice Scrips could apply to a list of queues, rather than just one queue or
+ all of them.
+
+
+Custom fields
+
+Nice Date custom fields
+Nice Some way to order and group custom fields.
+Nice Default values
+Nice Required values
+Nice Make custom fields apply to an enumerated list of queues,
+ rather than just one.
+
+
+Web infrastructure
+
+
+Installation
+
+Should Better FSSTD conformance:
+ bins in /bin
+ admin tools in /sbin (does this include rtadmin?)
+ ephemeral data in /var
+ rename config file
+ force local RT search path?
+
+Mail gateway
+
+must Integrate gpg-authenticated command-by-mail mode
+
+
+
+Core
+
+should Use apache logging, if available
+should Use syslog, if available.
+should Mail user new password, as an Action, so it can be invoked either
+ as a scripaction or from the web ui.
+
+
+
+Web Services Framework
+
+Should Expose an API to create a ticket by HTTP posting an XML document.
+Should Provide an RSS feed to display tickets matching certain criteria
+Nice Allow ticket updates via the web ui
+Nice Export full ticket metadata and history as XML
+
+Note: I currently favor the REST philosophy that GET and POST to specific,
+ defined URLs provides everything one needs to build comprehensive
+ web services without the massive added complexity of a SOAP or XML-RPC
+ framework. Sadly, the world doesn't agree with me
+
+
+ACLs:
+
+Wish New ACL primitives for:
+
+ List all users who have right "FOO" on object "BAR"
+ List all rights user "BAZ" has for object "BAR"
+ List all objects for which user "BAR" has right "FOO"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+For the near future:
+
+ Use case:
+ Jesse wants to get notified of all tickets in queue 'RT Bugs'
+ with a severity of 'critical' and also have a requestor whcih matches 'fsck.com'.
+ I'm not sure this is the best idea.
+
+
+ Site admins define a number of subscriptions and can sign up individual
+ users, groups or metagroups to get mail on that subscription.
+
+ Basically, an admin would define "On Condition, notify as comment with
+ template _template_"
+
+ There would be a new table called "subscriptions"(?) that would have
+ the structure:
+
+ id
+ ScripId
+ PrincipalType ENUM: User, Group, Owner, Requestors, AdminCcs, Ccs
+ PrincipalId -- UserId or GroupId. For Owner, Requestors, AdminCcs, Ccs, it doesn't really make a lick of difference.
diff --git a/rt/docs/design_docs/groups_notes b/rt/docs/design_docs/groups_notes
new file mode 100644
index 0000000..234fd37
--- /dev/null
+++ b/rt/docs/design_docs/groups_notes
@@ -0,0 +1,88 @@
+CREATE TABLE Prinicpals (
+ id int auto_increment
+ PrincipalType VARCHAR(16) not_null,
+ PrincipalId int # foreign key to Users or Groups, depending
+)
+
+CREATE TABLE Groups (
+ id int auto_increment,
+ Domain varchar(255),
+ Instance varchar(16),
+ Name varchar(255),
+ Description varchar(255),
+);
+CREATE TABLE ACL (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Principal integer NULL , #Foreign key to principals
+ RightName varchar(25) NULL ,
+ RightDomain varchar(25) NULL ,
+ RightInstance integer NULL ,
+ PRIMARY KEY (id)
+);
+
+CREATE TABLE GroupMembers (
+ id int auto_increment,
+ Group int, # foreign key to Principals
+ Member int # foreign key to Principals
+)
+
+create table GroupMembersCache (
+ id int auto_increment,
+ Group int, # foreign key to Principals
+ Member int, # foreign key to Principals
+ Via int, #foreign key to g_m_u
+)
+
+insert into principals values ('bubbles);
+insert into principals values ('fubar');
+insert into principals values ('sheeri');
+insert into principals values ('sgw');
+
+insert into principals values ('staff');
+insert into principals values ('sysadmin');
+insert into principals values ('senior admin');
+
+
+insert into group_members values(1, 'staff', 'bubbles');
+insert into group_members values(2, 'sysadmin', 'sheeri');
+insert into group_members values(3,'senior admin', 'sgw');
+insert into group_members values(4,'senior admin', 'fubar');
+insert into group_members values(5, 'sysadmin', 'senior admin')
+
+Groups
+
+
+
+Domain Queues
+Instance <queueid#>
+Name AdminCc, Cc
+
+/Queues/1/AdminCc
+/Queues/3/Cc
+
+Domain Tickets
+Instance <#n>
+Name Owner, Requestor, Cc, AdminCc
+
+/Tickets/1/Owner
+/Tickets/1/Requestor
+/Tickets/1/Cc
+ Has members: /Queues/whatever queue the ticket has/Cc
+/Tickets/1/AdminCc
+ Has members: /Queues/whatever queue the ticket has/AdminCc
+
+
+Domain Users
+Instance <userid>
+
+/Users/1/MyDelegates
+/Users/1/MyOtherDelegates
+
+
+Domain System
+Name Admins, AdminManagers
+
+/System/Administrators
+/System/Blah
+
+
diff --git a/rt/docs/design_docs/link-definitions.txt b/rt/docs/design_docs/link-definitions.txt
new file mode 100644
index 0000000..e109744
--- /dev/null
+++ b/rt/docs/design_docs/link-definitions.txt
@@ -0,0 +1,143 @@
+For 2.0, those Linking actions should be supported:
+
+1. DependentOn; TobiX-style.
+
+ BASE is dependent on TARGET.
+
+ ...meaning that TARGET has to be resolved before BASE (really) is
+ resolved.
+
+ According to TobiX, those "weird action" makes sense:
+ ...when the link and/or TARGET is created, the BASE might be stalled.
+ Alternatively, this should be very trivial to request through the UIs.
+ ...when the TARGET is resolved, BASE will be reopened if it's stalled.
+
+ An alternative to those "weird actions" is to have some run-time logic that
+ takes care of this; i.e. letting the search interface handle "please hide
+ all requests with unresolved dependencies"
+
+ TobiX will need to make dependency links into Bugzilla.
+
+ Dependency links should be made when more work to BASE should be done
+ after the TARGET is resolved and/or BASE can't be resolved before TARGET is
+ resolved.
+
+ Dependency links are often 1:1, but n:n links makes sense; one ticket can
+ depend on several others, several tickets can depend on one ticket, etc.
+
+ Loops don't make sense at all, but the system above won't break if it
+ encounter loops.
+
+ Dependency links is more for workflow than anything else. When a new
+ TARGET is created, some of the work might be passed over to another
+ department/person ... but _not_ the responsibility for the communication
+ with the external requestor.
+
+2. MemberOf link (grouping)
+
+ BASE is a member of TARGET.
+
+ TobiX-style "weird actions":
+ ...when TARGET is beeing replied to, all BASE requestors should get the
+ reply.
+
+ ...when TARGET is resolved, all BASE tickets should be resolved (unless
+ they have other unresolved Dependencies/MemberOf links).
+
+ ...when all BASEs to one TARGET are resolved, TARGET should be resolved.
+
+ The alternative is to let the user choose "reply to all" and "resolve all"
+ through the user interfaces.
+
+ MemberOf should be used when BASE ticket states more or less the same as
+ the TARGET ticket, and we do want to give a reply to all requestors, but we
+ don't want to merge them (Individual tickets from individual external
+ requestors should be respected as separate entities). If BASE tickets from
+ more than one external requestor is linked to a TARGET ticket, we denote
+ the TARGET ticket as a "Group ticket". This is only a documentation
+ definition, you won't find any references to "Group tickets" in the source.
+
+ I think the proper etiquette should be to clearly state in a reply to a
+ group ticket that the mail is going to several persons, and that the
+ requestor should reply back if they feel their Ticket hasn't got the
+ attention it deserves. The user documentation should reflect this.
+
+ MemberOf links can also be used to hand away the work flow. The person in
+ charge of the TARGET ticket will also be in charge of the BASE tickets and
+ the communication with the end user.
+
+ If a work task needs to be splitted into two subtasks, MemberOf might also be
+ used.
+
+ 1:n links makes more sense, but n:n can also work in some cases.
+ The reply stuff might break seriously upon loops. Recursement might be
+ handy for splitting a work task into subtasks (making a hierarchical tree
+ of the worktasks).
+
+
+3. Merge (connecting)
+
+ BASE is the same as TARGET.
+
+ ...the system should somehow merge together transactions for both tickets.
+ ...BASE should be more or less deleted, only the TARGET should apply.
+ ...actions done toward BASE should be redirected to TARGET.
+
+ I think MergeLinks should be used when two tickets accidentally has
+ appeared twice in the system, and/or there is no reason to keep the two
+ tickets separately. It might be that it's the same requestor (i.e.
+ clicking the "send" button twice in a web environment) or that we don't
+ care much about giving the requestor individual follow-up (typically
+ "internal" requestors, etc.)
+
+ Based on user feedback, merged tickets will be displayed as the same ticket
+within RT's user interfaces. but the original tickets' transactions will be
+kept separated in the database. this may require some magic.
+
+4. RefersTo / No Action link (linking)
+
+ BASE is somewhat related to TARGET
+
+ No special actions will be taken.
+
+ Loops might maybe make sense
+
+BASE and TARGET are usually Tickets within one RT instance, but it
+might also point to external RT instances, other DB systems, etc.
+
+
+
+
+In future revisions, it should be very easy to set up site-specific link action types.
+We should also consider to include more linking actions in the box.
+
+An example stolen from John Rouillard. Eventually the [comments] should be
+removed, and the text modified to fit the planned 2.0 link actions:
+
+ ticket problem
+ 1 can't connect to hosts with netscape
+ 2 ping is broken
+ 3 Can't send email: error no space on spool/mqueue
+
+ You have the above in the queue. You realize that DNS is down. Spawn
+ a ticket
+
+ 4 DNS is down
+
+ mergelink 1 and 2 to it [I would rather say "make a MemberOf link _or_ a
+ dependency link from 1 and 2 to 4" --TobiX] (if you choose to stall 1 and
+ 2 automatically feel free, its just a shell script change) [well, you
+ might choose dependency instead of MemberOf --TobiX]. The person working
+ on 3 has come to the conclusion that outgoing mail is backing up because
+ of the DNS failure. She has cleared space by copying the mail queue to
+ another disk, but can't really get email working till DNS is up. So she
+ creates a Dependency linkon ticket 4 stalling ticket 3.
+
+ We finally get DNS working and resolve ticket 3. What happens? Tickets 1
+ and 2 are resolved and email is sent to requestors notifying them of the
+ resolution [This is the default behaviour for 2.0 MemberOf-linked tickets.
+ Remember that if we send Replies to "Group Tickets" (that is, the target
+ of several "MemberOf" links) --TobiX]. Ticket 4 [should be 3? --TobiX] is
+ reopened and the person working on it starts flushing the mail queue and
+ the moved mailq by hand.
+
diff --git a/rt/docs/design_docs/realflow.txt b/rt/docs/design_docs/realflow.txt
new file mode 100644
index 0000000..3717e27
--- /dev/null
+++ b/rt/docs/design_docs/realflow.txt
@@ -0,0 +1,191 @@
+- I have a MonitoredQueue that sets tickets to "Monitored"
+ if its subject matches /monitored/.
+
+- I want to have a kind of Ticket that are 'Monitored'.
+- I want all monitored tickets, when they are overdue for
+ 14 days, to:
+ - send notification to manager
+ - mark as stalled
+- I want all monitored tickets, when they are overdue for
+ 28 days, to:
+ - mark as rejected
+- I want to query all tickets that are monitored as such
+- I want to modify 14 => 15 and have it affect all existing
+ tickets that are monitored
+
+{
+- I want to add a new "overdue for 27 days, add a 'ultimatum'
+ correspondence to it" rule for all monitored tickets.
+- I want to add a new "overdue for 27 days, add a 'ultimatum'
+ correspondence to it" rule for all _new_ monitored tickets
+ without affecting existing ones.
+}
+
+- The user of OrderRequest queue needs to fill a numeric "CF",
+ called "Price".
+- On creation, it needs to create following approvals:
+ - "Manager" approval if CF.Price is > 1000
+ - "President" approval if CF.Price is > 2000
+- When all of "M", "P" are resolved (if any, or if there were none
+ to begin with), Create a new approval, "Finance".
+- If any approvals above is rejected, reject the original ticket.
+- If "Finance" is resolved, resolve original ticket.
+- If "Finance" is rejected, create an approval for "CEO".
+- If "CEO" is resolved, resolve the original ticket.
+- If "CEO" is rejected, reject the original ticket.
+
+[RuleAction CreateTicketWithRuleset]
+ -> ReleaseMyLockOnRuleset $ruleset
+ -> UnlessLockOnRuleset $ruleset
+ # i.e. if no active tickets still have a lock on it
+ -> ForceCreateTicketWithRuleset $ruleset
+
+[Queue OrderRequest]
+ -> Condition: OnCreate
+ Action: AddTicketRuleSet "PurchaseApproval"
+ # Triggers immediately
+
+[RuleSet: PurchaseApproval]
+ -> Condition: OnCreate
+ Condition: CF.Price > 1000
+ Action: CreateTicketWithRuleset "ManagerApproval"
+ -> Condition: OnCreate
+ Condition: CF.Price > 2000
+ Action: CreateTicketWithRuleset "PresidentApproval"
+ -> Condition: OnCreate
+ Action: CreateTicketWithRuleset "FinanceApproval"
+ -> Condition: OnReject
+ Action: DeleteTree
+
+[RuleSet: ManagerApproval]
+ -> Condition: OnCreate
+ Action: Prohibit Ruleset "FinanceApproval"
+ -> Condition: OnResolve
+ Action: CreateTicketWithRuleset "FinanceApproval"
+ -> Condition: OnReject
+ Action: RejectTicket TOP
+
+[RuleSet: PresidentApproval]
+ -> Condition: OnCreate
+ Action: Prohibit CreateTicketWithRuleset "FinanceApproval"
+ -> Condition: OnResolve
+ Action: CreateTicketWithRuleset "FinanceApproval"
+ -> Condition: OnReject
+ Action: RejectTicket TOP
+
+[RuleSet: FinanceApproval]
+ -> Condition: OnCreate
+ Action: Prohibit RuleSet "CEOApproval"
+ -> Condition: OnResolve
+ Action: ResolveTicket TOP
+ -> Condition: OnReject
+ Action: CreateTicketWithRuleset "CEOApproval"
+
+[RuleSet: CEOApproval]
+ -> Condition: OnResolve
+ Action: ResolveTicket TOP
+ -> Condition: OnReject
+ Action: RejectTicket TOP
+
+
+
+Prohibit Ticket Operation:
+ Ruleset CEOApproval
+
+
+
+
+
+
+
+ ,--------.
+[TOP] --> [M] --> [F]
+ ` `-> [P] -'
+ `
+ `-> [X] --> [Y]
+
+
+[TOP] => [Approval]
+ -> Queue: B
+ -> Rule: yyy
+ -> Workflow: W
+ -> Stage: Approval
+ -> Rule: xxx
+
+isa_ok( $Approval->Type, 'RT::Ticket' );
+is( $Approval->Workflow->Name, 'W' );
+is( $Approval->Stage->Name, 'Approval' );
+
+[Queue: A]
+ -> Workflow: W
+
+[Workflow: W]
+ -> Stage: TOP
+ -> Stage: Approval
+ -> Stage: SUCCESS
+ -> Stage: FAIL
+
+"RuleCollections"
+
+[Stage: TOP]
+ -> Rule: OnCreate RunStage Approval
+
+ok( TicketA->Rules->HasEntry($ApprovalRule) )
+ok( TicketB->Rules->HasEntry($ApprovalRule) )
+
+[Rule: Approval]
+ -> Rule: OnResolve RunStage SUCCESS
+ -> Rule: OnReject RunStage FAIL
+
+[Stage: SUCCESS]
+ -> Rule: OnCreate SetStatus('resolved') TOP
+
+[Stage: FAIL]
+ -> Rule: OnCreate SetStatus('rejected') TOP
+
+[Unassociated]
+ - Rule FOO: OnAnything {
+ CreateTicketIfNotBlocked StageFOO
+ AddLink DependedOnBy TOP to Stage1
+ AssignRule DoStage2 to Stage1
+ AssignRule DoStage3 to Stage1
+ }
+ - Rule BAR: OnAnything {
+ CreateTicketIfNotBlocked StageBAR
+ DoSomethingBizzare
+ }
+
+ ,==> [Stage0] ==>.
+ , .
+[TOP] ==> [Stage1] ==> [Stage3]
+ ` '
+ `==> [Stage2] ==>'
+
+OnTransaction:
+ $self->Ticket->Queues->Scrips->Apply
+
+OnTransaction:
+ $self->Ticket->Queues->Scrips->Apply
+ ->Scrips->Apply
+
+OnTransaction:
+ $self->Ticket->Queues->Scrips->Apply
+ ->Stages->Scrips->Apply
+
+[QueueX]
+ - Rule:
+ OnCreate:
+ RunRule FOO
+
+[QueueY]
+ - Rule:
+ OnWhatever:
+ RunRule FOO
+
+
+[TOP] => [Stage1] => [Stage2] => [END]
+ `- => [Stage3] => [END]
+ ` -> [Stage4]
+
+[Stages]
+ ->
diff --git a/rt/docs/design_docs/recursive_group_membership_algorithm b/rt/docs/design_docs/recursive_group_membership_algorithm
new file mode 100644
index 0000000..250b9ad
--- /dev/null
+++ b/rt/docs/design_docs/recursive_group_membership_algorithm
@@ -0,0 +1,109 @@
+Group A has members 1, 2, 3
+
+ Cached members 1 is a member of A via ""
+ 2 is a member of A via ""
+ 3 is a member of A via ""
+
+
+Group B has members A, 4, 5
+
+ Cached members: 4 is a member of B via "" $1
+ 5 is a member of B via "" $2
+ A is a member of B via "" $3
+ 1 is a member of B via "$3" $4
+ 2 is a member of B via "$3" $5
+ 3 is a member of B via "$3" $6
+
+Group C has members A, B, 6
+ 6 is a member of C via "" $7
+ A is a member of C via "" $8
+ 1 is a member of C via $8 $9
+ 2 is a member of C via $8 $10
+ 3 is a member of C via $8 $11
+ B is a member of C via "" $12
+ 4 is a member of C via $12 $13
+ 5 is a member of C via $12 $14
+ A is a member of C via $12 $15
+ 1 is a member of C via $15 $16
+ 2 is a member of C via $15 $17
+ 3 is a member of C via $15 $18
+
+
+
+Group D has members A, C
+
+ A is a member of D via "" $19
+ 1 is a member of D via $19 $20
+ 2 is a member of D via $19 $21
+ 3 is a member of D via $19 $22
+ C is a member of D via "" $23
+ 6 is a member of D via $23 $24
+ A is a member of D via $23 $25
+ 1 is a member of D via $25 $26
+ 2 is a member of D via $25 $27
+ 3 is a member of D via $25 $28
+ B is a member of D via $23 $29
+ 4 is a member of D via $29 $30
+ 5 is a member of D via $29 $31
+ A is a member of D via $29 $32
+ 1 is a member of D via $32 $33
+ 2 is a member of D via $32 $34
+ 3 is a member of D via $32 $35
+
+
+
+Adding a new user, 7, to group A.
+
+
+ Add the user to group A in the groups table.
+
+ Find all entries for group A in the cache table.
+
+ For each entry in that list:
+ Add "7 is a member of $entry->top via $entry->id"
+
+Deleting a user, 7, from group A:
+
+ Remove the user from group A in the groups table.
+ find all entries in the cache table where the principal id is user 7 and
+ the parent id is A. (requires a self join)
+ nuke them
+
+ Alternatively:
+ find all entries for A in the cache table.
+ For each one, find the child whose id is 7.
+ Nuke it
+
+
+Adding a group, B to group D.
+
+ Add group B as a member of D in the groups table.
+ In the cache table:
+ $id = Add group B as a member of D via ""
+
+ For each member of group B (4, 5, A):
+
+ $sid= 4 is a member of D via $id
+ $sid= 5 is a member of D via $id
+ $sid= A is a member of D via $id
+
+ if the member is a group itself, recurse down:
+
+ 1 is a member of D via $sid
+ 2 is a member of D via $sid
+ 3 is a member of D via $sid
+
+ Find all places where D is a member of $foo.
+ Repeat the above procedure, substituting $foo for D
+ and making $id D's id.
+
+Removing B as a member of D:
+
+ Remove B as a member of D in the groups table.
+ Find all references to D in the pseudogroups table.
+ Find all children of D which are B:
+ Recurse down with the following algorithm:
+ If it's a user, delete it.
+ If it's a group, recurse through each member,
+ deleting its children and then deleting the
+ group itself.
diff --git a/rt/docs/design_docs/rql_parser_machine.graphviz b/rt/docs/design_docs/rql_parser_machine.graphviz
new file mode 100644
index 0000000..36463ec
--- /dev/null
+++ b/rt/docs/design_docs/rql_parser_machine.graphviz
@@ -0,0 +1,32 @@
+
+/* GraphViz graph representing the state diagram of the RQL parser.
+*/
+
+digraph G {
+
+ PAREN -> PAREN;
+ PAREN -> KEYWORD;
+ PAREN -> AGGREG;
+
+ AGGREG -> KEYWORD;
+ AGGREG -> PAREN;
+
+ KEYWORD -> OP;
+
+ OP -> VALUE;
+
+ VALUE -> PAREN;
+ VALUE -> AGGREG;
+
+/*
+ Blue lines represent added complexity of q[IN (x,y,z)] support.
+ The only place that the "blue tree" can be entered is at IN, and
+ exited at PAREN.
+*/
+ KEYWORD -> IN [color=blue];
+ IN -> PAREN [color=blue];
+ PAREN -> VALUE [color=blue];
+ VALUE -> COMMA [color=blue];
+ COMMA -> VALUE [color=blue];
+ VALUE -> PAREN [color=blue];
+}
diff --git a/rt/docs/design_docs/rt-mvc b/rt/docs/design_docs/rt-mvc
new file mode 100644
index 0000000..3518b7d
--- /dev/null
+++ b/rt/docs/design_docs/rt-mvc
@@ -0,0 +1,32 @@
+Goals:
+
+
+ Never write an init block for a page that just views/edits pages
+ No style embedded in view/edit pages
+
+ Validation / Error display and re-editing.
+
+
+Implementation.
+
+
+ For a given object's fields:
+
+ print a label for the field
+ print the current values for the field
+ print an edit widget for create
+ print an edit widget for update
+
+
+
+ for a given form buttons for "perform the action" "don't perform the main action"
+
+
+Edit widgets
+
+
+ text input
+ hidden
+ fixed enum as { dropdown, select multiple, sleect single, radio}
+ checkbox fixed enum
+
diff --git a/rt/docs/design_docs/ruleset-workflow.txt b/rt/docs/design_docs/ruleset-workflow.txt
new file mode 100644
index 0000000..f19dbd7
--- /dev/null
+++ b/rt/docs/design_docs/ruleset-workflow.txt
@@ -0,0 +1,158 @@
+# For an online version, see http://wiki.bestpractical.com/?RulesetWorkflow
+
+_*This is a design document for a work in progress.
+It describes features that do not exist today and may never exist*_
+
+== Text Description
+
+* The user of PurchaseOrder queue fill in a numeric "CF", called "Price".
+* On creation, it needs to create following approvals:
+** "ManagerApproval" if CF.Price is > 1000
+** "PresidentApproval" if CF.Price is > 2000
+* When all of "M", "P" are resolved (or if there were none to begin with), Create "FinanceApproval".
+* If any approvals above is rejected, reject the original ticket.
+* If "FinanceApproval" is resolved, resolve original ticket.
+* If "FinanceApproval" is rejected, create an approval for "CEOApproval".
+* If "CEOApproval" is resolved, resolve the original ticket.
+* If "CEOApproval" is rejected, reject the original ticket.
+
+== ASCII Diagram
+
+ ,----------. ,---------------------->[DONE]
+ | \ / ^
+ [TOP]-+-?---->[M]---->[F] |
+ | | / \ |
+ `-?->[P]-+-' `-(!)->[C]-----------------'
+ | | |
+ | | `-(!)---------->[FAIL]
+ | | ^
+ `-(!)----------------------------------'
+
+== Objects
+
+Note that "Scrips" are now called "Rules".
+
+=== RuleAction "AquireMyLocks"
+
+ FOREACH $Scrip IN $TicketObj->Scrips
+ WHERE $Scrip.Action.Type == "TryCreateTicketWithRuleset"
+ DO LockRuleset $Scrip.Action.Argument
+
+=== RuleAction "TryCreateTicketWithRuleset"
+
+ DO ReleaseMyLockOnRuleset $Argument
+ UNLESS RulesetLocked $Argument
+ DO CreateTicketWithRuleset $Argument
+
+=== RuleAction "CreateTicketWithRuleset"
+
+ GIVEN $Ticket AS CreateTicket(@OtherArguments)
+ DO SetTicketRuleSet $Argument
+ DO RunTicketRuleSet $Argument
+
+=== GlobalRule "AquireLocks"
+
+* AppliesTo: All Objects
+* Condition: OnCreate
+* Action: AquireMyLocks
+
+=== Queue "PurchaseOrder"
+
+* Rule:
+** Condition: OnCreate
+** Action: SetTicketRuleSet "PurchaseFlow"
+** Action: RunTicketRuleSet "PurchaseFlow"
+
+=== RuleSet "PurchaseFlow"
+
+* Rule (implicitly run by AcquireMyLocks):
+** Condition: OnCreate
+** Action: LockRuleSet "ManagerApproval"
+** Action: LockRuleSet "PresidentApproval"
+** Action: LockRuleSet "FinanceApproval"
+
+* Rule:
+** Condition: OnCreate
+** Condition: CF.Price > 1000
+** Action: TryCreateTicketWithRuleset "ManagerApproval"
+
+* Rule:
+** Condition: OnCreate
+** Condition: CF.Price > 2000
+** Action: TryCreateTicketWithRuleset "PresidentApproval"
+
+* Rule:
+** Condition: OnCreate
+** Condition: "Finance" is not blocked
+** Action: TryCreateTicketWithRuleset "FinanceApproval"
+
+* Rule:
+** Condition: OnReject
+** Action: DeleteTree
+
+=== RuleSet: "ManagerApproval"
+
+* Rule (implicitly run by AcquireMyLocks):
+** Condition: OnCreate
+** Action: LockRuleSet "FinanceApproval"
+
+* Rule:
+** Condition: OnResolve
+** Action: TryCreateTicketWithRuleset "FinanceApproval"
+
+* Rule:
+** Condition: OnReject
+** Action: RejectTicket "PurchaseFlow"
+
+=== RuleSet: "PresidentApproval"
+
+* Rule (implicitly run by AcquireMyLocks):
+** Condition: OnCreate
+** Action: LockRuleSet "FinanceApproval"
+
+* Rule:
+** Condition: OnResolve
+** Action: TryCreateTicketWithRuleset "FinanceApproval"
+
+* Rule:
+** Condition: OnReject
+** Action: RejectTicket "PurchaseFlow"
+
+=== RuleSet: "FinanceApproval"
+
+* Rule:
+** Condition: OnResolve
+** Action: ResolveTicket "PurchaseFlow"
+
+* Rule:
+** Condition: OnReject
+** Action: ForceCreateTicketWithRuleset "CEOApproval"
+
+=== RuleSet: "CEOApproval"
+
+* Rule:
+** Condition: OnResolve
+** Action: ResolveTicket "PurchaseFlow"
+
+* Rule:
+** Condition: OnReject
+** Action: RejectTicket "PurchaseFlow"
+
+### FNORD FNORD FNORD FNORD FNORD FNORD FNORD FNORD FNORD ###
+
+== Another Text Description
+
+* I have a MonitoredQueue that sets tickets to "Monitored" if its subject matches /monitored/.
+* I want to have a kind of Ticket that are 'Monitored'.
+* I want all monitored tickets, when they are overdue for 14 days, to:
+** Send notification to manager
+** Mark as stalled
+* I want all monitored tickets, when they are overdue for 28 days, to:
+** Mark as rejected
+* I want to query all tickets that are monitored as such
+* I want to modify 14 => 15 and have it affect all existing tickets that are monitored
+* I want to add a new "overdue for 27 days, add a 'ultimatum' correspondence to it" rule
+** For all monitored tickets.
+* I want to add a new "overdue for 27 days, add a 'ultimatum' correspondence to it" rule
+** For all _new_ monitored tickets.
+** Without affecting existing ones.
diff --git a/rt/docs/design_docs/string-extraction-guide.txt b/rt/docs/design_docs/string-extraction-guide.txt
new file mode 100644
index 0000000..bd60a43
--- /dev/null
+++ b/rt/docs/design_docs/string-extraction-guide.txt
@@ -0,0 +1,100 @@
+# $File: //depot/RT/rt-devel/docs/design_docs/string-extraction-guide.txt $ $Author: ivan $
+# $Revision: 1.1 $ $Change: 1431 $ $DateTime: 2002/10/15 17:24:45 $
+
+Run 'p4 edit lib/RT/I18N/zh_tw.pm' and 'perl l10n.pl' to add new
+extractions to the zh_tw.pm.
+
+Edit lib/RT/I18N/zh_tw.pm for chinese counterparts.
+
+Attached is a copy of the freshly rewritten string extraction style guide.
+Please point out anything that's unclear or underspecified. I
+localized a number of the core modules in RT 2.1.3 (Starting with
+Queue_Overlay.pm). I only touched a couple of the web templates in the
+Elements/ directory of the web ui.
+
+RT String extraction styleguide:
+
+Web templates:
+
+Templates should use the /l filtering component to call the localisation
+framework
+
+The string Foo!
+
+Should become <&|/l&>Foo!</&>
+
+All newlines should be removed from localized strings, to make it easy to
+grep the codebase for strings to be localized
+
+The string Foo
+ Bar
+ Baz
+
+Should become <&|/l&>Foo Bar Baz</&>
+
+
+Variable subsititutions should be moved to Locale::MakeText format
+
+The string Hello, <%$name %>
+
+should become <&|/l, $name &>Hello, [_1]</&>
+
+
+Multiple variables work just like single variables
+
+The string You found <%$num%> tickets in queue <%$queue%>
+
+should become <&|/l, $num, $queue &>You found [_1] tickets in queue [_2]</&>
+
+When subcomponents are called in the middle of a phrase, they need to be escaped
+too:
+
+The string <input type="submit" value="New ticket in">&nbsp<& /Elements/SelectNewTicketQueue&>
+
+should become <&|/l, $m->scomp('/Elements/SelectNewTicketQueue')&><input type="submit" value="New ticket in">&nbsp;[_1]</&>
+
+
+
+There are places inside the web ui where strings are defined, which need to be
+localised. it is important to note here that each localized string is split out
+onto its own line, but never split across two lines and two localized strings
+are never included on the same line. It is also important to note
+that this will genereate code which will not work in RT 2.1.3. I need
+to add a bit of framework to make it work in 2.1.4
+
+
+The string <& /Elements/TitleBoxStart, width=> "40%", titleright => "RT $RT::VERSION for $RT::rtname", title => 'Login' &>
+
+should become <& /Elements/TitleBoxStart,
+ width=> "40%",
+ titleright => loc("RT [_1] for [_2]",$RT::VERSION, $RT::rtname),
+ title => loc('Login'),
+ &>
+
+
+
+
+
+
+Within RT's core code, every module has a localization handle available through the 'loc' method:
+
+The code return ( $id, "Queue created" );
+
+should become return ( $id, $self->loc("Queue created") );
+
+When returning or localizing a single string, the "extra" set of parenthesis () should be omitted.
+
+The code return ("Subject changed to ". $self->Data );
+
+should become return $self->loc( "Subject changed to [_1]", $self->Data );
+
+
+It is important not to localize the names of rights or statuses within RT's core, as there is logic that depends on them as string identifiers. The proper place to localize these values is when they're presented for display in the web or commandline interfaces.
+
+
+
+
+
+--
+http://www.bestpractical.com/products/rt -- Trouble Ticketing. Free.
+
diff --git a/rt/docs/design_docs/subscription-definitions.txt b/rt/docs/design_docs/subscription-definitions.txt
new file mode 100755
index 0000000..deda35c
--- /dev/null
+++ b/rt/docs/design_docs/subscription-definitions.txt
@@ -0,0 +1,113 @@
+NEW SCRIP NOTES
+
+
+RT Actions:
+
+
+ EmailOwnerAsComment
+ Send mail to the ticket owner from the queue's comment address
+
+ EmailOwnerOrAdminWatchersAsComment
+ Send mail to the ticket owner, or if there is no owner, the ticket's admin watchers
+ from the queue's comment addresses
+
+ EmailAdminWatchersAsComment
+ Send mail to the ticket's adminstrative watchers from the queue's comment address
+
+
+
+ EmailOwner
+ Send mail to the ticket owner from the queue's correspond address
+
+ EmailOwnerOrAdminWatchers
+ Send mail to the ticket owner, or if there is no owner, the ticket's admin watchers
+ from the queue's correspond addresses
+
+ EmailAdminWatchers
+ Send mail to the ticket's adminstrative watchers from the queue's correspond address
+
+ EmailWatchers
+ Send mail to the ticket watchers from the queue's correspond address
+
+ AutoReply
+ Sendmail to the requestor from the queue's correspond address.
+
+
+
+RT Conditions:
+ OnCreate
+ OnEachTransaction
+ OnComment
+ OnCorrespond
+
+
+
+
+
+What is an Action?
+
+...some piece of code that can do something whenever a transaction is done.
+The actions shipped with RT sends email and can handle some logic that makes
+sense for some instances. site-specific modules can be dropped in to
+perform special actions.
+
+
+What can an Action do?
+
+- decide whether it's applicable or not
+- prepare
+- commit
+- describe itself
+
+...and if it's a subclass of SendEmail, you can also override a lot.
+
+Currently the schema.mysql contains a list of the basic subscription-related
+actions that will be bundled with RT.
+
+
+What is a Scrip?
+
+...it's an entry in the database that tells that an action is to be
+performed with a certain template and argument. Template and argument
+doesn't make sense in all contexts. A scrip can be limited to transaction
+types; the current implementation allows a comma-separated list (though for
+a "cleaner" schema design, it should be a separate table for this?). It has
+a name and a description.
+
+
+What is a ScripScope?
+
+...an indication of what queues the different Scrips applies to. It should
+be easy to remove/insert ScripScope objects by the admin tools.
+
+
+What is a Watcher?
+
+...it's a request for beeing kept updated on a ticket and/or a queue
+and/or whatever. It is to be used by the Actions. Watcher items can
+easily be enabled/disabled through the `Quiet' attribute. `Type' might
+indicate what emails the watcher wants to get and how to get them.
+
+The Bcc/Cc watchers should be handled by the NotifyWatchers action which is
+run regardless of the Scrips.
+
+
+What is a Template?
+
+...A template is a text template that is to be used for outgoing email -
+or for different use for different actions. One template can be used by
+several Scrips.
+
+
+How does the system determinate whom to send mail to?
+
+The ScripScope table in the DB should indicate whether a Scrip is relevant
+for a queue or not /* TobiX thinks that this might eventually be extended to
+keywords, tickets, etc, and not only Queues */ ... the Scope table should
+indicate whether the Scrip is relevant for a given transaction type ... then
+the given Action should determinate whether it applies or not, and finally
+the Action has to find out (via the Watchers table) whom it applies to, and
+how to contact them ... and the Template tells how the mails that are sent
+out should look like.
+
+
diff --git a/rt/docs/design_docs/ticket_templates b/rt/docs/design_docs/ticket_templates
new file mode 100644
index 0000000..7850edf
--- /dev/null
+++ b/rt/docs/design_docs/ticket_templates
@@ -0,0 +1,16 @@
+===Create-Ticket: foo
+ Subject: APPROVE <%TOP-Subject%>
+ Status: status
+ Queue: <%TOP-Queue%>
+ Owner: <%TOP-Owner%>
+ Depends-on: <%TOP-Id%>
+ Child-of: <%TOP-Id%>
+ Refers-to: <%TOP-Id%>
+ Content-Type: text/plain
+ Content: This is content
+blah
+blah
+blah
+===Create-Ticket: bar
+Subject: <%foo-Subject%>
+
diff --git a/rt/docs/design_docs/users b/rt/docs/design_docs/users
new file mode 100644
index 0000000..71c4476
--- /dev/null
+++ b/rt/docs/design_docs/users
@@ -0,0 +1,14 @@
+RT2 makes everybody a user. some sites won't like this. there
+should be away to make an "anonymous" user who the mailgate makes
+the requestor for all mailed in tickets. it would then set the
+ticket 'requestor' watcher's alternate email address to the real
+requestor's email.
+
+additionally, eventually, users will need to be deleted. RT doesn't
+want any user deleted. Instead, there will be a flag in the user's
+entry in the users table called 'Disabled.' Disabled users will
+not be able to be granted rights.
+
+ The process of disabling a user should remove their acls and
+should force the giving away of their tickets or reject the disabling.
+
diff --git a/rt/docs/rt3-schema-relationships.dot b/rt/docs/rt3-schema-relationships.dot
new file mode 100644
index 0000000..e290f8b
--- /dev/null
+++ b/rt/docs/rt3-schema-relationships.dot
@@ -0,0 +1,89 @@
+digraph g {
+graph [
+rankdir = "RL",
+ concentrate = true,
+ratio = auto
+];
+node [
+fontsize = "18",
+shape = record, fontsize = 18
+];
+edge [
+];
+
+"Records" [shape = record, fontsize = 18, label = "(Any RT::Record)" ];
+"Records" -> "Principals" [label = "Creator -> id"];
+"ACL" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"ACL" -> "Principals" [label="PrincipalId -> id"];
+"ACL" -> "Principals" [label="DelegatedBy -> id"];
+"ACL" -> "ACL" [label="DelegatedFrom -> id"];
+
+"Attachments" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Attachments" -> "Transactions" [label="TransactionId -> id"];
+"Attachments" -> "Attachments" [label="Parent -> id"];
+
+"CachedGroupMemers" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"CachedGroupMemers" -> "Groups" [label="GroupId -> id", weight=2];
+"CachedGroupMemers" -> "Principals" [label="MemberId -> id"];
+"CachedGroupMemers" -> "CachedGroupMemers" [label="Via -> id"];
+"CachedGroupMemers" -> "Groups" [label="ImmediateParentId -> id"];
+
+"CustomFields" [shape = record, fontsize = 18, label = "<col0> \N " ];
+
+"CustomFieldValues" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"CustomFieldValues" -> "CustomFields" [label="CustomField -> id"];
+
+"GroupMembers" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"GroupMembers" -> "Groups" [label="GroupId -> id", weight=2];
+"GroupMembers" -> "Principals" [label="MemberId -> id", weight = 2];
+
+"Groups" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Groups" -> "Principals" [label="Groups.id -> id"];
+
+"Links" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Links" -> "Tickets" [label="LocalBase => id (usually)", style="dotted"];
+"Links" -> "Tickets" [label="LocalTarget => id (usually)", style="dotted"];
+
+"Principals" [shape = record, fontsize = 18, label = "<col0> \N " ];
+
+"Attributes" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Attributes" -> "Records" [label="ObjectId -> id"];
+
+"Queues" [shape = record, fontsize = 18, label = "<col0> \N " ];
+
+"ScripActions" [shape = record, fontsize = 18, label = "<col0> \N " ];
+
+"ScripConditions" [shape = record, fontsize = 18, label = "<col0> \N " ];
+
+"Scrips" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Scrips" -> "ScripConditions" [label="ScripCondition -> id"];
+"Scrips" -> "ScripActions" [label="ScripAction -> id"];
+"Scrips" -> "Templates" [label="Template -> id"];
+"Scrips" -> "Queues" [label="Queue -> id"];
+
+"Templates" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Templates" -> "Queues" [label ="Queue -> id" ];
+
+"ObjectCustomFields" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"ObjectCustomFields" -> "CustomFields" [label="CustomField -> id"];
+"ObjectCustomFields" -> "Records" [label="ObjectId -> id"];
+
+"ObjectCustomFieldValues" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"ObjectCustomFieldValues" -> "CustomFields" [label="CustomField -> id"];
+"ObjectCustomFieldValues" -> "Records" [label="ObjectId -> id"];
+
+"Tickets" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Tickets" -> "Tickets" [label="EffectiveId -> id"];
+"Tickets" -> "Principals" [label="Owner -> id"];
+"Queues" -> "Tickets" [style="invis"];
+"Tickets" -> "Queues" [label="Queue -> id"];
+
+"Transactions" [shape = record, fontsize = 18, label = "<col0> \N " ];
+"Transactions" -> "Records" [label="ObjectId -> id"];
+
+"Users" [shape = record, fontsize = 18, label = "<col0> \N " ];
+
+"Users" -> "Principals" [label="id -> id"];
+
+
+}
diff --git a/rt/etc/RT_Config.pm b/rt/etc/RT_Config.pm
new file mode 100644
index 0000000..7f7eadc
--- /dev/null
+++ b/rt/etc/RT_Config.pm
@@ -0,0 +1,587 @@
+#
+# WARNING: NEVER EDIT RT_Config.pm. Instead, copy any sections you want to change to RT_SiteConfig.pm
+# and edit them there.
+#
+
+package RT;
+
+=head1 NAME
+
+RT::Config
+
+=for testing
+
+use RT::Config;
+
+=cut
+
+# {{{ Base Configuration
+
+# $rtname is the string that RT will look for in mail messages to
+# figure out what ticket a new piece of mail belongs to
+
+# Your domain name is recommended, so as not to pollute the namespace.
+# once you start using a given tag, you should probably never change it.
+# (otherwise, mail for existing tickets won't get put in the right place
+
+Set($rtname , "example.com");
+
+
+# This regexp controls what subject tags RT recognizes as its own.
+# If you're not dealing with historical $rtname values, you'll likely
+# never have to enable this feature.
+#
+# Be VERY CAREFUL with it. Note that it overrides $rtname for subject
+# token matching and that you should use only "non-capturing" parenthesis
+# grouping. For example:
+#
+# Set($EmailSubjectTagRegex, qr/(?:example.com|example.org)/i );
+#
+# and NOT
+#
+# Set($EmailSubjectTagRegex, qr/(example.com|example.org)/i );
+#
+# This setting would make RT behave exactly as it does without the
+# setting enabled.
+#
+# Set($EmailSubjectTagRegex, qr/\Q$rtname\E/i );
+
+
+
+# You should set this to your organization's DNS domain. For example,
+# fsck.com or asylum.arkham.ma.us. It's used by the linking interface to
+# guarantee that ticket URIs are unique and easy to construct.
+
+Set($Organization , "example.com");
+
+# $user_passwd_min defines the minimum length for user passwords. Setting
+# it to 0 disables this check
+Set($MinimumPasswordLength , "5");
+
+# $Timezone is used to convert times entered by users into GMT and back again
+# It should be set to a timezone recognized by your local unix box.
+Set($Timezone , 'US/Eastern');
+
+# }}}
+
+# {{{ Database Configuration
+
+# Database driver beeing used. Case matters
+# Valid types are "mysql", "Oracle" and "Pg"
+
+Set($DatabaseType , 'Pg');
+
+# The domain name of your database server
+# If you're running mysql and it's on localhost,
+# leave it blank for enhanced performance
+Set($DatabaseHost , 'localhost');
+Set($DatabaseRTHost , 'localhost');
+
+# The port that your database server is running on. Ignored unless it's
+# a positive integer. It's usually safe to leave this blank
+Set($DatabasePort , '');
+
+#The name of the database user (inside the database)
+Set($DatabaseUser , 'freeside');
+
+# Password the DatabaseUser should use to access the database
+Set($DatabasePassword , '');
+
+# The name of the RT's database on your database server
+Set($DatabaseName , 'freeside');
+
+# If you're using Postgres and have compiled in SSL support,
+# set DatabaseRequireSSL to 1 to turn on SSL communication
+Set($DatabaseRequireSSL , undef);
+
+# }}}
+
+# {{{ Incoming mail gateway configuration
+
+# OwnerEmail is the address of a human who manages RT. RT will send
+# errors generated by the mail gateway to this address. This address
+# should _not_ be an address that's managed by your RT instance.
+
+Set($OwnerEmail , 'root');
+
+# If $LoopsToRTOwner is defined, RT will send mail that it believes
+# might be a loop to $RT::OwnerEmail
+
+Set($LoopsToRTOwner , 1);
+
+# If $StoreLoops is defined, RT will record messages that it believes
+# to be part of mail loops.
+# As it does this, it will try to be careful not to send mail to the
+# sender of these messages
+
+Set($StoreLoops , undef);
+
+# $MaxAttachmentSize sets the maximum size (in bytes) of attachments stored
+# in the database.
+
+# For mysql and oracle, we set this size at 10 megabytes.
+# If you're running a postgres version earlier than 7.1, you will need
+# to drop this to 8192. (8k)
+
+Set($MaxAttachmentSize , 10000000);
+
+# $TruncateLongAttachments: if this is set to a non-undef value,
+# RT will truncate attachments longer than MaxAttachmentSize.
+
+Set($TruncateLongAttachments , undef);
+
+# $DropLongAttachments: if this is set to a non-undef value,
+# RT will silently drop attachments longer than MaxAttachmentSize.
+
+Set($DropLongAttachments , undef);
+
+# If $ParseNewMessageForTicketCcs is true, RT will attempt to divine
+# Ticket 'Cc' watchers from the To and Cc lines of incoming messages
+# Be forewarned that if you have _any_ addresses which forward mail to
+# RT automatically and you enable this option without modifying
+# "RTAddressRegexp" below, you will get yourself into a heap of trouble.
+
+Set($ParseNewMessageForTicketCcs , undef);
+
+# RTAddressRegexp is used to make sure RT doesn't add itself as a ticket CC if
+# the setting above is enabled.
+
+Set($RTAddressRegexp , '^rt\@example.com$');
+
+# RT provides functionality which allows the system to rewrite
+# incoming email addresses. In its simplest form,
+# you can substitute the value in CanonicalizeEmailAddressReplace
+# for the value in CanonicalizeEmailAddressMatch
+# (These values are passed to the CanonicalizeEmailAddress subroutine in RT/User.pm)
+# By default, that routine performs a s/$Match/$Replace/gi on any address passed to it
+
+#Set($CanonicalizeEmailAddressMatch , '@subdomain\.example\.com$');
+#Set($CanonicalizeEmailAddressReplace , '@example.com');
+
+# set this to true and the create new user page will use the values that you
+# enter in the form but use the function CanonicalizeUserInfo in User_Local.pm
+Set($CanonicalizeOnCreate , 0);
+
+# If $SenderMustExistInExternalDatabase is true, RT will refuse to
+# create non-privileged accounts for unknown users if you are using
+# the "LookupSenderInExternalDatabase" option.
+# Instead, an error message will be mailed and RT will forward the
+# message to $RTOwner.
+#
+# If you are not using $LookupSenderInExternalDatabase, this option
+# has no effect.
+#
+# If you define an AutoRejectRequest template, RT will use this
+# template for the rejection message.
+
+Set($SenderMustExistInExternalDatabase , undef);
+
+# }}}
+
+# {{{ Outgoing mail configuration
+
+# RT is designed such that any mail which already has a ticket-id associated
+# with it will get to the right place automatically.
+
+# $CorrespondAddress and $CommentAddress are the default addresses
+# that will be listed in From: and Reply-To: headers of correspondence
+# and comment mail tracked by RT, unless overridden by a queue-specific
+# address.
+
+Set($CorrespondAddress , 'RT_CorrespondAddressNotSet');
+
+Set($CommentAddress , 'RT_CommentAddressNotSet');
+
+#Sendmail Configuration
+
+# $MailCommand defines which method RT will use to try to send mail
+# We know that 'sendmailpipe' works fairly well.
+# If 'sendmailpipe' doesn't work well for you, try 'sendmail'
+#
+# Note that you should remove the '-t' from $SendmailArguments
+# if you use 'sendmail' rather than 'sendmailpipe'
+
+Set($MailCommand , 'sendmailpipe');
+
+# $SendmailArguments defines what flags to pass to $Sendmail
+# assuming you picked 'sendmail' or 'sendmailpipe' as the $MailCommand above.
+# If you picked 'sendmailpipe', you MUST add a -t flag to $SendmailArguments
+
+# These options are good for most sendmail wrappers and workalikes
+Set($SendmailArguments , "-oi -t");
+
+# $SendmailBounceArguments defines what flags to pass to $Sendmail
+# assuming RT needs to send an error (ie. bounce).
+
+Set($SendmailBounceArguments , '-f "<>"');
+
+# These arguments are good for sendmail brand sendmail 8 and newer
+#Set($SendmailArguments,"-oi -t -ODeliveryMode=b -OErrorMode=m");
+
+# If you selected 'sendmailpipe' above, you MUST specify the path
+# to your sendmail binary in $SendmailPath.
+# !! If you did not # select 'sendmailpipe' above, this has no effect!!
+Set($SendmailPath , "/usr/sbin/sendmail");
+
+# By default, RT sets the outgoing mail's "From:" header to
+# "SenderName via RT". Setting this option to 0 disables it.
+
+Set($UseFriendlyFromLine , 1);
+
+# sprintf() format of the friendly 'From:' header; its arguments
+# are SenderName and SenderEmailAddress.
+Set($FriendlyFromLineFormat , "\"%s via RT\" <%s>");
+
+# RT can optionally set a "Friendly" 'To:' header when sending messages to
+# Ccs or AdminCcs (rather than having a blank 'To:' header.
+
+# This feature DOES NOT WORK WITH SENDMAIL[tm] BRAND SENDMAIL
+# If you are using sendmail, rather than postfix, qmail, exim or some other MTA,
+# you _must_ disable this option.
+
+Set($UseFriendlyToLine , 0);
+
+# sprintf() format of the friendly 'From:' header; its arguments
+# are WatcherType and TicketId.
+Set($FriendlyToLineFormat, "\"%s of $RT::rtname Ticket #%s\":;");
+
+# By default, RT doesn't notify the person who performs an update, as they
+# already know what they've done. If you'd like to change this behaviour,
+# Set $NotifyActor to 1
+
+Set($NotifyActor, 0);
+
+# By default, RT records each message it sends out to its own internal database.# To change this behaviour, set $RecordOutgoingEmail to 0
+
+Set($RecordOutgoingEmail, 1);
+
+# VERP support (http://cr.yp.to/proto/verp.txt)
+# uncomment the following two directives to generate envelope senders
+# of the form ${VERPPrefix}${originaladdress}@${VERPDomain}
+# (i.e. rt-jesse=fsck.com@rt.example.com ) This currently only works
+# with sendmail and sendmailppie.
+# Set($VERPPrefix, 'rt-');
+# Set($VERPDomain, $RT::Organization);
+
+# }}}
+
+# {{{ Logging
+
+# Logging. The default is to log anything except debugging
+# information to syslog. Check the Log::Dispatch POD for
+# information about how to get things by syslog, mail or anything
+# else, get debugging info in the log, etc.
+
+# It might generally make
+# sense to send error and higher by email to some administrator.
+# If you do this, be careful that this email isn't sent to this RT instance.
+
+# the minimum level error that will be logged to the specific device.
+# levels from lowest to highest:
+# debug info notice warning error critical alert emergency
+
+# Mail loops will generate a critical log message.
+Set($LogToSyslog , 'debug');
+Set($LogToScreen , 'error');
+Set($LogToFile , undef);
+Set($LogDir, '/opt/rt3/var/log');
+Set($LogToFileNamed , "rt.log"); #log to rt.log
+
+# If true generates stack traces to file log or screen
+# never generates traces to syslog
+
+Set($LogStackTraces , 0);
+
+# On Solaris or UnixWare, set to ( socket => 'inet' ). Options here
+# override any other options RT passes to Log::Dispatch::Syslog.
+# Other interesting flags include facility and logopt. (See the
+# Log::Dispatch::Syslog documentation for more information.) (Maybe
+# ident too, if you have multiple RT installations.)
+
+@LogToSyslogConf = () unless (@LogToSyslogConf);
+
+# RT has rudimentary SQL statement logging support if you have
+# DBIx-SearchBuilder 1.31_1 or higher; simply set $StatementLog to be
+# the level that you wish SQL statements to be logged at.
+Set($StatementLog, undef);
+
+# }}}
+
+# {{{ Web interface configuration
+
+# This determines the default stylesheet the RT web interface will use.
+# RT ships with two valid values by default:
+#
+# 3.5-default The totally new, default layout for RT 3.5
+# 3.4-compat A 3.4 compatibility stylesheet to make RT 3.5 look
+# (mostly) like 3.4
+#
+# This value actually specifies a directory in share/html/NoAuth/css/
+# from which RT will try to load the file main.css (which should
+# @import any other files the stylesheet needs). This allows you to
+# easily and cleanly create your own stylesheets to apply to RT.
+
+Set($WebDefaultStylesheet, '3.5-default');
+
+# Define the directory name to be used for images in rt web
+# documents.
+
+# If you're putting the web ui somewhere other than at the root of
+# your server, you should set $WebPath to the path you'll be
+# serving RT at.
+# $WebPath requires a leading / but no trailing /.
+#
+# In most cases, you should leave $WebPath set to '' (an empty value).
+
+Set($WebPath , "");
+
+# If we're running as a superuser, run on port 80
+# Otherwise, pick a high port for this user.
+
+Set($WebPort , 80);# + ($< * 7274) % 32766 + ($< && 1024));
+
+# This is the Scheme, server and port for constructing urls to webrt
+# $WebBaseURL doesn't need a trailing /
+
+Set($WebBaseURL , "http://localhost:$WebPort");
+
+Set($WebURL , $WebBaseURL . $WebPath . "/");
+
+# $WebImagesURL points to the base URL where RT can find its images.
+
+Set($WebImagesURL , $WebPath . "/NoAuth/images/");
+
+# $LogoURL points to the URL of the RT logo displayed in the web UI
+
+Set($LogoURL , $WebImagesURL . "bplogo.gif");
+
+# WebNoAuthRegex - What portion of RT's URLspace should not require
+# authentication.
+Set($WebNoAuthRegex, qr!^/rt(?:/+NoAuth/|
+ /+REST/\d+\.\d+/NoAuth/)!x );
+
+# For message boxes, set the entry box width and what type of wrapping
+# to use.
+#
+# Default width: 72
+Set($MessageBoxWidth , 72);
+
+# Default wrapping: "HARD" (choices "SOFT", "HARD")
+Set($MessageBoxWrap, "HARD");
+
+# Support implicit links in WikiText custom fields? A true value
+# causes InterCapped or ALLCAPS words in WikiText fields to
+# automatically become links to searches for those words. If used on
+# RTFM articles, it links to the RTFM article with that name.
+Set($WikiImplicitLinks, 0);
+
+# if TrustHTMLAttachments is not defined, we will display them
+# as text. This prevents malicious HTML and javascript from being
+# sent in a request (although there is probably more to it than that)
+Set($TrustHTMLAttachments , undef);
+
+# Should RT redistribute correspondence that it identifies as
+# machine generated? A true value will do so; setting this to '0'
+# will cause no such messages to be redistributed.
+# You can also use 'privileged' (the default), which will redistribute
+# only to privileged users. This helps to protect against malformed
+# bounces and loops caused by autocreated requestors with bogus addresses.
+Set($RedistributeAutoGeneratedMessages, 'privileged');
+
+# If PreferRichText is set to a true value, RT will show HTML/Rich text
+# messages in preference to their plaintext alternatives. RT "scrubs" the
+# html to show only a minimal subset of HTML to avoid possible contamination
+# by cross-site-scripting attacks.
+Set($PreferRichText, undef);
+
+# If $WebExternalAuth is defined, RT will defer to the environment's
+# REMOTE_USER variable.
+
+Set($WebExternalAuth , undef);
+
+# If $WebFallbackToInternalAuth is undefined, the user is allowed a chance
+# of fallback to the login screen, even if REMOTE_USER failed.
+
+Set($WebFallbackToInternalAuth , undef);
+
+# $WebExternalGecos means to match 'gecos' field as the user identity);
+# useful with mod_auth_pwcheck and IIS Integrated Windows logon.
+
+Set($WebExternalGecos , undef);
+
+# $WebExternalAuto will create users under the same name as REMOTE_USER
+# upon login, if it's missing in the Users table.
+
+Set($WebExternalAuto , undef);
+
+# $WebSessionClass is the class you wish to use for managing Sessions.
+# It defaults to use your SQL database, but if you are using MySQL 3.x and
+# plans to use non-ascii Queue names, uncomment and add this line to
+# RT_SiteConfig.pm will prevent session corruption.
+
+# Set($WebSessionClass , 'Apache::Session::File');
+
+
+# By default, RT's session cookie isn't marked as "secure" Some web browsers
+# will treat secure cookies more carefully than non-secure ones, being careful
+# not to write them to disk, only send them over an SSL secured connection
+# and so on. To enable this behaviour, set # $WebSecureCookies to a true value.
+# NOTE: You probably don't want to turn this on _unless_ users are only connecting
+# via SSL encrypted HTTP connections.
+
+Set($WebSecureCookies, 0);
+
+
+# By default, RT clears its database cache after every page view.
+# This ensures that you've always got the most current information
+# when working in a multi-process (mod_perl or FastCGI) Environment
+# Setting $WebFlushDbCacheEveryRequest to '0' will turn this off,
+# which will speed RT up a bit, at the expense of a tiny bit of data
+# accuracy.
+
+Set($WebFlushDbCacheEveryRequest, '1');
+
+
+# $MaxInlineBody is the maximum attachment size that we want to see
+# inline when viewing a transaction. 13456 is a random sane-sounding
+# default.
+
+Set($MaxInlineBody, 13456);
+
+# $DefaultSummaryRows is default number of rows displayed in for search
+# results on the frontpage.
+
+Set($DefaultSummaryRows, 10);
+
+# By default, RT shows newest transactions at the bottom of the ticket
+# history page, if you want see them at the top set this to '0'.
+
+Set($OldestTransactionsFirst, '1');
+
+# By default, RT shows images attached to incoming (and outgoing) ticket updates
+# inline. Set this variable to 0 if you'd like to disable that behaviour
+
+Set($ShowTransactionImages, 1);
+
+
+# $HomepageComponents is an arrayref of allowed components on a user's
+# customized homepage ("RT at a glance").
+
+Set($HomepageComponents, [qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage)]);
+
+# @MasonParameters is the list of parameters for the constructor of
+# HTML::Mason's Apache or CGI Handler. This is normally only useful
+# for debugging, eg. profiling individual components with:
+# use MasonX::Profiler; # available on CPAN
+# @MasonParameters = (preamble => 'my $p = MasonX::Profiler->new($m, $r);');
+
+@MasonParameters = () unless (@MasonParameters);
+
+# $DefaultSearchResultFormat is the default format for RT search results
+Set ($DefaultSearchResultFormat, qq{
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
+ Status,
+ QueueName,
+ OwnerName,
+ Priority,
+ '__NEWLINE__',
+ '',
+ '<small>__Requestors__</small>',
+ '<small>__CreatedRelative__</small>',
+ '<small>__ToldRelative__</small>',
+ '<small>__LastUpdatedRelative__</small>',
+ '<small>__TimeLeft__</small>'});
+
+# If $SuppressInlineTextFiles is set to a true value, then uploaded
+# text files (text-type attachments with file names) are prevented
+# from being displayed in-line when viewing a ticket's history.
+
+Set($SuppressInlineTextFiles, undef);
+
+# If $DontSearchFileAttachments is set to a true value, then uploaded
+# files (attachments with file names) are not searched during full-content
+# ticket searches.
+
+Set($DontSearchFileAttachments, undef);
+
+
+# }}}
+
+# {{{ RT UTF-8 Settings
+
+# An array that contains languages supported by RT's internationalization
+# interface. Defaults to all *.po lexicons; setting it to qw(en ja) will make
+# RT bilingual instead of multilingual, but will save some memory.
+
+@LexiconLanguages = qw(*) unless (@LexiconLanguages);
+
+# An array that contains default encodings used to guess which charset
+# an attachment uses if not specified. Must be recognized by
+# Encode::Guess.
+
+@EmailInputEncodings = qw(utf-8 iso-8859-1 us-ascii) unless (@EmailInputEncodings);
+
+# The charset for localized email. Must be recognized by Encode.
+
+Set($EmailOutputEncoding , 'utf-8');
+
+# }}}
+
+# {{{ RT Date Handling Options (for Time::ParseDate)
+
+# Set this to 1 if your local date convention looks like "dd/mm/yy"
+# instead of "mm/dd/yy".
+
+Set($DateDayBeforeMonth , 1);
+
+# Should "Tuesday" default to meaning "Next Tuesday" or "Last Tuesday"?
+# Set to 0 for "Next" or 1 for "Last".
+
+Set($AmbiguousDayInPast , 1);
+
+# }}}
+
+# {{{ Miscellaneous RT Settings
+
+# You can define new statuses and even reorder existing statuses here.
+# WARNING. DO NOT DELETE ANY OF THE DEFAULT STATUSES. If you do, RT
+# will break horribly. The statuses you add must be no longer than
+# 10 characters.
+
+@ActiveStatus = qw(new open stalled) unless @ActiveStatus;
+@InactiveStatus = qw(resolved rejected deleted) unless @InactiveStatus;
+
+# Backward compatability setting. Add/Delete Link used to record one
+# transaction and run one scrip. Set this value to 1 if you want
+# only one of the link transactions to have scrips run.
+Set($LinkTransactionsRun1Scrip , 0);
+
+# When this feature is enabled an user need ModifyTicket right on both
+# tickets to link them together, otherwise he can have right on any of
+# two.
+Set($StrictLinkACL, 1);
+
+# }}}
+
+
+# {{{ Development Mode
+#
+# RT comes with a "Development mode" setting.
+# This setting, as a convenience for developers, turns on
+# all sorts of development options that you most likely don't want in
+# production:
+#
+# * Turns off Mason's 'static_source' directive. By default, you can't
+# edit RT's web ui components on the fly and have RT magically pick up
+# your changes. (It's a big performance hit)
+#
+# * More to come
+#
+
+Set($DevelMode, '0');
+
+# }}}
+
+
+1;
diff --git a/rt/etc/RT_Config.pm.in b/rt/etc/RT_Config.pm.in
new file mode 100644
index 0000000..cf089fb
--- /dev/null
+++ b/rt/etc/RT_Config.pm.in
@@ -0,0 +1,594 @@
+#
+# WARNING: NEVER EDIT RT_Config.pm. Instead, copy any sections you want to change to RT_SiteConfig.pm
+# and edit them there.
+#
+
+package RT;
+
+=head1 NAME
+
+RT::Config
+
+=for testing
+
+use RT::Config;
+
+=cut
+
+# {{{ Base Configuration
+
+# $rtname is the string that RT will look for in mail messages to
+# figure out what ticket a new piece of mail belongs to
+
+# Your domain name is recommended, so as not to pollute the namespace.
+# once you start using a given tag, you should probably never change it.
+# (otherwise, mail for existing tickets won't get put in the right place
+
+Set($rtname , "example.com");
+
+
+# This regexp controls what subject tags RT recognizes as its own.
+# If you're not dealing with historical $rtname values, you'll likely
+# never have to enable this feature.
+#
+# Be VERY CAREFUL with it. Note that it overrides $rtname for subject
+# token matching and that you should use only "non-capturing" parenthesis
+# grouping. For example:
+#
+# Set($EmailSubjectTagRegex, qr/(?:example.com|example.org)/i );
+#
+# and NOT
+#
+# Set($EmailSubjectTagRegex, qr/(example.com|example.org)/i );
+#
+# This setting would make RT behave exactly as it does without the
+# setting enabled.
+#
+# Set($EmailSubjectTagRegex, qr/\Q$rtname\E/i );
+
+
+
+# You should set this to your organization's DNS domain. For example,
+# fsck.com or asylum.arkham.ma.us. It's used by the linking interface to
+# guarantee that ticket URIs are unique and easy to construct.
+
+Set($Organization , "example.com");
+
+# $user_passwd_min defines the minimum length for user passwords. Setting
+# it to 0 disables this check
+Set($MinimumPasswordLength , "5");
+
+# $Timezone is used to convert times entered by users into GMT and back again
+# It should be set to a timezone recognized by your local unix box.
+Set($Timezone , 'US/Eastern');
+
+# }}}
+
+# {{{ Database Configuration
+
+# Database driver beeing used. Case matters
+# Valid types are "mysql", "Oracle" and "Pg"
+
+Set($DatabaseType , '@DB_TYPE@');
+
+# The domain name of your database server
+# If you're running mysql and it's on localhost,
+# leave it blank for enhanced performance
+Set($DatabaseHost , '@DB_HOST@');
+Set($DatabaseRTHost , '@DB_RT_HOST@');
+
+# The port that your database server is running on. Ignored unless it's
+# a positive integer. It's usually safe to leave this blank
+Set($DatabasePort , '@DB_PORT@');
+
+#The name of the database user (inside the database)
+Set($DatabaseUser , '@DB_RT_USER@');
+
+# Password the DatabaseUser should use to access the database
+Set($DatabasePassword , '@DB_RT_PASS@');
+
+# The name of the RT's database on your database server
+Set($DatabaseName , '@DB_DATABASE@');
+
+# If you're using Postgres and have compiled in SSL support,
+# set DatabaseRequireSSL to 1 to turn on SSL communication
+Set($DatabaseRequireSSL , undef);
+
+# }}}
+
+# {{{ Incoming mail gateway configuration
+
+# OwnerEmail is the address of a human who manages RT. RT will send
+# errors generated by the mail gateway to this address. This address
+# should _not_ be an address that's managed by your RT instance.
+
+Set($OwnerEmail , 'root');
+
+# If $LoopsToRTOwner is defined, RT will send mail that it believes
+# might be a loop to $RT::OwnerEmail
+
+Set($LoopsToRTOwner , 1);
+
+# If $StoreLoops is defined, RT will record messages that it believes
+# to be part of mail loops.
+# As it does this, it will try to be careful not to send mail to the
+# sender of these messages
+
+Set($StoreLoops , undef);
+
+# $MaxAttachmentSize sets the maximum size (in bytes) of attachments stored
+# in the database.
+
+# For mysql and oracle, we set this size at 10 megabytes.
+# If you're running a postgres version earlier than 7.1, you will need
+# to drop this to 8192. (8k)
+
+Set($MaxAttachmentSize , 10000000);
+
+# $TruncateLongAttachments: if this is set to a non-undef value,
+# RT will truncate attachments longer than MaxAttachmentSize.
+
+Set($TruncateLongAttachments , undef);
+
+# $DropLongAttachments: if this is set to a non-undef value,
+# RT will silently drop attachments longer than MaxAttachmentSize.
+
+Set($DropLongAttachments , undef);
+
+# If $ParseNewMessageForTicketCcs is true, RT will attempt to divine
+# Ticket 'Cc' watchers from the To and Cc lines of incoming messages
+# Be forewarned that if you have _any_ addresses which forward mail to
+# RT automatically and you enable this option without modifying
+# "RTAddressRegexp" below, you will get yourself into a heap of trouble.
+
+Set($ParseNewMessageForTicketCcs , undef);
+
+# RTAddressRegexp is used to make sure RT doesn't add itself as a ticket CC if
+# the setting above is enabled.
+
+Set($RTAddressRegexp , '^rt\@example.com$');
+
+# RT provides functionality which allows the system to rewrite
+# incoming email addresses. In its simplest form,
+# you can substitute the value in CanonicalizeEmailAddressReplace
+# for the value in CanonicalizeEmailAddressMatch
+# (These values are passed to the CanonicalizeEmailAddress subroutine in RT/User.pm)
+# By default, that routine performs a s/$Match/$Replace/gi on any address passed to it
+
+#Set($CanonicalizeEmailAddressMatch , '@subdomain\.example\.com$');
+#Set($CanonicalizeEmailAddressReplace , '@example.com');
+
+# set this to true and the create new user page will use the values that you
+# enter in the form but use the function CanonicalizeUserInfo in User_Local.pm
+Set($CanonicalizeOnCreate , 0);
+
+# If $SenderMustExistInExternalDatabase is true, RT will refuse to
+# create non-privileged accounts for unknown users if you are using
+# the "LookupSenderInExternalDatabase" option.
+# Instead, an error message will be mailed and RT will forward the
+# message to $RTOwner.
+#
+# If you are not using $LookupSenderInExternalDatabase, this option
+# has no effect.
+#
+# If you define an AutoRejectRequest template, RT will use this
+# template for the rejection message.
+
+Set($SenderMustExistInExternalDatabase , undef);
+
+# }}}
+
+# {{{ Outgoing mail configuration
+
+# RT is designed such that any mail which already has a ticket-id associated
+# with it will get to the right place automatically.
+
+# $CorrespondAddress and $CommentAddress are the default addresses
+# that will be listed in From: and Reply-To: headers of correspondence
+# and comment mail tracked by RT, unless overridden by a queue-specific
+# address.
+
+Set($CorrespondAddress , 'RT_CorrespondAddressNotSet');
+
+Set($CommentAddress , 'RT_CommentAddressNotSet');
+
+#Sendmail Configuration
+
+# $MailCommand defines which method RT will use to try to send mail
+# We know that 'sendmailpipe' works fairly well.
+# If 'sendmailpipe' doesn't work well for you, try 'sendmail'
+#
+# Note that you should remove the '-t' from $SendmailArguments
+# if you use 'sendmail' rather than 'sendmailpipe'
+
+Set($MailCommand , 'sendmailpipe');
+
+# $SendmailArguments defines what flags to pass to $Sendmail
+# assuming you picked 'sendmail' or 'sendmailpipe' as the $MailCommand above.
+# If you picked 'sendmailpipe', you MUST add a -t flag to $SendmailArguments
+
+# These options are good for most sendmail wrappers and workalikes
+Set($SendmailArguments , "-oi -t");
+
+# $SendmailBounceArguments defines what flags to pass to $Sendmail
+# assuming RT needs to send an error (ie. bounce).
+
+Set($SendmailBounceArguments , '-f "<>"');
+
+# These arguments are good for sendmail brand sendmail 8 and newer
+#Set($SendmailArguments,"-oi -t -ODeliveryMode=b -OErrorMode=m");
+
+# If you selected 'sendmailpipe' above, you MUST specify the path
+# to your sendmail binary in $SendmailPath.
+# !! If you did not # select 'sendmailpipe' above, this has no effect!!
+Set($SendmailPath , "/usr/sbin/sendmail");
+
+# By default, RT sets the outgoing mail's "From:" header to
+# "SenderName via RT". Setting this option to 0 disables it.
+
+Set($UseFriendlyFromLine , 1);
+
+# sprintf() format of the friendly 'From:' header; its arguments
+# are SenderName and SenderEmailAddress.
+Set($FriendlyFromLineFormat , "\"%s via RT\" <%s>");
+
+# RT can optionally set a "Friendly" 'To:' header when sending messages to
+# Ccs or AdminCcs (rather than having a blank 'To:' header.
+
+# This feature DOES NOT WORK WITH SENDMAIL[tm] BRAND SENDMAIL
+# If you are using sendmail, rather than postfix, qmail, exim or some other MTA,
+# you _must_ disable this option.
+
+Set($UseFriendlyToLine , 0);
+
+# sprintf() format of the friendly 'From:' header; its arguments
+# are WatcherType and TicketId.
+Set($FriendlyToLineFormat, "\"%s of $RT::rtname Ticket #%s\":;");
+
+# By default, RT doesn't notify the person who performs an update, as they
+# already know what they've done. If you'd like to change this behaviour,
+# Set $NotifyActor to 1
+
+Set($NotifyActor, 0);
+
+# By default, RT records each message it sends out to its own internal database.# To change this behaviour, set $RecordOutgoingEmail to 0
+
+Set($RecordOutgoingEmail, 1);
+
+# VERP support (http://cr.yp.to/proto/verp.txt)
+# uncomment the following two directives to generate envelope senders
+# of the form ${VERPPrefix}${originaladdress}@${VERPDomain}
+# (i.e. rt-jesse=fsck.com@rt.example.com ) This currently only works
+# with sendmail and sendmailppie.
+# Set($VERPPrefix, 'rt-');
+# Set($VERPDomain, $RT::Organization);
+
+# }}}
+
+# {{{ Logging
+
+# Logging. The default is to log anything except debugging
+# information to syslog. Check the Log::Dispatch POD for
+# information about how to get things by syslog, mail or anything
+# else, get debugging info in the log, etc.
+
+# It might generally make
+# sense to send error and higher by email to some administrator.
+# If you do this, be careful that this email isn't sent to this RT instance.
+
+# the minimum level error that will be logged to the specific device.
+# levels from lowest to highest:
+# debug info notice warning error critical alert emergency
+
+# Mail loops will generate a critical log message.
+Set($LogToSyslog , 'debug');
+Set($LogToScreen , 'error');
+Set($LogToFile , undef);
+Set($LogDir, '@RT_LOG_PATH@');
+Set($LogToFileNamed , "rt.log"); #log to rt.log
+
+# If true generates stack traces to file log or screen
+# never generates traces to syslog
+
+Set($LogStackTraces , 0);
+
+# On Solaris or UnixWare, set to ( socket => 'inet' ). Options here
+# override any other options RT passes to Log::Dispatch::Syslog.
+# Other interesting flags include facility and logopt. (See the
+# Log::Dispatch::Syslog documentation for more information.) (Maybe
+# ident too, if you have multiple RT installations.)
+
+@LogToSyslogConf = () unless (@LogToSyslogConf);
+
+# RT has rudimentary SQL statement logging support if you have
+# DBIx-SearchBuilder 1.31_1 or higher; simply set $StatementLog to be
+# the level that you wish SQL statements to be logged at.
+Set($StatementLog, undef);
+
+# }}}
+
+# {{{ Web interface configuration
+
+# This determines the default stylesheet the RT web interface will use.
+# RT ships with two valid values by default:
+#
+# 3.5-default The totally new, default layout for RT 3.5
+# 3.4-compat A 3.4 compatibility stylesheet to make RT 3.5 look
+# (mostly) like 3.4
+#
+# This value actually specifies a directory in share/html/NoAuth/css/
+# from which RT will try to load the file main.css (which should
+# @import any other files the stylesheet needs). This allows you to
+# easily and cleanly create your own stylesheets to apply to RT.
+
+Set($WebDefaultStylesheet, '3.5-default');
+
+# Define the directory name to be used for images in rt web
+# documents.
+
+# If you're putting the web ui somewhere other than at the root of
+# your server, you should set $WebPath to the path you'll be
+# serving RT at.
+# $WebPath requires a leading / but no trailing /.
+#
+# In most cases, you should leave $WebPath set to '' (an empty value).
+
+Set($WebPath , "");
+
+# If we're running as a superuser, run on port 80
+# Otherwise, pick a high port for this user.
+
+Set($WebPort , 80);# + ($< * 7274) % 32766 + ($< && 1024));
+
+# This is the Scheme, server and port for constructing urls to webrt
+# $WebBaseURL doesn't need a trailing /
+
+Set($WebBaseURL , "http://localhost:$WebPort");
+
+Set($WebURL , $WebBaseURL . $WebPath . "/");
+
+# $WebImagesURL points to the base URL where RT can find its images.
+
+Set($WebImagesURL , $WebPath . "/NoAuth/images/");
+
+# $LogoURL points to the URL of the RT logo displayed in the web UI
+
+Set($LogoURL , $WebImagesURL . "bplogo.gif");
+
+# WebNoAuthRegex - What portion of RT's URLspace should not require
+# authentication.
+Set($WebNoAuthRegex, qr!^/rt(?:/+NoAuth/|
+ /+REST/\d+\.\d+/NoAuth/)!x );
+
+# For message boxes, set the entry box width and what type of wrapping
+# to use.
+#
+# Default width: 72
+Set($MessageBoxWidth , 72);
+
+# Default wrapping: "HARD" (choices "SOFT", "HARD")
+Set($MessageBoxWrap, "HARD");
+
+# Support implicit links in WikiText custom fields? A true value
+# causes InterCapped or ALLCAPS words in WikiText fields to
+# automatically become links to searches for those words. If used on
+# RTFM articles, it links to the RTFM article with that name.
+Set($WikiImplicitLinks, 0);
+
+# if TrustHTMLAttachments is not defined, we will display them
+# as text. This prevents malicious HTML and javascript from being
+# sent in a request (although there is probably more to it than that)
+Set($TrustHTMLAttachments , undef);
+
+# Should RT redistribute correspondence that it identifies as
+# machine generated? A true value will do so; setting this to '0'
+# will cause no such messages to be redistributed.
+# You can also use 'privileged' (the default), which will redistribute
+# only to privileged users. This helps to protect against malformed
+# bounces and loops caused by autocreated requestors with bogus addresses.
+Set($RedistributeAutoGeneratedMessages, 'privileged');
+
+# If PreferRichText is set to a true value, RT will show HTML/Rich text
+# messages in preference to their plaintext alternatives. RT "scrubs" the
+# html to show only a minimal subset of HTML to avoid possible contamination
+# by cross-site-scripting attacks.
+Set($PreferRichText, undef);
+
+# If $WebExternalAuth is defined, RT will defer to the environment's
+# REMOTE_USER variable.
+
+Set($WebExternalAuth , undef);
+
+# If $WebFallbackToInternalAuth is undefined, the user is allowed a chance
+# of fallback to the login screen, even if REMOTE_USER failed.
+
+Set($WebFallbackToInternalAuth , undef);
+
+# $WebExternalGecos means to match 'gecos' field as the user identity);
+# useful with mod_auth_pwcheck and IIS Integrated Windows logon.
+
+Set($WebExternalGecos , undef);
+
+# $WebExternalAuto will create users under the same name as REMOTE_USER
+# upon login, if it's missing in the Users table.
+
+Set($WebExternalAuto , undef);
+
+# $WebSessionClass is the class you wish to use for managing Sessions.
+# It defaults to use your SQL database, but if you are using MySQL 3.x and
+# plans to use non-ascii Queue names, uncomment and add this line to
+# RT_SiteConfig.pm will prevent session corruption.
+
+# Set($WebSessionClass , 'Apache::Session::File');
+
+
+# By default, RT's session cookie isn't marked as "secure" Some web browsers
+# will treat secure cookies more carefully than non-secure ones, being careful
+# not to write them to disk, only send them over an SSL secured connection
+# and so on. To enable this behaviour, set # $WebSecureCookies to a true value.
+# NOTE: You probably don't want to turn this on _unless_ users are only connecting
+# via SSL encrypted HTTP connections.
+
+Set($WebSecureCookies, 0);
+
+
+# By default, RT clears its database cache after every page view.
+# This ensures that you've always got the most current information
+# when working in a multi-process (mod_perl or FastCGI) Environment
+# Setting $WebFlushDbCacheEveryRequest to '0' will turn this off,
+# which will speed RT up a bit, at the expense of a tiny bit of data
+# accuracy.
+
+Set($WebFlushDbCacheEveryRequest, '1');
+
+
+# $MaxInlineBody is the maximum attachment size that we want to see
+# inline when viewing a transaction. 13456 is a random sane-sounding
+# default.
+
+Set($MaxInlineBody, 13456);
+
+# $DefaultSummaryRows is default number of rows displayed in for search
+# results on the frontpage.
+
+Set($DefaultSummaryRows, 10);
+
+# By default, RT shows newest transactions at the bottom of the ticket
+# history page, if you want see them at the top set this to '0'.
+
+Set($OldestTransactionsFirst, '1');
+
+# By default, RT shows images attached to incoming (and outgoing) ticket updates
+# inline. Set this variable to 0 if you'd like to disable that behaviour
+
+Set($ShowTransactionImages, 1);
+
+
+# $HomepageComponents is an arrayref of allowed components on a user's
+# customized homepage ("RT at a glance").
+
+Set($HomepageComponents, [qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage)]);
+
+# @MasonParameters is the list of parameters for the constructor of
+# HTML::Mason's Apache or CGI Handler. This is normally only useful
+# for debugging, eg. profiling individual components with:
+# use MasonX::Profiler; # available on CPAN
+# @MasonParameters = (preamble => 'my $p = MasonX::Profiler->new($m, $r);');
+
+@MasonParameters = () unless (@MasonParameters);
+
+# $DefaultSearchResultFormat is the default format for RT search results
+Set ($DefaultSearchResultFormat, qq{
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
+ Status,
+ QueueName,
+ OwnerName,
+ Priority,
+ '__NEWLINE__',
+ '',
+ '<small>__Requestors__</small>',
+ '<small>__CreatedRelative__</small>',
+ '<small>__ToldRelative__</small>',
+ '<small>__LastUpdatedRelative__</small>',
+ '<small>__TimeLeft__</small>'});
+
+# If $SuppressInlineTextFiles is set to a true value, then uploaded
+# text files (text-type attachments with file names) are prevented
+# from being displayed in-line when viewing a ticket's history.
+
+Set($SuppressInlineTextFiles, undef);
+
+# If $DontSearchFileAttachments is set to a true value, then uploaded
+# files (attachments with file names) are not searched during full-content
+# ticket searches.
+
+Set($DontSearchFileAttachments, undef);
+
+# The GD module (which RT uses for graphs) uses a builtin font that doesn't
+# have full Unicode support. You can use a particular TrueType font by setting
+# $ChartFont to the absolute path of that font. Your GD library must have
+# support for TrueType fonts to use this option.
+
+Set($ChartFont, undef);
+
+
+# }}}
+
+# {{{ RT UTF-8 Settings
+
+# An array that contains languages supported by RT's internationalization
+# interface. Defaults to all *.po lexicons; setting it to qw(en ja) will make
+# RT bilingual instead of multilingual, but will save some memory.
+
+@LexiconLanguages = qw(*) unless (@LexiconLanguages);
+
+# An array that contains default encodings used to guess which charset
+# an attachment uses if not specified. Must be recognized by
+# Encode::Guess.
+
+@EmailInputEncodings = qw(utf-8 iso-8859-1 us-ascii) unless (@EmailInputEncodings);
+
+# The charset for localized email. Must be recognized by Encode.
+
+Set($EmailOutputEncoding , 'utf-8');
+
+# }}}
+
+# {{{ RT Date Handling Options (for Time::ParseDate)
+
+# Set this to 1 if your local date convention looks like "dd/mm/yy"
+# instead of "mm/dd/yy".
+
+Set($DateDayBeforeMonth , 1);
+
+# Should "Tuesday" default to meaning "Next Tuesday" or "Last Tuesday"?
+# Set to 0 for "Next" or 1 for "Last".
+
+Set($AmbiguousDayInPast , 1);
+
+# }}}
+
+# {{{ Miscellaneous RT Settings
+
+# You can define new statuses and even reorder existing statuses here.
+# WARNING. DO NOT DELETE ANY OF THE DEFAULT STATUSES. If you do, RT
+# will break horribly. The statuses you add must be no longer than
+# 10 characters.
+
+@ActiveStatus = qw(new open stalled) unless @ActiveStatus;
+@InactiveStatus = qw(resolved rejected deleted) unless @InactiveStatus;
+
+# Backward compatability setting. Add/Delete Link used to record one
+# transaction and run one scrip. Set this value to 1 if you want
+# only one of the link transactions to have scrips run.
+Set($LinkTransactionsRun1Scrip , 0);
+
+# When this feature is enabled an user need ModifyTicket right on both
+# tickets to link them together, otherwise he can have right on any of
+# two.
+Set($StrictLinkACL, 1);
+
+# }}}
+
+
+# {{{ Development Mode
+#
+# RT comes with a "Development mode" setting.
+# This setting, as a convenience for developers, turns on
+# all sorts of development options that you most likely don't want in
+# production:
+#
+# * Turns off Mason's 'static_source' directive. By default, you can't
+# edit RT's web ui components on the fly and have RT magically pick up
+# your changes. (It's a big performance hit)
+#
+# * More to come
+#
+
+Set($DevelMode, '@RT_DEVEL_MODE@');
+
+# }}}
+
+
+1;
diff --git a/rt/etc/RT_SiteConfig.pm b/rt/etc/RT_SiteConfig.pm
new file mode 100644
index 0000000..c3d6a66
--- /dev/null
+++ b/rt/etc/RT_SiteConfig.pm
@@ -0,0 +1,52 @@
+# Any configuration directives you include here will override
+# RT's default configuration file, RT_Config.pm
+#
+# To include a directive here, just copy the equivalent statement
+# from RT_Config.pm and change the value. We've included a single
+# sample value below.
+#
+# This file is actually a perl module, so you can include valid
+# perl code, as well.
+#
+# The converse is also true, if this file isn't valid perl, you're
+# going to run into trouble. To check your SiteConfig file, use
+# this comamnd:
+#
+# perl -c /path/to/your/etc/RT_SiteConfig.pm
+
+#Set( $rtname, 'example.com');
+
+# These settings should have been inserted by the initial Freeside install.
+# Sometimes you may want to change domain, timezone, or freeside::URL later,
+# everything else should probably stay untouched.
+
+$RT::rtname = '%%%RT_DOMAIN%%%';
+$RT::Organization = '%%%RT_DOMAIN%%%';
+
+$RT::Timezone = '%%%RT_TIMEZONE%%%';
+
+$RT::WebExternalAuth = 1;
+$RT::WebFallbackToInternal = 1; #no
+$RT::WebExternalAuto = 1;
+
+$RT::URI::freeside::IntegrationType = 'Internal';
+$RT::URI::freeside::URL = '%%%FREESIDE_URL%%%';
+
+$RT::URI::freeside::URL =~ m(^(https?://[^/]+)(/.*)$)i;
+$RT::WebBaseURL = $1;
+$RT::WebPath = "$2/rt";
+
+Set($DatabaseHost , '');
+
+# These settings are user-editable.
+
+#old, RT 3.4 style (deprecated, useless):
+#$RT::MyTicketsLength = 10;
+#NEW, RT 3.6 style (uncomment to use):
+#Set($DefaultSummaryRows, 10);
+
+$RT::QuickCreateLong = 0; #set to true to cause quick ticket creation to
+ #redirect to the "long" ticket creation screen
+ #instead of just creating a ticket with the subject.
+
+1;
diff --git a/rt/etc/acl.Informix b/rt/etc/acl.Informix
new file mode 100644
index 0000000..bca0408
--- /dev/null
+++ b/rt/etc/acl.Informix
@@ -0,0 +1,5 @@
+sub acl {
+return (
+"GRANT RESOURCE TO ${RT::DatabaseUser};");
+}
+1;
diff --git a/rt/etc/acl.Oracle b/rt/etc/acl.Oracle
new file mode 100644
index 0000000..ac29215
--- /dev/null
+++ b/rt/etc/acl.Oracle
@@ -0,0 +1,10 @@
+sub acl {
+return (
+"CREATE USER ${RT::DatabaseUser} identified by ${RT::DatabasePassword} ".
+"default tablespace USERS " .
+"temporary tablespace TEMP " .
+"quota unlimited on USERS" ,
+"grant connect, resource to ${RT::DatabaseUser}"
+);
+}
+1;
diff --git a/rt/etc/acl.Pg b/rt/etc/acl.Pg
new file mode 100755
index 0000000..fb62559
--- /dev/null
+++ b/rt/etc/acl.Pg
@@ -0,0 +1,67 @@
+sub acl {
+ my $dbh = shift;
+
+ my @acls;
+
+ my @tables = qw (
+
+ attachments_id_seq
+ Attachments
+ Attributes
+ attributes_id_seq
+ queues_id_seq
+ Queues
+ links_id_seq
+ Links
+ principals_id_seq
+ Principals
+ groups_id_seq
+ Groups
+ scripconditions_id_seq
+ ScripConditions
+ transactions_id_seq
+ Transactions
+ scrips_id_seq
+ Scrips
+ acl_id_seq
+ ACL
+ groupmembers_id_seq
+ GroupMembers
+ cachedgroupmembers_id_seq
+ CachedGroupMembers
+ users_id_seq
+ Users
+ tickets_id_seq
+ Tickets
+ scripactions_id_seq
+ ScripActions
+ templates_id_seq
+ Templates
+ objectcustomfieldvalues_id_s
+ ObjectCustomFieldValues
+ customfields_id_seq
+ CustomFields
+ objectcustomfields_id_s
+ ObjectCustomFields
+ customfieldvalues_id_seq
+ CustomFieldValues
+ sessions
+ );
+
+ # if there's already an rt_user, drop it.
+ my @row =
+ $dbh->selectrow_array( "select usename from pg_user where usename = '" . $RT::DatabaseUser."'" );
+ if ( $row[0] ) {
+ push @acls, "drop user ${RT::DatabaseUser};",;
+ }
+
+ push @acls, "create user ${RT::DatabaseUser} with password '${RT::DatabasePassword}' NOCREATEDB NOCREATEUSER;";
+ foreach my $table (@tables) {
+ push @acls,
+ "GRANT SELECT, INSERT, UPDATE, DELETE ON $table to "
+ . $RT::DatabaseUser . ";";
+
+ }
+ return (@acls);
+}
+1;
diff --git a/rt/etc/acl.Sybase b/rt/etc/acl.Sybase
new file mode 100644
index 0000000..6192b4e
--- /dev/null
+++ b/rt/etc/acl.Sybase
@@ -0,0 +1,6 @@
+sub acl {
+return (
+"SP_ADDLOGIN ${RT::DatabaseUser}, ${RT::DatabasePassword}, ${RT::DatabaseName} ",
+);
+}
+1;
diff --git a/rt/etc/acl.mysql b/rt/etc/acl.mysql
new file mode 100755
index 0000000..621ef12
--- /dev/null
+++ b/rt/etc/acl.mysql
@@ -0,0 +1,9 @@
+sub acl {
+return () if !$RT::DatabaseUser or $RT::DatabaseUser eq 'root';
+return (
+"USE mysql;",
+"DELETE FROM user WHERE user = '${RT::DatabaseUser}';",
+"DELETE FROM db where db = '${RT::DatabaseName}';",
+"GRANT SELECT,INSERT,CREATE,INDEX,UPDATE,DELETE ON ${RT::DatabaseName}.* TO ${RT::DatabaseUser}\@'${RT::DatabaseRTHost}' IDENTIFIED BY '${RT::DatabasePassword}';");
+}
+1;
diff --git a/rt/etc/constraints.mysql b/rt/etc/constraints.mysql
new file mode 100644
index 0000000..355d2c5
--- /dev/null
+++ b/rt/etc/constraints.mysql
@@ -0,0 +1,85 @@
+
+ ALTER TABLE Links ADD INDEX(LocalBase);
+ ALTER TABLE Links ADD FOREIGN KEY (LocalBase) REFERENCES Tickets(id);
+ ALTER TABLE Links ADD INDEX(LocalTarget);
+ ALTER TABLE Links ADD FOREIGN KEY (LocalTarget) REFERENCES Tickets(id);
+ ALTER TABLE Tickets ADD INDEX(Queue);
+ ALTER TABLE Tickets ADD FOREIGN KEY (Queue) REFERENCES Queues(id);
+ ALTER TABLE Tickets ADD INDEX(EffectiveId);
+ ALTER TABLE Tickets ADD FOREIGN KEY (EffectiveId) REFERENCES Tickets(id);
+ ALTER TABLE Tickets ADD INDEX(Owner);
+ ALTER TABLE Tickets ADD FOREIGN KEY (Owner) REFERENCES Principals(id);
+ ALTER TABLE Tickets ADD INDEX(Creator);
+ ALTER TABLE Tickets ADD INDEX(LastUpdatedBy);
+ ALTER TABLE Tickets ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE Tickets ADD FOREIGN KEY (LastUpdatedBy) REFERENCES Users(id);
+ ALTER TABLE Transactions ADD INDEX(Creator);
+ ALTER TABLE Transactions ADD INDEX (Ticket) ;
+ ALTER TABLE Transactions ADD INDEX (EffectiveTicket) ;
+ ALTER TABLE Transactions ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE Transactions ADD FOREIGN KEY (Ticket) REFERENCES Tickets(id);
+ ALTER TABLE Transactions ADD FOREIGN KEY (EffectiveTicket) REFERENCES Tickets(id);
+ ALTER TABLE Attachments ADD INDEX (TransactionId) ;
+ ALTER TABLE Attachments ADD INDEX (Parent) ;
+ ALTER TABLE Attachments ADD FOREIGN KEY (TransactionId) REFERENCES Transactions(id);
+ ALTER TABLE Attachments ADD FOREIGN KEY (Parent) REFERENCES Attachments(id);
+ ALTER TABLE Scrips ADD INDEX (ScripCondition) ;
+ ALTER TABLE Scrips ADD INDEX (ScripAction) ;
+ ALTER TABLE Scrips ADD INDEX (Template) ;
+ ALTER TABLE Scrips ADD INDEX (Queue) ;
+ ALTER TABLE Scrips ADD INDEX (Creator) ;
+ ALTER TABLE Scrips ADD INDEX (LastUpdatedBy) ;
+ ALTER TABLE Scrips ADD FOREIGN KEY (ScripCondition) REFERENCES ScripConditions(id);
+ ALTER TABLE Scrips ADD FOREIGN KEY (ScripAction) REFERENCES ScripActions(id);
+ ALTER TABLE Scrips ADD FOREIGN KEY (Template) REFERENCES Templates(id);
+ ALTER TABLE Scrips ADD FOREIGN KEY (Queue) REFERENCES Queues(id);
+ ALTER TABLE Scrips ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE Scrips ADD FOREIGN KEY (LastUpdatedBy) REFERENCES Users(id);
+ ALTER TABLE ACL ADD INDEX (PrincipalId) ;
+ ALTER TABLE ACL ADD INDEX (DelegatedBy) ;
+ ALTER TABLE ACL ADD INDEX (DelegatedFrom) ;
+ ALTER TABLE ACL ADD FOREIGN KEY (PrincipalId) REFERENCES Principals(id);
+ ALTER TABLE ACL ADD FOREIGN KEY (DelegatedBy) REFERENCES Principals(id);
+ ALTER TABLE ACL ADD FOREIGN KEY (DelegatedFrom) REFERENCES ACL(id);
+ ALTER TABLE GroupMembers ADD INDEX (MemberId);
+ ALTER TABLE GroupMembers ADD INDEX (GroupId);
+ ALTER TABLE GroupMembers ADD FOREIGN KEY (GroupId) REFERENCES Groups(id);
+ ALTER TABLE GroupMembers ADD FOREIGN KEY (MemberId) REFERENCES Principals(id);
+ ALTER TABLE CachedGroupMembers ADD INDEX (ImmediateParentId) ;
+ ALTER TABLE CachedGroupMembers ADD INDEX (GroupId) ;
+ ALTER TABLE CachedGroupMembers ADD INDEX (MemberId) ;
+ ALTER TABLE CachedGroupMembers ADD INDEX (Via) ;
+ ALTER TABLE CachedGroupMembers ADD FOREIGN KEY (ImmediateParentId) REFERENCES Principals(id);
+ ALTER TABLE CachedGroupMembers ADD FOREIGN KEY (GroupId) REFERENCES Principals(id);
+ ALTER TABLE CachedGroupMembers ADD FOREIGN KEY (MemberId) REFERENCES Principals(id);
+ ALTER TABLE CachedGroupMembers ADD FOREIGN KEY (Via) REFERENCES CachedGroupMembers(id);
+ ALTER TABLE ScripActions ADD INDEX(Creator);
+ ALTER TABLE ScripActions ADD INDEX(LastUpdatedBy);
+ ALTER TABLE ScripActions ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE ScripActions ADD FOREIGN KEY (LastUpdatedBy) REFERENCES Users(id);
+ ALTER TABLE Templates ADD INDEX(Queue);
+ ALTER TABLE Templates ADD INDEX(Creator);
+ ALTER TABLE Templates ADD INDEX(LastUpdatedBy);
+ ALTER TABLE Templates ADD FOREIGN KEY (Queue) REFERENCES Queues(id);
+ ALTER TABLE Templates ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE Templates ADD FOREIGN KEY (LastUpdatedBy) REFERENCES Users(id);
+ ALTER TABLE CustomFields ADD INDEX(Queue);
+ ALTER TABLE CustomFields ADD INDEX(Creator);
+ ALTER TABLE CustomFields ADD INDEX(LastUpdatedBy);
+ ALTER TABLE CustomFields ADD FOREIGN KEY (Queue) REFERENCES Queues(id);
+ ALTER TABLE CustomFields ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE CustomFields ADD FOREIGN KEY (LastUpdatedBy) REFERENCES Users(id);
+ ALTER TABLE TicketCustomFieldValues ADD INDEX(Ticket);
+ ALTER TABLE TicketCustomFieldValues ADD INDEX(CustomField);
+ ALTER TABLE TicketCustomFieldValues ADD INDEX(Creator);
+ ALTER TABLE TicketCustomFieldValues ADD INDEX(LastUpdatedBy);
+ ALTER TABLE TicketCustomFieldValues ADD FOREIGN KEY (Ticket) REFERENCES Tickets(id);
+ ALTER TABLE TicketCustomFieldValues ADD FOREIGN KEY (CustomField) REFERENCES CustomFields(id);
+ ALTER TABLE TicketCustomFieldValues ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE TicketCustomFieldValues ADD FOREIGN KEY (LastUpdatedBy) REFERENCES Users(id);
+ ALTER TABLE CustomFieldValues ADD INDEX(CustomField);
+ ALTER TABLE CustomFieldValues ADD INDEX(Creator);
+ ALTER TABLE CustomFieldValues ADD INDEX(LastUpdatedBy);
+ ALTER TABLE CustomFieldValues ADD FOREIGN KEY (CustomField) REFERENCES CustomFields(id);
+ ALTER TABLE CustomFieldValues ADD FOREIGN KEY (Creator) REFERENCES Users(id);
+ ALTER TABLE CustomFieldValues ADD FOREIGN KEY (LastUpdatedBy) REFERENCES Users(id);
diff --git a/rt/etc/drop.Informix b/rt/etc/drop.Informix
new file mode 100644
index 0000000..ce7cc01
--- /dev/null
+++ b/rt/etc/drop.Informix
@@ -0,0 +1,19 @@
+DROP TABLE ACL;
+DROP TABLE ATTACHMENTS;
+DROP TABLE CACHEDGROUPMEMBERS;
+DROP TABLE CUSTOMFIELDS;
+DROP TABLE CUSTOMFIELDVALUES;
+DROP TABLE GROUPMEMBERS;
+DROP TABLE GROUPS;
+DROP TABLE LINKS;
+DROP TABLE PRINCIPALS;
+DROP TABLE QUEUES;
+DROP TABLE SCRIPACTIONS;
+DROP TABLE SCRIPCONDITIONS;
+DROP TABLE SCRIPS;
+DROP TABLE SESSIONS;
+DROP TABLE TEMPLATES;
+DROP TABLE TICKETCUSTOMFIELDVALUES;
+DROP TABLE TICKETS;
+DROP TABLE TRANSACTIONS;
+DROP TABLE USERS;
diff --git a/rt/etc/drop.Oracle b/rt/etc/drop.Oracle
new file mode 100644
index 0000000..475e984
--- /dev/null
+++ b/rt/etc/drop.Oracle
@@ -0,0 +1,41 @@
+DROP TABLE ACL;
+DROP TABLE ATTACHMENTS;
+DROP TABLE ATTRIBUTES;
+DROP TABLE CACHEDGROUPMEMBERS;
+DROP TABLE CUSTOMFIELDS;
+DROP TABLE CUSTOMFIELDVALUES;
+DROP TABLE GROUPMEMBERS;
+DROP TABLE GROUPS;
+DROP TABLE LINKS;
+DROP TABLE PRINCIPALS;
+DROP TABLE QUEUES;
+DROP TABLE SCRIPACTIONS;
+DROP TABLE SCRIPCONDITIONS;
+DROP TABLE SCRIPS;
+DROP TABLE SESSIONS;
+DROP TABLE TEMPLATES;
+DROP TABLE OBJECTCUSTOMFIELDS;
+DROP TABLE OBJECTCUSTOMFIELDVALUES;
+DROP TABLE TICKETS;
+DROP TABLE TRANSACTIONS;
+DROP TABLE USERS;
+DROP SEQUENCE ACL_seq;
+DROP SEQUENCE ATTACHMENTS_seq;
+DROP SEQUENCE ATTRIBUTES_seq;
+DROP SEQUENCE CACHEDGROUPMEMBERS_seq;
+DROP SEQUENCE CUSTOMFIELDS_seq;
+DROP SEQUENCE CUSTOMFIELDVALUES_seq;
+DROP SEQUENCE GROUPMEMBERS_seq;
+DROP SEQUENCE GROUPS_seq;
+DROP SEQUENCE LINKS_seq;
+DROP SEQUENCE PRINCIPALS_seq;
+DROP SEQUENCE QUEUES_seq;
+DROP SEQUENCE SCRIPACTIONS_seq;
+DROP SEQUENCE SCRIPCONDITIONS_seq;
+DROP SEQUENCE SCRIPS_seq;
+DROP SEQUENCE TEMPLATES_seq;
+DROP SEQUENCE OBJECTCUSTOMFIELDVALUES_seq;
+DROP SEQUENCE OBJECTCUSTOMFIELDS_seq;
+DROP SEQUENCE TICKETS_seq;
+DROP SEQUENCE TRANSACTIONS_seq;
+DROP SEQUENCE USERS_seq;
diff --git a/rt/etc/initialdata b/rt/etc/initialdata
new file mode 100644
index 0000000..54fa9d1
--- /dev/null
+++ b/rt/etc/initialdata
@@ -0,0 +1,625 @@
+# Initial data for a fresh RT3 Installation.
+
+@Users = (
+ { Name => 'Nobody',
+ RealName => 'Nobody in particular',
+ Comments => 'Do not delete or modify this user. It is integral '
+ . 'to RT\'s internal data structures',
+ Privileged => '0', },
+
+ { Name => 'root',
+ Gecos => 'root',
+ RealName => 'Enoch Root',
+ Password => 'password',
+ EmailAddress => "root\@localhost",
+ Comments => 'SuperUser',
+ Privileged => '1', } );
+
+@Groups = (
+ { Name => '',
+ Type => 'Everyone', # loc
+ Domain => 'SystemInternal',
+ Instance => '',
+ Description => 'Pseudogroup for internal use', # loc
+ },
+ { Type => 'Privileged', # loc
+ Domain => 'SystemInternal',
+ Instance => '',
+ Name => '',
+ Description => 'Pseudogroup for internal use', # loc
+ },
+ { Name => '',
+ Type => 'Unprivileged', # loc
+ Domain => 'SystemInternal',
+ Instance => '',
+ Description => 'Pseudogroup for internal use', # loc
+ },
+ { Name => '',
+ Type => 'Owner', # loc
+ Domain => 'RT::System-Role',
+ Instance => '',
+ Description => 'SystemRolegroup for internal use', # loc
+ },
+ { Name => '',
+ Type => 'Requestor', # loc
+ Domain => 'RT::System-Role',
+ Instance => '',
+ Description => 'SystemRolegroup for internal use', # loc
+ },
+ { Name => '',
+ Type => 'Cc', # loc
+ Domain => 'RT::System-Role',
+ Instance => '',
+ Description => 'SystemRolegroup for internal use', # loc
+ },
+ { Name => '',
+ Type => 'AdminCc', # loc
+ Domain => 'RT::System-Role',
+ Instance => '',
+ Description => 'Pseudogroup for internal use', # loc
+ }, );
+
+@Queues = ({ Name => 'General',
+ Description => 'The default queue',
+ CorrespondAddress => "",
+ CommentAddress => "", },
+ { Name => '___Approvals',
+ Description => 'A system-internal queue for the approvals system',
+ Disabled => 2, } );
+
+@ScripActions = (
+
+ { Name => 'Autoreply To Requestors', # loc
+ Description =>
+'Always sends a message to the requestors independent of message sender' , # loc
+ ExecModule => 'Autoreply',
+ Argument => 'Requestor' },
+ { Name => 'Notify Requestors', # loc
+ Description => 'Sends a message to the requestors', # loc
+ ExecModule => 'Notify',
+ Argument => 'Requestor' },
+ { Name => 'Notify Owner as Comment', # loc
+ Description => 'Sends mail to the owner', # loc
+ ExecModule => 'NotifyAsComment',
+ Argument => 'Owner' },
+ { Name => 'Notify Owner', # loc
+ Description => 'Sends mail to the owner', # loc
+ ExecModule => 'Notify',
+ Argument => 'Owner' },
+ { Name => 'Notify Ccs as Comment', # loc
+ Description => 'Sends mail to the Ccs as a comment', # loc
+ ExecModule => 'NotifyAsComment',
+ Argument => 'Cc' },
+ { Name => 'Notify Ccs', # loc
+ Description => 'Sends mail to the Ccs', # loc
+ ExecModule => 'Notify',
+ Argument => 'Cc' },
+ { Name => 'Notify AdminCcs as Comment', # loc
+ Description => 'Sends mail to the administrative Ccs as a comment', # loc
+ ExecModule => 'NotifyAsComment',
+ Argument => 'AdminCc' },
+ { Name => 'Notify AdminCcs', # loc
+ Description => 'Sends mail to the administrative Ccs', # loc
+ ExecModule => 'Notify',
+ Argument => 'AdminCc' },
+
+ { Name => 'Notify Requestors and Ccs as Comment', # loc
+ Description => 'Send mail to requestors and Ccs as a comment', # loc
+ ExecModule => 'NotifyAsComment',
+ Argument => 'Requestor,Cc' },
+
+ { Name => 'Notify Requestors and Ccs', # loc
+ Description => 'Send mail to requestors and Ccs', # loc
+ ExecModule => 'Notify',
+ Argument => 'Requestor,Cc' },
+
+ { Name => 'Notify Requestors, Ccs and AdminCcs as Comment', # loc
+ Description => 'Send mail to all watchers as a "comment"', # loc
+ ExecModule => 'NotifyAsComment',
+ Argument => 'All' },
+ { Name => 'Notify Requestors, Ccs and AdminCcs', # loc
+ Description => 'Send mail to all watchers', # loc
+ ExecModule => 'Notify',
+ Argument => 'All' },
+ { Name => 'Notify Other Recipients as Comment', # loc
+ Description => 'Sends mail to explicitly listed Ccs and Bccs', # loc
+ ExecModule => 'NotifyAsComment',
+ Argument => 'OtherRecipients' },
+ { Name => 'Notify Other Recipients', # loc
+ Description => 'Sends mail to explicitly listed Ccs and Bccs', # loc
+ ExecModule => 'Notify',
+ Argument => 'OtherRecipients' },
+ { Name => 'User Defined', # loc
+ Description => 'Perform a user-defined action', # loc
+ ExecModule => 'UserDefined', },
+ { Name => 'Create Tickets', # loc
+ Description =>
+ 'Create new tickets based on this scrip\'s template', # loc
+ ExecModule => 'CreateTickets', },
+ { Name => 'Open Tickets', # loc
+ Description => 'Open tickets on correspondence', # loc
+ ExecModule => 'AutoOpen' },
+);
+
+@ScripConditions = (
+ { Name => 'On Create', # loc
+ Description => 'When a ticket is created', # loc
+ ApplicableTransTypes => 'Create',
+ ExecModule => 'AnyTransaction', },
+
+ { Name => 'On Transaction', # loc
+ Description => 'When anything happens', # loc
+ ApplicableTransTypes => 'Any',
+ ExecModule => 'AnyTransaction', },
+ {
+
+ Name => 'On Correspond', # loc
+ Description => 'Whenever correspondence comes in', # loc
+ ApplicableTransTypes => 'Correspond',
+ ExecModule => 'AnyTransaction', },
+
+ {
+
+ Name => 'On Comment', # loc
+ Description => 'Whenever comments come in', # loc
+ ApplicableTransTypes => 'Comment',
+ ExecModule => 'AnyTransaction' },
+ {
+
+ Name => 'On Status Change', # loc
+ Description => 'Whenever a ticket\'s status changes', # loc
+ ApplicableTransTypes => 'Status',
+ ExecModule => 'AnyTransaction',
+
+ },
+ {
+
+ Name => 'On Priority Change', # loc
+ Description => 'Whenever a ticket\'s priority changes', # loc
+ ApplicableTransTypes => 'Set',
+ ExecModule => 'PriorityChange',
+ },
+ {
+
+ Name => 'On Owner Change', # loc
+ Description => 'Whenever a ticket\'s owner changes', # loc
+ ApplicableTransTypes => 'Any',
+ ExecModule => 'OwnerChange',
+
+ },
+ {
+
+ Name => 'On Queue Change', # loc
+ Description => 'Whenever a ticket\'s queue changes', # loc
+ ApplicableTransTypes => 'Set',
+ ExecModule => 'QueueChange',
+
+ },
+ { Name => 'On Resolve', # loc
+ Description => 'Whenever a ticket is resolved', # loc
+ ApplicableTransTypes => 'Status',
+ ExecModule => 'StatusChange',
+ Argument => 'resolved'
+
+ },
+
+ { Name => 'User Defined', # loc
+ Description => 'Whenever a user-defined condition occurs', # loc
+ ApplicableTransTypes => 'Any',
+ ExecModule => 'UserDefined'
+
+ },
+
+);
+
+@Templates = (
+ { Queue => '0',
+ Name => 'Blank', # loc
+ Description => 'A blank template', # loc
+ Content => '', },
+ { Queue => '0',
+ Name => 'Autoreply', # loc
+ Description => 'Default Autoresponse template', # loc
+ Content => 'Subject: AutoReply: {$Ticket->Subject}
+
+
+Greetings,
+
+This message has been automatically generated in response to the
+creation of a trouble ticket regarding:
+ "{$Ticket->Subject()}",
+a summary of which appears below.
+
+There is no need to reply to this message right now. Your ticket has been
+assigned an ID of [{$rtname} #{$Ticket->id()}].
+
+Please include the string:
+
+ [{$rtname} #{$Ticket->id}]
+
+in the subject line of all future correspondence about this issue. To do so,
+you may reply to this message.
+
+ Thank you,
+ {$Ticket->QueueObj->CorrespondAddress()}
+
+-------------------------------------------------------------------------
+{$Transaction->Content()}
+'
+ },
+
+ { Queue => '0',
+ Name => 'Transaction', # loc
+ Description => 'Default transaction template', # loc
+ Content => 'RT-Attach-Message: yes
+
+
+{$Transaction->CreatedAsString}: Request {$Ticket->id} was acted upon.
+Transaction: {$Transaction->Description}
+ Queue: {$Ticket->QueueObj->Name}
+ Subject: {$Transaction->Subject || $Ticket->Subject || "(No subject given)"}
+ Owner: {$Ticket->OwnerObj->Name}
+ Requestors: {$Ticket->RequestorAddresses}
+ Status: {$Ticket->Status}
+ Ticket <URL: {$RT::WebURL}Ticket/Display.html?id={$Ticket->id} >
+
+
+{$Transaction->Content()}
+'
+ },
+
+ {
+
+ Queue => '0',
+ Name => 'Admin Correspondence', # loc
+ Description => 'Default admin correspondence template', # loc
+ Content => 'RT-Attach-Message: yes
+
+
+<URL: {$RT::WebURL}Ticket/Display.html?id={$Ticket->id} >
+
+{$Transaction->Content()}
+'
+ },
+
+ { Queue => '0',
+ Name => 'Correspondence', # loc
+ Description => 'Default correspondence template', # loc
+ Content => 'RT-Attach-Message: yes
+
+{$Transaction->Content()}
+'
+ },
+
+ { Queue => '0',
+ Name => 'Admin Comment', # loc
+ Description => 'Default admin comment template', # loc
+ Content =>
+'Subject: [Comment] {my $s=($Transaction->Subject||$Ticket->Subject); $s =~ s/\\[Comment\\]//g; $comment =~ s/^Re//i; $s;}
+
+
+{$RT::WebURL}Ticket/Display.html?id={$Ticket->id}
+This is a comment. It is not sent to the Requestor(s):
+
+{$Transaction->Content()}
+'
+ },
+
+ { Queue => '0',
+ Name => 'Status Change', # loc
+ Description => 'Ticket status changed', # loc
+ Content => 'Subject: Status Changed to: {$Transaction->NewValue}
+
+
+{$RT::WebURL}Ticket/Display.html?id={$Ticket->id}
+
+{$Transaction->Content()}
+'
+ },
+
+ {
+
+ Queue => '0',
+ Name => 'Resolved', # loc
+ Description => 'Ticket Resolved', # loc
+ Content => 'Subject: Resolved: {$Ticket->Subject}
+
+According to our records, your request has been resolved. If you have any
+further questions or concerns, please respond to this message.
+'
+ },
+ { Queue => '___Approvals',
+ Name => "New Pending Approval", # loc
+ Description =>
+ "Notify Owners and AdminCcs of new items pending their approval", # loc
+ Content => 'Subject: New Pending Approval: {$Ticket->Subject}
+
+Greetings,
+
+There is a new item pending your approval: "{$Ticket->Subject()}",
+a summary of which appears below.
+
+Please visit {$RT::WebURL}Approvals/Display.html?id={$Ticket->id}
+to approve or reject this ticket, or {$RT::WebURL}Approvals/ to
+batch-process all your pending approvals.
+
+-------------------------------------------------------------------------
+{$Transaction->Content()}
+'
+ },
+ { Queue => '___Approvals',
+ Name => "Approval Passed", # loc
+ Description =>
+ "Notify Owner of their ticket has been approved by some approver", # loc
+ Content => 'Subject: Ticket Approved: {$Ticket->Subject}
+
+Greetings,
+
+Your ticket has been approved by { eval { $Approval->OwnerObj->Name } }.
+Other approvals may be pending.
+'
+ },
+ { Queue => '___Approvals',
+ Name => "All Approvals Passed", # loc
+ Description =>
+ "Notify Owner of their ticket has been approved by all approvers", # loc
+ Content => 'Subject: Ticket Approved: {$Ticket->Subject}
+
+Greetings,
+
+Your ticket has been approved. Its Owner may now start to act on it.
+'
+ },
+ { Queue => '___Approvals',
+ Name => "Approval Rejected", # loc
+ Description =>
+ "Notify Owner of their rejected ticket", # loc
+ Content => 'Subject: Ticket Rejected: {$Ticket->Subject}
+
+Greetings,
+
+Your ticket has been rejected by { eval { $Approval->OwnerObj->Name } }.
+'
+ },
+);
+# }}}
+
+@Scrips = (
+ { ScripCondition => 'On Correspond',
+ ScripAction => 'Open Tickets',
+ Template => 'Blank' },
+ { ScripCondition => 'On Owner Change',
+ ScripAction => 'Notify Owner',
+ Template => 'Transaction' },
+ { ScripCondition => 'On Create',
+ ScripAction => 'AutoReply To Requestors',
+ Template => 'AutoReply' },
+ { ScripCondition => 'On Create',
+ ScripAction => 'Notify AdminCcs',
+ Template => 'Transaction' },
+ { ScripCondition => 'On Correspond',
+ ScripAction => 'Notify AdminCcs',
+ Template => 'Admin Correspondence' },
+ { ScripCondition => 'On Correspond',
+ ScripAction => 'Notify Requestors And Ccs',
+ Template => 'Correspondence' },
+ { ScripCondition => 'On Correspond',
+ ScripAction => 'Notify Other Recipients',
+ Template => 'Correspondence' },
+ { ScripCondition => 'On Comment',
+ ScripAction => 'Notify AdminCcs As Comment',
+ Template => 'Admin Comment' },
+ { ScripCondition => 'On Comment',
+ ScripAction => 'Notify Other Recipients As Comment',
+ Template => 'Correspondence' },
+ { ScripCondition => 'On Resolve',
+ ScripAction => 'Notify Requestors',
+ Template => 'Resolved' },
+ { Description => "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval", # loc
+ Queue => '___Approvals',
+ ScripCondition => 'User Defined',
+ CustomIsApplicableCode => q[
+ $self->TicketObj->Type eq 'approval' and
+ $self->TransactionObj->Field eq 'Status' and
+ $self->TransactionObj->NewValue eq 'open' and
+ eval { $T::Approving = ($self->TicketObj->AllDependedOnBy( Type => 'ticket' ))[0] }
+ ],
+ ScripAction => 'Notify Owner',
+ Template => 'New Pending Approval' },
+ { Description => "If an approval is rejected, reject the original and delete pending approvals", # loc
+ Queue => '___Approvals',
+ ScripCondition => 'On Status Change',
+ ScripAction => 'User Defined',
+ CustomPrepareCode => q[
+# ------------------------------------------------------------------- #
+return(0) unless ( lc($self->TransactionObj->NewValue) eq "rejected" or
+ lc($self->TransactionObj->NewValue) eq "deleted" );
+
+my $rejected = 0;
+my $links = $self->TicketObj->DependedOnBy;
+foreach my $link (@{ $links->ItemsArrayRef }) {
+ my $obj = $link->BaseObj;
+ if ($obj->QueueObj->IsActiveStatus($obj->Status)) {
+ if ($obj->Type eq 'ticket') {
+ $obj->Comment(
+ Content => $self->loc("Your request was rejected."),
+ );
+ $obj->SetStatus(
+ Status => 'rejected',
+ Force => 1,
+ );
+
+ $T::Approval = $self->TicketObj; # so we can access it inside templates
+ $self->{TicketObj} = $obj; # we want the original id in the token line
+ $rejected = 1;
+ }
+ else {
+ $obj->SetStatus(
+ Status => 'deleted',
+ Force => 1,
+ );
+ }
+ }
+}
+
+$links = $self->TicketObj->DependsOn;
+foreach my $link (@{ $links->ItemsArrayRef }) {
+ my $obj = $link->TargetObj;
+ if ($obj->QueueObj->IsActiveStatus($obj->Status)) {
+ $obj->SetStatus(
+ Status => 'deleted',
+ Force => 1,
+ );
+ }
+}
+
+# Now magically turn myself into a Requestor Notify object...
+require RT::Action::Notify; bless($self, 'RT::Action::Notify');
+$self->{Argument} = 'Requestor'; $self->Prepare;
+
+return $rejected;
+# ------------------------------------------------------------------- #
+ ],
+ CustomCommitCode => '"never needed"',
+ Template => 'Approval Rejected', },
+ { Description => "When a ticket has been approved by any approver, add correspondence to the original ticket", # loc
+ Queue => '___Approvals',
+ ScripCondition => 'On Resolve',
+ ScripAction => 'User Defined',
+ CustomPrepareCode => q[
+# ------------------------------------------------------------------- #
+return(0) unless ($self->TicketObj->Type eq 'approval');
+
+my $note;
+my $t = $self->TicketObj->Transactions;
+while (my $o = $t->Next) {
+ $note .= $o->Content . "\n" if $o->ContentObj
+ and $o->Content !~ /Default Approval/;
+}
+
+foreach my $obj ($self->TicketObj->AllDependedOnBy( Type => 'ticket' )) {
+ $obj->Comment(
+ Content => $self->loc( "Your request has been approved by [_1]. Other approvals may still be pending.", # loc
+ $self->TransactionObj->CreatorObj->Name,
+ ) . "\n" . $self->loc( "Approver's notes: [_1]", # loc
+ $note
+ ),
+ );
+ $T::Approval = $self->TicketObj; # so we can access it inside templates
+ $self->{TicketObj} = $obj; # we want the original id in the token line
+}
+
+# Now magically turn myself into a Requestor Notify object...
+require RT::Action::Notify; bless($self, 'RT::Action::Notify');
+$self->{Argument} = 'Requestor'; $self->Prepare;
+
+return 1;
+# ------------------------------------------------------------------- #
+ ],
+ CustomCommitCode => '"never needed"',
+ Template => 'Approval Passed' },
+ { Description => "When a ticket has been approved by all approvers, add correspondence to the original ticket", # loc
+ Queue => '___Approvals',
+ ScripCondition => 'On Resolve',
+ ScripAction => 'User Defined',
+ CustomPrepareCode => q[
+# ------------------------------------------------------------------- #
+# Find all the tickets that depend on this (that this is approving)
+
+my $Ticket = $self->TicketObj;
+my @TOP = $Ticket->AllDependedOnBy( Type => 'ticket' );
+my $links = $Ticket->DependedOnBy;
+my $passed = 0;
+
+while (my $link = $links->Next) {
+ my $obj = $link->BaseObj;
+ next if ($obj->HasUnresolvedDependencies( Type => 'approval' ));
+
+ if ($obj->Type eq 'ticket') {
+ $obj->Comment(
+ Content => $self->loc("Your request has been approved."),
+ );
+ $T::Approval = $Ticket; # so we can access it inside templates
+ $self->{TicketObj} = $obj; # we want the original id in the token line
+ $passed = 1;
+ }
+ elsif ($obj->Type eq 'approval') {
+ $obj->SetStatus( Status => 'open', Force => 1 );
+ }
+ elsif ($RT::UseCodeTickets and $obj->Type eq 'code') {
+ my $code = $obj->Transactions->First->Content;
+ my $rv;
+
+ foreach my $TOP (@TOP) {
+ local $@;
+ $rv++ if eval $code;
+ $RT::Logger->error("Cannot eval code: $@") if $@;
+ }
+
+ if ($rv or !@TOP) {
+ $obj->SetStatus( Status => 'resolved', Force => 1,);
+ }
+ else {
+ $obj->SetStatus( Status => 'rejected', Force => 1,);
+ }
+ }
+}
+
+# Now magically turn myself into a Requestor Notify object...
+require RT::Action::Notify; bless($self, 'RT::Action::Notify');
+$self->{Argument} = 'Requestor'; $self->Prepare;
+
+return 0; # ignore $passed;
+# ------------------------------------------------------------------- #
+ ],
+ CustomCommitCode => '"never needed"',
+ Template => 'All Approvals Passed', },
+
+);
+
+@ACL = (
+ { UserId => 'Nobody', # - principalId
+ Right => 'OwnTicket', },
+
+ { UserId => 'root', # - principalid
+ Right => 'SuperUser', },
+
+);
+
+# Predefined searches
+
+@Attributes = (
+ { Name => 'Search - My Tickets',
+ Description => '[_1] highest priority tickets I own', # loc
+ Content =>
+ { Format => "'<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__id__</a>/TITLE:#', '<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__Subject__</a>/TITLE:Subject', Priority, QueueName, ExtendedStatus",
+ Query => " Owner = '__CurrentUser__' AND ( Status = 'new' OR Status = 'open')",
+ OrderBy => 'Priority',
+ Order => 'DESC' },
+ },
+ { Name => 'Search - Unowned Tickets',
+ Description => '[_1] newest unowned tickets', # loc
+ Content =>
+# 'Take' #loc
+ { Format => "'<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__id__</a>/TITLE:#', '<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__Subject__</a>/TITLE:Subject', QueueName, ExtendedStatus, CreatedRelative, '<A HREF=\"__WebPath__/Ticket/Display.html?Action=Take&id=__id__\">__loc(Take)__</a>/TITLE:&nbsp;' ",
+ Query => " Owner = 'Nobody' AND ( Status = 'new' OR Status = 'open')",
+ OrderBy => 'Created',
+ Order => 'DESC' },
+ },
+ { Name => 'HomepageSettings',
+ Description => 'HomepageSettings',
+ Content =>
+ { 'body' => # loc
+ [ { type => 'system', name => 'My Tickets' },
+ { type => 'system', name => 'Unowned Tickets' },
+ { type => 'component', name => 'QuickCreate'},
+ ],
+ 'summary' => # loc
+ [
+ { type => 'component', name => 'MyReminders' },
+ { type => 'component', name => 'Quicksearch' },
+ { type => 'component', name => 'RefreshHomepage' },
+ ]
+ },
+}
+);
diff --git a/rt/etc/rt.spec b/rt/etc/rt.spec
new file mode 100644
index 0000000..14200c1
--- /dev/null
+++ b/rt/etc/rt.spec
@@ -0,0 +1,137 @@
+Summary: rt Request Tracker
+
+Name: rt
+Version: 2.0.9pre5
+Release: 1
+Group: Applications/Web
+Packager: Jesse Vincent <jesse@bestpractical.com>
+Vendor: http://www.fsck.com/projects/rt
+Requires: perl
+Requires: mod_perl > 1.22
+Requires: perl-DBI >= 1.18
+Requires: perl-DBIx-DataSource >= 0.02
+Requires: perl-DBIx-SearchBuilder >= 0.47
+Requires: perl-HTML-Parser
+Requires: perl-MLDBM
+Requires: perl-libnet
+Requires: perl-CGI.pm >= 2.78
+Requires: perl-Params-Validate >= 0.02
+Requires: perl-HTML-Mason >= 0.896
+Requires: perl-libapreq
+Requires: perl-Apache-Session >= 1.53
+Requires: perl-MIME-tools >= 5.411
+Requires: perl-MailTools >= 1.20
+Requires: perl-Getopt-Long >= 2.24
+Requires: perl-Tie-IxHash
+Requires: perl-TimeDate
+Requires: perl-Time-HiRes
+Requires: perl-Text-Wrapper
+Requires: perl-Text-Template
+Requires: perl-File-Spec >= 0.8
+Requires: perl-FreezeThaw
+Requires: perl-Storable
+Requires: perl-File-Temp
+Requires: perl-Log-Dispatch >= 1.6
+
+Source: http://www.fsck.com/pub/rt/release/%{name}.tar.gz
+
+Copyright: GPL
+BuildRoot: /var/tmp/rt-root
+
+%description
+RT is an industrial-grade ticketing system. It lets a group
+of people intelligently and efficiently manage requests
+submitted by a community of users. RT is used by systems
+administrators, customer support staffs, NOCs, developers
+and even marketing departments at over a thousand sites
+around the world.
+
+%prep
+groupadd rt || true
+%setup -q -n %{name}
+
+%build
+
+%install
+
+if [ x$RPM_BUILD_ROOT != x ]; then
+rm -rf $RPM_BUILD_ROOT
+fi
+
+#
+# Perform all the non-site specfic steps whilst building the package
+#
+make dirs libs-install html-install bin-install DESTDIR=$RPM_BUILD_ROOT
+#
+# fixperms needs these, so make fake empty files
+touch $RPM_BUILD_ROOT/opt/rt2/etc/insertdata $RPM_BUILD_ROOT/opt/rt2/etc/config.pm
+make fixperms insert-install WEB_USER=www DESTDIR=$RPM_BUILD_ROOT
+
+#
+# Copy in the files needed again after install
+#
+mkdir -p $RPM_BUILD_ROOT/opt/rt2/postinstall/bin
+cp -rp Makefile etc tools $RPM_BUILD_ROOT/opt/rt2/postinstall
+cp -rp bin/initacls.* $RPM_BUILD_ROOT/opt/rt2/postinstall/bin
+
+# logging in /var/log/rt2
+mkdir -p $RPM_BUILD_ROOT/var/log/rt2
+chown www $RPM_BUILD_ROOT/var/log/rt2
+chgrp rt $RPM_BUILD_ROOT/var/log/rt2
+chmod ug=rwx,o= $RPM_BUILD_ROOT/var/log/rt2
+
+%clean
+if [ x$RPM_BUILD_ROOT != x ]; then
+rm -rf $RPM_BUILD_ROOT
+fi
+
+#
+# A new rt groups is required
+#
+%pre
+groupadd rt || true
+
+#
+# Show the user the site specific steps required after install
+#
+%post
+cat <<EOF
+-----------------------------------------------------------------------
+rt2 installation is complete. Now create the rt2 database by running:
+-----------------------------------------------------------------------
+
+# cd /opt/rt2/postinstall
+# make config-replace initialize.mysql insert RT_LOG_PATH=/var/log/rt2 DB_RT_PASS=new_rt_user_password
+
+Choose your own new_rt_user_password. You will need the mysql root password.
+You can try Pg or Oracle instead of mysql - untested.
+
+Review and configure your site specific details in /opt/rt2/etc/config.pm
+EOF
+
+%preun
+
+%files
+%dir /opt/rt2
+/opt/rt2/bin
+/opt/rt2/WebRT
+/opt/rt2/lib
+/opt/rt2/local
+/opt/rt2/man
+/opt/rt2/postinstall
+%dir /opt/rt2/etc
+/opt/rt2/etc/insertdata
+%config /opt/rt2/etc/config.pm
+%dir /var/log/rt2
+
+%changelog
+* Mon Sep 24 2001 Jesse Vincent <jesse@bestpractical.com>
+ Switch to rt DESTDIR support
+* Fri Sep 14 2001 Cris Bailiff <c.bailiff@devsecure.com>
+ Fix permissions on created /var/log/rt2 and roll in 2.0.7
+* Tue Sep 4 2001 Cris Bailiff <c.bailiff@devsecure.com>
+- created initial spec file
+* Tue Sep 4 2001 Cris Bailiff <c.bailiff@devsecure.com>
+- created initial spec file
+* Tue Sep 4 2001 Cris Bailiff <c.bailiff@devsecure.com>
+- created initial spec file
diff --git a/rt/etc/schema.Informix b/rt/etc/schema.Informix
new file mode 100644
index 0000000..6a4e533
--- /dev/null
+++ b/rt/etc/schema.Informix
@@ -0,0 +1,364 @@
+-- This schema was adopted from the oracle schema by
+-- Andre Koppel.
+-- Version 0.2 Date 2003.10.21
+-- The work is still in progress
+
+CREATE TABLE Attachments (
+ id SERIAL,
+ TransactionId INTEGER NOT NULL,
+ Parent INTEGER DEFAULT 0 NOT NULL,
+ MessageId VARCHAR(160),
+ Subject VARCHAR(255),
+ Filename VARCHAR(255),
+ ContentType VARCHAR(80),
+ ContentEncoding VARCHAR(80),
+ Content BYTE,
+ Headers BYTE,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+CREATE INDEX Attachments1 ON Attachments (Parent);
+CREATE INDEX Attachments2 ON Attachments (TransactionId);
+CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId);
+
+
+CREATE TABLE Queues (
+ id SERIAL,
+ Name VARCHAR(200) DEFAULT '' NOT NULL,
+ Description VARCHAR(255) DEFAULT NULL,
+ CorrespondAddress VARCHAR(120) DEFAULT NULL,
+ CommentAddress VARCHAR(120) DEFAULT NULL,
+ InitialPriority INTEGER DEFAULT 0 NOT NULL,
+ FinalPriority INTEGER DEFAULT 0 NOT NULL,
+ DefaultDueIn INTEGER DEFAULT 0 NOT NULL,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ Disabled INTEGER DEFAULT 0 NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE UNIQUE INDEX Queues1 ON Queues (Name);
+CREATE INDEX Queues2 ON Queues (Disabled);
+
+
+CREATE TABLE Links (
+ id SERIAL,
+ Base VARCHAR(240) DEFAULT NULL,
+ Target VARCHAR(240) DEFAULT NULL,
+ Type VARCHAR(20) DEFAULT '' NOT NULL,
+ LocalTarget INTEGER DEFAULT 0 NOT NULL,
+ LocalBase INTEGER DEFAULT 0 NOT NULL,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+-- CREATE UNIQUE INDEX Links1 ON Links (Base, Target, Type);
+CREATE INDEX Links2 ON Links (Base, Type);
+CREATE INDEX Links3 ON Links (Target, Type);
+CREATE INDEX Links4 ON Links(Type,LocalBase);
+
+
+CREATE TABLE Principals (
+ id SERIAL,
+ PrincipalType VARCHAR(16) DEFAULT '' NOT NULL,
+ ObjectId INTEGER DEFAULT 0,
+ Disabled INTEGER DEFAULT 0 NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE INDEX Principals2 ON Principals (ObjectId);
+
+
+CREATE TABLE Groups (
+ id SERIAL,
+ Name VARCHAR(200) DEFAULT NULL,
+ Description VARCHAR(255) DEFAULT NULL,
+ Domain VARCHAR(64) DEFAULT '',
+ Type VARCHAR(64) DEFAULT '',
+ Instance INTEGER DEFAULT 0 NOT NULL,
+-- Instance VARCHAR(64) DEFAULT '' NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE INDEX Groups1 ON Groups (Domain, Instance, Type, id);
+CREATE INDEX Groups2 ON Groups (Type, Instance, Domain);
+
+
+CREATE TABLE ScripConditions (
+ id SERIAL,
+ Name VARCHAR(200),
+ Description VARCHAR(255),
+ ExecModule VARCHAR(60),
+ Argument VARCHAR(255),
+ ApplicableTransTypes VARCHAR(60),
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE Transactions (
+ id SERIAL,
+ ObjectType VARCHAR(255),
+ ObjectId INTEGER DEFAULT 0 NOT NULL,
+ TimeTaken INTEGER DEFAULT 0 NOT NULL,
+ Type VARCHAR(20),
+ Field VARCHAR(40),
+ OldValue VARCHAR(255),
+ NewValue VARCHAR(255),
+ ReferenceType VARCHAR(255),
+ OldReference INTEGER DEFAULT 0,
+ NewReference INTEGER DEFAULT 0,
+ Data VARCHAR(255),
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+
+
+CREATE TABLE Scrips (
+ id SERIAL,
+ Description VARCHAR(255) DEFAULT '',
+ ScripCondition INTEGER DEFAULT 0 NOT NULL,
+ ScripAction INTEGER DEFAULT 0 NOT NULL,
+ ConditionRules BYTE,
+ ActionRules BYTE,
+ CustomIsApplicableCode BYTE,
+ CustomPrepareCode BYTE,
+ CustomCommitCode BYTE,
+ Stage VARCHAR(32),
+ Queue INTEGER DEFAULT 0 NOT NULL,
+ Template INTEGER DEFAULT 0 NOT NULL,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE ACL (
+ id SERIAL,
+ PrincipalType VARCHAR(25) NOT NULL,
+ PrincipalId INTEGER NOT NULL,
+ RightName VARCHAR(25) NOT NULL,
+ ObjectType VARCHAR(25) NOT NULL,
+ ObjectId INTEGER DEFAULT 0 NOT NULL,
+ DelegatedBy INTEGER DEFAULT 0 NOT NULL,
+ DelegatedFrom INTEGER DEFAULT 0 NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE INDEX ACL1 ON ACL(RightName, ObjectType, ObjectId, PrincipalType, PrincipalId);
+
+
+CREATE TABLE GroupMembers (
+ id SERIAL,
+ GroupId INTEGER DEFAULT 0 NOT NULL,
+ MemberId INTEGER DEFAULT 0 NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE UNIQUE INDEX GroupMembers1 ON GroupMembers (GroupId, MemberId);
+
+
+CREATE TABLE CachedGroupMembers (
+ id SERIAL,
+ GroupId INTEGER DEFAULT 0,
+ MemberId INTEGER DEFAULT 0,
+ Via INTEGER DEFAULT 0,
+ ImmediateParentId INTEGER DEFAULT 0,
+ Disabled INTEGER DEFAULT 0 NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE INDEX DisGrouMem ON CachedGroupMembers (GroupId, MemberId, Disabled);
+CREATE INDEX GrouMem ON CachedGroupMembers (GroupId, MemberId);
+
+
+CREATE TABLE Users (
+ id SERIAL,
+ Name VARCHAR(200) NOT NULL,
+ Password VARCHAR(40),
+ Comments BYTE,
+ Signature BYTE,
+ EmailAddress VARCHAR(120),
+ FreeFormContactInfo BYTE,
+ Organization VARCHAR(200),
+ RealName VARCHAR(120),
+ NickName VARCHAR(16),
+ Lang VARCHAR(16),
+ EmailEncoding VARCHAR(16),
+ WebEncoding VARCHAR(16),
+ ExternalContactInfoId VARCHAR(100),
+ ContactInfoSystem VARCHAR(30),
+ ExternalAuthId VARCHAR(100),
+ AuthSystem VARCHAR(30),
+ Gecos VARCHAR(16),
+ HomePhone VARCHAR(30),
+ WorkPhone VARCHAR(30),
+ MobilePhone VARCHAR(30),
+ PagerPhone VARCHAR(30),
+ Address1 VARCHAR(200),
+ Address2 VARCHAR(200),
+ City VARCHAR(100),
+ State VARCHAR(100),
+ Zip VARCHAR(16),
+ Country VARCHAR(50),
+ Timezone VARCHAR(50),
+ PGPKey BYTE,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+-- CREATE UNIQUE INDEX Users1 ON Users (Name);
+CREATE INDEX Users2 ON Users (Name);
+CREATE INDEX Users3 ON Users (id, EmailAddress);
+CREATE INDEX Users4 ON Users (EmailAddress);
+
+
+CREATE TABLE Tickets (
+ id SERIAL,
+ EffectiveId INTEGER DEFAULT 0 NOT NULL,
+ Queue INTEGER DEFAULT 0 NOT NULL,
+ Type VARCHAR(16),
+ IssueStatement INTEGER DEFAULT 0 NOT NULL,
+ Resolution INTEGER DEFAULT 0 NOT NULL,
+ Owner INTEGER DEFAULT 0 NOT NULL,
+ Subject VARCHAR(200) DEFAULT '[no subject]',
+ InitialPriority INTEGER DEFAULT 0 NOT NULL,
+ FinalPriority INTEGER DEFAULT 0 NOT NULL,
+ Priority INTEGER DEFAULT 0 NOT NULL,
+ TimeEstimated INTEGER DEFAULT 0 NOT NULL,
+ TimeWorked INTEGER DEFAULT 0 NOT NULL,
+ Status VARCHAR(10),
+ TimeLeft INTEGER DEFAULT 0 NOT NULL,
+ Told DATETIME YEAR TO SECOND,
+ Starts DATETIME YEAR TO SECOND,
+ Started DATETIME YEAR TO SECOND,
+ Due DATETIME YEAR TO SECOND,
+ Resolved DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ Disabled INTEGER DEFAULT 0 NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE INDEX Tickets1 ON Tickets (Queue, Status);
+CREATE INDEX Tickets2 ON Tickets (Owner);
+CREATE INDEX Tickets3 ON Tickets (EffectiveId);
+CREATE INDEX Tickets4 ON Tickets (id, Status);
+CREATE INDEX Tickets5 ON Tickets (id, EffectiveId);
+CREATE INDEX Tickets6 ON Tickets (EffectiveId, Type);
+
+
+CREATE TABLE ScripActions (
+ id SERIAL,
+ Name VARCHAR(200),
+ Description VARCHAR(255),
+ ExecModule VARCHAR(60),
+ Argument VARCHAR(255),
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE Templates (
+ id SERIAL,
+ Queue INTEGER DEFAULT 0 NOT NULL,
+ Name VARCHAR(200) NOT NULL,
+ Description VARCHAR(255),
+ Type VARCHAR(16),
+ Language VARCHAR(16),
+ TranslationOf INTEGER DEFAULT 0 NOT NULL,
+ Content BYTE,
+ LastUpdated DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE TicketCustomFieldValues (
+ id SERIAL,
+ Ticket INTEGER NOT NULL,
+ CustomField INTEGER NOT NULL,
+ Content VARCHAR(255),
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX TicketCustomFieldValues1 ON TicketCustomFieldValues (CustomField,Ticket,Content);
+CREATE INDEX TicketCustomFieldValues2 ON TicketCustomFieldValues (CustomField,Ticket);
+
+CREATE TABLE CustomFields (
+ id SERIAL,
+ Name VARCHAR(200),
+ Type VARCHAR(200),
+ MaxValues INTEGER DEFAULT 0 NOT NULL,
+ Pattern VARCHAR(255),
+ LookupType VARCHAR(255),
+ Description VARCHAR(255),
+ SortOrder INTEGER DEFAULT 0 NOT NULL,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ Disabled SMALLINT DEFAULT 0 NOT NULL,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE CustomFieldValues (
+ id SERIAL,
+ CustomField INTEGER NOT NULL,
+ Name VARCHAR(200),
+ Description VARCHAR(255),
+ SortOrder INTEGER DEFAULT 0 NOT NULL,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
+
+CREATE TABLE Attributes (
+ id SERIAL,
+ Name VARCHAR(255) DEFAULT '' NOT NULL,
+ Description VARCHAR(255) DEFAULT NULL,
+ Content BYTE,
+ ContentType VARCHAR(16),
+ ObjectType VARCHAR(25) NOT NULL,
+ ObjectId INTEGER DEFAULT 0 NOT NULL,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+
+CREATE TABLE sessions (
+ id VARCHAR(32) NOT NULL,
+ a_session BYTE,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
diff --git a/rt/etc/schema.Pg b/rt/etc/schema.Pg
new file mode 100755
index 0000000..2d45a94
--- /dev/null
+++ b/rt/etc/schema.Pg
@@ -0,0 +1,636 @@
+------------------------------------------------------------------
+-- My2Pg 1.23 translated dump
+--
+------------------------------------------------------------------
+
+
+
+--
+-- Sequences for table ATTACHMENTS
+--
+
+CREATE SEQUENCE attachments_id_seq;
+
+-- {{{ Attachments
+
+CREATE TABLE Attachments (
+ id INTEGER DEFAULT nextval('attachments_id_seq'),
+ TransactionId integer NOT NULL ,
+ Parent integer NOT NULL DEFAULT 0 ,
+ MessageId varchar(160) NULL ,
+ Subject varchar(255) NULL ,
+ Filename varchar(255) NULL ,
+ ContentType varchar(80) NULL ,
+ ContentEncoding varchar(80) NULL ,
+ Content text NULL ,
+ Headers text NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX Attachments1 ON Attachments (Parent) ;
+CREATE INDEX Attachments2 ON Attachments (TransactionId) ;
+CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId) ;
+-- }}}
+
+-- {{{ Queues
+
+
+--
+-- Sequences for table QUEUES
+--
+
+CREATE SEQUENCE queues_id_seq;
+
+CREATE TABLE Queues (
+ id INTEGER DEFAULT nextval('queues_id_seq'),
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ CorrespondAddress varchar(120) NULL ,
+ CommentAddress varchar(120) NULL ,
+ InitialPriority integer NOT NULL DEFAULT 0 ,
+ FinalPriority integer NOT NULL DEFAULT 0 ,
+ DefaultDueIn integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+
+);
+CREATE UNIQUE INDEX Queues1 ON Queues (Name) ;
+
+-- }}}
+
+-- {{{ Links
+
+
+
+--
+-- Sequences for table LINKS
+--
+
+CREATE SEQUENCE links_id_seq;
+
+CREATE TABLE Links (
+ id INTEGER DEFAULT nextval('links_id_seq'),
+ Base varchar(240) NULL ,
+ Target varchar(240) NULL ,
+ Type varchar(20) NOT NULL ,
+ LocalTarget integer NOT NULL DEFAULT 0 ,
+ LocalBase integer NOT NULL DEFAULT 0 ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+CREATE UNIQUE INDEX Links1 ON Links (Base, Target, Type) ;
+CREATE INDEX Links4 ON Links(Type,LocalBase);
+
+-- }}}
+
+-- {{{ Principals
+
+
+
+--
+-- Sequences for table PRINCIPALS
+--
+
+CREATE SEQUENCE principals_id_seq;
+
+CREATE TABLE Principals (
+ id INTEGER DEFAULT nextval('principals_id_seq') not null,
+ PrincipalType VARCHAR(16) not null,
+ ObjectId integer,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX Principals2 ON Principals (ObjectId);
+
+
+-- }}}
+
+-- {{{ Groups
+
+
+
+--
+-- Sequences for table GROUPS
+--
+
+CREATE SEQUENCE groups_id_seq;
+
+CREATE TABLE Groups (
+ id INTEGER DEFAULT nextval('groups_id_seq'),
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ Domain varchar(64),
+ Type varchar(64),
+ Instance integer,
+ PRIMARY KEY (id)
+
+);
+CREATE UNIQUE INDEX Groups1 ON Groups (Domain,Instance,Type,id, Name);
+CREATE INDEX Groups2 On Groups (Type, Instance, Domain);
+
+
+-- }}}
+
+-- {{{ ScripConditions
+
+
+
+--
+-- Sequences for table SCRIPCONDITIONS
+--
+
+CREATE SEQUENCE scripconditions_id_seq;
+
+CREATE TABLE ScripConditions (
+ id INTEGER DEFAULT nextval('scripconditions_id_seq'),
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ ApplicableTransTypes varchar(60) NULL ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
+-- {{{ Transactions
+
+
+--
+-- Sequences for table TRANSACTIONS
+--
+
+CREATE SEQUENCE transactions_id_seq;
+
+CREATE TABLE Transactions (
+ id INTEGER DEFAULT nextval('transactions_id_seq'),
+ ObjectType varchar(255) NOT NULL ,
+ ObjectId integer NOT NULL DEFAULT 0 ,
+ TimeTaken integer NOT NULL DEFAULT 0 ,
+ Type varchar(20) NULL ,
+ Field varchar(40) NULL ,
+ OldValue varchar(255) NULL ,
+ NewValue varchar(255) NULL ,
+ ReferenceType varchar(255) NULL,
+ OldReference integer NULL ,
+ NewReference integer NULL ,
+ Data varchar(255) NULL ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+
+-- }}}
+
+-- {{{ Scrips
+
+
+
+--
+-- Sequences for table SCRIPS
+--
+
+CREATE SEQUENCE scrips_id_seq;
+
+CREATE TABLE Scrips (
+ id INTEGER DEFAULT nextval('scrips_id_seq'),
+ Description varchar(255),
+ ScripCondition integer NOT NULL DEFAULT 0 ,
+ ScripAction integer NOT NULL DEFAULT 0 ,
+ ConditionRules text NULL ,
+ ActionRules text NULL ,
+ CustomIsApplicableCode text NULL ,
+ CustomPrepareCode text NULL ,
+ CustomCommitCode text NULL ,
+ Stage varchar(32) NULL ,
+ Queue integer NOT NULL DEFAULT 0 ,
+ Template integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
+-- {{{ ACL
+
+
+--
+-- Sequences for table ACL
+--
+
+CREATE SEQUENCE acl_id_seq;
+
+CREATE TABLE ACL (
+ id INTEGER DEFAULT nextval('acl_id_seq'),
+ PrincipalType varchar(25) NOT NULL,
+
+ PrincipalId integer NOT NULL ,
+ RightName varchar(25) NOT NULL ,
+ ObjectType varchar(25) NOT NULL ,
+ ObjectId integer NOT NULL DEFAULT 0,
+ DelegatedBy integer NOT NULL DEFAULT 0,
+ DelegatedFrom integer NOT NULL DEFAULT 0,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX ACL1 on ACL(RightName, ObjectType, ObjectId,PrincipalType,PrincipalId);
+
+
+-- }}}
+
+-- {{{ GroupMembers
+
+
+
+--
+-- Sequences for table GROUPMEMBERS
+--
+
+CREATE SEQUENCE groupmembers_id_seq;
+
+CREATE TABLE GroupMembers (
+ id INTEGER DEFAULT nextval('groupmembers_id_seq'),
+ GroupId integer NOT NULL DEFAULT 0,
+ MemberId integer NOT NULL DEFAULT 0,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
+-- {{{ GroupMembersCache
+
+
+
+--
+-- Sequences for table CACHEDGROUPMEMBERS
+--
+
+CREATE SEQUENCE cachedgroupmembers_id_seq;
+
+CREATE TABLE CachedGroupMembers (
+ id int DEFAULT nextval('cachedgroupmembers_id_seq'),
+ GroupId int,
+ MemberId int,
+ Via int,
+ ImmediateParentId int,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX CachedGroupMembers2 on CachedGroupMembers (MemberId);
+CREATE INDEX CachedGroupMembers3 on CachedGroupMembers (GroupId);
+CREATE INDEX DisGrouMem on CachedGroupMembers (GroupId,MemberId,Disabled);
+
+-- }}}
+
+-- {{{ Users
+
+
+
+--
+-- Sequences for table USERS
+--
+
+CREATE SEQUENCE users_id_seq;
+
+CREATE TABLE Users (
+ id INTEGER DEFAULT nextval('users_id_seq'),
+ Name varchar(200) NOT NULL ,
+ Password varchar(40) NULL ,
+ Comments text NULL ,
+ Signature text NULL ,
+ EmailAddress varchar(120) NULL ,
+ FreeformContactInfo text NULL ,
+ Organization varchar(200) NULL ,
+ RealName varchar(120) NULL ,
+ NickName varchar(16) NULL ,
+ Lang varchar(16) NULL ,
+ EmailEncoding varchar(16) NULL ,
+ WebEncoding varchar(16) NULL ,
+ ExternalContactInfoId varchar(100) NULL ,
+ ContactInfoSystem varchar(30) NULL ,
+ ExternalAuthId varchar(100) NULL ,
+ AuthSystem varchar(30) NULL ,
+ Gecos varchar(16) NULL ,
+ HomePhone varchar(30) NULL ,
+ WorkPhone varchar(30) NULL ,
+ MobilePhone varchar(30) NULL ,
+ PagerPhone varchar(30) NULL ,
+ Address1 varchar(200) NULL ,
+ Address2 varchar(200) NULL ,
+ City varchar(100) NULL ,
+ State varchar(100) NULL ,
+ Zip varchar(16) NULL ,
+ Country varchar(50) NULL ,
+ Timezone varchar(50) NULL ,
+ PGPKey text NULL,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+
+CREATE UNIQUE INDEX Users1 ON Users (Name) ;
+CREATE INDEX Users2 ON Users (Name);
+CREATE INDEX Users3 ON Users (id, EmailAddress);
+CREATE INDEX Users4 ON Users (EmailAddress);
+
+
+-- }}}
+
+-- {{{ Tickets
+
+
+
+--
+-- Sequences for table TICKETS
+--
+
+CREATE SEQUENCE tickets_id_seq;
+
+CREATE TABLE Tickets (
+ id INTEGER DEFAULT nextval('tickets_id_seq'),
+ EffectiveId integer NOT NULL DEFAULT 0 ,
+ Queue integer NOT NULL DEFAULT 0 ,
+ Type varchar(16) NULL ,
+ IssueStatement integer NOT NULL DEFAULT 0 ,
+ Resolution integer NOT NULL DEFAULT 0 ,
+ Owner integer NOT NULL DEFAULT 0 ,
+ Subject varchar(200) NULL DEFAULT '[no subject]' ,
+ InitialPriority integer NOT NULL DEFAULT 0 ,
+ FinalPriority integer NOT NULL DEFAULT 0 ,
+ Priority integer NOT NULL DEFAULT 0 ,
+ TimeEstimated integer NOT NULL DEFAULT 0 ,
+ TimeWorked integer NOT NULL DEFAULT 0 ,
+ Status varchar(10) NULL ,
+ TimeLeft integer NOT NULL DEFAULT 0 ,
+ Told TIMESTAMP NULL ,
+ Starts TIMESTAMP NULL ,
+ Started TIMESTAMP NULL ,
+ Due TIMESTAMP NULL ,
+ Resolved TIMESTAMP NULL ,
+
+
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX Tickets1 ON Tickets (Queue, Status) ;
+CREATE INDEX Tickets2 ON Tickets (Owner) ;
+CREATE INDEX Tickets3 ON Tickets (EffectiveId) ;
+CREATE INDEX Tickets4 ON Tickets (id, Status) ;
+CREATE INDEX Tickets5 ON Tickets (id, EffectiveId) ;
+
+-- }}}
+
+-- {{{ ScripActions
+
+
+
+--
+-- Sequences for table SCRIPACTIONS
+--
+
+CREATE SEQUENCE scripactions_id_seq;
+
+CREATE TABLE ScripActions (
+ id INTEGER DEFAULT nextval('scripactions_id_seq'),
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
+-- {{{ Templates
+
+
+
+--
+-- Sequences for table TEMPLATES
+--
+
+CREATE SEQUENCE templates_id_seq;
+
+CREATE TABLE Templates (
+ id INTEGER DEFAULT nextval('templates_id_seq'),
+ Queue integer NOT NULL DEFAULT 0 ,
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ Type varchar(16) NULL ,
+ Language varchar(16) NULL ,
+ TranslationOf integer NOT NULL DEFAULT 0 ,
+ Content text NULL ,
+ LastUpdated TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
+-- {{{ ObjectCustomFieldValues
+
+
+
+--
+-- Sequences for table TICKETCUSTOMFIELDVALUES
+--
+
+CREATE SEQUENCE objectcustomfieldvalues_id_s;
+
+CREATE TABLE ObjectCustomFieldValues (
+ id INTEGER DEFAULT nextval('objectcustomfieldvalues_id_s'),
+ CustomField int NOT NULL ,
+ ObjectType varchar(255) NULL ,
+ ObjectId int NOT NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Content varchar(255) NULL ,
+ LargeContent text NULL,
+ ContentType varchar(80) NULL,
+ ContentEncoding varchar(80) NULL ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX ObjectCustomFieldValues1 ON ObjectCustomFieldValues (CustomField,ObjectType,ObjectId,Content);
+CREATE INDEX ObjectCustomFieldValues2 ON ObjectCustomFieldValues (CustomField,ObjectType,ObjectId);
+
+-- }}}
+
+-- {{{ CustomFields
+
+
+
+--
+-- Sequences for table CUSTOMFIELDS
+--
+
+CREATE SEQUENCE customfields_id_seq;
+
+CREATE TABLE CustomFields (
+ id INTEGER DEFAULT nextval('customfields_id_seq'),
+ Name varchar(200) NULL ,
+ Type varchar(200) NULL ,
+ MaxValues integer NOT NULL DEFAULT 0 ,
+ Repeated integer NOT NULL DEFAULT 0 ,
+ Pattern varchar(255) NULL ,
+ LookupType varchar(255) NOT NULL ,
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
+-- {{{ ObjectCustomFields
+
+CREATE SEQUENCE objectcustomfields_id_s;
+
+CREATE TABLE ObjectCustomFields (
+ id INTEGER DEFAULT nextval('objectcustomfields_id_s'),
+ CustomField integer NOT NULL,
+ ObjectId integer NOT NULL,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
+-- {{{ CustomFieldValues
+
+
+
+--
+-- Sequences for table CUSTOMFIELDVALUES
+--
+
+CREATE SEQUENCE customfieldvalues_id_seq;
+
+CREATE TABLE CustomFieldValues (
+ id INTEGER DEFAULT nextval('customfieldvalues_id_seq'),
+ CustomField int NOT NULL ,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
+
+-- }}}
+
+
+-- {{{ Attributes
+
+CREATE SEQUENCE attributes_id_seq;
+
+CREATE TABLE Attributes (
+ id INTEGER DEFAULT nextval('attributes_id_seq'),
+ Name varchar(255) NOT NULL ,
+ Description varchar(255) NULL ,
+ Content text,
+ ContentType varchar(16),
+ ObjectType varchar(64),
+ ObjectId integer,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+-- }}}
+
+-- {{{ Sessions
+
+-- sessions is used by Apache::Session to keep sessions in the database.
+-- We should have a reaper script somewhere.
+
+CREATE TABLE sessions (
+ id char(32) NOT NULL,
+ a_session bytea,
+ LastUpdated TIMESTAMP not null default current_timestamp,
+ PRIMARY KEY (id)
+
+);
+
+-- }}}
+
diff --git a/rt/etc/schema.SQLite b/rt/etc/schema.SQLite
new file mode 100644
index 0000000..8791bb4
--- /dev/null
+++ b/rt/etc/schema.SQLite
@@ -0,0 +1,442 @@
+--- {{{ Attachments
+
+CREATE TABLE Attachments (
+ id INTEGER PRIMARY KEY ,
+ TransactionId INTEGER ,
+ Parent integer NULL ,
+ MessageId varchar(160) NULL ,
+ Subject varchar(255) NULL ,
+ Filename varchar(255) NULL ,
+ ContentType varchar(80) NULL ,
+ ContentEncoding varchar(80) NULL ,
+ Content LONGTEXT NULL ,
+ Headers LONGTEXT NULL ,
+ Creator integer NULL ,
+ Created DATETIME NULL
+
+) ;
+
+CREATE INDEX Attachments1 ON Attachments (Parent) ;
+CREATE INDEX Attachments2 ON Attachments (TransactionId) ;
+CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId) ;
+--- }}}
+
+--- {{{ Queues
+CREATE TABLE Queues (
+ id INTEGER PRIMARY KEY ,
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ CorrespondAddress varchar(120) NULL ,
+ CommentAddress varchar(120) NULL ,
+ InitialPriority integer NULL ,
+ FinalPriority integer NULL ,
+ DefaultDueIn integer NULL ,
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0
+
+) ;
+CREATE UNIQUE INDEX Queues1 ON Queues (Name) ;
+
+--- }}}
+
+--- {{{ Links
+
+CREATE TABLE Links (
+ id INTEGER PRIMARY KEY ,
+ Base varchar(240) NULL ,
+ Target varchar(240) NULL ,
+ Type varchar(20) NOT NULL ,
+ LocalTarget integer NULL ,
+ LocalBase integer NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL ,
+ Creator integer NULL ,
+ Created DATETIME NULL
+
+) ;
+CREATE UNIQUE INDEX Links1 ON Links (Base, Target, Type) ;
+CREATE INDEX Links4 ON Links(Type,LocalBase);
+
+--- }}}
+
+--- {{{ Principals
+
+CREATE TABLE Principals (
+ id INTEGER PRIMARY KEY,
+ PrincipalType VARCHAR(16) not null,
+ ObjectId integer,
+ Disabled int2 NOT NULL DEFAULT 0
+
+) ;
+
+--- }}}
+
+--- {{{ Groups
+
+CREATE TABLE Groups (
+ id INTEGER ,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ Domain varchar(64),
+ Type varchar(64),
+ Instance integer
+
+) ;
+
+CREATE UNIQUE INDEX Groups1 ON Groups (Name,Domain,Type,Instance) ;
+
+--- }}}
+
+--- {{{ ScripConditions
+
+CREATE TABLE ScripConditions (
+ id INTEGER PRIMARY KEY ,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ ApplicableTransTypes varchar(60) NULL ,
+
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL
+
+) ;
+
+--- }}}
+
+--- {{{ Transactions
+CREATE TABLE Transactions (
+ id INTEGER PRIMARY KEY ,
+ ObjectType varchar(255) NULL ,
+ ObjectId integer NULL ,
+ TimeTaken integer NULL ,
+ Type varchar(20) NULL ,
+ Field varchar(40) NULL ,
+ OldValue varchar(255) NULL ,
+ NewValue varchar(255) NULL ,
+ ReferenceType varchar(255) NULL ,
+ OldReference integer NULL ,
+ NewReference integer NULL ,
+ Data varchar(255) NULL ,
+
+ Creator integer NULL ,
+ Created DATETIME NULL
+
+) ;
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+
+--- }}}
+
+--- {{{ Scrips
+
+CREATE TABLE Scrips (
+ id INTEGER PRIMARY KEY ,
+ Description varchar(255),
+ ScripCondition integer NULL ,
+ ScripAction integer NULL ,
+ ConditionRules text NULL ,
+ ActionRules text NULL ,
+ CustomIsApplicableCode text NULL ,
+ CustomPrepareCode text NULL ,
+ CustomCommitCode text NULL ,
+ Stage varchar(32) NULL ,
+ Queue integer NULL ,
+ Template integer NULL ,
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL
+
+) ;
+
+--- }}}
+
+--- {{{ ACL
+CREATE TABLE ACL (
+ id INTEGER PRIMARY KEY ,
+ PrincipalType varchar(25) NOT NULL,
+
+ PrincipalId INTEGER,
+ RightName varchar(25) NOT NULL ,
+ ObjectType varchar(25) NOT NULL ,
+ ObjectId INTEGER default 0,
+ DelegatedBy integer NOT NULL default 0,
+ DelegatedFrom integer NOT NULL default 0
+
+) ;
+
+
+--- }}}
+
+--- {{{ GroupMembers
+
+CREATE TABLE GroupMembers (
+ id INTEGER PRIMARY KEY ,
+ GroupId integer NULL,
+ MemberId integer NULL
+
+) ;
+
+--- }}}
+
+--- {{{ CachedGroupMembers
+
+create table CachedGroupMembers (
+ id integer primary key ,
+ GroupId int,
+ MemberId int,
+ Via int,
+ ImmediateParentId int,
+ Disabled int2 NOT NULL DEFAULT 0 # if this cached group member is a member of this group by way of a disabled
+ # group or this group is disabled, this will be set to 1
+ # this allows us to not find members of disabled subgroups when listing off
+ # group members recursively.
+ # Also, this allows us to have the ACL system elide members of disabled groups
+
+
+) ;
+
+--- }}}
+
+--- {{{ Users
+
+CREATE TABLE Users (
+ id INTEGER ,
+ Name varchar(200) NOT NULL ,
+ Password varchar(40) NULL ,
+ Comments blob NULL ,
+ Signature blob NULL ,
+ EmailAddress varchar(120) NULL ,
+ FreeformContactInfo blob NULL ,
+ Organization varchar(200) NULL ,
+ RealName varchar(120) NULL ,
+ NickName varchar(16) NULL ,
+ Lang varchar(16) NULL ,
+ EmailEncoding varchar(16) NULL ,
+ WebEncoding varchar(16) NULL ,
+ ExternalContactInfoId varchar(100) NULL ,
+ ContactInfoSystem varchar(30) NULL ,
+ ExternalAuthId varchar(100) NULL ,
+ AuthSystem varchar(30) NULL ,
+ Gecos varchar(16) NULL ,
+ HomePhone varchar(30) NULL ,
+ WorkPhone varchar(30) NULL ,
+ MobilePhone varchar(30) NULL ,
+ PagerPhone varchar(30) NULL ,
+ Address1 varchar(200) NULL ,
+ Address2 varchar(200) NULL ,
+ City varchar(100) NULL ,
+ State varchar(100) NULL ,
+ Zip varchar(16) NULL ,
+ Country varchar(50) NULL ,
+ Timezone char(50) NULL ,
+ PGPKey text NULL,
+
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL
+
+) ;
+
+
+CREATE UNIQUE INDEX Users1 ON Users (Name) ;
+CREATE INDEX Users2 ON Users (Name);
+CREATE INDEX Users3 ON Users (id, EmailAddress);
+CREATE INDEX Users4 ON Users (EmailAddress);
+
+
+--- }}}
+
+--- {{{ Tickets
+
+CREATE TABLE Tickets (
+ id INTEGER PRIMARY KEY ,
+ EffectiveId integer NULL ,
+ Queue integer NULL ,
+ Type varchar(16) NULL ,
+ IssueStatement integer NULL ,
+ Resolution integer NULL ,
+ Owner integer NULL ,
+ Subject varchar(200) NULL DEFAULT '[no subject]' ,
+ InitialPriority integer NULL ,
+ FinalPriority integer NULL ,
+ Priority integer NULL ,
+ TimeEstimated integer NULL ,
+ TimeWorked integer NULL ,
+ Status varchar(10) NULL ,
+ TimeLeft integer NULL ,
+ Told DATETIME NULL ,
+ Starts DATETIME NULL ,
+ Started DATETIME NULL ,
+ Due DATETIME NULL ,
+ Resolved DATETIME NULL ,
+
+
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL ,
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0
+
+) ;
+
+CREATE INDEX Tickets1 ON Tickets (Queue, Status) ;
+CREATE INDEX Tickets2 ON Tickets (Owner) ;
+CREATE INDEX Tickets3 ON Tickets (EffectiveId) ;
+CREATE INDEX Tickets4 ON Tickets (id, Status) ;
+CREATE INDEX Tickets5 ON Tickets (id, EffectiveId) ;
+
+--- }}}
+
+--- {{{ ScripActions
+
+CREATE TABLE ScripActions (
+ id INTEGER PRIMARY KEY ,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL
+
+) ;
+
+--- }}}
+
+--- {{{ Templates
+
+CREATE TABLE Templates (
+ id INTEGER PRIMARY KEY ,
+ Queue integer NOT NULL DEFAULT 0 ,
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ Type varchar(16) NULL ,
+ Language varchar(16) NULL ,
+ TranslationOf integer NULL ,
+ Content blob NULL ,
+ LastUpdated DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ Creator integer NULL ,
+ Created DATETIME NULL
+
+) ;
+
+--- }}}
+
+
+
+# {{{ ObjectCustomFieldValues
+
+CREATE TABLE ObjectCustomFieldValues (
+ id INTEGER NOT NULL ,
+ CustomField int NOT NULL ,
+ ObjectType varchar(255) NOT NULL, # Final target of the Object
+ ObjectId int NOT NULL , # New -- Replaces Ticket
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Content varchar(255) NULL ,
+ LargeContent LONGTEXT NULL, # New -- to hold 255+ strings
+ ContentType varchar(80) NULL, # New -- only text/* gets searched
+ ContentEncoding varchar(80) NULL , # New -- for binary Content
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX ObjectCustomFieldValues1 ON ObjectCustomFieldValues (Content);
+CREATE INDEX ObjectCustomFieldValues2 ON ObjectCustomFieldValues (CustomField,ObjectType,ObjectId);
+
+# }}}
+
+# {{{ CustomFields
+
+CREATE TABLE CustomFields (
+ id INTEGER NOT NULL ,
+ Name varchar(200) NULL ,
+ Type varchar(200) NULL , # Changed -- 'Single' and 'Multiple' is moved out
+ MaxValues integer, # New -- was 'Single'(1) and 'Multiple'(0)
+ Pattern varchar(255) NULL , # New -- Must validate against this
+ Repeated int2 NOT NULL DEFAULT 0 , # New -- repeated table entry
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+ LookupType varchar(255) NOT NULL,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+) ;
+
+# }}}
+
+# {{{ ObjectCustomFields
+
+CREATE TABLE ObjectCustomFields (
+ id INTEGER NOT NULL ,
+ CustomField int NOT NULL ,
+ ObjectId integer NOT NULL,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+# }}}
+
+# {{{ CustomFieldValues
+
+CREATE TABLE CustomFieldValues (
+ id INTEGER NOT NULL ,
+ CustomField int NOT NULL ,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
+
+# }}}
+
+--- {{{ Attributes
+CREATE TABLE Attributes (
+ id INTEGER PRIMARY KEY ,
+ Name varchar(255) NOT NULL ,
+ Description varchar(255) NULL ,
+ Content LONGTEXT NULL ,
+ ContentType varchar(16),
+ ObjectType varchar(25) NOT NULL ,
+ ObjectId INTEGER default 0,
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL
+
+) ;
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+--- }}}
+
diff --git a/rt/etc/schema.Sybase b/rt/etc/schema.Sybase
new file mode 100644
index 0000000..67a411d
--- /dev/null
+++ b/rt/etc/schema.Sybase
@@ -0,0 +1,444 @@
+# {{{ Attachments
+
+CREATE TABLE rt3.Attachments (
+ id numeric(38,0) identity,
+ TransactionId integer NOT NULL ,
+ Parent integer NOT NULL ,
+ MessageId varchar(160) NULL ,
+ Subject varchar(255) NULL ,
+ Filename varchar(255) NULL ,
+ ContentType varchar(80) NULL ,
+ ContentEncoding varchar(80) NULL ,
+ Content TEXT NULL ,
+ Headers TEXT NULL ,
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX Attachments1 ON Attachments (Parent) ;
+CREATE INDEX Attachments2 ON Attachments (TransactionId) ;
+CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId) ;
+# }}}
+
+# {{{ Queues
+CREATE TABLE rt3.Queues (
+ id numeric(38,0) identity,
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ CorrespondAddress varchar(120) NULL ,
+ CommentAddress varchar(120) NULL ,
+ InitialPriority integer NOT NULL ,
+ FinalPriority integer NOT NULL ,
+ DefaultDueIn integer NOT NULL ,
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ Disabled numeric(1) NOT NULL ,
+ PRIMARY KEY (id)
+) ;
+CREATE UNIQUE INDEX Queues1 ON Queues (Name) ;
+CREATE INDEX Queues2 ON Queues (Disabled) ;
+
+# }}}
+
+# {{{ Links
+
+CREATE TABLE rt3.Links (
+ id numeric(38,0) identity,
+ Base varchar(240) NULL ,
+ Target varchar(240) NULL ,
+ Type varchar(20) NOT NULL ,
+ LocalTarget integer NOT NULL ,
+ LocalBase integer NOT NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE UNIQUE INDEX Links1 ON Links (Base, Target, Type) ;
+CREATE INDEX Links2 ON Links (Base, Type) ;
+CREATE INDEX Links3 ON Links (Target, Type) ;
+CREATE INDEX Links4 ON Links(Type,LocalBase);
+
+# }}}
+
+# {{{ Principals
+
+CREATE TABLE rt3.Principals (
+ id numeric(38,0) identity,
+ PrincipalType VARCHAR(16) not null,
+ ObjectId integer, Disabled numeric(1) NOT NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX Principals2 ON Principals (ObjectId);
+
+# }}}
+
+# {{{ Groups
+
+CREATE TABLE rt3.Groups (
+ id numeric(38,0) identity,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ Domain varchar(64),
+ Type varchar(64),
+ Instance integer,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX Groups1 ON Groups (Domain,Instance,Type,id);
+CREATE INDEX Groups2 On Groups (Type, Instance, Domain);
+
+# }}}
+
+# {{{ ScripConditions
+
+CREATE TABLE rt3.ScripConditions (
+ id numeric(38,0) identity,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ ApplicableTransTypes varchar(60) NULL ,
+
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+# }}}
+
+# {{{ Transactions
+CREATE TABLE rt3.Transactions (
+ id numeric(38,0) identity,
+ EffectiveTicket integer NOT NULL ,
+ Ticket integer NOT NULL ,
+ TimeTaken integer NOT NULL ,
+ Type varchar(20) NULL ,
+ Field varchar(40) NULL ,
+ OldValue varchar(255) NULL ,
+ NewValue varchar(255) NULL ,
+ Data varchar(255) NULL ,
+
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+CREATE INDEX Transactions1 ON Transactions (Ticket);
+CREATE INDEX Transactions2 ON Transactions (EffectiveTicket);
+
+# }}}
+
+# {{{ Scrips
+
+CREATE TABLE rt3.Scrips (
+ id numeric(38,0) identity,
+ Description varchar(255),
+ ScripCondition integer NOT NULL ,
+ ScripAction integer NOT NULL ,
+ ConditionRules text NULL ,
+ ActionRules text NULL ,
+ CustomIsApplicableCode text NULL ,
+ CustomPrepareCode text NULL ,
+ CustomCommitCode text NULL ,
+ Stage varchar(32) NULL ,
+ Queue integer NOT NULL ,
+ Template integer NOT NULL ,
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+# }}}
+
+# {{{ ACL
+CREATE TABLE rt3.ACL (
+ id numeric(38,0) identity,
+ PrincipalType varchar(25) NOT NULL, #"User" "Group", "Owner", "Cc" "AdminCc", "Requestor", "Requestor"
+
+ PrincipalId integer NOT NULL , #Foreign key to principals
+ RightName varchar(25) NOT NULL ,
+ ObjectType varchar(25) NOT NULL ,
+ ObjectId integer NOT NULL ,
+ DelegatedBy integer NOT NULL , #foreign key to principals with a userid
+ DelegatedFrom integer NOT NULL , #foreign key to ACL
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX ACL1 on ACL(RightName, ObjectType, ObjectId,PrincipalType,PrincipalId);
+
+# }}}
+
+# {{{ GroupMembers
+
+CREATE TABLE rt3.GroupMembers (
+ id numeric(38,0) identity,
+ GroupId integer NOT NULL ,
+ MemberId integer NOT NULL , #Foreign key to Principals
+ PRIMARY KEY (id)
+) ;
+CREATE UNIQUE INDEX GroupMembers1 on GroupMembers (GroupId, MemberId);
+
+
+# }}}
+
+# {{{ GroupMembersCache
+
+CREATE TABLE rt3.CachedGroupMembers (
+ id numeric(38,0) identity,
+ GroupId int, # foreign key to Principals
+ MemberId int, # foreign key to Principals
+ Via int, #foreign key to CachedGroupMembers. (may point to $self->id)
+ ImmediateParentId int, #foreign key to prinicpals.
+ # this points to the group that the member is
+ # a member of, for ease of deletes.
+ Disabled numeric(1) NOT NULL , # if this cached group member is a member of this group by way of a disabled
+ # group or this group is disabled, this will be set to 1
+ # this allows us to not find members of disabled subgroups when listing off
+ # group members recursively.
+ # Also, this allows us to have the ACL system elide members of disabled groups
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX DisGrouMem on CachedGroupMembers (GroupId,MemberId,Disabled);
+CREATE INDEX GrouMem on CachedGroupMembers (GroupId,MemberId);
+
+# }}}
+
+# {{{ Users
+
+CREATE TABLE rt3.Users (
+ id numeric(38,0) identity,
+ Name varchar(200) NOT NULL ,
+ Password varchar(40) NULL ,
+ Comments text NULL ,
+ Signature text NULL ,
+ EmailAddress varchar(120) NULL ,
+ FreeformContactInfo text NULL ,
+ Organization varchar(200) NULL ,
+ RealName varchar(120) NULL ,
+ NickName varchar(16) NULL ,
+ Lang varchar(16) NULL ,
+ EmailEncoding varchar(16) NULL ,
+ WebEncoding varchar(16) NULL ,
+ ExternalContactInfoId varchar(100) NULL ,
+ ContactInfoSystem varchar(30) NULL ,
+ ExternalAuthId varchar(100) NULL ,
+ AuthSystem varchar(30) NULL ,
+ Gecos varchar(16) NULL ,
+ HomePhone varchar(30) NULL ,
+ WorkPhone varchar(30) NULL ,
+ MobilePhone varchar(30) NULL ,
+ PagerPhone varchar(30) NULL ,
+ Address1 varchar(200) NULL ,
+ Address2 varchar(200) NULL ,
+ City varchar(100) NULL ,
+ State varchar(100) NULL ,
+ Zip varchar(16) NULL ,
+ Country varchar(50) NULL ,
+ Timezone varchar(50) NULL ,
+ PGPKey text NULL,
+
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+
+CREATE UNIQUE INDEX Users1 ON Users (Name) ;
+CREATE INDEX Users2 ON Users (Name);
+CREATE INDEX Users3 ON Users (id, EmailAddress);
+CREATE INDEX Users4 ON Users (EmailAddress);
+
+
+# }}}
+
+# {{{ Tickets
+
+CREATE TABLE rt3.Tickets (
+ id numeric(38,0) identity,
+ EffectiveId integer NOT NULL ,
+ Queue integer NOT NULL ,
+ Type varchar(16) NULL ,
+ IssueStatement integer NOT NULL ,
+ Resolution integer NOT NULL ,
+ Owner integer NOT NULL ,
+ Subject varchar(200) NULL,
+ InitialPriority integer NOT NULL ,
+ FinalPriority integer NOT NULL ,
+ Priority integer NOT NULL ,
+ TimeEstimated integer NOT NULL ,
+ TimeWorked integer NOT NULL ,
+ Status varchar(10) NULL ,
+ TimeLeft integer NOT NULL ,
+ Told DATETIME NULL ,
+ Starts DATETIME NULL ,
+ Started DATETIME NULL ,
+ Due DATETIME NULL ,
+ Resolved DATETIME NULL ,
+
+
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ Disabled numeric(1) NOT NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX Tickets1 ON Tickets (Queue, Status) ;
+CREATE INDEX Tickets2 ON Tickets (Owner) ;
+CREATE INDEX Tickets3 ON Tickets (EffectiveId) ;
+CREATE INDEX Tickets4 ON Tickets (id, Status) ;
+CREATE INDEX Tickets5 ON Tickets (id, EffectiveId) ;
+CREATE INDEX Tickets6 ON Tickets (EffectiveId, Type) ;
+
+# }}}
+
+# {{{ ScripActions
+
+CREATE TABLE rt3.ScripActions (
+ id numeric(38,0) identity,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+# }}}
+
+# {{{ Templates
+
+CREATE TABLE rt3.Templates (
+ id numeric(38,0) identity,
+ Queue integer NOT NULL ,
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ Type varchar(16) NULL ,
+ Language varchar(16) NULL ,
+ TranslationOf integer NOT NULL ,
+ Content text NULL ,
+ LastUpdated DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+# }}}
+
+# {{{ TicketCustomFieldValues
+
+CREATE TABLE rt3.TicketCustomFieldValues (
+ id numeric(38,0) identity,
+ Ticket int NOT NULL ,
+ CustomField int NOT NULL ,
+ Content varchar(255) NULL ,
+
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX TicketCustomFieldValues1 ON TicketCustomFieldValues (CustomField,Ticket,Content);
+CREATE INDEX TicketCustomFieldValues2 ON TicketCustomFieldValues (CustomField,Ticket);
+
+# }}}
+
+# {{{ CustomFields
+
+CREATE TABLE rt3.CustomFields (
+ id numeric(38,0) identity,
+ Name varchar(200) NULL ,
+ Type varchar(200) NULL ,
+ Queue integer NOT NULL ,
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL ,
+
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ Disabled numeric(1) NOT NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX CustomFields1 on CustomFields (Disabled, Queue);
+
+
+# }}}
+
+# {{{ CustomFieldValues
+
+CREATE TABLE rt3.CustomFieldValues (
+ id numeric(38,0) identity,
+ CustomField int NOT NULL ,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL ,
+
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
+
+# }}}
+
+
+# {{{ Attributes
+
+CREATE TABLE rt3.Attributes (
+ id numeric(38,0) identity,
+ Name varchar(255) NULL ,
+ Description varchar(255) NULL ,
+ Content text,
+ ContentType varchar(16),
+ ObjectType varchar(64),
+ ObjectId integer, # foreign key to anything
+ Creator integer NOT NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) ;
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+# }}}
+
+# {{{ Sessions
+
+# sessions is used by Apache::Session to keep sessions in the database.
+# We should have a reaper script somewhere.
+
+CREATE TABLE rt3.sessions (
+ id char(32) NOT NULL,
+ a_session TEXT,
+ LastUpdated DATETIME,
+ PRIMARY KEY (id)
+);
+
+# }}}
diff --git a/rt/etc/schema.mysql b/rt/etc/schema.mysql
new file mode 100755
index 0000000..b7d53f8
--- /dev/null
+++ b/rt/etc/schema.mysql
@@ -0,0 +1,463 @@
+# {{{ Attachments
+
+CREATE TABLE Attachments (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ TransactionId integer NOT NULL ,
+ Parent integer NOT NULL DEFAULT 0 ,
+ MessageId varchar(160) NULL ,
+ Subject varchar(255) NULL ,
+ Filename varchar(255) NULL ,
+ ContentType varchar(80) NULL ,
+ ContentEncoding varchar(80) NULL ,
+ Content LONGTEXT NULL ,
+ Headers LONGTEXT NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX Attachments2 ON Attachments (TransactionId) ;
+CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId) ;
+# }}}
+
+# {{{ Queues
+CREATE TABLE Queues (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ CorrespondAddress varchar(120) NULL ,
+ CommentAddress varchar(120) NULL ,
+ InitialPriority integer NOT NULL DEFAULT 0 ,
+ FinalPriority integer NOT NULL DEFAULT 0 ,
+ DefaultDueIn integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+CREATE UNIQUE INDEX Queues1 ON Queues (Name) ;
+CREATE INDEX Queues2 ON Queues (Disabled) ;
+
+# }}}
+
+# {{{ Links
+
+CREATE TABLE Links (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Base varchar(240) NULL ,
+ Target varchar(240) NULL ,
+ Type varchar(20) NOT NULL ,
+ LocalTarget integer NOT NULL DEFAULT 0 ,
+ LocalBase integer NOT NULL DEFAULT 0 ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX Links2 ON Links (Base, Type) ;
+CREATE INDEX Links3 ON Links (Target, Type) ;
+CREATE INDEX Links4 ON Links (Type,LocalBase);
+
+# }}}
+
+# {{{ Principals
+
+CREATE TABLE Principals (
+ id INTEGER AUTO_INCREMENT not null,
+ PrincipalType VARCHAR(16) not null,
+ ObjectId integer, # foreign key to Users or Groups, depending
+ Disabled int2 NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX Principals2 ON Principals (ObjectId);
+
+# }}}
+
+# {{{ Groups
+
+CREATE TABLE Groups (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ Domain varchar(64),
+ Type varchar(64),
+ Instance integer,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX Groups1 ON Groups (Domain,Instance,Type,id);
+CREATE INDEX Groups2 On Groups (Type, Instance);
+
+# }}}
+
+# {{{ ScripConditions
+
+CREATE TABLE ScripConditions (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ ApplicableTransTypes varchar(60) NULL ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+# }}}
+
+# {{{ Transactions
+CREATE TABLE Transactions (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ ObjectType varchar(64) NOT NULL,
+ ObjectId integer NOT NULL DEFAULT 0 ,
+ TimeTaken integer NOT NULL DEFAULT 0 ,
+ Type varchar(20) NULL ,
+ Field varchar(40) NULL ,
+ OldValue varchar(255) NULL ,
+ NewValue varchar(255) NULL ,
+ ReferenceType varchar(255) NULL,
+ OldReference integer NULL ,
+ NewReference integer NULL ,
+ Data varchar(255) NULL ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+
+# }}}
+
+# {{{ Scrips
+
+CREATE TABLE Scrips (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Description varchar(255),
+ ScripCondition integer NOT NULL DEFAULT 0 ,
+ ScripAction integer NOT NULL DEFAULT 0 ,
+ ConditionRules text NULL ,
+ ActionRules text NULL ,
+ CustomIsApplicableCode text NULL ,
+ CustomPrepareCode text NULL ,
+ CustomCommitCode text NULL ,
+ Stage varchar(32) NULL ,
+ Queue integer NOT NULL DEFAULT 0 ,
+ Template integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+# }}}
+
+# {{{ ACL
+CREATE TABLE ACL (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ PrincipalType varchar(25) NOT NULL, #"User" "Group", "Owner", "Cc" "AdminCc", "Requestor", "Requestor"
+
+ PrincipalId integer NOT NULL , #Foreign key to principals
+ RightName varchar(25) NOT NULL ,
+ ObjectType varchar(25) NOT NULL ,
+ ObjectId integer NOT NULL default 0,
+ DelegatedBy integer NOT NULL default 0, #foreign key to principals with a userid
+ DelegatedFrom integer NOT NULL default 0, #foreign key to ACL
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX ACL1 on ACL(RightName, ObjectType, ObjectId,PrincipalType,PrincipalId);
+
+# }}}
+
+# {{{ GroupMembers
+
+CREATE TABLE GroupMembers (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ GroupId integer NOT NULL DEFAULT 0,
+ MemberId integer NOT NULL DEFAULT 0, #Foreign key to Principals
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+CREATE UNIQUE INDEX GroupMembers1 on GroupMembers (GroupId, MemberId);
+
+
+# }}}
+
+# {{{ GroupMembersCache
+
+create table CachedGroupMembers (
+ id int auto_increment,
+ GroupId int, # foreign key to Principals
+ MemberId int, # foreign key to Principals
+ Via int, #foreign key to CachedGroupMembers. (may point to $self->id)
+ ImmediateParentId int, #foreign key to prinicpals.
+ # this points to the group that the member is
+ # a member of, for ease of deletes.
+ Disabled int2 NOT NULL DEFAULT 0 , # if this cached group member is a member of this group by way of a disabled
+ # group or this group is disabled, this will be set to 1
+ # this allows us to not find members of disabled subgroups when listing off
+ # group members recursively.
+ # Also, this allows us to have the ACL system elide members of disabled groups
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX DisGrouMem on CachedGroupMembers (GroupId,MemberId,Disabled);
+
+# }}}
+
+# {{{ Users
+
+CREATE TABLE Users (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(200) NOT NULL ,
+ Password varchar(40) NULL ,
+ Comments blob NULL ,
+ Signature blob NULL ,
+ EmailAddress varchar(120) NULL ,
+ FreeformContactInfo blob NULL ,
+ Organization varchar(200) NULL ,
+ RealName varchar(120) NULL ,
+ NickName varchar(16) NULL ,
+ Lang varchar(16) NULL ,
+ EmailEncoding varchar(16) NULL ,
+ WebEncoding varchar(16) NULL ,
+ ExternalContactInfoId varchar(100) NULL ,
+ ContactInfoSystem varchar(30) NULL ,
+ ExternalAuthId varchar(100) NULL ,
+ AuthSystem varchar(30) NULL ,
+ Gecos varchar(16) NULL ,
+ HomePhone varchar(30) NULL ,
+ WorkPhone varchar(30) NULL ,
+ MobilePhone varchar(30) NULL ,
+ PagerPhone varchar(30) NULL ,
+ Address1 varchar(200) NULL ,
+ Address2 varchar(200) NULL ,
+ City varchar(100) NULL ,
+ State varchar(100) NULL ,
+ Zip varchar(16) NULL ,
+ Country varchar(50) NULL ,
+ Timezone varchar(50) NULL ,
+ PGPKey text NULL,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+
+CREATE UNIQUE INDEX Users1 ON Users (Name) ;
+CREATE INDEX Users4 ON Users (EmailAddress);
+
+
+# }}}
+
+# {{{ Tickets
+
+CREATE TABLE Tickets (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ EffectiveId integer NOT NULL DEFAULT 0 ,
+ Queue integer NOT NULL DEFAULT 0 ,
+ Type varchar(16) NULL ,
+ IssueStatement integer NOT NULL DEFAULT 0 ,
+ Resolution integer NOT NULL DEFAULT 0 ,
+ Owner integer NOT NULL DEFAULT 0 ,
+ Subject varchar(200) NULL DEFAULT '[no subject]' ,
+ InitialPriority integer NOT NULL DEFAULT 0 ,
+ FinalPriority integer NOT NULL DEFAULT 0 ,
+ Priority integer NOT NULL DEFAULT 0 ,
+ TimeEstimated integer NOT NULL DEFAULT 0 ,
+ TimeWorked integer NOT NULL DEFAULT 0 ,
+ Status varchar(10) NULL ,
+ TimeLeft integer NOT NULL DEFAULT 0 ,
+ Told DATETIME NULL ,
+ Starts DATETIME NULL ,
+ Started DATETIME NULL ,
+ Due DATETIME NULL ,
+ Resolved DATETIME NULL ,
+
+
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX Tickets1 ON Tickets (Queue, Status) ;
+CREATE INDEX Tickets2 ON Tickets (Owner) ;
+CREATE INDEX Tickets6 ON Tickets (EffectiveId, Type) ;
+
+# }}}
+
+# {{{ ScripActions
+
+CREATE TABLE ScripActions (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ ExecModule varchar(60) NULL ,
+ Argument varchar(255) NULL ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+# }}}
+
+# {{{ Templates
+
+CREATE TABLE Templates (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Queue integer NOT NULL DEFAULT 0 ,
+ Name varchar(200) NOT NULL ,
+ Description varchar(255) NULL ,
+ Type varchar(16) NULL ,
+ Language varchar(16) NULL ,
+ TranslationOf integer NOT NULL DEFAULT 0 ,
+ Content blob NULL ,
+ LastUpdated DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+# }}}
+
+# {{{ ObjectCustomFieldValues
+
+CREATE TABLE ObjectCustomFieldValues (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ CustomField int NOT NULL ,
+ ObjectType varchar(255) NOT NULL, # Final target of the Object
+ ObjectId int NOT NULL , # New -- Replaces Ticket
+ SortOrder integer NOT NULL DEFAULT 0 , # New -- ordering for multiple values
+
+ Content varchar(255) NULL ,
+ LargeContent LONGTEXT NULL, # New -- to hold 255+ strings
+ ContentType varchar(80) NULL, # New -- only text/* gets searched
+ ContentEncoding varchar(80) NULL , # New -- for binary Content
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0 , # New -- whether the value was current
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX ObjectCustomFieldValues1 ON ObjectCustomFieldValues (Content);
+CREATE INDEX ObjectCustomFieldValues2 ON ObjectCustomFieldValues (CustomField,ObjectType,ObjectId);
+
+# }}}
+
+# {{{ CustomFields
+
+CREATE TABLE CustomFields (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(200) NULL ,
+ Type varchar(200) NULL , # Changed -- 'Single' and 'Multiple' is moved out
+ MaxValues integer, # New -- was 'Single'(1) and 'Multiple'(0)
+ Pattern varchar(255) NULL , # New -- Must validate against this
+ Repeated int2 NOT NULL DEFAULT 0 , # New -- repeated table entry
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+ LookupType varchar(255) NOT NULL,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ Disabled int2 NOT NULL DEFAULT 0 ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+# }}}
+
+# {{{ ObjectCustomFields
+
+CREATE TABLE ObjectCustomFields (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ CustomField int NOT NULL ,
+ ObjectId integer NOT NULL,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+# }}}
+
+# {{{ CustomFieldValues
+
+CREATE TABLE CustomFieldValues (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ CustomField int NOT NULL ,
+ Name varchar(200) NULL ,
+ Description varchar(255) NULL ,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
+
+# }}}
+
+
+# {{{ Attributes
+
+CREATE TABLE Attributes (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(255) NULL ,
+ Description varchar(255) NULL ,
+ Content text,
+ ContentType varchar(16),
+ ObjectType varchar(64),
+ ObjectId integer, # foreign key to anything
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+# }}}
+
+# {{{ Sessions
+
+# sessions is used by Apache::Session to keep sessions in the database.
+# We should have a reaper script somewhere.
+
+CREATE TABLE sessions (
+ id char(32) NOT NULL,
+ a_session LONGTEXT,
+ LastUpdated TIMESTAMP,
+ PRIMARY KEY (id)
+);
+
+# }}}
diff --git a/rt/etc/upgrade/3.1.0/acl.Informix b/rt/etc/upgrade/3.1.0/acl.Informix
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/acl.Informix
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.1.0/acl.Oracle b/rt/etc/upgrade/3.1.0/acl.Oracle
new file mode 100755
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/acl.Oracle
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.1.0/acl.Pg b/rt/etc/upgrade/3.1.0/acl.Pg
new file mode 100755
index 0000000..809e99a
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/acl.Pg
@@ -0,0 +1,19 @@
+sub acl {
+ my $dbh = shift;
+
+ my @acls;
+
+ my @tables = qw (
+ attributes_id_seq
+ attributes
+ );
+
+ foreach my $table (@tables) {
+ push @acls,
+ "GRANT SELECT, INSERT, UPDATE, DELETE ON $table to "
+ . $RT::DatabaseUser . ";";
+
+ }
+ return (@acls);
+}
+1;
diff --git a/rt/etc/upgrade/3.1.0/acl.SQLite b/rt/etc/upgrade/3.1.0/acl.SQLite
new file mode 100755
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/acl.SQLite
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.1.0/acl.mysql b/rt/etc/upgrade/3.1.0/acl.mysql
new file mode 100755
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/acl.mysql
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.1.0/content b/rt/etc/upgrade/3.1.0/content
new file mode 100644
index 0000000..3117daf
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/content
@@ -0,0 +1,2 @@
+# nothing to do
+1;
diff --git a/rt/etc/upgrade/3.1.0/schema.Informix b/rt/etc/upgrade/3.1.0/schema.Informix
new file mode 100644
index 0000000..722eb70
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/schema.Informix
@@ -0,0 +1,17 @@
+CREATE TABLE Attributes (
+ id SERIAL,
+ Name VARCHAR(255) DEFAULT '' NOT NULL,
+ Description VARCHAR(255) DEFAULT NULL,
+ Content BYTE,
+ ContentType VARCHAR(16),
+ ObjectType VARCHAR(25) NOT NULL,
+ ObjectId INTEGER DEFAULT 0 NOT NULL,
+ Creator INTEGER DEFAULT 0 NOT NULL,
+ Created DATETIME YEAR TO SECOND,
+ LastUpdatedBy INTEGER DEFAULT 0 NOT NULL,
+ LastUpdated DATETIME YEAR TO SECOND,
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
diff --git a/rt/etc/upgrade/3.1.0/schema.Oracle b/rt/etc/upgrade/3.1.0/schema.Oracle
new file mode 100644
index 0000000..a8aae18
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/schema.Oracle
@@ -0,0 +1,17 @@
+CREATE SEQUENCE ATTRIBUTES_seq;
+CREATE TABLE Attributes (
+ id NUMBER(11,0) PRIMARY KEY,
+ Name VARCHAR2(255) NOT NULL,
+ Description VARCHAR2(255),
+ Content CLOB,
+ ContentType VARCHAR(16),
+ ObjectType VARCHAR2(25) NOT NULL,
+ ObjectId NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
diff --git a/rt/etc/upgrade/3.1.0/schema.Pg b/rt/etc/upgrade/3.1.0/schema.Pg
new file mode 100755
index 0000000..94c3fe7
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/schema.Pg
@@ -0,0 +1,25 @@
+-- {{{ Attributes
+
+CREATE SEQUENCE attributes_id_seq;
+
+CREATE TABLE Attributes (
+ id INTEGER DEFAULT nextval('attributes_id_seq'),
+ Name varchar(255) NOT NULL ,
+ Description varchar(255) NULL ,
+ Content text,
+ ContentType varchar(16),
+ ObjectType varchar(64),
+ ObjectId integer,
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+-- }}}
+
diff --git a/rt/etc/upgrade/3.1.0/schema.SQLite b/rt/etc/upgrade/3.1.0/schema.SQLite
new file mode 100644
index 0000000..1dd466f
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/schema.SQLite
@@ -0,0 +1,21 @@
+--- {{{ Attributes
+CREATE TABLE Attributes (
+ id INTEGER PRIMARY KEY ,
+ Name varchar(255) NOT NULL ,
+ Description varchar(255) NULL ,
+ Content LONGTEXT NULL ,
+ ContentType varchar(16),
+ ObjectType varchar(25) NOT NULL ,
+ ObjectId INTEGER default 0,
+ Creator integer NULL ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NULL ,
+ LastUpdated DATETIME NULL
+
+) ;
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+--- }}}
+
diff --git a/rt/etc/upgrade/3.1.0/schema.mysql b/rt/etc/upgrade/3.1.0/schema.mysql
new file mode 100755
index 0000000..c4a345d
--- /dev/null
+++ b/rt/etc/upgrade/3.1.0/schema.mysql
@@ -0,0 +1,21 @@
+# {{{ Attributes
+
+CREATE TABLE Attributes (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(255) NULL ,
+ Description varchar(255) NULL ,
+ Content text,
+ ContentType varchar(16),
+ ObjectType varchar(64),
+ ObjectId integer, # foreign key to anything
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+CREATE INDEX Attributes1 on Attributes(Name);
+CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
+
+# }}}
diff --git a/rt/etc/upgrade/3.1.15/content b/rt/etc/upgrade/3.1.15/content
new file mode 100644
index 0000000..d23125a
--- /dev/null
+++ b/rt/etc/upgrade/3.1.15/content
@@ -0,0 +1,7 @@
+@Scrips = (
+ { ScripCondition => 'On Owner Change',
+ ScripAction => 'Notify Owner',
+ Template => 'Transaction' },
+);
+
+1;
diff --git a/rt/etc/upgrade/3.1.17/content b/rt/etc/upgrade/3.1.17/content
new file mode 100644
index 0000000..1d648d8
--- /dev/null
+++ b/rt/etc/upgrade/3.1.17/content
@@ -0,0 +1,22 @@
+@ScripActions = (
+ { Name => 'Notify Ccs as Comment', # loc
+ Description => 'Sends mail to the Ccs as a comment', # loc
+ ExecModule => 'NotifyAsComment',
+ Argument => 'Cc' },
+ { Name => 'Notify Ccs', # loc
+ Description => 'Sends mail to the Ccs', # loc
+ ExecModule => 'Notify',
+ Argument => 'Cc' },
+);
+
+
+@ScripConditions = (
+ {
+ Name => 'On Priority Change', # loc
+ Description => 'Whenever a ticket\'s priority changes', # loc
+ ApplicableTransTypes => 'Set',
+ ExecModule => 'PriorityChange',
+ },
+);
+
+1;
diff --git a/rt/etc/upgrade/3.3.0/acl.Informix b/rt/etc/upgrade/3.3.0/acl.Informix
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/acl.Informix
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.0/acl.Oracle b/rt/etc/upgrade/3.3.0/acl.Oracle
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/acl.Oracle
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.0/acl.Pg b/rt/etc/upgrade/3.3.0/acl.Pg
new file mode 100644
index 0000000..2069a19
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/acl.Pg
@@ -0,0 +1,20 @@
+sub acl {
+ my $dbh = shift;
+
+ my @acls;
+
+ my @tables = qw (
+ objectcustomfieldvalues
+ objectcustomfields_id_s
+ objectcustomfields
+ );
+
+ foreach my $table (@tables) {
+ push @acls,
+ "GRANT SELECT, INSERT, UPDATE, DELETE ON $table to "
+ . $RT::DatabaseUser . ";";
+
+ }
+ return (@acls);
+}
+1;
diff --git a/rt/etc/upgrade/3.3.0/acl.SQLite b/rt/etc/upgrade/3.3.0/acl.SQLite
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/acl.SQLite
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.0/acl.mysql b/rt/etc/upgrade/3.3.0/acl.mysql
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/acl.mysql
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.0/content b/rt/etc/upgrade/3.3.0/content
new file mode 100644
index 0000000..0afc604
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/content
@@ -0,0 +1 @@
+1;
diff --git a/rt/etc/upgrade/3.3.0/schema.Oracle b/rt/etc/upgrade/3.3.0/schema.Oracle
new file mode 100644
index 0000000..f81feeb
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/schema.Oracle
@@ -0,0 +1,65 @@
+alter Table Transactions ADD ObjectType VARCHAR2(64);
+UPDATE Transactions set ObjectType = 'RT::Ticket';
+ALTER TABLE Transactions modify ObjectType NOT NULL;
+ALTER TABLE Transactions drop column EffectiveTicket;
+ALTER TABLE Transactions ADD ReferenceType VARCHAR2(255) NULL;
+ALTER TABLE Transactions ADD OldReference NUMBER(11,0) NULL;
+ALTER TABLE Transactions ADD NewReference NUMBER(11,0) NULL;
+DROP INDEX transactions1;
+ALTER TABLE Transactions rename column Ticket to ObjectId;
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+
+ALTER TABLE TicketCustomFieldValues rename to ObjectCustomFieldValues;
+ALTER TABLE ObjectCustomFieldValues rename column Ticket to ObjectId;
+ALTER TABLE ObjectCustomFieldValues ADD ObjectType VARCHAR2(255);
+UPDATE ObjectCustomFieldValues set ObjectType = 'RT::Ticket';
+ALTER TABLE ObjectCustomFieldValues MODIFY ObjectType NOT NULL;
+ALTER TABLE ObjectCustomFieldValues ADD Disabled NUMBER(11,0);
+ALTER TABLE ObjectCustomFieldValues MODIFY Disabled default 0;
+UPDATE ObjectCustomFieldValues SET Disabled = 0;
+ALTER TABLE ObjectCustomFieldValues MODIFY Disabled NOT NULL;
+ALTER TABLE ObjectCustomFieldValues ADD LargeContent CLOB NULL;
+ALTER TABLE ObjectCustomFieldValues ADD ContentType VARCHAR2(80) NULL;
+ALTER TABLE ObjectCustomFieldValues ADD ContentEncoding VARCHAR2(80) NULL;
+ALTER TABLE ObjectCustomFieldValues ADD SortOrder NUMBER(11,0) DEFAULT 0 NOT NULL;
+
+
+
+CREATE INDEX ObjectCustomFieldValues1 on ObjectCustomFieldValues (CustomField,ObjectType,ObjectId,Content);
+CREATE INDEX ObjectCustomFieldValues2 on ObjectCustomFieldValues (CustomField,ObjectType,ObjectId);
+
+
+
+CREATE SEQUENCE OBJECTCUSTOMFIELDS_seq;
+CREATE TABLE ObjectCustomFields (
+ id NUMBER(11,0)
+ CONSTRAINT ObjectCustomFields_Key PRIMARY KEY,
+ CustomField NUMBER(11,0) NOT NULL,
+ ObjectId NUMBER(11,0) NOT NULL,
+ SortOrder NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
+
+
+INSERT into ObjectCustomFields (id, CustomField, ObjectId, SortOrder, Creator, LastUpdatedBy) SELECT objectcustomfields_seq.nextval, id, Queue, SortOrder, Creator, LastUpdatedBy from CustomFields;
+
+ALTER TABLE CustomFields ADD LookupType VARCHAR2(255);
+ALTER TABLE CustomFields ADD Repeated NUMBER(11,0);
+ALTER TABLE CustomFields ADD Pattern VARCHAR2(255) NULL;
+ALTER TABLE CustomFields ADD MaxValues NUMBER(11,0);
+
+UPDATE CustomFields SET MaxValues = 0 WHERE Type LIKE '%Multiple';
+UPDATE CustomFields SET MaxValues = 1 WHERE Type LIKE '%Single';
+UPDATE CustomFields SET Type = 'Select' WHERE Type LIKE 'Select%';
+UPDATE CustomFields SET Type = 'Freeform' WHERE Type LIKE 'Freeform%';
+UPDATE CustomFields Set LookupType = 'RT::Queue-RT::Ticket';
+ALTER TABLE CustomFields MODIFY LookupType NOT NULL;
+UPDATE CustomFields Set Repeated = 0;
+ALTER TABLE CustomFields MODIFY Repeated DEFAULT 0;
+ALTER TABLE CustomFields MODIFY Repeated NOT NULL;
+ALTER TABLE CustomFields drop column Queue;
+
+
diff --git a/rt/etc/upgrade/3.3.0/schema.Pg b/rt/etc/upgrade/3.3.0/schema.Pg
new file mode 100644
index 0000000..427eae7
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/schema.Pg
@@ -0,0 +1,74 @@
+alter Table Transactions ADD Column ObjectType varchar(64);
+update Transactions set ObjectType = 'RT::Ticket';
+ALTER TABLE Transactions ALTER COLUMN ObjectType SET NOT NULL;
+alter table Transactions drop column EffectiveTicket;
+alter table Transactions add column ReferenceType varchar(255) NULL;
+alter table Transactions add column OldReference integer NULL;
+alter table Transactions add column NewReference integer NULL;
+drop index transactions1;
+alter table Transactions rename column Ticket to ObjectId;
+
+
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+
+alter table TicketCustomFieldValues rename to ObjectCustomFieldValues;
+
+alter table ObjectCustomFieldValues rename column Ticket to ObjectId;
+
+alter table objectcustomfieldvalues add column ObjectType varchar(255);
+
+update objectcustomfieldvalues set ObjectType = 'RT::Ticket';
+
+ALTER TABLE objectcustomfieldvalues ALTER COLUMN ObjectType SET NOT NULL;
+
+alter table objectcustomfieldvalues add column Current int;
+
+alter table objectcustomfieldvalues alter column Current SET default 1;
+
+UPDATE objectcustomfieldvalues SET Current = 1;
+
+alter table objectcustomfieldvalues add column LargeContent TEXT NULL;
+
+alter table objectcustomfieldvalues add column ContentType varchar(80) NULL;
+
+alter table objectcustomfieldvalues add column ContentEncoding varchar(80) NULL;
+
+create index ObjectCustomFieldValues1 on objectcustomfieldvalues (CustomField,ObjectType,ObjectId,Content);
+
+create index ObjectCustomFieldValues2 on objectcustomfieldvalues (CustomField,ObjectType,ObjectId);
+
+
+CREATE SEQUENCE objectcustomfields_id_s;
+
+CREATE TABLE ObjectCustomFields (
+ id INTEGER DEFAULT nextval('objectcustomfields_id_s'),
+ CustomField integer NOT NULL,
+ ObjectId integer NOT NULL,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created TIMESTAMP NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated TIMESTAMP NULL ,
+ PRIMARY KEY (id)
+
+);
+
+
+INSERT into ObjectCustomFields (CustomField, ObjectId, SortOrder, Creator, LastUpdatedBy) SELECT id, Queue, SortOrder, Creator, LastUpdatedBy from CustomFields;
+
+alter table CustomFields add column LookupType varchar(255);
+alter table CustomFields add column Repeated int2;
+alter table CustomFields add column Pattern varchar(255) NULL;
+alter table CustomFields add column MaxValues integer;
+
+UPDATE CustomFields SET MaxValues = 0 WHERE Type LIKE '%Multiple';
+UPDATE CustomFields SET MaxValues = 1 WHERE Type LIKE '%Single';
+UPDATE CustomFields SET Type = 'Select' WHERE Type LIKE 'Select%';
+UPDATE CustomFields SET Type = 'Freeform' WHERE Type LIKE 'Freeform%';
+UPDATE CustomFields Set LookupType = 'RT::Queue-RT::Ticket';
+ALTER TABLE CustomFields ALTER COLUMN LookupType SET NOT NULL;
+UPDATE CustomFields Set Repeated = 0;
+ALTER TABLE CustomFields ALTER COLUMN Repeated SET DEFAULT 0;
+ALTER TABLE CustomFields ALTER COLUMN Repeated SET NOT NULL;
+alter table CustomFields drop column Queue;
diff --git a/rt/etc/upgrade/3.3.0/schema.mysql b/rt/etc/upgrade/3.3.0/schema.mysql
new file mode 100644
index 0000000..0e33a28
--- /dev/null
+++ b/rt/etc/upgrade/3.3.0/schema.mysql
@@ -0,0 +1,65 @@
+alter Table Transactions ADD Column (ObjectType varchar(64) not null);
+update Transactions set ObjectType = 'RT::Ticket';
+alter table Transactions drop column EffectiveTicket;
+alter table Transactions add column ReferenceType varchar(255) NULL;
+alter table Transactions add column OldReference integer NULL;
+alter table Transactions add column NewReference integer NULL;
+alter table Transactions drop index transactions1;
+alter table Transactions change Ticket ObjectId integer NOT NULL DEFAULT 0 ;
+
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+
+alter table TicketCustomFieldValues rename ObjectCustomFieldValues;
+
+alter table ObjectCustomFieldValues change Ticket ObjectId integer NOT NULL DEFAULT 0 ;
+
+alter table ObjectCustomFieldValues add column ObjectType varchar(255) not null;
+
+update ObjectCustomFieldValues set ObjectType = 'RT::Ticket';
+
+alter table ObjectCustomFieldValues add column Current bool default 1;
+
+alter table ObjectCustomFieldValues add column LargeContent LONGTEXT NULL;
+
+alter table ObjectCustomFieldValues add column ContentType varchar(80) NULL;
+
+alter table ObjectCustomFieldValues add column ContentEncoding varchar(80) NULL;
+
+# These could fail if there's no such index and there's no "drop index if exists" syntax
+#alter table ObjectCustomFieldValues drop index ticketcustomfieldvalues1;
+#alter table ObjectCustomFieldValues drop index ticketcustomfieldvalues2;
+
+alter table ObjectCustomFieldValues add index ObjectCustomFieldValues1 (Content);
+
+alter table ObjectCustomFieldValues add index ObjectCustomFieldValues2 (CustomField,ObjectType,ObjectId);
+
+
+CREATE TABLE ObjectCustomFields (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ CustomField int NOT NULL ,
+ ObjectId integer NOT NULL,
+ SortOrder integer NOT NULL DEFAULT 0 ,
+
+ Creator integer NOT NULL DEFAULT 0 ,
+ Created DATETIME NULL ,
+ LastUpdatedBy integer NOT NULL DEFAULT 0 ,
+ LastUpdated DATETIME NULL ,
+ PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+
+INSERT into ObjectCustomFields (id, CustomField, ObjectId, SortOrder, Creator, LastUpdatedBy) SELECT null, id, Queue, SortOrder, Creator, LastUpdatedBy from CustomFields;
+
+alter table CustomFields add column LookupType varchar(255) NOT NULL;
+alter table CustomFields add column Repeated int2 NOT NULL DEFAULT 0 ;
+alter table CustomFields add column Pattern varchar(255) NULL;
+alter table CustomFields add column MaxValues integer;
+# See above
+# alter table CustomFields drop index CustomFields1;
+
+UPDATE CustomFields SET MaxValues = 0 WHERE Type LIKE '%Multiple';
+UPDATE CustomFields SET MaxValues = 1 WHERE Type LIKE '%Single';
+UPDATE CustomFields SET Type = 'Select' WHERE Type LIKE 'Select%';
+UPDATE CustomFields SET Type = 'Freeform' WHERE Type LIKE 'Freeform%';
+UPDATE CustomFields Set LookupType = 'RT::Queue-RT::Ticket';
+alter table CustomFields drop column Queue;
diff --git a/rt/etc/upgrade/3.3.11/acl.Oracle b/rt/etc/upgrade/3.3.11/acl.Oracle
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/acl.Oracle
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.11/acl.Pg b/rt/etc/upgrade/3.3.11/acl.Pg
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/acl.Pg
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.11/acl.SQLite b/rt/etc/upgrade/3.3.11/acl.SQLite
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/acl.SQLite
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.11/acl.mysql b/rt/etc/upgrade/3.3.11/acl.mysql
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/acl.mysql
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/rt/etc/upgrade/3.3.11/content b/rt/etc/upgrade/3.3.11/content
new file mode 100644
index 0000000..0afc604
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/content
@@ -0,0 +1 @@
+1;
diff --git a/rt/etc/upgrade/3.3.11/schema.Oracle b/rt/etc/upgrade/3.3.11/schema.Oracle
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/schema.Oracle
diff --git a/rt/etc/upgrade/3.3.11/schema.Pg b/rt/etc/upgrade/3.3.11/schema.Pg
new file mode 100644
index 0000000..6ab5d65
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/schema.Pg
@@ -0,0 +1,11 @@
+ALTER TABLE ObjectCustomFieldValues ADD COLUMN SortOrder INTEGER;
+UPDATE ObjectCustomFieldValues SET SortOrder = 0;
+ALTER TABLE ObjectCustomFieldValues ALTER COLUMN SortOrder SET DEFAULT 0;
+ALTER TABLE ObjectCustomFieldValues ALTER COLUMN SortOrder SET NOT NULL;
+ALTER TABLE ObjectCustomFieldValues ADD COLUMN Disabled INTEGER;
+UPDATE ObjectCustomFieldValues SET Disabled = 1 WHERE Current = 0;
+UPDATE ObjectCustomFieldValues SET Disabled = 0 WHERE Current != 0;
+ALTER TABLE ObjectCustomFieldValues ALTER COLUMN Disabled SET DEFAULT 0;
+ALTER TABLE ObjectCustomFieldValues ALTER COLUMN Disabled SET NOT NULL;
+
+ALTER TABLE ObjectCustomFieldValues DROP COLUMN Current;
diff --git a/rt/etc/upgrade/3.3.11/schema.SQLite b/rt/etc/upgrade/3.3.11/schema.SQLite
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/schema.SQLite
diff --git a/rt/etc/upgrade/3.3.11/schema.mysql b/rt/etc/upgrade/3.3.11/schema.mysql
new file mode 100644
index 0000000..cc35d40
--- /dev/null
+++ b/rt/etc/upgrade/3.3.11/schema.mysql
@@ -0,0 +1,5 @@
+ALTER TABLE ObjectCustomFieldValues ADD COLUMN SortOrder INTEGER NOT NULL DEFAULT 0;
+ALTER TABLE ObjectCustomFieldValues ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+
+UPDATE ObjectCustomFieldValues SET Disabled = 1 WHERE Current = 0;
+ALTER TABLE ObjectCustomFieldValues DROP COLUMN Current;
diff --git a/rt/etc/upgrade/3.5.1/content b/rt/etc/upgrade/3.5.1/content
new file mode 100644
index 0000000..e3898a7
--- /dev/null
+++ b/rt/etc/upgrade/3.5.1/content
@@ -0,0 +1,36 @@
+@Attributes = (
+ { Name => 'Search - My Tickets',
+ Description => '[_1] highest priority tickets I own',
+ Content =>
+ { Format => "'<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__id__</a>/TITLE:#', '<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__Subject__</a>/TITLE:Subject', Priority, QueueName, ExtendedStatus",
+ Query => " Owner = '__CurrentUser__' AND ( Status = 'new' OR Status = 'open')",
+ OrderBy => 'Priority',
+ Order => 'DESC' },
+ },
+ { Name => 'Search - Unowned Tickets',
+ Description => '[_1] newest unowned tickets',
+ Content =>
+ { Format => "'<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__id__</a>/TITLE:#', '<a href=\"__WebPath__/Ticket/Display.html?id=__id__\">__Subject__</a>/TITLE:Subject', QueueName, ExtendedStatus, CreatedRelative, '<A HREF=\"__WebPath__/Ticket/Display.html?Action=Take&id=__id__\">__loc(Take)__</a>/TITLE:&nbsp;' ",
+ Query => " Owner = 'Nobody' AND ( Status = 'new' OR Status = 'open')",
+ OrderBy => 'Created',
+ Order => 'DESC' },
+ },
+ { Name => 'HomepageSettings',
+ Description => 'HomepageSettings',
+ Content =>
+ { 'body' =>
+ [ { type => 'system', name => 'My Tickets' },
+ { type => 'system', name => 'Unowned Tickets' },
+ { type => 'component', name => 'QuickCreate'},
+ ],
+ 'summary' =>
+ [
+ { type => 'component', name => 'MyReminders' },
+ { type => 'component', name => 'Quicksearch' },
+ { type => 'component', name => 'RefreshHomepage' },
+ ]
+ },
+}
+);
+
+1;
diff --git a/rt/html/Admin/CustomFields/GroupRights.html b/rt/html/Admin/CustomFields/GroupRights.html
new file mode 100644
index 0000000..a416327
--- /dev/null
+++ b/rt/html/Admin/CustomFields/GroupRights.html
@@ -0,0 +1,172 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/CustomFieldTabs,
+ id => $id,
+ current_tab => "Admin/CustomFields/GroupRights.html?id=".$id,
+ Title => $title
+&>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="GroupRights.html">
+ <input type="hidden" class="hidden" name="id" value="<% $CustomFieldObj->id %>" />
+
+
+<h1><&|/l&>System groups</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToSystemInternalGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% loc($Group->Type) %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object => $CustomFieldObj &>
+ </td>
+ </tr>
+% }
+</table>
+<h1><&|/l&>User defined groups</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToUserDefinedGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% $Group->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object => $CustomFieldObj &>
+ </td>
+ </tr>
+% }
+</table>
+
+ <& /Elements/Submit, Caption => loc("Be sure to save your changes"), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+
+
+
+
+
+if (!defined $id) {
+ $m->comp("/Elements/Error", Why => loc("No CustomField defined"));
+}
+
+my $CustomFieldObj = RT::CustomField->new($session{'CurrentUser'});
+$CustomFieldObj->Load($id) || $m->comp("/Elements/Error", Why => loc("Couldn't load CustomField [_1]",$id));
+
+my $Groups;
+
+ my ( $ACL, @results );
+
+ foreach my $arg (keys %ARGS) {
+ if ($arg =~ /GrantRight-(\d+)-(.*?)-(\d+)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $rights = $ARGS{$arg};
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+ my $obj;
+
+ if ($object_type eq 'RT::CustomField') {
+ $obj = RT::CustomField->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be granted for [_1]", $object_type));
+ next;
+ }
+
+ my @rights = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} : ($ARGS{$arg});
+ foreach my $right (@rights) {
+ next unless ($right);
+ my ($val, $msg) = $principal->GrantRight(Object => $obj, Right => $right);
+ push (@results, $msg);
+ }
+ }
+ elsif ($arg =~ /RevokeRight-(\d+)-(.*?)-(\d+)-(.*?)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $right = $4;
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+ next unless ($right);
+ my $obj;
+
+ if ($object_type eq 'RT::CustomField') {
+ $obj = RT::CustomField->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be revoked for [_1]", $object_type));
+ next;
+ }
+ my ($val, $msg) = $principal->RevokeRight(Object => $obj, Right => $right);
+ push (@results, $msg);
+ }
+}
+
+my $title = loc('Modify group rights for custom field [_1]', $CustomFieldObj->Name);
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/CustomFields/Modify.html b/rt/html/Admin/CustomFields/Modify.html
new file mode 100644
index 0000000..0202f0a
--- /dev/null
+++ b/rt/html/Admin/CustomFields/Modify.html
@@ -0,0 +1,258 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/CustomFieldTabs,
+ id => $CustomFieldObj->Id ,
+ current_tab => $current_tab,
+ Title => $title &>
+<& /Elements/ListActions, actions => \@results &>
+
+
+<form method="post" action="Modify.html" name="ModifyCustomField">
+<input type="hidden" class="hidden" name="id" value="<%$id %>" />
+<table>
+<tr>
+<td class="label"><&|/l&>Name</&></td>
+<td><input name="Name" value="<%$CustomFieldObj->Name%>" size="20" /></td></tr>
+<tr>
+<td class="label"><&|/l&>Description</&></td>
+<td><input name="Description" value="<%$CustomFieldObj->Description%>" size="80" /></td>
+</tr>
+
+<tr>
+<td class="label"><&|/l&>Type</&></td>
+<td><& /Admin/Elements/SelectCustomFieldType,
+ Name => "TypeComposite",
+ Default => $CustomFieldObj->TypeComposite, &>
+</td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Applies to</&></td>
+<td><& /Admin/Elements/SelectCustomFieldLookupType,
+ Name => "LookupType",
+ Default => $CustomFieldObj->LookupType, &>
+</td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Validation</&></td>
+<td>
+<& /Widgets/ComboBox,
+ Name => 'Pattern',
+ Default => $CustomFieldObj->Pattern,
+ Size => 20,
+ Values => [
+ '(?#Mandatory).',
+ '(?#Digits)^[\d.]+$',
+ '(?#Year)^[12]\d{3}$',
+ ],
+&>
+</td></tr>
+</tr>
+<tr>
+<td class="label">&nbsp;</td>
+<td>
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <%$EnabledChecked%> /> <&|/l&>Enabled (Unchecking this box disables this custom field)</&>
+</td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Link values to</&></td>
+</td>
+<td><input size="60" name="LinkValueTo" value="<%$CustomFieldObj->LinkValueTo%>" />
+<div class="hints">
+<&|/l&>RT can make this custom field's values into hyperlinks to another service.</&>
+<&|/l&>Fill in this field with a URL.</&>
+<&|/l&>RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively</&>
+</div>
+</td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Include page</&></td>
+</td>
+<td><input size="60" name="IncludeContentForValue" value="<%$CustomFieldObj->IncludeContentForValue%>" />
+<div class="hints">
+<&|/l&>RT can include content from another web service when showing this custom field.</&>
+<&|/l&>Fill in this field with a URL.</&>
+<&|/l&>RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively</&>
+<i><&|/l&>Some browsers may only load content from the same domain as your RT server.</&></i>
+</div>
+</td>
+</tr>
+
+
+
+</table>
+<br />
+% if ($CustomFieldObj->Id && $CustomFieldObj->IsSelectionType) {
+<h2><&|/l&>Values</&></h2>
+<div>
+<& /Admin/Elements/EditCustomFieldValues, CustomField => $CustomFieldObj &>
+<& /Admin/Elements/AddCustomFieldValue, CustomField => $CustomFieldObj &>
+</div>
+% }
+<&/Elements/Submit&>
+</form>
+
+
+
+<%INIT>
+
+
+
+my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
+my ( $title, @results, $EnabledChecked, $Disabled);
+$EnabledChecked = "CHECKED";
+
+if ( !$id ) {
+ $title = loc("Create a CustomField");
+ $id = 'new';
+}
+else {
+
+ if ( $id eq 'new' ) {
+ my ( $val, $msg ) = $CustomFieldObj->Create(Name => $Name,
+ TypeComposite => $TypeComposite,
+ LookupType => $LookupType,
+ Description => $Description,
+ Pattern => $Pattern,);
+ $m->comp("/Elements/Error", Why => loc( "Could not create CustomField", $msg ) ) unless ($val);
+ push @results, $msg;
+ $title = loc( 'Created CustomField [_1]', $CustomFieldObj->Name() );
+ }
+ else {
+ $CustomFieldObj->Load($id) || $m->comp("/Elements/Error", Why => loc('No CustomField') );
+ $title = loc( 'Editing CustomField [_1]', $CustomFieldObj->Name() );
+
+ my @attribs = qw( Pattern Name TypeComposite LookupType Description LinkValueTo IncludeContentForValue);
+ my @aresults = UpdateRecordObject( AttributesRef => \@attribs,
+ Object => $CustomFieldObj,
+ ARGSRef => \%ARGS );
+
+ push @results, @aresults;
+
+ #we're asking about enabled on the web page but really care about disabled.
+ if ($Enabled == 1) {
+ $Disabled = 0;
+ }
+ else {
+ $Disabled = 1;
+ }
+ if ( ($SetEnabled) and ( $Disabled != $CustomFieldObj->Disabled) ) {
+ my ($code, $msg) = $CustomFieldObj->SetDisabled($Disabled);
+ push @results, loc('Enabled status: [_1]', loc_fuzzy($msg));
+ }
+
+ if ($CustomFieldObj->Disabled()) {
+ $EnabledChecked ="";
+ }
+ }
+
+ $id = $CustomFieldObj->id;
+}
+
+
+
+
+my $paramtag = "CustomField-".$CustomFieldObj->Id."-Value-";
+# Delete any fields that want to be deleted
+foreach my $key (keys %ARGS) {
+
+ next unless ($key =~ /^Delete-$paramtag(\d+)$/);
+ my ($val, $msg) = $CustomFieldObj->DeleteValue($1);
+ push (@results, $msg);
+
+
+}
+# Update any existing values
+my $values = $CustomFieldObj->ValuesObj;
+while (my $value = $values->Next) {
+ foreach my $attr qw(Name Description SortOrder Category) {
+ my $param = $paramtag.$value->Id."-".$attr;
+
+ if ( $ARGS{$param} && ($value->$attr() ne $ARGS{$param})) {
+ my $mutator = "Set$attr";
+ my ($id, $msg) = $value->$mutator($ARGS{$param});
+ push (@results, $msg);
+ }
+ }
+
+
+}
+
+
+
+# Add any new values
+if ($ARGS{$paramtag."new-Name"}) {
+ my ($id, $msg) = $CustomFieldObj->AddValue (
+ map { $_ => $ARGS{$paramtag."new-$_"} }
+ qw( Name Description SortOrder Category )
+ );
+ push (@results, $msg);
+}
+
+my $current_tab;
+if ($ARGS{'Create'}){
+ $current_tab = "Admin/CustomFields/Modify.html?Create=1";
+} else {
+ $current_tab = "Admin/CustomFields/Modify.html?id=".$id;
+ }
+
+
+</%INIT>
+<%ARGS>
+$id => undef
+$TypeComposite => undef
+$LookupType => undef
+$MaxValues => undef
+$SortOrder => undef
+$Description => undef
+$Pattern => undef
+$Name => undef
+$SetEnabled => undef
+$Enabled => undef
+</%ARGS>
diff --git a/rt/html/Admin/CustomFields/Objects.html b/rt/html/Admin/CustomFields/Objects.html
new file mode 100644
index 0000000..d829a0d
--- /dev/null
+++ b/rt/html/Admin/CustomFields/Objects.html
@@ -0,0 +1,147 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/CustomFieldTabs,
+ id => $id,
+ current_tab => "Admin/CustomFields/Objects.html?id=".$id,
+ Title => $title
+ &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="Objects.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<% $id %>" />
+<input type="hidden" class="hidden" name="UpdateObjs" value="1" />
+
+<h2><&|/l&>Selected objects</&></h2>
+<& /Admin/Elements/PickObjects, Objects => \@AssignedObjs, id => $id, Checked => 1 &>
+<h2><&|/l&>Unselected objects</&></h2>
+<& /Admin/Elements/PickObjects, Objects => \@UnassignedObjs, id => $id &>
+
+<& /Elements/Submit, CheckAll => 1, ClearAll => 1 &>
+</form>
+
+<%INIT>
+my $CF = RT::CustomField->new($session{'CurrentUser'});
+$CF->Load($id) or Abort(loc("Could not load CustomField [_1]"), $id);
+my $LookupType = $CF->LookupType;
+$LookupType =~ /^(.*?)-/ ||
+ Abort(loc("Object of type [_1] cannot take custom fields", $LookupType));
+
+my $Class = $1;
+my $CollectionClass;
+if (UNIVERSAL::can($Class.'Collection', 'new') ) {
+$CollectionClass = $Class.'Collection';
+
+} elsif (UNIVERSAL::can($Class.'es', 'new') ) {
+ $CollectionClass = $Class.'es';
+
+} elsif (UNIVERSAL::can($Class.'s', 'new') ) {
+ $CollectionClass = $Class.'s';
+
+} else {
+ Abort(loc("Can't find a collection class for '[_1]'", $Class));
+}
+
+
+my $title = loc('Modify associated objects for [_1]', $CF->Name);
+
+my $Objects = $CollectionClass->new($session{'CurrentUser'});
+my (@results);
+my (@AssignedObjs, @UnassignedObjs);
+
+$Objects->UnLimit;
+$Objects->OrderBy( FIELD => 'Name' );
+
+
+my $ObjectCFs;
+$ObjectCFs = RT::ObjectCustomFields->new($session{'CurrentUser'});
+$ObjectCFs->UnLimit;
+$ObjectCFs->LimitToCustomField($id);
+
+my %seen;
+while (my $OCF = $ObjectCFs->Next) {
+ $seen{$OCF->ObjectId}++;
+}
+
+while (my $obj = $Objects->Next) {
+ my $obj_id = $obj->Id;
+
+ if ($UpdateObjs) {
+ # Go through and delete all the custom field relationships that this object
+ # no longer has
+ my $key = "Object-$obj_id-CF-$id";
+ if ($ARGS{$key}) {
+ if (!$seen{$obj_id}) {
+ my ($val, $msg) = $CF->AddToObject($obj);
+ push (@results, $msg);
+ push @UnassignedObjs, $obj if !$val;
+ }
+ }
+ else {
+ push @UnassignedObjs, $obj;
+ if ($seen{$obj_id}) {
+ my ($val, $msg) = $CF->RemoveFromObject($obj);
+ push (@results, $msg);
+ pop @UnassignedObjs if !$val;
+ }
+ }
+ }
+ elsif (!$seen{$obj_id}) {
+ push @UnassignedObjs, $obj;
+ }
+ next if @UnassignedObjs and $UnassignedObjs[-1] == $obj;
+ push @AssignedObjs, $obj;
+}
+
+</%INIT>
+<%ARGS>
+$id => undef
+$FindDisabledObjects => 0
+$UpdateObjs => 0
+</%ARGS>
diff --git a/rt/html/Admin/CustomFields/UserRights.html b/rt/html/Admin/CustomFields/UserRights.html
new file mode 100644
index 0000000..a714597
--- /dev/null
+++ b/rt/html/Admin/CustomFields/UserRights.html
@@ -0,0 +1,170 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/CustomFieldTabs, id => $id,
+current_tab => "Admin/CustomFields/UserRights.html?id=".$id,
+Title => $title, &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="UserRights.html">
+ <input type="hidden" class="hidden" name="id" value="<% $CustomFieldObj->id %>" />
+
+
+<table>
+
+% while (my $Member = $Users->Next()) {
+% my $UserObj = $Member->MemberObj->Object();
+% my $group = RT::Group->new($session{'CurrentUser'});
+% $group->LoadACLEquivalenceGroup($Member->MemberObj);
+ <tr align="right">
+ <td valign="top">
+ <% $UserObj->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId=> $group->PrincipalId,
+ Object => $CustomFieldObj &>
+ </td>
+ </tr>
+% }
+ </table>
+
+ <& /Elements/Submit, Caption => loc("Be sure to save your changes"), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+ #Update the acls.
+ my @results;
+foreach my $arg (keys %ARGS) {
+ if ($arg =~ /GrantRight-(\d+)-(.*?)-(\d+)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $rights = $ARGS{$arg};
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+ my $obj;
+
+ if ($object_type eq 'RT::CustomField') {
+ $obj = RT::CustomField->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be granted for [_1]",
+$object_type));
+ next;
+ }
+
+ my @rights = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} :
+($ARGS{$arg});
+ foreach my $right (@rights) {
+ next unless ($right);
+ my ($val, $msg) = $principal->GrantRight(Object => $obj, Right
+=> $right);
+ push (@results, $msg);
+ }
+ }
+ elsif ($arg =~ /RevokeRight-(\d+)-(.*?)-(\d+)-(.*?)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $right = $4;
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+ next unless ($right);
+ my $obj;
+
+ if ($object_type eq 'RT::CustomField') {
+ $obj = RT::CustomField->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be revoked for [_1]",
+$object_type));
+ next;
+ }
+ my ($val, $msg) = $principal->RevokeRight(Object => $obj, Right =>
+$right);
+ push (@results, $msg);
+ }
+}
+
+
+# {{{ Deal with setting up the display of current rights.
+
+
+
+if (!defined $id) {
+ $m->comp("/Elements/Error", Why => loc("No Class defined"));
+}
+
+my $CustomFieldObj = RT::CustomField->new($session{'CurrentUser'});
+$CustomFieldObj->Load($id) || $m->comp("/Elements/Error", Why => loc("Couldn't load Class [_1]",$id));
+
+# Find out which users we want to display ACL selects for
+my $Privileged = RT::Group->new($session{'CurrentUser'});
+$Privileged->LoadSystemInternalGroup('Privileged');
+my $Users = $Privileged->MembersObj();
+
+
+my $title = loc('Modify user rights for custom field [_1]', $CustomFieldObj->Name);
+
+# }}}
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+$UserString => undef
+$UserOp => undef
+$UserField => undef
+</%ARGS>
diff --git a/rt/html/Admin/CustomFields/index.html b/rt/html/Admin/CustomFields/index.html
new file mode 100644
index 0000000..badee95
--- /dev/null
+++ b/rt/html/Admin/CustomFields/index.html
@@ -0,0 +1,93 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Select a Custom Field') &>
+<& /Admin/Elements/CustomFieldTabs,
+ current_tab => 'Admin/CustomFields/',
+ Title => loc('Select a Custom Field') &>
+
+% my @types;
+% my $prev_lookup = '';
+% while (my $CustomFieldObj = $CustomFields->Next) {
+% $CustomFieldObj->CurrentUserHasRight('AdminCustomField') or next;
+% my $lookup = $CustomFieldObj->FriendlyLookupType;
+% if ($lookup ne $prev_lookup) {
+% if ($prev_lookup) {
+</ul>
+% }
+<h2><% loc("Custom Fields for [_1]", $lookup) %></h2>
+<ul>
+% $prev_lookup = $lookup;
+% push @types, [$lookup, $CustomFieldObj->LookupType];
+% }
+%
+<li>
+<a href="Modify.html?id=<%$CustomFieldObj->id()%>"><%$CustomFieldObj->Name%>: <%$CustomFieldObj->Description%></a>
+</li>
+% }
+% if ($prev_lookup) {
+</ul>
+% }
+
+<form action="<%$RT::WebPath%>/Admin/CustomFields/index.html" method="get">
+<&|/l&>Only show custom fields for:</&>
+<select name="type">
+% for (@types) {
+<option value="<% $_->[1] %>"><% $_->[0] %></option>
+% }
+</select> <input type="submit" value="<%loc('Go!')%>" />
+</form>
+
+<%args>
+$type => undef
+</%args>
+<%INIT>
+my $CustomFields = RT::CustomFields->new($session{'CurrentUser'});
+$CustomFields->UnLimit();
+$CustomFields->LimitToLookupType($type) if defined $type;
+$CustomFields->OrderByCols( { FIELD => 'LookupType' }, { FIELD => 'Name' } );
+</%INIT>
diff --git a/rt/html/Admin/Elements/AddCustomFieldValue b/rt/html/Admin/Elements/AddCustomFieldValue
new file mode 100644
index 0000000..39916e5
--- /dev/null
+++ b/rt/html/Admin/Elements/AddCustomFieldValue
@@ -0,0 +1,74 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<b><&|/l&>Add Value</&></b>
+<table border="0">
+<tr><td>
+<&|/l&>Sort</&>:<br />
+<input size="3" name="CustomField-<%$CustomField->Id%>-Value-new-SortOrder" />
+</td>
+<td>
+<&|/l&>Name</&>:<br />
+<input type="text" size="30" name="CustomField-<%$CustomField->Id%>-Value-new-Name" />
+</td>
+<td>
+<&|/l&>Description</&>:<br />
+<input type="text" size="50" name="CustomField-<%$CustomField->Id%>-Value-new-Description" />
+</td>
+% if ($CustomField->Type ne 'Combobox') {
+<td>
+<&|/l&>Category</&>:<br />
+<input type="text" size="10" name="CustomField-<%$CustomField->Id%>-Value-new-Category" />
+</td>
+% }
+</tr>
+</table>
+<%init>
+</%init>
+<%args>
+$CustomField => undef
+</%args>
diff --git a/rt/html/Admin/Elements/ConfigureMyRT b/rt/html/Admin/Elements/ConfigureMyRT
new file mode 100644
index 0000000..363a3b6
--- /dev/null
+++ b/rt/html/Admin/Elements/ConfigureMyRT
@@ -0,0 +1,80 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Action
+$OnSave
+$items
+$panes
+$current_portlets
+</%args>
+<%init>
+my $portlets = $current_portlets;
+my @panes;
+for my $pane (@$panes) {
+ push @panes, $m->comp(
+ '/Widgets/SelectionBox:new',
+ Action => $Action,
+ Name => $pane,
+ Available => $items,
+ AutoSave => 1,
+ OnSubmit => sub {
+ my $sel = shift;
+ $portlets->{$pane} = [
+ map { m/(\w+)-(.*)$}/;
+ { type => $1,
+ name => $2 } } @{ $sel->{Current} }
+ ];
+ $OnSave->( $portlets, $pane );
+ },
+ Selected => [ map { join( '-', @{$_}{qw/type name/} ) }
+ @{ $portlets->{$pane} } ]
+ );
+}
+
+return @panes;
+</%init>
diff --git a/rt/html/Admin/Elements/CreateUserCalled b/rt/html/Admin/Elements/CreateUserCalled
new file mode 100644
index 0000000..6918325
--- /dev/null
+++ b/rt/html/Admin/Elements/CreateUserCalled
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<form method="get" action="<% $RT::WebPath %>/Admin/Users/Create.html">
+<&|/l&>New user called</&> <input name="Name" size="10" /><input type="submit" class="button" value="<&|/l&>Create</&>" />
+</form>
diff --git a/rt/html/Admin/Elements/CustomFieldTabs b/rt/html/Admin/Elements/CustomFieldTabs
new file mode 100644
index 0000000..078dbe0
--- /dev/null
+++ b/rt/html/Admin/Elements/CustomFieldTabs
@@ -0,0 +1,118 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Tabs,
+ current_tab => 'Admin/CustomFields/',
+ subtabs => $tabs,
+ current_subtab => $current_tab,
+ Title => $Title &>
+<%INIT>
+my $tabs;
+
+if ($id) {
+ my $cf = RT::CustomField->new( $session{'CurrentUser'} );
+ $cf->Load($id);
+ $tabs = {
+ this => {
+ title => $cf->Name,
+ path => "Admin/CustomFields/Modify.html?id=" . $id,
+ current_subtab => $current_tab,
+
+ subtabs => {
+
+ C => { title => loc('Basics'),
+ path => "Admin/CustomFields/Modify.html?id=" . $id,
+ },
+ F => { title => loc('Group Rights'),
+ path => "Admin/CustomFields/GroupRights.html?id="
+ . $id, },
+ G => {
+ title => loc('User Rights'),
+ path => "Admin/CustomFields/UserRights.html?id=" . $id,
+ },
+
+ } }
+
+ };
+
+
+ if ($cf->LookupType =~ /^RT::Queue/io) {
+ $tabs->{'this'}->{subtabs}->{D} = {
+ title => loc('Applies to'),
+ path => "Admin/CustomFields/Objects.html?id=" . $id,
+ };
+ }
+}
+
+if ($session{'CurrentUser'}->HasRight( Object => $RT::System, Right => 'AdminCustomField')) {
+ $tabs->{"A"} = { title => loc('Select custom field'),
+ path => "Admin/CustomFields/",
+ };
+ $tabs->{"B"} = { title => loc('New custom field'),
+ path => "Admin/CustomFields/Modify.html?Create=1",
+ separator => 1,
+ };
+}
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+foreach my $tab (sort keys %{$tabs->{'this'}->{'subtabs'}}) {
+ if ($tabs->{'this'}->{'subtabs'}->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{'subtabs'} = $subtabs;
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{'current_subtab'} = $current_subtab;
+ }
+}
+if( $id ) { $current_tab = "Admin/CustomFields/Modify.html?id=" . $id }
+</%INIT>
+<%ARGS>
+$Title => undef
+$id => undef
+$current_tab => undef
+$subtabs => undef
+$current_subtab => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/EditCustomField b/rt/html/Admin/Elements/EditCustomField
new file mode 100644
index 0000000..d6dda06
--- /dev/null
+++ b/rt/html/Admin/Elements/EditCustomField
@@ -0,0 +1,159 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/ListActions, actions => \@results &>
+
+
+<form method="post" action="CustomField.html">
+<input type="hidden" class="hidden" name="CustomField" value="<%$id %>" />
+<input type="hidden" class="hidden" name="Queue" value="<%$Queue%>" />
+
+<table width="100%" border="0">
+<tr><td align="right">
+<&|/l&>Name</&>:
+</td><td>
+<input name="Name" value="<%$CustomFieldObj->Name%>" size="20" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Description</&>:
+</td><td>
+<input name="Description" value="<%$CustomFieldObj->Description%>" size="80" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Type</&>:
+</td><td>
+<& /Admin/Elements/SelectCustomFieldType, Name => "Type", Default => $CustomFieldObj->Type &>
+</td></tr>
+<tr><td>
+</td><td>
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <%$EnabledChecked%> /> <&|/l&>Enabled (Unchecking this box disables this custom field)</&>
+</td></tr>
+</table>
+
+<p>
+% if ($CustomFieldObj->Id and $CustomFieldObj->Type =~ /Select/) {
+<h2><&|/l&>Values</&></h2>
+<font size="-1">
+<& /Admin/Elements/EditCustomFieldValues, CustomField => $CustomFieldObj &>
+<& /Admin/Elements/AddCustomFieldValue, CustomField => $CustomFieldObj &>
+</font>
+% }
+<&/Elements/Submit, Label => loc('Create') &>
+</form>
+
+
+
+<%INIT>
+
+my $CustomFieldObj = RT::CustomField->new($session{'CurrentUser'});
+my $EnabledChecked = "CHECKED";
+my (@results);
+
+if (! $CustomField ) {
+ $title = loc("Create a CustomField");
+ $id = 'new';
+} else {
+
+ if ($CustomField eq 'new') {
+ my ($val, $msg) = $CustomFieldObj->Create(Queue => $Queue,
+ Name => $Name,
+ Type => $Type,
+ Description => $Description,
+ );
+
+ # if there is an error, then abort. But since at this point there's
+ # stuff already printed, clear it out.
+ # (This only works in conjunction with temporarily turning autoflush
+ # off in the files that use this component.)
+ unless ($val) {
+ $m->clear_buffer;
+ Abort(loc("Could not create CustomField: [_1]", $msg));
+ }
+ push @results, $msg;
+ $CustomFieldObj->SetSortOrder($CustomFieldObj->id);
+ $title = loc('Created CustomField [_1]', $CustomFieldObj->Name());
+ } else {
+ $CustomFieldObj->Load($CustomField) || Abort(loc('No CustomField'));
+ $title = loc('Editing CustomField [_1]', $CustomFieldObj->Name());
+
+ my @aresults = ProcessCustomFieldUpdates (
+ CustomFieldObj => $CustomFieldObj,
+ ARGSRef => \%ARGS );
+ push @results, @aresults;
+ }
+
+
+$id = $CustomFieldObj->id;
+
+ #we're asking about enabled on the web page but really care about disabled.
+ my $Disabled = ($Enabled ? 0 : 1);
+
+ if ( ($SetEnabled) and ( $Disabled != $CustomFieldObj->Disabled) ) {
+ my ($code, $msg) = $CustomFieldObj->SetDisabled($Disabled);
+ push @results, loc('Enabled status [_1]', loc_fuzzy($msg));
+ }
+
+ if ($CustomFieldObj->Disabled()) {
+ $EnabledChecked ="";
+ }
+
+}
+
+
+</%INIT>
+<%ARGS>
+$id => undef
+$title => undef
+$Queue => undef
+$CustomField => undef
+$Type => undef
+$Description => undef
+$Name => undef
+$SetEnabled => undef
+$Enabled => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/EditCustomFieldValues b/rt/html/Admin/Elements/EditCustomFieldValues
new file mode 100644
index 0000000..e7da87c
--- /dev/null
+++ b/rt/html/Admin/Elements/EditCustomFieldValues
@@ -0,0 +1,96 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if (!$values->Count) {
+<p><em><&|/l&>(no values)</&></em></p>
+% return;
+% }
+<i><&|/l&>(Check box to delete)</&></i>
+<table>
+<tr>
+<td>&nbsp;</td>
+<td><&|/l&>Sort</&></td>
+<td><&|/l&>Name</&></td>
+<td><&|/l&>Description</&></td>
+% if ($CustomField->Type ne 'Combobox') {
+<td><&|/l&>Category</&></td>
+% }
+</tr>
+% while (my $value = $values->Next) {
+<tr>
+<td>
+<input type="checkbox" class="checkbox" name="Delete-CustomField-<%$CustomField->Id%>-Value-<%$value->Id%>" />
+</td>
+<td>
+<input size="3" name="CustomField-<%$CustomField->Id%>-Value-<%$value->Id%>-SortOrder" value="<%$value->SortOrder%>" />
+</td>
+<td>
+<input type="text" size="30" name="CustomField-<%$CustomField->Id%>-Value-<%$value->Id%>-Name" value="<%$value->Name%>" />
+</td>
+<td>
+<font size="-1">
+<input type="text" size="50" name="CustomField-<%$CustomField->Id%>-Value-<%$value->Id%>-Description" value="<%$value->Description%>" />
+</font>
+</td>
+% if ($CustomField->Type ne 'Combobox') {
+<td>
+<font size="-1">
+<input type="text" size="10" name="CustomField-<%$CustomField->Id%>-Value-<%$value->Id%>-Category" value="<%$value->Category%>" />
+</font>
+</td>
+% }
+</tr>
+% }
+</table>
+<%init>
+
+my $values = $CustomField->ValuesObj();
+
+</%init>
+<%args>
+$CustomField => undef
+</%args>
diff --git a/rt/html/Admin/Elements/EditCustomFields b/rt/html/Admin/Elements/EditCustomFields
new file mode 100644
index 0000000..9b1176a
--- /dev/null
+++ b/rt/html/Admin/Elements/EditCustomFields
@@ -0,0 +1,205 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="<%$RT::WebPath%><% $m->request_comp->path |n %>" method="post" name="EditCustomFields">
+<input type="hidden" class="hidden" name="id" value="<% $Object->Id %>" />
+<input type="hidden" class="hidden" name="ObjectType" value="<% $ObjectType %>" />
+<input type="hidden" class="hidden" name="SubType" value="<% $SubType %>" />
+<input type="hidden" class="hidden" name="UpdateCFs" value="1" />
+
+% if ($Object->Id) {
+<h2><&|/l&>Global Custom Fields</&></h2>
+<& PickCustomFields, CustomFields => \@GlobalCFs, ReadOnly => 1, id => $id, SubType => $SubType &>
+% }
+<h2><&|/l&>Selected Custom Fields</&></h2>
+<& PickCustomFields, CustomFields => [$ObjectCFs->CustomFields], id => $id, Checked => 1, SubType => $SubType &>
+<h2><&|/l&>Unselected Custom Fields</&></h2>
+<& PickCustomFields, CustomFields => \@UnassignedCFs, id => $id, SubType => $SubType &>
+
+<& /Elements/Submit, CheckAll => 1, ClearAll => 1 &>
+</form>
+
+
+<%INIT>
+my $CustomFields = RT::CustomFields->new($session{'CurrentUser'});
+my @results;
+my (@GlobalCFs, @UnassignedCFs);
+
+my $id = $Object->Id;
+if ($id and !$Object->CurrentUserHasRight('AssignCustomFields')) {
+ $m->out('<p><i>', loc('(No custom fields)'), '</i></p>');
+ return;
+}
+
+my $lookup = $ObjectType;
+$lookup .= "-$SubType" if $SubType;
+
+$CustomFields->LimitToLookupType($lookup);
+$CustomFields->OrderBy( FIELD => 'Name' );
+
+
+my ($GlobalCFs, $ObjectCFs);
+$ObjectCFs = RT::ObjectCustomFields->new($session{'CurrentUser'});
+$ObjectCFs->UnLimit;
+$ObjectCFs->LimitToObjectId($id);
+$ObjectCFs->LimitToLookupType($lookup);
+
+# Check sanity of SortOrders
+my %SortOrders;
+$SortOrders{$_->SortOrder}++
+ while ($_ = $ObjectCFs->Next);
+
+# If there are duplicates, run though and squash them
+if (grep {$_ > 1} values %SortOrders) {
+ my $i = 1;
+ while (my $ObjectCF = $ObjectCFs->Next) {
+ $ObjectCF->SetSortOrder($i++);
+ }
+ $ObjectCFs->GotoFirstItem;
+}
+
+# {{{ deal with moving sortorder of custom fields
+if ($CustomField and $Move) {
+ my $SourceObj = RT::ObjectCustomField->new($session{'CurrentUser'});
+ $SourceObj->LoadByCols( ObjectId => $id, CustomField => $CustomField );
+
+ my $TargetObj;
+ my $target_order = $SourceObj->SortOrder + $Move;
+ while (my $ObjectCF = $ObjectCFs->Next) {
+ my $this_order = $ObjectCF->SortOrder;
+
+ # if we have an exact match, finish the loop now
+ ($TargetObj = $ObjectCF, last) if $this_order == $target_order;
+
+ # otherwise, we need to apropos toward the general direction
+ # ... first, check the sign is correct
+ next unless ($this_order - $SourceObj->SortOrder) * $Move > 0;
+
+ # ... next, see if we already have a candidate
+ if ($TargetObj) {
+ # ... if yes, compare the delta and choose the smaller one
+ my $orig_delta = abs($TargetObj->SortOrder - $target_order);
+ my $this_delta = abs($this_order - $target_order);
+ next if $orig_delta < $this_delta;
+ }
+
+ $TargetObj = $ObjectCF;
+ }
+
+ if ($TargetObj) {
+ # swap their sort order
+ my ($s, $t) = ($SourceObj->SortOrder, $TargetObj->SortOrder);
+ $TargetObj->SetSortOrder($s);
+ $SourceObj->SetSortOrder($t);
+ # because order changed, we must redo search for subsequent uses
+ }
+
+ $ObjectCFs->GotoFirstItem;
+}
+# }}}
+
+if ($id) {
+ $GlobalCFs = RT::ObjectCustomFields->new($session{'CurrentUser'});
+ $GlobalCFs->LimitToObjectId(0);
+ $GlobalCFs->LimitToLookupType($lookup);
+}
+
+while (my $cf = $CustomFields->Next) {
+ my $cf_id = $cf->Id;
+
+ if ($GlobalCFs and $GlobalCFs->HasEntryForCustomField($cf_id)) {
+ push @GlobalCFs, $cf;
+ next;
+ }
+
+ if ($UpdateCFs) {
+ # Go through and delete all the custom field relationships that this object
+ # no longer has
+ my $key = "Object-$id-CF-$cf_id";
+ if ($ARGS{$key}) {
+ if (!$ObjectCFs->HasEntryForCustomField($cf_id)) {
+ my ($val, $msg) = $cf->AddToObject($Object);
+ push (@results, $msg);
+ push @UnassignedCFs, $cf if !$val;
+ }
+ }
+ else {
+ push @UnassignedCFs, $cf;
+ if ($ObjectCFs->HasEntryForCustomField($cf_id)) {
+ my ($val, $msg) = $cf->RemoveFromObject($Object);
+ push (@results, $msg);
+ pop @UnassignedCFs if !$val;
+ }
+ }
+ }
+ elsif (!$ObjectCFs->HasEntryForCustomField($cf_id)) {
+ push @UnassignedCFs, $cf;
+ }
+ else {
+ }
+}
+
+# redo search...
+$ObjectCFs = RT::ObjectCustomFields->new($session{'CurrentUser'});
+$ObjectCFs->UnLimit;
+$ObjectCFs->LimitToObjectId($id);
+$ObjectCFs->LimitToLookupType($lookup);
+
+</%INIT>
+<%ARGS>
+$title => undef
+$Move => undef
+$Source => undef
+$CustomField => undef
+$FindDisabledCustomFields => undef
+$UpdateCFs => 0
+$Object
+$ObjectType
+$SubType => ''
+</%ARGS>
diff --git a/rt/html/Admin/Elements/EditQueueWatchers b/rt/html/Admin/Elements/EditQueueWatchers
new file mode 100644
index 0000000..dc076f5
--- /dev/null
+++ b/rt/html/Admin/Elements/EditQueueWatchers
@@ -0,0 +1,78 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%if ($Members->Count == 0 ) {
+<ul>
+<li><i><&|/l&>none</&></i>
+% } else {
+<i><&|/l&>(Check box to delete)</&></i><br /><br />
+<ul>
+% while (my $watcher=$Members->Next) {
+<li>
+<input type="checkbox" class="checkbox" name="Queue-<%$QueueObj->Id%>-DeleteWatcher-Type-<%$Watchers->Type%>-Principal-<%$watcher->MemberId%>" value="1"
+ unchecked />
+% if ($watcher->MemberObj->IsUser) {
+<a href="<%$RT::WebPath%>/Admin/Users/Modify.html?id=<%$watcher->MemberObj->ObjectId%>">
+% } else {
+<a href="<%$RT::WebPath%>/Admin/Groups/Modify.html?id=<%$watcher->MemberObj->ObjectId%>">
+% }
+<%$watcher->MemberObj->Object->Name%></a>
+% }
+% }
+</ul>
+
+<%INIT>
+my $Members = $Watchers->MembersObj;
+</%INIT>
+
+<%ARGS>
+$QueueObj => undef
+$Watchers => undef
+</%ARGS>
+
+
+
diff --git a/rt/html/Admin/Elements/EditScrip b/rt/html/Admin/Elements/EditScrip
new file mode 100644
index 0000000..edf949b
--- /dev/null
+++ b/rt/html/Admin/Elements/EditScrip
@@ -0,0 +1,183 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/ListActions, actions => \@actions &>
+
+<form method="post" action="Scrip.html">
+<input type="hidden" class="hidden" name="id" value="<% $id %>" />
+<input type="hidden" class="hidden" name="Queue" value="<% $Queue %>" />
+
+<&| /Widgets/TitleBox, title => loc('Scrip Fields') &>
+<table>
+
+<tr><td align="right"><&|/l&>Description</&>:</td><td>
+<input name="Scrip-<% $id %>-Description" value="<% $ARGS{"Scrip-$id-Description"} || $scrip->Description %>" />
+</td></tr>
+
+<tr><td align="right"><&|/l&>Condition</&>:</td><td>
+<& /Admin/Elements/SelectScripCondition,
+ Name => "Scrip-$id-ScripCondition",
+ Default => $ARGS{"Scrip-$id-ScripCondition"} || $scrip->ConditionObj->Id,
+&></td></tr>
+
+<tr><td align="right"><&|/l&>Action</&>:</td><td>
+<& /Admin/Elements/SelectScripAction,
+ Name => "Scrip-$id-ScripAction",
+ Default => $ARGS{"Scrip-$id-ScripAction"} || $scrip->ActionObj->Id,
+&></td></tr>
+
+<tr><td align="right"><&|/l&>Template</&>:</td><td>
+<& /Admin/Elements/SelectTemplate,
+ Name => "Scrip-$id-Template",
+ Default => $ARGS{"Scrip-$id-Template"} || $scrip->TemplateObj->Id,
+ Queue => $Queue,
+&></td></tr>
+
+<tr><td align="right"><&|/l&>Stage</&>:</td><td>
+<& /Admin/Elements/SelectStage,
+ Name => "Scrip-$id-Stage",
+ Default => $ARGS{"Scrip-$id-Stage"} || $scrip->Stage,
+&></td></tr>
+
+</table>
+</&>
+
+<& /Elements/Submit,
+ Label => $SubmitLabel,
+ Caption => loc("Be sure to save your changes"),
+ Reset => 1,
+&><br />
+
+<&| /Widgets/TitleBox, title => loc('User Defined conditions and actions') &>
+<table>
+<tr><td colspan="2">
+<i><&|/l&>(Use these fields when you choose 'User Defined' for a condition or action)</&></i>
+</td></tr>
+
+<tr><td class="labeltop"><&|/l&>Custom condition</&>:</td><td>
+<textarea cols="80" rows="5" name="Scrip-<% $id %>-CustomIsApplicableCode"><% $ARGS{"Scrip-$id-CustomIsApplicableCode"} || $scrip->CustomIsApplicableCode %></textarea>
+</td></tr>
+
+<tr><td class="labeltop"><&|/l&>Custom action preparation code</&>:</td><td>
+<textarea cols="80" rows="5" name="Scrip-<% $id %>-CustomPrepareCode"><% $ARGS{"Scrip-$id-CustomPrepareCode"} || $scrip->CustomPrepareCode %></textarea>
+</td></tr>
+
+<tr><td class="labeltop"><&|/l&>Custom action cleanup code</&>:</td><td>
+<textarea cols="80" rows="5" name="Scrip-<% $id %>-CustomCommitCode"><% $ARGS{"Scrip-$id-CustomCommitCode"} || $scrip->CustomCommitCode %></textarea>
+</td></tr>
+
+</table>
+</&>
+
+<& /Elements/Submit, Label => $SubmitLabel, Reset => 1 &>
+
+</form>
+<%init>
+
+my (@actions, $SubmitLabel);
+
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+
+if ( $id ) {
+ $scrip->Load( $id );
+ unless ( $id = $scrip->id ) {
+ push @actions, loc("Couldn't load scrip #[_1]", $id);
+ }
+ $SubmitLabel = loc('Update');
+}
+
+unless ( $id ) {
+ $id = 'new';
+ $SubmitLabel = loc('Create');
+}
+
+</%init>
+
+<%ARGS>
+$id => undef
+$title => undef
+$Queue => 0
+</%ARGS>
+
+<%METHOD Process>
+<%ARGS>
+$id => undef
+$Queue => undef
+</%ARGS>
+<%INIT>
+return ($id) unless $id;
+
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+if ( $id eq 'new' ) {
+ return $scrip->Create(
+ Queue => $Queue,
+ ScripAction => $ARGS{"Scrip-new-ScripAction"},
+ ScripCondition => $ARGS{"Scrip-new-ScripCondition"},
+ Template => $ARGS{"Scrip-new-Template"},
+ Description => $ARGS{"Scrip-new-Description"},
+ CustomPrepareCode => $ARGS{"Scrip-new-CustomPrepareCode"},
+ CustomCommitCode => $ARGS{"Scrip-new-CustomCommitCode"},
+ CustomIsApplicableCode => $ARGS{"Scrip-new-CustomIsApplicableCode"},
+ );
+}
+else {
+ $scrip->Load( $id );
+ return (undef, loc("Couldn't load scrip #[_1]", $id))
+ unless $scrip->id;
+
+ my @attribs = qw(Queue ScripAction ScripCondition Template Stage
+ Description CustomPrepareCode CustomCommitCode CustomIsApplicableCode);
+ my @results = UpdateRecordObject(
+ AttributesRef => \@attribs,
+ AttributePrefix => 'Scrip-'.$scrip->Id,
+ Object => $scrip,
+ ARGSRef => \%ARGS
+ );
+ return ($scrip->id, @results);
+}
+</%INIT>
+</%METHOD>
diff --git a/rt/html/Admin/Elements/EditScrips b/rt/html/Admin/Elements/EditScrips
new file mode 100644
index 0000000..f3ef8fb
--- /dev/null
+++ b/rt/html/Admin/Elements/EditScrips
@@ -0,0 +1,125 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/ListActions, actions => \@actions &>
+
+<form action="Scrips.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<%$id%>" />
+
+<h2><&|/l&>Current Scrips</&></h2>
+% if ($Scrips->Count == 0 ) {
+<p><i><&|/l&>(No scrips)</&></i></p>
+% } else {
+<table width="100%">
+<p><i><&|/l&>(Check box to delete)</&></i></p>
+
+% while (my $scrip = $Scrips->Next ) {
+<tr>
+<td>
+<input type="checkbox" class="checkbox" name="DeleteScrip-<%$scrip->Id%>" value="1" />
+</td>
+<td>
+<a href="Scrip.html?id=<%$scrip->Id%>&Queue=<%$id%>"><% $scrip->Description || "<i>(".loc('no value').")</i>" |n %></a><br />
+<small><&|/l, loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name) &>[_1] [_2] with template [_3]</&></small>
+</td>
+</tr>
+% }
+
+</table>
+
+% }
+<& /Elements/Submit,
+ Caption => loc("Delete selected scrips"),
+ Label => loc("Delete") &>
+</form>
+
+<%init>
+my (@actions);
+
+my $Scrips = RT::Scrips->new($session{'CurrentUser'});
+
+
+my $QueueObj = RT::Queue->new($session{'CurrentUser'});
+if ($id) {
+ $QueueObj->Load($id);
+}
+
+if ($QueueObj->id) {
+ $Scrips->LimitToQueue($id);
+}
+else {
+ $Scrips->LimitToGlobal();
+}
+
+$Scrips->OrderBy( FIELD => 'description' );
+
+
+
+# {{{ deal with modifying and deleting existing scrips
+foreach my $key (keys %ARGS) {
+ # {{{ if we're trying to delete the scrip
+ if ($key =~ /^DeleteScrip-(\d+)/) {
+ my $id = $1;
+ my $scrip = new RT::Scrip($session{'CurrentUser'});
+ $scrip->Load($id);
+ my ($retval, $msg) = $scrip->Delete;
+ if ($retval) {
+ push @actions, loc("Scrip deleted");
+ }
+ else {
+ push @actions, $msg;
+ }
+ }
+ # }}}
+}
+# }}}
+</%init>
+
+<%ARGS>
+$id => undef
+$title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/EditTemplates b/rt/html/Admin/Elements/EditTemplates
new file mode 100644
index 0000000..5d770ef
--- /dev/null
+++ b/rt/html/Admin/Elements/EditTemplates
@@ -0,0 +1,128 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/ListActions, actions => \@actions &>
+
+<form method="get" action="Templates.html">
+<input type="hidden" class="hidden" name="id" value="<%$id%>" />
+
+% if ($Templates->Count == 0 ) {
+<p><i><&|/l&>(No templates)</&></i></p>
+% } else {
+<table width="100%">
+<tr>
+<th>
+<i><&|/l&>(Check box to delete)</&></i>
+</th>
+<th>
+</th>
+</tr>
+% my $count;
+% while (my $TemplateObj = $Templates->Next) {
+<tr>
+<td>
+<input type="checkbox" class="checkbox" name="DeleteTemplate-<%$TemplateObj->Id%>" value="1" />
+</td>
+<td>
+<a href="Template.html?Queue=<%$id%>&Template=<%$TemplateObj->id()%>">
+<strong><% loc($TemplateObj->Name) %></strong></a>
+<br /><% loc($TemplateObj->Description) %>
+</td>
+</tr>
+
+% }
+</table>
+% }
+
+<& /Elements/Submit, Label => loc('Delete Template') &>
+</form>
+
+<%INIT>
+my $Templates = RT::Templates->new($session{'CurrentUser'});
+my $QueueObj = RT::Queue->new($session{'CurrentUser'});
+my @actions;
+
+if ($id) {
+ $QueueObj->Load($id);
+}
+
+if ($QueueObj->id) {
+ $Templates->LimitToQueue($id);
+}
+else {
+ $Templates->LimitToGlobal();
+}
+
+# Now let callbacks add their extra limits
+$m->comp('/Elements/Callback', Templates => $Templates, %ARGS);
+
+# {{{ deal with deleting existing templates
+foreach my $key (keys %ARGS) {
+ # {{{ if we're trying to delete the template
+ if ($key =~ /^DeleteTemplate-(\d+)/) {
+ my $id = $1;
+ my $TemplateObj = RT::Template->new($session{'CurrentUser'});
+ $TemplateObj->Load($id);
+ my ($retval, $msg) = $TemplateObj->Delete;
+ if ($retval) {
+ push @actions, loc("Template deleted");
+ }
+ else {
+ push @actions, $msg;
+ }
+ }
+ # }}}
+}
+# }}}
+</%INIT>
+<%ARGS>
+$id => 0
+$title => undef
+$Move => undef
+$Source => undef
+$Template => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/EditUserComments b/rt/html/Admin/Elements/EditUserComments
new file mode 100644
index 0000000..5035189
--- /dev/null
+++ b/rt/html/Admin/Elements/EditUserComments
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => "Comments about $name" &>
+<&|/l&>These comments aren't generally visible to the user</&>:<br />
+<input type="hidden" class="hidden" name="id" value="<%$id%>" />
+<textarea cols="60" rows="15" wrap="soft" name="Comments"><% $UserObj->Comments %></textarea>
+</form>
+
+<%ARGS>
+$UserObj => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/GlobalCustomFieldTabs b/rt/html/Admin/Elements/GlobalCustomFieldTabs
new file mode 100755
index 0000000..db69df8
--- /dev/null
+++ b/rt/html/Admin/Elements/GlobalCustomFieldTabs
@@ -0,0 +1,95 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/SystemTabs, subtabs => $tabs,
+ current_tab => 'Admin/Global/CustomFields/index.html',
+ current_subtab => $current_tab,
+ Title => $Title &>
+<%INIT>
+
+
+ my $tabs = {
+
+ A => { title => loc('Users'),
+ text => loc('Modify scrips which apply to all queues'),
+ path => 'Admin/Global/CustomFields/Users.html',
+ },
+ B => { title => loc('Groups'),
+ text => loc('Edit system templates'),
+ path => 'Admin/Global/CustomFields/Groups.html',
+ },
+
+ F => { title => loc('Tickets'),
+ text => loc('Modify global custom fields'),
+ path => 'Admin/Global/CustomFields/Queue-Tickets.html',
+ },
+
+ G => { title => loc('Ticket Transactions'),
+ text => loc('Modify global group rights'),
+ path => 'Admin/Global/CustomFields/Queue-Transactions.html',
+ },
+
+};
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+ foreach my $tab (sort keys %{$tabs}) {
+ if ($tabs->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+ }
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$current_tab => undef
+$subtabs => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/GroupTabs b/rt/html/Admin/Elements/GroupTabs
new file mode 100644
index 0000000..bf6a1cf
--- /dev/null
+++ b/rt/html/Admin/Elements/GroupTabs
@@ -0,0 +1,102 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Tabs,
+ subtabs => $tabs,
+ current_tab => 'Admin/Groups/',
+ current_subtab => $current_tab,
+ Title => $Title &>
+<%INIT>
+my $tabs;
+
+if ( $GroupObj and $GroupObj->id ) {
+$tabs->{"this"} = { class => "currentnav",
+ path => "Admin/Groups/Modify.html?id=" . $GroupObj->id,
+ title => $GroupObj->Name,
+ current_subtab => $current_subtab,
+ subtabs => {
+ C => { title => loc('Basics'),
+ path => "Admin/Groups/Modify.html?id=" . $GroupObj->id },
+
+ D => { title => loc('Members'),
+ path => "Admin/Groups/Members.html?id=" . $GroupObj->id },
+
+ F => { title => loc('Group Rights'),
+ path => "Admin/Groups/GroupRights.html?id=" . $GroupObj->id, },
+ G => { title => loc('User Rights'),
+ path => "Admin/Groups/UserRights.html?id=" . $GroupObj->id, },
+ H => { title => loc('History'),
+ path => "Admin/Groups/History.html?id=" . $GroupObj->id },
+ }
+}
+}
+$tabs->{"A"} = { title => loc('Select group'),
+ path => "Admin/Groups/", };
+$tabs->{"B"} = { title => loc('New group'),
+ path => "Admin/Groups/Modify.html?Create=1",
+ separator => 1, };
+
+# Now let callbacks add their extra tabs
+$m->comp( '/Elements/Callback', tabs => $tabs, %ARGS );
+foreach my $tab ( sort keys %{$tabs->{'this'}->{'subtabs'}} ) {
+ if ( $tabs->{'this'}->{'subtabs'}->{$tab}->{'path'} eq $current_tab ) {
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+}
+ $tabs->{'this'}->{"current_subtab"} = $current_tab;
+ $current_tab = "Admin/Groups/Modify.html?id=".$GroupObj->id if $GroupObj;
+
+</%INIT>
+<%ARGS>
+$GroupObj => undef
+$subtabs => undef
+$current_subtab => undef
+$current_tab => undef
+$Title => undef
+</%ARGS>
+
diff --git a/rt/html/Admin/Elements/Header b/rt/html/Admin/Elements/Header
new file mode 100644
index 0000000..e9b0356
--- /dev/null
+++ b/rt/html/Admin/Elements/Header
@@ -0,0 +1,52 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, %ARGS &>
+
+<%ARGS>
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/ListGlobalCustomFields b/rt/html/Admin/Elements/ListGlobalCustomFields
new file mode 100644
index 0000000..24308fc
--- /dev/null
+++ b/rt/html/Admin/Elements/ListGlobalCustomFields
@@ -0,0 +1,61 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% my $count = 0;
+% while (my $CustomFieldObj = $CustomFields->Next) {
+% $count++;
+<font size="-1"><%$CustomFieldObj->id%>/<% loc($CustomFieldObj->Type) %>/<%$CustomFieldObj->Name%>: <%$CustomFieldObj->Description%></font>
+<br />
+% }
+% if (!$count) {
+<font size="-1"><&|/l&>(No custom fields)</&></font>
+% }
+
+<%init>
+my $CustomFields = new RT::CustomFields ($session{'CurrentUser'});
+$CustomFields->LimitToGlobal();
+</%INIT>
diff --git a/rt/html/Admin/Elements/ListGlobalScrips b/rt/html/Admin/Elements/ListGlobalScrips
new file mode 100644
index 0000000..e2e8b15
--- /dev/null
+++ b/rt/html/Admin/Elements/ListGlobalScrips
@@ -0,0 +1,76 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if ($Scrips->Count == 0) {
+
+<p><i><&|/l&>(No scrips)</&></i></p>
+
+% } else {
+
+<ul>
+
+% while (my $scrip = $Scrips->Next ) {
+<li>
+<a href="<%$RT::WebPath%>/Admin/Global/Scrip.html?id=<%$scrip->Id%>&Queue=<%0%>">
+% if ($scrip->Description) {
+<% $scrip->Description %>
+% } else {
+<i>(<&|/l, $scrip->Id&>Scrip #[_1]</&>)</i>
+% }
+</a><br />
+<small><&|/l, loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name) &>[_1] [_2] with template [_3]</&></small>
+</li>
+% }
+
+</ul>
+
+% }
+
+<%init>
+my $Scrips = new RT::Scrips ($session{'CurrentUser'});
+$Scrips->LimitToGlobal();
+</%INIT>
diff --git a/rt/html/Admin/Elements/ModifyTemplate b/rt/html/Admin/Elements/ModifyTemplate
new file mode 100644
index 0000000..b667e05
--- /dev/null
+++ b/rt/html/Admin/Elements/ModifyTemplate
@@ -0,0 +1,84 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+<tr>
+<td align="right">
+<&|/l&>Name</&>:
+</td>
+<td>
+<input name="Name" value="<%$Name%>" size="20" /><br />
+</td>
+</tr>
+<tr>
+<td align="right">
+<&|/l&>Description</&>:
+</td>
+<td>
+<input name="Description" value="<%$Description%>" size="80" /><br />
+</td>
+</tr>
+<tr>
+<td align="right" valign="top">
+<&|/l&>Content</&>:<br />
+</td>
+<td>
+<textarea name="Content" rows="25" cols="80" wrap="soft">
+<%$Content%></textarea>
+</td>
+</tr>
+</table>
+
+<%INIT>
+
+</%INIT>
+
+<%ARGS>
+$Name => undef
+$Description => undef
+$Content => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/ObjectCustomFields b/rt/html/Admin/Elements/ObjectCustomFields
new file mode 100644
index 0000000..61a10cd
--- /dev/null
+++ b/rt/html/Admin/Elements/ObjectCustomFields
@@ -0,0 +1,111 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& $ObjectTabs,
+$id ? (
+ id => $Object->id,
+ current_tab => "Admin/$Types/CustomFields.html?$sub_type_url&id=".$id,
+ current_subtab => "Admin/$Types/CustomFields.html?$sub_type_url&id=".$id,
+ "${Type}Obj" => $Object,
+) : (
+ current_tab => "Admin/Global/CustomFields/${QualifiedType}s.html",
+),
+ Title => $title
+ &>
+
+<& /Admin/Elements/EditCustomFields, %ARGS, title => $title, Object => $Object &>
+<%INIT>
+# XXX TODO: Validate here?
+#$ObjectType =~ /^RT::(Queue|User|Group)$/
+# or Abort(loc("Object of type [_1] cannot take custom fields", $ObjectType));
+
+
+
+my $Type = $1;
+my $Types = $Type.'s';
+my $ObjectTabs;
+my $Object = $ObjectType->new($session{'CurrentUser'});
+
+
+my $QualifiedType;
+my $FriendlySubTypes;
+if ($SubType =~/^RT::(.*)$/) {
+ $FriendlySubTypes = RT::CustomField->new($session{'CurrentUser'})->FriendlyLookupType($Object->CustomFieldLookupType);
+ $QualifiedType = "$Type-$1";
+} else {
+ $QualifiedType = $Type;
+}
+
+if ($id) {
+ $Object->Load($id) || Abort(loc("Couldn't load object [_1]", $id));
+ $ObjectTabs = "/Admin/Elements/${Type}Tabs";
+} else {
+ $ObjectTabs = "/Admin/Elements/GlobalCustomFieldTabs";
+
+}
+
+my $title;
+if ($id) {
+$title = loc('Edit Custom Fields for [_1]', $Object->Name);
+}
+elsif ($SubType) {
+
+ $title= loc("Modify Custom Fields which apply to [_1] for all [_2]", loc(lc($FriendlySubTypes)), loc(lc($Types)));
+} else {
+ $title =loc("Modify Custom Fields which apply to all [_1]", loc(lc($Types)));
+
+}
+my $sub_type_url;
+$sub_type_url = "SubType=$SubType" if $SubType;
+
+</%INIT>
+<%ARGS>
+$id => undef
+$ObjectType
+$SubType => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/PickCustomFields b/rt/html/Admin/Elements/PickCustomFields
new file mode 100644
index 0000000..7b88433
--- /dev/null
+++ b/rt/html/Admin/Elements/PickCustomFields
@@ -0,0 +1,98 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if (@CustomFields == 0) {
+<p><i><&|/l&>(None)</&></i></p>
+% } else {
+<table cellspacing="0" cellpadding="2">
+% my $count;
+% foreach my $CustomFieldObj (@CustomFields) {
+<tr>
+% if (!$ReadOnly) {
+ <td valign="top">
+<input type="checkbox" class="checkbox" name="Object-<%$id%>-CF-<%$CustomFieldObj->Id%>" value="1" <% $Checked ? 'CHECKED' : '' %>
+/>
+ </td>
+% }
+ <td valign="top">
+ <a href="<%$RT::WebPath%>/Admin/CustomFields/Modify.html?id=<%$CustomFieldObj->id()%>">
+% if ($CustomFieldObj->Name) {
+<b><%$CustomFieldObj->Name%></b>
+% } else {
+<i>(<&|/l&>no name</&>)</i>
+% }
+</a><br />
+ <%$CustomFieldObj->Description%>
+ </td>
+ <td valign="top">
+ <i><% $CustomFieldObj->FriendlyTypeComposite %></i>
+ </td>
+% # show 'move up' unless it's the first item
+% if ($count++ and $Checked) {
+ <td valign="top">
+ [<a href="<%$RT::WebPath%><% $m->request_comp->path |n %>?id=<%$id%>&SubType=<%$SubType%>&CustomField=<%$CustomFieldObj->id%>&Move=-1"><&|/l&>Move up</&></a>]
+% } else {
+ <td valign="top" align="right">
+% }
+
+% # show 'move down' unless it's the last item
+% if ($count != @CustomFields and $Checked) {
+% $m->print(' | ') if $count > 1;
+ [<a href="<%$RT::WebPath%><% $m->request_comp->path |n %>?id=<%$id%>&SubType=<%$SubType%>&CustomField=<%$CustomFieldObj->id%>&Move=1"><&|/l&>Move down</&></a>]
+% }
+ </td>
+</tr>
+% }
+</table>
+% }
+<%ARGS>
+@CustomFields
+$id
+$ReadOnly => 0
+$Checked => 0
+$SubType
+</%ARGS>
diff --git a/rt/html/Admin/Elements/PickObjects b/rt/html/Admin/Elements/PickObjects
new file mode 100644
index 0000000..5fc0863
--- /dev/null
+++ b/rt/html/Admin/Elements/PickObjects
@@ -0,0 +1,81 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if (@Objects == 0) {
+<p><i><&|/l&>(None)</&></i></p>
+% } else {
+<table cellspacing="0" cellpadding="2">
+% my $count;
+% foreach my $Object (@Objects) {
+<tr>
+% my $id = "Object-".$Object->id."-CF-".$id;
+% if (!$ReadOnly) {
+ <td valign="top">
+<input type="checkbox" id="<% $id %>" name="<% $id %>" value="1" <% $Checked ? 'CHECKED' : ''%>
+/>
+ </td>
+% }
+ <td valign="top">
+ <label for="<% $id %>">
+% if ($Object->Name) {
+ <b><%$Object->Name%></b><br />
+% } else {
+ <i>(<%loc("no name")%>)</i><br />
+% }
+ <%$Object->can('Description') && $Object->Description%>
+ </label>
+ </td>
+</tr>
+% }
+</table>
+% }
+<%ARGS>
+@Objects
+$id
+$ReadOnly => 0
+$Checked => 0
+</%ARGS>
diff --git a/rt/html/Admin/Elements/QueueRightsForUser b/rt/html/Admin/Elements/QueueRightsForUser
new file mode 100644
index 0000000..15af784
--- /dev/null
+++ b/rt/html/Admin/Elements/QueueRightsForUser
@@ -0,0 +1,64 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<ul>
+%while(my $ACE = $ACL->Next) {
+
+<li><checkbox name="delete_ace_<%$ACE->id%>" value="1"> <% loc($ACE->RightName) %> (<%$ACE->UserObj->RealName%>)
+
+%}
+</ul>
+
+<%INIT>
+my $ACL = new RT::ACL($session{'CurrentUser'});
+$ACL->LimitToQueue($QueueObj->id);
+$ACL->LimitPrincipalToUser($PrincipalId);
+</%INIT>
+<%ARGS>
+$PrincipalId => undef
+$QueueObj => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/QueueTabs b/rt/html/Admin/Elements/QueueTabs
new file mode 100644
index 0000000..0d06704
--- /dev/null
+++ b/rt/html/Admin/Elements/QueueTabs
@@ -0,0 +1,120 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Tabs,
+ subtabs => $tabs,
+ current_tab => 'Admin/Queues/',
+ current_subtab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+my $tabs;
+if ($id) {
+ $tabs->{'this'} = {
+ title => $QueueObj->Name,
+ path => "Admin/Queues/Modify.html?id=".$id,
+ current_subtab => $current_tab,
+ subtabs => {
+ C => { title => loc('Basics'),
+ path => "Admin/Queues/Modify.html?id=".$id,
+ },
+ D => { title => loc('Watchers'),
+ path => "Admin/Queues/People.html?id=".$id,
+ },
+
+ E => { title => loc('Scrips'),
+ path => "Admin/Queues/Scrips.html?id=".$id,
+ },
+ F => { title => loc('Templates'),
+ path => "Admin/Queues/Templates.html?id=".$id,
+ },
+
+ G1 => { title => loc('Ticket Custom Fields'),
+ path => 'Admin/Queues/CustomFields.html?SubType=RT::Ticket&id='.$id,
+ },
+
+ G2 => { title => loc('Transaction Custom Fields'),
+ path => 'Admin/Queues/CustomFields.html?SubType=RT::Ticket-RT::Transaction&id='.$id,
+ },
+
+ H => { title => loc('Group Rights'),
+ path => "Admin/Queues/GroupRights.html?id=".$id,
+ },
+ I => { title => loc('User Rights'),
+ path => "Admin/Queues/UserRights.html?id=".$id,
+ }
+ }
+ };
+}
+if ($session{'CurrentUser'}->HasRight( Object => $RT::System, Right => 'AdminQueue')) {
+ $tabs->{"A"} = { title => loc('Select queue'),
+ path => "Admin/Queues/",
+ };
+ $tabs->{"B"} = { title => loc('New queue'),
+ path => "Admin/Queues/Modify.html?Create=1",
+ separator => 1, };
+}
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+foreach my $tab ( sort keys %{$tabs->{'this'}->{'subtabs'}} ) {
+ if ( $tabs->{'this'}->{'subtabs'}->{$tab}->{'path'} eq $current_tab ) {
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+}
+ $current_tab = "Admin/Queues/Modify.html?id=".$id if $id;
+</%INIT>
+
+<%ARGS>
+$QueueObj => undef
+$id => undef
+$subtabs => undef
+$current_subtab => undef
+$current_tab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectCustomFieldLookupType b/rt/html/Admin/Elements/SelectCustomFieldLookupType
new file mode 100644
index 0000000..ff8bfdb
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectCustomFieldLookupType
@@ -0,0 +1,60 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+%for my $option ($cf->LookupTypes) {
+<option value="<%$option%>" <%$option eq $Default && "SELECTED"%>><% $cf->FriendlyLookupType($option) %></option>
+%}
+</select>
+<%INIT>
+my $cf = RT::CustomField->new($session{'CurrentUser'});
+
+</%INIT>
+<%ARGS>
+$Default=>undef
+$Name => 'LookupType'
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectCustomFieldType b/rt/html/Admin/Elements/SelectCustomFieldType
new file mode 100644
index 0000000..c913979
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectCustomFieldType
@@ -0,0 +1,60 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+%for my $option ($cf->TypeComposites) {
+<option value="<%$option%>" <%$option eq $Default && "SELECTED"%>><% $cf->FriendlyTypeComposite($option) %></option>
+%}
+</select>
+<%INIT>
+my $cf = RT::CustomField->new($session{'CurrentUser'});
+
+</%INIT>
+<%ARGS>
+$Default=>undef
+$Name => 'TypeComposite'
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectGroups b/rt/html/Admin/Elements/SelectGroups
new file mode 100644
index 0000000..662273a
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectGroups
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select multiple name="<%$Name%>" size="10">
+%while (my $group = $groups->Next) {
+<option value="<%$group->id%>"><%$group->Name%>
+%}
+</select>
+
+<%INIT>
+my $groups = new RT::Groups($session{'CurrentUser'});
+$groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => $Domain);
+
+</%INIT>
+<%ARGS>
+$Name => 'groups'
+$Domain => 'UserDefined';
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectModifyGroup b/rt/html/Admin/Elements/SelectModifyGroup
new file mode 100644
index 0000000..4db67c5
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectModifyGroup
@@ -0,0 +1,57 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%while ( $Group = $Groups->Next) {
+<a href="Modify.html?id=<%$Group->id%>"><%$Group->id%>: <%$Group->Name%></a><br />
+%}
+<%INIT>
+my ($Group);
+my $Groups = new RT::Groups($session{'CurrentUser'});
+$Groups->UnLimit;
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectModifyQueue b/rt/html/Admin/Elements/SelectModifyQueue
new file mode 100644
index 0000000..c3e6a98
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectModifyQueue
@@ -0,0 +1,57 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%while ( $queue = $queues->Next) {
+<a href="Modify.html?id=<%$queue->id%>"><%$queue->id%>: <%$queue->Name%></a><br />
+%}
+<%INIT>
+my ($queue);
+my $queues = new RT::Queues($session{'CurrentUser'});
+$queues->UnLimit;
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectModifyUser b/rt/html/Admin/Elements/SelectModifyUser
new file mode 100644
index 0000000..2b28402
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectModifyUser
@@ -0,0 +1,73 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%while ( $user = $users->Next) {
+<a href="Modify.html?id=<%$user->id%>"><%$user->id%>: <%$user->Name%></a><br />
+%}
+<%INIT>
+my ($user);
+my $users = new RT::Users($session{'CurrentUser'});
+$users->Limit(FIELD => 'id',
+ VALUE => $RT::SystemUser->id,
+ OPERATOR => '!=' );
+
+if (defined $IdLike) {
+$users->Limit(FIELD => 'Name',
+ VALUE => $IdLike,
+ OPERATOR => 'LIKE' );
+}
+if (defined $EmailLike) {
+$users->Limit(FIELD => 'EmailAddress',
+ VALUE => $EmailLike,
+ OPERATOR => 'LIKE');
+
+}
+</%INIT>
+<%ARGS>
+$IdLike => undef
+$EmailLike => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectNewGroupMembers b/rt/html/Admin/Elements/SelectNewGroupMembers
new file mode 100644
index 0000000..34d711f
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectNewGroupMembers
@@ -0,0 +1,99 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if ($Show ne 'Groups') {
+<b><&|/l&>Users</&></b>
+<select multiple name="<%$Name%>Users" size="10">
+%while (my $user = $users->Next) {
+%next if $SkipUsers->{$user->id};
+<option value="User-<%$user->id%>"><%$user->Name%></option>
+%}
+</select>
+<br />
+% }
+% if ($Show ne 'Users') {
+<b><&|/l&>Groups</&></b>
+<select multiple name="<%$Name%>Groups" size="10">
+%while (my $group = $groups->Next) {
+%next if $SkipGroups->{$group->id};
+<option value="Group-<%$group->id%>"><%$group->Name%></option>
+%}
+</select>
+% }
+
+<%INIT>
+my $users = new RT::Users($session{'CurrentUser'});
+
+$users->Limit(
+ FIELD => 'id',
+ VALUE => $RT::SystemUser->id,
+ OPERATOR => '!=',
+ ENTRYAGGREGATOR => 'AND'
+);
+$users->Limit(
+ FIELD => 'id',
+ VALUE => $RT::Nobody->id,
+ OPERATOR => '!=',
+ ENTRYAGGREGATOR => 'AND'
+);
+$users->LimitToPrivileged();
+
+my $groups = new RT::Groups($session{'CurrentUser'});
+
+# self-recursive group membership considered harmful!
+$groups->Limit(FIELD => 'id', VALUE => $Group->id, OPERATOR => '!=' );
+$groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined');
+
+
+</%INIT>
+<%ARGS>
+$Name => 'Users'
+$Show => 'All'
+$Group
+$SkipUsers => {}
+$SkipGroups => {}
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectRights b/rt/html/Admin/Elements/SelectRights
new file mode 100644
index 0000000..a0a5736
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectRights
@@ -0,0 +1,118 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<input type="hidden" class="hidden" name="CheckACL" value="<%$ACLDesc%>" />
+ <table border="0">
+<tr>
+<td valign="top" width="180" align="left">
+% my %current_rights;
+<h3><&|/l&>Current rights</&></h3>
+% if ($ACLObj->Count() == 0) {
+<i><&|/l&>No rights granted.</&></i> <br />
+% } else {
+<i>(<&|/l&>Check box to revoke right</&>)</i> <br />
+% while (my $right = $ACLObj->Next()) {
+% if ($right->RightName) {
+% $current_rights{$right->RightName} = 1;
+<input type="checkbox" class="checkbox" value="<%$right->Id%>" name="RevokeRight-<%$ACLDesc%>-<%$right->RightName%>" /> <% loc($right->RightName) %><br />
+% }
+% }
+% }
+</td>
+<td valign="top">
+<h3><&|/l&>New rights</&></h3>
+<select size="5" multiple name="GrantRight-<%$ACLDesc%>">
+% foreach $right (sort keys %Rights) {
+% next if $current_rights{$right};
+ <option value="<%$right%>"
+ ><% loc($right) %></option>
+% }
+<option value="" selected><&|/l&>(no value)</&></option>
+</select>
+</td>
+</tr>
+</table>
+<%INIT>
+ my ($right, $ACLDesc, $AppliesTo, %Rights);
+
+ # if the principal id points to a user, we really want to point
+ # to their ACL equivalence group. The machinations we're going through
+ # lead me to start to suspect that we really want users and groups
+ # to just be the same table. or _maybe_ that we want an object db.
+ my $princ = RT::Principal->new($RT::SystemUser);
+ $princ->Load($PrincipalId);
+ if ($princ->PrincipalType eq 'User') {
+ my $group = RT::Group->new($RT::SystemUser);
+ $group->LoadACLEquivalenceGroup($princ);
+ $PrincipalId = $group->PrincipalId;
+ }
+
+
+ my $ACLObj = new RT::ACL($session{'CurrentUser'});
+ my $ACE = new RT::ACE($session{'CurrentUser'});
+
+
+ $ACLObj->LimitToObject( $Object);
+ $ACLObj->LimitToPrincipal( Id => $PrincipalId);
+ $ACLObj->OrderBy(FIELD=>'RightName');
+
+ if (ref($Object) && UNIVERSAL::can($Object, 'AvailableRights')) {
+ %Rights = %{$Object->AvailableRights};
+ }
+
+ else {
+ %Rights = ( loc('System Error') => loc("No rights found") );
+ }
+
+ $ACLDesc = "$PrincipalId-".ref($Object)."-".$Object->Id;
+</%INIT>
+
+<%ARGS>
+$PrincipalType => undef
+$PrincipalId => undef
+$Object =>undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectScrip b/rt/html/Admin/Elements/SelectScrip
new file mode 100644
index 0000000..027efba
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectScrip
@@ -0,0 +1,72 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+<option value=""
+<% $Default eq undef && 'SELECTED' %>
+>-</option>
+%while (my $Scrip = $Scrips->Next) {
+<option value="<% $Scrip->Id %>"
+<% $Scrip->Id == $Default && 'SELECTED' %>
+><% loc($Scrip->Name) %>
+</option>
+%}
+</select>
+
+<%INIT>
+my $Scrips = RT::Scrips->new($session{'CurrentUser'});
+$Scrips->UnLimit;
+
+
+
+</%INIT>
+<%ARGS>
+
+$Default => undef
+$Name => 'Scrip'
+
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectScripAction b/rt/html/Admin/Elements/SelectScripAction
new file mode 100644
index 0000000..3bf2f17
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectScripAction
@@ -0,0 +1,73 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+<option value=""
+<% ! defined $Default && 'SELECTED' %>
+>-</option>
+%while (my $ScripAction = $ScripActions->Next) {
+<option value="<%$ScripAction->Id%>"
+<% defined $Default && $ScripAction->Id == $Default && 'SELECTED' %>
+><% loc($ScripAction->Name) %>
+</option>
+%}
+</select>
+
+<%INIT>
+my $ScripActions = RT::ScripActions->new($session{'CurrentUser'});
+$ScripActions->UnLimit;
+$ScripActions->OrderBy(FIELD => 'Name');
+
+
+
+</%INIT>
+<%ARGS>
+
+$Default => undef
+$Name => 'ScripAction'
+
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectScripCondition b/rt/html/Admin/Elements/SelectScripCondition
new file mode 100644
index 0000000..3068730
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectScripCondition
@@ -0,0 +1,72 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+<option value=""
+<% ! defined $Default && 'SELECTED' %>
+>-</option>
+%while (my $ScripCondition = $ScripConditions->Next) {
+<option value="<%$ScripCondition->Id%>"
+<% defined $Default && $ScripCondition->Id == $Default && 'SELECTED' %>
+><% loc($ScripCondition->Name) %>
+</option>
+%}
+</select>
+
+<%INIT>
+my $ScripConditions = RT::ScripConditions->new($session{'CurrentUser'});
+$ScripConditions->UnLimit;
+$ScripConditions->OrderBy(FIELD => 'Name');
+
+
+</%INIT>
+<%ARGS>
+
+$Default => undef
+$Name => 'ScripCondition'
+
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectSingleOrMultiple b/rt/html/Admin/Elements/SelectSingleOrMultiple
new file mode 100644
index 0000000..4844b8d
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectSingleOrMultiple
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+ <select name="<%$Name%>">
+ <option value="1" <%$SingleDefault%>><&|/l&>Single</&></option>
+ <option value="0" <%$MultipleDefault%>><&|/l&>Multiple</&></option>
+ </select>
+
+
+<%INIT>
+my ($SingleDefault, $MultipleDefault);
+if ($Default == 1) {
+ $SingleDefault = "SELECTED";
+}
+elsif ($Default == 0 ) {
+ $MultipleDefault = "SELECTED";
+}
+
+</%INIT>
+<%ARGS>
+$Name => 'Single'
+$Default => 1
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectStage b/rt/html/Admin/Elements/SelectStage
new file mode 100644
index 0000000..68bf485
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectStage
@@ -0,0 +1,66 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+% foreach my $stage (@stages) {
+<option value="<%$stage%>"
+<% ($stage eq $Default) && 'SELECTED' %>
+><% loc($stage) %>
+</option>
+% }
+<%INIT>
+if ($Default eq '') {
+ $Default = 'TransactionCreate';
+}
+my @stages = 'TransactionCreate';
+push @stages, 'TransactionBatch' if $RT::UseTransactionBatch;
+push @stages, 'Disabled';
+</%INIT>
+<%ARGS>
+$Default => 'TransactionCreate'
+$Name => 'Stage'
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectTemplate b/rt/html/Admin/Elements/SelectTemplate
new file mode 100644
index 0000000..4f002c7
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectTemplate
@@ -0,0 +1,87 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+<option value=""
+<% $Default eq 'none' && 'SELECTED' %>
+>-</option>
+%while (my $Template = $PrimaryTemplates->Next) {
+<option value="<%$Template->Id%>"
+<% ($Template->Id == $Default) && 'SELECTED' %>
+><% loc($Template->Name) %>
+</option>
+%}
+%while (my $Template = $OtherTemplates->Next) {
+<option value="<%$Template->Id%>"
+<% ($Template->Id == $Default) && 'SELECTED'%>
+><&|/l, loc($Template->Name) &>Global template: [_1]</&>
+</option>
+%}
+</select>
+
+<%INIT>
+
+
+my $PrimaryTemplates = RT::Templates->new($session{'CurrentUser'});
+if ($Queue != 0) {
+$PrimaryTemplates->LimitToQueue($Queue);
+$PrimaryTemplates->OrderBy(FIELD => 'Name');
+}
+
+my $OtherTemplates = RT::Templates->new($session{'CurrentUser'});
+$OtherTemplates->LimitToGlobal($DefaultQueue);
+$OtherTemplates->OrderBy(FIELD => 'Name');
+
+</%INIT>
+<%ARGS>
+
+$Queue => undef
+$Default => 'none'
+$DefaultQueue => undef
+$Name => 'Template'
+
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SelectUsers b/rt/html/Admin/Elements/SelectUsers
new file mode 100644
index 0000000..103ab30
--- /dev/null
+++ b/rt/html/Admin/Elements/SelectUsers
@@ -0,0 +1,64 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select multiple name="<%$Name%>" size="10">
+%while (my $user = $users->Next) {
+<option value="<%$user->id%>"><%$user->Name%>
+%}
+</select>
+
+<%INIT>
+my $users = new RT::Users($session{'CurrentUser'});
+
+$users->Limit(FIELD => 'id', VALUE => $RT::SystemUser->id, OPERATOR => '!=' );
+$users->Limit(FIELD => 'id', VALUE => $RT::Nobody->id, OPERATOR => '!=' );
+$users->LimitToPrivileged();
+
+</%INIT>
+<%ARGS>
+$Name => 'Users'
+</%ARGS>
diff --git a/rt/html/Admin/Elements/SystemTabs b/rt/html/Admin/Elements/SystemTabs
new file mode 100644
index 0000000..fd317c4
--- /dev/null
+++ b/rt/html/Admin/Elements/SystemTabs
@@ -0,0 +1,97 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Tabs, subtabs => $tabs,
+ current_tab => 'Admin/Global/',
+ current_subtab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+ my $tabs = {
+
+ A => { title => loc('Scrips'),
+ path => 'Admin/Global/Scrips.html',
+ },
+ B => { title => loc('Templates'),
+ path => 'Admin/Global/Templates.html',
+ },
+
+ F => { title => loc('Custom Fields'),
+ path => 'Admin/Global/CustomFields/index.html',
+ },
+
+ G => { title => loc('Group Rights'),
+ path => 'Admin/Global/GroupRights.html',
+ },
+ H => { title => loc('User Rights'),
+ path => 'Admin/Global/UserRights.html',
+ },
+ I => { title => loc('RT at a glance'),
+ path => 'Admin/Global/MyRT.html',
+ },
+
+};
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+ foreach my $tab (sort keys %{$tabs}) {
+ if ($tabs->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+ }
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$current_tab => undef
+$subtabs => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/Tabs b/rt/html/Admin/Elements/Tabs
new file mode 100644
index 0000000..40df82a
--- /dev/null
+++ b/rt/html/Admin/Elements/Tabs
@@ -0,0 +1,93 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Tabs,
+ tabs => $tabs,
+ current_toptab => 'Admin/',
+ current_tab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+ my $tabs = { A => { title => loc('Users'),
+ path => 'Admin/Users/',
+ },
+ B => { title => loc('Groups'),
+ path => 'Admin/Groups/',
+ },
+ C => { title => loc('Queues'),
+ path => 'Admin/Queues/',
+ },
+ D => { 'title' => loc('Custom Fields'),
+ path => 'Admin/CustomFields/',
+ },
+ E => { 'title' => loc('Global'),
+ path => 'Admin/Global/',
+ },
+ F => { 'title' => loc('Tools'),
+ path => 'Admin/Tools/',
+ },
+ };
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+ foreach my $tab (sort keys %{$tabs}) {
+ if ($tabs->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+ }
+
+</%INIT>
+
+
+<%ARGS>
+$subtabs => undef
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/ToolTabs b/rt/html/Admin/Elements/ToolTabs
new file mode 100755
index 0000000..37ba33e
--- /dev/null
+++ b/rt/html/Admin/Elements/ToolTabs
@@ -0,0 +1,80 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Tabs, subtabs => $tabs,
+ current_tab => 'Admin/Tools/',
+ current_subtab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+ my $tabs = {
+
+ A => { title => loc('System Configuration'),
+ path => 'Admin/Tools/Configuration.html',
+ }
+
+};
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+ foreach my $tab (sort keys %{$tabs}) {
+ if ($tabs->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+ }
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$current_tab => undef
+$subtabs => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Elements/UserTabs b/rt/html/Admin/Elements/UserTabs
new file mode 100644
index 0000000..f5f8d0e
--- /dev/null
+++ b/rt/html/Admin/Elements/UserTabs
@@ -0,0 +1,113 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Tabs,
+ subtabs => $tabs,
+ current_tab => 'Admin/Users/',
+ current_subtab => $current_tab,
+ Title => $Title &>
+<%INIT>
+my $tabs;
+if ($id) {
+$tabs->{'this'} = { title => eval { $UserObj->Name },
+
+ path => "Admin/Users/Modify.html?id=".$id,
+subtabs => {
+ Basics => { title => loc('Basics'),
+ path => "Admin/Users/Modify.html?id=".$id
+ },
+ Memberships => { title => loc('Memberships'),
+ path => "Admin/Users/Memberships.html?id=".$id
+ },
+ History => { title => loc('History'),
+ path => "Admin/Users/History.html?id=".$id
+ },
+ 'MyRT' => { title => loc('RT at a glance'),
+ path => "Admin/Users/MyRT.html?id=".$id
+ },
+# Scrips => { title => loc('Rights'),
+# path => "Admin/Users/Rights.html?id=".$id
+# }
+
+ }
+}
+}
+if ($session{'CurrentUser'}->HasRight( Object => $RT::System, Right => 'AdminUsers')) {
+ $tabs->{"A"} = { title => loc('Select user'),
+ path => "Admin/Users/",
+ };
+ $tabs->{"B"} = { title => loc('New user'),
+ path => "Admin/Users/Modify.html?Create=1",
+ separator => 1,
+ };
+}
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+#foreach my $tab ( sort keys %{$tabs} ) {
+# if ( $tabs->{$tab}->{'path'} eq $current_subtab ) {
+# $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+# }
+#}
+foreach my $tab ( sort keys %{$tabs->{'this'}->{'subtabs'}} ) {
+ if ( $tabs->{'this'}->{'subtabs'}->{$tab}->{'path'} eq $current_tab ) {
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{'this'}->{'subtabs'}->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+}
+$tabs->{'this'}->{"current_subtab"} = $current_tab;
+$current_tab = "Admin/Users/Modify.html?id=".$id if $id;
+</%INIT>
+<%ARGS>
+$UserObj => undef
+$id => undef
+$current_tab => undef
+$subtabs => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Admin/Global/CustomFields/Groups.html b/rt/html/Admin/Global/CustomFields/Groups.html
new file mode 100644
index 0000000..62bd31c
--- /dev/null
+++ b/rt/html/Admin/Global/CustomFields/Groups.html
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/GlobalCustomFieldTabs,
+ current_tab => "Admin/Global/CustomFields/Groups.html",
+ current_subtab => "Admin/Global/CustomFields/Groups.html",
+ Title => $title
+ &>
+ <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, ObjectType => 'RT::Group', Object=> $object &>
+<%INIT>
+ my $title = loc( 'Edit Custom Fields for all groups');
+ my $object = RT::Group->new($session{'CurrentUser'});
+</%INIT>
diff --git a/rt/html/Admin/Global/CustomFields/Queue-Tickets.html b/rt/html/Admin/Global/CustomFields/Queue-Tickets.html
new file mode 100755
index 0000000..3feb424
--- /dev/null
+++ b/rt/html/Admin/Global/CustomFields/Queue-Tickets.html
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/GlobalCustomFieldTabs,
+ current_tab => "Admin/Global/CustomFields/Queue-Tickets.html",
+ current_subtab => "Admin/Global/CustomFields/Queue-Tickets.html",
+ Title => $title
+ &>
+ <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, ObjectType => 'RT::Queue', Object=> $object, SubType => 'RT::Ticket' &>
+<%INIT>
+ my $title = loc( 'Edit Custom Fields for tickets in all queues');
+ my $object = RT::Queue->new($session{'CurrentUser'});
+</%INIT>
diff --git a/rt/html/Admin/Global/CustomFields/Queue-Transactions.html b/rt/html/Admin/Global/CustomFields/Queue-Transactions.html
new file mode 100755
index 0000000..5f58a18
--- /dev/null
+++ b/rt/html/Admin/Global/CustomFields/Queue-Transactions.html
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/GlobalCustomFieldTabs,
+ current_tab => "Admin/Global/CustomFields/Queue-Transactions.html",
+ current_subtab => "Admin/Global/CustomFields/Queue-Transactions.html",
+ Title => $title
+ &>
+ <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, ObjectType => 'RT::Queue', Object=> $object, SubType => 'RT::Ticket-RT::Transaction' &>
+<%INIT>
+ my $title = loc( 'Edit Custom Fields for tickets in all queues');
+ my $object = RT::Queue->new($session{'CurrentUser'});
+</%INIT>
diff --git a/rt/html/Admin/Global/CustomFields/Users.html b/rt/html/Admin/Global/CustomFields/Users.html
new file mode 100644
index 0000000..6f965b7
--- /dev/null
+++ b/rt/html/Admin/Global/CustomFields/Users.html
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/GlobalCustomFieldTabs,
+ current_tab => "Admin/Global/CustomFields/Users.html",
+ current_subtab => "Admin/Global/CustomFields/Users.html",
+ Title => $title
+ &>
+ <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, ObjectType => 'RT::User', Object=> $object &>
+<%INIT>
+ my $title = loc( 'Edit Custom Fields for all users');
+ my $object = RT::User->new($session{'CurrentUser'});
+</%INIT>
diff --git a/rt/html/Admin/Global/CustomFields/index.html b/rt/html/Admin/Global/CustomFields/index.html
new file mode 100644
index 0000000..b5c9fe8
--- /dev/null
+++ b/rt/html/Admin/Global/CustomFields/index.html
@@ -0,0 +1,93 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Callback, tabs => $tabs, %ARGS &>
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/GlobalCustomFieldTabs, Title => $title &>
+
+<ul>
+% foreach my $key (sort keys %$tabs) {
+<li><span><a href="<% $tabs->{$key}{path} %>"><% $tabs->{$key}{title} %></a></span><br />
+<% $tabs->{$key}{text} %>
+</li>
+% }
+</ul>
+
+<%INIT>
+my $title = loc("Global custom field configuration");
+
+my $tabs = {
+
+ A => {
+ title => loc('Users'),
+ text => loc('Select custom fields for all users'),
+ path => 'Users.html',
+ },
+ B => {
+ title => loc('Groups'),
+ text => loc('Select custom fields for all user groups'),
+ path => 'Groups.html',
+ },
+
+ F => {
+ title => loc('Tickets'),
+ text => loc('Select custom fields for tickets in all queues'),
+ path => 'Queue-Tickets.html',
+ },
+
+ G => {
+ title => loc('Ticket Transactions'),
+ text =>
+ loc('Select custom fields for transactions on tickets in all queues'),
+ path => 'Queue-Transactions.html',
+ },
+
+};
+
+
+$m->comp('/Elements/Callback', tabs => $tabs);
+</%INIT>
diff --git a/rt/html/Admin/Global/GroupRights.html b/rt/html/Admin/Global/GroupRights.html
new file mode 100644
index 0000000..7c863d5
--- /dev/null
+++ b/rt/html/Admin/Global/GroupRights.html
@@ -0,0 +1,123 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify global group rights') &>
+<& /Admin/Elements/SystemTabs,
+ current_tab => 'Admin/Global/GroupRights.html',
+ Title => loc('Modify global group rights') &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="GroupRights.html">
+
+<&| /Widgets/TitleBox, title => loc('Modify global group rights.')&>
+
+<h1><&|/l&>System groups</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToSystemInternalGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% loc($Group->Type) %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object =>$RT::System &>
+ </td>
+ </tr>
+% }
+</table>
+<h1><&|/l&>Roles</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToRolesForSystem();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% loc($Group->Type) %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object => $RT::System &>
+ </td>
+ </tr>
+% }
+</table>
+<h1><&|/l&>User defined groups</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToUserDefinedGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% $Group->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object => $RT::System &>
+ </td>
+ </tr>
+% }
+</table>
+
+ </&>
+ <& /Elements/Submit, Label => loc('Modify Group Rights'), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+ #Update the acls.
+ my @results = ProcessACLChanges(\%ARGS);
+
+
+my $Groups;
+
+</%INIT>
+
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/Admin/Global/MyRT.html b/rt/html/Admin/Global/MyRT.html
new file mode 100644
index 0000000..8bbcb5d
--- /dev/null
+++ b/rt/html/Admin/Global/MyRT.html
@@ -0,0 +1,104 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc("RT at a glance") &>
+<& /Admin/Elements/SystemTabs,
+ current_tab => 'Admin/Global/MyRT.html',
+ Title => loc("RT at a glance"),
+&>
+
+<& /Widgets/SelectionBox:header, nojs => 1 &>
+
+<& /Elements/ListActions, actions => \@actions &>
+<br />
+% for my $pane (@panes) {
+<&|/Widgets/TitleBox, title => loc('RT at a glance').': '.loc($pane->{Name}), bodyclass => "" &>
+<& /Widgets/SelectionBox:show, self => $pane, nojs => 1 &></&>
+<br />
+% }
+<%init>
+my @actions;
+
+my @items = map { [ "component-$_", $_ ] } sort @{$RT::HomepageComponents};
+my $sys = RT::System->new( $session{'CurrentUser'} );
+# XXX: put this in savedsearches_to_portlet_items
+for ( $m->comp( "/Search/Elements/SearchesForObject",
+ Object => $sys )) {
+ my ( $desc, $search ) = @$_;
+ my $SearchType = $search->Content->{'SearchType'} || 'Ticket';
+ if ( $SearchType eq 'Ticket' ) {
+ push @items, [ "system-$desc", $desc ];
+ } else {
+ my $oid = ref($sys) . '-' . $sys->Id . '-SavedSearch-' . $search->Id;
+ my $type =
+ ( $SearchType eq 'Ticket' )
+ ? 'Saved Search' : $SearchType; # loc
+ push @items, [ "saved-$oid", loc($type) . ": $desc" ];
+ }
+}
+
+my ($default_portlets) = $sys->Attributes->Named('HomepageSettings');
+
+my @panes = $m->comp(
+ '/Admin/Elements/ConfigureMyRT',
+ panes => ['body', 'summary'],
+ Action => 'MyRT.html',
+ items => \@items,
+ current_portlets => $default_portlets->Content,
+ OnSave => sub {
+ my ( $conf, $pane ) = @_;
+ $default_portlets->SetContent( $conf );
+ push @actions, loc( 'Global portlet [_1] saved.', $pane );
+ }
+);
+
+$m->comp( '/Widgets/SelectionBox:process', %ARGS, self => $_, nojs => 1 )
+ for @panes;
+
+
+</%init>
+
diff --git a/rt/html/Admin/Global/Scrip.html b/rt/html/Admin/Global/Scrip.html
new file mode 100644
index 0000000..fc72ca9
--- /dev/null
+++ b/rt/html/Admin/Global/Scrip.html
@@ -0,0 +1,87 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/SystemTabs,
+ current_tab => 'Admin/Global/Scrips.html',
+ current_subtab => $current_subtab,
+ subtabs => $subtabs,
+ Title => $title &>
+
+<& /Elements/ListActions, actions => \@results &>
+<& /Admin/Elements/EditScrip, title => $title, %ARGS, id => $id &>
+
+<%init>
+my $subtabs = {
+ A => {
+ title => loc('Select scrip'),
+ path => "Admin/Global/Scrips.html",
+ },
+ B => {
+ title => loc('New scrip'),
+ path => "Admin/Global/Scrip.html?create=1&Queue=0",
+ separator => 1,
+ },
+};
+
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+my ($id, @results) = $m->comp( '/Admin/Elements/EditScrip:Process', %ARGS );
+
+my ($title, $current_subtab);
+if ( $id ) {
+ $current_subtab = "Admin/Global/Scrip.html?id=$id&Queue=0";
+ $title = loc("Modify a scrip which applies to all queues");
+ $subtabs->{"C"} = {
+ title => loc('Scrip #[_1]', $id),
+ path => "Admin/Global/Scrip.html?id=$id&Queue=0",
+ };
+}
+else {
+ $current_subtab = "Admin/Global/Scrip.html?create=1&Queue=0";
+ $title = loc("Add a scrip which will apply to all queues");
+}
+</%init>
diff --git a/rt/html/Admin/Global/Scrips.html b/rt/html/Admin/Global/Scrips.html
new file mode 100644
index 0000000..a5fe267
--- /dev/null
+++ b/rt/html/Admin/Global/Scrips.html
@@ -0,0 +1,77 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/SystemTabs,
+ current_tab => 'Admin/Global/Scrips.html',
+ current_subtab => 'Admin/Global/Scrips.html',
+ subtabs => $subtabs,
+ Title => $title &>
+<& /Admin/Elements/EditScrips, title => $title, id => $id, %ARGS &>
+</form>
+<%init>
+
+my $subtabs = {
+ A => { title => loc('Select scrip'),
+ path => "Admin/Global/Scrips.html",
+ },
+ B => { title => loc('New scrip'),
+ path => "Admin/Global/Scrip.html?create=1&Queue=0",
+ separator => 1,
+ }
+ };
+my $title = loc("Modify scrips which apply to all queues");
+
+my (@actions);
+
+</%init>
+
+
+
+<%ARGS>
+$id => 0
+</%ARGS>
diff --git a/rt/html/Admin/Global/Template.html b/rt/html/Admin/Global/Template.html
new file mode 100644
index 0000000..9d5a409
--- /dev/null
+++ b/rt/html/Admin/Global/Template.html
@@ -0,0 +1,125 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc("Modify template [_1]", $TemplateObj->id) &>
+<& /Admin/Elements/SystemTabs,
+ current_tab => 'Admin/Global/Templates.html',
+ current_subtab => $current_subtab,
+ subtabs => $subtabs,
+ Title => loc("Modify template [_1]", $TemplateObj->id) &>
+<& /Elements/ListActions, actions => \@results &>
+
+
+<form method="post" action="Template.html">
+%if ($Create ) {
+<input type="hidden" class="hidden" name="Template" value="new" />
+% } else {
+<input type="hidden" class="hidden" name="Template" value="<%$TemplateObj->Id%>" />
+% }
+
+%# hang onto the queue id
+<input type="hidden" class="hidden" name="Queue" value="<%$Queue%>" />
+
+<& /Admin/Elements/ModifyTemplate, Name => $TemplateObj->Name, Description => $TemplateObj->Description, Content => $TemplateObj->Content &>
+
+<& /Elements/Submit, Label => loc('Save Changes'), Reset => 1 &>
+</form>
+
+
+
+<%INIT>
+
+my $TemplateObj = new RT::Template($session{'CurrentUser'});
+my ($title, @results, $current_subtab);
+
+my $subtabs = {
+ A => { title => loc('Select template'),
+ path => "Admin/Global/Templates.html"
+ },
+ B => { title => loc('New template'),
+ path => "Admin/Global/Template.html?Create=1&Queue=0",
+ separator => 1,
+ }
+ };
+
+
+if ($Create) {
+ $current_subtab = "Admin/Global/Template.html?Create=1&Queue=0";
+ $title = loc("Create a template");
+}
+
+else {
+ if ($Template eq 'new') {
+ my ($val, $msg) = $TemplateObj->Create(Queue => $Queue, Name => $Name);
+ Abort(loc("Could not create template: [_1]", $msg)) unless ($val);
+ push @results, $msg;
+ }
+ else {
+ $TemplateObj->Load($Template) || Abort(loc('No Template'));
+ }
+ $title = loc('Modify template [_1]', loc($TemplateObj->Name()));
+
+
+}
+if ($TemplateObj->Id()) {
+ my @attribs = qw( Description Content Queue Name);
+ my @aresults = UpdateRecordObject( AttributesRef => \@attribs,
+ Object => $TemplateObj,
+ ARGSRef => \%ARGS);
+ $current_subtab = "Admin/Global/Template.html?Queue=0&Template=".$TemplateObj->Id();
+ $subtabs->{"C"} = { title => loc('Template #[_1]', $TemplateObj->Id()),
+ path => "Admin/Global/Template.html?Queue=0&Template=".$TemplateObj->Id(),
+ };
+ push @results, @aresults;
+}
+</%INIT>
+<%ARGS>
+$Queue => undef
+$Template => undef
+$Create => undef
+$Name => undef
+</%ARGS>
diff --git a/rt/html/Admin/Global/Templates.html b/rt/html/Admin/Global/Templates.html
new file mode 100644
index 0000000..afd290d
--- /dev/null
+++ b/rt/html/Admin/Global/Templates.html
@@ -0,0 +1,77 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title, FeedURI => 'templates' &>
+<& /Admin/Elements/SystemTabs,
+ current_tab => 'Admin/Global/Templates.html',
+ current_subtab => 'Admin/Global/Templates.html',
+ subtabs => $subtabs,
+ Title => $title &>
+<& /Admin/Elements/EditTemplates, title => $title, %ARGS &>
+</form>
+<%init>
+
+my $subtabs = {
+ A => { title => loc('Select template'),
+ path => "Admin/Global/Templates.html"
+ },
+ B => { title => loc('New template'),
+ path => "Admin/Global/Template.html?Create=1&Queue=0",
+ separator => 1,
+ }
+ };
+my $title = loc("Modify templates which apply to all queues");
+
+my (@actions);
+
+</%init>
+
+
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Global/UserRights.html b/rt/html/Admin/Global/UserRights.html
new file mode 100644
index 0000000..9276b1a
--- /dev/null
+++ b/rt/html/Admin/Global/UserRights.html
@@ -0,0 +1,101 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify global user rights') &>
+<& /Admin/Elements/SystemTabs,
+ current_tab => 'Admin/Global/UserRights.html',
+ Title => loc('Modify global user rights') &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="UserRights.html">
+
+<&| /Widgets/TitleBox, title => loc('Modify global user rights.') &>
+
+<table>
+
+% while (my $Member = $Users->Next()) {
+% my $UserObj = $Member->MemberObj->Object();
+% my $group = RT::Group->new($session{'CurrentUser'});
+% $group->LoadACLEquivalenceGroup($Member->MemberObj);
+ <tr align="right">
+ <td valign="top">
+ <% $UserObj->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $group->PrincipalId,
+ Object => $RT::System &>
+ </td>
+ </tr>
+% }
+</table>
+
+ </&>
+ <& /Elements/Submit, Label => loc('Modify User Rights'), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+ #Update the acls.
+ my @results = ProcessACLChanges(\%ARGS);
+
+# {{{ Deal with setting up the display of current rights.
+
+
+# Find out which users we want to display ACL selects for
+my $Privileged = RT::Group->new($session{'CurrentUser'});
+$Privileged->LoadSystemInternalGroup('Privileged');
+my $Users = $Privileged->MembersObj();
+
+
+
+# }}}
+
+</%INIT>
+
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/Admin/Global/index.html b/rt/html/Admin/Global/index.html
new file mode 100644
index 0000000..77e9caa
--- /dev/null
+++ b/rt/html/Admin/Global/index.html
@@ -0,0 +1,94 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Callback, tabs => $tabs, %ARGS &>
+<& /Admin/Elements/Header, Title => loc('Admin/Global configuration') &>
+<& /Admin/Elements/SystemTabs,
+ Title => loc('Admin/Global configuration') &>
+
+<ul>
+% foreach my $key (sort keys %$tabs) {
+<li><span><a href="<% $tabs->{$key}{path} %>"><% $tabs->{$key}{title} %></a></span><br />
+<% $tabs->{$key}{text} %>
+</li>
+% }
+</ul>
+
+<%INIT>
+ my $tabs = {
+
+ A => { title => loc('Scrips'),
+ text => loc('Modify scrips which apply to all queues'),
+ path => 'Scrips.html',
+ },
+ B => { title => loc('Templates'),
+ text => loc('Edit system templates'),
+ path => 'Templates.html',
+ },
+
+ F => { title => loc('Custom Fields'),
+ text => loc('Modify global custom fields'),
+ path => 'CustomFields/index.html',
+ },
+
+ G => { title => loc('Group Rights'),
+ text => loc('Modify global group rights'),
+ path => 'GroupRights.html',
+ },
+ H => { title => loc('User Rights'),
+ text => loc('Modify global user rights'),
+ path => 'UserRights.html',
+ },
+ I => { title => loc('RT at a glance'),
+ text => loc('Modify the default "RT at a glance" view'),
+ path => 'MyRT.html',
+ },
+
+
+
+};
+</%INIT>
diff --git a/rt/html/Admin/Groups/CustomFields.html b/rt/html/Admin/Groups/CustomFields.html
new file mode 100644
index 0000000..8ffa54a
--- /dev/null
+++ b/rt/html/Admin/Groups/CustomFields.html
@@ -0,0 +1,48 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/ObjectCustomFields, %ARGS, ObjectType => 'RT::Group' &>
diff --git a/rt/html/Admin/Groups/GroupRights.html b/rt/html/Admin/Groups/GroupRights.html
new file mode 100644
index 0000000..b81b6a9
--- /dev/null
+++ b/rt/html/Admin/Groups/GroupRights.html
@@ -0,0 +1,119 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify group rights for group [_1]', $GroupObj->Name) &>
+<& /Admin/Elements/GroupTabs,
+ GroupObj => $GroupObj,
+ current_tab => 'Admin/Groups/GroupRights.html?id='.$id,
+ Title => loc('Modify group rights for group [_1]', $GroupObj->Name) &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="GroupRights.html">
+ <input type="hidden" class="hidden" name="id" value="<% $GroupObj->id %>" />
+
+<&| /Widgets/TitleBox, title => loc('Modify group rights for group [_1]', $GroupObj->Name) &>
+
+<h1><&|/l&>System groups</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToSystemInternalGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% loc($Group->Type) %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ PrincipalType => 'Group',
+ Object => $GroupObj &>
+ </td>
+ </tr>
+% }
+</table>
+<h1><&|/l&>User defined groups</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToUserDefinedGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% $Group->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ PrincipalType => 'Group',
+ Object => $GroupObj &>
+ </td>
+ </tr>
+% }
+</table>
+
+ </&>
+ <& /Elements/Submit, Label => loc('Modify Group Rights'), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+ #Update the acls.
+ my @results = ProcessACLChanges(\%ARGS);
+
+
+if (!defined $id) {
+ Abort(loc("No Group defined"));
+}
+
+my $GroupObj = RT::Group->new($session{'CurrentUser'});
+$GroupObj->Load($id) || Abort(loc("Couldn't load group [_1]",$id));
+
+my $Groups;
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Groups/History.html b/rt/html/Admin/Groups/History.html
new file mode 100644
index 0000000..ee1d553
--- /dev/null
+++ b/rt/html/Admin/Groups/History.html
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/GroupTabs,
+ id => $id,
+ GroupObj => $GroupObj,
+ current_subtab => $current_tab,
+ Title => $title &>
+
+<& /Ticket/Elements/ShowHistory,
+ Ticket => $GroupObj,
+ ShowDisplayModes => 0,
+&>
+
+<%INIT>
+my $current_tab = 'Admin/Groups/History.html?id='.$id;
+my $GroupObj = new RT::Group($session{'CurrentUser'});
+$GroupObj->Load($id) || Abort("Couldn't load group '$id'");
+my $title = loc("History of the group [_1]", $GroupObj->Name);
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Groups/Members.html b/rt/html/Admin/Groups/Members.html
new file mode 100644
index 0000000..77c3fb0
--- /dev/null
+++ b/rt/html/Admin/Groups/Members.html
@@ -0,0 +1,168 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => "RT/Admin/Edit the group ". $Group->Name &>
+<& /Admin/Elements/GroupTabs, GroupObj => $Group,
+ current_tab => 'Admin/Groups/Members.html?id='.$id,
+ Title => "RT/Admin/Edit the group ". $Group->Name &>
+<& /Elements/ListActions, actions => \@results &>
+
+
+<&| /Widgets/TitleBox, title => loc('Editing membership for group [_1]', $Group->Name) &>
+
+<form action="<%$RT::WebPath%>/Admin/Groups/Members.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<%$Group->Id%>" />
+<table width="100%">
+<tr>
+<td>
+<h3><&|/l&>Current members</&></h3>
+</td>
+<td>
+<h3><&|/l&>Add members</&></h3>
+</td>
+</tr>
+
+<tr>
+<td valign="top">
+
+% if ($Group->MembersObj->Count == 0 ) {
+<em><&|/l&>(No members)</&></em>
+% } else {
+<em><&|/l&>(Check box to delete)</&></em>
+<br />
+<br />
+<&|/l&>Users</&>
+% my $Users = $Group->UserMembersObj;
+% $Users->OrderBy( FIELD => $UserOrderBy, ORDER => $UserOrder );
+<ul>
+% while (my $user = $Users->Next()) {
+% $UsersSeen{$user->id} = 1 if $SkipSeenUsers;
+<li><input type="checkbox" class="checkbox" name="DeleteMember-<%$user->PrincipalObj->Id%>" value="1" />
+<%$user->Name%> (<%$user->RealName%>)
+% }
+</ul>
+<&|/l&>Groups</&>
+<ul>
+% my $GroupMembers = $Group->MembersObj;
+% $GroupMembers->LimitToGroups();
+% while (my $member = $GroupMembers->Next()) {
+% $GroupsSeen{$member->MemberId} = 1 if $SkipSeenGroups;
+<li><input type="checkbox" class="checkbox" name="DeleteMember-<%$member->MemberId%>" value="1" />
+<%$member->MemberObj->Object->Name%>
+% }
+</ul>
+% }
+</td>
+<td valign="top">
+<& /Admin/Elements/SelectNewGroupMembers, Name => "AddMembers", Group => $Group,
+ SkipUsers => \%UsersSeen, SkipGroups => \%GroupsSeen &>
+</td>
+</tr>
+</table>
+</&>
+<& /Elements/Submit, Label => loc('Modify Members'), Reset => 1 &>
+</form>
+
+
+<%INIT>
+
+my $Group = new RT::Group($session{'CurrentUser'});
+$Group->Load($id) || Abort(loc('Could not load group'));
+
+my (@results);
+
+my $key;
+foreach $key (keys %ARGS) {
+
+if ($key =~ /^DeleteMember-(\d+)$/) {
+ my $id = $1;
+ my ($val,$msg) = $Group->DeleteMember($id);
+ push (@results, $msg);
+}
+}
+
+# Make sure AddMembers is always an array
+my @AddMembers = (
+ ((ref $AddMembersUsers eq 'ARRAY') ? @{$AddMembersUsers} : ($AddMembersUsers)),
+ ((ref $AddMembersGroups eq 'ARRAY') ? @{$AddMembersGroups} : ($AddMembersGroups)),
+);
+
+foreach my $member (@AddMembers) {
+ next unless ($member);
+
+ my $principal;
+
+ if ($member =~ /^Group-(\d+)$/) {
+ $principal = RT::Group->new($session{'CurrentUser'});
+ $principal->Load($1);
+ } elsif ($member =~ /^User-(\d+)$/) {
+ $principal = RT::User->new($session{'CurrentUser'});
+ $principal->Load($1);
+ } else {
+ next;
+ }
+
+
+ my ($val, $msg) = $Group->AddMember($principal->PrincipalId);
+ push (@results, $msg);
+}
+
+my %UsersSeen;
+my %GroupsSeen;
+$GroupsSeen{$Group->id} = 1; # can't be a member of ourself
+
+</%INIT>
+
+<%ARGS>
+$AddMembersUsers => undef
+$AddMembersGroups => undef
+$id => undef
+$UserOrderBy => 'Name'
+$UserOrder => 'ASC'
+$SkipSeenUsers => 1
+$SkipSeenGroups => 1
+</%ARGS>
diff --git a/rt/html/Admin/Groups/Modify.html b/rt/html/Admin/Groups/Modify.html
new file mode 100644
index 0000000..f3c13fe
--- /dev/null
+++ b/rt/html/Admin/Groups/Modify.html
@@ -0,0 +1,174 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+
+<& /Admin/Elements/GroupTabs,
+ GroupObj => $Group,
+ current_tab => $current_tab,
+ Title => $title &>
+<& /Elements/ListActions, actions => \@results &>
+
+
+
+<form action="<%$RT::WebPath%>/Admin/Groups/Modify.html" method="post" enctype="multipart/form-data">
+
+%unless ($Group->Id) {
+<input type="hidden" class="hidden" name="id" value="new" />
+% } else {
+<input type="hidden" class="hidden" name="id" value="<%$Group->Id%>" />
+% }
+<table>
+<tr><td align="right">
+<&|/l&>Name</&>:
+</td>
+<td><input name="Name" value="<%$Group->Name%>" /></td>
+</tr>
+<tr>
+<td align="right">
+<&|/l&>Description</&>:</td><td colspan="3"><input name="Description" value="<%$Group->Description%>" size="60" /></td>
+</tr>
+% my $CFs = $Group->CustomFields;
+% while (my $CF = $CFs->Next) {
+<tr valign="top"><td align="right">
+<% $CF->Name %>:
+</td><td>
+<& /Elements/EditCustomField, CustomField => $CF,
+ Object => $Group,
+ ($Create ? (NamePrefix => 'Object-RT::Group--CustomField-')
+ : () )&>
+</td></tr>
+% }
+<tr>
+<td colspan="2">
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <%$EnabledChecked%> /> <&|/l&>Enabled (Unchecking this box disables this group)</&><br />
+</td>
+</tr>
+<& /Elements/Callback, GroupObj => $Group, results => \@results, %ARGS &>
+</table>
+<& /Elements/Submit, Label => loc('Save Changes'), Reset => 1 &>
+</form>
+<%INIT>
+
+my $current_tab;
+my ($title, @results, $Disabled, $EnabledChecked);
+
+my $Group = RT::Group->new($session{'CurrentUser'});
+
+if ($Create) {
+ $current_tab = 'Admin/Groups/Modify.html?Create=1';
+ $title = loc("Create a new group");
+}
+
+else {
+ $current_tab = 'Admin/Groups/Modify.html?id='.$id;
+ if ($id eq 'new' ) {
+
+ my ($create_id, $create_msg) = $Group->CreateUserDefinedGroup(Name =>
+ "$Name");
+ unless ($create_id) {
+ Abort (loc("Group could not be created: [_1]", $create_msg));
+ }
+ $id = $Group->Id;
+ }
+ else {
+ $Group->Load($id) || Abort('Could not load group');
+ }
+
+
+ if ($id) {
+ $title = loc("Modify the group [_1]", $Group->Name);
+
+ }
+
+ # If the create failed
+ else {
+ $title = loc("Create a new group");
+ $Create = 1;
+ }
+
+}
+
+if ($id) {
+
+ my @fields = qw(Description Name );
+ my @fieldresults = UpdateRecordObject ( AttributesRef => \@fields,
+ Object => $Group,
+ ARGSRef => \%ARGS );
+ push (@results,@fieldresults);
+ push @results, ProcessObjectCustomFieldUpdates( ARGSRef => \%ARGS, Object => $Group );
+}
+
+#we're asking about enabled on the web page but really care about disabled.
+if ($Enabled == 1) {
+ $Disabled = 0;
+}
+else {
+ $Disabled = 1;
+}
+if ( ($SetEnabled) and ( $Disabled != $Group->Disabled) ) {
+ my ($code, $msg) = $Group->SetDisabled($Disabled);
+ push @results, loc('Enabled status [_1]', loc_fuzzy($msg));
+}
+
+unless ($Group->Disabled()) {
+ $EnabledChecked ="CHECKED";
+}
+
+
+</%INIT>
+
+
+<%ARGS>
+$Create => undef
+$Name => undef
+$Description => undef
+$SetEnabled => undef
+$Enabled => undef
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Groups/UserRights.html b/rt/html/Admin/Groups/UserRights.html
new file mode 100644
index 0000000..b77edb0
--- /dev/null
+++ b/rt/html/Admin/Groups/UserRights.html
@@ -0,0 +1,116 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify user rights for group [_1]', $GroupObj->Name) &>
+<& /Admin/Elements/GroupTabs,
+ GroupObj => $GroupObj,
+ current_tab => 'Admin/Groups/UserRights.html?id='.$id,
+ Title => loc('Modify user rights for group [_1]', $GroupObj->Name) &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="UserRights.html">
+ <input type="hidden" class="hidden" name="id" value="<% $GroupObj->id %>" />
+
+<&| /Widgets/TitleBox, title => loc('Modify user rights for group [_1]', $GroupObj->Name) &>
+
+<table>
+
+% while (my $Member = $Users->Next()) {
+% my $UserObj = $Member->MemberObj->Object();
+ <tr align="right">
+ <td valign="top">
+ <% $UserObj->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Member->MemberObj->Id,
+ PrincipalType => 'User',
+ Object => $GroupObj &>
+ </td>
+ </tr>
+% }
+ </table>
+
+ </&>
+ <& /Elements/Submit, Label => loc('Modify User Rights'), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+ #Update the acls.
+ my @results = ProcessACLChanges(\%ARGS);
+
+# {{{ Deal with setting up the display of current rights.
+
+
+#Define vars used in html above
+
+
+if (!defined $id) {
+ Abort(loc("No Group defined"));
+}
+
+my $GroupObj = RT::Group->new($session{'CurrentUser'});
+$GroupObj->Load($id) || Abort(loc("Couldn't load group [_1]",$id));
+
+# Find out which users we want to display ACL selects for
+my $Privileged = RT::Group->new($session{'CurrentUser'});
+$Privileged->LoadSystemInternalGroup('Privileged');
+my $Users = $Privileged->MembersObj();
+
+
+
+# }}}
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+$UserString => undef
+$UserOp => undef
+$UserField => undef
+</%ARGS>
diff --git a/rt/html/Admin/Groups/index.html b/rt/html/Admin/Groups/index.html
new file mode 100644
index 0000000..384092b
--- /dev/null
+++ b/rt/html/Admin/Groups/index.html
@@ -0,0 +1,113 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/GroupTabs, current_tab => 'Admin/Groups/',
+ current_subtab => 'Admin/Groups/',
+ Title => $title &>
+<%$caption%>:<br /> <ul>
+%if ($Groups->Count == 0) {
+<li> <em><&|/l&>No groups matching search criteria found.</&></em>
+% }
+%my @ids;
+%while ( my $Group = $Groups->Next) {
+% push @ids, $Group->Id;
+<li><a href="Modify.html?id=<%$Group->id%>"><%$Group->Name || loc('(empty)')%></a><br />
+%}
+</ul>
+%if (my $ids = join(',', @ids)) {
+<em>(<a href="<%$RT::WebPath%>/Download/Tabular/Group/<% $ids %>/Groups.tsv"><&|/l&>Download as a tab-delimited file</&></a>)</em><br />
+%}
+<br /><br />
+<form method="post" action="<% $RT::WebPath %>/Admin/Groups/index.html">
+<input type="checkbox" class="checkbox" name="FindDisabledGroups" value="1" /> <&|/l&>Include disabled groups in listing.</&>
+<br />
+<div align="right"><input type="submit" class="button" value="<&|/l&>Go!</&>" /></div>
+</form>
+
+<br /><br />
+<form method="post" action="<% $RT::WebPath %>/Admin/Groups/index.html">
+<&|/l&>Find groups whose</&> <& /Elements/SelectGroups &><br />
+<div align="right"><input type="submit" class="button" value="<&|/l&>Go!</&>" /></div>
+</form>
+<%INIT>
+my $Groups = RT::Groups->new($session{'CurrentUser'});
+$Groups->LimitToUserDefinedGroups();
+my $title = loc('Select a group');
+my $caption;
+
+if ($FindDisabledGroups) {
+ $Groups->FindAllRows();
+}
+
+if (length $GroupString) {
+ $caption = loc("Groups matching search criteria");
+ if ($GroupField =~ /^CustomField-(\d+)/) {
+ $Groups->LimitCustomField(
+ CUSTOMFIELD => $1,
+ OPERATOR => $GroupOp,
+ VALUE => $GroupString,
+ );
+ }
+ else {
+ $Groups->Limit(
+ FIELD => $GroupField,
+ OPERATOR => $GroupOp,
+ VALUE => $GroupString,
+ );
+ }
+}
+else {
+ $caption = loc("User-defined groups");
+}
+</%INIT>
+<%ARGS>
+$GroupString => undef
+$GroupOp => '='
+$GroupField => 'Name'
+$FindDisabledGroups => 0
+</%ARGS>
diff --git a/rt/html/Admin/Queues/CustomField.html b/rt/html/Admin/Queues/CustomField.html
new file mode 100644
index 0000000..5282927
--- /dev/null
+++ b/rt/html/Admin/Queues/CustomField.html
@@ -0,0 +1,87 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/QueueTabs, id => $QueueObj->Id,
+ QueueObj => $QueueObj,
+ current_tab => 'Admin/Queues/CustomFields.html?id='.$QueueObj->id,
+ current_subtab => $current_subtab,
+ subtabs => $subtabs,
+ Title => $title &>
+
+<& /Admin/Elements/EditCustomField, title => $title, %ARGS &>
+
+<%INIT>
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+
+my ($title, $current_subtab);
+
+unless($QueueObj->id) {
+ Abort(loc("Queue [_1] not found", $Queue));
+}
+if ($CustomField) {
+ $title = loc('Modify a CustomField for queue [_1]', $QueueObj->Name());
+}else {
+ $current_subtab = "Admin/Queues/CustomField.html?create=1&Queue=".$QueueObj->id;
+ $title = loc('Create a CustomField for queue [_1]', $QueueObj->Name());
+}
+
+my $subtabs = {
+ A => { title => loc('New custom field'),
+ path => "Admin/Queues/CustomField.html?create=1&Queue=".$QueueObj->id
+ }
+ };
+
+</%INIT>
+<%ARGS>
+$CustomField => undef
+$Queue => 0
+</%ARGS>
+<%ATTR>
+AutoFlush => 0
+</%ATTR>
diff --git a/rt/html/Admin/Queues/CustomFields.html b/rt/html/Admin/Queues/CustomFields.html
new file mode 100644
index 0000000..8ce4f52
--- /dev/null
+++ b/rt/html/Admin/Queues/CustomFields.html
@@ -0,0 +1,72 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/QueueTabs,
+ id => $Object->id,
+ current_tab => "Admin/Queues/CustomFields.html?SubType=$SubType&id=$id",
+ current_subtab => "Admin/Queues/CustomFields.html?SubType=$SubType&id=$id",
+ QueueObj => $Object,
+ Title => $title
+ &>
+
+ <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, Object => $Object, ObjectType => 'RT::Queue' &>
+<%INIT>
+my $Object = RT::Queue->new( $session{'CurrentUser'} );
+
+$Object->Load($id) || Abort( loc( "Couldn't load object [_1]", $id ) );
+my $FriendlySubTypes =
+ RT::CustomField->new( $session{'CurrentUser'} )
+ ->FriendlyLookupType( $Object->CustomFieldLookupType );
+
+my $title = loc( 'Edit Custom Fields for [_1]', $Object->Name );
+
+</%INIT>
+<%ARGS>
+$id => undef
+$SubType => 'RT::Queue-RT::Ticket'
+</%ARGS>
diff --git a/rt/html/Admin/Queues/GroupRights.html b/rt/html/Admin/Queues/GroupRights.html
new file mode 100644
index 0000000..85075bd
--- /dev/null
+++ b/rt/html/Admin/Queues/GroupRights.html
@@ -0,0 +1,134 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify group rights for queue [_1]', $QueueObj->Name) &>
+<& /Admin/Elements/QueueTabs, id => $id,
+ QueueObj => $QueueObj,
+ current_tab => $current_tab,
+ Title => loc('Modify group rights for queue [_1]', $QueueObj->Name) &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="GroupRights.html">
+ <input type="hidden" class="hidden" name="id" value="<% $QueueObj->id %>" />
+
+
+<h1><&|/l&>System groups</&></h1>
+<table>
+<& /Elements/Callback, QueueObj => $QueueObj, results => \@results, %ARGS &>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToSystemInternalGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% loc($Group->Type) %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object => $QueueObj &>
+ </td>
+ </tr>
+% }
+</table>
+<h1><&|/l&>Roles</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToRolesForQueue($QueueObj->Id);
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% loc($Group->Type) %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object => $QueueObj &>
+ </td>
+ </tr>
+% }
+</table>
+<h1><&|/l&>User defined groups</&></h1>
+<table>
+% $Groups = RT::Groups->new($session{'CurrentUser'});
+% $Groups->LimitToUserDefinedGroups();
+% while (my $Group = $Groups->Next()) {
+ <tr align="right">
+ <td valign="top">
+ <% $Group->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId => $Group->PrincipalId,
+ Object => $QueueObj &>
+ </td>
+ </tr>
+% }
+</table>
+
+ <& /Elements/Submit, Label => loc('Modify Group Rights'), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+ #Update the acls.
+ my @results = ProcessACLChanges(\%ARGS);
+
+
+if (!defined $id) {
+ Abort(loc("No Queue defined"));
+}
+
+my $QueueObj = RT::Queue->new($session{'CurrentUser'});
+$QueueObj->Load($id) || Abort(loc("Couldn't load queue [_1]",$id));
+
+my $Groups;
+my $current_tab;
+$current_tab = 'Admin/Queues/GroupRights.html?id='.$QueueObj->id;
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Queues/Modify.html b/rt/html/Admin/Queues/Modify.html
new file mode 100644
index 0000000..e84d14f
--- /dev/null
+++ b/rt/html/Admin/Queues/Modify.html
@@ -0,0 +1,193 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/QueueTabs, id => $QueueObj->id,
+ QueueObj => $QueueObj,
+ current_tab => $current_tab,
+ Title => $title &>
+<& /Elements/ListActions, actions => \@results &>
+
+
+
+<form action="<%$RT::WebPath%>/Admin/Queues/Modify.html" method="post">
+%if ($Create ) {
+<input type="hidden" class="hidden" name="id" value="new" />
+% } else {
+<input type="hidden" class="hidden" name="id" value="<%$QueueObj->Id%>" />
+% }
+
+<table>
+<tr><td align="right">
+<&|/l&>Queue Name</&>:
+</td>
+<td><input name="Name" value="<% ($Create) ? "" : $QueueObj->Name %>" /></td>
+</tr><tr>
+<td align="right">
+<&|/l&>Description</&>:</td><td colspan="3"><input name="Description" value="<% ($Create) ? "" : $QueueObj->Description %>" size="60" /></td></tr>
+<tr>
+<td align="right">
+<&|/l&>Reply Address</&>:
+</td><td>
+<input name="CorrespondAddress" value="<% ($Create) ? "" : $QueueObj->CorrespondAddress %>" />
+<br /><span><em><&|/l , $RT::CorrespondAddress&>(If left blank, will default to [_1])</&></em></span>
+</td>
+<td align="right">
+
+<&|/l&>Comment Address</&>: </td><td>
+<input name="CommentAddress" value="<% ($Create) ? "" : $QueueObj->CommentAddress %>" />
+<br /><span><em><&|/l , $RT::CommentAddress&>(If left blank, will default to [_1])</&></em></span>
+</td>
+</tr><tr>
+
+<td align="right">
+<&|/l&>Priority starts at</&>:
+</td><td><input name="InitialPriority" value="<% ($Create) ? "" : $QueueObj->InitialPriority %>" />
+</td>
+<td align="right">
+<&|/l&>Over time, priority moves toward</&>:
+</td><td><input name="FinalPriority" value="<% ($Create) ? "" : $QueueObj->FinalPriority %>" />
+</td>
+</tr>
+<tr>
+<td align="right">
+<&|/l&>Requests should be due in</&>:
+</td><td>
+<input name="DefaultDueIn" value="<% ($Create) ? "" : $QueueObj->DefaultDueIn%>" /> <&|/l&>days</&>.
+</td>
+</tr>
+<tr>
+<td>
+</td>
+<td colspan="4"><input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <%$EnabledChecked%> /> <&|/l&>Enabled (Unchecking this box disables this queue)</&><br />
+<& /Elements/Callback, QueueObj => $QueueObj, results => \@results, %ARGS &>
+</td>
+</tr>
+
+</table>
+<& /Elements/Submit, Label => loc('Save Changes') &>
+</form>
+
+
+
+<%INIT>
+my $current_tab;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($id);
+my ($title, @results, $Disabled, $EnabledChecked);
+$EnabledChecked = "CHECKED";
+
+if ($Create) {
+ $current_tab = 'Admin/Queues/Modify.html?Create=1';
+ $title = loc("Create a queue");
+} else {
+ if ($id eq 'new') {
+ my ($val, $msg) = $QueueObj->Create(Name => $Name);
+ delete $session{'create_in_queues'};
+ if ($val == 0 ) {
+ Abort("$msg");
+ }
+ else {
+ push @results, $msg;
+ }
+ }
+ else {
+ $QueueObj->Load($id) || $QueueObj->Load($Name) || Abort("Couldn't load queue '$Name'");
+ }
+ $title = loc('Editing Configuration for queue [_1]', $QueueObj->Name);
+
+ $current_tab = 'Admin/Queues/Modify.html?id='.$QueueObj->id;
+}
+if ($QueueObj->Id()) {
+ delete $session{'create_in_queues'};
+my @attribs= qw(Description CorrespondAddress CommentAddress Name
+ InitialPriority FinalPriority DefaultDueIn);
+
+ @results = UpdateRecordObject( AttributesRef => \@attribs,
+ Object => $QueueObj,
+ ARGSRef => \%ARGS);
+
+ #we're asking about enabled on the web page but really care about disabled.
+ if ($Enabled == 1) {
+ $Disabled = 0;
+ }
+ else {
+ $Disabled = 1;
+ }
+ if ( ($SetEnabled) and ( $Disabled != $QueueObj->Disabled) ) {
+ my ($code, $msg) = $QueueObj->SetDisabled($Disabled);
+ push @results, loc('Enabled status: [_1]', loc_fuzzy($msg));
+ }
+
+ if ($QueueObj->Disabled()) {
+ $EnabledChecked ="";
+ }
+
+ my @linkresults;
+ $m->comp('/Elements/Callback', results => \@linkresults,
+ RecordObj => $QueueObj, ARGSRef => \%ARGS,
+ _CallbackName => 'ProcessLinks');
+ push @results, @linkresults;
+}
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$result => undef
+$Name => undef
+$Create => undef
+$Description => undef
+$CorrespondAddress => undef
+$CommentAddress => undef
+$InitialPriority => undef
+$FinalPriority => undef
+$DefaultDueIn => undef
+$SetEnabled => undef
+$Enabled => undef
+</%ARGS>
diff --git a/rt/html/Admin/Queues/People.html b/rt/html/Admin/Queues/People.html
new file mode 100644
index 0000000..4eeda3c
--- /dev/null
+++ b/rt/html/Admin/Queues/People.html
@@ -0,0 +1,210 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc('Modify people related to queue [_1]', $QueueObj->Name) &>
+<& /Admin/Elements/QueueTabs, id => $id,
+ QueueObj => $QueueObj,
+ current_tab => $current_tab,
+ Title => loc('Modify people related to queue [_1]', $QueueObj->Name) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+
+<form method="post" action="People.html">
+<input type="hidden" class="hidden" name="id" value="<%$QueueObj->Id%>" />
+
+<table width="100%">
+<tr>
+<td valign="top" >
+
+<h3><&|/l&>Current watchers</&></h3>
+
+
+<&|/l&>Cc</&>:
+
+<& /Admin/Elements/EditQueueWatchers, QueueObj => $QueueObj, Watchers => $QueueObj->Cc &>
+
+<&|/l&>Administrative Cc</&>:
+
+<& /Admin/Elements/EditQueueWatchers, QueueObj => $QueueObj, Watchers => $QueueObj->AdminCc &>
+
+
+</td>
+<td valign="top">
+<h3><&|/l&>New watchers</&></h3>
+
+<&|/l&>Find people whose</&><br />
+<& /Elements/SelectUsers &>
+<input type="submit" class="button" name="OnlySearchForPeople" value="<&|/l&>Go!</&>" />
+<br />
+<&|/l&>Find groups whose</&><br />
+<& /Elements/SelectGroups &>
+<input type="submit" class="button" name="OnlySearchForGroup" value="<&|/l&>Go!</&>" />
+
+<p>
+<&|/l&>Add new watchers</&>:<br />
+<p>
+<strong><&|/l&>Users</&></strong>
+% if ($user_msg) {
+<br />
+<em><%$user_msg%></em>
+% } elsif ($Users) {
+<ul>
+% while (my $u = $Users->Next ) {
+<li><&/Elements/SelectWatcherType, Scope=>'queue', Name =>
+"Queue-AddWatcher-Principal-".$u->PrincipalId &> <%$u->Name%>
+(<%$u->RealName%>)
+% }
+</ul>
+% }
+
+<p>
+<strong><&|/l&>Groups</&></strong>
+
+% if ($group_msg) {
+<br />
+<em><%$group_msg%></em>
+% } elsif ($Groups) {
+<ul>
+% while (my $g = $Groups->Next ) {
+<li><&/Elements/SelectWatcherType, Scope=>'queue', Name =>
+"Queue-AddWatcher-Principal-".$g->PrincipalId &> <%$g->Name%>
+(<%$g->Description%>)
+% }
+</ul>
+% }
+
+</td>
+</tr>
+</table>
+
+
+
+
+<& /Elements/Submit, Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to"), Reset => 1 &>
+</form>
+
+<%INIT>
+
+my $current_tab;
+my ($field, @results, $User, $Users, $Groups, $watcher, $user_msg, $group_msg);
+
+# {{{ Load the queue
+#If we get handed two ids, mason will make them an array. bleck.
+# We want teh first one. Just because there's no other sensible way
+# to deal
+
+
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($id) || Abort(loc("Couldn't load queue", $id));
+# }}}
+
+# {{{ Delete deletable watchers
+
+foreach my $key (keys %ARGS) {
+ my $id = $QueueObj->Id;
+
+ if (($key =~ /^Queue-$id-DeleteWatcher-Type-(.*?)-Principal-(\d*)$/)) {;
+ my ($code, $msg) = $QueueObj->DeleteWatcher(Type => $1,
+ PrincipalId => $2);
+ push @results, $msg;
+ }
+}
+# }}}
+
+# {{{ Add new watchers
+foreach my $key (keys %ARGS) {
+ #They're in this order because otherwise $1 gets clobbered :/
+ if ( ($ARGS{$key} =~ /^(AdminCc|Cc)$/) and
+ ($key =~ /^Queue-AddWatcher-Principal-(\d*)$/) ) {
+ $RT::Logger->debug("Adding a watcher $1 to ".$ARGS{$key}."\n");
+ my ($code, $msg) = $QueueObj->AddWatcher(Type => $ARGS{$key},
+ PrincipalId => $1);
+ push @results, $msg;
+ }
+}
+
+# }}}
+
+
+
+if (!length $ARGS{'UserString'}) {
+$user_msg = loc("No principals selected.");
+ }
+else {
+ $Users = new RT::Users($session{'CurrentUser'});
+ $Users->Limit(FIELD => $ARGS{'UserField'},
+ VALUE => $ARGS{'UserString'},
+ OPERATOR => $ARGS{'UserOp'});
+ }
+
+if (!length $ARGS{'GroupString'}) {
+$group_msg = loc("No principals selected.");
+ }
+else {
+$Groups = new RT::Groups($session{'CurrentUser'});
+$Groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined');
+$Groups->Limit(FIELD => $ARGS{'GroupField'},
+ VALUE => $ARGS{'GroupString'},
+ OPERATOR => $ARGS{'GroupOp'});
+ }
+
+$current_tab = 'Admin/Queues/People.html?id='.$QueueObj->id;
+</%INIT>
+
+<%ARGS>
+$UserField => 'Name'
+$UserOp => '='
+$UserString => undef
+$GroupField => 'Name'
+$GroupOp => '='
+$GroupString => undef
+$Type => undef
+$id => undef
+</%ARGS>
+
diff --git a/rt/html/Admin/Queues/Scrip.html b/rt/html/Admin/Queues/Scrip.html
new file mode 100644
index 0000000..190be8b
--- /dev/null
+++ b/rt/html/Admin/Queues/Scrip.html
@@ -0,0 +1,100 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/QueueTabs, id => $QueueObj->Id,
+ QueueObj => $QueueObj,
+ current_tab => 'Admin/Queues/Scrips.html?id='.$QueueObj->id,
+ current_subtab => $current_subtab,
+ subtabs => $subtabs,
+ Title => $title &>
+
+<& /Elements/ListActions, actions => \@results &>
+<& /Admin/Elements/EditScrip, title => $title, %ARGS, id => $id &>
+
+<%init>
+my $QueueObj = RT::Queue->new( $session{'CurrentUser'} );
+$QueueObj->Load( $Queue );
+unless( $QueueObj->id ) {
+ Abort(loc("Queue [_1] not found", $id));
+}
+
+my ($title, $current_subtab);
+my $subtabs = {
+ A => {
+ title => loc('Select scrip'),
+ path => "Admin/Queues/Scrips.html?id=".$QueueObj->id,
+ },
+ B => {
+ title => loc('New scrip'),
+ path => "Admin/Queues/Scrip.html?create=1&Queue=".$QueueObj->id,
+ separator => 1,
+ },
+};
+
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+($id, my @results) = $m->comp( '/Admin/Elements/EditScrip:Process', %ARGS );
+
+if ( $id ) {
+ $current_subtab = "Admin/Queues/Scrip.html?id=$id&Queue=". $QueueObj->id;
+ $title = loc("Modify a scrip for queue [_1]", $QueueObj->Name);
+ $subtabs->{"C"} = {
+ title => loc("Scrip #[_1]",$id),
+ path => "Admin/Queues/Scrip.html?id=$id&Queue=".$QueueObj->id
+ };
+} else {
+ $current_subtab = "Admin/Queues/Scrip.html?create=1&Queue=".$QueueObj->id;
+ $title = loc("Create a scrip for queue [_1]", $QueueObj->Name);
+}
+
+
+</%init>
+
+<%ARGS>
+$id => undef
+$Queue => undef
+</%ARGS>
diff --git a/rt/html/Admin/Queues/Scrips.html b/rt/html/Admin/Queues/Scrips.html
new file mode 100644
index 0000000..8fb0db4
--- /dev/null
+++ b/rt/html/Admin/Queues/Scrips.html
@@ -0,0 +1,87 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/QueueTabs, id => $QueueObj->id,
+ QueueObj => $QueueObj,
+ current_tab => 'Admin/Queues/Scrips.html?id='.$id,
+ current_subtab => 'Admin/Queues/Scrips.html?id='.$id,
+ subtabs => $subtabs,
+ Title => $title &>
+
+% if (!$QueueObj->Disabled) { # Global scrips does not apply to disabled queues
+<h2><&|/l&>Scrips which apply to all queues</&></h2>
+<& /Admin/Elements/ListGlobalScrips &>
+<br />
+% }
+<& /Admin/Elements/EditScrips, title => $title, %ARGS &>
+<%init>
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($id);
+
+my $title;
+
+if ($QueueObj->id) {
+ $title = loc("Modify scrips for queue [_1]", $QueueObj->Name);
+} else {
+ Abort(loc("Queue [_1] not found",$id));
+}
+
+my $subtabs = {
+ A => { title => loc('Select scrip'),
+ path => "Admin/Queues/Scrips.html?id=".$id,
+ },
+ B => { title => loc('New scrip'),
+ path => "Admin/Queues/Scrip.html?create=1&Queue=".$id,
+ separator => 1,
+ }
+ };
+</%init>
+
+<%ARGS>
+$id => undef #some identifier that a Queue could
+</%ARGS>
diff --git a/rt/html/Admin/Queues/Template.html b/rt/html/Admin/Queues/Template.html
new file mode 100644
index 0000000..2d6343f
--- /dev/null
+++ b/rt/html/Admin/Queues/Template.html
@@ -0,0 +1,130 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/QueueTabs, id => $Queue,
+ QueueObj => $QueueObj,
+ current_tab => 'Admin/Queues/Templates.html?id='.$Queue,
+ current_subtab => $current_subtab,
+ subtabs => $subtabs,
+ Title => $title &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="Template.html">
+%if ($Create ) {
+<input type="hidden" class="hidden" name="Template" value="new" />
+% } else {
+<input type="hidden" class="hidden" name="Template" value="<%$TemplateObj->Id%>" />
+% }
+
+%# hang onto the queue id
+<input type="hidden" class="hidden" name="Queue" value="<%$Queue%>" />
+<& /Admin/Elements/ModifyTemplate, Name => $TemplateObj->Name, Description =>
+$TemplateObj->Description, Content => $TemplateObj->Content &>
+<& /Elements/Submit, Label => loc('Create'), Reset => 1 &>
+</form>
+
+
+<%INIT>
+
+my $TemplateObj = new RT::Template($session{'CurrentUser'});
+my ($title, @results, $current_subtab);
+
+my $subtabs = {
+ A => { title => loc('Select template'),
+ path => "Admin/Queues/Templates.html?id=$Queue"
+ },
+ B => { title => loc('New template'),
+ path => "Admin/Queues/Template.html?Create=1&Queue=$Queue",
+ separator => 1,
+ }
+ };
+
+if ($Create) {
+ $title = loc("Create a template");
+ $current_subtab = "Admin/Queues/Template.html?Create=1&Queue=".$Queue;
+}
+
+else {
+ if ($Template eq 'new') {
+ my ($val, $msg) = $TemplateObj->Create(Queue => $Queue, Name => $Name);
+ Abort(loc("Could not create template: [_1]", $msg)) unless ($val);
+ push @results, $msg;
+ }
+ else {
+ $TemplateObj->Load($Template) || Abort(loc('No Template'));
+ }
+ $title = loc('Modify template [_1]', loc($TemplateObj->Name()));
+
+
+}
+my $QueueObj;
+if ($TemplateObj->Id()) {
+ $Queue = $TemplateObj->Queue;
+ $QueueObj = $TemplateObj->QueueObj;
+
+ my @attribs = qw( Description Content Queue Name);
+ my @aresults = UpdateRecordObject( AttributesRef => \@attribs,
+ Object => $TemplateObj,
+ ARGSRef => \%ARGS);
+ $current_subtab = "Admin/Queues/Template.html?Queue=$Queue&Template=".$TemplateObj->Id();
+ $subtabs->{"C"} = { title => loc('Template #[_1]', $TemplateObj->Id()),
+ path => "Admin/Queues/Template.html?Queue=$Queue&Template=".$TemplateObj->Id(),
+ };
+ push @results, @aresults;
+} else {
+ $QueueObj = RT::Queue->new($session{'CurrentUser'});
+ $QueueObj->Load($Queue);
+}
+
+</%INIT>
+<%ARGS>
+$Queue => undef
+$Template => undef
+$Create => undef
+$Name => undef
+</%ARGS>
diff --git a/rt/html/Admin/Queues/Templates.html b/rt/html/Admin/Queues/Templates.html
new file mode 100644
index 0000000..d62b497
--- /dev/null
+++ b/rt/html/Admin/Queues/Templates.html
@@ -0,0 +1,81 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/QueueTabs, id => $QueueObj->id,
+ current_tab => 'Admin/Queues/Templates.html?id='.$id,
+ current_subtab => 'Admin/Queues/Templates.html?id='.$id,
+ QueueObj => $QueueObj,
+ subtabs => $subtabs,
+ Title => $title &>
+
+<& /Admin/Elements/EditTemplates, title => $title, %ARGS &>
+
+<%INIT>
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($id);
+
+my ($title, $current_subtab);
+
+if ($QueueObj->id) {
+ $title = loc("Edit Templates for queue [_1]", $QueueObj->Name);
+} else {
+ Abort(loc("Queue [_1] not found",$id));
+}
+my $subtabs = {
+ A => { title => loc('Select template'),
+ path => "Admin/Queues/Templates.html?id=".$id,
+ },
+ B => { title => loc('New template'),
+ path => "Admin/Queues/Template.html?Create=1&Queue=".$id,
+ }
+ };
+
+</%INIT>
+<%ARGS>
+$id => undef #some identifier that a Queue could
+</%ARGS>
diff --git a/rt/html/Admin/Queues/UserRights.html b/rt/html/Admin/Queues/UserRights.html
new file mode 100644
index 0000000..c277f17
--- /dev/null
+++ b/rt/html/Admin/Queues/UserRights.html
@@ -0,0 +1,114 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Modify user rights for queue [_1]', $QueueObj->Name) &>
+<& /Admin/Elements/QueueTabs, id => $id,
+ QueueObj => $QueueObj,
+ current_tab => $current_tab,
+ Title => loc('Modify user rights for queue [_1]', $QueueObj->Name) &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <form method="post" action="UserRights.html">
+ <input type="hidden" class="hidden" name="id" value="<% $QueueObj->id %>" />
+
+
+<table>
+<& /Elements/Callback, QueueObj => $QueueObj, results => \@results, %ARGS &>
+% while (my $Member = $Users->Next()) {
+% my $UserObj = $Member->MemberObj->Object();
+% my $group = RT::Group->new($session{'CurrentUser'});
+% $group->LoadACLEquivalenceGroup($Member->MemberObj);
+ <tr align="right">
+ <td valign="top">
+ <% $UserObj->Name %>
+ </td>
+ <td>
+ <& /Admin/Elements/SelectRights, PrincipalId=> $group->PrincipalId,
+ Object => $QueueObj &>
+ </td>
+ </tr>
+% }
+ </table>
+
+ <& /Elements/Submit, Label => loc('Modify User Rights'), Reset => 1 &>
+
+ </form>
+
+<%INIT>
+
+ #Update the acls.
+ my @results = ProcessACLChanges(\%ARGS);
+
+# {{{ Deal with setting up the display of current rights.
+
+
+
+if (!defined $id) {
+ Abort(loc("No Queue defined"));
+}
+
+my $QueueObj = RT::Queue->new($session{'CurrentUser'});
+$QueueObj->Load($id) || Abort(loc("Couldn't load queue [_1]",$id));
+
+# Find out which users we want to display ACL selects for
+my $Privileged = RT::Group->new($session{'CurrentUser'});
+$Privileged->LoadSystemInternalGroup('Privileged');
+my $Users = $Privileged->MembersObj();
+
+
+
+# }}}
+my $current_tab;
+$current_tab = 'Admin/Queues/UserRights.html?id='.$QueueObj->id;
+</%INIT>
+
+<%ARGS>
+$id => undef
+$UserString => undef
+$UserOp => undef
+$UserField => undef
+</%ARGS>
diff --git a/rt/html/Admin/Queues/index.html b/rt/html/Admin/Queues/index.html
new file mode 100644
index 0000000..bfa7604
--- /dev/null
+++ b/rt/html/Admin/Queues/index.html
@@ -0,0 +1,86 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc("Admin queues") &>
+<& /Admin/Elements/QueueTabs, current_tab => 'Admin/Queues/',
+ current_subtab => 'Admin/Queues/',
+ Title => loc("Admin queues") &>
+
+
+
+<h1><%$caption%></h1>
+<p><&|/l&>Select a queue</&>:</p>
+<ul>
+%if ($queues->Count == 0) {
+<li><em><&|/l&>No queues matching search criteria found.</&></em></li>
+% }
+%while ( $queue = $queues->Next) {
+<li><a href="Modify.html?id=<%$queue->id%>"><%$queue->Name%></a></li>
+%}
+</ul>
+<br />
+<form method="post" action="<% $RT::WebPath %>/Admin/Queues/index.html">
+<input type="checkbox" class="checkbox" name="FindDisabledQueues" value="1" /> <&|/l&>Include disabled queues in listing.</&>
+<div align="right"><input type="submit" class="button" value="<&|/l&>Go!</&>" /></div>
+</form>
+
+<%INIT>
+my ($queue, $caption);
+my $queues = new RT::Queues($session{'CurrentUser'});
+$queues->UnLimit();
+
+if ($FindDisabledQueues) {
+ $caption = loc("All Queues");
+ $queues->{'find_disabled_rows'} = 1;
+} else {
+ $caption = loc("Enabled Queues");
+}
+
+</%INIT>
+<%ARGS>
+$FindDisabledQueues => 0
+</%ARGS>
diff --git a/rt/html/Admin/Tools/Configuration.html b/rt/html/Admin/Tools/Configuration.html
new file mode 100644
index 0000000..205b9a0
--- /dev/null
+++ b/rt/html/Admin/Tools/Configuration.html
@@ -0,0 +1,100 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+require Module::Versions::Report;
+my $title = loc('System Configuration');
+unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'SuperUser')) {
+ Abort(loc('This feature is only available to system administrators'));
+}
+
+
+</%init>
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/ToolTabs,
+ current_tab => 'Admin/Tools/Configuration.html',
+ current_subtab => 'Admin/Tools/Configuration.html',
+ Title => $title &>
+
+
+
+
+<h2><&|/l&>Loaded perl modules</&></h2>
+<pre>
+% my $report = Module::Versions::Report::report();
+% my @report = grep { /v\d/ } split("\n",$report);
+<%join('<br />', @report)|n%>
+
+
+</pre>
+
+<h2><&|/l&>RT Variables</&></h2>
+<table>
+%{
+%no strict qw/refs/;
+
+%foreach my $key (sort keys %{*RT::}) {
+% next unless (${'RT::'.$key} );
+% next if (ref ${'RT::'.$key} );
+<tr><td>RT::<%$key%></td>
+<td>
+% if ($key =~ /Password(?!Length)/i) {
+<em>Password not printed</em>
+% } else {
+<%${'RT::'.$key} %>
+% }
+</td>
+</tr>
+% }
+%}
+</table>
+
+<h2><&|/l&>Perl configuration</&></h2>
+% require Config;
+<pre>
+<%Config::myconfig()%>
+</pre>
diff --git a/rt/html/Admin/Tools/index.html b/rt/html/Admin/Tools/index.html
new file mode 100644
index 0000000..3b4ec62
--- /dev/null
+++ b/rt/html/Admin/Tools/index.html
@@ -0,0 +1,55 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+my $title = loc('System Tools');
+</%init>
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/ToolTabs,
+ current_tab => 'Admin/Tools/index.html',
+ current_subtab => 'Admin/Tools/Configuration.html',
+ Title => $title &>
diff --git a/rt/html/Admin/Users/CustomFields.html b/rt/html/Admin/Users/CustomFields.html
new file mode 100644
index 0000000..1455c1d
--- /dev/null
+++ b/rt/html/Admin/Users/CustomFields.html
@@ -0,0 +1,71 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/UserTabs,
+ id => $Object->id,
+ current_tab => "Admin/Users/CustomFields.html?$id=".$id,
+ current_subtab => "Admin/Users/CustomFields.html?id=".$id,
+ UserObj => $Object,
+ Title => $title
+ &>
+
+ <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, Object => $Object, ObjectType => 'RT::User' &>
+<%INIT>
+my $Object = RT::User->new( $session{'CurrentUser'} );
+
+$Object->Load($id) || Abort( loc( "Couldn't load object [_1]", $id ) );
+my $FriendlySubTypes =
+ RT::CustomField->new( $session{'CurrentUser'} )
+ ->FriendlyLookupType( $Object->CustomFieldLookupType );
+
+my $title = loc( 'Edit Custom Fields for [_1]', $Object->Name );
+
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Users/History.html b/rt/html/Admin/Users/History.html
new file mode 100644
index 0000000..e47f67e
--- /dev/null
+++ b/rt/html/Admin/Users/History.html
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/UserTabs,
+ id => $id,
+ UserObj => $UserObj,
+ current_tab => $current_tab,
+ Title => $title &>
+
+<& /Ticket/Elements/ShowHistory,
+ Ticket => $UserObj,
+ ShowDisplayModes => 0,
+&>
+
+<%INIT>
+my $current_tab = 'Admin/Users/History.html?id='.$id;
+my $UserObj = new RT::User($session{'CurrentUser'});
+$UserObj->Load($id) || Abort("Couldn't load user '$id'");
+my $title = loc("History of the user [_1]", $UserObj->Name);
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Users/Memberships.html b/rt/html/Admin/Users/Memberships.html
new file mode 100644
index 0000000..493cb0f
--- /dev/null
+++ b/rt/html/Admin/Users/Memberships.html
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/UserTabs,
+ id => $id,
+ UserObj => $UserObj,
+ current_tab => $current_tab,
+ Title => $title &>
+
+<h2><&|/l&>Groups</&></h2>
+
+<& /Elements/ShowMemberships, UserObj => $UserObj &>
+
+<%INIT>
+my $UserObj = RT::User->new($session{'CurrentUser'});
+$UserObj->Load($id) || Abort("Couldn't load user '$id'");
+my $title = loc("Memberships of the user [_1]", $UserObj->Name);
+my $current_tab = 'Admin/Users/Memberships.html?id='.$id;
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Users/Modify.html b/rt/html/Admin/Users/Modify.html
new file mode 100644
index 0000000..be50dca
--- /dev/null
+++ b/rt/html/Admin/Users/Modify.html
@@ -0,0 +1,445 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/UserTabs,
+ id => $id,
+ UserObj => $UserObj,
+ current_tab => $current_tab,
+ Title => $title &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="<%$RT::WebPath%>/Admin/Users/Modify.html" method="post" enctype="multipart/form-data">
+%if ($Create) {
+<input type="hidden" class="hidden" name="id" value="new" />
+% } else {
+<input type="hidden" class="hidden" name="id" value="<%$UserObj->Id%>" />
+% }
+<table width="100%" border="0">
+<tr>
+
+<td valign="top" class="boxcontainer">
+<&| /Widgets/TitleBox, title => loc('Identity') &>
+
+<table>
+<tr><td align="right">
+<&|/l&>Username</&>:
+</td><td>
+<input name="Name" value="<%$UserObj->Name%>" /> <strong><&|/l&>(required)</&></strong>
+</td></tr>
+<tr><td align="right">
+<&|/l&>Email</&>:
+</td><td>
+<input name="EmailAddress" value="<%$UserObj->EmailAddress%>" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Real Name</&>:
+</td><td>
+<input name="RealName" value="<%$UserObj->RealName%>" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Nickname</&>:
+</td><td>
+<input name="NickName" value="<%$UserObj->NickName%>" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Unix login</&>:
+</td><td>
+<input name="Gecos" value="<%$UserObj->Gecos%>" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Language</&>:
+</td><td>
+<& /Elements/SelectLang, Name => 'Lang', Default => $UserObj->Lang &>
+</td></tr>
+<tr><td align="right">
+<&|/l&>Extra info</&>:
+</td><td>
+<textarea name="FreeformContactInfo" cols="20" rows="5"><%$UserObj->FreeformContactInfo%></textarea>
+</td></tr>
+</table>
+</&>
+<br />
+
+<&| /Widgets/TitleBox, title => loc('Customers') &>
+<& /Elements/EditCustomers, Object => $UserObj, CustomerString=> $CustomerString, ServiceString => $ServiceString &>
+</&>
+<br />
+
+<&| /Widgets/TitleBox, title => loc('Access control') &>
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <%$EnabledChecked%> />
+<&|/l&>Let this user access RT</&><br />
+
+
+<input type="hidden" class="hidden" name="SetPrivileged" value="1" />
+<input type="checkbox" class="checkbox" name="Privileged" value="1" <%$PrivilegedChecked%> /> <&|/l&>Let this user be granted rights</&><br />
+
+% unless ($RT::WebExternalAuth and !$RT::WebFallbackToInternalAuth) {
+<table>
+<tr>
+<td align="right">
+<&|/l&>New Password</&>:
+</td>
+<td align="left">
+<input type="password" name="Pass1" />
+</td>
+</tr>
+<tr><td align="right">
+<&|/l&>Retype Password</&>:
+</td>
+<td>
+<input type="password" name="Pass2" />
+</td>
+</tr>
+</table>
+% }
+</&>
+<& /Elements/Callback, _CallbackName => 'LeftColumnBottom', UserObj => $UserObj, %ARGS &>
+</td>
+
+<td valign="top" class="boxcontainer">
+<&| /Widgets/TitleBox, title => loc('Location') &>
+<table>
+<tr><td align="right">
+<&|/l&>Organization</&>:
+</td><td>
+<input name="Organization" value="<%$UserObj->Organization%>" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Address1</&>:
+</td><td>
+<input name="Address1" value="<%$UserObj->Address1%>" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Address2</&>:
+</td><td>
+<input name="Address2" value="<%$UserObj->Address2%>" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>City</&>:
+</td><td>
+<input name="City" value="<%$UserObj->City%>" size="14" />
+
+</td></tr>
+<tr><td align="right">
+<&|/l&>State</&>:
+</td><td>
+<input name="State" value="<%$UserObj->State%>" size="3" />
+
+</td></tr>
+<tr><td align="right">
+<&|/l&>Zip</&>:
+</td><td>
+<input name="Zip" value="<%$UserObj->Zip%>" size="9" />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Country</&>:
+</td><td>
+<input name="Country" value="<%$UserObj->Country%>" />
+</td></tr>
+</table>
+</&>
+<br />
+<&| /Widgets/TitleBox, title => loc('Phone numbers') &>
+<table>
+<tr><td align="right">
+<&|/l&>Residence</&>:
+</td><td>
+<input name="HomePhone" value="<%$UserObj->HomePhone%>" size="13" /><br />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Work</&>:
+</td><td>
+<input name="WorkPhone" value="<%$UserObj->WorkPhone%>" size="13" /><br />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Mobile</&>:
+</td><td>
+<input name="MobilePhone" value="<%$UserObj->MobilePhone%>" size="13" /><br />
+</td></tr>
+<tr><td align="right">
+<&|/l&>Pager</&>:
+</td><td>
+<input name="PagerPhone" value="<%$UserObj->PagerPhone%>" size="13" /><br />
+</td>
+</tr>
+</table>
+</&>
+<br />
+<&| /Widgets/TitleBox, title => loc('Custom Fields') &>
+<table>
+% my $CFs = $UserObj->CustomFields;
+% while (my $CF = $CFs->Next) {
+<tr valign="top"><td align="right">
+<% $CF->Name %>:
+</td><td>
+% if ($UserObj->id) {
+<& /Elements/EditCustomField, %ARGS, Object => $UserObj, CustomField => $CF &>
+% } else {
+<& /Elements/EditCustomField, %ARGS, NamePrefix => 'Object-RT::User-new-CustomField-', CustomField => $CF &>
+% }
+</td></tr>
+% }
+</table>
+</&>
+<& /Elements/Callback, _CallbackName => 'RightColumnBottom', UserObj => $UserObj, %ARGS &>
+</td></tr>
+<tr>
+<td colspan="2">
+<&| /Widgets/TitleBox, title => loc('Comments about this user') &>
+<textarea class="comments" name="Comments" cols="80" rows="5" wrap="virtual"><%$UserObj->Comments%></textarea>
+</&>
+%if ($UserObj->Privileged) {
+<br />
+<&| /Widgets/TitleBox, title => loc('Signature') &>
+<textarea class="signature" cols="80" rows="5" name="Signature" wrap="hard"><%$UserObj->Signature%></textarea>
+</&>
+% }
+
+</td>
+</tr>
+</table>
+
+<& /Elements/Submit, Label => loc('Save Changes') &>
+</form>
+
+<%INIT>
+
+my $current_tab;
+my $UserObj = new RT::User($session{'CurrentUser'});
+my ($title, $PrivilegedChecked, $EnabledChecked, $Disabled, $result, @results);
+
+my ($val, $msg);
+
+if ($Create) {
+ $current_tab = 'Admin/Users/Modify.html?Create=1';
+ $title = loc("Create a new user");
+}
+else {
+
+ $current_tab = 'Admin/Users/Modify.html?id='.$id;
+ if ($id eq 'new') {
+ ( $val, $msg ) = $UserObj->Create(
+ Name => $Name,
+ EmailAddress => $ARGS{'EmailAddress'},
+ Name => $ARGS{'Name'},
+ Comments => $ARGS{'Comments'},
+ Signature => $ARGS{'Signature'},
+ EmailAddress => $ARGS{'EmailAddress'},
+ FreeformContactInfo => $ARGS{'FreeformContactInfo'},
+ Organization => $ARGS{'Organization'},
+ RealName => $ARGS{'RealName'},
+ NickName => $ARGS{'NickName'},
+ Lang => $ARGS{'Lang'},
+ EmailEncoding => $ARGS{'EmailEncoding'},
+ WebEncoding => $ARGS{'WebEncoding'},
+ ExternalContactInfoId => $ARGS{'ExternalContactInfoId'},
+ ContactInfoSystem => $ARGS{'ContactInfoSystem'},
+ Gecos => $ARGS{'Gecos'},
+ ExternalAuthId => $ARGS{'ExternalAuthId'},
+ AuthSystem => $ARGS{'AuthSystem'},
+ HomePhone => $ARGS{'HomePhone'},
+ WorkPhone => $ARGS{'WorkPhone'},
+ MobilePhone => $ARGS{'MobilePhone'},
+ PagerPhone => $ARGS{'PagerPhone'},
+ Address1 => $ARGS{'Address1'},
+ Address2 => $ARGS{'Address2'},
+ City => $ARGS{'City'},
+ State => $ARGS{'State'},
+ Zip => $ARGS{'Zip'},
+ Country => $ARGS{'Country'},
+ Privileged => $ARGS{'Privileged'},
+ Disabled => ($ARGS{'Enabled'} ? 0 : 1)
+ );
+
+ if ($val) {
+ push @results, $msg;
+ foreach my $key ( keys %ARGS) {
+ # Convert custom fields on the "new" object to custom fields on the one we've just created
+ if ($key =~ /^Object-RT::User-new-CustomField-(.*)$/) {
+ $ARGS{'Object-RT::User-'.$val.'-CustomField-'.$1} = delete $ARGS{$key};
+ }
+ }
+ push @results, ProcessObjectCustomFieldUpdates( ARGSRef => \%ARGS, Object => $UserObj );
+ } else {
+ push @results, loc('User could not be created: [_1]', $msg);
+ }
+ } else {
+ $UserObj->Load($id) || $UserObj->Load($Name) || Abort("Couldn't load user '$Name'");
+ $val = $UserObj->Id();
+ }
+
+ if ($val) {
+ $title = loc("Modify the user [_1]", $UserObj->Name);
+ }
+
+ # If the create failed
+ else {
+ $title = loc("Create a new user");
+ $Create = 1;
+ }
+}
+
+
+
+
+# If we have a user to modify, lets try.
+if ($UserObj->Id && $id ne 'new') {
+
+ my @fields = qw(Name Comments Signature EmailAddress FreeformContactInfo
+ Organization RealName NickName Lang EmailEncoding WebEncoding
+ ExternalContactInfoId ContactInfoSystem Gecos ExternalAuthId
+ AuthSystem HomePhone WorkPhone MobilePhone PagerPhone Address1
+ Address2 City State Zip Country
+ );
+
+ my @fieldresults = UpdateRecordObject ( AttributesRef => \@fields,
+ Object => $UserObj,
+ ARGSRef => \%ARGS );
+ push (@results,@fieldresults);
+ push @results, ProcessObjectCustomFieldUpdates( ARGSRef => \%ARGS, Object => $UserObj );
+
+ #deal with freeside customer links
+ push @results, ProcessObjectCustomers( ARGSRef => \%ARGS, Object => $UserObj );
+
+ # {{{ Deal with special fields: Privileged, Enabled
+ if ( $SetPrivileged and $Privileged != $UserObj->Privileged ) {
+ my ($code, $msg) = $UserObj->SetPrivileged($Privileged);
+ push @results, loc('Privileged status: [_1]', loc_fuzzy($msg));
+ }
+
+ #we're asking about enabled on the web page but really care about disabled.
+ $Disabled = $Enabled ? 0 : 1;
+
+ if ( ($SetEnabled) and ( $Disabled != $UserObj->Disabled) ) {
+ my ($code, $msg) = $UserObj->SetDisabled($Disabled);
+ push @results, loc('Enabled status [_1]', loc_fuzzy($msg));
+ }
+
+
+ # }}}
+}
+
+if ( $UserObj->Id ) {
+ my $password_not_set;
+ # Deal with Password field
+ if ( !$Pass1 and !$Pass2 ) {
+ $password_not_set = 1;
+ } elsif ( $Pass1 ne $Pass2 ) {
+ $password_not_set = 1;
+ push @results, loc("Passwords do not match.");
+ } elsif ( $Pass1 eq $Pass2 and !$UserObj->IsPassword($Pass1) ) {
+ my ($code, $msg) = $UserObj->SetPassword($Pass1);
+ push @results, loc_fuzzy($msg);
+ $password_not_set = 1 unless $code;
+ }
+ if ($id eq 'new' and $password_not_set) {
+ push @results, loc("A password was not set, so user won't be able to login.");
+ }
+}
+
+
+# {{{ Do some setup for the ui
+unless ($UserObj->Disabled()) {
+ $EnabledChecked ="CHECKED";
+}
+
+if ($UserObj->Privileged()) {
+ $PrivilegedChecked = "CHECKED";
+}
+
+# }}}
+
+# set the id, so the the menu will have the right info, this needs to
+# be done here to avoid creating and then modifying a user
+$id = $UserObj->Id;
+
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$Name => undef
+$Comments => undef
+$Signature => undef
+$EmailAddress => undef
+$FreeformContactInfo => undef
+$Organization => undef
+$RealName => undef
+$NickName => undef
+$Privileged => undef
+$SetPrivileged => undef
+$Enabled => undef
+$SetEnabled => undef
+$Lang => undef
+$EmailEncoding => undef
+$WebEncoding => undef
+$ExternalContactInfoId => undef
+$ContactInfoSystem => undef
+$Gecos => undef
+$ExternalAuthId => undef
+$AuthSystem => undef
+$HomePhone => undef
+$WorkPhone => undef
+$MobilePhone => undef
+$PagerPhone => undef
+$Address1 => undef
+$Address2 => undef
+$City => undef
+$State => undef
+$Zip => undef
+$Country => undef
+$Pass1 => undef
+$Pass2=> undef
+$Create=> undef
+$OnlySearchForCustomers => undef
+$OnlySearchForServices => undef
+$CustomerString => undef
+$ServiceString => undef
+</%ARGS>
diff --git a/rt/html/Admin/Users/MyRT.html b/rt/html/Admin/Users/MyRT.html
new file mode 100644
index 0000000..6eed0ab
--- /dev/null
+++ b/rt/html/Admin/Users/MyRT.html
@@ -0,0 +1,132 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/UserTabs,
+ id => $id,
+ UserObj => $UserObj,
+ current_tab => $current_tab,
+ Title => $title &>
+
+<& /Widgets/SelectionBox:header, nojs => 1 &>
+
+<& /Elements/ListActions, actions => \@actions &>
+
+<form method="post" action="MyRT.html">
+<input type="hidden" name="id" value="<% $id %>" />
+<input type="hidden" name="Reset" value="1" />
+<input type="submit" class="button" value="<%loc('Reset to default')%>">
+</form>
+
+<br />
+
+% for my $pane (@panes) {
+<&|/Widgets/TitleBox, title => loc('RT at a glance').': '.loc($pane->{Name}), bodyclass => "" &>
+<& /Widgets/SelectionBox:show, self => $pane, nojs => 1 &></&>
+<br />
+% }
+
+<%init>
+my $current_tab = 'Admin/Users/MyRT.html?id='.$id;
+my $UserObj = new RT::User($session{'CurrentUser'});
+$UserObj->Load($id) || Abort("Couldn't load user '$id'");
+my $title = loc("RT at a glance for the user [_1]", $UserObj->Name);
+
+if ($ARGS{Reset}) {
+ $UserObj->SetPreferences('HomepageSettings', {});
+}
+
+my ($default_portlets) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
+my $portlets = $UserObj->Preferences('HomepageSettings', $default_portlets ? $default_portlets->Content : {});
+
+my %allowed_components = map {$_ => 1} @{$RT::HomepageComponents};
+my @items;
+
+push @items, map {["component-$_", $_]} sort keys %allowed_components;
+
+my $sys = RT::System->new( RT::CurrentUser->new($UserObj) );
+my @objs = ($sys);
+
+push @objs, RT::SavedSearches->new( RT::CurrentUser->new( $UserObj ) )->_PrivacyObjects;
+my @actions;
+
+for my $object (@objs) {
+ for ($m->comp("/Search/Elements/SearchesForObject", Object => $object)) {
+ my ($desc, $search) = @$_;
+ my $SearchType = $search->Content->{'SearchType'} || 'Ticket';
+ if ($object eq $sys && $SearchType eq 'Ticket') {
+ push @items, ["system-$desc", $desc];
+ }
+ else {
+ my $oid = ref($object).'-'.$object->Id.'-SavedSearch-'.$search->Id;
+ my $type = ($SearchType eq 'Ticket')
+ ? 'Saved Search' : $SearchType; # loc
+ push @items, ["saved-$oid", loc($type).": $desc"];
+ }
+ }
+}
+
+my @panes = $m->comp(
+ '/Admin/Elements/ConfigureMyRT',
+ panes => ['body', 'summary'],
+ Action => "MyRT.html?id=$id",
+ items => \@items,
+ current_portlets => $portlets,
+ OnSave => sub {
+ my ( $conf, $pane ) = @_;
+ $UserObj->SetPreferences( 'HomepageSettings', $conf );
+ push @actions, loc( 'Preferences [_1] for user [_2] .', $pane, $UserObj->Name );
+ }
+);
+
+$m->comp( '/Widgets/SelectionBox:process', %ARGS, self => $_, nojs => 1 )
+ for @panes;
+
+</%init>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Admin/Users/index.html b/rt/html/Admin/Users/index.html
new file mode 100644
index 0000000..bc47d4f
--- /dev/null
+++ b/rt/html/Admin/Users/index.html
@@ -0,0 +1,115 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('Select a user') &>
+<& /Admin/Elements/UserTabs, current_tab => 'Admin/Users/',
+ current_subtab => 'Admin/Users/',
+ Title => loc('Select a user') &>
+
+<h1><%$caption%></h1>
+<p><&|/l&>Select a user</&>:</p>
+<ul>
+%if ($users->Count == 0) {
+<li><em><&|/l&>No users matching search criteria found.</&></em></li>
+% }
+%my @ids;
+%while ( $user = $users->Next) {
+% push @ids, $user->Id;
+<li><a href="Modify.html?id=<%$user->id%>"><%$user->Name || loc('(no name listed)')%></a></li>
+%}
+</ul>
+%if (my $ids = join(',', @ids)) {
+<em>(<a href="<%$RT::WebPath%>/Download/Tabular/User/<% $ids %>/Users.tsv"><&|/l&>Download as a tab-delimited file</&></a>)</em><br />
+%}
+
+<br /><br />
+<form method="post" action="<% $RT::WebPath %>/Admin/Users/index.html">
+
+<&|/l&>Find people whose</&> <& /Elements/SelectUsers &><br />
+<input type="checkbox" class="checkbox" name="FindDisabledUsers" value="1" /> <&|/l&>Include disabled users in search.</&>
+<br />
+<div align="right"><input type="submit" class="button" value="<&|/l&>Go!</&>" /></div>
+</form>
+
+<%INIT>
+my ($user, $caption);
+my $users = new RT::Users($session{'CurrentUser'});
+
+if ($FindDisabledUsers) {
+ $users->{'find_disabled_rows'} = 1;
+}
+
+if (length $UserString) {
+ $caption = loc("Users matching search criteria");
+ if ($UserField =~ /^CustomField-(\d+)/) {
+ $users->LimitCustomField(
+ CUSTOMFIELD => $1,
+ OPERATOR => $UserOp,
+ VALUE => $UserString,
+ );
+ }
+ else {
+ $users->Limit(
+ FIELD => $UserField,
+ OPERATOR => $UserOp,
+ VALUE => $UserString,
+ );
+ }
+}
+else {
+ $caption = loc("Privileged users");
+ $users->LimitToPrivileged;
+}
+</%INIT>
+<%ARGS>
+$UserString => undef
+$UserOp => '='
+$UserField => 'Name'
+$IdLike => undef
+$EmailLike => undef
+$FindDisabledUsers => 0
+</%ARGS>
diff --git a/rt/html/Admin/autohandler b/rt/html/Admin/autohandler
new file mode 100644
index 0000000..b346cbc
--- /dev/null
+++ b/rt/html/Admin/autohandler
@@ -0,0 +1,53 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+$m->call_next(%ARGS) if $session{'CurrentUser'}->UserObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System,
+);
+</%init> \ No newline at end of file
diff --git a/rt/html/Admin/index.html b/rt/html/Admin/index.html
new file mode 100644
index 0000000..c89a883
--- /dev/null
+++ b/rt/html/Admin/index.html
@@ -0,0 +1,101 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Admin/Elements/Header, Title => loc('RT Administration') &>
+<& /Admin/Elements/Tabs, Title => loc('RT Administration') &>
+
+<ul>
+% foreach my $key (sort keys %$tabs) {
+<li><span><a href="<%$RT::WebPath%>/<%$tabs->{$key}->{'path'}|n %>"><%$tabs->{$key} ->{'title'}%></a></span><br />
+<%$tabs->{$key}->{description}%>
+</li>
+%}
+</ul>
+<%init>
+
+
+
+my $tabs = {
+ A => {
+ title => loc('Users'),
+ path => 'Admin/Users/index.html',
+ description => loc('Manage users and passwords'),
+ },
+ B => {
+ title => loc('Groups'),
+ path => 'Admin/Groups/index.html',
+ description => loc('Manage groups and group membership'),
+ },
+ C => {
+ title => loc('Queues'),
+ path => 'Admin/Queues/index.html',
+ description => loc('Manage queues and queue-specific properties'),
+ },
+ D => {
+ 'title' => loc('Custom Fields'),
+ description => loc('Manage custom fields and custom field values'),
+ path => 'Admin/CustomFields/index.html',
+ },
+ E => {
+ 'title' => loc('Global'),
+ path => 'Admin/Global/index.html',
+ description =>
+ loc('Manage properties and configuration which apply to all queues'),
+ },
+ F => {
+ 'title' => loc('Tools'),
+ path => 'Admin/Tools/index.html',
+ description => loc('Use other RT administrative tools')
+ },
+};
+
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+
+
+
+</%init>
diff --git a/rt/html/Approvals/Display.html b/rt/html/Approvals/Display.html
new file mode 100644
index 0000000..4779597
--- /dev/null
+++ b/rt/html/Approvals/Display.html
@@ -0,0 +1,72 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+
+<& Elements/Tabs,
+ current_tab => "Approvals/Display.html",
+ Title => $title &>
+<form method="post" action="<%$RT::WebPath%>/Approvals/index.html">
+
+<&| /Widgets/TitleBox, title => $title &>
+<& /Ticket/Elements/ShowHistory , Ticket => $Ticket, Collapsed => 0, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0 &>
+<hr>
+<& Elements/Approve, ticket => $Ticket, ShowApproving => 0 &>
+</&>
+<& /Elements/Submit&>
+</form>
+<& Elements/ShowDependency, Ticket => $Ticket &>
+
+<%init>
+my $Ticket = LoadTicket($id);
+
+my $title = loc("Approval #[_1]: [_2]", $Ticket->Id, $Ticket->Subject);
+
+</%init>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Approvals/Elements/Approve b/rt/html/Approvals/Elements/Approve
new file mode 100644
index 0000000..6dd562a
--- /dev/null
+++ b/rt/html/Approvals/Elements/Approve
@@ -0,0 +1,94 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<div class="approval">
+ <div class="name">
+ <a href="<%$RT::WebPath%>/Approvals/Display.html?id=<%$ticket->Id%>"><% loc("#[_1]: [_2]", $ticket->Id, $ticket->Subject) %></a> (<%loc($ticket->Status)%>)
+ </div>
+% if ($ShowApproving) {
+% foreach my $approving ( $ticket->AllDependedOnBy( Type => 'ticket' ) ) {
+ <div class="originating-ticket">
+ <span class="link"><a href="<%$RT::WebPath%>/Ticket/Display.html?id=<% $approving->Id %>"><&|/l, $approving->Id, $approving->Subject &>Originating ticket: #[_1]</&></a></span>
+ <div class="info">
+% if ($ShowCustomFields) {
+ <& /Ticket/Elements/ShowCustomFields, Ticket => $approving &>
+% }
+% if ($ShowHistory) {
+ <& /Ticket/Elements/ShowHistory, Ticket => $approving, Collapsed => 0, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0 &>
+% }
+ </div>
+ </div>
+% }
+% }
+ <div class="form">
+ <div class="action">
+ <div>
+ <input type="radio" class="radio" id="Approval-<%$ticket->Id%>-Action-approve" name="Approval-<%$ticket->Id%>-Action" value="approve" />
+ <label for="Approval-<%$ticket->Id%>-Action-approve"><&|/l&>Approve</&></label>
+ </div>
+ <div>
+ <input type="radio" class="radio" id="Approval-<%$ticket->Id%>-Action-deny" name="Approval-<%$ticket->Id%>-Action" value="deny" />
+ <label for="Approval-<%$ticket->Id%>-Action-deny"><&|/l&>Deny</&></label>
+ </div>
+ <div>
+ <input type="radio" class="radio" id="Approval-<%$ticket->Id%>-Action-none" name="Approval-<%$ticket->Id%>-Action" value="none" checked="checked" />
+ <label for="Approval-<%$ticket->Id%>-Action-none"><&|/l&>No action</&></label>
+ </div>
+ </div>
+ <div class="notes">
+ <label for="Approval-<%$ticket->Id%>-Notes"><&|/l&>Notes</&></label>
+ <textarea name="Approval-<%$ticket->Id%>-Notes" id="Approval-<%$ticket->Id%>-Notes" rows="2" cols="70"></textarea>
+ </div>
+ <div class="clear"></div>
+ </div>
+</div>
+<%ARGS>
+$ShowApproving => 1
+$ShowCustomFields => 1
+$ShowHistory => 1
+$ticket => undef
+</%ARGS>
diff --git a/rt/html/Approvals/Elements/PendingMyApproval b/rt/html/Approvals/Elements/PendingMyApproval
new file mode 100644
index 0000000..e03145e
--- /dev/null
+++ b/rt/html/Approvals/Elements/PendingMyApproval
@@ -0,0 +1,111 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<script type="text/javascript"><!--
+ onLoadHook('createCalendarLink("CreatedBefore");');
+ onLoadHook('createCalendarLink("CreatedAfter");');
+--></script>
+% my %done;
+% foreach ($tickets, $group_tickets) {
+% while (my $ticket = $_->Next() ) {
+% next if !$ARGS{'ShowDependent'} and $ticket->HasUnresolvedDependencies( Type => 'approval' );
+% next if $done{$ticket->Id}++; # don't show duplicate tickets
+<& Approve, ticket => $ticket &>
+% }
+% }
+
+<&| /Widgets/TitleBox, title => loc("Search for approvals") &>
+<input type="checkbox" class="checkbox" value="1" name="ShowPending"
+ <%((!$ARGS{'ShowRejected'} && !$ARGS{'ShowResolved'}) ||
+ $ARGS{'ShowPending'})
+ && "checked"%> /> <&|/l&>Show pending requests</&><br />
+<input type="checkbox" class="checkbox" value="1" name="ShowResolved" <%$ARGS{'ShowResolved'} && "checked"%> /> <&|/l&>Show approved requests</&><br />
+<input type="checkbox" class="checkbox" value="1" name="ShowRejected" <%$ARGS{'ShowRejected'} && "checked"%> /> <&|/l&>Show denied requests</&><br />
+<input type="checkbox" class="checkbox" value="1" name="ShowDependent" <%$ARGS{'ShowDependent'} && "checked"%> /> <&|/l&>Show requests awaiting other approvals</&><br />
+
+<&|/l,"<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />"&>Only show approvals for requests created before [_1]</&><br />
+
+<&|/l, "<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />"&>Only show approvals for requests created after [_1]</&>
+</&>
+
+<%init>
+my $tickets = RT::Tickets->new( $session{'CurrentUser'} );
+$tickets->LimitOwner( VALUE => $session{'CurrentUser'}->Id );
+
+# also consider AdminCcs as potential approvers.
+my $group_tickets = RT::Tickets->new( $session{'CurrentUser'} );
+$group_tickets->LimitWatcher( VALUE => $session{'CurrentUser'}->UserObj->EmailAddress, TYPE => 'AdminCc' );
+
+my $created_before = RT::Date->new( $session{'CurrentUser'} );
+my $created_after = RT::Date->new( $session{'CurrentUser'} );
+
+foreach ($tickets, $group_tickets) {
+ $_->Limit( FIELD => 'Type', VALUE => 'approval' );
+
+ if ( $ARGS{'ShowResolved'} ) {
+ $_->LimitStatus( VALUE => 'resolved' );
+ }
+ if ( $ARGS{'ShowRejected'} ) {
+ $_->LimitStatus( VALUE => 'rejected' );
+ }
+ if ( $ARGS{'ShowPending'} || ( !$ARGS{'ShowRejected'} && !$ARGS{'Resolved'} ) ) {
+ $_->LimitStatus( VALUE => 'open' );
+ $_->LimitStatus( VALUE => 'new' );
+ $_->LimitStatus( VALUE => 'stalled' );
+ }
+
+ if ( $ARGS{'CreatedBefore'} ) {
+ $created_before->Set( Format => 'unknown', Value => $ARGS{'CreatedBefore'} );
+ $_->LimitCreated( OPERATOR => "<=", VALUE => $created_before->ISO );
+ }
+ if ( $ARGS{'CreatedAfter'} ) {
+ $created_after->Set( Format => 'unknown', Value => $ARGS{'CreatedAfter'} );
+ $_->LimitCreated( OPERATOR => ">=", VALUE => $created_after->ISO );
+ }
+}
+
+</%init>
diff --git a/rt/html/Approvals/Elements/ShowDependency b/rt/html/Approvals/Elements/ShowDependency
new file mode 100644
index 0000000..6da28f1
--- /dev/null
+++ b/rt/html/Approvals/Elements/ShowDependency
@@ -0,0 +1,109 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% my $approving = $Ticket->DependedOnBy();
+% if ($approving->Count) {
+<h3><&|/l&>Tickets which depend on this approval:</&></h3>
+
+<table width="100%">
+<tr>
+<td width="25" bgcolor="#999999">&nbsp;</td><td>
+<%PERL>
+my %show;
+while (my $link = $approving->Next()) {
+ next unless ($link->BaseURI->IsLocal());
+ my $text = '<a name="' . $link->BaseObj->Id . '">';
+ my $head = '';
+ my $type = $link->BaseObj->Type;
+ my $dep = $m->scomp('ShowDependency', Ticket => $link->BaseObj, _seen => $_seen);
+
+ if ($type eq 'approval') {
+ $head .= $m->scomp('/Widgets/TitleBoxStart', title => loc("Approval #[_1]: [_2]", $link->BaseObj->Id, $link->BaseObj->Subject));
+ $text .= $head;
+ $text .= $m->scomp('/Ticket/Elements/ShowCustomFields', Ticket => $link->BaseObj);
+ } elsif ($type eq 'ticket') {
+ $head .= $m->scomp('/Widgets/TitleBoxStart', title => loc("Ticket #[_1]: [_2]", $link->BaseObj->Id, $link->BaseObj->Subject));
+ $text .= $head;
+ $text .= $m->scomp('/Ticket/Elements/ShowSummary', Ticket => $link->BaseObj);
+ } else {
+ $head .= $m->scomp('/Widgets/TitleBoxStart', title => loc("#[_1]: [_2]", $link->BaseObj->Id, $link->BaseObj->Subject));
+ $text .= $head;
+ }
+
+ $text .= $m->scomp('/Ticket/Elements/ShowHistory' , Ticket => $link->BaseObj, Collapsed => ($type ne 'ticket'), ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0);
+
+ $head .= $m->scomp('/Widgets/TitleBoxEnd');
+ $text .= $m->scomp('/Widgets/TitleBoxEnd');
+ $text .= $dep;
+ $text .= '</a>';
+ $show{$link->BaseObj->Id} = {
+ text => $text,
+ head => $head,
+ };
+}
+
+my $refer;
+foreach my $id (sort keys %show) {
+ if ($_seen->{$id}++) {
+ $refer .= "<a href='#txn-$id'>" . $show{$id}{head} . "</a>";
+ next;
+ }
+
+ $m->print($show{$id}{text});
+}
+$m->print($refer);
+
+</%PERL>
+</td>
+</tr>
+</table>
+
+% }
+<%ARGS>
+$Ticket
+$_seen => {}
+</%ARGS>
diff --git a/rt/html/Approvals/Elements/Tabs b/rt/html/Approvals/Elements/Tabs
new file mode 100644
index 0000000..9ba4250
--- /dev/null
+++ b/rt/html/Approvals/Elements/Tabs
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Tabs,
+ tabs => $tabs,
+ current_toptab => 'Approvals/',
+ current_tab => $current_tab,
+ Title => $Title &>
+
+<%ARGS>
+$tabs => undef
+$current_tab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Approvals/index.html b/rt/html/Approvals/index.html
new file mode 100644
index 0000000..ab91ffe
--- /dev/null
+++ b/rt/html/Approvals/index.html
@@ -0,0 +1,90 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("My approvals") &>
+<& /Approvals/Elements/Tabs, Title => loc("My approvals") &>
+
+<& /Elements/ListActions, actions => \@actions &>
+<form method="post">
+<& Elements/PendingMyApproval, %ARGS &>
+<& /Elements/Submit, Label => loc('Go!') &>
+</form>
+<%init>
+
+my (@actions);
+foreach my $arg ( keys %ARGS ) {
+
+ next unless ( $arg =~ /Approval-(\d+)-Action/ );
+
+ my ( $notesval, $notesmsg );
+
+ my $ticket = LoadTicket($1);
+
+ if ( $ARGS{ "Approval-" . $ticket->Id . "-Notes" } ) {
+ my $notes = MIME::Entity->build(
+ Data => [ $ARGS{ "Approval-" . $ticket->Id . "-Notes" } ]
+ );
+ RT::I18N::SetMIMEEntityToUTF8($notes); # convert text parts into utf-8
+
+ my ( $notesval, $notesmsg ) = $ticket->Correspond( MIMEObj => $notes );
+ if ($notesval) {
+ push ( @actions, loc("Approval #[_1]: Notes recorded",$ticket->Id ));
+ } else {
+ push ( @actions, loc("Approval #[_1]: Notes not recorded due to a system error",$ticket->Id ));
+ }
+ }
+
+ my ($val, $msg);
+ if ( $ARGS{$arg} eq 'deny' ) {
+ ( $val, $msg ) = $ticket->SetStatus('rejected');
+ }
+ elsif ( $ARGS{$arg} eq 'approve' ) {
+ ( $val, $msg ) = $ticket->SetStatus('resolved');
+ }
+ push ( @actions, loc("Approval #[_1]: [_2]",$ticket->id, $msg )) if ($msg);
+}
+</%init>
diff --git a/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default b/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default
new file mode 100644
index 0000000..f85d2e0
--- /dev/null
+++ b/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default
@@ -0,0 +1,7 @@
+<%init>
+if ($ARGS{current_toptab} eq "Tools/Offline.html") {
+ $ARGS{tabs}{r} ||= { path => 'Reports/Activity/index.html',
+ title => 'Reports',
+ };
+}
+</%init> \ No newline at end of file
diff --git a/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default b/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default
new file mode 100644
index 0000000..30480f7
--- /dev/null
+++ b/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default
@@ -0,0 +1,71 @@
+table.miniplot {
+ width: 100%;
+ border-collapse: collapse;
+}
+table.miniplot td {
+ margin: 0;
+ padding: 0;
+ border-bottom: 1px solid black;
+}
+table.miniplot .graph {
+ margin-left: auto;
+ margin-right: auto;
+ position: relative;
+ width: 60px;
+}
+table.miniplot .graph ul {
+ height: 100px;
+ margin: 0;
+ padding: 0;
+}
+table.miniplot .graph ul li {
+ list-style: none;
+ position: absolute;
+ bottom: 0px;
+ padding: 0 !important;
+ margin: 0 !important;
+ border-bottom: none;
+}
+table.miniplot .graph ul li .data {
+ display: none;
+}
+
+.miniplot .demoblock { margin: 0 10px; padding: 0 30px; }
+
+.miniplot .c1 { border: 2px solid #990000; background: #ff0000; }
+.miniplot .c2 { border: 2px solid #996600; background: #ff9900; }
+.miniplot .c3 { border: 2px solid #009900; background: #00ff00; }
+.miniplot .c4 { border: 2px solid #009999; background: #00ffff; }
+.miniplot .c5 { border: 2px solid #000099; background: #0000ff; }
+.miniplot .c6 { border: 2px solid #990099; background: #ff00ff; }
+graph .c5 { border: 2px solid #000099; background: #0000ff; }
+.graph .c6 { border: 2px solid #990099; background: #ff00ff; }
+
+tr.titlerow th {
+
+ border-bottom: solid black 1px;
+ margin: 0;
+ font-size:80%;
+ text-wrap: none;
+
+}
+
+tr.grandtotal td{
+ border-top: 1px solid black;
+}
+
+tr.grandtotal th{
+ border-top: 1px solid black;
+}
+
+th.label {
+ align: left;
+
+}
+
+table.miniplot th.legend {
+ font-style: normal;
+ font-size: 80%;
+
+}
+
diff --git a/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions b/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions
new file mode 100644
index 0000000..4775a9a
--- /dev/null
+++ b/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions
@@ -0,0 +1,7 @@
+<a href="<% $RT::WebPath %>/Reports/Activity/index.html?<% $QueryString %>">Generate reports</a>
+<%init>
+use YAML;
+my %args = $m->caller_args(2);
+
+my $QueryString = $m->comp('/Elements/QueryString', query => $args{Query});
+</%init> \ No newline at end of file
diff --git a/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default b/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default
new file mode 100644
index 0000000..db74ced
--- /dev/null
+++ b/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default
@@ -0,0 +1,13 @@
+%# The day after tomorrow is the third day of the rest of your life.
+<%INIT>
+if ($session{'CurrentUser'}->UserObj->HasRight(
+ Right => 'SuperUser',
+ Object => $RT::System,
+)) {
+ $toptabs->{'ZZ-RT-WebCronTool'} = { title =>loc("Web CronTool"),
+ path => "Developer/CronTool/index.html" };
+}
+</%init>
+<%args>
+$toptabs =>undef
+</%args>
diff --git a/rt/html/Callbacks/kStatistics/Elements/Tabs/Default b/rt/html/Callbacks/kStatistics/Elements/Tabs/Default
new file mode 100644
index 0000000..d4ca2b9
--- /dev/null
+++ b/rt/html/Callbacks/kStatistics/Elements/Tabs/Default
@@ -0,0 +1,11 @@
+<%init>
+use RTx::Statistics;
+if (($Statistics::RestrictAccess == 0) || ($session{'CurrentUser'}->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System ))) {
+ $toptabs->{'ZZ-RTx-STATS'} = { title => 'RTx-Statistics',
+ path => "RTx/Statistics/index.html" };
+}
+</%init>
+<%args>
+ $toptabs =>undef
+</%args>
diff --git a/rt/html/Developer/CronTool/autohandler b/rt/html/Developer/CronTool/autohandler
new file mode 100644
index 0000000..7daa09e
--- /dev/null
+++ b/rt/html/Developer/CronTool/autohandler
@@ -0,0 +1,9 @@
+%# All theoretical chemistry is really physics;
+%# and all theoretical chemists know it.
+%# -- Richard P. Feynman
+<%INIT>
+$m->call_next(%ARGS) if $session{'CurrentUser'}->UserObj->HasRight(
+ Right => 'SuperUser',
+ Object => $RT::System,
+);
+</%INIT>
diff --git a/rt/html/Developer/CronTool/index.html b/rt/html/Developer/CronTool/index.html
new file mode 100644
index 0000000..67c9e56
--- /dev/null
+++ b/rt/html/Developer/CronTool/index.html
@@ -0,0 +1,116 @@
+% if ($@) {
+<P><FONT Color="red"><% $@ %></FONT></P>
+% }
+% if (!$NoUI) {
+<HR>
+<FORM Action="index.html" Method="POST">
+<TABLE>
+% foreach my $class (qw( Search Condition Action )) {
+<TR><TH>
+<% loc($class) %>
+</TH><TD>
+<SELECT NAME="<% $class %>">
+% require File::Find;
+% my @modules;
+% File::Find::find(sub {
+% push @modules, $1 if /^(?!Generic|UserDefined)(\w+)\.pm$/i;
+% }, grep -d, map "$_/RT/$class", @INC);
+<OPTION <% $ARGS{$class} ? '' : 'SELECTED' %>></OPTION>
+% foreach my $module (sort @modules) {
+% my $fullname = "RT::$class\::$module";
+ <OPTION VALUE="<% $fullname %>" <% ($fullname eq $ARGS{$class}) ? 'SELECTED' : '' %>><% $module %></OPTION>
+% }
+</SELECT>
+</TD><TH>
+<&|/l&>Parameter</&>
+</TH><TD>
+<INPUT NAME="<% $class %>Arg" VALUE="<% $ARGS{$class.'Arg'} %>">
+</TD></TR>
+% }
+<TR>
+<TD COLSPAN="4" ALIGN="Right">
+<LABEL>
+<INPUT TYPE="CheckBox" NAME="Verbose" <% $Verbose ? 'CHECKED' : '' %>><&|/l&>Verbose</&>
+</LABEL>
+<INPUT TYPE="Submit" VALUE="<&|/l&>Run</&>">
+</TD>
+</TABLE>
+</FORM>
+<HR>
+% }
+<%INIT>
+$m->print("<H1>", loc("Web CronTool"), "</H1>");
+if ($Search) {
+ my $load_module = sub {
+ my $modname = $_[0];
+ $modname =~ s{::}{/}g;
+ require "$modname.pm" or die (
+ loc( "Failed to load module [_1]. ([_2])", $_[0], $@ ) . "\n"
+ );
+ };
+ $m->print(loc("Starting..."), "<UL>");
+ eval {
+ $load_module->($Search);
+ $load_module->($Action) if $Action;
+ $load_module->($Condition) if $Condition;
+
+ if ($TemplateId and !$TemplateObj) {
+ $TemplateObj = RT::Template->new($RT::Nobody);
+ $TemplateObj->LoadById($TemplateId);
+ }
+
+ my $tickets = RT::Tickets->new($RT::SystemUser);
+ my $search = $Search->new( TicketsObj => $tickets, Argument => $SearchArg );
+ $search->Prepare;
+ my $tickets_found = $search->TicketsObj;
+
+ #for each ticket we've found
+ while ( my $ticket = $tickets_found->Next ) {
+ $m->print("<LI>" . $ticket->Id . ": ") if $Verbose;
+ $m->print(loc("Checking...")) if $Verbose;
+
+ # perform some more advanced check
+ if ($Condition) {
+ my $ConditionObj = $Condition->new(
+ TicketObj => $ticket,
+ Argument => $ConditionArg
+ );
+
+ # if the condition doesn't apply, get out of here
+ next unless ( $ConditionObj->IsApplicable );
+ $m->print(loc("Condition matches...")) if $Verbose;
+ }
+
+ if ($Action) {
+ #prepare our action
+ my $ActionObj = $Action->new(
+ TicketObj => $ticket,
+ TemplateObj => $TemplateObj,
+ Argument => $ActionArg
+ );
+
+ #if our preparation, move onto the next ticket
+ next unless ( $ActionObj->Prepare );
+ $m->print(loc("Action prepared...")) if $Verbose;
+
+ #commit our action.
+ next unless ( $ActionObj->Commit );
+ $m->print(loc("Action committed.")) if $Verbose;
+ }
+ }
+ };
+ $m->print('</UL>', loc("Finished."));
+}
+</%INIT>
+<%ARGS>
+$Search => undef
+$SearchArg => undef
+$Condition => undef
+$ConditionArg => undef
+$Action => undef
+$ActionArg => undef
+$TemplateId => undef
+$TemplateObj => undef
+$Verbose => 1
+$NoUI => 0
+</%ARGS>
diff --git a/rt/html/Download/CustomFieldValue/dhandler b/rt/html/Download/CustomFieldValue/dhandler
new file mode 100644
index 0000000..461267e
--- /dev/null
+++ b/rt/html/Download/CustomFieldValue/dhandler
@@ -0,0 +1,77 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%perl>
+my $id;
+my $arg = $m->dhandler_arg; # get rest of path
+if ($arg =~ /^(\d+)\//) {
+ $id = $1;
+}
+else {
+ Abort("Corrupted customfieldvalue URL.");
+}
+my $OCFV = RT::ObjectCustomFieldValue->new($session{'CurrentUser'});
+$OCFV->Load($id) || Abort("OCFV '$id' could not be loaded");
+
+unless ($OCFV->id) {
+ Abort("Bad OCFV id. Couldn't find OCFV '$id'\n");
+}
+
+my $content_type = $OCFV->ContentType || 'text/plain';
+
+unless ($RT::TrustHTMLAttachments) {
+ $content_type = 'text/plain' if ($content_type =~ /^text\/html/i);
+}
+
+$r->content_type( $content_type );
+$m->clear_buffer();
+$m->out($OCFV->LargeContent);
+$m->abort;
+</%perl>
+<%attr>
+AutoFlush => 0
+</%attr>
diff --git a/rt/html/Download/Tabular/dhandler b/rt/html/Download/Tabular/dhandler
new file mode 100644
index 0000000..0abb8f9
--- /dev/null
+++ b/rt/html/Download/Tabular/dhandler
@@ -0,0 +1,76 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%perl>
+my ($class, $filename, @ids);
+my $arg = $m->dhandler_arg; # get rest of path
+if ($arg =~ /^(\w+)\/([,\d]+)(?:\/([^\/]+))?/) {
+ $class = "RT::$1";
+ $filename = $3 || "$1s.tsv";
+ @ids = sort split(/,+/, $2);
+}
+else {
+ Abort("Corrupted tabular URL.");
+}
+
+my @cols = $class->BasicColumns or return;
+
+#$r->content_type( 'application/octet-stream' );
+$r->content_type( 'text/plain' );
+$r->headers_out->{'Content-Disposition'} = "attachment; filename=$filename";
+$m->clear_buffer();
+$m->out(join("\t", "Id", map $_->[1], @cols), "\n");
+foreach my $id (@ids) {
+ my $obj = $class->new;
+ $obj->Load($id) or next;
+ $m->out(join("\t", map $obj->$_, "Id", map $_->[0], @cols), "\n");
+}
+$m->abort;
+</%perl>
+<%attr>
+AutoFlush => 0
+</%attr>
diff --git a/rt/html/Elements/AddCustomers b/rt/html/Elements/AddCustomers
new file mode 100644
index 0000000..aaf8ca8
--- /dev/null
+++ b/rt/html/Elements/AddCustomers
@@ -0,0 +1,59 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+<BR>
+<%$msg%><br>
+
+% if (@Customers) {
+
+<br><i>(Check box to link)<i>
+<table>
+% foreach my $customer (@Customers) {
+<tr>
+ <td>
+ <input type="checkbox" name="Object-AddCustomer-<% $customer->{'custnum'} %>" VALUE="1" <% scalar(@Customers) == 1 ? 'CHECKED' : '' %>>
+ <A HREF="<%$freeside_url%>/view/cust_main.cgi?<% $customer->{'custnum'} %>"><% &RT::URI::freeside::small_custview($customer->{'custnum'}, &RT::URI::freeside::FreesideGetConfig('countrydefault'), 1) |n %>
+ </td>
+</tr>
+% }
+</table>
+
+% }
+
+<%INIT>
+my ($msg);
+
+my $freeside_url = &RT::URI::freeside::FreesideURL();
+
+warn "/Elements/AddCustomers called with CustomerString $CustomerString\n"
+ if $Debug;
+
+my @Customers = ();
+if ( $CustomerString ) {
+ @Customers = &RT::URI::freeside::smart_search( 'search' => $CustomerString );
+}
+
+my @Services = ();
+if ($ServiceString) {
+ @Services = (); #service_search();
+}
+
+warn "/Elements/AddCustomers displaying ". scalar(@Customers). " customers\n"
+ if $Debug;
+
+</%INIT>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+$Debug => 0
+</%ARGS>
diff --git a/rt/html/Elements/BevelBoxRaisedEnd b/rt/html/Elements/BevelBoxRaisedEnd
new file mode 100644
index 0000000..b1ba8f6
--- /dev/null
+++ b/rt/html/Elements/BevelBoxRaisedEnd
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+ </td>
+</tr>
+</table>
diff --git a/rt/html/Elements/BevelBoxRaisedStart b/rt/html/Elements/BevelBoxRaisedStart
new file mode 100644
index 0000000..b690c1d
--- /dev/null
+++ b/rt/html/Elements/BevelBoxRaisedStart
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table cellspacing="0" cellpadding="0" width="100%" height="100%">
+ <tr>
+ <td width="100%" height="100%">
diff --git a/rt/html/Elements/Callback b/rt/html/Elements/Callback
new file mode 100644
index 0000000..b498f28
--- /dev/null
+++ b/rt/html/Elements/Callback
@@ -0,0 +1,92 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%once>
+my %cache;
+</%once>
+<%init>
+$Page = $m->callers(1)->path unless ($Page);
+
+my $CacheKey = "Callback--$Page--$_CallbackName";
+my $callbacks = $cache{$CacheKey} || $m->notes($CacheKey);
+
+if (!$callbacks) {
+ my $path = "/Callbacks/*$Page/$_CallbackName";
+
+ # Due to API changes after Mason 1.28, we have to check for which
+ # version we're running when getting the component roots
+ my @roots = map { $_->[1] }
+ $HTML::Mason::VERSION <= 1.28
+ ? $m->interp->resolver->comp_root_array
+ : $m->interp->comp_root_array;
+
+ my %seen;
+
+ for my $root (@roots) {
+ push @$callbacks,
+ # Skip backup files, files without a leading package name,
+ # and files we've already seen
+ grep { !/\/\.|~$/
+ and $_ ne "/Callbacks/$Page/$_CallbackName"
+ and not $seen{$_}++ }
+ $m->interp->resolver->glob_path($path, $root);
+ }
+
+ $m->notes($CacheKey => $callbacks);
+ $cache{$CacheKey} = $callbacks if !$RT::DevelMode;
+}
+
+my @rv;
+foreach my $comp (sort @$callbacks) {
+ push @rv, $m->comp($comp, %ARGS);
+}
+return @rv;
+</%init>
+<%args>
+$_CallbackName => 'Default'
+$Page => undef
+</%args>
diff --git a/rt/html/Elements/Checkbox b/rt/html/Elements/Checkbox
new file mode 100644
index 0000000..5593c7a
--- /dev/null
+++ b/rt/html/Elements/Checkbox
@@ -0,0 +1,63 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<input type="checkbox" class="checkbox" name="<%$Name%>" value="1" <%$IsChecked%> />
+
+<%ARGS>
+$Name => undef
+$Default => undef
+$True => undef
+$False => undef
+$IsChecked => undef
+</%ARGS>
+
+<%INIT>
+$IsChecked =
+ ($Default && $Default =~ /checked/i)
+ ? " CHECKED " : "";
+1;
+</%INIT>
diff --git a/rt/html/Elements/CollectionAsTable/Header b/rt/html/Elements/CollectionAsTable/Header
new file mode 100644
index 0000000..a3277b3
--- /dev/null
+++ b/rt/html/Elements/CollectionAsTable/Header
@@ -0,0 +1,125 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%ARGS>
+@Format => undef
+$FormatString => undef
+$AllowSorting => undef
+$Order=>undef
+$BaseURL => undef
+$Query => undef
+$Rows => undef
+$Page => undef
+$maxitems => undef
+</%ARGS>
+<tr class="collection-as-table">
+<%perl>
+
+my %generic_query_args = ( Query => $Query, Rows => $Rows, Page => $Page, Format => $FormatString );
+
+my $item = 0;
+foreach my $col (@Format) {
+ $item++;
+ if ( $col->{title} && ($col->{title} eq 'NEWLINE') ) {
+ while ( $item < $maxitems ) {
+ $m->out(qq{<th class="collection-as-table">&nbsp;</th>\n});
+ $item++;
+ }
+
+ $item = 0;
+ $m->out(qq{</tr>\n<tr class="collection-as-table">});
+ }
+ else {
+ $m->out('<th class="collection-as-table">');
+ my $title = $col->{title} || '';
+ $title =~ s/^__(.*)__$/$1/o;
+ $title = (
+ $m->comp(
+ '/Elements/RT__Ticket/ColumnMap',
+ Name => $title,
+ Attr => 'title'
+ )
+ || $title
+ );
+ if (
+ $AllowSorting
+ && $col->{'attribute'}
+ && $m->comp(
+ '/Elements/RT__Ticket/ColumnMap',
+ Name => $col->{'attribute'},
+ Attr => 'attribute'
+ )
+ )
+ {
+
+ $m->out(
+ '<a href="' . $BaseURL
+ . $m->comp(
+ '/Elements/QueryString',
+ %generic_query_args,
+ OrderBy => (
+ $m->comp(
+ '/Elements/RT__Ticket/ColumnMap',
+ Name => $col->{'attribute'},
+ Attr => 'attribute'
+ )
+ || $col->{'attribute'}
+ ),
+ Order => ( $ARGS{'Order'} eq 'ASC' ? 'DESC' : 'ASC' )
+ )
+ . '">'
+ . loc($title) . '</a>'
+ );
+ }
+ else {
+ $m->out( loc($title) );
+ }
+ $m->out('</th>');
+ }
+}
+</%perl>
+</tr>
diff --git a/rt/html/Elements/CollectionAsTable/ParseFormat b/rt/html/Elements/CollectionAsTable/ParseFormat
new file mode 100644
index 0000000..2270760
--- /dev/null
+++ b/rt/html/Elements/CollectionAsTable/ParseFormat
@@ -0,0 +1,106 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%ARGS>
+$Format
+</%ARGS>
+
+<%init>
+use Regexp::Common qw/delimited/;
+my @Columns;
+
+while ($Format =~ /($RE{delimited}{-delim=>qq{\'"}}|[{}\w.]+)/go) {
+ my $col = $1;
+
+ if ($col =~ /^$RE{quoted}$/o) {
+ substr($col,0,1) = "";
+ substr($col,-1,1) = "";
+ }
+
+ my $colref = {
+ title => '',
+ };
+
+ if ( $col =~ s!/STYLE:([^/]+)!!io ) {
+ $colref->{'style'} = $1;
+ }
+ if ( $col =~ s!/CLASS:([^/]+)!!io ) {
+ $colref->{'class'} = $1;
+ }
+ if ( $col =~ s!/TITLE:([^/]+)!!io ) {
+ $colref->{'title'} = $1;
+ }
+ if ( $col =~ s!/ALIGN:([^\/]+)!!io ) {
+ $colref->{'align'} = $1;
+ }
+ if ( $col =~ /__(.*?)__/gio ) {
+ my @subcols;
+ while ( $col =~ s/^(.*?)__(.*?)__//o ) {
+ push ( @subcols, $1 ) if ($1);
+ push ( @subcols, "__$2__" );
+ $colref->{'attribute'} = $2;
+ }
+ push ( @subcols, $col );
+ @{ $colref->{'output'} } = @subcols;
+ }
+ else {
+ @{ $colref->{'output'} } = ( "__" . $col . "__" );
+ $colref->{'attribute'} = $col;
+ }
+
+ if ( !$colref->{'title'} && grep { /^__(.+?)__$/io }
+ @{ $colref->{'output'} } )
+ {
+ $colref->{'title'} = $1;
+ $colref->{'attribute'} = $1;
+ }
+
+
+ push @Columns, $colref;
+}
+ return(@Columns);
+</%init>
diff --git a/rt/html/Elements/CollectionAsTable/Row b/rt/html/Elements/CollectionAsTable/Row
new file mode 100644
index 0000000..d849226
--- /dev/null
+++ b/rt/html/Elements/CollectionAsTable/Row
@@ -0,0 +1,117 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%ARGS>
+$i => undef
+@Format => undef
+$record => undef
+$maxitems => undef
+$Depth => undef
+$Warning => undef
+</%ARGS>
+
+<%PERL>
+use HTML::Entities;
+$m->out('<tr class="' . ( $Warning ? 'warnline' : $i % 2 ? 'oddline' : 'evenline' ) . '" >' );
+my $item;
+foreach my $column (@Format) {
+ if ( defined $column->{title} && $column->{title} eq 'NEWLINE' ) {
+ while ( $item < $maxitems ) {
+ $m->out(qq{<td class="collection-as-table">&nbsp;</td>\n});
+ $item++;
+ }
+ $item = 0;
+ $m->out('</tr>');
+ $m->out('<tr class="'
+ . ( $Warning ? 'warnline' : $i % 2 ? 'oddline' : 'evenline' )
+ . '" >' );
+ next;
+ }
+ $item++;
+ my $class = $column->{class}
+ ? encode_entities($column->{class}, q{'"&<>}) : 'collection-as-table';
+ $m->out(qq{<td class="$class" });
+ $m->out( 'align="' . $column->{align} . '"' ) if ( $column->{align} );
+ $m->out( 'style="' . $column->{style} . '"' ) if ( $column->{style} );
+ $m->out('>');
+ foreach my $subcol ( @{ $column->{output} } ) {
+ if ( $subcol =~ /^__(.*?)__$/o ) {
+ my $col = $1;
+ my $value = $m->comp(
+ '/Elements/RT__Ticket/ColumnMap',
+ Name => $col,
+ Attr => 'value'
+ );
+ my @out;
+
+ if ( $value && ref($value) ) {
+
+ # All HTML snippets are returned by the callback function
+ # as scalar references. Data fetched from the objects are
+ # plain scalars, and needs to be escaped properly.
+ @out =
+ map {
+ ref($_) ? $$_ : $m->interp->apply_escapes( $_ => 'h' )
+ } &{$value}( $record, $i )
+ ;
+ }
+ else {
+
+ # Simple value; just escape it.
+ @out = $m->interp->apply_escapes( $value => 'h' );
+ }
+ s/\n/<br \/>/gs for @out;
+ $m->out( @out );
+ }
+ else {
+ $m->out($subcol);
+ }
+ }
+ $m->out('</td>');
+}
+$m->out('</tr>');
+</%PERL>
diff --git a/rt/html/Elements/CreateTicket b/rt/html/Elements/CreateTicket
new file mode 100644
index 0000000..644df22
--- /dev/null
+++ b/rt/html/Elements/CreateTicket
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<form action="<% $RT::WebPath %>/Ticket/Create.html" name="CreateTicketInQueue">
+<&|/l, $m->scomp('/Elements/SelectNewTicketQueue')&><input type="submit" class="button" value="New ticket in" />&nbsp;[_1]</&>
+</form>
diff --git a/rt/html/Elements/EditCustomField b/rt/html/Elements/EditCustomField
new file mode 100644
index 0000000..d247c63
--- /dev/null
+++ b/rt/html/Elements/EditCustomField
@@ -0,0 +1,99 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%INIT>
+my $Values;
+if ($Object and $Object->id) {
+ $Values = $Object->CustomFieldValues($CustomField->id);
+ $Values->Columns( qw( id CustomField ObjectType ObjectId Disabled Content ContentType ContentEncoding ) );
+ $NamePrefix ||= join('-', 'Object', ref($Object), $Object->Id, 'CustomField', '');
+} elsif (not $Default) {
+ my %TOP = $m->request_args;
+ $Default = $TOP{ $NamePrefix .$CustomField->Id . '-Values' }
+ || $TOP{ $NamePrefix .$CustomField->Id . '-Value' };
+}
+my $Type = $CustomField->Type;
+
+return unless ($Type); # if we can't see the type, all hell will break loose.
+
+my $MaxValues = $CustomField->MaxValues;
+if ($MaxValues == 1 and $Object and $Values) {
+ # what exactly is this doing? Without the "unless" it breaks RTFM
+ # transaction extraction into articles.
+ $Default = ($Values->First ? $Values->First->Content : '') unless $Default;
+ $Values->GotoFirstItem;
+}
+# The "Magic" hidden input causes RT to know that we were trying to edit the field, even if
+# we don't see a value later, since browsers aren't compelled to submit empty form fields
+$m->out("\n".'<input type="hidden" class="hidden" name="'.$NamePrefix.$CustomField->Id.'-Values-Magic" value="1" />'."\n");
+
+my $EditComponent = "EditCustomField$Type";
+$m->comp('/Elements/Callback', _CallbackName => 'EditComponentName', Name => \$EditComponent, CustomField => $CustomField, Object => $Object );
+$EditComponent = "EditCustomField$Type" unless $m->comp_exists($EditComponent);
+
+return $m->comp(
+ $EditComponent,
+ %ARGS,
+ Rows => $Rows,
+ Cols => $Cols,
+ Default => $Default,
+ Object => $Object,
+ Values => $Values,
+ MaxValues => $MaxValues,
+ Multiple => ($MaxValues != 1),
+ NamePrefix => $NamePrefix,
+ CustomField => $CustomField,
+);
+</%INIT>
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Rows => 5
+$Cols => 15
+$Default => undef
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomFieldBinary b/rt/html/Elements/EditCustomFieldBinary
new file mode 100644
index 0000000..485457c
--- /dev/null
+++ b/rt/html/Elements/EditCustomFieldBinary
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% while ($Values and my $value = $Values->Next ) {
+%# XXX - let user download the file(s) here?
+<input type="checkbox" class="checkbox" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" value="<% $value->Id %>" /><a href="<%$RT::WebPath%>/Download/CustomFieldValue/<% $value->Id %>/<% $value->Content %>"><% $value->Content %></a><br />
+% }
+% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
+<input type="file" name="<%$NamePrefix%><%$CustomField->Id%>-Upload" />
+% }
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Default => undef
+$Values => undef
+$MaxValues => undef
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomFieldCombobox b/rt/html/Elements/EditCustomFieldCombobox
new file mode 100644
index 0000000..071ef07
--- /dev/null
+++ b/rt/html/Elements/EditCustomFieldCombobox
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% while ($Values and my $value = $Values->Next and $Multiple) {
+<input type="checkbox" class="checkbox" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" value="<% $value->Id %>" /><% $value->Content %>
+<br />
+% }
+% (!$Multiple or !$MaxValues or !$Values or $Values->Count < $MaxValues) or return;
+<& /Widgets/ComboBox,
+ Name => $NamePrefix . $CustomField->Id . "-Value",
+ Default => $Default,
+ Rows => $Rows,
+ Values => [map {$_->Name} @{$CustomField->Values->ItemsArrayRef}],
+&>
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Default => undef
+$Values => undef
+$Multiple => 0
+$Rows => undef
+$MaxValues => undef
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomFieldFreeform b/rt/html/Elements/EditCustomFieldFreeform
new file mode 100644
index 0000000..279632d
--- /dev/null
+++ b/rt/html/Elements/EditCustomFieldFreeform
@@ -0,0 +1,74 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% my $name = $NamePrefix . $CustomField->Id . '-Value';
+% if ($Multiple) {
+<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$name%>s" id="<%$name%>s" ><%$Default%></textarea>
+% } else {
+<input name="<%$name%>" id="<%$name%>" size="<%$Cols%>" value="<%$Default ? $Default : ''%>" />
+% }
+<%INIT>
+if ($Multiple and $Values) {
+ $Default = '';
+ while (my $value = $Values->Next ) {
+ $Default .= $value->Content."\n";
+ }
+}
+elsif ( ! $Multiple ) {
+ $Default =~ s/\s*\n\s*/ /g if $Default;
+}
+</%INIT>
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Default => undef
+$Values => undef
+$Multiple => undef
+$Cols
+$Rows
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomFieldImage b/rt/html/Elements/EditCustomFieldImage
new file mode 100644
index 0000000..b6a30c6
--- /dev/null
+++ b/rt/html/Elements/EditCustomFieldImage
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% while ($Values and my $value = $Values->Next ) {
+<input type="checkbox" class="checkbox" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" value="<% $value->Id %>" /><& ShowCustomFieldImage, Object => $value &>
+<br />
+% }
+% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
+<input type="file" name="<%$NamePrefix%><%$CustomField->Id%>-Upload" />
+% }
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Default => undef
+$Values => undef
+$MaxValues => undef
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomFieldSelect b/rt/html/Elements/EditCustomFieldSelect
new file mode 100644
index 0000000..815e977
--- /dev/null
+++ b/rt/html/Elements/EditCustomFieldSelect
@@ -0,0 +1,128 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# Build up the set of cascading select boxes as "guides"
+%# each one limits the options of the final one a bit
+%# (perhaps by tweaking the .display style?)
+% my $selected = 0;
+% my @category;
+% my $id = $NamePrefix . $CustomField->Id;
+% my $out = $m->scomp('SELF:options', %ARGS, SelectedRef => \$selected, CategoryRef => \@category);
+% if (@category) {
+<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/cascaded.js"></script>
+%# XXX - Hide this select from w3m?
+ <select onchange="filter_cascade('<% $id %>-Values', this.value)" name="<%$id%>-Category">
+ <option value="" <% !$selected && 'SELECTED' %>><&|/l&>-</&></option>
+% foreach my $cat (@category) {
+% my ($depth, $name) = @$cat;
+ <option value="<% $name %>"><% '&nbsp;' x $depth |n %><%$name%></option>
+% }
+ </select><br />
+% }
+ <select name="<%$id%>-Values" id="<%$id%>-Values"
+% if ($Multiple or !@category) {
+ size="<%$Rows%>"
+% }
+ <% $Multiple && 'MULTIPLE' %>>
+ <option value="" <% !$selected && 'SELECTED' %>><&|/l&>(no value)</&></option>
+% $m->out($out);
+ </select>
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Default => undef
+$Values => undef
+$Multiple => 0
+$Rows => undef
+</%ARGS>
+
+<%method options>
+% my $selected;
+% my $CFVs = $CustomField->Values;
+% my @levels;
+% while ($CFVs and my $value = $CFVs->Next ) {
+% my $category = $value->Category;
+% if (1) { # length $category) {
+% my $level = (split(/:/, $category || ''))[0];
+% while (@levels) {
+% if ($levels[-1] eq $level) {
+% undef $level;
+% last;
+% } elsif (index($level, $levels[-1]) != 0) {
+ </optgroup>
+% pop @levels;
+% } else {
+% last;
+% }
+% }
+% if ($level) {
+% push @$CategoryRef, [0+@levels, $level];
+ <optgroup style="padding-left: <% @levels/2 %>em" label="<%$category%>">
+% push @levels, $level;
+% }
+% }
+ <option value="<%$value->Name%>"
+% if ($Values) {
+ <% ($Values->HasEntry($value->Name)||'') && ($$SelectedRef = 1) && 'SELECTED' %>
+% } elsif ($Default) {
+ <% (ref $Default ? (grep {$_ eq $value->Name} @{$Default}) : ($Default eq $value->Name))
+ && ($$SelectedRef = 1) && 'SELECTED' %>
+% }
+ ><% $value->Name%></option>
+% }
+% for (@levels) {
+ </optgroup>
+% }
+<%args>
+$CustomField => undef
+$Default => undef
+$Values => undef
+$SelectedRef => undef
+$CategoryRef => undef
+</%args>
+</%method>
diff --git a/rt/html/Elements/EditCustomFieldText b/rt/html/Elements/EditCustomFieldText
new file mode 100644
index 0000000..b7569b0
--- /dev/null
+++ b/rt/html/Elements/EditCustomFieldText
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% while ($Values and my $value = $Values->Next ) {
+<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values"><% $value->Content %></textarea><br />
+% }
+% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
+<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values"><% $Default %></textarea>
+% }
+<%INIT>
+# XXX - MultiValue textarea is for now outlawed.
+$MaxValues = 1;
+</%INIT>
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Default => undef
+$Values => undef
+$MaxValues => undef
+$Cols
+$Rows
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomFieldWikitext b/rt/html/Elements/EditCustomFieldWikitext
new file mode 100644
index 0000000..b7569b0
--- /dev/null
+++ b/rt/html/Elements/EditCustomFieldWikitext
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% while ($Values and my $value = $Values->Next ) {
+<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values"><% $value->Content %></textarea><br />
+% }
+% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
+<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values"><% $Default %></textarea>
+% }
+<%INIT>
+# XXX - MultiValue textarea is for now outlawed.
+$MaxValues = 1;
+</%INIT>
+<%ARGS>
+$Object => undef
+$CustomField => undef
+$NamePrefix => undef
+$Default => undef
+$Values => undef
+$MaxValues => undef
+$Cols
+$Rows
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomers b/rt/html/Elements/EditCustomers
new file mode 100644
index 0000000..68efb5f
--- /dev/null
+++ b/rt/html/Elements/EditCustomers
@@ -0,0 +1,63 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+<TABLE width=100%>
+ <TR>
+ <TD VALIGN=TOP WIDTH=50%>
+ <h3><&|/l&>Current Customers</&></h3>
+
+<table>
+ <tr>
+ <td><i><&|/l&>(Check box to disassociate)</&></i></td>
+ </tr>
+ <tr>
+ <td class="value">
+% foreach my $link ( @{ $Object->Customers->ItemsArrayRef } ) {
+
+ <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
+%# <& ShowLink, URI => $link->TargetURI &><br>
+ <A HREF="<% $link->TargetURI->Resolver->HREF %>"><% $link->TargetURI->Resolver->AsStringLong |n %></A>
+ <BR>
+% }
+ </td>
+ </tr>
+</table>
+
+</TD>
+
+<TD VALIGN=TOP>
+<h3><&|/l&>New Customer Links</&></h3>
+<&|/l&>Find customer</&><BR>
+<input name="CustomerString">
+<input type=submit name="OnlySearchForCustomers" value="<&|/l&>Go!</&>">
+<br><i>cust #, name, company or phone</i>
+<BR>
+%#<BR>
+%#<&|/l&>Find service</&><BR>
+%#<input name="ServiceString">
+%#<input type=submit name="OnlySearchForServices" value="<&|/l&>Go!</&>">
+%#<br><i>username, username@domain, domain, or IP address</i>
+%#<BR>
+
+<& AddCustomers, Object => $Object,
+ CustomerString => $CustomerString,
+ ServiceString => $ServiceString, &>
+
+</TD>
+</TR>
+</TABLE>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+$Object => undef
+</%ARGS>
diff --git a/rt/html/Elements/EditLinks b/rt/html/Elements/EditLinks
new file mode 100755
index 0000000..7670ffa
--- /dev/null
+++ b/rt/html/Elements/EditLinks
@@ -0,0 +1,177 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table width="100%">
+ <tr>
+ <td valign="top" width="50%">
+ <h3><&|/l&>Current Links</&></h3>
+
+<table>
+ <tr>
+ <td></td>
+ <td><i><&|/l&>(Check box to delete)</&></i></td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Depends on</&>:</td>
+ <td class="value">
+% while (my $link = $Object->DependsOn->Next) {
+ <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <& ShowLink, URI => $link->TargetURI &><br />
+% }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Depended on by</&>:</td>
+ <td class="value">
+% while (my $link = $Object->DependedOnBy->Next) {
+ <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <& ShowLink, URI => $link->BaseURI &><br />
+% }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Parents</&>:</td>
+ <td class="value">
+% while (my $link = $Object->MemberOf->Next) {
+ <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <& ShowLink, URI => $link->TargetURI &><br />
+% }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Children</&>:</td>
+ <td class="value">
+% while (my $link = $Object->Members->Next) {
+ <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <& ShowLink, URI => $link->BaseURI &><br />
+% }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Refers to</&>:</td>
+ <td class="value">
+% while (my $link = $Object->RefersTo->Next) {
+ <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <& ShowLink, URI => $link->TargetURI &><br />
+%}
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Referred to by</&>:</td>
+ <td class="value">
+% while (my $link = $Object->ReferredToBy->Next) {
+% # Skip reminders
+% next if (UNIVERSAL::isa($link->BaseObj, 'RT::Ticket') && $link->BaseObj->Type eq 'reminder');
+ <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <& ShowLink, URI => $link->BaseURI &><br />
+% }
+ </td>
+ </tr>
+</table>
+
+</td>
+<td valign="top">
+<h3><&|/l&>New Links</&></h3>
+% if (ref($Object) eq 'RT::Ticket') {
+<i><&|/l&>Enter tickets or URIs to link tickets to. Separate multiple entries with spaces.</&>
+<& /Elements/Callback, _CallbackName => 'ExtraLinkInstructions' &>
+</i><br />
+% } elsif (ref($Object) eq 'RT::Queue') {
+<i><&|/l&>Enter queues or URIs to link queues to. Separate multiple entries with spaces.</&>
+</i><br />
+% } else {
+<i><&|/l&>Enter objects or URIs to link objects to. Separate multiple entries with spaces.</&></i><br />
+% }
+<table>
+% if ($Merge) {
+ <tr>
+ <td class="label"><&|/l&>Merge into</&>:</td>
+ <td class="entry"><input name="<%$id%>-MergeInto" /> <i><&|/l&>(only one ticket)</&></i></td>
+ </tr>
+% }
+ <tr>
+ <td class="label"><&|/l&>Depends on</&>:</td>
+ <td class="entry"><input name="<%$id%>-DependsOn" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Depended on by</&>:</td>
+ <td class="entry"><input name="DependsOn-<%$id%>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Parents</&>:</td>
+ <td class="entry"><input name="<%$id%>-MemberOf" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Children</&>:</td>
+ <td class="entry"> <input name="MemberOf-<%$id%>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Refers to</&>:</td>
+ <td class="entry"><input name="<%$id%>-RefersTo" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Referred to by</&>:</td>
+ <td class="entry"> <input name="RefersTo-<%$id%>" /></td>
+ </tr>
+</table>
+</td>
+</tr>
+</table>
+
+<%INIT>
+my $id;
+if ($Object && $Object->Id) {
+ $id = $Object->Id;
+} else {
+ $id = 'new';
+}
+</%INIT>
+
+<%ARGS>
+$Object => undef
+$Merge => 0
+</%ARGS>
diff --git a/rt/html/Elements/EmailInput b/rt/html/Elements/EmailInput
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/html/Elements/EmailInput
diff --git a/rt/html/Elements/Error b/rt/html/Elements/Error
new file mode 100644
index 0000000..3693274
--- /dev/null
+++ b/rt/html/Elements/Error
@@ -0,0 +1,86 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Callback, %ARGS, error => $error &>
+<& /Elements/Header, Code => $Code, Why => $Why, Title => $Title &>
+<& /Elements/Tabs, Title => $Title &>
+<div class="error">
+<%$Why%>
+<br />
+<%$Details%>
+</div>
+
+<%cleanup>
+$m->comp('/Elements/Footer');
+$m->abort();
+</%cleanup>
+
+<%args>
+$Code => undef
+$Details =>''
+$Title => loc("RT Error")
+$Why => loc("the calling component did not specify why")
+</%args>
+
+<%INIT>
+my $error = "WebRT: $Why ($Details)";
+
+# TODO: Log::Dispatch isn't UTF-8 safe. Autrijus needs to talk to dave rolsky about getting this fixed
+if ($] >= 5.007001) {
+ require Encode;
+ Encode::_utf8_off($error);
+}
+
+$RT::Logger->error($error);
+
+if ( defined ($session{'SessionType'}) && $session{'SessionType'} eq 'REST' ) {
+ $r->content_type('text/plain');
+ $m->out( "Error: " . $Why . "\n" );
+ $m->out( $Details . "\n" );
+ $m->abort();
+}
+</%INIT>
diff --git a/rt/html/Elements/Footer b/rt/html/Elements/Footer
new file mode 100644
index 0000000..0cb528f
--- /dev/null
+++ b/rt/html/Elements/Footer
@@ -0,0 +1,94 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# End of div#body from /Elements/PageLayout
+</div>
+</td>
+</tr>
+<tr>
+<td>
+<& /Elements/Callback, %ARGS &>
+<div id="footer">
+ <p id="time">
+ <span><&|/l&>Time to display</&>: <%Time::HiRes::tv_interval( $m->{'rt_base_time'} )%></span>
+ </p>
+
+<!--
+ <p id="bpscredits">
+ <span>
+<&|/l, '&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>', &>[_1] RT [_2] Copyright 1996-[_3] [_4].</&>
+</span>
+</p>
+% if (!$Menu) {
+ <p id="legal">
+<&|/l&>Distributed under version 2 <a href="http://www.gnu.org/copyleft/gpl.html"> of the GNU GPL.</a></&><br />
+<&|/l, '<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>' &>To inquire about support, training, custom development or licensing, please contact [_1].</&><br />
+ </p>
+% }
+
+</div>
+-->
+% if ($Debug >= 2 ) {
+% require Data::Dumper;
+% my $d = Data::Dumper->new([\%ARGS], [qw(%ARGS)]);
+<pre>
+<%$d->Dump() %>
+</pre>
+% }
+
+</TD>
+</TR>
+</TABLE>
+
+ </body>
+</html>
+% $m->abort();
+
+<%ARGS>
+$Debug => 0
+$Menu => 1
+</%ARGS>
diff --git a/rt/html/Elements/FreesideInvoiceSearch b/rt/html/Elements/FreesideInvoiceSearch
new file mode 100644
index 0000000..3842b2f
--- /dev/null
+++ b/rt/html/Elements/FreesideInvoiceSearch
@@ -0,0 +1,20 @@
+% if ( $FS::CurrentUser::CurrentUser->access_right('View invoices') ) {
+
+ <form action="<% $RT::URI::freeside::URL %>/search/cust_bill.html" STYLE="margin:0">
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_invoice (what) {
+ if ( what.value == '(inv #)' )
+ what.value = '';
+ }
+ </SCRIPT>
+ <input name="invnum" accesskey="0" VALUE="(inv #)" SIZE="4" onFocus="clearhint_search_invoice(this);" onClick="clearhint_search_invoice(this);" STYLE="text-align:right; margin-bottom:1px; font-family: Arial, Verdana, Helvetica, sans-serif;">
+
+% if ( $FS::CurrentUser::CurrentUser->access_right('List invoices') ) {
+ <A HREF="<% $RT::URI::freeside::URL %>search/report_cust_bill.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+% }
+ <BR>
+
+ <input type="submit" value="<&|/l&>Search invoices</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+ </form>
+
+% }
diff --git a/rt/html/Elements/FreesideNewCust b/rt/html/Elements/FreesideNewCust
new file mode 100644
index 0000000..f60e995
--- /dev/null
+++ b/rt/html/Elements/FreesideNewCust
@@ -0,0 +1,3 @@
+<form action="<% $RT::URI::freeside::URL %>/edit/cust_main.cgi" STYLE="margin:0">
+<INPUT TYPE="submit" VALUE="<&|/l&>New customer</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="vertical-align:bottom; font-size:100%">&nbsp;
+</FORM>
diff --git a/rt/html/Elements/FreesideSearch b/rt/html/Elements/FreesideSearch
new file mode 100644
index 0000000..8e609bb
--- /dev/null
+++ b/rt/html/Elements/FreesideSearch
@@ -0,0 +1,13 @@
+% if ( $FS::CurrentUser::CurrentUser->access_right('List customers') ) {
+<form action="<% $RT::URI::freeside::URL %>/search/cust_main.cgi" STYLE="margin:0">
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_cust (what) {
+ if ( what.value == '(cust #, name, company or phone)' )
+ what.value = '';
+ }
+ </SCRIPT>
+<input name="search_cust" accesskey="0" VALUE="(cust #, name, company or phone)" SIZE="28" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+<A HREF="<% $RT::URI::freeside::URL %>/search/report_cust_main.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" value="<&|/l&>Search customers</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+</form>
+% }
diff --git a/rt/html/Elements/FreesideSvcSearch b/rt/html/Elements/FreesideSvcSearch
new file mode 100644
index 0000000..4a59424
--- /dev/null
+++ b/rt/html/Elements/FreesideSvcSearch
@@ -0,0 +1,11 @@
+<form action="<% $RT::URI::freeside::URL %>/search/cust_svc.html" STYLE="margin:0">
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_svc (what) {
+ if ( what.value == '(user, user@domain or domain)' )
+ what.value = '';
+ }
+ </SCRIPT>
+<input name="search_svc" accesskey="0" VALUE="(user, user@domain or domain)" SIZE="26" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+ <A NOTYET="<% $RT::URI::freeside::URL %>search/svc_Smarter.html" STYLE="color: #000000; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" value="<&|/l&>Search services</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+</form>
diff --git a/rt/html/Elements/GotoTicket b/rt/html/Elements/GotoTicket
new file mode 100644
index 0000000..ad6ad1b
--- /dev/null
+++ b/rt/html/Elements/GotoTicket
@@ -0,0 +1,48 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<form action="<%$RT::WebPath%>/Ticket/Display.html"><input type="submit" class="button" value="<&|/l&>Goto ticket</&>" />&nbsp;<input size="5" name="id" accesskey="0" /></form>
diff --git a/rt/html/Elements/Header b/rt/html/Elements/Header
new file mode 100644
index 0000000..bf6fa46
--- /dev/null
+++ b/rt/html/Elements/Header
@@ -0,0 +1,172 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
+
+<title><%$Title%></title>
+
+% if ($Refresh && $Refresh > 0) {
+ <meta http-equiv="refresh" content="<%$Refresh%>" />
+% }
+
+<link rel="shortcut icon" href="<%$RT::WebImagesURL%>/favicon.png" type="image/png" />
+<link rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/css/<% $RT::WebDefaultStylesheet %>/main-squished.css" type="text/css" media="all" />
+<link rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/css/print.css" type="text/css" media="print" />
+
+% if ( $RSSAutoDiscovery ) {
+ <link rel="alternate" href="<%$RSSAutoDiscovery%>" type="application/rss+xml" title="RSS RT Search" />
+% }
+
+<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/util.js"></script>
+<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/ahah.js"></script>
+<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/titlebox-state.js"></script>
+<script type="text/javascript"><!--
+ onLoadHook("loadTitleBoxStates()");
+% if ( $Focus ) {
+ onLoadHook("focusElementById('<% $Focus %>')");
+% }
+% if ( $onload ) {
+ onLoadHook("<% $onload |n %>");
+% }
+--></script>
+
+<& /Elements/Callback, _CallbackName => 'Head', %ARGS &>
+
+</head>
+ <body NOTBACKGROUND="<% $RT::URI::freeside::URL %>/images/background-cheat.png"
+ STYLE="margin-top:0; margin-bottom:0; margin-left:0; margin-right:0"
+ <% $id && qq[ id="comp-$id"] |n %>
+ >
+
+% if ($ShowBar) {
+
+<table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#FFFFFF" STYLE="padding-left:0; padding-right:4">
+ <tr>
+ <td colspan=2 rowspan=2><img border=0 alt="freeside" src="<%$RT::WebImagesURL%>/small-logo.png" width="92" height="62"></td>
+ <td align="left" rowspan=2><font size=6><% &RT::URI::freeside::FreesideGetConfig('company_name') || 'ExampleCo' %></font></td>
+ <td align="right" valign="top">
+
+<div id="quickbar">
+ <div id="quick-personal">
+ <span class="hide"><a href="#skipnav"><&|/l&>Skip Menu</&></a> | </span>
+% if ($session{'CurrentUser'}->Name) {
+ <&|/l, "<span>".$session{'CurrentUser'}->Name."</span>" &>Logged in as [_1]</&>
+% if ($session{'CurrentUser'}->HasRight( Right => 'ModifySelf', Object => $RT::System )) {
+ | <a href="<%$RT::WebPath%><%$Prefs%>"><&|/l&>Preferences</&></a>
+% }
+% } else {
+ <&|/l&>Not logged in.</&>
+% }
+ <& /Elements/Callback, %ARGS &>
+% unless (!$session{'CurrentUser'}->Name
+% or ($RT::WebExternalAuth and !$RT::WebFallbackToInternalAuth)) {
+ | <a href="<%$RT::WebPath%>/NoAuth/Logout.html<%$URL ? "?URL=".$URL : ''%>"><&|/l&>Logout</&></a>
+% }
+ </div>
+% }
+
+ </td>
+
+ </tr>
+ <tr>
+
+ <td align=right valign=bottom>
+ <table>
+ <tr>
+ <td align=right>
+ <FONT SIZE="-3">
+ <A HREF="http://www.sisd.com/freeside">Freeside</A>&nbsp;v<% &RT::URI::freeside::FreesideVersion() %><BR>
+ <A HREF="<% FS::Conf->new->config('support-key') ? "http://www.sisd.com/mediawiki/index.php/Supported:Documentation" : "http://www.sisd.com/mediawiki/index.php/Freeside:1.9:Documentation" %>">Documentation</A><BR>
+ </FONT>
+ </td>
+ <td bgcolor=#000000></td>
+ <td align=left>
+ <FONT SIZE="-3">
+ <A HREF="http://www.bestpractical.com/rt">RT</A>&nbsp;v<% $RT::VERSION %><BR>
+ <A HREF="http://wiki.bestpractical.com/">Documentation</A><BR>
+ </FONT>
+ </td>
+
+ </tr>
+ </table>
+ </td>
+
+ </tr>
+</table>
+
+<%INIT>
+$r->headers_out->{'Pragma'} = 'no-cache';
+$r->headers_out->{'Cache-control'} = 'no-cache';
+
+require RT::URI::freeside;
+
+my $id = $m->request_comp->path;
+$id =~ s|^/||g;
+$id =~ s|/|-|g;
+$id =~ s|\.html$||g;
+$id =~ s|index$||g
+ if $id ne 'index';
+$id =~ s|-$||g;
+</%INIT>
+
+<%ARGS>
+$Prefs => '/User/Prefs.html'
+#$Focus => 'focus'
+$Focus => ''
+$Title => 'RT'
+$Code => undef
+$Refresh => 0
+$Why => undef
+$ShowBar => 1
+$URL => undef
+$RSSAutoDiscovery => undef
+$onload => undef
+</%ARGS>
diff --git a/rt/html/Elements/ListActions b/rt/html/Elements/ListActions
new file mode 100644
index 0000000..24f923a
--- /dev/null
+++ b/rt/html/Elements/ListActions
@@ -0,0 +1,65 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&| /Widgets/TitleBox, title => loc('Results') &>
+ <ul class="action-results">
+% foreach my $action (@actions) {
+% next unless $action;
+% my $skip = 0;
+% $m->comp('/Elements/Callback', _CallbackName => 'ModifyRow', row => \$action, skip => \$skip, %ARGS);
+% next if $skip;
+ <li><%$action%></li>
+% }
+ </ul>
+</&>
+<%init>
+@actions = grep $_, @actions;
+return unless @actions;
+</%init>
+<%ARGS>
+@actions => undef
+</%ARGS>
diff --git a/rt/html/Elements/Login b/rt/html/Elements/Login
new file mode 100644
index 0000000..cd39b87
--- /dev/null
+++ b/rt/html/Elements/Login
@@ -0,0 +1,138 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%INIT>
+if ($m->request_comp->path =~ '^/REST/\d+\.\d+/') {
+ $r->content_type("text/plain");
+ $m->error_format("text");
+ $m->out("RT/$RT::VERSION 401 Credentials required\n");
+ $m->out("\n$Error\n") if $Error;
+ $m->abort;
+}
+
+my $req_uri;
+
+if (UNIVERSAL::can($r, 'uri') and $r->uri =~ m{.*/(.*)}) {
+ $req_uri = $1;
+}
+
+my $form_action = defined $goto ? $goto
+ : defined $req_uri ? $req_uri
+ : $RT::WebPath
+ ;
+</%INIT>
+
+<& /Elements/Callback, %ARGS, _CallbackName => 'Header' &>
+<& /Elements/Header, Title => loc('Login'), Focus => 'user' &>
+
+%# End of div#quickbar from /Elements/Header
+</div>
+
+<div id="body" class="login-body">
+
+% if ($Error) {
+<&| "/Widgets/TitleBox", title => loc('Error'), hideable => 0 &>
+<% $Error %>
+</&>
+% }
+
+<& /Elements/Callback, %ARGS, _CallbackName => 'BeforeForm' &>
+
+<div id="login-box">
+<&| /Widgets/TitleBox, title => loc('Login'), titleright => $RT::VERSION, hideable => 0 &>
+
+% unless ($RT::WebExternalAuth and !$RT::WebFallbackToInternalAuth) {
+<form id="login" name="login" method="post" action="<% $form_action %>">
+
+<div class="input-row">
+ <span class="label"><&|/l&>Username</&>:</span>
+ <span class="input"><input name="user" value="<%$user%>" id="user" /></span>
+</div>
+
+<div class="input-row">
+ <span class="label"><&|/l&>Password</&>:</span>
+ <span class="input"><input type="password" name="pass" /></span>
+</div>
+
+<div class="button-row">
+ <span class="input"><input type="submit" class="button" value="<&|/l&>Login</&>" /></span>
+</div>
+
+%# Give callbacks a chance to add more control elements
+<& /Elements/Callback, %ARGS &>
+
+% # From mason 1.0.1 forward, this doesn't work. in fact, it breaks things.
+% # But on Mason 1.15 it's fixed again, so we still use it.
+% # The code below iterates through everything in the passed in arguments
+% # Preserving all the old parameters
+% # This would be easier, except mason is 'smart' and calls multiple values
+% # arrays rather than multiple hash keys
+% my $key; my $val;
+% foreach $key (keys %ARGS) {
+% if (($key ne 'user') and ($key ne 'pass')) {
+% if (ref($ARGS{$key}) =~ /ARRAY/) {
+% foreach $val (@{$ARGS{$key}}) {
+<input type="hidden" class="hidden" name="<%$key %>" value="<% $val %>" />
+% }
+% }
+% else {
+<input type="hidden" class="hidden" name="<% $key %>" value="<% $ARGS{$key} %>" />
+% }
+% }
+% }
+</form>
+% }
+</&>
+</div><!-- #login-box -->
+<& /Elements/Callback, %ARGS, _CallbackName => 'AfterForm' &>
+<& /Elements/Footer, Menu => 0 &>
+<%ARGS>
+$user => ""
+$pass => undef
+$goto => undef
+$Error => undef
+</%ARGS>
diff --git a/rt/html/Elements/Logo b/rt/html/Elements/Logo
new file mode 100644
index 0000000..157f7be
--- /dev/null
+++ b/rt/html/Elements/Logo
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+ <div id="logo">
+ <a href="http://bestpractical.com"><img src="<%$RT::WebImagesURL%>/bplogo.gif" alt="<%loc("Best Practical Solutions, LLC corporate logo")%>" width="177" height="33" /></a>
+% if ($show_name) {
+ <div class="rtname"><% loc("RT for [_1]", $RT::rtname) %></div>
+% }
+ </div>
+<%args>
+ $show_name => 1
+</%args>
diff --git a/rt/html/Elements/Menu b/rt/html/Elements/Menu
new file mode 100644
index 0000000..b5b2bda
--- /dev/null
+++ b/rt/html/Elements/Menu
@@ -0,0 +1,134 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<ul<% !$level ? ' id="system-menu"' : ''|n %><% $menu_class ? qq[ class="$menu_class"] : ''|n %>>
+<div<% $menu_class ? qq[ class="$menu_class"] : ''|n %>><div class="wrapper">
+% my $sep = 0;
+% my $postsep = 0;
+% my $accesskey = 1;
+%
+% $count = 0;
+% $class = {};
+%
+% foreach $tab (sort keys %{$toptabs}) {
+% $count++;
+%
+% my $current = $current_toptab || "";
+% my $path = $toptabs->{$tab}->{'path'} || "";
+%
+% $path =~ s#/index.html$##gi;
+% $current =~ s#/index.html$##gi;
+%
+% $sep = $toptabs->{$tab}->{'separator'} ? 1 : 0;
+%
+% my @aclass;
+% push @aclass, 'selected'
+% if $path eq $current;
+%
+% push @aclass, 'odd'
+% if $level % 2;
+%
+% $class->{a} = join ' ', @aclass;
+%
+% my @li;
+% push @li, 'first'
+% if $count == 1;
+%
+% push @li, 'pre-separator'
+% if $sep;
+%
+% push @li, 'post-separator'
+% if $postsep;
+%
+% $class->{li} = join ' ', @li;
+%
+% my $url = ($toptabs->{$tab}->{'path'}||'') =~ /^https?:/i
+% ? $toptabs->{$tab}->{'path'} || ''
+% : $RT::WebPath . "/" . $toptabs->{$tab}->{'path'};
+%
+ <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>>
+ <% $count > 1 && !$postsep && qq[<span class="bullet">&#183; </span>]|n%>
+ <a href="<% $url %>"
+ <% $class->{a} && qq[ class="$class->{a}"] |n%>
+ <% !$level && " accesskey='".$accesskey++."'" |n%>>
+ <% $toptabs->{$tab}->{'title'} || ''%></a>
+%# Second-level items
+% if ($toptabs->{$tab}->{'subtabs'}
+% and keys %{$toptabs->{$tab}->{'subtabs'}})
+% {
+ <& /Elements/Menu, level => $level+1,
+ current_toptab => $toptabs->{$tab}->{'current_subtab'},
+ toptabs => $toptabs->{$tab}->{'subtabs'},
+ last_level => $toptabs->{$tab}->{last_system_menu_level} &>
+% }
+ </li>
+% if ($sep) {
+ <li class="separator">&#183;&#183;&#183;</li>
+% }
+%
+% $postsep = $sep;
+% }
+</div></div>
+</ul>
+
+<%INIT>
+my ($tab, $class, $count);
+
+my @ul;
+push @ul, 'last-menu-level'
+ if $last_level;
+push @ul, 'odd'
+ if $level % 2;
+my $menu_class = join ' ', @ul;
+</%INIT>
+
+<%ARGS>
+$current_toptab => ""
+$toptabs => undef
+$level => 0
+$last_level => 0
+</%ARGS>
diff --git a/rt/html/Elements/MessageBox b/rt/html/Elements/MessageBox
new file mode 100644
index 0000000..0149e1b
--- /dev/null
+++ b/rt/html/Elements/MessageBox
@@ -0,0 +1,74 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<textarea class="messagebox" cols="<%$Width%>" rows="<%$Height%>" wrap="<%$Wrap%>" name="<%$Name%>"><& /Elements/Callback, %ARGS &><% $Default %><%$message%><%$IncludeSignature ? $signature : ''%></textarea>
+<%INIT>
+
+my $message = '';
+
+if ($QuoteTransaction) {
+ my $transaction=RT::Transaction->new($session{'CurrentUser'});
+ $transaction->Load($QuoteTransaction);
+ $message=$transaction->Content(Quote => 1);
+}
+
+my $signature = '';
+if ($IncludeSignature && $session{'CurrentUser'}->UserObj->Signature) {
+ $signature = "-- \n".$session{'CurrentUser'}->UserObj->Signature;
+}
+
+</%INIT>
+<%ARGS>
+$QuoteTransaction => undef
+$Name => 'Content'
+$Default => ''
+$Width => $RT::MessageBoxWidth || 72
+$Height => $RT::MessageBoxHeight || 15
+$Wrap => $RT::MessageBoxWrap || 'HARD'
+$IncludeSignature => 1
+</%ARGS>
+
diff --git a/rt/html/Elements/MyAdminQueues b/rt/html/Elements/MyAdminQueues
new file mode 100644
index 0000000..7e720ad
--- /dev/null
+++ b/rt/html/Elements/MyAdminQueues
@@ -0,0 +1,54 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&|/Widgets/TitleBox, title => loc("Queues I administer"), bodyclass => "" &>
+<& /Elements/QueueSummary,
+ cache => 'my_admin_queues',
+ queue_filter => sub { $_->CurrentUserHasRight('AdminQueue') },
+ conditions => [ {cond => "Status = 'new'", name => loc ('new') },
+ {cond => "Status = 'open'", name => loc ('open') }] &>
+</&>
diff --git a/rt/html/Elements/MyRT b/rt/html/Elements/MyRT
new file mode 100644
index 0000000..3fde555
--- /dev/null
+++ b/rt/html/Elements/MyRT
@@ -0,0 +1,100 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table border="0" width="100%">
+<tr valign="top">
+
+<td class="boxcontainer" <% $summary? 'width="70%"': '' |n %>>
+% $show_cb->($_) foreach @$body;
+</td>
+
+% if ( $summary ) {
+<td class="boxcontainer">
+% $show_cb->($_) foreach @$summary;
+</td>
+% }
+
+</tr>
+</table>
+
+<%INIT>
+
+my $user = $session{'CurrentUser'}->UserObj;
+unless (exists $session{'my_rt_portlets'}) {
+ my ($default_portlets) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
+ $session{'my_rt_portlets'} = $user->Preferences(
+ HomepageSettings => $default_portlets? $default_portlets->Content: {},
+ );
+}
+
+my ($body, $summary) = @{$session{'my_rt_portlets'}}{qw(body summary)};
+unless( $body && @$body ) {
+ $body = $summary || [];
+ $summary = undef;
+}
+$summary = undef unless $summary && @$summary;
+
+my $Rows = $user->Preferences( 'SummaryRows', ( $RT::DefaultSummaryRows || 10 ) );
+
+my $show_cb = sub {
+ my $entry = shift;
+ my $type = $entry->{type};
+ if ( $type eq 'component' ) {
+ my $name = $entry->{name};
+
+ # security check etc.
+ $m->comp( $name, %{ $entry->{arguments} || {} } );
+ } elsif ( $type eq 'system' ) {
+ $m->comp( '/Elements/ShowSearch', Name => $entry->{name}, Override => { Rows => $Rows } );
+ } elsif ( $type eq 'saved' ) {
+ $m->comp( '/Elements/ShowSearch', SavedSearch => $entry->{name}, Override => { Rows => $Rows } );
+ } else {
+ $RT::Logger->error("unknown portlet type $type");
+ }
+};
+
+</%INIT>
diff --git a/rt/html/Elements/MyReminders b/rt/html/Elements/MyReminders
new file mode 100755
index 0000000..1e962bd
--- /dev/null
+++ b/rt/html/Elements/MyReminders
@@ -0,0 +1,73 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# DEPRECATED
+<&|/Widgets/TitleBox,
+ title => loc("Reminders") &>
+<table width="100%">
+% my $i =0;
+% while (my $reminder = $reminders->Next) {
+% $i++;
+% if ($reminder->RefersTo->First) {
+% my $ticket= $reminder->RefersTo->First->TargetObj;
+<tr class="<%$i%2 ? 'evenline' : 'oddline'%>"><td><a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$ticket->id%>"><%$reminder->Subject%></a><br />
+<blockquote>
+#<%$ticket->id%>: <%$ticket->Subject%><br />
+<%$reminder->OwnerObj->Name %> <%$reminder->DueObj->Unix >0 ? '&bull; '.$reminder->DueObj->AgeAsString : '' |n %>
+</blockquote>
+</td>
+</tr>
+% }}
+</table>
+</&>
+
+<%init>
+my $reminders = RT::Tickets->new($session{'CurrentUser'});
+$reminders->FromSQL('(Owner = "Nobody" OR Owner = "'.$session{'CurrentUser'}->Name.'")' .
+ ' AND Type = "reminder" AND (Status = "new" OR Status = "open") AND Due > "1970-01-01"');
+$reminders->OrderBy(FIELD => 'Due', ORDER => 'DESC');
+</%init>
diff --git a/rt/html/Elements/MyRequests b/rt/html/Elements/MyRequests
new file mode 100644
index 0000000..fc1e270
--- /dev/null
+++ b/rt/html/Elements/MyRequests
@@ -0,0 +1,49 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# DEPRECATED
+<& /Elements/ShowSearch, Name => 'My Requests' &>
diff --git a/rt/html/Elements/MySupportQueues b/rt/html/Elements/MySupportQueues
new file mode 100644
index 0000000..f9e18a0
--- /dev/null
+++ b/rt/html/Elements/MySupportQueues
@@ -0,0 +1,54 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&|/Widgets/TitleBox, title => loc("Queues I'm an AdminCc for"), bodyclass => "" &>
+<& /Elements/QueueSummary,
+ cache => 'my_support_queues',
+ queue_filter => sub { $_->IsAdminCc($session{'CurrentUser'}->Id) },
+ conditions => [ {cond => "Status = 'new'", name => loc ('new') },
+ {cond => "Status = 'open'", name => loc ('open') }] &>
+</&>
diff --git a/rt/html/Elements/MyTickets b/rt/html/Elements/MyTickets
new file mode 100644
index 0000000..8d657db
--- /dev/null
+++ b/rt/html/Elements/MyTickets
@@ -0,0 +1,49 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# DEPRECATED
+<& /Elements/ShowSearch, Name => 'My Tickets' &>
diff --git a/rt/html/Elements/PageLayout b/rt/html/Elements/PageLayout
new file mode 100644
index 0000000..b9fd31f
--- /dev/null
+++ b/rt/html/Elements/PageLayout
@@ -0,0 +1,256 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+
+<table class="black" border=0 cellspacing=0 cellpadding=0 width="100%">
+<tr>
+ <TD colspan=5 WIDTH="100%" STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<% $RT::URI::freeside::URL %>/images/black-gradient.png" HEIGHT="13" WIDTH="100%"></TD>
+</tr>
+<tr>
+
+ <div id="topactions">
+% my $notfirst = 0; foreach my $action (sort keys %{$topactions}) {
+ <span class="topaction">
+ <td class="blackright" ALIGN="right" VALIGN="center">
+% $m->out($topactions->{"$action"}->{'html'});
+ </td>
+ </span>
+% }
+ </div>
+
+</tr>
+</table>
+
+%# End of div#quickbar from /Elements/Header
+</div>
+
+<table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%">
+ <TR>
+ <TD STYLE="padding:0" WIDTH="100%"><IMG BORDER=0 ALT="" SRC="<% $RT::URI::freeside::URL %>/images/black-gray-top.png" HEIGHT="13" WIDTH="100%"></TD>
+ </TR>
+ <TR HEIGHT="100%">
+ <TD>
+
+% if ( $show_menu ) {
+<div id="nav">
+<& /Elements/Menu, toptabs => $toptabs, current_toptab => $current_toptab &>
+</div>
+% }
+
+<div id="header">
+ <h1><%$title%></h1>
+
+% my $sep = 0;
+% my $postsep = 0;
+% my $count = 0;
+% my $class = { };
+%
+ <ul id="page-menu"<% (($actions && %$actions) || ($subactions && %$subactions)) && q[ class="actions-present"] | n %>>
+ <div><div><div>
+% if ($page_tabs) {
+% foreach my $tab (sort keys %{$page_tabs}) {
+% next if $tab =~ /^(?:current_toptab|this)$/;
+% $count++;
+%
+% my $current = $page_tabs->{current_toptab} || "";
+% my $path = $page_tabs->{$tab}->{'path'} || "";
+%
+% $path =~ s#/index.html$##gi;
+% $current =~ s#/index.html$##gi;
+%
+% $sep = $toptabs->{$tab}->{'separator'} ? 1 : 0;
+%
+% $class->{a} = $path eq $current ? ' class="selected"' : undef;
+%
+% my @li;
+% push @li, 'first'
+% if $count == 1;
+%
+% push @li, 'pre-separator'
+% if $sep;
+%
+% push @li, 'post-separator'
+% if $postsep;
+%
+% $class->{li} = join ' ', @li;
+%
+%
+ <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>><% $count > 1 && !$postsep && "&#183; "|n%><a href="<%$RT::WebPath%>/<%$page_tabs->{$tab}->{'path'}%>"<%$class->{a}|n%><% $class->{a} ? ' name="focus"' : ''|n %>><% $page_tabs->{$tab}->{'title'} %></a></li>
+%
+% if ($sep) {
+ <li class="separator">&#183;&#183;&#183;</li>
+% }
+% $postsep = $sep;
+% }
+% } else {
+&nbsp;
+% }
+ </div></div></div>
+ </ul>
+
+% if (($actions && %$actions) || ($subactions && %$subactions)) {
+ <ul id="actions-menu">
+ <div><div><div>
+% $sep = 0;
+% $postsep = 0;
+% $count = 0;
+% $class = { };
+%
+% for my $type ($actions, $subactions) {
+%
+% if ($type && %$type) {
+% foreach my $action (sort keys %{$type}) {
+% $count++;
+%
+% $sep = $type->{$action}->{'separator'} ? 1 : 0;
+%
+% my @li;
+% push @li, 'first'
+% if $count == 1;
+%
+% push @li, 'pre-separator'
+% if $sep;
+%
+% push @li, 'post-separator'
+% if $postsep;
+%
+% $class->{li} = join ' ', @li;
+%
+ <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>><% $count > 1 && !$postsep && qq[<span class="bullet">&#183; </span>]|n%>
+% if ($type->{"$action"}->{'html'}) {
+ <% $type->{"$action"}->{'html'} | n %>
+% } else {
+ <a href="<%$RT::WebPath%>/<%$type->{$action}->{'path'}%>"<% $type->{$action}->{class} && ' class="'.$type->{$action}->{class}.'"' |n %><% $type->{$action}->{id} && ' id="'.$type->{$action}->{id}.'"' |n %>><%$type->{$action}->{'title'}%></a>
+% }
+ </li>
+% if ($sep) {
+ <li class="separator">&#183;&#183;&#183;</li>
+% }
+% $postsep = $sep;
+% }
+% }
+% }
+ </div></div></div>
+ </ul>
+% }
+</div>
+
+<div id="body">
+<& /Elements/Callback, _CallbackName => 'BeforeBody', %ARGS &>
+%$m->flush_buffer(); # we've got the page laid out, let's flush the buffer;
+
+<%INIT>
+ foreach my $tab (sort keys %{$toptabs}) {
+ if ($current_toptab && $toptabs->{$tab}->{'path'} eq $current_toptab) {
+ $toptabs->{$tab}->{"subtabs"} = $tabs;
+ $toptabs->{$tab}->{"current_subtab"} = $current_tab;
+ }
+ }
+
+if (! defined($AppName)) {
+ $AppName = loc("RT for [_1]", $RT::rtname);
+}
+
+my ($menu_depth, $almost_last, $page_tabs);
+
+if ($RT::WebDefaultStylesheet ne '3.4-compat') {
+ ($menu_depth, $almost_last) = @{$m->comp('.menu_recurse', data => $toptabs)};
+
+ if (defined $almost_last->{subtabs} and %{$almost_last->{subtabs}}) {
+ $page_tabs = {
+ current_toptab => $almost_last->{current_subtab},
+ %{$almost_last->{subtabs}},
+ };
+
+ delete $almost_last->{subtabs};
+ delete $almost_last->{current_subtab};
+ }
+}
+</%INIT>
+
+%# There's probably a better way to do this that involves three times as
+%# much work and redoing the whole menu/tab system... which would seem a
+%# bit out of scope.
+%#
+%# This function recurses through the menu and returns the second to
+%# last menu, that is, the menu holding the last reference to
+%# and submenu. It also returns the number of menu levels minus
+%# the last submenu.
+<%def .menu_recurse>
+ <%args>
+ $data => { }
+ $pdata => { }
+ $ppdata => { }
+ $level => 0
+ </%args>
+ <%init>
+ for my $key (keys %$data) {
+ return $m->comp('.menu_recurse', data => $data->{$key}->{subtabs},
+ pdata => $data->{$key},
+ ppdata => $pdata,
+ level => $level+1)
+ if ref($data->{$key}) eq 'HASH'
+ and defined $data->{$key}->{subtabs}
+ and %{$data->{$key}->{subtabs}};
+ }
+ $ppdata->{last_system_menu_level}++;
+ return [$level, $pdata];
+ </%init>
+</%def>
+
+<%ARGS>
+$current_toptab => undef
+$current_tab => undef
+$toptabs => undef
+$topactions => undef
+$tabs => undef
+$actions => undef
+$subactions => undef
+$title => $m->callers(-1)->path
+$AppName => undef
+$show_menu => 1
+</%ARGS>
diff --git a/rt/html/Elements/QueryString b/rt/html/Elements/QueryString
new file mode 100644
index 0000000..0176180
--- /dev/null
+++ b/rt/html/Elements/QueryString
@@ -0,0 +1,63 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+my @params;
+while ( my ($key, $value) = each %ARGS ){
+ if( UNIVERSAL::isa( $value, 'ARRAY' ) ) {
+ push @params, map $key."=".$m->interp->apply_escapes($_,'u'), @$value;
+ } else {
+ if (ref $value eq "ARRAY") {
+ push @params, $key."=".$m->interp->apply_escapes($_, 'u')
+ for @{$value};
+ } else {
+ push @params, $key."=".$m->interp->apply_escapes($value||"",'u');
+ }
+ }
+}
+return(join('&',@params));
+</%init>
diff --git a/rt/html/Elements/QueueSummary b/rt/html/Elements/QueueSummary
new file mode 100644
index 0000000..133539c
--- /dev/null
+++ b/rt/html/Elements/QueueSummary
@@ -0,0 +1,92 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table border="0" cellspacing="0" cellpadding="1" width="100%">
+<tr>
+ <th class="collection-as-table"><&|/l&>Queue</&></th>
+% for my $condition (@$conditions) {
+ <th class="collection-as-table"><% $condition->{name} %></th>
+% }
+</tr>
+% my $i;
+% for my $queue (@queues) {
+% $i++;
+% my $queue_cond = "Queue = '$queue->{Name}' AND ";
+% my $all_q = $queue_cond . "(Status = 'open' OR Status = 'new' OR Status = 'stalled')";
+<tr class="<% $i%2 ? 'oddline' : 'evenline'%>" >
+<td><a href="<% $RT::WebPath%>/Search/Results.html?Query=<% $all_q |u,n %>" title="<% $queue->{Description} %>"><% $queue->{Name} %></a></td>
+% for my $condition (@$conditions) {
+% $Tickets->FromSQL( "Queue = $queue->{id} AND ". $condition->{cond} );
+<td align="right"><a href="<% $RT::WebPath%>/Search/Results.html?Query=<% $queue_cond.$condition->{cond} |u,n %>"><% $Tickets->Count %></a></td>
+% }
+</tr>
+% }
+</table>
+<%INIT>
+my @queues;
+
+if ($cache && exists $session{$cache}) {
+ @queues = @{$session{$cache}};
+}
+else {
+ my $Queues = RT::Queues->new($session{'CurrentUser'});
+ $Queues->UnLimit();
+ @queues = map {
+ { Name => $_->Name, Description => $_->Description,
+ id => $_->Id } }
+ grep $queue_filter->($_), @{$Queues->ItemsArrayRef};
+
+ $session{$cache} = \@queues if $cache;
+}
+
+my $Tickets = RT::Tickets->new($session{'CurrentUser'});
+</%INIT>
+<%ARGS>
+$cache => undef
+$queue_filter => undef
+$conditions => ()
+</%ARGS>
diff --git a/rt/html/Elements/QuickCreate b/rt/html/Elements/QuickCreate
new file mode 100644
index 0000000..75b3a45
--- /dev/null
+++ b/rt/html/Elements/QuickCreate
@@ -0,0 +1,71 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<div class="quick-create">
+<&| /Widgets/TitleBox, title => loc('Quick ticket creation') &>
+<form method="post" action="<%$RT::WebPath%>/<% $RT::QuickCreateLong ? 'Ticket/Create.html' : 'index.html' %>">
+<input type="hidden" class="hidden" name="QuickCreate" value="1" />
+<table>
+<tr><td>
+<&|/l&>Subject</&>:<br /><input size="30" name="Subject" />
+</td><td>
+<&|/l&>Queue</&>:<br /><& /Elements/SelectNewTicketQueue, Name => 'Queue', ShowNullOption => 0 &>
+</td><td>
+<&|/l&>Owner</&>:<br />
+<select type="select" name="Owner">
+<option value="<%$session{'CurrentUser'}->id%>" selected><%$session{'CurrentUser'}->Name %></option>
+<option value="<%$RT::Nobody->id%>"><%loc('Nobody')%></option>
+</select>
+</td>
+</tr>
+%#<tr><td colspan="3"><textarea cols="50" rows="3"></textarea></td></tr>
+</table>
+<div align="right"><input type="submit" class="button" value="<%loc('Create')%>" /></div>
+</form>
+</&>
+</div>
+
diff --git a/rt/html/Elements/Quicksearch b/rt/html/Elements/Quicksearch
new file mode 100644
index 0000000..4acbc20
--- /dev/null
+++ b/rt/html/Elements/Quicksearch
@@ -0,0 +1,61 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<div class="ticket-overview">
+<&|/Widgets/TitleBox, title => loc("Quick search"), bodyclass => "",
+ titleright => loc("Edit"), titleright_href => $RT::WebPath.'/Prefs/Quicksearch.html' &>
+<& /Elements/QueueSummary,
+ cache => 'quick_search_queues',
+ queue_filter => sub { $_->CurrentUserHasRight('ShowTicket') && !exists $unwanted->{$_->Name} },
+ conditions => [ {cond => "Status = 'new'", name => loc ('new') },
+ {cond => "Status = 'open'", name => loc ('open') },
+ {cond => "Status = 'stalled'", name => loc ('stalled') }] &>
+</&>
+</div>
+<%INIT>
+my $unwanted = $session{'CurrentUser'}->UserObj->Preferences('QuickSearch', {});
+</%INIT>
diff --git a/rt/html/Elements/RT__Ticket/ColumnMap b/rt/html/Elements/RT__Ticket/ColumnMap
new file mode 100644
index 0000000..21dc2a7
--- /dev/null
+++ b/rt/html/Elements/RT__Ticket/ColumnMap
@@ -0,0 +1,314 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%ARGS>
+$Name => undef
+$Attr => undef
+</%ARGS>
+
+
+<%ONCE>
+our ( $COLUMN_MAP );
+
+my $ColumnMap = sub {
+ my $name = shift;
+ my $attr = shift;
+
+ # First deal with the simple things from the map
+ if ( $COLUMN_MAP->{$name} ) {
+ return ( $COLUMN_MAP->{$name}->{$attr} );
+ }
+
+ # now, let's deal with harder things, like Custom Fields
+
+ elsif ( $name =~ /^(?:CF|CustomField)\.\{(.+)\}$/ ) {
+ my $field = $1;
+
+ if ( $attr eq 'attribute' ) {
+ return (undef);
+ }
+ elsif ( $attr eq 'title' ) {
+ return ( $field );
+ }
+ elsif ( $attr eq 'value' ) {
+ # Display custom field contents, separated by newlines.
+ # For Image custom fields we also show a thumbnail here.
+ return sub {
+ my $values = $_[0]->CustomFieldValues($field);
+ my @values = map {
+ (
+ ($_->CustomFieldObj->Type eq 'Image')
+ ? \($m->scomp( '/Elements/ShowCustomFieldImage', Object => $_ ))
+ : $_->Content
+ ),
+ \'<br />',
+ } @{ $values->ItemsArrayRef };
+ pop @values; # Remove that last <br />
+ return @values;
+ };
+ }
+ }
+};
+
+my $LinkCallback = sub {
+ my $method = shift;
+
+ my $mode = $RT::Ticket::LINKTYPEMAP{$method}{Mode};
+ my $type = $RT::Ticket::LINKTYPEMAP{$method}{Type};
+ my $other_mode = ($mode eq "Target" ? "Base" : "Target");
+ my $mode_uri = $mode.'URI';
+ my $local_type = 'Local'.$mode;
+
+ return sub {
+ map {
+ \'<a href="',
+ $_->$mode_uri->Resolver->HREF,
+ \'">',
+ ( $_->$mode_uri->IsLocal ? $_->$local_type : $_->$mode ),
+ \'</a><br />',
+ } @{ $_[0]->Links($other_mode,$type)->ItemsArrayRef }
+ }
+};
+
+$COLUMN_MAP = {
+ QueueName => {
+ attribute => 'Queue',
+ title => 'Queue',
+ value => sub { return $_[0]->QueueObj->Name }
+ },
+ OwnerName => {
+ title => 'Owner',
+ attribute => 'Owner',
+ value => sub { return $_[0]->OwnerObj->Name }
+ },
+ id => {
+ attribute => 'id',
+ align => 'right',
+ value => sub { return $_[0]->id }
+ },
+ Status => {
+ attribute => 'Status',
+ value => sub { return loc($_[0]->Status) }
+ },
+ Subject => {
+ attribute => 'Subject',
+ value => sub { return $_[0]->Subject || "(" . loc('No subject') . ")" }
+ },
+ ExtendedStatus => {
+ title => 'Status',
+ attribute => 'Status',
+ value => sub {
+ my $Ticket = shift;
+
+ if ( $Ticket->HasUnresolvedDependencies ) {
+ if ( $Ticket->HasUnresolvedDependencies( Type => 'approval' )
+ or $Ticket->HasUnresolvedDependencies( Type => 'code' ) )
+ {
+ return \'<em>', loc('(pending approval)'), \'</em>';
+ }
+ else {
+ return \'<em>', loc('(pending other Collection)'), \'</em>';
+ }
+ }
+ else {
+ return loc( $Ticket->Status );
+ }
+
+ }
+ },
+ Priority => {
+ attribute => 'Priority',
+ value => sub { return $_[0]->Priority }
+ },
+ InitialPriority => {
+ attribute => 'InitialPriority',
+ name => 'Initial Priority',
+ value => sub { return $_[0]->InitialPriority }
+ },
+ FinalPriority => {
+ attribute => 'FinalPriority',
+ name => 'Final Priority',
+ value => sub { return $_[0]->FinalPriority }
+ },
+ EffectiveId => {
+ attribute => 'EffectiveId',
+ value => sub { return $_[0]->EffectiveId }
+ },
+ Type => {
+ attribute => 'Type',
+ value => sub { return $_[0]->Type }
+ },
+ TimeWorked => {
+ attribute => 'TimeWorked',
+ title => 'Time Worked',
+ value => sub { return $_[0]->TimeWorked }
+ },
+ TimeLeft => {
+ attribute => 'TimeLeft',
+ title => 'Time Left',
+ value => sub { return $_[0]->TimeLeft }
+ },
+ TimeEstimated => {
+ attribute => 'TimeEstimated',
+ title => 'Time Estimated',
+ value => sub { return $_[0]->TimeEstimated }
+ },
+ Requestors => {
+ attribute => 'Requestor.EmailAddress',
+ value => sub { return $_[0]->Requestors->MemberEmailAddressesAsString }
+ },
+ Cc => {
+ attribute => 'Cc.EmailAddress',
+ value => sub { return $_[0]->Cc->MemberEmailAddressesAsString }
+ },
+ AdminCc => {
+ attribute => 'AdminCc.EmailAddress',
+ value => sub { return $_[0]->AdminCc->MemberEmailAddressesAsString }
+ },
+ StartsRelative => {
+ title => 'Starts',
+ attribute => 'Starts',
+ value => sub { return $_[0]->StartsObj->AgeAsString }
+ },
+ StartedRelative => {
+ title => 'Started',
+ attribute => 'Started',
+ value => sub { return $_[0]->StartedObj->AgeAsString }
+ },
+ CreatedRelative => {
+ title => 'Created',
+ attribute => 'Created',
+ value => sub { return $_[0]->CreatedObj->AgeAsString }
+ },
+ LastUpdatedRelative => {
+ title => 'Last Updated',
+ attribute => 'LastUpdated',
+ value => sub { return $_[0]->LastUpdatedObj->AgeAsString }
+ },
+ ToldRelative => {
+ title => 'Told',
+ attribute => 'Told',
+ value => sub { return $_[0]->ToldObj->AgeAsString }
+ },
+ DueRelative => {
+ title => 'Due',
+ attribute => 'Due',
+ value => sub {
+ my $date = $_[0]->DueObj;
+ if ($date && $date->Unix > 0 && $date->Unix < time()) {
+ return (\'<span class="overdue">' , $date->AgeAsString , \'</span>');
+ } else {
+ return $date->AgeAsString;
+ }
+ }
+ },
+ ResolvedRelative => {
+ title => 'Resolved',
+ attribute => 'Resolved',
+ value => sub { return $_[0]->ResolvedObj->AgeAsString }
+ },
+ Starts => {
+ attribute => 'Starts',
+ value => sub { return $_[0]->StartsObj->AsString }
+ },
+ Started => {
+ attribute => 'Started',
+ value => sub { return $_[0]->StartedObj->AsString }
+ },
+ Created => {
+ attribute => 'Created',
+ value => sub { return $_[0]->CreatedObj->AsString }
+ },
+ CreatedBy => {
+ attribute => 'CreatedBy',
+ title => 'Created By',
+ value => sub { return $_[0]->CreatorObj->Name }
+ },
+ LastUpdated => {
+ attribute => 'LastUpdated',
+ title => 'Last Updated',
+ value => sub { return $_[0]->LastUpdatedObj->AsString }
+ },
+ LastUpdatedBy => {
+ attribute => 'LastUpdatedBy',
+ title => 'Last Updated By',
+ value => sub { return $_[0]->LastUpdatedByObj->Name }
+ },
+ Told => {
+ attribute => 'Told',
+ value => sub { return $_[0]->ToldObj->AsString }
+ },
+ Due => {
+ attribute => 'Due',
+ value => sub { return $_[0]->DueObj->AsString }
+ },
+ Resolved => {
+ attribute => 'Resolved',
+ value => sub { return $_[0]->ResolvedObj->AsString }
+ },
+
+ # Everything from LINKTYPEMAP
+ (map {
+ $_ => { value => $LinkCallback->( $_ ) }
+ } keys %RT::Ticket::LINKTYPEMAP),
+
+ '_CLASS' => {
+ value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
+ },
+ '_CHECKBOX' => {
+ attribute => 'checkbox',
+ title => loc('Update'),
+ align => 'right',
+ value => sub { return \('<input type="checkbox" class="checkbox" name="UpdateTicket'.$_[0]->id.'" value="1" checked />') }
+ },
+
+};
+</%ONCE>
+<%init>
+$m->comp( '/Elements/Callback', COLUMN_MAP => $COLUMN_MAP, _CallbackName => 'ColumnMap');
+return $ColumnMap->( $Name, $Attr );
+</%init>
diff --git a/rt/html/Elements/Refresh b/rt/html/Elements/Refresh
new file mode 100644
index 0000000..6edec09
--- /dev/null
+++ b/rt/html/Elements/Refresh
@@ -0,0 +1,69 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+<option value="-1"
+%unless ($Default) {
+ selected
+%}
+><&|/l&>Don't refresh this page.</&></option>
+%foreach my $value (@refreshevery) {
+<option value="<%$value%>"
+% if ( $Default && ($value == $Default)) {
+selected
+% }
+><&|/l, $value/60 &>Refresh this page every [_1] minutes.</&></option>
+%}
+</select>
+
+<%INIT>
+my @refreshevery = qw(120 300 600 1200 3600 7200);
+</%INIT>
+<%ARGS>
+$Name => undef
+$Default => 0
+</%ARGS>
diff --git a/rt/html/Elements/RefreshHomepage b/rt/html/Elements/RefreshHomepage
new file mode 100644
index 0000000..167a48c
--- /dev/null
+++ b/rt/html/Elements/RefreshHomepage
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<form method="get" action="<%$RT::WebPath%>/index.html">
+<& /Elements/Refresh, Name => 'HomeRefreshInterval', Default => $session {'home_refresh_interval'} &>
+<div align="right"><input type="submit" class="button" value="<&|/l&>Go!</&>" /></div>
+</form>
diff --git a/rt/html/Elements/ScrubHTML b/rt/html/Elements/ScrubHTML
new file mode 100644
index 0000000..f382cc2
--- /dev/null
+++ b/rt/html/Elements/ScrubHTML
@@ -0,0 +1,73 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+my $scrubber = HTML::Scrubber->new();
+
+$scrubber->default(
+ 0,
+ {
+ '*' => 0,
+ id => 1,
+ class => 1,
+ # Match http, ftp and relative urls
+ href => qr{^(?:http:|ftp:|https:|/|__Web(?:Path|BaseURL|URL)__)}i,
+ face => 1,
+ size => 1,
+ target => 1
+ }
+);
+
+$scrubber->deny(qw[*]);
+$scrubber->allow(
+ qw[A B U P BR I HR BR SMALL EM FONT SPAN DIV UL OL LI DL DT DD PRE]);
+$scrubber->comment(0);
+return ( $scrubber->scrub($Content) );
+</%init>
+<%args>
+$Content => undef
+</%args>
diff --git a/rt/html/Elements/Section b/rt/html/Elements/Section
new file mode 100644
index 0000000..befe247
--- /dev/null
+++ b/rt/html/Elements/Section
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<h1><%$title%></h1>
+<%ARGS>
+$title => undef
+</%ARGS>
diff --git a/rt/html/Elements/SelectAttachmentField b/rt/html/Elements/SelectAttachmentField
new file mode 100644
index 0000000..8a37ca5
--- /dev/null
+++ b/rt/html/Elements/SelectAttachmentField
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+<option value="Subject"><&|/l&>Subject</&></option>
+<option value="Content"><&|/l&>Content</&></option>
+<option value="ContentType"><&|/l&>Content-Type</&></option>
+<option value="Filename"><&|/l&>Filename</&></option>
+</select>
+<%ARGS>
+$Name => 'AttachmentField'
+</%ARGS>
diff --git a/rt/html/Elements/SelectBoolean b/rt/html/Elements/SelectBoolean
new file mode 100644
index 0000000..b34e07a
--- /dev/null
+++ b/rt/html/Elements/SelectBoolean
@@ -0,0 +1,71 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+<option value="<%$TrueVal%>" <%$TrueDefault%>><%$True%></option>
+<option value="<%$FalseVal%>" <%$FalseDefault%>><%$False%></option>
+</select>
+
+<%ARGS>
+$Name => undef
+$True => loc("is")
+$Default => 'true'
+$TrueVal => 1
+$FalseVal => 0
+$False => loc("isn't")
+</%ARGS>
+
+<%INIT>
+my $TrueDefault = '';
+my $FalseDefault ='';
+if ($Default && $Default !~ /true/i) {
+ $FalseDefault = "SELECTED";
+}
+else {
+ $TrueDefault = "SELECTED";
+}
+</%INIT>
diff --git a/rt/html/Elements/SelectCustomFieldOperator b/rt/html/Elements/SelectCustomFieldOperator
new file mode 100644
index 0000000..ecb306c
--- /dev/null
+++ b/rt/html/Elements/SelectCustomFieldOperator
@@ -0,0 +1,64 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+% while (my $option = shift @Options) {
+% my $value = shift @Values;
+<option value="<%$value%>"
+% if ($Default eq $value) {
+selected
+% }
+><%$option%></option>
+% }
+</select>
+
+<%ARGS>
+$Name => undef
+@Options => ( loc('contains'), loc("doesn't contain"), loc('is'), loc("isn't"), loc('less than'), loc('greater than'))
+@Values => ('LIKE', 'NOT LIKE', '=', '!=', '<', '>')
+$Default => ''
+</%ARGS>
diff --git a/rt/html/Elements/SelectCustomFieldValue b/rt/html/Elements/SelectCustomFieldValue
new file mode 100644
index 0000000..2bcb35e
--- /dev/null
+++ b/rt/html/Elements/SelectCustomFieldValue
@@ -0,0 +1,65 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Callback, %ARGS &>
+% if ($CustomField->Type =~ /Select/i) {
+% my $values = $CustomField->Values;
+<select name="<%$Name%>">
+<option value="" selected>-</option>
+<option value="NULL"><&|/l&>(no value)</&></option>
+% while (my $value = $values->Next) {
+<option value="<%$value->Name%>"><%$value->Name%></option>
+% }
+</select>
+% }
+% else {
+<input name="<%$Name%>" size="20" />
+% }
+<%args>
+$Name => undef
+$CustomField =>undef
+</%args>
diff --git a/rt/html/Elements/SelectDate b/rt/html/Elements/SelectDate
new file mode 100644
index 0000000..b43f324
--- /dev/null
+++ b/rt/html/Elements/SelectDate
@@ -0,0 +1,75 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<script type="text/javascript"><!--
+ onLoadHook('createCalendarLink("<% $Name %>");');
+--></script>
+<input type="text" id="<% $Name %>" name="<% $Name %>" value="<% $Default %>" size="<% $Size %>" />
+<%init>
+unless ((defined $Default) or
+ ($current <= 0)) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
+ localtime($current);
+ $Default = sprintf("%04d-%02d-%02d %02d:%02d",
+ $year+1900,$mon+1,$mday,
+ $hour,$min);
+}
+
+unless ($Name) {
+ $Name = $menu_prefix. "_Date";
+}
+</%init>
+
+<%args>
+
+$ShowTime => undef
+$menu_prefix=>''
+$current=>time
+$Default => ''
+$Name => undef
+$Size => 16
+</%args>
diff --git a/rt/html/Elements/SelectDateRelation b/rt/html/Elements/SelectDateRelation
new file mode 100644
index 0000000..9c80be4
--- /dev/null
+++ b/rt/html/Elements/SelectDateRelation
@@ -0,0 +1,60 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+<option value="&lt;"><%$Before%></option>
+<option value="="><%$On%></option>
+<option value="&gt;"><%$After%></option>
+</select>
+
+<%ARGS>
+$Name => undef
+$Default => undef
+$Before => loc('Before')
+$On => loc('On')
+$After => loc('After')
+</%ARGS>
diff --git a/rt/html/Elements/SelectDateType b/rt/html/Elements/SelectDateType
new file mode 100644
index 0000000..653949a
--- /dev/null
+++ b/rt/html/Elements/SelectDateType
@@ -0,0 +1,60 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+<option value="Created"><&|/l&>Created</&></option>
+<option value="Started"><&|/l&>Started</&></option>
+<option value="Resolved"><&|/l&>Resolved</&></option>
+<option value="Told"><&|/l&>Last Contacted</&></option>
+<option value="LastUpdated"><&|/l&>Last Updated</&></option>
+<option value="Starts"><&|/l&>Starts</&></option>
+<option value="Due"><&|/l&>Due</&></option>
+<option value="Updated"><&|/l&>Updated</&></option>
+</select>
+<%ARGS>
+$Name => 'DateType'
+</%ARGS>
diff --git a/rt/html/Elements/SelectEqualityOperator b/rt/html/Elements/SelectEqualityOperator
new file mode 100644
index 0000000..851add3
--- /dev/null
+++ b/rt/html/Elements/SelectEqualityOperator
@@ -0,0 +1,64 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+% while (my $option = shift @Options) {
+% my $value = shift @Values;
+<option value="<%$value%>"
+% if ($Default eq $value) {
+selected
+% }
+><%$option%></option>
+% }
+</select>
+
+<%ARGS>
+$Name => undef
+@Options => (loc('less than'), loc('equal to'), loc('greater than'), loc('not equal to'))
+@Values => qw(< = > !=)
+$Default =>''
+</%ARGS>
diff --git a/rt/html/Elements/SelectGroups b/rt/html/Elements/SelectGroups
new file mode 100644
index 0000000..c1fb9df
--- /dev/null
+++ b/rt/html/Elements/SelectGroups
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="GroupField">
+% foreach my $col (RT::Group->BasicColumns) {
+<option value="<% $col->[0] %>"><% loc($col->[1]) %></option>
+% }
+% while (my $CF = $CFs->Next) {
+<option value="CustomField-<% $CF->Id %>"><&|/l&>CustomField</&>: <% $CF->Name %></option>
+% }
+</select>
+<& /Elements/SelectMatch, Name=> 'GroupOp' &>
+<input size="8" name="GroupString" />
+<%INIT>
+my $CFs = RT::CustomFields->new($session{'CurrentUser'});
+$CFs->LimitToChildType('RT::Group');
+$CFs->OrderBy( FIELD => 'Name' );
+</%INIT>
diff --git a/rt/html/Elements/SelectLang b/rt/html/Elements/SelectLang
new file mode 100644
index 0000000..7640744
--- /dev/null
+++ b/rt/html/Elements/SelectLang
@@ -0,0 +1,80 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+% if ($ShowNullOption) {
+<option value="">-</option>
+% }
+% foreach my $lang (@lang) {
+<option value="<%$lang%>" <%($Default && ($lang eq $Default)) && 'SELECTED'%>><% $lang_to_desc{$lang} %>
+% if (($Verbose) and (my $description = I18N::LangTags::List::native_name($lang)) ){
+(<%$description%>)
+% }
+</option>
+% }
+</select>
+<%ARGS>
+$ShowNullOption => 1
+$ShowAllQueues => 1
+$Name => undef
+$Verbose => undef
+$Default => 0
+$Lite => 0
+</%ARGS>
+
+<%ONCE>
+use I18N::LangTags::List;
+my (@lang, %lang_to_desc);
+foreach my $lang (map { s/:://; s/_/-/g; $_ } grep { /^\w+::$/ } keys %RT::I18N::) {
+ next if $lang =~ /i-default|en-us/;
+ my $desc = I18N::LangTags::List::name($lang);
+ next unless ($desc);
+ $desc =~ s/(.*) (.*)/$2 ($1)/;
+ $lang_to_desc{$lang} = $desc;
+}
+@lang = sort { $lang_to_desc{$a} cmp $lang_to_desc{$b} } keys %lang_to_desc;
+</%ONCE>
diff --git a/rt/html/Elements/SelectLinkType b/rt/html/Elements/SelectLinkType
new file mode 100644
index 0000000..4617e7c
--- /dev/null
+++ b/rt/html/Elements/SelectLinkType
@@ -0,0 +1,61 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+<option value="MemberOf"><&|/l&>Member of</&></option>
+<option value="DependsOn"><&|/l&>Depends on</&></option>
+<option value="RefersTo"><&|/l&>Refers to</&></option>
+</select>
+
+<%ARGS>
+$Name => "LinkType"
+$Default => undef
+</%ARGS>
+
+<%INIT>
+# TODO handle Default
+</%INIT>
diff --git a/rt/html/Elements/SelectMatch b/rt/html/Elements/SelectMatch
new file mode 100644
index 0000000..646ad4e
--- /dev/null
+++ b/rt/html/Elements/SelectMatch
@@ -0,0 +1,82 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+<option value="LIKE" <%$LikeDefault%>><%$Like%></option>
+<option value="NOT LIKE" <%$NotLikeDefault%>><%$NotLike%></option>
+<option value="=" <%$TrueDefault%>><%$True%></option>
+<option value="!=" <%$FalseDefault%>><%$False%></option>
+</select>
+
+<%ARGS>
+$Name => undef
+$Like => loc('contains')
+$NotLike => loc("doesn't contain")
+$True => loc('is')
+$False => loc("isn't")
+$Default => undef
+</%ARGS>
+<%INIT>
+
+my $TrueDefault = '';
+my $FalseDefault='';
+my $LikeDefault='';
+my $NotLikeDefault ='';
+
+if ($Default && $Default =~ /false/i) {
+ $FalseDefault = "SELECTED";
+}
+elsif ($Default && $Default =~ /true/i) {
+ $TrueDefault = "SELECTED";
+}
+elsif ($Default && $Default =~ /notlike/i) {
+ $NotLikeDefault = "SELECTED";
+}
+else {
+ $LikeDefault = "SELECTED";
+}
+</%INIT>
diff --git a/rt/html/Elements/SelectNewTicketQueue b/rt/html/Elements/SelectNewTicketQueue
new file mode 100644
index 0000000..55dc7d2
--- /dev/null
+++ b/rt/html/Elements/SelectNewTicketQueue
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<label accesskey="9">
+ <& /Elements/SelectQueue, Name => 'Queue', %ARGS, ShowNullOption => 0, ShowAllQueues => 0 &>
+</label>
diff --git a/rt/html/Elements/SelectOwner b/rt/html/Elements/SelectOwner
new file mode 100644
index 0000000..f33b346
--- /dev/null
+++ b/rt/html/Elements/SelectOwner
@@ -0,0 +1,110 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+%if ($DefaultValue) {
+<option <% !$Default ? "SELECTED" : '' %> value=""><%$DefaultLabel%></option>
+%}
+%foreach my $User ( @users) {
+<option <% ($User->Id == $Default) ? "SELECTED" : ''%>
+%if ($ValueAttribute eq 'id') {
+ value="<%$User->id%>"
+%} elsif ($ValueAttribute eq 'Name') {
+ value="<%$User->Name%>"
+%}
+><%$User->Name()%></option>
+%}
+</select>
+<%INIT>
+my @objects;
+my @users;
+
+if ($TicketObj) {
+ @objects = ($TicketObj);
+}
+elsif ($QueueObj) {
+ @objects = ($QueueObj);
+}
+elsif ($cfqueues) {
+ @objects = keys %{$cfqueues};
+}
+else {
+ # Let's check rights on an empty queue object. that will do a search for any queue.
+ my $queue = RT::Queue->new($session{'CurrentUser'});
+ push( @objects, $queue );
+}
+
+my %user_uniq_hash;
+
+
+foreach my $object (@objects) {
+ my $Users = RT::Users->new($session{CurrentUser});
+ $Users->WhoHaveRight(Right => 'OwnTicket', Object => $object, IncludeSystemRights => 1, IncludeSuperusers => 0);
+ while (my $User = $Users->Next()) {
+ next if ($User->id == $RT::Nobody->id); # skip nobody here, so we can make them first later
+ $user_uniq_hash{$User->Id()} = $User;
+ }
+}
+
+@users = sort { uc($a->Name) cmp uc($b->Name) } values %user_uniq_hash;
+unshift(@users, $RT::Nobody);
+
+
+
+</%INIT>
+
+<%ARGS>
+$QueueObj => undef
+$Name => undef
+$Default => 0
+$User => undef
+$TicketObj => undef
+$DefaultValue => 1
+$DefaultLabel => "-"
+$ValueAttribute => 'id'
+$cfqueues => undef
+</%ARGS>
diff --git a/rt/html/Elements/SelectQueue b/rt/html/Elements/SelectQueue
new file mode 100644
index 0000000..0decb5b
--- /dev/null
+++ b/rt/html/Elements/SelectQueue
@@ -0,0 +1,97 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if ($Lite) {
+% my $d = new RT::Queue($session{'CurrentUser'});
+% $d->Load($Default);
+<input name="<%$Name%>" size="25" value="<%$d->Name%>" />
+% }
+% else {
+<select name="<%$Name%>">
+% if ($ShowNullOption) {
+ <option value="">-</option>
+% }
+% for my $queue (@{$session{$cache_key}}) {
+ <option value="<% ($NamedValues ? $queue->{Name} : $queue->{Id}) %>" <% ($queue->{Id} eq $Default ? 'selected="selected"' : '') |n %>>
+ <%$queue->{Name}%>
+% if ($Verbose and $queue->{Description}) {
+ (<%$queue->{Description}%>)
+% }
+ </option>
+% }
+</select>
+% }
+<%args>
+$CheckQueueRight => 'CreateTicket'
+$ShowNullOption => 1
+$ShowAllQueues => 1
+$Name => undef
+$Verbose => undef
+$NamedValues => 0
+$Default => 0
+$Lite => 0
+</%args>
+<%init>
+my $cache_key = "SelectQueue---"
+ . $session{'CurrentUser'}->Id
+ . "---$CheckQueueRight---$ShowAllQueues";
+
+if (not defined $session{$cache_key} and not $Lite) {
+ my $q = new RT::Queues($session{'CurrentUser'});
+ $q->UnLimit;
+
+ while (my $queue = $q->Next) {
+ if ($ShowAllQueues || $queue->CurrentUserHasRight($CheckQueueRight)) {
+ push @{$session{$cache_key}}, {
+ Id => $queue->Id,
+ Name => $queue->Name,
+ Description => $queue->Description,
+ };
+ }
+ }
+}
+</%init>
diff --git a/rt/html/Elements/SelectResultsPerPage b/rt/html/Elements/SelectResultsPerPage
new file mode 100644
index 0000000..a83cfaa
--- /dev/null
+++ b/rt/html/Elements/SelectResultsPerPage
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# TODO: Better default handling
+
+<select name="<% $Name %>">
+% foreach my $value ( @values ) {
+<option value="<% $value %>" <% $value == $Default? 'selected': '' %>>
+<% shift @labels %>
+</option>
+% }
+</select>
+
+<%INIT>
+my @values = qw(0 10 25 50 100);
+my @labels = (loc('Unlimited'), qw(10 25 50 100));
+$Default = 50 unless defined $Default;
+</%INIT>
+<%ARGS>
+
+$Name => undef
+$Default => 50
+
+</%ARGS>
diff --git a/rt/html/Elements/SelectSortOrder b/rt/html/Elements/SelectSortOrder
new file mode 100644
index 0000000..2a27717
--- /dev/null
+++ b/rt/html/Elements/SelectSortOrder
@@ -0,0 +1,65 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+%foreach my $order (@orders) {
+<option value="<%$order%>" <%$order eq $Default && 'SELECTED' %>>
+<% shift @order_names %>
+</option>
+% }
+</select>
+
+<%INIT>
+my @orders = qw (ASC DESC);
+my @order_names = (loc('Ascending'), loc('Descending'));
+
+</%INIT>
+
+<%ARGS>
+$Name => 'SortOrder'
+$Default => 'ASC'
+</%ARGS>
diff --git a/rt/html/Elements/SelectStatus b/rt/html/Elements/SelectStatus
new file mode 100644
index 0000000..5f861af
--- /dev/null
+++ b/rt/html/Elements/SelectStatus
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+%if ($DefaultValue) {
+<option <% !$Default && "SELECTED" %> value=""><%$DefaultLabel%></option>
+%}
+%foreach my $status (@status) {
+%next if ($SkipDeleted && $status eq 'deleted');
+<option <% ($status eq $Default) && "SELECTED" %> value="<%$status%>"><%loc($status)%></option>
+% }
+</select>
+<%ONCE>
+my $queue = new RT::Queue($session{'CurrentUser'});
+my @status = $queue->StatusArray();
+</%ONCE>
+<%ARGS>
+$Name => undef
+$Default => ''
+$SkipDeleted => 0
+$DefaultValue => 1
+$DefaultLabel => "-"
+</%ARGS>
diff --git a/rt/html/Elements/SelectTicketSortBy b/rt/html/Elements/SelectTicketSortBy
new file mode 100644
index 0000000..aca98f8
--- /dev/null
+++ b/rt/html/Elements/SelectTicketSortBy
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+% foreach my $field (@sortfields) {
+<option value="<%$field%>" <% $field eq $Default && 'SELECTED'%>><% loc($field) %></option>
+% }
+</select>
+
+<%INIT>
+my $tickets = new RT::Tickets($session{'CurrentUser'});
+my @sortfields = $tickets->SortFields();
+
+</%INIT>
+<%ARGS>
+$Name => 'SortTicketsBy'
+$Default => 'id'
+</%ARGS>
diff --git a/rt/html/Elements/SelectTicketTypes b/rt/html/Elements/SelectTicketTypes
new file mode 100644
index 0000000..5bacccf
--- /dev/null
+++ b/rt/html/Elements/SelectTicketTypes
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+%foreach (@Types) {
+<option value="<% $_ %>" <% ($_ eq $Default) && "SELECTED" %>><&|/l&><% $_ %></&>
+%}
+</select>
+
+<%ARGS>
+$Name => 'TickType'
+$Default => undef
+@Types => qw(Approval Ticket)
+</%ARGS>
diff --git a/rt/html/Elements/SelectTimeUnits b/rt/html/Elements/SelectTimeUnits
new file mode 100755
index 0000000..e1656f1
--- /dev/null
+++ b/rt/html/Elements/SelectTimeUnits
@@ -0,0 +1,57 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<% $Name %>">
+<option value="minutes" selected><% loc('Minutes') %></option>
+<option value="hours"><% loc('Hours') %></option>
+</select>
+<%INIT>
+$Name .= '-TimeUnits' unless $Name =~ /-TimeUnits$/io;
+</%INIT>
+<%ARGS>
+$Name => ''
+</%ARGS>
diff --git a/rt/html/Elements/SelectUsers b/rt/html/Elements/SelectUsers
new file mode 100644
index 0000000..52febb8
--- /dev/null
+++ b/rt/html/Elements/SelectUsers
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="UserField">
+% foreach my $col (RT::User->BasicColumns) {
+<option value="<% $col->[0] %>"><% loc($col->[1]) %></option>
+% }
+% while (my $CF = $CFs->Next) {
+<option value="CustomField-<% $CF->Id %>"><&|/l&>CustomField</&>: <% $CF->Name %></option>
+% }
+</select>
+<& /Elements/SelectMatch, Name=> 'UserOp' &>
+<input size="8" name="UserString" />
+<%INIT>
+my $CFs = RT::CustomFields->new($session{'CurrentUser'});
+$CFs->LimitToChildType('RT::User');
+$CFs->OrderBy( FIELD => 'Name' );
+</%INIT>
diff --git a/rt/html/Elements/SelectWatcherType b/rt/html/Elements/SelectWatcherType
new file mode 100644
index 0000000..26854a7
--- /dev/null
+++ b/rt/html/Elements/SelectWatcherType
@@ -0,0 +1,71 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+% if ($AllowNull) {
+<option value="">-</option>
+% }
+%for my $option (@types) {
+<option value="<%$option%>" <%defined($Default) && $option eq $Default && "SELECTED"%>><%loc($option)%></option>
+%}
+</select>
+
+<%INIT>
+my @types;
+if ($Scope =~ 'queue') {
+ @types = qw(Cc AdminCc);
+}
+else {
+ @types = qw(Requestor Cc AdminCc);
+}
+</%INIT>
+<%ARGS>
+$AllowNull => 1
+$Default=>undef
+$Scope => 'ticket'
+$Name => 'WatcherType'
+</%ARGS>
diff --git a/rt/html/Elements/SetupSessionCookie b/rt/html/Elements/SetupSessionCookie
new file mode 100644
index 0000000..63101af
--- /dev/null
+++ b/rt/html/Elements/SetupSessionCookie
@@ -0,0 +1,126 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+return if $m->is_subrequest; # avoid reentrancy, as suggested by masonbook
+
+my %cookies = CGI::Cookie->fetch();
+my $cookiename = "RT_SID_" . $RT::rtname . "." . $ENV{'SERVER_PORT'};
+$SessionCookie ||= $cookies{$cookiename} ? $cookies{$cookiename}->value : undef;
+
+my %backends = (
+ mysql => 'Apache::Session::MySQL',
+ Pg => 'Apache::Session::Postgres',
+
+ # Oracle => 'Apache::Session::Oracle',
+);
+
+my $session_class = $RT::WebSessionClass
+ || $backends{$RT::DatabaseType}
+ || 'Apache::Session::File';
+my $pm = "$session_class.pm";
+$pm =~ s|::|/|g;
+require $pm;
+
+# morning bug avoidance attempt -- pdh 20030815
+unless ( $RT::Handle->dbh && $RT::Handle->dbh->ping ) {
+ $RT::Handle->Connect();
+}
+
+my $session_properties;
+if ( $session_class eq 'Apache::Session::File' ) {
+ $session_properties = {
+ Directory => $RT::MasonSessionDir,
+ LockDirectory => $RT::MasonSessionDir,
+ Transaction => 1
+ };
+} else {
+ $session_properties = {
+ Handle => $RT::Handle->dbh,
+ LockHandle => $RT::Handle->dbh,
+ Transaction => 1
+ };
+}
+
+eval {
+ tie %session, $session_class, $SessionCookie, $session_properties
+};
+if ($@) {
+
+ # If the session is invalid, create a new session.
+ eval {
+ tie %session, $session_class, undef, $session_properties;
+ undef $cookies{$cookiename};
+ };
+}
+
+if ($@) {
+ die loc("RT couldn't store your session.") . "\n"
+ . loc(
+ "This may mean that that the directory '[_1]' isn't writable or a database table is missing or corrupt.",
+ $RT::MasonSessionDir
+ )
+ . "\n\n"
+ . $@;
+}
+
+if ( !$cookies{$cookiename} ) {
+ my $cookie = new CGI::Cookie(
+ -name => $cookiename,
+ -value => $session{_session_id},
+ -path => $RT::WebPath,
+ -secure => ($RT::WebSecureCookies ? 1 :0)
+ );
+ $r->headers_out->{'Set-Cookie'} = $cookie->as_string;
+
+}
+
+return ();
+</%init>
+<%args>
+$SessionCookie => undef
+</%args>
diff --git a/rt/html/Elements/ShowCustomFieldBinary b/rt/html/Elements/ShowCustomFieldBinary
new file mode 100644
index 0000000..9dd5faa
--- /dev/null
+++ b/rt/html/Elements/ShowCustomFieldBinary
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<a href="<%$RT::WebPath%>/Download/CustomFieldValue/<% $Object->Id %>/<% $Object->Content %>"><% $Object->Content %></a>
+<%ARGS>
+$Object => undef
+</%ARGS>
diff --git a/rt/html/Elements/ShowCustomFieldImage b/rt/html/Elements/ShowCustomFieldImage
new file mode 100644
index 0000000..c49ae0e
--- /dev/null
+++ b/rt/html/Elements/ShowCustomFieldImage
@@ -0,0 +1,53 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% my $url = $RT::WebPath . "/Download/CustomFieldValue/".$Object->Id.'/'.$Object->Content;
+<a href="<% $url %>"><% $Object->Content %></a>
+<img type="<% $Object->ContentType %>" height="64" src="<% $url %>" align="middle" />
+<%ARGS>
+$Object
+</%ARGS>
diff --git a/rt/html/Elements/ShowCustomFieldWikitext b/rt/html/Elements/ShowCustomFieldWikitext
new file mode 100644
index 0000000..1ddd7da
--- /dev/null
+++ b/rt/html/Elements/ShowCustomFieldWikitext
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% my $content = $Object->LargeContent || $Object->Content;
+% $content = $m->comp('/Elements/ScrubHTML', Content => $content);
+% my $base = $Object->Object->WikiBase;
+% my $wiki_content = Text::WikiFormat::format( $content."\n" , {}, { extended => 1, absolute_links => 1, implicit_links => $RT::WikiImplicitLinks, prefix => $base} );
+<%$wiki_content|n%>
+<%init>
+use Text::WikiFormat;
+</%init>
+<%ARGS>
+$Object
+</%ARGS>
diff --git a/rt/html/Elements/ShowCustomFields b/rt/html/Elements/ShowCustomFields
new file mode 100644
index 0000000..743c999
--- /dev/null
+++ b/rt/html/Elements/ShowCustomFields
@@ -0,0 +1,115 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+% while ( my $CustomField = $CustomFields->Next ) {
+% my $Values = $Object->CustomFieldValues( $CustomField->Id );
+% my $count = $Values->Count;
+ <tr id="CF-<%$CustomField->id%>-ShowRow">
+ <td class="label"><% $CustomField->Name %>:</td>
+ <td class="value">
+% unless ( $count ) {
+<i><&|/l&>(no value)</&></i>
+% } elsif ( $count == 1 ) {
+% $print_value->( $CustomField, $Values->First );
+% } else {
+<ul>
+% while ( my $Value = $Values->Next ) {
+<li>
+% $print_value->( $CustomField, $Value );
+</li>
+% }
+</ul>
+% }
+ </td>
+ </tr>
+% }
+</table>
+<%INIT>
+my $CustomFields = $Object->CustomFields;
+$m->comp('/Elements/Callback', _CallbackName => 'MassageCustomFields',
+ CustomFields => $CustomFields);
+
+my $print_value = sub {
+ my ($cf, $value) = @_;
+ my $linked = $cf->LinkValueTo;
+ if ( $linked ) {
+ $m->out('<a href="'. $value->LinkValueTo .'" target="_new">');
+ }
+ my $comp = "ShowCustomField". $cf->Type;
+ $m->comp('/Elements/Callback',
+ _CallbackName => 'ShowComponentName',
+ Name => \$comp,
+ CustomField => $cf,
+ Object => $Object
+ );
+ if ( $m->comp_exists( $comp ) ) {
+ $m->comp( $comp, Object => $value );
+ } else {
+ $m->print( $value->Content );
+ }
+ $m->out('</a>') if $linked;
+
+ # This section automatically populates a div with the "IncludeContentForValue" for this custom
+ # field if it's been defined
+ if ( $cf->IncludeContentForValue ) {
+ my $vid = $value->id;
+ $m->out( '<div class="object_cf_value_include" id="object_cf_value_'. $vid .'">' );
+ $m->print( loc("See also:") );
+ $m->out( '<a href="'. $value->IncludeContentForValue .'">' );
+ $m->print( $value->IncludeContentForValue );
+ $m->out( qq{</a></div>\n} );
+ $m->out( qq{<script><!--\nahah('} );
+ $m->print( $value->IncludeContentForValue );
+ $m->out( qq{', 'object_cf_value_$vid');\n--></script>\n} );
+ }
+};
+
+</%INIT>
+<%ARGS>
+$Object => undef
+</%ARGS>
diff --git a/rt/html/Elements/ShowLink b/rt/html/Elements/ShowLink
new file mode 100644
index 0000000..a1d6bdf
--- /dev/null
+++ b/rt/html/Elements/ShowLink
@@ -0,0 +1,64 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<a href="<%$URI->Resolver->HREF%>">
+% if ($URI->IsLocal) {
+% my $member = $URI->Object;
+% if (UNIVERSAL::isa($member, "RT::Ticket")) {
+<%$member->Id%>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> [<% loc($member->Status) %>]
+% } elsif ( UNIVERSAL::can($member, 'Name')) {
+<%$URI->Resolver->AsString%>: <%$member->Name%>
+% } else {
+<%$URI->Resolver->AsString%>
+% }
+% } else {
+<%$URI->Resolver->AsString%>
+% }
+</a>
+<%ARGS>
+$URI => undef
+</%ARGS>
diff --git a/rt/html/Elements/ShowLinks b/rt/html/Elements/ShowLinks
new file mode 100755
index 0000000..800e82e
--- /dev/null
+++ b/rt/html/Elements/ShowLinks
@@ -0,0 +1,112 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+ <tr>
+ <td class="labeltop"><&|/l&>Depends on</&>:</td>
+ <td class="value">
+<ul>
+% while (my $Link = $Ticket->DependsOn->Next) {
+<li><& ShowLink, URI => $Link->TargetURI &></li>
+% }
+</ul>
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Depended on by</&>:</td>
+ <td class="value">
+<ul>
+% while (my $Link = $Ticket->DependedOnBy->Next) {
+<li><& ShowLink, URI => $Link->BaseURI &></li>
+% }
+</ul>
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Parents</&>:</td>
+ <td class="value">
+<ul>
+% while (my $Link = $Ticket->MemberOf->Next) {
+<li><& ShowLink, URI => $Link->TargetURI &></li>
+% }
+</ul>
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Children</&>:</td>
+ <td class="value"><& /Ticket/Elements/ShowMembers, Ticket => $Ticket &></td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Refers to</&>:</td>
+ <td class="value">
+<ul>
+% while (my $Link = $Ticket->RefersTo->Next) {
+<li><& ShowLink, URI => $Link->TargetURI &></li>
+% }
+</ul>
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Referred to by</&>:</td>
+ <td class="value">
+ <ul>
+% while (my $Link = $Ticket->ReferredToBy->Next) {
+% next if (UNIVERSAL::isa($Link->BaseObj, 'RT::Ticket') && $Link->BaseObj->Type eq 'reminder');
+<li><& ShowLink, URI => $Link->BaseURI &></li>
+% }
+</ul>
+ </td>
+ </tr>
+
+% # Allow people to add more rows to the table
+% $m->comp('/Elements/Callback', %ARGS );
+
+</table>
+
+<%ARGS>
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Elements/ShowMemberships b/rt/html/Elements/ShowMemberships
new file mode 100644
index 0000000..f6b2817
--- /dev/null
+++ b/rt/html/Elements/ShowMemberships
@@ -0,0 +1,88 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<ul>
+% while ( my $GroupMember = $GroupMembers->Next ) {
+% my $Group = RT::Group->new($session{'CurrentUser'});
+% $Group->Load($GroupMember->GroupId) or next;
+% if ($Group->Domain eq 'UserDefined') {
+<li><a href="<%$RT::WebPath%>/Admin/Groups/Modify.html?id=<% $Group->Id %>"><% $Group->Name %></a></li>
+% } elsif ($Group->Domain eq 'SystemInternal') {
+<li><em><% loc($Group->Type) %></em></li>
+% }
+% }
+</ul>
+<%INIT>
+my $GroupMembers = RT::GroupMembers->new($session{'CurrentUser'});
+$GroupMembers->Limit( FIELD => 'MemberId', VALUE => $UserObj->Id );
+my $alias = $GroupMembers->Join(
+ TYPE => 'left',
+ ALIAS1 => 'main',
+ FIELD1 => 'GroupId',
+ TABLE2 => 'Groups',
+ FIELD2 => 'id'
+);
+$GroupMembers->Limit(
+ ALIAS => $alias,
+ FIELD => 'Domain',
+ OPERATOR => '=',
+ VALUE => 'SystemInternal',
+);
+$GroupMembers->Limit(
+ ALIAS => $alias,
+ FIELD => 'Domain',
+ OPERATOR => '=',
+ VALUE => 'UserDefined',
+);
+$GroupMembers->OrderByCols(
+ { ALIAS => $alias, FIELD => 'Domain' },
+ { ALIAS => $alias, FIELD => 'Name' },
+);
+</%INIT>
+<%ARGS>
+$UserObj
+</%ARGS>
diff --git a/rt/html/Elements/ShowSearch b/rt/html/Elements/ShowSearch
new file mode 100644
index 0000000..ded865b
--- /dev/null
+++ b/rt/html/Elements/ShowSearch
@@ -0,0 +1,126 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&|/Widgets/TitleBox,
+ title => loc($search->Description, $ProcessedSearchArg->{'Rows'}),
+ title_href => $query_link_url.$QueryString,
+ titleright => $customize ? loc('Edit') : '',
+ titleright_href => $customize &>
+<& $query_display_component, %$ProcessedSearchArg, ShowNavigation => 0 &>
+</&>
+<%init>
+my $search;
+my $user = $session{'CurrentUser'}->UserObj;
+my $SearchArg;
+my $customize;
+my $query_display_component = '/Elements/TicketList';
+my $query_link_url = 'Search/Results.html';
+
+if ($SavedSearch) {
+ my ( $container_object, $search_id ) = _parse_saved_search($SavedSearch);
+ $search = $container_object->Attributes->WithId($search_id);
+ unless ( $search->Id && ref( $SearchArg = $search->Content ) eq 'HASH' ) {
+ $m->out("Saved Search $SavedSearch not found");
+ return;
+ }
+ $SearchArg->{'SearchType'} ||= 'Ticket';
+ if ( $SearchArg->{SearchType} ne 'Ticket' ) {
+
+ # XXX: dispatch to different handler here
+ $query_display_component
+ = '/Search/Elements/' . $SearchArg->{SearchType};
+ $query_link_url = "Search/$SearchArg->{SearchType}.html";
+ } else {
+ $customize = $RT::WebPath . '/Search/Build.html?'
+ . $m->comp( '/Elements/QueryString',
+ LoadSavedSearch => $SavedSearch );
+ }
+} else {
+ ($search) = RT::System->new( $session{'CurrentUser'} ) ->Attributes->Named( 'Search - ' . $Name );
+ unless ( $search && $search->Id ) {
+ my (@custom_searches) = RT::System->new( $session{'CurrentUser'} )->Attributes->Named('SavedSearch');
+ foreach my $custom (@custom_searches) {
+ if ($custom->Description eq $Name) { $search = $custom; last }
+ }
+ unless ($search && $search->id) {
+ $m->out("Predefined search $Name not found");
+ return;
+ }
+ }
+
+ $SearchArg = $user->Preferences( $search, $search->Content );
+ $customize = $RT::WebPath . '/Prefs/Search.html?'
+ . $m->comp( '/Elements/QueryString',
+ name => ref($search) . '-' . $search->Id );
+}
+
+# ProcessedSearchArg is a search with overridings, but for link we use
+# orginal search's poperties
+my $ProcessedSearchArg = $SearchArg;
+$ProcessedSearchArg = { %$SearchArg, %Override } if keys %Override;
+
+$m->comp(
+ '/Elements/Callback', %ARGS,
+ _CallbackName => 'ModifySearch',
+ OriginalSearch => $SearchArg,
+ Search => $ProcessedSearchArg,
+);
+
+foreach ( $SearchArg, $ProcessedSearchArg ) {
+ $_->{'Query'} =~ s/__CurrentUser__/$session{'CurrentUser'}->Id/ge;
+ $_->{'Format'} =~ s/__WebPath__/$RT::WebPath/g;
+ $_->{'Format'} =~ s/__loc\(["']?(\w+)["']?\)__/loc("$1")/ge;
+}
+
+my $QueryString = '?' . $m->comp( '/Elements/QueryString', %$SearchArg );
+
+</%init>
+<%ARGS>
+$Name => undef
+$SavedSearch => undef
+%Override => ()
+</%ARGS>
diff --git a/rt/html/Elements/SimpleSearch b/rt/html/Elements/SimpleSearch
new file mode 100644
index 0000000..a4fd7e2
--- /dev/null
+++ b/rt/html/Elements/SimpleSearch
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<form action="<% $RT::WebPath %>/Search/Simple.html" STYLE="margin:0">
+<SCRIPT TYPE="text/javascript">
+ function clearhint_search_ticket (what) {
+ if ( what.value == '(ticket # or subject string)' )
+ what.value = '';
+ }
+</SCRIPT>
+<input name="q" autocomplete="off" accesskey="0" class="field" VALUE="(ticket # or subject string)" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+<A HREF="<% $RT::WebPath %>/Search/Build.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" class="fsblackbutton" value="<&|/l&>Search tickets</&>" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%;padding-left:2px;padding-right:2px">
+</form>
diff --git a/rt/html/Elements/Submit b/rt/html/Elements/Submit
new file mode 100644
index 0000000..ee3e8b6
--- /dev/null
+++ b/rt/html/Elements/Submit
@@ -0,0 +1,86 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<div class="submit">
+ <div class="extra-buttons">
+% if ($CheckAll) {
+ <input type="button" value="<%$CheckAllLabel%>" onclick="setCheckbox(this.form, '<% $CheckboxName %>', true);return false;" class="button" />
+% }
+% if ($ClearAll) {
+ <input type="button" value="<%$ClearAllLabel%>" onclick="setCheckbox(this.form, '<% $CheckboxName %>', false);return false;" class="button" />
+% }
+% if ($Reset) {
+ <input type="reset" value="<%$ResetLabel%>" class="button" />
+% }
+ </div>
+ <div class="buttons">
+% if ($AlternateLabel) {
+ <span class="caption"><%$AlternateCaption%></span>
+ <input type="submit" <% $Name && qq[ name="$Name"] | n %> value="<%$AlternateLabel%>" class="button" />
+% } else {
+ <span class="caption"><%$Caption%></span>
+ <input type="submit" <% $Name && qq[ name="$Name"] | n %> value="<%$Label%>" class="button" />
+% }
+ </div>
+ <div class="submit-clear"></div>
+</div>
+
+<%ARGS>
+$color => undef
+$Caption => ''
+$AlternateCaption => undef
+$AlternateLabel => undef
+$Label => loc('Submit')
+$Name => undef
+$CheckAll => undef
+$CheckAllLabel => loc('Check All')
+$ClearAll => undef
+$ClearAllLabel => loc('Clear All')
+$CheckboxName => ''
+$Reset => undef
+$ResetLabel => loc('Reset')
+</%ARGS>
diff --git a/rt/html/Elements/Tabs b/rt/html/Elements/Tabs
new file mode 100644
index 0000000..9d1eea6
--- /dev/null
+++ b/rt/html/Elements/Tabs
@@ -0,0 +1,137 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/PageLayout,
+ current_toptab => $current_toptab,
+ current_tab => $current_tab,
+ toptabs => $toptabs,
+ topactions => $topactions,
+ tabs => $tabs,
+ actions => $actions,
+ subactions => $subactions,
+ title => $Title,
+ show_menu => $show_menu,
+&>
+<a name="skipnav" id="skipnav" accesskey="8"></a>
+<%INIT>
+my $action;
+my $basetopactions = {
+# A => { html => $m->scomp('/Elements/CreateTicket')
+# },
+ A => { html => $m->scomp('/Elements/FreesideNewCust')
+ },
+ B => { html => $m->scomp('/Elements/FreesideSearch')
+ },
+ C => { html => $m->scomp('/Elements/FreesideInvoiceSearch')
+ },
+ D => { html => $m->scomp('/Elements/FreesideSvcSearch')
+ },
+ E => { html => $m->scomp('/Elements/SimpleSearch')
+ }
+ };
+my $basetabs = {
+ ' A'=> { title => 'Billing Main',
+ path => &RT::URI::freeside::FreesideURL(),
+ },
+ A => { #title => loc('Homepage'),
+ title => 'Ticketing Main',
+ path => '',
+ },
+ Ab => { title => loc('Simple Ticket Search'),
+ path => 'Search/Simple.html'
+ },
+ B => { title => loc('Adv. Ticket Search'),
+ path => 'Search/Build.html'
+ },
+ C => { title => loc('Tools'),
+ path => 'Tools/index.html'
+ },
+ P => { title => loc('Approval'),
+ path => 'Approvals/'
+ },
+ };
+
+if ($session{'CurrentUser'}->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System )) {
+ $basetabs->{E} = { title => loc('Configuration'),
+ path => 'Admin/',
+ };
+}
+
+if ($session{'CurrentUser'}->HasRight( Right => 'ModifySelf',
+ Object => $RT::System )) {
+ $basetabs->{K} = { title => loc('Preferences'),
+ path => 'User/Prefs.html'
+ };
+}
+
+if (!defined $toptabs) {
+ $toptabs = $basetabs;
+}
+if (!defined $topactions) {
+ $topactions = $basetopactions;
+}
+
+ require RT::URI::freeside;
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback',
+ topactions => $topactions,
+ toptabs => $toptabs, %ARGS);
+
+</%INIT>
+<%ARGS>
+$current_toptab => undef
+$current_tab => undef
+$toptabs => undef
+$topactions => undef
+$tabs => undef
+$actions => undef
+$subactions => undef
+$Title => undef
+$show_menu => 1
+</%ARGS>
diff --git a/rt/html/Elements/TicketList b/rt/html/Elements/TicketList
new file mode 100644
index 0000000..b36101e
--- /dev/null
+++ b/rt/html/Elements/TicketList
@@ -0,0 +1,179 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table border="0" cellspacing="0" cellpadding="1" width="100%">
+
+% if ($ShowHeader) {
+<& /Elements/CollectionAsTable/Header,
+ Format => \@Format,
+ FormatString => $Format,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => $Query,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+
+% my $i;
+% while (my $record = $Collection->Next) {
+% $i++;
+% # Every ten rows, flush the buffer and put something on the page.
+% # hun, this flushes things out out-of-order for me on "RT at a glance"...?
+% # $m->flush_buffer() unless ($i % 10);
+<& /Elements/CollectionAsTable/Row, Format => \@Format, i => $i, record => $record, maxitems => $maxitems &>
+% }
+
+</table>
+
+% if ($Rows && $ShowNavigation) {
+<hr>
+% my $oddRows;
+% if (($TotalFound % $Rows) == 0) {
+% $oddRows = 0;
+% } else { $oddRows = 1; }
+% my $pages = int($TotalFound/$Rows)+$oddRows;
+% $pages = 1 if $pages < 1;
+<&|/l, $Page, $pages &>Page [_1] of [_2]</&>
+
+<%perl>
+my $prev = $m->comp(
+ '/Elements/QueryString',
+ Query => $Query,
+ Format => $Format,
+ Rows => $Rows,
+ OrderBy => $OrderBy,
+ Order => $Order,
+ Page => ( $Page - 1 )
+);
+my $next = $m->comp(
+ '/Elements/QueryString',
+ Query => $Query,
+ Format => $Format,
+ Rows => $Rows,
+ OrderBy => $OrderBy,
+ Order => $Order,
+ Page => ( $Page + 1 )
+);
+</%perl>
+% if ($Page > 1) {
+<a href="<%$BaseURL%><%$prev%>"><&|/l&>Previous Page</&></a>
+% }
+% if (($Page * $Rows) < $TotalFound) {
+<a href="<%$BaseURL%><%$next%>"><&|/l&>Next Page</&></a>
+% }
+% }
+<%INIT>
+my $maxitems = 0;
+
+$Format ||= $RT::DefaultSearchResultFormat;
+
+# DisplayFormat lets us use a "temporary" format for display, while
+# still using our original format for next/prev page links.
+# bulk update uses this feature to add checkboxes
+
+
+$DisplayFormat ||= $Format;
+
+# Scrub the html of the format string to remove any potential nasties.
+$Format = $m->comp('/Elements/ScrubHTML', Content => $Format);
+$DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $DisplayFormat);
+
+
+unless ($Collection) {
+ $Collection = RT::Tickets->new($session{'CurrentUser'});
+ $Collection->FromSQL($Query);
+}
+
+my (@Format) = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat);
+
+# Find the maximum number of items in any row, so we can pad the table.
+my $item = 0;
+foreach my $col (@Format) {
+ $item++;
+ if ( $col->{title} && ($col->{title} eq 'NEWLINE') ) {
+ $item = 0;
+ }
+ else {
+ $maxitems = $item if $item > $maxitems;
+ }
+}
+
+if ($OrderBy =~ /\|/) {
+ # Multiple Sorts
+ my @OrderBy = split /\|/,$OrderBy;
+ my @Order = split /\|/,$Order;
+ $Collection->OrderByCols(
+ map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0
+ .. $#OrderBy ) );;
+} else {
+ $Collection->OrderBy(FIELD => $OrderBy, ORDER => $Order);
+}
+
+$Collection->RowsPerPage($Rows) if ($Rows);
+$Page = 1 unless $Page > 0; # workaround problems with Page = '' or undef
+$Collection->GotoPage( $Page - 1 ); # SB uses page 0 as the first page
+my $TotalFound = $Collection->CountAll();
+
+</%INIT>
+<%ARGS>
+$Query => undef
+$Rows => 50
+$Page => 1
+$Title => 'Ticket Search'
+$Collection => undef
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$BaseURL => undef
+$Format => $RT::DefaultSearchResultFormat
+$DisplayFormat => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+</%ARGS>
diff --git a/rt/html/Elements/TitleBox b/rt/html/Elements/TitleBox
new file mode 100644
index 0000000..dfab9ea
--- /dev/null
+++ b/rt/html/Elements/TitleBox
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+# For compatibility with 3.4
+$m->comp('/Widgets/TitleBox', %ARGS );
+</%init>
diff --git a/rt/html/Elements/TitleBoxEnd b/rt/html/Elements/TitleBoxEnd
new file mode 100644
index 0000000..933d770
--- /dev/null
+++ b/rt/html/Elements/TitleBoxEnd
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+# For compatibility with 3.4
+$m->comp('/Widgets/TitleBoxEnd', %ARGS );
+</%init>
diff --git a/rt/html/Elements/TitleBoxStart b/rt/html/Elements/TitleBoxStart
new file mode 100644
index 0000000..ba24fd9
--- /dev/null
+++ b/rt/html/Elements/TitleBoxStart
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+# For compatibility with 3.4
+$m->comp('/Widgets/TitleBoxStart', %ARGS );
+</%init>
diff --git a/rt/html/Elements/ValidateCustomFields b/rt/html/Elements/ValidateCustomFields
new file mode 100644
index 0000000..4830219
--- /dev/null
+++ b/rt/html/Elements/ValidateCustomFields
@@ -0,0 +1,81 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%INIT>
+my $valid = 1;
+$CustomFields->GotoFirstItem;
+while (my $CF = $CustomFields->Next) {
+ my $pattern = $CF->Pattern;
+ my $field = $NamePrefix . $CF->Id . "-Value";
+ my $value;
+
+ if ($ARGSRef->{"${field}s-Magic"} and exists $ARGSRef->{"${field}s"}) {
+ $value = $ARGSRef->{"${field}s"};
+
+ # We only validate Single Combos -- multis can never be user input
+ next if ref $value;
+ }
+ else {
+ $value = $ARGSRef->{$field};
+ }
+
+ $m->notes(('Field-' . $CF->Id) => $value);
+ next if $CF->MatchPattern($value);
+ $m->notes(
+ ('InvalidField-' . $CF->Id)
+ => (loc("Input must match [_1]", $CF->FriendlyPattern))
+ );
+ $valid = 0;
+}
+$m->notes('ValidFields', $valid);
+return $valid;
+</%INIT>
+<%ARGS>
+$CustomFields
+$ARGSRef
+$NamePrefix => "Object-RT::Ticket--CustomField-"
+</%ARGS>
diff --git a/rt/html/Helpers/CalPopup.html b/rt/html/Helpers/CalPopup.html
new file mode 100644
index 0000000..9509d13
--- /dev/null
+++ b/rt/html/Helpers/CalPopup.html
@@ -0,0 +1,129 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, ShowBar => 0 &>
+%# From /Elements/Header
+</div>
+<div id="body" class="calpopup">
+
+<a href="#" onclick="window.close(); return false;"><&|/l&>Close window</&></a>
+
+<div class="calendar">
+ <table>
+ <caption>
+ <a class="prev" href="CalPopup.html?DisplayedMonth=<%$prev_month%>&DisplayedYear=<%$prev_year%>&field=<%$field%>"><&|/l&>Prev</&></a>
+ <span class="month"><% $months[$DisplayedMonth-1] %> <% $DisplayedYear %></span>
+ <a class="next" href="CalPopup.html?DisplayedMonth=<%$next_month%>&DisplayedYear=<%$next_year%>&field=<%$field%>"><&|/l&>Next</&></a>
+ </caption>
+ <tr>
+% foreach my $wday (@weekdays) {
+ <th><%$wday%></th>
+% }
+ </tr>
+% foreach my $week (@cal) {
+ <tr>
+% foreach my $day (@{$week}) {
+ <td>
+% if ($day) {
+% my $datestr = sprintf('%04d-%02d-%02d', $DisplayedYear, $DisplayedMonth, $day);
+ <a href="#" onclick="updateParentField('<% $field %>','<% $datestr %>'); return false;"><% $day %></a>
+% } else {
+ &nbsp;
+% }
+ </td>
+% } #foreach $day
+ </tr>
+% } # foreach $week
+ </table>
+</div>
+</div>
+</body>
+</html>
+% $m->abort();
+
+<%init>
+use Calendar::Simple;
+my @today = localtime(time());
+
+my @weekdays;
+push @weekdays, loc($_)
+ for qw(Sun Mon Tue Wed Thu Fri Sat);
+
+my @months;
+push @months, loc($_)
+ for qw(January February March April May June July August
+ September October November December);
+
+unless ($DisplayedYear) {
+ $DisplayedMonth = $today[4] + 1;
+ $DisplayedYear = ($today[5] + 1900);
+}
+
+my ($prev_year, $next_year, $prev_month, $next_month);
+$prev_month = $next_month = $DisplayedMonth;
+$prev_year = $next_year = $DisplayedYear;
+
+$next_month++;
+$prev_month--;
+
+if ($DisplayedMonth == 12) {
+ $next_year++;
+ $next_month = 1;
+}
+elsif ($DisplayedMonth == 1) {
+ $prev_month = 12;
+ $prev_year--;
+}
+
+my @cal = calendar($DisplayedMonth, $DisplayedYear);
+</%init>
+
+<%args>
+$field => 'none'
+$DisplayedMonth => undef
+$DisplayedYear => undef
+</%args>
diff --git a/rt/html/Helpers/EmailAutocomplete b/rt/html/Helpers/EmailAutocomplete
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/html/Helpers/EmailAutocomplete
diff --git a/rt/html/NoAuth/Logout.html b/rt/html/NoAuth/Logout.html
new file mode 100644
index 0000000..49b7a4e
--- /dev/null
+++ b/rt/html/NoAuth/Logout.html
@@ -0,0 +1,74 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<html>
+<head>
+<title>RT: Logout</title>
+ <meta HTTP-EQUIV="Refresh" content="0;URL=<%$URL%>">
+</head>
+<body>
+<p><&|/l&>You have been logged out of RT.</&>
+
+<br />
+<br />
+<a href="<%$URL%>"><&|/l&>You're welcome to login again</&></a>.
+
+% $m->abort();
+
+<%INIT>
+$m->comp('/Elements/Callback', _CallbackName => 'BeforeSessionDelete', %ARGS);
+
+if (defined %session) {
+ tied(%session)->delete;
+}
+
+$m->comp('/Elements/Callback', _CallbackName => 'AfterSessionDelete', %ARGS);
+</%INIT>
+
+<%ARGS>
+$URL => $RT::WebPath."/"
+</%ARGS>
diff --git a/rt/html/NoAuth/Reminder.html b/rt/html/NoAuth/Reminder.html
new file mode 100644
index 0000000..dc935d4
--- /dev/null
+++ b/rt/html/NoAuth/Reminder.html
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, title => loc('Password Reminder') &>
+
+<&|/l&>Not yet implemented.</&>
diff --git a/rt/html/NoAuth/css/3.4-compat/body.css b/rt/html/NoAuth/css/3.4-compat/body.css
new file mode 100644
index 0000000..6188951
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/body.css
@@ -0,0 +1,75 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#body {
+ margin: 0.5em 0.5em 0 0.5em;
+ float: left;
+ width: 80%;
+}
+
+#body h1 { font-size: 1.5em; }
+#body h2 { font-size: 1.3em; }
+#body h3 { font-size: 1.1em; }
+#body h4 { font-size: 1em; }
+#body h5 { font-size: 0.9em; }
+#body h6 { font-size: 0.8em; }
+
+#body h1, #body h2, #body h3, #body h4, #body h5, #body h6 {
+ font-weight: bold;
+}
+
+#body :link { color: black; }
+
+#body :link, #body :visited {
+ font-weight: bold;
+ text-decoration: none;
+}
+
+#body :link:hover, #body :visited:hover {
+ text-decoration: underline;
+}
+
diff --git a/rt/html/NoAuth/css/3.4-compat/footer.css b/rt/html/NoAuth/css/3.4-compat/footer.css
new file mode 100644
index 0000000..4e90c50
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/footer.css
@@ -0,0 +1,61 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#footer {
+ clear: both;
+ font-size: 0.8em;
+ margin-top: 5em;
+ padding-bottom: 2em;
+ color: #888;
+}
+
+#footer p {
+ text-align: right;
+ padding: 0 0.5em 0 0;
+ margin: 0;
+}
+
diff --git a/rt/html/NoAuth/css/3.4-compat/forms.css b/rt/html/NoAuth/css/3.4-compat/forms.css
new file mode 100644
index 0000000..301f8f3
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/forms.css
@@ -0,0 +1,104 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+form .label, form label {
+ font-weight: bold;
+}
+
+.submit {
+ background: #069;
+%# These borders are needed so the container actually surrounds the floats inside it
+ border-top: 1px solid white;
+ border-bottom: 1px solid white;
+ color: #ffdb00;
+ font-weight: bold;
+}
+
+.submit .buttons { float: right; }
+.submit .extra-buttons { float: left; }
+.submit .button { font-size: 0.9em; }
+
+.submit .submit-clear { clear: right; }
+
+.input-row {
+ clear: both;
+ padding: 0.25em;
+}
+
+%# ComboBox styles... some properties like height and width must be dynamically
+%# set in the JS (at least for now).
+.combobox {
+ border: 2px inset ButtonHighlight;
+ padding-left: 0.5em;
+ padding-bottom: 0.1em;
+}
+
+.combobox .combo-button {
+ padding: 0 2px 0 2px;
+ margin: 0;
+ background: ButtonFace;
+ color: ButtonText;
+ border: 2px outset ButtonHighlight;
+ cursor: default;
+ font-size: 8pt;
+}
+
+.combobox .combo-text {
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+.combobox .combo-list {
+ z-index: 200;
+}
+
+#quickbar #topactions form {
+ display: inline;
+ margin-left: 2em;
+}
+
diff --git a/rt/html/NoAuth/css/3.4-compat/header.css b/rt/html/NoAuth/css/3.4-compat/header.css
new file mode 100644
index 0000000..a8e3184
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/header.css
@@ -0,0 +1,88 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#header #page-menu {
+ display: none;
+}
+
+#header {
+ background: #4282b5;
+ margin-top: 0;
+ padding-bottom: 0.2em;
+ float: left;
+ width: 82%;
+}
+
+#header h1 {
+ background: #4282b5;
+ color: white;
+ font-size: 1.7em;
+ margin: 0;
+ padding: 0;
+}
+
+#header #actions-menu {
+ display: block;
+ margin: 0 1em 0 0;
+ padding: 0;
+ color: white;
+ text-align: right;
+ font-size: 1.2em;
+}
+
+#header #actions-menu li {
+ display: inline;
+}
+
+#header #actions-menu :link, #header #actions-menu :visited {
+ color: white;
+ text-decoration: none;
+}
+
+#header #actions-menu :link:hover, #header #actions-menu :visited:hover {
+ text-decoration: underline;
+}
diff --git a/rt/html/NoAuth/css/3.4-compat/login.css b/rt/html/NoAuth/css/3.4-compat/login.css
new file mode 100644
index 0000000..2b967e0
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/login.css
@@ -0,0 +1,54 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#body.login-body {
+ width: 98%;
+}
+
+#login-box {
+ width: 30em;
+}
diff --git a/rt/html/NoAuth/css/3.4-compat/main.css b/rt/html/NoAuth/css/3.4-compat/main.css
new file mode 100644
index 0000000..f2c9ad3
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/main.css
@@ -0,0 +1,69 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# Import the 3.5 styles we want to build off of...
+@import "../3.5-default/logo.css";
+@import "../3.5-default/misc.css";
+@import "../3.5-default/transactions.css";
+@import "../3.5-default/approvals.css";
+@import "../3.5-default/login.css";
+@import "../3.5-default/quickbar.css";
+@import "../3.5-default/ticket.css";
+
+%# ...and then import the 3.4 compat styles afterwards so they can cascade
+@import "nav.css";
+@import "footer.css";
+@import "body.css";
+@import "titlebox.css";
+@import "header.css";
+@import "forms.css";
+@import "transactions.css";
+@import "ticket.css";
+@import "login.css";
+@import "quickbar.css";
+@import "misc.css";
+
diff --git a/rt/html/NoAuth/css/3.4-compat/misc.css b/rt/html/NoAuth/css/3.4-compat/misc.css
new file mode 100644
index 0000000..09be77a
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/misc.css
@@ -0,0 +1,49 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.oddline { background: white; }
+.evenline { background: #cecfef; }
diff --git a/rt/html/NoAuth/css/3.4-compat/nav.css b/rt/html/NoAuth/css/3.4-compat/nav.css
new file mode 100644
index 0000000..f83c0fd
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/nav.css
@@ -0,0 +1,106 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#nav {
+ clear: left;
+ float: left;
+ width: 18%;
+ font-size: 1.4em;
+ color: #eee;
+ margin: 0;
+ background: #4282b5 url(<%$RT::WebImagesURL%>/css/cbr.gif) no-repeat bottom right;
+}
+
+#nav #system-menu {
+ padding: 0 0.2em 0.2em 0.2em;
+ margin-top: 0;
+/* background: transparent url(<%$RT::WebImagesURL%>/css/ctr.gif) no-repeat top right; */
+}
+
+#nav ul {
+ list-style: none;
+ padding-left: 0.5em;
+ margin-left: 0;
+}
+
+#nav ul .bullet, #nav ul .separator {
+ display: none;
+}
+
+#nav ul li {
+ padding: 0.4em 0 0.4em 0.2em;
+ border-bottom: 1px solid white;
+}
+
+#nav li ul {
+ font-size: 0.9em;
+}
+
+#nav li ul li {
+ border-bottom: none;
+ padding: 0.2em 0 0 0;
+}
+
+#nav :link, #nav :visited {
+ text-decoration: none;
+ color: #eee;
+}
+
+#nav :link:hover,
+#nav :visited:hover,
+#nav :link.selected,
+#nav :visited.selected
+{
+ color: #ff6;
+}
+
+#nav :link.selected,
+#nav :visited.selected
+{
+ text-decoration: underline;
+ font-weight: bold;
+}
diff --git a/rt/html/NoAuth/css/3.4-compat/quickbar.css b/rt/html/NoAuth/css/3.4-compat/quickbar.css
new file mode 100644
index 0000000..d7aac5d
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/quickbar.css
@@ -0,0 +1,82 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#quickbar {
+ border: 1px solid transparent;
+}
+
+#quickbar #quick-personal {
+ display: inline;
+ color: #888;
+ padding: 0.5em 1em 0 0;
+ float: right;
+}
+
+#quickbar #quick-personal span {
+ font-weight: bold;
+}
+
+#quickbar #quick-personal :link,
+#quickbar #quick-personal :visited
+{
+ color: #888;
+ font-weight: bold;
+}
+
+#quickbar #quick-personal :link:hover,
+#quickbar #quick-personal :visited:hover
+{
+ color: black;
+}
+
+#quickbar #topactions {
+ color: white;
+ font-size: 0.9em;
+ position: relative;
+ right: 1em;
+ float: right;
+}
diff --git a/rt/html/NoAuth/css/3.4-compat/ticket.css b/rt/html/NoAuth/css/3.4-compat/ticket.css
new file mode 100644
index 0000000..2c80892
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/ticket.css
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.value {
+ font-weight: bold;
+}
diff --git a/rt/html/NoAuth/css/3.4-compat/titlebox.css b/rt/html/NoAuth/css/3.4-compat/titlebox.css
new file mode 100644
index 0000000..363da69
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/titlebox.css
@@ -0,0 +1,103 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.titlebox {
+ margin: 0.5em 0;
+}
+
+.titlebox .titlebox-content {
+ padding: 0.05em;
+}
+
+.titlebox .titlebox-title {
+ background: #069;
+ padding: 0.2em 0.5em;
+ color: white;
+ border-top: 1px solid black;
+ border-bottom: 1px solid black;
+ font-weight: bold;
+ position: relative;
+}
+
+.titlebox .titlebox-title .right {
+ position: absolute;
+ right: 1.5em;
+ font-size: 0.9em;
+}
+
+#body .titlebox .titlebox-title :link, #body .titlebox .titlebox-title :visited {
+ color: white;
+}
+
+#body .titlebox .titlebox-title .widget :link, #body .titlebox .titlebox-title .widget :visited {
+ color: black;
+}
+
+.titlebox .titlebox-content hr.clear {
+ visibility: hidden;
+}
+
+%# TRS: I wish there was a more elegant way to do this... I essentially need to
+%# select all elements X that do NOT have element Y as a descendant... which I can
+%# fake with the child selector of CSS2, but IE is stupid and does not support that.
+
+% for (qw(index
+% Search-Build
+% User-Prefs
+% Approvals
+% Admin-Users-Modify
+% SelfService
+% SelfService-Closed
+% Ticket-ModifyAll
+% ))
+% {
+#comp-<%$_%> .titlebox .titlebox-content,
+% }
+.titlebox .titlebox .titlebox-content
+{
+ background: #cecfce;
+}
diff --git a/rt/html/NoAuth/css/3.4-compat/transactions.css b/rt/html/NoAuth/css/3.4-compat/transactions.css
new file mode 100644
index 0000000..2078a31
--- /dev/null
+++ b/rt/html/NoAuth/css/3.4-compat/transactions.css
@@ -0,0 +1,83 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.ticket-transaction {
+ margin: 0;
+ border: none;
+}
+
+.ticket-transaction .type {
+ width: 1em;
+}
+
+.ticket-transaction.even {
+ background: #cecfef;
+}
+
+.ticket-transaction.basics { border-color: #9c3031; }
+.ticket-transaction.basics .type { background: #9c3031; }
+.ticket-summary .ticket-info-basics .titlebox-content { border-left: none; }
+.ticket-summary .ticket-info-basics .titlebox-title { background: #9c3031; }
+
+.ticket-transaction.people { border-color: #31309c; }
+.ticket-transaction.people .type { background: #31309c; }
+.ticket-summary .ticket-info-people .titlebox-content { border-left: none; }
+.ticket-summary .ticket-info-people .titlebox-title { background: #31309c; }
+
+.ticket-transaction.links { border-color: #316531; }
+.ticket-transaction.links .type { background: #316531; }
+.ticket-summary .ticket-info-links .titlebox-content { border-left: none; }
+.ticket-summary .ticket-info-links .titlebox-title { background: #316531; }
+
+.ticket-transaction.dates { border-color: #633063; }
+.ticket-transaction.dates .type { background: #633063; }
+.ticket-summary .ticket-info-dates .titlebox-content { border-left: none; }
+.ticket-summary .ticket-info-dates .titlebox-title { background: #633063; }
+
+.ticket-transaction.message { border-color: #069; }
+.ticket-transaction.message .type { background: #069; }
+
diff --git a/rt/html/NoAuth/css/3.5-default/approvals.css b/rt/html/NoAuth/css/3.5-default/approvals.css
new file mode 100644
index 0000000..5bc836a
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/approvals.css
@@ -0,0 +1,97 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.approval {
+ margin-bottom: 3em;
+ padding: 0 0 1em 0;
+ border: 1px solid #069;
+}
+
+.approval .name {
+ background: #069;
+ color: white;
+ font-size: 1.1em;
+ padding: 0.2em 0 0.4em 0.2em;
+}
+
+#body .approval .name :link, #body .approval .name :visited {
+ color: white;
+}
+
+.approval .originating-ticket {
+ margin: 0.5em;
+ border: 1px solid #aaa;
+}
+
+.approval .originating-ticket .link {
+ display: block;
+ background: #aaa;
+ padding: 0.2em 0 0.4em 0.2em;
+}
+
+.approval .originating-ticket .info {
+ padding: 0.5em;
+}
+
+#body .approval .originating-ticket .link :link,
+#body .approval .originating-ticket .link :visited {
+ color: black;
+}
+
+.approval .form {
+ margin: 1em 0.5em 0.5em 0.5em;
+}
+
+.approval .form .action, .approval .form .notes {
+ float: left;
+ margin-left: 1em;
+}
+
+.approval .form .action { padding-top: 1em; }
+
+.approval .form .action label { font-weight: normal; }
+.approval .form .notes label { display: block; }
diff --git a/rt/html/NoAuth/css/3.5-default/body.css b/rt/html/NoAuth/css/3.5-default/body.css
new file mode 100755
index 0000000..d447034
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/body.css
@@ -0,0 +1,81 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#body {
+ clear: both;
+ margin: 0 0.75em 0 2em;
+ padding-top: 0.5em;
+}
+
+#body h1 {
+ border-bottom: 1px dotted #069;
+ padding-left: 0.5em;
+}
+
+#body h1 { font-size: 1.5em; }
+#body h2 { font-size: 1.3em; }
+#body h3 { font-size: 1.1em; }
+#body h4 { font-size: 1em; }
+#body h5 { font-size: 0.9em; }
+#body h6 { font-size: 0.8em; }
+
+#body h1, #body h2, #body h3, #body h4, #body h5, #body h6 {
+ color: #930;
+ font-weight: bold;
+}
+
+#body :link { color: #069; }
+
+#body :link, #body :visited {
+ font-weight: bold;
+ text-decoration: none;
+}
+
+#body :link:hover, #body :visited:hover {
+ text-decoration: underline;
+}
+
diff --git a/rt/html/NoAuth/css/3.5-default/footer.css b/rt/html/NoAuth/css/3.5-default/footer.css
new file mode 100644
index 0000000..c469982
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/footer.css
@@ -0,0 +1,91 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#footer {
+ clear: both;
+ font-size: 0.8em;
+ margin-top: 5em;
+ padding-bottom: 3em;
+}
+
+#footer p { float: left; }
+
+#footer #time {
+ color: white;
+ background: #069 url(<%$RT::WebImagesURL%>/css/ctr-b2g.gif) no-repeat top right;
+ padding: 0.2em 0 0.3em 0;
+ margin: 0;
+ position: relative;
+ z-index: 2;
+}
+
+#footer #time span {
+ padding: 0.2em 2em 0.3em 3em;
+ background: url(<%$RT::WebImagesURL%>/css/cbr-b2g.gif) no-repeat bottom right;
+}
+
+#footer #bpscredits {
+ background: #ccc url(<%$RT::WebImagesURL%>/css/ctr-gray.gif) no-repeat top right;
+ padding: 0.2em 0 0.3em 0;
+ margin: 0;
+ position: relative;
+ left: -10px;
+ z-index: 1;
+}
+
+#footer #bpscredits span {
+ padding: 0.2em 2em 0.3em 3em;
+ background: url(<%$RT::WebImagesURL%>/css/cbr-gray.gif) no-repeat bottom right;
+}
+
+#footer #legal {
+ float: none;
+ color: #888;
+ padding: 1em 0 0 2em;
+ clear: both;
+}
+
diff --git a/rt/html/NoAuth/css/3.5-default/forms.css b/rt/html/NoAuth/css/3.5-default/forms.css
new file mode 100755
index 0000000..e49c45f
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/forms.css
@@ -0,0 +1,136 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+form input.button {
+ border: 3px double #069;
+ border-top-color: #08c;
+ border-left-color: #08c;
+ padding: 0.25em;
+ background: white;
+ font-weight: bold;
+ font-size: 1em;
+ margin: 0.5em 0.5em 0 0.5em;
+}
+
+form input.button:active {
+ border: 3px double #08c;
+ border-top-color: #069;
+ border-left-color: #069;
+}
+
+form select {
+ border: 1px solid #069;
+ padding: 1px;
+}
+
+form input.field, form input, form textarea {
+ border: 1px solid #069;
+ padding: 3px;
+}
+
+form input.checkbox, form input.radio {
+ border: none;
+ padding: 0;
+}
+
+/* form .entry input, form .value input */
+
+.label, form label, .labeltop {
+ font-weight: bold;
+}
+
+.labeltop {
+ vertical-align: top;
+}
+
+.submit {
+ font-weight: bold;
+ color: #a00;
+ font-size: 1.1em;
+ padding: 0.3em 1.5em 0 1.5em;
+ border-top: 1px solid #930;
+ margin: 1.5em 0 2.5em 0;
+}
+
+.submit .buttons { float: right; }
+.submit .extra-buttons { float: left; }
+.submit .button { font-size: 0.9em; }
+.submit .submit-clear { display: none; }
+
+.input-row {
+ clear: both;
+ padding: 0.25em;
+}
+
+%# ComboBox styles... some properties like height and width must be dynamically
+%# set in the JS (at least for now).
+.combobox {
+ border: 2px inset #069;
+ padding-left: 0.5em;
+ padding-bottom: 0.1em;
+}
+
+.combobox .combo-button {
+ padding: 0 2px 0 2px;
+ margin: 0;
+ background: ButtonFace;
+ color: ButtonText;
+ border: 2px outset ButtonHighlight;
+ cursor: default;
+ font-size: 8pt;
+}
+
+.combobox .combo-text {
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+.combobox .combo-list {
+ z-index: 200;
+}
+
diff --git a/rt/html/NoAuth/css/3.5-default/freeside.css b/rt/html/NoAuth/css/3.5-default/freeside.css
new file mode 100644
index 0000000..a595061
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/freeside.css
@@ -0,0 +1,82 @@
+.black {
+ background-color: #000000;
+ color: #ffffff;
+ background-position: left top;
+ vertical-align: top;
+ text-align: left;
+}
+
+.blackright {
+ background-color: #000000;
+ color: #ffffff;
+ background-position: left top;
+ vertical-align: center;
+ text-align: right;
+ font-size:16px;
+ padding-right:4px
+}
+
+input.fsblackbutton {
+ background-color:#333333;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-family: Arial, Verdana, Helvetica, sans-serif;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff333333',EndColorStr='#ff666666')
+}
+
+input.fsblackbuttonselected {
+ background-color:#7e0079;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ font-family: Arial, Verdana, Helvetica, sans-serif;
+ font-weight:bold;
+ padding-left:12px;
+ padding-right:12px;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff330033',EndColorStr='#ff7e0079')
+}
+
+.darkmediumgray {
+ background-color: #aaaaaa;
+ background-position: left top;
+ vertical-align: top;
+ text-align: left;
+}
+.darkmediumgrayright {
+ background-color: #aaaaaa;
+ background-position: left top;
+ vertical-align: top;
+ text-align: right;
+}
+.bggray {
+ background-color: #e8e8e8;
+ background-position: left top;
+ vertical-align: top;
+ text-align: left;
+}
+.bggrayright {
+ background-color: #e8e8e8;
+ background-position: left top;
+ vertical-align: top;
+ text-align: right;
+}
+
+div.titlebox {
+ background: #d4d4d4;
+}
+
+div.titlebox-title {
+ background: #e8e8e8;
+}
diff --git a/rt/html/NoAuth/css/3.5-default/header.css b/rt/html/NoAuth/css/3.5-default/header.css
new file mode 100644
index 0000000..cc5c9e4
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/header.css
@@ -0,0 +1,152 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#header {
+ clear: both;
+ margin: 0 0.75em 0 0.75em;
+ padding-top: 1em;
+}
+
+#header h1 {
+ margin: 0;
+ padding: 0;
+ color: #930;
+ position: relative;
+ font-size: 2em;
+ font-weight: bold;
+ left: 1.3em;
+ top: 0.15em;
+ z-index: 3;
+ width: 95%;
+}
+
+#header ul {
+ margin: 0;
+ padding: 0;
+ color: #eee;
+ float: left;
+}
+
+#header #page-menu {
+ position: relative;
+ z-index: 2;
+ background: #069 url(<%$RT::WebImagesURL%>/css/ct.gif) no-repeat top left;
+ min-width: 65%;
+}
+
+%# This is an interesting bit of CSS. expression() is an IE-only extension to
+%# it's CSS implementation. Just in case other browsers might choke on it,
+%# the rule is enclosed in a selector only IE will (wrongly) match to an element.
+%#
+%# The expression() function takes Javascript, and basically what it's doing here
+%# is checking to see if the width of the menu would be greater than 65% of the body
+%# width. If it is, great, leave it alone to automatically resize. If it is not, set
+%# it to 65% of the body width. This amounts to emulating the min-width rule that
+%# compliant browsers understand above.
+* html #header ul#page-menu {
+ width: expression(document.body.clientWidth*0.65 < document.getElementById('page-menu').clientWidth ? "auto" : "65%");
+ overflow: visible;
+}
+
+#page-menu div {
+ position: relative;
+ z-index: 3;
+}
+
+#page-menu div { background: url(<%$RT::WebImagesURL%>/css/cb.gif) no-repeat bottom left; }
+#page-menu div div { background: url(<%$RT::WebImagesURL%>/css/cbr.gif) no-repeat bottom right; }
+#page-menu div div div {
+ background: url(<%$RT::WebImagesURL%>/css/ctr.gif) no-repeat top right;
+ padding: 0.2em 1em 0.4em 1em;
+}
+
+#page-menu.actions-present div div { background: url(<%$RT::WebImagesURL%>/css/cbr-b2lb.gif) no-repeat bottom right; }
+#page-menu.actions-present div div div { background: url(<%$RT::WebImagesURL%>/css/ctr-b2lb.gif) no-repeat top right; }
+
+#header ul li {
+ display: inline;
+}
+
+#header #actions-menu {
+ position: relative;
+ background: #08c;
+}
+
+#actions-menu div {
+ position: relative;
+ z-index: 2;
+}
+
+/*#actions-menu div { background: url(<%$RT::WebImagesURL%>/css/cb.gif) no-repeat bottom left; }*/
+#actions-menu div div { background: url(<%$RT::WebImagesURL%>/css/cbr.gif) no-repeat bottom right; }
+#actions-menu div div div {
+ background: url(<%$RT::WebImagesURL%>/css/ctr.gif) no-repeat top right;
+ padding: 0.2em 1em 0.4em 1em;
+}
+
+#header :link,
+#header :visited
+{
+ color: white;
+ text-decoration: none;
+}
+
+#header :link.selected,
+#header :visited.selected,
+#header :link:hover,
+#header :visited:hover
+{
+ color: #fc6; /*#ff6;*/
+}
+
+#header :link.selected,
+#header :visited.selected
+{
+ font-weight: bold;
+ text-decoration: underline;
+}
+
diff --git a/rt/html/NoAuth/css/3.5-default/login.css b/rt/html/NoAuth/css/3.5-default/login.css
new file mode 100644
index 0000000..b9e7aeb
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/login.css
@@ -0,0 +1,85 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#login-box {
+ width: 50%;
+ margin: 0 auto 4em auto;
+}
+
+%# More rules only IE will recognize (but are still valid!) to correct for
+%# IE's incorrect handling of auto margins and the W3C defined behavior.
+%# text-align will affect a block element in IE, therefore centering it, like
+%# left and right auto margins *should*
+* html #login-box {
+ text-align: center;
+}
+
+%# ... and align the text back the way it should be
+* html #login-box .titlebox {
+ text-align: left;
+}
+
+#login-box .input-row {
+ padding: 0.5em;
+}
+
+#login-box .input-row .label {
+ width: 8em;
+ float: left;
+ text-align: right;
+ padding: 0.2em 1em 0 0;
+}
+
+#login-box .input-row .input {
+ float: left;
+}
+
+#login-box .button-row {
+ clear: both;
+ padding: 0.5em;
+ float: right;
+}
diff --git a/rt/html/NoAuth/css/3.5-default/logo.css b/rt/html/NoAuth/css/3.5-default/logo.css
new file mode 100644
index 0000000..e9c079c
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/logo.css
@@ -0,0 +1,60 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#logo {
+ float: left;
+ clear: left;
+
+ margin: 0.5em 0 0.5em 10px;
+}
+
+#logo img { border: none; }
+#logo div.rtname {
+ text-align: center;
+ font-weight: bold;
+}
+
diff --git a/rt/html/NoAuth/css/3.5-default/main.css b/rt/html/NoAuth/css/3.5-default/main.css
new file mode 100644
index 0000000..7c4fa5a
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/main.css
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+@import "misc.css";
+@import "login.css";
+@import "logo.css";
+@import "quickbar.css";
+@import "body.css";
+@import "approvals.css";
+@import "titlebox.css";
+@import "forms.css";
+@import "ticket.css";
+@import "transactions.css";
+@import "nav.css";
+@import "header.css";
+@import "footer.css";
+@import "freeside.css";
+
diff --git a/rt/html/NoAuth/css/3.5-default/misc.css b/rt/html/NoAuth/css/3.5-default/misc.css
new file mode 100755
index 0000000..ddb2e68
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/misc.css
@@ -0,0 +1,92 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+body {
+ font-family: Verdana, sans-serif;
+ font-size: 76%;
+ margin: 0;
+ /* background-color: white; */
+ background-color: #e8e8e8;
+}
+
+.hide, .hidden { display: none !important; }
+
+#body.calpopup {
+ margin-left: 2em;
+}
+
+.calendar {
+ text-align: center;
+ margin: 2em 0 0 0;
+}
+
+.calendar td, .calendar th { padding: 0.1em 0.25em 0.1em 0.25em; }
+
+.calendar caption .month {
+ padding: 0 1em 0 1em;
+ font-size: 1.5em;
+}
+
+.evenline { background-color: white; }
+.oddline { background-color: #ddd; }
+
+td {
+ padding: 0.1em 0.5em 0.1em 0.5em;
+}
+
+.clear { clear: both; }
+
+ul.action-results {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#comp-Search-Build .titlebox-content {
+ padding-left: 0.7em;
+ padding-right: 0.3em;
+}
+
diff --git a/rt/html/NoAuth/css/3.5-default/nav.css b/rt/html/NoAuth/css/3.5-default/nav.css
new file mode 100644
index 0000000..78323aa
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/nav.css
@@ -0,0 +1,163 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#nav {
+ clear: both;
+ font-size: 1.1em;
+}
+
+#nav #system-menu,
+#nav ul
+{
+ min-width: 85%;
+}
+
+%# This is an interesting bit of CSS. expression() is an IE-only extension to
+%# it's CSS implementation. Just in case other browsers might choke on it,
+%# the rule is enclosed in a selector only IE will (wrongly) match to an element.
+%#
+%# The expression() function takes Javascript, and basically what it's doing here
+%# is checking to see if the width of the menu would be greater than 85% of the body
+%# width. If it is, great, leave it alone to automatically resize. If it is not, set
+%# it to 85% of the body width. This amounts to emulating the min-width rule that
+%# compliant browsers understand above.
+* html #nav #system-menu {
+ width: expression(document.body.clientWidth*0.85 < document.getElementById('page-menu').clientWidth ? "auto" : "85%");
+ overflow: visible;
+}
+
+#nav ul {
+ float: left;
+ clear: left;
+
+ color: #eee;
+ font-weight: bold;
+
+ margin: 0;
+ padding: 0;
+
+ list-style: none;
+}
+
+#nav li ul {
+ margin-top: 0.75em;
+}
+
+/*
+%# We need the extra padding above for browsers where we display the arrows
+%# but those don't work in IE so we don't want as much spacing
+%#
+%# IE wrongly matches the selector below even though there isn't an element
+%# above <html> in the doc tree
+*/
+* html #nav li ul {
+ margin-top: 0.25em;
+}
+
+#nav li {
+ display: inline;
+ margin-bottom: 1em;
+ padding: 0.2em 0 0.4em 0;
+}
+
+#nav li.first { padding-left: 1em; }
+
+#nav ul div div.wrapper {
+ text-align: left;
+ padding: 0.2em 1em 0.4em 0;
+}
+
+/****/
+
+#nav :link,
+#nav :visited
+{
+ color: #ececec;
+ text-decoration: none;
+}
+
+#nav :link.selected,
+#nav :visited.selected,
+#nav :link:hover,
+#nav :visited:hover
+{
+ color: #fc6; /*#ff6;*/
+}
+
+#nav :link.selected,
+#nav :visited.selected
+{
+ text-decoration: underline;
+}
+
+html>body #nav :link.selected,
+html>body #nav :visited.selected
+{
+ padding-bottom: 0.8em;
+ background: transparent url(<%$RT::WebImagesURL%>/css/dark-arrow.png) no-repeat bottom center;
+}
+
+html>body #nav :link.selected.odd,
+html>body #nav :visited.selected.odd
+{
+ padding-bottom: 0.8em;
+ background: transparent url(<%$RT::WebImagesURL%>/css/light-arrow.png) no-repeat bottom center;
+}
+
+/*
+#nav ul { background: #069 url(<%$RT::WebImagesURL%>/css/ctr.gif) no-repeat top right; }
+#nav ul div { background: transparent url(<%$RT::WebImagesURL%>/css/cbr.gif) no-repeat bottom right; }
+#nav ul.odd { background: #08c url(<%$RT::WebImagesURL%>/css/ctr.gif) no-repeat top right; }
+#nav ul.odd div { background: transparent url(<%$RT::WebImagesURL%>/css/cbr.gif) no-repeat bottom right; }
+*/
+
+
+#nav ul div.wrapper { background: transparent url(<%$RT::WebImagesURL%>/css/ctr.gif) no-repeat top right; }
+#nav ul div { background: #069 url(<%$RT::WebImagesURL%>/css/cbr.gif) no-repeat bottom right; }
+#nav ul.odd div.wrapper { background: transparent url(<%$RT::WebImagesURL%>/css/ctr.gif) no-repeat top right; }
+#nav ul div.odd { background: #08c url(<%$RT::WebImagesURL%>/css/cbr.gif) no-repeat bottom right; }
+
diff --git a/rt/html/NoAuth/css/3.5-default/quickbar.css b/rt/html/NoAuth/css/3.5-default/quickbar.css
new file mode 100644
index 0000000..16f83f7
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/quickbar.css
@@ -0,0 +1,98 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+#quickbar #quick-personal {
+ display: inline;
+ color: #888;
+ padding: 0.5em 1em 0 0;
+ float: right;
+}
+
+#quickbar #quick-personal span {
+ font-weight: bold;
+}
+
+#quickbar #quick-personal :link,
+#quickbar #quick-personal :visited
+{
+ color: #888;
+ font-weight: bold;
+}
+
+#quickbar #quick-personal :link:hover,
+#quickbar #quick-personal :visited:hover
+{
+ color: black;
+}
+
+#quickbar #topactions {
+ float: right;
+ clear: right;
+
+ font-size: 0.9em;
+ padding: 1em;
+}
+
+#quickbar #topactions form {
+ display: inline;
+ margin-left: 1em;
+}
+
+#quickbar #topactions form .button {
+ padding: 0 2px 0 2px;
+ font-size: 1em;
+ margin: 0;
+}
+
+#quickbar #topactions form .field {
+ padding: 1px;
+ font-size: 0.9em;
+}
+
+#quickbar #topactions form input.field {
+ padding: 3px;
+}
diff --git a/rt/html/NoAuth/css/3.5-default/ticket.css b/rt/html/NoAuth/css/3.5-default/ticket.css
new file mode 100644
index 0000000..5e8eeb7
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/ticket.css
@@ -0,0 +1,57 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.ticket-info-cfs .label {
+ vertical-align: top;
+}
+
+.ticket-info-cfs ul {
+ margin: 0;
+ padding: 0;
+ margin-left: 0.5em;
+ list-style: none;
+}
diff --git a/rt/html/NoAuth/css/3.5-default/titlebox.css b/rt/html/NoAuth/css/3.5-default/titlebox.css
new file mode 100644
index 0000000..79d8040
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/titlebox.css
@@ -0,0 +1,179 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.titlebox {
+ margin-bottom: 1em;
+}
+
+.titlebox .titlebox-content {
+ margin-top: -1px;
+ padding: 1em 2em 0.5em 2em;
+ margin: 0;
+ /*margin: 1em 2em 0.5em 2em;*/
+}
+
+.titlebox th { font-size: 0.8em; }
+
+%# TRS: I wish there was a more elegant way to do this... I essentially need to
+%# select all elements X that do NOT have element Y as a descendant... which I can
+%# fake with the child selector of CSS2, but IE is stupid and does not support that.
+
+% for (qw(index
+% Search-Build
+% User-Prefs
+% Approvals
+% Admin-Users-Modify
+% SelfService
+% SelfService-Closed
+% ))
+% {
+#comp-<%$_%> .titlebox .titlebox-content,
+% }
+.titlebox .titlebox .titlebox-content
+{
+ background: #eee;
+ border-bottom: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ border-left: 0.5em solid #069;
+}
+
+#login-box .titlebox .titlebox-content
+{
+ background: none;
+ border: none;
+}
+
+.titlebox .titlebox-title {
+ position: relative;
+ font-weight: bold;
+ color: #930;
+ font-size: 1.2em;
+ padding: 0.2em 0 0.2em 4em;
+ border-bottom: 1px solid #069;
+}
+
+.titlebox .titlebox-title .right {
+ position: absolute;
+ top: 0.5em;
+ right: 1.5em;
+ font-size: 0.9em;
+ color: #888;
+}
+
+.titlebox .titlebox-title .right .selected { color: #930; }
+
+#body .titlebox .titlebox-title .right :link,
+#body .titlebox .titlebox-title .right :visited {
+ color: #888;
+}
+
+#body .titlebox .titlebox-title .right :link:hover,
+#body .titlebox .titlebox-title .right :visited:hover {
+ color: #930;
+}
+
+.titlebox .titlebox-title .widget a {
+ display: block;
+ padding-top: 1em;
+ width: 20px;
+
+ background: url(<%$RT::WebImagesURL%>/css/rollup-arrow.gif) no-repeat center center;
+
+ margin: 0;
+ text-indent: -9999px;
+
+ position: absolute;
+ top: 0.4em;
+ left: 0.75em;
+ float: left;
+
+%# Basically IE5 will see those crazy backslashes and prematurely end the rule.
+%# This allows values for IE 5's broken box model to be set before the hack and
+%# the real values to be set after. We also set voice-family back to whatever it
+%# would have been on the off chance it's actually used.
+ /* WIN IE5 hack */
+ height: 7px;
+ voice-family: "\"}\"";
+ voice-family: inherit;
+ height: 0;
+ overflow: hidden;
+}
+
+%# IE also doesn't support the child selector ">", so we can use it to set values
+%# that only other browsers will see (in this case, playing nice with Opera, which
+%# also suffers from the backslash hack above.)
+html>body .titlebox .titlebox-title .widget a {
+ height: 0;
+ overflow: hidden;
+}
+
+%# Compensates for IE's bad box model by hiding this rule from other browsers
+* html .titlebox .titlebox-title .widget a {
+ background-position: center 0.3em;
+ left: -3.5em;
+}
+
+.titlebox.rolled-up .titlebox-title .widget a {
+ background-image: url(<%$RT::WebImagesURL%>/css/rolldown-arrow.gif);
+}
+
+#body .titlebox .titlebox-title :link,
+#body .titlebox .titlebox-title :visited
+{
+ color: #930;
+ text-decoration: none;
+}
+
+#body .titlebox .titlebox-title :link:hover,
+#body .titlebox .titlebox-title :visited:hover
+{
+ text-decoration: underline;
+}
+
+.titlebox .titlebox-content hr.clear {
+ visibility: hidden;
+}
diff --git a/rt/html/NoAuth/css/3.5-default/transactions.css b/rt/html/NoAuth/css/3.5-default/transactions.css
new file mode 100755
index 0000000..e9decf8
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/transactions.css
@@ -0,0 +1,150 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+.ticket-transaction {
+ border-bottom: 1px solid #ddd;
+ border-right: 3px solid #069;
+}
+
+#ticket-history .ticket-transaction {
+ border-bottom-color: #ccc;
+}
+
+.ticket-transaction.even {
+ background: #eee;
+}
+.ticket-transaction.odd {
+ background: #fff;
+}
+
+
+.ticket-transaction .date {
+ font-size: 0.9em;
+ width: 10em;
+}
+
+.ticket-transaction .description {
+ font-weight: bold;
+ font-size: 0.9em;
+ text-align: left;
+}
+
+.ticket-transaction .actions {
+ text-align: right;
+ font-weight: bold;
+}
+
+.ticket-transaction .type {
+ background: #888;
+ width: 1.2em;
+ color: white;
+ text-align: center;
+ font-size: 1em;
+}
+
+#body .ticket-transaction .type :link,
+#body .ticket-transaction .type :visited
+{
+ color: white;
+ font-weight: normal;
+}
+
+.ticket-transaction.basics { border-color: #b32; }
+.ticket-transaction.basics .type { background: #b32; }
+.ticket-summary .ticket-info-basics .titlebox-content { border-left: 0.5em solid #b32; }
+
+.ticket-transaction.people { border-color: #48c; }
+.ticket-transaction.people .type { background: #48c; }
+.ticket-summary .ticket-info-people .titlebox-content { border-left: 0.5em solid #48c; }
+
+%# light green - #ad8
+.ticket-transaction.links { border-color: #316531; }
+.ticket-transaction.links .type { background: #316531; }
+.ticket-summary .ticket-info-links .titlebox-content { border-left: 0.5em solid #316531; }
+
+%# orange - #d71
+.ticket-transaction.dates { border-color: #633063; }
+.ticket-transaction.dates .type { background: #633063; }
+.ticket-summary .ticket-info-dates .titlebox-content { border-left: 0.5em solid #633063; }
+
+.ticket-transaction.message { border-color: #069; }
+.ticket-transaction.message .type { background: #069; }
+
+.ticket-transaction.other { border-color: #888; }
+
+.ticket-transaction td .message-header-value {
+ padding: 0;
+}
+
+.ticket-transaction td .message-header-key {
+ padding: 0 1em 0 1.5em;
+ font-weight: bold;
+}
+
+.ticket-transaction .downloadattachment {
+ float: right;
+ font-size: 0.9em;
+ text-align: right;
+}
+
+.ticket-transaction .messagebody {
+ clear: both;
+ padding-left: 3em;
+ padding-bottom: 1em;
+}
+
+%# Message stanza colors
+.message-stanza-depth-0 { color: #000; }
+.message-stanza-depth-1 { color: #600; }
+.message-stanza-depth-2 { color: #060; }
+.message-stanza-depth-3 { color: #006; }
+.message-stanza-depth-4 { color: #c00; }
+.message-stanza-depth-5 { color: #0c0; }
+.message-stanza-depth-6 { color: #00c; }
+.message-stanza-depth-7 { color: #f00; }
+.message-stanza-depth-8 { color: #0f0; }
+.message-stanza-depth-9 { color: #00f; }
diff --git a/rt/html/NoAuth/css/autohandler b/rt/html/NoAuth/css/autohandler
new file mode 100644
index 0000000..980e5f0
--- /dev/null
+++ b/rt/html/NoAuth/css/autohandler
@@ -0,0 +1,53 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+RT::Interface::Web::StaticFileHeaders();
+$r->content_type('text/css');
+$m->call_next();
+return();
+</%init>
diff --git a/rt/html/NoAuth/css/dhandler b/rt/html/NoAuth/css/dhandler
new file mode 100644
index 0000000..4c8ba34
--- /dev/null
+++ b/rt/html/NoAuth/css/dhandler
@@ -0,0 +1,30 @@
+<%ONCE>
+my $squisher;
+</%ONCE>
+<%INIT>
+my $arg = $m->dhandler_arg;
+my $path;
+if ( $arg =~ m{^(.*)-squished(\.[^\.]+)$} ) {
+ $path = $m->current_comp->dir_path .'/'. $1 . $2;
+}
+else {
+ return $m->decline;
+}
+
+$squisher = new RT::CSS::Squish unless $squisher;
+$squisher->{'mason'} = $m;
+
+$m->out( $squisher->concatenate( $path ) );
+
+package RT::CSS::Squish;
+use CSS::Squish '0.06';
+use base qw(CSS::Squish);
+sub file_handle {
+ my $self = shift;
+ my $file = shift;
+ my $content = $self->{'mason'}->scomp($file);
+ open my $fh, '<', \$content or die "$!";
+ return $fh;
+}
+
+</%INIT>
diff --git a/rt/html/NoAuth/css/print.css b/rt/html/NoAuth/css/print.css
new file mode 100644
index 0000000..40d23d0
--- /dev/null
+++ b/rt/html/NoAuth/css/print.css
@@ -0,0 +1,85 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+* {
+ float: none;
+ position: static;
+}
+
+body {
+ margin: 1em;
+ font-size: 10pt;
+}
+
+#body {
+ margin: 0;
+}
+
+#header h1 {
+ margin-bottom: 2em;
+}
+
+#header {
+ padding: 0 !important;
+}
+
+#quickbar,
+#nav,
+#header #page-menu,
+#header #actions-menu,
+.titlebox .title .widget,
+#footer
+{
+display: none;
+}
+
+a:link, a:visited {
+ background: transparent;
+ font-weight: bold !important;
+ text-decoration: underline !important;
+}
+
diff --git a/rt/html/NoAuth/images/autohandler b/rt/html/NoAuth/images/autohandler
new file mode 100644
index 0000000..7209798
--- /dev/null
+++ b/rt/html/NoAuth/images/autohandler
@@ -0,0 +1,28 @@
+<%INIT>
+&RT::Interface::Web::StaticFileHeaders();
+
+# This autohandler will spit out RT's images if the user hasn't
+# properly configured their webserver to stop RT from passing
+# images through the mason handler.
+my $file = $m->base_comp->source_file;
+
+
+my $type = "application/octet-stream";
+if ($file =~ /\.(gif|png|jpe?g)$/i) {
+ $type = "image/$1";
+ $type =~ s/jpg/jpeg/gi;
+}
+
+die "file not found" unless -f $file && -r _;
+
+$r->content_type($type);
+open my $fh, "<$file" or die "couldn't open file: $!";
+binmode($fh);
+{
+ local $/ = \16384;
+ $m->out($_) while (<$fh>);
+ $m->flush_buffer;
+}
+close $fh;
+$m->abort;
+</%INIT>
diff --git a/rt/html/NoAuth/images/bplogo.gif b/rt/html/NoAuth/images/bplogo.gif
new file mode 100644
index 0000000..1bb0adf
--- /dev/null
+++ b/rt/html/NoAuth/images/bplogo.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cb-light.gif b/rt/html/NoAuth/images/css/cb-light.gif
new file mode 100644
index 0000000..d5e3059
--- /dev/null
+++ b/rt/html/NoAuth/images/css/cb-light.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cb.gif b/rt/html/NoAuth/images/css/cb.gif
new file mode 100644
index 0000000..49a4a97
--- /dev/null
+++ b/rt/html/NoAuth/images/css/cb.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cbr-b2g.gif b/rt/html/NoAuth/images/css/cbr-b2g.gif
new file mode 100644
index 0000000..6bca03d
--- /dev/null
+++ b/rt/html/NoAuth/images/css/cbr-b2g.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cbr-b2lb.gif b/rt/html/NoAuth/images/css/cbr-b2lb.gif
new file mode 100644
index 0000000..d207f84
--- /dev/null
+++ b/rt/html/NoAuth/images/css/cbr-b2lb.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cbr-gray.gif b/rt/html/NoAuth/images/css/cbr-gray.gif
new file mode 100644
index 0000000..d732710
--- /dev/null
+++ b/rt/html/NoAuth/images/css/cbr-gray.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cbr-trans.gif b/rt/html/NoAuth/images/css/cbr-trans.gif
new file mode 100644
index 0000000..dc272ee
--- /dev/null
+++ b/rt/html/NoAuth/images/css/cbr-trans.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cbr.gif b/rt/html/NoAuth/images/css/cbr.gif
new file mode 100644
index 0000000..eeb7ff4
--- /dev/null
+++ b/rt/html/NoAuth/images/css/cbr.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ct-light.gif b/rt/html/NoAuth/images/css/ct-light.gif
new file mode 100644
index 0000000..55125b0
--- /dev/null
+++ b/rt/html/NoAuth/images/css/ct-light.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ct.gif b/rt/html/NoAuth/images/css/ct.gif
new file mode 100644
index 0000000..d2ae8d8
--- /dev/null
+++ b/rt/html/NoAuth/images/css/ct.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ctr-b2g.gif b/rt/html/NoAuth/images/css/ctr-b2g.gif
new file mode 100644
index 0000000..540e6d0
--- /dev/null
+++ b/rt/html/NoAuth/images/css/ctr-b2g.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ctr-b2lb.gif b/rt/html/NoAuth/images/css/ctr-b2lb.gif
new file mode 100644
index 0000000..c98b18c
--- /dev/null
+++ b/rt/html/NoAuth/images/css/ctr-b2lb.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ctr-gray.gif b/rt/html/NoAuth/images/css/ctr-gray.gif
new file mode 100644
index 0000000..8d5e5dd
--- /dev/null
+++ b/rt/html/NoAuth/images/css/ctr-gray.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ctr-trans.gif b/rt/html/NoAuth/images/css/ctr-trans.gif
new file mode 100644
index 0000000..bb316cf
--- /dev/null
+++ b/rt/html/NoAuth/images/css/ctr-trans.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ctr.gif b/rt/html/NoAuth/images/css/ctr.gif
new file mode 100644
index 0000000..d17e647
--- /dev/null
+++ b/rt/html/NoAuth/images/css/ctr.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/dark-arrow-up.png b/rt/html/NoAuth/images/css/dark-arrow-up.png
new file mode 100644
index 0000000..443096a
--- /dev/null
+++ b/rt/html/NoAuth/images/css/dark-arrow-up.png
Binary files differ
diff --git a/rt/html/NoAuth/images/css/dark-arrow.png b/rt/html/NoAuth/images/css/dark-arrow.png
new file mode 100644
index 0000000..a83500a
--- /dev/null
+++ b/rt/html/NoAuth/images/css/dark-arrow.png
Binary files differ
diff --git a/rt/html/NoAuth/images/css/fieldbg-autocomplete.gif b/rt/html/NoAuth/images/css/fieldbg-autocomplete.gif
new file mode 100644
index 0000000..aa7eed0
--- /dev/null
+++ b/rt/html/NoAuth/images/css/fieldbg-autocomplete.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/light-arrow-up.png b/rt/html/NoAuth/images/css/light-arrow-up.png
new file mode 100644
index 0000000..c209d43
--- /dev/null
+++ b/rt/html/NoAuth/images/css/light-arrow-up.png
Binary files differ
diff --git a/rt/html/NoAuth/images/css/light-arrow.png b/rt/html/NoAuth/images/css/light-arrow.png
new file mode 100644
index 0000000..575d4e5
--- /dev/null
+++ b/rt/html/NoAuth/images/css/light-arrow.png
Binary files differ
diff --git a/rt/html/NoAuth/images/css/rolldown-arrow.gif b/rt/html/NoAuth/images/css/rolldown-arrow.gif
new file mode 100644
index 0000000..3c296dc
--- /dev/null
+++ b/rt/html/NoAuth/images/css/rolldown-arrow.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/rolldown-arrow.png b/rt/html/NoAuth/images/css/rolldown-arrow.png
new file mode 100644
index 0000000..33d8ab1
--- /dev/null
+++ b/rt/html/NoAuth/images/css/rolldown-arrow.png
Binary files differ
diff --git a/rt/html/NoAuth/images/css/rollup-arrow.gif b/rt/html/NoAuth/images/css/rollup-arrow.gif
new file mode 100644
index 0000000..f009ff4
--- /dev/null
+++ b/rt/html/NoAuth/images/css/rollup-arrow.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/favicon.png b/rt/html/NoAuth/images/favicon.png
new file mode 100644
index 0000000..ed1ee37
--- /dev/null
+++ b/rt/html/NoAuth/images/favicon.png
Binary files differ
diff --git a/rt/html/NoAuth/images/small-logo.png b/rt/html/NoAuth/images/small-logo.png
new file mode 100644
index 0000000..1e415e6
--- /dev/null
+++ b/rt/html/NoAuth/images/small-logo.png
Binary files differ
diff --git a/rt/html/NoAuth/js/ahah.js b/rt/html/NoAuth/js/ahah.js
new file mode 100644
index 0000000..03ed12a
--- /dev/null
+++ b/rt/html/NoAuth/js/ahah.js
@@ -0,0 +1,80 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+/*
+% $r->content_type('application/x-javascript');
+*/
+// Fetched from http://www.opendarwin.org/~drernie/src/ahah.js
+function ahah(url, target, delay) {
+ // document.getElementById(target).innerHTML = 'Loading <a href="'+url+'">'+url +'</a>...';
+ if (window.XMLHttpRequest) {
+ req = new XMLHttpRequest();
+ } else if (window.ActiveXObject) {
+ req = new ActiveXObject("Microsoft.XMLHTTP");
+ }
+ if (req != undefined) {
+ req.onreadystatechange = function() {ahahDone(url, target, delay);};
+ req.open("GET", url, true);
+ req.send("");
+ }
+}
+
+function ahahDone(url, target, delay) {
+ if (req.readyState == 4) { // only if req is "loaded"
+ if (req.status == 200) { // only if "OK"
+ document.getElementById(target).innerHTML = req.responseText;
+ } else {
+ document.getElementById(target).innerHTML="Error loading '"+url+"':\n"+req.statusText;
+ }
+ if (delay != undefined) {
+ setTimeout("ahah(url,target,delay)", delay); // resubmit after delay
+ //server should ALSO delay before responding
+ }
+ }
+}
+
+% $m->abort();
diff --git a/rt/html/NoAuth/js/autohandler b/rt/html/NoAuth/js/autohandler
new file mode 100644
index 0000000..fd1b900
--- /dev/null
+++ b/rt/html/NoAuth/js/autohandler
@@ -0,0 +1,53 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+&RT::Interface::Web::StaticFileHeaders();
+$r->content_type('application/x-javascript');
+$m->call_next();
+return();
+</%init>
diff --git a/rt/html/NoAuth/js/cascaded.js b/rt/html/NoAuth/js/cascaded.js
new file mode 100644
index 0000000..79da416
--- /dev/null
+++ b/rt/html/NoAuth/js/cascaded.js
@@ -0,0 +1,66 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+function filter_cascade (id, val) {
+ var select = document.getElementById(id);
+ if (!select) { return };
+ var i;
+ var children = select.childNodes;
+ for (i in children) {
+ var style = children[i].style;
+ if (!style) { continue };
+ if (val == '') {
+ style.display = 'block';
+ continue;
+ }
+ if (children[i].label.substr(0, val.length) == val) {
+ style.display = 'block';
+ continue;
+ }
+ style.display = 'none';
+ }
+}
diff --git a/rt/html/NoAuth/js/class.js b/rt/html/NoAuth/js/class.js
new file mode 100644
index 0000000..9e4c70e
--- /dev/null
+++ b/rt/html/NoAuth/js/class.js
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+/* by TKirby, released under GPL */
+
+ function _ClassSetup(Object) {
+ this.prototype = Object;
+ return this;
+ }
+
+ function Class(name) {
+ var _newclass_;
+ eval("window."+name+" = new Function('this."+name+".apply(this,arguments);');");
+ eval("window."+name+".define = _ClassSetup;");
+ eval("_newclass_ = window."+name+";");
+ return _newclass_;
+ }
+
diff --git a/rt/html/NoAuth/js/combobox.js b/rt/html/NoAuth/js/combobox.js
new file mode 100644
index 0000000..9225870
--- /dev/null
+++ b/rt/html/NoAuth/js/combobox.js
@@ -0,0 +1,265 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+function ComboBox_InitWith(n) {
+ if ( typeof( window.addEventListener ) != "undefined" ) {
+ window.addEventListener("load", ComboBox_Init(n), false);
+ } else if ( typeof( window.attachEvent ) != "undefined" ) {
+ window.attachEvent("onload", ComboBox_Init(n));
+ } else {
+ ComboBox_Init(n)();
+ }
+}
+function ComboBox_Init(n) {
+ return function () {
+ if ( ComboBox_UplevelBrowser( n ) ) {
+ ComboBox_Load( n );
+ }
+ }
+}
+function ComboBox_UplevelBrowser( n ) {
+ if( typeof( document.getElementById ) == "undefined" ) return false;
+ var combo = document.getElementById( n + "_Container" );
+ if( combo == null || typeof( combo ) == "undefined" ) return false;
+ if( typeof( combo.style ) == "undefined" ) return false;
+ if( typeof( combo.innerHTML ) == "undefined" ) return false;
+ return true;
+}
+function ComboBox_Load( comboId ) {
+ var combo = document.getElementById( comboId + "_Container" );
+ var button = document.getElementById( comboId + "_Button" );
+ var list = document.getElementById( comboId + "_List" );
+ var text = document.getElementById( comboId );
+
+
+ combo.List = list;
+ combo.Button = button;
+ combo.Text = text;
+
+ button.Container = combo;
+ button.Toggle = ComboBox_ToggleList;
+ button.onclick = button.Toggle;
+ button.onmouseover = function(e) { this.Container.List.DisableBlur(e); };
+ button.onmouseout = function(e) { this.Container.List.EnableBlur(e); };
+ button.innerHTML = "\u25BC";
+ button.onselectstart = function(e){ return false; };
+ button.style.height = ( list.offsetHeight - 4 ) + "px";
+
+ text.Container = combo;
+ text.TypeDown = ComboBox_TextTypeDown;
+ text.KeyAccess = ComboBox_TextKeyAccess;
+ text.onkeyup = function(e) { this.KeyAccess(e); this.TypeDown(e); };
+ text.style.width = ( list.offsetWidth ) + "px";
+
+ list.Container = combo;
+ list.Show = ComboBox_ShowList;
+ list.Hide = ComboBox_HideList;
+ list.EnableBlur = ComboBox_ListEnableBlur;
+ list.DisableBlur = ComboBox_ListDisableBlur;
+ list.Select = ComboBox_ListItemSelect;
+ list.ClearSelection = ComboBox_ListClearSelection;
+ list.KeyAccess = ComboBox_ListKeyAccess;
+ list.FireTextChange = ComboBox_ListFireTextChange;
+ list.onchange = null;
+ list.onclick = function(e){ this.Select(e); this.ClearSelection(); this.FireTextChange(); };
+ list.onkeyup = function(e) { this.KeyAccess(e); };
+ list.EnableBlur(null);
+ list.style.position = "absolute";
+ list.size = ComboBox_GetListSize( list );
+ list.IsShowing = true;
+ list.Hide();
+
+}
+function ComboBox_InitEvent( e ) {
+ if( typeof( e ) == "undefined" && typeof( window.event ) != "undefined" ) e = window.event;
+ if( e == null ) e = new Object();
+ return e;
+}
+function ComboBox_ListClearSelection() {
+ if ( typeof( this.Container.Text.createTextRange ) == "undefined" ) return;
+ var rNew = this.Container.Text.createTextRange();
+ rNew.moveStart('character', this.Container.Text.value.length) ;
+ rNew.select();
+}
+function ComboBox_GetListSize( theList ) {
+ ComboBox_EnsureListSize( theList );
+ return theList.listSize;
+}
+function ComboBox_EnsureListSize( theList ) {
+ if ( typeof( theList.listSize ) == "undefined" ) {
+ if( typeof( theList.getAttribute ) != "undefined" ) {
+ if( theList.getAttribute( "listSize" ) != null && theList.getAttribute( "listSize" ) != "" ) {
+ theList.listSize = theList.getAttribute( "listSize" );
+ return;
+ }
+ }
+ if( theList.options.length > 0 ) {
+ theList.listSize = theList.options.length;
+ return;
+ }
+ theList.listSize = 4;
+ }
+}
+function ComboBox_ListKeyAccess(e) { //Make enter/space and escape do the right thing :)
+ e = ComboBox_InitEvent( e );
+ if( e.keyCode == 13 || e.keyCode == 32 ) {
+ this.Select();
+ return;
+ }
+ if( e.keyCode == 27 ) {
+ this.Hide();
+ this.Container.Text.focus();
+ return;
+ }
+}
+function ComboBox_TextKeyAccess(e) { //Make alt+arrow expand the list
+ e = ComboBox_InitEvent( e );
+ if( e.altKey && (e.keyCode == 38 || e.keyCode == 40) ) {
+ this.Container.List.Show();
+ }
+}
+function ComboBox_TextTypeDown(e) { //Make the textbox do a type-down on the list
+ e = ComboBox_InitEvent( e );
+ var items = this.Container.List.options;
+ if( this.value == "" ) return;
+ var ctrlKeys = Array( 8, 46, 37, 38, 39, 40, 33, 34, 35, 36, 45, 16, 20 );
+ for( var i = 0; i < ctrlKeys.length; i++ ) {
+ if( e.keyCode == ctrlKeys[i] ) return;
+ }
+ for( var i = 0; i < items.length; i++ ) {
+ var item = items[i];
+ if( item.text.toLowerCase().indexOf( this.value.toLowerCase() ) == 0 ) {
+ this.Container.List.selectedIndex = i;
+ if ( typeof( this.Container.Text.createTextRange ) != "undefined" ) {
+ this.Container.List.Select();
+ }
+ break;
+ }
+ }
+}
+function ComboBox_ListFireTextChange() {
+ var textOnChange = this.Container.Text.onchange;
+ if ( textOnChange != null && typeof(textOnChange) == "function" ) {
+ textOnChange();
+ }
+}
+function ComboBox_ListEnableBlur(e) {
+ this.onblur = this.Hide;
+}
+function ComboBox_ListDisableBlur(e) {
+ this.onblur = null;
+}
+function ComboBox_ListItemSelect(e) {
+ if( this.options.length > 0 ) {
+ var text = this.Container.Text;
+ var oldValue = text.value;
+ var newValue = this.options[ this.selectedIndex ].text;
+ text.value = newValue;
+ if ( typeof( text.createTextRange ) != "undefined" ) {
+ if (newValue != oldValue) {
+ var rNew = text.createTextRange();
+ rNew.moveStart('character', oldValue.length) ;
+ rNew.select();
+ }
+ }
+ }
+ this.Hide();
+ this.Container.Text.focus();
+}
+function ComboBox_ToggleList(e) {
+ if( this.Container.List.IsShowing == true ) {
+ this.Container.List.Hide();
+ } else {
+ this.Container.List.Show();
+ }
+}
+function ComboBox_ShowList(e) {
+ if ( !this.IsShowing && !this.disabled ) {
+ this.style.width = ( this.Container.offsetWidth ) + "px";
+ this.style.top = ( this.Container.offsetHeight + ComboBox_RecursiveOffsetTop(this.Container,true) ) + "px";
+ this.style.left = ( ComboBox_RecursiveOffsetLeft(this.Container,true) + 1 ) + "px";
+ ComboBox_SetVisibility(this,true);
+ this.focus();
+ this.IsShowing = true;
+ }
+}
+function ComboBox_HideList(e) {
+ if( this.IsShowing ) {
+ ComboBox_SetVisibility(this,false);
+ this.IsShowing = false;
+ }
+}
+function ComboBox_SetVisibility(theList, isVisible) {
+ setVisibility(theList, isVisible);
+}
+function ComboBox_RecursiveOffsetTop(thisObject,isFirst) {
+ if(thisObject.offsetParent) {
+ if ( thisObject.style.position == "absolute" && !isFirst && typeof(document.designMode) != "undefined" ) {
+ return 0;
+ }
+ return (thisObject.offsetTop + ComboBox_RecursiveOffsetTop(thisObject.offsetParent,false));
+ } else {
+ return thisObject.offsetTop;
+ }
+}
+function ComboBox_RecursiveOffsetLeft(thisObject,isFirst) {
+ if(thisObject.offsetParent) {
+ if ( thisObject.style.position == "absolute" && !isFirst && typeof(document.designMode) != "undefined" ) {
+ return 0;
+ }
+ return (thisObject.offsetLeft + ComboBox_RecursiveOffsetLeft(thisObject.offsetParent,false));
+ } else {
+ return thisObject.offsetLeft;
+ }
+}
+function ComboBox_SimpleAttach(selectElement,textElement) {
+ textElement.value = selectElement.options[ selectElement.options.selectedIndex ].text;
+ var textOnChange = textElement.onchange;
+ if ( textOnChange != null && typeof( textOnChange ) == "function" ) {
+ textOnChange();
+ }
+}
diff --git a/rt/html/NoAuth/js/list.js b/rt/html/NoAuth/js/list.js
new file mode 100644
index 0000000..9753b97
--- /dev/null
+++ b/rt/html/NoAuth/js/list.js
@@ -0,0 +1,159 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+/* by TKirby, released under GPL */
+/* Define the "list" Class */
+Class("list").define({
+ name : null,
+ xml : null,
+ sels : null,
+ list : function (src, esrc, name) { this.init(src, esrc, name); },
+ read : function () {
+ var i = 0;
+ if(this.xml.readyState!=4) { setTimeout(this.name+".read()", 100); }
+ else if(this.xml.status!=200) alert("Document not available.");
+ else {
+ var doc = this.xml.responseXML;
+ var nNode = null;
+ if(doc.childNodes[0].nodeName=="parseerror") alert("Parse Error.");
+ doc = doc.getElementsByTagName("list")[0];
+ for(i=0;i<doc.childNodes.length;i++) {
+ if(doc.childNodes[i].childNodes.length>0) {
+ nNode = document.createElement("option");
+ nNode.appendChild(document.createTextNode(doc.childNodes[i].childNodes[0].nodeValue));
+ this.sels[0].appendChild(nNode);
+ }
+ }
+ }
+ },
+
+ init : function (src,esrc,name) {
+ if(!src) return;
+ this.name = name;
+ this.sels = new Array();
+ var i = 0;
+ for(i=0;i<src.childNodes.length;i++) {
+ if(src.childNodes[i].nodeName=="select" || src.childNodes[i].nodeName=="SELECT") {
+ this.sels.push(src.childNodes[i]);
+ }
+
+ if((src.childNodes[i].nodeName=="input" || src.childNodes[i].nodeName=="INPUT")
+ && (src.childNodes[i].name=="fromjs")) {
+ src.childNodes[i].value = 1;
+ }
+
+ if((src.childNodes[i].nodeName=="input" || src.childNodes[i].nodeName=="INPUT")
+ && (src.childNodes[i].type=="submit" || src.childNodes[i].type=="SUBMIT")) {
+
+ if (src.childNodes[i].name.indexOf("Save") < 0) {
+ var tmp = document.createElement("input");
+ tmp.type = "button";
+ tmp.name = src.childNodes[i].name;
+ tmp.value = src.childNodes[i].value;
+ src.replaceChild(tmp,src.childNodes[i]);
+ }
+
+ if(src.childNodes[i].name=="add")
+ src.childNodes[i].onclick = new Function(this.name+".add();");
+ if(src.childNodes[i].name=="remove")
+ src.childNodes[i].onclick = new Function(this.name+".remove();");
+ if(src.childNodes[i].name=="moveup")
+ src.childNodes[i].onclick = new Function(this.name+".moveup();");
+ if(src.childNodes[i].name=="movedown")
+ src.childNodes[i].onclick = new Function(this.name+".movedown();");
+ }
+ }
+ if (esrc) {
+ this.xml = (window.navigator.appName!="Microsoft Internet Explorer"
+ ?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP"));
+ this.xml.open("GET", esrc);
+ this.xml.send("");
+ setTimeout(this.name+".read()", 100);
+ }
+ },
+
+ add : function() {
+ var i, j = 0;
+ var dNode = null;
+ for(i=0;i<this.sels[0].length;i++) if(this.sels[0][i].selected) {
+ for(j=0;j<this.sels[1].length;j++) if(this.sels[1][j].value==this.sels[0][i].value) break;
+ if(j==this.sels[1].length) dNode = this.sels[0][i].cloneNode(true),
+ this.sels[1].appendChild(dNode);
+ }
+ },
+
+ moveup : function() { this.move(-1); },
+ movedown : function() { this.move(1); },
+ move : function(v) {
+ var i = 0;
+ if(v<0) for(i=0;i<this.sels[1].length;i++) this.moveOne(v, i);
+ else if(v>0) for(i=this.sels[1].length-1;i>=0;i--)this.moveOne(v, i);
+ },
+
+ moveOne : function(v, i) {
+ var ins = v + i;
+ if(ins<0 || ins>=this.sels[1].length) return;
+ if(this.sels[1][ins].selected) return;
+ if(this.sels[1][i].selected) {
+ Node = this.sels[1][i];
+ this.sels[1].removeChild(Node);
+ this.sels[1].insertBefore(Node, this.sels[1][ins]);
+ }
+ },
+
+ remove : function() {
+ var i = 0;
+ for(i=this.sels[1].length-1;i>=0;i--) if(this.sels[1][i].selected)
+ this.sels[1].removeChild(this.sels[1][i]);
+ },
+
+ selectAll: function() {
+ var i = 0;
+ for(i=0;i<this.sels[0].length;i++) this.sels[0][i].selected = false;
+ for(i=0;i<this.sels[1].length;i++) this.sels[1][i].selected = true;
+ }
+});
diff --git a/rt/html/NoAuth/js/scriptaculous/controls.js b/rt/html/NoAuth/js/scriptaculous/controls.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/html/NoAuth/js/scriptaculous/controls.js
diff --git a/rt/html/NoAuth/js/scriptaculous/effects.js b/rt/html/NoAuth/js/scriptaculous/effects.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/html/NoAuth/js/scriptaculous/effects.js
diff --git a/rt/html/NoAuth/js/scriptaculous/prototype.js b/rt/html/NoAuth/js/scriptaculous/prototype.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/html/NoAuth/js/scriptaculous/prototype.js
diff --git a/rt/html/NoAuth/js/scriptaculous/scriptaculous.js b/rt/html/NoAuth/js/scriptaculous/scriptaculous.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rt/html/NoAuth/js/scriptaculous/scriptaculous.js
diff --git a/rt/html/NoAuth/js/titlebox-state.js b/rt/html/NoAuth/js/titlebox-state.js
new file mode 100644
index 0000000..8950f9e
--- /dev/null
+++ b/rt/html/NoAuth/js/titlebox-state.js
@@ -0,0 +1,83 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+function createCookie(name,value,days) {
+ var path = "<%$RT::WebPath%>" ? "<%$RT::WebPath%>" : "/";
+
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime()+(days*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ }
+ else
+ expires = "";
+
+ document.cookie = name+"="+value+expires+"; path="+path;
+}
+
+function loadTitleBoxStates() {
+ var cookies = document.cookie.split(/;\s*/);
+ var len = cookies.length;
+
+ for (var i = 0; i < len; i++) {
+ var c = cookies[i].split('=');
+
+ if (c[0].match(/^TitleBox--/)) {
+ var e = document.getElementById(c[0]);
+ if (e) {
+ var e2 = e.parentNode;
+
+ if (c[1] != 0) {
+ set_rollup_state(e,e2,'shown');
+ }
+ else {
+ set_rollup_state(e,e2,'hidden');
+ }
+ }
+ }
+ }
+}
diff --git a/rt/html/NoAuth/js/util.js b/rt/html/NoAuth/js/util.js
new file mode 100644
index 0000000..0de071a
--- /dev/null
+++ b/rt/html/NoAuth/js/util.js
@@ -0,0 +1,250 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+/* $(...)
+ Returns DOM node or array of nodes (if more then one argument passed).
+ If argument is node object allready then do nothing.
+ // Stolen from Prototype
+*/
+function $() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+
+/* Visibility */
+
+function show(id) { delClass( id, 'hidden' ) }
+function hide(id) { addClass( id, 'hidden' ) }
+
+function hideshow(id) { return toggleVisibility( id ) }
+function toggleVisibility(id) {
+ var e = $(id);
+
+ if ( e.className.match( /\bhidden\b/ ) )
+ show(e);
+ else
+ hide(e);
+
+ return false;
+}
+
+function setVisibility(id, visibility) {
+ if ( visibility ) show(id);
+ else hide(id);
+}
+
+function switchVisibility(id1, id2) {
+ // Show both and then hide the one we want
+ show(id1);
+ show(id2);
+ hide(id2);
+ return false;
+}
+
+/* Classes */
+
+function addClass(id, value) {
+ var e = $(id);
+ if ( e.className.match( new RegExp('\b'+ value +'\b') ) )
+ return;
+ e.className += e.className? ' '+value : value;
+}
+
+function delClass(id, value) {
+ var e = $(id);
+ e.className = e.className.replace( new RegExp('\\s?\\b'+ value +'\\b', 'g'), '' );
+}
+
+/* Rollups */
+
+function rollup(id) {
+ var e = $(id);
+ var e2 = e.parentNode;
+
+ if (e.className.match(/\bhidden\b/)) {
+ set_rollup_state(e,e2,'shown');
+ createCookie(id,1,365);
+ }
+ else {
+ set_rollup_state(e,e2,'hidden');
+ createCookie(id,0,365);
+ }
+ return false;
+}
+
+function set_rollup_state(e,e2,state) {
+ if (e && e2) {
+ if (state == 'shown') {
+ show(e);
+ delClass( e2, 'rolled-up' );
+ }
+ else if (state == 'hidden') {
+ hide(e);
+ addClass( e2, 'rolled-up' );
+ }
+ }
+}
+
+
+/* onload handlers */
+
+var onLoadStack = new Array();
+var onLoadLastStack = new Array();
+var onLoadExecuted = 0;
+
+function onLoadHook(commandStr) {
+ if(typeof(commandStr) == "string") {
+ onLoadStack[ onLoadStack.length ] = commandStr;
+ return true;
+ }
+ return false;
+}
+
+// some things *really* need to be done after everything else
+function onLoadLastHook(commandStr) {
+ if(typeof(commandStr) == "string"){
+ onLoadLastStack[onLoadLastStack.length] = commandStr;
+ return true;
+ }
+ return false;
+}
+
+function doOnLoadHooks() {
+ if(onLoadExecuted) return;
+
+ var i;
+ for ( i in onLoadStack ) {
+ eval( onLoadStack[i] );
+ }
+ for ( i in onLoadLastStack ) {
+ eval( onLoadLastStack[i] );
+ }
+ onLoadExecuted = 1;
+}
+
+window.onload = doOnLoadHooks;
+
+/* calendar functions */
+
+function openCalWindow(field) {
+ var objWindow = window.open('<%$RT::WebPath%>/Helpers/CalPopup.html?field='+field,
+ 'RT_Calendar',
+ 'height=235,width=285,scrollbars=1');
+ objWindow.focus();
+}
+
+function createCalendarLink(input) {
+ var e = $(input);
+ if (e) {
+ var link = document.createElement('a');
+ link.setAttribute('href', '#');
+
+ clickevent = function clickevent(e) { openCalWindow(input); return false; };
+ if (! addEvent(link, "click", clickevent)) {
+ return false;
+ }
+
+ var text = document.createTextNode('<% loc("Choose a date") %>');
+ link.appendChild(text);
+
+ var space = document.createTextNode(' ');
+
+ e.parentNode.insertBefore(link, e.nextSibling);
+ e.parentNode.insertBefore(space, e.nextSibling);
+
+ return true;
+ }
+ return false;
+}
+
+/* other utils */
+
+function focusElementById(id) {
+ var e = $(id);
+ if (e) e.focus();
+}
+
+function updateParentField(field, value) {
+ if (window.opener) {
+ window.opener.$(field).value = value;
+ window.close();
+ }
+}
+
+function addEvent(obj, sType, fn) {
+ if (obj.addEventListener) {
+ obj.addEventListener(sType, fn, false);
+ } else if (obj.attachEvent) {
+ var r = obj.attachEvent("on"+sType, fn);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+function setCheckbox(form, name, val) {
+ var myfield = form.getElementsByTagName('input');
+ for ( var i = 0; i < myfield.length; i++ ) {
+ if ( name && myfield[i].name != name ) continue;
+ if ( myfield[i].type != 'checkbox' ) continue;
+
+ myfield[i].checked = val;
+ }
+}
+
diff --git a/rt/html/Prefs/Elements/Tabs b/rt/html/Prefs/Elements/Tabs
new file mode 100644
index 0000000..e706d9a
--- /dev/null
+++ b/rt/html/Prefs/Elements/Tabs
@@ -0,0 +1,72 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /User/Elements/Tabs,
+ subtabs => $tabs,
+ current_tab => 'Prefs/MyRT.html',
+ current_subtab => $current_subtab,
+ Title => $Title &>
+
+<%INIT>
+my $tabs;
+unless ($Searches) {
+ $Searches = [$m->comp("/Search/Elements/SearchesForObject", Object => RT::System->new($session{'CurrentUser'}))];
+}
+
+$tabs->{a} = { title => loc('Quick search'),
+ path => 'Prefs/Quicksearch.html' };
+for my $search (@$Searches) {
+ $tabs->{$search->[0]} = { title => $search->[0],
+ path => "Prefs/Search.html?".$m->comp('/Elements/QueryString', name => ref($search->[1]).'-'.$search->[1]->Id) };
+}
+</%INIT>
+<%ARGS>
+$GroupObj => undef
+$current_subtab => undef
+$Title => undef
+$Searches => undef
+</%ARGS>
diff --git a/rt/html/Prefs/MyRT.html b/rt/html/Prefs/MyRT.html
new file mode 100644
index 0000000..ba35fd2
--- /dev/null
+++ b/rt/html/Prefs/MyRT.html
@@ -0,0 +1,151 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Prefs/Elements/Tabs,
+ current_tab => 'Prefs/MyRT.html',
+ Title => $title,
+ Searches => \@sys_searches
+&>
+
+<& /Widgets/SelectionBox:header, nojs => 1 &>
+
+<& /Elements/ListActions, actions => \@actions &>
+<br />
+
+<form method="post" action="MyRT.html">
+<input type="hidden" name="Reset" value="1" />
+<input type="submit" class="button" value="<%loc('Reset to default')%>">
+</form>
+
+<br />
+
+% for my $pane (@panes) {
+<&|/Widgets/TitleBox, title => loc('RT at a glance').': '.loc($pane->{Name}), bodyclass => "" &>
+<& /Widgets/SelectionBox:show, self => $pane, nojs => 1 &></&>
+<br />
+% }
+<&|/Widgets/TitleBox, title => loc('Options'), bodyclass => "" &>
+<form method="post" action="MyRT.html">
+ <&|/l&>Rows per box</&>:<input name="SummaryRows" value="<% $ARGS{SummaryRows} %>" /> <input type="submit" class="button" value="<%loc('Save')%>" />
+</form>
+</&>
+<%INIT>
+my @actions;
+
+my $title = loc("Customize").' '.loc("RT at a glance");
+my $user = $session{'CurrentUser'}->UserObj;
+
+if ($ARGS{Reset}) {
+ $user->SetPreferences('HomepageSettings', {});
+ delete $session{'my_rt_portlets'};
+}
+
+unless (exists $session{'my_rt_portlets'}) {
+ my ($default_portlets) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
+ my $portlets = $default_portlets ? $default_portlets->Content : {};
+ $session{'my_rt_portlets'} = $user->Preferences('HomepageSettings', $portlets);
+}
+if ($ARGS{SummaryRows}) {
+ $user->SetPreferences('SummaryRows', $ARGS{SummaryRows});
+ push @actions, loc ('Preferences saved for [_1].', loc('summary rows'));
+}
+else {
+ $ARGS{SummaryRows} = $user->Preferences('SummaryRows', $RT::DefaultSummaryRows);
+}
+
+
+my $portlets = $session{'my_rt_portlets'};
+
+my %allowed_components = map {$_ => 1} @{$RT::HomepageComponents};
+my @items;
+
+push @items, map {["component-$_", $_]} sort keys %allowed_components;
+
+my $sys = RT::System->new($session{'CurrentUser'});
+my @objs = ($sys);
+
+push @objs, RT::SavedSearches->new( $session{CurrentUser} )->_PrivacyObjects
+ if $session{'CurrentUser'}->HasRight( Right => 'LoadSavedSearch',
+ Object => $RT::System );
+
+my @sys_searches;
+for my $object (@objs) {
+ for ($m->comp("/Search/Elements/SearchesForObject", Object => $object)) {
+ my ($desc, $search) = @$_;
+ my $SearchType = $search->Content->{'SearchType'} || 'Ticket';
+ if ($object eq $sys && $SearchType eq 'Ticket') {
+ push @items, ["system-$desc", $desc];
+ push @sys_searches, [$desc, $search];
+ }
+ else {
+ my $oid = ref($object).'-'.$object->Id.'-SavedSearch-'.$search->Id;
+ my $type = ($SearchType eq 'Ticket')
+ ? 'Saved Search' : $SearchType; # loc
+ push @items, ["saved-$oid", loc($type).": $desc"];
+ }
+ }
+}
+
+my @panes = $m->comp(
+ '/Admin/Elements/ConfigureMyRT',
+ panes => ['body', 'summary'],
+ Action => 'MyRT.html',
+ items => \@items,
+ current_portlets => $portlets,
+ OnSave => sub {
+ my ( $conf, $pane ) = @_;
+ $user->SetPreferences( 'HomepageSettings', $conf );
+ push @actions, loc( 'Preferences saved for [_1].', $pane );
+ delete $session{'my_rt_portlets'};
+ }
+);
+
+$m->comp( '/Widgets/SelectionBox:process', %ARGS, self => $_, nojs => 1 )
+ for @panes;
+
+</%INIT>
diff --git a/rt/html/Prefs/Quicksearch.html b/rt/html/Prefs/Quicksearch.html
new file mode 100644
index 0000000..f4becc7
--- /dev/null
+++ b/rt/html/Prefs/Quicksearch.html
@@ -0,0 +1,96 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Prefs/Elements/Tabs,
+ current_tab => 'Prefs/MyRT.html',
+ current_subtab => 'Prefs/Quicksearch.html',
+ Title => $title
+&>
+<& /Elements/ListActions, actions => \@actions &>
+<h1><&|/l&>Select queues to be displayed on the "RT at a glance" page</&></h1>
+<form method="post" action="Quicksearch.html" name="Preferences">
+<ul>
+% for my $queue (@queues) {
+<li><input type="checkbox" class="checkbox" name="Want-<%$queue->Name%>" value="1"
+% unless ($unwanted->{$queue->Name}) {
+checked
+% }
+/><%$queue->Name%>: <%$queue->Description%></li>
+% }
+</ul>
+<& /Elements/Submit, Caption => loc("Save Changes"), Label => loc('Save'), Name => 'Save'&>
+
+</form>
+
+<%INIT>
+my @actions;
+my $title = loc("Customize").' '.loc("Quick search");
+# The queue list is not loaded from cache, so it might be a bit inconsistent
+my $user = $session{'CurrentUser'}->UserObj;
+my $unwanted = $user->Preferences('QuickSearch', {});
+my $Queues = RT::Queues->new($session{'CurrentUser'});
+$Queues->UnLimit;
+my @queues = grep {$_->CurrentUserHasRight('ShowTicket')} @{$Queues->ItemsArrayRef};
+
+if ($ARGS{'Save'}) {
+ for my $queue (@queues) {
+ if ($ARGS{"Want-".$queue->Name}) {
+ delete $unwanted->{$queue->Name};
+ }
+ else {
+ ++$unwanted->{$queue->Name};
+ }
+ }
+
+ $user->SetPreferences('QuickSearch', $unwanted);
+ push @actions, loc ('Preferences saved.');
+ # Let QueueSummary rebuild the cache
+ delete $session{'quick_search_queues'};
+}
+
+</%INIT>
diff --git a/rt/html/Prefs/Search.html b/rt/html/Prefs/Search.html
new file mode 100644
index 0000000..8754d53
--- /dev/null
+++ b/rt/html/Prefs/Search.html
@@ -0,0 +1,108 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Prefs/Elements/Tabs,
+ current_tab => 'Prefs/MyRT.html',
+# current_subtab => 'Prefs/Search.html?name='.$m->comp('/Elements/QueryString', name => $ARGS{name}),
+ current_subtab => 'Prefs/Search.html?name='.$ARGS{name},
+ Title => $title
+&>
+<& /Elements/ListActions, actions => \@actions &>
+% if ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'SuperUser')) {
+<p>
+ <&|/l&>You can also edit the predefined search itself</&>:
+ <a href="<% $RT::WebPath.'/Search/Build.html?'.
+ $m->comp('/Elements/QueryString',
+ LoadSavedSearch => 'RT::System-1-SavedSearch-'.$id) %>"><% $search->Name %></a>
+</p>
+% }
+
+<form method="post" action="Search.html" name="BuildQuery">
+<input type="hidden" name="name" value="<%$ARGS{name}%>" class="hidden" />
+<input type="hidden" name="Format" value="<%$ARGS{Format}%>" class="hidden" />
+
+<& /Search/Elements/DisplayOptions, %$SearchArg, %ARGS,
+ AvailableColumns => $AvailableColumns, CurrentFormat => $CurrentFormat &>
+<& /Elements/Submit, Caption => loc("Save"), Label => loc('Save'), Name => 'Save'&>
+
+</form>
+
+<%INIT>
+my @actions;
+my $title = loc("Customize").' ';
+
+my @fields = qw(Format Order OrderBy RowsPerPage);
+my ($class, $id) = ( $ARGS{name} =~ m/^(.*)-(\d+)$/ );
+
+Abort('No search specified')
+ unless $class eq 'RT::Attribute';
+
+my $search = $class->new ($session{'CurrentUser'});
+$search->LoadById ($id);
+$title .= loc ($search->Description, loc ('"N"'));
+my $user = $session{'CurrentUser'}->UserObj;
+my $SearchArg = $user->Preferences($search, $search->Content);
+for (@fields) {
+ $ARGS{$_} = $SearchArg->{$_} unless defined $ARGS{$_};
+}
+$ARGS{'Order'} = join '|', grep defined && /\S/, (ref $ARGS{'Order'})? @{$ARGS{'Order'}}: $ARGS{'Order'};
+$ARGS{'OrderBy'} = join '|', grep defined && /\S/, (ref $ARGS{'OrderBy'})? @{$ARGS{'OrderBy'}}: $ARGS{'OrderBy'};
+
+my ( $AvailableColumns, $CurrentFormat );
+( $ARGS{Format}, $AvailableColumns, $CurrentFormat ) = $m->comp(
+ '/Search/Elements/BuildFormatString',
+ cfqueues => {}, %ARGS
+);
+
+if ($ARGS{'Save'}) {
+ my $hash = {map { $_ => $ARGS{$_}} @fields};
+ my $pref = $user->SetPreferences ($search, $hash);
+ push @actions, loc ('Preferences saved.');
+}
+
+</%INIT>
diff --git a/rt/html/Prefs/SearchOptions.html b/rt/html/Prefs/SearchOptions.html
new file mode 100644
index 0000000..7cc71b0
--- /dev/null
+++ b/rt/html/Prefs/SearchOptions.html
@@ -0,0 +1,114 @@
+
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Search Preferences") &>
+<& /User/Elements/Tabs,
+ current_tab => "Prefs/SearchOptions.html",
+ Title => loc("Search Preferences")
+&>
+
+<form method="post" action="SearchOptions.html">
+<input type="hidden" class="hidden" name="Format" value="<%$Format%>" />
+ <& /Search/Elements/DisplayOptions, %ARGS,
+ Format=> $Format,
+ AvailableColumns => $AvailableColumns,
+ CurrentFormat => $CurrentFormat,
+ RowsPerPage => $RowsPerPage,
+ OrderBy => $OrderBy,
+ Order => $Order &>
+
+<& /Elements/Submit, Name => 'SavePreferences', Label => loc('Save Changes') &>
+</form>
+
+<%INIT>
+
+# {{{ If we're saving search preferences, do that now
+$Order = join '|', grep defined && /\S/, (ref $Order)? @{$Order}: $Order;
+$OrderBy = join '|', grep defined && /\S/, (ref $OrderBy)? @{$OrderBy}: $OrderBy;
+
+if ($ARGS{'SavePreferences'}) {
+ $session{'CurrentUser'}->UserObj->SetPreferences("SearchDisplay",
+ {
+ Format => $Format,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ RowsPerPage => $RowsPerPage,
+ });
+}
+
+# }}}
+
+
+
+
+
+
+
+# Read from user preferences
+my $prefs = $session{'CurrentUser'}->UserObj->Preferences("SearchDisplay") || {};
+
+$Format ||= $prefs->{'Format'};
+$Order ||= $prefs->{'Order'} || 'ASC';
+$OrderBy ||= $prefs->{'OrderBy'} || 'id';
+($RowsPerPage = defined( $prefs->{'RowsPerPage'} ) ? $prefs->{'RowsPerPage'} : 50) unless defined ($RowsPerPage);
+
+my ( $AvailableColumns, $CurrentFormat );
+( $Format, $AvailableColumns, $CurrentFormat ) = $m->comp(
+ '/Search/Elements/BuildFormatString',
+ %ARGS, Format => $Format
+);
+</%INIT>
+
+<%ARGS>
+$Format => undef
+$Description => undef
+$Order => undef
+$OrderBy => undef
+$RowsPerPage => undef
+</%ARGS>
+
diff --git a/rt/html/REST/1.0/Forms/queue/default b/rt/html/REST/1.0/Forms/queue/default
new file mode 100644
index 0000000..cfc4b52
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/queue/default
@@ -0,0 +1,170 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/queue/default
+%#
+<%ARGS>
+$id
+$format => 's'
+$changes => {}
+</%ARGS>
+<%perl>
+my @comments;
+my ($c, $o, $k, $e) = ("", [], {}, 0);
+my %data = %$changes;
+my $queue = new RT::Queue $session{CurrentUser};
+my @fields = qw(Name Description CorrespondAddress CommentAddress
+ InitialPriority FinalPriority DefaultDueIn);
+my %fields = map { lc $_ => $_ } @fields;
+
+if ($id ne 'new') {
+ $queue->Load($id);
+ if (!$queue->Id) {
+ return [ "# Queue $id does not exist.", [], {}, 1 ];
+ }
+}
+else {
+ if (%data == 0) {
+ return [
+ "# Required: Name",
+ [ "id", @fields ],
+ {
+ id => 'queue/new',
+ Name => '<queue name>',
+ Description => "",
+ CommentAddress => "",
+ CorrespondAddress => "",
+ InitialPriority => "",
+ FinalPriority => "",
+ DefaultDueIn => "",
+ },
+ 0
+ ];
+ }
+ else {
+ my %v;
+ my %create = %fields;
+
+ foreach my $k (keys %data) {
+ if (exists $create{lc $k}) {
+ $v{$create{lc $k}} = delete $data{$k};
+ }
+ }
+
+ if ($v{Name} eq '<queue name>') {
+ my %o = keys %$changes;
+ delete @o{"id", @fields};
+ return [
+ "# Please set the queue name.",
+ [ "id", @fields, keys %o ], $changes, 1
+ ];
+ }
+
+ $queue->Create(%v);
+ unless ($queue->Id) {
+ return [ "# Could not create queue.", [], {}, 1 ];
+ }
+
+ delete $data{id};
+ $id = $queue->Id;
+ push(@comments, "# Queue $id created.");
+ goto DONE if %data == 0;
+ }
+}
+
+if (%data == 0) {
+ my @data;
+
+ push @data, [ id => "queue/".$queue->Id ];
+ foreach my $key (@fields) {
+ push @data, [ $key => $queue->$key ];
+ }
+
+ my %k = map {@$_} @data;
+ $o = [ map {$_->[0]} @data ];
+ $k = \%k;
+}
+else {
+ my ($get, $set, $key, $val, $n, $s);
+
+ foreach $key (keys %data) {
+ $val = $data{$key};
+ $key = lc $key;
+ $n = 1;
+
+ if (exists $fields{$key}) {
+ $key = $fields{$key};
+ $set = "Set$key";
+
+ next if $val eq $queue->$key;
+ ($n, $s) = $queue->$set($val);
+ }
+ elsif ($key ne 'id') {
+ $n = 0;
+ $s = "Unknown field.";
+ }
+
+ SET:
+ if ($n == 0) {
+ $e = 1;
+ push @comments, "# $key: $s";
+ unless (@$o) {
+ my %o = keys %$changes;
+ delete @o{"id", @fields};
+ @$o = ("id", @fields, keys %o);
+ $k = $changes;
+ }
+ }
+ }
+
+ push(@comments, "# Queue $id updated.") unless $n == 0;
+}
+
+DONE:
+$c ||= join("\n", @comments) if @comments;
+return [ $c, $o, $k, $e ];
+</%perl>
diff --git a/rt/html/REST/1.0/Forms/queue/ns b/rt/html/REST/1.0/Forms/queue/ns
new file mode 100644
index 0000000..2273189
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/queue/ns
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/queue/ns
+%#
+<%ARGS>
+$id
+</%ARGS>
+<%perl>
+use RT::Queues;
+
+my $queues = new RT::Queues $session{CurrentUser};
+$queues->Limit(FIELD => 'Name', OPERATOR => '=', VALUE => $id);
+if ($queues->Count == 0) {
+ return (0, "No queue named $id exists.");
+}
+return $queues->Next->Id;
+</%perl>
diff --git a/rt/html/REST/1.0/Forms/ticket/attachments b/rt/html/REST/1.0/Forms/ticket/attachments
new file mode 100644
index 0000000..4ee409b
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/ticket/attachments
@@ -0,0 +1,135 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/ticket/attachments
+%#
+<%ARGS>
+$id
+$args => undef
+</%ARGS>
+<%INIT>
+my @data;
+my ($c, $o, $k, $e) = ("", [], {}, "");
+my $ticket = new RT::Ticket $session{CurrentUser};
+
+$ticket->Load($id);
+unless ($ticket->Id) {
+ return [ "# Ticket $id does not exist.", [], {}, 1 ];
+}
+
+my @arglist = split('/', $args);
+my ($aid, $content);
+
+if ($arglist[1] eq 'content') {
+ $aid = $arglist[0];
+ $content = 1;
+} else {
+ $aid = $args;
+ $content = 0;
+}
+
+if ($aid) {
+ unless ($aid =~ /^\d+$/) {
+ return [ "# Invalid attachment id: $aid", [], {}, 1 ];
+ }
+ my $attachment = new RT::Attachment $session{CurrentUser};
+ $attachment->Load($aid);
+ unless ($attachment->Id eq $aid) {
+ return [ "# Invalid attachment id: $aid", [], {}, 1 ];
+ }
+ if ($content) {
+ $c = $attachment->OriginalContent;
+ # if we're sending a binary attachment (and only the attachment)
+ # flag it so bin/rt knows to special case it
+ if ($attachment->ContentType !~ /^text\//) {
+ $r->content_type($attachment->ContentType);
+ }
+ } else {
+ my @data;
+ push @data, [ id => $attachment->Id ];
+ push @data, [ Subject => $attachment->Subject ];
+ push @data, [ Creator => $attachment->Creator ];
+ push @data, [ Created => $attachment->Created ];
+ push @data, [ Transaction => $attachment->TransactionId ];
+ push @data, [ Parent => $attachment->Parent ];
+ push @data, [ MessageId => $attachment->MessageId ];
+ push @data, [ Filename => $attachment->Filename ];
+ push @data, [ ContentType => $attachment->ContentType ];
+ push @data, [ ContentEncoding => $attachment->ContentEncoding ];
+ push @data, [ Headers => $attachment->Headers ];
+ push @data, [ Content => $attachment->Content ];
+
+ my %k = map {@$_} @data;
+ $o = [ map {$_->[0]} @data ];
+ $k = \%k;
+ }
+
+}
+else {
+ my @attachments;
+ my $transactions = $ticket->Transactions;
+ while (my $t = $transactions->Next) {
+ my $attachments = $t->Attachments;
+ while (my $a = $attachments->Next) {
+ my $size = length($a->Content);
+ if ($size > 1024) { $size = int($size/102.4)/10 . "k" }
+ else { $size .= "b" }
+ push @attachments, $a->Id.": ".$a->Filename." (".$a->ContentType . " / ".$size.")";
+ }
+ }
+
+ if (@attachments) {
+ $o = [ "id", "Attachments" ];
+ $k = {
+ id => "ticket/".$ticket->Id."/attachments",
+ Attachments => \@attachments
+ };
+ }
+}
+
+return [ $c, $o, $k, $e ];
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/ticket/comment b/rt/html/REST/1.0/Forms/ticket/comment
new file mode 100755
index 0000000..4ed2da8
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/ticket/comment
@@ -0,0 +1,152 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/ticket/comment
+%#
+<%ARGS>
+$id
+%changes
+</%ARGS>
+<%INIT>
+use MIME::Entity;
+use LWP::MediaTypes;
+use RT::Interface::REST;
+use File::Temp qw(tempfile);
+
+$RT::Logger->debug("Got ticket id=$id for comment");
+$RT::Logger->debug("Got args @{[keys(%changes)]}.");
+
+my $ticket = new RT::Ticket $session{CurrentUser};
+my ($c, $o, $k, $e) = ("", [], {}, 0);
+
+# http://.../REST/1.0/ticket/1/comment
+$ticket->Load($id);
+if (!$ticket->Id) {
+ $e = 1;
+ $c = "# Ticket $id does not exist.";
+ goto OUTPUT;
+}
+
+my $action;
+($action = $changes{Action}) =~ s/^(.)(.*)$/\U$1\L$2\E/;
+unless ($action =~ /^(?:Comment|Correspond)$/) {
+ $e = 1;
+ $c = "# Invalid action: `$action'.";
+ goto OUTPUT;
+}
+
+my $text = $changes{Text};
+my @atts = @{ vsplit($changes{Attachment}) };
+
+if (!$changes{Text} && @atts == 0) {
+ $e = 1;
+ $c = "# Empty comment with no attachments submitted.";
+ goto OUTPUT;
+}
+
+my $cgi = $m->cgi_object;
+my $ent = MIME::Entity->build(Type => "multipart/mixed");
+$ent->attach(Data => $changes{Text}) if $changes{Text};
+
+my $i = 1;
+foreach my $att (@atts) {
+ local $/=undef;
+ my $file = $att;
+ $file =~ s#^.*[\\/]##;
+
+ my $fh = $cgi->upload("attachment_$i");
+ if ($fh) {
+ my $buf;
+ my ($w, $tmp) = tempfile();
+ my $info = $cgi->uploadInfo();
+
+ while (sysread($fh, $buf, 8192)) {
+ syswrite($w, $buf);
+ }
+
+ $ent->attach(
+ Path => $tmp,
+ Type => $info->{'Content-Type'} || guess_media_type($tmp),
+ Filename => $file,
+ Disposition => "attachment"
+ );
+ }
+ else {
+ $e = 1;
+ $c = "# No attachment for $att.";
+ goto OUTPUT;
+ }
+
+ $i++;
+}
+
+unless ($ticket->CurrentUserHasRight('ModifyTicket') ||
+ ($action eq "Comment" &&
+ $ticket->CurrentUserHasRight("CommentOnTicket")) ||
+ ($action eq "Correspond" &&
+ $ticket->CurrentUserHasRight("ReplyToTicket")))
+{
+ $e = 1;
+ $c = "# You are not allowed to $action on ticket $id.";
+ goto OUTPUT;
+}
+
+my $cc = join ", ", @{ vsplit($changes{Cc}) };
+my $bcc = join ", ", @{ vsplit($changes{Bcc}) };
+my ($n, $s) = $ticket->$action(MIMEObj => $ent,
+ CcMessageTo => $cc,
+ BccMessageTo => $bcc,
+ TimeTaken => $changes{TimeWorked} || 0);
+$c = "# ".$s;
+if ($changes{Status}) {
+ my ($status_n, $status_s) = $ticket->SetStatus($changes{'Status'} );
+ $c .= "\n# ".$status_s;
+}
+
+OUTPUT:
+return [ $c, $o, $k, $e ];
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/ticket/default b/rt/html/REST/1.0/Forms/ticket/default
new file mode 100644
index 0000000..2bfc43d
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/ticket/default
@@ -0,0 +1,345 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/ticket/default
+%#
+<%ARGS>
+$id
+$changes => {}
+$fields => undef
+$args => undef
+</%ARGS>
+<%INIT>
+use MIME::Entity;
+
+my @comments;
+my ($c, $o, $k, $e) = ("", [], {}, 0);
+my %data = %$changes;
+my $ticket = new RT::Ticket $session{CurrentUser};
+my @dates = qw(Created Starts Started Due Resolved Told LastUpdated);
+my @people = qw(Requestors Cc AdminCc);
+my @create = qw(Queue Requestor Subject Cc AdminCc Owner Status Priority
+ InitialPriority FinalPriority TimeEstimated TimeWorked
+ TimeLeft Starts Started Due Resolved);
+my @simple = qw(Subject Status Priority Disabled TimeEstimated TimeWorked
+ TimeLeft InitialPriority FinalPriority);
+my %dates = map {lc $_ => $_} @dates;
+my %people = map {lc $_ => $_} @people;
+my %create = map {lc $_ => $_} @create;
+my %simple = map {lc $_ => $_} @simple;
+
+# Are we dealing with an existing ticket?
+if ($id ne 'new') {
+ $ticket->Load($id);
+ if (!$ticket->Id) {
+ return [ "# Ticket $id does not exist.", [], {}, 1 ];
+ }
+ elsif (!$ticket->CurrentUserHasRight('ShowTicket') ||
+ (%data && !$ticket->CurrentUserHasRight('ModifyTicket')))
+ {
+ my $act = %data ? "modify" : "display";
+ return [ "# You are not allowed to $act ticket $id.", [], {}, 1 ];
+ }
+}
+else {
+ if (!keys(%data)) {
+ # GET ticket/new: Return a suitable default form.
+ # We get defaults from queue/1 (XXX: What if it isn't there?).
+ my $due = new RT::Date $session{CurrentUser};
+ my $queue = new RT::Queue $session{CurrentUser};
+ my $starts = new RT::Date $session{CurrentUser};
+ $queue->Load(1);
+ $due->SetToNow;
+ $due->AddDays($queue->DefaultDueIn) if $queue->DefaultDueIn;
+ $starts->SetToNow;
+
+ return [
+ "# Required: id, Queue",
+ [ qw(id Queue Requestor Subject Cc AdminCc Owner Status Priority
+ InitialPriority FinalPriority TimeEstimated Starts Due Text) ],
+ {
+ id => "ticket/new",
+ Queue => $queue->Name,
+ Requestor => $session{CurrentUser}->Name,
+ Subject => "",
+ Cc => [],
+ AdminCc => [],
+ Owner => "",
+ Status => "new",
+ Priority => $queue->InitialPriority,
+ InitialPriority => $queue->InitialPriority,
+ FinalPriority => $queue->FinalPriority,
+ TimeEstimated => 0,
+ Starts => $starts->ISO,
+ Due => $due->ISO,
+ Text => "",
+ },
+ 0
+ ];
+ }
+ else {
+ # We'll create a new ticket, and fall through to set fields that
+ # can't be set in the call to Create().
+ my (%v, $text);
+
+ foreach my $k (keys %data) {
+ if (exists $create{lc $k}) {
+ $v{$create{lc $k}} = delete $data{$k};
+ }
+ # Set custom field
+ elsif ($k =~ /^CF-/i) {
+ my $cf = RT::CustomField->new( $RT::SystemUser );
+ my $cfk = $k;
+ $cfk =~ s/^CF-//i;
+ unless($cf->LoadByName( Name => $cfk )) {
+ push @comments, "# Invalid custom field name ($cfk)";
+ delete $data{$k};
+ next;
+ }
+ $v{"CustomField-".$cf->Id()} = delete $data{$k};
+ }
+ elsif (lc $k eq 'text') {
+ $text = delete $data{$k};
+ }
+ }
+
+ # people fields allow multiple values
+ $v{$_} = vsplit($v{$_}) foreach ( grep $create{lc $_}, @people );
+
+ if ($text) {
+ $v{MIMEObj} =
+ MIME::Entity->build(
+ From => $session{CurrentUser}->EmailAddress,
+ Subject => $v{Subject},
+ Data => $text
+ );
+ }
+
+ my($tid,$trid,$terr) = $ticket->Create(%v);
+ unless ($tid) {
+ push(@comments, "# Could not create ticket.");
+ push(@comments, "# " . $terr);
+ goto DONE;
+ }
+
+ delete $data{id};
+ $id = $ticket->Id;
+ push(@comments, "# Ticket $id created.");
+ # see if the hash is empty
+ goto DONE if ! keys(%data);
+ }
+}
+
+# Now we know we're dealing with an existing ticket.
+if (!keys(%data)) {
+ my ($time, $key, $val, @data);
+
+ push @data, [ id => "ticket/".$ticket->Id ];
+ push @data, [ Queue => $ticket->QueueObj->Name ]
+ if (!%$fields || exists $fields->{lc 'Queue'});
+ push @data, [ Owner => $ticket->OwnerObj->Name ]
+ if (!%$fields || exists $fields->{lc 'Owner'});
+ push @data, [ Creator => $ticket->CreatorObj->Name ]
+ if (!%$fields || exists $fields->{lc 'Creator'});
+
+ foreach (qw(Subject Status Priority InitialPriority FinalPriority)) {
+ next unless (!%$fields || (exists $fields->{lc $_}));
+ push @data, [$_ => $ticket->$_ ];
+ }
+
+ foreach $key (@people) {
+ next unless (!%$fields || (exists $fields->{lc $key}));
+ push @data, [ $key => [ $ticket->$key->MemberEmailAddresses ] ];
+ }
+
+ $time = new RT::Date ($session{CurrentUser});
+ foreach $key (@dates) {
+ next unless (!%$fields || (exists $fields->{lc $key}));
+ $time->Set(Format => 'sql', Value => $ticket->$key);
+ push @data, [ $key => $time->AsString ];
+ }
+
+ $time = new RT::Date ($session{CurrentUser});
+ foreach $key (qw(TimeEstimated TimeWorked TimeLeft)) {
+ next unless (!%$fields || (exists $fields->{lc $key}));
+ $val = $ticket->$key || 0;
+ $val = "$val minutes" if $val;
+ push @data, [ $key => $val ];
+ }
+
+ # Display custom fields
+ my $CustomFields = $ticket->QueueObj->TicketCustomFields();
+ while (my $cf = $CustomFields->Next()) {
+ next unless (!%$fields || (exists $fields->{"cf-".lc $cf->Name}));
+ my $vals = $ticket->CustomFieldValues($cf->Id());
+ my @out = ();
+ while (my $v = $vals->Next()) {
+ push @out, $v->Content;
+ }
+ push @data, [ 'CF-' . $cf->Name => join ',', @out ];
+ }
+
+ my %k = map {@$_} @data;
+ $o = [ map {$_->[0]} @data ];
+ $k = \%k;
+}
+else {
+ my ($get, $set, $key, $val, $n, $s);
+
+ foreach $key (keys %data) {
+ $val = $data{$key};
+ $key = lc $key;
+ $n = 1;
+
+ if (ref $val eq 'ARRAY') {
+ unless ($key =~ /^(?:Requestors|Cc|AdminCc)$/i) {
+ $n = 0;
+ $s = "$key may have only one value.";
+ goto SET;
+ }
+ }
+
+ if ($key =~ /^queue$/i) {
+ next if $val eq $ticket->QueueObj->Name;
+ ($n, $s) = $ticket->SetQueue($val);
+ }
+ elsif ($key =~ /^owner$/i) {
+ next if $val eq $ticket->OwnerObj->Name;
+ ($n, $s) = $ticket->SetOwner($val);
+ }
+ elsif (exists $simple{$key}) {
+ $key = $simple{$key};
+ $set = "Set$key";
+
+ next if (($val eq $ticket->$key)|| ($ticket->$key =~ /^\d+$/ && $val == $ticket->$key));
+ ($n, $s) = $ticket->$set("$val");
+ }
+ elsif (exists $dates{$key}) {
+ $key = $dates{$key};
+ $set = "Set$key";
+
+ my $time = new RT::Date $session{CurrentUser};
+ $time->Set(Format => 'sql', Value => $ticket->$key);
+ next if ($val =~ /^not set$/i || $val eq $time->AsString);
+ ($n, $s) = $ticket->$set($val);
+ }
+ elsif (exists $people{$key}) {
+ $key = $people{$key};
+ my ($p, @msgs);
+
+ my %new = map {$_=>1} @{ vsplit($val) };
+ my %old = map {$_=>1} $ticket->$key->MemberEmailAddresses;
+ my $type = $key eq 'Requestors' ? 'Requestor' : $key;
+
+ foreach $p (keys %old) {
+ unless (exists $new{$p}) {
+ ($s, $n) = $ticket->DeleteWatcher(Type => $type,
+ Email => $p);
+ push @msgs, [ $s, $n ];
+ }
+ }
+ foreach $p (keys %new) {
+ # XXX: This is a stupid test.
+ unless ($p =~ /^[\w.+-]+\@([\w.-]+\.)*\w+.?$/) {
+ $s = 0;
+ $n = "$p is not a valid email address.";
+ push @msgs, [ $s, $n ];
+ next;
+ }
+ unless ($ticket->IsWatcher(Type => $type, Email => $p)) {
+ ($s, $n) = $ticket->AddWatcher(Type => $type,
+ Email => $p);
+ push @msgs, [ $s, $n ];
+ }
+ }
+
+ $n = 1;
+ if (@msgs = grep {$_->[0] == 0} @msgs) {
+ $n = 0;
+ $s = join "\n", map {"# ".$_->[1]} @msgs;
+ $s =~ s/^# //;
+ }
+ }
+ # Set custom field
+ elsif ($key =~ /^CF-/i) {
+ my $cf = RT::CustomField->new( $RT::SystemUser );
+ $key =~ s/^CF-//i;
+ if (not $cf->LoadByName( Name => $key )) {
+ $n = 0;
+ $s = "Unknown custom field.";
+ }
+ else {
+ ($n, $s) = $ticket->AddCustomFieldValue(
+ Field => $cf, Value => $val );
+ $s =~ s/^# // if defined $s;
+ }
+ }
+ elsif ($key ne 'id' && $key ne 'type' && $key ne 'creator') {
+ $n = 0;
+ $s = "Unknown field.";
+ }
+
+ SET:
+ if ($n == 0) {
+ $e = 1;
+ push @comments, "# $key: $s";
+ unless (@$o) {
+ my %o = keys %$changes;
+ delete $o{id};
+ @$o = ("id", keys %o);
+ $k = $changes;
+ }
+ }
+ }
+ push(@comments, "# Ticket ".$ticket->id." updated.") unless $n == 0;
+}
+
+DONE:
+$c ||= join("\n", @comments) if @comments;
+return [$c, $o, $k, $e];
+
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/ticket/history b/rt/html/REST/1.0/Forms/ticket/history
new file mode 100644
index 0000000..5dd7417
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/ticket/history
@@ -0,0 +1,200 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/ticket/history
+%#
+<%ARGS>
+$id
+$args => undef
+$format => undef
+$fields => undef
+</%ARGS>
+<%INIT>
+my $ticket = new RT::Ticket $session{CurrentUser};
+my ($c, $o, $k, $e) = ("", [], {}, "");
+
+$ticket->Load($id);
+unless ($ticket->Id) {
+ return [ "# Ticket $id does not exist.", [], {}, 1 ];
+}
+
+my $trans = $ticket->Transactions();
+my $total = $trans->Count();
+
+chomp $args;
+my @arglist = split('/', $args);
+my ($type, $tid);
+
+if ($arglist[0] eq 'type') {
+ $type = $arglist[1];
+} elsif ($arglist[0] eq 'id') {
+ $tid = $arglist[1];
+} else {
+ $type = $args;
+}
+
+if ($type) {
+ # Create, Set, Status, Correspond, Comment, Give, Steal, Take, Told
+ # CustomField, AddLink, DeleteLink, AddWatcher, DelWatcher
+ if ($args =~ /^links?$/) {
+ $trans->Limit(FIELD => 'Type', OPERATOR => 'LIKE', VALUE => '%Link');
+ }
+ elsif ($args =~ /^watchers?$/) {
+ $trans->Limit(FIELD => 'Type', OPERATOR => 'LIKE', VALUE => '%Watcher');
+ }
+ else {
+ $trans->Limit(FIELD => 'Type', OPERATOR => '=', VALUE => $type);
+ }
+} elsif ($tid) {
+ $trans->Limit(FIELD => 'Id', OPERATOR => '=', VALUE => $tid);
+}
+
+if ($tid) {
+ my @data;
+ my $t = new RT::Transaction $session{CurrentUser};
+
+ # this paragraph limits the transaction ID query to transactions on this ticket.
+ # Otherwise you can query any transaction from any ticket, which makes no sense.
+ my $Transactions = $ticket->Transactions;
+ my $tok=0;
+ while (my $T = $Transactions->Next()) {
+ $tok=1 if ($T->Id == $tid)
+ }
+ if ($tok) {
+ $t->Load($tid);
+ } else {
+ return [ "# Transaction $tid is not related to Ticket $id", [], {}, 1 ];
+ }
+
+ push @data, [ id => $t->Id ];
+ push @data, [ Ticket => $t->Ticket ]
+ if (!%$fields || exists $fields->{lc 'Ticket'});
+ push @data, [ TimeTaken => $t->TimeTaken ]
+ if (!%$fields || exists $fields->{lc 'TimeTaken'});
+ push @data, [ Type => $t->Type ]
+ if (!%$fields || exists $fields->{lc 'Type'});
+ push @data, [ Field => $t->Field ]
+ if (!%$fields || exists $fields->{lc 'Field'});
+ push @data, [ OldValue => $t->OldValue ]
+ if (!%$fields || exists $fields->{lc 'OldValue'});
+ push @data, [ NewValue => $t->NewValue ]
+ if (!%$fields || exists $fields->{lc 'NewValue'});
+ push @data, [ Data => $t->Data ]
+ if (!%$fields || exists $fields->{lc 'Data'});
+ push @data, [ Description => $t->Description ]
+ if (!%$fields || exists $fields->{lc 'Description'});
+ push @data, [ Content => $t->Content ]
+ if (!%$fields || exists $fields->{lc 'Content'});
+
+
+ if (!%$fields || exists $fields->{lc 'Content'}) {
+ my $creator = new RT::User $session{CurrentUser};
+ $creator->Load($t->Creator);
+ push @data, [ Creator => $creator->Name ];
+ }
+ push @data, [ Created => $t->Created ]
+ if (!%$fields || exists $fields->{lc 'Created'});
+
+ if (!%$fields || exists $fields->{lc 'Attachments'}) {
+ my $attachlist;
+ my $attachments = $t->Attachments;
+ while (my $a = $attachments->Next) {
+ my $size = length($a->Content);
+ if ($size > 1024) { $size = int($size/102.4)/10 . "k" }
+ else { $size .= "b" }
+ $attachlist .= "\n" . $a->Id.": ".($a->Filename || "untitled")." (".$size.")";
+ }
+
+ push @data, [Attachments => $attachlist];
+ }
+
+ my %k = map {@$_} @data;
+ $o = [ map {$_->[0]} @data ];
+ $k = \%k;
+
+} else {
+ my (@data, $tids);
+ $format ||= "s";
+ $format = "l" if (%$fields);
+
+ while (my $t = $trans->Next) {
+ my $tid = $t->Id;
+
+ if ($format eq "l") {
+ $tids .= "," if $tids;
+ $tids .= $tid;
+ } else {
+ push @$o, $tid;
+ $k->{$tid} = $t->Description;
+ }
+ }
+
+ if ($format eq "l") {
+ my @tid;
+ push @tid, "ticket/$id/history/id/$tids";
+ my $fieldstring;
+ foreach my $key (keys %$fields) {
+ $fieldstring .= "," if $fieldstring;
+ $fieldstring .= $key;
+ }
+ my ($content, $forms);
+ $m->subexec("/REST/1.0/show",
+ id => \@tid,
+ format => $format,
+ fields => $fieldstring);
+ return [ $c, $o, $k, $e ];
+ }
+}
+
+if (!$c) {
+ my $sub = $trans->Count();
+ $c = "# $sub/$total ($args/total)";
+}
+
+return [ $c, $o, $k, $e ];
+
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/ticket/links b/rt/html/REST/1.0/Forms/ticket/links
new file mode 100644
index 0000000..6b2ed22
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/ticket/links
@@ -0,0 +1,172 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/ticket/links
+%#
+<%ARGS>
+$id
+$format => 's'
+$changes => undef
+</%ARGS>
+<%INIT>
+my @data;
+my $ticket = new RT::Ticket $session{CurrentUser};
+
+$ticket->Load($id);
+if (!$ticket->Id) {
+ return [ "# Ticket $id does not exist.", [], {}, 1 ];
+}
+
+my ($c, $o, $k, $e) = ("", [], {}, 0);
+my @fields = qw(DependsOn DependedOnBy RefersTo ReferredToBy Members MemberOf);
+my %fields = map { lc $_ => $_ } @fields;
+
+my %lfields = (
+ Members => { Type => 'MemberOf', Mode => 'Base' },
+ ReferredToBy => { Type => 'RefersTo', Mode => 'Base' },
+ DependedOnBy => { Type => 'DependsOn', Mode => 'Base' },
+ MemberOf => { Type => 'MemberOf', Mode => 'Target' },
+ RefersTo => { Type => 'RefersTo', Mode => 'Target' },
+ DependsOn => { Type => 'DependsOn', Mode => 'Target' },
+);
+
+if ($changes) {
+ my ($get, $set, $key, $val, $n, $s);
+ my %data = %$changes;
+ my @comments;
+
+ foreach $key (keys %data) {
+ $val = $data{$key};
+ $key = lc $key;
+ $n = 1;
+
+ if (exists $fields{$key}) {
+ $key = $fields{$key};
+
+ my %old;
+ my $field = $lfields{$key}->{Mode};
+ while (my $link = $ticket->$key->Next) {
+ $old{$link->$field} = 1;
+ }
+
+ my %new;
+ foreach my $nkey (@{vsplit($val)}) {
+ if ($nkey =~ /^\d+$/) {
+ my $uri = new RT::URI $session{CurrentUser};
+ my $tick = new RT::Ticket $session{CurrentUser};
+ $tick->Load($nkey);
+ if ($tick->Id) {
+ $nkey = $uri->FromObject($tick);
+ }
+ else {
+ $n = 0;
+ $s = "Ticket $nkey does not exist.";
+ goto SET;
+ }
+ }
+ $new{$nkey} = 1;
+ }
+
+ foreach my $u (keys %old) {
+ if (exists $new{$u}) {
+ delete $new{$u};
+ }
+ else {
+ my $type = $lfields{$key}->{Type};
+ my $mode = $lfields{$key}->{Mode};
+ ($n, $s) = $ticket->DeleteLink(Type => $type, $mode => $u);
+ goto SET;
+ }
+ }
+ foreach my $u (keys %new) {
+ my $type = $lfields{$key}->{Type};
+ my $mode = $lfields{$key}->{Mode};
+ ($n, $s) = $ticket->AddLink(Type => $type, $mode => $u);
+ goto SET;
+ }
+ }
+ elsif ($key ne 'id' && $key ne 'type') {
+ $n = 0;
+ $s = "Unknown field: $key";
+ }
+
+ SET:
+ if ($n == 0) {
+ $e = 1;
+ push @comments, "# $key: $s";
+ unless (@$o) {
+ @$o = ("id", @fields);
+ %$k = %data;
+ }
+ }
+ }
+
+ push(@comments, "# Links for ticket $id updated.") unless @comments;
+ $c = join("\n", @comments) if @comments;
+}
+else {
+ my @data;
+
+ push @data, [ id => "ticket/".$ticket->Id."/links" ];
+ foreach my $key (@fields) {
+ my @val;
+
+ my $field = $lfields{$key}->{Mode};
+ while (my $link = $ticket->$key->Next) {
+ push @val, $link->$field;
+ }
+ push(@val, "") if (@val == 0 && $format eq 'l');
+ push @data, [ $key => [ @val ] ] if @val;
+ }
+
+ my %k = map {@$_} @data;
+ $o = [ map {$_->[0]} @data ];
+ $k = \%k;
+}
+
+return [ $c, $o, $k, $e ];
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/ticket/merge b/rt/html/REST/1.0/Forms/ticket/merge
new file mode 100755
index 0000000..69af6c8
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/ticket/merge
@@ -0,0 +1,96 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/ticket/merge
+%#
+<%ARGS>
+$id
+$args
+</%ARGS>
+<%INIT>
+use RT::Interface::REST;
+
+my $into = $args;
+
+my $ticket = new RT::Ticket $session{CurrentUser};
+my $ticket_into = new RT::Ticket $session{CurrentUser};
+my ($c, $o, $k, $e) = ("", [], {}, 0);
+
+# http://.../REST/1.0/ticket/1/merge/6 (merges ticket 1 into ticket 6)
+
+$ticket->Load($id);
+if (!$ticket->Id) {
+ $e = 1;
+ $c = "# Ticket $id does not exist.";
+ goto OUTPUT;
+}
+$ticket_into->Load($into);
+if (!$ticket_into->Id) {
+ $e = 1;
+ $c = "# Ticket $into does not exist.";
+ goto OUTPUT;
+}
+
+if (!$ticket->CurrentUserHasRight('ModifyTicket')) {
+ $e = 1;
+ $c = "# You are not allowed to modify ticket $id.";
+ goto OUTPUT;
+}
+
+my ($n, $s) = $ticket->MergeInto($into);
+
+if ($n == 0) {
+ $e = 1;
+ $c = "# Could not complete the merge.";
+}
+else {
+ $c = "# Merge completed.";
+}
+
+OUTPUT:
+return [ $c, $o, $k, $e ];
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/ticket/take b/rt/html/REST/1.0/Forms/ticket/take
new file mode 100755
index 0000000..35ee11f
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/ticket/take
@@ -0,0 +1,135 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/ticket/take
+%#
+<%ARGS>
+$id
+%changes
+</%ARGS>
+<%INIT>
+use RT::Interface::REST;
+
+my $ticket = new RT::Ticket $session{CurrentUser};
+my ($c, $o, $k, $e) = ("", [], {}, 0);
+
+# http://.../REST/1.0/ticket/1/take
+$ticket->Load( $id );
+unless ( $ticket->Id ) {
+ $e = 1;
+ $c = "# Ticket $id does not exist.";
+ goto OUTPUT;
+}
+
+my $action;
+
+my @comments;
+
+($action = $changes{Action}) =~ s/^(.)(.*)$/\U$1\L$2\E/;
+unless ($action =~ /^(?:Take|Steal|Untake)$/) {
+ $e = 1;
+ $c = "# Invalid action: `$action'.";
+ goto OUTPUT;
+}
+
+my ($status, $msg) = $ticket->$action();
+$c = "# $msg";
+$e = 1 unless $status;
+goto OUTPUT;
+
+#unless ($ticket->CurrentUserHasRight('ModifyTicket') ||
+# ( ($action eq "Take" || $action eq 'Untake') &&
+# $ticket->CurrentUserHasRight("TakeTicket")) ||
+# ($action eq "Steal" &&
+# $ticket->CurrentUserHasRight("StealTicket")))
+#{
+# $e = 1;
+# $c = "# You are not allowed to $action ticket $id.";
+# goto OUTPUT;
+#}
+
+#if ( keys %changes ) {
+#}
+#else {
+# # process the form data structure
+# my ($key, $val);
+#
+# foreach $key (keys %data) {
+# $val = $data{$key};
+#
+# if ($key =~ /^force$/i) {
+# if ($val !~ /^(?:0|1)$/) {
+# push(@comments, "# invalid value for 'force': $val");
+# goto DONE;
+# }
+# my ($ret_id, $msg);
+#
+# ### take
+# if ($val == 0) {
+# ($ret_id, $msg) = $ticket->Take;
+# if (!$ret_id) {
+# push(@comments, "# Couldn't take ticket $id: $msg");
+# goto DONE;
+# }
+# push(@comments, "# Ticket $id taken.");
+# }
+# ### steal
+# else {
+# ($ret_id, $msg) = $ticket->Steal;
+# if (!$ret_id) {
+# push(@comments, "# Couldn't steal ticket $id: $msg");
+# goto DONE;
+# }
+# push(@comments, "# Ticket $id stolen.");
+# }
+# }
+# }
+#}
+
+OUTPUT:
+return [ $c, $o, $k, $e ];
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/transaction/default b/rt/html/REST/1.0/Forms/transaction/default
new file mode 100644
index 0000000..e23098a
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/transaction/default
@@ -0,0 +1,143 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/transaction
+%#
+<%ARGS>
+$id
+$args => undef
+$format => undef
+$fields => undef
+</%ARGS>
+<%INIT>
+my $trans = new RT::Transactions $session{CurrentUser};
+my ($c, $o, $k, $e) = ("", [], {} , "");
+
+chomp $args;
+my @arglist = split('/', $args);
+my $tid = $id;
+
+$trans->Limit(FIELD => 'Id', OPERATOR => '=', VALUE => $tid);
+
+if ($tid) {
+ my @data;
+ my $t = new RT::Transaction $session{CurrentUser};
+ $t->Load($tid);
+ if ($format eq "l") {
+ push @data, [ id => $t->Id ];
+ push @data, [ Ticket => $t->Ticket ]
+ if (!%$fields || exists $fields->{lc 'Ticket'});
+ push @data, [ TimeTaken => $t->TimeTaken ]
+ if (!%$fields || exists $fields->{lc 'TimeTaken'});
+ push @data, [ Type => $t->Type ]
+ if (!%$fields || exists $fields->{lc 'Type'});
+ push @data, [ Field => $t->Field ]
+ if (!%$fields || exists $fields->{lc 'Field'});
+ push @data, [ OldValue => $t->OldValue ]
+ if (!%$fields || exists $fields->{lc 'OldValue'});
+ push @data, [ NewValue => $t->NewValue ]
+ if (!%$fields || exists $fields->{lc 'NewValue'});
+ push @data, [ Data => $t->Data ]
+ if (!%$fields || exists $fields->{lc 'Data'});
+ push @data, [ Description => $t->Description ]
+ if (!%$fields || exists $fields->{lc 'Description'});
+ push @data, [ Content => $t->Content ]
+ if (!%$fields || exists $fields->{lc 'Content'});
+
+ if (!%$fields || exists $fields->{lc 'Content'}) {
+ my $creator = new RT::User $session{CurrentUser};
+ $creator->Load($t->Creator);
+ push @data, [ Creator => $creator->Name ];
+ }
+ push @data, [ Created => $t->Created ]
+ if (!%$fields || exists $fields->{lc 'Created'});
+
+ if (!%$fields || exists $fields->{lc 'Attachments'}) {
+ my $attachlist;
+ my $attachments = $t->Attachments;
+ while (my $a = $attachments->Next) {
+ my $size = length($a->Content);
+ if ($size > 1024) {
+ $size = int($size/102.4)/10 . "k";
+ }
+ else {
+ $size .= "b";
+ }
+ $attachlist .= "\n" . $a->Id.": ".($a->Filename || "untitled")." (".$size.")";
+ }
+ push @data, [Attachments => $attachlist];
+ }
+
+ } else {
+ push @data, [ id => $t->Id ];
+ push @data, [ Description => $t->Description ];
+ }
+
+ my %k = map {@$_} @data;
+ $o = [ map {$_->[0]} @data ];
+ $k = \%k;
+}
+#else {
+# my (@data, $tids);
+# $format ||= "s";
+# $format = "l" if (%$fields);
+#
+# while (my $t = $trans->Next) {
+# my $tid = $t->Id;
+# if ($format eq "l") {
+# $tids .= "," if $tids;
+# $tids .= $tid;
+# } else {
+# push @$o, $tid;
+# $k->{$tid} = $t->Description;
+# }
+# }
+#}
+
+return [ $c, $o, $k, $e ];
+
+</%INIT>
diff --git a/rt/html/REST/1.0/Forms/user/default b/rt/html/REST/1.0/Forms/user/default
new file mode 100644
index 0000000..621e96d
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/user/default
@@ -0,0 +1,188 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/user/default
+%#
+<%ARGS>
+$id
+$format => 's'
+$changes => {}
+</%ARGS>
+<%perl>
+my @comments;
+my ($c, $o, $k, $e) = ("", [], {}, 0);
+my %data = %$changes;
+my $user = new RT::User $session{CurrentUser};
+my @fields = qw(RealName NickName Gecos Organization Address1 Address2 City
+ State Zip Country HomePhone WorkPhone MobilePhone PagerPhone
+ FreeformContactInfo Comments Signature Lang EmailEncoding
+ WebEncoding ExternalContactInfoId ContactInfoSystem
+ ExternalAuthId AuthSystem);
+my %fields = map { lc $_ => $_ } @fields;
+
+if ($id ne 'new') {
+ $user->Load($id);
+ if (!$user->Id) {
+ return [ "# User $id does not exist.", [], {}, 1 ];
+ }
+}
+else {
+ if (%data == 0) {
+ return [
+ "# Required: Name, EmailAddress",
+ [ qw(id Name EmailAddress Organization Password Comments) ],
+ {
+ id => "user/new",
+ Name => "",
+ EmailAddress => "",
+ Organization => "",
+ Password => "",
+ Comments => ""
+ },
+ 0
+ ];
+ }
+ else {
+ my %v;
+ my %create = %fields;
+ $create{name} = "Name";
+ $create{password} = "Password";
+ $create{emailaddress} = "EmailAddress";
+ $create{contactinfo} = "FreeformContactInfo";
+ # Do any fields need to be excluded here?
+
+ foreach my $k (keys %data) {
+ if (exists $create{lc $k}) {
+ $v{$create{lc $k}} = delete $data{$k};
+ }
+ }
+
+ $user->Create(%v);
+ unless ($user->Id) {
+ return [ "# Could not create user.", [], {}, 1 ];
+ }
+
+ $id = $user->Id;
+ delete $data{id};
+ push(@comments, "# User $id created.");
+ goto DONE if %data == 0;
+ }
+}
+
+if (%data == 0) {
+ my @data;
+
+ push @data, [ id => "user/".$user->Id ];
+ push @data, [ Name => $user->Name ];
+ push @data, [ Password => '********' ];
+ push @data, [ EmailAddress => $user->EmailAddress ];
+
+ foreach my $key (@fields) {
+ my $val = $user->$key;
+
+ if ($format eq 'l' || (defined $val && $val ne '')) {
+ $key = "ContactInfo" if $key eq 'FreeformContactInfo';
+ push @data, [ $key => $val ];
+ }
+ }
+
+ my %k = map {@$_} @data;
+ $o = [ map {$_->[0]} @data ];
+ $k = \%k;
+}
+else {
+ my ($get, $set, $key, $val, $n, $s);
+
+ foreach $key (keys %data) {
+ $val = $data{$key};
+ $key = lc $key;
+ $n = 1;
+
+ if ($key eq 'name' || $key eq 'emailaddress' ||
+ $key eq 'contactinfo' || exists $fields{$key})
+ {
+ if (exists $fields{$key}) {
+ $key = $fields{$key};
+ }
+ else {
+ $key = "FreeformContactInfo" if $key eq 'contactinfo';
+ $key = "EmailAddress" if $key eq 'emailaddress';
+ $key = "Name" if $key eq 'name';
+ }
+ $set = "Set$key";
+
+ next if $val eq $user->$key;
+ ($n, $s) = $user->$set($val);
+ }
+ elsif ($key eq 'password') {
+ ($n, $s) = $user->SetPassword($val) unless $val =~ /^\**$/;
+ }
+ elsif ($key ne 'id') {
+ $n = 0;
+ $s = "Unknown field.";
+ }
+
+ SET:
+ if ($n == 0) {
+ $e = 1;
+ push @comments, "# $key: $s";
+ unless (@$o) {
+ my %o = keys %$changes;
+ delete @o{"id", @fields};
+ @$o = ("id", @fields, keys %o);
+ $k = $changes;
+ }
+ }
+ }
+
+ push(@comments, "# User $id updated.") unless $n == 0;
+}
+
+DONE:
+$c ||= join("\n", @comments) if @comments;
+return [ $c, $o, $k, $e ];
+</%perl>
diff --git a/rt/html/REST/1.0/Forms/user/ns b/rt/html/REST/1.0/Forms/user/ns
new file mode 100644
index 0000000..3503d8a
--- /dev/null
+++ b/rt/html/REST/1.0/Forms/user/ns
@@ -0,0 +1,65 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/Forms/user/ns
+%#
+<%ARGS>
+$id
+</%ARGS>
+<%perl>
+use RT::Users;
+
+my $field = "Name";
+$field = "EmailAddress" if $id =~ /\@/;
+
+my $users = new RT::Users $session{CurrentUser};
+$users->Limit(FIELD => $field, OPERATOR => '=', VALUE => $id);
+if ($users->Count == 0) {
+ return (0, "No user named $id exists.");
+}
+return $users->Next->Id;
+</%perl>
diff --git a/rt/html/REST/1.0/NoAuth/mail-gateway b/rt/html/REST/1.0/NoAuth/mail-gateway
new file mode 100644
index 0000000..739dace
--- /dev/null
+++ b/rt/html/REST/1.0/NoAuth/mail-gateway
@@ -0,0 +1,84 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%flags>
+inherit => undef # inhibit UTF8 conversion done in /autohandler
+</%flags>
+<%ARGS>
+$queue => 1
+$action => "correspond"
+$ticket => undef
+</%ARGS>
+<%init>
+$m->comp('/Elements/Callback', _CallbackName => 'Pre', %ARGS);
+use RT::Interface::Email (); # It's an exporter, but we don't care
+$r->content_type('text/plain; charset=utf-8');
+$m->error_format('text');
+my ( $status, $error, $Ticket ) = RT::Interface::Email::Gateway( \%ARGS );
+if ( $status == 1 ) {
+ $m->out('ok');
+ if ( $Ticket->Id ) {
+ $m->out( 'Ticket: ' . ($Ticket->Id || '') );
+ $m->out( 'Queue: ' . ($Ticket->QueueObj->Name || '') );
+ $m->out( 'Owner: ' . ($Ticket->OwnerObj->Name || '') );
+ $m->out( 'Status: ' . ($Ticket->Status || '') );
+ $m->out( 'Subject: ' . ($Ticket->Subject || '') );
+ $m->out(
+ 'Requestor: ' . ($Ticket->Requestors->MemberEmailAddressesAsString || '') );
+ }
+}
+else {
+ $RT::Logger->error( "Could not record email: " . $error );
+ if ( $status == -75 ) {
+ $m->out( "temporary failure - " . $error );
+ }
+ else {
+ $m->out( 'not ok - ' . $error );
+ }
+}
+$m->abort();
+</%init>
diff --git a/rt/html/REST/1.0/autohandler b/rt/html/REST/1.0/autohandler
new file mode 100644
index 0000000..e8247b1
--- /dev/null
+++ b/rt/html/REST/1.0/autohandler
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/autohandler
+%#
+<%INIT>
+use RT::Interface::REST;
+$r->content_type('text/plain; charset=utf-8');
+$m->error_format('text');
+$m->call_next();
+$m->abort();
+</%INIT>
diff --git a/rt/html/REST/1.0/dhandler b/rt/html/REST/1.0/dhandler
new file mode 100644
index 0000000..7406520
--- /dev/null
+++ b/rt/html/REST/1.0/dhandler
@@ -0,0 +1,316 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/dhandler
+%#
+<%ARGS>
+@id => ()
+$fields => undef
+$format => undef
+$content => undef
+</%ARGS>
+<%INIT>
+use RT::Interface::REST;
+
+my $output = "";
+my $status = "200 Ok";
+my $object = $m->dhandler_arg;
+
+my $name = qr{[\w.-]+};
+my $list = '(?:(?:\d+-)?\d+,)*(?:\d+-)?\d+';
+my $label = '[a-zA-Z0-9@_.+-]+';
+my $field = '[a-zA-Z](?:[a-zA-Z0-9_-]|\s+)*';
+my $labels = "(?:$label,)*$label";
+
+# We must handle requests such as the following:
+#
+# 1. http://.../REST/1.0/show (with a list of object specifications).
+# 2. http://.../REST/1.0/edit (with a self-contained list of forms).
+# 3. http://.../REST/1.0/ticket/show (implicit type specification).
+# http://.../REST/1.0/ticket/edit
+# 4. http://.../REST/1.0/ticket/nn (all possibly with a single form).
+# http://.../REST/1.0/ticket/nn/history
+# http://.../REST/1.0/ticket/nn/comment
+# http://.../REST/1.0/ticket/nn/attachment/1
+#
+# Objects are specified by their type, and either a unique numeric ID,
+# or a unique name (e.g. ticket/1, queue/foo). Multiple objects of the
+# same type may be specified by a comma-separated list of identifiers
+# (e.g., user/ams,rai or ticket/1-3,5-7).
+#
+# Ultimately, we want a list of object specifications to operate upon.
+# The URLs in (4) provide enough information to identify an object. We
+# will assemble submitted information into that format in other cases.
+#
+my (@objects, $forms);
+my $utype;
+
+if ($object eq 'show' || # $REST/show
+ (($utype) = ($object =~ m{^($name)/show$}))) # $REST/ticket/show
+{
+ # We'll convert type/range specifications ("ticket/1-3,7-9/history")
+ # into a list of singular object specifications ("ticket/1/history").
+ # If the URL specifies a type, we'll accept only that one.
+ foreach my $id (@id) {
+ $id =~ s|^(?:$utype/)?|$utype/| if $utype;
+ if (my ($type, $oids, $extra) =
+ ($id =~ m#^($name)/($list|$labels)(?:(/.*))?$#o))
+ {
+ foreach my $oid (expand_list($oids)) {
+ if ($extra =~ m{^(?:/($name)(?:/(.*))?)?$}o) {
+ my ($attr, $args) = ($1, $2);
+ # expand transaction and attachment range specifications
+ # (if applicable)
+ my $tids;
+ if ($attr eq 'history' && $args =~ m#id/(\d.*)#o) {
+ $tids = $1;
+ }
+ if ($tids) {
+ push(@objects, "$type/$oid/$attr/id/$_") for expand_list($tids);
+ } else {
+ push(@objects, "$type/$oid$extra");
+ }
+ }
+ }
+ }
+ else {
+ $status = "400 Bad Request";
+ $output = "Invalid object ID specified: '$id'";
+ goto OUTPUT;
+ }
+ }
+}
+elsif ($object eq 'edit' || # $REST/edit
+ (($utype) = ($object =~ m{^($name)/edit$}))) # $REST/ticket/edit
+{
+ # We'll make sure each of the submitted forms is syntactically valid
+ # and sufficiently identifies an object to operate upon, then add to
+ # the object list as above.
+ my @output;
+
+ $forms = form_parse($content);
+ foreach my $form (@$forms) {
+ my ($c, $o, $k, $e) = @$form;
+
+ if ($e) {
+ push @output, [ "# Syntax error.", $o, $k, $e ];
+ }
+ else {
+ my ($type, $id);
+
+ # Look for matching types in the ID, form, and URL.
+ $type = exists $k->{type} ? $k->{type} : $utype;
+ $type =~ s|^(?:$utype)?|$utype/| if $utype;
+ $type =~ s|/$||;
+
+ if (exists $k->{id}) {
+ $id = $k->{id};
+ $id =~ s|^(?:$type/)?|$type/| if $type;
+
+ if ($id =~ m#^$name/(?:$label|\d+)(?:/.*)?#o) {
+ push @objects, $id;
+ }
+ else {
+ push @output, [ "# Invalid object ID: '$id'", $o, $k, $e ];
+ }
+ }
+ else {
+ push @output, [ "# No object ID specified.", $o, $k, $e ];
+ }
+ }
+ }
+ # If we saw any errors at this stage, we won't process any part of
+ # the submitted data.
+ if (@output) {
+ unshift @output, [ "# Please resubmit with errors corrected." ];
+ $status = "409 Syntax Error";
+ $output = form_compose(\@output);
+ goto OUTPUT;
+ }
+}
+else {
+ # We'll assume that this is in the correct format already. Otherwise
+ # it will be caught by the loop below.
+ push @objects, $object;
+
+ if ($content) {
+ $forms = form_parse($content);
+
+ if (@$forms > 1) {
+ $status = "400 Bad Request";
+ $output = "You may submit only one form to this object.";
+ goto OUTPUT;
+ }
+
+ my ($c, $o, $k, $e) = @{ $forms->[0] };
+ if ($e) {
+ $status = "409 Syntax Error";
+ $output = form_compose([ ["# Syntax error.", $o, $k, $e] ]);
+ goto OUTPUT;
+ }
+ }
+}
+
+# Make sure we have something to do.
+unless (@objects) {
+ $status = "400 Bad Request";
+ $output = "No objects specified.";
+ goto OUTPUT;
+}
+
+# Parse and validate any field specifications.
+my (%fields, @fields);
+if ($fields) {
+ unless ($fields =~ /^(?:$field,)*$field$/) {
+ $status = "400 Bad Request";
+ $output = "Invalid field specification: $fields";
+ goto OUTPUT;
+ }
+ @fields = map lc, split /,/, $fields;
+ @fields{@fields} = ();
+ unless (exists $fields{id}) {
+ unshift @fields, "id";
+ $fields{id} = ();
+ }
+}
+
+my (@comments, @output);
+
+foreach $object (@objects) {
+ my ($handler, $type, $id, $attr, $args);
+ my ($c, $o, $k, $e) = ("", ["id"], {id => $object}, 0);
+
+ my $i = 0;
+ if ($object =~ m{^($name)/(\d+|$label)(?:/($name)(?:/(.*))?)?$}o ||
+ $object =~ m{^($name)/(new)$}o)
+ {
+ ($type, $id, $attr, $args) = ($1, $2, ($3 || 'default'), $4);
+ $handler = "Forms/$type/$attr";
+
+ unless ($m->comp_exists($handler)) {
+ $args = "$attr/$args";
+ $handler = "Forms/$type/default";
+
+ unless ($m->comp_exists($handler)) {
+ $i = 2;
+ $c = "# Unknown object type: $type";
+ }
+ }
+ elsif ($id ne 'new' && $id !~ /^\d+$/) {
+ my $ns = "Forms/$type/ns";
+
+ # Can we resolve named objects?
+ unless ($m->comp_exists($ns)) {
+ $i = 3;
+ $c = "# Objects of type $type must be specified by numeric id.";
+ }
+ else {
+ my ($n, $s) = $m->comp("Forms/$type/ns", id => $id);
+ if ($n <= 0) { $i = 4; $c = "# $s"; }
+ else { $i = 0; $id = $n; }
+ }
+ }
+ else {
+ $i = 0;
+ }
+ }
+ else {
+ $i = 1;
+ $c = "# Invalid object specification: '$object'";
+ }
+
+ if ($i != 0) {
+ if ($content) {
+ (undef, $o, $k, $e) = @{ shift @$forms };
+ }
+ push @output, [ $c, $o, $k ];
+ next;
+ }
+
+ unless ($content) {
+ my $d = $m->comp($handler, id => $id, args => $args, format => $format, fields => \%fields);
+ my ($c, $o, $k, $e) = @$d;
+
+ if (!$e && @$o && keys %fields) {
+ my %lk = map { lc $_ => $_ } keys %$k;
+ @$o = map { $lk{$_} } @fields;
+ foreach my $key (keys %$k) {
+ delete $k->{$key} unless exists $fields{lc $key};
+ }
+ }
+ push(@output, [ $c, $o, $k ]) if ($c || @$o || keys %$k);
+ }
+ else {
+ my ($c, $o, $k, $e) = @{ shift @$forms };
+ my $d = $m->comp($handler, id => $id, args => $args, format => $format,
+ changes => $k);
+ ($c, $o, $k, $e) = @$d;
+
+ # We won't pass $e through to compose, trusting instead that the
+ # handler added suitable comments for the user.
+ if ($e) {
+ if (@$o) {
+ $status = "409 Syntax Error";
+ }
+ else {
+ $status = "400 Bad Request";
+ }
+ push @output, [ $c, $o, $k ];
+ }
+ else {
+ push @comments, $c;
+ }
+ }
+}
+
+unshift(@output, [ join "\n", @comments ]) if @comments;
+$output = form_compose(\@output);
+
+OUTPUT:
+$m->out("RT/".$RT::VERSION ." ".$status ."\n\n$output\n") if ($output || $status != 200);
+return;
+</%INIT>
diff --git a/rt/html/REST/1.0/logout b/rt/html/REST/1.0/logout
new file mode 100644
index 0000000..4152a7e
--- /dev/null
+++ b/rt/html/REST/1.0/logout
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%PERL>
+tied(%session)->delete if (defined %session);
+</%PERL>
+RT/<% $RT::VERSION %> 200 Ok
diff --git a/rt/html/REST/1.0/search/dhandler b/rt/html/REST/1.0/search/dhandler
new file mode 100644
index 0000000..3e6b314
--- /dev/null
+++ b/rt/html/REST/1.0/search/dhandler
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/search/dhandler
+%#
+<%INIT>
+my $status = "500 Server Error";
+my $output = "Unsupported object type.";
+</%INIT>
+RT/<% $RT::VERSION %> <% $status %>
+
+<% $output |n %>
diff --git a/rt/html/REST/1.0/search/ticket b/rt/html/REST/1.0/search/ticket
new file mode 100644
index 0000000..3788393
--- /dev/null
+++ b/rt/html/REST/1.0/search/ticket
@@ -0,0 +1,158 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/search/ticket
+%#
+<%ARGS>
+$query
+$format => undef
+$orderby => undef
+$fields => undef
+</%ARGS>
+<%INIT>
+my $output = "";
+my $status = "200 Ok";
+my $tickets = new RT::Tickets $session{CurrentUser};
+
+# Parse and validate any field specifications.
+my $field = '[a-zA-Z](?:[a-zA-Z0-9_-]|\s+)*';
+my (%fields, @fields);
+if ($fields) {
+ $format ||= "l";
+ unless ($fields =~ /^(?:$field,)*$field$/) {
+ $status = "400 Bad Request";
+ $output = "Invalid field specification: $fields";
+ goto OUTPUT;
+ }
+ @fields = map lc, split /,/, $fields;
+ @fields{@fields} = ();
+ unless (exists $fields{id}) {
+ unshift @fields, "id";
+ $fields{id} = ();
+ }
+}
+
+$format ||= "s";
+if ($format !~ /^[isl]$/) {
+ $status = "400 Bad request";
+ $output = "Unknown listing format: $format. (Use i, s, or l.)\n";
+ goto OUTPUT;
+}
+
+my ($n, $s);
+eval {
+ ($n, $s) = $tickets->FromSQL($query);
+};
+my $sortstring = "";
+if ($orderby) {
+ $sortstring = 'FIELD => ';
+ my $order = substr($orderby, 0, 1);
+ if ($order eq '+' || $order eq '-') {
+ $sortstring .= 'substr($orderby, 1)';
+ if ($order eq '+') {
+ $sortstring .= ", ORDER => 'ASC'";
+ } elsif ($order eq '-') {
+ $sortstring .= ", ORDER => 'DESC'";
+ }
+ } else {
+ $sortstring .= '$orderby';
+ }
+ my $foo = 'FIELD => ';
+ $foo .= '$orderby';
+ $tickets->OrderBy(eval $sortstring);
+}
+if ($@ || $n == 0) {
+ $s ||= $@;
+ $status = "400 Bad request";
+ $output = "Invalid query: '$s'.\n";
+ goto OUTPUT;
+}
+
+$n = 0;
+my @output;
+while (my $ticket = $tickets->Next) {
+ $n++;
+
+ my $id = $ticket->Id;
+ if ($format eq "i") {
+ $output .= "ticket/" . $id . "\n";
+ }
+ elsif ($format eq "s") {
+ if ($fields) {
+ my $result = $m->comp("/REST/1.0/Forms/ticket/default", id => $id, format => $format, fields => \%fields);
+ my ($notes, $order, $key_values, $errors) = @$result;
+ # If it's the first time through, add our header
+ if ($n == 1) {
+ $output .= join("\t",@$order)."\n";
+ }
+ # Cut off the annoying ticket/ before the id;
+ $key_values->{'id'} = $id;
+ $output .= join("\t", map {$key_values->{$_}} @$order)."\n";
+
+
+ } else {
+ $output .= $ticket->Id . ": ". $ticket->Subject . "\n";
+ }
+ }
+ else {
+ my $d = $m->comp("/REST/1.0/Forms/ticket/default", id => $id, format => $format, fields => \%fields);
+ my ($c, $o, $k, $e) = @$d;
+ push @output, [ $c, $o, $k ];
+ }
+}
+if ($n == 0 && $format ne "i") {
+ $output = "No matching results.\n";
+}
+
+$output = form_compose(\@output) if @output;
+
+OUTPUT:
+$m->out("RT/". $RT::VERSION . " " . $status ."\n\n");
+
+$m->out($output );
+return();
+</%INIT>
diff --git a/rt/html/REST/1.0/ticket/comment b/rt/html/REST/1.0/ticket/comment
new file mode 100644
index 0000000..240dd90
--- /dev/null
+++ b/rt/html/REST/1.0/ticket/comment
@@ -0,0 +1,177 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/ticket/comment
+%#
+<%ARGS>
+$content
+</%ARGS>
+<%INIT>
+use MIME::Entity;
+use LWP::MediaTypes;
+use RT::Interface::REST;
+use File::Temp qw(tempfile);
+
+my $ticket = new RT::Ticket $session{CurrentUser};
+my $object = $r->path_info;
+my $status = "200 Ok";
+my $output;
+my $action;
+
+# http://.../REST/1.0/ticket/1/comment
+my ($c, $o, $k, $e) = @{ form_parse($content)->[0] };
+if ($e || !$o) {
+ if (!$o) {
+ $output = "Empty form submitted.\n";
+ }
+ else {
+ $c = "# Syntax error.";
+ $output = form_compose([[$c, $o, $k, $e]]);
+ }
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+
+$object =~ s#^/##;
+$object ||= $k->{Ticket};
+unless ($object =~ /^\d+/) {
+ $output = "Invalid ticket id: `$object'.\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+if ($k->{Ticket} && $object ne $k->{Ticket}) {
+ $output = "The submitted form and URL specify different tickets.\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+
+($action = $k->{Action}) =~ s/^(.)(.*)$/\U$1\L$2\E/;
+unless ($action =~ /^(?:Comment|Correspond)$/) {
+ $output = "Invalid action: `$action'.\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+
+my $text = $k->{Text};
+my @atts = @{ vsplit($k->{Attachment}) };
+
+if (!$k->{Text} && @atts == 0) {
+ $status = "400 Bad Request";
+ $output = "Empty comment with no attachments submitted.\n";
+ goto OUTPUT;
+}
+
+my $cgi = $m->cgi_object;
+my $ent = MIME::Entity->build(Type => "multipart/mixed");
+$ent->attach(Data => $k->{Text}) if $k->{Text};
+
+my $i = 1;
+foreach my $att (@atts) {
+ local $/=undef;
+ my $file = $att;
+ $file =~ s#^.*[\\/]##;
+
+ my $fh = $cgi->upload("attachment_$i");
+ if ($fh) {
+ my $buf;
+ my ($w, $tmp) = tempfile();
+ my $info = $cgi->uploadInfo();
+
+ while (sysread($fh, $buf, 8192)) {
+ syswrite($w, $buf);
+ }
+
+ $ent->attach(
+ Path => $tmp,
+ Type => $info->{'Content-Type'} || guess_media_type($tmp),
+ Filename => $file,
+ Disposition => "attachment"
+ );
+ }
+ else {
+ $status = "400 Bad Request";
+ $output = "No attachment for $att.\n";
+ goto OUTPUT;
+ }
+
+ $i++;
+}
+
+$ticket->Load($object);
+unless ($ticket->Id) {
+ $output = "Couldn't load ticket id: `$object'.\n";
+ $status = "404 Ticket not found";
+ goto OUTPUT;
+}
+unless ($ticket->CurrentUserHasRight('ModifyTicket') ||
+ ($action eq "Comment" &&
+ $ticket->CurrentUserHasRight("CommentOnTicket")) ||
+ ($action eq "Correspond" &&
+ $ticket->CurrentUserHasRight("ReplyToTicket")))
+{
+ $output = "You are not allowed to $action on ticket $object.\n";
+ $status = "403 Permission denied";
+ goto OUTPUT;
+}
+
+my $cc = join ", ", @{ vsplit($k->{Cc}) };
+my $bcc = join ", ", @{ vsplit($k->{Bcc}) };
+my ($n, $s) = $ticket->$action(MIMEObj => $ent,
+ CcMessageTo => $cc,
+ BccMessageTo => $bcc,
+ TimeTaken => $k->{TimeWorked} || 0);
+$output = $s;
+if ($k->{Status}) {
+ my ($status_n, $status_s) = $ticket->SetStatus($k->{'Status'} );
+ $output .= "\n".$status_s;
+}
+
+OUTPUT:
+</%INIT>
+RT/<% $RT::VERSION %> <% $status %>
+
+<% $output |n %>
diff --git a/rt/html/REST/1.0/ticket/link b/rt/html/REST/1.0/ticket/link
new file mode 100644
index 0000000..766e126
--- /dev/null
+++ b/rt/html/REST/1.0/ticket/link
@@ -0,0 +1,123 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/ticket/link
+%#
+<%ARGS>
+$id => undef
+$del => 0
+$rel
+$to
+</%ARGS>
+<%INIT>
+use RT::Interface::REST;
+
+my $output;
+my $status = "200 Ok";
+my $ticket = new RT::Ticket $session{CurrentUser};
+my $object = $r->path_info;
+
+my @fields = qw(DependsOn DependedOnBy RefersTo ReferredToBy HasMember MemberOf);
+my %fields = map { lc $_ => $_ } @fields;
+my %lfields = (
+ HasMember => { Type => 'MemberOf', Mode => 'Base' },
+ ReferredToBy => { Type => 'RefersTo', Mode => 'Base' },
+ DependedOnBy => { Type => 'DependsOn', Mode => 'Base' },
+ MemberOf => { Type => 'MemberOf', Mode => 'Target' },
+ RefersTo => { Type => 'RefersTo', Mode => 'Target' },
+ DependsOn => { Type => 'DependsOn', Mode => 'Target' },
+);
+
+# http://.../REST/1.0/ticket/link/1
+
+$object =~ s#^/REST/1.0/ticket/link##;
+if ($id && $object && $id != $object) {
+ $output = "Different ids in URL (`$object') and submitted form (`$id').\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+$id ||= $object;
+unless ($id =~ /^\d+$/ && $to =~ /^\d+$/) {
+ my $bad = ($id !~ /^\d+$/) ? $id : $to;
+ $output = $r->path_info. "\n";
+ $output .= "Invalid ticket id: '$bad'.\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+unless (exists $fields{lc $rel}) {
+ $output = "Invalid link: '$rel'.\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+$rel = $fields{lc $rel};
+
+$ticket->Load($id);
+unless ($ticket->Id) {
+ $output = "Couldn't load ticket id: '$id'.\n";
+ $status = "404 Ticket not found";
+ goto OUTPUT;
+}
+
+my $type = $lfields{$rel}->{Type};
+my $mode = $lfields{$rel}->{Mode};
+
+my $n = 1;
+my $op = $del ? "DeleteLink" : "AddLink";
+
+($n, $output) = $ticket->$op(Type => $type, $mode => $to);
+if ($n == 0) {
+ $status = "500 Error";
+} else {
+ my $action = $del ? "Deleted" : "Created";
+ $output .= " $action link " . $ticket->Id . " $rel $to";
+}
+
+OUTPUT:
+</%INIT>
+RT/<% $RT::VERSION %> <% $status %>
+
+<% $output |n %>
diff --git a/rt/html/REST/1.0/ticket/merge b/rt/html/REST/1.0/ticket/merge
new file mode 100644
index 0000000..d6ae96f
--- /dev/null
+++ b/rt/html/REST/1.0/ticket/merge
@@ -0,0 +1,102 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# REST/1.0/ticket/merge
+%#
+<%ARGS>
+$id => undef
+$into
+</%ARGS>
+<%INIT>
+use RT::Interface::REST;
+
+my $output;
+my $status = "200 Ok";
+my $ticket = new RT::Ticket $session{CurrentUser};
+my $object = $r->path_info;
+
+# http://.../REST/1.0/ticket/merge/1
+
+$object =~ s#^/##;
+if ($id && $object && $id != $object) {
+ $output = "Different ids in URL (`$object') and submitted form (`$id').\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+$id ||= $object;
+unless ($id =~ /^\d+$/ && $into =~ /^\d+$/) {
+ my $bad = ($id !~ /^\d+$/) ? $id : $into;
+ $output = $r->path_info. "\n";
+ $output .= "Invalid ticket id: `$bad'.\n";
+ $status = "400 Bad Request";
+ goto OUTPUT;
+}
+
+$ticket->Load($id);
+unless ($ticket->Id) {
+ $output = "Couldn't load ticket id: `$id'.\n";
+ $status = "404 Ticket not found";
+ goto OUTPUT;
+}
+unless ($ticket->CurrentUserHasRight('ModifyTicket')) {
+ $output = "You are not allowed to modify ticket $id.\n";
+ $status = "403 Permission denied";
+ goto OUTPUT;
+}
+
+my ($n, $s) = $ticket->MergeInto($into);
+
+if ($n == 0) {
+ $status = "500 Error";
+}
+$output = $s;
+
+OUTPUT:
+</%INIT>
+RT/<% $RT::VERSION %> <% $status %>
+
+<% $output |n %>
diff --git a/rt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart b/rt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart
new file mode 100755
index 0000000..02a183b
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart
@@ -0,0 +1,39 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+</%perl>
+<em><&|/l, $#data+1&>[_1] Plot Elements</&></em><p>
+% foreach my $value (@data) {
+<% $value %><p>
+% }
+<em><&|/l&>x_labels</&>:</em><p>
+<% $ARGS{x_labels} %>
+<p>
+<em><&|/l&>legend</&>:</em><p>
+<% $ARGS{set_legend} %>
+<p>
+<em><&|/l, (keys %ARGS) - 2&>[_1] data sets</&>:</em><p>
+
+% for (1..(scalar keys %ARGS)-2) {
+<% $_ %> <% $ARGS{"data$_"} %><p>
+% }
+
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Day of Week',
+ y_label => 'Tickets per day');
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+my $format = $graph->export_format;
+push @data, [split /,/ , $ARGS{x_labels}];
+for (1..((scalar keys %ARGS)-2)) {
+ push @data, [split /,/ , $ARGS{"data".$_}];
+}
+
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/CallsMultiQueue/index.html b/rt/html/RTx/Statistics/CallsMultiQueue/index.html
new file mode 100755
index 0000000..abf8aa7
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsMultiQueue/index.html
@@ -0,0 +1,330 @@
+<& /Elements/Header, Title => loc('Tickets per day in Multiple queues') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('Tickets per day in Multiple Queues by status') &>
+
+<h3>Description</h3>
+<p>This chart shows details of tickets per day by their status. You can select multiple queues to display at the same time, but only one status. You can chose any of the defined status values.
+There is also the option to display all available queues at the same time.
+The default display shows tickets resolved in your default queue (General unless altered locally).
+The line chart below shows the same information in a graphical form.
+
+<br />
+
+<form method="POST" action="index.html">
+
+%# Build Legend
+% my @legend;
+% for (sort keys %queues_to_show) {
+% push @legend, $_;
+% }
+
+%my $title = "Tickets with Status $status in " . join(', ', @queues) . ", per day from " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+
+<& /Elements/TitleBoxStart, title => $title, title_href => "/RTx/Statistics/OpenStalled/index.html?$QueryString"&>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH="100%">
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@RowFormat,
+ FormatString => $RowFormat,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 0;
+% LINE: for my $d (0..$#dates) {
+% if ($d == $#dates ){
+% next LINE;
+% }
+% $line++;
+% my $x = 1;
+% $values{Statistics_Date} = Statistics::FormatDate($dateformat, $dates[$d]);
+% my $row_total=0;
+% foreach my $q (sort keys %queues_to_show) {
+% my $tix = new RT::Tickets($session{'CurrentUser'});
+% if ($status eq "resolved") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "new") {
+% $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "deleted") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "stalled") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "open") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "rejected") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% $tix->LimitQueue (VALUE => $q);
+% $values{$q} = $tix->Count;
+% $row_total += $tix->Count;
+% $data[$x++][$d] = $tix->Count;
+% }
+% $values{Statistics_Totals} = $row_total;
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@RowFormat, i => $line, record => $record, maxitems => $maxitems &>
+% }
+</table>
+<& /Elements/TitleBoxEnd&>
+
+<hr>
+
+<BR />
+<BR />
+
+<!-- <td>Show:</td>
+ <td COLSPAN=2><SELECT NAME="status">
+% for (qw(resolved new deleted stalled rejected open)) {
+ <OPTION VALUE="<% $_ %>" <% $_ eq $status && "SELECTED" %>>
+ <% loc($_) %></OPTION>
+% }
+--!>
+
+<%perl>
+# Create the graph URL
+my $url = 'Elements/Chart?x_labels=';
+#$url .= join ",", @{ shift @data } . "&";
+for (0..$max) {
+ $url .= $m->interp->apply_escapes($data[0][$_],'u') . ",";
+}
+chop $url;
+$url .= "&";
+shift @data;
+$url .= 'set_legend='.(join ",", @legend)."&";
+for (0..$#data) {
+ $url .= "data".(1+$_)."=". (join ",", @{$data[$_]})."&";
+}
+chop $url;
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Status, Queues or Dates",
+ ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+ eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+ weekends => $weekends,
+ ShowMultiQueues => 1, queues_ref => \@queues,
+ ShowStatus => 1, Status => $status
+ &>
+
+</form>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsMultiQueue/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a>
+%# | <a href="<%$RT::WebPath%>/RTx/Statistics/CallsMultiQueue/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+<%ARGS>
+$status => $Statistics::MultiQueueStatus
+$max => $Statistics::MultiQueueMaxRows
+@queues => @Statistics::MultiQueueQueueList
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$dateformat => $Statistics::MultiQueueDateFormat
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+$AddAllCheck => undef
+</%ARGS>
+
+<%INIT>
+
+use RTx::Statistics;
+use Time::Local;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $selected;
+my $diff;
+my %queues_to_show;
+my $secsPerDay=86400;
+my $sEpoch;
+my $eEpoch;
+my $QueryString;
+my $maxitems;
+my $RowFormat;
+my $BoldRowFormat;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+
+ # Handle the Add All Checkbox
+ if($AddAllCheck eq "on") {
+ $AddAllCheck = undef;
+ undef (@queues);
+ my $q=new RT::Queues($session{'CurrentUser'});
+ $q->UnLimit;
+ while (my $queue=$q->Next) {
+ next if !$queue->CurrentUserHasRight('SeeQueue');
+ push @queues, $queue->Name;
+ }
+ }
+
+ # If the user has the right to see the queue, put it into the map
+ for my $q (@queues) {
+ my $Queueobj = new RT::Queue($session{'CurrentUser'});
+ $Queueobj->Load($q);
+ next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+ $queues_to_show{$q} = 1;
+ }
+
+ $maxitems = (scalar @queues) + 2;
+
+ # Build the format strings
+ $RowFormat = "'__Statistics_Date__'";
+ $BoldRowFormat = "'<B>__Statistics_Date__</B>'";
+ for my $q (@queues) {
+ $RowFormat .= ",'__Statistics_Dynamic__/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Dynamic__</B>/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ }
+ $RowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ # Parse the formats into structures.
+ my (@RowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $RowFormat);
+ my (@BoldRowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldRowFormat);
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+# Build the new query string
+$QueryString = "queues=" . join("&queues=", @queues);
+$QueryString .= "&sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends";
+
+
+
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+$n = 0;
+until ($#dates == $diff) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+ unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+
+# We put an extra day into the lists to cover up till midnight of the next day,
+# But we don't want that to appear in the labels, so pop it off.
+pop( @{ $data[0] } );
+
+my $queue = new RT::Queues($session{CurrentUser});
+$queue->UnLimit;
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($queue);
+</%INIT>
diff --git a/rt/html/RTx/Statistics/CallsQueueDay/Elements/Chart b/rt/html/RTx/Statistics/CallsQueueDay/Elements/Chart
new file mode 100755
index 0000000..9a3a505
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsQueueDay/Elements/Chart
@@ -0,0 +1,29 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Day of Week',
+ y_label => 'Tickets per Day',
+ x_labels_vertical => 1,
+ );
+my $format = $graph->export_format;
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/CallsQueueDay/Results.tsv b/rt/html/RTx/Statistics/CallsQueueDay/Results.tsv
new file mode 100644
index 0000000..23f0c69
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsQueueDay/Results.tsv
@@ -0,0 +1,191 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Queue => undef
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my @dates;
+my $n = 0;
+my %Totals;
+my $now = new RT::Date($session{CurrentUser});
+my $sEpoch;
+my $eEpoch;
+
+if (!defined $Queue) {
+ $Queue = $Statistics::PerDayQueue;
+}
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# set content type
+$r->content_type('application/vnd.ms-excel');
+
+# Put out some data about the generation of this file
+$m->out("Tickets per day for Queue:\t" . $Queue . "\tGenerated at:\t" . Statistics::FormatDate("%x %X", $now). "\n\n");
+
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+my $diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+
+# Build array of dates
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+until ($#dates == $diff) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+}
+
+# Output header row
+$m->out("Date\tcreate\tresolved\tdeleted\n");
+
+
+LINE: for my $d (0..$#dates) {
+ if ($d == $#dates){
+ next LINE;
+ }
+ my $x = 1;
+ # Output the date for this row
+ $m->out(Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]));
+
+ # output the 3 columns for this row
+ for my $status (qw(created resolved deleted)) {
+ my $tix = new RT::Tickets($session{'CurrentUser'});
+ if ($status eq "created") {
+ $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+ if ($dates[$d+1]) {
+ $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+ }
+ } elsif ($status eq "resolved") {
+ $tix->LimitStatus(VALUE => $status);
+ $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+ if ($dates[$d+1]) {
+ $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+ }
+ } elsif ($status eq "deleted") {
+ $tix->LimitStatus(VALUE => $status);
+ $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+ if ($dates[$d+1]) {
+ $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+ }
+ }
+ $tix->LimitQueue (VALUE => $Queue);
+ $m->out( "\t" . $tix->Count );
+ $Totals{$status} += $tix->Count;
+ }
+ $m->out("\n");
+}
+
+# Output the totals
+$m->out("Totals\t$Totals{created}\t$Totals{resolved}\t$Totals{deleted}\n");
+
+$m->abort();
+</%INIT>
diff --git a/rt/html/RTx/Statistics/CallsQueueDay/index.html b/rt/html/RTx/Statistics/CallsQueueDay/index.html
new file mode 100755
index 0000000..06fc484
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsQueueDay/index.html
@@ -0,0 +1,275 @@
+<& /Elements/Header, Title => loc("Tickets per day in Queue:" . $QueueObj->Name()) &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("Tickets by status per day in Queue:" . $QueueObj->Name()) &>
+
+<h3>Description</h3>
+<p>This page displays details about tickets in the selected queue over the date range chosen. It shows how many tickets were created on
+each day in the chosen range, and how many of those were either Resolved or Deleted.</p>
+<p>To always show the current month to date, bookmark this <a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/index.html?currentMonth=1">link</a>, or
+for a spreadsheet, use this <a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/Results.tsv?currentMonth=1">link</a>.</p>
+
+<form method="POST" action="index.html">
+
+% Statistics::DebugLog("queue name=" . $QueueObj->Name() . "\n");
+
+%my $title = "Ticket counts in " . $QueueObj->Name() . " by status per day from " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+<&|/Elements/TitleBox,
+ title => $title,
+ title_href => "/RTx/Statistics/CallsQueueDay/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@Format,
+ FormatString => $Format,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 1;
+% LINE: for my $d (0..$#dates) {
+% if ($d == $#dates){
+% next LINE;
+% }
+% my $x = 1;
+% $values{Statistics_Date} = Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]);
+%# NOTE need to handle all status values here....
+% for my $status (qw(created resolved deleted)) {
+% my $tix = new RT::Tickets($session{'CurrentUser'});
+% $tix->LimitQueue (VALUE => $Queue);
+% if ($status eq "created") {
+% $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% $values{Statistics_Created_Count} = $tix->Count;
+% $Totals{Statistics_Created_Count} += $tix->Count;
+% }
+% elsif ($status eq "resolved") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% $values{Statistics_Resolved_Count} = $tix->Count;
+% $Totals{Statistics_Resolved_Count} += $tix->Count;
+% }
+% elsif ($status eq "deleted") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% $values{Statistics_Deleted_Count} = $tix->Count;
+% $Totals{Statistics_Deleted_Count} += $tix->Count;
+% }
+% $data[$x++][$d] = $tix->Count;
+% }
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+% }
+% $values {Statistics_Date} = "Totals";
+% $values {Statistics_Created_Count} = $Totals{Statistics_Created_Count};
+% $values {Statistics_Resolved_Count} = $Totals{Statistics_Resolved_Count};
+% $values {Statistics_Deleted_Count} = $Totals{Statistics_Deleted_Count};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldFormat, i => $line, record => $record, maxitems => $maxitems &>
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+# Create the graph URL
+my $url= 'Elements/Chart?x_labels=';
+for (1..$diff) {
+ $url .= $data[0][$_] . ",";
+}
+chop $url;
+$url .= "&";
+shift @data;
+for (0..$#data) {
+ $url .= "data".(1+$_)."=".(join ",", @{$data[$_]})."&";
+}
+chop $url;
+$url .= "&set_legend=Created,Resolved,Deleted";
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Queue or Dates",
+ ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+ eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+ weekends => $weekends,
+ ShowSingleQueue => 1, Queue => $Queue
+ &>
+
+</form>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a> |
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+
+% Statistics::DebugLog("ref of eMonth is " . ref($eMonth) . "\n");
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+$Queue => undef
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my $selected;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $diff;
+my $sEpoch=0;
+my $eEpoch=0;
+my %Totals;
+my $QueryString;
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+
+# If debugging, set things up and display all the args
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $Format = qq{ Statistics_Date,
+ '__Statistics_Created_Count__/STYLE:text-align:right;',
+ '__Statistics_Resolved_Count__/STYLE:text-align:right;',
+ '__Statistics_Deleted_Count__/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>',
+ '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Deleted_Count__</B>/STYLE:text-align:right;' };
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+Statistics::DebugLog("CallsQueueDay/index.html Format array=" . join(',', @Format) . "\n");
+
+if (!defined $Queue) {
+ my $QueueObj = new RT::Queue($session{'CurrentUser'});
+ $QueueObj->Load($Statistics::PerDayQueue);
+ $Queue = $QueueObj->Id();
+}
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+# Set up the string for the current query for bookmarkable link
+$QueryString = "sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends&Queue=$Queue";
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+$n = 0;
+until ($#dates == $diff) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+ unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+
+# We put an extra day into the lists to cover up till midnight of the next day,
+# But we don't want that to appear in the labels, so pop it off.
+pop( @{ $data[0] } );
+
+</%INIT>
diff --git a/rt/html/RTx/Statistics/DayOfWeek/Elements/Chart b/rt/html/RTx/Statistics/DayOfWeek/Elements/Chart
new file mode 100755
index 0000000..239c095
--- /dev/null
+++ b/rt/html/RTx/Statistics/DayOfWeek/Elements/Chart
@@ -0,0 +1,26 @@
+% $r->content_type("image/$format");
+% $m->print($graph->plot(\@data)->$format());
+% $m->abort();
+<&|/l, $#data+1&>[_1] Elements</&>:<p>
+% for (0..$#data) {
+<% $data[$_] %><p>
+% }
+<%INIT>
+use GD::Graph::bars;
+
+my @data;
+my $graph = GD::Graph::bars->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Day of Week',
+ y_label => 'Ticket actions per Day by type');
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/DayOfWeek/index.html b/rt/html/RTx/Statistics/DayOfWeek/index.html
new file mode 100755
index 0000000..2e82b9c
--- /dev/null
+++ b/rt/html/RTx/Statistics/DayOfWeek/index.html
@@ -0,0 +1,155 @@
+<& /Elements/Header, Title =>loc('Tickets by Day Of Week in Queue:' . $QueueObj->Name()) &>
+<& /RTx/Statistics/Elements/Tabs, Title =>loc('Trends in ticket status by Day Of Week in Queue:' . $QueueObj->Name()) &>
+
+<h3>Description</h3>
+<p>The purpose of this page is to show historical trends for each day of the week.
+It displays details of number of tickets created in your
+selected queue for each day. It also hows how many of those created tickets were Resolved or Deleted</p>
+
+<form method="POST" action="index.html">
+
+
+%my $title = "Ticket counts by day of week in " . $QueueObj->Name();
+<&|/Elements/TitleBox,
+ title => $title,
+ title_href => "/RTx/Statistics/DayOfWeek/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@Format,
+ FormatString => $Format,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 1;
+% for my $d (0..$#days) {
+% my $x = 1;
+% $values{Statistics_Date} = $days[$d];
+%# NOTE Show all status values???
+% $values{Statistics_Created_Count} = $counts[$d]{new};
+% $values{Statistics_Resolved_Count} = $counts[$d]{resolved};
+% $values{Statistics_Deleted_Count} = $counts[$d]{deleted};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+% }
+% $values {Statistics_Date} = "Totals";
+% $values {Statistics_Created_Count} = $Totals{new};
+% $values {Statistics_Resolved_Count} = $Totals{resolved};
+% $values {Statistics_Deleted_Count} = $Totals{deleted};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldFormat, i => $line, record => $record, maxitems => $maxitems &>
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+my $url = 'Elements/Chart?&x_labels=';
+for (0..$#days) {
+ $url .= $days[$_] . "," ;
+}
+chop $url;
+$url .= "&";
+
+my @things = qw(new resolved deleted);
+for my $th (0..$#things) {
+ $url .= "data".(1+$th)."=".(join ",", map { $counts[$_]{$things[$th]} } (0..6))."&";
+}
+chop $url;
+$url .= '&set_legend=Created,Resolved,Deleted';
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+% Statistics::DebugLog("queue name=" . $QueueObj->Id() . "\n");
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Queue",
+ ShowSingleQueue => 1, Queue => $QueueObj->Id()
+ &>
+
+</form>
+
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+$Queue => $Statistics::DayOfWeekQueue
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use GD::Graph;
+use RTx::Statistics;
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+my %Totals = (
+ resolved => 0,
+ deleted => 0,
+ new => 0
+);
+my $QueryString = "Queue=$Queue";
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+my $Format = qq{ Statistics_Date,
+ '__Statistics_Created_Count__/STYLE:text-align:right;',
+ '__Statistics_Resolved_Count__/STYLE:text-align:right;',
+ '__Statistics_Deleted_Count__/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>',
+ '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Deleted_Count__</B>/STYLE:text-align:right;' };
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+$RT::Logger->warning("Loaded queue $Queue, name=". $QueueObj->Name());
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $Queue);
+$tix->UnLimit;
+if ($tix->Count) {
+ # Initialize the counters to zero, so that all the cells show up
+ foreach my $day (0..@days) {
+ $counts[$day]{resolved} = 0;
+ $counts[$day]{deleted} = 0;
+ $counts[$day]{new} = 0;
+ }
+ while (my $t = $tix->RT::SearchBuilder::Next) { # BLOODY HACK
+ if($t->Status eq "resolved") {
+ $counts[(localtime($t->ResolvedObj->Unix))[6]]{resolved}++;
+ $Totals{resolved}++;
+ }
+ if($t->Status eq "deleted") {
+ $counts[(localtime($t->LastUpdatedObj->Unix))[6]]{deleted}++;
+ $Totals{deleted}++;
+ }
+ $counts[(localtime($t->CreatedObj->Unix))[6]]{new}++;
+ $Totals{new}++;
+ }
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/DurationAsString b/rt/html/RTx/Statistics/DurationAsString
new file mode 100755
index 0000000..c0b4d9a
--- /dev/null
+++ b/rt/html/RTx/Statistics/DurationAsString
@@ -0,0 +1,18 @@
+<%$days|'00'%> days <%$hours|'00'%>:<%$minutes|'00'%>
+<%INIT>
+
+my $MINUTE = 60;
+my $HOUR = $MINUTE*60;
+my $DAY = $HOUR * 24;
+my $WEEK = $DAY * 7;
+my $days = int($Duration / $DAY);
+$Duration = $Duration % $DAY;
+my $hours = int($Duration / $HOUR);
+$hours = sprintf("%02d", $hours);
+$Duration = $Duration % $HOUR;
+my $minutes = int($Duration/$MINUTE);
+$minutes = sprintf("%02d", $minutes);
+</%INIT>
+<%ARGS>
+$Duration => undef
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/Elements/CollectionAsTable/Header b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Header
new file mode 100644
index 0000000..cecb02e
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Header
@@ -0,0 +1,126 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+@Format => undef
+$FormatString => undef
+$AllowSorting => undef
+$Order=>undef
+$BaseURL => undef
+$Query => undef
+$Rows => undef
+$Page => undef
+$maxitems => undef
+</%ARGS>
+<TR class="collection-as-table">
+<%perl>
+
+my %generic_query_args = ( Query => $Query, Rows => $Rows, Page => $Page, Format => $FormatString );
+
+my $item = 0;
+foreach my $col (@Format) {
+ $item++;
+ if ( $col->{title} eq 'NEWLINE' ) {
+ while ( $item < $maxitems ) {
+ $m->out(qq{<th class="collection-as-table">&nbsp;</th>\n});
+ $item++;
+ }
+
+ $item = 0;
+ $m->out(qq{</TR>\n<TR class="collection-as-table">});
+ }
+ else {
+ $m->out('<TH class="collection-as-table" ');
+ $m->out( 'align="' . $col->{align} . '"' ) if ( $col->{align} );
+ $m->out( 'style="' . $col->{style} . '"' ) if ( $col->{style} );
+ $m->out('>');
+ my $title = $col->{title};
+ $title =~ s/^__(.*)__$/$1/o;
+ $title = (
+ $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $title,
+ Attr => 'title'
+ )
+ || $title
+ );
+ if (
+ $AllowSorting
+ && $col->{'attribute'}
+ && $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $col->{'attribute'},
+ Attr => 'attribute'
+ )
+ )
+ {
+
+ $m->out(
+ '<a href="' . $BaseURL
+ . $m->comp(
+ '/Elements/QueryString',
+ %generic_query_args,
+ OrderBy => (
+ $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $col->{'attribute'},
+ Attr => 'attribute'
+ )
+ || $col->{'attribute'}
+ ),
+ Order => ( $ARGS{'Order'} eq 'ASC' ? 'DESC' : 'ASC' )
+ )
+ . '">'
+ . loc($title) . '</a>'
+ );
+ }
+ else {
+ $m->out( loc($title) );
+ }
+ $m->out('</TH>');
+ }
+}
+</%perl>
+</TR>
diff --git a/rt/html/RTx/Statistics/Elements/CollectionAsTable/ParseFormat b/rt/html/RTx/Statistics/Elements/CollectionAsTable/ParseFormat
new file mode 100644
index 0000000..a482f81
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/CollectionAsTable/ParseFormat
@@ -0,0 +1,109 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Format
+</%ARGS>
+
+<%init>
+use Regexp::Common;
+my @Columns;
+
+while ($Format =~ /($RE{delimited}{-delim=>qq{\'"}}|[{}\w.]+)/go) {
+ my $col = $1;
+
+ if ($col =~ /^$RE{quoted}$/o) {
+ substr($col,0,1) = "";
+ substr($col,-1,1) = "";
+ }
+
+ my $colref;
+
+ # kfh at mqsoftware.com added this to be able
+ # to create columns where the actual heading and value
+ # aren't know ahead of time. For instance queue names.
+ # it will work with subcols, but all subcols will have the same KEY
+ if ( $col =~ s!/KEY:([^/]+)!!io ) {
+ $colref->{'keyname'} = $1;
+ }
+ if ( $col =~ s!/STYLE:([^/]+)!!io ) {
+ $colref->{'style'} = $1;
+ }
+ if ( $col =~ s!/CLASS:([^/]+)!!io ) {
+ $colref->{'class'} = $1;
+ }
+ if ( $col =~ s!/TITLE:([^/]+)!!io ) {
+ $colref->{'title'} = $1;
+ }
+ if ( $col =~ s!/ALIGN:([^\/]+)!!io ) {
+ $colref->{'align'} = $1;
+ }
+ if ( $col =~ /__(.*?)__/gio ) {
+ my @subcols;
+ while ( $col =~ s/^(.*?)__(.*?)__//o ) {
+ push ( @subcols, $1 ) if ($1);
+ push ( @subcols, "__$2__" );
+ $colref->{'attribute'} = $2;
+ }
+ push ( @subcols, $col );
+ @{ $colref->{'output'} } = @subcols;
+ }
+ else {
+ @{ $colref->{'output'} } = ( "__" . $col . "__" );
+ $colref->{'attribute'} = $col;
+ }
+
+ if ( !$colref->{'title'} && grep { /^__(.*?)__$/io }
+ @{ $colref->{'output'} } )
+ {
+ $colref->{'title'} = $1;
+ $colref->{'attribute'} = $1;
+ }
+
+
+ push @Columns, $colref;
+}
+ return(@Columns);
+</%init>
diff --git a/rt/html/RTx/Statistics/Elements/CollectionAsTable/Row b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Row
new file mode 100644
index 0000000..bcfabe5
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Row
@@ -0,0 +1,112 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$i => undef
+@Format => undef
+$record => undef
+$maxitems => undef
+$Depth => undef
+$Warning => undef
+</%ARGS>
+
+<%PERL>
+$m->out('<TR class="' . ( $Warning ? 'warnline' : $i % 2 ? 'oddline' : 'evenline' ) . '" >' );
+my $item;
+foreach my $column (@Format) {
+ if ( $column->{title} eq 'NEWLINE' ) {
+ while ( $item < $maxitems ) {
+ $m->out(qq{<td class="collection-as-table">&nbsp;</td>\n});
+ $item++;
+ }
+ $item = 0;
+ $m->out('</TR>');
+ $m->out('<TR class="'
+ . ( $Warning ? 'warnline' : $i % 2 ? 'oddline' : 'evenline' )
+ . '" >' );
+ next;
+ }
+ $item++;
+ $m->out('<td class="collection-as-table" ');
+ $m->out( 'align="' . $column->{align} . '"' ) if ( $column->{align} );
+ $m->out( 'style="' . $column->{style} . '"' ) if ( $column->{style} );
+ $m->out('>');
+ foreach my $subcol ( @{ $column->{output} } ) {
+ if ( $subcol =~ /^__(.*?)__$/o ) {
+ my $col = $1;
+ my $value = $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $col,
+ Attr => 'value'
+ );
+ my @out;
+
+ if ( $value && ref($value) ) {
+
+ # All HTML snippets are returned by the callback function
+ # as scalar references. Data fetched from the objects are
+ # plain scalars, and needs to be escaped properly.
+ @out =
+ map {
+ ref($_) ? $$_ : $m->interp->apply_escapes( $_ => 'h' )
+ } &{$value}( $record, $i, $column->{keyname} );
+ ;
+ }
+ else {
+
+ # Simple value; just escape it.
+ @out = $m->interp->apply_escapes( $value => 'h' );
+ }
+ s/\n/<br>/gs for @out;
+ $m->out( @out );
+ }
+ else {
+ $m->out($subcol);
+ }
+ }
+ $m->out('</td>');
+}
+$m->out('</TR>');
+</%PERL>
diff --git a/rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox b/rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox
new file mode 100644
index 0000000..ce043e2
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox
@@ -0,0 +1,103 @@
+<table class="box" bgcolor="#336699" style="border-style:none solid solid solid;border-width:1px;border-color:#2E2E8C;" cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr>
+ <th style="color: rgb(51, 102, 153);" class="titlebox">
+ <span class="titleboxclose">
+ <a href="#" onclick="hideshow('stats_control')">X</a></span>&nbsp;
+ <span class="titleboxtitle" style="color: rgb(255, 255, 255);">
+ <b><% $Title %></b></span>
+ </th>
+ <th style="color: rgb(51, 102, 153);" class="titleboxright">
+ <span class="titleboxright">&nbsp;</span>
+ </th>
+ </tr>
+ <tr id="element-stats_control">
+ <td colspan="3" class="" bgcolor="#dddddd">
+ <table border="0" cellpadding="1" cellspacing="0">
+% if (defined $ShowStatus) {
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Show Status:</td>
+ <td COLSPAN=3 class="collection-as-table" style="text-align:left;">
+ <& /Elements/SelectStatus, Name=>"status", Default => $Status, DefaultValue => undef &>
+ </td>
+ </tr>
+% }
+% if (defined $ShowSingleQueue) {
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Show Queue:</td>
+ <td COLSPAN=3 class="collection-as-table" style="text-align:left;">
+ <& /Elements/SelectQueue, Name=>"Queue", Default=>$Queue ,ShowNullOption=>0,
+ CheckQueueRight=>'SeeQueue' &>
+ </td>
+ </tr>
+% }
+% if (defined $ShowDates) {
+ <tr>
+ <& /RTx/Statistics/Elements/DateSelectRow, Label => "Start Date:",
+ refMonth => $sMonth, nameMonth => "sMonth",
+ refDay => $sDay, nameDay => "sDay",
+ refYear => $sYear, nameYear => "sYear" &>
+ </tr>
+ <tr>
+ <& /RTx/Statistics/Elements/DateSelectRow, Label => "End Date:",
+ refMonth => $eMonth, nameMonth => "eMonth",
+ refDay => $eDay, nameDay => "eDay",
+ refYear => $eYear, nameYear => "eYear" &>
+ </tr>
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Show Weekends:</td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=weekends>
+ <option value=0 <% (!$weekends) && 'selected' %> >No</option>
+ <option value=1 <% $weekends && 'selected' %> >Yes</option>
+ </select>
+ </td>
+ </tr>
+% }
+% if (defined $ShowMultiQueues) {
+ <tr>
+% if (defined $ShowDates) {
+%# If we're showing the dates, we put these side by side.
+ <td COLSPAN=2 class="collection-as-table" style="text-align:left;">Select All Queues: <input type=checkbox name="AddAllCheck"></td>
+ <td COLSPAN=3 class="collection-as-table" >
+ <& /RTx/Statistics/Elements/SelectMultiQueue, Name=>"queues", Selected=>$queues_ref,
+ ShowNullOption=>0, CheckQueueRight=>'SeeQueue', Size => 10, NamedValues => 1 &>
+ </td>
+% } else {
+ <td COLSPAN=3 class="collection-as-table" style="text-align:left;">
+ <& /RTx/Statistics/Elements/SelectMultiQueue, Name=>"queues", Selected=>$queues_ref,
+ ShowNullOption=>0, CheckQueueRight=>'SeeQueue', Size => 10, NamedValues => 1 &>
+ </td>
+ </tr>
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Select All Queues: <input type=checkbox name="AddAllCheck"></td>
+% }
+ </tr>
+% }
+ <& /RTx/Statistics/Elements/ControlsAsTable/UpdatePage &>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<BR>
+<%args>
+$Title => undef
+$ShowMultiQueues => undef
+$queues_ref => undef
+$ShowDates => undef
+$sMonth => undef
+$sDay => undef
+$sYear => undef
+$eMonth => undef
+$eDay => undef
+$eYear => undef
+$weekends => undef
+$ShowSingleQueue => undef
+$Queue => undef
+$ShowStatus => undef
+$Status => undef
+</%args>
+
diff --git a/rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage b/rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage
new file mode 100644
index 0000000..b4ccfd5
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage
@@ -0,0 +1,5 @@
+<tr>
+ <td colspan="4" style="text-align:center;padding-top:3px; background-color:#C8C8C8;">
+ <INPUT TYPE="submit" VALUE="<&|/l&>Update Page</&>">
+ </td>
+</tr>
diff --git a/rt/html/RTx/Statistics/Elements/DateSelectRow b/rt/html/RTx/Statistics/Elements/DateSelectRow
new file mode 100644
index 0000000..325e168
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/DateSelectRow
@@ -0,0 +1,55 @@
+ <td class="collection-as-table" style="text-align:left;"><% $Label %></td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=<% $nameMonth %> >
+% for ($n=0;$n<=$#Statistics::months;$n++){
+% if ($$refMonth eq $n){
+% $selected ="selected";
+% }else {
+% $selected ="";
+% }
+ <option value=<% $n %> <% $selected %> ><% $Statistics::months[$n] %></option>
+%}
+ </select>
+ </td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=<% $nameDay %> >
+% for ($n=1;$n<=31;$n++){
+% if ($$refDay == $n ){
+% $selected ="selected";
+% }else {
+% $selected ="";
+% }
+ <option value=<% $n %> <% $selected %> ><% $n %></option>
+% }
+ </select>
+ </td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=<% $nameYear %> >
+%
+% for ($n=0;$n <= scalar @Statistics::years-1;$n++){
+% if ($Statistics::years[$n] == $$refYear){
+% $selected ="selected";
+% }else{
+% $selected ="";
+% }
+ <option value=<% $Statistics::years[$n] %> <% $selected %> ><% $Statistics::years[$n] %></option>
+% }
+ </select>
+ </td>
+
+
+<%args>
+$Label => undef
+$refMonth => undef
+$nameMonth => undef
+$refDay => undef
+$nameDay => undef
+$refYear => undef
+$nameYear => undef
+</%args>
+<%init>
+use RTx::Statistics;
+my $n;
+my $selected;
+
+</%init>
diff --git a/rt/html/RTx/Statistics/Elements/DurationAsString b/rt/html/RTx/Statistics/Elements/DurationAsString
new file mode 100755
index 0000000..c0b4d9a
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/DurationAsString
@@ -0,0 +1,18 @@
+<%$days|'00'%> days <%$hours|'00'%>:<%$minutes|'00'%>
+<%INIT>
+
+my $MINUTE = 60;
+my $HOUR = $MINUTE*60;
+my $DAY = $HOUR * 24;
+my $WEEK = $DAY * 7;
+my $days = int($Duration / $DAY);
+$Duration = $Duration % $DAY;
+my $hours = int($Duration / $HOUR);
+$hours = sprintf("%02d", $hours);
+$Duration = $Duration % $HOUR;
+my $minutes = int($Duration/$MINUTE);
+$minutes = sprintf("%02d", $minutes);
+</%INIT>
+<%ARGS>
+$Duration => undef
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/Elements/GraphBox b/rt/html/RTx/Statistics/Elements/GraphBox
new file mode 100644
index 0000000..3dc0697
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/GraphBox
@@ -0,0 +1,27 @@
+<div style="float:left; padding-right:30px;">
+<table class="box" bgcolor="#336699" style="border-style:none solid solid solid;border-width:1px;border-color:#2E2E8C;" cellpadding="0" cellspacing="0">
+ <tbody><tr>
+ <th style="color: rgb(51, 102, 153);" class="titlebox">
+ <span class="titleboxclose">
+ <a href="#" onclick="hideshow('stats_chart')">X</a></span>&nbsp;
+
+ <span class="titleboxtitle">
+ <b><a href="<% $GraphURL %>">Download Chart as Image</a></b>
+ </span>
+ </th>
+ <th style="color: rgb(51, 102, 153);" class="titleboxright">
+ <span class="titleboxright">&nbsp;</span>
+ </th>
+ </tr>
+
+ <tr id="element-stats_chart">
+ <td colspan="3" class="" bgcolor="#dddddd">
+ <img src="<% $GraphURL %>" ALT="Result Graph" >
+ </td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<%args>
+$GraphURL => undef
+</%args>
diff --git a/rt/html/RTx/Statistics/Elements/SelectMultiQueue b/rt/html/RTx/Statistics/Elements/SelectMultiQueue
new file mode 100755
index 0000000..637f6dc
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/SelectMultiQueue
@@ -0,0 +1,81 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<SELECT NAME ="<%$Name%>" multiple size="<% $Size %>">
+% if ($ShowNullOption) {
+<OPTION VALUE="">-</OPTION>
+% }
+% while (my $queue=$q->Next) {
+% if ($ShowAllQueues || $queue->CurrentUserHasRight($CheckQueueRight)) {
+% my $targ="," . $queue->Name . ",";
+<OPTION VALUE="<%($NamedValues ? $queue->Name : $queue->Id) %>" <%( ($sel_list =~ m/$targ/) ? 'SELECTED' : '')%>><%$queue->Name%>
+% if (($Verbose) and ($queue->Description) ){
+(<%$queue->Description%>)
+% }
+</OPTION>
+% }
+% }
+</SELECT>
+<%ARGS>
+$CheckQueueRight => 'CreateTicket'
+$ShowNullOption => 1
+$ShowAllQueues => 1
+$Name => undef
+$Verbose => undef
+$NamedValues => 0
+$Selected => undef # ref to array containing selected queue names
+$Lite => 0
+$Size => 5
+</%ARGS>
+
+<%INIT>
+
+# put list of queue names into string, starting and ending with commas
+my $sel_list = "," . join(",", @$Selected) . ",";
+
+my $q=new RT::Queues($session{'CurrentUser'});
+$q->UnLimit;
+
+</%INIT>
diff --git a/rt/html/RTx/Statistics/Elements/StatColumnMap b/rt/html/RTx/Statistics/Elements/StatColumnMap
new file mode 100644
index 0000000..aef9e2f
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/StatColumnMap
@@ -0,0 +1,173 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Name => undef
+$Attr => undef
+</%ARGS>
+
+
+<%ONCE>
+our ( $STAT_COLUMN_MAP );
+
+sub StatColumnMap {
+ my $name = shift;
+ my $attr = shift;
+
+ # First deal with the simple things from the map
+ if ( $STAT_COLUMN_MAP->{$name} ) {
+ return ( $STAT_COLUMN_MAP->{$name}->{$attr} );
+ }
+
+ # now, let's deal with harder things, like Custom Fields
+
+ elsif ( $name =~ /^(?:CF|CustomField)\.\{(.+)\}$/ ) {
+ my $field = $1;
+
+ if ( $attr eq 'attribute' ) {
+ return (undef);
+ }
+ elsif ( $attr eq 'title' ) {
+ return ( $field );
+ }
+ elsif ( $attr eq 'value' ) {
+ # Display custom field contents, separated by newlines.
+ # For Image custom fields we also show a thumbnail here.
+ return sub {
+ my $values = $_[0]->CustomFieldValues($field);
+ return map {
+ (
+ ($_->CustomFieldObj->Type eq 'Image')
+ ? \($m->scomp( '/Elements/ShowCustomFieldImage', Object => $_ ))
+ : $_->Content
+ ),
+ \'<br>',
+ } @{ $values->ItemsArrayRef }
+ };
+ }
+ }
+}
+
+sub LinkCallback {
+ my $method = shift;
+
+ my $mode = $RT::Ticket::LINKTYPEMAP{$method}{Mode};
+ my $type = $RT::Ticket::LINKTYPEMAP{$method}{Type};
+ my $mode_uri = $mode.'URI';
+ my $local_type = 'Local'.$mode;
+
+ return sub {
+ map {
+ \'<A HREF="',
+ $_->$mode_uri->Resolver->HREF,
+ \'">',
+ ( $_->$mode_uri->IsLocal ? $_->$local_type : $_->$mode ),
+ \'</A><BR>',
+ } @{ $_[0]->Links($mode,$type)->ItemsArrayRef }
+ }
+}
+
+$STAT_COLUMN_MAP = {
+ LastUpdated => {
+ attribute => 'LastUpdated',
+ title => 'Last Updated',
+ value => sub { return $_[0]->LastUpdatedObj->AsString }
+ },
+
+ Statistics_Date => {
+ title => 'Date',
+ value => sub { return $_[0]{values}{Statistics_Date} }
+ },
+
+ Statistics_Created_Count => {
+ title => 'Created',
+ value => sub { return $_[0]{values}{Statistics_Created_Count} }
+ },
+
+ Statistics_Resolved_Count => {
+ title => 'Resolved',
+ value => sub { return $_[0]{values}{Statistics_Resolved_Count} }
+ },
+
+ Statistics_Deleted_Count => {
+ title => 'Deleted',
+ value => sub { return $_[0]{values}{Statistics_Deleted_Count} }
+ },
+
+ Statistics_Totals => {
+ title => 'Totals',
+ value => sub { return $_[0]{values}{Statistics_Totals} }
+ },
+
+ Statistics_Status => {
+ title => 'Status',
+ value => sub { return $_[0]{values}{Statistics_Status} }
+ },
+
+ Statistics_Dynamic => {
+ # Depends on having a KEY as second param
+ value => sub {
+ my $record = shift;
+ my $line = shift;
+ my $key = shift;
+ return $$record{values}{$key}
+ }
+ },
+
+ # Everything from LINKTYPEMAP
+ (map {
+ $_ => { value => LinkCallback( $_ ) }
+ } keys %RT::Ticket::LINKTYPEMAP),
+
+ '_CLASS' => {
+ value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
+ },
+
+};
+</%ONCE>
+<%init>
+$m->comp( '/Elements/Callback', STAT_COLUMN_MAP => $STAT_COLUMN_MAP, _CallbackName => 'StatColumnMap');
+return StatColumnMap($Name, $Attr);
+</%init>
diff --git a/rt/html/RTx/Statistics/Elements/Tabs b/rt/html/RTx/Statistics/Elements/Tabs
new file mode 100755
index 0000000..4fde113
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/Tabs
@@ -0,0 +1,72 @@
+%# BEGIN LICENSE BLOCK
+%#
+%# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+%#
+%# (Except where explictly superceded by other copyright notices)
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# Unless otherwise specified, all modifications, corrections or
+%# extensions to this work which alter its source code become the
+%# property of Best Practical Solutions, LLC when submitted for
+%# inclusion in the work.
+%#
+%#
+%# END LICENSE BLOCK
+<& /Elements/Tabs,
+ tabs => $tabs,
+ current_toptab => 'RTx/Statistics/index.html',
+ current_tab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+ my $tabs = { A => { title => loc('Tickets per Day'),
+ path => 'RTx/Statistics/CallsQueueDay/index.html',
+ },
+ B => { title => loc('Tickets by status'),
+ path => 'RTx/Statistics/OpenStalled/index.html',
+ },
+ C => { title => loc('Multiple Queues'),
+ path => 'RTx/Statistics/CallsMultiQueue/index.html',
+ },
+ D => { title => loc('Ticket Trends by Day'),
+ path => 'RTx/Statistics/DayOfWeek/index.html',
+ },
+ E => { 'title' => loc('Time to Resolve'),
+ path => 'RTx/Statistics/Resolution/index.html',
+ },
+ F => { 'title' => loc('Resolve Time Graph'),
+ path => 'RTx/Statistics/TimeToResolve/index.html',
+ },
+ Z => { 'title' => loc('FAQ'),
+ path => 'RTx/Statistics/FAQ/index.html',
+ },
+ };
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+ foreach my $tab (sort keys %{$tabs}) {
+ if ($tabs->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+ }
+
+</%INIT>
+
+
+<%ARGS>
+$subtabs => undef
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/FAQ/index.html b/rt/html/RTx/Statistics/FAQ/index.html
new file mode 100644
index 0000000..e7839ea
--- /dev/null
+++ b/rt/html/RTx/Statistics/FAQ/index.html
@@ -0,0 +1,23 @@
+<& /Elements/Header, Title => 'FAQ and known issues' &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("FAQ and Known Issues") &>
+<hr noshade size="1">
+<p>This page will be used to contain known issues and FAQ`s for the Statistics
+package<br />
+This will also be used to clarify limitations of the package as they stand.</p>
+
+<p><strong>What Version of the Statistics package is this?</strong></p>
+<p>0.1.8</p>
+
+<p><strong>What time zone are the charts set to?</strong></p>
+<p>Because of the new programming method of the date functions, the charts are currently built in GMT(UTC). This may once again be
+customisable in a future release.</p>
+
+<p><strong>What is the default date period and queue?</strong></p>
+<p>The default date period is the previous 10 days, except where the chart is over a fixed 7 day period. The default queue is either
+General, or another queue set in your local configuration.</p>
+
+<p><strong>What are the limitations of the date function?</strong></p>
+<p>It has few, but it will not let you chose less than one day. you cannot select an end date before the start date and it is not
+recommended to select a date in the future or an illegal date, such at 30th February. Code has been put in place to trap these, but it may
+not be fool proof.</p>
+<hr size="1" noshade>
diff --git a/rt/html/RTx/Statistics/OpenStalled/Elements/Chart b/rt/html/RTx/Statistics/OpenStalled/Elements/Chart
new file mode 100755
index 0000000..9505881
--- /dev/null
+++ b/rt/html/RTx/Statistics/OpenStalled/Elements/Chart
@@ -0,0 +1,27 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::bars;
+
+my @data;
+my $graph = GD::Graph::bars->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Queue name',
+ y_label => 'Total per queue by status');
+my $format = $graph->export_format;
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/OpenStalled/Results.tsv b/rt/html/RTx/Statistics/OpenStalled/Results.tsv
new file mode 100644
index 0000000..2ec1e0c
--- /dev/null
+++ b/rt/html/RTx/Statistics/OpenStalled/Results.tsv
@@ -0,0 +1,114 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+@queues => @Statistics::OpenStalledQueueList
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+
+ my $n = 0;
+ my @data;
+ my @msgs;
+ my %totals;
+ my $QueryString;
+ my $now = new RT::Date($session{CurrentUser});
+ my $tix = new RT::Tickets($session{'CurrentUser'});
+
+ my %queues = map {
+ $_ => 1;
+ } (@queues);
+
+ # set content type
+ $r->content_type('application/vnd.ms-excel');
+
+ $QueryString = "queues=" . join("&queues=", @queues);
+
+ my $queue = new RT::Queues($session{CurrentUser});
+ $queue->UnLimit;
+
+ my $QueueObj = new RT::Queue($session{'CurrentUser'});
+ $QueueObj->Load($queue);
+
+ # Put out some data about the generation of this file
+ $m->out("Tickets by Status by Queue for Queues:\t" . join(',', @queues) . "\tGenerated at:\t" . Statistics::FormatDate("%x %X", $now). "\n\n");
+
+ # basically the same as index.html
+
+ # Output header row
+ $m->out("Status");
+ for ( sort keys %queues) {
+ push @data, $_;
+ my $Queueobj = new RT::Queue($session{'CurrentUser'});
+ $Queueobj->Load($_);
+ next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+ $m->out("\t" . $_);
+ }
+ $m->out("\tTotals\n");
+
+ foreach my $s (qw(new open stalled)) {
+ $m->out("$s");
+ my $total=0;
+ foreach my $q (sort keys %queues) {
+ $tix = new RT::Tickets($session{'CurrentUser'});
+ $tix->LimitQueue(VALUE => "$q");
+ $tix->LimitStatus(VALUE => "$s");
+ $totals{$q} += $tix->Count; # Add up columns for each queue
+ $m->out("\t" . $tix->Count);
+ $total += $tix->Count;
+ }
+ $m->out("\t$total\n");
+ $totals{"Totals"} += $total;
+ }
+ $m->out("Totals");
+ foreach my $q (sort keys %queues) {
+ $m->out("\t" . $totals{$q});
+ }
+ $m->out("\t" . $totals{"Totals"} . "\n");
+
+ $m->abort();
+</%INIT>
diff --git a/rt/html/RTx/Statistics/OpenStalled/index.html b/rt/html/RTx/Statistics/OpenStalled/index.html
new file mode 100755
index 0000000..d0cd9f1
--- /dev/null
+++ b/rt/html/RTx/Statistics/OpenStalled/index.html
@@ -0,0 +1,188 @@
+<& /Elements/Header, Title => loc('New, Open and Stalled tickets by Queue') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('New, Open and Stalled tickets by Queue') &>
+
+<h3>Description</h3>
+<p>The purpose of this page is to show a snapshot of the current status of tickets by Queue. You can multi select Queues from the dropdown
+list or simply show all available queues. This will indicate how many tickets have not yet been viewed (New), how many have been at least
+viewed once (Open) and how many have had their status changed to stalled.</p>
+
+<form method="POST" action="index.html">
+
+%my $tix = new RT::Tickets($session{'CurrentUser'});
+%if ($queue) {
+% $tix->LimitQueue (VALUE => $queue);
+%}
+
+
+%my $title = "New, Open and Stalled Tickets in " . join(', ', @queues);
+<& /Elements/TitleBoxStart, title => $title, title_href => "/RTx/Statistics/OpenStalled/index.html?$QueryString"&>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH="100%">
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@RowFormat,
+ FormatString => $RowFormat,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+
+% for ( sort keys %queues_to_show) {
+% push @data, $_;
+% }
+% my @legend;
+% my $total = 0;
+% my $line = 0;
+%# NOTE need to handle all status values (see share/html/Elements/SelectStatus).
+% foreach my $s (qw(new open stalled)) {
+% $line++;
+% push @legend, $s;
+% $total=0;
+% foreach my $q (sort keys %queues_to_show) {
+% $tix = new RT::Tickets($session{'CurrentUser'});
+% $tix->LimitQueue(VALUE => "$q");
+% $tix->LimitStatus(VALUE => "$s");
+% push @data, $tix->Count;
+% $totals{$q} += $tix->Count; # Add up columns for each queue
+% $total += $tix->Count;
+% $values{$q} = $tix->Count;
+% }
+% $totals{"Totals"} += $total;
+% $values{Statistics_Status} = $s;
+% $values{Statistics_Totals} = $total;
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@RowFormat, i => $line, record => $record, maxitems => $maxitems &>
+% }
+% $values{Statistics_Status} = "Totals";
+% foreach my $q (sort keys %queues_to_show) {
+% $values{$q} = $totals{$q};
+% }
+% $values{Statistics_Totals} = $totals{"Totals"};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldRowFormat, i => $line+1, record => $record, maxitems => $maxitems &>
+</table>
+<& /Elements/TitleBoxEnd&>
+
+<hr>
+
+<BR />
+<BR />
+
+% use Data::Dumper;
+% Statistics::DebugLog("Dump of data array is " . Dumper(@data) . "\n");
+% my $url = 'Elements/Chart?x_labels=';
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% chop $url;
+% $url .= '&data1=' ;
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% chop $url;
+% $url .= '&data2=' ;
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% chop $url;
+% $url .= '&data3=' ;
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% $url .= '&set_legend='.(join ",", @legend);
+
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox, Title => "Select Queues", ShowMultiQueues => 1, queues_ref => \@queues &>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/OpenStalled/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a>
+%# | <a href="<%$RT::WebPath%>/RTx/Statistics/OpenStalled/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+</FORM>
+
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+@queues => @Statistics::OpenStalledQueueList
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+$AddAllCheck => undef
+</%ARGS>
+
+<%INIT>
+ use RTx::Statistics;
+
+ my $n = 0;
+ my @data;
+ my @msgs;
+ my %totals;
+ my $QueryString;
+ my %queues_to_show;
+ my $maxitems;
+ my $RowFormat;
+ my $BoldRowFormat;
+ my %record;
+ my %values;
+ my $record = \%record;
+
+ $record{values} = \%values;
+
+ Statistics::DebugClear();
+
+ # Handle the Add All Checkbox
+ if($AddAllCheck eq "on") {
+ $AddAllCheck = undef;
+ undef (@queues);
+ my $q=new RT::Queues($session{'CurrentUser'});
+ $q->UnLimit;
+ while (my $queue=$q->Next) {
+ next if !$queue->CurrentUserHasRight('SeeQueue');
+ push @queues, $queue->Name;
+ }
+ }
+
+ # If the user has the right to see the queue, put it into the map
+ for my $q (@queues) {
+ my $Queueobj = new RT::Queue($session{'CurrentUser'});
+ $Queueobj->Load($q);
+ next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+ $queues_to_show{$q} = 1;
+ }
+
+ $maxitems = (scalar @queues) + 2;
+
+ # Build the new query string
+ $QueryString = "queues=" . join("&queues=", @queues);
+
+ # Build the format strings
+ $RowFormat = "'__Statistics_Status__'";
+ $BoldRowFormat = "'<B>__Statistics_Status__</B>'";
+ for my $q (@queues) {
+ $RowFormat .= ",'__Statistics_Dynamic__/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Dynamic__</B>/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ }
+ $RowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ # Parse the formats into structures.
+ my (@RowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $RowFormat);
+ my (@BoldRowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldRowFormat);
+
+
+ my $queue = new RT::Queues($session{CurrentUser});
+ $queue->UnLimit;
+
+ my $QueueObj = new RT::Queue($session{'CurrentUser'});
+ $QueueObj->Load($queue);
+
+</%INIT>
diff --git a/rt/html/RTx/Statistics/Resolution/Elements/Chart b/rt/html/RTx/Statistics/Resolution/Elements/Chart
new file mode 100755
index 0000000..fa0ac55
--- /dev/null
+++ b/rt/html/RTx/Statistics/Resolution/Elements/Chart
@@ -0,0 +1,29 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Days',
+ y_label => 'Average time in Days');
+
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+#$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/Resolution/index.html b/rt/html/RTx/Statistics/Resolution/index.html
new file mode 100644
index 0000000..d9885b0
--- /dev/null
+++ b/rt/html/RTx/Statistics/Resolution/index.html
@@ -0,0 +1,269 @@
+<& /Elements/Header, Title => 'Time to Resolution' &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("Time To Resolve tickets by Queue for : " .$QueueObj->Name()) &>
+<h3>Description</h3>
+<p>This page shows details of resolution of tickets in the selected queue. It displays tickets created on each day in your selected date
+range. Of those tickets created on that day, how many have been resolved and the total time it has taken for all tickets created on that
+day to be resolved.</p>
+<p>At the bottom of the chart is shows total time taken to resolve all tickets
+in the selected date range and the average time per ticket to
+resolve.</p>
+
+<form method="POST" action="index.html">
+
+%my $title = "Time to resolve in " . $QueueObj->Name() . " per day from " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+<&|/Elements/TitleBox,
+ title => $title,
+ title_href => "/RTx/Statistics/Resolution/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@Format,
+ FormatString => $Format,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 1;
+% LINE: for my $d (0..$#dates ) {
+% if ($d == $#dates ){
+% next LINE;
+% }
+% my $x = 1;
+% $values{Statistics_Date} = Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]);
+% my $tix = new RT::Tickets($session{'CurrentUser'});
+% $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% if ($Queue) {
+% $tix->LimitQueue (VALUE => $Queue);
+% }
+% $values{Statistics_Created_Count} = $tix->Count;
+% $tix->LimitStatus(VALUE => "resolved");
+% $values{Statistics_Resolved_Count} = $tix->Count;
+% if ($tix->Count) {
+% my @tix = @{$tix->ItemsArrayRef};
+% my $total;
+% $total += ($_->ResolvedObj->Unix - $_->CreatedObj->Unix) for @tix;
+% $size+= ($#tix +1);
+% $grandtotal += $total;
+% $values{Duration} = Statistics::DurationAsString($total);
+% $data[$x++][$d] = int ($total );
+% } else {
+% $values{Duration} = "N/A";
+% }
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+%}
+% $size =1 if $size==0;
+% $values{text} = "Average time to resolve = " . Statistics::DurationAsString($grandtotal / $size);
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@OneCellFormat, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+% $values{text} = "Total time to resolve = " . Statistics::DurationAsString( $grandtotal );
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@OneCellFormat, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+# Create the graph URL
+
+# change the total time to resolve to a floating point number of days
+foreach my $dat(@{$data[1]} ){
+ $dat = ($dat / $Statistics::secsPerDay);
+ $dat = sprintf("%0.4f", $dat);
+}
+
+my $url = 'Elements/Chart?x_labels=';
+for (0..$diff-1) {
+ $url .= $data[0][$_] . ",";
+}
+chop $url;
+shift @data;
+$url .= "&data1=";
+for(0..$diff-1) {
+ $data[0][$_] = 0 if !$data[0][$_];
+ $url .= $data[0][$_] . ",";
+}
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Queue or Dates",
+ ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+ eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+ weekends => $weekends,
+ ShowSingleQueue => 1, Queue => $Queue
+ &>
+
+</form>
+
+<%ARGS>
+$max => $Statistics::TimeToResolveMaxRows
+$Queue => undef
+$weekends =>$Statistics::TimeToResolveWeekends
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $size;
+my $selected;
+my $grandtotal = 0;
+my $diff;
+my $sEpoch=0;
+my $eEpoch=0;
+my $QueryString;
+
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+
+# If debugging, set things up and display all the args
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $Format = qq{ Statistics_Date,
+ '__Statistics_Created_Count__/STYLE:text-align:right;',
+ '__Statistics_Resolved_Count__/STYLE:text-align:right;',
+ '__Statistics_Dynamic__/KEY:Duration/TITLE:Time To Resolve/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>',
+ '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Dynamic__</B>/KEY:Duration/TITLE:Time To Resolve/STYLE:text-align:right;' };
+
+# TODO need way to make this cell do colspan
+my $OneCellFormat = qq{ '<B>__Statistics_Dynamic__</B>/KEY:text/STYLE:text-align:left;','','','' };
+
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+my (@OneCellFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $OneCellFormat);
+
+Statistics::DebugLog("CallsQueueDay/index.html Format array=" . join(',', @Format) . "\n");
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+if (!defined $Queue) {
+ $QueueObj->Load($Statistics::TimeToResolveQueue);
+ $Queue = $QueueObj->Id();
+}
+
+# Set up the string for the current query for bookmarkable link
+$QueryString = "sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends&Queue=$Queue";
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+$QueueObj->Load($Queue);
+# NOTE: list loop starts at the end of the date range, unshifting dates onto
+# the arrays, so that they end up in start to finish order.
+$eEpoch += $Statistics::secsPerDay;
+$n = 0;
+until ($#dates == $diff ) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+ unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/TimeToResolve/Elements/Chart b/rt/html/RTx/Statistics/TimeToResolve/Elements/Chart
new file mode 100755
index 0000000..a069a7b
--- /dev/null
+++ b/rt/html/RTx/Statistics/TimeToResolve/Elements/Chart
@@ -0,0 +1,23 @@
+<%perl>
+print $graph->plot(\@data)->$format();
+$m->abort();
+</%perl>
+<%INIT>
+use GD::Graph::points;
+
+my @data;
+my $graph = GD::Graph::points->new(400,300);
+$graph->set(export_format => "png",
+ marker_size => $ARGS{marker_size},
+ x_label => 'Average time to resolve (Days)',
+ y_label => 'Number of tickets resolved' );
+#$r->content_type("image/$format");
+my $format = $graph->export_format;
+push @data, [split /,/ , $ARGS{x_labels}];
+for (1..((scalar keys %ARGS)-2)) {
+ push @data, [split /,/ , $ARGS{"data".$_}];
+}
+
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/TimeToResolve/index.html b/rt/html/RTx/Statistics/TimeToResolve/index.html
new file mode 100755
index 0000000..2124b53
--- /dev/null
+++ b/rt/html/RTx/Statistics/TimeToResolve/index.html
@@ -0,0 +1,75 @@
+<& /Elements/Header, Title => 'Time to Resolve in Queue' &>
+<& /RTx/Statistics/Elements/Tabs, Title => 'Time to Resolve, by ticket in Queue:' . $QueueObj->Name() &>
+
+<h3>Description</h3>
+<p>This page displays the same information as the Time to Resolve chart, but in a scattergraph format and only for the previous 7 calendar
+days. It only displays data for tickets which have been resolved. Each division on the Days axis is one day and the granularity of this chart
+is 30 minutes.</p>
+<form method="POST">
+
+<table>
+ <tr>
+ <td>Show Queue:</td>
+ <td COLSPAN=3><& /Elements/SelectQueue, Name=>"queue", Default=>$queue ,ShowNullOption=>0,
+ CheckQueueRight=>'SeeQueue' &></td>
+ </tr>
+</table>
+<INPUT TYPE="submit" VALUE="Update Page"</INPUT>
+</form>
+
+<BR>
+% my $url = 'Elements/Chart?x_labels=';
+% my $i;
+% $url .= join ",", (map {(int($_/2) == $_/2 && (++$i)%2) ? $_/2 : ""} grep {$counts[$_]} 0..($#counts-1)), "longer";
+% $url .= '&';
+% $url .= "marker_size=1&";
+% $url .= "data1=".(join ",", map { $_ || () } @counts)."&";
+% chop $url;
+<IMG SRC="<% $url %>">
+
+<BR>
+
+%Statistics::DebugInit($m);
+
+<%ARGS>
+$queue => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+
+Statistics::DebugClear();
+Statistics::DebugLog("TimeToResolve/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+if (!defined $queue) {
+ $QueueObj->Load($Statistics::TimeToResolveGraphQueue);
+ $queue = $QueueObj->Id();
+} else {
+ $QueueObj->Load($queue);
+}
+
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $queue) if $queue;
+$tix->LimitStatus(VALUE => "resolved");
+$tix->UnLimit;
+if ($tix->Count) {
+ while (my $t = $tix->RT::SearchBuilder::Next) { # BLOODY HACK
+ my $when = $t->ResolvedObj->Unix - $t->CreatedObj->Unix;
+ next unless $when > 0; # Doubly bloody hack
+ my $max = (60*60*24*2) / 1800;
+ my $x = int($when / 1800);
+ $counts[$x > $max ? $max : $x]++;
+ }
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/UserTest/Elements/Chart b/rt/html/RTx/Statistics/UserTest/Elements/Chart
new file mode 100755
index 0000000..99eb2a2
--- /dev/null
+++ b/rt/html/RTx/Statistics/UserTest/Elements/Chart
@@ -0,0 +1,28 @@
+<%perl>
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new(640,480);
+$graph->set(export_format => "png",
+ x_label => 'Days',
+ y_label => 'Average time in Days');
+
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+#$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/UserTest/index.html b/rt/html/RTx/Statistics/UserTest/index.html
new file mode 100755
index 0000000..7bc25da
--- /dev/null
+++ b/rt/html/RTx/Statistics/UserTest/index.html
@@ -0,0 +1,54 @@
+<& /Elements/Header, Title => 'Time to Resolve in Queue' &>
+<& /RTx/Statistics/Elements/Tabs, Title => 'Time to Resolve, by ticket in Queue:' . $QueueObj->Name() &>
+
+
+<form method="POST">
+
+See Queue:<BR>
+<& /Elements/SelectQueue, Name=>"queue", Default => "$queue" &>
+<BR>
+<INPUT TYPE="submit" VALUE="Go!"</INPUT>
+</form>
+
+<BR>
+% my $url = 'Elements/Chart?x_labels=';
+% my $i;
+% $url .= join ",", (map {(int($_/2) == $_/2 && (++$i)%2) ? $_/2 : ""} grep {$counts[$_]} 0..($#counts-1)), "longer";
+% $url .= '&';
+% $url .= "marker_size=1&";
+% $url .= "data1=".(join ",", map { $_ || () } @counts)."&";
+% chop $url;
+<IMG SRC="<% $url %>">
+
+<BR>
+
+<%ARGS>
+$queue => $Statistics::TimeToResolveGraphQueue;
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($queue);
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $queue) if $queue;
+$tix->LimitStatus(VALUE => "resolved");
+$tix->UnLimit;
+if ($tix->Count) {
+ while (my $t = $tix->RT::SearchBuilder::Next) { # BLOODY HACK
+ my $when = $t->ResolvedObj->Unix - $t->CreatedObj->Unix;
+ next unless $when > 0; # Doubly bloody hack
+ my $max = (60*60*24*2) / 1800;
+ my $x = int($when / 1800);
+ $counts[$x > $max ? $max : $x]++;
+ }
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/index.html b/rt/html/RTx/Statistics/index.html
new file mode 100755
index 0000000..41490de
--- /dev/null
+++ b/rt/html/RTx/Statistics/index.html
@@ -0,0 +1,59 @@
+%# BEGIN LICENSE BLOCK
+%#
+%# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+%#
+%# (Except where explictly superceded by other copyright notices)
+%#
+%# Copyright this file (c) 2003 Harald Wagener <hwagener@hamburg.fcb.com>
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# Unless otherwise specified, all modifications, corrections or
+%# extensions to this work which alter its source code become the
+%# property of Best Practical Solutions, LLC when submitted for
+%# inclusion in the work.
+%#
+%#
+%# END LICENSE BLOCK
+<& /Elements/Header, Title => loc('RT Statistics') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('RT Statistics') &>
+
+<&|/l&><h2>Description</h2>
+<p>These 6 options below enable you to display management data from the RT Database in table and graphical forms, enabling trends, bottlenecks, load problems etc to be identified.
+Each contains a description of how the data is displayed and describes the options available to you.</p></&>
+<ul>
+<li><strong><a href="CallsQueueDay/index.html">
+<&|/l&>Tickets per day per Queue</&></a></strong><br />
+<&|/l&>View the number of tickets created, resolved or deleted in a<br /> specific Queue, over the requested period of days</&>
+</li>
+<li><strong><a href="OpenStalled/index.html">
+<&|/l&>Tickets status by Queue</&></a></strong><br>
+<&|/l&>View numbers of new, open and stalled tickets in a selected Queue</&>
+</li>
+<li><strong><a href="CallsMultiQueue/index.html">
+<&|/l&>Tickets per Day in Multiple Queues</&>
+</a></strong><br>
+<&|/l&>View tickets created, resolved or deleted on in one or more Queues<br /> over a specified time period</&>
+</li>
+<li><strong><a href="DayOfWeek/index.html">
+<&|/l&>Tickets per Day of Week (absolute)</&></a></strong><br>
+<&|/l&>View trends showing when tickets are created, resolved or deleted</&>
+</li>
+<li><strong><a href="Resolution/index.html">
+<&|/l&>Time to Resolve</&></a></strong><br>
+<&|/l&>View how long tickets take to be resolved by Queue</&>
+</li>
+</li>
+<li><strong><a href="TimeToResolve/index.html">
+<&|/l&>Time to Resolve (scatter graph)</&></a></strong><br>
+<&|/l&>View a detailed scatter graph of time to resolve tickets by Queue</&>
+</li>
+</ul>
diff --git a/rt/html/Reports/Activity/ActivityDetail.html b/rt/html/Reports/Activity/ActivityDetail.html
new file mode 100644
index 0000000..ef0d830
--- /dev/null
+++ b/rt/html/Reports/Activity/ActivityDetail.html
@@ -0,0 +1,83 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Activity detail"),
+ path => "Reports/Activity/ActivityDetail.html",
+ &>
+
+<& Elements/MiniPlot, data => \%counts &>
+
+<table style="width: 100%">
+<tr class="titlerow">
+<th>Queue</th><th>Activity</th><th>Date</th><th>Time</th><th>Ticket #</th><th>User</th><th>Short description</th>
+</tr>
+% for my $item (@items) {
+<tr>
+<td><% $item->{queue} %></td>
+<td><% $item->{status} %></td>
+<td><% $item->{date} %></td>
+<td><% $item->{time} %></td>
+<td><% $item->{id} %></td>
+<td><% $item->{actor} %></td>
+<td><% $item->{notes} %></td>
+</tr>
+% }
+</table>
+
+</&>
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+
+my $summary_tickets = RT::Tickets->new($session{'CurrentUser'});
+$summary_tickets->FromSQL($query . " AND ( Updated >= '$start' AND Updated <= '$end')");
+my %counts;
+while (my $ticket = $summary_tickets->Next) {
+ my $txns = $ticket->Transactions;
+ $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start);
+ $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end);
+ # I think they really don't just want status changes
+ $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR');
+ $txns->Limit(FIELD => 'Type', VALUE => 'Create');
+
+ while (my $txn = $txns->Next){
+ my $date = substr($txn->Created, 0, 10);
+ # we don't have data on the status of a new ticket, default to 'new'
+ $counts{$date}{$txn->NewValue || 'new'}++;
+ }
+}
+
+
+my $tickets = RT::Tickets->new($session{'CurrentUser'});
+$tickets->FromSQL($query);
+my @items;
+while (my $ticket = $tickets->Next) {
+ my $txns = $ticket->Transactions;
+ $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start);
+ $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end);
+ # I think they really don't just want status changes
+ $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR');
+ $txns->Limit(FIELD => 'Type', VALUE => 'Create');
+
+ while (my $txn = $txns->Next) {
+ push @items, { queue => $txn->TicketObj->QueueObj->Name,
+ id => $txn->TicketObj->id,
+ date => (split ' ', $txn->CreatedObj->ISO)[0],
+ time => (split ' ', $txn->CreatedObj->ISO)[1],
+ status => $txn->NewValue || 'new',
+ actor => $txn->CreatorObj->Name,
+ notes => ($txn->Content ne 'This transaction appears to have no content' ? substr($txn->Content, 0, 60) : $txn->BriefDescription)
+ };
+ }
+}
+
+@items = sort {
+ $a->{queue} cmp $b->{'queue'}
+ || $a->{'status'} cmp $b->{'status'}
+ || $a->{'id'} <=> $b->{'id'}
+ || $a->{'actor'} cmp $b->{'actor'}
+ || $a->{'notes'} <=> $b->{'notes'}
+} @items;
+
+</%init>
diff --git a/rt/html/Reports/Activity/ActivitySummary.html b/rt/html/Reports/Activity/ActivitySummary.html
new file mode 100644
index 0000000..7bb756f
--- /dev/null
+++ b/rt/html/Reports/Activity/ActivitySummary.html
@@ -0,0 +1,61 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Activity summary"),
+ path => "Reports/Activity/ActivitySummary.html",
+ &>
+
+<& Elements/MiniPlot, data => \%queues &>
+
+<table style="width: 100%">
+<tr class="titlerow">
+<th>Queue</th>
+% for my $status (sort keys %status) {
+<th><% $status %></th>
+% }
+<th>Total</th>
+</tr>
+% for my $queue (sort keys %queues) {
+<th class="label"><% $queue %></th>
+% for my $status (sort keys %status) {
+<td><% $queues{$queue}{$status} || 0 %>
+% }
+<td><% $total{$queue} %></td>
+</tr>
+% }
+<tr class="grandtotal">
+<th class="label" >Grand Total</th>
+% for my $status (sort keys %status) {
+<td><% $status{$status} %></td>
+% }
+<td><% $total %></td>
+</table>
+</&>
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+my $tickets = RT::Tickets->new($session{'CurrentUser'});
+$tickets->FromSQL($query . " AND ( Updated >= '$start' AND Updated <= '$end')");
+
+my %queues;
+my %status;
+my %total;
+my $total;
+while (my $ticket = $tickets->Next) {
+ my $txns = $ticket->Transactions;
+ $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start);
+ $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end);
+ $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR');
+ $txns->Limit(FIELD => 'Type', VALUE => 'Create');
+
+ while (my $txn = $txns->Next) {
+ $queues{$txn->TicketObj->QueueObj->Name}{$txn->NewValue || 'new'}++;
+ $status{$txn->NewValue || 'new'}++;
+ $total{$txn->TicketObj->QueueObj->Name}++;
+ $total++;
+ }
+}
+
+
+</%init>
diff --git a/rt/html/Reports/Activity/Elements/LimitReport b/rt/html/Reports/Activity/Elements/LimitReport
new file mode 100644
index 0000000..7c4aac7
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/LimitReport
@@ -0,0 +1,23 @@
+<form action="index.html" method="POST" enctype="multipart/form-data">
+Query:
+<textarea name="query" rows="5" cols="80"><% $query %></textarea><br />
+
+Report type: <select name="type">
+<option value="ActivityDetail" <% $ARGS{path} =~ /ActivityDetail/ ? 'selected' : '' %>>Activity detail</option>
+<option value="ActivitySummary" <% $ARGS{path} =~ /ActivitySummary/ ? 'selected' : '' %>>Activity summary</option>
+<option value="ResolutionComments" <% $ARGS{path} =~ /ResolutionComments/ ? 'selected' : '' %>>Resolution comments</option>
+<option value="ResolutionStatistics" <% $ARGS{path} =~ /ResolutionStatistics/ ? 'selected' : '' %>>Resolution statistics</option>
+</select><br />
+
+Start date: <input type="text" name="start" value="<% $start %>" /><br />
+End date: <input type="text" name="end" value="<% $end %>" /><br />
+<& /Elements/Submit, Label => loc('Report') &>
+</form>
+<%args>
+$type => undef
+$start => undef
+$end => undef
+$query => undef
+</%args>
+<%init>
+</%init>
diff --git a/rt/html/Reports/Activity/Elements/MiniPlot b/rt/html/Reports/Activity/Elements/MiniPlot
new file mode 100644
index 0000000..f920328
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/MiniPlot
@@ -0,0 +1,57 @@
+<table class="miniplot"><tr>
+% for my $major (@major) {
+<td><div class="graph">
+ <ul>
+% my $i = 0;
+% for my $minor (@minor) {
+% my $percent = int( 100 * ($data->{$major}{$minor} || 0) / $max );
+ <li class="c<% ($i % 6) + 1%>" style="width: <% $barwidth %>%;
+ left: <% $baroffset + $each * $i %>%;
+ height: <% $percent %>%;"><div class="data"><% $minor %>: <% $percent %>%</div></li>
+% $i++;
+% }
+ </ul>
+</div></td>
+% }
+</tr><tr>
+% for my $major (@major) {
+<th class="legend"><% $major %></th>
+% }
+</tr>
+</table>
+
+<table class="miniplot"><tr>
+% my $i = 0;
+% for my $minor (@minor) {
+<th><span class="demoblock c<% ($i++ % 6) + 1 %>"></span> <% $minor %></th>
+% }
+</tr>
+</table>
+
+<%args>
+$data
+$major => undef
+$minor => undef
+</%args>
+<%init>
+
+my $max = 1;
+
+my %minor;
+for my $major (keys %{$data}) {
+ for (keys %{$data->{$major}}) {
+ $minor{$_}++;
+ $max = $data->{$major}{$_} if $data->{$major}{$_} > $max;
+ }
+}
+
+my @major = $major ? @{$major} : sort keys %{$data};
+my @minor = $minor ? @{$minor} : sort keys %minor;
+
+return unless @minor and @major;
+
+my $each = int( (100 / @minor) );
+my $barwidth = int( (100 / @minor) * (3/4) );
+my $baroffset = int( (100 / @minor) * (1/8) );
+
+</%init>
diff --git a/rt/html/Reports/Activity/Elements/PrintFooter b/rt/html/Reports/Activity/Elements/PrintFooter
new file mode 100644
index 0000000..fa9f475
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/PrintFooter
@@ -0,0 +1,7 @@
+<hr/>
+<div style="text-align: center;">
+<%$RT::ReportFooterMessage || 'Proprietary and Confidential' %>
+</div>
+</body>
+</html>
+%$m->abort();
diff --git a/rt/html/Reports/Activity/Elements/PrintHeader b/rt/html/Reports/Activity/Elements/PrintHeader
new file mode 100644
index 0000000..b7c4b34
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/PrintHeader
@@ -0,0 +1,32 @@
+<%args>
+$title => undef
+$path => undef
+$query => undef
+</%args>
+<HTML>
+<HEAD>
+<TITLE><%$title%></TITLE>
+<link rel="shortcut icon" href="<%$RT::WebImagesURL%>/favicon.png" type="image/png" />
+<link media="all" rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/webrt.css" type="text/css" />
+<link media="print" rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/printrt.css" type="text/css" />
+%# XXX TODO THIS SHOULD NOT BE A TABLE
+<body>
+<table width="100%">
+<tr>
+<td align="left">
+<div id="username">User: <%$session{'CurrentUser'}->Name%></div>
+<div id="reportdate">
+%my $d= RT::Date->new($session{'CurrentUser'}); $d->SetToNow;
+<%$d->AsString%></div>
+</td>
+<td align="center">
+<h1><%$title%></h1>
+</td>
+<td align="right">
+<img src="<%$RT::LogoURL%>" alt="RT Logo"/>
+</td>
+</tr>
+</table>
+<hr/>
+<&|/l&>Report criteria:</&> <%$query%>
+<hr />
diff --git a/rt/html/Reports/Activity/Elements/ScreenFooter b/rt/html/Reports/Activity/Elements/ScreenFooter
new file mode 100644
index 0000000..235b7b3
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/ScreenFooter
@@ -0,0 +1,13 @@
+<& LimitReport, %ARGS &>
+% if ($show_print_link) {
+<div align="right">
+% my %printable_args = %ARGS;
+% delete $printable_args{$_} for (qw/path title mode/);
+% $printable_args{'mode'} = 'print';
+% my $url = $ARGS{'path'} .'?'. join(';', map { $_."=".$printable_args{$_} } keys %printable_args);
+<a href="<%$RT::WebPath|n%>/<%$url|n%>"><&|/l&>Printable version</&></a>
+</div>
+% }
+<%args>
+$show_print_link => 1
+</%args>
diff --git a/rt/html/Reports/Activity/Elements/ScreenHeader b/rt/html/Reports/Activity/Elements/ScreenHeader
new file mode 100644
index 0000000..080efc0
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/ScreenHeader
@@ -0,0 +1,8 @@
+<%args>
+$title => undef
+$path => undef
+</%args>
+<& /Elements/Header, Title => $title &>
+<& Tabs,
+ current_subtab => $path,
+ Title => $title &>
diff --git a/rt/html/Reports/Activity/Elements/Tabs b/rt/html/Reports/Activity/Elements/Tabs
new file mode 100644
index 0000000..a949820
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/Tabs
@@ -0,0 +1,52 @@
+<& /Elements/Tabs,
+ tabs => $tabs,
+ subtabs => $subtabs,
+ current_toptab => 'Tools/Offline.html',
+ current_tab => 'Reports/Activity/index.html'.$args,
+ Title => $Title &>
+
+<%INIT>
+my $subtabs = {};
+
+my $top = $m->caller_args(-1);
+my $args = "?" . $m->comp( '/Elements/QueryString',
+ query => $top->{query},
+ start => $top->{start},
+ end => $top->{end});
+if ($m->caller_args(-1)->{'query'}) {
+ $current_subtab .= $args;
+ $subtabs = {
+ a => { title => 'Activity detail',
+ path => 'Reports/Activity/ActivityDetail.html'.$args,
+ },
+ b => { title => 'Activity summary',
+ path => 'Reports/Activity/ActivitySummary.html'.$args,
+ },
+ c => { title => 'Resolution comments',
+ path => 'Reports/Activity/ResolutionComments.html'.$args,
+ },
+ d => { title => 'Resolution statistics',
+ path => 'Reports/Activity/ResolutionStatistics.html'.$args,
+ },
+ };
+}
+
+my $tabs = {
+ a => { title => loc('Offline'),
+ path => 'Tools/Offline.html',
+ },
+ r => { title => loc('Reports'),
+ path => 'Reports/Activity/index.html'.$args,
+ subtabs => $subtabs,
+ current_subtab => $current_subtab,
+ }
+ };
+
+</%INIT>
+
+
+<%ARGS>
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Reports/Activity/Elements/Wrapper b/rt/html/Reports/Activity/Elements/Wrapper
new file mode 100644
index 0000000..6f81f5f
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/Wrapper
@@ -0,0 +1,16 @@
+<%args>
+$mode => 'screen'
+</%args>
+
+% if ($mode eq 'print') {
+<& PrintHeader, %ARGS &>
+%} else {
+<& ScreenHeader, %ARGS &>
+% }
+<%$m->content |n%>
+% if ($mode eq 'print') {
+<& PrintFooter, %ARGS &>
+%} else {
+<& ScreenFooter, %ARGS &>
+% }
+
diff --git a/rt/html/Reports/Activity/ResolutionComments.html b/rt/html/Reports/Activity/ResolutionComments.html
new file mode 100644
index 0000000..81ca301
--- /dev/null
+++ b/rt/html/Reports/Activity/ResolutionComments.html
@@ -0,0 +1,62 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Resolution Comments"),
+ path => "Reports/Activity/ResolutionComments.html",
+ &>
+
+<table style="width: 100%">
+<tr>
+<th>Queue</th><th>Ticket #</th><th>Created</th><th>Resolved</th><th>Time to resolve</th>
+</tr>
+<tr>
+<th colspan="5">Resolution comments</th>
+</tr>
+% for my $item (@items) {
+<tr class="titlerow">
+<td><% $item->{queue} %></td>
+<td><% $item->{id} %></td>
+<td><% $item->{created} %></td>
+<td><% $item->{resolved} %></td>
+<td><% $item->{duration} %></td>
+</tr>
+<tr>
+<td colspan="5"><% $item->{whiteboard} %></td>
+</tr>
+% }
+</table>
+</&>
+
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+use Time::Duration;
+
+my $summary_tickets = RT::Tickets->new( $session{'CurrentUser'} );
+$summary_tickets->FromSQL(
+ $query . " AND (Status = 'resolved') AND ( Updated >= '$start' AND Updated <= '$end')" );
+
+my @items;
+while ( my $ticket = $summary_tickets->Next ) {
+ push @items, {
+ queue => $ticket->QueueObj->Name,
+ id => $ticket->id,
+ created => $ticket->CreatedObj->AsString,
+ resolved => $ticket->ResolvedObj->AsString,
+ duration => Time::Duration::concise(
+ Time::Duration::duration(
+ $ticket->ResolvedObj->Unix - $ticket->CreatedObj->Unix
+ )
+ ),
+ whiteboard => $ticket->FirstCustomFieldValue('Whiteboard')
+ };
+}
+
+@items = sort { $a->{queue} cmp $b->{queue} || $a->{id} <=> $b->{id} } @items;
+
+
+
+
+
+</%init>
diff --git a/rt/html/Reports/Activity/ResolutionStatistics.html b/rt/html/Reports/Activity/ResolutionStatistics.html
new file mode 100644
index 0000000..4ecde2c
--- /dev/null
+++ b/rt/html/Reports/Activity/ResolutionStatistics.html
@@ -0,0 +1,95 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Resolution statistics"),
+ path => "Reports/Activity/ResolutionStatistics.html",
+ &>
+
+<& Elements/MiniPlot,
+ data => \%plot,
+ major => ['Date range','Last 30 days','Last 60 days','Last 90 days','Ever'],
+ minor => [(sort keys %queues), "Average"]
+ &>
+
+<table style="width: 100%">
+<tr>
+<td></td><th colspan="4">Number of tickets closed / Average resolution time per ticket</th>
+</tr>
+<tr class="titlerow">
+<th>Queue</th>
+<th>Date range</th>
+<th>Last 30 days</th>
+<th>Last 60 days</th>
+<th>Last 90 days</th>
+<th>Ever</th>
+</tr>
+% for my $queue (sort keys %queues) {
+<tr>
+<th><% $queue %></th>
+% for my $period ('Date range','Last 30 days','Last 60 days','Last 90 days','Ever') {
+<td><% scalar @{$closed{$period}{$queue}} %> / <% $average_resolve_times{$period}{$queue} %></td>
+% }
+</tr>
+% }
+<tr class="grandtotal">
+<th>Ticket average</th>
+% for my $period ('Date range','Last 30 days','Last 60 days','Last 90 days','Ever') {
+<td><% $average_resolve_times{$period}{_all_count} %> / <% $average_resolve_times{$period}{_all} %></td>
+% }
+</tr>
+</table>
+
+</&>
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+my $in_30_days = RT::Date->new($session{'CurrentUser'});
+$in_30_days->Set(Format => 'Unix', Value => ( time - (86400*30)));
+my $in_60_days = RT::Date->new($session{'CurrentUser'});
+$in_60_days->Set(Format => 'Unix', Value => ( time - (86400*60)));
+my $in_90_days = RT::Date->new($session{'CurrentUser'});
+$in_90_days->Set(Format => 'Unix', Value => ( time - (86400*90)));
+
+my %queries;
+$queries{'Date range'} = "(Resolved >= '$start' AND Resolved <= '$end')";
+$queries{'Last 30 days'} = "(Resolved >= '".$in_30_days->ISO."')";
+$queries{'Last 60 days'} = "(Resolved >= '".$in_60_days->ISO."')";
+$queries{'Last 90 days'} = "(Resolved >= '".$in_90_days->ISO."')";
+$queries{'Ever'} = "(Status = 'resolved' OR Status = 'rejected')";
+
+
+my %closed;
+my %queues;
+foreach my $period (keys %queries) {
+ my $tix = RT::Tickets->new($session{'CurrentUser'});
+ $tix->FromSQL($query . " AND " .$queries{$period});
+
+ while (my $ticket = $tix->Next) {
+ push @{ $closed{$period}{$ticket->QueueObj->Name}}, $ticket;
+ $queues{$ticket->QueueObj->Name}++;
+ }
+}
+
+my %restimes;
+my %average_resolve_times;
+my %plot;
+use Time::Duration;
+foreach my $period ( keys %closed ) {
+ foreach my $queue ( keys %{$closed{$period}} ) {
+ foreach my $ticket (@{$closed{$period}{$queue}} ) {
+ push @{$restimes{$period}{$queue}}, ( $ticket->ResolvedObj->Unix - $ticket->CreatedObj->Unix);
+ }
+
+ my $total_time = 0;
+ $total_time+= $_ for @{$restimes{$period}{$queue}};
+ $average_resolve_times{$period}{'_all_time'} += $total_time;
+ $average_resolve_times{$period}{'_all_count'} += @{$restimes{$period}{$queue}};
+ $plot{$period}{$queue} = $total_time / @{$restimes{$period}{$queue}};
+ $average_resolve_times{$period}{$queue} = Time::Duration::concise(Time::Duration::duration($plot{$period}{$queue}));
+ }
+ $plot{$period}{Average} = $average_resolve_times{$period}{'_all_time'} / $average_resolve_times{$period}{'_all_count'};
+ $average_resolve_times{$period}{'_all'} = Time::Duration::concise(Time::Duration::duration($plot{$period}{Average}));
+}
+
+</%init>
diff --git a/rt/html/Reports/Activity/index.html b/rt/html/Reports/Activity/index.html
new file mode 100644
index 0000000..1f6ddb0
--- /dev/null
+++ b/rt/html/Reports/Activity/index.html
@@ -0,0 +1,29 @@
+<&| Elements/Wrapper, %ARGS, title => loc("Activity reports"), show_print_link => 0 &>
+
+
+</&>
+
+<%args>
+$type => undef
+$start => undef
+$end => undef
+$query => "Status = 'resolved'"
+</%args>
+<%init>
+
+unless ($start) {
+ my $then = RT::Date->new($session{'CurrentUser'});
+ $then->Set(Format => 'Unix', Value => time - (86400*7));
+ $ARGS{start} = substr($then->ISO,0,10);
+}
+
+unless ($end) {
+ my $now = RT::Date->new($session{'CurrentUser'});
+ $now->SetToNow();
+ $ARGS{end} = substr($now->ISO,0,10);
+}
+
+if ($type) {
+ $m->redirect($type . ".html?" . $m->comp('/Elements/QueryString', query => $query, start => $start, end => $end));
+}
+</%init>
diff --git a/rt/html/Search/Build.html b/rt/html/Search/Build.html
new file mode 100644
index 0000000..2639587
--- /dev/null
+++ b/rt/html/Search/Build.html
@@ -0,0 +1,832 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%#
+%# Data flow here:
+%# The page receives a Query from the previous page, and maybe arguments
+%# corresponding to actions. (If it doesn't get a Query argument, it pulls
+%# one out of the session hash. Also, it could be getting just a raw query from
+%# Build/Edit.html (Advanced).)
+%#
+%# After doing some stuff with default arguments and saved searches, the ParseQuery
+%# function (which is similar to, but not the same as, _parser in RT/Tickets_Overlay_SQL)
+%# converts the Query into a RT::Interface::Web::QueryBuilder::Tree. This mason file
+%# then adds stuff to or modifies the tree based on the actions that had been requested
+%# by clicking buttons. It then calls GetQueryAndOptionList on the tree to generate
+%# the SQL query (which is saved as a hidden input) and the option list for the Clauses
+%# box in the top right corner.
+%#
+%# Worthwhile refactoring: the tree manipulation code for the actions could use some cleaning
+%# up. The node-adding code is different in the "add" actions from in ParseQuery, which leads
+%# to things like ParseQuery correctly not quoting numbers in numerical fields, while the "add"
+%# action does quote it (this breaks SQLite).
+%#
+<& /Elements/Header, Title => $title &>
+<& /Ticket/Elements/Tabs,
+ current_tab => "Search/Build.html".$QueryString,
+ Title => $title,
+ Format => $Format,
+ Query => $Query,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ Rows => $RowsPerPage
+&>
+
+<form method="post" action="Build.html" name="BuildQuery">
+<input type="hidden" class="hidden" name="SearchId" value="<%$SearchId%>" />
+<input type="hidden" class="hidden" name="Query" value="<%$Query%>" />
+<input type="hidden" class="hidden" name="Format" value="<%$Format%>" />
+<table width="100%" border="0" cellpadding="5">
+<tr valign="top">
+<td class="boxcontainer" rowspan="2" width="65%">
+<& Elements/PickCriteria, query => $Query, cfqueues => $queues &>
+<& /Elements/Submit, Caption => loc('Add these terms to your search'), Label => loc('Add'), Name => 'AddClause'&>
+</td>
+
+<td>
+<& Elements/EditQuery,
+ %ARGS,
+ actions => \@actions,
+ optionlist => $optionlist,
+ Description => $Description &>
+<& /Elements/Submit, Label => loc('Add and Search'), Name => 'DoSearch'&>
+</td>
+</tr>
+
+<tr valign="top">
+<td>
+<& Elements/EditSearches, CurrentSearch => $search_hash, Dirty => $dirty, SearchId => $SearchId &>
+</td>
+</tr>
+
+<tr>
+<td colspan="2" class="boxcontainer">
+
+<& Elements/DisplayOptions, %ARGS, Format=> $Format,
+AvailableColumns => $AvailableColumns, CurrentFormat => $CurrentFormat, RowsPerPage => $RowsPerPage, OrderBy => $OrderBy, Order => $Order &>
+<& /Elements/Submit, Label => loc('Add and Search'), Name => 'DoSearch'&>
+</td>
+</tr>
+</table>
+</form>
+
+<%INIT>
+use RT::Interface::Web::QueryBuilder;
+use RT::Interface::Web::QueryBuilder::Tree;
+
+my $search_hash = {};
+my $search;
+my $title = loc("Query Builder");
+
+# {{{ Clear out unwanted data
+if ( $NewQuery or $ARGS{'Delete'} ) {
+
+ # Wipe all data-carrying variables clear if we want a new
+ # search, or we're deleting an old one..
+ $Query = '';
+ $Format = '';
+ $Description = '';
+ $SearchId = '';
+ $Order = '';
+ $OrderBy = '';
+ $RowsPerPage = undef;
+
+ # ($search hasn't been set yet; no need to clear)
+
+ # ..then wipe the session out..
+ undef $session{'CurrentSearchHash'};
+
+ # ..and the search results.
+ $session{'tickets'}->CleanSlate() if defined $session{'tickets'};
+}
+
+# }}}
+
+if (ref $OrderBy eq "ARRAY") {
+ $OrderBy = join("|", @$OrderBy);
+}
+if (ref $Order eq "ARRAY") {
+ $Order = join("|", @$Order);
+}
+
+# {{{ Attempt to load what we can from the session, set defaults
+
+# We don't read or write to the session again until the end
+$search_hash = $session{'CurrentSearchHash'};
+
+# Read from user preferences
+my $prefs = $session{'CurrentUser'}->UserObj->Preferences("SearchDisplay") || {};
+
+# These variables are what define a search_hash; this is also
+# where we give sane defaults.
+$Query ||= $search_hash->{'Query'};
+$Format ||= $search_hash->{'Format'} || $prefs->{'Format'};
+$Description ||= $search_hash->{'Description'};
+$SearchId ||= $search_hash->{'SearchId'} || 'new';
+$Order ||= $search_hash->{'Order'} || $prefs->{'Order'} || 'ASC';
+$OrderBy ||= $search_hash->{'OrderBy'} || $prefs->{'OrderBy'} || 'id';
+
+unless ( defined $RowsPerPage ) {
+ if ( defined $search_hash->{'RowsPerPage'} ) {
+ $RowsPerPage = $search_hash->{'RowsPerPage'};
+ }
+ elsif ( defined $prefs->{'RowsPerPage'} ) {
+ $RowsPerPage = $prefs->{'RowsPerPage'};
+ }
+ else {
+ $RowsPerPage = 50;
+ }
+}
+
+ $search ||= $search_hash->{'Object'};
+
+# }}}
+
+my @actions = ();
+
+# Clean unwanted junk from the format
+$Format = $m->comp( '/Elements/ScrubHTML', Content => $Format ) if ($Format);
+
+# {{{ If we're asked to delete the current search, make it go away and reset the search parameters
+if ( $ARGS{'Delete'} ) {
+
+ # We set $SearchId to 'new' above already, so peek into the %ARGS
+ my ($container_object, $search_id) = _parse_saved_search ($ARGS{'SearchId'});
+ if ($container_object && $container_object->id) {
+ # We have the object the entry is an attribute on; delete the
+ # entry..
+ $container_object->Attributes->DeleteEntry(
+ Name => 'SavedSearch',
+ id => $search_id
+ );
+ }
+}
+
+# }}}
+
+# {{{ If the user wants to copy a search, uncouple from the one that this was based on, but don't erase the $Query or $Format
+if ( $ARGS{'CopySearch'} ) {
+ $SearchId = 'new';
+ $search = undef;
+ $Description = loc( "[_1] copy", $Description );
+}
+
+# }}}
+
+# {{{ if we're asked to revert the current search, we just want to load it
+if ( $ARGS{'Revert'} ) {
+ $ARGS{'LoadSavedSearch'} = $SearchId;
+}
+
+# }}}
+
+# {{{ if we're asked to load a search, load it.
+
+if ( my ($container_object, $search_id ) = _parse_saved_search ($ARGS{'LoadSavedSearch'})) {
+ $search = $container_object->Attributes->WithId($search_id);
+
+ # We have a $search and now; import the others
+ $SearchId = $ARGS{'LoadSavedSearch'};
+ $Description = $search->Description;
+ $Format = $search->SubValue('Format');
+ $Query = $search->SubValue('Query');
+ $Order = $search->SubValue('Order');
+ $OrderBy = $search->SubValue('OrderBy');
+ $RowsPerPage = $search->SubValue('RowsPerPage');
+}
+
+# }}}
+
+# {{{ if we're asked to save the current search, save it
+if ( $ARGS{'Save'} ) {
+ if ( $search && $search->id ) {
+ # permission check
+ if ($search->Object->isa('RT::System')) {
+ unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'SuperUser')) {
+ Abort("No permission to save system-wide searches");
+ }
+ }
+
+ # This search is based on a previously loaded search -- so
+ # just update the current search object with new values
+ $search->SetSubValues(
+ Format => $Format,
+ Query => $Query,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ RowsPerPage => $RowsPerPage,
+ );
+ $search->SetDescription($Description);
+
+ }
+ elsif ( $SearchId eq 'new' ) {
+ my $saved_search = RT::SavedSearch->new( $session{'CurrentUser'} );
+ my ( $ok, $search_msg ) = $saved_search->Save(
+ Privacy => $ARGS{'Owner'},
+ Name => $Description,
+ SearchParams => {
+ Format => $Format,
+ Query => $Query,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ RowsPerPage => $RowsPerPage } );
+
+ if ($ok) {
+ $search = $session{'CurrentUser'}->UserObj->Attributes->WithId($saved_search->Id);
+ # Build new SearchId
+ $SearchId =
+ ref( $session{'CurrentUser'}->UserObj ) . '-'
+ . $session{'CurrentUser'}->UserObj->Id
+ . '-SavedSearch-'
+ . $search->Id;
+ }
+ else {
+ push @actions, [ loc("Can't find a saved search to work with").': '.loc($search_msg), 0 ];
+ }
+ }
+ else {
+ push @actions, [ loc("Can't save this search"), 0 ];
+ }
+
+}
+
+# }}}
+
+
+# {{{ Parse the query
+use Regexp::Common qw /delimited/;
+
+# States
+use constant VALUE => 1;
+use constant AGGREG => 2;
+use constant OP => 4;
+use constant PAREN => 8;
+use constant KEYWORD => 16;
+
+my $_match = sub {
+
+ # Case insensitive equality
+ my ( $y, $x ) = @_;
+ return 1 if $x =~ /^$y$/i;
+
+ # return 1 if ((lc $x) eq (lc $y)); # Why isnt this equiv?
+ return 0;
+};
+
+my $ParseQuery = sub {
+ my $string = shift;
+ my $tree = shift;
+ my $actions = shift;
+ my $want = KEYWORD | PAREN;
+ my $last = undef;
+
+ my $depth = 1;
+
+ # make a tree root
+ $$tree = RT::Interface::Web::QueryBuilder::Tree->new;
+ my $root = RT::Interface::Web::QueryBuilder::Tree->new( 'AND', $$tree );
+ my $parentnode = $root;
+
+ # on new searches, we're passed undef but still need to construct the
+ # RT::Interface::Web::QueryBuilder::Tree. Quiet warning
+ return unless defined $string;
+
+ # get the FIELDS from Tickets_Overlay
+ my $tickets = new RT::Tickets( $session{'CurrentUser'} );
+ my %FIELDS = %{ $tickets->FIELDS };
+
+ # Lower Case version of FIELDS, for case insensitivity
+ my %lcfields = map { ( lc($_) => $_ ) } ( keys %FIELDS );
+
+ my @tokens = qw[VALUE AGGREG OP PAREN KEYWORD];
+ my $re_aggreg = qr[(?i:AND|OR)];
+ my $re_value = qr[$RE{delimited}{-delim=>qq{\'\"}}|\d+];
+ my $re_keyword = qr[$RE{delimited}{-delim=>qq{\'\"}}|(?:\{|\}|\w|\.)+];
+ my $re_op =
+ qr[=|!=|>=|<=|>|<|(?i:IS NOT)|(?i:IS)|(?i:NOT LIKE)|(?i:LIKE)]
+ ; # long to short
+ my $re_paren = qr'\(|\)';
+
+ # assume that $ea is AND if it is not set
+ my ( $ea, $key, $op, $value ) = ( "AND", "", "", "" );
+
+ # order of matches in the RE is important.. op should come early,
+ # because it has spaces in it. otherwise "NOT LIKE" might be parsed
+ # as a keyword or value.
+
+ while (
+ $string =~ /(
+ $re_aggreg
+ |$re_op
+ |$re_keyword
+ |$re_value
+ |$re_paren
+ )/igx
+ )
+ {
+ my $val = $1;
+ my $current = 0;
+
+ # Highest priority is last
+ $current = OP if $_match->( $re_op, $val );
+ $current = VALUE if $_match->( $re_value, $val );
+ $current = KEYWORD
+ if $_match->( $re_keyword, $val ) && ( $want & KEYWORD );
+ $current = AGGREG if $_match->( $re_aggreg, $val );
+ $current = PAREN if $_match->( $re_paren, $val );
+
+ unless ( $current && $want & $current ) {
+
+ # Error
+ # FIXME: I will only print out the highest $want value
+ my $token = $tokens[ ( ( log $want ) / ( log 2 ) ) ];
+ push @$actions,
+ [
+ loc("Error near ->[_1]<- expecting a [_2] in '[_3]'",
+ $val, $token, $string ),
+ -1
+ ];
+ }
+
+ # State Machine:
+ my $parentdepth = $depth;
+
+ # Parens are highest priority
+ if ( $current & PAREN ) {
+ if ( $val eq "(" ) {
+ $depth++;
+
+ # make a new node that the clauses can be children of
+ $parentnode = RT::Interface::Web::QueryBuilder::Tree->new( $ea, $parentnode );
+ }
+ else {
+ $depth--;
+ $parentnode = $parentnode->getParent();
+ }
+
+ $want = KEYWORD | PAREN | AGGREG;
+ }
+ elsif ( $current & AGGREG ) {
+ $ea = $val;
+ $parentnode->setNodeValue($ea);
+ $want = KEYWORD | PAREN;
+ }
+ elsif ( $current & KEYWORD ) {
+ $key = $val;
+ $want = OP;
+ }
+ elsif ( $current & OP ) {
+ $op = $val;
+ $want = VALUE;
+ }
+ elsif ( $current & VALUE ) {
+ $value = $val;
+
+ # Remove surrounding quotes from $key, $val
+ # (in future, simplify as for($key,$val) { action on $_ })
+ if ( $key =~ /$RE{delimited}{-delim=>qq{\'\"}}/ ) {
+ substr( $key, 0, 1 ) = "";
+ substr( $key, -1, 1 ) = "";
+ }
+ if ( $val =~ /$RE{delimited}{-delim=>qq{\'\"}}/ ) {
+ substr( $val, 0, 1 ) = "";
+ substr( $val, -1, 1 ) = "";
+ }
+
+ # Unescape escaped characters
+ $key =~ s!\\(.)!$1!g;
+ $val =~ s!\\(.)!$1!g;
+
+ my $class;
+
+ my ($key_base, $subkey) = split(/\./,$key,2);
+ $key_base =~ s/\..*$//; # Strip off .EmailAddress, for example
+
+ if ( exists $lcfields{lc $key_base } ) {
+ $key = $lcfields{lc $key_base } . (defined $subkey ? '.'.$subkey : '');
+ $class = $FIELDS{$key_base}->[0];
+ }
+ elsif ( $key =~ /^C(?:ustom)?F(?:ield)?\.{(.*)}$/i ) {
+ $class = $FIELDS{'CF'}->[0];
+ }
+
+ if ( $class ne 'INT' ) {
+ $val = "'$val'";
+ }
+
+ push @$actions, [ loc("Unknown field: [_1]", $key), -1 ] unless $class;
+
+ $want = PAREN | AGGREG;
+ }
+ else {
+ push @$actions, [ loc("I'm lost"), -1 ];
+ }
+
+ if ( $current & VALUE ) {
+ if ( $key =~ /^CF./ ) {
+ $key = "'" . $key . "'";
+ }
+ my $clause = {
+ Key => $key,
+ Op => $op,
+ Value => $val
+ };
+
+ # explicity add a child to it
+ RT::Interface::Web::QueryBuilder::Tree->new( $clause, $parentnode );
+
+ ( $ea, $key, $op, $value ) = ( "", "", "", "" );
+
+ }
+
+ $last = $current;
+ } # while
+
+ push @$actions, [ loc("Incomplete query"), -1 ]
+ unless ( ( $want | PAREN ) || ( $want | KEYWORD ) );
+
+ push @$actions, [ loc("Incomplete Query"), -1 ]
+ unless ( $last && ( $last | PAREN ) || ( $last || VALUE ) );
+
+ # This will never happen, because the parser will complain
+ push @$actions, [ loc("Mismatched parentheses"), -1 ]
+ unless $depth == 1;
+};
+
+my $tree;
+{
+ my @parsing_errors;
+ $ParseQuery->( $Query, \$tree, \@parsing_errors );
+
+ # if parsing went poorly, send them to the edit page
+ # to fix it
+ if ( @parsing_errors ) {
+ return $m->comp(
+ "Edit.html",
+ Query => $Query,
+ actions => \@parsing_errors
+ );
+ }
+}
+
+$Query = "";
+
+my @options = $tree->GetDisplayedNodes;
+
+my @current_values = grep { defined } @options[@clauses];
+
+# {{{ Move things around
+if ( $ARGS{"Up"} ) {
+ if (@current_values) {
+ foreach my $value (@current_values) {
+ my $index = $value->getIndex();
+ if ( $value->getIndex() > 0 ) {
+ my $parent = $value->getParent();
+ $parent->removeChild($index);
+ $parent->insertChild( $index - 1, $value );
+ $value = $parent->getChild( $index - 1 );
+ }
+ else {
+ push( @actions, [ loc("error: can't move up"), -1 ] );
+ }
+ }
+ }
+ else {
+ push( @actions, [ loc("error: nothing to move"), -1 ] );
+ }
+}
+elsif ( $ARGS{"Down"} ) {
+ if (@current_values) {
+ foreach my $value (@current_values) {
+ my $index = $value->getIndex();
+ my $parent = $value->getParent();
+ if ( $value->getIndex() < ( $parent->getChildCount - 1 ) ) {
+ $parent->removeChild($index);
+ $parent->insertChild( $index + 1, $value );
+ $value = $parent->getChild( $index + 1 );
+ }
+ else {
+ push( @actions, [ loc("error: can't move down"), -1 ] );
+ }
+ }
+ }
+ else {
+ push( @actions, [ loc("error: nothing to move"), -1 ] );
+ }
+}
+elsif ( $ARGS{"Left"} ) {
+ if (@current_values) {
+ foreach my $value (@current_values) {
+ my $parent = $value->getParent();
+ my $grandparent = $parent->getParent();
+ if ( !$grandparent->isRoot ) {
+ my $index = $parent->getIndex();
+ $parent->removeChild($value);
+ $grandparent->insertChild( $index, $value );
+ if ( $parent->isLeaf() ) {
+ $grandparent->removeChild($parent);
+ }
+ }
+ else {
+ push( @actions, [ loc("error: can't move left"), -1 ] );
+ }
+ }
+ }
+ else {
+ push( @actions, [ loc("error: nothing to move"), -1 ] );
+ }
+}
+elsif ( $ARGS{"Right"} ) {
+ if (@current_values) {
+ foreach my $value (@current_values) {
+ my $parent = $value->getParent();
+ my $index = $value->getIndex();
+ my $newparent;
+ if ( $index > 0 ) {
+ my $sibling = $parent->getChild( $index - 1 );
+ if ( ref( $sibling->getNodeValue ) ) {
+ $parent->removeChild($value);
+ my $newtree = RT::Interface::Web::QueryBuilder::Tree->new( 'AND', $parent );
+ $newtree->addChild($value);
+ }
+ else {
+ $parent->removeChild($index);
+ $sibling->addChild($value);
+ }
+ }
+ else {
+ $parent->removeChild($value);
+ $newparent = RT::Interface::Web::QueryBuilder::Tree->new( 'AND', $parent );
+ $newparent->addChild($value);
+ }
+ }
+ }
+ else {
+ push( @actions, [ loc("error: nothing to move"), -1 ] );
+ }
+}
+elsif ( $ARGS{"DeleteClause"} ) {
+ if (@current_values) {
+ $_->getParent()->removeChild($_) for @current_values;
+ @current_values = ();
+ }
+ else {
+ push( @actions, [ loc("error: nothing to delete"), -1 ] );
+ }
+}
+elsif ( $ARGS{"Toggle"} ) {
+ my $ea;
+ if (@current_values) {
+ foreach my $value (@current_values) {
+ my $parent = $value->getParent();
+
+ if ( $parent->getNodeValue eq 'AND' ) {
+ $parent->setNodeValue('OR');
+ }
+ else {
+ $parent->setNodeValue('AND');
+ }
+ }
+ }
+ else {
+ push( @actions, [ loc("error: nothing to toggle"), -1 ] );
+ }
+}
+
+# {{{ Try to find if we're adding a clause
+foreach my $arg ( keys %ARGS ) {
+ if (
+ $arg =~ m/^ValueOf(\w+|'CF.{.*?}')$/
+ && ( ref $ARGS{$arg} eq "ARRAY"
+ ? grep { $_ ne "" } @{ $ARGS{$arg} }
+ : $ARGS{$arg} ne "" )
+ )
+ {
+
+ # We're adding a $1 clause
+ my $field = $1;
+ my ( $keyword, $op, $value );
+
+ #figure out if it's a grouping
+ if ( $ARGS{ $field . "Field" } ) {
+ $keyword = $ARGS{ $field . "Field" };
+ }
+ else {
+ $keyword = $field;
+ }
+
+ my ( @ops, @values );
+ if ( ref $ARGS{ 'ValueOf' . $field } eq "ARRAY" ) {
+
+ # we have many keys/values to iterate over, because there is
+ # more than one CF with the same name.
+ @ops = @{ $ARGS{ $field . 'Op' } };
+ @values = @{ $ARGS{ 'ValueOf' . $field } };
+ }
+ else {
+ @ops = ( $ARGS{ $field . 'Op' } );
+ @values = ( $ARGS{ 'ValueOf' . $field } );
+ }
+ $RT::Logger->error("Bad Parameters passed into Query Builder")
+ unless @ops == @values;
+
+ for my $i ( 0 .. @ops - 1 ) {
+ my ( $op, $value ) = ( $ops[$i], $values[$i] );
+ next if $value eq "";
+
+ if ( $value eq 'NULL' && $op =~ /=/ ) {
+ if ( $op eq '=' ) {
+ $op = "IS";
+ }
+ elsif ( $op eq '!=' ) {
+ $op = "IS NOT";
+ }
+
+ # This isn't "right", but...
+ # It has to be this way until #5182 is fixed
+ $value = "'NULL'";
+ }
+ else {
+ $value = "'$value'";
+ }
+
+ my $clause = {
+ Key => $keyword,
+ Op => $op,
+ Value => $value
+ };
+
+ my $newnode = RT::Interface::Web::QueryBuilder::Tree->new($clause);
+ if (@current_values) {
+ foreach my $value (@current_values) {
+ my $newindex = $value->getIndex() + 1;
+ $value->insertSibling( $newindex, $newnode );
+ $value = $newnode;
+ }
+ }
+ else {
+ $tree->getChild(0)->addChild($newnode);
+ @current_values = $newnode;
+ }
+ $newnode->getParent()->setNodeValue( $ARGS{'AndOr'} );
+ }
+ }
+}
+
+# }}}
+
+$tree->PruneChildlessAggregators;
+
+# }}}
+
+# {{{ Rebuild $Query based on the additions / movements
+$Query = "";
+my $optionlist_arrayref;
+
+($Query, $optionlist_arrayref) = $tree->GetQueryAndOptionList(\@current_values);
+
+my $optionlist = join "\n", map { qq(<option value="$_->{INDEX}" $_->{SELECTED}>)
+ . ("&nbsp;" x (5 * $_->{DEPTH}))
+ . $m->interp->apply_escapes($_->{TEXT}, 'h') . qq(</option>) } @$optionlist_arrayref;
+
+
+
+
+# }}}
+
+# }}}
+
+my $queues = $tree->GetReferencedQueues;
+
+# {{{ Deal with format changes
+my ( $AvailableColumns, $CurrentFormat );
+( $Format, $AvailableColumns, $CurrentFormat ) = $m->comp(
+ 'Elements/BuildFormatString',
+ cfqueues => $queues,
+ %ARGS, Format => $Format
+);
+
+# }}}
+
+# {{{ If we're modifying an old query, check if it has changed
+my $dirty = 0;
+$dirty = 1
+ if defined $search
+ and ($search->SubValue('Format') ne $Format
+ or $search->SubValue('Query') ne $Query
+ or $search->SubValue('Order') ne $Order
+ or $search->SubValue('OrderBy') ne $OrderBy
+ or $search->SubValue('RowsPerPage') ne $RowsPerPage );
+
+# }}}
+
+# {{{ Push the updates into the session so we don't loose 'em
+$search_hash->{'SearchId'} = $SearchId;
+$search_hash->{'Format'} = $Format;
+$search_hash->{'Query'} = $Query;
+$search_hash->{'Description'} = $Description;
+$search_hash->{'Object'} = $search;
+$search_hash->{'Order'} = $Order;
+$search_hash->{'OrderBy'} = $OrderBy;
+$search_hash->{'RowsPerPage'} = $RowsPerPage;
+
+$session{'CurrentSearchHash'} = $search_hash;
+
+# }}}
+
+# {{{ Show the results, if we were asked.
+if ( $ARGS{"DoSearch"}) {
+ $m->comp(
+ "Results.html",
+ Query => $Query,
+ Format => $Format,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ Rows => $RowsPerPage
+ );
+ $m->comp('/Elements/Footer');
+ $m->abort();
+}
+
+# }}}
+
+# {{{ Build a querystring for the tabs
+
+my $QueryString;
+if ($NewQuery) {
+ $QueryString = '?NewQuery=1';
+}
+else {
+ $QueryString = '?'
+ . $m->comp(
+ '/Elements/QueryString',
+ Query => $Query,
+ Format => $Format,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ Rows => $RowsPerPage
+ )
+ if ($Query);
+}
+
+# }}}
+
+</%INIT>
+
+<%ARGS>
+$NewQuery => 0
+$SearchId => undef
+$Query => undef
+$Format => undef
+$Description => undef
+$Order => undef
+$OrderBy => undef
+$RowsPerPage => undef
+$HideResults => 0
+@clauses => ()
+</%ARGS>
+
diff --git a/rt/html/Search/Bulk.html b/rt/html/Search/Bulk.html
new file mode 100644
index 0000000..9742df5
--- /dev/null
+++ b/rt/html/Search/Bulk.html
@@ -0,0 +1,396 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Ticket/Elements/Tabs,
+ current_tab => "Search/Bulk.html",
+ Title => $title,
+ Format => $ARGS{'Format'}, # we don't want the locally modified one
+ Query => $Query,
+ Rows => $Rows,
+ OrderBy => $OrderBy,
+ Order => $Order &>
+
+<& /Elements/ListActions, actions => \@results &>
+<form method="post" action="<%$RT::WebPath%>/Search/Bulk.html" enctype="multipart/form-data">
+% foreach my $var qw(Query Format OrderBy Order Rows Page) {
+<input type="hidden" class="hidden" name="<%$var%>" value="<%$ARGS{$var}%>" />
+%}
+<& /Elements/TicketList, Query => $Query,
+ DisplayFormat => $Format,
+ Format => $ARGS{'Format'},
+ Verbatim => 1,
+ AllowSorting => 1,
+ OrderBy => $OrderBy,
+ Order => $Order,
+ Rows => $Rows,
+ Page => $Page,
+ BaseURL => $RT::WebPath."/Search/Bulk.html?"
+ &>
+
+<hr>
+
+<& /Elements/Submit, Label => loc('Update'), CheckAll => 1, ClearAll => 1 &>
+<br />
+<&|/Widgets/TitleBox, title => $title &>
+<table>
+<tr>
+<td valign="top">
+<table>
+<tr><td class="label"> <&|/l&>Make Owner</&>: </td>
+<td class="value"> <& /Elements/SelectOwner, Name => "Owner" &> (<input type="checkbox" class="checkbox" name="ForceOwnerChange" /> <&|/l&>Force change</&>) </td></tr>
+<tr><td class="label"> <&|/l&>Add Requestor</&>: </td>
+<td class="value"> <input name="AddRequestor" size="20" /> </td></tr>
+<tr><td class="label"> <&|/l&>Remove Requestor</&>: </td>
+<td class="value"> <input name="DeleteRequestor" size="20" /> </td></tr>
+<tr><td class="label"> <&|/l&>Add Cc</&>: </td>
+<td class="value"> <input name="AddCc" size="20" /> </td></tr>
+<tr><td class="label"> <&|/l&>Remove Cc</&>: </td>
+<td class="value"> <input name="DeleteCc" size="20" /> </td></tr>
+<tr><td class="label"> <&|/l&>Add AdminCc</&>: </td>
+<td class="value"> <input name="AddAdminCc" size="20" /> </td></tr>
+<tr><td class="label"> <&|/l&>Remove AdminCc</&>: </td>
+<td class="value"> <input name="DeleteAdminCc" size="20" /> </td></tr>
+</table>
+</td>
+<td valign="top">
+<table>
+<tr><td class="label"> <&|/l&>Make subject</&>: </td>
+<td class="value"> <input name="Subject" size="20" /> </td></tr>
+<tr><td class="label"> <&|/l&>Make priority</&>: </td>
+<td class="value"> <input name="Priority" size="4" /> </td></tr>
+<tr><td class="label"> <&|/l&>Make queue</&>: </td>
+<td class="value"> <& /Elements/SelectQueue, Name => "Queue" &> </td></tr>
+<tr><td class="label"> <&|/l&>Make Status</&>: </td>
+<td class="value"> <& /Elements/SelectStatus, Name => "Status" &> </td></tr>
+<tr><td class="label"> <&|/l&>Make date Starts</&>: </td>
+<td class="value"> <& /Elements/SelectDate, Name => "Starts_Date", ShowTime => 0, Default => '' &> </td></tr>
+<tr><td class="label"> <&|/l&>Make date Started</&>: </td>
+<td class="value"> <& /Elements/SelectDate, Name => "Started_Date", ShowTime => 0, Default => '' &> </td></tr>
+<tr><td class="label"> <&|/l&>Make date Told</&>: </td>
+<td class="value"> <& /Elements/SelectDate, Name => "Told_Date", ShowTime => 0, Default => '' &> </td></tr>
+<tr><td class="label"> <&|/l&>Make date Due</&>: </td>
+<td class="value"> <& /Elements/SelectDate, Name => "Due_Date", ShowTime => 0, Default => '' &> </td></tr>
+<tr><td class="label"> <&|/l&>Make date Resolved</&>: </td>
+<td class="value"> <& /Elements/SelectDate, Name => "Resolved_Date", ShowTime => 0, Default => '' &> </td></tr>
+</table>
+
+</td>
+</tr>
+</table>
+</&>
+<&| /Widgets/TitleBox, title => loc('Add comments or replies to selected tickets') &>
+<table>
+<tr><td align="right"><&|/l&>Update Type</&>:</td>
+<td><select name="UpdateType">
+ <option value="private" ><&|/l&>Comments (not sent to requestors)</&></option>
+<option value="response" ><&|/l&>Reply to requestors</&></option>
+</select>
+</td></tr>
+<tr><td align="right"><&|/l&>Subject</&>:</td><td> <input name="UpdateSubject" size="60" value="" /></td></tr>
+% while (my $CF = $TxnCFs->Next()) {
+<tr>
+<td align="right"><% $CF->Name %>:</td>
+<td><& /Elements/EditCustomField,
+ CustomField => $CF,
+ NamePrefix => "Object-RT::Transaction--CustomField-"
+ &><em><% $CF->FriendlyType %></em></td>
+</td></tr>
+% } # end if while
+ <tr><td align="right"><&|/l&>Attach</&>:</td><td><input name="UpdateAttachment" type="file" /></td></tr>
+ <tr><td class="labeltop"><&|/l&>Message</&>:</td><td>
+ <& /Elements/MessageBox, Name=>"UpdateContent"&>
+ </td></tr>
+ </table>
+
+</&>
+<&|/Widgets/TitleBox, title => loc('Edit Custom Fields'), color => "#336633"&>
+<%perl>
+my $cfs = RT::CustomFields->new($session{'CurrentUser'});
+$cfs->LimitToGlobal();
+$cfs->LimitToQueue($_) for keys %$seen_queues;
+</%perl>
+<table>
+<tr>
+<th><&|/l&>Name</&></th>
+<th><&|/l&>Add values</&></th>
+<th><&|/l&>Delete values</&></th>
+</tr>
+% while (my $cf = $cfs->Next()) {
+<tr>
+<td class="label"><%$cf->Name%><br />
+<em>(<%$cf->FriendlyType%>)</em></td>
+% my $rows = 5;
+% my @add = (NamePrefix => 'Bulk-Add-CustomField-', CustomField => $cf, Rows => $rows, Multiple => ($cf->MaxValues ==1 ? 0 : 1) , Cols => 25);
+% my @del = (NamePrefix => 'Bulk-Delete-CustomField-', CustomField => $cf, Rows => $rows, Multiple => 1, Cols => 25);
+% if ($cf->Type eq 'Select') {
+<td><& /Elements/EditCustomFieldSelect, @add &></td>
+<td><& /Elements/EditCustomFieldSelect, @del &></td>
+% } elsif ($cf->Type eq 'Combobox') {
+<td><& /Elements/EditCustomFieldCombobox, @add &></td>
+<td><& /Elements/EditCustomFieldCombobox, @del &></td>
+% } elsif ($cf->Type eq 'Freeform') {
+<td><& /Elements/EditCustomFieldFreeform, @add &></td>
+<td><& /Elements/EditCustomFieldFreeform, @del &></td>
+% } elsif ($cf->Type eq 'Text') {
+<td><& /Elements/EditCustomFieldText, @add &></td>
+<td>&nbsp;</td>
+% } else {
+% $RT::Logger->crit("Unknown CustomField type: " . $cf->Type);
+% }
+</tr>
+% }
+</table>
+</&>
+
+<&|/Widgets/TitleBox, title => loc('Edit Links'), color => "#336633"&>
+<em><&|/l&>Enter tickets or URIs to link tickets to. Separate multiple entries with spaces.</&></em><br />
+<& /Ticket/Elements/BulkLinks &>
+</&>
+
+<& /Elements/Submit, Label => loc('Update') &>
+
+
+</form>
+
+
+<%INIT>
+my $title = loc("Update multiple tickets");
+
+# Iterate through the ARGS hash and remove anything with a null value.
+map ( $ARGS{$_} =~ /^$/ && ( delete $ARGS{$_} ), keys %ARGS );
+
+my (@results);
+
+$Page ||= 1;
+
+$Format ||= $RT::DefaultSearchResultFormat;
+
+# inject _CHECKBOX to the first field.
+$Format =~ s/'?([^']+)'?,/'___CHECKBOX__$1',/;
+
+my $Tickets = RT::Tickets->new( $session{'CurrentUser'} );
+$Tickets->FromSQL($Query);
+if ( $OrderBy =~ /\|/ ) {
+
+ # Multiple Sorts
+ my @OrderBy = split /\|/, $OrderBy;
+ my @Order = split /\|/, $Order;
+ $Tickets->OrderByCols(
+ map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } }
+ ( 0 .. $#OrderBy ) );
+}
+else {
+ $Tickets->OrderBy( FIELD => $OrderBy, ORDER => $Order );
+}
+
+$Tickets->RowsPerPage($Rows) if ($Rows);
+$Tickets->GotoPage( $Page - 1 ); # SB uses page 0 as the first page
+
+Abort( loc("No search to operate on.") ) unless ($Tickets);
+
+# build up a list of all custom fields for tickets that we're displaying, so
+# we can display sane edit widgets.
+
+my $fields = {};
+my $seen_queues = {};
+while ( my $ticket = $Tickets->Next ) {
+ next if $seen_queues->{ $ticket->Queue }++;
+
+ my $custom_fields = $ticket->QueueObj->TicketCustomFields;
+ while ( my $field = $custom_fields->Next ) {
+ $fields->{ $field->id } = $field;
+ }
+}
+
+my $do_comment_reply = 0;
+
+# Prepare for ticket updates
+if ($ARGS{'UpdateContent'}) {
+ $ARGS{'UpdateContent'} =~ s/\r\n/\n/g;
+ chomp( $ARGS{'UpdateContent'} );
+
+ if ($ARGS{'UpdateContent'} ne ''
+ && $ARGS{'UpdateContent'} ne "-- \n"
+ . $session{'CurrentUser'}->UserObj->Signature ) {
+ $do_comment_reply = 1;
+ }
+}
+
+#Iterate through each ticket we've been handed
+my @linkresults;
+my %queues;
+
+$Tickets->RedoSearch();
+
+# pull out the labels for any custom fields we want to update
+
+my $cf_del_keys;
+@$cf_del_keys = grep { /^Bulk-Delete-CustomField/ } keys %ARGS;
+my $cf_add_keys;
+@$cf_add_keys = grep { /^Bulk-Add-CustomField/ } keys %ARGS;
+
+
+while ( my $Ticket = $Tickets->Next ) {
+ next unless ( $ARGS{ "UpdateTicket" . $Ticket->Id } );
+
+ #Update the links
+ $ARGS{'id'} = $Ticket->id;
+ $queues{ $Ticket->QueueObj->Id }++;
+
+ my @updateresults;
+ if ($do_comment_reply) {
+ ProcessUpdateMessage(
+ TicketObj => $Ticket,
+ ARGSRef => \%ARGS,
+ Actions => \@updateresults
+ );
+ }
+
+ #Update the basics.
+ my @basicresults =
+ ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS );
+ my @dateresults =
+ ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+ #Update the watchers
+ my @watchresults =
+ ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+ foreach my $type qw(MergeInto DependsOn MemberOf RefersTo) {
+ $ARGS{ $Ticket->id . "-" . $type } = $ARGS{"Ticket-$type"};
+ $ARGS{ $type . "-" . $Ticket->id } = $ARGS{"$type-Ticket"};
+ }
+ @linkresults =
+ ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS );
+ foreach my $type qw(MergeInto DependsOn MemberOf RefersTo) {
+ delete $ARGS{ $type . "-" . $Ticket->id };
+ delete $ARGS{ $Ticket->id . "-" . $type };
+ }
+
+ my @cfresults;
+
+ foreach my $list ( $cf_add_keys, $cf_del_keys ) {
+ next unless $list->[0];
+
+
+ my $op;
+ if ( $list->[0] =~ /Add/ ) {
+ $op = 'add';
+
+ }
+ elsif ( $list->[0] =~ /Del/ ) {
+ $op = 'del';
+ }
+ else {
+ $RT::Logger->crit(
+ "Got an op that was neither add nor delete. can never happen"
+ . $list->[0] );
+ last;
+ }
+
+ foreach my $key (@$list) {
+ my ( $cfid, $cf );
+ if ( $key =~ /CustomField-(\d+)-/ ) {
+ $cfid = $1;
+ $cf = RT::CustomField->new( $session{'CurrentUser'} );
+ $cf->Load($cfid);
+ }
+ else {next}
+ my @values =
+ ref( $ARGS{$key} ) eq 'ARRAY'
+ ? @{ $ARGS{$key} }
+ : ( $ARGS{$key} );
+ map { s/(\r\n|\r)/\n/g; } @values; # fix the newlines
+ # now break the multiline values into multivalues
+ @values = map { split( /\n/, $_ ) } @values
+ unless ( $cf->SingleValue );
+
+ my $current_values = $Ticket->CustomFieldValues($cfid);
+ foreach my $value (@values) {
+ if ( $op eq 'del' && $current_values->HasEntry($value) ) {
+ my ( $id, $msg ) = $Ticket->DeleteCustomFieldValue(
+ Field => $cfid,
+ Value => $value
+ );
+ push @cfresults, $msg;
+ }
+
+ elsif ( $op eq 'add' && !$current_values->HasEntry($value) ) {
+ my ( $id, $msg ) = $Ticket->AddCustomFieldValue(
+ Field => $cfid,
+ Value => $value
+ );
+ push @cfresults, $msg;
+ }
+ }
+ }
+ }
+ my @tempresults = (
+ @watchresults, @basicresults, @dateresults,
+ @updateresults, @linkresults, @cfresults
+ );
+
+ @tempresults =
+ map { loc( "Ticket [_1]: [_2]", $Ticket->Id, $_ ) } @tempresults;
+
+ @results = ( @results, @tempresults );
+}
+
+my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} );
+$TxnCFs->LimitToLookupType( RT::Transaction->CustomFieldLookupType );
+$TxnCFs->LimitToGlobalOrObjectId( sort keys %queues );
+
+</%INIT>
+<%args>
+$Format => undef
+$Page => 1
+$Rows => undef
+$Order => 'ASC'
+$OrderBy => 'id'
+$Query => undef
+</%args>
diff --git a/rt/html/Search/Chart b/rt/html/Search/Chart
new file mode 100644
index 0000000..82704fd
--- /dev/null
+++ b/rt/html/Search/Chart
@@ -0,0 +1,188 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Query => "id > 0"
+$PrimaryGroupBy => 'Queue'
+$SecondaryGroupBy => undef
+$ChartStyle => 'bars'
+</%args>
+<%init>
+my @keys;
+my @values;
+my $chart_class;
+use GD;
+use GD::Text;
+
+if ($ChartStyle eq 'pie') {
+ require GD::Graph::pie;
+ $chart_class = "GD::Graph::pie";
+} else {
+ require GD::Graph::bars;
+ $chart_class = "GD::Graph::bars";
+}
+
+use RT::Report::Tickets;
+my $tix = RT::Report::Tickets->new( $session{'CurrentUser'} );
+$tix->FromSQL( $Query );
+my $count_name = $tix->Column( FUNCTION => 'COUNT', FIELD => 'id' );
+$tix->GroupBy( FIELD => $PrimaryGroupBy );
+my $value_name = $tix->Column( FIELD => $PrimaryGroupBy );
+
+my $chart = $chart_class->new( 600 => 400 );
+
+my $font = $RT::ChartFont || ['verdana', 'arial', gdMediumBoldFont];
+$chart->set_title_font( $font, 12 ) if $chart->can('set_title_font');
+$chart->set_legend_font( $font, 12 ) if $chart->can('set_legend_font');
+$chart->set_x_label_font( $font, 10 ) if $chart->can('set_x_label_font');
+$chart->set_y_label_font( $font, 10 ) if $chart->can('set_y_label_font');
+$chart->set_label_font( $font, 10 ) if $chart->can('set_label_font');
+$chart->set_x_axis_font( $font, 9 ) if $chart->can('set_x_axis_font');
+$chart->set_y_axis_font( $font, 9 ) if $chart->can('set_y_axis_font');
+$chart->set_values_font( $font, 9 ) if $chart->can('set_values_font');
+$chart->set_value_font( $font, 9 ) if $chart->can('set_value_font');
+
+# Pie charts don't like having no input, so we show a special image
+# that indicates an error message. Because this is used in an <img>
+# context, it can't be a simple error message. Without this check,
+# the chart will just be a non-loading image.
+if ($tix->Count == 0) {
+ my $plot = GD::Image->new(600 => 400);
+ $plot->colorAllocate(255, 255, 255); # background
+ my $black = $plot->colorAllocate(0, 0, 0);
+
+ require GD::Text::Wrap;
+ my $error = GD::Text::Wrap->new($plot,
+ color => $black,
+ text => loc("No tickets found."),
+ );
+ $error->set_font( $font, 12 );
+ $error->draw(0, 0);
+
+ $m->comp( 'SELF:Plot', plot => $plot, %ARGS );
+}
+
+if ($chart_class eq "GD::Graph::bars") {
+ $chart->set(
+ x_label => $tix->Label( $PrimaryGroupBy ),
+ x_labels_vertical => 1,
+ y_label => loc('Tickets'),
+ show_values => 1
+ );
+}
+
+my %class = (
+ Queue => 'RT::Queue',
+ Owner => 'RT::User',
+);
+my $class = $class{ $PrimaryGroupBy };
+
+while ( my $entry = $tix->Next ) {
+ if ( $class ) {
+ my $q = $class->new( $session{'CurrentUser'} );
+ $q->Load( $entry->__Value( $value_name ) );
+ push @keys, $q->Name;
+ }
+ else {
+ push @keys, $entry->__Value($value_name);
+ }
+
+ $keys[-1] ||= loc('(no value)');
+ if ($chart_class eq 'GD::Graph::pie') {
+ $keys[-1] .= " - ". $entry->__Value( $count_name );
+ }
+ push @values, $entry->__Value($count_name);
+}
+
+# XXX: Convert 1970-01-01 date to the 'Not Set'
+# this code should be generalized!!!
+if ( $PrimaryGroupBy =~ /(Daily|Monthly|Annually)$/ ) {
+ my $re;
+ $re = qr{1970-01-01} if $PrimaryGroupBy =~ /Daily$/;
+ $re = qr{1970-01} if $PrimaryGroupBy =~ /Monthly$/;
+ $re = qr{1970} if $PrimaryGroupBy =~ /Annually$/;
+ foreach (@keys) {
+ s/^$re/loc('Not Set')/e;
+ }
+}
+
+unless (@keys && @values) {
+ @keys = ('');
+ @values = (0);
+}
+
+my %data;
+foreach my $key (@keys) { $data{$key} = shift @values; }
+my @sorted_keys = sort @keys;
+my @sorted_values = map { $data{$_}} @sorted_keys;
+
+
+
+my $plot = $chart->plot( [ [@sorted_keys], [@sorted_values] ] ) or die $chart->error;
+$m->comp( 'SELF:Plot', plot => $plot, %ARGS );
+</%init>
+
+<%METHOD Plot>
+<%ARGS>
+$plot => undef
+</%ARGS>
+<%INIT>
+my @types = ('png', 'gif');
+
+for my $type (@types) {
+ $plot->can($type)
+ or next;
+
+ $r->content_type("image/$type");
+ $m->out( $plot->$type );
+ $m->abort();
+}
+
+die "Your GD library appears to support none of the following image types: " . join(', ', @types);
+</%INIT>
+
+</%METHOD>
diff --git a/rt/html/Search/Chart.html b/rt/html/Search/Chart.html
new file mode 100644
index 0000000..a07d895
--- /dev/null
+++ b/rt/html/Search/Chart.html
@@ -0,0 +1,73 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Query => "id > 0"
+$PrimaryGroupBy => 'Queue'
+$SecondaryGroupBy => ''
+$ChartStyle => 'bars'
+$Description => undef
+</%args>
+<%init>
+$ARGS{SecondaryGroupBy} ||= '';
+
+my $title = loc( "Search results grouped by [_1]", $PrimaryGroupBy );
+
+my $saved_search = $m->comp( '/Widgets/SavedSearch:new',
+ SearchType => 'Chart',
+ SearchFields => [qw(Query PrimaryGroupBy SecondaryGroupBy ChartStyle)] );
+
+my @actions = $m->comp( '/Widgets/SavedSearch:process', args => \%ARGS, self => $saved_search );
+
+</%init>
+<& /Elements/Header, Title => $title &>
+<& /Ticket/Elements/Tabs, Title => $title &>
+<& /Elements/ListActions, actions => \@actions &>
+<& /Search/Elements/Chart, %ARGS &>
+
+
+<& /Widgets/SavedSearch:show, %ARGS, Action => 'Chart.html', self => $saved_search, Title => 'Saved charts' &>
diff --git a/rt/html/Search/Edit.html b/rt/html/Search/Edit.html
new file mode 100755
index 0000000..21df0db
--- /dev/null
+++ b/rt/html/Search/Edit.html
@@ -0,0 +1,88 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title&>
+<& /Ticket/Elements/Tabs,
+ current_tab => "Search/Edit.html?".$QueryString,
+ Title => $title,
+ Format => $Format,
+ Query => $Query,
+ Rows => $ARGS{'Rows'},
+ OrderBy => $ARGS{'OrderBy'},
+ Order => $ARGS{'Order'} &>
+
+<& Elements/NewListActions, actions => \@actions &>
+
+<form method="post" action="Build.html">
+<input type="hidden" class="hidden" name="SearchId" value="<%$SearchId%>" />
+<textarea name="Query" rows="8" cols="72"><%$Query%></textarea>
+<br />
+<textarea name="Format" rows="8" cols="72"><%$Format%></textarea>
+<br />
+<& /Elements/Submit, Label => loc("Apply"), Reset => 1, Caption => loc("Apply your changes")&>
+</form>
+
+<%INIT>
+my $title = loc("Edit Query");
+$Format = $m->comp('/Elements/ScrubHTML', Content => $Format);
+my $QueryString = $m->comp('/Elements/QueryString',
+ Query => $Query,
+ Format => $Format,
+ Rows => $ARGS{'Rows'},
+ OrderBy => $ARGS{'OrderBy'},
+ Order => $ARGS{'Order'},
+ );
+
+</%INIT>
+
+
+<%ARGS>
+$Query => undef
+$Format => undef
+$SearchId => 'new'
+@actions => undef
+</%ARGS>
diff --git a/rt/html/Search/Elements/BuildFormatString b/rt/html/Search/Elements/BuildFormatString
new file mode 100644
index 0000000..3bd39b5
--- /dev/null
+++ b/rt/html/Search/Elements/BuildFormatString
@@ -0,0 +1,244 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Format => undef
+%cfqueues => undef
+$Face => undef
+$Size => undef
+$Link => undef
+$Title => undef
+$AddCol => undef
+$RemoveCol => undef
+$ColUp => undef
+$ColDown => undef
+$SelectDisplayColumns => undef
+$CurrentDisplayColumns => undef
+</%args>
+<%init>
+
+unless ($Format) {
+ $Format = $RT::DefaultSearchResultFormat;
+}
+
+
+# All the things we can display in the format string by default
+my @fields = qw(
+ id
+ Status
+ ExtendedStatus
+ Subject
+ QueueName
+ OwnerName
+ Priority
+ InitialPriority
+ FinalPriority
+ Type
+ TimeWorked
+ TimeLeft
+ TimeEstimated
+ CreatedBy
+ LastUpdatedBy
+ Requestors
+ Cc
+ AdminCc
+ Starts
+ StartsRelative
+ Started
+ StartedRelative
+ Created
+ CreatedRelative
+ LastUpdated
+ LastUpdatedRelative
+ Told
+ ToldRelative
+ Due
+ DueRelative
+ Resolved
+ ResolvedRelative
+ RefersTo
+ ReferredToBy
+ DependsOn
+ DependedOnBy
+ MemberOf
+ Members
+ Parents
+ Children
+ NEWLINE
+);
+
+my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
+foreach my $id (keys %cfqueues) {
+ # What does this _do_? What are the keys to cfqueues
+ $id =~ s/^.'*(.*).'*$/$1/;
+ # Gotta load up the $queue object, since queues get stored by name now.
+ my $queue = RT::Queue->new($session{'CurrentUser'});
+ $queue->Load($id);
+ $CustomFields->LimitToQueue($queue->Id);
+}
+$CustomFields->LimitToGlobal;
+
+while ( my $CustomField = $CustomFields->Next ) {
+ push @fields, "CustomField.{" . $CustomField->Name . "}";
+}
+
+my ( @seen);
+
+my @format = split( /,\s*/, $Format );
+foreach my $field (@format) {
+ my %column = ();
+ $field =~ s/'(.*)'/$1/;
+ my ( $prefix, $suffix );
+ if ( $field =~ m/(.*)__(.*)__(.*)/ ) {
+ $prefix = $1;
+ $suffix = $3;
+ $field = $2;
+ }
+ $field = "<blank>" if !$field;
+ $column{Prefix} = $prefix;
+ $column{Suffix} = $suffix;
+ $field =~ s/\s*(.*)\s*/$1/;
+ $column{Column} = $field;
+ push @seen, \%column;
+}
+
+if ( $RemoveCol ) {
+ my $index = $CurrentDisplayColumns;
+ my $column = $seen[$index];
+ if ($index) {
+ delete $seen[$index];
+ my @temp = @seen;
+ @seen = ();
+ foreach my $element (@temp) {
+ next unless $element;
+ push @seen, $element;
+ }
+ }
+}
+elsif ( $AddCol ) {
+ if ( defined $SelectDisplayColumns ) {
+ my $selected = $SelectDisplayColumns;
+ my @columns;
+ if (ref($selected) eq 'ARRAY') {
+ @columns = @$selected;
+ } else {
+ push @columns, $selected;
+ }
+ foreach my $col (@columns) {
+ my %column = ();
+ $column{Column} = $col;
+
+ if ( $Face eq "Bold" ) {
+ $column{Prefix} .= "<b>";
+ $column{Suffix} .= "</b>";
+ }
+ if ( $Face eq "Italic" ) {
+ $column{Prefix} .= "<i>";
+ $column{Suffix} .= "</i>";
+ }
+ if ($Size) {
+ $column{Prefix} .= "<" . $m->interp->apply_escapes( $Size, 'h' ) . ">";
+ $column{Suffix} .= "</" . $m->interp->apply_escapes( $Size, 'h' ) . ">";
+ }
+ if ( $Link eq "Display" ) {
+ $column{Prefix} .=
+ "<a HREF=\"" . $RT::WebPath . "/Ticket/Display.html?id=__id__\">";
+ $column{Suffix} .= "</a>";
+ }
+ elsif ( $Link eq "Take" ) {
+ $column{Prefix} .= "<a HREF=\"" . $RT::WebPath
+ . "/Ticket/Display.html?Action=Take&id=__id__\">";
+ $column{Suffix} .= "</a>";
+ }
+
+ if ($Title) {
+ $column{Suffix} .= "/TITLE:" . $m->interp->apply_escapes( $Title, 'h' );
+ }
+ push @seen, \%column;
+}
+}
+}
+elsif ( $ColUp ) {
+ my $index = $CurrentDisplayColumns;
+ if ( defined $index && ( $index - 1 ) >= 0 ) {
+ my $column = $seen[$index];
+ $seen[$index] = $seen[ $index - 1 ];
+ $seen[ $index - 1 ] = $column;
+ $CurrentDisplayColumns = $index - 1;
+ }
+}
+elsif ( $ColDown ) {
+ my $index = $CurrentDisplayColumns;
+ if ( defined $index && ( $index + 1 ) < scalar @seen ) {
+ my $column = $seen[$index];
+ $seen[$index] = $seen[ $index + 1 ];
+ $seen[ $index + 1 ] = $column;
+ $CurrentDisplayColumns = $index + 1;
+ }
+}
+
+
+my @format_string;
+foreach my $field (@seen) {
+ next unless $field;
+ my $row = "'";
+ $row .= $field->{Prefix} if $field->{Prefix};
+ $row .= "__" . ($field->{Column} =~ m/\(/ ? $field->{Column} # func, don't escape
+ : $m->interp->apply_escapes( $field->{Column}, 'h' )) . "__"
+ unless ( $field->{Column} eq "<blank>" );
+ $row .= $field->{Suffix} if $field->{Suffix};
+ $row .= "'";
+ push( @format_string, $row );
+}
+
+$Format = join(",\n", @format_string);
+
+
+return($Format, \@fields, \@seen);
+
+</%init>
+
diff --git a/rt/html/Search/Elements/Chart b/rt/html/Search/Elements/Chart
new file mode 100644
index 0000000..2eca6af
--- /dev/null
+++ b/rt/html/Search/Elements/Chart
@@ -0,0 +1,139 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Query => "id > 0"
+$PrimaryGroupBy => 'Queue'
+$SecondaryGroupBy => undef
+$ChartStyle => 'bars'
+</%args>
+<%init>
+use RT::Report::Tickets;
+my $tix = RT::Report::Tickets->new( $session{'CurrentUser'} );
+$tix->FromSQL( $Query );
+my $count_name = $tix->Column( FUNCTION => 'COUNT', FIELD => 'id' );
+$tix->GroupBy( FIELD => $PrimaryGroupBy );
+my $value_name = $tix->Column( FIELD => $PrimaryGroupBy );
+
+my %class = (
+ Queue => 'RT::Queue',
+ Owner => 'RT::User',
+);
+my $class = $class{ $PrimaryGroupBy };
+
+my (@keys, @values);
+while ( my $entry = $tix->Next ) {
+ if ($class) {
+ my $q = $class->new( $session{'CurrentUser'} );
+ $q->Load( $entry->__Value( $value_name ) );
+ push @keys, $q->Name;
+ }
+ else {
+ push @keys, $entry->__Value( $value_name );
+ }
+ $keys[-1] ||= loc('(no value)');
+ push @values, $entry->__Value( $count_name );
+}
+
+# XXX: Convert 1970-01-01 date to the 'Not Set'
+# this code should be generalized!!!
+if ( $PrimaryGroupBy =~ /(Daily|Monthly|Annually)$/ ) {
+ my $re;
+ $re = qr{1970-01-01} if $PrimaryGroupBy =~ /Daily$/;
+ $re = qr{1970-01} if $PrimaryGroupBy =~ /Monthly$/;
+ $re = qr{1970} if $PrimaryGroupBy =~ /Annually$/;
+ foreach (@keys) {
+ s/^$re/loc('Not Set')/e;
+ }
+}
+
+my %data;
+foreach my $key (@keys) { $data{$key} = shift @values; }
+my @sorted_keys = sort @keys;
+my @sorted_values = map { $data{$_}} @sorted_keys;
+
+
+my $query_string = $m->comp('/Elements/QueryString', %ARGS);
+</%init>
+
+<% loc('Query:') %>&nbsp;<% $Query %><br />
+
+<img src="<%$RT::WebPath%>/Search/Chart?<%$query_string|n%>" /><br />
+
+<table class="collection-as-table">
+<tr>
+<th class="collection-as-table"><% $tix->Label($PrimaryGroupBy) %>
+</th>
+<th class="collection-as-table"><&|/l&>Tickets</&>
+</th>
+</tr>
+% my ($i,$total);
+% while (my $key = shift @sorted_keys) {
+% $i++;
+% my $value = shift @sorted_values;
+% $total += $value;
+<tr class="<%$i%2 ? 'evenline' : 'oddline' %>">
+<td class="label collection-as-table">
+<%$key%>
+</td>
+<td class="value collection-as-table">
+<%$value%>
+</td>
+</tr>
+% }
+
+%$i++;
+<tr class="<%$i%2 ? 'evenline' : 'oddline' %>">
+<td class="label collection-as-table">
+<%loc('Total')%>
+</td>
+<td class="value collection-as-table">
+<%$total%>
+</td>
+</tr>
+
+</table>
diff --git a/rt/html/Search/Elements/DisplayOptions b/rt/html/Search/Elements/DisplayOptions
new file mode 100644
index 0000000..cc57f96
--- /dev/null
+++ b/rt/html/Search/Elements/DisplayOptions
@@ -0,0 +1,143 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&| /Widgets/TitleBox, title => loc("Display Columns") &>
+<table width="100%">
+<tr>
+<td>
+<& EditFormat, %ARGS &>
+</td>
+<td valign="top">
+<table valign="top">
+
+% for my $o (0..3) {
+<tr>
+<td class="label">
+% if ($o == 0) {
+<&|/l&>Order by</&>:
+% }
+</td>
+<td class="value">
+<select name="OrderBy">
+% if ($o > 0) {
+<option value=""><&|/l&>~[none~]</&></option>
+% }
+% foreach my $field (sort keys %fields) {
+% next unless $field;
+<option value="<%$field%>"
+% if (defined $OrderBy[$o] and $field eq $OrderBy[$o]) {
+selected
+% }
+><&|/l&><%$field%></&></option>
+% }
+</select>
+<select name="Order">
+<option value="ASC"
+% unless ( ($Order[$o]||'') eq "DESC" ) {
+selected
+% }
+><&|/l&>Asc</&></option>
+<option value="DESC"
+% if ( ($Order[$o]||'') eq "DESC" ) {
+selected
+% }
+><&|/l&>Desc</&></option>
+</select>
+</td>
+</tr>
+% }
+<tr>
+<td class="label">
+<&|/l&>Rows per page</&>:
+</td><td class="value">
+<& /Elements/SelectResultsPerPage,
+ Name => "RowsPerPage",
+ Default => $RowsPerPage &>
+</td>
+</tr>
+</table>
+</td>
+</tr>
+</table>
+</&>
+
+<%INIT>
+my $tickets = new RT::Tickets($session{'CurrentUser'});
+my %fields = %{$tickets->FIELDS};
+map { $fields{$_}->[0] =~ /^(?:ENUM|INT|DATE|STRING)$/ || delete $fields{$_} } keys %fields;
+delete $fields{'EffectiveId'};
+$fields{ $_ . '.EmailAddress' } = 1 foreach( qw(Requestor Cc AdminCc) );
+
+# Add all available CustomFields to the list of sortable columns.
+my @cfs = grep /^CustomField/, @{$ARGS{AvailableColumns}};
+$fields{$_}=1 for @cfs;
+
+# Add PAW sort
+$fields{'Custom.Ownership'} = 1;
+
+my @Order;
+my @OrderBy;
+if ($OrderBy =~ /\|/) {
+ @OrderBy = split /\|/, $OrderBy;
+} else {
+ @OrderBy = ( $OrderBy );
+}
+if ($Order =~ /\|/) {
+ @Order = split /\|/, $Order;
+} else {
+ @Order = ( $Order );
+}
+
+</%INIT>
+
+<%ARGS>
+$Order => undef
+$OrderBy => undef
+$RowsPerPage => undef
+$Format => undef
+$GroupBy => 'id'
+</%ARGS>
diff --git a/rt/html/Search/Elements/EditFormat b/rt/html/Search/Elements/EditFormat
new file mode 100644
index 0000000..fa0ac96
--- /dev/null
+++ b/rt/html/Search/Elements/EditFormat
@@ -0,0 +1,116 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+<tr>
+<td>
+<&|/l&>Add Columns</&>:
+</td>
+<td>
+<&|/l&>Format</&>:
+</td>
+<td></td>
+<td>
+<&|/l&>Show Columns</&>:
+</td>
+<tr>
+<td valign="top">
+<select size="6" name="SelectDisplayColumns" multiple>
+% foreach my $field ( @$AvailableColumns) {
+<option value="<%$field%>"><% loc( $field) %></option>
+%# $m->comp( '/Elements/RT__Ticket/ColumnMap', Name => $field, Attr => 'title') ||
+% }
+</select>
+</td>
+<td>
+<&|/l&>Link</&>:
+<select name="Link">
+<option value="None">-</option>
+<option value="Display"><&|/l&>Display</&></option>
+<option value="Take"><&|/l&>Take</&></option>
+</select>
+<br /><&|/l&>Title</&>: <input name="Title" size="10" />
+<br /><&|/l&>Size</&>:
+<select name="Size">
+<option value="">-</option>
+<option value="Small"><&|/l&>Small</&></option>
+<option value="Large"><&|/l&>Large</&></option>
+</select>
+<br /><&|/l&>Style</&>:
+<select name="Face">
+<option value="">-</option>
+<option value="Bold"><&|/l&>Bold</&></option>
+<option value="Italic"><&|/l&>Italic</&></option>
+</select>
+</td>
+<td>
+<input type="submit" class="button" name="AddCol" value=" &rarr; " />
+</td>
+<td valign="top">
+<select size="4" name="CurrentDisplayColumns">
+% my $i=0;
+% foreach my $field (@$CurrentFormat) {
+<option value="<%$i++%>><%$field->{Column}%>">
+<%loc( $field->{Column}) %></option>
+% }
+</select>
+<br />
+<center>
+<input type="submit" class="button" name="ColUp" value=" &uarr; " />
+<input type="submit" class="button" name="ColDown" value=" &darr; " />
+<input type="submit" class="button" name="RemoveCol" value="<%loc('Delete')%>" />
+</center>
+</td>
+<td colspan="3" align="center">
+</td>
+</tr>
+</table>
+
+<%ARGS>
+$CurrentFormat => undef
+$AvailableColumns => undef
+</%ARGS>
diff --git a/rt/html/Search/Elements/EditQuery b/rt/html/Search/Elements/EditQuery
new file mode 100644
index 0000000..5c40c25
--- /dev/null
+++ b/rt/html/Search/Elements/EditQuery
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& NewListActions, actions => $actions &>
+<&|/Widgets/TitleBox, title => join(': ', grep defined, loc("Current search"), $Description) &>
+<select size="10" name="clauses" style="width: 100%" multiple>
+% $m->out($optionlist);
+</select>
+<p align="center">
+<input type="submit" class="button" name="Up" value=" &uarr; " />
+<input type="submit" class="button" name="Down" value=" &darr; " />
+<input type="submit" class="button" name="Left" value=" &larr; " />
+<input type="submit" class="button" name="Right" value=" &rarr; " />
+<input type="submit" class="button" name="Toggle" value="<&|/l&>And/Or</&>" />
+<input type="submit" class="button" name="DeleteClause" value="<&|/l&>Delete</&>" />
+%#<input type="submit" class="button" name="EditQuery" value="Advanced" />
+</p>
+</&>
+<%ARGS>
+$Description
+$optionlist
+$actions
+</%ARGS>
diff --git a/rt/html/Search/Elements/EditSearches b/rt/html/Search/Elements/EditSearches
new file mode 100644
index 0000000..3978ea3
--- /dev/null
+++ b/rt/html/Search/Elements/EditSearches
@@ -0,0 +1,103 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&| /Widgets/TitleBox, title => loc($Title)&>
+%# Hide all the save functionality if the user shouldn't see it.
+% if ($session{'CurrentUser'}->HasRight( Right => 'CreateSavedSearch',
+% Object=> $RT::System )) {
+<&|/l&>Privacy:</&>
+% if ($CurrentSearch->{'Object'} && $CurrentSearch->{'Object'}->id) {
+<& SearchPrivacy, Object => $CurrentSearch->{'Object'}->Object &><br />
+% } else {
+<& SelectSearchObject, Name => 'Owner', Objects => \@Objects &><br />
+% }
+<&|/l&>Description</&>:<br>
+<font size="-1"><input size="25" name="Description" value="<%$CurrentSearch->{'Description'} || ''%>" /></font>
+% if ($SearchId ne 'new') {
+<nobr>
+% if ($Dirty) {
+<input type="submit" class="button" name="Revert" value="<%loc('Revert')%>" />
+% }
+<input type="submit" class="button" name="Delete" value="<%loc('Delete')%>" />
+% if ($AllowCopy) {
+<input type="submit" class="button" name="CopySearch" value="<%loc('Copy')%>" />
+% }
+</nobr>
+
+% }
+<input type="submit" name="Save" value="<%loc('Save')%>" class="button" />
+<hr />
+% }
+<&|/l&>Load saved search:</&><br />
+<& SelectSearchesForObjects, Name => 'LoadSavedSearch', Objects => \@Objects, SearchType => $SearchType &>
+<input value="<%loc('Load')%>" type="submit" class="button" />
+</&>
+
+<%init>
+unless ($session{'CurrentUser'}->HasRight( Right => 'LoadSavedSearch',
+ Object=> $RT::System )) {
+ return;
+}
+
+use RT::SavedSearches;
+my @Objects = RT::SavedSearches->new($session{CurrentUser})->_PrivacyObjects;
+push @Objects, RT::System->new($session{'CurrentUser'})
+ if $session{'CurrentUser'}->HasRight( Object=> $RT::System,
+ Right => 'SuperUser');
+
+</%INIT>
+
+<%ARGS>
+$SearchType => 'Ticket'
+$SearchId => undef
+$CurrentSearch => undef
+$Description => undef
+$HideResults => 0
+$Dirty => 0
+$AllowCopy => 1
+$Title => 'Saved searches'
+</%ARGS>
diff --git a/rt/html/Search/Elements/NewListActions b/rt/html/Search/Elements/NewListActions
new file mode 100644
index 0000000..535ac8c
--- /dev/null
+++ b/rt/html/Search/Elements/NewListActions
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if ($actions[0] ) {
+<b><%loc('Results')%></b><br />
+% foreach my $action (@actions) {
+% next unless ($action);
+% my @item = @$action;
+% if ($item[1] < 0) {
+<font color="red">
+% }
+&nbsp;<%$item[0]%><br />
+% if ($item[1] < 0) {
+</font>
+% }
+% }
+<br />
+% }
+<%init>
+@actions = grep (/./,@actions);
+</%init>
+<%ARGS>
+@actions => undef
+</%ARGS>
diff --git a/rt/html/Search/Elements/PickBasics b/rt/html/Search/Elements/PickBasics
new file mode 100644
index 0000000..44a378c
--- /dev/null
+++ b/rt/html/Search/Elements/PickBasics
@@ -0,0 +1,176 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<tr>
+<td class="label">
+<&|/l&>id</&>
+</td><td>
+<& /Elements/SelectEqualityOperator, Name => "idOp" &>
+</td><td>
+<input name="ValueOfid" size="5" />
+</td>
+</tr>
+
+<tr><td>
+<& /Elements/SelectAttachmentField, Name => 'AttachmentField' &>
+</td><td>
+<& /Elements/SelectBoolean, Name => "AttachmentOp",
+ True => loc("matches"),
+ False => loc("does not match"),
+ TrueVal => 'LIKE',
+ FalseVal => 'NOT LIKE'
+&>
+</td><td>
+<input name="ValueOfAttachment" size="20" />
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Queue</&>
+</td><td>
+<& /Elements/SelectBoolean, Name => "QueueOp" ,
+ True => loc("is"),
+ False => loc("isn't"),
+ TrueVal=> '=',
+ FalseVal => '!=' &>
+</td><td>
+<& /Elements/SelectQueue,
+ Name => "ValueOfQueue",
+ NamedValues => 1,
+ CheckQueueRight => 'ShowTicket' &>
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Status</&>
+</td><td>
+<& /Elements/SelectBoolean, Name => "StatusOp",
+ True => loc("is"),
+ False => loc("isn't"),
+ TrueVal=> '=',
+ FalseVal => '!='
+&>
+</td><td>
+<& /Elements/SelectStatus, Name => "ValueOfStatus", SkipDeleted => 1 &>
+</td>
+</tr>
+<tr><td class="label">
+<select name="ActorField">
+<option value="Owner"><&|/l&>Owner</&></option>
+<option value="Creator"><&|/l&>Creator</&></option>
+<option value="LastUpdatedBy"><&|/l&>LastUpdatedBy</&></option>
+</select>
+</td><td>
+<& /Elements/SelectBoolean, Name => "ActorOp",
+ TrueVal=> '=',
+ FalseVal => '!='
+&>
+</td><td>
+<& /Elements/SelectOwner, Name => "ValueOfActor", ValueAttribute => 'Name' &>
+</td>
+</tr>
+<tr>
+<td class="label">
+<& SelectPersonType, Name => 'WatcherField', Default => 'Requestor' &>
+</td><td>
+<& /Elements/SelectMatch, Name => "WatcherOp" &>
+</td><td>
+<input name="ValueOfWatcher" size="20" />
+</tr>
+<tr>
+<td class="label">
+<& /Elements/SelectDateType, Name=>"DateField" &>
+</td><td>
+<& /Elements/SelectDateRelation, Name=>"DateOp" &>
+</td><td>
+<& /Elements/SelectDate, Name => "ValueOfDate", ShowTime => 0, Default => '' &>
+</td></tr>
+<tr>
+<td class="label">
+<select name="TimeField">
+<option value="TimeWorked"><&|/l&>Time Worked</&></option>
+<option value="TimeEstimated"><&|/l&>Time Estimated</&></option>
+<option value="TimeLeft"><&|/l&>Time Left</&></option>
+</select>
+</td><td>
+<& /Elements/SelectEqualityOperator, Name => "TimeOp" &>
+</td><td>
+<input name="ValueOfTime" size="5" />
+<& /Elements/SelectTimeUnits, Name =>'ValueOfTime' &>
+</td>
+</tr>
+<tr>
+<td class="label">
+<select name="PriorityField">
+<option value="Priority"><&|/l&>Priority</&></option>
+<option value="InitialPriority"><&|/l&>Initial Priority</&></option>
+<option value="FinalPriority"><&|/l&>Final Priority</&></option>
+</select>
+</td><td>
+<& /Elements/SelectEqualityOperator, Name => "PriorityOp" &>
+</td><td>
+<input name="ValueOfPriority" size="5" />
+</td>
+</tr>
+<tr>
+<td class="label">
+<& SelectLinks, Name=>"LinksField" &>
+</td><td>
+<& /Elements/SelectBoolean, Name => "LinksOp",
+ True => loc("is"),
+ False => loc("isn't"),
+ TrueVal=> '=',
+ FalseVal => '!=' &>
+</td><td>
+<input name="ValueOfLinks" value="" size="5" />
+</td></tr>
+<%INIT>
+my @people = ('Actor',
+ 'Watcher',
+ 'WatcherGroup',
+ );
+</%INIT>
diff --git a/rt/html/Search/Elements/PickCFs b/rt/html/Search/Elements/PickCFs
new file mode 100644
index 0000000..734f5f8
--- /dev/null
+++ b/rt/html/Search/Elements/PickCFs
@@ -0,0 +1,80 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% while ( my $CustomField = $CustomFields->Next ) {
+% my $name = "'CF.{" . $CustomField->Name . "}'";
+<tr><td class="label">
+<% $CustomField->Name %>
+</td>
+<td>
+ <& /Elements/SelectCustomFieldOperator, Name => $name . "Op",
+ True => loc("is"),
+ False => loc("isn't"),
+ TrueVal=> '=', FalseVal => '!=' &>
+</td>
+<td>
+<& /Elements/SelectCustomFieldValue, Name => "ValueOf" . $name,
+ CustomField => $CustomField,
+ &>
+</td></tr>
+% }
+<%INIT>
+my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
+foreach my $id (keys %cfqueues) {
+ $id =~ s/^.'*(.*).'*$/$1/;
+ # Gotta load up the $queue object, since queues get stored by name now. my $id
+ my $queue = RT::Queue->new($session{'CurrentUser'});
+ $queue->Load($id);
+ $CustomFields->LimitToQueue($queue->Id);
+}
+$CustomFields->LimitToGlobal();
+
+</%INIT>
+
+<%ARGS>
+%cfqueues => undef
+</%ARGS>
diff --git a/rt/html/Search/Elements/PickCriteria b/rt/html/Search/Elements/PickCriteria
new file mode 100644
index 0000000..58b29fb
--- /dev/null
+++ b/rt/html/Search/Elements/PickCriteria
@@ -0,0 +1,82 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&| /Widgets/TitleBox, title => loc('Add Criteria')&>
+<table width="100%" cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td>
+ <table cellspacing="0" border="0">
+ <tr><td class="label">
+ <&|/l&>Aggregator</&>:
+ </td>
+ <td><& SelectAndOr, Name => "AndOr" &>
+ </td></tr>
+ </table>
+ </td></tr>
+ <tr>
+ <td>
+ <hr>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <table cellspacing="0" border="0">
+ <& PickBasics &>
+ <& PickCFs, cfqueues => \%cfqueues &>
+ </table>
+ </td>
+ </tr>
+ <tr><td>&nbsp;</td></tr>
+</table>
+
+</&>
+
+<%ARGS>
+$addquery => 0
+$query => undef
+%cfqueues => undef
+</%ARGS>
diff --git a/rt/html/Search/Elements/SearchPrivacy b/rt/html/Search/Elements/SearchPrivacy
new file mode 100644
index 0000000..745ba62
--- /dev/null
+++ b/rt/html/Search/Elements/SearchPrivacy
@@ -0,0 +1,55 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Object => undef
+</%args>
+% if (ref($Object) eq 'RT::User' && $Object->id == $session{'CurrentUser'}->Id) {
+<&|/l&>My saved searches</&>
+% } else {
+<&|/l, $Object->Name&>[_1]'s saved searches</&>
+% }
diff --git a/rt/html/Search/Elements/SearchesForObject b/rt/html/Search/Elements/SearchesForObject
new file mode 100644
index 0000000..45aa453
--- /dev/null
+++ b/rt/html/Search/Elements/SearchesForObject
@@ -0,0 +1,65 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Object => undef
+</%args>
+<%init>
+# Returns an array of search objects associated on $Object,
+# in the form of [Description, searchObj]
+my @result;
+while (my $search = $Object->Attributes->Next) {
+ my $desc;
+ if ($search->Name eq 'SavedSearch') {
+ push @result, [$search->Description, $search];
+ }
+ elsif ($search->Name =~ m/^Search - (.*)/) {
+ push @result, [$1, $search];
+ }
+}
+return @result;
+</%init>
diff --git a/rt/html/Search/Elements/SelectAndOr b/rt/html/Search/Elements/SelectAndOr
new file mode 100644
index 0000000..c812266
--- /dev/null
+++ b/rt/html/Search/Elements/SelectAndOr
@@ -0,0 +1,53 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<input type="radio" class="radio" name="<%$Name%>" checked value="AND" /><&|/l&>AND</&></input>
+<input type="radio" class="radio" name="<%$Name%>" value="OR" /><&|/l&>OR</&></input>
+
+<%ARGS>
+$Name => "Operator"
+</%ARGS>
diff --git a/rt/html/Search/Elements/SelectChartType b/rt/html/Search/Elements/SelectChartType
new file mode 100644
index 0000000..43a6182
--- /dev/null
+++ b/rt/html/Search/Elements/SelectChartType
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Name => 'ChartType'
+$Default => 'bar'
+</%args>
+<select name="<%$Name%>">
+% foreach my $option qw(bar pie) {
+<option value="<%$option%>" <% $option eq $Default ? 'SELECTED' : '' %>><%loc($option)%></option>
+% }
+</select>
diff --git a/rt/html/Search/Elements/SelectGroup b/rt/html/Search/Elements/SelectGroup
new file mode 100644
index 0000000..3f78d39
--- /dev/null
+++ b/rt/html/Search/Elements/SelectGroup
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+% if ($AllowNull) {
+<option value="">-</option>
+% }
+%while (my $group = $groups->Next) {
+<option value="<%$group->id%>" <%$group->id eq $Default && "SELECTED"%>><%$group->Name%></option>
+%}
+</select>
+
+<%INIT>
+my $groups = new RT::Groups($session{'CurrentUser'});
+$groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => $Domain);
+
+</%INIT>
+<%ARGS>
+$AllowNull => 1
+$Default=> ''
+$Name => 'Group'
+$Domain => 'UserDefined';
+</%ARGS>
diff --git a/rt/html/Search/Elements/SelectGroupBy b/rt/html/Search/Elements/SelectGroupBy
new file mode 100644
index 0000000..0ffb5e4
--- /dev/null
+++ b/rt/html/Search/Elements/SelectGroupBy
@@ -0,0 +1,63 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Name => 'GroupBy'
+$Default => 'Status'
+$Query => ''
+</%args>
+<select name="<% $Name %>">
+% while (@options) {
+% my ($text, $value) = (shift @options, shift @options);
+<option value="<% $value %>" <% $value eq $Default ? 'selected' : '' %>><% loc($text) %></option>
+% }
+</select>
+<%init>
+use RT::Report::Tickets;
+my $report = RT::Report::Tickets->new( $session{'CurrentUser'} );
+my @options = $report->Groupings( Query => $Query );
+</%init>
diff --git a/rt/html/Search/Elements/SelectLinks b/rt/html/Search/Elements/SelectLinks
new file mode 100644
index 0000000..f358652
--- /dev/null
+++ b/rt/html/Search/Elements/SelectLinks
@@ -0,0 +1,66 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select name="<%$Name%>">
+% foreach (@fields) {
+<option value="<%$_%>"><&|/l&><%$_%></&></option>
+% }
+</select>
+<%ARGS>
+$Name => 'LinksField'
+</%ARGS>
+
+<%INIT>
+my @fields = ('HasMember',
+ 'MemberOf',
+ 'DependsOn',
+ 'DependedOnBy',
+ 'RefersTo',
+ 'ReferredToBy',
+ 'LinkedTo',
+ );
+</%INIT>
diff --git a/rt/html/Search/Elements/SelectPersonType b/rt/html/Search/Elements/SelectPersonType
new file mode 100644
index 0000000..bc631db
--- /dev/null
+++ b/rt/html/Search/Elements/SelectPersonType
@@ -0,0 +1,84 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<select NAME ="<%$Name%>">
+% if ($AllowNull) {
+<option value="">-</option>
+% }
+% for my $option (@types) {
+% if ($Suffix) {
+<option value="<% $option %><% $Suffix %>" <%$option eq $Default && "SELECTED"%> ><%loc($option)%></option>
+% next;
+% }
+% foreach my $subtype (@subtypes) {
+<option value="<%"$option.$subtype"%>" <%$option eq $Default && $subtype eq 'EmailAddress' && "SELECTED"%> ><% loc($option) %> <% loc($subtype) %></option>
+% }
+% }
+</select>
+
+<%INIT>
+my @types;
+if ($Scope =~ 'queue') {
+ @types = qw(Cc AdminCc);
+}
+elsif ($Suffix eq 'Group') {
+ @types = qw(Requestor Cc AdminCc Watcher);
+}
+else {
+ @types = qw(Requestor Cc AdminCc Watcher Owner);
+}
+
+my @subtypes = qw(EmailAddress Name RealName Nickname Organization Address1 Address2 WorkPhone HomePhone MobilePhone PagerPhone);
+
+</%INIT>
+<%ARGS>
+$AllowNull => 1
+$Suffix => ''
+$Default=>undef
+$Scope => 'ticket'
+$Name => 'WatcherType'
+</%ARGS>
diff --git a/rt/html/Search/Elements/SelectSearchObject b/rt/html/Search/Elements/SelectSearchObject
new file mode 100644
index 0000000..f52a833
--- /dev/null
+++ b/rt/html/Search/Elements/SelectSearchObject
@@ -0,0 +1,60 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+@Objects => undef
+$Name => undef
+</%args>
+<select name="<%$Name%>">
+% foreach my $object (@Objects) {
+% if (ref($object) eq 'RT::User' && $object->id == $session{'CurrentUser'}->Id) {
+<option value="<%ref($object)%>-<%$object->id%>"><&|/l&>My saved searches</&></option>
+% } else {
+<option value="<%ref($object)%>-<%$object->id%>"><&|/l, $object->Name&>[_1]'s saved searches</&></option>
+% }
+% }
+</select>
diff --git a/rt/html/Search/Elements/SelectSearchesForObjects b/rt/html/Search/Elements/SelectSearchesForObjects
new file mode 100644
index 0000000..dc83685
--- /dev/null
+++ b/rt/html/Search/Elements/SelectSearchesForObjects
@@ -0,0 +1,69 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+@Objects => undef
+$Name => undef
+$SearchType => 'Ticket',
+</%args>
+<select name="<%$Name%>">
+% foreach my $object (@Objects) {
+% if (ref($object) eq 'RT::User' && $object->id == $session{'CurrentUser'}->Id) {
+<option value=""><&|/l&>My saved searches</&></option>
+% } else {
+<option value=""></option>
+<option value=""><&|/l, $object->Name&>[_1]'s saved searches</&></option>
+% }
+% my @searches = $object->Attributes->Named('SavedSearch');
+% foreach my $search (@searches) {
+% # Skip it if it is not of search type we want.
+% next if ($search->SubValue('SearchType')
+% && $search->SubValue('SearchType') ne $SearchType);
+<option value="<%ref($object)%>-<%$object->id%>-SavedSearch-<%$search->Id%>"> -<%$search->Description||loc('Unnamed search')%></option>
+% }
+% }
+</select>
diff --git a/rt/html/Search/Results.html b/rt/html/Search/Results.html
new file mode 100755
index 0000000..01bdfff
--- /dev/null
+++ b/rt/html/Search/Results.html
@@ -0,0 +1,177 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title, Refresh => $session{'tickets_refresh_interval'},
+ RSSAutoDiscovery => $RSSFeedURL &>
+<& /Ticket/Elements/Tabs,
+ current_tab => "Search/Results.html".$QueryString,
+ Title => $title,
+ Format => $Format,
+ Query => $Query,
+ Rows => $Rows,
+ OrderBy => $OrderBy,
+ Order => $Order &>
+<& /Elements/TicketList,
+ Query => $Query,
+ AllowSorting => 1,
+ OrderBy => $OrderBy,
+ Order => $Order,
+ Rows => $Rows,
+ Page => $Page,
+ Format => $Format,
+ BaseURL => $RT::WebPath."/Search/Results.html?"
+
+ &>
+% my %hiddens = (Query => $Query, Format => $Format, Rows => $Rows, OrderBy => $OrderBy, Order => $Order, HideResults => $HideResults, Page => $Page );
+<div align="right">
+<form method="get" action="<%$RT::WebPath%>/Search/Results.html">
+%foreach my $key (keys(%hiddens)) {
+<input type="hidden" class="hidden" name="<%$key%>" value="<%defined($hiddens{$key})?$hiddens{$key}:''%>"/>
+%}
+<& /Elements/Refresh, Name => 'TicketsRefreshInterval', Default => $session {'tickets_refresh_interval'} &>
+<input type="submit" class="button" value="<&|/l&>Go!</&>" />
+</form>
+</div>
+<div align="right">
+<a href="<%$RT::WebPath%>/Search/Bulk.html<%$QueryString%>"><&|/l&>Update multiple tickets</&></a><br />
+<a href="<%$RT::WebPath%>/Search/Results.html<%$QueryString%>"><&|/l&>Bookmarkable link</&></a><br />
+<a href="<%$RT::WebPath%>/Search/Results.tsv<%$QueryString%>"><&|/l&>spreadsheet</&></a> |
+<a href="<%$RSSFeedURL%>"><&|/l&>RSS</&></a> |
+<a href="<%$RT::WebPath%>/Tools/Offline.html<%$ShortQueryString%>"><&|/l&>Work offline</&></a><br />
+<form method="get" action="<%$RT::WebPath%>/Search/Chart.html"><&|/l&>chart</&>
+% %hiddens = (Query => $Query, Format => $Format, Rows => $Rows, OrderBy => $OrderBy, Order => $Order);
+%foreach my $key (keys(%hiddens)) {
+<input type="hidden" class="hidden" name="<%$key%>" value="<%defined($hiddens{$key})?$hiddens{$key}:''%>"/>
+%}
+<&|/l, $m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query) &>grouped by [_1]</&>
+<&|/l, $m->scomp('Elements/SelectChartType', Name => 'ChartStyle') &>style: [_1]</&>
+<input type="submit" class="button" value="<%loc('Go!')%>" />
+</form>
+<& /Elements/Callback, _CallbackName => 'SearchActions', QueryString => $QueryString&>
+</div>
+<%INIT>
+# Read from user preferences
+my $prefs = $session{'CurrentUser'}->UserObj->Preferences("SearchDisplay") || {};
+
+# These variables are what define a search_hash; this is also
+# where we give sane defaults.
+$Format ||= $prefs->{'Format'};
+$Order ||= $prefs->{'Order'} || 'ASC';
+$OrderBy ||= $prefs->{'OrderBy'} || 'id';
+
+# Some forms pass in "RowsPerPage" rather than "Rows"
+# We call it RowsPerPage everywhere else.
+
+if ( !defined($Rows) ) {
+ if ( $ARGS{'RowsPerPage'} ) {
+ $Rows = $ARGS{'RowsPerPage'};
+ } elsif ( defined $prefs->{'RowsPerPage'} ) {
+ $Rows = $prefs->{'RowsPerPage'};
+ } else {
+ $Rows = 50;
+ }
+}
+
+my ($title, $ticketcount);
+$session{'i'}++;
+$session{'tickets'} = RT::Tickets->new($session{'CurrentUser'}) ;
+$session{'tickets'}->FromSQL($Query) if ($Query);
+
+if ($OrderBy =~ /\|/) {
+ # Multiple Sorts
+ my @OrderBy = split /\|/,$OrderBy;
+ my @Order = split /\|/,$Order;
+ $session{'tickets'}->OrderByCols(
+ map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0
+ .. $#OrderBy ) );;
+} else {
+ $session{'tickets'}->OrderBy(FIELD => $OrderBy, ORDER => $Order);
+}
+
+$session{'CurrentSearchHash'} = {
+ Format => $Format,
+ Query => $Query,
+ Page => $Page,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ RowsPerPage => $Rows
+ };
+
+
+if ( $session{'tickets'}->Query()) {
+ $ticketcount = $session{tickets}->CountAll();
+ $title = loc('Found [quant,_1,ticket]', $ticketcount);
+} else {
+ $title = loc("Find tickets");
+}
+
+my $QueryString = "?".$m->comp('/Elements/QueryString',
+ Query => $Query,
+ Format => $Format,
+ Rows => $Rows,
+ OrderBy => $OrderBy,
+ Order => $Order,
+ Page => $Page);
+my $ShortQueryString = "?".$m->comp('/Elements/QueryString', Query => $Query);
+my $RSSFeedURL = "$RT::WebPath/Search/Results.rdf$ShortQueryString";
+
+if ($ARGS{'TicketsRefreshInterval'}) {
+ $session{'tickets_refresh_interval'} = $ARGS{'TicketsRefreshInterval'};
+}
+</%INIT>
+<%CLEANUP>
+$session{'tickets'}->PrepForSerialization();
+</%CLEANUP>
+<%ARGS>
+$Query => undef
+$Format => undef
+$HideResults => 0
+$Rows => undef
+$Page => 1
+$OrderBy => undef
+$Order => undef
+</%ARGS>
diff --git a/rt/html/Search/Results.rdf b/rt/html/Search/Results.rdf
new file mode 100644
index 0000000..fe97a31
--- /dev/null
+++ b/rt/html/Search/Results.rdf
@@ -0,0 +1,87 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%INIT>
+
+my $Tickets = RT::Tickets->new($session{'CurrentUser'});
+$Tickets->FromSQL($ARGS{'Query'});
+$r->content_type('application/rss+xml');
+
+
+
+ # create an RSS 1.0 file (http://purl.org/rss/1.0/)
+ use XML::RSS;
+ my $rss = new XML::RSS (version => '1.0');
+ $rss->channel(
+ title => "$RT::rtname: Syndicated Search",
+ link => $RT::WebURL,
+ description => "",
+ dc => {
+ },
+ syn => {
+ updatePeriod => "hourly",
+ updateFrequency => "1",
+ updateBase => "1901-01-01T00:00+00:00",
+ },
+ );
+
+
+ while ( my $Ticket = $Tickets->Next()) {
+ my $row;
+ $rss->add_item(
+ title => $Ticket->Subject,
+ link => $RT::WebURL."/Ticket/Display.html?id=".$Ticket->id,
+ description => $Ticket->Transactions->First->Content,
+ dc => {
+ subject => ($Ticket->Subject || loc('No subject')),
+ creator => $Ticket->CreatorObj->RealName . "<".$Ticket->CreatorObj->EmailAddress.">",
+ },
+ );
+ }
+$m->out($rss->as_string);
+$m->abort();
+</%INIT>
diff --git a/rt/html/Search/Results.tsv b/rt/html/Search/Results.tsv
new file mode 100644
index 0000000..bb19073
--- /dev/null
+++ b/rt/html/Search/Results.tsv
@@ -0,0 +1,134 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%ARGS>
+$OrderBy => 'id'
+$Order => 'ASC'
+</%ARGS>
+<%INIT>
+
+my $Tickets = RT::Tickets->new( $session{'CurrentUser'} );
+$Tickets->FromSQL( $ARGS{'Query'} );
+if ( $OrderBy =~ /\|/ ) {
+
+ # Multiple Sorts
+ my @OrderBy = split /\|/, $OrderBy;
+ my @Order = split /\|/, $Order;
+ $Tickets->OrderByCols(
+ map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } }
+ ( 0 .. $#OrderBy ) );
+}
+else {
+ $Tickets->OrderBy( FIELD => $OrderBy, ORDER => $Order );
+}
+
+my @rows;
+my %known_cfs;
+
+my @attrs = qw( id QueueObj->Name Subject Status TimeEstimated TimeWorked TimeLeft Priority FinalPriority OwnerObj->Name
+ Requestors->MemberEmailAddressesAsString Cc->MemberEmailAddressesAsString AdminCc->MemberEmailAddressesAsString
+ DueObj->ISO ToldObj->ISO CreatedObj->ISO ResolvedObj->ISO LastUpdatedObj->ISO);
+
+$r->content_type('application/vnd.ms-excel');
+while ( my $Ticket = $Tickets->Next()) {
+ my $row;
+ foreach my $attr (@attrs) {
+ if ($attr =~ /(.*)->ISO$/ and $Ticket->$1->Unix <= 0) {
+ $row->{$attr} = "";
+ } else {
+ my $method = '$Ticket->'.$attr.'()';
+ $row->{$attr} = eval $method;
+ if ($@) {die "Failed to find $attr - ". $@};
+ }
+ }
+
+ my $cfs = $Ticket->QueueObj->TicketCustomFields();
+ while (my $cf = $cfs->Next) {
+ my @content;
+ my $values = $Ticket->CustomFieldValues($cf->Id);
+ while (my $value = $values->Next) {
+ push @content, $value->Content;
+ }
+ $row->{'CustomField-'.$cf->Id} = join(', ',@content);
+ if ($row->{'CustomField-'.$cf->Id}) {
+ $known_cfs{$cf->Id} = $cf->Name;
+ }
+ }
+ push @rows, $row;
+}
+
+{
+ my @header;
+ foreach my $attr (@attrs) {
+ my $label = $attr;
+ $label =~ s'Obj-.(?:AsString|Name|ISO)''g;
+ $label =~ s'-\>MemberEmailAddressesAsString''g;
+ push @header, $label;
+ }
+ foreach my $id (sort keys %known_cfs) {
+ push @header, "CF-".$known_cfs{$id};
+ }
+ $m->out(join("\t", @header));
+ $m->out("\n");
+}
+
+foreach my $row (@rows) {
+ my @row;
+ foreach my $attr(@attrs) {
+ push @row, $row->{"$attr"};
+ }
+ foreach my $id (sort keys %known_cfs) {
+ my $val = $row->{'CustomField-'.$id};
+ $val =~ s/(\n|\r)//g;
+ push @row, $val;
+ }
+ $m->out(join("\t",@row));
+ $m->out("\n");
+}
+
+$m->abort();
+</%INIT>
diff --git a/rt/html/Search/Simple.html b/rt/html/Search/Simple.html
new file mode 100644
index 0000000..01424a3
--- /dev/null
+++ b/rt/html/Search/Simple.html
@@ -0,0 +1,107 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Elements/Tabs,
+ current_toptab => "Search/Simple.html",
+ Title => $title
+&>
+
+<& /Elements/Callback, _CallbackName => 'PreForm', %ARGS &>
+
+<div id="SimpleSearchForm">
+<form action="Simple.html" method="get">
+
+<p><&|/l&>Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments.</&></p>
+
+<p><&|/l&>Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>.</&></p>
+<p><&|/l&>RT will look for anything else you enter in ticket subjects.</&></p>
+
+<br />
+<br />
+<div align="center">
+<input name="q" size="60" /><input type="submit" class="button" value="<&|/l&>Search</&>" />
+</div>
+
+</form>
+
+<& /Elements/Callback, _CallbackName => 'PostForm', %ARGS &>
+
+</div>
+
+<%INIT>
+my $title = loc("Search for tickets");
+use RT::Search::Googleish;
+
+if ($q) {
+ my $tickets = new RT::Tickets( $session{'CurrentUser'} );
+
+ $m->comp('/Elements/Callback', %ARGS, _CallbackName => 'ModifyQuery', query => \$q);
+
+ if ($q =~ /^(\d+)$/) {
+ RT::Interface::Web::Redirect($RT::WebURL."/Ticket/Display.html?id=".$q);
+ }
+
+ my %args = (
+ Argument => $q,
+ TicketsObj => $tickets,
+ );
+
+ $m->comp('/Elements/Callback', %ARGS, _CallbackName => 'SearchArgs', args => \%args);
+
+ my $search = RT::Search::Googleish->new(%args);
+
+ $m->comp( "Results.html", Query => $search->QueryToSQL() );
+ $m->comp( "/Elements/Footer" );
+ $m->abort();
+}
+</%INIT>
+
+<%ARGS>
+$q => undef
+</%ARGS>
+
diff --git a/rt/html/SelfService/Attachment/dhandler b/rt/html/SelfService/Attachment/dhandler
new file mode 100644
index 0000000..e7d59e6
--- /dev/null
+++ b/rt/html/SelfService/Attachment/dhandler
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+$m->comp('/Ticket/Attachment/dhandler', %ARGS);
+$m->abort;
+</%init>
diff --git a/rt/html/SelfService/Closed.html b/rt/html/SelfService/Closed.html
new file mode 100644
index 0000000..766b0bc
--- /dev/null
+++ b/rt/html/SelfService/Closed.html
@@ -0,0 +1,56 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /SelfService/Elements/Header, Title => loc('Closed tickets') &>
+
+<& /SelfService/Elements/MyRequests, status => ['rejected', 'resolved'],
+ friendly_status => loc('closed'),
+ BaseURL => $RT::WebPath . "/SelfService/Closed.html?",
+ Page => $Page &>
+<%ARGS>
+$Page => 1
+</%ARGS>
diff --git a/rt/html/SelfService/Create.html b/rt/html/SelfService/Create.html
new file mode 100644
index 0000000..f66961d
--- /dev/null
+++ b/rt/html/SelfService/Create.html
@@ -0,0 +1,117 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& Elements/Header, Title => loc("Create a ticket") &>
+
+<form action="Display.html" method="post" enctype="multipart/form-data">
+<input type="hidden" class="hidden" name="id" value="new" />
+
+<table>
+<tr>
+<td class="label">
+<&|/l&>Queue</&>:
+</td>
+<td class="value">
+ <input type="hidden" class="hidden" name="Queue" value="<%$queue_obj->id%>" />
+ <strong><%$queue_obj->Name%></strong> (<%$queue_obj->Description%>)
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Requestors</&>:
+</td>
+<td class="value">
+<input name="Requestors" value="<%$session{CurrentUser}->EmailAddress%>" size="20" />
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Cc</&>:
+</td>
+<td class="value">
+ <input name="Cc" size="20" />
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Subject</&>:
+</td>
+<td class="value">
+<input name="Subject" size="60" maxsize="200" value="" />
+</td>
+</tr>
+<tr>
+ <td colspan="2">
+ <& /Ticket/Elements/EditCustomFields, QueueObj => $queue_obj &>
+ </td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Attach file</&>:
+</td>
+<td class="value">
+<input name="Attach" type="file" />
+</td>
+</tr>
+<tr>
+<td colspan="2">
+<&|/l&>Describe the issue below</&>:<br />
+<& /Elements/MessageBox &>
+</td>
+</tr>
+</table>
+<& /Elements/Submit, Label => loc("Create ticket")&>
+
+
+</form>
+<%args>
+$Queue => undef
+</%args>
+<%init>
+my $queue_obj = RT::Queue->new($session{'CurrentUser'});
+$queue_obj->Load($Queue);
+</%init>
diff --git a/rt/html/SelfService/CreateTicketInQueue.html b/rt/html/SelfService/CreateTicketInQueue.html
new file mode 100755
index 0000000..fd7e426
--- /dev/null
+++ b/rt/html/SelfService/CreateTicketInQueue.html
@@ -0,0 +1,63 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& Elements/Header, Title => loc("Create a ticket") &>
+
+<h1><&|/l&>Select a queue for your new ticket</&></h1>
+
+<dl>
+% while (my $queue = $queues->Next) {
+% next unless $queue->CurrentUserHasRight('CreateTicket');
+
+<dt><a href="<%$RT::WebPath%>/SelfService/Create.html?Queue=<%$queue->id%>"><%$queue->Name%></a></dt>
+<dd><%$queue->Description%></dd>
+% }
+</dl>
+<%init>
+my $queues = RT::Queues->new($session{'CurrentUser'});
+$queues->UnLimit;
+</%init>
diff --git a/rt/html/SelfService/Display.html b/rt/html/SelfService/Display.html
new file mode 100644
index 0000000..a38d259
--- /dev/null
+++ b/rt/html/SelfService/Display.html
@@ -0,0 +1,235 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /SelfService/Elements/Header, Title => loc('#[_1]: [_2]', $Ticket->id, $Ticket->Subject) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+ <table width="100%" class="ticketsummary" >
+ <tr>
+ <td valign="top" width="50%" class="boxcontainer">
+ <&| /Widgets/TitleBox, title => loc('The Basics'),
+ title_class=> 'inverse',
+ color => "#993333" &>
+ <& /Ticket/Elements/ShowBasics, Ticket => $Ticket &>
+ <& /Ticket/Elements/ShowCustomFields, Ticket => $Ticket &>
+ </&>
+</td>
+ <td valign="top" width="50%" class="boxcontainer">
+ <&| /Widgets/TitleBox, title => loc("Dates"),
+ title_class=> 'inverse',
+ color => "#663366" &>
+ <& /Ticket/Elements/ShowDates, Ticket => $Ticket, UpdatedLink => 0 &>
+ </&>
+</td>
+</tr>
+</table>
+
+
+
+%#!!pape: selfservice_find_attachments.patch {{
+<& /Ticket/Elements/ShowHistory,
+ Ticket => $Ticket,
+ URIFile => "Display.html",
+ ShowHeaders => $ARGS{'ShowHeaders'},
+ AttachPath => "Attachment",
+ Attachments => $attachments,
+ UpdatePath => "Update.html"
+&>
+%#!!pape: selfservice_find_attachments.patch }}
+
+
+
+<%INIT>
+
+my ( $field, @results );
+
+# {{{ Load the ticket
+#If we get handed two ids, mason will make them an array. bleck.
+# We want teh first one. Just because there's no other sensible way
+# to deal
+my @id = ( ref $id eq 'ARRAY' ) ? @{$id} : ($id);
+
+my $Ticket = new RT::Ticket( $session{'CurrentUser'} );
+
+# store the uploaded attachment in session
+if ( $ARGS{'Attach'} ) { # attachment?
+ $session{'Attachments'} = {} unless defined $session{'Attachments'};
+
+ my $subject = "$ARGS{'Attach'}";
+
+ # since CGI.pm deutf8izes the magic field, we need to add it back.
+ Encode::_utf8_on($subject);
+
+ # strip leading directories
+ $subject =~ s#^.*[\\/]##;
+
+ my $attachment = MakeMIMEEntity(
+ Subject => $subject,
+ Body => "",
+ AttachmentFieldName => 'Attach'
+ );
+
+ $session{'Attachments'} =
+ { %{ $session{'Attachments'} || {} },
+ $ARGS{'Attach'} => $attachment };
+}
+
+if ( $id[0] eq 'new' ) {
+
+ # {{{ Create a new ticket
+
+ my $Queue = new RT::Queue( $session{'CurrentUser'} );
+ unless ( $Queue->Load( $ARGS{'Queue'} ) ) {
+ $m->comp( 'Error.html', Why => loc('Queue not found') );
+ $m->abort;
+ }
+
+ unless ( $Queue->CurrentUserHasRight('CreateTicket') ) {
+ $m->comp( 'Error.html',
+ Why =>
+ loc('You have no permission to create tickets in that queue.') );
+ $m->abort;
+ }
+
+
+ ( $Ticket, @results ) =
+ CreateTicket( Attachments => $session{'Attachments'}, %ARGS, Status => 'new' );
+
+ unless ( $Ticket->id ) {
+ $m->comp( 'Error.html', Why => join( "\n", @results ));
+ $m->abort();
+ }
+
+ # }}}
+
+ # delete temporary storage entry to make WebUI clean
+ unless ( keys %{ $session{'Attachments'} } and $ARGS{'UpdateAttach'} ) {
+ delete $session{'Attachments'};
+ }
+
+ # }}}
+ }
+ else {
+ unless ( $Ticket->Load( $id[0] ) ) {
+ $m->comp( 'Error.html',
+ Why => loc( "Couldn't load ticket '[_1]'", $id ) );
+ $m->abort();
+ }
+
+ my ( $code, $msg );
+
+ #Update the status
+ if ( ( defined $ARGS{'Status'} )
+ and $ARGS{'Status'}
+ and ( $ARGS{'Status'} ne $Ticket->Status ) )
+ {
+ ( $code, $msg ) = $Ticket->SetStatus( $ARGS{'Status'} );
+ push @results, "$msg";
+ }
+
+ # }}}
+
+ if (
+ $session{'Attachments'}
+ || ( defined $ARGS{'UpdateContent'}
+ && $ARGS{'UpdateContent'} ne ''
+ && $ARGS{'UpdateContent'} ne "-- \n"
+ . $session{'CurrentUser'}->UserObj->Signature )
+ )
+ {
+ $ARGS{UpdateAttachments} = $session{'Attachments'};
+ }
+ ProcessUpdateMessage(
+ ARGSRef => \%ARGS,
+ Actions => \@results,
+ TicketObj => $Ticket
+ );
+ delete $session{'Attachments'};
+
+ # delete temporary storage entry to make WebUI clean
+ unless ( keys %{ $session{'Attachments'} } and $ARGS{'UpdateAttach'} ) {
+ delete $session{'Attachments'};
+ }
+
+ my @cfupdates = ProcessObjectCustomFieldUpdates(Object => $Ticket, ARGSRef => \%ARGS);
+ push (@results, @cfupdates);
+
+ # }}}
+
+ }
+
+ # This code does automatic redirection if any updates happen.
+
+ unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
+ $m->comp( 'Error.html',
+ Why => loc("No permission to display that ticket") );
+ $m->abort();
+ }
+
+ if (@results) {
+ # We've done something, so we need to clear the decks to avoid
+ # resubmission on refresh.
+ # But we need to store Actions somewhere too, so we don't lose them.
+ $session{"Actions"} = \@results;
+ RT::Interface::Web::Redirect($RT::WebURL."SelfService/Display.html?id="
+ . $Ticket->id);
+ } else {
+ @results = @{ delete $session{"Actions"} || [] };
+ }
+
+ my $Transactions = $Ticket->Transactions;
+
+ my $attachments =
+ $m->comp( '/Ticket/Elements/FindAttachments', Ticket => $Ticket );
+
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/SelfService/Elements/GotoTicket b/rt/html/SelfService/Elements/GotoTicket
new file mode 100644
index 0000000..5eac306
--- /dev/null
+++ b/rt/html/SelfService/Elements/GotoTicket
@@ -0,0 +1,48 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<form action="<%$RT::WebPath%>/SelfService/Display.html"><input type="submit" class="button" value="<&|/l&>Goto ticket</&>" />&nbsp;<input size="4" name="id" /></form>
diff --git a/rt/html/SelfService/Elements/Header b/rt/html/SelfService/Elements/Header
new file mode 100644
index 0000000..f248292
--- /dev/null
+++ b/rt/html/SelfService/Elements/Header
@@ -0,0 +1,49 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, %ARGS, Prefs => '/SelfService/Prefs.html' &>
+<& /SelfService/Elements/Tabs, %ARGS &>
diff --git a/rt/html/SelfService/Elements/MyRequests b/rt/html/SelfService/Elements/MyRequests
new file mode 100644
index 0000000..41f6b09
--- /dev/null
+++ b/rt/html/SelfService/Elements/MyRequests
@@ -0,0 +1,84 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&| /Widgets/TitleBox, title => $title &>
+<& /Elements/TicketList, Title => $title,
+ Format => @Format,
+ Query => $Query,
+ Order => $Order,
+ OrderBy => $OrderBy,
+ BaseURL => $BaseURL,
+ Page => $Page &>
+</&>
+
+<%INIT>
+my $id = $session{'CurrentUser'}->id;
+my $Query = "( "
+ . join( ' OR ', map "$_.id = $id", @roles )
+ . ")";
+if ( @status ) {
+ $Query .= " AND ( "
+ . join( ' OR ', map "Status = '$_'", @status )
+ . " )";
+}
+my $Order = "ASC";
+my $OrderBy = "Created";
+my @Format = qq{
+ '<B><A HREF="$RT::WebPath/SelfService/Display.html?id=__id__">__id__</a></B>/TITLE:#',
+ '<B><A HREF="$RT::WebPath/SelfService/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
+ Status,
+ Requestors,
+ OwnerName};
+</%INIT>
+<%ARGS>
+$friendly_status => loc('open')
+$title => loc("My [_1] tickets", $friendly_status)
+@roles => ('Watcher')
+@status => ('open', 'new', 'stalled')
+$BaseURL => undef
+$Page => 1
+</%ARGS>
diff --git a/rt/html/SelfService/Elements/Tabs b/rt/html/SelfService/Elements/Tabs
new file mode 100644
index 0000000..164fa28
--- /dev/null
+++ b/rt/html/SelfService/Elements/Tabs
@@ -0,0 +1,113 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Callback, tabs => $tabs, %ARGS &>
+<& /Elements/PageLayout,
+ current_toptab => $current_toptab,
+ current_tab => $current_tab,
+ toptabs => $tabs,
+ topactions => $actions,
+ title => $Title
+&>
+<a name="skipnav" id="skipnav" accesskey="8"></a>
+<%INIT>
+my $queues = RT::Queues->new($session{'CurrentUser'});
+$queues->UnLimit;
+
+my $queue_count = 0;
+my $queue_id = 1;
+
+while (my $queue = $queues->Next) {
+ next unless $queue->CurrentUserHasRight('CreateTicket');
+ $queue_id = $queue->id;
+ $queue_count++;
+ last if ($queue_count > 1);
+}
+
+if ($Title) {
+$Title = loc("RT Self Service") . " / " . $Title;
+} else {
+$Title = loc("RT Self Service");
+
+}
+my ($tab);
+my $tabs = { A => { title => loc('Open tickets'),
+ path => 'SelfService/',
+ },
+ B => { title => loc('Closed tickets'),
+ path => 'SelfService/Closed.html',
+ },
+ };
+
+if ($queue_count > 1) {
+ $tabs->{C} = { title => loc('New ticket'),
+ path => 'SelfService/CreateTicketInQueue.html'
+ };
+} else {
+ $tabs->{C} = { title => loc('New ticket'),
+ path => 'SelfService/Create.html?Queue=' . $queue_id
+ };
+}
+
+if ($session{'CurrentUser'}->HasRight( Right => 'ModifySelf',
+ Object => $RT::System )) {
+ $tabs->{Z} = { title => loc('Preferences'),
+ path => 'SelfService/Prefs.html'
+ };
+}
+
+my $actions = {
+ B => { html => $m->scomp('GotoTicket')
+ }
+ };
+</%INIT>
+<%ARGS>
+$Title => undef
+$current_toptab => undef
+$current_tab => undef
+</%ARGS>
+
diff --git a/rt/html/SelfService/Error.html b/rt/html/SelfService/Error.html
new file mode 100644
index 0000000..81a483d
--- /dev/null
+++ b/rt/html/SelfService/Error.html
@@ -0,0 +1,70 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /SelfService/Elements/Header, Title => loc('Error') &>
+<h2 class="title"><%loc('Error')%></h2>
+<&| /Widgets/TitleBox, title => $Title &>
+<%$Why%>
+<br />
+<font size="-1">
+<%$Details%>
+</font>
+</&>
+</body>
+</html>
+
+
+<%args>
+$Code => undef
+$Details => undef
+$Title => loc("RT Error")
+$Why => loc("the calling component did not specify why")
+</%args>
+
+<%INIT>
+$RT::Logger->error("WebRT: $Why ($Details)");
+</%INIT>
diff --git a/rt/html/SelfService/Prefs.html b/rt/html/SelfService/Prefs.html
new file mode 100644
index 0000000..0cd3756
--- /dev/null
+++ b/rt/html/SelfService/Prefs.html
@@ -0,0 +1,92 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /SelfService/Elements/Header, Title => loc('Preferences') &>
+
+<& /Elements/ListActions, actions => \@results &>
+<form method="post">
+
+% unless ($RT::WebExternalAuth and !$RT::WebFallbackToInternalAuth) {
+<&| /Widgets/TitleBox, title => loc('Change password') &>
+<&|/l&>New password</&>: <input type="password" name="NewPass1" size="16" />
+<&|/l&>Confirm</&>: <input type="password" name="NewPass2" size="16" />
+</&>
+<br />
+% }
+<& /Elements/Submit, Label => loc('Save Changes') &>
+ </form>
+
+
+<%INIT>
+my @results;
+
+if ($NewPass1) {
+ if ($NewPass1 ne $NewPass2) {
+ push (@results, "Passwords did not match.");
+ }
+ else {
+ my ($val, $msg)=$session{'CurrentUser'}->UserObj->SetPassword($NewPass1);
+ push (@results, "Password: ".$msg);
+ }
+}
+if ($Signature) {
+ $Signature =~ s/(\r\n|\r)/\n/g;
+ if ($Signature ne $session{'CurrentUser'}->UserObj->Signature) {
+ my ($val, $msg)=$session{'CurrentUser'}->UserObj->SetSignature($Signature);
+ push (@results, "Signature: ".$msg);
+ }
+}
+#A hack to make sure that session gets rewritten.
+
+$session{'i'}++;
+</%INIT>
+
+<%ARGS>
+$Signature => undef
+$NewPass1 => undef
+$NewPass2 => undef
+</%ARGS>
diff --git a/rt/html/SelfService/Update.html b/rt/html/SelfService/Update.html
new file mode 100644
index 0000000..b10716f
--- /dev/null
+++ b/rt/html/SelfService/Update.html
@@ -0,0 +1,129 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /SelfService/Elements/Header,
+ Title =>loc('Update ticket #[_1]', $Ticket->id)
+&>
+
+
+<form action="Display.html" method="post" enctype="multipart/form-data">
+<input type="hidden" class="hidden" name="UpdateType" value="response" />
+<input type="hidden" class="hidden" name="id" value="<%$Ticket->Id%>" />
+<table>
+ <tr>
+ <td class="label">
+ <&|/l&>Status</&>
+ </td>
+ <td class="value">
+ <& /Elements/SelectStatus, Name=>"Status", DefaultLabel => loc("[_1] (Unchanged)",loc($DefaultStatus)) &>
+ </td>
+ </tr>
+ <tr>
+ <td class="label">
+ <&|/l&>Subject</&>
+ </td>
+ <td class="value">
+ <input name="UpdateSubject" size="60" value="<% $Ticket->Subject %>" />
+ </td>
+
+ </tr>
+% if (exists $session{'Attachments'}) {
+<tr>
+ <td class="label">
+ <&|/l&>Attached file</&>
+ </td>
+ <td colspan="5" class="value">
+ <&|/l&>Check box to delete</&><br />
+% foreach my $attach_name (keys %{$session{'Attachments'}}) {
+ <input type="checkbox" class="checkbox" name="DeleteAttach-<%$attach_name%>" value="1" /><%$attach_name%><br />
+% } # end of foreach
+ </td>
+</tr>
+% } # end of if
+<tr>
+ <td class"label">
+ <&|/l&>Attach</&>
+ </td>
+ <td class="value">
+ <input name="Attach" type="file" />
+ <input type="hidden" class="hidden" name="UpdateAttach" value="1" />
+ </td>
+ </tr>
+</table>
+<& /Ticket/Elements/EditCustomFields, TicketObj => $Ticket &>
+<& /Elements/MessageBox,
+ Name => "UpdateContent",
+ QuoteTransaction => $ARGS{QuoteTransaction}
+ &>
+ <br />
+
+
+<& /Elements/Submit &>
+ </form>
+
+
+
+<%INIT>
+
+my $Ticket = LoadTicket($id);
+
+my $title = loc( "Update ticket #[_1]", $Ticket->id );
+
+$DefaultStatus = $Ticket->Status() unless ($DefaultStatus);
+
+
+Abort( loc("No permission to view update ticket") )
+ unless ( $Ticket->CurrentUserHasRight('ReplyToTicket')
+ or $Ticket->CurrentUserHasRight('ModifyTicket') );
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+$Action => undef
+$DefaultStatus => undef
+</%ARGS>
diff --git a/rt/html/SelfService/index.html b/rt/html/SelfService/index.html
new file mode 100644
index 0000000..cd7dfb0
--- /dev/null
+++ b/rt/html/SelfService/index.html
@@ -0,0 +1,54 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /SelfService/Elements/Header, Title => loc('Open tickets') &>
+
+<& /SelfService/Elements/MyRequests, BaseURL => $RT::WebPath . "/SelfService/?",
+ Page => $Page &>
+<%ARGS>
+$Page => 1
+</%ARGS>
diff --git a/rt/html/Ticket/Attachment/dhandler b/rt/html/Ticket/Attachment/dhandler
new file mode 100644
index 0000000..4872ef2
--- /dev/null
+++ b/rt/html/Ticket/Attachment/dhandler
@@ -0,0 +1,94 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%perl>
+ my ($ticket, $trans,$attach, $filename);
+ my $arg = $m->dhandler_arg; # get rest of path
+ if ($arg =~ '^(\d+)/(\d+)') {
+ $trans = $1;
+ $attach = $2;
+ }
+ else {
+ Abort("Corrupted attachment URL.");
+ }
+ my $AttachmentObj = new RT::Attachment($session{'CurrentUser'});
+ $AttachmentObj->Load($attach) || Abort("Attachment '$attach' could not be loaded");
+
+
+ unless ($AttachmentObj->id) {
+ Abort("Bad attachment id. Couldn't find attachment '$attach'\n");
+ }
+ unless ($AttachmentObj->TransactionId() == $trans ) {
+ Abort("Bad transaction number for attachment. $trans should be".$AttachmentObj->TransactionId() ."\n");
+
+ }
+
+ my $content_type = $AttachmentObj->ContentType || 'text/plain';
+
+ unless ($RT::TrustHTMLAttachments) {
+ $content_type = 'text/plain' if ($content_type =~ /^text\/html/i);
+ }
+
+ if (my $enc = $AttachmentObj->OriginalEncoding) {
+ # normalize Encode.pm convention with IANA ones
+ $enc = 'big5' if $enc eq 'big5-eten';
+ $enc = 'utf-8' if $enc eq 'utf8';
+ $content_type .= ";charset=$enc";
+ }
+
+ # unless ($RT::TrustMIMEAttachments) {
+ # $content_type = 'application/octet-stream';
+ # }
+
+ $r->content_type( $content_type );
+ $m->clear_buffer();
+ $m->out($AttachmentObj->OriginalContent);
+ $m->abort;
+</%perl>
+<%attr>
+AutoFlush => 0
+</%attr>
diff --git a/rt/html/Ticket/Create.html b/rt/html/Ticket/Create.html
new file mode 100644
index 0000000..2061b3b
--- /dev/null
+++ b/rt/html/Ticket/Create.html
@@ -0,0 +1,405 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header,
+ Title => loc("Create a new ticket"),
+ onload => "hide(document.getElementById('Ticket-Create-details'));" &>
+<& /Elements/Tabs,
+ current_toptab => "Ticket/Create.html",
+ Title => loc("Create a new ticket"),
+ actions => $actions &>
+<& /Elements/ListActions, actions => \@results &>
+<form action="<%$RT::WebPath%>/Ticket/Create.html" method="post" enctype="multipart/form-data" name="TicketCreate">
+<input type="hidden" class="hidden" name="id" value="new" />
+<& /Elements/Callback, _CallbackName => 'FormStart',ARGSRef =>\%ARGS &>
+
+<div id="Ticket-Create-basics">
+<a name="basics"></a>
+<&| /Widgets/TitleBox, title => loc("Create a new ticket") &>
+<table border="0" cellpadding="0" cellspacing="0">
+<tr><td class="label"><&|/l&>Queue</&>:</td>
+<td class="value"><& Elements/ShowQueue, QueueObj => $QueueObj &>
+<input type="hidden" class="hidden" name="Queue" value="<% $QueueObj->Name %>" />
+</td>
+<td class="label"><&|/l&>Status</&>:
+</td>
+<td class="value">
+<& /Elements/SelectStatus, Name => "Status", Default => $ARGS{Status}||'new', DefaultValue => 0 &>
+</td>
+<td class="label">
+<&|/l&>Owner</&>:
+</td>
+<td class="value">
+<& /Elements/SelectOwner, Name => "Owner", QueueObj => $QueueObj, Default => $ARGS{Owner}||$RT::Nobody->Id, DefaultValue => 0 &>
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Requestors</&>:
+</td>
+<td class="value" colspan="5">
+<input name="Requestors" value="<% ($ARGS{Requestors}) || $session{CurrentUser}->EmailAddress %>" size="40" />
+</td>
+</tr>
+<tr>
+<td class="labeltop">
+<&|/l&>Cc</&>:
+</td>
+<td class="value" colspan="5">
+<input name="Cc" size="40" value="<% $ARGS{Cc} %>" /><br />
+<i><font size="-2">
+<&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)</&></font></i>
+</td>
+</tr>
+<tr>
+<td class="labeltop">
+<&|/l&>Admin Cc</&>:
+</td>
+<td class="value" colspan="5">
+<input name="AdminCc" size="40" value="<% $ARGS{AdminCc} %>" /><br />
+<i><font size="-2">
+<&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)</&></font></i>
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Subject</&>:
+</td>
+<td class="value" colspan="5">
+<input name="Subject" size="60" maxsize="200" value="<%$ARGS{Subject} || ''%>" />
+</td>
+</tr>
+<tr>
+<td colspan="6">
+<& /Ticket/Elements/EditCustomFields, QueueObj => $QueueObj &>
+</td>
+</tr>
+% if ($TxnCFs->Count) {
+% while (my $CF = $TxnCFs->Next()) {
+<tr>
+<td align="right"><% $CF->Name %>:</td>
+<td><& /Elements/EditCustomField, CustomField => $CF, NamePrefix =>
+ "Object-RT::Transaction--CustomField-" &><em><% $CF->FriendlyType %></em></td>
+</td></tr>
+% }
+% }
+<tr>
+% if (exists $session{'Attachments'}) {
+<td class="label">
+<&|/l&>Attached file</&>:
+</td>
+<td colspan="5">
+<&|/l&>Check box to delete</&><br />
+% foreach my $attach_name (keys %{$session{'Attachments'}}) {
+<input type="checkbox" class="checkbox" name="DeleteAttach-<%$attach_name%>" value="1" /><%$attach_name%><br />
+% } # end of foreach
+</td>
+</tr>
+<tr>
+% } # end of if
+<td>
+<&|/l&>Attach file</&>:
+</td>
+<td class="value" colspan="5">
+<input type="file" name="Attach" />
+<input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" />
+</td>
+</tr>
+<tr>
+<td colspan="6">
+<&|/l&>Describe the issue below</&>:<br />
+% if (exists $ARGS{Content}) {
+<& /Elements/MessageBox, Default => $ARGS{Content}, IncludeSignature => 0 &>
+% } else {
+<& /Elements/MessageBox, QuoteTransaction => $QuoteTransaction &>
+%}
+
+<br />
+</td>
+</tr>
+<tr>
+<td align="right" colspan="2">
+</td>
+</tr>
+</table>
+</&>
+<& /Elements/Submit, Label => loc("Create")&>
+</div>
+
+<div id="Ticket-Create-details">
+<a name="details"></a>
+<table width="100%" border="0">
+<tr>
+<td width="50%" valign="top">
+
+ <&| /Widgets/TitleBox, title => loc('The Basics'),
+ title_class=> 'inverse',
+ color => "#993333" &>
+<table border="0">
+<tr><td align="right"><&|/l&>Priority</&>:</td><td><input size="3" name="InitialPriority" value="<% $ARGS{InitialPriority} ? $ARGS{InitialPriority} : $QueueObj->InitialPriority %>" /></td></tr>
+<tr><td align="right"><&|/l&>Final Priority</&>:</td><td><input size="3" name="FinalPriority" value="<% $ARGS{FinalPriority} ? $ARGS{FinalPriority} : $QueueObj->FinalPriority %>" /></td></tr>
+<tr><td align="right"><&|/l&>Time Estimated</&>:</td>
+<td>
+<input size="3" name="TimeEstimated" value="<%$ARGS{TimeEstimated}%>" />
+<& /Elements/SelectTimeUnits, Name =>'TimeEstimated' &>
+
+</td></tr>
+<tr><td align="right"><&|/l&>Time Worked</&>:</td>
+<td>
+<input size="3" name="TimeWorked" value="<%$ARGS{TimeWorked}%>" />
+<& /Elements/SelectTimeUnits, Name =>'TimeWorked' &>
+
+</td></tr>
+<tr>
+<td align="right"><&|/l&>Time Left</&>:</td>
+<td><input size="3" name="TimeLeft" value="<%$ARGS{TimeLeft}%>" />
+<& /Elements/SelectTimeUnits, Name =>'TimeLeft' &>
+</td></tr>
+</table>
+</&>
+<br />
+<&|/Widgets/TitleBox, title => loc("Dates"),
+ title_class=> 'inverse',
+ color => "#663366" &>
+
+<table>
+<tr><td class="label"><&|/l&>Starts</&>:</td><td><& /Elements/SelectDate, Name => "Starts", Default => $ARGS{Starts} || '' &></td></tr>
+<tr><td class="label"><&|/l&>Due</&>:</td><td><& /Elements/SelectDate, Name => "Due", Default => $ARGS{Due} || '' &></td></tr>
+</table>
+</&>
+<br />
+</td>
+
+<td valign="top">
+<&| /Widgets/TitleBox, title => loc('Links'), title_class=> 'inverse' &>
+
+<em><&|/l&>(Enter ticket ids or URLs, separated with spaces)</&></em>
+<table border="0">
+<tr><td align="right"><&|/l&>Depends on</&></td><td><input size="10" name="new-DependsOn" value="<% $ARGS{'new-DependsOn'} %>" /></td></tr>
+<tr><td align="right"><&|/l&>Depended on by</&></td><td><input size="10" name="DependsOn-new" value="<% $ARGS{'DependsOn-new'} %>" /></td></tr>
+<tr><td align="right"><&|/l&>Parents</&></td><td><input size="10" name="new-MemberOf" value="<% $ARGS{'new-MemberOf'} %>" /></td></tr>
+<tr><td align="right"><&|/l&>Children</&></td><td><input size="10" name="MemberOf-new" value="<% $ARGS{'MemberOf-new'} %>" /></td></tr>
+<tr><td align="right"><&|/l&>Refers to</&></td><td><input size="10" name="new-RefersTo" value="<% $ARGS{'new-RefersTo'} %>" /></td></tr>
+<tr><td align="right"><&|/l&>Referred to by</&></td><td><input size="10" name="RefersTo-new" value="<% $ARGS{'RefersTo-new'} %>" /></td></tr>
+
+
+</table>
+</&>
+<br />
+
+</td>
+</tr>
+</table>
+<& /Elements/Submit, Label => loc("Create") &>
+</div>
+</form>
+
+<%INIT>
+
+my $CloneTicketObj;
+if ( $CloneTicket ) {
+ $CloneTicketObj = RT::Ticket->new( $session{CurrentUser} );
+ $CloneTicketObj->Load($CloneTicket) or Abort(loc("Ticket could not be loaded"));
+
+ my $clone = {
+ Requestors => join( ',', $CloneTicketObj->RequestorAddresses ),
+ Cc => join( ',', $CloneTicketObj->CcAddresses),
+ AdminCc => join( ',', $CloneTicketObj->AdminCcAddresses),
+ InitialPriority => $CloneTicketObj->Priority,
+ };
+
+ $clone->{$_} = $CloneTicketObj->$_()
+ for qw/Owner Subject FinalPriority TimeEstimated TimeWorked
+ Status TimeLeft Starts Started Due Resolved/;
+
+ my $members = $CloneTicketObj->Members;
+ my ( @members, @members_of, @refers, @refers_by, @depends, @depends_by );
+ while ( my $member = $members->Next ) {
+ push @members, $member->LocalBase;
+ }
+ $clone->{'MemberOf-new'} = join ' ', @members;
+
+ my $members_of = $CloneTicketObj->MemberOf;
+ while ( my $member_of = $members_of->Next ) {
+ push @members_of, $member_of->LocalTarget;
+ }
+ $clone->{'new-MemberOf'} = join ' ', @members_of;
+
+ my $refers = $CloneTicketObj->RefersTo;
+ while ( my $refer = $refers->Next ) {
+ push @refers, $refer->LocalTarget;
+ }
+ $clone->{'new-RefersTo'} = join ' ', @refers;
+
+ my $refers_by = $CloneTicketObj->ReferredToBy;
+ while ( my $refer_by = $refers_by->Next ) {
+ push @refers_by, $refer_by->LocalBase;
+ }
+ $clone->{'RefersTo-new'} = join ' ', @refers_by;
+
+ my $depends = $CloneTicketObj->DependsOn;
+ while ( my $depend = $depends->Next ) {
+ push @depends, $depend->LocalTarget;
+ }
+ $clone->{'new-DependsOn'} = join ' ', @depends;
+
+ my $depends_by = $CloneTicketObj->DependedOnBy;
+ while ( my $depend_by = $depends_by->Next ) {
+ push @depends_by, $depend_by->LocalBase;
+ }
+ $clone->{'DependsOn-new'} = join ' ', @depends_by;
+
+
+
+ my $cfs = $CloneTicketObj->QueueObj->TicketCustomFields();
+ while ( my $cf = $cfs->Next ) {
+ my $cf_id = $cf->id;
+ my $cf_values = $CloneTicketObj->CustomFieldValues( $cf->id );
+ my @cf_values;
+ while ( my $cf_value = $cf_values->Next ) {
+ push @cf_values, $cf_value->Content;
+ }
+ $clone->{"Object-RT::Ticket--CustomField-$cf_id-Value"}
+ = join "\n", @cf_values;
+ }
+
+ for ( keys %$clone ) {
+ $ARGS{$_} = $clone->{$_} if not defined $ARGS{$_};
+ }
+
+}
+
+my @results;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue) || Abort(loc("Queue could not be loaded."));
+my $CFs = $QueueObj->TicketCustomFields();
+my $TxnCFs = $QueueObj->TicketTransactionCustomFields();
+
+my $ValidCFs = $m->comp(
+ '/Elements/ValidateCustomFields',
+ CustomFields => $CFs,
+ ARGSRef => \%ARGS
+);
+
+# if no due date has been set explicitly, then use the
+# queue's default if it exists
+if ($QueueObj->DefaultDueIn && !$ARGS{'Due'}) {
+ my $default_due = RT::Date->new($session{'CurrentUser'});
+ $default_due->SetToNow();
+ $default_due->AddDays($QueueObj->DefaultDueIn);
+ $ARGS{'Due'} = $default_due->ISO();
+}
+
+# {{{ deal with deleting uploaded attachments
+foreach my $key (keys %ARGS) {
+ if ($key =~ m/^DeleteAttach-(.+)$/) {
+ delete $session{'Attachments'}{$1};
+ }
+ $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
+}
+
+# {{{ store the uploaded attachment in session
+if ($ARGS{'Attach'}) { # attachment?
+ $session{'Attachments'} = {} unless defined $session{'Attachments'};
+
+ my $subject = "$ARGS{'Attach'}";
+
+ # strip leading directories
+ $subject =~ s#^.*[\\/]##;
+
+ my $attachment = MakeMIMEEntity(
+ Subject => $subject,
+ Body => "",
+ AttachmentFieldName => 'Attach'
+ );
+
+ $session{'Attachments'} = { %{$session{'Attachments'} || {}},
+ $ARGS{'Attach'} => $attachment };
+}
+# }}}
+
+# delete temporary storage entry to make WebUI clean
+unless (keys %{$session{'Attachments'}} and $ARGS{'id'} eq 'new') {
+ delete $session{'Attachments'};
+}
+
+
+# }}}
+
+if ((!exists $ARGS{'AddMoreAttach'}) and ($ARGS{'id'} eq 'new')) { # new ticket?
+ if ($ValidCFs) {
+ $m->comp('Display.html', %ARGS);
+ $RT::Logger->crit("After display call; error is $@");
+ $m->abort();
+ }
+ else {
+ # Invalid CFs
+ while (my $CF = $CFs->Next) {
+ my $msg = $m->notes('InvalidField-' . $CF->Id) or next;
+ push @results, $CF->Name . ': ' . $msg;
+ }
+ }
+}
+
+my $actions = {
+ A => {
+ html => q[<a href="#basics" onclick="return switchVisibility('Ticket-Create-basics','Ticket-Create-details');">] . loc('Show basics') . q[</a>],
+ },
+ B => {
+ html => q[<a href="#details" onclick="return switchVisibility('Ticket-Create-details','Ticket-Create-basics');">] . loc('Show details') . q[</a>],
+ },
+};
+</%INIT>
+
+<%ARGS>
+$DependsOn => undef
+$DependedOnBy => undef
+$MemberOf => undef
+$QuoteTransaction => undef
+$Queue => undef
+$CloneTicket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Display.html b/rt/html/Ticket/Display.html
new file mode 100644
index 0000000..17d00a0
--- /dev/null
+++ b/rt/html/Ticket/Display.html
@@ -0,0 +1,182 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header,
+ Title => loc("#[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $TicketObj,
+ current_tab => 'Ticket/Display.html?id='.$TicketObj->id,
+ Title => loc("#[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &>
+
+<& /Elements/ListActions, actions => \@Actions &>
+<& /Elements/Callback, _CallbackName => 'BeforeShowSummary', Ticket => $TicketObj, %ARGS &>
+<&| /Widgets/TitleBox, title => loc('Ticket metadata') &>
+<& /Ticket/Elements/ShowSummary, Ticket => $TicketObj, Attachments => $attachments &>
+</&>
+
+<br />
+
+<& /Elements/Callback, _CallbackName => 'BeforeShowHistory', Ticket => $TicketObj, %ARGS &>
+
+<& /Ticket/Elements/ShowHistory ,
+ Ticket => $TicketObj,
+ Tickets => $Tickets,
+ Collapsed => $ARGS{'Collapsed'},
+ ShowHeaders => $ARGS{'ShowHeaders'},
+ Attachments => $attachments,
+ AttachmentContent => $attachment_content
+
+ &>
+
+<& /Elements/Callback, _CallbackName => 'AfterShowHistory', Ticket => $TicketObj,
+current_tab => 'Ticket/Display.html?id=' . $TicketObj->id, %ARGS &>
+
+<%ARGS>
+$id => undef
+$Create => undef
+$ShowHeaders => 0
+$Collapsed => undef
+$TicketObj => undef
+</%ARGS>
+
+<%INIT>
+
+$m->comp('/Elements/Callback', _CallbackName => 'Initial', TicketObj => $TicketObj, ARGSRef => \%ARGS);
+
+my ($linkid, $message, $tid, @Actions, $Tickets);
+
+unless ($id || $TicketObj) {
+ Abort('No ticket specified');
+}
+
+if ($ARGS{'id'} eq 'new') {
+ # {{{ Create a new ticket
+
+ my $Queue = new RT::Queue($session{'CurrentUser'});
+ unless ($Queue->Load($ARGS{'Queue'})) {
+ Abort('Queue not found');
+ }
+
+ unless ($Queue->CurrentUserHasRight('CreateTicket')) {
+ Abort('You have no permission to create tickets in that queue.');
+ }
+ ($TicketObj, @Actions) =
+ CreateTicket(Attachments => $session{'Attachments'}, %ARGS);
+ delete $session{'Attachments'};
+ unless ($TicketObj->CurrentUserHasRight('ShowTicket')) {
+ Abort("No permission to view newly created ticket #".$TicketObj->id.".");
+ }
+ # }}}
+} else {
+ if (!$TicketObj) {
+
+ $TicketObj = RT::Ticket->new($session{'CurrentUser'});
+
+ $TicketObj = LoadTicket($ARGS{'id'});
+ unless ($TicketObj->CurrentUserHasRight('ShowTicket')) {
+ Abort("No permission to view ticket");
+ }
+ }
+
+ $m->comp('/Elements/Callback', _CallbackName => 'BeforeProcessArguments',
+ TicketObj => $TicketObj, Tickets => $Tickets,
+ ActionsRef => \@Actions, ARGSRef => \%ARGS);
+
+ if (defined $ARGS{'Action'}) {
+ if ($ARGS{'Action'} =~ /^(Steal|Kill|Take|SetTold)$/) {
+ my $action = $1;
+ my ($res, $msg)=$TicketObj->$action();
+ push(@Actions, $msg);
+ }
+ }
+
+ $ARGS{'UpdateContent'} =~ s/\r\n/\n/g if defined $ARGS{'UpdateContent'};
+ if ( $ARGS{'UpdateTimeWorked'} || (
+ defined $ARGS{'UpdateContent'}
+ && $ARGS{'UpdateContent'} ne ''
+ && $ARGS{'UpdateContent'} ne "-- \n"
+ . $session{'CurrentUser'}->UserObj->Signature ) )
+ {
+ $ARGS{UpdateAttachments} = $session{'Attachments'};
+ ProcessUpdateMessage(
+ ARGSRef => \%ARGS,
+ Actions => \@Actions,
+ TicketObj => $TicketObj,
+ );
+ delete $session{'Attachments'};
+ }
+ #Process status updates
+ my @PeopleActions = ProcessTicketWatchers(ARGSRef => \%ARGS, TicketObj=>$TicketObj);
+ my @BasicActions = ProcessTicketBasics(ARGSRef => \%ARGS, TicketObj=>$TicketObj);
+ my @results = ProcessTicketLinks( TicketObj => $TicketObj, ARGSRef => \%ARGS);
+
+ push (@Actions, @PeopleActions, @BasicActions, @results);
+}
+
+$m->comp('/Elements/Callback', _CallbackName => 'BeforeDisplay',
+ TicketObj => \$TicketObj,
+ Tickets => \$Tickets,
+ Actions => \@Actions,
+ ARGSRef => \%ARGS,
+);
+
+# This code does automatic redirection if any updates happen.
+
+if (@Actions) {
+ # We've done something, so we need to clear the decks to avoid
+ # resubmission on refresh.
+ # But we need to store Actions somewhere too, so we don't lose them.
+ $session{"Actions"} = \@Actions;
+ RT::Interface::Web::Redirect($RT::WebURL."Ticket/Display.html?id=".$TicketObj->id);
+} else {
+ @Actions = @{ delete $session{"Actions"} || [] };
+}
+
+my $attachments = $m->comp('Elements/FindAttachments', Ticket => $TicketObj, Tickets => $Tickets);
+my $attachment_content = $m->comp('Elements/LoadTextAttachments', Ticket => $TicketObj);
+
+</%INIT>
diff --git a/rt/html/Ticket/Elements/AddCustomers b/rt/html/Ticket/Elements/AddCustomers
new file mode 100644
index 0000000..e04c077
--- /dev/null
+++ b/rt/html/Ticket/Elements/AddCustomers
@@ -0,0 +1,52 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+<BR>
+<%$msg%><br>
+
+% if (@Customers) {
+
+<br><i>(Check box to link)<i>
+<table>
+% foreach my $customer (@Customers) {
+<tr>
+ <td>
+ <input type="checkbox" name="Ticket-AddCustomer-<% $customer->{'custnum'} %>" VALUE="1" <% scalar(@Customers) == 1 ? 'CHECKED' : '' %>>
+ <A HREF="<%$freeside_url%>/view/cust_main.cgi?<% $customer->{'custnum'} %>"><% &RT::URI::freeside::small_custview($customer->{'custnum'}, &RT::URI::freeside::FreesideGetConfig('countrydefault'), 1) |n %>
+ </td>
+</tr>
+% }
+</table>
+
+% }
+
+<%INIT>
+my ($msg);
+
+my $freeside_url = &RT::URI::freeside::FreesideURL();
+
+my @Customers = ();
+if ( $CustomerString ) {
+ @Customers = &RT::URI::freeside::smart_search( 'search' => $CustomerString );
+}
+
+my @Services = ();
+if ($ServiceString) {
+ @Services = (); #service_search();
+}
+
+</%INIT>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/AddWatchers b/rt/html/Ticket/Elements/AddWatchers
new file mode 100644
index 0000000..891ff95
--- /dev/null
+++ b/rt/html/Ticket/Elements/AddWatchers
@@ -0,0 +1,123 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<br />
+<%$msg%><br />
+
+<&|/l&>Add new watchers</&>:<br />
+
+<table>
+% if ($Users and $Users->Count) {
+<tr><td>
+<&|/l&>Type</&>
+</td><td>
+<&|/l&>Username</&>
+</td></tr>
+% while (my $u = $Users->Next ) {
+<tr><td><&/Elements/SelectWatcherType, Name => "Ticket-AddWatcher-Principal-".$u->PrincipalId &></td><td><%$u->Name%> (<%$u->RealName%>)</td></tr>
+% }
+% }
+
+% if ($Groups and $Groups->Count) {
+<tr><td>
+<&|/l&>Type</&>
+</td><td>
+<&|/l&>Group</&>
+</td></tr>
+% while (my $g = $Groups->Next ) {
+<tr><td><&/Elements/SelectWatcherType, Name => "Ticket-AddWatcher-Principal-".$g->PrincipalId, Scope => 'queue' &></td><td><%$g->Name%> (<%$g->Description%>)</td></tr>
+% }
+% }
+
+<tr><td>
+<&|/l&>Type</&>
+</td><td>
+<&|/l&>Email</&>
+</td></tr>
+<tr><td>
+<&/Elements/SelectWatcherType, Name => "WatcherTypeEmail1" &>
+</td><td>
+<input name="WatcherAddressEmail1" size="15" />
+</td></tr>
+<tr><td>
+<&/Elements/SelectWatcherType, Name => "WatcherTypeEmail2" &>
+</td><td>
+<input name="WatcherAddressEmail2" size="15" />
+</td></tr>
+<tr><td>
+<&/Elements/SelectWatcherType, Name => "WatcherTypeEmail3" &>
+</td><td>
+<input name="WatcherAddressEmail3" size="15" />
+</td></tr>
+</table>
+
+<%INIT>
+my ($msg, $Users, $Groups);
+
+if ($UserString) {
+ $Users = RT::Users->new($session{'CurrentUser'});
+ $Users->Limit(FIELD => $UserField, VALUE => $UserString, OPERATOR => $UserOp);
+ $Users->LimitToPrivileged if $PrivilegedOnly;
+ }
+
+if ($GroupString) {
+ $Groups = RT::Groups->new($session{'CurrentUser'});
+ $Groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined');
+ $Groups->Limit(FIELD => $GroupField, VALUE => $GroupString, OPERATOR => $GroupOp);
+ }
+
+</%INIT>
+
+<%ARGS>
+$UserField => 'Name'
+$UserOp => '='
+$UserString => undef
+$GroupField => 'Name'
+$GroupOp => '='
+$GroupString => undef
+$PrivilegedOnly => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/BulkLinks b/rt/html/Ticket/Elements/BulkLinks
new file mode 100644
index 0000000..e449b18
--- /dev/null
+++ b/rt/html/Ticket/Elements/BulkLinks
@@ -0,0 +1,77 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+ <tr>
+ <td class="label"><&|/l&>Merge into</&>:</td>
+ <td class="entry"><input name="Ticket-MergeInto" /> <i><&|/l&>(only one ticket)</&></i></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Depends on</&>:</td>
+ <td class="entry"><input name="Ticket-DependsOn" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Depended on by</&>:</td>
+ <td class="entry"><input name="DependsOn-Ticket" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Parents</&>:</td>
+ <td class="entry"><input name="Ticket-MemberOf" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Children</&>:</td>
+ <td class="entry"> <input name="MemberOf-Ticket" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Refers to</&>:</td>
+ <td class="entry"><input name="Ticket-RefersTo" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Referred to by</&>:</td>
+ <td class="entry"> <input name="RefersTo-Ticket" /></td>
+ </tr>
+</table>
diff --git a/rt/html/Ticket/Elements/EditBasics b/rt/html/Ticket/Elements/EditBasics
new file mode 100644
index 0000000..d68fe65
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditBasics
@@ -0,0 +1,117 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+ <tr>
+ <td class="label"><&|/l&>Subject</&>:</td>
+ <td class="value"><input name="Subject" value="<%$TicketObj->Subject|h%>" size="50" /></td>
+ </tr>
+
+ <tr>
+ <td class="label"><&|/l&>Status</&>:</td>
+ <td class="value"><%$SelectStatus|n%></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Queue</&>:</td>
+ <td class="value"><%$SelectQueue|n%></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Owner</&>:</td>
+ <td class="value"><& /Elements/SelectOwner,
+ Name => 'Owner',
+ QueueObj => $TicketObj->QueueObj,
+ TicketObj => $TicketObj,
+ Default => $TicketObj->OwnerObj->Id,
+ DefaultValue => 0,
+ &></td>
+ </tr>
+
+ <tr>
+ <td class="label"><&|/l&>Time Estimated</&>:</td>
+ <td class="value"><input name="TimeEstimated" value="<%$TicketObj->TimeEstimated|h%>" size="5" />
+ <& /Elements/SelectTimeUnits, Name =>'TimeEstimated' &>
+</td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Time Worked</&>:</td>
+ <td class="value"><input name="TimeWorked" value="<%$TicketObj->TimeWorked|h%>" size="5" />
+ <& /Elements/SelectTimeUnits, Name =>'TimeWorked' &>
+</td>
+
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Time Left</&>:</td>
+ <td class="value"><input name="TimeLeft" value="<%$TicketObj->TimeLeft|h%>" size="5" />
+ <& /Elements/SelectTimeUnits, Name =>'TimeLeft' &>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="label"><&|/l&>Priority</&>:</td>
+ <td class="value"><input name="Priority" value="<%$TicketObj->Priority|h%>" size="5" /></td>
+ </tr>
+
+ <tr>
+ <td class="label"><&|/l&>Final Priority</&>:</td>
+ <td class="value"><input name="FinalPriority" value="<%$TicketObj->FinalPriority|h%>" size="5" /></td>
+ </tr>
+
+
+
+<& /Elements/Callback, _CallbackName => 'EndOfList', TicketObj => $TicketObj, %ARGS &>
+</table>
+
+<%INIT>
+#It's hard to do this inline, so we'll preload the html of the selectstatus in here.
+my $SelectStatus = $m->scomp("/Elements/SelectStatus", Name => 'Status', DefaultLabel => loc("[_1] (Unchanged)",loc($TicketObj->Status)));
+my $SelectQueue = $m->scomp("/Elements/SelectQueue", Name => 'Queue', Default =>$TicketObj->QueueObj->Id);
+
+</%INIT>
+<%ARGS>
+
+$TicketObj => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/EditCustomField b/rt/html/Ticket/Elements/EditCustomField
new file mode 100644
index 0000000..fff3925
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditCustomField
@@ -0,0 +1,57 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+
+# RT 3.2 API compatibility glue
+
+$RT::Logger->debug("Ticket/Elements/EditCustomField is deprecated in RT 3.4 and will be removed in 3.6.");
+
+$ARGS{'NamePrefix'} =~ s/^Ticket-/Object-RT::Ticket-/;
+$ARGS{'NamePrefix'} =~ s/^CustomField-/Object-RT::Ticket--CustomField-/;
+$m->comp('/Elements/EditCustomField', %ARGS, Object=> $ARGS{'TicketObj'});
+</%init>
diff --git a/rt/html/Ticket/Elements/EditCustomFields b/rt/html/Ticket/Elements/EditCustomFields
new file mode 100644
index 0000000..04d7180
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditCustomFields
@@ -0,0 +1,110 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+% my $i = 0;
+% while ( my $CustomField = $CustomFields->Next ) {
+% next unless $CustomField->CurrentUserHasRight('ModifyCustomField');
+% $i++;
+% if ( $i % 2 ) {
+<tr>
+% }
+<td width="50%">
+<table>
+ <tr id="CF-<%$CustomField->id%>-EditRow">
+ <td class="labeltop">
+ <b><%$CustomField->Name%></b><br />
+ <i><%$CustomField->FriendlyType%></i>
+ </td>
+ <td class="entry"><& /Elements/EditCustomField,
+ Object => $TicketObj,
+ CustomField => $CustomField,
+ NamePrefix => $NamePrefix ,
+ Default => $m->notes('Field-' . $CustomField->Id),
+ &>
+% if (my $msg = $m->notes('InvalidField-' . $CustomField->Id)) {
+ <br />
+ <em style="color: red"><% $msg %></em>
+% }
+ </td>
+ </tr>
+</table>
+</td>
+
+% unless ( $i % 2 ) {
+</tr>
+% }
+
+% }
+
+%# close row if required
+% if ( $i % 2 ) {
+</tr>
+% }
+
+</table>
+<%INIT>
+my $CustomFields;
+my $NamePrefix;
+
+if ($TicketObj) {
+ $CustomFields = $TicketObj->CustomFields();
+ $NamePrefix = "Object-RT::Ticket-".$TicketObj->Id."-CustomField-";
+
+} else {
+ $CustomFields = $QueueObj->TicketCustomFields();
+ $NamePrefix = "Object-RT::Ticket--CustomField-";
+}
+
+ $m->comp('/Elements/Callback', _CallbackName => 'MassageCustomFields',
+ CustomFields => $CustomFields);
+
+</%INIT>
+<%ARGS>
+$TicketObj => undef
+$QueueObj => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/EditCustomers b/rt/html/Ticket/Elements/EditCustomers
new file mode 100644
index 0000000..0ba6e44
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditCustomers
@@ -0,0 +1,63 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+<TABLE width=100%>
+ <TR>
+ <TD VALIGN=TOP WIDTH=50%>
+ <h3><&|/l&>Current Customers</&></h3>
+
+<table>
+ <tr>
+ <td><i><&|/l&>(Check box to disassociate)</&></i></td>
+ </tr>
+ <tr>
+ <td class="value">
+% foreach my $link ( @{ $Ticket->Customers->ItemsArrayRef } ) {
+
+ <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
+%# <& ShowLink, URI => $link->TargetURI &><br>
+ <A HREF="<% $link->TargetURI->Resolver->HREF %>"><% $link->TargetURI->Resolver->AsStringLong |n %></A>
+ <BR>
+% }
+ </td>
+ </tr>
+</table>
+
+</TD>
+
+<TD VALIGN=TOP>
+<h3><&|/l&>New Customer Links</&></h3>
+<&|/l&>Find customer</&><BR>
+<input name="CustomerString">
+<input type=submit name="OnlySearchForCustomers" value="<&|/l&>Go!</&>">
+<br><i>cust #, name, company or phone</i>
+<BR>
+%#<BR>
+%#<&|/l&>Find service</&><BR>
+%#<input name="ServiceString">
+%#<input type=submit name="OnlySearchForServices" value="<&|/l&>Go!</&>">
+%#<br><i>username, username@domain, domain, or IP address</i>
+%#<BR>
+
+<& AddCustomers, Ticket => $Ticket,
+ CustomerString => $CustomerString,
+ ServiceString => $ServiceString, &>
+
+</TD>
+</TR>
+</TABLE>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/EditDates b/rt/html/Ticket/Elements/EditDates
new file mode 100644
index 0000000..f694506
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditDates
@@ -0,0 +1,77 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+ <tr>
+ <td class="label"><&|/l&>Starts</&>:</td>
+ <td class="entry"><& /Elements/SelectDate, menu_prefix => 'Starts', current => 0 &>
+ (<% $TicketObj->StartsObj->AsString %>)</td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Started</&>:</td>
+ <td class="entry"><& /Elements/SelectDate, menu_prefix => 'Started', current => 0 &> (<%$TicketObj->StartedObj->AsString %>)</td>
+ </tr>
+
+ <tr>
+ <td class="label">
+ <&|/l&>Last Contact</&>:
+ </td>
+ <td class="entry">
+ <& /Elements/SelectDate, menu_prefix => 'Told', current => 0 &> (<% $TicketObj->ToldObj->AsString %>)
+ </td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Due</&>:</td>
+ <td class="entry">
+ <& /Elements/SelectDate, menu_prefix => 'Due', current => 0 &> (<% $TicketObj->DueObj->AsString %>)
+ </td>
+ </tr>
+</table>
+<%ARGS>
+$TicketObj => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/Elements/EditPeople b/rt/html/Ticket/Elements/EditPeople
new file mode 100644
index 0000000..fd23ae0
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditPeople
@@ -0,0 +1,93 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table width="100%">
+<tr>
+<td valign="top">
+
+<h3><&|/l&>New watchers</&></h3>
+<&|/l&>Find people whose</&><br />
+<& /Elements/SelectUsers &>
+<input type="submit" class="button" name="OnlySearchForPeople" value="<&|/l&>Go!</&>" />
+<br />
+<&|/l&>Find groups whose</&><br />
+<& /Elements/SelectGroups &>
+<input type="submit" class="button" name="OnlySearchForGroup" value="<&|/l&>Go!</&>" />
+
+<& AddWatchers, Ticket => $Ticket, UserString => $UserString,
+ UserOp => $UserOp, UserField => $UserField,
+ GroupString => $GroupString, GroupOp => $GroupOp,
+ GroupField => $GroupField, PrivilegedOnly => $PrivilegedOnly &>
+</td><td valign="top">
+<h3><&|/l&>Owner</&></h3>
+<&|/l&>Owner</&>: <& /Elements/SelectOwner, Name => 'Owner', QueueObj => $Ticket->QueueObj, TicketObj => $Ticket, Default => $Ticket->OwnerObj->Id, DefaultValue => 0&>
+<h3><&|/l&>Current watchers</&></h3>
+<&|/l&>(Check box to delete)</&><br />
+
+<&|/l&>Requestors</&>:
+<& EditWatchers, TicketObj => $Ticket, Watchers => $Ticket->Requestors &>
+
+<&|/l&>Cc</&>:
+<& EditWatchers, TicketObj => $Ticket, Watchers => $Ticket->Cc &>
+
+<&|/l&>Administrative Cc</&>:
+<& EditWatchers, TicketObj => $Ticket, Watchers => $Ticket->AdminCc &>
+
+</td>
+</tr>
+</table>
+
+<%ARGS>
+$UserField => undef
+$UserOp => undef
+$UserString => undef
+$GroupField => undef
+$GroupOp => undef
+$GroupString => undef
+$PrivilegedOnly => undef
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/EditWatchers b/rt/html/Ticket/Elements/EditWatchers
new file mode 100644
index 0000000..918dddb
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditWatchers
@@ -0,0 +1,81 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<ul>
+%# Print out a placeholder if there are none.
+%if ($Members->Count == 0 ) {
+<li><i><&|/l&>none</&></i></li>
+% }
+
+
+%while (my $watcher=$Members->Next) {
+<li>
+<input type="checkbox" class="checkbox" name="Ticket-DeleteWatcher-Type-<%$Watchers->Type%>-Principal-<%$watcher->MemberId%>" value="1" unchecked />
+%if ($watcher->MemberObj->IsUser) {
+<a href="<%$RT::WebPath%>/Admin/Users/Modify.html?id=<%$watcher->MemberObj->Object->id%>">
+<%$watcher->MemberObj->Object->Name%></a>
+% if ($TicketObj and grep { $_->Content eq $watcher->MemberObj->Object->EmailAddress } $TicketObj->SquelchMailTo) {
+<b><&|/l&>(Will not be sent email)</&></b>
+% }
+
+%} else {
+<a href="<%$RT::WebPath%>/Admin/Groups/Modify.html?id=<%$watcher->MemberObj->Object->id%>">
+<%$watcher->MemberObj->Object->Name%></a>
+%}
+</li>
+% }
+</ul>
+<%INIT>
+my $Members = $Watchers->MembersObj;
+</%INIT>
+<%ARGS>
+$TicketObj => undef
+$Watchers => undef
+</%ARGS>
+
+
+
diff --git a/rt/html/Ticket/Elements/FindAttachments b/rt/html/Ticket/Elements/FindAttachments
new file mode 100755
index 0000000..a9d698d
--- /dev/null
+++ b/rt/html/Ticket/Elements/FindAttachments
@@ -0,0 +1,95 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%INIT>
+my %documents;
+
+#A default implementation here loops through all transactions and pulls out all their attachments.
+# We end up doing an end-run around that to get a bit more performance
+
+# We force the cache of ticket transactions to get populated up front. otherwise, the
+# code that looks at attachments will look at each one in turn.
+my $attachments = RT::Attachments->new( $session{'CurrentUser'} );
+
+$attachments->Columns( qw( Id Filename ContentType Headers Subject Parent ContentEncoding ContentType TransactionId Created));
+
+my $transactions = $attachments->NewAlias('Transactions');
+$attachments->Join( ALIAS1 => 'main',
+ FIELD1 => 'TransactionId',
+ ALIAS2 => $transactions,
+ FIELD2 => 'id' );
+
+my $tickets = $attachments->NewAlias('Tickets');
+
+ $attachments->Join( ALIAS1 => $transactions,
+ FIELD1 => 'ObjectId',
+ ALIAS2 => $tickets,
+ FIELD2 => 'id' );
+
+ $attachments->Limit( ALIAS => $transactions,
+ FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket');
+if ($Tickets) {
+ while ($Ticket = $Tickets->Next) {
+ $attachments->Limit( ALIAS => $tickets,
+ FIELD => 'EffectiveId',
+ VALUE => $Ticket->id() );
+ }
+} else {
+ $attachments->Limit( ALIAS => $tickets,
+ FIELD => 'EffectiveId',
+ VALUE => $Ticket->id() );
+}
+
+
+return ($attachments);
+</%INIT>
+<%ARGS>
+$Ticket => undef
+$Tickets => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/Elements/LoadTextAttachments b/rt/html/Ticket/Elements/LoadTextAttachments
new file mode 100755
index 0000000..fc83d66
--- /dev/null
+++ b/rt/html/Ticket/Elements/LoadTextAttachments
@@ -0,0 +1,94 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%INIT>
+
+my $attachments = RT::Attachments->new( $session{'CurrentUser'} );
+
+$attachments->Columns( qw(id Content ContentType TransactionId ContentEncoding));
+
+if ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
+ my $transactions = $attachments->NewAlias('Transactions');
+ $attachments->Join( ALIAS1 => 'main',
+ FIELD1 => 'TransactionId',
+ ALIAS2 => $transactions,
+ FIELD2 => 'id' );
+
+ my $tickets = $attachments->NewAlias('Tickets');
+
+
+ $attachments->Join( ALIAS1 => $transactions,
+ FIELD1 => 'ObjectId',
+ ALIAS2 => $tickets,
+ FIELD2 => 'id' );
+
+ $attachments->Limit( ALIAS => $transactions,
+ FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket');
+
+
+ $attachments->Limit( ALIAS => $tickets,
+ FIELD => 'EffectiveId',
+ VALUE => $Ticket->id() );
+ # if the user may not see comments do not return them
+ unless ( $Ticket->CurrentUserHasRight('ShowTicketComments') ) {
+ $attachments->Limit( ALIAS => $transactions, FIELD => 'Type', OPERATOR => '!=', VALUE => "Comment" );
+ }
+
+ $attachments->Limit ( FIELD => 'ContentType', OPERATOR => '=', VALUE => 'text/plain');
+ $attachments->Limit ( FIELD => 'ContentType', OPERATOR => 'STARTSWITH', VALUE => 'message/');
+ $attachments->Limit ( FIELD => 'ContentType', OPERATOR => '=', VALUE => 'text');
+ if ($RT::SuppressInlineTextFiles) {
+ $attachments->Limit ( FIELD => 'Filename', OPERATOR => 'IS', VALUE => 'NULL');
+ }
+}
+return ($attachments);
+</%INIT>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/Elements/PreviewScrips b/rt/html/Ticket/Elements/PreviewScrips
new file mode 100755
index 0000000..423040c
--- /dev/null
+++ b/rt/html/Ticket/Elements/PreviewScrips
@@ -0,0 +1,133 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$TicketObj => undef
+
+</%args>
+<%init>
+
+my $arg = 'Ticket-'.$TicketObj->Id.'-SquelchMailTo';
+my @squelchto = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} : ($ARGS{$arg});
+
+foreach my $address (@squelchto) {
+ $TicketObj->SquelchMailTo($address) if ($address);
+}
+
+
+$arg = 'Ticket-'.$TicketObj->Id.'-UnsquelchMailTo';
+my @unsquelchto = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} : ($ARGS{$arg});
+
+foreach my $address (@unsquelchto) {
+ $TicketObj->UnsquelchMailTo($address) if ($address);
+}
+
+
+my $action;
+
+if (( $ARGS{'UpdateType'} eq 'response' ) || ($ARGS{'Action'} eq 'Respond' )) {
+ $action = 'Correspond';
+}
+else {
+ $action = 'Comment';
+}
+
+my $Message = MakeMIMEEntity(
+ Subject => $ARGS{'UpdateSubject'},
+ Body => $ARGS{'UpdateContent'},
+);
+
+my ( $Transaction, $Description, $Object ) = $TicketObj->$action(
+ CcMessageTo => $ARGS{'UpdateCc'},
+ BccMessageTo => $ARGS{'UpdateBcc'},
+ MIMEObj => $Message,
+ TimeTaken => $ARGS{'UpdateTimeWorked'},
+ DryRun => 1
+);
+unless ( $Transaction ) {
+ $RT::Logger->error("Coulfn't fire '$action' action: $Description");
+}
+
+
+my @non_recipients = $TicketObj->SquelchMailTo;
+</%init>
+<h2><&|/l&>This message will be sent to...</&></h2>
+
+% if ( $Object ) {
+<i><&|/l&>(Check boxes to disable notifications to the listed recipients)</&></i><br />
+% foreach my $scrip (@{$Object->Scrips->Prepared}) {
+% next unless $scrip->ActionObj->Action->isa('RT::Action::SendEmail');
+<b><% $scrip->Description %></b><br />
+<&|/l, loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name)&>[_1] [_2] with template [_3]</&>
+<br />
+%foreach my $type qw(To Cc Bcc) {
+%my @addresses = $scrip->ActionObj->Action->$type();
+<ul>
+%foreach my $addr (@addresses) {
+<li> <b><%loc($type)%></b>: <input type="checkbox" class="checkbox" name="Ticket-<%$TicketObj->id%>-SquelchMailTo" value="<%$addr->address%>" /> <%$addr->address%>
+% }
+</ul>
+% }
+% if ($RT::PreviewScripMessages) {
+<textarea cols="80" rows="5">
+<%$scrip->ActionObj->TemplateObj->MIMEObj->as_string%>
+</textarea>
+% }
+% }
+% }
+<br />
+
+<h2><&|/l&>Messages about this ticket will not be sent to...</&></h2>
+<i><&|/l&>(Check boxes to enable notifications to the listed recipients)</&></i>
+<br />
+<ul>
+% foreach my $recipient (@non_recipients) {
+<li><input type="checkbox" class="checkbox" name="Ticket-<%$TicketObj->id%>-UnsquelchMailTo" value="<%$recipient->Content%>" />
+<% $recipient->Content %>
+% }
+</ul>
+<& /Elements/Submit, Value => 'UpdatePreview', Label => loc('Save changes')&>
diff --git a/rt/html/Ticket/Elements/Reminders b/rt/html/Ticket/Elements/Reminders
new file mode 100644
index 0000000..63d68c7
--- /dev/null
+++ b/rt/html/Ticket/Elements/Reminders
@@ -0,0 +1,168 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Ticket => undef
+$id => undef
+$ShowCompleted => 0
+$Edit => 0
+</%args>
+<%init>
+
+$Ticket = LoadTicket($id) if ($id);
+
+my $request_args = $m->request_args();
+
+my $reminder_collection = $Ticket->Reminders->Collection;
+
+if ( $request_args->{'update-reminders'} ) {
+ while ( my $reminder = $reminder_collection->Next ) {
+ if ( $reminder->Status ne 'resolved' && $request_args->{ 'Complete-Reminder-' . $reminder->id } ) {
+ $Ticket->Reminders->Resolve($reminder);
+ }
+ elsif ( $reminder->Status eq 'resolved' && !$request_args->{ 'Complete-Reminder-' . $reminder->id } ) {
+ $Ticket->Reminders->Open($reminder);
+ }
+
+ if ( exists( $request_args->{ 'Reminder-Subject-' . $reminder->id } ) && ( $reminder->Subject ne $request_args->{ 'Reminder-Subject-' . $reminder->id } )) {
+ $reminder->SetSubject( $request_args->{ 'Reminder-Subject-' . $reminder->id } ) ;
+ }
+
+ if ( exists( $request_args->{ 'Reminder-Owner-' . $reminder->id } ) && ( $reminder->Owner != $request_args->{ 'Reminder-Owner-' . $reminder->id } )) {
+ $reminder->SetOwner( $request_args->{ 'Reminder-Owner-' . $reminder->id } , "Force" ) ;
+ }
+
+ if ( exists( $request_args->{ 'Reminder-Due-' . $reminder->id } ) && ( $reminder->DueObj->Date ne $request_args->{ 'Reminder-Due-' . $reminder->id } )) {
+ $reminder->SetDue( $request_args->{ 'Reminder-Due-' . $reminder->id } ) ;
+ }
+ }
+}
+
+if ( $request_args->{'NewReminder-Subject'} ) {
+ my $due_obj = RT::Date->new( $session{'CurrentUser'} );
+ my $date = Time::ParseDate::parsedate(
+ $request_args->{'NewReminder-Due'},
+ UK => $RT::DateDayBeforeMonth,
+ PREFER_PAST => 0,
+ PREFER_FUTURE => 1
+ );
+ $due_obj->Set( Value => $date, Format => 'unix' );
+ my ( $add_id, $msg, $txnid ) = $Ticket->Reminders->Add(
+
+ Subject => $request_args->{'NewReminder-Subject'},
+ Owner => $request_args->{'NewReminder-Owner'},
+ Due => $due_obj->ISO
+ );
+}
+
+# We've made changes, let's reload our search
+
+$reminder_collection = $Ticket->Reminders->Collection;
+</%init>
+<input type="hidden" class="hidden" name="id" value="<% $Ticket->id %>" />
+<input type="hidden" class="hidden" name="update-reminders" value="1" />
+<div>
+% while (my $reminder = $reminder_collection->Next) {
+% if ($reminder->Status eq 'resolved' && !$ShowCompleted) {
+<input type="hidden" class="hidden" name="Complete-Reminder-<% $reminder->id %>" value="1" />
+% } elsif ($Edit) {
+<& SELF:EditEntry, Reminder => $reminder, Ticket => $Ticket &>
+% } else {
+<& SELF:ShowEntry, Reminder => $reminder, Ticket => $Ticket &>
+% }
+% }
+</div>
+<div>
+<h3><&|/l&>New reminder:</&></h3>
+<& SELF:NewReminder, Ticket => $Ticket &>
+<%method NewReminder>
+<%args>
+$Ticket
+</%args>
+<div class="input-row">
+<label class="horizontal" for="NewReminder-Subject" ><&|/l&>Subject</&>:</label>
+<input type="text" size="15" name="NewReminder-Subject" />
+</div>
+<div class="input-row">
+<label class="horizontal" for="NewReminder-Owner" ><&|/l&>Owner</&>:</label>
+<& /Elements/SelectOwner, Name => 'NewReminder-Owner', QueueObj => $Ticket->QueueObj, DefaultValue => 0 &>
+</div>
+<div class="input-row">
+<label class="horizontal" for="NewReminder-Due" ><&|/l&>Due</&> <&|/l&>(yyyy/mm/dd)</&>:</label>
+<& /Elements/SelectDate, Name => "NewReminder-Due", Default => "" &>
+</div>
+</div>
+</%method>
+<%method EditEntry>
+<%args>
+$Reminder
+$Ticket
+</%args>
+<input
+ type="checkbox"
+ name="Complete-Reminder-<%$Reminder->id%>"
+ <% $Reminder->Status eq 'resolved' ? 'CHECKED' : '' %>
+/>
+ <input type="text" size="15" name="Reminder-Subject-<% $Reminder->id %>" value="<%$Reminder->Subject%>" /> &bull;
+ <& /Elements/SelectOwner, Name => 'Reminder-Owner-'.$Reminder->id, Queue => $Ticket->QueueObj, Default => $Reminder->Owner, DefaultValue => 0 &>
+ <& /Elements/SelectDate, Name => 'Reminder-Due-'.$Reminder->id, Default => $Reminder->DueObj->Date &>
+ (<%$Reminder->DueObj->Unix>0 ? $Reminder->DueObj->AgeAsString : '' %>)<br />
+</%method>
+<%method ShowEntry>
+<%args>
+$Reminder
+$Ticket
+</%args>
+<input
+ type="checkbox"
+ name="Complete-Reminder-<%$Reminder->id%>"
+ <% $Reminder->Status eq 'resolved' ? 'CHECKED' : '' %>
+/>
+ <%$Reminder->Subject%> &bull;
+ <%$Reminder->OwnerObj->Name%>
+ <%$Reminder->DueObj->Unix>0 ? "&bull; ". $Reminder->DueObj->AgeAsString : '' |n%><br />
+</%method>
diff --git a/rt/html/Ticket/Elements/ShowAttachments b/rt/html/Ticket/Elements/ShowAttachments
new file mode 100644
index 0000000..6f1de62
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowAttachments
@@ -0,0 +1,104 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if (keys %documents) {
+<&| /Widgets/TitleBox, title => loc('Attachments'),
+ title_class=> 'inverse',
+ color => "#336699" &>
+
+% foreach my $key (keys %documents) {
+
+<%$key%><br />
+<ul>
+% foreach my $rev (@{$documents{$key}}) {
+
+<%PERL>
+my $size = $rev->ContentLength;
+
+if ($size) {
+ if ($size > 1024) {
+ $size = int($size/102.4)/10 . "k";
+ }
+ else {
+ $size = $size ."b";
+ }
+
+</%PERL>
+
+<li><font size="-2">
+<a href="<%$RT::WebPath%>/Ticket/Attachment/<%$rev->TransactionId%>/<%$rev->Id%>/<%$rev->Filename | u%>">
+<&|/l, $rev->CreatedAsString, $size, $rev->CreatorObj->Name &>[_1] ([_2]) by [_3]</&>
+</a>
+</font></li>
+% }
+% }
+</ul>
+
+% }
+</&>
+
+<br />
+% }
+
+<%INIT>
+
+# If we haven't been passed in an Attachments object (through the precaching mechanism)
+# then we need to find one
+$Attachments ||= $m->comp('FindAttachments', Ticket => $Ticket);
+
+my %documents;
+while ( my $attach = $Attachments->Next() ) {
+ next unless ($attach->Filename());
+ unshift( @{ $documents{ $attach->Filename } }, $attach );
+}
+
+</%INIT>
+<%ARGS>
+$Ticket => undef
+$Attachments => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/Elements/ShowBasics b/rt/html/Ticket/Elements/ShowBasics
new file mode 100644
index 0000000..631b909
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowBasics
@@ -0,0 +1,85 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+ <tr>
+ <td class="label id"><&|/l&>Id</&>:</td>
+ <td class="value id"><%$Ticket->Id %></td>
+ </tr>
+ <tr>
+ <td class="label status"><&|/l&>Status</&>:</td>
+ <td class="value status"><&|/l&><% $Ticket->Status%></&></td>
+ </tr>
+% if ($Ticket->TimeEstimated) {
+ <tr>
+ <td class="label time estimated"><&|/l&>Estimated</&>:</td>
+ <td class="value time estimated"><& ShowTime, minutes => $Ticket->TimeEstimated &></td>
+ </tr>
+% }
+% if ($Ticket->TimeWorked) {
+ <tr>
+ <td class="label time worked"><&|/l&>Worked</&>:</td>
+ <td class="value time worked"><& ShowTime, minutes => $Ticket->TimeWorked &></td>
+ </tr>
+% }
+ <tr>
+ <td class="label time left"><&|/l&>Left</&>:</td>
+ <td class="value time left"><& ShowTime, minutes => $Ticket->TimeLeft &></td>
+ </tr>
+ <tr>
+ <td class="label priority"><&|/l&>Priority</&>:</td>
+ <td class="value priority"><%$Ticket->Priority%>/<%$Ticket->FinalPriority %></td>
+ </tr>
+ <tr>
+ <td class="label queue"><&|/l&>Queue</&>:</td>
+ <td class="value queue"><& ShowQueue, QueueObj => $Ticket->QueueObj &></td>
+ </tr>
+<& /Elements/Callback, _CallbackName => 'EndOfList', TicketObj => $Ticket, %ARGS &>
+</table>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowCustomFields b/rt/html/Ticket/Elements/ShowCustomFields
new file mode 100644
index 0000000..f307d9d
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowCustomFields
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/ShowCustomFields, Object => $Ticket &>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowCustomers b/rt/html/Ticket/Elements/ShowCustomers
new file mode 100644
index 0000000..3acf92d
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowCustomers
@@ -0,0 +1,38 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+<table>
+% my $cust = 0;
+% foreach my $custResolver ( map { $_->TargetURI->Resolver }
+% @{ $Ticket->Customers->ItemsArrayRef }
+% )
+% {
+% $cust++;
+% my $cust_main = '';
+ <tr>
+ <td class="value">
+ <A HREF="<% $custResolver->HREF %>"><% $custResolver->AsStringLong |n %></A>
+ </td>
+ </tr>
+% }
+% unless ( $cust ) {
+ <tr>
+ <td class="labeltop">
+ <i>(none)<i>
+ </td>
+ </tr>
+
+% }
+</table>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/Elements/ShowDates b/rt/html/Ticket/Elements/ShowDates
new file mode 100644
index 0000000..e00b5ee
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowDates
@@ -0,0 +1,86 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+ <tr>
+ <td class="label date created"><&|/l&>Created</&>:</td>
+ <td class="value date created"><% $Ticket->CreatedObj->AsString %></td>
+ </tr>
+ <tr>
+ <td class="label date starts"><&|/l&>Starts</&>:</td>
+ <td class="value date starts"><% $Ticket->StartsObj->AsString %></td>
+ </tr>
+ <tr>
+ <td class="label date started"><&|/l&>Started</&>:</td>
+ <td class="value date started"><% $Ticket->StartedObj->AsString %></td>
+ </tr>
+ <tr>
+ <td class="label date told"><a href="<% $RT::WebPath %>/Ticket/Display.html?id=<% $Ticket->id %>&Action=SetTold"><&|/l&>Last Contact</&></a>:</td>
+ <td class="value date told"><% $Ticket->ToldObj->AsString %></td>
+ </tr>
+ <tr>
+ <td class="label date due"><&|/l&>Due</&>:</td>
+ <td class="value date due"><% $Ticket->DueObj->AsString %></td>
+ </tr>
+ <tr>
+ <td class="label date resolved"><&|/l&>Closed</&>:</td>
+ <td class="value date resolved"><% $Ticket->ResolvedObj->AsString %></td>
+ </tr>
+ <tr>
+ <td class="label date updated"><&|/l&>Updated</&>:</td>
+% my $UpdatedString = $Ticket->LastUpdated ? loc("[_1] by [_2]", $Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name) : loc("Never");
+% if ($UpdatedLink) {
+ <td class="value date updated"><A HREF="#lasttrans"><% $UpdatedString | h %></a></td>
+% } else {
+ <td class="value date updated"><% $UpdatedString | h %></td>
+% }
+ </tr>
+</table>
+<%ARGS>
+$Ticket => undef
+$UpdatedLink => 1
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowDependencies b/rt/html/Ticket/Elements/ShowDependencies
new file mode 100644
index 0000000..b2f4d29
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowDependencies
@@ -0,0 +1,65 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<&|/l&>Depends on</&>:<br />
+% while (my $Link = $Ticket->DependsOn->Next) {
+% my $member = $Link->TargetObj;
+<a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%>
+[<%$member->Status%>]
+ <br />
+% }
+<&|/l&>Depended on by</&>:<br />
+% while (my $Link = $Ticket->DependedOnBy->Next) {
+% my $member = $Link->TargetObj;
+<a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%>
+[<%$member->Status%>]
+ <br />
+% }
+
+<%ARGS>
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowGroupMembers b/rt/html/Ticket/Elements/ShowGroupMembers
new file mode 100644
index 0000000..e39bc69
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowGroupMembers
@@ -0,0 +1,63 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# Released under the terms of version 2 of the GNU Public License
+
+% my $UserMembers = $Group->UserMembersObj;
+% while (my $member = $UserMembers->Next()) {
+<& ShowUserEntry, User => $member, Ticket => $Ticket &><br />
+% }
+% my $GroupMembers = $Group->MembersObj;
+% $GroupMembers->LimitToGroups();
+% while (my $member = $GroupMembers->Next()) {
+<&|/l&>Group</&>: <%$member->MemberObj->Object->Name%><br />
+% }
+
+<%ARGS>
+$Group => undef
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowHistory b/rt/html/Ticket/Elements/ShowHistory
new file mode 100644
index 0000000..45cd512
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowHistory
@@ -0,0 +1,166 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%doc>
+# This is (ab)used in Admin/(Users|Groups)/History.html and should probably
+# be generalized at some point.
+</%doc>
+<%perl>
+if ($ShowDisplayModes or $ShowTitle) {
+ my $title = $ShowTitle
+ ? loc('History')
+ : '&nbsp;';
+
+ my $titleright;
+
+ if ($ShowDisplayModes) {
+ $titleright = q[<span style="color: black">] . loc('Display mode') . ':</span> ';
+
+ if ($ShowHeaders) {
+ $titleright .= qq{<a href="$URIFile?id=} .
+ $Ticket->id.qq{">} .
+ loc("Brief headers") .
+ qq{</a> &mdash; };
+ $titleright .= q[<span class="selected">] . loc("Full headers") . "</span>";
+ }
+ else {
+ $titleright .= q[<span class="selected">] . loc("Brief headers") . "</span> &mdash; ";
+ $titleright .= qq{<a href="$URIFile?ShowHeaders=1;id=} .
+ $Ticket->id.qq{">} .
+ loc("Full headers") .
+ qq{</a>};
+ }
+ }
+</%perl>
+<& /Widgets/TitleBoxStart, title => $title, titleright => $titleright &>
+% }
+
+<div id="ticket-history">
+<%perl>
+my @attachments = @{$Attachments->ItemsArrayRef()};
+my @attachment_content = @{$AttachmentContent->ItemsArrayRef()};
+
+while ( my $Transaction = $Transactions->Next ) {
+ my $skip = 0;
+ $m->comp( '/Elements/Callback',
+ _CallbackName => 'SkipTransaction',
+ Transaction => $Transaction,
+ skip => \$skip,
+ %ARGS );
+ next if $skip;
+ $i++;
+
+ my @trans_attachments = grep { $_->TransactionId == $Transaction->Id } @attachments;
+
+ my $trans_content = {};
+ grep { ($_->TransactionId == $Transaction->Id ) && ($trans_content->{$_->Id} = $_) } @attachment_content;
+
+
+ #Args is first because we're clobbering the "Attachments" parameter
+ $m->comp( 'ShowTransaction',
+ %ARGS,
+
+ AttachPath => $AttachPath,
+ UpdatePath => $UpdatePath,
+ Ticket => $Ticket,
+ Transaction => $Transaction,
+ ShowHeaders => $ShowHeaders,
+ Collapsed => $Collapsed,
+ RowNum => $i,
+ ShowTitleBarCommands => $ShowTitleBarCommands,
+ Attachments => \@trans_attachments,
+ AttachmentContent => $trans_content,
+ LastTransaction => $Transactions->IsLast
+ );
+
+# manually flush the content buffer after each txn, so the user sees
+# some update
+$m->flush_buffer();
+}
+
+</%perl>
+</div>
+% if ($ShowDisplayModes or $ShowTitle) {
+<& /Widgets/TitleBoxEnd &>
+% }
+<%INIT>
+my $Transactions = new RT::Transactions($session{'CurrentUser'});
+if ($Tickets) {
+ while (my $t = $Tickets->Next) {
+ $Transactions->LimitToTicket($t->id);
+ }
+} else {
+ $Transactions = $Ticket->Transactions;
+}
+
+
+my $OldestFirst = $RT::OldestTransactionsFirst? 'ASC': 'DESC';
+$Transactions->OrderByCols( { FIELD => 'Created',
+ ORDER => $OldestFirst },
+ { FIELD => 'id',
+ ORDER => $OldestFirst },
+ );
+
+my $i;
+$Attachments ||= $m->comp('/Ticket/Elements/FindAttachments', Ticket => $Ticket, Tickets => $Tickets || undef);
+$AttachmentContent ||= $m->comp('/Ticket/Elements/LoadTextAttachments', Ticket => $Ticket);
+
+</%INIT>
+<%ARGS>
+$URIFile => $RT::WebPath."/Ticket/Display.html"
+$Ticket => undef
+$Tickets => undef
+$Attachments => undef
+$AttachmentContent => undef
+$ShowHeaders => undef
+$Collapsed => undef
+$ShowTitle => 1
+$ShowDisplayModes => 1
+$ShowTitleBarCommands => 1
+$AttachPath => $RT::WebPath."/Ticket/Attachment"
+$UpdatePath => $RT::WebPath."/Ticket/Update.html"
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowMembers b/rt/html/Ticket/Elements/ShowMembers
new file mode 100644
index 0000000..f87ce8f
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowMembers
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<ul>
+% while (my $link = $members->Next) {
+<li><& /Elements/ShowLink, URI => $link->BaseURI &><br />
+% if ($depth < 8) {
+<& /Ticket/Elements/ShowMembers, Ticket => $link->BaseObj, depth => ($depth+1) &>
+% }
+</li>
+% }
+</ul>
+
+<%INIT>
+
+my $members = $Ticket->Members;
+return unless $members->Count;
+
+</%INIT>
+
+<%ARGS>
+$Ticket => undef
+$depth => 1
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowMessageHeaders b/rt/html/Ticket/Elements/ShowMessageHeaders
new file mode 100644
index 0000000..81c5637
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowMessageHeaders
@@ -0,0 +1,92 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+% foreach my $header (@headers) {
+% next unless $display_headers{_all}
+% or ($display_headers{ lc $header->{Tag} }
+% and length $header->{Value});
+ <tr>
+ <td align="right" class="message-header-key"><%$header->{'Tag'}%>:</td>
+ <td class="message-header-value"><%$header->{'Value'} | n%></td>
+ </tr>
+% }
+</table>
+<%INIT>
+my $content = $Headers;
+$m->comp('/Elements/Callback', content => \$content, %ARGS);
+
+# apply html escaping on the original content
+# we'll display the value without escaping later (for MakeClicky et al.)
+$content = $m->interp->apply_escapes($content, 'h');
+
+my @lines = split /\n/, $content;
+my $in_header = 0;
+my @headers;
+
+for (@lines) {
+ if (/^(\S+):\s+(.*)$/) {
+ push @headers, { Tag => $1, Value => $2 };
+ }
+ elsif (/^\s+/) {
+ $headers[-1]->{'Value'} .= $_;
+ }
+ else {
+ s/:$//;
+ push @headers, { Tag => $_, Value => '' };
+ }
+}
+
+my %display_headers = map { lc($_) => 1 } @$DisplayHeaders;
+
+$m->comp('/Elements/Callback', _CallbackName => 'Headers', content => \$content, headers => \@headers, display_headers => \%display_headers, %ARGS);
+
+</%INIT>
+<%ARGS>
+$Headers => undef
+$DisplayHeaders => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowMessageStanza b/rt/html/Ticket/Elements/ShowMessageStanza
new file mode 100644
index 0000000..8040096
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowMessageStanza
@@ -0,0 +1,84 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if (ref($Message)) {
+<div class="message-stanza-depth-<% $Depth %>">
+<%perl>
+foreach my $stanza (@$Message) {
+ if ( ref $stanza eq "ARRAY" ) {
+ $m->comp( 'ShowMessageStanza',
+ Depth => $Depth + 1,
+ Transaction => $Transaction,
+ Message => $stanza );
+ }
+ elsif ( ref $stanza eq "HASH" ) {
+ my $content = $stanza->{raw};
+ RT::Interface::Web::EscapeUTF8(\$content);
+ $m->comp('/Elements/Callback', content => \$content, %ARGS);
+ $content =~ s{$}{<br />}mg
+ if defined $content;
+
+</%perl>
+<%$content |n%>
+% }
+% } # end foreach
+</div>
+% } else {
+% my $content = $Message;
+% RT::Interface::Web::EscapeUTF8(\$content);
+% $m->comp('/Elements/Callback', content => \$content, %ARGS);
+% $content =~ s{$}{<br />}mg;
+<%$content |n%>
+% }
+<%INIT>
+use URI::URL;
+</%INIT>
+<%ARGS>
+$Message => undef
+$Depth => 0
+$Transaction => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowPeople b/rt/html/Ticket/Elements/ShowPeople
new file mode 100644
index 0000000..76336ec
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowPeople
@@ -0,0 +1,68 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<table>
+ <tr>
+ <td class="label"><&|/l&>Owner</&>:</td>
+ <td class="value"><& ShowUserEntry, User => $Ticket->OwnerObj, Ticket => $Ticket &></td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Requestors</&>:</td>
+ <td class="value"><& ShowGroupMembers, Group => $Ticket->Requestors, Ticket => $Ticket &></td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Cc</&>:</td>
+ <td class="value"><& ShowGroupMembers, Group => $Ticket->Cc, Ticket => $Ticket &></td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>AdminCc</&>:</td>
+ <td class="value"><& ShowGroupMembers, Group => $Ticket->AdminCc, Ticket => $Ticket &></td>
+ </tr>
+</table>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowQueue b/rt/html/Ticket/Elements/ShowQueue
new file mode 100644
index 0000000..4847602
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowQueue
@@ -0,0 +1,9 @@
+<% $QueueObj->Name %>
+<%ARGS>
+$QueueObj
+</%ARGS>
+<%INIT>
+my $value = $QueueObj->Name;
+$value = '#'. $QueueObj->id
+ unless defined $value && length $value;
+</%INIT>
diff --git a/rt/html/Ticket/Elements/ShowRequestor b/rt/html/Ticket/Elements/ShowRequestor
new file mode 100644
index 0000000..d7fe6b1
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowRequestor
@@ -0,0 +1,89 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%PERL>
+my $rows = 10;
+my $has_right_adminusers = $session{'CurrentUser'}->HasRight(Object => $RT::System, Right => 'AdminUsers');
+my $people = $Ticket->Requestors->UserMembersObj;
+while (my $requestor=$people->Next) {
+next if $requestor->Privileged;
+my $name=$requestor->RealName || $requestor->EmailAddress;
+my $tickets = RT::Tickets->new($session{'CurrentUser'});
+$tickets->FromSQL( "Requestor.id = ". $requestor->id ." AND (Status = 'open' OR Status = 'new')" );
+$tickets->RowsPerPage($rows);
+$tickets->OrderBy(FIELD => 'Priority', ORDER => 'DESC');
+</%PERL>
+
+<&| /Widgets/TitleBox,
+ title_href => $has_right_adminusers ? "$RT::WebPath/Admin/Users/Modify.html?id=".$requestor->id : undef,
+ title=> loc("More about [_1]", $name),
+&>
+
+%# Additional information about this user. Empty by default.
+<& /Elements/Callback, _CallbackName => 'AboutThisUser', requestor => $requestor, %ARGS &>
+
+<&|/l&>Comments about this user</&>:<br />
+<b><% ($requestor->Comments || loc("No comment entered about this user")) %></b><br />
+
+<&|/l, $rows &>This user's [_1] highest priority tickets</&>:<br />
+<ul>
+%while (my $w=$tickets->Next) {
+<li><a href="<%$RT::WebPath%><%$DisplayPath%>?id=<%$w->id%>"><%$w->Id%>: <%$w->Subject%></a> (<%$w->Status%>)
+%}
+</ul>
+
+<&|/l&>Groups this user belongs to</&>:<br />
+
+<& /Elements/ShowMemberships, UserObj => $requestor &>
+
+</&>
+
+%}
+<%ARGS>
+$Ticket=>undef
+$DisplayPath => "/Ticket/Display.html"
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowSummary b/rt/html/Ticket/Elements/ShowSummary
new file mode 100644
index 0000000..e3464c7
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowSummary
@@ -0,0 +1,120 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+ <table width="100%" class="ticket-summary">
+ <tr>
+ <td valign="top" width="50%" class="boxcontainer">
+ <&| /Widgets/TitleBox, title => loc('The Basics'),
+ title_href =>"$RT::WebPath/Ticket/Modify.html?id=".$Ticket->Id,
+ class => 'ticket-info-basics' &>
+ <& /Ticket/Elements/ShowBasics, Ticket => $Ticket &>
+ </&>
+
+% if ($Ticket->QueueObj->TicketCustomFields->First) {
+ <&| /Widgets/TitleBox, title => loc('Custom Fields'),
+ title_href =>"$RT::WebPath/Ticket/Modify.html?id=".$Ticket->Id,
+ class => 'ticket-info-cfs' &>
+ <& /Ticket/Elements/ShowCustomFields, Ticket => $Ticket &>
+ </&>
+% }
+ <&| /Widgets/TitleBox, title => loc('People'),
+ title_href =>"$RT::WebPath/Ticket/ModifyPeople.html?id=".$Ticket->Id,
+ class => 'ticket-info-people' &>
+ <& /Ticket/Elements/ShowPeople, Ticket => $Ticket &>
+ </&>
+
+ <&| /Widgets/TitleBox, title => loc('Customers'),
+ title_href =>"$RT::WebPath/Ticket/ModifyCustomers.html?id=".$Ticket->Id,
+ class=> 'ticket-info-customers' &>
+ <& /Ticket/Elements/ShowCustomers, Ticket => $Ticket &>
+ </&>
+
+ <& /Ticket/Elements/ShowAttachments, Ticket => $Ticket, Attachments => $Attachments &>
+ <br />
+ <& /Ticket/Elements/ShowRequestor, Ticket => $Ticket &>
+
+ <& /Elements/Callback, %ARGS, _CallbackName => 'LeftColumn' &>
+ </td>
+ <td valign="top" width="50%" class="boxcontainer">
+ <&|/Widgets/TitleBox, title => loc("Reminders"),
+ title_href =>"$RT::WebPath/Ticket/Reminders.html?id=".$Ticket->Id,
+ class => 'ticket-info-reminders' &>
+ <table>
+ <tr>
+ <td>
+ <form action="<%$RT::WebPath%>/Ticket/Display.html" method="post">
+ <& /Ticket/Elements/Reminders, Ticket => $Ticket, ShowCompleted => 0 &>
+ <div align="right"><input type="submit" class="button" value="Save" /></div>
+ </form>
+ </td>
+ </tr>
+ </table>
+ </&>
+ <&| /Widgets/TitleBox, title => loc("Dates"),
+ title_href =>"$RT::WebPath/Ticket/ModifyDates.html?id=".$Ticket->Id,
+ class => 'ticket-info-dates' &>
+ <& /Ticket/Elements/ShowDates, Ticket => $Ticket &>
+ </&>
+
+ <&| /Widgets/TitleBox, title => loc('Links'),
+ title_href => "$RT::WebPath/Ticket/ModifyLinks.html?id=".$Ticket->Id,
+ class => 'ticket-info-links' &>
+ <& /Elements/ShowLinks, Ticket => $Ticket &>
+ </&>
+ <& /Elements/Callback, %ARGS, _CallbackName => 'RightColumn' &>
+
+ </td>
+ </tr>
+ </table>
+<%ARGS>
+$Ticket => undef
+$Attachments => undef
+</%ARGS>
+
+
+
+
diff --git a/rt/html/Ticket/Elements/ShowTime b/rt/html/Ticket/Elements/ShowTime
new file mode 100644
index 0000000..92e84f6
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowTime
@@ -0,0 +1,55 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+% if ($minutes < 60) {
+<&|/l, $minutes &>[_1] min</&>
+% } else {
+<&|/l, sprintf("%.1f",$minutes / 60) &>[quant,_1,hour]</&> (<&|/l, $minutes &>[_1] min</&>)
+% }
+<%ARGS>
+$minutes
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowTransaction b/rt/html/Ticket/Elements/ShowTransaction
new file mode 100644
index 0000000..f6d73c2f
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowTransaction
@@ -0,0 +1,194 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<div class="ticket-transaction<% $type_class && " $type_class" %><% $RowNum % 2 ? ' odd' : ' even' %>">
+<table width="100%" cellspacing="0" cellpadding="2" border="0">
+ <tr>
+ <td rowspan="2" valign="top" class="type">
+ <a name="txn-<%$Transaction->Id%>" href="<% $DisplayPath %>#txn-<%$Transaction->Id%>">#</a>
+ <% $LastTransaction ? '<a name="lasttrans">&nbsp;</a>' : '&nbsp;' |n %>
+ </td>
+ <td class="date"><% $transdate|n %></td>
+% my $desc = $Transaction->BriefDescription;
+% $m->comp('/Elements/Callback', _CallbackName => 'ModifyDisplay', text => \$desc, Transaction => $Transaction, %ARGS);
+ <td class="description">
+ <%$Transaction->CreatorObj->Name%> - <%$TicketString%> <%$desc%>
+ </td>
+ <td class="time-taken"><%$TimeTaken%></td>
+ <td class="actions"><%$titlebar_commands|n%></td>
+ </tr>
+
+ <tr>
+ <td colspan="4" class="content">
+% if ($Transaction->CustomFieldValues->Count) {
+ <& /Elements/ShowCustomFields, Object => $Transaction &>
+% }
+% $m->comp('ShowTransactionAttachments', %ARGS, Parent => 0) unless ($Collapsed ||!$ShowBody);
+ </td>
+ </tr>
+</table>
+</div>
+
+<%ARGS>
+$Ticket => undef
+$Transaction => undef
+$ShowHeaders => 0
+$Collapsed => undef
+$ShowTitleBarCommands => 1
+$RowNum => 1
+$DisplayPath => $RT::WebPath."/Ticket/Display.html?id=".$Ticket->id
+$AttachPath => $RT::WebPath."/Ticket/Attachment"
+$UpdatePath => $RT::WebPath."/Ticket/Update.html"
+$EmailRecordPath => $RT::WebPath."/Ticket/ShowEmailRecord.html"
+$Attachments => undef
+$AttachmentContent => undef
+$ShowBody => 1
+$LastTransaction => 0
+</%ARGS>
+
+<%INIT>
+
+my ( $TimeTaken, $TicketString, $type_class );
+
+my $transdate = $Transaction->CreatedAsString();
+$transdate =~ s/\s/&nbsp;/g;
+
+if ( $Transaction->Type =~ /^(Create|Correspond|Comment$)/ ) {
+ if ( $Transaction->IsInbound ) {
+ $type_class = 'message';
+ }
+ else {
+ $type_class = 'message';
+ }
+}
+elsif ( ( $Transaction->Field =~ /^Owner$/ )
+ or ( $Transaction->Type =~ /^(AddWatcher|DelWatcher)$/ ) ) {
+ $type_class = 'people';
+
+}
+elsif ( $Transaction->Type =~ /^(AddLink|DeleteLink)$/ ) {
+ $type_class = 'links';
+}
+elsif ( $Transaction->Type =~ /^(Status|Set|Told)$/ ) {
+ if ( $Transaction->Field =~ /^(Told|Starts|Started|Due)$/ ) {
+ $type_class = 'dates';
+ }
+ else {
+ $type_class = 'basics';
+ }
+}
+else {
+ $type_class = 'other';
+}
+
+if ( $Ticket->Id != $Transaction->Ticket ) {
+ $TicketString = "Ticket " . $Transaction->Ticket . ": ";
+}
+$TicketString ||= '';
+
+if ( $Transaction->TimeTaken != 0 ) {
+ $TimeTaken = $Transaction->TimeTaken . " min";
+} else {
+ $TimeTaken = '';
+}
+
+unless ($Attachments) {
+ my $attachments = $Transaction->Attachments;
+ $attachments->Columns( qw( Id Filename ContentType Headers Subject Parent ContentEncoding ContentType TransactionId) );
+ $Attachments = $attachments->ItemsArrayRef();
+}
+my $titlebar_commands = '&nbsp;';
+
+my @DisplayHeaders=qw ( _all);
+
+if ( $Transaction->Type =~ /EmailRecord$/ ) {
+ @DisplayHeaders = qw(To Cc Bcc);
+
+ $titlebar_commands .=
+ "[<a target=\"_blank\" href=\"$EmailRecordPath?id="
+ . $Transaction->Ticket
+ . "&Transaction="
+ . $Transaction->Id
+ . "&Attachment="
+ . ( $Attachments->[0] && $Attachments->[0]->id )
+ . '">' . loc('Show') . "</a>]&nbsp;";
+ $ShowBody = 0;
+}
+
+
+# If the transaction has anything attached to it at all
+else {
+
+ unless ( $ShowHeaders ) {
+ @DisplayHeaders = qw(To From RT-Send-Cc Cc Bcc Date Subject);
+ }
+
+ if ( $Attachments->[0] && $ShowTitleBarCommands ) {
+ if ( $Transaction->TicketObj->CurrentUserHasRight('ReplyToTicket')
+ or $Transaction->TicketObj->CurrentUserHasRight('ModifyTicket')) {
+ $titlebar_commands .=
+ "[<a href=\"".$UpdatePath."?id="
+ . $Transaction->Ticket
+ . "&QuoteTransaction="
+ . $Transaction->Id
+ . "&Action=Respond\">"
+ . loc('Reply')
+ . "</a>]&nbsp;";
+ }
+ if ( $Transaction->TicketObj->CurrentUserHasRight('CommentOnTicket')
+ or $Transaction->TicketObj->CurrentUserHasRight('ModifyTicket')) {
+ $titlebar_commands .=
+ "[<a href=\"".$UpdatePath."?id="
+ . $Transaction->Ticket
+ . "&QuoteTransaction="
+ . $Transaction->Id
+ . "&Action=Comment\">"
+ . loc('Comment') . "</a>]";
+ }
+ }
+}
+</%INIT>
diff --git a/rt/html/Ticket/Elements/ShowTransactionAttachments b/rt/html/Ticket/Elements/ShowTransactionAttachments
new file mode 100644
index 0000000..85e04e5
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowTransactionAttachments
@@ -0,0 +1,215 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%perl>
+# Find all the attachments which have parent $Parent
+# For each of these attachments
+foreach my $message ( grep { $_->Parent == $Parent } @$Attachments ) {
+
+ # {{{ show the headers
+ my $headers = $message->Headers;
+ chomp $headers;
+
+ # localize the common headers (like 'Subject:'), too.
+ $headers =~ s/^([^:]+)(?=:)/loc($1)/em;
+ $m->comp(
+ 'ShowMessageHeaders',
+ Headers => $headers,
+ Transaction => $Transaction,
+ DisplayHeaders => \@DisplayHeaders
+ );
+
+ # }}}
+ # {{{ if there's any size at all, show the download link
+ my $size = $message->ContentLength;
+ if ($size) {
+
+</%perl>
+<div class="downloadattachment">
+<%perl>
+
+ # show a download link
+ if ( $size > 1024 ) {
+ $size = loc( "[_1]k", int( $size / 102.4 ) / 10 );
+ }
+ else {
+ $size = loc( "[_1]b", $size );
+ }
+
+</%PERL>
+<a href="<%$AttachPath%>/<%$Transaction->Id%>/<%$message->Id%>/<%$message->Filename | u%>"><&|/l&>Download</&> <%$message->Filename || loc('(untitled)') %></a>
+<span class="downloadcontenttype">
+[<%$message->ContentType%> <% $size %>]
+</span>
+</div>
+% }
+% # }}}
+<div class="messagebody">
+<%perl>
+# {{{ if it has a content-disposition: attachment, don't show inline
+unless ( ($message->GetHeader('Content-Disposition')||"") =~ /attachment/i ) {
+
+ my $content;
+
+ # If it's text
+ if ( $message->ContentType =~ m{^(text|message)}i
+ && !($RT::SuppressInlineTextFiles && $message->Filename)
+ && $message->ContentLength <= $RT::MaxInlineBody )
+ {
+
+ if (
+
+ # it's a toplevel object
+ !$ParentObj
+
+ # or its parent isn't a multipart alternative
+ || ( $ParentObj->ContentType !~ m{^multipart/alternative$}i )
+
+ # or it's of our prefered alterative type
+ || (
+ (
+ $RT::PreferRichText
+ && ( $message->ContentType =~ m{^text/(?:html|enriched)$} )
+ )
+ || ( !$RT::PreferRichText
+ && ( $message->ContentType !~ m{^text/(?:html|enriched)$} )
+ )
+ )
+ )
+ {
+
+ if ( $AttachmentContent->{ $message->id } ) {
+ $content = $AttachmentContent->{ $message->id }->Content;
+ }
+ else {
+ $content = $message->Content;
+ }
+
+ # if it's a text/html clean the body and show it
+ if ( $message->ContentType =~ m{^text/(?:html|enriched)$}i ) {
+ $content =
+ $m->comp( '/Elements/ScrubHTML', Content => $content );
+ $m->out($content);
+ }
+
+ # if it's a text/plain show the body
+ elsif ( $message->ContentType =~ m{^(text|message|text)}i ) {
+
+ #don't want to use this even if it is installed, its
+ #segfaulting on weird characters and silently truncating the
+ #ticket history output
+ #see:
+ # r44838@pinglin: jesse | 2006-11-14 15:53:18 -0500
+ # * Move Text::Quoted back to being a run-time require. So that it's possible to turn off the feature if it causes your perl to segfault. (Text::Tabs is...not robust in the face of perl bugs)
+ #eval { require Text::Quoted; $content = Text::Quoted::extract($content); };
+ #if ($@) { 1; }
+
+ $m->comp(
+ 'ShowMessageStanza',
+ Depth => 0,
+ Message => $content,
+ Transaction => $Transaction
+ );
+ }
+ }
+
+ }
+
+ # if it's an image, show it as an image
+ elsif ( $RT::ShowTransactionImages and $message->ContentType =~ /^image\//i ) {
+ $m->out('<img src="'
+ . $AttachPath . '/'
+ . $Transaction->Id . '/'
+ . $message->Id
+ . '/" />' );
+ }
+ elsif ( $message->ContentLength > 0 ) {
+ $m->out(
+ loc( 'Message body not shown because it is too large or is not plain text.' )
+ );
+ }
+}
+
+# }}}
+
+$m->comp(
+ 'ShowTransactionAttachments', %ARGS,
+ Parent => $message->id,
+ ParentObj => $message
+);
+
+</%PERL>
+</div>
+% }
+<%ARGS>
+$Ticket => undef
+$Transaction => undef
+$ShowHeaders => 0
+$Collapsed => undef
+$ShowTitleBarCommands => 1
+$RowNum => 1
+$AttachPath => $RT::WebPath."/Ticket/Attachment"
+$UpdatePath => $RT::WebPath."/Ticket/Update.html"
+$EmailRecordPath => $RT::WebPath."/Ticket/ShowEmailRecord.html"
+$Attachments => undef
+$AttachmentContent => undef
+$ShowBody => 1
+$Parent => 0
+$ParentObj => 0
+</%ARGS>
+<%INIT>
+my @DisplayHeaders=qw( _all);
+
+if ( $Transaction->Type =~ /EmailRecord$/ ) {
+ @DisplayHeaders = qw(To Cc Bcc);
+}
+
+# If the transaction has anything attached to it at all
+elsif (!$ShowHeaders) {
+ @DisplayHeaders = qw(To From RT-Send-Cc Cc Bcc Date Subject);
+}
+</%INIT>
diff --git a/rt/html/Ticket/Elements/ShowUserEntry b/rt/html/Ticket/Elements/ShowUserEntry
new file mode 100644
index 0000000..8481c14
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowUserEntry
@@ -0,0 +1,61 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# Released under the terms of version 2 of the GNU Public License
+
+<%$User->Name%>
+% if ($User->EmailAddress && $User->EmailAddress ne $User->Name) {
+&lt;<%$User->EmailAddress%>&gt;
+% }
+% if ($Ticket and grep { $_->Content eq $User->EmailAddress } $Ticket->SquelchMailTo) {
+<b><&|/l&>(Will not be sent email)</&></b>
+% }
+
+<%ARGS>
+$User => undef
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/Tabs b/rt/html/Ticket/Elements/Tabs
new file mode 100644
index 0000000..3dee8df
--- /dev/null
+++ b/rt/html/Ticket/Elements/Tabs
@@ -0,0 +1,250 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Callback, Ticket => $Ticket, actions=> $actions, tabs => $tabs, %ARGS &>
+<& /Elements/Tabs,
+ tabs => $tabs,
+ actions => $actions,
+ current_tab => $current_tab,
+ current_toptab => $current_toptab,
+ Title => $Title &>
+<%INIT>
+
+my $tabs = {};
+my $current_toptab = "Search/Build.html",
+my $searchtabs = {};
+my $actions;
+
+if ( $Ticket) {
+
+my $id = $Ticket->id();
+
+if ( defined $session{'tickets'} ) {
+
+ # we have to update session data if we get new ItemMap
+ my $updatesession = 1 unless($session{'tickets'}->{'item_map'});
+
+ my $item_map = $session{'tickets'}->ItemMap;
+
+ if ($updatesession) {
+ $session{'i'}++;
+ $session{'tickets'}->PrepForSerialization();
+ }
+
+ # Don't display prev links if we're on the first ticket
+ if ($item_map->{$Ticket->Id}->{prev}) {
+ $searchtabs->{'_a'} = {
+ class => "nav",
+ path => "Ticket/Display.html?id=" . $item_map->{first},
+ title => '<< ' . loc('First') };
+ $searchtabs->{"_b"} = { class => "nav",
+ path => "Ticket/Display.html?id=" . $item_map->{$Ticket->Id}->{prev},
+ title => '< ' . loc('Prev') };
+ }
+
+
+ # Don't display next links if we're on the last ticket
+ if ($item_map->{$Ticket->Id}->{next}) {
+ $searchtabs->{'d'} = { class => "nav",
+ path => "Ticket/Display.html?id=" . $item_map->{$Ticket->Id}->{next},
+ title => loc('Next') . ' >' };
+ $searchtabs->{'e'} = {
+ class => "nav",
+ path => "Ticket/Display.html?id=" . $item_map->{last},
+ title => loc('Last') . ' >>' };
+ }
+}
+
+
+
+$tabs->{"this"} = { class => "currentnav",
+ path => "Ticket/Display.html?id=" . $Ticket->id,
+ title => "#" . $id,
+ current_subtab => $current_subtab };
+
+my $ticket_page_tabs = {
+ _A => { title => loc('Display'),
+ path => "Ticket/Display.html?id=" . $id, },
+
+ _Ab => { title => loc('History'),
+ path => "Ticket/History.html?id=" . $id, },
+ _B => { title => loc('Basics'),
+ path => "Ticket/Modify.html?id=" . $id, },
+
+ _C => { title => loc('Dates'),
+ path => "Ticket/ModifyDates.html?id=" . $id, },
+ _D =>
+ { title => loc('People'), path => "Ticket/ModifyPeople.html?id=" . $id, },
+ _E => { title => loc('Links'),
+ path => "Ticket/ModifyLinks.html?id=" . $id, },
+ _Eb=> { title => loc('Customers'),
+ path => "Ticket/ModifyCustomers.html?id=" . $id, },
+ _F => { title => loc('Reminders'),
+ path => "Ticket/Reminders.html?id=" . $id,
+ separator => 1, },
+ _X => { title => loc('Jumbo'),
+ path => "Ticket/ModifyAll.html?id=" . $id,
+ },
+
+};
+
+foreach my $tab ( sort keys %{$ticket_page_tabs} ) {
+ if ( $ticket_page_tabs->{$tab}->{'path'} eq $current_tab ) {
+ $ticket_page_tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{'this'}->{"current_subtab"} =
+ $ticket_page_tabs->{$tab}->{"path"};
+ }
+}
+$tabs->{'this'}->{"subtabs"} = $ticket_page_tabs;
+$current_tab = "Ticket/Display.html?id=" . $id;
+
+my %can = (
+ ModifyTicket => $Ticket->CurrentUserHasRight('ModifyTicket'),
+);
+
+if ( $can{'ModifyTicket'} or $Ticket->CurrentUserHasRight('ReplyToTicket') ) {
+ $actions->{'F'} = {
+ title => loc('Reply'),
+ path => "Ticket/Update.html?Action=Respond&id=" . $id,
+ };
+}
+
+if ( $can{'ModifyTicket'} ) {
+ if ( $Ticket->Status ne 'resolved' ) {
+ $actions->{'G'} = {
+ path => "Ticket/Update.html?Action=Comment&DefaultStatus=resolved&id=" . $id,
+ title => loc('Resolve') };
+ }
+ if ( $Ticket->Status ne 'open' ) {
+ $actions->{'A'} = { path => "Ticket/Display.html?Status=open&id=" . $id,
+ title => loc('Open it') };
+ }
+}
+
+if ( $Ticket->CurrentUserHasRight('OwnTicket') ) {
+ if ( $Ticket->OwnerObj->Id == $RT::Nobody->id
+ and ( $can{'ModifyTicket'} or $Ticket->CurrentUserHasRight('TakeTicket') ) )
+ {
+ $actions->{'B'} = {
+ path => "Ticket/Display.html?Action=Take&id=" . $id,
+ title => loc('Take'),
+ };
+ }
+ elsif ( $Ticket->OwnerObj->id != $session{CurrentUser}->id
+ and ( $can{'ModifyTicket'} or $Ticket->CurrentUserHasRight('StealTicket') ) )
+ {
+ $actions->{'C'} = {
+ path => "Ticket/Display.html?Action=Steal&id=" . $id,
+ title => loc('Steal'),
+ };
+ }
+}
+
+if ( $can{'ModifyTicket'} or $Ticket->CurrentUserHasRight('CommentOnTicket') ) {
+ $actions->{'E'} = {
+ title => loc('Comment'),
+ path => "Ticket/Update.html?Action=Comment&id=" . $id,
+ };
+}
+}
+
+if ( (defined $actions->{A} || defined $actions->{B} || defined $actions->{C})
+ && (defined $actions->{E} || defined $actions->{F} || defined $actions->{G}) ) {
+
+ if (defined $actions->{C}) { $actions->{C}->{separator} = 1 }
+ elsif (defined $actions->{B}) { $actions->{B}->{separator} = 1 }
+ elsif (defined $actions->{A}) { $actions->{A}->{separator} = 1 }
+}
+
+my $args;
+$args= "?" . $m->comp(
+ '/Elements/QueryString',
+ Query => $ARGS{'Query'} || $session{'CurrentSearchHash'}->{'Query'},
+ Format => $ARGS{'Format'} || $session{'CurrentSearchHash'}->{'Format'},
+ OrderBy => $ARGS{'OrderBy'} || $session{'CurrentSearchHash'}->{'OrderBy'},
+ Order => $ARGS{'Order'} || $session{'CurrentSearchHash'}->{'Order'},
+ Page => $ARGS{'Page'} || $session{'CurrentSearchHash'}->{'Page'},
+ Rows => $ARGS{'Rows'},
+ ) if ($ARGS{'Query'} or $session{'CurrentSearchHash'}->{'Query'});
+$args ||= '';
+
+$tabs->{"f"} = { path => "Search/Build.html?NewQuery=1",
+ title => loc('New Search')};
+$tabs->{"g"} = { path => "Search/Build.html$args",
+ title => loc('Edit Search')};
+$tabs->{"h"} = { path => "Search/Edit.html$args",
+ title => loc('Advanced'),
+ separator => 1 };
+if ($args) {
+ $tabs->{"i"} = { path => "Search/Results.html$args",
+ title => loc('Show Results'),
+ };
+ if ($current_tab =~ "Search/Results.html") {
+ $current_tab = "Search/Results.html$args";
+ }
+ $tabs->{"j"} = { path => "Search/Bulk.html$args",
+ title => loc('Bulk Update'),
+ };
+ if ($current_tab =~ "Search/Bulk.html") {
+ $current_tab = "Search/Bulk.html$args";
+ }
+ foreach my $searchtab (keys %{$searchtabs}) {
+ ($searchtab =~ /^_/) ? $tabs->{"s".$searchtab} = $searchtabs->{$searchtab} : $tabs->{"z_".$searchtab} = $searchtabs->{$searchtab};
+ }
+}
+
+
+</%INIT>
+
+
+<%ARGS>
+$Ticket => undef
+$subtabs => undef
+$current_tab => ''
+$current_subtab => ''
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Ticket/History.html b/rt/html/Ticket/History.html
new file mode 100644
index 0000000..bf533b3
--- /dev/null
+++ b/rt/html/Ticket/History.html
@@ -0,0 +1,89 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Ticket History # [_1] [_2]", $Ticket->Id, $Ticket->Subject) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $Ticket, current_tab => 'Ticket/History.html?id='.$Ticket->id,
+ Title => loc("Ticket History # [_1] [_2]", $Ticket->Id, $Ticket->Subject) &>
+
+<br />
+
+<& /Ticket/Elements/ShowHistory ,
+ Ticket => $Ticket,
+ ShowHeaders => $ARGS{'ShowHeaders'},
+ URIFile => 'History.html',
+ Attachments => $attachments,
+ AttachmentContent => $attachment_content
+ &>
+
+<& /Elements/Callback, _CallbackName => 'AfterShowHistory', Ticket => $Ticket,
+current_tab => 'Ticket/History.html?id=' . $Ticket->id, %ARGS &>
+
+<%ARGS>
+$id => undef
+</%ARGS>
+
+<%INIT>
+
+
+
+my $Ticket = LoadTicket ($id);
+
+unless ($Ticket->CurrentUserHasRight('ShowTicket')) {
+ Abort("No permission to view ticket");
+}
+
+my $attachments = $m->comp('Elements/FindAttachments', Ticket => $Ticket);
+my $attachment_content = $m->comp('Elements/LoadTextAttachments', Ticket =>
+$Ticket);
+
+
+</%INIT>
+
+
+
+
diff --git a/rt/html/Ticket/Modify.html b/rt/html/Ticket/Modify.html
new file mode 100644
index 0000000..488e0ad
--- /dev/null
+++ b/rt/html/Ticket/Modify.html
@@ -0,0 +1,91 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc('Modify ticket #[_1]', $TicketObj->Id) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $TicketObj, current_subtab => "Ticket/Modify.html?id=".$TicketObj->Id,
+ Title => loc('Modify ticket #[_1]', $TicketObj->Id) &>
+
+<& /Elements/ListActions, actions => \@results &>
+<form method="post" action="Modify.html" enctype="multipart/form-data">
+<& /Elements/Callback, _CallbackName => 'FormStart',ARGSRef =>\%ARGS &>
+<input type="hidden" class="hidden" name="id" value="<%$TicketObj->Id%>" />
+<&| /Widgets/TitleBox, title => loc('Modify ticket #[_1]',$TicketObj->Id) &>
+<& Elements/EditBasics, TicketObj => $TicketObj &>
+<& Elements/EditCustomFields, TicketObj => $TicketObj &>
+</&>
+
+<& /Elements/Submit, Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to"), color => "#993333" &>
+</form>
+<%INIT>
+
+my $TicketObj = LoadTicket($id);
+my $CustomFields = $TicketObj->QueueObj->TicketCustomFields();
+
+# Now let callbacks have a chance at editing %ARGS
+$m->comp('/Elements/Callback', TicketObj => $TicketObj, CustomFields => $CustomFields, ARGSRef => \%ARGS);
+
+my @results = ProcessTicketBasics(TicketObj => $TicketObj, ARGSRef => \%ARGS);
+my @cf_results = ProcessObjectCustomFieldUpdates(Object => $TicketObj, ARGSRef => \%ARGS);
+push (@results, @cf_results);
+
+# undef so that TransactionBatch scrips run and update the ticket
+$TicketObj = undef;
+$TicketObj = LoadTicket($id);
+
+# TODO: display the results, even if we can't display the ticket
+
+unless ($TicketObj->CurrentUserHasRight('ShowTicket')) {
+ Abort("No permission to view ticket");
+}
+
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Ticket/ModifyAll.html b/rt/html/Ticket/ModifyAll.html
new file mode 100644
index 0000000..7f7ae30
--- /dev/null
+++ b/rt/html/Ticket/ModifyAll.html
@@ -0,0 +1,221 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Ticket #[_1] Jumbo update: [_2]", $Ticket->Id, $Ticket->Subject) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $Ticket,
+ current_tab => "Ticket/ModifyAll.html?id=".$Ticket->Id,
+ Title => loc("Ticket #[_1] Jumbo update: [_2]", $Ticket->Id, $Ticket->Subject) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="ModifyAll.html" enctype="multipart/form-data">
+<& /Elements/Callback, _CallbackName => 'FormStart',ARGSRef =>\%ARGS &>
+<input type="hidden" class="hidden" name="id" value="<%$Ticket->Id%>" />
+
+<&| /Widgets/TitleBox, title => loc('Modify ticket # [_1]', $Ticket->Id) &>
+<& Elements/EditBasics, TicketObj => $Ticket &>
+<& Elements/EditCustomFields, TicketObj => $Ticket &>
+</&>
+
+<br />
+
+<&| /Widgets/TitleBox, title => loc('Dates') &>
+<& Elements/EditDates, TicketObj => $Ticket &>
+</&>
+
+<br />
+
+
+<&| /Widgets/TitleBox, title => loc('People') &>
+<& Elements/EditPeople, Ticket => $Ticket, UserField => $UserField, UserString => $UserString, UserOp => $UserOp &>
+</&>
+
+<br />
+
+<&| /Widgets/TitleBox, title => loc('Links') &>
+<& /Elements/EditLinks, Object => $Ticket, Merge => 1 &>
+</&>
+
+<br />
+
+<&| /Widgets/TitleBox, title => loc('Update ticket') &>
+<table>
+ <tr>
+ <td class="label"><&|/l&>Update Type</&>:</td>
+ <td class="entry">
+ <select name="UpdateType">
+% if ($CanComment) {
+ <option value="private" ><&|/l&>Comments (Not sent to requestors)</&></option>
+% }
+% if ($CanRespond) {
+ <option value="response"><&|/l&>Reply to requestors</&></option>
+% }
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Subject</&>:</td>
+ <td class="entry"><input name="UpdateSubject" size="60" value="<%$Ticket->Subject%>" /></td>
+ </tr>
+% if (my $TxnCFs = $Ticket->TransactionCustomFields) {
+% while (my $CF = $TxnCFs->Next()) {
+<tr>
+<td class="label"><% $CF->Name %>:</td>
+<td class="entry"><& /Elements/EditCustomField,
+ CustomField => $CF,
+ NamePrefix => "Object-RT::Transaction--CustomField-"
+ &><em><% $CF->FriendlyType %></em></td>
+</td></tr>
+% } # end if while
+% } # end of if
+ <tr>
+ <td class="label"><&|/l&>Attach</&>:</td>
+ <td class="entry"><input name="UpdateAttachment" type="file" /></td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Content</&>:</td>
+ <td class="entry"><& /Elements/MessageBox, Name=>"UpdateContent", QuoteTransaction=>$ARGS{QuoteTransaction} &></td>
+ </tr>
+</table>
+</&>
+
+
+<& /Elements/Submit,
+ Label => loc('Save Changes'),
+ Caption => loc("If you've updated anything above, be sure to"), color => "#333399" &>
+</form>
+
+<%INIT>
+
+
+
+my $Ticket = LoadTicket($id);
+
+my $CanRespond = 0;
+my $CanComment = 0;
+
+
+$CanRespond = 1 if ( $Ticket->CurrentUserHasRight('ReplyToTicket') or
+ $Ticket->CurrentUserHasRight('ModifyTicket') );
+
+$CanComment = 1 if ( $Ticket->CurrentUserHasRight('CommentOnTicket') or
+ $Ticket->CurrentUserHasRight('ModifyTicket') );
+
+
+$m->comp('/Elements/Callback', TicketObj => $Ticket, ARGSRef => \%ARGS);
+my (@wresults, @results, @dresults, @lresults, @cf_results);
+
+unless ($OnlySearchForPeople) {
+ # There might be two owners.
+ if ( ref ($ARGS{'Owner'} )) {
+ my @owners =@{$ARGS{'Owner'}};
+ delete $ARGS{'Owner'};
+ foreach my $owner(@owners){
+ $ARGS{'Owner'} = $owner unless ($Ticket->OwnerObj->id == $owner);
+ }
+
+ }
+
+ @wresults = ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS);
+ @cf_results = ProcessObjectCustomFieldUpdates( Object => $Ticket, ARGSRef => \%ARGS);
+ @dresults = ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS);
+ @lresults = ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS);
+
+ if ($ARGS{'UpdateAttachment'}) {
+ my $subject = "$ARGS{'UpdateAttachment'}";
+ # since CGI.pm deutf8izes the magic field, we need to add it back.
+ Encode::_utf8_on($subject);
+ # strip leading directories
+ $subject =~ s#^.*[\\/]##;
+
+ my $attachment = MakeMIMEEntity(
+ Subject => $subject,
+ Body => "",
+ AttachmentFieldName => 'UpdateAttachment'
+ );
+ delete $ARGS{'UpdateAttachment'};
+ $ARGS{'UpdateAttachments'}->{ $subject } = $attachment;
+ }
+
+ $ARGS{'UpdateContent'} =~ s/\r+\n/\n/g if $ARGS{'UpdateContent'};
+
+ if ($ARGS{'UpdateAttachments'} || ( $ARGS{'UpdateContent'} && $ARGS{'UpdateContent'} ne "-- \n" .
+ $session{'CurrentUser'}->UserObj->Signature)) {
+ ProcessUpdateMessage(TicketObj => $Ticket, ARGSRef=>\%ARGS, Actions=>\@results);
+ }
+ @results = ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS);
+}
+push @results, @wresults;
+push @results, @dresults;
+push @results, @lresults;
+push @results, @cf_results;
+
+# undef so that TransactionBatch scrips run and update the ticket
+$Ticket = undef;
+$Ticket = LoadTicket($id);
+
+# If they've gone and moved the ticket to somewhere they can't see, etc...
+# TODO: display the results, even if we can't display the ticket.
+
+unless ($Ticket->CurrentUserHasRight('ShowTicket')) {
+ Abort("No permission to view ticket");
+}
+
+
+</%INIT>
+
+
+
+<%ARGS>
+$OnlySearchForPeople => undef
+$UserField => undef
+$UserOp => undef
+$UserString => undef
+$id => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/ModifyCustomers.html b/rt/html/Ticket/ModifyCustomers.html
new file mode 100644
index 0000000..72d103b
--- /dev/null
+++ b/rt/html/Ticket/ModifyCustomers.html
@@ -0,0 +1,49 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+<& /Elements/Header, Title => loc("Customers for ticket #[_1]", $Ticket->Id) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $Ticket,
+ current_tab => "Ticket/ModifyCustomers.html?id=".$Ticket->Id,
+ Title => loc("Customers for ticket #[_1]", $Ticket->Id) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="ModifyCustomers.html" method="post">
+<input type="hidden" name="id" value="<%$Ticket->id%>">
+
+<& /Elements/TitleBoxStart, title => loc('Edit Customer Links'), color => "#7f007b"&>
+<& Elements/EditCustomers, Ticket => $Ticket, CustomerString => $CustomerString, ServiceString => $ServiceString &>
+<& /Elements/TitleBoxEnd &>
+<& /Elements/Submit, color => "#7f007b", Label => loc('Save Changes') &>
+</form>
+
+
+<%INIT>
+
+my @results = ();
+my $Ticket = LoadTicket($id);
+
+# if we're trying to search for customers/services and nothing else
+unless ( $OnlySearchForCustomers || $OnlySearchForServices) {
+ @results = ProcessTicketCustomers( TicketObj => $Ticket, ARGSRef => \%ARGS);
+}
+
+</%INIT>
+
+
+<%ARGS>
+$OnlySearchForCustomers => undef
+$OnlySearchForServices => undef
+$CustomerString => undef
+$ServiceString => undef
+$id => undef
+</%ARGS>
diff --git a/rt/html/Ticket/ModifyDates.html b/rt/html/Ticket/ModifyDates.html
new file mode 100644
index 0000000..d126190
--- /dev/null
+++ b/rt/html/Ticket/ModifyDates.html
@@ -0,0 +1,77 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc('Modify dates for #[_1]', $TicketObj->Id) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $TicketObj,
+ current_tab => "Ticket/ModifyDates.html?id=".$TicketObj->Id,
+ Title => loc('Modify dates for #[_1]', $TicketObj->Id) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="ModifyDates.html">
+<& /Elements/Callback, _CallbackName => 'FormStart',ARGSRef =>\%ARGS &>
+<input type="hidden" class="hidden" name="id" value="<%$TicketObj->Id%>" />
+<&| /Widgets/TitleBox,title => loc('Modify dates for ticket # [_1]', $TicketObj->Id) &>
+<& Elements/EditDates, TicketObj => $TicketObj &>
+</&>
+<& /Elements/Submit, Label => loc('Save Changes') &>
+</form>
+
+
+<%INIT>
+
+my $TicketObj = LoadTicket($id);
+$m->comp('/Elements/Callback', TicketObj => $TicketObj, ARGSRef => \%ARGS);
+my @results = ProcessTicketDates( TicketObj => $TicketObj, ARGSRef => \%ARGS);
+
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Ticket/ModifyLinks.html b/rt/html/Ticket/ModifyLinks.html
new file mode 100644
index 0000000..93c80db
--- /dev/null
+++ b/rt/html/Ticket/ModifyLinks.html
@@ -0,0 +1,82 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Link ticket #[_1]", $Ticket->Id) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $Ticket,
+ current_tab => "Ticket/ModifyLinks.html?id=".$Ticket->Id,
+ Title => loc("Link ticket #[_1]", $Ticket->Id) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="ModifyLinks.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<%$Ticket->id%>" />
+<& /Elements/Callback, _CallbackName => 'FormStart',ARGSRef =>\%ARGS &>
+<&| /Widgets/TitleBox, title => loc('Edit Links') &>
+
+<& /Elements/EditLinks, Object => $Ticket, Merge => 1 &>
+</&>
+<& /Elements/Submit, Label => loc('Save Changes') &>
+</form>
+
+
+
+
+<%INIT>
+
+my $Ticket = LoadTicket($id);
+
+my @results;
+$m->comp('/Elements/Callback', TicketObj => $Ticket, ARGSRef => \%ARGS, Results => \@results );
+push @results, ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Ticket/ModifyPeople.html b/rt/html/Ticket/ModifyPeople.html
new file mode 100644
index 0000000..aa38112
--- /dev/null
+++ b/rt/html/Ticket/ModifyPeople.html
@@ -0,0 +1,94 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc('Modify people related to ticket #[_1]', $Ticket->id) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $Ticket,
+ current_tab => "Ticket/ModifyPeople.html?id=".$Ticket->Id,
+ Title => loc('Modify people related to ticket #[_1]', $Ticket->id) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="ModifyPeople.html">
+<input type="hidden" class="hidden" name="id" value="<%$Ticket->Id%>" />
+<& /Elements/Callback, _CallbackName => 'FormStart',ARGSRef =>\%ARGS &>
+<&| /Widgets/TitleBox, title => loc('Modify people related to ticket #[_1]', $Ticket->Id), width => "100%", color=> "#333399" &>
+<& Elements/EditPeople, Ticket => $Ticket, UserField => $UserField, UserString => $UserString, UserOp => $UserOp, GroupString => $GroupString, GroupOp => $GroupOp, GroupField => $GroupField &>
+</&>
+<& /Elements/Submit, Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to"), color => "#333399" &>
+</form>
+
+<%INIT>
+
+my (@results, @wresults);
+
+my $Ticket = LoadTicket($id);
+$m->comp('/Elements/Callback', TicketObj => $Ticket, ARGSRef => \%ARGS);
+
+# if we're trying to search for watchers and nothing else
+unless ($OnlySearchForPeople or $OnlySearchForGroup) {
+ @results = ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS);
+ @wresults = ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS);
+}
+
+push @results, @wresults;
+</%INIT>
+
+
+
+<%ARGS>
+$OnlySearchForPeople => undef
+$OnlySearchForGroup => undef
+$UserField => undef
+$UserOp => undef
+$UserString => undef
+$GroupField => undef
+$GroupOp => undef
+$GroupString => undef
+$id => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/Reminders.html b/rt/html/Ticket/Reminders.html
new file mode 100755
index 0000000..2a3ba4c
--- /dev/null
+++ b/rt/html/Ticket/Reminders.html
@@ -0,0 +1,71 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Reminder ticket #[_1]", $Ticket->Id) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $Ticket,
+ current_tab => "Ticket/Reminders.html?id=".$Ticket->Id,
+ Title => loc("Reminders for ticket #[_1]", $Ticket->Id) &>
+<form action="<%$RT::WebPath%>/Ticket/Reminders.html" method="post">
+<&|/Widgets/TitleBox, title => loc("Reminders"),
+ title_class=> 'inverse',
+ color => "#666699" &>
+
+<& /Ticket/Elements/Reminders, Ticket => $Ticket, ShowCompleted => 1, Edit => 1 &>
+</&>
+<& /Elements/Submit, Label => 'Save'&>
+</form>
+
+
+<%INIT>
+
+my $Ticket = LoadTicket($id);
+
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/rt/html/Ticket/ShowEmailRecord.html b/rt/html/Ticket/ShowEmailRecord.html
new file mode 100644
index 0000000..f77406c
--- /dev/null
+++ b/rt/html/Ticket/ShowEmailRecord.html
@@ -0,0 +1,73 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Attachment => undef
+$Transaction => undef
+</%ARGS>
+<%init>
+ my $AttachmentObj = new RT::Attachment($session{'CurrentUser'});
+ $AttachmentObj->Load($Attachment) || Abort(loc("Attachment '[_1]' could not be loaded", $Attachment));
+
+
+ unless ($AttachmentObj->id) {
+ Abort(loc("Attachment '[_1]' could not be loaded", $Attachment));
+ }
+ unless ($AttachmentObj->TransactionId() == $Transaction ) {
+ Abort(loc("Attachment '[_1]' could not be loaded", $Attachment));
+ }
+
+</%init>
+<& /Elements/Header, ShowBar => 0 &>
+<pre style="padding: 2em;">
+<%$AttachmentObj->Headers%>
+
+<%$AttachmentObj->Content%>
+</pre>
+</body>
+</html>
+%$m->abort;
diff --git a/rt/html/Ticket/Update.html b/rt/html/Ticket/Update.html
new file mode 100644
index 0000000..3824873
--- /dev/null
+++ b/rt/html/Ticket/Update.html
@@ -0,0 +1,228 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $TicketObj,
+ Title=> $title &>
+
+<form action="Update.html" name="TicketUpdate"
+ method="post" enctype="multipart/form-data">
+<& /Elements/Callback, _CallbackName => 'FormStart',ARGSRef =>\%ARGS &>
+<input type="hidden" class="hidden" name="QuoteTransaction" value="<% $ARGS{QuoteTransaction} %>" />
+<input type="hidden" class="hidden" name="DefaultStatus" value="<% $DefaultStatus %>" />
+<input type="hidden" class="hidden" name="Action" value="<% $ARGS{Action} %>" />
+<table width="100%" border="0">
+
+<tr><td align="right"><&|/l&>Status</&>:</td>
+<td>
+<& /Elements/SelectStatus, Name=>"Status", DefaultLabel => loc("[_1] (Unchanged)", loc($TicketObj->Status)), Default => $ARGS{'Status'} || ($TicketObj->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
+<&|/l&>Owner</&>:
+<& /Elements/SelectOwner, Name=>"Owner", DefaultLabel => loc("[_1] (Unchanged)", $TicketObj->OwnerObj->Name()), QueueObj => $TicketObj->QueueObj, TicketObj => $TicketObj, Default => $ARGS{'Owner'} &>
+<&|/l&>Worked</&>: <input size="4" name="UpdateTimeWorked" value="<% $ARGS{UpdateTimeWorked} %>" />
+<& /Elements/SelectTimeUnits, Name => 'UpdateTimeWorked'&>
+</td></tr>
+% my $skip;
+<& /Elements/Callback, _CallbackName => 'BeforeUpdateType', skip => \$skip, %ARGS &>
+% if (!$skip) {
+<input type="hidden" class="hidden" name="id" value="<%$TicketObj->Id%>" /><br />
+% }
+<tr><td align="right"><&|/l&>Update Type</&>:</td>
+<td><select name="UpdateType">
+% if ($CanComment) {
+ <option value="private" <%$ARGS{'UpdateType'} eq "private" ? "SELECTED" : !$ARGS{'UpdateType'}&&$CommentDefault%>><&|/l&>Comments (Not sent to requestors)</&></option>
+% }
+% if ($CanRespond) {
+ <option value="response" <%$ARGS{'UpdateType'} eq "response" ? "SELECTED" : !$ARGS{'UpdateType'}&&$ResponseDefault%>><&|/l&>Reply to requestors</&></option>
+% }
+</select>
+</td></tr>
+<tr><td align="right"><&|/l&>Subject</&>:</td><td> <input name="UpdateSubject" size="60" value="<% $ARGS{UpdateSubject} || $TicketObj->Subject()%>" /></td></tr>
+<tr><td align="right"><&|/l&>Cc</&>:</td><td> <input name="UpdateCc" size="60"
+value="<%$ARGS{UpdateCc}||""%>" /><br />
+<i><font size="-2">
+<&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)</&></font></i>
+</td></tr>
+<tr><td align="right"><&|/l&>Bcc</&>:</td><td> <input name="UpdateBcc" size="60" value="<%$ARGS{UpdateBcc}||""%>" /><br />
+<i><font size="-2">
+<&|/l&>(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)</&></font></i>
+</td></tr>
+% if (exists $session{'Attachments'}) {
+<td>
+<&|/l&>Attached file</&>:
+</td>
+<td colspan="5">
+<&|/l&>Check box to delete</&><br />
+% foreach my $attach_name (keys %{$session{'Attachments'}}) {
+<input type="checkbox" class="checkbox" name="DeleteAttach-<%$attach_name%>" value="1" /><%$attach_name%><br />
+% } # end of foreach
+</td>
+</tr>
+<tr>
+% } # end of if
+
+% if (my $TxnCFs = $TicketObj->TransactionCustomFields) {
+% while (my $CF = $TxnCFs->Next()) {
+<tr>
+<td align="right"><% $CF->Name %>:</td>
+<td><& /Elements/EditCustomField, CustomField => $CF, NamePrefix =>
+ "Object-RT::Transaction--CustomField-" &><em><% $CF->FriendlyType %></em></td>
+</tr>
+% } # end if while
+% } # end of if
+
+<tr><td align="right"><&|/l&>Attach</&>:</td><td><input name="Attach" type="file" /><input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" /><input type="hidden" class="hidden" name="UpdateAttach" value="1" />
+</td></tr>
+<tr><td align="right" valign="top"><&|/l&>Message</&>:</td><td>
+<& /Elements/Callback, _CallbackName => 'BeforeMessageBox', %ARGS &>
+% if (exists $ARGS{UpdateContent}) {
+% # preserve QuoteTransaction so we can use it to set up sane references/in/reply to
+% my $temp = $ARGS{'QuoteTransaction'};
+% delete $ARGS{'QuoteTransaction'};
+<& /Elements/MessageBox, Name=>"UpdateContent", Default=>$ARGS{UpdateContent}, IncludeSignature => 0, %ARGS&>
+% $ARGS{'QuoteTransaction'} = $temp;
+% } else {
+<& /Elements/MessageBox, Name=>"UpdateContent", %ARGS &>
+% }
+</td></tr>
+</table>
+
+
+
+
+<& /Elements/Submit, Label => loc('Update Ticket'), Name => 'SubmitTicket' &>
+% if ($TicketObj->CurrentUserHasRight('ShowOutgoingEmail')) {
+<& /Ticket/Elements/PreviewScrips, TicketObj => $TicketObj, %ARGS &>
+% }
+</form>
+<%INIT>
+my $CanRespond = 0;
+my $CanComment = 0;
+my $title;
+
+my $TicketObj = LoadTicket($id);
+
+unless($DefaultStatus){
+ $DefaultStatus=($ARGS{'Status'} ||$TicketObj->Status());
+}
+
+if ($DefaultStatus =~ '^new$'){
+ $DefaultStatus='open';
+}
+
+if ($DefaultStatus eq 'resolved') {
+ $title = loc("Resolve ticket #[_1] ([_2])", $TicketObj->id, $TicketObj->Subject);
+} else {
+ $title = loc("Update ticket #[_1] ([_2])", $TicketObj->id, $TicketObj->Subject);
+}
+
+# Things needed in the template - we'll do the processing here, just
+# for the convenience:
+
+my ($CommentDefault, $ResponseDefault);
+if ($Action ne 'Respond') {
+ $CommentDefault = "SELECTED";
+} else {
+ $ResponseDefault = "SELECTED";
+}
+
+
+$CanRespond = 1 if ( $TicketObj->CurrentUserHasRight('ReplyToTicket') or
+ $TicketObj->CurrentUserHasRight('ModifyTicket') );
+
+$CanComment = 1 if ( $TicketObj->CurrentUserHasRight('CommentOnTicket') or
+ $TicketObj->CurrentUserHasRight('ModifyTicket') );
+
+
+# {{{ deal with deleting uploaded attachments
+foreach my $key (keys %ARGS) {
+ if ($key =~ m/^DeleteAttach-(.+)$/) {
+ delete $session{'Attachments'}{$1};
+ }
+ $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
+}
+# }}}
+
+# {{{ store the uploaded attachment in session
+if ($ARGS{'Attach'}) { # attachment?
+ $session{'Attachments'} = {} unless defined $session{'Attachments'};
+
+ my $subject = "$ARGS{'Attach'}";
+ # since CGI.pm deutf8izes the magic field, we need to add it back.
+ Encode::_utf8_on($subject);
+ # strip leading directories
+ $subject =~ s#^.*[\\/]##;
+
+ my $attachment = MakeMIMEEntity(
+ Filename => $subject,
+ Body => "",
+ AttachmentFieldName => 'Attach'
+ );
+
+ $session{'Attachments'} = { %{$session{'Attachments'} || {}},
+ $ARGS{'Attach'} => $attachment };
+}
+# }}}
+
+# delete temporary storage entry to make WebUI clean
+unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) {
+ delete $session{'Attachments'};
+}
+# }}}
+
+if ( exists $ARGS{SubmitTicket} ) {
+ $m->comp('Display.html', TicketObj => $TicketObj, %ARGS);
+ return;
+}
+
+</%INIT>
+
+<%ARGS>
+$id => undef
+$Action => undef
+$DefaultStatus => undef
+</%ARGS>
diff --git a/rt/html/Tools/Elements/Tabs b/rt/html/Tools/Elements/Tabs
new file mode 100644
index 0000000..cd6d169
--- /dev/null
+++ b/rt/html/Tools/Elements/Tabs
@@ -0,0 +1,84 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Tabs,
+ tabs => $tabs,
+ current_toptab => 'Tools/index.html',
+ current_tab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+my $tabs = {
+ a => {
+ title => loc('Offline'),
+ path => 'Tools/Offline.html',
+ },
+ b => {
+ title => loc('Reports'),
+ path => 'Tools/Reports/index.html',
+ },
+ c => {
+ title => loc('My Day'),
+ path => 'Tools/MyDay.html',
+ },
+};
+
+$m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+foreach my $tab ( sort keys %{$tabs} ) {
+ if ( $tabs->{$tab}->{'path'} eq $current_tab ) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+}
+</%INIT>
+<%ARGS>
+$subtabs => undef
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Tools/MyDay.html b/rt/html/Tools/MyDay.html
new file mode 100644
index 0000000..dbda0f9
--- /dev/null
+++ b/rt/html/Tools/MyDay.html
@@ -0,0 +1,117 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /Tools/Elements/Tabs, current_tab => "Tools/MyDay.html", Title => $title &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<&|/l, $session{'CurrentUser'}->Name&>(displaying new and open tickets for [_1])</&>
+<form method="post" action="MyDay.html">
+<table width="100%" cellpadding="0" cellspacing="0">
+% while ( my $Ticket = $Tickets->Next()) {
+% my $class;
+% $i++;
+% if ($i % 2 ) {
+% $class = 'class="oddline"';
+% }
+<tr <%$class|n%>><td colspan="2"><h2><a
+href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$Ticket->Id%>"><%$Ticket->Id%>:
+<%$Ticket->Subject%></a></h2></td></tr>
+<tr <%$class|n%>><td><span class="label"><&|/l&>Worked</&>:</span><input size="3" name="UpdateTimeWorked-<%$Ticket->Id%>" /> <&|/l&>minutes</&>
+</td>
+<td rowspan="2"><span class="label"><&|/l&>Comments</&>:<br /></span><textarea name="UpdateContent-<%$Ticket->Id%>" rows="5"
+cols="60"></textarea></td></tr>
+<tr <%$class|n%>>
+<td><span class="label"><&|/l&>Status</&>:</span> <& /Elements/SelectStatus, Name=> 'UpdateStatus-'.$Ticket->Id,
+ DefaultLabel => loc("[_1] (Unchanged)",loc($Ticket->Status())) &></td>
+ </tr>
+
+% }
+</table>
+<& /Elements/Submit, Label => "Record all updates" , Reset => 'Clear'&>
+</form>
+</html>
+<%INIT>
+my $title = loc("What I did today");
+
+my $i = 0;
+my @results;
+foreach my $arg ( keys %ARGS ) {
+ next unless ( $arg =~ /^UpdateStatus-(\d*)$/ );
+ my $id = $1;
+ my $ticket = LoadTicket($id);
+ next unless ( $ticket->id );
+ if ( my $content = $ARGS{'UpdateContent-'.$id} ) {
+ my ( $val, $msg ) = $ticket->Comment(
+ Content => $content,
+ TimeTaken => $ARGS{ 'UpdateTimeWorked-' . $id }
+ );
+ push @results, loc( "Ticket [_1]: [_2]", $id, $msg );
+ } elsif ( my $worked = $ARGS{ 'UpdateTimeWorked-' . $id } ) {
+ my ( $val, $msg ) = $ticket->SetTimeWorked( $worked + $ticket->TimeWorked );
+ push @results, loc( "Ticket [_1]: [_2]", $id, $msg );
+ }
+
+ if ( my $status = $ARGS{ 'UpdateStatus-' . $id } ) {
+ if ( $status ne $ticket->Status ) {
+ my ( $val, $msg ) = $ticket->SetStatus($status);
+ push @results, loc( "Ticket [_1]: [_2]", $id, $msg );
+
+ }
+ }
+
+}
+
+my $Tickets = RT::Tickets->new($session{'CurrentUser'});
+$Tickets->LimitOwner(VALUE => $session{'CurrentUser'}->Id);
+$Tickets->LimitStatus( VALUE => 'open' );
+$Tickets->LimitStatus ( VALUE => 'new');
+$Tickets->OrderBy ( FIELD => 'Priority', ORDER => 'DESC');
+
+
+</%INIT>
diff --git a/rt/html/Tools/Offline.html b/rt/html/Tools/Offline.html
new file mode 100644
index 0000000..385e6ad
--- /dev/null
+++ b/rt/html/Tools/Offline.html
@@ -0,0 +1,166 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Offline upload") &>
+<& Elements/Tabs,
+ current_tab => "Tools/Offline.html",
+ Title => loc("Offline edits") &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<& /Elements/Callback, Requestor => \$requestoraddress,
+ Queue => \$qname, %ARGS &>
+
+<form action="Offline.html" name="TicketUpdate"
+ method="post" enctype="multipart/form-data">
+<table>
+<tr>
+<td class="label">
+<&|/l&>Default Queue</&>:
+</td>
+<td>
+<& /Elements/SelectQueue, Name => "qname", NamedValues => 1 &>
+<em><&|/l&>If no queue is specified, create tickets in this queue.</&></em>
+</td>
+</tr>
+<tr>
+<td class="label">
+<&|/l&>Default Requestor</&>:
+</td>
+<td>
+<input name="requestoraddress" value="<%$requestoraddress%>" />
+<em><&|/l&>If no Requestor is specified, create tickets with this requestor.</&></em>
+</td>
+</tr>
+<tr><td class="labeltop">
+<&|/l&>Template</&>:
+</td>
+<td colspan="2">
+<textarea name="string" cols="80" rows="30"><%$string%></textarea>
+</td>
+</tr>
+<tr><td class="label">
+<&|/l&>Get template from file</&>:
+</td>
+<td>
+<input name="Template" type="file" value="foo" />
+<input type="submit" class="button" name="Parse" value="<&|/l&>Go!</&>" />
+</td>
+</tr>
+</table>
+<& /Elements/Submit, Name => 'UpdateTickets', Label => loc('Upload'), Caption => loc("Upload your changes"), color => "#993333" &>
+
+</form>
+<%args>
+$requestoraddress => ''
+$qname => undef
+$string => undef
+</%args>
+<%INIT>
+
+my @results;
+use RT::Action::CreateTickets;
+my $action = RT::Action::CreateTickets->new(CurrentUser => $session{'CurrentUser'});
+;
+if ($ARGS{'Parse'} && $ARGS{'Template'}) {
+ $string = "";
+ my $cgi_object = $m->cgi_object;
+ my $fh = $cgi_object->upload('Template');
+ my $filename = "$fh";
+
+ my ($buffer, $template);
+ while ( my $bytesread = read( $fh, $buffer, 4096 ) ) {
+ $template .= $buffer;
+ }
+ $template =~ s/\r\n/\n/gs;
+ $action->Parse(Content => $template, Queue => $qname, Requestor => $requestoraddress);
+ foreach ( @{ $action->{'create_tickets'} } ) {
+ my $id = $_;
+ $id =~ s/^create\-//;
+ $string .= "===Create-Ticket: $id\n";
+ $string .= $action->{'templates'}->{$_} . "\n";
+ }
+ foreach ( @{ $action->{'update_tickets'} } ) {
+ my $id = $_;
+ $id =~ s/^update\-//;
+ $string .= "===Update-Ticket: $id\n";
+ $string .= $action->{'templates'}->{$_} . "\n";
+ }
+
+
+} elsif ($ARGS{'UpdateTickets'}) {
+ $action->Parse(Content => $ARGS{string}, Queue => $qname, Requestor=> $requestoraddress);
+ push @results, $action->CreateByTemplate();
+ push @results, $action->UpdateByTemplate();
+} else {
+ if ($ARGS{'Query'}) {
+ my $Tickets = RT::Tickets->new($session{'CurrentUser'});
+ $Tickets->FromSQL($ARGS{'Query'});
+
+ while (my $t = $Tickets->Next) {
+ $string .= "===Update-Ticket: " . $t->Id . "\n";
+ $string .= $action->GetUpdateTemplate($t);
+ $string .= "" . "\n";
+ }
+
+ $string .= "" . "\n";
+ $string .= "===# DO NOT EDIT BELOW THIS LINE#===\n";
+ $string .= "" . "\n";
+
+ while (my $t = $Tickets->Next) {
+ $string .= "===# DO NOT EDIT #===\n";
+ $string .= "===Base-Ticket: " . $t->Id . "\n";
+ $string .= $action->GetBaseTemplate($t);
+ $string .= "===# DO NOT EDIT #===\n";
+ $string .= "" . "\n";
+ }
+ } else {
+ $string .= "===Create-Ticket: ticket1\n";
+ $string .= $action->GetCreateTemplate();
+ }
+}
+</%INIT>
diff --git a/rt/html/Tools/Reports/CreatedByDates.html b/rt/html/Tools/Reports/CreatedByDates.html
new file mode 100644
index 0000000..b6d6f94
--- /dev/null
+++ b/rt/html/Tools/Reports/CreatedByDates.html
@@ -0,0 +1,94 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Queue => undef
+$CreatedBefore => undef
+$CreatedAfter => undef
+</%args>
+<%init>
+my $title = loc("Created tickets in period, grouped by status");
+my $q = RT::Queue->new($session{'CurrentUser'});
+my $before = RT::Date->new($session{'CurrentUser'});
+my $after = RT::Date->new($session{'CurrentUser'});
+my $query = 'Status != "deleted" ';
+
+
+if ($CreatedAfter) {
+ $after->Set(Format => 'unknown', Value => $CreatedAfter);
+ $CreatedAfter = $after->AsString;
+}
+if ($CreatedBefore) {
+ $before->Set(Format => 'unknown', Value => $CreatedBefore);
+ $CreatedBefore = $before->AsString;
+}
+
+
+$q->LoadByCols(Name => $Queue);
+</%init>
+<& /Elements/Header, Title => $title &>
+<& /Tools/Reports/Elements/Tabs, current_tab => 'Tools/Reports/CreatedByDates.html', Title => $title &>
+<form method="post" action="CreatedByDates.html">
+% if ($Queue|| $CreatedBefore ||$CreatedAfter) {
+% # if we have a queue, do the search
+% if ($Queue) { $query .= " AND Queue = '$Queue'"}
+% if ($CreatedBefore) { $query .= " AND Created < '".$before->ISO."'"; }
+% if ($CreatedAfter) { $query .= " AND Created > '".$after->ISO."'"}
+% my $groupby = 'Status';
+<& /Search/Elements/Chart, Query => $query, PrimaryGroupBy => $groupby &>
+% }
+
+<hr>
+
+<br /><&|/l&>Queue</&>: <& /Elements/SelectQueue, Name => 'Queue', NamedValues => 1, Default => $q->id &>
+<br /><&|/l&>Tickets created after</&>:
+<input size="20" name="CreatedAfter" value="<%$CreatedAfter%>" />
+<br /><&|/l&>Tickets created before</&>:
+<input size="20" name="CreatedBefore" value="<%$CreatedBefore%>" />
+
+<& /Elements/Submit&>
+</form>
diff --git a/rt/html/Tools/Reports/Elements/Tabs b/rt/html/Tools/Reports/Elements/Tabs
new file mode 100644
index 0000000..18829f0
--- /dev/null
+++ b/rt/html/Tools/Reports/Elements/Tabs
@@ -0,0 +1,89 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Tools/Elements/Tabs,
+ subtabs => $tabs,
+ current_tab => 'Tools/Reports/index.html',
+ current_subtab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+my $tabs = {
+ a => {
+ title => loc('Resolved by owner'),
+ path => 'Tools/Reports/ResolvedByOwner.html',
+ },
+ b => {
+ title => loc('Resolved in date range'),
+ path => 'Tools/Reports/ResolvedByDates.html',
+ },
+ c => {
+ title => loc('Created in a date range'),
+ path => 'Tools/Reports/CreatedByDates.html',
+ },
+};
+
+
+
+
+$m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+foreach my $tab ( sort keys %{$tabs} ) {
+ if ( $tabs->{$tab}->{'path'} eq $current_tab ) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+}
+</%INIT>
+
+
+<%ARGS>
+$subtabs => undef
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Tools/Reports/ResolvedByDates.html b/rt/html/Tools/Reports/ResolvedByDates.html
new file mode 100644
index 0000000..265a1ca
--- /dev/null
+++ b/rt/html/Tools/Reports/ResolvedByDates.html
@@ -0,0 +1,95 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Queue => undef
+$ResolvedBefore => undef
+$ResolvedAfter => undef
+</%args>
+<%init>
+my $title = loc("Resolved tickets in period, grouped by owner");
+my $q = RT::Queue->new($session{'CurrentUser'});
+my $before = RT::Date->new($session{'CurrentUser'});
+my $after = RT::Date->new($session{'CurrentUser'});
+my $query = '';
+
+
+if ($ResolvedAfter) {
+ $after->Set(Format => 'unknown', Value => $ResolvedAfter);
+ $ResolvedAfter = $after->AsString;
+}
+if ($ResolvedBefore) {
+ $before->Set(Format => 'unknown', Value => $ResolvedBefore);
+ $ResolvedBefore = $before->AsString;
+}
+
+
+$q->LoadByCols(Name => $Queue);
+</%init>
+<& /Elements/Header, Title => $title &>
+<& /Tools/Reports/Elements/Tabs, current_tab => 'Tools/Reports/ResolvedByDates.html', Title => $title &>
+<form method="post" action="ResolvedByDates.html">
+% if ($Queue|| $ResolvedBefore ||$ResolvedAfter) {
+% # if we have a queue, do the search
+% $query = "Status = 'resolved'";
+% if ($Queue) { $query .= " AND Queue = '$Queue'"}
+% if ($ResolvedBefore) { $query .= " AND Resolved < '".$before->ISO."'"; }
+% if ($ResolvedAfter) { $query .= " AND Resolved > '".$after->ISO."'"}
+% my $groupby = 'Owner';
+<& /Search/Elements/Chart, Query => $query, PrimaryGroupBy => $groupby &>
+% }
+
+<hr>
+
+<br /><&|/l&>Queue</&>: <& /Elements/SelectQueue, Name => 'Queue', NamedValues => 1, Default => $q->id &>
+<br /><&|/l&>Tickets resolved after</&>:
+<input size="20" name="ResolvedAfter" value="<%$ResolvedAfter%>" />
+<br /><&|/l&>Tickets resolved before</&>:
+<input size="20" name="ResolvedBefore" value="<%$ResolvedBefore%>" />
+
+<& /Elements/Submit&>
+</form>
diff --git a/rt/html/Tools/Reports/ResolvedByOwner.html b/rt/html/Tools/Reports/ResolvedByOwner.html
new file mode 100644
index 0000000..142af64
--- /dev/null
+++ b/rt/html/Tools/Reports/ResolvedByOwner.html
@@ -0,0 +1,70 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%args>
+$Queue => undef
+</%args>
+<%init>
+my $title = loc("Resolved tickets, grouped by owner");
+my $q = RT::Queue->new($session{'CurrentUser'});
+$q->LoadByCols(Name => $Queue);
+</%init>
+<& /Elements/Header, Title => $title &>
+<& /Tools/Reports/Elements/Tabs, current_tab => '/Tools/Reports/ResolvedByOwner.html', Title => $title &>
+<form method="post" action="ResolvedByOwner.html">
+% if ($Queue) {
+% # if we have a queue, do the search
+% my $query = "Status = 'resolved' AND Queue = '$Queue'";
+% my $groupby = 'Owner';
+<& /Search/Elements/Chart, Query => $query, PrimaryGroupBy => $groupby &>
+% }
+
+<hr>
+
+<&|/l&>Queue</&>: <& /Elements/SelectQueue, Name => 'Queue', NamedValues => 1, Default => $q->id &>
+<& /Elements/Submit&>
+</form>
diff --git a/rt/html/Tools/Reports/index.html b/rt/html/Tools/Reports/index.html
new file mode 100644
index 0000000..149dc76
--- /dev/null
+++ b/rt/html/Tools/Reports/index.html
@@ -0,0 +1,50 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc('Reports') &>
+<& /Tools/Reports/Elements/Tabs, Title => loc('Reports') &>
+<& /Elements/Callback &>
diff --git a/rt/html/Tools/index.html b/rt/html/Tools/index.html
new file mode 100644
index 0000000..8d2efdb
--- /dev/null
+++ b/rt/html/Tools/index.html
@@ -0,0 +1,52 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Tools") &>
+<& Elements/Tabs,
+ current_tab => "Tools/index.html",
+ Title => loc("Tools") &>
+
diff --git a/rt/html/User/Delegation.html b/rt/html/User/Delegation.html
new file mode 100644
index 0000000..2587f8c
--- /dev/null
+++ b/rt/html/User/Delegation.html
@@ -0,0 +1,107 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => loc("Delegate rights") &>
+<& /User/Elements/Tabs,
+ current_tab => 'User/Delegation.html',
+ Title => loc("Delegate rights") &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post">
+<& Elements/DelegateRights, personalgroups => $personalgroups, objects => $objects, ObjectType => 'RT::System' &>
+<& Elements/DelegateRights, personalgroups => $personalgroups, objects => $objects, ObjectType => 'RT::Queue' &>
+<& Elements/DelegateRights, personalgroups => $personalgroups, objects => $objects, ObjectType => 'RT::Group' &>
+
+<& /Elements/Submit, Label => loc('Modify Rights') &>
+</form>
+<%INIT>
+
+my (@results, $arg);
+foreach $arg (keys %ARGS) {
+ next unless ($arg =~ /^Delegate-Existing-ACE-(\d+)-to-(\d+)-as-(\d+)$/);
+ my $parent = $1;
+ my $principal = $2;
+ my $delegation = $3;
+ unless ($ARGS{"Delegate-ACE-$1-to-$2"}) {
+ my $ace_to_del = RT::ACE->new($session{'CurrentUser'});
+ $ace_to_del->Load($delegation);
+ my ($delval, $delmsg) = $ace_to_del->Delete();
+ push (@results, $delmsg);
+ }
+}
+
+foreach $arg (keys %ARGS) {
+ next unless ($arg =~ /^Delegate-ACE-(\d+)-to-(\d+)$/);
+ my $parent = $1;
+ my $principal = $2;
+ # if we already delegate it, we just don't care
+ next if (grep /^Delegate-Existing-ACE-$parent-to-$principal-/, keys %ARGS);
+ my $ace = RT::ACE->new($session{'CurrentUser'});
+ $ace->Load($1);
+ unless ($ace->Id) {
+ push (@results, loc('Right not found'));
+ next;
+ }
+ my ($delid, $delmsg) = $ace->Delegate(PrincipalId => $principal);
+ push (@results, $delmsg);
+}
+
+my $personalgroups = RT::Groups->new($session{'CurrentUser'});
+$personalgroups->LimitToPersonalGroupsFor($session{'CurrentUser'}->PrincipalId);
+
+my $objects;
+my $acl = RT::ACL->new ($session{'CurrentUser'});
+$acl->ExcludeDelegatedRights();
+$acl->LimitToPrincipal(Id => $session{'CurrentUser'}->PrincipalId,
+ IncludeGroupMembership => 1
+ );
+
+while(my $right = $acl->Next) {
+ push @{$objects->{$right->ObjectType}{$right->ObjectId}},$right;
+}
+</%INIT>
diff --git a/rt/html/User/Elements/DelegateRights b/rt/html/User/Elements/DelegateRights
new file mode 100644
index 0000000..3d42ed5
--- /dev/null
+++ b/rt/html/User/Elements/DelegateRights
@@ -0,0 +1,109 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<h2><%$sectionheading%></h2>
+<%perl>
+
+foreach my $object (keys %{$objects->{$ObjectType}}) {
+unless ($ObjectType eq 'RT::System') {
+my $object_obj = @{$objects->{$ObjectType}{$object}}[0]->Object;
+
+</%perl>
+<h3><% $object_obj->Name %></h3>
+% }
+<table width="100%" border="0" cellspacing="0" cellpadding="3">
+<tr>
+ <th width="15%"><&|/l&>Personal groups:</&></th>
+% while (my $pg = $personalgroups->Next) {
+<th><%$pg->Name%></th>
+% }
+</tr>
+<%perl>
+my $i;
+foreach my $right (@{$objects->{$ObjectType}{$object}}) {
+my $delegations = RT::ACL->new($session{'CurrentUser'});
+$delegations->DelegatedBy( Id => $session{'CurrentUser'}->PrincipalId);
+$delegations->DelegatedFrom ( Id => $right->Id);
+
+my $del_hash = {};
+while ( my $delegation = $delegations->Next) {
+ $del_hash->{$delegation->PrincipalId} = $delegation;
+}
+</%perl>
+% $i++;
+%
+<tr class="<%($i%2) && 'oddline'%>">
+<td>
+<% loc($right->RightName) %><br />
+<div align="right"><font size="-2" color="#999999"><&|/l, $right->PrincipalObj->Object->SelfDescription &>as granted to [_1]</&></font></div>
+ </td>
+% while (my $pg = $personalgroups->Next) {
+<td align="center">
+ <input name="Delegate-ACE-<% $right->Id %>-to-<% $pg->PrincipalId%>" type="checkbox" value="1" <%$ del_hash->{$pg->PrincipalId} && 'CHECKED' %> />
+% if ( $del_hash->{$pg->PrincipalId}) {
+<input type="hidden" class="hidden" name="Delegate-Existing-ACE-<% $right->Id %>-to-<% $pg->PrincipalId%>-as-<%$del_hash->{$pg->PrincipalId}->Id%>" />
+% }
+</td>
+% }
+<td>&nbsp;</td>
+</tr>
+%}
+</table>
+% }
+<%init>
+
+my $sectionheading = loc("[_1] rights", loc($ObjectType =~ /^RT::(.*)$/));
+# 'System' # loc
+# 'Group' # loc
+# 'Queue' # loc
+
+</%init>
+<%args>
+$ObjectType => undef
+$objects => undef
+$personalgroups => undef
+</%args>
diff --git a/rt/html/User/Elements/GroupTabs b/rt/html/User/Elements/GroupTabs
new file mode 100644
index 0000000..b0eba0f
--- /dev/null
+++ b/rt/html/User/Elements/GroupTabs
@@ -0,0 +1,84 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /User/Elements/Tabs,
+ subtabs => $tabs,
+ current_tab => 'User/Groups/',
+ current_subtab => $current_subtab,
+ Title => $Title &>
+
+<%INIT>
+my $tabs;
+if ( $GroupObj and $GroupObj->id ) {
+ $tabs->{"this"} = {
+ title => $GroupObj->Name,
+ path => "User/Groups/Modify.html?id=" . $GroupObj->id,
+ subtabs => {
+ Basics => { title => loc('Basics'),
+ path => "User/Groups/Modify.html?id=" . $GroupObj->id
+ },
+
+ Members => { title => loc('Members'),
+ path => "User/Groups/Members.html?id=" . $GroupObj->id
+ },
+
+ } };
+ $tabs->{'this'}->{'current_subtab'} = $current_subtab;
+ $current_subtab = "User/Groups/Modify.html?id=" . $GroupObj->id,
+}
+$tabs->{"A"} = { title => loc('Select group'),
+ path => "User/Groups/index.html" };
+$tabs->{"B"} = { title => loc('New group'),
+ path => "User/Groups/Modify.html?Create=1",
+ separator => 1 };
+
+</%INIT>
+<%ARGS>
+$GroupObj => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/User/Elements/Tabs b/rt/html/User/Elements/Tabs
new file mode 100644
index 0000000..0aca8b5
--- /dev/null
+++ b/rt/html/User/Elements/Tabs
@@ -0,0 +1,89 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Tabs,
+ tabs => $tabs,
+ current_toptab => 'User/Prefs.html',
+ current_tab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+ my $tabs = { a => { title => loc('About me'),
+ path => 'User/Prefs.html',
+ },
+ g => { title => loc('Personal Groups'),
+ path => 'User/Groups/',
+ },
+ h => { title => loc('Delegation'),
+ path => 'User/Delegation.html',
+ },
+ f => { title => loc('Search options'),
+ path => 'Prefs/SearchOptions.html',
+ },
+ r => { title => loc('RT at a glance'),
+ path => 'Prefs/MyRT.html',
+ },
+ };
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+ foreach my $tab (sort keys %{$tabs}) {
+ if ($tabs->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+ }
+</%INIT>
+
+
+<%ARGS>
+$subtabs => undef
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/User/Groups/Members.html b/rt/html/User/Groups/Members.html
new file mode 100644
index 0000000..0634260
--- /dev/null
+++ b/rt/html/User/Groups/Members.html
@@ -0,0 +1,160 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /User/Elements/GroupTabs,
+ GroupObj => $Group,
+ current_subtab => "User/Groups/Members.html?id=".$Group->id,
+ Title => $title &>
+<& /Elements/ListActions, actions => \@results &>
+
+
+
+<form action="<%$RT::WebPath%>/User/Groups/Members.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<%$Group->Id%>" />
+<table width="100%">
+<tr>
+<td>
+<&|/l&>Add members</&>
+</td>
+<td>
+<&|/l&>Current members</&>
+</td>
+</tr>
+
+<tr>
+<td valign="top">
+<& /Admin/Elements/SelectNewGroupMembers, Name => "AddMembers", Group => $Group &>
+</td>
+<td valign="top">
+
+% if ($Group->MembersObj->Count == 0 ) {
+<em><&|/l&>(No members)</&></em>
+% } else {
+<em><&|/l&>(Check box to delete)</&></em>
+<br />
+<br />
+<&|/l&>Users</&>
+% my $UserMembers = $Group->MembersObj;
+% $UserMembers->LimitToUsers();
+<ul>
+% while (my $member = $UserMembers->Next()) {
+<li><input type="checkbox" class="checkbox" name="DeleteMember-<%$member->MemberId%>" value="1" />
+<%$member->MemberObj->Object->Name%> (<%$member->MemberObj->Object->RealName%>)
+% }
+</ul>
+<&|/l&>Groups</&>
+<ul>
+% my $GroupMembers = $Group->MembersObj;
+% $GroupMembers->LimitToGroups();
+% while (my $member = $GroupMembers->Next()) {
+<li><input type="checkbox" class="checkbox" name="DeleteMember-<%$member->MemberId%>" value="1" />
+<%$member->MemberObj->Object->Name%>
+% }
+</ul>
+% }
+</td>
+</tr>
+</table>
+<& /Elements/Submit, Label => loc('Modify Members') &>
+</form>
+
+
+<%INIT>
+
+my $Group = new RT::Group($session{'CurrentUser'});
+$Group->Load($id) ;
+
+unless ($Group->id) {
+ Abort(loc('Could not load group'));
+}
+
+my (@results);
+
+foreach my $key (keys %ARGS) {
+
+if ($key =~ /^DeleteMember-(\d+)$/) {
+ my $mem_id = $1;
+ my ($val,$msg) = $Group->DeleteMember($mem_id);
+ push (@results, $msg);
+}
+}
+
+# Make sure AddMembers is always an array
+my @AddMembersUsers = (ref $AddMembersUsers eq 'ARRAY') ? @{$AddMembersUsers} : ($AddMembersUsers);
+my @AddMembersGroups = (ref $AddMembersGroups eq 'ARRAY') ? @{$AddMembersGroups} : ($AddMembersGroups);
+
+foreach my $member (@AddMembersUsers, @AddMembersGroups) {
+ next unless ($member);
+
+ my $principal;
+
+ if ($member =~ /^Group-(\d+)$/) {
+ $principal = RT::Group->new($session{'CurrentUser'});
+ $principal->Load($1);
+ } elsif ($member =~ /^User-(\d+)$/) {
+ $principal = RT::User->new($session{'CurrentUser'});
+ $principal->Load($1);
+ } else {
+ next;
+ }
+
+
+ my ($val, $msg) = $Group->AddMember($principal->PrincipalId);
+ push (@results, $msg);
+}
+
+
+my $title = loc('Editing membership for personal group [_1]', $Group->Name);
+
+</%INIT>
+
+<%ARGS>
+$AddMembersUsers => undef
+$AddMembersGroups => undef
+$id => undef
+</%ARGS>
diff --git a/rt/html/User/Groups/Modify.html b/rt/html/User/Groups/Modify.html
new file mode 100644
index 0000000..da42f2c
--- /dev/null
+++ b/rt/html/User/Groups/Modify.html
@@ -0,0 +1,157 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+
+<& /User/Elements/GroupTabs,
+ GroupObj => $Group,
+ current_subtab => $current_tab,
+ Title => $title &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+
+<form action="<%$RT::WebPath%>/User/Groups/Modify.html" method="post">
+
+%unless ($Group->Id) {
+<input type="hidden" class="hidden" name="id" value="new" />
+% } else {
+<input type="hidden" class="hidden" name="id" value="<%$Group->Id%>" />
+% }
+<table>
+<tr><td align="right">
+<&|/l&>Name</&>:
+</td>
+<td><input name="Name" value="<%$Group->Name%>" /></td>
+</tr><tr>
+<td align="right">
+<&|/l&>Description</&>:</td><td colspan="3"><input name="Description" value="<%$Group->Description%>" size="60" /></td>
+</tr><tr>
+<td colspan="2">
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <%$EnabledChecked%> /> <&|/l&>Enabled (Unchecking this box disables this group)</&><br />
+</tr>
+</table>
+<& /Elements/Submit, Label => loc('Save Changes'), Reset => 1 &>
+</form>
+<%INIT>
+
+my $current_tab;
+my ($title, @results, $Disabled, $EnabledChecked);
+
+my $Group = RT::Group->new($session{'CurrentUser'});
+
+if ($Create) {
+ $current_tab = 'User/Groups/Modify.html?Create=1';
+ $title = loc("Create a new personal group");
+}
+else {
+ if ( $id eq 'new' ) {
+
+ my ( $id, $msg ) = $Group->CreatePersonalGroup(
+ Name => "$Name",
+ PrincipalId => $session{'CurrentUser'}->PrincipalId
+ );
+ unless ($id) {
+ Abort( loc("Could not create group") );
+ }
+ $id = $Group->Id;
+ }
+ else {
+ $Group->Load($id) || Abort( loc('Could not load group') );
+ }
+
+ if ($id) {
+ $title = loc( "Modify the group [_1]", $Group->Name );
+
+ }
+
+ # If the create failed
+ else {
+ $title = loc("Create a new personal group");
+ $Create = 1;
+ }
+
+ $current_tab = 'User/Groups/Modify.html?id=' . $Group->Id;
+}
+
+if ($id) {
+
+ my @fields = qw(Description Name );
+ my @fieldresults = UpdateRecordObject ( AttributesRef => \@fields,
+ Object => $Group,
+ ARGSRef => \%ARGS );
+ push (@results,@fieldresults);
+}
+
+#we're asking about enabled on the web page but really care about disabled.
+if ($Enabled == 1) {
+ $Disabled = 0;
+}
+else {
+ $Disabled = 1;
+}
+if ( ($SetEnabled) and ( $Disabled != $Group->Disabled) ) {
+ my ($code, $msg) = $Group->SetDisabled($Disabled);
+ push @results, loc('Enabled status [_1]', loc_fuzzy($msg));
+}
+
+unless ($Group->Disabled()) {
+ $EnabledChecked ="CHECKED";
+}
+
+</%INIT>
+
+
+<%ARGS>
+$Create => undef
+$Name => undef
+$Description => undef
+$SetEnabled => undef
+$Enabled => undef
+$id => undef
+</%ARGS>
diff --git a/rt/html/User/Groups/index.html b/rt/html/User/Groups/index.html
new file mode 100644
index 0000000..76ffac3
--- /dev/null
+++ b/rt/html/User/Groups/index.html
@@ -0,0 +1,67 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title => $title &>
+<& /User/Elements/GroupTabs,
+ current_subtab => 'User/Groups/index.html',
+ Title => $title &>
+
+<&|/l&>Personal groups</&>:<br />
+<ul>
+%while ( my $Group = $Groups->Next) {
+<li><a href="Modify.html?id=<%$Group->id%>"><%$Group->Name || loc('(empty)')%></a><br />
+%}
+</ul>
+
+<%INIT>
+my $Groups = RT::Groups->new($session{'CurrentUser'});
+$Groups->LimitToPersonalGroupsFor($session{'CurrentUser'}->PrincipalId());
+my $title = loc('Personal groups');
+
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/User/Prefs.html b/rt/html/User/Prefs.html
new file mode 100644
index 0000000..8c6d5f1
--- /dev/null
+++ b/rt/html/User/Prefs.html
@@ -0,0 +1,289 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Header, Title=>loc("Preferences") &>
+<& /User/Elements/Tabs,
+ current_tab => 'User/Prefs.html',
+ Title=>loc("Preferences") &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="<%$RT::WebPath%>/User/Prefs.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<%$UserObj->Id%>" />
+
+<table width="100%" border="0">
+<tr>
+
+<td valign="top" class="boxcontainer">
+<&| /Widgets/TitleBox, title => loc('Identity'), id => "user-prefs-identity" &>
+
+<input type="hidden" class="hidden" name="Name" value="<%$UserObj->Name%>" />
+<table cellspacing="0" cellpadding="0">
+ <tr>
+ <td class="label"><&|/l&>Email</&>: </td>
+ <td class="value"><input name="EmailAddress" value="<%$UserObj->EmailAddress%>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Real Name</&>:</td>
+ <td class="value"><input name="RealName" value="<%$UserObj->RealName%>" /></td> </tr>
+ <tr>
+ <td class="label"><&|/l&>Nickname</&>:</td>
+ <td class="value"><input name="NickName" value="<%$UserObj->NickName%>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Language</&>:</td>
+ <td class="value"><& /Elements/SelectLang, Name => 'Lang', Default => $UserObj->Lang &></td>
+ </tr>
+</table>
+</&>
+<&| /Widgets/TitleBox, title => loc('Phone numbers'), id => "user-prefs-phone" &>
+<table cellspacing="0" cellpadding="0">
+ <tr>
+ <td class="label"><&|/l&>Residence</&>:</td>
+ <td class="value"><input name="HomePhone" value="<%$UserObj->HomePhone%>" size="13" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Work</&>:</td>
+ <td class="value"><input name="WorkPhone" value="<%$UserObj->WorkPhone%>" size="13" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Mobile</&>:</td>
+ <td class="value"><input name="MobilePhone" value="<%$UserObj->MobilePhone%>" size="13" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Pager</&>:</td>
+ <td class="value"><input name="PagerPhone" value="<%$UserObj->PagerPhone%>" size="13" /></td>
+ </tr>
+</table>
+</&>
+<& /Elements/Callback, _CallbackName => 'FormLeftColumn', UserObj => $UserObj, %ARGS &>
+</td>
+<td valign="top" class="boxcontainer">
+% unless ($RT::WebExternalAuth and !$RT::WebFallbackToInternalAuth) {
+<&| /Widgets/TitleBox, title => loc('Password'), id => "user-prefs-password" &>
+<table>
+<tr>
+<td class="label">
+<&|/l&>New Password</&>:
+</td>
+<td class="value">
+<input type="password" name="Pass1" />
+</td>
+</tr>
+<tr><td class="label">
+<&|/l&>Retype Password</&>:
+</td>
+<td class="value">
+<input type="password" name="Pass2" />
+</td>
+</tr>
+</table>
+</&>
+% }
+
+<&| /Widgets/TitleBox, title => loc('Location'), id => "user-prefs-location" &>
+<table cellspacing="0" cellpadding="0">
+ <tr>
+ <td class="label"><&|/l&>Organization</&>:</td>
+ <td class="value"><input name="Organization" value="<%$UserObj->Organization%>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Address1</&>:</td>
+ <td class="value"><input name="Address1" value="<%$UserObj->Address1%>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Address2</&>:</td>
+ <td class="value"><input name="Address2" value="<%$UserObj->Address2%>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>City</&>:</td>
+ <td><input name="City" value="<%$UserObj->City%>" size="14" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>State</&>:</td>
+ <td class="value"><input name="State" value="<%$UserObj->State%>" size="3" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Zip</&>:</td>
+ <td class="value"><input name="Zip" value="<%$UserObj->Zip%>" size="9" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Country</&>:</td>
+ <td class="value"><input name="Country" value="<%$UserObj->Country%>" /></td>
+ </tr>
+</table>
+</&>
+<& /Elements/Callback, _CallbackName => 'FormRightColumn', UserObj => $UserObj, %ARGS &>
+</td>
+</tr>
+<tr>
+
+
+
+<td colspan="2" valign="top" class="boxcontainer">
+%if ($UserObj->Privileged) {
+<br />
+<&| /Widgets/TitleBox, title => loc('Signature') &>
+<textarea cols="80" rows="5" name="Signature" class="signature" wrap="hard">
+<%$UserObj->Signature%></textarea>
+</&>
+% }
+
+</td>
+
+</tr>
+</table>
+
+<& /Elements/Callback, _CallbackName => 'FormEnd', UserObj => $UserObj, %ARGS &>
+
+<& /Elements/Submit, Label => loc('Save Preferences') &>
+</form>
+
+
+<%INIT>
+
+my $UserObj = new RT::User($session{'CurrentUser'});
+my ($title, $PrivilegedChecked, $EnabledChecked, $Disabled, $result, @results);
+
+my ($val, $msg);
+
+
+ $UserObj->Load($id) || $UserObj->Load($Name) || Abort("Couldn't load user '$Name'");
+ $val = $UserObj->Id();
+
+
+
+
+
+
+# If we have a user to modify, lets try.
+if ($UserObj->Id) {
+
+ my @fields = qw(Name Comments Signature EmailAddress FreeformContactInfo
+ Organization RealName NickName Lang EmailEncoding WebEncoding
+ ExternalContactInfoId ContactInfoSystem Gecos ExternalAuthId
+ AuthSystem HomePhone WorkPhone MobilePhone PagerPhone Address1
+ Address2 City State Zip Country Lang
+ );
+
+ $m->comp('/Elements/Callback', _CallbackName => 'UpdateLogic',
+ fields => \@fields,
+ results => \@results,
+ UserObj => $UserObj,
+ ARGSRef => \%ARGS);
+
+ my @fieldresults = UpdateRecordObject ( AttributesRef => \@fields,
+ Object => $UserObj,
+ ARGSRef => \%ARGS );
+ if ($Lang) {
+ $session{'CurrentUser'}->LanguageHandle($Lang);
+ $session{'CurrentUser'} = $session{'CurrentUser'}; # force writeback
+ }
+
+ push (@results,@fieldresults);
+
+
+# {{{ Deal with special fields: Privileged, Enabled and Password
+if ( ($SetPrivileged) and ( $Privileged != $UserObj->Privileged) ) {
+my ($code, $msg) = $UserObj->SetPrivileged($Privileged);
+ push @results, loc('Privileged status: [_1]', loc_fuzzy($msg));
+}
+
+
+
+#TODO: make this report errors properly
+if ((defined $Pass1) and ($Pass1 ne '') and ($Pass1 eq $Pass2) and (!$UserObj->IsPassword($Pass1))) {
+ my ($code, $msg);
+ ($code, $msg) = $UserObj->SetPassword($Pass1);
+ push @results, loc('Password: [_1]', loc_fuzzy($msg));
+} elsif ( $Pass1 && ($Pass1 ne $Pass2)) {
+ push @results, loc("Passwords do not match. Your password has not been changed");
+}
+
+# }}}
+}
+
+
+</%INIT>
+
+
+<%ARGS>
+$id => $session{'CurrentUser'}->Id
+$Name => undef
+$Comments => undef
+$Signature => undef
+$EmailAddress => undef
+$FreeformContactInfo => undef
+$Organization => undef
+$RealName => undef
+$NickName => undef
+$Privileged => undef
+$SetPrivileged => undef
+$Enabled => undef
+$SetEnabled => undef
+$Lang => undef
+$EmailEncoding => undef
+$WebEncoding => undef
+$ExternalContactInfoId => undef
+$ContactInfoSystem => undef
+$Gecos => undef
+$ExternalAuthId => undef
+$AuthSystem => undef
+$HomePhone => undef
+$WorkPhone => undef
+$MobilePhone => undef
+$PagerPhone => undef
+$Address1 => undef
+$Address2 => undef
+$City => undef
+$State => undef
+$Zip => undef
+$Country => undef
+$Pass1 => undef
+$Pass2=> undef
+$Create=> undef
+</%ARGS>
diff --git a/rt/html/Widgets/ComboBox b/rt/html/Widgets/ComboBox
new file mode 100644
index 0000000..8fb5682
--- /dev/null
+++ b/rt/html/Widgets/ComboBox
@@ -0,0 +1,69 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<nobr>
+<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/combobox.js"></script>
+
+<span id="<% $Name %>_Container" class="combobox">
+<input name="<% $Name %>" id="<% $Name %>" class="combo-text" value="<% $Default %>" type="text" <% $Size ? "size='$Size'" : '' |n %> autocomplete="off" />
+<br style="display: none" /><span id="<% $Name %>_Button" class="combo-button"></span></span><select name="List-<% $Name %>" id="<% $Name %>_List" class="combo-list" onchange="ComboBox_SimpleAttach(this, this.form['<% $Name %>']); " rows="<% $Rows %>">
+<option style="display: none" value="">-</option>
+% foreach my $value (@Values) {
+ <option value="<%$value%>"><% $value%></option>
+% }
+</select>
+<script language="javascript"><!--
+ComboBox_InitWith('<% $Name %>');
+//--></script>
+</nobr>
+<%ARGS>
+$Name
+$Size => undef
+$Rows => 5
+$Default => ''
+@Values => ()
+</%ARGS>
diff --git a/rt/html/Widgets/SavedSearch b/rt/html/Widgets/SavedSearch
new file mode 100644
index 0000000..b873c2b
--- /dev/null
+++ b/rt/html/Widgets/SavedSearch
@@ -0,0 +1,158 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%method new>
+<%init>
+return \%ARGS;
+</%init>
+</%method>
+
+<%method process>
+
+<%init>
+my @actions;
+my @Objects = RT::SavedSearches->new( $session{CurrentUser} )->_PrivacyObjects;
+push @Objects, RT::System->new($session{'CurrentUser'})
+ if $session{'CurrentUser'}->HasRight( Object=> $RT::System,
+ Right => 'SuperUser');
+$self->{SearchId} ||= 'new';
+my $SearchParams = { map { $_ => $args->{$_} } @{$self->{SearchFields}} };
+
+if ( my ( $container_object, $search_id ) = _parse_saved_search( $args->{'LoadSavedSearch'} ) ) {
+ my $search = $container_object->Attributes->WithId($search_id);
+ # We have a $search and now; import the others
+ $self->{SearchId} = $args->{'LoadSavedSearch'};
+ $self->{CurrentSearch}{Object} = $search;
+ $args->{$_} = $search->SubValue($_) for @{ $self->{SearchFields} };
+}
+
+# look for the current one in the available saved searches
+if ($self->{SearchId} eq 'new') {
+ for my $obj (@Objects) {
+ for ( $m->comp( "/Search/Elements/SearchesForObject", Object => $obj ) ) {
+ my ( $desc, $search ) = @$_;
+ use Data::Dumper;
+ # FFS
+ local $Data::Dumper::Sortkeys = 1;
+ if ( Dumper( $search->Content ) eq
+ Dumper( { %$SearchParams, SearchType => $self->{SearchType} } ) ) {
+ $self->{CurrentSearch}{Object} = $search;
+ $self->{SearchId} = $search->Id;
+ }
+ }
+ }
+}
+
+if ( $args->{Save} ) {
+ if ( my $search = $self->{CurrentSearch}{Object} ) {
+ # rename
+ $search->SetDescription( $args->{Description} );
+ push @actions, loc($self->{SearchType}).loc( ' [_1] renamed to [_2].', $self->{CurrentSearch}{Description}, $args->{Description} );
+ }
+ else {
+ # new saved search
+ my $saved_search = RT::SavedSearch->new( $session{'CurrentUser'} );
+ my ( $ok, $search_msg ) = $saved_search->Save(
+ Privacy => $args->{'Owner'},
+ Name => $args->{'Description'},
+ Type => $self->{'SearchType'},
+ SearchParams => $SearchParams
+ );
+ if ($ok) {
+ $self->{CurrentSearch}{Object} = $saved_search->{Attribute};
+ push @actions, loc($self->{SearchType}).loc( ' [_1] saved.', $args->{Description} );
+ } else {
+ push @actions,
+ [ loc("Can't save [_1]", loc($self->{SearchType})) . ': ' . loc($search_msg), 0 ];
+ }
+ }
+}
+
+if ( $args->{Delete} && $self->{CurrentSearch}{Object} ) {
+ my ($ok, $msg) = $self->{CurrentSearch}{Object}->Delete;
+ push @actions, $ok ? loc($self->{SearchType}).loc( ' [_1] deleted.', $self->{CurrentSearch}{Object}->Description ) : $msg;
+ delete $self->{CurrentSearch}{Object};
+ delete $self->{SearchId};
+
+}
+
+$self->{CurrentSearch}{Description} = $self->{CurrentSearch}{Object}->Description
+ if $self->{CurrentSearch}{Object};
+
+return @actions;
+</%init>
+<%ARGS>
+$self
+$args
+</%ARGS>
+
+</%method>
+
+<%method show>
+<form method="post" action="<% $Action %>" name="SaveSearch">
+<& /Search/Elements/EditSearches, Name => 'Owner', SearchType => $self->{SearchType}, AllowCopy => 0,
+ CurrentSearch => $self->{CurrentSearch}, SearchId => $self->{SearchId}, Title => $Title &><br />
+<%PERL>
+foreach my $field ( @{$self->{SearchFields}} ) {
+ if ( ref($ARGS{$field}) && ref($ARGS{$field}) ne 'ARRAY' ) {
+ $RT::Logger->error("Couldn't store '$field'. it's reference to ". ref($ARGS{$field}) );
+ next;
+ }
+ foreach my $value ( grep defined, ref($ARGS{$field})? @{ $ARGS{$field} } : $ARGS{$field} ) {
+</%PERL>
+<input type="hidden" class="hidden" name="<% $field %>" value="<% $value %>" />
+% }
+% }
+</form>
+<%ARGS>
+$self => undef
+$Action => ''
+$Title => 'Saved searches'
+</%ARGS>
+<%init>
+</%init>
+</%method>
diff --git a/rt/html/Widgets/SelectionBox b/rt/html/Widgets/SelectionBox
new file mode 100644
index 0000000..38ae7a0
--- /dev/null
+++ b/rt/html/Widgets/SelectionBox
@@ -0,0 +1,243 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+%# The SelectionBox Widget
+%#
+%# SYNOPSIS
+%#
+%# include javascript:
+%# <& /Widgets/SelectionBox:header &>
+%#
+%# <%init>:
+%# my $sel = $m->comp ('/Widgets/SelectionBox:new',
+%# Action => me.html',
+%# Name => 'my-selection',
+%# Available => \@items,
+%# # you can do things with @{$sel->{Current}} in the
+%# # OnSubmit callback
+%# OnSubmit => sub { my $sel = shift; },
+%# Selected => \@selected);
+%#
+%# $m->comp ('/Widgets/SelectionBox:process', %ARGS, self => $sel)
+%#
+%# where @items is an arrayref, each element is [value, label],
+%# and @selected is an arrayref of selected values from @items.
+%#
+%# and in html:
+%# <& /Widgets/SelectionBox:sow, self => $sel &>
+%#
+%# if the SelectionBox is created with AutoSave option, OnSubmit will be called
+%# on every button clicked in non-js mode.
+<%method header>
+% unless ($nojs) {
+<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/class.js"></script>
+<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/list.js"></script>
+% }
+<%ARGS>
+$nojs => 0
+</%ARGS>
+</%method>
+
+<%method new>
+<%init>
+$ARGS{_item_map} = {map {$_->[0] => $_->[1]} @{$ARGS{Available}}};
+return \%ARGS;
+</%init>
+</%method>
+
+<%method process>
+<%init>
+unless ($ARGS{$self->{Name}.'-Submit'}) {
+ # init
+ $self->{Current} = $self->{Selected};
+ $self->{Selected} = [];
+ return;
+}
+
+$self->{Selected} = $ARGS{$self->{Name}.'-Selected'};
+if ($self->{Selected} && !ref($self->{Selected})) {
+ $self->{Selected} = [$self->{Selected}];
+}
+
+if ($ARGS{fromjs}) {
+ $self->{Current} = $self->{Selected};
+}
+else {
+ my $current = $self->{Current} = $ARGS{$self->{Name}.'-Current'};
+ ++$self->{Modified};
+ if ($current && !ref ($current)) {
+ $current = [$current];
+ }
+
+ if ($ARGS{add}) {
+ my $choosed = $ARGS{$self->{Name}.'-Available'};
+ for my $add (ref($choosed) ? @$choosed : $choosed) {
+ next if grep { $_ eq $add } @$current;
+ push @$current, $add;
+ }
+ }
+
+ if ($ARGS{remove}) {
+ my $choosed = $ARGS{$self->{Name}.'-Selected'};
+ for my $del (ref($choosed) ? @$choosed : $choosed) {
+ @$current = map { $_ eq $del ? () : $_ } @$current;
+ }
+ }
+
+ if ($ARGS{moveup} or $ARGS{movedown}) {
+ my $offset = $ARGS{moveup} ? 1 : 0;
+ my $choosed = $ARGS{$self->{Name}.'-Selected'};
+ $choosed = [$choosed] unless ref ($choosed);
+ my $canmove = 0; # not in the cornor
+ for my $i ($ARGS{moveup} ? 0..$#{$current} : reverse 0..$#{$current}) {
+ if (grep {$_ eq $current->[$i]} @$choosed) {
+ if ($canmove) {
+ splice (@$current, $i-$offset, 2,
+ @{$current}[$i+1-$offset,$i-$offset]);
+ }
+ }
+ else {
+ ++$canmove;
+ }
+ }
+ }
+
+ if ($ARGS{clear}) {
+ $current = [];
+ }
+
+ $self->{Current} = $current;
+}
+
+@{$self->{Current}} = grep { exists $self->{_item_map}{$_} } @{$self->{Current}};
+
+if ($self->{AutoSave} or $ARGS{$self->{Name}.'-Save'}) {
+ $self->{OnSubmit}->($self);
+ delete $self->{Modified};
+}
+
+</%init>
+<%ARGS>
+$self => undef
+</%ARGS>
+
+</%method>
+
+<%method current>
+% for (@{$self->{Current}}) {
+<input type="hidden" class="hidden" name="<% $self->{Name} %>-Current" value="<%$_%>" />
+% }
+<%INIT>
+</%INIT>
+<%ARGS>
+$self => undef
+</%ARGS>
+
+</%method>
+
+<%method show>
+<form method="post" action="<%$self->{Action}%>" name="SelectionBox-<% $name %>" id="SelectionBox-<% $name %>"
+% unless ($nojs) {
+onsubmit="list_<% $name %>.selectAll();"
+% }
+>
+<input type="hidden" class="hidden" name="<% $self->{Name} %>-Submit" value="1" />
+<& SelectionBox:current, self => $self &>
+<input type="hidden" class="hidden" name="fromjs" value="0" />
+<&|/l&>Available</&>:
+<br />
+<select name="<%$name%>-Available" id="<%$name%>-Available" size="<%$size%>" multiple="multiple">
+% for (@{$self->{Available}}) {
+<option value="<% $_->[0] %>"><% $_->[1] %></option>
+% }
+</select>
+<input name="add" type="submit" class="button" value=" &rarr; " />
+<select name="<%$name%>-Selected" id="<%$name%>-Selected" size="<%$size%>" multiple="multiple">
+% for (@{$self->{Current}}) {
+<option value="<% $_ %>"
+% if (exists $selected{$_}) {
+selected="selected"
+% }
+><% $self->{_item_map}{$_} %></option>
+% }
+</select>
+% unless ($ARGS{'NoArrows'}) {
+ <input name="moveup" type="submit" class="button" value=" &uarr; " />
+ <input name="movedown" type="submit" class="button" value=" &darr; " />
+% }
+ <input name="remove" type="submit" class="button" value="<&|/l&>Delete</&>" />
+% if ($ARGS{'Clear'}) {
+ <input name="clear" type="submit" class="button" value="<&|/l&>Clear</&>" />
+% }
+
+% my $caption = "";
+% unless ($self->{'AutoSave'}) {
+% if ($self->{Modified}) {
+% $caption = loc('Selections modified. Please save your changes');
+% }
+<& /Elements/Submit, Caption => loc($caption), Label => loc('Save'), Name => $name.'-Save' &>
+% }
+</form>
+
+% unless ($nojs) {
+<script type="text/javascript">
+//<![CDATA[
+var list_<%$name%> = new list(document.getElementById("SelectionBox-<% $name %>"), 0, "list_<%$name%>");
+//]]>
+</script>
+% }
+<%ARGS>
+$self => undef
+$size => 10
+$nojs => 0
+</%ARGS>
+<%INIT>
+my $name = $self->{Name};
+my %selected = map {$_ => 1} @{$self->{Selected}};
+</%INIT>
+
+</%method>
diff --git a/rt/html/Widgets/TitleBox b/rt/html/Widgets/TitleBox
new file mode 100644
index 0000000..00d9e4c
--- /dev/null
+++ b/rt/html/Widgets/TitleBox
@@ -0,0 +1,54 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<div class="<% $class %>">
+ <& TitleBoxStart, %ARGS &><% $m->content | n %><& TitleBoxEnd &>
+</div>
+<%ARGS>
+$class => ''
+</%ARGS>
+
diff --git a/rt/html/Widgets/TitleBoxEnd b/rt/html/Widgets/TitleBoxEnd
new file mode 100755
index 0000000..b12b58f
--- /dev/null
+++ b/rt/html/Widgets/TitleBoxEnd
@@ -0,0 +1,59 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+ <hr class="clear" />
+ </div>
+</div>
+
+% #Manually flush the content buffer after each titlebox is displayed
+% $m->flush_buffer();
+
+<%ARGS>
+$title => undef
+$content => undef
+</%ARGS>
+
diff --git a/rt/html/Widgets/TitleBoxStart b/rt/html/Widgets/TitleBoxStart
new file mode 100755
index 0000000..1d8548d
--- /dev/null
+++ b/rt/html/Widgets/TitleBoxStart
@@ -0,0 +1,86 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<div class="titlebox <% $class %>" id="<% $id %>">
+ <div class="titlebox-title<% $title_class && " $title_class" %>">
+% if ($hideable) {
+ <span class="widget"><a href="#" onclick="return rollup('<%$tid%>');" onfocus="this.blur(); return false;" title="Toggle visibility">X</a></span>
+% }
+ <span class="left"><% $title_href && qq[<a href="$title_href">] | n %><% $title |n %><% $title_href && "</a>" |n%></span>
+ <span class="right"><% $titleright_href && qq[<a href="$titleright_href">] | n %><% $titleright |n %><% $titleright_href && "</a>" |n%></span>
+ </div>
+ <div class="titlebox-content <% $bodyclass %>" id="<%$tid%>">
+
+<%ARGS>
+$width => undef
+$class => ''
+$bodyclass => ''
+$title_href => undef
+$title => ''
+$title_class => ''
+$titleright_href => undef
+$titleright => undef
+$id => ''
+$hideable => 1
+</%ARGS>
+
+<%init>
+#
+# This should be pretty bulletproof
+#
+my $page = $m->request_comp->path;
+
+my $tid = "TitleBox--$page--" .
+ join '--', ($class, $bodyclass, $title, $id);
+
+$tid =~ s{/}{_}g;
+
+my $i = 0;
+$i++ while $m->notes("$tid-$i");
+$m->notes("$tid-$i" => 1);
+$tid = "$tid-$i";
+</%init>
diff --git a/rt/html/autohandler b/rt/html/autohandler
new file mode 100644
index 0000000..909b922
--- /dev/null
+++ b/rt/html/autohandler
@@ -0,0 +1,331 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%INIT>
+
+# Roll back any dangling transactions from a previous failed connection
+$RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth;
+
+
+if ($RT::StatementLog) {
+ $RT::Handle->ClearSQLStatementLog;
+ $RT::Handle->LogSQLStatements(1);
+}
+
+local *session
+ unless $m->is_subrequest; # avoid reentrancy, as suggested by masonbook
+
+# Disable AutoFlush using an attribute
+if ( $m->request_comp->attr_exists('AutoFlush') ) {
+ $m->autoflush( $m->request_comp->attr('AutoFlush') );
+}
+
+%ARGS = map {
+
+ # if they've passed multiple values, they'll be an array. if they've
+ # passed just one, a scalar whatever they are, mark them as utf8
+ my $type = ref($_);
+ ( !$type )
+ ? Encode::is_utf8($_)
+ ? $_
+ : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
+ : ( $type eq 'ARRAY' )
+ ? [
+ map {
+ ( ref($_) or Encode::is_utf8($_) )
+ ? $_
+ : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
+ } @$_
+ ]
+ : ( $type eq 'HASH' )
+ ? {
+ map {
+ ( ref($_) or Encode::is_utf8($_) )
+ ? $_
+ : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
+ } %$_
+ }
+ : $_
+} %ARGS;
+
+# Latter in the code we use
+# $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS );
+# instead of $m->call_next to avoid problems with UTF8 keys in arguments.
+# The call_next method pass through original arguments and if you have
+# an argument with unicode key then in a next component you'll get two
+# records in the args hash: one with key without UTF8 flag and another
+# with the flag, which may result into errors. "{ base_comp => $m->request_comp }"
+# is copied from mason's source to get the same results as we get from
+# call_next method, this feature is not documented, so we just leave it
+# here to avoid possible side effects.
+
+# This code canonicalizes time inputs in hours into minutes
+foreach my $field ( keys %ARGS ) {
+ next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS{$1};
+ my $local = $1;
+ $ARGS{$local} =~ s{\b (?: (\d+) \s+ )? (\d+)/(\d+) \b}
+ {($1 || 0) + $3 ? $2 / $3 : 0}xe;
+ if ( $ARGS{$field} && $ARGS{$field} =~ /hours/i ) {
+ $ARGS{$local} *= 60;
+ }
+ delete $ARGS{$field};
+}
+
+$m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ];
+
+$m->comp( '/Elements/SetupSessionCookie', %ARGS );
+
+unless ( $session{'CurrentUser'} && $session{'CurrentUser'}->Id ) {
+ $session{'CurrentUser'} = RT::CurrentUser->new();
+}
+
+# Set the proper encoding for the current language handle
+$r->content_type("text/html; charset=utf-8");
+
+# If it's a noauth file, don't ask for auth.
+if ( $m->base_comp->path =~ $RT::WebNoAuthRegex ) {
+ $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
+ $m->abort;
+}
+
+# If RT is configured for external auth, let's go through and get REMOTE_USER
+elsif ($RT::WebExternalAuth) {
+
+ # do we actually have a REMOTE_USER equivlent?
+ if ( RT::Interface::Web::WebCanonicalizeInfo() ) {
+
+ my $orig_user = $user;
+
+ $user = RT::Interface::Web::WebCanonicalizeInfo();
+ $session{'CurrentUser'} = RT::CurrentUser->new();
+ my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load';
+
+ if ( $^O eq 'MSWin32' and $RT::WebExternalGecos ) {
+ my $NodeName = Win32::NodeName();
+ $user =~ s/^\Q$NodeName\E\\//i;
+ }
+
+ $session{'CurrentUser'}->$load_method($user);
+
+ if ( $RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) {
+
+ # Create users on-the-fly
+
+ my $UserObj = RT::User->new( RT::CurrentUser->new('RT_System') );
+
+ my ( $val, $msg ) = $UserObj->Create(
+ %{ ref($RT::AutoCreate) ? $RT::AutoCreate : {} },
+ Name => $user,
+ Gecos => $user,
+ );
+
+ if ($val) {
+
+ # now get user specific information, to better create our user.
+ my $new_user_info
+ = RT::Interface::Web::WebExternalAutoInfo($user);
+
+ # set the attributes that have been defined.
+ # FIXME: this is a horrible kludge. I'm sure there's something cleaner
+ foreach my $attribute (
+ 'Name', 'Comments',
+ 'Signature', 'EmailAddress',
+ 'PagerEmailAddress', 'FreeformContactInfo',
+ 'Organization', 'Disabled',
+ 'Privileged', 'RealName',
+ 'NickName', 'Lang',
+ 'EmailEncoding', 'WebEncoding',
+ 'ExternalContactInfoId', 'ContactInfoSystem',
+ 'ExternalAuthId', 'Gecos',
+ 'HomePhone', 'WorkPhone',
+ 'MobilePhone', 'PagerPhone',
+ 'Address1', 'Address2',
+ 'City', 'State',
+ 'Zip', 'Country'
+ )
+ {
+ $m->comp( '/Elements/Callback', %ARGS,
+ _CallbackName => 'NewUser' );
+
+ my $method = "Set$attribute";
+ $UserObj->$method( $new_user_info->{$attribute} )
+ if ( defined $new_user_info->{$attribute} );
+ }
+ $session{'CurrentUser'}->Load($user);
+ }
+ else {
+
+ # we failed to successfully create the user. abort abort abort.
+ delete $session{'CurrentUser'};
+ $m->abort() unless $RT::WebFallbackToInternalAuth;
+ $m->comp( '/Elements/Login', %ARGS,
+ Error => loc( 'Cannot create user: [_1]', $msg ) );
+ }
+ }
+
+ unless ( $session{'CurrentUser'}->Id() ) {
+ delete $session{'CurrentUser'};
+ $user = $orig_user;
+
+ if ($RT::WebExternalOnly) {
+ $m->comp( '/Elements/Login', %ARGS,
+ Error => loc('You are not an authorized user') );
+ $m->abort();
+ }
+ }
+ }
+ elsif ($RT::WebFallbackToInternalAuth) {
+ unless ( defined( $session{'CurrentUser'} ) ) {
+ $m->comp( '/Elements/Login', %ARGS,
+ Error => loc('You are not an authorized user') );
+ $m->abort();
+ }
+ }
+ else {
+
+ # WebExternalAuth is set, but we don't have a REMOTE_USER. abort
+ delete $session{'CurrentUser'} if defined $session{'CurrentUser'};
+ }
+}
+
+delete $session{'CurrentUser'}
+ unless $session{'CurrentUser'}
+ and $session{'CurrentUser'}->Id;
+
+# Process per-page authentication callbacks
+$m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'Auth' );
+
+# If the user is logging in, let's authenticate
+if ( !$session{'CurrentUser'} && defined $user && defined $pass ) {
+ $session{'CurrentUser'} = RT::CurrentUser->new();
+ $session{'CurrentUser'}->Load($user);
+
+ unless ( $session{'CurrentUser'}->id
+ && $session{'CurrentUser'}->IsPassword($pass) )
+ {
+ delete $session{'CurrentUser'};
+ $RT::Logger->error("FAILED LOGIN for $user from $ENV{'REMOTE_ADDR'}");
+ $m->comp( '/Elements/Login', %ARGS,
+ Error => loc('Your username or password is incorrect') );
+ $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'FailedLogin' );
+ $m->abort;
+ }
+ else {
+ $RT::Logger->info(
+ "Successful login for $user from $ENV{'REMOTE_ADDR'}");
+ $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'SuccessfulLogin' );
+ }
+}
+
+# If we've got credentials, let's serve the file up.
+if ( ( defined $session{'CurrentUser'} )
+ and ( $session{'CurrentUser'}->Id ) )
+{
+
+ # Process per-page global callbacks
+ $m->comp( '/Elements/Callback', %ARGS );
+
+ # If the user isn't privileged, they can only see SelfService
+ if ( not $session{'CurrentUser'}->Privileged ) {
+
+ # if the user is trying to access a ticket, redirect them
+ if ( $m->request_comp->path =~ '^(/+)Ticket/Display.html'
+ and $ARGS{'id'} )
+ {
+ RT::Interface::Web::Redirect($RT::WebURL."SelfService/Display.html?id=".$ARGS{'id'});
+ }
+
+ # otherwise, drop the user at the SelfService default page
+ elsif ( $m->base_comp->path !~ '^(/+)SelfService/' ) {
+ RT::Interface::Web::Redirect($RT::WebURL."SelfService/");
+ }
+ else {
+ $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
+ }
+ }
+ else {
+ $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
+ }
+}
+
+# If we have no credentials
+else {
+ $m->comp( '/Elements/Login', %ARGS );
+ $m->abort();
+}
+
+if ($RT::StatementLog) {
+ my @log = $RT::Handle->SQLStatementLog;
+ $RT::Handle->ClearSQLStatementLog;
+ for my $stmt (@log) {
+ my ( $time, $sql, $bind, $duration ) = @{$stmt};
+ my @bind;
+ if ( ref $bind ) {
+ @bind = @{$bind};
+ }
+ else {
+
+ # Older DBIx-SB
+ $duration = $bind;
+ }
+ $RT::Logger->log(
+ level => $RT::StatementLog,
+ message => "SQL(" . sprintf( "%.2f", $duration ) . "s): $sql;"
+ . (
+ @bind ? " [ bound values: @{[map{qq|'$_'|} @bind]} ]" : ""
+ )
+ );
+ }
+}
+
+</%INIT>
+<& /Elements/Footer, %ARGS &>
+<%ARGS>
+$user => undef
+$pass => undef
+$menu => undef
+</%ARGS>
diff --git a/rt/html/index.html b/rt/html/index.html
new file mode 100644
index 0000000..af95a65
--- /dev/null
+++ b/rt/html/index.html
@@ -0,0 +1,117 @@
+<& /Elements/Header, Title=>loc("RT at a glance"), Refresh => $session{'home_refresh_interval'} &>
+<!--
+% $m->out('--'.'>');
+% if (0) {
+%# -->
+<html><head>
+<meta http-equiv="refresh" content="30; url=http://bestpractical.com/rt/rt-broken-install.html">
+<title>Almost there!</title></head>
+<body>
+
+<img src="http://www.bestpractical.com/images/unconfigured-rtlogo.jpg" />
+<br /><br />
+<h1>You're almost there!</h1>
+You haven't yet configured your webserver to run RT.
+
+You appear to have installed RT's web interface correctly, but haven't yet configured your web
+server to "run" the RT server which powers the web interface.
+
+The next step is to edit your webserver's configuration file to instruct it to use
+RT's <strong>mod_perl</strong>, <strong>FastCGI</strong> or <strong>SpeedyCGI</strong> handler.
+
+If you need commercial support, please contact us at sales@bestpractical.com.
+
+
+<!--
+% }
+
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<& /Elements/Tabs,
+ current_toptab => '',
+ Title=>loc("RT at a glance"),
+ actions => $actions,
+ &>
+<& /Elements/ListActions, actions => \@results &>
+<& /Elements/MyRT &>
+<%init>
+
+my @results;
+
+if ($ARGS{'QuickCreate'} ) {
+ my $ticket = RT::Ticket->new($session{'CurrentUser'});
+ my ($tid, $trans, $tmsg) = $ticket->Create(Queue => $ARGS{'Queue'},
+ Owner => $ARGS{'Owner'},
+ Requestor => $session{'CurrentUser'}->UserObj->EmailAddress,
+ Subject => $ARGS{'Subject'});
+
+
+ push (@results, $tmsg);
+}
+
+
+if ( $ARGS{'q'} ) {
+ RT::Interface::Web::Redirect($RT::WebURL."Search/Simple.html?q=".$m->interp->apply_escapes($ARGS{q}));
+}
+
+if ($ARGS{'HomeRefreshInterval'}) {
+ $session{'home_refresh_interval'} = $ARGS{'HomeRefreshInterval'};
+}
+
+my $actions;
+if ($session{'CurrentUser'}->HasRight(Right => 'ModifySelf', Object => $RT::System)) {
+ $actions = {
+ A => { title => loc('Edit'),
+ path => 'Prefs/MyRT.html',
+ },
+ };
+}
+
+</%init>
+
+%# --></body></html>
diff --git a/rt/html/l b/rt/html/l
new file mode 100644
index 0000000..32302aa
--- /dev/null
+++ b/rt/html/l
@@ -0,0 +1,52 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.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 }}}
+<%init>
+ my $hand = ($session{'CurrentUser'} ||= RT::CurrentUser->new)->LanguageHandle;
+ $m->print($hand->maketext($m->content,@_));
+ return(1);
+</%init>
diff --git a/rt/install-sh b/rt/install-sh
new file mode 100644
index 0000000..11870f1
--- /dev/null
+++ b/rt/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ :
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=$mkdirprog
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f "$src" ] || [ -d "$src" ]
+ then
+ :
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ :
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ :
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+ '
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ :
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ :
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/rt/lib/RT.pm b/rt/lib/RT.pm
new file mode 100644
index 0000000..0d0c0f5
--- /dev/null
+++ b/rt/lib/RT.pm
@@ -0,0 +1,465 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT;
+use strict;
+use RT::I18N;
+use RT::CurrentUser;
+use RT::System;
+
+use vars qw($VERSION $System $SystemUser $Nobody $Handle $Logger
+ $CORE_CONFIG_FILE
+ $SITE_CONFIG_FILE
+ $BasePath
+ $EtcPath
+ $VarPath
+ $LocalPath
+ $LocalEtcPath
+ $LocalLexiconPath
+ $LogDir
+ $BinPath
+ $MasonComponentRoot
+ $MasonLocalComponentRoot
+ $MasonDataDir
+ $MasonSessionDir
+);
+
+$VERSION = '3.6.4';
+$CORE_CONFIG_FILE = "/opt/rt3/etc/RT_Config.pm";
+$SITE_CONFIG_FILE = "/opt/rt3/etc/RT_SiteConfig.pm";
+
+
+
+$BasePath = '/opt/rt3';
+
+$EtcPath = '/opt/rt3/etc';
+$BinPath = '/opt/rt3/bin';
+$VarPath = '/opt/rt3/var';
+$LocalPath = '/opt/rt3/local';
+$LocalEtcPath = '/opt/rt3/local/etc';
+$LocalLexiconPath = '/opt/rt3/local/po';
+
+# $MasonComponentRoot is where your rt instance keeps its mason html files
+
+$MasonComponentRoot = '/var/www/freeside/rt';
+
+# $MasonLocalComponentRoot is where your rt instance keeps its site-local
+# mason html files.
+
+$MasonLocalComponentRoot = '/opt/rt3/local/html';
+
+# $MasonDataDir Where mason keeps its datafiles
+
+$MasonDataDir = '/usr/local/etc/freeside/masondata';
+
+# RT needs to put session data (for preserving state between connections
+# via the web interface)
+$MasonSessionDir = '/opt/rt3/var/session_data';
+
+
+
+=head1 NAME
+
+RT - Request Tracker
+
+=head1 SYNOPSIS
+
+A fully featured request tracker package
+
+=head1 DESCRIPTION
+
+=head2 LoadConfig
+
+Load RT's config file. First, the site configuration file
+(C<RT_SiteConfig.pm>) is loaded, in order to establish overall site
+settings like hostname and name of RT instance. Then, the core
+configuration file (C<RT_Config.pm>) is loaded to set fallback values
+for all settings; it bases some values on settings from the site
+configuration file.
+
+In order for the core configuration to not override the site's
+settings, the function C<Set> is used; it only sets values if they
+have not been set already.
+
+=cut
+
+sub LoadConfig {
+ local *Set = sub { $_[0] = $_[1] unless defined $_[0] };
+
+ my $username = getpwuid($>);
+ my $group = getgrgid($();
+ my $message = <<EOF;
+
+RT couldn't load RT config file %s as:
+ user: $username
+ group: $group
+
+The file is owned by user %s and group %s.
+
+This usually means that the user/group your webserver is running
+as cannot read the file. Be careful not to make the permissions
+on this file too liberal, because it contains database passwords.
+You may need to put the webserver user in the appropriate group
+(%s) or change permissions be able to run succesfully.
+EOF
+
+
+ if ( -f "$SITE_CONFIG_FILE" ) {
+ eval { require $SITE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($SITE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $SITE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage\n$@");
+ }
+ }
+ eval { require $CORE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($SITE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $SITE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage '$CORE_CONFIG_FILE'\n$@")
+ }
+
+ # RT::Essentials mistakenly recommends that WebPath be set to '/'.
+ # If the user does that, do what they mean.
+ $RT::WebPath = '' if ($RT::WebPath eq '/');
+
+ $ENV{'TZ'} = $RT::Timezone if ($RT::Timezone);
+
+ RT::I18N->Init;
+}
+
+=head2 Init
+
+Conenct to the database, set up logging.
+
+=cut
+
+sub Init {
+
+ CheckPerlRequirements();
+
+ #Get a database connection
+ ConnectToDatabase();
+
+ #RT's system user is a genuine database user. its id lives here
+ $SystemUser = new RT::CurrentUser();
+ $SystemUser->LoadByName('RT_System');
+
+ #RT's "nobody user" is a genuine database user. its ID lives here.
+ $Nobody = new RT::CurrentUser();
+ $Nobody->LoadByName('Nobody');
+
+ $System = RT::System->new();
+
+ InitClasses();
+ InitLogging();
+}
+
+
+=head2 ConnectToDatabase
+
+Get a database connection
+
+=cut
+
+sub ConnectToDatabase {
+ require RT::Handle;
+ unless ($Handle && $Handle->dbh && $Handle->dbh->ping) {
+ $Handle = RT::Handle->new();
+ }
+ $Handle->Connect();
+}
+
+=head2 InitLogging
+
+Create the RT::Logger object.
+
+=cut
+
+sub InitLogging {
+
+ # We have to set the record separator ($, man perlvar)
+ # or Log::Dispatch starts getting
+ # really pissy, as some other module we use unsets it.
+
+ $, = '';
+ use Log::Dispatch 1.6;
+
+ unless ($RT::Logger) {
+
+ $RT::Logger = Log::Dispatch->new();
+
+ my $simple_cb = sub {
+ # if this code throw any warning we can get segfault
+ no warnings;
+
+ my %p = @_;
+
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
+
+ my ($package, $filename, $line) = caller($frame);
+ $p{message} =~ s/(?:\r*\n)+$//;
+ my $str = "[".gmtime(time)."] [".$p{level}."]: $p{message} ($filename:$line)\n";
+
+ if( $RT::LogStackTraces ) {
+ $str .= "\nStack trace:\n";
+ # skip calling of the Log::* subroutins
+ $frame++ while( caller($frame) && (caller($frame))[3] =~ /^Log::/ );
+ while( my ($package, $filename, $line, $sub) = caller($frame++) ) {
+ $str .= "\t". $sub ."() called at $filename:$line\n";
+ }
+ }
+ return $str;
+ };
+
+ my $syslog_cb = sub {
+ my %p = @_;
+
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
+ my ($package, $filename, $line) = caller($frame);
+
+ # syswrite() cannot take utf8; turn it off here.
+ Encode::_utf8_off($p{message});
+
+ $p{message} =~ s/(?:\r*\n)+$//;
+ if ($p{level} eq 'debug') {
+ return "$p{message}\n"
+ } else {
+ return "$p{message} ($filename:$line)\n"
+ }
+ };
+
+ if ($RT::LogToFile) {
+ my ($filename, $logdir);
+ if ($RT::LogToFileNamed =~ m![/\\]!) {
+ # looks like an absolute path.
+ $filename = $RT::LogToFileNamed;
+ ($logdir) = $RT::LogToFileNamed =~ m!^(.*[/\\])!;
+ }
+ else {
+ $filename = "$RT::LogDir/$RT::LogToFileNamed";
+ $logdir = $RT::LogDir;
+ }
+
+ unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
+ # localizing here would be hard when we don't have a current user yet
+ die "Log file $filename couldn't be written or created.\n RT can't run.";
+ }
+
+ package Log::Dispatch::File;
+ require Log::Dispatch::File;
+ $RT::Logger->add(Log::Dispatch::File->new
+ ( name=>'rtlog',
+ min_level=> $RT::LogToFile,
+ filename=> $filename,
+ mode=>'append',
+ callbacks => $simple_cb,
+ ));
+ }
+ if ($RT::LogToScreen) {
+ package Log::Dispatch::Screen;
+ require Log::Dispatch::Screen;
+ $RT::Logger->add(Log::Dispatch::Screen->new
+ ( name => 'screen',
+ min_level => $RT::LogToScreen,
+ callbacks => $simple_cb,
+ stderr => 1,
+ ));
+ }
+ if ($RT::LogToSyslog) {
+ package Log::Dispatch::Syslog;
+ require Log::Dispatch::Syslog;
+ $RT::Logger->add(Log::Dispatch::Syslog->new
+ ( name => 'syslog',
+ ident => 'RT',
+ min_level => $RT::LogToSyslog,
+ callbacks => $syslog_cb,
+ stderr => 1,
+ @RT::LogToSyslogConf
+ ));
+ }
+
+ }
+
+# {{{ Signal handlers
+
+## This is the default handling of warnings and die'ings in the code
+## (including other used modules - maybe except for errors catched by
+## Mason). It will log all problems through the standard logging
+## mechanism (see above).
+
+ $SIG{__WARN__} = sub {
+ # The 'wide character' warnings has to be silenced for now, at least
+ # until HTML::Mason offers a sane way to process both raw output and
+ # unicode strings.
+ # use 'goto &foo' syntax to hide ANON sub from stack
+ if( index($_[0], 'Wide character in ') != 0 ) {
+ unshift @_, $RT::Logger, qw(level warning message);
+ goto &Log::Dispatch::log;
+ }
+ };
+
+#When we call die, trap it and log->crit with the value of the die.
+
+$SIG{__DIE__} = sub {
+ unless ($^S || !defined $^S ) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("$_[0]");
+ }
+ die $_[0];
+};
+
+# }}}
+
+}
+
+
+sub CheckPerlRequirements {
+ if ($^V < 5.008003) {
+ die sprintf "RT requires Perl v5.8.3 or newer. Your current Perl is v%vd\n", $^V;
+ }
+
+ local ($@);
+ eval {
+ my $x = '';
+ my $y = \$x;
+ require Scalar::Util; Scalar::Util::weaken($y);
+ };
+ if ($@) {
+ die <<"EOF";
+
+RT requires the Scalar::Util module be built with support for the 'weaken'
+function.
+
+It is sometimes the case that operating system upgrades will replace
+a working Scalar::Util with a non-working one. If your system was working
+correctly up until now, this is likely the cause of the problem.
+
+Please reinstall Scalar::Util, being careful to let it build with your C
+compiler. Ususally this is as simple as running the following command as
+root.
+
+ perl -MCPAN -e'install Scalar::Util'
+
+EOF
+
+ }
+}
+
+
+=head2 InitClasses
+
+Load all modules that define base classes
+
+=cut
+
+sub InitClasses {
+ require RT::Tickets;
+ require RT::Transactions;
+ require RT::Users;
+ require RT::CurrentUser;
+ require RT::Templates;
+ require RT::Queues;
+ require RT::ScripActions;
+ require RT::ScripConditions;
+ require RT::Scrips;
+ require RT::Groups;
+ require RT::GroupMembers;
+ require RT::CustomFields;
+ require RT::CustomFieldValues;
+ require RT::ObjectCustomFields;
+ require RT::ObjectCustomFieldValues;
+}
+
+# }}}
+
+
+sub SystemUser {
+ return($SystemUser);
+}
+
+sub Nobody {
+ return ($Nobody);
+}
+
+=head1 BUGS
+
+Please report them to rt-bugs@fsck.com, if you know what's broken and have at least
+some idea of what needs to be fixed.
+
+If you're not sure what's going on, report them rt-devel@lists.bestpractical.com.
+
+=head1 SEE ALSO
+
+L<RT::StyleGuide>
+L<DBIx::SearchBuilder>
+
+=begin testing
+
+ok ($RT::Nobody->Name() eq 'Nobody', "Nobody is nobody");
+ok ($RT::Nobody->Name() ne 'root', "Nobody isn't named root");
+ok ($RT::SystemUser->Name() eq 'RT_System', "The system user is RT_System");
+ok ($RT::SystemUser->Name() ne 'noname', "The system user isn't noname");
+
+=end testing
+
+=cut
+
+eval "require RT_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT_Local.pm});
+
+1;
diff --git a/rt/lib/RT.pm.in b/rt/lib/RT.pm.in
new file mode 100644
index 0000000..1a9bf08
--- /dev/null
+++ b/rt/lib/RT.pm.in
@@ -0,0 +1,467 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT;
+use strict;
+use RT::I18N;
+use RT::CurrentUser;
+use RT::System;
+
+use vars qw($VERSION $System $SystemUser $Nobody $Handle $Logger
+ $CORE_CONFIG_FILE
+ $SITE_CONFIG_FILE
+ $BasePath
+ $EtcPath
+ $VarPath
+ $LocalPath
+ $LocalEtcPath
+ $LocalLexiconPath
+ $LogDir
+ $BinPath
+ $MasonComponentRoot
+ $MasonLocalComponentRoot
+ $MasonDataDir
+ $MasonSessionDir
+);
+
+$VERSION = '@RT_VERSION_MAJOR@.@RT_VERSION_MINOR@.@RT_VERSION_PATCH@';
+$CORE_CONFIG_FILE = "@CONFIG_FILE_PATH@/RT_Config.pm";
+$SITE_CONFIG_FILE = "@CONFIG_FILE_PATH@/RT_SiteConfig.pm";
+
+@DATABASE_ENV_PREF@
+
+$BasePath = '@RT_PATH@';
+
+$EtcPath = '@RT_ETC_PATH@';
+$BinPath = '@RT_BIN_PATH@';
+$VarPath = '@RT_VAR_PATH@';
+$LocalPath = '@RT_LOCAL_PATH@';
+$LocalEtcPath = '@LOCAL_ETC_PATH@';
+$LocalLexiconPath = '@LOCAL_LEXICON_PATH@';
+
+# $MasonComponentRoot is where your rt instance keeps its mason html files
+
+$MasonComponentRoot = '@MASON_HTML_PATH@';
+
+# $MasonLocalComponentRoot is where your rt instance keeps its site-local
+# mason html files.
+
+$MasonLocalComponentRoot = '@MASON_LOCAL_HTML_PATH@';
+
+# $MasonDataDir Where mason keeps its datafiles
+
+$MasonDataDir = '@MASON_DATA_PATH@';
+
+# RT needs to put session data (for preserving state between connections
+# via the web interface)
+$MasonSessionDir = '@MASON_SESSION_PATH@';
+
+
+
+=head1 NAME
+
+RT - Request Tracker
+
+=head1 SYNOPSIS
+
+A fully featured request tracker package
+
+=head1 DESCRIPTION
+
+=head2 LoadConfig
+
+Load RT's config file. First, the site configuration file
+(C<RT_SiteConfig.pm>) is loaded, in order to establish overall site
+settings like hostname and name of RT instance. Then, the core
+configuration file (C<RT_Config.pm>) is loaded to set fallback values
+for all settings; it bases some values on settings from the site
+configuration file.
+
+In order for the core configuration to not override the site's
+settings, the function C<Set> is used; it only sets values if they
+have not been set already.
+
+=cut
+
+sub LoadConfig {
+ local *Set = sub { $_[0] = $_[1] unless defined $_[0] };
+
+ my $username = getpwuid($>);
+ my $group = getgrgid($();
+ my $message = <<EOF;
+
+RT couldn't load RT config file %s as:
+ user: $username
+ group: $group
+
+The file is owned by user %s and group %s.
+
+This usually means that the user/group your webserver is running
+as cannot read the file. Be careful not to make the permissions
+on this file too liberal, because it contains database passwords.
+You may need to put the webserver user in the appropriate group
+(%s) or change permissions be able to run succesfully.
+EOF
+
+
+ if ( -f "$SITE_CONFIG_FILE" ) {
+ eval { require $SITE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($SITE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $SITE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage\n$@");
+ }
+ }
+ eval { require $CORE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($CORE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $CORE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage\n$@")
+ }
+
+ # RT::Essentials mistakenly recommends that WebPath be set to '/'.
+ # If the user does that, do what they mean.
+ $RT::WebPath = '' if ($RT::WebPath eq '/');
+
+ $ENV{'TZ'} = $RT::Timezone if ($RT::Timezone);
+
+ RT::I18N->Init;
+}
+
+=head2 Init
+
+Conenct to the database, set up logging.
+
+=cut
+
+sub Init {
+
+ CheckPerlRequirements();
+
+ #Get a database connection
+ ConnectToDatabase();
+
+ #RT's system user is a genuine database user. its id lives here
+ $SystemUser = new RT::CurrentUser();
+ $SystemUser->LoadByName('RT_System');
+
+ #RT's "nobody user" is a genuine database user. its ID lives here.
+ $Nobody = new RT::CurrentUser();
+ $Nobody->LoadByName('Nobody');
+
+ $System = RT::System->new();
+
+ InitClasses();
+ InitLogging();
+}
+
+
+=head2 ConnectToDatabase
+
+Get a database connection
+
+=cut
+
+sub ConnectToDatabase {
+ require RT::Handle;
+ unless ($Handle && $Handle->dbh && $Handle->dbh->ping) {
+ $Handle = RT::Handle->new();
+ }
+ $Handle->Connect();
+}
+
+=head2 InitLogging
+
+Create the RT::Logger object.
+
+=cut
+
+sub InitLogging {
+
+ # We have to set the record separator ($, man perlvar)
+ # or Log::Dispatch starts getting
+ # really pissy, as some other module we use unsets it.
+
+ $, = '';
+ use Log::Dispatch 1.6;
+
+ unless ($RT::Logger) {
+
+ $RT::Logger = Log::Dispatch->new();
+
+ my $simple_cb = sub {
+ # if this code throw any warning we can get segfault
+ no warnings;
+
+ my %p = @_;
+
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
+
+ my ($package, $filename, $line) = caller($frame);
+ $p{message} =~ s/(?:\r*\n)+$//;
+ my $str = "[".gmtime(time)."] [".$p{level}."]: $p{message} ($filename:$line)\n";
+
+ if( $RT::LogStackTraces ) {
+ $str .= "\nStack trace:\n";
+ # skip calling of the Log::* subroutins
+ $frame++ while( caller($frame) && (caller($frame))[3] =~ /^Log::/ );
+ while( my ($package, $filename, $line, $sub) = caller($frame++) ) {
+ $str .= "\t". $sub ."() called at $filename:$line\n";
+ }
+ }
+ return $str;
+ };
+
+ my $syslog_cb = sub {
+ my %p = @_;
+
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
+ my ($package, $filename, $line) = caller($frame);
+
+ # syswrite() cannot take utf8; turn it off here.
+ Encode::_utf8_off($p{message});
+
+ $p{message} =~ s/(?:\r*\n)+$//;
+ if ($p{level} eq 'debug') {
+ return "$p{message}\n"
+ } else {
+ return "$p{message} ($filename:$line)\n"
+ }
+ };
+
+ if ($RT::LogToFile) {
+ my ($filename, $logdir);
+ if ($RT::LogToFileNamed =~ m![/\\]!) {
+ # looks like an absolute path.
+ $filename = $RT::LogToFileNamed;
+ ($logdir) = $RT::LogToFileNamed =~ m!^(.*[/\\])!;
+ }
+ else {
+ $filename = "$RT::LogDir/$RT::LogToFileNamed";
+ $logdir = $RT::LogDir;
+ }
+
+ unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
+ # localizing here would be hard when we don't have a current user yet
+ die "Log file $filename couldn't be written or created.\n RT can't run.";
+ }
+
+ package Log::Dispatch::File;
+ require Log::Dispatch::File;
+ $RT::Logger->add(Log::Dispatch::File->new
+ ( name=>'rtlog',
+ min_level=> $RT::LogToFile,
+ filename=> $filename,
+ mode=>'append',
+ callbacks => $simple_cb,
+ ));
+ }
+ if ($RT::LogToScreen) {
+ package Log::Dispatch::Screen;
+ require Log::Dispatch::Screen;
+ $RT::Logger->add(Log::Dispatch::Screen->new
+ ( name => 'screen',
+ min_level => $RT::LogToScreen,
+ callbacks => $simple_cb,
+ stderr => 1,
+ ));
+ }
+ if ($RT::LogToSyslog) {
+ package Log::Dispatch::Syslog;
+ require Log::Dispatch::Syslog;
+ $RT::Logger->add(Log::Dispatch::Syslog->new
+ ( name => 'syslog',
+ ident => 'RT',
+ min_level => $RT::LogToSyslog,
+ callbacks => $syslog_cb,
+ stderr => 1,
+ @RT::LogToSyslogConf
+ ));
+ }
+
+ }
+
+# {{{ Signal handlers
+
+## This is the default handling of warnings and die'ings in the code
+## (including other used modules - maybe except for errors catched by
+## Mason). It will log all problems through the standard logging
+## mechanism (see above).
+
+ $SIG{__WARN__} = sub {
+ # The 'wide character' warnings has to be silenced for now, at least
+ # until HTML::Mason offers a sane way to process both raw output and
+ # unicode strings.
+ # use 'goto &foo' syntax to hide ANON sub from stack
+ if( index($_[0], 'Wide character in ') != 0 ) {
+ unshift @_, $RT::Logger, qw(level warning message);
+ goto &Log::Dispatch::log;
+ }
+ };
+
+#When we call die, trap it and log->crit with the value of the die.
+
+$SIG{__DIE__} = sub {
+ unless ($^S || !defined $^S ) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("$_[0]");
+ }
+ die $_[0];
+};
+
+# }}}
+
+}
+
+
+sub CheckPerlRequirements {
+ if ($^V < 5.008003) {
+ die sprintf "RT requires Perl v5.8.3 or newer. Your current Perl is v%vd\n", $^V;
+ }
+
+ local ($@);
+ eval {
+ my $x = '';
+ my $y = \$x;
+ require Scalar::Util; Scalar::Util::weaken($y);
+ };
+ if ($@) {
+ die <<"EOF";
+
+RT requires the Scalar::Util module be built with support for the 'weaken'
+function.
+
+It is sometimes the case that operating system upgrades will replace
+a working Scalar::Util with a non-working one. If your system was working
+correctly up until now, this is likely the cause of the problem.
+
+Please reinstall Scalar::Util, being careful to let it build with your C
+compiler. Ususally this is as simple as running the following command as
+root.
+
+ perl -MCPAN -e'install Scalar::Util'
+
+EOF
+
+ }
+}
+
+
+=head2 InitClasses
+
+Load all modules that define base classes
+
+=cut
+
+sub InitClasses {
+ require RT::Tickets;
+ require RT::Transactions;
+ require RT::Users;
+ require RT::CurrentUser;
+ require RT::Templates;
+ require RT::Queues;
+ require RT::ScripActions;
+ require RT::ScripConditions;
+ require RT::Scrips;
+ require RT::Groups;
+ require RT::GroupMembers;
+ require RT::CustomFields;
+ require RT::CustomFieldValues;
+ require RT::ObjectCustomFields;
+ require RT::ObjectCustomFieldValues;
+}
+
+# }}}
+
+
+sub SystemUser {
+ return($SystemUser);
+}
+
+sub Nobody {
+ return ($Nobody);
+}
+
+=head1 BUGS
+
+Please report them to rt-bugs@fsck.com, if you know what's broken and have at least
+some idea of what needs to be fixed.
+
+If you're not sure what's going on, report them rt-devel@lists.bestpractical.com.
+
+=head1 SEE ALSO
+
+L<RT::StyleGuide>
+L<DBIx::SearchBuilder>
+
+=begin testing
+
+ok ($RT::Nobody->Name() eq 'Nobody', "Nobody is nobody");
+ok ($RT::Nobody->Name() ne 'root', "Nobody isn't named root");
+ok ($RT::SystemUser->Name() eq 'RT_System', "The system user is RT_System");
+ok ($RT::SystemUser->Name() ne 'noname', "The system user isn't noname");
+
+=end testing
+
+=cut
+
+eval "require RT_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT_Vendor.pm});
+eval "require RT_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT_Local.pm});
+
+1;
diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm
new file mode 100755
index 0000000..087a7e4
--- /dev/null
+++ b/rt/lib/RT/ACE.pm
@@ -0,0 +1,328 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::ACE
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::ACE;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('ACL');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(25) 'PrincipalType'.
+ int(11) 'PrincipalId'.
+ varchar(25) 'RightName'.
+ varchar(25) 'ObjectType'.
+ int(11) 'ObjectId'.
+ int(11) 'DelegatedBy'.
+ int(11) 'DelegatedFrom'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ PrincipalType => '',
+ PrincipalId => '0',
+ RightName => '',
+ ObjectType => '',
+ ObjectId => '0',
+ DelegatedBy => '0',
+ DelegatedFrom => '0',
+
+ @_);
+ $self->SUPER::Create(
+ PrincipalType => $args{'PrincipalType'},
+ PrincipalId => $args{'PrincipalId'},
+ RightName => $args{'RightName'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+ DelegatedBy => $args{'DelegatedBy'},
+ DelegatedFrom => $args{'DelegatedFrom'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 PrincipalType
+
+Returns the current value of PrincipalType.
+(In the database, PrincipalType is stored as varchar(25).)
+
+
+
+=head2 SetPrincipalType VALUE
+
+
+Set PrincipalType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, PrincipalType will be stored as a varchar(25).)
+
+
+=cut
+
+
+=head2 PrincipalId
+
+Returns the current value of PrincipalId.
+(In the database, PrincipalId is stored as int(11).)
+
+
+
+=head2 SetPrincipalId VALUE
+
+
+Set PrincipalId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, PrincipalId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 RightName
+
+Returns the current value of RightName.
+(In the database, RightName is stored as varchar(25).)
+
+
+
+=head2 SetRightName VALUE
+
+
+Set RightName to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, RightName will be stored as a varchar(25).)
+
+
+=cut
+
+
+=head2 ObjectType
+
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(25).)
+
+
+
+=head2 SetObjectType VALUE
+
+
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(25).)
+
+
+=cut
+
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=head2 SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 DelegatedBy
+
+Returns the current value of DelegatedBy.
+(In the database, DelegatedBy is stored as int(11).)
+
+
+
+=head2 SetDelegatedBy VALUE
+
+
+Set DelegatedBy to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, DelegatedBy will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 DelegatedFrom
+
+Returns the current value of DelegatedFrom.
+(In the database, DelegatedFrom is stored as int(11).)
+
+
+
+=head2 SetDelegatedFrom VALUE
+
+
+Set DelegatedFrom to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, DelegatedFrom will be stored as a int(11).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ PrincipalType =>
+ {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ PrincipalId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ RightName =>
+ {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ ObjectType =>
+ {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ ObjectId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ DelegatedBy =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ DelegatedFrom =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::ACE_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ACE_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ACE_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ACE_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ACE_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ACE_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ACE_Overlay, RT::ACE_Vendor, RT::ACE_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ACE_Overlay.pm b/rt/lib/RT/ACE_Overlay.pm
new file mode 100644
index 0000000..54c6561
--- /dev/null
+++ b/rt/lib/RT/ACE_Overlay.pm
@@ -0,0 +1,958 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 SYNOPSIS
+
+ use RT::ACE;
+ my $ace = new RT::ACE($CurrentUser);
+
+
+=head1 DESCRIPTION
+
+
+
+=head1 METHODS
+
+=begin testing
+
+ok(require RT::ACE);
+
+=end testing
+
+=cut
+
+
+package RT::ACE;
+
+use strict;
+no warnings qw(redefine);
+use RT::Principals;
+use RT::Queues;
+use RT::Groups;
+
+use vars qw (
+ %LOWERCASERIGHTNAMES
+ %OBJECT_TYPES
+ %TICKET_METAPRINCIPALS
+);
+
+
+# {{{ Descriptions of rights
+
+=head1 Rights
+
+# Queue rights are the sort of queue rights that can only be granted
+# to real people or groups
+
+
+=begin testing
+
+my $Queue = RT::Queue->new($RT::SystemUser);
+
+is ($Queue->AvailableRights->{'DeleteTicket'} , 'Delete tickets', "Found the delete ticket right");
+is ($RT::System->AvailableRights->{'SuperUser'}, 'Do anything and everything', "Found the superuser right");
+
+
+=end testing
+
+=cut
+
+
+
+
+# }}}
+
+# {{{ Descriptions of principals
+
+%TICKET_METAPRINCIPALS = (
+ Owner => 'The owner of a ticket', # loc_pair
+ Requestor => 'The requestor of a ticket', # loc_pair
+ Cc => 'The CC of a ticket', # loc_pair
+ AdminCc => 'The administrative CC of a ticket', # loc_pair
+);
+
+# }}}
+
+
+# {{{ sub LoadByValues
+
+=head2 LoadByValues PARAMHASH
+
+Load an ACE by specifying a paramhash with the following fields:
+
+ PrincipalId => undef,
+ PrincipalType => undef,
+ RightName => undef,
+
+ And either:
+
+ Object => undef,
+
+ OR
+
+ ObjectType => undef,
+ ObjectId => undef
+
+=cut
+
+sub LoadByValues {
+ my $self = shift;
+ my %args = ( PrincipalId => undef,
+ PrincipalType => undef,
+ RightName => undef,
+ Object => undef,
+ ObjectId => undef,
+ ObjectType => undef,
+ @_ );
+
+ my $princ_obj;
+ ( $princ_obj, $args{'PrincipalType'} ) =
+ $self->_CanonicalizePrincipal( $args{'PrincipalId'},
+ $args{'PrincipalType'} );
+
+ unless ( $princ_obj->id ) {
+ return ( 0,
+ $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
+ );
+ }
+
+ my ($object, $object_type, $object_id) = $self->_ParseObjectArg( %args );
+ unless( $object ) {
+ return ( 0, $self->loc("System error. Right not granted.") );
+ }
+
+ $self->LoadByCols( PrincipalId => $princ_obj->Id,
+ PrincipalType => $args{'PrincipalType'},
+ RightName => $args{'RightName'},
+ ObjectType => $object_type,
+ ObjectId => $object_id);
+
+ #If we couldn't load it.
+ unless ( $self->Id ) {
+ return ( 0, $self->loc("ACE not found") );
+ }
+
+ # if we could
+ return ( $self->Id, $self->loc("Right Loaded") );
+
+}
+
+# }}}
+
+# {{{ sub Create
+
+=head2 Create <PARAMS>
+
+PARAMS is a parameter hash with the following elements:
+
+ PrincipalId => The id of an RT::Principal object
+ PrincipalType => "User" "Group" or any Role type
+ RightName => the name of a right. in any case
+ DelegatedBy => The Principal->Id of the user delegating the right
+ DelegatedFrom => The id of the ACE which this new ACE is delegated from
+
+
+ Either:
+
+ Object => An object to create rights for. ususally, an RT::Queue or RT::Group
+ This should always be a DBIx::SearchBuilder::Record subclass
+
+ OR
+
+ ObjectType => the type of the object in question (ref ($object))
+ ObjectId => the id of the object in question $object->Id
+
+
+
+ Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's false.
+
+
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = ( PrincipalId => undef,
+ PrincipalType => undef,
+ RightName => undef,
+ Object => undef,
+ @_ );
+ #if we haven't specified any sort of right, we're talking about a global right
+ if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) {
+ $args{'Object'} = $RT::System;
+ }
+ ($args{'Object'}, $args{'ObjectType'}, $args{'ObjectId'}) = $self->_ParseObjectArg( %args );
+ unless( $args{'Object'} ) {
+ return ( 0, $self->loc("System error. Right not granted.") );
+ }
+
+ # {{{ Validate the principal
+ my $princ_obj;
+ ( $princ_obj, $args{'PrincipalType'} ) =
+ $self->_CanonicalizePrincipal( $args{'PrincipalId'},
+ $args{'PrincipalType'} );
+
+ unless ( $princ_obj->id ) {
+ return ( 0,
+ $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
+ );
+ }
+
+ # }}}
+
+ # {{{ Check the ACL
+
+ if (ref( $args{'Object'}) eq 'RT::Group' ) {
+ unless ( $self->CurrentUser->HasRight( Object => $args{'Object'},
+ Right => 'AdminGroup' )
+ ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+
+ else {
+ unless ( $self->CurrentUser->HasRight( Object => $args{'Object'}, Right => 'ModifyACL' )) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+ # }}}
+
+ # {{{ Canonicalize and check the right name
+ unless ( $args{'RightName'} ) {
+ return ( 0, $self->loc('Invalid right') );
+ }
+
+ $args{'RightName'} = $self->CanonicalizeRightName( $args{'RightName'} );
+
+ #check if it's a valid RightName
+ if ( ref ($args{'Object'} eq 'RT::Queue' )) {
+ unless ( exists $args{'Object'}->AvailableRights->{ $args{'RightName'} } ) {
+ $RT::Logger->warning("Couldn't validate right name". $args{'RightName'});
+ return ( 0, $self->loc('Invalid right') );
+ }
+ }
+ elsif ( ref ($args{'Object'} eq 'RT::Group' )) {
+ unless ( exists $args{'Object'}->AvailableRights->{ $args{'RightName'} } ) {
+ $RT::Logger->warning("Couldn't validate group right name". $args{'RightName'});
+ return ( 0, $self->loc('Invalid right') );
+ }
+ }
+ elsif ( ref ($args{'Object'} eq 'RT::System' )) {
+ my $q = RT::Queue->new($self->CurrentUser);
+ my $g = RT::Group->new($self->CurrentUser);
+
+ unless (( exists $g->AvailableRights->{ $args{'RightName'} } )
+ || ( exists $g->AvailableRights->{ $args{'RightName'} } )
+ || ( exists $RT::System->AvailableRights->{ $args{'RightName'} } ) ) {
+ $RT::Logger->warning("Couldn't validate system right name - ". $args{'RightName'});
+ return ( 0, $self->loc('Invalid right') );
+ }
+ }
+
+ # }}}
+
+ # Make sure the right doesn't already exist.
+ $self->LoadByCols( PrincipalId => $princ_obj->id,
+ PrincipalType => $args{'PrincipalType'},
+ RightName => $args{'RightName'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+ DelegatedBy => 0,
+ DelegatedFrom => 0 );
+ if ( $self->Id ) {
+ return ( 0, $self->loc('That principal already has that right') );
+ }
+
+ my $id = $self->SUPER::Create( PrincipalId => $princ_obj->id,
+ PrincipalType => $args{'PrincipalType'},
+ RightName => $args{'RightName'},
+ ObjectType => ref( $args{'Object'} ),
+ ObjectId => $args{'Object'}->id,
+ DelegatedBy => 0,
+ DelegatedFrom => 0 );
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ RT::Principal->InvalidateACLCache();
+
+ if ( $id > 0 ) {
+ return ( $id, $self->loc('Right Granted') );
+ }
+ else {
+ return ( 0, $self->loc('System error. Right not granted.') );
+ }
+}
+
+# }}}
+
+# {{{ sub Delegate
+
+=head2 Delegate <PARAMS>
+
+This routine delegates the current ACE to a principal specified by the
+B<PrincipalId> parameter.
+
+Returns an error if the current user doesn't have the right to be delegated
+or doesn't have the right to delegate rights.
+
+Always returns a tuple of (ReturnValue, Message)
+
+=begin testing
+
+use_ok(RT::User);
+my $user_a = RT::User->new($RT::SystemUser);
+$user_a->Create( Name => 'DelegationA', Privileged => 1);
+ok ($user_a->Id, "Created delegation user a");
+
+my $user_b = RT::User->new($RT::SystemUser);
+$user_b->Create( Name => 'DelegationB', Privileged => 1);
+ok ($user_b->Id, "Created delegation user b");
+
+
+use_ok(RT::Queue);
+my $q = RT::Queue->new($RT::SystemUser);
+$q->Create(Name =>'DelegationTest');
+ok ($q->Id, "Created a delegation test queue");
+
+
+#------ First, we test whether a user can delegate a right that's been granted to him personally
+my ($val, $msg) = $user_a->PrincipalObj->GrantRight(Object => $RT::System, Right => 'AdminOwnPersonalGroups');
+ok($val, $msg);
+
+($val, $msg) = $user_a->PrincipalObj->GrantRight(Object =>$q, Right => 'OwnTicket');
+ok($val, $msg);
+
+ok($user_a->HasRight( Object => $RT::System, Right => 'AdminOwnPersonalGroups') ,"user a has the right 'AdminOwnPersonalGroups' directly");
+
+my $a_delegates = RT::Group->new($user_a);
+$a_delegates->CreatePersonalGroup(Name => 'Delegates');
+ok( $a_delegates->Id ,"user a creates a personal group 'Delegates'");
+ok( $a_delegates->AddMember($user_b->PrincipalId) ,"user a adds user b to personal group 'delegates'");
+
+ok( !$user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to OwnTicket' in queue 'DelegationTest'");
+ok( $user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a has the right to 'OwnTicket' in queue 'DelegationTest'");
+ok(!$user_a->HasRight( Object => $RT::System, Right => 'DelegateRights') ,"user a does not have the right 'delegate rights'");
+
+
+my $own_ticket_ace = RT::ACE->new($user_a);
+my $user_a_equiv_group = RT::Group->new($user_a);
+$user_a_equiv_group->LoadACLEquivalenceGroup($user_a->PrincipalObj);
+ok ($user_a_equiv_group->Id, "Loaded the user A acl equivalence group");
+my $user_b_equiv_group = RT::Group->new($user_b);
+$user_b_equiv_group->LoadACLEquivalenceGroup($user_b->PrincipalObj);
+ok ($user_b_equiv_group->Id, "Loaded the user B acl equivalence group");
+$own_ticket_ace->LoadByValues( PrincipalType => 'Group', PrincipalId => $user_a_equiv_group->PrincipalId, Object=>$q, RightName => 'OwnTicket');
+
+ok ($own_ticket_ace->Id, "Found the ACE we want to test with for now");
+
+
+($val, $msg) = $own_ticket_ace->Delegate(PrincipalId => $a_delegates->PrincipalId) ;
+ok( !$val ,"user a tries and fails to delegate the right 'ownticket' in queue 'DelegationTest' to personal group 'delegates' - $msg");
+
+
+($val, $msg) = $user_a->PrincipalObj->GrantRight( Right => 'DelegateRights');
+ok($val, "user a is granted the right to 'delegate rights' - $msg");
+
+ok($user_a->HasRight( Object => $RT::System, Right => 'DelegateRights') ,"user a has the right 'AdminOwnPersonalGroups' directly");
+
+($val, $msg) = $own_ticket_ace->Delegate(PrincipalId => $a_delegates->PrincipalId) ;
+
+ok( $val ,"user a tries and succeeds to delegate the right 'ownticket' in queue 'DelegationTest' to personal group 'delegates' - $msg");
+ok( $user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b has the right to own tickets in queue 'DelegationTest'");
+my $delegated_ace = RT::ACE->new($user_a);
+$delegated_ace->LoadByValues ( Object => $q, RightName => 'OwnTicket', PrincipalType => 'Group',
+PrincipalId => $a_delegates->PrincipalId, DelegatedBy => $user_a->PrincipalId, DelegatedFrom => $own_ticket_ace->Id);
+ok ($delegated_ace->Id, "Found the delegated ACE");
+
+ok( $a_delegates->DeleteMember($user_b->PrincipalId) ,"user a removes b from pg 'delegates'");
+ok( !$user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to own tickets in queue 'DelegationTest'");
+ok( $a_delegates->AddMember($user_b->PrincipalId) ,"user a adds user b to personal group 'delegates'");
+ok( $user_b->HasRight(Right => 'OwnTicket', Object=> $q) ,"user b has the right to own tickets in queue 'DelegationTest'");
+ok( $delegated_ace->Delete ,"user a revokes pg 'delegates' right to 'OwnTickets' in queue 'DelegationTest'");
+ok( ! $user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to own tickets in queue 'DelegationTest'");
+
+($val, $msg) = $own_ticket_ace->Delegate(PrincipalId => $a_delegates->PrincipalId) ;
+ok( $val ,"user a delegates pg 'delegates' right to 'OwnTickets' in queue 'DelegationTest' - $msg");
+
+ok( $user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a does not have the right to own tickets in queue 'DelegationTest'");
+
+($val, $msg) = $user_a->PrincipalObj->RevokeRight(Object=>$q, Right => 'OwnTicket');
+ok($val, "Revoked user a's right to own tickets in queue 'DelegationTest". $msg);
+
+ok( !$user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a does not have the right to own tickets in queue 'DelegationTest'");
+
+ ok( !$user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to own tickets in queue 'DelegationTest'");
+
+($val, $msg) = $user_a->PrincipalObj->GrantRight(Object=>$q, Right => 'OwnTicket');
+ok($val, $msg);
+
+ ok( $user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a has the right to own tickets in queue 'DelegationTest'");
+
+ ok( !$user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to own tickets in queue 'DelegationTest'");
+
+# {{{ get back to a known clean state
+($val, $msg) = $user_a->PrincipalObj->RevokeRight( Object => $q, Right => 'OwnTicket');
+ok($val, "Revoked user a's right to own tickets in queue 'DelegationTest -". $msg);
+ok( !$user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"make sure that user a can't own tickets in queue 'DelegationTest'");
+# }}}
+
+
+# {{{ Set up some groups and membership
+my $del1 = RT::Group->new($RT::SystemUser);
+($val, $msg) = $del1->CreateUserDefinedGroup(Name => 'Del1');
+ok( $val ,"create a group del1 - $msg");
+
+my $del2 = RT::Group->new($RT::SystemUser);
+($val, $msg) = $del2->CreateUserDefinedGroup(Name => 'Del2');
+ok( $val ,"create a group del2 - $msg");
+($val, $msg) = $del1->AddMember($del2->PrincipalId);
+ok( $val,"make del2 a member of del1 - $msg");
+
+my $del2a = RT::Group->new($RT::SystemUser);
+($val, $msg) = $del2a->CreateUserDefinedGroup(Name => 'Del2a');
+ok( $val ,"create a group del2a - $msg");
+($val, $msg) = $del2->AddMember($del2a->PrincipalId);
+ok($val ,"make del2a a member of del2 - $msg");
+
+my $del2b = RT::Group->new($RT::SystemUser);
+($val, $msg) = $del2b->CreateUserDefinedGroup(Name => 'Del2b');
+ok( $val ,"create a group del2b - $msg");
+($val, $msg) = $del2->AddMember($del2b->PrincipalId);
+ok($val ,"make del2b a member of del2 - $msg");
+
+($val, $msg) = $del2->AddMember($user_a->PrincipalId) ;
+ok($val,"make 'user a' a member of del2 - $msg");
+
+($val, $msg) = $del2b->AddMember($user_a->PrincipalId) ;
+ok($val,"make 'user a' a member of del2b - $msg");
+
+# }}}
+
+# {{{ Grant a right to a group and make sure that a submember can delegate the right and that it does not get yanked
+# when a user is removed as a submember, when they're a sumember through another path
+($val, $msg) = $del1->PrincipalObj->GrantRight( Object=> $q, Right => 'OwnTicket');
+ok( $val ,"grant del1 the right to 'OwnTicket' in queue 'DelegationTest' - $msg");
+
+ok( $user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"make sure that user a can own tickets in queue 'DelegationTest'");
+
+my $group_ace= RT::ACE->new($user_a);
+$group_ace->LoadByValues( PrincipalType => 'Group', PrincipalId => $del1->PrincipalId, Object => $q, RightName => 'OwnTicket');
+
+ok ($group_ace->Id, "Found the ACE we want to test with for now");
+
+($val, $msg) = $group_ace->Delegate(PrincipalId => $a_delegates->PrincipalId);
+
+ok( $val ,"user a tries and succeeds to delegate the right 'ownticket' in queue 'DelegationTest' to personal group 'delegates' - $msg");
+ok( $user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b has the right to own tickets in queue 'DelegationTest'");
+
+
+($val, $msg) = $del2b->DeleteMember($user_a->PrincipalId);
+ok( $val ,"remove user a from group del2b - $msg");
+ok( $user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a has the right to own tickets in queue 'DelegationTest'");
+ok( $user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b has the right to own tickets in queue 'DelegationTest'");
+
+# }}}
+
+# {{{ When a user is removed froom a group by the only path they're in there by, make sure the delegations go away
+($val, $msg) = $del2->DeleteMember($user_a->PrincipalId);
+ok( $val ,"remove user a from group del2 - $msg");
+ok( !$user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a does not have the right to own tickets in queue 'DelegationTest' ");
+ok( !$user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to own tickets in queue 'DelegationTest' ");
+# }}}
+
+($val, $msg) = $del2->AddMember($user_a->PrincipalId);
+ok( $val ,"make user a a member of group del2 - $msg");
+
+($val, $msg) = $del2->PrincipalObj->GrantRight(Object=>$q, Right => 'OwnTicket');
+ok($val, "grant the right 'own tickets' in queue 'DelegationTest' to group del2 - $msg");
+
+my $del2_right = RT::ACE->new($user_a);
+$del2_right->LoadByValues( PrincipalId => $del2->PrincipalId, PrincipalType => 'Group', Object => $q, RightName => 'OwnTicket');
+ok ($del2_right->Id, "Found the right");
+
+($val, $msg) = $del2_right->Delegate(PrincipalId => $a_delegates->PrincipalId);
+ok( $val ,"user a tries and succeeds to delegate the right 'ownticket' in queue 'DelegationTest' gotten via del2 to personal group 'delegates' - $msg");
+
+# They have it via del1 and del2
+ok( $user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user b has the right to own tickets in queue 'DelegationTest'");
+
+
+($val, $msg) = $del2->PrincipalObj->RevokeRight(Object=>$q, Right => 'OwnTicket');
+ok($val, "revoke the right 'own tickets' in queue 'DelegationTest' to group del2 - $msg");
+ok( $user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a does has the right to own tickets in queue 'DelegationTest' via del1");
+ok( !$user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to own tickets in queue 'DelegationTest'");
+
+($val, $msg) = $del2->PrincipalObj->GrantRight(Object=>$q, Right => 'OwnTicket');
+ok($val, "grant the right 'own tickets' in queue 'DelegationTest' to group del2 - $msg");
+
+
+$group_ace= RT::ACE->new($user_a);
+$group_ace->LoadByValues( PrincipalType => 'Group', PrincipalId => $del1->PrincipalId, Object=>$q, RightName => 'OwnTicket');
+
+ok ($group_ace->Id, "Found the ACE we want to test with for now");
+
+($val, $msg) = $group_ace->Delegate(PrincipalId => $a_delegates->PrincipalId);
+
+ok( $val ,"user a tries and succeeds to delegate the right 'ownticket' in queue 'DelegationTest' to personal group 'delegates' - $msg");
+
+ok( $user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b has the right to own tickets in queue 'DelegationTest'");
+
+($val, $msg) = $del2->DeleteMember($user_a->PrincipalId);
+ok( $val ,"remove user a from group del2 - $msg");
+
+ok( !$user_a->HasRight(Right => 'OwnTicket', Object => $q) ,"user a does not have the right to own tickets in queue 'DelegationTest'");
+
+ok( !$user_b->HasRight(Right => 'OwnTicket', Object => $q) ,"user b does not have the right to own tickets in queue 'DelegationTest'");
+
+
+
+=end testing
+
+=cut
+
+sub Delegate {
+ my $self = shift;
+ my %args = ( PrincipalId => undef,
+ @_ );
+
+ unless ( $self->Id ) {
+ return ( 0, $self->loc("Right not loaded.") );
+ }
+ my $princ_obj;
+ ( $princ_obj, $args{'PrincipalType'} ) =
+ $self->_CanonicalizePrincipal( $args{'PrincipalId'},
+ $args{'PrincipalType'} );
+
+ unless ( $princ_obj->id ) {
+ return ( 0,
+ $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
+ );
+ }
+
+ # }}}
+
+ # {{{ Check the ACL
+
+ # First, we check to se if the user is delegating rights and
+ # they have the permission to
+ unless ( $self->CurrentUser->HasRight(Right => 'DelegateRights', Object => $self->Object) ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ unless ( $self->PrincipalObj->IsGroup ) {
+ return ( 0, $self->loc("System Error") );
+ }
+ unless ( $self->PrincipalObj->Object->HasMemberRecursively(
+ $self->CurrentUser->PrincipalObj
+ )
+ ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ # }}}
+
+ my $concurrency_check = RT::ACE->new($RT::SystemUser);
+ $concurrency_check->Load( $self->Id );
+ unless ( $concurrency_check->Id ) {
+ $RT::Logger->crit(
+ "Trying to delegate a right which had already been deleted");
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ my $delegated_ace = RT::ACE->new( $self->CurrentUser );
+
+ # Make sure the right doesn't already exist.
+ $delegated_ace->LoadByCols( PrincipalId => $princ_obj->Id,
+ PrincipalType => 'Group',
+ RightName => $self->__Value('RightName'),
+ ObjectType => $self->__Value('ObjectType'),
+ ObjectId => $self->__Value('ObjectId'),
+ DelegatedBy => $self->CurrentUser->PrincipalId,
+ DelegatedFrom => $self->id );
+ if ( $delegated_ace->Id ) {
+ return ( 0, $self->loc('That principal already has that right') );
+ }
+ my $id = $delegated_ace->SUPER::Create(
+ PrincipalId => $princ_obj->Id,
+ PrincipalType => 'Group', # do we want to hardcode this?
+ RightName => $self->__Value('RightName'),
+ ObjectType => $self->__Value('ObjectType'),
+ ObjectId => $self->__Value('ObjectId'),
+ DelegatedBy => $self->CurrentUser->PrincipalId,
+ DelegatedFrom => $self->id );
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+
+ if ( $id > 0 ) {
+ return ( $id, $self->loc('Right Delegated') );
+ }
+ else {
+ return ( 0, $self->loc('System error. Right not delegated.') );
+ }
+}
+
+# }}}
+
+# {{{ sub Delete
+
+=head2 Delete { InsideTransaction => undef}
+
+Delete this object. This method should ONLY ever be called from RT::User or RT::Group (or from itself)
+If this is being called from within a transaction, specify a true value for the parameter InsideTransaction.
+Really, DBIx::SearchBuilder should use and/or fake subtransactions
+
+This routine will also recurse and delete any delegations of this right
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+ unless ( $self->Id ) {
+ return ( 0, $self->loc('Right not loaded.') );
+ }
+
+ # A user can delete an ACE if the current user has the right to modify it and it's not a delegated ACE
+ # or if it's a delegated ACE and it was delegated by the current user
+ unless (
+ ( $self->CurrentUser->HasRight(Right => 'ModifyACL', Object => $self->Object)
+ && $self->__Value('DelegatedBy') == 0 )
+ || ( $self->__Value('DelegatedBy') == $self->CurrentUser->PrincipalId )
+ ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $self->_Delete(@_);
+}
+
+# Helper for Delete with no ACL check
+sub _Delete {
+ my $self = shift;
+ my %args = ( InsideTransaction => undef,
+ @_ );
+
+ my $InsideTransaction = $args{'InsideTransaction'};
+
+ $RT::Handle->BeginTransaction() unless $InsideTransaction;
+
+ my $delegated_from_this = RT::ACL->new($RT::SystemUser);
+ $delegated_from_this->Limit( FIELD => 'DelegatedFrom',
+ OPERATOR => '=',
+ VALUE => $self->Id );
+
+ my $delete_succeeded = 1;
+ my $submsg;
+ while ( my $delegated_ace = $delegated_from_this->Next ) {
+ ( $delete_succeeded, $submsg ) =
+ $delegated_ace->_Delete( InsideTransaction => 1 );
+ last unless ($delete_succeeded);
+ }
+
+ unless ($delete_succeeded) {
+ $RT::Handle->Rollback() unless $InsideTransaction;
+ return ( 0, $self->loc('Right could not be revoked') );
+ }
+
+ my ( $val, $msg ) = $self->SUPER::Delete(@_);
+
+ # If we're revoking delegation rights (see above), we may need to
+ # revoke all rights delegated by the recipient.
+ if ($val and ($self->RightName() eq 'DelegateRights' or
+ $self->RightName() eq 'SuperUser')) {
+ $val = $self->PrincipalObj->_CleanupInvalidDelegations( InsideTransaction => 1 );
+ }
+
+ if ($val) {
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+ $RT::Handle->Commit() unless $InsideTransaction;
+ return ( $val, $self->loc('Right revoked') );
+ }
+
+ $RT::Handle->Rollback() unless $InsideTransaction;
+ return ( 0, $self->loc('Right could not be revoked') );
+}
+
+# }}}
+
+# {{{ sub _BootstrapCreate
+
+=head2 _BootstrapCreate
+
+Grant a right with no error checking and no ACL. this is _only_ for
+installation. If you use this routine without the author's explicit
+written approval, he will hunt you down and make you spend eternity
+translating mozilla's code into FORTRAN or intercal.
+
+If you think you need this routine, you've mistaken.
+
+=cut
+
+sub _BootstrapCreate {
+ my $self = shift;
+ my %args = (@_);
+
+ # When bootstrapping, make sure we get the _right_ users
+ if ( $args{'UserId'} ) {
+ my $user = RT::User->new( $self->CurrentUser );
+ $user->Load( $args{'UserId'} );
+ delete $args{'UserId'};
+ $args{'PrincipalId'} = $user->PrincipalId;
+ $args{'PrincipalType'} = 'User';
+ }
+
+ my $id = $self->SUPER::Create(%args);
+
+ if ( $id > 0 ) {
+ return ($id);
+ }
+ else {
+ $RT::Logger->err('System error. right not granted.');
+ return (undef);
+ }
+
+}
+
+# }}}
+
+# {{{ sub CanonicalizeRightName
+
+=head2 CanonicalizeRightName <RIGHT>
+
+Takes a queue or system right name in any case and returns it in
+the correct case. If it's not found, will return undef.
+
+=cut
+
+sub CanonicalizeRightName {
+ my $self = shift;
+ my $right = shift;
+ $right = lc $right;
+ if ( exists $LOWERCASERIGHTNAMES{"$right"} ) {
+ return ( $LOWERCASERIGHTNAMES{"$right"} );
+ }
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
+
+# {{{ sub Object
+
+=head2 Object
+
+If the object this ACE applies to is a queue, returns the queue object.
+If the object this ACE applies to is a group, returns the group object.
+If it's the system object, returns undef.
+
+If the user has no rights, returns undef.
+
+=cut
+
+
+
+
+sub Object {
+ my $self = shift;
+
+ my $appliesto_obj;
+
+ if ($self->__Value('ObjectType') && $OBJECT_TYPES{$self->__Value('ObjectType')} ) {
+ $appliesto_obj = $self->__Value('ObjectType')->new($self->CurrentUser);
+ unless (ref( $appliesto_obj) eq $self->__Value('ObjectType')) {
+ return undef;
+ }
+ $appliesto_obj->Load( $self->__Value('ObjectId') );
+ return ($appliesto_obj);
+ }
+ else {
+ $RT::Logger->warning( "$self -> Object called for an object "
+ . "of an unknown type:"
+ . $self->__Value('ObjectType') );
+ return (undef);
+ }
+}
+
+# }}}
+
+# {{{ sub PrincipalObj
+
+=head2 PrincipalObj
+
+Returns the RT::Principal object for this ACE.
+
+=cut
+
+sub PrincipalObj {
+ my $self = shift;
+
+ my $princ_obj = RT::Principal->new( $self->CurrentUser );
+ $princ_obj->Load( $self->__Value('PrincipalId') );
+
+ unless ( $princ_obj->Id ) {
+ $RT::Logger->err(
+ "ACE " . $self->Id . " couldn't load its principal object" );
+ }
+ return ($princ_obj);
+
+}
+
+# }}}
+
+# {{{ ACL related methods
+
+# {{{ sub _Set
+
+sub _Set {
+ my $self = shift;
+ return ( 0, $self->loc("ACEs can only be created and deleted.") );
+}
+
+# }}}
+
+# {{{ sub _Value
+
+sub _Value {
+ my $self = shift;
+
+ if ( $self->__Value('DelegatedBy') eq $self->CurrentUser->PrincipalId ) {
+ return ( $self->__Value(@_) );
+ }
+ elsif ( $self->PrincipalObj->IsGroup
+ && $self->PrincipalObj->Object->HasMemberRecursively(
+ $self->CurrentUser->PrincipalObj
+ )
+ ) {
+ return ( $self->__Value(@_) );
+ }
+ elsif ( $self->CurrentUser->HasRight(Right => 'ShowACL', Object => $self->Object) ) {
+ return ( $self->__Value(@_) );
+ }
+ else {
+ return undef;
+ }
+}
+
+# }}}
+
+
+# }}}
+
+# {{{ _CanonicalizePrincipal
+
+=head2 _CanonicalizePrincipal (PrincipalId, PrincipalType)
+
+Takes a principal id and a principal type.
+
+If the principal is a user, resolves it to the proper acl equivalence group.
+Returns a tuple of (RT::Principal, PrincipalType) for the principal we really want to work with
+
+=cut
+
+sub _CanonicalizePrincipal {
+ my $self = shift;
+ my $princ_id = shift;
+ my $princ_type = shift;
+
+ my $princ_obj = RT::Principal->new($RT::SystemUser);
+ $princ_obj->Load($princ_id);
+
+ unless ( $princ_obj->Id ) {
+ use Carp;
+ $RT::Logger->crit(Carp::cluck);
+ $RT::Logger->crit("Can't load a principal for id $princ_id");
+ return ( $princ_obj, undef );
+ }
+
+ # Rights never get granted to users. they get granted to their
+ # ACL equivalence groups
+ if ( $princ_type eq 'User' ) {
+ my $equiv_group = RT::Group->new( $self->CurrentUser );
+ $equiv_group->LoadACLEquivalenceGroup($princ_obj);
+ unless ( $equiv_group->Id ) {
+ $RT::Logger->crit( "No ACL equiv group for princ " . $princ_obj->id );
+ return ( RT::Principal->new($RT::SystemUser), undef );
+ }
+ $princ_obj = $equiv_group->PrincipalObj();
+ $princ_type = 'Group';
+
+ }
+ return ( $princ_obj, $princ_type );
+}
+
+sub _ParseObjectArg {
+ my $self = shift;
+ my %args = ( Object => undef,
+ ObjectId => undef,
+ ObjectType => undef,
+ @_ );
+
+ if( $args{'Object'} && ($args{'ObjectId'} || $args{'ObjectType'}) ) {
+ $RT::Logger->crit( "Method called with an ObjectType or an ObjectId and Object args" );
+ return ();
+ } elsif( $args{'Object'} && !UNIVERSAL::can($args{'Object'},'id') ) {
+ $RT::Logger->crit( "Method called called Object that has no id method" );
+ return ();
+ } elsif( $args{'Object'} ) {
+ my $obj = $args{'Object'};
+ return ($obj, ref $obj, $obj->id);
+ } elsif ( $args{'ObjectType'} ) {
+ my $obj = $args{'ObjectType'}->new( $self->CurrentUser );
+ $obj->Load( $args{'ObjectId'} );
+ return ($obj, ref $obj, $obj->id);
+ } else {
+ $RT::Logger->crit( "Method called with wrong args" );
+ return ();
+ }
+}
+
+
+# }}}
+1;
diff --git a/rt/lib/RT/ACL.pm b/rt/lib/RT/ACL.pm
new file mode 100755
index 0000000..a85d8c9
--- /dev/null
+++ b/rt/lib/RT/ACL.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::ACL -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::ACL
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::ACL;
+
+use RT::SearchBuilder;
+use RT::ACE;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'ACL';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::ACE item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::ACE->new($self->CurrentUser));
+}
+
+ eval "require RT::ACL_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ACL_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ACL_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ACL_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ACL_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ACL_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ACL_Overlay, RT::ACL_Vendor, RT::ACL_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ACL_Overlay.pm b/rt/lib/RT/ACL_Overlay.pm
new file mode 100644
index 0000000..09e10cc
--- /dev/null
+++ b/rt/lib/RT/ACL_Overlay.pm
@@ -0,0 +1,373 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::ACL - collection of RT ACE objects
+
+=head1 SYNOPSIS
+
+ use RT::ACL;
+my $ACL = new RT::ACL($CurrentUser);
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok(require RT::ACL);
+
+=end testing
+
+=cut
+
+
+package RT::ACL;
+
+use strict;
+no warnings qw(redefine);
+
+
+=head2 Next
+
+Hand out the next ACE that was found
+
+=cut
+
+
+# {{{ LimitToObject
+
+=head2 LimitToObject $object
+
+Limit the ACL to rights for the object $object. It needs to be an RT::Record class.
+
+=cut
+
+sub LimitToObject {
+ my $self = shift;
+ my $obj = shift;
+ unless ( defined($obj)
+ && ref($obj)
+ && UNIVERSAL::can( $obj, 'id' )
+ && $obj->id )
+ {
+ return undef;
+ }
+ $self->Limit(
+ FIELD => 'ObjectType',
+ OPERATOR => '=',
+ VALUE => ref($obj),
+ ENTRYAGGREGATOR => 'OR'
+ );
+ $self->Limit(
+ FIELD => 'ObjectId',
+ OPERATOR => '=',
+ VALUE => $obj->id,
+ ENTRYAGGREGATOR => 'OR',
+ QUOTEVALUE => 0
+ );
+
+}
+
+# }}}
+
+# {{{ LimitNotObject
+
+=head2 LimitNotObject $object
+
+Limit the ACL to rights NOT on the object $object. $object needs to be
+an RT::Record class.
+
+=cut
+
+sub LimitNotObject {
+ my $self = shift;
+ my $obj = shift;
+ unless ( defined($obj)
+ && ref($obj)
+ && UNIVERSAL::can( $obj, 'id' )
+ && $obj->id )
+ {
+ return undef;
+ }
+ $self->Limit( FIELD => 'ObjectType',
+ OPERATOR => '!=',
+ VALUE => ref($obj),
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $obj->id
+ );
+ $self->Limit( FIELD => 'ObjectId',
+ OPERATOR => '!=',
+ VALUE => $obj->id,
+ ENTRYAGGREGATOR => 'OR',
+ QUOTEVALUE => 0,
+ SUBCLAUSE => $obj->id
+ );
+}
+
+# }}}
+
+# {{{ LimitToPrincipal
+
+=head2 LimitToPrincipal { Type => undef, Id => undef, IncludeGroupMembership => undef }
+
+Limit the ACL to the principal with PrincipalId Id and PrincipalType Type
+
+Id is not optional.
+Type is.
+
+if IncludeGroupMembership => 1 is specified, ACEs which apply to the principal due to group membership will be included in the resultset.
+
+
+=cut
+
+sub LimitToPrincipal {
+ my $self = shift;
+ my %args = ( Type => undef,
+ Id => undef,
+ IncludeGroupMembership => undef,
+ @_ );
+ if ( $args{'IncludeGroupMembership'} ) {
+ my $cgm = $self->NewAlias('CachedGroupMembers');
+ $self->Join( ALIAS1 => 'main',
+ FIELD1 => 'PrincipalId',
+ ALIAS2 => $cgm,
+ FIELD2 => 'GroupId' );
+ $self->Limit( ALIAS => $cgm,
+ FIELD => 'MemberId',
+ OPERATOR => '=',
+ VALUE => $args{'Id'},
+ ENTRYAGGREGATOR => 'OR' );
+ }
+ else {
+ if ( defined $args{'Type'} ) {
+ $self->Limit( FIELD => 'PrincipalType',
+ OPERATOR => '=',
+ VALUE => $args{'Type'},
+ ENTRYAGGREGATOR => 'OR' );
+ }
+ # if the principal id points to a user, we really want to point
+ # to their ACL equivalence group. The machinations we're going through
+ # lead me to start to suspect that we really want users and groups
+ # to just be the same table. or _maybe_ that we want an object db.
+ my $princ = RT::Principal->new($RT::SystemUser);
+ $princ->Load($args{'Id'});
+ if ($princ->PrincipalType eq 'User') {
+ my $group = RT::Group->new($RT::SystemUser);
+ $group->LoadACLEquivalenceGroup($princ);
+ $args{'Id'} = $group->PrincipalId;
+ }
+ $self->Limit( FIELD => 'PrincipalId',
+ OPERATOR => '=',
+ VALUE => $args{'Id'},
+ ENTRYAGGREGATOR => 'OR' );
+ }
+}
+
+# }}}
+
+
+
+# {{{ ExcludeDelegatedRights
+
+=head2 ExcludeDelegatedRights
+
+Don't list rights which have been delegated.
+
+=cut
+
+sub ExcludeDelegatedRights {
+ my $self = shift;
+ $self->DelegatedBy(Id => 0);
+ $self->DelegatedFrom(Id => 0);
+}
+# }}}
+
+# {{{ DelegatedBy
+
+=head2 DelegatedBy { Id => undef }
+
+Limit the ACL to rights delegated by the principal whose Principal Id is
+B<Id>
+
+Id is not optional.
+
+=cut
+
+sub DelegatedBy {
+ my $self = shift;
+ my %args = (
+ Id => undef,
+ @_
+ );
+ $self->Limit(
+ FIELD => 'DelegatedBy',
+ OPERATOR => '=',
+ VALUE => $args{'Id'},
+ ENTRYAGGREGATOR => 'OR'
+ );
+
+}
+
+# }}}
+
+# {{{ DelegatedFrom
+
+=head2 DelegatedFrom { Id => undef }
+
+Limit the ACL to rights delegate from the ACE which has the Id specified
+by the Id parameter.
+
+Id is not optional.
+
+=cut
+
+sub DelegatedFrom {
+ my $self = shift;
+ my %args = (
+ Id => undef,
+ @_);
+ $self->Limit(FIELD => 'DelegatedFrom', OPERATOR=> '=', VALUE => $args{'Id'}, ENTRYAGGREGATOR => 'OR');
+
+}
+
+# }}}
+
+
+# {{{ sub Next
+sub Next {
+ my $self = shift;
+
+ my $ACE = $self->SUPER::Next();
+ if ( ( defined($ACE) ) and ( ref($ACE) ) ) {
+
+ if ( $self->CurrentUser->HasRight( Right => 'ShowACL',
+ Object => $ACE->Object )
+ or $self->CurrentUser->HasRight( Right => 'ModifyACL',
+ Object => $ACE->Object )
+ ) {
+ return ($ACE);
+ }
+
+ #If the user doesn't have the right to show this ACE
+ else {
+ return ( $self->Next() );
+ }
+ }
+
+ #if there never was any ACE
+ else {
+ return (undef);
+ }
+
+}
+
+# }}}
+
+
+
+#wrap around _DoSearch so that we can build the hash of returned
+#values
+sub _DoSearch {
+ my $self = shift;
+ # $RT::Logger->debug("Now in ".$self."->_DoSearch");
+ my $return = $self->SUPER::_DoSearch(@_);
+ # $RT::Logger->debug("In $self ->_DoSearch. return from SUPER::_DoSearch was $return\n");
+ $self->_BuildHash();
+ return ($return);
+}
+
+
+#Build a hash of this ACL's entries.
+sub _BuildHash {
+ my $self = shift;
+
+ while (my $entry = $self->Next) {
+ my $hashkey = $entry->ObjectType . "-" . $entry->ObjectId . "-" . $entry->RightName . "-" . $entry->PrincipalId . "-" . $entry->PrincipalType;
+
+ $self->{'as_hash'}->{"$hashkey"} =1;
+
+ }
+}
+
+
+# {{{ HasEntry
+
+=head2 HasEntry
+
+=cut
+
+sub HasEntry {
+
+ my $self = shift;
+ my %args = ( RightScope => undef,
+ RightAppliesTo => undef,
+ RightName => undef,
+ PrincipalId => undef,
+ PrincipalType => undef,
+ @_ );
+
+ #if we haven't done the search yet, do it now.
+ $self->_DoSearch();
+
+ if ($self->{'as_hash'}->{ $args{'RightScope'} . "-" .
+ $args{'RightAppliesTo'} . "-" .
+ $args{'RightName'} . "-" .
+ $args{'PrincipalId'} . "-" .
+ $args{'PrincipalType'}
+ } == 1) {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+# }}}
+1;
diff --git a/rt/lib/RT/Action/AutoOpen.pm b/rt/lib/RT/Action/AutoOpen.pm
new file mode 100644
index 0000000..54b5ab4
--- /dev/null
+++ b/rt/lib/RT/Action/AutoOpen.pm
@@ -0,0 +1,109 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+# This Action will open the BASE if a dependent is resolved.
+
+package RT::Action::AutoOpen;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA=qw(RT::Action::Generic);
+
+#Do what we need to do and send it out.
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return (ref $self );
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+
+ # if the ticket is already open or the ticket is new and the message is more mail from the
+ # requestor, don't reopen it.
+
+ my $status = $self->TicketObj->Status;
+ return undef if $status eq 'open';
+ return undef if $status eq 'new' && $self->TransactionObj->IsInbound;
+
+ if ( my $msg = $self->TransactionObj->Message->First ) {
+ return undef if ($msg->GetHeader('RT-Control') || '') =~ /\bno-autoopen\b/i;
+ }
+
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+ my $oldstatus = $self->TicketObj->Status();
+ $self->TicketObj->__Set( Field => 'Status', Value => 'open' );
+ $self->TicketObj->_NewTransaction(
+ Type => 'Status',
+ Field => 'Status',
+ OldValue => $oldstatus,
+ NewValue => 'open',
+ Data => 'Ticket auto-opened on incoming correspondence'
+ );
+
+
+ return(1);
+}
+
+eval "require RT::Action::AutoOpen_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/AutoOpen_Vendor.pm});
+eval "require RT::Action::AutoOpen_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/AutoOpen_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm
new file mode 100755
index 0000000..37dda00
--- /dev/null
+++ b/rt/lib/RT/Action/Autoreply.pm
@@ -0,0 +1,140 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Action::Autoreply;
+require RT::Action::SendEmail;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::SendEmail);
+
+=head2 Prepare
+
+Set up the relevant recipients, then call our parent.
+
+=cut
+
+
+sub Prepare {
+ my $self = shift;
+ $self->SetRecipients();
+ $self->SUPER::Prepare();
+}
+
+# {{{ sub SetRecipients
+
+=head2 SetRecipients
+
+Sets the recipients of this message to this ticket's Requestor.
+
+=cut
+
+
+sub SetRecipients {
+ my $self=shift;
+
+ push(@{$self->{'To'}}, $self->TicketObj->Requestors->MemberEmailAddresses);
+
+ return(1);
+}
+
+# }}}
+
+
+# {{{ sub SetReturnAddress
+
+=head2 SetReturnAddress
+
+Set this message\'s return address to the apropriate queue address
+
+=cut
+
+sub SetReturnAddress {
+ my $self = shift;
+ my %args = ( is_comment => 0,
+ @_
+ );
+
+ my $replyto;
+ if ($args{'is_comment'}) {
+ $replyto = $self->TicketObj->QueueObj->CommentAddress ||
+ $RT::CommentAddress;
+ }
+ else {
+ $replyto = $self->TicketObj->QueueObj->CorrespondAddress ||
+ $RT::CorrespondAddress;
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('From')) {
+ if ($RT::UseFriendlyFromLine) {
+ my $friendly_name = $self->TicketObj->QueueObj->Description ||
+ $self->TicketObj->QueueObj->Name;
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader( 'From',
+ sprintf($RT::FriendlyFromLineFormat,
+ $self->MIMEEncodeString( $friendly_name, $RT::EmailOutputEncoding ), $replyto),
+ );
+ }
+ else {
+ $self->SetHeader( 'From', $replyto );
+ }
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('Reply-To')) {
+ $self->SetHeader('Reply-To', "$replyto");
+ }
+
+}
+
+# }}}
+
+eval "require RT::Action::Autoreply_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Autoreply_Vendor.pm});
+eval "require RT::Action::Autoreply_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Autoreply_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/CreateTickets.pm b/rt/lib/RT/Action/CreateTickets.pm
new file mode 100644
index 0000000..4e72e11
--- /dev/null
+++ b/rt/lib/RT/Action/CreateTickets.pm
@@ -0,0 +1,1476 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Action::CreateTickets;
+require RT::Action::Generic;
+
+use strict;
+use warnings;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
+
+use MIME::Entity;
+
+=head1 NAME
+
+ RT::Action::CreateTickets
+
+Create one or more tickets according to an externally supplied template.
+
+
+=head1 SYNOPSIS
+
+ ===Create-Ticket codereview
+ Subject: Code review for {$Tickets{'TOP'}->Subject}
+ Depended-On-By: TOP
+ Content: Someone has created a ticket. you should review and approve it,
+ so they can finish their work
+ ENDOFCONTENT
+
+=head1 DESCRIPTION
+
+
+Using the "CreateTickets" ScripAction and mandatory dependencies, RT now has
+the ability to model complex workflow. When a ticket is created in a queue
+that has a "CreateTickets" scripaction, that ScripAction parses its "Template"
+
+
+
+=head2 FORMAT
+
+CreateTickets uses the template as a template for an ordered set of tickets
+to create. The basic format is as follows:
+
+
+ ===Create-Ticket: identifier
+ Param: Value
+ Param2: Value
+ Param3: Value
+ Content: Blah
+ blah
+ blah
+ ENDOFCONTENT
+ ===Create-Ticket: id2
+ Param: Value
+ Content: Blah
+ ENDOFCONTENT
+
+
+Each ===Create-Ticket: section is evaluated as its own
+Text::Template object, which means that you can embed snippets
+of perl inside the Text::Template using {} delimiters, but that
+such sections absolutely can not span a ===Create-Ticket boundary.
+
+After each ticket is created, it's stuffed into a hash called %Tickets
+so as to be available during the creation of other tickets during the same
+ScripAction. The hash is prepopulated with the ticket which triggered the
+ScripAction as $Tickets{'TOP'}; you can also access that ticket using the
+shorthand TOP.
+
+A simple example:
+
+ ===Create-Ticket: codereview
+ Subject: Code review for {$Tickets{'TOP'}->Subject}
+ Depended-On-By: TOP
+ Content: Someone has created a ticket. you should review and approve it,
+ so they can finish their work
+ ENDOFCONTENT
+
+
+
+A convoluted example
+
+ ===Create-Ticket: approval
+ { # Find out who the administrators of the group called "HR"
+ # of which the creator of this ticket is a member
+ my $name = "HR";
+
+ my $groups = RT::Groups->new($RT::SystemUser);
+ $groups->LimitToUserDefinedGroups();
+ $groups->Limit(FIELD => "Name", OPERATOR => "=", VALUE => "$name");
+ $groups->WithMember($TransactionObj->CreatorObj->Id);
+
+ my $groupid = $groups->First->Id;
+
+ my $adminccs = RT::Users->new($RT::SystemUser);
+ $adminccs->WhoHaveRight(
+ Right => "AdminGroup",
+ Object =>$groups->First,
+ IncludeSystemRights => undef,
+ IncludeSuperusers => 0,
+ IncludeSubgroupMembers => 0,
+ );
+
+ my @admins;
+ while (my $admin = $adminccs->Next) {
+ push (@admins, $admin->EmailAddress);
+ }
+ }
+ Queue: ___Approvals
+ Type: approval
+ AdminCc: {join ("\nAdminCc: ",@admins) }
+ Depended-On-By: TOP
+ Refers-To: TOP
+ Subject: Approval for ticket: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject}
+ Due: {time + 86400}
+ Content-Type: text/plain
+ Content: Your approval is requested for the ticket {$Tickets{"TOP"}->Id}: {$Tickets{"TOP"}->Subject}
+ Blah
+ Blah
+ ENDOFCONTENT
+ ===Create-Ticket: two
+ Subject: Manager approval
+ Depended-On-By: TOP
+ Refers-On: {$Tickets{"approval"}->Id}
+ Queue: ___Approvals
+ Content-Type: text/plain
+ Content:
+ Your approval is requred for this ticket, too.
+ ENDOFCONTENT
+
+=head2 Acceptable fields
+
+A complete list of acceptable fields for this beastie:
+
+
+ * Queue => Name or id# of a queue
+ Subject => A text string
+ ! Status => A valid status. defaults to 'new'
+ Due => Dates can be specified in seconds since the epoch
+ to be handled literally or in a semi-free textual
+ format which RT will attempt to parse.
+
+
+
+ Starts =>
+ Started =>
+ Resolved =>
+ Owner => Username or id of an RT user who can and should own
+ this ticket; forces the owner if necessary
+ + Requestor => Email address
+ + Cc => Email address
+ + AdminCc => Email address
+ TimeWorked =>
+ TimeEstimated =>
+ TimeLeft =>
+ InitialPriority =>
+ FinalPriority =>
+ Type =>
+ +! DependsOn =>
+ +! DependedOnBy =>
+ +! RefersTo =>
+ +! ReferredToBy =>
+ +! Members =>
+ +! MemberOf =>
+ Content => content. Can extend to multiple lines. Everything
+ within a template after a Content: header is treated
+ as content until we hit a line containing only
+ ENDOFCONTENT
+ ContentType => the content-type of the Content field. Defaults to
+ 'text/plain'
+ UpdateType => 'correspond' or 'comment'; used in conjunction with
+ 'content' if this is an update. Defaults to
+ 'correspond'
+
+ CustomField-<id#> => custom field value
+ CF-name => custom field value
+ CustomField-name => custom field value
+
+Fields marked with an * are required.
+
+Fields marked with a + may have multiple values, simply
+by repeating the fieldname on a new line with an additional value.
+
+Fields marked with a ! are postponed to be processed after all
+tickets in the same actions are created. Except for 'Status', those
+field can also take a ticket name within the same action (i.e.
+the identifiers after ==Create-Ticket), instead of raw Ticket ID
+numbers.
+
+When parsed, field names are converted to lowercase and have -s stripped.
+Refers-To, RefersTo, refersto, refers-to and r-e-f-er-s-tO will all
+be treated as the same thing.
+
+
+=begin testing
+
+ok (require RT::Action::CreateTickets);
+use_ok(RT::Scrip);
+use_ok(RT::Template);
+use_ok(RT::ScripAction);
+use_ok(RT::ScripCondition);
+use_ok(RT::Ticket);
+
+my $approvalsq = RT::Queue->new($RT::SystemUser);
+$approvalsq->Create(Name => 'Approvals');
+ok ($approvalsq->Id, "Created Approvals test queue");
+
+
+my $approvals =
+'===Create-Ticket: approval
+Queue: ___Approvals
+Type: approval
+AdminCc: {join ("\nAdminCc: ",@admins) }
+Depended-On-By: {$Tickets{"TOP"}->Id}
+Refers-To: TOP
+Subject: Approval for ticket: {$Tickets{"TOP"}->Id} - {$Tickets{"TOP"}->Subject}
+Due: {time + 86400}
+Content-Type: text/plain
+Content: Your approval is requested for the ticket {$Tickets{"TOP"}->Id}: {$Tickets{"TOP"}->Subject}
+Blah
+Blah
+ENDOFCONTENT
+===Create-Ticket: two
+Subject: Manager approval.
+Depended-On-By: approval
+Queue: ___Approvals
+Content-Type: text/plain
+Content:
+Your minion approved ticket {$Tickets{"TOP"}->Id}. you ok with that?
+ENDOFCONTENT
+';
+
+ok ($approvals =~ /Content/, "Read in the approvals template");
+
+my $apptemp = RT::Template->new($RT::SystemUser);
+$apptemp->Create( Content => $approvals, Name => "Approvals", Queue => "0");
+
+ok ($apptemp->Id);
+
+my $q = RT::Queue->new($RT::SystemUser);
+$q->Create(Name => 'WorkflowTest');
+ok ($q->Id, "Created workflow test queue");
+
+my $scrip = RT::Scrip->new($RT::SystemUser);
+my ($sval, $smsg) =$scrip->Create( ScripCondition => 'On Transaction',
+ ScripAction => 'Create Tickets',
+ Template => 'Approvals',
+ Queue => $q->Id);
+ok ($sval, $smsg);
+ok ($scrip->Id, "Created the scrip");
+ok ($scrip->TemplateObj->Id, "Created the scrip template");
+ok ($scrip->ConditionObj->Id, "Created the scrip condition");
+ok ($scrip->ActionObj->Id, "Created the scrip action");
+
+my $t = RT::Ticket->new($RT::SystemUser);
+my($tid, $ttrans, $tmsg) = $t->Create(Subject => "Sample workflow test",
+ Owner => "root",
+ Queue => $q->Id);
+
+ok ($tid,$tmsg);
+
+my $deps = $t->DependsOn;
+is ($deps->Count, 1, "The ticket we created depends on one other ticket");
+my $dependson= $deps->First->TargetObj;
+ok ($dependson->Id, "It depends on a real ticket");
+unlike ($dependson->Subject, qr/{/, "The subject doesn't have braces in it. that means we're interpreting expressions");
+is ($t->ReferredToBy->Count,1, "It's only referred to by one other ticket");
+is ($t->ReferredToBy->First->BaseObj->Id,$t->DependsOn->First->TargetObj->Id, "The same ticket that depends on it refers to it.");
+use RT::Action::CreateTickets;
+my $action = RT::Action::CreateTickets->new( CurrentUser => $RT::SystemUser);;
+
+# comma-delimited templates
+my $commas = <<"EOF";
+id,Queue,Subject,Owner,Content
+ticket1,General,"foo, bar",root,blah
+ticket2,General,foo bar,root,blah
+ticket3,General,foo' bar,root,blah'boo
+ticket4,General,foo' bar,,blah'boo
+EOF
+
+
+# Comma delimited templates with missing data
+my $sparse_commas = <<"EOF";
+id,Queue,Subject,Owner,Requestor
+ticket14,General,,,bobby
+ticket15,General,,,tommy
+ticket16,General,,suzie,tommy
+ticket17,General,Foo "bar" baz,suzie,tommy
+ticket18,General,'Foo "bar" baz',suzie,tommy
+ticket19,General,'Foo bar' baz,suzie,tommy
+EOF
+
+
+# tab-delimited templates
+my $tabs = <<"EOF";
+id\tQueue\tSubject\tOwner\tContent
+ticket10\tGeneral\t"foo' bar"\troot\tblah'
+ticket11\tGeneral\tfoo, bar\troot\tblah
+ticket12\tGeneral\tfoo' bar\troot\tblah'boo
+ticket13\tGeneral\tfoo' bar\t\tblah'boo
+EOF
+
+my %expected;
+
+$expected{ticket1} = <<EOF;
+Queue: General
+Subject: foo, bar
+Owner: root
+Content: blah
+ENDOFCONTENT
+EOF
+
+$expected{ticket2} = <<EOF;
+Queue: General
+Subject: foo bar
+Owner: root
+Content: blah
+ENDOFCONTENT
+EOF
+
+$expected{ticket3} = <<EOF;
+Queue: General
+Subject: foo' bar
+Owner: root
+Content: blah'boo
+ENDOFCONTENT
+EOF
+
+$expected{ticket4} = <<EOF;
+Queue: General
+Subject: foo' bar
+Owner:
+Content: blah'boo
+ENDOFCONTENT
+EOF
+
+$expected{ticket10} = <<EOF;
+Queue: General
+Subject: foo' bar
+Owner: root
+Content: blah'
+ENDOFCONTENT
+EOF
+
+$expected{ticket11} = <<EOF;
+Queue: General
+Subject: foo, bar
+Owner: root
+Content: blah
+ENDOFCONTENT
+EOF
+
+$expected{ticket12} = <<EOF;
+Queue: General
+Subject: foo' bar
+Owner: root
+Content: blah'boo
+ENDOFCONTENT
+EOF
+
+$expected{ticket13} = <<EOF;
+Queue: General
+Subject: foo' bar
+Owner:
+Content: blah'boo
+ENDOFCONTENT
+EOF
+
+
+$expected{'ticket14'} = <<EOF;
+Queue: General
+Subject:
+Owner:
+Requestor: bobby
+EOF
+$expected{'ticket15'} = <<EOF;
+Queue: General
+Subject:
+Owner:
+Requestor: tommy
+EOF
+$expected{'ticket16'} = <<EOF;
+Queue: General
+Subject:
+Owner: suzie
+Requestor: tommy
+EOF
+$expected{'ticket17'} = <<EOF;
+Queue: General
+Subject: Foo "bar" baz
+Owner: suzie
+Requestor: tommy
+EOF
+$expected{'ticket18'} = <<EOF;
+Queue: General
+Subject: Foo "bar" baz
+Owner: suzie
+Requestor: tommy
+EOF
+$expected{'ticket19'} = <<EOF;
+Queue: General
+Subject: 'Foo bar' baz
+Owner: suzie
+Requestor: tommy
+EOF
+
+
+
+
+$action->Parse(Content =>$commas);
+$action->Parse(Content =>$sparse_commas);
+$action->Parse(Content => $tabs);
+
+my %got;
+foreach (@{ $action->{'create_tickets'} }) {
+ $got{$_} = $action->{'templates'}->{$_};
+}
+
+foreach my $id ( sort keys %expected ) {
+ ok(exists($got{"create-$id"}), "template exists for $id");
+ is($got{"create-$id"}, $expected{$id}, "template is correct for $id");
+}
+
+=end testing
+
+
+=head1 AUTHOR
+
+Jesse Vincent <jesse@bestpractical.com>
+
+=head1 SEE ALSO
+
+perl(1).
+
+=cut
+
+my %LINKTYPEMAP = (
+ MemberOf => {
+ Type => 'MemberOf',
+ Mode => 'Target',
+ },
+ Parents => {
+ Type => 'MemberOf',
+ Mode => 'Target',
+ },
+ Members => {
+ Type => 'MemberOf',
+ Mode => 'Base',
+ },
+ Children => {
+ Type => 'MemberOf',
+ Mode => 'Base',
+ },
+ HasMember => {
+ Type => 'MemberOf',
+ Mode => 'Base',
+ },
+ RefersTo => {
+ Type => 'RefersTo',
+ Mode => 'Target',
+ },
+ ReferredToBy => {
+ Type => 'RefersTo',
+ Mode => 'Base',
+ },
+ DependsOn => {
+ Type => 'DependsOn',
+ Mode => 'Target',
+ },
+ DependedOnBy => {
+ Type => 'DependsOn',
+ Mode => 'Base',
+ },
+
+);
+
+# {{{ Scrip methods (Commit, Prepare)
+
+# {{{ sub Commit
+#Do what we need to do and send it out.
+sub Commit {
+ my $self = shift;
+
+ # Create all the tickets we care about
+ return (1) unless $self->TicketObj->Type eq 'ticket';
+
+ $self->CreateByTemplate( $self->TicketObj );
+ $self->UpdateByTemplate( $self->TicketObj );
+ return (1);
+}
+
+# }}}
+
+# {{{ sub Prepare
+
+sub Prepare {
+ my $self = shift;
+
+ unless ( $self->TemplateObj ) {
+ $RT::Logger->warning("No template object handed to $self\n");
+ }
+
+ unless ( $self->TransactionObj ) {
+ $RT::Logger->warning("No transaction object handed to $self\n");
+
+ }
+
+ unless ( $self->TicketObj ) {
+ $RT::Logger->warning("No ticket object handed to $self\n");
+
+ }
+
+ $self->Parse(
+ Content => $self->TemplateObj->Content,
+ _ActiveContent => 1
+ );
+ return 1;
+
+}
+
+# }}}
+
+# }}}
+
+sub CreateByTemplate {
+ my $self = shift;
+ my $top = shift;
+
+ $RT::Logger->debug("In CreateByTemplate");
+
+ my @results;
+
+ # XXX: cargo cult programming that works. i'll be back.
+ use bytes;
+
+ local %T::Tickets = %T::Tickets;
+ local $T::TOP = $T::TOP;
+ local $T::ID = $T::ID;
+ $T::Tickets{'TOP'} = $T::TOP = $top if $top;
+
+ my $ticketargs;
+ my ( @links, @postponed );
+ foreach my $template_id ( @{ $self->{'create_tickets'} } ) {
+ $RT::Logger->debug("Workflow: processing $template_id of $T::TOP")
+ if $T::TOP;
+
+ $T::ID = $template_id;
+ @T::AllID = @{ $self->{'create_tickets'} };
+
+ ( $T::Tickets{$template_id}, $ticketargs )
+ = $self->ParseLines( $template_id, \@links, \@postponed );
+
+ # Now we have a %args to work with.
+ # Make sure we have at least the minimum set of
+ # reasonable data and do our thang
+
+ my ( $id, $transid, $msg )
+ = $T::Tickets{$template_id}->Create(%$ticketargs);
+
+ foreach my $res ( split( '\n', $msg ) ) {
+ push @results,
+ $T::Tickets{$template_id}
+ ->loc( "Ticket [_1]", $T::Tickets{$template_id}->Id ) . ': '
+ . $res;
+ }
+ if ( !$id ) {
+ if ( $self->TicketObj ) {
+ $msg = "Couldn't create related ticket $template_id for "
+ . $self->TicketObj->Id . " "
+ . $msg;
+ } else {
+ $msg = "Couldn't create ticket $template_id " . $msg;
+ }
+
+ $RT::Logger->error($msg);
+ next;
+ }
+
+ $RT::Logger->debug("Assigned $template_id with $id");
+ $T::Tickets{$template_id}->SetOriginObj( $self->TicketObj )
+ if $self->TicketObj
+ && $T::Tickets{$template_id}->can('SetOriginObj');
+
+ }
+
+ $self->PostProcess( \@links, \@postponed );
+
+ return @results;
+}
+
+sub UpdateByTemplate {
+ my $self = shift;
+ my $top = shift;
+
+ # XXX: cargo cult programming that works. i'll be back.
+ use bytes;
+
+ my @results;
+ local %T::Tickets = %T::Tickets;
+ local $T::ID = $T::ID;
+
+ my $ticketargs;
+ my ( @links, @postponed );
+ foreach my $template_id ( @{ $self->{'update_tickets'} } ) {
+ $RT::Logger->debug("Update Workflow: processing $template_id");
+
+ $T::ID = $template_id;
+ @T::AllID = @{ $self->{'update_tickets'} };
+
+ ( $T::Tickets{$template_id}, $ticketargs )
+ = $self->ParseLines( $template_id, \@links, \@postponed );
+
+ # Now we have a %args to work with.
+ # Make sure we have at least the minimum set of
+ # reasonable data and do our thang
+
+ my @attribs = qw(
+ Subject
+ FinalPriority
+ Priority
+ TimeEstimated
+ TimeWorked
+ TimeLeft
+ Status
+ Queue
+ Due
+ Starts
+ Started
+ Resolved
+ );
+
+ my $id = $template_id;
+ $id =~ s/update-(\d+).*/$1/;
+ my ($loaded, $msg) = $T::Tickets{$template_id}->LoadById($id);
+
+ unless ( $loaded ) {
+ $RT::Logger->error("Couldn't update ticket $template_id: " . $msg);
+ push @results, $self->loc( "Couldn't load ticket '[_1]'", $id );
+ next;
+ }
+
+ my $current = $self->GetBaseTemplate( $T::Tickets{$template_id} );
+
+ $template_id =~ m/^update-(.*)/;
+ my $base_id = "base-$1";
+ my $base = $self->{'templates'}->{$base_id};
+ if ($base) {
+ $base =~ s/\r//g;
+ $base =~ s/\n+$//;
+ $current =~ s/\n+$//;
+
+ # If we have no base template, set what we can.
+ if ( $base ne $current ) {
+ push @results,
+ "Could not update ticket "
+ . $T::Tickets{$template_id}->Id
+ . ": Ticket has changed";
+ next;
+ }
+ }
+ push @results, $T::Tickets{$template_id}->Update(
+ AttributesRef => \@attribs,
+ ARGSRef => $ticketargs
+ );
+
+ if ( $ticketargs->{'Owner'} ) {
+ ($id, $msg) = $T::Tickets{$template_id}->SetOwner($ticketargs->{'Owner'}, "Force");
+ push @results, $msg unless $msg eq $self->loc("That user already owns that ticket");
+ }
+
+ push @results,
+ $self->UpdateWatchers( $T::Tickets{$template_id}, $ticketargs );
+
+ push @results,
+ $self->UpdateCustomFields( $T::Tickets{$template_id}, $ticketargs );
+
+ next unless $ticketargs->{'MIMEObj'};
+ if ( $ticketargs->{'UpdateType'} =~ /^(private|comment)$/i ) {
+ my ( $Transaction, $Description, $Object )
+ = $T::Tickets{$template_id}->Comment(
+ BccMessageTo => $ticketargs->{'Bcc'},
+ MIMEObj => $ticketargs->{'MIMEObj'},
+ TimeTaken => $ticketargs->{'TimeWorked'}
+ );
+ push( @results,
+ $T::Tickets{$template_id}
+ ->loc( "Ticket [_1]", $T::Tickets{$template_id}->id )
+ . ': '
+ . $Description );
+ } elsif ( $ticketargs->{'UpdateType'} =~ /^(public|response|correspond)$/i ) {
+ my ( $Transaction, $Description, $Object )
+ = $T::Tickets{$template_id}->Correspond(
+ BccMessageTo => $ticketargs->{'Bcc'},
+ MIMEObj => $ticketargs->{'MIMEObj'},
+ TimeTaken => $ticketargs->{'TimeWorked'}
+ );
+ push( @results,
+ $T::Tickets{$template_id}
+ ->loc( "Ticket [_1]", $T::Tickets{$template_id}->id )
+ . ': '
+ . $Description );
+ } else {
+ push(
+ @results,
+ $T::Tickets{$template_id}->loc(
+ "Update type was neither correspondence nor comment.")
+ . " "
+ . $T::Tickets{$template_id}->loc("Update not recorded.")
+ );
+ }
+ }
+
+ $self->PostProcess( \@links, \@postponed );
+
+ return @results;
+}
+
+=head2 Parse TEMPLATE_CONTENT, DEFAULT_QUEUE, DEFAULT_REQEUESTOR ACTIVE
+
+Parse a template from TEMPLATE_CONTENT
+
+If $active is set to true, then we'll use Text::Template to parse the templates,
+allowing you to embed active perl in your templates.
+
+=cut
+
+sub Parse {
+ my $self = shift;
+ my %args = (
+ Content => undef,
+ Queue => undef,
+ Requestor => undef,
+ _ActiveContent => undef,
+ @_
+ );
+
+ if ( $args{'_ActiveContent'} ) {
+ $self->{'UsePerlTextTemplate'} = 1;
+ } else {
+
+ $self->{'UsePerlTextTemplate'} = 0;
+ }
+
+ if ( substr( $args{'Content'}, 0, 3 ) eq '===' ) {
+ $self->_ParseMultilineTemplate(%args);
+ } elsif ( $args{'Content'} =~ /(?:\t|,)/i ) {
+ $self->_ParseXSVTemplate(%args);
+
+ }
+}
+
+=head2 _ParseMultilineTemplate
+
+Parses mulitline templates. Things like:
+
+ ===Create-Ticket ...
+
+Takes the same arguments as Parse
+
+=cut
+
+sub _ParseMultilineTemplate {
+ my $self = shift;
+ my %args = (@_);
+
+ my $template_id;
+ my ( $queue, $requestor );
+ $RT::Logger->debug("Line: ===");
+ foreach my $line ( split( /\n/, $args{'Content'} ) ) {
+ $line =~ s/\r$//;
+ $RT::Logger->debug("Line: $line");
+ if ( $line =~ /^===/ ) {
+ if ( $template_id && !$queue && $args{'Queue'} ) {
+ $self->{'templates'}->{$template_id}
+ .= "Queue: $args{'Queue'}\n";
+ }
+ if ( $template_id && !$requestor && $args{'Requestor'} ) {
+ $self->{'templates'}->{$template_id}
+ .= "Requestor: $args{'Requestor'}\n";
+ }
+ $queue = 0;
+ $requestor = 0;
+ }
+ if ( $line =~ /^===Create-Ticket: (.*)$/ ) {
+ $template_id = "create-$1";
+ $RT::Logger->debug("**** Create ticket: $template_id");
+ push @{ $self->{'create_tickets'} }, $template_id;
+ } elsif ( $line =~ /^===Update-Ticket: (.*)$/ ) {
+ $template_id = "update-$1";
+ $RT::Logger->debug("**** Update ticket: $template_id");
+ push @{ $self->{'update_tickets'} }, $template_id;
+ } elsif ( $line =~ /^===Base-Ticket: (.*)$/ ) {
+ $template_id = "base-$1";
+ $RT::Logger->debug("**** Base ticket: $template_id");
+ push @{ $self->{'base_tickets'} }, $template_id;
+ } elsif ( $line =~ /^===#.*$/ ) { # a comment
+ next;
+ } else {
+ if ( $line =~ /^Queue:(.*)/i ) {
+ $queue = 1;
+ my $value = $1;
+ $value =~ s/^\s//;
+ $value =~ s/\s$//;
+ if ( !$value && $args{'Queue'} ) {
+ $value = $args{'Queue'};
+ $line = "Queue: $value";
+ }
+ }
+ if ( $line =~ /^Requestors?:(.*)/i ) {
+ $requestor = 1;
+ my $value = $1;
+ $value =~ s/^\s//;
+ $value =~ s/\s$//;
+ if ( !$value && $args{'Requestor'} ) {
+ $value = $args{'Requestor'};
+ $line = "Requestor: $value";
+ }
+ }
+ $self->{'templates'}->{$template_id} .= $line . "\n";
+ }
+ }
+ if ( $template_id && !$queue && $args{'Queue'} ) {
+ $self->{'templates'}->{$template_id} .= "Queue: $args{'Queue'}\n";
+ }
+ }
+
+sub ParseLines {
+ my $self = shift;
+ my $template_id = shift;
+ my $links = shift;
+ my $postponed = shift;
+
+ my $content = $self->{'templates'}->{$template_id};
+
+ if ( $self->{'UsePerlTextTemplate'} ) {
+
+ $RT::Logger->debug(
+ "Workflow: evaluating\n$self->{templates}{$template_id}");
+
+ my $template = Text::Template->new(
+ TYPE => 'STRING',
+ SOURCE => $content
+ );
+
+ my $err;
+ $content = $template->fill_in(
+ PACKAGE => 'T',
+ BROKEN => sub {
+ $err = {@_}->{error};
+ }
+ );
+
+ $RT::Logger->debug("Workflow: yielding\n$content");
+
+ if ($err) {
+ $RT::Logger->error( "Ticket creation failed: " . $err );
+ while ( my ( $k, $v ) = each %T::X ) {
+ $RT::Logger->debug(
+ "Eliminating $template_id from ${k}'s parents.");
+ delete $v->{$template_id};
+ }
+ next;
+ }
+ }
+
+ my $TicketObj ||= RT::Ticket->new( $self->CurrentUser );
+
+ my %args;
+ my %original_tags;
+ my @lines = ( split( /\n/, $content ) );
+ while ( defined( my $line = shift @lines ) ) {
+ if ( $line =~ /^(.*?):(?:\s+)(.*?)(?:\s*)$/ ) {
+ my $value = $2;
+ my $original_tag = $1;
+ my $tag = lc($original_tag);
+ $tag =~ s/-//g;
+ $tag =~ s/^(requestor|cc|admincc)s?$/$1/i;
+
+ $original_tags{$tag} = $original_tag;
+
+ if ( ref( $args{$tag} ) )
+ { #If it's an array, we want to push the value
+ push @{ $args{$tag} }, $value;
+ } elsif ( defined( $args{$tag} ) )
+ { #if we're about to get a second value, make it an array
+ $args{$tag} = [ $args{$tag}, $value ];
+ } else { #if there's nothing there, just set the value
+ $args{$tag} = $value;
+ }
+
+ if ( $tag =~ /^content$/i ) { #just build up the content
+ # convert it to an array
+ $args{$tag} = defined($value) ? [ $value . "\n" ] : [];
+ while ( defined( my $l = shift @lines ) ) {
+ last if ( $l =~ /^ENDOFCONTENT\s*$/ );
+ push @{ $args{'content'} }, $l . "\n";
+ }
+ } else {
+ # if it's not content, strip leading and trailing spaces
+ if ( $args{$tag} ) {
+ $args{$tag} =~ s/^\s+//g;
+ $args{$tag} =~ s/\s+$//g;
+ }
+ if (($tag =~ /^(requestor|cc|admincc)$/i or grep {lc $_ eq $tag} keys %LINKTYPEMAP) and $args{$tag} =~ /,/) {
+ $args{$tag} = [ split /,\s*/, $args{$tag} ];
+ }
+ }
+ }
+ }
+
+ foreach my $date qw(due starts started resolved) {
+ my $dateobj = RT::Date->new( $self->CurrentUser );
+ next unless $args{$date};
+ if ( $args{$date} =~ /^\d+$/ ) {
+ $dateobj->Set( Format => 'unix', Value => $args{$date} );
+ } else {
+ eval {
+ $dateobj->Set( Format => 'iso', Value => $args{$date} );
+ };
+ if ($@ or $dateobj->Unix <= 0) {
+ $dateobj->Set( Format => 'unknown', Value => $args{$date} );
+ }
+ }
+ $args{$date} = $dateobj->ISO;
+ }
+
+ $args{'requestor'} ||= $self->TicketObj->Requestors->MemberEmailAddresses
+ if $self->TicketObj;
+
+ $args{'type'} ||= 'ticket';
+
+ my %ticketargs = (
+ Queue => $args{'queue'},
+ Subject => $args{'subject'},
+ Status => $args{'status'} || 'new',
+ Due => $args{'due'},
+ Starts => $args{'starts'},
+ Started => $args{'started'},
+ Resolved => $args{'resolved'},
+ Owner => $args{'owner'},
+ Requestor => $args{'requestor'},
+ Cc => $args{'cc'},
+ AdminCc => $args{'admincc'},
+ TimeWorked => $args{'timeworked'},
+ TimeEstimated => $args{'timeestimated'},
+ TimeLeft => $args{'timeleft'},
+ InitialPriority => $args{'initialpriority'} || 0,
+ FinalPriority => $args{'finalpriority'} || 0,
+ Type => $args{'type'},
+ );
+
+ if ( $args{content} ) {
+ my $mimeobj = MIME::Entity->new();
+ $mimeobj->build(
+ Type => $args{'contenttype'} || 'text/plain',
+ Data => $args{'content'}
+ );
+ $ticketargs{MIMEObj} = $mimeobj;
+ $ticketargs{UpdateType} = $args{'updatetype'} || 'correspond';
+ }
+
+ foreach my $tag ( keys(%args) ) {
+ # if the tag was added later, skip it
+ my $orig_tag = $original_tags{$tag} or next;
+ if ( $orig_tag =~ /^customfield-?(\d+)$/i ) {
+ $ticketargs{ "CustomField-" . $1 } = $args{$tag};
+ } elsif ( $orig_tag =~ /^(?:customfield|cf)-?(.*)$/i ) {
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->LoadByName( Name => $1, Queue => $ticketargs{Queue} );
+ $ticketargs{ "CustomField-" . $cf->id } = $args{$tag};
+ } elsif ($orig_tag) {
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->LoadByName( Name => $orig_tag, Queue => $ticketargs{Queue} );
+ next unless ($cf->id) ;
+ $ticketargs{ "CustomField-" . $cf->id } = $args{$tag};
+
+ }
+ }
+
+ $self->GetDeferred( \%args, $template_id, $links, $postponed );
+
+ return $TicketObj, \%ticketargs;
+}
+
+
+=head2 _ParseXSVTemplate
+
+Parses a tab or comma delimited template. Should only ever be called by Parse
+
+=cut
+
+sub _ParseXSVTemplate {
+ my $self = shift;
+ my %args = (@_);
+
+ use Regexp::Common qw(delimited);
+ my($first, $content) = split(/\r?\n/, $args{'Content'}, 2);
+
+ my $delimiter;
+ if ( $first =~ /\t/ ) {
+ $delimiter = "\t";
+ } else {
+ $delimiter = ',';
+ }
+ my @fields = split( /$delimiter/, $first );
+
+ my $delimiter_re = qr[$delimiter];
+ my $justquoted = qr[$RE{quoted}];
+
+ # Used to generate automatic template ids
+ my $autoid = 1;
+
+ LINE:
+ while ($content) {
+ $content =~ s/^(\s*\r?\n)+//;
+
+ # Keep track of Queue and Requestor, so we can provide defaults
+ my $queue;
+ my $requestor;
+
+ # The template for this line
+ my $template;
+
+ # What column we're on
+ my $i = 0;
+
+ # If the last iteration was the end of the line
+ my $EOL = 0;
+
+ # The template id
+ my $template_id;
+
+ COLUMN:
+ while (not $EOL and length $content and $content =~ s/^($justquoted|.*?)($delimiter_re|$)//smix) {
+ $EOL = not $2;
+
+ # Strip off quotes, if they exist
+ my $value = $1;
+ if ( $value =~ /^$RE{delimited}{-delim=>qq{\'\"}}$/ ) {
+ substr( $value, 0, 1 ) = "";
+ substr( $value, -1, 1 ) = "";
+ }
+
+ # What column is this?
+ my $field = $fields[$i++];
+ next COLUMN unless $field =~ /\S/;
+ $field =~ s/^\s//;
+ $field =~ s/\s$//;
+
+ if ( $field =~ /^id$/i ) {
+ # Special case if this is the ID column
+ if ( $value =~ /^\d+$/ ) {
+ $template_id = 'update-' . $value;
+ push @{ $self->{'update_tickets'} }, $template_id;
+ } elsif ( $value =~ /^#base-(\d+)$/ ) {
+ $template_id = 'base-' . $1;
+ push @{ $self->{'base_tickets'} }, $template_id;
+ } elsif ( $value =~ /\S/ ) {
+ $template_id = 'create-' . $value;
+ push @{ $self->{'create_tickets'} }, $template_id;
+ }
+ } else {
+ # Some translations
+ if ( $field =~ /^Body$/i
+ || $field =~ /^Data$/i
+ || $field =~ /^Message$/i )
+ {
+ $field = 'Content';
+ } elsif ( $field =~ /^Summary$/i ) {
+ $field = 'Subject';
+ } elsif ( $field =~ /^Queue$/i ) {
+ # Note that we found a queue
+ $queue = 1;
+ $value ||= $args{'Queue'};
+ } elsif ( $field =~ /^Requestors?$/i ) {
+ $field = 'Requestor'; # Remove plural
+ # Note that we found a requestor
+ $requestor = 1;
+ $value ||= $args{'Requestor'};
+ }
+
+ # Tack onto the end of the template
+ $template .= $field . ": ";
+ $template .= (defined $value ? $value : "");
+ $template .= "\n";
+ $template .= "ENDOFCONTENT\n"
+ if $field =~ /^Content$/i;
+ }
+ }
+
+ # Ignore blank lines
+ next unless $template;
+
+ # If we didn't find a queue of requestor, tack on the defaults
+ if ( !$queue && $args{'Queue'} ) {
+ $template .= "Queue: $args{'Queue'}\n";
+ }
+ if ( !$requestor && $args{'Requestor'} ) {
+ $template .= "Requestor: $args{'Requestor'}\n";
+ }
+
+ # If we never found an ID, come up with one
+ unless ($template_id) {
+ $autoid++ while exists $self->{'templates'}->{"create-auto-$autoid"};
+ $template_id = "create-auto-$autoid";
+ # Also, it's a ticket to create
+ push @{ $self->{'create_tickets'} }, $template_id;
+ }
+
+ # Save the template we generated
+ $self->{'templates'}->{$template_id} = $template;
+
+ }
+}
+
+sub GetDeferred {
+ my $self = shift;
+ my $args = shift;
+ my $id = shift;
+ my $links = shift;
+ my $postponed = shift;
+
+ # Deferred processing
+ push @$links,
+ (
+ $id,
+ { DependsOn => $args->{'dependson'},
+ DependedOnBy => $args->{'dependedonby'},
+ RefersTo => $args->{'refersto'},
+ ReferredToBy => $args->{'referredtoby'},
+ Children => $args->{'children'},
+ Parents => $args->{'parents'},
+ }
+ );
+
+ push @$postponed, (
+
+ # Status is postponed so we don't violate dependencies
+ $id, { Status => $args->{'status'}, }
+ );
+}
+
+sub GetUpdateTemplate {
+ my $self = shift;
+ my $t = shift;
+
+ my $string;
+ $string .= "Queue: " . $t->QueueObj->Name . "\n";
+ $string .= "Subject: " . $t->Subject . "\n";
+ $string .= "Status: " . $t->Status . "\n";
+ $string .= "UpdateType: correspond\n";
+ $string .= "Content: \n";
+ $string .= "ENDOFCONTENT\n";
+ $string .= "Due: " . $t->DueObj->AsString . "\n";
+ $string .= "Starts: " . $t->StartsObj->AsString . "\n";
+ $string .= "Started: " . $t->StartedObj->AsString . "\n";
+ $string .= "Resolved: " . $t->ResolvedObj->AsString . "\n";
+ $string .= "Owner: " . $t->OwnerObj->Name . "\n";
+ $string .= "Requestor: " . $t->RequestorAddresses . "\n";
+ $string .= "Cc: " . $t->CcAddresses . "\n";
+ $string .= "AdminCc: " . $t->AdminCcAddresses . "\n";
+ $string .= "TimeWorked: " . $t->TimeWorked . "\n";
+ $string .= "TimeEstimated: " . $t->TimeEstimated . "\n";
+ $string .= "TimeLeft: " . $t->TimeLeft . "\n";
+ $string .= "InitialPriority: " . $t->Priority . "\n";
+ $string .= "FinalPriority: " . $t->FinalPriority . "\n";
+
+ foreach my $type ( sort keys %LINKTYPEMAP ) {
+
+ # don't display duplicates
+ if ( $type eq "HasMember"
+ || $type eq "Members"
+ || $type eq "MemberOf" )
+ {
+ next;
+ }
+ $string .= "$type: ";
+
+ my $mode = $LINKTYPEMAP{$type}->{Mode};
+ my $method = $LINKTYPEMAP{$type}->{Type};
+
+ my $links;
+ while ( my $link = $t->$method->Next ) {
+ $links .= ", " if $links;
+
+ my $object = $mode . "Obj";
+ my $member = $link->$object;
+ $links .= $member->Id if $member;
+ }
+ $string .= $links;
+ $string .= "\n";
+ }
+
+ return $string;
+}
+
+sub GetBaseTemplate {
+ my $self = shift;
+ my $t = shift;
+
+ my $string;
+ $string .= "Queue: " . $t->Queue . "\n";
+ $string .= "Subject: " . $t->Subject . "\n";
+ $string .= "Status: " . $t->Status . "\n";
+ $string .= "Due: " . $t->DueObj->Unix . "\n";
+ $string .= "Starts: " . $t->StartsObj->Unix . "\n";
+ $string .= "Started: " . $t->StartedObj->Unix . "\n";
+ $string .= "Resolved: " . $t->ResolvedObj->Unix . "\n";
+ $string .= "Owner: " . $t->Owner . "\n";
+ $string .= "Requestor: " . $t->RequestorAddresses . "\n";
+ $string .= "Cc: " . $t->CcAddresses . "\n";
+ $string .= "AdminCc: " . $t->AdminCcAddresses . "\n";
+ $string .= "TimeWorked: " . $t->TimeWorked . "\n";
+ $string .= "TimeEstimated: " . $t->TimeEstimated . "\n";
+ $string .= "TimeLeft: " . $t->TimeLeft . "\n";
+ $string .= "InitialPriority: " . $t->Priority . "\n";
+ $string .= "FinalPriority: " . $t->FinalPriority . "\n";
+
+ return $string;
+}
+
+sub GetCreateTemplate {
+ my $self = shift;
+
+ my $string;
+
+ $string .= "Queue: General\n";
+ $string .= "Subject: \n";
+ $string .= "Status: new\n";
+ $string .= "Content: \n";
+ $string .= "ENDOFCONTENT\n";
+ $string .= "Due: \n";
+ $string .= "Starts: \n";
+ $string .= "Started: \n";
+ $string .= "Resolved: \n";
+ $string .= "Owner: \n";
+ $string .= "Requestor: \n";
+ $string .= "Cc: \n";
+ $string .= "AdminCc:\n";
+ $string .= "TimeWorked: \n";
+ $string .= "TimeEstimated: \n";
+ $string .= "TimeLeft: \n";
+ $string .= "InitialPriority: \n";
+ $string .= "FinalPriority: \n";
+
+ foreach my $type ( keys %LINKTYPEMAP ) {
+
+ # don't display duplicates
+ if ( $type eq "HasMember"
+ || $type eq 'Members'
+ || $type eq 'MemberOf' )
+ {
+ next;
+ }
+ $string .= "$type: \n";
+ }
+ return $string;
+}
+
+sub UpdateWatchers {
+ my $self = shift;
+ my $ticket = shift;
+ my $args = shift;
+
+ my @results;
+
+ foreach my $type qw(Requestor Cc AdminCc) {
+ my $method = $type . 'Addresses';
+ my $oldaddr = $ticket->$method;
+
+ # Skip unless we have a defined field
+ next unless defined $args->{$type};
+ my $newaddr = $args->{$type};
+
+ my @old = split( /,\s*/, $oldaddr );
+ my @new;
+ for (ref $newaddr ? @{$newaddr} : split( /,\s*/, $newaddr )) {
+ # Sometimes these are email addresses, sometimes they're
+ # users. Try to guess which is which, as we want to deal
+ # with email addresses if at all possible.
+ if (/^\S+@\S+$/) {
+ push @new, $_;
+ } else {
+ # It doesn't look like an email address. Try to load it.
+ my $user = RT::User->new($self->CurrentUser);
+ $user->Load($_);
+ if ($user->Id) {
+ push @new, $user->EmailAddress;
+ } else {
+ push @new, $_;
+ }
+ }
+ }
+
+ my %oldhash = map { $_ => 1 } @old;
+ my %newhash = map { $_ => 1 } @new;
+
+ my @add = grep( !defined $oldhash{$_}, @new );
+ my @delete = grep( !defined $newhash{$_}, @old );
+
+ foreach (@add) {
+ my ( $val, $msg ) = $ticket->AddWatcher(
+ Type => $type,
+ Email => $_
+ );
+
+ push @results,
+ $ticket->loc( "Ticket [_1]", $ticket->Id ) . ': ' . $msg;
+ }
+
+ foreach (@delete) {
+ my ( $val, $msg ) = $ticket->DeleteWatcher(
+ Type => $type,
+ Email => $_
+ );
+ push @results,
+ $ticket->loc( "Ticket [_1]", $ticket->Id ) . ': ' . $msg;
+ }
+ }
+ return @results;
+}
+
+sub UpdateCustomFields {
+ my $self = shift;
+ my $ticket = shift;
+ my $args = shift;
+
+ my @results;
+ foreach my $arg (keys %{$args}) {
+ next unless $arg =~ /^CustomField-(\d+)$/;
+ my $cf = $1;
+
+ my $CustomFieldObj = RT::CustomField->new($self->CurrentUser);
+ $CustomFieldObj->LoadById($cf);
+
+ my @values;
+ if ($CustomFieldObj->Type =~ /text/i) { # Both Text and Wikitext
+ @values = ($args->{$arg});
+ } else {
+ @values = split /\n/, $args->{$arg};
+ }
+
+ if ( ($CustomFieldObj->Type eq 'Freeform'
+ && ! $CustomFieldObj->SingleValue) ||
+ $CustomFieldObj->Type =~ /text/i) {
+ foreach my $val (@values) {
+ $val =~ s/\r//g;
+ }
+ }
+
+ foreach my $value (@values) {
+ next unless length($value);
+ my ( $val, $msg ) = $ticket->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ return @results;
+}
+
+sub PostProcess {
+ my $self = shift;
+ my $links = shift;
+ my $postponed = shift;
+
+ # postprocessing: add links
+
+ while ( my $template_id = shift(@$links) ) {
+ my $ticket = $T::Tickets{$template_id};
+ $RT::Logger->debug( "Handling links for " . $ticket->Id );
+ my %args = %{ shift(@$links) };
+
+ foreach my $type ( keys %LINKTYPEMAP ) {
+ next unless ( defined $args{$type} );
+ foreach my $link (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ next unless $link;
+
+ if ( $link =~ /^TOP$/i ) {
+ $RT::Logger->debug( "Building $type link for $link: "
+ . $T::Tickets{TOP}->Id );
+ $link = $T::Tickets{TOP}->Id;
+
+ } elsif ( $link !~ m/^\d+$/ ) {
+ my $key = "create-$link";
+ if ( !exists $T::Tickets{$key} ) {
+ $RT::Logger->debug(
+ "Skipping $type link for $key (non-existent)");
+ next;
+ }
+ $RT::Logger->debug( "Building $type link for $link: "
+ . $T::Tickets{$key}->Id );
+ $link = $T::Tickets{$key}->Id;
+ } else {
+ $RT::Logger->debug("Building $type link for $link");
+ }
+
+ my ( $wval, $wmsg ) = $ticket->AddLink(
+ Type => $LINKTYPEMAP{$type}->{'Type'},
+ $LINKTYPEMAP{$type}->{'Mode'} => $link,
+ Silent => 1
+ );
+
+ $RT::Logger->warning("AddLink thru $link failed: $wmsg")
+ unless $wval;
+
+ # push @non_fatal_errors, $wmsg unless ($wval);
+ }
+
+ }
+ }
+
+ # postponed actions -- Status only, currently
+ while ( my $template_id = shift(@$postponed) ) {
+ my $ticket = $T::Tickets{$template_id};
+ $RT::Logger->debug( "Handling postponed actions for " . $ticket->id );
+ my %args = %{ shift(@$postponed) };
+ $ticket->SetStatus( $args{Status} ) if defined $args{Status};
+ }
+
+}
+
+eval "require RT::Action::CreateTickets_Vendor";
+die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Vendor.pm} );
+eval "require RT::Action::CreateTickets_Local";
+die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/CreateTickets_Local.pm} );
+
+1;
+
diff --git a/rt/lib/RT/Action/EscalatePriority.pm b/rt/lib/RT/Action/EscalatePriority.pm
new file mode 100644
index 0000000..1a62aab
--- /dev/null
+++ b/rt/lib/RT/Action/EscalatePriority.pm
@@ -0,0 +1,167 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Action::EscalatePriority
+
+=head1 DESCRIPTION
+
+EscalatePriority is a ScripAction which is NOT intended to be called
+per transaction. It's intended to be called by an RT escalation tool.
+One such tool is called rt-crontool and is located in $RTHOME/bin (see
+C<rt-crontool -h> for more details)
+
+EsclatePriority uses the following formula to change a ticket's priority:
+
+ Priority = Priority + (( FinalPriority - Priority ) / ( DueDate-Today))
+
+Unless the duedate is past, in which case priority gets bumped straight
+to final priority.
+
+In this way, priority is either increased or decreased toward the final priority
+as the ticket heads toward its due date.
+
+
+=cut
+
+
+package RT::Action::EscalatePriority;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA=qw(RT::Action::Generic);
+
+#Do what we need to do and send it out.
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return (ref $self . " will move a ticket's priority toward its final priority.");
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+
+ if ($self->TicketObj->Priority() == $self->TicketObj->FinalPriority()) {
+ # no update necessary.
+ return 0;
+ }
+
+ #compute the number of days until the ticket is due
+ my $due = $self->TicketObj->DueObj();
+
+
+ # If we don't have a due date, adjust the priority by one
+ # until we hit the final priority
+ if ($due->Unix() < 1) {
+ if ( $self->TicketObj->Priority > $self->TicketObj->FinalPriority ){
+ $self->{'prio'} = ($self->TicketObj->Priority - 1);
+ return 1;
+ }
+ elsif ( $self->TicketObj->Priority < $self->TicketObj->FinalPriority ){
+ $self->{'prio'} = ($self->TicketObj->Priority + 1);
+ return 1;
+ }
+ # otherwise the priority is at the final priority. we don't need to
+ # Continue
+ else {
+ return 0;
+ }
+ }
+
+ # we've got a due date. now there are other things we should do
+ else {
+ my $diff_in_seconds = $due->Diff(time());
+ my $diff_in_days = int( $diff_in_seconds / 86400);
+
+ #if we haven't hit the due date yet
+ if ($diff_in_days > 0 ) {
+
+ # compute the difference between the current priority and the
+ # final priority
+
+ my $prio_delta =
+ $self->TicketObj->FinalPriority() - $self->TicketObj->Priority;
+
+ my $inc_priority_by = int( $prio_delta / $diff_in_days );
+
+ #set the ticket's priority to that amount
+ $self->{'prio'} = $self->TicketObj->Priority + $inc_priority_by;
+
+ }
+ #if $days is less than 1, set priority to final_priority
+ else {
+ $self->{'prio'} = $self->TicketObj->FinalPriority();
+ }
+
+ }
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+ my ($val, $msg) = $self->TicketObj->SetPriority($self->{'prio'});
+
+ unless ($val) {
+ $RT::Logger->debug($self . " $msg\n");
+ }
+}
+
+eval "require RT::Action::EscalatePriority_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/EscalatePriority_Vendor.pm});
+eval "require RT::Action::EscalatePriority_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/EscalatePriority_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/Generic.pm b/rt/lib/RT/Action/Generic.pm
new file mode 100755
index 0000000..166e7aa
--- /dev/null
+++ b/rt/lib/RT/Action/Generic.pm
@@ -0,0 +1,231 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Action::Generic - a generic baseclass for RT Actions
+
+=head1 SYNOPSIS
+
+ use RT::Action::Generic;
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=begin testing
+
+ok (require RT::Action::Generic);
+
+=end testing
+
+=cut
+
+package RT::Action::Generic;
+
+use strict;
+use Scalar::Util;
+
+use base qw/RT::Base/;
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ my %args = ( Argument => undef,
+ CurrentUser => undef,
+ ScripActionObj => undef,
+ ScripObj => undef,
+ TemplateObj => undef,
+ TicketObj => undef,
+ TransactionObj => undef,
+ Type => undef,
+
+ @_ );
+
+ $self->{'Argument'} = $args{'Argument'};
+ $self->CurrentUser( $args{'CurrentUser'});
+ $self->{'ScripActionObj'} = $args{'ScripActionObj'};
+ $self->{'ScripObj'} = $args{'ScripObj'};
+ $self->{'TemplateObj'} = $args{'TemplateObj'};
+ $self->{'TicketObj'} = $args{'TicketObj'};
+ $self->{'TransactionObj'} = $args{'TransactionObj'};
+ $self->{'Type'} = $args{'Type'};
+
+ Scalar::Util::weaken($self->{'ScripActionObj'});
+ Scalar::Util::weaken($self->{'ScripObj'});
+ Scalar::Util::weaken($self->{'TemplateObj'});
+ Scalar::Util::weaken($self->{'TicketObj'});
+ Scalar::Util::weaken($self->{'TransactionObj'});
+
+}
+# }}}
+
+# Access Scripwide data
+
+# {{{ sub Argument
+sub Argument {
+ my $self = shift;
+ return($self->{'Argument'});
+}
+# }}}
+
+# {{{ sub TicketObj
+sub TicketObj {
+ my $self = shift;
+ return($self->{'TicketObj'});
+}
+# }}}
+
+# {{{ sub TransactionObj
+sub TransactionObj {
+ my $self = shift;
+ return($self->{'TransactionObj'});
+}
+# }}}
+
+# {{{ sub TemplateObj
+sub TemplateObj {
+ my $self = shift;
+ return($self->{'TemplateObj'});
+}
+# }}}
+
+# {{{ sub ScripObj
+sub ScripObj {
+ my $self = shift;
+ return($self->{'ScripObj'});
+}
+# }}}
+
+# {{{ sub ScripActionObj
+sub ScripActionObj {
+ my $self = shift;
+ return($self->{'ScripActionObj'});
+}
+# }}}
+
+# {{{ sub Type
+sub Type {
+ my $self = shift;
+ return($self->{'Type'});
+}
+# }}}
+
+
+# Scrip methods
+
+#Do what we need to do and send it out.
+
+# {{{ sub Commit
+sub Commit {
+ my $self = shift;
+ return(0, $self->loc("Commit Stubbed"));
+}
+# }}}
+
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return $self->loc("No description for [_1]", ref $self);
+}
+# }}}
+
+
+#Parse the templates, get things ready to go.
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+ return (0, $self->loc("Prepare Stubbed"));
+}
+# }}}
+
+
+#If this rule applies to this transaction, return true.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ return(undef);
+}
+# }}}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self = shift;
+
+ # We need to clean up all the references that might maybe get
+ # oddly circular
+ $self->{'ScripActionObj'} = undef;
+ $self->{'ScripObj'} = undef;
+ $self->{'TemplateObj'} =undef
+ $self->{'TicketObj'} = undef;
+ $self->{'TransactionObj'} = undef;
+}
+
+# }}}
+
+eval "require RT::Action::Generic_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Vendor.pm});
+eval "require RT::Action::Generic_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm
new file mode 100755
index 0000000..8a7d7c9
--- /dev/null
+++ b/rt/lib/RT/Action/Notify.pm
@@ -0,0 +1,179 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+#
+package RT::Action::Notify;
+require RT::Action::SendEmail;
+use Mail::Address;
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::SendEmail);
+
+
+=head2 Prepare
+
+Set up the relevant recipients, then call our parent.
+
+=cut
+
+
+sub Prepare {
+ my $self = shift;
+ $self->SetRecipients();
+ $self->SUPER::Prepare();
+}
+
+# {{{ sub SetRecipients
+
+=head2 SetRecipients
+
+Sets the recipients of this meesage to Owner, Requestor, AdminCc, Cc or All.
+Explicitly B<does not> notify the creator of the transaction by default
+
+=cut
+
+sub SetRecipients {
+ my $self = shift;
+
+ my $arg = $self->Argument;
+
+ $arg =~ s/\bAll\b/Owner,Requestor,AdminCc,Cc/;
+
+ my ( @To, @PseudoTo, @Cc, @Bcc );
+
+
+ if ( $arg =~ /\bOtherRecipients\b/ ) {
+ if ( $self->TransactionObj->Attachments->First ) {
+ my @cc_addresses = Mail::Address->parse($self->TransactionObj->Attachments->First->GetHeader('RT-Send-Cc'));
+ foreach my $addr (@cc_addresses) {
+ push @Cc, $addr->address;
+ }
+ my @bcc_addresses = Mail::Address->parse($self->TransactionObj->Attachments->First->GetHeader('RT-Send-Bcc'));
+
+ foreach my $addr (@bcc_addresses) {
+ push @Bcc, $addr->address;
+ }
+
+ }
+ }
+
+ if ( $arg =~ /\bRequestor\b/ ) {
+ push ( @To, $self->TicketObj->Requestors->MemberEmailAddresses );
+ }
+
+
+
+ if ( $arg =~ /\bCc\b/ ) {
+
+ #If we have a To, make the Ccs, Ccs, otherwise, promote them to To
+ if (@To) {
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
+ }
+ else {
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @To, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
+ }
+ }
+
+ if ( ( $arg =~ /\bOwner\b/ )
+ && ( $self->TicketObj->OwnerObj->id != $RT::Nobody->id ) )
+ {
+
+ # If we're not sending to Ccs or requestors,
+ # then the Owner can be the To.
+ if (@To) {
+ push ( @Bcc, $self->TicketObj->OwnerObj->EmailAddress );
+ }
+ else {
+ push ( @To, $self->TicketObj->OwnerObj->EmailAddress );
+ }
+
+ }
+
+ if ( $arg =~ /\bAdminCc\b/ ) {
+ push ( @Bcc, $self->TicketObj->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $self->TicketObj->QueueObj->AdminCc->MemberEmailAddresses );
+ }
+
+ if ($RT::UseFriendlyToLine) {
+ unless (@To) {
+ push (
+ @PseudoTo,
+ sprintf($RT::FriendlyToLineFormat, $arg, $self->TicketObj->id),
+ );
+ }
+ }
+
+ my $creator = $self->TransactionObj->CreatorObj->EmailAddress();
+
+ #Strip the sender out of the To, Cc and AdminCc and set the
+ # recipients fields used to build the message by the superclass.
+ # unless a flag is set
+ if ($RT::NotifyActor) {
+ @{ $self->{'To'} } = @To;
+ @{ $self->{'Cc'} } = @Cc;
+ @{ $self->{'Bcc'} } = @Bcc;
+ }
+ else {
+ @{ $self->{'To'} } = grep ( lc $_ ne lc $creator, @To );
+ @{ $self->{'Cc'} } = grep ( lc $_ ne lc $creator, @Cc );
+ @{ $self->{'Bcc'} } = grep ( lc $_ ne lc $creator, @Bcc );
+ }
+ @{ $self->{'PseudoTo'} } = @PseudoTo;
+
+
+}
+
+# }}}
+
+eval "require RT::Action::Notify_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Notify_Vendor.pm});
+eval "require RT::Action::Notify_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Notify_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm
new file mode 100755
index 0000000..d74b21d
--- /dev/null
+++ b/rt/lib/RT/Action/NotifyAsComment.pm
@@ -0,0 +1,79 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Action::NotifyAsComment;
+require RT::Action::Notify;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Notify);
+
+
+=head2 SetReturnAddress
+
+Tell SendEmail that this message should come out as a comment.
+Calls SUPER::SetReturnAddress.
+
+=cut
+
+sub SetReturnAddress {
+ my $self = shift;
+
+ # Tell RT::Action::SendEmail that this should come
+ # from the relevant comment email address.
+ $self->{'comment'} = 1;
+
+ return($self->SUPER::SetReturnAddress(is_comment => 1));
+}
+
+eval "require RT::Action::NotifyAsComment_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/NotifyAsComment_Vendor.pm});
+eval "require RT::Action::NotifyAsComment_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/NotifyAsComment_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Action/RecordComment.pm b/rt/lib/RT/Action/RecordComment.pm
new file mode 100644
index 0000000..285b33f
--- /dev/null
+++ b/rt/lib/RT/Action/RecordComment.pm
@@ -0,0 +1,120 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Action::RecordComment;
+require RT::Action::Generic;
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
+
+=head1 NAME
+
+RT::Action::RecordComment - An Action which can be used from an
+external tool, or in any situation where a ticket transaction has not
+been started, to make a comment on the ticket.
+
+=head1 SYNOPSIS
+
+my $action_obj = RT::Action::RecordComment->new('TicketObj' => $ticket_obj,
+ 'TemplateObj' => $template_obj,
+ );
+my $result = $action_obj->Prepare();
+$action_obj->Commit() if $result;
+
+=head1 METHODS
+
+=head2 Prepare
+
+Check for the existence of a Transaction. If a Transaction already
+exists, and is of type "Comment" or "Correspond", abort because that
+will give us a loop.
+
+=cut
+
+
+sub Prepare {
+ my $self = shift;
+ if (defined $self->{'TransactionObj'} &&
+ $self->{'TransactionObj'}->Type =~ /^(Comment|Correspond)$/) {
+ return undef;
+ }
+ return 1;
+}
+
+=head2 Commit
+
+Create a Transaction by calling the ticket's Comment method on our
+parsed Template, which may have an RT-Send-Cc or RT-Send-Bcc header.
+The Transaction will be of type Comment. This Transaction can then be
+used by the scrips that actually send the email.
+
+=cut
+
+sub Commit {
+ my $self = shift;
+ $self->CreateTransaction();
+}
+
+sub CreateTransaction {
+ my $self = shift;
+
+ my ($result, $msg) = $self->{'TemplateObj'}->Parse(
+ TicketObj => $self->{'TicketObj'});
+ return undef unless $result;
+
+ my ($trans, $desc, $transaction) = $self->{'TicketObj'}->Comment(
+ MIMEObj => $self->TemplateObj->MIMEObj);
+ $self->{'TransactionObj'} = $transaction;
+}
+
+
+eval "require RT::Action::RecordComment_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordComment_Vendor.pm});
+eval "require RT::Action::RecordComment_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordComment_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/RecordCorrespondence.pm b/rt/lib/RT/Action/RecordCorrespondence.pm
new file mode 100644
index 0000000..c01c89a
--- /dev/null
+++ b/rt/lib/RT/Action/RecordCorrespondence.pm
@@ -0,0 +1,121 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Action::RecordCorrespondence;
+require RT::Action::Generic;
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
+
+=head1 NAME
+
+RT::Action::RecordCorrespondence - An Action which can be used from an
+external tool, or in any situation where a ticket transaction has not
+been started, to make a comment on the ticket.
+
+=head1 SYNOPSIS
+
+my $action_obj = RT::Action::RecordCorrespondence->new(
+ 'TicketObj' => $ticket_obj,
+ 'TemplateObj' => $template_obj,
+ );
+my $result = $action_obj->Prepare();
+$action_obj->Commit() if $result;
+
+=head1 METHODS
+
+=head2 Prepare
+
+Check for the existence of a Transaction. If a Transaction already
+exists, and is of type "Comment" or "Correspond", abort because that
+will give us a loop.
+
+=cut
+
+
+sub Prepare {
+ my $self = shift;
+ if (defined $self->{'TransactionObj'} &&
+ $self->{'TransactionObj'}->Type =~ /^(Comment|Correspond)$/) {
+ return undef;
+ }
+ return 1;
+}
+
+=head2 Commit
+
+Create a Transaction by calling the ticket's Correspond method on our
+parsed Template, which may have an RT-Send-Cc or RT-Send-Bcc header.
+The Transaction will be of type Correspond. This Transaction can then
+be used by the scrips that actually send the email.
+
+=cut
+
+sub Commit {
+ my $self = shift;
+ $self->CreateTransaction();
+}
+
+sub CreateTransaction {
+ my $self = shift;
+
+ my ($result, $msg) = $self->{'TemplateObj'}->Parse(
+ TicketObj => $self->{'TicketObj'});
+ return undef unless $result;
+
+ my ($trans, $desc, $transaction) = $self->{'TicketObj'}->Correspond(
+ MIMEObj => $self->TemplateObj->MIMEObj);
+ $self->{'TransactionObj'} = $transaction;
+}
+
+
+eval "require RT::Action::RecordCorrespondence_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordCorrespondence_Vendor.pm});
+eval "require RT::Action::RecordCorrespondence_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/RecordCorrespondence_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/ResolveMembers.pm b/rt/lib/RT/Action/ResolveMembers.pm
new file mode 100644
index 0000000..a28d88d
--- /dev/null
+++ b/rt/lib/RT/Action/ResolveMembers.pm
@@ -0,0 +1,112 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+# This Action will resolve all members of a resolved group ticket
+
+package RT::Action::ResolveMembers;
+require RT::Action::Generic;
+require RT::Links;
+
+use strict;
+use vars qw/@ISA/;
+@ISA=qw(RT::Action::Generic);
+
+#Do what we need to do and send it out.
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return $self->loc("[_1] will resolve all members of a resolved group ticket.", ref $self);
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ # nothing to prepare
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+
+ my $Links=RT::Links->new($RT::SystemUser);
+ $Links->Limit(FIELD => 'Type', VALUE => 'MemberOf');
+ $Links->Limit(FIELD => 'Target', VALUE => $self->TicketObj->id);
+
+ while (my $Link=$Links->Next()) {
+ # Todo: Try to deal with remote URIs as well
+ next unless $Link->BaseURI->IsLocal;
+ my $base=RT::Ticket->new($self->TicketObj->CurrentUser);
+ # Todo: Only work if Base is a plain ticket num:
+ $base->Load($Link->Base);
+ # I'm afraid this might be a major bottleneck if ResolveGroupTicket is on.
+ $base->Resolve;
+ }
+}
+
+
+# Applicability checked in Commit.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ 1;
+ return 1;
+}
+# }}}
+
+eval "require RT::Action::ResolveMembers_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/ResolveMembers_Vendor.pm});
+eval "require RT::Action::ResolveMembers_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/ResolveMembers_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm
new file mode 100755
index 0000000..cfa9139
--- /dev/null
+++ b/rt/lib/RT/Action/SendEmail.pm
@@ -0,0 +1,1004 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Portions Copyright 2000 Tobias Brox <tobix@cpan.org>
+
+package RT::Action::SendEmail;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
+
+use MIME::Words qw(encode_mimeword);
+
+use RT::EmailParser;
+use Mail::Address;
+use Date::Format qw(strftime);
+
+=head1 NAME
+
+RT::Action::SendEmail - An Action which users can use to send mail
+or can subclassed for more specialized mail sending behavior.
+RT::Action::AutoReply is a good example subclass.
+
+=head1 SYNOPSIS
+
+ require RT::Action::SendEmail;
+ @ISA = qw(RT::Action::SendEmail);
+
+
+=head1 DESCRIPTION
+
+Basically, you create another module RT::Action::YourAction which ISA
+RT::Action::SendEmail.
+
+=begin testing
+
+ok (require RT::Action::SendEmail);
+
+=end testing
+
+
+=head1 AUTHOR
+
+Jesse Vincent <jesse@bestpractical.com> and Tobias Brox <tobix@cpan.org>
+
+=head1 SEE ALSO
+
+perl(1).
+
+=cut
+
+# {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable)
+
+
+# {{{ sub Commit
+
+sub Commit {
+ # DO NOT SHIFT @_ in this subroutine. It breaks Hook::LexWrap's
+ # ability to pass @_ to a 'post' routine.
+ my $self = $_[0];
+
+ my ($ret) = $self->SendMessage( $self->TemplateObj->MIMEObj );
+ if ( $ret > 0 ) {
+ $self->RecordOutgoingMailTransaction( $self->TemplateObj->MIMEObj )
+ if ($RT::RecordOutgoingEmail);
+ }
+ return (abs $ret);
+}
+
+# }}}
+
+# {{{ sub Prepare
+
+sub Prepare {
+ my $self = shift;
+
+ my ( $result, $message ) = $self->TemplateObj->Parse(
+ Argument => $self->Argument,
+ TicketObj => $self->TicketObj,
+ TransactionObj => $self->TransactionObj
+ );
+ if ( !$result ) {
+ return (undef);
+ }
+
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
+
+ # Header
+ $self->SetRTSpecialHeaders();
+
+ $self->RemoveInappropriateRecipients();
+
+ my %seen;
+ foreach my $type qw(To Cc Bcc) {
+ @{ $self->{ $type } } =
+ grep defined && length && !$seen{ lc $_ }++,
+ @{ $self->{ $type } };
+ }
+
+ # Go add all the Tos, Ccs and Bccs that we need to to the message to
+ # make it happy, but only if we actually have values in those arrays.
+
+ # TODO: We should be pulling the recipients out of the template and shove them into To, Cc and Bcc
+
+ $self->SetHeader( 'To', join ( ', ', @{ $self->{'To'} } ) )
+ if ( ! $MIMEObj->head->get('To') && $self->{'To'} && @{ $self->{'To'} } );
+ $self->SetHeader( 'Cc', join ( ', ', @{ $self->{'Cc'} } ) )
+ if ( !$MIMEObj->head->get('Cc') && $self->{'Cc'} && @{ $self->{'Cc'} } );
+ $self->SetHeader( 'Bcc', join ( ', ', @{ $self->{'Bcc'} } ) )
+ if ( !$MIMEObj->head->get('Bcc') && $self->{'Bcc'} && @{ $self->{'Bcc'} } );
+
+ # PseudoTo (fake to headers) shouldn't get matched for message recipients.
+ # If we don't have any 'To' header (but do have other recipients), drop in
+ # the pseudo-to header.
+ $self->SetHeader( 'To', join ( ', ', @{ $self->{'PseudoTo'} } ) )
+ if ( $self->{'PseudoTo'} && ( @{ $self->{'PseudoTo'} } )
+ and ( !$MIMEObj->head->get('To') ) ) and ( $MIMEObj->head->get('Cc') or $MIMEObj->head->get('Bcc'));
+
+ # We should never have to set the MIME-Version header
+ $self->SetHeader( 'MIME-Version', '1.0' );
+
+ # fsck.com #5959: Since RT sends 8bit mail, we should say so.
+ $self->SetHeader( 'Content-Transfer-Encoding','8bit');
+
+ # For security reasons, we only send out textual mails.
+ my @parts = $MIMEObj;
+ while (my $part = shift @parts) {
+ if ($part->is_multipart) {
+ push @parts, $part->parts;
+ }
+ else {
+ if ( RT::I18N::IsTextualContentType( $part->mime_type ) ) {
+ $part->head->mime_attr( "Content-Type" => $part->mime_type )
+ } else {
+ $part->head->mime_attr( "Content-Type" => 'text/plain' );
+ }
+ $part->head->mime_attr( "Content-Type.charset" => 'utf-8' );
+ }
+ }
+
+
+ RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, $RT::EmailOutputEncoding, 'mime_words_ok' );
+
+ # Build up a MIME::Entity that looks like the original message.
+ $self->AddAttachments() if ( $MIMEObj->head->get('RT-Attach-Message') );
+
+ return $result;
+
+}
+
+# }}}
+
+# }}}
+
+
+
+=head2 To
+
+Returns an array of Mail::Address objects containing all the To: recipients for this notification
+
+=cut
+
+sub To {
+ my $self = shift;
+ return ($self->_AddressesFromHeader('To'));
+}
+
+=head2 Cc
+
+Returns an array of Mail::Address objects containing all the Cc: recipients for this notification
+
+=cut
+
+sub Cc {
+ my $self = shift;
+ return ($self->_AddressesFromHeader('Cc'));
+}
+
+=head2 Bcc
+
+Returns an array of Mail::Address objects containing all the Bcc: recipients for this notification
+
+=cut
+
+
+sub Bcc {
+ my $self = shift;
+ return ($self->_AddressesFromHeader('Bcc'));
+
+}
+
+sub _AddressesFromHeader {
+ my $self = shift;
+ my $field = shift;
+ my $header = $self->TemplateObj->MIMEObj->head->get($field);
+ my @addresses = Mail::Address->parse($header);
+
+ return (@addresses);
+}
+
+
+# {{{ SendMessage
+
+=head2 SendMessage MIMEObj
+
+sends the message using RT's preferred API.
+TODO: Break this out to a separate module
+
+=cut
+
+sub SendMessage {
+ # DO NOT SHIFT @_ in this subroutine. It breaks Hook::LexWrap's
+ # ability to pass @_ to a 'post' routine.
+ my ( $self, $MIMEObj ) = @_;
+
+ my $msgid = $MIMEObj->head->get('Message-ID');
+ chomp $msgid;
+
+ $self->ScripActionObj->{_Message_ID}++;
+
+ $RT::Logger->info( $msgid . " #"
+ . $self->TicketObj->id . "/"
+ . $self->TransactionObj->id
+ . " - Scrip "
+ . $self->ScripObj->id . " "
+ . $self->ScripObj->Description );
+
+ #If we don't have any recipients to send to, don't send a message;
+ unless ( $MIMEObj->head->get('To')
+ || $MIMEObj->head->get('Cc')
+ || $MIMEObj->head->get('Bcc') )
+ {
+ $RT::Logger->info( $msgid . " No recipients found. Not sending.\n" );
+ return (-1);
+ }
+
+ unless ($MIMEObj->head->get('Date')) {
+ # We coerce localtime into an array since strftime has a flawed prototype that only accepts
+ # a list
+ $MIMEObj->head->replace(Date => strftime('%a, %d %b %Y %H:%M:%S %z', @{[localtime()]}));
+ }
+
+ return (0) unless ($self->OutputMIMEObject($MIMEObj));
+
+ my $success = $msgid . " sent ";
+ foreach( qw(To Cc Bcc) ) {
+ my $recipients = $MIMEObj->head->get($_);
+ $success .= " $_: ". $recipients if $recipients;
+ }
+ $success =~ s/\n//g;
+
+ $RT::Logger->info($success);
+
+ return (1);
+}
+
+
+=head2 OutputMIMEObject MIME::Entity
+
+Sends C<MIME::Entity> as an email message according to RT's mailer configuration.
+
+=cut
+
+
+
+sub OutputMIMEObject {
+ my $self = shift;
+ my $MIMEObj = shift;
+
+ my $msgid = $MIMEObj->head->get('Message-ID');
+ chomp $msgid;
+
+ my $SendmailArguments = $RT::SendmailArguments;
+ if (defined $RT::VERPPrefix && defined $RT::VERPDomain) {
+ my $EnvelopeFrom = $self->TransactionObj->CreatorObj->EmailAddress;
+ $EnvelopeFrom =~ s/@/=/g;
+ $EnvelopeFrom =~ s/\s//g;
+ $SendmailArguments .= " -f ${RT::VERPPrefix}${EnvelopeFrom}\@${RT::VERPDomain}";
+ }
+
+
+ if ( $RT::MailCommand eq 'sendmailpipe' ) {
+ eval {
+ # don't ignore CHLD signal to get proper exit code
+ local $SIG{'CHLD'} = 'DEFAULT';
+
+ my $mail;
+ unless( open $mail, "|$RT::SendmailPath $SendmailArguments" ) {
+ die "Couldn't run $RT::SendmailPath: $!";
+ }
+
+ # if something wrong with $mail->print we will get PIPE signal, handle it
+ local $SIG{'PIPE'} = sub { die "$RT::SendmailPath closed pipe" };
+ $MIMEObj->print($mail);
+
+ unless ( close $mail ) {
+ die "Close failed: $!" if $!; # system error
+ # sendmail exit statuses mostly errors with data not software
+ # TODO: status parsing: core dump, exit on signal or EX_*
+ $RT::Logger->warning( "$RT::SendmailPath exitted with status $?" );
+ }
+ };
+ if ($@) {
+ $RT::Logger->crit( $msgid . "Could not send mail: " . $@ );
+ return 0;
+ }
+ }
+ else {
+ my @mailer_args = ($RT::MailCommand);
+
+ local $ENV{MAILADDRESS};
+
+ if ( $RT::MailCommand eq 'sendmail' ) {
+ push @mailer_args, split(/\s+/, $SendmailArguments);
+ }
+ elsif ( $RT::MailCommand eq 'smtp' ) {
+ $ENV{MAILADDRESS} = $RT::SMTPFrom || $MIMEObj->head->get('From');
+ push @mailer_args, ( Server => $RT::SMTPServer );
+ push @mailer_args, ( Debug => $RT::SMTPDebug );
+ }
+ else {
+ push @mailer_args, $RT::MailParams;
+ }
+
+ unless ( $MIMEObj->send(@mailer_args) ) {
+ $RT::Logger->crit( $msgid . "Could not send mail." );
+ return (0);
+ }
+ }
+ return 1;
+}
+
+# }}}
+
+# {{{ AddAttachments
+
+=head2 AddAttachments
+
+Takes any attachments to this transaction and attaches them to the message
+we're building.
+
+=cut
+
+
+sub AddAttachments {
+ my $self = shift;
+
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
+
+ $MIMEObj->head->delete('RT-Attach-Message');
+
+ my $attachments = RT::Attachments->new($RT::SystemUser);
+ $attachments->Limit(
+ FIELD => 'TransactionId',
+ VALUE => $self->TransactionObj->Id
+ );
+ $attachments->OrderBy( FIELD => 'id');
+
+ my $transaction_content_obj = $self->TransactionObj->ContentObj;
+
+ # attach any of this transaction's attachments
+ while ( my $attach = $attachments->Next ) {
+
+ # Don't attach anything blank
+ next unless ( $attach->ContentLength );
+
+# We want to make sure that we don't include the attachment that's being used as the "Content" of this message.
+ next
+ if ( $transaction_content_obj
+ && $transaction_content_obj->Id == $attach->Id
+ && $transaction_content_obj->ContentType =~ qr{text/plain}i );
+ $MIMEObj->make_multipart('mixed');
+ $MIMEObj->attach(
+ Type => $attach->ContentType,
+ Charset => $attach->OriginalEncoding,
+ Data => $attach->OriginalContent,
+ Filename => $self->MIMEEncodeString( $attach->Filename,
+ $RT::EmailOutputEncoding ),
+ 'RT-Attachment:' => $self->TicketObj->Id."/".$self->TransactionObj->Id."/".$attach->id,
+ Encoding => '-SUGGEST'
+ );
+ }
+
+}
+
+# }}}
+
+# {{{ RecordOutgoingMailTransaction
+
+=head2 RecordOutgoingMailTransaction MIMEObj
+
+Record a transaction in RT with this outgoing message for future record-keeping purposes
+
+=cut
+
+
+
+sub RecordOutgoingMailTransaction {
+ my $self = shift;
+ my $MIMEObj = shift;
+
+
+ my @parts = $MIMEObj->parts;
+ my @attachments;
+ my @keep;
+ foreach my $part (@parts) {
+ my $attach = $part->head->get('RT-Attachment');
+ if ($attach) {
+ $RT::Logger->debug("We found an attachment. we want to not record it.");
+ push @attachments, $attach;
+ } else {
+ $RT::Logger->debug("We found a part. we want to record it.");
+ push @keep, $part;
+ }
+ }
+ $MIMEObj->parts(\@keep);
+ foreach my $attachment (@attachments) {
+ $MIMEObj->head->add('RT-Attachment', $attachment);
+ }
+
+ RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, 'utf-8', 'mime_words_ok' );
+
+ my $transaction = RT::Transaction->new($self->TransactionObj->CurrentUser);
+
+ # XXX: TODO -> Record attachments as references to things in the attachments table, maybe.
+
+ my $type;
+ if ($self->TransactionObj->Type eq 'Comment') {
+ $type = 'CommentEmailRecord';
+ } else {
+ $type = 'EmailRecord';
+ }
+
+ my $msgid = $MIMEObj->head->get('Message-ID');
+ chomp $msgid;
+
+ my ( $id, $msg ) = $transaction->Create(
+ Ticket => $self->TicketObj->Id,
+ Type => $type,
+ Data => $msgid,
+ MIMEObj => $MIMEObj,
+ ActivateScrips => 0
+ );
+
+ if( $id ) {
+ $self->{'OutgoingMailTransaction'} = $id;
+ } else {
+ $RT::Logger->warning( "Could not record outgoing message transaction: $msg" );
+ }
+ return $id;
+}
+
+# }}}
+#
+
+# {{{ sub SetRTSpecialHeaders
+
+=head2 SetRTSpecialHeaders
+
+This routine adds all the random headers that RT wants in a mail message
+that don't matter much to anybody else.
+
+=cut
+
+sub SetRTSpecialHeaders {
+ my $self = shift;
+
+ $self->SetSubject();
+ $self->SetSubjectToken();
+ $self->SetHeaderAsEncoding( 'Subject', $RT::EmailOutputEncoding )
+ if ($RT::EmailOutputEncoding);
+ $self->SetReturnAddress();
+ $self->SetReferencesHeaders();
+
+ unless ($self->TemplateObj->MIMEObj->head->get('Message-ID')) {
+ # Get Message-ID for this txn
+ my $msgid = "";
+ $msgid = $self->TransactionObj->Message->First->GetHeader("RT-Message-ID")
+ || $self->TransactionObj->Message->First->GetHeader("Message-ID")
+ if $self->TransactionObj->Message && $self->TransactionObj->Message->First;
+
+ # If there is one, and we can parse it, then base our Message-ID on it
+ if ($msgid
+ and $msgid =~ s/<(rt-.*?-\d+-\d+)\.(\d+)-\d+-\d+\@\Q$RT::Organization\E>$/
+ "<$1." . $self->TicketObj->id
+ . "-" . $self->ScripObj->id
+ . "-" . $self->ScripActionObj->{_Message_ID}
+ . "@" . $RT::Organization . ">"/eg
+ and $2 == $self->TicketObj->id) {
+ $self->SetHeader( "Message-ID" => $msgid );
+ } else {
+ $self->SetHeader( 'Message-ID',
+ "<rt-"
+ . $RT::VERSION . "-"
+ . $$ . "-"
+ . CORE::time() . "-"
+ . int(rand(2000)) . '.'
+ . $self->TicketObj->id . "-"
+ . $self->ScripObj->id . "-" # Scrip
+ . $self->ScripActionObj->{_Message_ID} . "@" # Email sent
+ . $RT::Organization
+ . ">" );
+ }
+ }
+
+ $self->SetHeader( 'Precedence', "bulk" )
+ unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") );
+
+ $self->SetHeader( 'X-RT-Loop-Prevention', $RT::rtname );
+ $self->SetHeader( 'RT-Ticket',
+ $RT::rtname . " #" . $self->TicketObj->id() );
+ $self->SetHeader( 'Managed-by',
+ "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
+
+ $self->SetHeader( 'RT-Originator',
+ $self->TransactionObj->CreatorObj->EmailAddress );
+
+}
+
+# }}}
+
+
+# }}}
+
+# {{{ RemoveInappropriateRecipients
+
+=head2 RemoveInappropriateRecipients
+
+Remove addresses that are RT addresses or that are on this transaction's blacklist
+
+=cut
+
+sub RemoveInappropriateRecipients {
+ my $self = shift;
+
+ my $msgid = $self->TemplateObj->MIMEObj->head->get ('Message-Id');
+
+
+
+ my @blacklist;
+
+ my @types = qw/To Cc Bcc/;
+
+ # Weed out any RT addresses. We really don't want to talk to ourselves!
+ foreach my $type (@types) {
+ @{ $self->{$type} } =
+ RT::EmailParser::CullRTAddresses( "", @{ $self->{$type} } );
+ }
+
+ # If there are no recipients, don't try to send the message.
+ # If the transaction has content and has the header RT-Squelch-Replies-To
+
+ if ( $self->TransactionObj->Attachments->First() ) {
+ if (
+ $self->TransactionObj->Attachments->First->GetHeader(
+ 'RT-DetectedAutoGenerated')
+ )
+ {
+
+ # What do we want to do with this? It's probably (?) a bounce
+ # caused by one of the watcher addresses being broken.
+ # Default ("true") is to redistribute, for historical reasons.
+
+ if ( !$RT::RedistributeAutoGeneratedMessages ) {
+
+ # Don't send to any watchers.
+ @{ $self->{'To'} } = ();
+ @{ $self->{'Cc'} } = ();
+ @{ $self->{'Bcc'} } = ();
+
+ $RT::Logger->info( $msgid . " The incoming message was autogenerated. Not redistributing this message based on site configuration.\n");
+ }
+ elsif ( $RT::RedistributeAutoGeneratedMessages eq 'privileged' ) {
+
+ # Only send to "privileged" watchers.
+ #
+
+ foreach my $type (@types) {
+
+ foreach my $addr ( @{ $self->{$type} } ) {
+ my $user = RT::User->new($RT::SystemUser);
+ $user->LoadByEmail($addr);
+ @{ $self->{$type} } =
+ grep ( !/^\Q$addr\E$/, @{ $self->{$type} } )
+ if ( !$user->Privileged );
+
+ }
+ }
+ $RT::Logger->info( $msgid . " The incoming message was autogenerated. Not redistributing this message to unprivileged users based on site configuration.\n");
+
+ }
+
+ }
+
+ my $squelch =
+ $self->TransactionObj->Attachments->First->GetHeader(
+ 'RT-Squelch-Replies-To');
+
+ if ($squelch) {
+ @blacklist = split( /,/, $squelch );
+ }
+ }
+
+ # Let's grab the SquelchMailTo attribue and push those entries into the @blacklist
+ my @non_recipients = $self->TicketObj->SquelchMailTo;
+ foreach my $attribute (@non_recipients) {
+ push @blacklist, $attribute->Content;
+ }
+
+ # Cycle through the people we're sending to and pull out anyone on the
+ # system blacklist
+
+ foreach my $person_to_yank (@blacklist) {
+ $person_to_yank =~ s/\s//g;
+ foreach my $type (@types) {
+ @{ $self->{$type} } =
+ grep ( !/^\Q$person_to_yank\E$/, @{ $self->{$type} } );
+ }
+ }
+}
+
+# }}}
+# {{{ sub SetReturnAddress
+
+=head2 SetReturnAddress is_comment => BOOLEAN
+
+Calculate and set From and Reply-To headers based on the is_comment flag.
+
+=cut
+
+sub SetReturnAddress {
+
+ my $self = shift;
+ my %args = (
+ is_comment => 0,
+ @_
+ );
+
+ # From and Reply-To
+ # $args{is_comment} should be set if the comment address is to be used.
+ my $replyto;
+
+ if ( $args{'is_comment'} ) {
+ $replyto = $self->TicketObj->QueueObj->CommentAddress
+ || $RT::CommentAddress;
+ }
+ else {
+ $replyto = $self->TicketObj->QueueObj->CorrespondAddress
+ || $RT::CorrespondAddress;
+ }
+
+ unless ( $self->TemplateObj->MIMEObj->head->get('From') ) {
+ if ($RT::UseFriendlyFromLine) {
+ my $friendly_name = $self->TransactionObj->CreatorObj->RealName
+ || $self->TransactionObj->CreatorObj->Name;
+ if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string
+ $friendly_name = $1;
+ }
+
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader(
+ 'From',
+ sprintf(
+ $RT::FriendlyFromLineFormat,
+ $self->MIMEEncodeString( $friendly_name,
+ $RT::EmailOutputEncoding ),
+ $replyto
+ ),
+ );
+ }
+ else {
+ $self->SetHeader( 'From', $replyto );
+ }
+ }
+
+ unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) {
+ $self->SetHeader( 'Reply-To', "$replyto" );
+ }
+
+}
+
+# }}}
+
+# {{{ sub SetHeader
+
+=head2 SetHeader FIELD, VALUE
+
+Set the FIELD of the current MIME object into VALUE.
+
+=cut
+
+sub SetHeader {
+ my $self = shift;
+ my $field = shift;
+ my $val = shift;
+
+ chomp $val;
+ chomp $field;
+ $self->TemplateObj->MIMEObj->head->fold_length( $field, 10000 );
+ $self->TemplateObj->MIMEObj->head->replace( $field, $val );
+ return $self->TemplateObj->MIMEObj->head->get($field);
+}
+
+# }}}
+
+
+# {{{ sub SetSubject
+
+=head2 SetSubject
+
+This routine sets the subject. it does not add the rt tag. that gets done elsewhere
+If $self->{'Subject'} is already defined, it uses that. otherwise, it tries to get
+the transaction's subject.
+
+=cut
+
+sub SetSubject {
+ my $self = shift;
+ my $subject;
+
+ my $message = $self->TransactionObj->Attachments;
+ if ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
+ return ();
+ }
+ if ( $self->{'Subject'} ) {
+ $subject = $self->{'Subject'};
+ }
+ elsif ( ( $message->First() ) && ( $message->First->Headers ) ) {
+ my $header = $message->First->Headers();
+ $header =~ s/\n\s+/ /g;
+ if ( $header =~ /^Subject: (.*?)$/m ) {
+ $subject = $1;
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
+
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
+
+ $subject =~ s/(\r\n|\n|\s)/ /gi;
+
+ chomp $subject;
+ $self->SetHeader( 'Subject', $subject );
+
+}
+
+# }}}
+
+# {{{ sub SetSubjectToken
+
+=head2 SetSubjectToken
+
+This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
+
+=cut
+
+sub SetSubjectToken {
+ my $self = shift;
+ my $sub = $self->TemplateObj->MIMEObj->head->get('Subject');
+ my $id = $self->TicketObj->id;
+
+ my $token_re = $RT::EmailSubjectTagRegex;
+ $token_re = qr/\Q$RT::rtname\E/o unless $token_re;
+ return if $sub =~ /\[$token_re\s+#$id\]/;
+
+ $sub =~ s/(\r\n|\n|\s)/ /gi;
+ chomp $sub;
+ $self->TemplateObj->MIMEObj->head->replace(
+ Subject => "[$RT::rtname #$id] $sub",
+ );
+}
+
+# }}}
+
+=head2 SetReferencesHeaders
+
+Set References and In-Reply-To headers for this message.
+
+=cut
+
+sub SetReferencesHeaders {
+
+ my $self = shift;
+ my ( @in_reply_to, @references, @msgid );
+
+ my $attachments = $self->TransactionObj->Message;
+
+ if ( my $top = $attachments->First() ) {
+ @in_reply_to = split(/\s+/m, $top->GetHeader('In-Reply-To') || '');
+ @references = split(/\s+/m, $top->GetHeader('References') || '' );
+ @msgid = split(/\s+/m, $top->GetHeader('Message-ID') || '');
+ }
+ else {
+ return (undef);
+ }
+
+ # There are two main cases -- this transaction was created with
+ # the RT Web UI, and hence we want to *not* append its Message-ID
+ # to the References and In-Reply-To. OR it came from an outside
+ # source, and we should treat it as per the RFC
+ if ( "@msgid" =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@$RT::Organization>/) {
+
+ # Make all references which are internal be to version which we
+ # have sent out
+ for (@references, @in_reply_to) {
+ s/<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@$RT::Organization>$/
+ "<$1." . $self->TicketObj->id .
+ "-" . $self->ScripObj->id .
+ "-" . $self->ScripActionObj->{_Message_ID} .
+ "@" . $RT::Organization . ">"/eg
+ }
+
+ # In reply to whatever the internal message was in reply to
+ $self->SetHeader( 'In-Reply-To', join( " ", ( @in_reply_to )));
+
+ # Default the references to whatever we're in reply to
+ @references = @in_reply_to unless @references;
+
+ # References are unchanged from internal
+ } else {
+ # In reply to that message
+ $self->SetHeader( 'In-Reply-To', join( " ", ( @msgid )));
+
+ # Default the references to whatever we're in reply to
+ @references = @in_reply_to unless @references;
+
+ # Push that message onto the end of the references
+ push @references, @msgid;
+ }
+
+ # Push pseudo-ref to the front
+ my $pseudo_ref = $self->PseudoReference;
+ @references = ($pseudo_ref, grep { $_ ne $pseudo_ref } @references);
+
+ # If there are more than 10 references headers, remove all but the
+ # first four and the last six (Gotta keep this from growing
+ # forever)
+ splice(@references, 4, -6) if ($#references >= 10);
+
+ # Add on the references
+ $self->SetHeader( 'References', join( " ", @references) );
+ $self->TemplateObj->MIMEObj->head->fold_length( 'References', 80 );
+
+}
+
+# }}}
+
+=head2 PseudoReference
+
+Returns a fake Message-ID: header for the ticket to allow a base level of threading
+
+=cut
+
+sub PseudoReference {
+
+ my $self = shift;
+ my $pseudo_ref = '<RT-Ticket-'.$self->TicketObj->id .'@'.$RT::Organization .'>';
+ return $pseudo_ref;
+}
+
+
+# {{{ SetHeadingAsEncoding
+
+=head2 SetHeaderAsEncoding($field_name, $charset_encoding)
+
+This routine converts the field into specified charset encoding.
+
+=cut
+
+sub SetHeaderAsEncoding {
+ my $self = shift;
+ my ( $field, $enc ) = ( shift, shift );
+
+ if ($field eq 'From' and $RT::SMTPFrom) {
+ $self->TemplateObj->MIMEObj->head->replace( $field, $RT::SMTPFrom );
+ return;
+ }
+
+ my $value = $self->TemplateObj->MIMEObj->head->get($field);
+
+ $value = $self->MIMEEncodeString($value, $enc);
+
+ $self->TemplateObj->MIMEObj->head->replace( $field, $value );
+
+
+}
+# }}}
+
+# {{{ MIMEEncodeString
+
+=head2 MIMEEncodeString STRING ENCODING
+
+Takes a string and a possible encoding and returns the string wrapped in MIME goo.
+
+=cut
+
+sub MIMEEncodeString {
+ my $self = shift;
+ my $value = shift;
+ # using RFC2047 notation, sec 2.
+ # encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
+ my $charset = shift;
+ my $encoding = 'B';
+ # An 'encoded-word' may not be more than 75 characters long
+ #
+ # MIME encoding increases 4/3*(number of bytes), and always in multiples
+ # of 4. Thus we have to find the best available value of bytes available
+ # for each chunk.
+ #
+ # First we get the integer max which max*4/3 would fit on space.
+ # Then we find the greater multiple of 3 lower or equal than $max.
+ my $max = int(((75-length('=?'.$charset.'?'.$encoding.'?'.'?='))*3)/4);
+ $max = int($max/3)*3;
+
+ chomp $value;
+
+ if ( $max <= 0 ) {
+ # gives an error...
+ $RT::Logger->crit("Can't encode! Charset or encoding too big.\n");
+ return ($value);
+ }
+
+ return ($value) unless $value =~ /[^\x20-\x7e]/;
+
+ $value =~ s/\s*$//;
+
+ # we need perl string to split thing char by char
+ Encode::_utf8_on($value) unless Encode::is_utf8( $value );
+
+ my ($tmp, @chunks) = ('', ());
+ while ( length $value ) {
+ my $char = substr($value, 0, 1, '');
+ my $octets = Encode::encode( $charset, $char );
+ if ( length($tmp) + length($octets) > $max ) {
+ push @chunks, $tmp;
+ $tmp = '';
+ }
+ $tmp .= $octets;
+ }
+ push @chunks, $tmp if length $tmp;
+
+ # encode an join chuncks
+ $value = join "\n ",
+ map encode_mimeword( $_, $encoding, $charset ), @chunks ;
+ return($value);
+}
+
+# }}}
+
+eval "require RT::Action::SendEmail_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm});
+eval "require RT::Action::SendEmail_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Action/SetPriority.pm b/rt/lib/RT/Action/SetPriority.pm
new file mode 100644
index 0000000..4d74cc0
--- /dev/null
+++ b/rt/lib/RT/Action/SetPriority.pm
@@ -0,0 +1,85 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Action::SetPriority;
+require RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA=qw(RT::Action::Generic);
+
+#Do what we need to do and send it out.
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return (ref $self . " will set a ticket's priority to the argument provided.");
+}
+# }}}
+
+
+# {{{ sub Prepare
+sub Prepare {
+ # nothing to prepare
+ return 1;
+}
+# }}}
+
+sub Commit {
+ my $self = shift;
+ $self->TicketObj->SetPriority($self->Argument);
+
+}
+
+eval "require RT::Action::SetPriority_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SetPriority_Vendor.pm});
+eval "require RT::Action::SetPriority_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SetPriority_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Action/UserDefined.pm b/rt/lib/RT/Action/UserDefined.pm
new file mode 100644
index 0000000..6aec928
--- /dev/null
+++ b/rt/lib/RT/Action/UserDefined.pm
@@ -0,0 +1,95 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+package RT::Action::UserDefined;
+use RT::Action::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
+
+=head2 Prepare
+
+This happens on every transaction. it's always applicable
+
+=cut
+
+sub Prepare {
+ my $self = shift;
+ my $retval = eval $self->ScripObj->CustomPrepareCode;
+ if ($@) {
+ $RT::Logger->error("Scrip ".$self->ScripObj->Id. " Prepare failed: ".$@);
+ return (undef);
+ }
+ return ($retval);
+}
+
+=head2 Commit
+
+This happens on every transaction. it's always applicable
+
+=cut
+
+sub Commit {
+ my $self = shift;
+ my $retval = eval $self->ScripObj->CustomCommitCode;
+ if ($@) {
+ $RT::Logger->error("Scrip ".$self->ScripObj->Id. " Commit failed: ".$@);
+ return (undef);
+ }
+ return ($retval);
+}
+
+eval "require RT::Action::UserDefined_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/UserDefined_Vendor.pm});
+eval "require RT::Action::UserDefined_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/UserDefined_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Attachment.pm b/rt/lib/RT/Attachment.pm
new file mode 100755
index 0000000..ac1fcfe
--- /dev/null
+++ b/rt/lib/RT/Attachment.pm
@@ -0,0 +1,396 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Attachment
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Attachment;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Attachments');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'TransactionId'.
+ int(11) 'Parent'.
+ varchar(160) 'MessageId'.
+ varchar(255) 'Subject'.
+ varchar(255) 'Filename'.
+ varchar(80) 'ContentType'.
+ varchar(80) 'ContentEncoding'.
+ longtext 'Content'.
+ longtext 'Headers'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ TransactionId => '0',
+ Parent => '0',
+ MessageId => '',
+ Subject => '',
+ Filename => '',
+ ContentType => '',
+ ContentEncoding => '',
+ Content => '',
+ Headers => '',
+
+ @_);
+ $self->SUPER::Create(
+ TransactionId => $args{'TransactionId'},
+ Parent => $args{'Parent'},
+ MessageId => $args{'MessageId'},
+ Subject => $args{'Subject'},
+ Filename => $args{'Filename'},
+ ContentType => $args{'ContentType'},
+ ContentEncoding => $args{'ContentEncoding'},
+ Content => $args{'Content'},
+ Headers => $args{'Headers'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 TransactionId
+
+Returns the current value of TransactionId.
+(In the database, TransactionId is stored as int(11).)
+
+
+
+=head2 SetTransactionId VALUE
+
+
+Set TransactionId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TransactionId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Parent
+
+Returns the current value of Parent.
+(In the database, Parent is stored as int(11).)
+
+
+
+=head2 SetParent VALUE
+
+
+Set Parent to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Parent will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 MessageId
+
+Returns the current value of MessageId.
+(In the database, MessageId is stored as varchar(160).)
+
+
+
+=head2 SetMessageId VALUE
+
+
+Set MessageId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, MessageId will be stored as a varchar(160).)
+
+
+=cut
+
+
+=head2 Subject
+
+Returns the current value of Subject.
+(In the database, Subject is stored as varchar(255).)
+
+
+
+=head2 SetSubject VALUE
+
+
+Set Subject to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Subject will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Filename
+
+Returns the current value of Filename.
+(In the database, Filename is stored as varchar(255).)
+
+
+
+=head2 SetFilename VALUE
+
+
+Set Filename to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Filename will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 ContentType
+
+Returns the current value of ContentType.
+(In the database, ContentType is stored as varchar(80).)
+
+
+
+=head2 SetContentType VALUE
+
+
+Set ContentType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ContentType will be stored as a varchar(80).)
+
+
+=cut
+
+
+=head2 ContentEncoding
+
+Returns the current value of ContentEncoding.
+(In the database, ContentEncoding is stored as varchar(80).)
+
+
+
+=head2 SetContentEncoding VALUE
+
+
+Set ContentEncoding to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ContentEncoding will be stored as a varchar(80).)
+
+
+=cut
+
+
+=head2 Content
+
+Returns the current value of Content.
+(In the database, Content is stored as longtext.)
+
+
+
+=head2 SetContent VALUE
+
+
+Set Content to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Content will be stored as a longtext.)
+
+
+=cut
+
+
+=head2 Headers
+
+Returns the current value of Headers.
+(In the database, Headers is stored as longtext.)
+
+
+
+=head2 SetHeaders VALUE
+
+
+Set Headers to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Headers will be stored as a longtext.)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ TransactionId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Parent =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ MessageId =>
+ {read => 1, write => 1, sql_type => 12, length => 160, is_blob => 0, is_numeric => 0, type => 'varchar(160)', default => ''},
+ Subject =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Filename =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ ContentType =>
+ {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ ContentEncoding =>
+ {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ Content =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
+ Headers =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::Attachment_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attachment_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attachment_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attachment_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attachment_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attachment_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Attachment_Overlay, RT::Attachment_Vendor, RT::Attachment_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Attachment_Overlay.pm b/rt/lib/RT/Attachment_Overlay.pm
new file mode 100644
index 0000000..c4fe47b
--- /dev/null
+++ b/rt/lib/RT/Attachment_Overlay.pm
@@ -0,0 +1,618 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 SYNOPSIS
+
+ use RT::Attachment;
+
+
+=head1 DESCRIPTION
+
+This module should never be instantiated directly by client code. it's an internal
+module which should only be instantiated through exported APIs in Ticket, Queue and other
+similar objects.
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Attachment);
+
+=end testing
+
+=cut
+
+
+package RT::Attachment;
+
+use strict;
+no warnings qw(redefine);
+
+use MIME::Base64;
+use MIME::QuotedPrint;
+
+
+# {{{ sub _OverlayAccessible
+sub _OverlayAccessible {
+ {
+ TransactionId => { 'read'=>1, 'public'=>1, 'write' => 0 },
+ MessageId => { 'read'=>1, 'write' => 0 },
+ Parent => { 'read'=>1, 'write' => 0 },
+ ContentType => { 'read'=>1, 'write' => 0 },
+ Subject => { 'read'=>1, 'write' => 0 },
+ Content => { 'read'=>1, 'write' => 0 },
+ ContentEncoding => { 'read'=>1, 'write' => 0 },
+ Headers => { 'read'=>1, 'write' => 0 },
+ Filename => { 'read'=>1, 'write' => 0 },
+ Creator => { 'read'=>1, 'auto'=>1, },
+ Created => { 'read'=>1, 'auto'=>1, },
+ };
+}
+# }}}
+
+# {{{ sub TransactionObj
+
+=head2 TransactionObj
+
+Returns the transaction object asscoiated with this attachment.
+
+=cut
+
+sub TransactionObj {
+ require RT::Transaction;
+ my $self=shift;
+ unless (exists $self->{_TransactionObj}) {
+ $self->{_TransactionObj}=RT::Transaction->new($self->CurrentUser);
+ $self->{_TransactionObj}->Load($self->TransactionId);
+ }
+ unless ($self->{_TransactionObj}->Id) {
+ $RT::Logger->crit("Attachment ".$self->id." can't find transaction ".$self->TransactionId." which it is ostensibly part of. That's bad");
+ }
+ return $self->{_TransactionObj};
+}
+
+# }}}
+
+# {{{ sub Create
+
+=head2 Create
+
+Create a new attachment. Takes a paramhash:
+
+ 'Attachment' Should be a single MIME body with optional subparts
+ 'Parent' is an optional id of the parent attachment
+ 'TransactionId' is the mandatory id of the transaction this attachment is associated with.;
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = ( id => 0,
+ TransactionId => 0,
+ Parent => 0,
+ Attachment => undef,
+ @_ );
+
+ #For ease of reference
+ my $Attachment = $args{'Attachment'};
+
+ #if we didn't specify a ticket, we need to bail
+ if ( $args{'TransactionId'} == 0 ) {
+ $RT::Logger->crit( "RT::Attachment->Create couldn't, as you didn't specify a transaction\n" );
+ return (0);
+
+ }
+
+ #If we possibly can, collapse it to a singlepart
+ $Attachment->make_singlepart;
+
+ #Get the subject
+ my $Subject = $Attachment->head->get( 'subject', 0 );
+ defined($Subject) or $Subject = '';
+ chomp($Subject);
+
+ #Get the Message-ID
+ my $MessageId = $Attachment->head->get( 'Message-ID', 0 );
+ defined($MessageId) or $MessageId = '';
+ chomp ($MessageId);
+ $MessageId =~ s/^<(.*)>$/$1/go;
+
+
+ #Get the filename
+ my $Filename = $Attachment->head->recommended_filename;
+
+ # If a message has no bodyhandle, that means that it has subparts (or appears to)
+ # and we should act accordingly.
+ unless ( defined $Attachment->bodyhandle ) {
+
+ my $id = $self->SUPER::Create(
+ TransactionId => $args{'TransactionId'},
+ Parent => 0,
+ ContentType => $Attachment->mime_type,
+ Headers => $Attachment->head->as_string,
+ MessageId => $MessageId,
+ Subject => $Subject
+ );
+
+ unless ($id) {
+ $RT::Logger->crit("Attachment insert failed - ".$RT::Handle->dbh->errstr);
+ }
+
+ foreach my $part ( $Attachment->parts ) {
+ my $SubAttachment = new RT::Attachment( $self->CurrentUser );
+ my ($id) = $SubAttachment->Create(
+ TransactionId => $args{'TransactionId'},
+ Parent => $id,
+ Attachment => $part,
+ );
+ unless ($id) {
+ $RT::Logger->crit("Attachment insert failed - ".$RT::Handle->dbh->errstr);
+ }
+ }
+ return ($id);
+ }
+
+ #If it's not multipart
+ else {
+
+ my ($ContentEncoding, $Body) = $self->_EncodeLOB( $Attachment->bodyhandle->as_string,
+ $Attachment->mime_type
+ );
+ my $id = $self->SUPER::Create(
+ TransactionId => $args{'TransactionId'},
+ ContentType => $Attachment->mime_type,
+ ContentEncoding => $ContentEncoding,
+ Parent => $args{'Parent'},
+ Headers => $Attachment->head->as_string,
+ Subject => $Subject,
+ Content => $Body,
+ Filename => $Filename,
+ MessageId => $MessageId,
+ );
+ unless ($id) {
+ $RT::Logger->crit("Attachment insert failed - ".$RT::Handle->dbh->errstr);
+ }
+
+ return ($id);
+ }
+}
+
+# }}}
+
+
+=head2 Import
+
+Create an attachment exactly as specified in the named parameters.
+
+=cut
+
+
+sub Import {
+ my $self = shift;
+ my %args = ( ContentEncoding => 'none',
+
+ @_ );
+
+
+ ($args{'ContentEncoding'}, $args{'Content'}) = $self->_EncodeLOB($args{'Content'}, $args{'MimeType'});
+
+ return($self->SUPER::Create(%args));
+}
+
+# {{{ sub Content
+
+=head2 Content
+
+Returns the attachment's content. if it's base64 encoded, decode it
+before returning it.
+
+=cut
+
+sub Content {
+ my $self = shift;
+ $self->_DecodeLOB($self->ContentType, $self->ContentEncoding, $self->_Value('Content', decode_utf8 => 0));
+}
+
+
+# }}}
+
+
+# {{{ sub OriginalContent
+
+=head2 OriginalContent
+
+Returns the attachment's content as octets before RT's mangling.
+Currently, this just means restoring text content back to its
+original encoding.
+
+=cut
+
+sub OriginalContent {
+ my $self = shift;
+
+ return $self->Content unless RT::I18N::IsTextualContentType($self->ContentType);
+
+ my $enc = $self->OriginalEncoding;
+
+ my $content;
+ if ( $self->ContentEncoding eq 'none' || ! $self->ContentEncoding ) {
+ $content = $self->_Value('Content', decode_utf8 => 0);
+ } elsif ( $self->ContentEncoding eq 'base64' ) {
+ $content = MIME::Base64::decode_base64($self->_Value('Content', decode_utf8 => 0));
+ } elsif ( $self->ContentEncoding eq 'quoted-printable' ) {
+ $content = MIME::QuotedPrint::decode($self->_Value('Content', decode_utf8 => 0));
+ } else {
+ return( $self->loc("Unknown ContentEncoding [_1]", $self->ContentEncoding));
+ }
+
+ # Turn *off* the SvUTF8 bits here so decode_utf8 and from_to below can work.
+ local $@;
+ Encode::_utf8_off($content);
+
+ if (!$enc || $enc eq '' || $enc eq 'utf8' || $enc eq 'utf-8') {
+ # If we somehow fail to do the decode, at least push out the raw bits
+ eval {return( Encode::decode_utf8($content))} || return ($content);
+ }
+
+ eval { Encode::from_to($content, 'utf8' => $enc) } if $enc;
+ if ($@) {
+ $RT::Logger->error("Could not convert attachment from assumed utf8 to '$enc' :".$@);
+ }
+ return $content;
+}
+
+# }}}
+
+
+# {{{ sub OriginalEncoding
+
+=head2 OriginalEncoding
+
+Returns the attachment's original encoding.
+
+=cut
+
+sub OriginalEncoding {
+ my $self = shift;
+ return $self->GetHeader('X-RT-Original-Encoding');
+}
+
+# }}}
+
+# {{{ sub Children
+
+=head2 Children
+
+ Returns an RT::Attachments object which is preloaded with all Attachments objects with this Attachment\'s Id as their 'Parent'
+
+=cut
+
+sub Children {
+ my $self = shift;
+
+ my $kids = new RT::Attachments($self->CurrentUser);
+ $kids->ChildrenOf($self->Id);
+ return($kids);
+}
+
+# }}}
+
+# {{{ UTILITIES
+
+# {{{ sub Quote
+
+
+
+sub Quote {
+ my $self=shift;
+ my %args=(Reply=>undef, # Prefilled reply (i.e. from the KB/FAQ system)
+ @_);
+
+ my ($quoted_content, $body, $headers);
+ my $max=0;
+
+ # TODO: Handle Multipart/Mixed (eventually fix the link in the
+ # ShowHistory web template?)
+ if (RT::I18N::IsTextualContentType($self->ContentType)) {
+ $body=$self->Content;
+
+ # Do we need any preformatting (wrapping, that is) of the message?
+
+ # Remove quoted signature.
+ $body =~ s/\n-- \n(.*)$//s;
+
+ # What's the longest line like?
+ foreach (split (/\n/,$body)) {
+ $max=length if ( length > $max);
+ }
+
+ if ($max>76) {
+ require Text::Wrapper;
+ my $wrapper=new Text::Wrapper
+ (
+ columns => 70,
+ body_start => ($max > 70*3 ? ' ' : ''),
+ par_start => ''
+ );
+ $body=$wrapper->wrap($body);
+ }
+
+ $body =~ s/^/> /gm;
+
+ $body = '[' . $self->TransactionObj->CreatorObj->Name() . ' - ' . $self->TransactionObj->CreatedAsString()
+ . "]:\n\n"
+ . $body . "\n\n";
+
+ } else {
+ $body = "[Non-text message not quoted]\n\n";
+ }
+
+ $max=60 if $max<60;
+ $max=70 if $max>78;
+ $max+=2;
+
+ return (\$body, $max);
+}
+# }}}
+
+# {{{ sub NiceHeaders - pulls out only the most relevant headers
+
+=head2 NiceHeaders
+
+Returns a multi-line string of the To, From, Cc, Date and Subject headers.
+
+=cut
+
+sub NiceHeaders {
+ my $self = shift;
+ my $hdrs = "";
+ my @hdrs = $self->_SplitHeaders;
+ while (my $str = shift @hdrs) {
+ next unless $str =~ /^(To|From|RT-Send-Cc|Cc|Bcc|Date|Subject):/i;
+ $hdrs .= $str . "\n";
+ $hdrs .= shift( @hdrs ) . "\n" while ($hdrs[0] =~ /^[ \t]+/);
+ }
+ return $hdrs;
+}
+# }}}
+
+# {{{ sub Headers
+
+=head2 Headers
+
+Returns this object's headers as a string. This method specifically
+removes the RT-Send-Bcc: header, so as to never reveal to whom RT sent a Bcc.
+We need to record the RT-Send-Cc and RT-Send-Bcc values so that we can actually send
+out mail. (The mailing rules are separated from the ticket update code by
+an abstraction barrier that makes it impossible to pass this data directly
+
+=cut
+
+sub Headers {
+ my $self = shift;
+ my $hdrs="";
+ my @headers = grep { !/^RT-Send-Bcc/i } $self->_SplitHeaders;
+ return join("\n",@headers);
+
+}
+
+
+# }}}
+
+# {{{ sub GetHeader
+
+=head2 GetHeader ( 'Tag')
+
+Returns the value of the header Tag as a string. This bypasses the weeding out
+done in Headers() above.
+
+=cut
+
+sub GetHeader {
+ my $self = shift;
+ my $tag = shift;
+ foreach my $line ($self->_SplitHeaders) {
+ if ($line =~ /^\Q$tag\E:\s+(.*)$/si) { #if we find the header, return its value
+ return ($1);
+ }
+ }
+
+ # we found no header. return an empty string
+ return undef;
+}
+# }}}
+
+# {{{ sub SetHeader
+
+=head2 SetHeader ( 'Tag', 'Value' )
+
+Replace or add a Header to the attachment's headers.
+
+=cut
+
+sub SetHeader {
+ my $self = shift;
+ my $tag = shift;
+ my $newheader = '';
+
+ foreach my $line ($self->_SplitHeaders) {
+ if (defined $tag and $line =~ /^\Q$tag\E:\s+(.*)$/i) {
+ $newheader .= "$tag: $_[0]\n";
+ undef $tag;
+ }
+ else {
+ $newheader .= "$line\n";
+ }
+ }
+
+ $newheader .= "$tag: $_[0]\n" if defined $tag;
+ $self->__Set( Field => 'Headers', Value => $newheader);
+}
+# }}}
+
+# {{{ sub _Value
+
+=head2 _Value
+
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
+
+=cut
+
+sub _Value {
+
+ my $self = shift;
+ my $field = shift;
+
+ #if the field is public, return it.
+ if ( $self->_Accessible( $field, 'public' ) ) {
+ return ( $self->__Value( $field, @_ ) );
+ }
+
+ #If it's a comment, we need to be extra special careful
+ elsif ( $self->TransactionObj->Type =~ /^Comment/ ) {
+ if ( $self->TransactionObj->CurrentUserHasRight('ShowTicketComments') )
+ {
+ return ( $self->__Value( $field, @_ ) );
+ }
+ }
+ elsif ( $self->TransactionObj->CurrentUserHasRight('ShowTicket') ) {
+ return ( $self->__Value( $field, @_ ) );
+ }
+
+ #if they ain't got rights to see, don't let em
+ else {
+ return (undef);
+ }
+
+}
+
+# }}}
+
+=head2 _SplitHeaders
+
+Returns an array of this attachment object's headers, with one header
+per array entry. multiple lines are folded.
+
+=begin testing
+
+my $test1 = "From: jesse";
+my @headers = RT::Attachment->_SplitHeaders($test1);
+is ($#headers, 0, $test1 );
+
+my $test2 = qq{From: jesse
+To: bobby
+Subject: foo
+};
+
+@headers = RT::Attachment->_SplitHeaders($test2);
+is ($#headers, 2, "testing a bunch of singline multiple headers" );
+
+
+my $test3 = qq{From: jesse
+To: bobby,
+ Suzie,
+ Sally,
+ Joey: bizzy,
+Subject: foo
+};
+
+@headers = RT::Attachment->_SplitHeaders($test3);
+is ($#headers, 2, "testing a bunch of singline multiple headers" );
+
+
+=end testing
+
+=cut
+
+sub _SplitHeaders {
+ my $self = shift;
+ my $headers = (shift || $self->SUPER::Headers());
+ my @headers;
+ for (split(/\n(?=\w|\z)/,$headers)) {
+ push @headers, $_;
+
+ }
+ return(@headers);
+}
+
+
+sub ContentLength {
+ my $self = shift;
+
+ unless ( (($self->TransactionObj->CurrentUserHasRight('ShowTicketComments')) and
+ ($self->TransactionObj->Type eq 'Comment') ) or
+ ($self->TransactionObj->CurrentUserHasRight('ShowTicket'))) {
+ return undef;
+ }
+
+ if (my $len = $self->GetHeader('Content-Length')) {
+ return $len;
+ }
+
+ {
+ use bytes;
+ my $len = length($self->Content);
+ $self->SetHeader('Content-Length' => $len);
+ return $len;
+ }
+}
+
+# }}}
+
+# Transactions don't change. by adding this cache congif directiove, we don't lose pathalogically on long tickets.
+sub _CacheConfig {
+ {
+ 'cache_p' => 1,
+ 'fast_update_p' => 1,
+ 'cache_for_sec' => 180,
+ }
+}
+
+1;
diff --git a/rt/lib/RT/Attachments.pm b/rt/lib/RT/Attachments.pm
new file mode 100755
index 0000000..5d90582
--- /dev/null
+++ b/rt/lib/RT/Attachments.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Attachments -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Attachments
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Attachments;
+
+use RT::SearchBuilder;
+use RT::Attachment;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Attachments';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Attachment item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Attachment->new($self->CurrentUser));
+}
+
+ eval "require RT::Attachments_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attachments_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attachments_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attachments_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attachments_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attachments_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Attachments_Overlay, RT::Attachments_Vendor, RT::Attachments_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Attachments_Overlay.pm b/rt/lib/RT/Attachments_Overlay.pm
new file mode 100644
index 0000000..395cee1
--- /dev/null
+++ b/rt/lib/RT/Attachments_Overlay.pm
@@ -0,0 +1,173 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Attachments - a collection of RT::Attachment objects
+
+=head1 SYNOPSIS
+
+ use RT::Attachments;
+
+=head1 DESCRIPTION
+
+This module should never be called directly by client code. it's an internal module which
+should only be accessed through exported APIs in Ticket, Queue and other similar objects.
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Attachments);
+
+=end testing
+
+=cut
+
+
+package RT::Attachments;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+
+ $self->{'table'} = "Attachments";
+ $self->{'primary_key'} = "id";
+ $self->OrderBy ( FIELD => 'id',
+ ORDER => 'ASC');
+ return ( $self->SUPER::_Init(@_));
+}
+# }}}
+
+
+# {{{ sub ContentType
+
+=head2 ContentType (VALUE => 'text/plain', ENTRYAGGREGATOR => 'OR', OPERATOR => '=' )
+
+Limit result set to attachments of ContentType 'TYPE'...
+
+=cut
+
+
+sub ContentType {
+ my $self = shift;
+ my %args = ( VALUE => 'text/plain',
+ OPERATOR => '=',
+ ENTRYAGGREGATOR => 'OR',
+ @_);
+
+ $self->Limit ( FIELD => 'ContentType',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ ENTRYAGGREGATOR => $args{'ENTRYAGGREGATOR'});
+}
+# }}}
+
+# {{{ sub ChildrenOf
+
+=head2 ChildrenOf ID
+
+Limit result set to children of Attachment ID
+
+=cut
+
+
+sub ChildrenOf {
+ my $self = shift;
+ my $attachment = shift;
+ $self->Limit ( FIELD => 'Parent',
+ VALUE => $attachment);
+}
+# }}}
+
+# {{{ sub NewItem
+sub NewItem {
+ my $self = shift;
+
+ use RT::Attachment;
+ my $item = new RT::Attachment($self->CurrentUser);
+ return($item);
+}
+# }}}
+
+# {{{ sub Next
+sub Next {
+ my $self = shift;
+
+ my $Attachment = $self->SUPER::Next();
+ if ((defined($Attachment)) and (ref($Attachment))) {
+ if ($Attachment->TransactionObj->__Value('Type') =~ /^Comment/ &&
+ $Attachment->TransactionObj->TicketObj->CurrentUserHasRight('ShowTicketComments')) {
+ return($Attachment);
+ } elsif ($Attachment->TransactionObj->__Value('Type') !~ /^Comment/ &&
+ $Attachment->TransactionObj->TicketObj->CurrentUserHasRight('ShowTicket')) {
+ return($Attachment);
+ }
+
+ #If the user doesn't have the right to show this ticket
+ else {
+ return($self->Next());
+ }
+ }
+
+ #if there never was any ticket
+ else {
+ return(undef);
+ }
+}
+# }}}
+
+ 1;
+
+
+
+
diff --git a/rt/lib/RT/Attribute.pm b/rt/lib/RT/Attribute.pm
new file mode 100644
index 0000000..89a856e
--- /dev/null
+++ b/rt/lib/RT/Attribute.pm
@@ -0,0 +1,349 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Attribute
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Attribute;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Attributes');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(255) 'Name'.
+ varchar(255) 'Description'.
+ text 'Content'.
+ varchar(16) 'ContentType'.
+ varchar(64) 'ObjectType'.
+ int(11) 'ObjectId'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Description => '',
+ Content => '',
+ ContentType => '',
+ ObjectType => '',
+ ObjectId => '',
+
+ @_);
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ Content => $args{'Content'},
+ ContentType => $args{'ContentType'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(255).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Content
+
+Returns the current value of Content.
+(In the database, Content is stored as text.)
+
+
+
+=head2 SetContent VALUE
+
+
+Set Content to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Content will be stored as a text.)
+
+
+=cut
+
+
+=head2 ContentType
+
+Returns the current value of ContentType.
+(In the database, ContentType is stored as varchar(16).)
+
+
+
+=head2 SetContentType VALUE
+
+
+Set ContentType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ContentType will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 ObjectType
+
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(64).)
+
+
+
+=head2 SetObjectType VALUE
+
+
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(64).)
+
+
+=cut
+
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=head2 SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Content =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ ContentType =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ ObjectType =>
+ {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ ObjectId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::Attribute_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attribute_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attribute_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attribute_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attribute_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attribute_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Attribute_Overlay, RT::Attribute_Vendor, RT::Attribute_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Attribute_Overlay.pm b/rt/lib/RT/Attribute_Overlay.pm
new file mode 100644
index 0000000..298f2e1
--- /dev/null
+++ b/rt/lib/RT/Attribute_Overlay.pm
@@ -0,0 +1,469 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Attribute;
+
+use strict;
+no warnings qw(redefine);
+use Storable qw/nfreeze thaw/;
+use MIME::Base64;
+
+
+=head1 NAME
+
+ RT::Attribute_Overlay
+
+=head1 Content
+
+=cut
+
+# the acl map is a map of "name of attribute" and "what right the user must have on the associated object to see/edit it
+
+our $ACL_MAP = {
+ SavedSearch => { create => 'EditSavedSearches',
+ update => 'EditSavedSearches',
+ delete => 'EditSavedSearches',
+ display => 'ShowSavedSearches' },
+
+};
+
+# There are a number of attributes that users should be able to modify for themselves, such as saved searches
+# we could do this with a different set of "modify" rights, but that gets very hacky very fast. this is even faster and even
+# hackier. we're hardcoding that a different set of rights are needed for attributes on oneself
+our $PERSONAL_ACL_MAP = {
+ SavedSearch => { create => 'ModifySelf',
+ update => 'ModifySelf',
+ delete => 'ModifySelf',
+ display => 'allow' },
+
+};
+
+=head2 LookupObjectRight { ObjectType => undef, ObjectId => undef, Name => undef, Right => { create, update, delete, display } }
+
+Returns the right that the user needs to have on this attribute's object to perform the related attribute operation. Returns "allow" if the right is otherwise unspecified.
+
+=cut
+
+sub LookupObjectRight {
+ my $self = shift;
+ my %args = ( ObjectType => undef,
+ ObjectId => undef,
+ Right => undef,
+ Name => undef,
+ @_);
+
+ # if it's an attribute on oneself, check the personal acl map
+ if (($args{'ObjectType'} eq 'RT::User') && ($args{'ObjectId'} eq $self->CurrentUser->Id)) {
+ return('allow') unless ($PERSONAL_ACL_MAP->{$args{'Name'}});
+ return('allow') unless ($PERSONAL_ACL_MAP->{$args{'Name'}}->{$args{'Right'}});
+ return($PERSONAL_ACL_MAP->{$args{'Name'}}->{$args{'Right'}});
+
+ }
+ # otherwise check the main ACL map
+ else {
+ return('allow') unless ($ACL_MAP->{$args{'Name'}});
+ return('allow') unless ($ACL_MAP->{$args{'Name'}}->{$args{'Right'}});
+ return($ACL_MAP->{$args{'Name'}}->{$args{'Right'}});
+ }
+}
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(255) 'Content'.
+ varchar(16) 'ContentType',
+ varchar(64) 'ObjectType'.
+ int(11) 'ObjectId'.
+
+You may pass a C<Object> instead of C<ObjectType> and C<ObjectId>.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Description => '',
+ Content => '',
+ ContentType => '',
+ Object => undef,
+ @_);
+
+ if ($args{Object} and UNIVERSAL::can($args{Object}, 'Id')) {
+ $args{ObjectType} = ref($args{Object});
+ $args{ObjectId} = $args{Object}->Id;
+ } else {
+ return(0, $self->loc("Required parameter '[_1]' not specified", 'Object'));
+
+ }
+
+ # object_right is the right that the user has to have on the object for them to have $right on this attribute
+ my $object_right = $self->LookupObjectRight(
+ Right => 'create',
+ ObjectId => $args{'ObjectId'},
+ ObjectType => $args{'ObjectType'},
+ Name => $args{'Name'}
+ );
+ if ($object_right eq 'deny') {
+ return (0, $self->loc('Permission Denied'));
+ }
+ elsif ($object_right eq 'allow') {
+ # do nothing, we're ok
+ }
+ elsif (!$self->CurrentUser->HasRight( Object => $args{Object}, Right => $object_right)) {
+ return (0, $self->loc('Permission Denied'));
+ }
+
+
+ if (ref ($args{'Content'}) ) {
+ eval {$args{'Content'} = $self->_SerializeContent($args{'Content'}); };
+ if ($@) {
+ return(0, $@);
+ }
+ $args{'ContentType'} = 'storable';
+ }
+
+
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Content => $args{'Content'},
+ ContentType => $args{'ContentType'},
+ Description => $args{'Description'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+);
+
+}
+
+
+# {{{ sub LoadByNameAndObject
+
+=head2 LoadByNameAndObject (Object => OBJECT, Name => NAME)
+
+Loads the Attribute named NAME for Object OBJECT.
+
+=cut
+
+sub LoadByNameAndObject {
+ my $self = shift;
+ my %args = (
+ Object => undef,
+ Name => undef,
+ @_,
+ );
+
+ return (
+ $self->LoadByCols(
+ Name => $args{'Name'},
+ ObjectType => ref($args{'Object'}),
+ ObjectId => $args{'Object'}->Id,
+ )
+ );
+
+}
+
+# }}}
+
+
+=head2 _DeserializeContent
+
+DeserializeContent returns this Attribute's "Content" as a hashref.
+
+
+=cut
+
+sub _DeserializeContent {
+ my $self = shift;
+ my $content = shift;
+
+ my $hashref;
+ eval {$hashref = thaw(decode_base64($content))} ;
+ if ($@) {
+ $RT::Logger->error("Deserialization of attribute ".$self->Id. " failed");
+ }
+
+ return($hashref);
+
+}
+
+
+=head2 Content
+
+Returns this attribute's content. If it's a scalar, returns a scalar
+If it's data structure returns a ref to that data structure.
+
+=cut
+
+sub Content {
+ my $self = shift;
+ # Here we call _Value to get the ACL check.
+ my $content = $self->_Value('Content');
+ if ($self->__Value('ContentType') eq 'storable') {
+ eval {$content = $self->_DeserializeContent($content); };
+ if ($@) {
+ $RT::Logger->error("Deserialization of content for attribute ".$self->Id. " failed. Attribute was: ".$content);
+ }
+ }
+
+ return($content);
+
+}
+
+sub _SerializeContent {
+ my $self = shift;
+ my $content = shift;
+ return( encode_base64(nfreeze($content)));
+}
+
+
+sub SetContent {
+ my $self = shift;
+ my $content = shift;
+
+ # Call __Value to avoid ACL check.
+ if ($self->__Value('ContentType') eq 'storable') {
+ # We eval the serialization because it will lose on a coderef.
+ eval {$content = $self->_SerializeContent($content); };
+ if ($@) {
+ $RT::Logger->error("For some reason, content couldn't be frozen");
+ return(0, $@);
+ }
+ }
+ return ($self->SUPER::SetContent($content));
+}
+
+=head2 SubValue KEY
+
+Returns the subvalue for $key.
+
+=begin testing
+
+my $user = $RT::SystemUser;
+my ($id, $msg) = $user->AddAttribute(Name => 'SavedSearch', Content => { Query => 'Foo'} );
+ok ($id, $msg);
+my $attr = RT::Attribute->new($RT::SystemUser);
+$attr->Load($id);
+ok($attr->Name eq 'SavedSearch');
+$attr->SetSubValues( Format => 'baz');
+
+my $format = $attr->SubValue('Format');
+is ($format , 'baz');
+
+$attr->SetSubValues( Format => 'bar');
+$format = $attr->SubValue('Format');
+is ($format , 'bar');
+
+$attr->DeleteAllSubValues();
+$format = $attr->SubValue('Format');
+is ($format, undef);
+
+$attr->SetSubValues(Format => 'This is a format');
+
+my $attr2 = RT::Attribute->new($RT::SystemUser);
+$attr2->Load($id);
+is ($attr2->SubValue('Format'), 'This is a format');
+$attr2->Delete;
+my $attr3 = RT::Attribute->new($RT::SystemUser);
+my ($id) = $attr3->Load($id);
+is ($id, 0);
+
+=end testing
+
+=cut
+
+sub SubValue {
+ my $self = shift;
+ my $key = shift;
+ my $values = $self->Content();
+ return undef unless ref($values);
+ return($values->{$key});
+}
+
+=head2 DeleteSubValue NAME
+
+Deletes the subvalue with the key NAME
+
+=cut
+
+sub DeleteSubValue {
+ my $self = shift;
+ my $key = shift;
+ my %values = $self->Content();
+ delete $values{$key};
+ $self->SetContent(%values);
+
+
+
+}
+
+
+=head2 DeleteAllSubValues
+
+Deletes all subvalues for this attribute
+
+=cut
+
+
+sub DeleteAllSubValues {
+ my $self = shift;
+ $self->SetContent({});
+}
+
+=head2 SetSubValues { }
+
+Takes a hash of keys and values and stores them in the content of this attribute.
+
+Each key B<replaces> the existing key with the same name
+
+Returns a tuple of (status, message)
+
+=cut
+
+
+sub SetSubValues {
+ my $self = shift;
+ my %args = (@_);
+ my $values = ($self->Content() || {} );
+ foreach my $key (keys %args) {
+ $values->{$key} = $args{$key};
+ }
+
+ $self->SetContent($values);
+
+}
+
+
+sub Object {
+ my $self = shift;
+ my $object_type = $self->__Value('ObjectType');
+ my $object;
+ eval { $object = $object_type->new($self->CurrentUser) };
+ unless(UNIVERSAL::isa($object, $object_type)) {
+ $RT::Logger->error("Attribute ".$self->Id." has a bogus object type - $object_type (".$@.")");
+ return(undef);
+ }
+ $object->Load($self->__Value('ObjectId'));
+
+ return($object);
+
+}
+
+
+sub Delete {
+ my $self = shift;
+ unless ($self->CurrentUserHasRight('delete')) {
+ return (0,$self->loc('Permission Denied'));
+ }
+ return($self->SUPER::Delete(@_));
+}
+
+
+sub _Value {
+ my $self = shift;
+ unless ($self->CurrentUserHasRight('display')) {
+ return (0,$self->loc('Permission Denied'));
+ }
+
+ return($self->SUPER::_Value(@_));
+
+
+}
+
+
+sub _Set {
+ my $self = shift;
+ unless ($self->CurrentUserHasRight('modify')) {
+
+ return (0,$self->loc('Permission Denied'));
+ }
+ return($self->SUPER::_Set(@_));
+
+}
+
+
+=head2 CurrentUserHasRight
+
+One of "display" "modify" "delete" or "create" and returns 1 if the user has that right for attributes of this name for this object.Returns undef otherwise.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ # object_right is the right that the user has to have on the object for them to have $right on this attribute
+ my $object_right = $self->LookupObjectRight(
+ Right => $right,
+ ObjectId => $self->__Value('ObjectId'),
+ ObjectType => $self->__Value('ObjectType'),
+ Name => $self->__Value('Name')
+ );
+
+ return (1) if ($object_right eq 'allow');
+ return (0) if ($object_right eq 'deny');
+ return(1) if ($self->CurrentUser->HasRight( Object => $self->Object, Right => $object_right));
+ return(0);
+
+}
+
+
+=head1 TODO
+
+We should be deserializing the content on load and then enver again, rather than at every access
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Attributes.pm b/rt/lib/RT/Attributes.pm
new file mode 100644
index 0000000..31694c1
--- /dev/null
+++ b/rt/lib/RT/Attributes.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Attributes -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Attributes
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Attributes;
+
+use RT::SearchBuilder;
+use RT::Attribute;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Attributes';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Attribute item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Attribute->new($self->CurrentUser));
+}
+
+ eval "require RT::Attributes_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attributes_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attributes_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attributes_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Attributes_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Attributes_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Attributes_Overlay, RT::Attributes_Vendor, RT::Attributes_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Attributes_Overlay.pm b/rt/lib/RT/Attributes_Overlay.pm
new file mode 100644
index 0000000..47b333e
--- /dev/null
+++ b/rt/lib/RT/Attributes_Overlay.pm
@@ -0,0 +1,198 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Attributes - collection of RT::Attribute objects
+
+=head1 SYNOPSIS
+
+ use RT::Attributes;
+my $Attributes = new RT::Attributes($CurrentUser);
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+
+package RT::Attributes;
+
+use strict;
+no warnings qw(redefine);
+
+
+sub _DoSearch {
+ my $self = shift;
+ $self->SUPER::_DoSearch();
+ $self->_BuildAccessTable();
+}
+
+
+sub _BuildAccessTable {
+ my $self = shift;
+ delete $self->{'attr'};
+ while (my $attr = $self->Next) {
+ push @{$self->{'attr'}->{$attr->Name}}, $attr;
+ }
+}
+
+
+sub _AttrHash {
+ my $self = shift;
+ $self->_DoSearch if ($self->{'must_redo_search'});
+ unless ($self->{'attr'}) {
+ $self->{'attr'}->{'__none'} = RT::Attribute->new($self->CurrentUser);
+ }
+ return ($self->{'attr'});
+}
+
+=head2 Names
+
+Returns a list of the Names of all attributes for this object.
+
+=cut
+
+sub Names {
+ my $self = shift;
+ my @keys = keys %{$self->_AttrHash};
+ return(@keys);
+
+
+}
+
+=head2 Named STRING
+
+Returns an array of all the RT::Attribute objects with the name STRING
+
+=cut
+
+sub Named {
+ my $self = shift;
+ my $name = shift;
+ my @attributes;
+ if ($self->_AttrHash) {
+ @attributes = @{($self->_AttrHash->{$name}||[])};
+ }
+ return (@attributes);
+}
+
+=head2 WithId ID
+
+Returns the RT::Attribute objects with the id ID
+
+XXX TODO XXX THIS NEEDS A BETTER ACL CHECK
+
+=cut
+
+sub WithId {
+ my $self = shift;
+ my $id = shift;
+
+ my $attr = RT::Attribute->new($self->CurrentUser);
+ $attr->LoadByCols( id => $id );
+ return($attr);
+}
+
+=head2 DeleteEntry { Name => Content => , id => }
+
+Deletes attributes with
+ the matching name
+ and the matching content or id
+
+If Content and id are both undefined, delete all attributes with
+the matching name.
+
+=cut
+
+
+sub DeleteEntry {
+ my $self = shift;
+ my %args = ( Name => undef,
+ Content => undef,
+ id => undef,
+ @_);
+ my $found = 0;
+ foreach my $attr ($self->Named($args{'Name'})){
+ if ((!defined $args{'id'} and !defined $args{'Content'})
+ or (defined $args{'id'} and $attr->id eq $args{'id'})
+ or (defined $args{'Content'} and $attr->Content eq $args{'Content'})) {
+ my ($id, $msg) = $attr->Delete;
+ return ($id, $msg) unless $id;
+ $found = 1;
+ }
+ }
+ return (0, "No entry found") unless $found;
+ $self->_DoSearch();
+ return (1, $self->loc('Attribute Deleted'));
+}
+
+
+# {{{ LimitToObject
+
+=head2 LimitToObject $object
+
+Limit the Attributes to rights for the object $object. It needs to be an RT::Record class.
+
+=cut
+
+sub LimitToObject {
+ my $self = shift;
+ my $obj = shift;
+ unless (defined($obj) && ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id) {
+ return undef;
+ }
+ $self->Limit(FIELD => 'ObjectType', OPERATOR=> '=', VALUE => ref($obj), ENTRYAGGREGATOR => 'OR');
+ $self->Limit(FIELD => 'ObjectId', OPERATOR=> '=', VALUE => $obj->id, ENTRYAGGREGATOR => 'OR', QUOTEVALUE => 0);
+
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/RT/Base.pm b/rt/lib/RT/Base.pm
new file mode 100644
index 0000000..7910588
--- /dev/null
+++ b/rt/lib/RT/Base.pm
@@ -0,0 +1,173 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Base;
+use Carp;
+use Scalar::Util;
+
+use strict;
+use vars qw(@EXPORT);
+
+@EXPORT=qw(loc CurrentUser);
+
+=head1 NAME
+
+RT::Base
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 FUNCTIONS
+
+=cut
+
+# {{{ sub CurrentUser
+
+=head2 CurrentUser
+
+If called with an argument, sets the current user to that user object.
+This will affect ACL decisions, etc. The argument can be either
+L<RT::CurrentUser> or L<RT::User> object.
+
+Returns the current user object of L<RT::CurrentUser> class.
+
+=cut
+
+sub CurrentUser {
+ my $self = shift;
+
+ if (@_) {
+ $self->{'original_user'} = $self->{'user'};
+ my $current_user = $_[0];
+ if ( ref $current_user eq 'RT::User' ) {
+ $self->{'user'} = new RT::CurrentUser;
+ $self->{'user'}->Load( $current_user->id );
+ } else {
+ $self->{'user'} = $current_user;
+ }
+ # We need to weaken the CurrentUser ($self->{'user'}) reference
+ # if the object in question is the currentuser object.
+ # This avoids memory leaks.
+ Scalar::Util::weaken($self->{'user'})
+ if ref $self->{'user'} && $self->{'user'} == $self;
+ }
+
+ unless ( ref $self->{'user'} && $self->{'user'}->isa('RT::CurrentUser') ) {
+ my $msg = "$self was created without a CurrentUser."
+ ." Any RT object which is subclass of RT::Base must be created"
+ ." with a RT::CurrentUser or a RT::User obejct as the first argument.";
+ $msg .= "\n". Carp::cluck() if @_;
+
+ $RT::Logger->err( $msg );
+ return $self->{'user'} = undef;
+ }
+
+ return ( $self->{'user'} );
+}
+
+# }}}
+
+sub OriginalUser {
+ my $self = shift;
+
+ if (@_) {
+ $self->{'original_user'} = shift;
+ Scalar::Util::weaken($self->{'original_user'})
+ if (ref($self->{'original_user'}) && $self->{'original_user'} == $self );
+ }
+ return ( $self->{'original_user'} || $self->{'user'} );
+}
+
+
+=head2 loc LOC_STRING
+
+l is a method which takes a loc string
+to this object's CurrentUser->LanguageHandle for localization.
+
+you call it like this:
+
+ $self->loc("I have [quant,_1,concrete mixer].", 6);
+
+In english, this would return:
+ I have 6 concrete mixers.
+
+
+=cut
+
+sub loc {
+ my $self = shift;
+ if (my $user = $self->OriginalUser) {
+ return $user->loc(@_);
+ }
+ else {
+ use Carp;
+ Carp::confess("No currentuser");
+ return ("Critical error:$self has no CurrentUser", $self);
+ }
+}
+
+sub loc_fuzzy {
+ my $self = shift;
+ if (my $user = $self->OriginalUser) {
+ return $user->loc_fuzzy(@_);
+ }
+ else {
+ use Carp;
+ Carp::confess("No currentuser");
+ return ("Critical error:$self has no CurrentUser", $self);
+ }
+}
+
+eval "require RT::Base_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Base_Vendor.pm});
+eval "require RT::Base_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Base_Local.pm});
+
+
+1;
diff --git a/rt/lib/RT/CachedGroupMember.pm b/rt/lib/RT/CachedGroupMember.pm
new file mode 100644
index 0000000..a813dd7
--- /dev/null
+++ b/rt/lib/RT/CachedGroupMember.pm
@@ -0,0 +1,282 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::CachedGroupMember
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::CachedGroupMember;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('CachedGroupMembers');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'GroupId'.
+ int(11) 'MemberId'.
+ int(11) 'Via'.
+ int(11) 'ImmediateParentId'.
+ smallint(6) 'Disabled'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ GroupId => '',
+ MemberId => '',
+ Via => '',
+ ImmediateParentId => '',
+ Disabled => '0',
+
+ @_);
+ $self->SUPER::Create(
+ GroupId => $args{'GroupId'},
+ MemberId => $args{'MemberId'},
+ Via => $args{'Via'},
+ ImmediateParentId => $args{'ImmediateParentId'},
+ Disabled => $args{'Disabled'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 GroupId
+
+Returns the current value of GroupId.
+(In the database, GroupId is stored as int(11).)
+
+
+
+=head2 SetGroupId VALUE
+
+
+Set GroupId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, GroupId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 MemberId
+
+Returns the current value of MemberId.
+(In the database, MemberId is stored as int(11).)
+
+
+
+=head2 SetMemberId VALUE
+
+
+Set MemberId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, MemberId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Via
+
+Returns the current value of Via.
+(In the database, Via is stored as int(11).)
+
+
+
+=head2 SetVia VALUE
+
+
+Set Via to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Via will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 ImmediateParentId
+
+Returns the current value of ImmediateParentId.
+(In the database, ImmediateParentId is stored as int(11).)
+
+
+
+=head2 SetImmediateParentId VALUE
+
+
+Set ImmediateParentId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ImmediateParentId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Disabled
+
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
+
+
+
+=head2 SetDisabled VALUE
+
+
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ GroupId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ MemberId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Via =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ ImmediateParentId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Disabled =>
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::CachedGroupMember_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMember_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CachedGroupMember_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMember_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CachedGroupMember_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMember_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::CachedGroupMember_Overlay, RT::CachedGroupMember_Vendor, RT::CachedGroupMember_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/CachedGroupMember_Overlay.pm b/rt/lib/RT/CachedGroupMember_Overlay.pm
new file mode 100644
index 0000000..6ed4281
--- /dev/null
+++ b/rt/lib/RT/CachedGroupMember_Overlay.pm
@@ -0,0 +1,366 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::CachedGroupMember;
+
+use strict;
+no warnings qw(redefine);
+
+=head1 NAME
+
+ RT::CachedGroupMember
+
+=head1 SYNOPSIS
+
+ use RT::CachedGroupMember;
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+# {{ Create
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ 'Group' is the "top level" group we're building the cache for. This
+ is an RT::Principal object
+
+ 'Member' is the RT::Principal of the user or group we're adding to
+ the cache.
+
+ 'ImmediateParent' is the RT::Principal of the group that this
+ principal belongs to to get here
+
+ int(11) 'Via' is an internal reference to CachedGroupMembers->Id of
+ the "parent" record of this cached group member. It should be empty if
+ this member is a "direct" member of this group. (In that case, it will
+ be set to this cached group member's id after creation)
+
+ This routine should _only_ be called by GroupMember->Create
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = ( Group => '',
+ Member => '',
+ ImmediateParent => '',
+ Via => '0',
+ Disabled => '0',
+ @_ );
+
+ unless ( $args{'Member'}
+ && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' )
+ && $args{'Member'}->Id ) {
+ $RT::Logger->debug("$self->Create: bogus Member argument");
+ }
+
+ unless ( $args{'Group'}
+ && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' )
+ && $args{'Group'}->Id ) {
+ $RT::Logger->debug("$self->Create: bogus Group argument");
+ }
+
+ unless ( $args{'ImmediateParent'}
+ && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' )
+ && $args{'ImmediateParent'}->Id ) {
+ $RT::Logger->debug("$self->Create: bogus ImmediateParent argument");
+ }
+
+ # If the parent group for this group member is disabled, it's disabled too, along with all its children
+ if ( $args{'ImmediateParent'}->Disabled ) {
+ $args{'Disabled'} = $args{'ImmediateParent'}->Disabled;
+ }
+
+ my $id = $self->SUPER::Create(
+ GroupId => $args{'Group'}->Id,
+ MemberId => $args{'Member'}->Id,
+ ImmediateParentId => $args{'ImmediateParent'}->Id,
+ Disabled => $args{'Disabled'},
+ Via => $args{'Via'}, );
+
+ unless ($id) {
+ $RT::Logger->warning( "Couldn't create "
+ . $args{'Member'}
+ . " as a cached member of "
+ . $args{'Group'}->Id . " via "
+ . $args{'Via'} );
+ return (undef); #this will percolate up and bail out of the transaction
+ }
+ if ( $self->__Value('Via') == 0 ) {
+ my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id );
+ unless ($vid) {
+ $RT::Logger->warning( "Due to a via error, couldn't create "
+ . $args{'Member'}
+ . " as a cached member of "
+ . $args{'Group'}->Id . " via "
+ . $args{'Via'} );
+ return (undef)
+ ; #this will percolate up and bail out of the transaction
+ }
+ }
+
+ if ( $args{'Member'}->IsGroup() ) {
+ my $GroupMembers = $args{'Member'}->Object->MembersObj();
+ while ( my $member = $GroupMembers->Next() ) {
+ my $cached_member =
+ RT::CachedGroupMember->new( $self->CurrentUser );
+ my $c_id = $cached_member->Create(
+ Group => $args{'Group'},
+ Member => $member->MemberObj,
+ ImmediateParent => $args{'Member'},
+ Disabled => $args{'Disabled'},
+ Via => $id );
+ unless ($c_id) {
+ return (undef); #percolate the error upwards.
+ # the caller will log an error and abort the transaction
+ }
+
+ }
+ }
+
+ return ($id);
+
+}
+
+# }}}
+
+# {{{ Delete
+
+=head2 Delete
+
+Deletes the current CachedGroupMember from the group it's in and cascades
+the delete to all submembers. This routine could be completely excised if
+mysql supported foreign keys with cascading deletes.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+
+ my $member = $self->MemberObj();
+ if ( $member->IsGroup ) {
+ my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+ $deletable->Limit( FIELD => 'id',
+ OPERATOR => '!=',
+ VALUE => $self->id );
+ $deletable->Limit( FIELD => 'Via',
+ OPERATOR => '=',
+ VALUE => $self->id );
+
+ while ( my $kid = $deletable->Next ) {
+ my $kid_err = $kid->Delete();
+ unless ($kid_err) {
+ $RT::Logger->error(
+ "Couldn't delete CachedGroupMember " . $kid->Id );
+ return (undef);
+ }
+ }
+ }
+ my $err = $self->SUPER::Delete();
+ unless ($err) {
+ $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id );
+ return (undef);
+ }
+
+ # Unless $self->GroupObj still has the member recursively $self->MemberObj
+ # (Since we deleted the database row above, $self no longer counts)
+ unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
+
+
+ # Find all ACEs granted to $self->GroupId
+ my $acl = RT::ACL->new($RT::SystemUser);
+ $acl->LimitToPrincipal( Id => $self->GroupId );
+
+
+ while ( my $this_ace = $acl->Next() ) {
+ # Find all ACEs which $self-MemberObj has delegated from $this_ace
+ my $delegations = RT::ACL->new($RT::SystemUser);
+ $delegations->DelegatedFrom( Id => $this_ace->Id );
+ $delegations->DelegatedBy( Id => $self->MemberId );
+
+ # For each delegation
+ while ( my $delegation = $delegations->Next ) {
+ # WHACK IT
+ my $del_ret = $delegation->_Delete(InsideTransaction => 1);
+ unless ($del_ret) {
+ $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
+ return(undef);
+ }
+ }
+ }
+ }
+ return ($err);
+}
+
+# }}}
+
+# {{{ SetDisabled
+
+=head2 SetDisabled
+
+SetDisableds the current CachedGroupMember from the group it's in and cascades
+the SetDisabled to all submembers. This routine could be completely excised if
+mysql supported foreign keys with cascading SetDisableds.
+
+=cut
+
+sub SetDisabled {
+ my $self = shift;
+ my $val = shift;
+
+ # if it's already disabled, we're good.
+ return {1} if ($self->__Value('Disabled') == $val);
+ my $err = $self->SUPER::SetDisabled($val);
+ my ($retval, $msg) = $err->as_array();
+ unless ($retval) {
+ $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id .": $msg");
+ return ($err);
+ }
+
+ my $member = $self->MemberObj();
+ if ( $member->IsGroup ) {
+ my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+ $deletable->Limit( FIELD => 'Via', OPERATOR => '=', VALUE => $self->id );
+ $deletable->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->id );
+
+ while ( my $kid = $deletable->Next ) {
+ my $kid_err = $kid->SetDisabled($val );
+ unless ($kid_err) {
+ $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id );
+ return ($kid_err);
+ }
+ }
+ }
+
+ # Unless $self->GroupObj still has the member recursively $self->MemberObj
+ # (Since we SetDisabledd the database row above, $self no longer counts)
+ unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
+ # Find all ACEs granted to $self->GroupId
+ my $acl = RT::ACL->new($RT::SystemUser);
+ $acl->LimitToPrincipal( Id => $self->GroupId );
+
+ while ( my $this_ace = $acl->Next() ) {
+ # Find all ACEs which $self-MemberObj has delegated from $this_ace
+ my $delegations = RT::ACL->new($RT::SystemUser);
+ $delegations->DelegatedFrom( Id => $this_ace->Id );
+ $delegations->DelegatedBy( Id => $self->MemberId );
+
+ # For each delegation, blow away the delegation
+ while ( my $delegation = $delegations->Next ) {
+ # WHACK IT
+ my $del_ret = $delegation->_Delete(InsideTransaction => 1);
+ unless ($del_ret) {
+ $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
+ return(undef);
+ }
+ }
+ }
+ }
+ return ($err);
+}
+
+# }}}
+
+# {{{ GroupObj
+
+=head2 GroupObj
+
+Returns the RT::Principal object for this group Group
+
+=cut
+
+sub GroupObj {
+ my $self = shift;
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ $principal->Load( $self->GroupId );
+ return ($principal);
+}
+
+# }}}
+
+# {{{ ImmediateParentObj
+
+=head2 ImmediateParentObj
+
+Returns the RT::Principal object for this group ImmediateParent
+
+=cut
+
+sub ImmediateParentObj {
+ my $self = shift;
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ $principal->Load( $self->ImmediateParentId );
+ return ($principal);
+}
+
+# }}}
+
+# {{{ MemberObj
+
+=head2 MemberObj
+
+Returns the RT::Principal object for this group member
+
+=cut
+
+sub MemberObj {
+ my $self = shift;
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ $principal->Load( $self->MemberId );
+ return ($principal);
+}
+
+# }}}
+1;
diff --git a/rt/lib/RT/CachedGroupMembers.pm b/rt/lib/RT/CachedGroupMembers.pm
new file mode 100644
index 0000000..9d6703d
--- /dev/null
+++ b/rt/lib/RT/CachedGroupMembers.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::CachedGroupMembers -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::CachedGroupMembers
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::CachedGroupMembers;
+
+use RT::SearchBuilder;
+use RT::CachedGroupMember;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'CachedGroupMembers';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::CachedGroupMember item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::CachedGroupMember->new($self->CurrentUser));
+}
+
+ eval "require RT::CachedGroupMembers_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMembers_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CachedGroupMembers_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMembers_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CachedGroupMembers_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMembers_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::CachedGroupMembers_Overlay, RT::CachedGroupMembers_Vendor, RT::CachedGroupMembers_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/CachedGroupMembers_Overlay.pm b/rt/lib/RT/CachedGroupMembers_Overlay.pm
new file mode 100644
index 0000000..0d1c6b1
--- /dev/null
+++ b/rt/lib/RT/CachedGroupMembers_Overlay.pm
@@ -0,0 +1,177 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::CachedGroupMembers - a collection of RT::GroupMember objects
+
+=head1 SYNOPSIS
+
+ use RT::CachedGroupMembers;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::CachedGroupMembers);
+
+=end testing
+
+=cut
+
+
+package RT::CachedGroupMembers;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ LimitToUsers
+
+=head2 LimitToUsers
+
+Limits this search object to users who are members of this group
+This is really useful when you want to have your UI separate out
+groups from users for display purposes
+
+=cut
+
+sub LimitToUsers {
+ my $self = shift;
+
+ my $principals = $self->NewAlias('Principals');
+ $self->Join( ALIAS1 => 'main', FIELD1 => 'MemberId',
+ ALIAS2 => $principals, FIELD2 =>'id');
+
+ $self->Limit( ALIAS => $principals,
+ FIELD => 'PrincipalType',
+ VALUE => 'User',
+ ENTRYAGGREGATOR => 'OR',
+ );
+}
+
+# }}}
+
+
+# {{{ LimitToGroups
+
+=head2 LimitToGroups
+
+Limits this search object to Groups who are members of this group
+This is really useful when you want to have your UI separate out
+groups from users for display purposes
+
+=cut
+
+sub LimitToGroups {
+ my $self = shift;
+
+ my $principals = $self->NewAlias('Principals');
+ $self->Join( ALIAS1 => 'main', FIELD1 => 'MemberId',
+ ALIAS2 => $principals, FIELD2 =>'id');
+
+ $self->Limit( ALIAS => $principals,
+ FIELD => 'PrincipalType',
+ VALUE => 'Group',
+ ENTRYAGGREGATOR => 'OR',
+ );
+}
+
+# }}}
+
+# {{{ sub LimitToMembersOfGroup
+
+=head2 LimitToMembersOfGroup PRINCIPAL_ID
+
+Takes a Principal Id as its only argument.
+Limits the current search principals which are _directly_ members
+of the group which has PRINCIPAL_ID as its principal id.
+
+=cut
+
+sub LimitToMembersOfGroup {
+ my $self = shift;
+ my $group = shift;
+
+ return ($self->Limit(
+ VALUE => $group,
+ FIELD => 'GroupId',
+ ENTRYAGGREGATOR => 'OR',
+ ));
+
+}
+# }}}
+
+# {{{ sub LimitToGroupsWithMember
+
+=head2 LimitToGroupsWithMember PRINCIPAL_ID
+
+Takes a Principal Id as its only argument.
+Limits the current search to groups which contain PRINCIPAL_ID as a member or submember.
+This function gets used by GroupMember->Create to populate subgroups
+
+=cut
+
+sub LimitToGroupsWithMember {
+ my $self = shift;
+ my $member = shift;
+
+
+
+ return ($self->Limit(
+ VALUE => $member || '0',
+ FIELD => 'MemberId',
+ ENTRYAGGREGATOR => 'OR',
+ QUOTEVALUE => 0
+ ));
+
+}
+# }}}
+1;
diff --git a/rt/lib/RT/Condition/AnyTransaction.pm b/rt/lib/RT/Condition/AnyTransaction.pm
new file mode 100644
index 0000000..5251537
--- /dev/null
+++ b/rt/lib/RT/Condition/AnyTransaction.pm
@@ -0,0 +1,75 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+package RT::Condition::AnyTransaction;
+require RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+This happens on every transaction. it's always applicable
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ return(1);
+}
+
+eval "require RT::Condition::AnyTransaction_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/AnyTransaction_Vendor.pm});
+eval "require RT::Condition::AnyTransaction_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/AnyTransaction_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Condition/BeforeDue.pm b/rt/lib/RT/Condition/BeforeDue.pm
new file mode 100644
index 0000000..88eadb6
--- /dev/null
+++ b/rt/lib/RT/Condition/BeforeDue.pm
@@ -0,0 +1,88 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Condition::BeforeDue;
+require RT::Condition::Generic;
+
+use RT::Date;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+sub IsApplicable {
+ my $self = shift;
+
+ # Parse date string. Format is "1d2h3m4s" for 1 day and 2 hours
+ # and 3 minutes and 4 seconds.
+ my %e;
+ foreach (qw(d h m s)) {
+ my @vals = $self->Argument =~ m/(\d+)$_/;
+ $e{$_} = pop @vals || 0;
+ }
+ my $elapse = $e{'d'} * 24*60*60 + $e{'h'} * 60*60 + $e{'m'} * 60 + $e{'s'};
+
+ my $cur = new RT::Date( $RT::SystemUser );
+ $cur->SetToNow();
+ my $due = $self->TicketObj->DueObj;
+ return (undef) if $due->Unix <= 0;
+
+ my $diff = $due->Diff($cur);
+ if ( $diff >= 0 and $diff <= $elapse ) {
+ return(1);
+ } else {
+ return(undef);
+ }
+}
+
+eval "require RT::Condition::BeforeDue_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/BeforeDue_Vendor.pm});
+eval "require RT::Condition::BeforeDue_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/BeforeDue_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Condition/Generic.pm b/rt/lib/RT/Condition/Generic.pm
new file mode 100755
index 0000000..82248e2
--- /dev/null
+++ b/rt/lib/RT/Condition/Generic.pm
@@ -0,0 +1,235 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Condition::Generic - ;
+
+=head1 SYNOPSIS
+
+ use RT::Condition::Generic;
+ my $foo = RT::Condition::Generic->new(
+ TransactionObj => $tr,
+ TicketObj => $ti,
+ ScripObj => $scr,
+ Argument => $arg,
+ Type => $type);
+
+ if ($foo->IsApplicable) {
+ # do something
+ }
+
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Condition::Generic);
+
+=end testing
+
+
+=cut
+
+package RT::Condition::Generic;
+
+use strict;
+use base qw/RT::Base/;
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ my %args = ( TransactionObj => undef,
+ TicketObj => undef,
+ ScripObj => undef,
+ TemplateObj => undef,
+ Argument => undef,
+ ApplicableTransTypes => undef,
+ CurrentUser => undef,
+ @_ );
+
+ $self->{'Argument'} = $args{'Argument'};
+ $self->{'ScripObj'} = $args{'ScripObj'};
+ $self->{'TicketObj'} = $args{'TicketObj'};
+ $self->{'TransactionObj'} = $args{'TransactionObj'};
+ $self->{'ApplicableTransTypes'} = $args{'ApplicableTransTypes'};
+ $self->CurrentUser($args{'CurrentUser'});
+}
+# }}}
+
+# Access Scripwide data
+
+# {{{ sub Argument
+
+=head2 Argument
+
+Return the optional argument associated with this ScripCondition
+
+=cut
+
+sub Argument {
+ my $self = shift;
+ return($self->{'Argument'});
+}
+# }}}
+
+# {{{ sub TicketObj
+
+=head2 TicketObj
+
+Return the ticket object we're talking about
+
+=cut
+
+sub TicketObj {
+ my $self = shift;
+ return($self->{'TicketObj'});
+}
+# }}}
+
+# {{{ sub ScripObj
+
+=head2 ScripObj
+
+Return the Scrip object we're talking about
+
+=cut
+
+sub ScripObj {
+ my $self = shift;
+ return($self->{'ScripObj'});
+}
+# }}}
+# {{{ sub TransactionObj
+
+=head2 TransactionObj
+
+Return the transaction object we're talking about
+
+=cut
+
+sub TransactionObj {
+ my $self = shift;
+ return($self->{'TransactionObj'});
+}
+# }}}
+
+# {{{ sub Type
+
+=head2 Type
+
+
+
+=cut
+
+sub ApplicableTransTypes {
+ my $self = shift;
+ return($self->{'ApplicableTransTypes'});
+}
+# }}}
+
+
+# Scrip methods
+
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->loc("No description for [_1]", ref $self));
+}
+# }}}
+
+
+#Parse the templates, get things ready to go.
+
+#If this rule applies to this transaction, return true.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ return(undef);
+}
+# }}}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self = shift;
+
+ # We need to clean up all the references that might maybe get
+ # oddly circular
+ $self->{'TemplateObj'} =undef
+ $self->{'TicketObj'} = undef;
+ $self->{'TransactionObj'} = undef;
+ $self->{'ScripObj'} = undef;
+
+}
+
+# }}}
+
+eval "require RT::Condition::Generic_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Generic_Vendor.pm});
+eval "require RT::Condition::Generic_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Generic_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Condition/Overdue.pm b/rt/lib/RT/Condition/Overdue.pm
new file mode 100644
index 0000000..b4ce41c
--- /dev/null
+++ b/rt/lib/RT/Condition/Overdue.pm
@@ -0,0 +1,92 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+
+=head1 NAME
+
+RT::Condition::Overdue
+
+=head1 DESCRIPTION
+
+Returns true if the ticket we're operating on is overdue
+
+=cut
+
+package RT::Condition::Overdue;
+require RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+If the due date is before "now" return true
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ if ($self->TicketObj->DueObj->Unix > 0 and
+ $self->TicketObj->DueObj->Unix < time()) {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+eval "require RT::Condition::Overdue_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Overdue_Vendor.pm});
+eval "require RT::Condition::Overdue_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Overdue_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Condition/OwnerChange.pm b/rt/lib/RT/Condition/OwnerChange.pm
new file mode 100644
index 0000000..4052812
--- /dev/null
+++ b/rt/lib/RT/Condition/OwnerChange.pm
@@ -0,0 +1,124 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+
+package RT::Condition::OwnerChange;
+require RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+If we're changing the owner return true, otherwise return false
+
+=begin testing
+
+my $q = RT::Queue->new($RT::SystemUser);
+$q->Create(Name =>'ownerChangeTest');
+
+ok($q->Id, "Created a scriptest queue");
+
+my $s1 = RT::Scrip->new($RT::SystemUser);
+my ($val, $msg) =$s1->Create( Queue => $q->Id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'On Owner Change',
+ CustomIsApplicableCode => '',
+ CustomPrepareCode => 'return 1',
+ CustomCommitCode => '
+ $self->TicketObj->SetPriority($self->TicketObj->Priority+1);
+ return(1);
+ ',
+ Template => 'Blank'
+ );
+ok($val,$msg);
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id,
+ Subject => "hair on fire",
+ InitialPriority => '20'
+ );
+ok($tv, $tm);
+ok($ticket->SetOwner('root'));
+is ($ticket->Priority , '21', "Ticket priority is set right");
+ok($ticket->Steal);
+is ($ticket->Priority , '22', "Ticket priority is set right");
+ok($ticket->Untake);
+is ($ticket->Priority , '23', "Ticket priority is set right");
+ok($ticket->Take);
+is ($ticket->Priority , '24', "Ticket priority is set right");
+
+
+
+
+
+=end testing
+
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ if ($self->TransactionObj->Field eq 'Owner') {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+eval "require RT::Condition::OwnerChange_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/OwnerChange_Vendor.pm});
+eval "require RT::Condition::OwnerChange_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/OwnerChange_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Condition/PriorityChange.pm b/rt/lib/RT/Condition/PriorityChange.pm
new file mode 100644
index 0000000..60fa50a
--- /dev/null
+++ b/rt/lib/RT/Condition/PriorityChange.pm
@@ -0,0 +1,82 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+
+package RT::Condition::PriorityChange;
+require RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+If the argument passed in is equivalent to the new value of
+the Priority Obj
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ if ($self->TransactionObj->Field eq 'Priority') {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+eval "require RT::Condition::PriorityChange_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/PriorityChange_Vendor.pm});
+eval "require RT::Condition::PriorityChange_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/PriorityChange_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Condition/PriorityExceeds.pm b/rt/lib/RT/Condition/PriorityExceeds.pm
new file mode 100644
index 0000000..0b7ec8f
--- /dev/null
+++ b/rt/lib/RT/Condition/PriorityExceeds.pm
@@ -0,0 +1,81 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+
+package RT::Condition::PriorityExceeds;
+require RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+If the priority exceeds the argument value
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ if ($self->TicketObj->Priority > $self->Argument) {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+eval "require RT::Condition::PriorityExceeds_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/PriorityExceeds_Vendor.pm});
+eval "require RT::Condition::PriorityExceeds_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/PriorityExceeds_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Condition/QueueChange.pm b/rt/lib/RT/Condition/QueueChange.pm
new file mode 100644
index 0000000..8b89e56
--- /dev/null
+++ b/rt/lib/RT/Condition/QueueChange.pm
@@ -0,0 +1,81 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+
+package RT::Condition::QueueChange;
+require RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+If the queue has changed.
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ if ($self->TransactionObj->Field eq 'Queue') {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+eval "require RT::Condition::QueueChange_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/QueueChange_Vendor.pm});
+eval "require RT::Condition::QueueChange_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/QueueChange_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Condition/StatusChange.pm b/rt/lib/RT/Condition/StatusChange.pm
new file mode 100644
index 0000000..b18996d
--- /dev/null
+++ b/rt/lib/RT/Condition/StatusChange.pm
@@ -0,0 +1,83 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+
+package RT::Condition::StatusChange;
+require RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+If the argument passed in is equivalent to the new value of
+the Status Obj
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ if (($self->TransactionObj->Field eq 'Status') and
+ ($self->Argument eq $self->TransactionObj->NewValue())) {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+eval "require RT::Condition::StatusChange_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/StatusChange_Vendor.pm});
+eval "require RT::Condition::StatusChange_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/StatusChange_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/Condition/UserDefined.pm b/rt/lib/RT/Condition/UserDefined.pm
new file mode 100644
index 0000000..49d6293
--- /dev/null
+++ b/rt/lib/RT/Condition/UserDefined.pm
@@ -0,0 +1,81 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+
+
+package RT::Condition::UserDefined;
+
+use RT::Condition::Generic;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
+
+
+=head2 IsApplicable
+
+This happens on every transaction. it's always applicable
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ my $retval = eval $self->ScripObj->CustomIsApplicableCode;
+ if ($@) {
+ $RT::Logger->error("Scrip ".$self->ScripObj->Id. " IsApplicable failed: ".$@);
+ return (undef);
+ }
+ return ($retval);
+}
+
+eval "require RT::Condition::UserDefined_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/UserDefined_Vendor.pm});
+eval "require RT::Condition::UserDefined_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/UserDefined_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/CurrentUser.pm b/rt/lib/RT/CurrentUser.pm
new file mode 100755
index 0000000..9d406cc
--- /dev/null
+++ b/rt/lib/RT/CurrentUser.pm
@@ -0,0 +1,481 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::CurrentUser - an RT object representing the current user
+
+=head1 SYNOPSIS
+
+ use RT::CurrentUser
+
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::CurrentUser);
+
+=end testing
+
+=cut
+
+
+package RT::CurrentUser;
+
+use RT::Record;
+use RT::I18N;
+
+use strict;
+use base qw/RT::Record/;
+
+# {{{ sub _Init
+
+#The basic idea here is that $self->CurrentUser is always supposed
+# to be a CurrentUser object. but that's hard to do when we're trying to load
+# the CurrentUser object
+
+sub _Init {
+ my $self = shift;
+ my $User = shift;
+
+ $self->{'table'} = "Users";
+
+ if ( defined($User) ) {
+
+ if ( UNIVERSAL::isa( $User, 'RT::User' )
+ || UNIVERSAL::isa( $User, 'RT::CurrentUser' ) )
+ {
+ $self->Load( $User->id );
+
+ }
+ elsif ( ref($User) ) {
+ $RT::Logger->crit(
+ "RT::CurrentUser->new() called with a bogus argument: $User");
+ }
+ else {
+ $self->Load($User);
+ }
+ }
+
+ $self->_BuildTableAttributes();
+
+}
+# }}}
+
+# {{{ sub Create
+
+sub Create {
+ my $self = shift;
+ return (0, $self->loc('Permission Denied'));
+}
+
+# }}}
+
+# {{{ sub Delete
+
+sub Delete {
+ my $self = shift;
+ return (0, $self->loc('Permission Denied'));
+}
+
+# }}}
+
+# {{{ sub UserObj
+
+=head2 UserObj
+
+ Returns the RT::User object associated with this CurrentUser object.
+
+=cut
+
+sub UserObj {
+ my $self = shift;
+
+ use RT::User;
+ my $user = RT::User->new($self);
+
+ unless ($user->Load($self->Id)) {
+ $RT::Logger->err($self->loc("Couldn't load [_1] from the users database.\n", $self->Id));
+ }
+ return ($user);
+}
+# }}}
+
+# {{{ sub PrincipalObj
+
+=head2 PrincipalObj
+
+ Returns this user's principal object. this is just a helper routine for
+ $self->UserObj->PrincipalObj
+
+=cut
+
+sub PrincipalObj {
+ my $self = shift;
+ return($self->UserObj->PrincipalObj);
+}
+
+
+# }}}
+
+
+# {{{ sub PrincipalId
+
+=head2 PrincipalId
+
+ Returns this user's principal Id. this is just a helper routine for
+ $self->UserObj->PrincipalId
+
+=cut
+
+sub PrincipalId {
+ my $self = shift;
+ return($self->UserObj->PrincipalId);
+}
+
+
+# }}}
+
+
+# {{{ sub _Accessible
+
+
+ sub _CoreAccessible {
+ {
+ Name => { 'read' => 1 },
+ Gecos => { 'read' => 1 },
+ RealName => { 'read' => 1 },
+ Lang => { 'read' => 1 },
+ Password => { 'read' => 0, 'write' => 0 },
+ EmailAddress => { 'read' => 1, 'write' => 0 }
+ };
+
+}
+# }}}
+
+# {{{ sub LoadByEmail
+
+=head2 LoadByEmail
+
+Loads a User into this CurrentUser object.
+Takes the email address of the user to load.
+
+=cut
+
+sub LoadByEmail {
+ my $self = shift;
+ my $identifier = shift;
+
+ $identifier = RT::User::CanonicalizeEmailAddress(undef, $identifier);
+
+ $self->LoadByCol("EmailAddress",$identifier);
+
+}
+# }}}
+
+# {{{ sub LoadByGecos
+
+=head2 LoadByGecos
+
+Loads a User into this CurrentUser object.
+Takes a unix username as its only argument.
+
+=cut
+
+sub LoadByGecos {
+ my $self = shift;
+ my $identifier = shift;
+
+ $self->LoadByCol("Gecos",$identifier);
+
+}
+# }}}
+
+# {{{ sub LoadByName
+
+=head2 LoadByName
+
+Loads a User into this CurrentUser object.
+Takes a Name.
+
+=cut
+
+sub LoadByName {
+ my $self = shift;
+ my $identifier = shift;
+ $self->LoadByCol("Name",$identifier);
+
+}
+# }}}
+
+# {{{ sub Load
+
+=head2 Load
+
+Loads a User into this CurrentUser object.
+Takes either an integer (users id column reference) or a Name
+The latter is deprecated. Instead, you should use LoadByName.
+Formerly, this routine also took email addresses.
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift;
+
+ #if it's an int, load by id. otherwise, load by name.
+ if ($identifier !~ /\D/) {
+ $self->SUPER::LoadById($identifier);
+ }
+
+ elsif (UNIVERSAL::isa($identifier,"RT::User")) {
+ # DWIM if they pass a user in
+ $self->SUPER::LoadById($identifier->Id);
+ }
+ else {
+ # This is a bit dangerous, we might get false authen if somebody
+ # uses ambigous userids or real names:
+ $self->LoadByCol("Name",$identifier);
+ }
+}
+
+# }}}
+
+# {{{ sub IsPassword
+
+=head2 IsPassword
+
+Takes a password as a string. Passes it off to IsPassword in this
+user's UserObj. If it is the user's password and the user isn't
+disabled, returns 1.
+
+Otherwise, returns undef.
+
+=cut
+
+sub IsPassword {
+ my $self = shift;
+ my $value = shift;
+
+ return ($self->UserObj->IsPassword($value));
+}
+
+# }}}
+
+# {{{ sub Privileged
+
+=head2 Privileged
+
+Returns true if the current user can be granted rights and be
+a member of groups.
+
+=cut
+
+sub Privileged {
+ my $self = shift;
+ return ($self->UserObj->Privileged());
+}
+
+# }}}
+
+
+# {{{ sub HasRight
+
+=head2 HasRight
+
+calls $self->UserObj->HasRight with the arguments passed in
+
+=cut
+
+sub HasRight {
+ my $self = shift;
+ return ($self->UserObj->HasRight(@_));
+}
+
+# }}}
+
+# {{{ Localization
+
+=head2 LanguageHandle
+
+Returns this current user's langauge handle. Should take a language
+specification. but currently doesn't
+
+=begin testing
+
+ok (my $cu = RT::CurrentUser->new('root'));
+ok (my $lh = $cu->LanguageHandle('en-us'));
+ok (defined $lh);
+ok ($lh->isa('Locale::Maketext'));
+is ($cu->loc('TEST_STRING'), "Concrete Mixer", "Localized TEST_STRING into English");
+ok ($lh = $cu->LanguageHandle('fr'));
+SKIP: {
+ skip "fr locale is not loaded", 1 unless grep $_ eq 'fr', @RT::LexiconLanguages;
+ is ($cu->loc('Before'), "Avant", "Localized TEST_STRING into Frenc");
+}
+
+=end testing
+
+=cut
+
+sub LanguageHandle {
+ my $self = shift;
+ if ( ( !defined $self->{'LangHandle'} )
+ || ( !UNIVERSAL::can( $self->{'LangHandle'}, 'maketext' ) )
+ || (@_) ) {
+ if ( !$RT::SystemUser or ($self->id || 0) == $RT::SystemUser->id() ) {
+ @_ = qw(en-US);
+ }
+
+ elsif ( $self->Lang ) {
+ push @_, $self->Lang;
+ }
+ $self->{'LangHandle'} = RT::I18N->get_handle(@_);
+ }
+
+ # Fall back to english.
+ unless ( $self->{'LangHandle'} ) {
+ die "We couldn't get a dictionary. Nye mogu naidti slovar. No puedo encontrar dictionario.";
+ }
+ return ( $self->{'LangHandle'} );
+}
+
+sub loc {
+ my $self = shift;
+ return '' if $_[0] eq '';
+
+ my $handle = $self->LanguageHandle;
+
+ if (@_ == 1) {
+ # pre-scan the lexicon hashes to return _AUTO keys verbatim,
+ # to keep locstrings containing '[' and '~' from tripping over Maketext
+ return $_[0] unless grep { exists $_->{$_[0]} } @{ $handle->_lex_refs };
+ }
+
+ return $handle->maketext(@_);
+}
+
+sub loc_fuzzy {
+ my $self = shift;
+ return '' if (!$_[0] || $_[0] eq '');
+
+ # XXX: work around perl's deficiency when matching utf8 data
+ return $_[0] if Encode::is_utf8($_[0]);
+ my $result = $self->LanguageHandle->maketext_fuzzy(@_);
+
+ return($result);
+}
+# }}}
+
+
+=head2 CurrentUser
+
+Return the current currentuser object
+
+=cut
+
+sub CurrentUser {
+ my $self = shift;
+ return($self);
+
+}
+
+=head2 Authenticate
+
+Takes $password, $created and $nonce, and returns a boolean value
+representing whether the authentication succeeded.
+
+If both $nonce and $created are specified, validate $password against:
+
+ encode_base64(sha1(
+ $nonce .
+ $created .
+ sha1_hex( "$username:$realm:$server_pass" )
+ ))
+
+where $server_pass is the md5_hex(password) digest stored in the
+database, $created is in ISO time format, and $nonce is a random
+string no longer than 32 bytes.
+
+=cut
+
+sub Authenticate {
+ my ($self, $password, $created, $nonce, $realm) = @_;
+
+ require Digest::MD5;
+ require Digest::SHA1;
+ require MIME::Base64;
+
+ my $username = $self->UserObj->Name or return;
+ my $server_pass = $self->UserObj->__Value('Password') or return;
+ my $auth_digest = MIME::Base64::encode_base64(Digest::SHA1::sha1(
+ $nonce .
+ $created .
+ Digest::MD5::md5_hex("$username:$realm:$server_pass")
+ ));
+
+ chomp($password);
+ chomp($auth_digest);
+
+ return ($password eq $auth_digest);
+}
+
+# }}}
+
+
+eval "require RT::CurrentUser_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Vendor.pm});
+eval "require RT::CurrentUser_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Local.pm});
+
+1;
+
diff --git a/rt/lib/RT/CustomField.pm b/rt/lib/RT/CustomField.pm
new file mode 100644
index 0000000..c062793
--- /dev/null
+++ b/rt/lib/RT/CustomField.pm
@@ -0,0 +1,421 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::CustomField
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::CustomField;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('CustomFields');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(200) 'Type'.
+ int(11) 'MaxValues'.
+ varchar(255) 'Pattern'.
+ smallint(6) 'Repeated'.
+ varchar(255) 'Description'.
+ int(11) 'SortOrder'.
+ varchar(255) 'LookupType'.
+ smallint(6) 'Disabled'.
+
+ 'LookupType' is generally the result of either
+ RT::Ticket->CustomFieldLookupType or RT::Transaction->CustomFieldLookupType
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Type => '',
+ MaxValues => '',
+ Pattern => '',
+ Repeated => '0',
+ Description => '',
+ SortOrder => '0',
+ LookupType => '',
+ Disabled => '0',
+
+ @_);
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Type => $args{'Type'},
+ MaxValues => $args{'MaxValues'},
+ Pattern => $args{'Pattern'},
+ Repeated => $args{'Repeated'},
+ Description => $args{'Description'},
+ SortOrder => $args{'SortOrder'},
+ LookupType => $args{'LookupType'},
+ Disabled => $args{'Disabled'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Type
+
+Returns the current value of Type.
+(In the database, Type is stored as varchar(200).)
+
+
+
+=head2 SetType VALUE
+
+
+Set Type to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Type will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 MaxValues
+
+Returns the current value of MaxValues.
+(In the database, MaxValues is stored as int(11).)
+
+
+
+=head2 SetMaxValues VALUE
+
+
+Set MaxValues to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, MaxValues will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Pattern
+
+Returns the current value of Pattern.
+(In the database, Pattern is stored as varchar(255).)
+
+
+
+=head2 SetPattern VALUE
+
+
+Set Pattern to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Pattern will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Repeated
+
+Returns the current value of Repeated.
+(In the database, Repeated is stored as smallint(6).)
+
+
+
+=head2 SetRepeated VALUE
+
+
+Set Repeated to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Repeated will be stored as a smallint(6).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 SortOrder
+
+Returns the current value of SortOrder.
+(In the database, SortOrder is stored as int(11).)
+
+
+
+=head2 SetSortOrder VALUE
+
+
+Set SortOrder to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, SortOrder will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 LookupType
+
+Returns the current value of LookupType.
+(In the database, LookupType is stored as varchar(255).)
+
+
+
+=head2 SetLookupType VALUE
+
+
+Set LookupType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, LookupType will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+=head2 Disabled
+
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
+
+
+
+=head2 SetDisabled VALUE
+
+
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Type =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ MaxValues =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Pattern =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Repeated =>
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ SortOrder =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LookupType =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Disabled =>
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::CustomField_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomField_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomField_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomField_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomField_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomField_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::CustomField_Overlay, RT::CustomField_Vendor, RT::CustomField_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/CustomFieldValue.pm b/rt/lib/RT/CustomFieldValue.pm
new file mode 100644
index 0000000..f4d7769
--- /dev/null
+++ b/rt/lib/RT/CustomFieldValue.pm
@@ -0,0 +1,318 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::CustomFieldValue
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::CustomFieldValue;
+use RT::Record;
+use RT::CustomField;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('CustomFieldValues');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'CustomField'.
+ varchar(200) 'Name'.
+ varchar(255) 'Description'.
+ int(11) 'SortOrder'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ CustomField => '0',
+ Name => '',
+ Description => '',
+ SortOrder => '0',
+
+ @_);
+ $self->SUPER::Create(
+ CustomField => $args{'CustomField'},
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ SortOrder => $args{'SortOrder'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 CustomField
+
+Returns the current value of CustomField.
+(In the database, CustomField is stored as int(11).)
+
+
+
+=head2 SetCustomField VALUE
+
+
+Set CustomField to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CustomField will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 CustomFieldObj
+
+Returns the CustomField Object which has the id returned by CustomField
+
+
+=cut
+
+sub CustomFieldObj {
+ my $self = shift;
+ my $CustomField = RT::CustomField->new($self->CurrentUser);
+ $CustomField->Load($self->__Value('CustomField'));
+ return($CustomField);
+}
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 SortOrder
+
+Returns the current value of SortOrder.
+(In the database, SortOrder is stored as int(11).)
+
+
+
+=head2 SetSortOrder VALUE
+
+
+Set SortOrder to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, SortOrder will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ CustomField =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ SortOrder =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::CustomFieldValue_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFieldValue_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomFieldValue_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFieldValue_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomFieldValue_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFieldValue_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::CustomFieldValue_Overlay, RT::CustomFieldValue_Vendor, RT::CustomFieldValue_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/CustomFieldValue_Overlay.pm b/rt/lib/RT/CustomFieldValue_Overlay.pm
new file mode 100644
index 0000000..0f0c590
--- /dev/null
+++ b/rt/lib/RT/CustomFieldValue_Overlay.pm
@@ -0,0 +1,97 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use warnings;
+use strict;
+
+package RT::CustomFieldValue;
+
+no warnings qw/redefine/;
+
+
+=head2 ValidateName
+
+Override the default ValidateName method that stops custom field values
+from being integers.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = @_;
+ (defined $args{$_} or delete $args{$_}) for keys %args;
+ %args = ((CustomField => '0',
+ Name => '',
+ Description => '',
+ SortOrder => '0',
+ Category => ''), %args);
+
+ my ($id, $msg) = $self->SUPER::Create(
+ map {$_ => $args{$_}} qw(CustomField Name Description SortOrder)
+ );
+ if ($id and length $args{Category}) {
+ # $self would be loaded at this stage
+ $self->SetCategory($args{Category});
+ }
+ return ($id, $msg);
+}
+
+sub Category {
+ my $self = shift;
+ my $attr = $self->FirstAttribute('Category') or return undef;
+ return $attr->Content;
+}
+
+sub SetCategory {
+ my $self = shift;
+ my $category = shift;
+ $self->SetAttribute(Name => 'Category', Content => $category);
+}
+
+sub ValidateName { 1 };
+
+1;
diff --git a/rt/lib/RT/CustomFieldValues.pm b/rt/lib/RT/CustomFieldValues.pm
new file mode 100644
index 0000000..6477212
--- /dev/null
+++ b/rt/lib/RT/CustomFieldValues.pm
@@ -0,0 +1,151 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::CustomFieldValues -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::CustomFieldValues
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::CustomFieldValues;
+
+use RT::SearchBuilder;
+use RT::CustomFieldValue;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'CustomFieldValues';
+ $self->{'primary_key'} = 'id';
+
+ # By default, order by SortOrder
+ $self->OrderByCols(
+ { ALIAS => 'main',
+ FIELD => 'SortOrder',
+ ORDER => 'ASC' },
+ { ALIAS => 'main',
+ FIELD => 'Name',
+ ORDER => 'ASC' },
+ { ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'ASC' },
+ );
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::CustomFieldValue item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::CustomFieldValue->new($self->CurrentUser));
+}
+
+ eval "require RT::CustomFieldValues_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFieldValues_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomFieldValues_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFieldValues_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomFieldValues_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFieldValues_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::CustomFieldValues_Overlay, RT::CustomFieldValues_Vendor, RT::CustomFieldValues_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/CustomFieldValues_Overlay.pm b/rt/lib/RT/CustomFieldValues_Overlay.pm
new file mode 100644
index 0000000..79c2161
--- /dev/null
+++ b/rt/lib/RT/CustomFieldValues_Overlay.pm
@@ -0,0 +1,73 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::CustomFieldValues;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub LimitToCustomField
+
+=head2 LimitToCustomField FIELD
+
+Limits the returned set to values for the custom field with Id FIELD
+
+=cut
+
+sub LimitToCustomField {
+ my $self = shift;
+ my $cf = shift;
+ return ($self->Limit( FIELD => 'CustomField',
+ VALUE => $cf,
+ OPERATOR => '='));
+
+}
+
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/CustomField_Overlay.pm b/rt/lib/RT/CustomField_Overlay.pm
new file mode 100644
index 0000000..5a4535e
--- /dev/null
+++ b/rt/lib/RT/CustomField_Overlay.pm
@@ -0,0 +1,1300 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::CustomField;
+
+use strict;
+no warnings qw(redefine);
+
+use vars qw(%FieldTypes $RIGHTS %FRIENDLY_OBJECT_TYPES);
+
+use RT::CustomFieldValues;
+use RT::ObjectCustomFieldValues;
+
+
+%FieldTypes = (
+ Select => [
+ 'Select multiple values', # loc
+ 'Select one value', # loc
+ 'Select up to [_1] values', # loc
+ ],
+ Freeform => [
+ 'Enter multiple values', # loc
+ 'Enter one value', # loc
+ 'Enter up to [_1] values', # loc
+ ],
+ Text => [
+ 'Fill in multiple text areas', # loc
+ 'Fill in one text area', # loc
+ 'Fill in up to [_1] text areas',# loc
+ ],
+ Wikitext => [
+ 'Fill in multiple wikitext areas', # loc
+ 'Fill in one wikitext area', # loc
+ 'Fill in up to [_1] wikitext areas',# loc
+ ],
+ Image => [
+ 'Upload multiple images', # loc
+ 'Upload one image', # loc
+ 'Upload up to [_1] images', # loc
+ ],
+ Binary => [
+ 'Upload multiple files', # loc
+ 'Upload one file', # loc
+ 'Upload up to [_1] files', # loc
+ ],
+ Combobox => [
+ 'Combobox: Select or enter multiple values', # loc
+ 'Combobox: Select or enter one value', # loc
+ 'Combobox: Select or enter up to [_1] values', # loc
+ ],
+);
+
+
+%FRIENDLY_OBJECT_TYPES = ();
+
+RT::CustomField->_ForObjectType( 'RT::Queue-RT::Ticket' => "Tickets", ); #loc
+RT::CustomField->_ForObjectType(
+ 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", ); #loc
+RT::CustomField->_ForObjectType( 'RT::User' => "Users", ); #loc
+RT::CustomField->_ForObjectType( 'RT::Group' => "Groups", ); #loc
+
+$RIGHTS = {
+ SeeCustomField => 'See custom fields', # loc_pair
+ AdminCustomField => 'Create, delete and modify custom fields', # loc_pair
+ ModifyCustomField => 'Add, delete and modify custom field values for objects' #loc_pair
+
+};
+
+# Tell RT::ACE that this sort of object can get acls granted
+$RT::ACE::OBJECT_TYPES{'RT::CustomField'} = 1;
+
+foreach my $right ( keys %{$RIGHTS} ) {
+ $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
+}
+
+sub AvailableRights {
+ my $self = shift;
+ return($RIGHTS);
+}
+
+=head1 NAME
+
+ RT::CustomField_Overlay
+
+=head1 DESCRIPTION
+
+=head1 'CORE' METHODS
+
+=cut
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(200) 'Type'.
+ int(11) 'MaxValues'.
+ varchar(255) 'Pattern'.
+ smallint(6) 'Repeated'.
+ varchar(255) 'Description'.
+ int(11) 'SortOrder'.
+ varchar(255) 'LookupType'.
+ smallint(6) 'Disabled'.
+
+ 'LookupType' is generally the result of either
+ RT::Ticket->CustomFieldLookupType or RT::Transaction->CustomFieldLookupType
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Type => '',
+ MaxValues => '0',
+ Pattern => '',
+ Description => '',
+ Disabled => '0',
+ LookupType => '',
+ Repeated => '0',
+
+ @_);
+
+ unless ($self->CurrentUser->HasRight(Object => $RT::System, Right => 'AdminCustomField')) {
+ return (0, $self->loc('Permission Denied'));
+ }
+
+
+ if ($args{TypeComposite}) {
+ @args{'Type', 'MaxValues'} = split(/-/, $args{TypeComposite}, 2);
+ }
+ elsif ($args{Type} =~ s/(?:(Single)|Multiple)$//) {
+ # old style Type string
+ $args{'MaxValues'} = $1 ? 1 : 0;
+ }
+
+ if ( !exists $args{'Queue'}) {
+ # do nothing -- things below are strictly backward compat
+ }
+ elsif ( ! $args{'Queue'} ) {
+ unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'AssignCustomFields') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $args{'LookupType'} = 'RT::Queue-RT::Ticket';
+ }
+ else {
+ my $queue = RT::Queue->new($self->CurrentUser);
+ $queue->Load($args{'Queue'});
+ unless ($queue->Id) {
+ return (0, $self->loc("Queue not found"));
+ }
+ unless ( $queue->CurrentUserHasRight('AssignCustomFields') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $args{'LookupType'} = 'RT::Queue-RT::Ticket';
+ $args{'Queue'} = $queue->Id;
+ }
+
+ my ($ok, $msg) = $self->_IsValidRegex($args{'Pattern'});
+ if (!$ok) {
+ return (0, $self->loc("Invalid pattern: [_1]", $msg));
+ }
+
+ my $rv = $self->SUPER::Create(
+ Name => $args{'Name'},
+ Type => $args{'Type'},
+ MaxValues => $args{'MaxValues'},
+ Pattern => $args{'Pattern'},
+ Description => $args{'Description'},
+ Disabled => $args{'Disabled'},
+ LookupType => $args{'LookupType'},
+ Repeated => $args{'Repeated'},
+);
+
+ return $rv unless exists $args{'Queue'};
+
+ # Compat code -- create a new ObjectCustomField mapping
+ my $OCF = RT::ObjectCustomField->new($self->CurrentUser);
+ $OCF->Create(
+ CustomField => $self->Id,
+ ObjectId => $args{'Queue'},
+ );
+
+ return $rv;
+}
+
+=head2 Load ID/NAME
+
+Load a custom field. If the value handed in is an integer, load by custom field ID. Otherwise, Load by name.
+
+=cut
+
+
+sub Load {
+ my $self = shift;
+ my $id = shift;
+
+ if ($id =~ /^\d+$/) {
+ return ($self->SUPER::Load($id));
+ } else {
+ return($self->LoadByName(Name => $id));
+ }
+}
+
+
+# {{{ sub LoadByName
+
+=head2 LoadByName (Queue => QUEUEID, Name => NAME)
+
+Loads the Custom field named NAME.
+
+If a Queue parameter is specified, only look for ticket custom fields tied to that Queue.
+
+If the Queue parameter is '0', look for global ticket custom fields.
+
+If no queue parameter is specified, look for any and all custom fields with this name.
+
+BUG/TODO, this won't let you specify that you only want user or group CFs.
+
+=cut
+
+# Compatibility for API change after 3.0 beta 1
+*LoadNameAndQueue = \&LoadByName;
+# Change after 3.4 beta.
+*LoadByNameAndQueue = \&LoadByName;
+
+sub LoadByName {
+ my $self = shift;
+ my %args = (
+ Queue => undef,
+ Name => undef,
+ @_,
+ );
+
+ # if we're looking for a queue by name, make it a number
+ if (defined $args{'Queue'} && $args{'Queue'} !~ /^\d+$/) {
+ my $QueueObj = RT::Queue->new($self->CurrentUser);
+ $QueueObj->Load($args{'Queue'});
+ $args{'Queue'} = $QueueObj->Id;
+ }
+
+ # XXX - really naive implementation. Slow. - not really. still just one query
+
+ my $CFs = RT::CustomFields->new($self->CurrentUser);
+
+ $CFs->Limit( FIELD => 'Name', VALUE => $args{'Name'}, CASESENSITIVE => 0);
+ # Don't limit to queue if queue is 0. Trying to do so breaks
+ # RT::Group type CFs.
+ if (defined $args{'Queue'}) {
+ $CFs->LimitToQueue( $args{'Queue'} );
+ }
+
+ # When loading by name, it's ok if they're disabled. That's not a big deal.
+ $CFs->{'find_disabled_rows'}=1;
+
+ # We only want one entry.
+ $CFs->RowsPerPage(1);
+ unless ($CFs->First) {
+ return(0);
+ }
+ return($self->Load($CFs->First->id));
+
+}
+
+# }}}
+
+# {{{ Dealing with custom field values
+
+=begin testing
+
+use_ok(RT::CustomField);
+ok(my $cf = RT::CustomField->new($RT::SystemUser));
+ok(my ($id, $msg)= $cf->Create( Name => 'TestingCF',
+ Queue => '0',
+ SortOrder => '1',
+ Description => 'A Testing custom field',
+ Type=> 'SelectSingle'), 'Created a global CustomField');
+ok($id != 0, 'Global custom field correctly created');
+ok ($cf->SingleValue);
+is($cf->Type, 'Select');
+is($cf->MaxValues, 1);
+
+my ($val, $msg) = $cf->SetMaxValues('0');
+ok($val, $msg);
+is($cf->Type, 'Select');
+is($cf->MaxValues, 0);
+ok(!$cf->SingleValue );
+ok(my ($bogus_val, $bogus_msg) = $cf->SetType('BogusType') , "Trying to set a custom field's type to a bogus type");
+ok($bogus_val == 0, "Unable to set a custom field's type to a bogus type");
+
+ok(my $bad_cf = RT::CustomField->new($RT::SystemUser));
+ok(my ($bad_id, $bad_msg)= $cf->Create( Name => 'TestingCF-bad',
+ Queue => '0',
+ SortOrder => '1',
+ Description => 'A Testing custom field with a bogus Type',
+ Type=> 'SelectSingleton'), 'Created a global CustomField with a bogus type');
+ok($bad_id == 0, 'Global custom field correctly decided to not create a cf with a bogus type ');
+
+=end testing
+
+=cut
+
+# {{{ AddValue
+
+=head2 AddValue HASH
+
+Create a new value for this CustomField. Takes a paramhash containing the elements Name, Description and SortOrder
+
+=begin testing
+
+ok(my $cf = RT::CustomField->new($RT::SystemUser));
+$cf->Load(1);
+ok($cf->Id == 1);
+ok(my ($val,$msg) = $cf->AddValue(Name => 'foo' , Description => 'TestCFValue', SortOrder => '6'));
+ok($val != 0);
+ok (my ($delval, $delmsg) = $cf->DeleteValue($val));
+ok ($delval,"Deleting a cf value: $delmsg");
+
+=end testing
+
+=cut
+
+sub AddValue {
+ my $self = shift;
+ my %args = @_;
+
+ unless ($self->CurrentUserHasRight('AdminCustomField')) {
+ return (0, $self->loc('Permission Denied'));
+ }
+
+ # allow zero value
+ if ( !defined $args{'Name'} || $args{'Name'} eq '' ) {
+ return(0, $self->loc("Can't add a custom field value without a name"));
+ }
+
+ my $newval = RT::CustomFieldValue->new($self->CurrentUser);
+ return($newval->Create(%args, CustomField => $self->Id));
+}
+
+
+# }}}
+
+# {{{ DeleteValue
+
+=head2 DeleteValue ID
+
+Deletes a value from this custom field by id.
+
+Does not remove this value for any article which has had it selected
+
+=cut
+
+sub DeleteValue {
+ my $self = shift;
+ my $id = shift;
+ unless ($self->CurrentUserHasRight('AdminCustomField')) {
+ return (0, $self->loc('Permission Denied'));
+ }
+
+ my $val_to_del = RT::CustomFieldValue->new($self->CurrentUser);
+ $val_to_del->Load($id);
+ unless ($val_to_del->Id) {
+ return (0, $self->loc("Couldn't find that value"));
+ }
+ unless ($val_to_del->CustomField == $self->Id) {
+ return (0, $self->loc("That is not a value for this custom field"));
+ }
+
+ my $retval = $val_to_del->Delete();
+ if ($retval) {
+ return ($retval, $self->loc("Custom field value deleted"));
+ } else {
+ return(0, $self->loc("Custom field value could not be deleted"));
+ }
+}
+
+# }}}
+
+# {{{ Values
+
+=head2 Values FIELD
+
+Return a CustomFieldeValues object of all acceptable values for this Custom Field.
+
+
+=cut
+
+*ValuesObj = \&Values;
+
+sub Values {
+ my $self = shift;
+
+ my $cf_values = RT::CustomFieldValues->new($self->CurrentUser);
+ # if the user has no rights, return an empty object
+ if ($self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
+ $cf_values->LimitToCustomField($self->Id);
+ }
+ return ($cf_values);
+}
+
+# }}}
+
+# }}}
+
+# {{{ Ticket related routines
+
+# {{{ ValuesForTicket
+
+=head2 ValuesForTicket TICKET
+
+Returns a RT::ObjectCustomFieldValues object of this Field's values for TICKET.
+TICKET is a ticket id.
+
+This is deprecated -- use ValuesForObject instead.
+
+
+=cut
+
+sub ValuesForTicket {
+ my $self = shift;
+ my $ticket_id = shift;
+
+ $RT::Logger->debug( ref($self) . " -> ValuesForTicket deprecated in favor of ValuesForObject at (". join(":",caller).")");
+ my $ticket = RT::Ticket->new($self->CurrentUser);
+ $ticket->Load($ticket_id);
+
+ return $self->ValuesForObject($ticket);
+}
+
+# }}}
+
+# {{{ AddValueForTicket
+
+=head2 AddValueForTicket HASH
+
+Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
+
+This is deprecated -- use AddValueForObject instead.
+
+=cut
+
+sub AddValueForTicket {
+ my $self = shift;
+ my %args = ( Ticket => undef,
+ Content => undef,
+ @_ );
+ $RT::Logger->debug( ref($self) . " -> AddValueForTicket deprecated in favor of AddValueForObject at (". join(":",caller).")");
+
+
+ my $ticket = RT::Ticket->new($self->CurrentUser);
+ $ticket->Load($args{'Ticket'});
+ return($self->AddValueForObject(Content => $args{'Content'}, Object => $ticket,@_));
+
+}
+
+
+# }}}
+
+# {{{ DeleteValueForTicket
+
+=head2 DeleteValueForTicket HASH
+
+Adds a custom field value for a ticket. Takes a param hash of Ticket and Content
+
+This is deprecated -- use DeleteValueForObject instead.
+
+=cut
+
+sub DeleteValueForTicket {
+ my $self = shift;
+ my %args = ( Ticket => undef,
+ Content => undef,
+ @_ );
+
+ $RT::Logger->debug( ref($self) . " -> DeleteValueForTicket deprecated in favor of DeleteValueForObject at (". join(":",caller).")");
+
+
+ my $ticket = RT::Ticket->new($self->CurrentUser);
+ $ticket->load($args{'Ticket'});
+ return ($self->DeleteValueForObject(Object => $ticket, Content => $args{'Content'}, @_));
+
+}
+
+# }}}
+# }}}
+
+
+=head2 ValidateQueue Queue
+
+Make sure that the queue specified is a valid queue name
+
+=cut
+
+sub ValidateQueue {
+ my $self = shift;
+ my $id = shift;
+
+ if ($id eq '0') { # 0 means "Global" null would _not_ be ok.
+ return (1);
+ }
+
+ my $q = RT::Queue->new($RT::SystemUser);
+ $q->Load($id);
+ unless ($q->id) {
+ return undef;
+ }
+ return (1);
+
+
+}
+
+
+# {{{ Types
+
+=head2 Types
+
+Retuns an array of the types of CustomField that are supported
+
+=cut
+
+sub Types {
+ return (keys %FieldTypes);
+}
+
+# }}}
+
+# {{{ IsSelectionType
+
+=head2 IsSelectionType
+
+Retuns a boolean value indicating whether the C<Values> method makes sense
+to this Custom Field.
+
+=cut
+
+sub IsSelectionType {
+ my $self = shift;
+ $self->Type =~ /(?:Select|Combobox)/;
+}
+
+# }}}
+
+
+=head2 FriendlyType [TYPE, MAX_VALUES]
+
+Returns a localized human-readable version of the custom field type.
+If a custom field type is specified as the parameter, the friendly type for that type will be returned
+
+=cut
+
+sub FriendlyType {
+ my $self = shift;
+
+ my $type = @_ ? shift : $self->Type;
+ my $max = @_ ? shift : $self->MaxValues;
+
+ if (my $friendly_type = $FieldTypes{$type}[$max>2 ? 2 : $max]) {
+ return ( $self->loc( $friendly_type, $max ) );
+ }
+ else {
+ return ( $self->loc( $type ) );
+ }
+}
+
+sub FriendlyTypeComposite {
+ my $self = shift;
+ my $composite = shift || $self->TypeComposite;
+ return $self->FriendlyType(split(/-/, $composite, 2));
+}
+
+
+=head2 ValidateType TYPE
+
+Takes a single string. returns true if that string is a value
+type of custom field
+
+=begin testing
+
+ok(my $cf = RT::CustomField->new($RT::SystemUser));
+ok($cf->ValidateType('SelectSingle'));
+ok($cf->ValidateType('SelectMultiple'));
+ok(!$cf->ValidateType('SelectFooMultiple'));
+
+=end testing
+
+=cut
+
+sub ValidateType {
+ my $self = shift;
+ my $type = shift;
+
+ if ($type =~ s/(?:Single|Multiple)$//) {
+ $RT::Logger->warning( "Prefix 'Single' and 'Multiple' to Type deprecated, use MaxValues instead at (". join(":",caller).")");
+ }
+
+ if( $FieldTypes{$type}) {
+ return(1);
+ }
+ else {
+ return undef;
+ }
+}
+
+
+sub SetType {
+ my $self = shift;
+ my $type = shift;
+ if ($type =~ s/(?:(Single)|Multiple)$//) {
+ $RT::Logger->warning("'Single' and 'Multiple' on SetType deprecated, use SetMaxValues instead at (". join(":",caller).")");
+ $self->SetMaxValues($1 ? 1 : 0);
+ }
+ $self->SUPER::SetType($type);
+}
+
+=head2 SetPattern STRING
+
+Takes a single string representing a regular expression. Performs basic
+validation on that regex, and sets the C<Pattern> field for the CF if it
+is valid.
+
+=cut
+
+sub SetPattern {
+ my $self = shift;
+ my $regex = shift;
+
+ my ($ok, $msg) = $self->_IsValidRegex($regex);
+ if ($ok) {
+ return $self->SUPER::SetPattern($regex);
+ }
+ else {
+ return (0, $self->loc("Invalid pattern: [_1]", $msg));
+ }
+}
+
+=head2 _IsValidRegex(Str $regex) returns (Bool $success, Str $msg)
+
+Tests if the string contains an invalid regex.
+
+=cut
+
+sub _IsValidRegex {
+ my $self = shift;
+ my $regex = shift or return (1, 'valid');
+
+ local $^W; local $@;
+ $SIG{__DIE__} = sub { 1 };
+ $SIG{__WARN__} = sub { 1 };
+
+ if (eval { qr/$regex/; 1 }) {
+ return (1, 'valid');
+ }
+
+ my $err = $@;
+ $err =~ s{[,;].*}{}; # strip debug info from error
+ chomp $err;
+ return (0, $err);
+}
+
+# {{{ SingleValue
+
+=head2 SingleValue
+
+Returns true if this CustomField only accepts a single value.
+Returns false if it accepts multiple values
+
+=cut
+
+sub SingleValue {
+ my $self = shift;
+ if ($self->MaxValues == 1) {
+ return 1;
+ }
+ else {
+ return undef;
+ }
+}
+
+sub UnlimitedValues {
+ my $self = shift;
+ if ($self->MaxValues == 0) {
+ return 1;
+ }
+ else {
+ return undef;
+ }
+}
+
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
+=head2 CurrentUserHasRight RIGHT
+
+Helper function to call the custom field's queue's CurrentUserHasRight with the passed in args.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ return $self->CurrentUser->HasRight(
+ Object => $self,
+ Right => $right,
+ );
+}
+
+# }}}
+
+# {{{ sub _Set
+
+sub _Set {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ return ( $self->SUPER::_Set(@_) );
+
+}
+
+# }}}
+
+# {{{ sub _Value
+
+=head2 _Value
+
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
+
+=cut
+
+sub _Value {
+
+ my $self = shift;
+ my $field = shift;
+
+ # we need to do the rights check
+ unless ( $self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
+ return (undef);
+ }
+ return ( $self->__Value($field) );
+
+}
+
+# }}}
+# {{{ sub SetDisabled
+
+=head2 SetDisabled
+
+Takes a boolean.
+1 will cause this custom field to no longer be avaialble for tickets.
+0 will re-enable this queue
+
+=cut
+
+# }}}
+
+sub Queue {
+ $RT::Logger->debug( ref($_[0]) . " -> Queue deprecated at (". join(":",caller).")");
+
+ return 0;
+}
+
+sub SetQueue {
+ $RT::Logger->debug( ref($_[0]) . " -> SetQueue deprecated at (". join(":",caller).")");
+
+ return 0;
+}
+
+sub QueueObj {
+ $RT::Logger->debug( ref($_[0]) . " -> QueueObj deprecated at (". join(":",caller).")");
+
+ return undef;
+}
+
+=head2 SetTypeComposite
+
+Set this custom field's type and maximum values as a composite value
+
+
+=cut
+
+sub SetTypeComposite {
+ my $self = shift;
+ my $composite = shift;
+ my ($type, $max_values) = split(/-/, $composite, 2);
+ $self->SetType($type);
+ $self->SetMaxValues($max_values);
+}
+
+=head2 SetLookupType
+
+Autrijus: care to doc how LookupTypes work?
+
+=cut
+
+sub SetLookupType {
+ my $self = shift;
+ my $lookup = shift;
+ if ($lookup ne $self->LookupType) {
+ # Okay... We need to invalidate our existing relationships
+ my $ObjectCustomFields = RT::ObjectCustomFields->new($self->CurrentUser);
+ $ObjectCustomFields->LimitToCustomField($self->Id);
+ $_->Delete foreach @{$ObjectCustomFields->ItemsArrayRef};
+ }
+ $self->SUPER::SetLookupType($lookup);
+}
+
+=head2 TypeComposite
+
+Returns a composite value composed of this object's type and maximum values
+
+=cut
+
+
+sub TypeComposite {
+ my $self = shift;
+ join('-', $self->Type, $self->MaxValues);
+}
+
+=head2 TypeComposites
+
+Returns an array of all possible composite values for custom fields.
+
+=cut
+
+sub TypeComposites {
+ my $self = shift;
+ return grep !/(?:[Tt]ext|Combobox)-0/, map { ("$_-1", "$_-0") } $self->Types;
+}
+
+=head2 LookupTypes
+
+Returns an array of LookupTypes available
+
+=cut
+
+
+sub LookupTypes {
+ my $self = shift;
+ return keys %FRIENDLY_OBJECT_TYPES;
+}
+
+my @FriendlyObjectTypes = (
+ "[_1] objects", # loc
+ "[_1]'s [_2] objects", # loc
+ "[_1]'s [_2]'s [_3] objects", # loc
+);
+
+=head2 FriendlyTypeLookup
+
+=cut
+
+sub FriendlyLookupType {
+ my $self = shift;
+ my $lookup = shift || $self->LookupType;
+
+ return ($self->loc( $FRIENDLY_OBJECT_TYPES{$lookup} ))
+ if (defined $FRIENDLY_OBJECT_TYPES{$lookup} );
+
+ my @types = map { s/^RT::// ? $self->loc($_) : $_ }
+ grep { defined and length }
+ split( /-/, $lookup )
+ or return;
+ return ( $self->loc( $FriendlyObjectTypes[$#types], @types ) );
+}
+
+
+=head2 AddToObject OBJECT
+
+Add this custom field as a custom field for a single object, such as a queue or group.
+
+Takes an object
+
+=cut
+
+
+sub AddToObject {
+ my $self = shift;
+ my $object = shift;
+ my $id = $object->Id || 0;
+
+ unless (index($self->LookupType, ref($object)) == 0) {
+ return ( 0, $self->loc('Lookup type mismatch') );
+ }
+
+ unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
+
+ $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
+ if ( $ObjectCF->Id ) {
+ return ( 0, $self->loc("That is already the current value") );
+ }
+ my ( $oid, $msg ) =
+ $ObjectCF->Create( ObjectId => $id, CustomField => $self->Id );
+
+ return ( $oid, $msg );
+}
+
+
+=head2 RemoveFromObject OBJECT
+
+Remove this custom field for a single object, such as a queue or group.
+
+Takes an object
+
+=cut
+
+
+sub RemoveFromObject {
+ my $self = shift;
+ my $object = shift;
+ my $id = $object->Id || 0;
+
+ unless (index($self->LookupType, ref($object)) == 0) {
+ return ( 0, $self->loc('Object type mismatch') );
+ }
+
+ unless ( $object->CurrentUserHasRight('AssignCustomFields') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ my $ObjectCF = RT::ObjectCustomField->new( $self->CurrentUser );
+
+ $ObjectCF->LoadByCols( ObjectId => $id, CustomField => $self->Id );
+ unless ( $ObjectCF->Id ) {
+ return ( 0, $self->loc("This custom field does not apply to that object") );
+ }
+ # XXX: Delete doesn't return anything
+ my ( $oid, $msg ) = $ObjectCF->Delete;
+
+ return ( $oid, $msg );
+}
+
+# {{{ AddValueForObject
+
+=head2 AddValueForObject HASH
+
+Adds a custom field value for a record object of some kind.
+Takes a param hash of
+
+Required:
+
+ Object
+ Content
+
+Optional:
+
+ LargeContent
+ ContentType
+
+=cut
+
+sub AddValueForObject {
+ my $self = shift;
+ my %args = (
+ Object => undef,
+ Content => undef,
+ LargeContent => undef,
+ ContentType => undef,
+ @_
+ );
+ my $obj = $args{'Object'} or return;
+
+ unless ( $self->CurrentUserHasRight('ModifyCustomField') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ unless ( $self->MatchPattern($args{Content}) ) {
+ return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
+ }
+
+ $RT::Handle->BeginTransaction;
+
+ my $current_values = $self->ValuesForObject($obj);
+
+ if ( $self->MaxValues ) {
+ my $extra_values = ( $current_values->Count + 1 ) - $self->MaxValues;
+
+ # (The +1 is for the new value we're adding)
+
+ # If we have a set of current values and we've gone over the maximum
+ # allowed number of values, we'll need to delete some to make room.
+ # which former values are blown away is not guaranteed
+
+ while ($extra_values) {
+ my $extra_item = $current_values->Next;
+
+ unless ( $extra_item->id ) {
+ $RT::Logger->crit(
+"We were just asked to delete a custom fieldvalue that doesn't exist!"
+ );
+ $RT::Handle->Rollback();
+ return (undef);
+ }
+ $extra_item->Delete;
+ $extra_values--;
+
+ }
+ }
+ my $newval = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
+ my $val = $newval->Create(
+ ObjectType => ref($obj),
+ ObjectId => $obj->Id,
+ Content => $args{'Content'},
+ LargeContent => $args{'LargeContent'},
+ ContentType => $args{'ContentType'},
+ CustomField => $self->Id
+ );
+
+ unless ($val) {
+ $RT::Handle->Rollback();
+ return ($val);
+ }
+
+ $RT::Handle->Commit();
+ return ($val);
+
+}
+
+# }}}
+
+# {{{ MatchPattern
+
+=head2 MatchPattern STRING
+
+Tests the incoming string against the Pattern of this custom field object
+and returns a boolean; returns true if the Pattern is empty.
+
+=cut
+
+sub MatchPattern {
+ my $self = shift;
+ my $regex = $self->Pattern;
+
+ return 1 if !length($regex);
+ return ($_[0] =~ $regex);
+}
+
+
+# }}}
+
+# {{{ FriendlyPattern
+
+=head2 FriendlyPattern
+
+Prettify the pattern of this custom field, by taking the text in C<(?#text)>
+and localizing it.
+
+=cut
+
+sub FriendlyPattern {
+ my $self = shift;
+ my $regex = $self->Pattern;
+
+ return '' if !length($regex);
+ if ($regex =~ /\(\?#([^)]*)\)/) {
+ return '[' . $self->loc($1) . ']';
+ }
+ else {
+ return $regex;
+ }
+}
+
+
+# }}}
+
+# {{{ DeleteValueForObject
+
+=head2 DeleteValueForObject HASH
+
+Deletes a custom field value for a ticket. Takes a param hash of Object and Content
+
+Returns a tuple of (STATUS, MESSAGE). If the call succeeded, the STATUS is true. otherwise it's false
+
+=cut
+
+sub DeleteValueForObject {
+ my $self = shift;
+ my %args = ( Object => undef,
+ Content => undef,
+ Id => undef,
+ @_ );
+
+
+ unless ($self->CurrentUserHasRight('ModifyCustomField')) {
+ return (0, $self->loc('Permission Denied'));
+ }
+
+ my $oldval = RT::ObjectCustomFieldValue->new($self->CurrentUser);
+
+ if (my $id = $args{'Id'}) {
+ $oldval->Load($id);
+ }
+ unless ($oldval->id) {
+ $oldval->LoadByObjectContentAndCustomField(
+ Object => $args{'Object'},
+ Content => $args{'Content'},
+ CustomField => $self->Id,
+ );
+ }
+
+
+ # check to make sure we found it
+ unless ($oldval->Id) {
+ return(0, $self->loc("Custom field value [_1] could not be found for custom field [_2]", $args{'Content'}, $self->Name));
+ }
+
+ # for single-value fields, we need to validate that empty string is a valid value for it
+ if ( $self->SingleValue and not $self->MatchPattern( '' ) ) {
+ return ( 0, $self->loc('Input must match [_1]', $self->FriendlyPattern) );
+ }
+
+ # delete it
+
+ my $ret = $oldval->Delete();
+ unless ($ret) {
+ return(0, $self->loc("Custom field value could not be found"));
+ }
+ return($oldval->Id, $self->loc("Custom field value deleted"));
+}
+
+
+=head2 ValuesForObject OBJECT
+
+Return an RT::ObjectCustomFieldValues object containing all of this custom field's values for OBJECT
+
+=cut
+
+sub ValuesForObject {
+ my $self = shift;
+ my $object = shift;
+
+ my $values = new RT::ObjectCustomFieldValues($self->CurrentUser);
+ unless ($self->CurrentUserHasRight('SeeCustomField')) {
+ # Return an empty object if they have no rights to see
+ return ($values);
+ }
+
+
+ $values->LimitToCustomField($self->Id);
+ $values->LimitToEnabled();
+ $values->LimitToObject($object);
+
+ return ($values);
+}
+
+
+=head2 _ForObjectType PATH FRIENDLYNAME
+
+Tell RT that a certain object accepts custom fields
+
+Examples:
+
+ 'RT::Queue-RT::Ticket' => "Tickets", # loc
+ 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", # loc
+ 'RT::User' => "Users", # loc
+ 'RT::Group' => "Groups", # loc
+
+This is a class method.
+
+=cut
+
+sub _ForObjectType {
+ my $self = shift;
+ my $path = shift;
+ my $friendly_name = shift;
+
+ $FRIENDLY_OBJECT_TYPES{$path} = $friendly_name;
+
+}
+
+
+=head2 IncludeContentForValue [VALUE] (and SetIncludeContentForValue)
+
+Gets or sets the C<IncludeContentForValue> for this custom field. RT
+uses this field to automatically include content into the user's browser
+as they display records with custom fields in RT.
+
+=cut
+
+sub SetIncludeContentForValue {
+ shift->IncludeContentForValue(@_);
+}
+sub IncludeContentForValue{
+ my $self = shift;
+ $self->_URLTemplate('IncludeContentForValue', @_);
+}
+
+
+
+=head2 LinkValueTo [VALUE] (and SetLinkValueTo)
+
+Gets or sets the C<LinkValueTo> for this custom field. RT
+uses this field to make custom field values into hyperlinks in the user's
+browser as they display records with custom fields in RT.
+
+=cut
+
+
+sub SetLinkValueTo {
+ shift->LinkValueTo(@_);
+}
+
+sub LinkValueTo {
+ my $self = shift;
+ $self->_URLTemplate('LinkValueTo', @_);
+
+}
+
+
+=head2 _URLTemplate NAME [VALUE]
+
+With one argument, returns the _URLTemplate named C<NAME>, but only if
+the current user has the right to see this custom field.
+
+With two arguments, attemptes to set the relevant template value.
+
+=cut
+
+
+
+sub _URLTemplate {
+ my $self = shift;
+ my $template_name = shift;
+ if (@_) {
+
+ my $value = shift;
+ unless ( $self->CurrentUserHasRight('AdminCustomField') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $self->SetAttribute( Name => $template_name, Content => $value );
+ return ( 1, $self->loc('Updated') );
+ } else {
+ unless ( $self->id && $self->CurrentUserHasRight('SeeCustomField') ) {
+ return (undef);
+ }
+
+ my @attr = $self->Attributes->Named($template_name);
+ my $attr = shift @attr;
+
+ if ($attr) { return $attr->Content }
+
+ }
+}
+1;
diff --git a/rt/lib/RT/CustomFields.pm b/rt/lib/RT/CustomFields.pm
new file mode 100644
index 0000000..fcab5a0
--- /dev/null
+++ b/rt/lib/RT/CustomFields.pm
@@ -0,0 +1,150 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::CustomFields -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::CustomFields
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::CustomFields;
+
+use RT::SearchBuilder;
+use RT::CustomField;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'CustomFields';
+ $self->{'primary_key'} = 'id';
+
+
+
+ # By default, order by SortOrder
+ $self->OrderByCols(
+ { ALIAS => 'main',
+ FIELD => 'SortOrder',
+ ORDER => 'ASC' },
+ { ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'ASC' },
+ );
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::CustomField item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::CustomField->new($self->CurrentUser));
+}
+
+ eval "require RT::CustomFields_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFields_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomFields_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFields_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::CustomFields_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/CustomFields_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::CustomFields_Overlay, RT::CustomFields_Vendor, RT::CustomFields_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/CustomFields_Overlay.pm b/rt/lib/RT/CustomFields_Overlay.pm
new file mode 100644
index 0000000..16b86ba
--- /dev/null
+++ b/rt/lib/RT/CustomFields_Overlay.pm
@@ -0,0 +1,264 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::CustomFields - a collection of RT CustomField objects
+
+=head1 SYNOPSIS
+
+ use RT::CustomFields;
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::CustomFields);
+
+=end testing
+
+=cut
+
+
+package RT::CustomFields;
+
+use strict;
+no warnings qw(redefine);
+use DBIx::SearchBuilder::Unique;
+
+
+sub _OCFAlias {
+ my $self = shift;
+ unless ($self->{_sql_ocfalias}) {
+
+ $self->{'_sql_ocfalias'} = $self->NewAlias('ObjectCustomFields');
+ $self->Join( ALIAS1 => 'main',
+ FIELD1 => 'id',
+ ALIAS2 => $self->_OCFAlias,
+ FIELD2 => 'CustomField' );
+ }
+ return($self->{_sql_ocfalias});
+}
+
+
+# {{{ sub LimitToGlobalOrQueue
+
+=head2 LimitToGlobalOrQueue QUEUEID
+
+Limits the set of custom fields found to global custom fields or those tied to the queue with ID QUEUEID
+
+=cut
+
+sub LimitToGlobalOrQueue {
+ my $self = shift;
+ my $queue = shift;
+ $self->LimitToGlobalOrObjectId( $queue );
+ $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+}
+
+# }}}
+
+# {{{ sub LimitToQueue
+
+=head2 LimitToQueue QUEUEID
+
+Takes a queue id (numerical) as its only argument. Makes sure that
+Scopes it pulls out apply to this queue (or another that you've selected with
+another call to this method
+
+=cut
+
+sub LimitToQueue {
+ my $self = shift;
+ my $queue = shift;
+
+ $self->Limit (ALIAS => $self->_OCFAlias,
+ ENTRYAGGREGATOR => 'OR',
+ FIELD => 'ObjectId',
+ VALUE => "$queue")
+ if defined $queue;
+ $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+}
+# }}}
+
+# {{{ sub LimitToGlobal
+
+=head2 LimitToGlobal
+
+Makes sure that
+Scopes it pulls out apply to all queues (or another that you've selected with
+another call to this method or LimitToQueue
+
+=cut
+
+
+sub LimitToGlobal {
+ my $self = shift;
+
+ $self->Limit (ALIAS => $self->_OCFAlias,
+ ENTRYAGGREGATOR => 'OR',
+ FIELD => 'ObjectId',
+ VALUE => 0);
+ $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+}
+# }}}
+
+
+# {{{ sub _DoSearch
+
+=head2 _DoSearch
+
+A subclass of DBIx::SearchBuilder::_DoSearch that makes sure that
+ _Disabled rows never get seen unless we're explicitly trying to see
+them.
+
+=cut
+
+sub _DoSearch {
+ my $self = shift;
+
+ #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
+ unless($self->{'find_disabled_rows'}) {
+ $self->LimitToEnabled();
+ }
+
+ return($self->SUPER::_DoSearch(@_));
+
+}
+
+# }}}
+
+# {{{ sub Next
+
+=head2 Next
+
+Returns the next custom field that this user can see.
+
+=cut
+
+sub Next {
+ my $self = shift;
+
+
+ my $CF = $self->SUPER::Next();
+ if ((defined($CF)) and (ref($CF))) {
+
+ if ($CF->CurrentUserHasRight('SeeCustomField')) {
+ return($CF);
+ }
+
+ #If the user doesn't have the right to show this queue
+ else {
+ return($self->Next());
+ }
+ }
+ #if there never was any queue
+ else {
+ return(undef);
+ }
+
+}
+# }}}
+
+sub LimitToLookupType {
+ my $self = shift;
+ my $lookup = shift;
+
+ $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
+}
+
+sub LimitToChildType {
+ my $self = shift;
+ my $lookup = shift;
+
+ $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
+ $self->Limit( FIELD => 'LookupType', ENDSWITH => "$lookup" );
+}
+
+sub LimitToParentType {
+ my $self = shift;
+ my $lookup = shift;
+
+ $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
+ $self->Limit( FIELD => 'LookupType', STARTSWITH => "$lookup" );
+}
+
+sub LimitToGlobalOrObjectId {
+ my $self = shift;
+ my $global_only = 1;
+
+
+ foreach my $id (@_) {
+ $self->Limit( ALIAS => $self->_OCFAlias,
+ FIELD => 'ObjectId',
+ OPERATOR => '=',
+ VALUE => $id || 0,
+ ENTRYAGGREGATOR => 'OR' );
+ $global_only = 0 if $id;
+ }
+
+ $self->Limit( ALIAS => $self->_OCFAlias,
+ FIELD => 'ObjectId',
+ OPERATOR => '=',
+ VALUE => 0,
+ ENTRYAGGREGATOR => 'OR' ) unless $global_only;
+
+ $self->OrderByCols(
+ { ALIAS => $self->_OCFAlias, FIELD => 'ObjectId' },
+ { ALIAS => $self->_OCFAlias, FIELD => 'SortOrder' },
+ );
+
+ # This doesn't work on postgres.
+ #$self->OrderBy( ALIAS => $class_cfs , FIELD => "SortOrder", ORDER => 'ASC');
+
+}
+
+1;
+
diff --git a/rt/lib/RT/Date.pm b/rt/lib/RT/Date.pm
new file mode 100644
index 0000000..7b441a6
--- /dev/null
+++ b/rt/lib/RT/Date.pm
@@ -0,0 +1,646 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Date - a simple Object Oriented date.
+
+=head1 SYNOPSIS
+
+ use RT::Date
+
+=head1 DESCRIPTION
+
+RT Date is a simple Date Object designed to be speedy and easy for RT to use
+
+The fact that it assumes that a time of 0 means "never" is probably a bug.
+
+=begin testing
+
+ok (require RT::Date);
+
+=end testing
+
+=head1 METHODS
+
+=cut
+
+
+package RT::Date;
+
+use Time::Local;
+
+use RT::Base;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw/RT::Base/;
+
+use vars qw($MINUTE $HOUR $DAY $WEEK $MONTH $YEAR);
+
+$MINUTE = 60;
+$HOUR = 60 * $MINUTE;
+$DAY = 24 * $HOUR;
+$WEEK = 7 * $DAY;
+$MONTH = 4 * $WEEK;
+$YEAR = 365 * $DAY;
+
+# {{{ sub new
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->CurrentUser(@_);
+ $self->Unix(0);
+ return $self;
+}
+
+# }}}
+
+# {{{ sub Set
+
+=head2 sub Set
+
+takes a param hash with the fields 'Format' and 'Value'
+
+if $args->{'Format'} is 'unix', takes the number of seconds since the epoch
+
+If $args->{'Format'} is ISO, tries to parse an ISO date.
+
+If $args->{'Format'} is 'unknown', require Time::ParseDate and make it figure
+things out. This is a heavyweight operation that should never be called from
+within RT's core. But it's really useful for something like the textbox date
+entry where we let the user do whatever they want.
+
+If $args->{'Value'} is 0, assumes you mean never.
+
+=begin testing
+
+use_ok(RT::Date);
+my $date = RT::Date->new($RT::SystemUser);
+$date->Set(Format => 'unix', Value => '0');
+ok ($date->ISO eq '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT");
+
+=end testing
+
+=cut
+
+sub Set {
+ my $self = shift;
+ my %args = ( Format => 'unix',
+ Value => time,
+ @_ );
+ if ( !$args{'Value'}
+ || ( ( $args{'Value'} =~ /^\d*$/ ) and ( $args{'Value'} == 0 ) ) ) {
+ $self->Unix(-1);
+ return ( $self->Unix() );
+ }
+
+ if ( $args{'Format'} =~ /^unix$/i ) {
+ $self->Unix( $args{'Value'} );
+ }
+
+ elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
+ $args{'Value'} =~ s!/!-!g;
+
+ if (( $args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
+ || ( $args{'Value'} =~
+ /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
+ || ( $args{'Value'} =~
+ /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
+ || ($args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
+ ) {
+
+ my $year = $1;
+ my $mon = $2;
+ my $mday = $3;
+ my $hours = $4;
+ my $min = $5;
+ my $sec = $6;
+
+ #timegm expects month as 0->11
+ $mon--;
+
+ #now that we've parsed it, deal with the case where everything
+ #was 0
+ if ( $mon == -1 ) {
+ $self->Unix(-1);
+ }
+ else {
+
+ #Dateamnip strings aren't in GMT.
+ if ( $args{'Format'} =~ /^datemanip$/i ) {
+ $self->Unix(
+ timelocal( $sec, $min, $hours, $mday, $mon, $year ) );
+ }
+
+ #ISO and SQL dates are in GMT
+ else {
+ $self->Unix(
+ timegm( $sec, $min, $hours, $mday, $mon, $year ) );
+ }
+
+ $self->Unix(-1) unless $self->Unix;
+ }
+ }
+ else {
+ use Carp;
+ Carp::cluck;
+ $RT::Logger->debug(
+ "Couldn't parse date $args{'Value'} as a $args{'Format'}");
+
+ }
+ }
+ elsif ( $args{'Format'} =~ /^unknown$/i ) {
+ require Time::ParseDate;
+
+ #Convert it to an ISO format string
+
+ my $date = Time::ParseDate::parsedate($args{'Value'},
+ UK => $RT::DateDayBeforeMonth,
+ PREFER_PAST => $RT::AmbiguousDayInPast,
+ PREFER_FUTURE => !($RT::AmbiguousDayInPast));
+
+ #This date has now been set to a date in the _local_ timezone.
+ #since ISO dates are known to be in GMT (for RT's purposes);
+
+ $RT::Logger->debug( "RT::Date used date::parse to make "
+ . $args{'Value'}
+ . " $date\n" );
+
+ return ( $self->Set( Format => 'unix', Value => "$date" ) );
+ }
+ else {
+ die "Unknown Date format: " . $args{'Format'} . "\n";
+ }
+
+ return ( $self->Unix() );
+}
+
+# }}}
+
+# {{{ sub SetToMidnight
+
+=head2 SetToMidnight [Timezone => 'utc']
+
+Sets the date to midnight (at the beginning of the day).
+Returns the unixtime at midnight.
+
+Arguments:
+
+=over 4
+
+=item Timezone - Timezone context C<server> or C<UTC>
+
+=cut
+
+sub SetToMidnight {
+ my $self = shift;
+ my %args = ( Timezone => 'UTC', @_ );
+ if ( lc $args{'Timezone'} eq 'server' ) {
+ $self->Unix( Time::Local::timelocal( 0,0,0,(localtime $self->Unix)[3..7] ) );
+ } else {
+ $self->Unix( Time::Local::timegm( 0,0,0,(gmtime $self->Unix)[3..7] ) );
+ }
+ return ($self->Unix);
+}
+
+
+# }}}
+
+# {{{ sub SetToNow
+sub SetToNow {
+ my $self = shift;
+ return($self->Set(Format => 'unix', Value => time))
+}
+# }}}
+
+# {{{ sub Diff
+
+=head2 Diff
+
+Takes either an RT::Date object or the date in unixtime format as a string
+
+Returns the differnce between $self and that time as a number of seconds
+
+=cut
+
+sub Diff {
+ my $self = shift;
+ my $other = shift;
+
+ if (ref($other) eq 'RT::Date') {
+ $other=$other->Unix;
+ }
+ return ($self->Unix - $other);
+}
+# }}}
+
+# {{{ sub DiffAsString
+
+=head2 sub DiffAsString
+
+Takes either an RT::Date object or the date in unixtime format as a string
+
+Returns the differnce between $self and that time as a number of seconds as
+as string fit for human consumption
+
+=cut
+
+sub DiffAsString {
+ my $self = shift;
+ my $other = shift;
+
+
+ if ($other < 1) {
+ return ("");
+ }
+ if ($self->Unix < 1) {
+ return("");
+ }
+ my $diff = $self->Diff($other);
+
+ return ($self->DurationAsString($diff));
+}
+# }}}
+
+# {{{ sub DurationAsString
+
+
+=head2 DurationAsString
+
+Takes a number of seconds. returns a string describing that duration
+
+=cut
+
+sub DurationAsString {
+
+ my $self = shift;
+ my $duration = shift;
+
+ my ( $negative, $s );
+
+ $negative = 1 if ( $duration < 0 );
+
+ $duration = abs($duration);
+
+ my $time_unit;
+ if ( $duration < $MINUTE ) {
+ $s = $duration;
+ $time_unit = $self->loc("sec");
+ }
+ elsif ( $duration < ( 2 * $HOUR ) ) {
+ $s = int( $duration / $MINUTE );
+ $time_unit = $self->loc("min");
+ }
+ elsif ( $duration < ( 2 * $DAY ) ) {
+ $s = int( $duration / $HOUR );
+ $time_unit = $self->loc("hours");
+ }
+ elsif ( $duration < ( 2 * $WEEK ) ) {
+ $s = int( $duration / $DAY );
+ $time_unit = $self->loc("days");
+ }
+ elsif ( $duration < ( 2 * $MONTH ) ) {
+ $s = int( $duration / $WEEK );
+ $time_unit = $self->loc("weeks");
+ }
+ elsif ( $duration < $YEAR ) {
+ $s = int( $duration / $MONTH );
+ $time_unit = $self->loc("months");
+ }
+ else {
+ $s = int( $duration / $YEAR );
+ $time_unit = $self->loc("years");
+ }
+
+ if ($negative) {
+ return $self->loc( "[_1] [_2] ago", $s, $time_unit );
+ }
+ else {
+ return $self->loc( "[_1] [_2]", $s, $time_unit );
+ }
+}
+
+# }}}
+
+# {{{ sub AgeAsString
+
+=head2 sub AgeAsString
+
+Takes nothing
+
+Returns a string that's the differnce between the time in the object and now
+
+=cut
+
+sub AgeAsString {
+ my $self = shift;
+ return ($self->DiffAsString(time));
+ }
+# }}}
+
+# {{{ sub AsString
+
+=head2 sub AsString
+
+Returns the object\'s time as a string with the current timezone.
+
+=cut
+
+sub AsString {
+ my $self = shift;
+ return ($self->loc("Not set")) if ($self->Unix <= 0);
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
+
+ return $self->loc("[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]", $self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900));
+}
+# }}}
+
+# {{{ GetWeekday
+
+=head2 GetWeekday DAY
+
+Takes an integer day of week and returns a localized string for that day of week
+
+=cut
+
+sub GetWeekday {
+ my $self = shift;
+ my $dow = shift;
+
+ return $self->loc('Mon.') if ($dow == 1);
+ return $self->loc('Tue.') if ($dow == 2);
+ return $self->loc('Wed.') if ($dow == 3);
+ return $self->loc('Thu.') if ($dow == 4);
+ return $self->loc('Fri.') if ($dow == 5);
+ return $self->loc('Sat.') if ($dow == 6);
+ return $self->loc('Sun.') if ($dow == 0);
+}
+
+# }}}
+
+# {{{ GetMonth
+
+=head2 GetMonth DAY
+
+Takes an integer month and returns a localized string for that month
+
+=cut
+
+sub GetMonth {
+ my $self = shift;
+ my $mon = shift;
+
+ # We do this rather than an array so that we don't call localize 12x what we need to
+ return $self->loc('Jan.') if ($mon == 0);
+ return $self->loc('Feb.') if ($mon == 1);
+ return $self->loc('Mar.') if ($mon == 2);
+ return $self->loc('Apr.') if ($mon == 3);
+ return $self->loc('May.') if ($mon == 4);
+ return $self->loc('Jun.') if ($mon == 5);
+ return $self->loc('Jul.') if ($mon == 6);
+ return $self->loc('Aug.') if ($mon == 7);
+ return $self->loc('Sep.') if ($mon == 8);
+ return $self->loc('Oct.') if ($mon == 9);
+ return $self->loc('Nov.') if ($mon == 10);
+ return $self->loc('Dec.') if ($mon == 11);
+}
+
+# }}}
+
+# {{{ sub AddSeconds
+
+=head2 sub AddSeconds
+
+Takes a number of seconds as a string
+
+Returns the new time
+
+=cut
+
+sub AddSeconds {
+ my $self = shift;
+ my $delta = shift;
+
+ $self->Set(Format => 'unix', Value => ($self->Unix + $delta));
+
+ return ($self->Unix);
+
+
+}
+
+# }}}
+
+# {{{ sub AddDays
+
+=head2 AddDays $DAYS
+
+Adds 24 hours * $DAYS to the current time
+
+=cut
+
+sub AddDays {
+ my $self = shift;
+ my $days = shift;
+ $self->AddSeconds($days * $DAY);
+
+}
+
+# }}}
+
+# {{{ sub AddDay
+
+=head2 AddDay
+
+Adds 24 hours to the current time
+
+=cut
+
+sub AddDay {
+ my $self = shift;
+ $self->AddSeconds($DAY);
+
+}
+
+# }}}
+
+# {{{ sub Unix
+
+=head2 sub Unix [unixtime]
+
+Optionally takes a date in unix seconds since the epoch format.
+Returns the number of seconds since the epoch
+
+=cut
+
+sub Unix {
+ my $self = shift;
+
+ $self->{'time'} = shift if (@_);
+
+ return ($self->{'time'});
+}
+# }}}
+
+# {{{ sub ISO
+
+=head2 ISO
+
+Takes nothing
+
+Returns the object's date in ISO format
+
+=cut
+
+sub ISO {
+ my $self=shift;
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
+
+ return ('1970-01-01 00:00:00') if ($self->Unix == -1);
+
+ # 0 1 2 3 4 5 6 7 8
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
+ #make the year YYYY
+ $year+=1900;
+
+ #the month needs incrementing, as gmtime returns 0-11
+ $mon++;
+
+ $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
+
+ return ($date);
+}
+
+# }}}
+
+# {{{ sub Date
+
+=head2 Date
+
+Takes nothing
+
+Returns the object's date in yyyy-mm-dd format; this is the same as
+the ISO format without the time
+
+=cut
+
+sub Date {
+ my $self = shift;
+ my ($date, $time) = split ' ', $self->ISO;
+ return $date;
+}
+
+# }}}}
+
+# {{{ sub Time
+
+=head2 Time
+
+Takes nothing
+
+Returns the object's time in hh:mm:ss format; this is the same as
+the ISO format without the date
+
+=cut
+
+sub Time {
+ my $self = shift;
+ my ($date, $time) = split ' ', $self->ISO;
+ return $time;
+}
+
+# }}}}
+
+# {{{ sub W3CDTF
+
+=head2 W3CDTF
+
+Takes nothing
+
+Returns the object's date in W3C DTF format
+
+=cut
+
+sub W3CDTF {
+ my $self = shift;
+ my $date = $self->ISO . 'Z';
+ $date =~ s/ /T/;
+ return $date;
+};
+
+# }}}
+
+# {{{ sub LocalTimezone
+
+=head2 LocalTimezone
+
+ Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
+pull from a 'Timezone' attribute of the CurrentUser
+
+=cut
+
+sub LocalTimezone {
+ my $self = shift;
+
+ return $self->CurrentUser->Timezone
+ if $self->CurrentUser and $self->CurrentUser->can('Timezone');
+
+ return ($RT::Timezone);
+}
+
+# }}}
+
+eval "require RT::Date_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
+eval "require RT::Date_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Local.pm});
+
+1;
diff --git a/rt/lib/RT/EmailParser.pm b/rt/lib/RT/EmailParser.pm
new file mode 100644
index 0000000..39c6105
--- /dev/null
+++ b/rt/lib/RT/EmailParser.pm
@@ -0,0 +1,636 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::EmailParser;
+
+
+use base qw/RT::Base/;
+
+use strict;
+use Mail::Address;
+use MIME::Entity;
+use MIME::Head;
+use MIME::Parser;
+use File::Temp qw/tempdir/;
+
+=head1 NAME
+
+ RT::EmailParser - helper functions for parsing parts from incoming
+ email messages
+
+=head1 SYNOPSIS
+
+
+=head1 DESCRIPTION
+
+
+=begin testing
+
+ok(require RT::EmailParser);
+
+=end testing
+
+
+=head1 METHODS
+
+=head2 new
+
+Returns a new RT::EmailParser object
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ return $self;
+}
+
+
+# {{{ sub SmartParseMIMEEntityFromScalar
+
+=head2 SmartParseMIMEEntityFromScalar { Message => SCALAR_REF, Decode => BOOL }
+
+Parse a message stored in a scalar from scalar_ref
+
+=cut
+
+sub SmartParseMIMEEntityFromScalar {
+ my $self = shift;
+ my %args = ( Message => undef, Decode => 1, @_ );
+
+ my ( $fh, $temp_file );
+ eval {
+
+ for ( 1 .. 10 ) {
+
+ # on NFS and NTFS, it is possible that tempfile() conflicts
+ # with other processes, causing a race condition. we try to
+ # accommodate this by pausing and retrying.
+ last
+ if ( $fh, $temp_file ) =
+ eval { File::Temp::tempfile( undef, UNLINK => 0 ) };
+ sleep 1;
+ }
+ if ($fh) {
+
+ #thank you, windows
+ binmode $fh;
+ $fh->autoflush(1);
+ print $fh $args{'Message'};
+ close($fh);
+ if ( -f $temp_file ) {
+
+ # We have to trust the temp file's name -- untaint it
+ $temp_file =~ /(.*)/;
+ $self->ParseMIMEEntityFromFile( $1, $args{'Decode'} );
+ unlink($1);
+ }
+ }
+ };
+
+ #If for some reason we weren't able to parse the message using a temp file
+ # try it with a scalar
+ if ( $@ || !$self->Entity ) {
+ $self->ParseMIMEEntityFromScalar( $args{'Message'}, $args{'Decode'} );
+ }
+
+}
+
+# }}}
+
+# {{{ sub ParseMIMEEntityFromSTDIN
+
+=head2 ParseMIMEEntityFromSTDIN
+
+Parse a message from standard input
+
+=cut
+
+sub ParseMIMEEntityFromSTDIN {
+ my $self = shift;
+ my $postprocess = (@_ ? shift : 1);
+ return $self->ParseMIMEEntityFromFileHandle(\*STDIN, $postprocess);
+}
+
+# }}}
+
+# {{{ ParseMIMEEntityFromScalar
+
+=head2 ParseMIMEEntityFromScalar $message
+
+Takes either a scalar or a reference to a scalr which contains a stringified MIME message.
+Parses it.
+
+Returns true if it wins.
+Returns false if it loses.
+
+=cut
+
+sub ParseMIMEEntityFromScalar {
+ my $self = shift;
+ my $message = shift;
+ my $postprocess = (@_ ? shift : 1);
+ $self->_ParseMIMEEntity($message,'parse_data', $postprocess);
+}
+
+# }}}
+
+# {{{ ParseMIMEEntityFromFilehandle *FH
+
+=head2 ParseMIMEEntityFromFilehandle *FH
+
+Parses a mime entity from a filehandle passed in as an argument
+
+=cut
+
+sub ParseMIMEEntityFromFileHandle {
+ my $self = shift;
+ my $filehandle = shift;
+ my $postprocess = (@_ ? shift : 1);
+ $self->_ParseMIMEEntity($filehandle,'parse', $postprocess);
+}
+
+# }}}
+
+# {{{ ParseMIMEEntityFromFile
+
+=head2 ParseMIMEEntityFromFile
+
+Parses a mime entity from a filename passed in as an argument
+
+=cut
+
+sub ParseMIMEEntityFromFile {
+ my $self = shift;
+ my $file = shift;
+ my $postprocess = (@_ ? shift : 1);
+ $self->_ParseMIMEEntity($file,'parse_open',$postprocess);
+}
+
+# }}}
+
+# {{{ _ParseMIMEEntity
+sub _ParseMIMEEntity {
+ my $self = shift;
+ my $message = shift;
+ my $method = shift;
+ my $postprocess = shift;
+ # Create a new parser object:
+
+ my $parser = MIME::Parser->new();
+ $self->_SetupMIMEParser($parser);
+
+
+ # TODO: XXX 3.0 we really need to wrap this in an eval { }
+ unless ( $self->{'entity'} = $parser->$method($message) ) {
+ $RT::Logger->crit("Couldn't parse MIME stream and extract the submessages");
+ # Try again, this time without extracting nested messages
+ $parser->extract_nested_messages(0);
+ unless ( $self->{'entity'} = $parser->$method($message) ) {
+ $RT::Logger->crit("couldn't parse MIME stream");
+ return ( undef);
+ }
+ }
+ if ($postprocess) {
+ $self->_PostProcessNewEntity() ;
+ }
+
+}
+
+# }}}
+
+# {{{ _PostProcessNewEntity
+
+=head2 _PostProcessNewEntity
+
+cleans up and postprocesses a newly parsed MIME Entity
+
+=cut
+
+sub _PostProcessNewEntity {
+ my $self = shift;
+
+ #Now we've got a parsed mime object.
+
+ # Unfold headers that are have embedded newlines
+ # Better do this before conversion or it will break
+ # with multiline encoded Subject (RFC2047) (fsck.com #5594)
+
+ $self->Head->unfold;
+
+
+ # try to convert text parts into utf-8 charset
+ RT::I18N::SetMIMEEntityToEncoding($self->{'entity'}, 'utf-8');
+
+
+
+
+}
+
+# }}}
+
+# {{{ sub ParseTicketId
+
+sub ParseTicketId {
+ my $self = shift;
+ $RT::Logger->warnings("RT::EmailParser->ParseTicketId deprecated. You should be using RT::Interface::Email at (". join(":",caller).")");
+
+ require RT::Interface::Email;
+ RT::Interface::Email::ParseTicketId(@_);
+}
+
+# }}}
+
+
+
+# {{{ ParseCcAddressesFromHead
+
+=head2 ParseCcAddressesFromHead HASHREF
+
+Takes a hashref object containing QueueObj, Head and CurrentUser objects.
+Returns a list of all email addresses in the To and Cc
+headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
+email address and anything that the $RTAddressRegexp matches.
+
+=cut
+
+sub ParseCcAddressesFromHead {
+
+ my $self = shift;
+
+ my %args = (
+ QueueObj => undef,
+ CurrentUser => undef,
+ @_
+ );
+
+ my (@Addresses);
+
+ my @ToObjs = Mail::Address->parse( $self->Head->get('To') );
+ my @CcObjs = Mail::Address->parse( $self->Head->get('Cc') );
+
+ foreach my $AddrObj ( @ToObjs, @CcObjs ) {
+ my $Address = $AddrObj->address;
+ my $user = RT::User->new($RT::SystemUser);
+ $Address = $user->CanonicalizeEmailAddress($Address);
+ next if ( lc $args{'CurrentUser'}->EmailAddress eq lc $Address );
+ next if ( lc $args{'QueueObj'}->CorrespondAddress eq lc $Address );
+ next if ( lc $args{'QueueObj'}->CommentAddress eq lc $Address );
+ next if ( $self->IsRTAddress($Address) );
+
+ push ( @Addresses, $Address );
+ }
+ return (@Addresses);
+}
+
+# }}}
+
+# {{{ ParseSenderAdddressFromHead
+
+=head2 ParseSenderAddressFromHead
+
+Takes a MIME::Header object. Returns a tuple: (user@host, friendly name)
+of the From (evaluated in order of Reply-To:, From:, Sender)
+
+=cut
+
+sub ParseSenderAddressFromHead {
+ my $self = shift;
+
+ #Figure out who's sending this message.
+ my $From = $self->Head->get('Reply-To')
+ || $self->Head->get('From')
+ || $self->Head->get('Sender');
+ return ( $self->ParseAddressFromHeader($From) );
+}
+
+# }}}
+
+# {{{ ParseErrorsToAdddressFromHead
+
+=head2 ParseErrorsToAddressFromHead
+
+Takes a MIME::Header object. Return a single value : user@host
+of the From (evaluated in order of Errors-To:,Reply-To:, From:, Sender)
+
+=cut
+
+sub ParseErrorsToAddressFromHead {
+ my $self = shift;
+
+ #Figure out who's sending this message.
+
+ foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
+
+ # If there's a header of that name
+ my $headerobj = $self->Head->get($header);
+ if ($headerobj) {
+ my ( $addr, $name ) = $self->ParseAddressFromHeader($headerobj);
+
+ # If it's got actual useful content...
+ return ($addr) if ($addr);
+ }
+ }
+}
+
+# }}}
+
+# {{{ ParseAddressFromHeader
+
+=head2 ParseAddressFromHeader ADDRESS
+
+Takes an address from $self->Head->get('Line') and returns a tuple: user@host, friendly name
+
+=cut
+
+sub ParseAddressFromHeader {
+ my $self = shift;
+ my $Addr = shift;
+
+ # Perl 5.8.0 breaks when doing regex matches on utf8
+ Encode::_utf8_off($Addr) if $] == 5.008;
+ my @Addresses = Mail::Address->parse($Addr);
+
+ my $AddrObj = $Addresses[0];
+
+ unless ( ref($AddrObj) ) {
+ return ( undef, undef );
+ }
+
+ my $Name = ( $AddrObj->phrase || $AddrObj->comment || $AddrObj->address );
+
+ #Lets take the from and load a user object.
+ my $Address = $AddrObj->address;
+
+ return ( $Address, $Name );
+}
+
+# }}}
+
+# {{{ IsRTAddress
+
+=head2 IsRTaddress ADDRESS
+
+Takes a single parameter, an email address.
+Returns true if that address matches the $RTAddressRegexp.
+Returns false, otherwise.
+
+=begin testing
+
+is(RT::EmailParser::IsRTAddress("","rt\@example.com"),1, "Regexp matched rt address" );
+is(RT::EmailParser::IsRTAddress("","frt\@example.com"),undef, "Regexp didn't match non-rt address" );
+
+=end testing
+
+=cut
+
+sub IsRTAddress {
+ my $self = shift;
+ my $address = shift;
+
+ # Example: the following rule would tell RT not to Cc
+ # "tickets@noc.example.com"
+ if ( defined($RT::RTAddressRegexp) &&
+ $address =~ /$RT::RTAddressRegexp/i ) {
+ return(1);
+ } else {
+ return (undef);
+ }
+}
+
+# }}}
+
+
+# {{{ CullRTAddresses
+
+=head2 CullRTAddresses ARRAY
+
+Takes a single argument, an array of email addresses.
+Returns the same array with any IsRTAddress()es weeded out.
+
+=begin testing
+
+@before = ("rt\@example.com", "frt\@example.com");
+@after = ("frt\@example.com");
+ok(eq_array(RT::EmailParser::CullRTAddresses("",@before),@after), "CullRTAddresses only culls RT addresses");
+
+=end testing
+
+=cut
+
+sub CullRTAddresses {
+ my $self = shift;
+ my @addresses= (@_);
+ my @addrlist;
+
+ foreach my $addr( @addresses ) {
+ # We use the class instead of the instance
+ # because sloppy code calls this method
+ # without a $self
+ push (@addrlist, $addr) unless RT::EmailParser->IsRTAddress($addr);
+ }
+ return (@addrlist);
+}
+
+# }}}
+
+
+# {{{ LookupExternalUserInfo
+
+
+# LookupExternalUserInfo is a site-definable method for synchronizing
+# incoming users with an external data source.
+#
+# This routine takes a tuple of EmailAddress and FriendlyName
+# EmailAddress is the user's email address, ususally taken from
+# an email message's From: header.
+# FriendlyName is a freeform string, ususally taken from the "comment"
+# portion of an email message's From: header.
+#
+# If you define an AutoRejectRequest template, RT will use this
+# template for the rejection message.
+
+
+=head2 LookupExternalUserInfo
+
+ LookupExternalUserInfo is a site-definable method for synchronizing
+ incoming users with an external data source.
+
+ This routine takes a tuple of EmailAddress and FriendlyName
+ EmailAddress is the user's email address, ususally taken from
+ an email message's From: header.
+ FriendlyName is a freeform string, ususally taken from the "comment"
+ portion of an email message's From: header.
+
+ It returns (FoundInExternalDatabase, ParamHash);
+
+ FoundInExternalDatabase must be set to 1 before return if the user
+ was found in the external database.
+
+ ParamHash is a Perl parameter hash which can contain at least the
+ following fields. These fields are used to populate RT's users
+ database when the user is created.
+
+ EmailAddress is the email address that RT should use for this user.
+ Name is the 'Name' attribute RT should use for this user.
+ 'Name' is used for things like access control and user lookups.
+ RealName is what RT should display as the user's name when displaying
+ 'friendly' names
+
+=cut
+
+sub LookupExternalUserInfo {
+ my $self = shift;
+ my $EmailAddress = shift;
+ my $RealName = shift;
+
+ my $FoundInExternalDatabase = 1;
+ my %params;
+
+ #Name is the RT username you want to use for this user.
+ $params{'Name'} = $EmailAddress;
+ $params{'EmailAddress'} = $EmailAddress;
+ $params{'RealName'} = $RealName;
+
+ # See RT's contributed code for examples.
+ # http://www.fsck.com/pub/rt/contrib/
+ return ($FoundInExternalDatabase, %params);
+}
+
+# }}}
+
+# {{{ Accessor methods for parsed email messages
+
+=head2 Head
+
+Return the parsed head from this message
+
+=cut
+
+sub Head {
+ my $self = shift;
+ return $self->Entity->head;
+}
+
+=head2 Entity
+
+Return the parsed Entity from this message
+
+=cut
+
+sub Entity {
+ my $self = shift;
+ return $self->{'entity'};
+}
+
+# }}}
+
+# {{{ _SetupMIMEParser
+
+=head2 _SetupMIMEParser $parser
+
+A private instance method which sets up a mime parser to do its job
+
+=cut
+
+
+ ## TODO: Does it make sense storing to disk at all? After all, we
+ ## need to put each msg as an in-core scalar before saving it to
+ ## the database, don't we?
+
+ ## At the same time, we should make sure that we nuke attachments
+ ## Over max size and return them
+
+sub _SetupMIMEParser {
+ my $self = shift;
+ my $parser = shift;
+
+ # Set up output directory for files:
+
+ my $tmpdir = File::Temp::tempdir( TMPDIR => 1, CLEANUP => 1 );
+ push ( @{ $self->{'AttachmentDirs'} }, $tmpdir );
+ $parser->output_dir($tmpdir);
+ $parser->filer->ignore_filename(1);
+
+ #If someone includes a message, extract it
+ $parser->extract_nested_messages(1);
+
+ $parser->extract_uuencode(1); ### default is false
+
+ # Set up the prefix for files with auto-generated names:
+ $parser->output_prefix("part");
+
+ # do _not_ store each msg as in-core scalar;
+
+ $parser->output_to_core(0);
+
+ # From the MIME::Parser docs:
+ # "Normally, tmpfiles are created when needed during parsing, and destroyed automatically when they go out of scope"
+ # Turns out that the default is to recycle tempfiles
+ # Temp files should never be recycled, especially when running under perl taint checking
+
+ $parser->tmp_recycling(0) if $parser->can('tmp_recycling');
+
+}
+
+# }}}
+
+sub DESTROY {
+ my $self = shift;
+ File::Path::rmtree([@{$self->{'AttachmentDirs'}}],0,1);
+}
+
+
+
+eval "require RT::EmailParser_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/EmailParser_Vendor.pm});
+eval "require RT::EmailParser_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/EmailParser_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Extension/ActivityReports.pm b/rt/lib/RT/Extension/ActivityReports.pm
new file mode 100644
index 0000000..52d8ba6
--- /dev/null
+++ b/rt/lib/RT/Extension/ActivityReports.pm
@@ -0,0 +1,3 @@
+package RT::Extension::ActivityReports;
+
+our $VERSION = '0.2';
diff --git a/rt/lib/RT/Group.pm b/rt/lib/RT/Group.pm
new file mode 100755
index 0000000..3dc832f
--- /dev/null
+++ b/rt/lib/RT/Group.pm
@@ -0,0 +1,282 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Group
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Group;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Groups');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(255) 'Description'.
+ varchar(64) 'Domain'.
+ varchar(64) 'Type'.
+ int(11) 'Instance'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Description => '',
+ Domain => '',
+ Type => '',
+ Instance => '',
+
+ @_);
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ Domain => $args{'Domain'},
+ Type => $args{'Type'},
+ Instance => $args{'Instance'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Domain
+
+Returns the current value of Domain.
+(In the database, Domain is stored as varchar(64).)
+
+
+
+=head2 SetDomain VALUE
+
+
+Set Domain to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Domain will be stored as a varchar(64).)
+
+
+=cut
+
+
+=head2 Type
+
+Returns the current value of Type.
+(In the database, Type is stored as varchar(64).)
+
+
+
+=head2 SetType VALUE
+
+
+Set Type to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Type will be stored as a varchar(64).)
+
+
+=cut
+
+
+=head2 Instance
+
+Returns the current value of Instance.
+(In the database, Instance is stored as int(11).)
+
+
+
+=head2 SetInstance VALUE
+
+
+Set Instance to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Instance will be stored as a int(11).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Domain =>
+ {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ Type =>
+ {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ Instance =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+
+ }
+};
+
+
+ eval "require RT::Group_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Group_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Group_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Group_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Group_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Group_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Group_Overlay, RT::Group_Vendor, RT::Group_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/GroupMember.pm b/rt/lib/RT/GroupMember.pm
new file mode 100755
index 0000000..96664e0
--- /dev/null
+++ b/rt/lib/RT/GroupMember.pm
@@ -0,0 +1,213 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::GroupMember
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::GroupMember;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('GroupMembers');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'GroupId'.
+ int(11) 'MemberId'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ GroupId => '0',
+ MemberId => '0',
+
+ @_);
+ $self->SUPER::Create(
+ GroupId => $args{'GroupId'},
+ MemberId => $args{'MemberId'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 GroupId
+
+Returns the current value of GroupId.
+(In the database, GroupId is stored as int(11).)
+
+
+
+=head2 SetGroupId VALUE
+
+
+Set GroupId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, GroupId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 MemberId
+
+Returns the current value of MemberId.
+(In the database, MemberId is stored as int(11).)
+
+
+
+=head2 SetMemberId VALUE
+
+
+Set MemberId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, MemberId will be stored as a int(11).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ GroupId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ MemberId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::GroupMember_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/GroupMember_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::GroupMember_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/GroupMember_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::GroupMember_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/GroupMember_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::GroupMember_Overlay, RT::GroupMember_Vendor, RT::GroupMember_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/GroupMember_Overlay.pm b/rt/lib/RT/GroupMember_Overlay.pm
new file mode 100644
index 0000000..1cc0309
--- /dev/null
+++ b/rt/lib/RT/GroupMember_Overlay.pm
@@ -0,0 +1,390 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::GroupMember - a member of an RT Group
+
+=head1 SYNOPSIS
+
+RT::GroupMember should never be called directly. It should ONLY
+only be accessed through the helper functions in RT::Group;
+
+If you're operating on an RT::GroupMember object yourself, you B<ARE>
+doing something wrong.
+
+=head1 DESCRIPTION
+
+
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::GroupMember);
+
+=end testing
+
+
+=cut
+
+
+package RT::GroupMember;
+
+use strict;
+no warnings qw(redefine);
+use RT::CachedGroupMembers;
+
+# {{{ sub Create
+
+=head2 Create { Group => undef, Member => undef }
+
+Add a Principal to the group Group.
+if the Principal is a group, automatically inserts all
+members of the principal into the cached members table recursively down.
+
+Both Group and Member are expected to be RT::Principal objects
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Group => undef,
+ Member => undef,
+ InsideTransaction => undef,
+ @_
+ );
+
+ unless ($args{'Group'} &&
+ UNIVERSAL::isa($args{'Group'}, 'RT::Principal') &&
+ $args{'Group'}->Id ) {
+
+ $RT::Logger->warning("GroupMember::Create called with a bogus Group arg");
+ return (undef);
+ }
+
+ unless($args{'Group'}->IsGroup) {
+ $RT::Logger->warning("Someone tried to add a member to a user instead of a group");
+ return (undef);
+ }
+
+ unless ($args{'Member'} &&
+ UNIVERSAL::isa($args{'Member'}, 'RT::Principal') &&
+ $args{'Member'}->Id) {
+ $RT::Logger->warning("GroupMember::Create called with a bogus Principal arg");
+ return (undef);
+ }
+
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+
+ $RT::Handle->BeginTransaction() unless ($args{'InsideTransaction'});
+
+ # We really need to make sure we don't add any members to this group
+ # that contain the group itself. that would, um, suck.
+ # (and recurse infinitely) Later, we can add code to check this in the
+ # cache and bail so we can support cycling directed graphs
+
+ if ($args{'Member'}->IsGroup) {
+ my $member_object = $args{'Member'}->Object;
+ if ($member_object->HasMemberRecursively($args{'Group'})) {
+ $RT::Logger->debug("Adding that group would create a loop");
+ return(undef);
+ }
+ elsif ( $args{'Member'}->Id == $args{'Group'}->Id) {
+ $RT::Logger->debug("Can't add a group to itself");
+ return(undef);
+ }
+ }
+
+
+ my $id = $self->SUPER::Create(
+ GroupId => $args{'Group'}->Id,
+ MemberId => $args{'Member'}->Id
+ );
+
+ unless ($id) {
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return (undef);
+ }
+
+ my $cached_member = RT::CachedGroupMember->new( $self->CurrentUser );
+ my $cached_id = $cached_member->Create(
+ Member => $args{'Member'},
+ Group => $args{'Group'},
+ ImmediateParent => $args{'Group'},
+ Via => '0'
+ );
+
+
+ #When adding a member to a group, we need to go back
+ #and popuplate the CachedGroupMembers of all the groups that group is part of .
+
+ my $cgm = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+ # find things which have the current group as a member.
+ # $group is an RT::Principal for the group.
+ $cgm->LimitToGroupsWithMember( $args{'Group'}->Id );
+
+ while ( my $parent_member = $cgm->Next ) {
+ my $parent_id = $parent_member->MemberId;
+ my $via = $parent_member->Id;
+ my $group_id = $parent_member->GroupId;
+
+ my $other_cached_member =
+ RT::CachedGroupMember->new( $self->CurrentUser );
+ my $other_cached_id = $other_cached_member->Create(
+ Member => $args{'Member'},
+ Group => $parent_member->GroupObj,
+ ImmediateParent => $parent_member->MemberObj,
+ Via => $parent_member->Id
+ );
+ unless ($other_cached_id) {
+ $RT::Logger->err( "Couldn't add " . $args{'Member'}
+ . " as a submember of a supergroup" );
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return (undef);
+ }
+ }
+
+ unless ($cached_id) {
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return (undef);
+ }
+
+ $RT::Handle->Commit() unless ($args{'InsideTransaction'});
+
+ return ($id);
+}
+
+# }}}
+
+# {{{ sub _StashUser
+
+=head2 _StashUser PRINCIPAL
+
+Create { Group => undef, Member => undef }
+
+Creates an entry in the groupmembers table, which lists a user
+as a member of himself. This makes ACL checks a whole bunch easier.
+This happens once on user create and never ever gets yanked out.
+
+PRINCIPAL is expected to be an RT::Principal object for a user
+
+This routine expects to be called inside a transaction by RT::User->Create
+
+=cut
+
+sub _StashUser {
+ my $self = shift;
+ my %args = (
+ Group => undef,
+ Member => undef,
+ @_
+ );
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+
+
+ # We really need to make sure we don't add any members to this group
+ # that contain the group itself. that would, um, suck.
+ # (and recurse infinitely) Later, we can add code to check this in the
+ # cache and bail so we can support cycling directed graphs
+
+ my $id = $self->SUPER::Create(
+ GroupId => $args{'Group'}->Id,
+ MemberId => $args{'Member'}->Id,
+ );
+
+ unless ($id) {
+ return (undef);
+ }
+
+ my $cached_member = RT::CachedGroupMember->new( $self->CurrentUser );
+ my $cached_id = $cached_member->Create(
+ Member => $args{'Member'},
+ Group => $args{'Group'},
+ ImmediateParent => $args{'Group'},
+ Via => '0'
+ );
+
+ unless ($cached_id) {
+ return (undef);
+ }
+
+ return ($id);
+}
+
+# }}}
+
+# {{{ sub Delete
+
+=head2 Delete
+
+Takes no arguments. deletes the currently loaded member from the
+group in question.
+
+Expects to be called _outside_ a transaction
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+
+ $RT::Handle->BeginTransaction();
+
+ # Find all occurrences of this member as a member of this group
+ # in the cache and nuke them, recursively.
+
+ # The following code will delete all Cached Group members
+ # where this member's group is _not_ the primary group
+ # (Ie if we're deleting C as a member of B, and B happens to be
+ # a member of A, will delete C as a member of A without touching
+ # C as a member of B
+
+ my $cached_submembers = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+ $cached_submembers->Limit(
+ FIELD => 'MemberId',
+ OPERATOR => '=',
+ VALUE => $self->MemberObj->Id
+ );
+
+ $cached_submembers->Limit(
+ FIELD => 'ImmediateParentId',
+ OPERATOR => '=',
+ VALUE => $self->GroupObj->Id
+ );
+
+
+
+
+
+ while ( my $item_to_del = $cached_submembers->Next() ) {
+ my $del_err = $item_to_del->Delete();
+ unless ($del_err) {
+ $RT::Handle->Rollback();
+ $RT::Logger->warning("Couldn't delete cached group submember ".$item_to_del->Id);
+ return (undef);
+ }
+ }
+
+ my ($err, $msg) = $self->SUPER::Delete();
+ unless ($err) {
+ $RT::Logger->warning("Couldn't delete cached group submember ".$self->Id);
+ $RT::Handle->Rollback();
+ return (undef);
+ }
+
+ # Since this deletion may have changed the former member's
+ # delegation rights, we need to ensure that no invalid delegations
+ # remain.
+ $err = $self->MemberObj->_CleanupInvalidDelegations(InsideTransaction => 1);
+ unless ($err) {
+ $RT::Logger->warning("Unable to revoke delegated rights for principal ".$self->Id);
+ $RT::Handle->Rollback();
+ return (undef);
+ }
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+
+ $RT::Handle->Commit();
+ return ($err);
+
+}
+
+# }}}
+
+# {{{ sub MemberObj
+
+=head2 MemberObj
+
+Returns an RT::Principal object for the Principal specified by $self->PrincipalId
+
+=cut
+
+sub MemberObj {
+ my $self = shift;
+ unless ( defined( $self->{'Member_obj'} ) ) {
+ $self->{'Member_obj'} = RT::Principal->new( $self->CurrentUser );
+ $self->{'Member_obj'}->Load( $self->MemberId ) if ($self->MemberId);
+ }
+ return ( $self->{'Member_obj'} );
+}
+
+# }}}
+
+# {{{ sub GroupObj
+
+=head2 GroupObj
+
+Returns an RT::Principal object for the Group specified in $self->GroupId
+
+=cut
+
+sub GroupObj {
+ my $self = shift;
+ unless ( defined( $self->{'Group_obj'} ) ) {
+ $self->{'Group_obj'} = RT::Principal->new( $self->CurrentUser );
+ $self->{'Group_obj'}->Load( $self->GroupId );
+ }
+ return ( $self->{'Group_obj'} );
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/RT/GroupMembers.pm b/rt/lib/RT/GroupMembers.pm
new file mode 100755
index 0000000..978bbba
--- /dev/null
+++ b/rt/lib/RT/GroupMembers.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::GroupMembers -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::GroupMembers
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::GroupMembers;
+
+use RT::SearchBuilder;
+use RT::GroupMember;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'GroupMembers';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::GroupMember item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::GroupMember->new($self->CurrentUser));
+}
+
+ eval "require RT::GroupMembers_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/GroupMembers_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::GroupMembers_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/GroupMembers_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::GroupMembers_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/GroupMembers_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::GroupMembers_Overlay, RT::GroupMembers_Vendor, RT::GroupMembers_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/GroupMembers_Overlay.pm b/rt/lib/RT/GroupMembers_Overlay.pm
new file mode 100644
index 0000000..eb27031
--- /dev/null
+++ b/rt/lib/RT/GroupMembers_Overlay.pm
@@ -0,0 +1,153 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::GroupMembers - a collection of RT::GroupMember objects
+
+=head1 SYNOPSIS
+
+ use RT::GroupMembers;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::GroupMembers);
+
+=end testing
+
+=cut
+
+
+package RT::GroupMembers;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ LimitToUsers
+
+=head2 LimitToUsers
+
+Limits this search object to users who are members of this group.
+This is really useful when you want to have your UI separate out
+groups from users for display purposes
+
+=cut
+
+sub LimitToUsers {
+ my $self = shift;
+
+ my $principals = $self->NewAlias('Principals');
+ $self->Join( ALIAS1 => 'main', FIELD1 => 'MemberId',
+ ALIAS2 => $principals, FIELD2 =>'id');
+
+ $self->Limit( ALIAS => $principals,
+ FIELD => 'PrincipalType',
+ VALUE => 'User',
+ ENTRYAGGREGATOR => 'OR',
+ );
+}
+
+# }}}
+
+
+# {{{ LimitToGroups
+
+=head2 LimitToGroups
+
+Limits this search object to Groups who are members of this group.
+This is really useful when you want to have your UI separate out
+groups from users for display purposes
+
+=cut
+
+sub LimitToGroups {
+ my $self = shift;
+
+ my $principals = $self->NewAlias('Principals');
+ $self->Join( ALIAS1 => 'main', FIELD1 => 'MemberId',
+ ALIAS2 => $principals, FIELD2 =>'id');
+
+ $self->Limit( ALIAS => $principals,
+ FIELD => 'PrincipalType',
+ VALUE => 'Group',
+ ENTRYAGGREGATOR => 'OR',
+ );
+}
+
+# }}}
+
+# {{{ sub LimitToMembersOfGroup
+
+=head2 LimitToMembersOfGroup PRINCIPAL_ID
+
+Takes a Principal Id as its only argument.
+Limits the current search principals which are _directly_ members
+of the group which has PRINCIPAL_ID as its principal id.
+
+=cut
+
+sub LimitToMembersOfGroup {
+ my $self = shift;
+ my $group = shift;
+
+ return ($self->Limit(
+ VALUE => $group,
+ FIELD => 'GroupId',
+ ENTRYAGGREGATOR => 'OR',
+ QUOTEVALUE => 0
+ ));
+
+}
+# }}}
+
+1;
diff --git a/rt/lib/RT/Group_Overlay.pm b/rt/lib/RT/Group_Overlay.pm
new file mode 100644
index 0000000..d2e2364
--- /dev/null
+++ b/rt/lib/RT/Group_Overlay.pm
@@ -0,0 +1,1383 @@
+
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Released under the terms of version 2 of the GNU Public License
+
+=head1 NAME
+
+ RT::Group - RT\'s group object
+
+=head1 SYNOPSIS
+
+use RT::Group;
+my $group = new RT::Group($CurrentUser);
+
+=head1 DESCRIPTION
+
+An RT group object.
+
+=head1 METHODS
+
+
+=begin testing
+
+# {{{ Tests
+ok (require RT::Group);
+
+ok (my $group = RT::Group->new($RT::SystemUser), "instantiated a group object");
+ok (my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'TestGroup', Description => 'A test group',
+ ), 'Created a new group');
+ok ($id != 0, "Group id is $id");
+ok ($group->Name eq 'TestGroup', "The group's name is 'TestGroup'");
+my $ng = RT::Group->new($RT::SystemUser);
+
+ok($ng->LoadUserDefinedGroup('TestGroup'), "Loaded testgroup");
+ok(($ng->id == $group->id), "Loaded the right group");
+
+
+ok (($id,$msg) = $ng->AddMember('1'), "Added a member to the group");
+ok($id, $msg);
+ok (($id,$msg) = $ng->AddMember('2' ), "Added a member to the group");
+ok($id, $msg);
+ok (($id,$msg) = $ng->AddMember('3' ), "Added a member to the group");
+ok($id, $msg);
+
+# Group 1 now has members 1, 2 ,3
+
+my $group_2 = RT::Group->new($RT::SystemUser);
+ok (my ($id_2, $msg_2) = $group_2->CreateUserDefinedGroup( Name => 'TestGroup2', Description => 'A second test group'), , 'Created a new group');
+ok ($id_2 != 0, "Created group 2 ok- $msg_2 ");
+ok (($id,$msg) = $group_2->AddMember($ng->PrincipalId), "Made TestGroup a member of testgroup2");
+ok($id, $msg);
+ok (($id,$msg) = $group_2->AddMember('1' ), "Added member RT_System to the group TestGroup2");
+ok($id, $msg);
+
+# Group 2 how has 1, g1->{1, 2,3}
+
+my $group_3 = RT::Group->new($RT::SystemUser);
+ok (($id_3, $msg) = $group_3->CreateUserDefinedGroup( Name => 'TestGroup3', Description => 'A second test group'), 'Created a new group');
+ok ($id_3 != 0, "Created group 3 ok - $msg");
+ok (($id,$msg) =$group_3->AddMember($group_2->PrincipalId), "Made TestGroup a member of testgroup2");
+ok($id, $msg);
+
+# g3 now has g2->{1, g1->{1,2,3}}
+
+my $principal_1 = RT::Principal->new($RT::SystemUser);
+$principal_1->Load('1');
+
+my $principal_2 = RT::Principal->new($RT::SystemUser);
+$principal_2->Load('2');
+
+ok (($id,$msg) = $group_3->AddMember('1' ), "Added member RT_System to the group TestGroup2");
+ok($id, $msg);
+
+# g3 now has 1, g2->{1, g1->{1,2,3}}
+
+ok($group_3->HasMember($principal_2) == undef, "group 3 doesn't have member 2");
+ok($group_3->HasMemberRecursively($principal_2), "group 3 has member 2 recursively");
+ok($ng->HasMember($principal_2) , "group ".$ng->Id." has member 2");
+my ($delid , $delmsg) =$ng->DeleteMember($principal_2->Id);
+ok ($delid !=0, "Sucessfully deleted it-".$delid."-".$delmsg);
+
+#Gotta reload the group objects, since we've been messing with various internals.
+# we shouldn't need to do this.
+#$ng->LoadUserDefinedGroup('TestGroup');
+#$group_2->LoadUserDefinedGroup('TestGroup2');
+#$group_3->LoadUserDefinedGroup('TestGroup');
+
+# G1 now has 1, 3
+# Group 2 how has 1, g1->{1, 3}
+# g3 now has 1, g2->{1, g1->{1, 3}}
+
+ok(!$ng->HasMember($principal_2) , "group ".$ng->Id." no longer has member 2");
+ok($group_3->HasMemberRecursively($principal_2) == undef, "group 3 doesn't have member 2");
+ok($group_2->HasMemberRecursively($principal_2) == undef, "group 2 doesn't have member 2");
+ok($ng->HasMember($principal_2) == undef, "group 1 doesn't have member 2");;
+ok($group_3->HasMemberRecursively($principal_2) == undef, "group 3 has member 2 recursively");
+
+# }}}
+
+=end testing
+
+
+
+=cut
+
+
+package RT::Group;
+
+use strict;
+no warnings qw(redefine);
+
+use RT::Users;
+use RT::GroupMembers;
+use RT::Principals;
+use RT::ACL;
+
+use vars qw/$RIGHTS/;
+
+$RIGHTS = {
+ AdminGroup => 'Modify group metadata or delete group', # loc_pair
+ AdminGroupMembership =>
+ 'Modify membership roster for this group', # loc_pair
+ ModifyOwnMembership => 'Join or leave this group', # loc_pair
+ EditSavedSearches => 'Edit saved searches for this group', # loc_pair
+ ShowSavedSearches => 'Display saved searches for this group', # loc_pair
+ SeeGroup => 'Make this group visible to user', # loc_pair
+};
+
+# Tell RT::ACE that this sort of object can get acls granted
+$RT::ACE::OBJECT_TYPES{'RT::Group'} = 1;
+
+
+#
+
+# TODO: This should be refactored out into an RT::ACLedObject or something
+# stuff the rights into a hash of rights that can exist.
+
+foreach my $right ( keys %{$RIGHTS} ) {
+ $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
+}
+
+
+=head2 AvailableRights
+
+Returns a hash of available rights for this object. The keys are the right names and the values are a description of what the rights do
+
+=cut
+
+sub AvailableRights {
+ my $self = shift;
+ return($RIGHTS);
+}
+
+
+# {{{ sub SelfDescription
+
+=head2 SelfDescription
+
+Returns a user-readable description of what this group is for and what it's named.
+
+=cut
+
+sub SelfDescription {
+ my $self = shift;
+ if ($self->Domain eq 'ACLEquivalence') {
+ my $user = RT::Principal->new($self->CurrentUser);
+ $user->Load($self->Instance);
+ return $self->loc("user [_1]",$user->Object->Name);
+ }
+ elsif ($self->Domain eq 'UserDefined') {
+ return $self->loc("group '[_1]'",$self->Name);
+ }
+ elsif ($self->Domain eq 'Personal') {
+ my $user = RT::User->new($self->CurrentUser);
+ $user->Load($self->Instance);
+ return $self->loc("personal group '[_1]' for user '[_2]'",$self->Name, $user->Name);
+ }
+ elsif ($self->Domain eq 'RT::System-Role') {
+ return $self->loc("system [_1]",$self->Type);
+ }
+ elsif ($self->Domain eq 'RT::Queue-Role') {
+ my $queue = RT::Queue->new($self->CurrentUser);
+ $queue->Load($self->Instance);
+ return $self->loc("queue [_1] [_2]",$queue->Name, $self->Type);
+ }
+ elsif ($self->Domain eq 'RT::Ticket-Role') {
+ return $self->loc("ticket #[_1] [_2]",$self->Instance, $self->Type);
+ }
+ elsif ($self->Domain eq 'SystemInternal') {
+ return $self->loc("system group '[_1]'",$self->Type);
+ }
+ else {
+ return $self->loc("undescribed group [_1]",$self->Id);
+ }
+}
+
+# }}}
+
+# {{{ sub Load
+
+=head2 Load ID
+
+Load a group object from the database. Takes a single argument.
+If the argument is numerical, load by the column 'id'. Otherwise,
+complain and return.
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift || return undef;
+
+ #if it's an int, load by id. otherwise, load by name.
+ if ( $identifier !~ /\D/ ) {
+ $self->SUPER::LoadById($identifier);
+ }
+ else {
+ $RT::Logger->crit("Group -> Load called with a bogus argument");
+ return undef;
+ }
+}
+
+# }}}
+
+# {{{ sub LoadUserDefinedGroup
+
+=head2 LoadUserDefinedGroup NAME
+
+Loads a system group from the database. The only argument is
+the group's name.
+
+
+=cut
+
+sub LoadUserDefinedGroup {
+ my $self = shift;
+ my $identifier = shift;
+
+ $self->LoadByCols( "Domain" => 'UserDefined',
+ "Name" => $identifier );
+}
+
+# }}}
+
+# {{{ sub LoadACLEquivalenceGroup
+
+=head2 LoadACLEquivalenceGroup PRINCIPAL
+
+Loads a user's acl equivalence group. Takes a principal object.
+ACL equivalnce groups are used to simplify the acl system. Each user
+has one group that only he is a member of. Rights granted to the user
+are actually granted to that group. This greatly simplifies ACL checks.
+While this results in a somewhat more complex setup when creating users
+and granting ACLs, it _greatly_ simplifies acl checks.
+
+
+
+=cut
+
+sub LoadACLEquivalenceGroup {
+ my $self = shift;
+ my $princ = shift;
+
+ $self->LoadByCols( "Domain" => 'ACLEquivalence',
+ "Type" => 'UserEquiv',
+ "Instance" => $princ->Id);
+}
+
+# }}}
+
+# {{{ sub LoadPersonalGroup
+
+=head2 LoadPersonalGroup {Name => NAME, User => USERID}
+
+Loads a personal group from the database.
+
+=cut
+
+sub LoadPersonalGroup {
+ my $self = shift;
+ my %args = ( Name => undef,
+ User => undef,
+ @_);
+
+ $self->LoadByCols( "Domain" => 'Personal',
+ "Instance" => $args{'User'},
+ "Type" => '',
+ "Name" => $args{'Name'} );
+}
+
+# }}}
+
+# {{{ sub LoadSystemInternalGroup
+
+=head2 LoadSystemInternalGroup NAME
+
+Loads a Pseudo group from the database. The only argument is
+the group's name.
+
+
+=cut
+
+sub LoadSystemInternalGroup {
+ my $self = shift;
+ my $identifier = shift;
+
+ $self->LoadByCols( "Domain" => 'SystemInternal',
+ "Type" => $identifier );
+}
+
+# }}}
+
+# {{{ sub LoadTicketRoleGroup
+
+=head2 LoadTicketRoleGroup { Ticket => TICKET_ID, Type => TYPE }
+
+Loads a ticket group from the database.
+
+Takes a param hash with 2 parameters:
+
+ Ticket is the TicketId we're curious about
+ Type is the type of Group we're trying to load:
+ Requestor, Cc, AdminCc, Owner
+
+=cut
+
+sub LoadTicketRoleGroup {
+ my $self = shift;
+ my %args = (Ticket => '0',
+ Type => undef,
+ @_);
+ $self->LoadByCols( Domain => 'RT::Ticket-Role',
+ Instance =>$args{'Ticket'},
+ Type => $args{'Type'}
+ );
+}
+
+# }}}
+
+# {{{ sub LoadQueueRoleGroup
+
+=head2 LoadQueueRoleGroup { Queue => Queue_ID, Type => TYPE }
+
+Loads a Queue group from the database.
+
+Takes a param hash with 2 parameters:
+
+ Queue is the QueueId we're curious about
+ Type is the type of Group we're trying to load:
+ Requestor, Cc, AdminCc, Owner
+
+=cut
+
+sub LoadQueueRoleGroup {
+ my $self = shift;
+ my %args = (Queue => undef,
+ Type => undef,
+ @_);
+ $self->LoadByCols( Domain => 'RT::Queue-Role',
+ Instance =>$args{'Queue'},
+ Type => $args{'Type'}
+ );
+}
+
+# }}}
+
+# {{{ sub LoadSystemRoleGroup
+
+=head2 LoadSystemRoleGroup Type
+
+Loads a System group from the database.
+
+Takes a single param: Type
+
+ Type is the type of Group we're trying to load:
+ Requestor, Cc, AdminCc, Owner
+
+=cut
+
+sub LoadSystemRoleGroup {
+ my $self = shift;
+ my $type = shift;
+ $self->LoadByCols( Domain => 'RT::System-Role',
+ Type => $type
+ );
+}
+
+# }}}
+
+# {{{ sub Create
+
+=head2 Create
+
+You need to specify what sort of group you're creating by calling one of the other
+Create_____ routines.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ $RT::Logger->crit("Someone called RT::Group->Create. this method does not exist. someone's being evil");
+ return(0,$self->loc('Permission Denied'));
+}
+
+# }}}
+
+# {{{ sub _Create
+
+=head2 _Create
+
+Takes a paramhash with named arguments: Name, Description.
+
+Returns a tuple of (Id, Message). If id is 0, the create failed
+
+=cut
+
+sub _Create {
+ my $self = shift;
+ my %args = (
+ Name => undef,
+ Description => undef,
+ Domain => undef,
+ Type => undef,
+ Instance => '0',
+ InsideTransaction => undef,
+ _RecordTransaction => 1,
+ @_
+ );
+
+ $RT::Handle->BeginTransaction() unless ($args{'InsideTransaction'});
+ # Groups deal with principal ids, rather than user ids.
+ # When creating this group, set up a principal Id for it.
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ my $principal_id = $principal->Create(
+ PrincipalType => 'Group',
+ ObjectId => '0'
+ );
+ $principal->__Set(Field => 'ObjectId', Value => $principal_id);
+
+
+ $self->SUPER::Create(
+ id => $principal_id,
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ Type => $args{'Type'},
+ Domain => $args{'Domain'},
+ Instance => ($args{'Instance'} || '0')
+ );
+ my $id = $self->Id;
+ unless ($id) {
+ return ( 0, $self->loc('Could not create group') );
+ }
+
+ # If we couldn't create a principal Id, get the fuck out.
+ unless ($principal_id) {
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ $RT::Logger->crit( "Couldn't create a Principal on new user create. Strange things are afoot at the circle K" );
+ return ( 0, $self->loc('Could not create group') );
+ }
+
+ # Now we make the group a member of itself as a cached group member
+ # this needs to exist so that group ACL checks don't fall over.
+ # you're checking CachedGroupMembers to see if the principal in question
+ # is a member of the principal the rights have been granted too
+
+ # in the ordinary case, this would fail badly because it would recurse and add all the members of this group as
+ # cached members. thankfully, we're creating the group now...so it has no members.
+ my $cgm = RT::CachedGroupMember->new($self->CurrentUser);
+ $cgm->Create(Group =>$self->PrincipalObj, Member => $self->PrincipalObj, ImmediateParent => $self->PrincipalObj);
+
+
+ if ( $args{'_RecordTransaction'} ) {
+ $self->_NewTransaction( Type => "Create" );
+ }
+
+ $RT::Handle->Commit() unless ($args{'InsideTransaction'});
+
+ return ( $id, $self->loc("Group created") );
+}
+
+# }}}
+
+# {{{ CreateUserDefinedGroup
+
+=head2 CreateUserDefinedGroup { Name => "name", Description => "Description"}
+
+A helper subroutine which creates a system group
+
+Returns a tuple of (Id, Message). If id is 0, the create failed
+
+=cut
+
+sub CreateUserDefinedGroup {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminGroup') ) {
+ $RT::Logger->warning( $self->CurrentUser->Name
+ . " Tried to create a group without permission." );
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ return($self->_Create( Domain => 'UserDefined', Type => '', Instance => '', @_));
+}
+
+# }}}
+
+# {{{ _CreateACLEquivalenceGroup
+
+=head2 _CreateACLEquivalenceGroup { Principal }
+
+A helper subroutine which creates a group containing only
+an individual user. This gets used by the ACL system to check rights.
+Yes, it denormalizes the data, but that's ok, as we totally win on performance.
+
+Returns a tuple of (Id, Message). If id is 0, the create failed
+
+=cut
+
+sub _CreateACLEquivalenceGroup {
+ my $self = shift;
+ my $princ = shift;
+
+ my $id = $self->_Create( Domain => 'ACLEquivalence',
+ Type => 'UserEquiv',
+ Name => 'User '. $princ->Object->Id,
+ Description => 'ACL equiv. for user '.$princ->Object->Id,
+ Instance => $princ->Id,
+ InsideTransaction => 1);
+ unless ($id) {
+ $RT::Logger->crit("Couldn't create ACL equivalence group");
+ return undef;
+ }
+
+ # We use stashuser so we don't get transactions inside transactions
+ # and so we bypass all sorts of cruft we don't need
+ my $aclstash = RT::GroupMember->new($self->CurrentUser);
+ my ($stash_id, $add_msg) = $aclstash->_StashUser(Group => $self->PrincipalObj,
+ Member => $princ);
+
+ unless ($stash_id) {
+ $RT::Logger->crit("Couldn't add the user to his own acl equivalence group:".$add_msg);
+ # We call super delete so we don't get acl checked.
+ $self->SUPER::Delete();
+ return(undef);
+ }
+ return ($id);
+}
+
+# }}}
+
+# {{{ CreatePersonalGroup
+
+=head2 CreatePersonalGroup { PrincipalId => PRINCIPAL_ID, Name => "name", Description => "Description"}
+
+A helper subroutine which creates a personal group. Generally,
+personal groups are used for ACL delegation and adding to ticket roles
+PrincipalId defaults to the current user's principal id.
+
+Returns a tuple of (Id, Message). If id is 0, the create failed
+
+=cut
+
+sub CreatePersonalGroup {
+ my $self = shift;
+ my %args = (
+ Name => undef,
+ Description => undef,
+ PrincipalId => $self->CurrentUser->PrincipalId,
+ @_
+ );
+
+ if ( $self->CurrentUser->PrincipalId == $args{'PrincipalId'} ) {
+
+ unless ( $self->CurrentUserHasRight('AdminOwnPersonalGroups') ) {
+ $RT::Logger->warning( $self->CurrentUser->Name
+ . " Tried to create a group without permission." );
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ }
+ else {
+ unless ( $self->CurrentUserHasRight('AdminAllPersonalGroups') ) {
+ $RT::Logger->warning( $self->CurrentUser->Name
+ . " Tried to create a group without permission." );
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ }
+
+ return (
+ $self->_Create(
+ Domain => 'Personal',
+ Type => '',
+ Instance => $args{'PrincipalId'},
+ Name => $args{'Name'},
+ Description => $args{'Description'}
+ )
+ );
+}
+
+# }}}
+
+# {{{ CreateRoleGroup
+
+=head2 CreateRoleGroup { Domain => DOMAIN, Type => TYPE, Instance => ID }
+
+A helper subroutine which creates a ticket group. (What RT 2.0 called Ticket watchers)
+Type is one of ( "Requestor" || "Cc" || "AdminCc" || "Owner")
+Domain is one of (RT::Ticket-Role || RT::Queue-Role || RT::System-Role)
+Instance is the id of the ticket or queue in question
+
+This routine expects to be called from {Ticket||Queue}->CreateTicketGroups _inside of a transaction_
+
+Returns a tuple of (Id, Message). If id is 0, the create failed
+
+=cut
+
+sub CreateRoleGroup {
+ my $self = shift;
+ my %args = ( Instance => undef,
+ Type => undef,
+ Domain => undef,
+ @_ );
+ unless ( $args{'Type'} =~ /^(?:Cc|AdminCc|Requestor|Owner)$/ ) {
+ return ( 0, $self->loc("Invalid Group Type") );
+ }
+
+
+ return ( $self->_Create( Domain => $args{'Domain'},
+ Instance => $args{'Instance'},
+ Type => $args{'Type'},
+ InsideTransaction => 1 ) );
+}
+
+# }}}
+
+# {{{ sub Delete
+
+=head2 Delete
+
+Delete this object
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminGroup') ) {
+ return ( 0, 'Permission Denied' );
+ }
+
+ $RT::Logger->crit("Deleting groups violates referential integrity until we go through and fix this");
+ # TODO XXX
+
+ # Remove the principal object
+ # Remove this group from anything it's a member of.
+ # Remove all cached members of this group
+ # Remove any rights granted to this group
+ # remove any rights delegated by way of this group
+
+ return ( $self->SUPER::Delete(@_) );
+}
+
+# }}}
+
+=head2 SetDisabled BOOL
+
+If passed a positive value, this group will be disabled. No rights it commutes or grants will be honored.
+It will not appear in most group listings.
+
+This routine finds all the cached group members that are members of this group (recursively) and disables them.
+
+=cut
+
+ # }}}
+
+ sub SetDisabled {
+ my $self = shift;
+ my $val = shift;
+ if ($self->Domain eq 'Personal') {
+ if ($self->CurrentUser->PrincipalId == $self->Instance) {
+ unless ( $self->CurrentUserHasRight('AdminOwnPersonalGroups')) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ } else {
+ unless ( $self->CurrentUserHasRight('AdminAllPersonalGroups') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+ }
+ else {
+ unless ( $self->CurrentUserHasRight('AdminGroup') ) {
+ return (0, $self->loc('Permission Denied'));
+ }
+ }
+ $RT::Handle->BeginTransaction();
+ $self->PrincipalObj->SetDisabled($val);
+
+
+
+
+ # Find all occurrences of this member as a member of this group
+ # in the cache and nuke them, recursively.
+
+ # The following code will delete all Cached Group members
+ # where this member's group is _not_ the primary group
+ # (Ie if we're deleting C as a member of B, and B happens to be
+ # a member of A, will delete C as a member of A without touching
+ # C as a member of B
+
+ my $cached_submembers = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+ $cached_submembers->Limit( FIELD => 'ImmediateParentId', OPERATOR => '=', VALUE => $self->Id);
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+
+
+
+ while ( my $item = $cached_submembers->Next() ) {
+ my $del_err = $item->SetDisabled($val);
+ unless ($del_err) {
+ $RT::Handle->Rollback();
+ $RT::Logger->warning("Couldn't disable cached group submember ".$item->Id);
+ return (undef);
+ }
+ }
+
+ $RT::Handle->Commit();
+ return (1, $self->loc("Succeeded"));
+
+}
+
+# }}}
+
+
+
+sub Disabled {
+ my $self = shift;
+ $self->PrincipalObj->Disabled(@_);
+}
+
+
+# {{{ DeepMembersObj
+
+=head2 DeepMembersObj
+
+Returns an RT::CachedGroupMembers object of this group's members,
+including all members of subgroups.
+
+=cut
+
+sub DeepMembersObj {
+ my $self = shift;
+ my $members_obj = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+ #If we don't have rights, don't include any results
+ # TODO XXX WHY IS THERE NO ACL CHECK HERE?
+ $members_obj->LimitToMembersOfGroup( $self->PrincipalId );
+
+ return ( $members_obj );
+
+}
+
+# }}}
+
+# {{{ UserMembersObj
+
+=head2 UserMembersObj
+
+Returns an RT::Users object of this group's members, including
+all members of subgroups
+
+=cut
+
+sub UserMembersObj {
+ my $self = shift;
+
+ my $users = RT::Users->new($self->CurrentUser);
+
+ #If we don't have rights, don't include any results
+ # TODO XXX WHY IS THERE NO ACL CHECK HERE?
+
+ my $cached_members = $users->NewAlias('CachedGroupMembers');
+ $users->Join(ALIAS1 => $cached_members, FIELD1 => 'MemberId',
+ ALIAS2 => $users->PrincipalsAlias, FIELD2 => 'id');
+ $users->Limit(ALIAS => $cached_members,
+ FIELD => 'GroupId',
+ OPERATOR => '=',
+ VALUE => $self->PrincipalId);
+
+ return ( $users);
+
+}
+
+# }}}
+
+# {{{ MembersObj
+
+=head2 MembersObj
+
+Returns an RT::GroupMembers object of this group's direct members.
+
+=cut
+
+sub MembersObj {
+ my $self = shift;
+ my $members_obj = RT::GroupMembers->new( $self->CurrentUser );
+
+ #If we don't have rights, don't include any results
+ # TODO XXX WHY IS THERE NO ACL CHECK HERE?
+ $members_obj->LimitToMembersOfGroup( $self->PrincipalId );
+
+ return ( $members_obj );
+
+}
+
+# }}}
+
+# {{{ MemberEmailAddresses
+
+=head2 MemberEmailAddresses
+
+Returns an array of the email addresses of all of this group's members
+
+
+=cut
+
+sub MemberEmailAddresses {
+ my $self = shift;
+
+ my %addresses;
+ my $members = $self->UserMembersObj();
+ while (my $member = $members->Next) {
+ $addresses{$member->EmailAddress} = 1;
+ }
+ return(sort keys %addresses);
+}
+
+# }}}
+
+# {{{ MemberEmailAddressesAsString
+
+=head2 MemberEmailAddressesAsString
+
+Returns a comma delimited string of the email addresses of all users
+who are members of this group.
+
+=cut
+
+
+sub MemberEmailAddressesAsString {
+ my $self = shift;
+ return (join(', ', $self->MemberEmailAddresses));
+}
+
+# }}}
+
+# {{{ AddMember
+
+=head2 AddMember PRINCIPAL_ID
+
+AddMember adds a principal to this group. It takes a single principal id.
+Returns a two value array. the first value is true on successful
+addition or 0 on failure. The second value is a textual status msg.
+
+=cut
+
+sub AddMember {
+ my $self = shift;
+ my $new_member = shift;
+
+
+
+ if ($self->Domain eq 'Personal') {
+ if ($self->CurrentUser->PrincipalId == $self->Instance) {
+ unless ( $self->CurrentUserHasRight('AdminOwnPersonalGroups')) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ } else {
+ unless ( $self->CurrentUserHasRight('AdminAllPersonalGroups') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+ }
+
+ else {
+ # We should only allow membership changes if the user has the right
+ # to modify group membership or the user is the principal in question
+ # and the user has the right to modify his own membership
+ unless ( ($new_member == $self->CurrentUser->PrincipalId &&
+ $self->CurrentUserHasRight('ModifyOwnMembership') ) ||
+ $self->CurrentUserHasRight('AdminGroupMembership') ) {
+ #User has no permission to be doing this
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ }
+ $self->_AddMember(PrincipalId => $new_member);
+}
+
+# A helper subroutine for AddMember that bypasses the ACL checks
+# this should _ONLY_ ever be called from Ticket/Queue AddWatcher
+# when we want to deal with groups according to queue rights
+# In the dim future, this will all get factored out and life
+# will get better
+
+# takes a paramhash of { PrincipalId => undef, InsideTransaction }
+
+sub _AddMember {
+ my $self = shift;
+ my %args = ( PrincipalId => undef,
+ InsideTransaction => undef,
+ @_);
+ my $new_member = $args{'PrincipalId'};
+
+ unless ($self->Id) {
+ $RT::Logger->crit("Attempting to add a member to a group which wasn't loaded. 'oops'");
+ return(0, $self->loc("Group not found"));
+ }
+
+ unless ($new_member =~ /^\d+$/) {
+ $RT::Logger->crit("_AddMember called with a parameter that's not an integer.");
+ }
+
+
+ my $new_member_obj = RT::Principal->new( $self->CurrentUser );
+ $new_member_obj->Load($new_member);
+
+
+ unless ( $new_member_obj->Id ) {
+ $RT::Logger->debug("Couldn't find that principal");
+ return ( 0, $self->loc("Couldn't find that principal") );
+ }
+
+ if ( $self->HasMember( $new_member_obj ) ) {
+
+ #User is already a member of this group. no need to add it
+ return ( 0, $self->loc("Group already has member") );
+ }
+ if ( $new_member_obj->IsGroup &&
+ $new_member_obj->Object->HasMemberRecursively($self->PrincipalObj) ) {
+
+ #This group can't be made to be a member of itself
+ return ( 0, $self->loc("Groups can't be members of their members"));
+ }
+
+
+ my $member_object = RT::GroupMember->new( $self->CurrentUser );
+ my $id = $member_object->Create(
+ Member => $new_member_obj,
+ Group => $self->PrincipalObj,
+ InsideTransaction => $args{'InsideTransaction'}
+ );
+ if ($id) {
+ return ( 1, $self->loc("Member added") );
+ }
+ else {
+ return(0, $self->loc("Couldn't add member to group"));
+ }
+}
+# }}}
+
+# {{{ HasMember
+
+=head2 HasMember RT::Principal
+
+Takes an RT::Principal object returns a GroupMember Id if that user is a
+member of this group.
+Returns undef if the user isn't a member of the group or if the current
+user doesn't have permission to find out. Arguably, it should differentiate
+between ACL failure and non membership.
+
+=cut
+
+sub HasMember {
+ my $self = shift;
+ my $principal = shift;
+
+
+ unless (UNIVERSAL::isa($principal,'RT::Principal')) {
+ $RT::Logger->crit("Group::HasMember was called with an argument that".
+ "isn't an RT::Principal. It's $principal");
+ return(undef);
+ }
+
+ unless ($principal->Id) {
+ return(undef);
+ }
+
+ my $member_obj = RT::GroupMember->new( $self->CurrentUser );
+ $member_obj->LoadByCols( MemberId => $principal->id,
+ GroupId => $self->PrincipalId );
+
+ #If we have a member object
+ if ( defined $member_obj->id ) {
+ return ( $member_obj->id );
+ }
+
+ #If Load returns no objects, we have an undef id.
+ else {
+ #$RT::Logger->debug($self." does not contain principal ".$principal->id);
+ return (undef);
+ }
+}
+
+# }}}
+
+# {{{ HasMemberRecursively
+
+=head2 HasMemberRecursively RT::Principal
+
+Takes an RT::Principal object and returns true if that user is a member of
+this group.
+Returns undef if the user isn't a member of the group or if the current
+user doesn't have permission to find out. Arguably, it should differentiate
+between ACL failure and non membership.
+
+=cut
+
+sub HasMemberRecursively {
+ my $self = shift;
+ my $principal = shift;
+
+ unless (UNIVERSAL::isa($principal,'RT::Principal')) {
+ $RT::Logger->crit("Group::HasMemberRecursively was called with an argument that".
+ "isn't an RT::Principal. It's $principal");
+ return(undef);
+ }
+ my $member_obj = RT::CachedGroupMember->new( $self->CurrentUser );
+ $member_obj->LoadByCols( MemberId => $principal->Id,
+ GroupId => $self->PrincipalId ,
+ Disabled => 0
+ );
+
+ #If we have a member object
+ if ( defined $member_obj->id ) {
+ return ( 1);
+ }
+
+ #If Load returns no objects, we have an undef id.
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
+# {{{ DeleteMember
+
+=head2 DeleteMember PRINCIPAL_ID
+
+Takes the principal id of a current user or group.
+If the current user has apropriate rights,
+removes that GroupMember from this group.
+Returns a two value array. the first value is true on successful
+addition or 0 on failure. The second value is a textual status msg.
+
+=cut
+
+sub DeleteMember {
+ my $self = shift;
+ my $member_id = shift;
+
+
+ # We should only allow membership changes if the user has the right
+ # to modify group membership or the user is the principal in question
+ # and the user has the right to modify his own membership
+
+ if ($self->Domain eq 'Personal') {
+ if ($self->CurrentUser->PrincipalId == $self->Instance) {
+ unless ( $self->CurrentUserHasRight('AdminOwnPersonalGroups')) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ } else {
+ unless ( $self->CurrentUserHasRight('AdminAllPersonalGroups') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+ }
+ else {
+ unless ( (($member_id == $self->CurrentUser->PrincipalId) &&
+ $self->CurrentUserHasRight('ModifyOwnMembership') ) ||
+ $self->CurrentUserHasRight('AdminGroupMembership') ) {
+ #User has no permission to be doing this
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+ $self->_DeleteMember($member_id);
+}
+
+# A helper subroutine for DeleteMember that bypasses the ACL checks
+# this should _ONLY_ ever be called from Ticket/Queue DeleteWatcher
+# when we want to deal with groups according to queue rights
+# In the dim future, this will all get factored out and life
+# will get better
+
+sub _DeleteMember {
+ my $self = shift;
+ my $member_id = shift;
+
+ my $member_obj = RT::GroupMember->new( $self->CurrentUser );
+
+ $member_obj->LoadByCols( MemberId => $member_id,
+ GroupId => $self->PrincipalId);
+
+
+ #If we couldn't load it, return undef.
+ unless ( $member_obj->Id() ) {
+ $RT::Logger->debug("Group has no member with that id");
+ return ( 0,$self->loc( "Group has no such member" ));
+ }
+
+ #Now that we've checked ACLs and sanity, delete the groupmember
+ my $val = $member_obj->Delete();
+
+ if ($val) {
+ return ( $val, $self->loc("Member deleted") );
+ }
+ else {
+ $RT::Logger->debug("Failed to delete group ".$self->Id." member ". $member_id);
+ return ( 0, $self->loc("Member not deleted" ));
+ }
+}
+
+# }}}
+
+# {{{ sub _CleanupInvalidDelegations
+
+=head2 _CleanupInvalidDelegations { InsideTransaction => undef }
+
+Revokes all ACE entries delegated by members of this group which are
+inconsistent with their current delegation rights. Does not perform
+permission checks. Should only ever be called from inside the RT
+library.
+
+If called from inside a transaction, specify a true value for the
+InsideTransaction parameter.
+
+Returns a true value if the deletion succeeded; returns a false value
+and logs an internal error if the deletion fails (should not happen).
+
+=cut
+
+# XXX Currently there is a _CleanupInvalidDelegations method in both
+# RT::User and RT::Group. If the recursive cleanup call for groups is
+# ever unrolled and merged, this code will probably want to be
+# factored out into RT::Principal.
+
+sub _CleanupInvalidDelegations {
+ my $self = shift;
+ my %args = ( InsideTransaction => undef,
+ @_ );
+
+ unless ( $self->Id ) {
+ $RT::Logger->warning("Group not loaded.");
+ return (undef);
+ }
+
+ my $in_trans = $args{InsideTransaction};
+
+ # TODO: Can this be unrolled such that the number of DB queries is constant rather than linear in exploded group size?
+ my $members = $self->DeepMembersObj();
+ $members->LimitToUsers();
+ $RT::Handle->BeginTransaction() unless $in_trans;
+ while ( my $member = $members->Next()) {
+ my $ret = $member->MemberObj->_CleanupInvalidDelegations(InsideTransaction => 1,
+ Object => $args{Object});
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ return (undef);
+ }
+ }
+ $RT::Handle->Commit() unless $in_trans;
+ return(1);
+}
+
+# }}}
+
+# {{{ ACL Related routines
+
+# {{{ sub _Set
+sub _Set {
+ my $self = shift;
+ my %args = (
+ Field => undef,
+ Value => undef,
+ TransactionType => 'Set',
+ RecordTransaction => 1,
+ @_
+ );
+
+ if ($self->Domain eq 'Personal') {
+ if ($self->CurrentUser->PrincipalId == $self->Instance) {
+ unless ( $self->CurrentUserHasRight('AdminOwnPersonalGroups')) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ } else {
+ unless ( $self->CurrentUserHasRight('AdminAllPersonalGroups') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+ }
+ else {
+ unless ( $self->CurrentUserHasRight('AdminGroup') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+
+ my $Old = $self->SUPER::_Value("$args{'Field'}");
+
+ my ($ret, $msg) = $self->SUPER::_Set( Field => $args{'Field'},
+ Value => $args{'Value'} );
+
+ #If we can't actually set the field to the value, don't record
+ # a transaction. instead, get out of here.
+ if ( $ret == 0 ) { return ( 0, $msg ); }
+
+ if ( $args{'RecordTransaction'} == 1 ) {
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => $args{'TransactionType'},
+ Field => $args{'Field'},
+ NewValue => $args{'Value'},
+ OldValue => $Old,
+ TimeTaken => $args{'TimeTaken'},
+ );
+ return ( $Trans, scalar $TransObj->Description );
+ }
+ else {
+ return ( $ret, $msg );
+ }
+}
+
+# }}}
+
+
+
+
+=head2 CurrentUserHasRight RIGHTNAME
+
+Returns true if the current user has the specified right for this group.
+
+
+ TODO: we don't deal with membership visibility yet
+
+=cut
+
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+
+
+ if ($self->Id &&
+ $self->CurrentUser->HasRight( Object => $self,
+ Right => $right )) {
+ return(1);
+ }
+ elsif ( $self->CurrentUser->HasRight(Object => $RT::System, Right => $right )) {
+ return (1);
+ } else {
+ return(undef);
+ }
+
+}
+
+# }}}
+
+
+
+
+# {{{ Principal related routines
+
+=head2 PrincipalObj
+
+Returns the principal object for this user. returns an empty RT::Principal
+if there's no principal object matching this user.
+The response is cached. PrincipalObj should never ever change.
+
+=begin testing
+
+ok(my $u = RT::Group->new($RT::SystemUser));
+ok($u->Load(4), "Loaded the first user");
+ok($u->PrincipalObj->ObjectId == 4, "user 4 is the fourth principal");
+ok($u->PrincipalObj->PrincipalType eq 'Group' , "Principal 4 is a group");
+
+=end testing
+
+=cut
+
+
+sub PrincipalObj {
+ my $self = shift;
+ unless ($self->{'PrincipalObj'} &&
+ ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
+ ($self->{'PrincipalObj'}->PrincipalType eq 'Group')) {
+
+ $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
+ $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
+ 'PrincipalType' => 'Group') ;
+ }
+ return($self->{'PrincipalObj'});
+}
+
+
+=head2 PrincipalId
+
+Returns this user's PrincipalId
+
+=cut
+
+sub PrincipalId {
+ my $self = shift;
+ return $self->Id;
+}
+
+# }}}
+
+sub BasicColumns {
+ (
+ [ Name => 'Name' ],
+ [ Description => 'Description' ],
+ );
+}
+
+1;
+
+=head1 AUTHOR
+
+Jesse Vincent, jesse@bestpractical.com
+
+=head1 SEE ALSO
+
+RT
+
diff --git a/rt/lib/RT/Groups.pm b/rt/lib/RT/Groups.pm
new file mode 100755
index 0000000..2ee4d58
--- /dev/null
+++ b/rt/lib/RT/Groups.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Groups -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Groups
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Groups;
+
+use RT::SearchBuilder;
+use RT::Group;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Groups';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Group item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Group->new($self->CurrentUser));
+}
+
+ eval "require RT::Groups_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Groups_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Groups_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Groups_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Groups_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Groups_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Groups_Overlay, RT::Groups_Vendor, RT::Groups_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Groups_Overlay.pm b/rt/lib/RT/Groups_Overlay.pm
new file mode 100644
index 0000000..82e021c
--- /dev/null
+++ b/rt/lib/RT/Groups_Overlay.pm
@@ -0,0 +1,537 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Groups - a collection of RT::Group objects
+
+=head1 SYNOPSIS
+
+ use RT::Groups;
+ my $groups = $RT::Groups->new($CurrentUser);
+ $groups->UnLimit();
+ while (my $group = $groups->Next()) {
+ print $group->Id ." is a group id\n";
+ }
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Groups);
+
+=end testing
+
+=cut
+
+
+package RT::Groups;
+
+use strict;
+no warnings qw(redefine);
+
+use RT::Users;
+
+# XXX: below some code is marked as subject to generalize in Groups, Users classes.
+# RUZ suggest name Principals::Generic or Principals::Base as abstract class, but
+# Jesse wants something that doesn't imply it's a Principals.pm subclass.
+# See comments below for candidats.
+
+
+# {{{ sub _Init
+
+=begin testing
+
+# next had bugs
+# Groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => xx );
+my $g = RT::Group->new($RT::SystemUser);
+my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'GroupsNotEqualTest');
+ok ($id, "created group #". $g->id) or diag("error: $msg");
+
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $g->id );
+$groups->LimitToUserDefinedGroups();
+my $bug = grep $_->id == $g->id, @{$groups->ItemsArrayRef};
+ok (!$bug, "didn't find group");
+
+=end testing
+
+=cut
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = "Groups";
+ $self->{'primary_key'} = "id";
+
+ my @result = $self->SUPER::_Init(@_);
+
+ $self->OrderBy( ALIAS => 'main',
+ FIELD => 'Name',
+ ORDER => 'ASC');
+
+ # XXX: this code should be generalized
+ $self->{'princalias'} = $self->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Principals',
+ FIELD2 => 'id'
+ );
+
+ # even if this condition is useless and ids in the Groups table
+ # only match principals with type 'Group' this could speed up
+ # searches in some DBs.
+ $self->Limit( ALIAS => $self->{'princalias'},
+ FIELD => 'PrincipalType',
+ VALUE => 'Group',
+ );
+
+ return (@result);
+}
+# }}}
+
+=head2 PrincipalsAlias
+
+Returns the string that represents this Users object's primary "Principals" alias.
+
+=cut
+
+# XXX: should be generalized, code duplication
+sub PrincipalsAlias {
+ my $self = shift;
+ return($self->{'princalias'});
+
+}
+
+
+# {{{ LimiToSystemInternalGroups
+
+=head2 LimitToSystemInternalGroups
+
+Return only SystemInternal Groups, such as "privileged" "unprivileged" and "everyone"
+
+=cut
+
+
+sub LimitToSystemInternalGroups {
+ my $self = shift;
+ $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'SystemInternal');
+ # All system internal groups have the same instance. No reason to limit down further
+ #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '0');
+}
+
+
+# }}}
+
+# {{{ LimiToUserDefinedGroups
+
+=head2 LimitToUserDefined Groups
+
+Return only UserDefined Groups
+
+=cut
+
+
+sub LimitToUserDefinedGroups {
+ my $self = shift;
+ $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined');
+ # All user-defined groups have the same instance. No reason to limit down further
+ #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '');
+}
+
+
+# }}}
+
+# {{{ LimiToPersonalGroups
+
+=head2 LimitToPersonalGroupsFor PRINCIPAL_ID
+
+Return only Personal Groups for the user whose principal id
+is PRINCIPAL_ID
+
+=cut
+
+
+sub LimitToPersonalGroupsFor {
+ my $self = shift;
+ my $princ = shift;
+
+ $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'Personal');
+ $self->Limit( FIELD => 'Instance',
+ OPERATOR => '=',
+ VALUE => $princ);
+}
+
+
+# }}}
+
+# {{{ LimitToRolesForQueue
+
+=head2 LimitToRolesForQueue QUEUE_ID
+
+Limits the set of groups found to role groups for queue QUEUE_ID
+
+=cut
+
+sub LimitToRolesForQueue {
+ my $self = shift;
+ my $queue = shift;
+ $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Queue-Role');
+ $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $queue);
+}
+
+# }}}
+
+# {{{ LimitToRolesForTicket
+
+=head2 LimitToRolesForTicket Ticket_ID
+
+Limits the set of groups found to role groups for Ticket Ticket_ID
+
+=cut
+
+sub LimitToRolesForTicket {
+ my $self = shift;
+ my $Ticket = shift;
+ $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Ticket-Role');
+ $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '$Ticket');
+}
+
+# }}}
+
+# {{{ LimitToRolesForSystem
+
+=head2 LimitToRolesForSystem System_ID
+
+Limits the set of groups found to role groups for System System_ID
+
+=cut
+
+sub LimitToRolesForSystem {
+ my $self = shift;
+ $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::System-Role');
+}
+
+# }}}
+
+=head2 WithMember {PrincipalId => PRINCIPAL_ID, Recursively => undef}
+
+Limits the set of groups returned to groups which have
+Principal PRINCIPAL_ID as a member
+
+=begin testing
+
+my $u = RT::User->new($RT::SystemUser);
+$u->Create(Name => 'Membertests');
+my $g = RT::Group->new($RT::SystemUser);
+my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'Membertests');
+ok ($id, $msg);
+
+my ($aid, $amsg) =$g->AddMember($u->id);
+ok ($aid, $amsg);
+ok($g->HasMember($u->PrincipalObj),"G has member u");
+
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->LimitToUserDefinedGroups();
+$groups->WithMember(PrincipalId => $u->id);
+ok ($groups->Count == 1,"found the 1 group - " . $groups->Count);
+ok ($groups->First->Id == $g->Id, "it's the right one");
+
+=end testing
+
+
+=cut
+
+sub WithMember {
+ my $self = shift;
+ my %args = ( PrincipalId => undef,
+ Recursively => undef,
+ @_);
+ my $members;
+
+ if ($args{'Recursively'}) {
+ $members = $self->NewAlias('CachedGroupMembers');
+ } else {
+ $members = $self->NewAlias('GroupMembers');
+ }
+ $self->Join(ALIAS1 => 'main', FIELD1 => 'id',
+ ALIAS2 => $members, FIELD2 => 'GroupId');
+
+ $self->Limit(ALIAS => $members, FIELD => 'MemberId', OPERATOR => '=', VALUE => $args{'PrincipalId'});
+}
+
+
+=head2 WithRight { Right => RIGHTNAME, Object => RT::Record, IncludeSystemRights => 1, IncludeSuperusers => 0, EquivObjects => [ ] }
+
+
+Find all groups which have RIGHTNAME for RT::Record. Optionally include global rights and superusers. By default, include the global rights, but not the superusers.
+
+=begin testing
+
+my $q = RT::Queue->new($RT::SystemUser);
+my ($id, $msg) =$q->Create( Name => 'GlobalACLTest');
+ok ($id, $msg);
+
+my $testuser = RT::User->new($RT::SystemUser);
+($id,$msg) = $testuser->Create(Name => 'JustAnAdminCc');
+ok ($id,$msg);
+
+my $global_admin_cc = RT::Group->new($RT::SystemUser);
+$global_admin_cc->LoadSystemRoleGroup('AdminCc');
+ok($global_admin_cc->id, "Found the global admincc group");
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'OwnTicket', Object => $q);
+is($groups->Count, 1);
+($id, $msg) = $global_admin_cc->PrincipalObj->GrantRight(Right =>'OwnTicket', Object=> $RT::System);
+ok ($id,$msg);
+ok (!$testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does not have the right to own tickets in the test queue");
+($id, $msg) = $q->AddWatcher(Type => 'AdminCc', PrincipalId => $testuser->id);
+ok($id,$msg);
+ok ($testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does have the right to own tickets now. thank god.");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'OwnTicket', Object => $q);
+ok ($id,$msg);
+is($groups->Count, 3);
+
+my $RTxGroup = RT::Group->new($RT::SystemUser);
+($id, $msg) = $RTxGroup->CreateUserDefinedGroup( Name => 'RTxGroup', Description => "RTx extension group");
+ok ($id,$msg);
+
+my $RTxSysObj = {};
+bless $RTxSysObj, 'RTx::System';
+*RTx::System::Id = sub { 1; };
+*RTx::System::id = *RTx::System::Id;
+my $ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System', ObjectId => 1);
+ok ($id, "ACL for RTxSysObj created");
+
+my $RTxObj = {};
+bless $RTxObj, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 4; };
+*RTx::System::Record::id = *RTx::System::Record::Id;
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxSysObj);
+is($groups->Count, 1, "RTxGroupRight found for RTxSysObj");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj);
+is($groups->Count, 0, "RTxGroupRight not found for RTxObj");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj using EquivObjects");
+
+$ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
+ok ($id, "ACL for RTxObj created");
+
+my $RTxObj2 = {};
+bless $RTxObj2, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 5; };
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj2");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj2");
+
+
+
+=end testing
+
+
+=cut
+
+#XXX: should be generilized
+sub WithRight {
+ my $self = shift;
+ my %args = ( Right => undef,
+ Object => => undef,
+ IncludeSystemRights => 1,
+ IncludeSuperusers => undef,
+ IncludeSubgroupMembers => 0,
+ EquivObjects => [ ],
+ @_ );
+
+ my $from_role = $self->Clone;
+ $from_role->WithRoleRight( %args );
+
+ my $from_group = $self->Clone;
+ $from_group->WithGroupRight( %args );
+
+ #XXX: DIRTY HACK
+ use DBIx::SearchBuilder 1.50; #no version on ::Union :(
+ use DBIx::SearchBuilder::Union;
+ my $union = new DBIx::SearchBuilder::Union;
+ $union->add($from_role);
+ $union->add($from_group);
+ %$self = %$union;
+ bless $self, ref($union);
+
+ return;
+}
+
+#XXX: methods are active aliases to Users class to prevent code duplication
+# should be generalized
+sub _JoinGroups {
+ my $self = shift;
+ my %args = (@_);
+ return 'main' unless $args{'IncludeSubgroupMembers'};
+ return $self->RT::Users::_JoinGroups( %args );
+}
+sub _JoinGroupMembers {
+ my $self = shift;
+ my %args = (@_);
+ return 'main' unless $args{'IncludeSubgroupMembers'};
+ return $self->RT::Users::_JoinGroupMembers( %args );
+}
+sub _JoinGroupMembersForGroupRights {
+ my $self = shift;
+ my %args = (@_);
+ my $group_members = $self->_JoinGroupMembers( %args );
+ unless( $group_members eq 'main' ) {
+ return $self->RT::Users::_JoinGroupMembersForGroupRights( %args );
+ }
+ $self->Limit( ALIAS => $args{'ACLAlias'},
+ FIELD => 'PrincipalId',
+ VALUE => "main.id",
+ QUOTEVALUE => 0,
+ );
+}
+sub _JoinACL { return (shift)->RT::Users::_JoinACL( @_ ) }
+sub _RoleClauses { return (shift)->RT::Users::_RoleClauses( @_ ) }
+sub _WhoHaveRoleRightSplitted { return (shift)->RT::Users::_WhoHaveRoleRightSplitted( @_ ) }
+sub _GetEquivObjects { return (shift)->RT::Users::_GetEquivObjects( @_ ) }
+sub WithGroupRight { return (shift)->RT::Users::WhoHaveGroupRight( @_ ) }
+sub WithRoleRight { return (shift)->RT::Users::WhoHaveRoleRight( @_ ) }
+
+# {{{ sub LimitToEnabled
+
+=head2 LimitToEnabled
+
+Only find items that haven\'t been disabled
+
+=cut
+
+sub LimitToEnabled {
+ my $self = shift;
+
+ $self->Limit( ALIAS => $self->PrincipalsAlias,
+ FIELD => 'Disabled',
+ VALUE => '0',
+ OPERATOR => '=',
+ );
+}
+# }}}
+
+# {{{ sub LimitToDisabled
+
+=head2 LimitToDeleted
+
+Only find items that have been deleted.
+
+=cut
+
+sub LimitToDeleted {
+ my $self = shift;
+
+ $self->{'find_disabled_rows'} = 1;
+ $self->Limit( ALIAS => $self->PrincipalsAlias,
+ FIELD => 'Disabled',
+ OPERATOR => '=',
+ VALUE => 1,
+ );
+}
+# }}}
+
+# {{{ sub Next
+
+sub Next {
+ my $self = shift;
+
+ # Don't show groups which the user isn't allowed to see.
+
+ my $Group = $self->SUPER::Next();
+ if ((defined($Group)) and (ref($Group))) {
+ unless ($Group->CurrentUserHasRight('SeeGroup')) {
+ return $self->Next();
+ }
+
+ return $Group;
+ }
+ else {
+ return undef;
+ }
+}
+
+
+
+sub _DoSearch {
+ my $self = shift;
+
+ #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
+ unless($self->{'find_disabled_rows'}) {
+ $self->LimitToEnabled();
+ }
+
+ return($self->SUPER::_DoSearch(@_));
+
+}
+
+1;
+
diff --git a/rt/lib/RT/Handle.pm b/rt/lib/RT/Handle.pm
new file mode 100644
index 0000000..7ba5ee8
--- /dev/null
+++ b/rt/lib/RT/Handle.pm
@@ -0,0 +1,143 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Handle - RT's database handle
+
+=head1 SYNOPSIS
+
+ use RT::Handle;
+
+=head1 DESCRIPTION
+
+=begin testing
+
+ok(require RT::Handle);
+
+=end testing
+
+=head1 METHODS
+
+=cut
+
+package RT::Handle;
+
+use strict;
+use vars qw/@ISA/;
+
+eval "use DBIx::SearchBuilder::Handle::$RT::DatabaseType;
+\@ISA= qw(DBIx::SearchBuilder::Handle::$RT::DatabaseType);";
+
+if ($@) {
+ die "Unable to load DBIx::SearchBuilder database handle for '$RT::DatabaseType'.".
+ "\n".
+ "Perhaps you've picked an invalid database type or spelled it incorrectly.".
+ "\n". $@;
+}
+
+=head2 Connect
+
+Connects to RT's database handle.
+Takes nothing. Calls SUPER::Connect with the needed args
+
+=cut
+
+sub Connect {
+ my $self = shift;
+
+ if ($RT::DatabaseType eq 'Oracle') {
+ $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
+ $ENV{'NLS_NCHAR'} = "AL32UTF8";
+
+ }
+
+ $self->SUPER::Connect(
+ User => $RT::DatabaseUser,
+ Password => $RT::DatabasePassword,
+ );
+
+ $self->dbh->{LongReadLen} = $RT::MaxAttachmentSize;
+
+}
+
+=head2 BuildDSN
+
+Build the DSN for the RT database. doesn't take any parameters, draws all that
+from the config file.
+
+=cut
+
+use File::Spec;
+
+sub BuildDSN {
+ my $self = shift;
+# Unless the database port is a positive integer, we really don't want to pass it.
+$RT::DatabasePort = undef unless (defined $RT::DatabasePort && $RT::DatabasePort =~ /^(\d+)$/);
+$RT::DatabaseHost = undef unless (defined $RT::DatabaseHost && $RT::DatabaseHost ne '');
+$RT::DatabaseName = File::Spec->catfile($RT::VarPath, $RT::DatabaseName)
+ if ($RT::DatabaseType eq 'SQLite') and
+ not File::Spec->file_name_is_absolute($RT::DatabaseName);
+
+
+ $self->SUPER::BuildDSN(Host => $RT::DatabaseHost,
+ Database => $RT::DatabaseName,
+ Port => $RT::DatabasePort,
+ Driver => $RT::DatabaseType,
+ RequireSSL => $RT::DatabaseRequireSSL,
+ DisconnectHandleOnDestroy => 1
+ );
+
+
+}
+
+eval "require RT::Handle_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Vendor.pm});
+eval "require RT::Handle_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Local.pm});
+
+1;
diff --git a/rt/lib/RT/I18N.pm b/rt/lib/RT/I18N.pm
new file mode 100644
index 0000000..9d0b2b5
--- /dev/null
+++ b/rt/lib/RT/I18N.pm
@@ -0,0 +1,501 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+RT::I18N - a base class for localization of RT
+
+=cut
+
+package RT::I18N;
+
+use strict;
+use warnings;
+
+use Locale::Maketext 1.04;
+use Locale::Maketext::Lexicon 0.25;
+use base ('Locale::Maketext::Fuzzy');
+
+use Encode;
+use MIME::Entity;
+use MIME::Head;
+
+# I decree that this project's first language is English.
+
+our %Lexicon = (
+ 'TEST_STRING' => 'Concrete Mixer',
+
+ '__Content-Type' => 'text/plain; charset=utf-8',
+
+ '_AUTO' => 1,
+ # That means that lookup failures can't happen -- if we get as far
+ # as looking for something in this lexicon, and we don't find it,
+ # then automagically set $Lexicon{$key} = $key, before possibly
+ # compiling it.
+
+ # The exception is keys that start with "_" -- they aren't auto-makeable.
+
+);
+# End of lexicon.
+
+=head2 Init
+
+Initializes the lexicons used for localization.
+
+=begin testing
+
+use_ok (RT::I18N);
+ok(RT::I18N->Init);
+
+=end testing
+
+=cut
+
+sub Init {
+ require File::Glob;
+
+ # Load language-specific functions
+ foreach my $language ( File::Glob::bsd_glob(substr(__FILE__, 0, -3) . "/*.pm")) {
+ if ($language =~ /^([-\w\s.\/\\~:]+)$/) {
+ require $1;
+ }
+ else {
+ warn("$language is tainted. not loading");
+ }
+ }
+
+ my @lang = @RT::LexiconLanguages;
+ @lang = ('*') unless @lang;
+
+ # Acquire all .po files and iterate them into lexicons
+ Locale::Maketext::Lexicon->import({
+ _decode => 1, map {
+ $_ => [
+ Gettext => (substr(__FILE__, 0, -3) . "/$_.po"),
+ Gettext => "$RT::LocalLexiconPath/*/$_.po",
+ Gettext => "$RT::LocalLexiconPath/$_.po",
+ ],
+ } @lang
+ });
+
+ return 1;
+}
+
+=head2 encoding
+
+Returns the encoding of the current lexicon, as yanked out of __ContentType's "charset" field.
+If it can't find anything, it returns 'ISO-8859-1'
+
+=begin testing
+
+ok(my $chinese = RT::I18N->get_handle('zh_tw'));
+ok(UNIVERSAL::can($chinese, 'maketext'));
+ok($chinese->maketext('__Content-Type') =~ /utf-8/i, "Found the utf-8 charset for traditional chinese in the string ".$chinese->maketext('__Content-Type'));
+ok($chinese->encoding eq 'utf-8', "The encoding is 'utf-8' -".$chinese->encoding);
+
+ok(my $en = RT::I18N->get_handle('en'));
+ok(UNIVERSAL::can($en, 'maketext'));
+ok($en->encoding eq 'utf-8', "The encoding ".$en->encoding." is 'utf-8'");
+
+=end testing
+
+
+=cut
+
+
+sub encoding { 'utf-8' }
+
+# {{{ SetMIMEEntityToUTF8
+
+=head2 SetMIMEEntityToUTF8 $entity
+
+An utility function which will try to convert entity body into utf8.
+It's now a wrap-up of SetMIMEEntityToEncoding($entity, 'utf-8').
+
+=cut
+
+sub SetMIMEEntityToUTF8 {
+ RT::I18N::SetMIMEEntityToEncoding(shift, 'utf-8');
+}
+
+# }}}
+
+# {{{ IsTextualContentType
+
+=head2 IsTextualContentType $type
+
+An utility function that determines whether $type is I<textual>, meaning
+that it can sensibly be converted to Unicode text.
+
+Currently, it returns true iff $type matches this regular expression
+(case-insensitively):
+
+ ^(?:text/(?:plain|html)|message/rfc822)\b
+
+# }}}
+
+=cut
+
+sub IsTextualContentType {
+ my $type = shift;
+ ($type =~ m{^(?:text/(?:plain|html)|message/rfc822)\b}i) ? 1 : 0;
+}
+
+# {{{ SetMIMEEntityToEncoding
+
+=head2 SetMIMEEntityToEncoding $entity, $encoding
+
+An utility function which will try to convert entity body into specified
+charset encoding (encoded as octets, *not* unicode-strings). It will
+iterate all the entities in $entity, and try to convert each one into
+specified charset if whose Content-Type is 'text/plain'.
+
+This function doesn't return anything meaningful.
+
+=cut
+
+sub SetMIMEEntityToEncoding {
+ my ( $entity, $enc, $preserve_words ) = ( shift, shift, shift );
+
+ # do the same for parts first of all
+ SetMIMEEntityToEncoding( $_, $enc, $preserve_words ) foreach $entity->parts;
+
+ my $charset = _FindOrGuessCharset($entity) or return;
+ # one and only normalization
+ $charset = 'utf-8' if $charset =~ /^utf-?8$/i;
+ $enc = 'utf-8' if $enc =~ /^utf-?8$/i;
+
+ SetMIMEHeadToEncoding(
+ $entity->head,
+ _FindOrGuessCharset($entity, 1) => $enc,
+ $preserve_words
+ );
+
+ my $head = $entity->head;
+
+ # convert at least MIME word encoded attachment filename
+ foreach my $attr (qw(content-type.name content-disposition.filename)) {
+ if ( my $name = $head->mime_attr($attr) and !$preserve_words ) {
+ $head->mime_attr( $attr => DecodeMIMEWordsToUTF8($name) );
+ }
+ }
+
+ # If this is a textual entity, we'd need to preserve its original encoding
+ $head->add( "X-RT-Original-Encoding" => $charset )
+ if $head->mime_attr('content-type.charset') or IsTextualContentType($head->mime_type);
+
+ return unless IsTextualContentType($head->mime_type);
+
+ my $body = $entity->bodyhandle;
+
+ if ( $enc ne $charset && $body) {
+ my @lines = $body->as_lines or return;
+
+ # {{{ Convert the body
+ eval {
+ $RT::Logger->debug("Converting '$charset' to '$enc' for ". $head->mime_type . " - ". ($head->get('subject') || 'Subjectless message'));
+
+ # NOTE:: see the comments at the end of the sub.
+ Encode::_utf8_off( $lines[$_] ) foreach ( 0 .. $#lines );
+ Encode::from_to( $lines[$_], $charset => $enc ) for ( 0 .. $#lines );
+ };
+
+ if ($@) {
+ $RT::Logger->error( "Encoding error: " . $@ . " defaulting to ISO-8859-1 -> UTF-8" );
+ eval {
+ Encode::from_to( $lines[$_], 'iso-8859-1' => $enc ) foreach ( 0 .. $#lines );
+ };
+ if ($@) {
+ $RT::Logger->crit( "Totally failed to convert to utf-8: " . $@ . " I give up" );
+ }
+ }
+ # }}}
+
+ my $new_body = MIME::Body::InCore->new( \@lines );
+
+ # set up the new entity
+ $head->mime_attr( "content-type" => 'text/plain' )
+ unless ( $head->mime_attr("content-type") );
+ $head->mime_attr( "content-type.charset" => $enc );
+ $entity->bodyhandle($new_body);
+ }
+}
+
+# NOTES: Why Encode::_utf8_off before Encode::from_to
+#
+# All the strings in RT are utf-8 now. Quotes from Encode POD:
+#
+# [$length =] from_to($octets, FROM_ENC, TO_ENC [, CHECK])
+# ... The data in $octets must be encoded as octets and not as
+# characters in Perl's internal format. ...
+#
+# Not turning off the UTF-8 flag in the string will prevent the string
+# from conversion.
+
+# }}}
+
+# {{{ DecodeMIMEWordsToUTF8
+
+=head2 DecodeMIMEWordsToUTF8 $raw
+
+An utility method which mimics MIME::Words::decode_mimewords, but only
+limited functionality. This function returns an utf-8 string.
+
+It returns the decoded string, or the original string if it's not
+encoded. Since the subroutine converts specified string into utf-8
+charset, it should not alter a subject written in English.
+
+Why not use MIME::Words directly? Because it fails in RT when I
+tried. Maybe it's ok now.
+
+=cut
+
+sub DecodeMIMEWordsToUTF8 {
+ my $str = shift;
+ DecodeMIMEWordsToEncoding($str, 'utf-8');
+}
+
+sub DecodeMIMEWordsToEncoding {
+ my $str = shift;
+ my $enc = shift;
+
+ @_ = $str =~ m/(.*?)=\?([^?]+)\?([QqBb])\?([^?]+)\?=([^=]*)/gcs;
+ return ($str) unless (@_);
+
+ # add everything that hasn't matched to the end of the latest
+ # string in array this happen when we have 'key="=?encoded?="; key="plain"'
+ $_[-1] .= substr($str, pos $str);
+
+ $str = "";
+ while (@_) {
+ my ($prefix, $charset, $encoding, $enc_str, $trailing) =
+ (shift, shift, lc shift, shift, shift);
+
+ $trailing =~ s/\s?\t?$//; # Observed from Outlook Express
+
+ if ( $encoding eq 'q' ) {
+ use MIME::QuotedPrint;
+ $enc_str =~ tr/_/ /; # Observed from Outlook Express
+ $enc_str = decode_qp($enc_str);
+ } elsif ( $encoding eq 'b' ) {
+ use MIME::Base64;
+ $enc_str = decode_base64($enc_str);
+ } else {
+ $RT::Logger->warning("Incorrect encoding '$encoding' in '$str', "
+ ."only Q(uoted-printable) and B(ase64) are supported");
+ }
+
+ # now we have got a decoded subject, try to convert into the encoding
+ unless ($charset eq $enc) {
+ eval { Encode::from_to($enc_str, $charset, $enc) };
+ if ($@) {
+ $charset = _GuessCharset( $enc_str );
+ Encode::from_to($enc_str, $charset, $enc);
+ }
+ }
+
+ # XXX TODO: RT doesn't currently do the right thing with mime-encoded headers
+ # We _should_ be preserving them encoded until after parsing is completed and
+ # THEN undo the mime-encoding.
+ #
+ # This routine should be translating the existing mimeencoding to utf8 but leaving
+ # things encoded.
+ #
+ # It's legal for headers to contain mime-encoded commas and semicolons which
+ # should not be treated as address separators. (Encoding == quoting here)
+ #
+ # until this is fixed, we must escape any string containing a comma or semicolon
+ # this is only a bandaid
+
+ $enc_str = qq{"$enc_str"} if ($enc_str =~ /[,;]/);
+ $str .= $prefix . $enc_str . $trailing;
+ }
+
+ # We might have \n without trailing whitespace, which will result in
+ # invalid headers.
+ $str =~ s/\n//g;
+
+ return ($str)
+}
+
+# }}}
+
+# {{{ _FindOrGuessCharset
+
+=head2 _FindOrGuessCharset MIME::Entity, $head_only
+
+When handed a MIME::Entity will first attempt to read what charset the message is encoded in. Failing that, will use Encode::Guess to try to figure it out
+
+If $head_only is true, only guesses charset for head parts. This is because header's encoding (e.g. filename="...") may be different from that of body's.
+
+=cut
+
+sub _FindOrGuessCharset {
+ my $entity = shift;
+ my $head_only = shift;
+ my $head = $entity->head;
+
+ if ( my $charset = $head->mime_attr("content-type.charset") ) {
+ return $charset;
+ }
+
+ if ( !$head_only and $head->mime_type =~ m{^text/}) {
+ my $body = $entity->bodyhandle or return;
+ return _GuessCharset( $body->as_string );
+ }
+ else {
+ # potentially binary data -- don't guess the body
+ return _GuessCharset( $head->as_string );
+ }
+}
+
+# }}}
+
+# {{{ _GuessCharset
+
+=head2 _GuessCharset STRING
+
+use Encode::Guess to try to figure it out the string's encoding.
+
+=cut
+
+sub _GuessCharset {
+ my $fallback = 'iso-8859-1';
+ my $charset;
+
+ if ( @RT::EmailInputEncodings and eval { require Encode::Guess; 1 } ) {
+ Encode::Guess->set_suspects(@RT::EmailInputEncodings);
+ my $decoder = Encode::Guess->guess( $_[0] );
+
+ if ( defined($decoder) ) {
+ if ( ref $decoder ) {
+ $charset = $decoder->name;
+ $RT::Logger->debug("Guessed encoding: $charset");
+ return $charset;
+ }
+ elsif ($decoder =~ /(\S+ or .+)/) {
+ my %matched = map { $_ => 1 } split(/ or /, $1);
+ return 'utf-8' if $matched{'utf8'}; # one and only normalization
+
+ foreach my $suspect (@RT::EmailInputEncodings) {
+ next unless $matched{$suspect};
+ $RT::Logger->debug("Encode::Guess ambiguous ($decoder); using $suspect");
+ $charset = $suspect;
+ last;
+ }
+ }
+ else {
+ $RT::Logger->warning("Encode::Guess failed: $decoder; fallback to $fallback");
+ }
+ }
+ else {
+ $RT::Logger->warning("Encode::Guess failed: decoder is undefined; fallback to $fallback");
+ }
+ }
+ else {
+ $RT::Logger->warning("Cannot Encode::Guess; fallback to $fallback");
+ }
+
+ return($charset || $fallback);
+}
+
+# }}}
+
+# {{{ SetMIMEHeadToEncoding
+
+=head2 SetMIMEHeadToEncoding HEAD OLD_CHARSET NEW_CHARSET
+
+Converts a MIME Head from one encoding to another. This totally violates the RFC.
+We should never need this. But, Surprise!, MUAs are badly broken and do this kind of stuff
+all the time
+
+
+=cut
+
+sub SetMIMEHeadToEncoding {
+ my ( $head, $charset, $enc, $preserve_words ) = ( shift, shift, shift, shift );
+
+ $charset = 'utf-8' if $charset eq 'utf8';
+ $enc = 'utf-8' if $enc eq 'utf8';
+
+ return if $charset eq $enc and $preserve_words;
+
+ foreach my $tag ( $head->tags ) {
+ next unless $tag; # seen in wild: headers with no name
+ my @values = $head->get_all($tag);
+ $head->delete($tag);
+ foreach my $value (@values) {
+ if ( $charset ne $enc ) {
+
+ eval {
+ Encode::_utf8_off($value);
+ Encode::from_to( $value, $charset => $enc );
+ };
+ if ($@) {
+ $RT::Logger->error( "Encoding error: " . $@
+ . " defaulting to ISO-8859-1 -> UTF-8" );
+ eval { Encode::from_to( $value, 'iso-8859-1' => $enc ) };
+ if ($@) {
+ $RT::Logger->crit( "Totally failed to convert to utf-8: " . $@ . " I give up" );
+ }
+ }
+ }
+ $value = DecodeMIMEWordsToEncoding( $value, $enc ) unless $preserve_words;
+ $head->add( $tag, $value );
+ }
+ }
+
+}
+# }}}
+
+eval "require RT::I18N_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/I18N_Vendor.pm});
+eval "require RT::I18N_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/I18N_Local.pm});
+
+1; # End of module.
+
diff --git a/rt/lib/RT/I18N/cs.pm b/rt/lib/RT/I18N/cs.pm
new file mode 100644
index 0000000..9b96be1
--- /dev/null
+++ b/rt/lib/RT/I18N/cs.pm
@@ -0,0 +1,115 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::I18N::cs;
+
+# # CZECH TRANSLATORS COMMENTS see Locale::Maketext::TPJ13
+# Obecne parametry musi byt docela slozite (v pripade Slavistickych jazyku)
+# typu pocet, slovo, pad a rod
+#
+#pad 1., rod muzsky:
+#0 krecku
+#1 krecek
+#2..4 krecci
+#5.. krecku (nehodi se zde resit pravidlo mod 1,2,3,4 krom mod 11,12,13,14)
+#
+#0 kabatu
+#1 kabat
+#2..4 kabaty
+#5 kabatu
+#
+# => Vyplati se udelat quant s parametry typu pocet, slovo1, slovo2..4, slovo5 a slovo0
+#
+
+sub quant {
+ my($handle, $num, @forms) = @_;
+
+ return $num if @forms == 0; # what should this mean?
+ return $forms[3] if @forms > 3 and $num == 0; # special zeroth case
+
+ # Normal case:
+ # Note that the formatting of $num is preserved.
+ #return( $handle->numf($num) . ' ' . $handle->numerate($num, @forms) );
+ return( $handle->numerate($num, @forms) );
+ # Most human languages put the number phrase before the qualified phrase.
+}
+
+
+sub numerate {
+ # return this lexical item in a form appropriate to this number
+ my($handle, $num, @forms) = @_;
+ my $s = ($num == 1);
+
+ return '' unless @forms;
+ return
+ $s ? $forms[0] :
+ ( $num > 1 && $num < 5 ) ? $forms[1] :
+ $forms[2];
+}
+
+#--------------------------------------------------------------------------
+
+sub numf {
+ my($handle, $num) = @_[0,1];
+ if($num < 10_000_000_000 and $num > -10_000_000_000 and $num == int($num)) {
+ $num += 0; # Just use normal integer stringification.
+ # Specifically, don't let %G turn ten million into 1E+007
+ } else {
+ $num = CORE::sprintf("%G", $num);
+ # "CORE::" is there to avoid confusion with the above sub sprintf.
+ }
+ while( $num =~ s/^([-+]?\d+)(\d{3})/$1,$2/s ) {1} # right from perlfaq5
+ # The initial \d+ gobbles as many digits as it can, and then we
+ # backtrack so it un-eats the rightmost three, and then we
+ # insert the comma there.
+
+ $num =~ tr<.,><,.> if ref($handle) and $handle->{'numf_comma'};
+ # This is just a lame hack instead of using Number::Format
+ return $num;
+}
+
+1;
diff --git a/rt/lib/RT/I18N/cs.po b/rt/lib/RT/I18N/cs.po
new file mode 100644
index 0000000..ebc8af5
--- /dev/null
+++ b/rt/lib/RT/I18N/cs.po
@@ -0,0 +1,6201 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2002-05-02 11:36+0800\n"
+"PO-Revision-Date: 2007-06-07 22:25+0200\n"
+"Last-Translator: Daniel Kastner <kastner@mediso-art.cz>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr "%1 smazán."
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr "%1 přejmenován na %2."
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr "%1 uložen."
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "#%1"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr "$1"
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr "$prefix %1"
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 #%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %3.%2.%7 %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 přidán"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "- %1 %2"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 změněno na %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 smazán"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 se vzorem %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 tento požadavek\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) vytvořil %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (Nezměněn)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "%1. až %2. zobrazený"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - argument k předání %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Výstupní stav jde do STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr "%1 - Zadejte id vzoru, který chcete použít"
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr "%1 - Zadejte, zda chcete použít transakci 'first' nebo 'last'"
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Jaký akÄní modul chcete použít"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Jaký podmínkový modul chcete použít"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Jaký vyhledávací modul chcete použít"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr "%1 - Zadejte typ transakce, kterou chcete použít"
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 Copyright 1996-%3 %4."
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScripAction %1 nahrána"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 přidáno jako hodnota pro %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "%1 aliasy vyžadují k Äinnosti TicketId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "%1 aliasy vyžadují k Äinnosti TicketId (odesílatel %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 vypadá jako lokální objekt, ale není v databázi"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 uživatelem %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 změněno z %2 na %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "kopie %1"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 nemůže být nastaveno na %2."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 nemůže zaÄít transakci (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 nemůže nastavit stav na vyřešen. Databáze RT může být nekonzistentní."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 vytvořen"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 smazán"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "%1 nejdůležitějších požadavků, které vlastním"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "%1 nejdůležitějších požadavků, které žádám..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 je nástroj zpracující požadavky z vnějšího plánovacího nástroje jako je cron"
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 již není %2 této fronty."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 již není %2 tohoto požadavku."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 již není hodnotou uživatelské položky %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 není platným identifikátorem fronty."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 %quant(%1,minuta,minuty,minut,minut)"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 nejnovějších nevlastněných požadavků"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 nezobrazeno"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 objekty"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "práva %1"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 provedeno\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "typ %1 neznámý pro $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "typ %1 neznámý pro %2"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 vyÅ™eší vÅ¡echny Äleny skupiny vyÅ™eÅ¡eného požadavku."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 odloží [místní] BÃZI, je-li závislá [Äi Älenem] na spjatém požadavku."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "Uložené dotazy patřící %1"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: neudána příloha"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1 B"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1 kB"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "%1 je neplatnou hodnotou pro stav"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "%1 je neznámá akce."
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Zatrhněte pro smazání scripu)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Zatrhněte pro smazání)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Zatrhněte pro zakázání upozorňování uvedených příjemců)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Zatrhněte pro povolení upozorňování uvedených příjemců)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Zadejte identifikátory Äi URL požadavku, oddÄ›lené mezerami)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Pro prázdné pole se použije %1)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Žádné uživatelské položky)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Žádní Älenové)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Žádné scripy)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Žádné vzory)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Žádné)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(ZaÅ¡le skrytou kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. <b>Neovlivňuje</b> příjemce budoucích aktualizací.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(ZaÅ¡le skrytou kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. <b>Neovlivňuje</b> příjemce budoucích aktualizací.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(ZaÅ¡le skrytou kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. <strong>Neovlivňuje</strong> příjemce budoucích aktualizací.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(ZaÅ¡le kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. Tito lidé <b>budou</b> dostávat budoucí aktualizace.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(ZaÅ¡le kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. Tito lidé <strong>budou</strong> dostávat budoucí aktualizace.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(ZaÅ¡le kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. <b>NemÄ›ní</b> příjemce budoucích aktualizací"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(ZaÅ¡le kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. <b>Neovlivňuje</b> příjemce budoucích aktualizací.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(ZaÅ¡le kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. <strong>Neovlivňuje</strong> příjemce budoucích aktualizací.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(ZaÅ¡le kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. Tito lidé <b>budou</b> dostávat budoucí aktualizace.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(ZaÅ¡le kopii této aktualizace Äárkami oddÄ›lenému seznamu e-mail adres. Tito lidé <strong>budou</strong> dostávat budoucí aktualizace.)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Použijte tyto položky, pokud jste v podmínce nebo akci vybrali hodnotu 'Uživatelem definované')"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(Nebude odeslán email)"
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(prázdná)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(žádné jméno nebylo vypsáno)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(bez předmětu)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(bez hodnoty)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(bez hodnot)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(jen jeden požadavek)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(oÄekávájící schválení)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(probíhá jiná Sbírka)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(jiné oÄekávající požadavky)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(povinné)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(nepojmenováno)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(yyyy/mm/dd)"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr "-"
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr "--parametrem požadavku může být pouze 'first' nebo 'last'"
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$field%>"
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Nový požadavek v\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Nový požadavek v\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Prázdný vzor"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Heslo nebylo nastaveno, uživatel se nebude moci přihlásit."
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE nenalezeno"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE mohou být pouze vytvářeny nebo rušeny."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "AND"
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Přerušeno k zamezení nežádoucích změn požadavku.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "O mnÄ›"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Řízení přístupu"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Akce"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Akce %1 nenalezena"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Akce provedena."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Akce provedena.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr "Akce je povinným parametrem"
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Akce připravena..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Přidat"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Přidat AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Přidat Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Přidat sloupce"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Přidat podmínku"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Přidat další soubory"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Přidat žadatele"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Přidat hodnotu"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Přidat nový globální scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Přidat scrip k této frontě"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Přidat scrip platný ve všech frontách"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "PÅ™idat dodateÄnou podmínku"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Přidat a vyhledat"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "PÅ™idat komentáře Äi odpovÄ›di k vybraným požadavkům"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "PÅ™idat Äleny"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Přidat nové pozorovatele"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Přidat tyto podmínky k dotazu"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "Přidat hodnoty"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Přidat, smazat nebo upravit hodnoty uživatelských položek pro objekty"
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "Přidat další stav"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Uživatel přidán do této fronty jako %1"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Uživatel přidán k tomuto požadavku jako %1"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adresa1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adresa2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Administrativní komentář"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Administrativní korespondence"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Správa front"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Správa uživatelů"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Správa/Globální konfigurace"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Správa/Skupiny"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Správa/Fronta/Základní údaje"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "Spravovat všechny osobní skupiny"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "AdminComment"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "AdminCorrespondence"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "Spravovat uživatelskou položku"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "Spravovat uživatelské položky"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "Spravovat skupinu"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "Spravovat Älenství ve skupinách"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "Spravovat vlastní osobní skupiny"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "Spravovat frontu"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "Spravovat uživatele"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Administrativní Cc"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "PokroÄilé"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "PokroÄilé vyhledávání"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Po"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Stáří"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Operátor"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Všechna schvalování prošla"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Všechny uživatelské položky"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "VÅ¡echny fronty"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Vždy posílá zprávu žadatelům nezávisle na odesílateli"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "And/Or"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Vztahuje se na"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Provést"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Provést změny"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Schvalování"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Schválení #%1: $2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Schválení #%1: Poznámky neuloženy kvůli systémové chybě"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Schválení #%1: Poznámky uloženy"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Detaily schválení"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Schvalování prošlo"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Schvalování odmítnuto"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Schvalovací diagram"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Schválit"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Poznámky schvalovatele: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "dub"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "VzestupnÄ›"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "VzestupnÄ›"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Přidělit a odebrat uživatelské položky"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "Přidělit uživatelské položky"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Přiložit"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Připojit soubor"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Připojený soubor"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Příloha '%1' nemůže být nahrána"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Příloha vytvořena"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Jméno souboru přílohy"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Přílohy"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Atribut smazán"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "srp"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "AuthSystem"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Automatická odpovÄ›Ä"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Automaticky odpověz žadatelům"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "Automatická odpovÄ›Ä Å¾adatelům"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "Dostupné"
+
+#: NOT FOUND IN SOURCE
+msgid "Available Columns"
+msgstr "Dostupné položky"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Chybná PGP signatura: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Chybný identifikátor přílohy. Nelze nalézt přílohu'%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Chybná data v %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Chybné Äíslo transakce u přílohy. %1 má být %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Základní údaje"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Nezapomeňte uložit své změny"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Před"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "ZaÄátek schvalování"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Best Practical Solutions, LLC corporate logo"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Prázdný"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "TuÄné"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Uložitelný odkaz"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Zkrácené hlaviÄky"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "Hromadná úprava"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Hromadná úprava požadavků"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Nelze měnit systémové uživatele"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Může tento uživatel vidět tuto frontu"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Uživatelské položce nelze přidat hodnotu beze jména"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "Nelze nalézt třídu collection pro '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Nelze nalézt uložený dotaz ke zpracování"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Požadavek nelze svázat se sebou samým"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Nelze slouÄit do slouÄeného požadavku. To by se vám nemÄ›lo nikdy stát."
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr "Nelze uložit %1"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Tento dotaz nelze uložit"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Nelze zadat zároveň zdroj i cíl"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Nelze vytvořit uživatele: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "Kategorie"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Změna hesla"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "OznaÄ vÅ¡e"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Zašrtnutím odstraníte"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Zatrhněte k odebrání práva"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Potomci"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Vybrat datum"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Město"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "OdznaÄ vÅ¡e"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "Zavřít okno"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Uzavřen"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "Uzavřené požadavky"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Uzavřené požadavky"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Combobox: Vybrat nebo zadat více hodnot"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Combobox: Vybrat nebo zadat jednu hodnotu"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Combobox: Vybrat nebo zadat nejvýše %1 %quant(%1,hodnotu,hodnoty,hodnot)"
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Neznámý příkaz!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Komentovat"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Adresa pro komentáře"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Komentář nezaznamenán"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Komentovat požadavky"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "Komentovat požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Poznámky"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Komentář (Neposílá se žadatelům)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Komentář (nepošle se žadatelům)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Poznámky o %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Poznámky o tomto uživateli"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Komentáře přidány"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Commit v zárodku"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Omezení překladu"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Podmínka"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "Podmínka je povinným parametrem"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Podmínky splněny..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Podmínka nenalezena"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Správa"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Potvrzení"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "Kontaktní informaÄní systém"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Datum kontaktu '%1' nemůže být rozpoznáno"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Obsah"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Content-Type"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Kopírovat"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Korespondence"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Adresa pro korespondenci"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Korespondence zaznamenána"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Korespondence nebyla zaznamenána"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "K požadavku nelze přidat novou hodnotu uživatelské položky. "
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Nelze přidat novou hodnotu uživatelské položky."
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Nelze přidat novou hodnotu uživatelské položky. %1"
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Nelze změnit vlastníka. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Nelze vytvořit uživatelskou položku"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Nelze vytvořit uživatelskou položku: %1"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Nelze vytvořit skupinu"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Nelze vytvořit vzor: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Nelze vytvořit požadavek. Nenastavena fronta"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Nelze vytvořit uživatele"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Nelze nalézt požadavek s identifikátorem %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Nelze nalézt skupinu %1."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Tohoto uživatele nelze nalézt nebo vytvořit"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Nelze naléze tohoto uživatele"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Nelze nalézt uživatele %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Nelze nahrát uživatelskou položku %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Nelze naÄíst skupinu"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "Nelze nahrát objekt pro %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Nelze nahrát atribut dotazu"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Tento uživatel nemůže být %1 této fronty"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Tento uživatel nemůže být %1 tohoto požadavku"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Tento uživatel nemůže být odstraněn jako %1 této fronty"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Tento uživatel nemůže být odstraněn jako %1 tohoto požadavku"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Nelze nastavit uživatelské informace"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "Nelze přidat přílohu"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Do skupiny nelze pÅ™idat Älena"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Nelze vytvořit transakci: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Nelze zjistit co dělat s gpg odpovědí\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Skupinu nelze nalézt\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Nemohu nalézt řádek"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Tohoto uživatele nelze nalézt"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Tuto hodnotu nelze nalézt"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Uživatele nelze nalézt\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Z databáze uživatelů nelze naÄíst %1.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "Nelze naÄíst třídu %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Nelze naÄíst uživatelskou položku %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "KonfiguraÄní soubor RT '%1'nelze naÄíst %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Scripy nelze naÄíst."
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr "Nelze naÄíst kopii požadavku #%1."
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Skupinu %1 nelze naÄíst"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Vazbu nelze naÄíst"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Nelze nahrát objekt %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Frontu nelze naÄíst"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Frontu %1 nelze naÄíst"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Scrip nelze naÄíst"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr "Nelze naÄíst scrip #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Vzor nelze naÄíst"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Uživatele (%1) nelze naÄíst"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Požadavek '%1' nelze naÄíst"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr "Nelze přeložit '%1' do URI."
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "ZemÄ›"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Vytvořit"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Vytvořit požadavky"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Vytvořit uživatelskou položku"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Vytvoření uživatelské položky pro frontu %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Vytvoření uživatelské položky pro všechny front"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Vytvořit novou uživatelskou položku"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Vytvořit nový globální scrip"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Vytvořit novou skupinu"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Vytvořit novou vlastní skupinu"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Vytvořit novou frontu"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Vytvořit nový scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Vytvořit nový vzor"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Vytvoření nového požadavku"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Vytvořit nového uživatele"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Vytvořit frontu"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Vytvořit frontu nazvanou"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Vytvořit požadavek"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Vytvořit scrips pro frontu %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Vytvořit vzor"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Vytvořit požadavek"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Vytvářet požadavky podle tohoto vzoru scripu"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Vytvořit požadavek"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Vytvářet požadavky v této frontě"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Vytvářet, mazat a měnit uživatelem definované položky"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Vytvářet, mazat a měnit fronty"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Vytvářet, mazat a mÄ›nit Äleny osobních skupin vÅ¡ech uživatelů"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Vytvářet, mazat a mÄ›nit Äleny osobních skupin"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Vytvářet, mazat a měnit uživatele"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "Vytvořit uložený dotaz"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "Vytvořit požadavek"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Vytvořeno"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Uživatelská položka %1 vytvořena"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Vytvořené za období"
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Vzor %1 vytvořen"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "Vytvořené požadavky za období, seskupené dle stavu"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Tvůrce"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Aktuální relace"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Aktuální scripy"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Aktuální Älenové"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Aktuální práva"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Aktuální dotaz"
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Aktuální vyhledávací podmínky"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Aktuální pozorovatelé"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Uživatelská položka #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Uživatelské položky"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Uživatelské položky pro %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Čistící kód uživatelské akce"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Přípravný kód uživatelské akce"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Uživatelská podmínka"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Uživatelská položka %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Uživatelská položka %1 má hodnotu."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Uživatelská položka %1 nemá hodnotu."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Uživatelská položka %1 nenalezena"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr "Uživatelská položka '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Uživatelská položka smazána"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Uživatelská položka nenalezena"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Hodnota %1 nemůže být nalezena v uživatelské položce %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Hodnota uživatelské položky změněna z %1 na %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Hodnota uživatelské položky nemůže být smazána"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Hodnota uživatelské položky nemůže být nalezena"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Hodnota uživatelské položky smazána"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "Uživatelská položka"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "Upravit"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Datumy"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "pro"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Implicitní vzor automatické odpovědi"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Implicitní fronta"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Implicitní žadatel"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Implicitní vzor administrativního komentáře"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Implicitní vzor administrativní korespondence"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Implicitní korespondenÄní vzor"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Implicitní transakÄní vzor"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Výchozí hodnota: %1/%2 změněno z %3 na %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Delegovat práva"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Delegovat specifická práva, která vám byla poskytnuta."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "Delegovat práva"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Pověření"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Smazat"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Smazat vzor"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Nelze smazat: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Smazat vybrané scripy"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Smazat požadavky"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr "Smazat hodnoty"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "Smazat požadavek"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Dotaz smazán"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Smazání tohoto objektu mohlo poruÅ¡it referenÄní integritu"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Smazání tohoto objektu by mohlo poruÅ¡it referenÄní integritu"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Smazání tohoto objektu by mohlo naruÅ¡it referenÄní integritu"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Zamítnout"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Je rekvizitou pro"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Závislosti: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Přidána závislost %1"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Závislost %1 smazána"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Přidána závislost na %1"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Závislost na %1 smazána"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Závisející na"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "SestupnÄ›"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "SestupnÄ›"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Popište případ níže"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Popis"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Podrobnosti"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Zobrazit"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Zobrazit přístupová práva"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Zobrazované položky"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Zobrazovat vzory scripů pro tuto frontu"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Zobrazovat scripy pro tuto frontu"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Režim zobrazení"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Zobraz uložené dotazy pro tuto skupinu"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Šířeno pod verzí 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> GNU GPL.</a>"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Dělat cokoli a všechno"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "NeobÄerstvovat tuto stránku."
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Stáhnout"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Stáhnout jako soubor oddělený tabelátory"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Termín dokonÄení"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Datum termínu dokonÄení '%1' nemůže být rozpoznán"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "CHYBA: Nelze naÄíst požadavek '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Upravit"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "Upravit uživatelské položky"
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Upravit uživatelské položky pro %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Upravit uživatelské položky pro všechny skupiny"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Upravit uživatelské položky pro všechny uživatele"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Upravit uživatelské položky pro požadavky ve všech frontách"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Upravit vazby"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Upravit dotaz"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Úprava dotazu"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Upravit vzory pro frontu %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Upravit uložené dotazy pro tuto skupinu"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Úprava systémových vzorů"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Upravit vzory pro %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "Upravovat uložené dotazy"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Úprava konfigurace pro frontu %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Úprava konfigurace pro uživatele %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Úprava uživatelské položky %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Úprava Älenství ve skupinÄ› %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Úprava Älenství ve vlastní skupinÄ› %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Úprava vzoru %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Zdroj Äi cíl musí být zadán"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Email"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Emailová adresa je použita"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "Emailová adresa"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "Kódování emailu"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Povolena (zrušením zatrhnutí zablokujete tuto uživatelskou položky)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Povolena (zrušením zatrhnutí zablokujete tuto skupinu)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Povoleno (zrušení zatrhnutí zablokuje tuto frontu)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "Povolené uživatelské položky"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Povolené fronty"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Povolen stav %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "Povolen stav: 1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Vyplnit více hodnot"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Zadejte objekty Äi URI k provázání. Více položek oddÄ›lujte mezerami."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Vyplnit jednu hodnotu"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Zadejte fronty Äi URI k provázání. Více položek oddÄ›lujte mezerami."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Zadejte požadavky Äi URI k provázání. Více položek oddÄ›lujte mezerami."
+
+#: NOT FOUND IN SOURCE
+msgid "Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces."
+msgstr "Zadejte požadavky Äi URI se nimiž požadavky svázat. OddÄ›lte více položek mezerami."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Zadejte nejvýše %1 %quant(%1,hodnotu,hodnoty,hodnot)"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Chyba"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Chyba v parametrech do Queue->AddWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Chyba v parametrech do Queue->DeleteWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Chyba v parametrech do Ticket->AddWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Chyba v parametrech do Ticket->DeleteWatcher"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Eskalovat požadavky"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Odhadovaný"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Kdokoli"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Příklad:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "Identifikátor externí autentizace"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "Identifikátor externího kontaktu"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Doplňkové údaje"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Nelze vytvořit atribut dotazu"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Nenalezena pseudoskupina uživatelů 'Privilegovaný'."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Nenalezena pseudoskupina uživatelů 'Neprivilegovaný'"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Nelze naÄíst modul %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "Nelze naÄíst objekt pro %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "úno"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Název souboru"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Vyplnit více textových oblastí"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Vyplnit více wiki textových oblastí"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Vyplnit jednu textovou oblast"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Vyplnit jednu wiki textovou oblast"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "Do této položky zadejte URL."
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Vyplnit nejvýše %1 %quant(%1,textovou oblast,textové oblasti,textových oblastí)"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Vyplnit nejvýše %1 wiki %quant(%1,textovou oblast,textové oblasti,textových oblastí)"
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Kon"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Koncová priorita"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "Koncová priorita"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Najít skupinu jejíž"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Najít skupiny jejichž"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Najít nové/otevřené požadavky"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Najít osoby, jejichž"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Nalézt požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "ZávereÄné schválení"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "První"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "První stránka"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Vynutit změnu"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Formát"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Nalezen%quant(%1,,y,o) %numf(%1) %quant(%1,požadavek,požadavky,požadavků)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Nalezen objekt"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "Kontaktní údaje ve volné podobě"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "Volná forma vícenásobně"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "Volná forma jedinkrát"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "pá"
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Celé hlaviÄky"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Vzít vzor ze souboru"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Předáno %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Globální"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Globální uživatelské položky"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Globální konfigurace uživatelských položek"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr "Globální portlet %1 uložen."
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Globální vzor: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Provést"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Spusť!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Správný PGP podpis od %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Přejít na stránku"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Přejít na požadavek"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Skupina"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Skupina %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Práva skupiny"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Skupina již má Älena"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Skupina nemůže být založena: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Skupina vytvořena"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Skupina nemá takového Älena"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Skupina nenalezena"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Skupina nenalezena.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Skupina nezadána.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Skupiny"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Skupiny nemohou být svými Äleny"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Skupiny splňující vyhledávací podmínku"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Skupiny, do nichž uživatel patří"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Ahoj!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Ahoj, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historie"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "Historie skupiny %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "Historie uživatele %1"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Telefon domů"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Domovská stránka"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "Hodin"
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Mám %quant(%1,míchaÄku,míchaÄky,míchaÄek)"
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "I have [quant,_1,concrete mixer]."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Jsem ztracen"
+
+#msgstr "Mám [quant,_1,MíchaÄku na beton,MíchaÄky na beton,MíchaÄek na beton]."
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Identifikátor"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identita"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Odmítni původce a zruš stávající schválení, bylo-li zamítnuto schválení"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Nebyl-li zadán Žadatel, vytvářejte požadavky jako tento žadatel."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Nebyla-li zadána fronta, vytvářejte požadavky v této frontě"
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Pokud by byl tento nástroj setgid, místní uživatel by jej mohl použit k získaní administrativního přístupu k RT"
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Pokud jste změnili cokoli nahoře, nezapomeňte"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Neplatná hodnota pro %1"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Neměnná položka"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Zahrnout do výpisu blokované uživatelské položky"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Zahrnout blokované skupiny ve výpisu."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Zahrnout blokované fronty do výpisu."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Zahrnout blokované uživatele do vyhledávání."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Zahrnout stránku"
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Neúplný dotaz"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Neúplný dotaz"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "PoÄáteÄní priorita"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "PoÄáteÄní priorita"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Chyba na vstupu"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr "Vstup musí odpovídat %1"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Vnitřní chyba"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Vnitřní chyba: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Neplatný typ skupiny"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Neplatné právo"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Neplatná data"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Neplatný vlastník. Použije se 'nobody'."
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "Neplatný vzor: %1"
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Neplatná fronta"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Neplatné právo"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Neplatná hodnota pro %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Neplatná hodnota pro uživatelskou položku"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Neplatná hodnota pro stav"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Je nesmírně důležité, aby neprivilegovaní uživatelé nemohli spustit tento nástroj."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Pro spuÅ¡tÄ›ní tohoto nástroje se doporuÄuje založení neprivilegovaného UNIXového uživatele se správným skupinovým Älenstvím a přístupem do RT."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Používá několik parametrů:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Kurzíva"
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Položky oÄekávající mé schválení"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "led"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "PÅ™idat se Äi odebrat z této skupiny"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Äec"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Maxi"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Äen"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "KlíÄové slovo"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Jazyk"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Jazyk"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Velké"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Poslední"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Poslední kontakt"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Naposledy kontaktován"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Naposledy upozorněn"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Naposledy aktualizován"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "Naposledy aktualizoval(a)"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Zbývá"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Umožnit tomuto uživateli přístup k RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Umožnit dávat tomuto uživateli práva"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Vlastník omezen na %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Fronta omezena na %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Odkaz"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Vazba již existuje"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Vazba nemůže být vytvořena"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Vazba vytvořena (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Vazba zrušena (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Vazba nenalezena"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Svázat požadavek #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "Odkazuje na"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr "Spojování. Přístup nepovolen"
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Vazby"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "NaÄíst"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "NaÄíst uložený dotaz:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "NaÄíst uložený dotaz"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "NaÄtené perlovské moduly"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "NaÄtený dotaz %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Umístění"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Logovací adresář %1 nenalezen nebo do ňeho nemůže být zapisováno.\\ RT nemůže běžet."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Přihlášen jako %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Přihlásit"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Odhlásit"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Nevhodný typ vyhledávání"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Nastavit vlastníka"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Nastavit stav"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Nastavit datum termínu dokonÄení"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Nastavit datum vyřešení"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Nastavit datum, kdy zaÄal"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Nastavit datum, kdy zaÄne"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Nastavit datum posledního kontaktu"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Nastavit prioritu"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Nastavit frontu"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Nastavit předmět"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Zviditelnit tuto skupinu uživateli"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Správa uživatelských položek a jejich hodnot"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Správa skupin a Älenství v nich"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Správa vlastností a konfigurace platné pro všechny fronty"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Správa front a jim příslušných vlastností"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Správa uživatelů a hesel"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "bře"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "kvÄ›"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Člen %1 přidán"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Člen %1 odebrán"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Člen přidán"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Člen odebrán"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Člen neodebrán"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "ÄŒlen"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Členové"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "Členství v %1 přidáno"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "Členství v %1 zrušeno"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Členství"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "Členství uživatele %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "SlouÄení úspěšné"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "SlouÄení se nepodaÅ™ilo. Nelze nastavit EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "SlouÄení se nepodaÅ™ilo. Nelze nastavit Status"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "SlouÄit do"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "SlouÄen do %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Zpráva"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "Tělo zprávy nezobrazeno, protože je příliš velké nebo nejde o holý text."
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Zpráva nemůže být zaznamenána"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Zpráva zaznamenána"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Zprávy o tomto požadavku NEBUDOU zaslány na..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Minut"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "Nevhodné závorky"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Chybí primární klíÄ?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Mobilní telefon"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Mobilní telefon"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Upravovat seznam přístupových práv"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Upravit uživatelskou položku %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Upravit uživatelské položky, platné pro %1, pro všechny %2"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Upravit uživatelské položky platné pro všechny %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Úprava uživatelských položek pro všechny fronty"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Úprava skupinových práv"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Upravit Äleny"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Upravit práva"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Upravovat vzory scripů této fronty"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Upravovat scripy této fronty"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Upravovat vzor %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Upravit práva uživatelů"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Upravovat uživatelskou položku pro frontu %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Upravovat uživatelskou položku pro všechny fronty"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Upravovat scrip pro frontu %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Upravovat scrip platný pro všechny fronty"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Upravovat přiřazené objekty k %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Upravit datumy pro #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Úprava datumů pro požadavek # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Úprava globálních uživatelských položek"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Úprava globálních skupinových práv"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Úprava globálních skupinových práv."
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Úprava globálních scripů"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Úprava globálních uživatelských práv"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Úprava globálních uživatelských práv."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Upravovat metadata skupiny nebo smazat skupinu"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Úprava skupinových práv pro uživatelskou položku %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Úprava skupinových práv pro %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Úprava skupinových práv pro frontu %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Upravovat seznam Älenů pro tuto skupinu"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Upravovat vlastní RT úÄet"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Úprava uživatelů fronty %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Úprava uživatelů souvisejících s požadavkem #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Úprava scripů pro frontu %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Úprava scripů platných ve všech frontách"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Úprava vzoru %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Upravit vzory pro všechny fronty"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "Úprava výchozího vzhledu stránky \"Přehled RT\""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Úprava skupiny %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Upravovat pozorovatele fronty"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Úprava uživatele %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Úprava požadavku # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Úprava požadavku #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Upravovat požadavky"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Upravit práva uživatelů k uživatelské položce %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Úprava práv uživatelů ke skupině %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Úprava práv uživatelů k frontě %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Úprava pozorovatelů fronty '%1'"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "Upravovat seznam přístupových práv"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "Upravovat uživatelskou položku"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "Upravovat Älenství ve skupinÄ›"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "Upravovat pozorovale fronty"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "Upravovat scripy"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "Upravovat sebe"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "Upravovat vzor"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "Upravovat požadavek"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "po"
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Více o %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Dát níže"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Dát výše"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Vícenásobná"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Nutno zadat atribut 'Jméno'"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "Mé požadavky ve stavu %1"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Mnou schválené"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "Můj den"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Mnou schválené"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Mé uložené dotazy"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Jméno"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Jméno je použito"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Je třeba schválení správcem systému"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Nikdy"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Nové"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nové vazby"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nové heslo"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nová probíhající schválení"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Nový dotaz"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Nové vyhledávání"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Vytvořit uživatelskou položku"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Založit skupinu"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nové heslo"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Oznámení o novém hesle zasláno"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Vytvoření fronty"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Nová upomínka:"
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Nový požadavek"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nová práva"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Vytvoření scripu"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Nové vyhledání"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Vytvořit vzor"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Nový požadavek"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Nový požadavek neexistuje"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Vytvořit uživatele"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Nový uživatel jména"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nový pozorovatel"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Nové nastavení okna"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Další"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Další stránka"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Další stránka"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Přezdívka"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Přezdívka"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Nedefinována žádná třída"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Žádná uživatelská položka"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Nedefinována žádná uživatelská položka"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Nedefinována žádná skupina"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Prázdný dotaz"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Nedefinována žádná fronta"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Žádný uživatel RT nenalezen. Prosím poraÄte se se správcem RT.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Žádný vzor"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Neudán požadavek. Přerušuje se požadavek "
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "Neudán požadavek. Přerušují se úpravy požadavku\\n\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "bez akce"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Neudán sloupec"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Příkaz nenalezen\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Poznámky k tomuto uživateli neudány"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Žádná připojená korespondence"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Pro %1 není popis"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Neudána skupina"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Žádná skupina neodpovídá vyhledávací podmínce."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Zpráva nepřipojena"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Heslo nenastaveno"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Nedostatek práv k vytváření front"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Nedostatek práv k vytváření požadavků ve frontě '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Nedostatek práv k vytváření uživatelů"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Nedostatek práv k zobrazení tohoto požadavku"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "Nedostatek práv k uložení dotazů pro celý systém"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Nedostatek práv k zobrazení aktualizace požadavku"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Nezadán uživatel"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Nevybráni uživatelé."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Nenalezeny žádné fronty odpovídající vyhledávací podmínce."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Práva nenalezena"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Nepřidělena žádná práva."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Dotaz nenaÄten"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Bez vyhledání nelze pracovat."
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Žádný předmět"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Neudán identifikátor požadavku"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Neudán typ transakce"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Nenalezeni žádní uživatelé odpovídající vyhledávací podmínce."
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Nenalezen platný uživatel RT. OvladaÄ RT CVS uvolnÄ›n. Prosím poraÄte se se svým správcem RT.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Žádná z hodnot nanastavena na _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Nikdo"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Neexistující položka?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr "Nenastaven"
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Nepřihlášen"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Nepřihlášen."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Nenastaven"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Zatím neimplementováno."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Zatím neimplementováno..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Poznámky"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Upozornění nemůže být zasláno"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Zaslat všem AdminCc"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Zaslat všem AdminCc jako komentář"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Upozornit všechny Cc"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Upozornit všechny Cc jako komentář"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Zaslat ostatním příjemcům"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Zaslat ostatním příjemcům jako komentář"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Zaslat vlastníkovi"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Zaslat vlastníkovi jako komentář"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Upozornění vlastníka o zamítnutí požadavku"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Upozornění vlastníka, že požadavek byl všemi schválen"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Upozornění vlastníka, že požadavek byl někým schválen"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Zaslat vlastníkům a vÅ¡em AdminCc nové případy oÄekávající jejich schválení"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Zaslat žadatelům"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Zaslat žadatelům a všem Cc"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Zaslat žadatelům a všem Cc jako komentář"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Zaslat žadatelům, všem Cc a všem AdminCc"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Zaslat žadatelům, vÄem Cc a vÄem AdminCc jako komentář"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "lis"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "OR"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objekt nemůže být vytvořen"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Objekt nemůže být smazán"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objekt vytvořen"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objekt smazán"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Objektu typu %1 nemohou být přiděleny uživatelské položky"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Nevhodný typ objektu"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "říj"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Off-line"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Úpravy off-line"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Off-line naÄtení"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Dne"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr "Dne %1, %2 napsal(a):"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Při komentáři"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Při korespondenci"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Při založení"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Při změně vlastníka"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Při změně priority"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Při změně fronty"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Při vyřešení"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Při změně stavu"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Při transakci"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Zobrazit jen schvalování pro požadavky založené po %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Zobrazit jen schvalování pro požadavky založení před %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Zobrazit jen uživatelské položky pro:"
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Otevřené"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "Otevřené požadavky"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Otevřít"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Otevřené požadavky"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Otevřené požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Otevřít požadavky (ze seznamu) v novém okně"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Otevřít požadavky (ze seznamu) v jiném okně"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Otevřít požadavky při korespondenci"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Volby"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Řadit podle"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Řazení a třídění"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organizace"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Původní požadavek: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Odchozí zpráva o komentáři zaznamenána"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Odchozí zpráva zaznamenána"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Časem se priorita posouvá k"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Vlastnit požadavky"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "Vlastnit požadavek"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Vlastník"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Vlastník změněn z %1 na %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Vlastník nemůže být nastaven."
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Vlastník nuceně změněn z %1 na %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "Vlastník"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Stránka %1 z %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Pager"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Číslo pageru"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "RodiÄe"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Heslo"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "PÅ™ipomínaÄ hesel"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Heslo změněno"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "Heslo musí být dlouhé nejméně %1 %quant(%1,znak,znaky,znaků)"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Heslo je nastaveno"
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Heslo je příliš krátké"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Heslo: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Heslo: Přístup odmítnut"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Hesla nesouhlasí."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Hesla nesouhlasí. Vaše heslo nebylo změněno"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Uživatelé"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Provedení uživatelem definované akce"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Konfigurace Perlu"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Přístup nepovolen"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "Přístup nepovolen"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "Přístupy nepovoleny"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Osobní skupiny"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Vlastní skupiny"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Vlastní skupiny:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Čísla telefonů"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Zábor místa"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Nastavení"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr "Nastavení %1 uživatele %2."
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr "Nastavení pro %1 uloženo."
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Nastavení"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Předchozí"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Předchozí stránka"
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Předchozí stránka"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Uživatel %1 nenalezen."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Priorita"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Priorita zaÄíná na"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Vlastní:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilegovaný"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Privilegovaný stav: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Privilegovaní uživatelé"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudoskupina pro vnitřní použití"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "Dotaz"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Tvůrce dotazu"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "Dotaz:"
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Fronta"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Fronta %1 nenalezena"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Fronta '%1' nenalezena\\n"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Název fronty"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Scripy fronty"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Fronta již existuje"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Fronta nemůže být vytvořena"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Fronta nemůže být naÄtena."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Fronta vytvořena"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Není zadána fronta."
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Fronta nenalezena"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Fronty"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Fronty mnou spravované"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Fronty v nichž jsem AdminCc"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Rychlé hledání"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Rychlé založení požadavku"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 pro %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 od <a href=\"http://bestpractical.com\">Best Practival Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Správa RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "AutentizaÄní chyba RT."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT Bounce: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "KonfiguraÄní chyba RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "Kritická chyba RT. Zpráva nezaznamenána!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "Chyba RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT přijal poštu (%1) od sebe samého."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT Samoobsluha / Uzavřené požadavky"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "Proměnné RT"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "Přehled RT"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr "Přehled RT pro uživatele %1"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT může vÄlenit obsah jiné webové stránky pÅ™i zobrazení této uživatelské položky."
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT může z hodnoty této uživatelské položky vytvořit odkaz na jinou stránku."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT vás nemůže autentizovat"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT nemůže nalézt žadatele přes hledání v externí databázi"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT nemůže nalézt frontu: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT nemůže uložit vaše sezení."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT nemůže ověřit tento PGP podpis. \\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT pro %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT pro %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT zpracoval vaše příkazy"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT je &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Šířeno pod <a href=\"http://www.gnu.org/copyleft/gpl.html\">verzí 2 GNU General Public License.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT považuje tuto zprávu za bounce"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "Vše ostatní, co zadáte, bude RT hledat v předmětech požadavků."
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT zpracuje tuto zprávu tak, jako by byla nepodepsaná.\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT nahradí <tt>__id__</tt> a <tt>__CustomField__</tt> identifikátorem záznamu a hodnotou uživatelské položky (v uvedeném pořadí)"
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "Emailový příkazový režim RT vyžaduje PGP autentizaci. Nepodepsal jste vaši zprávu nebo váš podpis nemůže být ověřen."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "SkuteÄné jméno"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "SkuteÄné jméno"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "Přidán odkaz z %1"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "Smazán odkaz z %1"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "Přidán odkaz na %1"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "Smazán odkaz na %1"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Je odkazem z"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Odkazuje na"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Zjemnit"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Zjemnit vyhledání"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Obnovit tuto stránku %quant(%1,každou,každé,každých) %numf(%1) %quant(%1,minutu,minuty,minut)."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "Upomínka '%1' přidána"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "Upomínka '%1' ukonÄena"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "Upomínka '%1' znovuotevřena"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "Upomínka požadavku #%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "Upomínky"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "Upomínky pro požadavek #%1"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Odstranit AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Odstranit Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Odstranit žadatele"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Odpovědět"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Adresa pro odpovÄ›Ä"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "OdpovÄ›Ä Å¾adatelům"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Odpovědět na požadavky"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "Odpovídat na požadavky"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Sestavy"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Žadatel"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Emailová adresa žadatele"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Žadatel(é)"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Žadatelé"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Požadavky mají být vyřešeny do"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Povinný parametr '%1' nezadán"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Vymazat"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr "Obnovit výchozí"
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Bydliště"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Vyřešit"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Vyřešení požadavku #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Vyřešen"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Vyřešené vlastníkem"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Vyřešené za období"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Vyřešené požadavky za období, seskupené dle vlastníka"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Vyřešené požadavky, seskupené dle vlastníka"
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "OdpovÄ›Ä Å¾adatelům"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Výsledky"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Výsledků na stránku"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Zopakujte heslo"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Vrátit"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Nenalezeno právo %1 pro %2 %3 v mezích %4 (%5)"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Právo delegováno"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Právo přidáno"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Právo naÄteno"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Právo nemůže být odebráno"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Právo nenalezeno"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Právo nenaÄteno."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Právo odebráno"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Práva"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Práva pro %1 nemohou být přidělena"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Práva nemohou být %1 odebrána"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Pravidla"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "Kořenový schvalovatel"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Řádků na oddíl"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Řádků na stránku"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "so"
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Uložit"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Uložit změny"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Uložit nastavení"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Nezapomeňte uložit změny - "
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "Uložený dotaz %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Uložené dotazy"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip vytvořen"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Položky scripu"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip smazán"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scripy"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Scripy fro %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scripy platné ve všech frontách"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Vyhledat"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Podmínky vyhledávání"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Nastavení hledání"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Chyba pÅ™i naÄítání atributu dotazu"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Vyhledávání schvalování"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "Hledat požadavky"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "Pro vyhledání požadavků zadejte buÄ <strong>identifikátor</strong> Äíslem nebo <strong>frontu</strong> jménem nebo vlastníka <strong>jménem uživatele</strong> nebo žadatele <strong>emailovou adresou</strong>. VÅ¡e ostatní, co zadáte, bude RT hledat v textech požadavků a jejich přílohách."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Volby pro hledání"
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "Výsledky hledání seskupit podle %1"
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Aktualizovat dotaz: %1"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "Fulltextové vyhledávání nad všemi požadavky může trvat dlouho, ale pokud to potřebujete, můžete hledat libovolné slovo v celé historii požadavku zadáním <b>fulltext:<i>slovo</i></b>."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "ZabezpeÄní:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "Viz také:"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Vidět uživatelské položky"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Vidět přesnou odchozí zprávu a její příjemce"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Vidět soukromé komentáře požadavku"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Vidět sumárně požadavek"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "Vidět uživatelskou položku"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "Vidět skupinu"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "Vidět frontu"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Výběr uživatelské položky"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Výběr skupiny"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Výběr fronty"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Výběr fronty pro váš nový požadavek"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Výběr uživatele"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Vybrat uživatelskou položku"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Vybrat uživatelské položky pro všechny skupiny uživatelů"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Vybrat uživatelské položky pro všechny uživatele"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Vybrat uživatelské položky pro požadavky ve všech frontách"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Vybrat uživatelské položky pro transakce s požadavky ve všech frontách"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Vybrat skupinu"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Vybrat více hodnot"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Vybrat jednu hodnotu"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Výběr fronty"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Výběr front, které budou zobrazeny na stránce \"Přehled RT\""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Výběr scripu"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Vybrat vzor"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Výběr nejvýše %1 %quant(%1,hodnoty,hodnot,hodnot)"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Výběr uživatele"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "Výběr vícenásobný"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "VýbÄ›t jedineÄný"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Vybrané uživatelské položky"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Vybrané objekty"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Výběr upraven. Prosím uložte si své změny"
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Samoobsluha"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Zaslat e-mail všem pozorovatelům"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Zaslat e-mail všem pozorovatelům jako \"komentář\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Zaslat e-mail žadatelům a všem Cc"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Zaslat e-mail žadatelům a všem Ccs jako komentář"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Posílá zprávu žadatelům"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Posílá e-mail všem přesně vyjmenovaným Cc a Bcc"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Posílá e-mail všem Cc"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Posílá e-mail všem Cc jako komentář"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Posílá e-mail všem administrativním Cc"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Posílá e-mail všem administrativním Cc jako komentář"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Posílá e-mail vlastníkovi"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "zář"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Zobrazit"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Zobrazit schválení"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Zobrazit sloupce"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Zobrazit výsledky"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Zobrazit schválené požadavky"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Zobrazit základní údaje"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Zobrazit odepřené požadavky"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Zobrazit podrobnosti"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Zobrazit trvající požadavky"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Zobrazit požadavky Äekající na jejich schválení"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Zobrazovat soukromé komentáře požadavku"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Zobrazovat výsledky požadavku"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "Zobrazovat seznam přístupových práv"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "Zobrazit záložku Správa"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "Zobrazit odchozí e-mail"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "Zobrazit uložené dotazy"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "Zobrazit scripy"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "Zobrazit vzor"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "Zobrazit požadavek"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "Zobrazit komentáře požadavku"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Být žadatelem Äi Cc požadavku nebo fronty"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Být AdminCc požadavku nebo fronty"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Podpis"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "Příhlášen jako %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Jednoduché vyhledávání"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Jednoduchá"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Velikost"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "PÅ™eskoÄit menu"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Malé"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "NÄ›které prohlížeÄe mohou nahrát obsah pouze ze stejné domény jako je váš RT server."
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Pořadí"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Třídící klíÄ"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Třídit výsledky dle"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "Třídící pořadí"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "Fáze"
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Odložené"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Úvodní stránka"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "ZapoÄato"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Datum zapoÄetí '%1' nemůže být rozpoznáno"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "ZaÄíná"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "ZaÄíná"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Datum zaÄínání '%1' nemůže být rozpoznáno"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Stát"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Stav"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Změna stavu"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Stav změněn z %1 na %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "Změna stavu"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Převzít"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Převzít požadavky"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "Převzít požadavek"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Převzato od %1"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Styl"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Předmět"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Předmět změněn na %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Odeslat"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Potvrdit model zpracování"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Úspěšné"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "ne"
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "Super uživatel"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Systém"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Systémová konfigurace"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Systémová chyba"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "Systémová chyba: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Systémové nástroje"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Systémová chyba. Právo nedelegováno."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Systémová chyba. Právo nepřiděleno."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Systémové skupiny"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "Skupina systémovýh pravidel pro vnitřní použití"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "MíchaÄka na beton"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Vzít"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Vzít požadavky"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "Vzít požadavek"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Vzal"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Vzor"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Vzor #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Vzor smazán"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr "Vzor je povinným parametrem"
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Vzor nenalezen"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Vzor nenalezen\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Vzor rozpoznán"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "Chyba při rozpoznávání vzoru"
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Vzory"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Vzory pro %1\\n"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Toto je již aktuální hodnota"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Toto není hodnota pro tuto uživatelskou položku"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Toto je shodná hodnota"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Tento uživatel již toto práva má"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Tento uživatel je již v této frontě %1"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Tento uživatel je již u tohoto požadavku %1"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Tento uživatel není v této frontě %1"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Tento uživatel není u tohoto požadavku %1"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Tato fronta neexistuje"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Tento požadavek má nevyřešené závislosti"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Tento uživatel již má toto právo"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Tento uživatel již tento požadavek vlastní"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Tento uživatel neexistuje"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Tento uživatel je již privilegován"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Tento uživatel je již neprivilegován"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Uživatel je nyní privilegován"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Uživatel je nyní neprivilegován"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "V této frontě nemůže tento uživatel vlastnit požadavky"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Toto není Äíselný identifikátor"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Základní údaje"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "Cc požadavku"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "Administrativní Cc požadavku"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Komentář byl zaznamenán"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Následující příkaz najde všechny aktivní požadavky ve frontě 'general' a nastaví jejich prioritu na 99, pokud nebyly tknuty poslední 4 hodiny:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "Následující příkazy nebyly zpracovány\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Nová hodnota nastavena."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Vlastník požadavku"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Žadatel požadavku"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Tyto komentáře nejsou běžně viditelné uživateli"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Tato uživatelská položka se nevztahuje k tomuto objektu"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Tato funkce je dostupná jen správcům systému"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Tato zpráva BUDE poslána na..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Tento požadavek %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Tento nástroj umožňuje uživateli spustit libovolné perl moduly z RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Tato transakce vypadá, že nemá obsah"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "%1 nejdůležitější%quant(%1, požadavek,požadavky,ch požadavků) tohoto uživatele"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "25 nejdůležitějších požadavků tohoto uživatele"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Ät"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Požadavek # %1 %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Požadavek #%1 Maxi aktualizace: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Požadavek #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "Požadavek %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Požadavek %1 vytvořen ve frontě '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Požadavek %1 naÄten\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Požadavek %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Uživatelské položky požadavků"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Historie požadavku # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Identifikátor požadavku"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Požadavek vyřešen"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Transakce s požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Příloha požadavku"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Obsah požadavku"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Content type požadavku"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Požadaven nemůže být vytvořen pro vnitřní chybu"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Požadavek vytvořen"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Nezdařilo se vytvoření požadavku"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Požadavek smazán"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Id požadavku nenalezeno"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Metadata požadavku"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Požadavek nenalezen"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Stav požadavku změněn"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Pozorovatelé požadavku"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "TicketSQL vyhledávací modul"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Požadavky %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Požadavky %1 dle %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "Požadavky vytvořené po"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "Požadavky vytvořené před"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Požadavky z %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "Požadavky vyřešené po"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "Požadavky vyřešené před"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Požadavky, které záleží na tomto schválení:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "PÅ™edpokládaný Äas"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Zbývající Äas"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Čas práce"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Zbývající Äas"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Čas k zobrazení"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Čas práce"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "Čas práce"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Nadpis"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Vytvořit diff tohoto commitu:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "Vytvořit diff tohoto commitu:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "K získání informací o podpoÅ™e, tréninku, zákaznických úpravách Äi licencování kontaktujte prosím %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Poslední kontakt"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Nástroje"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "Celkem"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transakce"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transakce %1 vymazána"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transakce vytvořena"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Uživatelské položky transakcí"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Bez udání id požadavku nelze volat Transaction->Create"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Bez udání typu objektu a id nelze volat Transaction->Create"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transakce jsou neměnné"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Pokus o smazání práva: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "út"
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Typ"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Neimplementováno"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unixový login"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Unixové uživatelské jméno"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Neznámé kódování obsahu %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Neznámé pole: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "NeomezenÄ›"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Nepojmenovaný dotaz"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Neprivilegovaný"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Nevybrané uživatelské položky"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Nevybrané objekty"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Vrácen"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Aktualizace"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Aktualizovat všechny"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Identifikátor aktualizace"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Aktualizovat požadavek"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Typ aktualizace"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Aktualizovat spoleÄnÄ› vÅ¡echny tyty požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Aktualizovat email"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Aktualizovat hromadně požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Aktualizovat jméno"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Aktualizace nezaznamenána"
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Aktualizovat vybrané požadavky"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Aktualizace podpisu"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Aktualizace požadavku"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Aktualizace požadavku # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Aktualizace požadavku #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Aktualizace požadavku #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Typ aktualizace nebyl ani korespondence ani komentář."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Aktualizováno"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Odeslat"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Odeslat více souborů"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Odeslat více obrázků"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Odeslat jeden soubor"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Odeslat jeden obrázek"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Odeslat nejvýše %1 %quant(%1,soubor,soubory,souborů)"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Odeslat nejvýše %1 %quant(%1,obrázek,obrázky,obrázků)"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "NaÄíst vaÅ¡e zmÄ›ny"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Další administrativní nástroje RT"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Uživatel %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Heslo uživatele %1: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "Uživatel '%1' nemůže být nalezen"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Uživatelem definované"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Uživatelem definované podmínky a akce"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "Identifikátor uživatele"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Identifikátor uživatele"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Práva uživatele"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Uživatel nemůže být vytvořen: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Uživatel vytvořen"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Uživatelem definované skupiny"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Uživatel naÄten"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Uživatel upozorněn"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Uživatelský pohled"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Uživatelem definované skupiny"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Uživatelské jméno"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Uživatelé"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Uživatelé odpovídající vyhledávací podmínce"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr "Používám transakci #%1..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Platný dotaz"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Validace"
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "Hodnota fronty"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Hodnoty"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Být pozorovatelem"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "Být AdminCc pozorovatelem"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Pozorovatelé"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "Kódování WWW"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "st"
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "Dnes jsem udělal"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Přidat korespondenci k původnímu požadavku, pokud byl požadavek schválen všemi"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Přidat korespondenci k původnímu požadavku, pokud byl požadavek kýmkoli schválen"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Když je požadavek vytvořen"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Upozornit vlastníka a vÅ¡echny AdminCc, jejichž schválení se oÄekává, pÅ™i vytvoÅ™ení schvalovaného požadavku"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Stane-li se cokoli"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Je-li vyřešen požadavek"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Změní-li se vlastník požadavku"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Při změně priority požadavku"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Změní-li se fronta požadavku"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Změní-li se stav požadavku"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Splní-li se uživatelská podmínka"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Přijde-li komentář"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Přijde-li korespondence"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Zaměstnání"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Pracovat off-line"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Telefon do zaměstnání"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Odpracováno"
+
+#: NOT FOUND IN SOURCE
+msgid "XXX CHANGEME You are not an authorized user"
+msgstr "XXX ZMĚNIT Nejste autorizovaný uživatel"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Požadavek již vlastníte"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Nejste autorizovaný uživatel"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "Můžeti si také upravit předvolený dotaz"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Můžete přidělit pouze požadavky, které jsou vaše nebo nejsou vlastněny"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr "Můžete vzít pouze požadavky, které nikdo nevlastní"
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "Nemáte právo k zobrazení tohoto požadavku.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Nalezl jste %1 požadavků ve frontě %2"
+
+#??? quant
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Byl jste odhlášen od RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "V této frontě nemáte práva vytvářet požadavky."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "V této frontě nemůžete vytvářet požadavky."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Jste vítáni k dalšímu přihlášení"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "%quant(%1,Váš %1 požadavek,Vaše %1 požadavky,Vašich %1 požadavků)"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "Váš správce RT chybně nastavil poštovní aliasy, které volají RT"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Váš požadavek byl schválen uživatelem %1. Další schválení mohou být jeÅ¡tÄ› oÄekávána."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Váš požadavek byl schválen."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Váš požadavek byl odmítnut"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Váš požadavek byl odmítnut."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "VaÅ¡e uživatelské jméno Äi heslo je nesprávné"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "PSČ"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "umožnit vytváření uložených dotazů"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "umožnit naÄítání uložených dotazů"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "jak je dovoleno %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "graf"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "uzavřen"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "obsahuje"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "obsah"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "content-type"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "korespondence (zřejmě) neposlána"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "korespondence poslána"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "dnů"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "smazat"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "smazán"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "neodpovídá"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "neobsahuje"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "je rovno"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "chyba: nelze přesunout dolů"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "chyba: nelze přesunout doleva"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "chyba: nelze přesunout nahorů"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "chyba: není co smazat"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "chyba: není co přesunout"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "chyba: není co přepnout"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "název souboru"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "větší než"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "skupina '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "seskupit podle %1"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "hodin"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "Identifikátor"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "je"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "není"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "menší než"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "odpovídá"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minut"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "úpravy\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "měsíců"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "nový"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "bez jména"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "bez hodnoty"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "žádný"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "není rovno"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "otevřený"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "vlastní skupina '%1' pro uživatele '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "fronta %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "zamítnutý"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "vyřešený"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sek"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "zobrazit záložku Správa"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "tabulka"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "odložený"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr "styl: %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "poÄet řádků"
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "systém %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "systémová skupina '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "volající komponenta neudala důvod"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "požadavek #%1 %2"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "nepopsaná skupina %1"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "nepopsaná skupina %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "uživatel %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "týdnů"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "se vzorem %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "roků"
+
diff --git a/rt/lib/RT/I18N/da.po b/rt/lib/RT/I18N/da.po
new file mode 100644
index 0000000..4f24ea9
--- /dev/null
+++ b/rt/lib/RT/I18N/da.po
@@ -0,0 +1,7074 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2007-03-16 13:02+0100\n"
+"Last-Translator: Heidi Senderovitz\n"
+"Language-Team: rt-devel <rt-devel@lists.fsck.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. ($self->{CurrentSearch}{Object}->Description)
+#: html/Widgets/SavedSearch:70
+msgid " %1 deleted."
+msgstr " %1 slettet."
+
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+#: html/Widgets/SavedSearch:47
+msgid " %1 renamed to %2."
+msgstr " %1 omdøbt til %2."
+
+#. ($args->{Description})
+#: html/Widgets/SavedSearch:60
+msgid " %1 saved."
+msgstr " %1 gemt."
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "#%1"
+
+#. ($TicketObj->Id, $TicketObj->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($Ticket->id, $Ticket->Subject)
+#: html/Approvals/Elements/Approve:48
+#: html/Approvals/Elements/ShowDependency:71
+#: html/SelfService/Display.html:46
+#: html/Ticket/Display.html:47
+#: html/Ticket/Display.html:51
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:104
+msgid "$1"
+msgstr "$1"
+
+#. ($label)
+#: lib/RT/Record.pm:940
+msgid "$prefix %1"
+msgstr "$præfiks %1"
+
+#. ($self->ObjectType, $self->Object->Id)
+#: lib/RT/URI/fsck_com_rt.pm:256
+msgid "%1 #%2"
+msgstr "%1 #%2"
+
+#. ($s, $time_unit)
+#: lib/RT/Date.pm:365
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+#: lib/RT/Date.pm:401
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%2 %1 %3 %4:%5:%6 %7"
+
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+#: lib/RT/Record.pm:1685
+#: lib/RT/Transaction_Overlay.pm:647
+#: lib/RT/Transaction_Overlay.pm:690
+msgid "%1 %2 added"
+msgstr "%1 %2 tilføjet"
+
+#. ($s, $time_unit)
+#: lib/RT/Date.pm:362
+msgid "%1 %2 ago"
+msgstr "%1 %2 siden"
+
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+#: lib/RT/Record.pm:1692
+#: lib/RT/Transaction_Overlay.pm:654
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 ændret til %3"
+
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+#: lib/RT/Record.pm:1689
+#: lib/RT/Transaction_Overlay.pm:650
+#: lib/RT/Transaction_Overlay.pm:696
+msgid "%1 %2 deleted"
+msgstr "%1 %2 slettet"
+
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+#: html/Admin/Elements/EditScrips:65
+#: html/Admin/Elements/ListGlobalScrips:63
+#: html/Ticket/Elements/PreviewScrips:103
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 med skabelon %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 denne sag\\n"
+
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+#: html/Ticket/Elements/ShowAttachments:72
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) af %3"
+
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+#. (loc($DefaultStatus))
+#: html/SelfService/Update.html:60
+#: html/Ticket/Elements/EditBasics:108
+#: html/Ticket/Update.html:61
+#: html/Ticket/Update.html:63
+#: html/Tools/MyDay.html:66
+msgid "%1 (Unchanged)"
+msgstr "%1 (Uændret)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "%1 - %2 vist"
+
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+#: bin/rt-crontool:237
+#: bin/rt-crontool:244
+#: bin/rt-crontool:250
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - En parameter til afsendelse til %2"
+
+#. ("--verbose")
+#: bin/rt-crontool:262
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Send statusopdateringer til STDOUT"
+
+#. ("--template-id")
+#: bin/rt-crontool:253
+msgid "%1 - Specify id of the template you want to use"
+msgstr "%1 - Angiv ID på den skabelon, du vil bruge"
+
+#. ("--transaction")
+#: bin/rt-crontool:256
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr "%1 - Angiv om du vil bruge enten 'første' eller 'sidste' transaktion"
+
+#. ("--action")
+#: bin/rt-crontool:247
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Angiv det handlingsmodul, du vil bruge"
+
+#. ("--condition")
+#: bin/rt-crontool:241
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Angiv det betingelsesmodul, du vil bruge"
+
+#. ("--search")
+#: bin/rt-crontool:234
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Angiv det søgemodul, du vil bruge"
+
+#. ("--transaction-type")
+#: bin/rt-crontool:259
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr "%1 - Angiv typen på den transaktion, du vil bruge"
+
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+#: html/Elements/Footer:56
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 Copyright 1996-%3 %4."
+
+#. ($self->Id)
+#: lib/RT/ScripAction_Overlay.pm:150
+msgid "%1 ScripAction loaded"
+msgstr "%1 Scrip-handling indlæst"
+
+#. ($args{'Value'}, $cf->Name)
+#: lib/RT/Record.pm:1722
+msgid "%1 added as a value for %2"
+msgstr "%1 tilføjet som en værdi til %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "et %1 alias skal tilknyttes et sagsnummer"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "et %1 alias skal tilknyttes et sagsnummer (fra %2) %3"
+
+#. ($args{'Base'})
+#. ($args{'Target'})
+#: lib/RT/Link_Overlay.pm:144
+#: lib/RT/Link_Overlay.pm:151
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 er tilsyneladende et lokalt objekt, men eksisterer ikke i databasen"
+
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#: html/Ticket/Elements/ShowDates:73
+#: lib/RT/Transaction_Overlay.pm:531
+msgid "%1 by %2"
+msgstr "%1 af %2"
+
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+#: lib/RT/Transaction_Overlay.pm:788
+#: lib/RT/Transaction_Overlay.pm:797
+#: lib/RT/Transaction_Overlay.pm:800
+msgid "%1 changed from %2 to %3"
+msgstr "%1 ændret fra %2 til %3"
+
+#. ($Description)
+#: html/Search/Build.html:213
+msgid "%1 copy"
+msgstr "%1 kopi"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 kunne ikke sættes til %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 kunne ikke igangsætte en handling (%2)\\n"
+
+#. ($self)
+#: lib/RT/Ticket_Overlay.pm:2787
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 kunne ikke sætte status til afsluttet. Der er muligvis inkonsekvens i RT-databasen."
+
+#. ($obj_type)
+#: lib/RT/Transaction_Overlay.pm:571
+msgid "%1 created"
+msgstr "%1 oprettet"
+
+#. ($obj_type)
+#: lib/RT/Transaction_Overlay.pm:576
+msgid "%1 deleted"
+msgstr "%1 slettet"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "Mine %1 højest prioriterede sager"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "Mine %1 højest prioriterede sager..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "De %1 højest prioriterede sager, jeg har rekvireret"
+
+#. ($0)
+#: bin/rt-crontool:229
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 er et værktøj, der arbejder med sager fra et bestemt planlægningsværktøj som f.eks. cron."
+
+#. ($principal->Object->Name, $args{'Type'})
+#: lib/RT/Queue_Overlay.pm:863
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 er ikke længere en %2 til denne kø."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 er ikke længere en %2 til denne sag."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 er ikke længere en værdi til ekstrafelt %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 er ikke et lovligt kø-ID."
+
+#. ($minutes)
+#: html/Ticket/Elements/ShowTime:47
+#: html/Ticket/Elements/ShowTime:49
+msgid "%1 min"
+msgstr "%1 min."
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 nyeste sager uden ejer"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 ikke vist"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 objekter"
+
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+#: html/User/Elements/DelegateRights:97
+msgid "%1 rights"
+msgstr "%1 rettigheder"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 lykkedes"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 type ukendt for $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 type ukendt for %2"
+
+#. (ref $self)
+#: lib/RT/Action/ResolveMembers.pm:63
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 vil løse alle medlemmer af en løst gruppesag."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 vil sætte en [lokal] BASE i bero, hvis den er afhængig [eller medlem] af en tilknyttet sag."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1's %2 objekter"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1's %2's %3 objekter"
+
+#. ($Object->Name)
+#. ($object->Name)
+#: html/Search/Elements/SearchPrivacy:52
+#: html/Search/Elements/SelectSearchObject:55
+#: html/Search/Elements/SelectSearchesForObjects:57
+msgid "%1's saved searches"
+msgstr "%1's gemte søgninger"
+
+#. ($self)
+#: lib/RT/Transaction_Overlay.pm:481
+msgid "%1: no attachment specified"
+msgstr "%1: ingen vedhæftet fil er angivet"
+
+#. ($size)
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+msgid "%1b"
+msgstr "%1b"
+
+#. (int( $size / 102.4 ) / 10)
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+msgid "%1k"
+msgstr "%1k"
+
+#. (sprintf("%.1f",$minutes / 60))
+#: html/Ticket/Elements/ShowTime:49
+msgid "%quant(%1,hour)"
+msgstr "%quant(%1,time)"
+
+#. ($args{'Status'})
+#: lib/RT/Ticket_Overlay.pm:1142
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' er ikke en gyldig statusværdi"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' er ikke en anerkendt handling. "
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Markér for at slette scrip)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50
+#: html/Admin/Elements/EditQueueWatchers:50
+#: html/Admin/Elements/EditScrips:56
+#: html/Admin/Elements/EditTemplates:57
+#: html/Admin/Groups/Members.html:73
+#: html/Elements/EditLinks:54
+#: html/Ticket/Elements/EditPeople:67
+#: html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Markér for at slette)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Markér for at fravælge besked til de angive modtagere)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Markér for at tilvælge besked til de angivne modtagere)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Indtast sagsnummer eller URL'er, adskilt af mellemrum)"
+
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+#: html/Admin/Queues/Modify.html:75
+#: html/Admin/Queues/Modify.html:81
+msgid "(If left blank, will default to %1)"
+msgstr "(Hvis intet angives, vil det som standard være %1)"
+
+#: html/Admin/Elements/EditCustomFields:74
+#: html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Ingen ekstrafelter)"
+
+#: html/Admin/Groups/Members.html:71
+#: html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Ingen medlemmer)"
+
+#: html/Admin/Elements/EditScrips:53
+#: html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Ingen scrips)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Ingen skabeloner)"
+
+#: html/Admin/Elements/PickCustomFields:47
+#: html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Ingen)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Sender en blind kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Ændrer <b>ikke</b>, hvem der vil modtage fremtidige opdateringer)."
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Sender en blind kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Ændrer <b>ikke</b>, hvem der vil modtage fremtidige opdateringer)."
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Sender en blind kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Ændrer <strong>ikke</strong> hvem der vil modtage fremtidige opdateringer)."
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Sender en kopi af denne opdatering til en kommasepareret liste af administrative e-mail-adresser. Disse personer <b>vil</b> modtage fremtidige opdateringer)."
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Sender en kopi af denne opdatering til en kommasepareret liste af administrative e-mail-adresser. Disse personer <strong>vil</strong> modtage fremtidige opdateringer)."
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Sender en kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Ændrer <b>IKKE</b>, hvem der vil modtage fremtidige opdateringer)."
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Sender en kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Ændrer <b>IKKE</b>, hvem der vil modtage fremtidige opdateringer)."
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Sender en kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Ændrer <strong>ikke</strong> hvem der vil modtage fremtidige opdateringer)."
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Sender en kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Disse personer <b>vil</b> modtage fremtidige opdateringer)."
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Sender en kopi af denne opdatering til en kommasepareret liste af e-mail-adresser. Disse personer <strong>vil</strong> modtage fremtidige opdateringer)."
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Brug disse felter, når du vælger 'brugerdefineret' som betingelse eller handling)"
+
+#: html/Ticket/Elements/EditWatchers:60
+#: html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(E-mail vil ikke blive sendt)"
+
+#: html/Admin/Groups/index.html:57
+#: html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(tom)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(intet navn angivet)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(intet emne)"
+
+#: html/Admin/Elements/SelectRights:72
+#: html/Elements/EditCustomFieldSelect:69
+#: html/Elements/SelectCustomFieldValue:51
+#: html/Elements/ShowCustomFields:54
+#: html/Search/Chart:56
+#: html/Search/Elements/Chart:76
+#: lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(ingen værdi)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(ingen værdier)"
+
+#: html/Elements/EditLinks:132
+#: html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(kun én sag)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(afventer godkendelse)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(afventer anden gruppe)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(afventer andre sager)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(obligatorisk)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(uden titel)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(åååå/mm/dd)"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr "-"
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr "--transaktionsparameter kan kun være 'først' eller 'sidst'"
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "Mine 25 højest prioriterede sager..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "De 25 højest prioriterede sager, jeg rekvirerede..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$field%>"
+
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+#: html/Elements/CreateTicket:47
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Ny sag kommet\" />&nbsp;%1"
+
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+#: docs/design_docs/string-extraction-guide.txt:54
+#: lib/RT/StyleGuide.pod:787
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Ny sag kommet\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "En tom skabelon"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Adgangskode er ikke blevet tildelt, så brugeren kan ikke logge ind."
+
+#: lib/RT/ACE_Overlay.pm:174
+#: lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE ikke fundet"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE'er kan kun oprettes og slettes."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "OG"
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Afbryder for at undgå utilsigtede sagsændringer.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Om mig"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Adgangskontrol"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Handling"
+
+#. ($args{'ScripAction'})
+#: lib/RT/Scrip_Overlay.pm:172
+msgid "Action %1 not found"
+msgstr "Handling %1 ikke fundet"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Handling igangsat."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Handling igangsat.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr "Handlingen er en obligatorisk parameter"
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Handling forberedt..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Tilføj"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Tilføj AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Tilføj Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Tilføj kolonner"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Tilføj kriterier"
+
+#: html/Ticket/Create.html:147
+#: html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Tilføj flere filer"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Tilføj rekvirent"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Tilføj værdi"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Tilføj en ny global scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Tilføj en scrip til denne kø"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Tilføj en scrip, som vil gælde for alle køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "Tilføj yderligere kriterier"
+
+#: html/Search/Build.html:109
+#: html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Tilføj og søg"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Tilføj kommentarer til eller svar på de udvalgte sager"
+
+#: html/Admin/Groups/Members.html:63
+#: html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Tilføj medlemmer"
+
+#: html/Admin/Queues/People.html:87
+#: html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Tilføj nye observatører"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Tilføj disse ord til din søgning"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "Tilføj værdier"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Tilføj, slet og rediger ekstrafeltværdier for objekter"
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "TilføjNæsteStatus"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:763
+msgid "Added principal as a %1 for this queue"
+msgstr "Tilføjede principal som %1 for denne kø"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1455
+msgid "Added principal as a %1 for this ticket"
+msgstr "Tilføjede principal som %1 for denne sag"
+
+#: html/Admin/Users/Modify.html:146
+#: html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adresse1"
+
+#: html/Admin/Users/Modify.html:151
+#: html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adresse2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Admin kommentar"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Admin korrespondance"
+
+#: html/Admin/Queues/index.html:46
+#: html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Admin køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Admin brugere"
+
+#: html/Admin/Global/index.html:47
+#: html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Amin/Global konfiguration"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Admin/Grupper"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Admin/Køer/Stamdata"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "AdminAllePersonligeGrupper"
+
+#: etc/initialdata:56
+#: html/Ticket/Elements/ShowPeople:60
+#: lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "AdminKommentar"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "AdminKorrespondance"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "AdminEkstrafelt"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "AdminEkstrafelter"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "AdminGruppe"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "AdminGruppeMedlemskab"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "AdminEgnePersonligeGrupper"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "AdminKø"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "AdminBrugere"
+
+#: html/Admin/Queues/People.html:69
+#: html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Administrativ Cc"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Avanceret"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Avanceret søgning"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Efter"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Alder"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Aggregator"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Alle godkendelser accepteret"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Alle ekstrafelter"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Alle køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Sender altid en besked til rekvirenten uafhængigt af beskedens afsender"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "Og/eller"
+
+#: html/Admin/CustomFields/Modify.html:73
+#: html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Gælder for"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Anvend"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Anvend dine ændringer"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Godkendelse"
+
+#. ($ticket->id, $msg)
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#: html/Approvals/Display.html:65
+#: html/Approvals/Elements/ShowDependency:63
+#: html/Approvals/index.html:86
+msgid "Approval #%1: %2"
+msgstr "Godkendelse #%1: %2"
+
+#. ($ticket->Id)
+#: html/Approvals/index.html:75
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Godkendelse #%1: Noter ikke gemt på grund af en systemfejl"
+
+#. ($ticket->Id)
+#: html/Approvals/index.html:73
+msgid "Approval #%1: Notes recorded"
+msgstr "Godkendelse #%1: Noter blev gemt"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Godkendelsesdetaljer"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Godkendelse accepteret"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Godkendelse afvist"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Godkendelsesdiagram"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Godkend"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Godkenderens noter: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Apr."
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "Stigende"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Stigende"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Tilknyt og slet ekstrafelter"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "TilknytEkstrafelter"
+
+#: html/Search/Bulk.html:142
+#: html/SelfService/Update.html:87
+#: html/Ticket/ModifyAll.html:115
+#: html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Vedhæft"
+
+#: html/SelfService/Create.html:92
+#: html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Vedhæft fil"
+
+#: html/SelfService/Update.html:75
+#: html/Ticket/Create.html:131
+#: html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Vedhæftet fil"
+
+#. ($Attachment)
+#: html/Ticket/ShowEmailRecord.html:52
+#: html/Ticket/ShowEmailRecord.html:56
+#: html/Ticket/ShowEmailRecord.html:59
+msgid "Attachment '%1' could not be loaded"
+msgstr "Vedhæftede fil '%1' kunne ikke indlæses"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Vedhæftet fil oprettet"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Navn på vedhæftet fil"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Vedhæftede filer"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Egenskab slettet"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Aug."
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "AuthSystem"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Autosvar"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Autosvar til rekvirenter"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "AutoSvarTilRekvirenter"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "Tilgængelig"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Ubrugelig PGP-signatur: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Ubrugelig vedhæftnings-ID. Kunne ikke finde vedhæftet fil '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Ubrugelige data i %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr ""
+"Ubrugeligt transaktionsnummer for vedhæftet fil. %1 skulle være %2\\n"
+"."
+
+#: html/Admin/Elements/CustomFieldTabs:65
+#: html/Admin/Elements/GroupTabs:60
+#: html/Admin/Elements/QueueTabs:60
+#: html/Admin/Elements/UserTabs:58
+#: html/Ticket/Elements/Tabs:113
+#: html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Stamdata"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91
+#: html/Admin/CustomFields/UserRights.html:74
+#: html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Sørg for at gemme dine ændringer"
+
+#: html/Elements/SelectDateRelation:55
+#: lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Før"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "PÃ¥begynd godkendelse"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Best Practical Solutions, LLC firmalogo"
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "Binær"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Tom"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Fed"
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "URL for denne søgning kan bogmærkes"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Reference kan bogmærkes"
+
+#: html/Ticket/Elements/ShowHistory:64
+#: html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Korte overskrifter"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "Masseopdatering"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Masse-sagsopdatering"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Kan ikke rette systembrugere"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Kan denne principal se denne kø"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Kan ikke tilføje ekstrafeltværdier uden navn"
+
+#. ($Class)
+#: html/Admin/CustomFields/Objects.html:86
+msgid "Can't find a collection class for '%1'"
+msgstr "Kan ikke finde gruppeklasse for '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Kan ikke finde en gemt søgning at arbejde med"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Kan ikke referere en sag til sig selv"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Kan ikke føjes ind i en sammenføjet sag. Denne fejl skulle aldrig opstå."
+
+#. (loc($self->{SearchType}))
+#: html/Widgets/SavedSearch:63
+msgid "Can't save %1"
+msgstr "Kan ikke gemme %1"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Kan ikke gemme denne søgning"
+
+#: lib/RT/Record.pm:1282
+#: lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Kan ikke angive både kilde og destination"
+
+#. ($msg)
+#: html/autohandler:204
+msgid "Cannot create user: %1"
+msgstr "Kan ikke oprette bruger: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62
+#: html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "Kategori"
+
+#: etc/initialdata:50
+#: html/Admin/Queues/People.html:65
+#: html/SelfService/Create.html:71
+#: html/Ticket/Create.html:88
+#: html/Ticket/Elements/EditPeople:72
+#: html/Ticket/Elements/ShowPeople:56
+#: html/Ticket/Update.html:83
+#: lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Skift adgangskode"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Vælg alle"
+
+#: html/SelfService/Update.html:78
+#: html/Ticket/Create.html:134
+#: html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Markér for at slette"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Markér for at inddrage rettighed"
+
+#: html/Elements/EditLinks:148
+#: html/Elements/EditLinks:85
+#: html/Elements/ShowLinks:78
+#: html/Ticket/Create.html:223
+#: html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Børn"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Vælg en dato"
+
+#: html/Admin/Users/Modify.html:156
+#: html/User/Prefs.html:141
+msgid "City"
+msgstr "By"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Fravælg alle"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "Luk vindue"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Lukket"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "Lukkede sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Lukkede sager"
+
+#: html/SelfService/Closed.html:46
+#: html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Lukkede sager"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Combobox: Vælg eller indtast flere værdier"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Combobox: Vælg eller indtast en værdi"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Combobox: Vælg eller indtast op til %1 værdier"
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Kommando ikke forstået!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190
+#: html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Kommentar"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Kommentar - adresse"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Kommentar ikke gemt"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Kommentar på sager"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "KommentarPÃ¥Sag"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Kommentarer"
+
+#: html/Ticket/ModifyAll.html:91
+#: html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Kommentarer (ikke sendt til rekvirenter)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Kommentarer (ikke sendt til rekvirenter)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Kommentarer om %1"
+
+#: html/Admin/Users/Modify.html:225
+#: html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Kommentarer om denne bruger"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Kommentarer tilføjet"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Commit tømt"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Kompilere afgrænsninger"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Betingelse"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "Betingelsen er en obligatorisk parameter"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Betingelser stemmer overens..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Betingelse ikke fundet"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Konfiguration"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Bekræft"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "KontaktInfoSystem"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Dato kontaktet '%1' kunne ikke fortolkes"
+
+#: html/Admin/Elements/ModifyTemplate:65
+#: html/Elements/SelectAttachmentField:48
+#: html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Indhold"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Indhold-Type"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Kopi"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Korrespondance"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Korrespondanceadresse"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Korrespondance tilføjet"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Korrespondance ikke gemt"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Kunne ikke tilføje ny ekstrafeltværdi til sag."
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Kunne ikke tilføje ny ekstrafeltværdi."
+
+#. (, $value_msg)
+#: lib/RT/Record.pm:1660
+msgid "Could not add new custom field value. %1 "
+msgstr "Kunne ikke tilføje ny ekstrafeltværdi. %1"
+
+#: lib/RT/Ticket_Overlay.pm:3048
+#: lib/RT/Ticket_Overlay.pm:3056
+#: lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Kunne ikke at skifte ejer."
+
+#. ($msg)
+#: html/Admin/CustomFields/Modify.html:161
+msgid "Could not create CustomField"
+msgstr "Kunne ikke oprette ekstrafelt"
+
+#. ($msg)
+#: html/Admin/Elements/EditCustomField:113
+msgid "Could not create CustomField: %1"
+msgstr "Kunne ikke oprette ekstrafelt: %1"
+
+#: html/User/Groups/Modify.html:98
+#: lib/RT/Group_Overlay.pm:494
+#: lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Kunne ikke oprette gruppe"
+
+#. ($msg)
+#: html/Admin/Global/Template.html:96
+#: html/Admin/Queues/Template.html:93
+msgid "Could not create template: %1"
+msgstr "Kunne ikke oprette skabelon: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075
+#: lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Kunne ikke at oprette sag. Kø ikke sat."
+
+#: lib/RT/User_Overlay.pm:255
+#: lib/RT/User_Overlay.pm:269
+#: lib/RT/User_Overlay.pm:278
+#: lib/RT/User_Overlay.pm:287
+#: lib/RT/User_Overlay.pm:296
+#: lib/RT/User_Overlay.pm:310
+#: lib/RT/User_Overlay.pm:320
+#: lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Kunne ikke oprette bruger"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Kunne ikke finde sag med ID %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Kunne ikke finde gruppe %1."
+
+#: lib/RT/Queue_Overlay.pm:741
+#: lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Kunne ikke finde eller oprette den bruger"
+
+#: lib/RT/Queue_Overlay.pm:802
+#: lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Kunne ikke finde den principal"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Kunne ikke finde bruger %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Kunne ikke indlæse ekstrafelt %1"
+
+#: html/Admin/Groups/Members.html:112
+#: html/User/Groups/Members.html:111
+#: html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Kunne ikke indlæse gruppe"
+
+#. ($privacy)
+#: lib/RT/SavedSearch.pm:119
+msgid "Could not load object for %1"
+msgstr "Kunne ikke indlæse objekt til %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Kunne ikke indlæse søgeegenskab"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:761
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Kunne ikke gøre den principal til %1 for denne kø"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1444
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Kunne ikke gøre den prinicipal til %1 for denne sag"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:860
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Kunne ikke slette den principal som %1 for denne kø"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Kunne ikke slette den principal som %1 for denne sag"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Kunne ikke angive brugerinfo"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "Kunne ikke tilføje fil"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Kunne ikke tilføje medlem til gruppe"
+
+#. ($Msg)
+#: lib/RT/Record.pm:1719
+#: lib/RT/Record.pm:1771
+msgid "Couldn't create a transaction: %1"
+msgstr "Kunne ikke oprette en transaktion: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Kunne ikke afgøre, hvad der skulle foretages ud fra GPG's svar\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Kunne ikke finde gruppe\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Kunne ikke finde række"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Kunne ikke finde den principal"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Kunne ikke finde den værdi"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Kunne ikke finde bruger\\n"
+
+#. ($self->Id)
+#: lib/RT/CurrentUser.pm:145
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Kunne ikke indlæse %1 fra brugerdatabase.\\n"
+
+#. ($id)
+#: html/Admin/CustomFields/UserRights.html:149
+msgid "Couldn't load Class %1"
+msgstr "Kunne ikke indlæse klasse %1"
+
+#. ($id)
+#: html/Admin/CustomFields/GroupRights.html:107
+msgid "Couldn't load CustomField %1"
+msgstr "Kunne ikke indlæse ekstrafelt %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "Kunne ikke indlæse RT-konfigurationsfil '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Kunne ikke indlæse scrips."
+
+#. ($self->Id)
+#: lib/RT/Ticket_Overlay.pm:2016
+msgid "Couldn't load copy of ticket #%1."
+msgstr "Kunne ikke indlæse kopi af sag #%1."
+
+#. ($id)
+#: html/Admin/Groups/GroupRights.html:109
+#: html/Admin/Groups/UserRights.html:96
+msgid "Couldn't load group %1"
+msgstr "Kunne ikke indlæse gruppe %1"
+
+#: lib/RT/Link_Overlay.pm:202
+#: lib/RT/Link_Overlay.pm:211
+#: lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Kunne ikke indlæse reference"
+
+#. ($id)
+#: html/Admin/Elements/ObjectCustomFields:83
+#: html/Admin/Queues/CustomFields.html:59
+#: html/Admin/Users/CustomFields.html:59
+msgid "Couldn't load object %1"
+msgstr "Kunne ikke indlæse objekt %1"
+
+#. ($id)
+#: html/Admin/Queues/People.html:142
+msgid "Couldn't load queue"
+msgstr "Kunne ikke indlæse kø"
+
+#. ($id)
+#: html/Admin/Queues/GroupRights.html:122
+#: html/Admin/Queues/UserRights.html:93
+msgid "Couldn't load queue %1"
+msgstr "Kunne ikke indlæse kø %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Kunne ikke indlæse scrip"
+
+#. ($id)
+#: html/Admin/Elements/EditScrip:126
+#: html/Admin/Elements/EditScrip:167
+msgid "Couldn't load scrip #%1"
+msgstr "Kunne ikke indlæse scrip #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Kunne ikke indlæse skabelon"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Kunne ikke indlæse den bruger (%1)"
+
+#. ($id)
+#: html/SelfService/Display.html:158
+#: lib/RT/Action/CreateTickets.pm:680
+msgid "Couldn't load ticket '%1'"
+msgstr "Kunne ikke indlæse sag '%1'"
+
+#. ($args{'URI'})
+#: lib/RT/Ticket_Overlay.pm:2643
+msgid "Couldn't resolve '%1' into a URI."
+msgstr "Kunne ikke konvertere '%1' til en URL."
+
+#: html/Admin/Users/Modify.html:173
+#: html/User/Prefs.html:153
+msgid "Country"
+msgstr "Land"
+
+#: html/Admin/Elements/CreateUserCalled:47
+#: html/Admin/Elements/EditCustomField:84
+#: html/Admin/Elements/EditScrip:133
+#: html/Admin/Queues/Template.html:66
+#: html/Elements/QuickCreate:65
+#: html/Ticket/Create.html:168
+#: html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Opret"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Opret sager"
+
+#: html/Admin/CustomFields/Modify.html:150
+#: html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Opret et ekstrafelt"
+
+#. ($QueueObj->Name())
+#: html/Admin/Queues/CustomField.html:69
+msgid "Create a CustomField for queue %1"
+msgstr "Opret et ekstrafelt til kø %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Opret et ekstrafelt, der gælder for alle køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Opret et nyt ekstrafelt"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Opret en ny global scrip"
+
+#: html/Admin/Groups/Modify.html:125
+#: html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Opret en ny gruppe"
+
+#: html/User/Groups/Modify.html:113
+#: html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Opret en ny personlig gruppe"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Opret en ny kø"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Opret en ny scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Opret en ny skabelon"
+
+#: html/Ticket/Create.html:47
+#: html/Ticket/Create.html:51
+#: html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Opret en ny sag"
+
+#: html/Admin/Users/Modify.html:252
+#: html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Opret en ny bruger"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Opret en ny kø"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Opret en kø med ved navn"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Opret en sag"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrip.html:89
+msgid "Create a scrip for queue %1"
+msgstr "Opret en scrip for kø %1"
+
+#: html/Admin/Global/Template.html:90
+#: html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Opret en skabelon"
+
+#: html/SelfService/Create.html:46
+#: html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Opret en sag"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Opret en ny sag baseret på denne scrips skabelon"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Opret sag"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Opret sager i denne kø"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Opret, slet og rediger ekstrafelter"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Opret, slet og rediger køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Opret, slet og rediger medlemmerne af hvilken som helst brugers personlige grupper"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Opret, slet og rediger medlemmernes personlige grupper"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Opret, slet og rediger brugere"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "OpretGemtSøgning"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "OpretSag"
+
+#: html/Elements/SelectDateType:47
+#: html/Ticket/Elements/ShowDates:48
+#: lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Oprettet"
+
+#. ($CustomFieldObj->Name())
+#: html/Admin/CustomFields/Modify.html:163
+#: html/Admin/Elements/EditCustomField:117
+msgid "Created CustomField %1"
+msgstr "Ekstrafelt %1 oprettet"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Oprettet i et datointerval"
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Skabelon %1 oprettet"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "Sager oprettet i en periode, fordelt pr. status"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Opretter"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Aktuelle referencer"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Aktuelle scrips"
+
+#: html/Admin/Groups/Members.html:60
+#: html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Aktuelle medlemmer"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Aktuelle rettigheder"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Aktiv søgning"
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Aktuelle søgekriterier"
+
+#: html/Admin/Queues/People.html:62
+#: html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Aktuelle observatører"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Ekstrafelt #%1"
+
+#: html/Admin/Elements/SystemTabs:61
+#: html/Admin/Elements/Tabs:62
+#: html/Admin/Global/index.html:71
+#: html/Admin/Users/Modify.html:205
+#: html/Admin/index.html:77
+#: html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Ekstrafelter"
+
+#. ($lookup)
+#: html/Admin/CustomFields/index.html:60
+msgid "Custom Fields for %1"
+msgstr "Ekstrafelter for %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Oprydningskode for specialtilpasset handling"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Ekstrahandling til forberedelseskode"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Ekstra betingelse"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Ekstrafelt %1 %2 %3"
+
+#. ($CF->Name)
+#: lib/RT/Tickets_Overlay.pm:2424
+msgid "Custom field %1 has a value."
+msgstr "Ekstrafelt %1 har en værdi."
+
+#. ($CF->Name)
+#: lib/RT/Tickets_Overlay.pm:2420
+msgid "Custom field %1 has no value."
+msgstr "Ekstrafelt %1 har ingen værdi."
+
+#. ($args{'Field'})
+#: lib/RT/Record.pm:1592
+#: lib/RT/Record.pm:1754
+msgid "Custom field %1 not found"
+msgstr "Ekstrafelt %1 ikke fundet"
+
+#. ($cf)
+#. ($obj->Name)
+#: lib/RT/Report/Tickets.pm:118
+#: lib/RT/Report/Tickets.pm:121
+msgid "Custom field '%1'"
+msgstr "Ekstrafelt '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Ekstrafelt slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Ekstrafelt ikke fundet"
+
+#. ($args{'Content'}, $self->Name)
+#: lib/RT/CustomField_Overlay.pm:1157
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Ekstrafeltværdi %1 kunne ikke findes til ekstrafelt %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Ekstrafeltværdi ændret fra %1 til %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Ekstrafeltværdi kunne ikke slettes"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Ekstrafeltværdi kunne ikke findes"
+
+#: lib/RT/CustomField_Overlay.pm:1171
+#: lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Ekstrafeltværdi slettet"
+
+#: html/Elements/SelectGroups:51
+#: html/Elements/SelectUsers:51
+#: lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "Ekstrafelt"
+
+#: html/Prefs/MyRT.html:78
+#: html/Prefs/Quicksearch.html:70
+#: html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "Tilpas"
+
+#: html/SelfService/Display.html:61
+#: html/Ticket/Create.html:203
+#: html/Ticket/Elements/ShowSummary:83
+#: html/Ticket/Elements/Tabs:116
+#: html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Datoer"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Dec."
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Standard Autosvarskabelon"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Standard Autosvarskabelon"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Standard kø"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Standard rekvirent"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Standard administrator kommentarskabelon"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Standard administrator korrespondanceskabelon"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Standard korrespondanceskabelon"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Standard transaktionsskabelon"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Standard: %1/%2 ændret fra %3 til %4"
+
+#: html/User/Delegation.html:46
+#: html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Overdrag rettigheder"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Overdrag specifikke rettigheder, som er blevet tildelt til dig."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "OverdragRettigheder"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Overdragelse"
+
+#: html/Admin/Elements/EditScrips:75
+#: html/Search/Elements/EditFormat:103
+#: html/Search/Elements/EditQuery:57
+#: html/Search/Elements/EditSearches:63
+#: html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Slet"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Slet skabelon"
+
+#. ($msg)
+#: lib/RT/SavedSearch.pm:220
+msgid "Delete failed: %1"
+msgstr "Sletning mislykkedes: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Slet valgte scrips"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Slet sager"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr "Slet værdier"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "SletSag"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Søgning slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Sletning af dette objekt kan ødelægge referentiel integritet"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Sletning af dette objekt vil ødelægge referentiel integritet"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Sletning af dette objekt vil stride mod referentiel integritet"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Afvis"
+
+#: html/Elements/EditLinks:140
+#: html/Elements/EditLinks:66
+#: html/Elements/ShowLinks:58
+#: html/Ticket/Create.html:221
+#: html/Ticket/Elements/BulkLinks:56
+#: html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Afhængighed til"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Afhængigheder: \\n"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:718
+msgid "Dependency by %1 added"
+msgstr "Afhængighed til %1 tilføjet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:758
+msgid "Dependency by %1 deleted"
+msgstr "Afhængighed til %1 slettet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:715
+msgid "Dependency on %1 added"
+msgstr "Afhængighed af %1 tilføjet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:755
+msgid "Dependency on %1 deleted"
+msgstr "Afhængighed af %1 slettet"
+
+#: html/Elements/EditLinks:136
+#: html/Elements/EditLinks:57
+#: html/Elements/SelectLinkType:48
+#: html/Elements/ShowLinks:48
+#: html/Ticket/Create.html:220
+#: html/Ticket/Elements/BulkLinks:52
+#: html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Afhængig af"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "Faldende"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Faldende"
+
+#: html/SelfService/Create.html:100
+#: html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Beskriv problemet nedenfor"
+
+#: html/Admin/CustomFields/Modify.html:61
+#: html/Admin/Elements/AddCustomFieldValue:57
+#: html/Admin/Elements/EditCustomField:60
+#: html/Admin/Elements/EditCustomFieldValues:56
+#: html/Admin/Elements/EditScrip:55
+#: html/Admin/Elements/ModifyTemplate:57
+#: html/Admin/Groups/Modify.html:71
+#: html/Admin/Queues/Modify.html:69
+#: html/Search/Elements/EditSearches:56
+#: html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Beskrivelse"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Detaljer"
+
+#: html/Search/Elements/EditFormat:71
+#: html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Vis"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Vis adgangskontrolliste"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Vis kolonner"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Vis scrip-skabeloner for denne kø"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Vis scrips for denne kø"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Vis tilstand"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Vis gemte søgninger for denne gruppe"
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Vis sag #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Distribueret under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> af GNU GPL'en.</a>"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Gør hvad som helst"
+
+#: NOT FOUND IN SOURCE
+msgid "Do the Search"
+msgstr "Udfør søgningen"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Opdater ikke denne side."
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Vis ikke søgeresultater"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Download"
+
+#: html/Admin/Groups/index.html:61
+#: html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Download som en tabulatoropdelt fil"
+
+#: html/Elements/SelectDateType:53
+#: html/Ticket/Create.html:209
+#: html/Ticket/Elements/EditDates:66
+#: html/Ticket/Elements/Reminders:133
+#: html/Ticket/Elements/ShowDates:64
+#: lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Forfalden"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Forfaldsdato '%1' kunne ikke fortolkes"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "FEJL: Kunne ikke indlæse sag '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48
+#: html/Elements/ShowSearch:49
+#: html/index.html:107
+msgid "Edit"
+msgstr "Rediger"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "Rediger ekstrafelter"
+
+#. ($Object->Name)
+#: html/Admin/Elements/ObjectCustomFields:92
+#: html/Admin/Queues/CustomFields.html:64
+#: html/Admin/Users/CustomFields.html:64
+msgid "Edit Custom Fields for %1"
+msgstr "Rediger ekstrafelter for %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Rediger ekstrafelter for alle grupper"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Rediger ekstrafelter for alle brugere"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54
+#: html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Rediger ekstrafelter for sager i alle køer"
+
+#: html/Search/Bulk.html:188
+#: html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Rediger referencer"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Rediger søgning"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Rediger søgning"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Templates.html:63
+msgid "Edit Templates for queue %1"
+msgstr "Rediger skabeloner for kø %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Rediger gemte søgninger for denne gruppe"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Rediger scrips"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60
+#: html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Rediger systemskabeloner"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Rediger skabeloner for %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "RedigerGemteSøgninger"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Modify.html:140
+msgid "Editing Configuration for queue %1"
+msgstr "Redigerer konfiguration for kø %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Redigerer konfiguration for bruger %1"
+
+#. ($CustomFieldObj->Name())
+#: html/Admin/CustomFields/Modify.html:167
+#: html/Admin/Elements/EditCustomField:120
+msgid "Editing CustomField %1"
+msgstr "Redigerer ekstrafelt %1"
+
+#. ($Group->Name)
+#: html/Admin/Groups/Members.html:53
+msgid "Editing membership for group %1"
+msgstr "Redigerer medlemskab for gruppe %1"
+
+#. ($Group->Name)
+#: html/User/Groups/Members.html:150
+msgid "Editing membership for personal group %1"
+msgstr "Redigerer medlemskab for personlig gruppe %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Redigerer skabelon %1"
+
+#: lib/RT/Record.pm:1295
+#: lib/RT/Record.pm:1372
+#: lib/RT/Ticket_Overlay.pm:2518
+#: lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Enten kilde eller destination skal angives"
+
+#: html/Admin/Users/Modify.html:74
+#: html/Ticket/Elements/AddWatchers:77
+#: html/User/Prefs.html:65
+msgid "Email"
+msgstr "E-mail"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "E-mail-adresse i brug"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "E-mailAdresse"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "E-mailKodning"
+
+#: html/Admin/CustomFields/Modify.html:98
+#: html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Aktiveret (fravalg af denne boks deaktiverer dette ekstrafelt)"
+
+#: html/Admin/Groups/Modify.html:84
+#: html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Aktiveret (fravalg af denne boks deaktiverer denne gruppe)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Aktiveret (fravalg af denne boks deaktiverer denne kø)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "Aktiverede ekstrafelter"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Aktiverede køer"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/Elements/EditCustomField:136
+#: html/Admin/Groups/Modify.html:150
+#: html/Admin/Users/Modify.html:350
+#: html/User/Groups/Modify.html:138
+msgid "Enabled status %1"
+msgstr "Aktiveret status %1"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/CustomFields/Modify.html:185
+#: html/Admin/Queues/Modify.html:162
+msgid "Enabled status: %1"
+msgstr "Aktiveret status: %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Indtast flere værdier"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Angiv objekter eller URL'er til tilknytning af objekter. Flere indtastninger adskilles med mellemrum."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Indtast en værdi"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Angiv køer eller URL'er til tilknytning af køer. Flere indtastninger adskilles med mellemrum."
+
+#: html/Elements/EditLinks:119
+#: html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Angiv sager eller URL'er til tilknytning af sager. Flere værdier adskilles med mellemrum."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Angiv op til %1 værdier"
+
+#: html/Elements/Login:76
+#: html/SelfService/Error.html:46
+#: html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Fejl"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Fejl i parametre til Kø->TilføjObservatør"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Fejl i parametre til Kø->SletObservatør"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Fejl i parametre til Kø->SletObservatør"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Fejl i parametre til Sag->TilføjObservatør"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Fejl i parametre til Sag->SletObservatør"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Fejl i parametre til Sag->SletObservatør"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Opprioriter sager"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Estimeret"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Alle"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Eksempel:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "EksternAuthID"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "EksternKontaktInfoID"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Ekstra information"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Kunne ikke oprette søgeegenskab"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Kunne ikke finde 'privilegerede' brugeres pseudogruppe."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Kunne ikke finde 'uprivilegerede' brugeres pseudogruppe."
+
+#. ($modname, $@)
+#: bin/rt-crontool:206
+msgid "Failed to load module %1. (%2)"
+msgstr "Kunne ikke indlæse modul %1. (%2)"
+
+#. ($privacy)
+#: lib/RT/SavedSearch.pm:152
+msgid "Failed to load object for %1"
+msgstr "Kunne ikke indlæse objekt for %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Filnavn"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Udfyld flere tekstområder"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Udfyld flere wikitekst-områder"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Udfyld et tekstområde"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Udfyld et wikitekst-område"
+
+#: html/Admin/CustomFields/Modify.html:107
+#: html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "Udfyld dette felt med en URL."
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Udfyld op til %1 tekstområder"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Udfyld op til %1 wikitekst-områder"
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Slut"
+
+#: html/Search/Elements/PickBasics:149
+#: html/Ticket/Create.html:182
+#: html/Ticket/Elements/EditBasics:97
+#: lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Endelig prioritet"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "EndeligPrioritet"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Find gruppe hvis"
+
+#: html/Admin/Groups/index.html:72
+#: html/Admin/Queues/People.html:82
+#: html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Find grupper hvis"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Find nye/Ã¥bne sager"
+
+#: html/Admin/Queues/People.html:78
+#: html/Admin/Users/index.html:70
+#: html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Find personer hvis"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Find sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Afslut godkendelse"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Første"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Første side"
+
+#: docs/design_docs/string-extraction-guide.txt:33
+#: lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24
+#: lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Gennemtving ændring"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Format"
+
+#. ($ticketcount)
+#: html/Search/Results.html:145
+msgid "Found %quant(%1,ticket)"
+msgstr "Fandt %quant(%1,ticket)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Object fundet"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "Frihånd"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FrihåndKontaktInfo"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "FrihåndMange"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "FrihåndEnkelt"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Fre."
+
+#: html/Ticket/Elements/ShowHistory:66
+#: html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Hele overskrifter"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Hent skabelon fra fil"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Henter den aktuelle bruger fra en PGP-signatur\\n"
+
+#. ($New->Name)
+#: lib/RT/Transaction_Overlay.pm:684
+msgid "Given to %1"
+msgstr "Givet til %1"
+
+#: html/Admin/Elements/Tabs:65
+#: html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Globale ekstrafelter"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Globale scrips"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Global ekstrafelt-konfiguration"
+
+#. ($pane)
+#: html/Admin/Global/MyRT.html:48
+msgid "Global portlet %1 saved."
+msgstr "Global portlet %1 gemt."
+
+#. (loc($Template->Name))
+#: html/Admin/Elements/SelectTemplate:59
+msgid "Global template: %1"
+msgstr "Global skabelon: %1"
+
+#: html/Admin/CustomFields/index.html:80
+#: html/Search/Results.html:90
+#: html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Start"
+
+#: html/Admin/Groups/index.html:67
+#: html/Admin/Groups/index.html:73
+#: html/Admin/Queues/People.html:80
+#: html/Admin/Queues/People.html:84
+#: html/Admin/Queues/index.html:66
+#: html/Admin/Users/index.html:73
+#: html/Elements/RefreshHomepage:48
+#: html/Search/Results.html:74
+#: html/Ticket/Elements/EditPeople:53
+#: html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Start!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Brugbar PGP-signatur fra %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "GÃ¥ til side"
+
+#: html/Elements/GotoTicket:46
+#: html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "GÃ¥ til sag"
+
+#: html/Ticket/Elements/AddWatchers:67
+#: html/Ticket/Elements/ShowGroupMembers:55
+#: html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Gruppe"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Gruppe %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68
+#: html/Admin/Elements/GroupTabs:66
+#: html/Admin/Elements/QueueTabs:82
+#: html/Admin/Elements/SystemTabs:65
+#: html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Grupperettigheder"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Gruppe har allerede medlem"
+
+#. ($create_msg)
+#: html/Admin/Groups/Modify.html:109
+msgid "Group could not be created: %1"
+msgstr "Gruppe kunne ikke oprettes: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Gruppe oprettet"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Gruppen har ikke et sådant medlem"
+
+#: lib/RT/Group_Overlay.pm:963
+#: lib/RT/Queue_Overlay.pm:748
+#: lib/RT/Queue_Overlay.pm:808
+#: lib/RT/Ticket_Overlay.pm:1430
+#: lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Gruppe ikke fundet"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Gruppe ikke fundet.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Gruppe ikke angivet.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59
+#: html/Admin/Elements/SelectNewGroupMembers:57
+#: html/Admin/Elements/Tabs:56
+#: html/Admin/Global/CustomFields/index.html:69
+#: html/Admin/Groups/Members.html:86
+#: html/Admin/Queues/People.html:104
+#: html/Admin/Users/Memberships.html:53
+#: html/Admin/index.html:67
+#: html/User/Groups/Members.html:88
+#: lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Grupper"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Grupper kan ikke være medlem af deres medlemmer"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Grupper som opfylder søgekriterier"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Grupper denne bruger hører til"
+
+#: lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hej!"
+
+#. ($name)
+#: docs/design_docs/string-extraction-guide.txt:40
+#: lib/RT/StyleGuide.pod:773
+msgid "Hello, %1"
+msgstr "Hej, %1"
+
+#: html/Admin/Elements/GroupTabs:70
+#: html/Admin/Elements/UserTabs:64
+#: html/Ticket/Elements/ShowHistory:53
+#: html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historik"
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/History.html:62
+msgid "History of the group %1"
+msgstr "Historik for gruppen %1"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/History.html:62
+msgid "History of the user %1"
+msgstr "Historik for brugeren %1"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Telefon hjemme"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Forside"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "Timer"
+
+#. (6)
+#: lib/RT/Base.pm:119
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Jeg har %quant(%1,concrete mixer)."
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "Jeg har [quant,_1,concrete mixer]."
+
+#: html/Search/Build.html:460
+#: lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Jeg er faret vild"
+
+#: html/Ticket/Elements/ShowBasics:48
+#: lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "ID"
+
+#: html/Admin/Users/Modify.html:65
+#: html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identitet"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Hvis en godkendelse bliver afvist, afvis den oprindelige og slet afventende godkendelser"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Hvis ingen rekvirent er angivet, opret sager med denne rekvirent."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Hvis ingen kø er oprettet, opret sager i denne kø."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Hvis dette værktøj var \"setgid\", kunne en fjendtlig bruger benytte det til at opnå administratoradgang til RT."
+
+#: html/Admin/Queues/People.html:126
+#: html/Ticket/Modify.html:60
+#: html/Ticket/ModifyAll.html:128
+#: html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Hvis du har foretaget opdateringer ovenfor, sørg for at..."
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Ulovlig værdi for %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Billede"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Felt kan ikke ændres"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Medtag deaktiverede ekstrafelter på liste."
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Medtag deaktiverede grupper på denne liste."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Medtag deaktiverede køer på liste."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Medtag deaktiverede brugere i søgning."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Medtag side"
+
+#: html/Search/Build.html:486
+#: lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Ufuldstændig søgning"
+
+#: html/Search/Build.html:483
+#: lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Ufuldstændig søgning"
+
+#: html/Search/Elements/PickBasics:148
+#: lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Første prioritet"
+
+#: lib/RT/Ticket_Overlay.pm:1163
+#: lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "FørstePrioritet"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Inputfejl"
+
+#. ($CF->FriendlyPattern)
+#. ($self->FriendlyPattern)
+#: html/Elements/ValidateCustomFields:68
+#: lib/RT/CustomField_Overlay.pm:1021
+#: lib/RT/CustomField_Overlay.pm:1162
+msgid "Input must match %1"
+msgstr "Input skal stemme overens med %1"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Intern fejl"
+
+#. ($id->{error_message})
+#: lib/RT/Record.pm:308
+msgid "Internal Error: %1"
+msgstr "Intern fejl: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Ugyldig gruppetype"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Ugyldig rettighed"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Ugyldige data"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Ugyldig ejer, sætter standard til 'ingen'"
+
+#. ($msg)
+#: lib/RT/CustomField_Overlay.pm:207
+#: lib/RT/CustomField_Overlay.pm:678
+msgid "Invalid pattern: %1"
+msgstr "Ugyldigt mønster: %1"
+
+#: lib/RT/Scrip_Overlay.pm:157
+#: lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Ugyldig kø"
+
+#: lib/RT/ACE_Overlay.pm:264
+#: lib/RT/ACE_Overlay.pm:273
+#: lib/RT/ACE_Overlay.pm:279
+#: lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Ugyldig rettighed"
+
+#. ($key)
+#: lib/RT/Record.pm:283
+msgid "Invalid value for %1"
+msgstr "Ugyldig værdi for %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Ugyldig værdi for ekstrafelt"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Ugyldig værdi for status"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Det er yderst vigtigt, at ikke-privilegerede brugere ikke får adgang til at bruge dette værktøj."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Det anbefales, at du opretter en ikke-privilegeret Unix-bruger med det korrekte gruppemedlemskab og RT-adgang for at benytte dette værktøj."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Flere parametre er nødvendige:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Kursiv"
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Punkter, der afventer min godkendelse"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Tilmeld dig eller forlad denne gruppe"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul."
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Jumbo"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun."
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Nøgleord"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Sprog"
+
+#: html/Admin/Users/Modify.html:94
+#: html/User/Prefs.html:76
+msgid "Language"
+msgstr "Sprog"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Stor"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Sidste"
+
+#: html/Ticket/Elements/EditDates:59
+#: html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Sidste kontakt"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Sidst kontaktet"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Sidst notificeret"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Sidst opdateret"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "SidstOpdateretAf"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Tilbage"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Lad denne bruger få adgang til RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Lad denne bruger få rettigheder"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Begrænser ejer til %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Begrænser kø til %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Reference"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Reference eksisterer allerede"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Reference kunne ikke oprettes"
+
+#. ($TransString)
+#: lib/RT/Record.pm:1326
+msgid "Link created (%1)"
+msgstr "Reference oprettet (%1)"
+
+#. ($TransString)
+#: lib/RT/Record.pm:1387
+msgid "Link deleted (%1)"
+msgstr "Reference slettet (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Reference ikke fundet"
+
+#. ($Ticket->Id)
+#: html/Ticket/ModifyLinks.html:46
+#: html/Ticket/ModifyLinks.html:50
+msgid "Link ticket #%1"
+msgstr "Referér sag #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "Referér værdier til"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr "Reference - adgang nægtet."
+
+#: html/Ticket/Create.html:216
+#: html/Ticket/Elements/ShowSummary:89
+#: html/Ticket/Elements/Tabs:120
+#: html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Referencer"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Indlæs"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Indlæs gemt søgning:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "IndlæsGemtSøgning"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Indlæste Perl-moduler"
+
+#. ($self->Name)
+#: lib/RT/SavedSearch.pm:111
+msgid "Loaded search %1"
+msgstr "Søgning %1 indlæst"
+
+#: html/Admin/Users/Modify.html:138
+#: html/User/Prefs.html:126
+msgid "Location"
+msgstr "Lokation"
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"Log directory %1 not found or couldn't be written.\\n"
+" RT can't run."
+msgstr ""
+"Mappe til logfiler %1 kunne ikke findes eller skrives til.\\n"
+" RT kan ikke køre."
+
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+#: html/Elements/Header:91
+msgid "Logged in as %1"
+msgstr "Logget ind som %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71
+#: html/Elements/Login:100
+#: html/Elements/Login:68
+#: html/Elements/Login:84
+#: lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Log ind"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Log ud"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Opslagstype mismatch"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Sæt ejer"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Sæt status"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Sæt forfaldsdato"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Sæt løsningsdato"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Sæt startdato"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Sæt datostart"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Sæt dato meddelt"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Sæt prioritet"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Sæt kø"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Sæt emne"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Gør denne gruppe synlig for bruger"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Administrer ekstrafelter og ekstrafeltværdier"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Administrer grupper og gruppemedlemskab"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Administrer egenskaber og konfiguration som gælder for alle køer"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Administrer køer og kø-specifikke egenskaber"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Administrer brugere og adgangskoder"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Maj."
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:731
+msgid "Member %1 added"
+msgstr "Medlem %1 tilføjet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:771
+msgid "Member %1 deleted"
+msgstr "Medlem %1 slettet"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Medlem tilføjet"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Medlem slettet"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Medlem ikke slettet"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Medlem af"
+
+#: html/Admin/Elements/GroupTabs:63
+#: html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Medlemmer"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:728
+msgid "Membership in %1 added"
+msgstr "Medlemskab af %1 tilføjet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:768
+msgid "Membership in %1 deleted"
+msgstr "Medlemskab af %1 slettet"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Medlemskaber"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/Memberships.html:60
+msgid "Memberships of the user %1"
+msgstr "Medlemskaber for brugeren %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Sammenføjning udført med succes"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Sammenføjning lykkedes ikke. Kunne ikke sætte EffektivID."
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Sammenføjning lykkedes ikke. Kunne ikke sætte status."
+
+#: html/Elements/EditLinks:131
+#: html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Føj ind i"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:734
+msgid "Merged into %1"
+msgstr "Føjet ind i %1"
+
+#: html/Search/Bulk.html:143
+#: html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Besked"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "Meddelelsens indhold er ikke vist, fordi meddelelsen er for stor, eller fordi den ikke er i almindelig tekst."
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Besked kunne ikke gemmes"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Besked gemt"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Beskeder angående denne sag vil ikke blive sendt til..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Minutter"
+
+#: html/Search/Build.html:490
+#: lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "Ikke-matchende parenteser"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Mangler en primærnøgle?: %1"
+
+#: html/Admin/Users/Modify.html:193
+#: html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Mobil"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Mobiltelefon"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Rediger adgangskontrolliste"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Rediger ekstrafelt %1"
+
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+#: html/Admin/Elements/ObjectCustomFields:96
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Rediger ekstrafelter, der gælder for %1 for alle %2"
+
+#. (loc(lc($Types)))
+#: html/Admin/Elements/ObjectCustomFields:98
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Rediger ekstrafelter, der gælder for alle %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Rediger ekstrafelter, der gælder for alle køer"
+
+#: html/Admin/Global/GroupRights.html:106
+#: html/Admin/Groups/GroupRights.html:94
+#: html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Rediger grupperettigheder"
+
+#: html/Admin/Groups/Members.html:105
+#: html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Rediger medlemmer"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Rediger rettigheder"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Rediger scrip-skabeloner for denne kø"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Rediger scrips for denne kø"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Rediger skabelon %1"
+
+#: html/Admin/Global/UserRights.html:75
+#: html/Admin/Groups/UserRights.html:76
+#: html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Rediger brugerrettigheder"
+
+#. ($QueueObj->Name())
+#: html/Admin/Queues/CustomField.html:66
+msgid "Modify a CustomField for queue %1"
+msgstr "Rediger et ekstrafelt for kø %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Rediger et ekstrafelt, der gælder for alle køer"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrip.html:82
+msgid "Modify a scrip for queue %1"
+msgstr "Rediger et scrip for kø %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Rediger et scrip, der gælder for alle køer"
+
+#. ($CF->Name)
+#: html/Admin/CustomFields/Objects.html:90
+msgid "Modify associated objects for %1"
+msgstr "Rediger tilhørende objekter til %1"
+
+#. ($TicketObj->Id)
+#: html/Ticket/ModifyDates.html:46
+#: html/Ticket/ModifyDates.html:50
+msgid "Modify dates for #%1"
+msgstr "Rediger datoer for #%1"
+
+#. ($TicketObj->Id)
+#: html/Ticket/ModifyDates.html:57
+msgid "Modify dates for ticket # %1"
+msgstr "Rediger datoer for sag # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65
+#: html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Rediger globale ekstrafelter"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70
+#: html/Admin/Global/GroupRights.html:46
+#: html/Admin/Global/GroupRights.html:49
+#: html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Rediger globale grupperettigheder"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Rediger globale grupperettigheder."
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Rediger globale scrips"
+
+#: html/Admin/Global/UserRights.html:46
+#: html/Admin/Global/UserRights.html:49
+#: html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Rediger globale brugerrettigheder"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Rediger globale brugerrettigheder."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Rediger gruppemetadata eller slet gruppe"
+
+#. ($CustomFieldObj->Name)
+#: html/Admin/CustomFields/GroupRights.html:164
+msgid "Modify group rights for custom field %1"
+msgstr "Rediger grupperettigheder for ekstrafelter %1"
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/GroupRights.html:46
+#: html/Admin/Groups/GroupRights.html:50
+#: html/Admin/Groups/GroupRights.html:56
+msgid "Modify group rights for group %1"
+msgstr "Rediger grupperettigheder for gruppe %1"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/GroupRights.html:46
+#: html/Admin/Queues/GroupRights.html:50
+msgid "Modify group rights for queue %1"
+msgstr "Rediger grupperettigheder for kø %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Rediger medlemskabsliste for denne gruppe"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Rediger din egen RT-konto"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/People.html:46
+#: html/Admin/Queues/People.html:50
+msgid "Modify people related to queue %1"
+msgstr "Rediger personer, der tilhører kø %1"
+
+#. ($Ticket->id)
+#. ($Ticket->Id)
+#: html/Ticket/ModifyPeople.html:46
+#: html/Ticket/ModifyPeople.html:50
+#: html/Ticket/ModifyPeople.html:57
+msgid "Modify people related to ticket #%1"
+msgstr "Rediger personer, der tilhører sag #%1"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrips.html:67
+msgid "Modify scrips for queue %1"
+msgstr "Rediger scrips for kø %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56
+#: html/Admin/Global/Scrips.html:65
+#: html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Rediger scrips, der gælder for alle køer"
+
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+#: html/Admin/Global/Template.html:102
+#: html/Admin/Global/Template.html:46
+#: html/Admin/Global/Template.html:51
+#: html/Admin/Queues/Template.html:99
+msgid "Modify template %1"
+msgstr "Rediger skabelon %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Rediger skabeloner, der gælder for alle køer"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "Tilpas standardvisningen af \"RT set fra oven\""
+
+#. ($Group->Name)
+#: html/Admin/Groups/Modify.html:119
+#: html/User/Groups/Modify.html:107
+msgid "Modify the group %1"
+msgstr "Rediger gruppen %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Rediger observatører for køen"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/Modify.html:309
+msgid "Modify the user %1"
+msgstr "Rediger brugeren %1"
+
+#. ($Ticket->Id)
+#: html/Ticket/ModifyAll.html:58
+msgid "Modify ticket # %1"
+msgstr "Rediger sag # %1"
+
+#. ($TicketObj->Id)
+#: html/Ticket/Modify.html:46
+#: html/Ticket/Modify.html:49
+#: html/Ticket/Modify.html:55
+msgid "Modify ticket #%1"
+msgstr "Rediger sag #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Rediger sager"
+
+#. ($CustomFieldObj->Name)
+#: html/Admin/CustomFields/UserRights.html:157
+msgid "Modify user rights for custom field %1"
+msgstr "Rediger brugerrettigheder for ekstrafelter %1"
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/UserRights.html:46
+#: html/Admin/Groups/UserRights.html:50
+#: html/Admin/Groups/UserRights.html:56
+msgid "Modify user rights for group %1"
+msgstr "Rediger brugerrettigheder for gruppe %1"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/UserRights.html:46
+#: html/Admin/Queues/UserRights.html:50
+msgid "Modify user rights for queue %1"
+msgstr "Rediger brugerrettigheder for kø %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Rediger observatører for kø '%1'"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "RedigerACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "RedigerEkstrafelt"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "RedigeretEgetMedlemskab"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "RedigerKøObservatører"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "RedigerScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "RedigerSelv"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "RedigerSkabelon"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "RedigerSag"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Man."
+
+#. ($name)
+#: html/Ticket/Elements/ShowRequestor:61
+msgid "More about %1"
+msgstr "Mere om %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Flyt ned"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Flyt op"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Flere"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Egenskab 'Navn' skal angives"
+
+#. ($friendly_status)
+#: html/SelfService/Elements/MyRequests:57
+msgid "My %1 tickets"
+msgstr "Mine %1 sager"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Mine godkendelser"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "Min dag"
+
+#: html/Approvals/index.html:46
+#: html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Mine godkendelser"
+
+#: html/Search/Elements/SearchPrivacy:50
+#: html/Search/Elements/SelectSearchObject:53
+#: html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Mine gemte søgninger"
+
+#: html/Admin/CustomFields/Modify.html:58
+#: html/Admin/Elements/AddCustomFieldValue:53
+#: html/Admin/Elements/EditCustomField:55
+#: html/Admin/Elements/EditCustomFieldValues:55
+#: html/Admin/Elements/ModifyTemplate:49
+#: html/Admin/Groups/Modify.html:65
+#: html/Search/Bulk.html:157
+#: html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Navn"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Navn i brug"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Kræver godkendelse af systemadministrator"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Aldrig"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Ny"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nye referencer"
+
+#: html/Admin/Users/Modify.html:119
+#: html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Ny adgangskode"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Ny afventende godkendelse"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Ny søgning"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Ny søgning"
+
+#: html/Admin/Elements/CustomFieldTabs:93
+#: html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nyt ekstrafelt"
+
+#: html/Admin/Elements/GroupTabs:77
+#: html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Ny gruppe"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Ny adgangskode"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Ny meddelelse om adgangskode sendt"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Ny kø"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Ny påmindelse:"
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Ny sag"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nye rettigheder"
+
+#: html/Admin/Global/Scrip.html:63
+#: html/Admin/Global/Scrips.html:60
+#: html/Admin/Queues/Scrip.html:71
+#: html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Ny scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Ny søgning"
+
+#: html/Admin/Global/Template.html:81
+#: html/Admin/Global/Templates.html:60
+#: html/Admin/Queues/Template.html:79
+#: html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Ny skabelon"
+
+#: html/SelfService/Elements/Tabs:84
+#: html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Ny sag"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Ny sag eksisterer ikke"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Ny bruger"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Ny bruger ved navn"
+
+#: html/Admin/Queues/People.html:76
+#: html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nye observatører"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Ny indstilling for vindue"
+
+#: html/Helpers/CalPopup.html:58
+#: html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Næste"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Næste side"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Næste side"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "KaldeNavn"
+
+#: html/Admin/Users/Modify.html:84
+#: html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Kaldenavn"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Ingen klasse defineret"
+
+#: html/Admin/CustomFields/Modify.html:166
+#: html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Intet ekstrafelt"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Intet ekstrafelt defineret"
+
+#: html/Admin/Groups/GroupRights.html:105
+#: html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Ingen gruppe defineret"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Ingen søgning"
+
+#: html/Admin/Queues/GroupRights.html:118
+#: html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Ingen kø defineret"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Ingen RT-bruger fundet. Kontakt venligst din RT-administrator.\\n"
+
+#: html/Admin/Global/Template.html:100
+#: html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Ingen skabelon"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Ingen sag angivet. Afbryder sag."
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"No Ticket specified. Aborting ticket modifications\\n"
+"\\n"
+msgstr ""
+"Ingen sag angivet. Afbryder sagsændringer\\n"
+"\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Ingen handling"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Ingen kolonne angivet"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Ingen kommando fundet\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Ingen kommentar indtastet for denne bruger"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Ingen korrespondance vedhæftet"
+
+#. (ref $self)
+#: lib/RT/Action/Generic.pm:185
+#: lib/RT/Condition/Generic.pm:197
+#: lib/RT/Search/ActiveTicketsInQueue.pm:77
+#: lib/RT/Search/Generic.pm:134
+#: lib/RT/Search/Googleish.pm:78
+msgid "No description for %1"
+msgstr "Ingen beskrivelse af %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Ingen gruppe angivet"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Ingen grupper fundet, der opfylder søgekriterier"
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Ingen meddelelse vedhæftet"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Ingen adgangskode sat"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Ingen adgang til at oprette køer"
+
+#. ($QueueObj->Name)
+#: lib/RT/Ticket_Overlay.pm:420
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Ingen adgang til at oprette sager i denne kø '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Ingen adgang til at oprette brugere"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Ingen adgang til at vise denne sag"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "Ingen tilladelse til at gemme søgninger for hele systemet"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Ingen adgang til at se opdater sag"
+
+#: lib/RT/Queue_Overlay.pm:795
+#: lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Ingen principal angivet"
+
+#: html/Admin/Queues/People.html:175
+#: html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Ingen principaler valgt."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Ingen køer fundet, der opfylder søgekriterier."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Ingen rettigheder fundet"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Ingen rettigheder tildelt."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Ingen søgning indlæst"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Ingen søgning at arbejde med."
+
+#: html/Elements/RT__Ticket/ColumnMap:137
+#: html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Intet emne"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Intet sagsnummer angivet"
+
+#: lib/RT/Transaction_Overlay.pm:528
+#: lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Ingen transaktionstype angivet"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Ingen brugere fundet, der opfylder søgekriterier"
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Ingen gyldig RT-bruger fundet. RT CVS-administrator frakoblet. Kontakt venligst din RT-administrator.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Ingen værdi sendt til _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Ingen"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Ikke-eksisterende felt?"
+
+#: html/Search/Chart:71
+#: html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr "Ikke sat"
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Ikke logget ind"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Ikke logget ind."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Ikke sat"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Endnu ikke implementeret."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Endnu ikke implementeret..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Noter"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Besked kunne ikke sendes"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Giv besked til AdminCc'ere"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Giv besked til AdminCc'ere som kommentar"
+
+#: etc/initialdata:93
+#: etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Giv besked til Cc'ere"
+
+#: etc/initialdata:89
+#: etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Giv besked til Cc'ere som kommentarer"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Giv besked til andre modtagere"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Giv besked til andre modtagere som kommentar"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Giv besked til ejer"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Giv besked til ejer som kommentar"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Giv besked til ejer om deres afviste sag"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Giv besked til ejere om, at deres sag er blevet godkendt af alle godkendere"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Giv besked til ejere om, at deres sag er blevet godkendt af nogle godkendere"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Giv besked til ejere og AdminCc'ere om nye emner, som afventer deres godkendelse"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Giv besked til rekvirenter"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Giv besked til rekvirenter og Cc'ere"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Giv besked til rekvirenter og Cc'ere som kommentar"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Giv besked til rekvirenter, Cc'ere og AdminCc'ere"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Giv besked til rekvirenter, Cc'ere og AdminCc'ere som kommentar"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "ELLER"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objekt kunne ikke oprettes"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Objekt kunne ikke slettes"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objekt oprettet"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objekt slettet"
+
+#. ($LookupType)
+#. ($ObjectType)
+#: html/Admin/CustomFields/Objects.html:72
+#: html/Admin/Elements/ObjectCustomFields:63
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Objekt af typen %1 kan ikke håndtere ekstrafelter"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Objekttype mismatch"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Okt."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Off-line"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Off-line redigeringer"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Off-line dataoverførsel"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Ved"
+
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+#: lib/RT/Transaction_Overlay.pm:326
+msgid "On %1, %2 wrote:"
+msgstr "PÃ¥ %1, skrev %2:"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Ved kommentar"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Ved korrespondance"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Ved oprettelse"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Ved ændring af ejer"
+
+#: etc/initialdata:177
+#: etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Ved ændring af prioritet"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Ved ændring af kø"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Ved løsning"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Ved statusændring"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Ved transaktion"
+
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+#: html/Approvals/Elements/PendingMyApproval:70
+msgid "Only show approvals for requests created after %1"
+msgstr "Vis kun godkendelser for sager oprettet efter %1"
+
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+#: html/Approvals/Elements/PendingMyApproval:68
+msgid "Only show approvals for requests created before %1"
+msgstr "Vis kun godkendelser for sager oprettet før %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Vis kun ekstrafelter for:"
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Ã…ben"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "Ã…bne sager"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Ã…bn den"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Ã…bne sager"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Ã…bne sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Ã…bn sager (fra liste) i et nyt vindue"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Ã…bn sager (fra liste) i et andet vindue"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Ã…bn sager ved korrespondance"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Valgmuligheder"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Sorteret efter"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Sortering"
+
+#: html/Admin/Users/Modify.html:141
+#: html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organisation"
+
+#. ($approving->Id, $approving->Subject)
+#: html/Approvals/Elements/Approve:53
+msgid "Originating ticket: #%1"
+msgstr "Oprindelig sag: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Udgående e-mail omkring en kommentar er gemt"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Udgående e-mail gemt"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Tid overskredet, prioritet går mod"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Egne sager"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "EgenSag"
+
+#: etc/initialdata:38
+#: html/Elements/QuickCreate:56
+#: html/Search/Elements/PickBasics:101
+#: html/Ticket/Create.html:72
+#: html/Ticket/Elements/EditBasics:61
+#: html/Ticket/Elements/EditPeople:64
+#: html/Ticket/Elements/EditPeople:65
+#: html/Ticket/Elements/Reminders:129
+#: html/Ticket/Elements/ShowPeople:48
+#: html/Ticket/Update.html:62
+#: lib/RT/ACE_Overlay.pm:110
+#: lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Ejer"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Ejer ændret fra %1 til %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Ejer kunne ikke defineres."
+
+#. ($Old->Name , $New->Name)
+#: lib/RT/Transaction_Overlay.pm:672
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Ejer tvungent ændret fra %1 til %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "Ejer er"
+
+#: NOT FOUND IN SOURCE
+msgid "PVCS Files"
+msgstr "PVCS-filer"
+
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+#: html/Elements/TicketList:78
+msgid "Page %1 of %2"
+msgstr "Side %1 af %2"
+
+#: html/Admin/Users/Modify.html:198
+#: html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Personsøger"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Personsøgertelefon"
+
+#: html/Elements/EditLinks:144
+#: html/Elements/EditLinks:76
+#: html/Elements/ShowLinks:68
+#: html/Ticket/Create.html:222
+#: html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Forældre"
+
+#: html/Elements/Login:95
+#: html/User/Prefs.html:105
+msgid "Password"
+msgstr "Adgangskode"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Adgangskodepåmindelse"
+
+#: lib/RT/Transaction_Overlay.pm:781
+#: lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Adgangskode ændret"
+
+#. ($RT::MinimumPasswordLength)
+#: lib/RT/User_Overlay.pm:1037
+#: lib/RT/User_Overlay.pm:214
+msgid "Password needs to be at least %1 characters long"
+msgstr "Adgangskode skal være på mindst %1 karakterer"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Adgangskode sat"
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Adgangskode for kort"
+
+#. (loc_fuzzy($msg))
+#: html/User/Prefs.html:240
+msgid "Password: %1"
+msgstr "Adgangskode: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Adgangskode: Adgang afvist"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Adgangskoder stemmer ikke overens."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Adgangskoder stemmer ikke overens. Din adgangskode er ikke blevet ændret."
+
+#: html/Ticket/Elements/ShowSummary:62
+#: html/Ticket/Elements/Tabs:119
+#: html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Personer"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Udfør en brugerdefineret handling"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl-konfiguration"
+
+#: lib/RT/ACE_Overlay.pm:251
+#: lib/RT/ACE_Overlay.pm:257
+#: lib/RT/ACE_Overlay.pm:580
+#: lib/RT/ACE_Overlay.pm:590
+#: lib/RT/ACE_Overlay.pm:600
+#: lib/RT/ACE_Overlay.pm:665
+#: lib/RT/Attribute_Overlay.pm:158
+#: lib/RT/Attribute_Overlay.pm:164
+#: lib/RT/Attribute_Overlay.pm:405
+#: lib/RT/Attribute_Overlay.pm:414
+#: lib/RT/Attribute_Overlay.pm:427
+#: lib/RT/CurrentUser.pm:116
+#: lib/RT/CurrentUser.pm:125
+#: lib/RT/CustomField_Overlay.pm:1017
+#: lib/RT/CustomField_Overlay.pm:1138
+#: lib/RT/CustomField_Overlay.pm:1281
+#: lib/RT/CustomField_Overlay.pm:172
+#: lib/RT/CustomField_Overlay.pm:189
+#: lib/RT/CustomField_Overlay.pm:200
+#: lib/RT/CustomField_Overlay.pm:374
+#: lib/RT/CustomField_Overlay.pm:403
+#: lib/RT/CustomField_Overlay.pm:763
+#: lib/RT/CustomField_Overlay.pm:936
+#: lib/RT/CustomField_Overlay.pm:971
+#: lib/RT/Group_Overlay.pm:1117
+#: lib/RT/Group_Overlay.pm:1121
+#: lib/RT/Group_Overlay.pm:1130
+#: lib/RT/Group_Overlay.pm:1240
+#: lib/RT/Group_Overlay.pm:1244
+#: lib/RT/Group_Overlay.pm:1250
+#: lib/RT/Group_Overlay.pm:445
+#: lib/RT/Group_Overlay.pm:542
+#: lib/RT/Group_Overlay.pm:620
+#: lib/RT/Group_Overlay.pm:628
+#: lib/RT/Group_Overlay.pm:726
+#: lib/RT/Group_Overlay.pm:730
+#: lib/RT/Group_Overlay.pm:736
+#: lib/RT/Group_Overlay.pm:922
+#: lib/RT/Group_Overlay.pm:926
+#: lib/RT/Group_Overlay.pm:939
+#: lib/RT/Queue_Overlay.pm:1054
+#: lib/RT/Queue_Overlay.pm:140
+#: lib/RT/Queue_Overlay.pm:158
+#: lib/RT/Queue_Overlay.pm:657
+#: lib/RT/Queue_Overlay.pm:667
+#: lib/RT/Queue_Overlay.pm:681
+#: lib/RT/Queue_Overlay.pm:819
+#: lib/RT/Queue_Overlay.pm:828
+#: lib/RT/Queue_Overlay.pm:841
+#: lib/RT/Scrip_Overlay.pm:149
+#: lib/RT/Scrip_Overlay.pm:160
+#: lib/RT/Scrip_Overlay.pm:224
+#: lib/RT/Scrip_Overlay.pm:538
+#: lib/RT/Template_Overlay.pm:108
+#: lib/RT/Template_Overlay.pm:277
+#: lib/RT/Ticket_Overlay.pm:1357
+#: lib/RT/Ticket_Overlay.pm:1367
+#: lib/RT/Ticket_Overlay.pm:1381
+#: lib/RT/Ticket_Overlay.pm:1522
+#: lib/RT/Ticket_Overlay.pm:1532
+#: lib/RT/Ticket_Overlay.pm:1546
+#: lib/RT/Ticket_Overlay.pm:1663
+#: lib/RT/Ticket_Overlay.pm:1983
+#: lib/RT/Ticket_Overlay.pm:2126
+#: lib/RT/Ticket_Overlay.pm:2296
+#: lib/RT/Ticket_Overlay.pm:2346
+#: lib/RT/Ticket_Overlay.pm:2525
+#: lib/RT/Ticket_Overlay.pm:2538
+#: lib/RT/Ticket_Overlay.pm:2614
+#: lib/RT/Ticket_Overlay.pm:2627
+#: lib/RT/Ticket_Overlay.pm:2748
+#: lib/RT/Ticket_Overlay.pm:2762
+#: lib/RT/Ticket_Overlay.pm:2990
+#: lib/RT/Ticket_Overlay.pm:3000
+#: lib/RT/Ticket_Overlay.pm:3005
+#: lib/RT/Ticket_Overlay.pm:3224
+#: lib/RT/Ticket_Overlay.pm:3228
+#: lib/RT/Ticket_Overlay.pm:3371
+#: lib/RT/Ticket_Overlay.pm:3497
+#: lib/RT/Transaction_Overlay.pm:516
+#: lib/RT/Transaction_Overlay.pm:523
+#: lib/RT/Transaction_Overlay.pm:551
+#: lib/RT/Transaction_Overlay.pm:558
+#: lib/RT/User_Overlay.pm:1176
+#: lib/RT/User_Overlay.pm:1856
+#: lib/RT/User_Overlay.pm:369
+#: lib/RT/User_Overlay.pm:735
+#: lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Adgang afvist"
+
+#: lib/RT/Template_Overlay.pm:238
+#: lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "Adgang afvist"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "Tilladelser ikke givet"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Personlige grupper"
+
+#: html/User/Groups/index.html:51
+#: html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Personlige grupper"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Personlige grupper:"
+
+#: html/Admin/Users/Modify.html:180
+#: html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefonnumre"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Placeholder"
+
+#: html/Elements/Header:93
+#: html/Elements/Tabs:91
+#: html/SelfService/Elements/Tabs:95
+#: html/SelfService/Prefs.html:46
+#: html/User/Prefs.html:46
+#: html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Præferencer"
+
+#. ($pane, $UserObj->Name)
+#: html/Admin/Users/MyRT.html:75
+msgid "Preferences %1 for user %2 ."
+msgstr "Præferencer %1 for bruger %2 ."
+
+#. ($pane)
+#: html/Prefs/MyRT.html:141
+msgid "Preferences saved for %1."
+msgstr "Præferencer gemt for %1."
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Præferencer"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Forbered tømning"
+
+#: html/Helpers/CalPopup.html:56
+#: html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Forr."
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Forrige side"
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Forrige side"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#. ($args{'PrincipalId'})
+#: lib/RT/ACE_Overlay.pm:157
+#: lib/RT/ACE_Overlay.pm:239
+#: lib/RT/ACE_Overlay.pm:569
+msgid "Principal %1 not found."
+msgstr "Principal %1 ikke fundet."
+
+#: html/Search/Elements/PickBasics:147
+#: html/Ticket/Create.html:181
+#: html/Ticket/Elements/EditBasics:92
+#: html/Ticket/Elements/ShowBasics:72
+#: lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioritet"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Prioritet starter ved"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Privat:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilegeret"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/Users/Modify.html:342
+#: html/User/Prefs.html:231
+msgid "Privileged status: %1"
+msgstr "Privilegeret status: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Privilegerede brugere"
+
+#: NOT FOUND IN SOURCE
+msgid "Product area"
+msgstr "Produktområde"
+
+#: etc/initialdata:23
+#: etc/initialdata:29
+#: etc/initialdata:35
+#: etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudogruppe til internt brug"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "Søgning"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Søgedefinition"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "Søgning:"
+
+#: html/Elements/QueueSummary:48
+#: html/Elements/QuickCreate:54
+#: html/Search/Elements/PickBasics:71
+#: html/SelfService/Create.html:54
+#: html/Ticket/Create.html:62
+#: html/Ticket/Elements/EditBasics:57
+#: html/Ticket/Elements/ShowBasics:76
+#: html/Tools/Reports/CreatedByDates.html:85
+#: html/Tools/Reports/ResolvedByDates.html:86
+#: html/Tools/Reports/ResolvedByOwner.html:66
+#: html/User/Elements/DelegateRights:101
+#: lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Kø"
+
+#. ($id)
+#. ($Queue)
+#: html/Admin/Queues/CustomField.html:63
+#: html/Admin/Queues/Scrip.html:61
+#: html/Admin/Queues/Scrips.html:69
+#: html/Admin/Queues/Templates.html:65
+msgid "Queue %1 not found"
+msgstr "Kø %1 ikke fundet"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Kø '%1' ikke fundet\\n"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Navn på kø"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Kø-scrips"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Kø eksisterer allerede"
+
+#: lib/RT/Queue_Overlay.pm:374
+#: lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Kø kunne ikke oprettes"
+
+#: html/Ticket/Create.html:244
+#: lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Kø kunne ikke indlæses."
+
+#: docs/design_docs/string-extraction-guide.txt:83
+#: lib/RT/Queue_Overlay.pm:384
+#: lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Kø oprettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Kø ikke angivet."
+
+#: html/SelfService/Display.html:126
+#: lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Kø ikke fundet"
+
+#: html/Admin/Elements/Tabs:59
+#: html/Admin/index.html:72
+msgid "Queues"
+msgstr "Køer"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Køer som jeg administrerer"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Køer: Jeg er en AdminCc for"
+
+#: html/Elements/Quicksearch:47
+#: html/Prefs/Elements/Tabs:58
+#: html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Hurtigsøgning"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Hurtigoprettelse af sager"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#. ($RT::VERSION, $RT::rtname)
+#: docs/design_docs/string-extraction-guide.txt:70
+#: lib/RT/StyleGuide.pod:796
+msgid "RT %1 for %2"
+msgstr "RT %1 for %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 fra <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46
+#: html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT-administration"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "RT-godkendelsesfejl."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT-afvisning: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "RT-konfigurationsfejl"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "RT kritisk fejl. Meddelelse ikke gemt!"
+
+#: html/Elements/Error:63
+#: html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT-fejl"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT modtog e-mail (%1) fra sig selv"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT selvbetjening / lukkede sager"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RT variable faktorer"
+
+#: html/Admin/Elements/SystemTabs:71
+#: html/Admin/Elements/UserTabs:67
+#: html/Admin/Global/MyRT.html:1
+#: html/Admin/Global/MyRT.html:12
+#: html/Admin/Global/MyRT.html:4
+#: html/Admin/Global/index.html:84
+#: html/Admin/Users/MyRT.html:21
+#: html/Prefs/MyRT.html:66
+#: html/Prefs/MyRT.html:78
+#: html/User/Elements/Tabs:65
+#: html/index.html:1
+#: html/index.html:75
+msgid "RT at a glance"
+msgstr "Request Tracker - hurtigt overblik"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/MyRT.html:30
+msgid "RT at a glance for the user %1"
+msgstr "Request Tracker - hurtigt overblik for bruger %1"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT kan medtage indhold fra en anden web-service, når dette ekstrafelt vises."
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT kan gøre dette ekstrafelts værdier til hyperlinks til en anden tjeneste."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT kunne ikke godkende dig"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT kunne ikke finde rekvirent via sit eksterne databaseopslag"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT kunne ikke finde køen: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT kunne ikke gemme din session."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT kunne ikke validere denne PGP-signatur. \\n"
+
+#. ($RT::rtname)
+#: html/Elements/Logo:49
+#: html/Elements/PageLayout:172
+msgid "RT for %1"
+msgstr "RT for %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT for %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT har behandlet dine kommandoer"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT er &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Bliver distribueret under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 af 'GNU General Public License'.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT tror, at denne besked er en afvisning"
+
+#: html/Search/Simple.html:60
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT vil kigge efter alt andet, som du indtaster i sagsemnerne."
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT vil behandle denne besked, som om den ikke var underskrevet.\\n"
+
+#: html/Admin/CustomFields/Modify.html:108
+#: html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT vil erstatte <tt>__ID__</tt> og <tt>__EkstraFelt__</tt> med henholdsvis sagsnummer og ekstrafeltværdi"
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RT's e-mail-kommandotilstand kræver PGP-verificering. Enten har du ikke underskrevet din besked, eller også kunne din signatur ikke verificeres."
+
+#: html/Admin/Users/Modify.html:79
+#: html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Fulde navn"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "FuldeNavn"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:725
+msgid "Reference by %1 added"
+msgstr "Reference fra %1 tilføjet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:765
+msgid "Reference by %1 deleted"
+msgstr "Reference fra %1 slettet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:722
+msgid "Reference to %1 added"
+msgstr "Reference til %1 tilføjet"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:762
+msgid "Reference to %1 deleted"
+msgstr "Reference til %1 slettet"
+
+#: html/Elements/EditLinks:103
+#: html/Elements/EditLinks:156
+#: html/Elements/ShowLinks:92
+#: html/Ticket/Create.html:225
+#: html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Refereret til af"
+
+#: html/Elements/EditLinks:152
+#: html/Elements/EditLinks:94
+#: html/Elements/SelectLinkType:49
+#: html/Elements/ShowLinks:82
+#: html/Ticket/Create.html:224
+#: html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Refererer til"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Præciser"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Præciser søgning"
+
+#. ($value/60)
+#: html/Elements/Refresh:57
+msgid "Refresh this page every %1 minutes."
+msgstr "Opdater denne side hver %1 minut."
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:811
+msgid "Reminder '%1' added"
+msgstr "Påmindelse '%1' tilføjet"
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:824
+msgid "Reminder '%1' completed"
+msgstr "PÃ¥mindelse '%1' afsluttet"
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:817
+msgid "Reminder '%1' reopened"
+msgstr "Påmindelse '%1' genåbnet"
+
+#. ($Ticket->Id)
+#: html/Ticket/Reminders.html:46
+msgid "Reminder ticket #%1"
+msgstr "PÃ¥mindelse for sag #%1"
+
+#: html/Elements/MyReminders:48
+#: html/Ticket/Elements/ShowSummary:75
+#: html/Ticket/Elements/Tabs:122
+#: html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "PÃ¥mindelser"
+
+#. ($Ticket->Id)
+#: html/Ticket/Reminders.html:50
+msgid "Reminders for ticket #%1"
+msgstr "PÃ¥mindelser for sag #%1"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Fjern AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Fjern Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Fjern rekvirent"
+
+#: html/Ticket/Elements/ShowTransaction:179
+#: html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Svar"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Svaradresse"
+
+#: html/Search/Bulk.html:129
+#: html/Ticket/ModifyAll.html:94
+#: html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Svar til rekvirenter"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Svar på sager"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "SvarPÃ¥Sag"
+
+#: html/Tools/Elements/Tabs:59
+#: html/Tools/Reports/index.html:46
+#: html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Rapporter"
+
+#: etc/initialdata:44
+#: lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Rekvirent"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Rekvirent e-mail-adresse"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Rekvirent(er)"
+
+#: html/SelfService/Create.html:63
+#: html/Ticket/Create.html:80
+#: html/Ticket/Elements/EditPeople:69
+#: html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Rekvirenter"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Sager skulle være forfaldne om"
+
+#. ('Object')
+#: lib/RT/Attribute_Overlay.pm:146
+msgid "Required parameter '%1' not specified"
+msgstr "Påkrævet parameter '%1' ikke angivet"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Nulstil"
+
+#: html/Admin/Users/MyRT.html:15
+#: html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr "Sæt tilbage til standard"
+
+#: html/Admin/Users/Modify.html:183
+#: html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Hjemme"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Løs"
+
+#. ($TicketObj->id, $TicketObj->Subject)
+#: html/Ticket/Update.html:156
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Løs sag #%1 (%2)"
+
+#: etc/initialdata:323
+#: html/Elements/SelectDateType:49
+#: lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Løst"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Løst af ejer"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Løst i datorækkefølge"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Sager løst i en periode, fordelt pr. ejer"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Sager løst, fordelt pr. ejer"
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Svar til rekvirenter"
+
+#: html/Elements/ListActions:46
+#: html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Resultater"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Resultater pr. side"
+
+#: html/Admin/Users/Modify.html:126
+#: html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Gentag adgangskoden"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "GÃ¥ tilbage"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Rettighed %1 ikke fundet for %2 %3 i område %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Rettighed overdraget"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Rettighed tildelt"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Rettighed indlæst"
+
+#: lib/RT/ACE_Overlay.pm:695
+#: lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Rettighed kunne ikke inddrages"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Rettighed ikke fundet"
+
+#: lib/RT/ACE_Overlay.pm:560
+#: lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Rettighed ikke indlæst."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Rettighed inddraget"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Rettigheder"
+
+#. ($object_type)
+#: html/Admin/CustomFields/GroupRights.html:129
+#: lib/RT/Interface/Web.pm:961
+msgid "Rights could not be granted for %1"
+msgstr "Rettigheder kunne ikke tildeles %1"
+
+#. ($object_type)
+#: html/Admin/CustomFields/GroupRights.html:156
+#: lib/RT/Interface/Web.pm:990
+msgid "Rights could not be revoked for %1"
+msgstr "Rettigheder kunne ikke inddrages for %1"
+
+#: html/Admin/Global/GroupRights.html:72
+#: html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Roller"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "RodGodkendelse"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Rækker pr. boks"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Rækker pr. side"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Lør."
+
+#: html/Prefs/MyRT.html:72
+#: html/Prefs/Quicksearch.html:64
+#: html/Prefs/Search.html:69
+#: html/Search/Elements/EditSearches:70
+#: html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Gem"
+
+#: html/Admin/Global/Template.html:67
+#: html/Admin/Groups/Modify.html:88
+#: html/Admin/Queues/Modify.html:111
+#: html/Admin/Queues/People.html:126
+#: html/Admin/Users/Modify.html:239
+#: html/Prefs/Quicksearch.html:64
+#: html/Prefs/SearchOptions.html:63
+#: html/SelfService/Prefs.html:58
+#: html/Ticket/Modify.html:60
+#: html/Ticket/ModifyAll.html:127
+#: html/Ticket/ModifyDates.html:60
+#: html/Ticket/ModifyLinks.html:61
+#: html/Ticket/ModifyPeople.html:60
+#: html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Gem ændringer"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Gem præferencer"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Gem ændringer"
+
+#. ($name)
+#: lib/RT/SavedSearch.pm:173
+msgid "Saved search %1"
+msgstr "Gemt søgning %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Gemte søgninger"
+
+#. ($id)
+#. ($scrip->Id)
+#: html/Admin/Elements/ListGlobalScrips:60
+#: html/Admin/Global/Scrip.html:77
+#: html/Admin/Queues/Scrip.html:84
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip oprettet"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Scrip-felter"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip slettet"
+
+#: html/Admin/Elements/QueueTabs:67
+#: html/Admin/Elements/SystemTabs:54
+#: html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Scrip for %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrips der gælder for alle køer"
+
+#: html/Elements/SimpleSearch:48
+#: html/Search/Simple.html:65
+msgid "Search"
+msgstr "Søg"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Søgekriterier"
+
+#: html/Prefs/SearchOptions.html:47
+#: html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Søgepræferencer"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Indlæsningsfejl på søgeegenskab"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Søg efter godkendelser"
+
+#: html/Search/Simple.html:69
+msgid "Search for tickets"
+msgstr "Søg efter sager"
+
+#: html/Search/Simple.html:57
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name"
+msgstr "Søg efter sager. Indtast <strong>ID</strong>-numre, <strong>køer</strong> inddelt efter navn."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Søgemuligheder"
+
+#. ($PrimaryGroupBy)
+#: html/Search/Chart.html:56
+msgid "Search results grouped by %1"
+msgstr "Søgeresultater fordelt pr. %1"
+
+#. ($msg)
+#: lib/RT/SavedSearch.pm:203
+msgid "Search update: %1"
+msgstr "Søgeopdatering: %1"
+
+#: html/Search/Simple.html:59
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "Det kan tage lang tid at søge på den fulde tekst i hver sag, men hvis det er nødvendigt at gøre det, kan du søge efter hvilket som helst ord i den komplette sagshistorik ved at taste <b>fulltext:<i>ord</i></b>."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Sikkerhed:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "Se også:"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Se ekstrafelter"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Se udgående e-mail-beskeder og deres modtagere"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Se private kommentarer for sager"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Se resumé af sager"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "SeEkstrafelt"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "SeGruppe"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "SeKø"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "Vælg"
+
+#: html/Admin/CustomFields/index.html:46
+#: html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Vælg et ekstrafelt"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Vælg en gruppe"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Vælg en kø"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Vælg en kø til din nye sag"
+
+#: html/Admin/Users/index.html:46
+#: html/Admin/Users/index.html:49
+#: html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Vælg en bruger"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Vælg ekstrafelt"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Vælg ekstrafelter for alle brugergrupper"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Vælg ekstrafelter for alle brugere"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Vælg ekstrafelter for sager i alle køer"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Vælg ekstrafelter for transaktioner på sager i alle køer"
+
+#: html/Admin/Elements/GroupTabs:75
+#: html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Vælg gruppe"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Vælg flere værdier"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Vælg en værdi"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Vælg kø"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Vælg køer, der skal vises på siden \"Request Tracker - hurtigt overblik\""
+
+#: html/Admin/Global/Scrip.html:59
+#: html/Admin/Global/Scrips.html:57
+#: html/Admin/Queues/Scrip.html:67
+#: html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Vælg scrip"
+
+#: html/Admin/Global/Template.html:78
+#: html/Admin/Global/Templates.html:57
+#: html/Admin/Queues/Template.html:76
+#: html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Vælg skabelon"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Vælg op til %1 værdier"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Vælg bruger"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "VælgFlere"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "VælgEnkelt"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Valgte ekstrafelter"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Valgte objekter"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Det udvalgte er ændret. Gem dine ændringer."
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Selvbetjening"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Send e-mail til alle observatører"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Send e-mail til alle observatører som en \"kommentar\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Send e-mail til rekvirenter og Cc'ere"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Send e-mail til rekvirent og Cc'ere som en kommentar"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Sender en besked til rekvirenterne"
+
+#: etc/initialdata:125
+#: etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Sender e-mail til synligt angivne Cc'ere og Bcc'ere"
+
+#: etc/initialdata:94
+#: etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Sender e-mail til Cc'erne"
+
+#: etc/initialdata:90
+#: etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Sender e-mail to Cc'ere som en kommentar"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Sender e-mail til de administrative Cc'ere"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Sender e-mail til de administrative Cc'ere som en kommentar"
+
+#: etc/initialdata:82
+#: etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Sender e-mail til ejeren"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Vis"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Vis godkendelser"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Vis kolonner"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Vis resultater"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Vis godkendte sager"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Vis stamdata"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Vis afviste sager"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Vis detaljer"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Vis afventende sager"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Vis sager, som afventer andre godkendelser"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Vis private kommentarer for sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Vis resumé for sager"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "VisACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "VisKonfigTab"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "VisUdgåendeEmail"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "VisGemteSøgninger"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "VisScrips"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "VisSkabelon"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "VisSag"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "VisSagKommentarer"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Tilmeld som sagsrekvirent, sag eller kø-Cc"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Tilmeld som sag eller kø-AdminCc"
+
+#: html/Admin/Users/Modify.html:230
+#: html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Signatur"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "Logget ind som %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Enkel søgning"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Enkelt"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Størrelse"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Spring menu over"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Lille"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "Nogle browsere indlæser kun indhold fra det samme domæne som din RT-server."
+
+#: html/Admin/Elements/AddCustomFieldValue:49
+#: html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sorter"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Sorteringsnøgle"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Sorter resultat efter"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "SorteringsRækkefølge"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "Trin"
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Sat i bero"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Startside"
+
+#: html/Elements/SelectDateType:48
+#: html/Ticket/Elements/EditDates:53
+#: html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Startet"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Startdato '%1' kunne ikke fortolkes"
+
+#: html/Elements/SelectDateType:52
+#: html/Ticket/Create.html:208
+#: html/Ticket/Elements/EditDates:48
+#: html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Starter"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Starter den"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Startdato '%1' kunne ikke fortolkes"
+
+#: html/Admin/Users/Modify.html:162
+#: html/User/Prefs.html:145
+msgid "State"
+msgstr "Stat"
+
+#: html/Search/Elements/PickBasics:87
+#: html/SelfService/Update.html:57
+#: html/Ticket/Create.html:66
+#: html/Ticket/Elements/EditBasics:53
+#: html/Ticket/Elements/ShowBasics:52
+#: html/Ticket/Update.html:59
+#: lib/RT/Ticket_Overlay.pm:1166
+#: lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Status"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Statusændring"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Status ændret fra %1 til %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "StatusÆndring"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Stjæl"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Stjæl sager"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "StjælSag"
+
+#. ($Old->Name)
+#: lib/RT/Transaction_Overlay.pm:678
+msgid "Stolen from %1"
+msgstr "Stjålet fra %1"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Stil"
+
+#: html/Elements/QuickCreate:52
+#: html/Elements/SelectAttachmentField:47
+#: html/Search/Bulk.html:132
+#: html/SelfService/Create.html:79
+#: html/SelfService/Update.html:65
+#: html/Ticket/Create.html:108
+#: html/Ticket/Elements/EditBasics:48
+#: html/Ticket/Elements/Reminders:125
+#: html/Ticket/ModifyAll.html:100
+#: html/Ticket/Update.html:82
+#: lib/RT/Ticket_Overlay.pm:1162
+#: lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Emne"
+
+#. ($self->Data)
+#: docs/design_docs/string-extraction-guide.txt:89
+#: lib/RT/StyleGuide.pod:815
+#: lib/RT/Transaction_Overlay.pm:700
+msgid "Subject changed to %1"
+msgstr "Emne ændret til %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Indsend"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Godkend arbejdsgang"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Lykkedes"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Søn."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "Superbruger"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "System"
+
+#: html/Admin/Elements/ToolTabs:54
+#: html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Systemkonfiguration"
+
+#: html/Admin/CustomFields/GroupRights.html:128
+#: html/Admin/CustomFields/GroupRights.html:155
+#: html/Admin/CustomFields/UserRights.html:128
+#: html/Admin/CustomFields/UserRights.html:98
+#: html/Admin/Elements/SelectRights:106
+#: lib/RT/ACE_Overlay.pm:584
+#: lib/RT/Interface/Web.pm:960
+#: lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Systemfejl"
+
+#. ($msg)
+#: lib/RT/Transaction_Overlay.pm:224
+#: lib/RT/Transaction_Overlay.pm:230
+msgid "System Error: %1"
+msgstr "Systemfejl: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Systemværktøjer"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Systemfejl. Rettighed ikke overdraget."
+
+#: lib/RT/ACE_Overlay.pm:163
+#: lib/RT/ACE_Overlay.pm:228
+#: lib/RT/ACE_Overlay.pm:323
+#: lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Systemfejl. Rettighed ikke tildelt."
+
+#: html/Admin/CustomFields/GroupRights.html:58
+#: html/Admin/Global/GroupRights.html:56
+#: html/Admin/Groups/GroupRights.html:58
+#: html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Systemgrupper"
+
+#: etc/initialdata:41
+#: etc/initialdata:47
+#: etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRolleGruppe til internt brug"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRENG"
+
+#: etc/initialdata:603
+#: html/Search/Elements/EditFormat:72
+#: html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Tag"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Tag sager"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "TagSag"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Taget"
+
+#: html/Admin/Elements/EditScrip:71
+#: html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Skabelon"
+
+#. ($TemplateObj->Id())
+#: html/Admin/Global/Template.html:112
+#: html/Admin/Queues/Template.html:113
+msgid "Template #%1"
+msgstr "Skabelon #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Skabelon slettet"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr "Skabelonen er en obligatorisk parameter"
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Skabelon ikke fundet"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Skabelon ikke fundet\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Skabelon fortolket"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "Skabelonfortolkningsfejl"
+
+#: html/Admin/Elements/QueueTabs:70
+#: html/Admin/Elements/SystemTabs:57
+#: html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Skabeloner"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Skabeloner til %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "Tekst"
+
+#: lib/RT/CustomField_Overlay.pm:943
+#: lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Det er allerede den nuværende værdi"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Det er ikke værdi for dette ekstrafelt"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Det er den samme værdi"
+
+#: lib/RT/ACE_Overlay.pm:305
+#: lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Den principal har allerede den rettighed"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:753
+msgid "That principal is already a %1 for this queue"
+msgstr "Den principal er allerede en %1 for denne kø"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1435
+msgid "That principal is already a %1 for this ticket"
+msgstr "Den principal er allerede en %1 for denne sag"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:852
+msgid "That principal is not a %1 for this queue"
+msgstr "Den principal er ikke en %1 for denne kø"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Den principal er ikke en %1 for denne sag"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Den kø eksisterer ikke"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Denne sag har uløste afhængigheder"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Den bruger har allerede den rettighed"
+
+#: lib/RT/Action/CreateTickets.pm:710
+#: lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Brugeren ejer allerede den sag"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Brugeren eksisterer ikke"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Brugeren er allerede privilegeret"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Brugeren er allerede uprivilegeret"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Brugeren er nu privilegeret"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Brugeren er nu uprivilegeret"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Den bruger må ikke eje sager i den kø"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Dette er ikke et numerisk ID"
+
+#: html/SelfService/Display.html:53
+#: html/Ticket/Create.html:177
+#: html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Stamdata"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "En sags CC"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "En sags administrative CC"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Kommentaren er gemt"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Den følgende kommando vil finde alle aktive sager i køen 'generel' og vil sætte deres prioritet til 99, hvis de ikke er blevet rørt i 4 timer:"
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"The following commands were not proccessed:\\n"
+"\\n"
+msgstr ""
+"Følgende kommandoer blev ikke behandlet:\\n"
+"\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Den nye værdi er sat."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Sagens ejer"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Sagens rekvirent"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Disse kommentarer er normalt ikke synlige for brugeren"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Dette ekstrafelt gælder ikke for dette objekt"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Denne funktion er kun tilgængelig for systemadministratorer"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Denne besked vil blive sendt til..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Denne sag %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Dette værktøj tillader brugeren at benytte vilkårlige Perl-moduler inde fra RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Denne transaktion har tilsyneladende ikke noget indhold"
+
+#. ($rows)
+#: html/Ticket/Elements/ShowRequestor:70
+msgid "This user's %1 highest priority tickets"
+msgstr "Denne brugers %1 højest prioriterede sager"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "Denne bruger 25 højest prioriterede sager"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Tors."
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Sag # %1 %2"
+
+#. ($Ticket->Id, $Ticket->Subject)
+#: html/Ticket/ModifyAll.html:46
+#: html/Ticket/ModifyAll.html:50
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Sag #%1 Jumbo-opdatering: %2"
+
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#: html/Approvals/Elements/ShowDependency:67
+msgid "Ticket #%1: %2"
+msgstr "Sag #%1: %2"
+
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+#: lib/RT/Action/CreateTickets.pm:1350
+#: lib/RT/Action/CreateTickets.pm:1359
+#: lib/RT/Action/CreateTickets.pm:605
+#: lib/RT/Action/CreateTickets.pm:729
+#: lib/RT/Action/CreateTickets.pm:741
+msgid "Ticket %1"
+msgstr "Sag %1"
+
+#. ($self->Id, $QueueObj->Name)
+#: lib/RT/Ticket_Overlay.pm:755
+#: lib/RT/Ticket_Overlay.pm:775
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Sag %1 oprettet i kø '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Sag %1 indlæst\\n"
+
+#. ($Ticket->Id, $_)
+#: html/Search/Bulk.html:377
+msgid "Ticket %1: %2"
+msgstr "Sag %1 : %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Sagens ekstrafelter"
+
+#. ($Ticket->Id, $Ticket->Subject)
+#: html/Ticket/History.html:46
+#: html/Ticket/History.html:49
+msgid "Ticket History # %1 %2"
+msgstr "Sagshistorik # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Sagsnummer"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Sag løst"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69
+#: html/Admin/Global/CustomFields/index.html:81
+#: lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Sagstransaktioner"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Vedhæftet fil til sag"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Sagsindhold"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Sagsindholdstype"
+
+#: lib/RT/Ticket_Overlay.pm:603
+#: lib/RT/Ticket_Overlay.pm:617
+#: lib/RT/Ticket_Overlay.pm:628
+#: lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Sag kunne ikke oprettes på grund af en intern fejl"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Sag oprettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Oprettelse af sag mislykkedes"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Sag slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Sagsnummer ikke fundet"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Sagens metadata"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Sag ikke fundet"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Satsstatus ændret"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Sagsobservatører"
+
+#. (ref $self)
+#: lib/RT/Search/FromSQL.pm:82
+msgid "TicketSQL search module"
+msgstr "SagSQL søgemodul"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64
+#: html/Admin/Global/CustomFields/index.html:75
+#: html/Elements/Tabs:71
+#: html/Search/Elements/Chart:109
+#: lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Sager %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Sager %1 af %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "Sager oprettet efter"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "Sager oprettet før"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Sager fra %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "Sager løst efter"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "Sager løst før"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Sager som afhænger af denne godkendelse:"
+
+#: html/Search/Elements/PickBasics:134
+#: html/Ticket/Create.html:183
+#: html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Anslået tid"
+
+#: html/Search/Elements/PickBasics:135
+#: html/Ticket/Create.html:196
+#: html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Resterende tid"
+
+#: html/Search/Elements/PickBasics:133
+#: html/Ticket/Create.html:189
+#: html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Tid brugt"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Resterende tid"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Tid for sidevisning"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Tid brugt"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "TidBrugt"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Titel"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "For at generere en difference af denne beslutning:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "For at generere en difference af denne beslutning:\\n"
+
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+#: html/Elements/Footer:62
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Ved forespørgsel om support, undervisning, specialudvikling eller licenser, kontakt venligst %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Meddelt"
+
+#: html/Admin/Elements/Tabs:68
+#: html/Admin/index.html:88
+#: html/Elements/Tabs:74
+#: html/Tools/index.html:46
+#: html/Tools/index.html:49
+msgid "Tools"
+msgstr "Værktøjer"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "Total"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transaktion"
+
+#. ($self->Data)
+#: lib/RT/Transaction_Overlay.pm:805
+msgid "Transaction %1 purged"
+msgstr "Transaktion %1 tømt"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transaktion oprettet"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Transaktions-ekstrafelter"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Transaktion->Kunne ikke oprette, fordi du ikke angav et sagsnummer"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transaktion->Kunne ikke oprette, fordi ikke angav en objekttype og et ID"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transaktioner kan ikke ændres"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Forsøger at slette en rettighed: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Tirs."
+
+#: html/Admin/CustomFields/Modify.html:66
+#: html/Admin/Elements/EditCustomField:65
+#: html/Ticket/Elements/AddWatchers:54
+#: html/Ticket/Elements/AddWatchers:65
+#: html/Ticket/Elements/AddWatchers:75
+#: lib/RT/Ticket_Overlay.pm:1168
+#: lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Type"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Ikke implementeret"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unix log-ind"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "UnixBrugernavn"
+
+#. ($ContentEncoding)
+#. ($self->ContentEncoding)
+#: lib/RT/Attachment_Overlay.pm:289
+#: lib/RT/Record.pm:861
+msgid "Unknown ContentEncoding %1"
+msgstr "Ukendt IndholdsKodning %1"
+
+#: html/Search/Build.html:455
+#: lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Ukendt felt: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Ubegrænset"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Unavngiven søgning"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Uprivilegeret"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Ikke-valgte ekstrafelter"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Ikke-valgte objekter"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Ikke taget"
+
+#: html/Admin/Elements/EditScrip:128
+#: html/Elements/RT__Ticket/ColumnMap:302
+#: html/Search/Bulk.html:193
+#: html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Opdater"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Opdater alle"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Opdater ID"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Opdater sag"
+
+#: html/Search/Bulk.html:126
+#: html/Ticket/ModifyAll.html:87
+#: html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Opdater type"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Opdater alle disse sager på en gang"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Opdater e-mail"
+
+#: html/Search/Bulk.html:200
+#: html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Opdater flere sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Opdater navn"
+
+#: lib/RT/Action/CreateTickets.pm:750
+#: lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Opdatering ikke registreret."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Opdater valgte sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Opdater signatur"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Opdater sag"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Opdater sag # %1"
+
+#. ($Ticket->id)
+#: html/SelfService/Update.html:112
+#: html/SelfService/Update.html:47
+msgid "Update ticket #%1"
+msgstr "Opdater sag #%1"
+
+#. ($TicketObj->id, $TicketObj->Subject)
+#: html/Ticket/Update.html:158
+msgid "Update ticket #%1 (%2)"
+msgstr "Opdater sag #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748
+#: lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Opdateringstype var hverken korrespondance eller kommentar"
+
+#: html/Elements/SelectDateType:54
+#: html/Ticket/Elements/ShowDates:72
+#: lib/RT/CustomField_Overlay.pm:1284
+#: lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Opdateret"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Overfør"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Overfør flere filer"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Overfør flere billeder"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Overfør en fil"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Overfør et billede"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Overfør op til %1 filer"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Overfør op til %1 billeder"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Overfør dine ændringer"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Brug andre RT-administrative værktøjer"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Bruger %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Bruger %1 adgangskode: %2\\n"
+
+#. ($args{'Owner'})
+#: lib/RT/Ticket_Overlay.pm:506
+msgid "User '%1' could not be found."
+msgstr "Bruger '%1' kunne ikke findes"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Bruger '%1' ikke fundet"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Bruger '%1' ikke fundet\\n"
+
+#: etc/initialdata:132
+#: etc/initialdata:206
+msgid "User Defined"
+msgstr "Brugerdefineret"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Brugerdefinerede betingelser og handlinger"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "Bruger-ID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Bruger-ID"
+
+#: html/Admin/Elements/CustomFieldTabs:72
+#: html/Admin/Elements/GroupTabs:68
+#: html/Admin/Elements/QueueTabs:85
+#: html/Admin/Elements/SystemTabs:68
+#: html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Brugerrettigheder"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "Bruger bad om en ukendt opdateringstype for ekstrafelt %1 til %1 objekt #%3"
+
+#. ($msg)
+#: html/Admin/Users/Modify.html:301
+msgid "User could not be created: %1"
+msgstr "Bruger kunne ikke oprettes: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Bruger oprettet"
+
+#: html/Admin/CustomFields/GroupRights.html:74
+#: html/Admin/Global/GroupRights.html:88
+#: html/Admin/Groups/GroupRights.html:75
+#: html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Brugerdefinerede grupper"
+
+#: lib/RT/User_Overlay.pm:592
+#: lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Bruger indlæst"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Sendt besked til bruger"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Brugervisning"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Brugerdefinerede grupper"
+
+#: html/Admin/Users/Modify.html:69
+#: html/Elements/Login:90
+#: html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Brugernavn"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55
+#: html/Admin/Elements/SelectNewGroupMembers:47
+#: html/Admin/Elements/Tabs:53
+#: html/Admin/Global/CustomFields/index.html:64
+#: html/Admin/Groups/Members.html:76
+#: html/Admin/Queues/People.html:89
+#: html/Admin/index.html:62
+#: html/User/Groups/Members.html:79
+#: lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Brugere"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Brugere som opfylder søgekriterier"
+
+#. ($transaction->id)
+#: bin/rt-crontool:134
+msgid "Using transaction #%1..."
+msgstr "Bruger transaktion #%1..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Gyldig søgning"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Validering"
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "KøVærdi"
+
+#: html/Admin/CustomFields/Modify.html:130
+#: html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Værdier"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Observer"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "ObserverSomAdminCC"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Observatører"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "WebKodning"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Ons."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "Hvad lavede jeg i dag"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Når en sag er blevet godkendt af alle godkendere, tilføj korrespondance til den oprindelige sag"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Når en sag er blevet godkendt af hvilken som helst godkender, tilføj korrespondance til den oprindelige sag"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "NÃ¥r en sag oprettes"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "NÃ¥r en godkendelsessag oprettes, giv besked til ejeren og AdminCc om det punkt, der afventer deres godkendelse"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "NÃ¥r der sker noget som helst"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Når en sag er blevet løst"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Når en sags ejer ændres"
+
+#: etc/initialdata:178
+#: etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Når en sags prioritet ændres"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Når en sags kø ændres"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Når en sags status ændres"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "NÃ¥r en brugerdefineret betingelse forekommer"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "NÃ¥r kommentarer kommer ind"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "NÃ¥r korrespondance kommer ind"
+
+#: html/Admin/Users/Modify.html:188
+#: html/User/Prefs.html:88
+msgid "Work"
+msgstr "Arbejde"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Arbejde off-line"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Arbejdstelefon"
+
+#: html/Ticket/Elements/ShowBasics:63
+#: html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Arbejdet"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Du ejer allerede denne sag"
+
+#: html/autohandler:214
+#: html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Du er ikke en autoriseret bruger"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "Du kan også redigere selve den foruddefinerede søgning"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Du kan kun videretildele sager, som du ejer, eller som ikke har en ejer"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr "Du kan kun tage sager uden ejer"
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "Du har ikke tilladelse til at se den sag.\\n"
+
+#. ($num, $queue)
+#: docs/design_docs/string-extraction-guide.txt:47
+#: lib/RT/StyleGuide.pod:780
+msgid "You found %1 tickets in queue %2"
+msgstr "Du fandt %1 sager i kø %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Du er blevet logget ud af RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Du har ikke tilladelse til at oprette sager i den kø."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Du må ikke oprette sager i den kø."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Du er velkommen til at logge ind igen"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Dine %1 sager"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "Din RT-administrator har ikke konfigureret de mail-alias'er, der starter RT, korrekt"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Din sag er blevet godkendt af %1. Andre godkendelser afventer muligvis stadigvæk."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Din sag er blevet godkendt."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Din sag blev afvist."
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Din sag blev afvist."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Dit brugernavn eller din adgangskode er forkert."
+
+#: html/Admin/Users/Modify.html:168
+#: html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Postnummer"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "tillad oprettelse af gemte søgninger"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "tillad indlæsning af gemte søgninger"
+
+#. ($right->PrincipalObj->Object->SelfDescription)
+#: html/User/Elements/DelegateRights:80
+msgid "as granted to %1"
+msgstr "som givet til %1"
+
+#: NOT FOUND IN SOURCE
+msgid "belongs to"
+msgstr "tilhører"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "diagram"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "lukket"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "indeholder"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "indhold"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "indholdstype"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "korrespondance (sandsynligvis) ikke sendt"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "korrespondance sendt"
+
+#: NOT FOUND IN SOURCE
+msgid "current: $current, want $want, Error near ->$val<- expecting a "
+msgstr "aktuel: $current, vil have $want, Fejl nær ->$val<- forventer "
+
+#: html/Admin/Queues/Modify.html:98
+#: lib/RT/Date.pm:346
+msgid "days"
+msgstr "dage"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "slet"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "does not belong to"
+msgstr "tilhører ikke"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "stemmer ikke overens"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "indeholder ikke"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "lig med"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "fejl: kan ikke flytte ned"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "fejl: kan ikke flytte til venstre"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "fejl: kan ikke flytte op"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "fejl: intet at slette"
+
+#: html/Search/Build.html:533
+#: html/Search/Build.html:552
+#: html/Search/Build.html:574
+#: html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "fejl: intet at flytte"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "fejl: intet at skifte"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "filnavn"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "større end"
+
+#. ($self->Name)
+#: lib/RT/Group_Overlay.pm:214
+msgid "group '%1'"
+msgstr "gruppe '%1'"
+
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+#: html/Search/Results.html:88
+msgid "grouped by %1"
+msgstr "fordelt pr. %1"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "timer"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "ID"
+
+#: html/Elements/SelectBoolean:53
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:57
+#: html/Search/Elements/PickBasics:162
+#: html/Search/Elements/PickBasics:74
+#: html/Search/Elements/PickBasics:90
+#: html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "er"
+
+#: html/Elements/SelectBoolean:57
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:58
+#: html/Search/Elements/PickBasics:163
+#: html/Search/Elements/PickBasics:75
+#: html/Search/Elements/PickBasics:91
+#: html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "er ikke"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "mindre end"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "stemmer overens"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min."
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minutter"
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"modifications\\n"
+"\\n"
+msgstr ""
+"ændringer\\n"
+"\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "måneder"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "ny"
+
+#: html/Admin/Elements/PickCustomFields:64
+#: html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "intet navn"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "ingen værdi"
+
+#: html/Admin/Elements/EditQueueWatchers:48
+#: html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "ingen"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "ikke lig med"
+
+#: lib/RT/Queue_Local.pm:2
+msgid "offer"
+msgstr "tilbud"
+
+#: html/SelfService/Elements/MyRequests:82
+#: lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "Ã¥ben"
+
+#. ($self->Name, $user->Name)
+#: lib/RT/Group_Overlay.pm:219
+msgid "personal group '%1' for user '%2'"
+msgstr "personlig gruppe '%1' for bruger '%2'"
+
+#. ($queue->Name, $self->Type)
+#: lib/RT/Group_Overlay.pm:227
+msgid "queue %1 %2"
+msgstr "kø %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "afvist"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "løst"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sek."
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "vis konfigurationsfane"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "regneark"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "sat i bero"
+
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+#: html/Search/Results.html:89
+msgid "style: %1"
+msgstr "stil: %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "oversigtsrækker"
+
+#. ($self->Type)
+#: lib/RT/Group_Overlay.pm:222
+msgid "system %1"
+msgstr "system %1"
+
+#. ($self->Type)
+#: lib/RT/Group_Overlay.pm:233
+msgid "system group '%1'"
+msgstr "systemgruppe '%1'"
+
+#: html/Elements/Error:64
+#: html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "den kaldende komponent angav ikke hvorfor"
+
+#: NOT FOUND IN SOURCE
+msgid "ticket #%1"
+msgstr "sag #%1"
+
+#. ($self->Instance, $self->Type)
+#: lib/RT/Group_Overlay.pm:230
+msgid "ticket #%1 %2"
+msgstr "sag #%1 %2"
+
+#. ($self->Id)
+#: lib/RT/Group_Overlay.pm:236
+msgid "undescribed group %1"
+msgstr "ubeskrevet gruppe %1"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "ubeskrevet gruppe %1"
+
+#. ($user->Object->Name)
+#: lib/RT/Group_Overlay.pm:211
+msgid "user %1"
+msgstr "bruger %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "uger"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "med skabelon %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "Ã¥r"
+
diff --git a/rt/lib/RT/I18N/de.po b/rt/lib/RT/I18N/de.po
new file mode 100644
index 0000000..f0389ea
--- /dev/null
+++ b/rt/lib/RT/I18N/de.po
@@ -0,0 +1,5194 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2006-12-20 15:50+0100\n"
+"Last-Translator: Torsten Brumm <tob@brummix.de>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr "$1"
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr "$prefix %1"
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 #%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %3. %2 %7, %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 hinzugefügt"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "vor %1 %2"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 geändert in %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 gelöscht"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 mit der Vorlage %3"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) von %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (Unverändert)"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Ein Argument zur Ãœbergabe an %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Statusausgabe auf STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Gibt an, welches Action-Modul benutzt werden soll"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Gibt an, welches Condition-Modul benutzt werden soll"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Gibt an, welches Search-Modul benutzt werden soll"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 Alle Rechte vorbehalten 1996-%3 %4."
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScripAction %1 geladen"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 als Wert für %2 hinzugefügt"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 konnte nicht in der Datenbank gefunden werden obwohl es ein lokales Objekt zu sein scheint"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 von %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 von %2 in %3 geändert"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 kopieren"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 konnte nicht auf %2 gesetzt werden."
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 konnte den Status nicht auf erledigt setzen. Die RT-Datenbank könnte inkonsistent sein."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 erstellt"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 gelöscht"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 grouped by %2"
+msgstr "%1 gruppiert durch %2"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "Ihre %1 Anfragen höchster Priorität"
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 ist ein Tool um mit Tickets von externen Programmen zu arbeiten, wie zum Beispiel cron."
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 ist kein %2 dieses Bereichs mehr."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 Min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "Die %1 neusten Anfragen ohne Besitzer"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 Objekte"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 Rechte"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 wird alle Mitglieder eines erledigten Gruppentickets anführen."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1's %2 Objekte"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1's %2's %3 Objekte"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "%1's gespeicherte Suchanfragen"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: kein Anhang angegeben"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Auswahl wird gelöscht)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Benachrichtigung der markierten Empfänger wird unterdrückt)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Benachrichtigungen an markierte Empfänger aktivieren)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Geben Sie die Anfrage IDs oder URLs getrennt durch Leerzeichen ein)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Standardwert: %1)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Keine benutzerdefinierten Felder)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Keine Mitglieder)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Keine Scrips)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Keine Vorlagen)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Nichts)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Sendet ein Blind Carbon Copy - BCC Update and eine mit Komma separierte Liste von Mail Adressen. Zukünftige Update werden <strong>nicht</strong> übermittelt.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Sendet ein Carbon Copy - CC Update and eine mit Komma separierte Liste von Mail Adressen. Zukünftige Update <strong>werden</strong> ebenfalls übermittelt.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Sendet ein Carbon Copy - CC Update and eine mit Komma separierte Liste von Mail Adressen. Zukünftige Update werden <strong>nicht</strong> übermittelt.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Sendet ein Carbon Copy - CC Update and eine mit Komma separierte Liste von Mail Adressen. Zukünftige Update <strong>werden</strong> ebenfalls übermittelt.)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Benutze diese Felder, für 'Benutzer definierte' Bedingungen oder Aktionen)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(leer)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(kein Name gelistet)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(kein Wert)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(keine Werte)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(nur eine Anfrage)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(wartet auf Freigabe)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(wartet auf andere Sammlung)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(notwendig)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(unbenannt)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(jjjj/mm/tt)"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$field%>"
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Neues Ticket in\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Neue Anfrage in\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Eine leere Vorlage"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Es wurde kein Passwort gesetzt, der User wird sich nicht anmelden können!"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE nicht gefunden"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACEs können nur erstellt und gelöscht werden."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "UND"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Ãœber mich"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Zugriffskontrolle"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Aktion"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Aktion %1 nicht gefunden"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Aktion durchgeführt.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Aktion vorbereitet..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "AdminCC hinzufügen"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "CC hinzufügen"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Spalten hinzufügen"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Kriterium hinzufügen"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Weitere Dateien anhängen"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Klient hinzufügen"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Wert hinzufügen"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Scrip erstellen, das auf alle Bereiche angewendet wird"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "zusätzliches Kriterium hinzufügen"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Hinzufügen und Suchen"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Füge den ausgewählten Anfragen Kommentare oder Antworten hinzu"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Mitglieder hinzufügen"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Neue Beobachter hinzufügen"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Diese Bedingungen zur Suche hinzufügen"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "Werte hinzufügen"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Hinzufügen, Löschen und Ändern von Werten benutzerdefinierter Felder von Objekten"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Rolle wurde als %1 für diesen Bereich hinzugefügt"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Rolle wurde als %1 für diese Anfrage hinzugefügt"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adresse 1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adresse 2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin CC"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Admin Kommentar"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Admin Korrespondenz"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Admin Bereiche"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Admin/Globale Einstellungen"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "AdminBenutzerdefiniertesFeld"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "AdminGruppe"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "AdminGruppenZugehörigkeit"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "AdminEigenePersönlicheGruppen"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "AdminBereich"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "AdminBenutzer"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Administrative CC"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Erweitert"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "nach dem"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Operator"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Alle Freigaben genehmigt"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Alle Bereiche"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "Und/Oder"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "gilt für"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "anwenden"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Änderungen anwenden"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Freigabe"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Freigabe #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Freigabe #%1: Notiz wurde aufgrund eines Systemfehlers nicht gespeichert"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Freigabe #%1: Notiz gespeichert"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Freigabe genehmigt"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Freigabe abgelehnt"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Freigeben"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Notizen des Freigebenden: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Apr."
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "aufsteigend"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Zuweisen und Entfernen von benutzerdefinierten Feldern"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "BenutzerdefiniertesFeldZuweisen"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Anhängen"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Datei anhängen"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Dateianhang"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Anhang '%1' konnte nicht geladen werden"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Anhang erstellt"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Dateiname des Anhangs"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Anhänge"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Attribut gelöscht"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Aug."
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Automatische Antwort"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Automatische Antwort an Klienten"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "Verfügbar"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Grundlagen"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "BCC"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Änderungen speichern"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "vor dem"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Best Practical Solutions, LLC Firmen Logo"
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "Binär"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Leer"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Fett"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Speicherbarer Link"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Kurze Kopfzeilen"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "Massen Update"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Massen Anfrageaktualisierung"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Kann Systembenutzer nicht ändern"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Kann diese Rolle diesen Bereich sehen"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Kann kein benutzerdefiniertes Feld ohne Namen hinzufügen"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "Es wurde keine Kollektion gefunden für '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Kann keine gespeicherte Suche finden"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Kann kein Anfrage auf sich selbst verweisen lassen!"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Kann diese Suche nicht speichern"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Sie können Basis und Ziel nicht gleichzeitig angeben"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Kann Benutzer nicht anlegen: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "Kategorie"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "CC"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Passwort ändern"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Alle auswählen"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Zum Löschen anwählen"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Zum Entziehen einer Berechtigung anwählen"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Kinder"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Datum auswählen"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Stadt"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Alles löschen"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "Fenster schliessen"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Geschlossen"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Geschlossene Anfragen"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Combobox: Selektiere oder gebe mehrere Werte ein"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Combobox: Selektiere oder gib einen Wert ein"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Combobox: Selektiere oder gib bis zu %1 Werte ein"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Kommentar"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Kommentaradresse"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Kommentiere Anfragen"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "AnfrageKommentieren"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Kommentare (werden nicht an Klienten geschickt)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Kommentare (werden nicht an Klienten geschickt)"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Kommentare zu diesem Benutzer"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Kommentar hinzugefügt"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Commit Stumpf ausgeführt"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Bedingung"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Bedingung trifft zu..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Bedingung nicht gefunden"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Konfiguration"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Bestätigen"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Inhalt"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Inhaltstyp"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Kopie"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Korrespondenz"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Korrespondenz hinzugefügt"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Konnte keinen neuen benutzerdefinierten Wert hinzufügen"
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Konnte kein neuen benutzerdefinierten Wert hinzufügen. %1 "
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Konnte den Besitzer nicht ändern. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Konnte benutzerdefiniertes Feld nicht erzeugen"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Konnte benutzerdefiniertes Feld nicht erzeugen: %1"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Konnte Gruppe nicht anlegen"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Konnte Vorlage nicht anlegen: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Konnte Anfrage nicht anlegen. Bereich nicht angegeben"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Konnte Benutzer nicht anlegen"
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Konnte diesen Benutzer nicht finden oder anlegen"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Konnte diese Rolle nicht finden"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Konnte benutzerdefiniertes Feld %1 nicht laden"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Konnte die Gruppe nicht laden"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "Kann Objekt für %1 nicht laden"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Kann Such Attribut nicht laden"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Konnte diese Rolle nicht zu einen %1 dieses Bereichs machen"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Konnte diese Rolle nicht zu einem %1 dieses Anfragen machen"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Konnte diese Rolle nicht als %1 dieses Bereichs entfernen"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Konnte Benutzerinformation nicht speichern"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Konnte Benutzer nicht der Gruppe hinzufügen"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Konnte die Transaktion nicht anlegen: %1"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Konnte Zeile nicht finden"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Konnte diese Rolle nicht finden"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Konnte diesen Wert nicht finden"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Konnte %1 nicht aus der Benutzerdatenbank laden.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "Konnte die Klasse %1 nicht laden"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Konnte das benutzerdefinierte Feld %1 nicht laden"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Konnte Gruppe %1 nicht laden"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Konnte den Verweis nicht laden"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Konnte Objekt %1 nicht laden"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Konnte den Bereich nicht laden"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Konnte den Bereich %1 nicht laden"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Konnte das Anfrage '%1' nicht laden"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't resolve base '%1' into a URI."
+msgstr "Basis '%1' kann nicht zu einer URI aufgelöst werden."
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't resolve target '%1' into a URI."
+msgstr "Ziel '%1' kann nicht zu einer URI aufgelöst werden."
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Land"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Erstellen"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Erstelle Anfragen"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Erstelle ein benutzerdefiniertes Feld"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Erstelle ein benutzerdefiniertes Feld für den Bereich %1"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Erstelle eine neue Gruppe"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Erstelle eine neue persönliche Gruppe"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Erstelle eine neue Anfrage"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Erstelle einen neuen Benutzer"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Erstelle einen Bereich"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Erstelle ein Scrip für den Bereich %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Erstelle eine Vorlage"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Neue Anfrage"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Erstelle neue Anfragen basierend auf der Vorlage dieses Scrips"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Anfrage erzeugen"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Erstelle Anfragen in diesem Bereich"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Erstellen, löschen und modifizieren von benutzerdefinierten Felder"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Erstelle, lösche und modifiziere Bereich"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Erstellen, löschen und modifizieren von Mitgliedern persönlicher Gruppen"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Erstellen, löschen und modifizieren von Benutzern"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "GespeicherteSucheErstellen"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "AnfrageErstellen"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Angelegt"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Erstelle ein benutzerdefiniertes Feld %1"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Erstellt in einem Zeitraum"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "Erstellte Tickets einer Periode, sortiert nach Status"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Ersteller"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Momentane Beziehungen"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Aktuelle Scrips"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Aktuelle Mitglieder"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Aktuelle Rechte"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Aktuelle Suche"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Aktuelle Beobachter"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Benutzerdefinierte Felder"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Benutzerdefinierte Felder für %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Benutzerdefinierter Aktions-Aufräum-Code"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Benutzerdefinierter Aktions-Vorbereitungs-Code"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Benutzerdefinierte Bedingung"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Benutzerdefiniertes Feld %1 hat einen Wert."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Benutzerdefiniertes Feld %1 hat keinen Wert."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Benutzerdefiniertes Feld %1 nicht gefunden"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Benutzerdefiniertes Feld nicht gefunden"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Wert %1 des benutzerdefinierten Feldes %2 konnte nicht gefunden werden"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Wert des benutzerdefinierten Felds konnte nicht gelöscht werden"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Wert des benutzerdefinierten Feldes konnte nicht gefunden werden"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Wert des benutzerdefinierten Feldes gelöscht"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "BenutzerdefiniertesFeld"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "Anpassen"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Datumsangaben"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Dez."
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Standard Vorlage für automatische Antworten"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Standard Bereich"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Standard Klient"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Standard Vorlage für Admin-Kommentar"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Standard Vorlage für Admin-Korrespondenz"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Standard Vorlage für Korrespondenz"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Standard Vorlage für Transaktion"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Standard: %1/%2 von \"%3\" auf \"%4\" geändert."
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Rechte weitergeben"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Ihnen gewährte Rechte weitergeben"
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "RechteWeitergabe"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Rechteweitergabe"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Löschen"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Vorlage löschen"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Löschen fehlgeshlagen: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Ausgewähltes Script löschen"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Anfragen löschen"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr "Werte löschen"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "AnfrageLöschen"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Suche löschen"
+
+# Are these three strings really different?
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Löschen dieses Objektes würde die referenzielle Integrität verletzen"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Löschen dieses Objektes würde die referenzielle Integrität verletzen"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Löschen dieses Objektes würde die referenzielle Integrität verletzen"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Ablehnen"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Ist Voraussetzung von"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Als Voraussetzung von %1 hinzugefügt"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Als Voraussetzung von %1 gelöscht"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Setzt jetzt %1 voraus"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Setzt %1 nicht mehr voraus "
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Voraussetzungen"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "absteigend"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Beschreiben Sie hier das Problem"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Beschreibung"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Anzeige"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Zeige Zugriffskontrollliste (ACL) an"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Spalten anzeigen"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Zeige Scrip-Vorlagen für diesen Bereich"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Zeige Scrips für diesen Bereich"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Anzeigemodus"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Gespeicherte Suchanfragen für diese Gruppe anzeigen"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Herausgegeben unter version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> der GNU GPL.</a>"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Mache irgend etwas und alles"
+
+#: NOT FOUND IN SOURCE
+msgid "Do the Search"
+msgstr "Suche durchführen"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Diese Seite nicht aktualisieren."
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Herunterladen"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Als Tabulator separierte Datei speichern"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Fällig"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Ändere"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "Benutzerdefinierte Felder verändern"
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Bearbeite benutzerdefinierte Felder für %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Benutzerdefinierte Felder für alle Gruppen verändern"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Benutzerdefinierte Felder für alle Benutzer verändern"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Benutzerdefinierte Felder von Tickets in allen Stapeln verändern"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Bearbeite Beziehungen"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Bearbeite Suche"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Suche editieren"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Bearbeite Vorlagen für den Bereich %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Bearbeite gespeicherte Suchanfragen für diese Gruppe"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Bearbeite Systemvorlagen"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "GespeicherteSucheEditieren"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Bearbeite Konfiguration für den Bereich %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Bearbeite benutzerdefiniertes Feld %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Bearbeite Mitgliedschaft für die Gruppe %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Bearbeite Mitgliedschaft der persönlichen Gruppe %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Es muss entweder eine Basis oder ein Ziel angegeben werden"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "E-Mail"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "E-Mail-Adresse bereits in Gebrauch"
+
+# ## muss das überhaupt übersetzt werden???
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Aktiviert (Abwählen deaktiviert dieses benutzerdefinierte Feld)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Aktiviert (Abwählen deaktiviert diese Gruppe)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Aktiviert (Abwählen deaktiviert diesen Bereich)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Aktivierte Bereiche"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Status aktiviert: %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "Status aktiviert: %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Mehrere Werte eingeben"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Gib Objekte oder URI's ein um zu anderen Objekten zu verlinken. Trenne mehrere Einträge mit Leerzeichen."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Einen Wert eingeben"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Gib Stapel oder URI's ein um zu anderen Stapeln zu verlinken. Trenne mehrere Einträge mit Leerzeichen."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Gib Anfragen oder URI's ein um zu anderen Anfragen zu verlinken. Trenne mehrere Einträge mit Leerzeichen."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Gib bis zu %1 Werte ein"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Fehler"
+
+# Queue->AddWatcher ist ein Code-Teil, nicht übersetzen
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Fehler in den Parametern zu Queue->AddWatcher"
+
+# Queue->DeleteWatcher ist ein Code-Teil, nicht übersetzen
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Fehler in den Parametern zu Queue->DeleteWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Fehler in den Parametern zu Ticket->AddWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Fehler in den Parametern zu Ticket->DeleteWatcher"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Anfragen eskalieren"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Geschätzt"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Jeder"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Beispiel:"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Zusatzinformationen"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Such Attribut konnte nicht erstellt werden"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Konnte die Pseudogruppe 'Privileged' nicht finden."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Konnte die Pseudogruppe 'Unprivileged' nicht finden."
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Konnte Modul %1 nicht laden. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "Objekt %1 konnte nicht geladen werden"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Dateiname"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Mehrere Textfelder füllen"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Schreibe mehrere Wikitext Bereiche"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Ein Textfeld füllen"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Schreibe einen Wikitext Bereich"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "%1 Textfelder füllen"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Schreibe bis zu %1 Wikitext Bereiche"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Endpriorität"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "EndPriorität"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Finde Gruppen dessen"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Finde Leute deren"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Anfragen suchen"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Erste"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Änderung erzwingen"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Format"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "%1 Anfrage(n) gefunden"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Objekt gefunden"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "Freie Eingabe"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Fr."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Alle Kopfzeilen"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Vorlage von Datei nehmen"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "An %1 übergeben"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Globale benutzerdefinierte Felder"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Konfiguration globaler benutzerdefinierter Felder"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Globale Vorlage: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Start"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Los!"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Zeige Anfrage"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Gruppe"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Gruppenrechte"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Gruppe hat bereits Mitglieder"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Gruppe konnte nicht erstellt werden: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Gruppe angelegt"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Gruppe hat kein solches Mitglied"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Gruppe nicht gefunden"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Gruppen"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Gruppen können nicht Mitglied eines ihrer Mitglieder sein"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Gruppen auf die das Suchkriterium passt"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Gruppen zu denen der Benutzer gehört"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hallo!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Hallo %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Verlauf"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "Verlauf der Gruppe %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "Verlauf des Benutzers %1"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Startseite"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "Stunden"
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Ich habe %quant(%1, Betonmischer)"
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Ich bin verwirrt"
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Nr."
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identität"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Wenn eine Freigabe abgewiesen wird, weise das Original ab und lösche wartende Freigaben"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Wenn kein Klient spezifiziert ist, dann generiere Anfragen mit diesem Klienten"
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Wenn kein Bereich spezifiziert ist, dann generiere das Anfrage in diesem Bereich"
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Wenn dieses Programm setgid ist, könnte ein böswilliger lokaler Nutzer Administrator Rechte auf dem RT bekommen."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Wenn Sie etwas aktualisiert haben, denken Sie daran hier zu speichern"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Unerlaubter Wert für %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Bild"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Unveränderbares Feld"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Zeige auch deaktivierte Gruppen an."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Zeige auch deaktivierte Bereiche an."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Zeige deaktivierte Benutzer auch in der Suche an."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Unvollständige Suche"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Unvollständige Anfrage"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Anfängliche Priorität"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "AnfänglichePriorität"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Eingabefehler"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr "Eingabe muss mit %1 übereinstimmen"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Interner Fehler"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Interner Fehler: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Ungültige Gruppenart"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Ungültiges Recht"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Ungültige Daten"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "Ungültiges Muster: %1"
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Ungültiger Bereich"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Ungültiges Recht"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Ungültiger Wert für %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Ungültiger Wert für das benutzerdefinierte Feld"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Ungültiger Statuswert"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Es ist wichtig, dass nicht authorisierte Benutzer dieses Programm nicht starten können."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Es wird empfohlen einen nicht priviligierten Unix User mit korrekten Gruppenrechten anzulegen um dieses Programm zu nutzen."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Es verarbeitet verschiedene Parameter:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "kursiv"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Betrete oder verlasse diese Gruppe"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul."
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Alles"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun."
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Sprache"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Groß"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Letzter Kontakt"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Letzter Kontakt"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Zuletzt Kontaktiert"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Zuletzt Aktualisiert"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "ZuletztBearbeitetVon"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Links"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Diesem Benutzer RT-Zugriff gewähren"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Diesem Benutzer Rechte zuweisen lassen"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Link"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Beziehung existiert bereits"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Beziehung konnte nicht erstellt werden"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Beziehung erstellt (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Beziehung gelöscht (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Beziehung nicht gefunden"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Verweise auf Anfrage #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Beziehungen"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Laden"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Gespeicherte Suchanfragen laden"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "GespeicherteSucheLaden"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Geladene Perl Module"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Geladene Suche %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Adresse"
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Angemeldet als %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Anmelden"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Abmelden"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Suchtyp unpassend"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Besitzer festlegen"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Status festlegen"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Fälligkeitsdatum festlegen"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Erledigungsdatum festlegen"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Anfangsdatum festlegen"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Startdatum festlegen"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Eingangsdatum festlegen"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Priorität festlegen"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Bereich festlegen"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Betreff festlegen"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Diese Gruppe dem Benutzer anzeigen"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Verwalte benutzerdefinierte Felder und Werte"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Gruppen und Gruppenmitglieder verwalten"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Eigenschaften und Einstellungen für alle Bereiche verwalten"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Bereich und bereichspezifische Einstellungen verwalten"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Benutzer und Passworte verwalten"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mär."
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Mai"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Mitglied %1 hinzugefügt"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Mitglied %1 gelöscht"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Mitglied hinzugefügt"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Mitglied gelöscht"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Mitglied nicht gelöscht"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Mitglied von"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Mitglieder"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "Mitgliedschaft in %1 hinzugefügt"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "Mitgliedschaft in %1 gelöscht"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Mitgliedschaft"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "Mitgliedschaft des Benutzers %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Vereinigung erfolgreich"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Vereinigung fehlgeschlagen. Konnte EffectiveId nicht setzen"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Vereinigung fehlgeschlagen. Konnte Status nicht setzen"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Vereinigen mit"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Vereinigt mit %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Nachricht"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Nachricht konnte nicht gespeichert werden"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Nachricht gespeichert"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Nachrichten über diese Anfrage werden nicht an diese Empfänger gesendet:"
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Minuten"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "Unausgeglichene Klammerung"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "%1: Fehlt ein Primärschlüssel?"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Handy"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Ändere Zugriffskontrollliste (ACL)"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Alle benutzerdefinierten Felder %2, die zu %1 gehören, ändern"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Alle benutzerdefinierten Felder, die zu %1 gehören, ändern"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Gruppenrechte ändern"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Mitglieder ändern"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Rechte ändern"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Ändere Scrip-Vorlagen für diesen Bereich"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Ändere Scrips für diesen Bereich"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Ändern der Benutzerrechte"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Ändere ein benutzerdefiniertes Feld für den Bereich %1"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Ändere ein Scrip für den Bereich %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Ändere ein globales benutzerdefiniertes Feld"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Ändern der assoziierte Objekte von %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Ändere Datumsangaben für #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Ändere Datumsangaben für Anfrage #%1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Ändern der globalen benutzerdefinierten Felder"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Ändere globale Gruppenrechte"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Ändere globale Gruppenrechte."
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Ändere globale Benutzerrechte"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Ändere globale Benutzerrechte."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Ändere Gruppen-Metadaten oder lösche die Gruppe"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Ändere Gruppenrechte für das benutzerdefinierte Feld %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Ändere die Gruppenrechte der Gruppe %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Ändere Gruppenrechte für den Bereich %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Ändere Mitgliedsverzeichnis dieser Gruppe"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Ändert den eigenen RT-Zugang"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Ändere Personen im Zusammenhang mit diesem Bereich %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Ändere Personen der Anfragen #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Ändere Scrips für den Bereich %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Ändere auf alle Bereiche angewandte Scrips"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Ändere Vorlage %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Ändere globale Vorlagen"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Ändere Gruppe %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Ändere die Bereichsbeobachter"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Ändere Benutzer %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Ändere Anfrage #%1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Ändere Anfrage #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Ändere Anfragen"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Ändere Benutzerrechte für benutzerdefinierte Felder von %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Ändere Benutzerrechte für die Gruppe %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Ändere Benutzerrechte für den Bereich %1"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ACLBearbeiten"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "BenutzerdefiniertesFeldBearbeiten"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "EigeneMitgliedschaftBearbeiten"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "BereichsBeobachterBearbeiten"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ScripsBearbeiten"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "SelbstBearbeiten"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "VorlageBearbeiten"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "AnfrageBearbeiten"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Mo."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Mehr über %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Hinunter verschieben"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Hinauf verschieben"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Mehrere"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Sie müssen eine Angabe bei 'Name' machen"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "Meine %1 Anfragen"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "Mein Tag"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Meine Freigaben"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Meine gespeicherten Suchanfragen"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Name"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Benutzername ist bereits in Gebrauch"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Niemals"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Neu"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Neue Beziehungen"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Neues Passwort"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Neue wartende Freigaben"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Neue Suche"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Neue Suche"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Neues benutzerdefiniertes Feld"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Neue Gruppe"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Neues Passwort"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Neue Passworterinnerung wurde verschickt"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Neuer Bereich"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Neue Wiedervorlage"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Neue Rechte"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Neues Scrip"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Neue Vorlage"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Neue Anfrage"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Neue Anfrage existiert nicht"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Neuer Benutzer"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Neuer Benutzer aufgerufen"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Neue Beobachter"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Nächste"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Nächste Seite"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Spitzname"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Keine Klasse definiert"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Kein benutzerdefiniertes Feld"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Kein benutzerdefiniertes Feld definiert"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Keine Gruppe definiert"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Keine Suche"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Kein Bereich vorhanden"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Kein RT-Benutzer gefunden. Bitte kontaktiere Sie Ihren RT-Administrator.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Keine Vorlage"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Keine Aktion"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Keine Spalte angegeben"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Kein Kommentar zu diesem Benutzer angegeben"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Keine Beschreibung für %1 vorhanden"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Keine Gruppe angegeben"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Keine Gruppe mit diesen Kriterien gefunden"
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Keine Nachricht angefügt"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Kein Passwort gesetzt"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Keine Erlaubnis Bereiche anzulegen"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Kein Erlaubnis um Anfragen im Bereich '%1' anzulegen"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Kein Recht Benutzer anzulegen"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Kein Recht dieses Anfrage anzuzeigen"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Kein Recht dieses Anfrage zu aktualisieren"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Keine Rolle angegeben"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Keine Rolle ausgewählt."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Keine den Suchkriterien entsprechenden Bereiche gefunden"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Keine Rechte gefunden"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Keine Rechte gewährt."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Keine Suche geladen"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Keine Suche zu bearbeiten."
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Kein Betreff"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Kein Transaktionstyp angegeben"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Keine auf die Suchkriterien passende Benutzer gefunden"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Kein Wert an _Set geschickt!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Niemand"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Nichtexistierendes Feld?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Nicht angemeldet."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Nicht angegeben"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Noch nicht implementiert."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Bemerkungen"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Benachrichtigung konnte nicht verschickt werden"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Benachrichtige AdminCCs"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Benachrichtige AdminCCs als Kommentar"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Benachrichtige CCs"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Benachrichtige CCs als Kommentar"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Benachrichtige andere Empfänger"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Benachrichtige andere Empfänger als Kommentar"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Benachrichtige Besitzer"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Benachrichtige Besitzer als Kommentar"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Besitzer über ihre abgewiesene Anfrage informieren"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Benachrichtige Besitzer, dass sein Anfrage von allen Entscheidungsträgern freigegeben wurde"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Benachrichtige Besitzer, dass sein Anfrage von einigen Entscheidungsträgern freigegeben wurde"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Benachrichtige Besitzer und AdminCCs neuer auf Freigabe wartende Anfragen"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Benachrichtige die Klienten"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Benachrichtige die Klienten und CCs"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Benachrichtige die Klienten und CCs als Kommentar"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Benachrichtige die Klienten, CCs und AdminCCs"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Benachrichtige die Klienten, CCs und AdminCCs als Kommentar"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "Oder"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objekt konnte nicht erstellt werden"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Objekt konnte nicht gelöscht werden"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objekt erstellt"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objekt gelöscht"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Objekt vom Typ %1 kann keine benutzerdefinierten Felder haben"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Objekt Typ passt nicht"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Okt."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Offline"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Offline Bearbeitung"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Offline Upload"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "am"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Bei Kommentar"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Bei Korrespondenz"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Bei Erstellen"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Bei Besitzerwechsel"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Bei Änderung der Priorität"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Bei Änderung des Bereichs"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Beim Erledigen"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Beim Ändern des Status"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Bei einer Transaktion"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Zeige nur Freigaben für nach dem %1 erstelle Anfragen"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Zeige nur Freigaben für vor dem %1 erstellte Anfragen"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Nur Benutzerdefinierte Felder anzeigen für:"
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Offen"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Öffnen"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Offene Anfragen"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Öffne Anfragen bei Korrespondenz"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Optionen"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Sortiert nach"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organisation"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Ursprüngliche Anfrage: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Ausgehende Mail eines Kommentars gespeichert"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Ausgehende Mail gespeichert"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Mit der Zeit steigt die Priorität auf"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Eigene Anfragen"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "EigeneAnfrage"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Besitzer"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Besitzer konnte nicht gesetzt werden"
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Besitzer mit Gewalt von %1 in %2 geändert"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Seite %1 von %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Pager"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Eltern"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Passwort"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Passworterinnerung"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Passwort geändert"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "Das Passwort muss mindestens %1 Zeichen lang sein"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Passwort erstellt"
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Passwort ist zu kurz"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Passwort: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Passwort: Zugriff verweigert"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Passwörter verschieden"
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Passwörter verschieden, keine Änderung"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Personen"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Führe eine benutzerdefinierte Aktion aus"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl Konfiguration"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Zugriff verweigert"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Persönliche Gruppen"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Persönliche Gruppen"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Persönliche Gruppen:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefonnummern"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Voreinstellungen"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Prepare Stumpf ausgeführt"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Vorherige"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Vorherige Seite"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Rolle %1 nicht gefunden."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Priorität"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Priorität beginnt bei"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Privatsphäre:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilegiert"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Privilegierungsstatus: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Privilegierte Benutzer"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudogruppe für internen Gebrauch"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "Suche"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Suche erstellen"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Bereich"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Bereich %2 nicht gefunden"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Name des Bereichs"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Bereich existiert bereits"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Bereich konnte nicht angelegt werden"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Bereich konnte nicht geladen werden"
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Bereich angelegt"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Bereich nicht gefunden"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Bereiche"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Stapel die ich verwalte"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Stapel in denen ich AdminCc bin"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Schnellsuche"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Schnelle Anfrageerstellung"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 für %2"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT Administration"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT Fehler"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RT Variablen"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT auf einen Blick"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT konnte die Session nicht speichern"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT für %1"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT wird nach allem suchen was in einem Anfragen Betreff steht."
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Echter Name"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "Referenz von %1 hinzugefügt"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "Referenz von %1 gelöscht"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "Referenz auf %1 hinzugefügt"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "Referenz auf %1 gelöscht"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Referenziert von"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Bezieht sich auf"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Seite alle %1 Minuten aktualisieren."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "Wiedervorlage '%1' zugefügt"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "Wiedervorlage '%1' abgeschlossen"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "Wiedervorlage '%1' erneut geöffnet"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "Wiedervorlage Anfrage #%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "Wiedervorlage"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "Wiedervorlage für Anfrage #%1"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Entferne AdminCC"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Entferne CC"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Entferne Klient"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Antworten"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Antwort Adresse"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Antwort an Klienten"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Antworte auf Anfragen"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "AnfrageBeantwortung"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Auswertungen"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Klient"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Klienten"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Anfragen sollten erlegt werden innerhalb"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Benötigter Parameter '%1' nicht angegeben"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Zurücksetzen"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Zuhause"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Erledigen"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Erledige Anfrage #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Erledigt"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Gelöst durch Besitzer"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Gelöst in Zeitraum"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Erledigte Tickets in einer Zeitspanne, gruppiert nach Besitzer"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Erledigte Tickets, sortiert nach Besitzer"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Ergebnisse"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Passwort wiederholen"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Zurücksetzen"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Recht weitergegeben"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Recht erteilt"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Recht geladen"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Recht konnte nicht zurückgezogen werden"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Recht nicht gefunden"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Recht nicht gefunden."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Recht zurückgezogen"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Rechte"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Rechte für %1 konnten nicht gewährt werden"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Rechte für %1 konnten nicht entzogen werden"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Rollen"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Reihen pro Fenster"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Zeilen pro Seite"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sa."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Speichern"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Änderungen Sichern"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Einstellungen speichern"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Änderungen Sichern"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "Gespeicherte Suche %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Gespeicherte Suchanfragen"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip angelegt"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Scrip Felder"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip gelöscht"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Auf alle Bereiche angewandte Scrips"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Suchen"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Such Einstellungen"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Suchattribut lade Fehler"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Suche nach Freigaben"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "Suche nach Anfragen"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "Suche nach Tickets. Eingabe <strong>id</strong> Nummer, <strong>Stapel</strong> nach Name, Besitzer nach <strong>Benutzername</strong> und Klienten nach <strong>Email Adresse</strong>. RT wird nach Deiner Eingabe in Tickets und Anhängen suchen."
+
+#: NOT FOUND IN SOURCE
+msgid "Search for tickets. Enter <strong>id</strong> numbers,<strong>queues</strong> by name"
+msgstr "Suche nach Tickets. Eingabe <strong>id</strong> Nummer,<strong>Stapel</strong> nach Name"
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Such Optionen"
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "Suchergebnis gruppiert nach %1"
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Suche erneuern: %1"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "Die Suche nach Volltexten in jedem Ticket kann sehr lange dauern, aber wenn Du es benötigst, kannst Du nach jedem Wort in jedem Ticketverlauf suchen indem Du <b>fulltext:<i>Wort</i></b> eingibst."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Sicherheit:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "Siehe auch:"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Benutzerdefinierte Felder anzeigen"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Anzeigen der gesendeten Nachricht und deren Empfänger"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Anzeigen von anfrage-privaten Kommentaren"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Anfrage Zusammenfassungen anzeigen"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "Benutzerdefiniertes Feld anzeigen"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "GruppeAnzeigen"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "BereichAnzeigen"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "Auswahl"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Wählen Sie ein benutzerdefiniertes Feld aus"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Wählen Sie eine Gruppe aus"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Stapel auswählen"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Wählen Sie einen Bereich für Ihre neue Anfrage"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Wählen Sie einen Benutzer aus"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Wählen Sie ein benutzerdefiniertes Feld"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Wählen Sie benutzerdefinierte Felder für alle Benutzergruppen aus"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Wählen Sie benutzerdefinierte Felder für alle Benutzer aus"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Wählen Sie benutzerdefinierte Felder für alle Anfragen in allen Bereichen aus"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Wählen Sie benutzerdefinierte Felder für Transaktionen mit Anfragen in allen Bereichen aus "
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Wählen Sie eine Gruppe aus"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Wählen Sie mehrere Werte aus"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Wählen Sie einen Wert aus"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Wählen Sie einen Bereich aus"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Wähle Stapel aus, welche auf der \"RT at a glance\" Seite angezeigt werden"
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Wählen Sie ein Scrip aus"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Wählen Sie eine Vorlage aus"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Wählen Sie bis zu %1 Werte aus"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Wählen Sie einen Benutzer aus"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Ausgewählte Benutzerdefinierte Felder"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Ausgewählte Objekte"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Auswahl wurde geändert. Bitte speichere Deine Änderungen"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Schickt eine Nachricht an alle Beobachter"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Schickt einen Kommentar an alle Beobachter"
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Schickt eine Nachricht an die Klienten und CCs"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Schickt einen Kommentar an die Klienten und CCs"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Schickt eine Nachricht an die Klienten"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Schickt eine Nachricht an die direkt angegebenen CCs und BCCs"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Schickt eine Nachricht an alle CCs"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Schickt einen Kommentar an alle CCs "
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Schickt eine Nachricht an die administrativen CCs"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Schickt einen Kommentar an die administrativen CCs"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Schickt eine Nachricht an den Besitzer"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Zeige"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Zeige Freigaben"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Zeige Spalten"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Zeige Ergebnisse"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Zeige freigegebene Anfragen"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Zeige Grundlagen"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Zeige abgelehnte Anfragen"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Zeige Details"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Zeige anhängige Anfragen"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Zeige auf andere Freigaben wartende Anfragen"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "ACLAnzeigen"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "KonfigurationsMenüAnzeigen"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "AusgehendeNachrichtenAnzeigen"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "GespeicherteSuchanfragenAnzeigen"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "ScripsAnzeigen"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "VorlagenAnzeigen"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "AnfragenAnzeigen"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "AnfrageKommentareAnzeigen"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Als Klient einer Anfrage oder Anfrage- bzw. Bereichs-CC eintragen"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Als Anfrage- oder Bereichs-AdminCC eintragen"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "E-Mail-Signatur"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Vereinfachte Suche"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Einzel"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Grösse"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Überspringe Menü"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Klein"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sortieren"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "Phase"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Begonnen"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Beginnt"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Staat"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Status"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Statusänderung"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Status von %1 auf %2 geändert"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Ãœbernehmen"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Anfragen übernehmen"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "AnfrageÃœbernehmen"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Gestohlen von %1"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Darstellung"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Betreff"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Betreff wurde auf %1 geändert"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Ãœbermitteln"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Gelungen"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "So."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "SuperBenutzer"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "System"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "System Konfiguration"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Systemfehler"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "System Fehlerr: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "System Werkzeuge"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Systemfehler. Recht nicht delegiert."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Systemfehler. Recht nicht gewährt."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Systemgruppen"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRolegroup für internen Gebrauch"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Ãœbernehmen"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Anfragen übernehmen"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "AnfrageÃœbernehmen"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Ãœbernommen"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Vorlage"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Vorlage #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Vorlage gelöscht"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Vorlage nicht gefunden"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Vorlagen eingelesen"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Vorlagen"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "Text"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Das ist bereits der aktuelle Wert"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Dies ist kein gültiger Wert für dieses benutzerdefinierte Feld"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Das ist der gleiche Wert"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Diese Rolle hat dieses Recht bereits"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Diese Rolle ist bereits ein %1 dieses Bereichs"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Diese Rolle ist bereits ein %1 dieser Anfrage"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Diese Rolle ist nicht ein %1 dieses Bereichs"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Dieser Bereich existiert nicht"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Diese Anfrage hat ungelöste Abhängigkeiten"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Diese Anfrage gehört bereits diesem Benutzer"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Dieser Benutzer existiert nicht"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Dieser Benutzer ist bereits privilegiert"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Dieser Benutzer ist bereits unprivilegiert"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Dieser Benutzer ist jetzt privilegiert"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Dieser Benutzer ist jetzt unprivilegiert"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Diesem Benutzer dürfen keine Anfragen aus diesen Bereich gehören"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Dies ist keine numerische ID"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Grundlagen"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "Der CC einer Anfrage"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "Der administrative CC einer Anfrage"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Das nachfolgende Kommando findet alle aktiven Tickets in dem Stapel 'General' und setzt deren Priorität auf 99 wenn die seit 4 Stunden nicht bearbeitet wurden:"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Der neue Wert wurde gesetzt."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Der Besitzer einer Anfrage"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Der Klient einer Anfrage"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Diese Kommentare sind generell nicht für den Benutzer sichtbar"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Dieses benutzerdefinierte Feld passt nicht zum Objekt"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Dieses Feature ist nur für Systemadministratoren verfügbar"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Diese Nachricht wird an folgende Empfänger gesendet:"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Dieses Werkzeug erlaubt es Benutzern beliebige Perl-Module von RT aus aufzurufen."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Diese Transaktion scheint keinen Inhalt zu haben"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Die %1 dringensten Anfragen dieses Benutzers"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Do."
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Anfrage #%1 Alles aktualisieren: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Anfrage #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "Anfrage %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Anfrage %1 wurde im Bereich '%2' angelegt"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Anfrage %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Benutzerdefinierte Anfrage Felder"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Geschichte von Anfrage #%1 %2"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Anfrage erledigt"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Anfrage Transaktion"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Inhalt der Anfrage"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Art des Inhalts der Anfrage"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Anfrage konnte aufgrund eines internen Fehlers nicht angelegt werden"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Anfrage Metadaten"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Status der Anfrage geändert"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "TicketSQL Suchmodul"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Anfragen"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "Anfragen erstellt nach"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "Anfragen erstellt vor"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "Anfragen gelöst nach"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "Anfragen gelöst vor"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Anfragen, die von dieser Freigabe abhängen:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Geschätzte Zeit"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Verbleibende Zeit"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Arbeitszeit"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Verbleibende Zeit"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Zeit zum anzeigen"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Gearbeitete Zeit"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "Arbeitszeit"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Titel"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Füri Hilfe, Schulung, angepasste Entwicklungen oder Lizensierung, kontaktiere bitte %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Eingegangen"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Werkzeuge"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "Summe"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transaktion"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transaktion %1 eliminiert"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transaktion erstellt"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Benutzerdefinierte Felder bei Transaktionen"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transaction->Create fehlgeschlagen, da Objekttyp und ID nicht angegeben wurden"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transaktionen sind unveränderbar"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Di."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Typ"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Nicht implementiert"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unix Login"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Unbekannte Inhaltskodierung (Content-Encoding) %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Unbekanntes Feld: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "unbegrenzt"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Unbenannte Suche"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Unprivilegiert"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Nichtausgewähltes benutzerdefiniertes Feld"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Nichtausgewähltes Objekte"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Zurückgegeben"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Aktualisieren"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Alles aktualisieren"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Anfrage Aktualisieren"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Aktualisierungtyp"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Mehrere Anfragen aktualisieren"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Aktualisierung nicht gespeichert."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Aktualisiere ausgewählte Anfragen"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Aktualisiere Anfrage"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Aktualisiere Anfrage #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Aktualisiere Anfrage #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Aktualisierungstyp war weder Korrespondenz noch Kommentar."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Aktualisiert"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Hochladen"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Hochladen mehrerer Dateien"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Hochladen mehrerer Bilder"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Hochladen einer Datei"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Hochladen eines Bildes"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Hochladen von bis zu %1 Dateien"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Hochladen von bis zu %1 Bildern"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Änderungen hochladen"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Benutze andere administrative RT Tools"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "Benutzer '%1' konnte nicht gefunden werden"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Benutzerdefiniert"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Benutzerdefinierte Konditionen und Aktionen"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Benutzerrechte"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "Benutzer hat einen unbekannten Aktualisierungstyp für das benutzerdefinierte Feld %1 bei %2-Objekt #%3 verlangt"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Benutzer konnte nicht angelegt werden: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Benutzer angelegt"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Benutzerdefinierte Gruppe"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Benutzer geladen"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Benutzerdefinierte Gruppe"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Benutzername"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Benutzer"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Auf diese Kriterien zutreffende Benutzer"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Gültige Suche"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Validierung"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Werte"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Beobachte"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "BeobachteAlsAdminCc"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Beobachter"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Mi."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "Was ich heute tat"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Wenn das Ticket freigegeben wurde von allen involvierten Personen, füge die Korrespondenz dem original Ticket hinzu"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Wenn das Ticket freigegeben wurde von einer involvierten Personen, füge die Korrespondenz dem original Ticket hinzu"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Wenn eine Anfrage erstellt wird"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Wenn ein freizugebendes Ticket erstellt wurde, informiere den Besitzer und den AdminCc des Tickets über die anstehende Freigabe"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Wenn irgendetwas passiert"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Immer wenn eine Anfrage erledigt wird"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Immer wenn der Besitzer einer Anfrage wechselt"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Immer wenn sich die Priorität eines Anfragen ändert"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Immer wenn eine Anfrage den Bereich wechselt"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Immer wenn sich der Status einer Anfrage ändert"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Immer wenn eine benutzerdefinierte Bedingung auftritt"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Immer wenn ein neuer Kommentar eingeht"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Immer wenn neue Korrespondenz eingeht"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Arbeit"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Offline arbeiten"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Gearbeitet"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Sie sind bereits Besitzer dieser Anfrage"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Sie sind kein autorisierter Benutzer"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "Du kannst die vordefinierte Such auch selbst editieren"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Sie können nur Anfragen ohne Besitzer zuweisen"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Sie haben %1 Anfragen im Bereich %2 gefunden"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Sie wurden von RT abgemeldet."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Sie haben kein Recht, Anfragen in diesem Bereich anzulegen."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Sie dürfen in diesem Bereich keine Anfragen erstellen"
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Sie können sich gerne wieder anmelden"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Ihre Anfrage wurde von %1 freigegeben. Andere Freigaben können noch ausstehen."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Ihre Anfrage wurde freigegeben."
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Ihre Anfrage wurde abgewiesen"
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Ihr Benutzername oder Passwort ist falsch"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "PLZ"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "Erlaube Erstellung von gespeicherten Suchabfragen"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "Erlaube das Laden von gespeicherten Suchabfragen"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "wie an %1 gewährt"
+
+#: NOT FOUND IN SOURCE
+msgid "belongs to"
+msgstr "gehört zu"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "Diagramm"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "geschlossen"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "enthält"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "Tage"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "gelöscht"
+
+#: NOT FOUND IN SOURCE
+msgid "does not belong to"
+msgstr "gehört nicht zu"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "enthält nicht"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "enthält nicht"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "gleich"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "Fehler: Kann nicht hinunter bewegen"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "Fehler: Kann nicht nach links bewegen"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "Fehler: Kann nicht hinauf bewegen"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "Fehler: Nichts zu löschen"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "Fehler: Nichts zu bewegen"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "Fehler: Nichts zum Umschalten"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "größer als"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "Gruppe '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "sortiert nach %1"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "Stunden"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "ID"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "ist"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "ist nicht"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "kleiner als"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "enthält"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "Min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "Minuten"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "Monate"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "neu"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "kein Name"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "kein Wert"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "keine"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "ungleich"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "offen"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "persönliche Gruppe '%1' für Benutzer '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "Bereich %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "abgewiesen"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "erledigt"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "Sek"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "Zeige Konfigurationsmenü"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "Tabellenkalkulation"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "zurückgestellt"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr "Darstellung: %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "Ergebnisreihen"
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "System %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "Systemgruppe '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "die aufrufende Komponente gab nicht an warum"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "Anfrage #%1 %2"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "unbeschriebene Gruppe %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "Benutzer %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "Wochen"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "Jahre"
+
diff --git a/rt/lib/RT/I18N/en.po b/rt/lib/RT/I18N/en.po
new file mode 100644
index 0000000..ddc58f8
--- /dev/null
+++ b/rt/lib/RT/I18N/en.po
@@ -0,0 +1,99 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"PO-Revision-Date: 2005-10-03 13:44-0400\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Apr"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Aug"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Dec"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Fri"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Home"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "May"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Mon"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Oct"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Open"
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Home"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sat"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Sun"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Thu"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Tue"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Wed"
+
diff --git a/rt/lib/RT/I18N/es.po b/rt/lib/RT/I18N/es.po
new file mode 100644
index 0000000..1e9c401
--- /dev/null
+++ b/rt/lib/RT/I18N/es.po
@@ -0,0 +1,6492 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2002-05-02 11:36+0800\n"
+"PO-Revision-Date: 2005-10-03 14:25-0400\n"
+"Last-Translator: Tomàs Núñez Lirola <tomasnl@dsl.upc.es>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "#%1"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "Añadido %1 %2"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "Hace %1 %2"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 ha cambiado a %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 borrado"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 con la plantilla %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 este caso\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (Sin cambios)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "%1 - %2 mostrados"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Un parámetro para pasar a %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - El estado de la salida actualiza STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Especifica el modulo de acción que quieres usar"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Especifica el modulo de condición que quieres usar"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Especifica el modulo de búsqueda que quieres usar"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "%1 ScripAction cargado"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "$1 añadido como un valor de %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "%1 alias requieren un TicketId en el que trabajar"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "%1 alias requieren un TicketId en el que trabajar "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "%1 alias requieren un TicketId en el que trabajar (de %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 parece ser un objeto local, pero no se encuentra en la base de datos"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 por %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 ha cambiado de %2 a %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 copiar"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 no se ha podido fijar a %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 no pudo iniciar una transacción (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 no pudo fijar el estado a resuelto. La base de datos de RT podría ser inconsistente."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 creado"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 borrado"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 grouped by %2"
+msgstr "%1 agrupado por %2"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "%1 casos de mayor prioridad que poseo"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "Los %1 tickets de mayor prioridad que poseo..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "Los %1 tickets de mayor prioridad que he pedido"
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "$1 es una herramienta para actuar sobre los tickets con una herramienta de planificación externa, como crom"
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 ha dejado de ser un %2 para esta cola."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 ha dejado de ser un %2 para este ticket."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 ha dejado de ser un valor para campo personalizable %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 no es un identificador de Cola válido."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 más nuevos casos sin propietario"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 no mostrado"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 ibjetos"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 privilegios"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 exitoso\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 tipo desconocido para $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 tipo desconocido para %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 was created without a CurrentUser\\n"
+msgstr "%1 se creó sin CurrentUser\\n"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 resolverá todos los miembros de un grupo de tickets resueltos."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 pondrá como pendiente una BASE [local] si es dependiente [o miembro] de una solicitud ligada."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1's %2 objetos"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1's %2's %3 objetos"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "búsquedas guardadas de %1"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: ningún archivo adjunto especificado"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' es un valor inválido para el estado"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' no es una acción reconocida. "
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(Marque la caja para borrar al miembro del grupo)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Marque la caja para borrar el scrip)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Marque la caja para borrar)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(Marque las cajas para borrar)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Marque las cajas para deshabilitar notificaciones a los receptores listados)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Marque las cajas para habilitar notificaciones a los receptores listados)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Entrar ids o URLs de casos, separados por espacios)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Si se deja vacio, pasara por defecto a %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(Sin Valor)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(No hay campos custom)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Sin miembros)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Sin scrips)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Sin plantillas)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Ninguno)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Envía una copia oculta de esta actualización a una lista delimitada por comas de direcciones de email. <b>NO</b> cambia quien recibirá futuras actualizaciones)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Envía una copia oculta de esta actualización a una lista de direcciones de correo delimitada por comas. <b>No</b> cambia a quien recibirá futuras actualizaciones.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Envía una copia oculta de esta actualización a una lista delimitada por comas de direcciones de email. <strong>No</strong> cambia quien recibirá futuras actualizaciones.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Envía una copia oculta de esta actualización a una lista delimitada por comas de direcciones de email administrativas. Estas personas <b>recibirán</b> las futuras actualizaciones.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Envía una copia de esta actualización a una lista delimitada por comas de direcciones de email administrativas. Estas personas <strong>recibirán</strong> futuras actualizaciones.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Envía una copia oculta de esta actualización a una lista delimitada por comas de direcciones de email.<b>NO</b> cambia quien recibirá futuras actualizaciones."
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Envía una copia de esta actualización a una lista de direcciones de correo delimitada por comas. <b>No</b> cambia quien recibirá futuras actualizaciones.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Envía una copia de esta actualización a una lista de direcciones de correo delimitada por comas. <strong>No</strong> cambia quien recibirá futuras actualizaciones.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Envía una copia de esta actualización a una lista de direcciones de correo delimitada por comas. Estas personas <b>recibirán</b> actualizaciones futuras."
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Envía una copia de esta actualización a una lista de direcciones de correo delimitada por comas. Estas personas <strong>recibirán</strong> futuras actualizaciones."
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Usar estos campos cuando se selecciona 'Definido por el usuario' para una condición o acción)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(vacío)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(no hay nombres listados)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(sin asunto)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(sin valor)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(sin valores)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(solo un ticket)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(pendiente de aprobación)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(pendiente de otra Recopilación)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(pendiente de otros tickets)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(requerido)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(sin titulo)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "Los 25 tickets de mayor prioridad que poseo..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "Los 25 tickets de mayor prioridad que he solicitado..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Nuevo caso en\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Nuevo ticket en\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Una plantilla en blanco"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Una contraseña no fue establecida, por lo tanto el usuario no podrá logearse."
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Deleted"
+msgstr "ACE Borrado"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Loaded"
+msgstr "ACE Cargado"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be deleted"
+msgstr "ACE no se pudo borrar"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be found"
+msgstr "ACE no se encontró"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE no encontrado"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACEs solo pueden ser creadas o borradas."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "Y"
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Abortando para prevenir modificaciones no intencionadas al ticket\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Sobre mi"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Control de acceso"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Acción"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Acción %1 no encontrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Acción realizada."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Acción realizada.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Acción preparada..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Añadir"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Añadir AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Añadir Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Añadir Columnas"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Añadir Criterio"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Añadir más archivos"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Añadir solicitante"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Añadir Valor"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a keyword selection to this queue"
+msgstr "Añadir una seleccion de palabra clave a esta cola"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Añadir un nuevo scrip global"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Añadir un scrip a esta cola"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Añadir un scrip que se aplicará a todas las colas"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Añadir y Buscar"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Añadir comentarios o respuestas a los tickets seleccionados"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Añadir miembro"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Añadir nuevos observadores"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Añadir estos términos a tu búsqueda"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Añadir, borrar y modificar campo personalizo para objetos"
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "AddNextState"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Principal ha sido añadido como %1 para esta cola"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Principal ha sido añadido como %1 para este ticket"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Dirección 1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Dirección 2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Admin Comment"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Admin Correspondence"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Administración de colas"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Administración de usuarios"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Adminsitración de la configuración global"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Administración de Grupos"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Administración de una cola"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "AdminAllPersonalGroups"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "AdminComment"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "AdminCorrespondence"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "AdminCustomFields"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "AdminGroup"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "AdminGroupMembership"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "AdminOwnPersonalGroups"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "AdminQueue"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "AdminUsers"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Cc Administrativa"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Avanzado"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Búsqueda avanzada"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Después"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Edad"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Agregador"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Todas las Aprobaciones Superadas"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Todos los Campos Personalizados"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Todas las colas"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Siempre envía un mensaje a los solicitantes independientemente del remitente del mensaje"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "Y/O"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Aplica a"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Aplicar"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Aplicar cambios"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Aprobación"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Aprobación #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Aprobación #%1: No se han guardado las notas debido a un error del sistema"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Aprobación #%1: Notas guardadas"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Detalles de la aprobación"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Aprobación superada"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Aprobación rechazada"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Diagrama de la aprobación"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Aprobar"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Notas del aprobador: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Abr."
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "Abril"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Ascendente"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Asignar y borrar campos personalizados"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Adjunto"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Adjuntar archivo"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Archivo adjunto"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Archivo adjunto '%1' no pudo ser cargado"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Archivo adjunto creado"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Nombre del archivo adjunto"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Archivos adjuntos"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Atributo borrado"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Ago."
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "Agosto"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "Sistema de autenticación"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Autorespuesta"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Autorespuesta a los solicitantes"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "AutoreplyToRequestors"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Firma PGP incorrecta: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Identificador de archivo adjunto erróneo. No se puede encontrar el archivo '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Datos incorrectos en %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Número de transacción incorrecta para el archivo adjunto. %1 debe ser %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Basicos"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Asegúrese de salvar sus cambios"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Antes"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "Begin Approval"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Vacio"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "URL para guardar esta búsqueda en sus marcadores"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Encabezados breves"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "Actualización en bloque"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Actualización de varios tickets en bloque"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "No se pueden modificar los usuarios del sistema"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Can this principal see this queue"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "No se puede agregar un campo personalizable si no tiene un nombre"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "No se puede encontrar una clase de recopilación para '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "No se puede encontrar una búsqueda guardada para trabajar"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "No se puede ligar un ticket a sí mismo"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "No se puede fusionar dentro de un caso ya fusionado. Nunca deberia recibir este error"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "No se puede grabar esta búsqueda"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "No se puede especificar origen y destino al mismo tiempo"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "No se puede crear el usuario: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "Categoria"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Cambiar contraseña"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Seleccionar Todo"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Selecciona la casilla para borrar"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Seleccione la caja para quitar el permiso"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Hijo"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Elije una fecha"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Ciudad"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Borrar Todo"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Cerrado"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Solicitudes cerradas"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Casos cerrados"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "No se entendió el comando!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Comentario"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Dirección de comentario"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Comentario no grabado"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Comentario sobre los tickets"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "CommentOnTicket"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Comentarios"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Comentarios (no se envían a los solicitantes)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Comentarios (no se envían a los solicitantes)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Comentarios acerca de %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Comentarios acerca de este usuario"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Comentarios añadidos"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Acción realizada"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Compilar restricciones"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Condición"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "La condición coincide..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Condición no encontrada"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Configuración"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Confirmar"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "Información de contacto"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Fecha de contacto '%1' no pudo ser leida"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Contenido"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Coould not create group"
+msgstr "No se pudo crear grupo"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Copiar"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Correspondencia"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Dirección de corresponencia"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Correspondencia agregada"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Correspondencia no guardada"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "No se pudo añadir un nuevo valor de campo personalizable para el ticket. "
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "No se pudo añadir un nuevo valor de campo personalizable para el ticket. %1 "
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "No se pudo añadir nuevo valor de campo personalizado."
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "No se pudo añadir nuevo valor de campo personalizado. %1 "
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "No se pudo cambiar el propietario. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "No se puede crear un CampoPersonalizable"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "No se pudo crear CustomField: %1"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "No se pudo crear el grupo"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "No se pudo crear la plantilla: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "No se pudo crear el ticket. Cola no seleccionada"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "No se pudo crear el usuario"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "No se pudo crear un observador para el solicitante"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "No se pudo encontrar un ticket con identificador $1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "No se pudo encontrar el grupo %1."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "No se pudo encontrar o crear el usuario"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "No se pudo encontrar ese principal"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "No se pudo encontrar el usuario %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "No se pudo cargar CustomField %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "No se puede cargar el grupo"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "No se pudo cargar objeto para %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "No se pudo cargar atributo de búsqueda"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "No se pudo hacer ese principal un %1 para esta cola"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "No se pudo hacer ese principal un %1 para este ticket"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "No se pudo quitar ese principal como un %1 para esta cola"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "No se pudo quitar ese principal como un %1 para este ticket"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "No se pudo establecer la información del usuario"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "No se pudo agregar el miembro al grupo"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "No se pudo crear la transacción: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "No se pudo averiguar que hacer a partir de la firma gpg de la respuesta"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "No se pudo encontrar el grupo\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "No se pudo encontrar la fila"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "No pudo enconcontrar ese principal"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "No se pudo encontrar ese valor"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "No se pudo encontrar ese observador"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "No se pudo encontrar el usuario\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "No se pudo cargar %1 desde la base de datos de usuarios.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "No se pudo cargar Class %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "No se pudo cargar CustomField %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load KeywordSelects."
+msgstr "No se pudo cargar KeywordSelects"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "No se pudo cargar el archivo de configuración de RT '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "No se pudieron cargar los Scrips."
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "No se pudo cargar el grupo %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "No se puedo cargar el enlace"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "No se pudo cargar objeto %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "No se pudo cargar la cola"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "No se pudo cargar la cola %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "No se pudo cargar el scrip"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "No se pudo cargar la plantilla"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "No se pudo cargar ese usuario (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "No se pudo cargar el ticket '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "País"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Crear"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Crear Tickets"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Crear CampoPersonalizable"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Crear un campo personalizables para la cola %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Crear un campo personalizable que se aplique a todas las colas"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Crear un nuevo campo personalizable"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Crear un nuevo scrip global"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Creat un nuevo grupo"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Crear un nuevo grupo personal"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Crear una nueva cola"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Crear un nuevo scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Crear una nueva plantilla"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Crear un nuevo ticket"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Crear un nuevo usuario"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Crear una cola"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Crear una cola llamada "
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Crear una solicitud"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Crear un scrip para la cola %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Crear una plantilla"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Crear un ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "Creación fallida: %1 / %2 / %3 "
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "Creación fallida: %1 / %2 / %3 "
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Crear nuevos tickets basados en esta plantilla de scrip"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Crear ticket"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Crear tickets en esta cola"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Crear, borrar y modifical campos personalizables"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Crear, borrar y modificar colas"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Crear, borrar y modificar los miembros de cualquier grupo personal de usuario"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Crear, borrar y modificar los miembros de los grupos personales"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Crear, borrar y modificar usuarios"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "CreateTicket"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Creado"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "CampoPersonalizable %1 creado"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Plantilla %1 creada"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Creador"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Relaciones actuales"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Scrips actuales"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Miembros actuales"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Permisos actuales"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Búsqueda actual"
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Criterio de búsqueda actual"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Observadores actuales"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Campo personalizable #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Campos personalizables"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Campos Personalizados para %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Código de limpieza de acción personalizable"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Código de preparación de acción personalizable"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Condición personalizable"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Campo personalizado %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Campo personalizado %1 tiene un valor."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Campo personalizado %1 no tiene un valor."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Campo personalizado %1 no encontrado"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Campo personalizable borrado"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Campo personalizado no encontrado"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "El valor del campo %1 no pudo ser encontrado para el campo %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Valor del campo cambiado de %1 a %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "El valor del campo no pudo ser borrado"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "El valor del campo no pudo se encontrado"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Valor del campo borrado"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "CustomField"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "Error de datos"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Fechas"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Dic."
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "Diciembre"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Plantilla de autorespuesta por defecto"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Plantilla de autorespuesta por defect"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Cola por Defecto"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Solicitante por Defecto"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Plantilla de comentario de admin por defecto"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Plantilla de correspondencia de admin por defecto"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Plantilla de correspondencia por defecto"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Plantilla de trasacciones por defecto"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Por defecto: %1/%2 ha cambiado de %3 a %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Delegar derechos"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Delegar derechos especificos que te han sido concedidos"
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "DelegateRights"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Delegar"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Borrar"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Borrar Plantilla"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Borrado fallido: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Borrar scripts seleccionados"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Borrar tickets"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "DeleteTicket"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Búsqueda borrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Al borrar este objeto, se puede romper la integridad referencial"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Al borrar este objeto, se romperá la integridad referencial"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Al borrar este objeto, se violará la integridad referencial"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "Al borrar este objeto, se violará la integridad referencial."
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity. That's bad."
+msgstr "Al borrar este objeto, se violará la integridad referencial. Eso es malo."
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Denegar"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Dependen de este ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Dependencias: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Dependencia para %1 añadida"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Dependencia para %1 borrada"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Dependencia en %1 añadida"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Dependencia en %1 borrada"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Depende de"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "DependsOn"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Descendiente"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Describa el problema debajo"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Descripción"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Detalles"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Despliegue"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Mostrar Lista de Control de Acceso"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Mostrar Columnas"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Mostrar plantillas de scrip para esta cola"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Mostrar scrips para esta cola"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Modo de despliegue"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Mostrar búsquedas guardadas para este grupo"
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Despliega ticket #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr ""
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Hacer cualquier cosa y todo"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "No recargar esta página"
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "No mostrar los resultados de la búsqueda"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Descargar"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Descargar como fichero delimitado por tabuladores"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Esperado"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "La fecha esperada '%1' no pudo ser leída"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "ERROR: No se pudo cargar el ticket '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Editar"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Editar campos personalizados para %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Editar Campos Personalizados para todos los grupos"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Editar Campos Personalizados para todos los usuarios"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Editar Campos Personalizados para casos en todas las colas"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Editar relaciones"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Editar Consulta"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Editar Búsqueda"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Editar plantillas para la cola %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "Editar palabras clave"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Editar búsquedas guardadas para este grupo"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Editar acciones"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Editar plantillas del sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Editar plantillas para %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Editando configuración para la cola %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Editando configuración para el usuario %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Editando campo %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Editando los miembros del grupo %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Editando los miembros para el grupo personal %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Editando plantilla %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "La base o el destinatario deben ser especificados"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Correo"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "La dirección de correo ya está en uso"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "Correo Electrónico"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "Codificación para el correo"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Habilitado (Desmarcar esta caja deshabilita este campo personalizable)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Habilitado (Desmarcar esta caja deshabilita este campo personalizable)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Habilitado (Desmarcar esta caja, deshabilita esta cola)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "Campos Personalizables Habilitados"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Colas habilitadas"
+
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Estado %1 habilitado"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "Estado habilitado: %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Introducir multiples valores"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Entrar objetos o URIs para linkar a los objetos. Separar múltiples entradas con espacios."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Introducir un valor"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Entrar colas o URIs para linkar a las colas. Separar múltiples entradas con espacios."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Ingrese los números de ticket o las URL que llevan hacia el ticket. Separe multiples entradas con espacios"
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Entrar hasta %1 valor/es"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Error"
+
+#: NOT FOUND IN SOURCE
+msgid "Error adding watcher"
+msgstr "Error añadiendo observador"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Error en los parámetros para Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Error en los parámetros para Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Error en los parámetros para Queue->DeleteWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Error en los parámetros para Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Error en los parámetros para Queue->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Error en los parámetros para Ticket->DeleteWatcher"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Escalar tickets"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Estimado"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Todos"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Ejemplo"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "ExternalAuthId"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "ExternalContactInfoId"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Información extra"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Fallado en crear atributo de búsqueda"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Problema para encontrar el pseudogrupo de usuarios 'Privileged'"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Problema para encontrar el pseudogrupo de usuarios 'Unprivileged'"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Error al cargar el modulo %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "Error al cargar objeto para %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb."
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "Febrero"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Nombre de fichero"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Rellenar en multiples areas de texto"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Rellenar en multiples areas wikitext"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Rellenar en un area de texto"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Rellenar en un area wikitext"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Rellenar en hasta %1 areas de texto"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Rellenar en hasta %1 areas wikitext"
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Fin"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Prioridad Final"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "FinalPriority"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Encontrar grupo que"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Encontrar grupos cuyo"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Encontrar tickets nuevos/abiertos"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Encontrar gente que"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Encontrar tickets"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Aprobación final"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Primero"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Primera página"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Forzar cambio"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Formato"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Encontrado %quant(%1,ticket)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Objeto encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FreeformContactInfo"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "FreeformMultiple"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "FreeformSingle"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Vie."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Encabezados completos"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Obtener plantilla de fichero"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Obteniendo el usuario de la firma pgp"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Given to %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Campos Personalizados Globales"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Keyword Selections"
+msgstr "Selección de palabras clave globales"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Acciones Globales"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Configuración de Campos Personalizados Globales"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Plantilla global"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Ir"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "¡Ir!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Firma pgp correcta de %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Ir a página"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Ir a ticket"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Grupo"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Grupo %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Derechos del grupo"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "El grupo ya tiene miembros"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "El grupo no se pudo crear"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "El grupo no se pudo crear: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Grupo creado"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "El grupo no tiene este miembro"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Grupo no encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Grupo no entontrado\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Grupo no especificado\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Grupos"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Los grupos no pueden ser miembros de sus propios miembros"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Grupos coincidentes con el criterio de búsqueda"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Grupos a los que este usuario pertenece"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hola!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Hola, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historial"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "Historico del grupo %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "Historico del usuario %1"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Tel Casa"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Inicio"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Tengo %quant(%1,concrete mixer)."
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "Tengo [quant,_1,concrete mixer]."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Estoy perdido"
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Id"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identidad"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Si una aprobación es rechazada, rechazar la original y borrar las aprobaciones pendientes"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Si no se especifica ningún Solicitante, crear casos con este solicitante."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Si no se especifica ninguna cola, crear casos en esta cola."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Si esta herramienta estaba setgid, un usuario hostil local podría usar esta herramienta para conseguir acceso administrativo a RT."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Si ha actualizado algo más arriba, no olvide"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Valor ilegal para %1"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Campo inmutable"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Incluir campos personalizables deshabilitados en el listado."
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Incluir grupos deshabilitados en el listado."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Incluir colas deshabilitadas en el listado"
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Incluir usuarios deshabilitados en la búsqueda"
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Incluir pagina"
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Consulta Incompleta"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Consulta incompleta"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Prioridad inicial"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "InitialPriority"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Error de entrada"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr "Entrada debe coincidir con %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Interest noted"
+msgstr "Interest noted"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Error interno"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Error interno: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Tipo de grupo inválido"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Derechos inválidos"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "Tipo inválido"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Datos no válidos"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Propietario inválido. Estableciéndolo a 'nobody'."
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "Patron inválido: $1"
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Ãrea inválida"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Permiso inválido"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Valor inválido para %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Valor inválido para el campo personalizable"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Valor inválido para el estado"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Es increiblemente importante que los usuarios sin privilegios no puedan ejecutar esta herramienta"
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Es recomendable crear un usuario unix sin privilegios que pertenezca al grupo correcto y que tenga aceso a ejecutar esta herramienta"
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Tiene varios parámetros:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Italica"
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Items pendientes de mi aprobación"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Ene."
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "Enero"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Unirse o abandonar este grupo"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul."
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "Julio"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Todo"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun."
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "Junio"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Palabras clave"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Leng"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Lenguaje"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Grande"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Último"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Último contacto"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Último contactado"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Se le notifico por ultima vez"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Actualizado por ultima vez"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "LastUpdated"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Queda"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Permitir a este usuario acceder al RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Permitir que este usuario tenga privilegios adicionales"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Limitando propietario a %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Limitando cola a %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "El vínculo ya existe"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "El vínculo no pudo ser creado"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Vínculo creado (%2)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Vínculo borrado (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Vínculo no encontrado"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Vincular caso #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "Enlazar ticket %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Enlaces"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Cargar"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Cargar búsqueda guardada:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Modulos perl cargados"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Búsqueda cargada %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Dirección"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "El directorio del log %1 no pudo ser encontrado o no se pudo escribir en él.\\n RT no puede ejecutarse."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Autenticado como %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Entrar"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Salir"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Hacer propietario a"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Establecer estatus"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Establecer fecha de plazo"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Establecer fecha de resolución"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Establecer fecha de inicio"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Establecer fecha de inicio"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Establecer fecha de último cambio"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Establecer prioridad"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Establecer cola"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Establecer título"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Hacer este grupo visible al usuario"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Gestionar campos personalizados y valores de campos personalizados"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Administrar grupos y miembros"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Administrar propiedades y configuración que se aplique a todas las colas"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Administrar colas y propiedades especificas"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Administrar usuarios y contraseñas"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Marzo"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Mayo"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "May."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Miembro %1 añadido"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Miembro %1 borrado"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Miembro añadido"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Miembro borrado"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Miembro no borrado"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Miembro de"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "MemberOf"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Miembros"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "Pertenencia a %1 añadida"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "Pertenencia a %1 borrada"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Pertenencias"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "Pertenencias del usuario %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Fusión exitosa"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Fusión fallida. No se pudo establecer el EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Unión fallida. No se pudo establecer el Estado"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Fusionar dentro de"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Unido en %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Mensaje"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "Cuerpo del mensaje no mostrado porque es muy largo o no es texto plano."
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Mensaje no puedo ser grabado"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Mensaje grabado"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Mensajes acerca de este caso no serán enviados a..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Minutos"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "Parentesis no coincidentes"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Falta una clave primaria?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Movil"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Telefono Movil"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Modificar lista de control de acceso"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Modificar el campo personalizable %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Modificar Campos Personalizados que aplican a %1 para todo %2"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Modificar Campos Personalizados que aplican a todo %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Modificar los campos personalizables que se apliquen a todas las colas"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Modificar Derechos de Grupo"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Modificar Miembros"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Modificar Derechos"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Modificar plantillas Sript para esta cola"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Modificar Scrips para esta cola"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "Modificar ACLs de sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Modificar plantilla %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Modificar Derechos del Usuario"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Modificar un campo personalizable para la cola %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Modificar un campo personalizable que se aplique a todas las colas"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Modificar un scrip para la cola %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Modificar un scrip que se aplique a todas las colas"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Modificar objetos asociados para %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "Modificar fechas para # %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Modificar fechas para #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Modificar fechas para ticket # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Modificar campos personalizados globales"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Modificar privilegios globales de grupo"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Modificar privilegios globales de grupo."
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "Modificar privilegios globales para grupos"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "Modificar privilegios globales para usuarios"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Modificar acciones globales"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Modificar derechos globales de usuario"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Modificar privilegios globales de usuario"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Modificar metadatos del grupo o borrar grupo"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Modificar derechos de grupo para campos personalizados %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Modificar privilegios de grupo para %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Modificar privilegios de grupo para la cola %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Modificar miembros de este grupo"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Modificar la propia cuenta RT"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Modificar personas relacionadas con la cola %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Modificar personas relacionadas con el ticket #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Modificar acciones para la cola %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Modificar scrips que se aplican a todas las colas"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Modificar plantilla %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Modificar plantillas que se aplican a todas las colas"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Modificar el grupo %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Modificar los observadores de la cola"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Modificar el usuario %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Modificar el ticket # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Modificar el ticket #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Modificar tickets"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Modificar derechos de usuario para campos personalizados %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Modificar privilegios de usuario para el grupo %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Modificar derechos de usuario para la cola %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Modificar observadores para la cola '%1'"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ModifyACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "ModifyOwnMembership"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "ModifyQueueWatchers"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ModifyScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "ModifySelf"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "ModifyTemplate"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "ModifyTicket"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Lun."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Más acerca de %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Mover hacia abajo"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Move hacia arriba"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Múltiple"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Se debe especificar un nombre"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "Mis %1 casos"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Mis aprobaciones"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Mis aprobaciones"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Mis búsquedas salvadas"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Nombre"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Nombre en uso"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Se necesita aprobación del administrador del sistema"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Nunca"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Nuevo"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nuevas relaciones"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nueva contraseña"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nueva pendiente de aprobación"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Nueva búsqueda"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nuevo campo personalizable"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Nuevo grupo"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nueva contraseña"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Notificación de nueva contraseña enviada"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Nueva cola"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Nuevo recordatorio:"
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Nueva solicitud"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nuevos privilegios"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Nuevo scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Nueva búsqueda"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Nueva plantilla"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Nuevo caso"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "El ticket nuevo no existe"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Nuevo usuario"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Nuevo usuario llamado"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nuevo observador"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Establecer nueva ventana "
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Siguiente"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Siguiente Página"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Siguiente página"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Alias"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Alias"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "No existe Class definida"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "No hay campo personalizable"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "No existe CustomField definido"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "No hay grupo definido"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "No existe Consulta"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "No hay cola definida"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "No se encontró el usuario. Por favor consulte al administrador.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "No hay plantilla"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "No se especificó el ticket. Abortada la transacción"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "No se especificó ticket. Abortando las modificaciones al ticket\\n\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "No acción"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "No se ha especificado ninguna columna"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Comando no encontrado\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "No hay comentarios sobre este usuario"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "No hay ningún archivo adjunto"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "No hay descripción para %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "No hay grupo especificado"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "No existen grupos que coincidan con el criterio de búsqueda."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Mensaje no adjuntado"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "No hay contraseña definida"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "No tiene privilegios para crear colas"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "No tiene privilegios para crear tickets en la cola '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "No tiene privilegios para crear usuarios"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "No tiene privilegios para mostrar el ticket"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Sin permiso para ver la actualización del ticket"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "No hay un principal especificado"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "No hay principales seleccionados"
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "No hay colas que concuerden con los criterios de búsqueda"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "No se encontraron derechos"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Sin privilegios concedidos"
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "No hay búsqueda cargada"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "No hay búsqueda sobre la que operar"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Sin asunto"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "No se especificó el identificador del ticket"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "No se especificó el tipo de transacción"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "No se especificó email o usuario"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "No se encontraron usuarios que concuerden con los criterios de búsqueda"
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Usuario no encontrado. El manejador cvs está deshabilitado. Por favor consulte a su administrador.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "No se envió ningun valor a _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Nadie"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Campo no existente?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "No autenticado"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "No autenticado."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "No establecido"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "No se ha implementado."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "No está implementado..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Notas"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "La notificación no se pudo enviar"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Notificar AdminCcs"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Notificar AdminCcs como comentario"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Notificar Ccs"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Notificar Ccs como Comentarios"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Notificar otros destinatarios"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Notificar otros destinatarios como comentario"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Notificar al propietario"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Notificar al propietario como comentario"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Notificar al Propietario de su caso rechazado"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Notificar al Propietario de que su caso ha sido aprobado por todos los aprobadores"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Notificar al Propietario de que su caso ha sido aprobado por algún aprobador"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Notificar propietarios y AdminCcs de nuevos items pendientes de aprobación"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Notificar solicitantes"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Notificar solicitantes y Ccs"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Notificar solicitantes y Ccs como comentario"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Notificar solicitantes, Ccs y AdminCcs"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Notificar solicitantes, Ccs y AdminCcs como comentario"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "Noviembre"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "O"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "No se pudo crear el objeto"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Objeto no pudo ser borrado"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objeto creado"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objeto borrado"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Objeto de tipo %1 no puede llevar campos personalizados"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Oct."
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "Octubre"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr ""
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr ""
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "en "
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Al comentar"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "On Correspond"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Al crear"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Al cambiar de propietario"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Al cambiar de prioridad"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Al cambiar de cola"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Al resolver"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Al cambiar de status"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Al hacer transacción"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Solo muestra aprobaciones para solicitudes creadas despues de %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Solo muestra aprobaciones para solicitudes creadas antes de %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Solo mostrar campos personalizados para:"
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Abierto"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Abrirlo"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Solicitudes abiertas"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Casos abiertos"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Tickets abiertos (del listado) en una nueva ventana"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Tickets abiertos (del listado) en otra ventana"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Open tickets on correspondence"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Opciones"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Ordenar por"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Ordenación y clasificación"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organización"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Ticket originario: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Email saliente acerca de un comentario grabado"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Email saliente grabado"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Pasada la fecha de gracia, la prioridad se mueve a"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Tickets poseidos"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "OwnTicket"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Propietario"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Propietario cambiado de %1 a %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "El Propietario no pudo ser establecido."
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Propietario cambiado forzosamente de %1 a %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "El propietario es"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Pagina %1 de %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Buscapersonas"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Buscapersonas Tel."
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Padres"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Contraseña"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Recordatorio de contraseña"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Contraseña cambiado"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "La Contraseña necesita ser al menos de %1 caracteres de longitud"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Contraseña establecida"
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Contraseña demasiado corta"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Contraseña: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Contraseña: Permiso Denegado"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Las Contraseña no coinciden"
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Las Contraseña no coinciden. Contraseña no cambiada"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Personas"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Realizar una acción definida por el usuario"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Permiso denegado"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "Permiso denegado"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "Permisos denegados"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Grupos personales"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Grupos personales"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Grupos personales:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Números de teléfono"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Placeholder"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Preferencias"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Prefs"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Preparación cortada"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Prev"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Página Anterior"
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Página anterior"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "No se encontró el principal %1"
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioridad"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "La prioridad empieza en"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Privadas:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilegiado"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Estado privilegiado: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Usuarios privilegiados:"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudogrupo para uso interno"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Constructor de Consulta"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Cola"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Cola %1 no encontrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Cola '%1' no encontrada\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Keyword Selections"
+msgstr "Selecciones de palabras clave de la cola"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Nombre de la cola"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Acciones de la cola"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "La cola ya existe"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "La cola no se pudo crear"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "La cola no se pudo cargar"
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Cola creada"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "No se especifico ninguna cola"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Cola no encontrada"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Colas"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Colas que YO administro"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Colas en las que YO soy AdminCc"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Búsqueda rápida"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Creación rápida de caso"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 para %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 de <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Derechos reservados 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Administración del RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "Error de autenticación en RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "Rechazo del RT: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "Error de configuración del RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "Error crítico en RT. El mensaje no fue grabado!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "Error del RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT recibió correo (%1) de sí mismo."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Recieved mail (%1) from itself."
+msgstr "RT recibió correo (%1) de sí mismo."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT AutoServicio / Tickets cerrados"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr ""
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT en un vistazo"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT no te pudo autenticar."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT no pudo encontrar el solicitante a través de una búsqueda a la base de datos externa"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT no pudo encontrar la cola: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT no pudo almacenar tu sesión."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT no pudo validar esta firma PGP. \\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT para %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT para %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT ha procesado tus comandos"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT es &copy; Copyright 1996-%1 de Jesse Vincent &lt;jesse@bestpractical.com&gt;. Es distrbuido bajo <a href=\"http://www.gnu.org/copyleft/gpl.html\">la version 2 de la licencia GNU GPL (General Public License)</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT cree que este mensaje puede ser un mensaje rebotado"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT buscará por cualquier otra cosa en los asuntos de los casos."
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT procesará este mensaje como si fuera uno no firmado\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "El modo de comandos por correo de RT requiere autenticación PGP. Ya sea que no haya firmado su mensaje, o que su firma no pueda ser verificada."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Nombre real"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "Nombre real"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "Referencia para %1 añadida"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "Referencia para %1 borrada"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "Referencia para %1 añadida"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "Referencia para %1 borrada"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Referenciado por"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Hace referencia a"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "RefersTo"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Refinar"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Refinar la búsqueda"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Refrescar esta página cada %1 minutos"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "Recordario '%1' añadida"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "Recordatorio '%1' completado"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "Recordatorio '%1' reabierto"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "Recordatorio caso #%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "Recordatorios"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "Recordatorios para caso #%1"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Quitar AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Quitar Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Quitar solicitante"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Responder"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Direccion de Respuesta"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Responder a solicitantes"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Responder a los tickets"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "ReplyToTicket"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Informes"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Solicitante"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Dirección de correo del solicitante"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Solicitante(s)"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "RequestorAddresses"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Solicitantes"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Las solicitudes entran en vencimiento en"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Requerido parámetro '%1' no especificado"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Borrar"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Residencia"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Resolver"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Resolver ticket #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Resuelto"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Resuelto por propietario"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Resuelto en rango de fechas"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Casos resueltos en el período, agrupado por propietario"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Casos resueltos, agrupados por propietario"
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Responder a los solicitantes"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Resultados"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Resultados por página"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Confirmar contraseña"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Revertir"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Privilegio %1 no encontrado para %2 %3 referente a %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Privilegio delegado"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Privilegio otorgado"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Privilegio cargado"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Privilegio no pudo ser revocado"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Privilegio no encontrado"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Privilegio no cargado"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Privilegio revocado"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Privilegios"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "No se pudieron conceder los privilegios a %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "No se pudieron revocar los privilegios de %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Roles"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "RootApproval"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Filas por caja"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Filas por página"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sab."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Guardar"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Guardar Cambios"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Guardar Preferencias"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Guardar cambios"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "Búsquedas guardadas %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Búsquedas guardadas"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Acción creada"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Campos de Acción"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Acción borrada"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Acciones"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Acciones para %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Acciones que se aplican a todas las colas"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Búsqueda"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Criterios de búsqueda"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Preferencias de Búsqueda"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Buscar aprobaciones"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "Búsqueda de casos"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "Búsqueda de casos. Entrar números <strong>id</strong>, <strong>colas</strong> por nombre, Propietarios por <strong>usuario</strong> y Solicitantes por <strong>dirección email</strong>. RT buscará por cualquier otra cosa que entres en cuerpos y adjuntos de casos."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Opciones de búsqueda"
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "Resultados de búsqueda agrupados por %1"
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Búsqueda actualizada: %1"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "Las busquedas por texto completo en cada caso puede llevar mucho tiempo, pero si necesita hacerlo, puede buscar por cualquier palabra en el histórico completo de casos para cualquier palabra escribiendo: <b>fulltext:<i>palabra</i></b>."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Seguridad:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "See also: %1"
+msgstr "Ver tambien: %1"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Ver campos personalizados"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Ver mensajes email salientes exactos y sus receptores"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Ver comentario privado de caso"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Ver sumarios de caso"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "VerCamposPersonalizados"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "VerGrupo"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "VerCola"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Seleccionar un Campo Personalizado"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Seleccione un grupo"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Seleccione una cola"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Seleccionar una cola para tu nuevo caso"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Seleccione un usuario"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Seleccionar un campo personalizable"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Seleccionar campos personalizables para todos los grupos de usuarios"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Seleccionar campos personalizables para todos los usuarios"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Seleccionar campos personalizables para casos en todas las colas"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Seleccionar campos personalizables para transacciones de casos en todas las colas"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Seleccionar grupo"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Seleccionar valores múltiples"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Seleccionar un valor"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Seleccionar cola"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Seleccionar colas para ser mostradas en la pagina \"RT en un vistazo\""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Seleccionar accion"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Selecionar plantilla"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Seleccionar hasta %1 valores"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Seleccionar usuario"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "SelectMultiple"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "SelectSingle"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Campos Personalizados Seleccionados"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Objetos Seleccionados"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Selecciones modificadas. Por favor graba tus cambios"
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Autoservicio"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Enviar mail a todos los observadores"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Enviar mail a todos los observadores como comentario"
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Enviar mail a los solicitantes y Ccs"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Enviar mail a los solicitantes y Ccs como comentario"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Envia un mesaje a los solicitantes"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Enviar mail a los Ccs y Bccs listados explicitamente"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Enviar correo a los Ccs"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Enviar correo a los Ccs como comentario"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Envia mail a los Ccs administrativos"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Envia mail a los Ccs administrativos como comentario"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Enviar mail al propietario"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep."
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "Septiembre"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Mostrar"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Mostrar Aprobaciones"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Mostrar Columnas"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Mostrar resultados"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Mostrar peticiones aprobadas"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Mostrar lo básico"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Mostrar solicitudes denegadas"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Mostrar detalles"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Mostrar solicitudes pendientes"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Mostrar solicitudes esperando otras aprobaciones"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Mostrar ticket en un comentario privado"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Mostrar resumen del ticket"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "ShowACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "ShowScrips"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "ShowTemplate"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "ShowTicket"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "ShowTicketComments"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Validarse como solicitante de ticket o ticket o cola Cc"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Validarse como ticket o cola AdminCc"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Firma"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "Validado como %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Búsqueda simple"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Sencillo"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Tamaño"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Saltar Menu"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Ordenar"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Clave de ordenación"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Ordenar resultados por"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "Ordenamiento"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Pendiente"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Página de inicio"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Comenzado"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "La fecha de inicio '%1' no se pudo leer"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Comienzo"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Comenzado por"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "La fecha de inicio '%1' no se pudo ser leer"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Estado"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Estado"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Cambio de status"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Estado cambiado de %1 a %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "StatusChange"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Robar"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Robar casos"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "RobarCaso"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Robado de %1"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Estilo"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Asunto"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Asunto cambiado a %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Enviar"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Submit Workflow"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Completado"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Dom."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "Superusuario"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Sistema"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Configuración del Sistema"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Error del sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. Right not granted."
+msgstr "Error de sistema. Derecho no concedido"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. right not granted"
+msgstr "Error de sistema. Derecho no concedido"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Herramientas del Sistema"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Error del sistema. Privilegio no delegado."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Error del sistema. Privilegio no otorgado"
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "Error de sistema. Incapaz de conceder permisos"
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Grupos del sistema"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRolegroup for internal use"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Coger"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Coger casos"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "CogerCaso"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Cogido"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Plantilla"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Plantilla #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Plantilla borrada"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Plantilla no encontrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Plantilla no encontrada\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Plantilla procesada"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "Error sintactico de Plantilla"
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Plantillas"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Plantillas de %1\\n"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Ese es el valor actual"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Ese no es un valor para este campo personalizable"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Este es el mismo valor"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Ese principal ya tiene ese derecho"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Ese principal ya es un %1 para esta cola"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Ese principal ya es un %1 para este ticket"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Ese principal no es un %1 para esta cola"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Ese principal no es un %1 para este ticket"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Esa cola no existe"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Ese ticket tiene dependencias sin resolver"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Ese usuario ya tiene ese privilegio"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Ese usuario ya posee ese ticket"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Ese usuario no existe"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Ese usuario ya tiene privilegios"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Ese usuario ya está sin privilegios"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Ese usuario ahora tiene privilegios"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Ese usuario ya no tiene privilegios"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "Este usuario ya no tiene privilegios"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Ese usuario puede no poseer tickets en esa cola"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Ese no es un identificador numérico"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Lo básico"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "El CC de un ticket"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "El CC administrativo de un ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "El comentario ha sido grabado"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "El siguiente comando encontrará todos los tickets activos en la cola 'general' y pondra su prioridad a 99 si no han sido tocados en 4 horas:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "Los siguientes comandos no han sido procesados:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Ha sido establecido el nuevo valor"
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "El propietario de un ticket"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "El solicitante de un ticket"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Estos comentarios generalmente no están visibles para el usuario"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Este campo personalizado no se aplica a este objeto"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Esta caracteristica esta solo disponible para administradores del sistema"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Este mensaje sera enviado a..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Este ticket %1 %2 (%3)"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Esta herramiento permite al usuario ejectutar modulos perl arbitrarios desde dentro de RT"
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Parece que esta transacción no tiene contenido"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Los %1 tickets de mayor prioridad de este usuario"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "Los 25 casos de mayor prioridad de este usuario"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Jue."
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Ticket # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "Actualización Jumbo para el ticket # %1: %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Actualización Jumbo para el ticket #%1: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Ticket #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Ticket %1 creado en la cola '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Ticket %1 cargado\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Ticket %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Campos Personalizables de Caso"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Historial del ticket # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Id del ticket:"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Ticket resuelto"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Transacciones del Caso"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Archivos adjuntos del ticket"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Contenido del ticket"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Tipo de contenido del ticket"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "No se pudo crear el ticket debido a un error interno"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Caso creado"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Creación del ticket fallida"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Caso borrado"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Id de ticket no encontrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "Caso eliminado"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Metadata de casos"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Caso no encontrado"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Estado del ticket cambiado"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Observadores del caso"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "Modulo de búsqueda TicketSQL"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Casos"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Casos %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Casos %1 por %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "Casos creados despues del"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "Casos creados antes del"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Casos de %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "Casos resueltos despues del"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "Casos resueltos antes del"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Casos que dependen de esta aprobación:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Tiempo Estimado"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Tiempo Restante"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Tiempo Trabajado"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Tiempo restante"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Tiempo para mostrar"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Tiempo trabajado"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "TimeLeft"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "TimeWorked"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Título"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Para generar una comparación de este cometido:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "Para generar una comparación de este cometido:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Última actualización"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Herramientas"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transacción"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transacción %1 limpiada"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transacción creada"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Campos personalizables de Transacción"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Transaction->Create no pudo, ya que no especificó un ID de ticket"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transaction->Create no pudo, ya que no especificó un tipo de objeto e ID"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Las transacciones son inmutables"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Intentando borrar el privilegio: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Mar."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Tipo"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "No implementado"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Usuario en Unix"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Usuario en Unix"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Codificación de contenido desconocida: %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Campo desconocido: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Ilimitado"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Búsqueda sin nombre"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "No privilegiado"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Campos Personalizados No Seleccionados"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Objetos No Seleccionados"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "No cogido"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Actualizar"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Id de actualización"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Actualizar Caso"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Tipo de actualización"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Actualizar todos estos casos al mismo tiempo"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Actualizar correo"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Actualizar múltiples casos"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Actualizar nombre"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Actualización no grabada."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Actualizar tickets seleccionados"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Actualizar firma"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Actualizar ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Actualización de ticket # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Actualizar ticket #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Actualizar ticket #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "El tipo de actualización no fue ni respuesta ni comentario"
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Actualizado"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Subir"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Subir multiples ficheros"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Subir multiples imagenes"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Subir un fichero"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Subir una imagen"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Subir hasta %1 ficheros"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Subir hasta %1 imagenes"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Subir cambios"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Usar otras herramientas administrativas de RT"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Usuario %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Usuario %1 Contraseña: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "No se encontro el Usuario '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Usuario '%1' no encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Usuario '%1' no encontrado\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Definido por el usuario"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Condiciones y acciones Definidas por el Usuario"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "ID de usuario"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Id de usuario"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Privilegios de usuario"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "El usuario no pudo ser creado: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Usuario creado"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Grupos definidos por el usuario"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Usuario cargado"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Usuario notificado"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Vista de usuario"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Grupos definidos por el usuario"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Nombre de usuario"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Usuarios"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Usuarios que concuerdan con los criterios de búsqueda"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Consulta Valida"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "Valor de la cola"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Valores"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "WatchAsAdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "Observador cargado"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Observadores"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "Codificación de Web"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Mie."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Cuando un ticket ha sido aprobado por todos los aprobadores, añadir correspondencia al ticket original"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Cuando un ticket ha sido aprobado por cualquier aprobador, añadir correspondencia al ticket original"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Cuando un ticket se crea"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Cuando una aprobación de ticket se crea, notifica al propietario y AdminCC del item que espera su aprobación"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Cuando pasa cualquier cosa"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Siempre que un ticket este sin resolver"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Siempre que el propietario de un ticket cambie"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Siempre que la prioridad de un ticket cambie"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Siempre que la cola de un ticket cambie"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Siempre que el estado de un ticket cambie"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Siempre que ocurra una condición definida por el usuario"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Siempre que venga algún comentario"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Siempre que venga correspondencia"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Trabajo"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Tel Trabajo"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Trabajado"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Usted ya es propietario de este caso"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Usted no es un usuario autorizado"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Usted solo puede reasignar casos que posee o que no posee nadie³"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "No tiene permiso para ver ese ticket.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Usted encontró %1 casos en la cola %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Se ha desconectado del sistema RT"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "No tiene permiso para crear tickets en esa cola."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "No puede crear solicitudes en esa cola."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Es bienvenido a regresar en cualquier momento."
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Sus solicitudes %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "Su administrador del RT ha desconfigurado el alias de correo que invoca el RT"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Su petición ha sido aprobada por %1. Otras aprobaciones pueden estar pendientes todavia"
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Su petición ha sido aprobada."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Su petición ha sido rechazada"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Su petición ha sido rechazada"
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Nombre o contraseña de usuario incorrectos"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Código Postal"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[sin asunto]"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "permitir la creación de búsquedas guardadas"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "permitir la carga de búsquedas guardadas"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "como priviligiado para %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "tabla"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "cerrado"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "contiene"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "contenido"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "content-type"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "Respuesta (probablemente) no enviada"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "Correspondencia enviada"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "días"
+
+#: NOT FOUND IN SOURCE
+msgid "dead"
+msgstr "muerto"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "borrar"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "borrado"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "no coincide"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "no contiene"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "igual a"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "error: no se puede mover abajo"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "error: no se puede mover a la izquierda"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "error: no se puede mover arriba"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "error: nada a borrar"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "error: nada a mover"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "falso"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "nombre de archivo"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "mayor que"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "grupo '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "agrupado por %1"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "horas"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "id"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "es"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "no es"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "menor que"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "contiene"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minutos"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "modificaciones\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "meses"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "nuevo"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "sin nombre"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "sin valor"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "ninguno"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "no igual a"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "notlike"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "abierto"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "grupo personal '%1' para usuario '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "Cola %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "rechazado"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "resuelto"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sec"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "mostrar etiqueta de Configuración"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "pendiente"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr "estilo: %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "sistema %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "grupo del sistema '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "el componente que llama no especifica por qué"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "ticket #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "verdadero"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "grupo sin descripción %1"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "grupo sin descripción %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "usuario %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "semanas"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "con plantilla %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "años"
+
diff --git a/rt/lib/RT/I18N/fi.po b/rt/lib/RT/I18N/fi.po
new file mode 100644
index 0000000..ca78562
--- /dev/null
+++ b/rt/lib/RT/I18N/fi.po
@@ -0,0 +1,6188 @@
+# Finnish localization catalog for Request Tracker (RT)
+# First Author: Janne Pirkkanen <jp@oppipoika.net>, Jul 2002
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2002-07-08 17:41+0200\n"
+"PO-Revision-Date: 2005-10-03 13:45-0400\n"
+"Last-Translator: Tuukka Vainio <tuukka.vainio@utu.fi>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr ""
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr ""
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %3.%2 %7 %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 lisätty"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 sitten"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1: %2 muutettu arvoon %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 poistettu"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 pohjalla %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 tässä tapauksessa\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "näkyvillä %1 - %2"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr ""
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr ""
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScriptAction %1 ladattu"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 lisätty arvoksi %2lle"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "%1 aliakset vaativat tapauksen id:n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "%1 aliakset vaativat tapauksen id:n "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "%1 aliakset vaativat tapauksen id:n (osoite %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 - %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 muutettu arvosta %2 arvoon %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr ""
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "Arvoa %1 ei voitu asettaa arvoksi %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 ei voinut suorittaa tapahtumaa (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 ei voinut asettaa tilaa päätetyksi. RT:n tietokanta saattaa olla vioittunut."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr ""
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "%1 tärkeintä omistamaani tapausta..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "%1 tärkeintä tilaamaani tapausta..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 ei ole enää %2 tälle työjonolle"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 ei ole enää %2 tälle tapaukselle"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 ei ole enää kentän %2 arvo"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 ei ole kelvollinen työjonon id"
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 ei näy"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 onnistui\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 tyyppi tuntematon viestille $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 tyyppi tuntematon viestille %2"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 päättää kaikki päätetyn ryhmän jäsentapaukset."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 jäädyttää [paikallisen] BASE jos se riippuu linkitetystä tapauksesta [tai on sen jäsen]."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: liitetiedostoa ei ole määritelty"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' ei kelpaa tilan arvoksi"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' ei ole tunnettu tapahtuma."
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(Rastita laatikko poistaaksesi ryhmän jäsenen)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Rastita laatikko poistaaksesi toiminnon)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Rastita laatikko poistaaksesi)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(Rastita laatikko poistaaksesi)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Syötä tapausten numerot tai www-osoitteet, välilyönneillä erotettuina)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Jos jätetään tyhjäksi, palaa arvoon %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(Ei arvoa)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Ei kenttiä)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Ei jäseniä)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Ei toimintoja)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Ei pohjia)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Lähettää piilokopion vain tästä päivityksestä pilkulla erotettuihin sähköpostiosoitteisiin. <b>Ei lisää</b> pysyviksi vastaanottajiksi.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Lähettää piilokopion vain tästä päivityksestä pilkulla erotettuihin sähköpostiosoitteisiin. <b>Ei lisää</b> pysyviksi vastaanottajiksi.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Lähettää kopion tästä päivityksestä pilkulla erotettuihin hallinnollisiin sähköpostiosoitteisiin. <b>Lisää</b> pysyviksi vastaanottajiksi.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Lähettää kopion vain tästä päivityksestä pilkulla erotettuihin sähköpostiosoitteisiin. <b>Ei lisää</b> pysyviksi vastaanottajiksi.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Lähettää kopion vain tästä päivityksestä pilkulla erotettuihin sähköpostiosoitteisiin. <b>Ei lisää</b> pysyviksi vastaanottajiksi.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Lähettää kopion tästä päivityksestä pilkulla erotettuihin sähköpostiosoitteisiin. <b>Lisää</b> pysyviksi vastaanottajiksi.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr ""
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(tyhjä)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(ei nimiä listattuna)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(ei otsikkoa)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(ei arvoa)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr ""
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(vain yksi tapaus)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(odottaa hyväksyntää)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(odottaa toisia tapauksia)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(pakollinen)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(nimetön)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "25 tärkeintä omistamaani tapausta..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "25 tärkeintä tilaamaani tapausta..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr ""
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr ""
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Uusi tapaus\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Tyhjä pohja"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE ei löytynyt"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE:ja voi vain luoda ja poistaa."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Peruutetaan tarkoittamattomien tapausmuutosten välttämiseksi.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Omat asetukset"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Pääsynvalvonta"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Tapahtuma"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Tapahtumaa %1 ei löydetty"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr ""
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr ""
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Lisää kopio ylläpidolle"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Lisää kopio"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr ""
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr ""
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Lisää useampi tiedosto"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Lisää tilaaja"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Lisää uusi yleinen toiminto"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Lisää toiminto tähän työjonoon"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Lisää kaikille työjonoille yhteinen toiminto"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Lisää kommentteja tai vastauksia valituille tapauksille"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Lisää jäseniä"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Lisää uusia tarkkailijoita"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Lisätty toimeksiantaja %1:ksi tähän työjonoon"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Lisätty toimeksiantaja %1:ksi tälle tapaukselle"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Osoite1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Osoite2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Kopio ylläpidolle"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr ""
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr ""
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Työjonojen ylläpito"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Käyttäjien ylläpito"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Ylläpito/Yleiset asetukset"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Ylläpito/Ryhmät"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Ylläpito/Työjono/Perustiedot"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "Kopio ylläpidolle"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr ""
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr ""
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr ""
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Kopio ylläpidolle"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Tarkennettu haku"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Jälkeen"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Ikä"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Kaikki kentät"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Kaikki työjonot"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr ""
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Hyväksyntä"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr ""
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr ""
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Hyväksynnän lisätiedot"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr ""
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr ""
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Hyväksy"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Hyväksyjän merkintöjä: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "huhti"
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "huhtikuu"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Nouseva"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Liitä"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Liitä tiedosto"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Liitä tiedosto"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Liitteen '%1' lataaminen ei onnistunut"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Liitetiedosto luotu"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Liitetiedoston nimi"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Liitetiedostot"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr ""
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "elo"
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "elokuu"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr ""
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Automaattivastaus tilaajille"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Virheellinen PGP allekirjoitus: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Virheellinen liitteen numero. Liitetiedostoa '%1' ei löytynyt\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Virheellistä dataa kentässä %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Virheellinen tapahtuman numero liitetiedostolle. %1 pitäisi olla %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Perustiedot"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Piilokopio"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Muista tallentaa muutokset"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "ennen"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Tyhjä"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "Osoite tähän kyselyyn (selaimen kirjanmerkkeihin)"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Lyhyet otsikot"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Tapausten ryhmäpäivitys"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Järjestelmäkäyttäjien muokkaus ei ole sallittua"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Näkeekö toimeksiantaja tämän työjonon"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Kentän lisääminen ilman nimeä ei onnistu"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Tapausta ei voi linkittää itseensä"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Et voi yhdistää jo yhdistettyyn tapaukseen. Sinun ei pitäisi saada tätä virhettä koskaan."
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr ""
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Sekä juuren ja kohteen määritteleminen samalla ei ole mahdollista"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Käyttäjää ei voitu luoda: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Kopio"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Muuta salasana"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr ""
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Valitse laatikko poistaaksesi"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Valitse laatikko peruaksesi oikeuden"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Lapset"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Kaupunki"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Suljettu"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Suljetut tapaukset"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Komentoa ei ymmärretty!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Kommentoi"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Kommenttien osoite"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Kommenttia ei tallennettu"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Kommentoi tapauksia"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Kommentit"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Kommentti (ei lähetetä tilaajille)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Kommentti (ei lähetetä tilaajille)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Kommentit kohteesta %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Kommentit tästä käyttäjästä"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Kommentit lisätty"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Suorita tumppi"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Kokoa rajoitukset"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Ehto"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Ehtoa ei löydetty"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Ylläpito"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Vahvista"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "Yhteystietojärjestelmä"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Järjestelmä ei ymmärrä päivää '%1'"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Sisältö"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr ""
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Kirjeenvaihdon osoite"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Kirjeenvaihto lisätty"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Vastausta ei tallennettu"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Uuden tiedon lisääminen kenttään epäonnistui"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "Uuden kentän lisäys tapaukselle epäonnistui. %1 "
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr ""
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Omistajaa ei voitu vaihtaa."
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Uuden kentän lisääminen epäonnistui"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr ""
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Ryhmän luominen epäonnistui"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Pohjan luonti epäonnistui: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Tapauksen luonti epäonnistui. Työjonoa ei ole asetettu"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Käyttäjän luonti epäonnistui"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Tapausta numerolla %1 ei löytynyt."
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Ryhmää %1 ei löytynyt."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Käyttäjää ei löydetty eikä pystytty luomaan"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Tätä toimeksiantajaa ei löytynyt"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Käyttäjää %1 ei löytynyt."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Ryhmän lataaminen ei onnistunut"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Ei voinut tehdä toimeksiantajaa %1:ksi tälle työjonolle"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Ei voinut tehdä toimeksiantajaa tälle tapaukselle: %1"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Toimeksiantajaa ei voitu poistaa tältä työjonolta: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Toimeksiantajaa ei voitu poistaa tältä tapaukselta: %1"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Jäsenen lisääminen ryhmään ei onnistunut"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Tapahtuman luominen ei onnistunut: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Järjestelmä ei gpg:n vastauksesta ymmärtänyt mitä tehdä\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Ryhmää ei löytynyt\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Riviä ei löytynyt"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Toimeksiantajaa ei löytynyt"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Arvoa ei löytynyt"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "Tarkkailijaa ei löytynyt"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Käyttäjää ei löytynyt\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Ei onnistuttu lataamaan käyttäjää %1 tietokannasta.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "RT-asetustiedoston lataaminen ei onnistunut:'%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Toimintojen lataaminen ei onnistunut."
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Ryhmän %1 lataaminen ei onnistunut"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Linkin lataaminen ei onnistunut"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr ""
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Työjonon lataaminen ei onnistunut"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Työjonon %1 lataaminen ei onnistunut"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Toiminnon lataaminen ei onnistunut"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Pohjan lataaminen ei onnistunut"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Tämän käyttäjän lataaminen ei onnistunut (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Tapauksen '%1' lataaminen ei onnistunut"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Maa"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Luo"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Luo tapauksia"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Luo kenttä"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Luo kenttä työjonolle %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Luo kenttä, jota sovelletaan kaikkiin työjonoihin"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Luo uusi kenttä"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Luo uusi yleinen toiminto"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Luo uusi ryhmä"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Luo uusi henkilökohtainen ryhmä"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Luo uusi työjono"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Luo uusi toiminto"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Luo uusi pohja"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Luo uusi tapaus"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Luo uusi käyttäjä"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Luo uusi työjono"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Luo työjono nimeltään"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Luo tapaus"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Luo toiminto työjonolle %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Luo pohja"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "Luonti epäonnistui %1 / %2 / %3 "
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "Luonti epäonnistui: %1/%2/%3"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Luo uusia tapauksia tämän toiminnon pohjalta"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Luo tapaus"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Luo tapauksia tähän työjonoon"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Luo, muokkaa ja poista kenttiä"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Luo, poista ja muokkaa työjonoja"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Luo, poista ja muokkaa kenen tahansa henkilökohtaisten ryhmien jäseniä"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Luo, poista ja muokkaa henkilökohtaisten ryhmien jäseniä"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Luo, poista ja muokkaa käyttäjiä"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr ""
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Luotu"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Luotu kenttä %1"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Luotu pohja %1"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr ""
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Tämänhetkiset suhteet"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Tämänhetkinen toiminto"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Tämänhetkiset jäsenet"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Tämänhetkiset oikeudet"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Tämänhetkiset hakumääritteet"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Tämänhetkiset tarkkailijat"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Kentät"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Kenttä %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Kentällä %1 on arvo"
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Kentällä %1 ei ole arvoa"
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Kenttää %1 ei löytynyt"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Kenttä poistettu"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Kenttää ei löytynyt"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Kentän arvoa %1 ei löytynyt kentälle %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Kentän arvo muutettu arvosta %1 arvoon"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Kentän arvoa ei pystytty poistamaan"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Kentän arvoa ei löydetty"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Kentän arvo poistettu"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr ""
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Päivät"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "joulu"
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "joulukuu"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr ""
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr ""
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr ""
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr ""
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr ""
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr ""
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Oletustapahtumapohja"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Oletus: %1/%2 muutettu arvosta %3 arvoon %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Delegoi oikeuksia"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr ""
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr ""
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Poista"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Poista tapaukset"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Tämän objektin poistaminen saattaa rikkoa tietokannan viitteet"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Tämän objektin poistaminen rikkoo tietokannan viitteet"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Tämän objektin poistaminen rikkoo tietokannan viitteet"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr ""
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Tästä tapauksesta riippuu"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Riippuvuudet: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Riippuu tapauksesta"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Laskeva"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Kuvaa tapausta"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Kuvaus"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Yksityiskohdat"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Näytä"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Näytä käyttöoikeuslista"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Näytä työjonon toimintopohjat"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Näytä työjonon toiminnot"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Näkymä"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Näytä tapaus #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr ""
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Tee mitä tahansa ja kaikki"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Älä päivitä tätä sivua"
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Älä näytä hakutuloksia"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Lataa"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr ""
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Erääntyy"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Erääntymispäivää '%1' ei onnistuttu tulkkaamaan järjestelmälle."
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "VIRHE: Tapauksen '%1' lataaminen ei onnistunut: %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Muokkaa"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Muokkaa työjonon %1 kenttiä"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Muokkaa suhteita"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Muokkaa työjonon %1 pohjia"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "Muokkaa avainsanoja"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Muokkaa toimintoja"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Muokkaa järjestelmäpohjia"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Muokkaa työjonon %1 pohjia"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Muokataan työjonon %1 asetuksia"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Muokataan käyttäjän %1 asetuksia"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Muokataan kenttää %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Muokataan ryhmän %1 jäseniä"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Muokataan henkilökohtaisen ryhmän %1 jäseniä"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Muokataan pohjaa %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Joko juuri tai kohde täytyy olla määritelty"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Sähköposti"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Sähköpostiosoite on jo käytössä"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "Sähköpostiosoite"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "Sähköpostin koodaus"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Aktiivinen (rastin poistaminen passivoi kentän)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Aktiivinen (rastin poistaminen passivoi ryhmän)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Aktiivinen (rastin poistaminen passivoi työjonon)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Aktiiviset työjonot"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Aktivoitu tila %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Anna useampi arvo"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Anna yksi arvo"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr ""
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Lisää tapausten numerot tai www-linkit. Erota useammat välilyönneillä."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr ""
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Virhe"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Virhe parametreissa: Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Virhe parametreissa: Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Virhe parametreissa: Ticket->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Virhe parametreissa: Ticket->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr ""
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr ""
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Kaikki"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "Ulkoinen autentikointitunnus"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "Ulkoinen yhteystietotunnus"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Lisätieto"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "'Erioikeutettu'-valeryhmää ei löytynyt"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "'Erioikeudeton'-valeryhmää ei löytynyt"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "helmi"
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "helmikuu"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Fin"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Loppuprioriteetti"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Etsi ryhmä, jonka"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Etsi uudet/avoimet tapaukset"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Etsi käyttäjät, joiden"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Hae tapauksia"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Ensimmäinen"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Viimeinen sivu"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr ""
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Pakota muutos"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr ""
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr ""
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "Vapaamuotoiset yhteystiedot"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Pe"
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Kokonaiset otsikot"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Annettu käyttäjälle %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Yleiset"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Yleiset toiminnot"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Yleinen pohja: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr ""
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Ok!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Hyvä PGP-allekirjoitus käyttäjältä %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Siirry sivulle"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Siirry tapaukseen"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Ryhmä"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Ryhmä %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Ryhmän oikeudet"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Ryhmässä on jo jäsen"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "Ryhmää ei voitu luoda."
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Ryhmää ei voitu luoda: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Ryhmä luotu"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Ryhmässä ei ole sellaista jäsentä"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Ryhmää ei löydetty"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Ryhmää ei löydetty.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Ryhmää ei määritelty.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Ryhmät"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Ryhmät eivät voi olla jäsentensä jäseniä"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr ""
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hei!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Hei, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historia"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr ""
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Kotipuhelin"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Kotisivu"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr ""
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Numero"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identiteetti"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Jos hyväksyntä hylätään, hylkää alkuperäinen ja poista siitä riippuvat hyväksynnät"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr ""
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr ""
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr ""
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Jos olet muuttanut tietoja, muista tallentaa"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Kelpaamaton arvo %1"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Muuttumaton kenttä"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Sisällytä passiiviset kentät listaukseen"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr ""
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Sisällytä listaukseen myös passiiviset työjonot."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Sisällytä listaukseen myös passiiviset käyttäjät."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Alkuprioriteetti"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Virhe syötteessä"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Sisäinen virhe"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Sisäinen virhe: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Ryhmän tyyppi ei kelpaa"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Kelpaamaton oikeus"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "Kelpaamaton tyyppi"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Kelpaamatonta dataa"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Omistaja ei kelpaa. Asetetaan oletusasetusten mukaan 'ei kukaan'"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Kelpaamaton työjono"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Kelpaamaton oikeus"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Kelpaamaton arvo kohteelle %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Kelpaamaton arvo kentälle"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Kelpaamaton arvo tilalle"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Tammi"
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "tammikuu"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Liity tai jätä ryhmä"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Heinä"
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "Heinäkuu"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Jätti"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "kesä"
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "kesäkuu"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Avainsana"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Kieli"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Viimeinen"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Viimeinen yhteydenotto"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Viimeksi otettu yhteyttä"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Viimeksi huomautettu"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Viimeksi päivitetty"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Jäljellä"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Päästä tämä käyttäjä sisään RT:iin"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Tälle käyttäjälle voidaan antaa oikeuksia"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Rajoitetaan omistajaa %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Rajoitetaan työjonoa %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Linkki on jo olemassa"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Linkkiä ei voitu luoda"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Linkki luotu (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Linkki poistettu (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Linkkiä ei löydetty"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Linkitä tapaus #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "Linkitä tapaus #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Linkit"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Sijainti"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Lokihakemistoa %1 ei löytynyt tai kirjoittaminen ei onnistunut.\\n RT ei voi toimia."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Olet kirjautunut sisään tunnuksella %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Kirjaudu sisään"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Kirjaudu ulos"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Aseta omistaja"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Aseta tila"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Aseta erääntymisaika"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Aseta päätösaika"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Aseta aloitusaika"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Aseta alkuaika"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Aseta oltu yhteydessä -aika"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Aseta prioriteetti"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Aseta työjono"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Aseta otsikko"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr ""
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Hallinnoi ryhmiä ja jäsenyyksiä"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Hallinnoi yleisiä asetuksia, jotka vaikuttavat kaikkiin työjonoihin"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Hallinnoi työjonoja ja työjonokohtaisia asetuksia"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Hallinnoi käyttäjiä ja salasanoja"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Maalis"
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Maaliskuu"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Toukokuu"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Touko"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Jäsen lisätty"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Jäsen poistettu"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Jäsentä ei poistettu"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Jäsen:"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Jäsenet"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr ""
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Yhdistäminen onnistui"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Yhdistäminen epäonnistui. EffectiveId:n arvoa ei pystytty asettamaan"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Yhdistä"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr ""
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Viesti"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr ""
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Kännykkä"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Kännykkä"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Muokkaa käyttöoikeusluetteloa"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Muokkaa kenttää %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Muokkaa kaikkia työjonoja koskevia kenttiä"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr ""
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Muokkaa tämän työjonon toimintopohjia"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Muokkaa tämän työjonon toimintoja"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "Muokkaa järjestelmän käyttöoikeusluetteloa"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Muokkaa pohjaa %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr ""
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr ""
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Muokkaa työjonon %1 toimintoa"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Muokkaa toimintoa, jota sovelletaan kaikkiin työjonoihin"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr ""
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Muokkaa tapauksen #%1 päiviä"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Muokkaa tapauksen #%1 päiviä"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Muokkaa ryhmien yleisiä oikeuksia"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Muokkaa ryhmien yleisiä oikeuksia."
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Muokkaa yleisiä toimintoja"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Muokkaa yleisiä käyttäjien oikeuksia"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Muokkaa käyttäjien yleisiä oikeuksia."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Muokkaa ryhmän metatietoja tai poista ryhmä"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Muokkaa ryhmän %1 oikeuksia."
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Muokkaa ryhmän oikeuksia työjonossa %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Muokkaa tämän ryhmän jäsenlistaa"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Muokkaa omaa RT-tunnustasi"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Muokkaa työjonoon %1 liittyviä käyttäjiä"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Muokkaa tapaukseen %1 liittyviä käyttäjiä"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Muokkaa työjonoon %1 liittyviä toimintoja"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Muokkaa toimintoja, joita sovelletaan kaikkiin työjonoihin"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Muokkaa pohjaa %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Muokkaaa pohjia, joita sovelletaan kaikkiin työjonoihin"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Muokkaa ryhmää %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Muokkaa työjonon tarkkailjoita"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Muokkaa käyttäjää %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Muokkaa tapausta #%1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Muokkaa tapausta #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Muokkaa tapauksia"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Muokkaa ryhmän %1 käyttäjien oikeuksia"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Muokkaa työjonoon %1 liittyviä käyttäjien oikeuksia"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Muokkaa tapauksen %1 seuraajia"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr ""
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Ma"
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Lisätietoa: %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Siirrä alas"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Siirrä ylös"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Monta"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "'Nimi' täytyy määritellä"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Hyväksyntäni"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Hyväksyntäni"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Nimi"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Nimi on käytössä"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Tarvitsee järjestelmän ylläpitäjän hyväksynnän"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Ei ikinä"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Uusi"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Uusi linkki"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Uusi salasana"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Uusi haku"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Uusi ryhmä"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Uusi salasana"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Uusi salasana"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Uusi työjono"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Uusi tapaus"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Uudet oikeudet"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Uusi toiminto"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Uusi haku"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Uusi pohja"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Uutta tapausta ei löydy"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Uusi käyttäjä"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Uusi käyttäjä pyydetty"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Uusi tarkkailija"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Uusi ikkunan asetus"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Seuraava"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Seuraava sivu"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Lempinimi"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Lempinimi"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Ei kenttiä"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Ryhmää ei ole määritelty"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Työjonoa ei ole määritelty"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Käyttäjää ei löydy. Ole hyvä ja ota yhteyttä RT:n ylläpitäjään.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Ei pohjaa"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Tapausta ei määritelty. Perutaan tapaus "
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "Tapausta ei määritelty. Poistutaan tapauksen muokkauksesta\\n\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr ""
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Komentoa ei löytynyt\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Käyttäjälle ei ole annettu kommentteja"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Ei kirjeenvaihtoa liitettynä"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Ei kuvausta kohteelle %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Ryhmää ei ole määritelty"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Salasanaa ei ole asetettu"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Ei oikeutta luoda työjonoja"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Ei oikeutta luoda tapauksia työjonoon '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Ei oikeutta luoda käyttäjiä"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Ei oikeutta tarkastella tätä tapausta"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Ei oikeutta päivittää tätä tapausta"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Toimeksiantajaa ei ole määritelty"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Toimeksiantajia ei ole valittu."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Yhtään hakukriteerit täyttävää tapausta ei löytynyt."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Oikeuksia ei löytynyt"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Ei myönnettyjä oikeuksia."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Ei työstettävää hakua"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Tapauksen numeroa ei ole määritelty"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Tapahtuman tyyppiä ei ole määritelty"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "Käyttäjää tai sähköpostiosoitetta ei ole määritelty"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Yhtään hakukriteerit täyttävää käyttäjää ei löytynyt."
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Ei kelpaa RT-käyttäjäksi. RT:n cvs-käsittelijä irrottautuu. Ole hyvä ja ota yhteyttä RT:n ylläpitäjään.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr ""
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Ei kukaan"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Olematon kenttä?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Sisäänkirjautumaton"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Et ole kirjautunut järjestelmään"
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Ei asetettu"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Ei vielä toteutettu."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Ei vielä toteutettu..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Merkintöjä"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Ilmoitusta ei pystytty lähettämään"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr ""
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr ""
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr ""
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr ""
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr ""
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr ""
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr ""
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr ""
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr ""
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr ""
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr ""
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr ""
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr ""
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr ""
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr ""
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Marras"
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "marraskuu"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr ""
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr ""
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr ""
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Loka"
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "lokakuu"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr ""
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr ""
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "-"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr ""
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr ""
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr ""
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr ""
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr ""
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr ""
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr ""
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr ""
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Näytä vain pyynnöt, jotka on luotu jälkeen %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Näytä vain pyynnöt, jotka on luotu ennen %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Avoin"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Avaa"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Avoimet tapaukset"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Avoimet tapaukset (listasta) uudessa ikkunassa"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Avoimet tapaukset (listasta) toisessa ikkunassa"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Avaa tapaukset, kun esiintyy kirjeenvaihtoa"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Järjestäminen"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Laitos"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Alkuperäinen tapaus: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Ajan kuluessa prioriteetti muuttuu kohti"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Omat tapaukset"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr ""
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Omistaja"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Omistaja vaihdettu tunnuksesta %1 tunnukseen %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Omistaja pakolla vaihdettu tunnuksesta %1 tunnukseen %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "Omistaja"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Hakulaite"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Hakulaite-puhelin"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Isät"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Salasana"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Salasanan muistutus"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Salasana liian lyhyt"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Salasana: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr ""
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr ""
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Käyttäjät"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Pääsy kielletty"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Omat ryhmät"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Omat ryhmät"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Omat ryhmät:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Puhelinnumerot"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Paikanpitäjä"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Asetukset"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Asetukset"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Valmistele tumppi"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Edellinen"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Edellinen sivu"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Toimeksiantajaa %1 ei löytynyt"
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioriteetti"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Prioriteetti alkaa arvosta"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr ""
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Erioikeutettu"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Erioikeutuksen tila: &1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Erioikeutetut käyttäjät"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Valeryhmä sisäiseen käyttöön"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr ""
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Työjono"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Työjonoa %1 ei löytynyt"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Työjonoa '%1' ei löytynyt"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Työjonon nimi"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Työjonon toiminnot"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Työjono on jo olemassa"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Työjonoa ei voitu luoda"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Työjonoa ei voitu ladata."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Työjono luotu"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Työjonoa ei määritelty"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Työjonoa ei löytynyt"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Työjonot"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr ""
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr ""
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 - %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1, tekijä: <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT Ylläpito"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "RT Virhe tunnistamisessa"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT palautus: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "RT Konfiguraatiovirhe"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "RT Kriittinen virhe. Viestiä ei tallennettu!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT Virhe"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT Sai sähköpostin (%1) itseltään."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT Itsepalvelu / Suljetut tapaukset"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr ""
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT yhdellä silmäyksellä"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT Ei pystynyt tunnistamaan sinua"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT ei löytänyt tilaajaa ulkopuolisesta tietokannasta"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT ei löytänyt työjonoa: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT ei pystynyt tarkistamaan tätä PGP allekirjoitusta.\\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "%1: RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT on prosessoinut antamasi komennot"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT on tekijänoikeuslain alainen, &copy; 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Se on jakelussa seuraavalla lisenssillä: <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT luulee että tämä viesti on palautus"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT prosessoi tämän viestin kuten se olisi allekirjoittamaton."
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RT:n sähköpostiohjaustila vaatii PGP-tunnistamista. Et allekirjoittanut (PGP) viestiä tai allekirjoitustasi ei pystytty vahvistamaan."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Oikea nimi"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "Oikea nimi"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Viitattu jostakin"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Viittaus johonkin"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Päivitä"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Päivitä haku"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Päivitä tämä sivu %1 minuutin välein"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Poista kopio ylläpidolle"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Poista kopio"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Poista tilaaja"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Vastaa"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr ""
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Vastaa tapauksiin"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr ""
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Tilaaja"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Tilaajan sähköpostiosoite"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Tilaajat"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Tilaajat"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Tapaus tulisi suorittaa mennessä"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr ""
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Palauta"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Koti"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Päätä"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Ratkaise tapaus #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Päätetty"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Vastaus tilaajille"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Tulokset"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Tuloksia sivulle: "
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Vahvista salasana"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Oikeutta %1 ei löydetty %2 %3 laajuudessa %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Oikeus delegoitu"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Oikeus myönnetty"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Oikeus ladattu"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Oikeutta ei voitu perua"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Oikeutta ei löydetty"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Oikeutta ei ladattu"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Oikeus peruttu"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Oikeudet"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Oikeuksia ei voitu myöntää: %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Oikeuksia ei voitu perua: %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Roolit"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr ""
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "La"
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr ""
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Tallenna muutokset"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Tallenna muutokset"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Toiminto #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Toiminto luotu"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Toiminto poistettu"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Toiminnot"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Toiminnot työjonolle %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Toiminnot, joita sovelletaan kaikkiin työjonoihin"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Hae"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Hakukriteerit"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr ""
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr ""
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr ""
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr ""
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr ""
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Valitse ryhmä"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Valitse työjono"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr ""
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Valitse käyttäjä"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Valitse ryhmä"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Valitse useita arvoja"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Valitse yksi arvo"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Valitse työjono"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Valitse toiminto"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Valitse pohja"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Valitse käyttäjä"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr ""
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Itsepalvelu"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr ""
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr ""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr ""
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr ""
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr ""
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr ""
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr ""
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Lähettää postia omistajalle"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Syys"
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "syyskuu"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr ""
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Näytä tulokset"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr ""
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Näytä perustiedot"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr ""
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Näytä yksityiskohdat"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Näytä tapauksen kommentointi"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Näytä tapausten yhteenvedot"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr ""
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Allekirjoitus"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Yksittäinen"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr ""
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Järjestys"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Järjestä tulokset"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "Lajittelujärjestys"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Jäädytetty"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Etusivu"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Aloitettu"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Aloittamisaikaa '%1' ei pystytty tulkitsemaan"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Alkaa"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Alkaa mennessä"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Alkamisaikaa '%1' ei pystytty tulkitsemaan"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Tila"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Tila"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Tila muutettu arvosta %1 arvoon %2"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Kaappaa"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Kaapattu käyttäjältä %1"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr ""
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Otsikko"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Otsikko vaihdettu: %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Lähetä"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Onnistui"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Su"
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Järjestelmä"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Järjestelmävirhe"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Järjestelmävirhe. Oikeutta ei delegoitu."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Järjestelmävirhe. Oikeutta ei luovutettu."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Järjestelmäryhmät"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr ""
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TESTIMERKKIJONO"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Ota itselle"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Otettu"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Pohja"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Pohja #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Pohja poistettu"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Pohjaa ei löydetty"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Pohjaa ei löydetty\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Pohja tulkittu"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Pohjat"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Pohjat työjonolle %1\\n"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Ei ole arvo tälle kentälle"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Tämä on sama arvo"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Toimeksiantaja on jo %1 tälle työjonolle"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Toimeksiantaja on jo %1 tälle tapaukselle"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Toimeksiantaja ei ole %1 tälle työjonolle"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Toimeksiantaja ei ole %1 tälle tapaukselle"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Työjonoa ei ole olemassa"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Tapaus sisältää päättämättömiä riippuvuuksia"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Käyttäjällä on jo tuo oikeus"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Käyttäjä omistaa jo tämän tapauksen"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Käyttäjää ei ole olemassa"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Käyttäjä on jo erioikeutettu"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Käyttäjä on jo erioikeudeton"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Käyttäjä on nyt erioikeutettu"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Käyttäjä on nyt erioikeudeton"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Käyttäjä ei voi omistaa tapauksia tuossa työjonossa"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Ei ole numero"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Perustiedot"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Kommentti on tallennettu"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "Seuraavia komentoja ei suoritettu:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Uusi arvo asetettu."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Tapauksen omistaja"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Tapauksen tilaaja"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Nämä kommentit eivät ole yleisesti näkyvillä käyttäjälle"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Tämä tapaus %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Tämä tapahtuma ei näytä sisältävän mitään"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Käyttäjän %1 tärkeintä tapausta"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "Käyttäjän 25 tärkeintä tapausta"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "To"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Tapaus # %1 %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Tapauksen #%1 jättipäivitys: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Tapaus #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Tapaus %1 luotu työjonoon '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Tapaus %1 ladattu\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Tapaus %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr ""
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Tapauksen historia # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Tapauksen numero"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Tapauksen liitteen"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Tapauksen sisältö"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Tapauksen sisällön tyyppi"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Tapausta ei voitu luoda sisäisestä virheestä johtuen"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Tapaus luotu"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Tapauksen luonti epäonnistui"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Tapaus poistettu"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Tapauksen numeroa ei löytynyt"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Tapausta ei löydy"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Tapauksen tila vaihdettu"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Tapauksen tarkkailijat"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Tapaukset"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Tapaukset %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Aikaa jäljellä"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Aikaa käytetty"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Aikaa jäljellä"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Aika"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Aikaa käytetty"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Luodaksesi diffin tästä käskystä:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "To generate a diff of this commit:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Oltu yhteydessä"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr ""
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Tapahtuma %1 puhdistettu"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Tapahtuma luotu"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Tapahtumat ovat muuttumattomia"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Yritetään poistaa oikeus: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Ti"
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Tyyppi"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Toteuttamaton"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unix login"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Käyttäjän Unix-tunnus"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Tuntematon sisällön koodaus %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Rajoittamaton"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr ""
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Erioikeudeton"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Ottamaton"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Päivitä"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Päivitä numero"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr ""
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Päivitä tyyppi"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Päivitä kaikki nämä tapaukset kerralla"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Päivitä sähköposti"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Päivitä nimi"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Päivitystä ei tallennettu"
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Päivitä valitut tapaukset"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Päivitä allekirjoitus"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Päivitä tapaus"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Päivitä tapaus # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Päivitä tapaus #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Päivitä tapaus #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Päivityksen tyyppi ei ollut kirjeenvaihto eikä kommentti."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Päivitetty"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr ""
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr ""
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Käyttäjä %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Käyttäjä %1 Salasana: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Käyttäjää '%1' ei löydetty"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Käyttäjää '%1' ei löydetty\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "Käyttäjätunnus"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Käyttäjätunnus"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Käyttäjän oikeudet"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Käyttäjää ei voitu luoda: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Käyttäjä luotu"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Käyttäjän luomat ryhmät"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Käyttäjää informoitu"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Käyttäjän näkymä"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Käyttäjätunnus"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Käyttäjät"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Hakua vastaavat käyttäjät"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "Tapauksen arvo"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Arvot"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Tarkkailijat"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "Web-koodaus"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Ke"
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr ""
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr ""
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr ""
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr ""
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr ""
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr ""
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr ""
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr ""
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr ""
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr ""
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Työ"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Työpuhelin"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Tehty"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Omistat jo tämän tapauksen"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Et ole valtuutettu käyttäjä"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Voit palauttaa vain tapauksia, jotka omistat itse tai jotka ovat ilman omistajaa"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "Sinulla ei ole valtuuksia tarkastella tätä tapausta.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Löysit %1 tapausta työjonosta %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Olet kirjautunut ulos RT:stä"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Sinulla ei ole valtuuksia luoda tapauksia tähän työjonoon."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Et voi luoda tapauksia tuohon työjonoon."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Tervetuloa kirjautumaan järjestelmään uudelleen"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Sinun %1 tapaustasi"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "RT:n ylläpitäjä on konfiguroinut RT:n käynnisävät sähköpostialiakset väärin."
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr ""
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr ""
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr ""
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Käyttäjätunnuksesi tai salasanasi on väärä"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Postinumero"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "sallittu käyttäjälle %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr ""
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "sisältää"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "sisältö"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "sisällön tyyppi"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "kirjeenvaihtoa (luultavasti) ei ole lähetetty"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "kirjeenvaihto lähetetty"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "päivää"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "poista"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "poistettu"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "ei sisällä"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "ei sisällä"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "on yhtäsuuri"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr ""
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "tiedostonimi"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "on suurempi kuin"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "ryhmä %1"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "tuntia"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "numero"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "on"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "ei ole"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "on pienempi kuin"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "sisältää"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minuuttia"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "muokkaukset\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "kuukausia"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "uusi"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr ""
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "ei mitään"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "on erisuuri kuin"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "avoin"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "oma ryhmä '%1' käyttäjälle '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "työjono %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "hylätty"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "päätetty"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sec"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "jäädytetty"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "järjestelmä %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "järjestelmäryhmä '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "kutsuva komponentti ei eritellyt syytä"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "tapaus #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "tosi"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "kuvailematon ryhmä %1"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "kuvailematon ryhmä %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "käyttäjä %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "viikkoa"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "pohjalla %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "vuosia"
+
diff --git a/rt/lib/RT/I18N/fr.po b/rt/lib/RT/I18N/fr.po
new file mode 100644
index 0000000..c3b8caa
--- /dev/null
+++ b/rt/lib/RT/I18N/fr.po
@@ -0,0 +1,5117 @@
+# translation of fr.po to
+# Copyright (c) 2002 Jesse Vincent <jesse@bestpractical.com>
+# valid as of 3.5-TESTING r3738
+# jfenal <jfenal@gmail.com>, 2005.
+# jfenal <jfenal@free.fr>, 2005.
+# Emmanuel Lacour <elacour@easter-eggs.com>, 2007.
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2002-05-02 11:36+0800\n"
+"PO-Revision-Date: 2005-10-28 02:19+0200\n"
+"Last-Translator: Emmanuel Lacour <elacour@easter-eggs.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.10.2\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr " %1 effacé."
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr " %1 renommé en %2."
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr " %1 sauvé."
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "n°"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "n°%1 : %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr "$1"
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr "$prefix %1"
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 n°%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 ajouté"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "il y a %1 %2"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 changé en %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 supprimé"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 avec le modèle %3"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) par %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (inchangé)"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Un paramètre à passer à %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Ecrit les mises à jour de statuts sur STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr "%1 - Précisez l'identifiant du modèle que vous souhaitez utiliser"
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr "%1 - Précisez si vous souhaitez utiliser la première ou la dernière transaction"
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Précisez l'action que vous voulez utiliser"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Précisez la condition que vous voulez utiliser"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Précisez la recherche que vous voulez utiliser"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr "%1 - Précisez le type de transaction que vous souhaitez utiliser"
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 Copyright 1996-%3 %4."
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScripAction %1 chargée"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 ajouté(e) comme valeur de %2"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 semble être un objet local, mais est introuvable dans la base de données"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 par %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 changé(e) de %2 à %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "copie de %1"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 n'a pas pu être positionné à %2."
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 ne peut pas mettre le statut à résolu. La base de données RT est peut être incohérente."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 créé(e)"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 supprimé(e)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 grouped by %2"
+msgstr "%1 groupés par %2"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "Mes %1 tickets de plus haute priorité"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Status changé de %1 à %2"
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 est un outil agissant sur les tickets depuis un ordonnanceur externe tel que cron."
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 n'est plus un %2 pour cette file."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "Les %1 derniers tickets sans propriétaire"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 objets"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "droits de %1"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 résoudra tous les membres d'un ticket groupé résolu."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1 de %2 objets"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1 de %2 de %3 objets"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "Recherches sauvées de %1"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1 : pas d'attachement spécifié"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1o"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' est un statut invalide"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Cocher la case pour supprimer)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Cocher les cases pour désactiver les notifications aux destinataires listés)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Cocher les cases pour activer les notifications aux destinataires listés)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Entrer le numéro du ticket ou son URL, séparés par des espaces)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Si à laissé blanc, le défaut sera %1)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Pas de champ personnalisé)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Aucun membre)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Aucun Scrip)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Aucun modèle)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Aucun)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Transmettre une copie cachée de cette mise à jour à une liste d'adresse mail séparée par des virgules. Cela ne modifie <strong>pas</strong> ceux qui recevront les mises à jour futures.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Transmettre une copie de cette mise à jour à une liste d'adresses mail séparées par des virgules. Ces personnes <strong>recevront</strong> les mises à jour suivantes.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Transmettre une copie de cette mise à jour à une liste d'adresses mail séparées par des virgules. Ceci <b>ne changera pas</b> les destinataires des mises à jour suivantes.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Transmettre une copie de cette mise à jour à une liste d'adresses mail séparées par des virgules. Ces personnes <strong>recevront</strong> les mises à jour suivantes.)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Utilisez ces champs lorsque vous sélectionnez « Défini par l'utilisateur » pour une condition ou une action)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(Ne sera pas envoyé par email)"
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(vide)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(aucun nom)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(sans information)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(aucune valeur)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(un seul ticket)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(en attente d'approbation)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(en attente d'autres Collection)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(exigé)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(sans titre)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(yyyy/mm/dd)"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr "-"
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr "l'argument pour --transaction ne peut être que 'first' ou 'last'"
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Statut%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$field%>"
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Créer un ticket dans\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Créer un ticket dans\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Un modèle vide"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Pas de mot de passe spécifié, l'utilisateur ne pourra donc pas se connecter."
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE non trouvé"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "Les ACE peuvent seulement être créés et effacés."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "ET"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "A propos"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "contrôle d'accès"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Action"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Action %1 non trouvée"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Action validée.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr "L'argument 'Action' est obligatoire"
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Action préparée..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Ajouter"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Ajouter AdminCC"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Ajouter CC"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Ajouter des colonnes"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Ajouter un critère"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Ajouter d'autres fichiers"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Ajouter Demandeur"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Ajouter une valeur"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Ajouter un scrip qui s'applique à toutes les files"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Ajouter et rechercher"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Ajouter des commentaires ou des réponses aux tickets sélectionnés"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Ajouter des membres"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Ajouter de nouveaux observateurs"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Ajouter ces termes à la recherche"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "Ajouter les valeurs"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Ajouter, retirer et modifier des champs personnalisés pour les objets"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Ajout groupe/utilisateur comme %1 pour cette file"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Ajout groupe/utilisateur comme %1 pour ce ticket"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adresse1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adresse2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Commentaire Admin"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Correspondance Admin "
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Administrateurs de files"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Configuration globale / administration"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "GérerChampPersonnalisé"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "GérerGroupes"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "GérerAppartenanceGroupes"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "GérerGroupesPersonnels"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "GérerGroupesPersonnelsPropres"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "GérerFile"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "GérerUtilisateurs"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Cc Administratif"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Avancé"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Après"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Aggrégateur"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Toutes les approbations obtenues"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Toutes les files"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "Et/Ou"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "S'applique à"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Appliquer"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Appliquer vos changements"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Approbations"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Approbation #%1 : %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Approbation #%1 : Notes non enregistrées à cause d'une erreur système"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Approbation #%1 : Notes enregistrées"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Approbations obtenues"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Approbations refusées"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Approuver"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Notes de l'approbateur : %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Avr."
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "Asc"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Croissant"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Fixer et supprimer les champs personnalisés"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "FixerChampsPersonnalisés"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Attaché"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Attacher un fichier"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Fichier attaché"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Attachement '%1' ne peut pas être chargé"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Attachement créé"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Nom de fichier de l'attachement"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Attachements"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Attibut supprimé"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Aoû."
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "RéponseAuto"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Réponse automatique aux demandeurs"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "Disponible"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Essentiel"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Assurez-vous de sauvegarder vos modifications"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Avant"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Logo d'entreprise de Best Practical Solutions, LLC"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Vide"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Gras"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Lien prédéfini"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "En-têtes courts"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "Màj en masse"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Les utilisateurs système ne peuvent être modifiés"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Le groupe/utilisateur peut-il voir cette file"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Impossible d'ajouter une valeur de champ personnalisé sans un nom"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "Impossible de trouver une collection pour la classe '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Impossible de trouver une recherche sauvée et de l'utiliser"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Un ticket ne peut être lié à lui même"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr "Impossible de sauvegarder %1"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Impossible de sauver cette recherche"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Impossible de spécifier à la fois la base et la cible"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Impossible de créer l'utilisateur : %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "Catégorie"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Changer le mot de passe"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Tout cocher"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Cocher la case pour supprimer"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Cocher la case pour retirer le droit"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Fils"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Choisissez une date"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Ville"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Tout effacer"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "Fermer la fenêtre"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Fermé"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Tickets fermés"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Boîte combo : Sélectionnez ou saisissez plusieurs valeurs"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Boîte combo : Sélectionnez ou saisissez une valeur"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Boîte combo : Sélectionner ou saisir au plus %1 valeurs"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Commenter"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Adresse de commentaire"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Commentaire sur le ticket"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "CommenterTicket"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Commentaires (non envoyés aux demandeurs)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Commentaires (non envoyés aux demandeurs)"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Commentaires sur cet utilisateur"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Commentaires ajoutés"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "tr(Commit Stubbed)"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Condition"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "L'argument condition est obligatoire"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "La condition satisfait..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Condition non trouvée"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Configuration"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Confirmer"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Contenu"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Type de contenu"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Copier"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Courrier"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Courrier ajouté"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Impossible d'ajouter une nouvelle valeur de champ personnalisé. "
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Impossible d'ajouter une nouvelle valeur de champ personnalisé. %1"
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Impossible de changer l'intervenant. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Impossible de créer le champ personnalisé"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Impossible de créer le champ personnalisé : %1"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Impossible de créer un groupe"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Impossible de créer le modèle : %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Impossible de créer un ticket. File non indiquée"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Impossible de créer l'utilisateur"
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Impossible de trouver ou créer cet utilisateur"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Impossible de trouver ce groupe ou utilisateur"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Impossible de charger le champ personnalisé %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Impossible de charger ce groupe"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "Impossible de charger l'objet pour %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Impossible de charger l'attribut de recherche"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Impossible de faire de ce groupe/utilisateur un %1 pour cette file"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Impossible de faire de ce groupe/utilisateur un %1 pour ce ticket"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Impossible de supprimer ce groupe/utilisateur comme un %1 pour cette file"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Impossible de sauver les informations utilisateur"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "Impossible d'ajouter la pièce jointe"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Impossible d'ajouter un membre à ce groupe"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Impossible de créer une transaction : %1"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Colonne introuvable"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Groupe/utilisateur introuvable"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Valeur introuvable"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Impossible de charger %1 depuis la base des utilisateurs.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "Impossible de charger la classe %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Impossible de charger le champ personnalisé %1"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr "Impossible de charger une copie du ticket #%1."
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Impossible de charger le groupe %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Impossible de charger le lien"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Impossible de charger l'objet %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Impossible de charger la file"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Impossible de charger la file %1"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr "Impossible de charger le scrip #%1"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Impossible de charger le ticket '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr "Impossible de convertir '%1' en URI."
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't resolve base '%1' into a URI."
+msgstr "Impossible de résoudre la base '%1' dans une URI."
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't resolve target '%1' into a URI."
+msgstr "Impossible de résoudre la cible '%1' dans une URI."
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Pays"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Ajouter"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Ajouter des tickets"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Ajouter un Champ Personnalisé"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Ajouter un champ personnalisé pour la file %1"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Ajouter un nouveau groupe"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Ajouter un nouveau groupe personnel"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Ajouter un nouveau ticket"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Ajouter un nouvel utilisateur"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Ajouter une file"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Ajouter un scrip pour la file %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Ajouter un modèle"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Ajouter un ticket"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Ajouter de nouveaux tickets basés sur le modèle de ce scrip"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Ajouter un ticket"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Ajouter des tickets dans cette file"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Ajouter, supprimer et modifier des champs personnalisés"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Ajouter, supprimer et modifier les files"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Ajouter, supprimer et modifier les membres d'un groupe personnel"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Ajouter, supprimer et modifier les utilisateurs"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "CréerRechercheSauvée"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "CréerTicket"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Créé"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Champ Personnalisé %1 créé"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Créé dans une période"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "Tickets créés dans une période, groupés par l'état"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Créateur"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Liens courants"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Scrips actuels"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Membres actuels"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Droits actuels"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Recherche courante"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Observateurs actuels"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Champs personnalisés"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Champs personnalisés pour %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Programme de nettoyage d'action personnalisé"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Programme de préparation d'action personnalisé "
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Condition personnalisée"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Le champ personnalisé %1 a une valeur."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Le champ personnalisé %1 n'a pas de valeur."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Le champ personnalisé %1 est introuvable"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr "Champs personnalisé '%1'"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "La valeur du champ personnalisé %1 ne peut pas être trouvée pour le champ personnalisé %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "La valeur du champ personnalisé ne peut pas être effacée"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "La valeur du champ personnalisé ne peut par être trouvée"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "La valeur du champ personnalisé est effacée"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "ChampPersonnalisé"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "Personnaliser"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Dates"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Déc."
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Modèle de réponse automatique par défaut"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "File par défaut"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Demandeur par défaut"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Modèle de commentaire administrateur par défaut"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Modèle de courrier administrateur par défaut"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Modèle de courrier par défaut"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Modèle de transaction par défaut"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Déléguer les droits"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Déléguer des droits spécifiques qui vous ont été accordés."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "DéléguerDroits"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Délégation"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Supprimer"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Détruire le modèle"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Échec de la destruction : %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Supprimer les scrips sélectionnés"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Supprimer les tickets"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr "Supprimer les valeurs"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "SupprimerTicket"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Recherche détruite"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Effacer cet objet briserait l'intégrité référentielle"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Effacer cet objet violerait l'intégrité référentielle"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Refuser"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "En dépend"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Ajout de la dépendance par %1"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Suppression de la dépendance par %1"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Ajout de la dépendance de %1"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Suppression de la dépendance de %1"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Dépend de"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "Desc"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Décroissant"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Décrivez la situation ci-dessous"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Description"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Afficher"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Afficher la liste des droits"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Afficher les colonnes"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Afficher les modèles de Scrips pour cette file"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Afficher les Scrips pour cette file"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Mode d'affichage"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Afficher les recherches sauvegardées de ce groupe"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Distribué selon les termes de la version 2 de la <a href=\"http://www.gnu.org/copyleft/gpl.html\">licence GNU GPL.</a>"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Faire tout et n'importe quoi"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Ne pas rafraîchir cette page."
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Télécharger"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Télécharger en tant que fichier délimité par tabulations"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Echéance"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Modifier"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "Modifier les champs personnalisés"
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Modifier les champs personnalisés pour %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Modifier les champs personnalisés pour tous les groupes"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Modifier les champs personnalisés pour tous les utilisateurs"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Modifier les champs personnalisés pour les tickets de toutes les files"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Modifier les liens"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Modifier la requête"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Mod. recherche"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Modifier les modèles pour la file %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Modifier les recherches sauvées du groupe"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Modifier les modèles système"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "ModifierRecherchesSaugardées"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Modifier la configuration de la file %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Modifier le champ personnalisé %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Modifier les membres du groupe %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Modifier les membres du groupe personnel %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "La base ou la cible doivent être spécifiées"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Email"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Adresse email utilisée"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Activé (Décocher cette case désactive ce champ personnalisé)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Activé (Décocher cette case désactive ce groupe)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Activé (Décocher cette case désactive cette file)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Files actives"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "État %1 activé"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "État actif : %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Saisir plusieurs valeurs"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Saisir les objets ou les URI pour y lier les objets. Séparer les saisies par des espaces."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Saisir une seule valeur"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Saisir des noms de files ou des URI pour lier aux files. Séparer les saisies avec des espaces."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Saisir des n° de tickets ou des URI pour lier aux tickets. Séparer les saisies par des espaces."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Saisir au plus %1 valeurs"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Erreur"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Erreur de paramètres pour Queue->AddWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Erreur dans les paramètre de Queue->DeleteWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Erreur de paramètres pour Ticket->AddWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Erreur dans les paramètres de Ticket->DeleteWatcher"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Échelonner les tickets"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Estimé"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Tout le monde"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Exemple : "
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Info supplémentaire"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Impossible de créer l'attribut de recherche"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Recherche du pseudo groupe d'utilisateurs 'Priviligiés' infructueuse."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Recherche du pseudo groupe d'utilisateurs 'non-privilégiés' infructueuse"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Impossible de charger le module %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "Impossible de charger l'objet pour %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Fév."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "NomFichier"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Saisir dans plusieurs champs de type texte"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Saisir plusieurs champs de type wiki"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Saisir dans un champ de type texte"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Saisir un champ de type wiki"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "Saisir une URL."
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Saisir au plus %1 champs de type texte"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Saisir au plus %1 champs de type wiki"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Priorité finale"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "PrioritéFinale"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Trouver les groupes dont"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Trouver les gens dont"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Rechercher des tickets"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Premier"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo ! "
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Forcer la modification"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Format"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Trouvé %quant(%1,ticket)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Objet trouvé"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Ven."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "En-têtes complets"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Obtenir le modèle à partir d'un fichier"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Donné à %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Champs personnalisés globaux"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Configuration globale des champs personnalisés"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr "Portlet global %1 sauvé."
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Modèle global :  %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Lancer"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Go !"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Aller au ticket"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Groupe"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Droits de groupe"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Le groupe a déjà un membre"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Le groupe %1 ne peut être créé"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Groupe ajouté"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Un tel membre n'appartient pas au groupe"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Groupe introuvable"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Groupes"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Les groupes ne peuvent pas être membres de leurs membres"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Groupes correspondant au critère de recherche"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Cette utilisateur appartient aux groupes"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Bonjour !"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Bonjour, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historique"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "Historique du groupe %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "Historique de l'utilisateur %1"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Accueil"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "Heuers"
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "J'ai %quant(%1, toupie à béton)."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Je suis perdu"
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Identifiant"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identité"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Si une approbation est refusée, rejette l'original et supprime les approbations en attente"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "SI aucun demandeur n'est spécifié, créer les tickets pour ce demandeur."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Si aucune file n'est spécifée, créer les tickets dans cette file."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Si cet outil était setgid, un utilisateur local mal intentionné pourrait l'utiliser pour obtenir un access administrateur à RT."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Si vous avez fait une modification, assurez vous de"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Valeur incorrecte pour %1"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Champ non modifiable"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Inclure les groupes inactifs dans le listage."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Afficher les files inactives."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Inclure les utilisateurs désactivés dans le résultat."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Requête incomplète"
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Requête incomplète"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Requête incomplète"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Priorité initiale"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "PrioritéInitiale"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Erreur à l'entrée"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr "La valeur saisie doit correspondre à %1"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Erreur interne"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Erreur interne : %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Type de groupe invalide"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Droit invalide"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Données invalides"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "Motif invalide : %1"
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "File invalide"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Droit invalide"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "File invalide pour %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Valeur incorrecte pour le champ personnalisé"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Valeur de statut invalide"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Il est extrêmement important que les utilisateurs non autorisés n'aient pas accès à cet outil."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Il est suggéré de créer un utilisateur Unix non privilégié appartenant au bon groupe et ayant accès à RT pour utiliser cet outil."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Il faut plusieurs paramètres : "
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Italique"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Rejoignez ou quittez ce groupe"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul."
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Tout"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun."
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Langue"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Grande"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Dernier"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Dernier contact"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Date dernier contact"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Date dernière MAJ"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "DernièreMAJPar"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Restant"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Donner accès à RT à cet utilisateur"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Autoriser cet utilisateur à recevoir des droits"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Relation"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Le lien existe déja"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Le lien ne peut être ajouté"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Le lien est ajouté (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Le lien est effacé (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Lien introuvable"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Lier le ticket n°%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "Lier les valeurs à"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr "Relation. autorisation refusée"
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Relations"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Charger"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Charger les recherches sauvées :"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "ChargerRechercheSauvée"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Modules perl chargés"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Recherche %1 chargée"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Localisation"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Le répertoire de journalisation %1 est introuvable ou inaccessible en écriture.\\n RT ne peut être lancé."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Connecté en tant que %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Connexion"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Déconnexion"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Faire attention aux types différents"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Attribuer"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Appliquer Statut"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Appliquer date d'échéance"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Appliquer date de résolution"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Appliquer date de début"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Appliquer date d'ouverture"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Appliquer Age"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Appliquer priorité"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Appliquer file"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Changer le sujet"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Malkovich Malkovich Malkovich Malkovich"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Gérer les champs personnalisés et leurs valeurs"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Gérer les groupes et leurs membres"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Gérer les propriétés et configurations générales des files"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Gérer les files et leurs propriétés individuelles"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Gérer les utilisateurs et mots de passe"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Mai."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Membre %1 ajouté"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Membre %1 supprimé"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Membre ajouté"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Membre supprimé"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Membre non supprimé"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Membre de"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Membres"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "Appartenance à %1 ajoutée"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "Appartenance à %1 supprimée"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Affiliations"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "Affiliations de l'utilisateur %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Fusion réussie"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Échec de fusion. Ne peut appliquer EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Échec de fusion. Ne peut appliquer Status"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Fusionner dans"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Fusionné avec %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Message"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "Corps du message non affiché car il est trop important ou n'est pas au format texte."
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Le message ne peut être sauvegardé"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Message sauvegardé"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Les messages relatifs à ce ticket ne seront pas envoyés à..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Minutes"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "Parenthèses non correspondantes"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Clé primaire manquante ? : %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Mobile"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Modifier la liste de droits"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Modifier les champs personnalisés s'appliquant à %1 pour tous les %2"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Modifier les champs personnalisés s'appliquant à tous les %1"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Modifier les droits du groupe"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Modifier les membres"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Modifier les droits"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Modifier les modèles de Scrips pour cette file"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Modifier les Scrips pour cette file"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Modifier les droits des usagers"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Modifier un champ personnalisé pour la file %1"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Modifier le scrip pour la file %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Modifier le scrip qui s'applique à toutes les files"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Modifier les objets associés à %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Modifier les dates pour n°%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Modifier les dates pour le ticket n°%1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Modifier globalement les champs personnalisés"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Modifier les droits de groupe globaux"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Modifier les droits de groupe globaux."
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Modifier les droits utilisateurs globaux"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Modifier les droits utilisateurs globaux."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Modifier les métadonnées ou supprimer le groupe"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Modifier les droits de groupe pour le champ personnalisé %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Modifier les droits de groupes du groupe %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Modifier les droits de groupe pour la file %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Modifier la liste des membres pour ce groupe"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Modifier son propre profil RT"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Modifier les utilisateurs de la file %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Modifier les utilisateurs du ticket n°%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Modifier les scrips de la file %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Modifier les scrips s'appliquant à toutes les files"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Modifier le modèle %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Modifier les modèles globaux"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "Modifier la vue \"RT en un coup d'œil\" par défaut"
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Modifier le groupe %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Modifier les observateurs de la file"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Modifier l'utilisateur %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Modifier le ticket n°%1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Modifier le ticket n°%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Modifier les tickets"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Modifier les droits utilisateurs pour le champ personnalisé %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Modifier les droits utilisateurs pour le groupe %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Modifier les droits utilisateurs pour la file %1"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ModifierACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "ModifierChampPersonnalisé"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "ModifierPropresAppartenances"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "ModifierObservateurs"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ModifierScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "ModifierDonnéesPerso"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "ModifierModèle"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "ModifierTicket"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Lun."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Plus d'informations sur %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Aller en bas"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Aller en haut"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Multiple"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Attribut 'Nom' obligatoire"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "Mes %1 tickets"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "Ma journée"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Mes approbations"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Mes recherches"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Nom"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Nom utilisé"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Jamais"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nouveaux liens"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nouveau mot de passe"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nouvelles approbations en attente"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Nouv. recherche"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nouveau champ personnalisé"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Nouveau groupe"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nouveau mot de passe"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Notification de nouveau mot de passe envoyée"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Nouvelle file"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Nouveau rappel :"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nouveaux droits"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Nouveau scrip"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Nouveau modèle"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Nouveau ticket"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Nouveau ticket inconnu"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Nouvel utilisateur"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Nouvel utilisateur appelé"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nouveaux observateurs"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Suivant"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Page suivante"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Surnom"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Aucune classe définie"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Aucun champ personnalisé"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Aucun champ personnalisé défini"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Aucun groupe défini"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Aucune requête"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Aucune file définie"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Pas d'utilisateur RT trouvé. Merci de consulter votre administrateur RT.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Pas de modèle"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Pas d'action"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Aucune colonne spécifiée"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Pas de commentaires concernant cet utilisateur"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Aucune description disponible pour %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Aucun groupe spécifié"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Aucun groupe trouvé d'après le critère de recherche."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Aucun messages attachés"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Pas de mot de passe configuré"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Permission refusée pour la création de file"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Vous n'êtes pas autorisé à créer un ticket dans cette file '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Permission refusée pour la création d'utilisateurs"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Pas de permission pour afficher ce ticket"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "Pas de permission pour sauvegarder des recherches systèmes"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Pas de permission pour afficher le ticket mis à jour"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Aucun groupe/utilisateur spécifié"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Aucun groupe/utilisateur sélectionné."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Pas de file correspondant aux critères de recherche."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Aucun droit trouvé"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Aucun droit accordé."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Pas de recherche chargée"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Pas de critère de recherche."
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Pas de sujet"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Aucun type de transaction spécifié"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Aucun utilisateur ne correspond aux critères de recherche."
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Pas de valeur à positionner  \\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Personne"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Champ inexistant ? "
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr "Non renseigné"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Non connecté."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Non renseigné"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Fonction pas encore disponible."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Notes"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Impossible d'envoyer la notification"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Avertir les AdminCCs"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Avertir les AdminCCs par un commentaire"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Aviser par cc"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Aviser par cc pour commentaires"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Avertir les autres destinataires"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Avertir les autres destinataires par un commentaire"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Avertir l'intervenant"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Avertir l'intervenant par un commentaire"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Avertir l'Intervenant du rejet de son ticket"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Avertir l'Intervenant de l'approbation de son ticket par tous les approbateurs"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Avertir l'Intervenant de l'approbation de son ticket par un des approbateurs"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Avertir les intervenants et les AdminCCs de nouveaux éléments attendant leur approbation"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Avertir les demandeurs"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Avertir les demandeurs et les Ccs"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Avertir les demandeurs et les CC par un commentaire"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Avertir les demandeurs, CCs et AdminCCs"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Avertir les demandeurs, CCs et AdminCCs par un commentaire"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "OU"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "L'objet n'a pas pu être ajouté"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Impossible d'effacer l'objet"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objet ajouté"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objet effacé"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Les objets de type %1 ne peuvent avoir de champs personnalisés"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Le type d'objet ne correspond pas"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Oct."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Déconnecté"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Édition hors ligne"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Téléchargement hors ligne"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Le"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr "Le %1, %2 a écrit :"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Lors d'un commentaire"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Lors d'un courrier"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Lors d'une création"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Lors d'un changement d'intervenant"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Lors d'un changement de priorité"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Lors d'un changement de file"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Lors de la résolution/clôture"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Lors d'un changement de statut"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Lors d'une transaction"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Ne montrer que les approbations pour les demandes créées après %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Ne montrer que les approbations pour les demandes créées avant %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Ne montrer que les champs personnalisés pour :"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "Tickets ouverts"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Ouvrir"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Tickets ouverts"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Ouvrir les tickets lors d'une correspondance"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Options"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Trier par"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organisation"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Ticket source : n°%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Mail sortant suite à l'enregistrement d'un commentaire"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Mail sortant enregistré"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Temps dépassé, priorité augmentée"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Tickets propres"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "PrendreTicket"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Intervenant"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Le propriétaire ne peut être sauvé."
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Intervenant forcé de %1 à %2"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Page %1 sur %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Bipeur"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Parents"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Mot de passe"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Pense-bête pour votre mot de passe"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Mot de passe changé"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "Le mot de passe doit comporter au moins %1 caractères"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Mot de passe défini"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Mot de passe : %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Mot de passe : non autorisé"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Les mots de passe sont différents."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Les mots de passe sont différents. Votre mot de passe n'a pas été modifié"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Personnes"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Réaliser une action définie par l'utilisateur"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Configuration de Perl"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Accès refusé"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "Accès refusé"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "Accès refusé"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Groupes personnels"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Groupes personnels"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Groupes personnels :"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Numéros de téléphone"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Préférences"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr "Préférence %1 pour l'utilisateur %2 ."
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr "Préférences sauvées pour %1."
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Préparation interrompue"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Précédent"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Page précédente"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Personne/groupe %1 non trouvé(e)."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Priorité"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "La priorité débute à "
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Confidentialité :"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilégié"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Statut privilégiés :  %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Utilisateurs privilégiés"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudo groupe pour usage interne"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Constructeur de requête"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "Requête:"
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "File"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "File %1 non trouvée"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Nom de la file"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "File déjà créée"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Impossible de créer la file"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "File ne pouvant être chargée."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "File créée"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "File inconnue"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Files"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Files I administrer"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Les files pour lesquelles je suis observateur AdminCc"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Recherche rapide"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Création rapide de ticket"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 pour %2"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Administration RT"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "Erreur RT"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "Variables de RT"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT en un coup d'Å“il"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr "RT en un coup d'Å“il pour l'utilisateur %1"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT peut inclure le contenu d'un autre service web losque ce champs personnalisé est affiché."
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT peu convertir les valeurs de ce champs personnalisé en liens vers un autre service."
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT ne peut enregistrer votre session."
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT pour %1"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT va rechercher dans tout ce que vous pouvez saisir d'autre dans les sujets des tickets."
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT remplacera respectivement <tt>__id__</tt> et <tt>__CustomField__</tt> avec l'identifiant et la valeur du champs personnalisé"
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Nom"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "Ajout d'une référence par %1"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "Suppression de la référence par %1"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "Ajout d'une référence à %1"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "Suppression d'une référence à %1"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Mentionné par"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Se rapporte à"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Rafraîchir cette page toutes les %1 minute(s)."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "Rappel '%1' ajouté"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "Rappel '%1' terminé"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "Rappel '%1' réouvert"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "Ticket de rappel n°%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "Rappels"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "Rappels pour le ticket n°%1"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Enlever AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Enlever Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Enlever Demandeur"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Répondre"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Adresse de réponse"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Répondre aux demandeurs"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Répondre aux tickets"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "RépondreTicket"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Rapports"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Demandeur"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Demandeurs"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Le demande doit être résolue dans"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Le paramètre requis '%1' n'est pas spécifié"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Remise à zéro"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr "Réinitialiser avec les valeurs par défaut"
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Domicile"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Résoudre"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Résoudre le ticket n°%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Résolu"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Résolus par intervenant"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Résolus dans la période de temps"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Tickets résolus dans la période, groupés par intervenant"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Tickets résolus, groupés par intervenant"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Résultats"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Saisissez à nouveau votre mot de passe"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Annuler"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Droit délégué"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Droit accordé"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Droit activé"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Droit irrévocable"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Droit inconnu"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Droit non activé."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Droit révoqué"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Droits"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Les droits n'ont pas pu être attribués à %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Les droits n'ont pas pu être révoqués pour %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Rôles"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Lignes par boîte"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Lignes par pages"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sam."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Sauver"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Enregistrer les modifications"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Sauvegarder les préférences"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Enregistrer les modifications"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "Recherche %1 sauvée"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Recherches sauvées"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip n°%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip ajouté"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Champs de scrip"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip supprimé"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrips s'appliquant à toutes les files"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Rechercher"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Préférences de recherche"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Impossible de charger les attributs de recherche"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Rechercher des approbations"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "Rechercher des tickets"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "Rechercher des tickets. Saisissez un numéro d'<strong>identifiant</strong>, un <strong>nom de file</strong>, les intervenants par <strong>nom</strong> et les demandeurs par <strong>adresse mail</strong>. RT recherchera tout ce qui peut se trouver dans le corps des tickets et les attachements."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Options de recherche"
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "Résultats groupés par %1"
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Mise à jour de la recherche %1"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "La recherche en texte intégral (<em>full text</em>) sur chacun des tickets peut prendre un long moment, mais si vous le nécessitez, vous pouvez rechercher n'importe quel mot dans les historiques de tickets en tapant <b>fulltext:<i>mot</i></b>."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Sécurité : "
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "Voir également:"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Voir les champs personnalisés"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Voir les mails sortant et leurs destinataires"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Voir les commentaires privés du ticket"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Vue récapitulative des tickets"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "VoirChampPersonnalisé"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "VoirGroupe"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "VoirFile"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Choisissez un champ personnalisé"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Choisir un groupe"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Choisir une file"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Choisir une file pour votre nouveau ticket"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Choisir un utilisateur"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Choisir le champ personnalisé"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Choisir des champs personnalisés pour tous les groupes d'utilisateurs"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Choisir des champs personnalisés pour tous les utilisateurs"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Choisir des champs personnalisés pour toutes les files"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Choisir des champs personnalisés pour les opération sur les tickets de toutes les files"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Choisir le groupe"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Choisir plusieurs valeurs"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Choisir une valeur"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Choisir la file"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Sélectionnez les files à afficher dans la page \"RT en un coup d'œil\" page"
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Choisir le scrip"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Choisir le modèle"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Choisir au plus %1 valeurs"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Choisir l'utilisateur"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Champs personnalisés sélectionnés"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Objets sélectionnés"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Sélections modifiées. Merci de sauver vos modifications"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Envoyer un courrier à tous les observateurs"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Envoyer un mail de commentaire à tous les observateurs"
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Envoyer un courrier aux demandeurs et aux CCs"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Envoyer un courrier aux demandeurs et aux CCs en tant que commentaire"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Envoyer un courrier aux demandeurs"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Envoyer un courrier aux CCs et Bccs explicitement indiqués"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Envoyer un courriel en cc"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Envoyer un courriel en cc pour commentaires"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Envoyer un mail aux AdminCCs"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Envoyer un mail aux AdminCCs en tant que commentaire"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Envoyer un courrier à l'intervenant"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Afficher"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Afficher les approbations"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Afficher les colonnes"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Aff. résultats"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Afficher les requêtes approuvées"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Affichage court"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Afficher les requêtes refusées"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Affichage long"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Afficher les requêtes en attente"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Afficher les requêtes attendant d'autres approbations"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "AfficherACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "VoirOngletConfiguration"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "AfficherEmailSortant"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "AfficherRecherchesSauvées"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "AfficherScrips"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "AfficherModèle"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "AfficherTicket"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "AfficherCommentairesTickets"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "S'identifier en tant que demandeur ou CC de file ou de ticket"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "S'identifier en tant qu'AdminCC de ticket ou de file"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Signature"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Recherche rapide"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Unique"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Taille"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Passer le menu"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Petite"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "Certains navigateurs peuvent restreindre le chargement du contenu au domaine du serveur RT."
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Trier"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "Étape"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Ouvert le"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Débute"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Etat"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Statut"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Changement de statut"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Voler"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Voler les tickets "
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "VolerTicket"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Volé à %1"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Style"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Sujet"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Sujet modifié en %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Valider"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Réussi"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Dim."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "SuperUtilisateur"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Système"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Configuration système"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Erreur système"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "Erreur système :  %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Outils système"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Erreur système. Droit non délégué."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Erreur système. Droit non accordé."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Groupes systèmes"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRolegroup à usage interne"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "Chaîne_de_test"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Prendre"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Prendre les tickets"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "PrendreTicket"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Pris"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Modèle"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Modèle n°%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Modèle supprimé"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr "Modèle est un argument obligatoire"
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Modèle inconnu"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Modèle analysé"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "Erreur d'analyse du modèle"
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Modèles"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Ceci est déjà la valeur actuelle"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Valeur incorrecte pour ce champ personnalisé"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Valeur identique"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Ce groupe/utilisateur dispose déjà de ce droit"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Ce groupe/utilisateur est déjà un %1 pour cette file"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Ce groupe/utilisateur est déjà un %1 pour ce ticket"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Ce groupe/utilisateur n'est pas un %1 pour cette file"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "File inconnue"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Ticket ayant des tickets fils ou dépendants non résolus"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Cet utilisateur possède déjà ce ticket"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Utilisateur inconnu"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Utilisateur possédant déjà un statut privilégié"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Utilisateur déjà sans privilèges"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Utilisateur bénéficiant à présent du statut privilégié"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Cet utilisateur est maintenant non privilégié"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Cet utilisateur ne peut pas posséder de ticket dans cette file"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Identifiant non numérique"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Éléments de base"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "Le CC d'un ticket"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "L'AdminCC d'un ticket"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Cette commande trouve tous les tickets actifs de la file 'general' et positionne leur priorité à 99 s'ils n'ont pas été touchés depuis quatre heures :"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "La nouvelle valeur est enregistrée."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "L'intervenant d'un ticket"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Le demandeur d'un ticket"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Ces commentaires ne sont généralement pas accessibles par l'utilisateur"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Ce champ personnalisé ne s'applique pas à cet objet"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Cet possibilité n'est offerte qu'aux administrateurs systèmes"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Ce message va être envoyé à..."
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Cet outil permet à l'utilisateur de lancer un module perl quelconque depuis RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Cette opération semble ne pas avoir de contenu"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Les %1 tickets prioritaires de cet utilisateur"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Jeu."
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Ticket n°%1 mise à jour globale : %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Ticket n°%1 : %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "Ticket %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Ticket %1 créé dans la file '%2'"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Ticket %1 :  %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Champs personnalisés du ticket"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Historique du ticket n°%1 %2"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Ticket résolu"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Transactions du ticket"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Contenu du ticket"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Type du contenu du ticket"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Une erreur interne a empêché l'ajout du ticket"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Méta-données du ticket"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Statut de ticket modifié"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "Module de recherche TicketSQL"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Tickets"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "Tickets créés après"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "Tickets créés avant"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "Tickets résolus après"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "Tickets résolus avant"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Tickets dépendant de cette approbation :"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Temps estimé"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Temps restant"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Temps passé"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Temps restant"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Temps de calcul"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Temps passé"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "TempsPassé"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Titre"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Pour vous renseigner au sujet du support, de la formation, des développements spécifiques ou au sujet de la licence, merci de contacter en anglais %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Annoncé"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Outils"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "Total"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transaction"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "La transaction %1 est supprimée"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transaction ajoutée"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Champs personnalisées de la transaction"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transation->Create a échoué car vous n'avez pas spécifié de type d'objet et d'id"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Les transactions ne peuvent être transférées"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Mar."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Type"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Fonction non disponible"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Identifiant Unix"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Type d'encodage de courrier inconnu : %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Champ inconnu : $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Illimité"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Recherche non nommée"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Non privilégié"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Champs personnalisés non sélectionnés"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Objets non sélectionnés"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Non pris"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Mettre à jour"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Mettre à jour le ticket"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Mettre à jour le type"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Mettre à jour plusieurs tickets"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Mise à jour non enregistrée."
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Mettre à jour le ticket"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Mette à jour le ticket n°%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Mette à jour le ticket n°%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Le type de mise à jour n'était ni un commentaire ni un courrier."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Mis(e) à jour"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Uploader"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Télécharger plusieurs fichiers"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Télécharger plusieurs images"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Télécharger un fichier"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Télécharger une image"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Télécharger un maximum de %1 fichiers"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Télécharger au plus %1 images"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Télécharger vos changements"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Utiliser les autres outils d'administration de RT"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "L'utilisateur %1 ne peut être trouvé."
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Défini par l'utilisateur"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Conditions et actions définies par l'utilisateur"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Droits utilisateurs"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "L'utilisateur a demandé un type de mise à jour non connu pour le champ personnalisé %1 de l'objet %2 n°%3"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "L'utilisateur ne peut être créé : %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Utilisateur créé"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Groupes définis par l'utilisateur"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Utilisateur chargé"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Groupe définis par l'utilisateur"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Utilisateurs correspondants aux critères de recherche"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr "Utilisation de la transaction #%1..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Valider la requête"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Validation"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Valeurs"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Observer"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "ObserverCommeAdminCC"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Observateurs"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Mer."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "Ce que j'ai fait aujourd'hui"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Quand un ticket a été approuvé par tous les approbateurs, ajoute le courrier au ticket source"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Quand un ticket a été approuvé par au moins un approbateur, ajoute le courrier au ticket source "
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Quand un ticket est créé"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Quand un ticket d'approbation est créé, informer l'intervenant et l'AdminCC de l'élément attendant leur approbation"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Quand quelque chose arrive"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Lorsqu'un ticket quelconque est résolu/clos"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Lorsqu'un ticket quelconque change d'intervenant"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Lorsqu'un ticket change de priorité"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Lorsqu'un ticket quelconque change de file"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Lorsqu'un ticket quelconque change de statut"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Lorsqu'une condition définie par l'utilisateur est satisfaite"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Lorsque un commentaire arrive"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Lorsque un courrier arrive"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Travail"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Travail hors ligne"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Travaillé"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Vous êtes déjà intervenant de ce ticket"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Vous n'êtes pas un utilisateur autorisé"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "Vous pouvez aussi modifier la recherche prédéfinie elle-même"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Vous pouvez seulement réaffecter vos ticket ou ceux qui ne sont pas affectés"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr "Vous pouvez uniquement prendre des tickets sans propriétaire"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "%1 tickets trouvés dans la file %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Vous avez été déconnecté de RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Vous n'avez pas l'autorisation de créer des tickets dans cette file."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Vous ne pouvez pas créer de demandes dans cette file."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Vous êtes invité à vous identifier à nouveau"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Votre demande a été approuvée par %1. D'autres approbations sont peut être toujours en attente."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Votre demande a été approuvée."
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Votre demande a été rejetée."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Votre nom d'utilisateur ou votre mot de passe est incorrect"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Code Postal"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "autorise la création de recherches sauvées"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "autorise le chargement de recherches sauvées"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "comme accordé à %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "graphique"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "fermé"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "contient"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "jours"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "effacé"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "ne correspond pas"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "ne contient pas"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "égal à"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "erreur : ne peut aller plus bas"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "erreur : ne peut aller à gauche"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "erreur : ne peut aller plus haut"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "erreur : rien à effacer"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "erreur : rien à déplacer"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "erreur : rien à commuter"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "supérieur à"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "groupe '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "groupés par %1"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "heures"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "n°"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "est"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "n'est pas"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "inférieur à"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "correspond"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "mois"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "nouveau"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "sans nom"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "non renseigné"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "aucun"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "différent de"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "ouvert"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "groupe personnel '%1' pour l'utilisateur '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "file %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "rejeté"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "résolu"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sec"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "montrer l'onglet de configuration"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "feuille de calcul"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "stagnant"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr "style : %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "lignes de sommaire"
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "système %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "groupe système '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "le composant appelant n'a pas spécifié pourquoi"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "ticket n°%1 %2"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "groupe %1 non décrit"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "utilisateur %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "semaines"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "années"
+
diff --git a/rt/lib/RT/I18N/he.po b/rt/lib/RT/I18N/he.po
new file mode 100644
index 0000000..aa6309d
--- /dev/null
+++ b/rt/lib/RT/I18N/he.po
@@ -0,0 +1,5414 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"PO-Revision-Date: 2005-10-03 13:47-0400\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr ""
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr ""
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr ""
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr ""
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr ""
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr ""
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr ""
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "%1 - %2 מוצגי×"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr ""
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr ""
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr ""
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr ""
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr ""
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr ""
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "%1 הפניות ×¢× ×”×¢×“×™×¤×•×ª הגבוהה ביותר בטיפולי..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "%1 הפניות ×¢× ×”×¢×“×™×¤×•×ª הגבוהה ביותר ש×× ×™ פתחתי..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr ""
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr ""
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "זכויות"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(סמן תיבה כדי למחוק חבר בקבוצה)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(סמן תיבה כדי למחוק סקריפ)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(סמן תיבה כדי למחוק)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(סמן תיבות כדי למחוק)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr ""
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr ""
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr ""
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr ""
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr ""
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr ""
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr ""
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(רק פנייה ×חת)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr ""
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr ""
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr ""
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr ""
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"פנייה חדשה ב\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr ""
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "מידע ×ודותי"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr ""
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr ""
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr ""
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "הוסף העתק ניהולי"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "הוסף העתק"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr ""
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr ""
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "הוסף עוד קבצי×"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "הוסף מבקש"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr ""
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "הוסף הערות ×ו תגובות לפניות הנבחרות"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr ""
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "הוסף ×¦×•×¤×™× ×—×“×©×™×"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "כתובת1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "כתובת2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr ""
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr ""
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr ""
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr ""
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr ""
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "העתק ניהולי"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr ""
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr ""
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr ""
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "העתק ניהולי"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "×חרי"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr ""
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr ""
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr ""
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "×ישור"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr ""
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr ""
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr ""
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr ""
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr ""
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr ""
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr ""
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "×פריל"
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "×פריל"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "עולה"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "צרף"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr ""
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "קובץ מצורף"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "קובץ צורף"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "×©× ×§×•×‘×¥ מצורף"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "×§×‘×¦×™× ×ž×¦×•×¨×¤×™×"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr ""
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "×וגוסט"
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "×וגוסט"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr ""
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr ""
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "בסיסי"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "×ל תשכח לשמור ×ת השינויי×"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "לפני"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "הוסף כתובת זו לספר הכתובות כדי לחזור על ×ותו חיפוש"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "תקציר כותרי×"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "עדכון פניות מרוכז"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr ""
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr ""
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr ""
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "העתק"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr ""
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr ""
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "סמן תיבה כדי למחוק"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "סמן תיבה כדי לבטל זכות"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "ילדי×"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "עיר"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "נסגר"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "הערה"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr ""
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "הערות (×œ× × ×©×œ×—×•×ª ×ל המבקשי×)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "הערות (×œ× × ×©×œ×—×•×ª ×ל המבקשי×)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "הערות לגבי %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "הערות לגבי משתמש זה"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "הערות נוספו"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr ""
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "הגדרות"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr ""
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "תוכן"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr ""
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr ""
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr ""
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr ""
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr ""
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr ""
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr ""
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr ""
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr ""
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr ""
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "×רץ"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "צור"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "צור פניות"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr ""
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr ""
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "צור קבוצה פרטית חדשה"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "צור תור חדש"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "צור סקריפ חדש"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "צור תבנית חדשה"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "צור פנייה חדשה"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "צור משתמש חדש"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "צור תור חדש"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "צור תור שנקר×"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "צור בקשה"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr ""
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr ""
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr ""
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr ""
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr ""
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr ""
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr ""
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr ""
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "נוצר"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr ""
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "×™×—×¡×™× × ×•×›×—×™×™×"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr ""
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr ""
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "קריטריוני החיפוש הנוכחיי×"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "×¦×•×¤×™× × ×•×›×—×™×™×"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr ""
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr ""
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr ""
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr ""
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr ""
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "ת×ריכי×"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "דצמבר"
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "דצמבר"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr ""
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr ""
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr ""
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr ""
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr ""
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr ""
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr ""
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr ""
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr ""
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr ""
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "דלגציות"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "מחק"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr ""
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr ""
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr ""
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "×ª×œ×•×™×™× ×‘×•"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "תלוי ב"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "יורד"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "תי×ור"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "פרטי×"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "הצג"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "מצב תצוגה"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "הצג פנייה #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr ""
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr ""
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "×ל תרענן דף ×–×”."
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "×ל תר××” ×ת תוצ×ות החיפוש"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "הורד"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr ""
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "ת×ריך יעד"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr ""
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr ""
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr ""
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr ""
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "××™-מייל"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "מופעל (מחיקת סימון תיבה זו מבטלת ×ת קבוצה זו)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr ""
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr ""
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr ""
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr ""
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "הכנס פניות ×ו כתובות כדי לקשר פניות ×ליהן. הפרד ×¢×¨×›×™× ×¨×‘×™× ×‘×מצעות רווחי×."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr ""
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr ""
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr ""
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr ""
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr ""
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "פברו×ר"
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "פברו×ר"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "עדיפות סופית"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr ""
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr ""
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "×ž×¦× ×× ×©×™× ×©"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "×ž×¦× ×¤× ×™×•×ª"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "עמוד ר×שון"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr ""
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "הכרח שינוי"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr ""
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "נמצ×ו %1 פניות"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr ""
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "שישי"
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "×›×•×ª×¨×™× ×ž×œ××™×"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr ""
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "גלוב×לי"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr ""
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr ""
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "חפש"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr ""
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "קבוצה"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "קבוצה %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "זכויות קבוצה"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "קבוצות"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr ""
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr ""
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "הסטוריה"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr ""
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr ""
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "דף הבית"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr ""
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "זהות"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr ""
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr ""
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr ""
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr ""
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "×× ×¢×“×›× ×ª משהו לעיל, ×ל תשכח ל"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr ""
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr ""
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr ""
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr ""
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr ""
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr ""
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr ""
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr ""
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr ""
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr ""
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr ""
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "ינו×ר"
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "ינו×ר"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr ""
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "יולי"
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "יולי"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "ג'מבו"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "יוני"
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "יוני"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr ""
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "מגע ×חרון"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "קשר ×חרון"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "נודע ל×חרונה"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "עדכון ×חרון"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "נותרה"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "תן למשתמש זה לגשת ל R"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "תן ×פשרות להעניק זכויות למשתמש ×–×”"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr ""
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr ""
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr ""
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr ""
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr ""
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "קישורי×"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "מיקו×"
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "מחובר כ %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "כניסה"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "יצי××”"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "שנה בעלות ל"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "שנה סטטוס"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "שנה ת×ריך יעד"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "שנה ת×ריך פתרון"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "שנה ת×ריך 'הותחל'"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "שנה ת×ריך התחלה"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "שנע ת×ריך מגע ×חרון"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "שנה עדיפות"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "שנה תור"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "שנה נוש×"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr ""
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "נהל קבוצות וחברות בקבוצות"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "נהל מ××¤×™×™× ×™× ×•×”×’×“×¨×•×ª ×©×ª×§×¤×™× ×œ×›×œ התורות"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "נהל תורות ומ××¤×™×™× ×™× ×¡×¤×¦×™×¤×™×™× ×œ×ª×•×¨×•×ª"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "נהל ×ž×©×ª×ž×©×™× ×•×¡×¤×¨×™×•×ª"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "מרץ"
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "מרץ"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "מ××™"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "מ××™"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "חבר הוסף"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "חבר נמחק"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "חבר ×œ× × ×ž×—×§"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "חבר ב"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "חברי×"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr ""
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "מיזוג הצליח"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "מיזוג נכשל. ×œ× ×™×›×•×œ×ª×™ להגדיר מזהה ×פקטיבי"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "מזג לתוך"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr ""
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "הודעה"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr ""
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "חסר מפתח ר×שי?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "נייד"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "טלפון נייד"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "שנה רשימת בקרת גישה"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr ""
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr ""
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr ""
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr ""
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr ""
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr ""
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr ""
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr ""
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr ""
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr ""
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr ""
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr ""
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr ""
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr ""
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr ""
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr ""
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr ""
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "שנה פנייה מספר %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "שינוי פנוייה מספר %1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "שינוי פניות"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr ""
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr ""
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "שני"
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "עוד לגבי %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr ""
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr ""
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "×”××™×©×•×¨×™× ×©×œ×™"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "×”××™×©×•×¨×™× ×©×œ×™"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "ש×"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "×©× ×‘×©×™×ž×•×©"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "חדש"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "×™×—×¡×™× ×—×“×©×™×"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "×¡×™×¡×ž× ×—×“×©×”"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "חיפוש חדש"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "קבוצה חדשה"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "×¡×™×¡×ž× ×—×“×©×”"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "תור חדש"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "בקשה חדשה"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "זכויות חדשות"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "סקריפ חדש"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "חיפוש חדש"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "תבנית חדשה"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "פנייה חדשה"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "פנייה חדשה ×œ× ×§×™×™×ž×ª"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "משתמש חדש"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "משתמש חדש שנקר×"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "×¦×•×¤×™× ×—×“×©×™×"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "הב×"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "דף הב×"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "כינוי"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "כינוי"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr ""
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr ""
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr ""
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr ""
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr ""
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr ""
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr ""
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr ""
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr ""
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr ""
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr ""
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr ""
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr ""
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr ""
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr ""
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr ""
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "××£ ×חד"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr ""
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "×œ× ×‘×ª×•×š המערכת"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "×œ× ×‘×ª×•×š המערכת."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "×œ× ×”×•×–×Ÿ"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr ""
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr ""
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr ""
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr ""
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr ""
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr ""
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr ""
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr ""
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr ""
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr ""
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr ""
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr ""
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr ""
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr ""
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr ""
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr ""
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr ""
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "נובמבר"
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "נובמבר"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr ""
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr ""
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr ""
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "×וקטובר"
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "×וקטובר"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr ""
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr ""
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "ב"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr ""
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr ""
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr ""
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr ""
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr ""
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr ""
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr ""
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr ""
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "הצג רק ××™×©×•×¨×™× ×¢×‘×•×¨ בקשות שנוצרו ×חרי %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "הצג רק ××™×©×•×¨×™× ×¢×‘×•×¨ בקשות שנוצרו לפני %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "פתוח"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "פתח"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr ""
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr ""
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "סידור ומיון"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "×רגון"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr ""
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "בעלי×"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "הבעלי×"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "ביפר"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "הורי×"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "סיסמ×"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "מזכיר סיסמ×"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "×¡×™×¡×ž× ×§×¦×¨×” מדי"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "סיסמ×: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "הסיסמ×ות ×ינן תו×מות"
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr ""
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "×נשי×"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "קבוצות ×ישיות"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "קבוצות ×ישיות"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "קבוצות ×ישיות"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "מספרי טלפון"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "מ×פייני×"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "הקוד×"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "דף קוד×"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr ""
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "עדיפות"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr ""
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr ""
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr ""
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr ""
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr ""
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "תור"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr ""
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr ""
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr ""
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "תורי×"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "חיפוש מהיר"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr ""
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr ""
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "ניהול RT"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr ""
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT ממבט כולל"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT / %1"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "×©× ×מיתי"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "×©× ×מיתי"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "×ž×ª×™×™×—×¡×™× ×ליו"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "מתייחס ל"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "חדד ×ת החיפוש"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "רענן דף זה כל %1 דקות."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "הסר העתק ניהולי"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "הסר העתק"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "הסר מבקש"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "הגב"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr ""
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "מענה לפנייה"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "מבקש"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "כתובת ×”××™-מייל של המבקש"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "מבקש(×™×)"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "כתובת הפונה"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "מבקשי×"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr ""
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr ""
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "×פס נתוני×"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "בית"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "פתור"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "פתור פנייה #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "נפתר"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "תגובה למבקשי×"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "תוצ×ות"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "תוצ×ות לעמוד"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "הקלד שנית:"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr ""
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr ""
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr ""
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr ""
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "שבת"
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr ""
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "שמור שינויי×"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "שמור שינויי×"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr ""
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr ""
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "חיפוש"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr ""
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr ""
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr ""
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr ""
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr ""
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr ""
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr ""
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr ""
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr ""
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "בחר קבוצה"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr ""
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr ""
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr ""
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr ""
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr ""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr ""
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr ""
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr ""
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr ""
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr ""
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr ""
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "ספטמבר"
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "ספטמבר"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr ""
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "הצג בקשות ש×ושרו"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "הצג בקשות שנדחו"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "הצג בקשות ממתינות"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "הצג בקשות שממתינות ל××™×©×•×¨×™× ×חרי×"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr ""
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "חתימה"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr ""
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "סדר תוצ×ות על פי"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "מושהה"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "התחיל"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "מתחיל ב"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "מדינה"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "מצב"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "גנוב"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "נגנב מ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "נגנב מ %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr ""
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "נוש×"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "× ×•×©× ×©×•× ×” ל %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "שלח"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "הצליח"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "ר×שון"
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "סופר-משתמש"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "מערכת"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr ""
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr ""
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr ""
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "קח"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "נלקחה"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr ""
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr ""
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr ""
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr ""
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "מידע בסיסי"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr ""
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr ""
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr ""
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr ""
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr ""
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "חמישי"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket"
+msgstr "פנייה"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "פנייה מספר %1 עדכון ג'מבו: %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "פנייה מספר %1 עדכון ג'מבו: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr ""
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr ""
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr ""
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr ""
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "מצורף לפנייה"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr ""
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "פנייה נוצרה"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "פנייה נמחקה"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "מזהה פנייה ×œ× × ×ž×¦×"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "פנייה נמחקה"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "פנייה ×œ× × ×ž×¦××”"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "סטטוס פנייה שונה"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "צופי הפנייה"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "פניות"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "זמן נותר"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "זמן עבודה"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "זמן נותר"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "זמן להציג"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "זמן עבודה"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr ""
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr ""
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr ""
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr ""
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "שלישי"
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "סוג"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "×œ× ×ž×™×™×•×©×"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr ""
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr ""
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "×œ× ×ž×•×’×‘×œ"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr ""
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "עדכן"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr ""
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "סוג עדכון"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "עדכן ×ת כל הפניות לעיל בבת ×חת"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "עדכן ××™-מייל"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "עדכן ש×"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "עדכן פניות נבחרות"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "עדכן חתימה"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "עדכן פנייה"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr ""
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr ""
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr ""
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "עודכן"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr ""
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr ""
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr ""
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "מזהה המשתמש"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "מזהה המשתמש"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "זכויות המשתמש"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr ""
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "×©× ×ž×©×ª×ž×©"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "משתמשי×"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr ""
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr ""
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "רביעי"
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr ""
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr ""
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "בכל ×¤×¢× ×©×“×‘×¨ ×›×œ×©×”×•× ×§×•×¨×”"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "בכל ×¤×¢× ×©×¤× ×™×™×” נסגרת"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "בכל ×¤×¢× ×©×‘×¢×œ×™ הפנייה משתנה"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr ""
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "בכל מצב שתור הפנייה משתנה"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "בכל ×¤×¢× ×©×ž×¦×‘ הפנייה משתנה"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "בכל ×¤×¢× ×©×ž×¦×‘ מוגדר על ידי משתמש קורה"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "בכל ×¤×¢× ×©×”×¢×¨×” מגיעה ב"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "בכל ×¤×¢× ×©×ª×›×ª×•×‘×ª מגיעה ב"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "עבודה"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "טלפון בעבודה"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "זמן טיפול"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "×תה כבר ×”×‘×¢×œ×™× ×©×œ פנייה זו"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "×ינך משתמש מורשה"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "×תה יכול להציב פניה רק ×× ×תה ×”×‘×¢×œ×™× ×©×œ×”, ×ו ש×ין לה בעלי×"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "×ין לך הרש××” כדי לר×ות ×ת פנייה זו.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "מצ×ת %1 פניות בתור %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "התנתקת מהמערכת."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "×ין לך הרש×ות ליצור פניות בתור ×–×”."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "×ינך מורשה ליצור פניות בתור ×–×”."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "הנך מוזמן להיכנס שנית"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "%1 הבקשות שלך"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "מנהל המערכת ×œ× ×”×’×“×™×¨ ×ת כתובות הדו×ר שמפעילות ×ת התוכנה כמו שצריך"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "בקשתך ×ושרה על ידי %1. ייתכן ש××™×©×•×¨×™× × ×•×¡×¤×™× ×¢×“×™×™×Ÿ ממתיני×."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "בקשתך ×ושרה."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "בקשתך נדחתה"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "בקשתך נדחתה."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "×©× ×”×ž×©×ª×ž×© ו/×ו ×”×¡×™×¡×ž× ××™× × × ×›×•× ×™×"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "מיקוד"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[×œ×œ× × ×•×©×]"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "שהוענק ל%1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "סגור"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "מכיל"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "תוכן"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "סוג התוכן"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "התכתבות (כנר××”) ×œ× × ×©×œ×—×”"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "התכתבות נשלחה"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "ימי×"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "מחק"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "מחוק"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "×œ× ×ž×›×™×œ"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "×œ× ×ž×›×™×œ"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "שווה ל"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr ""
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "×©× ×§×•×‘×¥"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "גדול מ"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "קבוצה %1"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "שעות"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "מזהה"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "הו×"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "×”×•× ×œ×"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "פחות מ"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "מכיל"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "דקות"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "דקות"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "חודשי×"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "חדש"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "×ין ערך"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "×ין"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "×œ× ×©×•×•×” ל"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "פתוח"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "נדחה"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "פתור"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr ""
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "מושהה"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr ""
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr ""
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr ""
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr ""
+
diff --git a/rt/lib/RT/I18N/hu.po b/rt/lib/RT/I18N/hu.po
new file mode 100644
index 0000000..1edebd1
--- /dev/null
+++ b/rt/lib/RT/I18N/hu.po
@@ -0,0 +1,5170 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2002-06-22 06:06+0200\n"
+"PO-Revision-Date: 2005-10-03 13:48-0400\n"
+"Last-Translator: Attila K. Mergl <mergl@astron.hu>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %7. %2 %3 %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "[%1] mezõbe '%2' érték felvétele"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 változtatása: '%2' --> '%3'"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 törölve"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 a %3 sablonnal"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (változatlan)"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - %2-nek átadandó argumentum"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Státuszfrissítés kiíratása a STDOUT-ra"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+# msgstr "%1 - Schreibe Statusupdates nach STDOUT"
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Adja meg a használandó akciómodult!"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Adja meg a használandó kondiciómodult!"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Adja meg a használandó keres?modult!"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScripAction %1 betöltve"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 értéke hozzáadva %2 höz"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 nem találhato az adatbázisban, pedig helyi objektumnak látszik"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 (%2)"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 változtatása: '%2' --> '%3'"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr ""
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 nem állítható %2 re."
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr ""
+"%1 nem tudta a státuszt lezártra állítani. Az RT adatbázisa talán "
+"inkonzisztens."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 létrehozva"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 törölve"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "A %1 nekem kiosztott, legsürgõsebb probléma..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "Ennek a sornak %1 már nem %2-je."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 perc"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 legújabb gazdátlan probléma"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 jogok"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: Nincs melléklet megadva!"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' nem lehet státusz érték"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Törléshez kiválasztandó!)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Szóközökkel elválasztva adható meg a problémák sorszáma vagy URL-je.)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Ha üresen marad: %1)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Nincs egyéni mezõ)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Nincsenek tagok)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Nincs Scrip)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Nincs sablon)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(nincs név)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr ""
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(üres)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(Nincs név a listában)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(nincs adat)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(nincs adat)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "<br>(Csak egy probléma adható meg!)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr ""
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(szükséges)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(névtelen)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr ""
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr ""
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Új probléma\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Ãœres sablon"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE nem található"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE-k csak létrehozhatók és törölhetõk."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "ÉS"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Magamról"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Hozzáférés ellenõrzése"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Akció"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "%1 akció nem található"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Akció végrehajtva.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Akció elõkészítve..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Hozzáad"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Adminisztratív tájékotatást kapó hozzáadása"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Tájékotatást kapó hozzáadása"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Mezõk hozzáadása"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Bõvített keresési feltételek"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "További fájlok csatolása"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Beküldõ hozáadása"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Értékek hozzáadása"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Minden sorra vonatkozó Scrip hozzáadása"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "További feltételek hozzáadása"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "A kiválasztott problémákhoz válasz vagy kiegészítés hozzáadása"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Tagfelvétel"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Új résztvevõk hozzáadása"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Egyéni mezõk lehetséges értékeinek létrehozása, módosítása, törlése"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Hozzendelt egy vezetõt mint %1 ehhez a sorhoz"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Hozzárendelt egy vezetõt mint %1 ehhez a problémához"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Utca, házszám"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Postafiók"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Adminisztratív tájékoztatást kap"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Adminisztratív megjegyzés"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr ""
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Válasszon sort!"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Konfiguráció / Ãltalános beállítások"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "Adminisztratív tájékoztatást kap"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr ""
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr ""
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr ""
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Adminisztratív tájékoztatást kap"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Kézi lekérdezés"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "után"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr ""
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Minden sor"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "ÉS/VAGY"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Mentés"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "A változtatás mentése"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Lezárás"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Engedélyezés #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Engedélyezés #%1: A megjegyzés rendszerhiba miatt nem rögzíthetõ"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Engedélyezés #%1: Megjegyzés rögzítve"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Lezárás rendben"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Lezárás visszautasítva"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Lezár"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Az engedélyezõ megjegyzése: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "ápr."
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "növekvõ"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Egyéni mezõ hozzárendelése vagy törlése"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Csatol"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Fájl csatolása"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Csatolt fájl"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "A csatolt '%1' nem betölthetõ"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Csatolt fájl létrehozva"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Csatolt fájl neve"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Csatolt fájlok"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr ""
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "aug."
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Automatikus válasz"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Automatikus válasz a beküldõnek"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Alapadatok módosítása"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "BCC"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr ""
+"Ha valamit módosított ne felejtsen el ide kattintani, hogy megtörténjen a"
+
+# ## wieder - Duzen???
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "elõtt"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Ãœres"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Félkövér"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Tömör fejléc"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Tömeges problémafrissítés"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Rendszerfelhasználó nem módosítható"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Ez a vezetõ nem láthatja ezt a sort"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Egyéni mehzõ név nélkül nem hozzáadható"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "A problémát nem lehet önmagához kapcsolni!"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Ezt a lekérdezést nem lehet lementeni"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Nem adhat meg egyszerre bázist és célt"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "A felhasználó nem hozható létre: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Tájékoztatást kap"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Jelszó-változtatás"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr ""
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Törléshez bejelölni"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Jog megvonásához megjelölni"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Leszármazottak"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Város"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Lezárva"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Lezárt problémák"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Kiegészít"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Kiegészitõcím"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr ""
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Kiegészítés (a beküldõ nem kapja meg!))"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Kiegészítés (a beküldõ nem kapja meg!)"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Kiegészítés ehhez a felhasználóhoz"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Kiegészítés hozzáfûzése"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Feltétel"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "A feltétel nem található"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Konfiguráció"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Igazolás"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Tartalom"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Másol"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Válasz"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Válasz hozzáfûzése"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Az egyéni mezõt nem sikerült hozzáadni."
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Az egyéni mezõértéket nem sikerült hozzáadni a #%1 problémához"
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Nem sikerült a tulajdonost megváltoztatni! "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Nem sikerült létrehozni az egyéni mezõt"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Nem sikerült létrehozni a(z) %1 egyéni mezõt"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Nem sikerült létrehozni a csoportot"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "A sablom nem jött létre: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Nem jött létre a probléma. A sor nincs meghatározva"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "A felhasználó nem jött létre"
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "A felhasználót nem sikerült megtalálni vagy létrehozni"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "A vezetõ nem található"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "A(z) %1 egyéni mezõ nem betölthetõ"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "A csoport nem betölthetõ"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "A vezetõ nem lehet %1-e a sornak"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "A vezetõ nem lehet %1-e a problémának"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "A vezetõt nem lehet mint a sor %1-ét eltávolítani"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "A felhasználó adatait nem sikerült elmenteni"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "A tagot nem lehet a csoporthoz adni"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "A tanzakció nem létrehozható: %1"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "A sor nem található"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "A csoport/felhasználó nem található"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Az érték nem található"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr ""
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "A(z) %1 egyéni mezõ nem betölthetõ"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "%1 csoport nem betölthetõ"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "%1 objektum nem betölthetõ"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "A sor nem betölthetõ"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "%1 sor nem betölthetõ"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "A(z) '%1' nem betölthetõ"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Ország"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Felvesz"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Probléma megadása"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Hozzon létre egy új egyéni mezõt!"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Hozzon létre egy egyéni mezõt a(z) %1 sorhoz"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Hozzon létre új csoportot!"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Hozzon létre egy új saját csoportot!"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Új probléma felvétele"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Hozzon létre egy új felhasználót!"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Hozzon létre egy sort!"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Hozzon létre egy scrip-et a(z) %1 sorhoz!"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Hozzon létre egy sablont!"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Új probléma"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Hozzon létre egy új problémát ennek a sablonnak scripjeire alapozva"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Létrehozás"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Hozzon létre problémákat ebben a sorban!"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Egyéni mezõk létrehozása, módosítása, törlése"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Sor létrehozása, módosítása, törlése"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr ""
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Felhasználók létrehozása, módosítása, törlése"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr ""
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Felvéve"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Létrehozó"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Jelenlegi kapcsolatok"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Jelenlegi Scripek"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Jelenlegi tagok"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Jelenlegi jogok"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Jelenlegi résztvevõk"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Egyéni mezõk"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "%1 egyéni mezõi"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Az akcióhoz rendelt egyéni lezáró kód"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Az akcióhoz rendelt egyéni elõkészítõ kód"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Egyéni feltétel"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Egyéni mezõ %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "[%1] egyéni mezõnek van értéke."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "[%1] egyéni mezõnek nincs értéke."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "[%1] egyéni mezõ nem található"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Egyéni mezõ nem található"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "A(z) %2 egyéni mezõ %1értéke nem található"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Az egyéni mezõ értéke nem található"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Az egyéni mezõ értéke nem található"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Az egyéni mezõ értéke törölve"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr ""
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Idõpontok"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "dec."
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr ""
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Alapértlemezett sor"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Alapértelmezett beküldõ"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr ""
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr ""
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr ""
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr ""
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Jogok továbbadása"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr ""
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr ""
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Jogok továbbadása"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Töröl"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Sablon törlése"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Kiválasztott Scrip törlése"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Problémák törlése"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr ""
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Elutasítva"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Ettõl függnek"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "A következõktõl függ"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "csökkenõ"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "A probléma szabatos leírása"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Leírás"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Mutat"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Mezõk kiválasztása"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Láthatóak legyenek az ehhez a sorhoz tartozó scrip sablonok"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Láthatóak legyen a sorhoz tartozó scripek"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Megjelenítési mód"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr ""
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr ""
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Do the Search"
+msgstr "Keresés indítása"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Ne frissítse ezt az oldalt."
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Letölt"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr ""
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Határidõ"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr ""
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Módosítsa %1 egyéni mezõit!"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Kapcsolatok módosítása"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Lekérdezés szerkesztése kézzel"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Módosítsa a(z) %1 sor sablonjait!"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Módosítsa a rendszersablonokat!"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Módosítsa a(z) %1 csoport tagságát!"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr ""
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Meg kell adni a bázist vagy a célt"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "E-mail cím"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "E-Mail-cím már foglalt"
+
+# ## muss das überhaupt übersetzt werden???
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Aktivált (kiválasztása letiltja ezt az egyéni mezõt)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Aktivált (Kiválasztása letiltja ezt a csoportot)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Aktivált (Kiválasztása letiltja ezt a sort)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Aktív sorok"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Status %1 aktiviert"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Adjon meg több értéket!"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Objektum vagy URI. Szóközzel elválasztva több is megadható"
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Adjon meg egy értéket!"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Sor vagy URI. Szóközzel elválasztva több is megadható"
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Probléma szám vagy URI. Szóközzel elválasztva több is megadható"
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Adjon meg egy értéket!"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Hiba"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Hiba a sor paraméterezésében ->AddWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Hiba a sor paraméterezésében->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Hiba a probléma paraméterezésében -> AddWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Hiba a probléma paraméterezésében->DelWatcher"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Becsült"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Mindenki"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Például:"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Kiegészítõ információk"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr ""
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "A %1 Modul nem betölthetõ. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "feb."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "fájlnév"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Végsõ prioritás"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr ""
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Találjon olyan csoportot, amelyiknél a"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Találj olyan embereket, akiknél a"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Problémák keresése"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Elsõ"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr ""
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "A változtatás kikényszerítése"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr ""
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "%quant(%1) problémát találtam"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr ""
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "P"
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Teljes fejléc"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "%1 felhasználónak adva"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Ãltalános"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Globális egyéni mezõ"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Egyéni mezõk általános beállításai"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Ãltalános probléma: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Hajrá!"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Hajrá!"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Mutassa a problémát"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Csoport"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Csoportjogok"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Csoportnak már vannak tagjai"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "A csoportot nem lehetett létrehozni: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Csoport létrehozva"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "A csoportnak már van ilyen tagja"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Csoport nem található"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Csoportok"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Csoportok nem lehetnek a saját tagjaik tagjai!"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "A keresési feltételeknek megfeleõ csoportok"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr ""
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hallo!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Hallo %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Részletes történet"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "A %1 csoport története"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "%1 felhasználó története"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Kezdõlap"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr ""
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Sorszám"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Személy"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr ""
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Ha a beküldõ ismeretlen, ezzel legyen az új probléma beküldõje"
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Ha nincs megadva sor, ebbe a sorba kerüljenek az új problémák"
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr ""
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr ""
+"Ha valamit módosított, ne felejtsen el ide kattintani, hogy megtörténjen a"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Érvénytelen érték %1-nek"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Kép"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Nem változtatható mezõ"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Láthatóak legyenek a letiltott sorok is."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Láthatóak legyenek a letiltott sorok is."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Keresés a letiltott felhasználók között is."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Kezdõ prioritás"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Input hiba"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Belsõ hiba"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Belsõ hiba: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Érvénytelen csoportfajta"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Érvénytelen jog"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Érvénytelen dátum"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Érvénytelen sor"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Érvénytelen jog"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Érvénytelen érték %1-nek"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Érvénytelen érték az egyéni mezõhöz"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Érvénytelen státuszérték"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "jan."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr ""
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "júl."
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Minden módosítása"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "jún."
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Nyelv"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Nagy"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Utolsó"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Utolsó kapcsolat"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Utoljára foglalkoztak vele"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Utoljára aktualizálva"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "Utoljára aktualizálta"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Felhasználónak RT-hozzáférés engedélyezése"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Felhasználónak több jog engedélyezése"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Kapcsolat már létezik"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Kapcsolat nem hozható létre"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Kapcsolat létrehozva (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Kapcsolat törölve (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Kapcsolat nem található"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "%1. számú probléma kapcsolatainak módosítása"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Kapcsolatok módosítása"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Lekérdezés betöltése"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Cím"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Cím"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr ""
+"Log-könyvtár %1 nem található, vagy nincs írási joga.\\n Az RT tud elindulni."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "%1-ként van bejelentkezve"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Bejelentkezés"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Kijelentkezés"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Legyen a tulajdonos"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Státusz változtatása"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Határidõ változtatása"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Megoldási dátum változtatása"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Hozzáfogás dátumának változtatása"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Kiosztás dátumának változtatása"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Bejelentés dátumának változtatása"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Prioritás állítása"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Sor változtatása"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Tárgy változtatása"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr ""
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Csoportok és tagjaik kezelése"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Minden sorra vonatkozó tulajdonságok és beállítások kezelése"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Sorok és a sorokra jellemzõ beállítások kezelése"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Felhasználók és jelszavaik kezelése"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "már."
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "máj."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Tag hozzáadva"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Tag törölve"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Tag hozzáadva"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Tag törölve"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Tag nincs törölve"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Tagja a"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Tagok"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "%1 tag hozzáadva"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "%1-beli tagság törölve"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Tagságok"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "%1 felhasználó tagságai"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Beolvasztás sikeres"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Beolvasztás sikertelen Konnte EffectiveId nicht setztn"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Beolvasztás sikertelen Konnte EffectiveId nicht setztn"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Beolvasztani ebbe"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Beolvasztva %1-be"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Üzenet szövege"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "A sor nem hozható létre"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Üzenet rögzítve"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr ""
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Mobil"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Változtassa meg a %1-re vonatkozó egyéni mezõket!"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Csoportjogok módosítása"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Tagok módosítása"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Jogok módosítása"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr ""
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Felhasználói jogok módosítása"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Változtassa meg a(z) %1 sor egyéni mezõjét!"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr ""
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "%1. számú probléma idõadatainak módosítása"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "%1. számú probléma idõadatainak módosítása"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Változtassa meg az általános csoportjogokat!"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Változtassa meg az általános csoportjogokat!"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Változtassa meg az általános felhasználói jogokat!"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Változtassa meg az általános felhasználói jogokat!"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Változztassa meg a(z) %1 sor csoportjogait!"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Módosítsa a(z) %1 csoport jogait"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Változtassa meg a %1 sor csoportjogait!"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr ""
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Változtassa meg a %1 sorhoz tarozó részvevõket!"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "A %1. számú problémában érintett személyek változtatása"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Változtassa meg a(z) %1 sor Scripjeit"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Változtassa meg a minden sorra vonatkozó scripeket!"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Hozzon létre egy sablont!"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Változtassa meg az általános sablonokat!"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "A %1 csoport módosítása"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "%1 felhasználó módosítása"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "%1. számú probléma adatainak módosítása"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "%1. számú probléma alapadatainak módosítása"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr ""
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Változtassa meg a(z) %1 csoport jogait!"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Változtassa meg a(z) %1 sor felhasználói jogait!"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr ""
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "H"
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "További részletek %1 felhasználóról"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Lefelé mozgatni"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Felfelé mozgatni"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr ""
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "%1 problémáim"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Lezárás"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Saját lekérdezések"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Név"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "A felhasználói név már foglalt"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Soha"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Új"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Új kapcsolatok"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Új jelszó"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Új lekérdezés"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr ""
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Új egyéni mezõ"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Új csoport"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Új jelszó"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Új sor"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Új jogok"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Új Scrip"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Új sablon"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Új probléma"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Az új probléma nem létezik"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Új felhasználó"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr ""
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Új résztvevõk"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Következõ"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Következõ oldal"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Becenév"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Nincs egyéni mezõ"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Nincs egyéni mezõ meghatározva"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Nincs csoport meghatározva"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Nincs sor meghatározva"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr ""
+"RT-felhasználó nem található. Vegyew fel a kapcsolatot az RT-"
+"adminisztrátorral.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Nincs sablon"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr ""
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Nincs <b>sor</b> megadva!"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Nincs kiegészítés tárolva"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Nincs leírás a(z) %1-hez"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Nincs <b>csoport</b> megadva!"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Nincs a keresési feltételeknek megfeleõ csoport."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Nincs jelszó megadva"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Nincs joga sort létrehozni"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Nincs joga a(z) '%1' sorban problémát létrehozni"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Nincs joga felhasználót létrehozni"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Nincs joga ezt a problémát aktualizálni"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Nincs <b>felelõs</b> megadva!"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Nincs vezetõ kiválasztva."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Nem található a keresési fletételeknek megfelelõ sor"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Nem találhatók jogok"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Nincsenek jogok engedélyezve."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Nincs feldolgozható keresési lista."
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "(nincs tárgy)"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Nincs <b>értesítési típus</b> megadva!"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Nincs a keresési feltételeknek megfeleõ felhasználó."
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr ""
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Senki"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Nem létezõ mezõ?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Nincs bejelentkezve."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Nincs megadva"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr ""
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Megjegyzések"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Értesítést nem sikerült elküldeni."
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "AdminCCs értesítése"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "AdminCCs értesítése kiegészítésként"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr ""
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Más címzettek értesítése"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Más címzettek értesítése kiegészítésként"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "A tulajdonos értesítése"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "A tulajdonos értesítése kiegészítésként"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr ""
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr ""
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr ""
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr ""
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Beküldõ értesítése"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "A beküldõ és a tájékoztatást kapó értesítése"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "A beküldõ és a tájékoztatást kapó értesítése kiegészítésként"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Minden résztvevõ tájékozatatása"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Minden résztvevõ értesítése kiegészítésként"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "nov."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "VAGY"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objekum nem hozható létre"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Az objekumot nem sikerült törölni"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objektum létehozva"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objektum törölve"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "okt."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr ""
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr ""
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "e napon"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Kiegészítéskor"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Válaszoláskor"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Létrehozáskor"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Tulajdonos változásakor"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Prioritás változásakor"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Sor megváltozásakor"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Megoldáskor"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Státusz megváltozásakor"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Tranzakciónál"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Csak a(z) %1 után létrejött poblémák engedélyezései legyenek láthatóak"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr ""
+"Csak a(z) %1 elõtt létrejött problémák engedélyezései legyenek láthatóak"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Nyitott"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Megnyit"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Nyitott problémák"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr ""
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Rendezés"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Cég, szervezet"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Prioritás idõvel emelkedik"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Saját problémák"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr ""
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Tulajdonos"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Erõszakos tulajdonosváltás: %1 --> %2"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "%1/%2 oldal"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr ""
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Felmenõk"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Jelszó"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Jelszó-emlékeztetõ"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Jelszó megváltoztatva"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "A jelszó túl rövid!"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Jelszó: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "A jelszavak nem egyeznek."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr ""
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Résztvevõk"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl konfiguráció"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Hozzáférés visszautasítva"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Saját Csoportok"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Saját csoportok"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Saját csoportok:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefonszámok"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Beállításaim"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Elõzõ"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Elõzõ oldal"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "%1 vezetõ nem található."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioritás"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Prioritás kezdõ értéke"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr ""
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Kiemelt"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr ""
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Kiemelt felhasználók:"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "Lekérdezés"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Lekérdezésszerkesztõ"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Sor"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "%2 sor nem található"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "A sor neve"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Sor már létezik"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "A sor nem hozható létre"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "a sor nem tölthetõ be."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "A sor létrehozva"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "A sor nem található"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Sorok"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Gyors lekérdezés"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Probléma felvétele (gyors)"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 %2-nek"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Konfiguráció"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT Hiba"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr ""
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "Ãttekintés"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr " %1 -- Request Tracker "
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Valódi név"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "%1 hivatkozás törölve"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "A következõk kapcsolódnak hozzá"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "A következõkhöz kapcsolódik"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "%1 percenként frissítse ezt az oldalt."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Adminisztratív tájékoztatást kapó eltávolítása"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Tájékoztatást kapó eltávolítása"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Beküldõ eltávolítása"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Válaszol"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Válaszcím"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Válasz a beküldõknek"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Válasz a problémákra"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr ""
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Beküldõ"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Beküldõ"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "A problémát meg kell oldani"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr ""
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Visszaállít"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Otthoni"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Megold"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "%1. (%2) probléma lezárása"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Megoldva"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Változtatások eredménye"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Jelszó megismételve"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "A jog megosztva"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "A jog betöltve"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "A jog nem vonnható vissza"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "A jog nem található"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "A jog nem található."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Jog visszavonva"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Jogok"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr " %1 jogai nem vonhatók vissza"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Szerepek"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Sorok száma oldalanként"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Szo"
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Mentés"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Változtatások mentése"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Beállítások mentése"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Változtatások mentése"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Elmentett lekérdezés"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip létrehozva"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Scrip mezök"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip törölve"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Minden sorhoz érvényes Scripek"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Keresés"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Engedélyezések keresése"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr ""
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Biztonság:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Eyéni mezõk"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr ""
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Válasszon egy egyéni mezõt!"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Válasszon csoportot!"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr ""
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr ""
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Válasszon felhasználót!"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Válasszon egy egyéni mezõt!"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Válasszon egy egyéni mezõt!"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Válasszon csoportot!"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Válasszon több értéket!"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Válasszon értéket!"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Válasszon sort!"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Válasszon Scripet!"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Válasszon sablont!"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Válasszon (legfeljebb %1) értéket!"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Válasszon felhasználót"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Kiválasztott egy egyéni mezõk"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Kiválasztott problémák"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr ""
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr ""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr ""
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr ""
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr ""
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr ""
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr ""
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr ""
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "szep."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr ""
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Lezárások"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Látható mezõk"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Lekérdezés eredménye"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Láthatóak legyenek a felszabadított problémák"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Problémaleírás"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "láthatóak legyenek az elutasított problémák"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Attribútumok"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Láthatóak legyenek a folyamatban levõ problémák"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Láthatóak legyenek a mások engedélyezésére váró problémák"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr ""
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Aláírás (e-mail-hez)"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr ""
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Kicsi"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Rendezés"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Elkezdve"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Kiosztva"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Ãllam"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Státusz"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Státusz változtatása: '%1' --> '%2'"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Elcsen"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Problémák elcsenése"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Probléma elcsenése %1 tulajdonostól"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Probléma elcsenése %1 tulajdonostól "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr ""
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Tárgy"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr ""
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Mentés"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Sikerült"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "V"
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Rendszer"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Rendszerkonfiguráció"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Rendszerhiba"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Rendszerkonfiguráció"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Rendszerhiba. Jog nem delegálható ."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Rendszerhiba. Jog nem adható."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Rendszercsoportok"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr ""
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr ""
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Atvesz"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Problémák átvétele"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Ãtvéve"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Sablon"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Sablon #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Sablon törölve"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Sablon nem található"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Sablon beolvasva"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Sablonok"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "Szöveg"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Már ez az aktuális érték"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Egyéni mezõhöz érvénytelen értek"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Ez ugyanaz az érték"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "A felhasználónak/csoportnak mát van ilyen joga"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "A vezetõ már %1-a a sornak"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "A vezetõ már %1-a a problémának"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "A vezetõ már nem %1-a a sornak"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Ez a sor nem létezik"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr ""
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Már ez a felhasználó a tulajdonos!"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Ilyen felhasználó nem létezik"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "A felhasználó már kiemelt"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "A felhasználó már nem kiemelt"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "A felhasználó mostantól kiemelt"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "A felhasználó mostantól nem kiemelt"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "a felhasználóhoz nem tartozhatnak problémák ebbõl a sorból"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr ""
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Alapadatok"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr ""
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr ""
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr ""
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr ""
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Diese Transaktion scheint keinen Inhalt zu haben"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "A felhasználó által felvetett %1 legsürgõsebb probléma"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Cs"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "%1. számú (%2) probléma jellemzõinek módosítása"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Probléma. %1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "%1. probléma"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr ""
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Probléma %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Probléma egyéni mezõi"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr ""
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Pobléma megoldva"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Probléma tranzakciói"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "A probléma tartalma"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr ""
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Probléma metadata"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr ""
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Problémák"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Maradék idõ"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Ráfordított munkaidõ"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Maradék idõ"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr ""
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Munkaidõ"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "Ráfordított munkaidõ"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr ""
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Beérkezett"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Eszközök"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Tranzakció egyéni mezõi"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr ""
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "K"
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Típus"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr ""
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr ""
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "korlátlan"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Névtelen lekérdezés"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Nem kiemelt"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Inaktív egyéni mezõk"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Inaktív objektumok"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Probléma visszaadása"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Frissít"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Mindet frissíti"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Probléma frissítése"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Frissítés típusa"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "A kiválasztott problémák frissítése"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "A kiválasztott problémák frissítése"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr ""
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr ""
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "%1. számú (%2) probléma aktualizálása"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr ""
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Frissítve"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Feltöltés"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Több fájl feltöltése egyszerre"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Több kép feltöltése egyszerre"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Egy fájl feltöltése"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Egy kép feltöltése"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr ""
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Módosítások feltöltése"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "'%1' felhasználó nem található"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Felhasználó által meghatározott"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr ""
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Felhasználói jogok"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Felhasználó létrehozva"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Felhasználó által definiált csoport"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Felhasználó betöltve"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Felhasználó által definiált csoportok"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Felhasználó"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Felhasználók"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr ""
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Értékek"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Résztvevõk"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Sze"
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr ""
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr ""
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr ""
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr ""
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr ""
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr ""
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr ""
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr ""
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr ""
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr ""
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Munkahelyi"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Munkaidõ"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Már az öné ez a probléma"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr ""
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "%1 Problémát talált a %2 sorban"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Kilépett a Request Tracker programból."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Nincs joga létrehozni problémákat ebben a sorban"
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Nem hozhat létre problémákat ebben a sorban"
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Itt ismét bejelentkezhetsz!"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr ""
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "A problémája engedélyezve."
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "A problémája elutasítva."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "A begépelt felhasználói név vagy jelszó hibás!"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Irányítószám"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "létrehozhat saját lekérdezéseket"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "betölthet saját lekérdezéseket"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "%1nak engedélyezve"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "Lezárt"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "tartalmazza"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "napja"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "törölve"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "nem"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "nem tartalmazza"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "egyenlõ"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "hiba: nincs mit törölni"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "nagyobb mint"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr " '%1' csopot"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "órája"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "Sorszám"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr " == "
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr " nem "
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "kisebb mint"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "=="
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "perce"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "perc"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "hónapja"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "új"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "(nincs név)"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "Nincs érték"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "senki"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "nem egyenlõ"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "Nyitott"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "Stapel %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "elutasítva"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "megoldva"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sec"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "várakozik"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "rendszer %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr ""
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "probléma #%1 %2"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "felhasználó %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "hete"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "éve"
+
diff --git a/rt/lib/RT/I18N/i_default.pm b/rt/lib/RT/I18N/i_default.pm
new file mode 100644
index 0000000..0f02a43
--- /dev/null
+++ b/rt/lib/RT/I18N/i_default.pm
@@ -0,0 +1,110 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::I18N::i_default;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::I18N);
+
+eval "require RT::I18N::i_default_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/I18N/i_default_Vendor.pm});
+eval "require RT::I18N::i_default_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/I18N/i_default_Local.pm});
+
+1;
+
+__END__
+
+This class just zero-derives from the project base class, which
+is English for this project. i-default is "English at least". It
+wouldn't be a bad idea to make our i-default messages be English
+plus, say, French -- i-default is meant to /contain/ English, not
+be /just/ English. If you have all your English messages in
+Whatever::en and all your French messages in Whatever::fr, it
+would be straightforward to define Whatever::i_default's as a subclass
+of Whatever::en, but for every case where a key gets you a string
+(as opposed to a coderef) from %Whatever::en::Lexicon and
+%Whatever::fr::Lexicon, you could make %Whatever::i_default::Lexicon
+be the concatenation of them both. So: "file '[_1]' not found.\n" and
+"fichier '[_1]' non trouve\n" could make for an
+%Whatever::i_default::Lexicon entry of
+"file '[_1]' not found\nfichier '[_1]' non trouve.\n".
+
+There may be entries, however, where that is undesirable.
+And in any case, it's not feasable once you have an _AUTO lexicon
+in the mix, as wo do here.
+
+
+
+RFC 2277 says:
+
+4.5. Default Language
+
+ When human-readable text must be presented in a context where the
+ sender has no knowledge of the recipient's language preferences (such
+ as login failures or E-mailed warnings, or prior to language
+ negotiation), text SHOULD be presented in Default Language.
+
+ Default Language is assigned the tag "i-default" according to the
+ procedures of RFC 1766. It is not a specific language, but rather
+ identifies the condition where the language preferences of the user
+ cannot be established.
+
+ Messages in Default Language MUST be understandable by an English-
+ speaking person, since English is the language which, worldwide, the
+ greatest number of people will be able to get adequate help in
+ interpreting when working with computers.
+
+ Note that negotiating English is NOT the same as Default Language;
+ Default Language is an emergency measure in otherwise unmanageable
+ situations.
+
+ In many cases, using only English text is reasonable; in some cases,
+ the English text may be augumented by text in other languages.
+
+
diff --git a/rt/lib/RT/I18N/id.po b/rt/lib/RT/I18N/id.po
new file mode 100644
index 0000000..77922aa
--- /dev/null
+++ b/rt/lib/RT/I18N/id.po
@@ -0,0 +1,5520 @@
+# Copyright (c) 2002 Jesse Vincent <jesse@bestpractical.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2005-10-03 13:48-0400\n"
+"Last-Translator: James <james@actionmessage.com>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: 7bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr "$Awalan %1"
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 #%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 sudah ditambah"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 yang lalu"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 diganti menjadi %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 sudah dihapus"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 gunakan template %3"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) oleh %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "(Belum diubah)"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Sebuah penjelasan untuk dilewati ke %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - status Output diperbaharui menjadi STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - menetapkan modul aksi yang ingin digunakan oleh anda"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - menetapkan moduk kondisi yang ingin digunakan oleh anda"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - menetapkan modul pencarian yang ingin digunakan oleh anda"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 Hak Cipta 1996-%3 %4."
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "%1 Memanggil AksiScrip"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 ditambahkan sebagai nilai untuk %2"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 ditampilkan sebagai objek lokal, tetapi tidak dapat ditemukan di dalam database"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 oleh %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 sudah diganti dari %2 ke %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 ditiru"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 tidak bisa diatur ke %2."
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 tidak dapat mengatur status untuk diselesaikan. Database RT's mungkin tidak konsisten."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 sudah dibuat"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 sudah dihapus"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "%1 Tiket dengan prioritas paling tinggi yang saya miliki"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "%1 Tiket dengan prioritas paling tinggi yang saya minta..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 adalah sebuah alat yang berfungsi pada tiket dari alat penjadwalan eksternal, seperti Cron."
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 tidak lagi menjadi %2 untuk antrian ini."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 tidak lagi menjadi nilai untuk kolom kustom %2"
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 menit"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 tiket terbaru yang belum dimiliki"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 newest unowned tickets..."
+msgstr "%1 tiket terbaru yang belum dimiliki..."
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 objek"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "hak-hak %1"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 akan memecahkan semua anggota dari grup tiket yang dipecahkan."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1's %2 objek"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1's %2's %3 objek"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "%1's pencarian disimpan"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: tidak ada lampiran yang ditentukan"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' adalah nilai yang tidak valid untuk status"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Klik kotak ini untuk hapus)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Klik kotak ini untuk menon-aktifkan pemberitahuan kepada penerima yang sudah terdaftar)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Klik kotak ini untuk mengaktifkan pemberitahuan kepada penerima yang sudah terdaftar)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Masukkan ID Tiket atau URL, dipisahkan dengan spasi)"
+
+#: NOT FOUND IN SOURCE
+msgid "(If left blank, will default to %1"
+msgstr "(Jika ditinggalkan kosong, default akan diatur menjadi %1"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Jika ditinggalkan kosong, default akan menjadi %1)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Tidak ada kolom kustom)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Tidak ada anggota)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Tidak ada scrip)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Tidak ada template)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Tidak satu pun)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Kirim salinan tembusan untuk memperbaharui daftar alamat email yang dibatasi dengan koma. <b>Tidak</b> akan mengubah siapakah yang akan menerima pembaharuan di masa datang.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Kirim salinan tembusan untuk memperbaharui daftar alamat administrasi email. Orang-orang ini <b>akan</b> menerima pembaharuan di masa datang.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Kirim salinan tembusan untuk memperbaharui daftar alamat email. <b>Tidak</b> akan mengubah siapakah yang akan menerima pembaharuan di masa datang.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Kirim salinan tembusan untuk memperbaharui daftar alamat email. Orang-orang ini <b>akan</b> menerima pembaharuan di masa datang.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "Gunakan kolom ini ketika anda memilih 'Definisikan Pengguna' untuk sebuah kondisi atau aksi"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(kosong)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(tidak ada nama yang terdaftar)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(Tidak ada nilai)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(Tidak ada nilai-nilai)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(hanya satu Tiket)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(izin ditunda)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(Koleksi yang lain ditunda)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(diperlukan)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(tidak ada judul)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Tiket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$kolom%>"
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<Tipe input=\"submit\" nilai=\"Tiket Baru di dalam \">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Template kosong"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE tidak ditemukan"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE hanya dapat dibuat dan dihapus."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "DAN"
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Dibatalkan untuk menghindari modifikasi tiket yang tidak diharapkan.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Tentang Aku"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Kontrol Akses"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Aksi"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Aksi %1 tidak ditemukan"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Aksi dijalankan."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Aksi dijalankan."
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Aksi disiapkan..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Tambah"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Tambah AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Tambah Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Tambah Kolom"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Tambah Kriteria"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Tambah File Lagi"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Tambah Pemohon"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Tambah Nilai"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Tambah scrip yang akan digunakan di semua antrian"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "Tambahkan kriteria tambahan"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Tambah Komentar atau balas ke tiket yang sudah dipilih"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Tambah Anggota"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Tambah Pengamat Baru"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Tambah, hapus dan ubah nilai kolom kustom untuk objek"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Tambahkan prinsipal sebagai %1 untuk antrian ini"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Tambahkan prinsipal sebagai %1 untuk Tiket ini"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Alamat1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Alamat2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Komentar Admin"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Korespondensi Admin"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Antrian Admin"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Konfigurasi Admin/Global"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "Kolom Kustom Admin"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "Grup Admin"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "Keanggotaan Grup Admin"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "Milik Grup Pribadi Admin"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "Antrian Admin"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "Pengguna Admin"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Cc Administrasi"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Lanjutan"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Setelah"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Kumpulan"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Semua Izin terlewati"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Semua Antrian"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "Dan/Atau"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Digunakan untuk"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Digunakan"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Digunakan untuk perubahan anda"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Izin"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Izin #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Izin #%1: Catatan yang tidak dicatat karena kesalahan sistem"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Izin #%1: Catatan yang dicatat"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Izin dilewati"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Izin ditolak"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Disetujui"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Catatan Pemberi Persetujuan: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Apr."
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Naik"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Menentukan dan memindahkan kolom kustom"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "Menentukan Kolom Kustom"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Melampirkan"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Melampirkan file"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "File dilampirkan"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Lampiran '%1' tidak dapat dipanggil"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Lampiran dibuat"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Nama File Lampiran"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Lampiran-lampiran"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Attribut sudah dihapus"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Agst."
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "Sistem Otorisasi"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Balas Otomatis"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Balas Otomatis ke Pemohon"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Available Columns"
+msgstr "Kolom Tersedia "
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Data buruk di dalam %1"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Dasar-dasar"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Pastikanlah untuk menyimpan setiap perubahan anda"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Sebelum"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Pemecahan Praktis yang terbaik, Logo bisnis LLC"
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "Binary"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Kosong"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Diberi huruf tebal"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Menyimpan penunjuk link"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Catatan Atas Singkat"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Memperbaharui tiket dengan jumlah banyak pada waktu yang sama"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Tidak dapat mengubah sistem pengguna"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Dapatkah prinsipal melihat antrian ini"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Tidak dapat menambah nilai kolom kustom tanpa nama"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "Tidak dapat menemukan kelas koleksi untuk '%'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Tidak dapat menemukan pencarian yang disimpan untuk bekerja dengan"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Tidak dapat menghubungkan tiket tersebut dengan dirinya sendiri"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Tidak dapat digabungkan ke dalam tiket gabungan. Anda seharusnya tidak pernah mendapatkan kesalahan ini."
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Tidak dapat menyimpan pencarian ini"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Tidak dapat menentukan baik dasar dan sasaran"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Tidak dapat membuat pengguna: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Ubah Sandi"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Periksa Semua"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Klik kotak ini untuk hapus"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Klik kotak ini untuk menghilangkan hak-hak"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Anak"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Kota"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Hapus Semua"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Ditutup"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Tiket sudah ditutup"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Komentar"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Alamat Komentar"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Komentar pada Tiket"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "Komentar Pada Tiket"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Komentar"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Komentar (Tidak dikirim ke pemohon)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Komentar (tidak dikirim ke pemohon)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Komentar mengenai %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Komentar tentang pengguna ini"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Komentar sudah ditambahkan"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Potongan Tiket Dijalankan"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Kondisi"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Kondisi sesuai dengan ..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Kondisi tidak ditemukan"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Konfigurasi"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Konfirmasi"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "Hubungi Sistem Info"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Isi"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Tipe - Isi"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Tiru"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Korespondensi"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Korespondensi sudah ditambah"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Tidak dapat menambah nilai kolom kustom baru untuk tiket."
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Tidak dapat menambah nilai kolom kustom baru"
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Tidak dapat menambah nilai kolom kustom baru"
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Tidak dapat mengubah pemilik tiket. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Tidak dapat membuat Kolom Kustom"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Tidak dapat membuat Kolom Kustom:%1 "
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Tidak dapat membuat grup"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Tidak dapat membuat template: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Tidak dapat membuat tiket. Antrian tidak dapat diatur"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Tidak dapat membuat pengguna"
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Tidak dapat menemukan atau membuat pengguna tersebut"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Tidak dapat menemukan prinsipal tersebut"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Tidak dapat memanggil KolomKustom %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Tidak dapat memanggil grup"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "Tidak dapat memanggil objek untuk %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Tidak dapat memanggil attribut pencarian"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Tidak dapat membuat prinsipal tersebut %1 untuk antrian ini"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Tidak dapat membuat prinsipal tersebut %1 untuk Tiket ini"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Tidak dapat menghapus prinsipal tersebut sebagai %1 untuk antrian ini"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Tidak dapat mengatur informasi pengguna"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Tidak dapat menambah anggota ke grup"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Tidak dapat membuat transaksi: %1"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Tidak dapat menemukan baris"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Tidak dapat menemukan prinsipal tersebut"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Tidak dapat menemukan nilai tersebut"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Tidak dapat memanggil %1 dari database pengguna.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "Tidak dapat memanggil Kelas %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Tidak dapat memanggil Kolom Kustom %1"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Tidak dapat memanggil grup %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Tidak dapat memanggil link"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Tidak dapat memanggil objek %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Tidak dapat memanggil antrian"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Tidak dapat memanggil antrian %1"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Tidak dapat memanggil pengguna tersebut (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Tidak dapat memanggil Tiket '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Negara"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Dibuat"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Buat Tiket"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Buat Kolom Kustom"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Buat Kolom Kustom untuk antrian %1"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Buat grup baru"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Buat grup pribadi baru"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Buat tiket baru"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Buat pengguna baru"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Buat antrian"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Buat scrip untuk antrian %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Buat template"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Buat Tiket"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Buat Tiket baru berdasarkan pada template scrip ini"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Buat Tiket"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Buat tiket untuk antrian ini"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Buat, hapus dan ubah kolom kustom"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Buat, hapus dan ubah antrian"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Buat, hapus dan ubah anggota dari grup pribadi"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Buat, hapus dan ubah pengguna"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "Buat Pencarian yang Disimpan"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "Buat Tiket"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "sudah Dibuat"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Kolom Kustom sudah dibuat %1"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Pencipta"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Link yang sekarang"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Relationships"
+msgstr "Hubungan Sekarang"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Scrips Sekarang "
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Anggota Sekarang"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Hak-hak Sekarang"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Kriteria pencarian sekarang"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Pengamat sekarang"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Kolom Kustom"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Kolom Kustom untuk %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Aksi kustom membersihkan kode"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Aksi kustom menyiapkan kode"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Kondisi kustom"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Kolom kustom %1 %2 %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 does not apply to this object"
+msgstr "Kolom kustom %1 tidak dapat digunakan untuk objek ini"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Kolom kustom %1 memiliki nilai."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Kolom kustom %1 tidak memiliki nilai."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Kolom kustom %1 tidak ditemukan"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Kolom kustom tidak ditemukan"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Nilai kolom kustom %1 tidak dapat ditemukan untuk kolom kustom %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Nilai kolom kustom tidak dapat dihapus"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Nilai kolom kustom tidak dapat ditemukan"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Nilai kolom kustom sudah dihapus"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "Kolom kustom"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Tanggal"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Des."
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Default template Balas Otomatis"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Default Antrian"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Default Pemohon"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Default template komentar admin"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Default template korespondensi admin"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Default template korespondensi"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Default template transaksi"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Default: %1/%2 diganti dari %3 ke %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Hak-hak Utusan"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Hak-hak spesifik utusan yang sudah diberikan kepada anda."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "Hak-hak Utusan"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Delegasi"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Hapus"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Hapus Template"
+
+#: NOT FOUND IN SOURCE
+msgid "Delete article #%1"
+msgstr "Hapus artikel #%1"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Gagal dihapus: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Hapus scrip yang sudah dipilih"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Hapus tiket"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "Hapus tiket"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Pencarian dihapus"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Menghapuskan objek ini dapat memutuskan integrasi referensial"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Menghapuskan objek ini dapat memutuskan integrasi referensial"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Menghapuskan objek ini dapat melanggar integrasi referensial"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Ditolak"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Bergantung oleh"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Ketergantungan oleh %1 sudah ditambah"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Ketergantungan oleh %1 sudah dihapus"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Ketergantungan oleh %1 sudah dihapus"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Ketergantungan oleh %1 sudah dihapus"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Bergantung pada"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Turun"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Gambarkan persoalan dibawah ini"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Deskripsi"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Tampilkan"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Tampilkan Daftar Kontrol Akses"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Tampilkan Kolom"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Tampilkan template Scrip untuk antrian ini"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Tampilkan Scrip untuk antrian ini"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Tampilkan mode"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Tampilkan pencarian yang disimpan untuk grup ini"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Didistribusikan dibawah versi 2 dari <a href=\"http://www.gnu.org/copyleft/gpl.html\"> GNU GPL.</a>"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Lakukan apa saja dan semuanya"
+
+#: NOT FOUND IN SOURCE
+msgid "Do the Search"
+msgstr "Lakukan Pencarian"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Jangan refresh halaman ini."
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Jangan tampilkan hasil pencarian"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Download"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Download sebagai file yang dibatasi dengan tab"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Batas Waktu"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "KESALAHAN: Tidak dapat memanggil Tiket '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Ubah"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Ubah Kolom Kustom untuk %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Ubah Kolom Kustom untuk semua grup"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Ubah Kolom Kustom untuk semua pengguna"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Ubah Kolom Kustom untuk tiket di dalam semua antrian"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Ubah Link"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Ubah Query"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Relationships"
+msgstr "Ubah Hubungan"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Ubah Templates untuk antrian %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Ubah pencarian yang disimpan untuk grup ini"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Ubah template sistem"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "Ubah Pencarian yang Disimpan"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Mengubah Konfigurasi untuk antrian %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Mengubah Konfigurasi untuk pengguna %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Mengubah Kolom Kustom %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Mengubah keanggotaan untuk grup ini %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Mengubah keanggotaan untuk grup pribadi %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Baik dasar ataupun sasaran harus ditentukan"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Email"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Alamat Email yang digunakan"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "Alamat Email"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "Set Karakter Email"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Mengaktifkan (Klik kotak ini untuk menon-aktifkan kolom kustom)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Mengaktifkan (Klik kotak ini untuk menon-aktifkan grup ini)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Mengaktifkan (Klik kotak ini untuk menon-aktifkan antrian ini)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Mengaktifkan Antrian"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Mengaktifkan status %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "Mengaktifkan status: %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Masukkan beberapa nilai"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Masukan objek atau URI untuk menghubungkan object tersebut. Pisahkan beberapa input tersebut dengan spasi."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Masukkan satu nilai"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Masukkan antrian atau URIs untuk menghubungkan antrian tersebut. Pisahkan beberapa input dengan spasi."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Masukkan tiket atau URIs untuk menghubungkan Tiket tersebut. Pisahkan beberapa input dengan spasi."
+
+#: NOT FOUND IN SOURCE
+msgid "Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces."
+msgstr "Masukkan tiket atau URIs untuk menghubungkan Tiket tersebut. Pisahkan beberapa input dengan spasi."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Masukkan sampai %1 nilai"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Kesalahan"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Kesalahan di dalam parameter ke Antrian->Tambah Pengamat"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Kesalahan di dalam parameter Ke Antrian->Hapus Pengamat"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Kesalahan di dalam parameter ke Antrian->Hapus Pengamat"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Kesalahan di dalam parameter ke Tiket->Tambah Pengamat"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Kesalahan di dalam parameter ke Tiket->Hapus Pengamat"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Kesalahan di dalam parameter ke Ticket->Hapus Pengamat"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Meningkatkan Prioritas Tiket"
+
+#: NOT FOUND IN SOURCE
+msgid "Estimate"
+msgstr "Perkiraan"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Diperkirakan"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Setiap Orang"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Contoh:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "AuthID Eksternal"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr " Kontak Info ID Eksternal"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Info Tambahan"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Gagal untuk membuat atribut pencarian"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Gagal untuk menemukan pengguna pseudogroup yang memiliki 'Hak-hak Istimewa'."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Gagal untuk menemukan pengguna pseudogroup yang tidak memiliki 'Hak-Hak Istimewa.'"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Gagal untuk memanggil modul %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "Gagal memanggil objek untuk %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Nama File"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Isilah di dalam beberapa area teks"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Isilah di dalam beberapa area wikiteks"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Isilah di dalam satu area teks"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Isilah di dalam satu area wikiteks"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Isilah sampai %1 area teks"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Isilah sampai %1 area wikiteks"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Prioritas Akhir"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "Prioritas Akhir"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Cari grup yang"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Cari orang yang"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Cari tiket"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Pertama"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Diubah dengan paksa"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Format"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Ditemukan % quant(%1,tiket)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Objek ditemukan"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "Freeform"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FreeformContactInfo"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Jum."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Catatan Atas Lengkap"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Ambil template dari file"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Berikan ke %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Kolom Kustom Global"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Konfigurasi kolom kustom global"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Template Global: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Ambil"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Ambil!"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Ambil tiket"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Grup"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Hak Grup"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Grup sudah memiliki anggota"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Grup tidak dapat dibuat: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Grup sudah dibuat"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Grup tidak memiliki anggota terseperti ini"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Grup tidak ditemukan"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Grup"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Grup tidak bisa menjadi anggota dari mereka"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Grup sesuai dengan kriteria pencarian"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Pengguna ini milik grup"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Halo!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Halo, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historis"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "Historis grup %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "Historis pengguna %1"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Telepon Rumah"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Homepage"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Saya memiliki %quant (%1,concrete mixer)."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Saya tersesat"
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Id"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identitas"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Jika izin ditolak, tolak izin yang asli dan hapus izin yang masih ditunda"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Jika tidak ada pemohon yang ditentukan, buat tiket dengan pemohon ini."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Jika tidak ada antrian yang ditentukan, buat tiket untuk antrian ini."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Jika alat ini adalah setgid, hostile local user dapat menggunakan alat ini untuk mendapatkan akses administrasi ke RT."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Jika anda memperbaharui apapun diatas, pastikanlah untuk"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Nilai Ilegal untuk %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Gambar"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Kolom yang tetap"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Termasuk menon-aktifkan grup di dalam daftar."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Termasuk menon-aktifkan antrian di dalam daftar."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Termasuk menon-aktifkan pengguna di dalam pencarian."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Query tidak lengkap"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "query tidak lengkap"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Prioritas Awal"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "Prioritas Awal"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Kesalahan Input"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Kesalahan Internal"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Kesalahan Internal: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Tipe Grup tidak valid"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Hak-hak tidak valid"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Data tidak valid"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Antrian tidak valid"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Hak tidak valid"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Nilai tidak valid untuk %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Nilai tidak valid untuk kolom kustom"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Nilai tidak valid untuk status"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Hal ini sangat penting bahwa pengguna yang tidak memiliki hak-hak istimewa tidak dapat diizinkan untuk menjalankan program ini."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Sangat disarankan bahwa anda membuat pengguna unix yang tidak memiliki hak-hak istimewa dengan keanggotaan grup yang benar dan akses RT untuk menjalankan alat ini."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Hal ini memerlukan beberapa penjelasan:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Diberi huruf miring"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Bergabung atau tinggalkan grup ini"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul."
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Jumbo"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun."
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Bhs"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Bahasa"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Besar"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Akhir"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Kontak Terakhir"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Kontak Terakhir"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Pemberitahuan Terakhir"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Pembaharuan Terakhir"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "Pembaharuan Terakhir Oleh"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Kirim"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Biarkan pengguna ini mengakses RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Biarlah pengguna ini diberi hak-hak"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Link"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Link sudah ada"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Link tidak dapat dibuat"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Link sudah dibuat (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Link sudah dihapus (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Link tidak ditemukan"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Link tiket #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Link-link"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Memanggil"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Memanggil pencarian yang disimpan:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "Memanggil pencarian yang disimpan"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Memanggil modul perl"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Memanggil pencarian %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Lokasi"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Direktori Log %1 tidak ditemukan atau tidak dapat ditulis.\\n RT tidak dapat dijalankan."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Masuk sebagai %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Login"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Logout"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Mencari tipe yang tidak sesuai (mismatch)"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Buat Pemilik"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Buat Status"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Buat Tanggal Batas Waktu"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Buat Tanggal Penyelesaian"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Buat Tanggal Mulai"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Buat Tanggal Mulai"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Buat Tanggal Pemberitahuan"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Buat prioritas"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Buat antrian"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Buat subjek"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Buat agar grup ini terlihat pada sisi pengguna"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Mengatur kolom kustom dan nilai kolom kustom"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Mengatur grup dan keanggotaan grup"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Mengatur propertis dan konfigurasi yang akan digunakan di setiap antrian"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Mengatur antrian dan propertis antrian tertentu"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Mengatur pengguna dan sandi"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Mei."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Anggota %1 sudah ditambah"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Anggota %1 sudah dihapus"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Anggota sudah ditambah"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Anggota sudah dihapus"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Anggota tidak dihapus"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Anggota dari"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Anggota-anggota"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "Keanggotaan di dalam %1 sudah ditambah"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "Keanggotaan di dalam %1 sudah dihapus"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Keanggotaan"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "Keanggotaan dari pengguna %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Penggabungan Berhasil"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Penggabungan gagal. Tidak dapat mengatur EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Penggabungan gagal. Tidak dapat mengatur Status"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Bergabung ke dalam"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Bergabung ke dalam %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Pesan"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Pesan tidak dapat dicatat"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Pesan sudah dicatat"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Pesan mengenai Tiket ini tidak akan dikirim ke..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "tanda kurung tidak sama"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Kunci utama tidak ditemukan?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Hp"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Handphone"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Mengubah Daftar Kontrol Akses"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Ubah Kolom Kustom yang digunakan untuk %1 untuk semua %2"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Mengatur Kolom Kustom yang dapat digunakan untuk seluruh %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Mengatur Kolom Kustom yang dapat digunakan di seluruh antrian"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Ubah Hak Grup"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Ubah Anggota"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Ubah Hak-hak"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Mengubah template Scrip untuk antrian ini"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Mengubah Scrip untuk antrian ini"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Ubah Hak Pengguna"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Mengubah Kolom Kustom untuk antrian %1"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Mengubah scrip untuk antrian %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Mengubah scrip yang dapat digunakan di seluruh antrian"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Mengubah objek-objek yang berhubungan untuk %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Mengubah tanggal untuk #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Mengubah tanggal untuk tiket # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Mengubah kolom kustom global"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Mengubah hak grup global"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Mengubah hak grup global."
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Mengubah hak pengguna global"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Mengubah hak pengguna global."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Mengubah grup metadata atau menghapus grup"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Mengubah hak grup untuk kolom kustom %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Mengubah hak grup untuk grup %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Mengubah hak grup untuk antrian %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Mengubah daftar nama keanggotaan untuk grup ini"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Mengubah pemilik account RT"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Mengubah orang-orang yang berhubungan dengan antrian %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Mengubah orang-orang yang berhubungan dengan tiket #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Mengubah scrip untuk antrian %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Mengubah scrip yang dapat digunakan di seluruh antrian"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Mengubah template %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Mengubah template yang dapat digunakan di seluruh antrian"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Mengubah grup %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Mengubah pengamat antrian"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Mengubah pengguna %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Mengubah tiket # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Mengubah tiket #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Mengubah tiket-tiket"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Mengubah hak pengguna untuk kolom kustom %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Mengubah hak pengguna untuk grup %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Mengubah hak pengguna untuk antrian %1"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "Mengubah ACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "Mengubah Kolom Kustom"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "Mengubah Milik Keanggotaan"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "Mengubah Pengamat Antrian"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "Mengubah Scrip"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "Mengubah Sendiri"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "Mengubah Template"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "Mengubah Tiket"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Sen."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Lebih mengenai %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Move"
+msgstr "Pindah"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Pindah ke bawah"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Pindah ke atas"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Beberapa"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Harus menentukan 'Nama' attribut"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "%1 tiketku"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Izinku"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Pencarian yang kusimpan"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Nama"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Nama yang digunakan"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Tidak pernah"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Baru"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Link Baru"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Sandi Baru"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Izin Baru yang Ditunda"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Query Baru"
+
+#: NOT FOUND IN SOURCE
+msgid "New Relationships"
+msgstr "Hubungan Baru"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr ""
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Kolom Kustom Baru"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Grup Baru"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Sandi Baru"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Kirim Pemberitahuan Sandi Baru"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Antrian Baru"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Hak-hak Baru"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Scrip Baru"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Template Baru"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Tiket Baru"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Tidak ada Tiket baru"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Pengguna Baru"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Pengguna Baru disebut"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Pengamat Baru"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Setting Window Baru"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Berikutnya"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Halaman Berikutnya"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Nama Panggilan"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Nama Panggilan"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Tidak ada Kelas yang didefinisikan"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Tidak ada Kolom Kustom"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Tidak ada Kolom Kustom yang didefinisikan"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Tidak ada Grup yang didefinisikan"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Tidak ada Query"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Tidak ada Antrian yang didefinisikan"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Tidak ada pengguna RT yang ditemukan. Silakan berkonsultasi dengan administrator RT anda.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Tidak ada Template"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Tidak ada Tiket yang ditentukan. Batalkan tiket "
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Tidak ada aksi"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Tidak ada kolom yang ditentukan"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Tidak ada komentar yang dimasukkan mengenai pengguna ini"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Tidak ada deskripsi untuk %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Tidak ada grup yang ditentukan"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Grup yang sesuai dengan kriteria pencarian tidak ditemukan."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Tidak ada pesan yang dilampirkan"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Tidak ada pengaturan Sandi"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Tidak ada izin untuk membuat antrian"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Tidak ada izin untuk membuat tiket ini di dalam antrian '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Tidak ada izin untuk membuat pengguna"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Tidak ada izin untuk menampilkan tiket tersebut"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Tidak ada izin untuk menampilkan tiket yang sudah diperbaharui"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Tidak ada prinsipal yang ditentukan"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Tidak ada prinsipal yang dipilih."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Antrian yang sesuai dengan kriteria pencarian tidak ditemukan."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Tidak ada hak-hak yang ditemukan"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Tidak ada hak-hak yang diberikan."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Tidak ada pencarian yang dipanggil"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Tidak ada pencarian untuk dijalankan di."
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Tidak ada subjek"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Tidak ada tipe transaksi yang ditentukan"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Pengguna yang sesuai dengan kriteria pencarian tidak ditemukan."
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Tidak ada pengguna RT yang valid ditemukan. RT cvs handler dilepaskan. Silakan berkonsultasi dengan administrator RT anda.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Tidak ada nilai yang dikirim ke _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Tidak seorangpun"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Kolom kosong?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Belum log in"
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Belum diatur"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Belum diimplementasikan."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Catatan"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Pemberitahuan tidak dapat dikirimkan"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Beritahu AdminCcs"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Beritahu AdminCcs dengan komentar"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Beritahu Ccs"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Beritahu Ccs dengan komentar"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Beritahu Penerima Lain"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Beritahu Penerima Lain dengan komentar"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Beritahu Pemilik"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Beritahu Pemilik dengan komentar"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Beritahu pemilik bahwa tiketnya ditolak"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Beritahu Pemilik bahwa tiketnya sudah disetujui oleh seluruh pemberi persetujuan"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Beritahu Pemilik bahwa tiketnya sudah disetujui oleh beberapa pemberi persetujuan"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Beritahu Pemilik dan AdminCcs mengenai izin item baru yang ditunda"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Beritahu Pemohon"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Beritahu Pemohon dan Ccs"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Beritahu Pemohon dan Ccs dengan komentar"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Beritahu Pemohon, Ccs dan AdminCcs"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Beritahu Pemohon, Ccs dan AdminCcs dengan komentar"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nop."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "ATAU"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objek tidak dapat dibuat"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Objek tidak dapat dihapus"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Object sudah dibuat"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objek sudah dihapus"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Tipe Objek %1 tidak dapat mengambil kolom kustom"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Tipe Objek tidak sesuai (mismatch)"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Okt."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Offline"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Ubah Offline"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Upload Offline"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Pada"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Pada Komentar"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Pada Hubungan"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Pada Pembuatan"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Pada Perubahan Pemilik"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Pada Perubahan Prioritas"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Pada Perubahan Antrian"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Pada Penyelesaian"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Pada Perubahan Status"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Pada Transaksi"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Hanya menampilkan izin untuk permintaan yang dibuat sesudah %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Hanya menampilkan izin untuk permintaan yang dibuat sebelum %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Buka"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Buka ini"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Buka tiket"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Buka tiket (dari daftar) di window yang baru"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Buka tiket (dari daftar) di window yang lain"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Buka tiket pada korespondensi"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Diurutkan sesuai"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Diurutkan dan disortir"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organisasi"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Tiket Awal: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Email yang keluar mengenai komentar yang dicatat"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Email keluar yang dicatat"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Lewat Batas waktu, prioritas pindah ke arah"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Memiliki tiket-tiket"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "Memiliki tiket"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Pemilik"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Pemilik tidak dapat diatur."
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Pemilik secara paksa digantikan dari %1 ke %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "Pemilik adalah"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Halaman %1 dari %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Pager"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Pager"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Induk "
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Sandi"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Pengingat Sandi"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Sandi sudah diubah"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "Minimal sandi memerlukan paling sedikit %1 karakter"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Sandi terlalu singkat"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Sandi: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Sandi tidak sesuai."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Sandi tidak sesuai. Sandi anda belum diganti"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Orang-Orang"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Menampilkan aksi pengguna yang belum didefinisikan"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Konfigurasi Perl"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Izin ditolak"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Grup Pribadi"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Grup pribadi"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Grup pribadi:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Nomor Telepon"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Pilihan"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Siapkan Potongan Tiket"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Sebelumnya"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Halaman Sebelumnya"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Prinsipal %1 tidak ditemukan."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioritas"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Prioritas dimulai pada"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Pribadi:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Hak-hak Istimewa"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Status Hak-hak Istimewa: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Pengguna Hak-hak Istimewa"
+
+#: NOT FOUND IN SOURCE
+msgid "Projects"
+msgstr "Proyek-Proyek"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudogroup digunakan untuk internal"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "Query"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Pembuat Query"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Antrian"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Antrian %1 tidak ditemukan"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Nama Antrian"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Antrian sudah ada"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Antrian tidak dapat dibuat"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Antrian tidak dapat dipanggil."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Antrian sudah dibuat"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Antrian tidak dapat ditemukan"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Antrian-antrian"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Pencarian Cepat"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Pembuatan Tiket Cepat"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 untuk %2"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Administrasi RT"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "Kesalahan RT"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "Variable RT "
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "Sekilas RT"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT tidak dapat menyimpan sesi anda"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT untuk %1"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Nama Asli"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "Nama Asli"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "Referensi oleh %1 sudah ditambah"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "Referensi oleh %1 sudah dihapus"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "Referensi ke %1 sudah ditambah"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "Referensi ke %1 sudah dihapus"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Diserahkan oleh"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Diserahkan ke"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Membersihkan pencarian"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Refresh halaman ini setiap %1 menit."
+
+#: NOT FOUND IN SOURCE
+msgid "Relationships"
+msgstr "Hubungan"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Hapus AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Hapus Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Hapus Pemohon"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Balas"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Balas ke Alamat"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Balas ke Pemohon"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Balas ke tiket"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "Balas ke Tiket"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Pemohon"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Pemohon"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "permintaan akan melewati batas waktu dalam"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Parameter yang diperlukan '%1' tidak didefinisikan"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Ulangi"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Tempat Tinggal"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Penyelesaian"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Penyelesaian tiket #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Sudah Diselesaikan"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Balas ke pemohon"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Hasil"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Hasil per halaman"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Ketik Ulang Sandi"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Kembali pada"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Hak-hak Utusan"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Memberikan Hak-hak"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Memanggil Hak-hak"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Hak-hak tidak dapat dihilangkan"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Hak-hak tidak ditemukan"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Hak-hak tidak dapat dipanggil."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Hak-hak tidak dapat dihilangkan"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Hak-hak"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Hak-hak tidak dapat diberikan untuk %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Hak-hak tidak dapat dihilangkan untuk %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Aturan"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Baris per halaman"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sab."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Simpan"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Simpan Perubahan"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Simpan Pilihan"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Simpan perubahaan"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "Simpan pencarian %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Simpan pencarian"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip sudah dibuat"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Kolom Scrip"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip sudah dihapus"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrip"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrip yang digunakan di seluruh antrian"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Pencarian"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Gagal memanggil atribut pencarian"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Pencarian untuk izin"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Perbaharui Pencarian: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Searches can't be associated with that kind of object"
+msgstr "Pencarian tidak dapat dihubungkan dengan objek yang seperti ini"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Keamanan:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Lihat kolom kustom"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Lihat pesan email yang keluar dan semua penerimanya dengan tepat"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Lihat Penjelasan Tiket Pribadi"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Lihat Ringkasan Tiket"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "Lihat Kolom Kustom"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "Lihat Grup"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "Lihat Antrian"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "Pilih"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Pilih Kolom Kustom"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Pilih grup"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Pilih antrian"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Pilih antrian untuk tiket baru anda"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Pilih Pengguna"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Pilih kolom kustom"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Pilih kolom kustom untuk seluruh grup pengguna"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Pilih kolom kustom untuk seluruh pengguna"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Pilih kolom kustom untuk tiket di seluruh antrian"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Pilih kolom kustom untuk transaksi pada tiket di dalam seluruh antrian"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Pilih grup"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Pilih beberapa nilai"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Pilih satu nilai"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Pilih antrian"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Pilih scrip"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Pilih template"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Pilih sampai %1 nilai"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Pilih pengguna"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Kolom Kustom yang dipilih"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Objek yang dipilih"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Kirim pesan ke seluruh pengamat"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Kirim pesan ke seluruh pengamat dengan \"komentar\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Kirim pesan ke pemohon dan Ccs"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Kirim pesan ke pemohon dan Ccs dengan komentar"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Kirim pesan ke pemohon"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Kirim pesan ke Ccs and Bccs yang terdaftar dengan jelas"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Kirim pesan ke Ccs"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Kirim pesan ke Ccs dengan komentar"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Kirim pesan ke Ccs administrasi"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Kirim pesan ke Ccs administrasi dengan komentar"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Kirim pesan ke pemilik"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Tampilkan"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Tampilkan Izin"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Tampilkan Kolom"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Tampilkan Hasil"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Tamplikan permintaan yang disetujui"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Tampilkan dasar-dasar"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Tampilkan permintaan yang ditolak"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Tampilkan detil"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Tampilkan permintaan yang ditunda"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Tampilkan permintaan izin lain yang ditunggu"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "Tampilkan ACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "Tampilkan Tab Config"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "Tampilkan Email yang Keluar"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "Tampilkan Pencarian yang Disimpan"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "Tampilkan Scrip"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "Tampilkan Template"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "Tampilkan Tiket"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "Tampilkan Komentar Tiket"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Daftar sebagai pemohon tiket atau tiket atau antrian Cc"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Daftar sebagai tiket atau antrian AdminCc"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Tanda Tangan"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Tunggal"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Ukuran"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Lewati Menu"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Kecil"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sortir"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Sortir hasil dengan"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "Tingkat"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Sudah dimulai"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Mulai"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Negara"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Status"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Status Diubah"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Status diubah dari %1 ke %2"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Curi"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Curi tiket"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "Curi Tiket"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Dicuri dari %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Dicuri dari %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Gaya"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Subjek"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Subjek diganti menjadi %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Submit"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Berhasil"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Min."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "PenggunaSuper"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Sistem"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Konfigurasi Sistem"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Kesalahan Sistem"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "Kesalahan sistem: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Peralatan Sistem"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Kesalahan sistem. Hak-hak tidak dapat diserahkan."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Kesalahan sistem. Hak-hak tidak dapat diberikan."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Grup Sistem"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr " GrupAturanSistem yang digunakan untuk internal"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TES_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Ambil"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Ambil tiket"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "Ambil Tiket"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Diambil"
+
+#: NOT FOUND IN SOURCE
+msgid "Task"
+msgstr "Tugas"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Template"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Template #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Template sudah dihapus"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Template tidak ditemukan"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Template parsed"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Template-template"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "Teks"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Ini adalah nilai yang sekarang"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Ini bukan nilai untuk kolom kustom ini"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Ini adalah nilai yang sama"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Prinsipal ini sudah memiliki hak-hak tersebut"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Prinsipal ini sudah %1 untuk antrian ini"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Prinsipal ini sudah %1 untuk tiket ini"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Prinsipal ini bukan %1 untuk antrian ini"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Antrian ini tidak ada"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Tiket ini mempunyai ketergantungan yang belum dapat diselesaikan"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Pengguna ini sudah memiliki tiket tersebut"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Pengguna ini tidak ada"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Pengguna ini sudah memiliki hak-hak istimewa"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Pengguna ini sudah tidak memiliki hak-hak istimewa"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Pengguna tersebut sekarang memiliki hak-hak istimewa"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Pengguna tersebut sekarang tidak memiliki hak-hak istimewa"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Pengguna tersebut mungkin tidak memiliki tiket di dalam antrian tersebut"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Ini bukan menurut angka ID"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Dasar-dasar"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "CC tiket"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "CC tiket administrasi"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Perintah berikut ini akan menemukan seluruh tiket yang aktif di dalam antrian 'umum' dan mengatur prioritas mereka ke 99 jika mereka tidak digunakan dalam waktu 4 jam:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "Perintah berikut ini tidak akan dijalankan:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Nilai baru sudah diatur."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Pemilik tiket"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Pemohon tiket"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Komentar ini umumnya tidak jelas bagi pengguna"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Kolom kustom ini tidak dapat digunakan pada objek tersebut"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Fitur ini hanya tersedia untuk sistem administrator"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Pesan ini akan dikirim ke..."
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Alat ini mengijinkan pengguna untuk menjalankan modul perl yang berubah-ubah dari dalam RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Transaksi ini tampaknya tidak memiliki isi"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Pengguna ini memiliki %1 tiket dengan prioritas tertinggi"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Kms."
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Tiket #%1 Perbaharui Jumbo: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Tiket #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "Tiket %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Tiket %1 sudah dibuat di dalam antrian '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Tiket %1 sudah dipanggil\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Tiket %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Tiket Kolom Kustom"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Historis Tiket # %1 %2"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Tiket sudah diselesaikan"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Transaksi Tiket"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Lampiran Tiket"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Isi Tiket"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Tipe isi Tiket"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Tiket tidak dapat dibuat karena ada kesalahan internal"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Metadata Tiket"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Status tiket sudah diubah"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "Pencarian modul Tiket SQL"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Tiket-tiket"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Tiket %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Tiket %1 oleh %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Tiket dari %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Tiket yang bergantung dengan izin ini:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Perkiraan Waktu"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Sisa Waktu"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Waktu Kerja"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Sisa waktu"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Waktu untuk ditampilkan"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Waktu kerja"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "Waktu Kerja"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Judul"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "To generate a diff of this commit:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "To generate a diff of this commit:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Untuk menanyakan tentang bantuan, latihan, perkembangan kustom atau izin, silakan hubungi %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Memberitahukan"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "alat-alat"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transaksi"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transaksi %1 sudah dibersihkan"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transaksi sudah dibuat"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Kolom Kustom Transaksi"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transaksi->Tidak dapat dibuat, karena anda tidak menentukan tipe objek dan id"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transaksi yang tetap"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Sel."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Tipe"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Belum diimplementasikan"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Login Unix"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Nama Pengguna Unix"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Content Encoding tidak dikenali%1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Kolom tidak dikenali: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Tidak terbatas"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Pencarian tidak dikenal"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Tidak memiliki hak-hak istimewa"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Kolom Kustom tidak dipilih"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Objek tidak dipilih"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Belum diambil"
+
+#: NOT FOUND IN SOURCE
+msgid "Untitled search"
+msgstr "Pencarian yang tidak memiliki judul"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Perbaharui"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Perbahurui Semua"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Perbaharui ID"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Perbaharui Tiket"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Perbaharui Tipe"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Perbaharui seluruh tiket ini pada saat yang sama"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Perbaharui email"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Perbaharui beberapa tiket"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Perbaharui nama"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Perbaharui tidak dicatat."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Perbaharui tiket yang terpilih"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Perbaharui tanda tangan"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Perbaharui tiket"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Perbaharui tiket #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Perbaharui tiket #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr " Memperbarui tipe baik korespondensi ataupun penjelasan."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Sudah diperbaharui"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Upload"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Upload beberapa file"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Upload beberapa gambar"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Upload satu file"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Upload satu gambar"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Upload sampai %1 file"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Upload sampai %1 gambar"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Upload perubahan anda"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Gunakan alat administrative RT yang lain"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "Pengguna '%1' tidak dapat ditemukan."
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Pengguna didefinisikan"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Pengguna mendefinisikan kondisi dan aksi"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "ID Pengguna"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Hak Pengguna"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "Pengguna akan ditanya mengenai tipe pembaharuan yang tidak dikenal untuk kolom kustom %1 untuk %2 objek #%3"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Pengguna tidak dapat dibuat: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Pengguna sudah dibuat"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Pengguna mendefinisikan grup"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Pengguna sudah dipanggil"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Lihat Pengguna"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Definisi Grup Pengguna"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Nama Pengguna"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Pengguna"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Pengguna sesuai dengan kriteria pencarian"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Query yang valid"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Nilai-nilai"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Amati"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "Amati Sebagai AdminCc"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Pengamat"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "WebEncoding"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Rabu."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Kalau tiket sudah disetujui oleh semua pemberi persetujuan, tambahkan korespondesi ke tiket yang asli"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Kalau tiket sudah disetujui oleh beberapa pemberi persetujuan, tambahkan korespondensi ke tiket yang asli"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Kalau tiket sudah dibuat"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Kalau tiket persetujuan sudah dibuat, beritahukan pemilik dan AdminCc dari item yang sedang ditunggu persetujuannya"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Kalau sesuatu terjadi"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Setiap saat tiket dapat diselesaikan"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Setiap saat pemilik tiket dapat diubah"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Setiap saat prioritas tiket dapat diubah"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Setiap saat antrian tiket dapat diubah"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Setiap saat status tiket dapat diubah"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Setiap saat kondisi yang didefinisikan oleh pengguna terjadi"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Setiap saat komentar dapat masuk"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Setiap saat korespondensi dapat masuk"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Bekerja"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Bekerja offline"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Telepon Kantor"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Sedang Bekerja"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Anda sudah memiliki tiket ini"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Anda bukanlah pengguna yang sah"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Anda hanya bisa menugaskan kembali tiket yang anda miliki atau yang belum anda miliki"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Anda menemukan %1 tiket di dalam antrian ini %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Anda sudah log out dari RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Anda tidak memiliki izin untuk membuat tiket di dalam antrian tersebut."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Anda mungkin tidak akan membuat permintaan di dalam antrian ini."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Anda dapat login kembali"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Permintaan anda sudah disetujui oleh. Persetujuan yang lain mungkin masih ditunda."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Permintaan anda sudah disetujui."
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Permintaan anda sudah ditolak."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Nama pengguna dan sandi anda tidak sama"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Zip"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "mengijinkan pembuatan pencarian yang disimpan"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "mengijinkan memanggil pencarian yang disimpan"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "sudah diberikan ke %1"
+
+#: NOT FOUND IN SOURCE
+msgid "belongs to"
+msgstr "Milik"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "sudah ditutup"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "berisi"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "hari"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "sudah dihapus"
+
+#: NOT FOUND IN SOURCE
+msgid "does not belong to"
+msgstr "Bukan milik"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "tidak sama"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "tidak berisi"
+
+#: NOT FOUND IN SOURCE
+msgid "email address"
+msgstr "alamat email"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "sama dengan"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "kesalahan: tidak dapat dipindahkan ke bawah"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "kesalahan: tidak dapat dipindahkan ke kiri"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "kesalahan: tidak dapat dipindahkan ke atas"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "kesalahan: tidak ada apapun yang dapat dihapus"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "kesalahan: tidak ada apapun yang dapat dipindahkan"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "kesalahan: tidak ada apapun yang dapat dihubungkan"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "lebih dari"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "grup '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "jam"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "id"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "adalah"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "tidak"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "kurang dari"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "sama"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "mnt"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "menit"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "perubahan\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "bulan"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "baru"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "tidak ada nama"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "tidak ada nilai"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "tidak satupun"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "tidak sama dengan"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "buka"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "grup pribadi '%1' untuk pengguna '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "antrian %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "sudah ditolak"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "sudah diselesaikan"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "dtk"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "tampilkan tab konfigurasi"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "lembar kerja"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "alasan"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "sistem %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "grup sistem '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "Pemanggilan komponent tidak menetapkan mengapa"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "tiket #%1 %2"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "grup yang belum dijelaskan %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "pengguna %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "minggu"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "tahun"
+
diff --git a/rt/lib/RT/I18N/it.po b/rt/lib/RT/I18N/it.po
new file mode 100644
index 0000000..13cbc9b
--- /dev/null
+++ b/rt/lib/RT/I18N/it.po
@@ -0,0 +1,6655 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2002-05-02 11:36+0800\n"
+"PO-Revision-Date: 2005-10-03 13:48-0400\n"
+"Last-Translator: Angelo Turetta <aturetta@bestunion.it>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "n°"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "n°%1"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "n°%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 n°%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %3 %2 %7 %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "Aggiunto %1 %2"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 fa"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 cambiato in %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 eliminato"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 of group %3"
+msgstr "%1 %2 del gruppo %3"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 con il modello %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 questo ticket\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) da %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (invariato)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "Mostrati da %1 a %2"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Un parametro da passare a %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Visualizza aggiornamenti dello stato su STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Specificare l'azione che si vuole eseguire"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Specificare la condizione che si vuole utilizzare"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Specificare la ricerca che si vuole utilizzare"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScripAction %1 caricato"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 aggiunto come valore di %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "gli alias %1 necessitano di un TicketId su cui lavorare"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "gli alias %1 necessitano di un TicketId su cui lavorare"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "gli alias %1 necessitano di un TicketId per lavorare con (da %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 sembra essere un oggetto locale, ma non è presente nel database"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 da %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 cambiato da %2 a %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "copia di %1"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 non può essere impostato a %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 non ha potuto iniziare una transazione (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 non ho potuto mettere lo stato a risolto. Il database RT potrebbe essere corrotto."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "creato %1"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "eliminato %1"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "I %1 ticket a maggior priorità che ho in carico"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "I %1 ticket a maggior priorità che ho in carico..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "I %1 ticket a maggior priorità che ho richiesto..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 è uno strumento per agire sui ticket da uno schedulatore esterno, per esempio cron."
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 non è più un %2 per questa coda."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 non è più un %2 per questo ticket."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 non è più un valore per il campo personalizzato %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 non è un identificativo di coda valido"
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "I %1 ticket più recenti non ancora assegnati"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 non mostrato"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 oggetti"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "Diritti su %1"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 riuscito\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "Tipo %1 sconosciuto per $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "Tipo %1 sconosciuto per %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 was created without a CurrentUser\\n"
+msgstr "%1 è stato creato senza un CurrentUser\\n"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 risolverà tutti i membri di un gruppo di ticket risolto."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 bloccherà una BASE [locale] se dipende da [o è membro di] una richiesta collegata."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%2 oggetti di %1"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%3 oggetti di %2 di %1"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "ricerche salvate di %1"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: nessun allegato specificato"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' è uno stato non valido"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' non è un'azione conosciuta. "
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(spunta la casella per rimuovere un appartenente dal gruppo)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(spunta la casella per cancellare uno scrip)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(spunta la casella per cancellare)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(spunta le caselle per cancellare)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(i destinatari con la casella spuntata non ricevono la notifica)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(solo i destinatari con la casella spuntata ricevono la notifica)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(inserire i numeri o gli URL dei ticket, separati da spazi)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Se non specificato usa: %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(nessun valore)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(nessun campo personalizzato)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(nessun appartenente)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(nessuno Scrip)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(nessun modello)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(nessuno)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(invia copia silente di questo aggiornamento ad una lista di indirizzi email separati da virgole. Ciò <b>non cambierà</b> i destinatari dei successivi aggiornamenti.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(invia copia silente di questo aggiornamento ad una lista di indirizzi email separati da virgole. Ciò <b>non cambierò</b> i destinatari dei successivi aggiornamenti.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(invia copia per conoscenza di questo aggiornamento ad una lista di indirizzi email amministrativi separati da virgole. Queste persone <b>riceveranno</b> i successivi aggiornamenti.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(invia copia per conoscenza di questo aggiornamento ad una lista di indirizzi email separati da virgole. Ciò <b>non cambierà</b> i destinatari dei successivi aggiornamenti.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(invia copia per conoscenza di questo aggiornamento ad una lista di indirizzi email separati da virgole. Ciò <b>non cambierà</b> i destinatari dei successivi aggiornamenti.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(invia copia per conoscenza di questo aggiornamento ad una lista di indirizzi email separati da virgole. Queste persone <b>riceveranno</b> i successivi aggiornamenti.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(usa questi campi se si sceglie 'Definito dall'utente' come condizione o azione)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(vuoto)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(nessun nome in elenco)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(nessun oggetto)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(nessun valore)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(nessun valore)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(un solo ticket)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(in attesa di approvazione)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(in attesa di un'altra Raccolta)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other ticket)"
+msgstr "(in attesa di altri ticket)"
+
+#: NOT FOUND IN SOURCE
+msgid "(requestor's group)"
+msgstr "(gruppo del richiedente)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(richiesto)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(senza titolo)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "I 25 ticket con priorità più alta che ho in carico..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "I 25 ticket con priorità più alta da me richiesti..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr ""
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr ""
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Crea un ticket in\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Un modello vuoto"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Deleted"
+msgstr "ACE eliminata"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Loaded"
+msgstr "ACE caricata"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be deleted"
+msgstr "impossibile eliminare l'ACE"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be found"
+msgstr "impossibile trovare l'ACE"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE non trovata"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "Le ACE possono essere solo create e cancellate."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Interruzione per evitare modifiche di ticket involontarie.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Informazioni personali"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Controllo di Accesso"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Azione"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Azione %1 non trovata"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Azione eseguita."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Azione eseguita.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Azione preparata..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Aggiungi"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Aggiungi AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Aggiungi Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Aggiungi colonne"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Aggiungi criterio"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Aggiungi altri file"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Next State"
+msgstr "Aggiungi prossimo stato"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Aggiungi il richiedente"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Aggiungi un valore"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a keyword selection to this queue"
+msgstr "Aggiungi una selezione di parole chiave a questa coda"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Aggiungi un nuovo scrip globale"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Aggiungi uno scrip a questa coda"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Aggiungi uno scrip da applicare a tutte le code"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "Aggiungi criteri addizionali"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Aggiungere commenti o repliche ai ticket selezionati"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Aggiungi appartenenti"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Aggiungi nuovi osservatori"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Aggiungi, cancella o modifica il valore dei campi personalizzati per gli oggetti"
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "AggiunguProssimoStato"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Aggiunto gruppo/utente come %1 per questa coda"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Aggiunto gruppo/utente come %1 per questo ticket"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Indirizzo1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Indirizzo2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Cc amministrativo"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Commento Amministrativo"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Corrispondenza Amministrativa"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Gestione delle code"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Gestione degli utenti"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Amministra/Configurazione globale"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Gestione/Gruppi"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Gestione/Code/Base"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "GestisciTuttiGruppiPersonali"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "GestisciCommenti"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "GestisciCorrispondenza"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "GestisciCampoPers."
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "GestisciCampiPers."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "GestisciGruppo"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "GestisciAppartenenzaGruppo"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "GestisciGruppiPersonali"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "GestisciCoda"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "GestisciUtenti"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "Admins"
+msgstr "Amministratori"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Avanzata"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Ricerca avanzata"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "dopo il"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Età"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Aggregatore"
+
+#: NOT FOUND IN SOURCE
+msgid "Alias for"
+msgstr "Alias per"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Ottenute tutte le approvazioni"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Tutti i campi personalizzati"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Tutte le code"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Invia comunque un messaggio al richiedente, anche se è lui stesso il mittente"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Si applica a"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Applica"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Applica modifiche"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Approvazione"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Approvazione n°%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Approvazione n°%1: Note non registrate a causa di un errore di sistema"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Approvazione n°%1: Note registrate"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Dettagli dell'approvazione"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Approvazione ottenuta"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Approvazione rifiutata"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Diagramma dell'approvazione"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Approva"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Note dell'approvatore: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Apr."
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "Aprile"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Crescente"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Assegna e rimuovi campi personalizzati"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "AssegnaCampiPers."
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Allega"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Allega un file"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "File allegato"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Impossibile caricare l'allegato '%1'"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Allegato creato"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Nome file dell'allegato"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Allegati"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Attributo cancellato"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Ago."
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "Agosto"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "SistemaAutorizzazioni"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "RispostaAutomatica"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Risposta automatica ai richiedenti"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "RispostaAutomaticaAiRichiedenti"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Firma PGP invalida: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Identificativo allegato errato. Impossibile trovare l'allegato '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Dati incorretti in %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Numero di transazione incorretto per l'allegato. %1 dovrebbe essere %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Dati base"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Se hai aggiornato qualche impostazione, assicurati di premere"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "prima del"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "Inizia approvazione"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Logo Best Practical Solutions, LLC"
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "Binario"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Vuoto"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Grassetto"
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "URL assoluto per questa ricerca"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Link assoluto"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Intestazione breve"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Modifica di ticket in blocco"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Gli utenti di sistema non possono essere modificati"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Può il gruppo/utente vedere questa coda"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Impossibile aggiungere un valore di campo personalizzato senza un nome"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "Impossibile trovare una classe collection per '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Impossibile trovare una ricerca salvata su cui lavorare"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Non è possibile collegare un ticket a se stesso"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Impossibile unire un ticket ad un ticket già unito. Questo errore non dovrebbe mai verificarsi"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Impossibile salvare questa ricerca"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Impossibile specificare sia la base che il target"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Impossibile creare l'utente: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Cambia la password"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Spunta tutte le caselle"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Spunta la casella per cancellare"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Spunta la casella per revocare i diritti"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Derivati"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Città"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Azzera"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Chiuso"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "Ticket chiusi"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Richieste chiuse"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Ticket chiusi"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Comando non riconosciuto!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Commento"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Inidirizzo per i commenti"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Commento non registrato"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Aggiunta di commenti ai ticket"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "AggiungiCommentiAlTicket"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Commenti"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Commenti (non inviati ai richiedenti)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Commenti (non inviati ai richiedenti)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Commenti su %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Commenti su questo utente"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Aggiunto commento"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Restrizioni di compilazione"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Condizione"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "La condizione è soddisfatta..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Condizione non trovata"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Configurazione"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Conferma"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "ContactInfoSystem"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Impossibile interpretare la data di contatto '%1'"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Contenuto"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Coould not create group"
+msgstr "Impossibile creare il gruppo"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Copia"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Corrispondenza"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Inidirizzo di corrispondenza"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Aggiunta corrispondenza"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Corrispondenza non registrata"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Impossibile aggiungere un nuovo valore di campo personalizzato al ticket. "
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "Impossibile aggiungere un nuovo valore di campo personalizzato al ticket. %1 "
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Impossibile aggiungere un nuovo valore di campo personalizzato. "
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Impossibile aggiungere un nuovo valore di campo personalizzato. %1 "
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Impossibile cambiare l'incaricato. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Impossibile creare il campo personalizzato"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Impossibile creare il campo personalizzato: %1"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Impossibile creare il gruppo"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Impossibile creare il modello: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Impossibile creare il ticket. Queue non impostata"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Impossibile creare l'utente"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "Impossibile creare l'osservatore per il richiedente"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Impossibile trovare il ticket numero %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Impossibile trovare il gruppo %1."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Impossibile trovare o creare questo utente"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Impossibile trovare questo gruppo/utente"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Impossibile trovare l'utente %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Impossibile caricare il campo personalizzato %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Impossibile caricare questo gruppo"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "Impossibile caricare l'oggetto per %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Impossibile caricare un attributo della ricerca"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Impossibile rendere questo gruppo/utente un %1 per questa coda"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Impossibile rendere questo gruppo/utente un %1 per questo ticket"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Impossibile rimuovere questo gruppo/utente come un %1 per questa coda"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Impossibile rimuovere questo gruppo/utente come un %1 per questo ticket"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Impossibile impostare le informazioni dell'utente"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Impossibile aggiungere un appartenente al gruppo"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Impossibile creare una transazione: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Impossibile capire che cosa fare con questa risposta gpg\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Impossibile trovare il gruppo\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Impossibile trovare la riga"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Impossibile trovare questo gruppo/utente"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Impossibile trovare questo valore"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "Impossibile trovare questo osservatore"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Impossibile trovare l'utente\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Impossibile caricare %1 dal database degli utenti.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "Impossibile caricare la classe %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Impossibile caricare ilcampo personalizzato %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load KeywordSelects."
+msgstr "Impossibile caricare KeywordSelects"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "Impossibile caricare il file di configurazione RT '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Impossibile caricare gli Scrips"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Impossibile caricare il gruppo %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Impossibile caricare il collegamento"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Impossibile caricare l'oggetto %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Impossibile caricare la coda"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Impossibile caricare la coda %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Impossibile caricare lo Scrip"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Impossibile caricare il modello"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Impossibile caricare questo utente (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Impossibile caricare il ticket '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Nazione"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Crea"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Crea ticket"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Crea un campo Personalizzato"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Crea un campo personalizzato per la coda %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Crea un campo personalizzato valido per tutte le code"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Crea un nuovo campo personalizzato"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Crea un nuovo scrip globale"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Crea un nuovo gruppo"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Crea un nuovo gruppo personale"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Crea una nuova coda"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Crea un nuovo scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Crea un nuovo modello"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Crea un nuovo ticket"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Crea un nuovo utente"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Crea una coda"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Crea una nuova coda di nome"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Crea una richiesta"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Crea uno scrip per la coda %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Crea un modello"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Nuovo ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "Creazione fallita: %1 / %2 / %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "Creazione fallita: %1/%2/%3"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Creare nuovi ticket basati su questo modello di scrip"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Crea il ticket"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Crea nuovi ticket in questa coda"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Crea, elimina e modifica campi personalizzati"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Crea, elimina e modifica le code"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Crea, elimina e modifica gli appartenenti ai gruppi personali di un qualunque utente"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Crea, elimina e modifica gli appartenenti ai gruppi personali "
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Crea, elimina e modifica gli utenti"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "CreaRichiestaSalvata"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "CreaTicket"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Creato"
+
+#: NOT FOUND IN SOURCE
+msgid "Created By"
+msgstr "Creato da"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Campo personalizzato %1 creato"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Modello %1 creato"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "CreatedBy"
+msgstr "CreatoDa"
+
+#: NOT FOUND IN SOURCE
+msgid "CreatedRelative"
+msgstr "TempoDaCreazione"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "CreatoDa"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Collegamenti attuali"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Scrips attuali"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Appartenenti attuali"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Diritti attuali"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Criterio di ricerca attuale"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Osservatori attuali"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Campo personalizzato n°%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Campi personalizzati"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Campi personalizzati per %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Programma di pulizia dell'azione personalizzata"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Programma di preparazione dell'azione personalizzata"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Condizione personalizzata"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Campi personalizzati %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Il campo personalizzato %1 ha un valore."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Il campo personalizzato %1 non ha valore."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Campo personalizzato %1 non trovato"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Campo personalizzato cancellato"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Campo personalizzato %1 non trovato"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Impossibile trovare il valore %1 per il campo personalizzato %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Valore del campo personalizzato cambiato da %1 a %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Impossibile cancellare il valore del campo personalizzato"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Valore del campo personalizzato non trovato"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Il valore del vampo personalizzato è stato eliminato"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "CampoPersonalizzato"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "Errore nei dati"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Date"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Dic."
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "Dicembre"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Modello di default per la risposta automatica"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Modello di default per la risposta automatica"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Coda di default"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Richiedente di default"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Modello di default per il commento amministrativo"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Modello di default per la corrispondenza amministrativa"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Modello di default per la corrispondenza"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Modello di default per la transazione"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Default: %1/%2 modificato da %3 a %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Delega i diritti"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Delega diritti specifici tra quelli che ti sono stati accordati"
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "DelegaDiritti"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Delega"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Cancella"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Cancella modello"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Cancellazione fallita: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Cancella gli scrips selezionati"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Cancella ticket"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "CancellaTicket"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Ricerca cancellata"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "La cancellazione di quest'oggetto può danneggiare l'integrità referenziale"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "La cancellazione di quest'oggetto danneggerebbe l'integrità referenziale"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "La cancellazione di quest'oggetto violerebbe l'integrità referenziale"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "La cancellazione di quest'oggetto violerebbe l'integrità referenziale"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity. That's bad."
+msgstr "La cancellazione di quest'oggetto violerebbe l'integrità referenziale. Ciò è male!"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Nega"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "È una dipendenza per"
+
+#: NOT FOUND IN SOURCE
+msgid "DependedOnBy"
+msgstr "DipendenzaPer"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Dipendenze: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Aggiunta dipendenza da parte di %1"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Cancellata dipendenza da parte di %1"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Aggiunta dipendenza da %1"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Cancellata dipendenza da %1"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Dipende da"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "DipendeDa"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Decrescente"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Descrivi il problema qui sotto"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Descrizione"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Dettagli"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Visualizza"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Visualizza la Lista Controllo Accessi (ACL)"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Visualizza colonne"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Visualizza i modelli di Scrips per questa coda"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Visualizza gli Scrips per questa coda"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Modalità visualizzazione"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Visualizza le ricerche salvate per questo gruppo"
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Visualizza il ticket n°%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Distribuito in accordo con la versione 2 della <a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU GPL</a>."
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Fai di tutto e di più"
+
+#: NOT FOUND IN SOURCE
+msgid "Do the Search"
+msgstr "Esegui la ricerca"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Non aggiornare questa pagina."
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Non mostrare i risultati della ricerca"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Download"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Scarica come file separato da TAB"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Scadenza"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Impossibile interpretare la data di scadenza '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "DueRelative"
+msgstr "TempoDaScadenza"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "ERRORE: impossibile caricare il ticket '%1' : %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Modifica"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Conditions"
+msgstr "Modifica condizioni"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Modifica i campi personalizzati per %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Modifica i campi personalizzati per tutti i gruppi"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Modifica i campi personalizzati per tutti gli utenti"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Modifica i campi personalizzati per tutte le code"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Custom Fields for transactions on tickets in all queues"
+msgstr "Modifica i campi personalizzati per le transazioni sui ticket in tutte le code"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Modifica collegamenti"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Modifica query"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Modifica i modelli per la coda %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "Modifica parole chiave"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Modifica ricerche salvate per questo gruppo"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Modifica scrips"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Modifca i modelli di sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Modifica i modelli per %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "ModificaRicercheSalvate"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Modifica della configurazione per la coda %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Modifica della configurazione per l'utente %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Modifica del campo personalizzato %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Modifica degli appartenenti al gruppo %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Modifica degli appartenenti al gruppo personale %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Modifica del modello %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "È necessario specificare base o target"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Email"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Inidirizzo email in uso"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "IndirizzoEmail"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "CodificaEmail"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Abilitato (togliere il segno di spunta per disabilitare questo campo personalizzato)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Abilitato (togliere il segno di spunta per disabilitare questo gruppo)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Abilitato (togliere il segno di spunta per disabilitare questa coda)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "Campi personalizzati abilitati"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Code abilitate"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Stato %1 abilitato"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "Stato abilitato: %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Valori multipli"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Inserire, separati da spazio, gli oggetti (o URI di oggetti) da collegare."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Valore singolo"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Inserire, separati da spazio, le code (o URI di code) da collegare."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Inserire, separati da spazio, i ticket (o URI di ticket) da collegare."
+
+#: NOT FOUND IN SOURCE
+msgid "Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces."
+msgstr "Inserire, separati da spazio, i ticket (o URI di ticket) da collegare."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Inserimento di un massimo di %1 valori"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Errore"
+
+#: NOT FOUND IN SOURCE
+msgid "Error adding watcher"
+msgstr "Errore durante l'aggiunta di un osservatore"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Errore nei parametri passati a Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Errore nei parametri passati a Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Errore nei parametri passati a Queue->DeleteWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Errore nei parametri passati a Ticket->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Errore nei parametri passati a Ticket->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Errore nei parametri passati a Ticket->DeleteWatcher"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Propaga ticket"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Stima"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Chiunque"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Esempio:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExtendedStatus"
+msgstr "StatoAggiuntivo"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "IdAutorizEsterno"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "IdContattoEsterno"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Informazioni aggiuntive"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Impossibile creare un attributo della ricerca"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Impossibile trovare il pseudogruppo di utenti 'Privilegiato'."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Impossibile trovare il pseudogruppo di utenti 'Non Privilegiato'."
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Errore nel caricamento del modulo %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "Impossibile caricare un oggetto per %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb."
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "Febbraio"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Nome file"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Testi multipli"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Testi WIKI multipli"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Testo singolo"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Testo WIKI singolo"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Testi multipli (max. %1)"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Testi WIKI multipli (max. %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Fin"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Priorità finale"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "PrioritaFinale"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Cerca il gruppo in cui"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Cerca i gruppi in cui"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Cerca ticket nuovi/aperti"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Cerca le persone in cui"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Ricerca ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Completa approvazione"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Primo"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Prima pagina"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Tizio Caio Sempronio"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Tizio!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Forza la modifica"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Opzioni colonna"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Trovati %quant(%1,ticket)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Trovato Oggetto"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "Testo libero"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FreeformContactInfo"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "FreeformMultiple"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "FreeformSingle"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Ven."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Intestazione completa"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Carica da file un modello compilato"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Sto individuando l'utente corrente da una firma pgp\\n"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Assegnato a %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Globale"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Campi personalizzati globali"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Keyword Selections"
+msgstr "Selezione globale delle parole chiave"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Scrips globali"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Configurazione globale campi personalizzati"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Modello globale: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Vai"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Vai!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Firma pgp valida da %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Vai a pagina"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Vai al ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Grand"
+msgstr "Grand"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Gruppo"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Gruppo %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Diritti di gruppo"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Appartiene già al gruppo"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "Impossibile creare il gruppo."
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Impossibile creare il gruppo: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Gruppo creato"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Non appartiene al gruppo"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Gruppo non trovato"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Gruppo non trovato.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Gruppo non specificato.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Gruppi"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "I gruppi non possono appartenere a gruppi che sono già loro appartenenti"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Gruppi che corrispondono ai criteri di ricerca"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "L'utente appartiene ai gruppi"
+
+#: NOT FOUND IN SOURCE
+msgid "HasMember"
+msgstr "HaUnAppartenente"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Ciao!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Ciao, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Cronologia"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "Cronologia del gruppo %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "Cronologia dell'utente %1"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "TelefonoCasa"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Homepage"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Ho %quant(%1,concrete mixer)."
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "Ho [quant,_1,concrete mixer]."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Mi sono perso..."
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Id"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identità"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Se un'approvazione viene negata, respingi l'originale e elimina le approvazioni accodate"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "(per i nuovi ticket in cui non viene specificato un richiedente)"
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "(per i nuovi ticket in cui non viene specificata una coda)"
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Se questo strumento viene ha il flag setgid, un utente locale malintenzionato potrebbe usarlo per ottenere accesso amministrativo a RT."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Se hai aggiornato qualche impostazione, assicurati di premere"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Valore non valido per %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Immagine"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Campo immutabile"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Elenca anche i campi personalizzati disabilitati."
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Elenca anche i gruppi disabilitati."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Elenca anche le code disabilitate."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Includi nella ricerca gli utenti disabilitati."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Query incompleta"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Query incompleta"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Priorità iniziale"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "PrioritaIniziale"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Errore in input"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Interest noted"
+msgstr "Interesse annotato"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Errore interno"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Errore interno: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Tipo di gruppo non valido"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Diritto non valido"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "Tipo non valido"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Dati non validi"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Incaricato non valido. Uso il default 'nessuno'."
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Coda non valida"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Diritto non valido"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Valore non valido per %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Valore non valido per il campo personalizzato"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Valore non valido per lo stato"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "È estremamente importante che agli utenti non previlegiati non sia consentita l'esecuzione di questo strumento."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Per eseguire questo strumento, si suggerisce di creare un utente unix non privilegiato appartenente al gruppo corretto e con i necessari diritti in RT."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Accetta svariati argomenti:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Corsivo"
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Oggetti in attesa della mia approvazione"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Gen."
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "Gennaio"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Cambia appartenenza a questo gruppo"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Lug."
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "Luglio"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Jumbo"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Giu."
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "Giugno"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Parola chiave"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Lingua"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Lingua"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Grande"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Ultimo"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Ultima comunicazione a richiedenti"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Ultima comunicazione a richiedenti"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Ultima notifica"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Ultima modifica"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Updated By"
+msgstr "Modificato Da"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "UltimaModifica"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "ModificatoPerUltimoDa"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdatedRelative"
+msgstr "TempoDaUltimoAggiornamento"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Rimasti"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Consenti a questo utente di accedere a RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Consenti la concessione di diritti espliciti a questo utente"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Limitare l'incaricato a %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Limitare la coda a %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Collegamento"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Il collegamento esiste già"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Il collegamento non può essere creato"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Collegamento creato (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Collegamento cancellato (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Collegamento non trovato"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Collega ticket n°%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "Collega ticket %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "LinkedTo"
+msgstr "CollegatoA"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Collegamenti"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Carica"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Carica ricerca salvata:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "CaricaRicercheSalvate"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Moduli perl caricati"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Caricata ricerca %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Domicilio"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Impossibile trovare la directory di log %1 (oppure mancano i permessi di scrittura).\\n RT non può essere eseguito."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Accesso effettuato come %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Accedi"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Esci"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Tipo di ricerca sbagliato"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Nuovo incaricato"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Nuovo stato"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Nuova data scadenza"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Nuova data risoluzione"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Nuova data iniziato"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Nuova data inizio previsto"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Nuova data ultimo contatto"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Nuova priorità"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Nuova coda"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Nuovo oggetto"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Rendi questo gruppo visibile all'utente"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Gestione campi personalizzati e relativi valori"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Gestione di gruppi e appartenenze"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Gestione di proprietà e configurazioni che si applicano a tutte le code"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Gestione delle code e delle propietà specifiche delle code"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Gestione di utenti e password"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Marzo"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Maggio"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Mag."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Aggiunto appartenente %1"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Rimosso appartenente %1"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Aggiunto appartenente"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Rimosso appartenente"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Appartenente non rimosso"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Appartenente a"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "AppartenteA"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Appartenenti"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "Aggiunta appartenenza al gruppo %1"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "Rimossa appartenenza al gruppo %1"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Appartenenza a gruppi"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "Appartenenza a gruppi per l'utente %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Unione avvenuta con Successo"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Unione fallita. Impossibile impostare EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Unione fallita. Impossibile impostare Stato"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Unisci a"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Unito a %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Messaggio"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Impossibile registrare il messaggio"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Messaggio registrato"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "I messaggi riguardanti questo ticket non saranno inviati a..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "Parentesi non bilanciate"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Manca una chiave primaria?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Cellulare"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "TelefonoCellulare"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Modifica la Lista Controllo Accessi (ACL)"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Modifica il campo personalizzato %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Modifica campi personalizzati applicabili a %1 per ogni %2"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Modifica i campi personalizzati applicabili a tutti %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Modifica i Campi Personalizzati validi per tutte le code"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Modifica diritti del gruppo"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Modifica appartenenti al gruppo"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Modifica diritti"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Modifica i modelli di Scrips per questa coda"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Modifica gli Scrips per questa coda"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "Modifica le ACL di Sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Modifica il modello %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Modifica diritti utente"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Modifica un campo personalizzato per la coda %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Modifica un campo personalizzato valido per tutte le code"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Modifica uno scrip per la coda %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Modifica uno scrip valido per tutte le code"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Modifica oggetti associati a %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "Modifica le date per n° %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Modifica le date per n°%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Modifica le date per il ticket n° %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Modifica campi personalizzati globali"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Modifica i diritti di gruppo globali"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Modifica i diritti di gruppo globali."
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "Modifica i diritti di gruppo globali"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "Modifica i diritti globali per gli utenti"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Modifica gli scrips globali"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Modifica i diritti globali per gli utenti"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Modifica i diritti globali per gli utenti."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Modifica i metadati del gruppo o elimina un gruppo"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Modifica i diritti di gruppo per il campo personalizzato %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Modifica i diritti di gruppo per il gruppo %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Modifica i diritti di gruppo per la coda %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Modifica gli appartenenti a questo gruppo"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Modifica il proprio account RT"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Modifica le persone collegate alla coda %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Modifica le persone collegate al ticket n°%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Modifica gli scrips per la coda %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Modifica gli scrips validi per tutte le code"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Modifica modello %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Modifica i modelli validi per tutte le code"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Modifica il gruppo %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Modifica gli osservatori della coda"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Modifica l'utente %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Modifica il ticket n° %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Modifica il ticket n°%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Modifica i ticket"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Modifica i diritti utente per il il campo personalizzato %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Modifica i diritti utente per il gruppo %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Modifica i diritti utente per la coda %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Modifica gli osservatori per la coda '%1'"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ModificaACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "ModificaCampoPers."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "ModificaPropriaAppartenenzaAlGruppo"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "ModificaOsservatoriDellaCoda"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ModificaScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "ModificaSuoiDati"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "ModificaModello"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "ModificaTicket"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Lun."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Maggiori informazioni su %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Sposta in basso"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Sposta in alto"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Multiplo"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "L'attributo 'name' deve essere specificato"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "I miei ticket in stato: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Le mie richieste di approvazione"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Le mie richieste di approvazione"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Le mie ricerche salvate"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Nome"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Nome già usato"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "È richiesta l'approvazione da parte del system administrator"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Mai"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Nuovo"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nuovi collegamenti"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nuova Password"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nuove approvazioni pendenti"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Nuova query"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Nuova Ricerca"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nuovo campo personalizzato"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Nuovo gruppo"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nuova password"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Inviata notifica della nuova password"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Nuova coda"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Nuova richiesta"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nuovi diritti"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Nuovo scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Nuova ricerca"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Nuovo modello"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Nuovo ticket"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Il nuovo ticket non esiste"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Nuovo utente"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Nuovo utente di nome"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nuovo osservatore"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Impostazioni nuova finestra"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Seguente"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Pagina seguente"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Pagina seguente"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Nomignolo"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Nomignolo"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Nessuna classe definita"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Nessun campo personalizzato"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Nessun campo personalizzato definito"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Nessun gruppo definito"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Nessuna query"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Nessuna coda definita"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Nessun utente RT trovato. Consulta il tuo amministratore di RT.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Nessun Modello"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Nessun ticket specificato. Annullo il ticket"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Nessuna azione"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Nessuna colonna specificata"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Nessun comando trovato\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Nessun commento su questo utente"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Nessuna corrispondenza allegata"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Nessuna descrizione per %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Nessun gruppo specificato"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Nessun gruppo corrisponde ai criteri di ricerca."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Nessun messaggio allegato"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Nessuna password impostata"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Manca il permesso per creare code"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Manca il permesso per creare ticket nella coda '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Manca il permesso per creare utenti"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Manca il permesso per visualizzare il ticket"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Manca il permesso per aggiornare il ticket"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Nessun utente/gruppo specificato"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Nessun utente/gruppo selezionato."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Nessuna coda soddisfa i criteri di ricerca."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Nessun diritto trovato"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Nessun diritto concesso."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Nessuna ricerca caricata"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Nessuna ricerca su cui operare."
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Senza oggetto"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Nessun identificativo di ticket specificato"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Nessun tipo transazione specificato"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "Nessun utente o indirizzo email specificato"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Nessuna utente soddisfa i criteri di ricerca."
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Nessun valore inviato a _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Nessuno"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Campo inesistente?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Accesso non effettuato"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Accesso non effettuato."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Non impostato"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Non ancora implementato."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Non ancora implementato...."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Note"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Impossibile inviare la notifica"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "invia notifica agli AdminCc"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "invia notifica agli AdminCc come commento"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "invia notifica ai Cc"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "invia notifica ai Cc come commento"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "invia notifica agli altri destinatari"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "invia notifica agli altri destinatari come commento"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "invia notifica all'incaricato"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "invia notifica all'incaricato come commento"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "invia notifica all'incaricato del ticket respinto"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "invia notifica all'incaricato del ticket che tutte le approvazioni sono state concesse"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "invia notifica all'incaricato del ticket che alcune approvazioni sono state concesse"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "invia notifica agli incaricati e AdminCc quando è necessario richiedere nuove approvazione"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "invia notifica al richiedente"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "invia notifica ai richiedenti e ai Cc"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "invia notifica ai richiedenti e ai Cc come commento"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "invia notifica ai richiedenti, Cc e AdminCc"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "invia notifica ai richiedenti, Cc a AdminCc come commento"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "Novembre"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "OR"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "L'oggetto non può essere creato"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "L'oggetto non può essere cancellato"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Oggetto creato"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Oggetto cancellato"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Oggetto di tipo %1 non accetta campi personalizzati"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Tipo oggetto non corrisponde"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Oct."
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "Ottobre"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Offline"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Modifiche offline"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Upload richiesta offline"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "il"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "All'arrivo di un commento"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "All'arrivo di una corrispondenza"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Alla creazione"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Al cambio dell'incaricato"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Al cambio della priorità"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Al cambio di coda"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Alla risoluzione"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Al cambio di stato"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Ad ogni transazione"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Mostra le approvazioni solo per le richieste create dopo il %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Mostra le approvazioni solo per le richieste create prima del %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Aperto"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Apri"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Richieste aperte"
+
+#: NOT FOUND IN SOURCE
+msgid "Open ticket"
+msgstr "Apri ticket"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Ticket aperti"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Apre ticket (dalla lista) in una nuova finestra"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Apre ticket (dalla lista) in un'altra finestra"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Apri i ticket all'arrivo di corrispondenza"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Ordina per"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Ordinamento"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Azienda"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Ticket originario: n°%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Registrata email in uscita riguardante un commento"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Registrata email in uscita"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Al passare del tempo, la priorità cresce fino a"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Prende in carico ticket"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "PrendeInCaricoTicket"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Incaricato"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Cambiato incaricato da %1 a %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "L'incaricato non può essere impostato."
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Cambiato forzatamente l'incaricato da %1 a %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "In carico a"
+
+#: NOT FOUND IN SOURCE
+msgid "OwnerName"
+msgstr "NomeIncaricato"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Pagina %1 di %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Cercapersone"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Cercapersone"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "DerivaDa"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Password"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Promemoria password"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Password cambiata"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "La password deve essere lunga almeno %1 caratteri"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Password troppo corta"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Password: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Le passwords non corrispondono."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Le password non corrispondono. La password non è stata cambiata"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Persone"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Esegui un'azione personalizzata"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Configurazione perl"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Operazione non consentita"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Gruppi personali"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Gruppi personali"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Gruppi personali:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Numeri telefonici"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Segnaposto"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Preferenze"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Prefs"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Preparazione non necessaria"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Precedente"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Pagina precedente"
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Pagina precedente"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Utente/gruppo %1 not trovato."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Priorità"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "La priorità inizia da"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Riservatezza:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilegiato"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Stato previlegiato: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Utenti privilegiati"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudogruppo per uso interno"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "Query"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Costruttore di query"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Coda"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Coda %1 non trovata"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Coda '%1' non trovata\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Keyword Selections"
+msgstr "Selezione delle parole chiave per la coda"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Nome della coda"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Scrips della coda"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "La coda esiste già"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Impossibile creare la coda"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Impossibile caricare la coda."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Coda creata"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Coda non specificata."
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Coda non trovata"
+
+#: NOT FOUND IN SOURCE
+msgid "QueueName"
+msgstr "NomeCoda"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Code"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Ricerca veloce"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Creazione veloce di un ticket"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 per %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 da <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Amministrazione di RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "Errore di autenticazione RT."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT Bounce: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "Errore nella configurazione di RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "Errore critico. Messaggio not registrato!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "Errore RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT ha ricevuto email (%1) da se stesso."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Recieved mail (%1) from itself."
+msgstr "RT ha ricevuto email (%1) da se stesso."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT Self Service / Ticket chiusi"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "Variabili RT"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "Quadro d'insieme"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT non ha potuto autenticarti"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT non è riuscito a trovare il richiedente consultando in database esterno"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT non ha trovato la coda: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT non può memorizzare la tua sessione"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT non può verificare questa firma PGP. \\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT / %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT / %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT ha eseguito i comandi"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. RT viene distribuito con la <a href=\"http://www.gnu.org/copyleft/gpl.html\">Versione 2 della GNU General Public License.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-2002 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT is &copy; Copyright 1996-2002 Jesse Vincent &lt;jesse@bestpractical.com&gt;. RT viene distribuito con la <a href=\"http://www.gnu.org/copyleft/gpl.html\">Versione 2 della GNU General Public License.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT thinks this message may be a bounce"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT will process this message as if it were unsigned.\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Nome reale"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "NomeReale"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "Aggiunto riferimento da %1"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "Cancellato riferimento da %1"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "Aggiunto riferimento a %1"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "Cancellato riferimento a %1"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "È un riferimento per"
+
+#: NOT FOUND IN SOURCE
+msgid "ReferredToBy"
+msgstr "RiferimentoPer"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Fa riferimento a"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "FaRiferimentoA"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Rifinisci"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Rifinisci la ricerca"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Aggiorna questa pagina ogni %1 minuti."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Rimuovi AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Rimuovi Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Rimuovi richiedente"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Risposta"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Indirizzo a cui indirizzare le risposte"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Rispondi ai richiedenti"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Rispondi ai ticket"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "RispondiAlTicket"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Richiedente"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Indirizzo email del richiedente"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Richiedente(i)"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "IndirizzoRichiedente"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Richiedenti"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Le richieste devono essere soddisfatte entro"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Il parametro obbligatorio '%1' non è stato specificato"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Azzera"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Casa"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Risolvi"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Risolvi il ticket n°%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Risolto"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ResolvedRelative"
+msgstr "TempoDaRisoluzione"
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Risposta ai richiedenti"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Risultati"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Risultati per pagina"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Ridigita Password"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Annulla modifiche"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Diritto %1 non trovato per %2 %3 limitatamente a %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Diritto delegato"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Diritto concesso"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Diritto caricato"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Il diritto non può essere revocato"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Diritto non trovato"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Diritto non caricato."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Diritto revocato"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Diritti"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "I diritti per %1 non possono essere concessi"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "I diritti per %1 non possono essere revocati"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Ruoli"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "ApprovazioneAmministratore"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Righe per pagina"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sab."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Salva"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Salva modifiche"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Salva preferenze"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Salva modifiche"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "Ricerca %1 salvata"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Ricerche salvate"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip n°%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip creato"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Campi dello scrip"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip eliminato"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Scrips per %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrips che si applicano a tutte le code"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Ricerca"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Criteri di ricerca"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Errore nel caricamento degli attributi della ricerca"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Ricerca le richieste di approvazione"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Aggiornamento della ricerca: %1"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Protezione:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Vedi campi personalizzati"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Vedi letteralmente i messaggi in uscita e i loro destinatari"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Vedi commenti privati del ticket"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Vedi riassunto dei ticket"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "VediCampoPers."
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "VediGruppo"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "VediCoda"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "Seleziona"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Scegli un campo personalizzato"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Seleziona un gruppo"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Seleziona una coda"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Scegli una coda per il nuovo ticket"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Seleziona un utente"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Seleziona un campo personalizzato"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Seleziona i campi personalizzati per tutti i gruppi degli utenti"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Seleziona i campi personalizzati per tutti gli utenti"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Seleziona i campi personalizzati per i ticket in tutte le code"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Seleziona i campi personalizzati per le transazioni relative ai ticket in tutte le code"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Seleziona il gruppo"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Elenco a selezione multipla"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Elenco a selezione singola"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Seleziona la coda"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Seleziona lo scrip"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Seleziona il modello"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Seleziona un massimo di %1 valori"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Seleziona l'utente"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "SelezioneMultipla"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "SelezioneSingola"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Campi personalizzati selezionati"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Oggetti selezionati"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Self Service"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Invia una mail a tutti gli osservatori"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Invia una mail atutti gli osservatori come \"commento\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Invia mail ai richiedenti e Ccs"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Invia mail come commento a richiedenti e Cc"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Invia un messaggio ai richiedenti"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Invia mail a Cc e Bcc esplicitamente elencati"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Invia mail ai Cc"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Invia mail ai Cc come commento"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Invia mail ai Cc amministrativi"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Invia mail ai Cc amministrativi come commento"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Invia mail all'incaricato"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Set."
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "Settembre"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Mostra"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Mostra richieste di approvazione"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Mostra colonne"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Mostra i risultati"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Mostra le richieste approvate"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Mostra info di base"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Mostra le richieste negate"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Mostra i dettagli"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Mostra le richieste in attesa"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Mostra le richieste in attesa di altre approvazioni"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Mostra commenti privati del ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Mostra riassunto dei ticket"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "MostraACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "MostraTabConfigurazione"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "MostraEmailUscente"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "MostraRicercheSalvate"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "MostraScrips"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "MostraModello"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "MostraTicket"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "MostraCommentiAlTicket"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Registra come richiede o come Cc del ticket o della coda"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Registra come AdminCc del ticket o della coda"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Firma"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "Accesso effettuato come %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Singolo"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Carattere"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Salta menu"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Piccolo"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Ordinamento"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Chiave di ordinamento"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Ordina i risultati per"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "Ordinamento"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "In stallo"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Pagina iniziale"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Iniziato"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Impossibile interpretare data inizio '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "StartedRelative"
+msgstr "TempoDaInizio"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "InizioPrevisto"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Inizio previsto il"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Impossibile interpretare data inizio previsto '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "StartsRelative"
+msgstr "TempoDaInizioPrevisto"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Provincia"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Stato"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Cambiamento di stato"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Cambiato lo stato da %1 a %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "CambiamentoStato"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Sottrai"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Sottrae ticket"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "SottraeTicket"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Sottratto da %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Sottratto da %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Stile"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Oggetto"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "L'oggetto diventa %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Invia"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Imposta workflow"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Completato con successo"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Dom."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "SuperUtente"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Sistema"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Configurazione del sistema"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Errore di sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. Right not granted."
+msgstr "Errore di sistema. Diritto non concesso."
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. right not granted"
+msgstr "Errore di sistema. Diritto non concesso"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "Errore di sistema: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Strumenti di sistema"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Errore di sistema. Diritto non delegato."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Errore di sistema. Diritto non concesso."
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "Errore di sistema. Impossibile concedere i diritti."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Gruppi di sistema"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRolegroup per uso interno"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Prendi in carico"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Prendi in carico ticket"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "PrendiInCaricoTicket"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Preso in carico"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Modello"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Modello n°%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Modello eliminato"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Modello non trovato"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Modello non trovato\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Modello elaborato"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Modelli"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Modelli per %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "Testo"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Il valore attuale è già questo"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Questo campo personalizzato non ammette questo valore"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Il valore è lo stesso"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Questo utente/gruppo ha già questo diritto"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Questo utente/gruppo è già %1 per questa coda"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Questo utente/gruppo è già %1 per questo ticket"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Questo utente/gruppo non è %1 per questa coda"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Questo utente/gruppo non è %1 per questo ticket"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "La coda non esiste"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Il ticket ha dipendenze non risolte"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "L'utente ha già questo diritto"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "L'utente ha già il ticket in carico"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "L'utente non esiste"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "L'utente è già previlegiato"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "L'utente è già non previlegiato"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Ora l'utente è previlegiato"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Ora l'utente è non previlegiato"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "Ora l'utente è non previlegiato"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "L'utente non può prendere in carico ticket da questa coda"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Non è un identificativo numerico"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Dati di base"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "Il Cc di un ticket"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "Il Cc amministrativo di un ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Il commento è stato registrato"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Il comando seguente troverà tutti i ticket attivi nella coda 'general' e ne imposterà la priorità a 99 se nessuno li ha toccati nelle ultime 4 ore:"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Il nuovo valore è stato impostato."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "L'incaricato del ticket"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Il richiedente del ticket"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Questi commenti non vengono normalmente mostrati all'utente"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "L'oggetto non ammette questo campo personalizzato"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Questa funzionalità è riservata agli amministratori di sistema"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Il messaggio sarà inviato a..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Il ticket %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Questo strumento permette di eseguire qualsiasi modulo perl da dentro RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "La transazione sembra non avere alcun contenuto"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "I %1 ticket a maggior priorità richiesti da questo utente"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "I 25 ticket a maggior priorità richiesti da questo utente"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Gio."
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Ticket n° %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "Ticket n° %1 aggiornamento Jumbo: %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Ticket n°%1 aggiornamento Jumbo: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Ticket n°%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Creato ticket %1 nella coda '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Caricato ticket %1\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Ticket %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Campi personalizzati del ticket"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Cronologia del ticket n° %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Identificativo ticket"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Ticket risolto"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Transazioni sul ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Allegato al ticket"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Contenuto del ticket"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Tipo del contenuto del ticket"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "A causa di un errore interno non è stato possibile creare il ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Ticket creato"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Creazione del ticket fallita"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Ticket eliminato"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Identificativo del ticket non trovato"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "Ticket eliminato"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Metadati del ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Ticket non trovato"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Stato del ticket cambiato"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Osservatori del ticket"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "Modulo di ricerca TicketSQL"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Ticket %1 da %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Ticket da %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Ticket che dipendono da questa approvazione:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Tempo stimato"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Tempo rimasto"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Tempo lavorato"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Tempo rimanente"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Tempo impiegato per l'elaborazione"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Tempo impiegato"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeEstimated"
+msgstr "TempoStimato"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "TempoRimanente"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "TempoImpiegato"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Titolo"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Per generare una diff dopo il commit:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "Per generare una diff dopo il commit:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Per richieste di supporto, formazione, personalizzazioni o acquisto di licenze, si prega di contattare %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "UltimaComunicazioneARichiedenti"
+
+#: NOT FOUND IN SOURCE
+msgid "ToldRelative"
+msgstr "TempoDaUltimoContattoRichiedente"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Strumenti"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transazioni"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transazione %1 ripulita"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transazione creata"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Campi personalizzati della transazione"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Transaction->Create fallita, devi specificare l'identificativo dell'oggetto"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transaction->Create fallita, devi specificare il tipo e l'identificativo dell'oggetto"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Le transazioni sono immutabili"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Provo a eliminare un diritto: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Mar."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Tipo"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "NonImplementato"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Username unix"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "UsernameUnix"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "ContentEncoding sconosciuto %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Campo sconosciuto: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Illimitato"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Ricerca senza nome"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "NonPrevilegiato"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Campi personalizzati non selezionati"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Oggetti non selezionati"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "NonPreso"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Aggiornamento"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Aggiorna tutti"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Identificativo aggiornamento"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Aggiorna ticket"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Tipo aggiornamento"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Aggiorna tutti questi ticket contemporaneamente"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Aggiorna email"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Aggiorna ticket multipli"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Aggiorna nome"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Aggiornamento non registrato."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Aggiorna i ticket selezionati"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Aggiorna la firma"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Aggiorna il ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Aggiorna il ticket n° %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Aggiorna il ticket n°%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Aggiorna il ticket n°%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Il tipo aggiornamento non era ne' corrispondenza ne' commento."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Aggiornato"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Upload"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Upload file multipli"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Upload immagini multiple"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Upload file singolo"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Upload immagine singola"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Upload fino a %1 file"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Upload fino a %1 immagini"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Upload delle modifiche"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Usa altri strumenti di amministrazione di RT"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Utente %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Utente %1 Password: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "Impossibile trovare l'utente '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Utente '%1' non trovato"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Utente '%1' non trovato\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Definito dall'utente"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Condizioni e azioni definite dall'utente"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "Username"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Username"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Diritti dell'utente"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "L'utente ha richiesto un aggiornamento di tipo sconosciuto sul campo personalizzato %1 per %2 l'oggetto n°%3"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Impossibile creare l'utente: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Utente creato"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Gruppi definiti dall'utente"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Utente caricato"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Utente notificato"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "User view"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Gruppi definiti dall'utente"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Username"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Utenti"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Utenti che soddisfano il criterio di ricerca"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Query valida"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "ValoreDellaCoda"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Valori"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Osserva"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "OsservaComeAdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher"
+msgstr "Osservatore"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "Osservatore caricato"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Osservatori"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "WebEncoding"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Mer."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Quando un ticket ha ricevuto tutte le approvazioni richieste, aggiungi una corrispondenza al ticket originale"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Quando un ticket riceve una qualsiasi delle approvazioni richieste, aggiungi una corrispondenza al ticket originale"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Quando viene creato un ticket"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Quando una richiesta di approvazione viene creata, notifica chi ha in carico il ticket e gli AdminCc che devono approvarlo"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Quando succede qualsiasi cosa"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Quando un ticket viene risolto"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Quando cambia colui che ha in carico il ticket"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Quando cambia la priorità di un ticket"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Quando il ticket si sposta in un'altra coda"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Quando cambia lo stato di un ticket"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Quando si verifica una condizione definita dall'utente"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Quando arrivano commenti"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Quando arrivano corrispondenze"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Lavoro"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Lavoro offline"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "TelefonoLavoro"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Impiegato"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Hai già in carico questo ticket"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Non sei un utente autorizzato"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Puoi solo riassegnare ticket che hai in carico, o che non sono ancora assegnati"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "Non hai i permessi per visualizzare questo ticket.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Hai trovato %1 ticket nella coda %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Ti sei scollegato da RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Non hai l'autorizzazione per creare ticket in questa coda."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Non puoi creare ticket in questa coda."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Collegati di nuovo"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Le tue %1 richieste"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "L'amministratore RT non ha configurato correttamente l'alias di email che invoca RT"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "La tua richiesta è stata approvata da %1. Potrebbero essere necessarie altre approvazioni."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "La tua richiesta è stata approvata."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Your request was rejected"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "La tua richiesta è stata respinta."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Il tuo username o la tua password non sono corretti"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "CAP"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[senza oggetto]"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "permetti la creazione di ricerche salvate"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "permetti il caricamento di ricerche salvate"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "come concesso a %1"
+
+#: NOT FOUND IN SOURCE
+msgid "belongs to"
+msgstr "appartiene a"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "chiuso"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "contiene"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "contenuto"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "content-type"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "corrispondenza (probabilmente) non inviata"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "corrispondenza inviata"
+
+#: NOT FOUND IN SOURCE
+msgid "current: $current, want $want, Error near ->$val<- expecting a "
+msgstr "attuale: $current, richiesto $want, Errore vicino a ->$val<- mi aspettavo "
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "giorni"
+
+#: NOT FOUND IN SOURCE
+msgid "dead"
+msgstr "morto"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "cancella"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "cancellato"
+
+#: NOT FOUND IN SOURCE
+msgid "does not belong to"
+msgstr "non appartiene a"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "non corrisponde a"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "non contiene"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "uguale a"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "errore: impossibile spostare in basso"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "errore: impossibile spostare a sinistra"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "errore: impossibile spostare in alto"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "errore: niente da cancellare"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "errore: niente da spostare"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "errore: niente da invertire"
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "falso"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "nome file"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "maggiore di"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "gruppo '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "ore"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "Codice"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "uguale a"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "diverso da"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "minore di"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "corrisponde a"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minuti"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "mesi"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "nuovo"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "nessun nome"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "nessun valore"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "nessuno"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "diverso da"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "diverso da"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "aperto"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "Gruppo personale '%1' per l'utente '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "coda %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "respinto"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "risolto"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sec"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "Mostra configurazione"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "foglio di calcolo"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "in stallo"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "sistema %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "gruppo di sistema '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "il componente chiamante non ha specificato il perché"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "ticket n°%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "vero"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "gruppo %1 senza descrizione"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "gruppo %1 senza descrizione"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "utente %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "settimane"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "con il modello %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "anni"
+
diff --git a/rt/lib/RT/I18N/ja.po b/rt/lib/RT/I18N/ja.po
new file mode 100644
index 0000000..f266907
--- /dev/null
+++ b/rt/lib/RT/I18N/ja.po
@@ -0,0 +1,6749 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.6.x\n"
+"POT-Creation-Date: 2002-05-02 11:36+0800\n"
+"PO-Revision-Date: 2007-11-30 01:18+0900\n"
+"Last-Translator: Shinji R. Yamane <s-yamane@computer.org>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Country: JAPAN\n"
+
+#. ($self->{CurrentSearch}{Object}->Description)
+#: html/Widgets/SavedSearch:70
+msgid " %1 deleted."
+msgstr "%1 を削除ã—ã¾ã—ãŸ"
+
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+#: html/Widgets/SavedSearch:47
+msgid " %1 renamed to %2."
+msgstr "%1 ã‚’ %2 ã«å¤‰æ›´ã—ã¾ã—ãŸ"
+
+#. ($args->{Description})
+#: html/Widgets/SavedSearch:60
+msgid " %1 saved."
+msgstr "%1 ã‚’ä¿å­˜ã—ã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+#: html/Approvals/Elements/Approve:48
+#: html/Approvals/Elements/ShowDependency:71
+#: html/SelfService/Display.html:46
+#: html/Ticket/Display.html:47
+#: html/Ticket/Display.html:51
+msgid "#%1: %2"
+msgstr ""
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#. ($label)
+#: lib/RT/Record.pm:940
+msgid "$prefix %1"
+msgstr ""
+
+#. ($self->ObjectType, $self->Object->Id)
+#: lib/RT/URI/fsck_com_rt.pm:256
+msgid "%1 #%2"
+msgstr ""
+
+#. ($s, $time_unit)
+#: lib/RT/Date.pm:365
+msgid "%1 %2"
+msgstr ""
+
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+#: lib/RT/Date.pm:401
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+#: lib/RT/Record.pm:1685
+#: lib/RT/Transaction_Overlay.pm:647
+#: lib/RT/Transaction_Overlay.pm:690
+msgid "%1 %2 added"
+msgstr ""
+
+#. ($s, $time_unit)
+#: lib/RT/Date.pm:362
+msgid "%1 %2 ago"
+msgstr "%1 %2 å‰"
+
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+#: lib/RT/Record.pm:1692
+#: lib/RT/Transaction_Overlay.pm:654
+msgid "%1 %2 changed to %3"
+msgstr "%3ã«å¤‰æ›´ã•ã‚ŒãŸ%1 %2 "
+
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+#: lib/RT/Record.pm:1689
+#: lib/RT/Transaction_Overlay.pm:650
+#: lib/RT/Transaction_Overlay.pm:696
+msgid "%1 %2 deleted"
+msgstr ""
+
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+#: html/Admin/Elements/EditScrips:65
+#: html/Admin/Elements/ListGlobalScrips:63
+#: html/Ticket/Elements/PreviewScrips:103
+msgid "%1 %2 with template %3"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 ã“ã®ãƒã‚±ãƒƒãƒˆ\\n"
+
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+#: html/Ticket/Elements/ShowAttachments:72
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+#: html/SelfService/Update.html:60
+#: html/Ticket/Elements/EditBasics:108
+#: html/Ticket/Update.html:61
+#: html/Ticket/Update.html:63
+#: html/Tools/MyDay.html:66
+msgid "%1 (Unchanged)"
+msgstr "%1 (変更ãªã—)"
+
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+#: bin/rt-crontool:237
+#: bin/rt-crontool:244
+#: bin/rt-crontool:250
+msgid "%1 - An argument to pass to %2"
+msgstr ""
+
+#. ("--verbose")
+#: bin/rt-crontool:262
+msgid "%1 - Output status updates to STDOUT"
+msgstr ""
+
+#. ("--template-id")
+#: bin/rt-crontool:253
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#. ("--transaction")
+#: bin/rt-crontool:256
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#. ("--action")
+#: bin/rt-crontool:247
+msgid "%1 - Specify the action module you want to use"
+msgstr ""
+
+#. ("--condition")
+#: bin/rt-crontool:241
+msgid "%1 - Specify the condition module you want to use"
+msgstr ""
+
+#. ("--search")
+#: bin/rt-crontool:234
+msgid "%1 - Specify the search module you want to use"
+msgstr ""
+
+#. ("--transaction-type")
+#: bin/rt-crontool:259
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+#: html/Elements/Footer:56
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#. ($self->Id)
+#: lib/RT/ScripAction_Overlay.pm:150
+msgid "%1 ScripAction loaded"
+msgstr "%1スクリプトアクションロードã—ã¾ã—ãŸ"
+
+#. ($args{'Value'}, $cf->Name)
+#: lib/RT/Record.pm:1722
+msgid "%1 added as a value for %2"
+msgstr "%2ã®å€¤ã«%1ãŒè¿½åŠ ã•ã‚Œã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "%1aliasesã‚’å‹•ã‹ã™ãŸã‚ã«ãƒã‚±ãƒƒãƒˆIDãŒå¿…è¦ã§ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "%1aliasesã‚’å‹•ã‹ã™ãŸã‚ã«ãƒã‚±ãƒƒãƒˆIDãŒå¿…è¦ã§ã™ "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "%1aliasesã‚’å‹•ã‹ã™ãŸã‚ã«ãƒã‚±ãƒƒãƒˆIDãŒå¿…è¦ã§ã™(%2ã‹ã‚‰) %3"
+
+#. ($args{'Base'})
+#. ($args{'Target'})
+#: lib/RT/Link_Overlay.pm:144
+#: lib/RT/Link_Overlay.pm:151
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr ""
+
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+#: html/Ticket/Elements/ShowDates:73
+#: lib/RT/Transaction_Overlay.pm:531
+msgid "%1 by %2"
+msgstr "%1 (%2)"
+
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+#: lib/RT/Transaction_Overlay.pm:788
+#: lib/RT/Transaction_Overlay.pm:797
+#: lib/RT/Transaction_Overlay.pm:800
+msgid "%1 changed from %2 to %3"
+msgstr "%1ã¯%2ã‹ã‚‰%3ã«å¤‰æ›´ã•ã‚Œã¾ã—ãŸ"
+
+#. ($Description)
+#: html/Search/Build.html:213
+msgid "%1 copy"
+msgstr ""
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1ã¯ãƒˆãƒ©ãƒ³ã‚¶ã‚¯ã‚·ãƒ§ãƒ³ã‚’開始ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ(%2)\\n"
+
+#. ($self)
+#: lib/RT/Ticket_Overlay.pm:2787
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1を解決状態ã«è¨­å®šã§ãã¾ã›ã‚“ã§ã—ãŸã€‚RTã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«ä¸€è²«æ€§ãŒãªã„å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚"
+
+#. ($obj_type)
+#: lib/RT/Transaction_Overlay.pm:571
+msgid "%1 created"
+msgstr ""
+
+#. ($obj_type)
+#: lib/RT/Transaction_Overlay.pm:576
+msgid "%1 deleted"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 grouped by %2"
+msgstr "優先度ã®é«˜ã„ãƒã‚±ãƒƒãƒˆ%1件"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "優先度ã®é«˜ã„ãƒã‚±ãƒƒãƒˆ%1件"
+
+#. ($0)
+#: bin/rt-crontool:229
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr ""
+
+#. ($principal->Object->Name, $args{'Type'})
+#: lib/RT/Queue_Overlay.pm:863
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1ã¯ã“ã®ã‚­ãƒ¥ãƒ¼ã§ã¯%2ã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1ã¯ã“ã®ãƒã‚±ãƒƒãƒˆã§ã¯%2ã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1ã¯ã‚‚ã†ã‚«ã‚¹ã‚¿ãƒ ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰%2ã®å€¤ã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1ã¯æœ‰åŠ¹ãªã‚­ãƒ¥ãƒ¼IDã§ã¯ã‚ã‚Šã¾ã›ã‚“。"
+
+#. ($minutes)
+#: html/Ticket/Elements/ShowTime:47
+#: html/Ticket/Elements/ShowTime:49
+msgid "%1 min"
+msgstr "%1分"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "担当者ãªã—ãƒã‚±ãƒƒãƒˆ%1件"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1表示ã•ã‚Œã¾ã›ã‚“"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr ""
+
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+#: html/User/Elements/DelegateRights:97
+msgid "%1 rights"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 æˆåŠŸã—ã¾ã—ãŸ\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 タイプ㯠$MessageIdID ã§ã¯ä¸æ˜Žã§ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 タイプã¯%2ã§ã¯ä¸æ˜Žã§ã™"
+
+#. (ref $self)
+#: lib/RT/Action/ResolveMembers.pm:63
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1ã¯ã‚°ãƒ«ãƒ¼ãƒ—ãƒã‚±ãƒƒãƒˆã®ã™ã¹ã¦ã®ãƒ¡ãƒ³ãƒãƒ¼ã‚’解決状態ã«ã—ã¾ã™ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1ãŒã‚‚ã—リンクã•ã‚ŒãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ä¾å­˜ã™ã‚‹ï¼ˆã‚‚ã—ãã¯ãƒ¡ãƒ³ãƒãƒ¼ã§ã‚る)場åˆã€[ローカル]ベースãŒã¨ãŽã‚Œã¾ã™ã€‚"
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#. ($object->Name)
+#. ($Object->Name)
+#: html/Search/Elements/SearchPrivacy:52
+#: html/Search/Elements/SelectSearchObject:55
+#: html/Search/Elements/SelectSearchesForObjects:57
+msgid "%1's saved searches"
+msgstr ""
+
+#. ($self)
+#: lib/RT/Transaction_Overlay.pm:481
+msgid "%1: no attachment specified"
+msgstr "%1:アタッãƒãƒ¡ãƒ³ãƒˆãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#. ($size)
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+msgid "%1b"
+msgstr ""
+
+#. (int( $size / 102.4 ) / 10)
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+msgid "%1k"
+msgstr ""
+
+#. (sprintf("%.1f",$minutes / 60))
+#: html/Ticket/Elements/ShowTime:49
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#. ($args{'Status'})
+#: lib/RT/Ticket_Overlay.pm:1142
+msgid "'%1' is an invalid value for status"
+msgstr "%1'ã¯ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã«ç„¡åŠ¹ãªå€¤ã§ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "%1' アクションをèªè­˜ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(ボックスã®ãƒã‚§ãƒƒã‚¯ã§ã‚¹ã‚¯ãƒªãƒ—トを削除)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50
+#: html/Admin/Elements/EditQueueWatchers:50
+#: html/Admin/Elements/EditScrips:56
+#: html/Admin/Elements/EditTemplates:57
+#: html/Admin/Groups/Members.html:73
+#: html/Elements/EditLinks:54
+#: html/Ticket/Elements/EditPeople:67
+#: html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(ボックスã®ãƒã‚§ãƒƒã‚¯ã§å‰Šé™¤)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(ボックスã®ãƒã‚§ãƒƒã‚¯ã§å‘ŠçŸ¥å—ã‘å–り先リストã‹ã‚‰å‰Šé™¤)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(ボックスã®ãƒã‚§ãƒƒã‚¯ã§å‘ŠçŸ¥å—ã‘å–り先リストã«è¿½åŠ )"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(ãƒã‚±ãƒƒãƒˆIDã¾ãŸã¯URLを空白文字ã§åŒºåˆ‡ã£ã¦å…¥åŠ›ã—ã¦ãã ã•ã„)"
+
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+#: html/Admin/Queues/Modify.html:75
+#: html/Admin/Queues/Modify.html:81
+msgid "(If left blank, will default to %1)"
+msgstr "(空白ã®å ´åˆã€%1を指定ã—ã¾ã™)"
+
+#: html/Admin/Elements/EditCustomFields:74
+#: html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:71
+#: html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(メンãƒãƒ¼ãªã—)"
+
+#: html/Admin/Elements/EditScrips:53
+#: html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(スクリプトãªã—)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr ""
+
+#: html/Admin/Elements/PickCustomFields:47
+#: html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(コンマã§åŒºåˆ‡ã£ãŸEメールアドレスã«ã“ã®æ›´æ–°æƒ…å ±ã®Bccã‚’é€ã‚Šã¾ã™ã€‚ã“ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯å°†æ¥ã®æ›´æ–°æƒ…å ±ã®é€ã‚Šå…ˆã«ã¯<strong>追加ã•ã‚Œã¾ã›ã‚“</strong>。)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(コンマã§åŒºåˆ‡ã£ãŸEメールアドレスã«ã“ã®æ›´æ–°æƒ…å ±ã®Bccã‚’é€ã‚Šã¾ã™ã€‚å°†æ¥ã®æ›´æ–°æƒ…å ±ã®é€ã‚Šå…ˆã«ã¯<strong>追加ã•ã‚Œã¾ã›ã‚“</strong>。)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(コンマã§åŒºåˆ‡ã£ãŸç®¡ç†ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«ã“ã®æ›´æ–°æƒ…å ±ã®Ccã‚’é€ã‚Šã¾ã™ã€‚ãã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯å°†æ¥ã®æ›´æ–°æƒ…å ±ã®é€ã‚Šå…ˆã«<strong>追加ã•ã‚Œã¾ã™</strong>。)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(コンマã§åŒºåˆ‡ã£ãŸEメールアドレスã«ã“ã®æ›´æ–°æƒ…å ±ã®Ccã‚’é€ã‚Šã¾ã™ã€‚ã“ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯å°†æ¥ã®æ›´æ–°æƒ…å ±ã®é€ã‚Šå…ˆã«ã¯<strong>追加ã•ã‚Œã¾ã›ã‚“</strong>。)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(コンマã§åŒºåˆ‡ã£ãŸEメールアドレスã«ã“ã®æ›´æ–°æƒ…å ±ã®Ccã‚’é€ã‚Šã¾ã™ã€‚å°†æ¥ã®æ›´æ–°æƒ…å ±ã®é€ã‚Šå…ˆã«ã¯<strong>追加ã•ã‚Œã¾ã›ã‚“</strong>。)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(コンマã§åŒºåˆ‡ã£ãŸEメールアドレスã«ã“ã®æ›´æ–°æƒ…å ±ã®Ccã‚’é€ã‚Šã¾ã™ã€‚ãã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯å°†æ¥ã®æ›´æ–°æƒ…å ±ã®é€ã‚Šå…ˆã«<strong>追加ã•ã‚Œã¾ã™</strong>。)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr ""
+
+#: html/Ticket/Elements/EditWatchers:60
+#: html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(メールをé€ã‚Šã¾ã›ã‚“)"
+
+#: html/Admin/Groups/index.html:57
+#: html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(空)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(件åãªã—)"
+
+#: html/Admin/Elements/SelectRights:72
+#: html/Elements/EditCustomFieldSelect:69
+#: html/Elements/SelectCustomFieldValue:51
+#: html/Elements/ShowCustomFields:54
+#: html/Search/Chart:56
+#: html/Search/Elements/Chart:76
+#: lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(値ãªã—)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr ""
+
+#: html/Elements/EditLinks:132
+#: html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(ã²ã¨ã¤ã®ãƒã‚±ãƒƒãƒˆã®ã¿)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(承èªå¾…ã¡)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(ä»–ã®ä½œæ¥­å¾…ã¡)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(必須項目ã§ã™)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(件åãªã—)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "ç§ãŒæ‰€æœ‰ã—ã¦ã„る最優先ãƒã‚±ãƒƒãƒˆ25件"
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "ç§ãŒãƒªã‚¯ã‚¨ã‚¹ãƒˆã—ãŸæœ€å„ªå…ˆãƒã‚±ãƒƒãƒˆ25件"
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr ""
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr ""
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+#: html/Elements/CreateTicket:47
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"æ–°è¦ãƒã‚±ãƒƒãƒˆä½œæˆ:\" />&nbsp;%1"
+
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+#: docs/design_docs/string-extraction-guide.txt:54
+#: lib/RT/StyleGuide.pod:787
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"æ–°è¦ä½œæˆ\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:174
+#: lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACEã¯ã¿ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACEã¯ä½œæˆã€å‰Šé™¤ã®ã¿ã§ã™ã€‚"
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "æ„図的ã§ãªã„ãƒã‚±ãƒƒãƒˆã®ä¿®æ­£ã‚’防ããŸã‚ã«å¼·åˆ¶çµ‚了ã—ã¾ã™ã€‚\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "個人情報"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "アクセスコントロール"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "アクション"
+
+#. ($args{'ScripAction'})
+#: lib/RT/Scrip_Overlay.pm:172
+msgid "Action %1 not found"
+msgstr "アクション%1ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr ""
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "追加ã™ã‚‹"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "管ç†Ccを追加ã™ã‚‹"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Ccを追加ã™ã‚‹"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "コラムを追加"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr ""
+
+#: html/Ticket/Create.html:147
+#: html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "ã•ã‚‰ã«ãƒ•ã‚¡ã‚¤ãƒ«ã‚’追加"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "作æˆè€…を追加ã™ã‚‹"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "æ–°ã—ã„グローãƒãƒ«ã‚¹ã‚¯ãƒªãƒ—トを追加ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "ã“ã®ã‚­ãƒ¥ãƒ¼ã«ã‚¹ã‚¯ãƒªãƒ—トを追加ã™ã‚‹"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "ã™ã¹ã¦ã®ã‚­ãƒ¥ãƒ¼ã«é©ç”¨ã™ã‚‹ã‚¹ã‚¯ãƒªãƒ—トを追加ã™ã‚‹"
+
+#: html/Search/Build.html:109
+#: html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "追加ã—ã¦æ¤œç´¢"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "é¸æŠžã•ã‚ŒãŸãƒã‚±ãƒƒãƒˆã¸ã®ã‚³ãƒ¡ãƒ³ãƒˆã¾ãŸã¯è¿”事を追加ã™ã‚‹"
+
+#: html/Admin/Groups/Members.html:63
+#: html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "メンãƒãƒ¼ã‚’追加ã™ã‚‹"
+
+#: html/Admin/Queues/People.html:87
+#: html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "æ–°ã—ã„ウォッãƒãƒ£ãƒ¼ã‚’追加ã™ã‚‹"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "ã“れらã®æ¤œç´¢æ¡ä»¶ã‚’追加"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:763
+msgid "Added principal as a %1 for this queue"
+msgstr "ã“ã®ã‚­ãƒ¥ãƒ¼ã«%1ã®è²¬ä»»è€…を追加ã—ã¾ã—ãŸ"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1455
+msgid "Added principal as a %1 for this ticket"
+msgstr "ã“ã®ãƒã‚±ãƒƒãƒˆã«%1ã®è²¬ä»»è€…を追加ã—ã¾ã—ãŸ"
+
+#: html/Admin/Users/Modify.html:146
+#: html/User/Prefs.html:133
+msgid "Address1"
+msgstr "ä½æ‰€1"
+
+#: html/Admin/Users/Modify.html:151
+#: html/User/Prefs.html:137
+msgid "Address2"
+msgstr "ä½æ‰€2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "管ç†Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr ""
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr ""
+
+#: html/Admin/Queues/index.html:46
+#: html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "管ç†ã‚­ãƒ¥ãƒ¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "管ç†ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+#: html/Admin/Global/index.html:47
+#: html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "管ç†/グローãƒãƒ«è¨­å®š"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "管ç†/グループ"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "管ç†/キュー/基本"
+
+#: etc/initialdata:56
+#: html/Ticket/Elements/ShowPeople:60
+#: lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "管ç†Cc"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "管ç†ã‚°ãƒ«ãƒ¼ãƒ—"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "管ç†ã‚°ãƒ«ãƒ¼ãƒ—メンãƒãƒ¼"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "管ç†ã‚­ãƒ¥ãƒ¼"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "管ç†ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+#: html/Admin/Queues/People.html:69
+#: html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "管ç†è€…Cc"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "詳細"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "絞込ã¿æ¤œç´¢"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "ãŒä»¥ä¸‹ã‚ˆã‚Šå¾Œã§ã‚ã‚‹"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr ""
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "ã™ã¹ã¦ã®ã‚­ãƒ¥ãƒ¼"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:73
+#: html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr ""
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "承èªçŠ¶æ³"
+
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#: html/Approvals/Display.html:65
+#: html/Approvals/Elements/ShowDependency:63
+#: html/Approvals/index.html:86
+msgid "Approval #%1: %2"
+msgstr ""
+
+#. ($ticket->Id)
+#: html/Approvals/index.html:75
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr ""
+
+#. ($ticket->Id)
+#: html/Approvals/index.html:73
+msgid "Approval #%1: Notes recorded"
+msgstr ""
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr ""
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr ""
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "承èª"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr ""
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "4月"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "昇順"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142
+#: html/SelfService/Update.html:87
+#: html/Ticket/ModifyAll.html:115
+#: html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "添付"
+
+#: html/SelfService/Create.html:92
+#: html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "添付ファイル"
+
+#: html/SelfService/Update.html:75
+#: html/Ticket/Create.html:131
+#: html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr ""
+
+#. ($Attachment)
+#: html/Ticket/ShowEmailRecord.html:52
+#: html/Ticket/ShowEmailRecord.html:56
+#: html/Ticket/ShowEmailRecord.html:59
+msgid "Attachment '%1' could not be loaded"
+msgstr "添付ファイル%1ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "添付ファイルãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "添付ファイルå"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "添付ファイル"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr ""
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "8月"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "èªè¨¼ã‚·ã‚¹ãƒ†ãƒ "
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr ""
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "リクエストã—ãŸäººã«è‡ªå‹•è¿”ä¿¡"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "ä¸æ­£ãªPGPç½²å: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "ä¸æ­£ãªæ·»ä»˜IDã§ã™ã€‚添付ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“'%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "%1ã®ä¸æ­£ãƒ‡ãƒ¼ã‚¿ã§ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr ""
+"添付ファイルã«ã¨ã£ã¦ä¸æ­£ãªãƒˆãƒ©ãƒ³ã‚¶ã‚¯ã‚·ãƒ§ãƒ³ãƒŠãƒ³ãƒãƒ¼ã§ã™ã€‚%1ã¯%2\\n"
+"ã®ã¯ãšã§ã™"
+
+#: html/Admin/Elements/CustomFieldTabs:65
+#: html/Admin/Elements/GroupTabs:60
+#: html/Admin/Elements/QueueTabs:60
+#: html/Admin/Elements/UserTabs:58
+#: html/Ticket/Elements/Tabs:113
+#: html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "基本項目"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91
+#: html/Admin/CustomFields/UserRights.html:74
+#: html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "本当ã«å¤‰æ›´ã‚’ä¿å­˜ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹"
+
+#: html/Elements/SelectDateRelation:55
+#: lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "ãŒä»¥ä¸‹ã‚ˆã‚Šå‰ã§ã‚ã‚‹"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "ã“ã®æ¤œç´¢ã«ãƒ–ックマークã®ã§ãã‚‹URLã§ã™"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64
+#: html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "短ã„ヘッダー"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "ã¾ã¨ã‚ã¦æ›´æ–°"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "ãƒã‚±ãƒƒãƒˆã‚’一括ã—ã¦æ›´æ–°"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "システムユーザーを修正ã§ãã¾ã›ã‚“"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "åå‰ãªã—ã«ã‚«ã‚¹ã‚¿ãƒ ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ãƒãƒªãƒ¥ãƒ¼ã®è¿½åŠ ã¯ã§ãã¾ã›ã‚“"
+
+#. ($Class)
+#: html/Admin/CustomFields/Objects.html:86
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "ãƒã‚±ãƒƒãƒˆè‡ªä½“ã«ã¯ãƒªãƒ³ã‚¯ã§ãã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "ã™ã§ã«çµåˆã—ãŸãƒã‚±ãƒƒãƒˆã«ã¯çµåˆã§ãã¾ã›ã‚“。ã“ã®ã‚¨ãƒ©ãƒ¼ã¯æ±ºã—ã¦å‡ºã•ãªã„ã§ãã ã•ã„"
+
+#. (loc($self->{SearchType}))
+#: html/Widgets/SavedSearch:63
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr ""
+
+#: lib/RT/Record.pm:1282
+#: lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "ベースã¨ã‚¿ãƒ¼ã‚²ãƒƒãƒˆä¸¡æ–¹ã‚’指定ã™ã‚‹äº‹ã¯ã§ãã¾ã›ã‚“"
+
+#. ($msg)
+#: html/autohandler:204
+msgid "Cannot create user: %1"
+msgstr "ユーザー: %1を作æˆã§ãã¾ã›ã‚“"
+
+#: html/Admin/Elements/AddCustomFieldValue:62
+#: html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50
+#: html/Admin/Queues/People.html:65
+#: html/SelfService/Create.html:71
+#: html/Ticket/Create.html:88
+#: html/Ticket/Elements/EditPeople:72
+#: html/Ticket/Elements/ShowPeople:56
+#: html/Ticket/Update.html:83
+#: lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "パスワードを変更ã™ã‚‹"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr ""
+
+#: html/SelfService/Update.html:78
+#: html/Ticket/Create.html:134
+#: html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "ボックスã®ãƒã‚§ãƒƒã‚¯ã§å‰Šé™¤"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "ボックスã®ãƒã‚§ãƒƒã‚¯ã§æ¨©åˆ©ã‚’剥脱"
+
+#: html/Elements/EditLinks:148
+#: html/Elements/EditLinks:85
+#: html/Elements/ShowLinks:78
+#: html/Ticket/Create.html:223
+#: html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "下ä½ãƒã‚±ãƒƒãƒˆ"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "カレンダーã‹ã‚‰é¸æŠž"
+
+#: html/Admin/Users/Modify.html:156
+#: html/User/Prefs.html:141
+msgid "City"
+msgstr "市町æ‘"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "ウィンドウを閉ã˜ã‚‹"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "解決日時"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "終了ã—ãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã§ã™"
+
+#: html/SelfService/Closed.html:46
+#: html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "ç†è§£ã—ã¦ã„ãªã„コマンド!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190
+#: html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "コメント"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "コメントアドレス"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "記録ã•ã‚Œã¦ã„ãªã„コメント"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "コメント"
+
+#: html/Ticket/ModifyAll.html:91
+#: html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "コメント(リクエストã—ãŸäººã«ã¯é€ä¿¡ã•ã‚Œã¾ã›ã‚“)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "コメント(リクエストã—ãŸäººã«ã¯é€ä¿¡ã•ã‚Œã¾ã›ã‚“)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "%1ã«ã¤ã„ã¦ã®ã‚³ãƒ¡ãƒ³ãƒˆ"
+
+#: html/Admin/Users/Modify.html:225
+#: html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã¤ã„ã¦ã®ã‚³ãƒ¡ãƒ³ãƒˆ"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "コメントãŒè¿½åŠ ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "コメントãŒçŸ­ãã•ã‚Œã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "æ¡ä»¶"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "æ¡ä»¶ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "システム設定"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "確èª"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "コンタクト情報"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "コンタクトã•ã‚ŒãŸæ—¥ã«ã¡'%1'を解æžã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/Admin/Elements/ModifyTemplate:65
+#: html/Elements/SelectAttachmentField:48
+#: html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "コンテント"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr ""
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "メールアドレス"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "通信ãŒè¿½åŠ ã•ã‚Œã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "記録ã•ã‚Œã¦ã„ãªã„通信"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "ãƒã‚±ãƒƒãƒˆã®æ–°ã—ã„カスタムフィールドãƒãƒªãƒ¥ãƒ¼ã‚’追加ã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr ""
+
+#. (, $value_msg)
+#: lib/RT/Record.pm:1660
+msgid "Could not add new custom field value. %1 "
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3048
+#: lib/RT/Ticket_Overlay.pm:3056
+#: lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "担当者変更ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($msg)
+#: html/Admin/CustomFields/Modify.html:161
+msgid "Could not create CustomField"
+msgstr "カスタムフィールドã®ä½œæˆãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($msg)
+#: html/Admin/Elements/EditCustomField:113
+msgid "Could not create CustomField: %1"
+msgstr ""
+
+#: html/User/Groups/Modify.html:98
+#: lib/RT/Group_Overlay.pm:494
+#: lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "グループã®ä½œæˆãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($msg)
+#: html/Admin/Global/Template.html:96
+#: html/Admin/Queues/Template.html:93
+msgid "Could not create template: %1"
+msgstr "テンプレート: %1ã®ä½œæˆãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Ticket_Overlay.pm:1075
+#: lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "ãƒã‚±ãƒƒãƒˆã®ä½œæˆãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚キューãŒã‚»ãƒƒãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: lib/RT/User_Overlay.pm:255
+#: lib/RT/User_Overlay.pm:269
+#: lib/RT/User_Overlay.pm:278
+#: lib/RT/User_Overlay.pm:287
+#: lib/RT/User_Overlay.pm:296
+#: lib/RT/User_Overlay.pm:310
+#: lib/RT/User_Overlay.pm:320
+#: lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "ユーザーã®ä½œæˆãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "ãƒã‚±ãƒƒãƒˆã¨ãã®ID%1ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "グループ %1ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Queue_Overlay.pm:741
+#: lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’作æˆã¾ãŸã¯è¦‹ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Queue_Overlay.pm:802
+#: lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "ãã®è²¬ä»»è€…を見ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "ユーザー%1を見ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:112
+#: html/User/Groups/Members.html:111
+#: html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "グループをロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($privacy)
+#: lib/RT/SavedSearch.pm:119
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr ""
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:761
+msgid "Could not make that principal a %1 for this queue"
+msgstr "ã“ã®ã‚­ãƒ¥ãƒ¼ã§ãã®è²¬ä»»è€…ã‚’%1ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1444
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "ã“ã®ãƒã‚±ãƒƒãƒˆã§ãã®è²¬ä»»è€…ã‚’%1ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:860
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "ã“ã®ã‚­ãƒ¥ãƒ¼ã§ãã®è²¬ä»»è€…ã‚’%1ã¨ã—ã¦å‰Šé™¤ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "ã“ã®ãƒã‚±ãƒƒãƒˆã§ãã®è²¬ä»»è€…ã‚’%1ã¨ã—ã¦å‰Šé™¤ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "グループã«ãƒ¡ãƒ³ãƒãƒ¼ã®è¿½åŠ ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($Msg)
+#: lib/RT/Record.pm:1719
+#: lib/RT/Record.pm:1771
+msgid "Couldn't create a transaction: %1"
+msgstr "トランザクション: %1ã®ä½œæˆãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr ""
+"GPGã®è¿”事\\n"
+"ã‹ã‚‰ä½•ã‚’è¡Œã£ãŸã‚‰ã‚ˆã„ã®ã‹ã‚ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr ""
+"グループ\\n"
+"ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "責任者ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "ãã®å€¤ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr ""
+"ユーザー\\n"
+"ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($self->Id)
+#: lib/RT/CurrentUser.pm:145
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr ""
+"ユーザーデータベース\\n"
+"ã‹ã‚‰%1をロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($id)
+#: html/Admin/CustomFields/UserRights.html:149
+msgid "Couldn't load Class %1"
+msgstr ""
+
+#. ($id)
+#: html/Admin/CustomFields/GroupRights.html:107
+msgid "Couldn't load CustomField %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "RT設定ファイル'%1' %2をロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "スクリプトをロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($self->Id)
+#: lib/RT/Ticket_Overlay.pm:2016
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#. ($id)
+#: html/Admin/Groups/GroupRights.html:109
+#: html/Admin/Groups/UserRights.html:96
+msgid "Couldn't load group %1"
+msgstr "グループ%1をロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Link_Overlay.pm:202
+#: lib/RT/Link_Overlay.pm:211
+#: lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "リンクをロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($id)
+#: html/Admin/Elements/ObjectCustomFields:83
+#: html/Admin/Queues/CustomFields.html:59
+#: html/Admin/Users/CustomFields.html:59
+msgid "Couldn't load object %1"
+msgstr ""
+
+#. ($id)
+#: html/Admin/Queues/People.html:142
+msgid "Couldn't load queue"
+msgstr "キューをロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($id)
+#: html/Admin/Queues/GroupRights.html:122
+#: html/Admin/Queues/UserRights.html:93
+msgid "Couldn't load queue %1"
+msgstr "キュー%1をロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "スクリプトをロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($id)
+#: html/Admin/Elements/EditScrip:126
+#: html/Admin/Elements/EditScrip:167
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "テンプレートをロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ï¼ˆ%1)をロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($id)
+#: html/SelfService/Display.html:158
+#: lib/RT/Action/CreateTickets.pm:680
+msgid "Couldn't load ticket '%1'"
+msgstr "ãƒã‚±ãƒƒãƒˆ'%1'をロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($args{'URI'})
+#: lib/RT/Ticket_Overlay.pm:2643
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173
+#: html/User/Prefs.html:153
+msgid "Country"
+msgstr "国"
+
+#: html/Admin/Elements/CreateUserCalled:47
+#: html/Admin/Elements/EditCustomField:84
+#: html/Admin/Elements/EditScrip:133
+#: html/Admin/Queues/Template.html:66
+#: html/Elements/QuickCreate:65
+#: html/Ticket/Create.html:168
+#: html/Ticket/Create.html:235
+msgid "Create"
+msgstr "作æˆ"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:150
+#: html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "カスタムフィールドã®ä½œæˆ"
+
+#. ($QueueObj->Name())
+#: html/Admin/Queues/CustomField.html:69
+msgid "Create a CustomField for queue %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "æ–°ã—ã„カスタムフィールドã®ä½œæˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "æ–°ã—ã„グローãƒãƒ«ã‚¹ã‚¯ãƒªãƒ—トã®ä½œæˆ"
+
+#: html/Admin/Groups/Modify.html:125
+#: html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "æ–°ã—ã„グループã®ä½œæˆ"
+
+#: html/User/Groups/Modify.html:113
+#: html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "æ–°ã—ã„個人グループã®ä½œæˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "æ–°ã—ã„キューã®ä½œæˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "æ–°ã—ã„テンプレートã®ä½œæˆ"
+
+#: html/Ticket/Create.html:47
+#: html/Ticket/Create.html:51
+#: html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "æ–°è¦ãƒã‚±ãƒƒãƒˆä½œæˆ"
+
+#: html/Admin/Users/Modify.html:252
+#: html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ä½œæˆ"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "キューã®ä½œæˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "呼ã³å‡ºã•ã‚ŒãŸã‚­ãƒ¥ãƒ¼ã®ä½œæˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "リクエストã®ä½œæˆ"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrip.html:89
+msgid "Create a scrip for queue %1"
+msgstr ""
+
+#: html/Admin/Global/Template.html:90
+#: html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "テンプレートã®ä½œæˆ"
+
+#: html/SelfService/Create.html:46
+#: html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr ""
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr ""
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "ãƒã‚±ãƒƒãƒˆã®ä½œæˆ"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr ""
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr ""
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr ""
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr ""
+
+#: html/Elements/SelectDateType:47
+#: html/Ticket/Elements/ShowDates:48
+#: lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "作æˆæ—¥æ™‚"
+
+#. ($CustomFieldObj->Name())
+#: html/Admin/CustomFields/Modify.html:163
+#: html/Admin/Elements/EditCustomField:117
+msgid "Created CustomField %1"
+msgstr "カスタムフィールド%1を作æˆã—ã¾ã—ãŸ"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "作æˆã•ã‚ŒãŸå…¨ãƒã‚±ãƒƒãƒˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "テンプレート%1を作æˆã—ã¾ã—ãŸ"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "作æˆè€…"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "ç¾åœ¨ã®ãƒªãƒ³ã‚¯"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:60
+#: html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "ç¾åœ¨ã®ãƒ¡ãƒ³ãƒãƒ¼"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "ç¾åœ¨ã®æ¨©åˆ©"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "ç¾åœ¨ã®æ¤œç´¢å¼"
+
+#: html/Admin/Queues/People.html:62
+#: html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "ç¾åœ¨ã®ã‚¦ã‚©ãƒƒãƒãƒ£ãƒ¼"
+
+#: html/Admin/Elements/SystemTabs:61
+#: html/Admin/Elements/Tabs:62
+#: html/Admin/Global/index.html:71
+#: html/Admin/Users/Modify.html:205
+#: html/Admin/index.html:77
+#: html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "カスタムフィールド"
+
+#. ($lookup)
+#: html/Admin/CustomFields/index.html:60
+msgid "Custom Fields for %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "カスタムフィールド%1 %2 %3"
+
+#. ($CF->Name)
+#: lib/RT/Tickets_Overlay.pm:2424
+msgid "Custom field %1 has a value."
+msgstr "カスタムフィールド%1ã¯å€¤ãŒå…¥åŠ›ã•ã‚Œã¦ã„ã¾ã™"
+
+#. ($CF->Name)
+#: lib/RT/Tickets_Overlay.pm:2420
+msgid "Custom field %1 has no value."
+msgstr "カスタムフィールド%1ã¯å€¤ãŒã‚ã‚Šã¾ã›ã‚“"
+
+#. ($args{'Field'})
+#: lib/RT/Record.pm:1592
+#: lib/RT/Record.pm:1754
+msgid "Custom field %1 not found"
+msgstr "カスタムフィールド%1ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#. ($cf)
+#. ($obj->Name)
+#: lib/RT/Report/Tickets.pm:118
+#: lib/RT/Report/Tickets.pm:121
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "カスタムフィールドãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#. ($args{'Content'}, $self->Name)
+#: lib/RT/CustomField_Overlay.pm:1157
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "カスタムフィールド%2ã®ãŸã‚ã®ã‚«ã‚¹ã‚¿ãƒ ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ãƒãƒªãƒ¥ãƒ¼%1ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "カスタムフィールドãŒ%1ã‹ã‚‰%2ã«å¤‰æ›´ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "カスタムフィールドãƒãƒªãƒ¥ãƒ¼ã¯å‰Šé™¤ã•ã‚Œã¾ã›ã‚“"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "カスタムフィールドãƒãƒªãƒ¥ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/CustomField_Overlay.pm:1171
+#: lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "カスタムフィールドãƒãƒªãƒ¥ãƒ¼ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
+
+#: html/Elements/SelectGroups:51
+#: html/Elements/SelectUsers:51
+#: lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr ""
+
+#: html/Prefs/MyRT.html:78
+#: html/Prefs/Quicksearch.html:70
+#: html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "カスタマイズ: "
+
+#: html/SelfService/Display.html:61
+#: html/Ticket/Create.html:203
+#: html/Ticket/Elements/ShowSummary:83
+#: html/Ticket/Elements/Tabs:116
+#: html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "日付"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "12月"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr ""
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr ""
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr ""
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr ""
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr ""
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr ""
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr ""
+
+#: html/User/Delegation.html:46
+#: html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "代表者ã®æ¨©åˆ©"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr ""
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr ""
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:75
+#: html/Search/Elements/EditFormat:103
+#: html/Search/Elements/EditQuery:57
+#: html/Search/Elements/EditSearches:63
+#: html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "削除"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr ""
+
+#. ($msg)
+#: lib/RT/SavedSearch.pm:220
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr ""
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "ã“ã®ã‚ªãƒ–ジェクトを削除ã™ã‚‹ã¨æŒ‡ç¤ºã®å®Œå…¨æ€§ãŒããšã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "ã“ã®ã‚ªãƒ–ジェクトを削除ã™ã‚‹ã¨æŒ‡ç¤ºã®å®Œå…¨æ€§ãŒããšã•ã‚Œã¾ã™"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "ã“ã®ã‚ªãƒ–ジェクトを削除ã™ã‚‹ã¨æŒ‡ç¤ºã®å®Œå…¨æ€§ãŒå¦¨å®³ã•ã‚Œã¾ã™"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr ""
+
+#: html/Elements/EditLinks:140
+#: html/Elements/EditLinks:66
+#: html/Elements/ShowLinks:58
+#: html/Ticket/Create.html:221
+#: html/Ticket/Elements/BulkLinks:56
+#: html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "ä¾å­˜ã•ã‚Œã¦ã„ã‚‹ãƒã‚±ãƒƒãƒˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "従属ãƒã‚±ãƒƒãƒˆ: \\n"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:718
+msgid "Dependency by %1 added"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:758
+msgid "Dependency by %1 deleted"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:715
+msgid "Dependency on %1 added"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:755
+msgid "Dependency on %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:136
+#: html/Elements/EditLinks:57
+#: html/Elements/SelectLinkType:48
+#: html/Elements/ShowLinks:48
+#: html/Ticket/Create.html:220
+#: html/Ticket/Elements/BulkLinks:52
+#: html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "ä¾å­˜ã—ã¦ã„ã‚‹ãƒã‚±ãƒƒãƒˆ"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "詳細"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "é™é †"
+
+#: html/SelfService/Create.html:100
+#: html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "ãƒã‚±ãƒƒãƒˆã®æœ¬æ–‡ã‚’以下ã«æ›¸ã込んã§ãã ã•ã„"
+
+#: html/Admin/CustomFields/Modify.html:61
+#: html/Admin/Elements/AddCustomFieldValue:57
+#: html/Admin/Elements/EditCustomField:60
+#: html/Admin/Elements/EditCustomFieldValues:56
+#: html/Admin/Elements/EditScrip:55
+#: html/Admin/Elements/ModifyTemplate:57
+#: html/Admin/Groups/Modify.html:71
+#: html/Admin/Queues/Modify.html:69
+#: html/Search/Elements/EditSearches:56
+#: html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "詳細"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "詳細"
+
+#: html/Search/Elements/EditFormat:71
+#: html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "表示"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "コラム表示"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "モード切替"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "ãƒã‚±ãƒƒãƒˆ#%1を表示"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "<a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU GPL</a> version 2 ã«ã‚‚ã¨ã¥ã„ã¦é…布"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr ""
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "ページを定期的ã«æ›´æ–°ã—ãªã„"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "ダウンロード"
+
+#: html/Admin/Groups/index.html:61
+#: html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr ""
+
+#: html/Elements/SelectDateType:53
+#: html/Ticket/Create.html:209
+#: html/Ticket/Elements/EditDates:66
+#: html/Ticket/Elements/Reminders:133
+#: html/Ticket/Elements/ShowDates:64
+#: lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "終了予定日時"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "期é™ãŒåˆ‡ã‚Œã‚‹æ—¥'%1'ã¯è§£æžã•ã‚Œã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr ""
+"ERROR: ãƒã‚±ãƒƒãƒˆ '%1': %2.\\n"
+"をロードã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/Elements/Quicksearch:48
+#: html/Elements/ShowSearch:49
+#: html/index.html:107
+msgid "Edit"
+msgstr "編集"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#. ($Object->Name)
+#: html/Admin/Elements/ObjectCustomFields:92
+#: html/Admin/Queues/CustomFields.html:64
+#: html/Admin/Users/CustomFields.html:64
+msgid "Edit Custom Fields for %1"
+msgstr "%1ã®ã‚«ã‚¹ã‚¿ãƒ ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã‚’編集ã™ã‚‹"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54
+#: html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188
+#: html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "リンク関係を編集ã™ã‚‹"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "クエリã®ç›´æŽ¥ç·¨é›†"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "検索を編集"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Templates.html:63
+msgid "Edit Templates for queue %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "スクリプトを編集ã™ã‚‹"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60
+#: html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "システムテンプレートを編集ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "%1ã®ãƒ†ãƒ³ãƒ—レートを編集ã™ã‚‹"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr ""
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Modify.html:140
+msgid "Editing Configuration for queue %1"
+msgstr "キュー%1ã®è¨­å®šã‚’編集ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "ユーザー%1ã®è¨­å®šã‚’編集ã™ã‚‹"
+
+#. ($CustomFieldObj->Name())
+#: html/Admin/CustomFields/Modify.html:167
+#: html/Admin/Elements/EditCustomField:120
+msgid "Editing CustomField %1"
+msgstr "カスタムフィールド%1を編集ã™ã‚‹"
+
+#. ($Group->Name)
+#: html/Admin/Groups/Members.html:53
+msgid "Editing membership for group %1"
+msgstr "グループ%1ã®ãƒ¡ãƒ³ãƒãƒ¼ã‚’編集ã™ã‚‹"
+
+#. ($Group->Name)
+#: html/User/Groups/Members.html:150
+msgid "Editing membership for personal group %1"
+msgstr "個人グループ%1ã®ãƒ¡ãƒ³ãƒãƒ¼ã‚’編集ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "テンプレート%1を編集ã™ã‚‹"
+
+#: lib/RT/Record.pm:1295
+#: lib/RT/Record.pm:1372
+#: lib/RT/Ticket_Overlay.pm:2518
+#: lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "ベースもã—ãã¯ã‚¿ãƒ¼ã‚²ãƒƒãƒˆã‚’指定ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“"
+
+#: html/Admin/Users/Modify.html:74
+#: html/Ticket/Elements/AddWatchers:77
+#: html/User/Prefs.html:65
+msgid "Email"
+msgstr "Eメール"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "ãŠä½¿ã„ã®Eメールアドレス"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "Eメールアドレス"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "Eメールエンコーディング"
+
+#: html/Admin/CustomFields/Modify.html:98
+#: html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:84
+#: html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "有効ã«ãªã‚Šã¾ã—ãŸï¼ˆã‚‚ã†ä¸€åº¦ã“ã®ãƒœãƒƒã‚¯ã‚¹ã‚’ãƒã‚§ãƒƒã‚¯ã™ã‚‹ã¨ã“ã®ã‚­ãƒ¥ãƒ¼ã¯æœ‰åŠ¹ã§ãªããªã‚Šã¾ã™ï¼‰"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "有効ãªã‚­ãƒ¥ãƒ¼"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/Elements/EditCustomField:136
+#: html/Admin/Groups/Modify.html:150
+#: html/Admin/Users/Modify.html:350
+#: html/User/Groups/Modify.html:138
+msgid "Enabled status %1"
+msgstr "有効ãªã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹%1"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/CustomFields/Modify.html:185
+#: html/Admin/Queues/Modify.html:162
+msgid "Enabled status: %1"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr ""
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr ""
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr ""
+
+#: html/Elements/EditLinks:119
+#: html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "ãƒã‚±ãƒƒãƒˆã‚’リンクã™ã‚‹ãƒã‚±ãƒƒãƒˆã¾ãŸã¯URLsを入力ã—ã¦ãã ã•ã„。入力ã™ã‚‹é …ç›®ãŒã„ãã¤ã‹ã‚ã‚‹å ´åˆã«ã¯ã‚¹ãƒšãƒ¼ã‚¹ã§åŒºåˆ‡ã£ã¦ãã ã•ã„。"
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr ""
+
+#: html/Elements/Login:76
+#: html/SelfService/Error.html:46
+#: html/SelfService/Error.html:47
+msgid "Error"
+msgstr "エラー"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "パラメーターã®ã‚¨ãƒ©ãƒ¼Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "パラメーターã®ã‚¨ãƒ©ãƒ¼Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "パラメーターã®ã‚¨ãƒ©ãƒ¼Ticket->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "パラメーターã®ã‚¨ãƒ©ãƒ¼Ticket->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr ""
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr ""
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr ""
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "外部ã®èªè¨¼ID"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "外部ã®ã‚³ãƒ³ã‚¿ã‚¯ãƒˆæƒ…å ±ID"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "ãã®ä»–ã®æƒ…å ±"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "'特権ã®ã‚ã‚‹'ユーザーã®æ“¬ä¼¼ã‚°ãƒ«ãƒ¼ãƒ—ã®æ¤œç´¢ãŒå¤±æ•—ã—ã¾ã—ãŸ"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "'特権ã®ãªã„'ユーザーã®æ“¬ä¼¼ã‚°ãƒ«ãƒ¼ãƒ—ã®æ¤œç´¢ãŒå¤±æ•—ã—ã¾ã—ãŸ"
+
+#. ($modname, $@)
+#: bin/rt-crontool:206
+msgid "Failed to load module %1. (%2)"
+msgstr ""
+
+#. ($privacy)
+#: lib/RT/SavedSearch.pm:152
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "2月"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107
+#: html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "終了"
+
+#: html/Search/Elements/PickBasics:149
+#: html/Ticket/Create.html:182
+#: html/Ticket/Elements/EditBasics:97
+#: lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "最終優先順ä½"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr ""
+
+#: html/Admin/Groups/index.html:72
+#: html/Admin/Queues/People.html:82
+#: html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "グループをã•ãŒã™:"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "æ–°ã—ã„/é–‹ããƒã‚±ãƒƒãƒˆã‚’見ã¤ã‘ã‚‹"
+
+#: html/Admin/Queues/People.html:78
+#: html/Admin/Users/index.html:70
+#: html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "個人をã•ãŒã™:"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "最åˆã®"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "最åˆã®ãƒšãƒ¼ã‚¸"
+
+#: docs/design_docs/string-extraction-guide.txt:33
+#: lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24
+#: lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "ã°ã‹ï¼"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "変更を強制ã—ã¾ã™"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "表示フォーマット"
+
+#. ($ticketcount)
+#: html/Search/Results.html:145
+msgid "Found %quant(%1,ticket)"
+msgstr ""
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "コンタクト情報(書å¼è‡ªç”±)"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "金曜日"
+
+#: html/Ticket/Elements/ShowHistory:66
+#: html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "フルヘッダー"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "PGPç½²åã‹ã‚‰ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’å¾—ã‚‹\\n"
+
+#. ($New->Name)
+#: lib/RT/Transaction_Overlay.pm:684
+msgid "Given to %1"
+msgstr "担当者変更: %1"
+
+#: html/Admin/Elements/Tabs:65
+#: html/Admin/index.html:82
+msgid "Global"
+msgstr "グローãƒãƒ«"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "グローãƒãƒ«ã‚¹ã‚¯ãƒªãƒ—ト"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#. ($pane)
+#: html/Admin/Global/MyRT.html:48
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#. (loc($Template->Name))
+#: html/Admin/Elements/SelectTemplate:59
+msgid "Global template: %1"
+msgstr ""
+
+#: html/Admin/CustomFields/index.html:80
+#: html/Search/Results.html:90
+#: html/Tools/Offline.html:89
+msgid "Go"
+msgstr ""
+
+#: html/Admin/Groups/index.html:67
+#: html/Admin/Groups/index.html:73
+#: html/Admin/Queues/People.html:80
+#: html/Admin/Queues/People.html:84
+#: html/Admin/Queues/index.html:66
+#: html/Admin/Users/index.html:73
+#: html/Elements/RefreshHomepage:48
+#: html/Search/Results.html:74
+#: html/Ticket/Elements/EditPeople:53
+#: html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr ""
+"%1\\n"
+"ã‹ã‚‰ã®æ­£ã—ã„PGPç½²å"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "ページã¸ç§»å‹•"
+
+#: html/Elements/GotoTicket:46
+#: html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "ãƒã‚±ãƒƒãƒˆã«ç§»å‹•"
+
+#: html/Ticket/Elements/AddWatchers:67
+#: html/Ticket/Elements/ShowGroupMembers:55
+#: html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "グループ%1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68
+#: html/Admin/Elements/GroupTabs:66
+#: html/Admin/Elements/QueueTabs:82
+#: html/Admin/Elements/SystemTabs:65
+#: html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "グループ権利"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "グループã«ã¯ã™ã§ã«ãƒ¡ãƒ³ãƒãƒ¼ãŒã„ã¾ã™"
+
+#. ($create_msg)
+#: html/Admin/Groups/Modify.html:109
+msgid "Group could not be created: %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "グループãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:963
+#: lib/RT/Queue_Overlay.pm:748
+#: lib/RT/Queue_Overlay.pm:808
+#: lib/RT/Ticket_Overlay.pm:1430
+#: lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "グループãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "グループãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "グループãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59
+#: html/Admin/Elements/SelectNewGroupMembers:57
+#: html/Admin/Elements/Tabs:56
+#: html/Admin/Global/CustomFields/index.html:69
+#: html/Admin/Groups/Members.html:86
+#: html/Admin/Queues/People.html:104
+#: html/Admin/Users/Memberships.html:53
+#: html/Admin/index.html:67
+#: html/User/Groups/Members.html:88
+#: lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "グループ"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "グループã¯å½¼ã‚‰ã®ãƒ¡ãƒ³ãƒãƒ¼ã«ã¯ãªã‚Œã¾ã›ã‚“"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr ""
+
+#: lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "ã“ã‚“ã«ã¡ã¯ï¼"
+
+#. ($name)
+#: docs/design_docs/string-extraction-guide.txt:40
+#: lib/RT/StyleGuide.pod:773
+msgid "Hello, %1"
+msgstr "ã“ã‚“ã«ã¡ã¯ã€%1ã•ã‚“"
+
+#: html/Admin/Elements/GroupTabs:70
+#: html/Admin/Elements/UserTabs:64
+#: html/Ticket/Elements/ShowHistory:53
+#: html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "更新履歴"
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/History.html:62
+msgid "History of the group %1"
+msgstr ""
+
+#. ($UserObj->Name)
+#: html/Admin/Users/History.html:62
+msgid "History of the user %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "電話(自宅)"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "ホーム"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "時間"
+
+#. (6)
+#: lib/RT/Base.pm:119
+msgid "I have %quant(%1,concrete mixer)."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "ç§ã¯[quant,_1,concrete mixer]ãŒã‚ã‚Šã¾ã™ã€‚"
+
+#: html/Search/Build.html:460
+#: lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48
+#: lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "ID"
+
+#: html/Admin/Users/Modify.html:65
+#: html/User/Prefs.html:60
+msgid "Identity"
+msgstr "個人識別情報"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr ""
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr ""
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr ""
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr ""
+
+#: html/Admin/Queues/People.html:126
+#: html/Ticket/Modify.html:60
+#: html/Ticket/ModifyAll.html:128
+#: html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "変更をå映ã™ã‚‹ã«ã¯ãƒœã‚¿ãƒ³ã‚’押ã—ã¦ãã ã•ã„"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr ""
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr ""
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr ""
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "利用åœæ­¢ã—ãŸã‚­ãƒ¥ãƒ¼ã‚‚リストã«å«ã‚ã‚‹"
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "利用åœæ­¢ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚‚検索ã«å«ã‚ã‚‹"
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486
+#: lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483
+#: lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:148
+#: lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "最åˆã®å„ªå…ˆæ¨©"
+
+#: lib/RT/Ticket_Overlay.pm:1163
+#: lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "入力エラー"
+
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+#: html/Elements/ValidateCustomFields:68
+#: lib/RT/CustomField_Overlay.pm:1021
+#: lib/RT/CustomField_Overlay.pm:1162
+msgid "Input must match %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr ""
+
+#. ($id->{error_message})
+#: lib/RT/Record.pm:308
+msgid "Internal Error: %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "無効ãªã‚°ãƒ«ãƒ¼ãƒ—タイプã§ã™"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr ""
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "無効ãªæ‹…当者ã§ã™ã€‚ 'nobody'ã«è¨­å®šã—ã¾ã™."
+
+#. ($msg)
+#: lib/RT/CustomField_Overlay.pm:207
+#: lib/RT/CustomField_Overlay.pm:678
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157
+#: lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "無効ãªã‚­ãƒ¥ãƒ¼ã§ã™"
+
+#: lib/RT/ACE_Overlay.pm:264
+#: lib/RT/ACE_Overlay.pm:273
+#: lib/RT/ACE_Overlay.pm:279
+#: lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "無効ãªæ¨©åˆ©ã§ã™"
+
+#. ($key)
+#: lib/RT/Record.pm:283
+msgid "Invalid value for %1"
+msgstr "%1ã¯ç„¡åŠ¹ãªå€¤ã§ã™"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "カスタムフィールドã«ç„¡åŠ¹ãªå€¤ã§ã™"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "ステータスã«ã¯ç„¡åŠ¹ãªå€¤ã§ã™"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr ""
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "1月"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr ""
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "7月"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "ç·åˆãƒ“ュー"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "6月"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "キーワード"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "言語"
+
+#: html/Admin/Users/Modify.html:94
+#: html/User/Prefs.html:76
+msgid "Language"
+msgstr "言語"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr ""
+
+#: html/Ticket/Elements/EditDates:59
+#: html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "最終コンタクト"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "最終コンタクト"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "最終更新日時"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "残り時間"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’RTã«ã‚¢ã‚¯ã‚»ã‚¹å¯èƒ½ã«ã—ã¾ã™"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ¨©åˆ©ã‚’èªã‚ã¾ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "担当者を%1 %2ã«åˆ¶é™ã—ã¾ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "キューを%1 %2ã«åˆ¶é™ã—ã¾ã™"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "リンク"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "ã™ã§ã«ãƒªãƒ³ã‚¯ã—ã¦ã„ã¾ã™"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "リンクãŒä½œæˆã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($TransString)
+#: lib/RT/Record.pm:1326
+msgid "Link created (%1)"
+msgstr "リンクãŒä½œæˆã•ã‚Œã¾ã—ãŸï¼ˆ%1)"
+
+#. ($TransString)
+#: lib/RT/Record.pm:1387
+msgid "Link deleted (%1)"
+msgstr "リンクãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸï¼ˆ%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "リンクãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#. ($Ticket->Id)
+#: html/Ticket/ModifyLinks.html:46
+#: html/Ticket/ModifyLinks.html:50
+msgid "Link ticket #%1"
+msgstr "リンクãƒã‚±ãƒƒãƒˆ#%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216
+#: html/Ticket/Elements/ShowSummary:89
+#: html/Ticket/Elements/Tabs:120
+#: html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "リンク"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "読ã¿è¾¼ã¿"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "ロードã•ã‚ŒãŸPerlモジュール"
+
+#. ($self->Name)
+#: lib/RT/SavedSearch.pm:111
+msgid "Loaded search %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:138
+#: html/User/Prefs.html:126
+msgid "Location"
+msgstr "ä½æ‰€"
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"Log directory %1 not found or couldn't be written.\\n"
+" RT can't run."
+msgstr ""
+"ログディレクトリー%1ãŒè¦‹ã¤ã‹ã‚‰ãªã„ã€ã¾ãŸã¯æ›¸ã出ã›ã¾ã›ã‚“。\\n"
+" RTãŒå‹•ãã¾ã›ã‚“"
+
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+#: html/Elements/Header:91
+msgid "Logged in as %1"
+msgstr "\"%1\"ã§ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ã„ã¾ã™"
+
+#: docs/design_docs/string-extraction-guide.txt:71
+#: html/Elements/Login:100
+#: html/Elements/Login:68
+#: html/Elements/Login:84
+#: lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "ログイン"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "ログアウト"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "担当者を決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "ステータスを決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "期é™æœŸæ—¥ã‚’決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "解決期日を決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "開始日を決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "開始日を決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "言ã‚ã‚ŒãŸæ—¥ã‚’決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "優先順ä½ã‚’決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "キューを決ã‚ã‚‹"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "サブジェクトを決ã‚ã‚‹"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr ""
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr ""
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr ""
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr ""
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr ""
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "3月"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "5月"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:731
+msgid "Member %1 added"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:771
+msgid "Member %1 deleted"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "メンãƒãƒ¼ãŒè¿½åŠ ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "メンãƒãƒ¼ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "メンãƒãƒ¼ãŒå‰Šé™¤ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "メンãƒãƒ¼:"
+
+#: html/Admin/Elements/GroupTabs:63
+#: html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "メンãƒãƒ¼"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:728
+msgid "Membership in %1 added"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:768
+msgid "Membership in %1 deleted"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr ""
+
+#. ($UserObj->Name)
+#: html/Admin/Users/Memberships.html:60
+msgid "Memberships of the user %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "çµåˆãŒæˆåŠŸã—ã¾ã—ãŸ"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "çµåˆãŒå¤±æ•—ã—ã¾ã—ãŸã€‚有効ãªIDãŒè¨­å®šã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131
+#: html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "次ã®ãƒã‚±ãƒƒãƒˆã«çµåˆ"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:734
+msgid "Merged into %1"
+msgstr ""
+
+#: html/Search/Bulk.html:143
+#: html/Ticket/Update.html:118
+msgid "Message"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "ã“ã®ãƒã‚±ãƒƒãƒˆã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã¯ä»¥ä¸‹ã®å®›å…ˆã«ã¯é€ä¿¡ã•ã‚Œãªããªã‚Šã¾ã™:"
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "分"
+
+#: html/Search/Build.html:490
+#: lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:193
+#: html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "æºå¸¯"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "æºå¸¯é›»è©±"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "カスタムフィールド%1を修正ã™ã‚‹"
+
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+#: html/Admin/Elements/ObjectCustomFields:96
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr ""
+
+#. (loc(lc($Types)))
+#: html/Admin/Elements/ObjectCustomFields:98
+msgid "Modify Custom Fields which apply to all %1"
+msgstr ""
+
+#: html/Admin/Global/GroupRights.html:106
+#: html/Admin/Groups/GroupRights.html:94
+#: html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:105
+#: html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr ""
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr ""
+
+#: html/Admin/Global/UserRights.html:75
+#: html/Admin/Groups/UserRights.html:76
+#: html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr ""
+
+#. ($QueueObj->Name())
+#: html/Admin/Queues/CustomField.html:66
+msgid "Modify a CustomField for queue %1"
+msgstr ""
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrip.html:82
+msgid "Modify a scrip for queue %1"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr ""
+
+#. ($CF->Name)
+#: html/Admin/CustomFields/Objects.html:90
+msgid "Modify associated objects for %1"
+msgstr ""
+
+#. ($TicketObj->Id)
+#: html/Ticket/ModifyDates.html:46
+#: html/Ticket/ModifyDates.html:50
+msgid "Modify dates for #%1"
+msgstr "#%1ã®æœŸæ—¥ã‚’修正ã™ã‚‹"
+
+#. ($TicketObj->Id)
+#: html/Ticket/ModifyDates.html:57
+msgid "Modify dates for ticket # %1"
+msgstr "ãƒã‚±ãƒƒãƒˆ#%1ã®æœŸæ—¥ã‚’修正ã™ã‚‹"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65
+#: html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70
+#: html/Admin/Global/GroupRights.html:46
+#: html/Admin/Global/GroupRights.html:49
+#: html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "グローãƒãƒ«ã‚°ãƒ«ãƒ¼ãƒ—ã®æ¨©åˆ©ã‚’修正ã™ã‚‹"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "グローãƒãƒ«ã‚°ãƒ«ãƒ¼ãƒ—ã®æ¨©åˆ©ã‚’修正ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "グローãƒãƒ«ã‚¹ã‚¯ãƒªãƒ—トを修正ã™ã‚‹"
+
+#: html/Admin/Global/UserRights.html:46
+#: html/Admin/Global/UserRights.html:49
+#: html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr ""
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "グローãƒãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ¨©åˆ©ã‚’修正ã™ã‚‹"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr ""
+
+#. ($CustomFieldObj->Name)
+#: html/Admin/CustomFields/GroupRights.html:164
+msgid "Modify group rights for custom field %1"
+msgstr ""
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/GroupRights.html:46
+#: html/Admin/Groups/GroupRights.html:50
+#: html/Admin/Groups/GroupRights.html:56
+msgid "Modify group rights for group %1"
+msgstr "%1ã®ã‚°ãƒ«ãƒ¼ãƒ—権利を修正ã™ã‚‹"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/GroupRights.html:46
+#: html/Admin/Queues/GroupRights.html:50
+msgid "Modify group rights for queue %1"
+msgstr "キュー%1ã®ã‚°ãƒ«ãƒ¼ãƒ—権利を修正ã™ã‚‹"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr ""
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/People.html:46
+#: html/Admin/Queues/People.html:50
+msgid "Modify people related to queue %1"
+msgstr "キュー%1ã®é–¢ä¸Žè€…を修正ã™ã‚‹"
+
+#. ($Ticket->id)
+#. ($Ticket->Id)
+#: html/Ticket/ModifyPeople.html:46
+#: html/Ticket/ModifyPeople.html:50
+#: html/Ticket/ModifyPeople.html:57
+msgid "Modify people related to ticket #%1"
+msgstr "ãƒã‚±ãƒƒãƒˆ#%1ã®é–¢ä¸Žè€…を修正ã™ã‚‹"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrips.html:67
+msgid "Modify scrips for queue %1"
+msgstr "キュー%1ã®ã‚¹ã‚¯ãƒªãƒ—トを修正ã™ã‚‹"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56
+#: html/Admin/Global/Scrips.html:65
+#: html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr ""
+
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+#: html/Admin/Global/Template.html:102
+#: html/Admin/Global/Template.html:46
+#: html/Admin/Global/Template.html:51
+#: html/Admin/Queues/Template.html:99
+msgid "Modify template %1"
+msgstr "テンプレート%1を修正ã™ã‚‹"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr ""
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#. ($Group->Name)
+#: html/Admin/Groups/Modify.html:119
+#: html/User/Groups/Modify.html:107
+msgid "Modify the group %1"
+msgstr "グループ%1を修正ã™ã‚‹"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr ""
+
+#. ($UserObj->Name)
+#: html/Admin/Users/Modify.html:309
+msgid "Modify the user %1"
+msgstr "ユーザー%1を修正ã™ã‚‹"
+
+#. ($Ticket->Id)
+#: html/Ticket/ModifyAll.html:58
+msgid "Modify ticket # %1"
+msgstr "ãƒã‚±ãƒƒãƒˆ# %1を修正ã™ã‚‹"
+
+#. ($TicketObj->Id)
+#: html/Ticket/Modify.html:46
+#: html/Ticket/Modify.html:49
+#: html/Ticket/Modify.html:55
+msgid "Modify ticket #%1"
+msgstr "ãƒã‚±ãƒƒãƒˆ#%1を修正ã™ã‚‹"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr ""
+
+#. ($CustomFieldObj->Name)
+#: html/Admin/CustomFields/UserRights.html:157
+msgid "Modify user rights for custom field %1"
+msgstr ""
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/UserRights.html:46
+#: html/Admin/Groups/UserRights.html:50
+#: html/Admin/Groups/UserRights.html:56
+msgid "Modify user rights for group %1"
+msgstr "グループ%1ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼æ¨©åˆ©ã‚’修正ã™ã‚‹"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/UserRights.html:46
+#: html/Admin/Queues/UserRights.html:50
+msgid "Modify user rights for queue %1"
+msgstr "キュー%1ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼æ¨©åˆ©ã‚’修正ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "キュー'%1'ã®ã‚¦ã‚©ãƒƒãƒãƒ£ãƒ¼ã‚’修正ã™ã‚‹"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr ""
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "月曜日"
+
+#. ($name)
+#: html/Ticket/Elements/ShowRequestor:61
+msgid "More about %1"
+msgstr "ã•ã‚‰ã«%1ã«ã¤ã„ã¦"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr ""
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "多ãã®"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "'åå‰'ã®å±žæ€§ã‚’指定ã—ã¦ãã ã•ã„"
+
+#. ($friendly_status)
+#: html/SelfService/Elements/MyRequests:57
+msgid "My %1 tickets"
+msgstr ""
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46
+#: html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "担当ãƒã‚±ãƒƒãƒˆã®æ‰¿èªçŠ¶æ³"
+
+#: html/Search/Elements/SearchPrivacy:50
+#: html/Search/Elements/SelectSearchObject:53
+#: html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:58
+#: html/Admin/Elements/AddCustomFieldValue:53
+#: html/Admin/Elements/EditCustomField:55
+#: html/Admin/Elements/EditCustomFieldValues:55
+#: html/Admin/Elements/ModifyTemplate:49
+#: html/Admin/Groups/Modify.html:65
+#: html/Search/Bulk.html:157
+#: html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "åå‰"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "ç¾åœ¨ãŠä½¿ã„ã®åå‰"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "æ–°è¦"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "æ–°ã—ã„リンク"
+
+#: html/Admin/Users/Modify.html:119
+#: html/User/Prefs.html:109
+msgid "New Password"
+msgstr "æ–°è¦ãƒ‘スワード"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "æ–°è¦æ¤œç´¢"
+
+#: html/Admin/Elements/CustomFieldTabs:93
+#: html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:77
+#: html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr ""
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "æ–°è¦ãƒ‘スワード"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "æ–°ã—ã„パスワード情報ãŒé€ã‚‰ã‚Œã¾ã—ãŸ"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr ""
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "æ–°è¦ãƒªãƒžã‚¤ãƒ³ãƒ€ä½œæˆ:"
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "æ–°è¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "æ–°ã—ã„権利"
+
+#: html/Admin/Global/Scrip.html:63
+#: html/Admin/Global/Scrips.html:60
+#: html/Admin/Queues/Scrip.html:71
+#: html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "æ–°è¦æ¤œç´¢"
+
+#: html/Admin/Global/Template.html:81
+#: html/Admin/Global/Templates.html:60
+#: html/Admin/Queues/Template.html:79
+#: html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr ""
+
+#: html/SelfService/Elements/Tabs:84
+#: html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "æ–°è¦ãƒã‚±ãƒƒãƒˆ"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "æ–°ã—ã„ãƒã‚±ãƒƒãƒˆã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "æ–°è¦ã«è¿½åŠ ã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+#: html/Admin/Queues/People.html:76
+#: html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "æ–°è¦ã‚¦ã‚©ãƒƒãƒãƒ£ãƒ¼"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "æ–°ã—ã„ウインドウ設定"
+
+#: html/Helpers/CalPopup.html:58
+#: html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "次ã¸"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "次ã®ãƒšãƒ¼ã‚¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "次ã®ãƒšãƒ¼ã‚¸"
+
+#: html/Admin/Users/Modify.html:84
+#: html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "ニックãƒãƒ¼ãƒ "
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:166
+#: html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "カスタムフィールドãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:105
+#: html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "グループãŒå®šç¾©ã•ã‚Œã¾ã›ã‚“"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:118
+#: html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "キューãŒå®šç¾©ã•ã‚Œã¾ã›ã‚“"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "RTユーザーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。RT管ç†è€…ã«ç›¸è«‡ã—ã¦ãã ã•ã„。\\n"
+
+#: html/Admin/Global/Template.html:100
+#: html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "テンプレートãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "ãƒã‚±ãƒƒãƒˆãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"No Ticket specified. Aborting ticket modifications\\n"
+"\\n"
+msgstr ""
+"ãƒã‚±ãƒƒãƒˆãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。ãƒã‚±ãƒƒãƒˆã®ä¿®æ­£ã‚’終了ã—ã¾ã™\\n"
+"\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr ""
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "コマンドãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«é–¢ã—ã¦ã®ã‚³ãƒ¡ãƒ³ãƒˆã¯å…¥åŠ›ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "メールã®æ·»ä»˜ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#. (ref $self)
+#: lib/RT/Action/Generic.pm:185
+#: lib/RT/Condition/Generic.pm:197
+#: lib/RT/Search/ActiveTicketsInQueue.pm:77
+#: lib/RT/Search/Generic.pm:134
+#: lib/RT/Search/Googleish.pm:78
+msgid "No description for %1"
+msgstr "%1 詳細情報ãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "グループãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "パスワードãŒè¨­å®šã•ã‚Œã¾ã›ã‚“"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "キューを作æˆã™ã‚‹è¨±å¯ãŒã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#. ($QueueObj->Name)
+#: lib/RT/Ticket_Overlay.pm:420
+msgid "No permission to create tickets in the queue '%1'"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "ユーザーを作æˆã™ã‚‹è¨±å¯ãŒã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "ãã®ãƒã‚±ãƒƒãƒˆã‚’表示ã™ã‚‹è¨±å¯ãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "æ›´æ–°ã•ã‚ŒãŸãƒã‚±ãƒƒãƒˆã‚’見る許å¯ãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/Queue_Overlay.pm:795
+#: lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "責任者ãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/Admin/Queues/People.html:175
+#: html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "責任者ãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "検索基準ã«ã‚ã£ãŸã‚­ãƒ¥ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr ""
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "権利ãŒè¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "æ“作ã®ãŸã‚ã®æ¤œç´¢ãŒã§ãã¾ã›ã‚“"
+
+#: html/Elements/RT__Ticket/ColumnMap:137
+#: html/Search/Results.rdf:78
+msgid "No subject"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "ãƒã‚±ãƒƒãƒˆIDãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: lib/RT/Transaction_Overlay.pm:528
+#: lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "トランザクションタイプãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "検索基準ã«ã‚ã£ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "有効ãªRTユーザーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。RT cvcãƒãƒ³ãƒ‰ãƒ©ãŒåˆ†é›¢ã—ã¦ã„ã¾ã™ã€‚RT管ç†è€…ã«ç›¸è«‡ã—ã¦ãã ã•ã„。\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr ""
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr ""
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr ""
+
+#: html/Search/Chart:71
+#: html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "ログインã—ã¦ã„ã¾ã›ã‚“"
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "未指定"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "未実装"
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "未実装..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "付記"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "ãŠçŸ¥ã‚‰ã›ã‚’é€ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr ""
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr ""
+
+#: etc/initialdata:93
+#: etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr ""
+
+#: etc/initialdata:89
+#: etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr ""
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr ""
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr ""
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr ""
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr ""
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr ""
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr ""
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr ""
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr ""
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr ""
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr ""
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr ""
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "11月"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr ""
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr ""
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr ""
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr ""
+
+#. ($ObjectType)
+#. ($LookupType)
+#: html/Admin/CustomFields/Objects.html:72
+#: html/Admin/Elements/ObjectCustomFields:63
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "10月"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "オフライン"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "オフライン編集"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "オフラインã‹ã‚‰ã‚¢ãƒƒãƒ—ロード"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "ãŒä»¥ä¸‹ã§ã‚ã‚‹"
+
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+#: lib/RT/Transaction_Overlay.pm:326
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr ""
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr ""
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr ""
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr ""
+
+#: etc/initialdata:177
+#: etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr ""
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr ""
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr ""
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr ""
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr ""
+
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+#: html/Approvals/Elements/PendingMyApproval:70
+msgid "Only show approvals for requests created after %1"
+msgstr "%1 以後ã«ä½œæˆã•ã‚ŒãŸæ‰¿èªã®ã¿è¡¨ç¤º"
+
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+#: html/Approvals/Elements/PendingMyApproval:68
+msgid "Only show approvals for requests created before %1"
+msgstr "%1 以å‰ã«ä½œæˆã•ã‚ŒãŸæ‰¿èªã®ã¿è¡¨ç¤º"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "ç€æ‰‹æ¸ˆã¿"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "リクエストを開ã"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "ãƒã‚±ãƒƒãƒˆã‚’(リストã‹ã‚‰ï¼‰æ–°ã—ã„ウインドウã‹ã‚‰é–‹ã"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "ãƒã‚±ãƒƒãƒˆã‚’(リストã‹ã‚‰ï¼‰ã»ã‹ã®ã‚¦ã‚¤ãƒ³ãƒ‰ã‚¦ã‹ã‚‰é–‹ã"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr ""
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "オプション"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "並ã³ã‹ãˆé †ç•ª"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "整列ã¨ä¸¦ã³æ›¿ãˆ"
+
+#: html/Admin/Users/Modify.html:141
+#: html/User/Prefs.html:129
+msgid "Organization"
+msgstr "組織"
+
+#. ($approving->Id, $approving->Subject)
+#: html/Approvals/Elements/Approve:53
+msgid "Originating ticket: #%1"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "コメントã«ã¤ã„ã¦ã®ãƒ¡ãƒ¼ãƒ«é€ä¿¡ã‚’記録ã—ã¾ã—ãŸ"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "メールé€ä¿¡ã‚’記録ã—ã¾ã—ãŸ"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "時間切れã§ã™ã€å„ªå…ˆé †ä½ãŒã†ã¤ã‚Šã¾ã—ãŸ"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr ""
+
+#: etc/initialdata:38
+#: html/Elements/QuickCreate:56
+#: html/Search/Elements/PickBasics:101
+#: html/Ticket/Create.html:72
+#: html/Ticket/Elements/EditBasics:61
+#: html/Ticket/Elements/EditPeople:64
+#: html/Ticket/Elements/EditPeople:65
+#: html/Ticket/Elements/Reminders:129
+#: html/Ticket/Elements/ShowPeople:48
+#: html/Ticket/Update.html:62
+#: lib/RT/ACE_Overlay.pm:110
+#: lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "担当者"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr ""
+
+#. ($Old->Name , $New->Name)
+#: lib/RT/Transaction_Overlay.pm:672
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "担当者ã¯å¼·åˆ¶çš„ã«%1ã‹ã‚‰%2を変更ã—ã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "担当者ã¯"
+
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+#: html/Elements/TicketList:78
+msgid "Page %1 of %2"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:198
+#: html/User/Prefs.html:96
+msgid "Pager"
+msgstr "ãƒã‚±ãƒƒãƒˆãƒ™ãƒ«"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "ãƒã‚±ãƒƒãƒˆãƒ™ãƒ«é›»è©±"
+
+#: html/Elements/EditLinks:144
+#: html/Elements/EditLinks:76
+#: html/Elements/ShowLinks:68
+#: html/Ticket/Create.html:222
+#: html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "上ä½ãƒã‚±ãƒƒãƒˆ"
+
+#: html/Elements/Login:95
+#: html/User/Prefs.html:105
+msgid "Password"
+msgstr "パスワード"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "パスワードã®ãŠçŸ¥ã‚‰ã›"
+
+#: lib/RT/Transaction_Overlay.pm:781
+#: lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr ""
+
+#. ($RT::MinimumPasswordLength)
+#: lib/RT/User_Overlay.pm:1037
+#: lib/RT/User_Overlay.pm:214
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "パスワードãŒçŸ­ã™ãŽã¾ã™"
+
+#. (loc_fuzzy($msg))
+#: html/User/Prefs.html:240
+msgid "Password: %1"
+msgstr "パスワード: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr ""
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr ""
+
+#: html/Ticket/Elements/ShowSummary:62
+#: html/Ticket/Elements/Tabs:119
+#: html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "担当者等"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perlã®è¨­å®šæƒ…å ±"
+
+#: lib/RT/ACE_Overlay.pm:251
+#: lib/RT/ACE_Overlay.pm:257
+#: lib/RT/ACE_Overlay.pm:580
+#: lib/RT/ACE_Overlay.pm:590
+#: lib/RT/ACE_Overlay.pm:600
+#: lib/RT/ACE_Overlay.pm:665
+#: lib/RT/Attribute_Overlay.pm:158
+#: lib/RT/Attribute_Overlay.pm:164
+#: lib/RT/Attribute_Overlay.pm:405
+#: lib/RT/Attribute_Overlay.pm:414
+#: lib/RT/Attribute_Overlay.pm:427
+#: lib/RT/CurrentUser.pm:116
+#: lib/RT/CurrentUser.pm:125
+#: lib/RT/CustomField_Overlay.pm:1017
+#: lib/RT/CustomField_Overlay.pm:1138
+#: lib/RT/CustomField_Overlay.pm:1281
+#: lib/RT/CustomField_Overlay.pm:172
+#: lib/RT/CustomField_Overlay.pm:189
+#: lib/RT/CustomField_Overlay.pm:200
+#: lib/RT/CustomField_Overlay.pm:374
+#: lib/RT/CustomField_Overlay.pm:403
+#: lib/RT/CustomField_Overlay.pm:763
+#: lib/RT/CustomField_Overlay.pm:936
+#: lib/RT/CustomField_Overlay.pm:971
+#: lib/RT/Group_Overlay.pm:1117
+#: lib/RT/Group_Overlay.pm:1121
+#: lib/RT/Group_Overlay.pm:1130
+#: lib/RT/Group_Overlay.pm:1240
+#: lib/RT/Group_Overlay.pm:1244
+#: lib/RT/Group_Overlay.pm:1250
+#: lib/RT/Group_Overlay.pm:445
+#: lib/RT/Group_Overlay.pm:542
+#: lib/RT/Group_Overlay.pm:620
+#: lib/RT/Group_Overlay.pm:628
+#: lib/RT/Group_Overlay.pm:726
+#: lib/RT/Group_Overlay.pm:730
+#: lib/RT/Group_Overlay.pm:736
+#: lib/RT/Group_Overlay.pm:922
+#: lib/RT/Group_Overlay.pm:926
+#: lib/RT/Group_Overlay.pm:939
+#: lib/RT/Queue_Overlay.pm:1054
+#: lib/RT/Queue_Overlay.pm:140
+#: lib/RT/Queue_Overlay.pm:158
+#: lib/RT/Queue_Overlay.pm:657
+#: lib/RT/Queue_Overlay.pm:667
+#: lib/RT/Queue_Overlay.pm:681
+#: lib/RT/Queue_Overlay.pm:819
+#: lib/RT/Queue_Overlay.pm:828
+#: lib/RT/Queue_Overlay.pm:841
+#: lib/RT/Scrip_Overlay.pm:149
+#: lib/RT/Scrip_Overlay.pm:160
+#: lib/RT/Scrip_Overlay.pm:224
+#: lib/RT/Scrip_Overlay.pm:538
+#: lib/RT/Template_Overlay.pm:108
+#: lib/RT/Template_Overlay.pm:277
+#: lib/RT/Ticket_Overlay.pm:1357
+#: lib/RT/Ticket_Overlay.pm:1367
+#: lib/RT/Ticket_Overlay.pm:1381
+#: lib/RT/Ticket_Overlay.pm:1522
+#: lib/RT/Ticket_Overlay.pm:1532
+#: lib/RT/Ticket_Overlay.pm:1546
+#: lib/RT/Ticket_Overlay.pm:1663
+#: lib/RT/Ticket_Overlay.pm:1983
+#: lib/RT/Ticket_Overlay.pm:2126
+#: lib/RT/Ticket_Overlay.pm:2296
+#: lib/RT/Ticket_Overlay.pm:2346
+#: lib/RT/Ticket_Overlay.pm:2525
+#: lib/RT/Ticket_Overlay.pm:2538
+#: lib/RT/Ticket_Overlay.pm:2614
+#: lib/RT/Ticket_Overlay.pm:2627
+#: lib/RT/Ticket_Overlay.pm:2748
+#: lib/RT/Ticket_Overlay.pm:2762
+#: lib/RT/Ticket_Overlay.pm:2990
+#: lib/RT/Ticket_Overlay.pm:3000
+#: lib/RT/Ticket_Overlay.pm:3005
+#: lib/RT/Ticket_Overlay.pm:3224
+#: lib/RT/Ticket_Overlay.pm:3228
+#: lib/RT/Ticket_Overlay.pm:3371
+#: lib/RT/Ticket_Overlay.pm:3497
+#: lib/RT/Transaction_Overlay.pm:516
+#: lib/RT/Transaction_Overlay.pm:523
+#: lib/RT/Transaction_Overlay.pm:551
+#: lib/RT/Transaction_Overlay.pm:558
+#: lib/RT/User_Overlay.pm:1176
+#: lib/RT/User_Overlay.pm:1856
+#: lib/RT/User_Overlay.pm:369
+#: lib/RT/User_Overlay.pm:735
+#: lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "許å¯ãŒä¸‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/Template_Overlay.pm:238
+#: lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr ""
+
+#: html/User/Groups/index.html:51
+#: html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "個人グループ"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "個人グループ:"
+
+#: html/Admin/Users/Modify.html:180
+#: html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "電話番å·"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "代替物"
+
+#: NOT FOUND IN SOURCE
+msgid "Pref"
+msgstr "設定"
+
+#: html/Elements/Header:93
+#: html/Elements/Tabs:91
+#: html/SelfService/Elements/Tabs:95
+#: html/SelfService/Prefs.html:46
+#: html/User/Prefs.html:46
+#: html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "個人設定"
+
+#. ($pane, $UserObj->Name)
+#: html/Admin/Users/MyRT.html:75
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#. ($pane)
+#: html/Prefs/MyRT.html:141
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:56
+#: html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "å‰ã®"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "å‰ã®ãƒšãƒ¼ã‚¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "優先度"
+
+#. ($args{'PrincipalId'})
+#: lib/RT/ACE_Overlay.pm:157
+#: lib/RT/ACE_Overlay.pm:239
+#: lib/RT/ACE_Overlay.pm:569
+msgid "Principal %1 not found."
+msgstr ""
+
+#: html/Search/Elements/PickBasics:147
+#: html/Ticket/Create.html:181
+#: html/Ticket/Elements/EditBasics:92
+#: html/Ticket/Elements/ShowBasics:72
+#: lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "優先度"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "優先順ä½ã¯æ¬¡ã®ã‚ˆã†ã«å§‹ã¾ã‚Šã¾ã™"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr ""
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr ""
+
+#. (loc_fuzzy($msg))
+#: html/Admin/Users/Modify.html:342
+#: html/User/Prefs.html:231
+msgid "Privileged status: %1"
+msgstr "特権ステータス: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "特権ã®ã‚るユーザー"
+
+#: etc/initialdata:23
+#: etc/initialdata:29
+#: etc/initialdata:35
+#: etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr ""
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "クエリ作æˆ"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "クエリ:"
+
+#: html/Elements/QueueSummary:48
+#: html/Elements/QuickCreate:54
+#: html/Search/Elements/PickBasics:71
+#: html/SelfService/Create.html:54
+#: html/Ticket/Create.html:62
+#: html/Ticket/Elements/EditBasics:57
+#: html/Ticket/Elements/ShowBasics:76
+#: html/Tools/Reports/CreatedByDates.html:85
+#: html/Tools/Reports/ResolvedByDates.html:86
+#: html/Tools/Reports/ResolvedByOwner.html:66
+#: html/User/Elements/DelegateRights:101
+#: lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "キュー"
+
+#. ($Queue)
+#. ($id)
+#: html/Admin/Queues/CustomField.html:63
+#: html/Admin/Queues/Scrip.html:61
+#: html/Admin/Queues/Scrips.html:69
+#: html/Admin/Queues/Templates.html:65
+msgid "Queue %1 not found"
+msgstr "キュー %1 ã¯ã¿ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "キュー'%1'ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ\\n"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "キューã®åå‰"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "キュースクリプト"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "キューã¯ã™ã§ã«å­˜åœ¨ã—ã¦ã„ã¾ã™"
+
+#: lib/RT/Queue_Overlay.pm:374
+#: lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "キューã®ä½œæˆãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/Ticket/Create.html:244
+#: lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "キューã®ãƒ­ãƒ¼ãƒ‰ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: docs/design_docs/string-extraction-guide.txt:83
+#: lib/RT/Queue_Overlay.pm:384
+#: lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "キューãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: html/SelfService/Display.html:126
+#: lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "キューãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: html/Admin/Elements/Tabs:59
+#: html/Admin/index.html:72
+msgid "Queues"
+msgstr "キュー"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "担当キュー情報"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "ãƒã‚±ãƒƒãƒˆæ–°è¦ä½œæˆ"
+
+#: html/Elements/Quicksearch:47
+#: html/Prefs/Elements/Tabs:58
+#: html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "簡易検索"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "ãƒã‚±ãƒƒãƒˆæ–°è¦ä½œæˆ"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#. ($RT::VERSION, $RT::rtname)
+#: docs/design_docs/string-extraction-guide.txt:70
+#: lib/RT/StyleGuide.pod:796
+msgid "RT %1 for %2"
+msgstr "%2ã®RT %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46
+#: html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT管ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "RTèªè¨¼ã‚¨ãƒ©ãƒ¼"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RTãƒã‚¦ãƒ³ã‚¹ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "RT設定エラー"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "RTé‡å¤§ãªã‚¨ãƒ©ãƒ¼ã€‚メッセージãŒè¨˜éŒ²ã•ã‚Œã¾ã›ã‚“"
+
+#: html/Elements/Error:63
+#: html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RTエラー"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RTå—信メール(%1)自身ã‹ã‚‰ã®ãƒ¡ãƒ¼ãƒ« "
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RTセルフサービス/クローズã•ã‚ŒãŸãƒã‚±ãƒƒãƒˆ"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RTã®å„種変数"
+
+#: html/Admin/Elements/SystemTabs:71
+#: html/Admin/Elements/UserTabs:67
+#: html/Admin/Global/MyRT.html:1
+#: html/Admin/Global/MyRT.html:12
+#: html/Admin/Global/MyRT.html:4
+#: html/Admin/Global/index.html:84
+#: html/Admin/Users/MyRT.html:21
+#: html/Prefs/MyRT.html:66
+#: html/Prefs/MyRT.html:78
+#: html/User/Elements/Tabs:65
+#: html/index.html:1
+#: html/index.html:75
+msgid "RT at a glance"
+msgstr ""
+
+#. ($UserObj->Name)
+#: html/Admin/Users/MyRT.html:30
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RTã§ã¯ã€ãŸã ã„ã¾ãŠä½¿ã„ã®æ–¹ã®èªè¨¼ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RTã¯å¤–部ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãƒ«ãƒƒã‚¯ã‚¢ãƒƒãƒ—を使ã£ã¦ä½œæˆè€…を見ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RTã¯ã‚­ãƒ¥ãƒ¼: %1を見ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RTã¯ã“ã®PGPç½²åを有効ã ã¨å‡¦ç†ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚\\n"
+
+#. ($RT::rtname)
+#: html/Elements/Logo:49
+#: html/Elements/PageLayout:172
+msgid "RT for %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RTã¯ã‚ãªãŸã®ã‚³ãƒžãƒ³ãƒ‰ã‚’処ç†ã—ã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT&copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;。RTã¯<a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU一般公衆利用許諾契約書ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³2ã«ã‚‚ã¨ã¥ã„ã¦é…布ã•ã‚Œã¦ã„ã¾ã™ã€‚</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RTã«ã‚ˆã‚‹ã¨ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã¯ãƒã‚¦ãƒ³ã‚¹ã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "ã“ã®ãƒ•ã‚©ãƒ¼ãƒ ã§ã¯ã‚ãªãŸãŒãƒã‚±ãƒƒãƒˆã«ã¤ã‘ãŸä»¶åã¯æ¤œç´¢ã•ã‚Œã¾ã›ã‚“."
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RTã¯ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ãŒé›»å­ç½²åã•ã‚Œã¦ã„ãªã„ã‚‚ã®ã¨ã—ã¦å‡¦ç†ã—ã¾ã™ã€‚\\n"
+
+#: html/Admin/CustomFields/Modify.html:108
+#: html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RTã®Eメールコマンドモードã§ã¯PGPèªè¨¼ãŒå¿…è¦ã§ã™ã€‚メッセージã«é›»å­ç½²åãŒãªã„ã‹ã€ç„¡åŠ¹ãªé›»å­ç½²åã§ã™ã€‚"
+
+#: html/Admin/Users/Modify.html:79
+#: html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "本å"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "本å"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:725
+msgid "Reference by %1 added"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:765
+msgid "Reference by %1 deleted"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:722
+msgid "Reference to %1 added"
+msgstr ""
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:762
+msgid "Reference to %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:103
+#: html/Elements/EditLinks:156
+#: html/Elements/ShowLinks:92
+#: html/Ticket/Create.html:225
+#: html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "å‚ç…§ã•ã‚Œã¦ã„ã‚‹ãƒã‚±ãƒƒãƒˆ"
+
+#: html/Elements/EditLinks:152
+#: html/Elements/EditLinks:94
+#: html/Elements/SelectLinkType:49
+#: html/Elements/ShowLinks:82
+#: html/Ticket/Create.html:224
+#: html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "å‚ç…§ã—ã¦ã„ã‚‹ãƒã‚±ãƒƒãƒˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "絞り込む"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "絞込ã¿æ¤œç´¢"
+
+#. ($value/60)
+#: html/Elements/Refresh:57
+msgid "Refresh this page every %1 minutes."
+msgstr "ページを%1分ãŠãã«æ›´æ–°ã™ã‚‹"
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:811
+msgid "Reminder '%1' added"
+msgstr ""
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:824
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:817
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#. ($Ticket->Id)
+#: html/Ticket/Reminders.html:46
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48
+#: html/Ticket/Elements/ShowSummary:75
+#: html/Ticket/Elements/Tabs:122
+#: html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "リマインダ通知"
+
+#. ($Ticket->Id)
+#: html/Ticket/Reminders.html:50
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "管ç†Ccを削除ã™ã‚‹"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Ccを削除ã™ã‚‹"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "作æˆè€…を削除ã™ã‚‹"
+
+#: html/Ticket/Elements/ShowTransaction:179
+#: html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "返信"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "返信アドレス"
+
+#: html/Search/Bulk.html:129
+#: html/Ticket/ModifyAll.html:94
+#: html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "リクエストã—ãŸäººã«è¿”ä¿¡"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr ""
+
+#: html/Tools/Elements/Tabs:59
+#: html/Tools/Reports/index.html:46
+#: html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "レãƒãƒ¼ãƒˆä½œæˆ"
+
+#: etc/initialdata:44
+#: lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "作æˆè€…"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "作æˆè€…ã®Eメールアドレス"
+
+#: html/SelfService/Create.html:63
+#: html/Ticket/Create.html:80
+#: html/Ticket/Elements/EditPeople:69
+#: html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "作æˆè€…"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "リクエストã¯æ¬¡ã®æ—¥ã¾ã§ã«è¡Œã‚ã‚Œãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“"
+
+#. ('Object')
+#: lib/RT/Attribute_Overlay.pm:146
+msgid "Required parameter '%1' not specified"
+msgstr ""
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "リセット"
+
+#: html/Admin/Users/MyRT.html:15
+#: html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183
+#: html/User/Prefs.html:84
+msgid "Residence"
+msgstr "ä½æ‰€"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "解決済ã¿ã«ã™ã‚‹"
+
+#. ($TicketObj->id, $TicketObj->Subject)
+#: html/Ticket/Update.html:156
+msgid "Resolve ticket #%1 (%2)"
+msgstr ""
+
+#: etc/initialdata:323
+#: html/Elements/SelectDateType:49
+#: lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "解決済ã¿"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "解決済ã¿ï¼ˆæ‰€æœ‰è€…別)"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "解決済ã¿ï¼ˆæœŸé–“)"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "作æˆè€…ã«è¿”ç­”ã™ã‚‹"
+
+#: html/Elements/ListActions:46
+#: html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "çµæžœ"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "一度ã«è¡¨ç¤ºã™ã‚‹å‡ºåŠ›æ•°"
+
+#: html/Admin/Users/Modify.html:126
+#: html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "パスワードã®å†å…¥åŠ›"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr ""
+"%2 %3ã®æ¨©åˆ©%1ãŒé ˜åŸŸ%4 %5\\n"
+"ã§è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "権利ãŒå§”託ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "権利ãŒè¨±å¯ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "権利ãŒãƒ­ãƒ¼ãƒ‰ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/ACE_Overlay.pm:695
+#: lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "権利を無効ã«ã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "権利ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/ACE_Overlay.pm:560
+#: lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "権利ãŒãƒ­ãƒ¼ãƒ‰ã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "権利ãŒç„¡åŠ¹ã«ãªã‚Šã¾ã—ãŸ"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "権利"
+
+#. ($object_type)
+#: html/Admin/CustomFields/GroupRights.html:129
+#: lib/RT/Interface/Web.pm:961
+msgid "Rights could not be granted for %1"
+msgstr ""
+
+#. ($object_type)
+#: html/Admin/CustomFields/GroupRights.html:156
+#: lib/RT/Interface/Web.pm:990
+msgid "Rights could not be revoked for %1"
+msgstr ""
+
+#: html/Admin/Global/GroupRights.html:72
+#: html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "役割"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "æž ã®ä¸­ã«è¡¨ç¤ºã™ã‚‹è¡Œæ•°"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "一度ã«è¡¨ç¤ºã™ã‚‹è¡Œæ•°"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "土曜日"
+
+#: html/Prefs/MyRT.html:72
+#: html/Prefs/Quicksearch.html:64
+#: html/Prefs/Search.html:69
+#: html/Search/Elements/EditSearches:70
+#: html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "ä¿å­˜ã™ã‚‹"
+
+#: html/Admin/Global/Template.html:67
+#: html/Admin/Groups/Modify.html:88
+#: html/Admin/Queues/Modify.html:111
+#: html/Admin/Queues/People.html:126
+#: html/Admin/Users/Modify.html:239
+#: html/Prefs/Quicksearch.html:64
+#: html/Prefs/SearchOptions.html:63
+#: html/SelfService/Prefs.html:58
+#: html/Ticket/Modify.html:60
+#: html/Ticket/ModifyAll.html:127
+#: html/Ticket/ModifyDates.html:60
+#: html/Ticket/ModifyLinks.html:61
+#: html/Ticket/ModifyPeople.html:60
+#: html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "変更をä¿å­˜ã™ã‚‹"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "変更をä¿å­˜ã™ã‚‹"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "変更をä¿å­˜ã™ã‚‹"
+
+#. ($name)
+#: lib/RT/SavedSearch.pm:173
+msgid "Saved search %1"
+msgstr ""
+
+#. ($scrip->Id)
+#. ($id)
+#: html/Admin/Elements/ListGlobalScrips:60
+#: html/Admin/Global/Scrip.html:77
+#: html/Admin/Queues/Scrip.html:84
+msgid "Scrip #%1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "スクリプトãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:67
+#: html/Admin/Elements/SystemTabs:54
+#: html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "スクリプト"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr ""
+"%1\\n"
+"ã®ã‚¹ã‚¯ãƒªãƒ—ト"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr ""
+
+#: html/Elements/SimpleSearch:48
+#: html/Search/Simple.html:63
+msgid "Search"
+msgstr "検索"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "検索基準"
+
+#: html/Prefs/SearchOptions.html:47
+#: html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "検索設定"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "承èªçŠ¶æ³ã®æ¤œç´¢"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "ãƒã‚±ãƒƒãƒˆã®æ¤œç´¢"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "ãƒã‚±ãƒƒãƒˆã‚’検索ã™ã‚‹ã«ã¯ã€ãƒã‚±ãƒƒãƒˆã®<strong>id</strong>番å·, <strong>キュー</strong>ã®å称, 所有者ã®<strong>ユーザーå</strong>, リクエスト元ã®<strong>Eメールアドレス</strong>を入力ã—ã¦ãã ã•ã„. RT ã¯ãƒã‚±ãƒƒãƒˆã®æœ¬æ–‡ã‚„添付ファイル以外ã®é …目を検索ã—ã¾ã™."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "検索オプション"
+
+#. ($PrimaryGroupBy)
+#: html/Search/Chart.html:56
+msgid "Search results grouped by %1"
+msgstr ""
+
+#. ($msg)
+#: lib/RT/SavedSearch.pm:203
+msgid "Search update: %1"
+msgstr ""
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "ãƒã‚±ãƒƒãƒˆã®å…¨æ–‡æ¤œç´¢ã¯æ™‚é–“ãŒã‹ã‹ã‚Šã¾ã™ãŒã€ã‚‚ã—ã‚‚å¿…è¦ãªå ´åˆã¯ã€ <b>fulltext:<i>検索語</i></b> ã¨è¨˜è¿°ã—ã¦ãã ã•ã„。"
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr ""
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr ""
+
+#: html/Admin/CustomFields/index.html:46
+#: html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr ""
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "グループã®é¸æŠž"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "キューã®é¸æŠž"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr ""
+
+#: html/Admin/Users/index.html:46
+#: html/Admin/Users/index.html:49
+#: html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "ユーザーã®é¸æŠž"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:75
+#: html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr ""
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59
+#: html/Admin/Global/Scrips.html:57
+#: html/Admin/Queues/Scrip.html:67
+#: html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr ""
+
+#: html/Admin/Global/Template.html:78
+#: html/Admin/Global/Templates.html:57
+#: html/Admin/Queues/Template.html:76
+#: html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr ""
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "セルフサービス"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr ""
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr ""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr ""
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr ""
+
+#: etc/initialdata:125
+#: etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr ""
+
+#: etc/initialdata:94
+#: etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr ""
+
+#: etc/initialdata:90
+#: etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr ""
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:82
+#: etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr ""
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "9月"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "表示ã™ã‚‹"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "承èªçŠ¶æ³ã‚’表示"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "コラム表示"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "çµæžœã‚’見る"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "承èªã•ã‚ŒãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示ã™ã‚‹"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "基本情報を見る"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "æ‹’å¦ã•ã‚ŒãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示ã™ã‚‹"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "詳細を見る"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "ä¿ç•™ã—ã¦ã„るリクエストを表示ã™ã‚‹"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "ä»–ã‹ã‚‰ã®æ‰¿èªå¾…ã¡ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示ã™ã‚‹"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr ""
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:230
+#: html/User/Prefs.html:168
+msgid "Signature"
+msgstr "ç½²å"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "シンプル検索"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "ã²ã¨ã¤ã®"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "サイズ"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49
+#: html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "並ã³æ›¿ãˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "並ã¹æ›¿ãˆã®ã‚­ãƒ¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "次ã®é …ç›®ã”ã¨ã®ä¸¦ã³æ›¿ãˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "並ã³é †"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "åœæ­¢ã—ã¦ã„ã¾ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "開始ページ"
+
+#: html/Elements/SelectDateType:48
+#: html/Ticket/Elements/EditDates:53
+#: html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "開始ã—ãŸæ—¥æ™‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "開始日'%1'ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/Elements/SelectDateType:52
+#: html/Ticket/Create.html:208
+#: html/Ticket/Elements/EditDates:48
+#: html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "開始予定日時"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "次ã®æ—¥æ™‚ã¾ã§ã«é–‹å§‹ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "開始日'%1'を解æžã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: html/Admin/Users/Modify.html:162
+#: html/User/Prefs.html:145
+msgid "State"
+msgstr "都é“府県"
+
+#: html/Search/Elements/PickBasics:87
+#: html/SelfService/Update.html:57
+#: html/Ticket/Create.html:66
+#: html/Ticket/Elements/EditBasics:53
+#: html/Ticket/Elements/ShowBasics:52
+#: html/Ticket/Update.html:59
+#: lib/RT/Ticket_Overlay.pm:1166
+#: lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "ステータス"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "ステータスãŒ%1ã‹ã‚‰%2ã«å¤‰æ›´ã•ã‚Œã¾ã—ãŸ"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "担当者変更"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr ""
+
+#. ($Old->Name)
+#: lib/RT/Transaction_Overlay.pm:678
+msgid "Stolen from %1"
+msgstr "%1ã‹ã‚‰æ‹…当者を変更ã—ã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "%1ã‹ã‚‰ç›—用ã—㟠"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "表示スタイル"
+
+#: html/Elements/QuickCreate:52
+#: html/Elements/SelectAttachmentField:47
+#: html/Search/Bulk.html:132
+#: html/SelfService/Create.html:79
+#: html/SelfService/Update.html:65
+#: html/Ticket/Create.html:108
+#: html/Ticket/Elements/EditBasics:48
+#: html/Ticket/Elements/Reminders:125
+#: html/Ticket/ModifyAll.html:100
+#: html/Ticket/Update.html:82
+#: lib/RT/Ticket_Overlay.pm:1162
+#: lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "件å"
+
+#. ($self->Data)
+#: docs/design_docs/string-extraction-guide.txt:89
+#: lib/RT/StyleGuide.pod:815
+#: lib/RT/Transaction_Overlay.pm:700
+msgid "Subject changed to %1"
+msgstr ""
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr ""
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "日曜日"
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "システム"
+
+#: html/Admin/Elements/ToolTabs:54
+#: html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "システムã®è¨­å®šæƒ…å ±"
+
+#: html/Admin/CustomFields/GroupRights.html:128
+#: html/Admin/CustomFields/GroupRights.html:155
+#: html/Admin/CustomFields/UserRights.html:128
+#: html/Admin/CustomFields/UserRights.html:98
+#: html/Admin/Elements/SelectRights:106
+#: lib/RT/ACE_Overlay.pm:584
+#: lib/RT/Interface/Web.pm:960
+#: lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "システムエラー"
+
+#. ($msg)
+#: lib/RT/Transaction_Overlay.pm:224
+#: lib/RT/Transaction_Overlay.pm:230
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "システムツール"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "システムエラー。権利ãŒå§”ä»»ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: lib/RT/ACE_Overlay.pm:163
+#: lib/RT/ACE_Overlay.pm:228
+#: lib/RT/ACE_Overlay.pm:323
+#: lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "システムエラー。権利ãŒèªå¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/Admin/CustomFields/GroupRights.html:58
+#: html/Admin/Global/GroupRights.html:56
+#: html/Admin/Groups/GroupRights.html:58
+#: html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "システムグループ"
+
+#: etc/initialdata:41
+#: etc/initialdata:47
+#: etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr ""
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "テスト_ストリング"
+
+#: etc/initialdata:603
+#: html/Search/Elements/EditFormat:72
+#: html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "担当ã™ã‚‹"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "担当済ã¿"
+
+#: html/Admin/Elements/EditScrip:71
+#: html/Tools/Offline.html:78
+msgid "Template"
+msgstr "テンプレート"
+
+#. ($TemplateObj->Id())
+#: html/Admin/Global/Template.html:112
+#: html/Admin/Queues/Template.html:113
+msgid "Template #%1"
+msgstr ""
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "テンプレートãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "テンプレートãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "テンプレートを解æžã—ã¾ã—ãŸ"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70
+#: html/Admin/Elements/SystemTabs:57
+#: html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "テンプレート"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr ""
+"%1\\n"
+"ã®ãƒ†ãƒ³ãƒ—レート"
+
+#: lib/RT/CustomField_Overlay.pm:943
+#: lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "ã“ã®ã‚«ã‚¹ã‚¿ãƒ ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã¯ãã®å€¤ã‚’ã†ã‘ã¤ã‘ã¾ã›ã‚“"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "åŒã˜å€¤ã§ã™"
+
+#: lib/RT/ACE_Overlay.pm:305
+#: lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr ""
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:753
+msgid "That principal is already a %1 for this queue"
+msgstr "ãã®è²¬ä»»è€…ã¯ã™ã§ã«ã“ã®ã‚­ãƒ¥ãƒ¼ã®%1ã§ã™"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1435
+msgid "That principal is already a %1 for this ticket"
+msgstr "ãã®è²¬ä»»è€…ã¯ã™ã§ã«ã“ã®ãƒã‚±ãƒƒãƒˆã®%1ã§ã™"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:852
+msgid "That principal is not a %1 for this queue"
+msgstr "ãã®è²¬ä»»è€…ã¯ã“ã®ã‚­ãƒ¥ãƒ¼ã®%1ã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "ãã®è²¬ä»»è€…ã¯ã“ã®ãƒã‚±ãƒƒãƒˆã®%1ã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "ãã®ã‚­ãƒ¥ãƒ¼ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "未解決ã®ä¾å­˜é–¢ä¿‚ãŒå­˜åœ¨ã—ã¾ã™"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã™ã§ã«æ¨©åˆ©ãŒã‚ã‚Šã¾ã™"
+
+#: lib/RT/Action/CreateTickets.pm:710
+#: lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã™ã§ã«ãƒã‚±ãƒƒãƒˆã‚’所有ã—ã¦ã„ã¾ã™"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯å­˜åœ¨ã—ã¾ã›ã‚“"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã™ã§ã«ç‰¹æ¨©ãŒä¸Žãˆã‚‰ã‚Œã¦ã„ã¾ã™"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã¯ã™ã§ã«ç‰¹æ¨©ãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ä»Šç‰¹æ¨©ã‚’与ãˆã‚‰ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ä»Šç‰¹æ¨©ã‚’失ã„ã¾ã—ãŸ"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã“ã®ã‚­ãƒ¥ãƒ¼ã§ã¯ãƒã‚±ãƒƒãƒˆã‚’所有ã—ã¦ã„ãªã„å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "ãã‚Œã¯æ•°å­—ã®IDã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: html/SelfService/Display.html:53
+#: html/Ticket/Create.html:177
+#: html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "基本"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "コメントã¯è¨˜éŒ²ã•ã‚Œã¾ã—ãŸ"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"The following commands were not proccessed:\\n"
+"\\n"
+msgstr ""
+"次ã®ã‚³ãƒžãƒ³ãƒ‰ã¯å‡¦ç†ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ:\\n"
+"\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr ""
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "ãれらã®ã‚³ãƒ¡ãƒ³ãƒˆã¯å®Ÿéš›ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã¯è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã¯ä»¥ä¸‹ã®å®›å…ˆã«é€ä¿¡ã•ã‚Œã¾ã™:"
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "ã“ã®ãƒã‚±ãƒƒãƒˆ%1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "ã“ã®ãƒˆãƒ©ãƒ³ã‚¶ã‚¯ã‚·ãƒ§ãƒ³ã«ã¯ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒã‚ã‚Šã¾ã›ã‚“"
+
+#. ($rows)
+#: html/Ticket/Elements/ShowRequestor:70
+msgid "This user's %1 highest priority tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®25ã®ã‚‚ã£ã¨ã‚‚高ã„優先ãƒã‚±ãƒƒãƒˆ"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "木曜日"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "ãƒã‚±ãƒƒãƒˆ# %1 %2"
+
+#. ($Ticket->Id, $Ticket->Subject)
+#: html/Ticket/ModifyAll.html:46
+#: html/Ticket/ModifyAll.html:50
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "ãƒã‚±ãƒƒãƒˆã€€#%1 更新(ç·åˆãƒ“ュー): %2"
+
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#: html/Approvals/Elements/ShowDependency:67
+msgid "Ticket #%1: %2"
+msgstr ""
+
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+#: lib/RT/Action/CreateTickets.pm:1350
+#: lib/RT/Action/CreateTickets.pm:1359
+#: lib/RT/Action/CreateTickets.pm:605
+#: lib/RT/Action/CreateTickets.pm:729
+#: lib/RT/Action/CreateTickets.pm:741
+msgid "Ticket %1"
+msgstr ""
+
+#. ($self->Id, $QueueObj->Name)
+#: lib/RT/Ticket_Overlay.pm:755
+#: lib/RT/Ticket_Overlay.pm:775
+msgid "Ticket %1 created in queue '%2'"
+msgstr "ãƒã‚±ãƒƒãƒˆ %1ãŒã‚­ãƒ¥ãƒ¼ '%2'ã§ä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "ãƒã‚±ãƒƒãƒˆ%1ãŒãƒ­ãƒ¼ãƒ‰ã•ã‚Œã¾ã—ãŸ\\n"
+
+#. ($Ticket->Id, $_)
+#: html/Search/Bulk.html:377
+msgid "Ticket %1: %2"
+msgstr "ãƒã‚±ãƒƒãƒˆã€€%1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr ""
+
+#. ($Ticket->Id, $Ticket->Subject)
+#: html/Ticket/History.html:46
+#: html/Ticket/History.html:49
+msgid "Ticket History # %1 %2"
+msgstr "ãƒã‚±ãƒƒãƒˆãƒ’ストリー # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "ãƒã‚±ãƒƒãƒˆID"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69
+#: html/Admin/Global/CustomFields/index.html:81
+#: lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "ãƒã‚±ãƒƒãƒˆæ·»ä»˜"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "ãƒã‚±ãƒƒãƒˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "ãƒã‚±ãƒƒãƒˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„タイプ"
+
+#: lib/RT/Ticket_Overlay.pm:603
+#: lib/RT/Ticket_Overlay.pm:617
+#: lib/RT/Ticket_Overlay.pm:628
+#: lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "ãƒã‚±ãƒƒãƒˆãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "ãƒã‚±ãƒƒãƒˆã®ä½œæˆãŒå¤±æ•—ã—ã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "ãƒã‚±ãƒƒãƒˆãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "ãƒã‚±ãƒƒãƒˆæƒ…å ±"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "ãƒã‚±ãƒƒãƒˆã‚¦ã‚©ãƒƒãƒãƒ£ãƒ¼"
+
+#. (ref $self)
+#: lib/RT/Search/FromSQL.pm:82
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64
+#: html/Admin/Global/CustomFields/index.html:75
+#: html/Elements/Tabs:71
+#: html/Search/Elements/Chart:109
+#: lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "ãƒã‚±ãƒƒãƒˆ"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "%1ã‹ã‚‰ã®ãƒã‚±ãƒƒãƒˆ"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:134
+#: html/Ticket/Create.html:183
+#: html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "所è¦æ™‚間(予想)"
+
+#: html/Search/Elements/PickBasics:135
+#: html/Ticket/Create.html:196
+#: html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "残り時間"
+
+#: html/Search/Elements/PickBasics:133
+#: html/Ticket/Create.html:189
+#: html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "作業時間"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "残り時間"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "表示ã™ã‚‹æ™‚é–“"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "使ã£ãŸæ™‚é–“"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "使ã£ãŸæ™‚é–“"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "タイトル"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã®ãƒ‡ã‚£ãƒ•ã‚’ã¤ãã‚‹ãŸã‚ã«:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã®ãƒ‡ã‚£ãƒ•ã‚’ã¤ãã‚‹ãŸã‚ã«:\\n"
+
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+#: html/Elements/Footer:62
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "サãƒãƒ¼ãƒˆã€ãƒˆãƒ¬ãƒ¼ãƒ‹ãƒ³ã‚°ã€ã‚«ã‚¹ã‚¿ãƒ é–‹ç™ºã¾ãŸã¯ãƒ©ã‚¤ã‚»ãƒ³ã‚¹å–å¾—ã«ã¤ã„ã¦ã¯ã€%1ã¾ã§ãŠå•ã„åˆã‚ã›ãã ã•ã„"
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr ""
+
+#: html/Admin/Elements/Tabs:68
+#: html/Admin/index.html:88
+#: html/Elements/Tabs:74
+#: html/Tools/index.html:46
+#: html/Tools/index.html:49
+msgid "Tools"
+msgstr "表示ツール"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "åˆè¨ˆ"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr ""
+
+#. ($self->Data)
+#: lib/RT/Transaction_Overlay.pm:805
+msgid "Transaction %1 purged"
+msgstr "トランザクション%1ãŒæ¶ˆåŽ»ã•ã‚Œã¾ã—ãŸ"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "トランザクションãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "トランザクションã¯å¤‰æ›´ã•ã‚Œã‚‹ã“ã¨ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "権利: %1を削除ã—ã¦ã„ã¾ã™"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "ç«æ›œæ—¥"
+
+#: html/Admin/CustomFields/Modify.html:66
+#: html/Admin/Elements/EditCustomField:65
+#: html/Ticket/Elements/AddWatchers:54
+#: html/Ticket/Elements/AddWatchers:65
+#: html/Ticket/Elements/AddWatchers:75
+#: lib/RT/Ticket_Overlay.pm:1168
+#: lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "タイプ"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "å°Žå…¥ã•ã‚Œã¦ã„ãªã„"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unixログイン"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Unixユーザーãƒãƒ¼ãƒ "
+
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+#: lib/RT/Attachment_Overlay.pm:289
+#: lib/RT/Record.pm:861
+msgid "Unknown ContentEncoding %1"
+msgstr "ä¸æ˜Žãªã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚°%1"
+
+#: html/Search/Build.html:455
+#: lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "無制é™"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "検索ã«ãƒžãƒƒãƒã—ã¾ã›ã‚“ã§ã—ãŸ"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr ""
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "ã¨ã‚‰ã‚Œã¦ã„ãªã„"
+
+#: html/Admin/Elements/EditScrip:128
+#: html/Elements/RT__Ticket/ColumnMap:302
+#: html/Search/Bulk.html:193
+#: html/Search/Bulk.html:75
+msgid "Update"
+msgstr "æ›´æ–°"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "IDã‚’æ›´æ–°"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "ãƒã‚±ãƒƒãƒˆã®æ›´æ–°"
+
+#: html/Search/Bulk.html:126
+#: html/Ticket/ModifyAll.html:87
+#: html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "タイプã®æ›´æ–°"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "ã™ã¹ã¦ã®ãƒã‚±ãƒƒãƒˆã‚’一度ã«æ›´æ–°ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Eメールを更新"
+
+#: html/Search/Bulk.html:200
+#: html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "複数ã®ãƒã‚±ãƒƒãƒˆã‚’æ›´æ–°"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "åå‰ã‚’æ›´æ–°"
+
+#: lib/RT/Action/CreateTickets.pm:750
+#: lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "更新内容ã¯è¨˜éŒ²ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "é¸æŠžã•ã‚ŒãŸãƒã‚±ãƒƒãƒˆã‚’æ›´æ–°ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "ç½²åã‚’æ›´æ–°ã™ã‚‹"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "ãƒã‚±ãƒƒãƒˆã‚’æ›´æ–°ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "ãƒã‚±ãƒƒãƒˆ # %1 ã‚’æ›´æ–°"
+
+#. ($Ticket->id)
+#: html/SelfService/Update.html:112
+#: html/SelfService/Update.html:47
+msgid "Update ticket #%1"
+msgstr "ãƒã‚±ãƒƒãƒˆ #%1 ã‚’æ›´æ–°"
+
+#. ($TicketObj->id, $TicketObj->Subject)
+#: html/Ticket/Update.html:158
+msgid "Update ticket #%1 (%2)"
+msgstr ""
+
+#: lib/RT/Action/CreateTickets.pm:748
+#: lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr ""
+
+#: html/Elements/SelectDateType:54
+#: html/Ticket/Elements/ShowDates:72
+#: lib/RT/CustomField_Overlay.pm:1284
+#: lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "最終更新日時"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "アップロード"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr ""
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr ""
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "ユーザー%1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "ユーザー%1パスワード: %2\\n"
+
+#. ($args{'Owner'})
+#: lib/RT/Ticket_Overlay.pm:506
+msgid "User '%1' could not be found."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "ユーザー'%1'ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "ユーザー'%1'ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“\\n"
+
+#: etc/initialdata:132
+#: etc/initialdata:206
+msgid "User Defined"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "ユーザーID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "ユーザーID"
+
+#: html/Admin/Elements/CustomFieldTabs:72
+#: html/Admin/Elements/GroupTabs:68
+#: html/Admin/Elements/QueueTabs:85
+#: html/Admin/Elements/SystemTabs:68
+#: html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "ユーザー権利"
+
+#. ($msg)
+#: html/Admin/Users/Modify.html:301
+msgid "User could not be created: %1"
+msgstr "ユーザーを作æˆã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "ユーザーãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
+
+#: html/Admin/CustomFields/GroupRights.html:74
+#: html/Admin/Global/GroupRights.html:88
+#: html/Admin/Groups/GroupRights.html:75
+#: html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "ユーザーãŒã‚°ãƒ«ãƒ¼ãƒ—を決定ã—ã¾ã—ãŸ"
+
+#: lib/RT/User_Overlay.pm:592
+#: lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "ユーザーã«é€šå‘Šã•ã‚Œã¾ã—ãŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "ユーザービュー"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:69
+#: html/Elements/Login:90
+#: html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "ユーザーå"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55
+#: html/Admin/Elements/SelectNewGroupMembers:47
+#: html/Admin/Elements/Tabs:53
+#: html/Admin/Global/CustomFields/index.html:64
+#: html/Admin/Groups/Members.html:76
+#: html/Admin/Queues/People.html:89
+#: html/Admin/index.html:62
+#: html/User/Groups/Members.html:79
+#: lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "ユーザー"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "ユーザーãŒæ¤œç´¢åŸºæº–ã«ã‚ã£ã¦ã„ã¾ã™"
+
+#. ($transaction->id)
+#: bin/rt-crontool:134
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:130
+#: html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "ウォッãƒãƒ£ãƒ¼"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "ウェブエンコーディング"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "水曜日"
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr ""
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr ""
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr ""
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr ""
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr ""
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr ""
+
+#: etc/initialdata:178
+#: etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr ""
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr ""
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr ""
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr ""
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr ""
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:188
+#: html/User/Prefs.html:88
+msgid "Work"
+msgstr "仕事"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "オフラインã§ä½œæ¥­ã™ã‚‹"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "電話(仕事)"
+
+#: html/Ticket/Elements/ShowBasics:63
+#: html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "ç´¯ç©ä½œæ¥­æ™‚é–“"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "ã™ã§ã«ã“ã®ãƒã‚±ãƒƒãƒˆã‚’担当ã—ã¦ã„ã¾ã™"
+
+#: html/autohandler:214
+#: html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "èªè¨¼ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "自分ãŒæ‹…当ã—ã¦ã„ã‚‹ãƒã‚±ãƒƒãƒˆã‹ã€æ‹…当者ã®ã„ãªã„ãƒã‚±ãƒƒãƒˆã—ã‹å¤‰æ›´ã™ã‚‹äº‹ãŒã§ãã¾ã›ã‚“。"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "ã‚ãªãŸã¯ãã®ãƒã‚±ãƒƒãƒˆã‚’見る許å¯ãŒã‚ã‚Šã¾ã›ã‚“。\\n"
+
+#. ($num, $queue)
+#: docs/design_docs/string-extraction-guide.txt:47
+#: lib/RT/StyleGuide.pod:780
+msgid "You found %1 tickets in queue %2"
+msgstr "キュー%2ã§%1件ã®ãƒã‚±ãƒƒãƒˆãŒãƒ’ットã—ã¾ã—ãŸ"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "RTã‹ã‚‰ãƒ­ã‚°ã‚¢ã‚¦ãƒˆã—ã¾ã—ãŸ"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "指定ã®ã‚­ãƒ¥ãƒ¼ã§ãƒã‚±ãƒƒãƒˆä½œæˆã®è¨±å¯ãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "指定ã®ã‚­ãƒ¥ãƒ¼ã§ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®ä½œæˆãŒã§ãã¾ã›ã‚“"
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "トップページ"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "ã‚ãªãŸã®%1ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "ã‚ãªãŸã®RT管ç†è€…ã¯RTを呼ã³å‡ºã™ãƒ¡ãƒ¼ãƒ«aliasesを設定ã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr ""
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "ã‚ãªãŸã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯æ‰¿èªã•ã‚Œã¾ã—ãŸ"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "ã‚ãªãŸã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯æ‹’å¦ã•ã‚Œã¾ã—ãŸ"
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¨ãƒ‘スワードãŒé–“é•ã£ã¦ã„ã¾ã™"
+
+#: html/Admin/Users/Modify.html:168
+#: html/User/Prefs.html:149
+msgid "Zip"
+msgstr "郵便番å·"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr ""
+
+#. ($right->PrincipalObj->Object->SelfDescription)
+#: html/User/Elements/DelegateRights:80
+msgid "as granted to %1"
+msgstr "%1ã¸ã®è¨±å¯"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr ""
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "ãŒä»¥ä¸‹ã‚’å«ã‚€"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "通知ã¯ï¼ˆãŠãらã)é€ä¿¡ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "通知ãŒé€ä¿¡ã•ã‚Œã¾ã—ãŸ"
+
+#: html/Admin/Queues/Modify.html:98
+#: lib/RT/Date.pm:346
+msgid "days"
+msgstr "æ—¥"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "削除"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "削除"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "ã‚ã„ã¾ã›ã‚“"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "ãŒä»¥ä¸‹ã‚’å«ã¾ãªã„"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "ç­‰ã—ã„"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr ""
+
+#: html/Search/Build.html:533
+#: html/Search/Build.html:552
+#: html/Search/Build.html:574
+#: html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "より大ãã„"
+
+#. ($self->Name)
+#: lib/RT/Group_Overlay.pm:214
+msgid "group '%1'"
+msgstr "グループ'%1'"
+
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+#: html/Search/Results.html:88
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "時間"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "ID"
+
+#: html/Elements/SelectBoolean:53
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:57
+#: html/Search/Elements/PickBasics:162
+#: html/Search/Elements/PickBasics:74
+#: html/Search/Elements/PickBasics:90
+#: html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "ãŒä»¥ä¸‹ã§ã‚ã‚‹"
+
+#: html/Elements/SelectBoolean:57
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:58
+#: html/Search/Elements/PickBasics:163
+#: html/Search/Elements/PickBasics:75
+#: html/Search/Elements/PickBasics:91
+#: html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "ãŒä»¥ä¸‹ã§ã¯ãªã„"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "よりå°ã•ã„"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "åˆã†"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "分"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "分"
+
+#: NOT FOUND IN SOURCE
+msgid ""
+"modifications\\n"
+"\\n"
+msgstr ""
+"修正\\n"
+"\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "月"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "æ–°è¦"
+
+#: html/Admin/Elements/PickCustomFields:64
+#: html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr ""
+
+#: html/Admin/Elements/EditQueueWatchers:48
+#: html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "ãªã—"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "ç­‰ã—ããªã„"
+
+#: html/SelfService/Elements/MyRequests:82
+#: lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "ç€æ‰‹"
+
+#. ($self->Name, $user->Name)
+#: lib/RT/Group_Overlay.pm:219
+msgid "personal group '%1' for user '%2'"
+msgstr "ユーザー '%2' ã®ãƒ‘ーソナルグループ '%1' "
+
+#. ($queue->Name, $self->Type)
+#: lib/RT/Group_Overlay.pm:227
+msgid "queue %1 %2"
+msgstr "キュー %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "æ‹’å¦"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "解決済ã¿"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "秒"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "ä¿ç•™"
+
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+#: html/Search/Results.html:89
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#. ($self->Type)
+#: lib/RT/Group_Overlay.pm:222
+msgid "system %1"
+msgstr "システム %1"
+
+#. ($self->Type)
+#: lib/RT/Group_Overlay.pm:233
+msgid "system group '%1'"
+msgstr "システムグループ '%1'"
+
+#: html/Elements/Error:64
+#: html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "呼ã³å‡ºã—ã¦ã„るコンãƒãƒ¼ãƒãƒ³ãƒˆã¯ãªãœæ¬¡ã®ã‚ˆã†ãªã“ã¨ãŒèµ·ã“ã‚‹ã®ã‹ç‰¹å®šã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#. ($self->Instance, $self->Type)
+#: lib/RT/Group_Overlay.pm:230
+msgid "ticket #%1 %2"
+msgstr "ãƒã‚±ãƒƒãƒˆã€€#%1 %2"
+
+#. ($self->Id)
+#: lib/RT/Group_Overlay.pm:236
+msgid "undescribed group %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "表示ã•ã‚Œãªã„グループ %1"
+
+#. ($user->Object->Name)
+#: lib/RT/Group_Overlay.pm:211
+msgid "user %1"
+msgstr "ユーザー %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "週間"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "テンプレート %1ã¨"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "å¹´"
+
diff --git a/rt/lib/RT/I18N/nl.po b/rt/lib/RT/I18N/nl.po
new file mode 100644
index 0000000..d7f01a4
--- /dev/null
+++ b/rt/lib/RT/I18N/nl.po
@@ -0,0 +1,6195 @@
+#
+msgit ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"PO-Revision-Date: 2005-10-03 13:50-0400\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: rt-devel <rt-devel@lists.fsck.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "msgstr "" "
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:86
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:941
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:257
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:361
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:397
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:634 lib/RT/Transaction_Overlay.pm:677
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 toegevoegd"
+
+#: lib/RT/Date.pm:358
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 geleden"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:641
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 veranderd naar %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:637 lib/RT/Transaction_Overlay.pm:683
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 verwijderd"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:64 html/Ticket/Elements/PreviewScrips:99
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 met sjabloon %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 dit ticket\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:104 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:65
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr ""
+
+#: bin/rt-crontool:194 bin/rt-crontool:201 bin/rt-crontool:207
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Een argument om door te geven aan %2"
+
+#: bin/rt-crontool:210
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Uitvoer status herzieningen naar STDOUT"
+
+#: bin/rt-crontool:204
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Specificeer de actie module die u wenst te gebruiken"
+
+#: bin/rt-crontool:198
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Specificeer de conditie module die u wenst te gebruiken"
+
+#: bin/rt-crontool:191
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Specificeer de zoek module die u wenst te gebruiken"
+
+ $RT::VERSION,
+ '2004',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+ $RT::VERSION,
+ '2004',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+ $RT::VERSION,
+ '2004',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+ $RT::VERSION,
+ '2004',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+ $RT::VERSION,
+ '2004',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+ $RT::VERSION,
+ '2005',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+ $RT::VERSION,
+ '2005',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+ $RT::VERSION,
+ '2005',
+ '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+#: html/Elements/Footer:57
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2005', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:151
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "%1 ScripAction geladen"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 toegevoegd als waarde voor %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "%1 aliassen hebben een TicketId nodig om mee te werken"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "%1 aliassen hebben een TicketId nodig om mee te werken"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "%1 aliassen hebben een TicketId nodig om mee te werken (van %2) %3"
+
+#: lib/RT/Link_Overlay.pm:145 lib/RT/Link_Overlay.pm:152
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 lijkt een lokaal object te zijn maar kan niet gelokaliseerd worden in de database"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:518
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 door %2"
+
+#: lib/RT/Transaction_Overlay.pm:775 lib/RT/Transaction_Overlay.pm:784 lib/RT/Transaction_Overlay.pm:787
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 veranderd van %2 naar %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr ""
+
+#: lib/RT/Record.pm:945
+msgid "%1 could not be set to %2."
+msgstr "%1 kon niet veranderd worden naar %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 kon geen transactie initiëren (%2)"
+
+#: lib/RT/Ticket_Overlay.pm:2732
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 kon status niet veranderen naar opgelost. RT's Database zou inconsistent kunnen zijn"
+
+#: lib/RT/Transaction_Overlay.pm:558
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 aangemaakt"
+
+#: lib/RT/Transaction_Overlay.pm:563
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 verwijderd"
+
+#: html/Search/Chart:71
+#. ($Query, $PrimaryGroupBy)
+msgid "%1 grouped by %2"
+msgstr ""
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "De %1 hoogste prioriteit tickets die ik bezit"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "De %1 hoogste prioriteit tickets die ik bezit..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "De %1 hoogste prioriteit tickets waar ik om verzocht heb..."
+
+#: bin/rt-crontool:186
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 is een gereedschap om te reageren op tickets van een extern rooster programma, zoals cron"
+
+#: lib/RT/Queue_Overlay.pm:864
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 is niet langer een %2 voor deze queue"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 is niet langer een %2 voor dit ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 is niet langer een waarde voor custom field %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 is niet een geldig queue id"
+
+#: html/Ticket/Elements/ShowTime:2 html/Ticket/Elements/ShowTime:4
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "De %1 nieuwste tickets die van niemand zijn"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 niet afgebeeld"
+
+#: lib/RT/CustomField_Overlay.pm:892
+msgid "%1 objects"
+msgstr "%1 objecten"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 rechten"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 gelukt\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 type onbekend voor $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 type onbekend voor %2"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 zal alle leden van een opgelost groep ticket omzetten."
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:56
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "%1's opgeslagen zoekopdrachten"
+
+#: lib/RT/Transaction_Overlay.pm:468
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: geen attachment gespecificeerd"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:4
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1119
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1 is een ongeldige waarde voor status"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' onherkende actie. "
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Vink hokje af om te verwijderen)"
+
+#: html/Ticket/Elements/PreviewScrips:94
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Markeer hokje om notificaties voor de genoemde ontvangers uit te zetten)"
+
+#: html/Ticket/Elements/PreviewScrips:118
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Markeer hokje om notificaties voor de genoemde ontvangers aan te zetten)"
+
+#: html/Ticket/Create.html:217
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Voer ticket ids of URLs in, gescheiden door spaties)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Indien leeggelaten, wordt voorzien van %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(Geen Waarde)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Geen eigen velden)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Geen Leden)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:49
+msgid "(No scrips)"
+msgstr "(Geen scrips)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Geen slablonen)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr (Geen)""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Stuur een BCC van deze update naar een door komma's gescheiden lijst van e-mail adressen. Deze personen zullen toekomstige updates <b>niet</b> ontvangen.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Stuur een BCC van deze update naar een door komma's gescheiden lijst van e-mail adressen. Deze personen zullen toekomstige updates <strong>niet</strong> ontvangen. )"
+
+#: html/Ticket/Create.html:102
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Stuur een CC van deze update naar een door komma's gescheiden lijst van administratieve e-mail adressen. Deze personen zullen toekomstige updates <strong>wel</strong> ontvangen. )"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Stuur een CC van deze update naar een door komma's gescheiden lijst van e-mail adressen. Deze personen zullen toekomstige updates <b>niet</b> ontvangen.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Stuur een CC van deze update naar een door komma's gescheiden lijst van e-mail adressen. Deze personen zullen toekomstige updates <strong>niet</strong> ontvangen. )"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Stuur een CC van deze update naar een door komma's gescheiden lijst van e-mail-adressen. Deze personen zullen toekomstige updates <b>wel</b> ontvangen.)"
+
+#: html/Ticket/Create.html:92
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Stuur een CC van deze update naar een door komma's gescheiden lijst van e-mail-adressen. Deze personen zullen toekomstige updates <stong>wel</stong> ontvangen.)"
+
+#: html/Admin/Elements/EditScrip:102
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Gebruik deze velden wanneer U kiest voor 'User Defined' in een conditie of actie)"
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(leeg)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(geen naam weergegeven)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(geen onderwerp)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:70 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:86 lib/RT/Transaction_Overlay.pm:578
+msgid "(no value)"
+msgstr "(geen waarde)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(geen waarden)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(slechts één ticket)"
+
+#: html/Elements/RT__Ticket/ColumnMap:147
+msgid "(pending approval)"
+msgstr "(wacht op goedkeuring)"
+
+#: html/Elements/RT__Ticket/ColumnMap:150
+msgid "(pending other Collection)"
+msgstr "(wacht op andere Collection)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(wacht op andere tickets)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(verplicht)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(zonder titel)"
+
+#: html/Ticket/Elements/Reminders:88
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:58
+msgid "-"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Nieuw ticket in\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Nieuw ticket in\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Een leeg sjabloon"
+
+#: html/Admin/Users/Modify.html:360
+msgid "A password was not set, so user won't be able to login."
+msgstr "Omdat er geen wachtwoord gezet is, kunt u niet inloggen"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Deleted"
+msgstr "ACE Verwijderd"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Loaded"
+msgstr "ACE Geladen"
+
+#: lib/RT/ACE_Overlay.pm:175 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE niet gevonden"
+
+#: lib/RT/ACE_Overlay.pm:854
+msgid "ACEs can only be created and deleted."
+msgstr "ACEs kunnen allen gecreëerd of verwijderd worden."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Afbraak om ongewenste ticket aanpassing te voorkomen.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Over mijzelf"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Toegangscontrole"
+
+#: html/Admin/Elements/EditScrip:71
+msgid "Action"
+msgstr "Actie"
+
+#: lib/RT/Scrip_Overlay.pm:173
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Actie %1 niet gevonden"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Actie uitgevoerd."
+
+#: bin/rt-crontool:148
+msgid "Action committed.\\n"
+msgstr ""
+
+#: bin/rt-crontool:144
+msgid "Action prepared..."
+msgstr "Actie voorbereid..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Voeg toe"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Voeg AdminCc toe"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Voeg Cc toe"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Voeg kolommen toe"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Criterium toevoegen"
+
+#: html/Ticket/Create.html:146 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Voeg Meer Bestanden Toe"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Voeg aanvrager Toe"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip to this queue"
+msgstr "Voeg een Scrip toe aan deze queue"
+
+#: html/Admin/Global/Scrip.html:76
+msgid "Add a scrip which will apply to all queues"
+msgstr "Voeg een scrip toe dat voor alle queues zal gelden"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Toevoegen en zoeken"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Voeg commentaar of reacties toe aan geselecteerde tickets"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Voeg leden toe"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Voeg nieuwe toeschouwers toe"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Voeg dit criterium toe aan de zoekopdracht"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "VoegVolgendeStaatToe"
+
+#: lib/RT/Queue_Overlay.pm:764
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Hoofdgebruiker toegevoegd als %1 voor deze queue"
+
+#: lib/RT/Ticket_Overlay.pm:1426
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Hoofdgebruiker toegevoegd als %1 voor dit ticket"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adres1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adres2"
+
+#: html/Ticket/Create.html:97
+msgid "Admin Cc"
+msgstr "Beheerder Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr ""
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr ""
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Beheerder queues"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Beheerdergebruikers"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Beheerder/Globale configuratie"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Beheerder/Groepen"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Beheerder/Queue/Basis"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:114
+msgid "AdminCc"
+msgstr "BeheerderCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "BeheerderCommentaar"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "BeheerderCorrespondentie"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "Beheerder custom field"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "BeheerderSpecifiekeVelden"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "BeheerderGroep"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "BeheerderGroepLidmaatschap"
+
+#: lib/RT/System.pm:81
+msgid "AdminOwnPersonalGroups"
+msgstr "BeheerderBezitPersoonlijkeGroepen"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "AdminQueue"
+msgstr "Beheerder queue"
+
+#: lib/RT/System.pm:82
+msgid "AdminUsers"
+msgstr "BeheerderGebruikers"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Administratieve Cc"
+
+#: html/Ticket/Elements/Tabs:209
+msgid "Advanced"
+msgstr "Geavanceerd"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Uitgebreid Zoeken"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Na"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Leeftijd"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Alle Gegeven Goedkeuringen"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Alle queues"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Stuurt altijd een bericht naar de aanvrager ongeacht de verzender van het bericht"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Heeft betrekking op"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Doorvoeren"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Doorvoeren van de wijzigingen"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Autorisatie"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Goedkeuring #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Goedkeuring #%1: Notities niet bewaard vanwege een systeem fout"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Goedkeuring #%1: Notities bewaard"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Goedkeuring Details"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr ""
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Goedkeuring diagram"
+
+#: html/Approvals/Elements/Approve:65
+msgid "Approve"
+msgstr "Goedkeuring"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Notities van de goedkeurer: %1"
+
+#: lib/RT/Date.pm:440
+msgid "Apr."
+msgstr "Ggk."
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "April"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "Oplopend"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Oplopend"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Assign and remove custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "AssignCustomFields"
+msgstr "WijsSpecifiekeVeldenToe"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Toevoegen"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:142
+msgid "Attach file"
+msgstr "Hecht bestand aan"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:130 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Aangehecht bestand"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Attachment '%1' kon niet geladen worden"
+
+#: lib/RT/Transaction_Overlay.pm:476
+msgid "Attachment created"
+msgstr "Attachment gecreëerd"
+
+#: lib/RT/Tickets_Overlay.pm:1932
+msgid "Attachment filename"
+msgstr "Attachment bestandsnaam"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Attachments"
+
+#: lib/RT/Attributes_Overlay.pm:172
+msgid "Attribute Deleted"
+msgstr "Attribuut Verwijderd"
+
+#: lib/RT/Date.pm:444
+msgid "Aug."
+msgstr "Aug."
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "Augustus"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "AuthenticatieSysteem"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Automatisch-antwoord"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Automatisch-antwoord aan aanvragers"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "Automatisch-antwoord aan aanvragers"
+
+#: html/Widgets/SelectionBox:186
+msgid "Available"
+msgstr "Beschikbaar"
+
+#: NOT FOUND IN SOURCE
+msgid "Available Columns"
+msgstr "Beschikbare Kolommen"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Ongeldige PGP Signature: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Ongeldig attachment id. Kan attachment '%1' niet vinden\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Ongeldige data in %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Ongeldig transactienummer voor attachment. %1 zou %2 moeten zijn\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Basis"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:95
+msgid "Be sure to save your changes"
+msgstr "Zorg ervoor dat u uw veranderingen bewaard"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:360
+msgid "Before"
+msgstr "Voor"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "Begin Goedkeuring"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Blanco"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "URL voor deze zoekopdracht, geschikt als bookmark"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Korte koppen"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Bulk Update"
+msgstr "Bulk update"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Bulk ticketherziening"
+
+#: lib/RT/User_Overlay.pm:1790
+msgid "Can not modify system users"
+msgstr "Kan systeemgebruikers niet wijzigen"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Can this principal see this queue"
+msgstr "Kan deze hoofdgebruiker deze queue zien"
+
+#: lib/RT/CustomField_Overlay.pm:378
+msgid "Can't add a custom field value without a name"
+msgstr "Kan geen custom field toevoegen zonder een naam"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "Kan geen bewaarde zoekopdracht vinden om mee te werken"
+
+#: html/Search/Build.html:745
+msgid "Can't find a saved search to work with"
+msgstr "Kan geen bewaarde zoekopdracht vinden om mee te werken"
+
+#: lib/RT/Link_Overlay.pm:160
+msgid "Can't link a ticket to itself"
+msgstr "Kan een ticket niet koppelen aan zichzelf"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Kan niet samenvoegen met een reeds samengevoegd ticket. U zou deze boodschap nooit mogen krijgen"
+
+#: html/Search/Build.html:751
+msgid "Can't save this search"
+msgstr "Kan deze zoekopdracht niet opslaan"
+
+#: lib/RT/Record.pm:1281 lib/RT/Record.pm:1359
+msgid "Can't specifiy both base and target"
+msgstr "Kan niet zowel basis als doel specificeren"
+
+#: html/autohandler:193
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Kan gebruiker %1 niet aanmaken"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:87 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:113
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Wijzig wachtwoord"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Allemaal markeren"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:133 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Vink hokje om te verwijderen"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Vink hokje om de rechten te verwijderen"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Afstammelingen"
+
+#: html/NoAuth/js/util.js:157
+msgid "Choose a date"
+msgstr "Kies een datum"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Stad"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Allemaal wissen"
+
+#: html/Helpers/CalPopup.html:6
+msgid "Close window"
+msgstr "Sluit window"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Gesloten"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Gesloten verzoeken"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Combobox: Selecteer of voer meerdere waardes in"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Combobox: Selecteer of voer één waarde in"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Combobox: Selecteer of voer tot %1 waarden in"
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Commando niet begrepen!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:178
+msgid "Comment"
+msgstr "Commentaar"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Commentaar Adres"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Commentaar niet bewaard"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Comment on tickets"
+msgstr "Commentaar op tickets"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "CommentOnTicket"
+msgstr "Commentaar op ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Commentaar"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Commentaar (Wordt niet verstuurd aan aanvragers)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Commentaar (Wordt niet verstuurd aan aanvragers)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Commentaar over %1"
+
+#: html/Admin/Users/Modify.html:221 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Opmerkingen over deze gebruiker"
+
+#: lib/RT/Transaction_Overlay.pm:621
+msgid "Comments added"
+msgstr "Commentaar toegevoegd"
+
+#: lib/RT/Action/Generic.pm:176
+msgid "Commit Stubbed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Compilatie Restricties"
+
+#: html/Admin/Elements/EditScrip:63
+msgid "Condition"
+msgstr "Voorwaarde"
+
+#: bin/rt-crontool:131
+msgid "Condition matches..."
+msgstr "Voorwaarde komt overeen..."
+
+#: lib/RT/Scrip_Overlay.pm:189
+msgid "Condition not found"
+msgstr "Voorwaarde niet gevonden"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Configuratie"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Bevestig"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "ContactInfoSysteem"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Contact datum '%1' kon niet ontleed worden"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Inhoud"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:64
+msgid "Copy"
+msgstr ""
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Correspondentie"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Correspondentieadres"
+
+#: lib/RT/Transaction_Overlay.pm:617
+msgid "Correspondence added"
+msgstr "Correspondentie toegevoegd"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Correspondentie niet bewaard"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Kan het nieuw custom field niet toevoegen voor dit ticket. "
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "Kan het nieuw custom field niet toevoegen voor dit ticket. %1"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Kan de nieuwe waarde voor dit custom field niet toevoegen"
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Kan het nieuwe custom field niet toevoegen. %1 "
+
+#: lib/RT/Ticket_Overlay.pm:2993 lib/RT/Ticket_Overlay.pm:3001 lib/RT/Ticket_Overlay.pm:3018
+msgid "Could not change owner. "
+msgstr "Kan de eigenaar niet wijzigen. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Kan custom field niet creëren"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr ""
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Kan de groep niet creëren"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Kan het sjabloon niet creëren: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1052 lib/RT/Ticket_Overlay.pm:397
+msgid "Could not create ticket. Queue not set"
+msgstr "Kan het ticket niet creëren. Queue niet ingesteld"
+
+#: lib/RT/User_Overlay.pm:256 lib/RT/User_Overlay.pm:270 lib/RT/User_Overlay.pm:279 lib/RT/User_Overlay.pm:288 lib/RT/User_Overlay.pm:297 lib/RT/User_Overlay.pm:311 lib/RT/User_Overlay.pm:321 lib/RT/User_Overlay.pm:497
+msgid "Could not create user"
+msgstr "Kan de gebruiker niet creëren"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "Kan toeschouwer niet creëren voor aanvrager"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Kan geen ticket vinden met id %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Kan groep %1 niet vinden. "
+
+#: lib/RT/Queue_Overlay.pm:742 lib/RT/Ticket_Overlay.pm:1394
+msgid "Could not find or create that user"
+msgstr "Kan deze gebruiker niet vinden of creëren"
+
+#: lib/RT/Queue_Overlay.pm:803 lib/RT/Ticket_Overlay.pm:1475
+msgid "Could not find that principal"
+msgstr "Kan deze hoofdgebruiker niet vinden"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Kan gebruiker %1 niet vinden."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Kan custom field %1 niet laden"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Kan groep niet laden"
+
+#: lib/RT/SavedSearch.pm:120
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:188
+msgid "Could not load search attribute"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:762
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Kan deze hoofdgebruiker geen %1 maken voor deze queue"
+
+#: lib/RT/Ticket_Overlay.pm:1415
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Kan deze hoofdgebruiker geen %1 maken voor dit ticket"
+
+#: lib/RT/Queue_Overlay.pm:861
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Kan deze hoofdgebruiker niet verwijderen als %1 voor deze queue"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Kan deze hoofdgebruiker niet verwijderen als %1 voor dit ticket"
+
+#: lib/RT/User_Overlay.pm:192
+msgid "Could not set user info"
+msgstr "Kan gebruikersinformatie niet toewijzen"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Kan gebruiker niet toevoegen aan groep"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Kan geen transactie creëren: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Kan niet bepalen welke actie te ondernemen aan de hand van gpg's antwoord\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Kan de groep niet vinden\\n"
+
+#: lib/RT/Record.pm:954
+msgid "Couldn't find row"
+msgstr "Kan de rij niet vinden"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Kan deze hoofdgebruiker niet vinden"
+
+#: lib/RT/CustomField_Overlay.pm:408
+msgid "Couldn't find that value"
+msgstr "Kan die waarde niet vinden"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "Kan die toeschouwer niet vinden"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Kan gebruiker niet vinden\\n"
+
+#: lib/RT/CurrentUser.pm:146
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Kan %1 niet laden uit de gebruikersdatabase.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load KeywordSelects."
+msgstr "Kan KeywordSelects niet laden."
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "Kan het RT configuratie bestand niet laden '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Kan de scrips niet laden"
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Kan de groep %1 niet laden"
+
+#: lib/RT/Link_Overlay.pm:203 lib/RT/Link_Overlay.pm:212 lib/RT/Link_Overlay.pm:239
+msgid "Couldn't load link"
+msgstr "Kan de link niet laden"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Kan het object %1 niet laden"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Kan de queue niet laden"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Kan de queue %1 niet laden "
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Kan het scrip niet laden"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Kan het sjabloon niet laden"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Kan de gebruiker (%1) niet laden"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Kan ticket '%1' niet laden"
+
+#: lib/RT/Ticket_Overlay.pm:2590
+#. ($args{'Base'})
+msgid "Couldn't resolve base '%1' into a URI."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2589
+#. ($args{'Target'})
+msgid "Couldn't resolve target '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Land"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:132 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:167 html/Ticket/Create.html:234
+msgid "Create"
+msgstr "Creëer"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Creëer Tickets"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Creëer een custom field"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Creëer een niuew Specifiek Veld"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global Scrip"
+msgstr "Creëer een nieuw globaal Scrip"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Creëer een nieuwe groep"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Creëer een nieuwe persoonlijke groep"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Creëer een nieuwe queue"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Creëer een nieuw scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Creëer een nieuw template"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:59
+msgid "Create a new ticket"
+msgstr "Creëer een nieuw ticket"
+
+#: html/Admin/Users/Modify.html:248 html/Admin/Users/Modify.html:303
+msgid "Create a new user"
+msgstr "Creëer een nieuwe gebruiker"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Creëer een queue"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Creëer een queue genaamd"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Creëer een verzoek"
+
+#: html/Admin/Queues/Scrip.html:80
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Creëer een scrip voor queue %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Creëer een sjabloon"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Maak een ticket aan"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "Creatie mislukt: %1 / %2 / %3 "
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Creëer nieuwe tickets gebaseerd op het sjabloon van dit scrip"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Creëer ticket"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Create tickets in this queue"
+msgstr "Creëer tickets in deze queue"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Creëer, verwijder en wijzig custom fields"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Create, delete and modify queues"
+msgstr "Creëer, verwijder en wijzig queues"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Creëer, verwijder en wijzig de leden van persoonlijke groepen"
+
+#: lib/RT/System.pm:82
+msgid "Create, delete and modify users"
+msgstr "Creëer, verwijder en wijzig gebruikers"
+
+#: lib/RT/System.pm:88
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "CreateTicket"
+msgstr "Creëer ticket"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1146
+msgid "Created"
+msgstr "Gecreëerd"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Custom field %1 gecreëerd"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Aangemaakt in een tijdvenster"
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Sjabloon %1 Gecreëerd"
+
+#: html/Tools/Reports/CreatedByDates.html:7
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Aanvrager"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Huidige Relaties"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Huidige Scrips"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Huidige leden"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Huidige rechten"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Huidige zoekcriteria"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Huidige toeschouwers"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Custom fields"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Custom fields voor %1"
+
+#: html/Admin/Elements/EditScrip:123
+msgid "Custom action cleanup code"
+msgstr "Specifieke actie opruim code"
+
+#: html/Admin/Elements/EditScrip:115
+msgid "Custom action preparation code"
+msgstr "Specifieke actie voorbereidings code"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom condition"
+msgstr "Specifieke voorwaarde"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Specifiek veld %1 %2 %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 does not apply to this object"
+msgstr "Specifiek Veld %1 is niet van toepassing op dit object"
+
+#: lib/RT/Tickets_Overlay.pm:2411
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Specifiek veld %1 heeft een waarde."
+
+#: lib/RT/Tickets_Overlay.pm:2407
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Specifiek veld %1 heeft geen waarde."
+
+#: lib/RT/Record.pm:1593 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Specifiek veld %1 niet gevonden"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Specifiek veld niet gevonden"
+
+#: lib/RT/CustomField_Overlay.pm:1155
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Specifiek veld waarde %1 kon niet gevonden worden voor custom field %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Specifiek veld waarde veranderd van %1 naar %2"
+
+#: lib/RT/CustomField_Overlay.pm:418
+msgid "Custom field value could not be deleted"
+msgstr "Specifiek veld waarde kon niet verwijderd worden"
+
+#: lib/RT/CustomField_Overlay.pm:1167
+msgid "Custom field value could not be found"
+msgstr "Specifiek veld waarde kon niet gevonden worden"
+
+#: lib/RT/CustomField_Overlay.pm:1169 lib/RT/CustomField_Overlay.pm:416
+msgid "Custom field value deleted"
+msgstr "Specifiek veld waarde verwijderd"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:625
+msgid "CustomField"
+msgstr "Custom field"
+
+#: html/Prefs/MyRT.html:70 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "Data fout"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:202 html/Ticket/Elements/ShowSummary:81 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Data"
+
+#: lib/RT/Date.pm:448
+msgid "Dec."
+msgstr "Dec."
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "December"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Standaard Auto-antwoord Sjabloon"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr ""
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Standaard queue"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Standaard aanvrager"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Standaard admin commentaar sjabloon"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Standaard admin correspondentie sjabloon"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Standaard correspondentie sjabloon"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Standaard transactie sjabloon"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Standaard: %1/%2 verandered van %3 naar %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Delegeer rechten"
+
+#: lib/RT/System.pm:85
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Delegeer specifieke rechten die aan u verleend zijn."
+
+#: lib/RT/System.pm:85
+msgid "DelegateRights"
+msgstr "DelegeerRechten"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Delegeren"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:205
+msgid "Delete"
+msgstr "Verwijderen"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:211
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Delete tickets"
+msgstr "Verwijder tickets"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "DeleteTicket"
+msgstr "VerwijderTicket"
+
+#: lib/RT/SavedSearch.pm:209
+msgid "Deleted search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Het verwijderen van dit object zou de referentiële integriteit kunnen ondermijnen"
+
+#: lib/RT/Queue_Overlay.pm:395
+msgid "Deleting this object would break referential integrity"
+msgstr "Het verwijderen van dit object zou de referentiële integriteit ondermijnen"
+
+#: lib/RT/User_Overlay.pm:513
+msgid "Deleting this object would violate referential integrity"
+msgstr "Het verwijderen van dit object zou de referentiële integriteit ondermijnen"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "Het verwijderen van dit object zou de referentiële integriteit ondermijnen"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Deny"
+msgstr "Wijs af"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Afhankelijkheid van"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Afhankelijkheden: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:705
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:745
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:702
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:742
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:219 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Is afhankelijk van"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "Aflopend"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Aflopend"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:151
+msgid "Describe the issue below"
+msgstr "Omschrijf onderstaande kwestie"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:56 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Omschrijving"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Details"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Toon"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Display Access Control List"
+msgstr "Toon de toegangscontrole lijst"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Toon de kolommen"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Display Scrip templates for this queue"
+msgstr "Toon de scrip template voor deze queue"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "Display Scrips for this queue"
+msgstr "Toon de scrips voor deze queue"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Toon de modus"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Toon de bewaarde zoekopdrachten voor deze groep"
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Toon ticket #%1"
+
+#: html/Elements/Footer:62
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Gedistribueerd onder versie 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> van de GNU GPL.<a/>"
+
+#: lib/RT/System.pm:76
+msgid "Do anything and everything"
+msgstr "Doe iets en alles"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Ververs deze pagina niet"
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Toon zoekresultaten niet"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Download"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Download als een met tabs gescheiden bestand"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:88 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1150
+msgid "Due"
+msgstr "Verwacht"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Verwachte datum '%1' kon niet ontleed worden"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "FOUT: Kan ticket '%1' niet laden: %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Wijzig"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Wijzig custom fields voor %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:9
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:9
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:9 html/Admin/Global/CustomFields/Queue-Transactions.html:9
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Wijzig Relaties"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Wijzig Zoekopdracht"
+
+#: html/Ticket/Elements/Tabs:207
+msgid "Edit Search"
+msgstr "Wijzig zoekopdracht"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Wijzig systeem sjablonen"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Wijzig sjablonen voor %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "WijzigOpgeslagenZoekopdrachten"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Bezig met wijzigen van de configuratie voor queue %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Bezit met het wijzigen van de configuratie voor gebruiker %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Bezit met het wijzigen van custom field %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Bezit met het wijzigen van lidmaatschap voor groep %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Bezit met het wijzigen van lidmaatschap voor persoonlijke groep %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Bezit met het wijzigen van sjabloon %1"
+
+#: lib/RT/Record.pm:1296 lib/RT/Record.pm:1373
+msgid "Either base or target must be specified"
+msgstr "Of basis of doel moeten gespecificeerd zijn"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "E-mail"
+
+#: lib/RT/User_Overlay.pm:236
+msgid "Email address in use"
+msgstr "E-mailadres in gebruik"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "E-mail adres"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "E-mailCodering"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Actief (Het uitzetten van dit vinkje deactiveert dit custom field)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Actief (Het uitzetten van dit vinkje deactiveert deze groep)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Actief (Het uitzetten van dit vinkje deactiveert deze queue)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Actieve queues"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:339 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Actieve status %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Voer meerdere waarden in"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Voer één waarde in"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr ""
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Vul tickets of URIs in om deze tickets aan te koppelen. Scheidt meerdere elementen met spaties."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr ""
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Fout"
+
+#: lib/RT/Queue_Overlay.pm:673
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Fout in paramaters naar Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Fout in paramaters naar Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:834
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1343
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Fout in paramaters naar Ticket->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Fout in paramaters naar Ticket->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1509
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr ""
+
+#: bin/rt-crontool:233
+msgid "Escalate tickets"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr ""
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Iedereen"
+
+#: bin/rt-crontool:219
+msgid "Example:"
+msgstr "Voorbeeld:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "ExternAuteurId"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "ExternContactInfoId"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Extra informatie"
+
+#: lib/RT/SavedSearch.pm:165
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:377
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Kan de gebruikers pseudogroep 'Privileged' niet vinden."
+
+#: lib/RT/User_Overlay.pm:384
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Kan de gebruikers pseudogroep 'Unprivileged' niet vinden."
+
+#: bin/rt-crontool:163
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Kan module %1 niet laden. (%2)"
+
+#: lib/RT/SavedSearch.pm:168
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:438
+msgid "Feb."
+msgstr "Feb."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Bestandsnaam"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Vul meerdere tekst velden"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Vul meerdere wikitekst velden"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Vul één tekst veld"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Vul één wiki tekst veld"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "Vul dit veld met een URL"
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Vul tot %1 tekst velden"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Vul tot %1 wiki tekst velden"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 lib/RT/Tickets_Overlay.pm:1828
+msgid "Final Priority"
+msgstr "Uiteindelijke prioriteit"
+
+#: lib/RT/Ticket_Overlay.pm:1141
+msgid "FinalPriority"
+msgstr "Uiteindelijke prioriteit"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Zoek groepen waarvan"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Zoek nieuwe/open tickets"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Zoek mensen waarvan"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Zoek tickets"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Beëindig Goedkeuring"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Eerste"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Eerste pagina"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Aap Noot Mies"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Aap!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Formaat"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr ""
+
+#: lib/RT/Record.pm:957
+msgid "Found Object"
+msgstr "Gevonden Object"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "Vrije vorm"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "Vrije vorm contact informatie"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "VrijevormMeerdere"
+
+#: lib/RT/Date.pm:417
+msgid "Fri."
+msgstr "Vr."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Volledige Kop"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Vul sjabloon vanuit bestand"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Bezig met het ophalen van de huidige gebruiker middels een pgp handtekening"
+
+#: lib/RT/Transaction_Overlay.pm:671
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Aan %1 gegeven"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Globaal"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Globaal custom fields"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Globaal sjabloon: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Ga"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Ga!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Goede pgp handtekening van %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Ga naar pagina"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Ga naar ticket"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Groep"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Groep %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Groeps rechten"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Groep heeft al een lid"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Groep kon niet gecreërd worden: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Groep gecreërd"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Groep heeft geen lid onder die naam"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:749 lib/RT/Queue_Overlay.pm:809 lib/RT/Ticket_Overlay.pm:1401 lib/RT/Ticket_Overlay.pm:1481
+msgid "Group not found"
+msgstr "Groep niet gevonden"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Groep niet gevonden.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Groep niet gespecificeerd.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1208
+msgid "Groups"
+msgstr "Groepen"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Groepen kunnen geen leden zijn van hun leden"
+
+#: html/Admin/Groups/index.html:82
+msgid "Groups matching search criteria"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Groepen waar deze gebruiker deel van uitmaakt"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hallo!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Hallo, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Geschiedenis"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr ""
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "ThuisNummer"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Startpagina"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "Uren"
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Ik heb %quant(%1,betonmixer)."
+
+#: html/Search/Build.html:400
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1753
+msgid "Id"
+msgstr "Id"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identiteit"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Als een goedkeuring afgewezen is, wijs het origineel af en verwijder goedkeuringen die in behandeling zijn"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Indien geen aanvrager is gespecificeerd, maak dan de tickets aan met deze aanvrager"
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Creëer tickets in deze queue indien geen queue is opgegeven."
+
+#: bin/rt-crontool:215
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Als dit gereedschap setgid zou zijn, zou een kwaadwillende lokale gebruiker dit gereedschap kunnen gebruiken om administratieve toegang te verkrijgen tot RT"
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Als u een van de bovenstaande elemented ververst heeft, zorg dan dat u"
+
+#: lib/RT/Record.pm:948
+msgid "Illegal value for %1"
+msgstr "Illegale waarde voor %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Afbeelding"
+
+#: lib/RT/Record.pm:951
+msgid "Immutable field"
+msgstr "Niet-wijzigbaar veld"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Neem inactieve groepen op in de weergave."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Neem inactieve queues op in de weergave."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Neem inactieve gebruikers op in de zoek opdracht"
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Pagina toevoegen"
+
+#: html/Search/Build.html:426
+msgid "Incomplete Query"
+msgstr "Incomplete query"
+
+#: html/Search/Build.html:423
+msgid "Incomplete query"
+msgstr "Incomplete query"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1803
+msgid "Initial Priority"
+msgstr "Initiële prioriteit"
+
+#: lib/RT/Ticket_Overlay.pm:1140 lib/RT/Ticket_Overlay.pm:1142
+msgid "InitialPriority"
+msgstr "Initiële prioriteit"
+
+#: lib/RT/ScripAction_Overlay.pm:134
+msgid "Input error"
+msgstr "Invoer fout"
+
+#: html/Elements/ValidateCustomFields:23 lib/RT/CustomField_Overlay.pm:1019 lib/RT/CustomField_Overlay.pm:1160
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3448
+msgid "Internal Error"
+msgstr "Interne Fout"
+
+#: lib/RT/Record.pm:309
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Interne Fout: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Ongeldig Groep Type"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Ongeldig Recht"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "Ongeldig Type"
+
+#: lib/RT/Record.pm:953
+msgid "Invalid data"
+msgstr "Ongeldige data"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Ongeldige eigenaar. Val terug op 'nobody'."
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:677
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "Ongeldig patroon: %1"
+
+#: lib/RT/Scrip_Overlay.pm:158 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Ongeldige queue"
+
+#: lib/RT/ACE_Overlay.pm:265 lib/RT/ACE_Overlay.pm:274 lib/RT/ACE_Overlay.pm:280 lib/RT/ACE_Overlay.pm:291
+msgid "Invalid right"
+msgstr "Ongeldige recht"
+
+#: lib/RT/Record.pm:284
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Ongeldige waarde voor %1"
+
+#: lib/RT/Record.pm:1611
+msgid "Invalid value for custom field"
+msgstr "Ongeldige waarde voor custom field"
+
+#: lib/RT/Ticket_Overlay.pm:414
+msgid "Invalid value for status"
+msgstr "Ongeldige waarde voor status"
+
+#: bin/rt-crontool:216
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Het is ontzettend belangrijk dat onbevoorrechtigde gebruikers geen toestemming hebben om dit gereedschap te gebruiken."
+
+#: bin/rt-crontool:217
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "We stellen voor dat u een onbevoorrechtigde unix gebruiker aanmaakt met het juiste groep lidmaatschap en RT toegang om dit gereedschap te gebruiken."
+
+#: bin/rt-crontool:188
+msgid "It takes several arguments:"
+msgstr "Het accepteerd meerdere argumenten:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Zaken die wachten op mijn goedkeuring"
+
+#: lib/RT/Date.pm:437
+msgid "Jan."
+msgstr "Jan."
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "Januari"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Sluit u aan of verlaat deze groep"
+
+#: lib/RT/Date.pm:443
+msgid "Jul."
+msgstr "Jul."
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Jumbo"
+
+#: lib/RT/Date.pm:442
+msgid "Jun."
+msgstr "Jun."
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Sleutelwoord"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Taal"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Taal"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Laatste"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Laatste Contact"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Laatste contact"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Laatste bericht"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Laatste aanpassing"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "Laatste aangepast door"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Over"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Geef deze gebruiker toegang tot RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Geef deze gebruiker rechten"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Eigenaar wordt gelimieteerd tot %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Queue wordt gelimiteerd tot %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1307
+msgid "Link already exists"
+msgstr "Koppeling bestaat al"
+
+#: lib/RT/Record.pm:1321
+msgid "Link could not be created"
+msgstr "Koppeling kon niet gecreëerd worden"
+
+#: lib/RT/Record.pm:1327
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Koppeling gecreëerd (%1)"
+
+#: lib/RT/Record.pm:1388
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Koppelink verwijderd (%1)"
+
+#: lib/RT/Record.pm:1394
+msgid "Link not found"
+msgstr "Koppeling niet gevonden"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Koppel ticket #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "Link waarden aan"
+
+#: html/Ticket/Create.html:215 html/Ticket/Elements/ShowSummary:87 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Koppelingen"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load"
+msgstr "Laden"
+
+#: html/Search/Elements/EditSearches:71
+msgid "Load saved search:"
+msgstr "Laad opgeslagen zoekopdracht:"
+
+#: lib/RT/System.pm:87
+msgid "LoadSavedSearch"
+msgstr "Laad opgeslagen zoekopdracht"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Laad perl modules"
+
+#: lib/RT/SavedSearch.pm:112
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Geladen zoekopdracht %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Locatie"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Log folder %1 niet gevonden of niet toegankelijk.\\n RT kan niet starten."
+
+#: html/Elements/Header:86
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Aangemeld als %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Aanmelden"
+
+#: html/Elements/Header:96
+msgid "Logout"
+msgstr "Afmelden"
+
+#: lib/RT/CustomField_Overlay.pm:931
+msgid "Lookup type mismatch"
+msgstr "Lookup type komt niet overeen"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Maak Eigenaar"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Maak Status"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Maak verwachtingsdatum"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Make oplossingsdatum"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Maak startdatum"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Maak datum gestart"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Maak datum gemeld"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Maak prioriteit"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Maak queue"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Maak onderwerp"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Beheer van custom fields"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Beheer van groepen"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Beheer van eigenschappen en configuraties die betrekking hebben op alle queues"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Beheer van queues"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Beheer van gebruikers"
+
+#: lib/RT/Date.pm:439
+msgid "Mar."
+msgstr "Maa."
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Maart"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Mei"
+
+#: lib/RT/Date.pm:441
+msgid "May."
+msgstr "Mei."
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Member %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Member %1 deleted"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Lid toegevoegd"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Lid verwijderd"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Lid niet verwijderd"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Lid van"
+
+#: NOT FOUND IN SOURCE
+msgid "HasMember"
+msgstr "Bevat lid"
+
+msgid "MemberOf"
+msgstr "Lid van"
+
+msgid "DependedOnBy"
+msgstr "Afhankelijkheid voor"
+
+msgid "DependsOn"
+msgstr "Afhankelijk van"
+
+msgid "RefersTo"
+msgstr "Verwijst naar"
+
+msgid "ReferredToBy"
+msgstr "Verwezen vanaf"
+
+msgid "LinkedTo"
+msgstr "Gekoppeld aan"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Leden"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Membership in %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr ""
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2838
+msgid "Merge Successful"
+msgstr "Samenvoeging Succesvol"
+
+#: lib/RT/Ticket_Overlay.pm:2725
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Samenvoeging mislukt. Kan EffectiefId niet instellen"
+
+#: lib/RT/Ticket_Overlay.pm:2733
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Voeg samen in"
+
+#: lib/RT/Transaction_Overlay.pm:721
+#. ($value)
+msgid "Merged into %1"
+msgstr "Samengevoegd tot %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Bericht"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:163
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "Bericht inhoud niet weergegeven omdat het te groot is of niet in platte tekst"
+
+#: lib/RT/Ticket_Overlay.pm:2416
+msgid "Message could not be recorded"
+msgstr "bericht kon niet opgeslagen worden"
+
+#: lib/RT/Ticket_Overlay.pm:2419
+msgid "Message recorded"
+msgstr "Bericht opgeslagen"
+
+#: html/Ticket/Elements/PreviewScrips:117
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Berichten over dit ticket worden niet verzonden aan..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Minuten"
+
+#: html/Search/Build.html:430
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:955
+msgid "Missing a primary key?: %1"
+msgstr "Mist primaire sleutel?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Mobiel"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "MobieleTelefoon"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify Access Control List"
+msgstr "Wijzig Toegangs Controle Lijst"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Wijzig Specifiek Veld %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Wijzig custom fields die betrekking hebben op %1 voor alle %2"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Wijzig custom fields die betrekking hebben op alle %1"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Wijzig groep rechten"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Wijzig leden"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Wijzig rechten"
+
+#: lib/RT/Queue_Overlay.pm:98
+msgid "Modify Scrip templates for this queue"
+msgstr "Wijzit Scrip sjabloon voor deze queue"
+
+#: lib/RT/Queue_Overlay.pm:101
+msgid "Modify Scrips for this queue"
+msgstr "Wijzig Scrips voor deze queue"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Wijzig Sjabloon %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr ""
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Wijzig een Specifiek Veld voor deze queue %1"
+
+#: html/Admin/Queues/Scrip.html:75
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Wijzig een scrip voor deze queue %1"
+
+#: html/Admin/Global/Scrip.html:69
+msgid "Modify a scrip which applies to all queues"
+msgstr "Wijzig een scrip die betrekking heeft op alle queues"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Wijzig objecten geassocieerd aan %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "Wijzig data voor # %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Wijzig data voor #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Wijzig data voor ticket # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Wijzig globale groepsrechten"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Wijzig globale groepsrechten"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Wijzig globale gebruikersrechten"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Wijzig globale gebruikersrechten"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Wijzig groepsmetadata of verwijder groep"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Wijzig groepsrechten voor custom field %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Wijzig groepsrechten voor groep %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Wijzig groepsrechten voor queue %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Wijzig lidmaatschap rooster voor dze groep"
+
+#: lib/RT/System.pm:83
+msgid "Modify one's own RT account"
+msgstr "Wijzig uw eigen RT "
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Wijzig mensen gekoppeld aan queue %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Wijzig mensen gekoppeld aan ticket #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Wijzig scrips voor queue %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Wijzig scrips die betrekking hebben op alle queues"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Wijzig sjabloon %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Wijzig sjablonen die van toepassing zijn op alle queues"
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Wijzig de groep %1"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Modify the queue watchers"
+msgstr "Wijzig de toeschouwers van de queue"
+
+#: html/Admin/Users/Modify.html:298
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Wijzig de gebruiker %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Wijzig ticket # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Wijzig ticket #%1"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Modify tickets"
+msgstr "Wijzig tickets"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Wijzig gebruikersrechten voor custom field %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Wijzig gebruikersrechten voor groep %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Wijzig gebruikersrechten voor queue %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Wijzig toeschouwers voor queue '%1'"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyACL"
+msgstr "WijzigACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "WijzigEigenLidmaatschap"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "ModifyQueueWatchers"
+msgstr "Wijzig queue toeschouwers"
+
+#: lib/RT/Queue_Overlay.pm:101
+msgid "ModifyScrips"
+msgstr "Wijzig scrips"
+
+#: lib/RT/System.pm:83
+msgid "ModifySelf"
+msgstr "WijzigZelf"
+
+#: lib/RT/Queue_Overlay.pm:98
+msgid "ModifyTemplate"
+msgstr "WijzigSjabloon"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "ModifyTicket"
+msgstr "WijzigTicket"
+
+#: lib/RT/Date.pm:413
+msgid "Mon."
+msgstr "Ma."
+
+#: html/Ticket/Elements/ShowRequestor:62
+#. ($name)
+msgid "More about %1"
+msgstr "Meer over %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr ""
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Meerdere"
+
+#: lib/RT/User_Overlay.pm:227
+msgid "Must specify 'Name' attribute"
+msgstr "Specificeren van 'Naam' attribuut verplicht"
+
+#: html/SelfService/Elements/MyRequests:69
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "Mijn %1 tickets"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Mijn Goedkeuringen"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Mijn goedkeuringen"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:53
+msgid "My saved searches"
+msgstr "Mijn bewaarde zoekopdrachten"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Naam"
+
+#: lib/RT/User_Overlay.pm:234
+msgid "Name in use"
+msgstr "Naam in gebruik"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Goedkeuring benodigd van de systeem beheerder"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Nooit"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Nieuw"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nieuwe Relaties"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nieuw wachtwoord"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nieuwe wachtende goedkeuring"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Nieuwe zoekopdracht"
+
+#: html/Ticket/Elements/Tabs:205
+msgid "New Search"
+msgstr "Nieuwe zoekopdracht"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nieuw custom field"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Nieuwe group"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nieuw wachtwoord"
+
+#: lib/RT/User_Overlay.pm:817
+msgid "New password notification sent"
+msgstr "Bericht voor nieuw wachtwoord verzonden"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Nieuwe queue"
+
+#: html/Ticket/Elements/Reminders:73
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Nieuw verzoek"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nieuwe rechten"
+
+#: html/Admin/Global/Scrip.html:61 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:64 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Nieuw scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Nieuwe zoekopdracht"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Nieuw sjabloon"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Nieuw ticket"
+
+#: lib/RT/Ticket_Overlay.pm:2702
+msgid "New ticket doesn't exist"
+msgstr "Nieuw ticket bestaat niet"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "New user"
+msgstr "Nieuwe gebruiker"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Nieuwe gebruiker genaamd"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nieuwe toeschouwers"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Nieuwe venster instelling"
+
+#: html/Helpers/CalPopup.html:13 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Volgende"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Volgende pagina"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Volgende pagina"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Bijnaam"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Bijnaam"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Geen Klasse gedefinieerd"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Geen custom field"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Geen custom field gedefinieerd"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Geen Groep gedefinieerd"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Geen Zoekopdracht"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Geen queue gedefinieerd"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Geen RT-gebruiker gevonden. Raadpleeg uw RT-beheerder.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Geen Sjabloon"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Geen ticket gespecificeerd. Ticket afgebroken "
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "Geen ticket gespecificeerd. Ticket wijzigingen afgebroken\\n\\n"
+
+#: html/Approvals/Elements/Approve:73
+msgid "No action"
+msgstr "Geen actie"
+
+#: lib/RT/Record.pm:950
+msgid "No column specified"
+msgstr "Geen kolom gespecificeerd"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Geen commando gevonden\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Geen commentaar ingevuld over deze gebruiker"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Geen correspondentie aangehecht"
+
+#: lib/RT/Action/Generic.pm:186 lib/RT/Condition/Generic.pm:198 lib/RT/Search/ActiveTicketsInQueue.pm:78 lib/RT/Search/Generic.pm:135 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Geen omschrijving voor %1"
+
+#: lib/RT/Users_Overlay.pm:191
+msgid "No group specified"
+msgstr "Geen groep gespecificeerd"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Er zijn geen groepen gevonden die voldoen aan het zoek criterium."
+
+#: lib/RT/Ticket_Overlay.pm:2359
+msgid "No message attached"
+msgstr "Geen bericht toegevoegd"
+
+#: lib/RT/User_Overlay.pm:1035
+msgid "No password set"
+msgstr "Geen wachtwoord ingesteld"
+
+#: lib/RT/Queue_Overlay.pm:362
+msgid "No permission to create queues"
+msgstr "Geen rechten om queues te creëren"
+
+#: lib/RT/Ticket_Overlay.pm:410
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Geen rechten om tickets te creëren in de queue '%1'"
+
+#: lib/RT/User_Overlay.pm:187
+msgid "No permission to create users"
+msgstr "Geen rechten om gebruikers te creëren"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Geen rechten om dat ticket te tonen"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Geen rechten om verversing ticket te bekijken"
+
+#: lib/RT/Queue_Overlay.pm:796 lib/RT/Ticket_Overlay.pm:1460
+msgid "No principal specified"
+msgstr "Geen hoofdgebruiker gespecificeerd"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Geen hoofdgebruikers geselecteerd"
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Geen queues gevonden die aan de zoekcriteria voldoen"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Geen rechten gevonden"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Geen rechten toegekend"
+
+#: lib/RT/SavedSearch.pm:187
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Geen zoek opdracht om uit te voeren."
+
+#: html/Elements/RT__Ticket/ColumnMap:135 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Geen onderwerp"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Geen ticket id gespecificeerd"
+
+#: lib/RT/Transaction_Overlay.pm:515 lib/RT/Transaction_Overlay.pm:552
+msgid "No transaction type specified"
+msgstr "Geen transactie type gespecificeerd"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "Geen gebruiker of email-adres gespecificeerd"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Geen gebruikers gevonden die aan de zoekcriteria voldoen"
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Geen geldige RT gebruiker gevonden. RT cvs behandelaar losgemaakt. Neemt u alstublieft contact op met uw RT beheerder.\\n"
+
+#: lib/RT/Record.pm:947
+msgid "No value sent to _Set!\\n"
+msgstr "Geen waarde gestuurd naar _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Niemand"
+
+#: lib/RT/Record.pm:952
+msgid "Nonexistant field?"
+msgstr "Nietbestaand veld?"
+
+#: html/Elements/Header:91
+msgid "Not logged in."
+msgstr "Niet aangemeld."
+
+#: lib/RT/Date.pm:393
+msgid "Not set"
+msgstr "Niet gezet"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Nog niet geïmplementeerd."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Nog niet geïmplementeerd...."
+
+#: html/Approvals/Elements/Approve:77
+msgid "Notes"
+msgstr "Notities"
+
+#: lib/RT/User_Overlay.pm:820
+msgid "Notification could not be sent"
+msgstr "Bericht kon niet verstuurd worden"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Informeer AdminCcs"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Informeer AdminCcs als Commentaar"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Informeer Ccs"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Informeer Ccs als CC"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Bericht Andere Ontvangers"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Bericht Andere Ontvangers als Commentaar"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Bericht Eigenaar"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Bericht Eigenaar als Commentaar"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Informeer de eigenaar over hun geweigerde ticket"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Informeer de eigenaar als hun ticket is geautoriseerd door de approvers"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Informeer de eigenaar als hun ticket is geautoriseerd door een aprover"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Informeer Eigenaars en AdminCcs over nieuwe zaken welke wachten op hun goedkeuring"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Informeer aanvragers"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Informeer aanvragers en Ccs"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Informeer aanvragers en Ccs als CC"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Informeer aanvragers, Ccs en AdminCcs"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Informeer aanvragers, Ccs en AdminCcs als CC"
+
+#: lib/RT/Date.pm:447
+msgid "Nov."
+msgstr "Nov."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr ""
+
+#: lib/RT/Record.pm:323
+msgid "Object could not be created"
+msgstr "Object kon niet gecreëerd worden"
+
+#: lib/RT/Record.pm:124
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:342
+msgid "Object created"
+msgstr "Object gecreëerd"
+
+#: lib/RT/Record.pm:121
+msgid "Object deleted"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:966
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:446
+msgid "Oct."
+msgstr "Okt."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr ""
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr ""
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Op"
+
+#: lib/RT/Transaction_Overlay.pm:313
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Bij Commentaar"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Bij Overeenkomst"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Bij Creatie"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Bij Eigenaarwijziging"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr ""
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Bij queue wijziging"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Bij Oplossing"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Bij Statuswijziging"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Bij Transactie"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Toon alleen goedkeuringen voor verzoeken gecreëerd na %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Toon alleen goedkeuringen voor verzoeken gecreëerd voor %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Toon alleen de custom fields voor:"
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Open"
+
+#: html/Ticket/Elements/Tabs:161
+msgid "Open it"
+msgstr "Open"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Open verzoeken"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Open tickets (van lijst) in een nieuw venster"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Open tickets (van lijst) in een ander venster"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Open ticket bij correspondentie"
+
+#: html/Prefs/MyRT.html:62
+msgid "Options"
+msgstr "Opties"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Sorteer op"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Ordening en sortering"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organisatie"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Voortgekomen uit ticket: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:609
+msgid "Outgoing email about a comment recorded"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:613
+msgid "Outgoing email recorded"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Naar mate de tijd vordert, verschuift de prioriteit richting"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Own tickets"
+msgstr "Eigen tickets"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "OwnTicket"
+msgstr "EigenTicket"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/SelfService/Elements/MyRequests:51 html/Ticket/Create.html:71 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:84 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:111 lib/RT/Tickets_Overlay.pm:1993
+msgid "Owner"
+msgstr "Eigenaar"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Eigenaar veranderd van %1 naar %2"
+
+#: lib/RT/Ticket_Overlay.pm:496
+msgid "Owner could not be set."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:659
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Eigenaar gedwongen veranderd van %1 naar %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "Eigenaar is"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Pagina %1 van %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Pieper"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Pieper"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Ouders"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Wachtwoord"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Wachtwoord Herinnering"
+
+#: lib/RT/Transaction_Overlay.pm:768 lib/RT/User_Overlay.pm:1046
+msgid "Password changed"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1038 lib/RT/User_Overlay.pm:215
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1045
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Wachtwoord te kort"
+
+#: html/User/Prefs.html:239
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Wachtwoord: %1"
+
+#: lib/RT/User_Overlay.pm:1031
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:353
+msgid "Passwords do not match."
+msgstr ""
+
+#: html/User/Prefs.html:241
+msgid "Passwords do not match. Your password has not been changed"
+msgstr ""
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Mensen"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Verricht een gebruiker gedefiniëerde actie"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl-configuratie"
+
+#: lib/RT/ACE_Overlay.pm:252 lib/RT/ACE_Overlay.pm:258 lib/RT/ACE_Overlay.pm:581 lib/RT/ACE_Overlay.pm:591 lib/RT/ACE_Overlay.pm:601 lib/RT/ACE_Overlay.pm:666 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:117 lib/RT/CurrentUser.pm:126 lib/RT/CustomField_Overlay.pm:1015 lib/RT/CustomField_Overlay.pm:1136 lib/RT/CustomField_Overlay.pm:1279 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:402 lib/RT/CustomField_Overlay.pm:762 lib/RT/CustomField_Overlay.pm:935 lib/RT/CustomField_Overlay.pm:970 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1055 lib/RT/Queue_Overlay.pm:141 lib/RT/Queue_Overlay.pm:159 lib/RT/Queue_Overlay.pm:658 lib/RT/Queue_Overlay.pm:668 lib/RT/Queue_Overlay.pm:682 lib/RT/Queue_Overlay.pm:820 lib/RT/Queue_Overlay.pm:829 lib/RT/Queue_Overlay.pm:842 lib/RT/Scrip_Overlay.pm:150 lib/RT/Scrip_Overlay.pm:161 lib/RT/Scrip_Overlay.pm:226 lib/RT/Scrip_Overlay.pm:540 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1328 lib/RT/Ticket_Overlay.pm:1338 lib/RT/Ticket_Overlay.pm:1352 lib/RT/Ticket_Overlay.pm:1493 lib/RT/Ticket_Overlay.pm:1503 lib/RT/Ticket_Overlay.pm:1517 lib/RT/Ticket_Overlay.pm:1634 lib/RT/Ticket_Overlay.pm:1954 lib/RT/Ticket_Overlay.pm:2092 lib/RT/Ticket_Overlay.pm:2262 lib/RT/Ticket_Overlay.pm:2312 lib/RT/Ticket_Overlay.pm:2484 lib/RT/Ticket_Overlay.pm:2555 lib/RT/Ticket_Overlay.pm:2603 lib/RT/Ticket_Overlay.pm:2693 lib/RT/Ticket_Overlay.pm:2707 lib/RT/Ticket_Overlay.pm:2931 lib/RT/Ticket_Overlay.pm:2941 lib/RT/Ticket_Overlay.pm:2946 lib/RT/Ticket_Overlay.pm:3169 lib/RT/Ticket_Overlay.pm:3173 lib/RT/Ticket_Overlay.pm:3316 lib/RT/Ticket_Overlay.pm:3442 lib/RT/Transaction_Overlay.pm:503 lib/RT/Transaction_Overlay.pm:510 lib/RT/Transaction_Overlay.pm:538 lib/RT/Transaction_Overlay.pm:545 lib/RT/User_Overlay.pm:1184 lib/RT/User_Overlay.pm:1793 lib/RT/User_Overlay.pm:370 lib/RT/User_Overlay.pm:736 lib/RT/User_Overlay.pm:775
+msgid "Permission Denied"
+msgstr "Toestemming Geweigerd"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Persoonlijke Groepen"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Persoonlijke groepen"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Persoonlijke groepen:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefoonnummers"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Plaatshouder"
+
+#: html/Elements/Header:88 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Instelingen"
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Voorkeuren"
+
+#: lib/RT/Action/Generic.pm:196
+msgid "Prepare Stubbed"
+msgstr "Bereid Plaatshouder Voor"
+
+#: html/Helpers/CalPopup.html:11 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Vorige"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Vorige pagina"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#: lib/RT/ACE_Overlay.pm:158 lib/RT/ACE_Overlay.pm:240 lib/RT/ACE_Overlay.pm:570
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Hoofd %1 niet gevonden."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:180 html/Ticket/Elements/EditBasics:87 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1777
+msgid "Priority"
+msgstr "Prioriteit"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Prioriteit begint bij"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Privacy:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Gerechtigd"
+
+#: html/Admin/Users/Modify.html:331 html/User/Prefs.html:230
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Gerechtigde status: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Gerechtigde gebruikers"
+
+#: NOT FOUND IN SOURCE
+msgid "Projects"
+msgstr "Projecten"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudogroep voor intern gebruik"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Zoek criteria"
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:61 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:40 html/Tools/Reports/ResolvedByDates.html:41 html/Tools/Reports/ResolvedByOwner.html:21 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1604
+msgid "Queue"
+msgstr "Queue"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Queue %1 niet gevonden"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Queue '%1' niet gevonden\\n"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Queue Naam"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Queue Scrips"
+
+#: lib/RT/Queue_Overlay.pm:366
+msgid "Queue already exists"
+msgstr "Queue bestaat al"
+
+#: lib/RT/Queue_Overlay.pm:375 lib/RT/Queue_Overlay.pm:381
+msgid "Queue could not be created"
+msgstr "Queue kon niet aangemaakt worden"
+
+#: html/Ticket/Create.html:243 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Queue kon niet geladen worden."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:385 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Queue aangemaakt"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Queue is niet gespecificeerd"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Queue niet gevonden"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Queues"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Queues die ik beheer"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Queues waarvan in een AdminCC ben"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Snel zoeken"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Snel een ticket aanmaken"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 voor %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 van <a href=\"http://bestpractical.com/\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT Beheer"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "RT Authenticatie fout"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT Doorgestuurd: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "RT Configuratie fout"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "RT Kritieke fout: Bericht niet bewaard!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT Fout"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT Ontving mail (%1) van zichzelf."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT Zelfbediening / Afgesloten Tickets"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RT Variabelen"
+
+#: html/Prefs/MyRT.html:58 html/Prefs/MyRT.html:70 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT in een oogopslag"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT kan content van een andere webpagina toevoegen wanneer dit custom field wordt weergegeven."
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT kan deze custom field waarden omzetten in hyperlinks naar andere services."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT kon u niet authenticeren"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT kan de aanvrager niet vinden in zijn interne database"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT kon de queue %1 niet vinden"
+
+#: html/Elements/SetupSessionCookie:90
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT kon deze PGP signatuur niet valideren. \\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT voor %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT voor %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT heeft uw commando's verwerkt"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Het is gedistribueerd onder <a href=\"http://www.gnu.org/copyleft/gpl.html\">Versie 2 van de GNU General Public License.</a>""
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT denkt dat dit bericht onbestelbaar zou kunnen zijn"
+
+#: html/Search/Simple.html:60
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "Standaard zoekt RT in de ticket onderwerpen"
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT zal dit bericht verwerken als of het ongesigneerd is.\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT zal <tt>__id__</tt> en <tt>__CustomField__</tt> vervangen met het record id respectievelijk de custom field waarde"
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RT's email commando modus vereist PGP authenticatie. Of u heeft uw bericht niet gesigneerd, of uw signatuur kon niet geverifieerd worden."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Echte Naam"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "EchteNaam"
+
+#: lib/RT/Transaction_Overlay.pm:712
+#. ($value)
+msgid "Reference by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:752
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:709
+#. ($value)
+msgid "Reference to %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:749
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Naar gerefeerd door"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Refereert aan"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Verfijn"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Verfijn Zoekopdracht"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Ververs deze pagina elke %1 minuten."
+
+#: lib/RT/Transaction_Overlay.pm:798
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:804
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:49 html/Ticket/Elements/ShowSummary:73 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "Herinneringen"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Verwijder AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Verwijder Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Verwijder aanvrager"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Antwoord"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Antwoord naar adres"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Antwoord naar aanvragers"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Reply to tickets"
+msgstr "Antwoord op tickets"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "ReplyToTicket"
+msgstr "Antwoord op ticket"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:1 html/Tools/Reports/index.html:2
+msgid "Reports"
+msgstr "Rapportage"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:112
+msgid "Requestor"
+msgstr "Aanvrager"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Aanvrager email adres"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Aanvrager(s)"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:79 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Aanvragers"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Verzoek is terug verwacht"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Verplichte parameter '%1' ontbreekt"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Herstel"
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Woonplaats"
+
+#: html/Ticket/Elements/Tabs:157
+msgid "Resolve"
+msgstr "Los op"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Los ticket #%1 (%2) op"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1149
+msgid "Resolved"
+msgstr "Opgelost"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Opgeloste incidenten per eigenaar"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Opgelost in het tijdvenster"
+
+#: html/Tools/Reports/ResolvedByDates.html:7
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:5
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Antwoord aan aanvragers"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Resultaten"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Resultaten per pagina"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Herhaal het wachtwoord"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Herstel"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Recht %1 niet gevonden voor %2 %3 in bereik %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:631
+msgid "Right Delegated"
+msgstr "Recht Gedelegeerd"
+
+#: lib/RT/ACE_Overlay.pm:321
+msgid "Right Granted"
+msgstr "Recht Toegekend"
+
+#: lib/RT/ACE_Overlay.pm:179
+msgid "Right Loaded"
+msgstr "Recht geladen"
+
+#: lib/RT/ACE_Overlay.pm:696 lib/RT/ACE_Overlay.pm:717
+msgid "Right could not be revoked"
+msgstr "Recht kon niet afgenomen worden"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Recht niet gevonden"
+
+#: lib/RT/ACE_Overlay.pm:561 lib/RT/ACE_Overlay.pm:656
+msgid "Right not loaded."
+msgstr "Recht niet geladen"
+
+#: lib/RT/ACE_Overlay.pm:713
+msgid "Right revoked"
+msgstr "Recht ingetrokken"
+
+#: html/Admin/Elements/UserTabs:67
+msgid "Rights"
+msgstr "Rechten"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:901
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:930
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr ""
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Rollen"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "RootGoedkeuring"
+
+#: html/Prefs/MyRT.html:64
+msgid "Rows per box"
+msgstr "Rijen per box"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Rijen per pagina"
+
+#: lib/RT/Date.pm:418
+msgid "Sat."
+msgstr "Za."
+
+#: html/Prefs/MyRT.html:64 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:68 html/Widgets/SelectionBox:212
+msgid "Save"
+msgstr "Bewaren"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:235 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Bewaar wijzigingen"
+
+#: html/User/Prefs.html:180
+msgid "Save Preferences"
+msgstr "Bewaar instellingen"
+
+#: html/Ticket/Elements/PreviewScrips:126
+msgid "Save changes"
+msgstr "Bewaar wijzigingen"
+
+#: lib/RT/SavedSearch.pm:162
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:46
+msgid "Saved searches"
+msgstr "Opgeslagen zoekopdrachten"
+
+#: html/Admin/Elements/ListGlobalScrips:61 html/Admin/Global/Scrip.html:70 html/Admin/Queues/Scrip.html:76
+#. ($scrip->Id)
+#. ($id)
+#. ($ARGS{'id'})
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:205
+msgid "Scrip Created"
+msgstr "Scrip aangemaakt"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Script verwijderd"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Scrips voor %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrips die betrekking hebben op alle queues"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:65
+msgid "Search"
+msgstr "Zoek"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Zoek Criteria"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:116
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Zoeken naar autorisaties"
+
+#: html/Search/Simple.html:69
+msgid "Search for tickets"
+msgstr "Zoeken naar tickets"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "Zoeken naar tickets. Voer het <strong>id nummer</strong>, de <strong>queue naam</strong>, de <strong>naam</strong> van de eigenaar of het <strong>e-mail adres</strong> van de aanvrager in. RT zal zoeken in de ticket inhoud en attachments."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Zoek opties"
+
+#: html/Search/Chart.html:7
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:194
+#. ($msg)
+msgid "Search update: %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Searches can't be associated with that kind of object"
+msgstr "Zoekopdrachten kunnen niet met dat type object geassocieerd worden"
+
+#: html/Search/Simple.html:59
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "Het doorzoeken van de gehele tekst van alle tickets kan lang duren, maar als dit nodig is, dan kun je ieder woord in de volledige ticket history opzoeken door het typen van <b>fulltext:<i>woord</i></b>."
+
+#: bin/rt-crontool:213
+msgid "Security:"
+msgstr "Veiligheid"
+
+#: html/Elements/ShowCustomFields:76
+#. ('<a href="'.$Value->IncludeContentForValue.'">' . $Value->IncludeContentForValue."</a>")
+msgid "See also: %1"
+msgstr "Bekijk ook: %1"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Bekijk custom fields"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Bekijk de uitgaande mail en haar ontvangers"
+
+#: lib/RT/Queue_Overlay.pm:105
+msgid "See ticket private commentary"
+msgstr "Bekijk ticket "
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket summaries"
+msgstr "Bekijk ticket samenvatting"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "Bekijk custom field"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "Bekijk groep"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "SeeQueue"
+msgstr "Bekijk queue"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Selecteer een custom field"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Selecteer een groep"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Selecteer een queue"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Selecteer een queue voor je nieuwe ticket"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Selecteer een gebruiker"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Selecteer custom field"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Selecteer custom fields voor alle gebruikers groepen"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Selecteer custom fields voor alle gebruikers"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Selecteer custom fields voor tickets in alle queues"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Selecteer custom fields for transacties op tickets in alle queues"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Selecteer de groep"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Selecteer meerdere waarden"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Selecteer één waarde"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Selecteer de queue"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Selecteer de queues die weergegeven worden op de \"RT in een oogopslag\" pagina"
+
+#: html/Admin/Global/Scrip.html:58 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Selecteer het scrip"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Selecteer de template"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Selecteer tot %1 waarden"
+
+#: html/Admin/Elements/UserTabs:75
+msgid "Select user"
+msgstr "Selecteer de gebruiker"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "SelecteerMeerdere"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "SelecteerEnkele"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr ""
+
+#: html/Widgets/SelectionBox:210
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Zelfbediening"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Stuurt mail naar alle toeschouwers"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Stuurt mail naar alle toeschouwers als een \"commentaar\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Stuurt mail naar alle aanvragers en Ccs"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Stuurt mail naar alle aanvragers en Ccs als een \"commentaar\""
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Stuurt een bericht aan de aanvragers"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Stuurt mail aan expliciet genoemde Ccs en Bccs"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr ""
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Stuurt mail aan de administratieve Ccs"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Stuurt mail aan de administratieve Ccs als een \"commentaar\""
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Stuurt mail aan de eigenaar"
+
+#: lib/RT/Date.pm:445
+msgid "Sep."
+msgstr "Sep."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Toon"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Toon autorisaties"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Toon kolommen"
+
+#: html/Ticket/Elements/Tabs:213
+msgid "Show Results"
+msgstr "Toon Resultaten"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Toon goedgekeurde verzoeken"
+
+#: html/Ticket/Create.html:315
+msgid "Show basics"
+msgstr "Toon beginselen"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Toon afgewezen verzoeken"
+
+#: html/Ticket/Create.html:318
+msgid "Show details"
+msgstr "Toon details"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Toon verzoeken die in behandeling zijn"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Toon verzoeken die wachten op andere goedkeuringen"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Toon ticket privé commentaar"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Toon ticket samenvattingen"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ShowACL"
+msgstr "ToonACL"
+
+#: lib/RT/System.pm:86
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "ShowOutgoingEmail"
+msgstr "ToonUitgaandeEmail"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "ToonOpgeslagenZoekopdrachten"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowScrips"
+msgstr "Toon scrips"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ShowTemplate"
+msgstr "Toon sjabloon"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicket"
+msgstr "Toon ticket"
+
+#: lib/RT/Queue_Overlay.pm:105
+msgid "ShowTicketComments"
+msgstr "Toon ticket commentaar"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Schrijf in als een ticket aanvrager of ticket of queue Cc"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Schrijf in als een ticket of queue AdminCc"
+
+#: html/Admin/Users/Modify.html:226 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Ondertekening"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Zoeken"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Enkel"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Omvang"
+
+#: html/Elements/Header:84
+msgid "Skip Menu"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "Sommige browser laden alleen content als het afkomstig is uit hetzelfde domain als de RT server."
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sorteren"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Sorteer sleutel"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Sorteer resultaten op"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "SorteerVolgorde"
+
+#: html/Admin/Elements/EditScrip:87
+msgid "Stage"
+msgstr "Stadium"
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Is blijven steken"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Start pagina"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Gestart"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Startum '%1' kon niet ontleed worden"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:207 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Begint"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Begint op"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Begindatum '%1' kon niet ontleed worden"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Provincie"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Elements/MyRequests:50 html/SelfService/Update.html:57 html/Ticket/Create.html:65 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1143 lib/RT/Tickets_Overlay.pm:1638
+msgid "Status"
+msgstr "Status"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Status veranderd van %1 naar %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "StatusVerandering"
+
+#: html/Ticket/Elements/Tabs:172
+msgid "Steal"
+msgstr "Steel"
+
+#: lib/RT/Queue_Overlay.pm:118
+msgid "Steal tickets"
+msgstr "Steel ticket"
+
+#: lib/RT/Queue_Overlay.pm:118
+msgid "StealTicket"
+msgstr "SteelTicket"
+
+#: lib/RT/Transaction_Overlay.pm:665
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Gestolen van %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Gestolen van %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Stijl"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Elements/MyRequests:49 html/SelfService/Update.html:65 html/Ticket/Create.html:107 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:80 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1139 lib/RT/Tickets_Overlay.pm:1720
+msgid "Subject"
+msgstr "Onderwerp"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:687
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Onderwerp veranderd naar %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Uitvoeren"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Registreer Workflow"
+
+msgid "ExtendedStatus"
+msgstr "Uitgebreide status"
+
+msgid "QueueName"
+msgstr "Naam van de queue"
+
+msgid "OwnerName"
+msgstr "Naam van de eigenaar"
+
+msgid "CreatedRelative"
+msgstr ""
+
+msgid "ToldRelative"
+msgstr ""
+
+msgid "LastUpdatedRelative"
+msgstr ""
+
+msgid "DueRelative"
+msgstr ""
+
+msgid "ResolvedRelative"
+msgstr ""
+
+msgid "LastUpdated"
+msgstr ""
+
+msgid "StartedRelative"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Gelukt"
+
+#: lib/RT/Date.pm:419
+msgid "Sun."
+msgstr "Zo."
+
+#: lib/RT/System.pm:76
+msgid "SuperUser"
+msgstr "Super gebruiker"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Systeem"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Systeem configuratie"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:585 lib/RT/Interface/Web.pm:900 lib/RT/Interface/Web.pm:929
+msgid "System Error"
+msgstr "Systeem Fout"
+
+#: lib/RT/Transaction_Overlay.pm:215 lib/RT/Transaction_Overlay.pm:221
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Systeem tools"
+
+#: lib/RT/ACE_Overlay.pm:634
+msgid "System error. Right not delegated."
+msgstr "Systeem fout. Recht niet gedelegeerd."
+
+#: lib/RT/ACE_Overlay.pm:164 lib/RT/ACE_Overlay.pm:229 lib/RT/ACE_Overlay.pm:324 lib/RT/ACE_Overlay.pm:921
+msgid "System error. Right not granted."
+msgstr "Systeem fout. Recht niet toegekend."
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "Systeem fout. Niet mogelijk om rechten toe te kennen"
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Systeem groepen"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SysteemRolgroep voor intern gebruik"
+
+#: lib/RT/CurrentUser.pm:358
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:168
+msgid "Take"
+msgstr "Neem"
+
+#: lib/RT/Queue_Overlay.pm:116
+msgid "Take tickets"
+msgstr "Neem ticket"
+
+#: lib/RT/Queue_Overlay.pm:116
+msgid "TakeTicket"
+msgstr "NeemTicket"
+
+#: lib/RT/Transaction_Overlay.pm:650
+msgid "Taken"
+msgstr "Genomen"
+
+#: NOT FOUND IN SOURCE
+msgid "Task"
+msgstr "Taak"
+
+#: html/Admin/Elements/EditScrip:79 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Sjabloon"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr ""
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Sjabloon verwijderd"
+
+#: lib/RT/Scrip_Overlay.pm:181
+msgid "Template not found"
+msgstr "Sjabloon niet gevonden"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Sjabloon niet gevonden\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Sjabloon ontleed"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Sjablonen"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Sjablonen voor %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "Tekst"
+
+#: lib/RT/CustomField_Overlay.pm:942 lib/RT/Record.pm:946
+msgid "That is already the current value"
+msgstr "Dat is al de huidige waarde"
+
+#: lib/RT/CustomField_Overlay.pm:411
+msgid "That is not a value for this custom field"
+msgstr "Dat is geen waarde voor dit custom field"
+
+#: lib/RT/Ticket_Overlay.pm:1965
+msgid "That is the same value"
+msgstr "Dat is de zelfde waarde"
+
+#: lib/RT/ACE_Overlay.pm:306 lib/RT/ACE_Overlay.pm:615
+msgid "That principal already has that right"
+msgstr "Deze hoofdgebruiker heeft reeds dat recht"
+
+#: lib/RT/Queue_Overlay.pm:754
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Deze hoofdgebruiker is reeds een %1 voor deze queue"
+
+#: lib/RT/Ticket_Overlay.pm:1406
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Deze hoofdgebruiker is reeds een %1 voor dit ticket"
+
+#: lib/RT/Queue_Overlay.pm:853
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Deze hoofdgebruiker is geen %1 voor deze queue"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Dexe hoofdgebruiker is geen %1 voor dit ticket"
+
+#: lib/RT/Ticket_Overlay.pm:1961
+msgid "That queue does not exist"
+msgstr "Die queue bestaat niet"
+
+#: lib/RT/Ticket_Overlay.pm:3178
+msgid "That ticket has unresolved dependencies"
+msgstr "Dat ticket heeft onopgeloste afhankelijkheden"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Die gebruiker heeft dat recht reeds"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:2982
+msgid "That user already owns that ticket"
+msgstr "Die gebruiker is al eigenaar van dat ticket"
+
+#: lib/RT/Ticket_Overlay.pm:2954
+msgid "That user does not exist"
+msgstr "Die gebruiker bestaat niet"
+
+#: lib/RT/User_Overlay.pm:390
+msgid "That user is already privileged"
+msgstr "Die gebruiker is al gerechtigd"
+
+#: lib/RT/User_Overlay.pm:411
+msgid "That user is already unprivileged"
+msgstr "Die gebruiker is reeds ontrechtigd"
+
+#: lib/RT/User_Overlay.pm:403
+msgid "That user is now privileged"
+msgstr "Die gebruiker is nu gerechtigd"
+
+#: lib/RT/User_Overlay.pm:424
+msgid "That user is now unprivileged"
+msgstr "Die gebruiker is nu ontrechtigd"
+
+#: lib/RT/Ticket_Overlay.pm:2975
+msgid "That user may not own tickets in that queue"
+msgstr "Die gebruiker mag geen eigenaar zijn van tickets in die queue"
+
+#: lib/RT/Link_Overlay.pm:234
+msgid "That's not a numerical id"
+msgstr "Dat is niet een numeriek ID"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:176 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "De Beginselen"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The CC of a ticket"
+msgstr "De CC van een ticket"
+
+#: lib/RT/ACE_Overlay.pm:114
+msgid "The administrative CC of a ticket"
+msgstr "De administratieve CC van een ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Het commentaar is bewaard"
+
+#: bin/rt-crontool:223
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Het volgende commando zal alle actieve tickets in de queue 'general' vinden en hun prioriteit op 99 zetten als ze meer dan 4 uur niet aangeraakt zijn:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "De volgende commando's zijn niet verwerkt:\\n\\n"
+
+#: lib/RT/Record.pm:949
+msgid "The new value has been set."
+msgstr "De waarde is gezet."
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The owner of a ticket"
+msgstr "De eigenaar van een ticket"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The requestor of a ticket"
+msgstr "De aanvrager van een ticket"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Dit commentaar is gewoonlijk niet zichtbaar voor de gebruiker"
+
+#: lib/RT/CustomField_Overlay.pm:977
+msgid "This custom field does not apply to that object"
+msgstr "Dit custom field is niet van toepassing op dat object"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Dit onderdeel is alleen beschikbaar voor systeembeheerders"
+
+#: html/Ticket/Elements/PreviewScrips:93
+msgid "This message will be sent to..."
+msgstr "Dit bericht zal verstuurd worden aan..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Dit ticket %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:214
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Dit gereedschap stelt de gebruiker in staat arbitraire perl modules te gebruiken vanuit RT"
+
+#: lib/RT/Transaction_Overlay.pm:288
+msgid "This transaction appears to have no content"
+msgstr "Het lijkt erop alsof deze transactie geen inhoud heeft"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "De %1 tickets met hoogste prioriteit van deze gebruiker"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "De 25 hoogste prioriteit tickets van deze gebruiker"
+
+#: lib/RT/Date.pm:416
+msgid "Thu."
+msgstr "Do."
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Ticket #%1 Jumbo actualisering: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Ticket #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "Ticket %1"
+
+#: lib/RT/Ticket_Overlay.pm:732 lib/RT/Ticket_Overlay.pm:752
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Ticket %1 aangemaakt in queue '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Toclet %1 geladen\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Ticket %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Ticket custom fields"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Ticket Historie # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Ticket Id"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Ticket Opgelost"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1205
+msgid "Ticket Transactions"
+msgstr "Ticket Transacties"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Ticket attachment"
+
+#: lib/RT/Tickets_Overlay.pm:1907
+msgid "Ticket content"
+msgstr "Ticket inhoud"
+
+#: lib/RT/Tickets_Overlay.pm:1956
+msgid "Ticket content type"
+msgstr "Ticket inhoud type"
+
+#: lib/RT/Ticket_Overlay.pm:594 lib/RT/Ticket_Overlay.pm:608 lib/RT/Ticket_Overlay.pm:619 lib/RT/Ticket_Overlay.pm:740
+msgid "Ticket could not be created due to an internal error"
+msgstr "Ticket kong niet aangemaakt worden vanwege een interne fout"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Ticket aangemaakt"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Ticket aanmaken gefaald"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Ticket verwijderd"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Ticket id niet gevonden"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Ticket metagegevens"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Ticket niet gevonden"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Ticket status gewijzigd"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Ticket toeschouwers"
+
+#: lib/RT/Search/FromSQL.pm:83
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:52 lib/RT/CustomField_Overlay.pm:1204
+msgid "Tickets"
+msgstr "Tickets"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Tickets %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Tickets %1 door %2"
+
+#: html/Tools/Reports/CreatedByDates.html:41
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:43
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Tickets van %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:42
+msgid "Tickets resolved after"
+msgstr "Tickets afgesloten na"
+
+#: html/Tools/Reports/ResolvedByDates.html:44
+msgid "Tickets resolved before"
+msgstr "Tickets afgesloten voor"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Tickets welke afhankelijk zijn van deze goedkeuring"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:67
+msgid "Time Estimated"
+msgstr "Geschatte Tijd"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:195 html/Ticket/Elements/EditBasics:80
+msgid "Time Left"
+msgstr "Resterende tijd"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:188 html/Ticket/Elements/EditBasics:73
+msgid "Time Worked"
+msgstr "Gewerkte tijd"
+
+#: lib/RT/Tickets_Overlay.pm:1878
+msgid "Time left"
+msgstr "Resterende tijd"
+
+#: html/Elements/Footer:52
+msgid "Time to display"
+msgstr "Tijd om te tonen"
+
+#: lib/RT/Tickets_Overlay.pm:1853
+msgid "Time worked"
+msgstr "Gewerkte tijd"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "Resterende tijd"
+
+#: lib/RT/Ticket_Overlay.pm:1144
+msgid "TimeWorked"
+msgstr "Gewerkte tijd"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Titel"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Om een verschil van deze uitvoering te genereren:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "Om een verschil van deze uitvoering te genereren:\\n"
+
+#: html/Elements/Footer:63
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Voor vragen over support, training, bedrijfsspecifieke software-ontwikkeling of licentieovereenkomsten, gelieve contact op te nemen met %1."
+
+#: lib/RT/Ticket_Overlay.pm:1147
+msgid "Told"
+msgstr "Verteld"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Tools"
+
+#: html/Search/Elements/Chart:73
+msgid "Total"
+msgstr "Totaal"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transactie"
+
+#: lib/RT/Transaction_Overlay.pm:792
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transactie %1 gezuiverd"
+
+#: lib/RT/Transaction_Overlay.pm:174
+msgid "Transaction Created"
+msgstr "Transactie Gecreëerd"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Transactie custom fields"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Transactie->Creëer kon niet, aangezien u geen ticket id gespecificeerd heeft"
+
+#: lib/RT/Transaction_Overlay.pm:125
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transactie->Cre‘r kon niet aangezien er geen objectttype en -id gespecificeerd is"
+
+#: lib/RT/Transaction_Overlay.pm:857
+msgid "Transactions are immutable"
+msgstr "Transacties zijn onwijzigbaar"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Tracht een recht te verwijderen: %1"
+
+#: lib/RT/Date.pm:414
+msgid "Tue."
+msgstr "Di."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1145 lib/RT/Tickets_Overlay.pm:1692
+msgid "Type"
+msgstr "Type"
+
+#: lib/RT/ScripCondition_Overlay.pm:129
+msgid "Unimplemented"
+msgstr "Niet geïmplementeerd"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unix login"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "UnixGebruikersnaam"
+
+#: lib/RT/Attachment_Overlay.pm:290 lib/RT/Record.pm:862
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Onbekende InhoudCodering %1"
+
+#: html/Search/Build.html:395
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Ongelimiteerd"
+
+#: html/Search/Elements/SelectSearchesForObjects:63
+msgid "Unnamed search"
+msgstr "Onbenoemde zoekopdracht"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Ongerechtigd"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:646
+msgid "Untaken"
+msgstr "Vrij"
+
+#: NOT FOUND IN SOURCE
+msgid "Untitled search"
+msgstr "Naamloze zoekopdracht"
+
+#: html/Elements/RT__Ticket/ColumnMap:300 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Ververs"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Ververs ID"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr ""
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Ververs Type"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Ververs al deze tickets in eens"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Ververs email"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Ververs meer dan één ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Ververs naam"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:524
+msgid "Update not recorded."
+msgstr "Verversing niet opgeslagen."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Ververs geselecteerde tickets"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Ververs signatuur"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Ververs ticket"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Ververs ticket # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Ververs ticket #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Ververs ticket #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:523
+msgid "Update type was neither correspondence nor comment."
+msgstr "Verversingstype was noch correspondentie, noch commentaar"
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1282 lib/RT/Ticket_Overlay.pm:1148
+msgid "Updated"
+msgstr "Ververst"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr ""
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr ""
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Gebruik andere administratieve RT tools"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Gebruiker %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Gebruiker %1 Wachtwoord: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:497
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Gebruiker '%1' niet gevonden"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Gebruiker '%1' niet gevonden\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Gebruiker Gedifiniëerd"
+
+#: html/Admin/Elements/EditScrip:98
+msgid "User Defined conditions and actions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "GebruikersID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Gebruiker Id"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Gebruikersrechten"
+
+#: lib/RT/Interface/Web.pm:1284
+#. ($CustomFieldObj->Name, $class, $Object->id)
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "De gebruiker vroeg om een onbekende aanpassing van custom field %1 voor %2 object #%3"
+
+#: html/Admin/Users/Modify.html:290
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Gebruiker kon niet aangemaakt worden: %1"
+
+#: lib/RT/User_Overlay.pm:331
+msgid "User created"
+msgstr "Gebruiker aangemaakt"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Door gebruiker gedefiniëerde groepen"
+
+#: lib/RT/User_Overlay.pm:593 lib/RT/User_Overlay.pm:613
+msgid "User loaded"
+msgstr "Gebruiker opgehaald"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Gebruiker verwittigd"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Gebruikers aanzicht"
+
+#: html/Admin/Groups/index.html:99
+msgid "User-defined groups"
+msgstr "Door gebruiker gedefinieerde groepen"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Gebruikersnaam"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1206
+msgid "Users"
+msgstr "Gebruikers"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Gebruikers die voldoen aan de zoek criteria"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Geldige Zoekopdracht"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Validatie"
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "Waarde van queue"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Waarden"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Watch"
+msgstr "Schouw toe"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "WatchAsAdminCc"
+msgstr "SchouwToeAlsAdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "Toeschouwer geladen"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Toeschouwers"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "WebCodering"
+
+#: lib/RT/Date.pm:415
+msgid "Wed."
+msgstr "Wo."
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Wanneer een ticket goedgekeurd is door alle goedkeurders, voeg correspondentie toe aan het orginele ticket"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Wanneer een ticket goedgekeurd is door een goedkeurder, voeg correspondentie toe aan het orginele ticket"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Wanneer een ticket is aangemaakt"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Wanneer een goedkeuringsticket is aangemaakts, verwittig de Eigenaar en de AdminCc van het onderwerp dat op hun goedkeuring wacht"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Wanneer iets gebeurt"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Wanneer een ticket is opgelost"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Wanneer de eigenaar van een ticket verandert"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr ""
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Wanneer de queue van een ticket verandert"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Wanneer de status van een ticket verandert"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Wanneer een door de gebruiker gedifiniëerde voorwaarde gebeurt"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Wanneer commentaar binnenkomt"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Wanneer correspondentie binnenkomt"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Werk"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "WerkTelefoon"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Gewerkt"
+
+#: lib/RT/Ticket_Overlay.pm:3085
+msgid "You already own this ticket"
+msgstr "U bent al eigenaar van dit ticket"
+
+#: html/autohandler:203 html/autohandler:211
+msgid "You are not an authorized user"
+msgstr "U bent geen geauthorizeerde gebruiker"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2967
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "U kunt alleen tickets opnieuw toebedelen die van u zijn, of van niemand"
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "U heeft geen toestemming om dat ticket te bekijken"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "U vond %1 tickets in queue %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "U bent afgemeld bij RT"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "U heeft geen toestemming om tickets aan te maken in die queue."
+
+#: lib/RT/Ticket_Overlay.pm:1974
+msgid "You may not create requests in that queue."
+msgstr "U mag geen verzoeken aanmaken in die queue"
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "U mag zich weer aanmelden"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Uw %1 verzoeken"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "Uw RT beheerder heeft de mail-aliasses welke RT aanroepen verkeerd geconfigureerd"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Uw verzoek is goedgekeurd door %1. Er zijn wellicht nog andere goedkeuringen in behandeling."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Uw verzoek is goedgekeurd."
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Uw verzoek was geweigerd."
+
+#: html/autohandler:240
+msgid "Your username or password is incorrect"
+msgstr "Uw gebruikersnaam of wachtwoord zijn onjuist"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Postcode"
+
+#: lib/RT/System.pm:88
+msgid "allow creation of saved searches"
+msgstr ""
+
+#: lib/RT/System.pm:87
+msgid "allow loading of saved searches"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "zoals gegeven aan %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "grafiek"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "gesloten"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "bevat"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "inhoud"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "inhoud-type"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "correspondentie (waarschijnlijk) niet verstuurd"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "correspondentie verstuurd"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:342
+msgid "days"
+msgstr "dagen"
+
+#: NOT FOUND IN SOURCE
+msgid "dead"
+msgstr "dood"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "verwijder"
+
+#: lib/RT/Queue_Overlay.pm:88
+msgid "deleted"
+msgstr "verwijderd"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "voldoet niet aan"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "bevat niet"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "gelijk aan"
+
+#: html/Search/Build.html:481
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:503
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:462
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:546
+msgid "error: nothing to delete"
+msgstr ""
+
+#: html/Search/Build.html:467 html/Search/Build.html:486 html/Search/Build.html:508 html/Search/Build.html:537
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:564
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "bestandsnaam"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "groter dan"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "groep '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy'))
+msgid "grouped by %1"
+msgstr "opgedeeld in"
+
+#: lib/RT/Date.pm:338
+msgid "hours"
+msgstr "uren"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "id"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "is"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "is niet"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "minder dan"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "voldoet aan"
+
+#: lib/RT/Date.pm:334
+msgid "min"
+msgstr "min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minuten"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "wijzigingen\\n\\n"
+
+#: lib/RT/Date.pm:350
+msgid "months"
+msgstr "maanden"
+
+#: lib/RT/Queue_Overlay.pm:83
+msgid "new"
+msgstr "nieuw"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr ""
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "geen"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "niet gelijk aan"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:84
+msgid "open"
+msgstr "open"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "persoonlijke groep '%1' voor gebruiker '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "queue %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "rejected"
+msgstr "geweigerd"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "resolved"
+msgstr "opgelost"
+
+#: lib/RT/Date.pm:330
+msgid "sec"
+msgstr "sec"
+
+#: lib/RT/System.pm:86
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "stalled"
+msgstr "geparkeerd"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:80
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "systeem %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "systeem groep '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "het aanroepende component specificeerde niet waarom"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "ticket #%1 %2"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "onbeschreven groep %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "gebruiker %1"
+
+#: lib/RT/Date.pm:346
+msgid "weeks"
+msgstr "weken"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "met sjabloon %1"
+
+#: lib/RT/Date.pm:354
+msgid "years"
+msgstr "jaren"
+
diff --git a/rt/lib/RT/I18N/no.po b/rt/lib/RT/I18N/no.po
new file mode 100644
index 0000000..74adef6
--- /dev/null
+++ b/rt/lib/RT/I18N/no.po
@@ -0,0 +1,6563 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2003-04-01 06:06+0200\n"
+"PO-Revision-Date: 2006-12-20 20:59+0100\n"
+"Last-Translator: Ronny Pettersen <ronny.pettersen@edb.com>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "#%1"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr "$1"
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr "$prefix %1"
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %3. %2 %7 %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 lagt til"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 siden"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 ble endret til %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 of group %3"
+msgstr "%1 %2 av gruppen %3"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 med mal %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 denne saken\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "%1 - %2 vist"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Et parameter til %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Viser statusoppdateringer til STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Oppgi kommandomodulen du ønsker å bruke"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Oppgi betingelsesmodulen du ønsker å bruke"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Oppgi søkemodulen du ønsker å bruke"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "%1 KommandoScript lastet"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 ble lagt til som verdi for %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "%1 alias trenger en ReferanseId å jobbe mot"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "%1 alias trenger en saksnummer å jobbe mot "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "%1 alias trenger et saksnummer å jobbe mot (fra %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 ser ut til å være et lokalt objekt, men kan ikke finnes i databasen"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 av %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 ble endret fra %2 til %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 kopi"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 kunne ikke settes til %2."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 kunne ikke starte en transaksjon (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 kunne ikke sette status til løst. RT-basen kan være inkonsistent."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 opprettet"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 slettet"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "Mine %1 høyst prioriterte saker..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "Mine %1 høyst prioriterte forespørsler..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 er et verktøy for å behandle saker fra eksterne verktøy, slik som cron."
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 er ikke lenger en %2 for denne køen."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 er ikke lenger en %2 for denne saken."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 er ikke lenger en verdi for fleksifeltet %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 er ikke et gyldig saksnummer."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 vises ikke"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 rettigheter"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 var velykket\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 er ukjent type for $saksnummer"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 er ukjent type for %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 was created without a CurrentUser\\n"
+msgstr "%1 ble opprettet uten en aktiv bruker\\n"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 vil løse alle medlemmer av en løst gruppesak."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 vil stoppe en [lokal] BASE hvis den er avhengig av/medlem av en tilkoblet sak."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: ingen vedlegg oppgitt"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' er en ugyldig statusverdi"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' er ikke en kjent handling"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(Merk for å slette gruppemedlem)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Merk for å slette Scrip)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Merk for å slette)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(Merk boksene for å slette)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Skriv inn referansenummer eller URler, separert med mellomrom)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Settes til standard %1 hvis blank)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(Ingen Verdi)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Ingen fleksifelt)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Ingen medlemmer)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Ingen scrips)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Ingen maler)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Sender en kopi av denne oppdateringen til en kommaseparert liste med epostaddresser. Endrer <b>ikke</b> hvem som vil motta fremtidige oppdatreinger.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Sender en kopi av denne oppdateringen til en kommaseparert liste med epostaddresser. Endrer <b>ikke</b> hvem som vil motta fremtidige oppdateringer.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Sender en kopi av denne oppdateringen til en kommaseparert liste av administrative epostaddresser. Disse vil <b>vil</b> motta fremtidige oppdateringer.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Sender en kopi av denne oppdateringen til en komma-separert liste av epostaddresser. Endrer <b>ikke</b> hvem som vil motta fremtidige oppdateringer.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Sender en kopi av denne oppdateringen til en kommaseparert liste med epost-addresser. Endrer <b->ikke</b> hvem som vi motta fremtige utfordrer dere nå."
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Sender en kopi av dette oppdateringen til en kommaseparert liste med epostaddresser. Disse <b>vill</b> motta fremtidige oppdateringer.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr ""
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(tom)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(navn ikke oppgitt)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(ingen overskrift)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(ingen verdi)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr ""
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(bare en sak)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(Venter på godkjenning)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(venter på andre saker)"
+
+#: NOT FOUND IN SOURCE
+msgid "(requestor's group)"
+msgstr "(kundens gruppe)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(nødvendig)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(ingen tittel)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "Mine 25 høyst prioriterte saker..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "Mine 25 høyst priorterte forespørsler..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket-:Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Ny sak i\">&nbsp;%1"
+
+#: NOT FOUND IN SOURCE
+msgid "??????"
+msgstr "??????"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "En tom mal"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Deleted"
+msgstr "ACE slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Loaded"
+msgstr "ACE lastet"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be deleted"
+msgstr "ACE kunne ikke slettes"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be found"
+msgstr "fant ikke ACE"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE ikke funnet"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACEr kan bare opprettes og slettes."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Avbryter for å ungå uånsket saksendring"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Om meg"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Aksesskontroll"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Handling"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Handling %1 finnes ikke"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Handling skrevet."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Handling forberedt"
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr ""
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Legg til AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Legg til Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr ""
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr ""
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Legg til flere filer"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Next State"
+msgstr "Legg til neste status"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Legg til kunde"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Legg til verdi"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip to this queue"
+msgstr "Legg til Scrip i denne køen"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip which will apply to all queues"
+msgstr "Legg til et Scrip som gjelder for alle køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a keyword selection to this queue"
+msgstr "Legg til et nøkkelordvalg på denne køen"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Legg til et globalt Scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Legg til et Scrip til denne køen"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Legg til et Scrip som vil gjelde for alle køer"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Legg til kommentarer eller svar til denne saken"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Legg til medlemmer"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Legg til overvåkere"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "AddNextState"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "La til primær som en %1 for denne køen"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "La til primær som en %1 for denne saken"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adresse1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adresse2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Admin Kommentar"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Admin-korrespondanse"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Adminkøer"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Adminbrukere"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Admin/Global konfigurasjon"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Admin/Grupper"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Admin/Køer/Grunnleggende"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "AdminAllePersonalGrupper"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "AdminKommentar"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "AdminKorrespondanse"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "AdminFleksifelt"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "AdminGruppe"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "AdminGruppeMedlemskap"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "AdminEgnePersonligeGrupper"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "AdminKø"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "AdminBrukere"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Administrativ Cc"
+
+#: NOT FOUND IN SOURCE
+msgid "Admins"
+msgstr "Admin"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Avansert Søk"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Etter"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Alder"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Alias"
+msgstr "Alias"
+
+#: NOT FOUND IN SOURCE
+msgid "Alias for"
+msgstr "Alias for"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Alle Fleksifelt"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Alle køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Send alltid en melding til kunden uavhengig av meldingssender"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr ""
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Godkjennelse"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Godkjennelse #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Godkjenning # %1: Notater kunne ikke lagres pga. systemfeil"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Godkjenning #%1: Notater lagret"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Godkjenning - Detaljer"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr ""
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Godkjenningsdiagram"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Godkjenn"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Godkjenners notater: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Apr."
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "April"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Stigende"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Legg Ved"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Legg ved fil"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Vedlagt fil"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Vedlegg '%1' kunne ikke lastes"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Vedlegg opprettet"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Vedleggsnavn"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Vedlegg"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr ""
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Aug."
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "August"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "AutSystem"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Autosvar"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Autosvar Til Kunde"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "AutosvarTilKunde"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Ugyldig PGP-signatur: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Ugyldig vedleggsid. Kunne ikke finne vedlegg '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Ugyldig data i %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Ugyldig transaksjonsnummer for vedlegg. %1 skulle vært %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Detaljer"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Sørg for å lagre endringene dine"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Før"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "Begynn Godkjenning"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Blank"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "URL som kan brukes som bokmerke for dette søket"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Begrens headere"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Masseoppdatering av saker"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Kan ikke endre systembrukere"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Kan denne primæren se denne køen"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Kan ikke legge til en verdi for et fleksifelt uten navn"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Kan ikke koble en sak til seg selv"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Kan ikke flette inn i en flettet sak. Denne meldingen bør ikke forekomme"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr ""
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Kan ikke spesifisere både base og mål."
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Kunne ikke oprette bruker: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Endre passord"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr ""
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Merk for å slette"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Merk for å trekke tilbake rettighet"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Barn"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "By"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Lukket"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "Lukkede Saker"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Lukkede forespørsler"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Lukkede saker"
+
+#: NOT FOUND IN SOURCE
+msgid "Code"
+msgstr "Kode"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Kunne ikke tolke kommando!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Kommenter"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Kommentaraddresse"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Kommentaren ble ikke lagret"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Kommenter saker"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "KommenterSak"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Kommentarer"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Kommentarer (Ikke send til kunder)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Kommentarer (ikke sendt til kunder)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Kommentarer til %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Kommentarer om denne brukeren"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "La til kommentarer "
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Lagring forkortet"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Kompilatorrestriksjoner"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Forutsetning"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Forutsetning gjelder..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Forutsetning ikke funnet"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Konfigurasjon"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Bekreft"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "KontaktInfoSystem"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Kontatdato '%1' kunne ikke tolkes"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Innhold"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Coould not create group"
+msgstr "Kunne ikke opprette gruppen"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr ""
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Korrespondanse"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Korrespondanseaddresse"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Korrespondanse lagt til"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Korrespondansen ble ikke lagret"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Kunne ikke legge til nye fleksifeltverdier for saken. "
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "Kunne ikke legge til nye fleksifeltverdier for saken. %1 "
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr ""
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Kunne ikke endre eier. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Kunne ikke opprette fleksifelt"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr ""
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Kunne ikke opprette gruppe"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Kunne ikke opprette mal: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Kunne ikke opprette sak. Kø ikke satt"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Kunne ikke opprette bruker"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "Kunne ikke opprette overvåker for kunde"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Kunne ikke finne en sak med id %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Kunne ikke finne gruppen %1."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Kunne ikke finne eller lage den brukeren"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Kunne ikke finne den primæren"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Kunne ikke finne brukeren %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Kunne ikke hente gruppen"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Kunne ikke sette den primæren som %1 for denne køen"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Kunne ikke sette den primæren som %1 for denne saken"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Kunne ikke fjerne den primæren som %1 for denne køen"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Knne ikke fjære den primæren som %1 for denne saken"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Kunne ikke legge til medlemmmer i gruppen"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Kunne ikke opprette en transaksjon: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Kunne ikke tolke gpgs svar\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Kunne ikke finne gruppen\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Kunne ikke finne raden"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Kunne ikke finne primæren"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Kunne ikke finne verdien"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "Kunne ikke finne den overvåkern"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Kunne ikke finne bruker\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Kunne ikke laste %1 fra brukerdatabasen.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load KeywordSelects."
+msgstr "Kunne ikke laste NøkkelordValg."
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "Kunne ikke laste RTs konfigurasjonsfil '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Kunne ikke laste Scripsene."
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Kunne ikke laste gruppen %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Kunne ikke laste linken"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr ""
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Kunne ikke laste køen"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Kunne ikke laste køen %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Kunne ikke laste scripet"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Kunne ikke finne mal"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Kunne ikke laste den brukeren (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Kunne ikke laste saken '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Land"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Opprett"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Opprett Saker"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Oprett et fleksifelt"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Opprett et fleksifelt for køen %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Opprett et fleksifelt for alle køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Opprett et nytt fleksifelt"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global Scrip"
+msgstr "Opprett et globalt Scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Opprett et nytt globalt scrip"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Opprett en ny gruppe"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Opprett en ny personlig gruppe"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Opprett en ny kø"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Opprett et nytt scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Opprett en ny mal"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Opprett en ny sak"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Opprett en ny bruker"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Opprett en ny kø"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Opprett en kø kalt"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Opprett en forespørsel"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Opprett et scrip for køen %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Opprett en mal"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Opprett en sak"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "Opprettelse feilet: %1 / %2 / %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "Opprettelse feilet: %1/%2/%3"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Opprett nye saker basert på dette scripets mal"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Opprett sak"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Opprett saker i denne køen"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Opprett, slett og modifiser fleksifelt"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Opprett, slett og endre køer"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Opprett, slett og modifiser medlemmene av en brukers personlige grupper"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Opprett, slett og modifiser medlemmene av personlige grupper"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Opprett, slett og modifiser brukere"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "OpprettSak"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Opprettet"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Opprettet Fleksifelt %1"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Opprettet malen %1"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr ""
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Eksisterende Forhold"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Eksisterende Scrips"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Eksisterende medlemmer"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Eksisterende rettigheter"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Eksisterende søkekriterier"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Eksisterende overvåkere"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Fleksifeltet #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Fleksifelt"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Avsluttningskode"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Forberedelseskode"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Forutsetning"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Fleksifeltet %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Fleksifeltet %1 har en verdi."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Fleksifeltet %1 har ingen verdi."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Fleksifeltet %1 kunne ikke finnes"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Fleksifeltet slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Fleksifeltet kunne ikke finnes"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Verdien %1 for fleksifeltet %2 kunne ikke finnes"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Fleksifeltets verdi endret fra %1 til %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Fleksifeltets verdi kunne ikke slettes"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Fleksifeltets verdi kunne ikke finnes"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Fleksifeltverdi slettet"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "FleksiFelt"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "Datafeil"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Datoer"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Des."
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "Desember"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Standard Autosvarmal"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Standard Autosvarmal"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr ""
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr ""
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Standard Adminkommentarmal"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Standard Adminkorrespondensemal"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Standard korrespondensemal"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Standard transaksjonsmal"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Standard: %1/%2 endret seg fra %3 til %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Deleger rettigheter"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Deleger spesifikke rettigheter som har blitt gitt til deg."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "DelegerRettigheter"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Delegering"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Slett"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Slett saker"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "SlettSak"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Sletting av dette objektet kan føre til inkonsistens"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Sletting av dette objektet vil føre til inkonsistens"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Sletting av dette objektet ville føre til inkonsistens"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "Sletting av dette objektet ville føre til inkonsisistens."
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity. That's bad."
+msgstr "Sletting av dette objektet ville føre til inkonsistens. Det er uheldig."
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Nekt"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Avhengighet fra"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Avhengigheter: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Avhengig av"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "AvhengigAv"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Synkende"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Beskriv problemet under"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Beskrivelse"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Detaljer"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Vis"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Vis Rettigheter"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Vis Scrip-maler for denne køen"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Vis Scrip-maler for denne køen"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Visningsmodus"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Vis saken #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr ""
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Gjør hva som helst"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Ikke last denne siden på nytt"
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Ikke vis søkeresultat"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Last ned"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr ""
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Innen"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Innendato '%1' kunne ikke tolkes"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "FEIL: Kunne ikke laste sak '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Rediger"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Conditions"
+msgstr "Rediger Forhold"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Rediger fleksifelt for %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Rediger Forhold"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Rediger Maler for køen %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "Rediger nøkkelord"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Rediger scrips"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Rediger systemmal"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Rediger maler for %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Rediger Konfigurasjon for køen %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Redigerer Konfigurasjonen av brukern %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Redigerer Fleksifeltet %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Redigerer medlemsskap for gruppen %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Redigerer medlemsskap for den personlige gruppen %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Redigerer malen %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Enten base eller mål må oppgis"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Epost"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Epostaddresse i bruk"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "EpostAddresse"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "EpostFormat"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Aktivt (Fjern merkingen for å deaktivere dette fleksifeltet)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Aktiv (Fjern merkingen for å deaktivere denne gruppen)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Aktiv (Fjern merkingen for å deaktivere denne køen)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "Aktive Fleksifelt"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Aktive Køer"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Aktiv status %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Skriv multiple verdier"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Skriv en verdi"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr ""
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Skriv saker og/eller URIer som det skal linkes til. Separer dem med mellomrom"
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr ""
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Feil"
+
+#: NOT FOUND IN SOURCE
+msgid "Error adding watcher"
+msgstr "Feilet ved opprettelse av Overvåker"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Feil i parameterne til Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Feil i parameterne til Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Feil i parameterne til Ticket->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Feil i parameterne til Ticket->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr ""
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr ""
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Alle"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Eksempel:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "EksternAutId"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "EksternKontaktInfoId"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Ekstra info"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Kunne ikke finne pseudogruppen 'Privilgerte' brukere."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Kunne ikke finne 'pseudogruppen 'Upriviligerte' brukere"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Kunne ikke laste modulen %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb."
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "Februar"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "End"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Endelig Prioritet"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "EndeligPrioritet"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Finn grupper hvor"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Finn nye/Ã¥pne saker"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Finn folk hvor"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Finn saker"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Fullfør godkjennelse"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Først"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Første side"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Tving gjennom endring"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr ""
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Fant %quant(%1) sak(er)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Fant Objektet"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FriforkKontaktInfo"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "FriformMultipel"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "FriformSingel"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Fre."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Fulle headere"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Henter brukerinfo fra pgp signatur\\n"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Gitt til %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Global Keyword Selections"
+msgstr "Globale Nøkkelordvalg"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Globale Scrip"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Globale maler: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr ""
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Start!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Gyldig pgp sig fra %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "GÃ¥ til siden"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "GÃ¥ til saken"
+
+#: NOT FOUND IN SOURCE
+msgid "Grand"
+msgstr "Stor"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Gruppe"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Gruppen %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Grupperettigheter"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Alt medlem av gruppen"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "Gruppen kunne ikke lastes."
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Gruppen kunne ikke opprettes: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Gruppen opprettet"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Gruppen har ikke det medlemmet"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Fant ikke gruppen"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Fant ikke gruppen.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Ikke spesifisert gruppe.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Grupper"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Grupper kan ikke være medlemmer av sine medlemmer"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr ""
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hallo!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Hallo, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historikk"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr ""
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "HjemmeTelefon"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Hjemmeside"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Jeg har %quant(%1, sementblandere)."
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "Jeg har [quant,_1,sementblandere]."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Id"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identitet"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Hvis en godkjenner blir avvist, avvis orginalen, og slett ventende godkjenninger"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr ""
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr ""
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Hvis dette verktøyet var setgid kunne en fiendtlig lokal bruker bruke dette verktøyet for å oppnå administrativ tilgang til RT."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Hvis du har oppdatert noe over, sørg for at"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Ugyldig verdig for %1"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "LÃ¥st felt"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Inkluder deaktiverte fleksifelt i listen."
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr ""
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Inkluder deaktiverte køer i listen."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Inkluder deaktiverte brukere i søket."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Startprioritet"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "StartPrioritet"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Feil i inntasting"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Interest noted"
+msgstr "Interesse registrert"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Intern Feil"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Intern Feil: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Ugyldig gruppetype"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Ugyldige rettigheter"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "Ugyldig Type"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Ugyldig data"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Ugydlig eier. Setter til 'nobody'."
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Ugyldig kø"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Ugyldige rettigheter"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Ugyldig verdi for %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Ugyldig verdi for fleksifeltet."
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Ugyldig verdi for status"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Det er ekstremt viktig at ikkepriviligerte brukere ikke har tilgang til dette verktøyet."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Det er anbefalt at du oppretter en upriviligert unixbruker med korrekt gruppemedlemsskap og tilgang til RT for  kjøre dette verktøyet."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Det tar flere parametere:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Ting som venter på min godkjenning"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan."
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "Januar"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Bli med i eller forlat denne gruppen"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul."
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "Juli"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Total"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun."
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "Juni"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Nøkkelord"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Språk"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Siste"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Siste Kontakt"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Sist kontaktet"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Sist Informert"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Sist Oppdatert"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "SistOppdatert"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Igjen"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "La denne brukeren få tilgang til RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "La denne brukeren få rettigheter"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Begrenser eier til %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Begrenser køen til %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Lenke finnes alt"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Lenke kunne ikke opprettes"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Lenke opprettet (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Lenke slettet (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Lenke ble ikke funnet"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Knytt sak #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "Knytt sak %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Lenker"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Lokasjon"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Logkatalogen %1 ble ikke funnet eller kunne ikke skrives til.\\nRT kan ikke kjøre."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Logget inn som %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Innlogging"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Logg av"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Sett Eier"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Sett Status"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Sett tidsfrist "
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Sett løsningsdato"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Sett startdato"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Sett startdato"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Sett informert dato"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Sett prioritet"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Sett Kø"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Sett Emne"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr ""
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Sett grupper og gruppemedlemsskap"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Rediger egenskaper og konfigurasjon som gjelder for alle køer"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Rediger køer og kø-spesifike egenskaper"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Rediger brukere og passord"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Mars"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Mai"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Mai."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Medlem lagt til"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Medlem slettet"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Medlem ikke slettet"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Medlem av"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "MedlemAv"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Medlemmer"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr ""
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Fletting vellykket"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Fletting feilet. Kunne ikke sette EffektivId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Flett inn i"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr ""
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Melding"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr ""
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Mangler en primærnøkkel?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Mobil"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "MobilTelefon"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Endre Tilgangslister"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Endre Fleksifeltet %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Endre Fleksifelt som gjelder for alle køer"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr ""
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Endre Scripmaler for denne køen"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Endre Scrips for denne køen"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "Endre SystemACLer"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Endre Malen %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr ""
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Endre et fleksifelt for køen %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Endre et fleksifelt som gjelder for alle køer"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Endre et scrip for køen %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Endre et scrip som gjelder for alle køer"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "Endre datoer for # %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Endre datoer for #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Endre datoer for sak # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Endre globale grupperettigheter"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Endre globale grupperettigheter"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "Endre globale rettigheter for grupper"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "Endre globale rettigheter for brukere"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Endre globale scrips"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Endre globale brukerrettigheter"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Endre globale brukerrettigheter"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Endre gruppens metadata eller slette gruppen"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Endre grupperettigheter for %1 gruppen"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Endre grupperettigheter %1 køen"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Endre medlemsliste for denne gruppen"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Endre sin egen RT konto"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Endre hvem som er relatert til %1 køen"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Endre hvem som er relater til sak #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Endre scrips for %1 køen"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Endre scrips som gjelder alle køer"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Endre mal %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Endre maler som gjelder for alle køer"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Endre gruppen %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Endre overvåkere for køen"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Endre brukeren %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Endre sak # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Endre sak #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Endre saker"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Endre brukerrettigheter for %1 gruppen"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Endre brukerrettigheter for %1 køen"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Endre overvåkere for '%1' køen"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "EndreACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "EndreEgetMedlemskap"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "EndreKøOvervåkere"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "EndreScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "EndreSegSelv"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "EndreMal"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "EndreSak"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Man."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Mer om %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Flytt ned"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Flytt opp"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Flere"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "MÃ¥ spesifisere attributten 'Navn'"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "Mine %1 saker"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Mine saker til godkjenning"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Mine saker til godkjenning"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Navn"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Navnet er i bruk"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Trenger godkjennelse fra systemadministrator"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Aldri"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Ny"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nye forhold"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nytt Passord"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Ny, Venter på Godkjennelse"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Nytt Søk"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nytt fleksifelt"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Ny gruppe"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nytt passord"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Melding om nytt passord sendt"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Ny kø"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Ny forespørsel"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nye rettigheter"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Nytt scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Nytt søk"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Ny mal"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Ny sak"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Ny sak eksistere ikke"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Ny bruker"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Ny bruker kalt"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Ny overvåker"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Instillinger for nytt vindu"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Neste"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Neste side"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "KalleNavn"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Kallenavn"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Ingen FleksiFelt"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Ingen grupper definert"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Ingen kø definert"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Ingen RT bruker funnet. Vennligst referer til manualen.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Ingen Mal"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Ingen sak oppgitt. Avbryter sak "
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "Ingen Sak oppgitt. Avbryter saksendring\\n\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Ingen handling"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Ingen kolonne spesifisert"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Ingen kommando funnet\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Ingen kommentar skrevet om denne brukeren"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Ingen korrespondanse vedlagt"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Ingen beskrivelse for %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Ingen gruppe spesifisert"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Passordet er ikke satt"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Ingen tilgang til å opprette køer"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Ikke tilgang til å opprette saker for køen '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Ikke tilgang til å opprette brukere"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Ikke tilgang til å vise den saken"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Ingen tilgang til å se oppdatering av saken"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Ingen primær spesifisert"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Ingen primære spesifisert"
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Det er ingen køer som matcher søkekriteriet"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Ingen rettigheter funnet"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Ingen rettigheter tildelt"
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Ingen søk å behandle"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Ingen saksid oppgitt"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Transaksjonstype ikke spesifisert"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "Ingen bruker eller epostaddresse oppgitt"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Fant ingen brukere som treffer søkekriteriene."
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Fant ingen gyldig RT bruker. RT cvs handler avstengt. Kontakt din RT administrator.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Ingen verdi sendt til _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Ingen"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Ukjent felt?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Ikke logget inn"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Ikke logget inn."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Ikke satt"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Ikke implementert enda."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Ikke implementert enda...."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Notater"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Melding kunne ikke sendes"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Raporter til AdminCc"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Rapporter til AdminCc som kommentar"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr ""
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Rapporter til andre mottakere"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Rapporter til andre mottakere som kommentar"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Rapporter til eier"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Rapportert til eier som kommentar"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr ""
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr ""
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr ""
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Rapporter til Eiere og AdminCc om nye ting som venter på godkjenning"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Rapporter til kunde"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Rapporter til Kunder og Cc"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Rapporter til Kunder og Cc som kommentar"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Rapporter til Kunder Cc og AdminCc"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Rapporter til Kunder Cc og AdminCc som Kommentar"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "November"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr ""
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objekter kunne ikke opprettes"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objektet ble opprettet"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Okt."
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "Oktober"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr ""
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr ""
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Ved"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Ved Kommentar"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Ved Korrespondanse"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Ved Opprettelse"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Ved Eierskifte"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr ""
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Ved Køendring"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Ved Løsning"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Ved statusendring"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Ved Transaksjon"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Vis kun godkjennelse for saker opprettet etter %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Bare vis godkjennelse for saker opprettet før %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Ã…pne"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Ã…pne den"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Åpne forespørsler"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Ã…pne saker"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Ã…pne saker (fra utlisting) i et nytt vindu"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Ã…pne saker (fra utlisting) it et annet vinud"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Ã…pne saker ved korrespondanse"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Rekkefølge og sortering"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organisasjon"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Opprinnelig sak: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Over tid beveger prioriteten seg mot"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Eie saker"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "EieSak"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Eier"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Eier endret fra %1 til %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Eier ble tvunget til å endres fra %1 til %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "Eier er"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Personsøker"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "PersonSøker"
+
+#: NOT FOUND IN SOURCE
+msgid "Parent"
+msgstr "Forelder"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Foreldre"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Passord"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Passordhint"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "For kort passord"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Passord: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Passordene stemmer ikke overens."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Passordene stemmer ikke overrens. Passordet ble ikke endret"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Personer"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Kjør en brukerdefinert handling"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Ingen Tilgang"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Personlige Grupper"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Personlige grupper"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Personlige grupper:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefonnummer"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Stedholder"
+
+#: NOT FOUND IN SOURCE
+msgid "Pref"
+msgstr "Pref"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Instillinger"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Pref"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Klargjør Forkortet"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Forrige"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Forrige side"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Primær %1 ikke funnet."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioritet"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Prioritet starter på"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr ""
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Priviligert"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Priviligert status: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Priviligerte brukere"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseduogruppe for intern bruk"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr ""
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Kø"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Køen %1 kunne ikke finnes"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Køen '%1' ikke funnet\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Keyword Selections"
+msgstr "Nøkkelordvalg for kø"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Kønavn"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Køscrip"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Køen eksisterer allerede"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Køen kunne ikke opprettes"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Køen kunne ikke lastes."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Køen opprettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Køen er ikke oppgitt."
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Køen ikke funnet"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Køer"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Raskt søk"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr ""
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 for %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 fra <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT-administrasjon"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "RT Autentiseringsfeil."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT Avvisning: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "RT Konfigurasjonsfeil"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "Kritisk RT feil. Meldingen ble ikke lagret!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT Feil"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT Mottok mail (%1) fra seg selv."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Recieved mail (%1) from itself."
+msgstr "RT Mottok mail (%1) fra seg selv."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT Selvbetjening / Lukkede Saker"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr ""
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT oversikt"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT kunne ikke autentisere deg"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT kunne ikke finne kunde via sitt eksterne databaseoppslag"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT kunne ikke finne køen: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT kunne ikke validere denne PGP signaturen. \\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT for %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT for %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT har behandlet dine kommandoer"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT er &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Den er distribuert under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-2002 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT er &copy; Copyright 1996-2002 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Den er distribuert under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT tror denne meldingen kan være en returmail"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT vil behandle denne meldingen som om den var usignert"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RT's epost kommandomodus krever PGP autentisering. Meldingen din var enten ikke signert, eller signaturen din kunne ikke bekreftes."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Ekte Navn"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "EkteNavn"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Referert til av"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Refererer til"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "RefererTil"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Redefiner"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Redefiner søket"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Last siden på nytt hvert %1 minutt."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Fjern AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Fjern Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Fjern Kunde"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Svar"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr ""
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Svar på sak"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "SvarPÃ¥Sak"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Kunde"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Kundens epostaddresse"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Kunde(r)"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "KundeAddresser"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Kunder"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Forespørsler skal være behandlet innen"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr ""
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Reset"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Hjemme"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Løs"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Løs saknr #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Løst"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Svar til kunder"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Resultater"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Resultater per side"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Skriv Passord igjen"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Rettighet %1 kunne ikke finnes for %2 %3 in scope %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Rettighet Deligert"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Rettighet Tildelt"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Rettighet lastet"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Rettigheten kunne ikke trekkes tilbake"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Rettighet ikke funnet"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Rettighet ikke lastet."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Rettighet fjernet"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Rettigheter"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Rettigheter kunne ikke tildeles for %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Rettigheter kunne ikke trekkes tilbake for %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Roller"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "RootGodkjenning"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr ""
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Lør."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr ""
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Lagre Endringer"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Lage endringer"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip Opprettet"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip slettet"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Scrip for %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrip som gjelder for alle køer"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Søk"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Søkekriteria"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Søk etter godkjenninger"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr ""
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Sikkerhet:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "SeKø"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr ""
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Velg en gruppe"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Velg en kø"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr ""
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Velg en bruker"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Velg fleksifelt"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Velg gruppe"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Velg flere verdier"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Velg en verdi"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Velg kø"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Velg scrip"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Velg mal"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Velg bruker"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "VelgFlere"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "VelgEnkelt"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr ""
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Selvbetjening"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Send epost til alle overvåkere"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Send epost til alle overvåkere som \"kommentar\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Send epost til kunder og Cc"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Send epost til kunder og Cc som kommentar"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Sender en melding til kundene"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Send epost til eksplisit oppgitte Ccer og Bccer"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr ""
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Send epost til Administrative Ccer"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Sender epost til de administrative Ccene som kommentar"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Sender epost til eieren"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep."
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "September"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr ""
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Vis Resultater"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Vis godkjente forespørsler"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Vis basisinfo"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Vis avviste forespørsler"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Vis detaljer"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Vis ventende forespørsler"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Vis forespørsler som venter på andre godkjenninger"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Vis sakens private kommentarer"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Vis sakssammendrag"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "VisACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "VisScrip"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "VisMal"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "VisSak"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "VisSaksKommentarer"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Meld deg på som saksforespørrer eller sak/kø Cc"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Meld deg på som sak/kø AdminCc"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Signatur"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "Logget inn som %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Enkel"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr ""
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Dropp Meny"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sorter"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Sorter nøkkel"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Sorter resultater etter"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "SorteringsRekkefølge"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Pauset"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Startside"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Startet"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Startdato '%1' kunne ikke tolkes"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Starter"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Starter Etter"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Startdato '%1' kunne ikke tolkes"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Stat"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Status"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Statusendring"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Status endret fra %1 til %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "EndreStatus"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Stjel"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Stjålet fra %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Stjålet fra %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr ""
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Emne"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Endre emne til %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Oppdater"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Send Arbeidsflyt"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Lykkes"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Søn."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "SuperBruker"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "System"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Systemfeil"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. Right not granted."
+msgstr "Systemfeil. Rettighet ikke tildelt."
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. right not granted"
+msgstr "Systemfeil. rettigheter ikke tildelt"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Systemfeil. Rettighet ikke tildelt."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Systemfeil. Rettighet ikke tildelt."
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "Systemfeil. Kunne ikke tildele rettigheter."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Systemgrupper"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRollegruppe for intern bruk"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRENG"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Ta"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Tatt"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Mal"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Mal #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Mal slettet"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Kunne ikke finne mal"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Kunne ikke finne mal\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Mal tolket"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Maler"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Maler for %1\\n"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Verdien er allerede satt"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Det er ikke en verdi for dette fleksifeltet"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Det er den samme verdien"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Den primæren har allerede den rettigheten"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Den primæren er allerede en %1 for denne køen"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Den primæren er allerede en %1 for denne køen"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Den primæren er ikke en %1 for denne køen"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Den primæren er ikke en %1 for denne saken"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Den køen eksisterer ikke"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Denne saken har uløste avhengigheter"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Den brukeren har allerede den rettigheten"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Den brukeren eier allerede den saken"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Den brukeren finnes ikke"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Den brukeren er allerede priviligert"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Den brukeren er allerede upriviligert"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Denne brukeren er nå priviligert"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Dette brukeren er nå upriviligert"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "Den brukeren er allerede upriviligert"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Den brukeren kan ikke eie saker i den køen"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Dette er ikke en numerisk id"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Detaljer"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "CCen til en sak"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "Administrative CCer for en sak"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Kommentarer er lagret"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "De følgende kommandoene vil finne alle aktive saker i køen 'general' og sette deres prioritet til 99 hvis de ikke har blitt rørt de siste 4 timene:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "De følgende kommandoene ble ikke behandlet:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Den nye verdien har blitt satt."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Eieren av en sak"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Forespørren av en sak"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Disse kommentarene er generelt ikke synlig for brukeren"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Denne saken %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Dette verktøyet tillater brukeren å kjøre perlmoduler fra inni RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Denne transaksjonen ser ikke ut til å ha noe innhold"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Denne brukerens %1 høyst prioriterte saker"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "Denne brukerens 23 høys prioriterte saker"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Tor."
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket"
+msgstr "Sak"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Sak # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "Sak $ %1 Jumbo oppdater: %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Sak #%1 Jumbo oppdatering: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Sak #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Sak %1 opprettet i '%2' køen"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Sak %1 lastet\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Sak %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr ""
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Sakshistorikk # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "SaksId"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Løst Sak"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Saks-vedlegg"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Saks-innhold"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Sakens innholdstype"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Saken kunne ikke opprettes på grunn av en intern feil"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Sak opprettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Saksopprettelse feilet"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Sak slettet"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Saksid ikke funnet"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "Sak drept"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Sak ikke funnet"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Saksstatus endret"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Saksovervåkere"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Saker"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Saker %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Saker %1 av %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Saker fra %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Saker som er avhengige av denne godkjennelsen:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Tid Igjen"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Arbeidstid"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Tid igjen"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Tid å vise"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Arbeidstid"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "TidIgjen"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "ArbeidsTid"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "For å generere en diff av denne bekreftelsen:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "For å genere en diff av denne bekreftelsen"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Fortalt"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr ""
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transaksjon"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transaksjon %1 slettet"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transaksjon Opprettet"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Transaction->Create kunne ikke, siden du ikke spesifiserte en saksid"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transaksjoner er låst"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Prøver å slette en rettighet: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Tir."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Type"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Uimplementert"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unix login"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "UnixBrukerNavn"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Ukjent InnholdsFormatering %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Ubegrenset"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr ""
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Upriviligert"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Ikke tatt"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Oppdater"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Oppdater ID"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr ""
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Oppdater Type"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Oppdater alle disse sakene samtidig"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Oppdater epost"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Oppdater navn"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Oppdatering ikke lagret."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Oppdater valgte saker"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Oppdater signatur"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Oppdater sak"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Ooppdater sak # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Oppdater sak #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Oppdater sak #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Oppdateringstype var verken korrespondanse eller kommentar."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Oppdatert"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr ""
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr ""
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Bruker %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Bruker %1 Passord: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Brukeren '%1' ble ikke funnet"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Brukeren '%1' ble ikke funnet"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Bruker Definert"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "BrukerID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "BrukerId"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Brukerrettigheter"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Bruker kunne ikke opprettes: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Bruker opprettet"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Brukerdefinerte grupper"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Bruker informert"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Brukervisning"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Brukernavn"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Brukere"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Brukere som treffer søkekriteria"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "KøVerdi"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Verdier"
+
+#: NOT FOUND IN SOURCE
+msgid "VrijevormEnkele"
+msgstr "VrijevormEnkele"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Overvåk"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "OvervåkSomAdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "Overvåker lastet"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Overvåkere"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "WebFormatering"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Ons."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "NÃ¥r en sak har blitt godkjent av alle godkjennere, legg til korrespondanse for den opprinnelige saken"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "NÃ¥r en sak har blitt godkjent av en godkjenner, legg til korrespondanse til den orginale saken"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "NÃ¥r er sak er opprettet"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Når er godkjennelsessak blir opprettet, gi melding til Eier og AdminCc om saken som venter på deres godkjenning"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "NÃ¥r noe skjer"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Når en sak er løst"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Når en sak får ny eier"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr ""
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Når en sak flyttes til en ny kø"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "NÃ¥r en saks status endres"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "NÃ¥r brukerdefinerte forhold intreffer"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "NÃ¥r kommentarer kommer inn"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "NÃ¥r korrespondanse kommer inn"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Arbeid"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "ArbeidsTelefon"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Arbeidet"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Du eier allerede denne saken"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Du er ikke en autorisert bruker"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Du kan bare omfordele saker som du eier eller som ikke har en eier"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "Du har ikke tilgang til å se den saken.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Du fant %1 saker i %2 køen"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr ""
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Du har ikke tilgang til å opprette saker i den køen."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Du kan ikke opprette forespørsler i den køen."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Velkommen tilbake"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Dine %1 forespørsler"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "Din RT administrastor har feilkonfigurert mail aliasene som kaller RT"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Din forespørsel har blitt godkjent av %1. Andre godkjennelser avventer kanskje fortsatt"
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Din forespørsel ble godkjent."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Din forespørsel ble avvist"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Din forespørsel ble avvist"
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Ditt brukernavn/passord er ugyldig"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Zip"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[ikke noe emne]"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "som tildelt til %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "lukket"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "inneholder"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "innhold"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "innholdstype"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "korrespondanse (sansynligvis) ikke sendt"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "korrespondanse sendt"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "dager"
+
+#: NOT FOUND IN SOURCE
+msgid "dead"
+msgstr "død"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "slett"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "slettet"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "treffer ikke"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "inneholder ikke"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "lik som"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr ""
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "usant"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "filnavn"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "større enn"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "gruppe '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "timer"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "id"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "er"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "er ikke"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "mindre enn"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "treffer"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minutter"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "endringer\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "måneder"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "ny"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "ingen verdi"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "ingen"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "ikke lik som"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "ikkelik"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "Ã¥pen"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "personlig gruppe '%1' for bruker '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "kø %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "avvist"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "løst"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sek"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "pauset"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "system %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "systemgruppe '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "den kallende komponenten oppga ikke hvorfor"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "sak #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "sant"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "ubeskrevet gruppe %1"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "ubeskrevet gruppe %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "bruker %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "uker"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "med malen %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "Ã¥r"
+
diff --git a/rt/lib/RT/I18N/pl.po b/rt/lib/RT/I18N/pl.po
new file mode 100644
index 0000000..05a713d
--- /dev/null
+++ b/rt/lib/RT/I18N/pl.po
@@ -0,0 +1,6715 @@
+# Copyright (c) 2002 Jesse Vincent <jesse@bestpractical.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2007-01-18 11:36+0800\n"
+"PO-Revision-Date: 2005-10-03 14:26-0400\n"
+"Last-Translator: Piotr Åšliwa <piotr.sliwa@comarch.pl>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "Nr"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 nr%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %3 %2 %7, %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 zostały dodane"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "przed %1 %2"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 zostało zamienione na %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 zostało usunięte"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 of group %3"
+msgstr "%1 %2 z groupy %3"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 wg szablonu %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 tego zgłoszenia\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) przez %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (Niezmienione)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "wyświetlone %1 - %2"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - warunek przejścia na %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Status wyjściowy zaktualizowany na STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Określ moduł działań, którego chcesz użyć"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Określ moduł warunków, którego chcesz użyć"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Określ moduł wyszukiwania, którego chcesz użyć"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 Wszelkie prawa zastrzeżone 1996-%3 %4"
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "Skrypt %1 został załadowany"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "dodano %1 jako wartość dla %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "do określenia aliasów dla %1 wymagany numer zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "do określenia aliasów dla %1 wymagany numer zgłoszenia "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "do określenia aliasów dla %1 wymagany numer zgłoszenia (od %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "Wydaje się, że %1 to obiekt lokalny, ale nie udało się wyszukać go w bazie danych"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 przez %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 zmieniło się z %2 na %3"
+
+# Nie jestem pewien!
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "kopia %1"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "nie udało się ustawić %1 na %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 nie udało się rozpocząć transakcji (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 nie udało się ustawić statusu na zamknięty. Baza danych RT może być niespójna"
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr ""
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "%1 zgłoszeń o najwyższym priorytecie, których jestem właścicielem"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "%1 zgłoszeń o najwyższym priorytecie, których właścicielem jest użytkownik..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "%1 zgłoszeń o najwyższym priorytecie zarejestrowanych przez użytkownika..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 jest narzędziem umożliwiającym obsługę zgłoszeń z zewnętrznej aplikacji do sporządzania wykazów, takiej jak cron."
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 nie jest już %2 dla tej kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 nie jest już %2 dla tego zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 nie jest już wartością pola %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 nie jest aktualnie obowiÄ…zujÄ…cym Id kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 matches"
+msgstr "%1 pasuje"
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 most recently updated articles"
+msgstr "%1 ostatnio zaktualizowanych artykułów"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 newest articles"
+msgstr "%1 najnowszych artykułów"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 zgłoszeń bez właściciela"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 nie zostało wyświetlone"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 objets"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 - uprawnienia"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 udało się"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 to nieznany typ Id wiadomości ($MessageId)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 to nieznany typ dla %2"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 zamknie wszystkie części zamykanego zgłoszenia grupowego."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 zamknie bazę lokalną, jeśli jest powiązana z podłączonym zgłoszeniem (lub jest jego częścią)"
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "Zapisane zapytania %1"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: nie określono załącznika"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' to nieprawidłowa wartość statusu"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' n'est pas une action connue. "
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(Zaznacz pole, aby usunąć element z grupy)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Zaznacz pole, aby usunąć skrypt)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Zaznacz pole, aby usunąć)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(Zaznacz pola, aby usunąć)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Zaznacz pola przy odbiorcach na liście, aby zablokować wysyłanie powiadomień)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Zaznacz pola przy odbiorcach na liście, aby uruchomić wysyłanie powiadomień)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Wprowadź numery lub adresy URL zgłoszeń, oddzielone spacjami)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Enter ticket ids or URLs, seperated with spaces)"
+msgstr "(Wprowadź numery lub adresy URL zgłoszeń, oddzielone spacjami)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Jeśli pozostawisz puste, domyślnie zostanie ustawione na %1)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Brak pól definiowanych przez użytkownika)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Brak członków grupy)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Brak skryptów)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Brak szablonów)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Brak)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Wysyła kopię zgłoszenia na listę adresów mailowych oddzielonych przecinkami, umieszczonych w polu BCC.<b> Nie zmienia</b> tego, kto będzie otrzymywał informacje o stanie zgłoszenia)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Wysyła kopię zgłoszenia na listę adresów mailowych oddzielonych przecinkami, umieszczonych w polu BCC. <b>Nie zmienia</b> tego, kto będzie otrzymywał informacje o stanie zgłoszenia)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Wysyła kopię zgłoszenia na listę administracyjnych adresów mailowych oddzielonych przecinkami. Te osoby <b> będą</b> otrzymywać informacje o stanie zgłoszenia)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Wysyła kopię zgłoszenia na listę adresów mailowych oddzielonych przecinkami. <b>Nie</b> zmienia tego, kto będzie otrzymywał informacje o stanie zgłoszenia)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Wysyła kopię zgłoszenia na listę adresów mailowych oddzielonych przecinkami. <b>Nie</b> zmienia tego, kto będzie otrzymywał informacje o stanie zgłoszenia)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Wysyła kopię zgłoszenia na listę adresów mailowych oddzielonych przecinkami. Te osoby <b>będą</b> otrzymywać informacje o stanie zgłoszenia)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Skorzystaj z tych pól jeśli wybrano 'Definiowany przez użytkownika' w atrybutach warunku lub operacji)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(pusta)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no Summary)"
+msgstr "(brak streszczenia)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(brak listy użytkowników)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no name)"
+msgstr "(brak nazwy)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(brak tematu)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(brak wartości)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(aucune valeur)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(tylko jedno zgłoszenie)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(oczekuje na zatwierdzenie)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(oczekuje na innÄ… KolekcjÄ™)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(oczekuje na inne zgłoszenia)"
+
+#: NOT FOUND IN SOURCE
+msgid "(requestor's group)"
+msgstr "(groupe du demandeur)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(wymagana)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(nienazwany)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "25 zgłoszeń o najwyższym priorytecie, których właścicielem jest użytkownik..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "25 zgłoszeń o najwyższym priorytecie zarejestrowanych przez użytkownika..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Nowe zgłoszenie w\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Pusty szablon"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "Nie udało się odnaleźć ACE"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE mogą być tylko utworzone lub usunięte."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Przerwanie operacji, aby uniknąć niezamierzonych modyfikacji zgłoszenia.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Użytkownika"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Kontrola dostępu"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Operacja"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Operacja %1 nie została odnaleziona"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Operacja została zatwierdzona"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Operacja została przygotowana..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Dodaj"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Dodaj AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Dodaj Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr ""
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Dodaj kryterium"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Dodaj więcej plików"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Dodaj zgłaszającego"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Dodaj wartość"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Dodaj nowy skrypt globalny"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Dodaj skrypt dla tej kolejki"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Dodaj skrypt, który będzie dotyczył wszystkich kolejek"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "Dodaj kolejne kryterium"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Dodaj komentarze lub odpowiedzi do wybranych zgłoszeń"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Dodaj członków grupy"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Dodaj nowych obserwatorów"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "Dodaj następny status"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Został dodany zarządzający %1 dla tej kolejki"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Został dodany zarządzający %1 dla tego zgłoszenia"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adres 1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adres 2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Administracja komentarzami"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Administracja korespondencjÄ…"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Administracja kolejkami"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Administracja użytkownikami"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Globalna konfiguracja - administracja"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Grupy - administracja"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Administracja kolejkami . podstawowe informacje"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "AdminAllPersonalGroups"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "AdminComment"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "AdminCorrespondence"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "AdminPolamiUżytk"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "AdminCustomFields"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "AdminGroup"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "AdminGroupMembership"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "AdminOwnPersonalGroups"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "AdminQueue"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "AdminUsers"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Administracyjne Cc"
+
+#: NOT FOUND IN SOURCE
+msgid "Admins"
+msgstr "Administrateurs"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Zaawansowane"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Wyszukiwanie zaawansowane"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search Criteria"
+msgstr "Kryteria wyszukiwania zaawansowanego"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "po"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Wiek"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Alias for"
+msgstr "Alias pour"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Wszystkie warunki poprawności spełnione"
+
+#: NOT FOUND IN SOURCE
+msgid "All Classes"
+msgstr "Wszystkie klasy"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Wszystkie pola definiowane przez użytkownika"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Wszystkie kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Zawsze wysyła wiadomość do zgłaszających niezależnie od nadawcy wiadomości"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "I/Lub"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Zastosuj do: "
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Zastosuj"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Zastosuj zmiany"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Potwierdzenia"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Potwierdzenie #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Potwierdzenie #%1: Uwagi nie zostały zapisane z powodu błędu systemu"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Potwierdzenie #%1: Uwagi zostały zapisane"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Szczegóły zatwierdzenia"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Zatwierdzone"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Odrzucone"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Diagram potwierdzenia"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Zatwierdź"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Uwagi zatwierdzajÄ…cego: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Kwi."
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "Kwiecień"
+
+#: NOT FOUND IN SOURCE
+msgid "Are you sure you want to delete this article?"
+msgstr "Czy jestes pewien, że chcesz usunąć ten artykuł?"
+
+#: NOT FOUND IN SOURCE
+msgid "Article #%1 deleted"
+msgstr "Artykuł #%1 został usunięty"
+
+#: NOT FOUND IN SOURCE
+msgid "Article #%1: %2"
+msgstr "Artykuł #%1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Article not found"
+msgstr "Artykuł nie został odnaleziony"
+
+#: NOT FOUND IN SOURCE
+msgid "Articles"
+msgstr "Artykuły"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "RosnÄ…co"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Załączniki"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Załącz plik"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Plik został załączony"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Nie udało się dodać załącznika '%1'"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Załącznik został utworzony"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Nazwa pliku załącznika"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Załączniki"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Atrybut usunięty"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Sie."
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "Sierpień"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "AuthSystem"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Automatyczna odpowiedź"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Automatyczna odpowiedź wysyłana do zgłaszających"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "Automatyczna odpowiedź wysyłana do zgłaszających"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Available Columns"
+msgstr "Dostępne kolumny"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Niewłaściwy podpis PGP: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Niewłaściwy numer id załącznika. Nie udało się wyszukać załącznika '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Niewłaściwe dane w %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Niewłaściwy numer transakcji dla załącznika. %1 należy zastąpić przez %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Podst. informacje"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Pamiętaj, aby zapisać zmiany"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "przed"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "Rozpocznij zatwierdzanie"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "Binaire"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Pusty"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "Adres URL do zapamiętania dla tego wyszukiwania"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "URL do zapamiętania"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Krótkie nagłówki"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Hurtowa aktualizacja zgłoszeń"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Nie można zmienić użytkowników systemu"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Czy ten zarzÄ…dzajÄ…cy widzi tÄ™ kolejkÄ™"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Nie można wprowadzić wartości pola bez nazwy"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Nie można połączyć zgłoszenia z tym samym zgłoszeniem"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Niemożliwe połączenie w jedno zgłoszenie"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Niemożliwe jest zapisanie tego zapytania"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Niemożliwe określenie jednocześnie podstaw i celu"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Niemożliwe utworzenie użytkownika: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Zmień hasło"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Zaznacz wszystko"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Zaznacz pole, aby usunąć"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Zaznacz pole, aby odebrać uprawnienie"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Zgłoszenia podrzędne"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Miasto"
+
+#: NOT FOUND IN SOURCE
+msgid "Class"
+msgstr "Klasa"
+
+#: NOT FOUND IN SOURCE
+msgid "Class Name"
+msgstr "Nazwa klasy"
+
+#: NOT FOUND IN SOURCE
+msgid "Class is"
+msgstr "KlasÄ… jest"
+
+#: NOT FOUND IN SOURCE
+msgid "Classes"
+msgstr "Klasy"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Odznacz wszystko"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Zamknięte"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "Zamknięte zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Demandes closes"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Zamknięte zgłoszenia"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Niezrozumiałe polecenie!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Komentuj"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Adres komentarza"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Komentarz nie został zapisany"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Komentarz do zgłoszeń"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "CommentOnTicket"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Komentarze"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Komentarze (które nie zostaną wysłane zgłaszającym)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Komentarze (które nie zostały wysłane zgłaszającym)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Komentarze dotyczÄ…ce %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Uwagi dotyczące użytkownika"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Komentarze zostały dodane"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Potwierdzenie wysłane do zgłaszającego"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Ograniczenia kompilacji"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Warunek"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Warunek zgadza siÄ™ z ..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Warunek nie został odnaleziony"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Konfiguracja"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Potwierdź"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "ContactInfoSystem"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Nie udało się rozpoznać daty modyfikacji'%1'"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Zawartość"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Typ zawartości"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Kopiuj"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Korespondencja"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Adres korespondencyjny"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Korespondencja została dodana"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Korespondencja nie została zapisana"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Nie udało się wprowadzić nowej wartości pola dla zgłoszenia."
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr ""
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Nie udało się zmienić właściciela."
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Nie udało się utworzyć pola definiowanego przez użytkownika"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr ""
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Nie udało się utworzyć grupy"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Nie udało się utworzyć szablonu: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Nie udało się utworzyć zgłoszenia. Nie określono kolejki"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Nie udało się utworzyć użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Nie udało się wyszukać zgłoszenia o numerze %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Nie udało się wyszukać grupy %1."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Nie udało się wyszukać ani utworzyć tego użytkownika"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Nie udało się wyszukać tego zarządzającego"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Nie udało się wyszukać użytkownika %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Nie udało się załadować grupy"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Nie udało się przypisać tego zarządzającego jako %1 dla tej kolejki"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Nie udało się przypisać tego zarządzającego jako %1 dla tego zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Nie udało się usunąć tego zarządzającego jako %1 dla tej kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Nie udało się usunąć tego zarządzającego jako %1 dla tego zgłoszenia"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Niemożliwe było zapisanie danych użytkownika"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Nie udało się dodać nowego członka grupy"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Nie udało się utworzyć transakcji: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Nie udało się ustalić, co zrobić na podstawie odpowiedzi z gpg\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Nie udało się wyszukać grupy\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Nie udało się wyszukać wiersza"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Nie udało się wyszukać tego zarządzającego"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Nie udało się wyszukać tej wartości"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Nie udało się wyszukać użytkownika\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Nie udało się załadować %1 z bazy użytkowników.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "Nie udało się załadować klasy %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Nie udało się załadować pola %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "Nie udało się załadować pliku konfiguracji RT '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Nie udało się załadować skryptów"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Nie udało się załadować grupy %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Nie udało się załadować połączenia"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Impossible de charger l'objet %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Nie udało się załadować kolejki"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr " Nie udało się załadować kolejki %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Nie udało się załadować skryptu"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Nie udało się załadować szablonu"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Nie udało się załadować użytkownika (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Nie udało się załadować zgłoszenia '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Kraj"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Utwórz"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Utwórz zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a Class"
+msgstr "Utwórz klasę"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Utwórz pole definiowane przez użytkownika"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Utwórz pole dla kolejki %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Utwórz pole, które dotyczy wszystkich kolejek"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Utwórz nowe pole definiowane przez użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new article"
+msgstr "Utwórz nowy artykuł"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Utwórz nowy skrypt globalny"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Utwórz nową grupę"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Utwórz nową grupę prywatną"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Utwórz nową kolejkę"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Utwórz nowy skrypt"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Utwórz nowy szablon"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Utwórz nowe zgłoszenie"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Utwórz nowego użytkownika"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Utwórz kolejkę"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Utwórz kolejkę nazwaną"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Utwórz zgłoszenie"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Utwórz skrypt dla kolejki %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Utwórz szablon"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Utwórz zgłoszenie"
+
+#: NOT FOUND IN SOURCE
+msgid "Create an article"
+msgstr "Utwórz artykuł"
+
+#: NOT FOUND IN SOURCE
+msgid "Create an article in class..."
+msgstr "Utwórz artykuł w ramach klasy..."
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Utwórz nowe zgłoszenia na podstawie szablonu tego skryptu"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Utwórz zgłoszenie"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Utwórz zgłoszenia w tej kolejce"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Utwórz, usuń i zmodyfikuj pola def. przez użytkownika"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Utwórz, usuń i zmodyfikuj kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Utwórz, usuń i zmodyfikuj członków którejś z prywatnych grup użytkownika"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr " Utwórz, usuń i zmodyfikuj członków prywatnych grup"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Utwórz, usuń i zmodyfikuj użytkowników"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "CreateTicket"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Zarejestrowane"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Pole %1 zostało utworzone"
+
+#: NOT FOUND IN SOURCE
+msgid "Created by"
+msgstr "Utworzony przez"
+
+#: NOT FOUND IN SOURCE
+msgid "Created during"
+msgstr "Utworzony podczas"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Szablon %1 został utworzony"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Zgłaszający"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Aktualne powiÄ…zania"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Relationships"
+msgstr "Aktualne powiÄ…zania"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Aktualne skrypty"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Aktualni członkowie"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Aktualne uprawnienia"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Aktualne kryteria wyszukiwania"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Aktualni obserwatorzy"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Pole def. przez użytkownika #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Pola def. przez użytkownika"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Treść procedury czyszczenia definiowana przez użytkownika"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Treść procedury definiowana przez użytkownika"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Warunek definiowany przez użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Pole definiowane przez użytkownika %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Pole %1 definiowane przez użytkownika ma wartość."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Brak wartości w polu %1 definiowanym przez użytkownika."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Nie udało się wyszukać pola %1 definiowanego przez użytkownika"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Pole definiowane przez użytkownika zostało usunięte"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Nie udało się wyszukać pola definiowanego przez użytkownika"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Nie udało się wyszukać wartości %1 dla pola %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Wartość pola zmieniła się z %1 na %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Nie można usunąć wartości pola"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Nie udało się wyszukać wartości pola"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Wartość pola została usunięta"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "Pole"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Daty"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Gru."
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "Grudzień"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Domyślnie wybierany szablon odpowiedzi wysyłanej automatycznie"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Domyślnie wybierany szablon odpowiedzi wysyłanej automatycznie"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Domyślna kolejka"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Domyślny zgłaszający"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Domyślnie wybierany szablon komentarza administratora"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Domyślnie wybierany szablon korespondencji administratora"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Domyślnie wybierany szablon korespondencji"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Domyślnie wybierany szablon transakcji"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Domyślnie: %1/%2 zmieniane z \"%3\" na \"%4\""
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Przekaż uprawnienia"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Przekaż specyficzne uprawnienia, które zostały Ci przyznane"
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "DelegateRights"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Przekazywanie uprawnień"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Usuń"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Usuń szablon"
+
+#: NOT FOUND IN SOURCE
+msgid "Delete article #%1"
+msgstr "Usuń artykuł #%1"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Usuń zaznaczone skrypty"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Usuń zgłoszenia"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "DeleteTicket"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Usunięcie tego obiektu mogło spowodować brak spójności"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Usunięcie tego obiektu spowoduje brak spójności"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Usunięcie tego obiektu naruszy spójność"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Odrzucić"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Zgłoszenia zależne"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Zależności"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Zależność %1 dodana"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Zależność %1 usunięta"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Zależność od %1 dodana"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Zależność od %1 usunięta"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Zależy od"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "ZależyOd"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "MalejÄ…co"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Opis zgłoszenia"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Opis"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Szczegóły"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Wyświetl"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Wyświetl Listę Praw Dostępu"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Wybierz kolumny"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Wyświetl szablony skryptów dla tej kolejki"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Wyświetl skrypty dla tej kolejki"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Tryb wyświetlania"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Wyświetl zgłoszenie #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Rozpowszechniane na mocy wersji 2 licencji GNU GPL <a href=\"http://www.gnu.org/copyleft/gpl.html\">."
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Zrób cokolwiek i wszystko"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Nie odświeżaj tej strony."
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Nie wyświetlaj wyników wyszukiwania"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Pobierz "
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Pobierz jako listę [dane każdego użytkownika/grupy w jednej linii, odzielone tabulatorem]"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Termin realizacji"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Termin realizacji '%1' nie może być rozpoznany"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "BÅÄ„D: Nie udaÅ‚o siÄ™ zaÅ‚adować zgÅ‚oszenia: '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Edytuj"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Conditions"
+msgstr "Modifier les conditions"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Edytuj pola dla kolejki %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Custom Fields for Class %1"
+msgstr "Edytuj pola dla klasy %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Edycja zdefiniowanych przez użytkownia pól dla zgłoszeń we wszystkich kolejkach"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Edytuj powiÄ…zania"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Zmodyfikuj zapytanie"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Relationships"
+msgstr "Edytuj powiÄ…zania"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Edytuj szablony kolejki %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Edytuj skrypty"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Edytuj szablony systemowe"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Edytuj szablony dla %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "EdytujZapisaneZapytania"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for Class %1"
+msgstr "Edytuj konfiguracjÄ™ klasy %1"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Edytuj konfiguracjÄ™ kolejki %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Edytuj konfigurację użytkownika %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Edytuj pole %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Edytuj listę członków grupy %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Edytuj listę członków prywatnej grupy %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Edytuj szablon %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Podstawy lub cel muszą być określone"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "e-mail"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Używany adres e-mail "
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "adres e-mail"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "kodowanie e-maila"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled (Unchecking this box disables this Class)"
+msgstr "Udostępniona (nieoznaczenie tego pola spowoduje, że klasa będzie niedostępna)"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Udostępnione (nieoznaczenie tego pola spowoduje, że pole będzie niedostępne)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Udostępniona (nieoznaczenie tego pola spowoduje, że grupa będzie niedostępna)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Udostępniona (nieoznaczenie tego pola spowoduje, że kolejka będzie niedostępna)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Classes"
+msgstr "Dostępne klasy"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "Dostępne pola definiowane przez użytkownika"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Dostępne kolejki"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Dostępny status %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "Statut actif: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Enter Articles or URIs to link Articles to. Seperate multiple entries with spaces."
+msgstr "Aby powiązać artykuły, wprowadź artykuły lub URI oddzielone spacjami."
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Wprowadzanie wielu wartości"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Aby powiązać obiekty, wprowadź URI obiektów oddzielone spacjami."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Wprowadzanie jednej wartości"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Aby powiązać kolejki, wprowadź URI kolejek oddzielone spacjamii."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Aby powiązać zgłoszenia, wprowadź numery zgłoszeń lub URI oddzielone spacjami."
+
+#: NOT FOUND IN SOURCE
+msgid "Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces."
+msgstr "Aby powiązać zgłoszenia, wprowadź numery zgłoszeń lub URI oddzielone spacjami."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Saisir %1 valeurs maximum"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "BÅ‚Ä…d"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Błąd w określeniu parametrów kolejki->Dodaj Obserwatora"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Błąd w określeniu parametrów kolejki -> Usuń Obserwatora"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Błąd w określeniu parametrów kolejki -> Usuń Obserwatora"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Błąd w określeniu parametrów zgłoszenia -> Dodaj Obserwatora"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr " Błąd w określeniu parametrów zgłoszenia -> Usuń Obserwatora"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Błąd w określeniu parametrów Zgłoszenia-> Usuń Obserwatora"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Eskaluj zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Estimate"
+msgstr "Estimer"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Szacowane"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Wszyscy"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Przykład:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "Zewnętrzne AuthId"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "ExternalContactInfoId"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Informacje dodatkowe"
+
+#: NOT FOUND IN SOURCE
+msgid "Extract article from ticket #%1"
+msgstr "Wydziel artykuł ze zgłoszenia #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Extract article from ticket #%1 into class %2"
+msgstr "Wydziel artykuł ze zgłoszenia #%1 w klasie %2"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Nie udało się wyszukać członków grupy 'Uprawnieni'"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Nie udało się wyszukać członków grupy 'Nieuprawnieni'"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Nie udało się załadować modułu %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Lut."
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "Luty"
+
+# Nie jestem pewien: jedno słowo!
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Nazwa pliku"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Saisir dans plusieurs champs de type texte"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Saisir dans un champ de type texte"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Saisir dans %1 champs de type texte maximum"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Koniec"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Końcowy priorytet"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "Końcowy priorytet"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Wyszukaj grupę, której"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Wyszukaj grupy, których:"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Wyszykaj nowe/otwarte zgłoszenia"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Wyszukaj użytkowników, których"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Wyszukaj zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Zakończ zatwierdzanie"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Pierwsze"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Pierwsza strona"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Ble ble"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "ble ble"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "WymuÅ› zmianÄ™"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr ""
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Wyszukano %1 zgłoszeń"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Wyszukany obiekt"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "FormulaireLibre"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FreeformContactInfo"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "FreeformMultiple"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "FreeformSingle"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Pt."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Pełne nagłówki"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Pobierz szablon z pliku"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Uzyskanie aktualnego użytkownika na podstawie podpisu pgp\\n"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Nadany dla %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Globalna"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Skrypty globalne"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Szablon globalny: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Pobierz"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Start!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Poprawny podpis pgp dla %1\\n "
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Przejdź do strony"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Przejdź do zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Grand"
+msgstr "Accorder"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Grupa"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Grupa %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Uprawnienia grupowe"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Grupa ma już członka"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Nie udało się utworzyć grupy: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Grupa została utworzona"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Nie ma takiego członka grupy"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Nie udało się wyszukać grupy"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Nie udało się wyszukać grupy.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Grupa nie została określona.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Grupy"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Nie można przypisac grup jako członków tych grup"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr ""
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Witaj!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Witaj, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historia"
+
+#: NOT FOUND IN SOURCE
+msgid "History for article #%1"
+msgstr "Historia artykułu #%1"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "Historique du groupe %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "Historique de l'utilisateur %1"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Tel. domowy"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Start"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Posiadam %quant(%1,concrete mixer)."
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "I have [quant,_1,concrete mixer]."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Nr "
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identyfikacja użytkownika"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Jeśli potwierdzenie zostało odrzucone, odrzuć oryginał i usuń oczekujące potwierdzenia"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Jeśli zgłaszający nie został określony, twórz zgłoszenia dla tego użytkownika."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Jeśli kolejka nie została określona, twórz zgłoszenia w tej kolejce."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Jeśli ta aplikacja została zachwiana, obcy lokalny użytkownik mógł używać tej aplikacji, aby uzyskać do RT dostęp na prawach administratora"
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Jeśli zmodyfikowałeś coś powyżej"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Niedopuszczalna wartość dla %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Image"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Pole, które nie może być powielane"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled classes in listing."
+msgstr "Uwzględnij na liście nieaktywne klasy"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Uwzględnij na liście nieaktywne pola."
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Uwzględnij na liście nieaktywne grupy."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Uwzględnij na liście nieaktywne kolejki."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Uwzględnij w wyszukiwaniu nieaktywnych użytkowników."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "PoczÄ…tkowy priorytet"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "PoczÄ…tkowy priorytet"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Błąd na wejściu"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Błąd wewnętrzny"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Błąd wewnętrzny: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Nieprawidłowy typ grupy"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Nieprawidłowe uprawnienie"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "Type invalide"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Nieprawidłowe dane"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Nieprawidłowy właściciel. Domyślnie 'nikt'"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Nieprawidłowa kolejka"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Nieprawidłowe uprawnienie"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Nieprawidłowa wartość dla %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Nieprawidłowa wartość pola"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Nieprawidłowy status"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Bardzo ważne, aby nieuprawnieni użytkownicy nie posiadali uprawnień do uruchomienia tej aplikacji."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Sugeruje się, aby utworzyć nieuprawnionego użytkownika w odpowiedniej grupie o odpowiednim dostępie do RT, aby uruchomić tę aplikację"
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Wymaga kilku argumentów:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Pozycje oczekujÄ…ce na moje zatwierdzenie"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Sty."
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "Styczeń"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Dołącz albo opuść tę grupę"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Lip."
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "Lipiec"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Wszystko"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Cze."
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "Czerwiec"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "SÅ‚owo kluczowe"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Język"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Język"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Ostatnie"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Ostatnia modyfikacja"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Ostatnio modyfikowane"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Ostatnio powiadomiony"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Ostatnio zaktualizowane"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "OstAktualiz"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "OstAktPrzez"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Pozostały"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Udostępnij temu użytkownikowi RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Przydziel uprawnienia temu użytkownikowi"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Ograniczenie właściciela do %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Ograniczenie kolejki do %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Połączenie już istnieje"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Nie udało się utworzyć połączenia"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Połączenie zostało utworzone (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Połączenie zostało usunięte (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Połączenie nie zostało odnalezione"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Powiąż zgłoszenie #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "Lier au ticket %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "PowiÄ…zania"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Załaduj"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Załaduj zapisane zapytanie:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Załadowane moduły PERL"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Lokalizacja"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Ścieżka logowania %1 nie została odnaleziona lub nie mógła być zapisana.\\n Nie udało się uruchomić RT"
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Zalogowano jako %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Zaloguj siÄ™"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Wyloguj siÄ™"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Wprowadź właściciela"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Wprowadź status"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Wprowadź termin realizacji"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Wprowadź datę zamknięcia"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Wprowadź datę rozpoczęcia realizacji"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Wprowadź datę rozpoczęcia realizacji"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Wprowadź datę wpływu"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Wprowadź priorytet"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Wprowadź kolejkę"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Wprowadź temat"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Administracja polami zdefiniowanymi przez użytkownika "
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Administracja grupami i członkami grup"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Administracja właściwościami i konfiguracją, które dotyczą wszystkich kolejek"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Administracja kolejkami i specyficznymi właściwościami kolejek"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Administracja użytkownikami i hasłami"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Marzec"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Maj"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Maj"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Członek %1 dodany"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Członek %1 usunięty"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Członek grupy został dodany"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Członek grupy został usunięty"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Członek grupy nie został usunięty"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Członek grupy"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "CzłonekGrupy"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Członkowie grup"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "Członkowstwo w %1 dodane"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "Członkowstwo w %1 usunięte"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Affiliations"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "Affiliations de l'utilisateur %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Udało się połączyć"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Nie udało się połączyć. Nie udało się ustawić efektywnego Id"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Połączyć w"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Połączono z %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Wiadomość"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Wiadomość nie mogła zostać zapisana"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Wiadomość zapisana"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Powiadomienie o tym zgłoszeniu nie zostanie wysłane do..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Brakujący klucz główny?: %1:"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Tel. komórkowy"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Tel. komórkowy"
+
+#: NOT FOUND IN SOURCE
+msgid "Modified"
+msgstr "Zmieniony"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify"
+msgstr "Zmodyfikuj"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Modyfikuj Listę Praw Dostępu"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Modyfikuj pole %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Modyfikuj pola, które dotyczą wszystkich kolejek"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Zmodyfikuj uprawnienia grup"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Modyfikuj Członków"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Zapisz uprawnienia"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Modyfikuj szablony skryptów dla tej kolejki"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Modyfikuj skrypty dla tej kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Modyfikuj szablon %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Zmodyfikuj uprawnienia użytkowników"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Modyfikuj pole dla kolejki %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Modyfikuj pole, które dotyczy wszystkich kolejek"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Modyfikuj skrypt dla kolejki %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Modyfikuj skrypt, który dotyczy wszystkich kolejek"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify article #%1"
+msgstr "Modyfikuj artykuł #%1"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr ""
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Modyfikuj daty dla #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Modyfikuj daty dla zgłoszenia # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Modyfikuj globalne pola definiowane przez użytkownika"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Modyfikuj globalne uprawnienia grupowe"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Modyfikuj globalne uprawnienia grupowe"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "Modifier les droits globaux des groupes"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "Modifier les droits globaux des utilisateurs"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Modyfikuj globalne skrypty"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Modyfikuj globalne uprawnienia użytkowników"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Modyfikuj globalne uprawnienia użytkowników."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Modyfikuj grupowe dane pośrednie lub usuń grupę"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Modyfikuj uprawnienia grupowe dla pola %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Modyfikuj uprawnienia grupowe dla grupy %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Modyfikuj uprawnienia grupowe dla kolejki %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Modyfikuj listę członków tej grupy"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Modyfikuj konto w RT"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Modyfikuj użytkowników powiązanych z kolejką %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Modyfikuj użytkowników powiązanych ze zgłoszeniem %1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Modyfikuj skrypty dla kolejki %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Modyfikuj skrypty, które dotyczą wszystkich kolejek"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Edytuj szablon %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Modyfikuj szablony, które dotyczą wszystkich kolejek"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Modyfikuj grupÄ™ %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Modyfikuj obserwatorów kolejki"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Modyfikuj użytkownika %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Modyfikuj zgłoszenie %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Modyfikuj zgłoszenie %1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Modyfikuj zgłoszenia"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Modyfikuj uprawnienia użytkowników dla grupy %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Modyfikuj uprawnienia użytkowników dla kolejki %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Modyfikuj obserwatorów kolejki %1"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ModifyACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "ModifyOwnMembership"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "ModifyQueueWatchers"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ModifyScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "ModifySelf"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "ModifyTemplate"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "ModifyTicket"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Pon."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Więcej o %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Przesuń w dół"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Przesuń w górę"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Wielokrotny"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Należy określić atrybut 'Nazwa'"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "%1 moich zgłoszeń"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Moje potwierdzenia"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Moje potwierdzenia"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Moje zapytania"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Nazwa"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Używana nazwa"
+
+#: NOT FOUND IN SOURCE
+msgid "Name matches"
+msgstr "Nazwa zgadza siÄ™ z"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Wymagane potwierdzenie od administratora systemu"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Nigdy"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Nowe"
+
+#: NOT FOUND IN SOURCE
+msgid "New Article"
+msgstr "Nowy artykuł"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nowe powiÄ…zania"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nowe hasło"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nowe zgłoszenie oczekujące na zatwierdzenie"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Nowe zapytanie"
+
+#: NOT FOUND IN SOURCE
+msgid "New Relationships"
+msgstr "Nowe powiÄ…zania"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Nowe wyszukiwanie"
+
+#: NOT FOUND IN SOURCE
+msgid "New class"
+msgstr "Nowa klasa"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nowe pole def. przez użytkownika"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Nowa grupa"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nowe hasło"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Nowe hasło zostało wysłane"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Nowa kolejka"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Nouvelle demande"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nowe uprawnienia"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Nowy skrypt"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Nowe wyszukiwanie"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Nowy szablon"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Nowe zgłoszenie"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Nowe zgłoszenie nie istnieje"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Nowy użytkownik"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Nowy użytkownik został nazwany"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nowi obserwatorzy"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Nowe ustawienia okna"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Następne"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Następna strona"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Następna strona"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Pseudonim"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Pseudonim"
+
+#: NOT FOUND IN SOURCE
+msgid "No"
+msgstr "Nie"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Nie zdefiniowano klasy"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Brak pola definiowanego przez użytkownika"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Nie zdefiniowano pola def. przez użytkownika"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Nie zdefiniowano grupy"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Brak zapytania"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Nie zdefiniowano kolejki"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Nie odnaleziono użytkownika RT. Proszę skontaktować się z administratorem RT.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Brak szablonu"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Nie określono zgłoszenia. Odrzucenie zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "Nie określono zgłoszenia. Odrzucenie modyfikacji zgłoszenia\\n\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Zawieszenie operacji"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Nie określono kolumny"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Nie udało się wyszukać polecenia\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Nie wprowadzono komentarza dotyczącego tego użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Nie załączono korespondencji"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Brak opisu dla %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Nie określono grupy"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Brak załączonej wiadomości"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Nie ustawiono hasła"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Brak uprawnień do tworzenia kolejek"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Brak uprawnień do tworzenia zgłoszeń w kolejce '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Brak uprawnień do tworzenia użytkowników"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Brak uprawnień do wyświetlenia tego zgłoszenia"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Brak uprawnień do przeglądania aktualizacji zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Nie określono zarządzającego"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Nie wybrano zarzÄ…dzajÄ…cych."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Brak kolejek odpowiadajÄ…cych kryteriom wyszukiwania"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Nie udało się wyszukać uprawnień"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Nie przyznano uprawnień."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Brak kryteriów wyszukiwania"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Brak tematu"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Nie określono numeru zgłoszenia"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Nie określono typu transakcji"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Brak użytkowników odpowiadających kryteriom wyszukiwania"
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Brak aktualnego użytkownika RT. Proszę skontaktować się z administratorem RT.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Nie wysłano wartości do zestawu!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Nikt"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "NieistniejÄ…ce pole?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Nie udało się zalogować"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Nie udało się zalogować."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Nie ustawiona"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Jeszcze nie zaimplementowane."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Jeszcze nie zaimplementowane..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Uwagi"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Powiadomienie nie może zostać wysłane"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Wyślij powiadomienie do osób o uprawnieniach AdminCc"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Wyślij powiadomienie do osób o uprawnieniach AdminCc jako komentarz"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Wyślij powiadomienia do osób o uprawnieniach Cc"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Wyślij powiadomienia do osób o uprawnieniach Cc jako komentarz"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Wyślij powiadomienie do innych odbiorców"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Wyślij powiadomienie do innych odbiorców jako komentarz"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Wyślij powiadomienie do właściciela"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Wyślij powiadomienie do właściciela jako komentarz"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Powiadom Właściciela o odrzuceniu jego zgłoszenia"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Powiadom Zgłaszającego o zatwierdzeniu jego zgłoszenia przez wszystkich zatwierdzających"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Powiadom Zgłaszającego o zatwierdzeniu jego zgłoszenia przez niektórych zatwierdzających"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Wyślij powiadomienie do właścicieli i osób o uprawnieniach AdminCc o nowych pozycjach oczekujących na zatwierdzenie przez nich"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Wyślij powiadomienie do zgłaszających"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Wyślij powiadomienie do zgłaszających i osób o uprawnieniach Cc"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Wyślij powiadomienie do zgłaszających i osób o uprawnieniach Cc jako komentarz"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Wyślij powiadomienie do zgłaszających oraz osób o uprawnieniach Cc i AdminCc"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Wyślij powiadomienie do zgłaszających oraz osób o uprawnieniach Cc i AdminCC jako komentarz"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Lis."
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "Listopad"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr ""
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Nie udało się utworzyć obiektu"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Obiekt został utworzony"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Le type d'objet ne correspond pas"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Paź."
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "Październik"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Praca zdalna"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Zdalne modyfikacje"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Załaduj modyfikowane zdalnie"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "dnia"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "w przypadku komentarza"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "w przypadku korespondencji"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "w przypadku rejestracji zgłoszeń"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "w przypadku zmiany właściciela"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "w przypadku zmiany priorytetu"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "w przypadku zmiany kolejki"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "w przypadku zamykania zgłoszeń"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "w przypadku zmiany statusu"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "w przypadku transakcji"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Wyświetl tylko potwierdzenia dotyczące zgłoszeń zarejestrowanych po %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Wyświetl tylko potwierdzenia dotyczące zgłoszeń zarejestrowanych przed %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Otwarte"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "Otwórz zgłoszenia"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Otwórz"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Ouvrir les demandes"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Otwórz zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Otwórz zgłoszenia (z listy) w nowym oknie"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Otwórz zgłoszenia (z listy) w innym oknie"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Otwórz zgłoszenia, które są w trakcie ustalania (wymiany korespondencji)"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "UporzÄ…dkuj wg"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "PorzÄ…dkowanie i sortowanie"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Firma"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Zgłoszenie źródłowe: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Zarejestrowano wychodzącą wiadomość o komentarzu"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Zarejestrowano wiadomość wychodzącą"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Z upływem czasu priorytet wzrasta do"
+
+#: NOT FOUND IN SOURCE
+msgid "Overview"
+msgstr "PrzeglÄ…d"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Moje zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "OwnTicket"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Właściciel"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Właściciel zmienił się z %1 na %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Właściciel nie mógł być ustanowiony."
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Wymuszono zmianę właściciela z %1 na %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "Właścicielem"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Strona %1 z %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Pager"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Nr pager'a"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Zgłoszenia nadrzędne"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Hasło"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Przypomnienie o haśle"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Hasło jest zbyt krótkie"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Hasło: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Hasła są różne."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Hasła są różne. Twoje hasło nie zostało zmienione."
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Osoby"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Wykonaj operację zdefiniowaną przez użytkownika"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Konfiguracja PERL"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Odmowa dostępu"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Grupy prywatne"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Grupy prywatne"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Grupy prywatne:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Numery tel."
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Placeholder"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Właściwości"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Właściwości"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Informacja o przygotowaniu została wysłana do zgłaszającego"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Poprzednie"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Poprzednia strona"
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Poprzednia strona"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Poprzedni"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Nie udało się wyszukać zarządzającego %1."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Priorytet"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Priorytet rozpoczyna siÄ™ od"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Prywatne:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Uprawnieni"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Uprawniony status: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Uprawnieni użytkownicy"
+
+#: NOT FOUND IN SOURCE
+msgid "Projects"
+msgstr "Projets"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Grupa do użytku wewnętrznego"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "Zapytanie"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Kreator zapytań"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Kolejka "
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Nie udało się wyszukać kolejki %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Nie udało się wyszukać kolejki '%1 '\\n"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Nazwa kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Skrypty kolejki"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Kolejka już istnieje"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Nie udało się utworzyć kolejki"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Nie udało się załadować kolejki"
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Kolejka została utworzona"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Kolejka nie została określona."
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Nie udało się wyszukać kolejki"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Kolejki"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Szybkie wyszukiwanie"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Szybkie tworzenie zgłoszeń"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS (XML)"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 dla %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 wyprodukowany przez <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Administracja RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "BÅ‚Ä…d autoryzacji w RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "Odbicie zgłoszenia przez RT: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "BÅ‚Ä…d konfiguracji RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "Błąd krytyczny RT. Nie udało się zapisać wiadomości!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "BÅ‚Ä…d RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT otrzymał e-mail (%1) z RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service"
+msgstr "Samoobsługa RT"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "Zmienne RT"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT - przeglÄ…d podstawowych informacji"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "Nieudana autoryzacja w RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "Nie udało się wyszukać zgłaszającego przez przejrzenie zewnętrznej bazy"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "Nie udało się wyszukać kolejki: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "Nie udało się zatwierdzić tego podpisu PGP.\\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT dla %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT dla %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT wykonał twoje polecenia"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Jest dystrybuowane jako <a href=\"http://www.gnu.org/copyleft/gpl.html\">Wersja 2 GNU General Public License.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "Wg RT ta wiadomość może być informacją odbitą"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT będzie postępował z tą wiadomością tak, jakby była niepodpisana.\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "Email w RT wymaga autoryzacji podpisu PGP. Albo nie podpisałeś wiadomości, albo Twój podpis nie może być zweryfikowany."
+
+#: NOT FOUND IN SOURCE
+msgid "RTFM Error"
+msgstr "BÅ‚Ä…d RTFM"
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "ImiÄ™ i nazwisko"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "RealName"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "PowiÄ…zanie %1 dodane"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "Powiązanie %1 usunięte"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "PowiÄ…zanie z %1 dodane"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "Powiązanie z %1 usunięte"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Zgłoszenia powiązane"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "PowiÄ…zane z"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "PowiÄ…zaneZ"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Popraw"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Wprowadź szczegółowe kryteria wyszukiwania"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Odświeżaj tę stronę co %1 minut."
+
+#: NOT FOUND IN SOURCE
+msgid "Relationships"
+msgstr "PowiÄ…zania"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Usuń AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Usuń Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Usuń zgłaszającego"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Odpowiedz"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Adres odpowiedzi"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Odpowiedz zgłaszającym"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Odpowiedz na zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "ReplyToTicket"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Zgłaszający"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Adres e-mail zgłaszającego"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Zgłaszający"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "AdresZgłaszającego"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Zgłaszający"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Zgłoszenia powinny być zrealizowane w ciągu"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Wymagany atrybut '%1' nie został określony"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Zresetuj"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Tel. domowy"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Zamknij"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Zamknij zgłoszenie nr %1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Zamknięte"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Odpowiedź dla zgłaszających"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Wyniki"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Wyniki - liczba na stronÄ™"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Wpisz ponownie hasło"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Przywróć"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Uprawnienie %1 nie zostało wyszukane dla %2 %3 w zakresie %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Uprawnienie zostało przekazane"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Uprawnienie zostało przyznane"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Uprawnienie zostało załadowane"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Uprawnienie nie może być odebrane"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Nie udało się wyszukać uprawnienia"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Nie udało się załadować uprawnienia"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Uprawnienie zostało odebrane"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Uprawnienia"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Nie udało się przydzielić uprawnień dla %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Nie udało się odebrać uprawnień dla %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Role"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "RootApproval"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Linii na stronie"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sob."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Zapisz"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Zapisz zmiany"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Zapisz"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Zapisz zmiany"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Zapisane zapytania"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Skrypt #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Skrypt został utworzony"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Atrybuty skryptu"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Skrypt został usunięty"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Skrypty"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Skrypty dla %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Skrypty, które dotyczą wszystkich kolejek"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Wyszukaj"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Kryteria wyszukiwania"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+# nie jestem pewien!
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Wyszukaj oczekujÄ…ce potwierdzenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Search for articles"
+msgstr "Wyszukaj artykuły"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr ""
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Zabezpieczenie:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Zobacz dosłowne wiadomości wychodzące i ich odbiorców"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Zobacz prywatne komentarze do zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Zobacz podsumowania zgłoszeń"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "DostDoPólUżytk"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "SeeQueue"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "Selectionner"
+
+#: NOT FOUND IN SOURCE
+msgid "Select a Class"
+msgstr "Wybierz klasÄ™"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Wybierz pole definiowane przez użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "Select a Custom Fields"
+msgstr "Wybierz pole definiowane przez użytkownika"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Wybierz grupÄ™"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Wybierz kolejkÄ™"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr ""
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Wybierz użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "Select class"
+msgstr "Wybierz klasÄ™"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Wybierz pole"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Wybór pól zdefiniowanych przez użytkownika dla zgłoszeń we wszystkich kolejkach"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Wybierz grupÄ™"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Wybór wielu wartości"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Wybór jednej wartości"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Wybierz kolejkÄ™"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Wybierz skrypt"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Wybierz szablon"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Choisir un maximum de %1 valeurs "
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Wybierz użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "Wybór wielu wartości"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "Wybór jednej wartości"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Zaznaczone pola"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr ""
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Samoobsługa"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Wyślij e-mail do wszystkich obserwatorów"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Wyślij e-mail do wszystkich obserwatorów jako \"komentarz\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Wyślij e-mail do zgłaszających i osób o uprawnieniach Cc"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Wyślij e-mail do zgłaszających i osób o uprawnieniach Cc jako komentarz"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Wysyła wiadomość do zgłaszających"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Wysyła e-mail do osób wymienionych w Cc i Bcc "
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Wysyła e-mail do użytkowników o uprawnieniach CC"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Wysyła e-mail jako komentarz do użytkowników o uprawnieniach CC"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Wysyła e-mail do osób wymienionych w administracyjnym Cc"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Wysyła e-mail do osób wymienionych w administracyjnym Cc jako komentarz"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Wysyła e-mail do właściciela"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Wrz."
+
+#: NOT FOUND IN SOURCE
+msgid "Seperate multiple URLs with spaces"
+msgstr "Oddziel spacjami adresy URL"
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "Wrzesień"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Pokaż"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Pokazuj potwierdzenia"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Wyświetlane kolumny"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Wyświetl wyniki"
+
+#: NOT FOUND IN SOURCE
+msgid "Show advanced search options..."
+msgstr "Wyświetl zaawansowane opcje wyszukiwania..."
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Wyświetl zatwierdzone zgłoszenia"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Wyświetl podstawowe informacje"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Wyświetl odrzucone zgłoszenia"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Wyświetl szczegóły"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Wyświetl zgłoszenia oczekujące na zatwierdzenie"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Wyświetl zgłoszenia oczekujące na pozostałe zatwierdzenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Wyświetl prywatny komentarz do zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Wyświetl streszczenia zgłoszeń"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "ShowACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "PodglÄ…dPocztyWychodz"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "PodglądZapytańZapis"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "ShowScrips"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "ShowTemplate"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "ShowTicket"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "ShowTicketComments"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Podpisz jako zgłaszający lub osoba o uprawnieniach Cc dla zgłoszenia lub kolejki"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Podpisz jako osoba o uprawnieniach AdminCc dla zgłoszenia lub kolejki"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Podpis"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "Wpisany jako %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Pojedynczy"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr ""
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Pomiń menu"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sortuj"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort Order"
+msgstr "Kryterium sortowania"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Klucz sortowania"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Sortuj wyniki wg"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "SortOrder"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "Etap"
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Zamrożone"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Strona powitalna"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Realizacja rozpoczęta"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Data rozpoczęcia realizacji '%1' nie może być rozpoznana"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "PoczÄ…tek realizacji"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "PoczÄ…tek realizacji od"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Data rozpoczęcia realizacji '%1' nie może być rozpoznana"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Województwo"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Status "
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Zmiana statusu"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Status zmieniony z %1 na %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "Zmiana statusu"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Przejmij"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Przejmij zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "PrzejecieZgloszen"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Przejęte od %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Przejęte od %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr ""
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Temat"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Temat został zmieniony na %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Zastosuj"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Query"
+msgstr "Wykonaj zapytanie"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Wprowadź obieg (Workflow)"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Powiodło się"
+
+#: NOT FOUND IN SOURCE
+msgid "Summary"
+msgstr "Streszczenie"
+
+#: NOT FOUND IN SOURCE
+msgid "Summary matches"
+msgstr "Streszczenie zgadza siÄ™ z"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Nie."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "SuperUser"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "System"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Konfiguracja systemu"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "BÅ‚Ä…d systemowy"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Narzędzia systemowe"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Błąd systemowy. Uprawnienie nie zostało przekazane"
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Błąd systemowy. Uprawnienie nie zostało przydzielone"
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Grupy systemowe"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "Grupa ról systemowych do użytku wewnętrznego"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Przyjmij"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Przyjmij zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "PrzyjęcieZgłoszeń"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Przyjęty"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Szablon"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Szablon #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Szablon został usunięty"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Nie udało się wyszukać szablonu"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Nie udało się wyszukać szablonu\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Szablon nie został rozpoznany"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Szablony"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Szablony dla %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "Texte"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "To już jest aktualna wartość"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "To nie jest wartość tego pola"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "To jest ta sama wartość"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Ten użytkownik/ grupa już dysponują tym prawem"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Ten zarządzający jest już %1 dla tej kolejki"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Ten zarządzający jest już %1 dla tego zgłoszenia"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Ten zarzÄ…dzajÄ…cy nie jest %1 dla tej kolejki"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Ten zarządzający nie jest %1 dla tego zgłoszenia"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Ta kolejka nie istnieje"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "To zgłoszenie ma niewykonane zgłoszenia zależne"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Ten użytkownik już posiada to uprawnienie"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Ten użytkownik już jest właścicielem tego zgłoszenia"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Taki użytkownik nie istnieje"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Ten użytkownik już posiada to uprawnienie"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Ten użytkownik nie jest uprawniony"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Ten użytkownik jest już uprawniony"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Ten użytkownik jest teraz nieuprawniony"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "Cet utilisateur a perdu ses droits"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Ten użytkownik może nie posiadać zgłoszeń w tej kolejce"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "To nie jest id typu liczbowego"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Podstawowe informacje"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "CC zgłoszenia"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "Administracyjne CC zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Komentarz został zapisany"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Następujące polecenie wyszuka wszystkie aktywne zgłoszenia w kolejce głównej i ustawi ich priorytet na 99, jeśli nie zostały zmodyfikowane w ciągu ostatnich 4 godzin:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "Następujące polecenia nie zostały wykonane:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Ustawiono nową wartość."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Właściciel zgłoszenia"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Zgłaszający"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Te komentarze nie są generalnie widoczne dla użytkownika"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Ta funkcja jest dostępna tylko dla administratora systemu"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Ta wiadomość zostanie wysłana do..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "To zgłoszenie %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Ta aplikacja pozwala użytkownikowi na przypadkowe uruchomienie tzw. perl modułów z RT"
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Nie określono zawartości tej transakcji"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "%1 zgłoszeń tego użytkownika o najwyższym priorytecie"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "25 zgłoszeń tego użytkownika o najwyższym priorytecie"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Czw."
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Zgłoszenie nr %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "Ticket n°%1 Jumbo update: %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Pełna aktualizacja: %2 zgłoszenia nr %1"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Zgłoszenie nr %1 %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "Zgłoszenie %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Zgłoszenie %1 zostało utworzone w kolejce '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Zgłoszenie %1 zostało załadowane\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Zgłoszenie %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr ""
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Historia zgłoszenia nr %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Nr zgłoszenia"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Zgłoszenie zamknięte"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Załącznik do zgłoszenia, którego"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Zawartość zgłoszenia"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Typ zawartości zgłoszenia"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Nie udało się utworzyć zgłoszenia z powodu wewnętrznego błędu"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Zgłoszenie zostało utworzone"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Nie udało się utworzyć zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Zgłoszenie zostało usunięte"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Nie udało się wyszukać numeru zgłoszenia"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Meta-dane zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Nie udało się wyszukać zgłoszenia"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Zmienił się status zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Obserwatorzy zgłoszenia"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Zgłoszenia %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Zgłoszenia %1 do %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Zgłoszenia od %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Zgłoszenia, które zależą od tego zatwierdzenia:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Szacowany czas"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Pozostały czas"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Czas realizacji"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Pozostały czas"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Wyświetlany czas"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Czas realizacji"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "PozostałyCzas"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "Czas realizacji"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Aby wygenerować różnice tego zatwierdzenia:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "Aby wygenerować różnice tego zatwierdzenia:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Więcej o wsparciu, szkoleniach, rozwoju i licencjonowaniu dostępne jest w %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Wpłynęło"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Narzędzia"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transakcja"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transakcja %1 została wyczyszczona"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transakcja została utworzona"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Transakcja-> Nie udało się utworzyć, ponieważ nie określono numeru zgłoszenia"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transakcje nie są możliwe do powielenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Próba usunięcia uprawnienia: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Wto."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Typ"
+
+#: NOT FOUND IN SOURCE
+msgid "Unable to load article"
+msgstr "Nie udało się załadować artykułu"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Niezaimplementowane"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr " Unix login"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Nazwa użytkownika typu 'unix'"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Nieznany Typ Zawartości %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Nieograniczona"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Zapytanie bezimienne"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Nieuprawnieni"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Niezaznaczone Pola"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Niepobrany"
+
+#: NOT FOUND IN SOURCE
+msgid "Untitled search"
+msgstr "Recherche sans titre"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Zaktualizuj"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Zaktualizuj wszystko"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Zaktualizuj ID"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Zaktualizuj zgłoszenie"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Zaktualizuj typ"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Zaktualizuj wszystkie te zgłoszenia jednocześnie"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Zaktualizuj e-mail"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Zaktualizuj wiele zgłoszeń jednocześnie"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Zaktualizuj nazwÄ™"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Aktualizacja nie została zapisana."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Zaktualizuj wybrane zgłoszenia"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Zaktualizuj podpis"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Zaktualizuj zgłoszenie"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Zaktualizuj zgłoszenie nr %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Zaktualizuj zgłoszenie nr %1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Zaktualizuj zgłoszenie nr %1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Aktualizacja nie dotyczyła korespondencji ani komentarza."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Zaktualizowane"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Zapisz"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Uploader plusieurs fichiers"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Uploader plusieurs images"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Uploader un fichier"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Uploader une image"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Uploader un maximum de %1 fichiers"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Uploader un maximum de %1 images"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Zapisz swoje zmiany"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Use the dropdown menus to select which transactions you want to extract into a new RTFM article"
+msgstr "Użyj rozwijalnych list, aby wybrać transakcje, z których chcesz utworzyć nowy artykuł RTFM"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Użytkownik %1 %2: %3 \\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Hasło użytkownika %1: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "Nie można znaleźć użytkownika %1."
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Nie udało się wyszukać użytkownika '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Nie udało się wyszukać użytkownika '%1'\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Definiowany przez użytkownika"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Warunki i operacje zdefiniowane przez użytkownika"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "Id"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Id"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Uprawnienia użytkowników"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Nie udało się utworzyć użytkownika: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Użytkownik został utworzony"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Grupy def. przez użytkownika"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Użytkownik załadowany"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Użytkownik został powiadomiony"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Widok użytkownika"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Grupy zdefiniowane przez użytkownika"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Nazwa"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Użytkownicy"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Użytkownicy odpowiadający kryteriom wyszukiwania"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Sprawdź zapytanie"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "Wartość kolejki"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Wartości"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Obserwuj"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "WatchAsAdminCc"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Obserwatorzy"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "Web-kodowanie"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Åšro."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Po zatwierdzeniu zgłoszenia przez wszystkich zatwierdzających, dodaj korespondencję do oryginalnego zgłoszenia"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Po zatwierdzeniu zgłoszenia przez któregokolwiek z zatwierdzających, dodaj korespondencję do oryginalnego zgłoszenia"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Po utworzeniu zgłoszenia"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Po utworzeniu zgłoszenia podlegającego zatwierdzeniu, powiadom właściciela i osoby o uprawnieniach AdminCc o zadaniu oczekującym zatwierdzenie przez nich"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Jeśli cokolwiek się wydarzy"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Zawsze gdy zgłoszenie będzie zamykane"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Zawsze gdy zmieni się właściciel zgłoszenia"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Zawsze gdy zmieni się priorytet zgłoszenia"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Zawsze gdy zmieni się kolejka zgłoszenia"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Zawsze gdy zmieni się status zgłoszenia"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Zawsze gdy wystąpi warunek definiowany przez użytkownika"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Zawsze gdy wystÄ…piÄ… komentarze"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Zawsze gdy wpłynie korespondencja"
+
+#: NOT FOUND IN SOURCE
+msgid "Which are referred to by "
+msgstr "Które są zgłoszeniami powiązanymi"
+
+#: NOT FOUND IN SOURCE
+msgid "Which refer to"
+msgstr "Które dotyczą"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Praca"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Praca zdalna"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Tel. do pracy"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Czas pracy"
+
+#: NOT FOUND IN SOURCE
+msgid "XXX CHANGEME You are not an authorized user"
+msgstr "XXX CHANGEME Nie jesteś użytkownikiem z uprawnieniami"
+
+#: NOT FOUND IN SOURCE
+msgid "Yes"
+msgstr "Tak"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Jesteś już właścicielem tego zgłoszenia"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Nie jesteś autoryzowanym użytkownikiem"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Możesz ponownie przydzielić tylko te zgłoszenia, których jesteś właścicielem lub te, które nie mają właściciela"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "Nie masz uprawnień do przeglądania tego zgłoszenia.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Wyszukałeś zgłoszenia %1 w kolejce %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Zostałeś wylogowany z RT"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Nie masz uprawnień do rejestrowania zgłoszeń w tej kolejce."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Nie możesz rejestrować zgłoszeń w tej kolejce."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Proszę zalogować się ponownie"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Twoje zgłoszenia %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "Administrator RT niewłaściwie skonfigurował aliasy maila, które wywołują RT"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Twoje zgłoszenie zostało zatwierdzone przez %1. Może nadal oczekiwać na inne zatwierdzenia."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Twoje zgłoszenie zostało zatwierdzone."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Twoje zgłoszenie zostało odrzucone"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Twoje zgłoszenie zostało odrzucone"
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Nazwa użytkownika lub hasło jest nieprawidłowe"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Kod pocztowy"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[Pas de sujet]"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "and is not"
+msgstr "i nie jest"
+
+#: NOT FOUND IN SOURCE
+msgid "and not"
+msgstr "i nie"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "tak jak przydzielone dla %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "zamknięte"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "zawiera"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "zawartość"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "typ zawartości"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "korespondencja (prawdopodobnie) nie została wysłana"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "korespondencja została wysłana"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "dniami"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "usuń"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "usunięte"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "nie zgadza siÄ™ z"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "nie zawiera"
+
+#: NOT FOUND IN SOURCE
+msgid "email address"
+msgstr "adresse email"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "równy"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr ""
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "faux"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "nazwa pliku"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "większy od"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "grupy '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "godz."
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "Numer"
+
+#: NOT FOUND IN SOURCE
+msgid "in class %1"
+msgstr "w ramach klasy %1"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "jest"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "nie jest"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "mniejszy od"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "zgadza siÄ™ z"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min."
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minuty"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "modyfikacje\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "miesiÄ…cami"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "nowe"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "bez nazwy"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "brak wartości"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "żaden"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "różny od"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "necontientpas"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "otwarte"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "prywatna grupa '%1' użytkownika '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "kolejka %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "odrzucone"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "zamknięte"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sek."
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "Arkusz kalk."
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "zamrożone"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "system %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "grupy systemowej '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "przywoływany komponent nie określił powodu"
+
+#: NOT FOUND IN SOURCE
+msgid "ticket #%1"
+msgstr "ticket n°%1"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "zgłoszenie #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "till"
+msgstr "do"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "vrai"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "nieopisana grupa %1"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "nieopisana grupa %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "użytkownika %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "tygodniami"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "wg szablonu %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "rokiem/latami"
+
diff --git a/rt/lib/RT/I18N/pt_br.po b/rt/lib/RT/I18N/pt_br.po
new file mode 100644
index 0000000..2690953
--- /dev/null
+++ b/rt/lib/RT/I18N/pt_br.po
@@ -0,0 +1,6531 @@
+# $Id: pt_br.po,v 1.1.1.8 2008-03-02 04:10:34 ivan Exp $
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.5.x\n"
+"POT-Creation-Date: 2002-05-02 11:36+0800\n"
+"PO-Revision-Date: 2005-10-03 13:51-0400\n"
+"Last-Translator: Gustavo Chaves <gustavo@cpqd.com.br>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 adicionado"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 atrás"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 alterado para %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 removido"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 of group %3"
+msgstr "%1 %2 do grupo %3"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 com modelo %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 este tíquete\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr ""
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "%1 - %2 apresentados"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Um argumento para passar para %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Mostra atualizações de estado no STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Especifica o módulo de ação que você quer usar"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Especifica o módulo de condição que você quer usar"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Especifica o módulo de busca que você quer usar"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScripAction %1 carregado"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 usado como um valor de %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "Aliases %1 requerem um TicketId no qual trabalhar"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "Aliases %1 requerem um TicketId no qual trabalhar "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "Aliases %1 requerem um TicketId no qual trabalhar (de %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 parece ser um objeto local, mas não pode ser encontrado no banco de dados"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 por %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 alterado de %2 para %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr ""
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 não pôde ser alterado para %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 não pôde iniciar uma transação (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 não pôde alterar estado para resolvido. O banco de dados do RT pode estar inconsistente."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr ""
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "%1 tíquetes de mais alta prioridade que eu possuo..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "%1 tíquetes de mais alta prioridade que eu requeri..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 é uma ferramenta para modificar tíquetes a partir de uma ferramenta de agenda externa, como o cron."
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 não é mais um %2 para esta fila."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 não é mais um %2 para este tíquete."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 não é mais um valor para o campo personalizado %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 não é um identificador de fila válido."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 min"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 não mostrado"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 direitos"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 teve sucesso\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "Tipo %1 desconhecido para $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "Tipo %1 desconhecido para %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 was created without a CurrentUser\\n"
+msgstr "%1 foi criado sem um CurrentUser\\n"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 resolverá todos os membros de um grupo de tíquetes resolvidos."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 colocará como pendente uma BASE [local] se for dependente [ou membro] de uma requisição ligada."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr ""
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: nenhum arquivo anexo especificado"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1b"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' é um valor inválido para o estado"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' não é uma ação reconhecida."
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(Assinale para remover o membro do grupo)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Assinale para remover o scrip)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Assinale para remover)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(Assinale para remover)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr ""
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Se deixado em branco, será entendido como %)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(Sem Valor)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Nenhum campo personalizado)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Sem membros)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Sem scrips)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Nenhum esquema)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Envia uma cópia-cega (Bcc) desta atualização para uma lista de endereços de email separados por vírgula. <b>Não</b> altera quem vai receber atualizações futuras.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Envia uma cópia-cega (Bcc) desta atualização para uma lista de endereços eletrônicos separados por vírgulas. <b>Não</b> altera o destinatário de atualizações futuras.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Envia uma cópia-cega (Bcc) desta atualização para uma lista de endereços eletrônicos separados por vírgulas. <b>Não</b> altera o destinatário de atualizações futuras.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Envia uma cópia-cega (Bcc) desta atualização para uma lista de endereços eletrônicos separados por vírgulas. <b>Não</b> altera o destinatário de atualizações futuras.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Envia uma cópia desta atualização para uma lista de endereços eletrônicos separados por vírgulas. <b>Não</b> altera o destinatário de atualizações futuras.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Envia uma cópia desta atualização para uma lista de endereços eletrônicos separados por vírgulas. Estas pessoas <b>receberão</b> as atualizações futuras.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr ""
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(vazio)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(nenhum nome listado)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(Sem assunto)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(sem valor)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr ""
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(somente um tíquete)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(aguardando aprovação)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(aguardando outros tíquetes)"
+
+#: NOT FOUND IN SOURCE
+msgid "(requestor's group)"
+msgstr "(grupo do requisitante)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(requerido)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(sem título)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr ""
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "25 tíquetes mais prioritários que possuo..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "25 tíquetes mais prioritários que requisitei..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Novo tíquete em\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "Um modelo vazio"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Deleted"
+msgstr "ACE Removida"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Loaded"
+msgstr "ACE Carregada"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be deleted"
+msgstr "ACE não pôde ser removida"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be found"
+msgstr "ACE não pode ser encontrada"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE não encontrado"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACEs só podem ser criados e removidos."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Abortando para evitar modificações indesejadas no tíquete.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Sobre mim"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Controle de acesso"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Ação"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "Ação %1 não encontrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "Ação confirmada."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Ação preparada..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr ""
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Adicionar AdminCc"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Adicionar Cc"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr ""
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr ""
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Adicionar Mais Arquivos"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Next State"
+msgstr "Adicionar Próximo Estado"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Adicionar Requisitante"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip to this queue"
+msgstr "Adicionar um Scrip nesta fila"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip which will apply to all queues"
+msgstr "Adicionar um Scrip que será aplicado a todas as filas"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a keyword selection to this queue"
+msgstr "Adicionar uma seleção de teclado a esta fila"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Adicionar um novo scrip global"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Adicionar um scrip a esta fila"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Adicionar um scrip que se aplicará a todas as filas"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Adicionar comentários ou respostas aos tíquetes selecionados"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Adicionar membros"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Adicionar novos observadores"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "AddNextState"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Principal adicionado como um %1 para esta fila"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Principal adicionado como um %1 para este tíquete"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Endereço 1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Endereço 2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin Cc"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Comentário do Administrador"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Correspondência do Administrador"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Administração de filas"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Administração de usuários"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Administração da configuração global"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Administração de Grupos"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "Administração de uma fila"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "AdminAllPersonalGroups"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "AdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "AdminComment"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "AdminCorrespondence"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "AdminCustomFields"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "AdminGroup"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "AdminGroupMembership"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "AdminOwnPersonalGroups"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "AdminQueue"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "AdminUsers"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Cc Administrativo"
+
+#: NOT FOUND IN SOURCE
+msgid "Admins"
+msgstr "Administradores"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "Busca avançada"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Depois"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "Idade"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Alias for"
+msgstr "Alias para"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Todos os Campos Personalizados"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Todas as filas"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Sempre envia uma mensagem para os requisitantes independentemente do remetente"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr ""
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr ""
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Aprovação"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Aprovação #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Aprovação #%1: Notas não registradas devido a um erro de sistema"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Aprovação #%1: Notas registradas"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Detalhes da Aprovação"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr ""
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Diagrama da aprovação"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Aprove"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Notas do aprovador: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Abr."
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "Abril"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Ascendente"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Anexar"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Anexar arquivo"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Arquivo anexado"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Arquivo anexo '%1' não pôde ser carregado"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Arquivo anexo criado"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Nome do arquivo anexo"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Arquivos anexos"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr ""
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Ago."
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "Agosto"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "Sistema de autenticação"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Autoreply"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Autoreply para Requisitantes"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "AutoreplyToRequestors"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "Assinatura PGP inválida: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Identificador de arquivo anexo inválido. Não pude encontrar o arquivo '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "Dados inválidos em %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Número inválido de transação para o arquivo anexo. %1 deveria ser %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Básicos"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Bcc"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Não se esqueça de salvar suas alterações"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Antes"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "Incício da Aprovação"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Vazio"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "URL para guardar esta busca em seus marcadores"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr ""
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Cabeçalhos resumidos"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "Atualização de tíquetes em lote"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Não posso modificar os usuários do sistema"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Este principal pode ver esta fila"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Não posso adicionar um valor de campo personalizado sem um nome"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr ""
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Não posso ligar um tíquete a ele mesmo"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Não posso unir a um tíquete já unido. Você nunca deve obter este erro"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr ""
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr ""
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Não especifique origem e destino simultaneamente"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Não posso criar o usuário: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr ""
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Cc"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Mudar a senha"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr ""
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Assinale para remover"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Assinalar para revogar o direito de acesso"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Filhos"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Cidade"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr ""
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Fechado"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Requisições fechadas"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Code"
+msgstr "Código"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Comando não entendido!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Comentário"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Endereço de Comentário"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Comentário não registrado"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Comente sobre os tíquetes"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "CommentOnTicket"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Comentários"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Comentários (não enviados aos requisitantes)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Comentários (não enviados aos requisitantes)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Comentários sobre %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Comentários sobre este usuário"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Comentários adicionados"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Compilar restrições"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Condição"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Condição satisfeita..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Condição não encontrada"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Configuração"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Confirmar"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "Informação de contato"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Data de contato '%1' não pôde ser entendida"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Conteúdo"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Coould not create group"
+msgstr "Não pude criar o grupo"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr ""
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Correspondência"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "Endereço de correspondência"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Correspondência adicionada"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "Correspondência não registrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Não pude adicionar novo valor de campo personalizado para o tíquete. "
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "Não pude adicionar novo valor de campo personalizado para o tíquete. %1"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr ""
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Não pude alterar o proprietário. "
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Não pude criar CampoPersonalizado"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr ""
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Não pude criar o grupo"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Não pude criar o modelo: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Não pude criar o tíquete. Fila não selecionada"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Não pude criar o usuário"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "Não pude criar um observador para o requisitante"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Não pude encontrar um tíquete com identificador %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Não pude encontrar o grupo %1."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Não pude encontrar ou criar o usuário"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Não pude encontrar este principal"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Não pude encontrar o usuário %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Não pude carregar o grupo"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Não pude fazer este principal um %1 para esta fila"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Não pude fazer este principal um %1 para este tíquete"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Não pude remover este principal como um %1 para esta fila"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Não pude remover este principal como um %1 para este tíquete"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Não pude adicionar o membro no grupo"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Não pude criar uma transação: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Não sei o que fazer com a resposta do gpg\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Não encontrei o grupo\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Não pude encontrar o registro"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Não encontrei este principal"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Não encontrei este valor"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "Não pude encontrar este observador"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Não pude encontrar o usuário\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Não pude carregar %1 do banco de dados de usuários.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load KeywordSelects."
+msgstr "Não pude carregar os KeywordSelects."
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "Não pude carregar o arquivo de configuração do RT '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Não pude carregar os Scrips."
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Não pude carregar o grupo %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Não pude carregar a ligação"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr ""
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Não pude carregar a fila"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Não pude carregar a fila %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Não pude carregar o scrip"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Não pude carregar o modelo"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Não pude carregar este usuário (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Não pude carregar o tíquete '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "País"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Criar"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Criar Tíquetes"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Criar um CampoPersonalizado"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Criar um Campo Personalizado para a fila %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Criar um Campo Personalizado para todas as filas"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Criar um novo Campo Personalizado"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global Scrip"
+msgstr "Criar um novo Scrip global"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Criar um novo scrip global"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Criar um novo grupo"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Criar um novo grupo pessoal"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Criar uma nova fila"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Criar um novo scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Criar um novo modelo"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Criar um novo tíquete"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Criar um novo usuário"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Criar uma fila"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Criar uma fila chamada"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Criar uma requisição"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Criar um scrip para a fila %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Criar um modelo"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "Criação falhou: %1 / %2 / %3 "
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "Criação falhou: %1/%2/%3"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Criar novos tíquetes baseados no esquema deste scrip"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Criar um tíquete"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Criar tíquetes nesta fila"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Criar, remover e modificar campos personalizados"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Criar, remover e modificar filas"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Criar, remover e modificar os membros dos grupos pessoais de qualquer usuário"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Criar, remover e modificar os membros de grupos pessoais"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Criar, remover e modificar usuários"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "CreateTicket"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Criado"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "CampoPersonalizado %1 criado"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Modelo %1 criado"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr ""
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Relações atuais"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Scrips correntes"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Membros atuais"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Direitos de acesso atuais"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Critério de busca atual"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Observadores atuais"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Campo Personalizado #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Campos Personalizados"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Código de finalização da ação customizada"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Código de preparação da ação customizada"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Condição customizada"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Campo personalizado %1 %2 %3"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "O campo personalizado %1 tem um valor."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "O campo personalizado %1 não tem valor."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Campo personalizado %1 não encontrado"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Campo personalizado removido"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Campo personalizado não encontrado"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "O valor de campo %1 não pôde ser encontrado para o campo personalizado %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "O valor do campo personalizado foi alterado de %1 para %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "O valor do campo personalizado não pôde ser removido"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "O valor de campo personalizado não pôde ser encontrado"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Valor do campo personalizado removido"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr ""
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "Erro de dado"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Datas"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Dez."
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "Dezembro"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Esquema Padrão de Autoresposta"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Esquema padrão de Autoresposta"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr ""
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr ""
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Esquema padrão de comentário administrativo"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Esquema padrão de correspondência administrativa"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Esquema padrão de correspondência"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Esquema padrão de transação"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Padrão: %1/%2 mudou de %3 para %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Delegar direitos de acesso"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Delegar direitos específicos que foram outorgados a você."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "DelegateRights"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Delegação"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Remover"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Remover tíquetes"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "DeleteTicket"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Ao remover este objeto você pode quebrar a integridade referencial"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Ao remover este objeto você quebra a integridade referencial"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Ao remover este objeto você viola a integridade referencial"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "Remover este objeto violaria a integridade referencial"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity. That's bad."
+msgstr "Remover este objeto violaria a integridade referencial. Isto é mau."
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Negue"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Dependem deste tíquete"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "Dependências: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Depende de"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "DependsOn"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Descendente"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Descreva o problema abaixo"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Descrição"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "Detalhes"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Apresentação"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Mostrar Lista de Controle de Acesso"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Mostras os esquemas de Scrip para esta fila"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Mostrar os Scrips para esta fila"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Modo de apresentação"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Apresentar o tíquete #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr ""
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Fazer qualquer coisa"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Não recarregar esta página."
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Não mostrar resultados da busca"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Baixar"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr ""
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Vencido"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "A data de vencimento '%1' não pôde ser entendida"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "ERRO: Não pude carregar o tíquete '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Editar"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Conditions"
+msgstr "Editar Condições"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Editar Campos Personalizados para %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Editar Relacionamentos"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Editar Esquemas para a fila %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "Editar palavras chave"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Editar scrips"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Editar os modelos do sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Editar os modelos para %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Editando a configuração para a fila %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Editando a configuração para o usuário %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Editando o campo %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Editando os membros do grupo %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Editando os membros do grupo pessoal %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Editando o modelo %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Você deve especificar a origem ou o destinatário"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Email"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "O endereço de email já está em uso"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "Correio Eletrônico"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "Codificação de Email"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Habilitado (Deselecionando este ítem desabilita este campo personalizado)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Habilitado (Deselecionando este ítem desabilita este grupo)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Habilitado (desassinalando desabilita esta fila)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "Campos Personalizados Habilitados"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Filas Habilitadas"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Estado %1 habilitado"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Entre com múltiplos valores"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Entre com um valor"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr ""
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Entre com identificadores de tíquete ou URIs que levam ao tíquete. Separe entradas múltiplas com espaços."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr ""
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Erro"
+
+#: NOT FOUND IN SOURCE
+msgid "Error adding watcher"
+msgstr "Erro ao adicionar um observador"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Erro nos parâmetros para Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Erro nos parâmetros para Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Erro nos parâmetros para Ticket->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Erro nos parâmetros para Ticket->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr ""
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr ""
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Todos"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Exemplo:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "ExternalAuthId"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "ExternalContactInfoId"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Informação adicional"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Não pude encontrar o pseudogrupo de usuários 'Privileged'."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Não pude encontrar o pseudogrupo de usuários 'Unprivileged'"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Falhou ao carregar o módulo %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Fev."
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "Fevereiro"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Fin"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Prioridade Final"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "FinalPriority"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Encontrar o grupo cujo"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Encontrar tíquetes novos/abertos"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Encontrar pessoas que"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Encontrar tíquetes"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Terminar Aprovação"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Primeiro"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "Primeira página"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Force alteração"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr ""
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Encontrado %quant(%1,tíquete)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Objeto Encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FreeformContactInfo"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "FreeformMultiple"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "FreeformSingle"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Sex."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Cabeçalhos completos"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Obtendo o usuário corrente a partir de uma assinatura pgp\\n"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Dado a %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Global Keyword Selections"
+msgstr "Seleções de Palavras Chave Globais"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Scrips Globais"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Esquema global: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr ""
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Ir!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "Assinatura pgp válida de %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Ir para a página"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Ir para o tíquete"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Grupo"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Grupo %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Direitos de Acesso do Grupo"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "O grupo já tem um membro"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "O grupo não pôde ser criado."
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "O grupo não pôde ser criado: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Grupo criado"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "O grupo não contém este membro"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Grupo não encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Grupo não encontrado.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Grupo não especificado.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Grupos"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Grupos não podem ser membros de seus próprios membros"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr ""
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr ""
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Olá!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Olá, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Histórico"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr ""
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Telefone Residencial"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Homepage"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Eu tenho %quant(%1,concrete mixer)."
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "Tenho [quant,_1,concrete mixer]."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Identificador"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identidade"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Se uma aprovação é rejeitada, rejeite a original e remova as aprovações pendentes"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr ""
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr ""
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Se esta ferramenta fosse setgid, um usuário local mal-intencionado poderia usá-la para obter acesso administrativo ao RT."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Se você alterou qualquer coisa acima, não se esqueça de"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Valor ilegal para %1"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Campo imutável"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Incluir campoas personalizados desabilitados na listagem."
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr ""
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Incluir filas desabilitadas na listagem."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Incluir usuários desabilitados na busca."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Prioridade Inicial"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "InitialPriority"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Erro de entrada"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Interest noted"
+msgstr "Interesse notado"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Erro Interno"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "Erro Interno: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Tipo Inválido de Grupo"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Direito Inválido"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "Tipo Inválido"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Dado inválido"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "Proprietário inválido. Usando 'nobody'."
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Fila inválida"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Direito de acesso inválido"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "Valor inválido para %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Valor inválido para o campo personalizado"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Valor inválido para o estado"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "É extremamente importante que usuários não privilegiados não possam executar esta ferramenta."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Sugere-se que você crie um usuário UNIX não privilegiado com o grupo e acesso RT corretos para executar esta ferramenta."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Requer vários argumentos:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "Itens requerendo minha aprovação"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan."
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "Janeiro"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Entre ou deixe este grupo"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Jul."
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "Julho"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Jumbo"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Jun."
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "Junho"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Palavra chave"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Líng"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Último"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Último Contato"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Contactado em"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "Notificado em"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Atualizado em"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "LastUpdated"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Resta"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Deixar este usuário acessar RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Deixar este usuário receber direitos de acesso adicionais"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Limitando proprietário a %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Limitando fila a %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "A ligação já existe"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "A ligação não pôde ser criada"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Ligação criada (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Ligação removida (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Ligação não encontrada"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Ligar o tíquete #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "Ligar o tíquete %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Ligações"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Localização"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "O diretório de log %1 não foi encontrado ou não pôde ser alterado.\\n RT não pode funcionar desta maneira."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "Assinado como %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Entrar"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Sair"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Definir como proprietário"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Definir o estado"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Definir o prazo final"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Definir a data de resolução"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Definir a data de iniciado"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Definir a data início"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Definir a data de última alteração"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Definir a prioridade"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Definir a fila"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Definir o assunto"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr ""
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Administrar grupos e seus membros"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Administrar propriedades e configurações aplicáveis a todas as filas"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Administrar filas e suas propriedades específicas"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Administrar usuários e senhas"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Março"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Maio"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Mai."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Membro adicionado"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Membro removido"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Membro não removido"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Membro de"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "MemberOf"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Membros"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr ""
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "União bem sucedida"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "União falhou. Não pude definir o EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Unir a"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr ""
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Mensagem"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr ""
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr ""
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Faltando uma chave primária?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Móvel"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Celular"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Modificar Lista de Controle de Acesso"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Modificar o campo personalizado %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Modificar Campos Personalizados que se aplicam a todas as filas"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr ""
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr ""
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Modificar esquemas de Scrip para esta fila"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Modificar Scrips para esta fila"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "Modificar ACLs do Sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Modificar Esquema %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr ""
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Modificar um Campo Personalizado para a fila %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Modificar um Campo Personalizado que se aplica a todas as filas"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Modificar um scrip para a fila %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Modificar um scrip aplicável a todas as filas"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "Modificar datas para # %1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Modificar as datas para #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Modificar as datas para o tíquete # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Modificar direitos de acesso globais de grupo"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Modificar direitos de acesso globais de grupo."
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "Modificar direitos globais para grupos"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "Modificar direitos globais para usuários"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Modificar scrips globais"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Modificar direitos de acesso globais de usuário"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Modificar direitos de acesso globais de usuário."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Modificar metadados do grupo ou removê-lo"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Modificar os direitos de acesso do grupo %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Modificar os direitos de acesso de grupo para a fila %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Modificar lista de membros deste grupo"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Modificar sua própria conta RT"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Modificar as pessoas relacionadas à fila %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Modificar as pessoas relacionadas ao tíquete #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Modificar os scrips da fila %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Modificar scrips aplicáveis a todas as filas"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Modificar o modelo %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Modificar esquemas que se aplicam a todas as filas"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr ""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Modificar o grupo %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Modificar os observadores da fila"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Modificar o usuário %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Modificar o tíquete # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Modificar o tíquete #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Modificar tíquetes"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr ""
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Modificar os direitos de acesso de usuário para o grupo %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Modificar os direitos de acesso de usuário para a fila %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Modificar os observadores para a fila '%1'"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ModifyACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "ModifyOwnMembership"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "ModifyQueueWatchers"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ModifyScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "ModifySelf"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "ModifyTemplate"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "ModifyTicket"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Seg."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Mais sobre %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Descer"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Subir"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Múltiplo"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "O atributo 'Name' deve ser especificado"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Minhas Aprovações"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Minhas aprovações"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Nome"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Nome em uso"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Precisa de aprovação do administrador do sistema"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Nunca"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Novo"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Novos Relacionamentos"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nova Senha"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nova Aprovação Pendente"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Nova busca"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Novo campo personalizado"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Novo grupo"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nova senha"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Notificação de nova senha enviada"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Nova fila"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Nova requisição"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Novos direitos de acesso"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Novo scrip"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Nova busca"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Novo esquema"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "O novo tíquete não existe"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Novo usuário"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Novo usuário chamado"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Novos observadores"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Abrir nova janela"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Próximo"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Próxima página"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "Apelido"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Apelido"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Não há Campo Personalizado"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Não há Grupo definido"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr ""
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Não há Fila definida"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Nenhum usuário RT foi encontrado. Favor consultar o administrador do RT.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Não há Modelo"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "Não há Tíquete especificado. Abortando o tíquete "
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "Não há Tíquete especificado. Abortando modificações no tíquete\\n\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Não há ação"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Não há coluna especificada"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Comando não encontrado\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Não há comentário sobre este usuário"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "Não há nenhum arquivo anexado"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Não há descrição para %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Não há grupo especificado"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Não há senha especificada"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Não há permissão para criar filas"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Sem permissão para criar tíquetes na fila '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Sem permissão para criar usuários"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Sem permissão para mostrar o tíquete"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr ""
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "sem permissão para ver modificar o tíquete"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Não há principal especificado"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Não há principal selecionado."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Não há fila satisfazendo o critério de busca."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Nenhum direito encontrado"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Nenhum direito outorgado."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr ""
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Não há busca a realizar"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Não há identificador de tíquete especificado"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Não há tipo de transação especificada"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "Não há usuário ou endereço de email especificado"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Nenhum usuário satisfazendo o critério de busca foi encontrado."
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Nenhum usuário RT válido foi encontrado. O tratador de CVS do RT está desabilitado. Por favor, consulte o administrador do RT.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Nenhum valor enviado a _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Ninguém"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Campo inexistente?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Não logado"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Não entrou."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Não definido"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Ainda não implementado."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Ainda não implementado..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Notas"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "A notificação não pôde ser enviada"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Notificar AdminCcs"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Notificar AdminCcs como Comentário"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr ""
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr ""
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Notificar Outros Destinatários"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Notificar Outros Destinatários como Comentário"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Notificar Proprietário"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Notificar Proprietário como Comentário"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr ""
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr ""
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr ""
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Notificar Proprietários e AdminCcs sobre novos itens pendendo suas aprovações"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Notificar Requisitantes"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Notificar Requisitantes e Ccs"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Notificar Requisitantes e Ccs como Comentário"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Notificar Requisitantes, Ccs e AdminCcs"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Notificar Requisitantes, Ccs e AdminCcs como Comentário"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "Novembro"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr ""
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objeto não pôde ser criado"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objeto criado"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr ""
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Out."
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "Outubro"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr ""
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr ""
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr ""
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Em"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr ""
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Sobre Comentário"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Sobre Correspondência"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Sobre Criação"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Sobre Mudança de Propriedade"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr ""
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Sobre Mudança de Fila"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Sobre Resolução"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Sobre Mudança de Estado"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Sobre Transação"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Só mostrar aprovações para requisições criadas depois de %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Só mostrar aprovações para requisições criadas antes de %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Aberto"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Abrir"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Requisições abertas"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Abrir tíquetes (da listagem) em uma nova janela"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Abrir tíquetes (da listagem) em outra janela"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Abrir tíquetes na correspondência"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "Requisitando e ordenando"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organização"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Tíquete originador: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr ""
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Após a data, a prioridade tende a"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Próprios tíquetes"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "OwnTicket"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Proprietário"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "Proprietário mudou de %1 para %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Proprietário alterado à força de %1 para %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "O proprietário é"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Pager"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Telefone do Pager"
+
+#: NOT FOUND IN SOURCE
+msgid "Parent"
+msgstr "Pai"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Pais"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Senha"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Lembrete de Senha"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Senha muito curta"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Senha: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr ""
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr ""
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Pessoas"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Realizar uma ação definida pelo usuário"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Permissão Negada"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Grupoas Pessoais"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Grupos pessoais"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Grupos pessoais:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefones"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Preferências"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr ""
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "Prefs"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr ""
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Anterior"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "Página anterior"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Pri"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Principal %1 não encontrado."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioridade"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "A prioridade inicia em"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr ""
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilegiado"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Estado privilegiado: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Usuários privilegiados"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Falso-grupo para uso interno"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr ""
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Fila"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Fila %1 não encontrada"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "A fila '%1' não foi encontrada\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Keyword Selections"
+msgstr "Seleções de Palavras-chave da Fila"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Nome da Fila"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Scrips da Fila"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "A fila já existe"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "A fila não pôde ser criada"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "A fila não pôde ser carregada"
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Fila criada"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "A fila não foi especificada."
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Fila não encontrada"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Filas"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr ""
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr ""
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 para %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 por <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Direitos reservados 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Direitos reservados 1996-2002 Jesse Vincent <jesse\\\\@bestpractical.com>\\\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "Adiministração do RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "Erro de autenticação no RT."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "Ricocheteio do RT: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "Erro de configuração do RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "Erro crítico no RT. A mensagem não foi registrada!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "Erro no RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "O RT recebeu email (%1) dele próprio."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Recieved mail (%1) from itself."
+msgstr "O RT recebeu email (%1) de si próprio."
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "Auto-serviço do RT / Tíquetes Fechados"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr ""
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT por alto"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "O RT não pôde autenticá-lo"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "O RT não pôde encontrar o requisitante através de consulta ao banco de dados externo"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "O RT não pôde encontrar a fila: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "O RT não pôde validar esta assinatura PGP. \\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT para %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT para %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "O RT processou seus comandos"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT tem &copy; Direitos Reservados 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. Ele é distribuído sob a <a href=\"http://www.gnu.org/copyleft/gpl.html\">Versão 2 da Licença Pública Geral GNU (GPL).</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-2002 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT tem &copy; Direitos Reservados 1996-%1 por Jesse Vincent &lt;jesse@bestpractical.com&gt;. Ele é distribuído sob a <a href=\\\"http://www.gnu.org/copyleft/gpl.html\\\">Versão 2 da Licença Pública Geral GNU (GPL).</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "O RT crê que esta mensagem seja um ricochete"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "O RT vai processar esta mensagem como se não fosse assinada.\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "O modo de comandos por email do RT requer autenticação PGP. Ou você não assinou sua mensagem ou sua assinatura não pôde ser verificada."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Nome real"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "Nome real"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr ""
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Referenciado por"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Faz referência a"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "RefersTo"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "Refinar"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "Refinar a Busca"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Recarregar esta página a cada %1 minutos."
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr ""
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr ""
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr ""
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Remover AdminCc"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Remover Cc"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Remover Requisitante"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Responder"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr ""
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Responder aos tíquetes"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "ReplyToTicket"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Requisitante"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Endereço eletrônico do requisitante"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Requisitante(s)"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "RequestorAddresses"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Requisitantes"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "A requisições vencem em"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr ""
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Restaurar"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Residência"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Resolver"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Resolver tíquete #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Resolvido"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Resposta aos requisitantes"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Resultados"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Resultados por página"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Confirmar a Senha"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Direito de acesso %1 não encontrado para %2 %3 referente a %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Direito de Acesso Delegado"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Direito de Acesso Outorgado"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Direito de Acesso Carregado"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Direito de acesso não pôde ser revogado"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Direito de acesso não encontrado"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Direito de acesso não carregado."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Direito de acesso revogado"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Direitos de Acesso"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Direitos de acesso não puderam ser outorgados a %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Direitos de acesso não puderam ser revogados de %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Papéis"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "RootApproval"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr ""
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Sáb."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr ""
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Salvar as Alterações"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Salvar as alterações"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip Criado"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip removido"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Scrips para %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrips aplicáveis a todas as filas"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Buscar"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Critérios de Busca"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr ""
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Buscar por aprovações"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr ""
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr ""
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Segurança:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "SeeQueue"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr ""
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Selecionar um grupo"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Selecionar uma fila"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr ""
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Selecionar um usuário"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Selecionar um campo personalizado"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr ""
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Selecionar um grupo"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Selecionar múltiplos valores"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Selecionar um valor"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Selecionar uma fila"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Selecionar um scrip"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Selecionar um esquema"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr ""
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Selecionar um usuário"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "SelectMultiple"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "SelectSingle"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr ""
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "Auto-serviço"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Enviar mensagem a todos os observadores"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Enviar mensagem a todos os observadores como um \"comentário\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Enviar mensagem aos requisitantes e Ccs"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Enviar mensagem aos requisitantes e Ccs como um comentário"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Envia uma mensagem aos requisitantes"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Envia uma mensagem aos Ccs e Bccs explicitamente listados"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr ""
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr ""
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Envia uma mensagem aos Ccs administrativos"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Envia uma mensagem aos Ccs administrativos como um comentário"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Envia uma mensagem ao proprietário"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Set."
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "Setembro"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr ""
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr ""
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Mostrar os Resultados"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Mostrar requisições aprovadas"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Mostrar o sumário"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Mostrar requisições negadas"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Mostrar os detalhes"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Mostrar requisições pendentes"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Mostrar requisições aguardando outras aprovações"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Mostrar comentário privado do tíquete"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Mostrar sumários do tíquete"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "ShowACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "ShowScrips"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "ShowTemplate"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "ShowTicket"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "ShowTicketComments"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Cadastrar como um Requisitante de tíquete ou um Cc de tíquete ou fila"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Cadastrar como um AdminCC de tíquete ou fila"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Assinatura"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "Assinado como %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Único"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr ""
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Saltar Menu"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr ""
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Chave de ordenação"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Ordenar os resultados por"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "Ordenação"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "Pendente"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "Página inicial"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Iniciado"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "A data de iníciado '%1' não pôde ser compreendida"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Inicia"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Inicia Por"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "A data de início '%1' não pôde ser compreendida"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Estado"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Estado"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Mudança de Estado"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Estado alterado de %1 para %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "StatusChange"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Roubar"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "Roubado de %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Roubado de %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr ""
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Assunto"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Assunto modou para %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Enviar"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Enviar Workflow"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Deu certo"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Dom."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "SuperUser"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Sistema"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr ""
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Erro do Sistema"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. Right not granted."
+msgstr "Erro de sistema. Direito não outorgado."
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. right not granted"
+msgstr "Erro de sistema. direito não outorgado"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr ""
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Erro do sistema. Direito de acesso não delegado."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Erro do sistema. Direito de acesso não outorgado."
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "Erro de sistema. Não posso outorgar direitos de acesso."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Grupos do sistema"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRolegroup para uso interno"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Tomar"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Tomado"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Modelo"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Esquema #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Esquema removido"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Modelo não encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Modelo não encontrado\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Modelo processado"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Modelos"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Modelos de %1\\n"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Este já é o valor atual"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Este não é um valor para este campo personalizado"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Este é o mesmo valor"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Este principal já é um %1 para esta fila"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Este principal já é um %1 para este tíquete"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Este principal não é um %1 para esta fila"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Este principal não é um %1 para este tíquete"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Esta fila não existe"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Este tíquete tem dependências não resolvidas"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Este usuário já tem este direito de acesso"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Este usuário já possui este tíquete"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Este usuário não existe"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Este usuário já tem privilégios"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Este usuário já não tem privilégios"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Este usuário agora tem privilégios"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Este usuário agora não tem privilégios"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "Este usuário agora é não privilegiado"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Este usuário não pode possuir tíquetes nesta fila"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Este não é um identificador numérico"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Sumário"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "O CC de um tíquete"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "O CC administrativo de um tíquete"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "O comentário foi registrado"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "O seguinte comando procurará por todos os tíquetes ativos na fila 'geral' e alterar sua prioridade para 99 se eles não tiverem sido alterados em 4 horas:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "Os seguintes comandos não foram processados:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "O novo valor foi atribuído."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "O proprietário de um tíquete"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "O requisitante de um tíquete"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Estes comandos geralmente não estão visíveis para o usuário"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr ""
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Este tíquete %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Esta ferramenta permite o usuário invocar módulos Perl arbitrários de dentro do RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Parece que esta transação não tem conteúdo"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Os %1 tíquetes mais prioritários deste usuário"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "Os 25 tíquetes de mais alta prioridade deste usuário"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Qui."
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket"
+msgstr "Tíquete"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "Tíquete # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "Tíquete # %1 atualização jumbo: %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Tíquete #%1 Atualização jumbo: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Tíquete #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Tíquete %1 criado na fila '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "Tíquete %1 carregado\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Tíquete %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr ""
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Histórico do Tíquete # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "Identificador do tíquete"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Tíquete Resolvido"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Arquivo anexo do tíquete"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Conteúdo do tíquete"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Tipo do conteúdo do tíquete"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "O tíquete não pôde ser criado devido a um erro interno"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "Tíquete criado"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "A criação do tíquete falhou"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "Tíquete removido"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Id de tíquete não encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "Tíquete destruído"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "Tíquete não encontrado"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "O estado do tíquete mudou"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Observadores do tíquete"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Tíquetes"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "Tíquetes %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "Tíquetes %1 por %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "Tíquetes de %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Tíquetes dependentes desta aprovação:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr ""
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Tempo Restante"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Tempo Trabalhado"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Tempo restante"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Tempo de apresentação"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Tempo trabalhado"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "TimeLeft"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "TimeWorked"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Para gerar as diferenças desta transação"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "Para gerar as diferenças desta transação:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Última atualização"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr ""
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr ""
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transação"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Transação %1 removida"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transação Criada"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "Transaction->Create não pôde, já que você não especificou um id de tíquete"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transações são imutáveis"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "Tentando remover um direito de acesso: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Ter."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Tipo"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Não implementado"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Usuário Unix"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Usuário Unix"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Codificação de conteúdo desconhecida %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Ilimitado"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr ""
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Não privilegiado"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Não tomado"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Atualizar"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Identificador de atualização"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr ""
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Tipo de atualização"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Atualizar todos estes tíquetes de uma vez"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Atualizar email"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Atualizar nome"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Atualização não registrada."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Atualizar os tíquetes selecionados"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Atualizar assinatura"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Atualizar o tíquete"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Atualizar o tíquete # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Atualizar o tíquete #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Atualizar tíquete #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "O tipo da atualização não foi nem correspondência e nem comentário."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Atualizado"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr ""
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr ""
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Usuário %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Usuário %1 Senha: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Usuário '%1' não encontrado"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Usuário '%1' não encontrado\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Definido pelo Usuário"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "Identificador de usuário"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Identificador do usuário"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Direitos de Acesso de Usuário"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "O usuário não pôde ser criado: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Usuário criado"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Grupos definidos pelo usuário"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Usuário notificado"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "Visualização de usuário"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Nome de usuário"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Usuários"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Usuários que satisfazem o critério de busca"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr ""
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "Valor da fila"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Valores"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Observar"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "WatchAsAdminCc"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "Observador carregado"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Observadores"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "Codificação de Web"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Qua."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Quando um tíquete for aprovado por todos os aprovadores, adicione uma correspondência ao tíquete original"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Quando um tíquete for aprovado por qualquer aprovador, adicione uma correspondência ao tíquete original"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Quando um tíquete é criado"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Quando um tíquete de aprovação é criado, notificar o Proprietário e o AdminCc do item aguardando sua aprovação"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Quando acontecer qualquer coisa"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Sempre que um tíquete for resolvido"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Sempre que mudar o proprietário de um tíquete"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr ""
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Sempre que um tíquete mudar de fila"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Sempre que o estado de um tíquete mudar"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Sempre que ocorrer uma condição definida por usuário"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Sempre que um novo comentário é adicionado"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Sempre que uma nova correspondência é adicionada"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Trabalho"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Telefone de trabalho"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Trabalhado"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Você já é proprietário deste tíquete"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Você não é um usuário autorizado"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Você só pode reatribuir seus próprios tíquetes ou aqueles que não têm dono"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "Você não tem permissão para ver este tíquete.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Você encontrou %1 tíquetes na fila %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Você foi desconectado do RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Você não tem permissão para criar tíquetes nesta fila."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Você não pode criar requisições nesta fila."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Volte sempre"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Suas %1 requisições"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "Seu administrador do RT configurou erradamente os endereços eletrônicos que invocam o RT"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Sua requisição foi aprovada por %1. Outras aprovações ainda podem estar pendentes."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Sua requisição foi aprovada."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Sua requisição foi rejeitada"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Sua requisição foi rejeitada."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Nome de usuário ou senha incorretos"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "CEP"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[sem assunto]"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr ""
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "como outorgado a %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr ""
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "contém"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "content"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "content-type"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "correspondência (provavelmente) não enviada"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "correspondência enviada"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "dias"
+
+#: NOT FOUND IN SOURCE
+msgid "dead"
+msgstr "morto"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "remover"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "removido"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "não satisfaz"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "não contém"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "igual a"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr ""
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr ""
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr ""
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr ""
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr ""
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "falso"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "filename"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "maior que"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "grupo '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr ""
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "horas"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "identificador"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "é"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "não é"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "menor que"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "satisfaz"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "minutos"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "modificações\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "meses"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "novo"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "sem valor"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "nenhum"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "diferente de"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "diferente"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "aberto"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "grupo pessoal '%1' para o usuário '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "fila %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "rejeitado"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "resolvido"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "seg"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr ""
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "pendente"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "sistema %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "grupo do sistema '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "o componente chamador não especificou por que"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "tíquete #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "verdadeiro"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "grupo %1 não descrito"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "grupo sem descrição %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "usuário %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "semanas"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "com modelo %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "anos"
+
diff --git a/rt/lib/RT/I18N/ru.po b/rt/lib/RT/I18N/ru.po
new file mode 100644
index 0000000..14f5c3b
--- /dev/null
+++ b/rt/lib/RT/I18N/ru.po
@@ -0,0 +1,6737 @@
+# translation of Request Tracker en.po to Russian
+#
+#
+# Andrew Kornilov <akornilov@gmail.com>, 2004, 2005, 2006.
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.6.x\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: (null)\n"
+"PO-Revision-Date: 2006-12-19 13:54+0200\n"
+"Last-Translator: Andrew Kornilov <akornilov@gmail.com>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr " %1 удалена."
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr " %1 переименована в %2."
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr " %1 Ñохранена."
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "&#8470;"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr ""
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr ""
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 добавлен"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 назад"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 изменено на %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 удален"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¾Ð¼ %3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 Ñта заÑвка\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) от %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (Ðе менÑÑ‚ÑŒ)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "%1 - %2 показано"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - Ðргумент Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ в %2"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr ""
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr ""
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr ""
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr ""
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr ""
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr ""
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "%1 СкриплетÐаДейÑтвие загружен"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1 добавлено как значение Ð´Ð»Ñ %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "%1 пÑевдонимы требуют идентификатор заÑвки Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "%1 пÑевдонимы требуют идентификатор заÑвки Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "%1 пÑевдонимы требуют идентификатор заÑвки Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ над (от %2) %3"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 ÑвлÑетÑÑ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ñ‹Ð¼ объектом, но не найден в базе данных"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 пользователем %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 изменена Ñ %2 на %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 копиÑ"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 невозможно уÑтановить в %2."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 не может инициировать транзакцию (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 не может изменить ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ð° Решено. Возможно, база данных RT иÑпорчена."
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 Ñоздана"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 удалена"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 grouped by %2"
+msgstr "%1 Ñгруппированные по %2"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "%1 ваших заÑвок Ñ Ð½Ð°Ð¸Ð²Ñ‹Ñшими приоритетами"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "%1 ваших заÑвок Ñ Ð½Ð°Ð¸Ð²Ñ‹Ñшими приоритетами..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "%1 заÑвок Ñ Ð½Ð°Ð¸Ð²Ñ‹Ñшими приоритетами, Ñозданных вами..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 больше не ÑвлÑетÑÑ %2 Ð´Ð»Ñ Ñтой очереди."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 больше не ÑвлÑетÑÑ %2 Ð´Ð»Ñ Ñтой заÑвки."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 больше не ÑвлÑетÑÑ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸ÐµÐ¼ Ð´Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ Ð¿Ð¾Ð»Ñ %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 неверный идентификатор очереди."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 matches"
+msgstr "%1 Ñовпадений"
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 мин"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 поÑледних неназначенных заÑвок"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "%1 не отображаетÑÑ"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 объектов"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "Права %1"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 уÑпешно произведено\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "%1 тип не извеÑтен Ð´Ð»Ñ $MessageId"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "%1 тип не извеÑтен Ð´Ð»Ñ %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 was created without a CurrentUser\\n"
+msgstr "%1 была Ñоздана без ТекущегоПользователÑ\\n"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 решит вÑе заÑвки, входÑщие в групповой запроÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "%1 приоÑтановит заÑвки, которые завиÑÑÑ‚ от запроÑа или включены в него"
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1 %2 объектов"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1 %2 %3 объектов"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "%1 Ñохраненных запроÑов"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: без вложений"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1б"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1к"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' ÑвлÑетÑÑ Ð½ÐµÐ²ÐµÑ€Ð½Ñ‹Ð¼ значением ÑтатуÑа"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1' не ÑвлÑетÑÑ Ð·Ð°Ñ€ÐµÐ³Ð¸Ñтрированным дейÑтвием."
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(Выделите пользователей Ð´Ð»Ñ Ð¸ÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð· группы)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(Выделите Ñкриплеты Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Выделите пункты Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(Выделите пункты Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Выделите получателей Ð´Ð»Ñ Ð¸ÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð· ÑпиÑка уведомлÑемых)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Выделите получателей Ð´Ð»Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð² ÑпиÑок уведомлÑемых)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Введите идентификаторы или ÑÑылки на заÑвки, разделенные пробелами)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Enter ticket ids or URLs, seperated with spaces)"
+msgstr "(Введите идентификаторы или ÑÑылки на заÑвки, разделенные пробелами)"
+
+#: NOT FOUND IN SOURCE
+msgid "(If left blank, will default to %1"
+msgstr "(ЕÑли не заполнено, то по умолчанию равно %1"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(ЕÑли не заполнено, то по умолчанию равно %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(Ðет значениÑ)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Ðет дополнительных полей)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Ðет пользователей)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Ðет Ñкриплетов)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Ðет шаблонов)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Ðет)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Отправить Ñкрытую копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. СпиÑок получателей, которые в дальнейшем будут получать Ñкрытые копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ…, <b>не</b> будет изменен.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Отправить Ñкрытую копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. СпиÑок получателей, которые в дальнейшем будут получать Ñкрытые копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ…, <b>не</b> будет изменен.)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Отправить Ñкрытую копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. СпиÑок получателей, которые в дальнейшем будут получать Ñкрытые копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ…, <b>не</b> будет изменен.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Отправить копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. Эти получатели в дальнейшем <b>будут</b> получать копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ….)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Отправить копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. Эти получатели в дальнейшем <b>будут</b> получать копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ….)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(Отправить копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. СпиÑок получателей, которые в дальнейшем будут получать копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ…, <b>не</b> будет изменен.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(Отправить копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. СпиÑок получателей, которые в дальнейшем будут получать копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменении, <b>не</b> будет изменен.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Отправить копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. СпиÑок получателей, которые в дальнейшем будут получать копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ…, <b>не</b> будет изменен.)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(Отправить копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. Эти получатели в дальнейшем <b>будут</b> получать копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ….)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Отправить копию ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтом изменении на Ñти адреÑа [разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой]. Эти получатели в дальнейшем <b>будут</b> получать копии ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± изменениÑÑ….)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(ИÑпользуйте Ñти Ð¿Ð¾Ð»Ñ Ð¿Ñ€Ð¸ выборе 'Задано пользователем' Ð´Ð»Ñ ÑƒÑловий или дейÑтвий)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(КорреÑÐ¿Ð¾Ð½Ð´ÐµÐ½Ñ†Ð¸Ñ Ð½Ðµ будет отправлена)"
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(пуÑто)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no Summary)"
+msgstr "(нет Ñводной информации)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(имена не указаны)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no name)"
+msgstr "(нет имени)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(нет темы)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(нет значениÑ)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(нет значений)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(только одна заÑвка)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(в ожидании подтверждениÑ)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(в ожидании Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÑвÑзанных заÑвок)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(в ожидании других заÑвок)"
+
+#: NOT FOUND IN SOURCE
+msgid "(requestor's group)"
+msgstr "(группа автора заÑвки)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(требуетÑÑ)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(без названиÑ)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(гггг/мм/дд)"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I own..."
+msgstr "25 ваших заÑвок Ñ Ð½Ð°Ð¸Ð²Ñ‹Ñшими приоритетами..."
+
+#: NOT FOUND IN SOURCE
+msgid "25 highest priority tickets I requested..."
+msgstr "25 заÑвок Ñ Ð½Ð°Ð¸Ð²Ñ‹Ñшими приоритетами, Ñозданные ваши..."
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr ""
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Создать заÑвку в\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Создать заÑвку в очереди\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "ПуÑтой шаблон"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Пароль не был уÑтановлен, поÑтому пользователь не Ñможет зайти в ÑиÑтему."
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be deleted"
+msgstr "Ðевозможно удалить ACE"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be found"
+msgstr "Ðевозможно найти ACE"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE не найден"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACEÑ‹ можно только Ñоздавать и удалÑÑ‚ÑŒ"
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "И"
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "Прекращаем работу во избежание нежелательного Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ñвки.\\n"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Личные наÑтройки"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Права доÑтупа"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "ДейÑтвие"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "ДейÑтвие %1 не найдено"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "ДейÑтвие зафикÑировано."
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "ДейÑтвие зафикÑировано.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr "ДейÑтвие ÑвлÑетÑÑ Ð¾Ð±Ñзательным аргументом"
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "ДейÑтвие подготовлено..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Добавить"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Добавить ÐдминиÑтративнуюКопию"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Добавить Копию"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Добавить колонки"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Добавить критерий"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Добавить еще файлы"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Next State"
+msgstr "Добавить Ñледующее ÑоÑтоÑние"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Добавить автора заÑвки"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Добавить значение"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip to this queue"
+msgstr "Добавить Ñкриплет Ð´Ð»Ñ Ñтой очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip which will apply to all queues"
+msgstr "Добавить Ñкриплет, который будет дейÑтвовать на вÑе очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "Добавить новый глобальный Ñкриплет"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "Добавить Ñкриплет Ð´Ð»Ñ Ñтой очереди"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Добавить Ñкриплет, который будет дейÑтвовать на вÑе очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "Добавить дополнительный критерий"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Добавить и начать поиÑк"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Добавить комментарии или ответы на выбранные заÑвки"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Добавить пользователей"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Добавить наблюдателей"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Добавить Ñти уÑÐ»Ð¾Ð²Ð¸Ñ Ð² ваш запроÑ"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "Добавить значениÑ"
+
+# Manual merge by Andrew Kornilov <andy@eva.dp.ua>
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Добавить, удалить или изменить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ñ… полей Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð¾Ð²"
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "ДобавлÑтьСледующееСоÑтоÑние"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Пользователь добавлен как %1 Ð´Ð»Ñ Ñтой очереди"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Пользователь добавлен как %1 Ð´Ð»Ñ Ñтой заÑвки"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "ÐдреÑ1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "ÐдреÑ2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "ÐдминиÑÑ‚Ñ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ ÐšÐ¾Ð¿Ð¸Ñ"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "ÐдминиÑтративный Комментарий"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "ÐдминиÑÑ‚Ñ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ ÐšÐ¾Ñ€Ñ€ÐµÑпонденциÑ"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Управление очередÑми"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "Управление пользователÑми"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Общие наÑтройки"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "Управление группами"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "ОÑновные параметры очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "ÐдминиÑтрироватьВÑеПерÑональныеГруппы"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "ÐдминиÑтративнаÑКопиÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "ÐдминиÑтрироватьКомментарий"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "ÐдминиÑтрироватьКорреÑпонденцию"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "ÐдминиÑтрироватьДополнительноеПоле"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "ÐдминиÑтрироватьДополнительныеПолÑ"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "ÐдминиÑтрироватьГруппу"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "ÐдминиÑтрироватьЧленÑтвоВГруппах"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "ÐдминиÑтрироватьСобÑтвенныеГруппы"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "ÐдминиÑтрироватьОчередь"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "ÐдминиÑтрироватьПользователей"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "ÐдминиÑÑ‚Ñ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ ÐšÐ¾Ð¿Ð¸Ñ"
+
+#: NOT FOUND IN SOURCE
+msgid "Admins"
+msgstr "ÐдминиÑтраторы"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Дополнительно"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "РаÑширенный поиÑк"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search Criteria"
+msgstr "Критерии раÑширенного поиÑка"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "ПоÑле"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "ВозраÑÑ‚"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Ðгрегатор"
+
+#: NOT FOUND IN SOURCE
+msgid "Alias"
+msgstr "ПÑевдоним"
+
+#: NOT FOUND IN SOURCE
+msgid "Alias for"
+msgstr "ПÑевдоним длÑ"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Ð’Ñе Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ñ‹"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "Ð’Ñе дополнительные полÑ"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Ð’Ñе очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "Ð’Ñегода отправлÑÑ‚ÑŒ ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð°Ð¼ заÑвок незавиÑимо от Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÐµÐ»Ñ ÑообщениÑ"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "И/Или"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "ПрименÑетÑÑ Ðº"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Применить"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Применить ваши изменениÑ"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "ПодтверждениÑ"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Подтверждение #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Подтверждение #%1: ÐŸÑ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð½Ðµ Ñохранены из-за ÑиÑтемной ошибки"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Подтверждение #%1: ÐŸÑ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñаны"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "Детали подтверждениÑ"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Подтверждение получено"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Подтверждение отклонено"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "Диаграмма подтверждений"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Подтвердить"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "ÐŸÑ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ð²ÑˆÐµÐ³Ð¾: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Ðпр."
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "Ðпрель"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "По возраÑтанию"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Ð’ порÑдке возраÑтаниÑ"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Ðазначение и удаление дополнительных полей"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "ÐазначатьДополнительныеПолÑ"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Вложение"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Вложить файл"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Вложенный файл"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Вложение '%1' не может быть загружено"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Вложение Ñоздано"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° Ð´Ð»Ñ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "ВложениÑ"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Ðтрибут удален"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Ðвг."
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "ÐвгуÑÑ‚"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "Тип региÑтрации"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Ðвтоответ"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Ðвтоответ Ðвторам ЗаÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "ÐвтоответÐвторамЗаÑвки"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "ДоÑтупно"
+
+#: NOT FOUND IN SOURCE
+msgid "Available Columns"
+msgstr "ДоÑтупные колонки"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ð¿Ð¾Ð´Ð¿Ð¸ÑÑŒ PGP: %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "Ðеверный идентификатор вложениÑ. Ðевозможно найти вложение '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ð´Ð°Ñ‚Ð° в %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "Ðеправильный номер транзакции Ð´Ð»Ñ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ. %1 должен быть %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "ОÑновное"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Ð¡ÐºÑ€Ñ‹Ñ‚Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Ðе забудьте Ñохранить наÑтройки"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "До"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "Ðачать подтверждение"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "Двоичный"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "ПуÑтой"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Жирный"
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "СÑылка на Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² избранное"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "СÑылка Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² избранное"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Сокращенные заголовки"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "МножеÑтвенное изменение"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "МножеÑтвенное изменение заÑвки"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Ðевозможно изменить ÑиÑтемных пользователей"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Может ли данный пользователь проÑматривать Ñту очередь"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Ðевозможно добавление Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ Ð¿Ð¾Ð»Ñ Ð±ÐµÐ· наименованиÑ"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "Ðевозможно найти клаÑÑ ÐºÐ¾Ð»Ð»ÐµÐºÑ†Ð¸Ð¸ Ð´Ð»Ñ '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Ðевозможно найти Ñохраненный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ð½Ð¸Ð¼"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Ðевозможно ÑвÑзать заÑвку Ñаму Ñ Ñобой"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "Ðевозможно объединить Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð½Ð¾Ð¹ заÑвкой (Ñта ошибка никогда не должна проиÑходить)."
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr "Ðевозможно Ñохранить %1"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Ðевозможно Ñохранить Ñтот запроÑ"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Ðевозможно указывать одновременно и иÑточник и Ð°Ð´Ñ€ÐµÑ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Ðевозможно Ñоздать пользователÑ: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "КатегориÑ"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "КопиÑ"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Изменить пароль"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Выбрать вÑе"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Выделите пункты Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Выделите права, которые хотите отозвать"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Потомки"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Выберите дату"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Город"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "ОчиÑтить вÑе"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "Закрыть окно"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Закрыто"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "Закрытые заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed requests"
+msgstr "Закрытые запроÑÑ‹"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Закрытые заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Code"
+msgstr "Код"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Комбинированный ÑпиÑок: Выберите или введите неÑколько значений"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Комбинированный ÑпиÑок: Выберите или введите одно значение"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Комбинированный ÑпиÑок: Выберите или введите до %1 значений"
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "Команда не раÑпознана!\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Комментировать"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "ÐÐ´Ñ€ÐµÑ Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸ÐµÐ²"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "Комментарий не запиÑан"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Комментарии заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "КомментироватьЗаÑвку"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "Комментарии"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Комментарии (Ðе отправлÑÑŽÑ‚ÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð°Ð¼ заÑвки)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Комментарии (Ðе отправлÑÑŽÑ‚ÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð°Ð¼ заÑвки)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "Комментарии о %1"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Комментарии об Ñтом пользователе"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Комментарии добавлены"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "Применить ограничениÑ"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "УÑловие"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "УÑловие ÑвлÑетÑÑ Ð¾Ð±Ñзательным аргументом"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "ПодходÑщее уÑловие..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "УÑловие не найдено"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "КонфигурациÑ"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Подтвердить"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "ÐšÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "Ðевозможно идентифицировать дату поÑледнего контакта '%1'"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Содержимое"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Тип-СодержаниÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Coould not create group"
+msgstr "Ðевозможно Ñоздать группу"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "КопиÑ"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "КорреÑпонденциÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "ÐÐ´Ñ€ÐµÑ Ð´Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÑпонденции"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "КорреÑÐ¿Ð¾Ð½Ð´ÐµÐ½Ñ†Ð¸Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð°"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "КорреÑÐ¿Ð¾Ð½Ð´ÐµÐ½Ñ†Ð¸Ñ Ð½Ðµ запиÑана"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "Ðевозможно добавить новое значение дополнительного Ð¿Ð¾Ð»Ñ Ð·Ð°Ñвки."
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "Ðевозможно добавить новое значение дополнительного Ð¿Ð¾Ð»Ñ Ð·Ð°Ñвки. %1"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Ошибка Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ полÑ"
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Ошибка Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ полÑ. %1"
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Ðевозможно изменить ответÑтвенного."
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Ðевозможно Ñоздать дополнительное поле"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ полÑ: %1"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Ðевозможно Ñоздать группу"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Ðевозможно Ñоздать шаблон: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Ðевозможно Ñоздать заÑвку. Ðе задана очередь."
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Ðевозможно Ñоздать пользователÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "Ðевозможно Ñоздать Ð½Ð°Ð±Ð»ÑŽÐ´Ð°Ñ‚ÐµÐ»Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð° заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "Ðевозможно найти заÑвку Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð¾Ð¼ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "Ðевозможно найти группу %1."
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Ðевозможно найти или Ñоздать Ñтого пользователÑ"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Ðевозможно найти Ñтого пользователÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "Ðевозможно найти Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1."
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Ðевозможно загрузить дополнительное поле %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Ðевозможно загрузить группу"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "Ошибка загрузки объекта Ð´Ð»Ñ %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Ошибка загрузки атрибута запроÑа"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Ðевозможно назначить Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1 Ð´Ð»Ñ Ñтой очереди"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Ðевозможно назначить Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1 Ð´Ð»Ñ Ñтой заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Ðевозможно отозвать функции у Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÐºÐ°Ðº %1 Ð´Ð»Ñ Ñтой очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "Ðевозможно отозвать функции у Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÐºÐ°Ðº %1 Ð´Ð»Ñ Ñтой заÑвки"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Ðевозможно уÑтановить информацию о пользователе"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "Ðевозможно добавить вложение"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Ðевозможно добавить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² группу"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Ðевозможно Ñоздать транзакцию: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "Ðевозможно определить дальнейшие дейÑÑ‚Ð²Ð¸Ñ Ð¸Ð· ответа gpg\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "Ðевозможно найти группу\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Ðевозможно найти Ñтроку"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Ðевозможно найти Ñтого пользователÑ"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Ðевозможно найти Ñто значение"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "Ðевозможно найти Ñтого наблюдателÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "Ðевозможно найти пользователÑ\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Ðевозможно загрузить %1 из базы пользователей.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "Ошибка загрузки клаÑÑа %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Ðевозможно загрузить дополнительное поле %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "Ðевозможно загрузить файл наÑтроек RT '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "Ðевозможно загрузить Ñкриплеты."
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr "Ðевозможно загрузить копию заÑвки #%1."
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "Ðевозможно загрузить группу %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Ðевозможно загрузить ÑÑылку"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "Ðевозможно загрузить объект %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Ðевозможно загрузить очередь"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "Ðевозможно загрузить очередь %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "Ðевозможно загрузить Ñкриплет"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr "Ðевозможно загрузить Ñкриплет #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "Ðевозможно загрузить шаблон"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "Ðевозможно загрузить Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "Ðевозможно загрузить заÑвку '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't resolve base '%1' into a URI."
+msgstr "Ðевозможно преобразовать базу '%1' в URI."
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't resolve target '%1' into a URI."
+msgstr "Ðевозможно преобразовать цель '%1' в URI."
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Страна"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Создать"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Создать заÑвки"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Создать дополнительное поле"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "Создать дополнительное поле Ð´Ð»Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´Ð¸ 1"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "Создать дополнительное поле Ð´Ð»Ñ Ð²Ñех очередей"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "Добавить новое дополнительное поле"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global Scrip"
+msgstr "Создать новый глобальный Ñкриплет"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "Создать новый глобальный Ñкриплет"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Создать новую группу"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Создать новую личную группу"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "Создать новую очередь"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "Создать новый Ñкриплет"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "Создать новый шаблон"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Создать новую заÑвку"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Создать нового пользователÑ"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Создать очередь"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "Создать очередь Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "Создать запроÑ"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "Создать Ñкриплет Ð´Ð»Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´Ð¸ %1"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Создать шаблон"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Создать заÑвку"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "Ошибка ÑозданиÑ: %1 / %2 / %3 "
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "Ошибка ÑозданиÑ: %1/%2/%3"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Создать новые заÑвки на оÑнове Ñтого шаблона Ñкриплета"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Создать заÑвку"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Создать заÑвки в Ñтой очереди"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Создать, удалить или изменить дополнительные полÑ"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Создать, удалить или изменить очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "Создать, удалить или изменить членов любой пользовательÑкой перÑональной группы"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Создать, удалить или изменить членов перÑональных групп"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Создать, удалить или изменить пользователей"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "СоздаватьСохраненныйЗапроÑ"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "Создать ЗаÑвку"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Создана"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Создано дополнительное поле %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Created by"
+msgstr "Создано пользователем"
+
+#: NOT FOUND IN SOURCE
+msgid "Created during"
+msgstr "Создано во времÑ"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Создано за промежуток времени"
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "Создан шаблон %1"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "Создано заÑвок за период, Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð¸Ñ€Ð¾Ð²ÐºÐ¾Ð¹ по ÑтатуÑу"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Ðвтор заÑвки"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Текущие ÑвÑзи"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Relationships"
+msgstr "Текущие взаимоÑвÑзи"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Текущие Ñкриплеты"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Текущие пользователи"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Текущие права"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Текущий запроÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "Текущие критерии поиÑка"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Текущие наблюдатели"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "Дополнительное поле #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Дополнительные полÑ"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "Дополнительные Ð¿Ð¾Ð»Ñ Ð´Ð»Ñ %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "ПользовательÑкий код очиÑтки дейÑтвиÑ"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "ПользовательÑкий код подготовки дейÑтвиÑ"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "ПользовательÑкое уÑловие"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "Дополнительное поле %1 %2 %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 does not apply to this object"
+msgstr "Дополнительное поле %1 не отноÑитÑÑ Ðº Ñтому объекту"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Дополнительное поле %1 имеет значение."
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Дополнительное поле %1 не имеет значениÑ."
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Ðевозможно найти дополнительное поле %1"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr "Дополнительное поле '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "Дополнительное поле удалено"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "Ðевозможно найти дополнительное поле"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Ðевозможно найти значение %1 дополнительного Ð¿Ð¾Ð»Ñ %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "Значение дополнительного Ð¿Ð¾Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¾ Ñ %1 на %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Ðевозможно удалить значение дополнительного полÑ"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Ðевозможно найти значение дополнительного Ð¿Ð¾Ð»Ñ "
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Значение дополнительного Ð¿Ð¾Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¾"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "ДополнительноеПоле"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "ÐаÑтроить"
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "Ошибка данных"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Даты"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Дек."
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "Декабрь"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "Шаблон автоответа по умолчанию"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Шаблон автоответа по умолчанию"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Очередь по умолчанию"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Ðвтор заÑвки по умолчанию"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Шаблон админиÑтративного ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ñ Ð¿Ð¾ умолчанию"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Шаблон админиÑтративной корреÑпонденции по умолчанию"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Шаблон корреÑпонденции по умолчанию"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Шаблон транзакции по умолчанию"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "Умолчание: %1/%2 изменено Ñ %3 на %4"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Делегирование прав"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Делегирование отдельных прав, которые выданы вам."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "ДелегироватьПрава"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Делегирование прав"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Удалить"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Удалить шаблон"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Ошибка удалениÑ: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Удалить выбранные Ñкриплеты"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Удалить заÑвки"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr "Удалить значениÑ"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "УдалÑтьЗаÑвку"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Удаленный запроÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "Удаление Ñтого объекта может нарушить ÑÑылочную целоÑтноÑÑ‚ÑŒ"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Удаление Ñтого объекта нарушит ÑÑылочную целоÑтноÑÑ‚ÑŒ"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Удаление Ñтого объекта нарушит ÑÑылочную целоÑтноÑÑ‚ÑŒ"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "Удаление Ñтого объекта нарушит ÑÑылочную целоÑтноÑÑ‚ÑŒ."
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity. That's bad."
+msgstr "Удаление Ñтого объекта нарушит ÑÑылочную целоÑтноÑÑ‚ÑŒ."
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Отказать"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "От неё завиÑÑÑ‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "ЗавиÑимоÑти: \\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "Добавлено требование заÑвкой %1"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "Удалено требование заÑвкой %1"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "Добавлена завиÑимоÑÑ‚ÑŒ от %1"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "Удалена завиÑимоÑÑ‚ÑŒ от %1"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "ЗавиÑит от"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "ЗавиÑит от"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "По убыванию"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Ð’ порÑдке убываниÑ"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Опишите проблему"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "ОпиÑание"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "ПодробноÑти"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Показать"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Показать ÑпиÑок прав доÑтупа"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Показать колонки"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Показать шаблоны Ñкриплетов Ð´Ð»Ñ Ñтой очереди"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Показать Ñкриплеты Ð´Ð»Ñ Ñтой очереди"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Режим отображениÑ"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Показать Ñохраненные запроÑÑ‹ Ð´Ð»Ñ Ñтой группы"
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "Показать заÑвку #%1"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "РаÑпроÑтранÑетÑÑ Ð¿Ð¾ верÑии 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> GNU GPL.</a>"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Делать вÑе и везде"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Ðе обновлÑÑ‚ÑŒ Ñту Ñтраницу"
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "Ðе показывать результаты поиÑка"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Загрузить"
+
+#: NOT FOUND IN SOURCE
+msgid "Download all the tickets as a tab delimited file"
+msgstr "Загрузить вÑе заÑвки как файл Ñо значениÑми, разделенными табулÑцией"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Загрузить как файл Ñо значениÑми, разделенными табулÑцией"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Дан Ñрок"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "Ðевозможно разобрать Ñрок Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹ '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "ОШИБКÐ: Ðевозможно загрузить заÑвку '%1': %2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Редактировать"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Conditions"
+msgstr "Редактировать уÑловиÑ"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "Редактировать Дополнительные ПолÑ"
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "Редактирование дополнительных полей Ð´Ð»Ñ %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Редактирование дополнительных полей вÑех групп"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Редактирование дополнительных полей вÑех пользователей"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Редактирование дополнительных полей заÑвок во вÑех очередÑÑ…"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Редактировать ÑвÑзи"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Редактировать запроÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Relationships"
+msgstr "Редактировать взаимоÑвÑзи"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Редактировать запроÑ"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "Редактировать шаблоны Ð´Ð»Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´Ð¸ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "Редактировать ключевые Ñлова"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Редактировать Ñохраненные запроÑÑ‹ Ñтой группы"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "Редактировать Ñкриплеты"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Редактировать ÑиÑтемные шаблоны"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "Редактирование шаблонов Ð´Ð»Ñ %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "РедактироватьСохраненныеЗапроÑÑ‹"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "Редактирование наÑтроек очереди %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "Редактирование наÑтроек Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Редактирование Ð¿Ð¾Ð»Ñ %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "Редактирование ÑпиÑка пользователей группы %1"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Редактирование ÑпиÑка пользователей перÑональной группы %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "Редактирование шаблона %1"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Ðужно указать либо иÑточник, либо Ð°Ð´Ñ€ÐµÑ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Email"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Email Ð°Ð´Ñ€ÐµÑ ÑƒÐ¶Ðµ занÑÑ‚"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "EmailAddress"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "EmailEncoding"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "ИÑпользуетÑÑ (ÑнÑтие отметки отключает иÑпользование Ñтого дополнительного полÑ)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "ИÑпользуетÑÑ (ÑнÑтие отметки отключает иÑпользование Ñтой группы)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "ИÑпользуетÑÑ (ÑнÑтие отметки отключает иÑпользование Ñтой очереди)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "ИÑпользуемые дополнительные полÑ"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "ИÑпользуемые очереди"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "Ðктивирован ÑÑ‚Ð°Ñ‚ÑƒÑ %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "Ðктивирован ÑтатуÑ: %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Введите неÑколько значений"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Введите объекты или ÑÑылки на объекты. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ð°Ð¼Ð¸."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Введите одно значение"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Введите очереди или ÑÑылки на очереди. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ð°Ð¼Ð¸."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Введите заÑвки или ÑÑылки на заÑвки. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ð°Ð¼Ð¸."
+
+#: NOT FOUND IN SOURCE
+msgid "Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces."
+msgstr "Введите заÑвки или ÑÑылки на заÑвки. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ð°Ð¼Ð¸."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Введите до %1 значений"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Ошибка"
+
+#: NOT FOUND IN SOURCE
+msgid "Error adding watcher"
+msgstr "Ошибка Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ð°Ð±Ð»ÑŽÐ´Ð°Ñ‚ÐµÐ»Ñ"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Ошибка в параметрах Queue->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Queue->DelWatcher"
+msgstr "Ошибка в параметрах Queue->DelWatcher"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Ошибка в параметрах Queue->DeleteWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Ошибка в параметрах Ticket->AddWatcher"
+
+#: NOT FOUND IN SOURCE
+msgid "Error in parameters to Ticket->DelWatcher"
+msgstr "Ошибка в параметрах Ticket->DelWatcher"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Ошибка в параметрах Ticket->DeleteWatcher"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "РаÑширить заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Estimate"
+msgstr "РаÑчетно"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "РаÑчетное"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Ð’Ñе"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Пример:"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "ExternalAuthId"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "ExternalContactInfoId"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð°Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð° запроÑа"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Ðевозможно найти пÑевдо-группу 'Привилегированные'"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Ðевозможно найти пÑевдо-группу 'Ðепривилегированные'"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "Ошибка загрузки Ð¼Ð¾Ð´ÑƒÐ»Ñ %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "Ошибка загрузки объекта Ð´Ð»Ñ %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Фев."
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "Февраль"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Заполнить неÑколько текÑтовых полей"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Заполнить неÑколько полей wikitext"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Заполнить одно текÑтоввое поле"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Заполнить одно поле wikitext"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "Введите в Ñтом поле URL."
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Заполнить до %1 текÑтовых полей"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Заполнить до %1 полей wikitext"
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "Конец"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Конечный приоритет"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "Конечный приоритет"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "Ðайти группы, у которых"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Ðайти группы, у которых"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "Ðайти новые/открытые заÑвки"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Ðайти пользователей, у которых"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "ПоиÑк заÑвок"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "Подтвердить"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Ðачало"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "ÐŸÐµÑ€Ð²Ð°Ñ Ñтраница"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Изменить принудительно"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Форматировать"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "Ðайдено %quant(%1,ticket)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Ðайден объект"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "FreeformContactInfo"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Птн."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Ð’Ñе заголовки"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "ВзÑÑ‚ÑŒ шаблон из файла"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "Берем текущего Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð· pgp подпиÑи\\n"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Ðазначено %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Общие"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Общие дополнительные полÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "Общие Ñкриплеты"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Конфигурирование общих дополнительных полей"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Общий шаблон: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Отправить"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Отправить!"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "ÐŸÑ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ pgp подпиÑÑŒ от %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "Перейти на Ñтраницу"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Показать заÑвку"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Групповые"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "Группа %1 %2: %3"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Права группы"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Пользователь уже входит в группу"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "Ðевозможно Ñоздать группу."
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Ðевозможно Ñоздать группу: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Группа Ñоздана"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Ð’ группе нет такого пользователÑ"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Группа не найдена"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "Группа не найдена.\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "Ðе задана группа.\\n"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Группы"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Группы не могут быть членами входÑщих в них пользователей"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Группы, удовлетворÑющие уÑловию поиÑка"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Группы, в которых ÑоÑтоит Ñтот пользователь"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "ЗдравÑтвуйте!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "ЗдравÑтвуйте, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "ИÑториÑ"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ %1"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "Домашний телефон"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "В начало"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "ЧаÑов"
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "ИмеетÑÑ %quant(%1,concrete mixer)."
+
+#: NOT FOUND IN SOURCE
+msgid "I have [quant,_1,concrete mixer]."
+msgstr "ИмеетÑÑ [quant,_1,concrete mixer]."
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "ЗаÑвка"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "ЛичноÑÑ‚ÑŒ"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "ЕÑли подтверждение отклонено, отклонить заÑвку-первоиÑточник и удалить ожидающие подтверждениÑ"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "ЕÑли не указан Ðвтор, Ñоздать заÑвки от имени Ñтого Ðвтора."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "ЕÑли очередь не указана, то Ñоздавать заÑвки в Ñтой очереди."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "ЕÑли бы Ñта программа имела уÑтановленный бит setgid, то зловредный пользователь мог бы воÑпользоватьÑÑ Ñтим Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтративных полномочий в RT."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "ПоÑле любых изменений необходимо"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "ÐедопуÑтимое значение Ð´Ð»Ñ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "Изображение"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "ÐеизменÑемое поле"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "Показывать неиÑпользуемые дополнительные полÑ."
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Показывать неиÑпользуемые группы."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Показывать неиÑпользуемые очереди."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Показывать отключенных пользователей."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Ð’Ñтавить Ñтраницу"
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Ðезавершенный запроÑ"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Ðезавершенный запроÑ"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Ðачальный приоритет"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "Ðачальный приоритет"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Ошибка ввода"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr "Ввод должен ÑоответÑтвовать %1"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ°"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ°: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "ÐедопуÑтимый тип группы"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "ÐедопуÑтимое право"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "ÐедопуÑтимый тип"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "ÐедопуÑтимые данные"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "ÐеÑущеÑтвующий ответÑтвенный. ИÑпользуем 'nobody'."
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "ÐедопуÑтимый образец: %1"
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "ÐедопуÑÑ‚Ð¸Ð¼Ð°Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´ÑŒ"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "ÐедопуÑтимое право"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "ÐедопуÑтимое значение Ð´Ð»Ñ %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "ÐедопуÑтимое значение дополнительного полÑ"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "ÐедопуÑтимое значение ÑтатуÑа"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Обратите внимание, что непривилегированные пользователи не имеют права запуÑкать Ñту программу."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "ПредполагаетÑÑ, что Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка Ñтой программы вы должны Ñоздать непривилегированную учетную запиÑÑŒ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Unix Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ñ‹Ð¼Ð¸ уÑтановками групп и доÑтупом к RT."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Это требует неÑколько параметров:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Ðаклонный"
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "ЗаÑвки, ожидающие вашего подтверждениÑ"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Янв."
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "Январь"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "ПриÑоединитьÑÑ Ð¸Ð»Ð¸ покинуть Ñту группу"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Июл."
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "Июль"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Ð’Ñе данные"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Июн."
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "Июнь"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "Ключевое Ñлово"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "Язык"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Язык"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Большой"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Конец"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "ПоÑледний контакт"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Contact</a>"
+msgstr "ПоÑледний контакт</a>"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "ПоÑледний контакт"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "ПоÑледнее уведомление"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "ПоÑледнее изменение"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "ПоÑледнее изменение"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "ПоÑледний раз изменено"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "ОÑталоÑÑŒ"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Разрешить доÑтуп к RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "ПредоÑтавить пользователю права"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "Ограничиваем ответÑтвенного Ð´Ð»Ñ %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "Ограничиваем очередь Ð´Ð»Ñ %1 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "СÑылка"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "СвÑзь уже ÑущеÑтвует"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Ðевозможно Ñоздать ÑвÑзь"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "СвÑзь Ñоздана (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "СвÑзь удалена (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "СвÑзь не найдена"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "СвÑзать заÑвку #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "СвÑзать заÑвку %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "СвÑзать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr "СвÑзывание. Ðет доÑтупа"
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "СвÑзи"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Загрузить"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Загрузить Ñохраненный запроÑ:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "ЗагружатьСохраненныеЗапроÑÑ‹"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Загруженные модули perl"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Загружен Ð·Ð°Ð¿Ñ€Ð¾Ñ %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "МеÑтонахождение"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "Ðе найден каталог Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ %1 или не доÑтупен на запиÑÑŒ.\\n RT не может продолжить работу."
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "ЗарегиÑтрирован как %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Войти"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Выйти"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr ""
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Ðазначить ответÑтвенного"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "УÑтановить ÑтатуÑ"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "УÑтановить конечный Ñрок"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "УÑтановить дату решениÑ"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "УÑтановить дату 'Ðачато'"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "УÑтановить дату 'ÐачинаетÑÑ'"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "УÑтановить дату поÑледнего контакта"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "УÑтановить приоритет"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Ðазначить очередь"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "УÑтановить тему"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Сделать Ñту группу видимой Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Управление дополнительными полÑми и их значениÑми"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Управление группами и членами групп"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Управление ÑвойÑтвами и наÑтройками, которые применÑÑŽÑ‚ÑÑ ÐºÐ¾ вÑем очередÑм"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Управление очередÑми и их параметрами"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Управление пользователÑми и паролÑми"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Мар."
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "Март"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "Май"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Май"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "УчаÑтник %1 добавлен"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "УчаÑтник %1 удален"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Пользователь добавлен в группу"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Пользователь удален из группы"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Пользователь не удален из группы"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "СоÑтоит в"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "СоÑтоит в"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "УчаÑтники"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "УчаÑтие в %1 добавлено"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "УчаÑтие в %1 удалено"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "УчаÑтие в группах"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "УчаÑтие в группах данного Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "ЗаÑвки уÑпешно объединены"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Ошибка объединениÑ. Ðевозможно уÑтановить идентификатор заÑвки."
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Ошибка объединениÑ. Ðевозможно уÑтановить ÑтатуÑ"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Объединить Ñ Ð·Ð°Ñвкой"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "Объединено в %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Сообщение"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "Тело ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð½Ðµ показано потому, что оно или Ñлишком большое или не ÑвлÑетÑÑ Ñ‚ÐµÐºÑтом."
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Ðевозможно запиÑать Ñообщение"
+
+#: NOT FOUND IN SOURCE
+msgid "Message recipients"
+msgstr "Получатели ÑообщениÑ"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Сообщение запиÑано"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾Ð± Ñтой заÑвке не будет отправлены..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Минут"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "ÐеÑовпадающие Ñкобки"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Пропущен первичный ключ?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Мобильный"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "Мобильный телефон"
+
+#: NOT FOUND IN SOURCE
+msgid "Modified"
+msgstr "Изменено"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify"
+msgstr "Изменить"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Изменить ÑпиÑок ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ñ Ð´Ð¾Ñтупа"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Field %1"
+msgstr "Изменить дополнительное поле %1"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Изменение дополнительных полей, которые применÑÑŽÑ‚ÑÑ Ðº %1 Ð´Ð»Ñ Ð²Ñех %2"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Изменить дополнительные полÑ, применÑемые ко вÑем %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "Изменить дополнительные полÑ, применÑемые ко вÑем очередÑм"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Изменить права группы"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Изменить учаÑтников"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Изменить права"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Изменить шаблоны Ñкриплетов Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð¹ очереди"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Изменить Ñкриплеты Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð¹ очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "Изменить ÑиÑтемные права доÑтупа"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "Изменить шаблон %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Изменить права пользователÑ"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "Изменить дополнительное поле Ð´Ð»Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´Ð¸ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "Изменить дополнительное поле, которое применÑетÑÑ ÐºÐ¾ вÑем очередÑм"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "Изменить Ñкриплет Ð´Ð»Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´Ð¸ %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Изменить Ñкриплет, который дейÑтвует Ð´Ð»Ñ Ð²Ñех очередей"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "Изменить аÑÑоциированные объекты Ð´Ð»Ñ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "Изменить даты заÑвки #%1"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "Изменить даты заÑвки #%1"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Изменить даты заÑвки #%1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Изменить общие дополнительные полÑ"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Изменить общие права группы"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Изменить общие права группы."
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "Изменить общие права Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿Ð¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "Изменить общие права Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "Изменить общие Ñкриплеты"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Изменить общие права пользователÑ"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Изменить общие права пользователÑ."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Изменить метаданные группы или удалить ее"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Изменить права группы на дополнительное поле %1"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "Изменить права группы на группу %1"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "Изменить права группы на очередь %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Изменить ÑпиÑок учаÑтников Ñтой группы"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Изменить ÑобÑтвенную учетную запиÑÑŒ RT"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "Изменить пользователей, отноÑÑщихÑÑ Ðº очереди %1"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "Изменить пользователей, отноÑÑщихÑÑ Ðº заÑвке #%1"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "Изменить Ñкриплеты Ð´Ð»Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´Ð¸ %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Изменить Ñкриплеты, которые дейÑтвуют на вÑе очереди"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "Изменить шаблон %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Изменить шаблоны, которые применÑÑŽÑ‚ÑÑ ÐºÐ¾ вÑем очередÑм"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "Изменить вид по умолчанию Ñтраницы \"Обзор RT\""
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "Изменить группу %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Изменить очередь наблюдателей"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "Изменить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "Изменить заÑвку # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "Изменить заÑвку # %1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Изменить заÑвки"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Изменить права Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½Ð° дополнительное поле %1"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "Изменить права пользователей группы %1"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "Изменить права Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½Ð° очередь %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "Изменить наблюдателей очереди '%1'"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ИзменÑтьПраваДоÑтупа"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "ИзменÑтьДополнительноеПоле"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "ИзменÑтьСобÑтвенноеУчаÑтиеВГруппах"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "ИзменÑÑ‚ÑŒÐаблюдателейОчереди"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ИзменÑтьСкриплеты"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "ИзменÑтьСебÑ"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "ИзменÑтьШаблон"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "ИзменÑтьЗаÑвку"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Пнд."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Move"
+msgstr "ПеремеÑтить"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "ПеремеÑтить вниз"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "ПеремеÑтить вверх"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "ÐеÑколько значений"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "Ð’Ñ‹ должны указать ИмÑ"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "%1 ваших заÑвок"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "Ваши подтверждениÑ"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "Ваши ÑегоднÑшние заÑвки"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Ваши подтверждениÑ"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Ваши Ñохраненные запроÑÑ‹"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "ИмÑ"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Ð˜Ð¼Ñ ÑƒÐ¶Ðµ иÑпользуетÑÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Name matches"
+msgstr "Совпадение имен"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "Ðеобходимо подтверждение ÑиÑтемного админиÑтратора"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Ðикогда"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "Ðовых"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Ðовые ÑвÑзи"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Ðовый пароль"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Ðовое ожидающее подтверждение"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "Ðовый запроÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "New Relationships"
+msgstr "Ðовые ÑвÑзи"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Ðовый поиÑк"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Ðовое дополнительное поле"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð°"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Ðовый пароль"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Отправлено Ñообщение Ñ Ð½Ð¾Ð²Ñ‹Ð¼ паролем"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´ÑŒ"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Ðовое напоминание:"
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "Ðовый запроÑ"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Ðовые права"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Ðовый Ñкриплет"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "Ðовый поиÑк"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Ðовый шаблон"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ñвка"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ñвка не ÑущеÑтвует"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Ðовый пользователь"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Добавить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Ðовые наблюдатели"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "Ðовые наÑтройки окна"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Вперед"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтраница"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтраница"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "ПÑевдоним"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "ПÑевдоним"
+
+#: NOT FOUND IN SOURCE
+msgid "No"
+msgstr "Ðет"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "КлаÑÑ Ð½Ðµ указан"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Ðет дополнительного полÑ"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Дополнительное поле не указано"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Группа не указана"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Ðет запроÑа"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Очередь не указана"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Пользователь RT не найден. ПожалуйÑта, обратитеÑÑŒ к вашему админиÑтратору RT.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Ðет шаблона"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "ЗаÑвка не указана. Отмена."
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "ЗаÑвка не указана. ОтменÑем Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ñвки\\n\\n"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Ðет дейÑтвиÑ"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Колонка не указана"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "Команда не найдена\\n"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Ðет комментариев о пользователе"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "ПуÑтое Ñообщение"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "Ðет опиÑÐ°Ð½Ð¸Ñ Ð´Ð»Ñ %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Ðе указана группа"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Группы, удовлетворÑющие уÑловию поиÑка, не найдены."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Ðет приÑоединенных Ñообщений"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Пароль не уÑтановлен"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Ðет прав Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ñ‡ÐµÑ€ÐµÐ´ÐµÐ¹"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Ðет прав Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ñвок в очереди '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Ðет прав Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Ðет прав Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра Ñтой заÑвки"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "Ðет доÑтупа Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð¸Ñка в глобальных наÑтройках"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Ðет прав Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра изменений Ñтой заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Пользователь не указан"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Пользователи не выбраны."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Очереди, удовлетворÑющие уÑловию поиÑка, не найдены."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Права не найдены"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Права не выданы."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Ðи один Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ загружен"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Ðет запроÑа Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка."
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Ðет темы"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "Идентификатор заÑвки не указан"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Тип транзакции не указан"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "Пользователь или Ð°Ð´Ñ€ÐµÑ email не указан"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Пользователи, удовлетворÑющие уÑловию поиÑка, не найдены."
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "Ðе найден пользователь RT. Обработчик CVS отключен. ОбратитеÑÑŒ к админиÑтратору RT.\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Ðикакое значение не отправлено _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Ðикто"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "ÐеÑущеÑтвующее поле?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr "Ðе уÑтановлено"
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "Ðе зарегиÑтрирован"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Ðе зарегиÑтрирован."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Ðе уÑтановлено"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Еще не реализовано."
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "Еще не реализовано..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "ПримечаниÑ"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Ðевозможно отправить уведомление"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "УведомлÑÑ‚ÑŒ ÐдминиÑтративныеКопии"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "УведомлÑÑ‚ÑŒ ÐдминиÑтративныеКопии как Комментарий"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "УведомлÑÑ‚ÑŒ Копии"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "УведомлÑÑ‚ÑŒ Копии как Комментарий"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "УведомлÑÑ‚ÑŒ других Получателей"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "УведомлÑÑ‚ÑŒ других Получателей как Комментарий"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "УведомлÑÑ‚ÑŒ ОтветÑтвенного"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "УведомлÑÑ‚ÑŒ ОтветÑтвенного как Комментарий"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "УведомлÑÑ‚ÑŒ ОтветÑтвенного об отклонении заÑвки"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "УведомлÑÑ‚ÑŒ ОтветÑтвенного о подтверждении заÑвки вÑеми подтверждающими"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "УведомлÑÑ‚ÑŒ ОтветÑтвенного о подтверждении заÑвки одним из подтверждающих"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "УведомлÑÑ‚ÑŒ ОтветÑтвенных и ÐдминиÑтративныеКопии о новых, ожидающих их подтверждениÑÑ…"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "УведомлÑÑ‚ÑŒ ÐвторовЗаÑвки"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "УведомлÑÑ‚ÑŒ ÐвторовЗаÑвки и Копии"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "УведомлÑÑ‚ÑŒ ÐвторовЗаÑвки и Копии как Комментарий"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "УведомлÑÑ‚ÑŒ Ðвторов заÑвки, Копии и ÐдминиÑтративныеКопии"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "УведомлÑÑ‚ÑŒ Ðвторов заÑвки, Копии и ÐдминиÑтративныеКопии как Комментарии"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "ÐоÑ."
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "ÐоÑбрь"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "ИЛИ"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Ðевозможно Ñоздать объект"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Ðевозможно удалить объект"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Объект Ñоздан"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Объект удален"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Тип объекта %1 не может Ñодержать дополнительные полÑ"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "ÐеÑовпадение типа объекта"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Окт."
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "ОктÑбрь"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Ðвтономно"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Ðвтономное редактирование"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "ÐÐ²Ñ‚Ð¾Ð½Ð¾Ð¼Ð½Ð°Ñ Ð·Ð°ÐºÐ°Ñ‡ÐºÐ°"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Ðа"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr "%1, %2 пиÑал:"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Ðа комментарий"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "При КорреÑпонденции"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "При Создании"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "При Изменении ОтветÑтвенного"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "При Изменении Приоритета"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "При Изменении Очереди"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "При Решении"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "При Изменении СтатуÑа"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "При Транзакции"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Показывать Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов, Ñозданных поÑле %1"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Показывать Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов Ñозданных до %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Показывать дополнительные Ð¿Ð¾Ð»Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ длÑ:"
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "Открытых"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "Открытые заÑвки"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Открыть"
+
+#: NOT FOUND IN SOURCE
+msgid "Open requests"
+msgstr "Открытые запроÑÑ‹"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Открытые заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "Открыть заÑвки (из ÑпиÑка) в новом окне"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "Открыть заÑвки (из ÑпиÑка) в другом окне"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Открыть заÑвки при корреÑпонденции"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Параметры"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Сортировать по"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "ПорÑдок и Ñортировка"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "ОрганизациÑ"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "ЗаÑвка-первоиÑточник: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "ИÑходÑÑ‰Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° о комментарии запиÑана"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "ИÑходÑÑ‰Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° запиÑана"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Со временем поднÑÑ‚ÑŒ приоритет до"
+
+#: NOT FOUND IN SOURCE
+msgid "Overview"
+msgstr "Обзор"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Ðазначить ÑебÑ"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "БытьОтветÑтвеннымЗаЗаÑвку"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "ОтветÑтвенный"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "ОтветÑтвенный изменен Ñ %1 на %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "ОтветÑтвенный не может быть назначен"
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "ОтветÑтвенный принудительно изменен Ñ %1 на %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "ОтветÑтвенный"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Страница %1 из %2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Пейджер"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "Телефон пейджера"
+
+#: NOT FOUND IN SOURCE
+msgid "Parent"
+msgstr "Предок"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Предки"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Пароль"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "ПодÑказка к паролю"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Пароль изменен"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "Пароль должен ÑоÑтоÑÑ‚ не менее чем из %1 Ñимволов"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Пароль уÑтановлен"
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "Пароль Ñлишком короткий"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Пароль: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Пароль: доÑтуп запрещен"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Пароли не Ñовпадают"
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Пароли не Ñовпадают. Ваш пароль не был изменен"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Пользователи"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Выполнить дейÑтвие, указанное пользователем"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Perl"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Ðет доÑтупа"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "ДоÑтуп запрещен"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "ДоÑтупы запрещены"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Личные группы"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Личные группы"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Личные группы:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Ðомера телефонов"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "Заполнитель"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "ÐаÑтройки"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr "ÐаÑтройки %1 Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %2 ."
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr "ÐаÑтройки %1 Ñохранены."
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "ÐаÑтройки"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Подготовка не реализована"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Ðазад"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ñтраница"
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ñтраница"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "Приоритет"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "Пользователь %1 не найден."
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Приоритет"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Приоритет начинаетÑÑ Ñ"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "КонфиденциальноÑÑ‚ÑŒ:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Привилегированные"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "СоÑтоÑние привилегий: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Привилегированные пользователи"
+
+#: NOT FOUND IN SOURCE
+msgid "Projects"
+msgstr "Проекты"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "ПÑевдогруппы Ð´Ð»Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÐµÐ³Ð¾ иÑпользованиÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "ЗапроÑ"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "ПоÑтроитель запроÑа"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "ЗапроÑ:"
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Очередь"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "Очередь %1 не найдена"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "Очередь '%1' не найдена\\n"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Ðаименование очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "Скриплеты очереди"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Очередь уже ÑущеÑтвует"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Ðевозможно Ñоздать очередь"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Ðевозможно загрузить очередь"
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Очередь Ñоздана"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "Очередь не указана."
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Очередь не найдена"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Очереди"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Очереди, которыми вы управлÑете"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Очереди, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… вы ÑвлÑетеÑÑŒ ÐдминиÑтративной Копией"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "БыÑтрый поиÑк"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "БыÑтрое Ñоздание заÑвки"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 Ð´Ð»Ñ %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 от <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "ÐаÑтройка RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "Ошибка региÑтрации в RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "ПиÑьмо возвращено RT: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "Ошибка конфигурации RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "КритичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° RT: Сообщение не было Ñохранено!"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "Ошибка RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT получил Ñвое ÑобÑтвенное Ñообщение (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Recieved mail (%1) from itself."
+msgstr "RT получил Ñвое ÑобÑтвенное Ñообщение (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service"
+msgstr "СамообÑлуживание RT"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "СамообÑлуживание RT / Закрытые заÑвки"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "Переменные RT"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "Обзор RT"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr "Обзор RT Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %1"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT может добавлÑÑ‚ÑŒ Ñодержимое другого реÑурÑа Ñети, когда отображает Ñто дополнительное поле."
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT может преобразовывать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñтого дополнительного Ð¿Ð¾Ð»Ñ Ð² гиперÑÑылки на другой ÑервиÑ."
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT не может зарегиÑтрировать ваÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT не Ñмог найти автора заÑвки во внешней базе данных"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT не может найти очередь: %1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²Ð°ÑˆÐµÐ¹ ÑеÑÑии"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT не может проверить Ñту подпиÑÑŒ PGP. \\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "RT Ð´Ð»Ñ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "RT Ð´Ð»Ñ %1: %2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT выполнил ваши команды"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "Ð’Ñе права на RT защищены и охранÑÑŽÑ‚ÑÑ Ð·Ð°ÐºÐ¾Ð½Ð¾Ð¼. &copy; 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. ПО раÑпроÑтранÑетÑÑ Ð¿Ð¾Ð´ <a href=\"http://www.gnu.org/copyleft/gpl.html\">Стандартной ОбщеÑтвенной Лицензией GNU ВерÑии 2.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent <jesse@bestpractical.com>. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "Ð’Ñе права на RT защищены и охранÑÑŽÑ‚ÑÑ Ð·Ð°ÐºÐ¾Ð½Ð¾Ð¼. &copy; 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. ПО раÑпроÑтранÑетÑÑ Ð¿Ð¾Ð´ <a href=\"http://www.gnu.org/copyleft/gpl.html\">Стандартной ОбщеÑтвенной Лицензией GNU ВерÑии 2.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-2002 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "Ð’Ñе права на RT защищены и охранÑÑŽÑ‚ÑÑ Ð·Ð°ÐºÐ¾Ð½Ð¾Ð¼. &copy; 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. ПО раÑпроÑтранÑетÑÑ Ð¿Ð¾Ð´ <a href=\"http://www.gnu.org/copyleft/gpl.html\">Стандартной ОбщеÑтвенной Лицензией GNU ВерÑии 2.</a>"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT Ñчитает, что Ñто Ñообщение может быть возвратом"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "Ð’ÑÑ‘ оÑтальное, что вы введёте, RT будет иÑкать в темах заÑвок."
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT будет обрабатывать Ñто Ñообщение как неподпиÑанное.\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT заменит <tt>__id__</tt> и <tt>__CustomField__</tt> идентификатором запиÑи и значением дополнительного Ð¿Ð¾Ð»Ñ ÑоответÑтвенно"
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "Командный режим RT email требует иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñи .PGP. Ð’Ñ‹ либо не подпиÑали Ñообщение, либо ваша подпиÑÑŒ не может быть проверена."
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Полное имÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "Полное имÑ"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "СÑылка на заÑвку заÑвки %1 добавлена"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "СÑылка на заÑвку заÑвки %1 удалена"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "СÑылка на %1 добавлена"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "СÑылка на %1 удалена"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Ðа неё ÑÑылаютÑÑ"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "СÑылаетÑÑ Ð½Ð°"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "СÑылаетÑÑ Ð½Ð°"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "ОчиÑтить"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "ОчиÑтить запроÑ"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "ОбновлÑÑ‚ÑŒ Ñту Ñтраницу каждые %1 минут."
+
+#: NOT FOUND IN SOURCE
+msgid "Relationships"
+msgstr "СвÑзи"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "Ðапоминание '%1' добавлено"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "Ðапоминание '%1' завершено"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "Ðапоминание '%1' открыто заново"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "ЗаÑвка Ð½Ð°Ð¿Ð¾Ð¼Ð¸Ð½Ð°Ð½Ð¸Ñ #%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "ÐапоминаниÑ"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "ÐÐ°Ð¿Ð¾Ð¼Ð¸Ð½Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ð·Ð°Ñвки #%1"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Удалить админиÑтративную копию"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Удалить копию"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Удалить Ðвтора ЗаÑвки"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Ответить"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "ÐÐ´Ñ€ÐµÑ Ð´Ð»Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ð°"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Отвечать авторам заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Отвечать на заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "ОтвечатьÐаЗаÑвку"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Отчёты"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Ðвтор заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "Email автора заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "Ðвтор(-Ñ‹)"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "ÐÐ´Ñ€ÐµÑ Ðвтора заÑвки"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Ðвторы заÑвок"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "ЗапроÑÑ‹ должны быть обработаны за"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Ðеобходимый параметр '%1' не указан"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "ОчиÑтить"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr "СброÑить на предуÑтановленные"
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Домашний"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Решить"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Решить заÑвку #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Решено"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Решено ответÑтвенным"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Решено в промежутке времени"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Решено заÑвок за период, Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð¸Ñ€Ð¾Ð²ÐºÐ¾Ð¹ по ответÑтвенному"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Решено заÑвок, Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð¸Ñ€Ð¾Ð²ÐºÐ¾Ð¹ по ответÑтвенному"
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "Ответ авторам заÑвки"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Результаты"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "Результатов на Ñтранице"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Повторите пароль"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Откатить"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "Право %1 не найдено Ð´Ð»Ñ %2 %3 в рамках %4 (%5)\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Право делегировано"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Право выдано"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Право загружено"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Право не может быть отозвано"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Право не найдено"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Право не загружено"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Право отозвано"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Права"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Ðевозможно выдать права Ð´Ð»Ñ %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Ðевозможно отозвать права %1"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "ПÑевдо-группы"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Строк Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Строк на Ñтранице"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Суб."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Сохранить"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Сохранить изменениÑ"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Сохранить наÑтройки"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Сохранить изменениÑ"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "Сохраненный Ð·Ð°Ð¿Ñ€Ð¾Ñ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "Сохраненные запроÑÑ‹"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Скриплет #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Скриплет Ñоздан"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "ÐŸÐ¾Ð»Ñ Ñкриплетов"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Скриплет удален"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Скриплеты"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "Скриплеты Ð´Ð»Ñ %1\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Скриплеты, которые дейÑтвуют Ð´Ð»Ñ Ð²Ñех очередей"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "ПоиÑк"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "Критерии поиÑка"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Параметры поиÑка"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Ошибка загрузки атрибута запроÑа"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "ПоиÑк подтверждений"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "ПоиÑк заÑвок"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "ПоиÑк заÑвок. Введите номер <strong>идентификатора</strong>, Ð¸Ð¼Ñ <strong>очереди</strong>, Ð¸Ð¼Ñ <strong>ответÑтвенного</strong>, email-Ð°Ð´Ñ€ÐµÑ <strong>автора заÑвки</strong>. Ð’ÑÑ‘ оÑтальное, что вы введёте, RT будет иÑкать в текÑте и вложениÑÑ… заÑвок."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Параметры поиÑка"
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "Группировать результаты поиÑка по %1"
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Изменение запроÑа: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Searches can't be associated with that kind of object"
+msgstr "ЗапроÑÑ‹ не могут быть аÑÑоциированы Ñ Ñ‚Ð°ÐºÐ¸Ð¼ типом объекта"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "ПолнотекÑтовый поиÑк по каждой заÑвке может занÑÑ‚ÑŒ длительное времÑ, но еÑли он необходим вам, вы можете иÑкать любое Ñлово по вÑей иÑтории заÑвки, Ð²Ð²ÐµÐ´Ñ <b>fulltext:<i>Ñлово</i></b>."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "БезопаÑноÑÑ‚ÑŒ:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "Смотрите также:"
+
+#: NOT FOUND IN SOURCE
+msgid "See also: %1"
+msgstr "Смотрите также: %1"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "ПроÑмотреть дополнительные полÑ"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "ПроÑмотреть полноÑтью иÑходÑщую почту и ее получателей"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "ПроÑмотреть конфиденциальный комментарий заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "ПроÑмотреть Ñводную информацию заÑвки"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "ПроÑматриватьДополнительныеПолÑ"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "ПроÑматриватьГруппу"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "ПроÑмативатьОчередь"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "Выбрать"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Выберите дополнительное поле"
+
+#: NOT FOUND IN SOURCE
+msgid "Select a Custom Fields"
+msgstr "Выберите дополнительные полÑ"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Выберите группу"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Выберите очередь"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Выберите очередь Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ новой заÑвки"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Выберите пользователÑ"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Выберите дополнительное поле"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Выберите дополнительные Ð¿Ð¾Ð»Ñ Ð²Ñех пользовательÑких групп"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Выберите дополнительные Ð¿Ð¾Ð»Ñ Ð²Ñех пользователей"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Выберите дополнительные Ð¿Ð¾Ð»Ñ Ð·Ð°Ñвок во вÑех очередÑÑ…"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Выберите дополнительные Ð¿Ð¾Ð»Ñ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ð¹ заÑвок во вÑех очередÑÑ…"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Выберите группу"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Выбрать неÑколько значений"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Выбрать одно значение"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Выберите очередь"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Выберите очереди, которые будут отображатьÑÑ Ð½Ð° Ñтранице \"Обзор RT\""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Выберите Ñкриплет"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Выберите шаблон"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Выберите до %1 значений"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Выберите пользователÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "ВыбиратьÐеÑколько"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "ВыбиратьОдно"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Выбранные дополнительные полÑ"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Выбранные объекты"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Выбор изменён. ПожалуйÑта, Ñохраните ваши изменениÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "СамообÑлуживание"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Отправить Ñообщение вÑем наблюдателÑм"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Отправить Ñообщение вÑем наблюдателÑм как \"комментарий\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Отправить Ñообщение вÑем Ðвторам заÑвки и КопиÑм"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Отправить Ñообщение вÑем Ðвторам заÑвки и КопиÑм"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "ОтправлÑет Ñообщение авторам заÑвки"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "ОтправлÑÑ‚ÑŒ почту Ñвно указанным КопиÑм и СкрытымКопиÑм"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "ОтправлÑÑ‚ÑŒ почту КопиÑм"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "ОтправлÑÑ‚ÑŒ почту КопиÑм как комментарий"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "ОтправлÑÑ‚ÑŒ почту ÐдминиÑтративнымКопиÑм"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "ОтправлÑÑ‚ÑŒ почту ÐдминиÑтративнымКопиÑм как комментарий"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "ОтправлÑÑ‚ÑŒ почту ОтветÑтвенному"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Сен."
+
+#: NOT FOUND IN SOURCE
+msgid "Seperate multiple URLs with spaces"
+msgstr "РазделÑйте неÑколько ÑÑылок пробелами"
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "СентÑбрь"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Показать"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Показать подтверждениÑ"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Показать колонки"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Показать результаты"
+
+#: NOT FOUND IN SOURCE
+msgid "Show advanced search options..."
+msgstr "Показать дополнительные параметры поиÑка..."
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Показать подтвержденные запроÑÑ‹"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Показать оÑновное"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Показать отклоненные запроÑÑ‹"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Показать подробноÑти"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Показать ожидающие запроÑÑ‹"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Показать запроÑÑ‹, ждущие других подтверждений"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "Показать конфиденциальный комментарий заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "Показать Ñводную информацию заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "ПоказыватьПраваДоÑтупа"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "ПоказыватьЗакладкуКонфигурации"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "ПоказыватьИÑходÑщуюПочту"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "ПоказыватьСохраненныеЗапроÑÑ‹"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "ПоказыватьСкриплеты"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "ПоказыватьШаблон"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "ПоказыватьЗаÑвку"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "ПоказыватьКомментарииЗаÑвки"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "ПодпиÑатьÑÑ ÐºÐ°Ðº Ðвтор заÑвки или ÐšÐ¾Ð¿Ð¸Ñ Ð·Ð°Ñвки или очереди"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "ПодпиÑатьÑÑ ÐºÐ°Ðº ÐдминиÑтративнаÑÐšÐ¾Ð¿Ð¸Ñ Ð·Ð°Ñвки или очереди"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "ПодпиÑÑŒ"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "ЗарегиÑтрирован как %1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "ПроÑтой поиÑк"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Одно значение"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Размер"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "ПропуÑтить меню"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Маленький"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "Ðекоторые обозреватели могут загружать Ñодержимое только Ñ Ñ‚Ð°ÐºÐ¾Ð³Ð¾ же домена, как у Ñервера RT."
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Сортировка"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort Order"
+msgstr "Метод Ñортировки"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "Ключ Ð´Ð»Ñ Ñортировки"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "Сортировать по полю"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "ПорÑдок Ñортировки"
+
+#: NOT FOUND IN SOURCE
+msgid "Squelched message recipients"
+msgstr "Скрытые получатели ÑообщениÑ"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "СтадиÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "ПриоÑтановленных"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñтраница"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "ÐачалÑÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "Ðе могу разобрать дату 'ÐачалÑÑ': '%1'"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "ÐачнетÑÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "Ðачато"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "Ðе могу разобрать дату 'Ðачато': '%1'"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "СоÑтоÑние"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "СтатуÑ"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Изменение ÑтатуÑа"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½ Ñ %1 на %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "ИзменÑтьСтатуÑ"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Переназначать Ñебе"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Переназначить заÑвки Ñебе"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "ПереназначатьЗаÑвкуСебе"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "ОтветÑтвенный переназначен Ñ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "ОтветÑтвенный переназначен Ñ %1"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Стиль"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Тема"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Тема изменена на %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Отправить"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Query"
+msgstr "Отправить запроÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "Отправить поÑледовательноÑÑ‚ÑŒ дейÑтвий"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "УÑпешно"
+
+#: NOT FOUND IN SOURCE
+msgid "Summary"
+msgstr "Ð¡Ð²Ð¾Ð´Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
+
+#: NOT FOUND IN SOURCE
+msgid "Summary matches"
+msgstr "Ð’Ñего Ñовпадений"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Ð’Ñк."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "ÐдминиÑтратор"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "СиÑтемные"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. Right not granted."
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Право не выдано"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. right not granted"
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Право не выдано"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "СиÑтемные утилиты"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Право не делегировано."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Право не выдано."
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Ðевозможно выдать права"
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "СиÑтемные группы"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¿Ñевдо-группа Ð´Ð»Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÐµÐ³Ð¾ иÑпользованиÑ"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Ðазначить ÑÐµÐ±Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ñтвенным"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Ðазначить ÑÐµÐ±Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ñтвенным за заÑвки"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "ÐазначатьСебÑОтветÑтвеннымЗаЗаÑвку"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Ðазначен ответÑтвенным"
+
+#: NOT FOUND IN SOURCE
+msgid "Task"
+msgstr "Задача"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Шаблон"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Шаблон #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Шаблон удален"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr "Шаблон ÑвлÑетÑÑ Ð¾Ð±Ñзательным аргументом"
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Шаблон не найден"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "Шаблон не найден\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Шаблон обработан"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "Ошибка обработки шаблона"
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Шаблоны"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "Шаблоны Ð´Ð»Ñ %1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "ТекÑÑ‚"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Это уже текущее значение"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Это поле не может иметь такого значениÑ"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Значение не изменилоÑÑŒ"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Этот пользователь уже имеет Ñто право."
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Этот пользователь уже %1 Ð´Ð»Ñ Ñтой очереди"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Этот пользователь уже %1 Ð´Ð»Ñ Ñтой заÑвки "
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Этот пользователь не %1 Ð´Ð»Ñ Ñтой очереди"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "Этот пользователь не %1 Ð´Ð»Ñ Ñтой заÑвки"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Эта очередь не ÑущеÑтвует"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Эта заÑвка имеет неразрешенные завиÑимоÑти"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "Пользователь уже имеет Ñто право"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Пользователь уже ответÑтвенен за Ñту заÑвку"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Пользователь не ÑущеÑтвует"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Этот пользователь уже привилегированный"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Этот пользователь уже непривилегированный"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Этот пользователь теперь привилегированный"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Этот пользователь теперь непривилегированный"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "Пользователь теперь непривилегированный"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Этот пользователь не может быть ответÑтвенным за заÑвки в Ñтой очереди"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Это не чиÑловой идентификатор"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "ОÑновное"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "ÐšÐ¾Ð¿Ð¸Ñ Ð·Ð°Ñвки"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "ÐдминиÑÑ‚Ñ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ Ð·Ð°Ñвки"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "Комментарий запиÑан"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "ÐижеÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° найдет вÑе активные заÑвки в очереди 'ОÑновнаÑ' и уÑтановит им приоритет 99, еÑли в них не было активноÑти более 4-Ñ… чаÑов:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "Эти команды не были выполнены:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Ðовое значение уÑтановлено"
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "ОтветÑтвенный за заÑвку"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Ðвтор заÑвки"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Эти комментарии не показываютÑÑ Ð¾Ð±Ñ‹ÐºÐ½Ð¾Ð²ÐµÐ½Ð½Ð¾Ð¼Ñƒ пользователю"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Это дополнительное поле не отноÑитÑÑ Ðº Ñтому объекту"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Это возможноÑÑ‚ÑŒ доÑтупна только Ð´Ð»Ñ ÑиÑтемных админиÑтраторов."
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Это Ñообщение будет отправлено..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "Эта заÑвка %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Этот инÑтрумент позволÑет пользователю запуÑкать некоторые модули Perl из RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Похоже, что Ñта Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ñ Ð½Ðµ имеет Ñодержимого"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "%1 заÑвок макÑимального приоритета Ñтого пользователÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "25 важнейших заÑвок пользователÑ..."
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Чтв."
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket"
+msgstr "ЗаÑвка"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "ЗаÑвка # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "ЗаÑвка # %1 Изменение вÑех данных: %2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "ЗаÑвка #%1 Изменение вÑех данных: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "ЗаÑвка #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "ЗаÑвка %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "ЗаÑвка %1 Ñоздана в очереди '%2'"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "ЗаÑвка %1 загружена\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "ЗаÑвка %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Дополнительные Ð¿Ð¾Ð»Ñ Ð·Ð°Ñвки"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°Ñвки # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "ЗаÑвка #"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "ЗаÑвка решена"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Транзакции заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "Ð’Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð·Ð°Ñвки"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Содержимое заÑвки"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Тип данных Ñодержимого заÑвки"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "ЗаÑвка не может быть Ñоздана из-за внутренней ошибки"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "ЗаÑвка Ñоздана"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "Ðевозможно Ñоздать заÑвку"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "ЗаÑвка удалена"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "Идентификатор заÑвки не найден"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "ЗаÑвка уничтожена"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Метаданные заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "ЗаÑвка не найдена"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð·Ð°Ñвки изменен"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "Ðаблюдатели за заÑвкой"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "Модуль поиÑка TicketSQL"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "ЗаÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "ЗаÑвки %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "ЗаÑвки %1 Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %2"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "ЗаÑвки, Ñозданные поÑле"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "ЗаÑвки, Ñозданные до"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "ЗаÑвки от %1"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "ЗаÑвки, решённые поÑле"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "ЗаÑвки, решённые до"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "От Ñтого Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð·Ð°Ð²Ð¸ÑÑÑ‚ Ñледующие заÑвки:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "РаÑчётное времÑ"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "ОÑталоÑÑŒ времени"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð² работе"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "ОÑталоÑÑŒ времени"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ Ñтраницы"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð² работе"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð² работе"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Заголовок"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "Ð”Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ изменений Ñтой фикÑации:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "Ð”Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ изменений Ñтой фикÑации:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Ð”Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа поддержки, обучениÑ, разработки на заказ или Ð»Ð¸Ñ†ÐµÐ½Ð·Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‰Ð°Ð¹Ñ‚ÐµÑÑŒ %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Контакт"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Утилиты"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "Ð’Ñего"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "ТранзакциÑ"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Ð¢Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ñ %1 удалена"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Ð¢Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ñ Ñоздана"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Дополнительные Ð¿Ð¾Ð»Ñ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ð¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "ТранзакциÑ->Создать невозможно, так как вы не указали идентификатор заÑвки"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "ТранзакциÑ->Создать невозможно, так как вы не указали тип объекта и идентификатор"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "ÐеизменÑемые транзакции"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "ПытаемÑÑ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ право: %1"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Ð’Ñ‚Ñ€."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Тип"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Ðе реализовано"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Логин UNIX"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ UNIX"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "ÐеизвеÑÑ‚Ð½Ð°Ñ ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²ÐºÐ° %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "ÐеизвеÑтное поле: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Ðе ограничено"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "БезымÑнный запроÑ"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Ðепривилегированный"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Ðевыбранные дополнительные полÑ"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Ðевыбранные объекты"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Ðет ответÑтвенного"
+
+#: NOT FOUND IN SOURCE
+msgid "Untitled search"
+msgstr "БезымÑнный запроÑ"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Изменить"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "Изменить вÑе"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "Изменить идентификатор"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Изменить заÑвку"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Изменить тип"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "Изменить вÑе Ñти заÑвки одновременно"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "Изменить e-mail"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Изменить неÑколько заÑвок"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "Изменить имÑ"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Изменение не запиÑано."
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "Изменить выбранные заÑвки"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "Изменить подпиÑÑŒ"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Изменить заÑвку"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "Изменение заÑвки # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Изменение заÑвки #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Изменение заÑвки #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Изменение не было ни Ñообщением, ни комментарием."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Изменена"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Загрузить"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Загрузить неÑколько файлов"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Загрузить неÑколько изображений"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Загрузить один файл"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Загрузить одно изображение"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Закачать до %1 файлов"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Закачать до %1 изображений"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Загрузить ваши изменениÑ."
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Другие админиÑтративные утилиты RT"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "Пользователь %1 %2: %3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "Пользователь %1 Пароль: %2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "Пользователь '%1' не найден."
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "Пользователь '%1' не найден"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "Пользователь '%1' не найден\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Задано пользователем"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Заданные пользователем уÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ дейÑтвиÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "Логин"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "Логин"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Права пользователÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "Пользователь запроÑил изменение неизвеÑтного типа Ð´Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ Ð¿Ð¾Ð»Ñ %1 Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° %2 #%3"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Ðевозможно Ñоздать пользователÑ: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Пользователь Ñоздан"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Группы, определенные пользователем"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Пользователь загружен"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "Пользователю отправлено напоминание"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "ПользовательÑкие наÑтройки"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Группы, заданные пользователем"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Пользователи"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Пользователи, удовлетворÑющие критериÑм поиÑка"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr "ИÑпользуетÑÑ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ñ #%1..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Проверить запроÑ"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Проверка корректноÑти"
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "Значение запроÑа"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "ЗначениÑ"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Ðаблюдать"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "ÐаблюдатьÐдминиÑтративнойКопией"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "Ðаблюдатель загружен"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Ðаблюдатели"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "WebEncoding"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Срд."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "Сделано ÑегоднÑ"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Когда заÑвка подтверждена вÑеми подтверждающими, добавить запиÑÑŒ в оригинальную заÑвку"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Когда заÑвка подтверждена любым из подтверждающих, добавить запиÑÑŒ в оригинальную заÑвку"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Когда заÑвка Ñоздана"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "При Ñоздании заÑвки Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÑÑ‚ÑŒ ОтветÑтвенного и ÐдминиÑтративнуюКопию заÑвки, ожидающей подтверждениÑ"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Когда что-либо произойдет"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Каждый раз при решении заÑвки"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Каждый раз при изменении ответÑтвенного заÑвки"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Каждый раз при изменении приоритета заÑвки"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Каждый раз при изменении очереди очереди"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Каждый раз при изменении ÑтатуÑа заÑвки"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Каждый раз при Ñрабатывании уÑловиÑ, заданного пользователем"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Каждый раз при добавлении комментариÑ"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Каждый раз при приходе корреÑпонденции"
+
+#: NOT FOUND IN SOURCE
+msgid "Which are referred to by "
+msgstr "ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÑвÑзана пользователем"
+
+#: NOT FOUND IN SOURCE
+msgid "Which refer to"
+msgstr "ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÑÑылаетÑÑ Ð½Ð°"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Рабочий"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Работать автономно"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "Рабочий телефон"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "В работе"
+
+#: NOT FOUND IN SOURCE
+msgid "Yes"
+msgstr "Да"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Ð’Ñ‹ уже ответÑтвенный за Ñту заÑвку"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Ð’Ñ‹ незарегиÑтрированный пользователь"
+
+#: NOT FOUND IN SOURCE
+msgid "You can access it with the Download button on the right."
+msgstr "Ð’Ñ‹ можете получить Ñто, нажав Ñправа кнопку Загрузить"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "Ð’Ñ‹ также можете редактировать предварительно заданный запроÑ"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Ð’Ñ‹ можете назначать ответÑтвенного только Ð´Ð»Ñ Ñвоих или ничьих заÑвок."
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr "Ð’Ñ‹ можете назначать ÑÐµÐ±Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ñтвенным только за заÑвки, которые никому не принадлежат"
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ права на проÑмотр Ñтой заÑвки.\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "Ðайдено %1 заÑвок в очереди %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Вы вышли из RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ права Ñоздавать заÑвки в Ñтой очереди."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Ð’Ñ‹ не должны Ñоздавать запроÑÑ‹ в Ñтой очереди."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Заходите еще"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "Ваши запроÑÑ‹: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "ÐдминиÑтратор RT неправильно наÑтроил почтовые алиаÑÑ‹"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ð» %1. Другие Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ продолжать находитьÑÑ Ð² ожидании."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½."
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¾Ñ‚ÐºÐ»Ð¾Ð½ÐµÐ½"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¾Ñ‚ÐºÐ»Ð¾Ð½ÐµÐ½."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ пароль неверные"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "ИндекÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[нет темы]"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "разрешить Ñоздание Ñохраненных запроÑов"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "разрешить загрузку Ñохраненных запроÑов"
+
+#: NOT FOUND IN SOURCE
+msgid "and is not"
+msgstr "и не ÑвлÑетÑÑ"
+
+#: NOT FOUND IN SOURCE
+msgid "and not"
+msgstr "и не"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "отноÑитÑÑ Ðº"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "закрыто"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "Ñодержит"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "Ñодержимое"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "тип данных"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "корреÑÐ¿Ð¾Ð½Ð´ÐµÐ½Ñ†Ð¸Ñ (возможно) не отправлена"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "корреÑÐ¿Ð¾Ð½Ð´ÐµÐ½Ñ†Ð¸Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð°"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "дней"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "удалить"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "удалена"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "не Ñовпадает"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "не Ñодержит"
+
+#: NOT FOUND IN SOURCE
+msgid "email address"
+msgstr "Ð°Ð´Ñ€ÐµÑ email"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "равнÑетÑÑ"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "ошибка: невозможно перемеÑтить вниз"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "ошибка: невозможно перемеÑтить влево"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "ошибка: невозможно перемеÑтить вверх"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "ошибка: нет данных Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "ошибка: нет данных Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "ошибка: нет данных Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ"
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "ложь"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "больше чем"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "группа '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "Ñгруппировано по %1"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "чаÑов"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "идентификатор"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "ÑвлÑетÑÑ"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "не ÑвлÑетÑÑ"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "меньше чем"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "Ñовпадает"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "мин"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "минут"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "изменениÑ\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "меÑÑцев"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "новаÑ"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "нет имени"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "нет значениÑ"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "нет"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "не равно"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "неравно"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "открыта"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "Ð»Ð¸Ñ‡Ð½Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð° '%1' Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%2'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "очередь %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "отклонена"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "решена"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "Ñек"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "показывать закладку КонфигурациÑ"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "ÑÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "приоÑтановлена"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr "Ñтиль: %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "Ñтроки Ñводной информации"
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "ÑиÑтема %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "ÑиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð° '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "вызывающий компонент не указал причину"
+
+#: NOT FOUND IN SOURCE
+msgid "ticket #%1"
+msgstr "заÑвка #%1"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "заÑвка #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "till"
+msgstr "пока"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "иÑтина"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "группа без опиÑаниÑ: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "undescripbed group %1"
+msgstr "группа без опиÑаниÑ: %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "пользователь %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "недель"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¾Ð¼ %1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "лет"
+
diff --git a/rt/lib/RT/I18N/sv.po b/rt/lib/RT/I18N/sv.po
new file mode 100644
index 0000000..25312a0
--- /dev/null
+++ b/rt/lib/RT/I18N/sv.po
@@ -0,0 +1,5840 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2007-03-16 12:57+0100\n"
+"Last-Translator: Heidi Senderovitz\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. ($self->{CurrentSearch}{Object}->Description)
+#: html/Widgets/SavedSearch:70
+msgid " %1 deleted."
+msgstr " %1 raderad."
+
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+#: html/Widgets/SavedSearch:47
+msgid " %1 renamed to %2."
+msgstr " %1 omdöpt till %2."
+
+#. ($args->{Description})
+#: html/Widgets/SavedSearch:60
+msgid " %1 saved."
+msgstr " %1 sparad."
+
+#. ($TicketObj->Id, $TicketObj->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($Ticket->id, $Ticket->Subject)
+#: html/Approvals/Elements/Approve:48
+#: html/Approvals/Elements/ShowDependency:71
+#: html/SelfService/Display.html:46
+#: html/Ticket/Display.html:47
+#: html/Ticket/Display.html:51
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:104
+msgid "$1"
+msgstr "$1"
+
+#. ($label)
+#: lib/RT/Record.pm:940
+msgid "$prefix %1"
+msgstr "$prefix %1"
+
+#. ($self->ObjectType, $self->Object->Id)
+#: lib/RT/URI/fsck_com_rt.pm:256
+msgid "%1 #%2"
+msgstr "%1 #%2"
+
+#. ($s, $time_unit)
+#: lib/RT/Date.pm:365
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+#: lib/RT/Date.pm:401
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%1 %2 %3 %4:%5:%6 %7"
+
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+#: lib/RT/Record.pm:1685
+#: lib/RT/Transaction_Overlay.pm:647
+#: lib/RT/Transaction_Overlay.pm:690
+msgid "%1 %2 added"
+msgstr "%1 %2 tillagt"
+
+#. ($s, $time_unit)
+#: lib/RT/Date.pm:362
+msgid "%1 %2 ago"
+msgstr "%1 %2 sedan"
+
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+#: lib/RT/Record.pm:1692
+#: lib/RT/Transaction_Overlay.pm:654
+msgid "%1 %2 changed to %3"
+msgstr "%1 %2 ändrat till %3"
+
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+#: lib/RT/Record.pm:1689
+#: lib/RT/Transaction_Overlay.pm:650
+#: lib/RT/Transaction_Overlay.pm:696
+msgid "%1 %2 deleted"
+msgstr "%1 %2 raderad"
+
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+#: html/Admin/Elements/EditScrips:65
+#: html/Admin/Elements/ListGlobalScrips:63
+#: html/Ticket/Elements/PreviewScrips:103
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 med mall %3"
+
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+#: html/Ticket/Elements/ShowAttachments:72
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) av %3"
+
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+#. (loc($DefaultStatus))
+#: html/SelfService/Update.html:60
+#: html/Ticket/Elements/EditBasics:108
+#: html/Ticket/Update.html:61
+#: html/Ticket/Update.html:63
+#: html/Tools/MyDay.html:66
+msgid "%1 (Unchanged)"
+msgstr "%1 (Oändrad)"
+
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+#: bin/rt-crontool:237
+#: bin/rt-crontool:244
+#: bin/rt-crontool:250
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - En parameter att skicka till %2"
+
+#. ("--verbose")
+#: bin/rt-crontool:262
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - Skicka statusuppdateringar till STDOUT"
+
+#. ("--template-id")
+#: bin/rt-crontool:253
+msgid "%1 - Specify id of the template you want to use"
+msgstr "%1 - Specificera ID på mall du vill använda"
+
+#. ("--transaction")
+#: bin/rt-crontool:256
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr "%1 - Specificera om du vill använda 'första' eller 'sista' transaktionen"
+
+#. ("--action")
+#: bin/rt-crontool:247
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Specificera den handling du vill använda"
+
+#. ("--condition")
+#: bin/rt-crontool:241
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Specificera den villkorsmodul du vill använda"
+
+#. ("--search")
+#: bin/rt-crontool:234
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Specificera den sökmodul du vill använda"
+
+#. ("--transaction-type")
+#: bin/rt-crontool:259
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr "%1 - Specificera den typ av transaktion du vill använda"
+
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+#: html/Elements/Footer:56
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 Copyright 1996-%3 %4."
+
+#. ($self->Id)
+#: lib/RT/ScripAction_Overlay.pm:150
+msgid "%1 ScripAction loaded"
+msgstr "%1 ScripAktion inläst"
+
+#. ($args{'Value'}, $cf->Name)
+#: lib/RT/Record.pm:1722
+msgid "%1 added as a value for %2"
+msgstr "%1 tillagt som värde för %2"
+
+#. ($args{'Base'})
+#. ($args{'Target'})
+#: lib/RT/Link_Overlay.pm:144
+#: lib/RT/Link_Overlay.pm:151
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 tycks vara ett lokalt objekt men går inte att hitta i databasen"
+
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#: html/Ticket/Elements/ShowDates:73
+#: lib/RT/Transaction_Overlay.pm:531
+msgid "%1 by %2"
+msgstr "%1 av %2"
+
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+#: lib/RT/Transaction_Overlay.pm:788
+#: lib/RT/Transaction_Overlay.pm:797
+#: lib/RT/Transaction_Overlay.pm:800
+msgid "%1 changed from %2 to %3"
+msgstr "%1 ändrat från %2 till %3"
+
+#. ($Description)
+#: html/Search/Build.html:213
+msgid "%1 copy"
+msgstr "%1 kopiera"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1 kunde inte sättas som %2."
+
+#. ($self)
+#: lib/RT/Ticket_Overlay.pm:2787
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 kunde inte markeras som löst. RT:s databas kan vara oförenlig."
+
+#. ($obj_type)
+#: lib/RT/Transaction_Overlay.pm:571
+msgid "%1 created"
+msgstr "%1 skapad"
+
+#. ($obj_type)
+#: lib/RT/Transaction_Overlay.pm:576
+msgid "%1 deleted"
+msgstr "%1 raderad"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "%1 högsta prioriterade ärenden som tillhör mig"
+
+#. ($0)
+#: bin/rt-crontool:229
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 är ett verktyg som reagerar på ärenden från ett externt planeringsverktyg, så som cron."
+
+#. ($principal->Object->Name, $args{'Type'})
+#: lib/RT/Queue_Overlay.pm:863
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 är inte längre en %2 för denna kö."
+
+#. ($minutes)
+#: html/Ticket/Elements/ShowTime:47
+#: html/Ticket/Elements/ShowTime:49
+msgid "%1 min"
+msgstr "%1 min."
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "%1 nyaste ärenden som inte tillhör någon"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 objekt"
+
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+#: html/User/Elements/DelegateRights:97
+msgid "%1 rights"
+msgstr "%1 rättigheter"
+
+#. (ref $self)
+#: lib/RT/Action/ResolveMembers.pm:63
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 kommer att lösa alla medlemmar av ett löst gruppärende."
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1:s %2 objekt"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1:s %2:s %3 objekt"
+
+#. ($Object->Name)
+#. ($object->Name)
+#: html/Search/Elements/SearchPrivacy:52
+#: html/Search/Elements/SelectSearchObject:55
+#: html/Search/Elements/SelectSearchesForObjects:57
+msgid "%1's saved searches"
+msgstr "%1:s sparade sökningar"
+
+#. ($self)
+#: lib/RT/Transaction_Overlay.pm:481
+msgid "%1: no attachment specified"
+msgstr "%1: inga bifogade filer angivna"
+
+#. ($size)
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+msgid "%1b"
+msgstr "%1b"
+
+#. (int( $size / 102.4 ) / 10)
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+msgid "%1k"
+msgstr "%1k"
+
+#. (sprintf("%.1f",$minutes / 60))
+#: html/Ticket/Elements/ShowTime:49
+msgid "%quant(%1,hour)"
+msgstr "%quant(%1,timme)"
+
+#. ($args{'Status'})
+#: lib/RT/Ticket_Overlay.pm:1142
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' är ogiltigt statusvärde"
+
+#: html/Admin/Elements/EditCustomFieldValues:50
+#: html/Admin/Elements/EditQueueWatchers:50
+#: html/Admin/Elements/EditScrips:56
+#: html/Admin/Elements/EditTemplates:57
+#: html/Admin/Groups/Members.html:73
+#: html/Elements/EditLinks:54
+#: html/Ticket/Elements/EditPeople:67
+#: html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Markera box för radering)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Markera boxar för bortväljande av meddelande till registrerade mottagare)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(Markera boxar för tillval av meddelande till registrerade mottagare)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Mata in ärende-ID:n eller URL, separerade med mellanrum)"
+
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+#: html/Admin/Queues/Modify.html:75
+#: html/Admin/Queues/Modify.html:81
+msgid "(If left blank, will default to %1)"
+msgstr "(Lämnas det tomt kommer default att vara %1)"
+
+#: html/Admin/Elements/EditCustomFields:74
+#: html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Inga extrafält)"
+
+#: html/Admin/Groups/Members.html:71
+#: html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Inga medlemmar)"
+
+#: html/Admin/Elements/EditScrips:53
+#: html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Inga scrips)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Inga mallar)"
+
+#: html/Admin/Elements/PickCustomFields:47
+#: html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Inga)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Skickar en hemlig kopia av denna uppdatering till en kommaseparerad lista med e-postadresser. Ändrar <strong>inte</strong> på vem som får framtida uppdateringar.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Skickar en kopia av denna uppdatering till en kommaseparerad lista med administrativa e-postadresser. Dessa <strong>kommer</strong> att få framtida uppdateringar.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Skickar en kopia av denna uppdatering till en kommaseparerad lista med e-postadresser. Ändrar <strong>inte</strong> på vem som får framtida uppdateringar.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Skickar en kopia av denna uppdatering till en kommaseparerad lista med e-postadresser. Dessa <strong>kommer</strong> att få framtida uppdateringar.)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Använd dessa fält när du väljer 'Användardefinierat' för villkor eller handling)"
+
+#: html/Ticket/Elements/EditWatchers:60
+#: html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(Kommer inte att skickas som e-post)"
+
+#: html/Admin/Groups/index.html:57
+#: html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(tom)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(inga namn registrerade)"
+
+#: html/Admin/Elements/SelectRights:72
+#: html/Elements/EditCustomFieldSelect:69
+#: html/Elements/SelectCustomFieldValue:51
+#: html/Elements/ShowCustomFields:54
+#: html/Search/Chart:56
+#: html/Search/Elements/Chart:76
+#: lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(inget värde)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(inga värden)"
+
+#: html/Elements/EditLinks:132
+#: html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(endast ett ärende)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(inväntar godkännande)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(inväntar annan Samling)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(obligatorisk)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(ej namngiven)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(yyyy/mm/dd)"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr "-"
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr "--transaktionsparameter kan bara vara 'först' eller 'sist'"
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ärende->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$fält%>"
+
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+#: html/Elements/CreateTicket:47
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Nytt ärende i\" />&nbsp;%1"
+
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+#: docs/design_docs/string-extraction-guide.txt:54
+#: lib/RT/StyleGuide.pod:787
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Nytt ärende i\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "En tom mall"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Det har inte satts något lösenord, så användare kan inte logga in."
+
+#: lib/RT/ACE_Overlay.pm:174
+#: lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE ej funnen"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE kan endast skapas och raderas."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "OCH"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Om mig"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "Tillgångskontroll"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Handling"
+
+#. ($args{'ScripAction'})
+#: lib/RT/Scrip_Overlay.pm:172
+msgid "Action %1 not found"
+msgstr "Handling %1 ej funnen"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Handling genomförd.\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr "Handling är obligatoriskt argument"
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Handling förberedd..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Lägg till"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Lägg till Admin.kopia"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Lägg till kopia"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Lägg till kolumner"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Lägg till kriterium"
+
+#: html/Ticket/Create.html:147
+#: html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Lägg till fler filer"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "Lägg till rekvirent"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "Lägg till värde"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Lägg till ett scrip som kommer att fungera för alla köer"
+
+#: html/Search/Build.html:109
+#: html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Lägg till och sök"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Lägg till kommentarer eller svar på utvalda ärenden"
+
+#: html/Admin/Groups/Members.html:63
+#: html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Lägg till medlemmar"
+
+#: html/Admin/Queues/People.html:87
+#: html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Lägg till nya observatörer"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Lägg till dessa termer till din sökning"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "Lägg till värden"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Lägg till, radera och modifiera extrafältvärden för objekt"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:763
+msgid "Added principal as a %1 for this queue"
+msgstr "Tillagd principal som en %1 för denna kö"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1455
+msgid "Added principal as a %1 for this ticket"
+msgstr "Tillagd principal som en %1 för detta ärende"
+
+#: html/Admin/Users/Modify.html:146
+#: html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adress1"
+
+#: html/Admin/Users/Modify.html:151
+#: html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adress2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Admin.kopia"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Admin.kommentar"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Admin.korrespondens"
+
+#: html/Admin/Queues/index.html:46
+#: html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Admin.köer"
+
+#: html/Admin/Global/index.html:47
+#: html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Admin/Global konfiguration"
+
+#: etc/initialdata:56
+#: html/Ticket/Elements/ShowPeople:60
+#: lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "Admin.kopia"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "AdminExtrafält"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "AdminGrupp"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "AdminGruppMedlemskap"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "AdminEgnaPersonligaGrupper"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "AdminKö"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "AdminAnvändare"
+
+#: html/Admin/Queues/People.html:69
+#: html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Administrativ kopia"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "Avancerad"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Efter"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Aggregator"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Alla godkännanden har accepterats"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Alla köer"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "Och/eller"
+
+#: html/Admin/CustomFields/Modify.html:73
+#: html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Gäller för"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Utför"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Utför dina ändringar"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Godkännande"
+
+#. ($ticket->id, $msg)
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#: html/Approvals/Display.html:65
+#: html/Approvals/Elements/ShowDependency:63
+#: html/Approvals/index.html:86
+msgid "Approval #%1: %2"
+msgstr "Godkännande #%1: %2"
+
+#. ($ticket->Id)
+#: html/Approvals/index.html:75
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Godkännande #%1: Anteckningar ej registrerade på grund av systemfel"
+
+#. ($ticket->Id)
+#: html/Approvals/index.html:73
+msgid "Approval #%1: Notes recorded"
+msgstr "Godkännande #%1: Anteckningar har registrerats"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Godkännande har accepterats"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Godkännande avvisat"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Godkänn"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Godkännares anteckningar: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Apr."
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "Stigande"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Stigande"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Lägg till och radera extrafält"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "LäggtillExtraFält"
+
+#: html/Search/Bulk.html:142
+#: html/SelfService/Update.html:87
+#: html/Ticket/ModifyAll.html:115
+#: html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Bifoga"
+
+#: html/SelfService/Create.html:92
+#: html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Bifoga fil"
+
+#: html/SelfService/Update.html:75
+#: html/Ticket/Create.html:131
+#: html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "Bifogad fil"
+
+#. ($Attachment)
+#: html/Ticket/ShowEmailRecord.html:52
+#: html/Ticket/ShowEmailRecord.html:56
+#: html/Ticket/ShowEmailRecord.html:59
+msgid "Attachment '%1' could not be loaded"
+msgstr "Bifogad fil '%1' kunde inte läsas in"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Bifogad fil skapad"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Bifogat filnamn"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Bifogade filer"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Attribut raderat"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "Aug."
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Autosvar"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "Autosvar till rekvirenter"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "Tillgänglig"
+
+#: html/Admin/Elements/CustomFieldTabs:65
+#: html/Admin/Elements/GroupTabs:60
+#: html/Admin/Elements/QueueTabs:60
+#: html/Admin/Elements/UserTabs:58
+#: html/Ticket/Elements/Tabs:113
+#: html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Grunddata"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Hemlig kopia"
+
+#: html/Admin/CustomFields/GroupRights.html:91
+#: html/Admin/CustomFields/UserRights.html:74
+#: html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "Se till att spara dina ändringar"
+
+#: html/Elements/SelectDateRelation:55
+#: lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Före"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Best Practical Solutions, LLC företagslogo"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "Tom"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Fetstil"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Länk som kan sparas som bokmärke"
+
+#: html/Ticket/Elements/ShowHistory:64
+#: html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Korta headers"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "Massärende-uppdatering"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Kan inte modifiera systemanvändare"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Kan denna principal se denna kö"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Kan inte lägga till ett extrafältvärde utan ett namn"
+
+#. ($Class)
+#: html/Admin/CustomFields/Objects.html:86
+msgid "Can't find a collection class for '%1'"
+msgstr "Kan inte hitta en samlingskategori för '%1'"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Kan inte hitta en sparad sökning att arbeta med"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Kan inte länka ett ärende till sig själv"
+
+#. (loc($self->{SearchType}))
+#: html/Widgets/SavedSearch:63
+msgid "Can't save %1"
+msgstr "kan inte spara %1"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Kan inte spara den här sökningen"
+
+#: lib/RT/Record.pm:1282
+#: lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Kan inte specificera både bas och mål"
+
+#. ($msg)
+#: html/autohandler:204
+msgid "Cannot create user: %1"
+msgstr "Kan inte skapa användare: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62
+#: html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "Kategori"
+
+#: etc/initialdata:50
+#: html/Admin/Queues/People.html:65
+#: html/SelfService/Create.html:71
+#: html/Ticket/Create.html:88
+#: html/Ticket/Elements/EditPeople:72
+#: html/Ticket/Elements/ShowPeople:56
+#: html/Ticket/Update.html:83
+#: lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Kopia"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Ändra lösenord"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Markera alla"
+
+#: html/SelfService/Update.html:78
+#: html/Ticket/Create.html:134
+#: html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Markera box för radering"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Markera box för indragande av rättigheter"
+
+#: html/Elements/EditLinks:148
+#: html/Elements/EditLinks:85
+#: html/Elements/ShowLinks:78
+#: html/Ticket/Create.html:223
+#: html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Barn"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Välj ett datum"
+
+#: html/Admin/Users/Modify.html:156
+#: html/User/Prefs.html:141
+msgid "City"
+msgstr "Ort"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Välj bort alla"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "Stäng fönster"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Stängt"
+
+#: html/SelfService/Closed.html:46
+#: html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Stängda ärenden"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Combobox: Välj eller mata in flera värden"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Combobox: Välj eller mata in ett värde"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Combobox: Välj eller mata in upp till %1 värden"
+
+#: html/Ticket/Elements/ShowTransaction:190
+#: html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Kommentar"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Kommentar adress"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Kommentar på ärenden"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "KommentarPåÄrende"
+
+#: html/Ticket/ModifyAll.html:91
+#: html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Kommentarer (Ej skickade till rekvirent)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Kommentarer (ej skickade till rekvirent)"
+
+#: html/Admin/Users/Modify.html:225
+#: html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Kommentarer om denna användare"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Kommentarer tillagda"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Commit tömt"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Villkor"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "Villkor är obligatorisk parameter"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Villkor matchar..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Villkor hittades ej"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Konfiguration"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Bekräfta"
+
+#: html/Admin/Elements/ModifyTemplate:65
+#: html/Elements/SelectAttachmentField:48
+#: html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "Innehåll"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "Innehållstyp"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Kopia"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Korrespondens"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Korrespondens tillagd"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Går ej att lägga till nytt extrafältvärde. "
+
+#. (, $value_msg)
+#: lib/RT/Record.pm:1660
+msgid "Could not add new custom field value. %1 "
+msgstr "Går ej att lägga till nytt extrafältvärde. %1 "
+
+#: lib/RT/Ticket_Overlay.pm:3048
+#: lib/RT/Ticket_Overlay.pm:3056
+#: lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Går ej att byta ägare. "
+
+#. ($msg)
+#: html/Admin/CustomFields/Modify.html:161
+msgid "Could not create CustomField"
+msgstr "Går ej att skapa ExtraFält"
+
+#. ($msg)
+#: html/Admin/Elements/EditCustomField:113
+msgid "Could not create CustomField: %1"
+msgstr "Går ej att skapa ExtraFält: %1"
+
+#: html/User/Groups/Modify.html:98
+#: lib/RT/Group_Overlay.pm:494
+#: lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "GÃ¥r ej att skapa grupp"
+
+#. ($msg)
+#: html/Admin/Global/Template.html:96
+#: html/Admin/Queues/Template.html:93
+msgid "Could not create template: %1"
+msgstr "GÃ¥r ej att skapa mall: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075
+#: lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Går ej att skapa ärende. Kö ej satt"
+
+#: lib/RT/User_Overlay.pm:255
+#: lib/RT/User_Overlay.pm:269
+#: lib/RT/User_Overlay.pm:278
+#: lib/RT/User_Overlay.pm:287
+#: lib/RT/User_Overlay.pm:296
+#: lib/RT/User_Overlay.pm:310
+#: lib/RT/User_Overlay.pm:320
+#: lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Går ej att skapa användare"
+
+#: lib/RT/Queue_Overlay.pm:741
+#: lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Går ej att hitta eller skapa denna användare"
+
+#: lib/RT/Queue_Overlay.pm:802
+#: lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "GÃ¥r ej att hitta denna principal"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Går ej att läsa in ExtraFält %1"
+
+#: html/Admin/Groups/Members.html:112
+#: html/User/Groups/Members.html:111
+#: html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Går ej att läsa in grupp"
+
+#. ($privacy)
+#: lib/RT/SavedSearch.pm:119
+msgid "Could not load object for %1"
+msgstr "Går ej att läsa in objekt för %1"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Går ej att läsa in sökattribut"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:761
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Går ej att göra denna principal %1 för denna kö"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1444
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Går ej att göra denna principal %1 för detta ärende"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:860
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Går ej att flytta denna principal som %1 för denna kö"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Går ej att sätta in användarinfo"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "Går ej att sätta in bifogad fil"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Går ej att lägga till medlem till grupp"
+
+#. ($Msg)
+#: lib/RT/Record.pm:1719
+#: lib/RT/Record.pm:1771
+msgid "Couldn't create a transaction: %1"
+msgstr "GÃ¥r ej att skapa transaktion: %1"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "GÃ¥r ej att hitta rad"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "GÃ¥r ej att hitta denna principal"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Går ej att hitta detta värde"
+
+#. ($self->Id)
+#: lib/RT/CurrentUser.pm:145
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "Går ej att läsa in %1 från användarens databas.\\n"
+
+#. ($id)
+#: html/Admin/CustomFields/UserRights.html:149
+msgid "Couldn't load Class %1"
+msgstr "Går ej att läsa in klass %1"
+
+#. ($id)
+#: html/Admin/CustomFields/GroupRights.html:107
+msgid "Couldn't load CustomField %1"
+msgstr "Går ej att läsa in ExtraFält %1"
+
+#. ($self->Id)
+#: lib/RT/Ticket_Overlay.pm:2016
+msgid "Couldn't load copy of ticket #%1."
+msgstr "Går ej att läsa in kopia av ärende #%1."
+
+#. ($id)
+#: html/Admin/Groups/GroupRights.html:109
+#: html/Admin/Groups/UserRights.html:96
+msgid "Couldn't load group %1"
+msgstr "Går ej att läsa in grupp %1"
+
+#: lib/RT/Link_Overlay.pm:202
+#: lib/RT/Link_Overlay.pm:211
+#: lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Går ej att läsa in länk"
+
+#. ($id)
+#: html/Admin/Elements/ObjectCustomFields:83
+#: html/Admin/Queues/CustomFields.html:59
+#: html/Admin/Users/CustomFields.html:59
+msgid "Couldn't load object %1"
+msgstr "Går ej att läsa in objekt %1"
+
+#. ($id)
+#: html/Admin/Queues/People.html:142
+msgid "Couldn't load queue"
+msgstr "Går ej att läsa in kö"
+
+#. ($id)
+#: html/Admin/Queues/GroupRights.html:122
+#: html/Admin/Queues/UserRights.html:93
+msgid "Couldn't load queue %1"
+msgstr "Går ej att läsa in kö %1"
+
+#. ($id)
+#: html/Admin/Elements/EditScrip:126
+#: html/Admin/Elements/EditScrip:167
+msgid "Couldn't load scrip #%1"
+msgstr "Går ej att läsa in scrip #%1"
+
+#. ($id)
+#: html/SelfService/Display.html:158
+#: lib/RT/Action/CreateTickets.pm:680
+msgid "Couldn't load ticket '%1'"
+msgstr "Går ej att läsa in ärende '%1'"
+
+#. ($args{'URI'})
+#: lib/RT/Ticket_Overlay.pm:2643
+msgid "Couldn't resolve '%1' into a URI."
+msgstr "Kunde inte omvandla '%1' till en URI."
+
+#: html/Admin/Users/Modify.html:173
+#: html/User/Prefs.html:153
+msgid "Country"
+msgstr "Land"
+
+#: html/Admin/Elements/CreateUserCalled:47
+#: html/Admin/Elements/EditCustomField:84
+#: html/Admin/Elements/EditScrip:133
+#: html/Admin/Queues/Template.html:66
+#: html/Elements/QuickCreate:65
+#: html/Ticket/Create.html:168
+#: html/Ticket/Create.html:235
+msgid "Create"
+msgstr "Skapa"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Skapa Ärenden"
+
+#: html/Admin/CustomFields/Modify.html:150
+#: html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Skapa ett ExtraFält"
+
+#. ($QueueObj->Name())
+#: html/Admin/Queues/CustomField.html:69
+msgid "Create a CustomField for queue %1"
+msgstr "Skapa ett ExtraFält för kö %1"
+
+#: html/Admin/Groups/Modify.html:125
+#: html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Skapa en ny grupp"
+
+#: html/User/Groups/Modify.html:113
+#: html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Skapa en ny personlig grupp"
+
+#: html/Ticket/Create.html:47
+#: html/Ticket/Create.html:51
+#: html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "Skapa ett nytt ärende"
+
+#: html/Admin/Users/Modify.html:252
+#: html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Skapa en ny användare"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Skapa en kö"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrip.html:89
+msgid "Create a scrip for queue %1"
+msgstr "Skapa en scrip för kö %1"
+
+#: html/Admin/Global/Template.html:90
+#: html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Skapa en mall"
+
+#: html/SelfService/Create.html:46
+#: html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Skapa ett ärende"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Skapa nya ärenden baserade på denna scrips mall"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Skapa ärende"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Skapa ärenden i denna kö"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Skapa, radera och modifiera extrafält"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Skapa, radera och modifiera köer"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Skapa, radera och modifiera medlemmar av personliga grupper"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Skapa, radera och modifiera användare"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "SkapaSparadSök"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "SkapaÄrende"
+
+#: html/Elements/SelectDateType:47
+#: html/Ticket/Elements/ShowDates:48
+#: lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "Skapat"
+
+#. ($CustomFieldObj->Name())
+#: html/Admin/CustomFields/Modify.html:163
+#: html/Admin/Elements/EditCustomField:117
+msgid "Created CustomField %1"
+msgstr "Skapat ExtraFält %1"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Skapad i en datumintervall"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "Skapade ärenden under period, grupperade efter status"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "Skapare"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Nuvarande relationer"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Nuvarande scrips"
+
+#: html/Admin/Groups/Members.html:60
+#: html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Nuvarande medlemmar"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Nuvarande rättigheter"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Nuvarande sökning"
+
+#: html/Admin/Queues/People.html:62
+#: html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Nuvarande observatörer"
+
+#: html/Admin/Elements/SystemTabs:61
+#: html/Admin/Elements/Tabs:62
+#: html/Admin/Global/index.html:71
+#: html/Admin/Users/Modify.html:205
+#: html/Admin/index.html:77
+#: html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Extrafält"
+
+#. ($lookup)
+#: html/Admin/CustomFields/index.html:60
+msgid "Custom Fields for %1"
+msgstr "Extrafält för %1"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Specialanpassad handling utrensningskod"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Specialanpassad handling förberedelsekod"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Specialanpassat villkor"
+
+#. ($CF->Name)
+#: lib/RT/Tickets_Overlay.pm:2424
+msgid "Custom field %1 has a value."
+msgstr "Extrafält %1 har ett värde."
+
+#. ($CF->Name)
+#: lib/RT/Tickets_Overlay.pm:2420
+msgid "Custom field %1 has no value."
+msgstr "Extrafält %1 har inget värde."
+
+#. ($args{'Field'})
+#: lib/RT/Record.pm:1592
+#: lib/RT/Record.pm:1754
+msgid "Custom field %1 not found"
+msgstr "Extrafält %1 ej funnet"
+
+#. ($cf)
+#. ($obj->Name)
+#: lib/RT/Report/Tickets.pm:118
+#: lib/RT/Report/Tickets.pm:121
+msgid "Custom field '%1'"
+msgstr "Extrafält '%1'"
+
+#. ($args{'Content'}, $self->Name)
+#: lib/RT/CustomField_Overlay.pm:1157
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Extrafältvärde %1 går ej att hitta för extrafält %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Extrafältvärde går ej att radera"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Extrafält går ej att hitta"
+
+#: lib/RT/CustomField_Overlay.pm:1171
+#: lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Extrafältvärde raderat"
+
+#: html/Elements/SelectGroups:51
+#: html/Elements/SelectUsers:51
+#: lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "ExtraFält"
+
+#: html/Prefs/MyRT.html:78
+#: html/Prefs/Quicksearch.html:70
+#: html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "Anpassa"
+
+#: html/SelfService/Display.html:61
+#: html/Ticket/Create.html:203
+#: html/Ticket/Elements/ShowSummary:83
+#: html/Ticket/Elements/Tabs:116
+#: html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Datum"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Dec."
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Default Autosvarmall"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Default Kö"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Default rekvirent"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Default admin. kommentarmall"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Default admin. korrespondensmall"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Default korrespondensmall"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Default transaktionsmall"
+
+#: html/User/Delegation.html:46
+#: html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Överlåt rättigheter"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Överlåt specifika rättigheter som har tilldelats dig."
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "ÖverlåtRättigheter"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Överlåtelse"
+
+#: html/Admin/Elements/EditScrips:75
+#: html/Search/Elements/EditFormat:103
+#: html/Search/Elements/EditQuery:57
+#: html/Search/Elements/EditSearches:63
+#: html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Radera"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "Radera Mall"
+
+#. ($msg)
+#: lib/RT/SavedSearch.pm:220
+msgid "Delete failed: %1"
+msgstr "Radering misslyckades: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Radera utvalda scrips"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Radera ärenden"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr "Radera värden"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "RaderaÄrende"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Radera sökning"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Radering av detta objekt bryter referentiell integritet"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Radering av detta objekt strider mot referentiell integritet"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Avvisa"
+
+#: html/Elements/EditLinks:140
+#: html/Elements/EditLinks:66
+#: html/Elements/ShowLinks:58
+#: html/Ticket/Create.html:221
+#: html/Ticket/Elements/BulkLinks:56
+#: html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Avhängighet av till"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:718
+msgid "Dependency by %1 added"
+msgstr "Avhängighet till %1 tillagd"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:758
+msgid "Dependency by %1 deleted"
+msgstr "Avhängighet till %1 raderad"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:715
+msgid "Dependency on %1 added"
+msgstr "Avhängighet av %1 tillagd"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:755
+msgid "Dependency on %1 deleted"
+msgstr "Avhängighet av %1 raderad"
+
+#: html/Elements/EditLinks:136
+#: html/Elements/EditLinks:57
+#: html/Elements/SelectLinkType:48
+#: html/Elements/ShowLinks:48
+#: html/Ticket/Create.html:220
+#: html/Ticket/Elements/BulkLinks:52
+#: html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Avhängig av"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "Fallande"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Fallande"
+
+#: html/SelfService/Create.html:100
+#: html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Beskriv problemet nedan"
+
+#: html/Admin/CustomFields/Modify.html:61
+#: html/Admin/Elements/AddCustomFieldValue:57
+#: html/Admin/Elements/EditCustomField:60
+#: html/Admin/Elements/EditCustomFieldValues:56
+#: html/Admin/Elements/EditScrip:55
+#: html/Admin/Elements/ModifyTemplate:57
+#: html/Admin/Groups/Modify.html:71
+#: html/Admin/Queues/Modify.html:69
+#: html/Search/Elements/EditSearches:56
+#: html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Beskrivning"
+
+#: html/Search/Elements/EditFormat:71
+#: html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Visa"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Visa Tillgångskontrollista"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Visa kolumner"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Visa scrip-mallar för denna kö"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Visa scrips för denna kö"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Visa modus"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Visa sparade sökningar för denna grupp"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "Distribuerade under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Gör något och allt"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Återinläs inte denna sida."
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Ladda ner"
+
+#: html/Admin/Groups/index.html:61
+#: html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Ladda ner som tab-uppdelad fil"
+
+#: html/Elements/SelectDateType:53
+#: html/Ticket/Create.html:209
+#: html/Ticket/Elements/EditDates:66
+#: html/Ticket/Elements/Reminders:133
+#: html/Ticket/Elements/ShowDates:64
+#: lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Förfallo"
+
+#: html/Elements/Quicksearch:48
+#: html/Elements/ShowSearch:49
+#: html/index.html:107
+msgid "Edit"
+msgstr "Redigera"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "Redigera extrafält"
+
+#. ($Object->Name)
+#: html/Admin/Elements/ObjectCustomFields:92
+#: html/Admin/Queues/CustomFields.html:64
+#: html/Admin/Users/CustomFields.html:64
+msgid "Edit Custom Fields for %1"
+msgstr "Redigera extrafält för %1"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Redigera extrafält för alla grupper"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Redigera extrafält för alla användare"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54
+#: html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Redigera extrafält för ärenden i alla köer"
+
+#: html/Search/Bulk.html:188
+#: html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Redigera länkar"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Redigera söksträng"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Redigera sökning"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Templates.html:63
+msgid "Edit Templates for queue %1"
+msgstr "Redigera mallar för kö %1"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Redigera sparade sökningar för denna grupp"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60
+#: html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Redigera systemmallar"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "RedigeraSparadeSökningar"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Modify.html:140
+msgid "Editing Configuration for queue %1"
+msgstr "Redigera konfiguration för kö %1"
+
+#. ($CustomFieldObj->Name())
+#: html/Admin/CustomFields/Modify.html:167
+#: html/Admin/Elements/EditCustomField:120
+msgid "Editing CustomField %1"
+msgstr "Redigera extrafält %1"
+
+#. ($Group->Name)
+#: html/Admin/Groups/Members.html:53
+msgid "Editing membership for group %1"
+msgstr "Redigera medlemskap för grupp %1"
+
+#. ($Group->Name)
+#: html/User/Groups/Members.html:150
+msgid "Editing membership for personal group %1"
+msgstr "Redigera medlemskap för personlig grupp %1"
+
+#: lib/RT/Record.pm:1295
+#: lib/RT/Record.pm:1372
+#: lib/RT/Ticket_Overlay.pm:2518
+#: lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Antingen bas eller mål måste specificeras"
+
+#: html/Admin/Users/Modify.html:74
+#: html/Ticket/Elements/AddWatchers:77
+#: html/User/Prefs.html:65
+msgid "Email"
+msgstr "E-post"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "E-postadress används"
+
+#: html/Admin/CustomFields/Modify.html:98
+#: html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Aktiverad (Bortval av denna box deaktiverar detta extrafält)"
+
+#: html/Admin/Groups/Modify.html:84
+#: html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Aktiverad (Bortval av denna box deaktiverar denna grupp)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Aktiverad (Bortval av denna box deaktiverar denna kö)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "Aktiverade köer"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/Elements/EditCustomField:136
+#: html/Admin/Groups/Modify.html:150
+#: html/Admin/Users/Modify.html:350
+#: html/User/Groups/Modify.html:138
+msgid "Enabled status %1"
+msgstr "Aktiverad status %1"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/CustomFields/Modify.html:185
+#: html/Admin/Queues/Modify.html:162
+msgid "Enabled status: %1"
+msgstr "Aktiverad status: %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Mata in flera värden"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Mata in objekt eller URI att koppla objekt till. Separera flera inmatningar med mellanrum."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Mata in ett värde"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Mata in köer eller URI att koppla köer till. Separera flera inmatningar med mellanrum."
+
+#: html/Elements/EditLinks:119
+#: html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Mata in ärenden eller URI att koppla ärenden till. Separera flera inmatningar med mellanrum."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "Mata in upp till %1 värde"
+
+#: html/Elements/Login:76
+#: html/SelfService/Error.html:46
+#: html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Fel"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Fel i parametrar till Kö->LäggtillObservatör"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Fel i parametrar till Kö->RaderaObservatör"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Fel i parametrar till Ärende->LäggtillObservatör"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Fel i parametrar till Ärende->RaderaObservatör"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Eskalera ärenden"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Uppskattning"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Alla"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Exempel:"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Extra info"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Misslyckades med att skapa sökattribut"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "Misslyckades med att hitta 'Privilegierad'-användares pseudogrupp."
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "Misslyckades med att hitta 'Icke-privilegierad'-användares pseudogrupp"
+
+#. ($modname, $@)
+#: bin/rt-crontool:206
+msgid "Failed to load module %1. (%2)"
+msgstr "Misslyckades med att läsa in modul %1. (%2)"
+
+#. ($privacy)
+#: lib/RT/SavedSearch.pm:152
+msgid "Failed to load object for %1"
+msgstr "Misslyckades med att läsa in objekt för %1"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Feb."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Filnamn"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Fyll i flera textområden"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Fyll i flera wikitextområden"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Fyll i ett textområde"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Fyll i ett wikitextområde"
+
+#: html/Admin/CustomFields/Modify.html:107
+#: html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "Fyll i detta fält med en URL."
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "Fyll i upp till %1 textområden"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "Fyll i upp till %1 wikitextområden"
+
+#: html/Search/Elements/PickBasics:149
+#: html/Ticket/Create.html:182
+#: html/Ticket/Elements/EditBasics:97
+#: lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Slutlig Prioritet"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "SlutligPrioritet"
+
+#: html/Admin/Groups/index.html:72
+#: html/Admin/Queues/People.html:82
+#: html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Hitta grupper vars"
+
+#: html/Admin/Queues/People.html:78
+#: html/Admin/Users/index.html:70
+#: html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "Hitta personer vars"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Hitta ärenden"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Första"
+
+#: docs/design_docs/string-extraction-guide.txt:33
+#: lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "Foo Bar Baz"
+
+#: docs/design_docs/string-extraction-guide.txt:24
+#: lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "Foo!"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "Genomtvinga ändring"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Format"
+
+#. ($ticketcount)
+#: html/Search/Results.html:145
+msgid "Found %quant(%1,ticket)"
+msgstr "Hittade %quant(%1,ärende)"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Hittade Objekt"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Fre."
+
+#: html/Ticket/Elements/ShowHistory:66
+#: html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Fullständiga headers"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Hämta mall från fil"
+
+#. ($New->Name)
+#: lib/RT/Transaction_Overlay.pm:684
+msgid "Given to %1"
+msgstr "Har givits till %1"
+
+#: html/Admin/Elements/Tabs:65
+#: html/Admin/index.html:82
+msgid "Global"
+msgstr "Global"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Globala extrafält"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Global extrafältkonfiguration"
+
+#. ($pane)
+#: html/Admin/Global/MyRT.html:48
+msgid "Global portlet %1 saved."
+msgstr "Global portlet %1 sparad."
+
+#. (loc($Template->Name))
+#: html/Admin/Elements/SelectTemplate:59
+msgid "Global template: %1"
+msgstr "Global mall: %1"
+
+#: html/Admin/CustomFields/index.html:80
+#: html/Search/Results.html:90
+#: html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Starta"
+
+#: html/Admin/Groups/index.html:67
+#: html/Admin/Groups/index.html:73
+#: html/Admin/Queues/People.html:80
+#: html/Admin/Queues/People.html:84
+#: html/Admin/Queues/index.html:66
+#: html/Admin/Users/index.html:73
+#: html/Elements/RefreshHomepage:48
+#: html/Search/Results.html:74
+#: html/Ticket/Elements/EditPeople:53
+#: html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Starta!"
+
+#: html/Elements/GotoTicket:46
+#: html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Gå till ärende"
+
+#: html/Ticket/Elements/AddWatchers:67
+#: html/Ticket/Elements/ShowGroupMembers:55
+#: html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Grupp"
+
+#: html/Admin/Elements/CustomFieldTabs:68
+#: html/Admin/Elements/GroupTabs:66
+#: html/Admin/Elements/QueueTabs:82
+#: html/Admin/Elements/SystemTabs:65
+#: html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Grupprättigheter"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Grupp har redan medlem"
+
+#. ($create_msg)
+#: html/Admin/Groups/Modify.html:109
+msgid "Group could not be created: %1"
+msgstr "Grupp kan inte skapas: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Grupp har skapats"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Grupp har ingen sådan medlem"
+
+#: lib/RT/Group_Overlay.pm:963
+#: lib/RT/Queue_Overlay.pm:748
+#: lib/RT/Queue_Overlay.pm:808
+#: lib/RT/Ticket_Overlay.pm:1430
+#: lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Grupp ej funnen"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59
+#: html/Admin/Elements/SelectNewGroupMembers:57
+#: html/Admin/Elements/Tabs:56
+#: html/Admin/Global/CustomFields/index.html:69
+#: html/Admin/Groups/Members.html:86
+#: html/Admin/Queues/People.html:104
+#: html/Admin/Users/Memberships.html:53
+#: html/Admin/index.html:67
+#: html/User/Groups/Members.html:88
+#: lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Grupper"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Grupper kan inte vara medlemmar av sina medlemmar"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Grupper som matchar sökkriterium"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Grupper som denna användare tillhör"
+
+#: lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Hej!"
+
+#. ($name)
+#: docs/design_docs/string-extraction-guide.txt:40
+#: lib/RT/StyleGuide.pod:773
+msgid "Hello, %1"
+msgstr "Hej, %1"
+
+#: html/Admin/Elements/GroupTabs:70
+#: html/Admin/Elements/UserTabs:64
+#: html/Ticket/Elements/ShowHistory:53
+#: html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Historik"
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/History.html:62
+msgid "History of the group %1"
+msgstr "Historik för gruppen %1"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/History.html:62
+msgid "History of the user %1"
+msgstr "Historik för användaren %1"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Hemadress"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "Timmar"
+
+#. (6)
+#: lib/RT/Base.pm:119
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "Jag har %quant(%1,concrete mixer)."
+
+#: html/Search/Build.html:460
+#: lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Jag är vilse"
+
+#: html/Ticket/Elements/ShowBasics:48
+#: lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "ID"
+
+#: html/Admin/Users/Modify.html:65
+#: html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Identitet"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Om ett godkännande avvisas, avvisa det ursprungliga och radera väntande godkännanden"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Om ingen rekvirent har specificerats, skapa ärenden med denna rekvirent."
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "Om ingen kö har specificerats, skapa ärenden i denna kö."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Om detta verktyg var setgid, kunde en fientlig användare använda detta verktyg för att få administrativ tillgång till RT."
+
+#: html/Admin/Queues/People.html:126
+#: html/Ticket/Modify.html:60
+#: html/Ticket/ModifyAll.html:128
+#: html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Om du har uppdaterat någonting av ovanstående, se till att"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "Ogiltigt värde för %1"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "Oföränderligt fält"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Ta med deaktiverade grupper på lista."
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Ta med deaktiverade köer på lista."
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Ta med deaktiverade användare på lista."
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Inkludera sida"
+
+#: html/Search/Build.html:486
+#: lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Ofullständig sökning"
+
+#: html/Search/Build.html:483
+#: lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Ofullständig sökning"
+
+#: html/Search/Elements/PickBasics:148
+#: lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Initiell prioritet"
+
+#: lib/RT/Ticket_Overlay.pm:1163
+#: lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "InitiellPrioritet"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Inläsningsfel"
+
+#. ($CF->FriendlyPattern)
+#. ($self->FriendlyPattern)
+#: html/Elements/ValidateCustomFields:68
+#: lib/RT/CustomField_Overlay.pm:1021
+#: lib/RT/CustomField_Overlay.pm:1162
+msgid "Input must match %1"
+msgstr "Inläsning måste matcha %1"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "Internt fel"
+
+#. ($id->{error_message})
+#: lib/RT/Record.pm:308
+msgid "Internal Error: %1"
+msgstr "Internt fel: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "Ogiltig grupptyp"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Ogiltig rättighet"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Ogiltig data"
+
+#. ($msg)
+#: lib/RT/CustomField_Overlay.pm:207
+#: lib/RT/CustomField_Overlay.pm:678
+msgid "Invalid pattern: %1"
+msgstr "Ogiltigt mönster: %1"
+
+#: lib/RT/Scrip_Overlay.pm:157
+#: lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Ogiltig kö"
+
+#: lib/RT/ACE_Overlay.pm:264
+#: lib/RT/ACE_Overlay.pm:273
+#: lib/RT/ACE_Overlay.pm:279
+#: lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Ogiltig rättighet"
+
+#. ($key)
+#: lib/RT/Record.pm:283
+msgid "Invalid value for %1"
+msgstr "Ogiltigt värde för %1"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Ogiltigt värde för extrafält"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Ogiltigt värde för status"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Det är oerhört viktigt att icke-privilegierade användare inte får lov att använda detta verktyg."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Det rekommenderas att du skapar en icke-privilegierad unix-användare med korrekt gruppmedlemskap och RT för att använda detta verktyg."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Det krävs åtskilliga parametrar:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "Kursiv"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Jan."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Gå med i eller lämna denna grupp"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Juli"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "Jumbo"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Juni"
+
+#: html/Admin/Users/Modify.html:94
+#: html/User/Prefs.html:76
+msgid "Language"
+msgstr "Språk"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "Stor"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Senaste"
+
+#: html/Ticket/Elements/EditDates:59
+#: html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Senaste kontakt"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Senast kontaktad"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Senast uppdaterad"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "SenastUppdateradAv"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Tillbaka"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Låt denna användare få tillgång till RT"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Låt denna användare få rättigheter"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Länk"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Länk finns redan"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Länk kan inte skapas"
+
+#. ($TransString)
+#: lib/RT/Record.pm:1326
+msgid "Link created (%1)"
+msgstr "Länk har skapats (%1)"
+
+#. ($TransString)
+#: lib/RT/Record.pm:1387
+msgid "Link deleted (%1)"
+msgstr "Länk har raderats (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Länk ej funnen"
+
+#. ($Ticket->Id)
+#: html/Ticket/ModifyLinks.html:46
+#: html/Ticket/ModifyLinks.html:50
+msgid "Link ticket #%1"
+msgstr "Koppla ärende #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "Koppla värden till"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr "Koppling. Tillgång nekas"
+
+#: html/Ticket/Create.html:216
+#: html/Ticket/Elements/ShowSummary:89
+#: html/Ticket/Elements/Tabs:120
+#: html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Länkar"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Läs in"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Läs in sparad sökning:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "LäsinSparadSökning"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Inlästa Perl-moduler"
+
+#. ($self->Name)
+#: lib/RT/SavedSearch.pm:111
+msgid "Loaded search %1"
+msgstr "Inläst sökning %1"
+
+#: html/Admin/Users/Modify.html:138
+#: html/User/Prefs.html:126
+msgid "Location"
+msgstr "Plats"
+
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+#: html/Elements/Header:91
+msgid "Logged in as %1"
+msgstr "Inloggad som %1"
+
+#: docs/design_docs/string-extraction-guide.txt:71
+#: html/Elements/Login:100
+#: html/Elements/Login:68
+#: html/Elements/Login:84
+#: lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "Logga in"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Logga ut"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Uppslagstyp matchar ej"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Sätt ägare"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Sätt status"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Sätt förfallodatum"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Sätt lösningsdatum"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Sätt startdatum"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Sätt datum startar"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Sätt datum uppgivet"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Sätt prioritet"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Sätt kö"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Sätt ämne"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Gör denna grupp synlig för användare"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Administrera extrafält och extrafältvärde"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Administrera grupper och gruppmedlemskap"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Administrera egenskaper och konfiguration som gäller för alla köer"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Administrera köer och kö-specifika egenskaper"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Administrera användare och lösenord"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mars"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "Maj"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:731
+msgid "Member %1 added"
+msgstr "Medlem %1 tillagd"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:771
+msgid "Member %1 deleted"
+msgstr "Medlem %1 raderad"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Medlem tillagd"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Medlem raderad"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Medlem ej raderad"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Medlem av"
+
+#: html/Admin/Elements/GroupTabs:63
+#: html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Medlemmar"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:728
+msgid "Membership in %1 added"
+msgstr "Medlemskap i %1 tillagt"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:768
+msgid "Membership in %1 deleted"
+msgstr "Medlemskap i %1 raderat"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Medlemskap"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/Memberships.html:60
+msgid "Memberships of the user %1"
+msgstr "Medlemskap för användaren %1"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Sammanslagning lyckades"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Sammanslagning misslyckades. Kunde inte sätta EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Sammanslagning misslyckades. Kunde inte sätta status"
+
+#: html/Elements/EditLinks:131
+#: html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Slå samman med"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:734
+msgid "Merged into %1"
+msgstr "Sammanslagen med %1"
+
+#: html/Search/Bulk.html:143
+#: html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Meddelande"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "Meddelande visades inte eftersom det är för omfångsrikt eller består av annat än vanlig text."
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Meddelande sparades inte"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Meddelande sparat"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Meddelande om detta ärende kommer inte att skickas till..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Minuter"
+
+#: html/Search/Build.html:490
+#: lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "Icke-matchande paranteser"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Saknas en primärnyckel?: %1"
+
+#: html/Admin/Users/Modify.html:193
+#: html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Mobil"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "Modifiera Tillgångskontrollista"
+
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+#: html/Admin/Elements/ObjectCustomFields:96
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Modifiera extrafält som gäller för %1 för alla %2"
+
+#. (loc(lc($Types)))
+#: html/Admin/Elements/ObjectCustomFields:98
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Modifiera extrafält som gäller för alla %1"
+
+#: html/Admin/Global/GroupRights.html:106
+#: html/Admin/Groups/GroupRights.html:94
+#: html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Modifiera grupprättigheter"
+
+#: html/Admin/Groups/Members.html:105
+#: html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Modifiera medlemmar"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Modifiera rättigheter"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Modifiera scrip-mallar för denna kö"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Modifiera scrips för denna kö"
+
+#: html/Admin/Global/UserRights.html:75
+#: html/Admin/Groups/UserRights.html:76
+#: html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Modifiera användarrättigheter"
+
+#. ($QueueObj->Name())
+#: html/Admin/Queues/CustomField.html:66
+msgid "Modify a CustomField for queue %1"
+msgstr "Modifiera ett extrafält för kö %1"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrip.html:82
+msgid "Modify a scrip for queue %1"
+msgstr "Modifiera ett scrip för kö %1"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Modifiera ett scrip som gäller för alla köer"
+
+#. ($CF->Name)
+#: html/Admin/CustomFields/Objects.html:90
+msgid "Modify associated objects for %1"
+msgstr "Modifiera objekt associerade med %1"
+
+#. ($TicketObj->Id)
+#: html/Ticket/ModifyDates.html:46
+#: html/Ticket/ModifyDates.html:50
+msgid "Modify dates for #%1"
+msgstr "Modifiera datum för #%1"
+
+#. ($TicketObj->Id)
+#: html/Ticket/ModifyDates.html:57
+msgid "Modify dates for ticket # %1"
+msgstr "Modifiera datum för ärende # %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65
+#: html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Modifiera globala extrafält"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70
+#: html/Admin/Global/GroupRights.html:46
+#: html/Admin/Global/GroupRights.html:49
+#: html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Modifiera globala grupprättigheter"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Modifiera globala grupprättigheter."
+
+#: html/Admin/Global/UserRights.html:46
+#: html/Admin/Global/UserRights.html:49
+#: html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Modifiera globala användarrättigheter"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Modifiera globala användarrättigheter."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Modifiera gruppmetadata eller radera grupp"
+
+#. ($CustomFieldObj->Name)
+#: html/Admin/CustomFields/GroupRights.html:164
+msgid "Modify group rights for custom field %1"
+msgstr "Modifiera grupprättigheter för extrafält %1"
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/GroupRights.html:46
+#: html/Admin/Groups/GroupRights.html:50
+#: html/Admin/Groups/GroupRights.html:56
+msgid "Modify group rights for group %1"
+msgstr "Modifiera grupprättigheter för grupp %1"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/GroupRights.html:46
+#: html/Admin/Queues/GroupRights.html:50
+msgid "Modify group rights for queue %1"
+msgstr "Modifiera grupprättigheter för kö %1"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Modifiera medlemskapslista för denna grupp"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Modifiera ens eget RT-konto"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/People.html:46
+#: html/Admin/Queues/People.html:50
+msgid "Modify people related to queue %1"
+msgstr "Modifiera personer relaterade till kö %1"
+
+#. ($Ticket->id)
+#. ($Ticket->Id)
+#: html/Ticket/ModifyPeople.html:46
+#: html/Ticket/ModifyPeople.html:50
+#: html/Ticket/ModifyPeople.html:57
+msgid "Modify people related to ticket #%1"
+msgstr "Modifiera personer relaterade till ärende #%1"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/Scrips.html:67
+msgid "Modify scrips for queue %1"
+msgstr "Modifiera scrips för kö %1"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56
+#: html/Admin/Global/Scrips.html:65
+#: html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Modifiera scrips som gäller för alla köer"
+
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+#: html/Admin/Global/Template.html:102
+#: html/Admin/Global/Template.html:46
+#: html/Admin/Global/Template.html:51
+#: html/Admin/Queues/Template.html:99
+msgid "Modify template %1"
+msgstr "Modifiera mall %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Modifiera mallar som gäller för alla köer"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "Modifiera default \"RT snabbtitt\" -visningen"
+
+#. ($Group->Name)
+#: html/Admin/Groups/Modify.html:119
+#: html/User/Groups/Modify.html:107
+msgid "Modify the group %1"
+msgstr "Modifiera gruppen %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Modifiera kö-observatörerna"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/Modify.html:309
+msgid "Modify the user %1"
+msgstr "Modifiera användaren %1"
+
+#. ($Ticket->Id)
+#: html/Ticket/ModifyAll.html:58
+msgid "Modify ticket # %1"
+msgstr "Modifiera ärende # %1"
+
+#. ($TicketObj->Id)
+#: html/Ticket/Modify.html:46
+#: html/Ticket/Modify.html:49
+#: html/Ticket/Modify.html:55
+msgid "Modify ticket #%1"
+msgstr "Modifiera ärende #%1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Modifiera ärende"
+
+#. ($CustomFieldObj->Name)
+#: html/Admin/CustomFields/UserRights.html:157
+msgid "Modify user rights for custom field %1"
+msgstr "Modifiera användarrättigheter för extrafält %1"
+
+#. ($GroupObj->Name)
+#: html/Admin/Groups/UserRights.html:46
+#: html/Admin/Groups/UserRights.html:50
+#: html/Admin/Groups/UserRights.html:56
+msgid "Modify user rights for group %1"
+msgstr "Modifiera användarrättigheter för grupp %1"
+
+#. ($QueueObj->Name)
+#: html/Admin/Queues/UserRights.html:46
+#: html/Admin/Queues/UserRights.html:50
+msgid "Modify user rights for queue %1"
+msgstr "Modifiera användarrättigheter för kö %1"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "ModifieraACL"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "ModifieraExtraFält"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "ModifieraEgetMedlemskap"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "ModifieraKöObservatörer"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "ModifieraScrips"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "ModifieraSjälv"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "ModifieraMall"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "ModifieraÄrende"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "MÃ¥n."
+
+#. ($name)
+#: html/Ticket/Elements/ShowRequestor:61
+msgid "More about %1"
+msgstr "Mer om %1"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Flytta ner"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Flytta upp"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Flera"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "'Namn'-attribut måste specificeras"
+
+#. ($friendly_status)
+#: html/SelfService/Elements/MyRequests:57
+msgid "My %1 tickets"
+msgstr "Mina %1 ärenden"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "Min dag"
+
+#: html/Approvals/index.html:46
+#: html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Mina godkännanden"
+
+#: html/Search/Elements/SearchPrivacy:50
+#: html/Search/Elements/SelectSearchObject:53
+#: html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Mina sparade sökningar"
+
+#: html/Admin/CustomFields/Modify.html:58
+#: html/Admin/Elements/AddCustomFieldValue:53
+#: html/Admin/Elements/EditCustomField:55
+#: html/Admin/Elements/EditCustomFieldValues:55
+#: html/Admin/Elements/ModifyTemplate:49
+#: html/Admin/Groups/Modify.html:65
+#: html/Search/Bulk.html:157
+#: html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Namn"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Namn som används"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Aldrig"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Nya länkar"
+
+#: html/Admin/Users/Modify.html:119
+#: html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Nytt lösenord"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Nytt väntande godkännande"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Ny sökning"
+
+#: html/Admin/Elements/CustomFieldTabs:93
+#: html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Nytt extrafält"
+
+#: html/Admin/Elements/GroupTabs:77
+#: html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Ny grupp"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Nytt lösenord"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Nytt lösenordmeddelande skickat"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Ny kö"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Ny påminnelse:"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Nya rättigheter"
+
+#: html/Admin/Global/Scrip.html:63
+#: html/Admin/Global/Scrips.html:60
+#: html/Admin/Queues/Scrip.html:71
+#: html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Nytt scrip"
+
+#: html/Admin/Global/Template.html:81
+#: html/Admin/Global/Templates.html:60
+#: html/Admin/Queues/Template.html:79
+#: html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Ny mall"
+
+#: html/SelfService/Elements/Tabs:84
+#: html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Nytt ärende"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Nytt ärende finns ej"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Ny användare"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Ny användare anropad"
+
+#: html/Admin/Queues/People.html:76
+#: html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Nya observatörer"
+
+#: html/Helpers/CalPopup.html:58
+#: html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Nästa"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Nästa sida"
+
+#: html/Admin/Users/Modify.html:84
+#: html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Smeknamn"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Ingen klass definierad"
+
+#: html/Admin/CustomFields/Modify.html:166
+#: html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Inget extrafält"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Inget extrafält definierat"
+
+#: html/Admin/Groups/GroupRights.html:105
+#: html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Ingen grupp definierad"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Ingen söksträng"
+
+#: html/Admin/Queues/GroupRights.html:118
+#: html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Ingen kö definierad"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "Ingen RT-användare funnen. Var god kontakta din RT administratör.\\n"
+
+#: html/Admin/Global/Template.html:100
+#: html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Ingen mall"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Ingen handling"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Ingen kolumn specificerad"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Ingen kommentar inmatad om denna användare"
+
+#. (ref $self)
+#: lib/RT/Action/Generic.pm:185
+#: lib/RT/Condition/Generic.pm:197
+#: lib/RT/Search/ActiveTicketsInQueue.pm:77
+#: lib/RT/Search/Generic.pm:134
+#: lib/RT/Search/Googleish.pm:78
+msgid "No description for %1"
+msgstr "Ingen beskrivning för %1"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Ingen grupp specificerad"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Det hittades inga grupper som matchar sökkriterierna."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Inget bifogat meddelande"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Inget lösenord satt"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Ingen tillåtelse att skapa köer"
+
+#. ($QueueObj->Name)
+#: lib/RT/Ticket_Overlay.pm:420
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "Ingen tillåtelse att skapa ärenden i kö '%1'"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Ingen tillåtelse att skapa användare"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Ingen tillåtelse att visa det ärendet"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "Ingen tillåtelse att spara systemomfattande sökningar"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Ingen tillåtelse att visa uppdatera ärende"
+
+#: lib/RT/Queue_Overlay.pm:795
+#: lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Ingen principal specificerad"
+
+#: html/Admin/Queues/People.html:175
+#: html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Inga principaler valda."
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Det hittades inga köer som matchar sökkriterium."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Inga rättigheter funna"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Inga rättigheter tilldelade."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Ingen sökning inmatad"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "Ingen sökning att arbeta med."
+
+#: html/Elements/RT__Ticket/ColumnMap:137
+#: html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Inget ämne"
+
+#: lib/RT/Transaction_Overlay.pm:528
+#: lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Ingen transaktionstyp specificerad"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Det hittades inga användare som matchar sökkriterium."
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "Det skickades inget värde till _Set!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Ingen"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Icke-existerande fält?"
+
+#: html/Search/Chart:71
+#: html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr "Ej inställd"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Ej inloggad."
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Ej inställd"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Har ännu inte implementerats."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Anteckningar"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Meddelande kunde inte skickas"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Meddela Admin.kopia-mottagare"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Meddela Admin.kopia-mottagare som kommentar"
+
+#: etc/initialdata:93
+#: etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Meddela kopia-mottagare"
+
+#: etc/initialdata:89
+#: etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Meddela kopia-mottagare som kommentar"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Meddela andra mottagare"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Meddela andra mottagare som kommentar"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Meddela ägare"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Meddela ägare som kommentar"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Meddela ägare om dess avvisade ärende"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Meddela ägare om dess ärende har godkänts av alla godkännare"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Meddela ägare om dess ärende har godkänts av någon godkännare"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Meddela ägare och Admin.kopia-mottagare om nya ämnen väntar på deras godkännande"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "Meddela rekvirenter"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "Meddela rekvirenter och kopia-mottagare"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "Meddela rekvirenter och kopia-mottagare som kommentar"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "Meddela rekvirenter, kopia-mottagare och Admin.kopia-mottagare"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "Meddela rekvirenter, kopia-mottagare och Admin.kopia-mottagare som kommentar"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Nov."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "ELLER"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Objekt kan ej skapas"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Objekt kan ej raderas"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Objekt har skapats"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Objekt har raderats"
+
+#. ($LookupType)
+#. ($ObjectType)
+#: html/Admin/CustomFields/Objects.html:72
+#: html/Admin/Elements/ObjectCustomFields:63
+msgid "Object of type %1 cannot take custom fields"
+msgstr "Objekt av typen %1 kan inte hantera extrafält"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Objekttypmissförhållande"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Okt."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Offline"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Offline-redigeringar"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Offline upload"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Vid"
+
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+#: lib/RT/Transaction_Overlay.pm:326
+msgid "On %1, %2 wrote:"
+msgstr "Vid %1, skrev %2:"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Vid kommentar"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Vid korrespondens"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "Vid skapande"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Vid ägarbyte"
+
+#: etc/initialdata:177
+#: etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Vid prioritetsändring"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Vid köbyte"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Vid lösning"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Vid statusändring"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Vid transaktion"
+
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+#: html/Approvals/Elements/PendingMyApproval:70
+msgid "Only show approvals for requests created after %1"
+msgstr "Visa endast godkännanden för förfrågningar som skapats efter %1"
+
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+#: html/Approvals/Elements/PendingMyApproval:68
+msgid "Only show approvals for requests created before %1"
+msgstr "Visa endast godkännanden för förfrågningar som skapats före %1"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Visa endast extrafält för:"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "Öppna ärenden"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Öppna den"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Öppna ärenden"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Öppna ärenden vid korrespondens"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Alternativ"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Sortera efter"
+
+#: html/Admin/Users/Modify.html:141
+#: html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Organisation"
+
+#. ($approving->Id, $approving->Subject)
+#: html/Approvals/Elements/Approve:53
+msgid "Originating ticket: #%1"
+msgstr "Ursprungligt ärende: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Utgående e-postmeddelande om en kommentar har sparats"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Utgående e-postmeddelande har sparats"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Tiden överskriden, prioritet rör sig mot"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Egna ärenden"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "EgetÄrende"
+
+#: etc/initialdata:38
+#: html/Elements/QuickCreate:56
+#: html/Search/Elements/PickBasics:101
+#: html/Ticket/Create.html:72
+#: html/Ticket/Elements/EditBasics:61
+#: html/Ticket/Elements/EditPeople:64
+#: html/Ticket/Elements/EditPeople:65
+#: html/Ticket/Elements/Reminders:129
+#: html/Ticket/Elements/ShowPeople:48
+#: html/Ticket/Update.html:62
+#: lib/RT/ACE_Overlay.pm:110
+#: lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Ägare"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Ägare kan inte sättas."
+
+#. ($Old->Name , $New->Name)
+#: lib/RT/Transaction_Overlay.pm:672
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Ägare har godtyckligt ändrats från %1 till %2"
+
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+#: html/Elements/TicketList:78
+msgid "Page %1 of %2"
+msgstr "Sida %1 av %2"
+
+#: html/Admin/Users/Modify.html:198
+#: html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Personsökare"
+
+#: html/Elements/EditLinks:144
+#: html/Elements/EditLinks:76
+#: html/Elements/ShowLinks:68
+#: html/Ticket/Create.html:222
+#: html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Föräldrar"
+
+#: html/Elements/Login:95
+#: html/User/Prefs.html:105
+msgid "Password"
+msgstr "Lösenord"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Lösenordpåminnelse"
+
+#: lib/RT/Transaction_Overlay.pm:781
+#: lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Lösenord ändrat"
+
+#. ($RT::MinimumPasswordLength)
+#: lib/RT/User_Overlay.pm:1037
+#: lib/RT/User_Overlay.pm:214
+msgid "Password needs to be at least %1 characters long"
+msgstr "Lösenord måste vara minst %1 tecken långt"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Lösenord har satts"
+
+#. (loc_fuzzy($msg))
+#: html/User/Prefs.html:240
+msgid "Password: %1"
+msgstr "Lösenord: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Lösenord: Tillgång nekas"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Lösenord stämmer inte överens."
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Lösenord stämmer inte överens. Ditt lösenord har inte ändrats"
+
+#: html/Ticket/Elements/ShowSummary:62
+#: html/Ticket/Elements/Tabs:119
+#: html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "Personer"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Genomför en användardefinierad handling"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl-konfiguration"
+
+#: lib/RT/ACE_Overlay.pm:251
+#: lib/RT/ACE_Overlay.pm:257
+#: lib/RT/ACE_Overlay.pm:580
+#: lib/RT/ACE_Overlay.pm:590
+#: lib/RT/ACE_Overlay.pm:600
+#: lib/RT/ACE_Overlay.pm:665
+#: lib/RT/Attribute_Overlay.pm:158
+#: lib/RT/Attribute_Overlay.pm:164
+#: lib/RT/Attribute_Overlay.pm:405
+#: lib/RT/Attribute_Overlay.pm:414
+#: lib/RT/Attribute_Overlay.pm:427
+#: lib/RT/CurrentUser.pm:116
+#: lib/RT/CurrentUser.pm:125
+#: lib/RT/CustomField_Overlay.pm:1017
+#: lib/RT/CustomField_Overlay.pm:1138
+#: lib/RT/CustomField_Overlay.pm:1281
+#: lib/RT/CustomField_Overlay.pm:172
+#: lib/RT/CustomField_Overlay.pm:189
+#: lib/RT/CustomField_Overlay.pm:200
+#: lib/RT/CustomField_Overlay.pm:374
+#: lib/RT/CustomField_Overlay.pm:403
+#: lib/RT/CustomField_Overlay.pm:763
+#: lib/RT/CustomField_Overlay.pm:936
+#: lib/RT/CustomField_Overlay.pm:971
+#: lib/RT/Group_Overlay.pm:1117
+#: lib/RT/Group_Overlay.pm:1121
+#: lib/RT/Group_Overlay.pm:1130
+#: lib/RT/Group_Overlay.pm:1240
+#: lib/RT/Group_Overlay.pm:1244
+#: lib/RT/Group_Overlay.pm:1250
+#: lib/RT/Group_Overlay.pm:445
+#: lib/RT/Group_Overlay.pm:542
+#: lib/RT/Group_Overlay.pm:620
+#: lib/RT/Group_Overlay.pm:628
+#: lib/RT/Group_Overlay.pm:726
+#: lib/RT/Group_Overlay.pm:730
+#: lib/RT/Group_Overlay.pm:736
+#: lib/RT/Group_Overlay.pm:922
+#: lib/RT/Group_Overlay.pm:926
+#: lib/RT/Group_Overlay.pm:939
+#: lib/RT/Queue_Overlay.pm:1054
+#: lib/RT/Queue_Overlay.pm:140
+#: lib/RT/Queue_Overlay.pm:158
+#: lib/RT/Queue_Overlay.pm:657
+#: lib/RT/Queue_Overlay.pm:667
+#: lib/RT/Queue_Overlay.pm:681
+#: lib/RT/Queue_Overlay.pm:819
+#: lib/RT/Queue_Overlay.pm:828
+#: lib/RT/Queue_Overlay.pm:841
+#: lib/RT/Scrip_Overlay.pm:149
+#: lib/RT/Scrip_Overlay.pm:160
+#: lib/RT/Scrip_Overlay.pm:224
+#: lib/RT/Scrip_Overlay.pm:538
+#: lib/RT/Template_Overlay.pm:108
+#: lib/RT/Template_Overlay.pm:277
+#: lib/RT/Ticket_Overlay.pm:1357
+#: lib/RT/Ticket_Overlay.pm:1367
+#: lib/RT/Ticket_Overlay.pm:1381
+#: lib/RT/Ticket_Overlay.pm:1522
+#: lib/RT/Ticket_Overlay.pm:1532
+#: lib/RT/Ticket_Overlay.pm:1546
+#: lib/RT/Ticket_Overlay.pm:1663
+#: lib/RT/Ticket_Overlay.pm:1983
+#: lib/RT/Ticket_Overlay.pm:2126
+#: lib/RT/Ticket_Overlay.pm:2296
+#: lib/RT/Ticket_Overlay.pm:2346
+#: lib/RT/Ticket_Overlay.pm:2525
+#: lib/RT/Ticket_Overlay.pm:2538
+#: lib/RT/Ticket_Overlay.pm:2614
+#: lib/RT/Ticket_Overlay.pm:2627
+#: lib/RT/Ticket_Overlay.pm:2748
+#: lib/RT/Ticket_Overlay.pm:2762
+#: lib/RT/Ticket_Overlay.pm:2990
+#: lib/RT/Ticket_Overlay.pm:3000
+#: lib/RT/Ticket_Overlay.pm:3005
+#: lib/RT/Ticket_Overlay.pm:3224
+#: lib/RT/Ticket_Overlay.pm:3228
+#: lib/RT/Ticket_Overlay.pm:3371
+#: lib/RT/Ticket_Overlay.pm:3497
+#: lib/RT/Transaction_Overlay.pm:516
+#: lib/RT/Transaction_Overlay.pm:523
+#: lib/RT/Transaction_Overlay.pm:551
+#: lib/RT/Transaction_Overlay.pm:558
+#: lib/RT/User_Overlay.pm:1176
+#: lib/RT/User_Overlay.pm:1856
+#: lib/RT/User_Overlay.pm:369
+#: lib/RT/User_Overlay.pm:735
+#: lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Tillgång nekas"
+
+#: lib/RT/Template_Overlay.pm:238
+#: lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "Tillgång nekas"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "Tillgång nekas"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "Personliga grupper"
+
+#: html/User/Groups/index.html:51
+#: html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "Personliga grupper"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "Personliga grupper:"
+
+#: html/Admin/Users/Modify.html:180
+#: html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefonnummer"
+
+#: html/Elements/Header:93
+#: html/Elements/Tabs:91
+#: html/SelfService/Elements/Tabs:95
+#: html/SelfService/Prefs.html:46
+#: html/User/Prefs.html:46
+#: html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Preferenser"
+
+#. ($pane, $UserObj->Name)
+#: html/Admin/Users/MyRT.html:75
+msgid "Preferences %1 for user %2 ."
+msgstr "Preferenser %1 för användare %2 ."
+
+#. ($pane)
+#: html/Prefs/MyRT.html:141
+msgid "Preferences saved for %1."
+msgstr "Preferenser har sparats för %1."
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Förbered tömning"
+
+#: html/Helpers/CalPopup.html:56
+#: html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Föreg."
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Föregående sida"
+
+#. ($args{'PrincipalId'})
+#: lib/RT/ACE_Overlay.pm:157
+#: lib/RT/ACE_Overlay.pm:239
+#: lib/RT/ACE_Overlay.pm:569
+msgid "Principal %1 not found."
+msgstr "Principal %1 ej funnen."
+
+#: html/Search/Elements/PickBasics:147
+#: html/Ticket/Create.html:181
+#: html/Ticket/Elements/EditBasics:92
+#: html/Ticket/Elements/ShowBasics:72
+#: lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Prioritet"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Prioritet börjar vid"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Privat:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Privilegierad"
+
+#. (loc_fuzzy($msg))
+#: html/Admin/Users/Modify.html:342
+#: html/User/Prefs.html:231
+msgid "Privileged status: %1"
+msgstr "Privilegierad status: %1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Privilegierade användare"
+
+#: etc/initialdata:23
+#: etc/initialdata:29
+#: etc/initialdata:35
+#: etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "Pseudogrupp för internt bruk"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Söksträngsbyggare"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "Söksträng:"
+
+#: html/Elements/QueueSummary:48
+#: html/Elements/QuickCreate:54
+#: html/Search/Elements/PickBasics:71
+#: html/SelfService/Create.html:54
+#: html/Ticket/Create.html:62
+#: html/Ticket/Elements/EditBasics:57
+#: html/Ticket/Elements/ShowBasics:76
+#: html/Tools/Reports/CreatedByDates.html:85
+#: html/Tools/Reports/ResolvedByDates.html:86
+#: html/Tools/Reports/ResolvedByOwner.html:66
+#: html/User/Elements/DelegateRights:101
+#: lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Kö"
+
+#. ($id)
+#. ($Queue)
+#: html/Admin/Queues/CustomField.html:63
+#: html/Admin/Queues/Scrip.html:61
+#: html/Admin/Queues/Scrips.html:69
+#: html/Admin/Queues/Templates.html:65
+msgid "Queue %1 not found"
+msgstr "Kö %1 ej funnen"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Könamn"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Kö finns redan"
+
+#: lib/RT/Queue_Overlay.pm:374
+#: lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Kö kan ej skapas"
+
+#: html/Ticket/Create.html:244
+#: lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Kö kan ej inläsas."
+
+#: docs/design_docs/string-extraction-guide.txt:83
+#: lib/RT/Queue_Overlay.pm:384
+#: lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Kö har skapats"
+
+#: html/SelfService/Display.html:126
+#: lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Kö ej funnen"
+
+#: html/Admin/Elements/Tabs:59
+#: html/Admin/index.html:72
+msgid "Queues"
+msgstr "Köer"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Köer som jag administrerar"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Köer som jag är en Admin.kopia-mottagare för"
+
+#: html/Elements/Quicksearch:47
+#: html/Prefs/Elements/Tabs:58
+#: html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Snabbsökning"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Snabbt ärendeskapande"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#. ($RT::VERSION, $RT::rtname)
+#: docs/design_docs/string-extraction-guide.txt:70
+#: lib/RT/StyleGuide.pod:796
+msgid "RT %1 for %2"
+msgstr "RT %1 för %2"
+
+#: html/Admin/index.html:46
+#: html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT-administration"
+
+#: html/Elements/Error:63
+#: html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT-fel"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RT-variabler"
+
+#: html/Admin/Elements/SystemTabs:71
+#: html/Admin/Elements/UserTabs:67
+#: html/Admin/Global/MyRT.html:1
+#: html/Admin/Global/MyRT.html:12
+#: html/Admin/Global/MyRT.html:4
+#: html/Admin/Global/index.html:84
+#: html/Admin/Users/MyRT.html:21
+#: html/Prefs/MyRT.html:66
+#: html/Prefs/MyRT.html:78
+#: html/User/Elements/Tabs:65
+#: html/index.html:1
+#: html/index.html:75
+msgid "RT at a glance"
+msgstr "RT-snabbtitt"
+
+#. ($UserObj->Name)
+#: html/Admin/Users/MyRT.html:30
+msgid "RT at a glance for the user %1"
+msgstr "RT-snabbtitt för användaren %1"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT kan innehålla innehåll från annan webbtjänst när detta extrafält visas."
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT kan göra alternativen för detta extrafält till hyperlänkar till annan tjänst."
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT har inte utrymme för din session."
+
+#. ($RT::rtname)
+#: html/Elements/Logo:49
+#: html/Elements/PageLayout:172
+msgid "RT for %1"
+msgstr "RT för %1"
+
+#: html/Search/Simple.html:60
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT kommer att söka efter allt annat som du matar in i ärendeämnen."
+
+#: html/Admin/CustomFields/Modify.html:108
+#: html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT kommer att ersätta <tt>__id__</tt> och <tt>__ExtraFält__</tt> med register-ID och extrafältvärde respektive"
+
+#: html/Admin/Users/Modify.html:79
+#: html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Fullständigt namn"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:725
+msgid "Reference by %1 added"
+msgstr "Referens från %1 tillagd"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:765
+msgid "Reference by %1 deleted"
+msgstr "Referens från %1 raderad"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:722
+msgid "Reference to %1 added"
+msgstr "Referens till %1 tillagd"
+
+#. ($value)
+#: lib/RT/Transaction_Overlay.pm:762
+msgid "Reference to %1 deleted"
+msgstr "Referens till %1 raderad"
+
+#: html/Elements/EditLinks:103
+#: html/Elements/EditLinks:156
+#: html/Elements/ShowLinks:92
+#: html/Ticket/Create.html:225
+#: html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Refererad till av"
+
+#: html/Elements/EditLinks:152
+#: html/Elements/EditLinks:94
+#: html/Elements/SelectLinkType:49
+#: html/Elements/ShowLinks:82
+#: html/Ticket/Create.html:224
+#: html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Refererar till"
+
+#. ($value/60)
+#: html/Elements/Refresh:57
+msgid "Refresh this page every %1 minutes."
+msgstr "Återinläs denna sida var %1 minut."
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:811
+msgid "Reminder '%1' added"
+msgstr "PÃ¥minnelse '%1' tillagd"
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:824
+msgid "Reminder '%1' completed"
+msgstr "Påminnelse '%1' genomförd"
+
+#. ($ticket->Subject)
+#: lib/RT/Transaction_Overlay.pm:817
+msgid "Reminder '%1' reopened"
+msgstr "Påminnelse '%1' öppnad igen"
+
+#. ($Ticket->Id)
+#: html/Ticket/Reminders.html:46
+msgid "Reminder ticket #%1"
+msgstr "Påminnelse ärende #%1"
+
+#: html/Elements/MyReminders:48
+#: html/Ticket/Elements/ShowSummary:75
+#: html/Ticket/Elements/Tabs:122
+#: html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "PÃ¥minnelser"
+
+#. ($Ticket->Id)
+#: html/Ticket/Reminders.html:50
+msgid "Reminders for ticket #%1"
+msgstr "Påminnelse för ärende #%1"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Ta bort Admin.kopia"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Ta bort kopia"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "Ta bort rekvirent"
+
+#: html/Ticket/Elements/ShowTransaction:179
+#: html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Svar"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Svarsadress"
+
+#: html/Search/Bulk.html:129
+#: html/Ticket/ModifyAll.html:94
+#: html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "Svar till rekvirenter"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Svar på ärenden"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "SvarTillÄrende"
+
+#: html/Tools/Elements/Tabs:59
+#: html/Tools/Reports/index.html:46
+#: html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Rapporter"
+
+#: etc/initialdata:44
+#: lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "Rekvirent"
+
+#: html/SelfService/Create.html:63
+#: html/Ticket/Create.html:80
+#: html/Ticket/Elements/EditPeople:69
+#: html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "Rekvirenter"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "Förfrågningar borde förfalla"
+
+#. ('Object')
+#: lib/RT/Attribute_Overlay.pm:146
+msgid "Required parameter '%1' not specified"
+msgstr "Nödvändig parameter '%1' ej specificerad"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Reset"
+
+#: html/Admin/Users/MyRT.html:15
+#: html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr "Reset till default"
+
+#: html/Admin/Users/Modify.html:183
+#: html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Hemma"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Lös"
+
+#. ($TicketObj->id, $TicketObj->Subject)
+#: html/Ticket/Update.html:156
+msgid "Resolve ticket #%1 (%2)"
+msgstr "Lös ärende #%1 (%2)"
+
+#: etc/initialdata:323
+#: html/Elements/SelectDateType:49
+#: lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Löst"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Löst av ägare"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Löst i datumordning"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Lösta ärenden under period, grupperade efter ägare"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Lösta ärenden, grupperade efter ägare"
+
+#: html/Elements/ListActions:46
+#: html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Resultat"
+
+#: html/Admin/Users/Modify.html:126
+#: html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Skriv in lösenord igen"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "GÃ¥ tillbaka"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Rättighet överlämnad"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Rättighet tilldelad"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Rättighet inläst"
+
+#: lib/RT/ACE_Overlay.pm:695
+#: lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Rättighet kan inte återkallas"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Rättighet ej funnen"
+
+#: lib/RT/ACE_Overlay.pm:560
+#: lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Rättighet ej inläst."
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Rättighet återkallad"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Rättigheter"
+
+#. ($object_type)
+#: html/Admin/CustomFields/GroupRights.html:129
+#: lib/RT/Interface/Web.pm:961
+msgid "Rights could not be granted for %1"
+msgstr "Rättigheter kan inte tilldelas %1"
+
+#. ($object_type)
+#: html/Admin/CustomFields/GroupRights.html:156
+#: lib/RT/Interface/Web.pm:990
+msgid "Rights could not be revoked for %1"
+msgstr "Rättigheter kan inte återkallas för %1"
+
+#: html/Admin/Global/GroupRights.html:72
+#: html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Roller"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Rader per box"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Rader per sida"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Lör."
+
+#: html/Prefs/MyRT.html:72
+#: html/Prefs/Quicksearch.html:64
+#: html/Prefs/Search.html:69
+#: html/Search/Elements/EditSearches:70
+#: html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Spara"
+
+#: html/Admin/Global/Template.html:67
+#: html/Admin/Groups/Modify.html:88
+#: html/Admin/Queues/Modify.html:111
+#: html/Admin/Queues/People.html:126
+#: html/Admin/Users/Modify.html:239
+#: html/Prefs/Quicksearch.html:64
+#: html/Prefs/SearchOptions.html:63
+#: html/SelfService/Prefs.html:58
+#: html/Ticket/Modify.html:60
+#: html/Ticket/ModifyAll.html:127
+#: html/Ticket/ModifyDates.html:60
+#: html/Ticket/ModifyLinks.html:61
+#: html/Ticket/ModifyPeople.html:60
+#: html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "Spara ändringar"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Spara preferenser"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "Spara ändringar"
+
+#. ($name)
+#: lib/RT/SavedSearch.pm:173
+msgid "Saved search %1"
+msgstr "Spara sökning %1"
+
+#. ($id)
+#. ($scrip->Id)
+#: html/Admin/Elements/ListGlobalScrips:60
+#: html/Admin/Global/Scrip.html:77
+#: html/Admin/Queues/Scrip.html:84
+msgid "Scrip #%1"
+msgstr "Scrip #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Scrip har skapats"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Scrip-fält"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Scrip har raderats"
+
+#: html/Admin/Elements/QueueTabs:67
+#: html/Admin/Elements/SystemTabs:54
+#: html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Scrips"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Scrips som gäller alla köer"
+
+#: html/Elements/SimpleSearch:48
+#: html/Search/Simple.html:65
+msgid "Search"
+msgstr "Sökning"
+
+#: html/Prefs/SearchOptions.html:47
+#: html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Sökpreferenser"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Sökattributinläsningsfel"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Sök efter godkännanden"
+
+#: html/Search/Simple.html:69
+msgid "Search for tickets"
+msgstr "Sök efter ärenden"
+
+#: NOT FOUND IN SOURCE
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name"
+msgstr "Sök efter ärenden. Skriv <strong>id</strong> nummer, <strong>köer</strong> med namn"
+
+#: html/Search/Simple.html:57
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name"
+msgstr ""
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Sökalternativ"
+
+#. ($PrimaryGroupBy)
+#: html/Search/Chart.html:56
+msgid "Search results grouped by %1"
+msgstr "Sökresultat grupperade efter %1"
+
+#. ($msg)
+#: lib/RT/SavedSearch.pm:203
+msgid "Search update: %1"
+msgstr "Sökuppdatering: %1"
+
+#: html/Search/Simple.html:59
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "Att söka igenom all text i alla ärenden kan ta lång tid, men om du måste göra det kan du söka efter vilket ord som helst i hela ärendehistoriken genom att skriva <b>fulltext:<i>word</i></b>."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Säkerhet:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "Se även:"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Se extrafält"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Se exakt utgående e-postmeddelanden och deras mottagare"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Se privata kommentarer för ärende"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Se sammanfattningar av ärenden"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "SeExtraFält"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "SeGrupp"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "SeKö"
+
+#: html/Admin/CustomFields/index.html:46
+#: html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Välj ett extrafält"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Välj en grupp"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Välj en kö"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Välj en kö för ditt nya ärende"
+
+#: html/Admin/Users/index.html:46
+#: html/Admin/Users/index.html:49
+#: html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Välj en användare"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Välj ett extrafält"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Välj extrafält för alla användargrupper"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Välj extrafält för alla användare"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Välj extrafält för ärenden i alla köer"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Välj extrafält för transaktioner på alla ärenden i alla köer"
+
+#: html/Admin/Elements/GroupTabs:75
+#: html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Välj grupp"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Välj flera värden"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Välj ett värde"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Välj kö"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "Välj kö som skall visas på \"RT-snabbtitt\" -sidan"
+
+#: html/Admin/Global/Scrip.html:59
+#: html/Admin/Global/Scrips.html:57
+#: html/Admin/Queues/Scrip.html:67
+#: html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Välj scrip"
+
+#: html/Admin/Global/Template.html:78
+#: html/Admin/Global/Templates.html:57
+#: html/Admin/Queues/Template.html:76
+#: html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Välj mall"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "Välj upp till %1 värden"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Välj användare"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Valda extrafält"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Valda objekt"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Urval modifierat. Var god spara dina ändringar"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Skicka e-post till alla observatörer"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Skicka e-post till alla observatörer som en \"kommentar\""
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "Skicka e-post till rekvirenter och kopia-mottagare"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "Skicka e-post till rekvirenter och kopia-mottagare som en kommentar"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "Skicka ett meddelande till rekvirenterna"
+
+#: etc/initialdata:125
+#: etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Skicka e-post till explicita kopia-mottagare och hemlig kopia-mottagare"
+
+#: etc/initialdata:94
+#: etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Skicka e-post till kopia-mottagarna"
+
+#: etc/initialdata:90
+#: etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Skicka e-post till kopia-mottagarna som en kommentar"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Skicka e-post till de administrativa kopia-mottagarna"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Skicka e-post till de administrativa kopia-mottagarna som en kommentar"
+
+#: etc/initialdata:82
+#: etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Skicka e-post till ägaren"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Sep."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Visa"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Visa godkännanden"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Visa kolumner"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Visa resultat"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Visa godkända förfrågningar"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Visa grunddata"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Visa avvisade förfrågningar"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Visa detaljer"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Visa väntande förfrågningar"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Visa förfrågningar som väntar på andra godkännanden"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "VisaACL"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "VisaKonfigurationTab"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "VisaUtgåendeE-post"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "VisaSparadeSökningar"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "VisaScrips"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "VisaMall"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "VisaÄrende"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "VisaÄrendeKommentarer"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Registrera som ärenderekvirent eller ärende eller kö-kopia"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Registrera som ärende eller kö-Admin.kopia"
+
+#: html/Admin/Users/Modify.html:230
+#: html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Signatur"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Enkel sökning"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "En enda"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Storlek"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Hoppa över meny"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Liten"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "Vissa webbläsare läser kanske bara in innehåll från samma domän som din RT-server."
+
+#: html/Admin/Elements/AddCustomFieldValue:49
+#: html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sortera"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "Steg"
+
+#: html/Elements/SelectDateType:48
+#: html/Ticket/Elements/EditDates:53
+#: html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Påbörjad"
+
+#: html/Elements/SelectDateType:52
+#: html/Ticket/Create.html:208
+#: html/Ticket/Elements/EditDates:48
+#: html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "Börjar"
+
+#: html/Admin/Users/Modify.html:162
+#: html/User/Prefs.html:145
+msgid "State"
+msgstr "Läge"
+
+#: html/Search/Elements/PickBasics:87
+#: html/SelfService/Update.html:57
+#: html/Ticket/Create.html:66
+#: html/Ticket/Elements/EditBasics:53
+#: html/Ticket/Elements/ShowBasics:52
+#: html/Ticket/Update.html:59
+#: lib/RT/Ticket_Overlay.pm:1166
+#: lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Status"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Statusändring"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Stjäl"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Stjäl ärenden"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "StjälÄrende"
+
+#. ($Old->Name)
+#: lib/RT/Transaction_Overlay.pm:678
+msgid "Stolen from %1"
+msgstr "Stulen från %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "Stulen från %1 "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Stil"
+
+#: html/Elements/QuickCreate:52
+#: html/Elements/SelectAttachmentField:47
+#: html/Search/Bulk.html:132
+#: html/SelfService/Create.html:79
+#: html/SelfService/Update.html:65
+#: html/Ticket/Create.html:108
+#: html/Ticket/Elements/EditBasics:48
+#: html/Ticket/Elements/Reminders:125
+#: html/Ticket/ModifyAll.html:100
+#: html/Ticket/Update.html:82
+#: lib/RT/Ticket_Overlay.pm:1162
+#: lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Ämne"
+
+#. ($self->Data)
+#: docs/design_docs/string-extraction-guide.txt:89
+#: lib/RT/StyleGuide.pod:815
+#: lib/RT/Transaction_Overlay.pm:700
+msgid "Subject changed to %1"
+msgstr "Ämne ändrat till %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Skicka"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Genomförd"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Sön."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "SuperAnvändare"
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "System"
+
+#: html/Admin/Elements/ToolTabs:54
+#: html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Systemkonfiguration"
+
+#: html/Admin/CustomFields/GroupRights.html:128
+#: html/Admin/CustomFields/GroupRights.html:155
+#: html/Admin/CustomFields/UserRights.html:128
+#: html/Admin/CustomFields/UserRights.html:98
+#: html/Admin/Elements/SelectRights:106
+#: lib/RT/ACE_Overlay.pm:584
+#: lib/RT/Interface/Web.pm:960
+#: lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Systemfel"
+
+#. ($msg)
+#: lib/RT/Transaction_Overlay.pm:224
+#: lib/RT/Transaction_Overlay.pm:230
+msgid "System Error: %1"
+msgstr "Systemfel: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Systemverktyg"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Systemfel. Rättighet inte överlåten."
+
+#: lib/RT/ACE_Overlay.pm:163
+#: lib/RT/ACE_Overlay.pm:228
+#: lib/RT/ACE_Overlay.pm:323
+#: lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Systemfel. Rättighet inte tilldelad."
+
+#: html/Admin/CustomFields/GroupRights.html:58
+#: html/Admin/Global/GroupRights.html:56
+#: html/Admin/Groups/GroupRights.html:58
+#: html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Systemgrupper"
+
+#: etc/initialdata:41
+#: etc/initialdata:47
+#: etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "SystemRollgrupp för internt bruk"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: etc/initialdata:603
+#: html/Search/Elements/EditFormat:72
+#: html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Ta"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Ta ärenden"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "TaÄrende"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Taget"
+
+#: html/Admin/Elements/EditScrip:71
+#: html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Mall"
+
+#. ($TemplateObj->Id())
+#: html/Admin/Global/Template.html:112
+#: html/Admin/Queues/Template.html:113
+msgid "Template #%1"
+msgstr "Mall #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Mall har raderats"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr "Mall är obligatorisk parameter"
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Mall ej funnen"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Mall har parsats"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "Mallparsfel"
+
+#: html/Admin/Elements/QueueTabs:70
+#: html/Admin/Elements/SystemTabs:57
+#: html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Mallar"
+
+#: lib/RT/CustomField_Overlay.pm:943
+#: lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Det är redan det nuvarande värdet"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Det är inte ett värde för detta extrafält"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Det är samma värde"
+
+#: lib/RT/ACE_Overlay.pm:305
+#: lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Principalen har redan den rättigheten"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:753
+msgid "That principal is already a %1 for this queue"
+msgstr "Principalen är redan en %1 för denna kö"
+
+#. ($self->loc($args{'Type'}))
+#: lib/RT/Ticket_Overlay.pm:1435
+msgid "That principal is already a %1 for this ticket"
+msgstr "Principalen är redan en %1 för detta ärende"
+
+#. ($args{'Type'})
+#: lib/RT/Queue_Overlay.pm:852
+msgid "That principal is not a %1 for this queue"
+msgstr "Principalen är inte en %1 för denna kö"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Kön finns ej"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Ärendet har olösta avhängigheter"
+
+#: lib/RT/Action/CreateTickets.pm:710
+#: lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Ärendet tillhör redan den användaren"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Användaren finns ej"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Användaren är redan privilegierad"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Användaren är redan icke-privilegierad"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Användaren är nu privilegierad"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Användaren är nu icke-privilegierad"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Användaren äger kanske inte ärenden i den kön"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Det är inte något numeriskt ID"
+
+#: html/SelfService/Display.html:53
+#: html/Ticket/Create.html:177
+#: html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Grunddata"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "Kopia för ett ärende"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "Adminstrativ kopia för ett ärende"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Följande kommando kommer att ta fram alla aktiva ärenden i kön 'general' och sätta deras prioritet som 99 om de inte rörts på 4 timmar:"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Det nya värdet har satts."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Ägaren till ett nytt ärende"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Rekvirenten till ett ärende"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Dessa kommentarer är vanligtvis inte synliga för användaren"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Detta extrafält gäller inte för det objektet"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Denna delen är endast tillgänglig för systemadministratörer"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Meddelandet kommer att skickas till..."
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Detta verktyg ger användaren möjlighet att köra godtyckliga perl-moduler inifrån RT."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Denna transaktion tycks inte ha något innehåll"
+
+#. ($rows)
+#: html/Ticket/Elements/ShowRequestor:70
+msgid "This user's %1 highest priority tickets"
+msgstr "Denna användares %1 högst prioriterade ärenden"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Tor."
+
+#. ($Ticket->Id, $Ticket->Subject)
+#: html/Ticket/ModifyAll.html:46
+#: html/Ticket/ModifyAll.html:50
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Ärende #%1 Jumbo uppdatering: %2"
+
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#: html/Approvals/Elements/ShowDependency:67
+msgid "Ticket #%1: %2"
+msgstr "Ärende #%1: %2"
+
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+#: lib/RT/Action/CreateTickets.pm:1350
+#: lib/RT/Action/CreateTickets.pm:1359
+#: lib/RT/Action/CreateTickets.pm:605
+#: lib/RT/Action/CreateTickets.pm:729
+#: lib/RT/Action/CreateTickets.pm:741
+msgid "Ticket %1"
+msgstr "Ärende %1"
+
+#. ($self->Id, $QueueObj->Name)
+#: lib/RT/Ticket_Overlay.pm:755
+#: lib/RT/Ticket_Overlay.pm:775
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Ärende %1 har skapats i kö '%2'"
+
+#. ($Ticket->Id, $_)
+#: html/Search/Bulk.html:377
+msgid "Ticket %1: %2"
+msgstr "Ärende %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Ärende extrafält"
+
+#. ($Ticket->Id, $Ticket->Subject)
+#: html/Ticket/History.html:46
+#: html/Ticket/History.html:49
+msgid "Ticket History # %1 %2"
+msgstr "Ärendehistorik # %1 %2"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Ärende löst"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69
+#: html/Admin/Global/CustomFields/index.html:81
+#: lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Ärendetransaktioner"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Ärendeinnehåll"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Ärendeinnehållstyp"
+
+#: lib/RT/Ticket_Overlay.pm:603
+#: lib/RT/Ticket_Overlay.pm:617
+#: lib/RT/Ticket_Overlay.pm:628
+#: lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Ärende kan inte skapas på grund av ett internt fel"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Ärende metadata"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Ärendestatus ändrad"
+
+#. (ref $self)
+#: lib/RT/Search/FromSQL.pm:82
+msgid "TicketSQL search module"
+msgstr "ÄrendeSQL sökmodul"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64
+#: html/Admin/Global/CustomFields/index.html:75
+#: html/Elements/Tabs:71
+#: html/Search/Elements/Chart:109
+#: lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Ärenden"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "Ärenden har skapats efter"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "Ärenden har skapats före"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "Ärenden lösta efter"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "Ärenden lösta före"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Ärenden som är beroende av detta godkännande:"
+
+#: html/Search/Elements/PickBasics:134
+#: html/Ticket/Create.html:183
+#: html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Uppskattad tid"
+
+#: html/Search/Elements/PickBasics:135
+#: html/Ticket/Create.html:196
+#: html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Resterande tid"
+
+#: html/Search/Elements/PickBasics:133
+#: html/Ticket/Create.html:189
+#: html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Använd tid"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Resterande tid"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Tid för visning"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Använd tid"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "AnvändTid"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Titel"
+
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+#: html/Elements/Footer:62
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Förfrågan om support, utbildning, kundutveckling eller licensiering, var god kontakta %1."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Uppgivet"
+
+#: html/Admin/Elements/Tabs:68
+#: html/Admin/index.html:88
+#: html/Elements/Tabs:74
+#: html/Tools/index.html:46
+#: html/Tools/index.html:49
+msgid "Tools"
+msgstr "Verktyg"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "Totalt"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Transaktion"
+
+#. ($self->Data)
+#: lib/RT/Transaction_Overlay.pm:805
+msgid "Transaction %1 purged"
+msgstr "Transaktion %1 har tömts"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Transaktion har skapats"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Transaktion extrafält"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transaktion->Skapa gick inte eftersom du inte specificerade en objekttyp och ID"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Transaktioner är oföränderliga"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Tis."
+
+#: html/Admin/CustomFields/Modify.html:66
+#: html/Admin/Elements/EditCustomField:65
+#: html/Ticket/Elements/AddWatchers:54
+#: html/Ticket/Elements/AddWatchers:65
+#: html/Ticket/Elements/AddWatchers:75
+#: lib/RT/Ticket_Overlay.pm:1168
+#: lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Typ"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Ej implementerad"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unix-inloggning"
+
+#. ($ContentEncoding)
+#. ($self->ContentEncoding)
+#: lib/RT/Attachment_Overlay.pm:289
+#: lib/RT/Record.pm:861
+msgid "Unknown ContentEncoding %1"
+msgstr "Okänd InnehållsKodning %1"
+
+#: html/Search/Build.html:455
+#: lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Okänt fält: $nyckel"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Obegränsat"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Ej namngiven sökning"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Icke-privilegierad"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Ej valda extrafält"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Ej valda objekt"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Ej tagen"
+
+#: html/Admin/Elements/EditScrip:128
+#: html/Elements/RT__Ticket/ColumnMap:302
+#: html/Search/Bulk.html:193
+#: html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Uppdatera"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Uppdatera ärende"
+
+#: html/Search/Bulk.html:126
+#: html/Ticket/ModifyAll.html:87
+#: html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Uppdatera typ"
+
+#: html/Search/Bulk.html:200
+#: html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Uppdatera flera ärenden"
+
+#: lib/RT/Action/CreateTickets.pm:750
+#: lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Uppdatera icke-inlästa."
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Uppdatera ärende"
+
+#. ($Ticket->id)
+#: html/SelfService/Update.html:112
+#: html/SelfService/Update.html:47
+msgid "Update ticket #%1"
+msgstr "Uppdatera ärende #%1"
+
+#. ($TicketObj->id, $TicketObj->Subject)
+#: html/Ticket/Update.html:158
+msgid "Update ticket #%1 (%2)"
+msgstr "Uppdatera ärende #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748
+#: lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Uppdateringstyp är varken korrespondens eller kommentar."
+
+#: html/Elements/SelectDateType:54
+#: html/Ticket/Elements/ShowDates:72
+#: lib/RT/CustomField_Overlay.pm:1284
+#: lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Uppdaterad"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Överför"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Överför flera filer"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Överför flera bilder"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Överför en fil"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Överför en bild"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "Överför upp till %1 filer"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "Överför upp till %1 bilder"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Överför dina ändringar"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Använd annat RT-administrativt verktyg"
+
+#. ($args{'Owner'})
+#: lib/RT/Ticket_Overlay.pm:506
+msgid "User '%1' could not be found."
+msgstr "Det gick inte att hitta användare '%1'."
+
+#: etc/initialdata:132
+#: etc/initialdata:206
+msgid "User Defined"
+msgstr "Användardefinierad"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Användardefinierade villkor och handlingar"
+
+#: html/Admin/Elements/CustomFieldTabs:72
+#: html/Admin/Elements/GroupTabs:68
+#: html/Admin/Elements/QueueTabs:85
+#: html/Admin/Elements/SystemTabs:68
+#: html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Användarrättigheter"
+
+#. ($msg)
+#: html/Admin/Users/Modify.html:301
+msgid "User could not be created: %1"
+msgstr "Det gick inte att skapa användare: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Användare har skapats"
+
+#: html/Admin/CustomFields/GroupRights.html:74
+#: html/Admin/Global/GroupRights.html:88
+#: html/Admin/Groups/GroupRights.html:75
+#: html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Användardefinierade grupper"
+
+#: lib/RT/User_Overlay.pm:592
+#: lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Användare har lästs in"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Användardefinierade grupper"
+
+#: html/Admin/Users/Modify.html:69
+#: html/Elements/Login:90
+#: html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Användarnamn"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55
+#: html/Admin/Elements/SelectNewGroupMembers:47
+#: html/Admin/Elements/Tabs:53
+#: html/Admin/Global/CustomFields/index.html:64
+#: html/Admin/Groups/Members.html:76
+#: html/Admin/Queues/People.html:89
+#: html/Admin/index.html:62
+#: html/User/Groups/Members.html:79
+#: lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Användare"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Användare som matchar kriterier"
+
+#. ($transaction->id)
+#: bin/rt-crontool:134
+msgid "Using transaction #%1..."
+msgstr "Använder transaktion #%1..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Giltig söksträng"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Bekräftelse"
+
+#: html/Admin/CustomFields/Modify.html:130
+#: html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "Värden"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Observera"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "ObserveraSomAdmin.kopia"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Observatörer"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Ons."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "Vad jag gjorde i dag"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "När ett ärende har godkänts av alla godkännare, lägg till korrespondens till originalärendet"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "När ett ärende har godkänts av någon godkännare, lägg till korrespondens till originalärendet"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "När ett ärende har skapats"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "När ett godkännandeärende skapas, meddela ägaren och Admin.kopia-mottagare om delen som väntar på deras godkännande"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "När något händer"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "När ett ärende har lösts"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "När ett ärendes ägare byts ut"
+
+#: etc/initialdata:178
+#: etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "När ett ärendes prioritet ändras"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "När ett ärendes kö ändras"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "När ett ärendes status ändras"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "När det förekommer ett användardefinierat villkor"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "När det kommer in kommentarer"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "När det kommer in korrespondens"
+
+#: html/Admin/Users/Modify.html:188
+#: html/User/Prefs.html:88
+msgid "Work"
+msgstr "Arbete"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Arbeta offline"
+
+#: html/Ticket/Elements/ShowBasics:63
+#: html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Arbetat"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Det här ärendet tillhör redan dig"
+
+#: html/autohandler:214
+#: html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Du är inte en auktoriserad användare"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "Du kan även redigera själva den fördefinierade sökningen"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Du kan endast dela ut ärenden som tillhör dig eller som inte tillhör någon"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr "Du kan endast ta ärenden som inte tillhör någon"
+
+#. ($num, $queue)
+#: docs/design_docs/string-extraction-guide.txt:47
+#: lib/RT/StyleGuide.pod:780
+msgid "You found %1 tickets in queue %2"
+msgstr "Du fann %1 ärenden i kö %2"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "Du har loggat av från RT."
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Du har inte tillåtelse att skapa ärenden i den kön."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Du får inte skapa förfrågningar i den kön."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Du är välkommen att logga in igen"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "Din förfrågan har godkänts av %1. Andra godkännanden inväntas kanske fortfarande."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "Din förfrågan har godkänts."
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Din förfrågan avvisades."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Ditt användarnamn eller lösenord är inte korrekt"
+
+#: html/Admin/Users/Modify.html:168
+#: html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Zip"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "tillåt skapande av sparade sökningar"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "tillåt inläsning av sparade sökningar"
+
+#. ($right->PrincipalObj->Object->SelfDescription)
+#: html/User/Elements/DelegateRights:80
+msgid "as granted to %1"
+msgstr "som givet till %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "diagram"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "stängd"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "innehåller"
+
+#: html/Admin/Queues/Modify.html:98
+#: lib/RT/Date.pm:346
+msgid "days"
+msgstr "dagar"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "raderad"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "matchar inte"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "innehåller inte"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "lika med"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "fel: kan inte flytta ner"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "fel: kan inte flytta till vänster"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "fel: kan inte flytta upp"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "fel: inget att radera"
+
+#: html/Search/Build.html:533
+#: html/Search/Build.html:552
+#: html/Search/Build.html:574
+#: html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "fel: inget att flytta"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "fel: inget att växla"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "större än"
+
+#. ($self->Name)
+#: lib/RT/Group_Overlay.pm:214
+msgid "group '%1'"
+msgstr "grupp '%1'"
+
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+#: html/Search/Results.html:88
+msgid "grouped by %1"
+msgstr "grupperad efter %1"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "timmar"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "ID"
+
+#: html/Elements/SelectBoolean:53
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:57
+#: html/Search/Elements/PickBasics:162
+#: html/Search/Elements/PickBasics:74
+#: html/Search/Elements/PickBasics:90
+#: html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "är"
+
+#: html/Elements/SelectBoolean:57
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectMatch:58
+#: html/Search/Elements/PickBasics:163
+#: html/Search/Elements/PickBasics:75
+#: html/Search/Elements/PickBasics:91
+#: html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "är inte"
+
+#: html/Elements/SelectCustomFieldOperator:59
+#: html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "mindre än"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "matchar"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "min."
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "månader"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "ny"
+
+#: html/Admin/Elements/PickCustomFields:64
+#: html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "inget namn"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "inget värde"
+
+#: html/Admin/Elements/EditQueueWatchers:48
+#: html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "inget"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "inte lika med"
+
+#: lib/RT/Queue_Local.pm:2
+msgid "offer"
+msgstr ""
+
+#: html/SelfService/Elements/MyRequests:82
+#: lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "öppen"
+
+#. ($self->Name, $user->Name)
+#: lib/RT/Group_Overlay.pm:219
+msgid "personal group '%1' for user '%2'"
+msgstr "personlig grupp '%1' för användare '%2'"
+
+#. ($queue->Name, $self->Type)
+#: lib/RT/Group_Overlay.pm:227
+msgid "queue %1 %2"
+msgstr "kö %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "avvisad"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "löst"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "sek."
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "visa konfigurationstab"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "kalkylblad"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "i väntläge"
+
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+#: html/Search/Results.html:89
+msgid "style: %1"
+msgstr "stil: %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "summeringsrader"
+
+#. ($self->Type)
+#: lib/RT/Group_Overlay.pm:222
+msgid "system %1"
+msgstr "system %1"
+
+#. ($self->Type)
+#: lib/RT/Group_Overlay.pm:233
+msgid "system group '%1'"
+msgstr "systemgrupp '%1'"
+
+#: html/Elements/Error:64
+#: html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "den anropade komponenten specificerade inte varför"
+
+#. ($self->Instance, $self->Type)
+#: lib/RT/Group_Overlay.pm:230
+msgid "ticket #%1 %2"
+msgstr "ärende #%1 %2"
+
+#. ($self->Id)
+#: lib/RT/Group_Overlay.pm:236
+msgid "undescribed group %1"
+msgstr "icke-beskriven grupp %1"
+
+#. ($user->Object->Name)
+#: lib/RT/Group_Overlay.pm:211
+msgid "user %1"
+msgstr "användare %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "veckor"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "Ã¥r"
+
diff --git a/rt/lib/RT/I18N/tr.po b/rt/lib/RT/I18N/tr.po
new file mode 100644
index 0000000..58c6b79
--- /dev/null
+++ b/rt/lib/RT/I18N/tr.po
@@ -0,0 +1,5079 @@
+# Turkish localization catalog for Request Tracker (RT)
+# First Author: Burak Gürsoy <burak@cpan.org>, Jun 2007
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.6.x\n"
+"POT-Creation-Date: 2007-15-06 22:30+0200\n"
+"PO-Revision-Date: 2007-15-06 22:30+0200\n"
+"Last-Translator: Burak Gürsoy <burak@cpan.org>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr " %1 silindi."
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr " %1 değeri %2 olarak yeniden adlandırıldı."
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr " %1 kaydedildi."
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr ""
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr ""
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr ""
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr ""
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr ""
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%3 %2 %7 %1, %4:%5:%6"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%1 %2 eklendi"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 önce"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1: %2 deÄŸeri %3 olarak deÄŸiÅŸtirildi"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%1 %2 silindi"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "%1 %2 ile ÅŸablon %3"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) oluÅŸturan: %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (DeÄŸiÅŸmemiÅŸ)"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - %2 komutuna geçilecek bir argüman"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - STDOUT' a gönderilen çıktı durumu güncellemeleri"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr "%1 - Kullanmak istediğiniz şablonun adını belirtin"
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr "%1 - 'first' veya 'last' hareketlerinden hangisini kullanacağınızı belirtin"
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - Kullanmak istediğiniz eylem modülünü belirtin"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - Kullanmak istediğiniz durum modülünü belirtin"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - Kullanmak istediğiniz arama modülünü belirtin"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr "%1 - Kullanmak istediğiniz hareket türünü belirtin"
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT sürüm %2 Telif Hakkı: 1996-%3 %4."
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "ScriptAction %1 yüklendi"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "%1, %2 için bir değer olarak eklendi"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 yerel bir nesne olarak gözüküyor, fakat veritabanında mevcut değil"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 - %2"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1, %2 deÄŸerinden %3 deÄŸerine deÄŸiÅŸti"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 kopya"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "%1, %2 olarak atanamıyor"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1, durumunu çözülmüş olarak değiştiremiyor. RT' nin veritabanı tutarsız olabilir"
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "%1 oluÅŸturuldu"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "%1 silindi"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "Sahibi olduğum yüksek öneme sahip %1 bilet"
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1, biletlere dışarıdan müdahale edebilen bir araçtır (cron gibi)"
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1, artık bu kuyruk için %2 değil."
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 dakika"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "Sahibi olmayan en yeni %1 bilet"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 nesne"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1 hak"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1, çözülmüş bir grup biletinin tüm üyelerini çözecektir"
+
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1 için %2 nesne"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1 ve %2 için %3 nesne"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "%1 için kaydedilmiş aramalar"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1: herhangi bir eklenti belirtilmedi"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr ""
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1', durum için geçersiz bir değer"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(Silmek için kutucuğu işaretleyin)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(Listelenen alıcılara uyarı gitmesini iptal etmek için kutucukları işaretleyin)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "Listelenen alıcılara uyarı gitmesi için kutucukları işaretleyin)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(Boşluklarla ayrılmış olarak, bilet numaralarını veya URL' lerini girin)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(Boş bırakılırsa, varsayılan olarak %1 değerini alacaktır)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(Özel alan yok)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(Ãœye yok)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(Senet yok)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "(Åžablon yok)"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(Hiçbiri)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Bu güncellemeyi, virgülle ayrılmış eposta listesine, görünmez karbon kopya olarak gönderir. İleriki güncellemeleri kimin alıp almayacağını <strong>değiştirmez</strong>.)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Bu güncellemeyi, virgülle ayrılmış eposta listesine, karbon kopya olarak gönderir. Listedeki kişiler ileriki güncellemeleri <strong>alacaktır</strong>.)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(Bu güncellemeyi, virgülle ayrılmış eposta listesine, karbon kopya olarak gönderir. İleriki güncellemeleri kimin alıp almayacağını <strong>değiştirmez</strong>.)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(Bu güncellemeyi, virgülle ayrılmış eposta listesine, karbon kopya olarak gönderir. Listedeki kişiler ileriki güncellemeleri <strong>alacaktır</strong>.)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(Bir durum veya eylem için 'Kullanıcı Tanımlı' yı seçtiğinizde, bu alanları kullanın)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(Eposta gönderilmeyecek)"
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(boÅŸ)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(herhangi bir ad listelenmedi)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(deÄŸer yok)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(deÄŸer yok)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(sadece bir bilet)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(onaylanmayı bekliyor)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(diÄŸer bir Koleksiyonu bekliyor)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(gerekli)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(başlıksız)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(yyyy/aa/gg)"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr ""
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr "--transaction argümanı sadece 'first' veya 'last' olabilir"
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr ""
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr ""
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr ""
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"Yeni bilet:\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"Yeni bilet:\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "BoÅŸ bir ÅŸablon"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "Bir parola atanmamış, dolayısıyla, kullanıcı giriş yapamayacak"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "ACE bulunamadı"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "ACE' ler sadece oluÅŸturulabilir ve silinebilirler."
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "VE"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "Hakkımda"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "EriÅŸim denetimi"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "Eylem"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "%1 eylemi bulunamadı"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "Eylem yapıldı"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr "Eylem, zorunlu bir argüman"
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "Eylem hazırlanıyor..."
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "Ekle"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "Yöneticiye karbon kopya ekle"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "Karbon kopya ekle"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "Sütun ekle"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "Kıstas ekle"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "Daha fazla dosya ekle"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "İstekçi ekle"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "DeÄŸer ekle"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "Bütün kuyruklara etki edecek bir senet ekle"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "Ekle ve Ara"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "Seçilen senetlere yorum veya cevap ekle"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "Ãœye ekle"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "Yeni gözcüler ekle"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "Aramanıza bu terimleri ekleyin"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "DeÄŸer ekle"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "Nesneler için, kişiselleştirilmiş alanları ekleyin, silin ve değiştirin"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "Bu kuyruk için, asıl, %1 olarak eklendi"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "Bu bilet için, asıl, %1 olarak eklendi"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "Adres1"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "Adres2"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "Yönetici Karbon Kopya"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "Yönetici Yorumu"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "Yönetici Yazışması"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "Yönetici kuyrukları"
+
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "Yönetici/Küresel ayarlar"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr ""
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr ""
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr ""
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "Yönetimsel karbon kopya"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "GeliÅŸmiÅŸ"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "Sonra"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "Toplayıcı"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "Bütün Onaylar Geçti"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "Bütün Kuyruklar"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "Ve/Veya"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "Etki edeceği bölümler: "
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "Uygula"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "Değişikliklerinizi uygulayın"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "Onaylama"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "Onaylama #%1: %2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "Onaylama #%1: Bir sistem hatası nedeniyle, notlar kaydedilemedi"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "Onaylama #%1: Notlar kaydedildi"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "Onay Geçti"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "Onay Reddedildi"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "Onayla"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "Onaylayanın notları: %1"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "Nis."
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "Art"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "Artan"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "Kişisel alanları ata ve kaldır"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr ""
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "Ekle"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "Dosya ekle"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "EklenmiÅŸ dosya"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "Eklenti '%1' yüklenemiyor"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "Eklenti oluÅŸturuldu"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "Eklenti dosya adı"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "Eklentiler"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "Öznitelik Silindi"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "AÄŸu."
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "Otomatik cevap"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "İstekçileri Otomatik Cevapla"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "Hazır"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "Temeller"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "Gizli karbon kopya"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "DeÄŸiÅŸikliklerinizi kaydettiÄŸinizden emin olun"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "Önce"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Best Practical Solutions, LLC ÅŸirket logosu"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "BoÅŸ"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "Kalın"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "Yer imi olarak eklenebilir bağlantı"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "Kısa başlıklar"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "Toplu Güncelleme"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "Sistem kullanıcıları değiştirilemez"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "Bu yetkili, bu kuyruğu gördü mü"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "Bir ad olmadan, kiÅŸisel alan deÄŸeri eklenemez"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "'%1' için bir koleksiyon sınıfı bulunamadı"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "Üzerinde çalışılabilecek bir kaydedilmiş arama bulunamadı"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "Bir bileti kendisine bağlayamazsınız"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr "%1 kaydedilemedi"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "Bu arama kaydedilemiyor"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "Taban ve hedefin ikisini birden belirtemezsiniz"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "Kullanıcı oluşturulamadı: %1"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "Kategori"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "Karbon kopya"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "Parolayı değiştir"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "Hepsini iÅŸaretle"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "Silmek için kutucuğu işaretleyin"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "Hak vermek için kutucuğu işaretleyin"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "Çocuklar"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "Bir tarih seçin"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "Åžehir"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "Hepsini Temizle"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "Pencereyi Kapat"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "Kapatıldı"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "Kapatılmış biletler"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "Seçim kutusu: Birden fazla değeri seçin veya girin"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "Seçim kutusu: Bir tek değeri seçin veya girin"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "Seçim kutusu: 1-%1 arası değer seçin veya girin"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "Yorum"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "Yorum Adresi"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "Biletler üzerine yorum yap"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr ""
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "Yorumlar (istekçilere gönderilmedi)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "Yorumlar (istekçilere gönderilmedi)"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "Bu kullanıcı hakkındaki yorumlar"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "Yorumlar eklendi"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "Saplanmışları Ada"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "Durum"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "Durum, zorunlu bir argüman"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "Durum eÅŸleÅŸiyor..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "Durum bulunamadı"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "Ayarlar"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "Onayla"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "İçerik"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr ""
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "Kopya"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "Uygunluk"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "Uygunluk eklendi"
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "Yeni özel bölüm eklenemiyor. "
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "Yeni özel bölüm eklenemiyor. %1"
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "Sahip deÄŸiÅŸtirilemiyor."
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "Özel bölüm oluşturulamıyor"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "Özel bölüm oluşturulamıyor: %1"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "Grup oluşturulamıyor"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "Şablon oluşturulamıyor: %1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "Bilet oluşturulamıyor. Kuyruk atanmadı"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "Kullanıcı oluşturulamıyor"
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "Bu kullanıcı bulunamıyor veya oluşturulamıyor"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "Bu yetkili bulunamıyor"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "Özel bölüm yüklenemiyor %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "Grup yüklenemiyor"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "%1 için nesne yüklenemiyor"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "Arama özniteliği yüklenemiyor"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "Bu yetkili, bu kuyruk için bir %1 yapılamıyor"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "Bu yetkili, bu bilet için bir %1 yapılamıyor"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "Bu yetkili, bu kuyruktan %1 olarak ayrılamıyor"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "Kullanıcı bilgisi atanamıyor"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "Eklenti eklenemiyor"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "Kullanıcı gruba eklenemiyor"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "Bir hareket oluşturulamıyor: %1"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "Satır bulunamadı"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "Bu yetkili bulunamadı"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "Bu değer bulunamadı"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "%1 değeri, kullanıcı veritabanından yüklenemedi.\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "%1 sınıfı yüklenemedi"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "Özel bölüm %1 yüklenemedi"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr "#%1 numaralı biletin kopyası yüklenemedi"
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "%1 grubu yüklenemedi"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "Bağlantı yüklenemedi"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "%1 nesnesi yüklenemedi"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "Kuyruk yüklenemedi"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "%1 kuyruğu yüklenemedi"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr "Senet #%1 yüklenemedi"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "'%1' bileti yüklenemedi"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr "'%1' değeri bir URI olarak çözülemiyor"
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "Ãœlke"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "OluÅŸtur"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "Bilet OluÅŸtur"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "Özel Bölüm Oluştur"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "%1 kuyruğu için özel bir bölüm oluştur"
+
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "Yeni bir grup oluÅŸtur"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "Yeni bir kiÅŸisel grup oluÅŸtur"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "YEni bir bilet oluÅŸtur"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "Yeni bir kullanıcı oluştur"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "Yeni bir kuyruk oluÅŸtur"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "%1 kuyruğu için yeni bir senet oluştur"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "Bir ÅŸablon oluÅŸtur"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "Yeni bir bilet oluÅŸtur"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "Bu senedin ÅŸablonunu temel alarak yeni biletler oluÅŸtur"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "Yeni bilet oluÅŸtur"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "Bu kuyrukta yeni biletler oluÅŸtur"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "Özel bölümleri oluştur, sil ve değiştir"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "Kuyrukları oluştur, sil ve değiştir"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "Kişisel grup üyelerini oluştur, sil ve değiştir"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "Kullanıcıları oluştur, sil ve değiştir"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr ""
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "OluÅŸturuldu"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "Özel bölüm %1 oluşturuldu"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "Bir zaman aralığı içinde oluşturuldu"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "Bir devir içinde biletle oluşturuldu ve duruma göre sıralandı"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "OluÅŸturan"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "Geçerli bağlantılar"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "Geçerli senetler"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "Geçerli üyeler"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "Geçerli haklar"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "Geçerli arama"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "Geçerli izleyiciler"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "Özel bölümler"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "%1 için özel bölümler"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "Özel hareket temizleme kodu"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "Özel hareket hazırlama kodu"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "Özel durum"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "Özel bölüm %1, bir değere sahip"
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "Özel bölüm %1, bir değere sahip değil"
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "Özel bölüm %1, bulunamadı"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr "Özel bölüm '%1'"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "Özel bölüm %2 için, özel bölüm değeri %1 bulunamıyor"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "Özel bölüm değeri silinemiyor"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "Özel bölüm değeri bulunamıyor"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "Özel bölüm değeri silindi"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "Özel Bölüm"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "Özelleştir"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "Tarihler"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "Ara."
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "Varsayılan OtoCevap şablonu"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "Varsayılan Kuyruk"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "Varsayılan İstekçi"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "Varsayılan yönetici yorum şablonu"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "Varsayılan yönetici cevap şablonu"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "Varsayılan cevap şablonu"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "Varsayılan hareket şablonu"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "Yetkileri devret"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "Size verilen belirli yetkileri devredin"
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "HaklarıDevret"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "Devretmek"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "Sil"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "Silme işlemi başarısız: %1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "Seçilen senetleri sil"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "Biletleri sil"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "BiletSil"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "Arama sil"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "Bu nesneyi silmek, tercihli bütünlüğü bozacaktır"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "Bu nesneyi silmek, tercihli bütünlüğü ihlal edecektir"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "Reddet"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "Şunun tarafından bağımlılığa sahip: "
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "%1 ile bağımlılık eklendi"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "%1 ile bağımlılık silindi"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "%1 için bağımlılık eklendi"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "%1 için bağımlılık silindi"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "Bağımlılık"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr Azal""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "Azalan"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "Sorunu aşağıda tanımlayın"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "Tanım"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "Görüntüle"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "Erişim Denetim Listesini Görüntüle"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "Sütunları Görüntüle"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "Bu kuyruk için senet şablonlarını görüntüle"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "Bu kuyruk için senetleri görüntüle"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "Görüntüleme kipi"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "Bu grup için, kaydedilmiş aramaları görüntüle"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "<a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU GPL</a>' nin 2. sürümü altında dağıtılmaktadır."
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "Hiçbirşeyi ve herşeyi yapın"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "Bu sayfayı yenilemeyin"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "Ä°ndir"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "Sekme ile ayrılmış dosya olarak indir"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "Vade"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "Düzenle"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "Özel Bölümleri Düzenle"
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "%1 için özel bölümleri düzenle"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "Bütün gruplar için özel bölümleri düzenle"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "Bütün kullanıcılar için özel bölümleri düzenle"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "Bütün kuyruklardaki bütün biletler için özel bölümleri düzenle"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "Bağlantıları Düzenle"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "Sorguyu Düzenle"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "Aramayı Düzenle"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "%1 kuyruğundaki şablonları düzenle"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "Bu grup için kaydedilmiş aramaları düzenle"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "Sistem şablonlarını düzenle"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "KaydedilmişAramalarıDüzenle"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "%1 kuyruğunun ayarları düzenleniyor"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "Özel bölüm %1 düzenleniyor"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "%1 grubu için üyelik düzenleniyor"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "Özel grup %1 için üyelik düzenleniyor"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "Taban veya hedef belirtilmeli"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "Eposta"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "Eposta adresi kullanımda"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "Etkinleştirildi (Bu kutudaki işareti kaldırmak, bu özel bölümü iptal eder)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "Etkinleştirildi (Bu kutudaki işareti kaldırmak, bu grubu iptal eder)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "Etkinleştirildi (Bu kutudaki işareti kaldırmak, bu kuyruğu iptal eder)"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "EtkinleÅŸtirilmiÅŸ Kuyruklar"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "EtkinleÅŸtirilme durumu %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "EtkinleÅŸtirilme durumu %1"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "Çoklu değer girin"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "Bu nesneleri bağlamak için, nesneler veya URIler girin. Çoklu girdileri boşluk ile ayırın."
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "Bir deÄŸer girin"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "Bu kuyrukları bağlamak için, kuyruklar veya URIler girin. Çoklu girdileri boşluk ile ayırın."
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "Bu biletleri bağlamak için, biletler veya URIler girin. Çoklu girdileri boşluk ile ayırın."
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "%1 adet deÄŸer girin"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "Hata"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "Queue->AddWatcher için geçilen parametrelerde hata var"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "Queue->DeleteWatcher için geçilen parametrelerde hata var"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "Ticket->AddWatcher için geçilen parametrelerde hata var"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "Ticket->DeleteWatcher için geçilen parametrelerde hata var"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "Biletleri Yükselt"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "Tahmini"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "Herkes"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "Örnek:"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "Ek Bilgi"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "Arama özniteliği oluşturulamadı"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "'Ayrıcalıklı' sahte kullanıcı grubu bulunamadı"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "'Ayrıcalıksız' sahte kullanıcı grubu bulunamadı"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "%1 modülü yüklenemedi. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "%1 için nesne yüklenemedi"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "Åžub."
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "Dosya adı"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "Çoklu metin alanları doldur"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "Çoklu wikimetin alanları doldur"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "Tek bir metin alanı doldur"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "Tek bir wikimetin alanı doldur"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "Bu bölümü bir URL ile doldur"
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "En fazla %1 adet metin alanı doldur"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "En fazla %1 adet wikimetin alanı doldur"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "Son Öncelik"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "SonÖncelik"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "Grupları bul:"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "KiÅŸileri bul:"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "Biletleri bul"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "Ä°lk"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr ""
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "DeÄŸiÅŸikliÄŸi zorla"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "Biçim"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "%quant(%1,ticket) bulundu"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "Nesne bulundu"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "Cum."
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "Tüm Başlıklar"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "Dosyadan ÅŸablon al"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "Verilen: %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "Küresel"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "Küresel Özel Alanlar"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "Küresel Özel Alan Ayarları"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr "Küresel portlet %1 kaydedildi"
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "Küresel şablon: %1"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "Git"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "Git!"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "Bilete git"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "Grup"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "Grup Yetkileri"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "Grup üyeye sahip"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "Grup oluşturulamıyor: %1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "Grup oluÅŸturuldu"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "Grupta böyle bir üye yok"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "Grup bulunamadı"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "Gruplar"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "Gruplar, üyelerinin üyeleri olamazlar"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "Arama kıstaslarına uyan gruplar"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "Bu kullanıcının kayıtlı olduğu gruplar"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "Merhaba!"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "Merhaba, %1"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "Geçmiş"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "%1 grubunun geçmişi"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "%1 üyesinin geçmişi"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "Ev Sayfası"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "Saat"
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr ""
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "Kayboldum"
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "Numara"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "Kimlik"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "Eğer bir onaylama reddedilmişse, özgün kaydı reddet ve bekleyen onayları sil"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "Eğer bir istekçi belirtilmemişse, biletleri, bu istekçi üzerinden oluştur"
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "EÄŸer bir kuyruk belirtilmemiÅŸse, biletleri bu kuyrukta oluÅŸtur."
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "Eğer bu araş setgid ise, saldırgan bir yerel kullanıcı, bu aracı kullanarak RT' ye yönetici erişimi hakkı kazanabilir."
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "Eğer yukarıda herhangi bir şeyi değiştirdiyseniz, mutlaka"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "%1 için geçersiz değer"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "DeÄŸiÅŸmez alan"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "Listelerken, etkinleştirilmemiş grupları ekle"
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "Listelerken, etkinleştirilmemiş kuyrukları ekle"
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "Listelerken, iptal edilmiş üyeleri ekle"
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "Sayfayı İçer"
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "Eksik Sorgu"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "Eksik sorgu"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "Başlangıç Önceliği"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "BaşlangıçÖnceliği"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "Girdi Hatası"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr "Girdi, %1 ile eÅŸleÅŸmeli"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "İç Hata"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "İç Hata: %1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr Geçersiz Grup Türü"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "Geçersiz Hak"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "Geçersiz veri"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "Geçersiz desen: %1"
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "Geçersiz kuyruk"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "Geçersiz hak"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "%1 için geçersiz değer"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "Özel alan için geçersiz değer"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "Durum için geçersiz değer"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "Yetkisiz kullanıcıların, bu aracı kullanmasını engellemek çok önemlidir."
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "Bu araca erişim için, doğru grup üyeliğiyle yetkisiz bir unix kullanıcısı oluşturmanız ve RT erişimi vermeniz gerekmektedir."
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "Çeşitli argümanlar alır:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "EÄŸik"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "Oca."
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "Bu gruba katılın veya ayrılın"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "Tem."
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr ""
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "Haz."
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "Dil"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "GeniÅŸ"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "Son"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "Son Temas"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "Son Temasedilen"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "Son Güncelleme"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "SonGüncelleyen"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "Kalan"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "Bu kullanıcının RT' ye erişimine izin ver"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "Bu kullanıcının yetkiye sahip olmasına izin ver"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "Bağlantı"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "Bağlantı mevcut"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "Bağlantı oluşturulamıyor"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "Bağlantı oulşturuldu (%1)"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "Bağlantı silindi (%1)"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "Bağlantı bulunamadı"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "Bileti baÄŸla #%1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "DeÄŸerleri baÄŸla:"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr "Bağlanıyor. Erişim reddedildi"
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "Bağlantılar"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "Yükle"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "Kaydedilmiş aramayı yükle:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "Yüklenmiş Perl modülleri"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "Yüklenmiş arama %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "Konum"
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "%1 olarak giriş yapıldı"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "GiriÅŸ yap"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "Çık"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "Arama türü eşleşmedi"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "Yapım Sahibi"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "Yapım Durumu"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "Yapım Zamanlaması"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "Yapım tarihi Çözümlendi"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "Yapma tarihi Başladı"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "Yapma tarihi Başlangıcı"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "Yapım tarihi Belirtilen"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "Yapım önceliği"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "Yapım kuyruğu"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "Yapım başlığı"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "Bu grubun, kullanıcıya görünür olmasını sağla"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "Özel alanları ve özel alan değerlerini yönet"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "Grupları ve grup üyeliklerini yönet"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "Bütün kuyruklara etki eden özellikleri ve ayarları yönetin"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "Kuyrukları ve kuyruğa-özel özellikleri yönetin"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "Kullanıcıları ve parolaları yönetin"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "Mar."
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "May."
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "Ãœye %1 eklendi"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "Ãœye %1 silindi"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "Ãœye eklendi"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "Ãœye Silindi"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "Ãœye Silinemedi"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "Ãœye olduÄŸu yer:"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "Ãœyeler"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "%1 için üyelik eklendi"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "%1 için üyelik silindi"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "Ãœyelikler"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "%1 üyesinin üyelikleri"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "Birleştirme başarılı"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "Birleştirme başarısız. Etkin Numara atanamıyor"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "Birleştirme başarısız. Durum atanamıyor"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "Åžununla birleÅŸtir: "
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "%1 içinde birleştirildi"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "Ä°leti"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "İleti gövdesi, çok büyük veya düz metin olmadığından gösterilmiyor."
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "Ä°leti kaydedilemedi"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "Ä°leti kaydedildi"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "Bu bilet hakkındaki iletiler gönderilmeyecek..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "Dakika"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "EÅŸleÅŸmemiÅŸ parantezler"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "Başlıca anahtarlardan birisi eksik mi?: %1"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "Hareketli"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "EriÅŸim Denetim listesini DeÄŸiÅŸtir"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "Bütün %2 için, %1 etki eden bütün özel alanları değiştir"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "Bütün %1 etki eden özel alanları değiştir"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "Grup yetkilerini deÄŸiÅŸtir"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "Ãœyeleri DeÄŸiÅŸtir"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "Yetkileri DeÄŸiÅŸtir"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "Bu kuyruk için, senet şablonlarını değiştir"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "Bu kuyruk için senetleri değiştir"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "Kullanıcı haklarını değiştir"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "%1 kuyruğu için bir ÖzelAlanı değiştir"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "%1 kuyruğu için bir senet değiştir"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "Bütün kuyruklara etki eden bir senedi değiştir"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "%1 için, ilişkili nesneleri değiştir"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "#%1 için tarihleri değiştir"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "Bilet # %1 için, tarihleri değiştir"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "Küresel özel alanları değiştir"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "Küresel grup izinlerini değiştir"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "Küresel grup izinlerini değiştir."
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "Küresel kullanıcı izinlerini değiştir"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "Küresel kullanıcı izinlerini değiştir."
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "Grup mal bilgisini deÄŸiÅŸtir veya grubu sil"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "Özel alan %1 için grup izinlerini değiştir"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "%1 grubu için, grup izinlerini değiştir."
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "%1 kuyruğu için, grup izinlerini değiştir."
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "Bu grup için, üyelik listesini değiştir"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "Kişinin kendi RT hesabını değiştir"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "%1 ile iliÅŸkili kiÅŸileri deÄŸiÅŸtir"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "%1 bileti ile iliÅŸkili kiÅŸileri deÄŸiÅŸtir"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "%1 kuyruğu için senetleri değiştir"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "Bütn kuyruklara etki eden senetleri değiştir"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "%1 ÅŸablonunu deÄŸiÅŸtir"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "Bütün kuyruklara etki eden şablonları değiştir"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "Varsayılan \"Bir bakışta RT\" görünümünü değiştir"
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "%1 grubunu deÄŸiÅŸtir"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "Kuyruk gözcülerini değiştir"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "%1 kullanıcısını değiştir"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "#%1 biletini deÄŸiÅŸtir"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "#%1 biletini deÄŸiÅŸtir"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "Biletleri deÄŸiÅŸtir"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "Özel alan %1 için kullanıcı haklarını değiştir"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "%1 grubu için kullanıcı haklarını değiştir"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "%1 kuyruğu için kullanıcı haklarını değiştir"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr ""
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr ""
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "Pzt."
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "%1 hakkında daha fazla bilgi"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "Aşağı git"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "Yukarı git"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "Çoklu"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "'Ad' özniteliği belirtilmeli"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "Benim %1 biletim"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "Benim Günüm"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "Benim Onaylarım"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "Benim kaydedilmiş aramalarım"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "Ad"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "Kullanımdaki ad"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "Asla"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "Yeni bağlantılar"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "Yeni parola"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "Yeni bekleyen onay"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "Yeni arama"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "Yeni özel alan"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "Yeni grup"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "Yeni parola"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "Yeni parola uyarısı iletildi"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "Yeni kuyruk"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "Yeni hatırlatıcı:"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "Yeni yetkiler"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "Yeni senet"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "Yeni ÅŸablon"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "Yeni bilet"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "Yeni bilet yok"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "Yeni kullanıcı"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "Yeni kullanıcı: "
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "Yeni gözcüler"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "Ä°leri"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "Sonraki Sayfa"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "Rumuz"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "Bir sınıf tanımlanmadı"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "Özel alan yok"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "Özel alan tanımlanmadı"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "Grup alan tanımlanmadı"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "Sorgu Yok"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "Kuyruk tanımlanmadı"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "RT kullanıcısı bulunamadı. Lütfen RT yöneticinize danışın.\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "Åžablon Yok"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "Eylem Yok"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "Sütun belirtilmedi"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "Bu kullanıcı hakkında yorum yapılmadı"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "%1 için herhangi bir tanım yok"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "Grup belirtilmedi"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "Arama kıstaslarıyla örtüşen bir grup bulunamadı."
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "Herhangi bir ileti eklenmedi"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "Herhangi bir parola atanmadı"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "Kuyruk oluşturmak için gerekli yetki yok"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "%1 kuyruğunda bilet oluşturmak için gerekli yetki yok"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "Kullanıcı oluşturmak için gerekli yetki yok"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "Bu bileti görüntülemek için gerekli yetki yok"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "Sistem genelinde yapılan aramaları kaydetmek için gerekli yetki yok"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "Bilet güncellemeyi görüntülemek için gerekli yetki yok"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "Yetkili belirtilmedi"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "Herhangi bir yetkili seçilmedi"
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "Arama kıstaslarıyla örtüşen bir kuyruk bulunamadı."
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "Herhangi bir yetki bulunamadı"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "Herhangi bir yetki verilmedi."
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "Herhangi bir arama yüklenemedi"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "İşlenecek bir arama bulunamadı"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "Başlık yok"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "Aktarım türü belirtilmedi"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "Arama kıstaslarıyla örtüşen herhangi bir kullanıcı bulunamadı."
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "_Set için herhangi bir değer gönderilmedi!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "Hiçkimse"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "Varolmayan Alan?"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr "Atanmadı"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "Giriş yapılmadı"
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "Atanmadı"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "Henüz oluşturulmadı."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "Notlar"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "Uyarı gönderilemiyor"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "Yönetici Kk' ları bildir"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "Yönetici Kk' ları yorum olarak bildir"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "Kk' ları bildir"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "Kk' ları yorum olarak bildir"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "Diğer alıcıları uyar"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "Diğer alıcıları yorum olarak uyar"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "Sahibi uyar"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "Sahibi yorum olarak uyar"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "Sahipleri, reddedilen biletleri hakkında uyar"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "Bütün onaycılar tarafından onaylanan bileti hakkında sahibi uyar"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "Herhangi bir onaycı tarafından onaylanan bileti hakkında sahibi uyar"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "Onaylanmayı bekleyen yeni öğeler hakkında sahipleri ve Yönetici Kk' ları uyar"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "İstekçileri Uyar"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "İstekçileri ve Kk' ları uyar"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "İstekçileri ve Kk' ları yorum olarak uyar"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "İstekçileri, Kk' ları ve Yönetici Kk' ları uyar"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "İstekçileri, Kk' ları ve Yönetici Kk' ları yorum olarak uyar"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "Kas."
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "VEYA"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "Nesne oluşturulamıyor"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "Nesne silinemiyor"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "Nesne oluÅŸturuldu"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "Nesne silindi"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "%1 türündeki nesneler, özel alanlar alamazlar"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "Nesne türü eşleşmiyor"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "Eki."
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "Çevrimdışı"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "Çevrimdışı düzenlemeler"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "Çevrimdışı yükleme"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "Tarih:"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr "%1 tarihinde, %2 yazdı:"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "Yorumda"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "Cevapta"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "OluÅŸturmada"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "Sahiplik deÄŸiÅŸiminde"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "Öncelik değişiminde"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "Kuyruk deÄŸiÅŸiminde"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "Çözümde"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "Durum deÄŸiÅŸikliÄŸinde"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "Harekette"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "Sadece %1 sonrası oluşturulmuş isteklere ait onayları göster"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "Sadece %1 öncesi oluşturulmuş isteklere ait onayları göster"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "Sadece şunun için özel alanları göster:"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "Biletleri Aç"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "Aç"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "Biletleri aç"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "Cevap geldiğinde biletleri aç"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "Seçenekler"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "Sıralama:"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "Kurum"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "Kaynak alınan bilet: #%1"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "Bir yorum hakkındaki giden eposta kaydedildi"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "Giden eposta kaydedildi"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "Zaman içinde, öncelik ilerler"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "Biletleri sahiplen"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "BiletleriSahiplen"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "Sahip"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "Sahip atanamıyor"
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "Sahip, zorla %1 den %2 ye deÄŸiÅŸtirildi"
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "Sayfa %1/%2"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "Sayfalayıcı"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "Ana bölüm"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "Parola"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "Parola Hatırlatıcı"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "Parola deÄŸiÅŸti"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "Parola en az %1 karakter uzunluğunda olmalı"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "Parola atandı"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "Parola: %1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "Parola: Ä°zin Reddedildi"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "Parolalar eÅŸleÅŸmiyor"
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "Parolalar eşleşmiyor. Parolanız değişmedi"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "KiÅŸiler"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "Kullanıcı tanımlı bir eylemi yerine getir"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl ayarları"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "Pääsy kielletty"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "Ä°zin Reddedildi"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "Ä°zin Reddedildi"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "KiÅŸisel gruplar"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "KiÅŸisel gruplar"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "KiÅŸisel gruplar:"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "Telefon numaraları"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "Tercihler"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr "%2 kullanıcısı için %1 tercihleri"
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr "%1 için tercihler kaydedildi"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "Küt hazırla"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "Önceki"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "Önceki sayfa"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "%1 yetkilisi bulunamadı"
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "Öncelik"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "Öncelik başlangıcı:"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "Gizlilik:"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "Ayrıcalıklı"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "Ayrıcalık durumu: &1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "Ayrıcalıklı kullanıcılar"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "İç kullanım için sahte grup"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "Sorgu OluÅŸturucu"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "Sorgu:"
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "Kuyruk"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "%1 kuyruğu bulunamadı"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "Kuyruk adı"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "Kuyruk halihazırda mevcut"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "Kuyruk oluşturulamıyor"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "Kuyruk yüklenemiyor."
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "Kuyruk oluÅŸturuldu"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "Kuyruk bulunamadı"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "Kuyruklar"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "Yönettiğim kuyruklar"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "Yönetici Kk olduğum kuyruklar"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "Hızlı arama"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "Hızlı bilet oluşturumu"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr ""
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "RT %1 - %2"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT Yönetimi"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT Hatası"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RT DeÄŸiÅŸkenleri"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "Bir bakışta RT"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr "%1 kullanıcısı için bir bakışta RT"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT, bu özel alanı gösterirken, diğerbir ağ hizmetinden içerik ekleyebilir"
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT, bu özel alanın değerlerini, diğer bir hizmetin hiperbağlantılarına dönüştürebilir."
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT, oturumunuzu kaydedemiyor."
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "%1: RT"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT, bilet başlıklarına gireceğiniz herhangi bir şeyi araştıracaktır."
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT, <tt>__id__</tt> ve <tt>__CustomField__</tt> değerlerini, kayıt numarası ve özel alan değeri ile değiştirecektir"
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "Gerçek Ad"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "%1 ile baÅŸvuru eklendi"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "%1 ile baÅŸvuru silindi"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "%1 için başvuru eklendi"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "%1 için başvuru silindi"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "Kaynak gösteren:"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "Kaynak gösterdiği:"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "Bu sayfayı, her %1 dakikada bir yenile"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "'%1' hatırlatıcısı eklendi"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "'%1' hatırlatıcısı tamamlandı"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "'%1' hatırlatıcısı tekrar açıldı"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "Hatırlatıcı bilet #%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "Hatırlatıcılar"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "#%1 bileti için hatırlatıcılar"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "Yönetici Kk Kaldır"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "Kk kaldır"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "İstekçi kaldır"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "Yanıtla"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "Yanıtlama Adresi"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "İstekçileri Yanıtla"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "Biletleri Yanıtla"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "BiletiYanıtla"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "Raporlar"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "İstekçi"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "İstekçiler"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "İsteklerin yapılacağı zaman:"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "Gerekli parametre '%1' belirtilmedi"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "Sıfırla"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr "Varsayılana sıfırla"
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "Hane"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "Çöz"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "#%1 biletini çözümle (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "Çözüldü"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "Sahip tarafından çözüldü"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "Zaman aralığında çözüldü"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "Dönemdeki çözülmüş biletler (sahip ile gruplandırılmış)"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "Çözülmüş biletler (sahip ile gruplandırılmış)"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "Sonuçlar"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "Parolayı tekrar yazın"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "Eskiye dön"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "Yetki Aktarıldı"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "Yetki Verildi"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "Yetki Yüklendi"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "Yetki geri alınamıyor"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "Yetki bulunamadı"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "Yetki yüklenemedi"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "Yetki geri alındı"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "Yetkiler"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "Yetkiler %1 için verilemiyor"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "Yetkiler %1 için geri alınamıyor"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "Roller"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "Kutu başına satır"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "Sayfa başına satır"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "Cmt."
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "Kaydet"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "DeÄŸiÅŸiklikleri Kaydet"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "Tercihleri Kaydet"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "DeÄŸiÅŸiklikleri kaydet"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "KaydedilmiÅŸ arama %1"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "Senet #%1"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "Senet oluÅŸturuldu"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "Senet alanları"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "Senet silindi"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "Senetler"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "Bütün kuyruklara etki eden senetler"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "Arama"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "Arama Tercihleri"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "Arama özniteliği yükleme hatası"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "Onayları ara"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "Biletleri ara"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "Biletleri ara. Bilet <strong>numarasını</strong>, <strong>Kuyrukları</strong> adı ile, Sahipleri <strong>kulanıcı adı</strong> ile ve İstekçileri <strong>eposta adresi</strong> ile girin. RT, girdiğiniz diğer şeyleri bilet gövdelerinde ve eklentilerde arayacaktır."
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "Arama tercihleri"
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "Arama sonuçları %1 ile gruplandırılmıştır."
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "Arama güncellemesi: %1"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "Her bir biletin tüm metninin aranması uzun sürebilir, ama eğer bunu yapmanız gerkiyorsa, <b>fulltext:<i>kelime</i></b> yazarak tüm bilet tarihçesindeki herhangi bir kelimeyi aratabilirsiniz."
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "Güvenlik:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "Bakınız:"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "Özel alanları gör"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "Giden eposta iletilerinin tamamını ve alıcılarını gör"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "Biletin gizli yorumlarını gör"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "Bilet özetlerini gör"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "ÖzelAlanıGör"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "GrubuGör"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "KuyruğuGör"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "Özel bir alanı seç"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "Bir grubu seç"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "Bir kuyruğu seç"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "Yeni biletiniz için bir kuyruk seçin"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "Bir kullanıcı seçin"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "Özel alan seçin"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "Bütün kullanıcı grupları için özel alanları seçin"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "Bütün kullanıcılar için özel alanları seçin"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "Bütün kuyruklardaki biletler için özel alanlar seç"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "Bütün kuyruklardaki biletlerin hareketleri için özel alanlar seçin"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "Grup seçin"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "Çoklu değer seçin"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "Bir değer seçin"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "Kuyruk seçin"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "\"Bir bakışta RT\" sayfasında görüntülenecek kuyrukları seçin"
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "Senet seçin"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "Şablon seçin"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "En çok %1 adet değer seçin"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "Kullanıcı seç"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "Seçili özel alanlar"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "Seçili nesneler"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "Seçimler değişti. Lütfen değişikliklerinizi kaydedin"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "Tüm izleyicilere posta gönder"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "Tüm izleyicilere \"yorum\" olarak posta gönder"
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "İstekçilere ve Kk' lara posta gönder"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "İstekçilere ve Kk' lara yorum olarak posta gönder"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "İstekçilere posta gönderir"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "Belirtilmiş Kk ve GKk listesine posta gönderir"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "Kk' lara posta gönderir"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "Kk' lara yorum olarak eposta gönderir"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "Yönetimsel Kk' lara eposta gönderir"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "Yönetimsel Kk' lara yorum olarak eposta gönderir"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "Sahibe posta gönderir"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "Eki."
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "Göster"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "Onayları Göster"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "Sütunları göster"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "Sonuçları Göster"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "Onaylanmış istekleri göster"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "Temelleri Göster"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "Reddedilmiş istekleri göster"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "Ayrıntıları göster"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "Bekleyen istekleri göster"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "Diğer onayları bekleyen istekleri göster"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr ""
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr ""
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "Bilet istekçisi veya bilet veya kuyruk Kk olarak yazılın"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "Bilet veya kuyruk Kk olarak yazılın"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "Ä°mza"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "Basit Arama"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "Tekil"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "Boyut"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "Menüyü Geç"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "Küçük"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "Bazı gezginler, sadece aynı alan adından (RT sunucunuz gibi) içerik yükleyebilirler."
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "Sırala"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "AÅŸama"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "Başlangıcı"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "BaÅŸlayacak"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "Durum"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "Durum"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "Durum DeÄŸiÅŸikliÄŸi"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "Çal"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "Biletleri Çal"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr ""
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "%1 den çalındı"
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "Biçem"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "Konu"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "Konu deÄŸiÅŸti: %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "Gönder"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "Başarılı"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "Paz."
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr ""
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "Sistem"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "Sistem Ayarları"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "Sistem Hatası"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "Sistem Hatası: %1"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "Sistem Araçları"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "Sistem hatası. Yetki devredilemedi."
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "Sistem hatası. Yetki verilemedi."
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "Sistem grupları"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "İç kullanım için SistemRolGrubu"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "DENEME_YAZISI"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "Al"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "Biletleri al"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "BiletiAl"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "Alındı"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "Åžablon"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "Åžablon #%1"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "Åžablon silindi"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr "Şablon gerekli bir argüman"
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "Şablon bulunamadı"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "Şablon ayrıştırıldı"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "Şablon ayrıştırma hatası"
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "Åžablonlar"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "Bu, zaten ÅŸimdiki deÄŸer"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "Bu, bu özel alan için bir değer değil"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "Bu, aynı değer"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "Bu yetkili, zaten bu hakka sahip"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "Bu yetkili, zaten bu kuyruk için bir %1"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "Bu yetkili, zaten bu bilet için bir %1"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "Bu yetkili, zaten bu kuyruk için bir %1 değil"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "Böyle bir kuyruk mevcut değil"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "Bu bilet, çözümlenmemiş bağımlılıklara sahip"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "Bu kullanıcı, zaten bu biletin sahibi"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "Bu kullanıcı bulunamadı"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "Bu kullanıcı zaten yetkili"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "Bu kullanıcı zaten yetkisiz"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "Bu kullanıcı şu anda yetkilendirildi"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "Bu kullanıcı artık yetkisiz"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "Bu kullanıcı, bu kuyruktaki biletleri sahiplenemez"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "Bu, sayısal bir kimlik değil"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "Temeller"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "Bir biletin KK' sı"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "Bir biletin yönetimsel KK' sı"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "Sonraki komut, 'genel' kuyruğundaki bütün biletleri bulacak ve eğer son 4 saat içinde dokunulmamışlarsa, önem derecelerini 99 a yükseltecektir."
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "Yeni değer atandı."
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "Bir biletin sahibi"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "Bir biletin istekçisi"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "Bu yorumlar, genellikle kullanıcıya görünür değillerdir"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "Bu özel alan, bu nesneye etkimez"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "Bu özellik, sadece sistem yöneticileri tarafından kullanılabilir"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "Bu iletinin gönderileceği kişi..."
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "Bu araç, kullanıcının RT içinden keyfi perl modüllerini çalıştırabilmesine olanak verir."
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "Bu hareketin bir içeriğe sahip olmadığı gözüküyor"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "Bu kullanıcının %1 en yüksek öneme sahip biletleri"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "Per."
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "Bilet #%1 Büyük güncelleme: %2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "Bilet #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "Bilet %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "Bilet %1, '%2' kuyruÄŸunda oluÅŸturuldu"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "Bilet %1: %2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "Bilet Özel Alanları"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "Bilet geçmişi # %1 %2"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "Bilet Çözümlendi"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "Bilet Hareketleri"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "Bilet oluÅŸturuldu"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "Bilet içerik türü"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "Bir iç hata nedeniyle, bilet oluşturulamadı"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "Bilet özel bilgisi"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "Bilet durumu deÄŸiÅŸti"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "BiletSQL arama modülü"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "Biletler"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "Åžundan sonra oluÅŸturulan biletler:"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "Şundan önce oluşturulan biletler:"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "Şundan sonra çözümlenen biletler:"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "Şundan önce çözümlenen biletler:"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "Bu onaya dayanan biletler:"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "Tahmini Süre"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "Kalan Süre"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "Çalışılan Süre"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "Kalan süre"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "Gösterilecek süre"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "Çalışılan süre"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "ÇalışılanSüre"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "Başlık"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "Destek hakkında soru sormak için, kişisel geliştirim veya lisanslama için, lütfen %1 ile temasa geçin."
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "Söylendi"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "Araçlar"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "Toplam"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "Hareket"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "Hareket %1 tasfiye edildi"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "Hareket OluÅŸturuldu"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "Hareket Özel Alanları"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "Transaction->Create gerçekleştirilemedi, çünkü bir nesne türü ve kimliği belirtmediniz"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "Hareketler deÄŸiÅŸemez"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "Sal."
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "Tür"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "Gerçekleştirilmedi"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "Unix giriÅŸi"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "Bilinmeyen İçerikKodlaması %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr "Bilinmeyen alan: $key"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "Sınırsız"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "Adlandırılmamış arama"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "Yetkisiz"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "Seçilmemiş Özel Alanlar"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "Seçilmemiş nesneler"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "Alınmamış"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "Güncelle"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "Bileti Güncelle"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "Türü Güncelle"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "Birden çok bileti güncelle"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "Güncelleme kaydedilmedi"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "Bileti güncelle"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "Bileti güncelle: #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "Bileti güncelle: #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "Güncelleme türü cevap veya yorum değildi."
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "Güncellendi"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "Yükle"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "Çoklu dostya yükle"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "Çoklu resim yükle"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "Tek bir dosya yükle"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "Tek bir resim yükle"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "En çok %1 dosya yükle"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "En çok %1 resim yükle"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "Değişikliklerinizi yükleyin"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "Diğer RT yönetimsel araçlarını kullan"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "Kullanıcı '%1' bulunamadı."
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "Kullanıcı Tanımlandı"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "Kullanıcı tanımlı durumlar ve eylemler"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "Kullanıcı Hakları"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "Kullanıcı oluşturulamıyor: %1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "Kullanıcı oluşturuldu"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "Kullanıcı tanımlı gruplar"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "Kullanıcı yüklendi"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "Kullanıcı-tanımlı gruplar"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "Kullanıcı adı"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "Kullanıcılar"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "Arama kıstaslarıyla eşleşen kullanıcılar"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr "Hareket #%1 kullanılıyor..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "Geçerli Sorgu"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "Geçerlilik"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "DeÄŸerler"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "Gözle"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "YöneticiKkOlarakGözle"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "Gözcüler"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "Çar."
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "Bugün ne yaptım"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "Bir bilet, bütün onaycılar tarafından onaylandığında, özgün bilete cevap yolla"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "Bir bilet, herhangi bir onaycı tarafından onaylandığında, özgün bilete cevap yolla"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "Bir bilet oluÅŸturulduÄŸunda"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "Bir onay bileti oluşturulduğunda, sahibi ve YöneticiKk' yı, nesnenin onaylarını beklediği konusunda uyar"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "Herhangi bir ÅŸey olduÄŸunda"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "Bir bilet çözümlendiğinde"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "Bir biletin sahibi deÄŸiÅŸtiÄŸinde"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "Bir biletin önceliği değiştiğinde"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "Bir biletin kuyruÄŸu deÄŸiÅŸtiÄŸinde"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "Bir biletin durumu deÄŸiÅŸtiÄŸinde"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "Kullanıcı tanımlı bir durum oluştuğunda"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "Bir yorum geldiÄŸinde"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "Cevap geldiÄŸinde"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "Çalışma"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "Çevrimdışı çalışma"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "Çalışıldı"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "Bu biletin sahibi zaten sizsiniz"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "Yetkili bir kullanıcı değilsiniz"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "Öntanımlı aramanın kendisinide değiştirebilirsiniz"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "Sahibi olduÄŸunuz veya sahipsiz biletleri tekrar tahsis edebilirsiniz"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr "Sadece sahibi olmadığınız biletleri alabilirsiniz"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "%2 kuyruÄŸunda %1 bilet buldunuz"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "RT' den çıkış yaptınız"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "Bu kuyrukta bilet oluÅŸturmaya yetkiniz yok."
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "Bu kuyrukta istek oluşturamazsınız."
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "Tekrar giriÅŸ yapabilirsiniz"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "İsteğiniz %1 tarafından onaylandı. Diğer onaylar, halen bekliyor olabilir."
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "İsteğiniz onaylandı"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "Ä°steÄŸiniz reddedildi."
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "Kullanıcı adınız veya parolanız yanlış"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "Posta Kodu"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "kaydedilmiş aramaların oluşturulmasına izin ver"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "kaydedilmiş aramaların yüklenmesine izin ver"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "%1' e verildi"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "ÅŸema"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "kapalı"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "içeriyor"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "gün"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "silindi"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "eÅŸleÅŸmiyor"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "içermiyor"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "eÅŸittir"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "hata: aşağı gidemiyorum"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "hata sola gidemiyorum"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "hata yukarı gidemiyorum"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "hata: silinecek bir ÅŸey yok"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "hata: taşınacak bir şey yok"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "hata: deÄŸiÅŸtirecek bir ÅŸey yok"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "büyüktür"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "grup %1"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "%1 ile gruplanmış"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "saat"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "numara"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "aynıdır"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "aynı değildir"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "azdır"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "eÅŸleÅŸir"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "en az"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "ay"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "yeni"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "adsız"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "deÄŸersiz"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "hiçbiri"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "eÅŸit deÄŸildir"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "açık"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "'%2' kullanıcısı için, kişisel grup '%1'"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "kuyruk %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "reddedildi"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "çözüldü"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "saniye"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "ayar sekmesini göster"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "hesap çizelgesi"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "savsaklandı"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr "biçem: %1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "özet satırları"
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "sistem %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "sistem grubu '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "çağıran bileşen, nedenini belirtmedi"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "bilet #%1 %2"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "betimlenmemiÅŸ grup %1"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "kullanıcı %1"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "hafta"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "yıl"
+
diff --git a/rt/lib/RT/I18N/zh_cn.po b/rt/lib/RT/I18N/zh_cn.po
new file mode 100644
index 0000000..84932e9
--- /dev/null
+++ b/rt/lib/RT/I18N/zh_cn.po
@@ -0,0 +1,8423 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.6.x\n"
+"PO-Revision-Date: 2007-12-09 13:05+0800\n"
+"Last-Translator: Audrey Tang <cpan@audreyt.org>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr " 已删除 %1。"
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr " %1 已更å为 %2。"
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr " %1 已储存。"
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "#%1"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr "$1"
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr "$prefix %1"
+
+#: NOT FOUND IN SOURCE
+msgid "%*(%1,group ticket)"
+msgstr "%*(%1) 件å‚与的申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "%*(%1,ticket) due"
+msgstr "%*(%1) 件é™æœŸå®Œæˆçš„申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "%*(%1,unresolved ticket)"
+msgstr "%*(%1) 件尚未解决的申请å•"
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 #%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%7-%2-%3 %4:%5:%6 %1"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%2 已新增为 %1"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 之å‰"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 已从 %2 改为 %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%2 已自 %1 删除"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 of group %3"
+msgstr "%3 群组的 %1 %2"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "æ¡ä»¶ï¼š%1 | 动作:%2 | 模æ¿ï¼š%3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 这份申请å•\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) - %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (未更改)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "显示第 %1 - %2 笔"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - 传递给 %2 的一个å‚æ•°"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - 将更新状æ€è¾“出到 STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr "%1 - 指定欲使用的模æ¿ç¼–å·"
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr "%1 - 指定欲使用的更动为 'first' (第一项) 或 'last' (最åŽä¸€é¡¹)"
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - 指定欲使用的动作模å—"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - 指定欲使用的æ¡ä»¶æ¨¡å—"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - 指定欲使用的查询模å—"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr "%1 - 指定欲使用的更动类别"
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 版,%4 版æƒæ‰€æœ‰ï¼Œ1996-%3。"
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "加载手续 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 Total"
+msgstr "共 %1 笔"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "新增 %1 作为 %2 的值"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "别å %1 需è¦å¯ç”¨çš„申请å•ç¼–å·"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "别å %1 需è¦å¯ç”¨çš„申请å•ç¼–å· "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "别å %1 需è¦å¯ç”¨çš„申请å•ç¼–å·ä»¥å¤„ç† %3(出自 %2)"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 看æ¥æ˜¯ä¸ªæœ¬åœ°å¯¹è±¡ï¼Œå´ä¸åœ¨æ•°æ®åº“里"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 (%2)"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 的值从 %2 改为 %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 å¤åˆ¶"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "无法将 %1 设定为 %2。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 无法åˆå§‹æ›´æ–° (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 无法将现况设æˆå·²è§£å†³ã€‚RT æ•°æ®åº“内容å¯èƒ½ä¸ä¸€è‡´ã€‚"
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "已建立 %1"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "已删除 %1"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "å‰ %1 份待处ç†ç”³è¯·å•"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "å‰ %1 份待处ç†ç”³è¯·å•..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "å‰ %1 份é€å‡ºçš„申请å•..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets pending my approval..."
+msgstr "å‰ %1 份待签核申请å•..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 是从外部排程程åº(如 cron)æ¥å¯¹ç”³è¯·å•è¿›è¡Œæ“作的工具。"
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 å·²ä¸å†æ˜¯æ­¤è¡¨å•çš„ %2。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 å·²ä¸å†æ˜¯æ­¤ç”³è¯·å•çš„ %2。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 å·²ä¸å†æ˜¯è‡ªè®¢å­—段 %2 的值。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 ä¸æ˜¯ä¸€ä¸ªåˆæ³•çš„表å•ç¼–å·ã€‚"
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 分钟"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "å‰ %1 份待认领的申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "没有显示 %1"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 对象"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 recent tickets I own..."
+msgstr "最新 %1 份待处ç†ç”³è¯·å•..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 recent tickets I requested..."
+msgstr "最新 %1 份é€å‡ºçš„申请å•..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 result(s) found"
+msgstr "找到 %1 项结果"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 完æˆ\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "ä¸çŸ¥é“ $MessageID çš„ %1 类别"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "ä¸çŸ¥é“ %2 çš„ %1 类别"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 was created without a CurrentUser\\n"
+msgstr "%1 新增时未指定现行使用者"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 会解决在已解决群组里æˆå‘˜çš„申请å•ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "如果 %1 起始申请å•ä¾èµ–于æŸä¸ªé“¾æŽ¥ï¼Œæˆ–是æŸä¸ªé“¾æŽ¥çš„æˆå‘˜ï¼Œå®ƒå°†ä¼šè¢«å»¶å®•ã€‚"
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1 内的 %2 对象"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1 内的 %2 的 %3 对象"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "%1 的预存查询"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1:未指定附件"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1 字节"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k 字节"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr "%1 å°æ—¶"
+
+#: NOT FOUND IN SOURCE
+msgid "%quant(%1,result) found"
+msgstr "找到 %1 项结果"
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' ä¸æ˜¯ä¸€ä¸ªåˆæ³•çš„状æ€å€¼"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1'为无法辨识的动作。 "
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(点选欲删除的æˆå‘˜)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(点选欲删除的手续)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(点选欲删除的项目)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(点选欲删除的项目)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(点选欲åœç”¨é€šçŸ¥çš„收件人)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(点选欲å¯ç”¨é€šçŸ¥çš„收件人)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(键入申请å•ç¼–å·æˆ–网å€ï¼Œä»¥ç©ºç™½åˆ†éš”)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(如果留白, 则预设为 %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(没有值)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(没有自订字段)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(没有æˆå‘˜)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(没有手续)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "没有模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "(No workflows)"
+msgstr "没有æµç¨‹"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(æ— )"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„密件副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<b>ä¸ä¼š</b>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„密件副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<b>ä¸ä¼š</b>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„密件副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<strong>ä¸ä¼š</strong>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本给åå•ä¸Šä»¥é€—å·éš”开的管ç†å‘˜ç”µå­é‚®ä»¶åœ°å€ã€‚è¿™<b>将会</b>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本给åå•ä¸Šä»¥é€—å·éš”开的管ç†å‘˜ç”µå­é‚®ä»¶åœ°å€ã€‚è¿™<strong>将会</strong>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<b>ä¸ä¼š</b>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<b>ä¸ä¼š</b>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<strong>ä¸ä¼š</strong>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<b>将会</b>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本给åå•ä¸Šä»¥é€—å·éš”开的电å­é‚®ä»¶åœ°å€ã€‚è¿™<strong>将会</strong>更改åŽç»­çš„收件者åå•ã€‚)"
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(当æ¡ä»¶æˆ–动作设为‘使用者自订’时,请填入这些字段)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr "(ä¸ä¼šæ”¶åˆ°é‚®ä»¶)"
+
+#: NOT FOUND IN SOURCE
+msgid "(default delegate)"
+msgstr "(预设代ç†äºº)"
+
+#: NOT FOUND IN SOURCE
+msgid "(delete)"
+msgstr "(删除)"
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(空白)"
+
+#: NOT FOUND IN SOURCE
+msgid "(new)"
+msgstr "(新增)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(没有列出姓å)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(没有主题)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(æ— )"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(没有值)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(仅能指定一份申请å•)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(等待签核)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(等待其它集åˆ)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(等待其它申请å•)"
+
+#: NOT FOUND IN SOURCE
+msgid "(requestor's group)"
+msgstr "(申请人所属)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(å¿…å¡«)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(未命å)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(yyyy/mm/dd)"
+
+#: NOT FOUND IN SOURCE
+msgid "*"
+msgstr "★"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr "-"
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr "--transaction 的值仅能为 'first' 或 'last'"
+
+#: NOT FOUND IN SOURCE
+msgid ":"
+msgstr ":"
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$field%>"
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"æ出申请å•\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"æ出申请å•\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "空白模æ¿"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr "å£ä»¤æ²¡æœ‰è®¾å®šï¼Œå› æ­¤è¯¥ä½¿ç”¨è€…将无法登入。"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Deleted"
+msgstr "ACE 已删除"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Loaded"
+msgstr "ACE 已加载"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be deleted"
+msgstr "无法删除 ACE"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be found"
+msgstr "找ä¸åˆ° ACE"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "找ä¸åˆ° ACE 设定"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "祇能新增或删除 ACE 设定。"
+
+#: NOT FOUND IN SOURCE
+msgid "ACLEquivalence"
+msgstr "ACLEquivalence"
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "AND"
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "离开以å…ä¸å°å¿ƒæ›´æ”¹åˆ°ç”³è¯·å•ã€‚\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "About Me"
+msgstr "个人信æ¯"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "个人信æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Access Right"
+msgstr "系统使用登录æƒé™"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "å­˜å–æƒé™"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "动作"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "动作 %1 找ä¸åˆ°"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "动作执行完毕"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "动作执行完毕。\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr "动作为必填字段"
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "动作准备完毕..."
+
+#: NOT FOUND IN SOURCE
+msgid "Activated Date"
+msgstr "申请激活时间"
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "新增"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "新增管ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "新增副本收件人"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr "新增字段"
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "新增æ¡ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Entry"
+msgstr "新增列"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "新增更多附件"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Next State"
+msgstr "新增下一项关å¡"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "新增申请人"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "新增字段值"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip to this queue"
+msgstr "新增此表å•çš„手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip which will apply to all queues"
+msgstr "新增适用于所有表å•çš„手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a keyword selection to this queue"
+msgstr "新增此表å•çš„关键è¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "新增全域手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "新增一é“手续到此表å•"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "新增一é“用于所有表å•çš„手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "新增查询æ¡ä»¶"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr "新增并开始查询"
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "新增评论或回å¤åˆ°æŒ‡å®šçš„申请å•"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "新增æˆå‘˜"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "新增视察员"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr "将这些æ¡ä»¶åŠ è¿›æŸ¥è¯¢å†…"
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr "新增值"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr "新增ã€åˆ é™¤åŠä¿®æ”¹å¯¹è±¡çš„自订字段值"
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "新增下一项关å¡"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "å•ä½å·²æ–°å¢žä¸ºæ­¤è¡¨å•çš„ %1"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "å•ä½å·²æ–°å¢žä¸ºæ­¤ç”³è¯·å•çš„ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Additional Hints"
+msgstr "é¢å¤–æ示"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "ä½å€"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "ä½å€(ç»­)"
+
+#: NOT FOUND IN SOURCE
+msgid "Adjust Blinking Rate"
+msgstr "调整闪çƒé€Ÿåº¦å¿«æ…¢"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin"
+msgstr "管ç†å‘˜"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "管ç†å‘˜å‰¯æœ¬"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "管ç†å‘˜è¯„论"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "管ç†å‘˜å›žå¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin Rights"
+msgstr "管ç†å‘˜æƒé™"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "表å•ç®¡ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "使用者管ç†"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "管ç†/全域设定"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "管ç†/群组"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "管ç†/表å•/基本信æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAddress"
+msgstr "管ç†å‘˜ Email"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "管ç†æ‰€æœ‰ä»£ç†äººç¾¤ç»„"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "管ç†å‘˜å‰¯æœ¬"
+
+msgid "AdminCc.EmailAddress"
+msgstr "管ç†å‘˜å‰¯æœ¬: 电å­é‚®ä»¶ä¿¡ç®±"
+
+msgid "Cc.EmailAddress"
+msgstr "副本: 电å­é‚®ä»¶ä¿¡ç®±"
+
+msgid "Requestor.EmailAddress"
+msgstr "申请人: 电å­é‚®ä»¶ä¿¡ç®±"
+
+msgid "Custom.Ownership"
+msgstr "自订: 承办状æ€"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "管ç†å‘˜è¯„论"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "管ç†å‘˜å›žå¤"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "管ç†è‡ªè®¢å­—段"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "管ç†è‡ªè®¢å­—段"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "管ç†ç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupDescription"
+msgstr "管ç†ç¾¤ç»„æè¿°"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "管ç†ç¾¤ç»„æˆå‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupName"
+msgstr "管ç†ç¾¤ç»„å称"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupPermission"
+msgstr "管ç†ç¾¤ç»„æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupStatus"
+msgstr "管ç†ç¾¤ç»„状æ€"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "管ç†ä»£ç†äººç¾¤ç»„"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "管ç†è¡¨å•"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "管ç†ä½¿ç”¨è€…"
+
+#: NOT FOUND IN SOURCE
+msgid "Administrative"
+msgstr "行政类"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "管ç†å‘˜å‰¯æœ¬"
+
+#: NOT FOUND IN SOURCE
+msgid "Admins"
+msgstr "主管"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "进阶"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "进阶查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search Criteria"
+msgstr "进阶查询æ¡ä»¶"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "晚于"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "ç»åŽ†æ—¶é—´"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr "结åˆæ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Alias"
+msgstr "执行其它æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Alias for"
+msgstr "相当于"
+
+#: NOT FOUND IN SOURCE
+msgid "All"
+msgstr "全部"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "完æˆå…¨éƒ¨ç­¾æ ¸"
+
+#: NOT FOUND IN SOURCE
+msgid "All Condition"
+msgstr "所有æ¡ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "所有自订字段"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "所有表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "All Users"
+msgstr "全体员工"
+
+#: NOT FOUND IN SOURCE
+msgid "All done! Now you can proceed to %1."
+msgstr "处ç†å®Œæ¯•ï¼æ‚¨çŽ°åœ¨å¯ä»¥ç»§ç»­è¿›è¡Œ %1。"
+
+#: NOT FOUND IN SOURCE
+msgid "Allowance Request"
+msgstr "ç¦åˆ©è¡¥åŠ©ç”³è¯·"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "无论寄件æ¥æºä¸ºä½•ï¼Œä¸€å¾‹å¯„信给申请人"
+
+#: NOT FOUND IN SOURCE
+msgid "Amount"
+msgstr "æ•°é¢"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "AND/OR"
+
+#: NOT FOUND IN SOURCE
+msgid "Any Condition"
+msgstr "ä»»æ„æ¡ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Applies To"
+msgstr "套用于"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "套用于"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "套用"
+
+#: NOT FOUND IN SOURCE
+msgid "Apply Template"
+msgstr "引用模æ¿"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "套用更动"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "签核"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "ç­¾æ ¸å• #%1:%2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "ç­¾æ ¸å• #%1:系统错误,记录失败"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "ç­¾æ ¸å• #%1:记录完毕"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "签核细节"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Due"
+msgstr "签核时é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Notes"
+msgstr "签核æ„è§"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "完æˆæŸé¡¹ç­¾æ ¸"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "驳回æŸé¡¹ç­¾æ ¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Result"
+msgstr "签核结果"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Status"
+msgstr "核准结果"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Type"
+msgstr "签核ç§ç±»"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "签核æµç¨‹"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "核准"
+
+#: NOT FOUND IN SOURCE
+msgid "Approver"
+msgstr "签核人"
+
+#: NOT FOUND IN SOURCE
+msgid "Approver Setting"
+msgstr "执行签核人设定"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "签核备注:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Apr"
+msgstr "四月"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "04"
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "四月"
+
+#: NOT FOUND IN SOURCE
+msgid "Are you sure to delete checked items?"
+msgstr "您确定è¦åˆ é™¤ï¼Ÿ"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr "递增"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "递增"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "指派åŠç§»é™¤è‡ªè®¢å­—段"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "指派自订字段"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "附件"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "附加档案"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "现有附件"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "无法加载附件 '%1'"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "附件新增完毕"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "附件档å"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "附件"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "已删除该属性"
+
+#: NOT FOUND IN SOURCE
+msgid "Attributes"
+msgstr "属性"
+
+#: NOT FOUND IN SOURCE
+msgid "Aug"
+msgstr "八月"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "08"
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "八月"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "认è¯æ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoReject"
+msgstr "自动驳回表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoResolve"
+msgstr "自动完æˆè¡¨å•å¤„ç†"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "自动回å¤"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "自动对申请人回å¤"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "自动对申请人回å¤"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr "å¯ç”¨"
+
+#: NOT FOUND IN SOURCE
+msgid "Available Columns"
+msgstr "å¯ç”¨çš„字段:"
+
+#: NOT FOUND IN SOURCE
+msgid "Available Rights:"
+msgstr "æƒé™é¡¹ç›®åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Back to Homepage"
+msgstr "回到首页"
+
+#: NOT FOUND IN SOURCE
+msgid "Back to Previous"
+msgstr "回上页"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "错误的 PGP 签章:%1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "错误的附件编å·ã€‚无法找到附件 '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "%1 çš„æ•°æ®é”™è¯¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "附件的处ç†å·ç é”™è¯¯ã€‚%1 应为 %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "基本信æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Batch Approval"
+msgstr "批次签核"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "密件副本"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "请别忘了储存修改。"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "早于"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "开始签核"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin From "
+msgstr "起始日"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr "Best Practical Solutions, LLC å…¬å¸è¯†åˆ«å›¾æ¡ˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "档案"
+
+#: NOT FOUND IN SOURCE
+msgid "Birthday"
+msgstr "生日"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "空白模æ¿"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr "粗体"
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "将查询结果转为å¯æ”¾å…¥ä¹¦ç­¾çš„网å€"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "å¯æ”¾å…¥ä¹¦ç­¾çš„网å€"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "精简标头档"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr "整批更新"
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "整批更新申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Business Unit"
+msgstr "事业部"
+
+#: NOT FOUND IN SOURCE
+msgid "Business Unit:"
+msgstr "事业部:"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "无法更改系统使用者"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "该å•ä½æ˜¯å¦èƒ½æŸ¥é˜…此表å•"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "ä¸èƒ½æ–°å¢žæ²¡æœ‰å称的自订字段值"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr "找ä¸åˆ°â€˜%1’的集åˆç±»åˆ«"
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "找ä¸åˆ°é¢„存查询"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "申请å•ä¸èƒ½é“¾æŽ¥è‡ªå·±ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "ä¸èƒ½æ•´åˆè¿›å·²æ•´åˆè¿‡çš„申请å•ã€‚这个错误ä¸è¯¥å‘生。"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr "无法储存 %1"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "无法储存此项查询"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "ä¸èƒ½åŒæ—¶æŒ‡å®šèµ·å§‹ç”³è¯·å•ä¸Žç›®çš„申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Cancel"
+msgstr "å–消"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "无法新增使用者:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Cannot login: Your system clock differs from server's by %1 seconds!"
+msgstr "您的系统时钟和æœåŠ¡å™¨ç›¸å·® %1 秒,无法登入ï¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Card No."
+msgstr "å¡å·"
+
+#: NOT FOUND IN SOURCE
+msgid "Categories"
+msgstr "分类管ç†"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "分类"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "副本"
+
+#: NOT FOUND IN SOURCE
+msgid "Cc Type"
+msgstr "副本类别"
+
+#: NOT FOUND IN SOURCE
+msgid "Chairperson's Office"
+msgstr "董事长室"
+
+#: NOT FOUND IN SOURCE
+msgid "Change Ticket"
+msgstr "修改申请å•"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "更改å£ä»¤"
+
+#: NOT FOUND IN SOURCE
+msgid "ChangeOwnerUI"
+msgstr "å¯å¦é€‰æ‹©è¡¨å•æ‰¿åŠžäºº"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "全部选å–"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "选择欲删除的项目"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "选择欲撤消的æƒåˆ©"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "å­ç”³è¯·å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Chinese Name"
+msgstr "中文姓å"
+
+#: NOT FOUND IN SOURCE
+msgid "Chinese/English"
+msgstr "中英文"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr "选择日期"
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "所在城市"
+
+#: NOT FOUND IN SOURCE
+msgid "ClassicUI"
+msgstr "传统接å£"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "全部清除"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "关闭窗å£"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "已解决"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "已解决的申请å•"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "已解决的申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Code"
+msgstr "执行程åºç "
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "下拉文字框:选择或键入多é‡é¡¹ç›®"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "下拉文字框:选择或键入å•ä¸€é¡¹ç›®"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "下拉文字框:选择或键入最多 %1 个项目"
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "指令无法辨识ï¼\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "评论"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "评论电å­é‚®ä»¶åœ°å€"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "评论未被纪录"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "对申请å•æ出评论"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "评论申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "评论"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "评论(ä¸é€ç»™ç”³è¯·äºº)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "评论(ä¸é€ç»™ç”³è¯·äºº)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "对 %1 的评论"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "使用者æè¿°"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "新增评论完毕"
+
+#: NOT FOUND IN SOURCE
+msgid "Commit"
+msgstr "确认"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "消除更动完毕"
+
+#: NOT FOUND IN SOURCE
+msgid "Company Name"
+msgstr "å…¬å¸å称"
+
+#: NOT FOUND IN SOURCE
+msgid "CompanySpecific"
+msgstr "å„å…¬å¸ç‹¬ç«‹æ˜¾ç¤º"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "设定查询æ¡ä»¶"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "æ¡ä»¶"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "æ¡ä»¶æ˜¯å¿…填字段"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "符åˆæ¡ä»¶..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "未找到符åˆçš„现况"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "设定"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "确认å£ä»¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Confirm Password"
+msgstr "å£ä»¤ç¡®è®¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Confirm Submit"
+msgstr "确定é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "Contact System Administrator"
+msgstr "连络系统管ç†å‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "连络信æ¯ç³»ç»Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "无法解读è”络日期 '%1'"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "内容"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "内容类型"
+
+#: NOT FOUND IN SOURCE
+msgid "Coould not create group"
+msgstr "无法新增群组"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "å¤åˆ¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Copy Field From:"
+msgstr "欲å¤åˆ¶å­—段:"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "回å¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "申请å•å›žå¤åœ°å€"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "新增申请å•å›žå¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "未纪录申请å•å›žå¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè®¢å­—段的值。"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè®¢å­—段的值。%1 "
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè®¢å­—段的值。"
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè®¢å­—段的值。%1 "
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "ä¸èƒ½æ›´æ”¹æ‰¿åŠžäººã€‚"
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "无法新增自订字段"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "无法新增自订字段:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create Scrip"
+msgstr "无法建立讯æ¯é€šçŸ¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create Template"
+msgstr "无法建立通知模æ¿"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "无法新增群组"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create item"
+msgstr "无法新增项目"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "无法新增模æ¿ï¼š%1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "无法新增申请å•ã€‚尚未指定表å•ã€‚"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "无法新增使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "无法为申请人新增视察员"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create workflow: %1"
+msgstr "无法新增æµç¨‹ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "找ä¸åˆ°ç¼–å· %1 的申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "找ä¸åˆ°ç¾¤ç»„ %1。"
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "找ä¸åˆ°æˆ–无法新增该å使用者"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "找ä¸åˆ°è¯¥å•ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… %1。"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "无法加载字段 %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "无法加载群组"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "无法为 %1 加载对象"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "无法加载查询属性"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "无法将该å•ä½è®¾ä¸ºæ­¤è¡¨å•çš„ %1。"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "无法将该å•ä½è®¾ä¸ºæ­¤ç”³è¯·å•çš„ %1。"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "无法将å•ä½ %1 从表å•ç§»é™¤ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "无法将å•ä½ %1 从申请å•ç§»é™¤ã€‚"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "无法设定使用者信æ¯"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "无法新增附件"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "无法新增æˆå‘˜è‡³ç¾¤ç»„"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "无法新增更动报告"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "无法从 gpg 回函辨识出该采å–的行动\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "找ä¸åˆ°ç¾¤ç»„\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "找ä¸åˆ°æ­¤åˆ—æ•°æ®"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "找ä¸åˆ°è¯¥å•ä½"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "找ä¸åˆ°è¯¥å€¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "找ä¸åˆ°è¯¥è§†å¯Ÿå‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "找ä¸åˆ°ä½¿ç”¨è€…\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "无法从使用者数æ®åº“加载 %1。\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "无法加载类别 %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "无法加载自订字段 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load KeywordSelects."
+msgstr "无法加载 KeywordSelects。"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "无法加载 RT 设定档 '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "无法加载手续。"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr "æ— æ³•åŠ è½½ç”³è¯·å• %1 çš„å¤æœ¬ã€‚"
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "无法加载手续 %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "无法加载链接。"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "无法加载对象 %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "无法加载表å•"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "æ— æ³•åŠ è½½è¡¨å• %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "无法加载手续"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr "无法加载手续 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "无法加载模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "无法加载该å使用者(%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "æ— æ³•åŠ è½½ç”³è¯·å• '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr "无法将‘%1’解读为网å€"
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "国家"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "新增"
+
+#: NOT FOUND IN SOURCE
+msgid "Create Subgroup:"
+msgstr "新增å­ç¾¤ç»„:"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "新增申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Create User:"
+msgstr "新增æˆå‘˜ï¼š"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "新增自订字段"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "为 %1 表å•æ–°å¢žè‡ªè®¢å­—段"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "为 %1 表å•æ–°å¢žè‡ªè®¢å­—段"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "新增自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global Scrip"
+msgstr "新增全域手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "新增全域手续"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "新增群组"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "新增代ç†äººç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "新增表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "新增手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "新增模æ¿"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "新增申请å•"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "新增使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new workflow"
+msgstr "新增æµç¨‹"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "新增表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "新增表å•å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "æ出申请"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "为 %1 表å•æ–°å¢žæ‰‹ç»­"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "新增模æ¿"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "æ出申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a workflow"
+msgstr "新增æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "新增失败:%1 / %2 / %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "新增失败:%1/%2/%3"
+
+#: NOT FOUND IN SOURCE
+msgid "Create new item"
+msgstr "建立新项目"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "ä¾æ®æ­¤é¡¹æ‰‹ç»­å†…的模版,新增申请å•"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "新增申请å•"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "在此表å•ä¸­æ–°å¢žç”³è¯·å•"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "新增ã€åˆ é™¤åŠæ›´æ”¹è‡ªè®¢å­—段"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "新增ã€åˆ é™¤åŠæ›´æ”¹è¡¨å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "新增ã€åˆ é™¤åŠæ›´æ”¹ä»»ä½•ä½¿ç”¨è€…的代ç†äººç¾¤ç»„"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "新增ã€åˆ é™¤åŠæ›´æ”¹ä»£ç†äººç¾¤ç»„"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "新增ã€åˆ é™¤åŠæ›´æ”¹ä½¿ç”¨è€…"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr "新增预存查询"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "新增申请å•"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "新增日"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "自订字段 %1 新增æˆåŠŸ"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr "在指定日期内建立"
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "æ¨¡æ¿ %1 新增æˆåŠŸ"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr "在指定日期内建立的申请å•ï¼Œä¾çŠ¶æ€åˆ†ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "Created workflow %1"
+msgstr "æµç¨‹ %1 新增æˆåŠŸ"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "建立者"
+
+#: NOT FOUND IN SOURCE
+msgid "Currency"
+msgstr "å¸åˆ«"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Approval Info"
+msgstr "截至目å‰ç­¾æ ¸ä¿¡æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Custom Fields"
+msgstr "现有自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Groups:"
+msgstr "现有群组列表:"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "现有关系"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Rights:"
+msgstr "现有æƒé™ï¼š"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "现有手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Status"
+msgstr "ç›®å‰çŠ¶æ€"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Templates"
+msgstr "现有模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Watchers"
+msgstr "现有视察员"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "现有æˆå‘˜"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "现有æƒé™"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr "现有查询æ¡ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "现有查询æ¡ä»¶"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "现有视察员"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "自订字段 #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "自订字段"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "%1 的自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Fields which apply to all queues"
+msgstr "适用于所有表å•çš„自订字段"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "动作åŽæ‰§è¡Œç¨‹åº"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "动作å‰æ‰§è¡Œç¨‹åº"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "自订æ¡ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "自订字段 %1 %2 %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 does not apply to this object"
+msgstr "自订字段 %1 ä¸é€‚用于此对象"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "自订字段 %1 已有值"
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "自订字段 %1 没有值"
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "找ä¸åˆ°è‡ªè®¢å­—段 %1"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr "自订字段‘%1’"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "自订字段已删除"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "找ä¸åˆ°è‡ªè®¢å­—段"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "无法从自订字段 %2 中找到 %1 这个字段值"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "自订字段值从 %1 改为 %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "无法删除自订字段值"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "找ä¸åˆ°è‡ªè®¢å­—段值"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "自订字段值删除æˆåŠŸ"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "自订字段"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr "自订"
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "æ•°æ®é”™è¯¯"
+
+#: NOT FOUND IN SOURCE
+msgid "DatabaseBindRemote"
+msgstr "容许外部è”机"
+
+#: NOT FOUND IN SOURCE
+msgid "DatabaseName"
+msgstr "MySQLæ•°æ®åº“"
+
+#: NOT FOUND IN SOURCE
+msgid "Date of Departure"
+msgstr "出å‘日期"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "日期"
+
+#: NOT FOUND IN SOURCE
+msgid "Dec"
+msgstr "å二月"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "12"
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "å二月"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Approval"
+msgstr "预设签核"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "预设自动å“应模æ¿"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "预设自动å“应模æ¿"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "预设表å•"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "预设申请人"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Value"
+msgstr "预设值"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "预设管ç†å‘˜è¯„论模æ¿"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "预设管ç†å‘˜å›žå¤æ¨¡æ¿"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "预设回å¤æ¨¡æ¿"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "预设更动模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "预设:%1/%2 已自 %3 改为 %4"
+
+#: NOT FOUND IN SOURCE
+msgid "DefaultApproval"
+msgstr "预设签核"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "代ç†äººæƒé™"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "将拥有的æƒé™å§”托他人代ç†"
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "设定代ç†äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Approval"
+msgstr "代ç†ç­¾æ ¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Queue"
+msgstr "代ç†è¡¨å•å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Queue:"
+msgstr "代ç†è¡¨å•ï¼š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Type"
+msgstr "代ç†è¡¨å•ç§ç±»"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates"
+msgstr "代ç†äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Enabled Status"
+msgstr "代ç†æ¿€æ´»çŠ¶æ€"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Info"
+msgstr "代ç†äººä¿¡æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Period"
+msgstr "代ç†æœŸé—´"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Permission Setting"
+msgstr "代ç†æƒé™è®¾å®š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Permission:"
+msgstr "代ç†æƒé™ï¼š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Setting"
+msgstr "代ç†äººè®¾å®š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Status"
+msgstr "代ç†çŠ¶æ€"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "代ç†äººæƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegation Groups"
+msgstr "代ç†äººç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegation Rights"
+msgstr "代ç†äººæƒé™"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "删除"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "删除模æ¿"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr "删除失败:%1"
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "删除指定的手续"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "删除申请å•"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr "删除值"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "删除申请å•"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr "已删除的æœå¯»"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "删除此对象å¯èƒ½ç ´åå‚考完整性"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "删除此对象å¯èƒ½ç ´åå‚考完整性"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "删除此对象会è¿åå‚考完整性"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "删除此对象会è¿åå‚考完整性"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity. That's bad."
+msgstr "删除此对象会è¿åå‚考完整性"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "驳回"
+
+#: NOT FOUND IN SOURCE
+msgid "Department"
+msgstr "部门"
+
+#: NOT FOUND IN SOURCE
+msgid "Department ID"
+msgstr "部门代ç "
+
+#: NOT FOUND IN SOURCE
+msgid "Department Name"
+msgstr "部门å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Department's"
+msgstr "部门之"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure Details"
+msgstr "差旅明细"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure From"
+msgstr "差旅起始日"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure Request"
+msgstr "请å‡å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure Until"
+msgstr "差旅截止日"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "å¯æŽ¥ç»­å¤„ç†çš„申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "附属性:\\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "已加入å¯æŽ¥ç»­å¤„ç†çš„ç”³è¯·å• %1"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "已移除å¯æŽ¥ç»­å¤„ç†çš„ç”³è¯·å• %1"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "已加入需先处ç†çš„ç”³è¯·å• %1"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "已移除需先处ç†çš„ç”³è¯·å• %1"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "需先处ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "需先处ç†"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr "递å‡"
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "递å‡"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "在以下字段æ述主题"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Description of Responsibility"
+msgstr "ç»åŠžä¸šåŠ¡è¯´æ˜Ž"
+
+#: NOT FOUND IN SOURCE
+msgid "Description:"
+msgstr "æ述:"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "细节"
+
+#: NOT FOUND IN SOURCE
+msgid "Direct"
+msgstr "直接"
+
+#: NOT FOUND IN SOURCE
+msgid "Disability"
+msgstr "残障身分"
+
+#: NOT FOUND IN SOURCE
+msgid "Disability Type"
+msgstr "残障类别"
+
+#: NOT FOUND IN SOURCE
+msgid "Disabled"
+msgstr "åœç”¨"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "显示内容"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "显示æƒé™æŽ§åˆ¶æ¸…å•"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "显示字段"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "显示此表å•çš„模æ¿"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "显示此表å•çš„手续"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "显示模å¼"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "显示此群组的预存查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "显示第%1å·ç”³è¯·å•"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "ä¾ <a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU 通用公共授æƒ</a> 第二版散布。"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "å…许一切æ“作"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "ä¸æ›´æ–°æ­¤é¡µé¢ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "ä¸æ˜¾ç¤ºæŸ¥è¯¢ç»“æžœ"
+
+#: NOT FOUND IN SOURCE
+msgid "Done"
+msgstr "完æˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Down"
+msgstr "下一页"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "下载"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "下载以 Tab 分隔的档案"
+
+#: NOT FOUND IN SOURCE
+msgid "Dr."
+msgstr "åšå£«"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "到期日"
+
+#: NOT FOUND IN SOURCE
+msgid "Due Date"
+msgstr "截止日"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "无法解读日期 '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "æ— æ³•åŠ è½½ç”³è¯·å• '%1':%2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "编辑"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Conditions"
+msgstr "编辑å‰ç½®æ¡ä»¶"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr "编辑自订字段"
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "编辑 %1 的自订字段"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr "编辑适用于所有群组的自订字段"
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr "编辑适用于所有使用者的自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Custom Fields for queue %1"
+msgstr "ç¼–è¾‘è¡¨å• %1 的自订字段"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr "编辑适用于所有表å•å†…申请å•çš„自订字段"
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "编辑申请å•å…³ç³»"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "编辑查询"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr "编辑查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Subgroups"
+msgstr "新增/维护å­ç¾¤ç»„"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "ç¼–è¾‘è¡¨å• %1 的模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Workflows for queue %1"
+msgstr "ç¼–è¾‘è¡¨å• %1 çš„æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "编辑关键è¯"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "编辑此群组的预存查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "编辑手续"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "编辑全域模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit system workflows"
+msgstr "编辑全域æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "编辑 %1 的模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit workflows for %1"
+msgstr "编辑 %1 çš„æµç¨‹"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "编辑预存查询"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "ç¼–è¾‘è¡¨å• %1 的设定"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "编辑使用者 %1 的设定"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "编辑自订字段 %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "编辑群组 %1 çš„æˆå‘˜ä¿¡æ¯"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "编辑代ç†äººç¾¤ç»„ %1 çš„æˆå‘˜ä¿¡æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "ç¼–è¾‘æ¨¡æ¿ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing workflow %1"
+msgstr "编辑æµç¨‹ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Education"
+msgstr "最高学历"
+
+#: NOT FOUND IN SOURCE
+msgid "EffectiveId"
+msgstr "有效编å·"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "需è¦æŒ‡å®šèµ·å§‹ç”³è¯·å•æˆ–目的申请å•"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "电å­é‚®ä»¶ä¿¡ç®±"
+
+#: NOT FOUND IN SOURCE
+msgid "Email Address"
+msgstr "电å­é‚®ä»¶ä¿¡ç®±"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "此电å­é‚®ä»¶ä¿¡ç®±å·²è¢«ä½¿ç”¨"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "电å­é‚®ä»¶ä¿¡ç®±åœ°å€"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "电å­é‚®ä»¶æ–‡å­—ç¼–ç æ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Embark Date"
+msgstr "外ç±å‘˜å·¥å…¥å¢ƒæ—¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Embarked Date"
+msgstr "抵达日期"
+
+#: NOT FOUND IN SOURCE
+msgid "Embarked Location"
+msgstr "抵达地点"
+
+#: NOT FOUND IN SOURCE
+msgid "Enable Delegates"
+msgstr "代ç†æ¿€æ´»"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "å¯ç”¨(å–消勾选将åœç”¨æ­¤è‡ªè®¢å­—段)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "å¯ç”¨(å–消勾选将åœç”¨æ­¤ç¾¤ç»„)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "å¯ç”¨(å–消勾选将åœç”¨æ­¤è¡¨å•)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "å·²å¯ç”¨çš„自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Date"
+msgstr "å¯ç”¨æ—¥æœŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Date:"
+msgstr "激活日期:"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "å·²å¯ç”¨çš„表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Status"
+msgstr "å¯ç”¨çŠ¶æ€"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "å¯ç”¨çŠ¶æ€ %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "å¯ç”¨çŠ¶æ€: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "End of Trial"
+msgstr "试用期满日"
+
+#: NOT FOUND IN SOURCE
+msgid "English Name"
+msgstr "英文姓å"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "键入多é‡é¡¹ç›®"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "键入欲将对象连结至的对象或 URI。项目之间请以空白隔开。"
+
+#: NOT FOUND IN SOURCE
+msgid "Enter one or more conditions below to search for users"
+msgstr "键入下列å•ä¸€æˆ–å¤å¼æ¡ä»¶ï¼ŒæŸ¥è¯¢ç”¨æˆ·æ•°æ®"
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "键入å•ä¸€é¡¹ç›®"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "键入欲将表å•è¿žç»“至的对象或 URI。项目之间请以空白隔开。"
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "键入申请å•å¯é“¾æŽ¥åˆ°çš„申请å•ç¼–å·æˆ–网å€ã€‚项目之间请以空白隔开。"
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "键入最多 %1 个项目"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryBoolean"
+msgstr "是éžå¡«è¡¨"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryDate"
+msgstr "日期填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryExternal"
+msgstr "系统填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryFreeform"
+msgstr "输入填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryMultiple"
+msgstr "多选填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryNumber"
+msgstr "数值填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntrySelect"
+msgstr "å•é€‰å¡«è¡¨"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryTime"
+msgstr "时间填表"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "错误"
+
+#: NOT FOUND IN SOURCE
+msgid "Error adding watcher"
+msgstr "新增视察员失败"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "表å•->新增视察员的å‚数有误"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "表å•->删除视察员的å‚数有误"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "申请å•->新增视察员的å‚数有误"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "申请å•->删除视察员的å‚数有误"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "调整申请å•ä¼˜å…ˆç­‰çº§"
+
+#: NOT FOUND IN SOURCE
+msgid "Estimate"
+msgstr "预计"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "预计"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "所有人"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "范例:"
+
+#: NOT FOUND IN SOURCE
+msgid "Existing user renamed from %1 to %2"
+msgstr "现有使用者 %1 已改å为 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Export"
+msgstr "汇出"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "外部认è¯å¸å·"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "外部è”络方å¼å¸å·"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalDatabaseDSN"
+msgstr "外部数æ®åº“连结字符串"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalDatabasePass"
+msgstr "外部数æ®åº“å£ä»¤"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalDatabaseUser"
+msgstr "外部数æ®åº“用户"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalURL"
+msgstr "外部接å£ç½‘å€"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "备注"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "查询属性建立失败"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "找ä¸åˆ°â€˜å†…部æˆå‘˜â€™è™šæ‹Ÿç¾¤ç»„的使用者。"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "找ä¸åˆ°â€˜éžå†…部æˆå‘˜â€™è™šæ‹Ÿç¾¤ç»„的使用者。"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "æ— æ³•åŠ è½½æ¨¡å— %1。(%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr "无法为 %1 加载对象。"
+
+#: NOT FOUND IN SOURCE
+msgid "Feb"
+msgstr "二月"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "02"
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "二月"
+
+#: NOT FOUND IN SOURCE
+msgid "Female"
+msgstr "女"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Content:"
+msgstr "字段内容:"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Description"
+msgstr "字段æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Name"
+msgstr "字段å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Type"
+msgstr "字段类别"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "æ¡£å"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "填入多个文字框"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr "填入多个 Wiki 文字框"
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "填入一个文字框"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr "填入一个 Wiki 文字框"
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr "填入一个网å€"
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "填入最多 %1 个文字框"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr "填入最多 %1 个 Wiki 文字框"
+
+#: NOT FOUND IN SOURCE
+msgid "Filter"
+msgstr "筛选"
+
+#: NOT FOUND IN SOURCE
+msgid "Filter people"
+msgstr "对象筛选"
+
+#: NOT FOUND IN SOURCE
+msgid "Filtered list:"
+msgstr "筛选列表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "最终"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "最终顺ä½"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "最终顺ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Financial Department:"
+msgstr "财务部:"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "寻找群组的"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "寻找群组的"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "寻找/å¼€å¯ç”³è¯·å•"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "寻找人员的"
+
+#: NOT FOUND IN SOURCE
+msgid "Find queues whose"
+msgstr "寻找表å•çš„"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "寻找申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "签核完毕"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "第一项"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "第一页"
+
+#: NOT FOUND IN SOURCE
+msgid "First-"
+msgstr "一"
+
+#: NOT FOUND IN SOURCE
+msgid "First-level Admins"
+msgstr "一阶主管"
+
+#: NOT FOUND IN SOURCE
+msgid "First-level Users"
+msgstr "一阶主管员工"
+
+#: NOT FOUND IN SOURCE
+msgid "Fixed shift"
+msgstr "固定ç­"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "甲 乙 丙"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "甲ï¼"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "强制更æ¢"
+
+#: NOT FOUND IN SOURCE
+msgid "Form Processing"
+msgstr "电å­è¡¨å•ä½œä¸šåŒº"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr "æ ¼å¼"
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "找到 %1 张申请å•"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "已找到对象"
+
+#: NOT FOUND IN SOURCE
+msgid "Fourth-"
+msgstr "å››"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "输入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "è”络方å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformDate"
+msgstr "日期输入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformExternal"
+msgstr "系统字段"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "多é‡è¾“å…¥"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformNumber"
+msgstr "数值输入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformPassword"
+msgstr "å£ä»¤è¾“å…¥"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "å•ä¸€è¾“å…¥"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformTime"
+msgstr "时间输入"
+
+#: NOT FOUND IN SOURCE
+msgid "Fri"
+msgstr "星期五"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "星期五"
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "完整标头档"
+
+#: NOT FOUND IN SOURCE
+msgid "Gecos"
+msgstr "登入å¸å·"
+
+#: NOT FOUND IN SOURCE
+msgid "Gender"
+msgstr "性别"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "å–出档案里的模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "å–å¾—ç›®å‰ä½¿ç”¨è€…çš„ pgp 签章\\n"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "交予 %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "全域设定"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Approval"
+msgstr "全域签核"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "全域自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Keyword Selections"
+msgstr "全域关键è¯é€‰å–"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Rights:"
+msgstr "拥有全域æƒé™åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "全域手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Setup"
+msgstr "全域设定"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr "全域自订字段设定"
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr "æˆåŠŸå‚¨å­˜å…¨åŸŸå…¥å£ç»„件 %1。"
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "全域模æ¿ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "GlobalApproval"
+msgstr "全域签核"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "执行"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "执行"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "%1 的 pgp 签章是正确的\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "到页é¢"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "跳到申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Grand"
+msgstr "上"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "群组"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "群组 %1 %2:%3"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Admin"
+msgstr "群组管ç†å‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Description"
+msgstr "群组æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Management"
+msgstr "群组管ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Members"
+msgstr "群组æˆå‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Name"
+msgstr "群组å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Name:"
+msgstr "群组å称:"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "群组æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Rights:"
+msgstr "拥有群组æƒé™åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Setup"
+msgstr "群组设定"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Status"
+msgstr "群组状æ€"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "群组内已有此æˆå‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "无法新增群组"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "无法新增群组:%1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "群组新增完毕"
+
+#: NOT FOUND IN SOURCE
+msgid "Group created: %1"
+msgstr "群组 %1 新增完毕"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "群组没有这个æˆå‘˜"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "找ä¸åˆ°ç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "找ä¸åˆ°ç¾¤ç»„。\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "未指定群组。\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group redescribed from %1 to %2"
+msgstr "群组æè¿° %1 已改为 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Group renamed from %1 to %2"
+msgstr "群组 %1 已改å为 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Group with Queue Rights"
+msgstr "拥有表å•æƒé™ç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "Group's"
+msgstr "群组之"
+
+#: NOT FOUND IN SOURCE
+msgid "Group:"
+msgstr "群组:"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "群组"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "ä¸èƒ½å°†ç¾¤ç»„设为群组内æˆå‘˜"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "符åˆæŸ¥è¯¢æ¡ä»¶çš„群组"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "使用者所属的群组"
+
+#: NOT FOUND IN SOURCE
+msgid "Groups with Global Rights"
+msgstr "拥有全域æƒé™ç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "HRMSDefined"
+msgstr "组织架构"
+
+#: NOT FOUND IN SOURCE
+msgid "HTML Attributes"
+msgstr "HTML 属性"
+
+#: NOT FOUND IN SOURCE
+msgid "Health Insurance"
+msgstr "å¥ä¿è¡¥åŠ©èº«ä»½"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "å—¨ï¼"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "嗨,%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Help"
+msgstr "说明"
+
+#: NOT FOUND IN SOURCE
+msgid "Help Desks"
+msgstr "å„项业务窗å£"
+
+#: NOT FOUND IN SOURCE
+msgid "Hidden"
+msgstr "éšè—"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "纪录"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "群组 %1 的纪录"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "使用者 %1 的纪录"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "ä½å¤„电è¯"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "主页"
+
+#: NOT FOUND IN SOURCE
+msgid "Hotel Expense"
+msgstr "ä½å®¿è´¹"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr "å°æ—¶"
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "我有 %quant(%1,份固体æ…拌器)。"
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr "我æ˜äº†"
+
+#: NOT FOUND IN SOURCE
+msgid "ID Number"
+msgstr "身分è¯å·"
+
+#: NOT FOUND IN SOURCE
+msgid "ID Type"
+msgstr "身分类别"
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "ç¼–å·"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "身份"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "若签核å•é­åˆ°é©³å›žï¼Œåˆ™è¿žå¸¦é©³å›žåŽŸç”³è¯·å•ï¼Œå¹¶åˆ é™¤å…¶å®ƒç›¸å…³çš„待签核事项"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "若没有指定申请者,则以此使用者作为申请者"
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "申请å•è‹¥æ²¡æœ‰æŒ‡å®šè¡¨å•ï¼Œåˆ™å°†å®ƒæ–°å¢žåœ¨æ­¤è¡¨å•å†…"
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "如果此工具程åºä¸º setgid,æ¶æ„的本地端用户å³èƒ½ç”±æ­¤å–å¾— RT 的管ç†å‘˜æƒé™ã€‚"
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "若您已更新以上数æ®ï¼Œè¯·è®°å¾—按一下"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "%1 的值错误"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "图片"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "此字段值ä¸å¯æ›´åŠ¨"
+
+#: NOT FOUND IN SOURCE
+msgid "Import"
+msgstr "汇入"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "列出åœç”¨çš„自订字段"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "列出åœç”¨çš„群组"
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "列出åœç”¨çš„表å•"
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "列出åœç”¨çš„使用者"
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr "引入页é¢"
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr "ä¸å®Œæ•´çš„查询"
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr "ä¸å®Œæ•´çš„查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Indirect Employee"
+msgstr "直接/间接员工"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "åˆå§‹ä¼˜å…ˆé¡ºä½"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "åˆå§‹ä¼˜å…ˆé¡ºä½"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "输入错误"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr "è¾“å…¥å¿…é¡»ç¬¦åˆ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Interest noted"
+msgstr "登记æˆåŠŸ"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "内部错误"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "内部错误:%1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "错误的群组类别"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "错误的æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "错误的类型"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "错误的数æ®"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "错误的承办人。改为预设承办人‘nobody’。"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr "ä¸åˆç†çš„æ ·å¼ï¼š%1"
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "错误的表å•"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "错误的æƒé™"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "%1 的值错误"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "错误的自订字段值"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "错误的状æ€å€¼"
+
+#: NOT FOUND IN SOURCE
+msgid "IssueStatement"
+msgstr "é€å‡ºé™ˆè¿°"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "请ç»å¯¹ä¸è¦è®©æœªå…·æƒé™çš„使用者执行此工具程åºã€‚"
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "建议您新增一个隶属于正确群组的低æƒé™ç³»ç»Ÿä½¿ç”¨è€…,并以该身份执行此工具程åºã€‚"
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "它接å—下列å‚数:"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr "斜体"
+
+#: NOT FOUND IN SOURCE
+msgid "Item Name"
+msgstr "å“å"
+
+#: NOT FOUND IN SOURCE
+msgid "Items"
+msgstr "笔"
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "待签核项目"
+
+#: NOT FOUND IN SOURCE
+msgid "Jan"
+msgstr "一月"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "01"
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "一月"
+
+#: NOT FOUND IN SOURCE
+msgid "Job"
+msgstr "èŒç§°"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "加入或离开此群组"
+
+#: NOT FOUND IN SOURCE
+msgid "Jul"
+msgstr "七月"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "07"
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "七月"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "全部信æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Jun"
+msgstr "六月"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "06"
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "六月"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "关键è¯"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelAttachments"
+msgstr "附件å·æ ‡"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelContent"
+msgstr "内容å·æ ‡"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelSubject"
+msgstr "主题å·æ ‡"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelURL"
+msgstr "链接å·æ ‡"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "使用语言"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "语言"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr "大"
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "上次更新"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "上次è”络"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "上次è”络日期"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "上次通知"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "上次更新"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "上次更新"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "上次更新者"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "剩馀时间"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "å…许这å使用者登入"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "内部æˆå‘˜ï¼ˆå…·æœ‰ä¸ªäººæƒé™ï¼‰"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "é™åˆ¶æ‰¿åŠžäººä¸º %1 到%2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "é™åˆ¶è¡¨å•ä¸º %1 到 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr "链接"
+
+#: NOT FOUND IN SOURCE
+msgid "Link a Queue"
+msgstr "申请表å•è¿žç»“"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "此链接已存在"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "无法新增链接"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "链接(%1)新增完毕"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "链接(%1)删除完毕"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "找ä¸åˆ°é“¾æŽ¥"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "é“¾æŽ¥ç”³è¯·å• #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "é“¾æŽ¥ç”³è¯·å• %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr "将值连结至"
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr "连结中。æƒé™ä¸è¶³"
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "链接"
+
+#: NOT FOUND IN SOURCE
+msgid "List All Users"
+msgstr "列出所有用户数æ®"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "加载"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "加载预存查询:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr "加载预存查询"
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "已加载的 Perl 模å—"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr "已加载查询 %1"
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "ä½ç½®"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "登入目录 %1 找ä¸åˆ°æˆ–无法写入\\n。无法执行 RT。"
+
+#: NOT FOUND IN SOURCE
+msgid "LogToFile"
+msgstr "纪录等级"
+
+#: NOT FOUND IN SOURCE
+msgid "LogToFileNamed"
+msgstr "纪录档å"
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "使用者:%1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "登入"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "注销"
+
+#: NOT FOUND IN SOURCE
+msgid "Long-term contractor"
+msgstr "长期契约员工"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "对应的类别ä¸ç¬¦"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "新增承办人"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "新增现况"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "新增到期日"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "新增解决日期"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "新增实际起始日期"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "新增应起始日期"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "新增报告日期"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "新增优先顺ä½"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "新增表å•"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "新增主题"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr "让此群组能被使用者看è§"
+
+#: NOT FOUND IN SOURCE
+msgid "Male"
+msgstr "ç”·"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "管ç†è‡ªè®¢å­—段åŠå­—段值"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "管ç†ç¾¤ç»„åŠæ‰€å±žæˆå‘˜"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "管ç†é€‚用于所有表å•çš„属性与设定"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "管ç†å„表å•åŠç›¸å…³å±žæ€§"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "管ç†ä½¿ç”¨è€…与å£ä»¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Manager"
+msgstr "ç»ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Mar"
+msgstr "三月"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "03"
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "三月"
+
+#: NOT FOUND IN SOURCE
+msgid "Marketing Department"
+msgstr "行销部"
+
+#: NOT FOUND IN SOURCE
+msgid "Match Pattern"
+msgstr "符åˆæ ·å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "五月"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "05"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "æˆå‘˜ %1 新增完毕"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "æˆå‘˜ %1 删除完毕"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "新增æˆå‘˜å®Œæ¯•"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "æˆå‘˜å·²åˆ é™¤"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "æˆå‘˜æœªåˆ é™¤"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "隶属于"
+
+#: NOT FOUND IN SOURCE
+msgid "Member since"
+msgstr "注册日期"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "隶属于"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "æˆå‘˜"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "所属群组 %1 加入完毕"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "所属群组 %1 移除完毕"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "所属群组"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "使用者 %1 的所属群组"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "æ•´åˆå®Œæ¯•"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "æ•´åˆå¤±è´¥ã€‚无法设定 EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr "æ•´åˆå¤±è´¥ã€‚无法设定 Status"
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "æ•´åˆè¿›"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "已整åˆè¿› %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "讯æ¯"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "信件内文ä¸æ˜¯çº¯æ–‡å­—,因此无法显示。"
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "无法纪录讯æ¯"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "讯æ¯çºªå½•æˆåŠŸ"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "此申请å•çš„相关讯æ¯ä¸ä¼šå¯„é€ç»™..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr "分钟"
+
+#: NOT FOUND IN SOURCE
+msgid "Misc. Expense"
+msgstr "æ‚è´¹"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "未对é½çš„括å·"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "缺少主键值?(%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Missing mandatory fields"
+msgstr "缺少必填字段"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "行动电è¯"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "行动电è¯"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "更改æƒé™æŽ§åˆ¶æ¸…å•"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "更改适用于 %1 内所有 %2 的自订字段"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "更改适用于所有%1的自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "更改适用于所有表å•çš„自订字段"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "更改群组æƒé™"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "更改æˆå‘˜"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "更改æƒé™"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "更改此表å•çš„模æ¿"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "更改此表å•çš„手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "更改系统æƒé™æ¸…å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "æ›´æ”¹æ¨¡æ¿ %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "更改使用者æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Workflow"
+msgstr "更改æµç¨‹"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "更改 %1 表å•å†…的自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "更改适用于所有表å•çš„自订字段"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "更改 %1 表å•å†…的手续"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "更改适用于所有表å•çš„手续"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "更改适用 %1 的对象"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "更改 # %1 的日期"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "更改 #%1 的日期"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "æ›´æ”¹ç”³è¯·å• # %1 的日期"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "更改全域自订字段"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "更改全域设定的群组æƒé™"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "更改全域设定的群组æƒé™ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "更改全域设定的群组æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "更改全域设定的使用者æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "更改全域手续"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "更改全域设定的使用者æƒé™"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "更改全域设定的使用者æƒé™ã€‚"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "更改群组数æ®åŠåˆ é™¤ç¾¤ç»„"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "更改自订字段 %1 的群组æƒé™"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "更改群组 %1 的群组æƒé™"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "æ›´æ”¹è¡¨å• %1 的群组æƒé™"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "更改此群组的æˆå‘˜åå•"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "更改个人的å¸å·ä¿¡æ¯"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "æ›´æ”¹é“¾æŽ¥åˆ°è¡¨å• %1 的人员"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "æ›´æ”¹ç”³è¯·å• #%1 链接到的人员"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "æ›´æ”¹è¡¨å• %1 的手续"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "更改适用于所有表å•çš„手续"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "æ›´æ”¹æ¨¡æ¿ %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "更改适用于所有表å•çš„模æ¿"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "更改预设的‘RT 一览’检视"
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "更改群组 %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "更改表å•è§†å¯Ÿå‘˜"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "更改使用者 %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "æ›´æ”¹ç”³è¯·å• # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "æ›´æ”¹ç”³è¯·å• # %1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "更改申请å•"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "更改自订字段 %1 的使用者æƒé™"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "更改群组 %1 的使用者æƒé™"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "æ›´æ”¹è¡¨å• %1 的使用者æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "更改 '%1' 的视察员"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify workflow %1"
+msgstr "更改æµç¨‹ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify workflows which apply to all queues"
+msgstr "更改适用于所有表å•çš„æµç¨‹"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "更改æƒé™æ¸…å•"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "更改自订字段"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "更改自己是å¦å±žäºŽæŸç¾¤ç»„"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "更改表å•è§†å¯Ÿå‘˜"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "更改手续"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "更改个人å¸å·"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "更改模æ¿"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "更改申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Mon"
+msgstr "星期一"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "星期一"
+
+#: NOT FOUND IN SOURCE
+msgid "More"
+msgstr "更多"
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "关于 %1 的进一步信æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Morning Shift"
+msgstr "æ—©ç­"
+
+#: NOT FOUND IN SOURCE
+msgid "Move"
+msgstr "移动"
+
+#: NOT FOUND IN SOURCE
+msgid "Move All"
+msgstr "全移"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "下移"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "上移"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "多é‡"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "必须指定 'Name' 的属性"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "我的 %1 申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "表å•ç­¾æ ¸"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr "今日事"
+
+#: NOT FOUND IN SOURCE
+msgid "My Requests"
+msgstr "表å•ç”³è¯·è¿½è¸ª"
+
+#: NOT FOUND IN SOURCE
+msgid "My Tickets"
+msgstr "表å•å¤„ç†"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "表å•ç­¾æ ¸"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "我的预存查询"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "å称"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "å¸å·å·²æœ‰äººä½¿ç”¨"
+
+#: NOT FOUND IN SOURCE
+msgid "Nationality"
+msgstr "国ç±"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "需先由系统管ç†å‘˜è¿›è¡Œæ‰¹å‡†"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "从未更动"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "新建立"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "新增关系"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "æ–°çš„å£ä»¤"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "新的待签核事项"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "新增查询"
+
+#: NOT FOUND IN SOURCE
+msgid "New Request"
+msgstr "表å•ç”³è¯·"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "新增查询"
+
+#: NOT FOUND IN SOURCE
+msgid "New Watchers"
+msgstr "新增视察员"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "新增自订字段"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "新增群组"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "æ–°çš„å£ä»¤"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "é€å‡ºæ–°å£ä»¤é€šçŸ¥"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "新增表å•"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr "新增æ醒项目:"
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "æ出申请å•"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "新增æƒé™"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "新增手续"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "é‡æ–°æŸ¥è¯¢"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "新增模æ¿"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "æ出申请å•"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "没有新申请å•"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "新增使用者"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "新使用者åå­—"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "新视察员"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "更新窗å£è®¾å®š"
+
+#: NOT FOUND IN SOURCE
+msgid "New workflow"
+msgstr "新增æµç¨‹"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "下一项"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "下一页"
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "下一页"
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "昵称"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "昵称"
+
+#: NOT FOUND IN SOURCE
+msgid "Night Shift"
+msgstr "å°å¤œç­"
+
+#: NOT FOUND IN SOURCE
+msgid "No"
+msgstr "å¦"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "尚未定义类别"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "无自订字段"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "尚未定义自订字段"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "尚未定义群组"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "没有查询"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "尚未定义表å•"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "找ä¸åˆ° RT ä½¿ç”¨è€…ã€‚è¯·å‘ RT 管ç†å‘˜æŸ¥è¯¢ã€‚\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "没有模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "未指定申请å•ã€‚é€€å‡ºç”³è¯·å• "
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "未指定申请å•ã€‚退出申请å•æ›´æ”¹\\n\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "No Workflow"
+msgstr "没有æµç¨‹"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "æš‚ä¸å¤„ç†"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "未指定字段"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "找ä¸åˆ°å‘½ä»¤"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "没有对这å使用者的评论"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "没有附上申请å•å›žå¤"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "没有对 %1 çš„æè¿°"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "未指定群组"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "找ä¸åˆ°ç¬¦åˆæŸ¥è¯¢æ¡ä»¶çš„群组。"
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "没有附上讯æ¯"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "没有设定å£ä»¤"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "没有新增表å•çš„æƒé™"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "æ²¡æœ‰åœ¨è¡¨å• '%1' 新增申请å•çš„æƒé™"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "没有新增使用者的æƒé™"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "没有显示该申请å•çš„æƒé™"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "没有储存全域预存查询的æƒé™"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "没有检视申请å•æ›´æ–°çš„æƒé™"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "未指定å•ä½"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "未指定å•ä½ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "No protocol specified in %1"
+msgstr "%1 内未指定åè®®"
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "找ä¸åˆ°ç¬¦åˆæŸ¥è¯¢æ¡ä»¶çš„表å•ã€‚"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "找ä¸åˆ°æƒé™"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "没有选定æƒé™"
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "尚未加载查询"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "没有è¦è¿›è¡Œçš„查询"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "没有标题"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "未指定申请å•ç¼–å·"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "未指定更动报告类别"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "未指定使用者或电å­é‚®ä»¶åœ°å€"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "找ä¸åˆ°ç¬¦åˆæŸ¥è¯¢æ¡ä»¶çš„使用者。"
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "找ä¸åˆ°åˆæ ¼çš„ RT 使用者。RT cvs 处ç†å™¨å·²åœç”¨ã€‚è¯·å‘ RT 管ç†è€…询问。\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "_Set 没有收到任何值!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "没有人"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "字段ä¸å­˜åœ¨ï¼Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "Normal Users"
+msgstr "一般用户群组"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr "未设定"
+
+#: NOT FOUND IN SOURCE
+msgid "Not configured to fetch the content from a %1 in %2"
+msgstr "未设定æˆä»Ž %2 å†…æ’·å– %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "尚未登入"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "尚未登入"
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "尚未设定"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "尚未完工。"
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "尚未完工..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "备注"
+
+#: NOT FOUND IN SOURCE
+msgid "Notes:"
+msgstr "备注:"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "无法é€å‡ºé€šçŸ¥"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "通知管ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "以评论方å¼é€šçŸ¥ç®¡ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "通知副本收件人"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "以评论方å¼é€šçŸ¥å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "通知其它收件人"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "以评论方å¼é€šçŸ¥å…¶å®ƒæ”¶ä»¶äºº"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "通知承办人"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "以评论方å¼é€šçŸ¥æ‰¿åŠžäºº"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "通知承办人申请å•å·²é©³å›ž"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "通知承办人申请å•å·²å®Œæˆå…¨éƒ¨ç­¾æ ¸"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "通知承办人申请å•å·²å®ŒæˆæŸé¡¹ç­¾æ ¸"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "æ•´ç†å¾…签核事项,通知承办人åŠç®¡ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "通知申请人"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "通知申请人åŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "以评论方å¼é€šçŸ¥ç”³è¯·äººåŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "通知申请人ã€å‰¯æœ¬åŠç®¡ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "以评论方å¼é€šçŸ¥ç”³è¯·äººã€å‰¯æœ¬åŠç®¡ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Notify people:"
+msgstr "通知对象"
+
+#: NOT FOUND IN SOURCE
+msgid "Nov"
+msgstr "å一月"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "11"
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "å一月"
+
+#: NOT FOUND IN SOURCE
+msgid "OIN104"
+msgstr "104eHRMS 接å£"
+
+#: NOT FOUND IN SOURCE
+msgid "OK"
+msgstr "确定"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "OR"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "无法新增对象"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr "无法删除对象"
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "对象新增完毕"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr "对象删除完毕"
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "自订字段ä¸é€‚用于类别为 %1 的对象"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "对象类别ä¸ç¬¦"
+
+#: NOT FOUND IN SOURCE
+msgid "Occupation Status"
+msgstr "在èŒçŠ¶æ€"
+
+#: NOT FOUND IN SOURCE
+msgid "Oct"
+msgstr "å月"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "10"
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "å月"
+
+#: NOT FOUND IN SOURCE
+msgid "Office Phone"
+msgstr "办公室电è¯"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "离线"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "离线编辑"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "离线上载"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "等于"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr "在 %1 时,%2 写到:"
+
+#: NOT FOUND IN SOURCE
+msgid "On Change"
+msgstr "更改申请å•æ—¶"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "评论时"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "回å¤ç”³è¯·å•æ—¶"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "新增申请å•æ—¶"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "承办人改å˜æ—¶"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "优先顺ä½æ”¹å˜æ—¶"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "表å•æ”¹å˜æ—¶"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "解决申请å•æ—¶"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "现况改å˜æ—¶"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "å‘生更动时"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "仅显示 %1 之åŽæ–°å¢žçš„申请å•"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "仅显示 %1 之å‰æ–°å¢žçš„申请å•"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr "仅显示适用于下列项目的自订字段:"
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "å¼€å¯"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr "å¼€å¯ç”³è¯·å•"
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "å¼€å¯"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "å¼€å¯çš„申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "在新窗å£å¼€å¯(列表的)申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "在å¦ä¸€ä¸ªçª—å£å¼€å¯(列表的)申请å•"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "收到回å¤æ—¶å³å¼€å¯ç”³è¯·å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Opened Tickets"
+msgstr "已申请è¿è¡Œä¸­è¡¨å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Opinion"
+msgstr "æ„è§"
+
+#: NOT FOUND IN SOURCE
+msgid "Option Description"
+msgstr "选项æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Option Name"
+msgstr "选项å称"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr "选项"
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "排åºæ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "顺åºä¸ŽæŽ’åºæ–¹å¼"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "组织å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Organization:"
+msgstr "组织:"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "原申请å•ï¼š#%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Other comma-delimited email addresses"
+msgstr "其它e-mailå¸å· (ä»…e-mail通知;多笔å¸å·è¯·ç”¨é€—å·','区隔)"
+
+#: NOT FOUND IN SOURCE
+msgid "Out of range"
+msgstr "期é™å¤–"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "已纪录å‘é€çš„评论邮件"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "已纪录å‘é€çš„邮件"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "优先顺ä½éšæ—¶é—´å¢žåŠ è°ƒæ•´ä¸º"
+
+#: NOT FOUND IN SOURCE
+msgid "Override current custom fields with fields from %1"
+msgstr "以 %1 表å•çš„自订字段å–代现有字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Override global rights"
+msgstr "å–代全域æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "OverrideGlobalACL status %1"
+msgstr "å–代全域æƒé™ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Overview"
+msgstr "总览"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "承办申请å•"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "承办申请å•"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "承办人"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "承办人已从 %1 改为 %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "无法设定承办人。"
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "强制将承办人从 %1 改为 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "承办人"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner's Phone"
+msgstr "承办人电è¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Page #"
+msgstr " "
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "第 %1/%2 页"
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "呼å«å™¨"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "呼å«å™¨å·ç "
+
+#: NOT FOUND IN SOURCE
+msgid "Parameter"
+msgstr "呼å«å‚æ•°"
+
+#: NOT FOUND IN SOURCE
+msgid "Parent"
+msgstr "上级"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "æ¯ç”³è¯·å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Park Space"
+msgstr "åœè½¦ä½ç”³è¯·"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "å£ä»¤"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "å£ä»¤æ示"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr "å£ä»¤æ›´æ”¹å®Œæ¯•"
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "å£ä»¤é•¿åº¦è‡³å°‘必须为 %1 个字元"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "å£ä»¤å·²è®¾å®š"
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "å£ä»¤å¤ªçŸ­"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "å£ä»¤ï¼š%1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr "å£ä»¤ï¼šæƒé™ä¸è¶³"
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "å£ä»¤ç¡®è®¤å¤±è´¥ã€‚"
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "å£ä»¤ç¡®è®¤å¤±è´¥ã€‚您的å£ä»¤å¹¶æœªæ”¹å˜ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Pelase select a queue"
+msgstr "请选择表å•å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Pending Approval"
+msgstr "等待签核"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "人员"
+
+#: NOT FOUND IN SOURCE
+msgid "People with Queue Rights"
+msgstr "拥有表å•æƒé™äººå‘˜"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "执行使用者自订的动作"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl 设定"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "æƒé™ä¸è¶³"
+
+#: NOT FOUND IN SOURCE
+msgid "Permission Settings"
+msgstr "æƒé™è®¾å®š"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr "æƒé™ä¸è¶³"
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr "æƒé™ä¸è¶³"
+
+#: NOT FOUND IN SOURCE
+msgid "Permitted Queues:"
+msgstr "拥有æƒé™è¡¨å•åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Personal"
+msgstr "代ç†äººç¾¤ç»„"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "代ç†äººç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "Personal Homepage"
+msgstr "个人首页"
+
+#: NOT FOUND IN SOURCE
+msgid "Personal Todo"
+msgstr "ç§äººå¾…办事项"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "代ç†äººç¾¤ç»„"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "代ç†äººç¾¤ç»„:"
+
+#: NOT FOUND IN SOURCE
+msgid "PersonalHomepage"
+msgstr "个人首页"
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 1: Create/Rename Groups (%1)"
+msgstr "第一阶段:群组建立åŠæ”¹å (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 2: Disable/Enable Groups (%1)"
+msgstr "第二阶段:群组åœç”¨åŠå¯ç”¨ (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 3: Create/Rename Users (%1)"
+msgstr "第三阶段:使用者建立åŠæ”¹å (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 4: Disable/Enable Users (%1)"
+msgstr "第四阶段:使用者åœç”¨åŠå¯ç”¨ (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phone"
+msgstr "电è¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Phone number"
+msgstr "电è¯å·ç "
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "电è¯å·ç "
+
+#: NOT FOUND IN SOURCE
+msgid "Pick"
+msgstr "挑选"
+
+#: NOT FOUND IN SOURCE
+msgid "Place of Departure"
+msgstr "出å‘地点"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "尚未完工"
+
+#: NOT FOUND IN SOURCE
+msgid "Please Select"
+msgstr "请选择"
+
+#: NOT FOUND IN SOURCE
+msgid "Please check items to be deleted first."
+msgstr "请先选中è¦åˆ é™¤çš„对象"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select a group"
+msgstr "请选择群组"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select a queue's workflow"
+msgstr "请选择表å•æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select one of the category types above."
+msgstr "请从上é¢é€‰æ‹©ä¸€é¡¹åˆ†ç±»ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select role"
+msgstr "请选择角色"
+
+#: NOT FOUND IN SOURCE
+msgid "Policy"
+msgstr "ç»è¥è§„ç« "
+
+#: NOT FOUND IN SOURCE
+msgid "Position"
+msgstr "èŒåŠ¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Position Level"
+msgstr "èŒç­‰"
+
+#: NOT FOUND IN SOURCE
+msgid "Position Name"
+msgstr "èŒåŠ¡å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Position Number"
+msgstr "èŒåŠ¡ä»£ç "
+
+#: NOT FOUND IN SOURCE
+msgid "Position Rank"
+msgstr "èŒçº§"
+
+#: NOT FOUND IN SOURCE
+msgid "Pref"
+msgstr "å好"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "å好"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr "使用者 %2 çš„ %1 å好。"
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr "æˆåŠŸå‚¨å­˜ %1 çš„å好。"
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "个人信æ¯"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "预备动作完毕"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "上一项"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "上一页"
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "å‰ä¸€é¡µ"
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "优先顺ä½"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "找ä¸åˆ°å•ä½ %1。"
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "优先顺ä½"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "优先顺ä½èµ·å§‹å€¼"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "éšç§è®¾å®šï¼š"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "内部æˆå‘˜"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "内部æˆå‘˜çŠ¶æ€ï¼š%1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "内部æˆå‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Process Status"
+msgstr "处ç†çŠ¶æ€"
+
+#: NOT FOUND IN SOURCE
+msgid "Project"
+msgstr "项目"
+
+#: NOT FOUND IN SOURCE
+msgid "Project Name"
+msgstr "项目å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Projects"
+msgstr "项目"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "内部用的虚拟群组"
+
+#: NOT FOUND IN SOURCE
+msgid "Public Description"
+msgstr "公开说明"
+
+#: NOT FOUND IN SOURCE
+msgid "Public Info"
+msgstr "公开信æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Public Service"
+msgstr "公共事务区"
+
+#: NOT FOUND IN SOURCE
+msgid "Purging stale data: %1"
+msgstr "移除过期数æ®: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "查询"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "建立查询"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr "查询:"
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "表å•"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "找ä¸åˆ°è¡¨å• %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "找ä¸åˆ°è¡¨å• '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Keyword Selections"
+msgstr "表å•å…³é”®è¯é€‰å–"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "表å•å称"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Owner"
+msgstr "业务承办人"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Priority"
+msgstr "优先等级"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Rights"
+msgstr "表å•æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "表å•æ‰‹ç»­"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Setup"
+msgstr "表å•è®¾å®š"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "表å•å·²å­˜åœ¨"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "无法新增表å•"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "无法加载表å•"
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "表å•æ–°å¢žå®Œæ¯•"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "未指定表å•ã€‚"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "找ä¸åˆ°è¡¨å•"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "表å•"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr "由我管ç†çš„表å•"
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr "管ç†å‘˜å‰¯æœ¬æœ‰æˆ‘的表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Quick Search"
+msgstr "表å•çŽ°å†µ"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "表å•ä¸€è§ˆ"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "快速建立申请å•"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "%2:RT %1 版"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 版,<a href=\"http://bestpractical.com\">Best Practical Solutions å…¬å¸</a>出å“。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1。版æƒæ‰€æœ‰ 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1。版æƒæ‰€æœ‰ 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT 管ç†é¡µé¢"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "RT 认è¯é”™è¯¯ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT 退信:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "RT 设定错误"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "RT 致命错误。讯æ¯æœªè¢«çºªå½•ã€‚"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT 错误"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT 收到从自己寄出的邮件 (%1)。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Recieved mail (%1) from itself."
+msgstr "RT 收到从自己寄出的邮件 (%1)。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service"
+msgstr "RT 自助æœåŠ¡"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RT çš„å˜æ•°"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT 一览"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr "使用者 %1 的 RT 一览"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT å¯äºŽæ˜¾ç¤ºæ­¤è‡ªè®¢å­—段时引入其它网站的内容"
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT å¯å°†æ­¤è‡ªè®¢å­—段的值视为连往其它网站的超链接"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT 无法认è¯æ‚¨çš„身份"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT 无法从外部数æ®åº“查询找到申请人信æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT 找ä¸åˆ°è¡¨å•ï¼š%1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr "RT 无法储存您的登入阶段。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT 无法确认这个 PGP 签章。\\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "%1 专用æµç¨‹ç³»ç»Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "%1 专用 RT 系统:%2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT 已执行您的命令"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT 版æƒæ‰€æœ‰ 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;。<br>æœ¬è½¯ä½“ä¾ <a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU 通用公共授æƒç¬¬äºŒç‰ˆ</a> 散布。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT 认为这å¯èƒ½æ˜¯é€€ä¿¡"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT 会在申请å•ä¸»æ—¨å†…æœå¯»å°†æ‚¨é”®å…¥çš„任何其它字样"
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT 以未签章方å¼å¤„ç†è¿™å°é‚®ä»¶ã€‚\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT 会将 <tt>__id__</tt> åŠ <tt>__CustomField__</tt> ç½®æ¢æˆçºªå½•ç¼–å·åŠè‡ªè®¢å­—段"
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RT 的电å­é‚®ä»¶å‘½ä»¤æ¨¡å¼é¡»è¦ PGP 认è¯ã€‚您å¯èƒ½æ²¡æœ‰ç­¾ç« ï¼Œæˆ–是您的签章无法辨识。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT::Queue-Role"
+msgstr "表å•è¿è¡Œè§’色"
+
+#: NOT FOUND IN SOURCE
+msgid "RT::System-Role"
+msgstr "系统è¿è¡Œè§’色"
+
+#: NOT FOUND IN SOURCE
+msgid "RT::Ticket-Role"
+msgstr "申请å•è¿è¡Œè§’色"
+
+#: NOT FOUND IN SOURCE
+msgid "RT_System"
+msgstr "系统讯æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Read Only"
+msgstr "åªè¯»"
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "真实姓å"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "真实姓å"
+
+#: NOT FOUND IN SOURCE
+msgid "Really reject this ticket?"
+msgstr "您确定è¦é©³å›žè¿™å¼ ç”³è¯·å•å—?"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "已加入 %1 为å‚考本申请å•"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "已移除 %1 为å‚考本申请å•"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "已加入å‚è€ƒç”³è¯·å• %1"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "已移除å‚è€ƒç”³è¯·å• %1"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "被å‚考"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "å‚考"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "å‚考"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "在结果范围内查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "调整查询æ¡ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Refresh"
+msgstr "æ›´æ–°"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "æ¯ %1 分钟更新页é¢"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "已建立æ醒项目‘%1’"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "已完æˆæ醒项目‘%1’"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "å·²é‡æ–°å¼€å¯æ醒项目‘%1’"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "æ醒项目 #%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr "æ醒项目"
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "ç”³è¯·å• #%1 çš„æ醒项目"
+
+#: NOT FOUND IN SOURCE
+msgid "Remove"
+msgstr "移除"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "移除管ç†å‘˜å‰¯æœ¬"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "移除副本"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "移除申请人"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "回å¤"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "回å¤åœ°å€"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "回å¤ç”³è¯·äºº"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "对申请å•è¿›è¡Œå›žå¤"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "回å¤ç”³è¯·å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Report to Duty"
+msgstr "上下ç­åˆ·å¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Reported on"
+msgstr "到èŒæ—¥æœŸ"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr "报表"
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "申请人"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "申请人电å­é‚®ä»¶ä¿¡ç®±åœ°å€"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor's"
+msgstr "申请人所属之第上"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor's Dept."
+msgstr "申请人所属部门之"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor's Phone"
+msgstr "申请人电è¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "申请人"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "申请人地å€"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "申请人"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "申请å•å¤„ç†æœŸé™"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "未指定必è¦çš„å‚数‘%1’"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "é‡è®¾"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr "é‡è®¾ä¸ºé¢„设值"
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "ä½å¤„"
+
+#: NOT FOUND IN SOURCE
+msgid "Resolution"
+msgstr "解决状æ€"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "解决"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "è§£å†³ç”³è¯·å• #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "已解决"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr "已由承办人解决"
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr "已在指定日期内解决"
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr "已在指定日期内内解决,ä¾æ‰¿åŠžäººåˆ†ç»„"
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr "已解决的申请å•ï¼Œä¾æ‰¿åŠžäººåˆ†ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "回å¤ç”³è¯·äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Responsibility Type"
+msgstr "责任区分"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "结果"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "æ¯é¡µåˆ—出几笔结果"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "å†æ¬¡è¾“å…¥å£ä»¤"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "å¤åŽŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "在 %4 (%5) 的范围内找ä¸åˆ° %2 %3 çš„ %1 æƒé™\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "æƒé™ä»£ç†å®Œæ¯•"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "æƒé™è®¾å®šå®Œæ¯•"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "æƒé™åŠ è½½å®Œæ¯•"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "无法撤消æƒé™"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "找ä¸åˆ°æƒé™"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "æƒé™å¹¶æœªåŠ è½½ã€‚"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "æƒé™æ’¤æ¶ˆå®Œæ¯•"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "æƒé™åŠä»£ç†äºº"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "无法将æƒé™èµ‹äºˆ %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "无法撤消 %1 çš„æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Role Members"
+msgstr "角色æˆå‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Role Name"
+msgstr "角色å称"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "角色"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "交由系统管ç†å‘˜ç­¾æ ¸"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr "æ¯æ ¼ç¬”æ•°"
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "æ¯é¡µç¬”æ•°"
+
+#: NOT FOUND IN SOURCE
+msgid "Run Approval"
+msgstr "签核执行"
+
+#: NOT FOUND IN SOURCE
+msgid "SMTPDebug"
+msgstr "SMTP 侦错纪录"
+
+#: NOT FOUND IN SOURCE
+msgid "SMTPFrom"
+msgstr "SMTP 寄件地å€"
+
+#: NOT FOUND IN SOURCE
+msgid "SMTPServer"
+msgstr "SMTP æœåŠ¡å™¨"
+
+#: NOT FOUND IN SOURCE
+msgid "Sat"
+msgstr "星期六"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "星期六"
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "储存"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "储存更改"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "储存å好"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "储存更改"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr "æˆåŠŸå‚¨å­˜æŸ¥è¯¢ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "预存查询"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "手续 #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrip Action"
+msgstr "讯æ¯é€šçŸ¥åŠ¨ä½œ"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrip Condition"
+msgstr "讯æ¯é€šçŸ¥æ¡ä»¶"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "手续新增完毕"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "手续字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrip Name"
+msgstr "讯æ¯å称"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "手续删除完毕"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "手续"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips "
+msgstr "讯æ¯é€šçŸ¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "%1 的手续\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "适用于所有表å•çš„手续"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "查询æ¡ä»¶"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr "æœå¯»å好"
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "æœå¯»å±žæ€§åŠ è½½å¤±è´¥"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "签核å•æŸ¥è¯¢"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr "申请å•æŸ¥è¯¢"
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "æœå¯»ç”³è¯·å•ã€‚请键入<strong>ç¼–å·</strong>ã€<strong>表å•å称</strong>ã€æ‰¿åŠžäººçš„<strong>使用者å称</strong>ã€æˆ–申请人的<strong>电å­é‚®ä»¶åœ°å€</strong>。以上格å¼ä¹‹å¤–的文字,则会在申请å•å†…æ–‡åŠé™„件内检索。"
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr "æœå¯»é€‰é¡¹"
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "æœå¯»ç»“æžœï¼Œä¾ %1 分组"
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "更新查询:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Searches can't be associated with that kind of object"
+msgstr "ä¸èƒ½å¯¹æ­¤ç±»å¯¹è±¡è¿›è¡ŒæŸ¥è¯¢"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "对所有申请å•çš„全文进行检索,å¯èƒ½ä¼šéœ€è¦å¾ˆä¹…的时间。但如果您真的有需è¦ï¼Œå¯é”®å…¥ <b>fulltext:<i>文字</i></b> æ¥æœå¯»ç”³è¯·å•çš„所有纪录。"
+
+#: NOT FOUND IN SOURCE
+msgid "Second-"
+msgstr "二"
+
+#: NOT FOUND IN SOURCE
+msgid "Second-level Users"
+msgstr "二阶主管员工"
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "安全性:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr "å‚è§ï¼š"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "查阅自订字段"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "查阅é€å‡ºçš„电å­é‚®ä»¶åŠæ”¶ä»¶äºº"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "查阅申请å•å†…çš„ç§äººè¯„论"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "查阅申请å•æ€»è§ˆ"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "查阅自订字段"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr "查阅群组"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "查阅表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "选择"
+
+#: NOT FOUND IN SOURCE
+msgid "Select All"
+msgstr "全选"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "选择自订字段"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "选择群组"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "选择表å•"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "为您新的申请å•é€‰æ‹©ä¸€ä¸ªè¡¨å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Select a queue to link to"
+msgstr "请选择欲连结表å•"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "选择使用者"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "选择自订字段"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "选择适用于所有使用者群组的自订字段"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "选择适用于所有使用者的自订字段"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "选择适用于所有表å•å†…申请å•çš„自订字段"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "选择适用于所有表å•å†…申请å•ä¹‹æ›´åŠ¨çš„自订字段"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "选择群组"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "选择多é‡é¡¹ç›®"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "选择å•ä¸€é¡¹ç›®"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "选择表å•"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr "选择è¦åœ¨â€˜RT 一览’页é¢æ˜¾ç¤ºçš„表å•"
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "选择手续"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "选择模æ¿"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "选择最多 %1 个值"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "选择使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "Select workflow"
+msgstr "选择æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectExternal"
+msgstr "系统选项"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "多é‡é€‰é¡¹"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "å•ä¸€é€‰é¡¹"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "已选å–的自订字段"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "已选å–的对象"
+
+#: NOT FOUND IN SOURCE
+msgid "Selected users:"
+msgstr "已选å–的使用者:"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "选å–的项目已更改。请储存您的更动"
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "自助æœåŠ¡"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "寄信给所有视察员"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "以评论方å¼å¯„信给所有视察员"
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "寄信给申请人åŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "以评论方å¼å¯„信给申请人åŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "寄信给申请人"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "寄信给特定的副本åŠå¯†ä»¶å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "寄信给副本收件人"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "以评论方å¼å¯„信给副本收件人"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "寄信给管ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "以评论寄信给管ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "寄信给申请人"
+
+#: NOT FOUND IN SOURCE
+msgid "Sep"
+msgstr "ä¹æœˆ"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "09"
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "ä¹æœˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Setting %1's 'Disabled' property to %2"
+msgstr "%1 的‘åœç”¨â€™å±žæ€§å·²è®¾ä¸º %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Shift Type"
+msgstr "ç­åˆ«å±žæ€§"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "显示"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "显示待签核申请å•"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "显示字段"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "显示结果"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "显示已批准的签核å•"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "显示基本信æ¯"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "显示已驳回的签核å•"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "显示细节"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "显示待处ç†çš„签核å•"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "显示尚待他人批准的签核å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "显示申请å•å†…çš„ç§äººè¯„论"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "显示申请å•æ‘˜è¦"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "显示æƒé™æ¸…å•"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr "显示设定页签"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "显示寄é€é‚®ä»¶"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "显示预存查询"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "显示手续"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "显示模æ¿"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "显示申请å•"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "显示申请å•çš„评论"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "登记æˆä¸ºç”³è¯·äººæˆ–副本收件人"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "登记æˆä¸ºç®¡ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "ç­¾åæ¡£"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "使用者:%1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr "简易查询"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "å•ä¸€"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr "大å°"
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "略过选å•"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr "å°"
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "æŸäº›æµè§ˆå™¨åªå…许加载和 RT æœåŠ¡å™¨åŒä¸€ä¸ªç½‘域的内容。"
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "顺åº"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "排åºæ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "结果排åºæ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "排åºé¡ºåº"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "å…³å¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Stage Action"
+msgstr "å…³å¡è¿è¡ŒåŠ¨ä½œ"
+
+#: NOT FOUND IN SOURCE
+msgid "Stage Condition"
+msgstr "å…³å¡è¿è¡Œæ¡ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "延宕"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "首页"
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "实际起始日"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "无法解读起始日期 '%1"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "应起始日"
+
+msgid "StartsRelative"
+msgstr "应起始日(相对值)"
+
+msgid "StartedRelative"
+msgstr "实际起始日(相对值)"
+
+msgid "CreatedRelative"
+msgstr "实际新增日(相对值)"
+
+msgid "LastUpdatedRelative"
+msgstr "上次更新(相对值)"
+
+msgid "ToldRelative"
+msgstr "告知日(相对值)"
+
+msgid "DueRelative"
+msgstr "到期日(相对值)"
+
+msgid "ResolvedRelative"
+msgstr "解决日(相对值)"
+
+msgid "ReferredToBy"
+msgstr "被å‚考"
+
+msgid "DependedOnBy"
+msgstr "å¯æŽ¥ç»­å¤„ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "应起始日"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "无法解读起始日期 '%1"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "å·ž"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "现况"
+
+msgid "ExtendedStatus"
+msgstr "é¢å¤–现况"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "现况改å˜æ—¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "现况从 %1 改为 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "现况改å˜æ—¶"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "强制更æ¢æ‰¿åŠžäºº"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "强制承办申请å•"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "强制承办申请å•"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "承办人从 %1 强制更æ¢"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "承办人从 %1 å¼ºåˆ¶æ›´æ¢ "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr "æ ·å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Subgroup"
+msgstr "å­ç¾¤ç»„"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "主题"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "标题已改为 %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "é€å‡ºæµç¨‹"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "设定æˆåŠŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Sun"
+msgstr "星期日"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "星期日"
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "系统管ç†å‘˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Sync now"
+msgstr "执行åŒæ­¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Sync104HRMS"
+msgstr "自动åŒæ­¥104HRMS"
+
+#: NOT FOUND IN SOURCE
+msgid "Synchronizing HRMS data. This may take a while..."
+msgstr "正在åŒæ­¥åŒ– HRMS 人事系统数æ®ã€‚请ç¨å¾…..."
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "系统"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "系统设定"
+
+#: NOT FOUND IN SOURCE
+msgid "System Defined"
+msgstr "系统定义"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "系统错误"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. Right not granted."
+msgstr "系统错误。设定æƒé™å¤±è´¥ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. right not granted"
+msgstr "系统错误。设定æƒé™å¤±è´¥ã€‚"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr "系统错误:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "System Rights"
+msgstr "系统æƒé™"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "系统工具"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "系统错误。æƒé™ä»£ç†å¤±è´¥ã€‚"
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "系统错误。设定æƒé™å¤±è´¥ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "系统错误。无法设定æƒé™ã€‚"
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "系统群组"
+
+#: NOT FOUND IN SOURCE
+msgid "SystemInternal"
+msgstr "系统内部用"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "内部使用的系统角色群组"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: NOT FOUND IN SOURCE
+msgid "TabbedUI"
+msgstr "页签接å£"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "å—ç†"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "自行承办申请å•"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "自行承办申请å•"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "å·²å—ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Task"
+msgstr "工作事项"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "模æ¿"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "æ¨¡æ¿ #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Template Content"
+msgstr "通知模æ¿å†…容"
+
+#: NOT FOUND IN SOURCE
+msgid "Template Description"
+msgstr "通知模æ¿æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Template Name"
+msgstr "通知模æ¿å称"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "模æ¿å·²åˆ é™¤"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr "模æ¿æ˜¯å¿…填字段"
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "找ä¸åˆ°æ¨¡æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "找ä¸åˆ°æ¨¡æ¿\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "模æ¿å‰–æžå®Œæ¯•"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr "模æ¿å‰–æžé”™è¯¯"
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates "
+msgstr "通知模æ¿"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "找ä¸åˆ° %1 的模æ¿\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "文字"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "å·²ç»æ˜¯ç›®å‰å­—段的值"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "è¿™ä¸æ˜¯è¯¥è‡ªè®¢å­—段的值"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "åŒæ ·çš„值"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "这项å•ä½å·²ç»æ‹¥æœ‰è¯¥æƒé™"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "这项å•ä½å·²ç»æ˜¯è¿™ä¸ªè¡¨å•çš„ %1"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "这项å•ä½å·²ç»æ˜¯è¿™ä»½ç”³è¯·å•çš„ %1"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "这项å•ä½ä¸æ˜¯è¿™ä¸ªè¡¨å•çš„ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "这项å•ä½ä¸æ˜¯è¿™ä»½ç”³è¯·å•çš„ %1"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "此表å•ä¸å­˜åœ¨"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "这份申请å•æœ‰å°šæœªè§£å†³çš„附属申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "使用者已具有该项æƒé™"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "该使用者已ç»æ‰¿åŠžè¿™ä»½ç”³è¯·å•"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "使用者ä¸å­˜åœ¨"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "è¿™å使用者已ç»æ˜¯å†…部æˆå‘˜"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "è¿™å使用者属于éžå†…部æˆå‘˜ç¾¤ç»„"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "使用者加入内部æˆå‘˜ç¾¤ç»„完毕"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "è¿™å使用者已加入éžå†…部æˆå‘˜ç¾¤ç»„"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "è¿™å使用者已加入éžå†…部æˆå‘˜ç¾¤ç»„"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "使用者å¯èƒ½æ²¡æœ‰æ‰¿åŠžè¡¨å•é‡Œçš„申请å•"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "è¿™ä¸æ˜¯ä¸€ä¸ªæ•°å­—ç¼–å·"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "基本信æ¯"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "申请å•çš„副本收件人"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "申请å•çš„管ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "评论已被纪录"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "下列命令会找到 'general' 表å•å†…所有è¿ä½œä¸­çš„申请å•ï¼Œå¹¶å°†å…¶ä¸­ 4 å°æ—¶å†…未处ç†çš„申请å•ä¼˜å…ˆç¨‹åº¦è®¾ä¸º 99:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "以下命令未被执行:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "新的字段值设定完æˆã€‚"
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "申请å•çš„承办人"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "申请å•çš„申请人"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "该使用者ä¸ä¼šçœ‹è§è¿™äº›è¯„论"
+
+#: NOT FOUND IN SOURCE
+msgid "Third-"
+msgstr "三"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "此自订字段ä¸é€‚用于该对象"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "此项功能仅é™ç³»ç»Ÿç®¡ç†å‘˜ä½¿ç”¨"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "此讯æ¯ä¼šå¯„ç»™..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "ç”³è¯·å• %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "此工具程åºä¼šè®©ä½¿ç”¨è€…ç»ç”± RT 执行任æ„命令。"
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "此项更动报告没有内容"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "使用者é€å‡ºçš„å‰ %1 份优先处ç†ç”³è¯·å•"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "使用者é€å‡ºçš„å‰ 25 份优先处ç†ç”³è¯·å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Thu"
+msgstr "星期四"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "星期四"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket"
+msgstr "申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "ç”³è¯·å• # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "æ›´æ–°ç”³è¯·å• # %1 的全部信æ¯ï¼š%2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "æ›´æ–°ç”³è¯·å• #%1 的全部信æ¯ï¼š%2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "ç”³è¯·å• #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "ç”³è¯·å• %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "ç”³è¯·å• #%1 æˆåŠŸæ–°å¢žäºŽ '%2' 表å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "åŠ è½½ç”³è¯·å• %1\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "ç”³è¯·å• %1:%2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "申请å•çš„自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Due"
+msgstr "表å•å¤„ç†æœŸé™"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "申请å•å¤„ç†çºªå½• # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket ID"
+msgstr "å•å·"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "申请å•ç¼–å·"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Processing Due"
+msgstr "表å•è¿è¡ŒæœŸé™"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "申请å•å·²è§£å†³"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "申请å•çš„更动"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Type"
+msgstr "表å•ç§ç±»"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "申请å•é™„件"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "申请å•å†…容"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "申请å•å†…容类别"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "内部错误,无法新增申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "申请å•æ–°å¢žå®Œæ¯•"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "申请å•æ–°å¢žå¤±è´¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "申请å•åˆ é™¤å®Œæ¯•"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "找ä¸åˆ°ç”³è¯·å•ç¼–å·"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "申请å•åˆ é™¤å®Œæ¯•"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "申请å•çš„æè¿°ä¿¡æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "找ä¸åˆ°ç”³è¯·å•"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "申请å•çŽ°å†µå·²æ”¹å˜"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "申请å•è§†å¯Ÿå‘˜"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr "TicketSQL 查询模å—"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "ç”³è¯·å• %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "ç”³è¯·å• %1 (%2)"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets I own"
+msgstr "待处ç†çš„申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets I requested"
+msgstr "é€å‡ºçš„申请å•"
+
+msgid "CreatedBy"
+msgstr "建立人"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr "申请å•å»ºç«‹èµ·å§‹æ—¥"
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr "申请å•å»ºç«‹æˆªæ­¢æ—¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "%1 的申请å•"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr "申请å•è§£å†³èµ·å§‹æ—¥"
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr "申请å•è§£å†³æˆªæ­¢æ—¥"
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "批准之åŽï¼Œå¯æŽ¥ç»­å¤„ç†ï¼š"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "预计时间"
+
+msgid "TimeEstimated"
+msgstr "预计时间"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "剩馀时间"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "处ç†æ—¶é—´"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "剩馀时间"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "显示时间"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "已处ç†æ—¶é—´"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "剩馀时间"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "已处ç†æ—¶é—´"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr "标题"
+
+msgid "QueueName"
+msgstr "表å•å称"
+
+msgid "OwnerName"
+msgstr "承办人å称"
+
+msgid "<blank>"
+msgstr "<留空>"
+
+msgid "NEWLINE"
+msgstr "(æ¢åˆ—)"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "产生这次更动的差异档:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "产生这次更动的差异档:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "如果有支æŒã€æ•™è‚²è®­ç»ƒåŠå®šåˆ¶å¼€å‘的需è¦ï¼Œè¯·è¿žç»œ %1。"
+
+#: NOT FOUND IN SOURCE
+msgid "Todo"
+msgstr "待办事项"
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "告知日"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "工具"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "页"
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "更动"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "清除更动报告 %1"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "更动报告已新增"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "更动的自订字段"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "未指定申请å•ç¼–å·ï¼Œæ— æ³•æ–°å¢žæ›´åŠ¨"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "未指定对象类别åŠç¼–å·ï¼Œæ— æ³•æ–°å¢žæ›´åŠ¨"
+
+#: NOT FOUND IN SOURCE
+msgid "TransactionBatch"
+msgstr "批次更动时"
+
+#: NOT FOUND IN SOURCE
+msgid "TransactionCreate"
+msgstr "新增更动时"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "ä¸å¯æ›´æ”¹æ›´åŠ¨æŠ¥å‘Š"
+
+#: NOT FOUND IN SOURCE
+msgid "Transfer to"
+msgstr "移交给"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "试图删除æŸé¡¹æƒé™ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Tue"
+msgstr "星期二"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "星期二"
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "类别"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "尚无实作"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "外部系统登入å¸å·"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "外部系统登入å¸å·"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "ä¸å¯è§£çš„内容文字编ç æ–¹å¼ %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: %1"
+msgstr "未知的字段:%1"
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "全数显示"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "未命å的查询"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "éžå†…部æˆå‘˜"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "未选å–的自订字段"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "未选å–的对象"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "未被å—ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Untitled search"
+msgstr "未命å的查询"
+
+#: NOT FOUND IN SOURCE
+msgid "Up"
+msgstr "上一页"
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "处ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "全部更新"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "æ›´æ–°ç¼–å·"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "更新申请å•"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "更新类别"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "整批更新申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "更新电å­é‚®ä»¶ä¿¡ç®±"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "批次更新申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "æ›´æ–°å¸å·"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "更新未被记录"
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "更新选择的申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "更新签章"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "更新申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "æ›´æ–°ç”³è¯·å• # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "æ›´æ–°ç”³è¯·å• #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "æ›´æ–°ç”³è¯·å• #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "更新的内容并éžç”³è¯·å•å›žå¤ä¹Ÿä¸æ˜¯è¯„论"
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "å‰æ¬¡æ›´æ–°"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "上载"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "上载多个档案"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "上载多份图片"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "上载一个档案"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "上载一份图片"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "上载最多 %1 个档案"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "上载最多 %1 份图片"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "上载您的更动"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr "使用其它的 RT 管ç†å·¥å…·"
+
+#: NOT FOUND IN SOURCE
+msgid "User"
+msgstr "使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "使用者 %1 %2:%3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "使用者 %1 å£ä»¤ï¼š%2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… '%1'。"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… '%1'\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "使用者自订"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "使用者自订的æ¡ä»¶åŠåŠ¨ä½œ"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "使用者 ID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "使用者 ID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Number"
+msgstr "员工编å·"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "使用者æƒé™"
+
+#: NOT FOUND IN SOURCE
+msgid "User Setup"
+msgstr "使用者设定"
+
+#: NOT FOUND IN SOURCE
+msgid "User Shift"
+msgstr "员工ç­åˆ«"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "使用者试图在 %2 对象 #%3 的自订字段 %1 上执行未知的更新æ“作"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "无法新增使用者:%1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "使用者新增完毕"
+
+#: NOT FOUND IN SOURCE
+msgid "User created: %1"
+msgstr "使用者 %1 新增完毕"
+
+#: NOT FOUND IN SOURCE
+msgid "User created: %1 (%2)"
+msgstr "使用者 %1 (%2) 新增完毕"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "使用者定义的群组"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "已加载使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "已通知使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "User renamed from %1 to %2"
+msgstr "使用者 %1 已改å为 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "使用者ç§äººæ•°æ®"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "使用者自定群组"
+
+#: NOT FOUND IN SOURCE
+msgid "UserDefined"
+msgstr "使用者自定"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "å¸å·"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "使用者"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "符åˆæŸ¥è¯¢æ¡ä»¶çš„使用者"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr "使用更动 #%1..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "åˆç†çš„查询"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "验è¯"
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "选择表å•"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "字段值"
+
+#: NOT FOUND IN SOURCE
+msgid "View log"
+msgstr "检视纪录档"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "视察"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "以管ç†å‘˜å‰¯æœ¬æ”¶ä»¶äººèº«ä»½è§†å¯Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "æˆåŠŸåŠ è½½è§†å¯Ÿå‘˜ä¿¡æ¯"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "视察员"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "网页文字编ç æ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Wed"
+msgstr "星期三"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "星期三"
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr "今日工作一览"
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "当申请å•é€šè¿‡æ‰€æœ‰ç­¾æ ¸åŽï¼Œå°†æ­¤è®¯æ¯å›žå¤åˆ°åŽŸç”³è¯·å•"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "当申请å•é€šè¿‡æŸé¡¹ç­¾æ ¸åŽï¼Œå°†æ­¤è®¯æ¯å›žå¤åˆ°åŽŸç”³è¯·å•"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "新增申请å•æ—¶"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "签核å•æ–°å¢žä¹‹åŽï¼Œé€šçŸ¥åº”å—ç†çš„承办人åŠç®¡ç†å‘˜å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "当任何事情å‘生时"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "当申请å•è§£å†³æ—¶"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "当申请å•æ›´æ¢æ‰¿åŠžäººæ—¶"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "当申请å•çš„优先顺åºæ”¹å˜æ—¶"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "当申请å•æ›´æ¢è¡¨å•æ—¶"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "当申请å•æ›´æ–°çŽ°å†µæ—¶"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "当使用者自订的情况å‘生时"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "当评论é€è¾¾æ—¶"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "当回å¤é€è¾¾æ—¶"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "å…¬å¸"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "离线工作"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "å…¬å¸ç”µè¯"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "处ç†æ—¶é—´"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow #%1"
+msgstr "æµç¨‹ #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow Begin"
+msgstr "æµç¨‹å¼€å§‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow End"
+msgstr "æµç¨‹ç»“æŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow deleted"
+msgstr "æµç¨‹å·²åˆ é™¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflows"
+msgstr "æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Writable"
+msgstr "å¯è¯»å†™"
+
+#: NOT FOUND IN SOURCE
+msgid "XXX CHANGEME You are not an authorized user"
+msgstr "XXX CHANGEME 您是未ç»æŽˆæƒçš„使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "Yes"
+msgstr "是"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "您已是这份申请å•çš„承办人"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "您ä¸æ˜¯è¢«æŽˆæƒçš„使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "You can access it with the Download button on the right."
+msgstr "您å¯ä»¥æŒ‰å³æ–¹çš„‘下载’键æ¥å–得。"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr "您也å¯ä»¥ç›´æŽ¥ç¼–辑预先定义的æœå¯»æ–¹å¼"
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "祇能é‡æ–°æŒ‡æ´¾æ‚¨æ‰€æ‰¿åŠžæˆ–是没有承办人的申请å•"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr "您祇能å—ç†å°šæ— æ‰¿åŠžäººçš„申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "您没有看那份申请å•çš„æƒé™ã€‚\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "æ‚¨ä¼šåœ¨è¡¨å• %2 找到 %1 的申请å•"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "您已注销 RT。"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "您没有在该表å•æ–°å¢žç”³è¯·å•çš„æƒé™ã€‚"
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "您ä¸èƒ½åœ¨è¯¥è¡¨å•ä¸­æ出申请。"
+
+#: NOT FOUND IN SOURCE
+msgid "You need to restart the Request Tracker service for saved changes to take effect."
+msgstr "您必须é‡æ–°æ¿€æ´» Request Tracker æœåŠ¡ï¼Œå‚¨å­˜çš„更动æ‰ä¼šç”Ÿæ•ˆã€‚"
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "欢迎下次å†æ¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "您æ出的 %1 申请å•"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "RT 管ç†å‘˜å¯èƒ½è®¾é”™äº†ç”± RT 寄出的邮件收件人标头档"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "申请å•å·²ç”± %1 批准。å¯èƒ½è¿˜æœ‰å…¶å®ƒå¾…签核的步骤。"
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "您的申请å•å·²å®Œæˆç­¾æ ¸ç¨‹åºã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "您的申请å•å·²è¢«é©³å›ž"
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected by %1."
+msgstr "您的申请å•å·²è¢« %1 驳回。"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "您的申请å•å·²è¢«é©³å›žã€‚"
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "您的å¸å·æˆ–å£ä»¤æœ‰è¯¯"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "邮政编ç "
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[没有标题]"
+
+msgid "[none]"
+msgstr "[æ— ]"
+
+#: NOT FOUND IN SOURCE
+msgid "ago"
+msgstr "过期"
+
+#: NOT FOUND IN SOURCE
+msgid "alert"
+msgstr "急讯"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "å…许建立预存查询"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "å…许加载预存查询"
+
+#: NOT FOUND IN SOURCE
+msgid "approving"
+msgstr "待签核"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "æƒé™åŒ %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr "图表"
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "已解决"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "包å«"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "内容"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "类型"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "申请å•å›žå¤(å¯èƒ½)未é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "申请å•å›žå¤å·²é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "critical"
+msgstr "严é‡"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "天"
+
+#: NOT FOUND IN SOURCE
+msgid "dead"
+msgstr "æ‹’ç»å¤„ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "debug"
+msgstr "侦错"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "删除"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "已删除"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "ä¸ç¬¦åˆ"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "ä¸åŒ…å«"
+
+#: NOT FOUND IN SOURCE
+msgid "email address"
+msgstr "电å­é‚®ä»¶ä¿¡ç®±"
+
+#: NOT FOUND IN SOURCE
+msgid "emergency"
+msgstr "å±éš¾"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "等于"
+
+#: NOT FOUND IN SOURCE
+msgid "error"
+msgstr "错误"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "错误:无法下移"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "错误:无法左移"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "错误:无法上移"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "错误:没有å¯åˆ é™¤çš„对象"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "错误:没有å¯ç§»åŠ¨çš„对象"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "错误:没有å¯åˆ‡æ¢çš„对象"
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "å‡"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "æ¡£å"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "大于"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "群组 '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "ä¾ %1 分组"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "å°æ—¶"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "ç¼–å·"
+
+#: NOT FOUND IN SOURCE
+msgid "info"
+msgstr "ä¿¡æ¯"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "是"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "ä¸æ˜¯"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "å°äºŽ"
+
+#: NOT FOUND IN SOURCE
+msgid "level Admin"
+msgstr "层主管"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "符åˆ"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "分"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "分钟"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "更改\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "月"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "新建立"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "没有å称"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "没有值"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "æ— "
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "ä¸ç­‰äºŽ"
+
+#: NOT FOUND IN SOURCE
+msgid "notice"
+msgstr "æ示"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "ä¸ç¬¦åˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "number"
+msgstr "å·"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "å¼€å¯"
+
+#: NOT FOUND IN SOURCE
+msgid "opened"
+msgstr "已开å¯"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "使用者‘%2’的‘%1’代ç†äººç¾¤ç»„"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "è¡¨å• %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "已驳回"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "已处ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "rtname"
+msgstr "æœåŠ¡å™¨å称"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "秒"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "显示设定页签"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "电å­è¡¨æ ¼"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "延宕"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr "æ ·å¼ï¼š%1"
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "加总列"
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "系统 %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "系统群组 '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "呼å«ç»„件未指明原因"
+
+#: NOT FOUND IN SOURCE
+msgid "ticket #%1"
+msgstr "ç”³è¯·å• #%1"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "ç”³è¯·å• #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "till"
+msgstr "至"
+
+#: NOT FOUND IN SOURCE
+msgid "to"
+msgstr "到"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "真"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "没有æ述的群组 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "unresolved"
+msgstr "未处ç†"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "使用者 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "warning"
+msgstr "警告"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "周"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "模æ¿ï¼š%1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "å¹´"
+
+msgid "Press 'Esc' to close this window."
+msgstr "按 'Esc' é”®å¯å…³é—­æœ¬çª—å£ã€‚"
+
+msgid "HasMember"
+msgstr "拥有æˆå‘˜"
+
+msgid "LinkedTo"
+msgstr "连结至"
+
+msgid "Watcher"
+msgstr "视察员"
+
+msgid "(displaying new and open tickets for %1)"
+msgstr "(显示 %1 å下新建立åŠå¼€å¯ä¸­çš„申请å•)"
diff --git a/rt/lib/RT/I18N/zh_tw.po b/rt/lib/RT/I18N/zh_tw.po
new file mode 100644
index 0000000..618f3dc
--- /dev/null
+++ b/rt/lib/RT/I18N/zh_tw.po
@@ -0,0 +1,8360 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RT 3.6.x\n"
+"PO-Revision-Date: 2007-12-09 13:05+0800\n"
+"Last-Translator: Audrey Tang <cpan@audreyt.org>\n"
+"Language-Team: rt-devel <rt-devel@lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: html/Widgets/SavedSearch:70
+#. ($self->{CurrentSearch}{Object}->Description)
+msgid " %1 deleted."
+msgstr ""
+
+#: html/Widgets/SavedSearch:47
+#. ($self->{CurrentSearch}{Description}, $args->{Description})
+msgid " %1 renamed to %2."
+msgstr ""
+
+#: html/Widgets/SavedSearch:60
+#. ($args->{Description})
+msgid " %1 saved."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "#"
+msgstr "#"
+
+#: NOT FOUND IN SOURCE
+msgid "#%1"
+msgstr "#%1"
+
+#: html/Approvals/Elements/Approve:48 html/Approvals/Elements/ShowDependency:71 html/SelfService/Display.html:46 html/Ticket/Display.html:47 html/Ticket/Display.html:51
+#. ($Ticket->id, $Ticket->Subject)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+#. ($ticket->Id, $ticket->Subject)
+#. ($TicketObj->Id, $TicketObj->Subject)
+msgid "#%1: %2"
+msgstr "#%1: %2"
+
+#: html/Elements/ShowSearch:105
+msgid "$1"
+msgstr "$1"
+
+#: lib/RT/Record.pm:940
+#. ($label)
+msgid "$prefix %1"
+msgstr "$prefix %1"
+
+#: NOT FOUND IN SOURCE
+msgid "%*(%1,group ticket)"
+msgstr "%*(%1) 件åƒèˆ‡çš„申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "%*(%1,ticket) due"
+msgstr "%*(%1) 件é™æœŸå®Œæˆçš„申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "%*(%1,unresolved ticket)"
+msgstr "%*(%1) 件尚未解決的申請單"
+
+#: lib/RT/URI/fsck_com_rt.pm:256
+#. ($self->ObjectType, $self->Object->Id)
+msgid "%1 #%2"
+msgstr "%1 #%2"
+
+#: lib/RT/Date.pm:365
+#. ($s, $time_unit)
+msgid "%1 %2"
+msgstr "%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 %3"
+msgstr "%1 %2 %3"
+
+#: lib/RT/Date.pm:401
+#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
+msgid "%1 %2 %3 %4:%5:%6 %7"
+msgstr "%7-%2-%3 %4:%5:%6 %1"
+
+#: lib/RT/Record.pm:1685 lib/RT/Transaction_Overlay.pm:647 lib/RT/Transaction_Overlay.pm:690
+#. ($cf->Name, $new_value->Content)
+#. ($field, $self->NewValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 added"
+msgstr "%2 已新增為 %1"
+
+#: lib/RT/Date.pm:362
+#. ($s, $time_unit)
+msgid "%1 %2 ago"
+msgstr "%1 %2 之å‰"
+
+#: lib/RT/Record.pm:1692 lib/RT/Transaction_Overlay.pm:654
+#. ($cf->Name, $old_content, $new_value->Content)
+#. ($field, $self->OldValue, $self->NewValue)
+msgid "%1 %2 changed to %3"
+msgstr "%1 已從 %2 改為 %3"
+
+#: lib/RT/Record.pm:1689 lib/RT/Transaction_Overlay.pm:650 lib/RT/Transaction_Overlay.pm:696
+#. ($cf->Name, $old_value->Content)
+#. ($field, $self->OldValue)
+#. ($self->Field, $principal->Object->Name)
+msgid "%1 %2 deleted"
+msgstr "%2 已自 %1 刪除"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 %2 of group %3"
+msgstr "%3 群組的 %1 %2"
+
+#: html/Admin/Elements/EditScrips:65 html/Admin/Elements/ListGlobalScrips:63 html/Ticket/Elements/PreviewScrips:103
+#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
+msgid "%1 %2 with template %3"
+msgstr "æ¢ä»¶ï¼š%1 | 動作:%2 | 範本:%3"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 (%2) %3 this ticket\\n"
+msgstr "%1 (%2) %3 這份申請單\\n"
+
+#: html/Ticket/Elements/ShowAttachments:72
+#. ($rev->CreatedAsString, $size, $rev->CreatorObj->Name)
+msgid "%1 (%2) by %3"
+msgstr "%1 (%2) - %3"
+
+#: html/SelfService/Update.html:60 html/Ticket/Elements/EditBasics:108 html/Ticket/Update.html:61 html/Ticket/Update.html:63 html/Tools/MyDay.html:66
+#. (loc($DefaultStatus))
+#. (loc($Ticket->Status()))
+#. (loc($TicketObj->Status))
+#. ($TicketObj->OwnerObj->Name())
+msgid "%1 (Unchanged)"
+msgstr "%1 (未更改)"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 - %2 shown"
+msgstr "顯示第 %1 - %2 筆"
+
+#: bin/rt-crontool:237 bin/rt-crontool:244 bin/rt-crontool:250
+#. ("--search-argument", "--search")
+#. ("--condition-argument", "--condition")
+#. ("--action-argument", "--action")
+msgid "%1 - An argument to pass to %2"
+msgstr "%1 - 傳éžçµ¦ %2 的一個åƒæ•¸"
+
+#: bin/rt-crontool:262
+#. ("--verbose")
+msgid "%1 - Output status updates to STDOUT"
+msgstr "%1 - 將更新狀態輸出到 STDOUT"
+
+#: bin/rt-crontool:253
+#. ("--template-id")
+msgid "%1 - Specify id of the template you want to use"
+msgstr "%1 - 指定欲使用的範本編號"
+
+#: bin/rt-crontool:256
+#. ("--transaction")
+msgid "%1 - Specify if you want to use either 'first' or 'last' tarnsaction"
+msgstr "%1 - 指定欲使用的更動為 'first' (第一項) 或 'last' (最後一項)"
+
+#: bin/rt-crontool:247
+#. ("--action")
+msgid "%1 - Specify the action module you want to use"
+msgstr "%1 - 指定欲使用的動作模組"
+
+#: bin/rt-crontool:241
+#. ("--condition")
+msgid "%1 - Specify the condition module you want to use"
+msgstr "%1 - 指定欲使用的æ¢ä»¶æ¨¡çµ„"
+
+#: bin/rt-crontool:234
+#. ("--search")
+msgid "%1 - Specify the search module you want to use"
+msgstr "%1 - 指定欲使用的查詢模組"
+
+#: bin/rt-crontool:259
+#. ("--transaction-type")
+msgid "%1 - Specify the type of a transaction you want to use"
+msgstr "%1 - 指定欲使用的更動類別"
+
+#: html/Elements/Footer:56
+#. ('&#187;&#124;&#171;', $RT::VERSION, '2006', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>',)
+msgid "%1 RT %2 Copyright 1996-%3 %4."
+msgstr "%1 RT %2 版,%4 版權所有,1996-%3。"
+
+#: lib/RT/ScripAction_Overlay.pm:150
+#. ($self->Id)
+msgid "%1 ScripAction loaded"
+msgstr "載入手續 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 Total"
+msgstr "å…± %1 ç­†"
+
+#: lib/RT/Record.pm:1722
+#. ($args{'Value'}, $cf->Name)
+msgid "%1 added as a value for %2"
+msgstr "新增 %1 作為 %2 的值"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on"
+msgstr "別å %1 需è¦å¯ç”¨çš„申請單編號"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on "
+msgstr "別å %1 需è¦å¯ç”¨çš„申請單編號 "
+
+#: NOT FOUND IN SOURCE
+msgid "%1 aliases require a TicketId to work on (from %2) %3"
+msgstr "別å %1 需è¦å¯ç”¨çš„ç”³è«‹å–®ç·¨è™Ÿä»¥è™•ç† %3(出自 %2)"
+
+#: lib/RT/Link_Overlay.pm:144 lib/RT/Link_Overlay.pm:151
+#. ($args{'Base'})
+#. ($args{'Target'})
+msgid "%1 appears to be a local object, but can't be found in the database"
+msgstr "%1 看來是個本地物件,å»ä¸åœ¨è³‡æ–™åº«è£¡"
+
+#: html/Ticket/Elements/ShowDates:73 lib/RT/Transaction_Overlay.pm:531
+#. ($self->BriefDescription , $self->CreatorObj->Name)
+#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
+msgid "%1 by %2"
+msgstr "%1 (%2)"
+
+#: lib/RT/Transaction_Overlay.pm:788 lib/RT/Transaction_Overlay.pm:797 lib/RT/Transaction_Overlay.pm:800
+#. ($self->Field , $q1->Name , $q2->Name)
+#. ($self->Field, $t2->AsString, $t1->AsString)
+#. ($self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'")
+msgid "%1 changed from %2 to %3"
+msgstr "%1 的值從 %2 改為 %3"
+
+#: html/Search/Build.html:213
+#. ($Description)
+msgid "%1 copy"
+msgstr "%1 複製"
+
+#: lib/RT/Record.pm:944
+msgid "%1 could not be set to %2."
+msgstr "無法將 %1 設定為 %2。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 couldn't init a transaction (%2)\\n"
+msgstr "%1 無法åˆå§‹æ›´æ–° (%2)\\n"
+
+#: lib/RT/Ticket_Overlay.pm:2787
+#. ($self)
+msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
+msgstr "%1 無法將ç¾æ³è¨­æˆå·²è§£æ±ºã€‚RT 資料庫內容å¯èƒ½ä¸ä¸€è‡´ã€‚"
+
+#: lib/RT/Transaction_Overlay.pm:571
+#. ($obj_type)
+msgid "%1 created"
+msgstr "已建立 %1"
+
+#: lib/RT/Transaction_Overlay.pm:576
+#. ($obj_type)
+msgid "%1 deleted"
+msgstr "已刪除 %1"
+
+#: etc/initialdata:593
+msgid "%1 highest priority tickets I own"
+msgstr "å‰ %1 份待處ç†ç”³è«‹å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I own..."
+msgstr "å‰ %1 份待處ç†ç”³è«‹å–®..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets I requested..."
+msgstr "å‰ %1 份é€å‡ºçš„申請單..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 highest priority tickets pending my approval..."
+msgstr "å‰ %1 份待簽核申請單..."
+
+#: bin/rt-crontool:229
+#. ($0)
+msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
+msgstr "%1 是從外部排程程å¼(如 cron)來å°ç”³è«‹å–®é€²è¡Œæ“作的工具。"
+
+#: lib/RT/Queue_Overlay.pm:863
+#. ($principal->Object->Name, $args{'Type'})
+msgid "%1 is no longer a %2 for this queue."
+msgstr "%1 å·²ä¸å†æ˜¯æ­¤è¡¨å–®çš„ %2。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a %2 for this ticket."
+msgstr "%1 å·²ä¸å†æ˜¯æ­¤ç”³è«‹å–®çš„ %2。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 is no longer a value for custom field %2"
+msgstr "%1 å·²ä¸å†æ˜¯è‡ªè¨‚æ¬„ä½ %2 的值。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 isn't a valid Queue id."
+msgstr "%1 ä¸æ˜¯ä¸€å€‹åˆæ³•çš„表單編號。"
+
+#: html/Ticket/Elements/ShowTime:47 html/Ticket/Elements/ShowTime:49
+#. ($minutes)
+msgid "%1 min"
+msgstr "%1 分é˜"
+
+#: etc/initialdata:601
+msgid "%1 newest unowned tickets"
+msgstr "å‰ %1 份待èªé ˜çš„申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 not shown"
+msgstr "沒有顯示 %1"
+
+#: lib/RT/CustomField_Overlay.pm:893
+msgid "%1 objects"
+msgstr "%1 物件"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 recent tickets I own..."
+msgstr "最新 %1 份待處ç†ç”³è«‹å–®..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 recent tickets I requested..."
+msgstr "最新 %1 份é€å‡ºçš„申請單..."
+
+#: NOT FOUND IN SOURCE
+msgid "%1 result(s) found"
+msgstr "找到 %1 é …çµæžœ"
+
+#: html/User/Elements/DelegateRights:97
+#. (loc($ObjectType =~ /^RT::(.*)$/))
+msgid "%1 rights"
+msgstr "%1權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 succeeded\\n"
+msgstr "%1 完æˆ\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for $MessageId"
+msgstr "ä¸çŸ¥é“ $MessageID çš„ %1 類別"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 type unknown for %2"
+msgstr "ä¸çŸ¥é“ %2 çš„ %1 類別"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 was created without a CurrentUser\\n"
+msgstr "%1 新增時未指定ç¾è¡Œä½¿ç”¨è€…"
+
+#: lib/RT/Action/ResolveMembers.pm:63
+#. (ref $self)
+msgid "%1 will resolve all members of a resolved group ticket."
+msgstr "%1 會解決在已解決群組裡æˆå“¡çš„申請單。"
+
+#: NOT FOUND IN SOURCE
+msgid "%1 will stall a [local] BASE if it's dependent [or member] of a linked up request."
+msgstr "如果 %1 起始申請單ä¾è³´æ–¼æŸå€‹éˆçµï¼Œæˆ–是æŸå€‹éˆçµçš„æˆå“¡ï¼Œå®ƒå°‡æœƒè¢«å»¶å®•ã€‚"
+
+#: lib/RT/CustomField_Overlay.pm:894
+msgid "%1's %2 objects"
+msgstr "%1 內的 %2 物件"
+
+#: lib/RT/CustomField_Overlay.pm:895
+msgid "%1's %2's %3 objects"
+msgstr "%1 內的 %2 的 %3 物件"
+
+#: html/Search/Elements/SearchPrivacy:52 html/Search/Elements/SelectSearchObject:55 html/Search/Elements/SelectSearchesForObjects:57
+#. ($object->Name)
+#. ($Object->Name)
+msgid "%1's saved searches"
+msgstr "%1 已儲存的查詢"
+
+#: lib/RT/Transaction_Overlay.pm:481
+#. ($self)
+msgid "%1: no attachment specified"
+msgstr "%1:未指定附件"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:78
+#. ($size)
+msgid "%1b"
+msgstr "%1 ä½å…ƒçµ„"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:75
+#. (int( $size / 102.4 ) / 10)
+msgid "%1k"
+msgstr "%1k ä½å…ƒçµ„"
+
+#: html/Ticket/Elements/ShowTime:49
+#. (sprintf("%.1f",$minutes / 60))
+msgid "%quant(%1,hour)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "%quant(%1,result) found"
+msgstr "找到 %1 é …çµæžœ"
+
+#: lib/RT/Ticket_Overlay.pm:1142
+#. ($args{'Status'})
+msgid "'%1' is an invalid value for status"
+msgstr "'%1' ä¸æ˜¯ä¸€å€‹åˆæ³•çš„狀態值"
+
+#: NOT FOUND IN SOURCE
+msgid "'%1' not a recognized action. "
+msgstr "'%1'為無法辨識的動作。 "
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete group member)"
+msgstr "(點é¸æ¬²åˆªé™¤çš„æˆå“¡)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check box to delete scrip)"
+msgstr "(點é¸æ¬²åˆªé™¤çš„手續)"
+
+#: html/Admin/Elements/EditCustomFieldValues:50 html/Admin/Elements/EditQueueWatchers:50 html/Admin/Elements/EditScrips:56 html/Admin/Elements/EditTemplates:57 html/Admin/Groups/Members.html:73 html/Elements/EditLinks:54 html/Ticket/Elements/EditPeople:67 html/User/Groups/Members.html:76
+msgid "(Check box to delete)"
+msgstr "(點é¸æ¬²åˆªé™¤çš„é …ç›®)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Check boxes to delete)"
+msgstr "(點é¸æ¬²åˆªé™¤çš„é …ç›®)"
+
+#: html/Ticket/Elements/PreviewScrips:99
+msgid "(Check boxes to disable notifications to the listed recipients)"
+msgstr "(點é¸æ¬²åœç”¨é€šçŸ¥çš„收件人)"
+
+#: html/Ticket/Elements/PreviewScrips:123
+msgid "(Check boxes to enable notifications to the listed recipients)"
+msgstr "(點é¸æ¬²å•Ÿç”¨é€šçŸ¥çš„收件人)"
+
+#: html/Ticket/Create.html:218
+msgid "(Enter ticket ids or URLs, separated with spaces)"
+msgstr "(éµå…¥ç”³è«‹å–®ç·¨è™Ÿæˆ–網å€ï¼Œä»¥ç©ºç™½åˆ†éš”)"
+
+#: html/Admin/Queues/Modify.html:75 html/Admin/Queues/Modify.html:81
+#. ($RT::CorrespondAddress)
+#. ($RT::CommentAddress)
+msgid "(If left blank, will default to %1)"
+msgstr "(如果留白, 則é è¨­ç‚º %1)"
+
+#: NOT FOUND IN SOURCE
+msgid "(No Value)"
+msgstr "(沒有值)"
+
+#: html/Admin/Elements/EditCustomFields:74 html/Admin/Elements/ListGlobalCustomFields:53
+msgid "(No custom fields)"
+msgstr "(沒有自訂欄ä½)"
+
+#: html/Admin/Groups/Members.html:71 html/User/Groups/Members.html:74
+msgid "(No members)"
+msgstr "(沒有æˆå“¡)"
+
+#: html/Admin/Elements/EditScrips:53 html/Admin/Elements/ListGlobalScrips:48
+msgid "(No scrips)"
+msgstr "(沒有手續)"
+
+#: html/Admin/Elements/EditTemplates:52
+msgid "(No templates)"
+msgstr "沒有範本"
+
+#: NOT FOUND IN SOURCE
+msgid "(No workflows)"
+msgstr "沒有æµç¨‹"
+
+#: html/Admin/Elements/PickCustomFields:47 html/Admin/Elements/PickObjects:47
+msgid "(None)"
+msgstr "(ç„¡)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„密件副本給å單上以逗號隔開的電å­éƒµä»¶ä½å€ã€‚這<b>ä¸æœƒ</b>更改後續的收件者å單。)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„密件副本給å單上以逗號隔開的電å­éƒµä»¶ä½å€ã€‚這<b>ä¸æœƒ</b>更改後續的收件者å單。)"
+
+#: html/Ticket/Update.html:90
+msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本給å單上以逗號隔開的管ç†å“¡é›»å­éƒµä»¶ä½å€ã€‚這<b>將會</b>更改後續的收件者å單。)"
+
+#: html/Ticket/Create.html:103
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本給å單上以逗號隔開的電å­éƒµä»¶ä½å€ã€‚這<b>ä¸æœƒ</b>更改後續的收件者å單。)"
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will recieve future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本給å單上以逗號隔開的電å­éƒµä»¶ä½å€ã€‚這<b>ä¸æœƒ</b>更改後續的收件者å單。)"
+
+#: html/Ticket/Update.html:86
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <strong>not</strong> change who will receive future updates.)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
+msgstr "(é€å‡ºæœ¬ä»½æ›´æ–°çš„副本給å單上以逗號隔開的電å­éƒµä»¶ä½å€ã€‚這<b>將會</b>更改後續的收件者å單。)"
+
+#: html/Ticket/Create.html:93
+msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)"
+msgstr ""
+
+#: html/Admin/Elements/EditScrip:96
+msgid "(Use these fields when you choose 'User Defined' for a condition or action)"
+msgstr "(當æ¢ä»¶æˆ–動作設為「使用者自訂ã€æ™‚,請填入這些欄ä½)"
+
+#: html/Ticket/Elements/EditWatchers:60 html/Ticket/Elements/ShowUserEntry:53
+msgid "(Will not be sent email)"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "(default delegate)"
+msgstr "(é è¨­ä»£ç†äºº)"
+
+#: NOT FOUND IN SOURCE
+msgid "(delete)"
+msgstr "(刪除)"
+
+#: html/Admin/Groups/index.html:57 html/User/Groups/index.html:54
+msgid "(empty)"
+msgstr "(空白)"
+
+#: NOT FOUND IN SOURCE
+msgid "(new)"
+msgstr "(新增)"
+
+#: html/Admin/Users/index.html:60
+msgid "(no name listed)"
+msgstr "(沒有列出姓å)"
+
+#: NOT FOUND IN SOURCE
+msgid "(no subject)"
+msgstr "(沒有主題)"
+
+#: html/Admin/Elements/SelectRights:72 html/Elements/EditCustomFieldSelect:69 html/Elements/SelectCustomFieldValue:51 html/Elements/ShowCustomFields:54 html/Search/Chart:56 html/Search/Elements/Chart:76 lib/RT/Transaction_Overlay.pm:591
+msgid "(no value)"
+msgstr "(ç„¡)"
+
+#: html/Admin/Elements/EditCustomFieldValues:47
+msgid "(no values)"
+msgstr "(沒有值)"
+
+#: html/Elements/EditLinks:132 html/Ticket/Elements/BulkLinks:49
+msgid "(only one ticket)"
+msgstr "(僅能指定一份申請單)"
+
+#: html/Elements/RT__Ticket/ColumnMap:149
+msgid "(pending approval)"
+msgstr "(等待簽核)"
+
+#: html/Elements/RT__Ticket/ColumnMap:152
+msgid "(pending other Collection)"
+msgstr "(等待其他集åˆ)"
+
+#: NOT FOUND IN SOURCE
+msgid "(pending other tickets)"
+msgstr "(等待其他申請單)"
+
+#: NOT FOUND IN SOURCE
+msgid "(requestor's group)"
+msgstr "(申請人所屬)"
+
+#: html/Admin/Users/Modify.html:71
+msgid "(required)"
+msgstr "(å¿…å¡«)"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "(untitled)"
+msgstr "(未命å)"
+
+#: html/Ticket/Elements/Reminders:133
+msgid "(yyyy/mm/dd)"
+msgstr "(yyyy/mm/dd)"
+
+#: NOT FOUND IN SOURCE
+msgid "*"
+msgstr "★"
+
+#: html/Elements/EditCustomFieldSelect:57
+msgid "-"
+msgstr "-"
+
+#: bin/rt-crontool:95
+msgid "--transaction argument could be only 'first' or 'last'"
+msgstr "--transaction 的值僅能為 'first' 或 'last'"
+
+#: NOT FOUND IN SOURCE
+msgid ":"
+msgstr ":"
+
+#: html/Ticket/Elements/ShowBasics:53
+msgid "<% $Ticket->Status%>"
+msgstr "<% $Ticket->Status%>"
+
+#: html/Elements/SelectTicketTypes:48
+msgid "<% $_ %>"
+msgstr "<% $_ %>"
+
+#: html/Search/Elements/SelectLinks:48
+msgid "<%$_%>"
+msgstr "<%$_%>"
+
+#: html/Search/Elements/DisplayOptions:73
+msgid "<%$field%>"
+msgstr "<%$field%>"
+
+#: html/Elements/CreateTicket:47
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" class=\"button\" value=\"New ticket in\" />&nbsp;%1"
+msgstr "<input type=\"submit\" class=\"button\" value=\"æ出申請單\" />&nbsp;%1"
+
+#: docs/design_docs/string-extraction-guide.txt:54 lib/RT/StyleGuide.pod:787
+#. ($m->scomp('/Elements/SelectNewTicketQueue'))
+msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
+msgstr "<input type=\"submit\" value=\"æ出申請單\">&nbsp;%1"
+
+#: etc/initialdata:218
+msgid "A blank template"
+msgstr "空白範本"
+
+#: html/Admin/Users/Modify.html:371
+msgid "A password was not set, so user won't be able to login."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Deleted"
+msgstr "ACE 已刪除"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE Loaded"
+msgstr "ACE 已載入"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be deleted"
+msgstr "無法刪除 ACE"
+
+#: NOT FOUND IN SOURCE
+msgid "ACE could not be found"
+msgstr "找ä¸åˆ° ACE"
+
+#: lib/RT/ACE_Overlay.pm:174 lib/RT/Principal_Overlay.pm:219
+msgid "ACE not found"
+msgstr "找ä¸åˆ° ACE 設定"
+
+#: lib/RT/ACE_Overlay.pm:853
+msgid "ACEs can only be created and deleted."
+msgstr "祇能新增或刪除 ACE 設定。"
+
+#: NOT FOUND IN SOURCE
+msgid "ACLEquivalence"
+msgstr "ACLEquivalence"
+
+#: html/Search/Elements/SelectAndOr:46
+msgid "AND"
+msgstr "AND"
+
+#: NOT FOUND IN SOURCE
+msgid "Aborting to avoid unintended ticket modifications.\\n"
+msgstr "離開以å…ä¸å°å¿ƒæ›´æ”¹åˆ°ç”³è«‹å–®ã€‚\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "About Me"
+msgstr "個人資訊"
+
+#: html/User/Elements/Tabs:53
+msgid "About me"
+msgstr "個人資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "Access Right"
+msgstr "系統使用登錄權é™"
+
+#: html/Admin/Users/Modify.html:106
+msgid "Access control"
+msgstr "å­˜å–權é™"
+
+#: html/Admin/Elements/EditScrip:65
+msgid "Action"
+msgstr "動作"
+
+#: lib/RT/Scrip_Overlay.pm:172
+#. ($args{'ScripAction'})
+msgid "Action %1 not found"
+msgstr "動作 %1 找ä¸åˆ°"
+
+#: NOT FOUND IN SOURCE
+msgid "Action committed."
+msgstr "動作執行完畢"
+
+#: bin/rt-crontool:171
+msgid "Action committed.\\n"
+msgstr "動作執行完畢。\\n"
+
+#: lib/RT/Scrip_Overlay.pm:168
+msgid "Action is mandatory argument"
+msgstr ""
+
+#: bin/rt-crontool:167
+msgid "Action prepared..."
+msgstr "動作準備完畢..."
+
+#: NOT FOUND IN SOURCE
+msgid "Activated Date"
+msgstr "申請啟動時間"
+
+#: html/Search/Build.html:85
+msgid "Add"
+msgstr "新增"
+
+#: html/Search/Bulk.html:92
+msgid "Add AdminCc"
+msgstr "新增管ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: html/Search/Bulk.html:88
+msgid "Add Cc"
+msgstr "新增副本收件人"
+
+#: html/Search/Elements/EditFormat:49
+msgid "Add Columns"
+msgstr ""
+
+#: html/Search/Elements/PickCriteria:46
+msgid "Add Criteria"
+msgstr "新增æ¢ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Entry"
+msgstr "新增列"
+
+#: html/Ticket/Create.html:147 html/Ticket/Update.html:116
+msgid "Add More Files"
+msgstr "新增更多附件"
+
+#: NOT FOUND IN SOURCE
+msgid "Add Next State"
+msgstr "新增下一項關å¡"
+
+#: html/Search/Bulk.html:84
+msgid "Add Requestor"
+msgstr "新增申請人"
+
+#: html/Admin/Elements/AddCustomFieldValue:46
+msgid "Add Value"
+msgstr "新增欄ä½å€¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip to this queue"
+msgstr "新增此表單的手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a Scrip which will apply to all queues"
+msgstr "新增é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a keyword selection to this queue"
+msgstr "新增此表單的關éµå­—"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a new a global scrip"
+msgstr "新增全域手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Add a scrip to this queue"
+msgstr "新增一é“手續到此表單"
+
+#: html/Admin/Global/Scrip.html:83
+msgid "Add a scrip which will apply to all queues"
+msgstr "新增一é“用於所有表單的手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Add additional criteria"
+msgstr "新增查詢æ¢ä»¶"
+
+#: html/Search/Build.html:109 html/Search/Build.html:94
+msgid "Add and Search"
+msgstr ""
+
+#: html/Search/Bulk.html:124
+msgid "Add comments or replies to selected tickets"
+msgstr "新增評論或回覆到指定的申請單"
+
+#: html/Admin/Groups/Members.html:63 html/User/Groups/Members.html:60
+msgid "Add members"
+msgstr "新增æˆå“¡"
+
+#: html/Admin/Queues/People.html:87 html/Ticket/Elements/AddWatchers:49
+msgid "Add new watchers"
+msgstr "新增視察員"
+
+#: html/Search/Build.html:85
+msgid "Add these terms to your search"
+msgstr ""
+
+#: html/Search/Bulk.html:158
+msgid "Add values"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "Add, delete and modify custom field values for objects"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "AddNextState"
+msgstr "新增下一項關å¡"
+
+#: lib/RT/Queue_Overlay.pm:763
+#. ($args{'Type'})
+msgid "Added principal as a %1 for this queue"
+msgstr "å–®ä½å·²æ–°å¢žç‚ºæ­¤è¡¨å–®çš„ %1"
+
+#: lib/RT/Ticket_Overlay.pm:1455
+#. ($self->loc($args{'Type'}))
+msgid "Added principal as a %1 for this ticket"
+msgstr "å–®ä½å·²æ–°å¢žç‚ºæ­¤ç”³è«‹å–®çš„ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Additional Hints"
+msgstr "é¡å¤–æ示"
+
+#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:133
+msgid "Address1"
+msgstr "ä½å€"
+
+#: html/Admin/Users/Modify.html:151 html/User/Prefs.html:137
+msgid "Address2"
+msgstr "ä½å€(續)"
+
+#: NOT FOUND IN SOURCE
+msgid "Adjust Blinking Rate"
+msgstr "調整閃çˆé€Ÿåº¦å¿«æ…¢"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin"
+msgstr "管ç†å“¡"
+
+#: html/Ticket/Create.html:98
+msgid "Admin Cc"
+msgstr "管ç†å“¡å‰¯æœ¬"
+
+#: etc/initialdata:295
+msgid "Admin Comment"
+msgstr "管ç†å“¡è©•è«–"
+
+#: etc/initialdata:274
+msgid "Admin Correspondence"
+msgstr "管ç†å“¡å›žè¦†"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin Rights"
+msgstr "管ç†å“¡æ¬Šé™"
+
+#: html/Admin/Queues/index.html:46 html/Admin/Queues/index.html:49
+msgid "Admin queues"
+msgstr "表單管ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin users"
+msgstr "使用者管ç†"
+
+#: html/Admin/Global/index.html:47 html/Admin/Global/index.html:49
+msgid "Admin/Global configuration"
+msgstr "管ç†/全域設定"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Groups"
+msgstr "管ç†/群組"
+
+#: NOT FOUND IN SOURCE
+msgid "Admin/Queue/Basics"
+msgstr "管ç†/表單/基本資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAddress"
+msgstr "管ç†å“¡ Email"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminAllPersonalGroups"
+msgstr "管ç†æ‰€æœ‰ä»£ç†äººç¾¤çµ„"
+
+#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:60 lib/RT/ACE_Overlay.pm:113
+msgid "AdminCc"
+msgstr "管ç†å“¡å‰¯æœ¬"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminComment"
+msgstr "管ç†å“¡è©•è«–"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCorrespondence"
+msgstr "管ç†å“¡å›žè¦†"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "AdminCustomField"
+msgstr "管ç†è‡ªè¨‚欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminCustomFields"
+msgstr "管ç†è‡ªè¨‚欄ä½"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "AdminGroup"
+msgstr "管ç†ç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupDescription"
+msgstr "管ç†ç¾¤çµ„æè¿°"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "AdminGroupMembership"
+msgstr "管ç†ç¾¤çµ„æˆå“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupName"
+msgstr "管ç†ç¾¤çµ„å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupPermission"
+msgstr "管ç†ç¾¤çµ„權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "AdminGroupStatus"
+msgstr "管ç†ç¾¤çµ„狀態"
+
+#: lib/RT/System.pm:80
+msgid "AdminOwnPersonalGroups"
+msgstr "管ç†ä»£ç†äººç¾¤çµ„"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "AdminQueue"
+msgstr "管ç†è¡¨å–®"
+
+#: lib/RT/System.pm:81
+msgid "AdminUsers"
+msgstr "管ç†ä½¿ç”¨è€…"
+
+#: NOT FOUND IN SOURCE
+msgid "Administrative"
+msgstr "行政類"
+
+#: html/Admin/Queues/People.html:69 html/Ticket/Elements/EditPeople:75
+msgid "Administrative Cc"
+msgstr "管ç†å“¡å‰¯æœ¬"
+
+#: NOT FOUND IN SOURCE
+msgid "Admins"
+msgstr "主管"
+
+#: html/Ticket/Elements/Tabs:216
+msgid "Advanced"
+msgstr "進階"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search"
+msgstr "進階查詢"
+
+#: NOT FOUND IN SOURCE
+msgid "Advanced Search Criteria"
+msgstr "進階查詢æ¢ä»¶"
+
+#: html/Elements/SelectDateRelation:57
+msgid "After"
+msgstr "晚於"
+
+#: NOT FOUND IN SOURCE
+msgid "Age"
+msgstr "經歷時間"
+
+#: html/Search/Elements/PickCriteria:52
+msgid "Aggregator"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Alias"
+msgstr "執行其他æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Alias for"
+msgstr "相當於"
+
+#: NOT FOUND IN SOURCE
+msgid "All"
+msgstr "全部"
+
+#: etc/initialdata:363
+msgid "All Approvals Passed"
+msgstr "完æˆå…¨éƒ¨ç°½æ ¸"
+
+#: NOT FOUND IN SOURCE
+msgid "All Condition"
+msgstr "所有æ¢ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "All Custom Fields"
+msgstr "所有自訂欄ä½"
+
+#: html/Admin/Queues/index.html:75
+msgid "All Queues"
+msgstr "所有表單"
+
+#: NOT FOUND IN SOURCE
+msgid "All Users"
+msgstr "全體員工"
+
+#: NOT FOUND IN SOURCE
+msgid "All done! Now you can proceed to %1."
+msgstr "處ç†å®Œç•¢ï¼æ‚¨ç¾åœ¨å¯ä»¥ç¹¼çºŒé€²è¡Œ %1。"
+
+#: NOT FOUND IN SOURCE
+msgid "Allowance Request"
+msgstr "ç¦åˆ©è£œåŠ©ç”³è«‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Always sends a message to the requestors independent of message sender"
+msgstr "無論寄件來æºç‚ºä½•ï¼Œä¸€å¾‹å¯„信給申請人"
+
+#: NOT FOUND IN SOURCE
+msgid "Amount"
+msgstr "數é¡"
+
+#: html/Search/Elements/EditQuery:56
+msgid "And/Or"
+msgstr "AND/OR"
+
+#: NOT FOUND IN SOURCE
+msgid "Any Condition"
+msgstr "ä»»æ„æ¢ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Applies To"
+msgstr "套用於"
+
+#: html/Admin/CustomFields/Modify.html:73 html/Admin/Elements/CustomFieldTabs:83
+msgid "Applies to"
+msgstr "套用於"
+
+#: html/Search/Edit.html:64
+msgid "Apply"
+msgstr "套用"
+
+#: NOT FOUND IN SOURCE
+msgid "Apply Template"
+msgstr "引用範本"
+
+#: html/Search/Edit.html:64
+msgid "Apply your changes"
+msgstr "套用更動"
+
+#: html/Elements/Tabs:77
+msgid "Approval"
+msgstr "簽核"
+
+#: html/Approvals/Display.html:65 html/Approvals/Elements/ShowDependency:63 html/Approvals/index.html:86
+#. ($Ticket->Id, $Ticket->Subject)
+#. ($ticket->id, $msg)
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Approval #%1: %2"
+msgstr "簽核單 #%1:%2"
+
+#: html/Approvals/index.html:75
+#. ($ticket->Id)
+msgid "Approval #%1: Notes not recorded due to a system error"
+msgstr "簽核單 #%1:系統錯誤,記錄失敗"
+
+#: html/Approvals/index.html:73
+#. ($ticket->Id)
+msgid "Approval #%1: Notes recorded"
+msgstr "簽核單 #%1:記錄完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Details"
+msgstr "簽核細節"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Due"
+msgstr "簽核時é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Notes"
+msgstr "簽核æ„見"
+
+#: etc/initialdata:351
+msgid "Approval Passed"
+msgstr "完æˆæŸé …簽核"
+
+#: etc/initialdata:374
+msgid "Approval Rejected"
+msgstr "é§å›žæŸé …簽核"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Result"
+msgstr "簽核çµæžœ"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Status"
+msgstr "核准çµæžœ"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval Type"
+msgstr "簽核種類"
+
+#: NOT FOUND IN SOURCE
+msgid "Approval diagram"
+msgstr "簽核æµç¨‹"
+
+#: html/Approvals/Elements/Approve:69
+msgid "Approve"
+msgstr "核准"
+
+#: NOT FOUND IN SOURCE
+msgid "Approver"
+msgstr "簽核人"
+
+#: NOT FOUND IN SOURCE
+msgid "Approver Setting"
+msgstr "執行簽核人設定"
+
+#: etc/initialdata:504
+msgid "Approver's notes: %1"
+msgstr "簽核備註:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Apr"
+msgstr "四月"
+
+#: lib/RT/Date.pm:444
+msgid "Apr."
+msgstr "04"
+
+#: NOT FOUND IN SOURCE
+msgid "April"
+msgstr "四月"
+
+#: NOT FOUND IN SOURCE
+msgid "Are you sure to delete checked items?"
+msgstr "您確定è¦åˆªé™¤ï¼Ÿ"
+
+#: html/Search/Elements/DisplayOptions:81
+msgid "Asc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Ascending"
+msgstr "éžå¢ž"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "Assign and remove custom fields"
+msgstr "指派åŠç§»é™¤è‡ªè¨‚欄ä½"
+
+#: lib/RT/Queue_Overlay.pm:96
+msgid "AssignCustomFields"
+msgstr "指派自訂欄ä½"
+
+#: html/Search/Bulk.html:142 html/SelfService/Update.html:87 html/Ticket/ModifyAll.html:115 html/Ticket/Update.html:116
+msgid "Attach"
+msgstr "附件"
+
+#: html/SelfService/Create.html:92 html/Ticket/Create.html:143
+msgid "Attach file"
+msgstr "附加檔案"
+
+#: html/SelfService/Update.html:75 html/Ticket/Create.html:131 html/Ticket/Update.html:94
+msgid "Attached file"
+msgstr "ç¾æœ‰é™„件"
+
+#: html/Ticket/ShowEmailRecord.html:52 html/Ticket/ShowEmailRecord.html:56 html/Ticket/ShowEmailRecord.html:59
+#. ($Attachment)
+msgid "Attachment '%1' could not be loaded"
+msgstr "無法載入附件 '%1'"
+
+#: lib/RT/Transaction_Overlay.pm:489
+msgid "Attachment created"
+msgstr "附件新增完畢"
+
+#: lib/RT/Tickets_Overlay.pm:1945
+msgid "Attachment filename"
+msgstr "附件檔å"
+
+#: html/Ticket/Elements/ShowAttachments:47
+msgid "Attachments"
+msgstr "附件"
+
+#: lib/RT/Attributes_Overlay.pm:171
+msgid "Attribute Deleted"
+msgstr "已刪除該屬性"
+
+#: NOT FOUND IN SOURCE
+msgid "Attributes"
+msgstr "屬性"
+
+#: NOT FOUND IN SOURCE
+msgid "Aug"
+msgstr "八月"
+
+#: lib/RT/Date.pm:448
+msgid "Aug."
+msgstr "08"
+
+#: NOT FOUND IN SOURCE
+msgid "August"
+msgstr "八月"
+
+#: NOT FOUND IN SOURCE
+msgid "AuthSystem"
+msgstr "èªè­‰æ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoReject"
+msgstr "自動é§å›žè¡¨å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoResolve"
+msgstr "自動完æˆè¡¨å–®è™•ç†"
+
+#: etc/initialdata:221
+msgid "Autoreply"
+msgstr "自動回覆"
+
+#: etc/initialdata:72
+msgid "Autoreply To Requestors"
+msgstr "自動å°ç”³è«‹äººå›žè¦†"
+
+#: NOT FOUND IN SOURCE
+msgid "AutoreplyToRequestors"
+msgstr "自動å°ç”³è«‹äººå›žè¦†"
+
+#: html/Widgets/SelectionBox:185
+msgid "Available"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Available Columns"
+msgstr "å¯ç”¨çš„欄ä½ï¼š"
+
+#: NOT FOUND IN SOURCE
+msgid "Available Rights:"
+msgstr "權é™é …目列表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Back to Homepage"
+msgstr "回到首é "
+
+#: NOT FOUND IN SOURCE
+msgid "Back to Previous"
+msgstr "回上é "
+
+#: NOT FOUND IN SOURCE
+msgid "Bad PGP Signature: %1\\n"
+msgstr "錯誤的 PGP 簽章:%1\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad attachment id. Couldn't find attachment '%1'\\n"
+msgstr "錯誤的附件編號。無法找到附件 '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad data in %1"
+msgstr "%1 的資料錯誤"
+
+#: NOT FOUND IN SOURCE
+msgid "Bad transaction number for attachment. %1 should be %2\\n"
+msgstr "附件的處ç†è™Ÿç¢¼éŒ¯èª¤ã€‚%1 應為 %2\\n"
+
+#: html/Admin/Elements/CustomFieldTabs:65 html/Admin/Elements/GroupTabs:60 html/Admin/Elements/QueueTabs:60 html/Admin/Elements/UserTabs:58 html/Ticket/Elements/Tabs:113 html/User/Elements/GroupTabs:59
+msgid "Basics"
+msgstr "基本資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "Batch Approval"
+msgstr "批次簽核"
+
+#: html/Ticket/Update.html:88
+msgid "Bcc"
+msgstr "密件副本"
+
+#: html/Admin/CustomFields/GroupRights.html:91 html/Admin/CustomFields/UserRights.html:74 html/Admin/Elements/EditScrip:89
+msgid "Be sure to save your changes"
+msgstr "請別忘了儲存修改。"
+
+#: html/Elements/SelectDateRelation:55 lib/RT/CurrentUser.pm:361
+msgid "Before"
+msgstr "æ—©æ–¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin Approval"
+msgstr "開始簽核"
+
+#: NOT FOUND IN SOURCE
+msgid "Begin From "
+msgstr "起始日"
+
+#: html/Elements/Logo:47
+msgid "Best Practical Solutions, LLC corporate logo"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Binary"
+msgstr "檔案"
+
+#: NOT FOUND IN SOURCE
+msgid "Birthday"
+msgstr "生日"
+
+#: etc/initialdata:217
+msgid "Blank"
+msgstr "空白範本"
+
+#: html/Search/Elements/EditFormat:84
+msgid "Bold"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bookmarkable URL for this search"
+msgstr "將查詢çµæžœè½‰ç‚ºå¯æ”¾å…¥æ›¸ç±¤çš„網å€"
+
+#: html/Search/Results.html:79
+msgid "Bookmarkable link"
+msgstr "å¯æ”¾å…¥æ›¸ç±¤çš„網å€"
+
+#: html/Ticket/Elements/ShowHistory:64 html/Ticket/Elements/ShowHistory:69
+msgid "Brief headers"
+msgstr "精簡標頭檔"
+
+#: html/Ticket/Elements/Tabs:227
+msgid "Bulk Update"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Bulk ticket update"
+msgstr "更新整批申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Business Unit"
+msgstr "事業部"
+
+#: NOT FOUND IN SOURCE
+msgid "Business Unit:"
+msgstr "事業部:"
+
+#: lib/RT/User_Overlay.pm:1853
+msgid "Can not modify system users"
+msgstr "無法更改系統使用者"
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "Can this principal see this queue"
+msgstr "該單ä½æ˜¯å¦èƒ½æŸ¥é–±æ­¤è¡¨å–®"
+
+#: lib/RT/CustomField_Overlay.pm:379
+msgid "Can't add a custom field value without a name"
+msgstr "ä¸èƒ½æ–°å¢žæ²’有å稱的自訂欄ä½å€¼"
+
+#: html/Admin/CustomFields/Objects.html:86
+#. ($Class)
+msgid "Can't find a collection class for '%1'"
+msgstr ""
+
+#: html/Search/Build.html:286
+msgid "Can't find a saved search to work with"
+msgstr "找ä¸åˆ°å·²å„²å­˜çš„查詢"
+
+#: lib/RT/Link_Overlay.pm:159
+msgid "Can't link a ticket to itself"
+msgstr "申請單ä¸èƒ½éˆçµè‡ªå·±ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Can't merge into a merged ticket. You should never get this error"
+msgstr "ä¸èƒ½æ•´åˆé€²å·²æ•´åˆéŽçš„申請單。這個錯誤ä¸è©²ç™¼ç”Ÿã€‚"
+
+#: html/Widgets/SavedSearch:63
+#. (loc($self->{SearchType}))
+msgid "Can't save %1"
+msgstr "無法儲存 %1"
+
+#: html/Search/Build.html:290
+msgid "Can't save this search"
+msgstr "無法儲存此項查詢"
+
+#: lib/RT/Record.pm:1282 lib/RT/Record.pm:1358
+msgid "Can't specifiy both base and target"
+msgstr "ä¸èƒ½åŒæ™‚指定起始申請單與目的申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Cancel"
+msgstr "å–消"
+
+#: html/autohandler:204
+#. ($msg)
+msgid "Cannot create user: %1"
+msgstr "無法新增使用者:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Cannot login: Your system clock differs from server's by %1 seconds!"
+msgstr "您的系統時é˜å’Œä¼ºæœå™¨ç›¸å·® %1 秒,無法登入ï¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Card No."
+msgstr "å¡è™Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "Categories"
+msgstr "分類管ç†"
+
+#: html/Admin/Elements/AddCustomFieldValue:62 html/Admin/Elements/EditCustomFieldValues:58
+msgid "Category"
+msgstr "分類"
+
+#: etc/initialdata:50 html/Admin/Queues/People.html:65 html/SelfService/Create.html:71 html/Ticket/Create.html:88 html/Ticket/Elements/EditPeople:72 html/Ticket/Elements/ShowPeople:56 html/Ticket/Update.html:83 lib/RT/ACE_Overlay.pm:112
+msgid "Cc"
+msgstr "副本"
+
+#: NOT FOUND IN SOURCE
+msgid "Cc Type"
+msgstr "副本類別"
+
+#: NOT FOUND IN SOURCE
+msgid "Chairperson's Office"
+msgstr "董事長室"
+
+#: NOT FOUND IN SOURCE
+msgid "Change Ticket"
+msgstr "修改申請單"
+
+#: html/SelfService/Prefs.html:52
+msgid "Change password"
+msgstr "更改密碼"
+
+#: NOT FOUND IN SOURCE
+msgid "ChangeOwnerUI"
+msgstr "å¯å¦é¸æ“‡è¡¨å–®æ‰¿è¾¦äºº"
+
+#: html/Elements/Submit:78
+msgid "Check All"
+msgstr "全部é¸å–"
+
+#: html/SelfService/Update.html:78 html/Ticket/Create.html:134 html/Ticket/Update.html:97
+msgid "Check box to delete"
+msgstr "é¸æ“‡æ¬²åˆªé™¤çš„é …ç›®"
+
+#: html/Admin/Elements/SelectRights:55
+msgid "Check box to revoke right"
+msgstr "é¸æ“‡æ¬²æ’¤æ¶ˆçš„權利"
+
+#: html/Elements/EditLinks:148 html/Elements/EditLinks:85 html/Elements/ShowLinks:78 html/Ticket/Create.html:223 html/Ticket/Elements/BulkLinks:64
+msgid "Children"
+msgstr "å­ç”³è«‹å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Chinese Name"
+msgstr "中文姓å"
+
+#: NOT FOUND IN SOURCE
+msgid "Chinese/English"
+msgstr "中英文"
+
+#: html/NoAuth/js/util.js:201
+msgid "Choose a date"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:141
+msgid "City"
+msgstr "所在城市"
+
+#: NOT FOUND IN SOURCE
+msgid "ClassicUI"
+msgstr "傳統介é¢"
+
+#: html/Elements/Submit:80
+msgid "Clear All"
+msgstr "全部清除"
+
+#: html/Helpers/CalPopup.html:51
+msgid "Close window"
+msgstr "關閉視窗"
+
+#: html/Ticket/Elements/ShowDates:68
+msgid "Closed"
+msgstr "已解決"
+
+#: NOT FOUND IN SOURCE
+msgid "Closed Tickets"
+msgstr "已解決的申請單"
+
+#: html/SelfService/Closed.html:46 html/SelfService/Elements/Tabs:78
+msgid "Closed tickets"
+msgstr "已解決的申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Code"
+msgstr "執行程å¼ç¢¼"
+
+#: lib/RT/CustomField_Overlay.pm:89
+msgid "Combobox: Select or enter multiple values"
+msgstr "下拉文字框:é¸æ“‡æˆ–éµå…¥å¤šé‡é …ç›®"
+
+#: lib/RT/CustomField_Overlay.pm:90
+msgid "Combobox: Select or enter one value"
+msgstr "下拉文字框:é¸æ“‡æˆ–éµå…¥å–®ä¸€é …ç›®"
+
+#: lib/RT/CustomField_Overlay.pm:91
+msgid "Combobox: Select or enter up to %1 values"
+msgstr "下拉文字框:é¸æ“‡æˆ–éµå…¥æœ€å¤š %1 個項目"
+
+#: NOT FOUND IN SOURCE
+msgid "Command not understood!\\n"
+msgstr "指令無法辨識ï¼\\n"
+
+#: html/Ticket/Elements/ShowTransaction:190 html/Ticket/Elements/Tabs:185
+msgid "Comment"
+msgstr "è©•è«–"
+
+#: html/Admin/Queues/Modify.html:79
+msgid "Comment Address"
+msgstr "è©•è«–é›»å­éƒµä»¶åœ°å€"
+
+#: NOT FOUND IN SOURCE
+msgid "Comment not recorded"
+msgstr "評論未被紀錄"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "Comment on tickets"
+msgstr "å°ç”³è«‹å–®æ出評論"
+
+#: lib/RT/Queue_Overlay.pm:111
+msgid "CommentOnTicket"
+msgstr "評論申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments"
+msgstr "è©•è«–"
+
+#: html/Ticket/ModifyAll.html:91 html/Ticket/Update.html:75
+msgid "Comments (Not sent to requestors)"
+msgstr "è©•è«–(ä¸é€çµ¦ç”³è«‹äºº)"
+
+#: html/Search/Bulk.html:128
+msgid "Comments (not sent to requestors)"
+msgstr "è©•è«–(ä¸é€çµ¦ç”³è«‹äºº)"
+
+#: NOT FOUND IN SOURCE
+msgid "Comments about %1"
+msgstr "å° %1 çš„è©•è«–"
+
+#: html/Admin/Users/Modify.html:225 html/Ticket/Elements/ShowRequestor:67
+msgid "Comments about this user"
+msgstr "使用者æè¿°"
+
+#: lib/RT/Transaction_Overlay.pm:634
+msgid "Comments added"
+msgstr "新增評論完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "Commit"
+msgstr "確èª"
+
+#: lib/RT/Action/Generic.pm:175
+msgid "Commit Stubbed"
+msgstr "消除更動完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "Company Name"
+msgstr "å…¬å¸å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "CompanySpecific"
+msgstr "å„å…¬å¸ç¨ç«‹é¡¯ç¤º"
+
+#: NOT FOUND IN SOURCE
+msgid "Compile Restrictions"
+msgstr "設定查詢æ¢ä»¶"
+
+#: html/Admin/Elements/EditScrip:59
+msgid "Condition"
+msgstr "æ¢ä»¶"
+
+#: lib/RT/Scrip_Overlay.pm:184
+msgid "Condition is mandatory argument"
+msgstr "æ¢ä»¶æ˜¯å¿…填欄ä½"
+
+#: bin/rt-crontool:151
+msgid "Condition matches..."
+msgstr "符åˆæ¢ä»¶..."
+
+#: lib/RT/Scrip_Overlay.pm:188
+msgid "Condition not found"
+msgstr "未找到符åˆçš„ç¾æ³"
+
+#: html/Elements/Tabs:84
+msgid "Configuration"
+msgstr "設定"
+
+#: html/SelfService/Prefs.html:54
+msgid "Confirm"
+msgstr "確èªå¯†ç¢¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Confirm Password"
+msgstr "密碼確èª"
+
+#: NOT FOUND IN SOURCE
+msgid "Confirm Submit"
+msgstr "確定é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "Contact System Administrator"
+msgstr "連絡系統管ç†å“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "ContactInfoSystem"
+msgstr "連絡資訊系統"
+
+#: NOT FOUND IN SOURCE
+msgid "Contacted date '%1' could not be parsed"
+msgstr "無法解讀è¯çµ¡æ—¥æœŸ '%1'"
+
+#: html/Admin/Elements/ModifyTemplate:65 html/Elements/SelectAttachmentField:48 html/Ticket/ModifyAll.html:119
+msgid "Content"
+msgstr "內容"
+
+#: html/Elements/SelectAttachmentField:49
+msgid "Content-Type"
+msgstr "內容類型"
+
+#: NOT FOUND IN SOURCE
+msgid "Coould not create group"
+msgstr "無法新增群組"
+
+#: html/Search/Elements/EditSearches:65
+msgid "Copy"
+msgstr "複製"
+
+#: NOT FOUND IN SOURCE
+msgid "Copy Field From:"
+msgstr "欲複製欄ä½ï¼š"
+
+#: etc/initialdata:286
+msgid "Correspondence"
+msgstr "回覆"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence Address"
+msgstr "申請單回覆地å€"
+
+#: lib/RT/Transaction_Overlay.pm:630
+msgid "Correspondence added"
+msgstr "新增申請單回覆"
+
+#: NOT FOUND IN SOURCE
+msgid "Correspondence not recorded"
+msgstr "未紀錄申請單回覆"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè¨‚欄ä½çš„值。"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not add new custom field value for ticket. %1 "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè¨‚欄ä½çš„值。%1 "
+
+#: lib/RT/Record.pm:1707
+msgid "Could not add new custom field value. "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè¨‚欄ä½çš„值。"
+
+#: lib/RT/Record.pm:1660
+#. (, $value_msg)
+msgid "Could not add new custom field value. %1 "
+msgstr "ä¸èƒ½æ–°å¢žè‡ªè¨‚欄ä½çš„值。%1 "
+
+#: lib/RT/Ticket_Overlay.pm:3048 lib/RT/Ticket_Overlay.pm:3056 lib/RT/Ticket_Overlay.pm:3073
+msgid "Could not change owner. "
+msgstr "ä¸èƒ½æ›´æ”¹æ‰¿è¾¦äººã€‚"
+
+#: html/Admin/CustomFields/Modify.html:161
+#. ($msg)
+msgid "Could not create CustomField"
+msgstr "無法新增自訂欄ä½"
+
+#: html/Admin/Elements/EditCustomField:113
+#. ($msg)
+msgid "Could not create CustomField: %1"
+msgstr "無法新增自訂欄ä½ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create Scrip"
+msgstr "無法建立訊æ¯é€šçŸ¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create Template"
+msgstr "無法建立通知範本"
+
+#: html/User/Groups/Modify.html:98 lib/RT/Group_Overlay.pm:494 lib/RT/Group_Overlay.pm:501
+msgid "Could not create group"
+msgstr "無法新增群組"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create item"
+msgstr "無法新增項目"
+
+#: html/Admin/Global/Template.html:96 html/Admin/Queues/Template.html:93
+#. ($msg)
+msgid "Could not create template: %1"
+msgstr "無法新增範本:%1"
+
+#: lib/RT/Ticket_Overlay.pm:1075 lib/RT/Ticket_Overlay.pm:407
+msgid "Could not create ticket. Queue not set"
+msgstr "無法新增申請單。尚未指定表單。"
+
+#: lib/RT/User_Overlay.pm:255 lib/RT/User_Overlay.pm:269 lib/RT/User_Overlay.pm:278 lib/RT/User_Overlay.pm:287 lib/RT/User_Overlay.pm:296 lib/RT/User_Overlay.pm:310 lib/RT/User_Overlay.pm:320 lib/RT/User_Overlay.pm:496
+msgid "Could not create user"
+msgstr "無法新增使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create watcher for requestor"
+msgstr "無法為申請人新增視察員"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not create workflow: %1"
+msgstr "無法新增æµç¨‹ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find a ticket with id %1"
+msgstr "找ä¸åˆ°ç·¨è™Ÿ %1 的申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find group %1."
+msgstr "找ä¸åˆ°ç¾¤çµ„ %1。"
+
+#: lib/RT/Queue_Overlay.pm:741 lib/RT/Ticket_Overlay.pm:1423
+msgid "Could not find or create that user"
+msgstr "找ä¸åˆ°æˆ–無法新增該å使用者"
+
+#: lib/RT/Queue_Overlay.pm:802 lib/RT/Ticket_Overlay.pm:1504
+msgid "Could not find that principal"
+msgstr "找ä¸åˆ°è©²å–®ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not find user %1."
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… %1。"
+
+#: html/Admin/CustomFields/Objects.html:69
+msgid "Could not load CustomField %1"
+msgstr "ç„¡æ³•è¼‰å…¥æ¬„ä½ %1"
+
+#: html/Admin/Groups/Members.html:112 html/User/Groups/Members.html:111 html/User/Groups/Modify.html:103
+msgid "Could not load group"
+msgstr "無法載入群組"
+
+#: lib/RT/SavedSearch.pm:119
+#. ($privacy)
+msgid "Could not load object for %1"
+msgstr "無法為 %1 載入物件"
+
+#: lib/RT/SavedSearch.pm:197
+msgid "Could not load search attribute"
+msgstr "無法載入查詢屬性"
+
+#: lib/RT/Queue_Overlay.pm:761
+#. ($args{'Type'})
+msgid "Could not make that principal a %1 for this queue"
+msgstr "無法將該單ä½è¨­ç‚ºæ­¤è¡¨å–®çš„ %1。"
+
+#: lib/RT/Ticket_Overlay.pm:1444
+#. ($self->loc($args{'Type'}))
+msgid "Could not make that principal a %1 for this ticket"
+msgstr "無法將該單ä½è¨­ç‚ºæ­¤ç”³è«‹å–®çš„ %1。"
+
+#: lib/RT/Queue_Overlay.pm:860
+#. ($args{'Type'})
+msgid "Could not remove that principal as a %1 for this queue"
+msgstr "ç„¡æ³•å°‡å–®ä½ %1 從表單移除。"
+
+#: NOT FOUND IN SOURCE
+msgid "Could not remove that principal as a %1 for this ticket"
+msgstr "ç„¡æ³•å°‡å–®ä½ %1 從申請單移除。"
+
+#: lib/RT/User_Overlay.pm:191
+msgid "Could not set user info"
+msgstr "無法設定使用者資訊"
+
+#: lib/RT/Transaction_Overlay.pm:159
+msgid "Couldn't add attachment"
+msgstr "無法新增附件"
+
+#: lib/RT/Group_Overlay.pm:1003
+msgid "Couldn't add member to group"
+msgstr "無法新增æˆå“¡è‡³ç¾¤çµ„"
+
+#: lib/RT/Record.pm:1719 lib/RT/Record.pm:1771
+#. ($Msg)
+msgid "Couldn't create a transaction: %1"
+msgstr "無法新增更動報告"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't figure out what to do from gpg's reply\\n"
+msgstr "無法從 gpg 回函辨識出該採å–的行動\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find group\\n"
+msgstr "找ä¸åˆ°ç¾¤çµ„\\n"
+
+#: lib/RT/Record.pm:953
+msgid "Couldn't find row"
+msgstr "找ä¸åˆ°æ­¤åˆ—資料"
+
+#: lib/RT/Group_Overlay.pm:977
+msgid "Couldn't find that principal"
+msgstr "找ä¸åˆ°è©²å–®ä½"
+
+#: lib/RT/CustomField_Overlay.pm:409
+msgid "Couldn't find that value"
+msgstr "找ä¸åˆ°è©²å€¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find that watcher"
+msgstr "找ä¸åˆ°è©²è¦–察員"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't find user\\n"
+msgstr "找ä¸åˆ°ä½¿ç”¨è€…\\n"
+
+#: lib/RT/CurrentUser.pm:145
+#. ($self->Id)
+msgid "Couldn't load %1 from the users database.\\n"
+msgstr "無法從使用者資料庫載入 %1。\\n"
+
+#: html/Admin/CustomFields/UserRights.html:149
+#. ($id)
+msgid "Couldn't load Class %1"
+msgstr "無法載入類別 %1"
+
+#: html/Admin/CustomFields/GroupRights.html:107
+#. ($id)
+msgid "Couldn't load CustomField %1"
+msgstr "ç„¡æ³•è¼‰å…¥è‡ªè¨‚æ¬„ä½ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load KeywordSelects."
+msgstr "無法載入 KeywordSelects。"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load RT config file '%1' %2"
+msgstr "無法載入 RT 設定檔 '%1' %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load Scrips."
+msgstr "無法載入手續。"
+
+#: lib/RT/Ticket_Overlay.pm:2016
+#. ($self->Id)
+msgid "Couldn't load copy of ticket #%1."
+msgstr ""
+
+#: html/Admin/Groups/GroupRights.html:109 html/Admin/Groups/UserRights.html:96
+#. ($id)
+msgid "Couldn't load group %1"
+msgstr "無法載入手續 %1"
+
+#: lib/RT/Link_Overlay.pm:202 lib/RT/Link_Overlay.pm:211 lib/RT/Link_Overlay.pm:238
+msgid "Couldn't load link"
+msgstr "無法載入éˆçµã€‚"
+
+#: html/Admin/Elements/ObjectCustomFields:83 html/Admin/Queues/CustomFields.html:59 html/Admin/Users/CustomFields.html:59
+#. ($id)
+msgid "Couldn't load object %1"
+msgstr "無法載入物件 %1"
+
+#: html/Admin/Queues/People.html:142
+#. ($id)
+msgid "Couldn't load queue"
+msgstr "無法載入表單"
+
+#: html/Admin/Queues/GroupRights.html:122 html/Admin/Queues/UserRights.html:93
+#. ($id)
+msgid "Couldn't load queue %1"
+msgstr "無法載入表單 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load scrip"
+msgstr "無法載入手續"
+
+#: html/Admin/Elements/EditScrip:126 html/Admin/Elements/EditScrip:167
+#. ($id)
+msgid "Couldn't load scrip #%1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load template"
+msgstr "無法載入範本"
+
+#: NOT FOUND IN SOURCE
+msgid "Couldn't load that user (%1)"
+msgstr "無法載入該å使用者(%1)"
+
+#: html/SelfService/Display.html:158 lib/RT/Action/CreateTickets.pm:680
+#. ($id)
+msgid "Couldn't load ticket '%1'"
+msgstr "無法載入申請單 '%1'"
+
+#: lib/RT/Ticket_Overlay.pm:2643
+#. ($args{'URI'})
+msgid "Couldn't resolve '%1' into a URI."
+msgstr ""
+
+#: html/Admin/Users/Modify.html:173 html/User/Prefs.html:153
+msgid "Country"
+msgstr "國家"
+
+#: html/Admin/Elements/CreateUserCalled:47 html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditScrip:133 html/Admin/Queues/Template.html:66 html/Elements/QuickCreate:65 html/Ticket/Create.html:168 html/Ticket/Create.html:235
+msgid "Create"
+msgstr "新增"
+
+#: NOT FOUND IN SOURCE
+msgid "Create Subgroup:"
+msgstr "新增å­ç¾¤çµ„:"
+
+#: etc/initialdata:135
+msgid "Create Tickets"
+msgstr "新增申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Create User:"
+msgstr "新增æˆå“¡ï¼š"
+
+#: html/Admin/CustomFields/Modify.html:150 html/Admin/Elements/EditCustomField:96
+msgid "Create a CustomField"
+msgstr "新增自訂欄ä½"
+
+#: html/Admin/Queues/CustomField.html:69
+#. ($QueueObj->Name())
+msgid "Create a CustomField for queue %1"
+msgstr "為 %1 表單新增自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a CustomField which applies to all queues"
+msgstr "為 %1 表單新增自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new Custom Field"
+msgstr "新增自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global Scrip"
+msgstr "新增全域手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new global scrip"
+msgstr "新增全域手續"
+
+#: html/Admin/Groups/Modify.html:125 html/Admin/Groups/Modify.html:99
+msgid "Create a new group"
+msgstr "新增群組"
+
+#: html/User/Groups/Modify.html:113 html/User/Groups/Modify.html:88
+msgid "Create a new personal group"
+msgstr "新增代ç†äººç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new queue"
+msgstr "新增表單"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new scrip"
+msgstr "新增手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new template"
+msgstr "新增範本"
+
+#: html/Ticket/Create.html:47 html/Ticket/Create.html:51 html/Ticket/Create.html:60
+msgid "Create a new ticket"
+msgstr "新增申請單"
+
+#: html/Admin/Users/Modify.html:252 html/Admin/Users/Modify.html:314
+msgid "Create a new user"
+msgstr "新增使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a new workflow"
+msgstr "新增æµç¨‹"
+
+#: html/Admin/Queues/Modify.html:125
+msgid "Create a queue"
+msgstr "新增表單"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a queue called"
+msgstr "新增表單å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a request"
+msgstr "æ出申請"
+
+#: html/Admin/Queues/Scrip.html:89
+#. ($QueueObj->Name)
+msgid "Create a scrip for queue %1"
+msgstr "為 %1 表單新增手續"
+
+#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:86
+msgid "Create a template"
+msgstr "新增範本"
+
+#: html/SelfService/Create.html:46 html/SelfService/CreateTicketInQueue.html:46
+msgid "Create a ticket"
+msgstr "æ出申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Create a workflow"
+msgstr "新增æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1 / %2 / %3 "
+msgstr "新增失敗:%1 / %2 / %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Create failed: %1/%2/%3"
+msgstr "新增失敗:%1/%2/%3"
+
+#: NOT FOUND IN SOURCE
+msgid "Create new item"
+msgstr "建立新項目"
+
+#: etc/initialdata:137
+msgid "Create new tickets based on this scrip's template"
+msgstr "ä¾æ“šæ­¤é …手續內的模版,新增申請單"
+
+#: html/SelfService/Create.html:105
+msgid "Create ticket"
+msgstr "新增申請單"
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "Create tickets in this queue"
+msgstr "在此表單中新增申請單"
+
+#: lib/RT/CustomField_Overlay.pm:106
+msgid "Create, delete and modify custom fields"
+msgstr "新增ã€åˆªé™¤åŠæ›´æ”¹è‡ªè¨‚欄ä½"
+
+#: lib/RT/Queue_Overlay.pm:92
+msgid "Create, delete and modify queues"
+msgstr "新增ã€åˆªé™¤åŠæ›´æ”¹è¡¨å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Create, delete and modify the members of any user's personal groups"
+msgstr "新增ã€åˆªé™¤åŠæ›´æ”¹ä»»ä½•ä½¿ç”¨è€…的代ç†äººç¾¤çµ„"
+
+#: lib/RT/System.pm:80
+msgid "Create, delete and modify the members of personal groups"
+msgstr "新增ã€åˆªé™¤åŠæ›´æ”¹ä»£ç†äººç¾¤çµ„"
+
+#: lib/RT/System.pm:81
+msgid "Create, delete and modify users"
+msgstr "新增ã€åˆªé™¤åŠæ›´æ”¹ä½¿ç”¨è€…"
+
+#: lib/RT/System.pm:87
+msgid "CreateSavedSearch"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:109
+msgid "CreateTicket"
+msgstr "新增申請單"
+
+#: html/Elements/SelectDateType:47 html/Ticket/Elements/ShowDates:48 lib/RT/Ticket_Overlay.pm:1169
+msgid "Created"
+msgstr "新增日"
+
+#: html/Admin/CustomFields/Modify.html:163 html/Admin/Elements/EditCustomField:117
+#. ($CustomFieldObj->Name())
+msgid "Created CustomField %1"
+msgstr "è‡ªè¨‚æ¬„ä½ %1 新增æˆåŠŸ"
+
+#: html/Tools/Reports/Elements/Tabs:63
+msgid "Created in a date range"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created template %1"
+msgstr "範本 %1 新增æˆåŠŸ"
+
+#: html/Tools/Reports/CreatedByDates.html:52
+msgid "Created tickets in period, grouped by status"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Created workflow %1"
+msgstr "æµç¨‹ %1 新增æˆåŠŸ"
+
+#: html/Search/Elements/PickBasics:102
+msgid "Creator"
+msgstr "建立者"
+
+#: NOT FOUND IN SOURCE
+msgid "Currency"
+msgstr "幣別"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Approval Info"
+msgstr "截至目å‰ç°½æ ¸è³‡è¨Š"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Custom Fields"
+msgstr "ç¾æœ‰è‡ªè¨‚欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Groups:"
+msgstr "ç¾æœ‰ç¾¤çµ„列表:"
+
+#: html/Elements/EditLinks:49
+msgid "Current Links"
+msgstr "ç¾æœ‰é—œä¿‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Rights:"
+msgstr "ç¾æœ‰æ¬Šé™ï¼š"
+
+#: html/Admin/Elements/EditScrips:51
+msgid "Current Scrips"
+msgstr "ç¾æœ‰æ‰‹çºŒ"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Status"
+msgstr "ç›®å‰ç‹€æ…‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Templates"
+msgstr "ç¾æœ‰ç¯„本"
+
+#: NOT FOUND IN SOURCE
+msgid "Current Watchers"
+msgstr "ç¾æœ‰è¦–察員"
+
+#: html/Admin/Groups/Members.html:60 html/User/Groups/Members.html:63
+msgid "Current members"
+msgstr "ç¾æœ‰æˆå“¡"
+
+#: html/Admin/Elements/SelectRights:51
+msgid "Current rights"
+msgstr "ç¾æœ‰æ¬Šé™"
+
+#: html/Search/Elements/EditQuery:47
+msgid "Current search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Current search criteria"
+msgstr "ç¾æœ‰æŸ¥è©¢æ¢ä»¶"
+
+#: html/Admin/Queues/People.html:62 html/Ticket/Elements/EditPeople:66
+msgid "Current watchers"
+msgstr "ç¾æœ‰è¦–察員"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Field #%1"
+msgstr "è‡ªè¨‚æ¬„ä½ #%1"
+
+#: html/Admin/Elements/SystemTabs:61 html/Admin/Elements/Tabs:62 html/Admin/Global/index.html:71 html/Admin/Users/Modify.html:205 html/Admin/index.html:77 html/Ticket/Elements/ShowSummary:56
+msgid "Custom Fields"
+msgstr "自訂欄ä½"
+
+#: html/Admin/CustomFields/index.html:60
+#. ($lookup)
+msgid "Custom Fields for %1"
+msgstr "%1 的自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom Fields which apply to all queues"
+msgstr "é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„自訂欄ä½"
+
+#: html/Admin/Elements/EditScrip:107
+msgid "Custom action cleanup code"
+msgstr "動作後執行程å¼"
+
+#: html/Admin/Elements/EditScrip:103
+msgid "Custom action preparation code"
+msgstr "動作å‰åŸ·è¡Œç¨‹å¼"
+
+#: html/Admin/Elements/EditScrip:99
+msgid "Custom condition"
+msgstr "自訂æ¢ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 %2 %3"
+msgstr "è‡ªè¨‚æ¬„ä½ %1 %2 %3"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field %1 does not apply to this object"
+msgstr "è‡ªè¨‚æ¬„ä½ %1 ä¸é©ç”¨æ–¼æ­¤ç‰©ä»¶"
+
+#: lib/RT/Tickets_Overlay.pm:2424
+#. ($CF->Name)
+msgid "Custom field %1 has a value."
+msgstr "è‡ªè¨‚æ¬„ä½ %1 已有值"
+
+#: lib/RT/Tickets_Overlay.pm:2420
+#. ($CF->Name)
+msgid "Custom field %1 has no value."
+msgstr "è‡ªè¨‚æ¬„ä½ %1 沒有值"
+
+#: lib/RT/Record.pm:1592 lib/RT/Record.pm:1754
+#. ($args{'Field'})
+msgid "Custom field %1 not found"
+msgstr "找ä¸åˆ°è‡ªè¨‚æ¬„ä½ %1"
+
+#: lib/RT/Report/Tickets.pm:118 lib/RT/Report/Tickets.pm:121
+#. ($cf)
+#. ($obj->Name)
+msgid "Custom field '%1'"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field deleted"
+msgstr "自訂欄ä½å·²åˆªé™¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field not found"
+msgstr "找ä¸åˆ°è‡ªè¨‚欄ä½"
+
+#: lib/RT/CustomField_Overlay.pm:1157
+#. ($args{'Content'}, $self->Name)
+msgid "Custom field value %1 could not be found for custom field %2"
+msgstr "ç„¡æ³•å¾žè‡ªè¨‚æ¬„ä½ %2 中找到 %1 這個欄ä½å€¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Custom field value changed from %1 to %2"
+msgstr "自訂欄ä½å€¼å¾ž %1 改為 %2"
+
+#: lib/RT/CustomField_Overlay.pm:419
+msgid "Custom field value could not be deleted"
+msgstr "無法刪除自訂欄ä½å€¼"
+
+#: lib/RT/CustomField_Overlay.pm:1169
+msgid "Custom field value could not be found"
+msgstr "找ä¸åˆ°è‡ªè¨‚欄ä½å€¼"
+
+#: lib/RT/CustomField_Overlay.pm:1171 lib/RT/CustomField_Overlay.pm:417
+msgid "Custom field value deleted"
+msgstr "自訂欄ä½å€¼åˆªé™¤æˆåŠŸ"
+
+#: html/Elements/SelectGroups:51 html/Elements/SelectUsers:51 lib/RT/Transaction_Overlay.pm:638
+msgid "CustomField"
+msgstr "自訂欄ä½"
+
+#: html/Prefs/MyRT.html:78 html/Prefs/Quicksearch.html:70 html/Prefs/Search.html:75
+msgid "Customize"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Data error"
+msgstr "資料錯誤"
+
+#: NOT FOUND IN SOURCE
+msgid "DatabaseBindRemote"
+msgstr "容許外部連線"
+
+#: NOT FOUND IN SOURCE
+msgid "DatabaseName"
+msgstr "MySQL資料庫"
+
+#: NOT FOUND IN SOURCE
+msgid "Date of Departure"
+msgstr "出發日期"
+
+#: html/SelfService/Display.html:61 html/Ticket/Create.html:203 html/Ticket/Elements/ShowSummary:83 html/Ticket/Elements/Tabs:116 html/Ticket/ModifyAll.html:65
+msgid "Dates"
+msgstr "日期"
+
+#: NOT FOUND IN SOURCE
+msgid "Dec"
+msgstr "å二月"
+
+#: lib/RT/Date.pm:452
+msgid "Dec."
+msgstr "12"
+
+#: NOT FOUND IN SOURCE
+msgid "December"
+msgstr "å二月"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Approval"
+msgstr "é è¨­ç°½æ ¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Autoresponse Template"
+msgstr "é è¨­è‡ªå‹•å›žæ‡‰ç¯„本"
+
+#: etc/initialdata:222
+msgid "Default Autoresponse template"
+msgstr "é è¨­è‡ªå‹•å›žæ‡‰ç¯„本"
+
+#: html/Tools/Offline.html:61
+msgid "Default Queue"
+msgstr "é è¨­è¡¨å–®"
+
+#: html/Tools/Offline.html:70
+msgid "Default Requestor"
+msgstr "é è¨­ç”³è«‹äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Default Value"
+msgstr "é è¨­å€¼"
+
+#: etc/initialdata:296
+msgid "Default admin comment template"
+msgstr "é è¨­ç®¡ç†å“¡è©•è«–範本"
+
+#: etc/initialdata:275
+msgid "Default admin correspondence template"
+msgstr "é è¨­ç®¡ç†å“¡å›žè¦†ç¯„本"
+
+#: etc/initialdata:287
+msgid "Default correspondence template"
+msgstr "é è¨­å›žè¦†ç¯„本"
+
+#: etc/initialdata:253
+msgid "Default transaction template"
+msgstr "é è¨­æ›´å‹•ç¯„本"
+
+#: NOT FOUND IN SOURCE
+msgid "Default: %1/%2 changed from %3 to %4"
+msgstr "é è¨­ï¼š%1/%2 已自 %3 改為 %4"
+
+#: NOT FOUND IN SOURCE
+msgid "DefaultApproval"
+msgstr "é è¨­ç°½æ ¸"
+
+#: html/User/Delegation.html:46 html/User/Delegation.html:49
+msgid "Delegate rights"
+msgstr "代ç†äººæ¬Šé™"
+
+#: lib/RT/System.pm:84
+msgid "Delegate specific rights which have been granted to you."
+msgstr "å°‡æ“有的權é™å§”託他人代ç†"
+
+#: lib/RT/System.pm:84
+msgid "DelegateRights"
+msgstr "設定代ç†äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Approval"
+msgstr "代ç†ç°½æ ¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Queue"
+msgstr "代ç†è¡¨å–®å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Queue:"
+msgstr "代ç†è¡¨å–®ï¼š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegated Type"
+msgstr "代ç†è¡¨å–®ç¨®é¡ž"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates"
+msgstr "代ç†äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Enabled Status"
+msgstr "代ç†å•Ÿå‹•ç‹€æ…‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Info"
+msgstr "代ç†äººè³‡è¨Š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Period"
+msgstr "代ç†æœŸé–“"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Permission Setting"
+msgstr "代ç†æ¬Šé™è¨­å®š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Permission:"
+msgstr "代ç†æ¬Šé™ï¼š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Setting"
+msgstr "代ç†äººè¨­å®š"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegates Status"
+msgstr "代ç†ç‹€æ…‹"
+
+#: html/User/Elements/Tabs:59
+msgid "Delegation"
+msgstr "代ç†äººæ¬Šé™"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegation Groups"
+msgstr "代ç†äººç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "Delegation Rights"
+msgstr "代ç†äººæ¬Šé™"
+
+#: html/Admin/Elements/EditScrips:75 html/Search/Elements/EditFormat:103 html/Search/Elements/EditQuery:57 html/Search/Elements/EditSearches:63 html/Widgets/SelectionBox:204
+msgid "Delete"
+msgstr "刪除"
+
+#: html/Admin/Elements/EditTemplates:79
+msgid "Delete Template"
+msgstr "刪除範本"
+
+#: lib/RT/SavedSearch.pm:220
+#. ($msg)
+msgid "Delete failed: %1"
+msgstr ""
+
+#: html/Admin/Elements/EditScrips:74
+msgid "Delete selected scrips"
+msgstr "刪除指定的手續"
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "Delete tickets"
+msgstr "刪除申請單"
+
+#: html/Search/Bulk.html:159
+msgid "Delete values"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:114
+msgid "DeleteTicket"
+msgstr "刪除申請單"
+
+#: lib/RT/SavedSearch.pm:218
+msgid "Deleted search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object could break referential integrity"
+msgstr "刪除此物件å¯èƒ½ç ´å£žåƒè€ƒå®Œæ•´æ€§"
+
+#: lib/RT/Queue_Overlay.pm:394
+msgid "Deleting this object would break referential integrity"
+msgstr "刪除此物件å¯èƒ½ç ´å£žåƒè€ƒå®Œæ•´æ€§"
+
+#: lib/RT/User_Overlay.pm:512
+msgid "Deleting this object would violate referential integrity"
+msgstr "刪除此物件會é•ååƒè€ƒå®Œæ•´æ€§"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity."
+msgstr "刪除此物件會é•ååƒè€ƒå®Œæ•´æ€§"
+
+#: NOT FOUND IN SOURCE
+msgid "Deleting this object would violate referential integrity. That's bad."
+msgstr "刪除此物件會é•ååƒè€ƒå®Œæ•´æ€§"
+
+#: html/Approvals/Elements/Approve:73
+msgid "Deny"
+msgstr "é§å›ž"
+
+#: NOT FOUND IN SOURCE
+msgid "Department"
+msgstr "部門"
+
+#: NOT FOUND IN SOURCE
+msgid "Department ID"
+msgstr "部門代碼"
+
+#: NOT FOUND IN SOURCE
+msgid "Department Name"
+msgstr "部門å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Department's"
+msgstr "部門之"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure Details"
+msgstr "差旅明細"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure From"
+msgstr "差旅起始日"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure Request"
+msgstr "è«‹å‡å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Departure Until"
+msgstr "差旅截止日"
+
+#: html/Elements/EditLinks:140 html/Elements/EditLinks:66 html/Elements/ShowLinks:58 html/Ticket/Create.html:221 html/Ticket/Elements/BulkLinks:56 html/Ticket/Elements/ShowDependencies:53
+msgid "Depended on by"
+msgstr "å¯æŽ¥çºŒè™•ç†çš„申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Dependencies: \\n"
+msgstr "附屬性:\\n"
+
+#: lib/RT/Transaction_Overlay.pm:718
+#. ($value)
+msgid "Dependency by %1 added"
+msgstr "已加入å¯æŽ¥çºŒè™•ç†çš„申請單 %1"
+
+#: lib/RT/Transaction_Overlay.pm:758
+#. ($value)
+msgid "Dependency by %1 deleted"
+msgstr "已移除å¯æŽ¥çºŒè™•ç†çš„申請單 %1"
+
+#: lib/RT/Transaction_Overlay.pm:715
+#. ($value)
+msgid "Dependency on %1 added"
+msgstr "已加入需先處ç†çš„申請單 %1"
+
+#: lib/RT/Transaction_Overlay.pm:755
+#. ($value)
+msgid "Dependency on %1 deleted"
+msgstr "已移除需先處ç†çš„申請單 %1"
+
+#: html/Elements/EditLinks:136 html/Elements/EditLinks:57 html/Elements/SelectLinkType:48 html/Elements/ShowLinks:48 html/Ticket/Create.html:220 html/Ticket/Elements/BulkLinks:52 html/Ticket/Elements/ShowDependencies:46
+msgid "Depends on"
+msgstr "需先處ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "DependsOn"
+msgstr "需先處ç†"
+
+#: html/Search/Elements/DisplayOptions:86
+msgid "Desc"
+msgstr ""
+
+#: html/Elements/SelectSortOrder:56
+msgid "Descending"
+msgstr "éžæ¸›"
+
+#: html/SelfService/Create.html:100 html/Ticket/Create.html:152
+msgid "Describe the issue below"
+msgstr "在以下欄ä½æ述主題"
+
+#: html/Admin/CustomFields/Modify.html:61 html/Admin/Elements/AddCustomFieldValue:57 html/Admin/Elements/EditCustomField:60 html/Admin/Elements/EditCustomFieldValues:56 html/Admin/Elements/EditScrip:55 html/Admin/Elements/ModifyTemplate:57 html/Admin/Groups/Modify.html:71 html/Admin/Queues/Modify.html:69 html/Search/Elements/EditSearches:56 html/User/Groups/Modify.html:70
+msgid "Description"
+msgstr "æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Description of Responsibility"
+msgstr "經辦業務說明"
+
+#: NOT FOUND IN SOURCE
+msgid "Description:"
+msgstr "æ述:"
+
+#: NOT FOUND IN SOURCE
+msgid "Details"
+msgstr "細節"
+
+#: NOT FOUND IN SOURCE
+msgid "Direct"
+msgstr "直接"
+
+#: NOT FOUND IN SOURCE
+msgid "Disability"
+msgstr "殘障身分"
+
+#: NOT FOUND IN SOURCE
+msgid "Disability Type"
+msgstr "殘障類別"
+
+#: NOT FOUND IN SOURCE
+msgid "Disabled"
+msgstr "åœç”¨"
+
+#: html/Search/Elements/EditFormat:71 html/Ticket/Elements/Tabs:108
+msgid "Display"
+msgstr "顯示內容"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "Display Access Control List"
+msgstr "顯示權é™æŽ§åˆ¶æ¸…å–®"
+
+#: html/Search/Elements/DisplayOptions:46
+msgid "Display Columns"
+msgstr "顯示欄ä½"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "Display Scrip templates for this queue"
+msgstr "顯示此表單的範本"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "Display Scrips for this queue"
+msgstr "顯示此表單的手續"
+
+#: html/Ticket/Elements/ShowHistory:59
+msgid "Display mode"
+msgstr "顯示模å¼"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "Display saved searches for this group"
+msgstr "顯示此群組已儲存的查詢"
+
+#: NOT FOUND IN SOURCE
+msgid "Display ticket #%1"
+msgstr "顯示第%1號申請單"
+
+#: html/Elements/Footer:61
+msgid "Distributed under version 2 <a href=\"http://www.gnu.org/copyleft/gpl.html\"> of the GNU GPL.</a>"
+msgstr "ä¾ <a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU 通用公共授權</a> 第二版散布。"
+
+#: lib/RT/System.pm:75
+msgid "Do anything and everything"
+msgstr "å…許一切æ“作"
+
+#: html/Elements/Refresh:51
+msgid "Don't refresh this page."
+msgstr "ä¸æ›´æ–°æ­¤é é¢ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Don't show search results"
+msgstr "ä¸é¡¯ç¤ºæŸ¥è©¢çµæžœ"
+
+#: NOT FOUND IN SOURCE
+msgid "Done"
+msgstr "完æˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Down"
+msgstr "下一é "
+
+#: html/Ticket/Elements/ShowTransactionAttachments:82
+msgid "Download"
+msgstr "下載"
+
+#: html/Admin/Groups/index.html:61 html/Admin/Users/index.html:64
+msgid "Download as a tab-delimited file"
+msgstr "下載以 Tab 分隔的檔案"
+
+#: NOT FOUND IN SOURCE
+msgid "Dr."
+msgstr "åšå£«"
+
+#: html/Elements/SelectDateType:53 html/Ticket/Create.html:209 html/Ticket/Elements/EditDates:66 html/Ticket/Elements/Reminders:133 html/Ticket/Elements/ShowDates:64 lib/RT/Ticket_Overlay.pm:1173
+msgid "Due"
+msgstr "到期日"
+
+#: NOT FOUND IN SOURCE
+msgid "Due Date"
+msgstr "截止日"
+
+#: NOT FOUND IN SOURCE
+msgid "Due date '%1' could not be parsed"
+msgstr "無法解讀日期 '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
+msgstr "無法載入申請單 '%1':%2.\\n"
+
+#: html/Elements/Quicksearch:48 html/Elements/ShowSearch:49 html/index.html:107
+msgid "Edit"
+msgstr "編輯"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Conditions"
+msgstr "編輯å‰ç½®æ¢ä»¶"
+
+#: html/Search/Bulk.html:149
+msgid "Edit Custom Fields"
+msgstr ""
+
+#: html/Admin/Elements/ObjectCustomFields:92 html/Admin/Queues/CustomFields.html:64 html/Admin/Users/CustomFields.html:64
+#. ($Object->Name)
+msgid "Edit Custom Fields for %1"
+msgstr "編輯 %1 的自訂欄ä½"
+
+#: html/Admin/Global/CustomFields/Groups.html:54
+msgid "Edit Custom Fields for all groups"
+msgstr ""
+
+#: html/Admin/Global/CustomFields/Users.html:54
+msgid "Edit Custom Fields for all users"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Custom Fields for queue %1"
+msgstr "編輯表單 %1 的自訂欄ä½"
+
+#: html/Admin/Global/CustomFields/Queue-Tickets.html:54 html/Admin/Global/CustomFields/Queue-Transactions.html:54
+msgid "Edit Custom Fields for tickets in all queues"
+msgstr ""
+
+#: html/Search/Bulk.html:188 html/Ticket/ModifyLinks.html:57
+msgid "Edit Links"
+msgstr "編輯申請單關係"
+
+#: html/Search/Edit.html:68
+msgid "Edit Query"
+msgstr "編輯查詢"
+
+#: html/Ticket/Elements/Tabs:214
+msgid "Edit Search"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Subgroups"
+msgstr "新增/維護å­ç¾¤çµ„"
+
+#: html/Admin/Queues/Templates.html:63
+#. ($QueueObj->Name)
+msgid "Edit Templates for queue %1"
+msgstr "編輯表單 %1 的範本"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit Workflows for queue %1"
+msgstr "編輯表單 %1 çš„æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit keywords"
+msgstr "編輯關éµå­—"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "Edit saved searches for this group"
+msgstr "編輯此群組已儲存的查詢"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit scrips"
+msgstr "編輯手續"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:60 html/Admin/Global/index.html:67
+msgid "Edit system templates"
+msgstr "編輯全域範本"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit system workflows"
+msgstr "編輯全域æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit templates for %1"
+msgstr "編輯 %1 的範本"
+
+#: NOT FOUND IN SOURCE
+msgid "Edit workflows for %1"
+msgstr "編輯 %1 çš„æµç¨‹"
+
+#: lib/RT/Group_Overlay.pm:167
+msgid "EditSavedSearches"
+msgstr "編輯已儲存的查詢"
+
+#: html/Admin/Queues/Modify.html:140
+#. ($QueueObj->Name)
+msgid "Editing Configuration for queue %1"
+msgstr "編輯表單 %1 的設定"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing Configuration for user %1"
+msgstr "編輯使用者 %1 的設定"
+
+#: html/Admin/CustomFields/Modify.html:167 html/Admin/Elements/EditCustomField:120
+#. ($CustomFieldObj->Name())
+msgid "Editing CustomField %1"
+msgstr "ç·¨è¼¯è‡ªè¨‚æ¬„ä½ %1"
+
+#: html/Admin/Groups/Members.html:53
+#. ($Group->Name)
+msgid "Editing membership for group %1"
+msgstr "編輯群組 %1 çš„æˆå“¡è³‡è¨Š"
+
+#: html/User/Groups/Members.html:150
+#. ($Group->Name)
+msgid "Editing membership for personal group %1"
+msgstr "編輯代ç†äººç¾¤çµ„ %1 çš„æˆå“¡è³‡è¨Š"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing template %1"
+msgstr "編輯範本 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Editing workflow %1"
+msgstr "編輯æµç¨‹ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Education"
+msgstr "最高學歷"
+
+#: NOT FOUND IN SOURCE
+msgid "EffectiveId"
+msgstr "有效編號"
+
+#: lib/RT/Record.pm:1295 lib/RT/Record.pm:1372 lib/RT/Ticket_Overlay.pm:2518 lib/RT/Ticket_Overlay.pm:2608
+msgid "Either base or target must be specified"
+msgstr "需è¦æŒ‡å®šèµ·å§‹ç”³è«‹å–®æˆ–目的申請單"
+
+#: html/Admin/Users/Modify.html:74 html/Ticket/Elements/AddWatchers:77 html/User/Prefs.html:65
+msgid "Email"
+msgstr "é›»å­éƒµä»¶ä¿¡ç®±"
+
+#: NOT FOUND IN SOURCE
+msgid "Email Address"
+msgstr "é›»å­éƒµä»¶ä¿¡ç®±"
+
+#: lib/RT/User_Overlay.pm:235
+msgid "Email address in use"
+msgstr "此電å­éƒµä»¶ä¿¡ç®±å·²è¢«ä½¿ç”¨"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailAddress"
+msgstr "é›»å­éƒµä»¶ä¿¡ç®±ä½å€"
+
+#: NOT FOUND IN SOURCE
+msgid "EmailEncoding"
+msgstr "é›»å­éƒµä»¶æ–‡å­—編碼方å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Embark Date"
+msgstr "外ç±å“¡å·¥å…¥å¢ƒæ—¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Embarked Date"
+msgstr "抵é”日期"
+
+#: NOT FOUND IN SOURCE
+msgid "Embarked Location"
+msgstr "抵é”地點"
+
+#: NOT FOUND IN SOURCE
+msgid "Enable Delegates"
+msgstr "代ç†å•Ÿå‹•"
+
+#: html/Admin/CustomFields/Modify.html:98 html/Admin/Elements/EditCustomField:72
+msgid "Enabled (Unchecking this box disables this custom field)"
+msgstr "啟用(å–消勾é¸å°‡åœç”¨æ­¤è‡ªè¨‚欄ä½)"
+
+#: html/Admin/Groups/Modify.html:84 html/User/Groups/Modify.html:74
+msgid "Enabled (Unchecking this box disables this group)"
+msgstr "啟用(å–消勾é¸å°‡åœç”¨æ­¤ç¾¤çµ„)"
+
+#: html/Admin/Queues/Modify.html:105
+msgid "Enabled (Unchecking this box disables this queue)"
+msgstr "啟用(å–消勾é¸å°‡åœç”¨æ­¤è¡¨å–®)"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Custom Fields"
+msgstr "已啟用的自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Date"
+msgstr "啟用日期"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Date:"
+msgstr "啟動日期:"
+
+#: html/Admin/Queues/index.html:78
+msgid "Enabled Queues"
+msgstr "已啟用的表單"
+
+#: NOT FOUND IN SOURCE
+msgid "Enabled Status"
+msgstr "啟用狀態"
+
+#: html/Admin/Elements/EditCustomField:136 html/Admin/Groups/Modify.html:150 html/Admin/Users/Modify.html:350 html/User/Groups/Modify.html:138
+#. (loc_fuzzy($msg))
+msgid "Enabled status %1"
+msgstr "啟用狀態 %1"
+
+#: html/Admin/CustomFields/Modify.html:185 html/Admin/Queues/Modify.html:162
+#. (loc_fuzzy($msg))
+msgid "Enabled status: %1"
+msgstr "啟用狀態: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "End of Trial"
+msgstr "試用期滿日"
+
+#: NOT FOUND IN SOURCE
+msgid "English Name"
+msgstr "英文姓å"
+
+#: lib/RT/CustomField_Overlay.pm:64
+msgid "Enter multiple values"
+msgstr "éµå…¥å¤šé‡é …ç›®"
+
+#: html/Elements/EditLinks:126
+msgid "Enter objects or URIs to link objects to. Separate multiple entries with spaces."
+msgstr "éµå…¥æ¬²å°‡ç‰©ä»¶é€£çµè‡³çš„物件或 URI。項目之間請以空白隔開。"
+
+#: NOT FOUND IN SOURCE
+msgid "Enter one or more conditions below to search for users"
+msgstr "éµå…¥ä¸‹åˆ—單一或複å¼æ¢ä»¶ï¼ŒæŸ¥è©¢ç”¨æˆ¶è³‡æ–™"
+
+#: lib/RT/CustomField_Overlay.pm:65
+msgid "Enter one value"
+msgstr "éµå…¥å–®ä¸€é …ç›®"
+
+#: html/Elements/EditLinks:123
+msgid "Enter queues or URIs to link queues to. Separate multiple entries with spaces."
+msgstr "éµå…¥æ¬²å°‡è¡¨å–®é€£çµè‡³çš„物件或 URI。項目之間請以空白隔開。"
+
+#: html/Elements/EditLinks:119 html/Search/Bulk.html:189
+msgid "Enter tickets or URIs to link tickets to. Separate multiple entries with spaces."
+msgstr "éµå…¥ç”³è«‹å–®å¯éˆçµåˆ°çš„申請單編號或網å€ã€‚項目之間請以空白隔開。"
+
+#: lib/RT/CustomField_Overlay.pm:66
+msgid "Enter up to %1 values"
+msgstr "éµå…¥æœ€å¤š %1 個項目"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryBoolean"
+msgstr "是éžå¡«è¡¨"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryDate"
+msgstr "日期填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryExternal"
+msgstr "系統填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryFreeform"
+msgstr "輸入填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryMultiple"
+msgstr "多é¸å¡«è¡¨"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryNumber"
+msgstr "數值填表"
+
+#: NOT FOUND IN SOURCE
+msgid "EntrySelect"
+msgstr "å–®é¸å¡«è¡¨"
+
+#: NOT FOUND IN SOURCE
+msgid "EntryTime"
+msgstr "時間填表"
+
+#: html/Elements/Login:76 html/SelfService/Error.html:46 html/SelfService/Error.html:47
+msgid "Error"
+msgstr "錯誤"
+
+#: NOT FOUND IN SOURCE
+msgid "Error adding watcher"
+msgstr "新增視察員失敗"
+
+#: lib/RT/Queue_Overlay.pm:672
+msgid "Error in parameters to Queue->AddWatcher"
+msgstr "表單->新增視察員的åƒæ•¸æœ‰èª¤"
+
+#: lib/RT/Queue_Overlay.pm:833
+msgid "Error in parameters to Queue->DeleteWatcher"
+msgstr "表單->刪除視察員的åƒæ•¸æœ‰èª¤"
+
+#: lib/RT/Ticket_Overlay.pm:1372
+msgid "Error in parameters to Ticket->AddWatcher"
+msgstr "申請單->新增視察員的åƒæ•¸æœ‰èª¤"
+
+#: lib/RT/Ticket_Overlay.pm:1538
+msgid "Error in parameters to Ticket->DeleteWatcher"
+msgstr "申請單->刪除視察員的åƒæ•¸æœ‰èª¤"
+
+#: bin/rt-crontool:285
+msgid "Escalate tickets"
+msgstr "調整申請單優先等級"
+
+#: NOT FOUND IN SOURCE
+msgid "Estimate"
+msgstr "é è¨ˆ"
+
+#: html/Ticket/Elements/ShowBasics:57
+msgid "Estimated"
+msgstr "é è¨ˆ"
+
+#: etc/initialdata:20
+msgid "Everyone"
+msgstr "所有人"
+
+#: bin/rt-crontool:271
+msgid "Example:"
+msgstr "範例:"
+
+#: NOT FOUND IN SOURCE
+msgid "Existing user renamed from %1 to %2"
+msgstr "ç¾æœ‰ä½¿ç”¨è€… %1 已改å為 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Export"
+msgstr "匯出"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalAuthId"
+msgstr "外部èªè­‰å¸³è™Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalContactInfoId"
+msgstr "外部è¯çµ¡æ–¹å¼å¸³è™Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalDatabaseDSN"
+msgstr "外部資料庫連çµå­—串"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalDatabasePass"
+msgstr "外部資料庫密碼"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalDatabaseUser"
+msgstr "外部資料庫用戶"
+
+#: NOT FOUND IN SOURCE
+msgid "ExternalURL"
+msgstr "外部介é¢ç¶²å€"
+
+#: html/Admin/Users/Modify.html:99
+msgid "Extra info"
+msgstr "備註"
+
+#: lib/RT/SavedSearch.pm:177
+msgid "Failed to create search attribute"
+msgstr "查詢屬性建立失敗"
+
+#: lib/RT/User_Overlay.pm:376
+msgid "Failed to find 'Privileged' users pseudogroup."
+msgstr "找ä¸åˆ°ã€Œå…§éƒ¨æˆå“¡ã€è™›æ“¬ç¾¤çµ„的使用者。"
+
+#: lib/RT/User_Overlay.pm:383
+msgid "Failed to find 'Unprivileged' users pseudogroup"
+msgstr "找ä¸åˆ°ã€Œéžå…§éƒ¨æˆå“¡ã€è™›æ“¬ç¾¤çµ„的使用者。"
+
+#: bin/rt-crontool:206
+#. ($modname, $@)
+msgid "Failed to load module %1. (%2)"
+msgstr "無法載入模組 %1. (%2)"
+
+#: lib/RT/SavedSearch.pm:152
+#. ($privacy)
+msgid "Failed to load object for %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Feb"
+msgstr "二月"
+
+#: lib/RT/Date.pm:442
+msgid "Feb."
+msgstr "02"
+
+#: NOT FOUND IN SOURCE
+msgid "February"
+msgstr "二月"
+
+#: NOT FOUND IN SOURCE
+msgid "Female"
+msgstr "女"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Content:"
+msgstr "欄ä½å…§å®¹ï¼š"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Description"
+msgstr "欄ä½æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Name"
+msgstr "欄ä½å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Field Type"
+msgstr "欄ä½é¡žåˆ¥"
+
+#: html/Elements/SelectAttachmentField:50
+msgid "Filename"
+msgstr "檔å"
+
+#: lib/RT/CustomField_Overlay.pm:69
+msgid "Fill in multiple text areas"
+msgstr "填入多個文字框"
+
+#: lib/RT/CustomField_Overlay.pm:74
+msgid "Fill in multiple wikitext areas"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:70
+msgid "Fill in one text area"
+msgstr "填入一個文字框"
+
+#: lib/RT/CustomField_Overlay.pm:75
+msgid "Fill in one wikitext area"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:107 html/Admin/CustomFields/Modify.html:118
+msgid "Fill in this field with a URL."
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:71
+msgid "Fill in up to %1 text areas"
+msgstr "填入最多 %1 個文字框"
+
+#: lib/RT/CustomField_Overlay.pm:76
+msgid "Fill in up to %1 wikitext areas"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Filter"
+msgstr "篩é¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Filter people"
+msgstr "å°è±¡ç¯©é¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Filtered list:"
+msgstr "篩é¸åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Fin"
+msgstr "最終"
+
+#: html/Search/Elements/PickBasics:149 html/Ticket/Create.html:182 html/Ticket/Elements/EditBasics:97 lib/RT/Tickets_Overlay.pm:1841
+msgid "Final Priority"
+msgstr "最終順ä½"
+
+#: lib/RT/Ticket_Overlay.pm:1164
+msgid "FinalPriority"
+msgstr "最終順ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Financial Department:"
+msgstr "財務部:"
+
+#: NOT FOUND IN SOURCE
+msgid "Find group whose"
+msgstr "尋找群組的"
+
+#: html/Admin/Groups/index.html:72 html/Admin/Queues/People.html:82 html/Ticket/Elements/EditPeople:55
+msgid "Find groups whose"
+msgstr "尋找群組的"
+
+#: NOT FOUND IN SOURCE
+msgid "Find new/open tickets"
+msgstr "尋找/開啟申請單"
+
+#: html/Admin/Queues/People.html:78 html/Admin/Users/index.html:70 html/Ticket/Elements/EditPeople:51
+msgid "Find people whose"
+msgstr "尋找人員的"
+
+#: NOT FOUND IN SOURCE
+msgid "Find queues whose"
+msgstr "尋找表單的"
+
+#: html/Search/Results.html:147
+msgid "Find tickets"
+msgstr "尋找申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Finish Approval"
+msgstr "簽核完畢"
+
+#: html/Ticket/Elements/Tabs:81
+msgid "First"
+msgstr "第一項"
+
+#: NOT FOUND IN SOURCE
+msgid "First page"
+msgstr "第一é "
+
+#: NOT FOUND IN SOURCE
+msgid "First-"
+msgstr "一"
+
+#: NOT FOUND IN SOURCE
+msgid "First-level Admins"
+msgstr "一階主管"
+
+#: NOT FOUND IN SOURCE
+msgid "First-level Users"
+msgstr "一階主管員工"
+
+#: NOT FOUND IN SOURCE
+msgid "Fixed shift"
+msgstr "固定ç­"
+
+#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:766
+msgid "Foo Bar Baz"
+msgstr "甲 乙 丙"
+
+#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:757
+msgid "Foo!"
+msgstr "甲ï¼"
+
+#: html/Search/Bulk.html:83
+msgid "Force change"
+msgstr "強制更æ›"
+
+#: NOT FOUND IN SOURCE
+msgid "Form Processing"
+msgstr "é›»å­è¡¨å–®ä½œæ¥­å€"
+
+#: html/Search/Elements/EditFormat:52
+msgid "Format"
+msgstr ""
+
+#: html/Search/Results.html:145
+#. ($ticketcount)
+msgid "Found %quant(%1,ticket)"
+msgstr "找到 %1 張申請單"
+
+#: lib/RT/Record.pm:956
+msgid "Found Object"
+msgstr "已找到物件"
+
+#: NOT FOUND IN SOURCE
+msgid "Fourth-"
+msgstr "å››"
+
+#: NOT FOUND IN SOURCE
+msgid "Freeform"
+msgstr "輸入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformContactInfo"
+msgstr "è¯çµ¡æ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformDate"
+msgstr "日期輸入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformExternal"
+msgstr "系統欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformMultiple"
+msgstr "多é‡è¼¸å…¥"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformNumber"
+msgstr "數值輸入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformPassword"
+msgstr "密碼輸入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformSingle"
+msgstr "單一輸入"
+
+#: NOT FOUND IN SOURCE
+msgid "FreeformTime"
+msgstr "時間輸入"
+
+#: NOT FOUND IN SOURCE
+msgid "Fri"
+msgstr "星期五"
+
+#: lib/RT/Date.pm:421
+msgid "Fri."
+msgstr "星期五"
+
+#: html/Ticket/Elements/ShowHistory:66 html/Ticket/Elements/ShowHistory:72
+msgid "Full headers"
+msgstr "完整標頭檔"
+
+#: NOT FOUND IN SOURCE
+msgid "Gecos"
+msgstr "登入帳號"
+
+#: NOT FOUND IN SOURCE
+msgid "Gender"
+msgstr "性別"
+
+#: html/Tools/Offline.html:85
+msgid "Get template from file"
+msgstr "å–出檔案裡的範本"
+
+#: NOT FOUND IN SOURCE
+msgid "Getting the current user from a pgp sig\\n"
+msgstr "å–å¾—ç›®å‰ä½¿ç”¨è€…çš„ pgp 簽章\\n"
+
+#: lib/RT/Transaction_Overlay.pm:684
+#. ($New->Name)
+msgid "Given to %1"
+msgstr "交予 %1"
+
+#: html/Admin/Elements/Tabs:65 html/Admin/index.html:82
+msgid "Global"
+msgstr "全域設定"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Approval"
+msgstr "全域簽核"
+
+#: html/Admin/Elements/EditCustomFields:55
+msgid "Global Custom Fields"
+msgstr "全域自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Keyword Selections"
+msgstr "全域關éµå­—é¸å–"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Rights:"
+msgstr "æ“有全域權é™åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Scrips"
+msgstr "全域手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Global Setup"
+msgstr "全域設定"
+
+#: html/Admin/Global/CustomFields/index.html:59
+msgid "Global custom field configuration"
+msgstr ""
+
+#: html/Admin/Global/MyRT.html:48
+#. ($pane)
+msgid "Global portlet %1 saved."
+msgstr ""
+
+#: html/Admin/Elements/SelectTemplate:59
+#. (loc($Template->Name))
+msgid "Global template: %1"
+msgstr "全域範本:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "GlobalApproval"
+msgstr "全域簽核"
+
+#: html/Admin/CustomFields/index.html:80 html/Search/Results.html:90 html/Tools/Offline.html:89
+msgid "Go"
+msgstr "執行"
+
+#: html/Admin/Groups/index.html:67 html/Admin/Groups/index.html:73 html/Admin/Queues/People.html:80 html/Admin/Queues/People.html:84 html/Admin/Queues/index.html:66 html/Admin/Users/index.html:73 html/Elements/RefreshHomepage:48 html/Search/Results.html:74 html/Ticket/Elements/EditPeople:53 html/Ticket/Elements/EditPeople:57
+msgid "Go!"
+msgstr "執行"
+
+#: NOT FOUND IN SOURCE
+msgid "Good pgp sig from %1\\n"
+msgstr "%1 的 pgp 簽章是正確的\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Goto page"
+msgstr "到é é¢"
+
+#: html/Elements/GotoTicket:46 html/SelfService/Elements/GotoTicket:46
+msgid "Goto ticket"
+msgstr "跳到申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Grand"
+msgstr "上"
+
+#: html/Ticket/Elements/AddWatchers:67 html/Ticket/Elements/ShowGroupMembers:55 html/User/Elements/DelegateRights:99
+msgid "Group"
+msgstr "群組"
+
+#: NOT FOUND IN SOURCE
+msgid "Group %1 %2: %3"
+msgstr "群組 %1 %2:%3"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Admin"
+msgstr "群組管ç†å“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Description"
+msgstr "群組æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Management"
+msgstr "群組管ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Members"
+msgstr "群組æˆå“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Name"
+msgstr "群組å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Name:"
+msgstr "群組å稱:"
+
+#: html/Admin/Elements/CustomFieldTabs:68 html/Admin/Elements/GroupTabs:66 html/Admin/Elements/QueueTabs:82 html/Admin/Elements/SystemTabs:65 html/Admin/Global/index.html:76
+msgid "Group Rights"
+msgstr "群組權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Rights:"
+msgstr "æ“有群組權é™åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Setup"
+msgstr "群組設定"
+
+#: NOT FOUND IN SOURCE
+msgid "Group Status"
+msgstr "群組狀態"
+
+#: lib/RT/Group_Overlay.pm:983
+msgid "Group already has member"
+msgstr "群組內已有此æˆå“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Group could not be created."
+msgstr "無法新增群組"
+
+#: html/Admin/Groups/Modify.html:109
+#. ($create_msg)
+msgid "Group could not be created: %1"
+msgstr "無法新增群組:%1"
+
+#: lib/RT/Group_Overlay.pm:521
+msgid "Group created"
+msgstr "群組新增完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "Group created: %1"
+msgstr "群組 %1 新增完畢"
+
+#: lib/RT/Group_Overlay.pm:1155
+msgid "Group has no such member"
+msgstr "群組沒有這個æˆå“¡"
+
+#: lib/RT/Group_Overlay.pm:963 lib/RT/Queue_Overlay.pm:748 lib/RT/Queue_Overlay.pm:808 lib/RT/Ticket_Overlay.pm:1430 lib/RT/Ticket_Overlay.pm:1510
+msgid "Group not found"
+msgstr "找ä¸åˆ°ç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not found.\\n"
+msgstr "找ä¸åˆ°ç¾¤çµ„。\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group not specified.\\n"
+msgstr "未指定群組。\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Group redescribed from %1 to %2"
+msgstr "群組æè¿° %1 已改為 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Group renamed from %1 to %2"
+msgstr "群組 %1 已改å為 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Group with Queue Rights"
+msgstr "æ“有表單權é™ç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "Group's"
+msgstr "群組之"
+
+#: NOT FOUND IN SOURCE
+msgid "Group:"
+msgstr "群組:"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:59 html/Admin/Elements/SelectNewGroupMembers:57 html/Admin/Elements/Tabs:56 html/Admin/Global/CustomFields/index.html:69 html/Admin/Groups/Members.html:86 html/Admin/Queues/People.html:104 html/Admin/Users/Memberships.html:53 html/Admin/index.html:67 html/User/Groups/Members.html:88 lib/RT/CustomField_Overlay.pm:1210
+msgid "Groups"
+msgstr "群組"
+
+#: lib/RT/Group_Overlay.pm:989
+msgid "Groups can't be members of their members"
+msgstr "ä¸èƒ½å°‡ç¾¤çµ„設為群組內æˆå“¡"
+
+#: html/Admin/Groups/index.html:86
+msgid "Groups matching search criteria"
+msgstr "符åˆæŸ¥è©¢æ¢ä»¶çš„群組"
+
+#: html/Ticket/Elements/ShowRequestor:77
+msgid "Groups this user belongs to"
+msgstr "使用者所屬的群組"
+
+#: NOT FOUND IN SOURCE
+msgid "Groups with Global Rights"
+msgstr "æ“有全域權é™ç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "HRMSDefined"
+msgstr "組織架構"
+
+#: NOT FOUND IN SOURCE
+msgid "HTML Attributes"
+msgstr "HTML 屬性"
+
+#: NOT FOUND IN SOURCE
+msgid "Health Insurance"
+msgstr "å¥ä¿è£œåŠ©èº«ä»½"
+
+#: lib/RT/Interface/CLI.pm:94 lib/RT/Interface/CLI.pm:94
+msgid "Hello!"
+msgstr "å—¨ï¼"
+
+#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:773
+#. ($name)
+msgid "Hello, %1"
+msgstr "嗨,%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Help"
+msgstr "說明"
+
+#: NOT FOUND IN SOURCE
+msgid "Help Desks"
+msgstr "å„項業務窗å£"
+
+#: NOT FOUND IN SOURCE
+msgid "Hidden"
+msgstr "éš±è—"
+
+#: html/Admin/Elements/GroupTabs:70 html/Admin/Elements/UserTabs:64 html/Ticket/Elements/ShowHistory:53 html/Ticket/Elements/Tabs:111
+msgid "History"
+msgstr "紀錄"
+
+#: html/Admin/Groups/History.html:62
+#. ($GroupObj->Name)
+msgid "History of the group %1"
+msgstr "群組 %1 的紀錄"
+
+#: html/Admin/Users/History.html:62
+#. ($UserObj->Name)
+msgid "History of the user %1"
+msgstr "使用者 %1 的紀錄"
+
+#: NOT FOUND IN SOURCE
+msgid "HomePhone"
+msgstr "ä½è™•é›»è©±"
+
+#: html/Elements/Tabs:65
+msgid "Homepage"
+msgstr "主é "
+
+#: NOT FOUND IN SOURCE
+msgid "Hotel Expense"
+msgstr "ä½å®¿è²»"
+
+#: html/Elements/SelectTimeUnits:48
+msgid "Hours"
+msgstr ""
+
+#: lib/RT/Base.pm:119
+#. (6)
+msgid "I have %quant(%1,concrete mixer)."
+msgstr "我有 %quant(%1,份固體攪拌器)。"
+
+#: html/Search/Build.html:460 lib/RT/Report/Tickets.pm:415
+msgid "I'm lost"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "ID Number"
+msgstr "身分證號"
+
+#: NOT FOUND IN SOURCE
+msgid "ID Type"
+msgstr "身分類別"
+
+#: html/Ticket/Elements/ShowBasics:48 lib/RT/Tickets_Overlay.pm:1766
+msgid "Id"
+msgstr "編號"
+
+#: html/Admin/Users/Modify.html:65 html/User/Prefs.html:60
+msgid "Identity"
+msgstr "身份"
+
+#: etc/initialdata:429
+msgid "If an approval is rejected, reject the original and delete pending approvals"
+msgstr "若簽核單é­åˆ°é§å›žï¼Œå‰‡é€£å¸¶é§å›žåŽŸç”³è«‹å–®ï¼Œä¸¦åˆªé™¤å…¶ä»–相關的待簽核事項"
+
+#: html/Tools/Offline.html:74
+msgid "If no Requestor is specified, create tickets with this requestor."
+msgstr "若沒有指定申請者,則以此使用者作為申請者"
+
+#: html/Tools/Offline.html:65
+msgid "If no queue is specified, create tickets in this queue."
+msgstr "申請單若沒有指定表單,則將它新增在此表單內"
+
+#: bin/rt-crontool:267
+msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
+msgstr "如果此工具程å¼ç‚º setgid,惡æ„的本地端用戶å³èƒ½ç”±æ­¤å–å¾— RT 的管ç†å“¡æ¬Šé™ã€‚"
+
+#: html/Admin/Queues/People.html:126 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:128 html/Ticket/ModifyPeople.html:60
+msgid "If you've updated anything above, be sure to"
+msgstr "若您已更新以上資料,請記得按一下"
+
+#: lib/RT/Record.pm:947
+msgid "Illegal value for %1"
+msgstr "%1 的值錯誤"
+
+#: NOT FOUND IN SOURCE
+msgid "Image"
+msgstr "圖片"
+
+#: lib/RT/Record.pm:950
+msgid "Immutable field"
+msgstr "此欄ä½å€¼ä¸å¯æ›´å‹•"
+
+#: NOT FOUND IN SOURCE
+msgid "Import"
+msgstr "匯入"
+
+#: NOT FOUND IN SOURCE
+msgid "Include disabled custom fields in listing."
+msgstr "列出åœç”¨çš„自訂欄ä½"
+
+#: html/Admin/Groups/index.html:65
+msgid "Include disabled groups in listing."
+msgstr "列出åœç”¨çš„群組"
+
+#: html/Admin/Queues/index.html:65
+msgid "Include disabled queues in listing."
+msgstr "列出åœç”¨çš„表單"
+
+#: html/Admin/Users/index.html:71
+msgid "Include disabled users in search."
+msgstr "列出åœç”¨çš„使用者"
+
+#: html/Admin/CustomFields/Modify.html:113
+msgid "Include page"
+msgstr ""
+
+#: html/Search/Build.html:486 lib/RT/Report/Tickets.pm:441
+msgid "Incomplete Query"
+msgstr ""
+
+#: html/Search/Build.html:483 lib/RT/Report/Tickets.pm:438
+msgid "Incomplete query"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Indirect Employee"
+msgstr "直接/間接員工"
+
+#: html/Search/Elements/PickBasics:148 lib/RT/Tickets_Overlay.pm:1816
+msgid "Initial Priority"
+msgstr "åˆå§‹å„ªå…ˆé †ä½"
+
+#: lib/RT/Ticket_Overlay.pm:1163 lib/RT/Ticket_Overlay.pm:1165
+msgid "InitialPriority"
+msgstr "åˆå§‹å„ªå…ˆé †ä½"
+
+#: lib/RT/ScripAction_Overlay.pm:133
+msgid "Input error"
+msgstr "輸入錯誤"
+
+#: html/Elements/ValidateCustomFields:68 lib/RT/CustomField_Overlay.pm:1021 lib/RT/CustomField_Overlay.pm:1162
+#. ($self->FriendlyPattern)
+#. ($CF->FriendlyPattern)
+msgid "Input must match %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Interest noted"
+msgstr "登記æˆåŠŸ"
+
+#: lib/RT/Ticket_Overlay.pm:3503
+msgid "Internal Error"
+msgstr "內部錯誤"
+
+#: lib/RT/Record.pm:308
+#. ($id->{error_message})
+msgid "Internal Error: %1"
+msgstr "內部錯誤:%1"
+
+#: lib/RT/Group_Overlay.pm:668
+msgid "Invalid Group Type"
+msgstr "錯誤的群組類別"
+
+#: lib/RT/Principal_Overlay.pm:161
+msgid "Invalid Right"
+msgstr "錯誤的權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid Type"
+msgstr "錯誤的類型"
+
+#: lib/RT/Record.pm:952
+msgid "Invalid data"
+msgstr "錯誤的資料"
+
+#: NOT FOUND IN SOURCE
+msgid "Invalid owner. Defaulting to 'nobody'."
+msgstr "錯誤的承辦人。改為é è¨­æ‰¿è¾¦äººã€Œnobodyã€ã€‚"
+
+#: lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:678
+#. ($msg)
+msgid "Invalid pattern: %1"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:157 lib/RT/Template_Overlay.pm:244
+msgid "Invalid queue"
+msgstr "錯誤的表單"
+
+#: lib/RT/ACE_Overlay.pm:264 lib/RT/ACE_Overlay.pm:273 lib/RT/ACE_Overlay.pm:279 lib/RT/ACE_Overlay.pm:290
+msgid "Invalid right"
+msgstr "錯誤的權é™"
+
+#: lib/RT/Record.pm:283
+#. ($key)
+msgid "Invalid value for %1"
+msgstr "%1 的值錯誤"
+
+#: lib/RT/Record.pm:1610
+msgid "Invalid value for custom field"
+msgstr "錯誤的自訂欄ä½å€¼"
+
+#: lib/RT/Ticket_Overlay.pm:424
+msgid "Invalid value for status"
+msgstr "錯誤的狀態值"
+
+#: NOT FOUND IN SOURCE
+msgid "IssueStatement"
+msgstr "é€å‡ºé™³è¿°"
+
+#: bin/rt-crontool:268
+msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
+msgstr "請絕å°ä¸è¦è®“未具權é™çš„使用者執行此工具程å¼ã€‚"
+
+#: bin/rt-crontool:269
+msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
+msgstr "建議您新增一個隸屬於正確群組的低權é™ç³»çµ±ä½¿ç”¨è€…,並以該身份執行此工具程å¼ã€‚"
+
+#: bin/rt-crontool:231
+msgid "It takes several arguments:"
+msgstr "它接å—下列åƒæ•¸ï¼š"
+
+#: html/Search/Elements/EditFormat:85
+msgid "Italic"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Item Name"
+msgstr "å“å"
+
+#: NOT FOUND IN SOURCE
+msgid "Items"
+msgstr "ç­†"
+
+#: NOT FOUND IN SOURCE
+msgid "Items pending my approval"
+msgstr "待簽核項目"
+
+#: NOT FOUND IN SOURCE
+msgid "Jan"
+msgstr "一月"
+
+#: lib/RT/Date.pm:441
+msgid "Jan."
+msgstr "01"
+
+#: NOT FOUND IN SOURCE
+msgid "January"
+msgstr "一月"
+
+#: NOT FOUND IN SOURCE
+msgid "Job"
+msgstr "è·ç¨±"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "Join or leave this group"
+msgstr "加入或離開此群組"
+
+#: NOT FOUND IN SOURCE
+msgid "Jul"
+msgstr "七月"
+
+#: lib/RT/Date.pm:447
+msgid "Jul."
+msgstr "07"
+
+#: NOT FOUND IN SOURCE
+msgid "July"
+msgstr "七月"
+
+#: html/Ticket/Elements/Tabs:125
+msgid "Jumbo"
+msgstr "全部資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "Jun"
+msgstr "六月"
+
+#: lib/RT/Date.pm:446
+msgid "Jun."
+msgstr "06"
+
+#: NOT FOUND IN SOURCE
+msgid "June"
+msgstr "六月"
+
+#: NOT FOUND IN SOURCE
+msgid "Keyword"
+msgstr "é—œéµå­—"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelAttachments"
+msgstr "附件標籤"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelContent"
+msgstr "內容標籤"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelSubject"
+msgstr "主題標籤"
+
+#: NOT FOUND IN SOURCE
+msgid "LabelURL"
+msgstr "éˆçµæ¨™ç±¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Lang"
+msgstr "使用語言"
+
+#: html/Admin/Users/Modify.html:94 html/User/Prefs.html:76
+msgid "Language"
+msgstr "語言"
+
+#: html/Search/Elements/EditFormat:79
+msgid "Large"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:96
+msgid "Last"
+msgstr "上次更新"
+
+#: html/Ticket/Elements/EditDates:59 html/Ticket/Elements/ShowDates:60
+msgid "Last Contact"
+msgstr "上次è¯çµ¡"
+
+#: html/Elements/SelectDateType:50
+msgid "Last Contacted"
+msgstr "上次è¯çµ¡æ—¥æœŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Last Notified"
+msgstr "上次通知"
+
+#: html/Elements/SelectDateType:51
+msgid "Last Updated"
+msgstr "上次更新"
+
+#: NOT FOUND IN SOURCE
+msgid "LastUpdated"
+msgstr "上次更新"
+
+#: html/Search/Elements/PickBasics:103
+msgid "LastUpdatedBy"
+msgstr "上次更新者"
+
+#: html/Ticket/Elements/ShowBasics:68
+msgid "Left"
+msgstr "剩餘時間"
+
+#: html/Admin/Users/Modify.html:109
+msgid "Let this user access RT"
+msgstr "å…許這å使用者登入"
+
+#: html/Admin/Users/Modify.html:113
+msgid "Let this user be granted rights"
+msgstr "內部æˆå“¡ï¼ˆå…·æœ‰å€‹äººæ¬Šé™ï¼‰"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting owner to %1 %2"
+msgstr "é™åˆ¶æ‰¿è¾¦äººç‚º %1 到%2"
+
+#: NOT FOUND IN SOURCE
+msgid "Limiting queue to %1 %2"
+msgstr "é™åˆ¶è¡¨å–®ç‚º %1 到 %2"
+
+#: html/Search/Elements/EditFormat:68
+msgid "Link"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Link a Queue"
+msgstr "申請表單連çµ"
+
+#: lib/RT/Record.pm:1306
+msgid "Link already exists"
+msgstr "æ­¤éˆçµå·²å­˜åœ¨"
+
+#: lib/RT/Record.pm:1320
+msgid "Link could not be created"
+msgstr "無法新增éˆçµ"
+
+#: lib/RT/Record.pm:1326
+#. ($TransString)
+msgid "Link created (%1)"
+msgstr "éˆçµ(%1)新增完畢"
+
+#: lib/RT/Record.pm:1387
+#. ($TransString)
+msgid "Link deleted (%1)"
+msgstr "éˆçµ(%1)刪除完畢"
+
+#: lib/RT/Record.pm:1393
+msgid "Link not found"
+msgstr "找ä¸åˆ°éˆçµ"
+
+#: html/Ticket/ModifyLinks.html:46 html/Ticket/ModifyLinks.html:50
+#. ($Ticket->Id)
+msgid "Link ticket #%1"
+msgstr "éˆçµç”³è«‹å–® #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Link ticket %1"
+msgstr "éˆçµç”³è«‹å–® %1"
+
+#: html/Admin/CustomFields/Modify.html:102
+msgid "Link values to"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:700
+msgid "Linking. Permission denied"
+msgstr ""
+
+#: html/Ticket/Create.html:216 html/Ticket/Elements/ShowSummary:89 html/Ticket/Elements/Tabs:120 html/Ticket/ModifyAll.html:78
+msgid "Links"
+msgstr "éˆçµ"
+
+#: NOT FOUND IN SOURCE
+msgid "List All Users"
+msgstr "列出所有用戶資料"
+
+#: html/Search/Elements/EditSearches:75
+msgid "Load"
+msgstr "載入"
+
+#: html/Search/Elements/EditSearches:73
+msgid "Load saved search:"
+msgstr "載入已儲存的查詢:"
+
+#: lib/RT/System.pm:86
+msgid "LoadSavedSearch"
+msgstr ""
+
+#: html/Admin/Tools/Configuration.html:64
+msgid "Loaded perl modules"
+msgstr "已載入的 Perl 模組"
+
+#: lib/RT/SavedSearch.pm:111
+#. ($self->Name)
+msgid "Loaded search %1"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:138 html/User/Prefs.html:126
+msgid "Location"
+msgstr "ä½ç½®"
+
+#: NOT FOUND IN SOURCE
+msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
+msgstr "登入目錄 %1 找ä¸åˆ°æˆ–無法寫入\\n。無法執行 RT。"
+
+#: NOT FOUND IN SOURCE
+msgid "LogToFile"
+msgstr "紀錄等級"
+
+#: NOT FOUND IN SOURCE
+msgid "LogToFileNamed"
+msgstr "紀錄檔å"
+
+#: html/Elements/Header:91
+#. ("<span>".$session{'CurrentUser'}->Name."</span>")
+msgid "Logged in as %1"
+msgstr "使用者:%1"
+
+#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:100 html/Elements/Login:68 html/Elements/Login:84 lib/RT/StyleGuide.pod:797
+msgid "Login"
+msgstr "登入"
+
+#: html/Elements/Header:101
+msgid "Logout"
+msgstr "登出"
+
+#: NOT FOUND IN SOURCE
+msgid "Long-term contractor"
+msgstr "長期契約員工"
+
+#: lib/RT/CustomField_Overlay.pm:932
+msgid "Lookup type mismatch"
+msgstr "å°æ‡‰çš„類別ä¸ç¬¦"
+
+#: html/Search/Bulk.html:82
+msgid "Make Owner"
+msgstr "新增承辦人"
+
+#: html/Search/Bulk.html:106
+msgid "Make Status"
+msgstr "新增ç¾æ³"
+
+#: html/Search/Bulk.html:114
+msgid "Make date Due"
+msgstr "新增到期日"
+
+#: html/Search/Bulk.html:116
+msgid "Make date Resolved"
+msgstr "新增解決日期"
+
+#: html/Search/Bulk.html:110
+msgid "Make date Started"
+msgstr "新增實際起始日期"
+
+#: html/Search/Bulk.html:108
+msgid "Make date Starts"
+msgstr "新增應起始日期"
+
+#: html/Search/Bulk.html:112
+msgid "Make date Told"
+msgstr "新增報告日期"
+
+#: html/Search/Bulk.html:102
+msgid "Make priority"
+msgstr "新增優先順ä½"
+
+#: html/Search/Bulk.html:104
+msgid "Make queue"
+msgstr "新增表單"
+
+#: html/Search/Bulk.html:100
+msgid "Make subject"
+msgstr "新增主題"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "Make this group visible to user"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Male"
+msgstr "ç”·"
+
+#: html/Admin/index.html:78
+msgid "Manage custom fields and custom field values"
+msgstr "管ç†è‡ªè¨‚欄ä½åŠæ¬„ä½å€¼"
+
+#: html/Admin/index.html:69
+msgid "Manage groups and group membership"
+msgstr "管ç†ç¾¤çµ„åŠæ‰€å±¬æˆå“¡"
+
+#: html/Admin/index.html:85
+msgid "Manage properties and configuration which apply to all queues"
+msgstr "管ç†é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„屬性與設定"
+
+#: html/Admin/index.html:74
+msgid "Manage queues and queue-specific properties"
+msgstr "管ç†å„表單åŠç›¸é—œå±¬æ€§"
+
+#: html/Admin/index.html:64
+msgid "Manage users and passwords"
+msgstr "管ç†ä½¿ç”¨è€…與密碼"
+
+#: NOT FOUND IN SOURCE
+msgid "Manager"
+msgstr "經ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Mar"
+msgstr "三月"
+
+#: lib/RT/Date.pm:443
+msgid "Mar."
+msgstr "03"
+
+#: NOT FOUND IN SOURCE
+msgid "March"
+msgstr "三月"
+
+#: NOT FOUND IN SOURCE
+msgid "Marketing Department"
+msgstr "行銷部"
+
+#: NOT FOUND IN SOURCE
+msgid "Match Pattern"
+msgstr "符åˆæ¨£å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "May"
+msgstr "五月"
+
+#: lib/RT/Date.pm:445
+msgid "May."
+msgstr "05"
+
+#: lib/RT/Transaction_Overlay.pm:731
+#. ($value)
+msgid "Member %1 added"
+msgstr "æˆå“¡ %1 新增完畢"
+
+#: lib/RT/Transaction_Overlay.pm:771
+#. ($value)
+msgid "Member %1 deleted"
+msgstr "æˆå“¡ %1 刪除完畢"
+
+#: lib/RT/Group_Overlay.pm:1000
+msgid "Member added"
+msgstr "新增æˆå“¡å®Œç•¢"
+
+#: lib/RT/Group_Overlay.pm:1162
+msgid "Member deleted"
+msgstr "æˆå“¡å·²åˆªé™¤"
+
+#: lib/RT/Group_Overlay.pm:1166
+msgid "Member not deleted"
+msgstr "æˆå“¡æœªåˆªé™¤"
+
+#: html/Elements/SelectLinkType:47
+msgid "Member of"
+msgstr "隸屬於"
+
+#: NOT FOUND IN SOURCE
+msgid "Member since"
+msgstr "註冊日期"
+
+#: NOT FOUND IN SOURCE
+msgid "MemberOf"
+msgstr "隸屬於"
+
+#: html/Admin/Elements/GroupTabs:63 html/User/Elements/GroupTabs:63
+msgid "Members"
+msgstr "æˆå“¡"
+
+#: lib/RT/Transaction_Overlay.pm:728
+#. ($value)
+msgid "Membership in %1 added"
+msgstr "所屬群組 %1 加入完畢"
+
+#: lib/RT/Transaction_Overlay.pm:768
+#. ($value)
+msgid "Membership in %1 deleted"
+msgstr "所屬群組 %1 移除完畢"
+
+#: html/Admin/Elements/UserTabs:61
+msgid "Memberships"
+msgstr "所屬群組"
+
+#: html/Admin/Users/Memberships.html:60
+#. ($UserObj->Name)
+msgid "Memberships of the user %1"
+msgstr "使用者 %1 的所屬群組"
+
+#: lib/RT/Ticket_Overlay.pm:2893
+msgid "Merge Successful"
+msgstr "æ•´åˆå®Œç•¢"
+
+#: lib/RT/Ticket_Overlay.pm:2780
+msgid "Merge failed. Couldn't set EffectiveId"
+msgstr "æ•´åˆå¤±æ•—。無法設定 EffectiveId"
+
+#: lib/RT/Ticket_Overlay.pm:2788
+msgid "Merge failed. Couldn't set Status"
+msgstr ""
+
+#: html/Elements/EditLinks:131 html/Ticket/Elements/BulkLinks:48
+msgid "Merge into"
+msgstr "æ•´åˆé€²"
+
+#: lib/RT/Transaction_Overlay.pm:734
+#. ($value)
+msgid "Merged into %1"
+msgstr "已整åˆé€² %1"
+
+#: html/Search/Bulk.html:143 html/Ticket/Update.html:118
+msgid "Message"
+msgstr "訊æ¯"
+
+#: html/Ticket/Elements/ShowTransactionAttachments:164
+msgid "Message body not shown because it is too large or is not plain text."
+msgstr "信件內文ä¸æ˜¯ç´”文字,因此無法顯示。"
+
+#: lib/RT/Ticket_Overlay.pm:2451
+msgid "Message could not be recorded"
+msgstr "無法紀錄訊æ¯"
+
+#: lib/RT/Ticket_Overlay.pm:2454
+msgid "Message recorded"
+msgstr "訊æ¯ç´€éŒ„æˆåŠŸ"
+
+#: html/Ticket/Elements/PreviewScrips:122
+msgid "Messages about this ticket will not be sent to..."
+msgstr "此申請單的相關訊æ¯ä¸æœƒå¯„é€çµ¦..."
+
+#: html/Elements/SelectTimeUnits:47
+msgid "Minutes"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Misc. Expense"
+msgstr "雜費"
+
+#: html/Search/Build.html:490 lib/RT/Report/Tickets.pm:445
+msgid "Mismatched parentheses"
+msgstr "未å°é½Šçš„括號"
+
+#: lib/RT/Record.pm:954
+msgid "Missing a primary key?: %1"
+msgstr "缺少主éµå€¼ï¼Ÿ(%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Missing mandatory fields"
+msgstr "缺少必填欄ä½"
+
+#: html/Admin/Users/Modify.html:193 html/User/Prefs.html:92
+msgid "Mobile"
+msgstr "行動電話"
+
+#: NOT FOUND IN SOURCE
+msgid "MobilePhone"
+msgstr "行動電話"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "Modify Access Control List"
+msgstr "更改權é™æŽ§åˆ¶æ¸…å–®"
+
+#: html/Admin/Elements/ObjectCustomFields:96
+#. (loc(lc($FriendlySubTypes)), loc(lc($Types)))
+msgid "Modify Custom Fields which apply to %1 for all %2"
+msgstr "更改é©ç”¨æ–¼ %1 內所有 %2 的自訂欄ä½"
+
+#: html/Admin/Elements/ObjectCustomFields:98
+#. (loc(lc($Types)))
+msgid "Modify Custom Fields which apply to all %1"
+msgstr "更改é©ç”¨æ–¼æ‰€æœ‰%1的自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Custom Fields which apply to all queues"
+msgstr "更改é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„自訂欄ä½"
+
+#: html/Admin/Global/GroupRights.html:106 html/Admin/Groups/GroupRights.html:94 html/Admin/Queues/GroupRights.html:107
+msgid "Modify Group Rights"
+msgstr "更改群組權é™"
+
+#: html/Admin/Groups/Members.html:105 html/User/Groups/Members.html:101
+msgid "Modify Members"
+msgstr "更改æˆå“¡"
+
+#: html/User/Delegation.html:58
+msgid "Modify Rights"
+msgstr "更改權é™"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "Modify Scrip templates for this queue"
+msgstr "更改此表單的範本"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "Modify Scrips for this queue"
+msgstr "更改此表單的手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify System ACLS"
+msgstr "更改系統權é™æ¸…å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Template %1"
+msgstr "更改範本 %1"
+
+#: html/Admin/Global/UserRights.html:75 html/Admin/Groups/UserRights.html:76 html/Admin/Queues/UserRights.html:75
+msgid "Modify User Rights"
+msgstr "更改使用者權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify Workflow"
+msgstr "更改æµç¨‹"
+
+#: html/Admin/Queues/CustomField.html:66
+#. ($QueueObj->Name())
+msgid "Modify a CustomField for queue %1"
+msgstr "更改 %1 表單內的自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify a CustomField which applies to all queues"
+msgstr "更改é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„自訂欄ä½"
+
+#: html/Admin/Queues/Scrip.html:82
+#. ($QueueObj->Name)
+msgid "Modify a scrip for queue %1"
+msgstr "更改 %1 表單內的手續"
+
+#: html/Admin/Global/Scrip.html:75
+msgid "Modify a scrip which applies to all queues"
+msgstr "更改é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„手續"
+
+#: html/Admin/CustomFields/Objects.html:90
+#. ($CF->Name)
+msgid "Modify associated objects for %1"
+msgstr "更改é©ç”¨ %1 的物件"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify dates for # %1"
+msgstr "更改 # %1 的日期"
+
+#: html/Ticket/ModifyDates.html:46 html/Ticket/ModifyDates.html:50
+#. ($TicketObj->Id)
+msgid "Modify dates for #%1"
+msgstr "更改 #%1 的日期"
+
+#: html/Ticket/ModifyDates.html:57
+#. ($TicketObj->Id)
+msgid "Modify dates for ticket # %1"
+msgstr "更改申請單 # %1 的日期"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:65 html/Admin/Global/index.html:72
+msgid "Modify global custom fields"
+msgstr "更改全域自訂欄ä½"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:70 html/Admin/Global/GroupRights.html:46 html/Admin/Global/GroupRights.html:49 html/Admin/Global/index.html:77
+msgid "Modify global group rights"
+msgstr "更改全域設定的群組權é™"
+
+#: html/Admin/Global/GroupRights.html:54
+msgid "Modify global group rights."
+msgstr "更改全域設定的群組權é™ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for groups"
+msgstr "更改全域設定的群組權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global rights for users"
+msgstr "更改全域設定的使用者權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify global scrips"
+msgstr "更改全域手續"
+
+#: html/Admin/Global/UserRights.html:46 html/Admin/Global/UserRights.html:49 html/Admin/Global/index.html:81
+msgid "Modify global user rights"
+msgstr "更改全域設定的使用者權é™"
+
+#: html/Admin/Global/UserRights.html:54
+msgid "Modify global user rights."
+msgstr "更改全域設定的使用者權é™ã€‚"
+
+#: lib/RT/Group_Overlay.pm:163
+msgid "Modify group metadata or delete group"
+msgstr "更改群組資料åŠåˆªé™¤ç¾¤çµ„"
+
+#: html/Admin/CustomFields/GroupRights.html:164
+#. ($CustomFieldObj->Name)
+msgid "Modify group rights for custom field %1"
+msgstr "æ›´æ”¹è‡ªè¨‚æ¬„ä½ %1 的群組權é™"
+
+#: html/Admin/Groups/GroupRights.html:46 html/Admin/Groups/GroupRights.html:50 html/Admin/Groups/GroupRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify group rights for group %1"
+msgstr "更改群組 %1 的群組權é™"
+
+#: html/Admin/Queues/GroupRights.html:46 html/Admin/Queues/GroupRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify group rights for queue %1"
+msgstr "更改表單 %1 的群組權é™"
+
+#: lib/RT/Group_Overlay.pm:165
+msgid "Modify membership roster for this group"
+msgstr "更改此群組的æˆå“¡åå–®"
+
+#: lib/RT/System.pm:82
+msgid "Modify one's own RT account"
+msgstr "更改個人的帳號資訊"
+
+#: html/Admin/Queues/People.html:46 html/Admin/Queues/People.html:50
+#. ($QueueObj->Name)
+msgid "Modify people related to queue %1"
+msgstr "更改éˆçµåˆ°è¡¨å–® %1 的人員"
+
+#: html/Ticket/ModifyPeople.html:46 html/Ticket/ModifyPeople.html:50 html/Ticket/ModifyPeople.html:57
+#. ($Ticket->id)
+#. ($Ticket->Id)
+msgid "Modify people related to ticket #%1"
+msgstr "更改申請單 #%1 éˆçµåˆ°çš„人員"
+
+#: html/Admin/Queues/Scrips.html:67
+#. ($QueueObj->Name)
+msgid "Modify scrips for queue %1"
+msgstr "更改表單 %1 的手續"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:56 html/Admin/Global/Scrips.html:65 html/Admin/Global/index.html:63
+msgid "Modify scrips which apply to all queues"
+msgstr "更改é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„手續"
+
+#: html/Admin/Global/Template.html:102 html/Admin/Global/Template.html:46 html/Admin/Global/Template.html:51 html/Admin/Queues/Template.html:99
+#. (loc($TemplateObj->Name()))
+#. ($TemplateObj->id)
+msgid "Modify template %1"
+msgstr "更改範本 %1"
+
+#: html/Admin/Global/Templates.html:65
+msgid "Modify templates which apply to all queues"
+msgstr "更改é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„範本"
+
+#: html/Admin/Global/index.html:85
+msgid "Modify the default \"RT at a glance\" view"
+msgstr "更改é è¨­çš„「RT 一覽ã€æª¢è¦–"
+
+#: html/Admin/Groups/Modify.html:119 html/User/Groups/Modify.html:107
+#. ($Group->Name)
+msgid "Modify the group %1"
+msgstr "更改群組 %1"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "Modify the queue watchers"
+msgstr "更改表單視察員"
+
+#: html/Admin/Users/Modify.html:309
+#. ($UserObj->Name)
+msgid "Modify the user %1"
+msgstr "更改使用者 %1"
+
+#: html/Ticket/ModifyAll.html:58
+#. ($Ticket->Id)
+msgid "Modify ticket # %1"
+msgstr "更改申請單 # %1"
+
+#: html/Ticket/Modify.html:46 html/Ticket/Modify.html:49 html/Ticket/Modify.html:55
+#. ($TicketObj->Id)
+msgid "Modify ticket #%1"
+msgstr "更改申請單 # %1"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "Modify tickets"
+msgstr "更改申請單"
+
+#: html/Admin/CustomFields/UserRights.html:157
+#. ($CustomFieldObj->Name)
+msgid "Modify user rights for custom field %1"
+msgstr "æ›´æ”¹è‡ªè¨‚æ¬„ä½ %1 的使用者權é™"
+
+#: html/Admin/Groups/UserRights.html:46 html/Admin/Groups/UserRights.html:50 html/Admin/Groups/UserRights.html:56
+#. ($GroupObj->Name)
+msgid "Modify user rights for group %1"
+msgstr "更改群組 %1 的使用者權é™"
+
+#: html/Admin/Queues/UserRights.html:46 html/Admin/Queues/UserRights.html:50
+#. ($QueueObj->Name)
+msgid "Modify user rights for queue %1"
+msgstr "更改表單 %1 的使用者權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify watchers for queue '%1'"
+msgstr "更改 '%1' 的視察員"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify workflow %1"
+msgstr "更改æµç¨‹ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Modify workflows which apply to all queues"
+msgstr "更改é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„æµç¨‹"
+
+#: lib/RT/Queue_Overlay.pm:94
+msgid "ModifyACL"
+msgstr "更改權é™æ¸…å–®"
+
+#: lib/RT/CustomField_Overlay.pm:108
+msgid "ModifyCustomField"
+msgstr "更改自訂欄ä½"
+
+#: lib/RT/Group_Overlay.pm:166
+msgid "ModifyOwnMembership"
+msgstr "更改自己是å¦å±¬æ–¼æŸç¾¤çµ„"
+
+#: lib/RT/Queue_Overlay.pm:95
+msgid "ModifyQueueWatchers"
+msgstr "更改表單視察員"
+
+#: lib/RT/Queue_Overlay.pm:100
+msgid "ModifyScrips"
+msgstr "更改手續"
+
+#: lib/RT/System.pm:82
+msgid "ModifySelf"
+msgstr "更改個人帳號"
+
+#: lib/RT/Queue_Overlay.pm:97
+msgid "ModifyTemplate"
+msgstr "更改範本"
+
+#: lib/RT/Queue_Overlay.pm:113
+msgid "ModifyTicket"
+msgstr "更改申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Mon"
+msgstr "星期一"
+
+#: lib/RT/Date.pm:417
+msgid "Mon."
+msgstr "星期一"
+
+#: NOT FOUND IN SOURCE
+msgid "More"
+msgstr "更多"
+
+#: html/Ticket/Elements/ShowRequestor:61
+#. ($name)
+msgid "More about %1"
+msgstr "關於 %1 的進一步資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "Morning Shift"
+msgstr "æ—©ç­"
+
+#: NOT FOUND IN SOURCE
+msgid "Move"
+msgstr "移動"
+
+#: NOT FOUND IN SOURCE
+msgid "Move All"
+msgstr "全移"
+
+#: html/Admin/Elements/PickCustomFields:83
+msgid "Move down"
+msgstr "下移"
+
+#: html/Admin/Elements/PickCustomFields:75
+msgid "Move up"
+msgstr "上移"
+
+#: html/Admin/Elements/SelectSingleOrMultiple:48
+msgid "Multiple"
+msgstr "多é‡"
+
+#: lib/RT/User_Overlay.pm:226
+msgid "Must specify 'Name' attribute"
+msgstr "必須指定 'Name' 的屬性"
+
+#: html/SelfService/Elements/MyRequests:57
+#. ($friendly_status)
+msgid "My %1 tickets"
+msgstr "我的 %1 申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "My Approvals"
+msgstr "表單簽核"
+
+#: html/Tools/Elements/Tabs:63
+msgid "My Day"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "My Requests"
+msgstr "表單申請追蹤"
+
+#: NOT FOUND IN SOURCE
+msgid "My Tickets"
+msgstr "表單處ç†"
+
+#: html/Approvals/index.html:46 html/Approvals/index.html:47
+msgid "My approvals"
+msgstr "表單簽核"
+
+#: html/Search/Elements/SearchPrivacy:50 html/Search/Elements/SelectSearchObject:53 html/Search/Elements/SelectSearchesForObjects:54
+msgid "My saved searches"
+msgstr "我已儲存的查詢"
+
+#: html/Admin/CustomFields/Modify.html:58 html/Admin/Elements/AddCustomFieldValue:53 html/Admin/Elements/EditCustomField:55 html/Admin/Elements/EditCustomFieldValues:55 html/Admin/Elements/ModifyTemplate:49 html/Admin/Groups/Modify.html:65 html/Search/Bulk.html:157 html/User/Groups/Modify.html:65
+msgid "Name"
+msgstr "å稱"
+
+#: lib/RT/User_Overlay.pm:233
+msgid "Name in use"
+msgstr "帳號已有人使用"
+
+#: NOT FOUND IN SOURCE
+msgid "Nationality"
+msgstr "國ç±"
+
+#: NOT FOUND IN SOURCE
+msgid "Need approval from system administrator"
+msgstr "需先由系統管ç†å“¡é€²è¡Œæ‰¹å‡†"
+
+#: html/Ticket/Elements/ShowDates:73
+msgid "Never"
+msgstr "從未更動"
+
+#: NOT FOUND IN SOURCE
+msgid "New"
+msgstr "新建立"
+
+#: html/Elements/EditLinks:117
+msgid "New Links"
+msgstr "新增關係"
+
+#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:109
+msgid "New Password"
+msgstr "新的密碼"
+
+#: etc/initialdata:332
+msgid "New Pending Approval"
+msgstr "新的待簽核事項"
+
+#: NOT FOUND IN SOURCE
+msgid "New Query"
+msgstr "新增查詢"
+
+#: NOT FOUND IN SOURCE
+msgid "New Request"
+msgstr "表單申請"
+
+#: html/Ticket/Elements/Tabs:212
+msgid "New Search"
+msgstr "新增查詢"
+
+#: NOT FOUND IN SOURCE
+msgid "New Watchers"
+msgstr "新增視察員"
+
+#: html/Admin/Elements/CustomFieldTabs:93 html/Admin/Queues/CustomField.html:73
+msgid "New custom field"
+msgstr "新增自訂欄ä½"
+
+#: html/Admin/Elements/GroupTabs:77 html/User/Elements/GroupTabs:73
+msgid "New group"
+msgstr "新增群組"
+
+#: html/SelfService/Prefs.html:53
+msgid "New password"
+msgstr "新的密碼"
+
+#: lib/RT/User_Overlay.pm:816
+msgid "New password notification sent"
+msgstr "é€å‡ºæ–°å¯†ç¢¼é€šçŸ¥"
+
+#: html/Admin/Elements/QueueTabs:95
+msgid "New queue"
+msgstr "新增表單"
+
+#: html/Ticket/Elements/Reminders:118
+msgid "New reminder:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "New request"
+msgstr "æ出申請單"
+
+#: html/Admin/Elements/SelectRights:65
+msgid "New rights"
+msgstr "新增權é™"
+
+#: html/Admin/Global/Scrip.html:63 html/Admin/Global/Scrips.html:60 html/Admin/Queues/Scrip.html:71 html/Admin/Queues/Scrips.html:76
+msgid "New scrip"
+msgstr "新增手續"
+
+#: NOT FOUND IN SOURCE
+msgid "New search"
+msgstr "é‡æ–°æŸ¥è©¢"
+
+#: html/Admin/Global/Template.html:81 html/Admin/Global/Templates.html:60 html/Admin/Queues/Template.html:79 html/Admin/Queues/Templates.html:71
+msgid "New template"
+msgstr "新增範本"
+
+#: html/SelfService/Elements/Tabs:84 html/SelfService/Elements/Tabs:88
+msgid "New ticket"
+msgstr "æ出申請單"
+
+#: lib/RT/Ticket_Overlay.pm:2757
+msgid "New ticket doesn't exist"
+msgstr "沒有新申請單"
+
+#: html/Admin/Elements/UserTabs:81
+msgid "New user"
+msgstr "新增使用者"
+
+#: html/Admin/Elements/CreateUserCalled:47
+msgid "New user called"
+msgstr "新使用者åå­—"
+
+#: html/Admin/Queues/People.html:76 html/Ticket/Elements/EditPeople:50
+msgid "New watchers"
+msgstr "新視察員"
+
+#: NOT FOUND IN SOURCE
+msgid "New window setting"
+msgstr "更新視窗設定"
+
+#: NOT FOUND IN SOURCE
+msgid "New workflow"
+msgstr "新增æµç¨‹"
+
+#: html/Helpers/CalPopup.html:58 html/Ticket/Elements/Tabs:92
+msgid "Next"
+msgstr "下一項"
+
+#: html/Elements/TicketList:104
+msgid "Next Page"
+msgstr "下一é "
+
+#: NOT FOUND IN SOURCE
+msgid "Next page"
+msgstr "下一é "
+
+#: NOT FOUND IN SOURCE
+msgid "NickName"
+msgstr "暱稱"
+
+#: html/Admin/Users/Modify.html:84 html/User/Prefs.html:72
+msgid "Nickname"
+msgstr "暱稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Night Shift"
+msgstr "å°å¤œç­"
+
+#: NOT FOUND IN SOURCE
+msgid "No"
+msgstr "å¦"
+
+#: html/Admin/CustomFields/UserRights.html:145
+msgid "No Class defined"
+msgstr "尚未定義類別"
+
+#: html/Admin/CustomFields/Modify.html:166 html/Admin/Elements/EditCustomField:119
+msgid "No CustomField"
+msgstr "無自訂欄ä½"
+
+#: html/Admin/CustomFields/GroupRights.html:103
+msgid "No CustomField defined"
+msgstr "尚未定義自訂欄ä½"
+
+#: html/Admin/Groups/GroupRights.html:105 html/Admin/Groups/UserRights.html:92
+msgid "No Group defined"
+msgstr "尚未定義群組"
+
+#: lib/RT/Tickets_Overlay_SQL.pm:482
+msgid "No Query"
+msgstr "沒有查詢"
+
+#: html/Admin/Queues/GroupRights.html:118 html/Admin/Queues/UserRights.html:89
+msgid "No Queue defined"
+msgstr "尚未定義表單"
+
+#: bin/rt-crontool:73
+msgid "No RT user found. Please consult your RT administrator.\\n"
+msgstr "找ä¸åˆ° RT ä½¿ç”¨è€…ã€‚è«‹å‘ RT 管ç†å“¡æŸ¥è©¢ã€‚\\n"
+
+#: html/Admin/Global/Template.html:100 html/Admin/Queues/Template.html:97
+msgid "No Template"
+msgstr "沒有範本"
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket "
+msgstr "未指定申請單。退出申請單 "
+
+#: NOT FOUND IN SOURCE
+msgid "No Ticket specified. Aborting ticket modifications\\n\\n"
+msgstr "未指定申請單。退出申請單更改\\n\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "No Workflow"
+msgstr "沒有æµç¨‹"
+
+#: html/Approvals/Elements/Approve:77
+msgid "No action"
+msgstr "æš«ä¸è™•ç†"
+
+#: lib/RT/Record.pm:949
+msgid "No column specified"
+msgstr "未指定欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "No command found\\n"
+msgstr "找ä¸åˆ°å‘½ä»¤"
+
+#: html/Ticket/Elements/ShowRequestor:68
+msgid "No comment entered about this user"
+msgstr "沒有å°é€™å使用者的評論"
+
+#: NOT FOUND IN SOURCE
+msgid "No correspondence attached"
+msgstr "沒有附上申請單回覆"
+
+#: lib/RT/Action/Generic.pm:185 lib/RT/Condition/Generic.pm:197 lib/RT/Search/ActiveTicketsInQueue.pm:77 lib/RT/Search/Generic.pm:134 lib/RT/Search/Googleish.pm:78
+#. (ref $self)
+msgid "No description for %1"
+msgstr "æ²’æœ‰å° %1 çš„æè¿°"
+
+#: lib/RT/Users_Overlay.pm:190
+msgid "No group specified"
+msgstr "未指定群組"
+
+#: html/Admin/Groups/index.html:52
+msgid "No groups matching search criteria found."
+msgstr "找ä¸åˆ°ç¬¦åˆæŸ¥è©¢æ¢ä»¶çš„群組。"
+
+#: lib/RT/Ticket_Overlay.pm:2393
+msgid "No message attached"
+msgstr "沒有附上訊æ¯"
+
+#: lib/RT/User_Overlay.pm:1034
+msgid "No password set"
+msgstr "沒有設定密碼"
+
+#: lib/RT/Queue_Overlay.pm:361
+msgid "No permission to create queues"
+msgstr "沒有新增表單的權é™"
+
+#: lib/RT/Ticket_Overlay.pm:420
+#. ($QueueObj->Name)
+msgid "No permission to create tickets in the queue '%1'"
+msgstr "沒有在表單 '%1' 新增申請單的權é™"
+
+#: lib/RT/User_Overlay.pm:186
+msgid "No permission to create users"
+msgstr "沒有新增使用者的權é™"
+
+#: html/SelfService/Display.html:167
+msgid "No permission to display that ticket"
+msgstr "沒有顯示該申請單的權é™"
+
+#: lib/RT/SavedSearch.pm:156
+msgid "No permission to save system-wide searches"
+msgstr "沒有儲存全域é å­˜æŸ¥è©¢çš„權é™"
+
+#: html/SelfService/Update.html:117
+msgid "No permission to view update ticket"
+msgstr "沒有檢視申請單更新的權é™"
+
+#: lib/RT/Queue_Overlay.pm:795 lib/RT/Ticket_Overlay.pm:1489
+msgid "No principal specified"
+msgstr "未指定單ä½"
+
+#: html/Admin/Queues/People.html:175 html/Admin/Queues/People.html:185
+msgid "No principals selected."
+msgstr "未指定單ä½ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "No protocol specified in %1"
+msgstr "%1 內未指定å”定"
+
+#: html/Admin/Queues/index.html:57
+msgid "No queues matching search criteria found."
+msgstr "找ä¸åˆ°ç¬¦åˆæŸ¥è©¢æ¢ä»¶çš„表單。"
+
+#: html/Admin/Elements/SelectRights:106
+msgid "No rights found"
+msgstr "找ä¸åˆ°æ¬Šé™"
+
+#: html/Admin/Elements/SelectRights:53
+msgid "No rights granted."
+msgstr "沒有é¸å®šæ¬Šé™"
+
+#: lib/RT/SavedSearch.pm:196
+msgid "No search loaded"
+msgstr "尚未載入查詢"
+
+#: html/Search/Bulk.html:232
+msgid "No search to operate on."
+msgstr "沒有è¦é€²è¡Œçš„查詢"
+
+#: html/Elements/RT__Ticket/ColumnMap:137 html/Search/Results.rdf:78
+msgid "No subject"
+msgstr "沒有標題"
+
+#: NOT FOUND IN SOURCE
+msgid "No ticket id specified"
+msgstr "未指定申請單編號"
+
+#: lib/RT/Transaction_Overlay.pm:528 lib/RT/Transaction_Overlay.pm:565
+msgid "No transaction type specified"
+msgstr "未指定更動報告類別"
+
+#: NOT FOUND IN SOURCE
+msgid "No user or email address specified"
+msgstr "未指定使用者或電å­éƒµä»¶åœ°å€"
+
+#: html/Admin/Users/index.html:55
+msgid "No users matching search criteria found."
+msgstr "找ä¸åˆ°ç¬¦åˆæŸ¥è©¢æ¢ä»¶çš„使用者。"
+
+#: NOT FOUND IN SOURCE
+msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
+msgstr "找ä¸åˆ°åˆæ ¼çš„ RT 使用者。RT cvs 處ç†å™¨å·²åœç”¨ã€‚è«‹å‘ RT 管ç†è€…è©¢å•ã€‚\\n"
+
+#: lib/RT/Record.pm:946
+msgid "No value sent to _Set!\\n"
+msgstr "_Set 沒有收到任何值!\\n"
+
+#: html/Elements/QuickCreate:59
+msgid "Nobody"
+msgstr "沒有人"
+
+#: lib/RT/Record.pm:951
+msgid "Nonexistant field?"
+msgstr "欄ä½ä¸å­˜åœ¨ï¼Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "Normal Users"
+msgstr "一般用戶群組"
+
+#: html/Search/Chart:71 html/Search/Elements/Chart:88
+msgid "Not Set"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Not configured to fetch the content from a %1 in %2"
+msgstr "未設定æˆå¾ž %2 å…§æ“·å– %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Not logged in"
+msgstr "尚未登入"
+
+#: html/Elements/Header:96
+msgid "Not logged in."
+msgstr "尚未登入"
+
+#: lib/RT/Date.pm:397
+msgid "Not set"
+msgstr "尚未設定"
+
+#: html/NoAuth/Reminder.html:48
+msgid "Not yet implemented."
+msgstr "尚未完工。"
+
+#: NOT FOUND IN SOURCE
+msgid "Not yet implemented...."
+msgstr "尚未完工..."
+
+#: html/Approvals/Elements/Approve:81
+msgid "Notes"
+msgstr "備註"
+
+#: NOT FOUND IN SOURCE
+msgid "Notes:"
+msgstr "備註:"
+
+#: lib/RT/User_Overlay.pm:819
+msgid "Notification could not be sent"
+msgstr "無法é€å‡ºé€šçŸ¥"
+
+#: etc/initialdata:101
+msgid "Notify AdminCcs"
+msgstr "通知管ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:97
+msgid "Notify AdminCcs as Comment"
+msgstr "以評論方å¼é€šçŸ¥ç®¡ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:93 etc/upgrade/3.1.17/content:6
+msgid "Notify Ccs"
+msgstr "通知副本收件人"
+
+#: etc/initialdata:89 etc/upgrade/3.1.17/content:2
+msgid "Notify Ccs as Comment"
+msgstr "以評論方å¼é€šçŸ¥å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:128
+msgid "Notify Other Recipients"
+msgstr "通知其他收件人"
+
+#: etc/initialdata:124
+msgid "Notify Other Recipients as Comment"
+msgstr "以評論方å¼é€šçŸ¥å…¶ä»–收件人"
+
+#: etc/initialdata:85
+msgid "Notify Owner"
+msgstr "通知承辦人"
+
+#: etc/initialdata:81
+msgid "Notify Owner as Comment"
+msgstr "以評論方å¼é€šçŸ¥æ‰¿è¾¦äºº"
+
+#: etc/initialdata:376
+msgid "Notify Owner of their rejected ticket"
+msgstr "通知承辦人申請單已é§å›ž"
+
+#: etc/initialdata:365
+msgid "Notify Owner of their ticket has been approved by all approvers"
+msgstr "通知承辦人申請單已完æˆå…¨éƒ¨ç°½æ ¸"
+
+#: etc/initialdata:353
+msgid "Notify Owner of their ticket has been approved by some approver"
+msgstr "通知承辦人申請單已完æˆæŸé …簽核"
+
+#: etc/initialdata:334
+msgid "Notify Owners and AdminCcs of new items pending their approval"
+msgstr "æ•´ç†å¾…簽核事項,通知承辦人åŠç®¡ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:77
+msgid "Notify Requestors"
+msgstr "通知申請人"
+
+#: etc/initialdata:111
+msgid "Notify Requestors and Ccs"
+msgstr "通知申請人åŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:106
+msgid "Notify Requestors and Ccs as Comment"
+msgstr "以評論方å¼é€šçŸ¥ç”³è«‹äººåŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:120
+msgid "Notify Requestors, Ccs and AdminCcs"
+msgstr "通知申請人ã€å‰¯æœ¬åŠç®¡ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:116
+msgid "Notify Requestors, Ccs and AdminCcs as Comment"
+msgstr "以評論方å¼é€šçŸ¥ç”³è«‹äººã€å‰¯æœ¬åŠç®¡ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "Notify people:"
+msgstr "通知å°è±¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Nov"
+msgstr "å一月"
+
+#: lib/RT/Date.pm:451
+msgid "Nov."
+msgstr "11"
+
+#: NOT FOUND IN SOURCE
+msgid "November"
+msgstr "å一月"
+
+#: NOT FOUND IN SOURCE
+msgid "OIN104"
+msgstr "104eHRMS 介é¢"
+
+#: NOT FOUND IN SOURCE
+msgid "OK"
+msgstr "確定"
+
+#: html/Search/Elements/SelectAndOr:47
+msgid "OR"
+msgstr "OR"
+
+#: lib/RT/Record.pm:322
+msgid "Object could not be created"
+msgstr "無法新增物件"
+
+#: lib/RT/Record.pm:123
+msgid "Object could not be deleted"
+msgstr ""
+
+#: lib/RT/Record.pm:341
+msgid "Object created"
+msgstr "物件新增完畢"
+
+#: lib/RT/Record.pm:120
+msgid "Object deleted"
+msgstr ""
+
+#: html/Admin/CustomFields/Objects.html:72 html/Admin/Elements/ObjectCustomFields:63
+#. ($ObjectType)
+#. ($LookupType)
+msgid "Object of type %1 cannot take custom fields"
+msgstr "自訂欄ä½ä¸é©ç”¨æ–¼é¡žåˆ¥ç‚º %1 的物件"
+
+#: lib/RT/CustomField_Overlay.pm:967
+msgid "Object type mismatch"
+msgstr "物件類別ä¸ç¬¦"
+
+#: NOT FOUND IN SOURCE
+msgid "Occupation Status"
+msgstr "在è·ç‹€æ…‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Oct"
+msgstr "å月"
+
+#: lib/RT/Date.pm:450
+msgid "Oct."
+msgstr "10"
+
+#: NOT FOUND IN SOURCE
+msgid "October"
+msgstr "å月"
+
+#: NOT FOUND IN SOURCE
+msgid "Office Phone"
+msgstr "辦公室電話"
+
+#: html/Tools/Elements/Tabs:55
+msgid "Offline"
+msgstr "離線"
+
+#: html/Tools/Offline.html:49
+msgid "Offline edits"
+msgstr "離線編輯"
+
+#: html/Tools/Offline.html:46
+msgid "Offline upload"
+msgstr "離線上載"
+
+#: html/Elements/SelectDateRelation:56
+msgid "On"
+msgstr "等於"
+
+#: lib/RT/Transaction_Overlay.pm:326
+#. ($self->CreatedAsString(), $self->CreatorObj->Name())
+msgid "On %1, %2 wrote:"
+msgstr "在 %1 時,%2 寫到:"
+
+#: NOT FOUND IN SOURCE
+msgid "On Change"
+msgstr "更改申請單時"
+
+#: etc/initialdata:163
+msgid "On Comment"
+msgstr "評論時"
+
+#: etc/initialdata:156
+msgid "On Correspond"
+msgstr "回覆申請單時"
+
+#: etc/initialdata:145
+msgid "On Create"
+msgstr "新增申請單時"
+
+#: etc/initialdata:184
+msgid "On Owner Change"
+msgstr "承辦人改變時"
+
+#: etc/initialdata:177 etc/upgrade/3.1.17/content:15
+msgid "On Priority Change"
+msgstr "優先順ä½æ”¹è®Šæ™‚"
+
+#: etc/initialdata:192
+msgid "On Queue Change"
+msgstr "表單改變時"
+
+#: etc/initialdata:198
+msgid "On Resolve"
+msgstr "解決申請單時"
+
+#: etc/initialdata:169
+msgid "On Status Change"
+msgstr "ç¾æ³æ”¹è®Šæ™‚"
+
+#: etc/initialdata:150
+msgid "On Transaction"
+msgstr "發生更動時"
+
+#: html/Approvals/Elements/PendingMyApproval:70
+#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter' id='CreatedAfter' />")
+msgid "Only show approvals for requests created after %1"
+msgstr "僅顯示 %1 之後新增的申請單"
+
+#: html/Approvals/Elements/PendingMyApproval:68
+#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore' id='CreatedBefore' />")
+msgid "Only show approvals for requests created before %1"
+msgstr "僅顯示 %1 之å‰æ–°å¢žçš„申請單"
+
+#: html/Admin/CustomFields/index.html:75
+msgid "Only show custom fields for:"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Open"
+msgstr "é–‹å•Ÿ"
+
+#: html/SelfService/index.html:46
+msgid "Open Tickets"
+msgstr ""
+
+#: html/Ticket/Elements/Tabs:160
+msgid "Open it"
+msgstr "é–‹å•Ÿ"
+
+#: html/SelfService/Elements/Tabs:75
+msgid "Open tickets"
+msgstr "開啟的申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in a new window"
+msgstr "在新視窗開啟(列表的)申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Open tickets (from listing) in another window"
+msgstr "在å¦ä¸€å€‹è¦–窗開啟(列表的)申請單"
+
+#: etc/initialdata:140
+msgid "Open tickets on correspondence"
+msgstr "收到回覆時å³é–‹å•Ÿç”³è«‹å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Opened Tickets"
+msgstr "已申請é‹è¡Œä¸­è¡¨å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Opinion"
+msgstr "æ„見"
+
+#: NOT FOUND IN SOURCE
+msgid "Option Description"
+msgstr "é¸é …æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Option Name"
+msgstr "é¸é …å稱"
+
+#: html/Prefs/MyRT.html:70
+msgid "Options"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:59
+msgid "Order by"
+msgstr "排åºæ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Ordering and sorting"
+msgstr "é †åºèˆ‡æŽ’åºæ–¹å¼"
+
+#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:129
+msgid "Organization"
+msgstr "組織å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Organization:"
+msgstr "組織:"
+
+#: html/Approvals/Elements/Approve:53
+#. ($approving->Id, $approving->Subject)
+msgid "Originating ticket: #%1"
+msgstr "原申請單:#%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Other comma-delimited email addresses"
+msgstr "其他e-mail帳號 (僅e-mail通知;多筆帳號請用逗號','å€éš”)"
+
+#: NOT FOUND IN SOURCE
+msgid "Out of range"
+msgstr "期é™å¤–"
+
+#: lib/RT/Transaction_Overlay.pm:622
+msgid "Outgoing email about a comment recorded"
+msgstr "已紀錄發é€çš„評論郵件"
+
+#: lib/RT/Transaction_Overlay.pm:626
+msgid "Outgoing email recorded"
+msgstr "已紀錄發é€çš„郵件"
+
+#: html/Admin/Queues/Modify.html:90
+msgid "Over time, priority moves toward"
+msgstr "優先順ä½éš¨æ™‚間增加調整為"
+
+#: NOT FOUND IN SOURCE
+msgid "Override current custom fields with fields from %1"
+msgstr "以 %1 表單的自訂欄ä½å–代ç¾æœ‰æ¬„ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Override global rights"
+msgstr "å–代全域權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "OverrideGlobalACL status %1"
+msgstr "å–ä»£å…¨åŸŸæ¬Šé™ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Overview"
+msgstr "總覽"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "Own tickets"
+msgstr "承辦申請單"
+
+#: lib/RT/Queue_Overlay.pm:112
+msgid "OwnTicket"
+msgstr "承辦申請單"
+
+#: etc/initialdata:38 html/Elements/QuickCreate:56 html/Search/Elements/PickBasics:101 html/Ticket/Create.html:72 html/Ticket/Elements/EditBasics:61 html/Ticket/Elements/EditPeople:64 html/Ticket/Elements/EditPeople:65 html/Ticket/Elements/Reminders:129 html/Ticket/Elements/ShowPeople:48 html/Ticket/Update.html:62 lib/RT/ACE_Overlay.pm:110 lib/RT/Tickets_Overlay.pm:2006
+msgid "Owner"
+msgstr "承辦人"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner changed from %1 to %2"
+msgstr "承辦人已從 %1 改為 %2"
+
+#: lib/RT/Ticket_Overlay.pm:505
+msgid "Owner could not be set."
+msgstr "無法設定承辦人。"
+
+#: lib/RT/Transaction_Overlay.pm:672
+#. ($Old->Name , $New->Name)
+msgid "Owner forcibly changed from %1 to %2"
+msgstr "強制將承辦人從 %1 改為 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner is"
+msgstr "承辦人"
+
+#: NOT FOUND IN SOURCE
+msgid "Owner's Phone"
+msgstr "承辦人電話"
+
+#: NOT FOUND IN SOURCE
+msgid "Page #"
+msgstr " "
+
+#: html/Elements/TicketList:78
+#. ($Page, int($TotalFound/$Rows)+$oddRows)
+msgid "Page %1 of %2"
+msgstr "第 %1/%2 é "
+
+#: html/Admin/Users/Modify.html:198 html/User/Prefs.html:96
+msgid "Pager"
+msgstr "呼å«å™¨"
+
+#: NOT FOUND IN SOURCE
+msgid "PagerPhone"
+msgstr "呼å«å™¨è™Ÿç¢¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Parameter"
+msgstr "呼å«åƒæ•¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Parent"
+msgstr "上級"
+
+#: html/Elements/EditLinks:144 html/Elements/EditLinks:76 html/Elements/ShowLinks:68 html/Ticket/Create.html:222 html/Ticket/Elements/BulkLinks:60
+msgid "Parents"
+msgstr "æ¯ç”³è«‹å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Park Space"
+msgstr "åœè»Šä½ç”³è«‹"
+
+#: html/Elements/Login:95 html/User/Prefs.html:105
+msgid "Password"
+msgstr "密碼"
+
+#: html/NoAuth/Reminder.html:46
+msgid "Password Reminder"
+msgstr "密碼æ示"
+
+#: lib/RT/Transaction_Overlay.pm:781 lib/RT/User_Overlay.pm:1045
+msgid "Password changed"
+msgstr ""
+
+#: lib/RT/User_Overlay.pm:1037 lib/RT/User_Overlay.pm:214
+#. ($RT::MinimumPasswordLength)
+msgid "Password needs to be at least %1 characters long"
+msgstr "密碼長度至少必須為 %1 個字元"
+
+#: lib/RT/User_Overlay.pm:1044
+msgid "Password set"
+msgstr "密碼已設定"
+
+#: NOT FOUND IN SOURCE
+msgid "Password too short"
+msgstr "密碼太短"
+
+#: html/User/Prefs.html:240
+#. (loc_fuzzy($msg))
+msgid "Password: %1"
+msgstr "密碼:%1"
+
+#: lib/RT/User_Overlay.pm:1030
+msgid "Password: Permission Denied"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:364
+msgid "Passwords do not match."
+msgstr "密碼確èªå¤±æ•—。"
+
+#: html/User/Prefs.html:242
+msgid "Passwords do not match. Your password has not been changed"
+msgstr "密碼確èªå¤±æ•—。您的密碼並未改變。"
+
+#: NOT FOUND IN SOURCE
+msgid "Pelase select a queue"
+msgstr "è«‹é¸æ“‡è¡¨å–®å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Pending Approval"
+msgstr "等待簽核"
+
+#: html/Ticket/Elements/ShowSummary:62 html/Ticket/Elements/Tabs:119 html/Ticket/ModifyAll.html:72
+msgid "People"
+msgstr "人員"
+
+#: NOT FOUND IN SOURCE
+msgid "People with Queue Rights"
+msgstr "æ“有表單權é™äººå“¡"
+
+#: etc/initialdata:133
+msgid "Perform a user-defined action"
+msgstr "執行使用者自訂的動作"
+
+#: html/Admin/Tools/Configuration.html:94
+msgid "Perl configuration"
+msgstr "Perl 設定"
+
+#: lib/RT/ACE_Overlay.pm:251 lib/RT/ACE_Overlay.pm:257 lib/RT/ACE_Overlay.pm:580 lib/RT/ACE_Overlay.pm:590 lib/RT/ACE_Overlay.pm:600 lib/RT/ACE_Overlay.pm:665 lib/RT/Attribute_Overlay.pm:158 lib/RT/Attribute_Overlay.pm:164 lib/RT/Attribute_Overlay.pm:405 lib/RT/Attribute_Overlay.pm:414 lib/RT/Attribute_Overlay.pm:427 lib/RT/CurrentUser.pm:116 lib/RT/CurrentUser.pm:125 lib/RT/CustomField_Overlay.pm:1017 lib/RT/CustomField_Overlay.pm:1138 lib/RT/CustomField_Overlay.pm:1281 lib/RT/CustomField_Overlay.pm:172 lib/RT/CustomField_Overlay.pm:189 lib/RT/CustomField_Overlay.pm:200 lib/RT/CustomField_Overlay.pm:374 lib/RT/CustomField_Overlay.pm:403 lib/RT/CustomField_Overlay.pm:763 lib/RT/CustomField_Overlay.pm:936 lib/RT/CustomField_Overlay.pm:971 lib/RT/Group_Overlay.pm:1117 lib/RT/Group_Overlay.pm:1121 lib/RT/Group_Overlay.pm:1130 lib/RT/Group_Overlay.pm:1240 lib/RT/Group_Overlay.pm:1244 lib/RT/Group_Overlay.pm:1250 lib/RT/Group_Overlay.pm:445 lib/RT/Group_Overlay.pm:542 lib/RT/Group_Overlay.pm:620 lib/RT/Group_Overlay.pm:628 lib/RT/Group_Overlay.pm:726 lib/RT/Group_Overlay.pm:730 lib/RT/Group_Overlay.pm:736 lib/RT/Group_Overlay.pm:922 lib/RT/Group_Overlay.pm:926 lib/RT/Group_Overlay.pm:939 lib/RT/Queue_Overlay.pm:1054 lib/RT/Queue_Overlay.pm:140 lib/RT/Queue_Overlay.pm:158 lib/RT/Queue_Overlay.pm:657 lib/RT/Queue_Overlay.pm:667 lib/RT/Queue_Overlay.pm:681 lib/RT/Queue_Overlay.pm:819 lib/RT/Queue_Overlay.pm:828 lib/RT/Queue_Overlay.pm:841 lib/RT/Scrip_Overlay.pm:149 lib/RT/Scrip_Overlay.pm:160 lib/RT/Scrip_Overlay.pm:224 lib/RT/Scrip_Overlay.pm:538 lib/RT/Template_Overlay.pm:108 lib/RT/Template_Overlay.pm:277 lib/RT/Ticket_Overlay.pm:1357 lib/RT/Ticket_Overlay.pm:1367 lib/RT/Ticket_Overlay.pm:1381 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1532 lib/RT/Ticket_Overlay.pm:1546 lib/RT/Ticket_Overlay.pm:1663 lib/RT/Ticket_Overlay.pm:1983 lib/RT/Ticket_Overlay.pm:2126 lib/RT/Ticket_Overlay.pm:2296 lib/RT/Ticket_Overlay.pm:2346 lib/RT/Ticket_Overlay.pm:2525 lib/RT/Ticket_Overlay.pm:2538 lib/RT/Ticket_Overlay.pm:2614 lib/RT/Ticket_Overlay.pm:2627 lib/RT/Ticket_Overlay.pm:2748 lib/RT/Ticket_Overlay.pm:2762 lib/RT/Ticket_Overlay.pm:2990 lib/RT/Ticket_Overlay.pm:3000 lib/RT/Ticket_Overlay.pm:3005 lib/RT/Ticket_Overlay.pm:3224 lib/RT/Ticket_Overlay.pm:3228 lib/RT/Ticket_Overlay.pm:3371 lib/RT/Ticket_Overlay.pm:3497 lib/RT/Transaction_Overlay.pm:516 lib/RT/Transaction_Overlay.pm:523 lib/RT/Transaction_Overlay.pm:551 lib/RT/Transaction_Overlay.pm:558 lib/RT/User_Overlay.pm:1176 lib/RT/User_Overlay.pm:1856 lib/RT/User_Overlay.pm:369 lib/RT/User_Overlay.pm:735 lib/RT/User_Overlay.pm:774
+msgid "Permission Denied"
+msgstr "權é™ä¸è¶³"
+
+#: NOT FOUND IN SOURCE
+msgid "Permission Settings"
+msgstr "權é™è¨­å®š"
+
+#: lib/RT/Template_Overlay.pm:238 lib/RT/Template_Overlay.pm:247
+msgid "Permission denied"
+msgstr ""
+
+#: lib/RT/Template_Overlay.pm:372
+msgid "Permissions denied"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Permitted Queues:"
+msgstr "æ“有權é™è¡¨å–®åˆ—表:"
+
+#: NOT FOUND IN SOURCE
+msgid "Personal"
+msgstr "代ç†äººç¾¤çµ„"
+
+#: html/User/Elements/Tabs:56
+msgid "Personal Groups"
+msgstr "代ç†äººç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "Personal Homepage"
+msgstr "個人首é "
+
+#: NOT FOUND IN SOURCE
+msgid "Personal Todo"
+msgstr "ç§äººå¾…辦事項"
+
+#: html/User/Groups/index.html:51 html/User/Groups/index.html:61
+msgid "Personal groups"
+msgstr "代ç†äººç¾¤çµ„"
+
+#: html/User/Elements/DelegateRights:58
+msgid "Personal groups:"
+msgstr "代ç†äººç¾¤çµ„:"
+
+#: NOT FOUND IN SOURCE
+msgid "PersonalHomepage"
+msgstr "個人首é "
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 1: Create/Rename Groups (%1)"
+msgstr "第一階段:群組建立åŠæ”¹å (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 2: Disable/Enable Groups (%1)"
+msgstr "第二階段:群組åœç”¨åŠå•Ÿç”¨ (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 3: Create/Rename Users (%1)"
+msgstr "第三階段:使用者建立åŠæ”¹å (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phase 4: Disable/Enable Users (%1)"
+msgstr "第四階段:使用者åœç”¨åŠå•Ÿç”¨ (%1)"
+
+#: NOT FOUND IN SOURCE
+msgid "Phone"
+msgstr "電話"
+
+#: NOT FOUND IN SOURCE
+msgid "Phone number"
+msgstr "電話號碼"
+
+#: html/Admin/Users/Modify.html:180 html/User/Prefs.html:81
+msgid "Phone numbers"
+msgstr "電話號碼"
+
+#: NOT FOUND IN SOURCE
+msgid "Pick"
+msgstr "挑é¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Place of Departure"
+msgstr "出發地點"
+
+#: NOT FOUND IN SOURCE
+msgid "Placeholder"
+msgstr "尚未完工"
+
+#: NOT FOUND IN SOURCE
+msgid "Please Select"
+msgstr "è«‹é¸æ“‡"
+
+#: NOT FOUND IN SOURCE
+msgid "Please check items to be deleted first."
+msgstr "è«‹å…ˆé¸ä¸­è¦åˆªé™¤çš„å°è±¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select a group"
+msgstr "è«‹é¸æ“‡ç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select a queue's workflow"
+msgstr "è«‹é¸æ“‡è¡¨å–®æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select one of the category types above."
+msgstr "請從上é¢é¸æ“‡ä¸€é …分類。"
+
+#: NOT FOUND IN SOURCE
+msgid "Please select role"
+msgstr "è«‹é¸æ“‡è§’色"
+
+#: NOT FOUND IN SOURCE
+msgid "Policy"
+msgstr "經營è¦ç« "
+
+#: NOT FOUND IN SOURCE
+msgid "Position"
+msgstr "è·å‹™"
+
+#: NOT FOUND IN SOURCE
+msgid "Position Level"
+msgstr "è·ç­‰"
+
+#: NOT FOUND IN SOURCE
+msgid "Position Name"
+msgstr "è·å‹™å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Position Number"
+msgstr "è·å‹™ä»£ç¢¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Position Rank"
+msgstr "è·ç´š"
+
+#: NOT FOUND IN SOURCE
+msgid "Pref"
+msgstr "å好"
+
+#: html/Elements/Header:93 html/Elements/Tabs:91 html/SelfService/Elements/Tabs:95 html/SelfService/Prefs.html:46 html/User/Prefs.html:46 html/User/Prefs.html:49
+msgid "Preferences"
+msgstr "å好"
+
+#: html/Admin/Users/MyRT.html:75
+#. ($pane, $UserObj->Name)
+msgid "Preferences %1 for user %2 ."
+msgstr "使用者 %2 çš„ %1 å好。"
+
+#: html/Prefs/MyRT.html:141
+#. ($pane)
+msgid "Preferences saved for %1."
+msgstr "æˆåŠŸå„²å­˜ %1 çš„å好。"
+
+#: NOT FOUND IN SOURCE
+msgid "Prefs"
+msgstr "個人資訊"
+
+#: lib/RT/Action/Generic.pm:195
+msgid "Prepare Stubbed"
+msgstr "é å‚™å‹•ä½œå®Œç•¢"
+
+#: html/Helpers/CalPopup.html:56 html/Ticket/Elements/Tabs:84
+msgid "Prev"
+msgstr "上一項"
+
+#: html/Elements/TicketList:101
+msgid "Previous Page"
+msgstr "上一é "
+
+#: NOT FOUND IN SOURCE
+msgid "Previous page"
+msgstr "å‰ä¸€é "
+
+#: NOT FOUND IN SOURCE
+msgid "Pri"
+msgstr "優先順ä½"
+
+#: lib/RT/ACE_Overlay.pm:157 lib/RT/ACE_Overlay.pm:239 lib/RT/ACE_Overlay.pm:569
+#. ($args{'PrincipalId'})
+msgid "Principal %1 not found."
+msgstr "找ä¸åˆ°å–®ä½ %1。"
+
+#: html/Search/Elements/PickBasics:147 html/Ticket/Create.html:181 html/Ticket/Elements/EditBasics:92 html/Ticket/Elements/ShowBasics:72 lib/RT/Tickets_Overlay.pm:1790
+msgid "Priority"
+msgstr "優先順ä½"
+
+#: html/Admin/Queues/Modify.html:86
+msgid "Priority starts at"
+msgstr "優先順ä½èµ·å§‹å€¼"
+
+#: html/Search/Elements/EditSearches:50
+msgid "Privacy:"
+msgstr "éš±ç§è¨­å®šï¼š"
+
+#: etc/initialdata:25
+msgid "Privileged"
+msgstr "內部æˆå“¡"
+
+#: html/Admin/Users/Modify.html:342 html/User/Prefs.html:231
+#. (loc_fuzzy($msg))
+msgid "Privileged status: %1"
+msgstr "內部æˆå“¡ç‹€æ…‹ï¼š%1"
+
+#: html/Admin/Users/index.html:102
+msgid "Privileged users"
+msgstr "內部æˆå“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Process Status"
+msgstr "處ç†ç‹€æ…‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Project"
+msgstr "專案"
+
+#: NOT FOUND IN SOURCE
+msgid "Project Name"
+msgstr "專案å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Projects"
+msgstr "專案"
+
+#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
+msgid "Pseudogroup for internal use"
+msgstr "內部用的虛擬群組"
+
+#: NOT FOUND IN SOURCE
+msgid "Public Description"
+msgstr "公開說明"
+
+#: NOT FOUND IN SOURCE
+msgid "Public Info"
+msgstr "公開資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "Public Service"
+msgstr "公共事務å€"
+
+#: NOT FOUND IN SOURCE
+msgid "Purging stale data: %1"
+msgstr "移除éŽæœŸè³‡æ–™: %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Query"
+msgstr "查詢"
+
+#: html/Search/Build.html:121
+msgid "Query Builder"
+msgstr "建立查詢"
+
+#: html/Search/Elements/Chart:101
+msgid "Query:"
+msgstr ""
+
+#: html/Elements/QueueSummary:48 html/Elements/QuickCreate:54 html/Search/Elements/PickBasics:71 html/SelfService/Create.html:54 html/Ticket/Create.html:62 html/Ticket/Elements/EditBasics:57 html/Ticket/Elements/ShowBasics:76 html/Tools/Reports/CreatedByDates.html:85 html/Tools/Reports/ResolvedByDates.html:86 html/Tools/Reports/ResolvedByOwner.html:66 html/User/Elements/DelegateRights:101 lib/RT/Tickets_Overlay.pm:1617
+msgid "Queue"
+msgstr "表單"
+
+#: html/Admin/Queues/CustomField.html:63 html/Admin/Queues/Scrip.html:61 html/Admin/Queues/Scrips.html:69 html/Admin/Queues/Templates.html:65
+#. ($Queue)
+#. ($id)
+msgid "Queue %1 not found"
+msgstr "找ä¸åˆ°è¡¨å–® %1"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue '%1' not found\\n"
+msgstr "找ä¸åˆ°è¡¨å–® '%1'\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Keyword Selections"
+msgstr "表單關éµå­—é¸å–"
+
+#: html/Admin/Queues/Modify.html:64
+msgid "Queue Name"
+msgstr "表單å稱"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Owner"
+msgstr "業務承辦人"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Priority"
+msgstr "優先等級"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Rights"
+msgstr "表單權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Scrips"
+msgstr "表單手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue Setup"
+msgstr "表單設定"
+
+#: lib/RT/Queue_Overlay.pm:365
+msgid "Queue already exists"
+msgstr "表單已存在"
+
+#: lib/RT/Queue_Overlay.pm:374 lib/RT/Queue_Overlay.pm:380
+msgid "Queue could not be created"
+msgstr "無法新增表單"
+
+#: html/Ticket/Create.html:244 lib/t/regression/01ticket_link_searching.t:17
+msgid "Queue could not be loaded."
+msgstr "無法載入表單"
+
+#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:384 lib/RT/StyleGuide.pod:809
+msgid "Queue created"
+msgstr "表單新增完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "Queue is not specified."
+msgstr "未指定表單。"
+
+#: html/SelfService/Display.html:126 lib/RT/CustomField_Overlay.pm:197
+msgid "Queue not found"
+msgstr "找ä¸åˆ°è¡¨å–®"
+
+#: html/Admin/Elements/Tabs:59 html/Admin/index.html:72
+msgid "Queues"
+msgstr "表單"
+
+#: html/Elements/MyAdminQueues:46
+msgid "Queues I administer"
+msgstr ""
+
+#: html/Elements/MySupportQueues:46
+msgid "Queues I'm an AdminCc for"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Quick Search"
+msgstr "表單ç¾æ³"
+
+#: html/Elements/Quicksearch:47 html/Prefs/Elements/Tabs:58 html/Prefs/Quicksearch.html:70
+msgid "Quick search"
+msgstr "表單一覽"
+
+#: html/Elements/QuickCreate:47
+msgid "Quick ticket creation"
+msgstr "快速建立申請單"
+
+#: html/Search/Results.html:81
+msgid "RSS"
+msgstr "RSS"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1"
+msgstr "RT %1"
+
+#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:796
+#. ($RT::VERSION, $RT::rtname)
+msgid "RT %1 for %2"
+msgstr "%2:RT %1 版"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
+msgstr "RT %1 版,<a href=\"http://bestpractical.com\">Best Practical Solutions å…¬å¸</a>出å“。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1。版權所有 1996-%1 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "RT %1. Copyright 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+msgstr "RT %1。版權所有 1996-2002 Jesse Vincent <jesse\\@bestpractical.com>\\n"
+
+#: html/Admin/index.html:46 html/Admin/index.html:47
+msgid "RT Administration"
+msgstr "RT 管ç†é é¢"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Authentication error."
+msgstr "RT èªè­‰éŒ¯èª¤ã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Bounce: %1"
+msgstr "RT 退信:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Configuration error"
+msgstr "RT 設定錯誤"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Critical error. Message not recorded!"
+msgstr "RT 致命錯誤。訊æ¯æœªè¢«ç´€éŒ„。"
+
+#: html/Elements/Error:63 html/SelfService/Error.html:62
+msgid "RT Error"
+msgstr "RT 錯誤"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Received mail (%1) from itself."
+msgstr "RT 收到從自己寄出的郵件 (%1)。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Recieved mail (%1) from itself."
+msgstr "RT 收到從自己寄出的郵件 (%1)。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT Self Service / Closed Tickets"
+msgstr "RT 自助æœå‹™/已解決的申請單"
+
+#: html/Admin/Tools/Configuration.html:73
+msgid "RT Variables"
+msgstr "RT 的變數"
+
+#: html/Admin/Elements/SystemTabs:71 html/Admin/Elements/UserTabs:67 html/Admin/Global/MyRT.html:1 html/Admin/Global/MyRT.html:12 html/Admin/Global/MyRT.html:4 html/Admin/Global/index.html:84 html/Admin/Users/MyRT.html:21 html/Prefs/MyRT.html:66 html/Prefs/MyRT.html:78 html/User/Elements/Tabs:65 html/index.html:1 html/index.html:75
+msgid "RT at a glance"
+msgstr "RT 一覽"
+
+#: html/Admin/Users/MyRT.html:30
+#. ($UserObj->Name)
+msgid "RT at a glance for the user %1"
+msgstr "使用者 %1 的 RT 一覽"
+
+#: html/Admin/CustomFields/Modify.html:117
+msgid "RT can include content from another web service when showing this custom field."
+msgstr "RT å¯æ–¼é¡¯ç¤ºæ­¤è‡ªè¨‚欄ä½æ™‚引入其他網站的內容"
+
+#: html/Admin/CustomFields/Modify.html:106
+msgid "RT can make this custom field's values into hyperlinks to another service."
+msgstr "RT å¯å°‡æ­¤è‡ªè¨‚欄ä½çš„值視為連往其他網站的超éˆçµ"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't authenticate you"
+msgstr "RT 無法èªè­‰æ‚¨çš„身份"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find requestor via its external database lookup"
+msgstr "RT 無法從外部資料庫查詢找到申請人資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't find the queue: %1"
+msgstr "RT 找ä¸åˆ°è¡¨å–®ï¼š%1"
+
+#: html/Elements/SetupSessionCookie:100
+msgid "RT couldn't store your session."
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "RT couldn't validate this PGP signature. \\n"
+msgstr "RT 無法確èªé€™å€‹ PGP 簽章。\\n"
+
+#: html/Elements/Logo:49 html/Elements/PageLayout:172
+#. ($RT::rtname)
+msgid "RT for %1"
+msgstr "%1 專用æµç¨‹ç³»çµ±"
+
+#: NOT FOUND IN SOURCE
+msgid "RT for %1: %2"
+msgstr "%1 專用 RT 系統:%2"
+
+#: NOT FOUND IN SOURCE
+msgid "RT has proccessed your commands"
+msgstr "RT 已執行您的命令"
+
+#: NOT FOUND IN SOURCE
+msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
+msgstr "RT 版權所有 1996-%1 Jesse Vincent &lt;jesse@bestpractical.com&gt;。<br>æœ¬è»Ÿé«”ä¾ <a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU 通用公共授權第二版</a> 散佈。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT thinks this message may be a bounce"
+msgstr "RT èªç‚ºé€™å¯èƒ½æ˜¯é€€ä¿¡"
+
+#: html/Search/Simple.html:58
+msgid "RT will look for anything else you enter in ticket subjects."
+msgstr "RT 會在申請單主旨內æœå°‹å°‡æ‚¨éµå…¥çš„任何其他字樣"
+
+#: NOT FOUND IN SOURCE
+msgid "RT will process this message as if it were unsigned.\\n"
+msgstr "RT 以未簽章方å¼è™•ç†é€™å°éƒµä»¶ã€‚\\n"
+
+#: html/Admin/CustomFields/Modify.html:108 html/Admin/CustomFields/Modify.html:119
+msgid "RT will replace <tt>__id__</tt> and <tt>__CustomField__</tt> with the record id and custom field value, respectively"
+msgstr "RT 會將 <tt>__id__</tt> åŠ <tt>__CustomField__</tt> ç½®æ›æˆç´€éŒ„編號åŠè‡ªè¨‚欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "RT's email command mode requires PGP authentication. Either you didn't sign your message, or your signature could not be verified."
+msgstr "RT çš„é›»å­éƒµä»¶å‘½ä»¤æ¨¡å¼é ˆè¦ PGP èªè­‰ã€‚您å¯èƒ½æ²’有簽章,或是您的簽章無法辨識。"
+
+#: NOT FOUND IN SOURCE
+msgid "RT::Queue-Role"
+msgstr "表單é‹è¡Œè§’色"
+
+#: NOT FOUND IN SOURCE
+msgid "RT::System-Role"
+msgstr "系統é‹è¡Œè§’色"
+
+#: NOT FOUND IN SOURCE
+msgid "RT::Ticket-Role"
+msgstr "申請單é‹è¡Œè§’色"
+
+#: NOT FOUND IN SOURCE
+msgid "RT_System"
+msgstr "系統訊æ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "Read Only"
+msgstr "唯讀"
+
+#: html/Admin/Users/Modify.html:79 html/User/Prefs.html:69
+msgid "Real Name"
+msgstr "真實姓å"
+
+#: NOT FOUND IN SOURCE
+msgid "RealName"
+msgstr "真實姓å"
+
+#: NOT FOUND IN SOURCE
+msgid "Really reject this ticket?"
+msgstr "您確定è¦é§å›žé€™å¼µç”³è«‹å–®å—Žï¼Ÿ"
+
+#: lib/RT/Transaction_Overlay.pm:725
+#. ($value)
+msgid "Reference by %1 added"
+msgstr "已加入 %1 為åƒè€ƒæœ¬ç”³è«‹å–®"
+
+#: lib/RT/Transaction_Overlay.pm:765
+#. ($value)
+msgid "Reference by %1 deleted"
+msgstr "已移除 %1 為åƒè€ƒæœ¬ç”³è«‹å–®"
+
+#: lib/RT/Transaction_Overlay.pm:722
+#. ($value)
+msgid "Reference to %1 added"
+msgstr "已加入åƒè€ƒç”³è«‹å–® %1"
+
+#: lib/RT/Transaction_Overlay.pm:762
+#. ($value)
+msgid "Reference to %1 deleted"
+msgstr "已移除åƒè€ƒç”³è«‹å–® %1"
+
+#: html/Elements/EditLinks:103 html/Elements/EditLinks:156 html/Elements/ShowLinks:92 html/Ticket/Create.html:225 html/Ticket/Elements/BulkLinks:72
+msgid "Referred to by"
+msgstr "被åƒè€ƒ"
+
+#: html/Elements/EditLinks:152 html/Elements/EditLinks:94 html/Elements/SelectLinkType:49 html/Elements/ShowLinks:82 html/Ticket/Create.html:224 html/Ticket/Elements/BulkLinks:68
+msgid "Refers to"
+msgstr "åƒè€ƒ"
+
+#: NOT FOUND IN SOURCE
+msgid "RefersTo"
+msgstr "åƒè€ƒ"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine"
+msgstr "在çµæžœç¯„åœå…§æŸ¥è©¢"
+
+#: NOT FOUND IN SOURCE
+msgid "Refine search"
+msgstr "調整查詢æ¢ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Refresh"
+msgstr "æ›´æ–°"
+
+#: html/Elements/Refresh:57
+#. ($value/60)
+msgid "Refresh this page every %1 minutes."
+msgstr "æ¯ %1 分é˜æ›´æ–°é é¢"
+
+#: lib/RT/Transaction_Overlay.pm:811
+#. ($ticket->Subject)
+msgid "Reminder '%1' added"
+msgstr "已建立æ醒項目「%1ã€"
+
+#: lib/RT/Transaction_Overlay.pm:824
+#. ($ticket->Subject)
+msgid "Reminder '%1' completed"
+msgstr "已完æˆæ醒項目「%1ã€"
+
+#: lib/RT/Transaction_Overlay.pm:817
+#. ($ticket->Subject)
+msgid "Reminder '%1' reopened"
+msgstr "å·²é‡æ–°é–‹å•Ÿæ醒項目「%1ã€"
+
+#: html/Ticket/Reminders.html:46
+#. ($Ticket->Id)
+msgid "Reminder ticket #%1"
+msgstr "æ醒項目 #%1"
+
+#: html/Elements/MyReminders:48 html/Ticket/Elements/ShowSummary:75 html/Ticket/Elements/Tabs:122 html/Ticket/Reminders.html:52
+msgid "Reminders"
+msgstr ""
+
+#: html/Ticket/Reminders.html:50
+#. ($Ticket->Id)
+msgid "Reminders for ticket #%1"
+msgstr "申請單 #%1 çš„æ醒項目"
+
+#: NOT FOUND IN SOURCE
+msgid "Remove"
+msgstr "移除"
+
+#: html/Search/Bulk.html:94
+msgid "Remove AdminCc"
+msgstr "移除管ç†å“¡å‰¯æœ¬"
+
+#: html/Search/Bulk.html:90
+msgid "Remove Cc"
+msgstr "移除副本"
+
+#: html/Search/Bulk.html:86
+msgid "Remove Requestor"
+msgstr "移除申請人"
+
+#: html/Ticket/Elements/ShowTransaction:179 html/Ticket/Elements/Tabs:147
+msgid "Reply"
+msgstr "回覆"
+
+#: html/Admin/Queues/Modify.html:72
+msgid "Reply Address"
+msgstr "回覆地å€"
+
+#: html/Search/Bulk.html:129 html/Ticket/ModifyAll.html:94 html/Ticket/Update.html:78
+msgid "Reply to requestors"
+msgstr "回覆申請人"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "Reply to tickets"
+msgstr "å°ç”³è«‹å–®é€²è¡Œå›žè¦†"
+
+#: lib/RT/Queue_Overlay.pm:110
+msgid "ReplyToTicket"
+msgstr "回覆申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Report to Duty"
+msgstr "上下ç­åˆ·å¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Reported on"
+msgstr "到è·æ—¥æœŸ"
+
+#: html/Tools/Elements/Tabs:59 html/Tools/Reports/index.html:46 html/Tools/Reports/index.html:47
+msgid "Reports"
+msgstr ""
+
+#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:111
+msgid "Requestor"
+msgstr "申請人"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor email address"
+msgstr "申請人電å­éƒµä»¶ä¿¡ç®±ä½å€"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor's"
+msgstr "申請人所屬之第上"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor's Dept."
+msgstr "申請人所屬部門之"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor's Phone"
+msgstr "申請人電話"
+
+#: NOT FOUND IN SOURCE
+msgid "Requestor(s)"
+msgstr "申請人"
+
+#: NOT FOUND IN SOURCE
+msgid "RequestorAddresses"
+msgstr "申請人地å€"
+
+#: html/SelfService/Create.html:63 html/Ticket/Create.html:80 html/Ticket/Elements/EditPeople:69 html/Ticket/Elements/ShowPeople:52
+msgid "Requestors"
+msgstr "申請人"
+
+#: html/Admin/Queues/Modify.html:96
+msgid "Requests should be due in"
+msgstr "申請單處ç†æœŸé™"
+
+#: lib/RT/Attribute_Overlay.pm:146
+#. ('Object')
+msgid "Required parameter '%1' not specified"
+msgstr "未指定必è¦çš„åƒæ•¸ã€Œ%1ã€"
+
+#: html/Elements/Submit:83
+msgid "Reset"
+msgstr "é‡è¨­"
+
+#: html/Admin/Users/MyRT.html:15 html/Prefs/MyRT.html:60
+msgid "Reset to default"
+msgstr ""
+
+#: html/Admin/Users/Modify.html:183 html/User/Prefs.html:84
+msgid "Residence"
+msgstr "ä½è™•"
+
+#: NOT FOUND IN SOURCE
+msgid "Resolution"
+msgstr "解決狀態"
+
+#: html/Ticket/Elements/Tabs:156
+msgid "Resolve"
+msgstr "解決"
+
+#: html/Ticket/Update.html:156
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Resolve ticket #%1 (%2)"
+msgstr "解決申請單 #%1 (%2)"
+
+#: etc/initialdata:323 html/Elements/SelectDateType:49 lib/RT/Ticket_Overlay.pm:1172
+msgid "Resolved"
+msgstr "已解決"
+
+#: html/Tools/Reports/Elements/Tabs:55
+msgid "Resolved by owner"
+msgstr ""
+
+#: html/Tools/Reports/Elements/Tabs:59
+msgid "Resolved in date range"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:52
+msgid "Resolved tickets in period, grouped by owner"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByOwner.html:50
+msgid "Resolved tickets, grouped by owner"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Response to requestors"
+msgstr "回覆申請人"
+
+#: NOT FOUND IN SOURCE
+msgid "Responsibility Type"
+msgstr "責任å€åˆ†"
+
+#: html/Elements/ListActions:46 html/Search/Elements/NewListActions:47
+msgid "Results"
+msgstr "çµæžœ"
+
+#: NOT FOUND IN SOURCE
+msgid "Results per page"
+msgstr "æ¯é åˆ—出幾筆çµæžœ"
+
+#: html/Admin/Users/Modify.html:126 html/User/Prefs.html:116
+msgid "Retype Password"
+msgstr "å†æ¬¡è¼¸å…¥å¯†ç¢¼"
+
+#: html/Search/Elements/EditSearches:61
+msgid "Revert"
+msgstr "復原"
+
+#: NOT FOUND IN SOURCE
+msgid "Right %1 not found for %2 %3 in scope %4 (%5)\\n"
+msgstr "在 %4 (%5) 的範åœå…§æ‰¾ä¸åˆ° %2 %3 çš„ %1 權é™\\n"
+
+#: lib/RT/ACE_Overlay.pm:630
+msgid "Right Delegated"
+msgstr "權é™ä»£ç†å®Œç•¢"
+
+#: lib/RT/ACE_Overlay.pm:320
+msgid "Right Granted"
+msgstr "權é™è¨­å®šå®Œç•¢"
+
+#: lib/RT/ACE_Overlay.pm:178
+msgid "Right Loaded"
+msgstr "權é™è¼‰å…¥å®Œç•¢"
+
+#: lib/RT/ACE_Overlay.pm:695 lib/RT/ACE_Overlay.pm:716
+msgid "Right could not be revoked"
+msgstr "無法撤消權é™"
+
+#: html/User/Delegation.html:85
+msgid "Right not found"
+msgstr "找ä¸åˆ°æ¬Šé™"
+
+#: lib/RT/ACE_Overlay.pm:560 lib/RT/ACE_Overlay.pm:655
+msgid "Right not loaded."
+msgstr "權é™ä¸¦æœªè¼‰å…¥ã€‚"
+
+#: lib/RT/ACE_Overlay.pm:712
+msgid "Right revoked"
+msgstr "權é™æ’¤æ¶ˆå®Œç•¢"
+
+#: html/Admin/Elements/UserTabs:70
+msgid "Rights"
+msgstr "權é™åŠä»£ç†äºº"
+
+#: html/Admin/CustomFields/GroupRights.html:129 lib/RT/Interface/Web.pm:961
+#. ($object_type)
+msgid "Rights could not be granted for %1"
+msgstr "無法將權é™è³¦äºˆ %1"
+
+#: html/Admin/CustomFields/GroupRights.html:156 lib/RT/Interface/Web.pm:990
+#. ($object_type)
+msgid "Rights could not be revoked for %1"
+msgstr "無法撤消 %1 的權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "Role Members"
+msgstr "角色æˆå“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Role Name"
+msgstr "角色å稱"
+
+#: html/Admin/Global/GroupRights.html:72 html/Admin/Queues/GroupRights.html:74
+msgid "Roles"
+msgstr "角色"
+
+#: NOT FOUND IN SOURCE
+msgid "RootApproval"
+msgstr "交由系統管ç†å“¡ç°½æ ¸"
+
+#: html/Prefs/MyRT.html:72
+msgid "Rows per box"
+msgstr ""
+
+#: html/Search/Elements/DisplayOptions:93
+msgid "Rows per page"
+msgstr "æ¯é ç­†æ•¸"
+
+#: NOT FOUND IN SOURCE
+msgid "Run Approval"
+msgstr "簽核執行"
+
+#: NOT FOUND IN SOURCE
+msgid "SMTPDebug"
+msgstr "SMTP åµéŒ¯ç´€éŒ„"
+
+#: NOT FOUND IN SOURCE
+msgid "SMTPFrom"
+msgstr "SMTP 寄件ä½å€"
+
+#: NOT FOUND IN SOURCE
+msgid "SMTPServer"
+msgstr "SMTP 伺æœå™¨"
+
+#: NOT FOUND IN SOURCE
+msgid "Sat"
+msgstr "星期六"
+
+#: lib/RT/Date.pm:422
+msgid "Sat."
+msgstr "星期六"
+
+#: html/Prefs/MyRT.html:72 html/Prefs/Quicksearch.html:64 html/Prefs/Search.html:69 html/Prefs/Search.html:69 html/Search/Elements/EditSearches:70 html/Widgets/SelectionBox:211
+msgid "Save"
+msgstr "儲存"
+
+#: html/Admin/Global/Template.html:67 html/Admin/Groups/Modify.html:88 html/Admin/Queues/Modify.html:111 html/Admin/Queues/People.html:126 html/Admin/Users/Modify.html:239 html/Prefs/Quicksearch.html:64 html/Prefs/SearchOptions.html:63 html/SelfService/Prefs.html:58 html/Ticket/Modify.html:60 html/Ticket/ModifyAll.html:127 html/Ticket/ModifyDates.html:60 html/Ticket/ModifyLinks.html:61 html/Ticket/ModifyPeople.html:60 html/User/Groups/Modify.html:77
+msgid "Save Changes"
+msgstr "儲存更改"
+
+#: html/User/Prefs.html:181
+msgid "Save Preferences"
+msgstr "儲存å好"
+
+#: html/Ticket/Elements/PreviewScrips:131
+msgid "Save changes"
+msgstr "儲存更改"
+
+#: lib/RT/SavedSearch.pm:173
+#. ($name)
+msgid "Saved search %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Saved searches"
+msgstr "已儲存的查詢"
+
+#: html/Admin/Elements/ListGlobalScrips:60 html/Admin/Global/Scrip.html:77 html/Admin/Queues/Scrip.html:84
+#. ($scrip->Id)
+#. ($id)
+msgid "Scrip #%1"
+msgstr "手續 #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrip Action"
+msgstr "訊æ¯é€šçŸ¥å‹•ä½œ"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrip Condition"
+msgstr "訊æ¯é€šçŸ¥æ¢ä»¶"
+
+#: lib/RT/Scrip_Overlay.pm:203
+msgid "Scrip Created"
+msgstr "手續新增完畢"
+
+#: html/Admin/Elements/EditScrip:52
+msgid "Scrip Fields"
+msgstr "手續欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrip Name"
+msgstr "訊æ¯å稱"
+
+#: html/Admin/Elements/EditScrips:109
+msgid "Scrip deleted"
+msgstr "手續刪除完畢"
+
+#: html/Admin/Elements/QueueTabs:67 html/Admin/Elements/SystemTabs:54 html/Admin/Global/index.html:62
+msgid "Scrips"
+msgstr "手續"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips "
+msgstr "訊æ¯é€šçŸ¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Scrips for %1\\n"
+msgstr "%1 的手續\\n"
+
+#: html/Admin/Queues/Scrips.html:55
+msgid "Scrips which apply to all queues"
+msgstr "é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®çš„手續"
+
+#: html/Elements/SimpleSearch:48 html/Search/Simple.html:63
+msgid "Search"
+msgstr "查詢"
+
+#: NOT FOUND IN SOURCE
+msgid "Search Criteria"
+msgstr "查詢æ¢ä»¶"
+
+#: html/Prefs/SearchOptions.html:47 html/Prefs/SearchOptions.html:50
+msgid "Search Preferences"
+msgstr ""
+
+#: lib/RT/SavedSearch.pm:115
+msgid "Search attribute load failure"
+msgstr "æœå°‹å±¬æ€§è¼‰å…¥å¤±æ•—"
+
+#: html/Approvals/Elements/PendingMyApproval:59
+msgid "Search for approvals"
+msgstr "簽核單查詢"
+
+#: html/Search/Simple.html:67
+msgid "Search for tickets"
+msgstr ""
+
+#: html/Search/Simple.html:55
+msgid "Search for tickets. Enter <strong>id</strong> numbers, <strong>queues</strong> by name, Owners by <strong>username</strong> and Requestors by <strong>email address</strong>. RT will look for anything else you enter in ticket bodies and attachments."
+msgstr "æœå°‹ç”³è«‹å–®ã€‚è«‹éµå…¥<strong>編號</strong>ã€<strong>表單å稱</strong>ã€æ‰¿è¾¦äººçš„<strong>使用者å稱</strong>ã€æˆ–申請人的<strong>é›»å­éƒµä»¶åœ°å€</strong>。以上格å¼ä¹‹å¤–的文字,則會在申請單內文åŠé™„件內檢索。"
+
+#: html/User/Elements/Tabs:62
+msgid "Search options"
+msgstr ""
+
+#: html/Search/Chart.html:56
+#. ($PrimaryGroupBy)
+msgid "Search results grouped by %1"
+msgstr "æœå°‹çµæžœï¼Œä¾ %1 分組"
+
+#: lib/RT/SavedSearch.pm:203
+#. ($msg)
+msgid "Search update: %1"
+msgstr "更新查詢:%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Searches can't be associated with that kind of object"
+msgstr "ä¸èƒ½å°æ­¤é¡žç‰©ä»¶é€²è¡ŒæŸ¥è©¢"
+
+#: html/Search/Simple.html:57
+msgid "Searching the full text of every ticket can take a long time, but if you need to do it, you can search for any word in full ticket history for any word by typing <b>fulltext:<i>word</i></b>."
+msgstr "å°æ‰€æœ‰ç”³è«‹å–®çš„全文進行檢索,å¯èƒ½æœƒéœ€è¦å¾ˆä¹…的時間。但如果您真的有需è¦ï¼Œå¯éµå…¥ <b>fulltext:<i>文字</i></b> 來æœå°‹ç”³è«‹å–®çš„所有紀錄。"
+
+#: NOT FOUND IN SOURCE
+msgid "Second-"
+msgstr "二"
+
+#: NOT FOUND IN SOURCE
+msgid "Second-level Users"
+msgstr "二階主管員工"
+
+#: bin/rt-crontool:265
+msgid "Security:"
+msgstr "安全性:"
+
+#: html/Elements/ShowCustomFields:98
+msgid "See also:"
+msgstr ""
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "See custom fields"
+msgstr "查閱自訂欄ä½"
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "See exact outgoing email messages and their recipeients"
+msgstr "查閱é€å‡ºçš„é›»å­éƒµä»¶åŠæ”¶ä»¶äºº"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "See ticket private commentary"
+msgstr "查閱申請單內的ç§äººè©•è«–"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "See ticket summaries"
+msgstr "查閱申請單總覽"
+
+#: lib/RT/CustomField_Overlay.pm:105
+msgid "SeeCustomField"
+msgstr "查閱自訂欄ä½"
+
+#: lib/RT/Group_Overlay.pm:169
+msgid "SeeGroup"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:91
+msgid "SeeQueue"
+msgstr "查閱表單"
+
+#: NOT FOUND IN SOURCE
+msgid "Select"
+msgstr "é¸æ“‡"
+
+#: NOT FOUND IN SOURCE
+msgid "Select All"
+msgstr "å…¨é¸"
+
+#: html/Admin/CustomFields/index.html:46 html/Admin/CustomFields/index.html:49
+msgid "Select a Custom Field"
+msgstr "é¸æ“‡è‡ªè¨‚欄ä½"
+
+#: html/Admin/Groups/index.html:78
+msgid "Select a group"
+msgstr "é¸æ“‡ç¾¤çµ„"
+
+#: html/Admin/Queues/index.html:54
+msgid "Select a queue"
+msgstr "é¸æ“‡è¡¨å–®"
+
+#: html/SelfService/CreateTicketInQueue.html:48
+msgid "Select a queue for your new ticket"
+msgstr "為您新的申請單é¸æ“‡ä¸€å€‹è¡¨å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Select a queue to link to"
+msgstr "è«‹é¸æ“‡æ¬²é€£çµè¡¨å–®"
+
+#: html/Admin/Users/index.html:46 html/Admin/Users/index.html:49 html/Admin/Users/index.html:52
+msgid "Select a user"
+msgstr "é¸æ“‡ä½¿ç”¨è€…"
+
+#: html/Admin/Elements/CustomFieldTabs:90
+msgid "Select custom field"
+msgstr "é¸æ“‡è‡ªè¨‚欄ä½"
+
+#: html/Admin/Global/CustomFields/index.html:70
+msgid "Select custom fields for all user groups"
+msgstr "é¸æ“‡é©ç”¨æ–¼æ‰€æœ‰ä½¿ç”¨è€…群組的自訂欄ä½"
+
+#: html/Admin/Global/CustomFields/index.html:65
+msgid "Select custom fields for all users"
+msgstr "é¸æ“‡é©ç”¨æ–¼æ‰€æœ‰ä½¿ç”¨è€…的自訂欄ä½"
+
+#: html/Admin/Global/CustomFields/index.html:76
+msgid "Select custom fields for tickets in all queues"
+msgstr "é¸æ“‡é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®å…§ç”³è«‹å–®çš„自訂欄ä½"
+
+#: html/Admin/Global/CustomFields/index.html:83
+msgid "Select custom fields for transactions on tickets in all queues"
+msgstr "é¸æ“‡é©ç”¨æ–¼æ‰€æœ‰è¡¨å–®å…§ç”³è«‹å–®ä¹‹æ›´å‹•çš„自訂欄ä½"
+
+#: html/Admin/Elements/GroupTabs:75 html/User/Elements/GroupTabs:71
+msgid "Select group"
+msgstr "é¸æ“‡ç¾¤çµ„"
+
+#: lib/RT/CustomField_Overlay.pm:59
+msgid "Select multiple values"
+msgstr "é¸æ“‡å¤šé‡é …ç›®"
+
+#: lib/RT/CustomField_Overlay.pm:60
+msgid "Select one value"
+msgstr "é¸æ“‡å–®ä¸€é …ç›®"
+
+#: html/Admin/Elements/QueueTabs:92
+msgid "Select queue"
+msgstr "é¸æ“‡è¡¨å–®"
+
+#: html/Prefs/Quicksearch.html:53
+msgid "Select queues to be displayed on the \"RT at a glance\" page"
+msgstr ""
+
+#: html/Admin/Global/Scrip.html:59 html/Admin/Global/Scrips.html:57 html/Admin/Queues/Scrip.html:67 html/Admin/Queues/Scrips.html:73
+msgid "Select scrip"
+msgstr "é¸æ“‡æ‰‹çºŒ"
+
+#: html/Admin/Global/Template.html:78 html/Admin/Global/Templates.html:57 html/Admin/Queues/Template.html:76 html/Admin/Queues/Templates.html:68
+msgid "Select template"
+msgstr "é¸æ“‡ç¯„本"
+
+#: lib/RT/CustomField_Overlay.pm:61
+msgid "Select up to %1 values"
+msgstr "é¸æ“‡æœ€å¤š %1 個值"
+
+#: html/Admin/Elements/UserTabs:78
+msgid "Select user"
+msgstr "é¸æ“‡ä½¿ç”¨è€…"
+
+#: NOT FOUND IN SOURCE
+msgid "Select workflow"
+msgstr "é¸æ“‡æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectExternal"
+msgstr "系統é¸é …"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectMultiple"
+msgstr "多é‡é¸é …"
+
+#: NOT FOUND IN SOURCE
+msgid "SelectSingle"
+msgstr "單一é¸é …"
+
+#: html/Admin/Elements/EditCustomFields:58
+msgid "Selected Custom Fields"
+msgstr "å·²é¸å–的自訂欄ä½"
+
+#: html/Admin/CustomFields/Objects.html:59
+msgid "Selected objects"
+msgstr "å·²é¸å–的物件"
+
+#: NOT FOUND IN SOURCE
+msgid "Selected users:"
+msgstr "å·²é¸å–的使用者:"
+
+#: html/Widgets/SelectionBox:209
+msgid "Selections modified. Please save your changes"
+msgstr "é¸å–的項目已更改。請儲存您的更動"
+
+#: NOT FOUND IN SOURCE
+msgid "Self Service"
+msgstr "自助æœå‹™"
+
+#: etc/initialdata:121
+msgid "Send mail to all watchers"
+msgstr "寄信給所有視察員"
+
+#: etc/initialdata:117
+msgid "Send mail to all watchers as a \"comment\""
+msgstr "以評論方å¼å¯„信給所有視察員"
+
+#: etc/initialdata:112
+msgid "Send mail to requestors and Ccs"
+msgstr "寄信給申請人åŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:107
+msgid "Send mail to requestors and Ccs as a comment"
+msgstr "以評論方å¼å¯„信給申請人åŠå‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:78
+msgid "Sends a message to the requestors"
+msgstr "寄信給申請人"
+
+#: etc/initialdata:125 etc/initialdata:129
+msgid "Sends mail to explicitly listed Ccs and Bccs"
+msgstr "寄信給特定的副本åŠå¯†ä»¶å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:94 etc/upgrade/3.1.17/content:7
+msgid "Sends mail to the Ccs"
+msgstr "寄信給副本收件人"
+
+#: etc/initialdata:90 etc/upgrade/3.1.17/content:3
+msgid "Sends mail to the Ccs as a comment"
+msgstr "以評論方å¼å¯„信給副本收件人"
+
+#: etc/initialdata:102
+msgid "Sends mail to the administrative Ccs"
+msgstr "寄信給管ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:98
+msgid "Sends mail to the administrative Ccs as a comment"
+msgstr "以評論寄信給管ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:82 etc/initialdata:86
+msgid "Sends mail to the owner"
+msgstr "寄信給申請人"
+
+#: NOT FOUND IN SOURCE
+msgid "Sep"
+msgstr "ä¹æœˆ"
+
+#: lib/RT/Date.pm:449
+msgid "Sep."
+msgstr "09"
+
+#: NOT FOUND IN SOURCE
+msgid "September"
+msgstr "ä¹æœˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "Setting %1's 'Disabled' property to %2"
+msgstr "%1 的「åœç”¨ã€å±¬æ€§å·²è¨­ç‚º %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Shift Type"
+msgstr "ç­åˆ¥å±¬æ€§"
+
+#: html/Ticket/Elements/ShowTransaction:158
+msgid "Show"
+msgstr "顯示"
+
+#: html/Approvals/index.html:52
+msgid "Show Approvals"
+msgstr "顯示待簽核申請單"
+
+#: html/Search/Elements/EditFormat:56
+msgid "Show Columns"
+msgstr "顯示欄ä½"
+
+#: html/Ticket/Elements/Tabs:220
+msgid "Show Results"
+msgstr "顯示çµæžœ"
+
+#: html/Approvals/Elements/PendingMyApproval:64
+msgid "Show approved requests"
+msgstr "顯示已批准的簽核單"
+
+#: html/Ticket/Create.html:316
+msgid "Show basics"
+msgstr "顯示基本資訊"
+
+#: html/Approvals/Elements/PendingMyApproval:65
+msgid "Show denied requests"
+msgstr "顯示已é§å›žçš„簽核單"
+
+#: html/Ticket/Create.html:319
+msgid "Show details"
+msgstr "顯示細節"
+
+#: html/Approvals/Elements/PendingMyApproval:63
+msgid "Show pending requests"
+msgstr "顯示待處ç†çš„簽核單"
+
+#: html/Approvals/Elements/PendingMyApproval:66
+msgid "Show requests awaiting other approvals"
+msgstr "顯示尚待他人批准的簽核單"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket private commentary"
+msgstr "顯示申請單內的ç§äººè©•è«–"
+
+#: NOT FOUND IN SOURCE
+msgid "Show ticket summaries"
+msgstr "顯示申請單摘è¦"
+
+#: lib/RT/Queue_Overlay.pm:93
+msgid "ShowACL"
+msgstr "顯示權é™æ¸…å–®"
+
+#: lib/RT/System.pm:85
+msgid "ShowConfigTab"
+msgstr ""
+
+#: lib/RT/Queue_Overlay.pm:106
+msgid "ShowOutgoingEmail"
+msgstr "顯示寄é€éƒµä»¶"
+
+#: lib/RT/Group_Overlay.pm:168
+msgid "ShowSavedSearches"
+msgstr "顯示已儲存的查詢"
+
+#: lib/RT/Queue_Overlay.pm:102
+msgid "ShowScrips"
+msgstr "顯示手續"
+
+#: lib/RT/Queue_Overlay.pm:99
+msgid "ShowTemplate"
+msgstr "顯示範本"
+
+#: lib/RT/Queue_Overlay.pm:103
+msgid "ShowTicket"
+msgstr "顯示申請單"
+
+#: lib/RT/Queue_Overlay.pm:104
+msgid "ShowTicketComments"
+msgstr "顯示申請單的評論"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Sign up as a ticket Requestor or ticket or queue Cc"
+msgstr "登記æˆç‚ºç”³è«‹äººæˆ–副本收件人"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "Sign up as a ticket or queue AdminCc"
+msgstr "登記æˆç‚ºç®¡ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: html/Admin/Users/Modify.html:230 html/User/Prefs.html:168
+msgid "Signature"
+msgstr "ç°½å檔"
+
+#: NOT FOUND IN SOURCE
+msgid "Signed in as %1"
+msgstr "使用者:%1"
+
+#: html/Elements/Tabs:68
+msgid "Simple Search"
+msgstr ""
+
+#: html/Admin/Elements/SelectSingleOrMultiple:47
+msgid "Single"
+msgstr "單一"
+
+#: html/Search/Elements/EditFormat:75
+msgid "Size"
+msgstr ""
+
+#: html/Elements/Header:89
+msgid "Skip Menu"
+msgstr "ç•¥éŽé¸å–®"
+
+#: html/Search/Elements/EditFormat:78
+msgid "Small"
+msgstr ""
+
+#: html/Admin/CustomFields/Modify.html:120
+msgid "Some browsers may only load content from the same domain as your RT server."
+msgstr "æŸäº›ç€è¦½å™¨åªå…許載入和 RT 伺æœå™¨åŒä¸€å€‹ç¶²åŸŸçš„內容。"
+
+#: html/Admin/Elements/AddCustomFieldValue:49 html/Admin/Elements/EditCustomFieldValues:54
+msgid "Sort"
+msgstr "é †åº"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort key"
+msgstr "排åºæ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Sort results by"
+msgstr "çµæžœæŽ’åºæ–¹å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "SortOrder"
+msgstr "排åºé †åº"
+
+#: html/Admin/Elements/EditScrip:78
+msgid "Stage"
+msgstr "é—œå¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Stage Action"
+msgstr "é—œå¡é‹è¡Œå‹•ä½œ"
+
+#: NOT FOUND IN SOURCE
+msgid "Stage Condition"
+msgstr "é—œå¡é‹è¡Œæ¢ä»¶"
+
+#: NOT FOUND IN SOURCE
+msgid "Stalled"
+msgstr "延宕"
+
+#: NOT FOUND IN SOURCE
+msgid "Start page"
+msgstr "首é "
+
+#: html/Elements/SelectDateType:48 html/Ticket/Elements/EditDates:53 html/Ticket/Elements/ShowDates:56
+msgid "Started"
+msgstr "實際起始日"
+
+#: NOT FOUND IN SOURCE
+msgid "Started date '%1' could not be parsed"
+msgstr "無法解讀起始日期 '%1"
+
+#: html/Elements/SelectDateType:52 html/Ticket/Create.html:208 html/Ticket/Elements/EditDates:48 html/Ticket/Elements/ShowDates:52
+msgid "Starts"
+msgstr "應起始日"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts By"
+msgstr "應起始日"
+
+#: NOT FOUND IN SOURCE
+msgid "Starts date '%1' could not be parsed"
+msgstr "無法解讀起始日期 '%1"
+
+#: html/Admin/Users/Modify.html:162 html/User/Prefs.html:145
+msgid "State"
+msgstr "å·ž"
+
+#: html/Search/Elements/PickBasics:87 html/SelfService/Update.html:57 html/Ticket/Create.html:66 html/Ticket/Elements/EditBasics:53 html/Ticket/Elements/ShowBasics:52 html/Ticket/Update.html:59 lib/RT/Ticket_Overlay.pm:1166 lib/RT/Tickets_Overlay.pm:1651
+msgid "Status"
+msgstr "ç¾æ³"
+
+#: etc/initialdata:309
+msgid "Status Change"
+msgstr "ç¾æ³æ”¹è®Šæ™‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Status changed from %1 to %2"
+msgstr "ç¾æ³å¾ž %1 改為 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "StatusChange"
+msgstr "ç¾æ³æ”¹è®Šæ™‚"
+
+#: html/Ticket/Elements/Tabs:178
+msgid "Steal"
+msgstr "強制更æ›æ‰¿è¾¦äºº"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "Steal tickets"
+msgstr "強制承辦申請單"
+
+#: lib/RT/Queue_Overlay.pm:117
+msgid "StealTicket"
+msgstr "強制承辦申請單"
+
+#: lib/RT/Transaction_Overlay.pm:678
+#. ($Old->Name)
+msgid "Stolen from %1"
+msgstr "承辦人從 %1 強制更æ›"
+
+#: NOT FOUND IN SOURCE
+msgid "Stolen from %1 "
+msgstr "承辦人從 %1 å¼·åˆ¶æ›´æ› "
+
+#: html/Search/Elements/EditFormat:81
+msgid "Style"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Subgroup"
+msgstr "å­ç¾¤çµ„"
+
+#: html/Elements/QuickCreate:52 html/Elements/SelectAttachmentField:47 html/Search/Bulk.html:132 html/SelfService/Create.html:79 html/SelfService/Update.html:65 html/Ticket/Create.html:108 html/Ticket/Elements/EditBasics:48 html/Ticket/Elements/Reminders:125 html/Ticket/ModifyAll.html:100 html/Ticket/Update.html:82 lib/RT/Ticket_Overlay.pm:1162 lib/RT/Tickets_Overlay.pm:1733
+msgid "Subject"
+msgstr "主題"
+
+#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:815 lib/RT/Transaction_Overlay.pm:700
+#. ($self->Data)
+msgid "Subject changed to %1"
+msgstr "標題已改為 %1"
+
+#: html/Elements/Submit:75
+msgid "Submit"
+msgstr "é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "Submit Workflow"
+msgstr "é€å‡ºæµç¨‹"
+
+#: lib/RT/Group_Overlay.pm:774
+msgid "Succeeded"
+msgstr "設定æˆåŠŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Sun"
+msgstr "星期日"
+
+#: lib/RT/Date.pm:423
+msgid "Sun."
+msgstr "星期日"
+
+#: lib/RT/System.pm:75
+msgid "SuperUser"
+msgstr "系統管ç†å“¡"
+
+#: NOT FOUND IN SOURCE
+msgid "Sync now"
+msgstr "執行åŒæ­¥"
+
+#: NOT FOUND IN SOURCE
+msgid "Sync104HRMS"
+msgstr "自動åŒæ­¥104HRMS"
+
+#: NOT FOUND IN SOURCE
+msgid "Synchronizing HRMS data. This may take a while..."
+msgstr "正在åŒæ­¥åŒ– HRMS 人事系統資料。請ç¨å¾…..."
+
+#: html/User/Elements/DelegateRights:98
+msgid "System"
+msgstr "系統"
+
+#: html/Admin/Elements/ToolTabs:54 html/Admin/Tools/Configuration.html:48
+msgid "System Configuration"
+msgstr "系統設定"
+
+#: NOT FOUND IN SOURCE
+msgid "System Defined"
+msgstr "系統定義"
+
+#: html/Admin/CustomFields/GroupRights.html:128 html/Admin/CustomFields/GroupRights.html:155 html/Admin/CustomFields/UserRights.html:128 html/Admin/CustomFields/UserRights.html:98 html/Admin/Elements/SelectRights:106 lib/RT/ACE_Overlay.pm:584 lib/RT/Interface/Web.pm:960 lib/RT/Interface/Web.pm:989
+msgid "System Error"
+msgstr "系統錯誤"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. Right not granted."
+msgstr "系統錯誤。設定權é™å¤±æ•—。"
+
+#: NOT FOUND IN SOURCE
+msgid "System Error. right not granted"
+msgstr "系統錯誤。設定權é™å¤±æ•—。"
+
+#: lib/RT/Transaction_Overlay.pm:224 lib/RT/Transaction_Overlay.pm:230
+#. ($msg)
+msgid "System Error: %1"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "System Rights"
+msgstr "系統權é™"
+
+#: html/Admin/Tools/index.html:47
+msgid "System Tools"
+msgstr "系統工具"
+
+#: lib/RT/ACE_Overlay.pm:633
+msgid "System error. Right not delegated."
+msgstr "系統錯誤。權é™ä»£ç†å¤±æ•—。"
+
+#: lib/RT/ACE_Overlay.pm:163 lib/RT/ACE_Overlay.pm:228 lib/RT/ACE_Overlay.pm:323 lib/RT/ACE_Overlay.pm:920
+msgid "System error. Right not granted."
+msgstr "系統錯誤。設定權é™å¤±æ•—。"
+
+#: NOT FOUND IN SOURCE
+msgid "System error. Unable to grant rights."
+msgstr "系統錯誤。無法設定權é™ã€‚"
+
+#: html/Admin/CustomFields/GroupRights.html:58 html/Admin/Global/GroupRights.html:56 html/Admin/Groups/GroupRights.html:58 html/Admin/Queues/GroupRights.html:57
+msgid "System groups"
+msgstr "系統群組"
+
+#: NOT FOUND IN SOURCE
+msgid "SystemInternal"
+msgstr "系統內部用"
+
+#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
+msgid "SystemRolegroup for internal use"
+msgstr "內部使用的系統角色群組"
+
+#: lib/RT/CurrentUser.pm:357
+msgid "TEST_STRING"
+msgstr "TEST_STRING"
+
+#: NOT FOUND IN SOURCE
+msgid "TabbedUI"
+msgstr "é ç±¤ä»‹é¢"
+
+#: etc/initialdata:603 html/Search/Elements/EditFormat:72 html/Ticket/Elements/Tabs:170
+msgid "Take"
+msgstr "å—ç†"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "Take tickets"
+msgstr "自行承辦申請單"
+
+#: lib/RT/Queue_Overlay.pm:115
+msgid "TakeTicket"
+msgstr "自行承辦申請單"
+
+#: lib/RT/Transaction_Overlay.pm:663
+msgid "Taken"
+msgstr "å·²å—ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Task"
+msgstr "工作事項"
+
+#: html/Admin/Elements/EditScrip:71 html/Tools/Offline.html:78
+msgid "Template"
+msgstr "範本"
+
+#: html/Admin/Global/Template.html:112 html/Admin/Queues/Template.html:113
+#. ($TemplateObj->Id())
+msgid "Template #%1"
+msgstr "範本 #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Template Content"
+msgstr "通知範本內容"
+
+#: NOT FOUND IN SOURCE
+msgid "Template Description"
+msgstr "通知範本æè¿°"
+
+#: NOT FOUND IN SOURCE
+msgid "Template Name"
+msgstr "通知範本å稱"
+
+#: html/Admin/Elements/EditTemplates:110
+msgid "Template deleted"
+msgstr "範本已刪除"
+
+#: lib/RT/Scrip_Overlay.pm:176
+msgid "Template is mandatory argument"
+msgstr ""
+
+#: lib/RT/Scrip_Overlay.pm:180
+msgid "Template not found"
+msgstr "找ä¸åˆ°ç¯„本"
+
+#: NOT FOUND IN SOURCE
+msgid "Template not found\\n"
+msgstr "找ä¸åˆ°ç¯„本\\n"
+
+#: lib/RT/Template_Overlay.pm:343
+msgid "Template parsed"
+msgstr "範本剖æžå®Œç•¢"
+
+#: lib/RT/Template_Overlay.pm:391
+msgid "Template parsing error"
+msgstr ""
+
+#: html/Admin/Elements/QueueTabs:70 html/Admin/Elements/SystemTabs:57 html/Admin/Global/index.html:66
+msgid "Templates"
+msgstr "範本"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates "
+msgstr "通知範本"
+
+#: NOT FOUND IN SOURCE
+msgid "Templates for %1\\n"
+msgstr "找ä¸åˆ° %1 的範本\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Text"
+msgstr "文字"
+
+#: lib/RT/CustomField_Overlay.pm:943 lib/RT/Record.pm:945
+msgid "That is already the current value"
+msgstr "已經是目å‰æ¬„ä½çš„值"
+
+#: lib/RT/CustomField_Overlay.pm:412
+msgid "That is not a value for this custom field"
+msgstr "這ä¸æ˜¯è©²è‡ªè¨‚欄ä½çš„值"
+
+#: lib/RT/Ticket_Overlay.pm:1994
+msgid "That is the same value"
+msgstr "åŒæ¨£çš„值"
+
+#: lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:614
+msgid "That principal already has that right"
+msgstr "這項單ä½å·²ç¶“æ“有該權é™"
+
+#: lib/RT/Queue_Overlay.pm:753
+#. ($args{'Type'})
+msgid "That principal is already a %1 for this queue"
+msgstr "這項單ä½å·²ç¶“是這個表單的 %1"
+
+#: lib/RT/Ticket_Overlay.pm:1435
+#. ($self->loc($args{'Type'}))
+msgid "That principal is already a %1 for this ticket"
+msgstr "這項單ä½å·²ç¶“是這份申請單的 %1"
+
+#: lib/RT/Queue_Overlay.pm:852
+#. ($args{'Type'})
+msgid "That principal is not a %1 for this queue"
+msgstr "這項單ä½ä¸æ˜¯é€™å€‹è¡¨å–®çš„ %1"
+
+#: NOT FOUND IN SOURCE
+msgid "That principal is not a %1 for this ticket"
+msgstr "這項單ä½ä¸æ˜¯é€™ä»½ç”³è«‹å–®çš„ %1"
+
+#: lib/RT/Ticket_Overlay.pm:1990
+msgid "That queue does not exist"
+msgstr "此表單ä¸å­˜åœ¨"
+
+#: lib/RT/Ticket_Overlay.pm:3233
+msgid "That ticket has unresolved dependencies"
+msgstr "這份申請單有尚未解決的附屬申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "That user already has that right"
+msgstr "使用者已具有該項權é™"
+
+#: lib/RT/Action/CreateTickets.pm:710 lib/RT/Ticket_Overlay.pm:3037
+msgid "That user already owns that ticket"
+msgstr "該使用者已經承辦這份申請單"
+
+#: lib/RT/Ticket_Overlay.pm:3012
+msgid "That user does not exist"
+msgstr "使用者ä¸å­˜åœ¨"
+
+#: lib/RT/User_Overlay.pm:389
+msgid "That user is already privileged"
+msgstr "這å使用者已經是內部æˆå“¡"
+
+#: lib/RT/User_Overlay.pm:410
+msgid "That user is already unprivileged"
+msgstr "這å使用者屬於éžå…§éƒ¨æˆå“¡ç¾¤çµ„"
+
+#: lib/RT/User_Overlay.pm:402
+msgid "That user is now privileged"
+msgstr "使用者加入內部æˆå“¡ç¾¤çµ„完畢"
+
+#: lib/RT/User_Overlay.pm:423
+msgid "That user is now unprivileged"
+msgstr "這å使用者已加入éžå…§éƒ¨æˆå“¡ç¾¤çµ„"
+
+#: NOT FOUND IN SOURCE
+msgid "That user is now unprivilegedileged"
+msgstr "這å使用者已加入éžå…§éƒ¨æˆå“¡ç¾¤çµ„"
+
+#: lib/RT/Ticket_Overlay.pm:3031
+msgid "That user may not own tickets in that queue"
+msgstr "使用者å¯èƒ½æ²’有承辦表單裡的申請單"
+
+#: lib/RT/Link_Overlay.pm:233
+msgid "That's not a numerical id"
+msgstr "這ä¸æ˜¯ä¸€å€‹æ•¸å­—編號"
+
+#: html/SelfService/Display.html:53 html/Ticket/Create.html:177 html/Ticket/Elements/ShowSummary:49
+msgid "The Basics"
+msgstr "基本資訊"
+
+#: lib/RT/ACE_Overlay.pm:112
+msgid "The CC of a ticket"
+msgstr "申請單的副本收件人"
+
+#: lib/RT/ACE_Overlay.pm:113
+msgid "The administrative CC of a ticket"
+msgstr "申請單的管ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: NOT FOUND IN SOURCE
+msgid "The comment has been recorded"
+msgstr "評論已被紀錄"
+
+#: bin/rt-crontool:275
+msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
+msgstr "下列命令會找到 'general' 表單內所有é‹ä½œä¸­çš„申請單,並將其中 4 å°æ™‚內未處ç†çš„申請單優先程度設為 99:"
+
+#: NOT FOUND IN SOURCE
+msgid "The following commands were not proccessed:\\n\\n"
+msgstr "以下命令未被執行:\\n\\n"
+
+#: lib/RT/Record.pm:948
+msgid "The new value has been set."
+msgstr "新的欄ä½å€¼è¨­å®šå®Œæˆã€‚"
+
+#: lib/RT/ACE_Overlay.pm:110
+msgid "The owner of a ticket"
+msgstr "申請單的承辦人"
+
+#: lib/RT/ACE_Overlay.pm:111
+msgid "The requestor of a ticket"
+msgstr "申請單的申請人"
+
+#: html/Admin/Elements/EditUserComments:47
+msgid "These comments aren't generally visible to the user"
+msgstr "該使用者ä¸æœƒçœ‹è¦‹é€™äº›è©•è«–"
+
+#: NOT FOUND IN SOURCE
+msgid "Third-"
+msgstr "三"
+
+#: lib/RT/CustomField_Overlay.pm:978
+msgid "This custom field does not apply to that object"
+msgstr "此自訂欄ä½ä¸é©ç”¨æ–¼è©²ç‰©ä»¶"
+
+#: html/Admin/Tools/Configuration.html:50
+msgid "This feature is only available to system administrators"
+msgstr "此項功能僅é™ç³»çµ±ç®¡ç†å“¡ä½¿ç”¨"
+
+#: html/Ticket/Elements/PreviewScrips:96
+msgid "This message will be sent to..."
+msgstr "此訊æ¯æœƒå¯„給..."
+
+#: NOT FOUND IN SOURCE
+msgid "This ticket %1 %2 (%3)\\n"
+msgstr "申請單 %1 %2 (%3)\\n"
+
+#: bin/rt-crontool:266
+msgid "This tool allows the user to run arbitrary perl modules from within RT."
+msgstr "此工具程å¼æœƒè®“使用者經由 RT 執行任æ„命令。"
+
+#: lib/RT/Transaction_Overlay.pm:301
+msgid "This transaction appears to have no content"
+msgstr "此項更動報告沒有內容"
+
+#: html/Ticket/Elements/ShowRequestor:70
+#. ($rows)
+msgid "This user's %1 highest priority tickets"
+msgstr "使用者é€å‡ºçš„å‰ %1 份優先處ç†ç”³è«‹å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "This user's 25 highest priority tickets"
+msgstr "使用者é€å‡ºçš„å‰ 25 份優先處ç†ç”³è«‹å–®"
+
+#: NOT FOUND IN SOURCE
+msgid "Thu"
+msgstr "星期四"
+
+#: lib/RT/Date.pm:420
+msgid "Thu."
+msgstr "星期四"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket"
+msgstr "申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 %2"
+msgstr "申請單 # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket # %1 Jumbo update: %2"
+msgstr "更新申請單 # %1 的全部資訊:%2"
+
+#: html/Ticket/ModifyAll.html:46 html/Ticket/ModifyAll.html:50
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket #%1 Jumbo update: %2"
+msgstr "更新申請單 #%1 的全部資訊:%2"
+
+#: html/Approvals/Elements/ShowDependency:67
+#. ($link->BaseObj->Id, $link->BaseObj->Subject)
+msgid "Ticket #%1: %2"
+msgstr "申請單 #%1: %2"
+
+#: lib/RT/Action/CreateTickets.pm:1350 lib/RT/Action/CreateTickets.pm:1359 lib/RT/Action/CreateTickets.pm:605 lib/RT/Action/CreateTickets.pm:729 lib/RT/Action/CreateTickets.pm:741
+#. ($T::Tickets{$template_id}->Id)
+#. ($T::Tickets{$template_id}->id)
+#. ($ticket->Id)
+msgid "Ticket %1"
+msgstr "申請單 %1"
+
+#: lib/RT/Ticket_Overlay.pm:755 lib/RT/Ticket_Overlay.pm:775
+#. ($self->Id, $QueueObj->Name)
+msgid "Ticket %1 created in queue '%2'"
+msgstr "申請單 #%1 æˆåŠŸæ–°å¢žæ–¼ '%2' 表單"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket %1 loaded\\n"
+msgstr "載入申請單 %1\\n"
+
+#: html/Search/Bulk.html:377
+#. ($Ticket->Id, $_)
+msgid "Ticket %1: %2"
+msgstr "申請單 %1:%2"
+
+#: html/Admin/Elements/QueueTabs:74
+msgid "Ticket Custom Fields"
+msgstr "申請單的自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Due"
+msgstr "表單處ç†æœŸé™"
+
+#: html/Ticket/History.html:46 html/Ticket/History.html:49
+#. ($Ticket->Id, $Ticket->Subject)
+msgid "Ticket History # %1 %2"
+msgstr "申請單處ç†ç´€éŒ„ # %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket ID"
+msgstr "單號"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Id"
+msgstr "申請單編號"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Processing Due"
+msgstr "表單é‹è¡ŒæœŸé™"
+
+#: etc/initialdata:324
+msgid "Ticket Resolved"
+msgstr "申請單已解決"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:69 html/Admin/Global/CustomFields/index.html:81 lib/RT/CustomField_Overlay.pm:1207
+msgid "Ticket Transactions"
+msgstr "申請單的更動"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket Type"
+msgstr "表單種類"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket attachment"
+msgstr "申請單附件"
+
+#: lib/RT/Tickets_Overlay.pm:1920
+msgid "Ticket content"
+msgstr "申請單內容"
+
+#: lib/RT/Tickets_Overlay.pm:1969
+msgid "Ticket content type"
+msgstr "申請單內容類別"
+
+#: lib/RT/Ticket_Overlay.pm:603 lib/RT/Ticket_Overlay.pm:617 lib/RT/Ticket_Overlay.pm:628 lib/RT/Ticket_Overlay.pm:763
+msgid "Ticket could not be created due to an internal error"
+msgstr "內部錯誤,無法新增申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket created"
+msgstr "申請單新增完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket creation failed"
+msgstr "申請單新增失敗"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket deleted"
+msgstr "申請單刪除完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket id not found"
+msgstr "找ä¸åˆ°ç”³è«‹å–®ç·¨è™Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket killed"
+msgstr "申請單刪除完畢"
+
+#: html/Ticket/Display.html:55
+msgid "Ticket metadata"
+msgstr "申請單的æ述資訊"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket not found"
+msgstr "找ä¸åˆ°ç”³è«‹å–®"
+
+#: etc/initialdata:310
+msgid "Ticket status changed"
+msgstr "申請單ç¾æ³å·²æ”¹è®Š"
+
+#: NOT FOUND IN SOURCE
+msgid "Ticket watchers"
+msgstr "申請單視察員"
+
+#: lib/RT/Search/FromSQL.pm:82
+#. (ref $self)
+msgid "TicketSQL search module"
+msgstr ""
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:64 html/Admin/Global/CustomFields/index.html:75 html/Elements/Tabs:71 html/Search/Elements/Chart:109 lib/RT/CustomField_Overlay.pm:1206
+msgid "Tickets"
+msgstr "申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 %2"
+msgstr "申請單 %1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets %1 by %2"
+msgstr "申請單 %1 (%2)"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets I own"
+msgstr "待處ç†çš„申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets I requested"
+msgstr "é€å‡ºçš„申請單"
+
+#: html/Tools/Reports/CreatedByDates.html:86
+msgid "Tickets created after"
+msgstr ""
+
+#: html/Tools/Reports/CreatedByDates.html:88
+msgid "Tickets created before"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "Tickets from %1"
+msgstr "%1 的申請單"
+
+#: html/Tools/Reports/ResolvedByDates.html:87
+msgid "Tickets resolved after"
+msgstr ""
+
+#: html/Tools/Reports/ResolvedByDates.html:89
+msgid "Tickets resolved before"
+msgstr ""
+
+#: html/Approvals/Elements/ShowDependency:48
+msgid "Tickets which depend on this approval:"
+msgstr "批准之後,å¯æŽ¥çºŒè™•ç†ï¼š"
+
+#: html/Search/Elements/PickBasics:134 html/Ticket/Create.html:183 html/Ticket/Elements/EditBasics:72
+msgid "Time Estimated"
+msgstr "é è¨ˆæ™‚é–“"
+
+#: html/Search/Elements/PickBasics:135 html/Ticket/Create.html:196 html/Ticket/Elements/EditBasics:85
+msgid "Time Left"
+msgstr "剩餘時間"
+
+#: html/Search/Elements/PickBasics:133 html/Ticket/Create.html:189 html/Ticket/Elements/EditBasics:78
+msgid "Time Worked"
+msgstr "處ç†æ™‚é–“"
+
+#: lib/RT/Tickets_Overlay.pm:1891
+msgid "Time left"
+msgstr "剩餘時間"
+
+#: html/Elements/Footer:51
+msgid "Time to display"
+msgstr "顯示時間"
+
+#: lib/RT/Tickets_Overlay.pm:1866
+msgid "Time worked"
+msgstr "已處ç†æ™‚é–“"
+
+#: NOT FOUND IN SOURCE
+msgid "TimeLeft"
+msgstr "剩餘時間"
+
+#: lib/RT/Ticket_Overlay.pm:1167
+msgid "TimeWorked"
+msgstr "已處ç†æ™‚é–“"
+
+#: html/Search/Elements/EditFormat:74
+msgid "Title"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:"
+msgstr "產生這次更動的差異檔:"
+
+#: NOT FOUND IN SOURCE
+msgid "To generate a diff of this commit:\\n"
+msgstr "產生這次更動的差異檔:\\n"
+
+#: html/Elements/Footer:62
+#. ('<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>')
+msgid "To inquire about support, training, custom development or licensing, please contact %1."
+msgstr "如果有支æ´ã€æ•™è‚²è¨“ç·´åŠå®šè£½é–‹ç™¼çš„需è¦ï¼Œè«‹é€£çµ¡ %1。"
+
+#: NOT FOUND IN SOURCE
+msgid "Todo"
+msgstr "待辦事項"
+
+#: lib/RT/Ticket_Overlay.pm:1170
+msgid "Told"
+msgstr "告知日期"
+
+#: html/Admin/Elements/Tabs:68 html/Admin/index.html:88 html/Elements/Tabs:74 html/Tools/index.html:46 html/Tools/index.html:49
+msgid "Tools"
+msgstr "工具"
+
+#: html/Search/Elements/Chart:130
+msgid "Total"
+msgstr "é "
+
+#: etc/initialdata:252
+msgid "Transaction"
+msgstr "æ›´å‹•"
+
+#: lib/RT/Transaction_Overlay.pm:805
+#. ($self->Data)
+msgid "Transaction %1 purged"
+msgstr "清除更動報告 %1"
+
+#: lib/RT/Transaction_Overlay.pm:183
+msgid "Transaction Created"
+msgstr "更動報告已新增"
+
+#: html/Admin/Elements/QueueTabs:78
+msgid "Transaction Custom Fields"
+msgstr "更動的自訂欄ä½"
+
+#: NOT FOUND IN SOURCE
+msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
+msgstr "未指定申請單編號,無法新增更動"
+
+#: lib/RT/Transaction_Overlay.pm:128
+msgid "Transaction->Create couldn't, as you didn't specify an object type and id"
+msgstr "未指定物件類別åŠç·¨è™Ÿï¼Œç„¡æ³•æ–°å¢žæ›´å‹•"
+
+#: NOT FOUND IN SOURCE
+msgid "TransactionBatch"
+msgstr "批次更動時"
+
+#: NOT FOUND IN SOURCE
+msgid "TransactionCreate"
+msgstr "新增更動時"
+
+#: lib/RT/Transaction_Overlay.pm:870
+msgid "Transactions are immutable"
+msgstr "ä¸å¯æ›´æ”¹æ›´å‹•å ±å‘Š"
+
+#: NOT FOUND IN SOURCE
+msgid "Transfer to"
+msgstr "移交給"
+
+#: NOT FOUND IN SOURCE
+msgid "Trying to delete a right: %1"
+msgstr "試圖刪除æŸé …權é™ï¼š%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Tue"
+msgstr "星期二"
+
+#: lib/RT/Date.pm:418
+msgid "Tue."
+msgstr "星期二"
+
+#: html/Admin/CustomFields/Modify.html:66 html/Admin/Elements/EditCustomField:65 html/Ticket/Elements/AddWatchers:54 html/Ticket/Elements/AddWatchers:65 html/Ticket/Elements/AddWatchers:75 lib/RT/Ticket_Overlay.pm:1168 lib/RT/Tickets_Overlay.pm:1705
+msgid "Type"
+msgstr "類別"
+
+#: lib/RT/ScripCondition_Overlay.pm:128
+msgid "Unimplemented"
+msgstr "尚無實作"
+
+#: html/Admin/Users/Modify.html:89
+msgid "Unix login"
+msgstr "外部系統登入帳號"
+
+#: NOT FOUND IN SOURCE
+msgid "UnixUsername"
+msgstr "外部系統登入帳號"
+
+#: lib/RT/Attachment_Overlay.pm:289 lib/RT/Record.pm:861
+#. ($self->ContentEncoding)
+#. ($ContentEncoding)
+msgid "Unknown ContentEncoding %1"
+msgstr "ä¸å¯è§£çš„å…§å®¹æ–‡å­—ç·¨ç¢¼æ–¹å¼ %1"
+
+#: html/Search/Build.html:455 lib/RT/Report/Tickets.pm:410
+msgid "Unknown field: $key"
+msgstr ""
+
+#: html/Elements/SelectResultsPerPage:58
+msgid "Unlimited"
+msgstr "全數顯示"
+
+#: html/Search/Elements/SelectSearchesForObjects:64
+msgid "Unnamed search"
+msgstr "未命å的查詢"
+
+#: etc/initialdata:32
+msgid "Unprivileged"
+msgstr "éžå…§éƒ¨æˆå“¡"
+
+#: html/Admin/Elements/EditCustomFields:60
+msgid "Unselected Custom Fields"
+msgstr "未é¸å–的自訂欄ä½"
+
+#: html/Admin/CustomFields/Objects.html:61
+msgid "Unselected objects"
+msgstr "未é¸å–的物件"
+
+#: lib/RT/Transaction_Overlay.pm:659
+msgid "Untaken"
+msgstr "未被å—ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Untitled search"
+msgstr "未命å的查詢"
+
+#: NOT FOUND IN SOURCE
+msgid "Up"
+msgstr "上一é "
+
+#: html/Admin/Elements/EditScrip:128 html/Elements/RT__Ticket/ColumnMap:302 html/Search/Bulk.html:193 html/Search/Bulk.html:75
+msgid "Update"
+msgstr "處ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "Update All"
+msgstr "全部更新"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ID"
+msgstr "更新編號"
+
+#: html/Ticket/Update.html:135
+msgid "Update Ticket"
+msgstr "更新申請單"
+
+#: html/Search/Bulk.html:126 html/Ticket/ModifyAll.html:87 html/Ticket/Update.html:72
+msgid "Update Type"
+msgstr "更新類別"
+
+#: NOT FOUND IN SOURCE
+msgid "Update all these tickets at once"
+msgstr "整批更新申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Update email"
+msgstr "æ›´æ–°é›»å­éƒµä»¶ä¿¡ç®±"
+
+#: html/Search/Bulk.html:200 html/Search/Results.html:78
+msgid "Update multiple tickets"
+msgstr "批次更新申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Update name"
+msgstr "更新帳號"
+
+#: lib/RT/Action/CreateTickets.pm:750 lib/RT/Interface/Web.pm:584
+msgid "Update not recorded."
+msgstr "更新未被記錄"
+
+#: NOT FOUND IN SOURCE
+msgid "Update selected tickets"
+msgstr "æ›´æ–°é¸æ“‡çš„申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Update signature"
+msgstr "更新簽章"
+
+#: html/Ticket/ModifyAll.html:84
+msgid "Update ticket"
+msgstr "更新申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Update ticket # %1"
+msgstr "更新申請單 # %1"
+
+#: html/SelfService/Update.html:112 html/SelfService/Update.html:47
+#. ($Ticket->id)
+msgid "Update ticket #%1"
+msgstr "更新申請單 #%1"
+
+#: html/Ticket/Update.html:158
+#. ($TicketObj->id, $TicketObj->Subject)
+msgid "Update ticket #%1 (%2)"
+msgstr "更新申請單 #%1 (%2)"
+
+#: lib/RT/Action/CreateTickets.pm:748 lib/RT/Interface/Web.pm:583
+msgid "Update type was neither correspondence nor comment."
+msgstr "更新的內容並éžç”³è«‹å–®å›žè¦†ä¹Ÿä¸æ˜¯è©•è«–"
+
+#: html/Elements/SelectDateType:54 html/Ticket/Elements/ShowDates:72 lib/RT/CustomField_Overlay.pm:1284 lib/RT/Ticket_Overlay.pm:1171
+msgid "Updated"
+msgstr "å‰æ¬¡æ›´æ–°"
+
+#: html/Tools/Offline.html:93
+msgid "Upload"
+msgstr "上載"
+
+#: lib/RT/CustomField_Overlay.pm:84
+msgid "Upload multiple files"
+msgstr "上載多個檔案"
+
+#: lib/RT/CustomField_Overlay.pm:79
+msgid "Upload multiple images"
+msgstr "上載多份圖片"
+
+#: lib/RT/CustomField_Overlay.pm:85
+msgid "Upload one file"
+msgstr "上載一個檔案"
+
+#: lib/RT/CustomField_Overlay.pm:80
+msgid "Upload one image"
+msgstr "上載一份圖片"
+
+#: lib/RT/CustomField_Overlay.pm:86
+msgid "Upload up to %1 files"
+msgstr "上載最多 %1 個檔案"
+
+#: lib/RT/CustomField_Overlay.pm:81
+msgid "Upload up to %1 images"
+msgstr "上載最多 %1 份圖片"
+
+#: html/Tools/Offline.html:93
+msgid "Upload your changes"
+msgstr "上載您的更動"
+
+#: html/Admin/index.html:90
+msgid "Use other RT administrative tools"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "User"
+msgstr "使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 %2: %3\\n"
+msgstr "使用者 %1 %2:%3\\n"
+
+#: NOT FOUND IN SOURCE
+msgid "User %1 Password: %2\\n"
+msgstr "使用者 %1 密碼:%2\\n"
+
+#: lib/RT/Ticket_Overlay.pm:506
+#. ($args{'Owner'})
+msgid "User '%1' could not be found."
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… '%1'。"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found"
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… '%1'"
+
+#: NOT FOUND IN SOURCE
+msgid "User '%1' not found\\n"
+msgstr "找ä¸åˆ°ä½¿ç”¨è€… '%1'\\n"
+
+#: etc/initialdata:132 etc/initialdata:206
+msgid "User Defined"
+msgstr "使用者自訂"
+
+#: html/Admin/Elements/EditScrip:93
+msgid "User Defined conditions and actions"
+msgstr "使用者自訂的æ¢ä»¶åŠå‹•ä½œ"
+
+#: NOT FOUND IN SOURCE
+msgid "User ID"
+msgstr "使用者 ID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Id"
+msgstr "使用者 ID"
+
+#: NOT FOUND IN SOURCE
+msgid "User Number"
+msgstr "員工編號"
+
+#: html/Admin/Elements/CustomFieldTabs:72 html/Admin/Elements/GroupTabs:68 html/Admin/Elements/QueueTabs:85 html/Admin/Elements/SystemTabs:68 html/Admin/Global/index.html:80
+msgid "User Rights"
+msgstr "使用者權é™"
+
+#: NOT FOUND IN SOURCE
+msgid "User Setup"
+msgstr "使用者設定"
+
+#: NOT FOUND IN SOURCE
+msgid "User Shift"
+msgstr "å“¡å·¥ç­åˆ¥"
+
+#: NOT FOUND IN SOURCE
+msgid "User asked for an unknown update type for custom field %1 for %2 object #%3"
+msgstr "使用者試圖在 %2 物件 #%3 çš„è‡ªè¨‚æ¬„ä½ %1 上執行未知的更新æ“作"
+
+#: html/Admin/Users/Modify.html:301
+#. ($msg)
+msgid "User could not be created: %1"
+msgstr "無法新增使用者:%1"
+
+#: lib/RT/User_Overlay.pm:330
+msgid "User created"
+msgstr "使用者新增完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "User created: %1"
+msgstr "使用者 %1 新增完畢"
+
+#: NOT FOUND IN SOURCE
+msgid "User created: %1 (%2)"
+msgstr "使用者 %1 (%2) 新增完畢"
+
+#: html/Admin/CustomFields/GroupRights.html:74 html/Admin/Global/GroupRights.html:88 html/Admin/Groups/GroupRights.html:75 html/Admin/Queues/GroupRights.html:90
+msgid "User defined groups"
+msgstr "使用者定義的群組"
+
+#: lib/RT/User_Overlay.pm:592 lib/RT/User_Overlay.pm:612
+msgid "User loaded"
+msgstr "已載入使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "User notified"
+msgstr "已通知使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "User renamed from %1 to %2"
+msgstr "使用者 %1 已改å為 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "User view"
+msgstr "使用者ç§äººè³‡æ–™"
+
+#: html/Admin/Groups/index.html:103
+msgid "User-defined groups"
+msgstr "使用者自定群組"
+
+#: NOT FOUND IN SOURCE
+msgid "UserDefined"
+msgstr "使用者自定"
+
+#: html/Admin/Users/Modify.html:69 html/Elements/Login:90 html/Ticket/Elements/AddWatchers:56
+msgid "Username"
+msgstr "帳號"
+
+#: html/Admin/Elements/GlobalCustomFieldTabs:55 html/Admin/Elements/SelectNewGroupMembers:47 html/Admin/Elements/Tabs:53 html/Admin/Global/CustomFields/index.html:64 html/Admin/Groups/Members.html:76 html/Admin/Queues/People.html:89 html/Admin/index.html:62 html/User/Groups/Members.html:79 lib/RT/CustomField_Overlay.pm:1208
+msgid "Users"
+msgstr "使用者"
+
+#: html/Admin/Users/index.html:85
+msgid "Users matching search criteria"
+msgstr "符åˆæŸ¥è©¢æ¢ä»¶çš„使用者"
+
+#: bin/rt-crontool:134
+#. ($transaction->id)
+msgid "Using transaction #%1..."
+msgstr "使用更動 #%1..."
+
+#: lib/RT/Tickets_Overlay_SQL.pm:528
+msgid "Valid Query"
+msgstr "åˆç†çš„查詢"
+
+#: html/Admin/CustomFields/Modify.html:80
+msgid "Validation"
+msgstr "é©—è­‰"
+
+#: NOT FOUND IN SOURCE
+msgid "ValueOfQueue"
+msgstr "é¸æ“‡è¡¨å–®"
+
+#: html/Admin/CustomFields/Modify.html:130 html/Admin/Elements/EditCustomField:78
+msgid "Values"
+msgstr "欄ä½å€¼"
+
+#: NOT FOUND IN SOURCE
+msgid "View log"
+msgstr "檢視紀錄檔"
+
+#: lib/RT/Queue_Overlay.pm:107
+msgid "Watch"
+msgstr "視察"
+
+#: lib/RT/Queue_Overlay.pm:108
+msgid "WatchAsAdminCc"
+msgstr "以管ç†å“¡å‰¯æœ¬æ”¶ä»¶äººèº«ä»½è¦–察"
+
+#: NOT FOUND IN SOURCE
+msgid "Watcher loaded"
+msgstr "æˆåŠŸè¼‰å…¥è¦–察員資訊"
+
+#: html/Admin/Elements/QueueTabs:63
+msgid "Watchers"
+msgstr "視察員"
+
+#: NOT FOUND IN SOURCE
+msgid "WebEncoding"
+msgstr "網é æ–‡å­—編碼方å¼"
+
+#: NOT FOUND IN SOURCE
+msgid "Wed"
+msgstr "星期三"
+
+#: lib/RT/Date.pm:419
+msgid "Wed."
+msgstr "星期三"
+
+#: html/Tools/MyDay.html:75
+msgid "What I did today"
+msgstr ""
+
+#: etc/initialdata:521
+msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
+msgstr "當申請單通éŽæ‰€æœ‰ç°½æ ¸å¾Œï¼Œå°‡æ­¤è¨Šæ¯å›žè¦†åˆ°åŽŸç”³è«‹å–®"
+
+#: etc/initialdata:485
+msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
+msgstr "當申請單通éŽæŸé …簽核後,將此訊æ¯å›žè¦†åˆ°åŽŸç”³è«‹å–®"
+
+#: etc/initialdata:146
+msgid "When a ticket is created"
+msgstr "新增申請單時"
+
+#: etc/initialdata:418
+msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
+msgstr "簽核單新增之後,通知應å—ç†çš„承辦人åŠç®¡ç†å“¡å‰¯æœ¬æ”¶ä»¶äºº"
+
+#: etc/initialdata:151
+msgid "When anything happens"
+msgstr "當任何事情發生時"
+
+#: etc/initialdata:199
+msgid "Whenever a ticket is resolved"
+msgstr "當申請單解決時"
+
+#: etc/initialdata:185
+msgid "Whenever a ticket's owner changes"
+msgstr "當申請單更æ›æ‰¿è¾¦äººæ™‚"
+
+#: etc/initialdata:178 etc/upgrade/3.1.17/content:16
+msgid "Whenever a ticket's priority changes"
+msgstr "當申請單的優先順åºæ”¹è®Šæ™‚"
+
+#: etc/initialdata:193
+msgid "Whenever a ticket's queue changes"
+msgstr "當申請單更æ›è¡¨å–®æ™‚"
+
+#: etc/initialdata:170
+msgid "Whenever a ticket's status changes"
+msgstr "當申請單更新ç¾æ³æ™‚"
+
+#: etc/initialdata:207
+msgid "Whenever a user-defined condition occurs"
+msgstr "當使用者自訂的情æ³ç™¼ç”Ÿæ™‚"
+
+#: etc/initialdata:164
+msgid "Whenever comments come in"
+msgstr "當評論é€é”時"
+
+#: etc/initialdata:157
+msgid "Whenever correspondence comes in"
+msgstr "當回覆é€é”時"
+
+#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:88
+msgid "Work"
+msgstr "å…¬å¸"
+
+#: html/Search/Results.html:82
+msgid "Work offline"
+msgstr "離線工作"
+
+#: NOT FOUND IN SOURCE
+msgid "WorkPhone"
+msgstr "å…¬å¸é›»è©±"
+
+#: html/Ticket/Elements/ShowBasics:63 html/Ticket/Update.html:64
+msgid "Worked"
+msgstr "處ç†æ™‚é–“"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow #%1"
+msgstr "æµç¨‹ #%1"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow Begin"
+msgstr "æµç¨‹é–‹å§‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow End"
+msgstr "æµç¨‹çµæŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflow deleted"
+msgstr "æµç¨‹å·²åˆªé™¤"
+
+#: NOT FOUND IN SOURCE
+msgid "Workflows"
+msgstr "æµç¨‹"
+
+#: NOT FOUND IN SOURCE
+msgid "Writable"
+msgstr "å¯è®€å¯«"
+
+#: NOT FOUND IN SOURCE
+msgid "XXX CHANGEME You are not an authorized user"
+msgstr "XXX CHANGEME 您是未經授權的使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "Yes"
+msgstr "是"
+
+#: lib/RT/Ticket_Overlay.pm:3140
+msgid "You already own this ticket"
+msgstr "您已是這份申請單的承辦人"
+
+#: html/autohandler:214 html/autohandler:222
+msgid "You are not an authorized user"
+msgstr "您ä¸æ˜¯è¢«æŽˆæ¬Šçš„使用者"
+
+#: NOT FOUND IN SOURCE
+msgid "You can access it with the Download button on the right."
+msgstr "您å¯ä»¥æŒ‰å³æ–¹çš„「下載ã€éµä¾†å–得。"
+
+#: html/Prefs/Search.html:56
+msgid "You can also edit the predefined search itself"
+msgstr ""
+
+#: lib/RT/Ticket_Overlay.pm:3025
+msgid "You can only reassign tickets that you own or that are unowned"
+msgstr "祇能é‡æ–°æŒ‡æ´¾æ‚¨æ‰€æ‰¿è¾¦æˆ–是沒有承辦人的申請單"
+
+#: lib/RT/Ticket_Overlay.pm:3021
+msgid "You can only take tickets that are unowned"
+msgstr ""
+
+#: NOT FOUND IN SOURCE
+msgid "You don't have permission to view that ticket.\\n"
+msgstr "您沒有看那份申請單的權é™ã€‚\\n"
+
+#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:780
+#. ($num, $queue)
+msgid "You found %1 tickets in queue %2"
+msgstr "您會在表單 %2 找到 %1 的申請單"
+
+#: html/NoAuth/Logout.html:52
+msgid "You have been logged out of RT."
+msgstr "您已登出 RT。"
+
+#: html/SelfService/Display.html:133
+msgid "You have no permission to create tickets in that queue."
+msgstr "您沒有在該表單新增申請單的權é™ã€‚"
+
+#: lib/RT/Ticket_Overlay.pm:2003
+msgid "You may not create requests in that queue."
+msgstr "您ä¸èƒ½åœ¨è©²è¡¨å–®ä¸­æ出申請。"
+
+#: NOT FOUND IN SOURCE
+msgid "You need to restart the Request Tracker service for saved changes to take effect."
+msgstr "您必須é‡æ–°å•Ÿå‹• Request Tracker æœå‹™ï¼Œå„²å­˜çš„更動纔會生效。"
+
+#: html/NoAuth/Logout.html:56
+msgid "You're welcome to login again"
+msgstr "歡迎下次å†ä¾†"
+
+#: NOT FOUND IN SOURCE
+msgid "Your %1 requests"
+msgstr "您æ出的 %1 申請單"
+
+#: NOT FOUND IN SOURCE
+msgid "Your RT administrator has misconfigured the mail aliases which invoke RT"
+msgstr "RT 管ç†å“¡å¯èƒ½è¨­éŒ¯äº†ç”± RT 寄出的郵件收件人標頭檔"
+
+#: etc/initialdata:502
+msgid "Your request has been approved by %1. Other approvals may still be pending."
+msgstr "申請單已由 %1 批准。å¯èƒ½é‚„有其他待簽核的步驟。"
+
+#: etc/initialdata:540
+msgid "Your request has been approved."
+msgstr "您的申請單已完æˆç°½æ ¸ç¨‹åºã€‚"
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected"
+msgstr "您的申請單已被é§å›ž"
+
+#: NOT FOUND IN SOURCE
+msgid "Your request was rejected by %1."
+msgstr "您的申請單已被 %1 é§å›žã€‚"
+
+#: etc/initialdata:445
+msgid "Your request was rejected."
+msgstr "您的申請單已被é§å›žã€‚"
+
+#: html/autohandler:251
+msgid "Your username or password is incorrect"
+msgstr "您的帳號或密碼有誤"
+
+#: html/Admin/Users/Modify.html:168 html/User/Prefs.html:149
+msgid "Zip"
+msgstr "郵éžå€è™Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "[no subject]"
+msgstr "[沒有標題]"
+
+#: NOT FOUND IN SOURCE
+msgid "ago"
+msgstr "éŽæœŸ"
+
+#: NOT FOUND IN SOURCE
+msgid "alert"
+msgstr "急訊"
+
+#: lib/RT/System.pm:87
+msgid "allow creation of saved searches"
+msgstr "å…許建立é å­˜æŸ¥è©¢"
+
+#: lib/RT/System.pm:86
+msgid "allow loading of saved searches"
+msgstr "å…許載入é å­˜æŸ¥è©¢"
+
+#: NOT FOUND IN SOURCE
+msgid "approving"
+msgstr "待簽核"
+
+#: html/User/Elements/DelegateRights:80
+#. ($right->PrincipalObj->Object->SelfDescription)
+msgid "as granted to %1"
+msgstr "權é™åŒ %1"
+
+#: html/Search/Results.html:83
+msgid "chart"
+msgstr ""
+
+#: html/SelfService/Closed.html:49
+msgid "closed"
+msgstr "已解決"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:55
+msgid "contains"
+msgstr "包å«"
+
+#: NOT FOUND IN SOURCE
+msgid "content"
+msgstr "內容"
+
+#: NOT FOUND IN SOURCE
+msgid "content-type"
+msgstr "é¡žåž‹"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence (probably) not sent"
+msgstr "申請單回覆(å¯èƒ½)未é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "correspondence sent"
+msgstr "申請單回覆已é€å‡º"
+
+#: NOT FOUND IN SOURCE
+msgid "critical"
+msgstr "åš´é‡"
+
+#: html/Admin/Queues/Modify.html:98 lib/RT/Date.pm:346
+msgid "days"
+msgstr "天"
+
+#: NOT FOUND IN SOURCE
+msgid "dead"
+msgstr "拒絕處ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "debug"
+msgstr "åµéŒ¯"
+
+#: NOT FOUND IN SOURCE
+msgid "delete"
+msgstr "刪除"
+
+#: lib/RT/Queue_Overlay.pm:87
+msgid "deleted"
+msgstr "已刪除"
+
+#: html/Search/Elements/PickBasics:61
+msgid "does not match"
+msgstr "ä¸ç¬¦åˆ"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:56
+msgid "doesn't contain"
+msgstr "ä¸åŒ…å«"
+
+#: NOT FOUND IN SOURCE
+msgid "email address"
+msgstr "é›»å­éƒµä»¶ä¿¡ç®±"
+
+#: NOT FOUND IN SOURCE
+msgid "emergency"
+msgstr "å±é›£"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "equal to"
+msgstr "等於"
+
+#: NOT FOUND IN SOURCE
+msgid "error"
+msgstr "錯誤"
+
+#: html/Search/Build.html:547
+msgid "error: can't move down"
+msgstr "錯誤:無法下移"
+
+#: html/Search/Build.html:569
+msgid "error: can't move left"
+msgstr "錯誤:無法左移"
+
+#: html/Search/Build.html:528
+msgid "error: can't move up"
+msgstr "錯誤:無法上移"
+
+#: html/Search/Build.html:612
+msgid "error: nothing to delete"
+msgstr "錯誤:沒有å¯åˆªé™¤çš„å°è±¡"
+
+#: html/Search/Build.html:533 html/Search/Build.html:552 html/Search/Build.html:574 html/Search/Build.html:603
+msgid "error: nothing to move"
+msgstr "錯誤:沒有å¯ç§»å‹•çš„å°è±¡"
+
+#: html/Search/Build.html:630
+msgid "error: nothing to toggle"
+msgstr "錯誤:沒有å¯åˆ‡æ›çš„å°è±¡"
+
+#: NOT FOUND IN SOURCE
+msgid "false"
+msgstr "å‡"
+
+#: NOT FOUND IN SOURCE
+msgid "filename"
+msgstr "檔å"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "greater than"
+msgstr "大於"
+
+#: lib/RT/Group_Overlay.pm:214
+#. ($self->Name)
+msgid "group '%1'"
+msgstr "群組 '%1'"
+
+#: html/Search/Results.html:88
+#. ($m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $Query))
+msgid "grouped by %1"
+msgstr "ä¾ %1 分組"
+
+#: lib/RT/Date.pm:342
+msgid "hours"
+msgstr "å°æ™‚"
+
+#: html/Search/Elements/PickBasics:48
+msgid "id"
+msgstr "編號"
+
+#: NOT FOUND IN SOURCE
+msgid "info"
+msgstr "資訊"
+
+#: html/Elements/SelectBoolean:53 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:57 html/Search/Elements/PickBasics:162 html/Search/Elements/PickBasics:74 html/Search/Elements/PickBasics:90 html/Search/Elements/PickCFs:53
+msgid "is"
+msgstr "是"
+
+#: html/Elements/SelectBoolean:57 html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectMatch:58 html/Search/Elements/PickBasics:163 html/Search/Elements/PickBasics:75 html/Search/Elements/PickBasics:91 html/Search/Elements/PickCFs:54
+msgid "isn't"
+msgstr "ä¸æ˜¯"
+
+#: html/Elements/SelectCustomFieldOperator:59 html/Elements/SelectEqualityOperator:59
+msgid "less than"
+msgstr "å°æ–¼"
+
+#: NOT FOUND IN SOURCE
+msgid "level Admin"
+msgstr "層主管"
+
+#: html/Search/Elements/PickBasics:60
+msgid "matches"
+msgstr "符åˆ"
+
+#: lib/RT/Date.pm:338
+msgid "min"
+msgstr "分"
+
+#: NOT FOUND IN SOURCE
+msgid "minutes"
+msgstr "分é˜"
+
+#: NOT FOUND IN SOURCE
+msgid "modifications\\n\\n"
+msgstr "更改\\n\\n"
+
+#: lib/RT/Date.pm:354
+msgid "months"
+msgstr "月"
+
+#: lib/RT/Queue_Overlay.pm:82
+msgid "new"
+msgstr "新建立"
+
+#: html/Admin/Elements/PickCustomFields:64 html/Admin/Elements/PickObjects:65
+msgid "no name"
+msgstr "沒有å稱"
+
+#: html/Admin/Elements/EditScrips:64
+msgid "no value"
+msgstr "沒有值"
+
+#: html/Admin/Elements/EditQueueWatchers:48 html/Ticket/Elements/EditWatchers:49
+msgid "none"
+msgstr "ç„¡"
+
+#: html/Elements/SelectEqualityOperator:59
+msgid "not equal to"
+msgstr "ä¸ç­‰æ–¼"
+
+#: NOT FOUND IN SOURCE
+msgid "notice"
+msgstr "æ示"
+
+#: NOT FOUND IN SOURCE
+msgid "notlike"
+msgstr "ä¸ç¬¦åˆ"
+
+#: NOT FOUND IN SOURCE
+msgid "number"
+msgstr "號"
+
+#: html/SelfService/Elements/MyRequests:82 lib/RT/Queue_Overlay.pm:83
+msgid "open"
+msgstr "é–‹å•Ÿ"
+
+#: NOT FOUND IN SOURCE
+msgid "opened"
+msgstr "已開啟"
+
+#: lib/RT/Group_Overlay.pm:219
+#. ($self->Name, $user->Name)
+msgid "personal group '%1' for user '%2'"
+msgstr "使用者「%2ã€çš„「%1ã€ä»£ç†äººç¾¤çµ„"
+
+#: lib/RT/Group_Overlay.pm:227
+#. ($queue->Name, $self->Type)
+msgid "queue %1 %2"
+msgstr "表單 %1 %2"
+
+#: lib/RT/Queue_Overlay.pm:86
+msgid "rejected"
+msgstr "å·²é§å›ž"
+
+#: lib/RT/Queue_Overlay.pm:85
+msgid "resolved"
+msgstr "已處ç†"
+
+#: NOT FOUND IN SOURCE
+msgid "rtname"
+msgstr "伺æœå™¨å稱"
+
+#: lib/RT/Date.pm:334
+msgid "sec"
+msgstr "秒"
+
+#: lib/RT/System.pm:85
+msgid "show Configuration tab"
+msgstr "顯示設定é ç±¤"
+
+#: html/Search/Results.html:80
+msgid "spreadsheet"
+msgstr "試算表"
+
+#: lib/RT/Queue_Overlay.pm:84
+msgid "stalled"
+msgstr "延宕"
+
+#: html/Search/Results.html:89
+#. ($m->scomp('Elements/SelectChartType', Name => 'ChartStyle'))
+msgid "style: %1"
+msgstr ""
+
+#: html/Prefs/MyRT.html:93
+msgid "summary rows"
+msgstr "加總列"
+
+#: lib/RT/Group_Overlay.pm:222
+#. ($self->Type)
+msgid "system %1"
+msgstr "系統 %1"
+
+#: lib/RT/Group_Overlay.pm:233
+#. ($self->Type)
+msgid "system group '%1'"
+msgstr "系統群組 '%1'"
+
+#: html/Elements/Error:64 html/SelfService/Error.html:63
+msgid "the calling component did not specify why"
+msgstr "呼å«å…ƒä»¶æœªæŒ‡æ˜ŽåŽŸå› "
+
+#: NOT FOUND IN SOURCE
+msgid "ticket #%1"
+msgstr "申請單 #%1"
+
+#: lib/RT/Group_Overlay.pm:230
+#. ($self->Instance, $self->Type)
+msgid "ticket #%1 %2"
+msgstr "申請單 #%1 %2"
+
+#: NOT FOUND IN SOURCE
+msgid "till"
+msgstr "至"
+
+#: NOT FOUND IN SOURCE
+msgid "to"
+msgstr "到"
+
+#: NOT FOUND IN SOURCE
+msgid "true"
+msgstr "真"
+
+#: lib/RT/Group_Overlay.pm:236
+#. ($self->Id)
+msgid "undescribed group %1"
+msgstr "沒有æ述的群組 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "unresolved"
+msgstr "未處ç†"
+
+#: lib/RT/Group_Overlay.pm:211
+#. ($user->Object->Name)
+msgid "user %1"
+msgstr "使用者 %1"
+
+#: NOT FOUND IN SOURCE
+msgid "warning"
+msgstr "警告"
+
+#: lib/RT/Date.pm:350
+msgid "weeks"
+msgstr "週"
+
+#: NOT FOUND IN SOURCE
+msgid "with template %1"
+msgstr "範本:%1"
+
+#: lib/RT/Date.pm:358
+msgid "years"
+msgstr "å¹´"
+
+msgid "Press 'Esc' to close this window."
+msgstr "按 'Esc' éµå¯é—œé–‰æœ¬è¦–窗。"
+
+msgid "HasMember"
+msgstr "æ“有æˆå“¡"
+
+msgid "LinkedTo"
+msgstr "連çµè‡³"
+
+msgid "Watcher"
+msgstr "視察員"
+
+msgid "(displaying new and open tickets for %1)"
+msgstr "(顯示 %1 å下新建立åŠé–‹å•Ÿä¸­çš„申請單)"
diff --git a/rt/lib/RT/Interface/CLI.pm b/rt/lib/RT/Interface/CLI.pm
new file mode 100644
index 0000000..c2b5454
--- /dev/null
+++ b/rt/lib/RT/Interface/CLI.pm
@@ -0,0 +1,270 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+
+use RT;
+package RT::Interface::CLI;
+
+
+
+BEGIN {
+ use Exporter ();
+ use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+ # set the version for version checking
+ $VERSION = do { my @r = (q$Revision: 1.1.1.6 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
+
+ @ISA = qw(Exporter);
+
+ # your exported package globals go here,
+ # as well as any optionally exported functions
+ @EXPORT_OK = qw(&CleanEnv
+ &GetCurrentUser &GetMessageContent &debug &loc);
+}
+
+=head1 NAME
+
+ RT::Interface::CLI - helper functions for creating a commandline RT interface
+
+=head1 SYNOPSIS
+
+ use lib "/path/to/rt/libraries/";
+
+ use RT::Interface::CLI qw(CleanEnv
+ GetCurrentUser GetMessageContent loc);
+
+ #Clean out all the nasties from the environment
+ CleanEnv();
+
+ #let's talk to RT'
+ use RT;
+
+ #Load RT's config file
+ RT::LoadConfig();
+
+ # Connect to the database. set up loggign
+ RT::Init();
+
+ #Get the current user all loaded
+ my $CurrentUser = GetCurrentUser();
+
+ print loc('Hello!'); # Synonym of $CuurentUser->loc('Hello!');
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok(require RT::Interface::CLI);
+
+=end testing
+
+=cut
+
+
+=head2 CleanEnv
+
+Removes some of the nastiest nasties from the user\'s environment.
+
+=cut
+
+sub CleanEnv {
+ $ENV{'PATH'} = '/bin:/usr/bin'; # or whatever you need
+ $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
+ $ENV{'SHELL'} = '/bin/sh' if defined $ENV{'SHELL'};
+ $ENV{'ENV'} = '' if defined $ENV{'ENV'};
+ $ENV{'IFS'} = '' if defined $ENV{'IFS'};
+}
+
+
+
+
+{
+
+ my $CurrentUser; # shared betwen GetCurrentUser and loc
+
+# {{{ sub GetCurrentUser
+
+=head2 GetCurrentUser
+
+ Figures out the uid of the current user and returns an RT::CurrentUser object
+loaded with that user. if the current user isn't found, returns a copy of RT::Nobody.
+
+=cut
+
+sub GetCurrentUser {
+
+ require RT::CurrentUser;
+
+ #Instantiate a user object
+
+ my $Gecos= ($^O eq 'MSWin32') ? Win32::LoginName() : (getpwuid($<))[0];
+
+ #If the current user is 0, then RT will assume that the User object
+ #is that of the currentuser.
+
+ $CurrentUser = new RT::CurrentUser();
+ $CurrentUser->LoadByGecos($Gecos);
+
+ unless ($CurrentUser->Id) {
+ $RT::Logger->debug("No user with a unix login of '$Gecos' was found. ");
+ }
+
+ return($CurrentUser);
+}
+# }}}
+
+
+# {{{ sub loc
+
+=head2 loc
+
+ Synonym of $CurrentUser->loc().
+
+=cut
+
+sub loc {
+ die "No current user yet" unless $CurrentUser ||= RT::CurrentUser->new;
+ return $CurrentUser->loc(@_);
+}
+# }}}
+
+}
+
+
+# {{{ sub GetMessageContent
+
+=head2 GetMessageContent
+
+Takes two arguments a source file and a boolean "edit". If the source file
+is undef or "", assumes an empty file. Returns an edited file as an
+array of lines.
+
+=cut
+
+sub GetMessageContent {
+ my %args = ( Source => undef,
+ Content => undef,
+ Edit => undef,
+ CurrentUser => undef,
+ @_);
+ my $source = $args{'Source'};
+
+ my $edit = $args{'Edit'};
+
+ my $currentuser = $args{'CurrentUser'};
+ my @lines;
+
+ use File::Temp qw/ tempfile/;
+
+ #Load the sourcefile, if it's been handed to us
+ if ($source) {
+ open (SOURCE, "<$source");
+ @lines = (<SOURCE>);
+ close (SOURCE);
+ }
+ elsif ($args{'Content'}) {
+ @lines = split('\n',$args{'Content'});
+ }
+ #get us a tempfile.
+ my ($fh, $filename) = tempfile();
+
+ #write to a tmpfile
+ for (@lines) {
+ print $fh $_;
+ }
+ close ($fh);
+
+ #Edit the file if we need to
+ if ($edit) {
+
+ unless ($ENV{'EDITOR'}) {
+ $RT::Logger->crit('No $EDITOR variable defined'. "\n");
+ return undef;
+ }
+ system ($ENV{'EDITOR'}, $filename);
+ }
+
+ open (READ, "<$filename");
+ my @newlines = (<READ>);
+ close (READ);
+
+ unlink ($filename) unless (debug());
+ return(\@newlines);
+
+}
+
+# }}}
+
+# {{{ sub debug
+
+sub debug {
+ my $val = shift;
+ my ($debug);
+ if ($val) {
+ $RT::Logger->debug($val."\n");
+ if ($debug) {
+ print STDERR "$val\n";
+ }
+ }
+ if ($debug) {
+ return(1);
+ }
+}
+
+# }}}
+
+
+eval "require RT::Interface::CLI_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/CLI_Vendor.pm});
+eval "require RT::Interface::CLI_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/CLI_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm
new file mode 100755
index 0000000..dfbd4ae
--- /dev/null
+++ b/rt/lib/RT/Interface/Email.pm
@@ -0,0 +1,991 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Email;
+
+use strict;
+use Mail::Address;
+use MIME::Entity;
+use RT::EmailParser;
+use File::Temp;
+use UNIVERSAL::require;
+
+BEGIN {
+ use Exporter ();
+ use vars qw ( @ISA @EXPORT_OK);
+
+ # set the version for version checking
+ our $VERSION = 2.0;
+
+ @ISA = qw(Exporter);
+
+ # your exported package globals go here,
+ # as well as any optionally exported functions
+ @EXPORT_OK = qw(
+ &CreateUser
+ &GetMessageContent
+ &CheckForLoops
+ &CheckForSuspiciousSender
+ &CheckForAutoGenerated
+ &CheckForBounce
+ &MailError
+ &ParseCcAddressesFromHead
+ &ParseSenderAddressFromHead
+ &ParseErrorsToAddressFromHead
+ &ParseAddressFromHeader
+ &Gateway);
+
+}
+
+=head1 NAME
+
+ RT::Interface::Email - helper functions for parsing email sent to RT
+
+=head1 SYNOPSIS
+
+ use lib "!!RT_LIB_PATH!!";
+ use lib "!!RT_ETC_PATH!!";
+
+ use RT::Interface::Email qw(Gateway CreateUser);
+
+=head1 DESCRIPTION
+
+
+=begin testing
+
+ok(require RT::Interface::Email);
+
+=end testing
+
+
+=head1 METHODS
+
+=cut
+
+# {{{ sub CheckForLoops
+
+sub CheckForLoops {
+ my $head = shift;
+
+ #If this instance of RT sent it our, we don't want to take it in
+ my $RTLoop = $head->get("X-RT-Loop-Prevention") || "";
+ chomp($RTLoop); #remove that newline
+ if ( $RTLoop eq "$RT::rtname" ) {
+ return (1);
+ }
+
+ # TODO: We might not trap the case where RT instance A sends a mail
+ # to RT instance B which sends a mail to ...
+ return (undef);
+}
+
+# }}}
+
+# {{{ sub CheckForSuspiciousSender
+
+sub CheckForSuspiciousSender {
+ my $head = shift;
+
+ #if it's from a postmaster or mailer daemon, it's likely a bounce.
+
+ #TODO: better algorithms needed here - there is no standards for
+ #bounces, so it's very difficult to separate them from anything
+ #else. At the other hand, the Return-To address is only ment to be
+ #used as an error channel, we might want to put up a separate
+ #Return-To address which is treated differently.
+
+ #TODO: search through the whole email and find the right Ticket ID.
+
+ my ( $From, $junk ) = ParseSenderAddressFromHead($head);
+
+ if ( ( $From =~ /^mailer-daemon\@/i )
+ or ( $From =~ /^postmaster\@/i ) )
+ {
+ return (1);
+
+ }
+
+ return (undef);
+
+}
+
+# }}}
+
+# {{{ sub CheckForAutoGenerated
+sub CheckForAutoGenerated {
+ my $head = shift;
+
+ my $Precedence = $head->get("Precedence") || "";
+ if ( $Precedence =~ /^(bulk|junk)/i ) {
+ return (1);
+ }
+
+ # First Class mailer uses this as a clue.
+ my $FCJunk = $head->get("X-FC-Machinegenerated") || "";
+ if ( $FCJunk =~ /^true/i ) {
+ return (1);
+ }
+
+ return (0);
+}
+
+# }}}
+
+# {{{ sub CheckForBounce
+sub CheckForBounce {
+ my $head = shift;
+
+ my $ReturnPath = $head->get("Return-path") || "";
+ return ( $ReturnPath =~ /<>/ );
+}
+
+# }}}
+
+# {{{ IsRTAddress
+
+=head2 IsRTAddress ADDRESS
+
+Takes a single parameter, an email address.
+Returns true if that address matches the $RTAddressRegexp.
+Returns false, otherwise.
+
+=cut
+
+sub IsRTAddress {
+ my $address = shift || '';
+
+ # Example: the following rule would tell RT not to Cc
+ # "tickets@noc.example.com"
+ if ( defined($RT::RTAddressRegexp)
+ && $address =~ /$RT::RTAddressRegexp/i )
+ {
+ return (1);
+ } else {
+ return (undef);
+ }
+}
+
+# }}}
+
+# {{{ CullRTAddresses
+
+=head2 CullRTAddresses ARRAY
+
+Takes a single argument, an array of email addresses.
+Returns the same array with any IsRTAddress()es weeded out.
+
+=cut
+
+sub CullRTAddresses {
+ return grep !IsRTAddress($_), @_;
+}
+
+# }}}
+
+# {{{ sub MailError
+sub MailError {
+ my %args = (
+ To => $RT::OwnerEmail,
+ Bcc => undef,
+ From => $RT::CorrespondAddress,
+ Subject => 'There has been an error',
+ Explanation => 'Unexplained error',
+ MIMEObj => undef,
+ Attach => undef,
+ LogLevel => 'crit',
+ @_
+ );
+
+ $RT::Logger->log(
+ level => $args{'LogLevel'},
+ message => $args{'Explanation'}
+ );
+ # the colons are necessary to make ->build include non-standard headers
+ my $entity = MIME::Entity->build(
+ Type => "multipart/mixed",
+ From => $args{'From'},
+ Bcc => $args{'Bcc'},
+ To => $args{'To'},
+ Subject => $args{'Subject'},
+ 'Precedence:' => 'bulk',
+ 'X-RT-Loop-Prevention:' => $RT::rtname,
+ 'In-Reply-To:' => $args{'MIMEObj'} ? $args{'MIMEObj'}->head->get('Message-Id') : undef
+ );
+
+ $entity->attach( Data => $args{'Explanation'} . "\n" );
+
+ my $mimeobj = $args{'MIMEObj'};
+ if ($mimeobj) {
+ $mimeobj->sync_headers();
+ $entity->add_part($mimeobj);
+ }
+
+ if ( $args{'Attach'} ) {
+ $entity->attach( Data => $args{'Attach'}, Type => 'message/rfc822' );
+
+ }
+
+ if ( $RT::MailCommand eq 'sendmailpipe' ) {
+ open( MAIL,
+ "|$RT::SendmailPath $RT::SendmailBounceArguments $RT::SendmailArguments"
+ )
+ || return (0);
+ print MAIL $entity->as_string;
+ close(MAIL);
+ } else {
+ $entity->send( $RT::MailCommand, $RT::MailParams );
+ }
+}
+
+# }}}
+
+# {{{ Create User
+
+sub CreateUser {
+ my ( $Username, $Address, $Name, $ErrorsTo, $entity ) = @_;
+ my $NewUser = RT::User->new($RT::SystemUser);
+
+ my ( $Val, $Message ) = $NewUser->Create(
+ Name => ( $Username || $Address ),
+ EmailAddress => $Address,
+ RealName => $Name,
+ Password => undef,
+ Privileged => 0,
+ Comments => 'Autocreated on ticket submission'
+ );
+
+ unless ($Val) {
+
+ # Deal with the race condition of two account creations at once
+ if ($Username) {
+ $NewUser->LoadByName($Username);
+ }
+
+ unless ( $NewUser->Id ) {
+ $NewUser->LoadByEmail($Address);
+ }
+
+ unless ( $NewUser->Id ) {
+ MailError(
+ To => $ErrorsTo,
+ Subject => "User could not be created",
+ Explanation =>
+ "User creation failed in mailgateway: $Message",
+ MIMEObj => $entity,
+ LogLevel => 'crit'
+ );
+ }
+ }
+
+ #Load the new user object
+ my $CurrentUser = RT::CurrentUser->new();
+ $CurrentUser->LoadByEmail($Address);
+
+ unless ( $CurrentUser->id ) {
+ $RT::Logger->warning(
+ "Couldn't load user '$Address'." . "giving up" );
+ MailError(
+ To => $ErrorsTo,
+ Subject => "User could not be loaded",
+ Explanation =>
+ "User '$Address' could not be loaded in the mail gateway",
+ MIMEObj => $entity,
+ LogLevel => 'crit'
+ );
+ }
+
+ return $CurrentUser;
+}
+
+# }}}
+
+# {{{ ParseCcAddressesFromHead
+
+=head2 ParseCcAddressesFromHead HASHREF
+
+Takes a hashref object containing QueueObj, Head and CurrentUser objects.
+Returns a list of all email addresses in the To and Cc
+headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
+email address and anything that the configuration sub RT::IsRTAddress matches.
+
+=cut
+
+sub ParseCcAddressesFromHead {
+ my %args = (
+ Head => undef,
+ QueueObj => undef,
+ CurrentUser => undef,
+ @_
+ );
+
+ my (@Addresses);
+
+ my @ToObjs = Mail::Address->parse( $args{'Head'}->get('To') );
+ my @CcObjs = Mail::Address->parse( $args{'Head'}->get('Cc') );
+
+ foreach my $AddrObj ( @ToObjs, @CcObjs ) {
+ my $Address = $AddrObj->address;
+ $Address = $args{'CurrentUser'}
+ ->UserObj->CanonicalizeEmailAddress($Address);
+ next if ( $args{'CurrentUser'}->EmailAddress =~ /^\Q$Address\E$/i );
+ next if ( $args{'QueueObj'}->CorrespondAddress =~ /^\Q$Address\E$/i );
+ next if ( $args{'QueueObj'}->CommentAddress =~ /^\Q$Address\E$/i );
+ next if ( RT::EmailParser->IsRTAddress($Address) );
+
+ push( @Addresses, $Address );
+ }
+ return (@Addresses);
+}
+
+# }}}
+
+# {{{ ParseSenderAdddressFromHead
+
+=head2 ParseSenderAddressFromHead
+
+Takes a MIME::Header object. Returns a tuple: (user@host, friendly name)
+of the From (evaluated in order of Reply-To:, From:, Sender)
+
+=cut
+
+sub ParseSenderAddressFromHead {
+ my $head = shift;
+
+ #Figure out who's sending this message.
+ foreach my $header ('Reply-To', 'From', 'Sender') {
+ my $From = $head->get($header);
+ my ($addr, $name) = ParseAddressFromHeader($From);
+ # only return if the address is not empty
+ return ($addr, $name) if $addr;
+ }
+
+ return (undef, undef);
+}
+# }}}
+
+# {{{ ParseErrorsToAdddressFromHead
+
+=head2 ParseErrorsToAddressFromHead
+
+Takes a MIME::Header object. Return a single value : user@host
+of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
+From:, Sender)
+
+=cut
+
+sub ParseErrorsToAddressFromHead {
+ my $head = shift;
+
+ #Figure out who's sending this message.
+
+ foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
+
+ # If there's a header of that name
+ my $headerobj = $head->get($header);
+ if ($headerobj) {
+ my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
+
+ # If it's got actual useful content...
+ return ($addr) if ($addr);
+ }
+ }
+}
+
+# }}}
+
+# {{{ ParseAddressFromHeader
+
+=head2 ParseAddressFromHeader ADDRESS
+
+Takes an address from $head->get('Line') and returns a tuple: user@host, friendly name
+
+=cut
+
+sub ParseAddressFromHeader {
+ my $Addr = shift;
+
+ # Some broken mailers send: ""Vincent, Jesse"" <jesse@fsck.com>. Hate
+ $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
+ my @Addresses = Mail::Address->parse($Addr);
+
+ my ($AddrObj) = grep ref $_, @Addresses;
+ unless ( $AddrObj ) {
+ return ( undef, undef );
+ }
+
+ my $Name = ( $AddrObj->phrase || $AddrObj->comment || $AddrObj->address );
+
+ #Lets take the from and load a user object.
+ my $Address = $AddrObj->address;
+
+ return ( $Address, $Name );
+}
+
+# }}}
+
+# {{{ sub ParseTicketId
+
+sub ParseTicketId {
+ my $Subject = shift;
+ my $id;
+
+ my $test_name = $RT::EmailSubjectTagRegex || qr/\Q$RT::rtname\E/i;
+
+ if ( $Subject =~ s/\[$test_name\s+\#(\d+)\s*\]//i ) {
+ my $id = $1;
+ $RT::Logger->debug("Found a ticket ID. It's $id");
+ return ($id);
+ } else {
+ return (undef);
+ }
+}
+
+# }}}
+
+=head2 Gateway ARGSREF
+
+
+Takes parameters:
+
+ action
+ queue
+ message
+
+
+This performs all the "guts" of the mail rt-mailgate program, and is
+designed to be called from the web interface with a message, user
+object, and so on.
+
+Can also take an optional 'ticket' parameter; this ticket id overrides
+any ticket id found in the subject.
+
+Returns:
+
+ An array of:
+
+ (status code, message, optional ticket object)
+
+ status code is a numeric value.
+
+ for temporary failures, the status code should be -75
+
+ for permanent failures which are handled by RT, the status code
+ should be 0
+
+ for succces, the status code should be 1
+
+
+
+=cut
+
+sub Gateway {
+ my $argsref = shift;
+ my %args = (
+ action => 'correspond',
+ queue => '1',
+ ticket => undef,
+ message => undef,
+ %$argsref
+ );
+
+ my $SystemTicket;
+ my $Right;
+
+ # Validate the action
+ my ( $status, @actions ) = IsCorrectAction( $args{'action'} );
+ unless ($status) {
+ return (
+ -75,
+ "Invalid 'action' parameter "
+ . $actions[0]
+ . " for queue "
+ . $args{'queue'},
+ undef
+ );
+ }
+
+ my $parser = RT::EmailParser->new();
+ $parser->SmartParseMIMEEntityFromScalar( Message => $args{'message'} );
+ my $Message = $parser->Entity();
+
+ unless ($Message) {
+ MailError(
+ To => $RT::OwnerEmail,
+ Subject => "RT Bounce: Unparseable message",
+ Explanation => "RT couldn't process the message below",
+ Attach => $args{'message'}
+ );
+
+ return ( 0,
+ "Failed to parse this message. Something is likely badly wrong with the message"
+ );
+ }
+
+ my $head = $Message->head;
+
+ my $ErrorsTo = ParseErrorsToAddressFromHead($head);
+
+ my $MessageId = $head->get('Message-ID')
+ || "<no-message-id-" . time . rand(2000) . "\@.$RT::Organization>";
+
+ #Pull apart the subject line
+ my $Subject = $head->get('Subject') || '';
+ chomp $Subject;
+
+ # {{{ Lets check for mail loops of various sorts.
+ my ($should_store_machine_generated_message, $IsALoop, $result);
+ ( $should_store_machine_generated_message, $ErrorsTo, $result, $IsALoop ) =
+ _HandleMachineGeneratedMail(
+ Message => $Message,
+ ErrorsTo => $ErrorsTo,
+ Subject => $Subject,
+ MessageId => $MessageId
+ );
+
+ # Do not pass loop messages to MailPlugins, to make sure the loop
+ # is broken, unless $RT::StoreLoops is set.
+ if ($IsALoop && !$should_store_machine_generated_message) {
+ return ( 0, $result, undef );
+ }
+
+ $args{'ticket'} ||= ParseTicketId($Subject);
+
+ $SystemTicket = RT::Ticket->new($RT::SystemUser);
+ $SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
+ if ( $SystemTicket->id ) {
+ $Right = 'ReplyToTicket';
+ } else {
+ $Right = 'CreateTicket';
+ }
+
+ #Set up a queue object
+ my $SystemQueueObj = RT::Queue->new($RT::SystemUser);
+ $SystemQueueObj->Load( $args{'queue'} );
+
+ # We can safely have no queue of we have a known-good ticket
+ unless ( $SystemTicket->id || $SystemQueueObj->id ) {
+ return ( -75, "RT couldn't find the queue: " . $args{'queue'}, undef );
+ }
+
+ # Authentication Level ($AuthStat)
+ # -1 - Get out. this user has been explicitly declined
+ # 0 - User may not do anything (Not used at the moment)
+ # 1 - Normal user
+ # 2 - User is allowed to specify status updates etc. a la enhanced-mailgate
+ my ( $CurrentUser, $AuthStat, $error );
+
+ # Initalize AuthStat so comparisons work correctly
+ $AuthStat = -9999999;
+
+ push @RT::MailPlugins, "Auth::MailFrom" unless @RT::MailPlugins;
+
+ # if plugin returns AuthStat -2 we skip action
+ # NOTE: this is experimental API and it would be changed
+ my %skip_action = ();
+
+ # Since this needs loading, no matter what
+ foreach (@RT::MailPlugins) {
+ my ($Code, $NewAuthStat);
+ if ( ref($_) eq "CODE" ) {
+ $Code = $_;
+ } else {
+ my $Class = $_;
+ $Class = "RT::Interface::Email::" . $Class
+ unless $Class =~ /^RT::Interface::Email::/;
+ $Class->require or
+ do { $RT::Logger->error("Couldn't load $Class: $@"); next };
+
+ no strict 'refs';
+ unless ( defined( $Code = *{ $Class . "::GetCurrentUser" }{CODE} ) ) {
+ $RT::Logger->crit( "No 'GetCurrentUser' function found in '$Class' module");
+ next;
+ }
+ }
+
+ foreach my $action (@actions) {
+ ( $CurrentUser, $NewAuthStat ) = $Code->(
+ Message => $Message,
+ RawMessageRef => \$args{'message'},
+ CurrentUser => $CurrentUser,
+ AuthLevel => $AuthStat,
+ Action => $action,
+ Ticket => $SystemTicket,
+ Queue => $SystemQueueObj
+ );
+
+# You get the highest level of authentication you were assigned, unless you get the magic -1
+# If a module returns a "-1" then we discard the ticket, so.
+ $AuthStat = $NewAuthStat
+ if ( $NewAuthStat > $AuthStat or $NewAuthStat == -1 or $NewAuthStat == -2 );
+
+ last if $AuthStat == -1;
+ $skip_action{$action}++ if $AuthStat == -2;
+ }
+
+ # strip actions we should skip
+ @actions = grep !$skip_action{$_}, @actions if $AuthStat == -2;
+ last unless @actions;
+
+ last if $AuthStat == -1;
+ }
+ # {{{ If authentication fails and no new user was created, get out.
+ if ( !$CurrentUser || !$CurrentUser->id || $AuthStat == -1 ) {
+
+ # If the plugins refused to create one, they lose.
+ unless ( $AuthStat == -1 ) {
+ _NoAuthorizedUserFound(
+ Right => $Right,
+ Message => $Message,
+ Requestor => $ErrorsTo,
+ Queue => $args{'queue'}
+ );
+
+ }
+ return ( 0, "Could not load a valid user", undef );
+ }
+
+ # If we got a user, but they don't have the right to say things
+ if ( $AuthStat == 0 ) {
+ MailError(
+ To => $ErrorsTo,
+ Subject => "Permission Denied",
+ Explanation =>
+ "You do not have permission to communicate with RT",
+ MIMEObj => $Message
+ );
+ return (
+ 0,
+ "$ErrorsTo tried to submit a message to "
+ . $args{'Queue'}
+ . " without permission.",
+ undef
+ );
+ }
+
+
+ unless ($should_store_machine_generated_message) {
+ return ( 0, $result, undef );
+ }
+
+ # if plugin's updated SystemTicket then update arguments
+ $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
+
+ my $Ticket = RT::Ticket->new($CurrentUser);
+
+ if ( !$args{'ticket'} && grep /^(comment|correspond)$/, @actions )
+ {
+
+ my @Cc;
+ my @Requestors = ( $CurrentUser->id );
+
+ if ($RT::ParseNewMessageForTicketCcs) {
+ @Cc = ParseCcAddressesFromHead(
+ Head => $head,
+ CurrentUser => $CurrentUser,
+ QueueObj => $SystemQueueObj
+ );
+ }
+
+ my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
+ Queue => $SystemQueueObj->Id,
+ Subject => $Subject,
+ Requestor => \@Requestors,
+ Cc => \@Cc,
+ MIMEObj => $Message
+ );
+ if ( $id == 0 ) {
+ MailError(
+ To => $ErrorsTo,
+ Subject => "Ticket creation failed: $Subject",
+ Explanation => $ErrStr,
+ MIMEObj => $Message
+ );
+ return ( 0, "Ticket creation failed: $ErrStr", $Ticket );
+ }
+
+ # strip comments&corresponds from the actions we don't need
+ # to record them if we've created the ticket just now
+ @actions = grep !/^(comment|correspond)$/, @actions;
+ $args{'ticket'} = $id;
+
+ } elsif ( $args{'ticket'} ) {
+
+ $Ticket->Load( $args{'ticket'} );
+ unless ( $Ticket->Id ) {
+ my $error = "Could not find a ticket with id " . $args{'ticket'};
+ MailError(
+ To => $ErrorsTo,
+ Subject => "Message not recorded: $Subject",
+ Explanation => $error,
+ MIMEObj => $Message
+ );
+
+ return ( 0, $error );
+ }
+ $args{'ticket'} = $Ticket->id;
+ } else {
+ return ( 1, "Success", $Ticket );
+ }
+
+ # }}}
+ foreach my $action (@actions) {
+
+ # If the action is comment, add a comment.
+ if ( $action =~ /^(?:comment|correspond)$/i ) {
+ my $method = ucfirst lc $action;
+ my ( $status, $msg ) = $Ticket->$method( MIMEObj => $Message );
+ unless ($status) {
+
+ #Warn the sender that we couldn't actually submit the comment.
+ MailError(
+ To => $ErrorsTo,
+ Subject => "Message not recorded: $Subject",
+ Explanation => $msg,
+ MIMEObj => $Message
+ );
+ return ( 0, "Message not recorded: $msg", $Ticket );
+ }
+ } elsif ($RT::UnsafeEmailCommands) {
+ my ( $status, $msg ) = _RunUnsafeAction(
+ Action => $action,
+ ErrorsTo => $ErrorsTo,
+ Message => $Message,
+ Ticket => $Ticket,
+ CurrentUser => $CurrentUser,
+ );
+ return ($status, $msg, $Ticket) unless $status == 1;
+ }
+ }
+ return ( 1, "Success", $Ticket );
+}
+
+sub _RunUnsafeAction {
+ my %args = (
+ Action => undef,
+ ErrorsTo => undef,
+ Message => undef,
+ Ticket => undef,
+ CurrentUser => undef,
+ @_
+ );
+
+ if ( $args{'Action'} =~ /^take$/i ) {
+ my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
+ unless ($status) {
+ MailError(
+ To => $args{'ErrorsTo'},
+ Subject => "Ticket not taken",
+ Explanation => $msg,
+ MIMEObj => $args{'Message'}
+ );
+ return ( 0, "Ticket not taken" );
+ }
+ } elsif ( $args{'Action'} =~ /^resolve$/i ) {
+ my ( $status, $msg ) = $args{'Ticket'}->SetStatus('resolved');
+ unless ($status) {
+
+ #Warn the sender that we couldn't actually submit the comment.
+ MailError(
+ To => $args{'ErrorsTo'},
+ Subject => "Ticket not resolved",
+ Explanation => $msg,
+ MIMEObj => $args{'Message'}
+ );
+ return ( 0, "Ticket not resolved" );
+ }
+ } else {
+ return ( 0, "Not supported unsafe action $args{'Action'}", $args{'Ticket'} );
+ }
+ return ( 1, "Success" );
+}
+
+=head2 _NoAuthorizedUserFound
+
+Emails the RT Owner and the requestor when the auth plugins return "No auth user found"
+
+=cut
+
+sub _NoAuthorizedUserFound {
+ my %args = (
+ Right => undef,
+ Message => undef,
+ Requestor => undef,
+ Queue => undef,
+ @_
+ );
+
+ # Notify the RT Admin of the failure.
+ MailError(
+ To => $RT::OwnerEmail,
+ Subject => "Could not load a valid user",
+ Explanation => <<EOT,
+RT could not load a valid user, and RT's configuration does not allow
+for the creation of a new user for this email (@{[$args{Requestor}]}).
+
+You might need to grant 'Everyone' the right '@{[$args{Right}]}' for the
+queue @{[$args{'Queue'}]}.
+
+EOT
+ MIMEObj => $args{'Message'},
+ LogLevel => 'error'
+ );
+
+ # Also notify the requestor that his request has been dropped.
+ if ($args{'Requestor'} ne $RT::OwnerEmail) {
+ MailError(
+ To => $args{'Requestor'},
+ Subject => "Could not load a valid user",
+ Explanation => <<EOT,
+RT could not load a valid user, and RT's configuration does not allow
+for the creation of a new user for your email.
+
+EOT
+ MIMEObj => $args{'Message'},
+ LogLevel => 'error'
+ );
+ }
+}
+
+=head2 _HandleMachineGeneratedMail
+
+Takes named params:
+ Message
+ ErrorsTo
+ Subject
+
+Checks the message to see if it's a bounce, if it looks like a loop, if it's autogenerated, etc.
+Returns a triple of ("Should we continue (boolean)", "New value for $ErrorsTo", "Status message",
+"This message appears to be a loop (boolean)" );
+
+=cut
+
+sub _HandleMachineGeneratedMail {
+ my %args = ( Message => undef, ErrorsTo => undef, Subject => undef, MessageId => undef, @_ );
+ my $head = $args{'Message'}->head;
+ my $ErrorsTo = $args{'ErrorsTo'};
+
+ my $IsBounce = CheckForBounce($head);
+
+ my $IsAutoGenerated = CheckForAutoGenerated($head);
+
+ my $IsSuspiciousSender = CheckForSuspiciousSender($head);
+
+ my $IsALoop = CheckForLoops($head);
+
+ my $SquelchReplies = 0;
+
+ #If the message is autogenerated, we need to know, so we can not
+ # send mail to the sender
+ if ( $IsBounce || $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) {
+ $SquelchReplies = 1;
+ $ErrorsTo = $RT::OwnerEmail;
+ }
+
+ # Warn someone if it's a loop, before we drop it on the ground
+ if ($IsALoop) {
+ $RT::Logger->crit("RT Received mail (".$args{MessageId}.") from itself.");
+
+ #Should we mail it to RTOwner?
+ if ($RT::LoopsToRTOwner) {
+ MailError(
+ To => $RT::OwnerEmail,
+ Subject => "RT Bounce: ".$args{'Subject'},
+ Explanation => "RT thinks this message may be a bounce",
+ MIMEObj => $args{Message}
+ );
+ }
+
+ #Do we actually want to store it?
+ return ( 0, $ErrorsTo, "Message Bounced", $IsALoop ) unless ($RT::StoreLoops);
+ }
+
+ # Squelch replies if necessary
+ # Don't let the user stuff the RT-Squelch-Replies-To header.
+ if ( $head->get('RT-Squelch-Replies-To') ) {
+ $head->add(
+ 'RT-Relocated-Squelch-Replies-To',
+ $head->get('RT-Squelch-Replies-To')
+ );
+ $head->delete('RT-Squelch-Replies-To');
+ }
+
+ if ($SquelchReplies) {
+
+ # Squelch replies to the sender, and also leave a clue to
+ # allow us to squelch ALL outbound messages. This way we
+ # can punt the logic of "what to do when we get a bounce"
+ # to the scrip. We might want to notify nobody. Or just
+ # the RT Owner. Or maybe all Privileged watchers.
+ my ( $Sender, $junk ) = ParseSenderAddressFromHead($head);
+ $head->add( 'RT-Squelch-Replies-To', $Sender );
+ $head->add( 'RT-DetectedAutoGenerated', 'true' );
+ }
+ return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
+}
+
+=head2 IsCorrectAction
+
+Returns a list of valid actions we've found for this message
+
+=cut
+
+sub IsCorrectAction {
+ my $action = shift;
+ my @actions = grep $_, split /-/, $action;
+ return ( 0, '(no value)' ) unless @actions;
+ foreach (@actions) {
+ return ( 0, $_ ) unless /^(?:comment|correspond|take|resolve)$/;
+ }
+ return ( 1, @actions );
+}
+
+eval "require RT::Interface::Email_Vendor";
+die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Email_Vendor.pm} );
+eval "require RT::Interface::Email_Local";
+die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Email_Local.pm} );
+
+1;
diff --git a/rt/lib/RT/Interface/Email/Auth/GnuPG.pm b/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
new file mode 100755
index 0000000..1150807
--- /dev/null
+++ b/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
@@ -0,0 +1,123 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+#
+package RT::Interface::Email::Auth::GnuPG;
+use Mail::GnuPG;
+
+=head2 GetCurrentUser
+
+To use the gnupg-secured mail gateway, you need to do the following:
+
+Set up a gnupgp key directory with a pubring containing only the keys
+you care about and specify the following in your SiteConfig.pm
+
+Set($RT::GPGKeyDir, "/path/to/keyring-directory");
+@RT::MailPlugins = qw(Auth::MailFrom Auth::GnuPG Filter::TakeAction);
+
+
+
+=cut
+
+
+
+sub GetCurrentUser {
+ my %args = (
+ Message => undef,
+ RawMessageRef => undef,
+ CurrentUser => undef,
+ AuthLevel => undef,
+ Ticket => undef,
+ Queue => undef,
+ Action => undef,
+ @_
+ );
+
+ my ( $val, $key, $address,$gpg );
+
+ eval {
+
+ my $parser = RT::EmailParser->new();
+ $parser->SmartParseMIMEEntityFromScalar(Message => ${$args{'RawMessageRef'}}, Decode => 0);
+ $gpg = Mail::GnuPG->new( keydir => $RT::GPGKeyDir );
+ my $entity = $parser->Entity;
+ ( $val, $key, $address ) = $gpg->verify( $parser->Entity);
+ $RT::Logger->crit("Got $val - $key - $address");
+ };
+
+ if ($@) {
+ $RT::Logger->crit($@);
+ }
+
+ unless ($address) {
+ $RT::Logger->crit( "Couldn't find a valid signature" . join ( "\n", @{ $gpg->{'last_message'} } ) );
+ return ( $args{'CurrentUser'}, $args{'AuthLevel'} );
+ }
+
+ my @addrs = Mail::Address->parse($address);
+ $address = $addrs[0]->address();
+
+ my $CurrentUser = RT::CurrentUser->new();
+ $CurrentUser->LoadByEmail($address);
+
+ if ( $CurrentUser->Id ) {
+ $RT::Logger->crit($address . " authenticated via PGP signature");
+ return ( $CurrentUser, 2 );
+ }
+
+}
+
+eval "require RT::Interface::Email::Auth::GnuPG_Vendor";
+die $@
+ if ( $@
+ && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/GnuPG_Vendor.pm} );
+eval "require RT::Interface::Email::Auth::GnuPG_Local";
+die $@
+ if ( $@
+ && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/GnuPG_Local.pm} );
+
+1;
diff --git a/rt/lib/RT/Interface/Email/Auth/MailFrom.pm b/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
new file mode 100644
index 0000000..32009c3
--- /dev/null
+++ b/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
@@ -0,0 +1,189 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Email::Auth::MailFrom;
+use RT::Interface::Email qw(ParseSenderAddressFromHead CreateUser);
+
+# This is what the ordinary, non-enhanced gateway does at the moment.
+
+sub GetCurrentUser {
+ my %args = ( Message => undef,
+ CurrentUser => undef,
+ AuthLevel => undef,
+ Ticket => undef,
+ Queue => undef,
+ Action => undef,
+ @_ );
+
+
+ # We don't need to do any external lookups
+ my ( $Address, $Name ) = ParseSenderAddressFromHead( $args{'Message'}->head );
+
+ unless ($Address) {
+ return ( $args{'CurrentUser'}, -1 );
+ }
+
+ my $CurrentUser = RT::CurrentUser->new();
+ $CurrentUser->LoadByEmail($Address);
+
+ unless ( $CurrentUser->Id ) {
+ $CurrentUser->LoadByName($Address);
+ }
+
+ if ( $CurrentUser->Id ) {
+ return ( $CurrentUser, 1 );
+ }
+
+
+
+ # If the user can't be loaded, we may need to create one. Figure out the acl situation.
+ my $unpriv = RT::Group->new($RT::SystemUser);
+ $unpriv->LoadSystemInternalGroup('Unprivileged');
+ unless ( $unpriv->Id ) {
+ $RT::Logger->crit( "Auth::MailFrom couldn't find the 'Unprivileged' internal group" );
+ return ( $args{'CurrentUser'}, -1 );
+ }
+
+ my $everyone = RT::Group->new($RT::SystemUser);
+ $everyone->LoadSystemInternalGroup('Everyone');
+ unless ( $everyone->Id ) {
+ $RT::Logger->crit( "Auth::MailFrom couldn't find the 'Everyone' internal group");
+ return ( $args{'CurrentUser'}, -1 );
+ }
+
+ # but before we do that, we need to make sure that the created user would have the right
+ # to do what we're doing.
+ if ( $args{'Ticket'} && $args{'Ticket'}->Id ) {
+ # We have a ticket. that means we're commenting or corresponding
+ if ( $args{'Action'} =~ /^comment$/i ) {
+
+ # check to see whether "Everyone" or "Unprivileged users" can comment on tickets
+ unless ( $everyone->PrincipalObj->HasRight(
+ Object => $args{'Queue'},
+ Right => 'CommentOnTicket'
+ )
+ || $unpriv->PrincipalObj->HasRight(
+ Object => $args{'Queue'},
+ Right => 'CommentOnTicket'
+ )
+ ) {
+ return ( $args{'CurrentUser'}, 0 );
+ }
+ }
+ elsif ( $args{'Action'} =~ /^correspond$/i ) {
+
+ # check to see whether "Everybody" or "Unprivileged users" can correspond on tickets
+ unless ( $everyone->PrincipalObj->HasRight(Object => $args{'Queue'},
+ Right => 'ReplyToTicket'
+ )
+ || $unpriv->PrincipalObj->HasRight(
+ Object => $args{'Queue'},
+ Right => 'ReplyToTicket'
+ )
+ ) {
+ return ( $args{'CurrentUser'}, 0 );
+ }
+
+ }
+ elsif ( $args{'Action'} =~ /^take$/i ) {
+
+ # check to see whether "Everybody" or "Unprivileged users" can correspond on tickets
+ unless ( $everyone->PrincipalObj->HasRight(Object => $args{'Queue'},
+ Right => 'OwnTicket'
+ )
+ || $unpriv->PrincipalObj->HasRight(
+ Object => $args{'Queue'},
+ Right => 'OwnTicket'
+ )
+ ) {
+ return ( $args{'CurrentUser'}, 0 );
+ }
+
+ }
+ elsif ( $args{'Action'} =~ /^resolve$/i ) {
+
+ # check to see whether "Everybody" or "Unprivileged users" can correspond on tickets
+ unless ( $everyone->PrincipalObj->HasRight(Object => $args{'Queue'},
+ Right => 'ModifyTicket'
+ )
+ || $unpriv->PrincipalObj->HasRight(
+ Object => $args{'Queue'},
+ Right => 'ModifyTicket'
+ )
+ ) {
+ return ( $args{'CurrentUser'}, 0 );
+ }
+
+ }
+ else {
+ return ( $args{'CurrentUser'}, 0 );
+ }
+ }
+
+ # We're creating a ticket
+ elsif ( $args{'Queue'} && $args{'Queue'}->Id ) {
+
+ # check to see whether "Everybody" or "Unprivileged users" can create tickets in this queue
+ unless ( $everyone->PrincipalObj->HasRight( Object => $args{'Queue'},
+ Right => 'CreateTicket' )
+ ) {
+ return ( $args{'CurrentUser'}, 0 );
+ }
+
+ }
+
+ $CurrentUser = CreateUser( undef, $Address, $Name, $Address, $args{'Message'} );
+
+ return ( $CurrentUser, 1 );
+}
+
+eval "require RT::Interface::Email::Auth::MailFrom_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/MailFrom_Vendor.pm});
+eval "require RT::Interface::Email::Auth::MailFrom_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/MailFrom_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm b/rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm
new file mode 100644
index 0000000..2f8b61c
--- /dev/null
+++ b/rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm
@@ -0,0 +1,96 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Email::Filter::SpamAssassin;
+
+use Mail::SpamAssassin;
+my $spamtest = Mail::SpamAssassin->new();
+
+sub GetCurrentUser {
+ my %args = (
+ Message => undef,
+ CurrentUser => undef,
+ AuthLevel => undef,
+ @_
+ );
+ my $status = $spamtest->check( $args{'Message'} );
+ return ( $args{'CurrentUser'}, $args{'AuthLevel'} )
+ unless $status->is_spam();
+
+ eval { $status->rewrite_mail() };
+ if ( $status->get_hits > $status->get_required_hits() * 1.5 ) {
+
+ # Spammy indeed
+ return ( $args{'CurrentUser'}, -1 );
+ }
+ return ( $args{'CurrentUser'}, $args{'AuthLevel'} );
+
+}
+
+=head1 NAME
+
+RT::Interface::Email::Filter::SpamAssassin - Spam filter for RT
+
+=head1 SYNOPSIS
+
+ @RT::MailPlugins = ("Filter::SpamAssassin", ...);
+
+=head1 DESCRIPTION
+
+This plugin checks to see if an incoming mail is spam (using
+C<spamassassin>) and if so, rewrites its headers. If the mail is very
+definitely spam - 1.5x more hits than required - then it is dropped on
+the floor; otherwise, it is passed on as normal.
+
+=cut
+
+eval "require RT::Interface::Email::Filter::SpamAssassin_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Filter/SpamAssassin_Vendor.pm});
+eval "require RT::Interface::Email::Filter::SpamAssassin_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Filter/SpamAssassin_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Interface/REST.pm b/rt/lib/RT/Interface/REST.pm
new file mode 100644
index 0000000..bb2bf24
--- /dev/null
+++ b/rt/lib/RT/Interface/REST.pm
@@ -0,0 +1,288 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# lib/RT/Interface/REST.pm
+#
+
+package RT::Interface::REST;
+use strict;
+use RT;
+
+BEGIN {
+ use Exporter ();
+ use vars qw($VERSION @ISA @EXPORT);
+
+ $VERSION = do { my @r = (q$Revision: 1.1.1.6 $ =~ /\d+/g); sprintf "%d."."%02d"x$#r, @r };
+
+ @ISA = qw(Exporter);
+ @EXPORT = qw(expand_list form_parse form_compose vpush vsplit);
+}
+
+my $field = '(?i:[a-z][a-z0-9_-]*|C(?:ustom)?F(?:ield)?-(?:[a-z0-9_ -]|\s)+)';
+
+# WARN: this code is duplicated in bin/rt.in,
+# change both functions at once
+sub expand_list {
+ my ($list) = @_;
+
+ my @elts;
+ foreach (split /,/, $list) {
+ push @elts, /^(\d+)-(\d+)$/? ($1..$2): $_;
+ }
+
+ return map $_->[0], # schwartzian transform
+ sort {
+ defined $a->[1] && defined $b->[1]?
+ # both numbers
+ $a->[1] <=> $b->[1]
+ :!defined $a->[1] && !defined $b->[1]?
+ # both letters
+ $a->[2] cmp $b->[2]
+ # mix, number must be first
+ :defined $a->[1]? -1: 1
+ }
+ map [ $_, (defined( /^(\d+)$/ )? $1: undef), lc($_) ],
+ @elts;
+}
+
+# Returns a reference to an array of parsed forms.
+sub form_parse {
+ my $state = 0;
+ my @forms = ();
+ my @lines = split /\n/, $_[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+(.*))?$/i) {
+ # 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, $ls) = ("");
+ foreach $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);
+
+ my $l;
+ foreach $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, $form);
+
+ foreach $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, $key);
+
+ foreach $key (@$o) {
+ my ($line, $sp, $v);
+ my @values = (ref $k->{$key} eq 'ARRAY') ?
+ @{ $k->{$key} } :
+ $k->{$key};
+
+ $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;
+}
+
+# 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 ($line, $word, @words);
+
+ foreach $line (map {split /\n/} (ref $val eq 'ARRAY') ? @$val : $val)
+ {
+ # XXX: This should become a real parser, à la Text::ParseWords.
+ $line =~ s/^\s+//;
+ $line =~ s/\s+$//;
+ push @words, split /\s*,\s*/, $line;
+ }
+
+ return \@words;
+}
+
+1;
+
+=head1 NAME
+
+ RT::Interface::REST - helper functions for the REST interface.
+
+=head1 SYNOPSIS
+
+ Only the REST should use this module.
diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm
new file mode 100644
index 0000000..bc63f7c
--- /dev/null
+++ b/rt/lib/RT/Interface/Web.pm
@@ -0,0 +1,1687 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+## Portions Copyright 2000 Tobias Brox <tobix@fsck.com>
+
+## This is a library of static subs to be used by the Mason web
+## interface to RT
+
+
+=head1 NAME
+
+RT::Interface::Web
+
+=begin testing
+
+use_ok(RT::Interface::Web);
+
+=end testing
+
+=cut
+
+
+use strict;
+use warnings;
+
+package RT::Interface::Web;
+use HTTP::Date;
+use RT::SavedSearches;
+use URI;
+
+# {{{ EscapeUTF8
+
+=head2 EscapeUTF8 SCALARREF
+
+does a css-busting but minimalist escaping of whatever html you're passing in.
+
+=cut
+
+sub EscapeUTF8 {
+ my $ref = shift;
+ return unless defined $$ref;
+ my $val = $$ref;
+ use bytes;
+ $val =~ s/&/&#38;/g;
+ $val =~ s/</&lt;/g;
+ $val =~ s/>/&gt;/g;
+ $val =~ s/\(/&#40;/g;
+ $val =~ s/\)/&#41;/g;
+ $val =~ s/"/&#34;/g;
+ $val =~ s/'/&#39;/g;
+ $$ref = $val;
+ Encode::_utf8_on($$ref);
+
+
+}
+
+# }}}
+
+# {{{ EscapeURI
+
+=head2 EscapeURI SCALARREF
+
+Escapes URI component according to RFC2396
+
+=cut
+
+use Encode qw();
+sub EscapeURI {
+ my $ref = shift;
+ $$ref = Encode::encode_utf8( $$ref );
+ $$ref =~ s/([^a-zA-Z0-9_.!~*'()-])/uc sprintf("%%%02X", ord($1))/eg;
+ Encode::_utf8_on( $$ref );
+}
+
+# }}}
+
+# {{{ WebCanonicalizeInfo
+
+=head2 WebCanonicalizeInfo();
+
+Different web servers set different environmental varibles. This
+function must return something suitable for REMOTE_USER. By default,
+just downcase $ENV{'REMOTE_USER'}
+
+=cut
+
+sub WebCanonicalizeInfo {
+ my $user;
+
+ if ( defined $ENV{'REMOTE_USER'} ) {
+ $user = lc ( $ENV{'REMOTE_USER'} ) if( length($ENV{'REMOTE_USER'}) );
+ }
+
+ return $user;
+}
+
+# }}}
+
+# {{{ WebExternalAutoInfo
+
+=head2 WebExternalAutoInfo($user);
+
+Returns a hash of user attributes, used when WebExternalAuto is set.
+
+=cut
+
+sub WebExternalAutoInfo {
+ my $user = shift;
+
+ my %user_info;
+
+ $user_info{'Privileged'} = 1;
+
+ if ($^O !~ /^(?:riscos|MacOS|MSWin32|dos|os2)$/) {
+ # Populate fields with information from Unix /etc/passwd
+
+ my ($comments, $realname) = (getpwnam($user))[5, 6];
+ $user_info{'Comments'} = $comments if defined $comments;
+ $user_info{'RealName'} = $realname if defined $realname;
+ }
+ elsif ($^O eq 'MSWin32' and eval 'use Net::AdminMisc; 1') {
+ # Populate fields with information from NT domain controller
+ }
+
+ # and return the wad of stuff
+ return {%user_info};
+}
+
+# }}}
+
+
+
+=head2 Redirect URL
+
+This routine ells the current user's browser to redirect to URL.
+Additionally, it unties the user's currently active session, helping to avoid
+A bug in Apache::Session 1.81 and earlier which clobbers sessions if we try to use
+a cached DBI statement handle twice at the same time.
+
+=cut
+
+
+sub Redirect {
+ my $redir_to = shift;
+ untie $HTML::Mason::Commands::session;
+ my $uri = URI->new($redir_to);
+ my $server_uri = URI->new($RT::WebURL);
+
+ # If the user is coming in via a non-canonical
+ # hostname, don't redirect them to the canonical host,
+ # it will just upset them (and invalidate their credentials)
+ if ($uri->host eq $server_uri->host &&
+ $uri->port eq $server_uri->port) {
+ $uri->host($ENV{'HTTP_HOST'});
+ $uri->port($ENV{'SERVER_PORT'});
+ }
+
+ $HTML::Mason::Commands::m->redirect($uri->canonical);
+ $HTML::Mason::Commands::m->abort;
+}
+
+
+=head2 StaticFileHeaders
+
+Send the browser a few headers to try to get it to (somewhat agressively)
+cache RT's static Javascript and CSS files.
+
+This routine could really use _accurate_ heuristics. (XXX TODO)
+
+=cut
+
+sub StaticFileHeaders {
+ # make cache public
+ $HTML::Mason::Commands::r->headers_out->{'Cache-Control'} = 'max-age=259200, public';
+
+ # Expire things in a month.
+ $HTML::Mason::Commands::r->headers_out->{'Expires'} = HTTP::Date::time2str( time() + 2592000 );
+
+ # if we set 'Last-Modified' then browser request a comp using 'If-Modified-Since'
+ # request, but we don't handle it and generate full reply again
+ # Last modified at server start time
+ #$HTML::Mason::Commands::r->headers_out->{'Last-Modified'} = HTTP::Date::time2str($^T);
+
+}
+
+
+package HTML::Mason::Commands;
+use vars qw/$r $m %session/;
+
+
+# {{{ loc
+
+=head2 loc ARRAY
+
+loc is a nice clean global routine which calls $session{'CurrentUser'}->loc()
+with whatever it's called with. If there is no $session{'CurrentUser'},
+it creates a temporary user, so we have something to get a localisation handle
+through
+
+=cut
+
+sub loc {
+
+ if ($session{'CurrentUser'} &&
+ UNIVERSAL::can($session{'CurrentUser'}, 'loc')){
+ return($session{'CurrentUser'}->loc(@_));
+ }
+ elsif ( my $u = eval { RT::CurrentUser->new($RT::SystemUser->Id) } ) {
+ return ($u->loc(@_));
+ }
+ else {
+ # pathetic case -- SystemUser is gone.
+ return $_[0];
+ }
+}
+
+# }}}
+
+
+# {{{ loc_fuzzy
+
+=head2 loc_fuzzy STRING
+
+loc_fuzzy is for handling localizations of messages that may already
+contain interpolated variables, typically returned from libraries
+outside RT's control. It takes the message string and extracts the
+variable array automatically by matching against the candidate entries
+inside the lexicon file.
+
+=cut
+
+sub loc_fuzzy {
+ my $msg = shift;
+
+ if ($session{'CurrentUser'} &&
+ UNIVERSAL::can($session{'CurrentUser'}, 'loc')){
+ return($session{'CurrentUser'}->loc_fuzzy($msg));
+ }
+ else {
+ my $u = RT::CurrentUser->new($RT::SystemUser->Id);
+ return ($u->loc_fuzzy($msg));
+ }
+}
+
+# }}}
+
+
+# {{{ sub Abort
+# Error - calls Error and aborts
+sub Abort {
+
+ if ($session{'ErrorDocument'} &&
+ $session{'ErrorDocumentType'}) {
+ $r->content_type($session{'ErrorDocumentType'});
+ $m->comp($session{'ErrorDocument'} , Why => shift);
+ $m->abort;
+ }
+ else {
+ $m->comp("/Elements/Error" , Why => shift);
+ $m->abort;
+ }
+}
+
+# }}}
+
+# {{{ sub CreateTicket
+
+=head2 CreateTicket ARGS
+
+Create a new ticket, using Mason's %ARGS. returns @results.
+
+=cut
+
+sub CreateTicket {
+ my %ARGS = (@_);
+
+ my (@Actions);
+
+ my $Ticket = new RT::Ticket( $session{'CurrentUser'} );
+
+ my $Queue = new RT::Queue( $session{'CurrentUser'} );
+ unless ( $Queue->Load( $ARGS{'Queue'} ) ) {
+ Abort('Queue not found');
+ }
+
+ unless ( $Queue->CurrentUserHasRight('CreateTicket') ) {
+ Abort('You have no permission to create tickets in that queue.');
+ }
+
+ my $due = new RT::Date( $session{'CurrentUser'} );
+ $due->Set( Format => 'unknown', Value => $ARGS{'Due'} );
+ my $starts = new RT::Date( $session{'CurrentUser'} );
+ $starts->Set( Format => 'unknown', Value => $ARGS{'Starts'} );
+
+ my $MIMEObj = MakeMIMEEntity(
+ Subject => $ARGS{'Subject'},
+ From => $ARGS{'From'},
+ Cc => $ARGS{'Cc'},
+ Body => $ARGS{'Content'},
+ Type => $ARGS{'ContentType'},
+ );
+
+ if ( $ARGS{'Attachments'} ) {
+ my $rv = $MIMEObj->make_multipart;
+ $RT::Logger->error("Couldn't make multipart message")
+ if !$rv || $rv !~ /^(?:DONE|ALREADY)$/;
+
+ foreach ( values %{$ARGS{'Attachments'}} ) {
+ unless ( $_ ) {
+ $RT::Logger->error("Couldn't add empty attachemnt");
+ next;
+ }
+ $MIMEObj->add_part($_);
+ }
+ }
+
+ my %create_args = (
+ Type => $ARGS{'Type'} || 'ticket',
+ Queue => $ARGS{'Queue'},
+ Owner => $ARGS{'Owner'},
+ InitialPriority => $ARGS{'InitialPriority'},
+ FinalPriority => $ARGS{'FinalPriority'},
+ TimeLeft => $ARGS{'TimeLeft'},
+ TimeEstimated => $ARGS{'TimeEstimated'},
+ TimeWorked => $ARGS{'TimeWorked'},
+ Subject => $ARGS{'Subject'},
+ Status => $ARGS{'Status'},
+ Due => $due->ISO,
+ Starts => $starts->ISO,
+ MIMEObj => $MIMEObj
+ );
+
+ my @temp_squelch;
+ foreach my $type (qw(Requestors Cc AdminCc)) {
+ my @tmp = map { $_->format } grep { $_->address} Mail::Address->parse( $ARGS{ $type } );
+
+ $create_args{ $type } = [
+ grep $_, map {
+ my $user = RT::User->new( $RT::SystemUser );
+ $user->LoadOrCreateByEmail( $_ );
+ # convert to ids to avoid work later
+ $user->id;
+ } @tmp
+ ];
+ $RT::Logger->debug(
+ "$type got ".join(',',@{$create_args{ $type }}) );
+
+ }
+ # XXX: workaround for name conflict :(
+ $create_args{'Requestor'} = delete $create_args{'Requestors'};
+
+ foreach my $arg (keys %ARGS) {
+ next if $arg =~ /-(?:Magic|Category)$/;
+
+ if ($arg =~ /^Object-RT::Transaction--CustomField-/) {
+ $create_args{$arg} = $ARGS{$arg};
+ }
+ # Object-RT::Ticket--CustomField-3-Values
+ elsif ($arg =~ /^Object-RT::Ticket--CustomField-(\d+)(.*?)$/) {
+ my $cfid = $1;
+ my $cf = RT::CustomField->new( $session{'CurrentUser'});
+ $cf->Load($cfid);
+
+ if ( $cf->Type eq 'Freeform' && ! $cf->SingleValue) {
+ $ARGS{$arg} =~ s/\r\n/\n/g;
+ $ARGS{$arg} = [split('\n', $ARGS{$arg})];
+ }
+
+ if ( $cf->Type =~ /text/i) { # Catch both Text and Wikitext
+ $ARGS{$arg} =~ s/\r//g;
+ }
+
+ if ( $arg =~ /-Upload$/ ) {
+ $create_args{"CustomField-".$cfid} = _UploadedFile($arg);
+ }
+ else {
+ $create_args{"CustomField-".$cfid} = $ARGS{"$arg"};
+ }
+ }
+ }
+
+
+ # XXX TODO This code should be about six lines. and badly needs refactoring.
+
+ # {{{ turn new link lists into arrays, and pass in the proper arguments
+ my (@dependson, @dependedonby, @parents, @children, @refersto, @referredtoby);
+
+ foreach my $luri ( split ( / /, $ARGS{"new-DependsOn"} ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ push @dependson, $luri;
+ }
+ $create_args{'DependsOn'} = \@dependson;
+
+ foreach my $luri ( split ( / /, $ARGS{"DependsOn-new"} ) ) {
+ push @dependedonby, $luri;
+ }
+ $create_args{'DependedOnBy'} = \@dependedonby;
+
+ foreach my $luri ( split ( / /, $ARGS{"new-MemberOf"} ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ push @parents, $luri;
+ }
+ $create_args{'Parents'} = \@parents;
+
+ foreach my $luri ( split ( / /, $ARGS{"MemberOf-new"} ) ) {
+ push @children, $luri;
+ }
+ $create_args{'Children'} = \@children;
+
+ foreach my $luri ( split ( / /, $ARGS{"new-RefersTo"} ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ push @refersto, $luri;
+ }
+ $create_args{'RefersTo'} = \@refersto;
+
+ foreach my $luri ( split ( / /, $ARGS{"RefersTo-new"} ) ) {
+ push @referredtoby, $luri;
+ }
+ $create_args{'ReferredToBy'} = \@referredtoby;
+ # }}}
+
+
+ my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args);
+ unless ( $id ) {
+ Abort($ErrMsg);
+ }
+
+ push ( @Actions, split("\n", $ErrMsg) );
+ unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
+ Abort( "No permission to view newly created ticket #"
+ . $Ticket->id . "." );
+ }
+ return ( $Ticket, @Actions );
+
+}
+
+# }}}
+
+# {{{ sub LoadTicket - loads a ticket
+
+=head2 LoadTicket id
+
+Takes a ticket id as its only variable. if it's handed an array, it takes
+the first value.
+
+Returns an RT::Ticket object as the current user.
+
+=cut
+
+sub LoadTicket {
+ my $id = shift;
+
+ if ( ref($id) eq "ARRAY" ) {
+ $id = $id->[0];
+ }
+
+ unless ($id) {
+ Abort("No ticket specified");
+ }
+
+ my $Ticket = RT::Ticket->new( $session{'CurrentUser'} );
+ $Ticket->Load($id);
+ unless ( $Ticket->id ) {
+ Abort("Could not load ticket $id");
+ }
+ return $Ticket;
+}
+
+# }}}
+
+# {{{ sub ProcessUpdateMessage
+
+sub ProcessUpdateMessage {
+
+ #TODO document what else this takes.
+ my %args = (
+ ARGSRef => undef,
+ Actions => undef,
+ TicketObj => undef,
+ @_
+ );
+
+ #Make the update content have no 'weird' newlines in it
+ if ( $args{ARGSRef}->{'UpdateTimeWorked'}
+ || $args{ARGSRef}->{'UpdateContent'}
+ || $args{ARGSRef}->{'UpdateAttachments'} )
+ {
+
+ if (
+ $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject() )
+ {
+ $args{ARGSRef}->{'UpdateSubject'} = undef;
+ }
+
+ my $Message = MakeMIMEEntity(
+ Subject => $args{ARGSRef}->{'UpdateSubject'},
+ Body => $args{ARGSRef}->{'UpdateContent'},
+ Type => $args{ARGSRef}->{'UpdateContentType'},
+ );
+
+ $Message->head->add( 'Message-ID' =>
+ "<rt-"
+ . $RT::VERSION . "-"
+ . $$ . "-"
+ . CORE::time() . "-"
+ . int(rand(2000)) . "."
+ . $args{'TicketObj'}->id . "-"
+ . "0" . "-" # Scrip
+ . "0" . "@" # Email sent
+ . $RT::Organization
+ . ">" );
+ my $old_txn = RT::Transaction->new( $session{'CurrentUser'} );
+ if ( $args{ARGSRef}->{'QuoteTransaction'} ) {
+ $old_txn->Load( $args{ARGSRef}->{'QuoteTransaction'} );
+ }
+ else {
+ $old_txn = $args{TicketObj}->Transactions->First();
+ }
+
+ if ( $old_txn->Message && $old_txn->Message->First ) {
+ my @in_reply_to = split(/\s+/m, $old_txn->Message->First->GetHeader('In-Reply-To') || '');
+ my @references = split(/\s+/m, $old_txn->Message->First->GetHeader('References') || '' );
+ my @msgid = split(/\s+/m,$old_txn->Message->First->GetHeader('Message-ID') || '');
+ my @rtmsgid = split(/\s+/m,$old_txn->Message->First->GetHeader('RT-Message-ID') || '');
+
+ $Message->head->replace( 'In-Reply-To', join (' ', @rtmsgid ? @rtmsgid : @msgid));
+ $Message->head->replace( 'References', join(' ', @references, @msgid, @rtmsgid));
+ }
+
+ if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
+ $Message->make_multipart;
+ $Message->add_part($_)
+ foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
+ }
+
+ ## TODO: Implement public comments
+ if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
+ my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Comment(
+ CcMessageTo => $args{ARGSRef}->{'UpdateCc'},
+ BccMessageTo => $args{ARGSRef}->{'UpdateBcc'},
+ MIMEObj => $Message,
+ TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
+ );
+ push( @{ $args{Actions} }, $Description );
+ $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
+ }
+ elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) {
+ my ( $Transaction, $Description, $Object ) =
+ $args{TicketObj}->Correspond(
+ CcMessageTo => $args{ARGSRef}->{'UpdateCc'},
+ BccMessageTo => $args{ARGSRef}->{'UpdateBcc'},
+ MIMEObj => $Message,
+ TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
+ );
+ push( @{ $args{Actions} }, $Description );
+ $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
+ }
+ else {
+ push(
+ @{ $args{'Actions'} },
+ loc("Update type was neither correspondence nor comment.") . " "
+ . loc("Update not recorded.")
+ );
+ }
+}
+}
+
+# }}}
+
+# {{{ sub MakeMIMEEntity
+
+=head2 MakeMIMEEntity PARAMHASH
+
+Takes a paramhash Subject, Body and AttachmentFieldName.
+
+Also takes Form, Cc and Type as optional paramhash keys.
+
+ Returns a MIME::Entity.
+
+=cut
+
+sub MakeMIMEEntity {
+
+ #TODO document what else this takes.
+ my %args = (
+ Subject => undef,
+ From => undef,
+ Cc => undef,
+ Body => undef,
+ AttachmentFieldName => undef,
+ Type => undef,
+# map Encode::encode_utf8($_), @_,
+ @_,
+ );
+
+ #Make the update content have no 'weird' newlines in it
+
+ $args{'Body'} =~ s/\r\n/\n/gs if $args{'Body'};
+ my $Message;
+ {
+ # MIME::Head is not happy in utf-8 domain. This only happens
+ # when processing an incoming email (so far observed).
+ no utf8;
+ use bytes;
+ $Message = MIME::Entity->build(
+ Subject => $args{'Subject'} || "",
+ From => $args{'From'},
+ Cc => $args{'Cc'},
+ Type => $args{'Type'} || 'text/plain',
+ 'Charset:' => 'utf8',
+ Data => [ $args{'Body'} ]
+ );
+ }
+
+ my $cgi_object = $m->cgi_object;
+
+ if (my $filehandle = $cgi_object->upload( $args{'AttachmentFieldName'} ) ) {
+
+
+
+ use File::Temp qw(tempfile tempdir);
+
+ #foreach my $filehandle (@filenames) {
+
+ my ( $fh, $temp_file );
+ for ( 1 .. 10 ) {
+ # on NFS and NTFS, it is possible that tempfile() conflicts
+ # with other processes, causing a race condition. we try to
+ # accommodate this by pausing and retrying.
+ last if ($fh, $temp_file) = eval { tempfile( UNLINK => 1) };
+ sleep 1;
+ }
+
+ binmode $fh; #thank you, windows
+ my ($buffer);
+ while ( my $bytesread = read( $filehandle, $buffer, 4096 ) ) {
+ print $fh $buffer;
+ }
+
+ my $uploadinfo = $cgi_object->uploadInfo($filehandle);
+
+ # Prefer the cached name first over CGI.pm stringification.
+ my $filename = $RT::Mason::CGI::Filename;
+ $filename = "$filehandle" unless defined($filename);
+
+ $filename =~ s#^.*[\\/]##;
+
+ $Message->attach(
+ Path => $temp_file,
+ Filename => Encode::decode_utf8($filename),
+ Type => $uploadinfo->{'Content-Type'},
+ );
+ close($fh);
+
+ # }
+
+ }
+
+ $Message->make_singlepart();
+ RT::I18N::SetMIMEEntityToUTF8($Message); # convert text parts into utf-8
+
+ return ($Message);
+
+}
+
+# }}}
+
+# {{{ sub ProcessSearchQuery
+
+=head2 ProcessSearchQuery
+
+ Takes a form such as the one filled out in webrt/Search/Elements/PickRestriction and turns it into something that RT::Tickets can understand.
+
+TODO Doc exactly what comes in the paramhash
+
+
+=cut
+
+sub ProcessSearchQuery {
+ my %args = @_;
+
+ ## TODO: The only parameter here is %ARGS. Maybe it would be
+ ## cleaner to load this parameter as $ARGS, and use $ARGS->{...}
+ ## instead of $args{ARGS}->{...} ? :)
+
+ #Searches are sticky.
+ if ( defined $session{'tickets'} ) {
+
+ # Reset the old search
+ $session{'tickets'}->GotoFirstItem;
+ }
+ else {
+
+ # Init a new search
+ $session{'tickets'} = RT::Tickets->new( $session{'CurrentUser'} );
+ }
+
+ #Import a bookmarked search if we have one
+ if ( defined $args{ARGS}->{'Bookmark'} ) {
+ $session{'tickets'}->ThawLimits( $args{ARGS}->{'Bookmark'} );
+ }
+
+ # {{{ Goto next/prev page
+ if ( $args{ARGS}->{'GotoPage'} eq 'Next' ) {
+ $session{'tickets'}->NextPage;
+ }
+ elsif ( $args{ARGS}->{'GotoPage'} eq 'Prev' ) {
+ $session{'tickets'}->PrevPage;
+ }
+ elsif ( $args{ARGS}->{'GotoPage'} > 0 ) {
+ $session{'tickets'}->GotoPage( $args{ARGS}->{GotoPage} - 1 );
+ }
+
+ # }}}
+
+ # {{{ Deal with limiting the search
+
+ if ( $args{ARGS}->{'RefreshSearchInterval'} ) {
+ $session{'tickets_refresh_interval'} =
+ $args{ARGS}->{'RefreshSearchInterval'};
+ }
+
+ if ( $args{ARGS}->{'TicketsSortBy'} ) {
+ $session{'tickets_sort_by'} = $args{ARGS}->{'TicketsSortBy'};
+ $session{'tickets_sort_order'} = $args{ARGS}->{'TicketsSortOrder'};
+ $session{'tickets'}->OrderBy(
+ FIELD => $args{ARGS}->{'TicketsSortBy'},
+ ORDER => $args{ARGS}->{'TicketsSortOrder'}
+ );
+ }
+
+ # }}}
+
+ # {{{ Set the query limit
+ if ( defined $args{ARGS}->{'RowsPerPage'} ) {
+ $RT::Logger->debug(
+ "limiting to " . $args{ARGS}->{'RowsPerPage'} . " rows" );
+
+ $session{'tickets_rows_per_page'} = $args{ARGS}->{'RowsPerPage'};
+ $session{'tickets'}->RowsPerPage( $args{ARGS}->{'RowsPerPage'} );
+ }
+
+ # }}}
+ # {{{ Limit priority
+ if ( $args{ARGS}->{'ValueOfPriority'} ne '' ) {
+ $session{'tickets'}->LimitPriority(
+ VALUE => $args{ARGS}->{'ValueOfPriority'},
+ OPERATOR => $args{ARGS}->{'PriorityOp'}
+ );
+ }
+
+ # }}}
+ # {{{ Limit owner
+ if ( $args{ARGS}->{'ValueOfOwner'} ne '' ) {
+ $session{'tickets'}->LimitOwner(
+ VALUE => $args{ARGS}->{'ValueOfOwner'},
+ OPERATOR => $args{ARGS}->{'OwnerOp'}
+ );
+ }
+
+ # }}}
+ # {{{ Limit requestor email
+ if ( $args{ARGS}->{'ValueOfWatcherRole'} ne '' ) {
+ $session{'tickets'}->LimitWatcher(
+ TYPE => $args{ARGS}->{'WatcherRole'},
+ VALUE => $args{ARGS}->{'ValueOfWatcherRole'},
+ OPERATOR => $args{ARGS}->{'WatcherRoleOp'},
+
+ );
+ }
+
+ # }}}
+ # {{{ Limit Queue
+ if ( $args{ARGS}->{'ValueOfQueue'} ne '' ) {
+ $session{'tickets'}->LimitQueue(
+ VALUE => $args{ARGS}->{'ValueOfQueue'},
+ OPERATOR => $args{ARGS}->{'QueueOp'}
+ );
+ }
+
+ # }}}
+ # {{{ Limit Status
+ if ( $args{ARGS}->{'ValueOfStatus'} ne '' ) {
+ if ( ref( $args{ARGS}->{'ValueOfStatus'} ) ) {
+ foreach my $value ( @{ $args{ARGS}->{'ValueOfStatus'} } ) {
+ $session{'tickets'}->LimitStatus(
+ VALUE => $value,
+ OPERATOR => $args{ARGS}->{'StatusOp'},
+ );
+ }
+ }
+ else {
+ $session{'tickets'}->LimitStatus(
+ VALUE => $args{ARGS}->{'ValueOfStatus'},
+ OPERATOR => $args{ARGS}->{'StatusOp'},
+ );
+ }
+
+ }
+
+ # }}}
+ # {{{ Limit Subject
+ if ( $args{ARGS}->{'ValueOfSubject'} ne '' ) {
+ my $val = $args{ARGS}->{'ValueOfSubject'};
+ if ($args{ARGS}->{'SubjectOp'} =~ /like/) {
+ $val = "%".$val."%";
+ }
+ $session{'tickets'}->LimitSubject(
+ VALUE => $val,
+ OPERATOR => $args{ARGS}->{'SubjectOp'},
+ );
+ }
+
+ # }}}
+ # {{{ Limit Dates
+ if ( $args{ARGS}->{'ValueOfDate'} ne '' ) {
+ my $date = ParseDateToISO( $args{ARGS}->{'ValueOfDate'} );
+ $args{ARGS}->{'DateType'} =~ s/_Date$//;
+
+ if ( $args{ARGS}->{'DateType'} eq 'Updated' ) {
+ $session{'tickets'}->LimitTransactionDate(
+ VALUE => $date,
+ OPERATOR => $args{ARGS}->{'DateOp'},
+ );
+ }
+ else {
+ $session{'tickets'}->LimitDate( FIELD => $args{ARGS}->{'DateType'},
+ VALUE => $date,
+ OPERATOR => $args{ARGS}->{'DateOp'},
+ );
+ }
+ }
+
+ # }}}
+ # {{{ Limit Content
+ if ( $args{ARGS}->{'ValueOfAttachmentField'} ne '' ) {
+ my $val = $args{ARGS}->{'ValueOfAttachmentField'};
+ if ($args{ARGS}->{'AttachmentFieldOp'} =~ /like/) {
+ $val = "%".$val."%";
+ }
+ $session{'tickets'}->Limit(
+ FIELD => $args{ARGS}->{'AttachmentField'},
+ VALUE => $val,
+ OPERATOR => $args{ARGS}->{'AttachmentFieldOp'},
+ );
+ }
+
+ # }}}
+
+ # {{{ Limit CustomFields
+
+ foreach my $arg ( keys %{ $args{ARGS} } ) {
+ my $id;
+ if ( $arg =~ /^CustomField(\d+)$/ ) {
+ $id = $1;
+ }
+ else {
+ next;
+ }
+ next unless ( $args{ARGS}->{$arg} );
+
+ my $form = $args{ARGS}->{$arg};
+ my $oper = $args{ARGS}->{ "CustomFieldOp" . $id };
+ foreach my $value ( ref($form) ? @{$form} : ($form) ) {
+ my $quote = 1;
+ if ($oper =~ /like/i) {
+ $value = "%".$value."%";
+ }
+ if ( $value =~ /^null$/i ) {
+
+ #Don't quote the string 'null'
+ $quote = 0;
+
+ # Convert the operator to something apropriate for nulls
+ $oper = 'IS' if ( $oper eq '=' );
+ $oper = 'IS NOT' if ( $oper eq '!=' );
+ }
+ $session{'tickets'}->LimitCustomField( CUSTOMFIELD => $id,
+ OPERATOR => $oper,
+ QUOTEVALUE => $quote,
+ VALUE => $value );
+ }
+ }
+
+ # }}}
+
+
+}
+
+# }}}
+
+# {{{ sub ParseDateToISO
+
+=head2 ParseDateToISO
+
+Takes a date in an arbitrary format.
+Returns an ISO date and time in GMT
+
+=cut
+
+sub ParseDateToISO {
+ my $date = shift;
+
+ my $date_obj = RT::Date->new($session{'CurrentUser'});
+ $date_obj->Set(
+ Format => 'unknown',
+ Value => $date
+ );
+ return ( $date_obj->ISO );
+}
+
+# }}}
+
+# {{{ sub ProcessACLChanges
+
+sub ProcessACLChanges {
+ my $ARGSref = shift;
+
+ my %ARGS = %$ARGSref;
+
+ my ( $ACL, @results );
+
+
+ foreach my $arg (keys %ARGS) {
+ if ($arg =~ /GrantRight-(\d+)-(.*?)-(\d+)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $rights = $ARGS{$arg};
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+
+ my $obj;
+
+ if ($object_type eq 'RT::System') {
+ $obj = $RT::System;
+ } elsif ($RT::ACE::OBJECT_TYPES{$object_type}) {
+ $obj = $object_type->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } else {
+ push (@results, loc("System Error"). ': '.
+ loc("Rights could not be granted for [_1]", $object_type));
+ next;
+ }
+
+ my @rights = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} : ($ARGS{$arg});
+ foreach my $right (@rights) {
+ next unless ($right);
+ my ($val, $msg) = $principal->GrantRight(Object => $obj, Right => $right);
+ push (@results, $msg);
+ }
+ }
+ elsif ($arg =~ /RevokeRight-(\d+)-(.*?)-(\d+)-(.*?)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $right = $4;
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+ next unless ($right);
+ my $obj;
+
+ if ($object_type eq 'RT::System') {
+ $obj = $RT::System;
+ } elsif ($RT::ACE::OBJECT_TYPES{$object_type}) {
+ $obj = $object_type->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } else {
+ push (@results, loc("System Error"). ': '.
+ loc("Rights could not be revoked for [_1]", $object_type));
+ next;
+ }
+ my ($val, $msg) = $principal->RevokeRight(Object => $obj, Right => $right);
+ push (@results, $msg);
+ }
+
+
+ }
+
+ return (@results);
+
+ }
+
+# }}}
+
+# {{{ sub UpdateRecordObj
+
+=head2 UpdateRecordObj ( ARGSRef => \%ARGS, Object => RT::Record, AttributesRef => \@attribs)
+
+@attribs is a list of ticket fields to check and update if they differ from the B<Object>'s current values. ARGSRef is a ref to HTML::Mason's %ARGS.
+
+Returns an array of success/failure messages
+
+=cut
+
+sub UpdateRecordObject {
+ my %args = (
+ ARGSRef => undef,
+ AttributesRef => undef,
+ Object => undef,
+ AttributePrefix => undef,
+ @_
+ );
+
+ my $Object = $args{'Object'};
+ my @results = $Object->Update(AttributesRef => $args{'AttributesRef'},
+ ARGSRef => $args{'ARGSRef'},
+ AttributePrefix => $args{'AttributePrefix'}
+ );
+
+ return (@results);
+}
+
+# }}}
+
+# {{{ Sub ProcessCustomFieldUpdates
+
+sub ProcessCustomFieldUpdates {
+ my %args = (
+ CustomFieldObj => undef,
+ ARGSRef => undef,
+ @_
+ );
+
+ my $Object = $args{'CustomFieldObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ my @attribs = qw( Name Type Description Queue SortOrder);
+ my @results = UpdateRecordObject(
+ AttributesRef => \@attribs,
+ Object => $Object,
+ ARGSRef => $ARGSRef
+ );
+
+ if ( $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" } ) {
+
+ my ( $addval, $addmsg ) = $Object->AddValue(
+ Name =>
+ $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" },
+ Description => $ARGSRef->{ "CustomField-"
+ . $Object->Id
+ . "-AddValue-Description" },
+ SortOrder => $ARGSRef->{ "CustomField-"
+ . $Object->Id
+ . "-AddValue-SortOrder" },
+ );
+ push ( @results, $addmsg );
+ }
+ my @delete_values = (
+ ref $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } eq
+ 'ARRAY' )
+ ? @{ $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } }
+ : ( $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } );
+ foreach my $id (@delete_values) {
+ next unless defined $id;
+ my ( $err, $msg ) = $Object->DeleteValue($id);
+ push ( @results, $msg );
+ }
+
+ my $vals = $Object->Values();
+ while (my $cfv = $vals->Next()) {
+ if (my $so = $ARGSRef->{ 'CustomField-' . $Object->Id . '-SortOrder' . $cfv->Id }) {
+ if ($cfv->SortOrder != $so) {
+ my ( $err, $msg ) = $cfv->SetSortOrder($so);
+ push ( @results, $msg );
+ }
+ }
+ }
+
+ return (@results);
+}
+
+# }}}
+
+# {{{ sub ProcessTicketBasics
+
+=head2 ProcessTicketBasics ( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+Returns an array of results messages.
+
+=cut
+
+sub ProcessTicketBasics {
+
+ my %args = (
+ TicketObj => undef,
+ ARGSRef => undef,
+ @_
+ );
+
+ my $TicketObj = $args{'TicketObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ # {{{ Set basic fields
+ my @attribs = qw(
+ Subject
+ FinalPriority
+ Priority
+ TimeEstimated
+ TimeWorked
+ TimeLeft
+ Type
+ Status
+ Queue
+ );
+
+
+ if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) {
+ my $tempqueue = RT::Queue->new($RT::SystemUser);
+ $tempqueue->Load( $ARGSRef->{'Queue'} );
+ if ( $tempqueue->id ) {
+ $ARGSRef->{'Queue'} = $tempqueue->Id();
+ }
+ }
+
+
+ # Status isn't a field that can be set to a null value.
+ # RT core complains if you try
+ delete $ARGSRef->{'Status'} unless ($ARGSRef->{'Status'});
+
+ my @results = UpdateRecordObject(
+ AttributesRef => \@attribs,
+ Object => $TicketObj,
+ ARGSRef => $ARGSRef
+ );
+
+ # We special case owner changing, so we can use ForceOwnerChange
+ if ( $ARGSRef->{'Owner'} && ( $TicketObj->Owner != $ARGSRef->{'Owner'} ) ) {
+ my ($ChownType);
+ if ( $ARGSRef->{'ForceOwnerChange'} ) {
+ $ChownType = "Force";
+ }
+ else {
+ $ChownType = "Give";
+ }
+
+ my ( $val, $msg ) =
+ $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType );
+ push ( @results, $msg );
+ }
+
+ # }}}
+
+ return (@results);
+}
+
+# }}}
+
+sub ProcessTicketCustomFieldUpdates {
+ my %args = @_;
+ $args{'Object'} = delete $args{'TicketObj'};
+ my $ARGSRef = { %{ $args{'ARGSRef'} } };
+
+ # Build up a list of objects that we want to work with
+ my %custom_fields_to_mod;
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /^Ticket-(\d+-.*)/) {
+ $ARGSRef->{"Object-RT::Ticket-$1"} = delete $ARGSRef->{$arg};
+ }
+ elsif ( $arg =~ /^CustomField-(\d+-.*)/) {
+ $ARGSRef->{"Object-RT::Ticket--$1"} = delete $ARGSRef->{$arg};
+ }
+ }
+
+ return ProcessObjectCustomFieldUpdates(%args, ARGSRef => $ARGSRef);
+}
+
+sub ProcessObjectCustomFieldUpdates {
+ my %args = @_;
+ my $ARGSRef = $args{'ARGSRef'};
+ my @results;
+
+ # Build up a list of objects that we want to work with
+ my %custom_fields_to_mod;
+ foreach my $arg ( keys %$ARGSRef ) {
+ # format: Object-<object class>-<object id>-CustomField-<CF id>-<commands>
+ next unless $arg =~ /^Object-([\w:]+)-(\d*)-CustomField-(\d+)-(.*)$/;
+
+ # For each of those objects, find out what custom fields we want to work with.
+ $custom_fields_to_mod{ $1 }{ $2 || 0 }{ $3 }{ $4 } = $ARGSRef->{ $arg };
+ }
+
+ # For each of those objects
+ foreach my $class ( keys %custom_fields_to_mod ) {
+ foreach my $id ( keys %{$custom_fields_to_mod{$class}} ) {
+ my $Object = $args{'Object'};
+ $Object = $class->new( $session{'CurrentUser'} )
+ unless $Object && ref $Object eq $class;
+
+ $Object->Load( $id ) unless ($Object->id || 0) == $id;
+ unless ( $Object->id ) {
+ $RT::Logger->warning("Couldn't load object $class #$id");
+ next;
+ }
+
+ foreach my $cf ( keys %{ $custom_fields_to_mod{ $class }{ $id } } ) {
+ my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
+ $CustomFieldObj->LoadById( $cf );
+ unless ( $CustomFieldObj->id ) {
+ $RT::Logger->warning("Couldn't load custom field #$id");
+ next;
+ }
+ push @results, _ProcessObjectCustomFieldUpdates(
+ Prefix => "Object-$class-$id-CustomField-$cf-",
+ Object => $Object,
+ CustomField => $CustomFieldObj,
+ ARGS => $custom_fields_to_mod{$class}{$id}{$cf},
+ );
+ }
+ }
+ }
+ return @results;
+}
+
+sub _ProcessObjectCustomFieldUpdates {
+ my %args = @_;
+ my $cf = $args{'CustomField'};
+ my $cf_type = $cf->Type;
+
+ my @results;
+ foreach my $arg ( keys %{ $args{'ARGS'} } ) {
+
+ next if $arg =~ /Category$/;
+
+ # since http won't pass in a form element with a null value, we need
+ # to fake it
+ if ( $arg eq 'Values-Magic' ) {
+ # We don't care about the magic, if there's really a values element;
+ next if $args{'ARGS'}->{'Value'} || $args{'ARGS'}->{'Values'};
+
+ # "Empty" values does not mean anything for Image and Binary fields
+ next if $cf_type =~ /^(?:Image|Binary)$/;
+
+ $arg = 'Values';
+ $args{'ARGS'}->{'Values'} = undef;
+ }
+
+ my @values = ();
+ if ( ref $args{'ARGS'}->{ $arg } eq 'ARRAY' ) {
+ @values = @{ $args{'ARGS'}->{$arg} };
+ } elsif ( $cf_type =~ /text/i ) { # Both Text and Wikitext
+ @values = ($args{'ARGS'}->{$arg});
+ } elsif ( defined( $args{'ARGS'}->{ $arg } ) ) {
+ @values = split /\n/, $args{'ARGS'}->{ $arg };
+ }
+
+ if ( ( $cf_type eq 'Freeform' && !$cf->SingleValue ) || $cf_type =~ /text/i ) {
+ s/\r//g foreach @values;
+ }
+ @values = grep defined && $_ ne '', @values;
+
+ if ( $arg eq 'AddValue' || $arg eq 'Value' ) {
+ foreach my $value (@values) {
+ my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
+ Field => $cf->id,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ elsif ( $arg eq 'Upload' ) {
+ my $value_hash = _UploadedFile( $args{'Prefix'} . $arg ) or next;
+ my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
+ %$value_hash,
+ Field => $cf,
+ );
+ push ( @results, $msg );
+ }
+ elsif ( $arg eq 'DeleteValues' ) {
+ foreach my $value ( @values ) {
+ my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
+ Field => $cf,
+ Value => $value,
+ );
+ push ( @results, $msg );
+ }
+ }
+ elsif ( $arg eq 'DeleteValueIds' ) {
+ foreach my $value ( @values ) {
+ my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
+ Field => $cf,
+ ValueId => $value,
+ );
+ push ( @results, $msg );
+ }
+ }
+ elsif ( $arg eq 'Values' && !$cf->Repeated ) {
+ my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id );
+
+ my %values_hash;
+ foreach my $value ( @values ) {
+ # build up a hash of values that the new set has
+ $values_hash{$value} = 1;
+ next if $cf_values->HasEntry( $value );
+
+ my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+
+ $cf_values->RedoSearch;
+ while ( my $cf_value = $cf_values->Next ) {
+ next if $values_hash{ $cf_value->Content };
+
+ my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
+ Field => $cf,
+ Value => $cf_value->Content
+ );
+ push ( @results, $msg);
+ }
+ }
+ elsif ( $arg eq 'Values' ) {
+ my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id );
+
+ # keep everything up to the point of difference, delete the rest
+ my $delete_flag;
+ foreach my $old_cf (@{$cf_values->ItemsArrayRef}) {
+ if (!$delete_flag and @values and $old_cf->Content eq $values[0]) {
+ shift @values;
+ next;
+ }
+
+ $delete_flag ||= 1;
+ $old_cf->Delete;
+ }
+
+ # now add/replace extra things, if any
+ foreach my $value ( @values ) {
+ my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ else {
+ push ( @results,
+ loc("User asked for an unknown update type for custom field [_1] for [_2] object #[_3]",
+ $cf->Name, ref $args{'Object'}, $args{'Object'}->id )
+ );
+ }
+ }
+ return @results;
+}
+
+# {{{ sub ProcessTicketWatchers
+
+=head2 ProcessTicketWatchers ( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+Returns an array of results messages.
+
+=cut
+
+sub ProcessTicketWatchers {
+ my %args = (
+ TicketObj => undef,
+ ARGSRef => undef,
+ @_
+ );
+ my (@results);
+
+ my $Ticket = $args{'TicketObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ # Munge watchers
+
+ foreach my $key ( keys %$ARGSRef ) {
+
+ # Delete deletable watchers
+ if ( ( $key =~ /^Ticket-DeleteWatcher-Type-(.*)-Principal-(\d+)$/ ) )
+ {
+ my ( $code, $msg ) = $Ticket->DeleteWatcher(
+ PrincipalId => $2,
+ Type => $1
+ );
+ push @results, $msg;
+ }
+
+ # Delete watchers in the simple style demanded by the bulk manipulator
+ elsif ( $key =~ /^Delete(Requestor|Cc|AdminCc)$/ ) {
+ my ( $code, $msg ) = $Ticket->DeleteWatcher(
+ Email => $ARGSRef->{$key},
+ Type => $1
+ );
+ push @results, $msg;
+ }
+
+ # Add new wathchers by email address
+ elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ )
+ and ( $key =~ /^WatcherTypeEmail(\d*)$/ ) )
+ {
+
+ #They're in this order because otherwise $1 gets clobbered :/
+ my ( $code, $msg ) = $Ticket->AddWatcher(
+ Type => $ARGSRef->{$key},
+ Email => $ARGSRef->{ "WatcherAddressEmail" . $1 }
+ );
+ push @results, $msg;
+ }
+
+ #Add requestors in the simple style demanded by the bulk manipulator
+ elsif ( $key =~ /^Add(Requestor|Cc|AdminCc)$/ ) {
+ my ( $code, $msg ) = $Ticket->AddWatcher(
+ Type => $1,
+ Email => $ARGSRef->{$key}
+ );
+ push @results, $msg;
+ }
+
+ # Add new watchers by owner
+ elsif ( $key =~ /^Ticket-AddWatcher-Principal-(\d*)$/ ) {
+ my $principal_id = $1;
+ my $form = $ARGSRef->{$key};
+ foreach my $value ( ref($form) ? @{$form} : ($form) ) {
+ next unless $value =~ /^(?:AdminCc|Cc|Requestor)$/i;
+
+ my ( $code, $msg ) = $Ticket->AddWatcher(
+ Type => $value,
+ PrincipalId => $principal_id
+ );
+ push @results, $msg;
+ }
+ }
+
+ }
+ return (@results);
+}
+
+# }}}
+
+# {{{ sub ProcessTicketDates
+
+=head2 ProcessTicketDates ( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+Returns an array of results messages.
+
+=cut
+
+sub ProcessTicketDates {
+ my %args = (
+ TicketObj => undef,
+ ARGSRef => undef,
+ @_
+ );
+
+ my $Ticket = $args{'TicketObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ my (@results);
+
+ # {{{ Set date fields
+ my @date_fields = qw(
+ Told
+ Resolved
+ Starts
+ Started
+ Due
+ );
+
+ #Run through each field in this list. update the value if apropriate
+ foreach my $field (@date_fields) {
+ my ( $code, $msg );
+
+ my $DateObj = RT::Date->new( $session{'CurrentUser'} );
+
+ #If it's something other than just whitespace
+ if ( $ARGSRef->{ $field . '_Date' } && ($ARGSRef->{ $field . '_Date' } ne '') ) {
+ $DateObj->Set(
+ Format => 'unknown',
+ Value => $ARGSRef->{ $field . '_Date' }
+ );
+ my $obj = $field . "Obj";
+ if ( ( defined $DateObj->Unix )
+ and ( $DateObj->Unix ne $Ticket->$obj()->Unix() ) )
+ {
+ my $method = "Set$field";
+ my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO );
+ push @results, "$msg";
+ }
+ }
+ }
+
+ # }}}
+ return (@results);
+}
+
+# }}}
+
+# {{{ sub ProcessTicketLinks
+
+=head2 ProcessTicketLinks ( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+Returns an array of results messages.
+
+=cut
+
+sub ProcessTicketLinks {
+ my %args = ( TicketObj => undef,
+ ARGSRef => undef,
+ @_ );
+
+ my $Ticket = $args{'TicketObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+
+ my (@results) = ProcessRecordLinks(RecordObj => $Ticket,
+ ARGSRef => $ARGSRef);
+
+ #Merge if we need to
+ if ( $ARGSRef->{ $Ticket->Id . "-MergeInto" } ) {
+ my ( $val, $msg ) =
+ $Ticket->MergeInto( $ARGSRef->{ $Ticket->Id . "-MergeInto" } );
+ push @results, $msg;
+ }
+
+ return (@results);
+}
+
+# }}}
+
+sub ProcessRecordLinks {
+ my %args = ( RecordObj => undef,
+ ARGSRef => undef,
+ @_ );
+
+ my $Record = $args{'RecordObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ my (@results);
+
+ # Delete links that are gone gone gone.
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+ my $base = $1;
+ my $type = $2;
+ my $target = $3;
+
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Record->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
+
+ push @results, $msg;
+
+ }
+
+ }
+
+ my @linktypes = qw( DependsOn MemberOf RefersTo );
+
+ foreach my $linktype (@linktypes) {
+ if ( $ARGSRef->{ $Record->Id . "-$linktype" } ) {
+ for my $luri ( split ( / /, $ARGSRef->{ $Record->Id . "-$linktype" } ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ my ( $val, $msg ) = $Record->AddLink( Target => $luri,
+ Type => $linktype );
+ push @results, $msg;
+ }
+ }
+ if ( $ARGSRef->{ "$linktype-" . $Record->Id } ) {
+
+ for my $luri ( split ( / /, $ARGSRef->{ "$linktype-" . $Record->Id } ) ) {
+ my ( $val, $msg ) = $Record->AddLink( Base => $luri,
+ Type => $linktype );
+
+ push @results, $msg;
+ }
+ }
+ }
+
+ return (@results);
+}
+
+
+=head2 _UploadedFile ( $arg );
+
+Takes a CGI parameter name; if a file is uploaded under that name,
+return a hash reference suitable for AddCustomFieldValue's use:
+C<( Value => $filename, LargeContent => $content, ContentType => $type )>.
+
+Returns C<undef> if no files were uploaded in the C<$arg> field.
+
+=cut
+
+sub _UploadedFile {
+ my $arg = shift;
+ my $cgi_object = $m->cgi_object;
+ my $fh = $cgi_object->upload($arg) or return undef;
+ my $upload_info = $cgi_object->uploadInfo($fh);
+
+ my $filename = "$fh";
+ $filename =~ s#^.*[\\/]##;
+ binmode($fh);
+
+ return {
+ Value => $filename,
+ LargeContent => do { local $/; scalar <$fh> },
+ ContentType => $upload_info->{'Content-Type'},
+ };
+}
+
+=head2 _load_container_object ( $type, $id );
+
+Instantiate container object for saving searches.
+
+=cut
+
+sub _load_container_object {
+ my ($obj_type, $obj_id) = @_;
+ return RT::SavedSearch->new($session{'CurrentUser'})->_load_privacy_object($obj_type, $obj_id);
+}
+
+=head2 _parse_saved_search ( $arg );
+
+Given a serialization string for saved search, and returns the
+container object and the search id.
+
+=cut
+
+sub _parse_saved_search {
+ my $spec = shift;
+ return unless $spec;
+ if ($spec !~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/ ) {
+ return;
+ }
+ my $obj_type = $1;
+ my $obj_id = $2;
+ my $search_id = $3;
+
+ return (_load_container_object ($obj_type, $obj_id), $search_id);
+}
+
+eval "require RT::Interface::Web_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Vendor.pm});
+eval "require RT::Interface::Web_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Interface/Web/Handler.pm b/rt/lib/RT/Interface/Web/Handler.pm
new file mode 100644
index 0000000..1e871ec
--- /dev/null
+++ b/rt/lib/RT/Interface/Web/Handler.pm
@@ -0,0 +1,211 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Web::Handler;
+
+use CGI qw/-private_tempfiles/;
+use MIME::Entity;
+use Text::Wrapper;
+use CGI::Cookie;
+use Time::ParseDate;
+use Time::HiRes;
+use HTML::Entities;
+use HTML::Scrubber;
+use RT::Interface::Web::Handler;
+use File::Path qw( rmtree );
+use File::Glob qw( bsd_glob );
+use File::Spec::Unix;
+
+sub DefaultHandlerArgs { (
+ comp_root => [
+ [ local => $RT::MasonLocalComponentRoot ],
+ [ standard => $RT::MasonComponentRoot ]
+ ],
+ default_escape_flags => 'h',
+ data_dir => "$RT::MasonDataDir",
+ allow_globals => [qw(%session)],
+ # Turn off static source if we're in developer mode.
+ static_source => ($RT::DevelMode ? '0' : '1'),
+ use_object_files => ($RT::DevelMode ? '0' : '1'),
+ autoflush => 0
+) };
+
+# {{{ sub new
+
+=head2 new
+
+ Constructs a web handler of the appropriate class.
+ Takes options to pass to the constructor.
+
+=cut
+
+sub new {
+ my $class = shift;
+ $class->InitSessionDir;
+
+ if ( $mod_perl::VERSION && $mod_perl::VERSION >= 1.9908 ) {
+ goto &NewApacheHandler;
+ }
+ elsif ($CGI::MOD_PERL) {
+ goto &NewApacheHandler;
+ }
+ else {
+ goto &NewCGIHandler;
+ }
+}
+
+sub InitSessionDir {
+ # Activate the following if running httpd as root (the normal case).
+ # Resets ownership of all files created by Mason at startup.
+ # Note that mysql uses DB for sessions, so there's no need to do this.
+ unless ( $RT::DatabaseType =~ /(?:mysql|Pg)/ ) {
+
+ # Clean up our umask to protect session files
+ umask(0077);
+
+ if ($CGI::MOD_PERL) { local $@; eval {
+
+ chown( Apache->server->uid, Apache->server->gid,
+ $RT::MasonSessionDir )
+ }}
+
+ # Die if WebSessionDir doesn't exist or we can't write to it
+ stat($RT::MasonSessionDir);
+ die "Can't read and write $RT::MasonSessionDir"
+ unless ( ( -d _ ) and ( -r _ ) and ( -w _ ) );
+ }
+
+}
+
+# }}}
+
+# {{{ sub NewApacheHandler
+
+=head2 NewApacheHandler
+
+ Takes extra options to pass to HTML::Mason::ApacheHandler->new
+ Returns a new Mason::ApacheHandler object
+
+=cut
+
+sub NewApacheHandler {
+ require HTML::Mason::ApacheHandler;
+ return NewHandler('HTML::Mason::ApacheHandler', args_method => "CGI", @_);
+}
+
+# }}}
+
+# {{{ sub NewApache2Handler
+
+=head2 NewApache2Handler
+
+ Takes extra options to pass to MasonX::Apache2Handler->new
+ Returns a new MasonX::Apache2Handler object
+
+=cut
+
+sub NewApache2Handler {
+ require MasonX::Apache2Handler;
+ return NewHandler('MasonX::Apache2Handler', args_method => "CGI", @_);
+}
+
+# }}}
+
+# {{{ sub NewCGIHandler
+
+=head2 NewCGIHandler
+
+ Returns a new Mason::CGIHandler object
+
+=cut
+
+sub NewCGIHandler {
+ require HTML::Mason::CGIHandler;
+ return NewHandler('HTML::Mason::CGIHandler', @_);
+}
+
+sub NewHandler {
+ my $class = shift;
+ my $handler = $class->new(
+ DefaultHandlerArgs(),
+ @_
+ );
+
+ $handler->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
+ $handler->interp->set_escape( u => \&RT::Interface::Web::EscapeURI );
+ return($handler);
+}
+
+=head2 CleanupRequest
+
+Rollback any uncommitted transaction.
+Flush the ACL cache
+Flush the searchbuilder query cache
+
+=cut
+
+sub CleanupRequest {
+
+ if ( $RT::Handle->TransactionDepth ) {
+ $RT::Handle->ForceRollback;
+ $RT::Logger->crit(
+ "Transaction not committed. Usually indicates a software fault."
+ . "Data loss may have occurred" );
+ }
+
+ # Clean out the ACL cache. the performance impact should be marginal.
+ # Consistency is imprived, too.
+ RT::Principal->InvalidateACLCache();
+ DBIx::SearchBuilder::Record::Cachable->FlushCache
+ if ( $RT::WebFlushDbCacheEveryRequest
+ and UNIVERSAL::can(
+ 'DBIx::SearchBuilder::Record::Cachable' => 'FlushCache' ) );
+
+}
+# }}}
+
+1;
diff --git a/rt/lib/RT/Interface/Web/Menu.pm b/rt/lib/RT/Interface/Web/Menu.pm
new file mode 100644
index 0000000..f2d78ef
--- /dev/null
+++ b/rt/lib/RT/Interface/Web/Menu.pm
@@ -0,0 +1,68 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Web::Menu;
+
+
+sub new {
+ my $class = shift;
+ my $self = bless {}, $class;
+ $self->{'root_node'} = RT::Interface::Web::Menu::Item->new();
+ return $self;
+}
+
+
+sub as_hash_of_hashes {
+
+}
+
+sub root {
+ my $self = shift;
+ return $self->{'root_node'};
+}
+
+1;
diff --git a/rt/lib/RT/Interface/Web/Menu/Item.pm b/rt/lib/RT/Interface/Web/Menu/Item.pm
new file mode 100644
index 0000000..5365db3
--- /dev/null
+++ b/rt/lib/RT/Interface/Web/Menu/Item.pm
@@ -0,0 +1,86 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Web::Menu::Item;
+
+
+sub new {
+ my $class = shift;
+ my $self = bless {},$class;
+ $self->{'_attributes'} = {};
+ return($self);
+}
+
+sub label { my $self = shift; $self->_accessor( label => @_) } ;
+sub absolute_url { my $self = shift; $self->_accessor( absolute_url => @_) } ;
+sub rt_path { my $self = shift; $self->_accessor( rt_path => @_) } ;
+sub hilight { my $self = shift; $self->_accessor( hilight => @_);
+ $self->parent->hilight(1);
+ } ;
+sub sort_order { my $self = shift; $self->_accessor( sort_order => @_) } ;
+
+sub add_child {
+}
+
+sub delete {
+}
+
+sub children {
+
+}
+
+sub _accessor {
+ my $self = shift;
+ my $key = shift;
+ if (@_){
+ $self->{'attributes'}->{$key} = shift;
+
+ }
+ return $self->{'_attributes'}->{$key};
+}
+
+1;
diff --git a/rt/lib/RT/Interface/Web/QueryBuilder.pm b/rt/lib/RT/Interface/Web/QueryBuilder.pm
new file mode 100755
index 0000000..56c5b03
--- /dev/null
+++ b/rt/lib/RT/Interface/Web/QueryBuilder.pm
@@ -0,0 +1,58 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Web::QueryBuilder;
+
+use strict;
+use warnings;
+
+eval "require RT::Interface::Web::QueryBuilder_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder_Vendor.pm});
+eval "require RT::Interface::Web::QueryBuilder_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
new file mode 100755
index 0000000..4676273
--- /dev/null
+++ b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
@@ -0,0 +1,247 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Web::QueryBuilder::Tree;
+
+use strict;
+use warnings;
+
+use base qw/Tree::Simple/;
+
+=head1 NAME
+
+ RT::Interface::Web::QueryBuilder::Tree - subclass of Tree::Simple used in Query Builder
+
+=head1 DESCRIPTION
+
+This class provides support functionality for the Query Builder (Search/Build.html).
+It is a subclass of L<Tree::Simple>.
+
+=head1 METHODS
+
+=head2 TraversePrePost PREFUNC POSTFUNC
+
+Traverses the tree depth-first. Before processing the node's children,
+calls PREFUNC with the node as its argument; after processing all of the
+children, calls POSTFUNC with the node as its argument.
+
+(Note that unlike Tree::Simple's C<traverse>, it actually calls its functions
+on the root node passed to it.)
+
+=cut
+
+sub TraversePrePost {
+ my ($self, $prefunc, $postfunc) = @_;
+
+ $prefunc->($self);
+
+ foreach my $child ($self->getAllChildren()) {
+ $child->TraversePrePost($prefunc, $postfunc);
+ }
+
+ $postfunc->($self);
+}
+
+=head2 GetReferencedQueues
+
+Returns a hash reference with keys each queue name referenced in a clause in
+the key (even if it's "Queue != 'Foo'"), and values all 1.
+
+=cut
+
+sub GetReferencedQueues {
+ my $self = shift;
+
+ my $queues = {};
+
+ $self->traverse(
+ sub {
+ my $node = shift;
+
+ return if $node->isRoot;
+
+ my $clause = $node->getNodeValue();
+
+ if ( ref($clause) and $clause->{Key} eq 'Queue' ) {
+ $queues->{ $clause->{Value} } = 1;
+ };
+ }
+ );
+
+ return $queues;
+}
+
+=head2 GetQueryAndOptionList SELECTED_NODES
+
+Given an array reference of tree nodes that have been selected by the user,
+traverses the tree and returns the equivalent SQL query and a list of hashes
+representing the "clauses" select option list. Each has contains the keys
+TEXT, INDEX, SELECTED, and DEPTH. TEXT is the displayed text of the option
+(including parentheses, not including indentation); INDEX is the 0-based
+index of the option in the list (also used as its CGI parameter); SELECTED
+is either 'SELECTED' or '', depending on whether the node corresponding
+to the select option was in the SELECTED_NODES list; and DEPTH is the
+level of indentation for the option.
+
+=cut
+
+sub GetQueryAndOptionList {
+ my $self = shift;
+ my $selected_nodes = shift;
+
+ my $optionlist = [];
+
+ my $i = 0;
+
+ $self->TraversePrePost(
+ sub { # This is called before recursing to the node's children.
+ my $node = shift;
+
+ return if $node->isRoot or $node->getParent->isRoot;
+
+ my $clause = $node->getNodeValue();
+ my $str = ' ';
+ my $aggregator_context = $node->getParent()->getNodeValue();
+ $str = $aggregator_context . " " if $node->getIndex() > 0;
+
+ if ( ref($clause) ) { # ie, it's a leaf
+ $str .=
+ $clause->{Key} . " " . $clause->{Op} . " " . $clause->{Value};
+ }
+
+ unless ($node->getParent->getParent->isRoot) {
+ # used to check !ref( $parent->getNodeValue() ) )
+ if ( $node->getIndex() == 0 ) {
+ $str = '( ' . $str;
+ }
+ }
+
+ push @$optionlist, {
+ TEXT => $str,
+ INDEX => $i,
+ SELECTED => (grep { $_ == $node } @$selected_nodes) ? 'SELECTED' : '',
+ DEPTH => $node->getDepth() - 1,
+ };
+
+ $i++;
+ }, sub {
+ # This is called after recursing to the node's children.
+ my $node = shift;
+
+ return if $node->isRoot or $node->getParent->isRoot or $node->getParent->getParent->isRoot;
+
+ # Only do this for the rightmost child.
+ return unless $node->getIndex == $node->getParent->getChildCount - 1;
+
+ $optionlist->[-1]{TEXT} .= ' )';
+ }
+ );
+
+ return (join ' ', map { $_->{TEXT} } @$optionlist), $optionlist;
+}
+
+=head2 PruneChildLessAggregators
+
+If tree manipulation has left it in a state where there are ANDs, ORs,
+or parenthesizations with no children, get rid of them.
+
+=cut
+
+sub PruneChildlessAggregators {
+ my $self = shift;
+
+ $self->TraversePrePost(
+ sub {
+ },
+ sub {
+ my $node = shift;
+
+ return if $node->isRoot or $node->getParent->isRoot;
+
+ # We're only looking for aggregators (AND/OR)
+ return if ref $node->getNodeValue;
+
+ return if $node->getChildCount != 0;
+
+ # OK, this is a childless aggregator. Remove self.
+
+ $node->getParent->removeChild($node);
+
+ # Deal with circular refs
+ $node->DESTROY;
+ }
+ );
+}
+
+=head2 GetDisplayedNodes
+
+This function returns a list of the nodes of the tree in depth-first
+order which correspond to options in the "clauses" multi-select box.
+In fact, it's all of them but the root and its child.
+
+=cut
+
+sub GetDisplayedNodes {
+ my $self = shift;
+ my @lines;
+
+ $self->traverse(sub {
+ my $node = shift;
+
+ push @lines, $node unless $node->isRoot or $node->getParent->isRoot;
+ });
+
+ return @lines;
+}
+
+
+eval "require RT::Interface::Web::QueryBuilder::Tree_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder/Tree_Vendor.pm});
+eval "require RT::Interface::Web::QueryBuilder::Tree_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder/Tree_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Interface/Web/Standalone.pm b/rt/lib/RT/Interface/Web/Standalone.pm
new file mode 100755
index 0000000..319e317
--- /dev/null
+++ b/rt/lib/RT/Interface/Web/Standalone.pm
@@ -0,0 +1,84 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Interface::Web::Standalone;
+
+use strict;
+use base 'HTTP::Server::Simple::Mason';
+use RT::Interface::Web::Handler;
+use RT::Interface::Web;
+
+sub handler_class { "RT::Interface::Web::Handler" }
+
+sub setup_escapes {
+ my $self = shift;
+ my $handler = shift;
+
+ # Override HTTP::Server::Simple::Mason's version of this method to do
+ # nothing. (RT::Interface::Web::Handler does this already for us in
+ # NewHandler.)
+}
+
+sub default_mason_config {
+ return @RT::MasonParameters;
+}
+
+sub handle_request {
+
+ my $self = shift;
+ my $cgi = shift;
+
+ Module::Refresh->refresh if $RT::DevelMode;
+
+ $self->SUPER::handle_request($cgi);
+ $RT::Logger->crit($@) if ($@);
+
+ RT::Interface::Web::Handler->CleanupRequest();
+
+}
+
+1;
diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm
new file mode 100644
index 0000000..1999096
--- /dev/null
+++ b/rt/lib/RT/Interface/Web_Vendor.pm
@@ -0,0 +1,201 @@
+# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+# Copyright (c) 2008 Freeside Internet Services, Inc.
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+
+=head1 NAME
+
+RT::Interface::Web_Vendor
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Freeside vendor overlay for RT::Interface::Web.
+
+=begin testing
+
+use_ok(RT::Interface::Web_Vendor);
+
+=end testing
+
+=cut
+
+#package RT::Interface::Web;
+#use strict;
+
+package HTML::Mason::Commands;
+use strict;
+
+=head2 ProcessTicketCustomers
+
+=cut
+
+sub ProcessTicketCustomers {
+ my %args = (
+ TicketObj => undef,
+ ARGSRef => undef,
+ Debug => 0,
+ @_
+ );
+ my @results = ();
+
+ my $Ticket = $args{'TicketObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+ my $Debug = $args{'Debug'};
+ my $me = 'ProcessTicketCustomers';
+
+ ### false laziness w/RT::Interface::Web::ProcessTicketLinks
+ # Delete links that are gone gone gone.
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+ my $base = $1;
+ my $type = $2;
+ my $target = $3;
+
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
+
+ push @results, $msg;
+
+ }
+
+ }
+ ###
+
+ ###
+ #find new customers
+ ###
+
+ my @custnums = map { /^Ticket-AddCustomer-(\d+)$/; $1 }
+ grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ keys %$ARGSRef;
+
+ #my @delete_custnums =
+ # map { /^Ticket-AddCustomer-(\d+)$/; $1 }
+ # grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ # keys %$ARGSRef;
+
+ ###
+ #figure out if we're going to auto-link requestors, and find them if so
+ ###
+
+ my $num_cur_cust = $Ticket->Customers->Count;
+ my $num_new_cust = scalar(@custnums);
+ warn "$me: $num_cur_cust current customers / $num_new_cust new customers\n"
+ if $Debug;
+
+ #if we're linking the first ticket to one customer
+ my $link_requestors = ( $num_cur_cust == 0 && $num_new_cust == 1 );
+ warn "$me: adding a single customer to a previously customerless".
+ " ticket, so linking customers to requestor too\n"
+ if $Debug && $link_requestors;
+
+ my @Requestors = ();
+ if ( $link_requestors ) {
+
+ #find any requestors without customers
+ @Requestors =
+ grep { ! $_->Customers->Count }
+ @{ $Ticket->Requestors->UserMembersObj->ItemsArrayRef };
+
+ warn "$me: found ". scalar(@Requestors). " requestors without".
+ " customers; linking them\n"
+ if $Debug;
+
+ }
+
+ ###
+ #link ticket (and requestors) to customers
+ ###
+
+ foreach my $custnum ( @custnums ) {
+
+ my @link = ( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+
+ my( $val, $msg ) = $Ticket->AddLink(@link);
+ push @results, $msg;
+
+ #add customer links to requestors
+ foreach my $Requestor ( @Requestors ) {
+ my( $val, $msg ) = $Requestor->AddLink(@link);
+ push @results, $msg;
+ warn "$me: linking requestor to custnum $custnum: $msg\n"
+ if $Debug > 1;
+ }
+
+ }
+
+ return @results;
+
+}
+
+#false laziness w/above... eventually it should go away in favor of this
+sub ProcessObjectCustomers {
+ my %args = (
+ Object => undef,
+ ARGSRef => undef,
+ @_
+ );
+ my @results = ();
+
+ my $Object = $args{'Object'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ ### false laziness w/RT::Interface::Web::ProcessTicketLinks
+ # Delete links that are gone gone gone.
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+ my $base = $1;
+ my $type = $2;
+ my $target = $3;
+
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Object->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
+
+ push @results, $msg;
+
+ }
+
+ }
+ ###
+
+ #my @delete_custnums =
+ # map { /^Object-AddCustomer-(\d+)$/; $1 }
+ # grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ # keys %$ARGSRef;
+
+ my @custnums = map { /^Object-AddCustomer-(\d+)$/; $1 }
+ grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ keys %$ARGSRef;
+
+ foreach my $custnum ( @custnums ) {
+ my( $val, $msg ) =
+ $Object->AddLink( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+ push @results, $msg;
+ }
+
+ return @results;
+
+}
+
+1;
+
diff --git a/rt/lib/RT/Link.pm b/rt/lib/RT/Link.pm
new file mode 100644
index 0000000..72ad1d5
--- /dev/null
+++ b/rt/lib/RT/Link.pm
@@ -0,0 +1,326 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Link
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Link;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Links');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(240) 'Base'.
+ varchar(240) 'Target'.
+ varchar(20) 'Type'.
+ int(11) 'LocalTarget'.
+ int(11) 'LocalBase'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Base => '',
+ Target => '',
+ Type => '',
+ LocalTarget => '0',
+ LocalBase => '0',
+
+ @_);
+ $self->SUPER::Create(
+ Base => $args{'Base'},
+ Target => $args{'Target'},
+ Type => $args{'Type'},
+ LocalTarget => $args{'LocalTarget'},
+ LocalBase => $args{'LocalBase'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Base
+
+Returns the current value of Base.
+(In the database, Base is stored as varchar(240).)
+
+
+
+=head2 SetBase VALUE
+
+
+Set Base to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Base will be stored as a varchar(240).)
+
+
+=cut
+
+
+=head2 Target
+
+Returns the current value of Target.
+(In the database, Target is stored as varchar(240).)
+
+
+
+=head2 SetTarget VALUE
+
+
+Set Target to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Target will be stored as a varchar(240).)
+
+
+=cut
+
+
+=head2 Type
+
+Returns the current value of Type.
+(In the database, Type is stored as varchar(20).)
+
+
+
+=head2 SetType VALUE
+
+
+Set Type to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Type will be stored as a varchar(20).)
+
+
+=cut
+
+
+=head2 LocalTarget
+
+Returns the current value of LocalTarget.
+(In the database, LocalTarget is stored as int(11).)
+
+
+
+=head2 SetLocalTarget VALUE
+
+
+Set LocalTarget to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, LocalTarget will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 LocalBase
+
+Returns the current value of LocalBase.
+(In the database, LocalBase is stored as int(11).)
+
+
+
+=head2 SetLocalBase VALUE
+
+
+Set LocalBase to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, LocalBase will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Base =>
+ {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+ Target =>
+ {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+ Type =>
+ {read => 1, write => 1, sql_type => 12, length => 20, is_blob => 0, is_numeric => 0, type => 'varchar(20)', default => ''},
+ LocalTarget =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LocalBase =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::Link_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Link_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Link_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Link_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Link_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Link_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Link_Overlay, RT::Link_Vendor, RT::Link_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Link_Overlay.pm b/rt/lib/RT/Link_Overlay.pm
new file mode 100644
index 0000000..e8d6c7c
--- /dev/null
+++ b/rt/lib/RT/Link_Overlay.pm
@@ -0,0 +1,390 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Link - an RT Link object
+
+=head1 SYNOPSIS
+
+ use RT::Link;
+
+=head1 DESCRIPTION
+
+This module should never be called directly by client code. it's an internal module which
+should only be accessed through exported APIs in Ticket other similar objects.
+
+=head1 METHODS
+
+
+=begin testing
+
+
+use RT::Link;
+my $link = RT::Link->new($RT::SystemUser);
+
+
+ok (ref $link);
+ok (UNIVERSAL::isa($link, 'RT::Link'));
+ok (UNIVERSAL::isa($link, 'RT::Base'));
+ok (UNIVERSAL::isa($link, 'RT::Record'));
+ok (UNIVERSAL::isa($link, 'DBIx::SearchBuilder::Record'));
+
+=end testing
+
+=cut
+
+
+package RT::Link;
+
+use strict;
+no warnings qw(redefine);
+
+
+use Carp;
+use RT::URI;
+
+
+# {{{ sub Create
+
+=head2 Create PARAMHASH
+
+Create a new link object. Takes 'Base', 'Target' and 'Type'.
+Returns undef on failure or a Link Id on success.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = ( Base => undef,
+ Target => undef,
+ Type => undef,
+ @_ );
+
+ my $base = RT::URI->new( $self->CurrentUser );
+ $base->FromURI( $args{'Base'} );
+
+ unless ( $base->Resolver && $base->Scheme ) {
+ my $msg = $self->loc("Couldn't resolve base '[_1]' into a URI.",
+ $args{'Base'});
+ $RT::Logger->warning( "$self $msg\n" );
+
+ if (wantarray) {
+ return(undef, $msg);
+ } else {
+ return (undef);
+ }
+ }
+
+ my $target = RT::URI->new( $self->CurrentUser );
+ $target->FromURI( $args{'Target'} );
+
+ unless ( $target->Resolver ) {
+ my $msg = $self->loc("Couldn't resolve target '[_1]' into a URI.",
+ $args{'Target'});
+ $RT::Logger->warning( "$self $msg\n" );
+
+ if (wantarray) {
+ return(undef, $msg);
+ } else {
+ return (undef);
+ }
+ }
+
+ my $base_id = 0;
+ my $target_id = 0;
+
+
+
+
+ if ( $base->IsLocal ) {
+ unless (UNIVERSAL::can($base->Object, 'Id')) {
+ return (undef, $self->loc("[_1] appears to be a local object, but can't be found in the database", $args{'Base'}));
+
+ }
+ $base_id = $base->Object->Id;
+ }
+ if ( $target->IsLocal ) {
+ unless (UNIVERSAL::can($target->Object, 'Id')) {
+ return (undef, $self->loc("[_1] appears to be a local object, but can't be found in the database", $args{'Target'}));
+
+ }
+ $target_id = $target->Object->Id;
+ }
+
+ # {{{ We don't want references to ourself
+ if ( $base->URI eq $target->URI ) {
+ return ( 0, $self->loc("Can't link a ticket to itself") );
+ }
+
+ # }}}
+
+ my ( $id, $msg ) = $self->SUPER::Create( Base => $base->URI,
+ Target => $target->URI,
+ LocalBase => $base_id,
+ LocalTarget => $target_id,
+ Type => $args{'Type'} );
+ return ( $id, $msg );
+}
+
+# }}}
+ # {{{ sub LoadByParams
+
+=head2 LoadByParams
+
+ Load an RT::Link object from the database. Takes three parameters
+
+ Base => undef,
+ Target => undef,
+ Type =>undef
+
+ Base and Target are expected to be integers which refer to Tickets or URIs
+ Type is the link type
+
+=cut
+
+sub LoadByParams {
+ my $self = shift;
+ my %args = ( Base => undef,
+ Target => undef,
+ Type => undef,
+ @_ );
+
+ my $base = RT::URI->new($self->CurrentUser);
+ $base->FromURI( $args{'Base'} );
+
+ my $target = RT::URI->new($self->CurrentUser);
+ $target->FromURI( $args{'Target'} );
+
+ unless ($base->Resolver && $target->Resolver) {
+ return ( 0, $self->loc("Couldn't load link") );
+ }
+
+
+ my ( $id, $msg ) = $self->LoadByCols( Base => $base->URI,
+ Type => $args{'Type'},
+ Target => $target->URI );
+
+ unless ($id) {
+ return ( 0, $self->loc("Couldn't load link") );
+ }
+}
+
+# }}}
+# {{{ sub Load
+
+=head2 Load
+
+ Load an RT::Link object from the database. Takes one parameter, the id of an entry in the links table.
+
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift;
+
+
+
+
+ if ( $identifier !~ /^\d+$/ ) {
+ return ( 0, $self->loc("That's not a numerical id") );
+ }
+ else {
+ my ( $id, $msg ) = $self->LoadById($identifier);
+ unless ( $self->Id ) {
+ return ( 0, $self->loc("Couldn't load link") );
+ }
+ return ( $id, $msg );
+ }
+}
+
+# }}}
+
+
+# {{{ TargetURI
+
+=head2 TargetURI
+
+returns an RT::URI object for the "Target" of this link.
+
+=cut
+
+sub TargetURI {
+ my $self = shift;
+ my $URI = RT::URI->new($self->CurrentUser);
+ $URI->FromURI($self->Target);
+ return ($URI);
+}
+
+# }}}
+# {{{ sub TargetObj
+
+=head2 TargetObj
+
+=cut
+
+sub TargetObj {
+ my $self = shift;
+ return $self->TargetURI->Object;
+}
+# }}}
+
+# {{{ BaseURI
+
+=head2 BaseURI
+
+returns an RT::URI object for the "Base" of this link.
+
+=cut
+
+sub BaseURI {
+ my $self = shift;
+ my $URI = RT::URI->new($self->CurrentUser);
+ $URI->FromURI($self->Base);
+ return ($URI);
+}
+
+# }}}
+# {{{ sub BaseObj
+
+=head2 BaseObj
+
+=cut
+
+sub BaseObj {
+ my $self = shift;
+ return $self->BaseURI->Object;
+}
+# }}}
+
+
+
+# Static methods:
+
+# {{{ sub BaseIsLocal
+
+=head2 BaseIsLocal
+
+Returns true if the base of this link is a local ticket
+
+=cut
+
+sub BaseIsLocal {
+ my $self = shift;
+ $RT::Logger->crit("Link::BaseIsLocal is deprecated in favor of Link->BaseURI->IsLocal at (". join(":",caller).")");
+ return $self->BaseURI->IsLocal;
+}
+
+# }}}
+
+# {{{ sub TargetIsLocal
+
+=head2 TargetIsLocal
+
+Returns true if the target of this link is a local ticket
+
+=cut
+
+sub TargetIsLocal {
+ my $self = shift;
+ $RT::Logger->crit("Link::BaseIsLocal is deprecated in favor of Link->BaseURI->IsLocal at (". join(":",caller).")");
+ return $self->TargetURI->IsLocal;
+}
+
+# }}}
+
+
+# {{{ sub BaseAsHREF
+
+=head2 BaseAsHREF
+
+Returns an HTTP url to access the base of this link
+
+=cut
+
+sub BaseAsHREF {
+ my $self = shift;
+ $RT::Logger->crit("Link::BaseAsHREF deprecated in favor of ->BaseURI->AsHREF at (". join(":",caller).")");
+ return $self->BaseURI->AsHREF;
+}
+# }}}
+
+# {{{ sub TargetAsHREF
+
+=head2 TargetAsHREF
+
+return an HTTP url to access the target of this link
+
+=cut
+
+sub TargetAsHREF {
+ my $self = shift;
+ $RT::Logger->crit("Link::TargetAsHREF deprecated in favor of ->TargetURI->AsHREF at (". join(":",caller).")");
+ return $self->TargetURI->AsHREF;
+}
+# }}}
+
+# {{{ sub AsHREF - Converts Link URIs to HTTP URLs
+
+=head2 URI
+
+Takes a URI and returns an http: url to access that object.
+
+=cut
+
+
+sub AsHREF {
+ my $self=shift;
+
+ $RT::Logger->crit("AsHREF is gone. look at URI::HREF to figure out what to do with \$URI");
+}
+
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Links.pm b/rt/lib/RT/Links.pm
new file mode 100644
index 0000000..5c21e2d
--- /dev/null
+++ b/rt/lib/RT/Links.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Links -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Links
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Links;
+
+use RT::SearchBuilder;
+use RT::Link;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Links';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Link item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Link->new($self->CurrentUser));
+}
+
+ eval "require RT::Links_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Links_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Links_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Links_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Links_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Links_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Links_Overlay, RT::Links_Vendor, RT::Links_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Links_Overlay.pm b/rt/lib/RT/Links_Overlay.pm
new file mode 100644
index 0000000..bf95e63
--- /dev/null
+++ b/rt/lib/RT/Links_Overlay.pm
@@ -0,0 +1,174 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Links - A collection of Link objects
+
+=head1 SYNOPSIS
+
+ use RT::Links;
+ my $links = new RT::Links($CurrentUser);
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Links);
+
+=end testing
+
+=cut
+
+
+package RT::Links;
+
+use strict;
+no warnings qw(redefine);
+use RT::URI;
+
+# {{{ sub Limit
+sub Limit {
+ my $self = shift;
+ my %args = ( ENTRYAGGREGATOR => 'AND',
+ OPERATOR => '=',
+ @_);
+
+ #if someone's trying to search for tickets, try to resolve the uris for searching.
+
+ if ( ( $args{'OPERATOR'} eq '=') and
+ ( $args{'FIELD'} eq 'Base') or ($args{'FIELD'} eq 'Target')
+ ) {
+ my $dummy = RT::URI->new($self->CurrentUser);
+ $dummy->FromURI($args{'VALUE'});
+ # $uri = $dummy->URI;
+ }
+
+
+ # If we're limiting by target, order by base
+ # (Order by the thing that's changing)
+
+ if ( ($args{'FIELD'} eq 'Target') or
+ ($args{'FIELD'} eq 'LocalTarget') ) {
+ $self->OrderBy (ALIAS => 'main',
+ FIELD => 'Base',
+ ORDER => 'ASC');
+ }
+ elsif ( ($args{'FIELD'} eq 'Base') or
+ ($args{'FIELD'} eq 'LocalBase') ) {
+ $self->OrderBy (ALIAS => 'main',
+ FIELD => 'Target',
+ ORDER => 'ASC');
+ }
+
+
+ $self->SUPER::Limit(%args);
+}
+# }}}
+
+# {{{ LimitRefersTo
+
+=head2 LimitRefersTo URI
+
+find all things that refer to URI
+
+=cut
+
+sub LimitRefersTo {
+ my $self = shift;
+ my $URI = shift;
+
+ $self->Limit(FIELD => 'Type', VALUE => 'RefersTo');
+ $self->Limit(FIELD => 'Target', VALUE => $URI);
+}
+
+# }}}
+# {{{ LimitReferredToBy
+
+=head2 LimitReferredToBy URI
+
+find all things that URI refers to
+
+=cut
+
+sub LimitReferredToBy {
+ my $self = shift;
+ my $URI = shift;
+
+ $self->Limit(FIELD => 'Type', VALUE => 'RefersTo');
+ $self->Limit(FIELD => 'Base', VALUE => $URI);
+}
+
+# }}}
+
+
+# {{{ Next
+sub Next {
+ my $self = shift;
+
+ my $Link = $self->SUPER::Next();
+ return $Link unless $Link && ref $Link;
+
+ # Skip links to local objects thast are deleted
+ if ( $Link->TargetURI->IsLocal and UNIVERSAL::isa($Link->TargetObj,"RT::Ticket")
+ and $Link->TargetObj->__Value('status') eq "deleted") {
+ return $self->Next;
+ } elsif ($Link->BaseURI->IsLocal and UNIVERSAL::isa($Link->BaseObj,"RT::Ticket")
+ and $Link->BaseObj->__Value('status') eq "deleted") {
+ return $self->Next;
+ } else {
+ return $Link;
+ }
+}
+
+# }}}
+1;
+
diff --git a/rt/lib/RT/ObjectCustomField.pm b/rt/lib/RT/ObjectCustomField.pm
new file mode 100644
index 0000000..077c8fc
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomField.pm
@@ -0,0 +1,295 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::ObjectCustomField
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::ObjectCustomField;
+use RT::Record;
+use RT::CustomField;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('ObjectCustomFields');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'CustomField'.
+ int(11) 'ObjectId'.
+ int(11) 'SortOrder'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ CustomField => '0',
+ ObjectId => '0',
+ SortOrder => '0',
+
+ @_);
+ $self->SUPER::Create(
+ CustomField => $args{'CustomField'},
+ ObjectId => $args{'ObjectId'},
+ SortOrder => $args{'SortOrder'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 CustomField
+
+Returns the current value of CustomField.
+(In the database, CustomField is stored as int(11).)
+
+
+
+=head2 SetCustomField VALUE
+
+
+Set CustomField to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CustomField will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 CustomFieldObj
+
+Returns the CustomField Object which has the id returned by CustomField
+
+
+=cut
+
+sub CustomFieldObj {
+ my $self = shift;
+ my $CustomField = RT::CustomField->new($self->CurrentUser);
+ $CustomField->Load($self->__Value('CustomField'));
+ return($CustomField);
+}
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=head2 SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 SortOrder
+
+Returns the current value of SortOrder.
+(In the database, SortOrder is stored as int(11).)
+
+
+
+=head2 SetSortOrder VALUE
+
+
+Set SortOrder to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, SortOrder will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ CustomField =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ ObjectId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ SortOrder =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::ObjectCustomField_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomField_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomField_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomField_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomField_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomField_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ObjectCustomField_Overlay, RT::ObjectCustomField_Vendor, RT::ObjectCustomField_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ObjectCustomFieldValue.pm b/rt/lib/RT/ObjectCustomFieldValue.pm
new file mode 100644
index 0000000..91c2013
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomFieldValue.pm
@@ -0,0 +1,433 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::ObjectCustomFieldValue
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::ObjectCustomFieldValue;
+use RT::Record;
+use RT::CustomField;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('ObjectCustomFieldValues');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'CustomField'.
+ varchar(255) 'ObjectType'.
+ int(11) 'ObjectId'.
+ int(11) 'SortOrder'.
+ varchar(255) 'Content'.
+ longtext 'LargeContent'.
+ varchar(80) 'ContentType'.
+ varchar(80) 'ContentEncoding'.
+ smallint(6) 'Disabled'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ CustomField => '0',
+ ObjectType => '',
+ ObjectId => '0',
+ SortOrder => '0',
+ Content => '',
+ LargeContent => '',
+ ContentType => '',
+ ContentEncoding => '',
+ Disabled => '0',
+
+ @_);
+ $self->SUPER::Create(
+ CustomField => $args{'CustomField'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+ SortOrder => $args{'SortOrder'},
+ Content => $args{'Content'},
+ LargeContent => $args{'LargeContent'},
+ ContentType => $args{'ContentType'},
+ ContentEncoding => $args{'ContentEncoding'},
+ Disabled => $args{'Disabled'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 CustomField
+
+Returns the current value of CustomField.
+(In the database, CustomField is stored as int(11).)
+
+
+
+=head2 SetCustomField VALUE
+
+
+Set CustomField to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CustomField will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 CustomFieldObj
+
+Returns the CustomField Object which has the id returned by CustomField
+
+
+=cut
+
+sub CustomFieldObj {
+ my $self = shift;
+ my $CustomField = RT::CustomField->new($self->CurrentUser);
+ $CustomField->Load($self->__Value('CustomField'));
+ return($CustomField);
+}
+
+=head2 ObjectType
+
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(255).)
+
+
+
+=head2 SetObjectType VALUE
+
+
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=head2 SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 SortOrder
+
+Returns the current value of SortOrder.
+(In the database, SortOrder is stored as int(11).)
+
+
+
+=head2 SetSortOrder VALUE
+
+
+Set SortOrder to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, SortOrder will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Content
+
+Returns the current value of Content.
+(In the database, Content is stored as varchar(255).)
+
+
+
+=head2 SetContent VALUE
+
+
+Set Content to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Content will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 LargeContent
+
+Returns the current value of LargeContent.
+(In the database, LargeContent is stored as longtext.)
+
+
+
+=head2 SetLargeContent VALUE
+
+
+Set LargeContent to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, LargeContent will be stored as a longtext.)
+
+
+=cut
+
+
+=head2 ContentType
+
+Returns the current value of ContentType.
+(In the database, ContentType is stored as varchar(80).)
+
+
+
+=head2 SetContentType VALUE
+
+
+Set ContentType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ContentType will be stored as a varchar(80).)
+
+
+=cut
+
+
+=head2 ContentEncoding
+
+Returns the current value of ContentEncoding.
+(In the database, ContentEncoding is stored as varchar(80).)
+
+
+
+=head2 SetContentEncoding VALUE
+
+
+Set ContentEncoding to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ContentEncoding will be stored as a varchar(80).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+=head2 Disabled
+
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
+
+
+
+=head2 SetDisabled VALUE
+
+
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ CustomField =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ ObjectType =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ ObjectId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ SortOrder =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Content =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ LargeContent =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
+ ContentType =>
+ {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ ContentEncoding =>
+ {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Disabled =>
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::ObjectCustomFieldValue_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFieldValue_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomFieldValue_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFieldValue_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomFieldValue_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFieldValue_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ObjectCustomFieldValue_Overlay, RT::ObjectCustomFieldValue_Vendor, RT::ObjectCustomFieldValue_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ObjectCustomFieldValue_Overlay.pm b/rt/lib/RT/ObjectCustomFieldValue_Overlay.pm
new file mode 100644
index 0000000..ed86b11
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomFieldValue_Overlay.pm
@@ -0,0 +1,262 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::ObjectCustomFieldValue;
+
+use strict;
+no warnings qw(redefine);
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ CustomField => '0',
+ ObjectType => '',
+ ObjectId => '0',
+ Disabled => '0',
+ Content => '',
+ LargeContent => '',
+ ContentType => '',
+ ContentEncoding => '',
+
+ @_);
+
+
+ if( $args{'Content'} && length($args{'Content'}) > 255 && !$args{'LargeContent'} ) {
+
+ $args{'LargeContent'} = $args{'Content'};
+ $args{'Content'} = '';
+ $args{'ContentType'} = 'text/plain';
+ }
+
+ ( $args{'ContentEncoding'}, $args{'LargeContent'} ) =
+ $self->_EncodeLOB( $args{'LargeContent'}, $args{'ContentType'} )
+ if ( $args{'LargeContent'} );
+
+ $self->SUPER::Create(
+ CustomField => $args{'CustomField'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+ Disabled => $args{'Disabled'},
+ Content => $args{'Content'},
+ LargeContent => $args{'LargeContent'},
+ ContentType => $args{'ContentType'},
+ ContentEncoding => $args{'ContentEncoding'},
+);
+
+
+
+}
+
+
+sub LargeContent {
+ my $self = shift;
+ $self->_DecodeLOB( $self->ContentType, $self->ContentEncoding,
+ $self->_Value( 'LargeContent', decode_utf8 => 0 ) );
+
+}
+
+
+
+
+=head2 LoadByTicketContentAndCustomField { Ticket => TICKET, CustomField => CUSTOMFIELD, Content => CONTENT }
+
+Loads a custom field value by Ticket, Content and which CustomField it's tied to
+
+=cut
+
+
+sub LoadByTicketContentAndCustomField {
+ my $self = shift;
+ my %args = ( Ticket => undef,
+ CustomField => undef,
+ Content => undef,
+ @_
+ );
+
+
+ $self->LoadByCols( Content => $args{'Content'},
+ CustomField => $args{'CustomField'},
+ ObjectType => 'RT::Ticket',
+ ObjectId => $args{'Ticket'},
+ Disabled => 0
+ );
+
+
+}
+
+sub LoadByObjectContentAndCustomField {
+ my $self = shift;
+ my %args = ( Object => undef,
+ CustomField => undef,
+ Content => undef,
+ @_
+ );
+
+ my $obj = $args{'Object'} or return;
+
+ $self->LoadByCols( Content => $args{'Content'},
+ CustomField => $args{'CustomField'},
+ ObjectType => ref($obj),
+ ObjectId => $obj->Id,
+ Disabled => 0
+ );
+
+}
+
+
+=head2 Content
+
+Return this custom field's content. If there's no "regular"
+content, try "LargeContent"
+
+=cut
+
+
+sub Content {
+ my $self = shift;
+ my $content = $self->SUPER::Content;
+ if (!$content && $self->ContentType eq 'text/plain') {
+ return $self->LargeContent();
+ } else {
+ return $content;
+ }
+}
+
+
+=head2 Object
+
+Returns the object this value applies to
+
+=cut
+
+sub Object {
+ my $self = shift;
+ my $Object = $self->__Value('ObjectType')->new($self->CurrentUser);
+ $Object->Load($self->__Value('ObjectId'));
+ return($Object);
+}
+
+
+=head2 Delete
+
+Disable this value. Used to remove "current" values from records while leaving them in the history.
+
+=cut
+
+
+sub Delete {
+ my $self = shift;
+ $self->SetDisabled(1);
+}
+
+=head2 _FillInTemplateURL URL
+
+Takes a URL containing placeholders and returns the URL as filled in for this
+ObjectCustomFieldValue.
+
+Available placeholders:
+
+=over
+
+=item __id__
+
+The id of the object in question.
+
+=item __CustomField__
+
+The value of this custom field for the object in question.
+
+=back
+
+=cut
+
+sub _FillInTemplateURL {
+
+ my $self = shift;
+
+ my $url = shift;
+
+ $url =~ s/__id__/@{[$self->ObjectId]}/g;
+ $url =~ s/__CustomField__/@{[$self->Content]}/g;
+
+ return $url;
+}
+
+
+=head2 ValueLinkURL
+
+Returns a filled in URL template for this ObjectCustomFieldValue, suitable for
+constructing a hyperlink in RT's webui. Returns undef if this custom field doesn't have
+a LinkValueTo
+
+=cut
+
+sub LinkValueTo {
+ my $self = shift;
+ return $self->_FillInTemplateURL($self->CustomFieldObj->LinkValueTo);
+}
+
+
+
+=head2 ValueIncludeURL
+
+Returns a filled in URL template for this ObjectCustomFieldValue, suitable for
+constructing a hyperlink in RT's webui. Returns undef if this custom field doesn't have
+a IncludeContentForValue
+
+=cut
+
+sub IncludeContentForValue {
+ my $self = shift;
+ return $self->_FillInTemplateURL($self->CustomFieldObj->IncludeContentForValue);
+}
+
+
+
+
+1;
diff --git a/rt/lib/RT/ObjectCustomFieldValues.pm b/rt/lib/RT/ObjectCustomFieldValues.pm
new file mode 100644
index 0000000..8d8dbf8
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomFieldValues.pm
@@ -0,0 +1,150 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::ObjectCustomFieldValues -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::ObjectCustomFieldValues
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::ObjectCustomFieldValues;
+
+use RT::SearchBuilder;
+use RT::ObjectCustomFieldValue;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'ObjectCustomFieldValues';
+ $self->{'primary_key'} = 'id';
+
+
+
+ # By default, order by SortOrder
+ $self->OrderByCols(
+ { ALIAS => 'main',
+ FIELD => 'SortOrder',
+ ORDER => 'ASC' },
+ { ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'ASC' },
+ );
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::ObjectCustomFieldValue item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::ObjectCustomFieldValue->new($self->CurrentUser));
+}
+
+ eval "require RT::ObjectCustomFieldValues_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFieldValues_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomFieldValues_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFieldValues_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomFieldValues_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFieldValues_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ObjectCustomFieldValues_Overlay, RT::ObjectCustomFieldValues_Vendor, RT::ObjectCustomFieldValues_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ObjectCustomFieldValues_Overlay.pm b/rt/lib/RT/ObjectCustomFieldValues_Overlay.pm
new file mode 100644
index 0000000..6917e89
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomFieldValues_Overlay.pm
@@ -0,0 +1,155 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::ObjectCustomFieldValues;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub LimitToCustomField
+
+=head2 LimitToCustomField FIELD
+
+Limits the returned set to values for the custom field with Id FIELD
+
+=cut
+
+sub LimitToCustomField {
+ my $self = shift;
+ my $cf = shift;
+ return ($self->Limit( FIELD => 'CustomField',
+ VALUE => $cf,
+ OPERATOR => '='));
+
+}
+
+# }}}
+
+# {{{ sub LimitToTicket
+
+=head2 LimitToTicket TICKETID
+
+Limits the returned set to values for the ticket with Id TICKETID
+
+=cut
+
+sub LimitToTicket {
+ my $self = shift;
+ my $ticket = shift;
+
+
+ $RT::Logger->warning(ref($self) . " -> LimitToTicket deprecated in favor of LimitToObject at (". join(":",caller).")");
+
+ $self->Limit( FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket',
+ OPERATOR => '=');
+ return ($self->Limit( FIELD => 'ObjectId',
+ VALUE => $ticket,
+ OPERATOR => '='));
+
+}
+
+# }}}
+
+
+sub LimitToObject {
+ my $self = shift;
+ my $object = shift;
+ $self->Limit( FIELD => 'ObjectType',
+ VALUE => ref($object),
+ OPERATOR => '=');
+ return ($self->Limit( FIELD => 'ObjectId',
+ VALUE => $object->Id,
+ OPERATOR => '='));
+
+}
+
+=sub HasEntry VALUE
+
+Returns true if this CustomFieldValues collection has an entry with content that eq VALUE
+
+=cut
+
+
+sub HasEntry {
+ my $self = shift;
+ my $value = shift;
+
+ #TODO: this could cache and optimize a fair bit.
+ foreach my $item (@{$self->ItemsArrayRef}) {
+ return(1) if ($item->Content eq $value);
+ }
+ return undef;
+
+}
+
+sub _DoSearch {
+ my $self = shift;
+
+ #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
+ unless($self->{'find_expired_rows'}) {
+ $self->LimitToEnabled();
+ }
+
+ return($self->SUPER::_DoSearch(@_));
+
+}
+
+sub _DoCount {
+ my $self = shift;
+
+ #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
+ unless($self->{'find_expired_rows'}) {
+ $self->LimitToEnabled();
+ }
+
+ return($self->SUPER::_DoCount(@_));
+
+}
+
+1;
+
diff --git a/rt/lib/RT/ObjectCustomField_Overlay.pm b/rt/lib/RT/ObjectCustomField_Overlay.pm
new file mode 100644
index 0000000..36cbceb
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomField_Overlay.pm
@@ -0,0 +1,103 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::ObjectCustomField;
+
+use strict;
+no warnings qw(redefine);
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ CustomField => '0',
+ ObjectId => '0',
+ SortOrder => undef,
+ @_);
+
+ if (!defined $args{SortOrder}) {
+ my $CF = $self->CustomFieldObj($args{'CustomField'});
+ my $ObjectCFs = RT::ObjectCustomFields->new($self->CurrentUser);
+ $ObjectCFs->LimitToObjectId($args{'ObjectId'});
+ $ObjectCFs->LimitToLookupType($CF->LookupType);
+
+ $args{SortOrder} = $ObjectCFs->Count + 1;
+ }
+
+ $self->SUPER::Create(
+ CustomField => $args{'CustomField'},
+ ObjectId => $args{'ObjectId'},
+ SortOrder => $args{'SortOrder'},
+ );
+}
+
+sub Delete {
+ my $self = shift;
+
+ my $ObjectCFs = RT::ObjectCustomFields->new($self->CurrentUser);
+ $ObjectCFs->LimitToObjectId($self->ObjectId);
+ $ObjectCFs->LimitToLookupType($self->CustomFieldObj->LookupType);
+
+ # Move everything below us up
+ my $sort_order = $self->SortOrder;
+ while (my $OCF = $ObjectCFs->Next) {
+ my $this_order = $OCF->SortOrder;
+ next if $this_order <= $sort_order;
+ $OCF->SetSortOrder($this_order - 1);
+ }
+
+ $self->SUPER::Delete;
+}
+
+sub CustomFieldObj {
+ my $self = shift;
+ my $id = shift || $self->CustomField;
+ my $CF = RT::CustomField->new($self->CurrentUser);
+ $CF->Load($id) or die "Cannot load CustomField $id";
+ return $CF;
+}
+
+1;
diff --git a/rt/lib/RT/ObjectCustomFields.pm b/rt/lib/RT/ObjectCustomFields.pm
new file mode 100644
index 0000000..fde8bfa
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomFields.pm
@@ -0,0 +1,150 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::ObjectCustomFields -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::ObjectCustomFields
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::ObjectCustomFields;
+
+use RT::SearchBuilder;
+use RT::ObjectCustomField;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'ObjectCustomFields';
+ $self->{'primary_key'} = 'id';
+
+
+
+ # By default, order by SortOrder
+ $self->OrderByCols(
+ { ALIAS => 'main',
+ FIELD => 'SortOrder',
+ ORDER => 'ASC' },
+ { ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'ASC' },
+ );
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::ObjectCustomField item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::ObjectCustomField->new($self->CurrentUser));
+}
+
+ eval "require RT::ObjectCustomFields_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFields_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomFields_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFields_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ObjectCustomFields_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ObjectCustomFields_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ObjectCustomFields_Overlay, RT::ObjectCustomFields_Vendor, RT::ObjectCustomFields_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ObjectCustomFields_Overlay.pm b/rt/lib/RT/ObjectCustomFields_Overlay.pm
new file mode 100644
index 0000000..0378bf3
--- /dev/null
+++ b/rt/lib/RT/ObjectCustomFields_Overlay.pm
@@ -0,0 +1,115 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::ObjectCustomFields;
+
+use strict;
+no warnings qw(redefine);
+
+sub LimitToCustomField {
+ my $self = shift;
+ my $id = shift;
+ $self->Limit( FIELD => 'CustomField', VALUE => $id );
+}
+
+sub LimitToObjectId {
+ my $self = shift;
+ my $id = shift || 0;
+ $self->Limit( FIELD => 'ObjectId', VALUE => $id );
+}
+
+sub LimitToLookupType {
+ my $self = shift;
+ my $lookup = shift;
+ unless ($self->{'_cfs_alias'}) {
+ $self->{'_cfs_alias'} = $self->NewAlias('CustomFields');
+ }
+ $self->Join( ALIAS1 => 'main',
+ FIELD1 => 'CustomField',
+ ALIAS2 => $self->{'_cfs_alias'},
+ FIELD2 => 'id' );
+ $self->Limit( ALIAS => $self->{'_cfs_alias'},
+ FIELD => 'LookupType',
+ OPERATOR => '=',
+ VALUE => $lookup );
+}
+
+sub HasEntryForCustomField {
+ my $self = shift;
+ my $id = shift;
+
+ my @items = grep {$_->CustomField == $id } @{$self->ItemsArrayRef};
+
+ if ($#items > 1) {
+ die "$self HasEntry had a list with more than one of $id in it. this can never happen";
+ }
+ if ($#items == -1 ) {
+ return undef;
+ }
+ else {
+ return ($items[0]);
+ }
+}
+
+sub CustomFields {
+ my $self = shift;
+ my %seen;
+ map { $_->CustomFieldObj } @{$self->ItemsArrayRef};
+}
+
+sub _DoSearch {
+ my $self = shift;
+ if ($self->{'_cfs_alias'}) {
+ $self->Limit( ALIAS => $self->{'_cfs_alias'},
+ FIELD => 'Disabled',
+ OPERATOR => '!=',
+ VALUE => 1);
+ }
+ $self->SUPER::_DoSearch()
+}
+
+1;
diff --git a/rt/lib/RT/Principal.pm b/rt/lib/RT/Principal.pm
new file mode 100644
index 0000000..cbc305e
--- /dev/null
+++ b/rt/lib/RT/Principal.pm
@@ -0,0 +1,236 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Principal
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Principal;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Principals');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(16) 'PrincipalType'.
+ int(11) 'ObjectId'.
+ smallint(6) 'Disabled'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ PrincipalType => '',
+ ObjectId => '',
+ Disabled => '0',
+
+ @_);
+ $self->SUPER::Create(
+ PrincipalType => $args{'PrincipalType'},
+ ObjectId => $args{'ObjectId'},
+ Disabled => $args{'Disabled'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 PrincipalType
+
+Returns the current value of PrincipalType.
+(In the database, PrincipalType is stored as varchar(16).)
+
+
+
+=head2 SetPrincipalType VALUE
+
+
+Set PrincipalType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, PrincipalType will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=head2 SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Disabled
+
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
+
+
+
+=head2 SetDisabled VALUE
+
+
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ PrincipalType =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ ObjectId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Disabled =>
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::Principal_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Principal_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Principal_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Principal_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Principal_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Principal_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Principal_Overlay, RT::Principal_Vendor, RT::Principal_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Principal_Overlay.pm b/rt/lib/RT/Principal_Overlay.pm
new file mode 100644
index 0000000..c311259
--- /dev/null
+++ b/rt/lib/RT/Principal_Overlay.pm
@@ -0,0 +1,596 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+#
+
+package RT::Principal;
+
+use strict;
+use warnings;
+
+no warnings qw(redefine);
+
+use Cache::Simple::TimedExpiry;
+
+
+
+use RT::Group;
+use RT::User;
+
+# Set up the ACL cache on startup
+our $_ACL_CACHE;
+InvalidateACLCache();
+
+# {{{ IsGroup
+
+=head2 IsGroup
+
+Returns true if this principal is a group.
+Returns undef, otherwise
+
+=cut
+
+sub IsGroup {
+ my $self = shift;
+ if ($self->PrincipalType eq 'Group') {
+ return(1);
+ }
+ else {
+ return undef;
+ }
+}
+
+# }}}
+
+# {{{ IsUser
+
+=head2 IsUser
+
+Returns true if this principal is a User.
+Returns undef, otherwise
+
+=cut
+
+sub IsUser {
+ my $self = shift;
+ if ($self->PrincipalType eq 'User') {
+ return(1);
+ }
+ else {
+ return undef;
+ }
+}
+
+# }}}
+
+# {{{ Object
+
+=head2 Object
+
+Returns the user or group associated with this principal
+
+=cut
+
+sub Object {
+ my $self = shift;
+
+ unless ($self->{'object'}) {
+ if ($self->IsUser) {
+ $self->{'object'} = RT::User->new($self->CurrentUser);
+ }
+ elsif ($self->IsGroup) {
+ $self->{'object'} = RT::Group->new($self->CurrentUser);
+ }
+ else {
+ $RT::Logger->crit("Found a principal (".$self->Id.") that was neither a user nor a group");
+ return(undef);
+ }
+ $self->{'object'}->Load($self->ObjectId());
+ }
+ return ($self->{'object'});
+
+
+}
+# }}}
+
+# {{{ ACL Related routines
+
+# {{{ GrantRight
+
+=head2 GrantRight { Right => RIGHTNAME, Object => undef }
+
+A helper function which calls RT::ACE->Create
+
+
+
+ Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's
+ false.
+
+=cut
+
+sub GrantRight {
+ my $self = shift;
+ my %args = ( Right => undef,
+ Object => undef,
+ @_);
+
+
+ unless ($args{'Right'}) {
+ return(0, $self->loc("Invalid Right"));
+ }
+
+
+ #ACL check handled in ACE.pm
+ my $ace = RT::ACE->new( $self->CurrentUser );
+
+
+ my $type = $self->_GetPrincipalTypeForACL();
+
+ # If it's a user, we really want to grant the right to their
+ # user equivalence group
+ return ( $ace->Create(RightName => $args{'Right'},
+ Object => $args{'Object'},
+ PrincipalType => $type,
+ PrincipalId => $self->Id
+ ) );
+}
+# }}}
+
+# {{{ RevokeRight
+
+=head2 RevokeRight { Right => "RightName", Object => "object" }
+
+Delete a right that a user has
+
+
+ Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's
+ false.
+
+
+=cut
+
+sub RevokeRight {
+
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ @_
+ );
+
+ #if we haven't specified any sort of right, we're talking about a global right
+ if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) {
+ $args{'Object'} = $RT::System;
+ }
+ #ACL check handled in ACE.pm
+ my $type = $self->_GetPrincipalTypeForACL();
+
+ my $ace = RT::ACE->new( $self->CurrentUser );
+ $ace->LoadByValues(
+ RightName => $args{'Right'},
+ Object => $args{'Object'},
+ PrincipalType => $type,
+ PrincipalId => $self->Id
+ );
+
+ unless ( $ace->Id ) {
+ return ( 0, $self->loc("ACE not found") );
+ }
+ return ( $ace->Delete );
+}
+
+# }}}
+
+# {{{ sub _CleanupInvalidDelegations
+
+=head2 sub _CleanupInvalidDelegations { InsideTransaction => undef }
+
+Revokes all ACE entries delegated by this principal which are
+inconsistent with this principal's current delegation rights. Does
+not perform permission checks, but takes no action and returns success
+if this principal still retains DelegateRights. Should only ever be
+called from inside the RT library.
+
+If this principal is a group, recursively calls this method on each
+cached user member of itself.
+
+If called from inside a transaction, specify a true value for the
+InsideTransaction parameter.
+
+Returns a true value if the deletion succeeded; returns a false value
+and logs an internal error if the deletion fails (should not happen).
+
+=cut
+
+# This is currently just a stub for the methods of the same name in
+# RT::User and RT::Group.
+
+sub _CleanupInvalidDelegations {
+ my $self = shift;
+ unless ( $self->Id ) {
+ $RT::Logger->warning("Principal not loaded.");
+ return (undef);
+ }
+ return ($self->Object->_CleanupInvalidDelegations(@_));
+}
+
+# }}}
+
+# {{{ sub HasRight
+
+=head2 sub HasRight (Right => 'right' Object => undef)
+
+
+Checks to see whether this principal has the right "Right" for the Object
+specified. If the Object parameter is omitted, checks to see whether the
+user has the right globally.
+
+This still hard codes to check to see if a user has queue-level rights
+if we ask about a specific ticket.
+
+
+This takes the params:
+
+ Right => name of a right
+
+ And either:
+
+ Object => an RT style object (->id will get its id)
+
+
+Returns 1 if a matching ACE was found.
+
+Returns undef if no ACE was found.
+
+=cut
+
+sub HasRight {
+
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ EquivObjects => undef,
+ @_,
+ );
+
+ unless ( $args{'Right'} ) {
+ $RT::Logger->crit("HasRight called without a right");
+ return (undef);
+ }
+
+ $args{'EquivObjects'} = [ @{ $args{'EquivObjects'} } ]
+ if $args{'EquivObjects'};
+
+ if ( $self->Disabled ) {
+ $RT::Logger->error( "Disabled User #"
+ . $self->id
+ . " failed access check for "
+ . $args{'Right'} );
+ return (undef);
+ }
+
+ if ( defined( $args{'Object'} )
+ && UNIVERSAL::can( $args{'Object'}, 'id' )
+ && $args{'Object'}->id ) {
+
+ push @{ $args{'EquivObjects'} }, $args{'Object'};
+ }
+ else {
+ $RT::Logger->crit("HasRight called with no valid object");
+ return (undef);
+ }
+
+ # If this object is a ticket, we care about ticket roles and queue roles
+ if ( UNIVERSAL::isa( $args{'Object'} => 'RT::Ticket' ) ) {
+
+ # this is a little bit hacky, but basically, now that we've done
+ # the ticket roles magic, we load the queue object
+ # and ask all the rest of our questions about the queue.
+ unshift @{ $args{'EquivObjects'} }, $args{'Object'}->QueueObj;
+
+ }
+
+ unshift @{ $args{'EquivObjects'} }, $RT::System
+ unless $self->can('_IsOverrideGlobalACL')
+ && $self->_IsOverrideGlobalACL( $args{'Object'} );
+
+
+ # {{{ If we've cached a win or loss for this lookup say so
+
+ # Construct a hashkeys to cache decisions:
+ # 1) full_hashkey - key for any result and for full combination of uid, right and objects
+ # 2) short_hashkey - one key for each object to store positive results only, it applies
+ # only to direct group rights and partly to role rights
+ my $self_id = $self->id;
+ my $full_hashkey = join ";:;", $self_id, $args{'Right'};
+ foreach ( @{ $args{'EquivObjects'} } ) {
+ my $ref_id = _ReferenceId($_);
+ $full_hashkey .= ";:;$ref_id";
+
+ my $short_hashkey = join ";:;", $self_id, $args{'Right'}, $ref_id;
+ my $cached_answer = $_ACL_CACHE->fetch($short_hashkey);
+ return $cached_answer > 0 if defined $cached_answer;
+ }
+
+ {
+ my $cached_answer = $_ACL_CACHE->fetch($full_hashkey);
+ return $cached_answer > 0 if defined $cached_answer;
+ }
+
+
+ my ($hitcount, $via_obj) = $self->_HasRight( %args );
+
+ $_ACL_CACHE->set( $full_hashkey => $hitcount? 1: -1 );
+ $_ACL_CACHE->set( "$self_id;:;$args{'Right'};:;$via_obj" => 1 )
+ if $via_obj && $hitcount;
+
+ return ($hitcount);
+}
+
+=head2 _HasRight
+
+Low level HasRight implementation, use HasRight method instead.
+
+=cut
+
+sub _HasRight
+{
+ my $self = shift;
+ {
+ my ($hit, @other) = $self->_HasGroupRight( @_ );
+ return ($hit, @other) if $hit;
+ }
+ {
+ my ($hit, @other) = $self->_HasRoleRight( @_ );
+ return ($hit, @other) if $hit;
+ }
+ return (0);
+}
+
+# this method handles role rights partly in situations
+# where user plays role X on an object and as well the right is
+# assigned to this role X of the object, for example right CommentOnTicket
+# is granted to Cc role of a queue and user is in cc list of the queue
+sub _HasGroupRight
+{
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ EquivObjects => [],
+ @_
+ );
+ my $right = $args{'Right'};
+
+ my $query =
+ "SELECT ACL.id, ACL.ObjectType, ACL.ObjectId " .
+ "FROM ACL, Principals, CachedGroupMembers WHERE " .
+
+ # Only find superuser or rights with the name $right
+ "(ACL.RightName = 'SuperUser' OR ACL.RightName = '$right') "
+
+ # Never find disabled groups.
+ . "AND Principals.id = ACL.PrincipalId "
+ . "AND Principals.PrincipalType = 'Group' "
+ . "AND Principals.Disabled = 0 "
+
+ # See if the principal is a member of the group recursively or _is the rightholder_
+ # never find recursively disabled group members
+ # also, check to see if the right is being granted _directly_ to this principal,
+ # as is the case when we want to look up group rights
+ . "AND CachedGroupMembers.GroupId = ACL.PrincipalId "
+ . "AND CachedGroupMembers.GroupId = Principals.id "
+ . "AND CachedGroupMembers.MemberId = ". $self->Id ." "
+ . "AND CachedGroupMembers.Disabled = 0 ";
+
+ my @clauses;
+ foreach my $obj ( @{ $args{'EquivObjects'} } ) {
+ my $type = ref( $obj ) || $obj;
+ my $clause = "ACL.ObjectType = '$type'";
+
+ if ( ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id ) {
+ $clause .= " AND ACL.ObjectId = ". $obj->id;
+ }
+
+ push @clauses, "($clause)";
+ }
+ if ( @clauses ) {
+ $query .= " AND (". join( ' OR ', @clauses ) .")";
+ }
+
+ $self->_Handle->ApplyLimits( \$query, 1 );
+ my ($hit, $obj, $id) = $self->_Handle->FetchResult( $query );
+ return (0) unless $hit;
+
+ $obj .= "-$id" if $id;
+ return (1, $obj);
+}
+
+sub _HasRoleRight
+{
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ EquivObjects => [],
+ @_
+ );
+ my $right = $args{'Right'};
+
+ my $query =
+ "SELECT ACL.id " .
+ "FROM ACL, Groups, Principals, CachedGroupMembers WHERE " .
+
+ # Only find superuser or rights with the name $right
+ "(ACL.RightName = 'SuperUser' OR ACL.RightName = '$right') "
+
+ # Never find disabled things
+ . "AND Principals.Disabled = 0 "
+ . "AND CachedGroupMembers.Disabled = 0 "
+
+ # We always grant rights to Groups
+ . "AND Principals.id = Groups.id "
+ . "AND Principals.PrincipalType = 'Group' "
+
+ # See if the principal is a member of the group recursively or _is the rightholder_
+ # never find recursively disabled group members
+ # also, check to see if the right is being granted _directly_ to this principal,
+ # as is the case when we want to look up group rights
+ . "AND Principals.id = CachedGroupMembers.GroupId "
+ . "AND CachedGroupMembers.MemberId = ". $self->Id ." "
+ . "AND ACL.PrincipalType = Groups.Type ";
+
+ my (@object_clauses);
+ foreach my $obj ( @{ $args{'EquivObjects'} } ) {
+ my $type = ref($obj)? ref($obj): $obj;
+ my $id;
+ $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
+
+ my $object_clause = "ACL.ObjectType = '$type'";
+ $object_clause .= " AND ACL.ObjectId = $id" if $id;
+ push @object_clauses, "($object_clause)";
+ }
+ # find ACLs that are related to our objects only
+ $query .= " AND (". join( ' OR ', @object_clauses ) .")";
+
+ # because of mysql bug in versions up to 5.0.45 we do one query per object
+ # each query should be faster on any DB as it uses indexes more effective
+ foreach my $obj ( @{ $args{'EquivObjects'} } ) {
+ my $type = ref($obj)? ref($obj): $obj;
+ my $id;
+ $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
+
+ my $tmp = $query;
+ $tmp .= " AND Groups.Domain = '$type-Role'";
+ # XXX: Groups.Instance is VARCHAR in DB, we should quote value
+ # if we want mysql 4.0 use indexes here. we MUST convert that
+ # field to integer and drop this quotes.
+ $tmp .= " AND Groups.Instance = '$id'" if $id;
+
+ $self->_Handle->ApplyLimits( \$tmp, 1 );
+ my ($hit) = $self->_Handle->FetchResult( $tmp );
+ return (1) if $hit;
+ }
+
+ return 0;
+}
+
+# }}}
+
+# }}}
+
+# {{{ ACL caching
+
+
+# {{{ InvalidateACLCache
+
+=head2 InvalidateACLCache
+
+Cleans out and reinitializes the user rights cache
+
+=cut
+
+sub InvalidateACLCache {
+ $_ACL_CACHE = Cache::Simple::TimedExpiry->new();
+ $_ACL_CACHE->expire_after($RT::ACLCacheLifetime||60);
+
+}
+
+# }}}
+
+# }}}
+
+
+# {{{ _GetPrincipalTypeForACL
+
+=head2 _GetPrincipalTypeForACL
+
+Gets the principal type. if it's a user, it's a user. if it's a role group and it has a Type,
+return that. if it has no type, return group.
+
+=cut
+
+sub _GetPrincipalTypeForACL {
+ my $self = shift;
+ my $type;
+ if ($self->PrincipalType eq 'Group' && $self->Object->Domain =~ /Role$/) {
+ $type = $self->Object->Type;
+ }
+ else {
+ $type = $self->PrincipalType;
+ }
+
+ return($type);
+}
+
+# }}}
+
+# {{{ _ReferenceId
+
+=head2 _ReferenceId
+
+Returns a list uniquely representing an object or normal scalar.
+
+For scalars, its string value is returned; for objects that has an
+id() method, its class name and Id are returned as a string separated by a "-".
+
+=cut
+
+sub _ReferenceId {
+ my $scalar = shift;
+
+ # just return the value for non-objects
+ return $scalar unless UNIVERSAL::can($scalar, 'id');
+
+ return ref($scalar) unless $scalar->id;
+
+ # an object -- return the class and id
+ return(ref($scalar)."-". $scalar->id);
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/RT/Principals.pm b/rt/lib/RT/Principals.pm
new file mode 100644
index 0000000..4c87cc9
--- /dev/null
+++ b/rt/lib/RT/Principals.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Principals -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Principals
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Principals;
+
+use RT::SearchBuilder;
+use RT::Principal;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Principals';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Principal item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Principal->new($self->CurrentUser));
+}
+
+ eval "require RT::Principals_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Principals_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Principals_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Principals_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Principals_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Principals_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Principals_Overlay, RT::Principals_Vendor, RT::Principals_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Principals_Overlay.pm b/rt/lib/RT/Principals_Overlay.pm
new file mode 100644
index 0000000..3f4bfee
--- /dev/null
+++ b/rt/lib/RT/Principals_Overlay.pm
@@ -0,0 +1,79 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Principals - a collection of RT::Principal objects
+
+=head1 SYNOPSIS
+
+ use RT::Principals;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Principals);
+
+=end testing
+
+=cut
+
+
+package RT::Principals;
+
+use strict;
+no warnings qw(redefine);
+
+
+
+
+1;
diff --git a/rt/lib/RT/Queue.pm b/rt/lib/RT/Queue.pm
new file mode 100755
index 0000000..3b4681a
--- /dev/null
+++ b/rt/lib/RT/Queue.pm
@@ -0,0 +1,395 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Queue
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Queue;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Queues');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(255) 'Description'.
+ varchar(120) 'CorrespondAddress'.
+ varchar(120) 'CommentAddress'.
+ int(11) 'InitialPriority'.
+ int(11) 'FinalPriority'.
+ int(11) 'DefaultDueIn'.
+ smallint(6) 'Disabled'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Description => '',
+ CorrespondAddress => '',
+ CommentAddress => '',
+ InitialPriority => '0',
+ FinalPriority => '0',
+ DefaultDueIn => '0',
+ Disabled => '0',
+
+ @_);
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ CorrespondAddress => $args{'CorrespondAddress'},
+ CommentAddress => $args{'CommentAddress'},
+ InitialPriority => $args{'InitialPriority'},
+ FinalPriority => $args{'FinalPriority'},
+ DefaultDueIn => $args{'DefaultDueIn'},
+ Disabled => $args{'Disabled'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 CorrespondAddress
+
+Returns the current value of CorrespondAddress.
+(In the database, CorrespondAddress is stored as varchar(120).)
+
+
+
+=head2 SetCorrespondAddress VALUE
+
+
+Set CorrespondAddress to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CorrespondAddress will be stored as a varchar(120).)
+
+
+=cut
+
+
+=head2 CommentAddress
+
+Returns the current value of CommentAddress.
+(In the database, CommentAddress is stored as varchar(120).)
+
+
+
+=head2 SetCommentAddress VALUE
+
+
+Set CommentAddress to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CommentAddress will be stored as a varchar(120).)
+
+
+=cut
+
+
+=head2 InitialPriority
+
+Returns the current value of InitialPriority.
+(In the database, InitialPriority is stored as int(11).)
+
+
+
+=head2 SetInitialPriority VALUE
+
+
+Set InitialPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, InitialPriority will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 FinalPriority
+
+Returns the current value of FinalPriority.
+(In the database, FinalPriority is stored as int(11).)
+
+
+
+=head2 SetFinalPriority VALUE
+
+
+Set FinalPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, FinalPriority will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 DefaultDueIn
+
+Returns the current value of DefaultDueIn.
+(In the database, DefaultDueIn is stored as int(11).)
+
+
+
+=head2 SetDefaultDueIn VALUE
+
+
+Set DefaultDueIn to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, DefaultDueIn will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+=head2 Disabled
+
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
+
+
+
+=head2 SetDisabled VALUE
+
+
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ CorrespondAddress =>
+ {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ CommentAddress =>
+ {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ InitialPriority =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ FinalPriority =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ DefaultDueIn =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Disabled =>
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::Queue_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Queue_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Queue_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Queue_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Queue_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Queue_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Queue_Overlay, RT::Queue_Vendor, RT::Queue_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Queue_Overlay.pm b/rt/lib/RT/Queue_Overlay.pm
new file mode 100644
index 0000000..c81cb1f
--- /dev/null
+++ b/rt/lib/RT/Queue_Overlay.pm
@@ -0,0 +1,1137 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Queue - an RT Queue object
+
+=head1 SYNOPSIS
+
+ use RT::Queue;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+use RT::Queue;
+
+=end testing
+
+=cut
+
+
+package RT::Queue;
+
+use strict;
+no warnings qw(redefine);
+
+use vars qw(@DEFAULT_ACTIVE_STATUS @DEFAULT_INACTIVE_STATUS $RIGHTS);
+
+use RT::Groups;
+use RT::ACL;
+use RT::Interface::Email;
+
+@DEFAULT_ACTIVE_STATUS = qw(new open stalled);
+@DEFAULT_INACTIVE_STATUS = qw(resolved rejected deleted);
+
+# $self->loc('new'); # For the string extractor to get a string to localize
+# $self->loc('open'); # For the string extractor to get a string to localize
+# $self->loc('stalled'); # For the string extractor to get a string to localize
+# $self->loc('resolved'); # For the string extractor to get a string to localize
+# $self->loc('rejected'); # For the string extractor to get a string to localize
+# $self->loc('deleted'); # For the string extractor to get a string to localize
+
+
+$RIGHTS = {
+ SeeQueue => 'Can this principal see this queue', # loc_pair
+ AdminQueue => 'Create, delete and modify queues', # loc_pair
+ ShowACL => 'Display Access Control List', # loc_pair
+ ModifyACL => 'Modify Access Control List', # loc_pair
+ ModifyQueueWatchers => 'Modify the queue watchers', # loc_pair
+ AssignCustomFields => 'Assign and remove custom fields', # loc_pair
+ ModifyTemplate => 'Modify Scrip templates for this queue', # loc_pair
+ ShowTemplate => 'Display Scrip templates for this queue', # loc_pair
+
+ ModifyScrips => 'Modify Scrips for this queue', # loc_pair
+ ShowScrips => 'Display Scrips for this queue', # loc_pair
+
+ ShowTicket => 'See ticket summaries', # loc_pair
+ ShowTicketComments => 'See ticket private commentary', # loc_pair
+ ShowOutgoingEmail => 'See exact outgoing email messages and their recipeients', # loc_pair
+
+ Watch => 'Sign up as a ticket Requestor or ticket or queue Cc', # loc_pair
+ WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc', # loc_pair
+ CreateTicket => 'Create tickets in this queue', # loc_pair
+ ReplyToTicket => 'Reply to tickets', # loc_pair
+ CommentOnTicket => 'Comment on tickets', # loc_pair
+ OwnTicket => 'Own tickets', # loc_pair
+ ModifyTicket => 'Modify tickets', # loc_pair
+ DeleteTicket => 'Delete tickets', # loc_pair
+ TakeTicket => 'Take tickets', # loc_pair
+ StealTicket => 'Steal tickets', # loc_pair
+
+};
+
+# Tell RT::ACE that this sort of object can get acls granted
+$RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
+
+# TODO: This should be refactored out into an RT::ACLedObject or something
+# stuff the rights into a hash of rights that can exist.
+
+foreach my $right ( keys %{$RIGHTS} ) {
+ $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
+}
+
+
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ return $self->SUPER::_AddLink(%args);
+}
+
+sub DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ #check acls
+ unless ( $self->CurrentUserHasRight('ModifyQueue') ) {
+ $RT::Logger->debug("No permission to delete links\n");
+ return ( 0, $self->loc('Permission Denied'))
+ }
+
+ return $self->SUPER::_DeleteLink(%args);
+}
+
+=head2 AvailableRights
+
+Returns a hash of available rights for this object. The keys are the right names and the values are a description of what the rights do
+
+=cut
+
+sub AvailableRights {
+ my $self = shift;
+ return($RIGHTS);
+}
+
+# {{{ ActiveStatusArray
+
+=head2 ActiveStatusArray
+
+Returns an array of all ActiveStatuses for this queue
+
+=cut
+
+sub ActiveStatusArray {
+ my $self = shift;
+ if (@RT::ActiveStatus) {
+ return (@RT::ActiveStatus)
+ } else {
+ $RT::Logger->warning("RT::ActiveStatus undefined, falling back to deprecated defaults");
+ return (@DEFAULT_ACTIVE_STATUS);
+ }
+}
+
+# }}}
+
+# {{{ InactiveStatusArray
+
+=head2 InactiveStatusArray
+
+Returns an array of all InactiveStatuses for this queue
+
+=cut
+
+sub InactiveStatusArray {
+ my $self = shift;
+ if (@RT::InactiveStatus) {
+ return (@RT::InactiveStatus)
+ } else {
+ $RT::Logger->warning("RT::InactiveStatus undefined, falling back to deprecated defaults");
+ return (@DEFAULT_INACTIVE_STATUS);
+ }
+}
+
+# }}}
+
+# {{{ StatusArray
+
+=head2 StatusArray
+
+Returns an array of all statuses for this queue
+
+=cut
+
+sub StatusArray {
+ my $self = shift;
+ return ($self->ActiveStatusArray(), $self->InactiveStatusArray());
+}
+
+# }}}
+
+# {{{ IsValidStatus
+
+=head2 IsValidStatus VALUE
+
+Returns true if VALUE is a valid status. Otherwise, returns 0.
+
+=begin testing
+
+my $q = RT::Queue->new($RT::SystemUser);
+ok($q->IsValidStatus('new')== 1, 'New is a valid status');
+ok($q->IsValidStatus('f00')== 0, 'f00 is not a valid status');
+
+=end testing
+
+=cut
+
+sub IsValidStatus {
+ my $self = shift;
+ my $value = shift;
+
+ my $retval = grep ( $_ eq $value, $self->StatusArray );
+ return ($retval);
+
+}
+
+# }}}
+
+# {{{ IsActiveStatus
+
+=head2 IsActiveStatus VALUE
+
+Returns true if VALUE is a Active status. Otherwise, returns 0
+
+=begin testing
+
+my $q = RT::Queue->new($RT::SystemUser);
+ok($q->IsActiveStatus('new')== 1, 'New is a Active status');
+ok($q->IsActiveStatus('rejected')== 0, 'Rejected is an inactive status');
+ok($q->IsActiveStatus('f00')== 0, 'f00 is not a Active status');
+
+=end testing
+
+=cut
+
+sub IsActiveStatus {
+ my $self = shift;
+ my $value = shift;
+
+ my $retval = grep ( $_ eq $value, $self->ActiveStatusArray );
+ return ($retval);
+
+}
+
+# }}}
+
+# {{{ IsInactiveStatus
+
+=head2 IsInactiveStatus VALUE
+
+Returns true if VALUE is a Inactive status. Otherwise, returns 0
+
+=begin testing
+
+my $q = RT::Queue->new($RT::SystemUser);
+ok($q->IsInactiveStatus('new')== 0, 'New is a Active status');
+ok($q->IsInactiveStatus('rejected')== 1, 'rejeected is an Inactive status');
+ok($q->IsInactiveStatus('f00')== 0, 'f00 is not a Active status');
+
+=end testing
+
+=cut
+
+sub IsInactiveStatus {
+ my $self = shift;
+ my $value = shift;
+
+ my $retval = grep ( $_ eq $value, $self->InactiveStatusArray );
+ return ($retval);
+
+}
+
+# }}}
+
+
+# {{{ sub Create
+
+
+
+
+=head2 Create(ARGS)
+
+Arguments: ARGS is a hash of named parameters. Valid parameters are:
+
+ Name (required)
+ Description
+ CorrespondAddress
+ CommentAddress
+ InitialPriority
+ FinalPriority
+ DefaultDueIn
+
+If you pass the ACL check, it creates the queue and returns its queue id.
+
+=begin testing
+
+my $queue = RT::Queue->new($RT::SystemUser);
+my ($id, $val) = $queue->Create( Name => 'Test1');
+ok($id, $val);
+
+($id, $val) = $queue->Create( Name => '66');
+ok(!$id, $val);
+
+=end testing
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => undef,
+ CorrespondAddress => '',
+ Description => '',
+ CommentAddress => '',
+ InitialPriority => "0",
+ FinalPriority => "0",
+ DefaultDueIn => "0",
+ @_
+ );
+
+ unless ( $self->CurrentUser->HasRight(Right => 'AdminQueue', Object => $RT::System) )
+ { #Check them ACLs
+ return ( 0, $self->loc("No permission to create queues") );
+ }
+
+ unless ( $self->ValidateName( $args{'Name'} ) ) {
+ return ( 0, $self->loc('Queue already exists') );
+ }
+
+ #TODO better input validation
+ $RT::Handle->BeginTransaction();
+
+ my $id = $self->SUPER::Create(%args);
+ unless ($id) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Queue could not be created') );
+ }
+
+ my $create_ret = $self->_CreateQueueGroups();
+ unless ($create_ret) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Queue could not be created') );
+ }
+
+ $RT::Handle->Commit();
+ return ( $id, $self->loc("Queue created") );
+}
+
+# }}}
+
+# {{{ sub Delete
+
+sub Delete {
+ my $self = shift;
+ return ( 0,
+ $self->loc('Deleting this object would break referential integrity') );
+}
+
+# }}}
+
+# {{{ sub SetDisabled
+
+=head2 SetDisabled
+
+Takes a boolean.
+1 will cause this queue to no longer be available for tickets.
+0 will re-enable this queue.
+
+=cut
+
+# }}}
+
+# {{{ sub Load
+
+=head2 Load
+
+Takes either a numerical id or a textual Name and loads the specified queue.
+
+=cut
+
+sub Load {
+ my $self = shift;
+
+ my $identifier = shift;
+ if ( !$identifier ) {
+ return (undef);
+ }
+
+ if ( $identifier =~ /^(\d+)$/ ) {
+ $self->SUPER::LoadById($identifier);
+ }
+ else {
+ $self->LoadByCols( Name => $identifier );
+ }
+
+ return ( $self->Id );
+
+}
+
+# }}}
+
+# {{{ sub ValidateName
+
+=head2 ValidateName NAME
+
+Takes a queue name. Returns true if it's an ok name for
+a new queue. Returns undef if there's already a queue by that name.
+
+=cut
+
+sub ValidateName {
+ my $self = shift;
+ my $name = shift;
+
+ my $tempqueue = new RT::Queue($RT::SystemUser);
+ $tempqueue->Load($name);
+
+ #If this queue exists, return undef
+ if ( $tempqueue->Name() && $tempqueue->id != $self->id) {
+ return (undef);
+ }
+
+ #If the queue doesn't exist, return 1
+ else {
+ return ($self->SUPER::ValidateName($name));
+ }
+
+}
+
+# }}}
+
+# {{{ sub Templates
+
+=head2 Templates
+
+Returns an RT::Templates object of all of this queue's templates.
+
+=cut
+
+sub Templates {
+ my $self = shift;
+
+ my $templates = RT::Templates->new( $self->CurrentUser );
+
+ if ( $self->CurrentUserHasRight('ShowTemplate') ) {
+ $templates->LimitToQueue( $self->id );
+ }
+
+ return ($templates);
+}
+
+# }}}
+
+# {{{ Dealing with custom fields
+
+# {{{ CustomField
+
+=head2 CustomField NAME
+
+Load the queue-specific custom field named NAME
+
+=cut
+
+sub CustomField {
+ my $self = shift;
+ my $name = shift;
+ my $cf = RT::CustomField->new($self->CurrentUser);
+ $cf->LoadByNameAndQueue(Name => $name, Queue => $self->Id);
+ return ($cf);
+}
+
+
+# {{{ CustomFields
+
+=head2 CustomFields
+
+Returns an RT::CustomFields object containing all global custom fields, as well as those tied to this queue
+
+=cut
+
+# XXX TODO - this should become TicketCustomFields
+
+sub CustomFields {
+ my $self = shift;
+ warn "Queue->CustomFields is deprecated, use Queue->TicketCustomFields instead at (". join(":",caller).")";
+ return $self->TicketCustomFields(@_);
+}
+
+sub TicketCustomFields {
+ my $self = shift;
+
+ my $cfs = RT::CustomFields->new( $self->CurrentUser );
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $cfs->LimitToGlobalOrObjectId( $self->Id );
+ $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+ }
+ return ($cfs);
+}
+
+sub TicketTransactionCustomFields {
+ my $self = shift;
+
+ my $cfs = RT::CustomFields->new( $self->CurrentUser );
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $cfs->LimitToGlobalOrObjectId( $self->Id );
+ $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' );
+ }
+ return ($cfs);
+}
+
+# }}}
+
+# }}}
+
+
+# {{{ Routines dealing with watchers.
+
+# {{{ _CreateQueueGroups
+
+=head2 _CreateQueueGroups
+
+Create the ticket groups and links for this ticket.
+This routine expects to be called from Ticket->Create _inside of a transaction_
+
+It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
+
+It will return true on success and undef on failure.
+
+=begin testing
+
+my $Queue = RT::Queue->new($RT::SystemUser); my ($id, $msg) = $Queue->Create(Name => "Foo",
+ );
+ok ($id, "Foo $id was created");
+ok(my $group = RT::Group->new($RT::SystemUser));
+ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
+ok ($group->Id, "Found the requestors object for this Queue");
+
+
+ok ((my $add_id, $add_msg) = $Queue->AddWatcher(Type => 'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok ($add_id, "Add succeeded: ($add_msg)");
+ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user");
+$bob->LoadByEmail('bob@fsck.com');
+ok($bob->Id, "Found the bob rt user");
+ok ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $bob->PrincipalId), "The Queue actually has bob at fsck.com as a requestor");;
+ok ((my $add_id, $add_msg) = $Queue->DeleteWatcher(Type =>'Cc', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok (!$Queue->IsWatcher(Type => 'Cc', Principal => $bob->PrincipalId), "The Queue no longer has bob at fsck.com as a requestor");;
+
+
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'Cc'));
+ok ($group->Id, "Found the cc object for this Queue");
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadQueueRoleGroup(Queue => $id, Type=> 'AdminCc'));
+ok ($group->Id, "Found the AdminCc object for this Queue");
+
+=end testing
+
+=cut
+
+
+sub _CreateQueueGroups {
+ my $self = shift;
+
+ my @types = qw(Cc AdminCc Requestor Owner);
+
+ foreach my $type (@types) {
+ my $type_obj = RT::Group->new($self->CurrentUser);
+ my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id,
+ Type => $type,
+ Domain => 'RT::Queue-Role');
+ unless ($id) {
+ $RT::Logger->error("Couldn't create a Queue group of type '$type' for ticket ".
+ $self->Id.": ".$msg);
+ return(undef);
+ }
+ }
+ return(1);
+
+}
+
+
+# }}}
+
+# {{{ sub AddWatcher
+
+=head2 AddWatcher
+
+AddWatcher takes a parameter hash. The keys are as follows:
+
+Type One of Requestor, Cc, AdminCc
+
+PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
+Email The email address of the new watcher. If a user with this
+ email address can't be found, a new nonprivileged user will be created.
+
+If the watcher you\'re trying to set has an RT account, set the Owner paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
+
+Returns a tuple of (status/id, message).
+
+=cut
+
+sub AddWatcher {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+ # {{{ Check ACLS
+ #If the watcher we're trying to add is for the current user
+ if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
+ # If it's an AdminCc and they don't have
+ # 'WatchAsAdminCc' or 'ModifyTicket', bail
+ if ( $args{'Type'} eq 'AdminCc' ) {
+ unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
+ or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
+ return ( 0, $self->loc('Permission Denied'))
+ }
+ }
+
+ # If it's a Requestor or Cc and they don't have
+ # 'Watch' or 'ModifyTicket', bail
+ elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
+
+ unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
+ or $self->CurrentUserHasRight('Watch') ) {
+ return ( 0, $self->loc('Permission Denied'))
+ }
+ }
+ else {
+ $RT::Logger->warning( "$self -> AddWatcher got passed a bogus type");
+ return ( 0, $self->loc('Error in parameters to Queue->AddWatcher') );
+ }
+ }
+
+ # If the watcher isn't the current user
+ # and the current user doesn't have 'ModifyQueueWatcher'
+ # bail
+ else {
+ unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+
+ # }}}
+
+ return ( $self->_AddWatcher(%args) );
+}
+
+#This contains the meat of AddWatcher. but can be called from a routine like
+# Create, which doesn't need the additional acl check
+sub _AddWatcher {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ Silent => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+
+ my $principal = RT::Principal->new($self->CurrentUser);
+ if ($args{'PrincipalId'}) {
+ $principal->Load($args{'PrincipalId'});
+ }
+ elsif ($args{'Email'}) {
+
+ my $user = RT::User->new($self->CurrentUser);
+ $user->LoadByEmail($args{'Email'});
+
+ unless ($user->Id) {
+ $user->Load($args{'Email'});
+ }
+ if ($user->Id) { # If the user exists
+ $principal->Load($user->PrincipalId);
+ } else {
+
+ # if the user doesn't exist, we need to create a new user
+ my $new_user = RT::User->new($RT::SystemUser);
+
+ my ( $Address, $Name ) =
+ RT::Interface::Email::ParseAddressFromHeader($args{'Email'});
+
+ my ( $Val, $Message ) = $new_user->Create(
+ Name => $Address,
+ EmailAddress => $Address,
+ RealName => $Name,
+ Privileged => 0,
+ Comments => 'Autocreated when added as a watcher');
+ unless ($Val) {
+ $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message);
+ # Deal with the race condition of two account creations at once
+ $new_user->LoadByEmail($args{'Email'});
+ }
+ $principal->Load($new_user->PrincipalId);
+ }
+ }
+ # If we can't find this watcher, we need to bail.
+ unless ($principal->Id) {
+ return(0, $self->loc("Could not find or create that user"));
+ }
+
+
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
+ unless ($group->id) {
+ return(0,$self->loc("Group not found"));
+ }
+
+ if ( $group->HasMember( $principal)) {
+
+ return ( 0, $self->loc('That principal is already a [_1] for this queue', $args{'Type'}) );
+ }
+
+
+ my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id);
+ unless ($m_id) {
+ $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
+
+ return ( 0, $self->loc('Could not make that principal a [_1] for this queue', $args{'Type'}) );
+ }
+ return ( 1, $self->loc('Added principal as a [_1] for this queue', $args{'Type'}) );
+}
+
+# }}}
+
+# {{{ sub DeleteWatcher
+
+=head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
+
+
+Deletes a queue watcher. Takes two arguments:
+
+Type (one of Requestor,Cc,AdminCc)
+
+and one of
+
+PrincipalId (an RT::Principal Id of the watcher you want to remove)
+ OR
+Email (the email address of an existing wathcer)
+
+
+=cut
+
+
+sub DeleteWatcher {
+ my $self = shift;
+
+ my %args = ( Type => undef,
+ PrincipalId => undef,
+ @_ );
+
+ unless ($args{'PrincipalId'} ) {
+ return(0, $self->loc("No principal specified"));
+ }
+ my $principal = RT::Principal->new($self->CurrentUser);
+ $principal->Load($args{'PrincipalId'});
+
+ # If we can't find this watcher, we need to bail.
+ unless ($principal->Id) {
+ return(0, $self->loc("Could not find that principal"));
+ }
+
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->Id);
+ unless ($group->id) {
+ return(0,$self->loc("Group not found"));
+ }
+
+ # {{{ Check ACLS
+ #If the watcher we're trying to add is for the current user
+ if ( $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) {
+ # If it's an AdminCc and they don't have
+ # 'WatchAsAdminCc' or 'ModifyQueue', bail
+ if ( $args{'Type'} eq 'AdminCc' ) {
+ unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
+ or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
+ return ( 0, $self->loc('Permission Denied'))
+ }
+ }
+
+ # If it's a Requestor or Cc and they don't have
+ # 'Watch' or 'ModifyQueue', bail
+ elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
+ unless ( $self->CurrentUserHasRight('ModifyQueueWatchers')
+ or $self->CurrentUserHasRight('Watch') ) {
+ return ( 0, $self->loc('Permission Denied'))
+ }
+ }
+ else {
+ $RT::Logger->warning( "$self -> DeleteWatcher got passed a bogus type");
+ return ( 0, $self->loc('Error in parameters to Queue->DeleteWatcher') );
+ }
+ }
+
+ # If the watcher isn't the current user
+ # and the current user doesn't have 'ModifyQueueWathcers' bail
+ else {
+ unless ( $self->CurrentUserHasRight('ModifyQueueWatchers') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+
+ # }}}
+
+
+ # see if this user is already a watcher.
+
+ unless ( $group->HasMember($principal)) {
+ return ( 0,
+ $self->loc('That principal is not a [_1] for this queue', $args{'Type'}) );
+ }
+
+ my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id);
+ unless ($m_id) {
+ $RT::Logger->error("Failed to delete ".$principal->Id.
+ " as a member of group ".$group->Id."\n".$m_msg);
+
+ return ( 0, $self->loc('Could not remove that principal as a [_1] for this queue', $args{'Type'}) );
+ }
+
+ return ( 1, $self->loc("[_1] is no longer a [_2] for this queue.", $principal->Object->Name, $args{'Type'} ));
+}
+
+# }}}
+
+# {{{ AdminCcAddresses
+
+=head2 AdminCcAddresses
+
+returns String: All queue AdminCc email addresses as a string
+
+=cut
+
+sub AdminCcAddresses {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return undef;
+ }
+
+ return ( $self->AdminCc->MemberEmailAddressesAsString )
+
+}
+
+# }}}
+
+# {{{ CcAddresses
+
+=head2 CcAddresses
+
+returns String: All queue Ccs as a string of email addresses
+
+=cut
+
+sub CcAddresses {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return undef;
+ }
+
+ return ( $self->Cc->MemberEmailAddressesAsString);
+
+}
+# }}}
+
+
+# {{{ sub Cc
+
+=head2 Cc
+
+Takes nothing.
+Returns an RT::Group object which contains this Queue's Ccs.
+If the user doesn't have "ShowQueue" permission, returns an empty group
+
+=cut
+
+sub Cc {
+ my $self = shift;
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $group->LoadQueueRoleGroup(Type => 'Cc', Queue => $self->Id);
+ }
+ return ($group);
+
+}
+
+# }}}
+
+# {{{ sub AdminCc
+
+=head2 AdminCc
+
+Takes nothing.
+Returns an RT::Group object which contains this Queue's AdminCcs.
+If the user doesn't have "ShowQueue" permission, returns an empty group
+
+=cut
+
+sub AdminCc {
+ my $self = shift;
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('SeeQueue') ) {
+ $group->LoadQueueRoleGroup(Type => 'AdminCc', Queue => $self->Id);
+ }
+ return ($group);
+
+}
+
+# }}}
+
+# {{{ IsWatcher, IsCc, IsAdminCc
+
+# {{{ sub IsWatcher
+# a generic routine to be called by IsRequestor, IsCc and IsAdminCc
+
+=head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID }
+
+Takes a param hash with the attributes Type and PrincipalId
+
+Type is one of Requestor, Cc, AdminCc and Owner
+
+PrincipalId is an RT::Principal id
+
+Returns true if that principal is a member of the group Type for this queue
+
+
+=cut
+
+sub IsWatcher {
+ my $self = shift;
+
+ my %args = ( Type => 'Cc',
+ PrincipalId => undef,
+ @_
+ );
+
+ # Load the relevant group.
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadQueueRoleGroup(Type => $args{'Type'}, Queue => $self->id);
+ # Ask if it has the member in question
+
+ my $principal = RT::Principal->new($self->CurrentUser);
+ $principal->Load($args{'PrincipalId'});
+ unless ($principal->Id) {
+ return (undef);
+ }
+
+ return ($group->HasMemberRecursively($principal));
+}
+
+# }}}
+
+
+# {{{ sub IsCc
+
+=head2 IsCc PRINCIPAL_ID
+
+Takes an RT::Principal id.
+Returns true if the principal is a requestor of the current queue.
+
+
+=cut
+
+sub IsCc {
+ my $self = shift;
+ my $cc = shift;
+
+ return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
+
+}
+
+# }}}
+
+# {{{ sub IsAdminCc
+
+=head2 IsAdminCc PRINCIPAL_ID
+
+Takes an RT::Principal id.
+Returns true if the principal is a requestor of the current queue.
+
+=cut
+
+sub IsAdminCc {
+ my $self = shift;
+ my $person = shift;
+
+ return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
+
+}
+
+# }}}
+
+
+# }}}
+
+
+
+
+
+# }}}
+
+# {{{ ACCESS CONTROL
+
+# {{{ sub _Set
+sub _Set {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminQueue') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ return ( $self->SUPER::_Set(@_) );
+}
+
+# }}}
+
+# {{{ sub _Value
+
+sub _Value {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('SeeQueue') ) {
+ return (undef);
+ }
+
+ return ( $self->__Value(@_) );
+}
+
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
+=head2 CurrentUserHasRight
+
+Takes one argument. A textual string with the name of the right we want to check.
+Returns true if the current user has that right for this queue.
+Returns undef otherwise.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ return (
+ $self->HasRight(
+ Principal => $self->CurrentUser,
+ Right => "$right"
+ )
+ );
+
+}
+
+# }}}
+
+# {{{ sub HasRight
+
+=head2 HasRight
+
+Takes a param hash with the fields 'Right' and 'Principal'.
+Principal defaults to the current user.
+Returns true if the principal has that right for this queue.
+Returns undef otherwise.
+
+=cut
+
+# TAKES: Right and optional "Principal" which defaults to the current user
+sub HasRight {
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Principal => $self->CurrentUser,
+ @_
+ );
+ unless ( defined $args{'Principal'} ) {
+ $RT::Logger->debug("Principal undefined in Queue::HasRight");
+
+ }
+ return (
+ $args{'Principal'}->HasRight(
+ Object => $self->Id ? $self : $RT::System,
+ Right => $args{'Right'}
+ )
+ );
+}
+
+# }}}
+
+# }}}
+
+1;
diff --git a/rt/lib/RT/Queues.pm b/rt/lib/RT/Queues.pm
new file mode 100755
index 0000000..bc5f480
--- /dev/null
+++ b/rt/lib/RT/Queues.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Queues -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Queues
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Queues;
+
+use RT::SearchBuilder;
+use RT::Queue;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Queues';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Queue item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Queue->new($self->CurrentUser));
+}
+
+ eval "require RT::Queues_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Queues_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Queues_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Queues_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Queues_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Queues_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Queues_Overlay, RT::Queues_Vendor, RT::Queues_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Queues_Overlay.pm b/rt/lib/RT/Queues_Overlay.pm
new file mode 100644
index 0000000..ffc1d78
--- /dev/null
+++ b/rt/lib/RT/Queues_Overlay.pm
@@ -0,0 +1,144 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Queues - a collection of RT::Queue objects
+
+=head1 SYNOPSIS
+
+ use RT::Queues;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Queues);
+
+=end testing
+
+=cut
+
+
+package RT::Queues;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = "Queues";
+ $self->{'primary_key'} = "id";
+
+ # By default, order by name
+ $self->OrderBy( ALIAS => 'main',
+ FIELD => 'Name',
+ ORDER => 'ASC');
+
+ return ($self->SUPER::_Init(@_));
+}
+# }}}
+
+# {{{ sub _DoSearch
+
+=head2 _DoSearch
+
+ A subclass of DBIx::SearchBuilder::_DoSearch that makes sure that _Disabled rows never get seen unless
+we're explicitly trying to see them.
+
+=cut
+
+sub _DoSearch {
+ my $self = shift;
+
+ #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
+ unless($self->{'find_disabled_rows'}) {
+ $self->LimitToEnabled();
+ }
+
+ return($self->SUPER::_DoSearch(@_));
+
+}
+
+# }}}
+
+
+# {{{ sub Limit
+sub Limit {
+ my $self = shift;
+ my %args = ( ENTRYAGGREGATOR => 'AND',
+ @_);
+ $self->SUPER::Limit(%args);
+}
+# }}}
+
+# {{{ sub AddRecord
+
+=head2 AddRecord
+
+Adds a record object to this collection if this user can see.
+This is used for filtering objects for both Next and ItemsArrayRef.
+
+=cut
+
+sub AddRecord {
+ my $self = shift;
+ my $Queue = shift;
+ return unless $Queue->CurrentUserHasRight('SeeQueue');
+
+ push @{$self->{'items'}}, $Queue;
+ $self->{'rows'}++;
+}
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm
new file mode 100755
index 0000000..b32ef55
--- /dev/null
+++ b/rt/lib/RT/Record.pm
@@ -0,0 +1,1941 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Record - Base class for RT record objects
+
+=head1 SYNOPSIS
+
+
+=head1 DESCRIPTION
+
+
+=begin testing
+
+ok (require RT::Record);
+
+=end testing
+
+=head1 METHODS
+
+=cut
+
+package RT::Record;
+
+use strict;
+use warnings;
+
+our @ISA;
+use base qw(RT::Base);
+
+use RT::Date;
+use RT::I18N;
+use RT::User;
+use RT::Attributes;
+use DBIx::SearchBuilder::Record::Cachable;
+use Encode qw();
+
+our $_TABLE_ATTR = { };
+
+
+if ( $RT::DontCacheSearchBuilderRecords ) {
+ push (@ISA, 'DBIx::SearchBuilder::Record');
+} else {
+ push (@ISA, 'DBIx::SearchBuilder::Record::Cachable');
+
+}
+
+# {{{ sub _Init
+
+sub _Init {
+ my $self = shift;
+ $self->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+ $self->CurrentUser(@_);
+}
+
+# }}}
+
+# {{{ _PrimaryKeys
+
+=head2 _PrimaryKeys
+
+The primary keys for RT classes is 'id'
+
+=cut
+
+sub _PrimaryKeys {
+ my $self = shift;
+ return ( ['id'] );
+}
+
+# }}}
+
+=head2 Delete
+
+Delete this record object from the database.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+ my ($rv) = $self->SUPER::Delete;
+ if ($rv) {
+ return ($rv, $self->loc("Object deleted"));
+ } else {
+
+ return(0, $self->loc("Object could not be deleted"))
+ }
+}
+
+=head2 ObjectTypeStr
+
+Returns a string which is this object's type. The type is the class,
+without the "RT::" prefix.
+
+=begin testing
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my $group = RT::Group->new($RT::SystemUser);
+is($ticket->ObjectTypeStr, 'Ticket', "Ticket returns correct typestring");
+is($group->ObjectTypeStr, 'Group', "Group returns correct typestring");
+
+=end testing
+
+=cut
+
+sub ObjectTypeStr {
+ my $self = shift;
+ if (ref($self) =~ /^.*::(\w+)$/) {
+ return $self->loc($1);
+ } else {
+ return $self->loc(ref($self));
+ }
+}
+
+=head2 Attributes
+
+Return this object's attributes as an RT::Attributes object
+
+=cut
+
+sub Attributes {
+ my $self = shift;
+
+ unless ($self->{'attributes'}) {
+ $self->{'attributes'} = RT::Attributes->new($self->CurrentUser);
+ $self->{'attributes'}->LimitToObject($self);
+ }
+ return ($self->{'attributes'});
+
+}
+
+
+=head2 AddAttribute { Name, Description, Content }
+
+Adds a new attribute for this object.
+
+=cut
+
+sub AddAttribute {
+ my $self = shift;
+ my %args = ( Name => undef,
+ Description => undef,
+ Content => undef,
+ @_ );
+
+ my $attr = RT::Attribute->new( $self->CurrentUser );
+ my ( $id, $msg ) = $attr->Create(
+ Object => $self,
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ Content => $args{'Content'} );
+
+
+ # XXX TODO: Why won't RedoSearch work here?
+ $self->Attributes->_DoSearch;
+
+ return ($id, $msg);
+}
+
+
+=head2 SetAttribute { Name, Description, Content }
+
+Like AddAttribute, but replaces all existing attributes with the same Name.
+
+=cut
+
+sub SetAttribute {
+ my $self = shift;
+ my %args = ( Name => undef,
+ Description => undef,
+ Content => undef,
+ @_ );
+
+ my @AttributeObjs = $self->Attributes->Named( $args{'Name'} )
+ or return $self->AddAttribute( %args );
+
+ my $AttributeObj = pop( @AttributeObjs );
+ $_->Delete foreach @AttributeObjs;
+
+ $AttributeObj->SetDescription( $args{'Description'} );
+ $AttributeObj->SetContent( $args{'Content'} );
+
+ $self->Attributes->RedoSearch;
+ return 1;
+}
+
+=head2 DeleteAttribute NAME
+
+Deletes all attributes with the matching name for this object.
+
+=cut
+
+sub DeleteAttribute {
+ my $self = shift;
+ my $name = shift;
+ return $self->Attributes->DeleteEntry( Name => $name );
+}
+
+=head2 FirstAttribute NAME
+
+Returns the first attribute with the matching name for this object (as an
+L<RT::Attribute> object), or C<undef> if no such attributes exist.
+
+Note that if there is more than one attribute with the matching name on the
+object, the choice of which one to return is basically arbitrary. This may be
+made well-defined in the future.
+
+=cut
+
+sub FirstAttribute {
+ my $self = shift;
+ my $name = shift;
+ return ($self->Attributes->Named( $name ))[0];
+}
+
+
+# {{{ sub _Handle
+sub _Handle {
+ my $self = shift;
+ return ($RT::Handle);
+}
+
+# }}}
+
+# {{{ sub Create
+
+=head2 Create PARAMHASH
+
+Takes a PARAMHASH of Column -> Value pairs.
+If any Column has a Validate$PARAMNAME subroutine defined and the
+value provided doesn't pass validation, this routine returns
+an error.
+
+If this object's table has any of the following atetributes defined as
+'Auto', this routine will automatically fill in their values.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %attribs = (@_);
+ foreach my $key ( keys %attribs ) {
+ my $method = "Validate$key";
+ unless ( $self->$method( $attribs{$key} ) ) {
+ if (wantarray) {
+ return ( 0, $self->loc('Invalid value for [_1]', $key) );
+ }
+ else {
+ return (0);
+ }
+ }
+ }
+ my $now = RT::Date->new( $self->CurrentUser );
+ $now->Set( Format => 'unix', Value => time );
+ $attribs{'Created'} = $now->ISO() if ( $self->_Accessible( 'Created', 'auto' ) && !$attribs{'Created'});
+
+ if ($self->_Accessible( 'Creator', 'auto' ) && !$attribs{'Creator'}) {
+ $attribs{'Creator'} = $self->CurrentUser->id || '0';
+ }
+ $attribs{'LastUpdated'} = $now->ISO()
+ if ( $self->_Accessible( 'LastUpdated', 'auto' ) && !$attribs{'LastUpdated'});
+
+ $attribs{'LastUpdatedBy'} = $self->CurrentUser->id || '0'
+ if ( $self->_Accessible( 'LastUpdatedBy', 'auto' ) && !$attribs{'LastUpdatedBy'});
+
+ my $id = $self->SUPER::Create(%attribs);
+ if ( UNIVERSAL::isa( $id, 'Class::ReturnValue' ) ) {
+ if ( $id->errno ) {
+ if (wantarray) {
+ return ( 0,
+ $self->loc( "Internal Error: [_1]", $id->{error_message} ) );
+ }
+ else {
+ return (0);
+ }
+ }
+ }
+ # If the object was created in the database,
+ # load it up now, so we're sure we get what the database
+ # has. Arguably, this should not be necessary, but there
+ # isn't much we can do about it.
+
+ unless ($id) {
+ if (wantarray) {
+ return ( $id, $self->loc('Object could not be created') );
+ }
+ else {
+ return ($id);
+ }
+
+ }
+
+ if (UNIVERSAL::isa('errno',$id)) {
+ exit(0);
+ warn "It's here!";
+ return(undef);
+ }
+
+ $self->Load($id) if ($id);
+
+
+
+ if (wantarray) {
+ return ( $id, $self->loc('Object created') );
+ }
+ else {
+ return ($id);
+ }
+
+}
+
+# }}}
+
+# {{{ sub LoadByCols
+
+=head2 LoadByCols
+
+Override DBIx::SearchBuilder::LoadByCols to do case-insensitive loads if the
+DB is case sensitive
+
+=cut
+
+sub LoadByCols {
+ my $self = shift;
+ my %hash = (@_);
+
+ # We don't want to hang onto this
+ delete $self->{'attributes'};
+
+ # If this database is case sensitive we need to uncase objects for
+ # explicit loading
+ if ( $self->_Handle->CaseSensitive ) {
+ my %newhash;
+ foreach my $key ( keys %hash ) {
+
+ # If we've been passed an empty value, we can't do the lookup.
+ # We don't need to explicitly downcase integers or an id.
+ if ( $key =~ '^id$'
+ || !defined( $hash{$key} )
+ || $hash{$key} =~ /^\d+$/
+ )
+ {
+ $newhash{$key} = $hash{$key};
+ }
+ else {
+ my ($op, $val, $func);
+ ($key, $op, $val, $func) = $self->_Handle->_MakeClauseCaseInsensitive($key, '=', $hash{$key});
+ $newhash{$key}->{operator} = $op;
+ $newhash{$key}->{value} = $val;
+ $newhash{$key}->{function} = $func;
+ }
+ }
+
+ # We've clobbered everything we care about. bash the old hash
+ # and replace it with the new hash
+ %hash = %newhash;
+ }
+ $self->SUPER::LoadByCols(%hash);
+}
+
+# }}}
+
+# {{{ Datehandling
+
+# There is room for optimizations in most of those subs:
+
+# {{{ LastUpdatedObj
+
+sub LastUpdatedObj {
+ my $self = shift;
+ my $obj = new RT::Date( $self->CurrentUser );
+
+ $obj->Set( Format => 'sql', Value => $self->LastUpdated );
+ return $obj;
+}
+
+# }}}
+
+# {{{ CreatedObj
+
+sub CreatedObj {
+ my $self = shift;
+ my $obj = new RT::Date( $self->CurrentUser );
+
+ $obj->Set( Format => 'sql', Value => $self->Created );
+
+ return $obj;
+}
+
+# }}}
+
+# {{{ AgeAsString
+#
+# TODO: This should be deprecated
+#
+sub AgeAsString {
+ my $self = shift;
+ return ( $self->CreatedObj->AgeAsString() );
+}
+
+# }}}
+
+# {{{ LastUpdatedAsString
+
+# TODO this should be deprecated
+
+sub LastUpdatedAsString {
+ my $self = shift;
+ if ( $self->LastUpdated ) {
+ return ( $self->LastUpdatedObj->AsString() );
+
+ }
+ else {
+ return "never";
+ }
+}
+
+# }}}
+
+# {{{ CreatedAsString
+#
+# TODO This should be deprecated
+#
+sub CreatedAsString {
+ my $self = shift;
+ return ( $self->CreatedObj->AsString() );
+}
+
+# }}}
+
+# {{{ LongSinceUpdateAsString
+#
+# TODO This should be deprecated
+#
+sub LongSinceUpdateAsString {
+ my $self = shift;
+ if ( $self->LastUpdated ) {
+
+ return ( $self->LastUpdatedObj->AgeAsString() );
+
+ }
+ else {
+ return "never";
+ }
+}
+
+# }}}
+
+# }}} Datehandling
+
+# {{{ sub _Set
+#
+sub _Set {
+ my $self = shift;
+
+ my %args = (
+ Field => undef,
+ Value => undef,
+ IsSQL => undef,
+ @_
+ );
+
+ #if the user is trying to modify the record
+ # TODO: document _why_ this code is here
+
+ if ( ( !defined( $args{'Field'} ) ) || ( !defined( $args{'Value'} ) ) ) {
+ $args{'Value'} = 0;
+ }
+
+ my $old_val = $self->__Value($args{'Field'});
+ $self->_SetLastUpdated();
+ my $ret = $self->SUPER::_Set(
+ Field => $args{'Field'},
+ Value => $args{'Value'},
+ IsSQL => $args{'IsSQL'}
+ );
+ my ($status, $msg) = $ret->as_array();
+
+ # @values has two values, a status code and a message.
+
+ # $ret is a Class::ReturnValue object. as such, in a boolean context, it's a bool
+ # we want to change the standard "success" message
+ if ($status) {
+ $msg =
+ $self->loc(
+ "[_1] changed from [_2] to [_3]",
+ $args{'Field'},
+ ( $old_val ? "'$old_val'" : $self->loc("(no value)") ),
+ '"' . $self->__Value( $args{'Field'}) . '"'
+ );
+ } else {
+
+ $msg = $self->CurrentUser->loc_fuzzy($msg);
+ }
+ return wantarray ? ($status, $msg) : $ret;
+
+}
+
+# }}}
+
+# {{{ sub _SetLastUpdated
+
+=head2 _SetLastUpdated
+
+This routine updates the LastUpdated and LastUpdatedBy columns of the row in question
+It takes no options. Arguably, this is a bug
+
+=cut
+
+sub _SetLastUpdated {
+ my $self = shift;
+ use RT::Date;
+ my $now = new RT::Date( $self->CurrentUser );
+ $now->SetToNow();
+
+ if ( $self->_Accessible( 'LastUpdated', 'auto' ) ) {
+ my ( $msg, $val ) = $self->__Set(
+ Field => 'LastUpdated',
+ Value => $now->ISO
+ );
+ }
+ if ( $self->_Accessible( 'LastUpdatedBy', 'auto' ) ) {
+ my ( $msg, $val ) = $self->__Set(
+ Field => 'LastUpdatedBy',
+ Value => $self->CurrentUser->id
+ );
+ }
+}
+
+# }}}
+
+# {{{ sub CreatorObj
+
+=head2 CreatorObj
+
+Returns an RT::User object with the RT account of the creator of this row
+
+=cut
+
+sub CreatorObj {
+ my $self = shift;
+ unless ( exists $self->{'CreatorObj'} ) {
+
+ $self->{'CreatorObj'} = RT::User->new( $self->CurrentUser );
+ $self->{'CreatorObj'}->Load( $self->Creator );
+ }
+ return ( $self->{'CreatorObj'} );
+}
+
+# }}}
+
+# {{{ sub LastUpdatedByObj
+
+=head2 LastUpdatedByObj
+
+ Returns an RT::User object of the last user to touch this object
+
+=cut
+
+sub LastUpdatedByObj {
+ my $self = shift;
+ unless ( exists $self->{LastUpdatedByObj} ) {
+ $self->{'LastUpdatedByObj'} = RT::User->new( $self->CurrentUser );
+ $self->{'LastUpdatedByObj'}->Load( $self->LastUpdatedBy );
+ }
+ return $self->{'LastUpdatedByObj'};
+}
+
+# }}}
+
+# {{{ sub URI
+
+=head2 URI
+
+Returns this record's URI
+
+=cut
+
+sub URI {
+ my $self = shift;
+ my $uri = RT::URI::fsck_com_rt->new($self->CurrentUser);
+ return($uri->URIForObject($self));
+}
+
+# }}}
+
+=head2 ValidateName NAME
+
+Validate the name of the record we're creating. Mostly, just make sure it's not a numeric ID, which is invalid for Name
+
+=cut
+
+sub ValidateName {
+ my $self = shift;
+ my $value = shift;
+ if ($value && $value=~ /^\d+$/) {
+ return(0);
+ } else {
+ return (1);
+ }
+}
+
+
+
+=head2 SQLType attribute
+
+return the SQL type for the attribute 'attribute' as stored in _ClassAccessible
+
+=cut
+
+sub SQLType {
+ my $self = shift;
+ my $field = shift;
+
+ return ($self->_Accessible($field, 'type'));
+
+
+}
+
+
+sub __Value {
+ my $self = shift;
+ my $field = shift;
+ my %args = ( decode_utf8 => 1,
+ @_ );
+
+ unless (defined $field && $field) {
+ $RT::Logger->error("$self __Value called with undef field");
+ }
+ my $value = $self->SUPER::__Value($field);
+
+ return('') if ( !defined($value) || $value eq '');
+
+ if( $args{'decode_utf8'} ) {
+ # XXX: is_utf8 check should be here unless Encode bug would be fixed
+ # see http://rt.cpan.org/NoAuth/Bug.html?id=14559
+ return Encode::decode_utf8($value) unless Encode::is_utf8($value);
+ } else {
+ # check is_utf8 here just to be shure
+ return Encode::encode_utf8($value) if Encode::is_utf8($value);
+ }
+ return $value;
+}
+
+# Set up defaults for DBIx::SearchBuilder::Record::Cachable
+
+sub _CacheConfig {
+ {
+ 'cache_p' => 1,
+ 'cache_for_sec' => 30,
+ }
+}
+
+
+
+sub _BuildTableAttributes {
+ my $self = shift;
+
+ my $attributes;
+ if ( UNIVERSAL::can( $self, '_CoreAccessible' ) ) {
+ $attributes = $self->_CoreAccessible();
+ } elsif ( UNIVERSAL::can( $self, '_ClassAccessible' ) ) {
+ $attributes = $self->_ClassAccessible();
+
+ }
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ if ( UNIVERSAL::can( $self, '_OverlayAccessible' ) ) {
+ $attributes = $self->_OverlayAccessible();
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ }
+ if ( UNIVERSAL::can( $self, '_VendorAccessible' ) ) {
+ $attributes = $self->_VendorAccessible();
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ }
+ if ( UNIVERSAL::can( $self, '_LocalAccessible' ) ) {
+ $attributes = $self->_LocalAccessible();
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ }
+
+}
+
+
+=head2 _ClassAccessible
+
+Overrides the "core" _ClassAccessible using $_TABLE_ATTR. Behaves identical to the version in
+DBIx::SearchBuilder::Record
+
+=cut
+
+sub _ClassAccessible {
+ my $self = shift;
+ return $_TABLE_ATTR->{ref($self)};
+}
+
+=head2 _Accessible COLUMN ATTRIBUTE
+
+returns the value of ATTRIBUTE for COLUMN
+
+
+=cut
+
+sub _Accessible {
+ my $self = shift;
+ my $column = shift;
+ my $attribute = lc(shift);
+ return 0 unless defined ($_TABLE_ATTR->{ref($self)}->{$column});
+ return $_TABLE_ATTR->{ref($self)}->{$column}->{$attribute} || 0;
+
+}
+
+=head2 _EncodeLOB BODY MIME_TYPE
+
+Takes a potentially large attachment. Returns (ContentEncoding, EncodedBody) based on system configuration and selected database
+
+=cut
+
+sub _EncodeLOB {
+ my $self = shift;
+ my $Body = shift;
+ my $MIMEType = shift;
+
+ my $ContentEncoding = 'none';
+
+ #get the max attachment length from RT
+ my $MaxSize = $RT::MaxAttachmentSize;
+
+ #if the current attachment contains nulls and the
+ #database doesn't support embedded nulls
+
+ if ( $RT::AlwaysUseBase64 or
+ ( !$RT::Handle->BinarySafeBLOBs ) && ( $Body =~ /\x00/ ) ) {
+
+ # set a flag telling us to mimencode the attachment
+ $ContentEncoding = 'base64';
+
+ #cut the max attchment size by 25% (for mime-encoding overhead.
+ $RT::Logger->debug("Max size is $MaxSize\n");
+ $MaxSize = $MaxSize * 3 / 4;
+ # Some databases (postgres) can't handle non-utf8 data
+ } elsif ( !$RT::Handle->BinarySafeBLOBs
+ && $MIMEType !~ /text\/plain/gi
+ && !Encode::is_utf8( $Body, 1 ) ) {
+ $ContentEncoding = 'quoted-printable';
+ }
+
+ #if the attachment is larger than the maximum size
+ if ( ($MaxSize) and ( $MaxSize < length($Body) ) ) {
+
+ # if we're supposed to truncate large attachments
+ if ($RT::TruncateLongAttachments) {
+
+ # truncate the attachment to that length.
+ $Body = substr( $Body, 0, $MaxSize );
+
+ }
+
+ # elsif we're supposed to drop large attachments on the floor,
+ elsif ($RT::DropLongAttachments) {
+
+ # drop the attachment on the floor
+ $RT::Logger->info( "$self: Dropped an attachment of size "
+ . length($Body) . "\n"
+ . "It started: " . substr( $Body, 0, 60 ) . "\n"
+ );
+ return ("none", "Large attachment dropped" );
+ }
+ }
+
+ # if we need to mimencode the attachment
+ if ( $ContentEncoding eq 'base64' ) {
+
+ # base64 encode the attachment
+ Encode::_utf8_off($Body);
+ $Body = MIME::Base64::encode_base64($Body);
+
+ } elsif ($ContentEncoding eq 'quoted-printable') {
+ Encode::_utf8_off($Body);
+ $Body = MIME::QuotedPrint::encode($Body);
+ }
+
+
+ return ($ContentEncoding, $Body);
+
+}
+
+sub _DecodeLOB {
+ my $self = shift;
+ my $ContentType = shift;
+ my $ContentEncoding = shift;
+ my $Content = shift;
+
+ if ( $ContentEncoding eq 'base64' ) {
+ $Content = MIME::Base64::decode_base64($Content);
+ }
+ elsif ( $ContentEncoding eq 'quoted-printable' ) {
+ $Content = MIME::QuotedPrint::decode($Content);
+ }
+ elsif ( $ContentEncoding && $ContentEncoding ne 'none' ) {
+ return ( $self->loc( "Unknown ContentEncoding [_1]", $ContentEncoding ) );
+ }
+
+ if ( RT::I18N::IsTextualContentType($ContentType) ) {
+ $Content = Encode::decode_utf8($Content) unless Encode::is_utf8($Content);
+ }
+ return ($Content);
+}
+
+# {{{ LINKDIRMAP
+# A helper table for links mapping to make it easier
+# to build and parse links between tickets
+
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
+ MemberOf => { Base => 'MemberOf',
+ Target => 'HasMember', },
+ RefersTo => { Base => 'RefersTo',
+ Target => 'ReferredToBy', },
+ DependsOn => { Base => 'DependsOn',
+ Target => 'DependedOnBy', },
+ MergedInto => { Base => 'MergedInto',
+ Target => 'MergedInto', },
+
+);
+
+=head2 Update ARGSHASH
+
+Updates fields on an object for you using the proper Set methods,
+skipping unchanged values.
+
+ ARGSRef => a hashref of attributes => value for the update
+ AttributesRef => an arrayref of keys in ARGSRef that should be updated
+ AttributePrefix => a prefix that should be added to the attributes in AttributesRef
+ when looking up values in ARGSRef
+ Bare attributes are tried before prefixed attributes
+
+Returns a list of localized results of the update
+
+=cut
+
+sub Update {
+ my $self = shift;
+
+ my %args = (
+ ARGSRef => undef,
+ AttributesRef => undef,
+ AttributePrefix => undef,
+ @_
+ );
+
+ my $attributes = $args{'AttributesRef'};
+ my $ARGSRef = $args{'ARGSRef'};
+ my @results;
+
+ foreach my $attribute (@$attributes) {
+ my $value;
+ if ( defined $ARGSRef->{$attribute} ) {
+ $value = $ARGSRef->{$attribute};
+ }
+ elsif (
+ defined( $args{'AttributePrefix'} )
+ && defined(
+ $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute }
+ )
+ ) {
+ $value = $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute };
+
+ }
+ else {
+ next;
+ }
+
+ $value =~ s/\r\n/\n/gs;
+
+
+ # If Queue is 'General', we want to resolve the queue name for
+ # the object.
+
+ # This is in an eval block because $object might not exist.
+ # and might not have a Name method. But "can" won't find autoloaded
+ # items. If it fails, we don't care
+ eval {
+ my $object = $attribute . "Obj";
+ next if ($self->$object->Name eq $value);
+ };
+ next if ( $value eq $self->$attribute() );
+ my $method = "Set$attribute";
+ my ( $code, $msg ) = $self->$method($value);
+ my ($prefix) = ref($self) =~ /RT(?:.*)::(\w+)/;
+
+ # Default to $id, but use name if we can get it.
+ my $label = $self->id;
+ $label = $self->Name if (UNIVERSAL::can($self,'Name'));
+ push @results, $self->loc( "$prefix [_1]", $label ) . ': '. $msg;
+
+=for loc
+
+ "[_1] could not be set to [_2].", # loc
+ "That is already the current value", # loc
+ "No value sent to _Set!\n", # loc
+ "Illegal value for [_1]", # loc
+ "The new value has been set.", # loc
+ "No column specified", # loc
+ "Immutable field", # loc
+ "Nonexistant field?", # loc
+ "Invalid data", # loc
+ "Couldn't find row", # loc
+ "Missing a primary key?: [_1]", # loc
+ "Found Object", # loc
+
+=cut
+
+ }
+
+ return @results;
+}
+
+# {{{ Routines dealing with Links
+
+# {{{ Link Collections
+
+# {{{ sub Members
+
+=head2 Members
+
+ This returns an RT::Links object which references all the tickets
+which are 'MembersOf' this ticket
+
+=cut
+
+sub Members {
+ my $self = shift;
+ return ( $self->_Links( 'Target', 'MemberOf' ) );
+}
+
+# }}}
+
+# {{{ sub MemberOf
+
+=head2 MemberOf
+
+ This returns an RT::Links object which references all the tickets that this
+ticket is a 'MemberOf'
+
+=cut
+
+sub MemberOf {
+ my $self = shift;
+ return ( $self->_Links( 'Base', 'MemberOf' ) );
+}
+
+# }}}
+
+# {{{ RefersTo
+
+=head2 RefersTo
+
+ This returns an RT::Links object which shows all references for which this ticket is a base
+
+=cut
+
+sub RefersTo {
+ my $self = shift;
+ return ( $self->_Links( 'Base', 'RefersTo' ) );
+}
+
+# }}}
+
+# {{{ ReferredToBy
+
+=head2 ReferredToBy
+
+This returns an L<RT::Links> object which shows all references for which this ticket is a target
+
+=cut
+
+sub ReferredToBy {
+ my $self = shift;
+ return ( $self->_Links( 'Target', 'RefersTo' ) );
+}
+
+# }}}
+
+# {{{ DependedOnBy
+
+=head2 DependedOnBy
+
+ This returns an RT::Links object which references all the tickets that depend on this one
+
+=cut
+
+sub DependedOnBy {
+ my $self = shift;
+ return ( $self->_Links( 'Target', 'DependsOn' ) );
+}
+
+# }}}
+
+
+
+=head2 HasUnresolvedDependencies
+
+ Takes a paramhash of Type (default to '__any'). Returns true if
+$self->UnresolvedDependencies returns an object with one or more members
+of that type. Returns false otherwise
+
+
+=begin testing
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ($id, $trans, $msg) = $t1->Create(Subject => 'DepTest1', Queue => 'general');
+ok($id, "Created dep test 1 - $msg");
+
+my $t2 = RT::Ticket->new($RT::SystemUser);
+my ($id2, $trans, $msg2) = $t2->Create(Subject => 'DepTest2', Queue => 'general');
+ok($id2, "Created dep test 2 - $msg2");
+my $t3 = RT::Ticket->new($RT::SystemUser);
+my ($id3, $trans, $msg3) = $t3->Create(Subject => 'DepTest3', Queue => 'general', Type => 'approval');
+ok($id3, "Created dep test 3 - $msg3");
+my ($addid, $addmsg);
+ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t2->id));
+ok ($addid, $addmsg);
+ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t3->id));
+
+ok ($addid, $addmsg);
+my $link = RT::Link->new($RT::SystemUser);
+my ($rv, $msg) = $link->Load($addid);
+ok ($rv, $msg);
+ok ($link->LocalTarget == $t3->id, "Link LocalTarget is correct");
+ok ($link->LocalBase == $t1->id, "Link LocalBase is correct");
+
+ok ($t1->HasUnresolvedDependencies, "Ticket ".$t1->Id." has unresolved deps");
+ok (!$t1->HasUnresolvedDependencies( Type => 'blah' ), "Ticket ".$t1->Id." has no unresolved blahs");
+ok ($t1->HasUnresolvedDependencies( Type => 'approval' ), "Ticket ".$t1->Id." has unresolved approvals");
+ok (!$t2->HasUnresolvedDependencies, "Ticket ".$t2->Id." has no unresolved deps");
+;
+
+my ($rid, $rmsg)= $t1->Resolve();
+ok(!$rid, $rmsg);
+my ($rid2, $rmsg2) = $t2->Resolve();
+ok ($rid2, $rmsg2);
+($rid, $rmsg)= $t1->Resolve();
+ok(!$rid, $rmsg);
+my ($rid3,$rmsg3) = $t3->Resolve;
+ok ($rid3,$rmsg3);
+($rid, $rmsg)= $t1->Resolve();
+ok($rid, $rmsg);
+
+
+=end testing
+
+=cut
+
+sub HasUnresolvedDependencies {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ @_
+ );
+
+ my $deps = $self->UnresolvedDependencies;
+
+ if ($args{Type}) {
+ $deps->Limit( FIELD => 'Type',
+ OPERATOR => '=',
+ VALUE => $args{Type});
+ }
+ else {
+ $deps->IgnoreType;
+ }
+
+ if ($deps->Count > 0) {
+ return 1;
+ }
+ else {
+ return (undef);
+ }
+}
+
+
+# {{{ UnresolvedDependencies
+
+=head2 UnresolvedDependencies
+
+Returns an RT::Tickets object of tickets which this ticket depends on
+and which have a status of new, open or stalled. (That list comes from
+RT::Queue->ActiveStatusArray
+
+=cut
+
+
+sub UnresolvedDependencies {
+ my $self = shift;
+ my $deps = RT::Tickets->new($self->CurrentUser);
+
+ my @live_statuses = RT::Queue->ActiveStatusArray();
+ foreach my $status (@live_statuses) {
+ $deps->LimitStatus(VALUE => $status);
+ }
+ $deps->LimitDependedOnBy($self->Id);
+
+ return($deps);
+
+}
+
+# }}}
+
+# {{{ AllDependedOnBy
+
+=head2 AllDependedOnBy
+
+Returns an array of RT::Ticket objects which (directly or indirectly)
+depends on this ticket; takes an optional 'Type' argument in the param
+hash, which will limit returned tickets to that type, as well as cause
+tickets with that type to serve as 'leaf' nodes that stops the recursive
+dependency search.
+
+=cut
+
+sub AllDependedOnBy {
+ my $self = shift;
+ my $dep = $self->DependedOnBy;
+ my %args = (
+ Type => undef,
+ _found => {},
+ _top => 1,
+ @_
+ );
+
+ while (my $link = $dep->Next()) {
+ next unless ($link->BaseURI->IsLocal());
+ next if $args{_found}{$link->BaseObj->Id};
+
+ if (!$args{Type}) {
+ $args{_found}{$link->BaseObj->Id} = $link->BaseObj;
+ $link->BaseObj->AllDependedOnBy( %args, _top => 0 );
+ }
+ elsif ($link->BaseObj->Type eq $args{Type}) {
+ $args{_found}{$link->BaseObj->Id} = $link->BaseObj;
+ }
+ else {
+ $link->BaseObj->AllDependedOnBy( %args, _top => 0 );
+ }
+ }
+
+ if ($args{_top}) {
+ return map { $args{_found}{$_} } sort keys %{$args{_found}};
+ }
+ else {
+ return 1;
+ }
+}
+
+# }}}
+
+# {{{ DependsOn
+
+=head2 DependsOn
+
+ This returns an RT::Links object which references all the tickets that this ticket depends on
+
+=cut
+
+sub DependsOn {
+ my $self = shift;
+ return ( $self->_Links( 'Base', 'DependsOn' ) );
+}
+
+# }}}
+
+# {{{ Customers
+
+=head2 Customers
+
+ This returns an RT::Links object which references all the customers that this object is a member of.
+
+=cut
+
+sub Customers {
+ my( $self, %opt ) = @_;
+ my $Debug = $opt{'Debug'};
+
+ unless ( $self->{'Customers'} ) {
+
+ $self->{'Customers'} = $self->MemberOf->Clone;
+
+ $self->{'Customers'}->Limit(
+ FIELD => 'Target',
+ OPERATOR => 'STARTSWITH',
+ VALUE => 'freeside://freeside/cust_main/',
+ );
+ }
+
+ warn "->Customers method called on $self; returning ".
+ ref($self->{'Customers'}). ' object'
+ if $Debug;
+
+ return $self->{'Customers'};
+}
+
+# }}}
+
+# {{{ sub _Links
+
+=head2 Links DIRECTION [TYPE]
+
+Return links (L<RT::Links>) to/from this object.
+
+DIRECTION is either 'Base' or 'Target'.
+
+TYPE is a type of links to return, it can be omitted to get
+links of any type.
+
+=cut
+
+*Links = \&_Links;
+
+sub _Links {
+ my $self = shift;
+
+ #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic ---
+ #tobias meant by $f
+ my $field = shift;
+ my $type = shift || "";
+
+ unless ( $self->{"$field$type"} ) {
+ $self->{"$field$type"} = new RT::Links( $self->CurrentUser );
+ # at least to myself
+ $self->{"$field$type"}->Limit( FIELD => $field,
+ VALUE => $self->URI,
+ ENTRYAGGREGATOR => 'OR' );
+ $self->{"$field$type"}->Limit( FIELD => 'Type',
+ VALUE => $type )
+ if ($type);
+ }
+ return ( $self->{"$field$type"} );
+}
+
+# }}}
+
+# }}}
+
+# {{{ sub _AddLink
+
+=head2 _AddLink
+
+Takes a paramhash of Type and one of Base or Target. Adds that link to this object.
+
+Returns C<link id>, C<message> and C<exist> flag.
+
+
+=cut
+
+
+sub _AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+
+ # Remote_link is the URI of the object that is not this ticket
+ my $remote_link;
+ my $direction;
+
+ if ( $args{'Base'} and $args{'Target'} ) {
+ $RT::Logger->debug( "$self tried to create a link. both base and target were specified\n" );
+ return ( 0, $self->loc("Can't specifiy both base and target") );
+ }
+ elsif ( $args{'Base'} ) {
+ $args{'Target'} = $self->URI();
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+ elsif ( $args{'Target'} ) {
+ $args{'Base'} = $self->URI();
+ $remote_link = $args{'Target'};
+ $direction = 'Base';
+ }
+ else {
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ # {{{ Check if the link already exists - we don't want duplicates
+ use RT::Link;
+ my $old_link = RT::Link->new( $self->CurrentUser );
+ $old_link->LoadByParams( Base => $args{'Base'},
+ Type => $args{'Type'},
+ Target => $args{'Target'} );
+ if ( $old_link->Id ) {
+ $RT::Logger->debug("$self Somebody tried to duplicate a link");
+ return ( $old_link->id, $self->loc("Link already exists"), 1 );
+ }
+
+ # }}}
+
+
+ # Storing the link in the DB.
+ my $link = RT::Link->new( $self->CurrentUser );
+ my ($linkid, $linkmsg) = $link->Create( Target => $args{Target},
+ Base => $args{Base},
+ Type => $args{Type} );
+
+ unless ($linkid) {
+ $RT::Logger->error("Link could not be created: ".$linkmsg);
+ return ( 0, $self->loc("Link could not be created") );
+ }
+
+ my $TransString =
+ "Record $args{'Base'} $args{Type} record $args{'Target'}.";
+
+ return ( $linkid, $self->loc( "Link created ([_1])", $TransString ) );
+}
+
+# }}}
+
+# {{{ sub _DeleteLink
+
+=head2 _DeleteLink
+
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
+
+=cut
+
+sub _DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ #we want one of base and target. we don't care which
+ #but we only want _one_
+
+ my $direction;
+ my $remote_link;
+
+ if ( $args{'Base'} and $args{'Target'} ) {
+ $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target\n");
+ return ( 0, $self->loc("Can't specifiy both base and target") );
+ }
+ elsif ( $args{'Base'} ) {
+ $args{'Target'} = $self->URI();
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+ elsif ( $args{'Target'} ) {
+ $args{'Base'} = $self->URI();
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
+ else {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ my $link = new RT::Link( $self->CurrentUser );
+ $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} . "\n" );
+
+
+ $link->LoadByParams( Base=> $args{'Base'}, Type=> $args{'Type'}, Target=> $args{'Target'} );
+ #it's a real link.
+ if ( $link->id ) {
+
+ my $linkid = $link->id;
+ $link->Delete();
+
+ my $TransString = "Record $args{'Base'} no longer $args{Type} record $args{'Target'}.";
+ return ( 1, $self->loc("Link deleted ([_1])", $TransString));
+ }
+
+ #if it's not a link we can find
+ else {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $self->loc("Link not found") );
+ }
+}
+
+# }}}
+
+# }}}
+
+# {{{ Routines dealing with transactions
+
+# {{{ sub _NewTransaction
+
+=head2 _NewTransaction PARAMHASH
+
+Private function to create a new RT::Transaction object for this ticket update
+
+=cut
+
+sub _NewTransaction {
+ my $self = shift;
+ my %args = (
+ TimeTaken => undef,
+ Type => undef,
+ OldValue => undef,
+ NewValue => undef,
+ OldReference => undef,
+ NewReference => undef,
+ ReferenceType => undef,
+ Data => undef,
+ Field => undef,
+ MIMEObj => undef,
+ ActivateScrips => 1,
+ CommitScrips => 1,
+ @_
+ );
+
+ my $old_ref = $args{'OldReference'};
+ my $new_ref = $args{'NewReference'};
+ my $ref_type = $args{'ReferenceType'};
+ if ($old_ref or $new_ref) {
+ $ref_type ||= ref($old_ref) || ref($new_ref);
+ if (!$ref_type) {
+ $RT::Logger->error("Reference type not specified for transaction");
+ return;
+ }
+ $old_ref = $old_ref->Id if ref($old_ref);
+ $new_ref = $new_ref->Id if ref($new_ref);
+ }
+
+ require RT::Transaction;
+ my $trans = new RT::Transaction( $self->CurrentUser );
+ my ( $transaction, $msg ) = $trans->Create(
+ ObjectId => $self->Id,
+ ObjectType => ref($self),
+ TimeTaken => $args{'TimeTaken'},
+ Type => $args{'Type'},
+ Data => $args{'Data'},
+ Field => $args{'Field'},
+ NewValue => $args{'NewValue'},
+ OldValue => $args{'OldValue'},
+ NewReference => $new_ref,
+ OldReference => $old_ref,
+ ReferenceType => $ref_type,
+ MIMEObj => $args{'MIMEObj'},
+ ActivateScrips => $args{'ActivateScrips'},
+ CommitScrips => $args{'CommitScrips'},
+ );
+
+ # Rationalize the object since we may have done things to it during the caching.
+ $self->Load($self->Id);
+
+ $RT::Logger->warning($msg) unless $transaction;
+
+ $self->_SetLastUpdated;
+
+ if ( defined $args{'TimeTaken'} and $self->can('_UpdateTimeTaken')) {
+ $self->_UpdateTimeTaken( $args{'TimeTaken'} );
+ }
+ if ( $RT::UseTransactionBatch and $transaction ) {
+ push @{$self->{_TransactionBatch}}, $trans if $args{'CommitScrips'};
+ }
+ return ( $transaction, $msg, $trans );
+}
+
+# }}}
+
+# {{{ sub Transactions
+
+=head2 Transactions
+
+ Returns an RT::Transactions object of all transactions on this record object
+
+=cut
+
+sub Transactions {
+ my $self = shift;
+
+ use RT::Transactions;
+ my $transactions = RT::Transactions->new( $self->CurrentUser );
+
+ #If the user has no rights, return an empty object
+ $transactions->Limit(
+ FIELD => 'ObjectId',
+ VALUE => $self->id,
+ );
+ $transactions->Limit(
+ FIELD => 'ObjectType',
+ VALUE => ref($self),
+ );
+
+ return ($transactions);
+}
+
+# }}}
+# }}}
+#
+# {{{ Routines dealing with custom fields
+
+sub CustomFields {
+ my $self = shift;
+ my $cfs = RT::CustomFields->new( $self->CurrentUser );
+
+ # XXX handle multiple types properly
+ $cfs->LimitToLookupType( $self->CustomFieldLookupType );
+ $cfs->LimitToGlobalOrObjectId(
+ $self->_LookupId( $self->CustomFieldLookupType ) );
+
+ return $cfs;
+}
+
+# TODO: This _only_ works for RT::Class classes. it doesn't work, for example, for RT::FM classes.
+
+sub _LookupId {
+ my $self = shift;
+ my $lookup = shift;
+ my @classes = ($lookup =~ /RT::(\w+)-/g);
+
+ my $object = $self;
+ foreach my $class (reverse @classes) {
+ my $method = "${class}Obj";
+ $object = $object->$method;
+ }
+
+ return $object->Id;
+}
+
+
+=head2 CustomFieldLookupType
+
+Returns the path RT uses to figure out which custom fields apply to this object.
+
+=cut
+
+sub CustomFieldLookupType {
+ my $self = shift;
+ return ref($self);
+}
+
+#TODO Deprecated API. Destroy in 3.6
+sub _LookupTypes {
+ my $self = shift;
+ $RT::Logger->warning("_LookupTypes call is deprecated at (". join(":",caller)."). Replace with CustomFieldLookupType");
+
+ return($self->CustomFieldLookupType);
+
+}
+
+# {{{ AddCustomFieldValue
+
+=head2 AddCustomFieldValue { Field => FIELD, Value => VALUE }
+
+VALUE should be a string.
+FIELD can be a CustomField object OR a CustomField ID.
+
+
+Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field,
+deletes the old value.
+If VALUE is not a valid value for the custom field, returns
+(0, 'Error message' ) otherwise, returns (1, 'Success Message')
+
+=cut
+
+sub AddCustomFieldValue {
+ my $self = shift;
+ $self->_AddCustomFieldValue(@_);
+}
+
+sub _AddCustomFieldValue {
+ my $self = shift;
+ my %args = (
+ Field => undef,
+ Value => undef,
+ RecordTransaction => 1,
+ @_
+ );
+
+ my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'});
+
+ unless ( $cf->Id ) {
+ return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) );
+ }
+
+ my $OCFs = $self->CustomFields;
+ $OCFs->Limit( FIELD => 'id', VALUE => $cf->Id );
+ unless ( $OCFs->Count ) {
+ return (
+ 0,
+ $self->loc(
+ "Custom field [_1] does not apply to this object",
+ $args{'Field'}
+ )
+ );
+ }
+ # Load up a ObjectCustomFieldValues object for this custom field and this ticket
+ my $values = $cf->ValuesForObject($self);
+
+ unless ( $cf->ValidateValue( $args{'Value'} ) ) {
+ return ( 0, $self->loc("Invalid value for custom field") );
+ }
+
+ # If the custom field only accepts a certain # of values, delete the existing
+ # value and record a "changed from foo to bar" transaction
+ unless ( $cf->UnlimitedValues) {
+
+ # We need to whack any old values here. In most cases, the custom field should
+ # only have one value to delete. In the pathalogical case, this custom field
+ # used to be a multiple and we have many values to whack....
+ my $cf_values = $values->Count;
+
+ if ( $cf_values > $cf->MaxValues ) {
+ my $i = 0; #We want to delete all but the max we can currently have , so we can then
+ # execute the same code to "change" the value from old to new
+ while ( my $value = $values->Next ) {
+ $i++;
+ if ( $i < $cf_values ) {
+ my ( $val, $msg ) = $cf->DeleteValueForObject(
+ Object => $self,
+ Content => $value->Content
+ );
+ unless ($val) {
+ return ( 0, $msg );
+ }
+ my ( $TransactionId, $Msg, $TransactionObj ) =
+ $self->_NewTransaction(
+ Type => 'CustomField',
+ Field => $cf->Id,
+ OldReference => $value,
+ );
+ }
+ }
+ $values->RedoSearch if $i; # redo search if have deleted at least one value
+ }
+
+ my ( $old_value, $old_content );
+ if ( $old_value = $values->First ) {
+ $old_content = $old_value->Content();
+ return (1) if( $old_content eq $args{'Value'} && $old_value->LargeContent eq $args{'LargeContent'});;
+ }
+
+ my ( $new_value_id, $value_msg ) = $cf->AddValueForObject(
+ Object => $self,
+ Content => $args{'Value'},
+ LargeContent => $args{'LargeContent'},
+ ContentType => $args{'ContentType'},
+ );
+
+ unless ($new_value_id) {
+ return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) );
+ }
+
+ my $new_value = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
+ $new_value->Load($new_value_id);
+
+ # now that adding the new value was successful, delete the old one
+ if ($old_value) {
+ my ( $val, $msg ) = $old_value->Delete();
+ unless ($val) {
+ return ( 0, $msg );
+ }
+ }
+
+ if ( $args{'RecordTransaction'} ) {
+ my ( $TransactionId, $Msg, $TransactionObj ) =
+ $self->_NewTransaction(
+ Type => 'CustomField',
+ Field => $cf->Id,
+ OldReference => $old_value,
+ NewReference => $new_value,
+ );
+ }
+
+ if ( $old_value eq '' ) {
+ return ( 1, $self->loc( "[_1] [_2] added", $cf->Name, $new_value->Content ));
+ }
+ elsif ( $new_value->Content eq '' ) {
+ return ( 1,
+ $self->loc( "[_1] [_2] deleted", $cf->Name, $old_value->Content ) );
+ }
+ else {
+ return ( 1, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_value->Content));
+ }
+
+ }
+
+ # otherwise, just add a new value and record "new value added"
+ else {
+ my ($new_value_id, $value_msg) = $cf->AddValueForObject(
+ Object => $self,
+ Content => $args{'Value'},
+ LargeContent => $args{'LargeContent'},
+ ContentType => $args{'ContentType'},
+ );
+
+ unless ($new_value_id) {
+ return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) );
+ }
+ if ( $args{'RecordTransaction'} ) {
+ my ( $TransactionId, $Msg, $TransactionObj ) =
+ $self->_NewTransaction(
+ Type => 'CustomField',
+ Field => $cf->Id,
+ NewReference => $new_value_id,
+ ReferenceType => 'RT::ObjectCustomFieldValue',
+ );
+ unless ($TransactionId) {
+ return ( 0,
+ $self->loc( "Couldn't create a transaction: [_1]", $Msg ) );
+ }
+ }
+ return ( 1, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name));
+ }
+
+}
+
+# }}}
+
+# {{{ DeleteCustomFieldValue
+
+=head2 DeleteCustomFieldValue { Field => FIELD, Value => VALUE }
+
+Deletes VALUE as a value of CustomField FIELD.
+
+VALUE can be a string, a CustomFieldValue or a ObjectCustomFieldValue.
+
+If VALUE is not a valid value for the custom field, returns
+(0, 'Error message' ) otherwise, returns (1, 'Success Message')
+
+=cut
+
+sub DeleteCustomFieldValue {
+ my $self = shift;
+ my %args = (
+ Field => undef,
+ Value => undef,
+ ValueId => undef,
+ @_
+ );
+
+ my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'});
+
+ unless ( $cf->Id ) {
+ return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) );
+ }
+ my ( $val, $msg ) = $cf->DeleteValueForObject(
+ Object => $self,
+ Id => $args{'ValueId'},
+ Content => $args{'Value'},
+ );
+ unless ($val) {
+ return ( 0, $msg );
+ }
+ my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction(
+ Type => 'CustomField',
+ Field => $cf->Id,
+ OldReference => $val,
+ ReferenceType => 'RT::ObjectCustomFieldValue',
+ );
+ unless ($TransactionId) {
+ return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $Msg ) );
+ }
+
+ return (
+ $TransactionId,
+ $self->loc(
+ "[_1] is no longer a value for custom field [_2]",
+ $TransactionObj->OldValue, $cf->Name
+ )
+ );
+}
+
+# }}}
+
+# {{{ FirstCustomFieldValue
+
+=head2 FirstCustomFieldValue FIELD
+
+Return the content of the first value of CustomField FIELD for this ticket
+Takes a field id or name
+
+=cut
+
+sub FirstCustomFieldValue {
+ my $self = shift;
+ my $field = shift;
+ my $values = $self->CustomFieldValues($field);
+ if ($values->First) {
+ return $values->First->Content;
+ } else {
+ return undef;
+ }
+
+}
+
+
+
+# {{{ CustomFieldValues
+
+=head2 CustomFieldValues FIELD
+
+Return a ObjectCustomFieldValues object of all values of the CustomField whose
+id or Name is FIELD for this record.
+
+Returns an RT::ObjectCustomFieldValues object
+
+=cut
+
+sub CustomFieldValues {
+ my $self = shift;
+ my $field = shift;
+
+ if ($field) {
+ my $cf = $self->LoadCustomFieldByIdentifier($field);
+
+ # we were asked to search on a custom field we couldn't fine
+ unless ( $cf->id ) {
+ return RT::ObjectCustomFieldValues->new( $self->CurrentUser );
+ }
+ return ( $cf->ValuesForObject($self) );
+ }
+
+ # we're not limiting to a specific custom field;
+ my $ocfs = RT::ObjectCustomFieldValues->new( $self->CurrentUser );
+ $ocfs->LimitToObject($self);
+ return $ocfs;
+
+}
+
+=head2 CustomField IDENTIFER
+
+Find the custom field has id or name IDENTIFIER for this object.
+
+If no valid field is found, returns an empty RT::CustomField object.
+
+=cut
+
+sub LoadCustomFieldByIdentifier {
+ my $self = shift;
+ my $field = shift;
+
+ my $cf = RT::CustomField->new($self->CurrentUser);
+
+ if ( UNIVERSAL::isa( $field, "RT::CustomField" ) ) {
+ $cf->LoadById( $field->id );
+ }
+ elsif ($field =~ /^\d+$/) {
+ $cf = RT::CustomField->new($self->CurrentUser);
+ $cf->Load($field);
+ } else {
+
+ my $cfs = $self->CustomFields($self->CurrentUser);
+ $cfs->Limit(FIELD => 'Name', VALUE => $field, CASESENSITIVE => 0);
+ $cf = $cfs->First || RT::CustomField->new($self->CurrentUser);
+ }
+ return $cf;
+}
+
+
+# }}}
+
+# }}}
+
+# }}}
+
+sub BasicColumns {
+}
+
+sub WikiBase {
+ return $RT::WebPath. "/index.html?q=";
+}
+
+eval "require RT::Record_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Record_Vendor.pm});
+eval "require RT::Record_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Record_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Reminders.pm b/rt/lib/RT/Reminders.pm
new file mode 100644
index 0000000..f99fbf7
--- /dev/null
+++ b/rt/lib/RT/Reminders.pm
@@ -0,0 +1,167 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Reminders;
+
+use base qw/RT::Base/;
+
+our $REMINDER_QUEUE = 'General';
+
+
+sub new {
+ my $class = shift;
+ my $self = {};
+ bless $self, $class;
+ $self->CurrentUser(@_);
+ return($self);
+}
+
+
+sub Ticket {
+ my $self = shift;
+ $self->{'_ticket'} = shift if (@_);
+ return ($self->{'_ticket'});
+}
+
+sub TicketObj {
+ my $self = shift;
+ unless ($self->{'_ticketobj'}) {
+ $self->{'_ticketobj'} = RT::Ticket->new($self->CurrentUser);
+ $self->{'_ticketobj'}->Load($self->Ticket);
+ }
+ return $self->{'_ticketobj'};
+}
+
+
+=head2 Collection
+
+Returns an RT::Tickets object containing reminders for this object's "Ticket"
+
+=cut
+
+sub Collection {
+ my $self = shift;
+ my $col = RT::Tickets->new($self->CurrentUser);
+
+ my $query = 'Queue = "'. $self->TicketObj->QueueObj->Name .'" AND Type = "reminder"';
+ $query .= ' AND RefersTo = "'.$self->Ticket.'"';
+
+ $col->FromSQL($query);
+
+ return($col);
+}
+
+=head2 Add
+
+Add a reminder for this ticket.
+
+Takes
+
+ Subject
+ Owner
+ Due
+
+
+=cut
+
+
+sub Add {
+ my $self = shift;
+ my %args = ( Subject => undef,
+ Owner => undef,
+ Due => undef,
+ @_);
+
+ my $reminder = RT::Ticket->new($self->CurrentUser);
+ $reminder->Create( Subject => $args{'Subject'},
+ Owner => $args{'Owner'},
+ Due => $args{'Due'},
+ RefersTo => $self->Ticket,
+ Type => 'reminder',
+ Queue => $self->TicketObj->Queue,
+
+ );
+ $self->TicketObj->_NewTransaction(Type => 'AddReminder',
+ Field => 'RT::Ticket',
+ NewValue => $reminder->id);
+
+
+}
+
+
+sub Open {
+ my $self = shift;
+ my $reminder = shift;
+
+ $reminder->SetStatus('open');
+ $self->TicketObj->_NewTransaction(Type => 'OpenReminder',
+ Field => 'RT::Ticket',
+ NewValue => $reminder->id);
+}
+
+
+sub Resolve {
+ my $self = shift;
+ my $reminder = shift;
+ $reminder->SetStatus('resolved');
+ $self->TicketObj->_NewTransaction(Type => 'ResolveReminder',
+ Field => 'RT::Ticket',
+ NewValue => $reminder->id);
+}
+
+ eval "require RT::Reminders_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Reminders_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Reminders_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Reminders_Local.pm}) {
+ die $@;
+ };
+
+
+1;
diff --git a/rt/lib/RT/Report/Tickets.pm b/rt/lib/RT/Report/Tickets.pm
new file mode 100644
index 0000000..97e27d3
--- /dev/null
+++ b/rt/lib/RT/Report/Tickets.pm
@@ -0,0 +1,451 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Report::Tickets;
+
+use base qw/RT::Tickets/;
+use RT::Report::Tickets::Entry;
+
+use strict;
+use warnings;
+
+sub Groupings {
+ my $self = shift;
+ my %args = (@_);
+ my @fields = qw(
+ Owner
+ Status
+ Queue
+ DueDaily
+ DueMonthly
+ DueAnnually
+ ResolvedDaily
+ ResolvedMonthly
+ ResolvedAnnually
+ CreatedDaily
+ CreatedMonthly
+ CreatedAnnually
+ LastUpdatedDaily
+ LastUpdatedMonthly
+ LastUpdatedAnnually
+ StartedDaily
+ StartedMonthly
+ StartedAnnually
+ StartsDaily
+ StartsMonthly
+ StartsAnnually
+ );
+
+ @fields = map {$_, $_} @fields;
+
+ my $queues = $args{'Queues'};
+ if ( !$queues && $args{'Query'} ) {
+ my @actions;
+ my $tree;
+ # XXX TODO REFACTOR OUT
+ $self->_ParseQuery( $args{'Query'}, \$tree, \@actions );
+ $queues = $tree->GetReferencedQueues;
+ }
+
+ if ( $queues ) {
+ my $CustomFields = RT::CustomFields->new( $self->CurrentUser );
+ foreach my $id (keys %$queues) {
+ my $queue = RT::Queue->new( $self->CurrentUser );
+ $queue->Load($id);
+ unless ($queue->id) {
+ # XXX TODO: This ancient code dates from a former developer
+ # we have no idea what it means or why cfqueues are so encoded.
+ $id =~ s/^.'*(.*).'*$/$1/;
+ $queue->Load($id);
+ }
+ $CustomFields->LimitToQueue($queue->Id);
+ }
+ $CustomFields->LimitToGlobal;
+ while ( my $CustomField = $CustomFields->Next ) {
+ push @fields, "Custom field '". $CustomField->Name ."'", "CF.{". $CustomField->id ."}";
+ }
+ }
+ return @fields;
+}
+
+sub Label {
+ my $self = shift;
+ my $field = shift;
+ if ( $field =~ /^(?:CF|CustomField)\.{(.*)}$/ ) {
+ my $cf = $1;
+ return $self->CurrentUser->loc( "Custom field '[_1]'", $cf ) if $cf =~ /\D/;
+ my $obj = RT::CustomField->new( $self->CurrentUser );
+ $obj->Load( $cf );
+ return $self->CurrentUser->loc( "Custom field '[_1]'", $obj->Name );
+ }
+ return $self->CurrentUser->loc($field);
+}
+
+sub GroupBy {
+ my $self = shift;
+ my %args = ref $_[0]? %{ $_[0] }: (@_);
+
+ $self->{'_group_by_field'} = $args{'FIELD'};
+ %args = $self->_FieldToFunction( %args );
+
+ $self->SUPER::GroupBy( \%args );
+}
+
+sub Column {
+ my $self = shift;
+ my %args = (@_);
+
+ if ( $args{'FIELD'} && !$args{'FUNCTION'} ) {
+ %args = $self->_FieldToFunction( %args );
+ }
+
+ return $self->SUPER::Column( %args );
+}
+
+=head2 _DoSearch
+
+Subclass _DoSearch from our parent so we can go through and add in empty
+columns if it makes sense
+
+=cut
+
+sub _DoSearch {
+ my $self = shift;
+ $self->SUPER::_DoSearch( @_ );
+ $self->AddEmptyRows;
+}
+
+=head2 _FieldToFunction FIELD
+
+Returns a tuple of the field or a database function to allow grouping on that
+field.
+
+=cut
+
+sub _FieldToFunction {
+ my $self = shift;
+ my %args = (@_);
+
+ my $field = $args{'FIELD'};
+
+ if ($field =~ /^(.*)(Daily|Monthly|Annually)$/) {
+ my ($field, $grouping) = ($1, $2);
+ if ( $grouping =~ /Daily/ ) {
+ $args{'FUNCTION'} = "SUBSTR($field,1,10)";
+ }
+ elsif ( $grouping =~ /Monthly/ ) {
+ $args{'FUNCTION'} = "SUBSTR($field,1,7)";
+ }
+ elsif ( $grouping =~ /Annually/ ) {
+ $args{'FUNCTION'} = "SUBSTR($field,1,4)";
+ }
+ } elsif ( $field =~ /^(?:CF|CustomField)\.{(.*)}$/ ) { #XXX: use CFDecipher method
+ my $cf_name = $1;
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->Load($cf_name);
+ unless ( $cf->id ) {
+ $RT::Logger->error("Couldn't load CustomField #$cf_name");
+ } else {
+ my ($ticket_cf_alias, $cf_alias) = $self->_CustomFieldJoin($cf->id, $cf->id, $cf_name);
+ @args{qw(ALIAS FIELD)} = ($ticket_cf_alias, 'Content');
+ }
+ }
+ return %args;
+}
+
+
+# Override the AddRecord from DBI::SearchBuilder::Unique. id isn't id here
+# wedon't want to disambiguate all the items with a count of 1.
+sub AddRecord {
+ my $self = shift;
+ my $record = shift;
+ push @{$self->{'items'}}, $record;
+ $self->{'rows'}++;
+}
+
+1;
+
+
+
+# Gotta skip over RT::Tickets->Next, since it does all sorts of crazy magic we
+# don't want.
+sub Next {
+ my $self = shift;
+ $self->RT::SearchBuilder::Next(@_);
+
+}
+
+sub NewItem {
+ my $self = shift;
+ return RT::Report::Tickets::Entry->new($RT::SystemUser); # $self->CurrentUser);
+}
+
+
+=head2 AddEmptyRows
+
+If we're grouping on a criterion we know how to add zero-value rows
+for, do that.
+
+=cut
+
+sub AddEmptyRows {
+ my $self = shift;
+ if ( $self->{'_group_by_field'} eq 'Status' ) {
+ my %has = map { $_->__Value('Status') => 1 } @{ $self->ItemsArrayRef || [] };
+
+ foreach my $status ( grep !$has{$_}, RT::Queue->new($self->CurrentUser)->StatusArray ) {
+
+ my $record = $self->NewItem;
+ $record->LoadFromHash( {
+ id => 0,
+ status => $status
+ } );
+ $self->AddRecord($record);
+ }
+ }
+}
+
+
+# XXX TODO: this code cut and pasted from html/Search/Build.html
+# This has already been improved (But not backported) in 3.7
+#
+# This code is hacky, evil and wrong. But it's end of lifed from day one and is
+# less likely to destabilize the codebase than the full refactoring it should get.
+use Regexp::Common qw /delimited/;
+
+# States
+use constant VALUE => 1;
+use constant AGGREG => 2;
+use constant OP => 4;
+use constant PAREN => 8;
+use constant KEYWORD => 16;
+
+sub _match {
+
+ # Case insensitive equality
+ my ( $y, $x ) = @_;
+ return 1 if $x =~ /^$y$/i;
+
+ # return 1 if ((lc $x) eq (lc $y)); # Why isnt this equiv?
+ return 0;
+}
+
+sub _ParseQuery {
+ my $self = shift;
+ my $string = shift;
+ my $tree = shift;
+ my @actions = shift;
+ my $want = KEYWORD | PAREN;
+ my $last = undef;
+
+ my $depth = 1;
+
+ # make a tree root
+ use RT::Interface::Web::QueryBuilder::Tree;
+ $$tree = RT::Interface::Web::QueryBuilder::Tree->new;
+ my $root = RT::Interface::Web::QueryBuilder::Tree->new( 'AND', $$tree );
+ my $lastnode = $root;
+ my $parentnode = $root;
+
+ # get the FIELDS from Tickets_Overlay
+ my $tickets = new RT::Tickets( $self->CurrentUser );
+ my %FIELDS = %{ $tickets->FIELDS };
+
+ # Lower Case version of FIELDS, for case insensitivity
+ my %lcfields = map { ( lc($_) => $_ ) } ( keys %FIELDS );
+
+ my @tokens = qw[VALUE AGGREG OP PAREN KEYWORD];
+ my $re_aggreg = qr[(?i:AND|OR)];
+ my $re_value = qr[$RE{delimited}{-delim=>qq{\'\"}}|\d+];
+ my $re_keyword = qr[$RE{delimited}{-delim=>qq{\'\"}}|(?:\{|\}|\w|\.)+];
+ my $re_op =
+ qr[=|!=|>=|<=|>|<|(?i:IS NOT)|(?i:IS)|(?i:NOT LIKE)|(?i:LIKE)]
+ ; # long to short
+ my $re_paren = qr'\(|\)';
+
+ # assume that $ea is AND if it is not set
+ my ( $ea, $key, $op, $value ) = ( "AND", "", "", "" );
+
+ # order of matches in the RE is important.. op should come early,
+ # because it has spaces in it. otherwise "NOT LIKE" might be parsed
+ # as a keyword or value.
+
+ while (
+ $string =~ /(
+ $re_aggreg
+ |$re_op
+ |$re_keyword
+ |$re_value
+ |$re_paren
+ )/igx
+ )
+ {
+ my $val = $1;
+ my $current = 0;
+
+ # Highest priority is last
+ $current = OP if _match( $re_op, $val );
+ $current = VALUE if _match( $re_value, $val );
+ $current = KEYWORD
+ if _match( $re_keyword, $val ) && ( $want & KEYWORD );
+ $current = AGGREG if _match( $re_aggreg, $val );
+ $current = PAREN if _match( $re_paren, $val );
+
+ unless ( $current && $want & $current ) {
+
+ # Error
+ # FIXME: I will only print out the highest $want value
+ my $token = $tokens[ ( ( log $want ) / ( log 2 ) ) ];
+ push @actions,
+ [
+ $self->CurrentUser->loc(
+"current: $current, want $want, Error near ->$val<- expecting a "
+ . $token
+ . " in '$string'\n"
+ ),
+ -1
+ ];
+ }
+
+ # State Machine:
+ my $parentdepth = $depth;
+
+ # Parens are highest priority
+ if ( $current & PAREN ) {
+ if ( $val eq "(" ) {
+ $depth++;
+
+ # make a new node that the clauses can be children of
+ $parentnode = RT::Interface::Web::QueryBuilder::Tree->new( $ea, $parentnode );
+ }
+ else {
+ $depth--;
+ $parentnode = $parentnode->getParent();
+ $lastnode = $parentnode;
+ }
+
+ $want = KEYWORD | PAREN | AGGREG;
+ }
+ elsif ( $current & AGGREG ) {
+ $ea = $val;
+ $want = KEYWORD | PAREN;
+ }
+ elsif ( $current & KEYWORD ) {
+ $key = $val;
+ $want = OP;
+ }
+ elsif ( $current & OP ) {
+ $op = $val;
+ $want = VALUE;
+ }
+ elsif ( $current & VALUE ) {
+ $value = $val;
+
+ # Remove surrounding quotes from $key, $val
+ # (in future, simplify as for($key,$val) { action on $_ })
+ if ( $key =~ /$RE{delimited}{-delim=>qq{\'\"}}/ ) {
+ substr( $key, 0, 1 ) = "";
+ substr( $key, -1, 1 ) = "";
+ }
+ if ( $val =~ /$RE{delimited}{-delim=>qq{\'\"}}/ ) {
+ substr( $val, 0, 1 ) = "";
+ substr( $val, -1, 1 ) = "";
+ }
+
+ # Unescape escaped characters
+ $key =~ s!\\(.)!$1!g;
+ $val =~ s!\\(.)!$1!g;
+
+ my $class;
+ if ( exists $lcfields{ lc $key } ) {
+ $key = $lcfields{ lc $key };
+ $class = $FIELDS{$key}->[0];
+ }
+ if ( $class ne 'INT' ) {
+ $val = "'$val'";
+ }
+
+ push @actions, [ $self->CurrentUser->loc("Unknown field: [_1]", $key), -1 ] unless $class;
+
+ $want = PAREN | AGGREG;
+ }
+ else {
+ push @actions, [ $self->CurrentUser->loc("I'm lost"), -1 ];
+ }
+
+ if ( $current & VALUE ) {
+ if ( $key =~ /^CF./ ) {
+ $key = "'" . $key . "'";
+ }
+ my $clause = {
+ Key => $key,
+ Op => $op,
+ Value => $val
+ };
+
+ # explicity add a child to it
+ $lastnode = RT::Interface::Web::QueryBuilder::Tree->new( $clause, $parentnode );
+ $lastnode->getParent()->setNodeValue($ea);
+
+ ( $ea, $key, $op, $value ) = ( "", "", "", "" );
+ }
+
+ $last = $current;
+ } # while
+
+ push @actions, [ $self->CurrentUser->loc("Incomplete query"), -1 ]
+ unless ( ( $want | PAREN ) || ( $want | KEYWORD ) );
+
+ push @actions, [ $self->CurrentUser->loc("Incomplete Query"), -1 ]
+ unless ( $last && ( $last | PAREN ) || ( $last || VALUE ) );
+
+ # This will never happen, because the parser will complain
+ push @actions, [ $self->CurrentUser->loc("Mismatched parentheses"), -1 ]
+ unless $depth == 1;
+};
+
+1;
diff --git a/rt/lib/RT/Report/Tickets/Entry.pm b/rt/lib/RT/Report/Tickets/Entry.pm
new file mode 100644
index 0000000..f6ee22c
--- /dev/null
+++ b/rt/lib/RT/Report/Tickets/Entry.pm
@@ -0,0 +1,55 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Report::Tickets::Entry;
+use base qw/RT::Record/;
+
+# XXX TODO: how the heck do we acl a report?
+sub CurrentUserHasRight {1}
+
+
+1;
diff --git a/rt/lib/RT/SavedSearch.pm b/rt/lib/RT/SavedSearch.pm
new file mode 100644
index 0000000..9cebe33
--- /dev/null
+++ b/rt/lib/RT/SavedSearch.pm
@@ -0,0 +1,348 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::SavedSearch - an API for saving and retrieving search form values.
+
+=head1 SYNOPSIS
+
+ use RT::SavedSearch
+
+=head1 DESCRIPTION
+
+ SavedSearch is an object that can belong to either an RT::User or an
+ RT::Group. It consists of an ID, a description, and a number of
+ search parameters.
+
+=head1 METHODS
+
+=begin testing
+
+use_ok(RT::SavedSearch);
+
+# Real tests are in lib/t/20savedsearch.t
+
+=end testing
+
+=cut
+
+package RT::SavedSearch;
+
+use RT::Base;
+use RT::Attribute;
+
+use strict;
+use warnings;
+use base qw/RT::Base/;
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ $self->{'Id'} = 0;
+ bless ($self, $class);
+ $self->CurrentUser(@_);
+ return $self;
+}
+
+=head2 Load
+
+Takes a privacy specification, an object ID, and a search ID. Loads
+the given search ID if it belongs to the stated user or group.
+Returns a tuple of status and message, where status is true on
+success.
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my ($privacy, $id) = @_;
+ my $object = $self->_GetObject($privacy);
+
+ if ($object) {
+ $self->{'Attribute'} = $object->Attributes->WithId($id);
+ if ($self->{'Attribute'}->Id) {
+ $self->{'Id'} = $self->{'Attribute'}->Id;
+ $self->{'Privacy'} = $privacy;
+ $self->{'Type'} = $self->{'Attribute'}->SubValue('SearchType');
+ return (1, $self->loc("Loaded search [_1]", $self->Name));
+ } else {
+ $RT::Logger->error("Could not load attribute " . $id
+ . " for object " . $privacy);
+ return (0, $self->loc("Search attribute load failure"));
+ }
+ } else {
+ $RT::Logger->warning("Could not load object $privacy when loading search");
+ return (0, $self->loc("Could not load object for [_1]", $privacy));
+ }
+
+}
+
+=head2 Save
+
+Takes a privacy, an optional type, a name, and a hashref containing the
+search parameters. Saves the given parameters to the appropriate user/
+group object, and loads the resulting search. Returns a tuple of status
+and message, where status is true on success. Defaults are:
+ Privacy: undef
+ Type: Ticket
+ Name: "new search"
+ SearchParams: (empty hash)
+
+=cut
+
+sub Save {
+ my $self = shift;
+ my %args = ('Privacy' => 'RT::User-' . $self->CurrentUser->Id,
+ 'Type' => 'Ticket',
+ 'Name' => 'new search',
+ 'SearchParams' => {},
+ @_);
+ my $privacy = $args{'Privacy'};
+ my $type = $args{'Type'};
+ my $name = $args{'Name'};
+ my %params = %{$args{'SearchParams'}};
+
+ $params{'SearchType'} = $type;
+ my $object = $self->_GetObject($privacy);
+
+ return (0, $self->loc("Failed to load object for [_1]", $privacy))
+ unless $object;
+
+ if ( $object->isa('RT::System') ) {
+ return ( 0, $self->loc("No permission to save system-wide searches") )
+ unless $self->CurrentUser->HasRight(
+ Object => $RT::System,
+ Right => 'SuperUser'
+ );
+ }
+
+ my ( $att_id, $att_msg ) = $object->AddAttribute(
+ 'Name' => 'SavedSearch',
+ 'Description' => $name,
+ 'Content' => \%params
+ );
+ if ($att_id) {
+ $self->{'Attribute'} = $object->Attributes->WithId($att_id);
+ $self->{'Id'} = $att_id;
+ $self->{'Privacy'} = $privacy;
+ $self->{'Type'} = $type;
+ return ( 1, $self->loc( "Saved search [_1]", $name ) );
+ }
+ else {
+ $RT::Logger->error("SavedSearch save failure: $att_msg");
+ return ( 0, $self->loc("Failed to create search attribute") );
+ }
+}
+
+=head2 Update
+
+Updates the parameters of an existing search. Takes the arguments
+"Name" and "SearchParams"; SearchParams should be a hashref containing
+the new parameters of the search. If Name is not specified, the name
+will not be changed.
+
+=cut
+
+sub Update {
+ my $self = shift;
+ my %args = ('Name' => '',
+ 'SearchParams' => {},
+ @_);
+
+ return(0, $self->loc("No search loaded")) unless $self->Id;
+ return(0, $self->loc("Could not load search attribute"))
+ unless $self->{'Attribute'}->Id;
+ my ($status, $msg) = $self->{'Attribute'}->SetSubValues(%{$args{'SearchParams'}});
+ if ($status && $args{'Name'}) {
+ ($status, $msg) = $self->{'Attribute'}->SetDescription($args{'Name'});
+ }
+ return ($status, $self->loc("Search update: [_1]", $msg));
+}
+
+=head2 Delete
+
+Deletes the existing search. Returns a tuple of status and message,
+where status is true upon success.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+ my ($status, $msg) = $self->{'Attribute'}->Delete;
+ if ($status) {
+ return (1, $self->loc("Deleted search"));
+ } else {
+ return (0, $self->loc("Delete failed: [_1]", $msg));
+ }
+}
+
+
+### Accessor methods
+
+=head2 Name
+
+Returns the name of the search.
+
+=cut
+
+sub Name {
+ my $self = shift;
+ return unless ref($self->{'Attribute'}) eq 'RT::Attribute';
+ return $self->{'Attribute'}->Description();
+}
+
+=head2 GetParameter
+
+Returns the given named parameter of the search, e.g. 'Query', 'Format'.
+
+=cut
+
+sub GetParameter {
+ my $self = shift;
+ my $param = shift;
+ return unless ref($self->{'Attribute'}) eq 'RT::Attribute';
+ return $self->{'Attribute'}->SubValue($param);
+}
+
+=head2 Id
+
+Returns the numerical id of this search.
+
+=cut
+
+sub Id {
+ my $self = shift;
+ return $self->{'Id'};
+}
+
+=head2 Privacy
+
+Returns the principal object to whom this search belongs, in a string
+"<class>-<id>", e.g. "RT::Group-16".
+
+=cut
+
+sub Privacy {
+ my $self = shift;
+ return $self->{'Privacy'};
+}
+
+=head2 Type
+
+Returns the type of this search, e.g. 'Ticket'. Useful for denoting the
+saved searches that are relevant to a particular search page.
+
+=cut
+
+sub Type {
+ my $self = shift;
+ return $self->{'Type'};
+}
+
+### Internal methods
+
+sub _load_privacy_object {
+ my ($self, $obj_type, $obj_id) = @_;
+ if ( $obj_type eq 'RT::User' && $obj_id == $self->CurrentUser->Id) {
+ return $self->CurrentUser->UserObj;
+ }
+ elsif ($obj_type eq 'RT::Group') {
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->Load($obj_id);
+ return $group;
+ }
+ elsif ($obj_type eq 'RT::System') {
+ return RT::System->new($self->CurrentUser);
+ }
+
+ $RT::Logger->error("Tried to load a search belonging to an $obj_type, which is neither a user nor a group");
+ return undef;
+}
+
+# _GetObject: helper routine to load the correct object whose parameters
+# have been passed.
+
+sub _GetObject {
+ my $self = shift;
+ my $privacy = shift;
+
+ my ($obj_type, $obj_id) = split(/\-/, $privacy);
+
+ my $object = $self->_load_privacy_object($obj_type, $obj_id);
+
+ unless (ref($object) eq $obj_type) {
+ $RT::Logger->error("Could not load object of type $obj_type with ID $obj_id");
+ return undef;
+ }
+
+ # Do not allow the loading of a user object other than the current
+ # user, or of a group object of which the current user is not a member.
+
+ if ($obj_type eq 'RT::User'
+ && $object->Id != $self->CurrentUser->UserObj->Id()) {
+ $RT::Logger->debug("Permission denied for user other than self");
+ return undef;
+ }
+ if ($obj_type eq 'RT::Group' &&
+ !$object->HasMemberRecursively($self->CurrentUser->PrincipalObj)) {
+ $RT::Logger->debug("Permission denied, ".$self->CurrentUser->Name.
+ " is not a member of group");
+ return undef;
+ }
+
+ return $object;
+}
+
+eval "require RT::SavedSearch_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/SavedSearch_Vendor.pm});
+eval "require RT::SavedSearch_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/SavedSearch_Local.pm});
+
+1;
diff --git a/rt/lib/RT/SavedSearches.pm b/rt/lib/RT/SavedSearches.pm
new file mode 100644
index 0000000..0b5ac97
--- /dev/null
+++ b/rt/lib/RT/SavedSearches.pm
@@ -0,0 +1,190 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::SavedSearches - a pseudo-collection for SavedSearch objects.
+
+=head1 SYNOPSIS
+
+ use RT::SavedSearch
+
+=head1 DESCRIPTION
+
+ SavedSearches is an object consisting of a number of SavedSearch objects.
+ It works more or less like a DBIx::SearchBuilder collection, although it
+ is not.
+
+=head1 METHODS
+
+=begin testing
+
+use_ok(RT::SavedSearches);
+
+# The real tests are in lib/t/20savedsearch.t
+
+=end testing
+
+=cut
+
+package RT::SavedSearches;
+
+use RT::Base;
+use RT::SavedSearch;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw/RT::Base/;
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->CurrentUser(@_);
+ $self->{'idx'} = 0;
+ $self->{'objects'} = [];
+ return $self;
+}
+
+=head2 LimitToPrivacy
+
+Takes two argumets: a privacy string, of the format "<class>-<id>", as
+produced by RT::SavedSearch::Privacy(); and a type string, as produced
+by RT::SavedSearch::Type(). The SavedSearches object will load the
+searches belonging to that user or group that are of the type
+specified. If no type is specified, all the searches belonging to the
+user/group will be loaded. Repeated calls to the same object should DTRT.
+
+=cut
+
+sub LimitToPrivacy {
+ my $self = shift;
+ my $privacy = shift;
+ my $type = shift;
+
+ my $object = $self->_GetObject($privacy);
+
+ if ($object) {
+ $self->{'objects'} = [];
+ my @search_atts = $object->Attributes->Named('SavedSearch');
+ foreach my $att (@search_atts) {
+ my $search = RT::SavedSearch->new($self->CurrentUser);
+ $search->Load($privacy, $att->Id);
+ next if $type && $search->Type ne $type;
+ push(@{$self->{'objects'}}, $search);
+ }
+ } else {
+ $RT::Logger->error("Could not load object $privacy");
+ }
+}
+
+### Accessor methods
+
+=head2 Next
+
+Returns the next object in the collection.
+
+=cut
+
+sub Next {
+ my $self = shift;
+ my $search = $self->{'objects'}->[$self->{'idx'}];
+ if ($search) {
+ $self->{'idx'}++;
+ } else {
+ # We have run out of objects; reset the counter.
+ $self->{'idx'} = 0;
+ }
+ return $search;
+}
+
+=head2 Count
+
+Returns the number of search objects found.
+
+=cut
+
+sub Count {
+ my $self = shift;
+ return scalar @{$self->{'objects'}};
+}
+
+### Internal methods
+
+# _GetObject: helper routine to load the correct object whose parameters
+# have been passed.
+
+sub _GetObject {
+ my $self = shift;
+ my $privacy = shift;
+
+ return RT::SavedSearch->new($self->CurrentUser)->_GetObject($privacy);
+}
+
+### Internal methods
+
+# _PrivacyObjects: returns a list of objects that can be used to load saved searches from.
+
+sub _PrivacyObjects {
+ my $self = shift;
+ my $CurrentUser = $self->CurrentUser;
+
+ my $groups = RT::Groups->new($CurrentUser);
+ $groups->LimitToUserDefinedGroups;
+ $groups->WithMember( PrincipalId => $CurrentUser->Id,
+ Recursively => 1 );
+
+ return ( $CurrentUser->UserObj, @{ $groups->ItemsArrayRef() } );
+}
+
+eval "require RT::SavedSearches_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/SavedSearches_Vendor.pm});
+eval "require RT::SavedSearches_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/SavedSearches_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Scrip.pm b/rt/lib/RT/Scrip.pm
new file mode 100755
index 0000000..8b24a64
--- /dev/null
+++ b/rt/lib/RT/Scrip.pm
@@ -0,0 +1,524 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Scrip
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Scrip;
+use RT::Record;
+use RT::Queue;
+use RT::Template;
+use RT::ScripCondition;
+use RT::ScripAction;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Scrips');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(255) 'Description'.
+ int(11) 'ScripCondition'.
+ int(11) 'ScripAction'.
+ text 'ConditionRules'.
+ text 'ActionRules'.
+ text 'CustomIsApplicableCode'.
+ text 'CustomPrepareCode'.
+ text 'CustomCommitCode'.
+ varchar(32) 'Stage'.
+ int(11) 'Queue'.
+ int(11) 'Template'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Description => '',
+ ScripCondition => '0',
+ ScripAction => '0',
+ ConditionRules => '',
+ ActionRules => '',
+ CustomIsApplicableCode => '',
+ CustomPrepareCode => '',
+ CustomCommitCode => '',
+ Stage => '',
+ Queue => '0',
+ Template => '0',
+
+ @_);
+ $self->SUPER::Create(
+ Description => $args{'Description'},
+ ScripCondition => $args{'ScripCondition'},
+ ScripAction => $args{'ScripAction'},
+ ConditionRules => $args{'ConditionRules'},
+ ActionRules => $args{'ActionRules'},
+ CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
+ CustomPrepareCode => $args{'CustomPrepareCode'},
+ CustomCommitCode => $args{'CustomCommitCode'},
+ Stage => $args{'Stage'},
+ Queue => $args{'Queue'},
+ Template => $args{'Template'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 ScripCondition
+
+Returns the current value of ScripCondition.
+(In the database, ScripCondition is stored as int(11).)
+
+
+
+=head2 SetScripCondition VALUE
+
+
+Set ScripCondition to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ScripCondition will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 ScripConditionObj
+
+Returns the ScripCondition Object which has the id returned by ScripCondition
+
+
+=cut
+
+sub ScripConditionObj {
+ my $self = shift;
+ my $ScripCondition = RT::ScripCondition->new($self->CurrentUser);
+ $ScripCondition->Load($self->__Value('ScripCondition'));
+ return($ScripCondition);
+}
+
+=head2 ScripAction
+
+Returns the current value of ScripAction.
+(In the database, ScripAction is stored as int(11).)
+
+
+
+=head2 SetScripAction VALUE
+
+
+Set ScripAction to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ScripAction will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 ScripActionObj
+
+Returns the ScripAction Object which has the id returned by ScripAction
+
+
+=cut
+
+sub ScripActionObj {
+ my $self = shift;
+ my $ScripAction = RT::ScripAction->new($self->CurrentUser);
+ $ScripAction->Load($self->__Value('ScripAction'));
+ return($ScripAction);
+}
+
+=head2 ConditionRules
+
+Returns the current value of ConditionRules.
+(In the database, ConditionRules is stored as text.)
+
+
+
+=head2 SetConditionRules VALUE
+
+
+Set ConditionRules to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ConditionRules will be stored as a text.)
+
+
+=cut
+
+
+=head2 ActionRules
+
+Returns the current value of ActionRules.
+(In the database, ActionRules is stored as text.)
+
+
+
+=head2 SetActionRules VALUE
+
+
+Set ActionRules to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ActionRules will be stored as a text.)
+
+
+=cut
+
+
+=head2 CustomIsApplicableCode
+
+Returns the current value of CustomIsApplicableCode.
+(In the database, CustomIsApplicableCode is stored as text.)
+
+
+
+=head2 SetCustomIsApplicableCode VALUE
+
+
+Set CustomIsApplicableCode to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CustomIsApplicableCode will be stored as a text.)
+
+
+=cut
+
+
+=head2 CustomPrepareCode
+
+Returns the current value of CustomPrepareCode.
+(In the database, CustomPrepareCode is stored as text.)
+
+
+
+=head2 SetCustomPrepareCode VALUE
+
+
+Set CustomPrepareCode to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CustomPrepareCode will be stored as a text.)
+
+
+=cut
+
+
+=head2 CustomCommitCode
+
+Returns the current value of CustomCommitCode.
+(In the database, CustomCommitCode is stored as text.)
+
+
+
+=head2 SetCustomCommitCode VALUE
+
+
+Set CustomCommitCode to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, CustomCommitCode will be stored as a text.)
+
+
+=cut
+
+
+=head2 Stage
+
+Returns the current value of Stage.
+(In the database, Stage is stored as varchar(32).)
+
+
+
+=head2 SetStage VALUE
+
+
+Set Stage to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Stage will be stored as a varchar(32).)
+
+
+=cut
+
+
+=head2 Queue
+
+Returns the current value of Queue.
+(In the database, Queue is stored as int(11).)
+
+
+
+=head2 SetQueue VALUE
+
+
+Set Queue to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Queue will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 QueueObj
+
+Returns the Queue Object which has the id returned by Queue
+
+
+=cut
+
+sub QueueObj {
+ my $self = shift;
+ my $Queue = RT::Queue->new($self->CurrentUser);
+ $Queue->Load($self->__Value('Queue'));
+ return($Queue);
+}
+
+=head2 Template
+
+Returns the current value of Template.
+(In the database, Template is stored as int(11).)
+
+
+
+=head2 SetTemplate VALUE
+
+
+Set Template to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Template will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 TemplateObj
+
+Returns the Template Object which has the id returned by Template
+
+
+=cut
+
+sub TemplateObj {
+ my $self = shift;
+ my $Template = RT::Template->new($self->CurrentUser);
+ $Template->Load($self->__Value('Template'));
+ return($Template);
+}
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ ScripCondition =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ ScripAction =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ ConditionRules =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ ActionRules =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ CustomIsApplicableCode =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ CustomPrepareCode =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ CustomCommitCode =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ Stage =>
+ {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => ''},
+ Queue =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Template =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::Scrip_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Scrip_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Scrip_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Scrip_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Scrip_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Scrip_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Scrip_Overlay, RT::Scrip_Vendor, RT::Scrip_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ScripAction.pm b/rt/lib/RT/ScripAction.pm
new file mode 100755
index 0000000..643977b
--- /dev/null
+++ b/rt/lib/RT/ScripAction.pm
@@ -0,0 +1,303 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::ScripAction
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::ScripAction;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('ScripActions');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(255) 'Description'.
+ varchar(60) 'ExecModule'.
+ varchar(255) 'Argument'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Description => '',
+ ExecModule => '',
+ Argument => '',
+
+ @_);
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ ExecModule => $args{'ExecModule'},
+ Argument => $args{'Argument'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 ExecModule
+
+Returns the current value of ExecModule.
+(In the database, ExecModule is stored as varchar(60).)
+
+
+
+=head2 SetExecModule VALUE
+
+
+Set ExecModule to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ExecModule will be stored as a varchar(60).)
+
+
+=cut
+
+
+=head2 Argument
+
+Returns the current value of Argument.
+(In the database, Argument is stored as varchar(255).)
+
+
+
+=head2 SetArgument VALUE
+
+
+Set Argument to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Argument will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ ExecModule =>
+ {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ Argument =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::ScripAction_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripAction_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripAction_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripAction_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripAction_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripAction_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ScripAction_Overlay, RT::ScripAction_Vendor, RT::ScripAction_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ScripAction_Overlay.pm b/rt/lib/RT/ScripAction_Overlay.pm
new file mode 100644
index 0000000..4b93c6c
--- /dev/null
+++ b/rt/lib/RT/ScripAction_Overlay.pm
@@ -0,0 +1,285 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::ScripAction - RT Action object
+
+=head1 SYNOPSIS
+
+ use RT::ScripAction;
+
+
+=head1 DESCRIPTION
+
+This module should never be called directly by client code. it's an internal module which
+should only be accessed through exported APIs in other modules.
+
+
+=begin testing
+
+ok (require RT::ScripAction);
+
+=end testing
+
+=head1 METHODS
+
+=cut
+
+
+package RT::ScripAction;
+
+use strict;
+no warnings qw(redefine);
+use RT::Template;
+
+# {{{ sub _Accessible
+sub _Accessible {
+ my $self = shift;
+ my %Cols = ( Name => 'read',
+ Description => 'read',
+ ExecModule => 'read',
+ Argument => 'read',
+ Creator => 'read/auto',
+ Created => 'read/auto',
+ LastUpdatedBy => 'read/auto',
+ LastUpdated => 'read/auto'
+ );
+ return($self->SUPER::_Accessible(@_, %Cols));
+}
+# }}}
+
+# {{{ sub Create
+
+=head2 Create
+
+Takes a hash. Creates a new Action entry. should be better
+documented.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ #TODO check these args and do smart things.
+ return($self->SUPER::Create(@_));
+}
+# }}}
+
+# {{{ sub Delete
+sub Delete {
+ my $self = shift;
+
+ return (0, "ScripAction->Delete not implemented");
+}
+# }}}
+
+# {{{ sub Load
+
+=head2 Load IDENTIFIER
+
+Loads an action by its Name.
+
+Returns: Id, Error Message
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift;
+
+ if (!$identifier) {
+ return (0, $self->loc('Input error'));
+ }
+
+ if ($identifier !~ /\D/) {
+ $self->SUPER::Load($identifier);
+ }
+ else {
+ $self->LoadByCol('Name', $identifier);
+
+ }
+
+ if (@_) {
+ # Set the template Id to the passed in template
+ my $template = shift;
+
+ $self->{'Template'} = $template;
+ }
+ return ($self->Id, ($self->loc('[_1] ScripAction loaded', $self->Id)));
+}
+# }}}
+
+# {{{ sub LoadAction
+
+=head2 LoadAction HASH
+
+ Takes a hash consisting of TicketObj and TransactionObj. Loads an RT::Action:: module.
+
+=cut
+
+sub LoadAction {
+ my $self = shift;
+ my %args = ( TransactionObj => undef,
+ TicketObj => undef,
+ @_ );
+
+ $self->{_TicketObj} = $args{TicketObj};
+
+ #TODO: Put this in an eval
+ $self->ExecModule =~ /^(\w+)$/;
+ my $module = $1;
+ my $type = "RT::Action::". $module;
+
+ eval "require $type" || die "Require of $type failed.\n$@\n";
+
+ $self->{'Action'} = $type->new ( Argument => $self->Argument,
+ CurrentUser => $self->CurrentUser,
+ ScripActionObj => $self,
+ ScripObj => $args{'ScripObj'},
+ TemplateObj => $self->TemplateObj,
+ TicketObj => $args{'TicketObj'},
+ TransactionObj => $args{'TransactionObj'},
+ );
+}
+# }}}
+
+# {{{ sub TemplateObj
+
+=head2 TemplateObj
+
+Return this action's template object
+
+TODO: Why are we not using the Scrip's template object?
+
+
+=cut
+
+sub TemplateObj {
+ my $self = shift;
+ return undef unless $self->{Template};
+ if ( !$self->{'TemplateObj'} ) {
+ $self->{'TemplateObj'} = RT::Template->new( $self->CurrentUser );
+ $self->{'TemplateObj'}->LoadById( $self->{'Template'} );
+
+ if ( ( $self->{'TemplateObj'}->__Value('Queue') == 0 )
+ && $self->{'_TicketObj'} ) {
+ my $tmptemplate = RT::Template->new( $self->CurrentUser );
+ my ( $ok, $err ) = $tmptemplate->LoadQueueTemplate(
+ Queue => $self->{'_TicketObj'}->QueueObj->id,
+ Name => $self->{'TemplateObj'}->Name);
+
+ if ( $tmptemplate->id ) {
+ # found the queue-specific template with the same name
+ $self->{'TemplateObj'} = $tmptemplate;
+ }
+ }
+
+ }
+
+ return ( $self->{'TemplateObj'} );
+}
+# }}}
+
+# The following methods call the action object
+
+# {{{ sub Prepare
+
+sub Prepare {
+ my $self = shift;
+ $self->{_Message_ID} = 0;
+ return ($self->Action->Prepare());
+
+}
+# }}}
+
+# {{{ sub Commit
+sub Commit {
+ my $self = shift;
+ return($self->Action->Commit());
+
+
+}
+# }}}
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->Action->Describe());
+
+}
+# }}}
+
+=head2 Action
+
+Return the actual RT::Action object for this scrip.
+
+=cut
+
+sub Action {
+ my $self = shift;
+ return ($self->{'Action'});
+}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self=shift;
+ $self->{'_TicketObj'} = undef;
+ $self->{'Action'} = undef;
+ $self->{'TemplateObj'} = undef;
+}
+# }}}
+
+=head2 TODO
+
+Between this, RT::Scrip and RT::Action::*, we need to be able to get rid of a
+class. This just reeks of too much complexity -- jesse
+
+=cut
+
+1;
+
+
diff --git a/rt/lib/RT/ScripActions.pm b/rt/lib/RT/ScripActions.pm
new file mode 100755
index 0000000..d32eb7e
--- /dev/null
+++ b/rt/lib/RT/ScripActions.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::ScripActions -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::ScripActions
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::ScripActions;
+
+use RT::SearchBuilder;
+use RT::ScripAction;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'ScripActions';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::ScripAction item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::ScripAction->new($self->CurrentUser));
+}
+
+ eval "require RT::ScripActions_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripActions_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripActions_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripActions_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripActions_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripActions_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ScripActions_Overlay, RT::ScripActions_Vendor, RT::ScripActions_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ScripActions_Overlay.pm b/rt/lib/RT/ScripActions_Overlay.pm
new file mode 100644
index 0000000..fd69e93
--- /dev/null
+++ b/rt/lib/RT/ScripActions_Overlay.pm
@@ -0,0 +1,114 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::ScripActions - Collection of Action objects
+
+=head1 SYNOPSIS
+
+ use RT::ScripActions;
+
+
+=head1 DESCRIPTION
+
+
+=begin testing
+
+ok (require RT::ScripActions);
+
+=end testing
+
+=head1 METHODS
+
+=cut
+
+
+package RT::ScripActions;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = "ScripActions";
+ $self->{'primary_key'} = "id";
+ return ( $self->SUPER::_Init(@_));
+}
+# }}}
+
+# {{{ sub LimitToType
+sub LimitToType {
+ my $self = shift;
+ my $type = shift;
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Type',
+ VALUE => "$type")
+ if defined $type;
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Type',
+ VALUE => "Correspond")
+ if $type eq "Create";
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Type',
+ VALUE => 'any');
+
+}
+# }}}
+
+# {{{ sub NewItem
+sub NewItem {
+ my $self = shift;
+ return(RT::ScripAction->new($self->CurrentUser));
+
+}
+# }}}
+
+
+1;
+
diff --git a/rt/lib/RT/ScripCondition.pm b/rt/lib/RT/ScripCondition.pm
new file mode 100755
index 0000000..5910797
--- /dev/null
+++ b/rt/lib/RT/ScripCondition.pm
@@ -0,0 +1,326 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::ScripCondition
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::ScripCondition;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('ScripConditions');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(255) 'Description'.
+ varchar(60) 'ExecModule'.
+ varchar(255) 'Argument'.
+ varchar(60) 'ApplicableTransTypes'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Description => '',
+ ExecModule => '',
+ Argument => '',
+ ApplicableTransTypes => '',
+
+ @_);
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ ExecModule => $args{'ExecModule'},
+ Argument => $args{'Argument'},
+ ApplicableTransTypes => $args{'ApplicableTransTypes'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 ExecModule
+
+Returns the current value of ExecModule.
+(In the database, ExecModule is stored as varchar(60).)
+
+
+
+=head2 SetExecModule VALUE
+
+
+Set ExecModule to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ExecModule will be stored as a varchar(60).)
+
+
+=cut
+
+
+=head2 Argument
+
+Returns the current value of Argument.
+(In the database, Argument is stored as varchar(255).)
+
+
+
+=head2 SetArgument VALUE
+
+
+Set Argument to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Argument will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 ApplicableTransTypes
+
+Returns the current value of ApplicableTransTypes.
+(In the database, ApplicableTransTypes is stored as varchar(60).)
+
+
+
+=head2 SetApplicableTransTypes VALUE
+
+
+Set ApplicableTransTypes to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ApplicableTransTypes will be stored as a varchar(60).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ ExecModule =>
+ {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ Argument =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ ApplicableTransTypes =>
+ {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::ScripCondition_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripCondition_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripCondition_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripCondition_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripCondition_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripCondition_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ScripCondition_Overlay, RT::ScripCondition_Vendor, RT::ScripCondition_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ScripCondition_Overlay.pm b/rt/lib/RT/ScripCondition_Overlay.pm
new file mode 100644
index 0000000..8032c3b
--- /dev/null
+++ b/rt/lib/RT/ScripCondition_Overlay.pm
@@ -0,0 +1,238 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::ScripCondition - RT scrip conditional
+
+=head1 SYNOPSIS
+
+ use RT::ScripCondition;
+
+
+=head1 DESCRIPTION
+
+This module should never be called directly by client code. it's an internal module which
+should only be accessed through exported APIs in other modules.
+
+
+=begin testing
+
+ok (require RT::ScripCondition);
+
+=end testing
+
+=head1 METHODS
+
+=cut
+
+
+package RT::ScripCondition;
+
+use strict;
+no warnings qw(redefine);
+
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = "ScripConditions";
+ return ($self->SUPER::_Init(@_));
+}
+# }}}
+
+# {{{ sub _Accessible
+sub _Accessible {
+ my $self = shift;
+ my %Cols = ( Name => 'read',
+ Description => 'read',
+ ApplicableTransTypes => 'read',
+ ExecModule => 'read',
+ Argument => 'read',
+ Creator => 'read/auto',
+ Created => 'read/auto',
+ LastUpdatedBy => 'read/auto',
+ LastUpdated => 'read/auto'
+ );
+ return($self->SUPER::_Accessible(@_, %Cols));
+}
+# }}}
+
+# {{{ sub Create
+
+=head2 Create
+
+ Takes a hash. Creates a new Condition entry.
+ should be better documented.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ return($self->SUPER::Create(@_));
+}
+# }}}
+
+# {{{ sub Delete
+
+=head2 Delete
+
+No API available for deleting things just yet.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+ return(0, $self->loc('Unimplemented'));
+}
+# }}}
+
+# {{{ sub Load
+
+=head2 Load IDENTIFIER
+
+Loads a condition takes a name or ScripCondition id.
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift;
+
+ unless (defined $identifier) {
+ return (undef);
+ }
+
+ if ($identifier !~ /\D/) {
+ return ($self->SUPER::LoadById($identifier));
+ }
+ else {
+ return ($self->LoadByCol('Name', $identifier));
+ }
+}
+# }}}
+
+# {{{ sub LoadCondition
+
+=head2 LoadCondition HASH
+
+takes a hash which has the following elements: TransactionObj and TicketObj.
+Loads the Condition module in question.
+
+=cut
+
+
+sub LoadCondition {
+ my $self = shift;
+ my %args = ( TransactionObj => undef,
+ TicketObj => undef,
+ @_ );
+
+ #TODO: Put this in an eval
+ $self->ExecModule =~ /^(\w+)$/;
+ my $module = $1;
+ my $type = "RT::Condition::". $module;
+
+ eval "require $type" || die "Require of $type failed.\n$@\n";
+
+ $self->{'Condition'} = $type->new ( 'ScripConditionObj' => $self,
+ 'TicketObj' => $args{'TicketObj'},
+ 'ScripObj' => $args{'ScripObj'},
+ 'TransactionObj' => $args{'TransactionObj'},
+ 'Argument' => $self->Argument,
+ 'ApplicableTransTypes' => $self->ApplicableTransTypes,
+ CurrentUser => $self->CurrentUser
+ );
+}
+# }}}
+
+# {{{ The following methods call the Condition object
+
+
+# {{{ sub Describe
+
+=head2 Describe
+
+Helper method to call the condition module\'s Describe method.
+
+=cut
+
+sub Describe {
+ my $self = shift;
+ return ($self->{'Condition'}->Describe());
+
+}
+# }}}
+
+# {{{ sub IsApplicable
+
+=head2 IsApplicable
+
+Helper method to call the condition module\'s IsApplicable method.
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ return ($self->{'Condition'}->IsApplicable());
+
+}
+# }}}
+
+# }}}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self=shift;
+ $self->{'Condition'} = undef;
+}
+# }}}
+
+
+1;
+
+
diff --git a/rt/lib/RT/ScripConditions.pm b/rt/lib/RT/ScripConditions.pm
new file mode 100755
index 0000000..a8a3919
--- /dev/null
+++ b/rt/lib/RT/ScripConditions.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::ScripConditions -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::ScripConditions
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::ScripConditions;
+
+use RT::SearchBuilder;
+use RT::ScripCondition;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'ScripConditions';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::ScripCondition item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::ScripCondition->new($self->CurrentUser));
+}
+
+ eval "require RT::ScripConditions_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripConditions_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripConditions_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripConditions_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::ScripConditions_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/ScripConditions_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::ScripConditions_Overlay, RT::ScripConditions_Vendor, RT::ScripConditions_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/ScripConditions_Overlay.pm b/rt/lib/RT/ScripConditions_Overlay.pm
new file mode 100644
index 0000000..53cabd0
--- /dev/null
+++ b/rt/lib/RT/ScripConditions_Overlay.pm
@@ -0,0 +1,114 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::ScripConditions - Collection of Action objects
+
+=head1 SYNOPSIS
+
+ use RT::ScripConditions;
+
+
+=head1 DESCRIPTION
+
+
+
+=begin testing
+
+ok (require RT::ScripConditions);
+
+=end testing
+
+=head1 METHODS
+
+=cut
+
+
+package RT::ScripConditions;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = "ScripConditions";
+ $self->{'primary_key'} = "id";
+ return ( $self->SUPER::_Init(@_));
+}
+# }}}
+
+# {{{ sub LimitToType
+sub LimitToType {
+ my $self = shift;
+ my $type = shift;
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Type',
+ VALUE => "$type")
+ if defined $type;
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Type',
+ VALUE => "Correspond")
+ if $type eq "Create";
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Type',
+ VALUE => 'any');
+
+}
+# }}}
+
+# {{{ sub NewItem
+sub NewItem {
+ my $self = shift;
+ return(RT::ScripCondition->new($self->CurrentUser));
+}
+# }}}
+
+
+1;
+
diff --git a/rt/lib/RT/Scrip_Overlay.pm b/rt/lib/RT/Scrip_Overlay.pm
new file mode 100644
index 0000000..7d1a697
--- /dev/null
+++ b/rt/lib/RT/Scrip_Overlay.pm
@@ -0,0 +1,618 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Scrip - an RT Scrip object
+
+=head1 SYNOPSIS
+
+ use RT::Scrip;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok (require RT::Scrip);
+
+
+my $q = RT::Queue->new($RT::SystemUser);
+$q->Create(Name => 'ScripTest');
+ok($q->Id, "Created a scriptest queue");
+
+my $s1 = RT::Scrip->new($RT::SystemUser);
+my ($val, $msg) =$s1->Create( Queue => $q->Id,
+ ScripAction => 'User Defined',
+ ScripCondition => 'User Defined',
+ CustomIsApplicableCode => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}',
+ CustomPrepareCode => 'return 1',
+ CustomCommitCode => '$self->TicketObj->SetPriority("87");',
+ Template => 'Blank'
+ );
+ok($val,$msg);
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id,
+ Subject => "hair on fire",
+ );
+ok($tv, $tm);
+
+ok ($ticket->Priority == '87', "Ticket priority is set right");
+
+
+my $ticket2 = RT::Ticket->new($RT::SystemUser);
+my ($t2v,$t2tv,$t2m) = $ticket2->Create(Queue => $q->Id,
+ Subject => "hair in water",
+ );
+ok($t2v, $t2m);
+
+ok ($ticket2->Priority != '87', "Ticket priority is set right");
+
+
+=end testing
+
+=cut
+
+
+package RT::Scrip;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub Create
+
+=head2 Create
+
+Creates a new entry in the Scrips table. Takes a paramhash with:
+
+ Queue => 0,
+ Description => undef,
+ Template => undef,
+ ScripAction => undef,
+ ScripCondition => undef,
+ CustomPrepareCode => undef,
+ CustomCommitCode => undef,
+ CustomIsApplicableCode => undef,
+
+
+
+
+Returns (retval, msg);
+retval is 0 for failure or scrip id. msg is a textual description of what happened.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Queue => 0,
+ Template => 0, # name or id
+ ScripAction => 0, # name or id
+ ScripCondition => 0, # name or id
+ Stage => 'TransactionCreate',
+ Description => undef,
+ CustomPrepareCode => undef,
+ CustomCommitCode => undef,
+ CustomIsApplicableCode => undef,
+ @_
+ );
+
+ unless ( $args{'Queue'} ) {
+ unless ( $self->CurrentUser->HasRight( Object => $RT::System,
+ Right => 'ModifyScrips' )
+ ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $args{'Queue'} = 0; # avoid undef sneaking in
+ }
+ else {
+ my $QueueObj = RT::Queue->new( $self->CurrentUser );
+ $QueueObj->Load( $args{'Queue'} );
+ unless ( $QueueObj->id ) {
+ return ( 0, $self->loc('Invalid queue') );
+ }
+ unless ( $QueueObj->CurrentUserHasRight('ModifyScrips') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ $args{'Queue'} = $QueueObj->id();
+ }
+
+ #TODO +++ validate input
+
+ require RT::ScripAction;
+ return ( 0, $self->loc("Action is mandatory argument") )
+ unless $args{'ScripAction'};
+ my $action = RT::ScripAction->new( $self->CurrentUser );
+ $action->Load( $args{'ScripAction'} );
+ return ( 0, $self->loc( "Action [_1] not found", $args{'ScripAction'} ) )
+ unless $action->Id;
+
+ require RT::Template;
+ return ( 0, $self->loc("Template is mandatory argument") )
+ unless $args{'Template'};
+ my $template = RT::Template->new( $self->CurrentUser );
+ $template->Load( $args{'Template'} );
+ return ( 0, $self->loc('Template not found') )
+ unless $template->Id;
+
+ require RT::ScripCondition;
+ return ( 0, $self->loc("Condition is mandatory argument") )
+ unless $args{'ScripCondition'};
+ my $condition = RT::ScripCondition->new( $self->CurrentUser );
+ $condition->Load( $args{'ScripCondition'} );
+ return ( 0, $self->loc('Condition not found') )
+ unless $condition->Id;
+
+ my ( $id, $msg ) = $self->SUPER::Create(
+ Queue => $args{'Queue'},
+ Template => $template->Id,
+ ScripCondition => $condition->id,
+ Stage => $args{'Stage'},
+ ScripAction => $action->Id,
+ Description => $args{'Description'},
+ CustomPrepareCode => $args{'CustomPrepareCode'},
+ CustomCommitCode => $args{'CustomCommitCode'},
+ CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
+ );
+ if ( $id ) {
+ return ( $id, $self->loc('Scrip Created') );
+ }
+ else {
+ return ( $id, $msg );
+ }
+}
+
+# }}}
+
+# {{{ sub Delete
+
+=head2 Delete
+
+Delete this object
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ return ( $self->SUPER::Delete(@_) );
+}
+
+# }}}
+
+# {{{ sub QueueObj
+
+=head2 QueueObj
+
+Retuns an RT::Queue object with this Scrip\'s queue
+
+=cut
+
+sub QueueObj {
+ my $self = shift;
+
+ if ( !$self->{'QueueObj'} ) {
+ require RT::Queue;
+ $self->{'QueueObj'} = RT::Queue->new( $self->CurrentUser );
+ $self->{'QueueObj'}->Load( $self->__Value('Queue') );
+ }
+ return ( $self->{'QueueObj'} );
+}
+
+# }}}
+
+# {{{ sub ActionObj
+
+=head2 ActionObj
+
+Retuns an RT::Action object with this Scrip\'s Action
+
+=cut
+
+sub ActionObj {
+ my $self = shift;
+
+ unless ( defined $self->{'ScripActionObj'} ) {
+ require RT::ScripAction;
+
+ $self->{'ScripActionObj'} = RT::ScripAction->new( $self->CurrentUser );
+
+ #TODO: why are we loading Actions with templates like this.
+ # two separate methods might make more sense
+ $self->{'ScripActionObj'}->Load( $self->ScripAction, $self->Template );
+ }
+ return ( $self->{'ScripActionObj'} );
+}
+
+# }}}
+
+# {{{ sub ConditionObj
+
+=head2 ConditionObj
+
+Retuns an RT::ScripCondition object with this Scrip's IsApplicable
+
+=cut
+
+sub ConditionObj {
+ my $self = shift;
+
+ unless ( defined $self->{'ScripConditionObj'} ) {
+ require RT::ScripCondition;
+ $self->{'ScripConditionObj'} =
+ RT::ScripCondition->new( $self->CurrentUser );
+ if ( $self->ScripCondition ) {
+ $self->{'ScripConditionObj'}->Load( $self->ScripCondition );
+ }
+ }
+ return ( $self->{'ScripConditionObj'} );
+}
+
+# }}}
+
+# {{{ sub TemplateObj
+
+=head2 TemplateObj
+
+Retuns an RT::Template object with this Scrip\'s Template
+
+=cut
+
+sub TemplateObj {
+ my $self = shift;
+
+ unless ( defined $self->{'TemplateObj'} ) {
+ require RT::Template;
+ $self->{'TemplateObj'} = RT::Template->new( $self->CurrentUser );
+ $self->{'TemplateObj'}->Load( $self->Template );
+ }
+ return ( $self->{'TemplateObj'} );
+}
+
+# }}}
+
+# {{{ Dealing with this instance of a scrip
+
+# {{{ sub Apply
+
+=head2 Apply { TicketObj => undef, TransactionObj => undef}
+
+This method instantiates the ScripCondition and ScripAction objects for a
+single execution of this scrip. it then calls the IsApplicable method of the
+ScripCondition.
+If that succeeds, it calls the Prepare method of the
+ScripAction. If that succeeds, it calls the Commit method of the ScripAction.
+
+Usually, the ticket and transaction objects passed to this method
+should be loaded by the SuperUser role
+
+=cut
+
+
+# XXX TODO : This code appears to be obsoleted in favor of similar code in Scrips->Apply.
+# Why is this here? Is it still called?
+
+sub Apply {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ $RT::Logger->debug("Now applying scrip ".$self->Id . " for transaction ".$args{'TransactionObj'}->id);
+
+ my $ApplicableTransactionObj = $self->IsApplicable( TicketObj => $args{'TicketObj'},
+ TransactionObj => $args{'TransactionObj'} );
+ unless ( $ApplicableTransactionObj ) {
+ return undef;
+ }
+
+ if ( $ApplicableTransactionObj->id != $args{'TransactionObj'}->id ) {
+ $RT::Logger->debug("Found an applicable transaction ".$ApplicableTransactionObj->Id . " in the same batch with transaction ".$args{'TransactionObj'}->id);
+ }
+
+ #If it's applicable, prepare and commit it
+ $RT::Logger->debug("Now preparing scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
+ unless ( $self->Prepare( TicketObj => $args{'TicketObj'},
+ TransactionObj => $ApplicableTransactionObj )
+ ) {
+ return undef;
+ }
+
+ $RT::Logger->debug("Now commiting scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
+ unless ( $self->Commit( TicketObj => $args{'TicketObj'},
+ TransactionObj => $ApplicableTransactionObj)
+ ) {
+ return undef;
+ }
+
+ $RT::Logger->debug("We actually finished scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id);
+ return (1);
+
+}
+
+# }}}
+
+# {{{ sub IsApplicable
+
+=head2 IsApplicable
+
+Calls the Condition object\'s IsApplicable method
+
+Upon success, returns the applicable Transaction object.
+Otherwise, undef is returned.
+
+If the Scrip is in the TransactionCreate Stage (the usual case), only test
+the associated Transaction object to see if it is applicable.
+
+For Scrips in the TransactionBatch Stage, test all Transaction objects
+created during the Ticket object's lifetime, and returns the first one
+that is applicable.
+
+=cut
+
+sub IsApplicable {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ my $return;
+ eval {
+
+ my @Transactions;
+
+ if ( $self->Stage eq 'TransactionCreate') {
+ # Only look at our current Transaction
+ @Transactions = ( $args{'TransactionObj'} );
+ }
+ elsif ( $self->Stage eq 'TransactionBatch') {
+ # Look at all Transactions in this Batch
+ @Transactions = @{ $args{'TicketObj'}->TransactionBatch || [] };
+ }
+ else {
+ $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage );
+ return (undef);
+ }
+ my $ConditionObj = $self->ConditionObj;
+ foreach my $TransactionObj ( @Transactions ) {
+ # in TxnBatch stage we can select scrips that are not applicable to all txns
+ my $txn_type = $TransactionObj->Type;
+ next unless( $ConditionObj->ApplicableTransTypes =~ /(?:^|,)(?:Any|\Q$txn_type\E)(?:,|$)/i );
+ # Load the scrip's Condition object
+ $ConditionObj->LoadCondition(
+ ScripObj => $self,
+ TicketObj => $args{'TicketObj'},
+ TransactionObj => $TransactionObj,
+ );
+
+ if ( $ConditionObj->IsApplicable() ) {
+ # We found an application Transaction -- return it
+ $return = $TransactionObj;
+ last;
+ }
+ }
+ };
+ if ($@) {
+ $RT::Logger->error( "Scrip IsApplicable " . $self->Id . " died. - " . $@ );
+ return (undef);
+ }
+
+ return ($return);
+
+}
+
+# }}}
+
+# {{{ SUb Prepare
+
+=head2 Prepare
+
+Calls the action object's prepare method
+
+=cut
+
+sub Prepare {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ my $return;
+ eval {
+ $self->ActionObj->LoadAction( ScripObj => $self,
+ TicketObj => $args{'TicketObj'},
+ TransactionObj => $args{'TransactionObj'},
+ );
+
+ $return = $self->ActionObj->Prepare();
+ };
+ if ($@) {
+ $RT::Logger->error( "Scrip Prepare " . $self->Id . " died. - " . $@ );
+ return (undef);
+ }
+ unless ($return) {
+ }
+ return ($return);
+}
+
+# }}}
+
+# {{{ sub Commit
+
+=head2 Commit
+
+Calls the action object's commit method
+
+=cut
+
+sub Commit {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ TransactionObj => undef,
+ @_ );
+
+ my $return;
+ eval {
+ $return = $self->ActionObj->Commit();
+ };
+
+#Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
+# may have changed
+ $args{'TicketObj'}->Load( $args{'TicketObj'}->Id );
+
+ if ($@) {
+ $RT::Logger->error( "Scrip Commit " . $self->Id . " died. - " . $@ );
+ return (undef);
+ }
+
+ # Not destroying or weakening hte Action and Condition here could cause a
+ # leak
+
+ return ($return);
+}
+
+# }}}
+
+# }}}
+
+# {{{ ACL related methods
+
+# {{{ sub _Set
+
+# does an acl check and then passes off the call
+sub _Set {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
+ $RT::Logger->debug(
+ "CurrentUser can't modify Scrips for " . $self->Queue . "\n" );
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ return $self->__Set(@_);
+}
+
+# }}}
+
+# {{{ sub _Value
+# does an acl check and then passes off the call
+sub _Value {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ShowScrips') ) {
+ $RT::Logger->debug( "CurrentUser can't modify Scrips for "
+ . $self->__Value('Queue')
+ . "\n" );
+ return (undef);
+ }
+
+ return $self->__Value(@_);
+}
+
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
+=head2 CurrentUserHasRight
+
+Helper menthod for HasRight. Presets Principal to CurrentUser then
+calls HasRight.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+ return ( $self->HasRight( Principal => $self->CurrentUser->UserObj,
+ Right => $right ) );
+
+}
+
+# }}}
+
+# {{{ sub HasRight
+
+=head2 HasRight
+
+Takes a param-hash consisting of "Right" and "Principal" Principal is
+an RT::User object or an RT::CurrentUser object. "Right" is a textual
+Right string that applies to Scrips.
+
+=cut
+
+sub HasRight {
+ my $self = shift;
+ my %args = ( Right => undef,
+ Principal => undef,
+ @_ );
+
+ if ( $self->SUPER::_Value('Queue') ) {
+ return $args{'Principal'}->HasRight(
+ Right => $args{'Right'},
+ Object => $self->QueueObj
+ );
+ }
+ else {
+ return $args{'Principal'}->HasRight(
+ Object => $RT::System,
+ Right => $args{'Right'},
+ );
+ }
+}
+
+# }}}
+
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Scrips.pm b/rt/lib/RT/Scrips.pm
new file mode 100755
index 0000000..cff1a53
--- /dev/null
+++ b/rt/lib/RT/Scrips.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Scrips -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Scrips
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Scrips;
+
+use RT::SearchBuilder;
+use RT::Scrip;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Scrips';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Scrip item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Scrip->new($self->CurrentUser));
+}
+
+ eval "require RT::Scrips_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Scrips_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Scrips_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Scrips_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Scrips_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Scrips_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Scrips_Overlay, RT::Scrips_Vendor, RT::Scrips_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Scrips_Overlay.pm b/rt/lib/RT/Scrips_Overlay.pm
new file mode 100644
index 0000000..64a8437
--- /dev/null
+++ b/rt/lib/RT/Scrips_Overlay.pm
@@ -0,0 +1,371 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Scrips - a collection of RT Scrip objects
+
+=head1 SYNOPSIS
+
+ use RT::Scrips;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Scrips);
+
+=end testing
+
+=cut
+
+
+package RT::Scrips;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub LimitToQueue
+
+=head2 LimitToQueue
+
+Takes a queue id (numerical) as its only argument. Makes sure that
+Scopes it pulls out apply to this queue (or another that you've selected with
+another call to this method
+
+=cut
+
+sub LimitToQueue {
+ my $self = shift;
+ my $queue = shift;
+
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Queue',
+ VALUE => "$queue")
+ if defined $queue;
+
+}
+# }}}
+
+# {{{ sub LimitToGlobal
+
+=head2 LimitToGlobal
+
+Makes sure that
+Scopes it pulls out apply to all queues (or another that you've selected with
+another call to this method or LimitToQueue
+
+=cut
+
+
+sub LimitToGlobal {
+ my $self = shift;
+
+ $self->Limit (ENTRYAGGREGATOR => 'OR',
+ FIELD => 'Queue',
+ VALUE => 0);
+
+}
+# }}}
+
+# {{{ sub NewItem
+sub NewItem {
+ my $self = shift;
+
+ return(new RT::Scrip($self->CurrentUser));
+}
+# }}}
+
+# {{{ sub Next
+
+=head2 Next
+
+Returns the next scrip that this user can see.
+
+=cut
+
+sub Next {
+ my $self = shift;
+
+
+ my $Scrip = $self->SUPER::Next();
+ if ((defined($Scrip)) and (ref($Scrip))) {
+
+ if ($Scrip->CurrentUserHasRight('ShowScrips')) {
+ return($Scrip);
+ }
+
+ #If the user doesn't have the right to show this scrip
+ else {
+ return($self->Next());
+ }
+ }
+ #if there never was any scrip
+ else {
+ return(undef);
+ }
+
+}
+# }}}
+
+=head2 Apply
+
+Run through the relevant scrips. Scrips will run in order based on
+description. (Most common use case is to prepend a number to the description,
+forcing the scrips to run in ascending alphanumerical order.)
+
+=cut
+
+sub Apply {
+ my $self = shift;
+
+ my %args = ( TicketObj => undef,
+ Ticket => undef,
+ Transaction => undef,
+ TransactionObj => undef,
+ Stage => undef,
+ Type => undef,
+ @_ );
+
+ $self->Prepare(%args);
+ $self->Commit();
+
+}
+
+=head2 Commit
+
+Commit all of this object's prepared scrips
+
+=cut
+
+sub Commit {
+ my $self = shift;
+
+
+ foreach my $scrip (@{$self->Prepared}) {
+
+ $scrip->Commit( TicketObj => $self->{'TicketObj'},
+ TransactionObj => $self->{'TransactionObj'} );
+ }
+}
+
+
+=head2 Prepare
+
+Only prepare the scrips, returning an array of the scrips we're interested in
+in order of preparation, not execution
+
+=cut
+
+sub Prepare {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ Ticket => undef,
+ Transaction => undef,
+ TransactionObj => undef,
+ Stage => undef,
+ Type => undef,
+ @_ );
+
+ #We're really going to need a non-acled ticket for the scrips to work
+ $self->_SetupSourceObjects( TicketObj => $args{'TicketObj'},
+ Ticket => $args{'Ticket'},
+ TransactionObj => $args{'TransactionObj'},
+ Transaction => $args{'Transaction'} );
+
+
+ $self->_FindScrips( Stage => $args{'Stage'}, Type => $args{'Type'} );
+
+
+ #Iterate through each script and check it's applicability.
+ while ( my $scrip = $self->Next() ) {
+
+ next
+ unless ( $scrip->IsApplicable(
+ TicketObj => $self->{'TicketObj'},
+ TransactionObj => $self->{'TransactionObj'}
+ ) );
+
+ #If it's applicable, prepare and commit it
+ next
+ unless ( $scrip->Prepare( TicketObj => $self->{'TicketObj'},
+ TransactionObj => $self->{'TransactionObj'}
+ ) );
+ push @{$self->{'prepared_scrips'}}, $scrip;
+
+ }
+
+ return (@{$self->Prepared});
+
+};
+
+=head2 Prepared
+
+Returns an arrayref of the scrips this object has prepared
+
+
+=cut
+
+sub Prepared {
+ my $self = shift;
+ return ($self->{'prepared_scrips'} || []);
+}
+
+
+# {{{ sup _SetupSourceObjects
+
+=head2 _SetupSourceObjects { TicketObj , Ticket, Transaction, TransactionObj }
+
+Setup a ticket and transaction for this Scrip collection to work with as it runs through the
+relevant scrips. (Also to figure out which scrips apply)
+
+Returns: nothing
+
+=cut
+
+
+sub _SetupSourceObjects {
+
+ my $self = shift;
+ my %args = (
+ TicketObj => undef,
+ Ticket => undef,
+ Transaction => undef,
+ TransactionObj => undef,
+ @_ );
+
+ if ( ( $self->{'TicketObj'} = $args{'TicketObj'} ) ) {
+ $self->{'TicketObj'}->CurrentUser( $self->CurrentUser );
+ }
+ else {
+ $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser );
+ $self->{'TicketObj'}->Load( $args{'Ticket'} )
+ || $RT::Logger->err("$self couldn't load ticket $args{'Ticket'}\n");
+ }
+
+ if ( ( $self->{'TransactionObj'} = $args{'TransactionObj'} ) ) {
+ $self->{'TransactionObj'}->CurrentUser( $self->CurrentUser );
+ }
+ else {
+ $self->{'TransactionObj'} = RT::Transaction->new( $self->CurrentUser );
+ $self->{'TransactionObj'}->Load( $args{'Transaction'} )
+ || $RT::Logger->err( "$self couldn't load transaction $args{'Transaction'}\n");
+ }
+}
+
+# }}}
+
+# {{{ sub _FindScrips;
+
+=head2 _FindScrips
+
+Find only the apropriate scrips for whatever we're doing now. Order them
+by their description. (Most common use case is to prepend a number to the
+description, forcing the scrips to display and run in ascending alphanumerical
+order.)
+
+=cut
+
+sub _FindScrips {
+ my $self = shift;
+ my %args = (
+ Stage => undef,
+ Type => undef,
+ @_ );
+
+
+ $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id )
+ ; #Limit it to $Ticket->QueueObj->Id
+ $self->LimitToGlobal();
+ # or to "global"
+
+ $self->Limit( FIELD => "Stage", VALUE => $args{'Stage'} );
+
+ my $ConditionsAlias = $self->NewAlias('ScripConditions');
+
+ $self->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'ScripCondition',
+ ALIAS2 => $ConditionsAlias,
+ FIELD2 => 'id'
+ );
+
+ #We only want things where the scrip applies to this sort of transaction
+ # TransactionBatch stage can define list of transaction
+ foreach( split /\s*,\s*/, ($args{'Type'} || '') ) {
+ $self->Limit(
+ ALIAS => $ConditionsAlias,
+ FIELD => 'ApplicableTransTypes',
+ OPERATOR => 'LIKE',
+ VALUE => $_,
+ ENTRYAGGREGATOR => 'OR',
+ )
+ }
+
+ # Or where the scrip applies to any transaction
+ $self->Limit(
+ ALIAS => $ConditionsAlias,
+ FIELD => 'ApplicableTransTypes',
+ OPERATOR => 'LIKE',
+ VALUE => "Any",
+ ENTRYAGGREGATOR => 'OR',
+ );
+
+ # Promise some kind of ordering
+ $self->OrderBy( FIELD => 'description' );
+
+ $RT::Logger->debug("Found ".$self->Count. " scrips");
+}
+
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Search/ActiveTicketsInQueue.pm b/rt/lib/RT/Search/ActiveTicketsInQueue.pm
new file mode 100644
index 0000000..d75cd1f
--- /dev/null
+++ b/rt/lib/RT/Search/ActiveTicketsInQueue.pm
@@ -0,0 +1,102 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Search::ActiveTicketsInQueue
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Find all active tickets in the queue named in the argument passed in
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Search::Generic);
+
+=end testing
+
+
+=cut
+
+package RT::Search::ActiveTicketsInQueue;
+
+use strict;
+use base qw(RT::Search::Generic);
+
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->loc("No description for [_1]", ref $self));
+}
+# }}}
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+
+ $self->TicketsObj->LimitQueue(VALUE => $self->Argument);
+
+ foreach my $status (RT::Queue->ActiveStatusArray()) {
+ $self->TicketsObj->LimitStatus(VALUE => $status);
+ }
+
+ return(1);
+}
+# }}}
+
+eval "require RT::Search::ActiveTicketsInQueue_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/ActiveTicketsInQueue_Vendor.pm});
+eval "require RT::Search::ActiveTicketsInQueue_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/ActiveTicketsInQueue_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Search/FromSQL.pm b/rt/lib/RT/Search/FromSQL.pm
new file mode 100644
index 0000000..e3a1264
--- /dev/null
+++ b/rt/lib/RT/Search/FromSQL.pm
@@ -0,0 +1,110 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Search::FromSQL
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Find all tickets described by the SQL statement passed as an argument
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Search::Generic);
+
+=end testing
+
+
+=cut
+
+package RT::Search::FromSQL;
+
+use strict;
+use base qw(RT::Search::Generic);
+
+=head2 Describe
+
+Returns a localized string describing the module's function.
+
+=cut
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->loc("TicketSQL search module", ref $self));
+}
+# }}}
+
+=head2 Prepare
+
+The meat of the module. Runs a search on its Tickets object, using
+the SQL string described in its Argument object. The Tickets object
+is reduced to those tickets matching the SQL query.
+
+=cut
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+
+ $self->TicketsObj->FromSQL($self->Argument);
+ return(1);
+}
+# }}}
+
+eval "require RT::Search::FromSQL_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/FromSQL_Vendor.pm});
+eval "require RT::Search::FromSQL_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/FromSQL_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Search/Generic.pm b/rt/lib/RT/Search/Generic.pm
new file mode 100644
index 0000000..15e5e96
--- /dev/null
+++ b/rt/lib/RT/Search/Generic.pm
@@ -0,0 +1,152 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Search::Generic - ;
+
+=head1 SYNOPSIS
+
+ use RT::Search::Generic;
+ my $tickets = RT::Tickets->new($CurrentUser);
+ my $foo = RT::Search::Generic->new(Argument => $arg,
+ TicketsObj => $tickets);
+ $foo->Prepare();
+ while ( my $ticket = $foo->Next ) {
+ # Do something with each ticket we've found
+ }
+
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Search::Generic);
+
+=end testing
+
+
+=cut
+
+package RT::Search::Generic;
+
+use strict;
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ my %args = (
+ TicketsObj => undef,
+ Argument => undef,
+ @_ );
+
+ $self->{'TicketsObj'} = $args{'TicketsObj'};
+ $self->{'Argument'} = $args{'Argument'};
+}
+# }}}
+
+# {{{ sub Argument
+
+=head2 Argument
+
+Return the optional argument associated with this Search
+
+=cut
+
+sub Argument {
+ my $self = shift;
+ return($self->{'Argument'});
+}
+# }}}
+
+
+=head2 TicketsObj
+
+Return the Tickets object passed into this search
+
+=cut
+
+sub TicketsObj {
+ my $self = shift;
+ return($self->{'TicketsObj'});
+}
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->loc("No description for [_1]", ref $self));
+}
+# }}}
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+ return(1);
+}
+# }}}
+
+eval "require RT::Search::Generic_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/Generic_Vendor.pm});
+eval "require RT::Search::Generic_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/Generic_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Search/Googleish.pm b/rt/lib/RT/Search/Googleish.pm
new file mode 100644
index 0000000..a2a8873
--- /dev/null
+++ b/rt/lib/RT/Search/Googleish.pm
@@ -0,0 +1,188 @@
+
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Search::Googlish
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Use the argument passed in as a "Google-style" set of keywords
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::Search::Generic);
+
+=end testing
+
+
+=cut
+
+package RT::Search::Googleish;
+
+use strict;
+use base qw(RT::Search::Generic);
+
+
+# sub _Init {{{
+sub _Init {
+ my $self = shift;
+ my %args = @_;
+
+ $self->{'Queues'} = delete($args{'Queues'}) || [];
+ $self->SUPER::_Init(%args);
+}
+# }}}
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->loc("No description for [_1]", ref $self));
+}
+# }}}
+
+# {{{ sub QueryToSQL
+sub QueryToSQL {
+ my $self = shift;
+ my $query = shift || $self->Argument;
+ my @keywords = split /\s+/, $query;
+ my (
+ @tql_clauses, @owner_clauses, @queue_clauses,
+ @user_clauses, @id_clauses, @status_clauses
+ );
+ my ( $Queue, $User );
+ for my $key (@keywords) {
+
+ # Is this a ticket number? If so, go to it.
+ if ( $key =~ m/^\d+$/ ) {
+ push @id_clauses, "id = '$key'";
+ }
+
+ elsif ( $key =~ /\w+\@\w+/ ) {
+ push @user_clauses, "Requestor LIKE '$key'";
+ }
+
+ # Is there a status with this name?
+ elsif (
+ $Queue = RT::Queue->new( $self->TicketsObj->CurrentUser )
+ and $Queue->IsValidStatus($key)
+ )
+ {
+ push @status_clauses, "Status = '" . $key . "'";
+ }
+
+ # Is there a owner named $key?
+ # Is there a queue named $key?
+ elsif ( $Queue = RT::Queue->new( $self->TicketsObj->CurrentUser )
+ and $Queue->Load($key) )
+ {
+ push @queue_clauses, "Queue = '" . $Queue->Name . "'";
+ }
+
+ # Is there a owner named $key?
+ elsif ( $User = RT::User->new( $self->TicketsObj->CurrentUser )
+ and $User->Load($key)
+ and $User->Privileged )
+ {
+ push @owner_clauses, "Owner = '" . $User->Name . "'";
+ }
+
+ elsif ($key =~ /^fulltext:(.*?)$/i) {
+ $key = $1;
+ $key =~ s/['\\].*//g;
+ push @tql_clauses, "Content LIKE '$key'";
+
+ }
+
+ # Else, subject must contain $key
+ else {
+ $key =~ s/['\\].*//g;
+ push @tql_clauses, "Subject LIKE '$key'";
+ }
+ }
+
+ # restrict to any queues requested by the caller
+ for my $queue (@{ $self->{'Queues'} }) {
+ my $QueueObj = RT::Queue->new($self->TicketsObj->CurrentUser);
+ $QueueObj->Load($queue) or next;
+ push @queue_clauses, "Queue = '" . $QueueObj->Name . "'";
+ }
+
+ push @tql_clauses, join( " OR ", sort @id_clauses );
+ push @tql_clauses, join( " OR ", sort @owner_clauses );
+ push @tql_clauses, join( " OR ", sort @status_clauses );
+ push @tql_clauses, join( " OR ", sort @user_clauses );
+ push @tql_clauses, join( " OR ", sort @queue_clauses );
+ @tql_clauses = grep { $_ ? $_ = "( $_ )" : undef } @tql_clauses;
+ return join " AND ", sort @tql_clauses;
+}
+# }}}
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+ my $tql = $self->QueryToSQL($self->Argument);
+
+ $RT::Logger->crit($tql);
+
+ $self->TicketsObj->FromSQL($tql);
+ return(1);
+}
+# }}}
+
+eval "require RT::Search::Googleish_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/Googleish_Vendor.pm});
+eval "require RT::Search::Googleish_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Search/Googleish_Local.pm});
+
+1;
diff --git a/rt/lib/RT/SearchBuilder.pm b/rt/lib/RT/SearchBuilder.pm
new file mode 100644
index 0000000..aa915d4
--- /dev/null
+++ b/rt/lib/RT/SearchBuilder.pm
@@ -0,0 +1,389 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::SearchBuilder - a baseclass for RT collection objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+
+=begin testing
+
+ok (require RT::SearchBuilder);
+
+=end testing
+
+
+=cut
+
+package RT::SearchBuilder;
+
+use RT::Base;
+use DBIx::SearchBuilder "1.50";
+
+use strict;
+use vars qw(@ISA);
+@ISA = qw(DBIx::SearchBuilder RT::Base);
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+
+ $self->{'user'} = shift;
+ unless(defined($self->CurrentUser)) {
+ use Carp;
+ Carp::confess("$self was created without a CurrentUser");
+ $RT::Logger->err("$self was created without a CurrentUser");
+ return(0);
+ }
+ $self->SUPER::_Init( 'Handle' => $RT::Handle);
+}
+# }}}
+
+# {{{ sub LimitToEnabled
+
+=head2 LimitToEnabled
+
+Only find items that haven\'t been disabled
+
+=cut
+
+sub LimitToEnabled {
+ my $self = shift;
+
+ $self->Limit( FIELD => 'Disabled',
+ VALUE => '0',
+ OPERATOR => '=' );
+}
+# }}}
+
+# {{{ sub LimitToDisabled
+
+=head2 LimitToDeleted
+
+Only find items that have been deleted.
+
+=cut
+
+sub LimitToDeleted {
+ my $self = shift;
+
+ $self->{'find_disabled_rows'} = 1;
+ $self->Limit( FIELD => 'Disabled',
+ OPERATOR => '=',
+ VALUE => '1'
+ );
+}
+# }}}
+
+# {{{ sub LimitAttribute
+
+=head2 LimitAttribute PARAMHASH
+
+Takes NAME, OPERATOR and VALUE to find records that has the
+matching Attribute.
+
+If EMPTY is set, also select rows with an empty string as
+Attribute's Content.
+
+If NULL is set, also select rows without the named Attribute.
+
+=cut
+
+my %Negate = qw(
+ = !=
+ != =
+ > <=
+ < >=
+ >= <
+ <= >
+ LIKE NOT LIKE
+ NOT LIKE LIKE
+ IS IS NOT
+ IS NOT IS
+);
+
+sub LimitAttribute {
+ my ($self, %args) = @_;
+ my $clause = 'ALIAS';
+ my $operator = ($args{OPERATOR} || '=');
+
+ if ($args{NULL} and exists $args{VALUE}) {
+ $clause = 'LEFTJOIN';
+ $operator = $Negate{$operator};
+ }
+ elsif ($args{NEGATE}) {
+ $operator = $Negate{$operator};
+ }
+
+ my $alias = $self->Join(
+ TYPE => 'left',
+ ALIAS1 => $args{ALIAS} || 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Attributes',
+ FIELD2 => 'ObjectId'
+ );
+
+ my $type = ref($self);
+ $type =~ s/(?:s|Collection)$//; # XXX - Hack!
+
+ $self->Limit(
+ $clause => $alias,
+ FIELD => 'ObjectType',
+ OPERATOR => '=',
+ VALUE => $type,
+ );
+ $self->Limit(
+ $clause => $alias,
+ FIELD => 'Name',
+ OPERATOR => '=',
+ VALUE => $args{NAME},
+ ) if exists $args{NAME};
+
+ return unless exists $args{VALUE};
+
+ $self->Limit(
+ $clause => $alias,
+ FIELD => 'Content',
+ OPERATOR => $operator,
+ VALUE => $args{VALUE},
+ );
+
+ # Capture rows with the attribute defined as an empty string.
+ $self->Limit(
+ $clause => $alias,
+ FIELD => 'Content',
+ OPERATOR => '=',
+ VALUE => '',
+ ENTRYAGGREGATOR => $args{NULL} ? 'AND' : 'OR',
+ ) if $args{EMPTY};
+
+ # Capture rows without the attribute defined
+ $self->Limit(
+ %args,
+ ALIAS => $alias,
+ FIELD => 'id',
+ OPERATOR => ($args{NEGATE} ? 'IS NOT' : 'IS'),
+ VALUE => 'NULL',
+ ) if $args{NULL};
+}
+# }}}
+
+# {{{ sub LimitCustomField
+
+=head2 LimitCustomField
+
+Takes a paramhash of key/value pairs with the following keys:
+
+=over 4
+
+=item CUSTOMFIELD - CustomField id. Optional
+
+=item OPERATOR - The usual Limit operators
+
+=item VALUE - The value to compare against
+
+=back
+
+=cut
+
+sub _SingularClass {
+ my $self = shift;
+ my $class = ref($self);
+ $class =~ s/s$// or die "Cannot deduce SingularClass for $class";
+ return $class;
+}
+
+sub LimitCustomField {
+ my $self = shift;
+ my %args = ( VALUE => undef,
+ CUSTOMFIELD => undef,
+ OPERATOR => '=',
+ @_ );
+
+ my $alias = $self->Join(
+ TYPE => 'left',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFieldValues',
+ FIELD2 => 'ObjectId'
+ );
+ $self->Limit(
+ ALIAS => $alias,
+ FIELD => 'CustomField',
+ OPERATOR => '=',
+ VALUE => $args{'CUSTOMFIELD'},
+ ) if ($args{'CUSTOMFIELD'});
+ $self->Limit(
+ ALIAS => $alias,
+ FIELD => 'ObjectType',
+ OPERATOR => '=',
+ VALUE => $self->_SingularClass,
+ );
+ $self->Limit(
+ ALIAS => $alias,
+ FIELD => 'Content',
+ OPERATOR => $args{'OPERATOR'},
+ VALUE => $args{'VALUE'},
+ );
+}
+
+# {{{ sub FindAllRows
+
+=head2 FindAllRows
+
+Find all matching rows, regardless of whether they are disabled or not
+
+=cut
+
+sub FindAllRows {
+ shift->{'find_disabled_rows'} = 1;
+}
+
+# {{{ sub Limit
+
+=head2 Limit PARAMHASH
+
+This Limit sub calls SUPER::Limit, but defaults "CASESENSITIVE" to 1, thus
+making sure that by default lots of things don't do extra work trying to
+match lower(colname) agaist lc($val);
+
+=cut
+
+sub Limit {
+ my $self = shift;
+ my %args = ( CASESENSITIVE => 1,
+ @_ );
+
+ return $self->SUPER::Limit(%args);
+}
+
+# }}}
+
+# {{{ sub ItemsOrderBy
+
+=head2 ItemsOrderBy
+
+If it has a SortOrder attribute, sort the array by SortOrder.
+Otherwise, if it has a "Name" attribute, sort alphabetically by Name
+Otherwise, just give up and return it in the order it came from the
+db.
+
+=cut
+
+sub ItemsOrderBy {
+ my $self = shift;
+ my $items = shift;
+
+ if ($self->NewItem()->_Accessible('SortOrder','read')) {
+ $items = [ sort { $a->SortOrder <=> $b->SortOrder } @{$items} ];
+ }
+ elsif ($self->NewItem()->_Accessible('Name','read')) {
+ $items = [ sort { lc($a->Name) cmp lc($b->Name) } @{$items} ];
+ }
+
+ return $items;
+}
+
+# }}}
+
+# {{{ sub ItemsArrayRef
+
+=head2 ItemsArrayRef
+
+Return this object's ItemsArray, in the order that ItemsOrderBy sorts
+it.
+
+=begin testing
+
+use_ok(RT::Queues);
+ok(my $queues = RT::Queues->new($RT::SystemUser), 'Created a queues object');
+ok( $queues->UnLimit(),'Unlimited the result set of the queues object');
+my $items = $queues->ItemsArrayRef();
+my @items = @{$items};
+
+ok($queues->NewItem->_Accessible('Name','read'));
+my @sorted = sort {lc($a->Name) cmp lc($b->Name)} @items;
+ok (@sorted, "We have an array of queues, sorted". join(',',map {$_->Name} @sorted));
+
+ok (@items, "We have an array of queues, raw". join(',',map {$_->Name} @items));
+my @sorted_ids = map {$_->id } @sorted;
+my @items_ids = map {$_->id } @items;
+
+is ($#sorted, $#items);
+is ($sorted[0]->Name, $items[0]->Name);
+is ($sorted[-1]->Name, $items[-1]->Name);
+is_deeply(\@items_ids, \@sorted_ids, "ItemsArrayRef sorts alphabetically by name");;
+
+
+=end testing
+
+=cut
+
+sub ItemsArrayRef {
+ my $self = shift;
+ my @items;
+
+ return $self->ItemsOrderBy($self->SUPER::ItemsArrayRef());
+}
+
+# }}}
+
+eval "require RT::SearchBuilder_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Vendor.pm});
+eval "require RT::SearchBuilder_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Local.pm});
+
+1;
+
+
diff --git a/rt/lib/RT/StyleGuide.pod b/rt/lib/RT/StyleGuide.pod
new file mode 100644
index 0000000..ff9a1b5
--- /dev/null
+++ b/rt/lib/RT/StyleGuide.pod
@@ -0,0 +1,920 @@
+=head1 NAME
+
+RT::StyleGuide - RT Style Guide
+
+=head1 INTRODUCTION
+
+All code and documentation that is submitted to be included in the RT
+distribution should follow the style in this document. This is not to
+try to stifle your creativity, but to make life easier for everybody who
+has to work with your code, and to aid those who are not quite sure how
+to do something.
+
+These conventions below apply to perl modules, web programs, and
+command-line programs, specifically, but also might apply to some
+degree to any Perl code written for use in RT.
+
+Note that these are all guidelines, not unbreakable rules. If you have
+a really good need to break one of the rules herein, however, then it is
+best to ask on the B<rt-devel> mailing list first.
+
+Note that with much of this document, it is not so much the Right Way as
+it is Our Way. We need to have conventions in order to make life easier
+for everyone. So don't gripe, and just follow it, because you didn't
+get a good grade in "Plays Well With Others" in kindergarten and you
+want to make up for it now.
+
+If you have any questions, please ask us on the B<rt-devel> mailing list:
+
+ http://www.bestpractical.com/rt/lists.html
+
+We don't always follow this guide. We are making changes throughout
+our code to be in line with it. But just because we didn't do
+it yet, that is no excuse. Do it anyway. :-)
+
+This document is subject to change at the whims of the core RT team.
+We hope to add any significant changes at the bottom of the document.
+
+
+=head1 CODING PRINCIPLES
+
+=head2 Perl Version
+
+We code everything to perl 5.8.3 or higher. Complete unicode support
+requires bugfixes found in 5.8.3.
+
+=head2 Documentation
+
+All modules will be documented using the POD examples in the module
+boilerplate. The function, purpose, use of the module will be
+explained, and each public API will be documented with name,
+description, inputs, outputs, side effects, etc.
+
+If an array or hash reference is returned, document the size of the
+array (including what each element is, as appropriate) and name each key
+in the hash. For complex data structures, map out the structure as
+appropriate (e.g., name each field returned for each column from a DB
+call; yes, this means you shouldn't use "SELECT *", which you shouldn't
+use anyway).
+
+Also document what kind of data returned values are. Is it an integer,
+a block of HTML, a boolean?
+
+All command-line program options will be documented using the
+boilerplate code for command-line programs, which doesn't yet exist.
+Each available function, switch, etc. should be documented, along
+with a statement of function, purpose, use of the program. Do not
+use the same options as another program, for a different purpose.
+
+All web templates should be documented with a statement of function,
+purpose, and use in a mason comment block.
+
+Any external documents, and documentation for command-line programs and
+modules, should be written in POD, where appropriate. From there, they
+can be translated to many formats with the various pod2* translators.
+Read the perlpod manpage before writing any POD, because although POD is
+not difficult, it is not what most people are used to. It is not a
+regular markup language; it is just a way to make easy documentation
+for translating to other formats. Read, and understand, the perlpod
+manpage, and ask us or someone else who knows if you have any questions.
+
+
+=head2 Version
+
+Our distribution versions use tuples, where the first number is the
+major revision, the second number is the version, and third
+number is the subversion. Odd-numbered versions are development
+versions. Examples:
+
+ 1.0.0 First release of RT 1
+ 1.0.1 Second release of RT 1.0
+ 1.0.10 etc.
+ 1.1.0 First development release of RT 1.2 (or 2.0)
+ 2.0.0 First release of RT 2
+
+Versions can be modified with a hyphen followed by some text, for
+special versions, or to give extra information. Examples:
+
+ 2.0.0-pre1 Notes that this is not final, but preview
+
+In perl 5.6.0, you can have versions like C<v2.0.0>, but this is not
+allowed in previous versions of perl. So to convert a tuple version
+string to a string to use with $VERSION, use a regular integer for
+the revision, and three digits for version and subversion. Examples:
+
+ 1.1.6 -> 1.001006
+ 2.0.0 -> 2.000000
+
+This way, perl can use the version strings in greater-than and
+less-than comparisons.
+
+
+=head2 Comments
+
+All code should be self-documenting as much as possible. Only include
+necessary comments. Use names like "$ticket_count", so you don't need to
+do something like:
+
+ # ticket count
+ my $tc = 0;
+
+Include any comments that are, or might be, necessary in order for
+someone else to understand the code. Sometimes a simple one-line
+comment is good to explain what the purpose of the following code is
+for. Sometimes each line needs to be commented because of a complex
+algorithm. Read Kernighan & Pike's I<Practice of Programming> about
+commenting. Good stuff, Maynard.
+
+
+=head2 Warnings and Strict
+
+All code must compile and run cleanly with "use strict" enabled and the
+perl "-w" (warnings) option on. If you must do something that -w or
+strict complains about, there are workarounds, but the chances that you
+really need to do it that way are remote.
+
+=head2 Lexical Variables
+
+Use only lexical variables, except for special global variables
+($VERSION, %ENV, @ISA, $!, etc.) or very special circumstances (see
+%HTML::Mason::Commands::session ). Global variables
+for regular use are never appropriate. When necessary, "declare"
+globals with "use vars" or "our()".
+
+A lexical variable is created with my(). A global variable is
+pre-existing (if it is a special variable), or it pops into existence
+when it is used. local() is used to tell perl to assign a temporary
+value to a variable. This should only be used with special variables,
+like $/, or in special circumstances. If you must assign to any global
+variable, consider whether or not you should use local().
+
+local() may also be used on elements of arrays and hashes, though there
+is seldom a need to do it, and you shouldn't.
+
+
+=head2 Exporting
+
+Do not export anything from a module by default. Feel free to put
+anything you want to in @EXPORT_OK, so users of your modules can
+explicitly ask for symbols (e.g., "use Something::Something qw(getFoo
+setFoo)"), but do not export them by default.
+
+
+=head2 Pass by Reference
+
+Arrays and hashes should be passed to and from functions by reference
+only. Note that a list and an array are NOT the same thing. This
+is perfectly fine:
+
+ return($user, $form, $constants);
+
+An exception might be a temporary array of discrete arguments:
+
+ my @return = ($user, $form);
+ push @return, $constants if $flag;
+ return @return;
+
+Although, usually, this is better (faster, easier to read, etc.):
+
+ if ($flag) {
+ return($user, $form, $constants);
+ } else {
+ return($user, $form);
+ }
+
+We need to talk about Class::ReturnValue here.
+
+
+=head2 Garbage Collection
+
+Perl does pretty good garbage collection for you. It will automatically
+clean up lexical variables that have gone out of scope and objects whose
+references have gone away. Normally you don't need to worry about
+cleaning up after yourself, if using lexicals.
+
+However, some glue code, code compiled in C and linked to Perl, might
+not automatically clean up for you. In such cases, clean up for
+yourself. If there is a method in that glue to dispose or destruct,
+then use it as appropriate.
+
+Also, if you have a long-running function that has a large data
+structure in it, it is polite to free up the memory as soon as you are
+done with it, if possible.
+
+ my $huge_data_structure = get_huge_data_structure();
+ do_something_with($huge_data_structure);
+ undef $huge_data_structure;
+
+=head2 DESTROY
+
+All object classes must provide a DESTROY method. If it won't do
+anything, provide it anyway:
+
+ sub DESTROY { }
+
+
+
+=head2 die() and exit()
+
+Don't do it. Do not die() or exit() from a web template or module. Do
+not call C<kill 9, $$>. Don't do it.
+
+In command-line programs, do as you please.
+
+
+=head2 shift and @_
+
+Do not use @_. Use shift. shift may take more lines, but Jesse thinks it
+leads to cleaner code.
+
+ my $var = shift; # right
+ my($var) = @_; # ick. no
+ sub foo { uc $_[0] } # icky. sometimes ok.
+
+
+ my($var1, $var2) = (shift, shift); # Um, no.
+
+ my $var1 = shift; # right
+ my $var2 = shift;
+
+=head2 Method parameters
+
+If a method takes exactly one mandatory argument, the argument should be
+passed in a straightforward manner:
+
+ my $self = shift;
+ my $id = shift;
+
+In all other cases, the method needs to take named parameters, usually
+using a C<%args> hash to store them:
+
+ my $self = shift;
+ my %args = ( Name => undef,
+ Description => undef,
+ @_ );
+
+You may specify defaults to those named parameters instead of using
+C<undef> above, as long as it is documented as such.
+
+It is worth noting that the existing RT codebase had not followed this
+style perfectly; we are trying to fix it without breaking exsiting APIs.
+
+=head2 Tests
+
+Modules should provide test code, with documentation on how to use
+it. Test::Inline allows tests to be embedded in code. Test::More makes it
+easy to create tests. Any code you write should have a testsuite.
+Any code you alter should have a test suite. If a patch comes in without
+tests, there is something wrong.
+
+When altering code, you must run the test harness before submitting a patch
+or committing code to the repository.
+
+"make regression" will extract inline tests, blow away the system database
+and run the test suite.
+
+"make regression-quiet" will do all that and not print the "ok" lines.
+
+
+
+=head2 STDIN/STDOUT
+
+Always report errors using $RT::Logger. It's a Log::Dispatch object.
+Unlike message meant for the user, log messages are not to be
+internationalized.
+
+There are several different levels ($RT::Logger methods) of logging:
+
+=over 4
+
+=item debug
+
+Used for messages only needed during system debugging.
+
+=item info
+
+Should be used to describe "system-critical" events which aren't errors.
+Examples: creating users, deleting users, creating tickets, creating queues,
+sending email (message id, time, recipients), recieving mail, changing
+passwords, changing access control, superuser logins)
+
+=item error
+
+Used for RT-generated failures during execution.
+
+=item crit
+
+Should be used for messages when an action can not be completed due to some
+error condition beyond our control.
+
+=back
+
+In the web UI and modules, never print directly to STDERR. Do not print
+directly to STDOUT, unless you need to print directly to the user's console.
+
+In command-line programs, feel free to print to STDERR and STDOUT as
+needed for direct console communication. But for actual error reporting,
+use the logging API.
+
+
+=head2 System Calls
+
+Always check return values from system calls, including open(),
+close(), mkdir(), or anything else that talks directly to the system.
+Perl built-in system calls return the error in $!; some functions in
+modules might return an error in $@ or some other way, so read the module's
+documentation if you don't know. Always do something, even if it is
+just calling $RT::Logger->warning(), when the return value is not what you'd expect.
+
+
+
+=head1 STYLE
+
+Much of the style section is taken from the perlsyle manpage. We make
+some changes to it here, but it wouldn't be a bad idea to read that
+document, too.
+
+=head2 Terminology
+
+=over 4
+
+=item RT the name
+
+"RT" is the name of the project. "RT" is, optionally, the
+specific name for the actual file distribution. That's it.
+
+While we sometimes use "RT2" or "RT3", that's shortand that's really
+not recommended. The name of the project is "RT".
+
+To specify a major version, use "RT 3.0".
+To specify a specific release, use "RT 3.0.12"
+
+=item function vs. sub(routine) vs. method
+
+Just because it is the Perl Way (not necessarily right for all
+languages, but the documented terminology in the perl documentation),
+"method" should be used only to refer to a subroutine that are object
+methods or class methods; that is, these are functions that are used
+with OOP that always take either an object or a class as the first
+argument. Regular subroutines, ones that are not object or class
+methods, are functions. Class methods that create and return an object
+are optionally called constructors.
+
+=item Users
+
+"users" are normally users of RT, the ones hitting the site; if using
+it in any other context, specify.
+"system users" are user
+names on the operating system. "database users" are the user names in
+the database server. None of these needs to be capitalized.
+
+=back
+
+
+=head2 Names
+
+Don't use single-character variables, except as iterator variables.
+
+Don't use two-character variables just to spite us over the above rule.
+
+Constants are in all caps; these are variables whose value will I<never>
+change during the course of the program.
+
+ $Minimum = 10; # wrong
+ $MAXIMUM = 50; # right
+
+Other variables are lowercase, with underscores separating the words.
+They words used should, in general, form a noun (usually singular),
+unless the variable is a flag used to denote some action that should be
+taken, in which case they should be verbs (or gerunds, as appropriate)
+describing that action.
+
+ $thisVar = 'foo'; # wrong
+ $this_var = 'foo'; # right
+ $work_hard = 1; # right, verb, boolean flag
+ $running_fast = 0; # right, gerund, boolean flag
+
+Arrays and hashes should be plural nouns, whether as regular arrays and
+hashes or array and hash references. Do not name references with "ref"
+or the data type in the name.
+
+ @stories = (1, 2, 3); # right
+ $comment_ref = [4, 5, 6]; # wrong
+ $comments = [4, 5, 6]; # right
+ $comment = $comments->[0]; # right
+
+Make the name descriptive. Don't use variables like "$sc" when you
+could call it "$story_count". See L<"Comments">.
+
+There are several variables in RT that are used throughout the code,
+that you should use in your code. Do not use these variable names for
+anything other than how they are normally used, and do not use any
+other variable names in their place. Some of these are:
+
+ $self # first named argument in object method
+
+Subroutines (except for special cases, like AUTOLOAD and simple accessors)
+begin with a verb, with words following to complete the action. Accessors
+don't start with "Get" if they're just the name of the attribute.
+
+Accessors which return an object should end with the suffix Obj.
+
+This section needs clarification for RT.
+
+Words begin with a capital letter. They
+should as clearly as possible describe the activity to be peformed, and
+the data to be returned.
+
+
+
+ Load(); # good
+ LoadByName(); # good
+ LoadById(); # good
+
+Subroutines beginning with C<_> are special: they are not to be used
+outside the current object. There is not to be enforced by the code
+itself, but by someone very big and very scary.
+
+For large for() loops, do not use $_, but name the variable.
+Do not use $_ (or assume it) except for when it is absolutely
+clear what is going on, or when it is required (such as with
+map() and grep()).
+
+ for (@list) {
+ print; # OK; everyone knows this one
+ print uc; # wrong; few people know this
+ print uc $_; # better
+ }
+
+Note that the special variable C<_> I<should> be used when possible.
+It is a placeholder that can be passed to stat() and the file test
+operators, that saves perl a trip to re-stat the file. In the
+example below, using C<$file> over for each file test, instead of
+C<_> for subsequent uses, is a performance hit. You should be
+careful that the last-tested file is what you think it is, though.
+
+ if (-d $file) { # $file is a directory
+ # ...
+ } elsif (-l _) { # $file is a symlink
+ # ...
+ }
+
+Package names begin with a capital letter in each word, followed by
+lower case letters (for the most part). Multiple words should be StudlyCapped.
+
+ RT::User # good
+ RT::Database::MySQL # proper name
+ RT::Display::Provider # good
+ RT::CustomField # not so good, but OK
+
+Plugin modules should begin with "RTx::", followed by the name
+of the plugin.
+
+=head1 Code formatting
+
+Use perltidy. Anything we say here is wrong if it conflicts with what
+perltidy does. Your perltidyrc should read:
+
+-lp -vt=2 -vtc=2 -nsfs -bar
+
+=head2 Indents and Blank Space
+
+All indents should be tabs. Set your tab stops whatever you want them
+to be; I use 8 spaces per tabs.
+
+No space before a semicolon that closes a statement.
+
+ foo(@bar) ; # wrong
+ foo(@bar); # right
+
+Line up corresponding items vertically.
+
+ my $foo = 1;
+ my $bar = 2;
+ my $xyzzy = 3;
+
+ open(FILE, $fh) or die $!;
+ open(FILE2, $fh2) or die $!;
+
+ $rot13 =~ tr[abcedfghijklmnopqrstuvwxyz]
+ [nopqrstuvwxyzabcdefghijklm];
+
+ # note we use a-mn-z instead of a-z,
+ # for readability
+ $rot13 =~ tr[a-mn-z]
+ [n-za-m];
+
+Put blank lines between groups of code that do different things. Put
+blank lines after your variable declarations. Put a blank line before a
+final return() statement. Put a blank line following a block (and
+before, with the exception of comment lines).
+
+An example:
+
+ # this is my function!
+ sub foo {
+ my $val = shift;
+ my $obj = new Constructor;
+ my($var1, $var2);
+
+ $obj->SetFoo($val);
+ $var1 = $obj->Foo();
+
+
+ return($val);
+ }
+
+ print 1;
+
+
+=head2 Parentheses
+
+For control structures, there is a space between the keyword and opening
+parenthesis. For functions, there is not.
+
+ for(@list) # wrong
+ for (@list) # right
+
+ my ($ref) # wrong
+ my($ref) # right
+
+Be careful about list vs. scalar context with parentheses!
+
+ my @array = ('a', 'b', 'c');
+ my($first_element) = @array; # a
+ my($first_element) = ('a', 'b', 'c'); # a
+ my $element_count = @array; # 3
+ my $last_element = ('a', 'b', 'c'); # c
+
+Always include parentheses after functions, even if there are no arguments.
+There are some exceptions, such as list operators (like print) and unary
+operators (like undef, delete, uc).
+
+There is no space inside the parentheses, unless it is needed for
+readability.
+
+ for ( map { [ $_, 1 ] } @list ) # OK
+ for ( @list ) # not really OK, not horrible
+
+On multi-line expressions, match up the closing parenthesis with either
+the opening statement, or the opening parenthesis, whichever works best.
+Examples:
+
+ @list = qw(
+ bar
+ baz
+ ); # right
+
+ if ($foo && $bar && $baz
+ && $buz && $xyzzy
+ ) {
+ print $foo;
+ }
+
+Whether or not there is space following a closing parenthesis is
+dependent on what it is that follows.
+
+ print foo(@bar), baz(@buz) if $xyzzy;
+
+Note also that parentheses around single-statement control expressions,
+as in C<if $xyzzy>, are optional (and discouraged) C<if> it is I<absolutely>
+clear -- to a programmer -- what is going on. There is absolutely no
+need for parentheses around C<$xyzzy> above, so leaving them out enhances
+readability. Use your best discretion. Better to include them, if
+there is any question.
+
+The same essentially goes for perl's built-in functions, when there is
+nothing confusing about what is going on (for example, there is only one
+function call in the statement, or the function call is separated by a
+flow control operator). User-supplied functions must always include
+parentheses.
+
+ print 1, 2, 3; # good
+ delete $hash{key} if isAnon($uid); # good
+
+
+However, if there is any possible confusion at all, then include the
+parentheses. Remember the words of Larry Wall in the perlstyle manpage:
+
+ When in doubt, parenthesize. At the very least it will
+ let some poor schmuck bounce on the % key in vi.
+
+ Even if you aren't in doubt, consider the mental welfare
+ of the person who has to maintain the code after you, and
+ who will probably put parens in the wrong place.
+
+So leave them out when it is absoutely clear to a programmer, but if
+there is any question, leave them in.
+
+
+=head2 Braces
+
+(This is about control braces, not hash/data structure braces.)
+
+There is always a space befor the opening brace.
+
+ while (<$fh>){ # wrong
+ while (<$fh>) { # right
+
+A one-line block may be put on one line, and the semicolon may be
+omitted.
+
+ for (@list) { print }
+
+Otherwise, finish each statement with a semicolon, put the keyword and
+opening curly on the first line, and the ending curly lined up with the
+keyword at the end.
+
+ for (@list) {
+ print;
+ smell();
+ }
+
+Generally, we prefer "uncuddled elses":
+
+ if ($foo) {
+ print;
+ }
+ else {
+ die;
+ }
+
+_If_ the if statement is very brief, sometimes "cuddling" the else makes code more readable. Feel free to cuddle them in that case:
+
+
+ if ($foo) {
+ print;
+ } else {
+ die;
+ }
+
+=head2 Operators
+
+Put space around most operators. The primary exception is the for
+aesthetics; e.g., sometimes the space around "**" is ommitted,
+and there is never a space before a ",", but always after.
+
+ print $x , $y; # wrong
+ print $x, $y; # right
+
+ $x = 2 >> 1; # good
+ $y = 2**2; # ok
+
+Note that "&&" and "||" have a higher precedence than "and" and "or".
+Other than that, they are exactly the same. It is best to use the lower
+precedence version for control, and the higher for testing/returning
+values. Examples:
+
+ $bool = $flag1 or $flag2; # WRONG (doesn't work)
+ $value = $foo || $bar; # right
+ open(FILE, $file) or die $!;
+
+ $true = foo($bar) && baz($buz);
+ foo($bar) and baz($buz);
+
+Note that "and" is seldom ever used, because the statement above is
+better written using "if":
+
+ baz($buz) if foo($bar);
+
+Most of the time, the confusion between and/&&, or/|| can be alleviated
+by using parentheses. If you want to leave off the parentheses then you
+I<must> use the proper operator. But if you use parentheses -- and
+normally, you should, if there is any question at all -- then it doesn't
+matter which you use. Use whichever is most readable and aesthetically
+pleasing to you at the time, and be consistent within your block of code.
+
+Break long lines AFTER operators, except for "and", "or", "&&", "||".
+Try to keep the two parts to a binary operator (an operator that
+has two operands) together when possible.
+
+ print "foo" . "bar" . "baz"
+ . "buz"; # wrong
+
+ print "foo" . "bar" . "baz" .
+ "buz"; # right
+
+ print $foo unless $x == 3 && $y ==
+ 4 && $z == 5; # wrong
+
+ print $foo unless $x == 3 && $y == 4
+ && $z == 5; # right
+
+
+=head2 Other
+
+Put space around a complex subscript inside the brackets or braces.
+
+ $foo{$bar{baz}{buz}}; # OK
+ $foo{ $bar{baz}{buz} }; # better
+
+In general, use single-quotes around literals, and double-quotes
+when the text needs to be interpolated.
+
+It is OK to omit quotes around names in braces and when using
+the => operator, but be careful not to use a name that doubles as
+a function; in that case, quote.
+
+ $what{'time'}{it}{is} = time();
+
+When making compound statements, put the primary action first.
+
+ open(FILE, $fh) or die $!; # right
+ die $! unless open(FILE, $fh); # wrong
+
+ print "Starting\n" if $verbose; # right
+ $verbose && print "Starting\n"; # wrong
+
+
+Use here-docs instead of repeated print statements.
+
+ print <<EOT;
+ This is a whole bunch of text.
+ I like it. I don't need to worry about messing
+ with lots of print statements and lining them up.
+ EOT
+
+Just remember that unless you put single quotes around your here-doc
+token (<<'EOT'), the text will be interpolated, so escape any "$" or "@"
+as needed.
+
+=head1 INTERNATIONALIZATION
+
+
+=head2 String extraction styleguide
+
+=over 4
+
+=item Web templates
+
+Templates should use the /l filtering component to call the localisation
+framework
+
+The string Foo!
+
+Should become <&|/l&>Foo!</&>
+
+All newlines should be removed from localized strings, to make it easy to
+grep the codebase for strings to be localized
+
+The string Foo
+ Bar
+ Baz
+
+Should become <&|/l&>Foo Bar Baz</&>
+
+
+Variable subsititutions should be moved to Locale::MakeText format
+
+The string Hello, <%$name %>
+
+should become <&|/l, $name &>Hello, [_1]</&>
+
+
+Multiple variables work just like single variables
+
+The string You found <%$num%> tickets in queue <%$queue%>
+
+should become <&|/l, $num, $queue &>You found [_1] tickets in queue [_2]</&>
+
+When subcomponents are called in the middle of a phrase, they need to be escaped
+too:
+
+The string <input type="submit" value="New ticket in">&nbsp<& /Elements/SelectNewTicketQueue&>
+
+should become <&|/l, $m->scomp('/Elements/SelectNewTicketQueue')&><input type="submit" value="New ticket in">&nbsp;[_1]</&>
+
+
+
+
+The string <& /Elements/TitleBoxStart, width=> "40%", titleright => "RT $RT::VERSION for $RT::rtname", title => 'Login' &>
+
+should become <& /Elements/TitleBoxStart,
+ width=> "40%",
+ titleright => loc("RT [_1] for [_2]",$RT::VERSION, $RT::rtname),
+ title => loc('Login'),
+ &>
+
+
+=item Library code
+
+
+
+Within RT's core code, every module has a localization handle available through the 'loc' method:
+
+The code return ( $id, "Queue created" );
+
+should become return ( $id, $self->loc("Queue created") );
+
+When returning or localizing a single string, the "extra" set of parenthesis () should be omitted.
+
+The code return ("Subject changed to ". $self->Data );
+
+should become return $self->loc( "Subject changed to [_1]", $self->Data );
+
+
+It is important not to localize the names of rights or statuses within RT's core, as there is logic that depends on them as string identifiers. The proper place to localize these values is when they're presented for display in the web or commandline interfaces.
+
+
+=back 4
+
+=head1 CODING PRCEDURE
+
+This is for new programs, modules, specific APIs, or anything else.
+
+=over 4
+
+=item Present idea to rt-devel
+
+We may know of a better way to approach the problem, or know of an
+existing way to deal with it, or know someone else is working on it.
+This is mostly informal, but a fairly complete explanation for the need
+and use of the code should be provided.
+
+
+=item Present complete specs to rt-devel
+
+The complete proposed API should be submitted for
+approval and discussion. For web and command-line programs, present the
+functionality and interface (op codes, command-lin switches, etc.).
+
+The best way to do this is to take the documentation portion of the
+boilerplate and fill it in. You can make changes later if necessary,
+but fill it in as much as you can.
+
+
+
+=item Prepare for code review
+
+When you are done, the code will undergo a code review by a member of
+the core team, or someone picked by the core team. This is not to
+belittle you (that's just a nice side effect), it is to make sure that
+you understand your code, that we understand your code, that it won't
+break other code, that it follows the documentation and existing
+proposal. It is to check for possible optimizations or better ways of
+doing it.
+
+Note that all code is expected to follow the coding principles and style
+guide contained in this document.
+
+
+=item Finish it up
+
+After the code is done (possibly going through multiple code reviews),
+if you do not have repository access, submit it to rt-<major-version>-bugs@fsck.com as a unified diff. From that point on, it'll be handled by someone with repository access.
+
+=back
+
+
+=head1 BUG REPORTS, PATCHES
+
+Use rt-<major-version>-bugs@fsck.com for I<any> bug that is not
+being fixed immediately. If it is not in RT, there
+is a good chance it will not be dealt with.
+
+Send patches to rt-<major-version>-bugs@fsck.com, too. Use C<diff
+-u> for patches.
+
+=head1 SCHEMA DESIGN
+
+RT uses a convention to denote the foreign key status in its tables.
+The rule of thumb is:
+
+=over 4
+
+=item When it references to another table, always use the table name
+
+For example, the C<Template> field in the C<Scrips> table refers to
+the C<Id> of the same-named C<Template> table.
+
+=item Otherwise, always use the C<Id> suffix
+
+For example, the C<ObjectId> field in the C<ACL> table can refer
+to any object, so it has the C<Id> suffix.
+
+=back
+
+There are some legacy fields that did not follow this rule, namely
+C<ACL.PrincipalId>, C<GroupMembers.GroupId> and C<Attachments.TransactionId>,
+but new tables are expected to be consistent.
+
+=head1 TO DO
+
+Talk about DBIx::SearchBuilder
+
+Talk about mason
+ component style
+ cascading style sheets
+
+Talk about adding a new translation
+
+Talk more about logging
+
+=head1 CHANGES
+
+ Adapted from Slash Styleguide by jesse - 20 Dec, 2002
+
+
+=head1 VERSION
+
+0.1
diff --git a/rt/lib/RT/System.pm b/rt/lib/RT/System.pm
new file mode 100644
index 0000000..ec0ae98
--- /dev/null
+++ b/rt/lib/RT/System.pm
@@ -0,0 +1,190 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+RT::System
+
+=head1 DESCRIPTION
+
+RT::System is a simple global object used as a focal point for things
+that are system-wide.
+
+It works sort of like an RT::Record, except it's really a single object that has
+an id of "1" when instantiated.
+
+This gets used by the ACL system so that you can have rights for the scope "RT::System"
+
+In the future, there will probably be other API goodness encapsulated here.
+
+=cut
+
+
+package RT::System;
+use base qw /RT::Record/;
+use strict;
+
+use RT::ACL;
+use vars qw/ $RIGHTS/;
+
+# System rights are rights granted to the whole system
+# XXX TODO Can't localize these outside of having an object around.
+$RIGHTS = {
+ SuperUser => 'Do anything and everything', # loc_pair
+ AdminAllPersonalGroups =>
+ "Create, delete and modify the members of any user's personal groups"
+ , # loc_pair
+ AdminOwnPersonalGroups =>
+ 'Create, delete and modify the members of personal groups', # loc_pair
+ AdminUsers => 'Create, delete and modify users', # loc_pair
+ ModifySelf => "Modify one's own RT account", # loc_pair
+ DelegateRights =>
+ "Delegate specific rights which have been granted to you.", # loc_pair
+ ShowConfigTab => "show Configuration tab", # loc_pair
+ LoadSavedSearch => "allow loading of saved searches", # loc_pair
+ CreateSavedSearch => "allow creation of saved searches", # loc_pair
+};
+
+# Tell RT::ACE that this sort of object can get acls granted
+$RT::ACE::OBJECT_TYPES{'RT::System'} = 1;
+
+foreach my $right ( keys %{$RIGHTS} ) {
+ $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
+}
+
+
+=head2 AvailableRights
+
+Returns a hash of available rights for this object. The keys are the right names and the values are a description of what the rights do
+
+=begin testing
+
+my $s = RT::System->new($RT::SystemUser);
+my $rights = $s->AvailableRights;
+ok ($rights, "Rights defined");
+ok ($rights->{'AdminUsers'},"AdminUsers right found");
+ok ($rights->{'CreateTicket'},"CreateTicket right found");
+ok ($rights->{'AdminGroupMembership'},"ModifyGroupMembers right found");
+ok (!$rights->{'CasdasdsreateTicket'},"bogus right not found");
+
+
+
+=end testing
+
+
+=cut
+
+sub AvailableRights {
+ my $self = shift;
+
+ my $queue = RT::Queue->new($RT::SystemUser);
+ my $group = RT::Group->new($RT::SystemUser);
+ my $cf = RT::CustomField->new($RT::SystemUser);
+
+ my $qr =$queue->AvailableRights();
+ my $gr = $group->AvailableRights();
+ my $cr = $cf->AvailableRights();
+
+ # Build a merged list of all system wide rights, queue rights and group rights.
+ my %rights = (%{$RIGHTS}, %{$gr}, %{$qr}, %{$cr});
+ return(\%rights);
+}
+
+sub _Init {
+ my $self = shift;
+ $self->SUPER::_Init (@_) if @_ && $_[0];
+}
+
+=head2 id
+
+Returns RT::System's id. It's 1.
+
+
+=begin testing
+
+use RT::System;
+my $sys = RT::System->new();
+is( $sys->Id, 1);
+is ($sys->id, 1);
+
+=end testing
+
+
+=cut
+
+*Id = \&id;
+
+sub id {
+ return (1);
+}
+
+=head2 Load
+
+Since this object is pretending to be an RT::Record, we need a load method.
+It does nothing
+
+=cut
+
+sub Load {
+ return (1);
+}
+
+sub Name {
+ return 'RT System';
+}
+
+sub __Set { 0 }
+sub __Value { 0 }
+sub Create { 0 }
+sub Delete { 0 }
+
+eval "require RT::System_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/System_Vendor.pm});
+eval "require RT::System_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/System_Local.pm});
+
+1;
diff --git a/rt/lib/RT/Template.pm b/rt/lib/RT/Template.pm
new file mode 100755
index 0000000..844ec29
--- /dev/null
+++ b/rt/lib/RT/Template.pm
@@ -0,0 +1,387 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Template
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Template;
+use RT::Record;
+use RT::Queue;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Templates');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'Queue'.
+ varchar(200) 'Name'.
+ varchar(255) 'Description'.
+ varchar(16) 'Type'.
+ varchar(16) 'Language'.
+ int(11) 'TranslationOf'.
+ blob 'Content'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Queue => '0',
+ Name => '',
+ Description => '',
+ Type => '',
+ Language => '',
+ TranslationOf => '0',
+ Content => '',
+
+ @_);
+ $self->SUPER::Create(
+ Queue => $args{'Queue'},
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ Type => $args{'Type'},
+ Language => $args{'Language'},
+ TranslationOf => $args{'TranslationOf'},
+ Content => $args{'Content'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Queue
+
+Returns the current value of Queue.
+(In the database, Queue is stored as int(11).)
+
+
+
+=head2 SetQueue VALUE
+
+
+Set Queue to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Queue will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 QueueObj
+
+Returns the Queue Object which has the id returned by Queue
+
+
+=cut
+
+sub QueueObj {
+ my $self = shift;
+ my $Queue = RT::Queue->new($self->CurrentUser);
+ $Queue->Load($self->__Value('Queue'));
+ return($Queue);
+}
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=head2 SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Type
+
+Returns the current value of Type.
+(In the database, Type is stored as varchar(16).)
+
+
+
+=head2 SetType VALUE
+
+
+Set Type to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Type will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 Language
+
+Returns the current value of Language.
+(In the database, Language is stored as varchar(16).)
+
+
+
+=head2 SetLanguage VALUE
+
+
+Set Language to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Language will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 TranslationOf
+
+Returns the current value of TranslationOf.
+(In the database, TranslationOf is stored as int(11).)
+
+
+
+=head2 SetTranslationOf VALUE
+
+
+Set TranslationOf to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TranslationOf will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Content
+
+Returns the current value of Content.
+(In the database, Content is stored as blob.)
+
+
+
+=head2 SetContent VALUE
+
+
+Set Content to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Content will be stored as a blob.)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Queue =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Description =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Type =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ Language =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ TranslationOf =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Content =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::Template_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Template_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Template_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Template_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Template_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Template_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Template_Overlay, RT::Template_Vendor, RT::Template_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Template_Overlay.pm b/rt/lib/RT/Template_Overlay.pm
new file mode 100644
index 0000000..4ce7298
--- /dev/null
+++ b/rt/lib/RT/Template_Overlay.pm
@@ -0,0 +1,422 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Portions Copyright 2000 Tobias Brox <tobix@cpan.org>
+
+=head1 NAME
+
+ RT::Template - RT's template object
+
+=head1 SYNOPSIS
+
+ use RT::Template;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok(require RT::Template);
+
+=end testing
+
+=cut
+
+
+package RT::Template;
+
+use strict;
+no warnings qw(redefine);
+
+use Text::Template;
+use MIME::Entity;
+use MIME::Parser;
+use File::Temp qw /tempdir/;
+
+
+# {{{ sub _Accessible
+
+sub _Accessible {
+ my $self = shift;
+ my %Cols = (
+ id => 'read',
+ Name => 'read/write',
+ Description => 'read/write',
+ Type => 'read/write', #Type is one of Action or Message
+ Content => 'read/write',
+ Queue => 'read/write',
+ Creator => 'read/auto',
+ Created => 'read/auto',
+ LastUpdatedBy => 'read/auto',
+ LastUpdated => 'read/auto'
+ );
+ return $self->SUPER::_Accessible( @_, %Cols );
+}
+
+# }}}
+
+# {{{ sub _Set
+
+sub _Set {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ return $self->SUPER::_Set( @_ );
+}
+
+# }}}
+
+# {{{ sub _Value
+
+=head2 _Value
+
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
+
+
+=begin testing
+
+my $t = RT::Template->new($RT::SystemUser);
+$t->Create(Name => "Foo", Queue => 1);
+my $t2 = RT::Template->new($RT::Nobody);
+$t2->Load($t->Id);
+ok($t2->QueueObj->id, "Got the template's queue objet");
+
+=end testing
+
+
+
+=cut
+
+sub _Value {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasQueueRight('ShowTemplate') ) {
+ return undef;
+ }
+ return $self->__Value( @_ );
+
+}
+
+# }}}
+
+# {{{ sub Load
+
+=head2 Load <identifer>
+
+Load a template, either by number or by name
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift;
+ return undef unless $identifier;
+
+ if ( $identifier =~ /\D/ ) {
+ return $self->LoadByCol( 'Name', $identifier );
+ }
+ return $self->LoadById( $identifier );
+}
+
+# }}}
+
+# {{{ sub LoadGlobalTemplate
+
+=head2 LoadGlobalTemplate NAME
+
+Load the global template with the name NAME
+
+=cut
+
+sub LoadGlobalTemplate {
+ my $self = shift;
+ my $id = shift;
+
+ return ( $self->LoadQueueTemplate( Queue => 0, Name => $id ) );
+}
+
+# }}}
+
+# {{{ sub LoadQueueTemplate
+
+=head2 LoadQueueTemplate (Queue => QUEUEID, Name => NAME)
+
+Loads the Queue template named NAME for Queue QUEUE.
+
+=cut
+
+sub LoadQueueTemplate {
+ my $self = shift;
+ my %args = (
+ Queue => undef,
+ Name => undef,
+ @_
+ );
+
+ return ( $self->LoadByCols( Name => $args{'Name'}, Queue => $args{'Queue'} ) );
+
+}
+
+# }}}
+
+# {{{ sub Create
+
+=head2 Create
+
+Takes a paramhash of Content, Queue, Name and Description.
+Name should be a unique string identifying this Template.
+Description and Content should be the template's title and content.
+Queue should be 0 for a global template and the queue # for a queue-specific
+template.
+
+Returns the Template's id # if the create was successful. Returns undef for
+unknown database failure.
+
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Content => undef,
+ Queue => 0,
+ Description => '[no description]',
+ Type => 'Action', #By default, template are 'Action' templates
+ Name => undef,
+ @_
+ );
+
+ unless ( $args{'Queue'} ) {
+ unless ( $self->CurrentUser->HasRight(Right =>'ModifyTemplate', Object => $RT::System) ) {
+ return ( undef, $self->loc('Permission denied') );
+ }
+ $args{'Queue'} = 0;
+ }
+ else {
+ my $QueueObj = new RT::Queue( $self->CurrentUser );
+ $QueueObj->Load( $args{'Queue'} ) || return ( undef, $self->loc('Invalid queue') );
+
+ unless ( $QueueObj->CurrentUserHasRight('ModifyTemplate') ) {
+ return ( undef, $self->loc('Permission denied') );
+ }
+ $args{'Queue'} = $QueueObj->Id;
+ }
+
+ my $result = $self->SUPER::Create(
+ Content => $args{'Content'},
+ Queue => $args{'Queue'},
+ Description => $args{'Description'},
+ Name => $args{'Name'},
+ );
+
+ return ($result);
+
+}
+
+# }}}
+
+# {{{ sub Delete
+
+=head2 Delete
+
+Delete this template.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+
+ return ( $self->SUPER::Delete(@_) );
+}
+
+# }}}
+
+# {{{ sub MIMEObj
+sub MIMEObj {
+ my $self = shift;
+ return ( $self->{'MIMEObj'} );
+}
+
+# }}}
+
+# {{{ sub Parse
+
+=head2 Parse
+
+ This routine performs Text::Template parsing on the template and then
+ imports the results into a MIME::Entity so we can really use it
+
+ Takes a hash containing Argument, TicketObj, and TransactionObj. TicketObj
+ and TransactionObj are not mandatory, but highly recommended.
+
+ It returns a tuple of (val, message)
+ If val is 0, the message contains an error message
+
+=cut
+
+sub Parse {
+ my $self = shift;
+
+ #We're passing in whatever we were passed. it's destined for _ParseContent
+ my ($content, $msg) = $self->_ParseContent(@_);
+ return ( 0, $msg ) unless defined $content;
+
+ #Lets build our mime Entity
+
+ my $parser = MIME::Parser->new();
+
+ # On some situations TMPDIR is non-writable. sad but true.
+ $parser->output_to_core(1);
+ $parser->tmp_to_core(1);
+
+ #If someone includes a message, don't extract it
+ $parser->extract_nested_messages(1);
+
+ # Set up the prefix for files with auto-generated names:
+ $parser->output_prefix("part");
+
+ # If content length is <= 50000 bytes, store each msg as in-core scalar;
+ # Else, write to a disk file (the default action):
+ $parser->output_to_core(50000);
+
+ ### Should we forgive normally-fatal errors?
+ $parser->ignore_errors(1);
+ $self->{'MIMEObj'} = eval { $parser->parse_data($content) };
+ if ( my $error = $@ || $parser->last_error ) {
+ $RT::Logger->error( "$error" );
+ return ( 0, $error );
+ }
+
+ # Unfold all headers
+ $self->{'MIMEObj'}->head->unfold;
+
+ return ( 1, $self->loc("Template parsed") );
+
+}
+
+# }}}
+
+# {{{ sub _ParseContent
+
+# Perform Template substitutions on the template
+
+sub _ParseContent {
+ my $self = shift;
+ my %args = (
+ Argument => undef,
+ TicketObj => undef,
+ TransactionObj => undef,
+ @_
+ );
+
+ no warnings 'redefine';
+ local $T::Ticket = $args{'TicketObj'};
+ local $T::Transaction = $args{'TransactionObj'};
+ local $T::Argument = $args{'Argument'};
+ local $T::Requestor = eval { $T::Ticket->Requestors->UserMembersObj->First->Name } if $T::Ticket;
+ local $T::rtname = $RT::rtname;
+
+ local *T::loc = sub {
+ $T::Ticket ? $T::Ticket->loc(@_)
+ : $self->CurrentUser->loc(@_)
+ };
+
+ my $content = $self->Content;
+ unless ( defined $content ) {
+ return ( undef, $self->loc("Permissions denied") );
+ }
+
+ # We need to untaint the content of the template, since we'll be working
+ # with it
+ $content =~ s/^(.*)$/$1/;
+ my $template = Text::Template->new(
+ TYPE => 'STRING',
+ SOURCE => $content
+ );
+
+ my $is_broken = 0;
+ my $retval = $template->fill_in( PACKAGE => 'T', BROKEN => sub {
+ my (%args) = @_;
+ $RT::Logger->error("Template parsing error: $args{error}")
+ unless $args{error} =~ /^Died at /; # ignore intentional die()
+ $is_broken++;
+ return undef;
+ } );
+ return ( undef, $self->loc('Template parsing error') ) if $is_broken;
+
+ # MIME::Parser has problems dealing with high-bit utf8 data.
+ Encode::_utf8_off($retval);
+ return ($retval);
+}
+
+# }}}
+
+# {{{ sub CurrentUserHasQueueRight
+
+=head2 CurrentUserHasQueueRight
+
+Helper function to call the template's queue's CurrentUserHasQueueRight with the passed in args.
+
+=cut
+
+sub CurrentUserHasQueueRight {
+ my $self = shift;
+ return ( $self->QueueObj->CurrentUserHasRight(@_) );
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/RT/Templates.pm b/rt/lib/RT/Templates.pm
new file mode 100755
index 0000000..3283806
--- /dev/null
+++ b/rt/lib/RT/Templates.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Templates -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Templates
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Templates;
+
+use RT::SearchBuilder;
+use RT::Template;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Templates';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Template item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Template->new($self->CurrentUser));
+}
+
+ eval "require RT::Templates_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Templates_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Templates_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Templates_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Templates_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Templates_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Templates_Overlay, RT::Templates_Vendor, RT::Templates_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Templates_Overlay.pm b/rt/lib/RT/Templates_Overlay.pm
new file mode 100644
index 0000000..079b903
--- /dev/null
+++ b/rt/lib/RT/Templates_Overlay.pm
@@ -0,0 +1,203 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Templates - a collection of RT Template objects
+
+=head1 SYNOPSIS
+
+ use RT::Templates;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok (require RT::Templates);
+
+=end testing
+
+=cut
+
+
+package RT::Templates;
+
+use strict;
+no warnings qw(redefine);
+
+
+# {{{ sub _Init
+
+=head2 _Init
+
+ Returns RT::Templates specific init info like table and primary key names
+
+=cut
+
+sub _Init {
+
+ my $self = shift;
+ $self->{'table'} = "Templates";
+ $self->{'primary_key'} = "id";
+ return ($self->SUPER::_Init(@_));
+}
+# }}}
+
+# {{{ LimitToNotInQueue
+
+=head2 LimitToNotInQueue
+
+Takes a queue id # and limits the returned set of templates to those which
+aren't that queue's templates.
+
+=cut
+
+sub LimitToNotInQueue {
+ my $self = shift;
+ my $queue_id = shift;
+ $self->Limit(FIELD => 'Queue',
+ VALUE => "$queue_id",
+ OPERATOR => '!='
+ );
+}
+# }}}
+
+# {{{ LimitToGlobal
+
+=head2 LimitToGlobal
+
+Takes no arguments. Limits the returned set to "Global" templates
+which can be used with any queue.
+
+=cut
+
+sub LimitToGlobal {
+ my $self = shift;
+ my $queue_id = shift;
+ $self->Limit(FIELD => 'Queue',
+ VALUE => "0",
+ OPERATOR => '='
+ );
+}
+# }}}
+
+# {{{ LimitToQueue
+
+=head2 LimitToQueue
+
+Takes a queue id # and limits the returned set of templates to that queue's
+templates
+
+=cut
+
+sub LimitToQueue {
+ my $self = shift;
+ my $queue_id = shift;
+ $self->Limit(FIELD => 'Queue',
+ VALUE => "$queue_id",
+ OPERATOR => '='
+ );
+}
+# }}}
+
+# {{{ sub NewItem
+
+=head2 NewItem
+
+Returns a new empty Template object
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+
+ use RT::Template;
+ my $item = new RT::Template($self->CurrentUser);
+ return($item);
+}
+# }}}
+
+# {{{ sub Next
+
+=head2 Next
+
+Returns the next template that this user can see.
+
+=cut
+
+sub Next {
+ my $self = shift;
+
+
+ my $templ = $self->SUPER::Next();
+ if ((defined($templ)) and (ref($templ))) {
+
+ # If it's part of a queue, and the user can read templates in
+ # that queue, or the user can globally read templates, show it
+ if ($templ->Queue && $templ->CurrentUserHasQueueRight('ShowTemplate') or
+ $templ->CurrentUser->HasRight(Object => $RT::System, Right => 'ShowTemplate')) {
+ return($templ);
+ }
+
+ #If the user doesn't have the right to show this template
+ else {
+ return($self->Next());
+ }
+ }
+ #if there never was any template
+ else {
+ return(undef);
+ }
+
+}
+# }}}
+
+1;
+
diff --git a/rt/lib/RT/Ticket.pm b/rt/lib/RT/Ticket.pm
new file mode 100755
index 0000000..b17683e
--- /dev/null
+++ b/rt/lib/RT/Ticket.pm
@@ -0,0 +1,686 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Ticket
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Ticket;
+use RT::Record;
+use RT::Queue;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Tickets');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'EffectiveId'.
+ int(11) 'Queue'.
+ varchar(16) 'Type'.
+ int(11) 'IssueStatement'.
+ int(11) 'Resolution'.
+ int(11) 'Owner'.
+ varchar(200) 'Subject' defaults to '[no subject]'.
+ int(11) 'InitialPriority'.
+ int(11) 'FinalPriority'.
+ int(11) 'Priority'.
+ int(11) 'TimeEstimated'.
+ int(11) 'TimeWorked'.
+ varchar(10) 'Status'.
+ int(11) 'TimeLeft'.
+ datetime 'Told'.
+ datetime 'Starts'.
+ datetime 'Started'.
+ datetime 'Due'.
+ datetime 'Resolved'.
+ smallint(6) 'Disabled'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ EffectiveId => '0',
+ Queue => '0',
+ Type => '',
+ IssueStatement => '0',
+ Resolution => '0',
+ Owner => '0',
+ Subject => '[no subject]',
+ InitialPriority => '0',
+ FinalPriority => '0',
+ Priority => '0',
+ TimeEstimated => '0',
+ TimeWorked => '0',
+ Status => '',
+ TimeLeft => '0',
+ Told => '',
+ Starts => '',
+ Started => '',
+ Due => '',
+ Resolved => '',
+ Disabled => '0',
+
+ @_);
+ $self->SUPER::Create(
+ EffectiveId => $args{'EffectiveId'},
+ Queue => $args{'Queue'},
+ Type => $args{'Type'},
+ IssueStatement => $args{'IssueStatement'},
+ Resolution => $args{'Resolution'},
+ Owner => $args{'Owner'},
+ Subject => $args{'Subject'},
+ InitialPriority => $args{'InitialPriority'},
+ FinalPriority => $args{'FinalPriority'},
+ Priority => $args{'Priority'},
+ TimeEstimated => $args{'TimeEstimated'},
+ TimeWorked => $args{'TimeWorked'},
+ Status => $args{'Status'},
+ TimeLeft => $args{'TimeLeft'},
+ Told => $args{'Told'},
+ Starts => $args{'Starts'},
+ Started => $args{'Started'},
+ Due => $args{'Due'},
+ Resolved => $args{'Resolved'},
+ Disabled => $args{'Disabled'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 EffectiveId
+
+Returns the current value of EffectiveId.
+(In the database, EffectiveId is stored as int(11).)
+
+
+
+=head2 SetEffectiveId VALUE
+
+
+Set EffectiveId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, EffectiveId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Queue
+
+Returns the current value of Queue.
+(In the database, Queue is stored as int(11).)
+
+
+
+=head2 SetQueue VALUE
+
+
+Set Queue to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Queue will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 QueueObj
+
+Returns the Queue Object which has the id returned by Queue
+
+
+=cut
+
+sub QueueObj {
+ my $self = shift;
+ my $Queue = RT::Queue->new($self->CurrentUser);
+ $Queue->Load($self->__Value('Queue'));
+ return($Queue);
+}
+
+=head2 Type
+
+Returns the current value of Type.
+(In the database, Type is stored as varchar(16).)
+
+
+
+=head2 SetType VALUE
+
+
+Set Type to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Type will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 IssueStatement
+
+Returns the current value of IssueStatement.
+(In the database, IssueStatement is stored as int(11).)
+
+
+
+=head2 SetIssueStatement VALUE
+
+
+Set IssueStatement to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, IssueStatement will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Resolution
+
+Returns the current value of Resolution.
+(In the database, Resolution is stored as int(11).)
+
+
+
+=head2 SetResolution VALUE
+
+
+Set Resolution to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Resolution will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Owner
+
+Returns the current value of Owner.
+(In the database, Owner is stored as int(11).)
+
+
+
+=head2 SetOwner VALUE
+
+
+Set Owner to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Owner will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Subject
+
+Returns the current value of Subject.
+(In the database, Subject is stored as varchar(200).)
+
+
+
+=head2 SetSubject VALUE
+
+
+Set Subject to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Subject will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 InitialPriority
+
+Returns the current value of InitialPriority.
+(In the database, InitialPriority is stored as int(11).)
+
+
+
+=head2 SetInitialPriority VALUE
+
+
+Set InitialPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, InitialPriority will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 FinalPriority
+
+Returns the current value of FinalPriority.
+(In the database, FinalPriority is stored as int(11).)
+
+
+
+=head2 SetFinalPriority VALUE
+
+
+Set FinalPriority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, FinalPriority will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Priority
+
+Returns the current value of Priority.
+(In the database, Priority is stored as int(11).)
+
+
+
+=head2 SetPriority VALUE
+
+
+Set Priority to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Priority will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 TimeEstimated
+
+Returns the current value of TimeEstimated.
+(In the database, TimeEstimated is stored as int(11).)
+
+
+
+=head2 SetTimeEstimated VALUE
+
+
+Set TimeEstimated to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TimeEstimated will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 TimeWorked
+
+Returns the current value of TimeWorked.
+(In the database, TimeWorked is stored as int(11).)
+
+
+
+=head2 SetTimeWorked VALUE
+
+
+Set TimeWorked to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TimeWorked will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Status
+
+Returns the current value of Status.
+(In the database, Status is stored as varchar(10).)
+
+
+
+=head2 SetStatus VALUE
+
+
+Set Status to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Status will be stored as a varchar(10).)
+
+
+=cut
+
+
+=head2 TimeLeft
+
+Returns the current value of TimeLeft.
+(In the database, TimeLeft is stored as int(11).)
+
+
+
+=head2 SetTimeLeft VALUE
+
+
+Set TimeLeft to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TimeLeft will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Told
+
+Returns the current value of Told.
+(In the database, Told is stored as datetime.)
+
+
+
+=head2 SetTold VALUE
+
+
+Set Told to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Told will be stored as a datetime.)
+
+
+=cut
+
+
+=head2 Starts
+
+Returns the current value of Starts.
+(In the database, Starts is stored as datetime.)
+
+
+
+=head2 SetStarts VALUE
+
+
+Set Starts to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Starts will be stored as a datetime.)
+
+
+=cut
+
+
+=head2 Started
+
+Returns the current value of Started.
+(In the database, Started is stored as datetime.)
+
+
+
+=head2 SetStarted VALUE
+
+
+Set Started to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Started will be stored as a datetime.)
+
+
+=cut
+
+
+=head2 Due
+
+Returns the current value of Due.
+(In the database, Due is stored as datetime.)
+
+
+
+=head2 SetDue VALUE
+
+
+Set Due to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Due will be stored as a datetime.)
+
+
+=cut
+
+
+=head2 Resolved
+
+Returns the current value of Resolved.
+(In the database, Resolved is stored as datetime.)
+
+
+
+=head2 SetResolved VALUE
+
+
+Set Resolved to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Resolved will be stored as a datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 Disabled
+
+Returns the current value of Disabled.
+(In the database, Disabled is stored as smallint(6).)
+
+
+
+=head2 SetDisabled VALUE
+
+
+Set Disabled to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Disabled will be stored as a smallint(6).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ EffectiveId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Queue =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Type =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ IssueStatement =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Resolution =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Owner =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Subject =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => '[no subject]'},
+ InitialPriority =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ FinalPriority =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Priority =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ TimeEstimated =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ TimeWorked =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Status =>
+ {read => 1, write => 1, sql_type => 12, length => 10, is_blob => 0, is_numeric => 0, type => 'varchar(10)', default => ''},
+ TimeLeft =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Told =>
+ {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Starts =>
+ {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Started =>
+ {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Due =>
+ {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Resolved =>
+ {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ Disabled =>
+ {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::Ticket_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Ticket_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Ticket_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Ticket_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Ticket_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Ticket_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Ticket_Overlay, RT::Ticket_Vendor, RT::Ticket_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Ticket_Overlay.pm b/rt/lib/RT/Ticket_Overlay.pm
new file mode 100644
index 0000000..dad9437
--- /dev/null
+++ b/rt/lib/RT/Ticket_Overlay.pm
@@ -0,0 +1,3914 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# {{{ Front Material
+
+=head1 SYNOPSIS
+
+ use RT::Ticket;
+ my $ticket = new RT::Ticket($CurrentUser);
+ $ticket->Load($ticket_id);
+
+=head1 DESCRIPTION
+
+This module lets you manipulate RT\'s ticket object.
+
+
+=head1 METHODS
+
+=begin testing
+
+use_ok ( RT::Queue);
+ok(my $testqueue = RT::Queue->new($RT::SystemUser));
+ok($testqueue->Create( Name => 'ticket tests'));
+ok($testqueue->Id != 0);
+use_ok(RT::CustomField);
+ok(my $testcf = RT::CustomField->new($RT::SystemUser));
+my ($ret, $cmsg) = $testcf->Create( Name => 'selectmulti',
+ Queue => $testqueue->id,
+ Type => 'SelectMultiple');
+ok($ret,"Created the custom field - ".$cmsg);
+($ret,$cmsg) = $testcf->AddValue ( Name => 'Value1',
+ SortOrder => '1',
+ Description => 'A testing value');
+
+ok($ret, "Added a value - ".$cmsg);
+
+ok($testcf->AddValue ( Name => 'Value2',
+ SortOrder => '2',
+ Description => 'Another testing value'));
+ok($testcf->AddValue ( Name => 'Value3',
+ SortOrder => '3',
+ Description => 'Yet Another testing value'));
+
+ok($testcf->Values->Count == 3);
+
+use_ok(RT::Ticket);
+
+my $u = RT::User->new($RT::SystemUser);
+$u->Load("root");
+ok ($u->Id, "Found the root user");
+ok(my $t = RT::Ticket->new($RT::SystemUser));
+ok(my ($id, $msg) = $t->Create( Queue => $testqueue->Id,
+ Subject => 'Testing',
+ Owner => $u->Id
+ ));
+ok($id != 0);
+ok ($t->OwnerObj->Id == $u->Id, "Root is the ticket owner");
+ok(my ($cfv, $cfm) =$t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1'));
+ok($cfv != 0, "Custom field creation didn't return an error: $cfm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 1);
+ok($t->CustomFieldValues($testcf->Id)->First &&
+ $t->CustomFieldValues($testcf->Id)->First->Content eq 'Value1');;
+
+ok(my ($cfdv, $cfdm) = $t->DeleteCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1'));
+ok ($cfdv != 0, "Deleted a custom field value: $cfdm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 0);
+
+ok(my $t2 = RT::Ticket->new($RT::SystemUser));
+ok($t2->Load($id));
+is($t2->Subject, 'Testing');
+is($t2->QueueObj->Id, $testqueue->id);
+ok($t2->OwnerObj->Id == $u->Id);
+
+my $t3 = RT::Ticket->new($RT::SystemUser);
+my ($id3, $msg3) = $t3->Create( Queue => $testqueue->Id,
+ Subject => 'Testing',
+ Owner => $u->Id);
+my ($cfv1, $cfm1) = $t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1');
+ok($cfv1 != 0, "Adding a custom field to ticket 1 is successful: $cfm");
+my ($cfv2, $cfm2) = $t3->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value2');
+ok($cfv2 != 0, "Adding a custom field to ticket 2 is successful: $cfm");
+my ($cfv3, $cfm3) = $t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value3');
+ok($cfv3 != 0, "Adding a custom field to ticket 1 is successful: $cfm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 2,
+ "This ticket has 2 custom field values");
+ok($t3->CustomFieldValues($testcf->Id)->Count == 1,
+ "This ticket has 1 custom field value");
+
+=end testing
+
+=cut
+
+
+package RT::Ticket;
+
+use strict;
+no warnings qw(redefine);
+
+use RT::Queue;
+use RT::User;
+use RT::Record;
+use RT::Links;
+use RT::Date;
+use RT::CustomFields;
+use RT::Tickets;
+use RT::Transactions;
+use RT::Reminders;
+use RT::URI::fsck_com_rt;
+use RT::URI;
+use MIME::Entity;
+
+=begin testing
+
+
+ok(require RT::Ticket, "Loading the RT::Ticket library");
+
+=end testing
+
+=cut
+
+# }}}
+
+# {{{ LINKTYPEMAP
+# A helper table for links mapping to make it easier
+# to build and parse links between tickets
+
+use vars '%LINKTYPEMAP';
+
+%LINKTYPEMAP = (
+ MemberOf => { Type => 'MemberOf',
+ Mode => 'Target', },
+ Parents => { Type => 'MemberOf',
+ Mode => 'Target', },
+ Members => { Type => 'MemberOf',
+ Mode => 'Base', },
+ Children => { Type => 'MemberOf',
+ Mode => 'Base', },
+ HasMember => { Type => 'MemberOf',
+ Mode => 'Base', },
+ RefersTo => { Type => 'RefersTo',
+ Mode => 'Target', },
+ ReferredToBy => { Type => 'RefersTo',
+ Mode => 'Base', },
+ DependsOn => { Type => 'DependsOn',
+ Mode => 'Target', },
+ DependedOnBy => { Type => 'DependsOn',
+ Mode => 'Base', },
+ MergedInto => { Type => 'MergedInto',
+ Mode => 'Target', },
+
+);
+
+# }}}
+
+# {{{ LINKDIRMAP
+# A helper table for links mapping to make it easier
+# to build and parse links between tickets
+
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
+ MemberOf => { Base => 'MemberOf',
+ Target => 'HasMember', },
+ RefersTo => { Base => 'RefersTo',
+ Target => 'ReferredToBy', },
+ DependsOn => { Base => 'DependsOn',
+ Target => 'DependedOnBy', },
+ MergedInto => { Base => 'MergedInto',
+ Target => 'MergedInto', },
+
+);
+
+# }}}
+
+sub LINKTYPEMAP { return \%LINKTYPEMAP }
+sub LINKDIRMAP { return \%LINKDIRMAP }
+
+# {{{ sub Load
+
+=head2 Load
+
+Takes a single argument. This can be a ticket id, ticket alias or
+local ticket uri. If the ticket can't be loaded, returns undef.
+Otherwise, returns the ticket id.
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $id = shift;
+
+ #TODO modify this routine to look at EffectiveId and do the recursive load
+ # thing. be careful to cache all the interim tickets we try so we don't loop forever.
+
+
+ #If it's a local URI, turn it into a ticket id
+ if ( $RT::TicketBaseURI && $id =~ /^$RT::TicketBaseURI(\d+)$/ ) {
+ $id = $1;
+ }
+
+ #If it's a remote URI, we're going to punt for now
+ elsif ( $id =~ '://' ) {
+ return (undef);
+ }
+
+ #If we have an integer URI, load the ticket
+ if ( $id =~ /^\d+$/ ) {
+ my ($ticketid,$msg) = $self->LoadById($id);
+
+ unless ($self->Id) {
+ $RT::Logger->crit("$self tried to load a bogus ticket: $id\n");
+ return (undef);
+ }
+ }
+
+ #It's not a URI. It's not a numerical ticket ID. Punt!
+ else {
+ $RT::Logger->warning("Tried to load a bogus ticket id: '$id'");
+ return (undef);
+ }
+
+ #If we're merged, resolve the merge.
+ if ( ( $self->EffectiveId ) and ( $self->EffectiveId != $self->Id ) ) {
+ $RT::Logger->debug ("We found a merged ticket.". $self->id ."/".$self->EffectiveId);
+ return ( $self->Load( $self->EffectiveId ) );
+ }
+
+ #Ok. we're loaded. lets get outa here.
+ return ( $self->Id );
+
+}
+
+# }}}
+
+# {{{ sub LoadByURI
+
+=head2 LoadByURI
+
+Given a local ticket URI, loads the specified ticket.
+
+=cut
+
+sub LoadByURI {
+ my $self = shift;
+ my $uri = shift;
+
+ if ( $uri =~ /^$RT::TicketBaseURI(\d+)$/ ) {
+ my $id = $1;
+ return ( $self->Load($id) );
+ }
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
+# {{{ sub Create
+
+=head2 Create (ARGS)
+
+Arguments: ARGS is a hash of named parameters. Valid parameters are:
+
+ id
+ Queue - Either a Queue object or a Queue Name
+ Requestor - A reference to a list of email addresses or RT user Names
+ Cc - A reference to a list of email addresses or Names
+ AdminCc - A reference to a list of email addresses or Names
+ Type -- The ticket\'s type. ignore this for now
+ Owner -- This ticket\'s owner. either an RT::User object or this user\'s id
+ Subject -- A string describing the subject of the ticket
+ Priority -- an integer from 0 to 99
+ InitialPriority -- an integer from 0 to 99
+ FinalPriority -- an integer from 0 to 99
+ Status -- any valid status (Defined in RT::Queue)
+ TimeEstimated -- an integer. estimated time for this task in minutes
+ TimeWorked -- an integer. time worked so far in minutes
+ TimeLeft -- an integer. time remaining in minutes
+ Starts -- an ISO date describing the ticket\'s start date and time in GMT
+ Due -- an ISO date describing the ticket\'s due date and time in GMT
+ MIMEObj -- a MIME::Entity object with the content of the initial ticket request.
+ CustomField-<n> -- a scalar or array of values for the customfield with the id <n>
+
+Ticket links can be set up during create by passing the link type as a hask key and
+the ticket id to be linked to as a value (or a URI when linking to other objects).
+Multiple links of the same type can be created by passing an array ref. For example:
+
+ Parent => 45,
+ DependsOn => [ 15, 22 ],
+ RefersTo => 'http://www.bestpractical.com',
+
+Supported link types are C<MemberOf>, C<HasMember>, C<RefersTo>, C<ReferredToBy>,
+C<DependsOn> and C<DependedOnBy>. Also, C<Parents> is alias for C<MemberOf> and
+C<Members> and C<Children> are aliases for C<HasMember>.
+
+Returns: TICKETID, Transaction Object, Error Message
+
+=begin testing
+
+my $t = RT::Ticket->new($RT::SystemUser);
+
+ok( $t->Create(Queue => 'General', Due => '2002-05-21 00:00:00', ReferredToBy => 'http://www.cpan.org', RefersTo => 'http://fsck.com', Subject => 'This is a subject'), "Ticket Created");
+
+ok ( my $id = $t->Id, "Got ticket id");
+ok ($t->RefersTo->First->Target =~ /fsck.com/, "Got refers to");
+ok ($t->ReferredToBy->First->Base =~ /cpan.org/, "Got referredtoby");
+ok ($t->ResolvedObj->Unix == -1, "It hasn't been resolved - ". $t->ResolvedObj->Unix);
+
+=end testing
+
+=cut
+
+sub Create {
+ my $self = shift;
+
+ my %args = (
+ id => undef,
+ EffectiveId => undef,
+ Queue => undef,
+ Requestor => undef,
+ Cc => undef,
+ AdminCc => undef,
+ Type => 'ticket',
+ Owner => undef,
+ Subject => '',
+ InitialPriority => undef,
+ FinalPriority => undef,
+ Priority => undef,
+ Status => 'new',
+ TimeWorked => "0",
+ TimeLeft => 0,
+ TimeEstimated => 0,
+ Due => undef,
+ Starts => undef,
+ Started => undef,
+ Resolved => undef,
+ MIMEObj => undef,
+ _RecordTransaction => 1,
+ @_
+ );
+
+ my ( $ErrStr, $Owner, $resolved );
+ my (@non_fatal_errors);
+
+ my $QueueObj = RT::Queue->new($RT::SystemUser);
+
+ if ( ( defined( $args{'Queue'} ) ) && ( !ref( $args{'Queue'} ) ) ) {
+ $QueueObj->Load( $args{'Queue'} );
+ }
+ elsif ( ref( $args{'Queue'} ) eq 'RT::Queue' ) {
+ $QueueObj->Load( $args{'Queue'}->Id );
+ }
+ else {
+ $RT::Logger->debug( $args{'Queue'} . " not a recognised queue object." );
+ }
+
+ #Can't create a ticket without a queue.
+ unless ( defined($QueueObj) && $QueueObj->Id ) {
+ $RT::Logger->debug("$self No queue given for ticket creation.");
+ return ( 0, 0, $self->loc('Could not create ticket. Queue not set') );
+ }
+
+ #Now that we have a queue, Check the ACLS
+ unless (
+ $self->CurrentUser->HasRight(
+ Right => 'CreateTicket',
+ Object => $QueueObj
+ )
+ )
+ {
+ return (
+ 0, 0,
+ $self->loc( "No permission to create tickets in the queue '[_1]'", $QueueObj->Name));
+ }
+
+ unless ( $QueueObj->IsValidStatus( $args{'Status'} ) ) {
+ return ( 0, 0, $self->loc('Invalid value for status') );
+ }
+
+ #Since we have a queue, we can set queue defaults
+ #Initial Priority
+
+ # If there's no queue default initial priority and it's not set, set it to 0
+ $args{'InitialPriority'} = ( $QueueObj->InitialPriority || 0 )
+ unless ( $args{'InitialPriority'} );
+
+ #Final priority
+
+ # If there's no queue default final priority and it's not set, set it to 0
+ $args{'FinalPriority'} = ( $QueueObj->FinalPriority || 0 )
+ unless ( $args{'FinalPriority'} );
+
+ # Priority may have changed from InitialPriority, for the case
+ # where we're importing tickets (eg, from an older RT version.)
+ my $priority = $args{'Priority'} || $args{'InitialPriority'};
+
+ # {{{ Dates
+ #TODO we should see what sort of due date we're getting, rather +
+ # than assuming it's in ISO format.
+
+ #Set the due date. if we didn't get fed one, use the queue default due in
+ my $Due = new RT::Date( $self->CurrentUser );
+
+ if ( $args{'Due'} ) {
+ $Due->Set( Format => 'ISO', Value => $args{'Due'} );
+ }
+ elsif ( my $due_in = $QueueObj->DefaultDueIn ) {
+ $Due->SetToNow;
+ $Due->AddDays( $due_in );
+ }
+
+ my $Starts = new RT::Date( $self->CurrentUser );
+ if ( defined $args{'Starts'} ) {
+ $Starts->Set( Format => 'ISO', Value => $args{'Starts'} );
+ }
+
+ my $Started = new RT::Date( $self->CurrentUser );
+ if ( defined $args{'Started'} ) {
+ $Started->Set( Format => 'ISO', Value => $args{'Started'} );
+ }
+
+ my $Resolved = new RT::Date( $self->CurrentUser );
+ if ( defined $args{'Resolved'} ) {
+ $Resolved->Set( Format => 'ISO', Value => $args{'Resolved'} );
+ }
+
+ #If the status is an inactive status, set the resolved date
+ if ( $QueueObj->IsInactiveStatus( $args{'Status'} ) && !$args{'Resolved'} )
+ {
+ $RT::Logger->debug( "Got a ". $args{'Status'}
+ ." ticket with undefined resolved date. Setting to now."
+ );
+ $Resolved->SetToNow;
+ }
+
+ # }}}
+
+ # {{{ Dealing with time fields
+
+ $args{'TimeEstimated'} = 0 unless defined $args{'TimeEstimated'};
+ $args{'TimeWorked'} = 0 unless defined $args{'TimeWorked'};
+ $args{'TimeLeft'} = 0 unless defined $args{'TimeLeft'};
+
+ # }}}
+
+ # {{{ Deal with setting the owner
+
+ if ( ref( $args{'Owner'} ) eq 'RT::User' ) {
+ $Owner = $args{'Owner'};
+ }
+
+ #If we've been handed something else, try to load the user.
+ elsif ( $args{'Owner'} ) {
+ $Owner = RT::User->new( $self->CurrentUser );
+ $Owner->Load( $args{'Owner'} );
+
+ push( @non_fatal_errors,
+ $self->loc("Owner could not be set.") . " "
+ . $self->loc( "User '[_1]' could not be found.", $args{'Owner'} )
+ )
+ unless ( $Owner->Id );
+ }
+
+ #If we have a proposed owner and they don't have the right
+ #to own a ticket, scream about it and make them not the owner
+ if (
+ ( defined($Owner) )
+ and ( $Owner->Id )
+ and ( $Owner->Id != $RT::Nobody->Id )
+ and (
+ !$Owner->HasRight(
+ Object => $QueueObj,
+ Right => 'OwnTicket'
+ )
+ )
+ )
+ {
+
+ $RT::Logger->warning( "User "
+ . $Owner->Name . "("
+ . $Owner->id
+ . ") was proposed "
+ . "as a ticket owner but has no rights to own "
+ . "tickets in "
+ . $QueueObj->Name );
+
+ push @non_fatal_errors,
+ $self->loc( "Owner '[_1]' does not have rights to own this ticket.",
+ $Owner->Name
+ );
+
+ $Owner = undef;
+ }
+
+ #If we haven't been handed a valid owner, make it nobody.
+ unless ( defined($Owner) && $Owner->Id ) {
+ $Owner = new RT::User( $self->CurrentUser );
+ $Owner->Load( $RT::Nobody->Id );
+ }
+
+ # }}}
+
+# We attempt to load or create each of the people who might have a role for this ticket
+# _outside_ the transaction, so we don't get into ticket creation races
+ foreach my $type ( "Cc", "AdminCc", "Requestor" ) {
+ next unless ( defined $args{$type} );
+ foreach my $watcher (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ my $user = RT::User->new($RT::SystemUser);
+ $user->LoadOrCreateByEmail($watcher)
+ if ( $watcher && $watcher !~ /^\d+$/ );
+ }
+ }
+
+ $RT::Handle->BeginTransaction();
+
+ my %params = (
+ Queue => $QueueObj->Id,
+ Owner => $Owner->Id,
+ Subject => $args{'Subject'},
+ InitialPriority => $args{'InitialPriority'},
+ FinalPriority => $args{'FinalPriority'},
+ Priority => $priority,
+ Status => $args{'Status'},
+ TimeWorked => $args{'TimeWorked'},
+ TimeEstimated => $args{'TimeEstimated'},
+ TimeLeft => $args{'TimeLeft'},
+ Type => $args{'Type'},
+ Starts => $Starts->ISO,
+ Started => $Started->ISO,
+ Resolved => $Resolved->ISO,
+ Due => $Due->ISO
+ );
+
+# Parameters passed in during an import that we probably don't want to touch, otherwise
+ foreach my $attr qw(id Creator Created LastUpdated LastUpdatedBy) {
+ $params{$attr} = $args{$attr} if ( $args{$attr} );
+ }
+
+ # Delete null integer parameters
+ foreach my $attr
+ qw(TimeWorked TimeLeft TimeEstimated InitialPriority FinalPriority) {
+ delete $params{$attr}
+ unless ( exists $params{$attr} && $params{$attr} );
+ }
+
+ # Delete the time worked if we're counting it in the transaction
+ delete $params{TimeWorked} if $args{'_RecordTransaction'};
+
+ my ($id,$ticket_message) = $self->SUPER::Create( %params);
+ unless ($id) {
+ $RT::Logger->crit( "Couldn't create a ticket: " . $ticket_message );
+ $RT::Handle->Rollback();
+ return ( 0, 0,
+ $self->loc("Ticket could not be created due to an internal error")
+ );
+ }
+
+ #Set the ticket's effective ID now that we've created it.
+ my ( $val, $msg ) = $self->__Set(
+ Field => 'EffectiveId',
+ Value => ( $args{'EffectiveId'} || $id )
+ );
+
+ unless ($val) {
+ $RT::Logger->crit("$self ->Create couldn't set EffectiveId: $msg\n");
+ $RT::Handle->Rollback();
+ return ( 0, 0,
+ $self->loc("Ticket could not be created due to an internal error")
+ );
+ }
+
+ my $create_groups_ret = $self->_CreateTicketGroups();
+ unless ($create_groups_ret) {
+ $RT::Logger->crit( "Couldn't create ticket groups for ticket "
+ . $self->Id
+ . ". aborting Ticket creation." );
+ $RT::Handle->Rollback();
+ return ( 0, 0,
+ $self->loc("Ticket could not be created due to an internal error")
+ );
+ }
+
+# Set the owner in the Groups table
+# We denormalize it into the Ticket table too because doing otherwise would
+# kill performance, bigtime. It gets kept in lockstep thanks to the magic of transactionalization
+
+ $self->OwnerGroup->_AddMember(
+ PrincipalId => $Owner->PrincipalId,
+ InsideTransaction => 1
+ );
+
+ # {{{ Deal with setting up watchers
+
+ foreach my $type ( "Cc", "AdminCc", "Requestor" ) {
+ next unless ( defined $args{$type} );
+ foreach my $watcher (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+
+ # If there is an empty entry in the list, let's get out of here.
+ next unless $watcher;
+
+ # we reason that all-digits number must be a principal id, not email
+ # this is the only way to can add
+ my $field = 'Email';
+ $field = 'PrincipalId' if $watcher =~ /^\d+$/;
+
+ my ( $wval, $wmsg );
+
+ if ( $type eq 'AdminCc' ) {
+
+ # Note that we're using AddWatcher, rather than _AddWatcher, as we
+ # actually _want_ that ACL check. Otherwise, random ticket creators
+ # could make themselves adminccs and maybe get ticket rights. that would
+ # be poor
+ ( $wval, $wmsg ) = $self->AddWatcher(
+ Type => $type,
+ $field => $watcher,
+ Silent => 1
+ );
+ }
+ else {
+ ( $wval, $wmsg ) = $self->_AddWatcher(
+ Type => $type,
+ $field => $watcher,
+ Silent => 1
+ );
+ }
+
+ push @non_fatal_errors, $wmsg unless ($wval);
+ }
+ }
+
+ # }}}
+ # {{{ Deal with setting up links
+
+ foreach my $type ( keys %LINKTYPEMAP ) {
+ next unless ( defined $args{$type} );
+ foreach my $link (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ # Check rights on the other end of the link if we must
+ # then run _AddLink that doesn't check for ACLs
+ if ( $RT::StrictLinkACL ) {
+ my ($val, $msg, $obj) = $self->__GetTicketFromURI( URI => $link );
+ unless ( $val ) {
+ push @non_fatal_errors, $msg;
+ next;
+ }
+ if ( $obj && !$obj->CurrentUserHasRight('ModifyTicket') ) {
+ push @non_fatal_errors, $self->loc('Linking. Permission denied');
+ next;
+ }
+ }
+
+ my ( $wval, $wmsg ) = $self->_AddLink(
+ Type => $LINKTYPEMAP{$type}->{'Type'},
+ $LINKTYPEMAP{$type}->{'Mode'} => $link,
+ Silent => 1
+ );
+
+ push @non_fatal_errors, $wmsg unless ($wval);
+ }
+ }
+
+ # }}}
+
+ # {{{ Deal with auto-customer association
+
+ #unless we already have (a) customer(s)...
+ unless ( $self->Customers->Count ) {
+
+ #first find any requestors with emails but *without* customer targets
+ my @NoCust_Requestors =
+ grep { $_->EmailAddress && ! $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ for my $Requestor (@NoCust_Requestors) {
+
+ #perhaps the stuff in here should be in a User method??
+ my @Customers =
+ &RT::URI::freeside::email_search( email=>$Requestor->EmailAddress );
+
+ foreach my $custnum ( map $_->{'custnum'}, @Customers ) {
+
+ ## false laziness w/RT/Interface/Web_Vendor.pm
+ my @link = ( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+
+ my( $val, $msg ) = $Requestor->_AddLink(@link);
+ #XXX should do something with $msg# push @non_fatal_errors, $msg;
+
+ }
+
+ }
+
+ #find any requestors with customer targets
+
+ my %cust_target = ();
+
+ my @Requestors =
+ grep { $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ foreach my $Requestor ( @Requestors ) {
+ foreach my $cust_link ( @{ $Requestor->Customers->ItemsArrayRef } ) {
+ $cust_target{ $cust_link->Target } = 1;
+ }
+ }
+
+ #and then auto-associate this ticket with those customers
+
+ foreach my $cust_target ( keys %cust_target ) {
+
+ my @link = ( 'Type' => 'MemberOf',
+ #'Target' => "freeside://freeside/cust_main/$custnum",
+ 'Target' => $cust_target,
+ );
+
+ my( $val, $msg ) = $self->_AddLink(@link);
+ push @non_fatal_errors, $msg;
+
+ }
+
+ }
+
+ # }}}
+
+ # {{{ Add all the custom fields
+
+ foreach my $arg ( keys %args ) {
+ next unless ( $arg =~ /^CustomField-(\d+)$/i );
+ my $cfid = $1;
+ foreach
+ my $value ( UNIVERSAL::isa( $args{$arg} => 'ARRAY' ) ? @{ $args{$arg} } : ( $args{$arg} ) )
+ {
+ next unless ( length($value) );
+
+ # Allow passing in uploaded LargeContent etc by hash reference
+ $self->_AddCustomFieldValue(
+ (UNIVERSAL::isa( $value => 'HASH' )
+ ? %$value
+ : (Value => $value)
+ ),
+ Field => $cfid,
+ RecordTransaction => 0,
+ );
+ }
+ }
+
+ # }}}
+
+ if ( $args{'_RecordTransaction'} ) {
+
+ # {{{ Add a transaction for the create
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => "Create",
+ TimeTaken => $args{'TimeWorked'},
+ MIMEObj => $args{'MIMEObj'}
+ );
+
+ if ( $self->Id && $Trans ) {
+
+ $TransObj->UpdateCustomFields(ARGSRef => \%args);
+
+ $RT::Logger->info( "Ticket " . $self->Id . " created in queue '" . $QueueObj->Name . "' by " . $self->CurrentUser->Name );
+ $ErrStr = $self->loc( "Ticket [_1] created in queue '[_2]'", $self->Id, $QueueObj->Name );
+ $ErrStr = join( "\n", $ErrStr, @non_fatal_errors );
+ }
+ else {
+ $RT::Handle->Rollback();
+
+ $ErrStr = join( "\n", $ErrStr, @non_fatal_errors );
+ $RT::Logger->error("Ticket couldn't be created: $ErrStr");
+ return ( 0, 0, $self->loc( "Ticket could not be created due to an internal error"));
+ }
+
+ $RT::Handle->Commit();
+ return ( $self->Id, $TransObj->Id, $ErrStr );
+
+ # }}}
+ }
+ else {
+
+ # Not going to record a transaction
+ $RT::Handle->Commit();
+ $ErrStr = $self->loc( "Ticket [_1] created in queue '[_2]'", $self->Id, $QueueObj->Name );
+ $ErrStr = join( "\n", $ErrStr, @non_fatal_errors );
+ return ( $self->Id, 0, $ErrStr );
+
+ }
+}
+
+
+# }}}
+
+
+# {{{ UpdateFrom822
+
+=head2 UpdateFrom822 $MESSAGE
+
+Takes an RFC822 format message as a string and uses it to make a bunch of changes to a ticket.
+Returns an um. ask me again when the code exists
+
+
+=begin testing
+
+my $simple_update = <<EOF;
+Subject: target
+AddRequestor: jesse\@example.com
+EOF
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id,$msg) =$ticket->Create(Subject => 'first', Queue => 'general');
+ok($ticket->Id, "Created the test ticket - ".$id ." - ".$msg);
+$ticket->UpdateFrom822($simple_update);
+is($ticket->Subject, 'target', "changed the subject");
+my $jesse = RT::User->new($RT::SystemUser);
+$jesse->LoadByEmail('jesse@example.com');
+ok ($jesse->Id, "There's a user for jesse");
+ok($ticket->Requestors->HasMember( $jesse->PrincipalObj), "It has the jesse principal object as a requestor ");
+
+=end testing
+
+
+=cut
+
+sub UpdateFrom822 {
+ my $self = shift;
+ my $content = shift;
+ my %args = $self->_Parse822HeadersForAttributes($content);
+
+
+ my %ticketargs = (
+ Queue => $args{'queue'},
+ Subject => $args{'subject'},
+ Status => $args{'status'},
+ Due => $args{'due'},
+ Starts => $args{'starts'},
+ Started => $args{'started'},
+ Resolved => $args{'resolved'},
+ Owner => $args{'owner'},
+ Requestor => $args{'requestor'},
+ Cc => $args{'cc'},
+ AdminCc => $args{'admincc'},
+ TimeWorked => $args{'timeworked'},
+ TimeEstimated => $args{'timeestimated'},
+ TimeLeft => $args{'timeleft'},
+ InitialPriority => $args{'initialpriority'},
+ Priority => $args{'priority'},
+ FinalPriority => $args{'finalpriority'},
+ Type => $args{'type'},
+ DependsOn => $args{'dependson'},
+ DependedOnBy => $args{'dependedonby'},
+ RefersTo => $args{'refersto'},
+ ReferredToBy => $args{'referredtoby'},
+ Members => $args{'members'},
+ MemberOf => $args{'memberof'},
+ MIMEObj => $args{'mimeobj'}
+ );
+
+ foreach my $type qw(Requestor Cc Admincc) {
+
+ foreach my $action ( 'Add', 'Del', '' ) {
+
+ my $lctag = lc($action) . lc($type);
+ foreach my $list ( $args{$lctag}, $args{ $lctag . 's' } ) {
+
+ foreach my $entry ( ref($list) ? @{$list} : ($list) ) {
+ push @{$ticketargs{ $action . $type }} , split ( /\s*,\s*/, $entry );
+ }
+
+ }
+
+ # Todo: if we're given an explicit list, transmute it into a list of adds/deletes
+
+ }
+ }
+
+ # Add custom field entries to %ticketargs.
+ # TODO: allow named custom fields
+ map {
+ /^customfield-(\d+)$/
+ && ( $ticketargs{ "CustomField-" . $1 } = $args{$_} );
+ } keys(%args);
+
+# for each ticket we've been told to update, iterate through the set of
+# rfc822 headers and perform that update to the ticket.
+
+
+ # {{{ Set basic fields
+ my @attribs = qw(
+ Subject
+ FinalPriority
+ Priority
+ TimeEstimated
+ TimeWorked
+ TimeLeft
+ Status
+ Queue
+ Type
+ );
+
+
+ # Resolve the queue from a name to a numeric id.
+ if ( $ticketargs{'Queue'} and ( $ticketargs{'Queue'} !~ /^(\d+)$/ ) ) {
+ my $tempqueue = RT::Queue->new($RT::SystemUser);
+ $tempqueue->Load( $ticketargs{'Queue'} );
+ $ticketargs{'Queue'} = $tempqueue->Id() if ( $tempqueue->id );
+ }
+
+ my @results;
+
+ foreach my $attribute (@attribs) {
+ my $value = $ticketargs{$attribute};
+
+ if ( $value ne $self->$attribute() ) {
+
+ my $method = "Set$attribute";
+ my ( $code, $msg ) = $self->$method($value);
+
+ push @results, $self->loc($attribute) . ': ' . $msg;
+
+ }
+ }
+
+ # We special case owner changing, so we can use ForceOwnerChange
+ if ( $ticketargs{'Owner'} && ( $self->Owner != $ticketargs{'Owner'} ) ) {
+ my $ChownType = "Give";
+ $ChownType = "Force" if ( $ticketargs{'ForceOwnerChange'} );
+
+ my ( $val, $msg ) = $self->SetOwner( $ticketargs{'Owner'}, $ChownType );
+ push ( @results, $msg );
+ }
+
+ # }}}
+# Deal with setting watchers
+
+
+# Acceptable arguments:
+# Requestor
+# Requestors
+# AddRequestor
+# AddRequestors
+# DelRequestor
+
+ foreach my $type qw(Requestor Cc AdminCc) {
+
+ # If we've been given a number of delresses to del, do it.
+ foreach my $address (@{$ticketargs{'Del'.$type}}) {
+ my ($id, $msg) = $self->DeleteWatcher( Type => $type, Email => $address);
+ push (@results, $msg) ;
+ }
+
+ # If we've been given a number of addresses to add, do it.
+ foreach my $address (@{$ticketargs{'Add'.$type}}) {
+ $RT::Logger->debug("Adding $address as a $type");
+ my ($id, $msg) = $self->AddWatcher( Type => $type, Email => $address);
+ push (@results, $msg) ;
+
+ }
+
+
+}
+
+
+}
+# }}}
+
+# {{{ _Parse822HeadersForAttributes Content
+
+=head2 _Parse822HeadersForAttributes Content
+
+Takes an RFC822 style message and parses its attributes into a hash.
+
+=cut
+
+sub _Parse822HeadersForAttributes {
+ my $self = shift;
+ my $content = shift;
+ my %args;
+
+ my @lines = ( split ( /\n/, $content ) );
+ while ( defined( my $line = shift @lines ) ) {
+ if ( $line =~ /^(.*?):(?:\s+(.*))?$/ ) {
+ my $value = $2;
+ my $tag = lc($1);
+
+ $tag =~ s/-//g;
+ if ( defined( $args{$tag} ) )
+ { #if we're about to get a second value, make it an array
+ $args{$tag} = [ $args{$tag} ];
+ }
+ if ( ref( $args{$tag} ) )
+ { #If it's an array, we want to push the value
+ push @{ $args{$tag} }, $value;
+ }
+ else { #if there's nothing there, just set the value
+ $args{$tag} = $value;
+ }
+ } elsif ($line =~ /^$/) {
+
+ #TODO: this won't work, since "" isn't of the form "foo:value"
+
+ while ( defined( my $l = shift @lines ) ) {
+ push @{ $args{'content'} }, $l;
+ }
+ }
+
+ }
+
+ foreach my $date qw(due starts started resolved) {
+ my $dateobj = RT::Date->new($RT::SystemUser);
+ if ( $args{$date} =~ /^\d+$/ ) {
+ $dateobj->Set( Format => 'unix', Value => $args{$date} );
+ }
+ else {
+ $dateobj->Set( Format => 'unknown', Value => $args{$date} );
+ }
+ $args{$date} = $dateobj->ISO;
+ }
+ $args{'mimeobj'} = MIME::Entity->new();
+ $args{'mimeobj'}->build(
+ Type => ( $args{'contenttype'} || 'text/plain' ),
+ Data => ($args{'content'} || '')
+ );
+
+ return (%args);
+}
+
+# }}}
+
+# {{{ sub Import
+
+=head2 Import PARAMHASH
+
+Import a ticket.
+Doesn\'t create a transaction.
+Doesn\'t supply queue defaults, etc.
+
+Returns: TICKETID
+
+=cut
+
+sub Import {
+ my $self = shift;
+ my ( $ErrStr, $QueueObj, $Owner );
+
+ my %args = (
+ id => undef,
+ EffectiveId => undef,
+ Queue => undef,
+ Requestor => undef,
+ Type => 'ticket',
+ Owner => $RT::Nobody->Id,
+ Subject => '[no subject]',
+ InitialPriority => undef,
+ FinalPriority => undef,
+ Status => 'new',
+ TimeWorked => "0",
+ Due => undef,
+ Created => undef,
+ Updated => undef,
+ Resolved => undef,
+ Told => undef,
+ @_
+ );
+
+ if ( ( defined( $args{'Queue'} ) ) && ( !ref( $args{'Queue'} ) ) ) {
+ $QueueObj = RT::Queue->new($RT::SystemUser);
+ $QueueObj->Load( $args{'Queue'} );
+
+ #TODO error check this and return 0 if it\'s not loading properly +++
+ }
+ elsif ( ref( $args{'Queue'} ) eq 'RT::Queue' ) {
+ $QueueObj = RT::Queue->new($RT::SystemUser);
+ $QueueObj->Load( $args{'Queue'}->Id );
+ }
+ else {
+ $RT::Logger->debug(
+ "$self " . $args{'Queue'} . " not a recognised queue object." );
+ }
+
+ #Can't create a ticket without a queue.
+ unless ( defined($QueueObj) and $QueueObj->Id ) {
+ $RT::Logger->debug("$self No queue given for ticket creation.");
+ return ( 0, $self->loc('Could not create ticket. Queue not set') );
+ }
+
+ #Now that we have a queue, Check the ACLS
+ unless (
+ $self->CurrentUser->HasRight(
+ Right => 'CreateTicket',
+ Object => $QueueObj
+ )
+ )
+ {
+ return ( 0,
+ $self->loc("No permission to create tickets in the queue '[_1]'"
+ , $QueueObj->Name));
+ }
+
+ # {{{ Deal with setting the owner
+
+ # Attempt to take user object, user name or user id.
+ # Assign to nobody if lookup fails.
+ if ( defined( $args{'Owner'} ) ) {
+ if ( ref( $args{'Owner'} ) ) {
+ $Owner = $args{'Owner'};
+ }
+ else {
+ $Owner = new RT::User( $self->CurrentUser );
+ $Owner->Load( $args{'Owner'} );
+ if ( !defined( $Owner->id ) ) {
+ $Owner->Load( $RT::Nobody->id );
+ }
+ }
+ }
+
+ #If we have a proposed owner and they don't have the right
+ #to own a ticket, scream about it and make them not the owner
+ if (
+ ( defined($Owner) )
+ and ( $Owner->Id != $RT::Nobody->Id )
+ and (
+ !$Owner->HasRight(
+ Object => $QueueObj,
+ Right => 'OwnTicket'
+ )
+ )
+ )
+ {
+
+ $RT::Logger->warning( "$self user "
+ . $Owner->Name . "("
+ . $Owner->id
+ . ") was proposed "
+ . "as a ticket owner but has no rights to own "
+ . "tickets in '"
+ . $QueueObj->Name . "'\n" );
+
+ $Owner = undef;
+ }
+
+ #If we haven't been handed a valid owner, make it nobody.
+ unless ( defined($Owner) ) {
+ $Owner = new RT::User( $self->CurrentUser );
+ $Owner->Load( $RT::Nobody->UserObj->Id );
+ }
+
+ # }}}
+
+ unless ( $self->ValidateStatus( $args{'Status'} ) ) {
+ return ( 0, $self->loc("'[_1]' is an invalid value for status", $args{'Status'}) );
+ }
+
+ $self->{'_AccessibleCache'}{Created} = { 'read' => 1, 'write' => 1 };
+ $self->{'_AccessibleCache'}{Creator} = { 'read' => 1, 'auto' => 1 };
+ $self->{'_AccessibleCache'}{LastUpdated} = { 'read' => 1, 'write' => 1 };
+ $self->{'_AccessibleCache'}{LastUpdatedBy} = { 'read' => 1, 'auto' => 1 };
+
+ # If we're coming in with an id, set that now.
+ my $EffectiveId = undef;
+ if ( $args{'id'} ) {
+ $EffectiveId = $args{'id'};
+
+ }
+
+ my $id = $self->SUPER::Create(
+ id => $args{'id'},
+ EffectiveId => $EffectiveId,
+ Queue => $QueueObj->Id,
+ Owner => $Owner->Id,
+ Subject => $args{'Subject'}, # loc
+ InitialPriority => $args{'InitialPriority'}, # loc
+ FinalPriority => $args{'FinalPriority'}, # loc
+ Priority => $args{'InitialPriority'}, # loc
+ Status => $args{'Status'}, # loc
+ TimeWorked => $args{'TimeWorked'}, # loc
+ Type => $args{'Type'}, # loc
+ Created => $args{'Created'}, # loc
+ Told => $args{'Told'}, # loc
+ LastUpdated => $args{'Updated'}, # loc
+ Resolved => $args{'Resolved'}, # loc
+ Due => $args{'Due'}, # loc
+ );
+
+ # If the ticket didn't have an id
+ # Set the ticket's effective ID now that we've created it.
+ if ( $args{'id'} ) {
+ $self->Load( $args{'id'} );
+ }
+ else {
+ my ( $val, $msg ) =
+ $self->__Set( Field => 'EffectiveId', Value => $id );
+
+ unless ($val) {
+ $RT::Logger->err(
+ $self . "->Import couldn't set EffectiveId: $msg\n" );
+ }
+ }
+
+ my $create_groups_ret = $self->_CreateTicketGroups();
+ unless ($create_groups_ret) {
+ $RT::Logger->crit(
+ "Couldn't create ticket groups for ticket " . $self->Id );
+ }
+
+ $self->OwnerGroup->_AddMember( PrincipalId => $Owner->PrincipalId );
+
+ my $watcher;
+ foreach $watcher ( @{ $args{'Cc'} } ) {
+ $self->_AddWatcher( Type => 'Cc', Email => $watcher, Silent => 1 );
+ }
+ foreach $watcher ( @{ $args{'AdminCc'} } ) {
+ $self->_AddWatcher( Type => 'AdminCc', Email => $watcher,
+ Silent => 1 );
+ }
+ foreach $watcher ( @{ $args{'Requestor'} } ) {
+ $self->_AddWatcher( Type => 'Requestor', Email => $watcher,
+ Silent => 1 );
+ }
+
+ return ( $self->Id, $ErrStr );
+}
+
+# }}}
+
+# {{{ Routines dealing with watchers.
+
+# {{{ _CreateTicketGroups
+
+=head2 _CreateTicketGroups
+
+Create the ticket groups and links for this ticket.
+This routine expects to be called from Ticket->Create _inside of a transaction_
+
+It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
+
+It will return true on success and undef on failure.
+
+=begin testing
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id, $msg) = $ticket->Create(Subject => "Foo",
+ Owner => $RT::SystemUser->Id,
+ Status => 'open',
+ Requestor => ['jesse@example.com'],
+ Queue => '1'
+ );
+ok ($id, "Ticket $id was created");
+ok(my $group = RT::Group->new($RT::SystemUser));
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Requestor'));
+ok ($group->Id, "Found the requestors object for this ticket");
+
+ok(my $jesse = RT::User->new($RT::SystemUser), "Creating a jesse rt::user");
+$jesse->LoadByEmail('jesse@example.com');
+ok($jesse->Id, "Found the jesse rt user");
+
+
+ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $jesse->PrincipalId), "The ticket actually has jesse at fsck.com as a requestor");
+ok ((my $add_id, $add_msg) = $ticket->AddWatcher(Type => 'Requestor', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok ($add_id, "Add succeeded: ($add_msg)");
+ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user");
+$bob->LoadByEmail('bob@fsck.com');
+ok($bob->Id, "Found the bob rt user");
+ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $bob->PrincipalId), "The ticket actually has bob at fsck.com as a requestor");;
+ok ((my $add_id, $add_msg) = $ticket->DeleteWatcher(Type =>'Requestor', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok (!$ticket->IsWatcher(Type => 'Requestor', Principal => $bob->PrincipalId), "The ticket no longer has bob at fsck.com as a requestor");;
+
+
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Cc'));
+ok ($group->Id, "Found the cc object for this ticket");
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'AdminCc'));
+ok ($group->Id, "Found the AdminCc object for this ticket");
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Owner'));
+ok ($group->Id, "Found the Owner object for this ticket");
+ok($group->HasMember($RT::SystemUser->UserObj->PrincipalObj), "the owner group has the member 'RT_System'");
+
+=end testing
+
+=cut
+
+
+sub _CreateTicketGroups {
+ my $self = shift;
+
+ my @types = qw(Requestor Owner Cc AdminCc);
+
+ foreach my $type (@types) {
+ my $type_obj = RT::Group->new($self->CurrentUser);
+ my ($id, $msg) = $type_obj->CreateRoleGroup(Domain => 'RT::Ticket-Role',
+ Instance => $self->Id,
+ Type => $type);
+ unless ($id) {
+ $RT::Logger->error("Couldn't create a ticket group of type '$type' for ticket ".
+ $self->Id.": ".$msg);
+ return(undef);
+ }
+ }
+ return(1);
+
+}
+
+# }}}
+
+# {{{ sub OwnerGroup
+
+=head2 OwnerGroup
+
+A constructor which returns an RT::Group object containing the owner of this ticket.
+
+=cut
+
+sub OwnerGroup {
+ my $self = shift;
+ my $owner_obj = RT::Group->new($self->CurrentUser);
+ $owner_obj->LoadTicketRoleGroup( Ticket => $self->Id, Type => 'Owner');
+ return ($owner_obj);
+}
+
+# }}}
+
+
+# {{{ sub AddWatcher
+
+=head2 AddWatcher
+
+AddWatcher takes a parameter hash. The keys are as follows:
+
+Type One of Requestor, Cc, AdminCc
+
+PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
+
+Email The email address of the new watcher. If a user with this
+ email address can't be found, a new nonprivileged user will be created.
+
+If the watcher you\'re trying to set has an RT account, set the Owner paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
+
+=cut
+
+sub AddWatcher {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+ # XXX, FIXME, BUG: if only email is provided then we only check
+ # for ModifyTicket right, but must try to get PrincipalId and
+ # check Watch* rights too if user exist
+
+ # {{{ Check ACLS
+ #If the watcher we're trying to add is for the current user
+ if ( $self->CurrentUser->PrincipalId == ($args{'PrincipalId'} || 0)
+ or lc( $self->CurrentUser->UserObj->EmailAddress )
+ eq lc( RT::User->CanonicalizeEmailAddress( $args{'Email'} ) || '' ) )
+ {
+ # If it's an AdminCc and they don't have
+ # 'WatchAsAdminCc' or 'ModifyTicket', bail
+ if ( $args{'Type'} eq 'AdminCc' ) {
+ unless ( $self->CurrentUserHasRight('ModifyTicket')
+ or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
+ return ( 0, $self->loc('Permission Denied'))
+ }
+ }
+
+ # If it's a Requestor or Cc and they don't have
+ # 'Watch' or 'ModifyTicket', bail
+ elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) ) {
+
+ unless ( $self->CurrentUserHasRight('ModifyTicket')
+ or $self->CurrentUserHasRight('Watch') ) {
+ return ( 0, $self->loc('Permission Denied'))
+ }
+ }
+ else {
+ $RT::Logger->warning( "$self -> AddWatcher got passed a bogus type");
+ return ( 0, $self->loc('Error in parameters to Ticket->AddWatcher') );
+ }
+ }
+
+ # If the watcher isn't the current user
+ # and the current user doesn't have 'ModifyTicket'
+ # bail
+ else {
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+
+ # }}}
+
+ return ( $self->_AddWatcher(%args) );
+}
+
+#This contains the meat of AddWatcher. but can be called from a routine like
+# Create, which doesn't need the additional acl check
+sub _AddWatcher {
+ my $self = shift;
+ my %args = (
+ Type => undef,
+ Silent => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+
+ my $principal = RT::Principal->new($self->CurrentUser);
+ if ($args{'Email'}) {
+ my $user = RT::User->new($RT::SystemUser);
+ my ($pid, $msg) = $user->LoadOrCreateByEmail($args{'Email'});
+ # If we can't load the user by email address, let's try to load by username
+ unless ($pid) {
+ ($pid,$msg) = $user->Load($args{'Email'})
+ }
+ if ($pid) {
+ $args{'PrincipalId'} = $pid;
+ }
+ }
+ if ($args{'PrincipalId'}) {
+ $principal->Load($args{'PrincipalId'});
+ }
+
+
+ # If we can't find this watcher, we need to bail.
+ unless ($principal->Id) {
+ $RT::Logger->error("Could not load create a user with the email address '".$args{'Email'}. "' to add as a watcher for ticket ".$self->Id);
+ return(0, $self->loc("Could not find or create that user"));
+ }
+
+
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadTicketRoleGroup(Type => $args{'Type'}, Ticket => $self->Id);
+ unless ($group->id) {
+ return(0,$self->loc("Group not found"));
+ }
+
+ if ( $group->HasMember( $principal)) {
+
+ return ( 0, $self->loc('That principal is already a [_1] for this ticket', $self->loc($args{'Type'})) );
+ }
+
+
+ my ( $m_id, $m_msg ) = $group->_AddMember( PrincipalId => $principal->Id,
+ InsideTransaction => 1 );
+ unless ($m_id) {
+ $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
+
+ return ( 0, $self->loc('Could not make that principal a [_1] for this ticket', $self->loc($args{'Type'})) );
+ }
+
+ unless ( $args{'Silent'} ) {
+ $self->_NewTransaction(
+ Type => 'AddWatcher',
+ NewValue => $principal->Id,
+ Field => $args{'Type'}
+ );
+ }
+
+ return ( 1, $self->loc('Added principal as a [_1] for this ticket', $self->loc($args{'Type'})) );
+}
+
+# }}}
+
+
+# {{{ sub DeleteWatcher
+
+=head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS }
+
+
+Deletes a Ticket watcher. Takes two arguments:
+
+Type (one of Requestor,Cc,AdminCc)
+
+and one of
+
+PrincipalId (an RT::Principal Id of the watcher you want to remove)
+ OR
+Email (the email address of an existing wathcer)
+
+
+=cut
+
+
+sub DeleteWatcher {
+ my $self = shift;
+
+ my %args = ( Type => undef,
+ PrincipalId => undef,
+ Email => undef,
+ @_ );
+
+ unless ( $args{'PrincipalId'} || $args{'Email'} ) {
+ return ( 0, $self->loc("No principal specified") );
+ }
+ my $principal = RT::Principal->new( $self->CurrentUser );
+ if ( $args{'PrincipalId'} ) {
+
+ $principal->Load( $args{'PrincipalId'} );
+ }
+ else {
+ my $user = RT::User->new( $self->CurrentUser );
+ $user->LoadByEmail( $args{'Email'} );
+ $principal->Load( $user->Id );
+ }
+
+ # If we can't find this watcher, we need to bail.
+ unless ( $principal->Id ) {
+ return ( 0, $self->loc("Could not find that principal") );
+ }
+
+ my $group = RT::Group->new( $self->CurrentUser );
+ $group->LoadTicketRoleGroup( Type => $args{'Type'}, Ticket => $self->Id );
+ unless ( $group->id ) {
+ return ( 0, $self->loc("Group not found") );
+ }
+
+ # {{{ Check ACLS
+ #If the watcher we're trying to add is for the current user
+ if ( $self->CurrentUser->PrincipalId == $principal->id ) {
+
+ # If it's an AdminCc and they don't have
+ # 'WatchAsAdminCc' or 'ModifyTicket', bail
+ if ( $args{'Type'} eq 'AdminCc' ) {
+ unless ( $self->CurrentUserHasRight('ModifyTicket')
+ or $self->CurrentUserHasRight('WatchAsAdminCc') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+
+ # If it's a Requestor or Cc and they don't have
+ # 'Watch' or 'ModifyTicket', bail
+ elsif ( ( $args{'Type'} eq 'Cc' ) or ( $args{'Type'} eq 'Requestor' ) )
+ {
+ unless ( $self->CurrentUserHasRight('ModifyTicket')
+ or $self->CurrentUserHasRight('Watch') ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+ else {
+ $RT::Logger->warn("$self -> DeleteWatcher got passed a bogus type");
+ return ( 0,
+ $self->loc('Error in parameters to Ticket->DeleteWatcher') );
+ }
+ }
+
+ # If the watcher isn't the current user
+ # and the current user doesn't have 'ModifyTicket' bail
+ else {
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+
+ # }}}
+
+ # see if this user is already a watcher.
+
+ unless ( $group->HasMember($principal) ) {
+ return ( 0,
+ $self->loc( 'That principal is not a [_1] for this ticket',
+ $args{'Type'} ) );
+ }
+
+ my ( $m_id, $m_msg ) = $group->_DeleteMember( $principal->Id );
+ unless ($m_id) {
+ $RT::Logger->error( "Failed to delete "
+ . $principal->Id
+ . " as a member of group "
+ . $group->Id . "\n"
+ . $m_msg );
+
+ return (0,
+ $self->loc(
+ 'Could not remove that principal as a [_1] for this ticket',
+ $args{'Type'} ) );
+ }
+
+ unless ( $args{'Silent'} ) {
+ $self->_NewTransaction( Type => 'DelWatcher',
+ OldValue => $principal->Id,
+ Field => $args{'Type'} );
+ }
+
+ return ( 1,
+ $self->loc( "[_1] is no longer a [_2] for this ticket.",
+ $principal->Object->Name,
+ $args{'Type'} ) );
+}
+
+
+
+# }}}
+
+
+=head2 SquelchMailTo [EMAIL]
+
+Takes an optional email address to never email about updates to this ticket.
+
+
+Returns an array of the RT::Attribute objects for this ticket's 'SquelchMailTo' attributes.
+
+=begin testing
+
+my $t = RT::Ticket->new($RT::SystemUser);
+ok($t->Create(Queue => 'general', Subject => 'SquelchTest'));
+
+is($#{$t->SquelchMailTo}, -1, "The ticket has no squelched recipients");
+
+my @returned = $t->SquelchMailTo('nobody@example.com');
+
+is($#returned, 0, "The ticket has one squelched recipients");
+
+my @names = $t->Attributes->Names;
+is(shift @names, 'SquelchMailTo', "The attribute we have is SquelchMailTo");
+@returned = $t->SquelchMailTo('nobody@example.com');
+
+
+is($#returned, 0, "The ticket has one squelched recipients");
+
+@names = $t->Attributes->Names;
+is(shift @names, 'SquelchMailTo', "The attribute we have is SquelchMailTo");
+
+
+my ($ret, $msg) = $t->UnsquelchMailTo('nobody@example.com');
+ok($ret, "Removed nobody as a squelched recipient - ".$msg);
+@returned = $t->SquelchMailTo();
+is($#returned, -1, "The ticket has no squelched recipients". join(',',@returned));
+
+
+=end testing
+
+=cut
+
+sub SquelchMailTo {
+ my $self = shift;
+ if (@_) {
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return undef;
+ }
+ my $attr = shift;
+ $self->AddAttribute( Name => 'SquelchMailTo', Content => $attr )
+ unless grep { $_->Content eq $attr }
+ $self->Attributes->Named('SquelchMailTo');
+
+ }
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return undef;
+ }
+ my @attributes = $self->Attributes->Named('SquelchMailTo');
+ return (@attributes);
+}
+
+
+=head2 UnsquelchMailTo ADDRESS
+
+Takes an address and removes it from this ticket's "SquelchMailTo" list. If an address appears multiple times, each instance is removed.
+
+Returns a tuple of (status, message)
+
+=cut
+
+sub UnsquelchMailTo {
+ my $self = shift;
+
+ my $address = shift;
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my ($val, $msg) = $self->Attributes->DeleteEntry ( Name => 'SquelchMailTo', Content => $address);
+ return ($val, $msg);
+}
+
+
+# {{{ a set of [foo]AsString subs that will return the various sorts of watchers for a ticket/queue as a comma delineated string
+
+=head2 RequestorAddresses
+
+ B<Returns> String: All Ticket Requestor email addresses as a string.
+
+=cut
+
+sub RequestorAddresses {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return undef;
+ }
+
+ return ( $self->Requestors->MemberEmailAddressesAsString );
+}
+
+
+=head2 AdminCcAddresses
+
+returns String: All Ticket AdminCc email addresses as a string
+
+=cut
+
+sub AdminCcAddresses {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return undef;
+ }
+
+ return ( $self->AdminCc->MemberEmailAddressesAsString )
+
+}
+
+=head2 CcAddresses
+
+returns String: All Ticket Ccs as a string of email addresses
+
+=cut
+
+sub CcAddresses {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return undef;
+ }
+
+ return ( $self->Cc->MemberEmailAddressesAsString);
+
+}
+
+# }}}
+
+# {{{ Routines that return RT::Watchers objects of Requestors, Ccs and AdminCcs
+
+# {{{ sub Requestors
+
+=head2 Requestors
+
+Takes nothing.
+Returns this ticket's Requestors as an RT::Group object
+
+=cut
+
+sub Requestors {
+ my $self = shift;
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('ShowTicket') ) {
+ $group->LoadTicketRoleGroup(Type => 'Requestor', Ticket => $self->Id);
+ }
+ return ($group);
+
+}
+
+# }}}
+
+# {{{ sub _Requestors
+
+=head2 _Requestors
+
+Private non-ACLed variant of Reqeustors so that we can look them up for the
+purposes of customer auto-association during create.
+
+=cut
+
+sub _Requestors {
+ my $self = shift;
+
+ my $group = RT::Group->new($RT::SystemUser);
+ $group->LoadTicketRoleGroup(Type => 'Requestor', Ticket => $self->Id);
+ return ($group);
+}
+
+# }}}
+
+# {{{ sub Cc
+
+=head2 Cc
+
+Takes nothing.
+Returns an RT::Group object which contains this ticket's Ccs.
+If the user doesn't have "ShowTicket" permission, returns an empty group
+
+=cut
+
+sub Cc {
+ my $self = shift;
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('ShowTicket') ) {
+ $group->LoadTicketRoleGroup(Type => 'Cc', Ticket => $self->Id);
+ }
+ return ($group);
+
+}
+
+# }}}
+
+# {{{ sub AdminCc
+
+=head2 AdminCc
+
+Takes nothing.
+Returns an RT::Group object which contains this ticket's AdminCcs.
+If the user doesn't have "ShowTicket" permission, returns an empty group
+
+=cut
+
+sub AdminCc {
+ my $self = shift;
+
+ my $group = RT::Group->new($self->CurrentUser);
+ if ( $self->CurrentUserHasRight('ShowTicket') ) {
+ $group->LoadTicketRoleGroup(Type => 'AdminCc', Ticket => $self->Id);
+ }
+ return ($group);
+
+}
+
+# }}}
+
+# }}}
+
+# {{{ IsWatcher,IsRequestor,IsCc, IsAdminCc
+
+# {{{ sub IsWatcher
+# a generic routine to be called by IsRequestor, IsCc and IsAdminCc
+
+=head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL }
+
+Takes a param hash with the attributes Type and either PrincipalId or Email
+
+Type is one of Requestor, Cc, AdminCc and Owner
+
+PrincipalId is an RT::Principal id, and Email is an email address.
+
+Returns true if the specified principal (or the one corresponding to the
+specified address) is a member of the group Type for this ticket.
+
+XX TODO: This should be Memoized.
+
+=cut
+
+sub IsWatcher {
+ my $self = shift;
+
+ my %args = ( Type => 'Requestor',
+ PrincipalId => undef,
+ Email => undef,
+ @_
+ );
+
+ # Load the relevant group.
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->LoadTicketRoleGroup(Type => $args{'Type'}, Ticket => $self->id);
+
+ # Find the relevant principal.
+ my $principal = RT::Principal->new($self->CurrentUser);
+ if (!$args{PrincipalId} && $args{Email}) {
+ # Look up the specified user.
+ my $user = RT::User->new($self->CurrentUser);
+ $user->LoadByEmail($args{Email});
+ if ($user->Id) {
+ $args{PrincipalId} = $user->PrincipalId;
+ }
+ else {
+ # A non-existent user can't be a group member.
+ return 0;
+ }
+ }
+ $principal->Load($args{'PrincipalId'});
+
+ # Ask if it has the member in question
+ return ($group->HasMember($principal));
+}
+
+# }}}
+
+# {{{ sub IsRequestor
+
+=head2 IsRequestor PRINCIPAL_ID
+
+ Takes an RT::Principal id
+ Returns true if the principal is a requestor of the current ticket.
+
+
+=cut
+
+sub IsRequestor {
+ my $self = shift;
+ my $person = shift;
+
+ return ( $self->IsWatcher( Type => 'Requestor', PrincipalId => $person ) );
+
+};
+
+# }}}
+
+# {{{ sub IsCc
+
+=head2 IsCc PRINCIPAL_ID
+
+ Takes an RT::Principal id.
+ Returns true if the principal is a requestor of the current ticket.
+
+
+=cut
+
+sub IsCc {
+ my $self = shift;
+ my $cc = shift;
+
+ return ( $self->IsWatcher( Type => 'Cc', PrincipalId => $cc ) );
+
+}
+
+# }}}
+
+# {{{ sub IsAdminCc
+
+=head2 IsAdminCc PRINCIPAL_ID
+
+ Takes an RT::Principal id.
+ Returns true if the principal is a requestor of the current ticket.
+
+=cut
+
+sub IsAdminCc {
+ my $self = shift;
+ my $person = shift;
+
+ return ( $self->IsWatcher( Type => 'AdminCc', PrincipalId => $person ) );
+
+}
+
+# }}}
+
+# {{{ sub IsOwner
+
+=head2 IsOwner
+
+ Takes an RT::User object. Returns true if that user is this ticket's owner.
+returns undef otherwise
+
+=cut
+
+sub IsOwner {
+ my $self = shift;
+ my $person = shift;
+
+ # no ACL check since this is used in acl decisions
+ # unless ($self->CurrentUserHasRight('ShowTicket')) {
+ # return(undef);
+ # }
+
+ #Tickets won't yet have owners when they're being created.
+ unless ( $self->OwnerObj->id ) {
+ return (undef);
+ }
+
+ if ( $person->id == $self->OwnerObj->id ) {
+ return (1);
+ }
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
+# }}}
+
+# }}}
+
+# {{{ Routines dealing with queues
+
+# {{{ sub ValidateQueue
+
+sub ValidateQueue {
+ my $self = shift;
+ my $Value = shift;
+
+ if ( !$Value ) {
+ $RT::Logger->warning( " RT:::Queue::ValidateQueue called with a null value. this isn't ok.");
+ return (1);
+ }
+
+ my $QueueObj = RT::Queue->new( $self->CurrentUser );
+ my $id = $QueueObj->Load($Value);
+
+ if ($id) {
+ return (1);
+ }
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
+# {{{ sub SetQueue
+
+sub SetQueue {
+ my $self = shift;
+ my $NewQueue = shift;
+
+ #Redundant. ACL gets checked in _Set;
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my $NewQueueObj = RT::Queue->new( $self->CurrentUser );
+ $NewQueueObj->Load($NewQueue);
+
+ unless ( $NewQueueObj->Id() ) {
+ return ( 0, $self->loc("That queue does not exist") );
+ }
+
+ if ( $NewQueueObj->Id == $self->QueueObj->Id ) {
+ return ( 0, $self->loc('That is the same value') );
+ }
+ unless (
+ $self->CurrentUser->HasRight(
+ Right => 'CreateTicket',
+ Object => $NewQueueObj
+ )
+ )
+ {
+ return ( 0, $self->loc("You may not create requests in that queue.") );
+ }
+
+ unless (
+ $self->OwnerObj->HasRight(
+ Right => 'OwnTicket',
+ Object => $NewQueueObj
+ )
+ )
+ {
+ my $clone = RT::Ticket->new( $RT::SystemUser );
+ $clone->Load( $self->Id );
+ unless ( $clone->Id ) {
+ return ( 0, $self->loc("Couldn't load copy of ticket #[_1].", $self->Id) );
+ }
+ my ($status, $msg) = $clone->SetOwner( $RT::Nobody->Id, 'Force' );
+ $RT::Logger->error("Couldn't set owner on queue change: $msg") unless $status;
+ }
+
+ return ( $self->_Set( Field => 'Queue', Value => $NewQueueObj->Id() ) );
+}
+
+# }}}
+
+# {{{ sub QueueObj
+
+=head2 QueueObj
+
+Takes nothing. returns this ticket's queue object
+
+=cut
+
+sub QueueObj {
+ my $self = shift;
+
+ my $queue_obj = RT::Queue->new( $self->CurrentUser );
+
+ #We call __Value so that we can avoid the ACL decision and some deep recursion
+ my ($result) = $queue_obj->Load( $self->__Value('Queue') );
+ return ($queue_obj);
+}
+
+# }}}
+
+# }}}
+
+# {{{ Date printing routines
+
+# {{{ sub DueObj
+
+=head2 DueObj
+
+ Returns an RT::Date object containing this ticket's due date
+
+=cut
+
+sub DueObj {
+ my $self = shift;
+
+ my $time = new RT::Date( $self->CurrentUser );
+
+ # -1 is RT::Date slang for never
+ if ( $self->Due ) {
+ $time->Set( Format => 'sql', Value => $self->Due );
+ }
+ else {
+ $time->Set( Format => 'unix', Value => -1 );
+ }
+
+ return $time;
+}
+
+# }}}
+
+# {{{ sub DueAsString
+
+=head2 DueAsString
+
+Returns this ticket's due date as a human readable string
+
+=cut
+
+sub DueAsString {
+ my $self = shift;
+ return $self->DueObj->AsString();
+}
+
+# }}}
+
+# {{{ sub ResolvedObj
+
+=head2 ResolvedObj
+
+ Returns an RT::Date object of this ticket's 'resolved' time.
+
+=cut
+
+sub ResolvedObj {
+ my $self = shift;
+
+ my $time = new RT::Date( $self->CurrentUser );
+ $time->Set( Format => 'sql', Value => $self->Resolved );
+ return $time;
+}
+
+# }}}
+
+# {{{ sub SetStarted
+
+=head2 SetStarted
+
+Takes a date in ISO format or undef
+Returns a transaction id and a message
+The client calls "Start" to note that the project was started on the date in $date.
+A null date means "now"
+
+=cut
+
+sub SetStarted {
+ my $self = shift;
+ my $time = shift || 0;
+
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ #We create a date object to catch date weirdness
+ my $time_obj = new RT::Date( $self->CurrentUser() );
+ if ( $time ) {
+ $time_obj->Set( Format => 'ISO', Value => $time );
+ }
+ else {
+ $time_obj->SetToNow();
+ }
+
+ #Now that we're starting, open this ticket
+ #TODO do we really want to force this as policy? it should be a scrip
+
+ #We need $TicketAsSystem, in case the current user doesn't have
+ #ShowTicket
+ #
+ my $TicketAsSystem = new RT::Ticket($RT::SystemUser);
+ $TicketAsSystem->Load( $self->Id );
+ if ( $TicketAsSystem->Status eq 'new' ) {
+ $TicketAsSystem->Open();
+ }
+
+ return ( $self->_Set( Field => 'Started', Value => $time_obj->ISO ) );
+
+}
+
+# }}}
+
+# {{{ sub StartedObj
+
+=head2 StartedObj
+
+ Returns an RT::Date object which contains this ticket's
+'Started' time.
+
+=cut
+
+sub StartedObj {
+ my $self = shift;
+
+ my $time = new RT::Date( $self->CurrentUser );
+ $time->Set( Format => 'sql', Value => $self->Started );
+ return $time;
+}
+
+# }}}
+
+# {{{ sub StartsObj
+
+=head2 StartsObj
+
+ Returns an RT::Date object which contains this ticket's
+'Starts' time.
+
+=cut
+
+sub StartsObj {
+ my $self = shift;
+
+ my $time = new RT::Date( $self->CurrentUser );
+ $time->Set( Format => 'sql', Value => $self->Starts );
+ return $time;
+}
+
+# }}}
+
+# {{{ sub ToldObj
+
+=head2 ToldObj
+
+ Returns an RT::Date object which contains this ticket's
+'Told' time.
+
+=cut
+
+sub ToldObj {
+ my $self = shift;
+
+ my $time = new RT::Date( $self->CurrentUser );
+ $time->Set( Format => 'sql', Value => $self->Told );
+ return $time;
+}
+
+# }}}
+
+# {{{ sub ToldAsString
+
+=head2 ToldAsString
+
+A convenience method that returns ToldObj->AsString
+
+TODO: This should be deprecated
+
+=cut
+
+sub ToldAsString {
+ my $self = shift;
+ if ( $self->Told ) {
+ return $self->ToldObj->AsString();
+ }
+ else {
+ return ("Never");
+ }
+}
+
+# }}}
+
+# {{{ sub TimeWorkedAsString
+
+=head2 TimeWorkedAsString
+
+Returns the amount of time worked on this ticket as a Text String
+
+=cut
+
+sub TimeWorkedAsString {
+ my $self = shift;
+ return "0" unless $self->TimeWorked;
+
+ #This is not really a date object, but if we diff a number of seconds
+ #vs the epoch, we'll get a nice description of time worked.
+
+ my $worked = new RT::Date( $self->CurrentUser );
+
+ #return the #of minutes worked turned into seconds and written as
+ # a simple text string
+
+ return ( $worked->DurationAsString( $self->TimeWorked * 60 ) );
+}
+
+# }}}
+
+# }}}
+
+# {{{ Routines dealing with correspondence/comments
+
+# {{{ sub Comment
+
+=head2 Comment
+
+Comment on this ticket.
+Takes a hashref with the following attributes:
+If MIMEObj is undefined, Content will be used to build a MIME::Entity for this
+commentl
+
+MIMEObj, TimeTaken, CcMessageTo, BccMessageTo, Content, DryRun
+
+If DryRun is defined, this update WILL NOT BE RECORDED. Scrips will not be committed.
+They will, however, be prepared and you'll be able to access them through the TransactionObj
+
+Returns: Transaction id, Error Message, Transaction Object
+(note the different order from Create()!)
+
+=cut
+
+sub Comment {
+ my $self = shift;
+
+ my %args = ( CcMessageTo => undef,
+ BccMessageTo => undef,
+ MIMEObj => undef,
+ Content => undef,
+ TimeTaken => 0,
+ DryRun => 0,
+ @_ );
+
+ unless ( ( $self->CurrentUserHasRight('CommentOnTicket') )
+ or ( $self->CurrentUserHasRight('ModifyTicket') ) ) {
+ return ( 0, $self->loc("Permission Denied"), undef );
+ }
+ $args{'NoteType'} = 'Comment';
+
+ if ($args{'DryRun'}) {
+ $RT::Handle->BeginTransaction();
+ $args{'CommitScrips'} = 0;
+ }
+
+ my @results = $self->_RecordNote(%args);
+ if ($args{'DryRun'}) {
+ $RT::Handle->Rollback();
+ }
+
+ return(@results);
+}
+# }}}
+
+# {{{ sub Correspond
+
+=head2 Correspond
+
+Correspond on this ticket.
+Takes a hashref with the following attributes:
+
+
+MIMEObj, TimeTaken, CcMessageTo, BccMessageTo, Content, DryRun
+
+if there's no MIMEObj, Content is used to build a MIME::Entity object
+
+If DryRun is defined, this update WILL NOT BE RECORDED. Scrips will not be committed.
+They will, however, be prepared and you'll be able to access them through the TransactionObj
+
+Returns: Transaction id, Error Message, Transaction Object
+(note the different order from Create()!)
+
+
+=cut
+
+sub Correspond {
+ my $self = shift;
+ my %args = ( CcMessageTo => undef,
+ BccMessageTo => undef,
+ MIMEObj => undef,
+ Content => undef,
+ TimeTaken => 0,
+ @_ );
+
+ unless ( ( $self->CurrentUserHasRight('ReplyToTicket') )
+ or ( $self->CurrentUserHasRight('ModifyTicket') ) ) {
+ return ( 0, $self->loc("Permission Denied"), undef );
+ }
+
+ $args{'NoteType'} = 'Correspond';
+ if ($args{'DryRun'}) {
+ $RT::Handle->BeginTransaction();
+ $args{'CommitScrips'} = 0;
+ }
+
+ my @results = $self->_RecordNote(%args);
+
+ #Set the last told date to now if this isn't mail from the requestor.
+ #TODO: Note that this will wrongly ack mail from any non-requestor as a "told"
+ $self->_SetTold unless ( $self->IsRequestor($self->CurrentUser->id));
+
+ if ($args{'DryRun'}) {
+ $RT::Handle->Rollback();
+ }
+
+ return (@results);
+
+}
+
+# }}}
+
+# {{{ sub _RecordNote
+
+=head2 _RecordNote
+
+the meat of both comment and correspond.
+
+Performs no access control checks. hence, dangerous.
+
+=cut
+
+sub _RecordNote {
+
+ my $self = shift;
+ my %args = ( CcMessageTo => undef,
+ BccMessageTo => undef,
+ MIMEObj => undef,
+ Content => undef,
+ TimeTaken => 0,
+ CommitScrips => 1,
+ @_ );
+
+ unless ( $args{'MIMEObj'} || $args{'Content'} ) {
+ return ( 0, $self->loc("No message attached"), undef );
+ }
+ unless ( $args{'MIMEObj'} ) {
+ $args{'MIMEObj'} = MIME::Entity->build( Data => (
+ ref $args{'Content'}
+ ? $args{'Content'}
+ : [ $args{'Content'} ]
+ ) );
+ }
+
+ # convert text parts into utf-8
+ RT::I18N::SetMIMEEntityToUTF8( $args{'MIMEObj'} );
+
+# If we've been passed in CcMessageTo and BccMessageTo fields,
+# add them to the mime object for passing on to the transaction handler
+# The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and RT-Send-Bcc:
+# headers
+
+
+ foreach my $type (qw/Cc Bcc/) {
+ if ( defined $args{ $type . 'MessageTo' } ) {
+
+ my $addresses = join ', ', (
+ map { RT::User->CanonicalizeEmailAddress( $_->address ) }
+ Mail::Address->parse( $args{ $type . 'MessageTo' } ) );
+ $args{'MIMEObj'}->head->add( 'RT-Send-' . $type, $addresses );
+ }
+ }
+
+ # If this is from an external source, we need to come up with its
+ # internal Message-ID now, so all emails sent because of this
+ # message have a common Message-ID
+ unless ( ($args{'MIMEObj'}->head->get('Message-ID') || '')
+ =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$RT::Organization>/ )
+ {
+ $args{'MIMEObj'}->head->set( 'RT-Message-ID',
+ "<rt-"
+ . $RT::VERSION . "-"
+ . $$ . "-"
+ . CORE::time() . "-"
+ . int(rand(2000)) . '.'
+ . $self->id . "-"
+ . "0" . "-" # Scrip
+ . "0" . "@" # Email sent
+ . $RT::Organization
+ . ">" );
+ }
+
+ #Record the correspondence (write the transaction)
+ my ( $Trans, $msg, $TransObj ) = $self->_NewTransaction(
+ Type => $args{'NoteType'},
+ Data => ( $args{'MIMEObj'}->head->get('subject') || 'No Subject' ),
+ TimeTaken => $args{'TimeTaken'},
+ MIMEObj => $args{'MIMEObj'},
+ CommitScrips => $args{'CommitScrips'},
+ );
+
+ unless ($Trans) {
+ $RT::Logger->err("$self couldn't init a transaction $msg");
+ return ( $Trans, $self->loc("Message could not be recorded"), undef );
+ }
+
+ return ( $Trans, $self->loc("Message recorded"), $TransObj );
+}
+
+# }}}
+
+# }}}
+
+# {{{ sub _Links
+
+sub _Links {
+ my $self = shift;
+
+ #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic ---
+ #tobias meant by $f
+ my $field = shift;
+ my $type = shift || "";
+
+ unless ( $self->{"$field$type"} ) {
+ $self->{"$field$type"} = new RT::Links( $self->CurrentUser );
+
+ #not sure what this ACL was supposed to do... but returning the
+ # bare (unlimited) RT::Links certainly seems wrong, it causes the
+ # $Ticket->Customers method during creation to return results for every
+ # ticket...
+ #if ( $self->CurrentUserHasRight('ShowTicket') ) {
+
+ # Maybe this ticket is a merged ticket
+ my $Tickets = new RT::Tickets( $self->CurrentUser );
+ # at least to myself
+ $self->{"$field$type"}->Limit( FIELD => $field,
+ VALUE => $self->URI,
+ ENTRYAGGREGATOR => 'OR' );
+ $Tickets->Limit( FIELD => 'EffectiveId',
+ VALUE => $self->EffectiveId );
+ while (my $Ticket = $Tickets->Next) {
+ $self->{"$field$type"}->Limit( FIELD => $field,
+ VALUE => $Ticket->URI,
+ ENTRYAGGREGATOR => 'OR' );
+ }
+ $self->{"$field$type"}->Limit( FIELD => 'Type',
+ VALUE => $type )
+ if ($type);
+ #}
+ }
+ return ( $self->{"$field$type"} );
+}
+
+# }}}
+
+# {{{ sub DeleteLink
+
+=head2 DeleteLink
+
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
+
+=cut
+
+sub DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ #check acls
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyTicket');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ # If the other URI is an RT::Ticket, we want to make sure the user
+ # can modify it too...
+ my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+ return (0, $msg) unless $status;
+ if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+ $right++;
+ }
+ if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+ ( $RT::StrictLinkACL && $right < 2 ) )
+ {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
+
+ if ( !$val ) {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $Msg );
+ }
+
+ my ($direction, $remote_link);
+
+ if ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+ elsif ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
+
+ if ( $args{'Silent'} ) {
+ return ( $val, $Msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => 'DeleteLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ OldValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0
+ );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'DeleteLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ OldValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+
+ return ( $Trans, $Msg );
+ }
+}
+
+# }}}
+
+# {{{ sub AddLink
+
+=head2 AddLink
+
+Takes a paramhash of Type and one of Base or Target. Adds that link to this ticket.
+
+=cut
+
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyTicket');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ # If the other URI is an RT::Ticket, we want to make sure the user
+ # can modify it too...
+ my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+ return (0, $msg) unless $status;
+ if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+ $right++;
+ }
+ if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+ ( $RT::StrictLinkACL && $right < 2 ) )
+ {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ return $self->_AddLink(%args);
+}
+
+sub __GetTicketFromURI {
+ my $self = shift;
+ my %args = ( URI => '', @_ );
+
+ # If the other URI is an RT::Ticket, we want to make sure the user
+ # can modify it too...
+ my $uri_obj = RT::URI->new( $self->CurrentUser );
+ $uri_obj->FromURI( $args{'URI'} );
+
+ unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
+ my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
+ $RT::Logger->warning( "$msg\n" );
+ return( 0, $msg );
+ }
+ my $obj = $uri_obj->Resolver->Object;
+ unless ( UNIVERSAL::isa($obj, 'RT::Ticket') && $obj->id ) {
+ return (1, 'Found not a ticket', undef);
+ }
+ return (1, 'Found ticket', $obj);
+}
+
+=head2 _AddLink
+
+Private non-acled variant of AddLink so that links can be added during create.
+
+=cut
+
+sub _AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
+ return ($val, $msg) if !$val || $exist;
+
+ my ($direction, $remote_link);
+ if ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction = 'Base';
+ } elsif ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+
+ # Don't write the transaction if we're doing this on create
+ if ( $args{'Silent'} ) {
+ return ( $val, $msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ #Write the transaction
+ my ( $Trans, $Msg, $TransObj ) =
+ $self->_NewTransaction(Type => 'AddLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ NewValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0 );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'AddLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ NewValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+ return ( $val, $Msg );
+ }
+
+}
+
+# }}}
+
+
+# {{{ sub MergeInto
+
+=head2 MergeInto
+
+MergeInto take the id of the ticket to merge this ticket into.
+
+
+=begin testing
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+$t1->Create ( Subject => 'Merge test 1', Queue => 'general', Requestor => 'merge1@example.com');
+my $t1id = $t1->id;
+my $t2 = RT::Ticket->new($RT::SystemUser);
+$t2->Create ( Subject => 'Merge test 2', Queue => 'general', Requestor => 'merge2@example.com');
+my $t2id = $t2->id;
+my ($msg, $val) = $t1->MergeInto($t2->id);
+ok ($msg,$val);
+$t1 = RT::Ticket->new($RT::SystemUser);
+is ($t1->id, undef, "ok. we've got a blank ticket1");
+$t1->Load($t1id);
+
+is ($t1->id, $t2->id);
+
+is ($t1->Requestors->MembersObj->Count, 2);
+
+
+=end testing
+
+=cut
+
+sub MergeInto {
+ my $self = shift;
+ my $ticket_id = shift;
+
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ # Load up the new ticket.
+ my $MergeInto = RT::Ticket->new($RT::SystemUser);
+ $MergeInto->Load($ticket_id);
+
+ # make sure it exists.
+ unless ( $MergeInto->Id ) {
+ return ( 0, $self->loc("New ticket doesn't exist") );
+ }
+
+ # Make sure the current user can modify the new ticket.
+ unless ( $MergeInto->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ $RT::Handle->BeginTransaction();
+
+ # We use EffectiveId here even though it duplicates information from
+ # the links table becasue of the massive performance hit we'd take
+ # by trying to do a separate database query for merge info everytime
+ # loaded a ticket.
+
+ #update this ticket's effective id to the new ticket's id.
+ my ( $id_val, $id_msg ) = $self->__Set(
+ Field => 'EffectiveId',
+ Value => $MergeInto->Id()
+ );
+
+ unless ($id_val) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("Merge failed. Couldn't set EffectiveId") );
+ }
+
+
+ if ( $self->__Value('Status') ne 'resolved' ) {
+
+ my ( $status_val, $status_msg )
+ = $self->__Set( Field => 'Status', Value => 'resolved' );
+
+ unless ($status_val) {
+ $RT::Handle->Rollback();
+ $RT::Logger->error(
+ $self->loc(
+ "[_1] couldn't set status to resolved. RT's Database may be inconsistent.",
+ $self
+ )
+ );
+ return ( 0, $self->loc("Merge failed. Couldn't set Status") );
+ }
+ }
+
+ # update all the links that point to that old ticket
+ my $old_links_to = RT::Links->new($self->CurrentUser);
+ $old_links_to->Limit(FIELD => 'Target', VALUE => $self->URI);
+
+ my %old_seen;
+ while (my $link = $old_links_to->Next) {
+ if (exists $old_seen{$link->Base."-".$link->Type}) {
+ $link->Delete;
+ }
+ elsif ($link->Base eq $MergeInto->URI) {
+ $link->Delete;
+ } else {
+ # First, make sure the link doesn't already exist. then move it over.
+ my $tmp = RT::Link->new($RT::SystemUser);
+ $tmp->LoadByCols(Base => $link->Base, Type => $link->Type, LocalTarget => $MergeInto->id);
+ if ($tmp->id) {
+ $link->Delete;
+ } else {
+ $link->SetTarget($MergeInto->URI);
+ $link->SetLocalTarget($MergeInto->id);
+ }
+ $old_seen{$link->Base."-".$link->Type} =1;
+ }
+
+ }
+
+ my $old_links_from = RT::Links->new($self->CurrentUser);
+ $old_links_from->Limit(FIELD => 'Base', VALUE => $self->URI);
+
+ while (my $link = $old_links_from->Next) {
+ if (exists $old_seen{$link->Type."-".$link->Target}) {
+ $link->Delete;
+ }
+ if ($link->Target eq $MergeInto->URI) {
+ $link->Delete;
+ } else {
+ # First, make sure the link doesn't already exist. then move it over.
+ my $tmp = RT::Link->new($RT::SystemUser);
+ $tmp->LoadByCols(Target => $link->Target, Type => $link->Type, LocalBase => $MergeInto->id);
+ if ($tmp->id) {
+ $link->Delete;
+ } else {
+ $link->SetBase($MergeInto->URI);
+ $link->SetLocalBase($MergeInto->id);
+ $old_seen{$link->Type."-".$link->Target} =1;
+ }
+ }
+
+ }
+
+ # Update time fields
+ foreach my $type qw(TimeEstimated TimeWorked TimeLeft) {
+
+ my $mutator = "Set$type";
+ $MergeInto->$mutator(
+ ( $MergeInto->$type() || 0 ) + ( $self->$type() || 0 ) );
+
+ }
+#add all of this ticket's watchers to that ticket.
+ foreach my $watcher_type qw(Requestors Cc AdminCc) {
+
+ my $people = $self->$watcher_type->MembersObj;
+ my $addwatcher_type = $watcher_type;
+ $addwatcher_type =~ s/s$//;
+
+ while ( my $watcher = $people->Next ) {
+
+ my ($val, $msg) = $MergeInto->_AddWatcher(
+ Type => $addwatcher_type,
+ Silent => 1,
+ PrincipalId => $watcher->MemberId
+ );
+ unless ($val) {
+ $RT::Logger->warning($msg);
+ }
+ }
+
+ }
+
+ #find all of the tickets that were merged into this ticket.
+ my $old_mergees = new RT::Tickets( $self->CurrentUser );
+ $old_mergees->Limit(
+ FIELD => 'EffectiveId',
+ OPERATOR => '=',
+ VALUE => $self->Id
+ );
+
+ # update their EffectiveId fields to the new ticket's id
+ while ( my $ticket = $old_mergees->Next() ) {
+ my ( $val, $msg ) = $ticket->__Set(
+ Field => 'EffectiveId',
+ Value => $MergeInto->Id()
+ );
+ }
+
+ #make a new link: this ticket is merged into that other ticket.
+ $self->AddLink( Type => 'MergedInto', Target => $MergeInto->Id());
+
+ $MergeInto->_SetLastUpdated;
+
+ $RT::Handle->Commit();
+ return ( 1, $self->loc("Merge Successful") );
+}
+
+# }}}
+
+# }}}
+
+# {{{ Routines dealing with ownership
+
+# {{{ sub OwnerObj
+
+=head2 OwnerObj
+
+Takes nothing and returns an RT::User object of
+this ticket's owner
+
+=cut
+
+sub OwnerObj {
+ my $self = shift;
+
+ #If this gets ACLed, we lose on a rights check in User.pm and
+ #get deep recursion. if we need ACLs here, we need
+ #an equiv without ACLs
+
+ my $owner = new RT::User( $self->CurrentUser );
+ $owner->Load( $self->__Value('Owner') );
+
+ #Return the owner object
+ return ($owner);
+}
+
+# }}}
+
+# {{{ sub OwnerAsString
+
+=head2 OwnerAsString
+
+Returns the owner's email address
+
+=cut
+
+sub OwnerAsString {
+ my $self = shift;
+ return ( $self->OwnerObj->EmailAddress );
+
+}
+
+# }}}
+
+# {{{ sub SetOwner
+
+=head2 SetOwner
+
+Takes two arguments:
+ the Id or Name of the owner
+and (optionally) the type of the SetOwner Transaction. It defaults
+to 'Give'. 'Steal' is also a valid option.
+
+=begin testing
+
+my $root = RT::User->new($RT::SystemUser);
+$root->Load('root');
+ok ($root->Id, "Loaded the root user");
+my $t = RT::Ticket->new($RT::SystemUser);
+$t->Load(1);
+$t->SetOwner('root');
+is ($t->OwnerObj->Name, 'root' , "Root owns the ticket");
+$t->Steal();
+is ($t->OwnerObj->id, $RT::SystemUser->id , "SystemUser owns the ticket");
+my $txns = RT::Transactions->new($RT::SystemUser);
+$txns->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$txns->Limit(FIELD => 'ObjectId', VALUE => '1');
+$txns->Limit(FIELD => 'ObjectType', VALUE => 'RT::Ticket');
+$txns->Limit(FIELD => 'Type', OPERATOR => '!=', VALUE => 'EmailRecord');
+
+my $steal = $txns->First;
+ok($steal->OldValue == $root->Id , "Stolen from root");
+ok($steal->NewValue == $RT::SystemUser->Id , "Stolen by the systemuser");
+
+=end testing
+
+=cut
+
+sub SetOwner {
+ my $self = shift;
+ my $NewOwner = shift;
+ my $Type = shift || "Give";
+
+ $RT::Handle->BeginTransaction();
+
+ $self->_SetLastUpdated(); # lock the ticket
+ $self->Load( $self->id ); # in case $self changed while waiting for lock
+
+ my $OldOwnerObj = $self->OwnerObj;
+
+ my $NewOwnerObj = RT::User->new( $self->CurrentUser );
+ $NewOwnerObj->Load( $NewOwner );
+ unless ( $NewOwnerObj->Id ) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("That user does not exist") );
+ }
+
+
+ # must have ModifyTicket rights
+ # or TakeTicket/StealTicket and $NewOwner is self
+ # see if it's a take
+ if ( $OldOwnerObj->Id == $RT::Nobody->Id ) {
+ unless ( $self->CurrentUserHasRight('ModifyTicket')
+ || $self->CurrentUserHasRight('TakeTicket') ) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+
+ # see if it's a steal
+ elsif ( $OldOwnerObj->Id != $RT::Nobody->Id
+ && $OldOwnerObj->Id != $self->CurrentUser->id ) {
+
+ unless ( $self->CurrentUserHasRight('ModifyTicket')
+ || $self->CurrentUserHasRight('StealTicket') ) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+ else {
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ }
+
+ # If we're not stealing and the ticket has an owner and it's not
+ # the current user
+ if ( $Type ne 'Steal' and $Type ne 'Force'
+ and $OldOwnerObj->Id != $RT::Nobody->Id
+ and $OldOwnerObj->Id != $self->CurrentUser->Id )
+ {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("You can only take tickets that are unowned") )
+ if $NewOwnerObj->id == $self->CurrentUser->id;
+ return (
+ 0,
+ $self->loc("You can only reassign tickets that you own or that are unowned" )
+ );
+ }
+
+ #If we've specified a new owner and that user can't modify the ticket
+ elsif ( !$NewOwnerObj->HasRight( Right => 'OwnTicket', Object => $self ) ) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("That user may not own tickets in that queue") );
+ }
+
+ # If the ticket has an owner and it's the new owner, we don't need
+ # To do anything
+ elsif ( $NewOwnerObj->Id == $OldOwnerObj->Id ) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("That user already owns that ticket") );
+ }
+
+ # Delete the owner in the owner group, then add a new one
+ # TODO: is this safe? it's not how we really want the API to work
+ # for most things, but it's fast.
+ my ( $del_id, $del_msg ) = $self->OwnerGroup->MembersObj->First->Delete();
+ unless ($del_id) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("Could not change owner. ") . $del_msg );
+ }
+
+ my ( $add_id, $add_msg ) = $self->OwnerGroup->_AddMember(
+ PrincipalId => $NewOwnerObj->PrincipalId,
+ InsideTransaction => 1 );
+ unless ($add_id) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("Could not change owner. ") . $add_msg );
+ }
+
+ # We call set twice with slightly different arguments, so
+ # as to not have an SQL transaction span two RT transactions
+
+ my ( $val, $msg ) = $self->_Set(
+ Field => 'Owner',
+ RecordTransaction => 0,
+ Value => $NewOwnerObj->Id,
+ TimeTaken => 0,
+ TransactionType => $Type,
+ CheckACL => 0, # don't check acl
+ );
+
+ unless ($val) {
+ $RT::Handle->Rollback;
+ return ( 0, $self->loc("Could not change owner. ") . $msg );
+ }
+
+ ($val, $msg) = $self->_NewTransaction(
+ Type => $Type,
+ Field => 'Owner',
+ NewValue => $NewOwnerObj->Id,
+ OldValue => $OldOwnerObj->Id,
+ TimeTaken => 0,
+ );
+
+ if ( $val ) {
+ $msg = $self->loc( "Owner changed from [_1] to [_2]",
+ $OldOwnerObj->Name, $NewOwnerObj->Name );
+ }
+ else {
+ $RT::Handle->Rollback();
+ return ( 0, $msg );
+ }
+
+ $RT::Handle->Commit();
+
+ return ( $val, $msg );
+}
+
+# }}}
+
+# {{{ sub Take
+
+=head2 Take
+
+A convenince method to set the ticket's owner to the current user
+
+=cut
+
+sub Take {
+ my $self = shift;
+ return ( $self->SetOwner( $self->CurrentUser->Id, 'Take' ) );
+}
+
+# }}}
+
+# {{{ sub Untake
+
+=head2 Untake
+
+Convenience method to set the owner to 'nobody' if the current user is the owner.
+
+=cut
+
+sub Untake {
+ my $self = shift;
+ return ( $self->SetOwner( $RT::Nobody->UserObj->Id, 'Untake' ) );
+}
+
+# }}}
+
+# {{{ sub Steal
+
+=head2 Steal
+
+A convenience method to change the owner of the current ticket to the
+current user. Even if it's owned by another user.
+
+=cut
+
+sub Steal {
+ my $self = shift;
+
+ if ( $self->IsOwner( $self->CurrentUser ) ) {
+ return ( 0, $self->loc("You already own this ticket") );
+ }
+ else {
+ return ( $self->SetOwner( $self->CurrentUser->Id, 'Steal' ) );
+
+ }
+
+}
+
+# }}}
+
+# }}}
+
+# {{{ Routines dealing with status
+
+# {{{ sub ValidateStatus
+
+=head2 ValidateStatus STATUS
+
+Takes a string. Returns true if that status is a valid status for this ticket.
+Returns false otherwise.
+
+=cut
+
+sub ValidateStatus {
+ my $self = shift;
+ my $status = shift;
+
+ #Make sure the status passed in is valid
+ unless ( $self->QueueObj->IsValidStatus($status) ) {
+ return (undef);
+ }
+
+ return (1);
+
+}
+
+# }}}
+
+# {{{ sub SetStatus
+
+=head2 SetStatus STATUS
+
+Set this ticket\'s status. STATUS can be one of: new, open, stalled, resolved, rejected or deleted.
+
+Alternatively, you can pass in a list of named parameters (Status => STATUS, Force => FORCE). If FORCE is true, ignore unresolved dependencies and force a status change.
+
+=begin testing
+
+my $tt = RT::Ticket->new($RT::SystemUser);
+my ($id, $tid, $msg)= $tt->Create(Queue => 'general',
+ Subject => 'test');
+ok($id, $msg);
+is($tt->Status, 'new', "New ticket is created as new");
+
+($id, $msg) = $tt->SetStatus('open');
+ok($id, $msg);
+like($msg, qr/open/i, "Status message is correct");
+($id, $msg) = $tt->SetStatus('resolved');
+ok($id, $msg);
+like($msg, qr/resolved/i, "Status message is correct");
+($id, $msg) = $tt->SetStatus('resolved');
+ok(!$id,$msg);
+
+
+=end testing
+
+
+=cut
+
+sub SetStatus {
+ my $self = shift;
+ my %args;
+
+ if (@_ == 1) {
+ $args{Status} = shift;
+ }
+ else {
+ %args = (@_);
+ }
+
+ #Check ACL
+ if ( $args{Status} eq 'deleted') {
+ unless ($self->CurrentUserHasRight('DeleteTicket')) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ } else {
+ unless ($self->CurrentUserHasRight('ModifyTicket')) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ }
+
+ if (!$args{Force} && ($args{'Status'} eq 'resolved') && $self->HasUnresolvedDependencies) {
+ return (0, $self->loc('That ticket has unresolved dependencies'));
+ }
+
+ my $now = RT::Date->new( $self->CurrentUser );
+ $now->SetToNow();
+
+ #If we're changing the status from new, record that we've started
+ if ( ( $self->Status =~ /new/ ) && ( $args{Status} ne 'new' ) ) {
+
+ #Set the Started time to "now"
+ $self->_Set( Field => 'Started',
+ Value => $now->ISO,
+ RecordTransaction => 0 );
+ }
+
+ #When we close a ticket, set the 'Resolved' attribute to now.
+ # It's misnamed, but that's just historical.
+ if ( $self->QueueObj->IsInactiveStatus($args{Status}) ) {
+ $self->_Set( Field => 'Resolved',
+ Value => $now->ISO,
+ RecordTransaction => 0 );
+ }
+
+ #Actually update the status
+ my ($val, $msg)= $self->_Set( Field => 'Status',
+ Value => $args{Status},
+ TimeTaken => 0,
+ CheckACL => 0,
+ TransactionType => 'Status' );
+
+ return($val,$msg);
+}
+
+# }}}
+
+# {{{ sub Kill
+
+=head2 Kill
+
+Takes no arguments. Marks this ticket for garbage collection
+
+=cut
+
+sub Kill {
+ my $self = shift;
+ $RT::Logger->crit("'Kill' is deprecated. use 'Delete' instead at (". join(":",caller).").");
+ return $self->Delete;
+}
+
+sub Delete {
+ my $self = shift;
+ return ( $self->SetStatus('deleted') );
+
+ # TODO: garbage collection
+}
+
+# }}}
+
+# {{{ sub Stall
+
+=head2 Stall
+
+Sets this ticket's status to stalled
+
+=cut
+
+sub Stall {
+ my $self = shift;
+ return ( $self->SetStatus('stalled') );
+}
+
+# }}}
+
+# {{{ sub Reject
+
+=head2 Reject
+
+Sets this ticket's status to rejected
+
+=cut
+
+sub Reject {
+ my $self = shift;
+ return ( $self->SetStatus('rejected') );
+}
+
+# }}}
+
+# {{{ sub Open
+
+=head2 Open
+
+Sets this ticket\'s status to Open
+
+=cut
+
+sub Open {
+ my $self = shift;
+ return ( $self->SetStatus('open') );
+}
+
+# }}}
+
+# {{{ sub Resolve
+
+=head2 Resolve
+
+Sets this ticket\'s status to Resolved
+
+=cut
+
+sub Resolve {
+ my $self = shift;
+ return ( $self->SetStatus('resolved') );
+}
+
+# }}}
+
+# }}}
+
+
+# {{{ Actions + Routines dealing with transactions
+
+# {{{ sub SetTold and _SetTold
+
+=head2 SetTold ISO [TIMETAKEN]
+
+Updates the told and records a transaction
+
+=cut
+
+sub SetTold {
+ my $self = shift;
+ my $told;
+ $told = shift if (@_);
+ my $timetaken = shift || 0;
+
+ unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my $datetold = new RT::Date( $self->CurrentUser );
+ if ($told) {
+ $datetold->Set( Format => 'iso',
+ Value => $told );
+ }
+ else {
+ $datetold->SetToNow();
+ }
+
+ return ( $self->_Set( Field => 'Told',
+ Value => $datetold->ISO,
+ TimeTaken => $timetaken,
+ TransactionType => 'Told' ) );
+}
+
+=head2 _SetTold
+
+Updates the told without a transaction or acl check. Useful when we're sending replies.
+
+=cut
+
+sub _SetTold {
+ my $self = shift;
+
+ my $now = new RT::Date( $self->CurrentUser );
+ $now->SetToNow();
+
+ #use __Set to get no ACLs ;)
+ return ( $self->__Set( Field => 'Told',
+ Value => $now->ISO ) );
+}
+
+# }}}
+
+=head2 TransactionBatch
+
+ Returns an array reference of all transactions created on this ticket during
+ this ticket object's lifetime, or undef if there were none.
+
+ Only works when the $RT::UseTransactionBatch config variable is set to true.
+
+=cut
+
+sub TransactionBatch {
+ my $self = shift;
+ return $self->{_TransactionBatch};
+}
+
+sub DESTROY {
+ my $self = shift;
+
+ # DESTROY methods need to localize $@, or it may unset it. This
+ # causes $m->abort to not bubble all of the way up. See perlbug
+ # http://rt.perl.org/rt3/Ticket/Display.html?id=17650
+ local $@;
+
+ # The following line eliminates reentrancy.
+ # It protects against the fact that perl doesn't deal gracefully
+ # when an object's refcount is changed in its destructor.
+ return if $self->{_Destroyed}++;
+
+ my $batch = $self->TransactionBatch or return;
+ return unless @$batch;
+
+ require RT::Scrips;
+ RT::Scrips->new($RT::SystemUser)->Apply(
+ Stage => 'TransactionBatch',
+ TicketObj => $self,
+ TransactionObj => $batch->[0],
+ Type => join(',', (map { $_->Type } @{$batch}) )
+ );
+}
+
+# }}}
+
+# {{{ PRIVATE UTILITY METHODS. Mostly needed so Ticket can be a DBIx::Record
+
+# {{{ sub _OverlayAccessible
+
+sub _OverlayAccessible {
+ {
+ EffectiveId => { 'read' => 1, 'write' => 1, 'public' => 1 },
+ Queue => { 'read' => 1, 'write' => 1 },
+ Requestors => { 'read' => 1, 'write' => 1 },
+ Owner => { 'read' => 1, 'write' => 1 },
+ Subject => { 'read' => 1, 'write' => 1 },
+ InitialPriority => { 'read' => 1, 'write' => 1 },
+ FinalPriority => { 'read' => 1, 'write' => 1 },
+ Priority => { 'read' => 1, 'write' => 1 },
+ Status => { 'read' => 1, 'write' => 1 },
+ TimeEstimated => { 'read' => 1, 'write' => 1 },
+ TimeWorked => { 'read' => 1, 'write' => 1 },
+ TimeLeft => { 'read' => 1, 'write' => 1 },
+ Told => { 'read' => 1, 'write' => 1 },
+ Resolved => { 'read' => 1 },
+ Type => { 'read' => 1 },
+ Starts => { 'read' => 1, 'write' => 1 },
+ Started => { 'read' => 1, 'write' => 1 },
+ Due => { 'read' => 1, 'write' => 1 },
+ Creator => { 'read' => 1, 'auto' => 1 },
+ Created => { 'read' => 1, 'auto' => 1 },
+ LastUpdatedBy => { 'read' => 1, 'auto' => 1 },
+ LastUpdated => { 'read' => 1, 'auto' => 1 }
+ };
+
+}
+
+# }}}
+
+# {{{ sub _Set
+
+sub _Set {
+ my $self = shift;
+
+ my %args = ( Field => undef,
+ Value => undef,
+ TimeTaken => 0,
+ RecordTransaction => 1,
+ UpdateTicket => 1,
+ CheckACL => 1,
+ TransactionType => 'Set',
+ @_ );
+
+ if ($args{'CheckACL'}) {
+ unless ( $self->CurrentUserHasRight('ModifyTicket')) {
+ return ( 0, $self->loc("Permission Denied"));
+ }
+ }
+
+ unless ($args{'UpdateTicket'} || $args{'RecordTransaction'}) {
+ $RT::Logger->error("Ticket->_Set called without a mandate to record an update or update the ticket");
+ return(0, $self->loc("Internal Error"));
+ }
+
+ #if the user is trying to modify the record
+
+ #Take care of the old value we really don't want to get in an ACL loop.
+ # so ask the super::_Value
+ my $Old = $self->SUPER::_Value("$args{'Field'}");
+
+ my ($ret, $msg);
+ if ( $args{'UpdateTicket'} ) {
+
+ #Set the new value
+ ( $ret, $msg ) = $self->SUPER::_Set( Field => $args{'Field'},
+ Value => $args{'Value'} );
+
+ #If we can't actually set the field to the value, don't record
+ # a transaction. instead, get out of here.
+ return ( 0, $msg ) unless $ret;
+ }
+
+ if ( $args{'RecordTransaction'} == 1 ) {
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => $args{'TransactionType'},
+ Field => $args{'Field'},
+ NewValue => $args{'Value'},
+ OldValue => $Old,
+ TimeTaken => $args{'TimeTaken'},
+ );
+ return ( $Trans, scalar $TransObj->BriefDescription );
+ }
+ else {
+ return ( $ret, $msg );
+ }
+}
+
+# }}}
+
+# {{{ sub _Value
+
+=head2 _Value
+
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
+
+=cut
+
+sub _Value {
+
+ my $self = shift;
+ my $field = shift;
+
+ #if the field is public, return it.
+ if ( $self->_Accessible( $field, 'public' ) ) {
+
+ #$RT::Logger->debug("Skipping ACL check for $field\n");
+ return ( $self->SUPER::_Value($field) );
+
+ }
+
+ #If the current user doesn't have ACLs, don't let em at it.
+
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return (undef);
+ }
+ return ( $self->SUPER::_Value($field) );
+
+}
+
+# }}}
+
+# {{{ sub _UpdateTimeTaken
+
+=head2 _UpdateTimeTaken
+
+This routine will increment the timeworked counter. it should
+only be called from _NewTransaction
+
+=cut
+
+sub _UpdateTimeTaken {
+ my $self = shift;
+ my $Minutes = shift;
+ my ($Total);
+
+ $Total = $self->SUPER::_Value("TimeWorked");
+ $Total = ( $Total || 0 ) + ( $Minutes || 0 );
+ $self->SUPER::_Set(
+ Field => "TimeWorked",
+ Value => $Total
+ );
+
+ return ($Total);
+}
+
+# }}}
+
+# }}}
+
+# {{{ Routines dealing with ACCESS CONTROL
+
+# {{{ sub CurrentUserHasRight
+
+=head2 CurrentUserHasRight
+
+ Takes the textual name of a Ticket scoped right (from RT::ACE) and returns
+1 if the user has that right. It returns 0 if the user doesn't have that right.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ return (
+ $self->HasRight(
+ Principal => $self->CurrentUser->UserObj(),
+ Right => "$right"
+ )
+ );
+
+}
+
+# }}}
+
+# {{{ sub HasRight
+
+=head2 HasRight
+
+ Takes a paramhash with the attributes 'Right' and 'Principal'
+ 'Right' is a ticket-scoped textual right from RT::ACE
+ 'Principal' is an RT::User object
+
+ Returns 1 if the principal has the right. Returns undef if not.
+
+=cut
+
+sub HasRight {
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Principal => undef,
+ @_
+ );
+
+ unless ( ( defined $args{'Principal'} ) and ( ref( $args{'Principal'} ) ) )
+ {
+ Carp::cluck;
+ $RT::Logger->crit("Principal attrib undefined for Ticket::HasRight");
+ return(undef);
+ }
+
+ return (
+ $args{'Principal'}->HasRight(
+ Object => $self,
+ Right => $args{'Right'}
+ )
+ );
+}
+
+# }}}
+
+# }}}
+
+=head2 Reminders
+
+Return the Reminders object for this ticket. (It's an RT::Reminders object.)
+It isn't acutally a searchbuilder collection itself.
+
+=cut
+
+sub Reminders {
+ my $self = shift;
+
+ unless ($self->{'__reminders'}) {
+ $self->{'__reminders'} = RT::Reminders->new($self->CurrentUser);
+ $self->{'__reminders'}->Ticket($self->id);
+ }
+ return $self->{'__reminders'};
+
+}
+
+
+
+# {{{ sub Transactions
+
+=head2 Transactions
+
+ Returns an RT::Transactions object of all transactions on this ticket
+
+=cut
+
+sub Transactions {
+ my $self = shift;
+
+ my $transactions = RT::Transactions->new( $self->CurrentUser );
+
+ #If the user has no rights, return an empty object
+ if ( $self->CurrentUserHasRight('ShowTicket') ) {
+ $transactions->LimitToTicket($self->id);
+
+ # if the user may not see comments do not return them
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ $transactions->Limit(
+ FIELD => 'Type',
+ OPERATOR => '!=',
+ VALUE => "Comment"
+ );
+ $transactions->Limit(
+ FIELD => 'Type',
+ OPERATOR => '!=',
+ VALUE => "CommentEmailRecord",
+ ENTRYAGGREGATOR => 'AND'
+ );
+
+ }
+ }
+
+ return ($transactions);
+}
+
+# }}}
+
+
+# {{{ TransactionCustomFields
+
+=head2 TransactionCustomFields
+
+ Returns the custom fields that transactions on tickets will have.
+
+=cut
+
+sub TransactionCustomFields {
+ my $self = shift;
+ return $self->QueueObj->TicketTransactionCustomFields;
+}
+
+# }}}
+
+# {{{ sub CustomFieldValues
+
+=head2 CustomFieldValues
+
+# Do name => id mapping (if needed) before falling back to
+# RT::Record's CustomFieldValues
+
+See L<RT::Record>
+
+=cut
+
+sub CustomFieldValues {
+ my $self = shift;
+ my $field = shift;
+ if ( $field and $field !~ /^\d+$/ ) {
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->LoadByNameAndQueue( Name => $field, Queue => $self->Queue );
+ unless ( $cf->id ) {
+ $cf->LoadByNameAndQueue( Name => $field, Queue => 0 );
+ }
+ unless ( $cf->id ) {
+ # If we didn't find a valid cfid, give up.
+ return RT::CustomFieldValues->new($self->CurrentUser);
+ }
+ }
+ return $self->SUPER::CustomFieldValues($field);
+}
+
+# }}}
+
+# {{{ sub CustomFieldLookupType
+
+=head2 CustomFieldLookupType
+
+Returns the RT::Ticket lookup type, which can be passed to
+RT::CustomField->Create() via the 'LookupType' hash key.
+
+=cut
+
+# }}}
+
+sub CustomFieldLookupType {
+ "RT::Queue-RT::Ticket";
+}
+
+1;
+
+=head1 AUTHOR
+
+Jesse Vincent, jesse@bestpractical.com
+
+=head1 SEE ALSO
+
+RT
+
+=cut
+
diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm
new file mode 100755
index 0000000..354f4c5
--- /dev/null
+++ b/rt/lib/RT/Tickets.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Tickets -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Tickets
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Tickets;
+
+use RT::SearchBuilder;
+use RT::Ticket;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Tickets';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Ticket item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Ticket->new($self->CurrentUser));
+}
+
+ eval "require RT::Tickets_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Tickets_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Tickets_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Tickets_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Tickets_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Tickets_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Tickets_Overlay, RT::Tickets_Vendor, RT::Tickets_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Tickets_Overlay.pm b/rt/lib/RT/Tickets_Overlay.pm
new file mode 100644
index 0000000..8bfbdb7
--- /dev/null
+++ b/rt/lib/RT/Tickets_Overlay.pm
@@ -0,0 +1,3053 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Major Changes:
+
+# - Decimated ProcessRestrictions and broke it into multiple
+# functions joined by a LUT
+# - Semi-Generic SQL stuff moved to another file
+
+# Known Issues: FIXME!
+
+# - ClearRestrictions and Reinitialization is messy and unclear. The
+# only good way to do it is to create a new RT::Tickets object.
+
+=head1 NAME
+
+ RT::Tickets - A collection of Ticket objects
+
+
+=head1 SYNOPSIS
+
+ use RT::Tickets;
+ my $tickets = new RT::Tickets($CurrentUser);
+
+=head1 DESCRIPTION
+
+ A collection of RT::Tickets.
+
+=head1 METHODS
+
+=begin testing
+
+ok (require RT::Tickets);
+ok( my $testtickets = RT::Tickets->new( $RT::SystemUser ) );
+ok( $testtickets->LimitStatus( VALUE => 'deleted' ) );
+# Should be zero until 'allow_deleted_search'
+ok( $testtickets->Count == 0 );
+
+=end testing
+
+=cut
+
+package RT::Tickets;
+
+use strict;
+no warnings qw(redefine);
+
+use RT::CustomFields;
+use DBIx::SearchBuilder::Unique;
+
+# Configuration Tables:
+
+# FIELD_METADATA is a mapping of searchable Field name, to Type, and other
+# metadata.
+
+my %FIELD_METADATA = (
+ Status => [ 'ENUM', ],
+ Queue => [ 'ENUM' => 'Queue', ],
+ Type => [ 'ENUM', ],
+ Creator => [ 'ENUM' => 'User', ],
+ LastUpdatedBy => [ 'ENUM' => 'User', ],
+ Owner => [ 'WATCHERFIELD' => 'Owner', ],
+ EffectiveId => [ 'INT', ],
+ id => [ 'INT', ],
+ InitialPriority => [ 'INT', ],
+ FinalPriority => [ 'INT', ],
+ Priority => [ 'INT', ],
+ TimeLeft => [ 'INT', ],
+ TimeWorked => [ 'INT', ],
+ TimeEstimated => [ 'INT', ],
+ MemberOf => [ 'LINK' => To => 'MemberOf', ],
+ DependsOn => [ 'LINK' => To => 'DependsOn', ],
+ RefersTo => [ 'LINK' => To => 'RefersTo', ],
+ HasMember => [ 'LINK' => From => 'MemberOf', ],
+ DependentOn => [ 'LINK' => From => 'DependsOn', ],
+ DependedOnBy => [ 'LINK' => From => 'DependsOn', ],
+ ReferredToBy => [ 'LINK' => From => 'RefersTo', ],
+ Told => [ 'DATE' => 'Told', ],
+ Starts => [ 'DATE' => 'Starts', ],
+ Started => [ 'DATE' => 'Started', ],
+ Due => [ 'DATE' => 'Due', ],
+ Resolved => [ 'DATE' => 'Resolved', ],
+ LastUpdated => [ 'DATE' => 'LastUpdated', ],
+ Created => [ 'DATE' => 'Created', ],
+ Subject => [ 'STRING', ],
+ Content => [ 'TRANSFIELD', ],
+ ContentType => [ 'TRANSFIELD', ],
+ Filename => [ 'TRANSFIELD', ],
+ TransactionDate => [ 'TRANSDATE', ],
+ Requestor => [ 'WATCHERFIELD' => 'Requestor', ],
+ Requestors => [ 'WATCHERFIELD' => 'Requestor', ],
+ Cc => [ 'WATCHERFIELD' => 'Cc', ],
+ AdminCc => [ 'WATCHERFIELD' => 'AdminCc', ],
+ Watcher => [ 'WATCHERFIELD', ],
+ LinkedTo => [ 'LINKFIELD', ],
+ CustomFieldValue => [ 'CUSTOMFIELD', ],
+ CustomField => [ 'CUSTOMFIELD', ],
+ CF => [ 'CUSTOMFIELD', ],
+ Updated => [ 'TRANSDATE', ],
+ RequestorGroup => [ 'MEMBERSHIPFIELD' => 'Requestor', ],
+ CCGroup => [ 'MEMBERSHIPFIELD' => 'Cc', ],
+ AdminCCGroup => [ 'MEMBERSHIPFIELD' => 'AdminCc', ],
+ WatcherGroup => [ 'MEMBERSHIPFIELD', ],
+);
+
+# Mapping of Field Type to Function
+my %dispatch = (
+ ENUM => \&_EnumLimit,
+ INT => \&_IntLimit,
+ LINK => \&_LinkLimit,
+ DATE => \&_DateLimit,
+ STRING => \&_StringLimit,
+ TRANSFIELD => \&_TransLimit,
+ TRANSDATE => \&_TransDateLimit,
+ WATCHERFIELD => \&_WatcherLimit,
+ MEMBERSHIPFIELD => \&_WatcherMembershipLimit,
+ LINKFIELD => \&_LinkFieldLimit,
+ CUSTOMFIELD => \&_CustomFieldLimit,
+);
+my %can_bundle = (); # WATCHERFIELD => "yes", );
+
+# Default EntryAggregator per type
+# if you specify OP, you must specify all valid OPs
+my %DefaultEA = (
+ INT => 'AND',
+ ENUM => {
+ '=' => 'OR',
+ '!=' => 'AND'
+ },
+ DATE => {
+ '=' => 'OR',
+ '>=' => 'AND',
+ '<=' => 'AND',
+ '>' => 'AND',
+ '<' => 'AND'
+ },
+ STRING => {
+ '=' => 'OR',
+ '!=' => 'AND',
+ 'LIKE' => 'AND',
+ 'NOT LIKE' => 'AND'
+ },
+ TRANSFIELD => 'AND',
+ TRANSDATE => 'AND',
+ LINK => 'OR',
+ LINKFIELD => 'AND',
+ TARGET => 'AND',
+ BASE => 'AND',
+ WATCHERFIELD => {
+ '=' => 'OR',
+ '!=' => 'AND',
+ 'LIKE' => 'OR',
+ 'NOT LIKE' => 'AND'
+ },
+
+ CUSTOMFIELD => 'OR',
+);
+
+# Helper functions for passing the above lexically scoped tables above
+# into Tickets_Overlay_SQL.
+sub FIELDS { return \%FIELD_METADATA }
+sub dispatch { return \%dispatch }
+sub can_bundle { return \%can_bundle }
+
+# Bring in the clowns.
+require RT::Tickets_Overlay_SQL;
+
+# {{{ sub SortFields
+
+our @SORTFIELDS = qw(id Status
+ Queue Subject
+ Owner Created Due Starts Started
+ Told
+ Resolved LastUpdated Priority TimeWorked TimeLeft);
+
+=head2 SortFields
+
+Returns the list of fields that lists of tickets can easily be sorted by
+
+=cut
+
+sub SortFields {
+ my $self = shift;
+ return (@SORTFIELDS);
+}
+
+# }}}
+
+# BEGIN SQL STUFF *********************************
+
+
+sub CleanSlate {
+ my $self = shift;
+ $self->SUPER::CleanSlate( @_ );
+ delete $self->{$_} foreach qw(
+ _sql_cf_alias
+ _sql_group_members_aliases
+ _sql_object_cfv_alias
+ _sql_role_group_aliases
+ _sql_transalias
+ _sql_trattachalias
+ _sql_u_watchers_alias_for_sort
+ _sql_u_watchers_aliases
+ );
+}
+
+=head1 Limit Helper Routines
+
+These routines are the targets of a dispatch table depending on the
+type of field. They all share the same signature:
+
+ my ($self,$field,$op,$value,@rest) = @_;
+
+The values in @rest should be suitable for passing directly to
+DBIx::SearchBuilder::Limit.
+
+Essentially they are an expanded/broken out (and much simplified)
+version of what ProcessRestrictions used to do. They're also much
+more clearly delineated by the TYPE of field being processed.
+
+=head2 _EnumLimit
+
+Handle Fields which are limited to certain values, and potentially
+need to be looked up from another class.
+
+This subroutine actually handles two different kinds of fields. For
+some the user is responsible for limiting the values. (i.e. Status,
+Type).
+
+For others, the value specified by the user will be looked by via
+specified class.
+
+Meta Data:
+ name of class to lookup in (Optional)
+
+=cut
+
+sub _EnumLimit {
+ my ( $sb, $field, $op, $value, @rest ) = @_;
+
+ # SQL::Statement changes != to <>. (Can we remove this now?)
+ $op = "!=" if $op eq "<>";
+
+ die "Invalid Operation: $op for $field"
+ unless $op eq "="
+ or $op eq "!=";
+
+ my $meta = $FIELD_METADATA{$field};
+ if ( defined $meta->[1] && defined $value && $value !~ /^\d+$/ ) {
+ my $class = "RT::" . $meta->[1];
+ my $o = $class->new( $sb->CurrentUser );
+ $o->Load($value);
+ $value = $o->Id;
+ }
+ $sb->_SQLLimit(
+ FIELD => $field,
+ VALUE => $value,
+ OPERATOR => $op,
+ @rest,
+ );
+}
+
+=head2 _IntLimit
+
+Handle fields where the values are limited to integers. (For example,
+Priority, TimeWorked.)
+
+Meta Data:
+ None
+
+=cut
+
+sub _IntLimit {
+ my ( $sb, $field, $op, $value, @rest ) = @_;
+
+ die "Invalid Operator $op for $field"
+ unless $op =~ /^(=|!=|>|<|>=|<=)$/;
+
+ $sb->_SQLLimit(
+ FIELD => $field,
+ VALUE => $value,
+ OPERATOR => $op,
+ @rest,
+ );
+}
+
+=head2 _LinkLimit
+
+Handle fields which deal with links between tickets. (MemberOf, DependsOn)
+
+Meta Data:
+ 1: Direction (From, To)
+ 2: Link Type (MemberOf, DependsOn, RefersTo)
+
+=cut
+
+sub _LinkLimit {
+ my ( $sb, $field, $op, $value, @rest ) = @_;
+
+ my $meta = $FIELD_METADATA{$field};
+ die "Incorrect Metadata for $field"
+ unless defined $meta->[1] && defined $meta->[2];
+
+ die "Invalid Operator $op for $field" unless $op =~ /^(=|!=|IS|IS NOT)$/io;
+
+ my $direction = $meta->[1];
+
+ my $matchfield;
+ my $linkfield;
+ if ( $direction eq 'To' ) {
+ $matchfield = "Target";
+ $linkfield = "Base";
+
+ }
+ elsif ( $direction eq 'From' ) {
+ $linkfield = "Target";
+ $matchfield = "Base";
+
+ }
+ else {
+ die "Invalid link direction '$meta->[1]' for $field\n";
+ }
+
+ my ($is_local, $is_null) = (1, 0);
+ if ( !$value || $value =~ /^null$/io ) {
+ $is_null = 1;
+ $op = ($op =~ /^(=|IS)$/)? 'IS': 'IS NOT';
+ }
+ elsif ( $value =~ /\D/o ) {
+ $is_local = 0;
+ }
+ $matchfield = "Local$matchfield" if $is_local;
+
+ my $is_negative = 0;
+ if ( $op eq '!=' ) {
+ $is_negative = 1;
+ $op = '=';
+ }
+
+#For doing a left join to find "unlinked tickets" we want to generate a query that looks like this
+# SELECT main.* FROM Tickets main
+# LEFT JOIN Links Links_1 ON ( (Links_1.Type = 'MemberOf')
+# AND(main.id = Links_1.LocalTarget))
+# WHERE Links_1.LocalBase IS NULL;
+
+ if ($is_null) {
+ my $linkalias = $sb->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Links',
+ FIELD2 => 'Local' . $linkfield
+ );
+ $sb->SUPER::Limit(
+ LEFTJOIN => $linkalias,
+ FIELD => 'Type',
+ OPERATOR => '=',
+ VALUE => $meta->[2],
+ );
+ $sb->_SQLLimit(
+ @rest,
+ ALIAS => $linkalias,
+ FIELD => $matchfield,
+ OPERATOR => $op,
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ );
+ }
+ elsif ( $is_negative ) {
+ my $linkalias = $sb->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Links',
+ FIELD2 => 'Local' . $linkfield
+ );
+ $sb->SUPER::Limit(
+ LEFTJOIN => $linkalias,
+ FIELD => 'Type',
+ OPERATOR => '=',
+ VALUE => $meta->[2],
+ );
+ $sb->SUPER::Limit(
+ LEFTJOIN => $linkalias,
+ FIELD => $matchfield,
+ OPERATOR => $op,
+ VALUE => $value,
+ );
+ $sb->_SQLLimit(
+ @rest,
+ ALIAS => $linkalias,
+ FIELD => $matchfield,
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ );
+ }
+ else {
+ my $linkalias = $sb->NewAlias('Links');
+ $sb->_OpenParen();
+ $sb->_SQLLimit(
+ @rest,
+ ALIAS => $linkalias,
+ FIELD => 'Type',
+ OPERATOR => '=',
+ VALUE => $meta->[2],
+ );
+ $sb->_SQLLimit(
+ ALIAS => $linkalias,
+ FIELD => 'Local' . $linkfield,
+ OPERATOR => '=',
+ VALUE => 'main.id',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ );
+ $sb->_SQLLimit(
+ ALIAS => $linkalias,
+ FIELD => $matchfield,
+ OPERATOR => $op,
+ VALUE => $value,
+ ENTRYAGGREGATOR => 'AND',
+ );
+ $sb->_CloseParen();
+ }
+}
+
+=head2 _DateLimit
+
+Handle date fields. (Created, LastTold..)
+
+Meta Data:
+ 1: type of link. (Probably not necessary.)
+
+=cut
+
+sub _DateLimit {
+ my ( $sb, $field, $op, $value, @rest ) = @_;
+
+ die "Invalid Date Op: $op"
+ unless $op =~ /^(=|>|<|>=|<=)$/;
+
+ my $meta = $FIELD_METADATA{$field};
+ die "Incorrect Meta Data for $field"
+ unless ( defined $meta->[1] );
+
+ my $date = RT::Date->new( $sb->CurrentUser );
+ $date->Set( Format => 'unknown', Value => $value );
+
+ if ( $op eq "=" ) {
+
+ # if we're specifying =, that means we want everything on a
+ # particular single day. in the database, we need to check for >
+ # and < the edges of that day.
+
+ $date->SetToMidnight( Timezone => 'server' );
+ my $daystart = $date->ISO;
+ $date->AddDay;
+ my $dayend = $date->ISO;
+
+ $sb->_OpenParen;
+
+ $sb->_SQLLimit(
+ FIELD => $meta->[1],
+ OPERATOR => ">=",
+ VALUE => $daystart,
+ @rest,
+ );
+
+ $sb->_SQLLimit(
+ FIELD => $meta->[1],
+ OPERATOR => "<=",
+ VALUE => $dayend,
+ @rest,
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ $sb->_CloseParen;
+
+ }
+ else {
+ $sb->_SQLLimit(
+ FIELD => $meta->[1],
+ OPERATOR => $op,
+ VALUE => $date->ISO,
+ @rest,
+ );
+ }
+}
+
+=head2 _StringLimit
+
+Handle simple fields which are just strings. (Subject,Type)
+
+Meta Data:
+ None
+
+=cut
+
+sub _StringLimit {
+ my ( $sb, $field, $op, $value, @rest ) = @_;
+
+ # FIXME:
+ # Valid Operators:
+ # =, !=, LIKE, NOT LIKE
+
+ $sb->_SQLLimit(
+ FIELD => $field,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ @rest,
+ );
+}
+
+=head2 _TransDateLimit
+
+Handle fields limiting based on Transaction Date.
+
+The inpupt value must be in a format parseable by Time::ParseDate
+
+Meta Data:
+ None
+
+=cut
+
+# This routine should really be factored into translimit.
+sub _TransDateLimit {
+ my ( $sb, $field, $op, $value, @rest ) = @_;
+
+ # See the comments for TransLimit, they apply here too
+
+ unless ( $sb->{_sql_transalias} ) {
+ $sb->{_sql_transalias} = $sb->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Transactions',
+ FIELD2 => 'ObjectId',
+ );
+ $sb->SUPER::Limit(
+ ALIAS => $sb->{_sql_transalias},
+ FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket',
+ ENTRYAGGREGATOR => 'AND',
+ );
+ }
+
+ my $date = RT::Date->new( $sb->CurrentUser );
+ $date->Set( Format => 'unknown', Value => $value );
+
+ $sb->_OpenParen;
+ if ( $op eq "=" ) {
+
+ # if we're specifying =, that means we want everything on a
+ # particular single day. in the database, we need to check for >
+ # and < the edges of that day.
+
+ $date->SetToMidnight( Timezone => 'server' );
+ my $daystart = $date->ISO;
+ $date->AddDay;
+ my $dayend = $date->ISO;
+
+ $sb->_SQLLimit(
+ ALIAS => $sb->{_sql_transalias},
+ FIELD => 'Created',
+ OPERATOR => ">=",
+ VALUE => $daystart,
+ CASESENSITIVE => 0,
+ @rest
+ );
+ $sb->_SQLLimit(
+ ALIAS => $sb->{_sql_transalias},
+ FIELD => 'Created',
+ OPERATOR => "<=",
+ VALUE => $dayend,
+ CASESENSITIVE => 0,
+ @rest,
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ }
+
+ # not searching for a single day
+ else {
+
+ #Search for the right field
+ $sb->_SQLLimit(
+ ALIAS => $sb->{_sql_transalias},
+ FIELD => 'Created',
+ OPERATOR => $op,
+ VALUE => $date->ISO,
+ CASESENSITIVE => 0,
+ @rest
+ );
+ }
+
+ $sb->_CloseParen;
+}
+
+=head2 _TransLimit
+
+Limit based on the Content of a transaction or the ContentType.
+
+Meta Data:
+ none
+
+=cut
+
+sub _TransLimit {
+
+ # Content, ContentType, Filename
+
+ # If only this was this simple. We've got to do something
+ # complicated here:
+
+ #Basically, we want to make sure that the limits apply to
+ #the same attachment, rather than just another attachment
+ #for the same ticket, no matter how many clauses we lump
+ #on. We put them in TicketAliases so that they get nuked
+ #when we redo the join.
+
+ # In the SQL, we might have
+ # (( Content = foo ) or ( Content = bar AND Content = baz ))
+ # The AND group should share the same Alias.
+
+ # Actually, maybe it doesn't matter. We use the same alias and it
+ # works itself out? (er.. different.)
+
+ # Steal more from _ProcessRestrictions
+
+ # FIXME: Maybe look at the previous FooLimit call, and if it was a
+ # TransLimit and EntryAggregator == AND, reuse the Aliases?
+
+ # Or better - store the aliases on a per subclause basis - since
+ # those are going to be the things we want to relate to each other,
+ # anyway.
+
+ # maybe we should not allow certain kinds of aggregation of these
+ # clauses and do a psuedo regex instead? - the problem is getting
+ # them all into the same subclause when you have (A op B op C) - the
+ # way they get parsed in the tree they're in different subclauses.
+
+ my ( $self, $field, $op, $value, @rest ) = @_;
+
+ unless ( $self->{_sql_transalias} ) {
+ $self->{_sql_transalias} = $self->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Transactions',
+ FIELD2 => 'ObjectId',
+ );
+ $self->SUPER::Limit(
+ ALIAS => $self->{_sql_transalias},
+ FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket',
+ ENTRYAGGREGATOR => 'AND',
+ );
+ }
+ unless ( defined $self->{_sql_trattachalias} ) {
+ $self->{_sql_trattachalias} = $self->_SQLJoin(
+ TYPE => 'LEFT', # not all txns have an attachment
+ ALIAS1 => $self->{_sql_transalias},
+ FIELD1 => 'id',
+ TABLE2 => 'Attachments',
+ FIELD2 => 'TransactionId',
+ );
+ }
+
+ $self->_OpenParen;
+
+ #Search for the right field
+ if ($field eq 'Content' and $RT::DontSearchFileAttachments) {
+ $self->_SQLLimit(
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => 'Filename',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ SUBCLAUSE => 'contentquery',
+ ENTRYAGGREGATOR => 'AND',
+ );
+ $self->_SQLLimit(
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => $field,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ @rest,
+ ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => 'contentquery',
+ );
+ } else {
+ $self->_SQLLimit(
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => $field,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ @rest
+ );
+ }
+
+ $self->_CloseParen;
+
+}
+
+=head2 _WatcherLimit
+
+Handle watcher limits. (Requestor, CC, etc..)
+
+Meta Data:
+ 1: Field to query on
+
+
+=begin testing
+
+# Test to make sure that you can search for tickets by requestor address and
+# by requestor name.
+
+my ($id,$msg);
+my $u1 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u1->Create( Name => 'RequestorTestOne', EmailAddress => 'rqtest1@example.com');
+ok ($id,$msg);
+my $u2 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u2->Create( Name => 'RequestorTestTwo', EmailAddress => 'rqtest2@example.com');
+ok ($id,$msg);
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ($trans);
+($id,$trans,$msg) =$t1->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u1->EmailAddress]);
+ok ($id, $msg);
+
+my $t2 = RT::Ticket->new($RT::SystemUser);
+($id,$trans,$msg) =$t2->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u2->EmailAddress]);
+ok ($id, $msg);
+
+
+my $t3 = RT::Ticket->new($RT::SystemUser);
+($id,$trans,$msg) =$t3->Create (Queue => 'general', Subject => 'Requestor test one', Requestor => [$u2->EmailAddress, $u1->EmailAddress]);
+ok ($id, $msg);
+
+
+my $tix1 = RT::Tickets->new($RT::SystemUser);
+$tix1->FromSQL('Requestor.EmailAddress LIKE "rqtest1" OR Requestor.EmailAddress LIKE "rqtest2"');
+
+is ($tix1->Count, 3);
+
+my $tix2 = RT::Tickets->new($RT::SystemUser);
+$tix2->FromSQL('Requestor.Name LIKE "TestOne" OR Requestor.Name LIKE "TestTwo"');
+
+is ($tix2->Count, 3);
+
+
+my $tix3 = RT::Tickets->new($RT::SystemUser);
+$tix3->FromSQL('Requestor.EmailAddress LIKE "rqtest1"');
+
+is ($tix3->Count, 2);
+
+my $tix4 = RT::Tickets->new($RT::SystemUser);
+$tix4->FromSQL('Requestor.Name LIKE "TestOne" ');
+
+is ($tix4->Count, 2);
+
+# Searching for tickets that have two requestors isn't supported
+# There's no way to differentiate "one requestor name that matches foo and bar"
+# and "two requestors, one matching foo and one matching bar"
+
+# my $tix5 = RT::Tickets->new($RT::SystemUser);
+# $tix5->FromSQL('Requestor.Name LIKE "TestOne" AND Requestor.Name LIKE "TestTwo"');
+#
+# is ($tix5->Count, 1);
+#
+# my $tix6 = RT::Tickets->new($RT::SystemUser);
+# $tix6->FromSQL('Requestor.EmailAddress LIKE "rqtest1" AND Requestor.EmailAddress LIKE "rqtest2"');
+#
+# is ($tix6->Count, 1);
+
+
+=end testing
+
+=cut
+
+sub _WatcherLimit {
+ my $self = shift;
+ my $field = shift;
+ my $op = shift;
+ my $value = shift;
+ my %rest = (@_);
+
+ my $meta = $FIELD_METADATA{ $field };
+ my $type = $meta->[1] || '';
+
+ # Owner was ENUM field, so "Owner = 'xxx'" allowed user to
+ # search by id and Name at the same time, this is workaround
+ # to preserve backward compatibility
+ if ( $field eq 'Owner' && !$rest{SUBKEY} && $op =~ /^!?=$/ ) {
+ my $o = RT::User->new( $self->CurrentUser );
+ $o->Load( $value );
+ $self->_SQLLimit(
+ FIELD => 'Owner',
+ OPERATOR => $op,
+ VALUE => $o->Id,
+ %rest,
+ );
+ return;
+ }
+ $rest{SUBKEY} ||= 'EmailAddress';
+
+ my $groups = $self->_RoleGroupsJoin( Type => $type );
+
+ $self->_OpenParen;
+ if ( $op =~ /^IS(?: NOT)?$/ ) {
+ my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+ # to avoid joining the table Users into the query, we just join GM
+ # and make sure we don't match records where group is member of itself
+ $self->SUPER::Limit(
+ LEFTJOIN => $group_members,
+ FIELD => 'GroupId',
+ OPERATOR => '!=',
+ VALUE => "$group_members.MemberId",
+ QUOTEVALUE => 0,
+ );
+ $self->_SQLLimit(
+ ALIAS => $group_members,
+ FIELD => 'GroupId',
+ OPERATOR => $op,
+ VALUE => $value,
+ %rest,
+ );
+ }
+ elsif ( $op =~ /^!=$|^NOT\s+/i ) {
+ # reverse op
+ $op =~ s/!|NOT\s+//i;
+
+ # XXX: we have no way to build correct "Watcher.X != 'Y'" when condition
+ # "X = 'Y'" matches more then one user so we try to fetch two records and
+ # do the right thing when there is only one exist and semi-working solution
+ # otherwise.
+ my $users_obj = RT::Users->new( $self->CurrentUser );
+ $users_obj->Limit(
+ FIELD => $rest{SUBKEY},
+ OPERATOR => $op,
+ VALUE => $value,
+ );
+ $users_obj->OrderBy;
+ $users_obj->RowsPerPage(2);
+ my @users = @{ $users_obj->ItemsArrayRef };
+
+ my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+ if ( @users <= 1 ) {
+ my $uid = 0;
+ $uid = $users[0]->id if @users;
+ $self->SUPER::Limit(
+ LEFTJOIN => $group_members,
+ ALIAS => $group_members,
+ FIELD => 'MemberId',
+ VALUE => $uid,
+ );
+ $self->_SQLLimit(
+ %rest,
+ ALIAS => $group_members,
+ FIELD => 'id',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ );
+ } else {
+ $self->SUPER::Limit(
+ LEFTJOIN => $group_members,
+ FIELD => 'GroupId',
+ OPERATOR => '!=',
+ VALUE => "$group_members.MemberId",
+ QUOTEVALUE => 0,
+ );
+ my $users = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $group_members,
+ FIELD1 => 'MemberId',
+ TABLE2 => 'Users',
+ FIELD2 => 'id',
+ );
+ $self->SUPER::Limit(
+ LEFTJOIN => $users,
+ ALIAS => $users,
+ FIELD => $rest{SUBKEY},
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
+ $self->_SQLLimit(
+ %rest,
+ ALIAS => $users,
+ FIELD => 'id',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ );
+ }
+ } else {
+ my $group_members = $self->_GroupMembersJoin(
+ GroupsAlias => $groups,
+ New => 0,
+ );
+
+ my $users = $self->{'_sql_u_watchers_aliases'}{$group_members};
+ unless ( $users ) {
+ $users = $self->{'_sql_u_watchers_aliases'}{$group_members} =
+ $self->NewAlias('Users');
+ $self->SUPER::Limit(
+ LEFTJOIN => $group_members,
+ ALIAS => $group_members,
+ FIELD => 'MemberId',
+ VALUE => "$users.id",
+ QUOTEVALUE => 0,
+ );
+ }
+
+ # we join users table without adding some join condition between tables,
+ # the only conditions we have are conditions on the table iteslf,
+ # for example Users.EmailAddress = 'x'. We should add this condition to
+ # the top level of the query and bundle it with another similar conditions,
+ # for example "Users.EmailAddress = 'x' OR Users.EmailAddress = 'Y'".
+ # To achive this goal we use own SUBCLAUSE for conditions on the users table.
+ $self->SUPER::Limit(
+ %rest,
+ SUBCLAUSE => '_sql_u_watchers_'. $users,
+ ALIAS => $users,
+ FIELD => $rest{'SUBKEY'},
+ VALUE => $value,
+ OPERATOR => $op,
+ CASESENSITIVE => 0,
+ );
+ # A condition which ties Users and Groups (role groups) is a left join condition
+ # of CachedGroupMembers table. To get correct results of the query we check
+ # if there are matches in CGM table or not using 'cgm.id IS NOT NULL'.
+ $self->_SQLLimit(
+ %rest,
+ ALIAS => $group_members,
+ FIELD => 'id',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ );
+ }
+ $self->_CloseParen;
+}
+
+sub _RoleGroupsJoin {
+ my $self = shift;
+ my %args = (New => 0, Type => '', @_);
+ return $self->{'_sql_role_group_aliases'}{ $args{'Type'} }
+ if $self->{'_sql_role_group_aliases'}{ $args{'Type'} } && !$args{'New'};
+
+ # XXX: this has been fixed in DBIx::SB-1.48
+ # XXX: if we change this from Join to NewAlias+Limit
+ # then Pg and mysql 5.x will complain because SB build wrong query.
+ # Query looks like "FROM (Tickets LEFT JOIN CGM ON(Groups.id = CGM.GroupId)), Groups"
+ # Pg doesn't like that fact that it doesn't know about Groups table yet when
+ # join CGM table into Tickets. Problem is in Join method which doesn't use
+ # ALIAS1 argument when build braces.
+
+ # we always have watcher groups for ticket, so we use INNER join
+ my $groups = $self->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Groups',
+ FIELD2 => 'Instance',
+ ENTRYAGGREGATOR => 'AND',
+ );
+ $self->SUPER::Limit(
+ LEFTJOIN => $groups,
+ ALIAS => $groups,
+ FIELD => 'Domain',
+ VALUE => 'RT::Ticket-Role',
+ );
+ $self->SUPER::Limit(
+ LEFTJOIN => $groups,
+ ALIAS => $groups,
+ FIELD => 'Type',
+ VALUE => $args{'Type'},
+ ) if $args{'Type'};
+
+ $self->{'_sql_role_group_aliases'}{ $args{'Type'} } = $groups
+ unless $args{'New'};
+
+ return $groups;
+}
+
+sub _GroupMembersJoin {
+ my $self = shift;
+ my %args = (New => 1, GroupsAlias => undef, @_);
+
+ return $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} }
+ if $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} }
+ && !$args{'New'};
+
+ my $alias = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $args{'GroupsAlias'},
+ FIELD1 => 'id',
+ TABLE2 => 'CachedGroupMembers',
+ FIELD2 => 'GroupId',
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} } = $alias
+ unless $args{'New'};
+
+ return $alias;
+}
+
+=head2 _WatcherJoin
+
+Helper function which provides joins to a watchers table both for limits
+and for ordering.
+
+=cut
+
+sub _WatcherJoin {
+ my $self = shift;
+ my $type = shift || '';
+
+
+ my $groups = $self->_RoleGroupsJoin( Type => $type );
+ my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+ # XXX: work around, we must hide groups that
+ # are members of the role group we search in,
+ # otherwise them result in wrong NULLs in Users
+ # table and break ordering. Now, we know that
+ # RT doesn't allow to add groups as members of the
+ # ticket roles, so we just hide entries in CGM table
+ # with MemberId == GroupId from results
+ $self->SUPER::Limit(
+ LEFTJOIN => $group_members,
+ FIELD => 'GroupId',
+ OPERATOR => '!=',
+ VALUE => "$group_members.MemberId",
+ QUOTEVALUE => 0,
+ );
+ my $users = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $group_members,
+ FIELD1 => 'MemberId',
+ TABLE2 => 'Users',
+ FIELD2 => 'id',
+ );
+ return ($groups, $group_members, $users);
+}
+
+=head2 _WatcherMembershipLimit
+
+Handle watcher membership limits, i.e. whether the watcher belongs to a
+specific group or not.
+
+Meta Data:
+ 1: Field to query on
+
+SELECT DISTINCT main.*
+FROM
+ Tickets main,
+ Groups Groups_1,
+ CachedGroupMembers CachedGroupMembers_2,
+ Users Users_3
+WHERE (
+ (main.EffectiveId = main.id)
+) AND (
+ (main.Status != 'deleted')
+) AND (
+ (main.Type = 'ticket')
+) AND (
+ (
+ (Users_3.EmailAddress = '22')
+ AND
+ (Groups_1.Domain = 'RT::Ticket-Role')
+ AND
+ (Groups_1.Type = 'RequestorGroup')
+ )
+) AND
+ Groups_1.Instance = main.id
+AND
+ Groups_1.id = CachedGroupMembers_2.GroupId
+AND
+ CachedGroupMembers_2.MemberId = Users_3.id
+ORDER BY main.id ASC
+LIMIT 25
+
+=cut
+
+sub _WatcherMembershipLimit {
+ my ( $self, $field, $op, $value, @rest ) = @_;
+ my %rest = @rest;
+
+ $self->_OpenParen;
+
+ my $groups = $self->NewAlias('Groups');
+ my $groupmembers = $self->NewAlias('CachedGroupMembers');
+ my $users = $self->NewAlias('Users');
+ my $memberships = $self->NewAlias('CachedGroupMembers');
+
+ if ( ref $field ) { # gross hack
+ my @bundle = @$field;
+ $self->_OpenParen;
+ for my $chunk (@bundle) {
+ ( $field, $op, $value, @rest ) = @$chunk;
+ $self->_SQLLimit(
+ ALIAS => $memberships,
+ FIELD => 'GroupId',
+ VALUE => $value,
+ OPERATOR => $op,
+ @rest,
+ );
+ }
+ $self->_CloseParen;
+ }
+ else {
+ $self->_SQLLimit(
+ ALIAS => $memberships,
+ FIELD => 'GroupId',
+ VALUE => $value,
+ OPERATOR => $op,
+ @rest,
+ );
+ }
+
+ # {{{ Tie to groups for tickets we care about
+ $self->_SQLLimit(
+ ALIAS => $groups,
+ FIELD => 'Domain',
+ VALUE => 'RT::Ticket-Role',
+ ENTRYAGGREGATOR => 'AND'
+ );
+
+ $self->Join(
+ ALIAS1 => $groups,
+ FIELD1 => 'Instance',
+ ALIAS2 => 'main',
+ FIELD2 => 'id'
+ );
+
+ # }}}
+
+ # If we care about which sort of watcher
+ my $meta = $FIELD_METADATA{$field};
+ my $type = ( defined $meta->[1] ? $meta->[1] : undef );
+
+ if ($type) {
+ $self->_SQLLimit(
+ ALIAS => $groups,
+ FIELD => 'Type',
+ VALUE => $type,
+ ENTRYAGGREGATOR => 'AND'
+ );
+ }
+
+ $self->Join(
+ ALIAS1 => $groups,
+ FIELD1 => 'id',
+ ALIAS2 => $groupmembers,
+ FIELD2 => 'GroupId'
+ );
+
+ $self->Join(
+ ALIAS1 => $groupmembers,
+ FIELD1 => 'MemberId',
+ ALIAS2 => $users,
+ FIELD2 => 'id'
+ );
+
+ $self->Join(
+ ALIAS1 => $memberships,
+ FIELD1 => 'MemberId',
+ ALIAS2 => $users,
+ FIELD2 => 'id'
+ );
+
+ $self->_CloseParen;
+
+}
+
+sub _LinkFieldLimit {
+ my $restriction;
+ my $self;
+ my $LinkAlias;
+ my %args;
+ if ( $restriction->{'TYPE'} ) {
+ $self->SUPER::Limit(
+ ALIAS => $LinkAlias,
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => 'Type',
+ OPERATOR => '=',
+ VALUE => $restriction->{'TYPE'}
+ );
+ }
+
+ #If we're trying to limit it to things that are target of
+ if ( $restriction->{'TARGET'} ) {
+
+ # If the TARGET is an integer that means that we want to look at
+ # the LocalTarget field. otherwise, we want to look at the
+ # "Target" field
+ my ($matchfield);
+ if ( $restriction->{'TARGET'} =~ /^(\d+)$/ ) {
+ $matchfield = "LocalTarget";
+ }
+ else {
+ $matchfield = "Target";
+ }
+ $self->SUPER::Limit(
+ ALIAS => $LinkAlias,
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => $matchfield,
+ OPERATOR => '=',
+ VALUE => $restriction->{'TARGET'}
+ );
+
+ #If we're searching on target, join the base to ticket.id
+ $self->_SQLJoin(
+ ALIAS1 => 'main',
+ FIELD1 => $self->{'primary_key'},
+ ALIAS2 => $LinkAlias,
+ FIELD2 => 'LocalBase'
+ );
+ }
+
+ #If we're trying to limit it to things that are base of
+ elsif ( $restriction->{'BASE'} ) {
+
+ # If we're trying to match a numeric link, we want to look at
+ # LocalBase, otherwise we want to look at "Base"
+ my ($matchfield);
+ if ( $restriction->{'BASE'} =~ /^(\d+)$/ ) {
+ $matchfield = "LocalBase";
+ }
+ else {
+ $matchfield = "Base";
+ }
+
+ $self->SUPER::Limit(
+ ALIAS => $LinkAlias,
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => $matchfield,
+ OPERATOR => '=',
+ VALUE => $restriction->{'BASE'}
+ );
+
+ #If we're searching on base, join the target to ticket.id
+ $self->_SQLJoin(
+ ALIAS1 => 'main',
+ FIELD1 => $self->{'primary_key'},
+ ALIAS2 => $LinkAlias,
+ FIELD2 => 'LocalTarget'
+ );
+ }
+}
+
+
+=head2 _CustomFieldDecipher
+
+Try and turn a CF descriptor into (cfid, cfname) object pair.
+
+=cut
+
+sub _CustomFieldDecipher {
+ my ($self, $field) = @_;
+
+ my $queue = 0;
+ if ( $field =~ /^(.+?)\.{(.+)}$/ ) {
+ ($queue, $field) = ($1, $2);
+ }
+ $field = $1 if $field =~ /^{(.+)}$/; # trim { }
+
+ my $cfid;
+ if ( $queue ) {
+ my $q = RT::Queue->new( $self->CurrentUser );
+ $q->Load( $queue ) if $queue;
+
+ my $cf;
+ if ( $q->id ) {
+ # $queue = $q->Name; # should we normalize the queue?
+ $cf = $q->CustomField( $field );
+ }
+ else {
+ $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->LoadByNameAndQueue( Queue => 0, Name => $field );
+ }
+ $cfid = $cf->id if $cf;
+ }
+
+ return ($queue, $field, $cfid);
+
+}
+
+=head2 _CustomFieldJoin
+
+Factor out the Join of custom fields so we can use it for sorting too
+
+=cut
+
+sub _CustomFieldJoin {
+ my ($self, $cfkey, $cfid, $field) = @_;
+ # Perform one Join per CustomField
+ if ( $self->{_sql_object_cfv_alias}{$cfkey} ||
+ $self->{_sql_cf_alias}{$cfkey} )
+ {
+ return ( $self->{_sql_object_cfv_alias}{$cfkey},
+ $self->{_sql_cf_alias}{$cfkey} );
+ }
+
+ my ($TicketCFs, $CFs);
+ if ( $cfid ) {
+ $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ TYPE => 'left',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFieldValues',
+ FIELD2 => 'ObjectId',
+ );
+ $self->SUPER::Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'CustomField',
+ VALUE => $cfid,
+ ENTRYAGGREGATOR => 'AND'
+ );
+ }
+ else {
+ my $ocfalias = $self->Join(
+ TYPE => 'LEFT',
+ FIELD1 => 'Queue',
+ TABLE2 => 'ObjectCustomFields',
+ FIELD2 => 'ObjectId',
+ );
+
+ $self->SUPER::Limit(
+ LEFTJOIN => $ocfalias,
+ ENTRYAGGREGATOR => 'OR',
+ FIELD => 'ObjectId',
+ VALUE => '0',
+ );
+
+ $CFs = $self->{_sql_cf_alias}{$cfkey} = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $ocfalias,
+ FIELD1 => 'CustomField',
+ TABLE2 => 'CustomFields',
+ FIELD2 => 'id',
+ );
+
+ $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ TYPE => 'left',
+ ALIAS1 => $CFs,
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFieldValues',
+ FIELD2 => 'CustomField',
+ );
+ $self->SUPER::Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'ObjectId',
+ VALUE => 'main.id',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ );
+ }
+ $self->SUPER::Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket',
+ ENTRYAGGREGATOR => 'AND'
+ );
+ $self->SUPER::Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'Disabled',
+ OPERATOR => '=',
+ VALUE => '0',
+ ENTRYAGGREGATOR => 'AND'
+ );
+
+ return ($TicketCFs, $CFs);
+}
+
+=head2 _CustomFieldLimit
+
+Limit based on CustomFields
+
+Meta Data:
+ none
+
+=cut
+
+sub _CustomFieldLimit {
+ my ( $self, $_field, $op, $value, @rest ) = @_;
+
+ my %rest = @rest;
+ my $field = $rest{SUBKEY} || die "No field specified";
+
+ # For our sanity, we can only limit on one queue at a time
+
+ my ($queue, $cfid);
+ ($queue, $field, $cfid ) = $self->_CustomFieldDecipher( $field );
+
+# If we're trying to find custom fields that don't match something, we
+# want tickets where the custom field has no value at all. Note that
+# we explicitly don't include the "IS NULL" case, since we would
+# otherwise end up with a redundant clause.
+
+ my $null_columns_ok;
+ if ( ( $op =~ /^NOT LIKE$/i ) or ( $op eq '!=' ) ) {
+ $null_columns_ok = 1;
+ }
+
+ my $cfkey = $cfid ? $cfid : "$queue.$field";
+ my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+
+ $self->_OpenParen;
+
+ if ( $CFs ) {
+ $self->SUPER::Limit(
+ ALIAS => $CFs,
+ FIELD => 'Name',
+ VALUE => $field,
+ ENTRYAGGREGATOR => 'AND',
+ );
+ }
+
+ $self->_OpenParen if $null_columns_ok;
+
+ $self->_SQLLimit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => $op,
+ VALUE => $value,
+ QUOTEVALUE => 1,
+ @rest
+ );
+
+ if ($null_columns_ok) {
+ $self->_SQLLimit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'OR',
+ );
+ $self->_CloseParen;
+ }
+
+ $self->_CloseParen;
+
+}
+
+# End Helper Functions
+
+# End of SQL Stuff -------------------------------------------------
+
+# {{{ Allow sorting on watchers
+
+=head2 OrderByCols ARRAY
+
+A modified version of the OrderBy method which automatically joins where
+C<ALIAS> is set to the name of a watcher type.
+
+=cut
+
+sub OrderByCols {
+ my $self = shift;
+ my @args = @_;
+ my $clause;
+ my @res = ();
+ my $order = 0;
+
+ foreach my $row (@args) {
+ if ( $row->{ALIAS} || $row->{FIELD} !~ /\./ ) {
+ push @res, $row;
+ next;
+ }
+ my ( $field, $subkey ) = split /\./, $row->{FIELD}, 2;
+ my $meta = $self->FIELDS->{$field};
+ if ( $meta->[0] eq 'WATCHERFIELD' ) {
+ # cache alias as we want to use one alias per watcher type for sorting
+ my $users = $self->{_sql_u_watchers_alias_for_sort}{ $meta->[1] };
+ unless ( $users ) {
+ $self->{_sql_u_watchers_alias_for_sort}{ $meta->[1] }
+ = $users = ( $self->_WatcherJoin( $meta->[1] ) )[2];
+ }
+ push @res, { %$row, ALIAS => $users, FIELD => $subkey };
+ } elsif ( $meta->[0] eq 'CUSTOMFIELD' ) {
+ my ($queue, $field, $cfid ) = $self->_CustomFieldDecipher( $subkey );
+ my $cfkey = $cfid ? $cfid : "$queue.$field";
+ my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ unless ($cfid) {
+ # For those cases where we are doing a join against the
+ # CF name, and don't have a CFid, use Unique to make sure
+ # we don't show duplicate tickets. NOTE: I'm pretty sure
+ # this will stay mixed in for the life of the
+ # class/package, and not just for the life of the object.
+ # Potential performance issue.
+ require DBIx::SearchBuilder::Unique;
+ DBIx::SearchBuilder::Unique->import;
+ }
+ my $CFvs = $self->Join(
+ TYPE => 'left',
+ ALIAS1 => $TicketCFs,
+ FIELD1 => 'CustomField',
+ TABLE2 => 'CustomFieldValues',
+ FIELD2 => 'CustomField',
+ );
+ $self->SUPER::Limit(
+ LEFTJOIN => $CFvs,
+ FIELD => 'Name',
+ QUOTEVALUE => 0,
+ VALUE => $TicketCFs . ".Content",
+ ENTRYAGGREGATOR => 'AND'
+ );
+
+ push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' };
+ push @res, { %$row, ALIAS => $TicketCFs, FIELD => 'Content' };
+ } elsif ( $field eq "Custom" && $subkey eq "Ownership") {
+ # PAW logic is "reversed"
+ my $order = "ASC";
+ if (exists $row->{ORDER} ) {
+ my $o = $row->{ORDER};
+ delete $row->{ORDER};
+ $order = "DESC" if $o =~ /asc/i;
+ }
+
+ # Unowned
+ # Else
+
+ # Ticket.Owner 1 0 0
+ my $ownerId = $self->CurrentUser->Id;
+ push @res, { %$row, FIELD => "Owner=$ownerId", ORDER => $order } ;
+
+ # Unowned Tickets 0 1 0
+ my $nobodyId = $RT::Nobody->Id;
+ push @res, { %$row, FIELD => "Owner=$nobodyId", ORDER => $order } ;
+
+ push @res, { %$row, FIELD => "Priority", ORDER => $order } ;
+ }
+ else {
+ push @res, $row;
+ }
+ }
+ return $self->SUPER::OrderByCols(@res);
+}
+
+# }}}
+
+# {{{ Limit the result set based on content
+
+# {{{ sub Limit
+
+=head2 Limit
+
+Takes a paramhash with the fields FIELD, OPERATOR, VALUE and DESCRIPTION
+Generally best called from LimitFoo methods
+
+=cut
+
+sub Limit {
+ my $self = shift;
+ my %args = (
+ FIELD => undef,
+ OPERATOR => '=',
+ VALUE => undef,
+ DESCRIPTION => undef,
+ @_
+ );
+ $args{'DESCRIPTION'} = $self->loc(
+ "[_1] [_2] [_3]", $args{'FIELD'},
+ $args{'OPERATOR'}, $args{'VALUE'}
+ )
+ if ( !defined $args{'DESCRIPTION'} );
+
+ my $index = $self->_NextIndex;
+
+# make the TicketRestrictions hash the equivalent of whatever we just passed in;
+
+ %{ $self->{'TicketRestrictions'}{$index} } = %args;
+
+ $self->{'RecalcTicketLimits'} = 1;
+
+# If we're looking at the effective id, we don't want to append the other clause
+# which limits us to tickets where id = effective id
+ if ( $args{'FIELD'} eq 'EffectiveId'
+ && ( !$args{'ALIAS'} || $args{'ALIAS'} eq 'main' ) )
+ {
+ $self->{'looking_at_effective_id'} = 1;
+ }
+
+ if ( $args{'FIELD'} eq 'Type'
+ && ( !$args{'ALIAS'} || $args{'ALIAS'} eq 'main' ) )
+ {
+ $self->{'looking_at_type'} = 1;
+ }
+
+ return ($index);
+}
+
+# }}}
+
+=head2 FreezeLimits
+
+Returns a frozen string suitable for handing back to ThawLimits.
+
+=cut
+
+sub _FreezeThawKeys {
+ 'TicketRestrictions', 'restriction_index', 'looking_at_effective_id',
+ 'looking_at_type';
+}
+
+# {{{ sub FreezeLimits
+
+sub FreezeLimits {
+ my $self = shift;
+ require Storable;
+ require MIME::Base64;
+ MIME::Base64::base64_encode(
+ Storable::freeze( \@{$self}{ $self->_FreezeThawKeys } ) );
+}
+
+# }}}
+
+=head2 ThawLimits
+
+Take a frozen Limits string generated by FreezeLimits and make this tickets
+object have that set of limits.
+
+=cut
+
+# {{{ sub ThawLimits
+
+sub ThawLimits {
+ my $self = shift;
+ my $in = shift;
+
+ #if we don't have $in, get outta here.
+ return undef unless ($in);
+
+ $self->{'RecalcTicketLimits'} = 1;
+
+ require Storable;
+ require MIME::Base64;
+
+ #We don't need to die if the thaw fails.
+ @{$self}{ $self->_FreezeThawKeys }
+ = eval { @{ Storable::thaw( MIME::Base64::base64_decode($in) ) }; };
+
+ $RT::Logger->error($@) if $@;
+
+}
+
+# }}}
+
+# {{{ Limit by enum or foreign key
+
+# {{{ sub LimitQueue
+
+=head2 LimitQueue
+
+LimitQueue takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of = or !=. (It defaults to =).
+VALUE is a queue id or Name.
+
+
+=cut
+
+sub LimitQueue {
+ my $self = shift;
+ my %args = (
+ VALUE => undef,
+ OPERATOR => '=',
+ @_
+ );
+
+ #TODO VALUE should also take queue objects
+ if ( defined $args{'VALUE'} && $args{'VALUE'} !~ /^\d+$/ ) {
+ my $queue = new RT::Queue( $self->CurrentUser );
+ $queue->Load( $args{'VALUE'} );
+ $args{'VALUE'} = $queue->Id;
+ }
+
+ # What if they pass in an Id? Check for isNum() and convert to
+ # string.
+
+ #TODO check for a valid queue here
+
+ $self->Limit(
+ FIELD => 'Queue',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join(
+ ' ', $self->loc('Queue'), $args{'OPERATOR'}, $args{'VALUE'},
+ ),
+ );
+
+}
+
+# }}}
+
+# {{{ sub LimitStatus
+
+=head2 LimitStatus
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of = or !=.
+VALUE is a status.
+
+RT adds Status != 'deleted' until object has
+allow_deleted_search internal property set.
+$tickets->{'allow_deleted_search'} = 1;
+$tickets->LimitStatus( VALUE => 'deleted' );
+
+=cut
+
+sub LimitStatus {
+ my $self = shift;
+ my %args = (
+ OPERATOR => '=',
+ @_
+ );
+ $self->Limit(
+ FIELD => 'Status',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Status'), $args{'OPERATOR'},
+ $self->loc( $args{'VALUE'} ) ),
+ );
+}
+
+# }}}
+
+# {{{ sub IgnoreType
+
+=head2 IgnoreType
+
+If called, this search will not automatically limit the set of results found
+to tickets of type "Ticket". Tickets of other types, such as "project" and
+"approval" will be found.
+
+=cut
+
+sub IgnoreType {
+ my $self = shift;
+
+ # Instead of faking a Limit that later gets ignored, fake up the
+ # fact that we're already looking at type, so that the check in
+ # Tickets_Overlay_SQL/FromSQL goes down the right branch
+
+ # $self->LimitType(VALUE => '__any');
+ $self->{looking_at_type} = 1;
+}
+
+# }}}
+
+# {{{ sub LimitType
+
+=head2 LimitType
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of = or !=, it defaults to "=".
+VALUE is a string to search for in the type of the ticket.
+
+
+
+=cut
+
+sub LimitType {
+ my $self = shift;
+ my %args = (
+ OPERATOR => '=',
+ VALUE => undef,
+ @_
+ );
+ $self->Limit(
+ FIELD => 'Type',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Type'), $args{'OPERATOR'}, $args{'Limit'}, ),
+ );
+}
+
+# }}}
+
+# }}}
+
+# {{{ Limit by string field
+
+# {{{ sub LimitSubject
+
+=head2 LimitSubject
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of = or !=.
+VALUE is a string to search for in the subject of the ticket.
+
+=cut
+
+sub LimitSubject {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'Subject',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Subject'), $args{'OPERATOR'}, $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# }}}
+
+# {{{ Limit based on ticket numerical attributes
+# Things that can be > < = !=
+
+# {{{ sub LimitId
+
+=head2 LimitId
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, >, < or !=.
+VALUE is a ticket Id to search for
+
+=cut
+
+sub LimitId {
+ my $self = shift;
+ my %args = (
+ OPERATOR => '=',
+ @_
+ );
+
+ $self->Limit(
+ FIELD => 'id',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION =>
+ join( ' ', $self->loc('Id'), $args{'OPERATOR'}, $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# {{{ sub LimitPriority
+
+=head2 LimitPriority
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, >, < or !=.
+VALUE is a value to match the ticket\'s priority against
+
+=cut
+
+sub LimitPriority {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'Priority',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Priority'),
+ $args{'OPERATOR'}, $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# {{{ sub LimitInitialPriority
+
+=head2 LimitInitialPriority
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, >, < or !=.
+VALUE is a value to match the ticket\'s initial priority against
+
+
+=cut
+
+sub LimitInitialPriority {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'InitialPriority',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Initial Priority'), $args{'OPERATOR'},
+ $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# {{{ sub LimitFinalPriority
+
+=head2 LimitFinalPriority
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, >, < or !=.
+VALUE is a value to match the ticket\'s final priority against
+
+=cut
+
+sub LimitFinalPriority {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'FinalPriority',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Final Priority'), $args{'OPERATOR'},
+ $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# {{{ sub LimitTimeWorked
+
+=head2 LimitTimeWorked
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, >, < or !=.
+VALUE is a value to match the ticket's TimeWorked attribute
+
+=cut
+
+sub LimitTimeWorked {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'TimeWorked',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Time Worked'),
+ $args{'OPERATOR'}, $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# {{{ sub LimitTimeLeft
+
+=head2 LimitTimeLeft
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, >, < or !=.
+VALUE is a value to match the ticket's TimeLeft attribute
+
+=cut
+
+sub LimitTimeLeft {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'TimeLeft',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Time Left'),
+ $args{'OPERATOR'}, $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# }}}
+
+# {{{ Limiting based on attachment attributes
+
+# {{{ sub LimitContent
+
+=head2 LimitContent
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, LIKE, NOT LIKE or !=.
+VALUE is a string to search for in the body of the ticket
+
+=cut
+
+sub LimitContent {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'Content',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Ticket content'), $args{'OPERATOR'},
+ $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# {{{ sub LimitFilename
+
+=head2 LimitFilename
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, LIKE, NOT LIKE or !=.
+VALUE is a string to search for in the body of the ticket
+
+=cut
+
+sub LimitFilename {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'Filename',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Attachment filename'), $args{'OPERATOR'},
+ $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+# {{{ sub LimitContentType
+
+=head2 LimitContentType
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of =, LIKE, NOT LIKE or !=.
+VALUE is a content type to search ticket attachments for
+
+=cut
+
+sub LimitContentType {
+ my $self = shift;
+ my %args = (@_);
+ $self->Limit(
+ FIELD => 'ContentType',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Ticket content type'), $args{'OPERATOR'},
+ $args{'VALUE'}, ),
+ );
+}
+
+# }}}
+
+# }}}
+
+# {{{ Limiting based on people
+
+# {{{ sub LimitOwner
+
+=head2 LimitOwner
+
+Takes a paramhash with the fields OPERATOR and VALUE.
+OPERATOR is one of = or !=.
+VALUE is a user id.
+
+=cut
+
+sub LimitOwner {
+ my $self = shift;
+ my %args = (
+ OPERATOR => '=',
+ @_
+ );
+
+ my $owner = new RT::User( $self->CurrentUser );
+ $owner->Load( $args{'VALUE'} );
+
+ # FIXME: check for a valid $owner
+ $self->Limit(
+ FIELD => 'Owner',
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ DESCRIPTION => join( ' ',
+ $self->loc('Owner'), $args{'OPERATOR'}, $owner->Name(), ),
+ );
+
+}
+
+# }}}
+
+# {{{ Limiting watchers
+
+# {{{ sub LimitWatcher
+
+=head2 LimitWatcher
+
+ Takes a paramhash with the fields OPERATOR, TYPE and VALUE.
+ OPERATOR is one of =, LIKE, NOT LIKE or !=.
+ VALUE is a value to match the ticket\'s watcher email addresses against
+ TYPE is the sort of watchers you want to match against. Leave it undef if you want to search all of them
+
+=begin testing
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+$t1->Create(Queue => 'general', Subject => "LimitWatchers test", Requestors => \['requestor1@example.com']);
+
+=end testing
+
+=cut
+
+sub LimitWatcher {
+ my $self = shift;
+ my %args = (
+ OPERATOR => '=',
+ VALUE => undef,
+ TYPE => undef,
+ @_
+ );
+
+ #build us up a description
+ my ( $watcher_type, $desc );
+ if ( $args{'TYPE'} ) {
+ $watcher_type = $args{'TYPE'};
+ }
+ else {
+ $watcher_type = "Watcher";
+ }
+
+ $self->Limit(
+ FIELD => $watcher_type,
+ VALUE => $args{'VALUE'},
+ OPERATOR => $args{'OPERATOR'},
+ TYPE => $args{'TYPE'},
+ DESCRIPTION => join( ' ',
+ $self->loc($watcher_type),
+ $args{'OPERATOR'}, $args{'VALUE'}, ),
+ );
+}
+
+sub LimitRequestor {
+ my $self = shift;
+ my %args = (@_);
+ $RT::Logger->error( "Tickets->LimitRequestor is deprecated at ("
+ . join( ":", caller )
+ . ")" );
+ $self->LimitWatcher( TYPE => 'Requestor', @_ );
+
+}
+
+# }}}
+
+# }}}
+
+# }}}
+
+# {{{ Limiting based on links
+
+# {{{ LimitLinkedTo
+
+=head2 LimitLinkedTo
+
+LimitLinkedTo takes a paramhash with two fields: TYPE and TARGET
+TYPE limits the sort of link we want to search on
+
+TYPE = { RefersTo, MemberOf, DependsOn }
+
+TARGET is the id or URI of the TARGET of the link
+(TARGET used to be 'TICKET'. 'TICKET' is deprecated, but will be treated as TARGET
+
+=cut
+
+sub LimitLinkedTo {
+ my $self = shift;
+ my %args = (
+ TICKET => undef,
+ TARGET => undef,
+ TYPE => undef,
+ OPERATOR => '=',
+ @_
+ );
+
+ $self->Limit(
+ FIELD => 'LinkedTo',
+ BASE => undef,
+ TARGET => ( $args{'TARGET'} || $args{'TICKET'} ),
+ TYPE => $args{'TYPE'},
+ DESCRIPTION => $self->loc(
+ "Tickets [_1] by [_2]",
+ $self->loc( $args{'TYPE'} ),
+ ( $args{'TARGET'} || $args{'TICKET'} )
+ ),
+ OPERATOR => $args{'OPERATOR'},
+ );
+}
+
+# }}}
+
+# {{{ LimitLinkedFrom
+
+=head2 LimitLinkedFrom
+
+LimitLinkedFrom takes a paramhash with two fields: TYPE and BASE
+TYPE limits the sort of link we want to search on
+
+
+BASE is the id or URI of the BASE of the link
+(BASE used to be 'TICKET'. 'TICKET' is deprecated, but will be treated as BASE
+
+
+=cut
+
+sub LimitLinkedFrom {
+ my $self = shift;
+ my %args = (
+ BASE => undef,
+ TICKET => undef,
+ TYPE => undef,
+ OPERATOR => '=',
+ @_
+ );
+
+ # translate RT2 From/To naming to RT3 TicketSQL naming
+ my %fromToMap = qw(DependsOn DependentOn
+ MemberOf HasMember
+ RefersTo ReferredToBy);
+
+ my $type = $args{'TYPE'};
+ $type = $fromToMap{$type} if exists( $fromToMap{$type} );
+
+ $self->Limit(
+ FIELD => 'LinkedTo',
+ TARGET => undef,
+ BASE => ( $args{'BASE'} || $args{'TICKET'} ),
+ TYPE => $type,
+ DESCRIPTION => $self->loc(
+ "Tickets [_1] [_2]",
+ $self->loc( $args{'TYPE'} ),
+ ( $args{'BASE'} || $args{'TICKET'} )
+ ),
+ OPERATOR => $args{'OPERATOR'},
+ );
+}
+
+# }}}
+
+# {{{ LimitMemberOf
+sub LimitMemberOf {
+ my $self = shift;
+ my $ticket_id = shift;
+ return $self->LimitLinkedTo(
+ @_,
+ TARGET => $ticket_id,
+ TYPE => 'MemberOf',
+ );
+}
+
+# }}}
+
+# {{{ LimitHasMember
+sub LimitHasMember {
+ my $self = shift;
+ my $ticket_id = shift;
+ return $self->LimitLinkedFrom(
+ @_,
+ BASE => "$ticket_id",
+ TYPE => 'HasMember',
+ );
+
+}
+
+# }}}
+
+# {{{ LimitDependsOn
+
+sub LimitDependsOn {
+ my $self = shift;
+ my $ticket_id = shift;
+ return $self->LimitLinkedTo(
+ @_,
+ TARGET => $ticket_id,
+ TYPE => 'DependsOn',
+ );
+
+}
+
+# }}}
+
+# {{{ LimitDependedOnBy
+
+sub LimitDependedOnBy {
+ my $self = shift;
+ my $ticket_id = shift;
+ return $self->LimitLinkedFrom(
+ @_,
+ BASE => $ticket_id,
+ TYPE => 'DependentOn',
+ );
+
+}
+
+# }}}
+
+# {{{ LimitRefersTo
+
+sub LimitRefersTo {
+ my $self = shift;
+ my $ticket_id = shift;
+ return $self->LimitLinkedTo(
+ @_,
+ TARGET => $ticket_id,
+ TYPE => 'RefersTo',
+ );
+
+}
+
+# }}}
+
+# {{{ LimitReferredToBy
+
+sub LimitReferredToBy {
+ my $self = shift;
+ my $ticket_id = shift;
+ return $self->LimitLinkedFrom(
+ @_,
+ BASE => $ticket_id,
+ TYPE => 'ReferredToBy',
+ );
+}
+
+# }}}
+
+# }}}
+
+# {{{ limit based on ticket date attribtes
+
+# {{{ sub LimitDate
+
+=head2 LimitDate (FIELD => 'DateField', OPERATOR => $oper, VALUE => $ISODate)
+
+Takes a paramhash with the fields FIELD OPERATOR and VALUE.
+
+OPERATOR is one of > or <
+VALUE is a date and time in ISO format in GMT
+FIELD is one of Starts, Started, Told, Created, Resolved, LastUpdated
+
+There are also helper functions of the form LimitFIELD that eliminate
+the need to pass in a FIELD argument.
+
+=cut
+
+sub LimitDate {
+ my $self = shift;
+ my %args = (
+ FIELD => undef,
+ VALUE => undef,
+ OPERATOR => undef,
+
+ @_
+ );
+
+ #Set the description if we didn't get handed it above
+ unless ( $args{'DESCRIPTION'} ) {
+ $args{'DESCRIPTION'} = $args{'FIELD'} . " "
+ . $args{'OPERATOR'} . " "
+ . $args{'VALUE'} . " GMT";
+ }
+
+ $self->Limit(%args);
+
+}
+
+# }}}
+
+sub LimitCreated {
+ my $self = shift;
+ $self->LimitDate( FIELD => 'Created', @_ );
+}
+
+sub LimitDue {
+ my $self = shift;
+ $self->LimitDate( FIELD => 'Due', @_ );
+
+}
+
+sub LimitStarts {
+ my $self = shift;
+ $self->LimitDate( FIELD => 'Starts', @_ );
+
+}
+
+sub LimitStarted {
+ my $self = shift;
+ $self->LimitDate( FIELD => 'Started', @_ );
+}
+
+sub LimitResolved {
+ my $self = shift;
+ $self->LimitDate( FIELD => 'Resolved', @_ );
+}
+
+sub LimitTold {
+ my $self = shift;
+ $self->LimitDate( FIELD => 'Told', @_ );
+}
+
+sub LimitLastUpdated {
+ my $self = shift;
+ $self->LimitDate( FIELD => 'LastUpdated', @_ );
+}
+
+#
+# {{{ sub LimitTransactionDate
+
+=head2 LimitTransactionDate (OPERATOR => $oper, VALUE => $ISODate)
+
+Takes a paramhash with the fields FIELD OPERATOR and VALUE.
+
+OPERATOR is one of > or <
+VALUE is a date and time in ISO format in GMT
+
+
+=cut
+
+sub LimitTransactionDate {
+ my $self = shift;
+ my %args = (
+ FIELD => 'TransactionDate',
+ VALUE => undef,
+ OPERATOR => undef,
+
+ @_
+ );
+
+ # <20021217042756.GK28744@pallas.fsck.com>
+ # "Kill It" - Jesse.
+
+ #Set the description if we didn't get handed it above
+ unless ( $args{'DESCRIPTION'} ) {
+ $args{'DESCRIPTION'} = $args{'FIELD'} . " "
+ . $args{'OPERATOR'} . " "
+ . $args{'VALUE'} . " GMT";
+ }
+
+ $self->Limit(%args);
+
+}
+
+# }}}
+
+# }}}
+
+# {{{ Limit based on custom fields
+# {{{ sub LimitCustomField
+
+=head2 LimitCustomField
+
+Takes a paramhash of key/value pairs with the following keys:
+
+=over 4
+
+=item CUSTOMFIELD - CustomField name or id. If a name is passed, an additional parameter QUEUE may also be passed to distinguish the custom field.
+
+=item OPERATOR - The usual Limit operators
+
+=item VALUE - The value to compare against
+
+=back
+
+=cut
+
+sub LimitCustomField {
+ my $self = shift;
+ my %args = (
+ VALUE => undef,
+ CUSTOMFIELD => undef,
+ OPERATOR => '=',
+ DESCRIPTION => undef,
+ FIELD => 'CustomFieldValue',
+ QUOTEVALUE => 1,
+ @_
+ );
+
+ my $CF = RT::CustomField->new( $self->CurrentUser );
+ if ( $args{CUSTOMFIELD} =~ /^\d+$/ ) {
+ $CF->Load( $args{CUSTOMFIELD} );
+ }
+ else {
+ $CF->LoadByNameAndQueue(
+ Name => $args{CUSTOMFIELD},
+ Queue => $args{QUEUE}
+ );
+ $args{CUSTOMFIELD} = $CF->Id;
+ }
+
+ #If we are looking to compare with a null value.
+ if ( $args{'OPERATOR'} =~ /^is$/i ) {
+ $args{'DESCRIPTION'}
+ ||= $self->loc( "Custom field [_1] has no value.", $CF->Name );
+ }
+ elsif ( $args{'OPERATOR'} =~ /^is not$/i ) {
+ $args{'DESCRIPTION'}
+ ||= $self->loc( "Custom field [_1] has a value.", $CF->Name );
+ }
+
+ # if we're not looking to compare with a null value
+ else {
+ $args{'DESCRIPTION'} ||= $self->loc( "Custom field [_1] [_2] [_3]",
+ $CF->Name, $args{OPERATOR}, $args{VALUE} );
+ }
+
+ my $q = "";
+ if ( $CF->Queue ) {
+ my $qo = new RT::Queue( $self->CurrentUser );
+ $qo->Load( $CF->Queue );
+ $q = $qo->Name;
+ }
+
+ my @rest;
+ @rest = ( ENTRYAGGREGATOR => 'AND' )
+ if ( $CF->Type eq 'SelectMultiple' );
+
+ $self->Limit(
+ VALUE => $args{VALUE},
+ FIELD => "CF."
+ . (
+ $q
+ ? $q . ".{" . $CF->Name . "}"
+ : $CF->Name
+ ),
+ OPERATOR => $args{OPERATOR},
+ CUSTOMFIELD => 1,
+ @rest,
+ );
+
+ $self->{'RecalcTicketLimits'} = 1;
+}
+
+# }}}
+# }}}
+
+# {{{ sub _NextIndex
+
+=head2 _NextIndex
+
+Keep track of the counter for the array of restrictions
+
+=cut
+
+sub _NextIndex {
+ my $self = shift;
+ return ( $self->{'restriction_index'}++ );
+}
+
+# }}}
+
+# }}}
+
+# {{{ Core bits to make this a DBIx::SearchBuilder object
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = "Tickets";
+ $self->{'RecalcTicketLimits'} = 1;
+ $self->{'looking_at_effective_id'} = 0;
+ $self->{'looking_at_type'} = 0;
+ $self->{'restriction_index'} = 1;
+ $self->{'primary_key'} = "id";
+ delete $self->{'items_array'};
+ delete $self->{'item_map'};
+ delete $self->{'columns_to_display'};
+ $self->SUPER::_Init(@_);
+
+ $self->_InitSQL;
+
+}
+
+# }}}
+
+# {{{ sub Count
+sub Count {
+ my $self = shift;
+ $self->_ProcessRestrictions() if ( $self->{'RecalcTicketLimits'} == 1 );
+ return ( $self->SUPER::Count() );
+}
+
+# }}}
+
+# {{{ sub CountAll
+sub CountAll {
+ my $self = shift;
+ $self->_ProcessRestrictions() if ( $self->{'RecalcTicketLimits'} == 1 );
+ return ( $self->SUPER::CountAll() );
+}
+
+# }}}
+
+# {{{ sub ItemsArrayRef
+
+=head2 ItemsArrayRef
+
+Returns a reference to the set of all items found in this search
+
+=cut
+
+sub ItemsArrayRef {
+ my $self = shift;
+ my @items;
+
+ unless ( $self->{'items_array'} ) {
+
+ my $placeholder = $self->_ItemsCounter;
+ $self->GotoFirstItem();
+ while ( my $item = $self->Next ) {
+ push( @{ $self->{'items_array'} }, $item );
+ }
+ $self->GotoItem($placeholder);
+ $self->{'items_array'}
+ = $self->ItemsOrderBy( $self->{'items_array'} );
+ }
+ return ( $self->{'items_array'} );
+}
+
+# }}}
+
+# {{{ sub Next
+sub Next {
+ my $self = shift;
+
+ $self->_ProcessRestrictions() if ( $self->{'RecalcTicketLimits'} == 1 );
+
+ my $Ticket = $self->SUPER::Next();
+ if ( ( defined($Ticket) ) and ( ref($Ticket) ) ) {
+
+ if ( $Ticket->__Value('Status') eq 'deleted'
+ && !$self->{'allow_deleted_search'} )
+ {
+ return ( $self->Next() );
+ }
+
+ # Since Ticket could be granted with more rights instead
+ # of being revoked, it's ok if queue rights allow
+ # ShowTicket. It seems need another query, but we have
+ # rights cache in Principal::HasRight.
+ elsif ($Ticket->QueueObj->CurrentUserHasRight('ShowTicket')
+ || $Ticket->CurrentUserHasRight('ShowTicket') )
+ {
+ return ($Ticket);
+ }
+
+ if ( $Ticket->__Value('Status') eq 'deleted' ) {
+ return ( $self->Next() );
+ }
+
+ # Since Ticket could be granted with more rights instead
+ # of being revoked, it's ok if queue rights allow
+ # ShowTicket. It seems need another query, but we have
+ # rights cache in Principal::HasRight.
+ elsif ($Ticket->QueueObj->CurrentUserHasRight('ShowTicket')
+ || $Ticket->CurrentUserHasRight('ShowTicket') )
+ {
+ return ($Ticket);
+ }
+
+ #If the user doesn't have the right to show this ticket
+ else {
+ return ( $self->Next() );
+ }
+ }
+
+ #if there never was any ticket
+ else {
+ return (undef);
+ }
+
+}
+
+# }}}
+
+# }}}
+
+# {{{ Deal with storing and restoring restrictions
+
+# {{{ sub LoadRestrictions
+
+=head2 LoadRestrictions
+
+LoadRestrictions takes a string which can fully populate the TicketRestrictons hash.
+TODO It is not yet implemented
+
+=cut
+
+# }}}
+
+# {{{ sub DescribeRestrictions
+
+=head2 DescribeRestrictions
+
+takes nothing.
+Returns a hash keyed by restriction id.
+Each element of the hash is currently a one element hash that contains DESCRIPTION which
+is a description of the purpose of that TicketRestriction
+
+=cut
+
+sub DescribeRestrictions {
+ my $self = shift;
+
+ my ( $row, %listing );
+
+ foreach $row ( keys %{ $self->{'TicketRestrictions'} } ) {
+ $listing{$row} = $self->{'TicketRestrictions'}{$row}{'DESCRIPTION'};
+ }
+ return (%listing);
+}
+
+# }}}
+
+# {{{ sub RestrictionValues
+
+=head2 RestrictionValues FIELD
+
+Takes a restriction field and returns a list of values this field is restricted
+to.
+
+=cut
+
+sub RestrictionValues {
+ my $self = shift;
+ my $field = shift;
+ map $self->{'TicketRestrictions'}{$_}{'VALUE'}, grep {
+ $self->{'TicketRestrictions'}{$_}{'FIELD'} eq $field
+ && $self->{'TicketRestrictions'}{$_}{'OPERATOR'} eq "="
+ }
+ keys %{ $self->{'TicketRestrictions'} };
+}
+
+# }}}
+
+# {{{ sub ClearRestrictions
+
+=head2 ClearRestrictions
+
+Removes all restrictions irretrievably
+
+=cut
+
+sub ClearRestrictions {
+ my $self = shift;
+ delete $self->{'TicketRestrictions'};
+ $self->{'looking_at_effective_id'} = 0;
+ $self->{'looking_at_type'} = 0;
+ $self->{'RecalcTicketLimits'} = 1;
+}
+
+# }}}
+
+# {{{ sub DeleteRestriction
+
+=head2 DeleteRestriction
+
+Takes the row Id of a restriction (From DescribeRestrictions' output, for example.
+Removes that restriction from the session's limits.
+
+=cut
+
+sub DeleteRestriction {
+ my $self = shift;
+ my $row = shift;
+ delete $self->{'TicketRestrictions'}{$row};
+
+ $self->{'RecalcTicketLimits'} = 1;
+
+ #make the underlying easysearch object forget all its preconceptions
+}
+
+# }}}
+
+# {{{ sub _RestrictionsToClauses
+
+# Convert a set of oldstyle SB Restrictions to Clauses for RQL
+
+sub _RestrictionsToClauses {
+ my $self = shift;
+
+ my $row;
+ my %clause;
+ foreach $row ( keys %{ $self->{'TicketRestrictions'} } ) {
+ my $restriction = $self->{'TicketRestrictions'}{$row};
+
+ #use Data::Dumper;
+ #print Dumper($restriction),"\n";
+
+ # We need to reimplement the subclause aggregation that SearchBuilder does.
+ # Default Subclause is ALIAS.FIELD, and default ALIAS is 'main',
+ # Then SB AND's the different Subclauses together.
+
+ # So, we want to group things into Subclauses, convert them to
+ # SQL, and then join them with the appropriate DefaultEA.
+ # Then join each subclause group with AND.
+
+ my $field = $restriction->{'FIELD'};
+ my $realfield = $field; # CustomFields fake up a fieldname, so
+ # we need to figure that out
+
+ # One special case
+ # Rewrite LinkedTo meta field to the real field
+ if ( $field =~ /LinkedTo/ ) {
+ $realfield = $field = $restriction->{'TYPE'};
+ }
+
+ # Two special case
+ # Handle subkey fields with a different real field
+ if ( $field =~ /^(\w+)\./ ) {
+ $realfield = $1;
+ }
+
+ die "I don't know about $field yet"
+ unless ( exists $FIELD_METADATA{$realfield}
+ or $restriction->{CUSTOMFIELD} );
+
+ my $type = $FIELD_METADATA{$realfield}->[0];
+ my $op = $restriction->{'OPERATOR'};
+
+ my $value = (
+ grep {defined}
+ map { $restriction->{$_} } qw(VALUE TICKET BASE TARGET)
+ )[0];
+
+ # this performs the moral equivalent of defined or/dor/C<//>,
+ # without the short circuiting.You need to use a 'defined or'
+ # type thing instead of just checking for truth values, because
+ # VALUE could be 0.(i.e. "false")
+
+ # You could also use this, but I find it less aesthetic:
+ # (although it does short circuit)
+ #( defined $restriction->{'VALUE'}? $restriction->{VALUE} :
+ # defined $restriction->{'TICKET'} ?
+ # $restriction->{TICKET} :
+ # defined $restriction->{'BASE'} ?
+ # $restriction->{BASE} :
+ # defined $restriction->{'TARGET'} ?
+ # $restriction->{TARGET} )
+
+ my $ea = $restriction->{ENTRYAGGREGATOR}
+ || $DefaultEA{$type}
+ || "AND";
+ if ( ref $ea ) {
+ die "Invalid operator $op for $field ($type)"
+ unless exists $ea->{$op};
+ $ea = $ea->{$op};
+ }
+
+ # Each CustomField should be put into a different Clause so they
+ # are ANDed together.
+ if ( $restriction->{CUSTOMFIELD} ) {
+ $realfield = $field;
+ }
+
+ exists $clause{$realfield} or $clause{$realfield} = [];
+
+ # Escape Quotes
+ $field =~ s!(['"])!\\$1!g;
+ $value =~ s!(['"])!\\$1!g;
+ my $data = [ $ea, $type, $field, $op, $value ];
+
+ # here is where we store extra data, say if it's a keyword or
+ # something. (I.e. "TYPE SPECIFIC STUFF")
+
+ #print Dumper($data);
+ push @{ $clause{$realfield} }, $data;
+ }
+ return \%clause;
+}
+
+# }}}
+
+# {{{ sub _ProcessRestrictions
+
+=head2 _ProcessRestrictions PARAMHASH
+
+# The new _ProcessRestrictions is somewhat dependent on the SQL stuff,
+# but isn't quite generic enough to move into Tickets_Overlay_SQL.
+
+=cut
+
+sub _ProcessRestrictions {
+ my $self = shift;
+
+ #Blow away ticket aliases since we'll need to regenerate them for
+ #a new search
+ delete $self->{'TicketAliases'};
+ delete $self->{'items_array'};
+ delete $self->{'item_map'};
+ delete $self->{'raw_rows'};
+ delete $self->{'rows'};
+ delete $self->{'count_all'};
+
+ my $sql = $self->Query; # Violating the _SQL namespace
+ if ( !$sql || $self->{'RecalcTicketLimits'} ) {
+
+ # "Restrictions to Clauses Branch\n";
+ my $clauseRef = eval { $self->_RestrictionsToClauses; };
+ if ($@) {
+ $RT::Logger->error( "RestrictionsToClauses: " . $@ );
+ $self->FromSQL("");
+ }
+ else {
+ $sql = $self->ClausesToSQL($clauseRef);
+ $self->FromSQL($sql) if $sql;
+ }
+ }
+
+ $self->{'RecalcTicketLimits'} = 0;
+
+}
+
+=head2 _BuildItemMap
+
+ # Build up a map of first/last/next/prev items, so that we can display search nav quickly
+
+=cut
+
+sub _BuildItemMap {
+ my $self = shift;
+
+ my $items = $self->ItemsArrayRef;
+ my $prev = 0;
+
+ delete $self->{'item_map'};
+ if ( $items->[0] ) {
+ $self->{'item_map'}->{'first'} = $items->[0]->EffectiveId;
+ while ( my $item = shift @$items ) {
+ my $id = $item->EffectiveId;
+ $self->{'item_map'}->{$id}->{'defined'} = 1;
+ $self->{'item_map'}->{$id}->{prev} = $prev;
+ $self->{'item_map'}->{$id}->{next} = $items->[0]->EffectiveId
+ if ( $items->[0] );
+ $prev = $id;
+ }
+ $self->{'item_map'}->{'last'} = $prev;
+ }
+}
+
+=head2 ItemMap
+
+Returns an a map of all items found by this search. The map is of the form
+
+$ItemMap->{'first'} = first ticketid found
+$ItemMap->{'last'} = last ticketid found
+$ItemMap->{$id}->{prev} = the ticket id found before $id
+$ItemMap->{$id}->{next} = the ticket id found after $id
+
+=cut
+
+sub ItemMap {
+ my $self = shift;
+ $self->_BuildItemMap()
+ unless ( $self->{'items_array'} and $self->{'item_map'} );
+ return ( $self->{'item_map'} );
+}
+
+=cut
+
+
+
+}
+
+
+
+# }}}
+
+# }}}
+
+=head2 PrepForSerialization
+
+You don't want to serialize a big tickets object, as the {items} hash will be instantly invalid _and_ eat lots of space
+
+=cut
+
+sub PrepForSerialization {
+ my $self = shift;
+ delete $self->{'items'};
+ $self->RedoSearch();
+}
+
+=head1 FLAGS
+
+RT::Tickets supports several flags which alter search behavior:
+
+
+allow_deleted_search (Otherwise never show deleted tickets in search results)
+looking_at_type (otherwise limit to type=ticket)
+
+These flags are set by calling
+
+$tickets->{'flagname'} = 1;
+
+BUG: There should be an API for this
+
+=cut
+
+=begin testing
+
+# We assume that we've got some tickets hanging around from before.
+ok( my $unlimittickets = RT::Tickets->new( $RT::SystemUser ) );
+ok( $unlimittickets->UnLimit );
+ok( $unlimittickets->Count > 0, "UnLimited tickets object should return tickets" );
+
+=end testing
+
+1;
+
+
+
diff --git a/rt/lib/RT/Tickets_Overlay_SQL.pm b/rt/lib/RT/Tickets_Overlay_SQL.pm
new file mode 100644
index 0000000..4531a16
--- /dev/null
+++ b/rt/lib/RT/Tickets_Overlay_SQL.pm
@@ -0,0 +1,587 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::Tickets;
+
+use strict;
+use warnings;
+
+# Import configuration data from the lexcial scope of __PACKAGE__ (or
+# at least where those two Subroutines are defined.)
+
+my %FIELD_METADATA = %{FIELDS()};
+my %dispatch = %{dispatch()};
+my %can_bundle = %{can_bundle()};
+
+# Lower Case version of FIELDS, for case insensitivity
+my %lcfields = map { ( lc($_) => $_ ) } (keys %FIELD_METADATA);
+
+sub _InitSQL {
+ my $self = shift;
+
+ # How many of these do we actually still use?
+
+ # Private Member Variales (which should get cleaned)
+ $self->{'_sql_linksc'} = 0;
+ $self->{'_sql_watchersc'} = 0;
+ $self->{'_sql_keywordsc'} = 0;
+ $self->{'_sql_subclause'} = "a";
+ $self->{'_sql_first'} = 0;
+ $self->{'_sql_opstack'} = [''];
+ $self->{'_sql_linkalias'} = undef;
+ $self->{'_sql_transalias'} = undef;
+ $self->{'_sql_trattachalias'} = undef;
+ $self->{'_sql_object_cf_alias'} = undef;
+ $self->{'_sql_depth'} = 0;
+ $self->{'_sql_localdepth'} = 0;
+ $self->{'_sql_query'} = '';
+ $self->{'_sql_looking_at'} = {};
+ $self->{'_sql_columns_to_display'} = [];
+
+}
+
+sub _SQLLimit {
+ my $self = shift;
+ my %args = (@_);
+ if ($args{'FIELD'} eq 'EffectiveId' &&
+ (!$args{'ALIAS'} || $args{'ALIAS'} eq 'main' ) ) {
+ $self->{'looking_at_effective_id'} = 1;
+ }
+
+ if ($args{'FIELD'} eq 'Type' &&
+ (!$args{'ALIAS'} || $args{'ALIAS'} eq 'main' ) ) {
+ $self->{'looking_at_type'} = 1;
+ }
+
+ # All SQL stuff goes into one SB subclause so we can deal with all
+ # the aggregation
+ $self->SUPER::Limit(%args,
+ SUBCLAUSE => 'ticketsql');
+}
+
+sub _SQLJoin {
+ # All SQL stuff goes into one SB subclause so we can deal with all
+ # the aggregation
+ my $this = shift;
+
+ $this->SUPER::Join(@_,
+ SUBCLAUSE => 'ticketsql');
+}
+
+# Helpers
+sub _OpenParen {
+ $_[0]->SUPER::_OpenParen( 'ticketsql' );
+}
+sub _CloseParen {
+ $_[0]->SUPER::_CloseParen( 'ticketsql' );
+}
+
+=head1 SQL Functions
+
+=cut
+
+=head2 Robert's Simple SQL Parser
+
+Documentation In Progress
+
+The Parser/Tokenizer is a relatively simple state machine that scans through a SQL WHERE clause type string extracting a token at a time (where a token is:
+
+ VALUE -> quoted string or number
+ AGGREGator -> AND or OR
+ KEYWORD -> quoted string or single word
+ OPerator -> =,!=,LIKE,etc..
+ PARENthesis -> open or close.
+
+And that stream of tokens is passed through the "machine" in order to build up a structure that looks like:
+
+ KEY OP VALUE
+ AND KEY OP VALUE
+ OR KEY OP VALUE
+
+That also deals with parenthesis for nesting. (The parentheses are
+just handed off the SearchBuilder)
+
+=cut
+
+use Regexp::Common qw /delimited/;
+
+# States
+use constant VALUE => 1;
+use constant AGGREG => 2;
+use constant OP => 4;
+use constant OPEN_PAREN => 8;
+use constant CLOSE_PAREN => 16;
+use constant KEYWORD => 32;
+my @tokens = qw[VALUE AGGREG OP OPEN_PAREN CLOSE_PAREN KEYWORD];
+
+my $re_aggreg = qr[(?i:AND|OR)];
+my $re_delim = qr[$RE{delimited}{-delim=>qq{\'\"}}];
+my $re_value = qr[$re_delim|\d+|NULL];
+my $re_keyword = qr[$re_delim|(?:\{|\}|\w|\.)+];
+my $re_op = qr[=|!=|>=|<=|>|<|(?i:IS NOT)|(?i:IS)|(?i:NOT LIKE)|(?i:LIKE)]; # long to short
+my $re_open_paren = qr'\(';
+my $re_close_paren = qr'\)';
+
+sub _close_bundle
+{
+ my ($self, @bundle) = @_;
+ return unless @bundle;
+ if (@bundle == 1) {
+ $bundle[0]->{dispatch}->(
+ $self,
+ $bundle[0]->{key},
+ $bundle[0]->{op},
+ $bundle[0]->{val},
+ SUBCLAUSE => "",
+ ENTRYAGGREGATOR => $bundle[0]->{ea},
+ SUBKEY => $bundle[0]->{subkey},
+ );
+ } else {
+ my @args;
+ for my $chunk (@bundle) {
+ push @args, [
+ $chunk->{key},
+ $chunk->{op},
+ $chunk->{val},
+ SUBCLAUSE => "",
+ ENTRYAGGREGATOR => $chunk->{ea},
+ SUBKEY => $chunk->{subkey},
+ ];
+ }
+ $bundle[0]->{dispatch}->(
+ $self, \@args,
+ );
+ }
+}
+
+sub _parser {
+ my ($self,$string) = @_;
+ my $want = KEYWORD | OPEN_PAREN;
+ my $last = undef;
+
+ my $depth = 0;
+ my @bundle;
+
+ my ($ea,$key,$op,$value) = ("","","","");
+
+ # order of matches in the RE is important.. op should come early,
+ # because it has spaces in it. otherwise "NOT LIKE" might be parsed
+ # as a keyword or value.
+
+
+
+
+
+ while ($string =~ /(
+ $re_aggreg
+ |$re_op
+ |$re_keyword
+ |$re_value
+ |$re_open_paren
+ |$re_close_paren
+ )/iogx ) {
+ my $val = $1;
+ my $current = 0;
+
+ # Highest priority is last
+ $current = OP if ($want & OP) && $val =~ /^$re_op$/io;
+ $current = VALUE if ($want & VALUE) && $val =~ /^$re_value$/io;
+ $current = KEYWORD if ($want & KEYWORD) && $val =~ /^$re_keyword$/io;
+ $current = AGGREG if ($want & AGGREG) && $val =~ /^$re_aggreg$/io;
+ $current = OPEN_PAREN if ($want & OPEN_PAREN) && $val =~ /^$re_open_paren$/io;
+ $current = CLOSE_PAREN if ($want & CLOSE_PAREN) && $val =~ /^$re_close_paren$/io;
+
+
+ unless ($current && $want & $current) {
+ # Error
+ # FIXME: I will only print out the highest $want value
+ die "Error near ->$val<- expecting a ", $tokens[((log $want)/(log 2))], " in $string\n";
+ }
+
+ # State Machine:
+
+ #$RT::Logger->debug("We've just found a '$current' called '$val'");
+
+ # Parens are highest priority
+ if ($current & OPEN_PAREN) {
+ $self->_close_bundle(@bundle); @bundle = ();
+ $depth++;
+ $self->_OpenParen;
+
+ $want = KEYWORD | OPEN_PAREN;
+ }
+ elsif ( $current & CLOSE_PAREN ) {
+ $self->_close_bundle(@bundle); @bundle = ();
+ $depth--;
+ $self->_CloseParen;
+
+ $want = CLOSE_PAREN | AGGREG;
+ }
+ elsif ( $current & AGGREG ) {
+ $ea = $val;
+ $want = KEYWORD | OPEN_PAREN;
+ }
+ elsif ( $current & KEYWORD ) {
+ $key = $val;
+ $want = OP;
+ }
+ elsif ( $current & OP ) {
+ $op = $val;
+ $want = VALUE;
+ }
+ elsif ( $current & VALUE ) {
+ $value = $val;
+
+ # Remove surrounding quotes from $key, $val
+ # (in future, simplify as for($key,$val) { action on $_ })
+ if ($key =~ /$re_delim/o) {
+ substr($key,0,1) = "";
+ substr($key,-1,1) = "";
+ }
+ if ($val =~ /$re_delim/o) {
+ substr($val,0,1) = "";
+ substr($val,-1,1) = "";
+ }
+ # Unescape escaped characters
+ $key =~ s!\\(.)!$1!g;
+ $val =~ s!\\(.)!$1!g;
+ # print "$ea Key=[$key] op=[$op] val=[$val]\n";
+
+
+ my $subkey = '';
+ if ($key =~ /^(.+?)\.(.+)$/) {
+ $key = $1;
+ $subkey = $2;
+ }
+
+ my $class;
+ if (exists $lcfields{lc $key}) {
+ $key = $lcfields{lc $key};
+ $class = $FIELD_METADATA{$key}->[0];
+ }
+ # no longer have a default, since CF's are now a real class, not fallthrough
+ # fixme: "default class" is not Generic.
+
+
+ die "Unknown field: $key" unless $class;
+
+ $self->{_sql_localdepth} = 0;
+ die "No such dispatch method: $class"
+ unless exists $dispatch{$class};
+ my $sub = $dispatch{$class} || die;;
+ if ($can_bundle{$class} &&
+ (!@bundle ||
+ ($bundle[-1]->{dispatch} == $sub &&
+ $bundle[-1]->{key} eq $key &&
+ $bundle[-1]->{subkey} eq $subkey)))
+ {
+ push @bundle, {
+ dispatch => $sub,
+ key => $key,
+ op => $op,
+ val => $val,
+ ea => $ea || "",
+ subkey => $subkey,
+ };
+ } else {
+ $self->_close_bundle(@bundle); @bundle = ();
+ $sub->(
+ $self,
+ $key,
+ $op,
+ $val,
+ SUBCLAUSE => "", # don't need anymore
+ ENTRYAGGREGATOR => $ea || "",
+ SUBKEY => $subkey,
+ );
+ }
+
+ $self->{_sql_looking_at}{lc $key} = 1;
+
+ ($ea,$key,$op,$value) = ("","","","");
+
+ $want = CLOSE_PAREN | AGGREG;
+ } else {
+ die "I'm lost";
+ }
+
+ $last = $current;
+ } # while
+
+ $self->_close_bundle(@bundle); @bundle = ();
+
+ die "Incomplete query"
+ unless (($want | CLOSE_PAREN) || ($want | KEYWORD));
+
+ die "Incomplete Query"
+ unless ($last && ($last | CLOSE_PAREN) || ($last || VALUE));
+
+ # This will never happen, because the parser will complain
+ die "Mismatched parentheses"
+ unless $depth == 0;
+
+}
+
+
+=head2 ClausesToSQL
+
+=cut
+
+sub ClausesToSQL {
+ my $self = shift;
+ my $clauses = shift;
+ my @sql;
+
+ for my $f (keys %{$clauses}) {
+ my $sql;
+ my $first = 1;
+
+ # Build SQL from the data hash
+ for my $data ( @{ $clauses->{$f} } ) {
+ $sql .= $data->[0] unless $first; $first=0; # ENTRYAGGREGATOR
+ $sql .= " '". $data->[2] . "' "; # FIELD
+ $sql .= $data->[3] . " "; # OPERATOR
+ $sql .= "'". $data->[4] . "' "; # VALUE
+ }
+
+ push @sql, " ( " . $sql . " ) ";
+ }
+
+ return join("AND",@sql);
+}
+
+=head2 FromSQL
+
+Convert a RT-SQL string into a set of SearchBuilder restrictions.
+
+Returns (1, 'Status message') on success and (0, 'Error Message') on
+failure.
+
+
+=begin testing
+
+use RT::Tickets;
+use strict;
+
+my $tix = RT::Tickets->new($RT::SystemUser);
+{
+ my $query = "Status = 'open'";
+ my ($status, $msg) = $tix->FromSQL($query);
+ ok ($status, "correct query") or diag("error: $msg");
+}
+
+
+my (@created,%created);
+my $string = 'subject/content SQL test';
+{
+ my $t = RT::Ticket->new($RT::SystemUser);
+ ok( $t->Create(Queue => 'General', Subject => $string), "Ticket Created");
+ $created{ $t->Id }++; push @created, $t->Id;
+}
+
+{
+ my $Message = MIME::Entity->build(
+ Subject => 'this is my subject',
+ From => 'jesse@example.com',
+ Data => [ $string ],
+ );
+
+ my $t = RT::Ticket->new($RT::SystemUser);
+ ok( $t->Create( Queue => 'General',
+ Subject => 'another ticket',
+ MIMEObj => $Message,
+ MemberOf => $created[0]
+ ),
+ "Ticket Created"
+ );
+ $created{ $t->Id }++; push @created, $t->Id;
+}
+
+{
+ my $query = ("Subject LIKE '$string' OR Content LIKE '$string'");
+ my ($status, $msg) = $tix->FromSQL($query);
+ ok ($status, "correct query") or diag("error: $msg");
+
+ my $count = 0;
+ while (my $tick = $tix->Next) {
+ $count++ if $created{ $tick->id };
+ }
+ is ($count, scalar @created, "number of returned tickets same as entered");
+}
+
+{
+ my $query = "id = $created[0] OR MemberOf = $created[0]";
+ my ($status, $msg) = $tix->FromSQL($query);
+ ok ($status, "correct query") or diag("error: $msg");
+
+ my $count = 0;
+ while (my $tick = $tix->Next) {
+ $count++ if $created{ $tick->id };
+ }
+ is ($count, scalar @created, "number of returned tickets same as entered");
+}
+
+
+=end testing
+
+
+=cut
+
+sub FromSQL {
+ my ($self,$query) = @_;
+
+ {
+ # preserve first_row and show_rows across the CleanSlate
+ local($self->{'first_row'}, $self->{'show_rows'});
+ $self->CleanSlate;
+ }
+ $self->_InitSQL();
+
+ return (1,$self->loc("No Query")) unless $query;
+
+ $self->{_sql_query} = $query;
+ eval { $self->_parser( $query ); };
+ if ($@) {
+ $RT::Logger->error( "Query error in <<$query>>:\n$@" );
+ return(0,$@);
+ }
+ # We only want to look at EffectiveId's (mostly) for these searches.
+ unless (exists $self->{_sql_looking_at}{'effectiveid'}) {
+ $self->SUPER::Limit( FIELD => 'EffectiveId',
+ ENTRYAGGREGATOR => 'AND',
+ OPERATOR => '=',
+ QUOTEVALUE => 0,
+ VALUE => 'main.id'
+ ); #TODO, we shouldn't be hard #coding the tablename to main.
+ }
+ # FIXME: Need to bring this logic back in
+
+ # if ($self->_isLimited && (! $self->{'looking_at_effective_id'})) {
+ # $self->SUPER::Limit( FIELD => 'EffectiveId',
+ # OPERATOR => '=',
+ # QUOTEVALUE => 0,
+ # VALUE => 'main.id'); #TODO, we shouldn't be hard coding the tablename to main.
+ # }
+ # --- This is hardcoded above. This comment block can probably go.
+ # Or, we need to reimplement the looking_at_effective_id toggle.
+
+ # Unless we've explicitly asked to look at a specific Type, we need
+ # to limit to it.
+ unless ($self->{looking_at_type}) {
+ $self->SUPER::Limit( FIELD => 'Type', OPERATOR => '=', VALUE => 'ticket');
+ }
+
+ # We don't want deleted tickets unless 'allow_deleted_search' is set
+ unless( $self->{'allow_deleted_search'} ) {
+ $self->SUPER::Limit(FIELD => 'Status',
+ OPERATOR => '!=',
+ VALUE => 'deleted');
+ }
+
+
+ # set SB's dirty flag
+ $self->{'must_redo_search'} = 1;
+ $self->{'RecalcTicketLimits'} = 0;
+
+ return (1,$self->loc("Valid Query"));
+
+}
+
+=head2 Query
+
+Returns the query that this object was initialized with
+
+=cut
+
+sub Query {
+ my $self = shift;
+ return ($self->{_sql_query});
+}
+
+
+
+1;
+
+=pod
+
+=head2 Exceptions
+
+Most of the RT code does not use Exceptions (die/eval) but it is used
+in the TicketSQL code for simplicity and historical reasons. Lest you
+be worried that the dies will trigger user visible errors, all are
+trapped via evals.
+
+99% of the dies fall in subroutines called via FromSQL and then parse.
+(This includes all of the _FooLimit routines in Tickets_Overlay.pm.)
+The other 1% or so are via _ProcessRestrictions.
+
+All dies are trapped by eval {}s, and will be logged at the 'error'
+log level. The general failure mode is to not display any tickets.
+
+=head2 General Flow
+
+Legacy Layer:
+
+ Legacy LimitFoo routines build up a RestrictionsHash
+
+ _ProcessRestrictions converts the Restrictions to Clauses
+ ([key,op,val,rest]).
+
+ Clauses are converted to RT-SQL (TicketSQL)
+
+New RT-SQL Layer:
+
+ FromSQL calls the parser
+
+ The parser calls the _FooLimit routines to do DBIx::SearchBuilder
+ limits.
+
+And then the normal SearchBuilder/Ticket routines are used for
+display/navigation.
+
+=cut
+
diff --git a/rt/lib/RT/Transaction.pm b/rt/lib/RT/Transaction.pm
new file mode 100755
index 0000000..67c5cd0
--- /dev/null
+++ b/rt/lib/RT/Transaction.pm
@@ -0,0 +1,442 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::Transaction
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::Transaction;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Transactions');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(64) 'ObjectType'.
+ int(11) 'ObjectId'.
+ int(11) 'TimeTaken'.
+ varchar(20) 'Type'.
+ varchar(40) 'Field'.
+ varchar(255) 'OldValue'.
+ varchar(255) 'NewValue'.
+ varchar(255) 'ReferenceType'.
+ int(11) 'OldReference'.
+ int(11) 'NewReference'.
+ varchar(255) 'Data'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ ObjectType => '',
+ ObjectId => '0',
+ TimeTaken => '0',
+ Type => '',
+ Field => '',
+ OldValue => '',
+ NewValue => '',
+ ReferenceType => '',
+ OldReference => '',
+ NewReference => '',
+ Data => '',
+
+ @_);
+ $self->SUPER::Create(
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+ TimeTaken => $args{'TimeTaken'},
+ Type => $args{'Type'},
+ Field => $args{'Field'},
+ OldValue => $args{'OldValue'},
+ NewValue => $args{'NewValue'},
+ ReferenceType => $args{'ReferenceType'},
+ OldReference => $args{'OldReference'},
+ NewReference => $args{'NewReference'},
+ Data => $args{'Data'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 ObjectType
+
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(64).)
+
+
+
+=head2 SetObjectType VALUE
+
+
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(64).)
+
+
+=cut
+
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=head2 SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 TimeTaken
+
+Returns the current value of TimeTaken.
+(In the database, TimeTaken is stored as int(11).)
+
+
+
+=head2 SetTimeTaken VALUE
+
+
+Set TimeTaken to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, TimeTaken will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Type
+
+Returns the current value of Type.
+(In the database, Type is stored as varchar(20).)
+
+
+
+=head2 SetType VALUE
+
+
+Set Type to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Type will be stored as a varchar(20).)
+
+
+=cut
+
+
+=head2 Field
+
+Returns the current value of Field.
+(In the database, Field is stored as varchar(40).)
+
+
+
+=head2 SetField VALUE
+
+
+Set Field to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Field will be stored as a varchar(40).)
+
+
+=cut
+
+
+=head2 OldValue
+
+Returns the current value of OldValue.
+(In the database, OldValue is stored as varchar(255).)
+
+
+
+=head2 SetOldValue VALUE
+
+
+Set OldValue to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, OldValue will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 NewValue
+
+Returns the current value of NewValue.
+(In the database, NewValue is stored as varchar(255).)
+
+
+
+=head2 SetNewValue VALUE
+
+
+Set NewValue to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, NewValue will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 ReferenceType
+
+Returns the current value of ReferenceType.
+(In the database, ReferenceType is stored as varchar(255).)
+
+
+
+=head2 SetReferenceType VALUE
+
+
+Set ReferenceType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ReferenceType will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 OldReference
+
+Returns the current value of OldReference.
+(In the database, OldReference is stored as int(11).)
+
+
+
+=head2 SetOldReference VALUE
+
+
+Set OldReference to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, OldReference will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 NewReference
+
+Returns the current value of NewReference.
+(In the database, NewReference is stored as int(11).)
+
+
+
+=head2 SetNewReference VALUE
+
+
+Set NewReference to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, NewReference will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Data
+
+Returns the current value of Data.
+(In the database, Data is stored as varchar(255).)
+
+
+
+=head2 SetData VALUE
+
+
+Set Data to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Data will be stored as a varchar(255).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ ObjectType =>
+ {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ ObjectId =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ TimeTaken =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Type =>
+ {read => 1, write => 1, sql_type => 12, length => 20, is_blob => 0, is_numeric => 0, type => 'varchar(20)', default => ''},
+ Field =>
+ {read => 1, write => 1, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+ OldValue =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ NewValue =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ ReferenceType =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ OldReference =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ NewReference =>
+ {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Data =>
+ {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::Transaction_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Transaction_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Transaction_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Transaction_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Transaction_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Transaction_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Transaction_Overlay, RT::Transaction_Vendor, RT::Transaction_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Transaction_Overlay.pm b/rt/lib/RT/Transaction_Overlay.pm
new file mode 100644
index 0000000..451f9d7
--- /dev/null
+++ b/rt/lib/RT/Transaction_Overlay.pm
@@ -0,0 +1,1134 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Transaction - RT\'s transaction object
+
+=head1 SYNOPSIS
+
+ use RT::Transaction;
+
+
+=head1 DESCRIPTION
+
+
+Each RT::Transaction describes an atomic change to a ticket object
+or an update to an RT::Ticket object.
+It can have arbitrary MIME attachments.
+
+
+=head1 METHODS
+
+=begin testing
+
+ok(require RT::Transaction);
+
+=end testing
+
+=cut
+
+
+package RT::Transaction;
+
+use strict;
+no warnings qw(redefine);
+
+use vars qw( %_BriefDescriptions $PreferredContentType );
+
+use RT::Attachments;
+use RT::Scrips;
+
+use HTML::FormatText;
+use HTML::TreeBuilder;
+
+
+# {{{ sub Create
+
+=head2 Create
+
+Create a new transaction.
+
+This routine should _never_ be called by anything other than RT::Ticket.
+It should not be called
+from client code. Ever. Not ever. If you do this, we will hunt you down and break your kneecaps.
+Then the unpleasant stuff will start.
+
+TODO: Document what gets passed to this
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ id => undef,
+ TimeTaken => 0,
+ Type => 'undefined',
+ Data => '',
+ Field => undef,
+ OldValue => undef,
+ NewValue => undef,
+ MIMEObj => undef,
+ ActivateScrips => 1,
+ CommitScrips => 1,
+ ObjectType => 'RT::Ticket',
+ ObjectId => 0,
+ ReferenceType => undef,
+ OldReference => undef,
+ NewReference => undef,
+ @_
+ );
+
+ $args{ObjectId} ||= $args{Ticket};
+
+ #if we didn't specify a ticket, we need to bail
+ unless ( $args{'ObjectId'} && $args{'ObjectType'}) {
+ return ( 0, $self->loc( "Transaction->Create couldn't, as you didn't specify an object type and id"));
+ }
+
+
+
+ #lets create our transaction
+ my %params = (
+ Type => $args{'Type'},
+ Data => $args{'Data'},
+ Field => $args{'Field'},
+ OldValue => $args{'OldValue'},
+ NewValue => $args{'NewValue'},
+ Created => $args{'Created'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+ ReferenceType => $args{'ReferenceType'},
+ OldReference => $args{'OldReference'},
+ NewReference => $args{'NewReference'},
+ );
+
+ # Parameters passed in during an import that we probably don't want to touch, otherwise
+ foreach my $attr qw(id Creator Created LastUpdated TimeTaken LastUpdatedBy) {
+ $params{$attr} = $args{$attr} if ($args{$attr});
+ }
+
+ my $id = $self->SUPER::Create(%params);
+ $self->Load($id);
+ if ( defined $args{'MIMEObj'} ) {
+ my ($id, $msg) = $self->_Attach( $args{'MIMEObj'} );
+ unless ( $id ) {
+ $RT::Logger->error("Couldn't add attachment: $msg");
+ return ( 0, $self->loc("Couldn't add attachment") );
+ }
+ }
+
+
+ #Provide a way to turn off scrips if we need to
+ $RT::Logger->debug('About to think about scrips for transaction #' .$self->Id);
+ if ( $args{'ActivateScrips'} and $args{'ObjectType'} eq 'RT::Ticket' ) {
+ $self->{'scrips'} = RT::Scrips->new($RT::SystemUser);
+
+ $RT::Logger->debug('About to prepare scrips for transaction #' .$self->Id);
+
+ $self->{'scrips'}->Prepare(
+ Stage => 'TransactionCreate',
+ Type => $args{'Type'},
+ Ticket => $args{'ObjectId'},
+ Transaction => $self->id,
+ );
+ if ($args{'CommitScrips'} ) {
+ $RT::Logger->debug('About to commit scrips for transaction #' .$self->Id);
+ $self->{'scrips'}->Commit();
+ }
+ }
+
+ return ( $id, $self->loc("Transaction Created") );
+}
+
+# }}}
+
+=head2 Scrips
+
+Returns the Scrips object for this transaction.
+This routine is only useful on a freshly created transaction object.
+Scrips do not get persisted to the database with transactions.
+
+
+=cut
+
+
+sub Scrips {
+ my $self = shift;
+ return($self->{'scrips'});
+}
+
+
+# {{{ sub Delete
+
+=head2 Delete
+
+Delete this transaction. Currently DOES NOT CHECK ACLS
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+
+ $RT::Handle->BeginTransaction();
+
+ my $attachments = $self->Attachments;
+
+ while (my $attachment = $attachments->Next) {
+ my ($id, $msg) = $attachment->Delete();
+ unless ($id) {
+ $RT::Handle->Rollback();
+ return($id, $self->loc("System Error: [_1]", $msg));
+ }
+ }
+ my ($id,$msg) = $self->SUPER::Delete();
+ unless ($id) {
+ $RT::Handle->Rollback();
+ return($id, $self->loc("System Error: [_1]", $msg));
+ }
+ $RT::Handle->Commit();
+ return ($id,$msg);
+}
+
+# }}}
+
+# {{{ Routines dealing with Attachments
+
+# {{{ sub Message
+
+=head2 Message
+
+ Returns the RT::Attachments Object which contains the "top-level"object
+ attachment for this transaction
+
+=cut
+
+sub Message {
+
+ my $self = shift;
+
+ if ( !defined( $self->{'message'} ) ) {
+
+ $self->{'message'} = new RT::Attachments( $self->CurrentUser );
+ $self->{'message'}->Limit(
+ FIELD => 'TransactionId',
+ VALUE => $self->Id
+ );
+
+ $self->{'message'}->ChildrenOf(0);
+ }
+ return ( $self->{'message'} );
+}
+
+# }}}
+
+# {{{ sub Content
+
+=head2 Content PARAMHASH
+
+If this transaction has attached mime objects, returns the body of the first
+textual part (as defined in RT::I18N::IsTextualContentType). Otherwise,
+returns undef.
+
+Takes a paramhash. If the $args{'Quote'} parameter is set, wraps this message
+at $args{'Wrap'}. $args{'Wrap'} defaults to 70.
+
+If $args{'Type'} is set to C<text/html>, plain texts are upgraded to HTML.
+Otherwise, HTML texts are downgraded to plain text. If $args{'Type'} is
+missing, it defaults to the value of C<$RT::Transaction::PreferredContentType>.
+
+=cut
+
+sub Content {
+ my $self = shift;
+ my %args = (
+ Type => $PreferredContentType,
+ Quote => 0,
+ Wrap => 70,
+ @_
+ );
+
+ my $content;
+ if (my $content_obj = $self->ContentObj) {
+ $content = $content_obj->Content;
+
+ if ($content_obj->ContentType =~ m{^text/html$}i) {
+ $content =~ s/<p>--\s+<br \/>.*?$//s if $args{'Quote'};
+
+ if ($args{Type} ne 'text/html') {
+ $content = HTML::FormatText->new(
+ leftmargin => 0,
+ rightmargin => 78,
+ )->format(
+ HTML::TreeBuilder->new_from_content( $content )
+ );
+ }
+ }
+ else {
+ $content =~ s/\n-- \n.*?$//s if $args{'Quote'};
+
+ if ($args{Type} eq 'text/html') {
+ # Extremely simple text->html converter
+ $content =~ s/&/&#38;/g;
+ $content =~ s/</&lt;/g;
+ $content =~ s/>/&gt;/g;
+ $content = "<pre>$content</pre>";
+ }
+ }
+ }
+
+ # If all else fails, return a message that we couldn't find any content
+ else {
+ $content = $self->loc('This transaction appears to have no content');
+ }
+
+ if ( $args{'Quote'} ) {
+
+ # What's the longest line like?
+ my $max = 0;
+ foreach ( split ( /\n/, $content ) ) {
+ $max = length if ( length > $max );
+ }
+
+ if ( $max > 76 ) {
+ require Text::Wrapper;
+ my $wrapper = new Text::Wrapper(
+ columns => $args{'Wrap'},
+ body_start => ( $max > 70 * 3 ? ' ' : '' ),
+ par_start => ''
+ );
+ $content = $wrapper->wrap($content);
+ }
+
+ $content =~ s/^/> /gm;
+ $content = $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString(), $self->CreatorObj->Name())
+ . "\n$content\n\n";
+ }
+
+ return ($content);
+}
+
+# }}}
+
+# {{{ ContentObj
+
+=head2 ContentObj
+
+Returns the RT::Attachment object which contains the content for this Transaction
+
+=cut
+
+
+sub ContentObj {
+
+ my $self = shift;
+
+ # If we don\'t have any content, return undef now.
+ unless ( $self->Attachments->First ) {
+ return (undef);
+ }
+
+ # Get the set of toplevel attachments to this transaction.
+ my $Attachment = $self->Attachments->First();
+
+ # If it's a textual part, just return the body.
+ if ( RT::I18N::IsTextualContentType($Attachment->ContentType) ) {
+ return ($Attachment);
+ }
+
+ # If it's a multipart object, first try returning the first part with preferred
+ # MIME type ('text/plain' by default).
+
+ elsif ( $Attachment->ContentType() =~ '^multipart/' ) {
+ my $plain_parts = $Attachment->Children();
+ $plain_parts->ContentType( VALUE => ($PreferredContentType || 'text/plain') );
+
+ # If we actully found a part, return its content
+ if ( $plain_parts->First && $plain_parts->First->Content ne '' ) {
+ return ( $plain_parts->First );
+ }
+
+
+ # If that fails, return the first textual part which has some content.
+
+ else {
+ my $all_parts = $self->Attachments();
+ while ( my $part = $all_parts->Next ) {
+ if ( ( RT::I18N::IsTextualContentType($part->ContentType) ) and ( $part->Content() ne '' ) ) {
+ return ($part);
+ }
+ }
+ }
+
+ }
+
+ # We found no content. suck
+ return (undef);
+}
+
+# }}}
+
+# {{{ sub Subject
+
+=head2 Subject
+
+If this transaction has attached mime objects, returns the first one's subject
+Otherwise, returns null
+
+=cut
+
+sub Subject {
+ my $self = shift;
+ if ( $self->Attachments->First ) {
+ return ( $self->Attachments->First->Subject );
+ }
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
+# {{{ sub Attachments
+
+=head2 Attachments
+
+ Returns all the RT::Attachment objects which are attached
+to this transaction. Takes an optional parameter, which is
+a ContentType that Attachments should be restricted to.
+
+=cut
+
+sub Attachments {
+ my $self = shift;
+
+ unless ( $self->{'attachments'} ) {
+ $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
+
+ #If it's a comment, return an empty object if they don't have the right to see it
+ if ( $self->Type eq 'Comment' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->{'attachments'} );
+ }
+ }
+
+ #if they ain't got rights to see, return an empty object
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ( $self->{'attachments'} );
+ }
+ }
+
+ $self->{'attachments'}->Limit( FIELD => 'TransactionId',
+ VALUE => $self->Id );
+
+ # Get the self->{'attachments'} in the order they're put into
+ # the database. Arguably, we should be returning a tree
+ # of self->{'attachments'}, not a set...but no current app seems to need
+ # it.
+
+ $self->{'attachments'}->OrderBy( ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'asc' );
+
+ }
+ return ( $self->{'attachments'} );
+
+}
+
+# }}}
+
+# {{{ sub _Attach
+
+=head2 _Attach
+
+A private method used to attach a mime object to this transaction.
+
+=cut
+
+sub _Attach {
+ my $self = shift;
+ my $MIMEObject = shift;
+
+ if ( !defined($MIMEObject) ) {
+ $RT::Logger->error(
+"$self _Attach: We can't attach a mime object if you don't give us one.\n"
+ );
+ return ( 0, $self->loc("[_1]: no attachment specified", $self) );
+ }
+
+ my $Attachment = new RT::Attachment( $self->CurrentUser );
+ my ($id, $msg) = $Attachment->Create(
+ TransactionId => $self->Id,
+ Attachment => $MIMEObject
+ );
+ return ( $Attachment, $msg || $self->loc("Attachment created") );
+
+}
+
+# }}}
+
+# }}}
+
+# {{{ Routines dealing with Transaction Attributes
+
+# {{{ sub Description
+
+=head2 Description
+
+Returns a text string which describes this transaction
+
+=cut
+
+sub Description {
+ my $self = shift;
+
+ #Check those ACLs
+ #If it's a comment or a comment email record,
+ # we need to be extra special careful
+
+ if ( $self->__Value('Type') =~ /^Comment/ ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->loc("Permission Denied") );
+ }
+ }
+
+ #if they ain't got rights to see, don't let em
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ($self->loc("Permission Denied") );
+ }
+ }
+
+ if ( !defined( $self->Type ) ) {
+ return ( $self->loc("No transaction type specified"));
+ }
+
+ return ( $self->loc("[_1] by [_2]",$self->BriefDescription , $self->CreatorObj->Name ));
+}
+
+# }}}
+
+# {{{ sub BriefDescription
+
+=head2 BriefDescription
+
+Returns a text string which briefly describes this transaction
+
+=cut
+
+sub BriefDescription {
+ my $self = shift;
+
+ #If it's a comment or a comment email record,
+ # we need to be extra special careful
+ if ( $self->__Value('Type') =~ /^Comment/ ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->loc("Permission Denied") );
+ }
+ }
+
+ #if they ain't got rights to see, don't let em
+ elsif ( $self->__Value('ObjectType') eq "RT::Ticket" ) {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ( $self->loc("Permission Denied") );
+ }
+ }
+
+ my $type = $self->Type; #cache this, rather than calling it 30 times
+
+ if ( !defined($type) ) {
+ return $self->loc("No transaction type specified");
+ }
+
+ my $obj_type = $self->FriendlyObjectType;
+
+ if ( $type eq 'Create' ) {
+ return ( $self->loc( "[_1] created", $obj_type ) );
+ }
+ elsif ( $type =~ /Status/ ) {
+ if ( $self->Field eq 'Status' ) {
+ if ( $self->NewValue eq 'deleted' ) {
+ return ( $self->loc( "[_1] deleted", $obj_type ) );
+ }
+ else {
+ return (
+ $self->loc(
+ "Status changed from [_1] to [_2]",
+ "'" . $self->loc( $self->OldValue ) . "'",
+ "'" . $self->loc( $self->NewValue ) . "'"
+ )
+ );
+
+ }
+ }
+
+ # Generic:
+ my $no_value = $self->loc("(no value)");
+ return (
+ $self->loc(
+ "[_1] changed from [_2] to [_3]",
+ $self->Field,
+ ( $self->OldValue ? "'" . $self->OldValue . "'" : $no_value ),
+ "'" . $self->NewValue . "'"
+ )
+ );
+ }
+
+ if ( my $code = $_BriefDescriptions{$type} ) {
+ return $code->($self);
+ }
+
+ return $self->loc(
+ "Default: [_1]/[_2] changed from [_3] to [_4]",
+ $type,
+ $self->Field,
+ (
+ $self->OldValue
+ ? "'" . $self->OldValue . "'"
+ : $self->loc("(no value)")
+ ),
+ "'" . $self->NewValue . "'"
+ );
+}
+
+%_BriefDescriptions = (
+ CommentEmailRecord => sub {
+ my $self = shift;
+ return $self->loc("Outgoing email about a comment recorded");
+ },
+ EmailRecord => sub {
+ my $self = shift;
+ return $self->loc("Outgoing email recorded");
+ },
+ Correspond => sub {
+ my $self = shift;
+ return $self->loc("Correspondence added");
+ },
+ Comment => sub {
+ my $self = shift;
+ return $self->loc("Comments added");
+ },
+ CustomField => sub {
+ my $self = shift;
+ my $field = $self->loc('CustomField');
+
+ if ( $self->Field ) {
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->Load( $self->Field );
+ $field = $cf->Name();
+ }
+
+ if ( $self->OldValue eq '' ) {
+ return ( $self->loc("[_1] [_2] added", $field, $self->NewValue) );
+ }
+ elsif ( $self->NewValue eq '' ) {
+ return ( $self->loc("[_1] [_2] deleted", $field, $self->OldValue) );
+
+ }
+ else {
+ return $self->loc("[_1] [_2] changed to [_3]", $field, $self->OldValue, $self->NewValue );
+ }
+ },
+ Untake => sub {
+ my $self = shift;
+ return $self->loc("Untaken");
+ },
+ Take => sub {
+ my $self = shift;
+ return $self->loc("Taken");
+ },
+ Force => sub {
+ my $self = shift;
+ my $Old = RT::User->new( $self->CurrentUser );
+ $Old->Load( $self->OldValue );
+ my $New = RT::User->new( $self->CurrentUser );
+ $New->Load( $self->NewValue );
+
+ return $self->loc("Owner forcibly changed from [_1] to [_2]" , $Old->Name , $New->Name);
+ },
+ Steal => sub {
+ my $self = shift;
+ my $Old = RT::User->new( $self->CurrentUser );
+ $Old->Load( $self->OldValue );
+ return $self->loc("Stolen from [_1]", $Old->Name);
+ },
+ Give => sub {
+ my $self = shift;
+ my $New = RT::User->new( $self->CurrentUser );
+ $New->Load( $self->NewValue );
+ return $self->loc( "Given to [_1]", $New->Name );
+ },
+ AddWatcher => sub {
+ my $self = shift;
+ my $principal = RT::Principal->new($self->CurrentUser);
+ $principal->Load($self->NewValue);
+ return $self->loc( "[_1] [_2] added", $self->Field, $principal->Object->Name);
+ },
+ DelWatcher => sub {
+ my $self = shift;
+ my $principal = RT::Principal->new($self->CurrentUser);
+ $principal->Load($self->OldValue);
+ return $self->loc( "[_1] [_2] deleted", $self->Field, $principal->Object->Name);
+ },
+ Subject => sub {
+ my $self = shift;
+ return $self->loc( "Subject changed to [_1]", $self->Data );
+ },
+ AddLink => sub {
+ my $self = shift;
+ my $value;
+ if ( $self->NewValue ) {
+ my $URI = RT::URI->new( $self->CurrentUser );
+ $URI->FromURI( $self->NewValue );
+ if ( $URI->Resolver ) {
+ $value = $URI->Resolver->AsString;
+ }
+ else {
+ $value = $self->NewValue;
+ }
+ if ( $self->Field eq 'DependsOn' ) {
+ return $self->loc( "Dependency on [_1] added", $value );
+ }
+ elsif ( $self->Field eq 'DependedOnBy' ) {
+ return $self->loc( "Dependency by [_1] added", $value );
+
+ }
+ elsif ( $self->Field eq 'RefersTo' ) {
+ return $self->loc( "Reference to [_1] added", $value );
+ }
+ elsif ( $self->Field eq 'ReferredToBy' ) {
+ return $self->loc( "Reference by [_1] added", $value );
+ }
+ elsif ( $self->Field eq 'MemberOf' ) {
+ return $self->loc( "Membership in [_1] added", $value );
+ }
+ elsif ( $self->Field eq 'HasMember' ) {
+ return $self->loc( "Member [_1] added", $value );
+ }
+ elsif ( $self->Field eq 'MergedInto' ) {
+ return $self->loc( "Merged into [_1]", $value );
+ }
+ }
+ else {
+ return ( $self->Data );
+ }
+ },
+ DeleteLink => sub {
+ my $self = shift;
+ my $value;
+ if ( $self->OldValue ) {
+ my $URI = RT::URI->new( $self->CurrentUser );
+ $URI->FromURI( $self->OldValue );
+ if ( $URI->Resolver ) {
+ $value = $URI->Resolver->AsString;
+ }
+ else {
+ $value = $self->OldValue;
+ }
+
+ if ( $self->Field eq 'DependsOn' ) {
+ return $self->loc( "Dependency on [_1] deleted", $value );
+ }
+ elsif ( $self->Field eq 'DependedOnBy' ) {
+ return $self->loc( "Dependency by [_1] deleted", $value );
+
+ }
+ elsif ( $self->Field eq 'RefersTo' ) {
+ return $self->loc( "Reference to [_1] deleted", $value );
+ }
+ elsif ( $self->Field eq 'ReferredToBy' ) {
+ return $self->loc( "Reference by [_1] deleted", $value );
+ }
+ elsif ( $self->Field eq 'MemberOf' ) {
+ return $self->loc( "Membership in [_1] deleted", $value );
+ }
+ elsif ( $self->Field eq 'HasMember' ) {
+ return $self->loc( "Member [_1] deleted", $value );
+ }
+ }
+ else {
+ return ( $self->Data );
+ }
+ },
+ Set => sub {
+ my $self = shift;
+ if ( $self->Field eq 'Password' ) {
+ return $self->loc('Password changed');
+ }
+ elsif ( $self->Field eq 'Queue' ) {
+ my $q1 = new RT::Queue( $self->CurrentUser );
+ $q1->Load( $self->OldValue );
+ my $q2 = new RT::Queue( $self->CurrentUser );
+ $q2->Load( $self->NewValue );
+ return $self->loc("[_1] changed from [_2] to [_3]", $self->Field , $q1->Name , $q2->Name);
+ }
+
+ # Write the date/time change at local time:
+ elsif ($self->Field =~ /Due|Starts|Started|Told/) {
+ my $t1 = new RT::Date($self->CurrentUser);
+ $t1->Set(Format => 'ISO', Value => $self->NewValue);
+ my $t2 = new RT::Date($self->CurrentUser);
+ $t2->Set(Format => 'ISO', Value => $self->OldValue);
+ return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $t2->AsString, $t1->AsString );
+ }
+ else {
+ return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
+ }
+ },
+ PurgeTransaction => sub {
+ my $self = shift;
+ return $self->loc("Transaction [_1] purged", $self->Data);
+ },
+ AddReminder => sub {
+ my $self = shift;
+ my $ticket = RT::Ticket->new($self->CurrentUser);
+ $ticket->Load($self->NewValue);
+ return $self->loc("Reminder '[_1]' added", $ticket->Subject);
+ },
+ OpenReminder => sub {
+ my $self = shift;
+ my $ticket = RT::Ticket->new($self->CurrentUser);
+ $ticket->Load($self->NewValue);
+ return $self->loc("Reminder '[_1]' reopened", $ticket->Subject);
+
+ },
+ ResolveReminder => sub {
+ my $self = shift;
+ my $ticket = RT::Ticket->new($self->CurrentUser);
+ $ticket->Load($self->NewValue);
+ return $self->loc("Reminder '[_1]' completed", $ticket->Subject);
+
+
+ }
+);
+
+# }}}
+
+# {{{ Utility methods
+
+# {{{ sub IsInbound
+
+=head2 IsInbound
+
+Returns true if the creator of the transaction is a requestor of the ticket.
+Returns false otherwise
+
+=cut
+
+sub IsInbound {
+ my $self = shift;
+ $self->ObjectType eq 'RT::Ticket' or return undef;
+ return ( $self->TicketObj->IsRequestor( $self->CreatorObj->PrincipalId ) );
+}
+
+# }}}
+
+# }}}
+
+sub _OverlayAccessible {
+ {
+
+ ObjectType => { public => 1},
+ ObjectId => { public => 1},
+
+ }
+};
+
+# }}}
+
+# }}}
+
+# {{{ sub _Set
+
+sub _Set {
+ my $self = shift;
+ return ( 0, $self->loc('Transactions are immutable') );
+}
+
+# }}}
+
+# {{{ sub _Value
+
+=head2 _Value
+
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
+
+=cut
+
+sub _Value {
+
+ my $self = shift;
+ my $field = shift;
+
+ #if the field is public, return it.
+ if ( $self->_Accessible( $field, 'public' ) ) {
+ return ( $self->__Value($field) );
+
+ }
+
+ #If it's a comment, we need to be extra special careful
+ if ( $self->__Value('Type') eq 'Comment' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return (undef);
+ }
+ }
+ elsif ( $self->__Value('Type') eq 'CommentEmailRecord' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments')
+ && $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
+ return (undef);
+ }
+
+ }
+ elsif ( $self->__Value('Type') eq 'EmailRecord' ) {
+ unless ( $self->CurrentUserHasRight('ShowOutgoingEmail')) {
+ return (undef);
+ }
+
+ }
+ # Make sure the user can see the custom field before showing that it changed
+ elsif ( ( $self->__Value('Type') eq 'CustomField' ) && $self->__Value('Field') ) {
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->Load( $self->__Value('Field') );
+ return (undef) unless ( $cf->CurrentUserHasRight('SeeCustomField') );
+ }
+
+
+ #if they ain't got rights to see, don't let em
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return (undef);
+ }
+ }
+
+ return ( $self->__Value($field) );
+
+}
+
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
+=head2 CurrentUserHasRight RIGHT
+
+Calls $self->CurrentUser->HasQueueRight for the right passed in here.
+passed in here.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+ return (
+ $self->CurrentUser->HasRight(
+ Right => "$right",
+ Object => $self->TicketObj
+ )
+ );
+}
+
+# }}}
+
+sub Ticket {
+ my $self = shift;
+ return $self->ObjectId;
+}
+
+sub TicketObj {
+ my $self = shift;
+ return $self->Object;
+}
+
+sub OldValue {
+ my $self = shift;
+ if ( my $type = $self->__Value('ReferenceType')
+ and my $id = $self->__Value('OldReference') )
+ {
+ my $Object = $type->new($self->CurrentUser);
+ $Object->Load( $id );
+ return $Object->Content;
+ }
+ else {
+ return $self->__Value('OldValue');
+ }
+}
+
+sub NewValue {
+ my $self = shift;
+ if ( my $type = $self->__Value('ReferenceType')
+ and my $id = $self->__Value('NewReference') )
+ {
+ my $Object = $type->new($self->CurrentUser);
+ $Object->Load( $id );
+ return $Object->Content;
+ }
+ else {
+ return $self->__Value('NewValue');
+ }
+}
+
+sub Object {
+ my $self = shift;
+ my $Object = $self->__Value('ObjectType')->new($self->CurrentUser);
+ $Object->Load($self->__Value('ObjectId'));
+ return($Object);
+}
+
+sub FriendlyObjectType {
+ my $self = shift;
+ my $type = $self->ObjectType or return undef;
+ $type =~ s/^RT:://;
+ return $self->loc($type);
+}
+
+=head2 UpdateCustomFields
+
+ Takes a hash of
+
+ CustomField-<<Id>> => Value
+ or
+
+ Object-RT::Transaction-CustomField-<<Id>> => Value parameters to update
+ this transaction's custom fields
+
+=cut
+
+sub UpdateCustomFields {
+ my $self = shift;
+ my %args = (@_);
+
+ # This method used to have an API that took a hash of a single
+ # value "ARGSRef", which was a reference to a hash of arguments.
+ # This was insane. The next few lines of code preserve that API
+ # while giving us something saner.
+
+
+ # TODO: 3.6: DEPRECATE OLD API
+
+ my $args;
+
+ if ($args{'ARGSRef'}) {
+ $args = $args{ARGSRef};
+ } else {
+ $args = \%args;
+ }
+
+ foreach my $arg ( keys %$args ) {
+ next
+ unless ( $arg =~
+ /^(?:Object-RT::Transaction--)?CustomField-(\d+)/ );
+ next if $arg =~ /-Magic$/;
+ my $cfid = $1;
+ my $values = $args->{$arg};
+ foreach
+ my $value ( UNIVERSAL::isa( $values, 'ARRAY' ) ? @$values : $values )
+ {
+ next unless length($value);
+ $self->_AddCustomFieldValue(
+ Field => $cfid,
+ Value => $value,
+ RecordTransaction => 0,
+ );
+ }
+ }
+}
+
+
+
+=head2 CustomFieldValues
+
+ Do name => id mapping (if needed) before falling back to RT::Record's CustomFieldValues
+
+ See L<RT::Record>
+
+=cut
+
+sub CustomFieldValues {
+ my $self = shift;
+ my $field = shift;
+
+ if ( UNIVERSAL::can( $self->Object, 'QueueObj' ) ) {
+
+ unless ( defined $field && $field =~ /^\d+$/o ) {
+ my $CFs = RT::CustomFields->new( $self->CurrentUser );
+ $CFs->Limit( FIELD => 'Name', VALUE => $field);
+ $CFs->LimitToLookupType($self->CustomFieldLookupType);
+ $CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id);
+ $field = $CFs->First->id if $CFs->First;
+ }
+ }
+ return $self->SUPER::CustomFieldValues($field);
+}
+
+# }}}
+
+# {{{ sub CustomFieldLookupType
+
+=head2 CustomFieldLookupType
+
+Returns the RT::Transaction lookup type, which can
+be passed to RT::CustomField->Create() via the 'LookupType' hash key.
+
+=cut
+
+# }}}
+
+sub CustomFieldLookupType {
+ "RT::Queue-RT::Ticket-RT::Transaction";
+}
+
+# Transactions don't change. by adding this cache congif directiove, we don't lose pathalogically on long tickets.
+sub _CacheConfig {
+ {
+ 'cache_p' => 1,
+ 'fast_update_p' => 1,
+ 'cache_for_sec' => 6000,
+ }
+}
+1;
diff --git a/rt/lib/RT/Transactions.pm b/rt/lib/RT/Transactions.pm
new file mode 100755
index 0000000..86f8a2d
--- /dev/null
+++ b/rt/lib/RT/Transactions.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Transactions -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Transactions
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Transactions;
+
+use RT::SearchBuilder;
+use RT::Transaction;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Transactions';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::Transaction item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::Transaction->new($self->CurrentUser));
+}
+
+ eval "require RT::Transactions_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Transactions_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Transactions_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Transactions_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Transactions_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Transactions_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Transactions_Overlay, RT::Transactions_Vendor, RT::Transactions_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Transactions_Overlay.pm b/rt/lib/RT/Transactions_Overlay.pm
new file mode 100644
index 0000000..9bf77af
--- /dev/null
+++ b/rt/lib/RT/Transactions_Overlay.pm
@@ -0,0 +1,187 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Transactions - a collection of RT Transaction objects
+
+=head1 SYNOPSIS
+
+ use RT::Transactions;
+
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok (require RT::Transactions);
+
+=end testing
+
+=cut
+
+
+package RT::Transactions;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+
+ $self->{'table'} = "Transactions";
+ $self->{'primary_key'} = "id";
+
+ # By default, order by the date of the transaction, rather than ID.
+ $self->OrderByCols( { FIELD => 'Created',
+ ORDER => 'ASC' },
+ { FIELD => 'id',
+ ORDER => 'ASC' } );
+
+ return ( $self->SUPER::_Init(@_));
+}
+# }}}
+
+=head2 Limit
+
+A wrapper around SUPER::Limit to catch migration issues
+
+=cut
+
+sub Limit {
+ my $self = shift;
+ my %args = (@_);
+
+ if ($args{'FIELD'} eq 'Ticket') {
+ Carp::cluck("Historical code calling RT::Transactions::Limit with a 'Ticket' at (". join(":",caller)."). This deprecated API will be deleted in 3.6");
+ $self->SUPER::Limit(FIELD => 'ObjectType', OPERATOR => '=', VALUE =>'RT::Ticket');
+ $args{'FIELD'} = 'ObjectId';
+ $self->SUPER::Limit(%args);
+
+ } else {
+
+ $self->SUPER::Limit(%args);
+ }
+
+
+}
+
+
+
+=head2 LimitToTicket TICKETID
+
+Find only transactions for the ticket whose id is TICKETID.
+
+This includes tickets merged into TICKETID.
+
+Repeated calls to this method will intelligently limit down to that set of tickets, joined with an OR
+
+
+=cut
+
+
+sub LimitToTicket {
+ my $self = shift;
+ my $tid = shift;
+
+ unless ( $self->{'tickets_table'} ) {
+ $self->{'tickets_table'} ||= $self->NewAlias('Tickets');
+ $self->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'ObjectId',
+ ALIAS2 => $self->{'tickets_table'},
+ FIELD2 => 'id'
+ );
+ $self->Limit(
+ FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket',
+ );
+ }
+ $self->Limit(
+ ALIAS => $self->{tickets_table},
+ FIELD => 'EffectiveId',
+ OPERATOR => '=',
+ ENTRYAGGREGATOR => 'OR',
+ VALUE => $tid,
+ );
+
+}
+
+
+# {{{ sub Next
+sub Next {
+ my $self = shift;
+
+ my $Transaction = $self->SUPER::Next();
+ if ((defined($Transaction)) and (ref($Transaction))) {
+ # If the user can see the transaction's type, then they can
+ # see the transaction and we should hand it back.
+ if ($Transaction->Type) {
+ return($Transaction);
+ }
+
+ #If the user doesn't have the right to show this ticket
+ else {
+ return($self->Next());
+ }
+ }
+
+ #if there never was any ticket
+ else {
+ return(undef);
+ }
+}
+# }}}
+
+
+
+1;
+
diff --git a/rt/lib/RT/URI.pm b/rt/lib/RT/URI.pm
new file mode 100644
index 0000000..c6abf93
--- /dev/null
+++ b/rt/lib/RT/URI.pm
@@ -0,0 +1,283 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::URI;;
+
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Base);
+
+use RT::URI::base;
+use Carp;
+
+=head1 NAME
+
+RT::URI
+
+=head1 DESCRIPTION
+
+This class provides a base class for URIs, such as those handled
+by RT::Link objects.
+
+=head1 API
+
+
+
+=cut
+
+
+
+
+=head2 new
+
+Create a new RT::URI object.
+
+=cut
+
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless( $self, $class );
+
+ $self->CurrentUser(@_);
+
+ return ($self);
+}
+
+
+
+# {{{ FromObject
+
+=head2 FromObject <Object>
+
+Given a local object, such as an RT::Ticket or an RT::FM::Article, this routine will return a URI for
+the local object
+
+=cut
+
+sub FromObject {
+ my $self = shift;
+ my $obj = shift;
+
+ return undef unless $obj->can('URI');
+ return $self->FromURI($obj->URI);
+}
+
+# }}}
+
+# {{{ FromURI
+
+=head2 FromURI <URI>
+
+Returns a local object id for this content. You are expected to know
+what sort of object this is the Id of
+
+Returns true if everything is ok, otherwise false
+
+=cut
+
+sub FromURI {
+ my $self = shift;
+ my $uri = shift;
+
+ return undef unless ($uri);
+
+ my $scheme;
+ # Special case: integers passed in as URIs must be ticket ids
+ if ($uri =~ /^(\d+)$/) {
+ $scheme = "fsck.com-rt";
+ } elsif ($uri =~ /^((?:\w|\.|-)+?):/) {
+ $scheme = $1;
+ }
+ else {
+ $RT::Logger->warning("$self Could not determine a URI scheme for $uri");
+ return (undef);
+ }
+
+ # load up a resolver object for this scheme
+ $self->_GetResolver($scheme);
+
+ unless ($self->Resolver->ParseURI($uri)) {
+ $RT::Logger->warning("Resolver ".ref($self->Resolver)." could not parse $uri");
+ $self->{resolver} = RT::URI::base->new( $self->CurrentUser ); # clear resolver
+ return (undef);
+ }
+
+return(1);
+
+}
+
+# }}}
+
+# {{{ _GetResolver
+
+=private _GetResolver <scheme>
+
+Gets an RT URI resolver for the scheme <scheme>.
+Falls back to a null resolver. RT::URI::base.
+
+=cut
+
+sub _GetResolver {
+ my $self = shift;
+ my $scheme = shift;
+
+ $scheme =~ s/(\.|-)/_/g;
+ my $resolver;
+
+
+ eval "
+ require RT::URI::$scheme;
+ \$resolver = RT::URI::$scheme->new(\$self->CurrentUser);
+ ";
+
+ if ($resolver) {
+ $self->{'resolver'} = $resolver;
+ } else {
+ $self->{'resolver'} = RT::URI::base->new($self->CurrentUser);
+ }
+
+}
+
+# }}}
+
+# {{{ Scheme
+
+=head2 Scheme
+
+Returns a local object id for this content. You are expected to know what sort of object this is the Id
+of
+
+=cut
+
+sub Scheme {
+ my $self = shift;
+ return ($self->Resolver->Scheme);
+
+}
+# }}}
+# {{{ URI
+
+=head2 URI
+
+Returns a local object id for this content. You are expected to know what sort of object this is the Id
+of
+
+=cut
+
+sub URI {
+ my $self = shift;
+ return ($self->Resolver->URI);
+
+}
+# }}}
+
+# {{{ Object
+
+=head2 Object
+
+Returns a local object for this content. This will usually be an RT::Ticket or somesuch
+
+=cut
+
+
+sub Object {
+ my $self = shift;
+ return($self->Resolver->Object);
+
+}
+
+
+# }}}
+
+# {{{ IsLocal
+
+=head2 IsLocal
+
+Returns a local object for this content. This will usually be an RT::Ticket or somesuch
+
+=cut
+
+sub IsLocal {
+ my $self = shift;
+ return $self->Resolver->IsLocal;
+}
+
+
+# }}}
+
+=head2 AsHREF
+
+
+=cut
+
+
+sub AsHREF {
+ my $self = shift;
+ return $self->Resolver->HREF;
+
+}
+=head Resolver
+
+Returns this URI's URI resolver object
+
+=cut
+
+
+sub Resolver {
+ my $self =shift;
+ return ($self->{'resolver'});
+}
+
+eval "require RT::URI_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI_Vendor.pm});
+eval "require RT::URI_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI_Local.pm});
+
+1;
diff --git a/rt/lib/RT/URI/base.pm b/rt/lib/RT/URI/base.pm
new file mode 100644
index 0000000..1b85060
--- /dev/null
+++ b/rt/lib/RT/URI/base.pm
@@ -0,0 +1,149 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::URI::base;
+
+use strict;
+use base qw(RT::Base);
+
+=head1 NAME
+
+RT::URI::base
+
+=head1 DESCRIPTION
+
+A baseclass (and fallback) RT::URI handler. Every URI handler needs to
+handle the API presented here
+
+=cut
+
+
+=head1 API
+
+=head2 new
+
+Create a new URI handler
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless( $self, $class );
+ $self->CurrentUser(@_);
+ return ($self);
+}
+
+sub ParseObject {
+ my $self = shift;
+ my $obj = shift;
+ $self->{'uri'} = "unknown-object:".ref($obj);
+}
+
+sub ParseURI {
+ my $self = shift;
+ my $uri = shift;
+
+ if ($uri =~ /^(.*?):/) {
+ $self->{'scheme'} = $1;
+ }
+ $self->{'uri'} = $uri;
+
+
+}
+
+
+sub Object {
+ my $self = shift;
+ return undef;
+
+}
+
+sub URI {
+ my $self = shift;
+ return($self->{'uri'});
+}
+
+sub Scheme {
+ my $self = shift;
+ return($self->{'scheme'});
+
+}
+
+sub HREF {
+ my $self = shift;
+ return($self->{'href'} || $self->{'uri'});
+}
+
+sub IsLocal {
+ my $self = shift;
+ return undef;
+}
+
+=head2 AsString
+
+Return a "pretty" string representing the URI object.
+
+This is meant to be used like this:
+
+ % $re = $uri->Resolver;
+ <A HREF="<% $re->HREF %>"><% $re->AsString %></A>
+
+=cut
+
+sub AsString {
+ my $self = shift;
+ return $self->URI;
+}
+
+eval "require RT::URI::base_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI/base_Vendor.pm});
+eval "require RT::URI::base_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI/base_Local.pm});
+
+1;
diff --git a/rt/lib/RT/URI/freeside.pm b/rt/lib/RT/URI/freeside.pm
new file mode 100644
index 0000000..d73dbac
--- /dev/null
+++ b/rt/lib/RT/URI/freeside.pm
@@ -0,0 +1,285 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+package RT::URI::freeside;
+
+use RT::URI::base;
+use strict;
+use vars qw(@ISA $IntegrationType $URL);
+@ISA = qw/RT::URI::base/;
+
+
+=head1 NAME
+
+RT::URI::freeside
+
+=head1 DESCRIPTION
+
+URI handler for Freeside URIs. See http://www.freeside.biz/ for more
+information on Freeside.
+
+
+=head1 Public subroutines
+
+=over 4
+
+=item FreesideGetConfig CONFKEY
+
+Subroutine that returns the freeside's configuration value(s) for CONFKEY
+as a scalar or list.
+
+=cut
+
+sub FreesideGetConfig { return undef; }
+
+
+=item FreesideURL
+
+Returns the URL for freeside's web interface.
+
+=cut
+
+sub FreesideURL { return $URL; }
+
+
+=item FreesideVersion
+
+Returns a string describing the freeside version being used.
+
+=cut
+
+sub FreesideVersion { return undef; }
+
+
+=item smart_search
+
+A wrapper for the FS::cust_main::smart_search subroutine.
+
+=cut
+
+sub smart_search { return undef; }
+
+
+=item small_custview
+
+A wrapper for the FS::CGI::small_custview subroutine.
+
+=cut
+
+sub small_custview { return 'Freeside integration error!</A>'; }
+
+
+=back
+
+=head1 Private methods
+
+=over 4
+
+=item _FreesideGetRecord
+
+Method returns a hashref of the freeside record referenced in the URI.
+Must be called after ParseURI.
+
+=cut
+
+sub _FreesideGetRecord { return undef; }
+
+
+=item _FreesideURIPrefix
+
+Method that returns the URI prefix for freeside URIs.
+
+=cut
+
+sub _FreesideURIPrefix {
+
+ my $self = shift;
+ return($self->Scheme . '://freeside');
+
+}
+
+=item _FreesideURILabel
+
+Method that returns a short string describing the customer referenced
+in the URI.
+
+=cut
+
+sub _FreesideURILabel {
+
+ my $self = shift;
+
+ $RT::Logger->debug("Called _FreesideURILabel()");
+
+ return unless (exists($self->{'fstable'}) and
+ exists($self->{'fspkey'}));
+
+ my $label;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+ if ($table ne 'cust_main') {
+ warn "FS::${table} not currently supported";
+ return;
+ }
+
+ my $rec = $self->_FreesideGetRecord();
+
+ if (ref($rec) eq 'HASH' and $table eq 'cust_main') {
+ my $name = $rec->{'last'} . ', ' . $rec->{'first'};
+ $name = $rec->{'company'} . " ($name)" if $rec->{'company'};
+ $label = "$pkey: $name";
+ } else {
+ $label = "$pkey: $table";
+ }
+
+ if ($label and !$@) {
+ return($label);
+ } else {
+ return;
+ }
+
+}
+
+=item _FreesideURILabelLong
+
+Method that returns a longer string describing the customer referenced
+in the URI.
+
+=cut
+
+sub _FreesideURILabelLong {
+
+ my $self = shift;
+
+ return $self->_FreesideURILabel();
+
+}
+
+=back
+
+=head1 Public methods
+
+=over 4
+
+=cut
+
+sub ParseURI {
+ my $self = shift;
+ my $uri = shift;
+ my ($table, $pkey);
+
+ my $uriprefix = $self->_FreesideURIPrefix;
+ if ($uri =~ /^$uriprefix\/(\w+)\/(\d+)$/) {
+ $table = $1;
+ $pkey = $2;
+ $self->{'scheme'} = $self->Scheme;
+ } else {
+ return(undef);
+ }
+
+ $self->{'uri'} = "${uriprefix}/${table}/${pkey}";
+ $self->{'fstable'} = $table;
+ $self->{'fspkey'} = $pkey;
+
+
+ my $url = $self->FreesideURL();
+
+ if ($url ne '') {
+ $self->{'href'} = "${url}/view/${table}.cgi?${pkey}";
+ } else {
+ $self->{'href'} = $self->{'uri'};
+ }
+
+ $self->{'uri'};
+
+}
+
+sub Scheme {
+ my $self = shift;
+ return('freeside');
+
+}
+
+sub HREF {
+ my $self = shift;
+ return($self->{'href'} || $self->{'uri'});
+}
+
+sub IsLocal {
+ my $self = shift;
+ return undef;
+}
+
+=item AsString
+
+Return a "pretty" string representing the URI object.
+
+This is meant to be used like this:
+
+ % $re = $uri->Resolver;
+ <A HREF="<% $re->HREF %>"><% $re->AsString %></A>
+
+=cut
+
+sub AsString {
+ my $self = shift;
+ my $prettystring;
+ if ($prettystring = $self->_FreesideURILabel) {
+ return $prettystring;
+ } else {
+ return $self->URI;
+ }
+}
+
+=item AsStringLong
+
+Return a longer (HTML) string representing the URI object.
+
+=cut
+
+sub AsStringLong {
+ my $self = shift;
+ my $prettystring;
+ if ($prettystring = $self->_FreesideURILabelLong || $self->_FreesideURILabel){
+ return $prettystring;
+ } else {
+ return $self->URI;
+ }
+}
+
+$IntegrationType ||= 'Internal';
+eval "require RT::URI::freeside::${RT::URI::freeside::IntegrationType}";
+warn $@ if $@;
+if ($@ &&
+ $@ !~ qr(^Can't locate RT/URI/freeside/${RT::URI::freeside::IntegrationType}.pm)) {
+ die $@;
+};
+
+=back
+
+=cut
+
+1;
diff --git a/rt/lib/RT/URI/freeside/Internal.pm b/rt/lib/RT/URI/freeside/Internal.pm
new file mode 100644
index 0000000..bd7c42c
--- /dev/null
+++ b/rt/lib/RT/URI/freeside/Internal.pm
@@ -0,0 +1,145 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+#
+use strict;
+no warnings qw(redefine);
+
+#use vars qw($conf);
+
+use FS;
+use FS::UID qw(dbh);
+use FS::CGI qw(popurl);
+use FS::UI::Web::small_custview qw(small_custview);
+use FS::Conf;
+use FS::Record qw(qsearchs qsearch dbdef);
+use FS::cust_main;
+use FS::cust_svc;
+
+=head1 NAME
+
+RT::URI::freeside::Internal
+
+=head1 DESCRIPTION
+
+Overlay for the RT::URI::freeside URI handler implementing the Internal integration type.
+
+See L<RT::URI::freeside> for public/private interface documentation.
+
+=cut
+
+
+
+sub _FreesideGetRecord {
+
+ my $self = shift;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+ $RT::Logger->debug("Called _FreesideGetRecord()");
+
+ #eval "use FS::$table;";
+
+ my $dbdef = dbdef;
+ unless ($dbdef) {
+ $RT::Logger->error("Using Internal freeside integration type, ".
+ "but it doesn't look like we're running under ".
+ "freeside's Mason handler.");
+ return;
+ }
+
+ my $pkeyfield = $dbdef->table($table)->primary_key;
+ unless ($pkeyfield) {
+ $RT::Logger->error("No primary key for freeside table '$table'");
+ return;
+ }
+
+ my $fsrec = qsearchs($table, { $pkeyfield => $pkey });
+ unless ($fsrec) {
+ $RT::Logger->error("Record with '$pkeyfield' == '$pkey' does " .
+ "not exist in table $table");
+ return;
+ }
+
+ return { $fsrec->hash, '_object' => $fsrec };
+
+}
+
+sub FreesideVersion {
+
+ return $FS::VERSION;
+
+}
+
+sub FreesideGetConfig {
+
+ #$conf = new FS::Conf unless ref($conf);
+ my $conf = new FS::Conf;
+
+ return scalar($conf->config(@_));
+
+}
+
+sub smart_search { #Subroutine
+
+ return map { { $_->hash } } &FS::cust_main::smart_search(@_);
+
+}
+
+sub email_search { #Subroutine
+
+ return map { { $_->hash } } &FS::cust_main::email_search(@_);
+
+}
+
+sub small_custview {
+
+ return &FS::UI::Web::small_custview::small_custview(@_);
+
+}
+
+sub _FreesideURILabelLong {
+
+ my $self = shift;
+
+ my $table = $self->{'fstable'};
+
+ if ( $table eq 'cust_main' ) {
+
+ my $rec = $self->_FreesideGetRecord();
+ return small_custview( $rec->{'_object'},
+ scalar(FS::Conf->new->config('countrydefault')),
+ 1 #nobalance
+ );
+
+ } else {
+
+ return $self->_FreesideURILabel();
+
+ }
+
+}
+
+1;
diff --git a/rt/lib/RT/URI/freeside/XMLRPC.pm b/rt/lib/RT/URI/freeside/XMLRPC.pm
new file mode 100644
index 0000000..916c20d
--- /dev/null
+++ b/rt/lib/RT/URI/freeside/XMLRPC.pm
@@ -0,0 +1,122 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+
+use strict;
+no warnings qw(redefine);
+
+use vars qw($XMLRPC_URL $_FS_VERSION);
+
+use Frontier::Client;
+
+=head1 NAME
+
+RT::URI::freeside::XMLRPC
+
+=head1 DESCRIPTION
+
+Overlay for the RT::URI::freeside URI handler implementing the XMLRPC integration type.
+
+See L<RT::URI::freeside> for public/private interface documentation.
+
+=cut
+
+
+sub _XMLRPCRequest { #Subroutine
+
+ my $method = shift;
+ my @args = @_;
+
+ my $result;
+ eval {
+ my $server = new Frontier::Client ( url => $XMLRPC_URL );
+ $result = $server->call($method, @args);
+ };
+
+ if (not $@ and ref($result) eq 'ARRAY') {
+ return (scalar(@$result) == 1) ? @$result[0] : @$result;
+ } else {
+ $RT::Logger->debug("Freeside XMLRPC: " . $result || $@);
+ return ();
+ }
+
+}
+
+sub _FreesideGetRecord {
+
+ my $self = shift;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+ my $record;
+
+ $RT::Logger->debug("Called XMLRPC::_FreesideGetRecord()");
+
+ #FIXME: Need a better way to get primary keys.
+ # Maybe create a method for it and cache them like version?
+ my %table_pkeys = (
+ cust_main => 'custnum',
+ );
+
+ my $method = 'Record.qsearchs';
+ my @args = ($table, { $table_pkeys{$table} => $pkey });
+ my ($record) = &_XMLRPCRequest($method, @args);
+
+ return $record;
+
+}
+
+
+sub FreesideGetConfig {
+
+ return _XMLRPCRequest('Conf.config', @_);
+
+}
+
+
+sub FreesideVersion {
+
+ return $_FS_VERSION if ($_FS_VERSION =~ /^\d+\.\d+\.\d+/);
+
+ $RT::Logger->debug("Requesting freeside version...");
+ ($_FS_VERSION) = &_XMLRPCRequest('version');
+ $RT::Logger->debug("Cached freeside version: ${_FS_VERSION}");
+
+ return $_FS_VERSION;
+
+}
+
+sub smart_search { #Subroutine
+
+ return _XMLRPCRequest('cust_main.smart_search', @_);
+
+}
+
+sub small_custview {
+
+ return _XMLRPCRequest('Web.UI.small_custview.small_custview', @_);
+
+}
+
+1;
diff --git a/rt/lib/RT/URI/fsck_com_rt.pm b/rt/lib/RT/URI/fsck_com_rt.pm
new file mode 100644
index 0000000..f3e4d21
--- /dev/null
+++ b/rt/lib/RT/URI/fsck_com_rt.pm
@@ -0,0 +1,270 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+package RT::URI::fsck_com_rt;
+
+use RT::Ticket;
+
+use RT::URI::base;
+
+use strict;
+use vars qw(@ISA);
+@ISA = qw/RT::URI::base/;
+
+
+
+
+=head2 LocalURIPrefix
+
+Returns the prefix for a local URI.
+
+=begin testing
+
+use_ok("RT::URI::fsck_com_rt");
+my $uri = RT::URI::fsck_com_rt->new($RT::SystemUser);
+
+ok(ref($uri));
+
+use Data::Dumper;
+
+
+ok (UNIVERSAL::isa($uri,RT::URI::fsck_com_rt), "It's an RT::URI::fsck_com_rt");
+
+ok ($uri->isa('RT::URI::base'), "It's an RT::URI::base");
+ok ($uri->isa('RT::Base'), "It's an RT::Base");
+
+is ($uri->LocalURIPrefix , 'fsck.com-rt://'.$RT::Organization);
+
+=end testing
+
+
+
+=cut
+
+sub LocalURIPrefix {
+ my $self = shift;
+
+ my $prefix = $self->Scheme. "://$RT::Organization";
+
+ return ($prefix);
+}
+
+=head2 ObjectType
+
+=cut
+
+sub ObjectType {
+ my $self = shift;
+ my $object = shift || $self->Object;
+
+ my $type = 'ticket';
+ if (ref($object) && (ref($object) ne 'RT::Ticket')) {
+ $type = ref($object);
+ }
+
+ return ($type);
+}
+
+
+
+
+=head2 URIForObject RT::Record
+
+Returns the RT URI for a local RT::Record object
+
+=begin testing
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+$ticket->Load(1);
+my $uri = RT::URI::fsck_com_rt->new($ticket->CurrentUser);
+is($uri->LocalURIPrefix. "/ticket/1" , $uri->URIForObject($ticket));
+
+=end testing
+
+=cut
+
+sub URIForObject {
+ my $self = shift;
+ my $obj = shift;
+ return ($self->LocalURIPrefix ."/". $self->ObjectType($obj) ."/". $obj->Id);
+}
+
+
+=head2 ParseURI URI
+
+When handed an fsck.com-rt: URI, figures out things like whether its a local record and what its ID is
+
+=cut
+
+
+sub ParseURI {
+ my $self = shift;
+ my $uri = shift;
+
+ if ( $uri =~ /^\d+$/ ) {
+ my $ticket = RT::Ticket->new( $self->CurrentUser );
+ $ticket->Load( $uri );
+ $self->{'uri'} = $ticket->URI;
+ $self->{'object'} = $ticket;
+ return ($ticket->id);
+ }
+ else {
+ $self->{'uri'} = $uri;
+ }
+
+ #If it's a local URI, load the ticket object and return its URI
+ if ( $self->IsLocal ) {
+ my $local_uri_prefix = $self->LocalURIPrefix;
+ if ( $self->{'uri'} =~ /^\Q$local_uri_prefix\E\/(.*?)\/(\d+)$/i ) {
+ my $type = $1;
+ my $id = $2;
+
+ if ( $type eq 'ticket' ) { $type = 'RT::Ticket' }
+
+ # We can instantiate any RT::Record subtype. but not anything else
+
+ if ( UNIVERSAL::isa( $type, 'RT::Record' ) ) {
+ my $record = $type->new( $self->CurrentUser );
+ $record->Load($id);
+
+ if ( $record->Id ) {
+ $self->{'object'} = $record;
+ return ( $record->Id );
+ }
+ }
+
+ }
+ }
+ return undef;
+}
+
+=head2 IsLocal
+
+Returns true if this URI is for a local ticket.
+Returns undef otherwise.
+
+
+
+=cut
+
+sub IsLocal {
+ my $self = shift;
+ my $local_uri_prefix = $self->LocalURIPrefix;
+ if ( $self->{'uri'} =~ /^\Q$local_uri_prefix/i ) {
+ return 1;
+ }
+ else {
+ return undef;
+ }
+}
+
+
+
+=head2 Object
+
+Returns the object for this URI, if it's local. Otherwise returns undef.
+
+=cut
+
+sub Object {
+ my $self = shift;
+ return ($self->{'object'});
+
+}
+
+=head2 Scheme
+
+Return the URI scheme for RT records
+
+=cut
+
+
+sub Scheme {
+ my $self = shift;
+ return "fsck.com-rt";
+}
+
+=head2 HREF
+
+If this is a local ticket, return an HTTP url to it.
+Otherwise, return its URI
+
+=cut
+
+
+sub HREF {
+ my $self = shift;
+ if ($self->IsLocal && $self->Object && ($self->ObjectType eq 'ticket')) {
+ return ( $RT::WebURL . "Ticket/Display.html?id=".$self->Object->Id);
+ }
+ else {
+ return ($self->URI);
+ }
+}
+
+=head2 AsString
+
+Returns either a localized string 'ticket #23' or the full URI if the object is not local
+
+=cut
+
+sub AsString {
+ my $self = shift;
+ if ($self->IsLocal && $self->Object) {
+ return $self->loc("[_1] #[_2]", $self->ObjectType, $self->Object->Id);
+ }
+ else {
+ return $self->URI;
+ }
+}
+
+eval "require RT::URI::fsck_com_rt_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI/fsck_com_rt_Vendor.pm});
+eval "require RT::URI::fsck_com_rt_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI/fsck_com_rt_Local.pm});
+
+1;
diff --git a/rt/lib/RT/URI/t.pm b/rt/lib/RT/URI/t.pm
new file mode 100644
index 0000000..efd13a4
--- /dev/null
+++ b/rt/lib/RT/URI/t.pm
@@ -0,0 +1,130 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+package RT::URI::t;
+
+use RT::Ticket;
+use RT::URI::base;
+
+use strict;
+use vars qw(@ISA);
+@ISA = qw/RT::URI::fsck_com_rt/;
+
+my $scheme = "t";
+
+=head2 ParseURI URI
+
+When handed an t: URI, figures out if it is an RT ticket. This is an
+alternate short form of specifying a full ticket URI.
+
+=begin testing
+
+use_ok("RT::URI::t");
+my $uri = RT::URI::t->new($RT::SystemUser);
+ok(ref($uri), "URI object exists");
+
+my $uristr = "t:1";
+$uri->ParseURI($uristr);
+is(ref($uri->Object), "RT::Ticket", "Object loaded is a ticket");
+is($uri->Object->Id, 1, "Object loaded has correct ID");
+is($uri->URI, 'fsck.com-rt://'.$RT::Organization.'/ticket/1',
+ "URI object has correct URI string");
+
+=end testing
+
+=cut
+
+sub ParseURI {
+ my $self = shift;
+ my $uri = shift;
+
+ # "t:<articlenum>"
+ # Pass this off to fsck_com_rt, which is equipped to deal with
+ # tickets after stripping off the t: prefix.
+
+ if ($uri =~ /^$scheme:(\d+)/) {
+ return $self->SUPER::ParseURI($1);
+ } else {
+ $self->{'uri'} = $uri;
+ return undef;
+ }
+}
+
+=head2 Scheme
+
+Return the URI scheme
+
+=cut
+
+sub Scheme {
+ return $scheme;
+}
+
+1;
diff --git a/rt/lib/RT/User.pm b/rt/lib/RT/User.pm
new file mode 100755
index 0000000..c28d89f
--- /dev/null
+++ b/rt/lib/RT/User.pm
@@ -0,0 +1,878 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+RT::User
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::User;
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('Users');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ varchar(200) 'Name'.
+ varchar(40) 'Password'.
+ blob 'Comments'.
+ blob 'Signature'.
+ varchar(120) 'EmailAddress'.
+ blob 'FreeformContactInfo'.
+ varchar(200) 'Organization'.
+ varchar(120) 'RealName'.
+ varchar(16) 'NickName'.
+ varchar(16) 'Lang'.
+ varchar(16) 'EmailEncoding'.
+ varchar(16) 'WebEncoding'.
+ varchar(100) 'ExternalContactInfoId'.
+ varchar(30) 'ContactInfoSystem'.
+ varchar(100) 'ExternalAuthId'.
+ varchar(30) 'AuthSystem'.
+ varchar(16) 'Gecos'.
+ varchar(30) 'HomePhone'.
+ varchar(30) 'WorkPhone'.
+ varchar(30) 'MobilePhone'.
+ varchar(30) 'PagerPhone'.
+ varchar(200) 'Address1'.
+ varchar(200) 'Address2'.
+ varchar(100) 'City'.
+ varchar(100) 'State'.
+ varchar(16) 'Zip'.
+ varchar(50) 'Country'.
+ varchar(50) 'Timezone'.
+ text 'PGPKey'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Password => '',
+ Comments => '',
+ Signature => '',
+ EmailAddress => '',
+ FreeformContactInfo => '',
+ Organization => '',
+ RealName => '',
+ NickName => '',
+ Lang => '',
+ EmailEncoding => '',
+ WebEncoding => '',
+ ExternalContactInfoId => '',
+ ContactInfoSystem => '',
+ ExternalAuthId => '',
+ AuthSystem => '',
+ Gecos => '',
+ HomePhone => '',
+ WorkPhone => '',
+ MobilePhone => '',
+ PagerPhone => '',
+ Address1 => '',
+ Address2 => '',
+ City => '',
+ State => '',
+ Zip => '',
+ Country => '',
+ Timezone => '',
+ PGPKey => '',
+
+ @_);
+ $self->SUPER::Create(
+ Name => $args{'Name'},
+ Password => $args{'Password'},
+ Comments => $args{'Comments'},
+ Signature => $args{'Signature'},
+ EmailAddress => $args{'EmailAddress'},
+ FreeformContactInfo => $args{'FreeformContactInfo'},
+ Organization => $args{'Organization'},
+ RealName => $args{'RealName'},
+ NickName => $args{'NickName'},
+ Lang => $args{'Lang'},
+ EmailEncoding => $args{'EmailEncoding'},
+ WebEncoding => $args{'WebEncoding'},
+ ExternalContactInfoId => $args{'ExternalContactInfoId'},
+ ContactInfoSystem => $args{'ContactInfoSystem'},
+ ExternalAuthId => $args{'ExternalAuthId'},
+ AuthSystem => $args{'AuthSystem'},
+ Gecos => $args{'Gecos'},
+ HomePhone => $args{'HomePhone'},
+ WorkPhone => $args{'WorkPhone'},
+ MobilePhone => $args{'MobilePhone'},
+ PagerPhone => $args{'PagerPhone'},
+ Address1 => $args{'Address1'},
+ Address2 => $args{'Address2'},
+ City => $args{'City'},
+ State => $args{'State'},
+ Zip => $args{'Zip'},
+ Country => $args{'Country'},
+ Timezone => $args{'Timezone'},
+ PGPKey => $args{'PGPKey'},
+);
+
+}
+
+
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(200).)
+
+
+
+=head2 SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Password
+
+Returns the current value of Password.
+(In the database, Password is stored as varchar(40).)
+
+
+
+=head2 SetPassword VALUE
+
+
+Set Password to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Password will be stored as a varchar(40).)
+
+
+=cut
+
+
+=head2 Comments
+
+Returns the current value of Comments.
+(In the database, Comments is stored as blob.)
+
+
+
+=head2 SetComments VALUE
+
+
+Set Comments to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Comments will be stored as a blob.)
+
+
+=cut
+
+
+=head2 Signature
+
+Returns the current value of Signature.
+(In the database, Signature is stored as blob.)
+
+
+
+=head2 SetSignature VALUE
+
+
+Set Signature to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Signature will be stored as a blob.)
+
+
+=cut
+
+
+=head2 EmailAddress
+
+Returns the current value of EmailAddress.
+(In the database, EmailAddress is stored as varchar(120).)
+
+
+
+=head2 SetEmailAddress VALUE
+
+
+Set EmailAddress to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, EmailAddress will be stored as a varchar(120).)
+
+
+=cut
+
+
+=head2 FreeformContactInfo
+
+Returns the current value of FreeformContactInfo.
+(In the database, FreeformContactInfo is stored as blob.)
+
+
+
+=head2 SetFreeformContactInfo VALUE
+
+
+Set FreeformContactInfo to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, FreeformContactInfo will be stored as a blob.)
+
+
+=cut
+
+
+=head2 Organization
+
+Returns the current value of Organization.
+(In the database, Organization is stored as varchar(200).)
+
+
+
+=head2 SetOrganization VALUE
+
+
+Set Organization to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Organization will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 RealName
+
+Returns the current value of RealName.
+(In the database, RealName is stored as varchar(120).)
+
+
+
+=head2 SetRealName VALUE
+
+
+Set RealName to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, RealName will be stored as a varchar(120).)
+
+
+=cut
+
+
+=head2 NickName
+
+Returns the current value of NickName.
+(In the database, NickName is stored as varchar(16).)
+
+
+
+=head2 SetNickName VALUE
+
+
+Set NickName to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, NickName will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 Lang
+
+Returns the current value of Lang.
+(In the database, Lang is stored as varchar(16).)
+
+
+
+=head2 SetLang VALUE
+
+
+Set Lang to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Lang will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 EmailEncoding
+
+Returns the current value of EmailEncoding.
+(In the database, EmailEncoding is stored as varchar(16).)
+
+
+
+=head2 SetEmailEncoding VALUE
+
+
+Set EmailEncoding to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, EmailEncoding will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 WebEncoding
+
+Returns the current value of WebEncoding.
+(In the database, WebEncoding is stored as varchar(16).)
+
+
+
+=head2 SetWebEncoding VALUE
+
+
+Set WebEncoding to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, WebEncoding will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 ExternalContactInfoId
+
+Returns the current value of ExternalContactInfoId.
+(In the database, ExternalContactInfoId is stored as varchar(100).)
+
+
+
+=head2 SetExternalContactInfoId VALUE
+
+
+Set ExternalContactInfoId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ExternalContactInfoId will be stored as a varchar(100).)
+
+
+=cut
+
+
+=head2 ContactInfoSystem
+
+Returns the current value of ContactInfoSystem.
+(In the database, ContactInfoSystem is stored as varchar(30).)
+
+
+
+=head2 SetContactInfoSystem VALUE
+
+
+Set ContactInfoSystem to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ContactInfoSystem will be stored as a varchar(30).)
+
+
+=cut
+
+
+=head2 ExternalAuthId
+
+Returns the current value of ExternalAuthId.
+(In the database, ExternalAuthId is stored as varchar(100).)
+
+
+
+=head2 SetExternalAuthId VALUE
+
+
+Set ExternalAuthId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ExternalAuthId will be stored as a varchar(100).)
+
+
+=cut
+
+
+=head2 AuthSystem
+
+Returns the current value of AuthSystem.
+(In the database, AuthSystem is stored as varchar(30).)
+
+
+
+=head2 SetAuthSystem VALUE
+
+
+Set AuthSystem to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, AuthSystem will be stored as a varchar(30).)
+
+
+=cut
+
+
+=head2 Gecos
+
+Returns the current value of Gecos.
+(In the database, Gecos is stored as varchar(16).)
+
+
+
+=head2 SetGecos VALUE
+
+
+Set Gecos to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Gecos will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 HomePhone
+
+Returns the current value of HomePhone.
+(In the database, HomePhone is stored as varchar(30).)
+
+
+
+=head2 SetHomePhone VALUE
+
+
+Set HomePhone to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, HomePhone will be stored as a varchar(30).)
+
+
+=cut
+
+
+=head2 WorkPhone
+
+Returns the current value of WorkPhone.
+(In the database, WorkPhone is stored as varchar(30).)
+
+
+
+=head2 SetWorkPhone VALUE
+
+
+Set WorkPhone to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, WorkPhone will be stored as a varchar(30).)
+
+
+=cut
+
+
+=head2 MobilePhone
+
+Returns the current value of MobilePhone.
+(In the database, MobilePhone is stored as varchar(30).)
+
+
+
+=head2 SetMobilePhone VALUE
+
+
+Set MobilePhone to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, MobilePhone will be stored as a varchar(30).)
+
+
+=cut
+
+
+=head2 PagerPhone
+
+Returns the current value of PagerPhone.
+(In the database, PagerPhone is stored as varchar(30).)
+
+
+
+=head2 SetPagerPhone VALUE
+
+
+Set PagerPhone to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, PagerPhone will be stored as a varchar(30).)
+
+
+=cut
+
+
+=head2 Address1
+
+Returns the current value of Address1.
+(In the database, Address1 is stored as varchar(200).)
+
+
+
+=head2 SetAddress1 VALUE
+
+
+Set Address1 to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Address1 will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 Address2
+
+Returns the current value of Address2.
+(In the database, Address2 is stored as varchar(200).)
+
+
+
+=head2 SetAddress2 VALUE
+
+
+Set Address2 to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Address2 will be stored as a varchar(200).)
+
+
+=cut
+
+
+=head2 City
+
+Returns the current value of City.
+(In the database, City is stored as varchar(100).)
+
+
+
+=head2 SetCity VALUE
+
+
+Set City to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, City will be stored as a varchar(100).)
+
+
+=cut
+
+
+=head2 State
+
+Returns the current value of State.
+(In the database, State is stored as varchar(100).)
+
+
+
+=head2 SetState VALUE
+
+
+Set State to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, State will be stored as a varchar(100).)
+
+
+=cut
+
+
+=head2 Zip
+
+Returns the current value of Zip.
+(In the database, Zip is stored as varchar(16).)
+
+
+
+=head2 SetZip VALUE
+
+
+Set Zip to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Zip will be stored as a varchar(16).)
+
+
+=cut
+
+
+=head2 Country
+
+Returns the current value of Country.
+(In the database, Country is stored as varchar(50).)
+
+
+
+=head2 SetCountry VALUE
+
+
+Set Country to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Country will be stored as a varchar(50).)
+
+
+=cut
+
+
+=head2 Timezone
+
+Returns the current value of Timezone.
+(In the database, Timezone is stored as varchar(50).)
+
+
+
+=head2 SetTimezone VALUE
+
+
+Set Timezone to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Timezone will be stored as a varchar(50).)
+
+
+=cut
+
+
+=head2 PGPKey
+
+Returns the current value of PGPKey.
+(In the database, PGPKey is stored as text.)
+
+
+
+=head2 SetPGPKey VALUE
+
+
+Set PGPKey to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, PGPKey will be stored as a text.)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Password =>
+ {read => 1, write => 1, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+ Comments =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ Signature =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ EmailAddress =>
+ {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ FreeformContactInfo =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ Organization =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ RealName =>
+ {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ NickName =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ Lang =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ EmailEncoding =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ WebEncoding =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ ExternalContactInfoId =>
+ {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ ContactInfoSystem =>
+ {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ ExternalAuthId =>
+ {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ AuthSystem =>
+ {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ Gecos =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ HomePhone =>
+ {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ WorkPhone =>
+ {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ MobilePhone =>
+ {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ PagerPhone =>
+ {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ Address1 =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ Address2 =>
+ {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ City =>
+ {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ State =>
+ {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ Zip =>
+ {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ Country =>
+ {read => 1, write => 1, sql_type => 12, length => 50, is_blob => 0, is_numeric => 0, type => 'varchar(50)', default => ''},
+ Timezone =>
+ {read => 1, write => 1, sql_type => 12, length => 50, is_blob => 0, is_numeric => 0, type => 'varchar(50)', default => ''},
+ PGPKey =>
+ {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ Creator =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy =>
+ {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated =>
+ {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+
+ }
+};
+
+
+ eval "require RT::User_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/User_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::User_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/User_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::User_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/User_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::User_Overlay, RT::User_Vendor, RT::User_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/User_Overlay.pm b/rt/lib/RT/User_Overlay.pm
new file mode 100644
index 0000000..8f4df46
--- /dev/null
+++ b/rt/lib/RT/User_Overlay.pm
@@ -0,0 +1,2205 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::User - RT User object
+
+=head1 SYNOPSIS
+
+ use RT::User;
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok(require RT::User);
+
+=end testing
+
+
+=cut
+
+
+package RT::User;
+
+use strict;
+no warnings qw(redefine);
+
+use vars qw(%_USERS_KEY_CACHE);
+
+%_USERS_KEY_CACHE = ();
+
+use Digest::MD5;
+use RT::Principals;
+use RT::ACE;
+use RT::Interface::Email;
+use Encode;
+
+# {{{ sub _Accessible
+
+
+sub _OverlayAccessible {
+ {
+
+ Name => { public => 1, admin => 1 },
+ Password => { read => 0 },
+ EmailAddress => { public => 1 },
+ Organization => { public => 1, admin => 1 },
+ RealName => { public => 1 },
+ NickName => { public => 1 },
+ Lang => { public => 1 },
+ EmailEncoding => { public => 1 },
+ WebEncoding => { public => 1 },
+ ExternalContactInfoId => { public => 1, admin => 1 },
+ ContactInfoSystem => { public => 1, admin => 1 },
+ ExternalAuthId => { public => 1, admin => 1 },
+ AuthSystem => { public => 1, admin => 1 },
+ Gecos => { public => 1, admin => 1 },
+ PGPKey => { public => 1, admin => 1 },
+
+ }
+}
+
+
+
+# }}}
+
+# {{{ sub Create
+
+=head2 Create { PARAMHASH }
+
+
+=begin testing
+
+# Make sure we can create a user
+
+my $u1 = RT::User->new($RT::SystemUser);
+is(ref($u1), 'RT::User');
+my ($id, $msg) = $u1->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok ($id, "Creating user CreateTest1 - " . $msg );
+
+# Make sure we can't create a second user with the same name
+my $u2 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u2->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-2@example.com');
+ok (!$id, $msg);
+
+
+# Make sure we can't create a second user with the same EmailAddress address
+my $u3 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u3->Create(Name => 'CreateTest2'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok (!$id, $msg);
+
+# Make sure we can create a user with no EmailAddress address
+my $u4 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u4->Create(Name => 'CreateTest3'.$$);
+ok ($id, $msg);
+
+# make sure we can create a second user with no EmailAddress address
+my $u5 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u5->Create(Name => 'CreateTest4'.$$);
+ok ($id, $msg);
+
+# make sure we can create a user with a blank EmailAddress address
+my $u6 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u6->Create(Name => 'CreateTest6'.$$, EmailAddress => '');
+ok ($id, $msg);
+# make sure we can create a second user with a blankEmailAddress address
+my $u7 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u7->Create(Name => 'CreateTest7'.$$, EmailAddress => '');
+ok ($id, $msg);
+
+# Can we change the email address away from from "";
+($id,$msg) = $u7->SetEmailAddress('foo@bar'.$$);
+ok ($id, $msg);
+# can we change the address back to "";
+($id,$msg) = $u7->SetEmailAddress('');
+ok ($id, $msg);
+is ($u7->EmailAddress, '');
+
+
+=end testing
+
+=cut
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Privileged => 0,
+ Disabled => 0,
+ EmailAddress => '',
+ _RecordTransaction => 1,
+ @_ # get the real argumentlist
+ );
+
+ # remove the value so it does not cripple SUPER::Create
+ my $record_transaction = delete $args{'_RecordTransaction'};
+
+ #Check the ACL
+ unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
+ return ( 0, $self->loc('No permission to create users') );
+ }
+
+
+ unless ($self->CanonicalizeUserInfo(\%args)) {
+ return ( 0, $self->loc("Could not set user info") );
+ }
+
+ $args{'EmailAddress'} = $self->CanonicalizeEmailAddress($args{'EmailAddress'});
+
+ # if the user doesn't have a name defined, set it to the email address
+ $args{'Name'} = $args{'EmailAddress'} unless ($args{'Name'});
+
+
+
+ # Privileged is no longer a column in users
+ my $privileged = $args{'Privileged'};
+ delete $args{'Privileged'};
+
+
+ if ($args{'CryptedPassword'} ) {
+ $args{'Password'} = $args{'CryptedPassword'};
+ delete $args{'CryptedPassword'};
+ }
+ elsif ( !$args{'Password'} ) {
+ $args{'Password'} = '*NO-PASSWORD*';
+ }
+ elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long",$RT::MinimumPasswordLength) );
+ }
+
+ else {
+ $args{'Password'} = $self->_GeneratePassword($args{'Password'});
+ }
+
+ #TODO Specify some sensible defaults.
+
+ unless ( $args{'Name'} ) {
+ use Data::Dumper;
+ $RT::Logger->crit(Dumper \%args);
+ return ( 0, $self->loc("Must specify 'Name' attribute") );
+ }
+
+ #SANITY CHECK THE NAME AND ABORT IF IT'S TAKEN
+ if ($RT::SystemUser) { #This only works if RT::SystemUser has been defined
+ my $TempUser = RT::User->new($RT::SystemUser);
+ $TempUser->Load( $args{'Name'} );
+ return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
+
+ return ( 0, $self->loc('Email address in use') )
+ unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
+ }
+ else {
+ $RT::Logger->warning( "$self couldn't check for pre-existing users");
+ }
+
+
+ $RT::Handle->BeginTransaction();
+ # Groups deal with principal ids, rather than user ids.
+ # When creating this user, set up a principal Id for it.
+ my $principal = RT::Principal->new($self->CurrentUser);
+ my $principal_id = $principal->Create(PrincipalType => 'User',
+ Disabled => $args{'Disabled'},
+ ObjectId => '0');
+ # If we couldn't create a principal Id, get the fuck out.
+ unless ($principal_id) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("Couldn't create a Principal on new user create.");
+ $RT::Logger->crit("Strange things are afoot at the circle K");
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+ $principal->__Set(Field => 'ObjectId', Value => $principal_id);
+ delete $args{'Disabled'};
+
+ $self->SUPER::Create(id => $principal_id , %args);
+ my $id = $self->Id;
+
+ #If the create failed.
+ unless ($id) {
+ $RT::Handle->Rollback();
+ $RT::Logger->error("Could not create a new user - " .join('-', %args));
+
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+ my $aclstash = RT::Group->new($self->CurrentUser);
+ my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
+
+ unless ($stash_id) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("Couldn't stash the user in groupmembers");
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+
+ my $everyone = RT::Group->new($self->CurrentUser);
+ $everyone->LoadSystemInternalGroup('Everyone');
+ unless ($everyone->id) {
+ $RT::Logger->crit("Could not load Everyone group on user creation.");
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+
+ my ($everyone_id, $everyone_msg) = $everyone->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
+ unless ($everyone_id) {
+ $RT::Logger->crit("Could not add user to Everyone group on user creation.");
+ $RT::Logger->crit($everyone_msg);
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+
+ my $access_class = RT::Group->new($self->CurrentUser);
+ if ($privileged) {
+ $access_class->LoadSystemInternalGroup('Privileged');
+ } else {
+ $access_class->LoadSystemInternalGroup('Unprivileged');
+ }
+
+ unless ($access_class->id) {
+ $RT::Logger->crit("Could not load Privileged or Unprivileged group on user creation");
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+
+ my ($ac_id, $ac_msg) = $access_class->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
+
+ unless ($ac_id) {
+ $RT::Logger->crit("Could not add user to Privileged or Unprivileged group on user creation. Aborted");
+ $RT::Logger->crit($ac_msg);
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+
+ if ( $record_transaction ) {
+ $self->_NewTransaction( Type => "Create" );
+ }
+
+ $RT::Handle->Commit;
+
+ return ( $id, $self->loc('User created') );
+}
+
+# }}}
+
+
+
+# {{{ SetPrivileged
+
+=head2 SetPrivileged BOOL
+
+If passed a true value, makes this user a member of the "Privileged" PseudoGroup.
+Otherwise, makes this user a member of the "Unprivileged" pseudogroup.
+
+Returns a standard RT tuple of (val, msg);
+
+=begin testing
+
+
+ok(my $user = RT::User->new($RT::SystemUser));
+ok($user->Load('root'), "Loaded user 'root'");
+ok($user->Privileged, "User 'root' is privileged");
+ok(my ($v,$m) = $user->SetPrivileged(0));
+ok ($v ==1, "Set unprivileged suceeded ($m)");
+ok(!$user->Privileged, "User 'root' is no longer privileged");
+ok(my ($v2,$m2) = $user->SetPrivileged(1));
+ok ($v2 ==1, "Set privileged suceeded ($m2");
+ok($user->Privileged, "User 'root' is privileged again");
+
+=end testing
+
+=cut
+
+sub SetPrivileged {
+ my $self = shift;
+ my $val = shift;
+
+ #Check the ACL
+ unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
+ return ( 0, $self->loc('Permission Denied') );
+ }
+ my $priv = RT::Group->new($self->CurrentUser);
+ $priv->LoadSystemInternalGroup('Privileged');
+
+ unless ($priv->Id) {
+ $RT::Logger->crit("Could not find Privileged pseudogroup");
+ return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
+ }
+
+ my $unpriv = RT::Group->new($self->CurrentUser);
+ $unpriv->LoadSystemInternalGroup('Unprivileged');
+ unless ($unpriv->Id) {
+ $RT::Logger->crit("Could not find unprivileged pseudogroup");
+ return(0,$self->loc("Failed to find 'Unprivileged' users pseudogroup"));
+ }
+
+ if ($val) {
+ if ($priv->HasMember($self->PrincipalObj)) {
+ #$RT::Logger->debug("That user is already privileged");
+ return (0,$self->loc("That user is already privileged"));
+ }
+ if ($unpriv->HasMember($self->PrincipalObj)) {
+ $unpriv->_DeleteMember($self->PrincipalId);
+ } else {
+ # if we had layered transactions, life would be good
+ # sadly, we have to just go ahead, even if something
+ # bogus happened
+ $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
+ "unprivileged. something is drastically wrong.");
+ }
+ my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
+ if ($status) {
+ return (1, $self->loc("That user is now privileged"));
+ } else {
+ return (0, $msg);
+ }
+ }
+ else {
+ if ($unpriv->HasMember($self->PrincipalObj)) {
+ #$RT::Logger->debug("That user is already unprivileged");
+ return (0,$self->loc("That user is already unprivileged"));
+ }
+ if ($priv->HasMember($self->PrincipalObj)) {
+ $priv->_DeleteMember( $self->PrincipalId);
+ } else {
+ # if we had layered transactions, life would be good
+ # sadly, we have to just go ahead, even if something
+ # bogus happened
+ $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
+ "unprivileged. something is drastically wrong.");
+ }
+ my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
+ if ($status) {
+ return (1, $self->loc("That user is now unprivileged"));
+ } else {
+ return (0, $msg);
+ }
+ }
+}
+
+# }}}
+
+# {{{ Privileged
+
+=head2 Privileged
+
+Returns true if this user is privileged. Returns undef otherwise.
+
+=cut
+
+sub Privileged {
+ my $self = shift;
+ my $priv = RT::Group->new($self->CurrentUser);
+ $priv->LoadSystemInternalGroup('Privileged');
+ if ($priv->HasMember($self->PrincipalObj)) {
+ return(1);
+ }
+ else {
+ return(undef);
+ }
+}
+
+# }}}
+
+# {{{ sub _BootstrapCreate
+
+#create a user without validating _any_ data.
+
+#To be used only on database init.
+# We can't localize here because it's before we _have_ a loc framework
+
+sub _BootstrapCreate {
+ my $self = shift;
+ my %args = (@_);
+
+ $args{'Password'} = '*NO-PASSWORD*';
+
+
+ $RT::Handle->BeginTransaction();
+
+ # Groups deal with principal ids, rather than user ids.
+ # When creating this user, set up a principal Id for it.
+ my $principal = RT::Principal->new($self->CurrentUser);
+ my $principal_id = $principal->Create(PrincipalType => 'User', ObjectId => '0');
+ $principal->__Set(Field => 'ObjectId', Value => $principal_id);
+
+ # If we couldn't create a principal Id, get the fuck out.
+ unless ($principal_id) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("Couldn't create a Principal on new user create. Strange things are afoot at the circle K");
+ return ( 0, 'Could not create user' );
+ }
+ $self->SUPER::Create(id => $principal_id, %args);
+ my $id = $self->Id;
+ #If the create failed.
+ unless ($id) {
+ $RT::Handle->Rollback();
+ return ( 0, 'Could not create user' ) ; #never loc this
+ }
+
+ my $aclstash = RT::Group->new($self->CurrentUser);
+ my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
+
+ unless ($stash_id) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("Couldn't stash the user in groupmembers");
+ return ( 0, $self->loc('Could not create user') );
+ }
+
+
+ $RT::Handle->Commit();
+
+ return ( $id, 'User created' );
+}
+
+# }}}
+
+# {{{ sub Delete
+
+sub Delete {
+ my $self = shift;
+
+ return ( 0, $self->loc('Deleting this object would violate referential integrity') );
+
+}
+
+# }}}
+
+# {{{ sub Load
+
+=head2 Load
+
+Load a user object from the database. Takes a single argument.
+If the argument is numerical, load by the column 'id'. Otherwise, load by
+the "Name" column which is the user's textual username.
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift || return undef;
+
+ #if it's an int, load by id. otherwise, load by name.
+ if ( $identifier !~ /\D/ ) {
+ $self->SUPER::LoadById($identifier);
+ }
+ else {
+ $self->LoadByCol( "Name", $identifier );
+ }
+}
+
+# }}}
+
+# {{{ sub LoadByEmail
+
+=head2 LoadByEmail
+
+Tries to load this user object from the database by the user's email address.
+
+
+=cut
+
+sub LoadByEmail {
+ my $self = shift;
+ my $address = shift;
+
+ # Never load an empty address as an email address.
+ unless ($address) {
+ return (undef);
+ }
+
+ $address = $self->CanonicalizeEmailAddress($address);
+
+ #$RT::Logger->debug("Trying to load an email address: $address\n");
+ return $self->LoadByCol( "EmailAddress", $address );
+}
+
+# }}}
+
+# {{{ LoadOrCreateByEmail
+
+=head2 LoadOrCreateByEmail ADDRESS
+
+Attempts to find a user who has the provided email address. If that fails, creates an unprivileged user with
+the provided email address. and loads them.
+
+Returns a tuple of the user's id and a status message.
+0 will be returned in place of the user's id in case of failure.
+
+=cut
+
+sub LoadOrCreateByEmail {
+ my $self = shift;
+ my $email = shift;
+
+ my ($val, $message);
+
+ my ( $Address, $Name ) =
+ RT::Interface::Email::ParseAddressFromHeader($email);
+ $email = $Address;
+
+ $self->LoadByEmail($email);
+ $message = $self->loc('User loaded');
+ unless ($self->Id) {
+ $self->Load($email);
+ }
+ unless($self->Id) {
+ ( $val, $message ) = $self->Create(
+ Name => $email,
+ EmailAddress => $email,
+ RealName => $Name,
+ Privileged => 0,
+ Comments => 'Autocreated when added as a watcher');
+ unless ($val) {
+ # Deal with the race condition of two account creations at once
+ $self->LoadByEmail($email);
+ unless ($self->Id) {
+ sleep 5;
+ $self->LoadByEmail($email);
+ }
+ if ($self->Id) {
+ $RT::Logger->error("Recovered from creation failure due to race condition");
+ $message = $self->loc("User loaded");
+ }
+ else {
+ $RT::Logger->crit("Failed to create user ".$email .": " .$message);
+ }
+ }
+ }
+
+ if ($self->Id) {
+ return($self->Id, $message);
+ }
+ else {
+ return(0, $message);
+ }
+
+
+ }
+
+# }}}
+
+# {{{ sub ValidateEmailAddress
+
+=head2 ValidateEmailAddress ADDRESS
+
+Returns true if the email address entered is not in use by another user or is
+undef or ''. Returns false if it's in use.
+
+=cut
+
+sub ValidateEmailAddress {
+ my $self = shift;
+ my $Value = shift;
+
+ # if the email address is null, it's always valid
+ return (1) if ( !$Value || $Value eq "" );
+
+ my $TempUser = RT::User->new($RT::SystemUser);
+ $TempUser->LoadByEmail($Value);
+
+ if ( $TempUser->id && ( $TempUser->id != $self->id ) )
+ { # if we found a user with that address
+ # it's invalid to set this user's address to it
+ return (undef);
+ }
+ else { #it's a valid email address
+ return (1);
+ }
+}
+
+# }}}
+
+# {{{ sub CanonicalizeEmailAddress
+
+
+
+=head2 CanonicalizeEmailAddress ADDRESS
+
+CanonicalizeEmailAddress converts email addresses into canonical form.
+it takes one email address in and returns the proper canonical
+form. You can dump whatever your proper local config is in here. Note
+that it may be called as a static method; in this case the first argument
+is class name not an object.
+
+=cut
+
+sub CanonicalizeEmailAddress {
+ my $self = shift;
+ my $email = shift;
+ # Example: the following rule would treat all email
+ # coming from a subdomain as coming from second level domain
+ # foo.com
+ if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
+ $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
+ }
+ return ($email);
+}
+
+
+# }}}
+
+# {{{ sub CanonicalizeUserInfo
+
+
+
+=head2 CanonicalizeUserInfo HASH of ARGS
+
+CanonicalizeUserInfo can convert all User->Create options.
+it takes a hashref of all the params sent to User->Create and
+returns that same hash, by default nothing is done.
+
+This function is intended to allow users to have their info looked up via
+an outside source and modified upon creation.
+
+=cut
+
+sub CanonicalizeUserInfo {
+ my $self = shift;
+ my $args = shift;
+ my $success = 1;
+
+ return ($success);
+}
+
+
+# }}}
+
+
+# {{{ Password related functions
+
+# {{{ sub SetRandomPassword
+
+=head2 SetRandomPassword
+
+Takes no arguments. Returns a status code and a new password or an error message.
+If the status is 1, the second value returned is the new password.
+If the status is anything else, the new value returned is the error code.
+
+=cut
+
+sub SetRandomPassword {
+ my $self = shift;
+
+ unless ( $self->CurrentUserCanModify('Password') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+
+ my $min = ( $RT::MinimumPasswordLength > 6 ? $RT::MinimumPasswordLength : 6);
+ my $max = ( $RT::MinimumPasswordLength > 8 ? $RT::MinimumPasswordLength : 8);
+
+ my $pass = $self->GenerateRandomPassword( $min, $max) ;
+
+ # If we have "notify user on
+
+ my ( $val, $msg ) = $self->SetPassword($pass);
+
+ #If we got an error return the error.
+ return ( 0, $msg ) unless ($val);
+
+ #Otherwise, we changed the password, lets return it.
+ return ( 1, $pass );
+
+}
+
+# }}}
+
+# {{{ sub ResetPassword
+
+=head2 ResetPassword
+
+Returns status, [ERROR or new password]. Resets this user\'s password to
+a randomly generated pronouncable password and emails them, using a
+global template called "RT_PasswordChange", which can be overridden
+with global templates "RT_PasswordChange_Privileged" or "RT_PasswordChange_NonPrivileged"
+for privileged and Non-privileged users respectively.
+
+=cut
+
+sub ResetPassword {
+ my $self = shift;
+
+ unless ( $self->CurrentUserCanModify('Password') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ my ( $status, $pass ) = $self->SetRandomPassword();
+
+ unless ($status) {
+ return ( 0, "$pass" );
+ }
+
+ my $template = RT::Template->new( $self->CurrentUser );
+
+ if ( $self->Privileged ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
+ }
+ else {
+ $template->LoadGlobalTemplate('RT_PasswordChange_NonPrivileged');
+ }
+
+ unless ( $template->Id ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange');
+ }
+
+ unless ( $template->Id ) {
+ $RT::Logger->crit( "$self tried to send "
+ . $self->Name
+ . " a password reminder "
+ . "but couldn't find a password change template" );
+ }
+
+ my $notification = RT::Action::SendPasswordEmail->new(
+ TemplateObj => $template,
+ Argument => $pass
+ );
+
+ $notification->SetHeader( 'To', $self->EmailAddress );
+
+ my ($ret);
+ $ret = $notification->Prepare();
+ if ($ret) {
+ $ret = $notification->Commit();
+ }
+
+ if ($ret) {
+ return ( 1, $self->loc('New password notification sent') );
+ }
+ else {
+ return ( 0, $self->loc('Notification could not be sent') );
+ }
+
+}
+
+# }}}
+
+# {{{ sub GenerateRandomPassword
+
+=head2 GenerateRandomPassword MIN_LEN and MAX_LEN
+
+Returns a random password between MIN_LEN and MAX_LEN characters long.
+
+=cut
+
+sub GenerateRandomPassword {
+ my $self = shift;
+ my $min_length = shift;
+ my $max_length = shift;
+
+ #This code derived from mpw.pl, a bit of code with a sordid history
+ # Its notes:
+
+ # Perl cleaned up a bit by Jesse Vincent 1/14/2001.
+ # Converted to perl from C by Marc Horowitz, 1/20/2000.
+ # Converted to C from Multics PL/I by Bill Sommerfeld, 4/21/86.
+ # Original PL/I version provided by Jerry Saltzer.
+
+ my ( $frequency, $start_freq, $total_sum, $row_sums );
+
+ #When munging characters, we need to know where to start counting letters from
+ my $a = ord('a');
+
+ # frequency of English digraphs (from D Edwards 1/27/66)
+ $frequency = [
+ [
+ 4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62, 23, 167,
+ 2, 14, 0, 83, 76, 127, 7, 25, 8, 1, 9, 1
+ ], # aa - az
+ [
+ 13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0,
+ 11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0
+ ], # ba - bz
+ [
+ 32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1, 0,
+ 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0
+ ], # ca - cz
+ [
+ 40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15, 6,
+ 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0
+ ], # da - dz
+ [
+ 84, 20, 55, 125, 51, 40, 19, 16, 50, 1,
+ 4, 55, 54, 146, 35, 37, 6, 191, 149, 65,
+ 9, 26, 21, 12, 5, 0
+ ], # ea - ez
+ [
+ 19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1, 0,
+ 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0
+ ], # fa - fz
+ [
+ 20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1, 4,
+ 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0
+ ], # ga - gz
+ [
+ 101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3, 2,
+ 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0
+ ], # ha - hz
+ [
+ 40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38, 25, 202,
+ 56, 12, 1, 46, 79, 117, 1, 22, 0, 4, 0, 3
+ ], # ia - iz
+ [
+ 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0
+ ], # ja - jz
+ [
+ 1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0
+ ], # ka - kz
+ [
+ 44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2, 2,
+ 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0
+ ], # la - lz
+ [
+ 52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7, 1,
+ 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0
+ ], # ma - mz
+ [
+ 42, 10, 47, 122, 63, 19, 106, 12, 30, 1,
+ 6, 6, 9, 7, 54, 7, 1, 7, 44, 124,
+ 6, 1, 15, 0, 12, 0
+ ], # na - nz
+ [
+ 7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19, 41, 134,
+ 13, 23, 0, 91, 23, 42, 55, 16, 28, 0, 4, 1
+ ], # oa - oz
+ [
+ 19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0,
+ 27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0
+ ], # pa - pz
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0
+ ], # qa - qz
+ [
+ 83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5, 26, 16,
+ 60, 4, 0, 24, 37, 55, 6, 11, 4, 0, 28, 0
+ ], # ra - rz
+ [
+ 65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7, 11, 12,
+ 56, 17, 6, 9, 48, 116, 35, 1, 28, 0, 4, 0
+ ], # sa - sz
+ [
+ 57, 22, 3, 1, 76, 5, 2, 330, 126, 1,
+ 0, 14, 10, 6, 79, 7, 0, 49, 50, 56,
+ 21, 2, 27, 0, 24, 0
+ ], # ta - tz
+ [
+ 11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31,
+ 1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0
+ ], # ua - uz
+ [
+ 7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0
+ ], # va - vz
+ [
+ 36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1, 8,
+ 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0
+ ], # wa - wz
+ [
+ 1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0,
+ 1, 5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0
+ ], # xa - xz
+ [
+ 14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7, 5,
+ 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0
+ ], # ya - yz
+ [
+ 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ ]
+ ]; # za - zz
+
+ #We need to know the totals for each row
+ $row_sums = [
+ map {
+ my $sum = 0;
+ map { $sum += $_ } @$_;
+ $sum;
+ } @$frequency
+ ];
+
+ #Frequency with which a given letter starts a word.
+ $start_freq = [
+ 1299, 425, 725, 271, 375, 470, 93, 223, 1009, 24,
+ 20, 355, 379, 319, 823, 618, 21, 317, 962, 1991,
+ 271, 104, 516, 6, 16, 14
+ ];
+
+ $total_sum = 0;
+ map { $total_sum += $_ } @$start_freq;
+
+ my $length = $min_length + int( rand( $max_length - $min_length ) );
+
+ my $char = $self->_GenerateRandomNextChar( $total_sum, $start_freq );
+ my @word = ( $char + $a );
+ for ( 2 .. $length ) {
+ $char =
+ $self->_GenerateRandomNextChar( $row_sums->[$char],
+ $frequency->[$char] );
+ push ( @word, $char + $a );
+ }
+
+ #Return the password
+ return pack( "C*", @word );
+
+}
+
+#A private helper function for RandomPassword
+# Takes a row summary and a frequency chart for the next character to be searched
+sub _GenerateRandomNextChar {
+ my $self = shift;
+ my ( $all, $freq ) = @_;
+ my ( $pos, $i );
+
+ for ( $pos = int( rand($all) ), $i = 0 ;
+ $pos >= $freq->[$i] ;
+ $pos -= $freq->[$i], $i++ )
+ {
+ }
+
+ return ($i);
+}
+
+# }}}
+
+# {{{ sub SetPassword
+
+=head2 SetPassword
+
+Takes a string. Checks the string's length and sets this user's password
+to that string.
+
+=cut
+
+sub SetPassword {
+ my $self = shift;
+ my $password = shift;
+
+ unless ( $self->CurrentUserCanModify('Password') ) {
+ return ( 0, $self->loc('Password: Permission Denied') );
+ }
+
+ if ( !$password ) {
+ return ( 0, $self->loc("No password set") );
+ }
+ elsif ( length($password) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long", $RT::MinimumPasswordLength) );
+ }
+ else {
+ my $new = !$self->HasPassword;
+ $password = $self->_GeneratePassword($password);
+ my ( $val, $msg ) = $self->SUPER::SetPassword($password);
+ if ($val) {
+ return ( 1, $self->loc("Password set") ) if $new;
+ return ( 1, $self->loc("Password changed") );
+ }
+ else {
+ return ( $val, $msg );
+ }
+ }
+
+}
+
+=head2 _GeneratePassword PASSWORD
+
+returns an MD5 hash of the password passed in, in hexadecimal encoding.
+
+=cut
+
+sub _GeneratePassword {
+ my $self = shift;
+ my $password = shift;
+
+ my $md5 = Digest::MD5->new();
+ $md5->add(encode_utf8($password));
+ return ($md5->hexdigest);
+
+}
+
+=head2 _GeneratePasswordBase64 PASSWORD
+
+returns an MD5 hash of the password passed in, in base64 encoding
+(obsoleted now).
+
+=cut
+
+sub _GeneratePasswordBase64 {
+ my $self = shift;
+ my $password = shift;
+
+ my $md5 = Digest::MD5->new();
+ $md5->add(encode_utf8($password));
+ return ($md5->b64digest);
+
+}
+
+# }}}
+
+
+=head2 HasPassword
+
+Returns true if the user has a valid password, otherwise returns false.
+
+=cut
+
+
+sub HasPassword {
+ my $self = shift;
+ my $pwd = $self->__Value('Password');
+ return undef if !defined $pwd
+ || $pwd eq ''
+ || $pwd eq '*NO-PASSWORD*';
+ return 1;
+}
+
+
+# {{{ sub IsPassword
+
+=head2 IsPassword
+
+Returns true if the passed in value is this user's password.
+Returns undef otherwise.
+
+=cut
+
+sub IsPassword {
+ my $self = shift;
+ my $value = shift;
+
+ #TODO there isn't any apparent way to legitimately ACL this
+
+ # RT does not allow null passwords
+ if ( ( !defined($value) ) or ( $value eq '' ) ) {
+ return (undef);
+ }
+
+ if ( $self->PrincipalObj->Disabled ) {
+ $RT::Logger->info(
+ "Disabled user " . $self->Name . " tried to log in" );
+ return (undef);
+ }
+
+ unless ($self->HasPassword) {
+ return(undef);
+ }
+
+ # generate an md5 password
+ if ($self->_GeneratePassword($value) eq $self->__Value('Password')) {
+ return(1);
+ }
+
+ # if it's a historical password we say ok.
+ if ($self->__Value('Password') eq crypt($value, $self->__Value('Password'))
+ or $self->_GeneratePasswordBase64($value) eq $self->__Value('Password'))
+ {
+ # ...but upgrade the legacy password inplace.
+ $self->SUPER::SetPassword( $self->_GeneratePassword($value) );
+ return(1);
+ }
+
+ # no password check has succeeded. get out
+
+ return (undef);
+}
+
+# }}}
+
+# }}}
+
+# {{{ sub SetDisabled
+
+=head2 Sub SetDisabled
+
+Toggles the user's disabled flag.
+If this flag is
+set, all password checks for this user will fail. All ACL checks for this
+user will fail. The user will appear in no user listings.
+
+=cut
+
+# }}}
+
+sub SetDisabled {
+ my $self = shift;
+ unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
+ return (0, $self->loc('Permission Denied'));
+ }
+ return $self->PrincipalObj->SetDisabled(@_);
+}
+
+sub Disabled {
+ my $self = shift;
+ return $self->PrincipalObj->Disabled(@_);
+}
+
+
+# {{{ Principal related routines
+
+=head2 PrincipalObj
+
+Returns the principal object for this user. returns an empty RT::Principal
+if there's no principal object matching this user.
+The response is cached. PrincipalObj should never ever change.
+
+=begin testing
+
+ok(my $u = RT::User->new($RT::SystemUser));
+ok($u->Load(1), "Loaded the first user");
+ok($u->PrincipalObj->ObjectId == 1, "user 1 is the first principal");
+is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group");
+
+=end testing
+
+=cut
+
+
+sub PrincipalObj {
+ my $self = shift;
+ unless ($self->{'PrincipalObj'} &&
+ ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
+ ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
+
+ $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
+ $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
+ 'PrincipalType' => 'User') ;
+ }
+ return($self->{'PrincipalObj'});
+}
+
+
+=head2 PrincipalId
+
+Returns this user's PrincipalId
+
+=cut
+
+sub PrincipalId {
+ my $self = shift;
+ return $self->Id;
+}
+
+# }}}
+
+
+
+# {{{ sub HasGroupRight
+
+=head2 HasGroupRight
+
+Takes a paramhash which can contain
+these items:
+ GroupObj => RT::Group or Group => integer
+ Right => 'Right'
+
+
+Returns 1 if this user has the right specified in the paramhash for the Group
+passed in.
+
+Returns undef if they don't.
+
+=cut
+
+sub HasGroupRight {
+ my $self = shift;
+ my %args = (
+ GroupObj => undef,
+ Group => undef,
+ Right => undef,
+ @_
+ );
+
+
+ if ( defined $args{'Group'} ) {
+ $args{'GroupObj'} = RT::Group->new( $self->CurrentUser );
+ $args{'GroupObj'}->Load( $args{'Group'} );
+ }
+
+ # {{{ Validate and load up the GroupId
+ unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
+ return undef;
+ }
+
+ # }}}
+
+
+ # Figure out whether a user has the right we're asking about.
+ my $retval = $self->HasRight(
+ Object => $args{'GroupObj'},
+ Right => $args{'Right'},
+ );
+
+ return ($retval);
+
+
+}
+
+# }}}
+
+# {{{ sub OwnGroups
+
+=head2 OwnGroups
+
+Returns a group collection object containing the groups of which this
+user is a member.
+
+=cut
+
+sub OwnGroups {
+ my $self = shift;
+ my $groups = RT::Groups->new($self->CurrentUser);
+ $groups->LimitToUserDefinedGroups;
+ $groups->WithMember(PrincipalId => $self->Id,
+ Recursively => 1);
+ return $groups;
+}
+
+# }}}
+
+# {{{ Links
+
+#much false laziness w/Ticket_Overlay.pm
+
+# A helper table for links mapping to make it easier
+# to build and parse links between tickets
+
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
+ MemberOf => { Base => 'MemberOf',
+ Target => 'HasMember', },
+ RefersTo => { Base => 'RefersTo',
+ Target => 'ReferredToBy', },
+ DependsOn => { Base => 'DependsOn',
+ Target => 'DependedOnBy', },
+ MergedInto => { Base => 'MergedInto',
+ Target => 'MergedInto', },
+
+);
+
+sub LINKDIRMAP { return \%LINKDIRMAP }
+
+#sub _Links {
+# my $self = shift;
+#
+# #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic ---
+# #tobias meant by $f
+# my $field = shift;
+# my $type = shift || "";
+#
+# unless ( $self->{"$field$type"} ) {
+# $self->{"$field$type"} = new RT::Links( $self->CurrentUser );
+# if ( $self->CurrentUserHasRight('ShowTicket') ) {
+# # Maybe this ticket is a merged ticket
+# my $Tickets = new RT::Tickets( $self->CurrentUser );
+# # at least to myself
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $self->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# $Tickets->Limit( FIELD => 'EffectiveId',
+# VALUE => $self->EffectiveId );
+# while (my $Ticket = $Tickets->Next) {
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $Ticket->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# }
+# $self->{"$field$type"}->Limit( FIELD => 'Type',
+# VALUE => $type )
+# if ($type);
+# }
+# }
+# return ( $self->{"$field$type"} );
+#}
+
+=head2 DeleteLink
+
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
+
+=cut
+
+sub DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ #check acls
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
+
+ if ( !$val ) {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $Msg );
+ }
+
+ my ($direction, $remote_link);
+
+ if ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+ elsif ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
+
+ if ( $args{'Silent'} ) {
+ return ( $val, $Msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => 'DeleteLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ OldValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0
+ );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'DeleteLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ OldValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+
+ return ( $Trans, $Msg );
+ }
+}
+
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ return $self->_AddLink(%args);
+}
+
+#sub __GetTicketFromURI {
+# my $self = shift;
+# my %args = ( URI => '', @_ );
+#
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my $uri_obj = RT::URI->new( $self->CurrentUser );
+# $uri_obj->FromURI( $args{'URI'} );
+#
+# unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
+# my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
+# $RT::Logger->warning( "$msg\n" );
+# return( 0, $msg );
+# }
+# my $obj = $uri_obj->Resolver->Object;
+# unless ( UNIVERSAL::isa($obj, 'RT::Ticket') && $obj->id ) {
+# return (1, 'Found not a ticket', undef);
+# }
+# return (1, 'Found ticket', $obj);
+#}
+
+=head2 _AddLink
+
+Private non-acled variant of AddLink so that links can be added during create.
+
+=cut
+
+sub _AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
+ return ($val, $msg) if !$val || $exist;
+
+ my ($direction, $remote_link);
+ if ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction = 'Base';
+ } elsif ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+
+ # Don't write the transaction if we're doing this on create
+ if ( $args{'Silent'} ) {
+ return ( $val, $msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ #Write the transaction
+ my ( $Trans, $Msg, $TransObj ) =
+ $self->_NewTransaction(Type => 'AddLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ NewValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0 );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'AddLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ NewValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+ return ( $val, $Msg );
+ }
+
+}
+
+
+
+# }}}
+
+
+# {{{ sub Rights testing
+
+=head1 Rights testing
+
+
+=begin testing
+
+my $root = RT::User->new($RT::SystemUser);
+$root->Load('root');
+ok($root->Id, "Found the root user");
+my $rootq = RT::Queue->new($root);
+$rootq->Load(1);
+ok($rootq->Id, "Loaded the first queue");
+
+ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
+
+my $new_user = RT::User->new($RT::SystemUser);
+my ($id, $msg) = $new_user->Create(Name => 'ACLTest'.$$);
+
+ok ($id, "Created a new user for acl test $msg");
+
+my $q = RT::Queue->new($new_user);
+$q->Load(1);
+ok($q->Id, "Loaded the first queue");
+
+
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "Some random user doesn't have the right to create tickets");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $q), "Granted the random user the right to create tickets");
+ok ($gval, "Grant succeeded - $gmsg");
+
+
+ok ($q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can create tickets after we grant him the right");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->RevokeRight( Right => 'CreateTicket', Object => $q), "revoked the random user the right to create tickets");
+ok ($gval, "Revocation succeeded - $gmsg");
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can't create tickets anymore");
+
+
+
+
+
+# Create a ticket in the queue
+my $new_tick = RT::Ticket->new($RT::SystemUser);
+my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General');
+ok($tickid, "Created ticket: $tickid");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+# Create a new group
+my $group = RT::Group->new($RT::SystemUser);
+$group->CreateUserDefinedGroup(Name => 'ACLTest'.$$);
+ok($group->Id, "Created a new group Ok");
+# Grant a group the right to modify tickets in a queue
+ok(my ($gv,$gm) = $group->PrincipalObj->GrantRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"Grant succeeed - $gm");
+# Add the user to the group
+ok( my ($aid, $amsg) = $group->AddMember($new_user->PrincipalId), "Added the member to the group");
+ok ($aid, "Member added to group: $amsg");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can modify the ticket with group membership");
+
+
+# Remove the user from the group
+ok( my ($did, $dmsg) = $group->DeleteMember($new_user->PrincipalId), "Deleted the member from the group");
+ok ($did,"Deleted the group member: $dmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+my $q_as_system = RT::Queue->new($RT::SystemUser);
+$q_as_system->Load(1);
+ok($q_as_system->Id, "Loaded the first queue");
+
+# Create a ticket in the queue
+my $new_tick2 = RT::Ticket->new($RT::SystemUser);
+my ($tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id);
+ok($tick2id, "Created ticket: $tick2id");
+is($new_tick2->QueueObj->id, $q_as_system->Id, "Created a new ticket in queue 1");
+
+
+# make sure that the user can't do this without subgroup membership
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# Create a subgroup
+my $subgroup = RT::Group->new($RT::SystemUser);
+$subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest',$$);
+ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok");
+#Add the subgroup as a subgroup of the group
+my ($said, $samsg) = $group->AddMember($subgroup->PrincipalId);
+ok ($said, "Added the subgroup as a member of the group");
+# Add the user to a subgroup of the group
+
+my ($usaid, $usamsg) = $subgroup->AddMember($new_user->PrincipalId);
+ok($usaid,"Added the user ".$new_user->Id."to the subgroup");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket with subgroup membership");
+
+# {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
+
+my ($id, $msg);
+($id, $msg) = $group->SetDisabled(1);
+ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$group->Id. " is disabled");
+ ($id, $msg) = $group->SetDisabled(0);
+ok($id,$msg);
+# Test what happens when we disable the group the user is a member of directly
+
+($id, $msg) = $subgroup->SetDisabled(1);
+ ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$subgroup->Id. " is disabled");
+ ($id, $msg) = $subgroup->SetDisabled(0);
+ ok ($id,$msg);
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
+
+# }}}
+
+
+my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId);
+ok($usrid,"removed the user from the group - $usrmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+#revoke the right to modify tickets in a queue
+ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"revoke succeeed - $gm");
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $q_as_system, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $q_as_system, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
+
+# Before we start Make sure the user does not have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without it being granted");
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $RT::System, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Make sure the user can't modify the ticket before they're added as a watcher
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without being an admincc");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok ($new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can modify tickets in the queue as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj being only a ticket admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+
+# Grant "privileged users" the system right to create users
+# Create a privileged user.
+# have that user create another user
+# Revoke the right for privileged users to create users
+# have the privileged user try to create another user and fail the ACL check
+
+=end testing
+
+=cut
+
+# }}}
+
+
+# {{{ sub HasRight
+
+=head2 HasRight
+
+Shim around PrincipalObj->HasRight. See RT::Principal
+
+=cut
+
+sub HasRight {
+
+ my $self = shift;
+ return $self->PrincipalObj->HasRight(@_);
+}
+
+# }}}
+
+# {{{ sub CurrentUserCanModify
+
+=head2 CurrentUserCanModify RIGHT
+
+If the user has rights for this object, either because
+he has 'AdminUsers' or (if he\'s trying to edit himself and the right isn\'t an
+admin right) 'ModifySelf', return 1. otherwise, return undef.
+
+=cut
+
+sub CurrentUserCanModify {
+ my $self = shift;
+ my $right = shift;
+
+ if ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
+ return (1);
+ }
+
+ #If the field is marked as an "administrators only" field,
+ # don\'t let the user touch it.
+ elsif ( $self->_Accessible( $right, 'admin' ) ) {
+ return (undef);
+ }
+
+ #If the current user is trying to modify themselves
+ elsif ( ( $self->id == $self->CurrentUser->id )
+ and ( $self->CurrentUser->HasRight(Right => 'ModifySelf', Object => $RT::System) ) )
+ {
+ return (1);
+ }
+
+ #If we don\'t have a good reason to grant them rights to modify
+ # by now, they lose
+ else {
+ return (undef);
+ }
+
+}
+
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
+=head2 CurrentUserHasRight
+
+Takes a single argument. returns 1 if $Self->CurrentUser
+has the requested right. returns undef otherwise
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ return ( $self->CurrentUser->HasRight(Right => $right, Object => $RT::System) );
+}
+
+sub _PrefName {
+ my $name = shift;
+ if (ref $name) {
+ $name = ref ($name).'-'.$name->Id;
+ }
+
+ return 'Pref-'.$name;
+}
+
+# {{{ sub Preferences
+
+=head2 Preferences NAME/OBJ DEFAULT
+
+ Obtain user preferences associated with given object or name.
+ Returns DEFAULT if no preferences found. If DEFAULT is a hashref,
+ override the entries with user preferences.
+
+=cut
+
+sub Preferences {
+ my $self = shift;
+ my $name = _PrefName (shift);
+ my $default = shift;
+
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
+
+ my $content = $attr->Id ? $attr->Content : undef;
+ if (ref ($content) eq 'HASH') {
+ if (ref ($default) eq 'HASH') {
+ for (keys %$default) {
+ exists $content->{$_} or $content->{$_} = $default->{$_};
+ }
+ }
+ elsif (defined $default) {
+ $RT::Logger->error("Preferences $name for user".$self->Id." is hash but default is not");
+ }
+ return $content;
+ }
+ else {
+ return defined $content ? $content : $default;
+ }
+}
+
+# }}}
+
+# {{{ sub SetPreferences
+
+=head2 SetPreferences NAME/OBJ VALUE
+
+ Set user preferences associated with given object or name.
+
+=cut
+
+sub SetPreferences {
+ my $self = shift;
+ my $name = _PrefName (shift);
+ my $value = shift;
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
+ if ($attr->Id) {
+ return $attr->SetContent ($value);
+ }
+ else {
+ return $self->AddAttribute ( Name => $name, Content => $value );
+ }
+}
+
+# }}}
+
+
+=head2 WatchedQueues ROLE_LIST
+
+Returns a RT::Queues object containing every queue watched by the user.
+
+Takes a list of roles which is some subset of ('Cc', 'AdminCc'). Defaults to:
+
+$user->WatchedQueues('Cc', 'AdminCc');
+
+=cut
+
+sub WatchedQueues {
+
+ my $self = shift;
+ my @roles = @_ || ('Cc', 'AdminCc');
+
+ $RT::Logger->debug('WatcheQueues got user ' . $self->Name);
+
+ my $watched_queues = RT::Queues->new($self->CurrentUser);
+
+ my $group_alias = $watched_queues->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Groups',
+ FIELD2 => 'Instance',
+ );
+
+ $watched_queues->Limit(
+ ALIAS => $group_alias,
+ FIELD => 'Domain',
+ VALUE => 'RT::Queue-Role',
+ ENTRYAGGREGATOR => 'AND',
+ );
+ if (grep { $_ eq 'Cc' } @roles) {
+ $watched_queues->Limit(
+ SUBCLAUSE => 'LimitToWatchers',
+ ALIAS => $group_alias,
+ FIELD => 'Type',
+ VALUE => 'Cc',
+ ENTRYAGGREGATOR => 'OR',
+ );
+ }
+ if (grep { $_ eq 'AdminCc' } @roles) {
+ $watched_queues->Limit(
+ SUBCLAUSE => 'LimitToWatchers',
+ ALIAS => $group_alias,
+ FIELD => 'Type',
+ VALUE => 'AdminCc',
+ ENTRYAGGREGATOR => 'OR',
+ );
+ }
+
+ my $queues_alias = $watched_queues->Join(
+ ALIAS1 => $group_alias,
+ FIELD1 => 'id',
+ TABLE2 => 'CachedGroupMembers',
+ FIELD2 => 'GroupId',
+ );
+ $watched_queues->Limit(
+ ALIAS => $queues_alias,
+ FIELD => 'MemberId',
+ VALUE => $self->PrincipalId,
+ );
+
+ $RT::Logger->debug("WatchedQueues got " . $watched_queues->Count . " queues");
+
+ return $watched_queues;
+
+}
+
+
+# {{{ sub _CleanupInvalidDelegations
+
+=head2 _CleanupInvalidDelegations { InsideTransaction => undef }
+
+Revokes all ACE entries delegated by this user which are inconsistent
+with their current delegation rights. Does not perform permission
+checks. Should only ever be called from inside the RT library.
+
+If called from inside a transaction, specify a true value for the
+InsideTransaction parameter.
+
+Returns a true value if the deletion succeeded; returns a false value
+and logs an internal error if the deletion fails (should not happen).
+
+=cut
+
+# XXX Currently there is a _CleanupInvalidDelegations method in both
+# RT::User and RT::Group. If the recursive cleanup call for groups is
+# ever unrolled and merged, this code will probably want to be
+# factored out into RT::Principal.
+
+sub _CleanupInvalidDelegations {
+ my $self = shift;
+ my %args = ( InsideTransaction => undef,
+ @_ );
+
+ unless ( $self->Id ) {
+ $RT::Logger->warning("User not loaded.");
+ return (undef);
+ }
+
+ my $in_trans = $args{InsideTransaction};
+
+ return(1) if ($self->HasRight(Right => 'DelegateRights',
+ Object => $RT::System));
+
+ # Look up all delegation rights currently posessed by this user.
+ my $deleg_acl = RT::ACL->new($RT::SystemUser);
+ $deleg_acl->LimitToPrincipal(Type => 'User',
+ Id => $self->PrincipalId,
+ IncludeGroupMembership => 1);
+ $deleg_acl->Limit( FIELD => 'RightName',
+ OPERATOR => '=',
+ VALUE => 'DelegateRights' );
+ my @allowed_deleg_objects = map {$_->Object()}
+ @{$deleg_acl->ItemsArrayRef()};
+
+ # Look up all rights delegated by this principal which are
+ # inconsistent with the allowed delegation objects.
+ my $acl_to_del = RT::ACL->new($RT::SystemUser);
+ $acl_to_del->DelegatedBy(Id => $self->Id);
+ foreach (@allowed_deleg_objects) {
+ $acl_to_del->LimitNotObject($_);
+ }
+
+ # Delete all disallowed delegations
+ while ( my $ace = $acl_to_del->Next() ) {
+ my $ret = $ace->_Delete(InsideTransaction => 1);
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
+ return (undef);
+ }
+ }
+
+ $RT::Handle->Commit() unless $in_trans;
+ return (1);
+}
+
+# }}}
+
+# {{{ sub _Set
+
+sub _Set {
+ my $self = shift;
+
+ my %args = (
+ Field => undef,
+ Value => undef,
+ TransactionType => 'Set',
+ RecordTransaction => 1,
+ @_
+ );
+
+ # Nobody is allowed to futz with RT_System or Nobody
+
+ if ( ($self->Id == $RT::SystemUser->Id ) ||
+ ($self->Id == $RT::Nobody->Id)) {
+ return ( 0, $self->loc("Can not modify system users") );
+ }
+ unless ( $self->CurrentUserCanModify( $args{'Field'} ) ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my $Old = $self->SUPER::_Value("$args{'Field'}");
+
+ my ($ret, $msg) = $self->SUPER::_Set( Field => $args{'Field'},
+ Value => $args{'Value'} );
+
+ #If we can't actually set the field to the value, don't record
+ # a transaction. instead, get out of here.
+ if ( $ret == 0 ) { return ( 0, $msg ); }
+
+ if ( $args{'RecordTransaction'} == 1 ) {
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => $args{'TransactionType'},
+ Field => $args{'Field'},
+ NewValue => $args{'Value'},
+ OldValue => $Old,
+ TimeTaken => $args{'TimeTaken'},
+ );
+ return ( $Trans, scalar $TransObj->BriefDescription );
+ }
+ else {
+ return ( $ret, $msg );
+ }
+}
+
+# }}}
+
+# {{{ sub _Value
+
+=head2 _Value
+
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
+
+=cut
+
+sub _Value {
+
+ my $self = shift;
+ my $field = shift;
+
+ #If the current user doesn't have ACLs, don't let em at it.
+
+ my @PublicFields = qw( Name EmailAddress Organization Disabled
+ RealName NickName Gecos ExternalAuthId
+ AuthSystem ExternalContactInfoId
+ ContactInfoSystem );
+
+ #if the field is public, return it.
+ if ( $self->_Accessible( $field, 'public' ) ) {
+ return ( $self->SUPER::_Value($field) );
+
+ }
+
+ #If the user wants to see their own values, let them
+ # TODO figure ouyt a better way to deal with this
+ elsif ( $self->CurrentUser->Id == $self->Id ) {
+ return ( $self->SUPER::_Value($field) );
+ }
+
+ #If the user has the admin users right, return the field
+ elsif ( $self->CurrentUser->HasRight(Right =>'AdminUsers', Object => $RT::System) ) {
+ return ( $self->SUPER::_Value($field) );
+ }
+ else {
+ return (undef);
+ }
+
+}
+
+# }}}
+
+sub BasicColumns {
+ (
+ [ Name => 'User Id' ],
+ [ EmailAddress => 'Email' ],
+ [ RealName => 'Name' ],
+ [ Organization => 'Organization' ],
+ );
+}
+
+1;
+
+
diff --git a/rt/lib/RT/Users.pm b/rt/lib/RT/Users.pm
new file mode 100755
index 0000000..7b71c56
--- /dev/null
+++ b/rt/lib/RT/Users.pm
@@ -0,0 +1,139 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+
+
+=head1 NAME
+
+ RT::Users -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::Users
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::Users;
+
+use RT::SearchBuilder;
+use RT::User;
+
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Users';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new RT::User item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::User->new($self->CurrentUser));
+}
+
+ eval "require RT::Users_Overlay";
+ if ($@ && $@ !~ qr{^Can't locate RT/Users_Overlay.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Users_Vendor";
+ if ($@ && $@ !~ qr{^Can't locate RT/Users_Vendor.pm}) {
+ die $@;
+ };
+
+ eval "require RT::Users_Local";
+ if ($@ && $@ !~ qr{^Can't locate RT/Users_Local.pm}) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::Users_Overlay, RT::Users_Vendor, RT::Users_Local
+
+=cut
+
+
+1;
diff --git a/rt/lib/RT/Users_Overlay.pm b/rt/lib/RT/Users_Overlay.pm
new file mode 100644
index 0000000..809fa67
--- /dev/null
+++ b/rt/lib/RT/Users_Overlay.pm
@@ -0,0 +1,677 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+=head1 NAME
+
+ RT::Users - Collection of RT::User objects
+
+=head1 SYNOPSIS
+
+ use RT::Users;
+
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=begin testing
+
+ok(require RT::Users);
+
+=end testing
+
+=cut
+
+
+package RT::Users;
+
+use strict;
+no warnings qw(redefine);
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'Users';
+ $self->{'primary_key'} = 'id';
+
+
+
+ my @result = $self->SUPER::_Init(@_);
+ # By default, order by name
+ $self->OrderBy( ALIAS => 'main',
+ FIELD => 'Name',
+ ORDER => 'ASC' );
+
+ $self->{'princalias'} = $self->NewAlias('Principals');
+
+ # XXX: should be generalized
+ $self->Join( ALIAS1 => 'main',
+ FIELD1 => 'id',
+ ALIAS2 => $self->{'princalias'},
+ FIELD2 => 'id' );
+ $self->Limit( ALIAS => $self->{'princalias'},
+ FIELD => 'PrincipalType',
+ VALUE => 'User',
+ );
+
+ return (@result);
+}
+
+# }}}
+
+=head2 PrincipalsAlias
+
+Returns the string that represents this Users object's primary "Principals" alias.
+
+=cut
+
+# XXX: should be generalized
+sub PrincipalsAlias {
+ my $self = shift;
+ return($self->{'princalias'});
+
+}
+
+
+# {{{ sub _DoSearch
+
+=head2 _DoSearch
+
+ A subclass of DBIx::SearchBuilder::_DoSearch that makes sure that _Disabled rows never get seen unless
+we're explicitly trying to see them.
+
+=cut
+
+sub _DoSearch {
+ my $self = shift;
+
+ #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
+ unless ( $self->{'find_disabled_rows'} ) {
+ $self->LimitToEnabled();
+ }
+ return ( $self->SUPER::_DoSearch(@_) );
+
+}
+
+# }}}
+# {{{ sub LimitToEnabled
+
+=head2 LimitToEnabled
+
+Only find items that haven\'t been disabled
+
+=cut
+
+# XXX: should be generalized
+sub LimitToEnabled {
+ my $self = shift;
+
+ $self->Limit( ALIAS => $self->PrincipalsAlias,
+ FIELD => 'Disabled',
+ VALUE => '0',
+ OPERATOR => '=' );
+}
+
+# }}}
+
+# {{{ LimitToEmail
+
+=head2 LimitToEmail
+
+Takes one argument. an email address. limits the returned set to
+that email address
+
+=cut
+
+sub LimitToEmail {
+ my $self = shift;
+ my $addr = shift;
+ $self->Limit( FIELD => 'EmailAddress', VALUE => "$addr" );
+}
+
+# }}}
+
+# {{{ MemberOfGroup
+
+=head2 MemberOfGroup PRINCIPAL_ID
+
+takes one argument, a group's principal id. Limits the returned set
+to members of a given group
+
+=cut
+
+sub MemberOfGroup {
+ my $self = shift;
+ my $group = shift;
+
+ return $self->loc("No group specified") if ( !defined $group );
+
+ my $groupalias = $self->NewAlias('CachedGroupMembers');
+
+ # Join the principal to the groups table
+ $self->Join( ALIAS1 => $self->PrincipalsAlias,
+ FIELD1 => 'id',
+ ALIAS2 => $groupalias,
+ FIELD2 => 'MemberId' );
+
+ $self->Limit( ALIAS => "$groupalias",
+ FIELD => 'GroupId',
+ VALUE => "$group",
+ OPERATOR => "=" );
+}
+
+# }}}
+
+# {{{ LimitToPrivileged
+
+=head2 LimitToPrivileged
+
+Limits to users who can be made members of ACLs and groups
+
+=cut
+
+sub LimitToPrivileged {
+ my $self = shift;
+
+ my $priv = RT::Group->new( $self->CurrentUser );
+ $priv->LoadSystemInternalGroup('Privileged');
+ unless ( $priv->Id ) {
+ $RT::Logger->crit("Couldn't find a privileged users group");
+ }
+ $self->MemberOfGroup( $priv->PrincipalId );
+}
+
+# }}}
+
+# {{{ WhoHaveRight
+
+=head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
+
+=begin testing
+
+ok(my $users = RT::Users->new($RT::SystemUser));
+$users->WhoHaveRight(Object =>$RT::System, Right =>'SuperUser');
+ok($users->Count == 1, "There is one privileged superuser - Found ". $users->Count );
+# TODO: this wants more testing
+
+my $RTxUser = RT::User->new($RT::SystemUser);
+($id, $msg) = $RTxUser->Create( Name => 'RTxUser', Comments => "RTx extension user", Privileged => 1);
+ok ($id,$msg);
+
+my $group = RT::Group->new($RT::SystemUser);
+$group->LoadACLEquivalenceGroup($RTxUser->PrincipalObj);
+
+my $RTxSysObj = {};
+bless $RTxSysObj, 'RTx::System';
+*RTx::System::Id = sub { 1; };
+*RTx::System::id = *RTx::System::Id;
+my $ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System', ObjectId => 1 );
+ok ($id, "ACL for RTxSysObj created");
+
+my $RTxObj = {};
+bless $RTxObj, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 4; };
+*RTx::System::Record::id = *RTx::System::Record::Id;
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxSysObj);
+is($users->Count, 1, "RTxUserRight found for RTxSysObj");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj);
+is($users->Count, 0, "RTxUserRight not found for RTxObj");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
+is($users->Count, 1, "RTxUserRight found for RTxObj using EquivObjects");
+
+$ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
+ok ($id, "ACL for RTxObj created");
+
+my $RTxObj2 = {};
+bless $RTxObj2, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 5; };
+*RTx::System::Record::id = sub { 5; };
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2);
+is($users->Count, 1, "RTxUserRight found for RTxObj2");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
+is($users->Count, 1, "RTxUserRight found for RTxObj2");
+
+
+=end testing
+
+find all users who the right Right for this group, either individually
+or as members of groups
+
+If passed a queue object, with no id, it will find users who have that right for _any_ queue
+
+=cut
+
+# XXX: should be generalized
+sub _JoinGroupMembers
+{
+ my $self = shift;
+ my %args = (
+ IncludeSubgroupMembers => 1,
+ @_
+ );
+
+ my $principals = $self->PrincipalsAlias;
+
+ # The cachedgroupmembers table is used for unrolling group memberships
+ # to allow fast lookups. if we bind to CachedGroupMembers, we'll find
+ # all members of groups recursively. if we don't we'll find only 'direct'
+ # members of the group in question
+ my $group_members;
+ if ( $args{'IncludeSubgroupMembers'} ) {
+ $group_members = $self->NewAlias('CachedGroupMembers');
+ }
+ else {
+ $group_members = $self->NewAlias('GroupMembers');
+ }
+
+ $self->Join(
+ ALIAS1 => $group_members,
+ FIELD1 => 'MemberId',
+ ALIAS2 => $principals,
+ FIELD2 => 'id'
+ );
+
+ return $group_members;
+}
+
+# XXX: should be generalized
+sub _JoinGroups
+{
+ my $self = shift;
+ my %args = (@_);
+
+ my $group_members = $self->_JoinGroupMembers( %args );
+ my $groups = $self->NewAlias('Groups');
+ $self->Join(
+ ALIAS1 => $groups,
+ FIELD1 => 'id',
+ ALIAS2 => $group_members,
+ FIELD2 => 'GroupId'
+ );
+
+ return $groups;
+}
+
+# XXX: should be generalized
+sub _JoinACL
+{
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ IncludeSuperusers => undef,
+ @_,
+ );
+
+ my $acl = $self->NewAlias('ACL');
+ $self->Limit(
+ ALIAS => $acl,
+ FIELD => 'RightName',
+ OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
+ VALUE => $args{Right} || 'NULL',
+ ENTRYAGGREGATOR => 'OR'
+ );
+ if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
+ $self->Limit(
+ ALIAS => $acl,
+ FIELD => 'RightName',
+ OPERATOR => '=',
+ VALUE => 'SuperUser',
+ ENTRYAGGREGATOR => 'OR'
+ );
+ }
+ return $acl;
+}
+
+# XXX: should be generalized
+sub _GetEquivObjects
+{
+ my $self = shift;
+ my %args = (
+ Object => undef,
+ IncludeSystemRights => undef,
+ EquivObjects => [ ],
+ @_
+ );
+ return () unless $args{'Object'};
+
+ my @objects = ($args{'Object'});
+ if ( UNIVERSAL::isa( $args{'Object'}, 'RT::Ticket' ) ) {
+ # If we're looking at ticket rights, we also want to look at the associated queue rights.
+ # this is a little bit hacky, but basically, now that we've done the ticket roles magic,
+ # we load the queue object and ask all the rest of our questions about the queue.
+
+ # XXX: This should be abstracted into object itself
+ if( $args{'Object'}->id ) {
+ push @objects, $args{'Object'}->QueueObj;
+ } else {
+ push @objects, 'RT::Queue';
+ }
+ }
+
+ if( $args{'IncludeSystemRights'} ) {
+ push @objects, 'RT::System';
+ }
+ push @objects, @{ $args{'EquivObjects'} };
+ return grep $_, @objects;
+}
+
+# XXX: should be generalized
+sub WhoHaveRight {
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ IncludeSystemRights => undef,
+ IncludeSuperusers => undef,
+ IncludeSubgroupMembers => 1,
+ EquivObjects => [ ],
+ @_
+ );
+
+ if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
+ $RT::Logger->crit( "WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
+ return (undef);
+ }
+
+ my @from_role = $self->Clone->_WhoHaveRoleRightSplitted( %args );
+
+ my $from_group = $self->Clone;
+ $from_group->WhoHaveGroupRight( %args );
+
+ #XXX: DIRTY HACK
+ use DBIx::SearchBuilder 1.50; #no version on ::Union :(
+ use DBIx::SearchBuilder::Union;
+ my $union = new DBIx::SearchBuilder::Union;
+ $union->add( $_ ) foreach @from_role;
+ $union->add( $from_group );
+ %$self = %$union;
+ bless $self, ref($union);
+
+ return;
+}
+# }}}
+
+# XXX: should be generalized
+sub WhoHaveRoleRight
+{
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ IncludeSystemRights => undef,
+ IncludeSuperusers => undef,
+ IncludeSubgroupMembers => 1,
+ EquivObjects => [ ],
+ @_
+ );
+
+ my $groups = $self->_JoinGroups( %args );
+ my $acl = $self->_JoinACL( %args );
+
+ $self->Limit( ALIAS => $acl,
+ FIELD => 'PrincipalType',
+ VALUE => "$groups.Type",
+ QUOTEVALUE => 0,
+ );
+
+ # no system user
+ $self->Limit( ALIAS => $self->PrincipalsAlias,
+ FIELD => 'id',
+ OPERATOR => '!=',
+ VALUE => $RT::SystemUser->id
+ );
+
+ my @objects = $self->_GetEquivObjects( %args );
+ unless ( @objects ) {
+ unless ( $args{'IncludeSystemRights'} ) {
+ $self->_AddSubClause( WhichObjects => "($acl.ObjectType != 'RT::System')" );
+ }
+ return;
+ }
+
+ my ($groups_clauses, $acl_clauses) = $self->_RoleClauses( $groups, $acl, @objects );
+ $self->_AddSubClause( "WhichObject", "(". join( ' OR ', @$groups_clauses ) .")" );
+ $self->_AddSubClause( "WhichRole", "(". join( ' OR ', @$acl_clauses ) .")" );
+
+ return;
+}
+
+sub _WhoHaveRoleRightSplitted {
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ IncludeSystemRights => undef,
+ IncludeSuperusers => undef,
+ IncludeSubgroupMembers => 1,
+ EquivObjects => [ ],
+ @_
+ );
+
+ my $groups = $self->_JoinGroups( %args );
+ my $acl = $self->_JoinACL( %args );
+
+ $self->Limit( ALIAS => $acl,
+ FIELD => 'PrincipalType',
+ VALUE => "$groups.Type",
+ QUOTEVALUE => 0,
+ );
+
+ # no system user
+ $self->Limit( ALIAS => $self->PrincipalsAlias,
+ FIELD => 'id',
+ OPERATOR => '!=',
+ VALUE => $RT::SystemUser->id
+ );
+
+ my @objects = $self->_GetEquivObjects( %args );
+ unless ( @objects ) {
+ unless ( $args{'IncludeSystemRights'} ) {
+ $self->_AddSubClause( WhichObjects => "($acl.ObjectType != 'RT::System')" );
+ }
+ return $self;
+ }
+
+ my ($groups_clauses, $acl_clauses) = $self->_RoleClauses( $groups, $acl, @objects );
+ $self->_AddSubClause( "WhichRole", "(". join( ' OR ', @$acl_clauses ) .")" );
+
+ my @res;
+ foreach ( @$groups_clauses ) {
+ my $tmp = $self->Clone;
+ $tmp->_AddSubClause( WhichObject => $_ );
+ push @res, $tmp;
+ }
+
+ return @res;
+}
+
+sub _RoleClauses {
+ my $self = shift;
+ my $groups = shift;
+ my $acl = shift;
+ my @objects = @_;
+
+ my @groups_clauses;
+ my @acl_clauses;
+ foreach my $obj ( @objects ) {
+ my $type = ref($obj)? ref($obj): $obj;
+ my $id;
+ $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
+
+ my $role_clause = "$groups.Domain = '$type-Role'";
+ # XXX: Groups.Instance is VARCHAR in DB, we should quote value
+ # if we want mysql 4.0 use indexes here. we MUST convert that
+ # field to integer and drop this quotes.
+ $role_clause .= " AND $groups.Instance = '$id'" if $id;
+ push @groups_clauses, "($role_clause)";
+
+ my $object_clause = "$acl.ObjectType = '$type'";
+ $object_clause .= " AND $acl.ObjectId = $id" if $id;
+ push @acl_clauses, "($object_clause)";
+ }
+ return (\@groups_clauses, \@acl_clauses);
+}
+
+# XXX: should be generalized
+sub _JoinGroupMembersForGroupRights
+{
+ my $self = shift;
+ my %args = (@_);
+ my $group_members = $self->_JoinGroupMembers( %args );
+ $self->Limit( ALIAS => $args{'ACLAlias'},
+ FIELD => 'PrincipalId',
+ VALUE => "$group_members.GroupId",
+ QUOTEVALUE => 0,
+ );
+}
+
+# XXX: should be generalized
+sub WhoHaveGroupRight
+{
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ IncludeSystemRights => undef,
+ IncludeSuperusers => undef,
+ IncludeSubgroupMembers => 1,
+ EquivObjects => [ ],
+ @_
+ );
+
+ # Find only rows where the right granted is
+ # the one we're looking up or _possibly_ superuser
+ my $acl = $self->_JoinACL( %args );
+
+ my ($check_objects) = ('');
+ my @objects = $self->_GetEquivObjects( %args );
+
+ if ( @objects ) {
+ my @object_clauses;
+ foreach my $obj ( @objects ) {
+ my $type = ref($obj)? ref($obj): $obj;
+ my $id;
+ $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
+
+ my $object_clause = "$acl.ObjectType = '$type'";
+ $object_clause .= " AND $acl.ObjectId = $id" if $id;
+ push @object_clauses, "($object_clause)";
+ }
+
+ $check_objects = join ' OR ', @object_clauses;
+ } else {
+ if( !$args{'IncludeSystemRights'} ) {
+ $check_objects = "($acl.ObjectType != 'RT::System')";
+ }
+ }
+ $self->_AddSubClause( "WhichObject", "($check_objects)" );
+
+ $self->_JoinGroupMembersForGroupRights( %args, ACLAlias => $acl );
+ # Find only members of groups that have the right.
+ $self->Limit( ALIAS => $acl,
+ FIELD => 'PrincipalType',
+ VALUE => 'Group',
+ );
+
+ # no system user
+ $self->Limit( ALIAS => $self->PrincipalsAlias,
+ FIELD => 'id',
+ OPERATOR => '!=',
+ VALUE => $RT::SystemUser->id
+ );
+ return;
+}
+
+# {{{ WhoBelongToGroups
+
+=head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1 }
+
+=cut
+
+# XXX: should be generalized
+sub WhoBelongToGroups {
+ my $self = shift;
+ my %args = ( Groups => undef,
+ IncludeSubgroupMembers => 1,
+ @_ );
+
+ # Unprivileged users can't be granted real system rights.
+ # is this really the right thing to be saying?
+ $self->LimitToPrivileged();
+
+ my $group_members = $self->_JoinGroupMembers( %args );
+
+ foreach my $groupid (@{$args{'Groups'}}) {
+ $self->Limit( ALIAS => $group_members,
+ FIELD => 'GroupId',
+ VALUE => $groupid,
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'OR',
+ );
+ }
+}
+# }}}
+
+
+1;
diff --git a/rt/lib/RTx/Statistics.pm b/rt/lib/RTx/Statistics.pm
new file mode 100755
index 0000000..8b9d6e4
--- /dev/null
+++ b/rt/lib/RTx/Statistics.pm
@@ -0,0 +1,239 @@
+package Statistics;
+
+use vars qw(
+$MultiQueueStatus $MultiQueueDateFormat @MultiQueueQueueList $MultiQueueMaxRows $MultiQueueWeekends $MultiQueueLabelDateFormat
+$PerDayStatus $PerDayDateFormat $PerDayQueue $PerDayMaxRows $PerDayWeekends $PerDayLabelDateFormat $PerDayPeriod
+$DayOfWeekQueue
+@OpenStalledQueueList $OpenStalledWeekends
+$TimeToResolveDateFormat $TimeToResolveQueue $TimeToResolveMaxRows $TimeToResolveWeekends $TimeToResolveLabelDateFormat
+$TimeToResolveGraphQueue
+@years @months %monthsMaxDay
+$secsPerDay
+$RestrictAccess
+$GraphWidth $GraphHeight
+);
+
+use Time::Local;
+
+# I couldn't figure out a way to override these in RT_SiteConfig, which would be
+# preferable.
+
+# Width and Height of all graphics
+$GraphWidth=500;
+$GraphHeight=400;
+
+# Initial settings for the CallsMultiQueue stat page
+$MultiQueueStatus = "resolved";
+$MultiQueueDateFormat = "%a %b %d %Y"; # format for dates on Multi Queue report, see "man strftime" for options
+@MultiQueueQueueList = ("General"); # list of queues to start Multi Queue per day reports
+$MultiQueueMaxRows = 10;
+$MultiQueueWeekends = 1;
+$MultiQueueLabelDateFormat = "%a";
+
+# Initial settings for the CallsQueueDay stat page
+$PerDayStatus = "resolved";
+$PerDayDateFormat = "%a %b %d %Y";
+$PerDayQueue = "General";
+$PerDayMaxRows = 10;
+$PerDayWeekends = 1;
+$PerDayLabelDateFormat = "%a";
+$PerDayPeriod = 10;
+
+# Initial settings for the DayOfWeek stat page
+$DayOfWeekQueue = "General";
+
+# Initial settings for the OpenStalled stat page
+@OpenStalledQueueList = ("General");
+$OpenStalledWeekends = 1;
+
+# Initial settings for the TimeToResolve stat page
+$TimeToResolveDateFormat = "%a %b %d";
+$TimeToResolveQueue = "General";
+$TimeToResolveMaxRows = 10;
+$TimeToResolveWeekends = 1;
+$TimeToResolveLabelDateFormat = "%a";
+
+# Initial settings for the TimeToResolve Graph page
+$TimeToResolveGraphQueue = "General";
+
+$secsPerDay = 86400;
+
+# List of years and months to populate drop down lists
+@years =('2010', '2009', '2008', '2007', '2006', '2005', '2004', '2003' ,'2003' ,'2002');
+@months=qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
+%monthsMaxDay = (
+ 0 => 31, # January
+ 1 => 29, # February, allow for leap year
+ 2 => 31, # March
+ 3 => 30, # April
+ 4 => 31, # May
+ 5 => 30, # June
+ 6 => 31, # July
+ 7 => 31, # August
+ 8 => 30, # September
+ 9 => 31, # October
+ 10=> 30, # November
+ 11=> 31 # December
+ );
+
+# Set to one to prevent users without the ShowConfigTab right from seeing Statistics
+$RestrictAccess = 0;
+
+# Variables to control debugging
+my $debugging=0; # set to 1 to enable debugging
+my $debugtext="";
+
+=head2 FormatDate
+
+Returns a string representing the specified date formatted by the specified string
+
+=cut
+sub FormatDate {
+ my $fmt = shift;
+ my $self = shift;
+ return POSIX::strftime($fmt, localtime($self->Unix));
+}
+
+
+=head2 RTDateSetToLocalMidnight
+
+Sets the date to midnight (at the beginning of the day) local time
+Returns the unixtime at midnight.
+
+=cut
+sub RTDateSetToLocalMidnight {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ $self->Unix(timelocal (0,0,0,$mday,$mon,$year,$wday,$yday));
+
+ return ($self->Unix);
+}
+
+=head2 RTDateIsWeekend
+
+Returns 1 if the date is on saturday or sunday
+
+=cut
+sub RTDateIsWeekend {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ return 1 if (($wday==6) || ($wday==0));
+ 0;
+}
+
+=head2 RTDateGetDateWeekday
+
+Returns the localized name of the day specified by date
+
+=cut
+sub RTDateGetDateWeekday {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ return $self->GetWeekday($wday);
+}
+
+=head2 RTDateSubDay
+
+Subtracts 24 hours from the current time
+
+=cut
+
+sub RTDateSubDay {
+ my $self = shift;
+ $self->AddSeconds(0 - $DAY);
+}
+
+=head2 RTDateSubDays $DAYS
+
+Subtracts 24 hours * $DAYS from the current time
+
+=cut
+
+sub RTDateSubDays {
+ my $self = shift;
+ my $days = shift;
+ $self->AddSeconds(0 - ($days * $DAY));
+}
+
+=head2 DebugInit
+
+Creates a text area on the page if debugging is on.
+
+=cut
+
+sub DebugInit {
+ if($debugging) {
+ my $m = shift;
+ $m->print("<TEXTAREA NAME=debugarea COLS=120 ROWS=50>$debugtext</TEXTAREA>\n");
+ }
+}
+
+=head2 DebugLog $logmsg
+
+Adds a message to the debug area
+
+=cut
+
+sub DebugLog {
+ if($debugging) {
+ my $line = shift;
+ $debugtext .= $line;
+ $RT::Logger->debug($line);
+ }
+}
+
+=head2 DebugClear
+
+Clears the current debug string, otherwise it builds from page to page
+
+=cut
+
+sub DebugClear {
+ if($debugging) {
+ $debugtext = undef;
+ }
+}
+
+=head2 DurationAsString
+
+Returns a string representing the specified duration
+
+=cut
+
+sub DurationAsString {
+ my $Duration = shift;
+ my $MINUTE = 60;
+ my $HOUR = $MINUTE*60;
+ my $DAY = $HOUR * 24;
+ my $WEEK = $DAY * 7;
+ my $days = int($Duration / $DAY);
+ $Duration = $Duration % $DAY;
+ my $hours = int($Duration / $HOUR);
+ $hours = sprintf("%02d", $hours);
+ $Duration = $Duration % $HOUR;
+ my $minutes = int($Duration/$MINUTE);
+ $minutes = sprintf("%02d", $minutes);
+ $Duration = $Duration % $MINUTE;
+ my $secs = sprintf("%02d", $Duration);
+
+ if(!$days) {
+ $days = "00";
+ }
+ if(!$hours) {
+ $hours = "00";
+ }
+ if(!$minutes) {
+ $minutes = "00";
+ }
+ if(!$secs) {
+ $secs = "00";
+ }
+ return "$days days $hours:$minutes:$secs";
+}
+
+1;
+
+
diff --git a/rt/lib/RTx/WebCronTool.pm b/rt/lib/RTx/WebCronTool.pm
new file mode 100644
index 0000000..5f086a2
--- /dev/null
+++ b/rt/lib/RTx/WebCronTool.pm
@@ -0,0 +1,41 @@
+package RTx::WebCronTool;
+$RTx::WebCronTool::VERSION = "0.01";
+
+1;
+
+__END__
+
+=head1 NAME
+
+RTx::WebCronTool - Web interface to rt-crontool
+
+=head1 VERSION
+
+This document describes version 0.01 of RTx::WebCronTool, released
+July 11, 2004.
+
+=head1 DESCRIPTION
+
+This RT extension provides a web interface for the built-in F<rt-crontool>
+utility, allowing scheduled processes to be launched remotely.
+
+After installation, log in as superuser, and click on the "Web CronTool" menu
+on the bottom of the navigation pane.
+
+To use it, simply submit the modules and arguments. All progress, error messages
+and debug information will then be displayed online.
+
+=head1 AUTHORS
+
+Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2004 by Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
diff --git a/rt/lib/t/00smoke.t b/rt/lib/t/00smoke.t
new file mode 100644
index 0000000..9e9bf4a
--- /dev/null
+++ b/rt/lib/t/00smoke.t
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+
+use Test::More qw(no_plan);
+
+use RT;
+ok(RT::LoadConfig);
+ok(RT::Init, "Basic initialization and DB connectivity");
+
+use File::Find;
+File::Find::find({wanted => \&wanted}, 'lib/');
+sub wanted { /^*\.pm\z/s && ok(require $_, "Requiring '$_'"); }
+
+
diff --git a/rt/lib/t/01harness.t b/rt/lib/t/01harness.t
new file mode 100644
index 0000000..98c28d2
--- /dev/null
+++ b/rt/lib/t/01harness.t
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+use Test::More qw(no_plan);
+
+use lib "/opt/rt3/lib";
+use RT;
+ok(RT::LoadConfig);
+ok(RT::Init, "Basic initialization and DB connectivity");
+
+my $test = shift @ARGV;
+require $test;
+
diff --git a/rt/lib/t/02regression.t b/rt/lib/t/02regression.t
new file mode 100644
index 0000000..4cc1318
--- /dev/null
+++ b/rt/lib/t/02regression.t
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+
+use Test::More qw(no_plan);
+
+use lib "/opt/rt3/lib";
+use RT;
+ok(RT::LoadConfig);
+ok(RT::Init, "Basic initialization and DB connectivity");
+
+# Create a new queue
+use_ok(RT::Queue);
+my $q = RT::Queue->new($RT::SystemUser);
+
+$q->Load('regression');
+if ($q->id != 0) {
+ die "Regression tests not starting with a clean DB. Bailing";
+}
+
+my ($id, $msg) = $q->Create( Name => 'Regression',
+ Description => 'A regression test queue',
+ CorrespondAddress => 'correspond@a',
+ CommentAddress => 'comment@a');
+
+isnt($id, 0, "Queue was created sucessfully - $msg");
+
+my $q2 = RT::Queue->new($RT::SystemUser);
+
+ok($q2->Load($id));
+is($q2->id, $id, "Sucessfully loaded the queue again");
+is($q2->Name, 'Regression');
+is($q2->Description, 'A regression test queue');
+is($q2->CorrespondAddress, 'correspond@a');
+is($q2->CommentAddress, 'comment@a');
+
+
+use File::Find;
+File::Find::find({wanted => \&wanted_autogen,
+ preprocess => sub {return sort @_}}, 'lib/t/autogen');
+sub wanted_autogen { /^autogen.*\.t\z/s && require $_; }
+
+File::Find::find({wanted => \&wanted_regression,
+ preprocess => sub {return sort @_}}, 'lib/t/regression');
+sub wanted_regression { /^*\.t\z/s && require $_; }
+
+require "/opt/rt3/lib/t/03web.pl";
+require "/opt/rt3/lib/t/04_send_email.pl";
+require "/opt/rt3/lib/t/05cronsupport.pl";
diff --git a/rt/lib/t/03web.pl b/rt/lib/t/03web.pl
new file mode 100644
index 0000000..597ad10
--- /dev/null
+++ b/rt/lib/t/03web.pl
@@ -0,0 +1,170 @@
+#!/usr/bin/perl
+
+use strict;
+use WWW::Mechanize;
+use HTTP::Request::Common;
+use HTTP::Cookies;
+use LWP;
+use Encode;
+
+my $cookie_jar = HTTP::Cookies->new;
+my $agent = WWW::Mechanize->new();
+
+# give the agent a place to stash the cookies
+
+$agent->cookie_jar($cookie_jar);
+
+
+# get the top page
+my $url = "http://localhost".$RT::WebPath."/";
+$agent->get($url);
+
+is ($agent->{'status'}, 200, "Loaded a page");
+
+
+# {{{ test a login
+
+# follow the link marked "Login"
+
+ok($agent->{form}->find_input('user'));
+
+ok($agent->{form}->find_input('pass'));
+ok ($agent->{'content'} =~ /username:/i);
+$agent->field( 'user' => 'root' );
+$agent->field( 'pass' => 'password' );
+# the field isn't named, so we have to click link 0
+$agent->click(0);
+is($agent->{'status'}, 200, "Fetched the page ok");
+ok( $agent->{'content'} =~ /Logout/i, "Found a logout link");
+
+
+
+$agent->get($url."Ticket/Create.html?Queue=1");
+is ($agent->{'status'}, 200, "Loaded Create.html");
+$agent->form(3);
+# Start with a string containing characters in latin1
+my $string = "I18N Web Testing æøå";
+Encode::from_to($string, 'iso-8859-1', 'utf8');
+$agent->field('Subject' => "Foo");
+$agent->field('Content' => $string);
+ok($agent->submit(), "Created new ticket with $string");
+
+ok( $agent->{'content'} =~ qr{$string} , "Found the content");
+
+$agent->get($url."Ticket/Create.html?Queue=1");
+is ($agent->{'status'}, 200, "Loaded Create.html");
+$agent->form(3);
+# Start with a string containing characters in latin1
+my $string = "I18N Web Testing æøå";
+Encode::from_to($string, 'iso-8859-1', 'utf8');
+$agent->field('Subject' => $string);
+$agent->field('Content' => "BAR");
+ok($agent->submit(), "Created new ticket with $string");
+
+ok( $agent->{'content'} =~ qr{$string} , "Found the content");
+
+
+
+# }}}
+
+# {{{ Query Builder tests
+
+my $response = $agent->get($url."Search/Build.html");
+ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
+
+# Parsing TicketSQL
+#
+# Adding items
+
+# set the first value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "aaa");
+$agent->submit();
+
+# set the next value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "bbb");
+$agent->submit();
+
+ok($agent->form_name('BuildQuery'));
+
+# get the query
+my $query = $agent->current_form->find_input("Query")->value;
+# strip whitespace from ends
+$query =~ s/^\s*//g;
+$query =~ s/\s*$//g;
+
+# collapse other whitespace
+$query =~ s/\s+/ /g;
+
+is ($query, "Subject LIKE 'aaa' AND Subject LIKE 'bbb'");
+
+# - new items go one level down
+# - add items at currently selected level
+# - if nothing is selected, add at end, one level down
+#
+# move left
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move left if you're at the top level
+#
+# move right
+# - error if nothing selected
+# - same item should be selected after move
+# - can always move right (no max depth...should there be?)
+#
+# move up
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move up if you're first in the list
+#
+# move down
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move down if you're last in the list
+#
+# toggle
+# - error if nothing selected
+# - change all aggregators in the grouping
+# - don't change any others
+#
+# delete
+# - error if nothing selected
+# - delete currently selected item
+# - delete all children of a grouping
+# - if delete leaves a node with no children, delete that, too
+# - what should be selected?
+#
+# Clear
+# - clears entire query
+# - clears it from the session, too
+
+# }}}
+
+use File::Find;
+find ( \&wanted , 'html/');
+
+sub wanted {
+ -f && /\.html$/ && $_ !~ /Logout.html$/ && test_get($File::Find::name);
+}
+
+sub test_get {
+ my $file = shift;
+
+
+ $file =~ s#^html/##;
+ ok ($agent->get("$url/$file", "GET $url/$file"));
+ is ($agent->{'status'}, 200, "Loaded $file");
+# ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
+ ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file");
+ ok( $agent->{'content'} !~ /System error/i, "Didn't get a Mason compilation error on $file");
+
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/t/04_send_email.pl b/rt/lib/t/04_send_email.pl
new file mode 100644
index 0000000..973d9d2
--- /dev/null
+++ b/rt/lib/t/04_send_email.pl
@@ -0,0 +1,506 @@
+#!/usr/bin/perl -w
+
+use strict;
+use RT::EmailParser;
+use RT::Tickets;
+use RT::Action::SendEmail;
+
+my @_outgoing_messages;
+my @scrips_fired;
+
+#We're not testing acls here.
+my $everyone = RT::Group->new($RT::SystemUser);
+$everyone->LoadSystemInternalGroup('Everyone');
+$everyone->PrincipalObj->GrantRight(Right =>'SuperUser');
+
+
+is (__PACKAGE__, 'main', "We're operating in the main package");
+
+
+{
+no warnings qw/redefine/;
+sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+
+ main::_fired_scrip($self->ScripObj);
+ main::ok(ref($MIME) eq 'MIME::Entity', "hey, look. it's a mime entity");
+}
+
+}
+
+# instrument SendEmail to pass us what it's about to send.
+# create a regular ticket
+
+my $parser = RT::EmailParser->new();
+
+
+# Let's test to make sure a multipart/report is processed correctly
+my $content = `cat /opt/rt3/lib/t/data/multipart-report` || die "couldn't find new content";
+# be as much like the mail gateway as possible.
+use RT::Interface::Email;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Content =~ /The original message was received/, "It's the bounce");
+
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+
+undef @scrips_fired;
+
+
+
+
+$parser->ParseMIMEEntityFromScalar('From: root@localhost
+To: rt@example.com
+Subject: This is a test of new ticket creation as an unknown user
+
+Blah!
+Foob!');
+
+
+use Data::Dumper;
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id, $tid, $msg ) = $ticket->Create(Requestor => ['root@localhost'], Queue => 'general', Subject => 'I18NTest', MIMEObj => $parser->Entity);
+ok ($id,$msg);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+ok ($tick->Subject eq 'I18NTest', "failed to create the new ticket from an unprivileged account");
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+# make sure it sends an autoreply
+# make sure it sends a notification to adminccs
+
+
+# we need to swap out SendMessage to test the new things we care about;
+&utf8_redef_sendmessage;
+
+# create an iso 8859-1 ticket
+@scrips_fired = ();
+
+my $content = `cat /opt/rt3/lib/t/data/new-ticket-from-iso-8859-1` || die "couldn't find new content";
+
+
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+use RT::Interface::Email;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
+
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+# make sure it sends an autoreply
+
+
+# make sure it sends a notification to adminccs
+
+# If we correspond, does it do the right thing to the outbound messages?
+
+$parser->ParseMIMEEntityFromScalar($content);
+my ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+$parser->ParseMIMEEntityFromScalar($content);
+($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+
+
+
+
+# we need to swap out SendMessage to test the new things we care about;
+&iso8859_redef_sendmessage;
+$RT::EmailOutputEncoding = 'iso-8859-1';
+# create an iso 8859-1 ticket
+@scrips_fired = ();
+
+my $content = `cat /opt/rt3/lib/t/data/new-ticket-from-iso-8859-1` || die "couldn't find new content";
+# be as much like the mail gateway as possible.
+use RT::Interface::Email;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
+
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+# make sure it sends an autoreply
+
+
+# make sure it sends a notification to adminccs
+
+
+# If we correspond, does it do the right thing to the outbound messages?
+
+$parser->ParseMIMEEntityFromScalar($content);
+my ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+$parser->ParseMIMEEntityFromScalar($content);
+($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+
+sub _fired_scrip {
+ my $scrip = shift;
+ push @scrips_fired, $scrip;
+}
+
+sub utf8_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval '
+ sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+
+ my $scrip = $self->ScripObj->id;
+ ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
+ main::_fired_scrip($self->ScripObj);
+ $MIME->make_singlepart;
+ main::ok( ref($MIME) eq \'MIME::Entity\',
+ "hey, look. it\'s a mime entity" );
+ main::ok( ref( $MIME->head ) eq \'MIME::Head\',
+ "its mime header is a mime header. yay" );
+ main::ok( $MIME->head->get(\'Content-Type\') =~ /utf-8/,
+ "Its content type is utf-8" );
+ my $message_as_string = $MIME->bodyhandle->as_string();
+ use Encode;
+ $message_as_string = Encode::decode_utf8($message_as_string);
+ main::ok(
+ $message_as_string =~ /H\x{e5}vard/,
+"The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
+
+ }';
+}
+
+sub iso8859_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval '
+ sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+
+ my $scrip = $self->ScripObj->id;
+ ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
+ main::_fired_scrip($self->ScripObj);
+ $MIME->make_singlepart;
+ main::ok( ref($MIME) eq \'MIME::Entity\',
+ "hey, look. it\'s a mime entity" );
+ main::ok( ref( $MIME->head ) eq \'MIME::Head\',
+ "its mime header is a mime header. yay" );
+ main::ok( $MIME->head->get(\'Content-Type\') =~ /iso-8859-1/,
+ "Its content type is iso-8859-1 - " . $MIME->head->get("Content-Type") );
+ my $message_as_string = $MIME->bodyhandle->as_string();
+ use Encode;
+ $message_as_string = Encode::decode("iso-8859-1",$message_as_string);
+ main::ok(
+ $message_as_string =~ /H\x{e5}vard/, "The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
+
+ }';
+}
+
+# {{{ test a multipart alternative containing a text-html part with an umlaut
+
+my $content = `cat /opt/rt3/lib/t/data/multipart-alternative-with-umlaut` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&umlauts_redef_sendmessage;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Content =~ /causes Error/, "We recorded the content right as text-plain");
+is ($tick->Transactions->First->Attachments->Count , 3 , "Has three attachments, presumably a text-plain, a text-html and a multipart alternative");
+
+sub umlauts_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage { }';
+}
+
+# }}}
+
+# {{{ test a text-html message with an umlaut
+
+my $content = `cat /opt/rt3/lib/t/data/text-html-with-umlaut` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_html_umlauts_redef_sendmessage;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Attachments->First->Content =~ /causes Error/, "We recorded the content as containing 'causes error'");
+ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/html/, "We recorded the content as text/html");
+ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-html and a multipart alternative");
+
+sub text_html_umlauts_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ use Data::Dumper;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ ok (is $MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
+ is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
+ is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
+ is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
+ }';
+}
+
+# }}}
+
+# {{{ test a text-html message with russian characters
+
+my $content = `cat /opt/rt3/lib/t/data/text-html-in-russian` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_html_russian_redef_sendmessage;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/html/, "We recorded the content right as text-html");
+ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-html and a multipart alternative");
+
+sub text_html_russian_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ use Data::Dumper;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ ok (is $MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
+ is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
+ is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
+ is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
+ my $content_1251;
+ $content_1251 = $MIME->parts(1)->bodyhandle->as_string();
+ ok ($content_1251 =~ qr{Ó÷eáíûé Öeíòp "ÊÀÄÐÛ ÄÅËÎÂÎÃÎ ÌÈÐÀ" ïpèãëaøaeò ía òpeíèíã:},
+"Content matches drugim in codepage 1251" );
+ }';
+}
+
+# }}}
+
+# {{{ test a message containing a russian subject and NO content type
+
+unshift (@RT::EmailInputEncodings, 'koi8-r');
+$RT::EmailOutputEncoding = 'koi8-r';
+my $content = `cat /opt/rt3/lib/t/data/russian-subject-no-content-type` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_plain_russian_redef_sendmessage;
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/plain/, "We recorded the content type right");
+ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-plain");
+is ($tick->Subject, "\x{442}\x{435}\x{441}\x{442} \x{442}\x{435}\x{441}\x{442}", "Recorded the subject right");
+sub text_plain_russian_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ is ($MIME->head->mime_type , "text/plain", "The only part is text/plain ");
+ my $subject = $MIME->head->get("subject");
+ chomp($subject);
+ #is( $subject , /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly");
+ };
+ ';
+}
+
+shift @RT::EmailInputEncodings;
+$RT::EmailOutputEncoding = 'utf-8';
+# }}}
+
+
+# {{{ test a message containing a nested RFC 822 message
+
+my $content = `cat /opt/rt3/lib/t/data/nested-rfc-822` || die "couldn't find new content";
+ok ($content, "Loaded nested-rfc-822 to test");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_plain_nested_redef_sendmessage;
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+is ($tick->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?");
+ok ($tick->Transactions->First->Attachments->First->ContentType =~ /multipart\/mixed/, "We recorded the content type right");
+is ($tick->Transactions->First->Attachments->Count , 5 , "Has one attachment, presumably a text-plain and a message RFC 822 and another plain");
+sub text_plain_nested_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart");
+ my $subject = $MIME->head->get("subject");
+ $subject = MIME::Base64::decode_base64( $subject);
+ chomp($subject);
+ # TODO, why does this test fail
+ #ok($subject =~ qr{Niv\x{e5}er}, "The subject matches the word - $subject");
+ 1;
+ }';
+}
+
+# }}}
+
+
+# {{{ test a multipart alternative containing a uuencoded mesage generated by lotus notes
+
+my $content = `cat /opt/rt3/lib/t/data/notes-uuencoded` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&notes_redef_sendmessage;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Content =~ /from Lotus Notes/, "We recorded the content right");
+is ($tick->Transactions->First->Attachments->Count , 3 , "Has three attachments");
+
+sub notes_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage { }';
+}
+
+# }}}
+
+# {{{ test a multipart that crashes the file-based mime-parser works
+
+my $content = `cat /opt/rt3/lib/t/data/crashes-file-based-parser` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&crashes_redef_sendmessage;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok ($tick->Transactions->First->Content =~ /FYI/, "We recorded the content right");
+is ($tick->Transactions->First->Attachments->Count , 5 , "Has three attachments");
+
+sub crashes_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage { }';
+}
+
+
+
+# }}}
+
+# {{{ test a multi-line RT-Send-CC header
+
+my $content = `cat /opt/rt3/lib/t/data/rt-send-cc` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+my $cc = $tick->Transactions->First->Attachments->First->GetHeader('RT-Send-Cc');
+ok ($cc =~ /test1/, "Found test 1");
+ok ($cc =~ /test2/, "Found test 2");
+ok ($cc =~ /test3/, "Found test 3");
+ok ($cc =~ /test4/, "Found test 4");
+ok ($cc =~ /test5/, "Found test 5");
+
+# }}}
+
+# Don't taint the environment
+$everyone->PrincipalObj->RevokeRight(Right =>'SuperUser');
+1;
diff --git a/rt/lib/t/create_data.pl b/rt/lib/t/create_data.pl
new file mode 100644
index 0000000..35226ea
--- /dev/null
+++ b/rt/lib/t/create_data.pl
@@ -0,0 +1,136 @@
+#!@PERL@ -w
+use strict;
+
+use Test::More qw/no_plan/;
+use Text::Lorem;
+use RT;
+RT::LoadConfig;
+RT::Init;
+
+#### Generate some number of RT accounts. Come up with random
+#### usernames if requested, otherwise use provided ones. Take
+#### $subdomain argument so that we can generate customer accounts,
+#### etc. Group memberships should also be provided.
+
+=head2 create_users
+
+=over 4
+
+This subroutine creates a number of RT users, if they don't already
+exist, and places them in the specified group. It also creates the
+group if it needs to. Returns a ref to a list containing the user
+objects.
+
+If a list of names is specified, users with those names are created.
+Otherwise, it will make names up, checking to be sure that a user with
+the random name does not yet exist. Each user will have an email
+address in "example.com".
+
+Takes a hash of the following arguments:
+number => How many users to create. Default is 1.
+names => A ref to a list of usernames to use. Optional.
+subdomain => The subdomain of example.com which should be used for
+ email addresses.
+group => The name of the group these users should belong to. Creates
+ the group if it does not yet exist.
+privileged => Whether the users should be able to be granted rights.
+ Default is 1.
+attributes => a ref to a list of hashrefs containing the arguments for
+ any unsupported attribute we should add to the user (for example, a
+ user saved search.)
+
+=back
+
+=cut
+
+sub create_users {
+ my %ARGS = (number => 1,
+ subdomain => undef,
+ privileged => 1,
+ @_);
+ my $lorem = Text::Lorem->new();
+ my @users_returned;
+
+ my @usernames;
+ my $anon;
+ if ($ARGS{'users'}) {
+ @usernames = @{$ARGS{'users'}};
+ $anon = 0;
+ } else {
+ @usernames = split(/\s+/, $lorem->words($ARGS{'number'}));
+ $anon = 1;
+ }
+
+ my $domain = 'example.com';
+ $domain = $ARGS{'subdomain'} . ".$domain" if $ARGS{'subdomain'};
+
+ foreach my $user (@usernames) {
+ my $user_obj = RT::User->new($RT::SystemUser);
+ $user_obj->Load($user);
+ if ($user_obj->Id() && !$anon) {
+ # Use this user; assume we know what we're doing. Don't
+ # modify it, other than adding it to any group specified.
+ push(@users_returned, $user_obj);
+ } elsif ($user_obj->Id()) {
+ # Oops. Get a different username and stick it on the back
+ # of the list.
+ append(@users, $lorem->words(1));
+ } else {
+ $user_obj->Create(Name => $user,
+ Password => $user."pass",
+ EmailAddress => $user.'@'.$domain,
+ RealName => "$user ipsum",
+ Privileged => $ARGS{'privileged'},
+ );
+ push(@users_returned, $user_obj);
+ }
+ }
+
+ # Now we have our list of users. Did we have groups to add them
+ # to?
+
+ if ($ARGS{'groups'}) {
+ my @groups = @{$ARGS{'groups'}};
+ foreach my $group (@groups) {
+ my $group_obj = RT::Group->new();
+ $group_obj->LoadUserDefinedGroup($group);
+ unless ($group_obj->Id()) {
+ # Create it.
+ $group_obj->CreateUserDefinedGroup(
+ Name => $group,
+ Description => "lorem defined group $group",
+ );
+ }
+ foreach (@users_returned) {
+ $group_obj->AddMember($_->Id);
+ }
+ }
+ }
+
+ # Do we have attributes to apply to the users?
+ if ($ARGS{'attributes'}) {
+ foreach my $attrib (@{$ARGS{'attributes'}}) {
+ my %attr_args = %{$attrib};
+ foreach (@users_returned) {
+ $_->AddAttribute(%attr_args);
+ }
+ }
+ }
+
+ # Return our list of users.
+ return \@users_returned;
+}
+
+#### Generate any RT groups. These ought to be named, by function.
+#### The group names should be given either as part of user creation,
+#### or as a name with a number of subgroups which should be members.
+
+
+#### Generate some queues. Users/groups who have permissions on
+#### queues need to be specified on this point. Permissions can be
+#### specified by role, e.g. "client" or "staffmember" or "admin" for
+#### each queue. If the queue should have anything special like a
+#### custom field, say so here.
+
+
+#### Generate some tickets and transactions.
diff --git a/rt/lib/t/data/8859-15-message-series/dir b/rt/lib/t/data/8859-15-message-series/dir
new file mode 100644
index 0000000..b9f8ec3
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/dir
@@ -0,0 +1,356 @@
+Return-Path: <rt-users-admin@lists.fsck.com>
+Delivered-To: j@pallas.eruditorum.org
+Received: from pallas.eruditorum.org (localhost [127.0.0.1])
+ by pallas.eruditorum.org (Postfix) with ESMTP
+ id 72E3A111B3; Mon, 26 May 2003 14:50:14 -0400 (EDT)
+Delivered-To: rt-users@pallas.eruditorum.org
+Received: from mail-in-02.arcor-online.net (mail-in-02.arcor-online.net [151.189.21.42])
+ by pallas.eruditorum.org (Postfix) with ESMTP id 15E761118D
+ for <rt-users@lists.fsck.com>; Mon, 26 May 2003 14:49:56 -0400 (EDT)
+Received: from otdial-212-144-012-186.arcor-ip.net (otdial-212-144-011-024.arcor-ip.net [212.144.11.24])
+ by mail-in-02.arcor-online.net (Postfix) with ESMTP
+ id 745EE15E87; Mon, 26 May 2003 20:53:15 +0200 (CEST)
+From: Dirk Pape <pape-rt@inf.fu-berlin.de>
+To: Jesse Vincent <jesse@bestpractical.com>,
+ rt-users <rt-users@lists.fsck.com>
+Subject: Re: [rt-users] [rt-announce] Development Snapshot 3.0.2++
+Message-ID: <2147483647.1053982235@otdial-212-144-011-024.arcor-ip.net>
+In-Reply-To: <2147483647.1053974498@[10.0.255.35]>
+References: <20030523202405.GF23719@fsck.com>
+ <2147483647.1053974498@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="==========2147500486=========="
+Sender: rt-users-admin@lists.fsck.com
+Errors-To: rt-users-admin@lists.fsck.com
+X-BeenThere: rt-users@lists.fsck.com
+X-Mailman-Version: 2.0.12
+Precedence: bulk
+List-Help: <mailto:rt-users-request@lists.fsck.com?subject=help>
+List-Post: <mailto:rt-users@lists.fsck.com>
+List-Subscribe: <http://lists.fsck.com/mailman/listinfo/rt-users>,
+ <mailto:rt-users-request@lists.fsck.com?subject=subscribe>
+List-Id: For users of RT: Request Tracker <rt-users.lists.fsck.com>
+List-Unsubscribe: <http://lists.fsck.com/mailman/listinfo/rt-users>,
+ <mailto:rt-users-request@lists.fsck.com?subject=unsubscribe>
+List-Archive: <http://lists.fsck.com/pipermail/rt-users/>
+Date: Mon, 26 May 2003 20:50:36 +0200
+X-Spam-Status: No, hits=-2.5 required=5.0
+ tests=AWL,IN_REP_TO,KNOWN_MAILING_LIST,QUOTED_EMAIL_TEXT,
+ REFERENCES,REPLY_WITH_QUOTES
+ autolearn=ham version=2.55
+X-Spam-Level:
+X-Spam-Checker-Version: SpamAssassin 2.55 (1.174.2.19-2003-05-19-exp)
+
+--==========2147500486==========
+Content-Type: text/plain; charset=us-ascii; format=flowed
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+Hello,
+
+here is the digest I forgot to attach. And I also forgot to say, that these
+were the only messages after a restart of apache.
+
+The messages in the digest are the copies which I - for testing purpose -
+allways queue into a mailbox just befor it is queued via rt-mailgate into
+the rt-system.
+
+--Am Montag, 26. Mai 2003 18:41 Uhr +0200 schrieb Dirk Pape
+<pape-rt@inf.fu-berlin.de>:
+
+> I attach a digest with mails I send one after another to the rt-system
+> and they get queued into one queue, each as a new ticket.
+
+
+
+
+--==========2147500486==========
+Content-Type: multipart/digest; boundary="==========2147489407=========="
+
+--==========2147489407==========
+Content-Type: message/rfc822; name="test _________"
+
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 27591 invoked by uid 9804); 26 May 2003 18:10:50 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:10:46 +0200
+Received: (Qmail 27575 invoked from network); 26 May 2003 18:10:46 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:10:46 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKYe-0000Yi-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:10:44 +0200
+Received: (qmail 27557 invoked by uid 9804); 26 May 2003 18:10:44 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:10:40 +0200
+Received: (Qmail 27540 invoked from network); 26 May 2003 18:10:40 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:10:40 +0200
+Date: Mon, 26 May 2003 18:11:00 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972660@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [27578] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
+--==========2147489407==========
+Content-Type: message/rfc822; name="test _________"
+
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 27754 invoked by uid 9804); 26 May 2003 18:11:24 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:20 +0200
+Received: (Qmail 27704 invoked from network); 26 May 2003 18:11:19 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:19 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKZA-0000Yy-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:11:16 +0200
+Received: (qmail 27690 invoked by uid 9804); 26 May 2003 18:11:16 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:13 +0200
+Received: (Qmail 27677 invoked from network); 26 May 2003 18:11:13 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:13 +0200
+Date: Mon, 26 May 2003 18:11:32 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972692@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [27711] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
+--==========2147489407==========
+Content-Type: message/rfc822; name="test _________"
+
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 27971 invoked by uid 9804); 26 May 2003 18:12:02 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:52 +0200
+Received: (Qmail 27908 invoked from network); 26 May 2003 18:11:52 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:52 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKZj-0000ZC-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:11:51 +0200
+Received: (qmail 27848 invoked by uid 9804); 26 May 2003 18:11:50 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:46 +0200
+Received: (Qmail 27809 invoked from network); 26 May 2003 18:11:45 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:45 +0200
+Date: Mon, 26 May 2003 18:12:05 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972725@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [27911] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
+--==========2147489407==========
+Content-Type: message/rfc822; name="test _________"
+
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 28283 invoked by uid 9804); 26 May 2003 18:12:39 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:12:36 +0200
+Received: (Qmail 28256 invoked from network); 26 May 2003 18:12:35 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:12:35 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKaQ-0000ZQ-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:12:34 +0200
+Received: (qmail 28236 invoked by uid 9804); 26 May 2003 18:12:34 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:12:30 +0200
+Received: (Qmail 28224 invoked from network); 26 May 2003 18:12:30 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:12:30 +0200
+Date: Mon, 26 May 2003 18:12:50 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972770@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [28259] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
+--==========2147489407==========
+Content-Type: message/rfc822; name="test _________"
+
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 28578 invoked by uid 9804); 26 May 2003 18:13:20 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:13:15 +0200
+Received: (Qmail 28534 invoked from network); 26 May 2003 18:13:14 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:13:14 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKb1-0000Ze-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:13:11 +0200
+Received: (qmail 28516 invoked by uid 9804); 26 May 2003 18:13:11 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:13:08 +0200
+Received: (Qmail 28479 invoked from network); 26 May 2003 18:13:07 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:13:07 +0200
+Date: Mon, 26 May 2003 18:13:27 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972807@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [28540] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
+
+--==========2147489407==========
+Content-Type: message/rfc822; name="test _________"
+
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 29108 invoked by uid 9804); 26 May 2003 18:14:15 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:14:10 +0200
+Received: (Qmail 29066 invoked from network); 26 May 2003 18:14:10 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:14:10 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKbw-0000Zr-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:14:08 +0200
+Received: (qmail 29054 invoked by uid 9804); 26 May 2003 18:14:08 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:14:04 +0200
+Received: (Qmail 29036 invoked from network); 26 May 2003 18:14:04 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:14:04 +0200
+Date: Mon, 26 May 2003 18:14:24 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972864@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [29069] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
+--==========2147489407==========
+Content-Type: message/rfc822; name="test _________"
+
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 29551 invoked by uid 9804); 26 May 2003 18:15:16 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:15:12 +0200
+Received: (Qmail 29521 invoked from network); 26 May 2003 18:15:12 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:15:12 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKcx-0000a4-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:15:11 +0200
+Received: (qmail 29511 invoked by uid 9804); 26 May 2003 18:15:10 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:15:07 +0200
+Received: (Qmail 29465 invoked from network); 26 May 2003 18:15:06 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:15:06 +0200
+Date: Mon, 26 May 2003 18:15:26 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972926@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [29524] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
+
+--==========2147489407==========--
+
+--==========2147500486==========--
+
+_______________________________________________
+rt-users mailing list
+rt-users@lists.fsck.com
+http://lists.fsck.com/mailman/listinfo/rt-users
+
+Have you read the FAQ? The RT FAQ Manager lives at http://fsck.com/rtfm
+
diff --git a/rt/lib/t/data/8859-15-message-series/msg1 b/rt/lib/t/data/8859-15-message-series/msg1
new file mode 100644
index 0000000..cc99c40
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/msg1
@@ -0,0 +1,36 @@
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 27591 invoked by uid 9804); 26 May 2003 18:10:50 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:10:46 +0200
+Received: (Qmail 27575 invoked from network); 26 May 2003 18:10:46 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:10:46 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKYe-0000Yi-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:10:44 +0200
+Received: (qmail 27557 invoked by uid 9804); 26 May 2003 18:10:44 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:10:40 +0200
+Received: (Qmail 27540 invoked from network); 26 May 2003 18:10:40 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:10:40 +0200
+Date: Mon, 26 May 2003 18:11:00 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972660@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [27578] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
diff --git a/rt/lib/t/data/8859-15-message-series/msg2 b/rt/lib/t/data/8859-15-message-series/msg2
new file mode 100644
index 0000000..dc442cf
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/msg2
@@ -0,0 +1,36 @@
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 27754 invoked by uid 9804); 26 May 2003 18:11:24 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:20 +0200
+Received: (Qmail 27704 invoked from network); 26 May 2003 18:11:19 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:19 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKZA-0000Yy-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:11:16 +0200
+Received: (qmail 27690 invoked by uid 9804); 26 May 2003 18:11:16 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:13 +0200
+Received: (Qmail 27677 invoked from network); 26 May 2003 18:11:13 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:13 +0200
+Date: Mon, 26 May 2003 18:11:32 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972692@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [27711] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
diff --git a/rt/lib/t/data/8859-15-message-series/msg3 b/rt/lib/t/data/8859-15-message-series/msg3
new file mode 100644
index 0000000..e23866d
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/msg3
@@ -0,0 +1,35 @@
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 27971 invoked by uid 9804); 26 May 2003 18:12:02 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:52 +0200
+Received: (Qmail 27908 invoked from network); 26 May 2003 18:11:52 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:52 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKZj-0000ZC-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:11:51 +0200
+Received: (qmail 27848 invoked by uid 9804); 26 May 2003 18:11:50 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:11:46 +0200
+Received: (Qmail 27809 invoked from network); 26 May 2003 18:11:45 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:11:45 +0200
+Date: Mon, 26 May 2003 18:12:05 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972725@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [27911] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
diff --git a/rt/lib/t/data/8859-15-message-series/msg4 b/rt/lib/t/data/8859-15-message-series/msg4
new file mode 100644
index 0000000..831695c
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/msg4
@@ -0,0 +1,35 @@
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 28283 invoked by uid 9804); 26 May 2003 18:12:39 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:12:36 +0200
+Received: (Qmail 28256 invoked from network); 26 May 2003 18:12:35 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:12:35 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKaQ-0000ZQ-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:12:34 +0200
+Received: (qmail 28236 invoked by uid 9804); 26 May 2003 18:12:34 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:12:30 +0200
+Received: (Qmail 28224 invoked from network); 26 May 2003 18:12:30 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:12:30 +0200
+Date: Mon, 26 May 2003 18:12:50 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972770@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [28259] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
diff --git a/rt/lib/t/data/8859-15-message-series/msg5 b/rt/lib/t/data/8859-15-message-series/msg5
new file mode 100644
index 0000000..272c93c
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/msg5
@@ -0,0 +1,35 @@
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 28578 invoked by uid 9804); 26 May 2003 18:13:20 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:13:15 +0200
+Received: (Qmail 28534 invoked from network); 26 May 2003 18:13:14 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:13:14 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKb1-0000Ze-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:13:11 +0200
+Received: (qmail 28516 invoked by uid 9804); 26 May 2003 18:13:11 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:13:08 +0200
+Received: (Qmail 28479 invoked from network); 26 May 2003 18:13:07 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:13:07 +0200
+Date: Mon, 26 May 2003 18:13:27 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972807@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [28540] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
diff --git a/rt/lib/t/data/8859-15-message-series/msg6 b/rt/lib/t/data/8859-15-message-series/msg6
new file mode 100644
index 0000000..3ae9d3b
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/msg6
@@ -0,0 +1,35 @@
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 29108 invoked by uid 9804); 26 May 2003 18:14:15 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:14:10 +0200
+Received: (Qmail 29066 invoked from network); 26 May 2003 18:14:10 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:14:10 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKbw-0000Zr-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:14:08 +0200
+Received: (qmail 29054 invoked by uid 9804); 26 May 2003 18:14:08 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:14:04 +0200
+Received: (Qmail 29036 invoked from network); 26 May 2003 18:14:04 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:14:04 +0200
+Date: Mon, 26 May 2003 18:14:24 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972864@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [29069] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
diff --git a/rt/lib/t/data/8859-15-message-series/msg7 b/rt/lib/t/data/8859-15-message-series/msg7
new file mode 100644
index 0000000..6149dd6
--- /dev/null
+++ b/rt/lib/t/data/8859-15-message-series/msg7
@@ -0,0 +1,36 @@
+Return-Path: <pape@inf.fu-berlin.de>
+Delivered-To: pape-rtdoublecheck@mi.fu-berlin.de
+Received: (qmail 29551 invoked by uid 9804); 26 May 2003 18:15:16 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:15:12 +0200
+Received: (Qmail 29521 invoked from network); 26 May 2003 18:15:12 +0200
+Received: From es.inf.fu-berlin.de (160.45.110.22)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:15:12 +0200
+Received: from leibniz ([160.45.40.10] helo=math.fu-berlin.de)
+ by es.inf.fu-berlin.de with smtp (Exim 3.35 #1 (Debian))
+ id 19KKcx-0000a4-00
+ for <staff@tec.mi.fu-berlin.de>; Mon, 26 May 2003 18:15:11 +0200
+Received: (qmail 29511 invoked by uid 9804); 26 May 2003 18:15:10 +0200
+Received: from localhost (HELO math.fu-berlin.de) (127.0.0.1)
+ by localhost with SMTP; 26 May 2003 18:15:07 +0200
+Received: (Qmail 29465 invoked from network); 26 May 2003 18:15:06 +0200
+Received: From eremix.inf.fu-berlin.de (HELO eremix) (160.45.113.36)
+ by leibniz.math.fu-berlin.de with SMTP; 26 May 2003 18:15:06 +0200
+Date: Mon, 26 May 2003 18:15:26 +0200
+From: Dirk Pape <pape@inf.fu-berlin.de>
+To: staff@tec.mi.fu-berlin.de
+Subject: =?ISO-8859-15?Q?test_=E4=F6=FC=DF=C4=D6=DC=DF=A4?=
+Message-ID: <2147483647.1053972926@[10.0.255.35]>
+X-Mailer: Mulberry/3.0.3 (Mac OS X)
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Envelope-Sender: pape@inf.fu-berlin.de
+X-Virus-Scanned: by AMaViS 0.3.12pre7-U23 [29524] (NAI-uvscan@math.fu-berlin.de)
+X-Remote-IP: 160.45.110.22
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-15; FORMAT=flowed
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+test nochmal in anderer Queue
+test =E4=F6=FC=DF=C4=D6=DC=DF=A4
+
diff --git a/rt/lib/t/data/crashes-file-based-parser b/rt/lib/t/data/crashes-file-based-parser
new file mode 100644
index 0000000..da1913e
--- /dev/null
+++ b/rt/lib/t/data/crashes-file-based-parser
@@ -0,0 +1,193 @@
+X-Real-To: <mitya@example.com>
+Received: from [194.87.5.31] (HELO sinbin.d-s.example.com)
+ by cgp.example.com (CommuniGate Pro SMTP 4.0.6/D4)
+ with ESMTP-TLS id 125035761 for mitya@example.com; Thu, 11 Dec 2003 15:17:46 +0300
+Received: (from daemon@localhost)
+ by sinbin.d-s.example.com (8.12.9p1/8.11.6) id hBBCHjN0031595
+ for mitya@example.com; Thu, 11 Dec 2003 15:17:45 +0300 (MSK)
+ (envelope-from noc@rt3.mx.example.com)
+Received: from d-s.example.com by sinbin.d-s.example.com with ESMTP id hBBCHjar031575;
+ (8.12.9p2/D) Thu, 11 Dec 2003 15:17:45 +0300 (MSK)
+X-Real-To: <mitya@example.com>
+Sender: <noc@rt3.mx.example.com> (Network Operation Center)
+To: mitya@example.com
+Date: Thu, 11 Dec 2003 15:17:45 +0300
+Message-ID: <redirect-137509289@d-s.example.com>
+X-Original-Return-Path: <vox19@b92.d-s.example.com>
+Received: from [194.87.0.16] (HELO mail.d-s.example.com)
+ by d-s.example.com (CommuniGate Pro SMTP 4.1.5/D1)
+ with ESMTP id 120757484 for noc@rt3.mx.example.com; Mon, 27 Oct 2003 09:40:53 +0300
+Received: from [194.87.0.22] (HELO moscvax.d-s.example.com)
+ by mail.d-s.example.com (CommuniGate Pro SMTP 4.1.5/D)
+ with ESMTP-TLS id 107945800 for noc@rt3.mx.example.com; Mon, 27 Oct 2003 09:40:53 +0300
+Received: from d-s.example.com (mx.d-s.example.com [194.87.0.32])
+ by moscvax.d-s.example.com (8.12.9/8.12.9) with ESMTP id h9R6erFm062621
+ for <security@d.example.com>; Mon, 27 Oct 2003 09:40:53 +0300 (MSK)
+ (envelope-from vox19@b92.d-s.example.com)
+Received: by d-s.example.com (CommuniGate Pro PIPE 4.1.5/D1)
+ with PIPE id 120757490; Mon, 27 Oct 2003 09:40:53 +0300
+Received: from [194.87.2.108] (HELO b92.d-s.example.com)
+ by d-s.example.com (CommuniGate Pro SMTP 4.1.5/D1)
+ with ESMTP-TLS id 120757480 for security@d.example.com; Mon, 27 Oct 2003 09:40:52 +0300
+Received: from b92.d-s.example.com (localhost [127.0.0.1])
+ by b92.d-s.example.com (8.12.8p1/8.12.3) with ESMTP id h9R6eqIe014669
+ for <security@d.example.com>; Mon, 27 Oct 2003 09:40:52 +0300 (MSK)
+ (envelope-from vox19@b92.d-s.example.com)
+Received: from localhost (localhost [[UNIX: localhost]])
+ by b92.d-s.example.com (8.12.8p1/8.12.3/Submit) id h9R6epst014668
+ for security@d.example.com; Mon, 27 Oct 2003 09:40:51 +0300 (MSK)
+From: "Stanislav" <drstas@d.example.com>
+Subject: Fwd: scanning my ports
+X-Original-Date: Mon, 27 Oct 2003 10:40:51 +0400
+User-Agent: KMail/1.5.4
+X-Original-To: security@d.example.com
+MIME-Version: 1.0
+Content-Type: Multipart/Mixed;
+ boundary="Boundary-00=_z3Ln/tUeUBipHgx"
+X-Original-Message-Id: <200310270940.51758.vox19@d.example.com>
+X-Spam-Checker-Version: SpamAssassin 2.60-jumbo.demos (1.212-2003-09-23-exp)
+X-Spam-Level:
+X-Spam-Status: No, hits=-6.8 required=5.0 tests=BAYES_00,FROM_ENDS_IN_NUMS,
+ HTML_MESSAGE,SUBJECT_RT autolearn=ham version=2.60-jumbo.demos
+X-Spam-Report: -6.8 points, 5.0 required;
+ * -3.0 SUBJECT_RT Tracking system
+ * 1.0 FROM_ENDS_IN_NUMS From: ends in numbers
+ * 0.1 HTML_MESSAGE BODY: HTML included in message
+ * -4.9 BAYES_00 BODY: Bayesian spam probability is 0 to 1%
+ * [score: 0.0000]
+
+
+--Boundary-00=_z3Ln/tUeUBipHgx
+Content-Type: text/plain;
+ charset="koi8-r"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+
+FYI
+
+
+---------- Forwarded Message ----------
+
+Subject: [DEMOS #12148] scanning my ports
+Date: Sunday 26 October 2003 20:19
+From: 1stwizard@isp.example.com
+To: no-reply@d-r.example.com
+
+This transaction appears to have no content
+
+-------------------------------------------------------
+
+
+
+--
+best wishes,
+
+Stanislav A. Mushkat
+http://www.di.example.com
+
+--Boundary-00=_z3Ln/tUeUBipHgx
+Content-Type: text/plain;
+ charset="iso-8859-1";
+ name=" "
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+Somebody at IP 127.0.0.1 scanned my ports.
+--Boundary-00=_z3Ln/tUeUBipHgx
+Content-Type: text/html;
+ charset="iso-8859-1";
+ name=" "
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+<HTML><HEAD>
+<META http-equiv=Content-Type content="text/html; charset=iso-8859-1">
+<META content="IncrediMail 1.0" name=GENERATOR>
+<!--IncrdiXMLRemarkStart>
+<IncrdiX-Info>
+<X-FID>BA285063-5BCE-11D4-AF8D-0050DAC67E11</X-FID>
+<X-FVER>2.0</X-FVER>
+<X-FIT>Letter</X-FIT>
+<X-FCOL>Elegant Paper</X-FCOL>
+<X-FCAT>Stationery</X-FCAT>
+<X-FDIS>Rice Fields</X-FDIS>
+<X-Extensions>SU1CTDEsNDEsgUmBSTAkkcGNgZmVTY0wNCxNhYUoiU0kOMEoTYGBjYEoJDSZnSyFhUksSU1CTDIsMCwsSU1CTDMsMCwsVHlwZVZlcnNpb24sMywxLjAs</X-Extensions>
+<X-BG>8E549F43-079D-11D8-B0F9-00B0D0B65B96</X-BG>
+<X-BGT>repeat</X-BGT>
+<X-BGC>#eff3f7</X-BGC>
+<X-BGPX>left</X-BGPX>
+<X-BGPY>0px</X-BGPY>
+<X-ASN>ANIM3D00-NONE-0000-0000-000000000000</X-ASN>
+<X-ASNF>0</X-ASNF>
+<X-ASH>ANIM3D00-NONE-0000-0000-000000000000</X-ASH>
+<X-ASHF>1</X-ASHF>
+<X-AN>6486DDE0-3EFD-11D4-BA3D-0050DAC68030</X-AN>
+<X-ANF>0</X-ANF>
+<X-AP>6486DDE0-3EFD-11D4-BA3D-0050DAC68030</X-AP>
+<X-APF>1</X-APF>
+<X-AD>C3C52140-4147-11D4-BA3D-0050DAC68030</X-AD>
+<X-ADF>0</X-ADF>
+<X-AUTO>X-ASN,X-ASH,X-AN,X-AP,X-AD</X-AUTO>
+<X-CNT>;</X-CNT>
+</IncrdiX-Info>
+<IncrdiXMLRemarkEnd-->
+</HEAD>
+<BODY style="BACKGROUND-POSITION: left 0px; FONT-SIZE: 12pt; MARGIN: 0px 10px 10px; COLOR: #00005b; BACKGROUND-REPEAT: repeat; FONT-FAMILY: Arial" text=#00005b bgColor=#eff3f7 background=cid:8E549F43-079D-11D8-B0F9-00B0D0B65B96 scroll=yes SIGCOLOR="0" X-ADF="0" X-AD="C3C52140-4147-11D4-BA3D-0050DAC68030" X-APF="1" X-AP="6486DDE0-3EFD-11D4-BA3D-0050DAC68030" X-ANF="0" X-AN="6486DDE0-3EFD-11D4-BA3D-0050DAC68030" X-ASHF="1" X-ASH="ANIM3D00-NONE-0000-0000-000000000000" X-ASNF="0" X-ASN="ANIM3D00-NONE-0000-0000-000000000000" X-FVER="2.0" X-FID="BA285063-5BCE-11D4-AF8D-0050DAC67E11" X-FIT="Letter" X-FCOL="Elegant Paper" X-FCAT="Elegant Paper" X-FDIS="Rice Fields" ORGYPOS="0">
+<TABLE id=INCREDIMAINTABLE cellSpacing=0 cellPadding=2 width="100%" border=0>
+<TBODY>
+<TR>
+<TD id=INCREDITEXTREGION style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-SIZE: 12pt; PADDING-BOTTOM: 0px; CURSOR: auto; PADDING-TOP: 0px" vAlign=top width="100%">
+<DIV>Somebody at IP 127.0.0.1 scanned my ports. </DIV>
+<DIV>&nbsp;</DIV>
+<DIV>&nbsp;</DIV></TD></TR>
+<TR>
+<TD id=INCREDIFOOTER width="100%">
+<TABLE cellSpacing=0 cellPadding=0 width="100%">
+<TBODY>
+<TR>
+<TD width="100%"></TD>
+<TD id=INCREDISOUND vAlign=bottom align=middle></TD>
+<TD id=INCREDIANIM vAlign=bottom align=middle></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></BODY></HTML>
+--Boundary-00=_z3Ln/tUeUBipHgx
+Content-Type: image/jpeg;
+ charset="iso-8859-1";
+ name="BackGrnd.jpg"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline; filename="BackGrnd.jpg"
+
+/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAHgAA/+4AIUFk
+b2JlAGTAAAAAAQMAEAMCAwYAAAHbAAAC1gAABZX/2wCEABALCwsMCxAMDBAX
+Dw0PFxsUEBAUGx8XFxcXFx8eFxoaGhoXHh4jJSclIx4vLzMzLy9AQEBAQEBA
+QEBAQEBAQEABEQ8PERMRFRISFRQRFBEUGhQWFhQaJhoaHBoaJjAjHh4eHiMw
+Ky4nJycuKzU1MDA1NUBAP0BAQEBAQEBAQEBAQP/CABEIAGUAcwMBIgACEQED
+EQH/xACAAAEBAQEAAAAAAAAAAAAAAAAAAQIGAQEBAAAAAAAAAAAAAAAAAAAA
+ARABAAICAwEAAgMAAAAAAAAAAQARIQIxQRIiQDIQMFARAAICAgIBBAIDAQEA
+AAAAAAERACExQVFhcYGRobECEsHhMtHxEgEAAAAAAAAAAAAAAAAAAABQ/9oA
+DAMBAAIRAxEAAADtRZYE1ASghQFgUZoCkKSwLmhcllAEqkSkqFAlhUomoAS3
+IoJqFlDNpFEAQFE1AIVYAWIVKAJRNZpYCwVmmshKACA0CBAUCBYGwf/aAAgB
+AgABBQD8B/yP/9oACAEDAAEFAPz6/or8H//aAAgBAQABBQC2+ZeHjbD+saX6
+hwXeDW1Rg4xLLTa+m7ZiIEsI1MTiHP1dYpvFADiFM1/X6nq9byuwdPPz5oFo
+fWlEMQ9ULKrWq2ppG9Y2J6INQma9lVTRdlUKgHzXXSEECw1SYu5WsGoJPkis
+ZYpx31GvXZQ/JM3VwShzVTsp1EZbBI8LcaUSih86+s2Zl4Wp6+lAZnVsDkjd
+ku5m+lJTdXDG2SHM9M2wKX1YxsaZTTwmoVrYnqsMrM652yjs01K0mtbGAz6Y
+5dpfqNz06qpq5QNjiIjiZtbhtceNuf0jyeqGgu6rXMvI4omPWbPMYzEfMI+a
+xHnFvOP4/9oACAECAgY/AGP/2gAIAQMCBj8AY//aAAgBAQEGPwB72Yucb1Bf
+IhFEaeZ+xRXFQELN+HEUQdjU0Xn4g9gRCQcpw1yajGYsP/kFvUzvjUBWrIMF
+HI2OJQNEAjiEEFdTmfG/MTHq5RFOnpTV3kzCBx7x4YOD1AV5uYJvnqMA0hep
+jfwpYCwC4Bx3q55zeZRBCw9TkoIuHw78RdczSNH2mgqcLpRC+RASAkA3B13m
+cYd5mR84c/yOx4lWtRAZ6mGDhiP9WgXVyhWA+xDgMOWGMsTg/wBTz8SjjXrP
+8hHIlX1MZ6mDzgc/cIV/iyN1GBR0MQMKjnEzvvMz8mUkErKlfqU63iV+IKNH
+7mNZBLFQEpEDeDOV32IVn8WR4caoywqI2p695mbZzNUQIcKfk0bo+0NpCqn7
+CiQiNGXkdQen1DpjGeZ7WNw3pK+I93maCPc16+Zkf6XxMCsFwAkaiIB57vc/
+IAhZ/HqZBBbB0ZokAEOGxsYqBgPp8agQBu4VSMJdqx6SwDsGBrTmAR93uZGX
+6KePowEADAIjoX8gw459CICaW/MLGvodQfkDW71zBxRHtB3j3jC4PMIYoAgK
+NfPMCQNN7jCzvlzXPopzhQvNZY3CRya9ZrEFfRE0iCB5mscZuVYfKmAi94uE
+3Q8qfytQ7xD0svmFcmaxNPI8iMjh3pmF2HbzqeUi+YkiD/MrOl5LmbwPuWVf
+mXpv3hDH8qAjPpiZHXkRnSd6ZhB53mejzKV6US0K9TCCLyCeIhtETX5MsHBG
+JkD/ANiFkMCE2qGoCdZ8Q8AMGpYFqEhdhRIYH3CF3d1M/Mexma+4CwdQ2Ddc
+x0exAlmj04QUQd8QWLB/iB5GxmEg5TENVZqPYzFV8eHAy9T/AEc8a4n3Ov6g
+/VwvE6lpQ4VNysXzhS8esOO8w/rlF/rypjV3B5H1Knr8T//Z
+
+--Boundary-00=_z3Ln/tUeUBipHgx--
+
diff --git a/rt/lib/t/data/lorem-ipsum b/rt/lib/t/data/lorem-ipsum
new file mode 100644
index 0000000..1aceb14
--- /dev/null
+++ b/rt/lib/t/data/lorem-ipsum
@@ -0,0 +1,5 @@
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut
+labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
+nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit
+esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
+culpa qui officia deserunt mollit anim id est laborum.
diff --git a/rt/lib/t/data/multipart-alternative-with-umlaut b/rt/lib/t/data/multipart-alternative-with-umlaut
new file mode 100644
index 0000000..1ad4fe3
--- /dev/null
+++ b/rt/lib/t/data/multipart-alternative-with-umlaut
@@ -0,0 +1,62 @@
+Return-Path: <gst@example.com>
+Delivered-To: j@pallas.eruditorum.org
+Received: from vis.example.com (vis.example.com [212.68.68.251])
+ by pallas.eruditorum.org (Postfix) with SMTP id 59236111C3
+ for <jesse@example.com>; Thu, 12 Jun 2003 02:14:44 -0400 (EDT)
+Received: (qmail 29541 invoked by uid 502); 12 Jun 2003 06:14:42 -0000
+Received: from sivd.example.com (HELO example.com) (192.168.42.1)
+ by 192.168.42.42 with SMTP; 12 Jun 2003 06:14:42 -0000
+Received: received from 172.20.72.174 by odie.example.com; Thu, 12 Jun 2003 08:14:27 +0200
+Received: by mailserver.example.com with Internet Mail Service (5.5.2653.19) id <LJSB7T54>; Thu, 12 Jun 2003 08:14:39 +0200
+Message-ID: <50362EC956CBD411A339009027F6257E013DD495@mailserver.example.com>
+Date: Thu, 12 Jun 2003 08:14:39 +0200
+From: "Stever, Gregor" <gst@example.com>
+MIME-Version: 1.0
+X-Mailer: Internet Mail Service (5.5.2653.19)
+To: "'jesse@example.com'" <jesse@example.com>
+Subject: RE: [rt-users] HTML-encoded mails with umlaute
+Date: Thu, 12 Jun 2003 08:14:39 +0200
+Content-Type: multipart/alternative;
+ boundary="----_=_NextPart_001_01C330A9.E7BDD590"
+X-Spam-Status: No, hits=0.0 required=5.0
+ tests=AWL,HTML_50_60,HTML_MESSAGE,INVALID_DATE
+ version=2.55
+X-Spam-Level:
+X-Spam-Checker-Version: SpamAssassin 2.55 (1.174.2.19-2003-05-19-exp)
+
+------_=_NextPart_001_01C330A9.E7BDD590
+Content-Type: text/plain;
+ charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+Hello,
+
+ist this kind of Messages, that causes rt to crash.=20
+
+Mit freundlichen Gr=FC=DFen
+Gregor Stever ^^causes Error!!
+
+
+------_=_NextPart_001_01C330A9.E7BDD590
+Content-Type: text/html;
+ charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD>
+<META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; charset=3Diso-8859-=
+1">
+
+
+<META content=3D"MSHTML 6.00.2800.1170" name=3DGENERATOR></HEAD>
+<BODY>
+<DIV><FONT face=3DArial><FONT size=3D2>Hello,<BR><BR>ist this kind of Messa=
+ges, that=20
+causes rt to crash.<BR><BR>Mit freundlichen Gr=FC=DFen<BR>Gregor=20
+Stever&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^^causes Error<SPAN=20
+class=3D975501206-12062003>!!</SPAN></FONT></FONT></DIV></BODY></HTML>
+
+
+------_=_NextPart_001_01C330A9.E7BDD590--
+
+
diff --git a/rt/lib/t/data/multipart-report b/rt/lib/t/data/multipart-report
new file mode 100644
index 0000000..538e0c8
--- /dev/null
+++ b/rt/lib/t/data/multipart-report
@@ -0,0 +1,66 @@
+Return-Path: <mailnull@example.com>
+Date: Sat, 23 Aug 2003 00:15:18 +0800 (SGT)
+From: Mail Delivery Subsystem <MAILER-DAEMON@other.example.com>
+Message-Id: <200308221615.CGA36111@mailbox.other.example.com>
+To: support@example.com
+MIME-Version: 1.0
+Content-Type: multipart/report; report-type=delivery-status;
+ boundary="CGA36111.1061568918/mailbox.other.example.com"
+Subject: Returned mail: User unknown
+Auto-Submitted: auto-generated (failure)
+
+This is a MIME-encapsulated message
+
+--CGA36111.1061568918/mailbox.other.example.com
+
+The original message was received at Sat, 23 Aug 2003 00:15:18 +0800 (SGT)
+from mx12.mcis.other.example.com [10.1.1.232]
+
+ ----- The following addresses had permanent delivery errors -----
+<jesmund>
+
+
+--CGA36111.1061568918/mailbox.other.example.com
+Content-Type: message/delivery-status
+
+Reporting-MTA: dns; mailbox.other.example.com
+Arrival-Date: Sat, 23 Aug 2003 00:15:18 +0800 (SGT)
+
+Final-Recipient: RFC822; jesmund@mailbox.other.example.com
+Action: failed
+Status: 5.1.1
+Remote-MTA: DNS; mail.mcis.other.example.com
+Diagnostic-Code: SMTP; 550 5.1.1 <jesmund>... User unknown
+Last-Attempt-Date: Sat, 23 Aug 2003 00:15:18 +0800 (SGT)
+
+--CGA36111.1061568918/mailbox.other.example.com
+Content-Type: message/rfc822
+
+Return-Path: <support@example.com>
+Received: from mx12.other.example.com (mx12.mcis.other.example.com [10.1.1.232])
+ by mailbox.other.example.com (Mirapoint Messaging Server MOS 3.3.3-GR)
+ with ESMTP id CGA36101;
+ Sat, 23 Aug 2003 00:15:17 +0800 (SGT)
+Received: from STATION13 (rhala.dsl.pe.net [64.38.69.104])
+ by mx12.other.example.com (8.12.9/8.12.9) with ESMTP id h7MGFGac020135
+ for <jesmund@other.example.com>; Sat, 23 Aug 2003 00:15:17 +0800
+Message-Id: <200308221615.h7MGFGac020135@mx12.other.example.com>
+From: <support@example.com>
+To: <jesmund@other.example.com>
+Subject: Thank you!
+Date: Fri, 22 Aug 2003 9:15:19 --0700
+X-MailScanner: Found to be clean
+Importance: Normal
+X-Mailer: Microsoft Outlook Express 6.00.2600.0000
+X-MSMail-Priority: Normal
+X-Priority: 3 (Normal)
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="_NextPart_000_05684DA4"
+
+
+
+--_NextPart_000_05684DA4--
+
+--CGA36111.1061568918/mailbox.other.example.com--
+
diff --git a/rt/lib/t/data/nested-mime-sample b/rt/lib/t/data/nested-mime-sample
new file mode 100644
index 0000000..8b85d94
--- /dev/null
+++ b/rt/lib/t/data/nested-mime-sample
@@ -0,0 +1,396 @@
+Return-Path: <Xxxxxx_Yyyyyyy@some.net>
+Delivered-To: jesse@pallas.eruditorum.org
+Received: by pallas.eruditorum.org (Postfix)
+ id B5D3E1123A; Fri, 12 Jul 2002 11:35:27 -0400 (EDT)
+Delivered-To: rt-2.0-bugs@pallas.eruditorum.org
+Received: from postman.some.net (postman.some.net [193.0.0.199])
+ by pallas.eruditorum.org (Postfix) with SMTP id 2736011234
+ for <rt-2.0-bugs@fsck.com>; Fri, 12 Jul 2002 11:35:27 -0400 (EDT)
+Received: (qmail 11615 invoked by uid 0); 12 Jul 2002 15:35:26 -0000
+Received: from x22.some.net (HELO x22.some.net.some.net) (193.0.1.22)
+ by postman.some.net with SMTP; 12 Jul 2002 15:35:26 -0000
+Date: Fri, 12 Jul 2002 17:35:26 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+To: rt-0.0-bugs@fsck.com
+Subject: Example MIME within MIME within MIME message
+Message-ID: <Pine.LNX.4.44.0207121734250.25020-120000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="12654081-192303556-1026488126=:25020"
+X-Spam-Status: No, hits=4.0 required=7.0
+ tests=DOUBLE_CAPSWORD,MIME_NULL_BLOCK,MIME_MISSING_BOUNDARY
+ version=2.31
+Content-Length: 11478
+
+ This message is in MIME format. The first part should be readable text,
+ while the remaining parts are likely unreadable without MIME-aware tools.
+ Send mail to mime@docserver.cac.washington.edu for more info.
+
+--12654081-192303556-1026488126=:25020
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+MIME is fun at times.
+
+
+--
+ Xxxxxx Yyyyyyy SOME
+ Systems/Network Engineer NCC
+ www.some.net - PGP000C8B1B Operations/Security
+
+--12654081-192303556-1026488126=:25020
+Content-Type: MULTIPART/Digest; BOUNDARY="12654081-2102091261-1026488126=:25020"
+Content-ID: <Pine.LNX.4.44.0207121734322.25020@x22.some.net>
+Content-Description: Digest of 2 messages
+
+ This message is in MIME format. The first part should be readable text,
+ while the remaining parts are likely unreadable without MIME-aware tools.
+ Send mail to mime@docserver.cac.washington.edu for more info.
+
+--12654081-2102091261-1026488126=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121734320.25020@x22.some.net>
+Content-Description: first outer message (fwd)
+
+Date: Fri, 12 Jul 2002 17:32:37 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: first outer message
+Message-ID: <Pine.LNX.4.44.0207121732180.25020-120000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="12654081-113777422-1026487957=:25020"
+
+
+--12654081-113777422-1026487957=:25020
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+first outer message
+
+--12654081-113777422-1026487957=:25020
+Content-Type: MULTIPART/Digest; BOUNDARY="12654081-387266385-1026487957=:25020"
+Content-ID: <Pine.LNX.4.44.0207121732222.25020@x22.some.net>
+Content-Description: Digest of 2 messages
+
+--12654081-387266385-1026487957=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121732220.25020@x22.some.net>
+Content-Description: middle message (fwd)
+
+Date: Fri, 12 Jul 2002 17:31:45 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: middle message
+Message-ID: <Pine.LNX.4.44.0207121731190.25020-120000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="12654081-1711788944-1026487905=:25020"
+
+
+--12654081-1711788944-1026487905=:25020
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+This is the first middle message
+
+
+--12654081-1711788944-1026487905=:25020
+Content-Type: MULTIPART/Digest; BOUNDARY="12654081-1221085552-1026487905=:25020"
+Content-ID: <Pine.LNX.4.44.0207121731262.25020@x22.some.net>
+Content-Description: Digest of 2 messages
+
+--12654081-1221085552-1026487905=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731260.25020@x22.some.net>
+Content-Description: This is the inner-most message (fwd)
+
+Date: Fri, 12 Jul 2002 17:30:31 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: This is the inner-most message
+Message-ID: <Pine.LNX.4.44.0207121730070.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+inner-msg
+
+
+
+--12654081-1221085552-1026487905=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731261.25020@x22.some.net>
+Content-Description: another inner message (need two before pine will do the mime-digest thing) (fwd)
+
+Date: Fri, 12 Jul 2002 17:31:12 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: another inner message (need two before pine will do the mime-digest
+ thing)
+Message-ID: <Pine.LNX.4.44.0207121730480.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+again
+
+
+
+--12654081-1221085552-1026487905=:25020--
+--12654081-1711788944-1026487905=:25020--
+
+--12654081-387266385-1026487957=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121732221.25020@x22.some.net>
+Content-Description: middle message (fwd)
+
+Date: Fri, 12 Jul 2002 17:32:05 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: middle message
+Message-ID: <Pine.LNX.4.44.0207121731470.25020-120000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="12654081-1731270459-1026487925=:25020"
+
+
+--12654081-1731270459-1026487925=:25020
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+This is the second middle message
+
+
+--12654081-1731270459-1026487925=:25020
+Content-Type: MULTIPART/Digest; BOUNDARY="12654081-128832654-1026487925=:25020"
+Content-ID: <Pine.LNX.4.44.0207121731502.25020@x22.some.net>
+Content-Description: Digest of 2 messages
+
+--12654081-128832654-1026487925=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731500.25020@x22.some.net>
+Content-Description: This is the inner-most message (fwd)
+
+Date: Fri, 12 Jul 2002 17:30:31 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: This is the inner-most message
+Message-ID: <Pine.LNX.4.44.0207121730070.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+inner-msg
+
+
+
+--12654081-128832654-1026487925=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731501.25020@x22.some.net>
+Content-Description: another inner message (need two before pine will do the mime-digest thing) (fwd)
+
+Date: Fri, 12 Jul 2002 17:31:12 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: another inner message (need two before pine will do the mime-digest
+ thing)
+Message-ID: <Pine.LNX.4.44.0207121730480.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+again
+
+
+
+--12654081-128832654-1026487925=:25020--
+--12654081-1731270459-1026487925=:25020--
+
+--12654081-387266385-1026487957=:25020--
+--12654081-113777422-1026487957=:25020--
+
+--12654081-2102091261-1026488126=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121734321.25020@x22.some.net>
+Content-Description: 2nd outer message (fwd)
+
+Date: Fri, 12 Jul 2002 17:32:54 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: 2nd outer message
+Message-ID: <Pine.LNX.4.44.0207121732380.25020-120000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="12654081-1955637437-1026487974=:25020"
+
+
+--12654081-1955637437-1026487974=:25020
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+2nd outer message
+
+
+--12654081-1955637437-1026487974=:25020
+Content-Type: MULTIPART/Digest; BOUNDARY="12654081-362457126-1026487974=:25020"
+Content-ID: <Pine.LNX.4.44.0207121732412.25020@x22.some.net>
+Content-Description: Digest of 2 messages
+
+--12654081-362457126-1026487974=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121732410.25020@x22.some.net>
+Content-Description: middle message (fwd)
+
+Date: Fri, 12 Jul 2002 17:31:45 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: middle message
+Message-ID: <Pine.LNX.4.44.0207121731190.25020-120000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="12654081-1711788944-1026487905=:25020"
+
+
+--12654081-1711788944-1026487905=:25020
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+This is the first middle message
+
+
+--12654081-1711788944-1026487905=:25020
+Content-Type: MULTIPART/Digest; BOUNDARY="12654081-1221085552-1026487905=:25020"
+Content-ID: <Pine.LNX.4.44.0207121731262.25020@x22.some.net>
+Content-Description: Digest of 2 messages
+
+--12654081-1221085552-1026487905=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731260.25020@x22.some.net>
+Content-Description: This is the inner-most message (fwd)
+
+Date: Fri, 12 Jul 2002 17:30:31 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: This is the inner-most message
+Message-ID: <Pine.LNX.4.44.0207121730070.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+inner-msg
+
+
+
+--12654081-1221085552-1026487905=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731261.25020@x22.some.net>
+Content-Description: another inner message (need two before pine will do the mime-digest thing) (fwd)
+
+Date: Fri, 12 Jul 2002 17:31:12 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: another inner message (need two before pine will do the mime-digest
+ thing)
+Message-ID: <Pine.LNX.4.44.0207121730480.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+again
+
+
+
+--12654081-1221085552-1026487905=:25020--
+--12654081-1711788944-1026487905=:25020--
+
+--12654081-362457126-1026487974=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121732411.25020@x22.some.net>
+Content-Description: middle message (fwd)
+
+Date: Fri, 12 Jul 2002 17:32:05 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: middle message
+Message-ID: <Pine.LNX.4.44.0207121731470.25020-120000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="12654081-1731270459-1026487925=:25020"
+
+
+--12654081-1731270459-1026487925=:25020
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+This is the second middle message
+
+
+--12654081-1731270459-1026487925=:25020
+Content-Type: MULTIPART/Digest; BOUNDARY="12654081-128832654-1026487925=:25020"
+Content-ID: <Pine.LNX.4.44.0207121731502.25020@x22.some.net>
+Content-Description: Digest of 2 messages
+
+--12654081-128832654-1026487925=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731500.25020@x22.some.net>
+Content-Description: This is the inner-most message (fwd)
+
+Date: Fri, 12 Jul 2002 17:30:31 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: This is the inner-most message
+Message-ID: <Pine.LNX.4.44.0207121730070.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+inner-msg
+
+
+
+--12654081-128832654-1026487925=:25020
+Content-Type: MESSAGE/RFC822; CHARSET=US-ASCII
+Content-ID: <Pine.LNX.4.44.0207121731501.25020@x22.some.net>
+Content-Description: another inner message (need two before pine will do the mime-digest thing) (fwd)
+
+Date: Fri, 12 Jul 2002 17:31:12 +0200 (CEST)
+From: Xxxxxx Yyyyyyy <Xxxxxx_Yyyyyyy@some.net>
+X-X-Sender: bc@x22.some.net
+To: Xxxxxx_Yyyyyyy@some.net
+Subject: another inner message (need two before pine will do the mime-digest
+ thing)
+Message-ID: <Pine.LNX.4.44.0207121730480.25020-100000@x22.some.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+
+
+again
+
+
+
+--12654081-128832654-1026487925=:25020--
+--12654081-1731270459-1026487925=:25020--
+
+--12654081-362457126-1026487974=:25020--
+--12654081-1955637437-1026487974=:25020--
+
+--12654081-2102091261-1026488126=:25020--
+--12654081-192303556-1026488126=:25020--
+
diff --git a/rt/lib/t/data/nested-rfc-822 b/rt/lib/t/data/nested-rfc-822
new file mode 100644
index 0000000..d4f118d
--- /dev/null
+++ b/rt/lib/t/data/nested-rfc-822
@@ -0,0 +1,253 @@
+Return-Path: <jonas@astral.example.com>
+Delivered-To: j@pallas.eruditorum.org
+Received: from example.com (example.com [213.88.137.35])
+ by pallas.eruditorum.org (Postfix) with ESMTP id 869591115E
+ for <jesse@bestpractical.com>; Sun, 29 Jun 2003 18:04:04 -0400 (EDT)
+Received: from jonas by example.com with local (Exim 4.20)
+ id 19WkLK-0004Vr-0I
+ for jesse@bestpractical.com; Mon, 30 Jun 2003 00:08:18 +0200
+Resent-To: jesse@bestpractical.com
+Resent-From: Jonas Liljegren <jonas@example.com>
+Resent-Date: Mon, 30 Jun 2003 00:08:17 +0200
+Received: from mail by example.com with spam-scanned (Exim 4.20)
+ id 19Wayz-00068j-KO
+ for jonas@astral.example.com; Sun, 29 Jun 2003 14:08:42 +0200
+Received: from jonas by example.com with local (Exim 4.20)
+ id 19Wayz-00068g-FY
+ for red@example.com; Sun, 29 Jun 2003 14:08:37 +0200
+To: Redaktionen <red@example.com>
+Subject: [Jonas Liljegren] Re: [Para] =?iso-8859-1?q?Niv=E5er=3F?=
+From: Jonas Liljegren <jonas@example.com>
+Date: Sun, 29 Jun 2003 14:08:37 +0200
+Message-ID: <87d6gxt7ay.fsf@example.com>
+User-Agent: Gnus/5.1002 (Gnus v5.10.2) Emacs/21.2 (gnu/linux)
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="=-=-="
+Sender: Jonas Liljegren <jonas@astral.example.com>
+Resent-Message-Id: <E19WkLK-0004Vr-0I@example.com>
+Resent-Sender: Jonas Liljegren <jonas@astral.example.com>
+Resent-Date: Mon, 30 Jun 2003 00:08:18 +0200
+X-Spam-Status: No, hits=-5.7 required=5.0
+ tests=AWL,BAYES_10,EMAIL_ATTRIBUTION,MAILTO_WITH_SUBJ,
+ QUOTED_EMAIL_TEXT,USER_AGENT_GNUS_UA
+ version=2.55
+X-Spam-Level:
+X-Spam-Checker-Version: SpamAssassin 2.55 (1.174.2.19-2003-05-19-exp)
+
+--=-=-=
+Content-Type: text/plain; charset=iso-8859-1
+Content-Transfer-Encoding: quoted-printable
+
+Material f=F6r att uppdatera texten om niv=E5er.
+
+Denna text b=F6r dessutom ligga som ett uppslagsord och inte d=E4r den ligg=
+er nu.
+
+
+--=-=-=
+Content-Type: message/rfc822
+Content-Disposition: inline
+
+Return-path: <list-bounces@example.com>
+Received: from mail by example.com with spam-scanned (Exim 4.20)
+ id 19WFzq-0005i1-WE
+ for jonas@example.com; Sat, 28 Jun 2003 15:44:13 +0200
+Received: from localhost
+ ([127.0.0.1] helo=example.com ident=list)
+ by example.com with esmtp (Exim 4.20)
+ id 19WFzp-0005hf-Tz; Sat, 28 Jun 2003 15:44:05 +0200
+Received: from mail by example.com with spam-scanned (Exim 4.20)
+ id 19WFzh-0005hR-Bu
+ for list@example.com; Sat, 28 Jun 2003 15:44:03 +0200
+Received: from jonas by example.com with local (Exim 4.20)
+ id 19WFzh-0005hO-AO
+ for list@example.com; Sat, 28 Jun 2003 15:43:57 +0200
+To: list@example.com
+Subject: Re: [Para] =?iso-8859-1?q?Niv=E5er=3F?=
+References: <002701c33d62$170fb2e0$a33740d5@TELIA.COM>
+ <002301c33d66$bf6483e0$d97864d5@remotel2tu76c8>
+ <64753.217.210.4.156.1056801224.squirrel@example.com>
+From: Jonas Liljegren <jonas@example.com>
+Date: Sat, 28 Jun 2003 15:43:57 +0200
+In-Reply-To: <64753.217.210.4.156.1056801224.squirrel@example.com> (Jakob
+ Carlsson's message of "Sat, 28 Jun 2003 13:53:44 +0200 (CEST)")
+Message-ID: <877k76uxk2.fsf@example.com>
+User-Agent: Gnus/5.1002 (Gnus v5.10.2) Emacs/21.2 (gnu/linux)
+X-BeenThere: list@example.com
+X-Mailman-Version: 2.1.2
+Precedence: list
+List-Id: &#214;ppen lista f&#246;r alla medlemmar <list.example.com>
+List-Unsubscribe: <http://example.com/cgi-bin/mailman/listinfo/list>,
+ <mailto:list-request@example.com?subject=unsubscribe>
+List-Archive: <http://example.com/pipermail/list>
+List-Post: <mailto:list@example.com>
+List-Help: <mailto:list-request@example.com?subject=help>
+List-Subscribe: <http://example.com/cgi-bin/mailman/listinfo/list>,
+ <mailto:list-request@example.com?subject=subscribe>
+Sender: list-bounces@example.com
+Errors-To: list-bounces@example.com
+X-Spam-Status: No, hits=-7.0 required=5.0
+ tests=BAYES_00,EMAIL_ATTRIBUTION,IN_REP_TO,QUOTED_EMAIL_TEXT,
+ REFERENCES,REPLY_WITH_QUOTES,USER_AGENT_GNUS_UA
+ version=2.55
+X-Spam-Level:
+X-Spam-Checker-Version: SpamAssassin 2.55 (1.174.2.19-2003-05-19-exp)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=iso-8859-1
+Content-Transfer-Encoding: quoted-printable
+
+"Jakob Carlsson" <esrange@example.com> writes:
+
+>> Om du g=E5r in p=E5 Hemsidan och sen p=E5 Torget.
+>> D=E4r ser du att det st=E5r ditt anv=E4ndarnamn och
+>> bredvid det Niv=E5 5.
+>> Klicka p=E5 niv=E5 5 s=E5 kommer du in p=E5 en sida som
+>> f=F6rklarar allt om niv=E5systemet.
+>
+> Bra svar. Men jag k=E4nner f=F6r att ge en kort f=F6rklaring av niv=E5-sy=
+stemet.
+
+Jag skulle kunna l=E4gga en massa tid p=E5 at skriva samma sak om och om
+igen. Fliker in h=E4r f=F6r att s=E4ga detta =E4nnu en g=E5ng...:
+
+ * Det =E4r jag som hittat p=E5 det h=E4r med niv=E5system
+
+ * Det =E4r en stor skillnad p=E5 hur det =E4r t=E4nkt att vara och hur det=
+ =E4r
+ nu. Jag har stora planer och en massa id=E9er jag vill genomf=F6ra.
+
+ * Niv=E5systemet =E4r en =E5terkoppling f=F6r vad man gjort f=F6r webbplat=
+sen.
+ Som ett tack g=F6r hj=E4lpen.
+
+ * Systemet finns som en inspiration f=F6r de som d=E5 k=E4nner f=F6r att g=
+=F6ra
+ mer. Men jag vill inte att det ska ge en negativ influens f=F6r de
+ som inte gillar niv=E5er. Var och en ska kunna v=E4lja att ignorera
+ niv=E5n. Speciellt b=F6r de f=F6rst=E5 att det inte har att g=F6ra med
+ graden av andlig utveckling, esoteriska kunskaper eller n=E5got
+ s=E5dant.
+
+ * Inspirationen till niv=E5erna kommer fr=E5n spel, hemliga ordenssystem,
+ kosmska hiearkier, skr=E5v=E4sen, akademier, politisk administration,
+ osv. Det =E4r ett element av rollspel. En lek.
+
+ * Olika niv=E5er motsvarar olika roller p=E5 webbplatsen. Webbplatsen
+ webbmaster och ansvbariga har en viss niv=E5, bes=F6kare och g=E4ster har
+ en annan niv=E5.
+
+ * Alla datorsystem har administrat=F6rssystem f=F6r dem som sk=F6ter
+ systemet. Jag har valt att arrangera dessa funktioner i en skala.
+ Niv=E5n anger hur mycket av systemet du har r=E4tt att administrera.
+
+ * Att ha ett niv=E5system f=F6r access g=F6r att man kan g=F6ra som p=E5 f=
+ilm;
+ att l=E5ta de med h=F6gre access komma =E5t mer information. De med
+ riktigt h=F6g niv=E5 kan n=E5 topphemlig information. P=E5 denna webbpl=
+ats
+ kan varje anv=E4ndae v=E4lja att h=E5lla vissa personliga uppgifter. Har
+ du h=F6g niv=E5 har du rollen som anv=E4ndaradministrat=F6r och har
+ tillg=E5ng till dessa uppgifter. Just nu =E4r vi tre personer med
+ denna niv=E5n.
+
+ * Niv=E5systemet =E4r ett m=E5tt p=E5 p=E5litlighet. Vi ger dig h=F6gre n=
+iv=E5 n=E4r
+ vi litar p=E5 att du inte kommer att f=F6rst=F6ra f=F6r oss. F=F6r ju h=
+=F6gre
+ niv=E5, desto l=E4ttare kan du sabbotera inneh=E5llet.
+
+ * P=E5 en h=F6gre niv=E5 beh=F6vs det inte bara att vi litar p=E5 att du v=
+ill
+ v=E4l. Du m=E5ste =E4ven ha ett gott omd=F6me, teknisk f=F6rst=E5else,
+ intresse och logiskt t=E4nkande. Utan detta =E4r det l=E4tt h=E4nt att =
+du
+ f=F6rst=F6r saker av misstag.
+
+ * Vi vill uppmuntra medlemmarna att g=F6ra det som =E4r bra f=F6r
+ webbplatsen. Tilldelandet av h=F6gre niv=E5 ska uppmuntra till att
+ g=F6ra det som =E4r bra.
+
+ * F=F6r att minska missbruk av e-postadresser visar vi e-postadresser
+ bara f=F6r de med lite h=F6gre niv=E5. P=E5 s=E5 vis vill vi undvika att
+ n=E5gon g=E5r med som medlem bara f=F6r att samla e-postadresser f=F6r a=
+tt
+ sedan g=F6ra reklamutskick.
+
+ * Idag n=E5r du olika niv=E5er p=E5 detta vis:
+
+ 0. Kom in p=E5 webbplatsen som g=E4st
+
+ 1. V=E4lj anv=E4ndarnamn och ange e-postadress
+
+ 2. Logga in med det l=F6senord som skickats till dig
+
+ 3. Skrivit en presentation
+
+ 5. Presentationen har godk=E4nts
+
+ 6. Du har svarat p=E5 ett f=E5tal fr=E5gor om dina intressen
+
+ 7. Du har svarat p=E5 en massa fr=E5gor om intressen och beskrivit dem
+ detaljerat
+
+ 10. N=E5gon v=E4ktare tycker du f=F6rtj=E4nar h=F6gre niv=E5 f=F6r att du=
+ =E4r s=E5
+ trevlig och engagerad i webbplatsen.
+
+ 11. Du har gjort ett antal kopplingar mellan =E4mnen och =F6verv=E4gande
+ delan av dem har godk=E4nts av en v=E4ktare, och du accepterar att
+ b=F6rja som l=E4rling i v=E4ktarakademin (jobbet som
+ systemadministrat=F6r)
+
+ 12-39. D=E5 och d=E5 tittar jag p=E5 vad du gjort i form av skrivande av
+ texter och arbetande med uppslagsverkets =E4mnen, och justerar din
+ niv=E5 i f=F6rh=E5llande till m=E4ngd och kvalit=E9 p=E5 arbetet
+
+ 40. Du har full=E4ndat ett helt =E4mnesomr=E5de. En m=E4ngd sammanl=E4nk=
+ade
+ =E4mnen med bra textinneh=E5ll.
+
+ 41. F=F6rtroende att arbeta med adminstration av medlemsregistret.
+
+ 42. Delaktig i utvecklandet av webbplatsens prgrammering.
+
+
+ * Allts=E5. Automatik tar dig till niv=E5 7.
+
+ * Men som sagt. Jag har en massa andra planer d=E4r mycket mer kopplas
+ till niv=E5er och d=E4r det finns systemautomatik f=F6r hela v=E4gen till
+ niv=E5 40.
+
+ * 41 och 42 ligger utanf=F6r niv=E5systemet i =F6vrigt. Den som har de
+ niv=E5erna har inte n=F6dv=E4ndigtvis tagit sig till niv=E5 40 innan. De
+ motsvaras av anv=E4ndaradministrat=F6r och systemadministrat=F6r och
+ niv=E5n speglar maktbefogenheterna snarare =E4n vad man i =F6vrigt gjort
+ f=F6r webbplatsen.
+
+ * Alla texter. Allt inneh=E5ll =E4r =F6ppet f=F6r alla. =C4ven f=F6r bes=
+=F6kare som
+ inte loggar in. Du kan till och med g=E5 in p=E5
+ administrationssidorna utan att logga in. Vi g=F6mmer inte inneh=E5ll.
+ Det vi g=F6r =E4r att hindra dig fr=E5n att =E4ndra inneh=E5llet. Vi d=
+=F6ljer
+ dock en del information om andra medlemmar i syfte att f=E5 s=E5 m=E5nga
+ som m=F6jligt att sj=E4lv skriva in sig och skriva en presentation.
+
+--=20
+/ Jonas - http://jonas.example.com/myself/en/index.html
+
+_______________________________________________
+List mailing list
+List@example.com
+http://example.com/cgi-bin/mailman/listinfo/list
+
+
+--=-=-=
+
+
+
+--
+/ Jonas - http://jonas.example.com/myself/en/index.html
+
+--=-=-=--
+
diff --git a/rt/lib/t/data/new-ticket-from-iso-8859-1 b/rt/lib/t/data/new-ticket-from-iso-8859-1
new file mode 100644
index 0000000..299392d
--- /dev/null
+++ b/rt/lib/t/data/new-ticket-from-iso-8859-1
@@ -0,0 +1,31 @@
+Return-Path: <hw@nordkapp.net>
+Delivered-To: j@pallas.eruditorum.org
+Received: from sm1.nordkapp.net (sm1.nordkapp.net [62.70.54.150])
+ by pallas.eruditorum.org (Postfix) with ESMTP id 48F4E11112
+ for <jesse@bestpractical.com>; Mon, 2 Jun 2003 14:58:37 -0400 (EDT)
+Received: (qmail 3612 invoked by uid 1009); 2 Jun 2003 18:58:36 -0000
+Received: from unknown (HELO office.nordkapp.net) (213.161.186.83)
+ by 0 with SMTP; 2 Jun 2003 18:58:36 -0000
+Message-Id: <5.2.1.1.0.20030602205708.0314c5f8@mail.nordkapp.net>
+X-Sender: hw@nordkapp.net@mail.nordkapp.net
+X-Mailer: QUALCOMM Windows Eudora Version 5.2.1
+Date: Mon, 02 Jun 2003 20:58:30 +0200
+To: Jesse Vincent <jesse@bestpractical.com>
+From: Wilhelmsen Håvard <hw@nordkapp.net>
+Subject: Re: rt-3.0.3pre1
+In-Reply-To: <20030602185607.GN10811@fsck.com>
+References: <5.2.1.1.0.20030602204834.031406d8@mail.nordkapp.net>
+ <5.2.1.1.0.20030530194214.0371c988@mail.nordkapp.net>
+ <5.2.1.1.0.20030530194214.0371c988@mail.nordkapp.net>
+ <5.2.1.1.0.20030602204834.031406d8@mail.nordkapp.net>
+Mime-Version: 1.0
+Content-Type: text/plain; charset="iso-8859-1"; format=flowed
+Content-Transfer-Encoding: 8bit
+X-Spam-Status: No, hits=-1.9 required=5.0
+ tests=AWL,EMAIL_ATTRIBUTION,IN_REP_TO,QUOTED_EMAIL_TEXT,
+ REFERENCES,REPLY_WITH_QUOTES
+ autolearn=ham version=2.55
+X-Spam-Level:
+X-Spam-Checker-Version: SpamAssassin 2.55 (1.174.2.19-2003-05-19-exp)
+
+Håvard
diff --git a/rt/lib/t/data/new-ticket-from-iso-8859-1-full b/rt/lib/t/data/new-ticket-from-iso-8859-1-full
new file mode 100644
index 0000000..493ca15
--- /dev/null
+++ b/rt/lib/t/data/new-ticket-from-iso-8859-1-full
@@ -0,0 +1,38 @@
+X-Mailer: QUALCOMM Windows Eudora Version 5.2.1
+To: Jesse Vincent <jesse@bestpractical.com>
+From: Wilhelmsen Håvard <hw@nordkapp.net>
+Subject: Re: rt-3.0.3pre1
+X-Spam-Status: No, hits=-1.9 required=5.0
+ tests=AWL,EMAIL_ATTRIBUTION,IN_REP_TO,QUOTED_EMAIL_TEXT,
+ REFERENCES,REPLY_WITH_QUOTES
+ autolearn=ham version=2.55
+X-Spam-Level:
+X-Spam-Checker-Version: SpamAssassin 2.55 (1.174.2.19-2003-05-19-exp)
+
+At 14:56 02.06.2003 -0400, you wrote:
+>> This patch didn't help us out.
+>> We still got problems with auto responding e-mails sent from the system
+>> when a new ticket is created.
+>> The same problem appears when one of the staff replays to an new ticket.
+>> All Norwegian letters is converted to strange letters like ø
+>>
+>> We would love if this bug could be fixed. On our mail server we are
+>running
+>> perl 5.6.1 since we are using debian stabel packet lists.
+>
+>I'd love it too. I just can't find it. Can you send me
+>(jesse@bestpractical.com) a couple of email messages containing
+>characters that break your RT?
+
+Hello again,
+
+Thanks for your fast replay!
+
+I don't know how this looks at your end but it is letters like: ø æ å
+If your want to make this in html it will be &oslash; &aring; and &aerlig;
+
+
+--
+HÃ¥vard
+
+
diff --git a/rt/lib/t/data/notes-uuencoded b/rt/lib/t/data/notes-uuencoded
new file mode 100644
index 0000000..f27fdf8
--- /dev/null
+++ b/rt/lib/t/data/notes-uuencoded
@@ -0,0 +1,2368 @@
+Return-Path: <mhenrion@example.com>
+Delivered-To: j@pallas.eruditorum.org
+Received: from serveurlotus.example.com (unknown [213.56.193.67])
+ by pallas.eruditorum.org (Postfix) with SMTP id C21DB113AA
+ for <jesse@vendor.example.com>; Thu, 27 Nov 2003 10:55:58 -0500 (EST)
+Received: by serveurlotus.example.com(Lotus SMTP MTA v4.6.1 (569.2 2-6-1998)) id C1256DEB.00578401 ; Thu, 27 Nov 2003 16:55:54 +0100
+X-Lotus-FromDomain: DOMAINEQZ
+From: "Maxime HENRION" <mhenrion@example.com>
+To: jesse@vendor.example.com
+Cc: support@example.com
+Message-ID: <C1256DEB.005717B5.00@serveurlotus.example.com>
+Date: Thu, 27 Nov 2003 16:55:50 +0100
+Subject: Test e-mail which exhibits problems with RT
+X-Spam-Status: No, hits=-2.6 required=7.0
+ tests=BAYES_20
+ version=2.55
+X-Spam-Level:
+X-Spam-Checker-Version: SpamAssassin 2.55 (1.174.2.19-2003-05-19-exp)
+Content-Length: 144905
+
+I send you this mail from Lotus Notes to make sure it'll exhibit the
+reported symptoms (lost attachment and body). I Cc: it to our RT address
+to verify it does cause the reported problems. Could you please mail me
+any replies to my personal e-mail, mux@example.org ?
+
+Thanks in advance,
+Maxime
+
+(See attached file: Naz_Head.jpg)
+
+(UUEncoded file named: Naz_Head.jpg follows)
+(Its format is: JPEG File Interchange )
+
+begin 644 Naz_Head.jpg
+M_]C_X``02D9)1@`!`@(```````#__@`>04-$(%-Y<W1E;7,@1&EG:71A;"!)
+M;6%G:6YG`/_``!$(!(D#$P,!(@`"$0$#$0'_VP"$``0"`P,#`@0#`P,$!`0$
+M!@H&!@4%!@P("0<*#@P/#PX,#@T0$A<3$!$5$0T.%!L4%1<8&1H9#Q,<'AP9
+M'A<9&1@!!@8&"0<)$0D)$248%1@E)24E)24E)24E)24E)24E)24E)24E)24E
+M)24E)24E)24E)24E)24E)24E)24E)24E)?_$`*(```$%`0$!`0``````````
+M``(``0,$!08'"`D0``$#`P,"!0($!`0$!0(""P$``A$#!"$2,4$%4083(F%Q
+M@9$',J&Q%"-"P5+1X?`5,V+Q"!8D-%,7<H(U0QACDI.B)29&5%8!`0$!`0$!
+M`0`````````````!`@,$!081`0$``@(#``$%`0$!`0$````!`A$A,0,205$$
+M$R(R87$%%5*!_]H`#`,!``(1`Q$`/P#Y`NZSM9#1`&(!5%[G\U"!WW5B](U:
+M0(GDE5'DR3$]R3,K53ZB?5?D$I,JNC=`\EN#.=PHMVZH^%$6?,)R3(`R90/K
+M:AB0!LHFG"&6Q`,?*(E\^"`"3.=TC6=J`)([!0M#=1U'X3.TZ9D[X**G=6=(
+MR82\]X,ZC`&"H0X"9<3W2:(&GOWF4+VLMN7D2T[[AR*E5<YPU?>56:T,@P<X
+M3^D#'*J+3:SC(<8[92\XB/5]U7VV$E/Z=(!.459_B7`0'&2F%<EX)D]R%7!)
+M:$B1.<S^B(G-PYFQQ/*7G$P9/M!5>9,:ML)Y$]AW03FLXR78A.VY=&=IV58&
+M2!^X1-BFWO)S""S_`!#M,.,'VV2;<.(C48"JR'#&!V*>!LUV_OLBK)KO@C5)
+M_9)M=V0(GNJSAL9"7!!E066UR&X)^BD%P06DOR%2&00WG=$WYSQ[JHL&Y&>9
+MQV3ON7.;ZG:@.).%59,Y.#[)H&F7.`/QNBK;:Y#?S$XP"4(N'[:S[Y46H-8<
+M2#C.4``SZA!V")I9?5=AVO[<IA7=J)C$[DJN""8!VY"3C$;@(6+?\4_83O@)
+MZ=U4;@$YQNJ0<>')L[3G?=15_P#BG`0"[/=(W;N3NJ@/J]>8PG=M(<!P%46Q
+M<5)C5!&Z%UR0^=6_,JJZ220[',XE!J(VD@HJZZZ<3J+H)X*=]T]K1ZLGW5*G
+M+G3N$[-()F/_`,1E0TN-N7@RUY)[R<)V7=1V=1^ZH@.,F<CA$V/+F8CLJBW6
+MO'^8/4[[HA=U,^L@K/?#GB-N4Y)Y)CW15RI>5'#_`)CL>Z`7CY]1=`[$Y50&
+M1,Y]BF&^G!]I07_XRII!#JD3]E(RO<NI.KM-30T@.=P"LV`T[C]D0<?+(DD$
+MR0D5<;?59(+S)P9)2_CJN_F.)VR51=+7@S'QA($N$#9$:-.^JR/63]5(R]K.
+MP*CM1]UF'7L,=H1M=+IY0:#[ZL'R'N$#9/\`\0N`3-1Q!.8*HD..1N@]0)@@
+MQ[HK39U"J2!YA`/*3>HU9_/O]5G,+F"9@>Z?5D3Q[HC2'4*PF'9=]T_\?6!'
+M\QQ^2LZG,[R-U(3Z):<<"9A*+K.I7(<2*KA]4XZI<:R[S78YE9H))$NB?=+4
+M(C.^4&O2ZM<:Y-=^F<PXJ9W6[ME1_E5ZA!V]1P%A%T-/'NI*+M(U3J/LLU8V
+M[?KM\#_[NK,?XBI7>(+\`#^+JQ[/*P/,<'SI`]E/1D[@'.`D*V[GKMZUP>;N
+MOW`#RJEQXCZB^KJ_BJ_P*CEG]1JD5PW5D?HJC2`V8,]P>$A.&PSKO41Z'7E;
+M/_649\0]18YO_K:I@Y]9@K%:\DDM/P.4#G._J)39IT%3Q#U(48;>U9]GE*WZ
+M_P!3:=;KVMCC65@L,M],_=3C\I9@^_*IIT-GX@ZL]P<;ZK#3)EY5YWB;J9`_
+M]75`]G9*YJB-%&!@G<=T?F!K`T3'LL4=(/$_5-`TWE4#_P"Y,?%750__`-[5
+MS[KG?,U-`)..W*$N$F=QW4TKH?\`S5U?7)O:@^J3O%/5&`3>5<_]2YQS_3,D
+M90OJ%L9S[*CI'>*^JA\_QE4>VN?[)4?%76`''^/KCC\Q7-.?WG[PHR\9[\!"
+M.I/C+K8R.I7(CL\J1OC?K[!J'5KH>WF$+DC5TM&#J_="YY=].$Z'8?\`G_Q(
+MTS_QJ\$\"L[_`#1?_4;Q8T"/$-^('_SN_P`UQ.O/`(3O?Z<\JZAR[=WXG>+V
+MMQXBZB)Y-R__`#3?_4[QHV2/$W4\<?Q3_P#-<,YTD-[(7N!.70!NAR[S_P"J
+M'C(;>)NJ0.]W4_S3?_5'QII.KQ/U0@;?^I?M]UPDN()DCLE2J/%6>?8Y5X-U
+MWP_$[QDTZO\`S+U0#L;A_P#FA/XI>-1__D_5!`__`-A_^:XBK4+A#=]LJ'62
+MT@@2H;KO:?XK^.`#'BCJ7_\`'=_FI&?B]XY;G_S1U$]OY[O\UYWYA+")*;6-
+MNRJ/26_C)X]8UI_\T]1@XCSBI/\`ZT_B$W;Q5?D]O-*\R#P`#O&R$UC,SDH/
+M4*?XX?B,R)\6]1$?_M4=3\<OQ&F6^+>HB?\`]IM^B\LU.W(D$*.M7\L$-DGW
+M31MZF_\`';\2*6H'Q3U!Q(P?.V_15S^./XE:-;O%O42WDFIM^B\K#_3).0>4
+M1>-,R,YB86M0Y>HL_'+\2"X$>*NH?_Q/]%+1_'7\1FNSXHO2>_F+RG5#,F/8
+M=U(S+@Z23\)J'+VNT_\`$'^)M.W:QOBJ[@=WJ3_]8;\3_P#_`*NZ_P#WUX]0
+M/\IOJ`13_P!8_P!_1.#E)>;X.DJE4`G4&CW]U>O/2YTY/95"VD\@O(8(_-NE
+M-(*I$\#ZJ.((DP#MRC?I!,.#@3P@<YLQ@D*)T!Y=!!'V3,!(!DR-NZ/!DDS_
+M`)H=3FOU#\Q$3*`2[U;F3^J9FG5DG!VA%J&"\3VA,PC.H3]4#")SCZ[IPZ6Y
+M.?;*9Q$P1">`3L`F@0!<W48QB)RDW\W8=DM)B0,C=(G'Y3OG*H)A]7YC]TGO
+M`;J&VTI8`@$@QRF`F)A`6HZ<&,(`XM$-),HM.TR`DTC)_9`@]H'?NB(U9)D*
+M)Q`R/L"G:]K3D$_*"3`)DS'(_P`X2>0`")).=10F`,SG9*`Y^DS'9#0VZM.'
+M@0=NZ51S@XYR=PG9IF'?1!4<)QF4!`X&<E.9%3F2.Z$Z-(:!$Y30-4SL@D`]
+M<ETSPD2`X%Q$#W3.>TTQ@",3R5'[%V$1,Y\B8S&Q0:S^;4")R9@(2^`<P>T[
+M)`@^IH&GL%%2-?@B8^J;60">3]T+=C.J1O@_ND<_U8'(RJ<E))W`GE,?=Q,<
+M%+TDD@R?E-$C#A[A0$`79&/JF!<U^X^B'!YRDUOJ(*HF#AY@Q&$6KDNB%"0"
+M9!2<8.3)Y4$CW2\MF#^Z8.(;!&KZIG-P-L#A"V6F=8!/""0%I.0/<(75#/:=
+M@$.F:>8`/)3#3OJ'T*(D:[&8B=Y4C7$"&M,J)L1Q'>$3@1L8'L@8F3@1]4+G
+M&(VX2(#3^:>\%`?54P9'SNJHZ;B.^4]0D@X!(0@..QCZIR`V"7-.)0)CR6P1
+MDHFZ@-_B<H`WU1IGV3L#L0!`X)*:41=.<GX1,?\`TYA``=<C`&0EI/,90&PN
+M+IQMR437D#8PHVM)B'>VZ.>9&?T32';4C&4FN.24S!G,B.4@"7P-D4;CZ02G
+MUQM@'E,`7,D-F-TP:03#B)XW03TW0`3/=.ZHT[20HJ0(=JP<83U=1'Y??=$I
+M.>20)B/JF<\D^W<!#)DQB>$)+@3@D=@@.1J(C\RGI.TTAL`%!;@U'@&8)4U<
+M#40..%*LA!WKD@?,JQ3<*;=6J?959U`<%*HXBG,R=MU`JM05+G42#[QLF>8:
+M0V"1O)W4;):`V3G>$4@$&()W0/YA+H&$S"=Q$'E,X34D!.&MF<>_NBIZ.G5.
+M1C=6*32XZSMP56:Z#I#1/=6ZKAY8:!$;PI:1(7B!)SV0O<T[&(0BF-&<#W05
+M7:G%L8&%!,UPC`$_*?7)))4+&NF9P-@0G/R,]D!$M)DGX"3G%P`W^NZ!A;OB
+M1[R@JU"1F(;VP4!ET'TD3[("8?&,^R`O!&,&=@A($`P2.TJFAQZAK/V0U()]
+M),\A(QITP/C>$+R(+B![(IY&DQN$!U$=OE*99G;V0ZVDSVXE7:0I$@DY12W5
+MF0.P47ID%(N@03]R@.1DL'ZH<X)#L\RF$;:O]$Y8WR]0J-+I_+&?^R&A.=`G
+M'T0!\2[)*%P,1D`<(7N`@$_JB40>#_DD#^G"C!R=_NE()$'?!0$X%Q$?NF(&
+MY!&.Z;;4`24#RYH!!)]T#U7A@,\JNZ-6?ND\EYDN$]T]-[\MUX[0M!/;+?E,
+MUDQ/WW2=4TGL3V*!SAQQS*(D:WD$P?T4M+8@29W]E!J],CE'J&H`.XR$&G0<
+M!1:(&R/6WL%#1TFDTEW'9%#/\?Z)N+I/<F"1,=]7^JI5G`N(';`[*[?$.<7[
+MS]BJ=8@'&$J('2&YF3QV0`-+`XD3VY1<D$@'=`T>L@B%`B9(SQF$/&TIHT[B
+M2F;!.3'L4#N:&P[?Y*:1!WD=T\S(.3\RA,:2Z.?T3:GD%I&Y&/A*8&9CLA(S
+M$B/S8PG<V88QIQGOE$T-CV#9SB>Q&R<Z09CW(40V@#9$=IP/>%0;B#F#CW14
+MXU>QX4?]1X*3,$X(]U!,_2'0XS"%S@'20(CLAJF'#E)I!!:2J$XMS(]L(26P
+M)S[(W._EENH@3J4+FR2/902.TZCP(V[HH!!C(*A;#@)V[<J6F<R3J&,3NA3@
+MXW*8",@'(15&AQD8C.\H>8@M^"J#<1&QE-SL0.Y2(SOLE'IF<($XB<F/="8+
+M(<<3$I/TD@`SVE!I].))"@-S@1(=@I"=)SNA((80#*(/$`M:!`SE%%,`#7&$
+MCN"YT_51DNB`Z0#CA.0X_5(AQ&`23[E/L1W/"C@.).T9*3S+@1/9/\.AM!.2
+M0">$3P0[)"%VD9!D#<I./I[IL2'5$`R$!&&SQA"UQTF73/'"?.($%#8Y&3//
+MV35`XOU&)2):`?3!'U0O,M$B`>^Q02!D;F2>P0&(,[I,:XB-4B,Q"1#@=49"
+M`J9!R^3&R=Q;J$(0(GD(*A<(='U2"1V&;#_-`R(D;%,\DL$B>Z$;X!55)G48
+M1-'IS&VZ'48&#GE,<C<0@(`:\`QVDI`;[<]TP=B00G:7"<;\A`;M1:2,8R<I
+M-&0)@H0XZ(<-(B>4[2[5`(GL902`.:#ID(2,Y$D]D33Z().$(F,'ZH#:"6GU
+MB9_*F$SO]$F-)V,%(.Y`D(:'4P0`?LD`[2)D`]]B@#LX:1*<O<&ZH4$C9&YP
+M-LIW.#6R'&>Y0D02-.>ZE<\NHM`:)'/*HBDQ.=MYW2C,D[(2XN;))"32XM(!
+M@'906+-LEU4DXF(2TD$$.)3AX91:WDY0DM#?RQQNI5A]!+@3/PA<=\@HGNCT
+M_?/"AJG$#8^V$"UD8!4E,P9;G':5$"`?5()X4[6.@.VCLJ@2UT`B?E.3#`9$
+M\93:H/I/SA.P-J$``R#NH+%KAVLOD=H4I<9@SW[*(0T>EV/8)5'$N&9![K-Y
+M5+J<68(R-TTB=0P4!<-`@(`YVH24$],SF?A"]P#MQG&4&J'">$+W>H09SA%%
+MEI(D"=RA+9;D_;!3.+B\DCG9,]X(,.T@;?'R@6Y,]L94L4_X4N=Z7R(`V4.M
+MPR2W*=S@:@&P(VE5."?KTR1!)F4#LB"-LRBG<B2[L@>YH`$85"!G@'ZIG0<D
+M093'&<#@"4%0AI@D`<`=T#D1F0/N4P!!@#?>>$S8D3*L=2_@!58+)U4MTC6:
+MD3JYVX00'T[YS\I4W`\H9#G"8^`D\M#AZ9^40Y<7'@?.4#@)!R24Q,]FI$ZG
+M$_W5#XW$S'=,UHF(W0DB#DY3R0V29(W0"78)*KU':R`9]D]=^O\`*%&60\1P
+M@,#5`VGNG`:T.,9!30`!#N,I0!N(`5*.B*/G!U5ITC)&<_91N+<D-)[0FJ/D
+M:08`[H]#O+%0QI)@9S]MU`#1+A,Q^RE9'FD$'M'91@MU&-^ZD9N-.,;JHNTM
+M(I@02B]/8H:=1K6``''LB\T=C]D.%FX.EKA@B,$JE<`AT3SPKEXZ"1&?M]53
+M>!J(G`*54-6=H"B/I?MDA2U3DG&.%&]Y=!+H(/W4`N_/@&$B6R'#=)[AJ(P(
+M0-$/@"9303HF0TR/T2:06!I)E*3!$8E-K=K!``;QE`0#9`P8]L(&_(CV*<&0
+M<C.X"9Y]7QB0B">.9.4[OR['L"A<1$`A&_\`(.!.854F-(:?4?A$6AKX[;Y3
+M4G.#,[=RE.!(CNB'=&X.R8F``,)2X',0)"B)).T245(..>=L)B<D`#M`3:M(
+M!=,@)Z1!?F<'_50)K1)!Q(V[J0#3B#F,(6.;KV(Y,%)S@0UHQP54['4<?B,H
+M69)!/M/=)I(AS@8VRAD:P!CNH)<O/ID$<)F$APR?A-JAL2?NE,9P(RJ&=.H\
+M&=I1-$`N<2)4=1\F03_F4=,X$$E`SP-1$D0A$C&(/.RE(:7.<-_?A1D&8&_O
+MRHIWDZ2-1&GE)C@TN!.3^B3=&D-(`_NA,-<X!V"(E$(;S(SV1/:]CAK:1(D>
+MXX0'<B$;WZC@N(C??"*6'")R3_LIVN`S(,(&N$#VY2$G(;DX0IR3[2.R<.DF
+M!!_=(D!@')R?=-/IDD2>R!SAP),>R1Y(SV]D+V_S2V))YE,T:F@`X"J)&2UI
+M'._*1Q$&/<!,(C_JVV28(:<0.5%Z%J)!)W`R5&).!&F4;@9,Y![<*,#!,[[!
+M!)IFG)?Z@8CV0Y`T@C2A@3NG;Z3\<DJB2GOO\Y0S(`(!)[*2E2<^D][`3HR2
+MA>7:9,`;0H&WR`(.(1;;D",8*:)Q)D;F4B#.'8!GL@?+7$'OPDV"_.3.Z6"1
+MM]1E.ULSD2,90$TES#\@83M&3&W=,USA$R0=RG`T@.!WVA%Z$UQ+CB!Q[I.(
+M.`([^R<.EFW/*`DEYDX(@RB:%.8,&/V1MD8R<*,B'`[QPIV#^43D#]D`Y#H.
+MK`Y"*J#$F028DY4?YJN,`=N457!<=I&<H:,]V)X`WA'3#L'^F=OA0@2=AE3!
+MIY=$F-DIH3CJ?K(V/")H$>K/919@P1DJ5^EEO`(G(W]EEI%5(+ISE!4=D<$'
+MO"6OU1Q*:)=G!Y5036G7)VVE76.<+5S1I')=L2J;"00`.5,S\D@C2$^!JAQL
+M('LCH,<&E\@$YCZ(6-UNB7$!2@[-&`-]U*';.2#`CLE3'I$@'O/"8P!G'=%2
+M=J>9^<J*>L#$#$8P@:3DD@J5X<##?Z9)([J(?F_+)B<H'#@203@9P@GU';/9
+M,V&R0,)V9SC&4(=X<'#,R.%&3#0XC$P$3\N(`V^B&GM@XVGO^BL!C!`&GLG)
+M:W!@Q&>9E`#IG<`\2FEN=L?JB%4($Y"!Q#8&1[]TG26`Z<`;=DG0&C[HH0,R
+M<?)Y35&D.(Q!V]T\C5($@#(*C>('(.\*H?`@'O.-T+RUOIW,Y@J:XHU:5.G4
+MJTRT5&ZV3R-I^X4+H+B<Y']T`O(;Z1N.1A*GIDNP2>XE.3J('`Y"6&M^NZ:-
+M!)RYV0"G!``&DB1DH8&7'`,C"8N],ZAP)0._2XB`94%5\NTCGE27)?2+6N8X
+M:A(GLJ\`F)S^ZH8P!`R-Y3G\V)D#^Z0(&,F#O[)IY)A$&T@&08QRF=!:,$3&
+MR<^H0TD$_JHWY]!W^J!W-);!&#NG.EI!U9!VRD0)`G/"%P;$@2(0&!%0.F09
+MG*.F`#^:3O""FT$`D[X^5)1G4!@GL54K1HU8I-!F81><.Q48H]W9&,92\D?X
+MBJ<+=T&!T-,R,:>%1J!DDQ,=RKEW`<?OO$*C5/IDG._RIEVJ-_J&IW(A0OES
+MRXF(X4E0Z7&"@<1,[\DJ`'`E_ORFP=C]?9$1#@9R.Z%_]1`D%$,?R:@WV^4P
+M'IP<<RG@@>Y,X1/]1)!`&T(H6Q'..QW0^^Q]D1/],`X2!<<GY0(F""9]I*=W
+MJW,H`)P08'MF%)HSF2$03&`M(DP,D),+@(!GA"X:6D#/PF&XP280V1:><_\`
+M9,2Z?RC!X1%IRX?=,-R<G&44B#JU`$=\[)Z8]1,!/MC2<^^$FGUS*H36#5J@
+M>PF$\>HC()V2<'!V/ZNZ%WYL#G[J=D$W#=S')'9"-B=C*+,3JG[X2:#C(SW5
+M#M(U>TIB8P"!GE-F<F2.0A(P29RH@HVC?=&PZ03`)G[(2&R`"B$!XAV$#G)S
+MB.4+XU;P-]^4[MB0[9`XXU`^Q"*%QB"79C&4AAV^1^R0,#3L92<3&=T"!VEQ
+MR8PF$AH+73*0D`XG.4M0:X#2-X0/.CC"(.]6/R]X3"`<[I-;#8G<3]43_"=,
+MZAE/,M(C?NDQTMT_E'9(X;D@$HH1(/.T;)VQF<&<E-!UY)`^-U(R0)B1R)R4
+M-&:/3O,>V4[`6QB3[!%I=H+P"&SM*6HZ1'',($\PR(.3@A0P9)VCA&XNG)R<
+MPF,$02,?HB$&XD_7NF@',9[)2=CE%`'LBI#5J&6ZX`&PV48>-7JV`_5/HV@?
+M/ND02Z7?TG[H&@#W._PB@Z@):1'=.)>_)@\^Z%Q/YG8(Y0%@$@N`G$#NDTRU
+MT<\IAB3.3G>4[7"#N0).$47]43.)_1$PB(C$J,8#8@2C9,SM_9-(-@]$`X.4
+M),C`(#433@M]S*&)=&<]D!TA)S^O)4U9H;;AHDSV4%(@`;F.%+6=Z`>$#,$.
+MW]H*8DDDG;M"'S#C2,#L93R3D"2@(0'#5$B813J<6R/="P'RR22!P$S8:^)U
+M3N2I5D2'3,1B=E$^"[_5&YP:6@A19F7#"FE$`8`'.91%IJX$B!RGI_FG)C,!
+M$[\LP?8@JHC@M#OS23W1EQ\L#&IW9"XR?=3T*;=1>/\`,(:%0:6CDR)_LG<0
+MVI^:.=U-TW^&_C!_$BJ]G^&F!)/`SQ*&]=JJ%PHLI@>G0&Q`[+*HR<S!Q$1V
+M2I8,S!WRD_@^W9,Z)W.!R$$SJKV,<`2)PHF:@09&QY1-_*)SPA(&Y,$X0`:A
+MG&V^439))!B-ONF>-1R?9*EI%3+CGA`FEQ<XR/\`[=Y]U')!(P8'U1@M!_*/
+M24-2=0(CZ*APXQ$^\\(9D%HYQ*1<X52[4<\SND\Z3D$1[H'JDBCD`9W49>-,
+MF)V^4]1PT;DD\("XZC'`50@_U#A!)@S.=H.R-[A$0@#@3+>4@=[M3`"3+1&3
+MPA.6:8R,9.Z6`-1.)0D#\VK3\;H?].02[!$D)G3IG4)/[IO=HE(@@29^B:*1
+M/H+7`P=@HW5"`/T`3$Z0=1.=@%%4(+OR[[JAWU'P-1,1@=DP=#L"1L"F`]$$
+M92S)'M`0$[!!!(GE".2?L4A&LD"('=,X1!D%"<B#@(XCW2U9.-\D#,I!HX;`
+M3%IF,=Q[JAG'U`P=7NBU!QC?]4="B^L:@:6-T@N.IP'[\J(D8;R#DSNHFA`,
+MU!L'&P[%2TY:X\Z3E0@@.#IF.94K&D.#OJ51>HU7-I-;)V1^>[N5'2CRQZ47
+MI_PE39__`%9N'D/=#RP$&8Y"H5B7`Q!`5V]U$G<-E4:N9_:%:(B!I^,B5&X0
+M,G<X")Q,EHP.T*-Y.N3E3M">TF`/U2]$&"9A._5IR1"C.-B?A!:Z76MZ-;76
+MIZQ!@1.?<**Y(\USF&&N<2`,0%'J=J&`.9*62XD.^J!9($../E)PSD'YE,XD
+MNDRB+6Z))`)X'"!-B(+A(3THR2<`Y4;?2Z"W[HV^Q@^Z"S>OH5?+\FB:4,AV
+M9U'NH-!D$`CW]DAD`';W2DB1/"!'TGA(3J&4P^#M".1Y8](!)C5R@'5#N8([
+MRF$1W[A,^-A)A)L[`9/97L'3`,^J/9.08^>4(])@G$Y"<OQ''N5`4PTQ!^4$
+MD&=7"1<`-MT,Y)_=`33(R[9,'D$]BF<1H&($YRA82XG`[!!('29&%/\`RBQI
+M8\E_(55N'0I&D<2(SW0'4\P[Q'NA=ZFSR/U3%W])P=\H*C@#(&>Z*<D[G<]^
+M4GET9,'ND'-)!,@'E+C!D($W\@@DGNG]4G'/*;:8&.Y0.X02$EK<<)-/IF<C
+M"%I(<8)!/"(9B.40]$_TPG^1,80L=)V2<1,D2500D$@P!RC`_E[0%&'%QDHV
+MN.?[*`M;M&((_9`7&#Z?NC:^&;Y/":H6^6#MG:=D`L).YB?>$U,AI)+)]I0N
+M($[3[IV#4'`D-Q(0,-\B/E.2-0Y[H6D'?CB40(B/LB[."7&(`^2CDSG([(#&
+M'$Q"9CFAWM^J"0D"=+O_`,(2:0?S8'<(7?D(9/=.TC>4!'_F?&P3M<"P`M)^
+M2A:2?Z))^J<'.PCF4!O@Q`C&TIP&C+2<H:L0#&^$I`P=O9`9?-4FJ3`Q(3L(
+M$P#"C`WU$8[J0;XS/!0('21&_=&'M,G`GOR@I-+\0,]\(@2<@"0@8M:9,D@"
+M)".A3>\:PUQCF$``@G`)[C"ELW%E20.,%`U>)(,[<)FZ/,!R8X2;-2H07'/)
+M35&C9@R-R"LJ:Z?JJ2TQ.P*9CR6^H;8A`6%P&H93M(@B2/A4&7NSI,81:VBD
+MT'NA$@#!":,:AN@DID/J1$QMA7':12TM>2!C3W5>T8&F7#<9A2G2'&-H6:HF
+M$MRTQIY&(2<YIV,F<GNAV;VG8%,[4!L0$"+V!N9DA/3=@'5*9^EK1DAW/`0T
+M8TDR@DU`8!W0X)TR@^1CY10!ZCD%`TF"3$;(>Y(&??=/Z=XD#W3TV4RQQ=4T
+MXD")DJ@=,TX'U!&4G%L@D$GB.4G$EH``B(D(20!I`@2@<N;!&P[90MR<'ZG=
+M"Z)`F)Y2>R'8F3]95#U`"TZG#V0,PZ8$?*0_*3J.-P4.H3D0!PF@3F1(D%!`
+M#?2_=$]K1SO[H"V`3[\(AA^724AEN(":)$G.>4Q`!+I]D(9X$21N8^5(:C?X
+M=M(TV`M.K5SMRH@T22YQ([RHZKPY\3LJ<&J$D[8&<8E1P-8WA&]L1O\`"$M$
+MG]B@*#I/;N$AW.2D[)B?\DQ:6MW0)L3!E+2`X<#;*;=VK:$>F6R3\(A%I`R)
+M!0./H)X]RG.N8:Z!V"`G_J'Q*+L[20"6N`'*4-+IG;@SE+3ODD']$T@;$_=$
+M2!KB3!)PBI2'1D3P2@IO(;@[CDJ1FJ<F>RHO4R`P`MV]T6IO^']4%/5H$3]D
+M_J[G[(BS?.:1O/Q_=4*S@XR($*W7+M6,?14;C\Y&DGY5JHWU&%NJ(^%$7-#I
+MR0.$YVWPAR';@1P0L[#.=,D$IAM,I.P/5L4FB1#<HA\%LM.1WY2IN#!,"8Y`
+M,(>^2(14?+TDO#@Z,0-S[H!D$B3]CA/Z9P))XF4C`.WW3"`[9OP-T!:O5I.(
+M1#,9Y0-`@D'(XSE/I$3O&=T$D9R0,IJD<#`[(7B!`B$=(,\LN/YAPJ'I!U2J
+M*8DEV!">H?5$Q'=0OB=C\IV%LB0?NH%J&`,?7=$7-F2?JHW?G@`QS"?1J@9D
+MJB1L.$EWZ%,XAQP)]X0-P-!DQ[E(@3B4#D09!QP$[BTO_P`/LHR`'01(&Y[)
+M`-@@GX"@,1M(*>`!.('9,`(B-]H2>WU3)^$BCH,+@ZH&ZVL$NCA"!)W.=D`D
+M'00[W1:?^HY]T00T[$B?E+3F9$]D).ET.]/8IC_TNYG=4.W!CT@C8%%B9G[%
+M`X-<R6N.N=CLG#@&`-&D]QRBE`'YMW?JFP!,@_5`!)U%TPGR9$X*"0P693-@
+M?Z(!,Q)E2TFZJ;R)EL&%-!\0`#)[)$2-OL@,GU`PDP&),`GGLB)6Z2,3CE)C
+M!/K='O,J*($@X.^5(&.))B1SA-B2"!I$Y358!R(E#)`.R%[P70W[=D4G#TG&
+M90AI&3F4G>QWW":(W(*(-K!L-SW3U"<9P$-*9!=.$_J@DG!]]U03HTB1*%D!
+MV3RA;/<3\IVM($3R@-S9'SC*?3&<GV0L<>2=T\S)XVC=`8$"03GW1`B3.2>5
+M%Z_\7UE$9`$\\I%&X"(+H[(@V!+=O9!,M&04B3.`?NH#C5MN4[!G<?"$O=$#
+MA%ZM.K<JFSN:22<>T)P(;.#W]D`+RB),[[Y]BH!((!$<;RI`T#^J9$!"T@5`
+M73],HF/=,Y!^5*IV"&^P1.!@:1]45NUU6HUC07%YC9=5XA\,ML^@T+NWU.>/
+M^;G8(;<B\1ZHDE`]K33&"'3O*L7`)I``[;X4,$#)P@4X$[IZ;7.J0`F)$!HG
+MZE3V[?+]7/)02.>Z-,$1V3=I](*4R[5!^H2?CC(S,+*E$.YQ\IW`SB<[RDZH
+M=S$H"\:).)[H",QB8W[)I+1)Y[(=8/!TQPG-22TP2`J%F#W"9@)9'*1=+>\E
+M(&,\(!J-,B3CVV3M\H:PXEQ/Y<PA+FEI$X/ZI:R0#P/9`M3G-+-6V8"$F3I)
+M4C'28'I("C>\-&"#)[H`@DZM0'UW4FC^5)W/*`N;,@Q.QA25GD4FM+@0-E1$
+MT%(^HYA,YS8$G[%#4>W5G,>Z`J0+WP2T?*!P+1GGA"7Q//NB#M3I=$#N@%TD
+MQ!RG:USCI`)+N(3!X)[?&ZBJU@`()U`]U4*N2'Z9/V43_>$^H&3,<E,2#@DY
+M0.<F(,IR0&['*#4.47HF0!'MRJA-<2(X*1,E,7``CGLEJR<J!Y($<(0X@1Q\
+MI@8D).TM;W11&/H4],M#7`_1`(<S!QV2;!WF408(SC(0F1.(GE*!,ZM^$8-$
+M4OR&9R^2@%D-/$?*FI.=,PK_`(;O^GV;WU+KIS;MQ$4W>8YI8?V(^BJA[#<$
+MAH:'&8_LJBRS\H_-]"G^CONI*8.@:=D\.[JFCU]&K))!6=<?.RM7;W!SLM!&
+M?=4:CP!)Q&.RS5#4<<`"04#G21.4GO<&P,#V35`=>D#;.>R(:);)D92TYAQ(
+MA!K]&DXGE.^IZ]1.Z!R!,.)PF<9,R!\IB[N)^J)Q@B0W81D($^7.DD.)RF;^
+M:)@\04P))P!/$[):])!/?=%'`WGZ)W21Q'8*.G.HD;GNB:^#B`"$B#@D`0/E
+M)K3&'8Y0M?`!$%*F_.VRI$KJ!IL8\/:0\2,R1F,J-ATY_1,2.3GY3/?+G.TY
+M*BG`DSRI,MIZ=,3O[J/5&",I.>"W5'^B"322V<9V*'!=`R1^J8&<B&CV2+A#
+M06&>2/ZD0[V@B&#$X3P]N)`U;CN@U0Z2)^4J;QKEPD?/*`J8$8W3G.^.R&1C
+MD#9,7("JZ]6EV^V4I]6T=RH^W/'RGUB`!L<(I'7$YSRG+8XF,X*'40P`#,I%
+M[33].LCW0&?R`G?A.]L8&2>R%C@21$QV3Z@1IB/D[(&>),SO[I>Q$)B]H<1.
+MV)28<@%THAR8;M\J2A7?1:XL.7")W46IH:0D'M+<`Y0&T^H%PD'=&`"2"0!\
+MH&N!SF2,E.UX+>/HBBI-8714<=/[(]88``R)P8*B#MO5]$S'%I)X/?\`[()G
+M?E(#<_J@(AA`S[HO^:XBF)`R8[=U%JP1(A`[LNVVX3M:#]DVK.0!'*9KP'D[
+M(@H),`E.[:(@[Y3,(U9C&-)2>9S@1P4X4F3I):)C,)__`,4)VN&DQPA9$:9B
+M>$T'`R2'8^$;&SZ9^J'>=(,?ME%OVA`U,,#<C?ORIJ=,:H,=P%'`<^1D=R-E
+M*P@-G>4`09+@G>`3@$?(1@M!)(WY"3CB8&>4#.C@P1S")T!QS(3-=C2(SR43
+ML.:"=N`B!,1)<E'JD1!/T1:(/K*38+ITF!^B+#,<!M@\93LT3)=D;!$Q[F&6
+MN(/',J%I&F!N?E1IJ^%7@=<H/<]K!3>"3JA>B^);RA4Z'>U650PD"F`>?9>5
+M,:0[21!"MUJ]>G;MM:CB&-,D'!!*K-G*.H3Y<;25!+M1G96*CFU&@-!$;F=U
+M#6;MF)X470:;"]_J,0%9!ETM<"/=10&MW$A'Z2"1.,PI06HM9I+M^YPDYVI"
+M-(W)SPDV)&!CLHIH<3))_P`DY=-/1((!^J9^_?5]4[9PUS`9._=4`TP,#;A*
+M1K./H$;O*#X9VV/=`"0\@8/)0(@@0D-3H!2V.9(F/A!L3R$!8+#$2-\;H9(S
+MJR.`E`TQ.^Z1;Z?3QA`F/(!=@\Y3$MEWOLF([;'VW0D@$1N3,!4.T@C+FYX3
+MO=#(:Z1V0.$,G8<ISH`B2>WL$`Y_J'RFT@&>QV2<3'YLM3``3!^$#$$3$)BZ
+M&C$\)-$P(.4#W9[J@G8+CO/"K/WVS\J0R#O]"FT:<@C=$!$X''*3\")`"+2!
+M_6)[[(3ZAGO"!,@1Z8)[A(F#@#(^4Y$@-#I*1WF2/:4`N/K&!/LGT`.$I.)!
+M`;N>$Y;@B9A$[",_W3$>D0#*(&"?9,V3OP@30`22#^R0`DX3_P!)DI$``.(P
+M?;*='!I^<^Z><P)^I3:8.K_91.R9:,#.4":[,B1*GHRYP)!+?E!1`#@:@+AR
+M&F%+0:21)`GVV6D:%N'&BTAKG#O*/2__`.-WW3TZ)+!,CZE/Y/N?N5>$X5+W
+M0U\`&!LXB/T5&IVP<[;<J]>Z=;HG'?95:H<6N&J`-_4LU5>J6R=(`GA`UPU3
+MSP0C<,'T_=!I,@$1/"BE-,$:@2W8B>4PC2X:=_N$B#IEP&<PA:(+N/E1"=#6
+M:MP.$HT[Y]T0$&/NF;)!Q'P531_26@#9-@.U$Q[!.#)R,PA=F,GMOM_N4#\2
+M`#/"+2W48!RDUN9F<[SNG9^4`JA3+6M+6C3LAV<=Q"*H/5#<M]\(1EIWX4!&
+M)&F(.Z9X:#N93@&,!$]K#&)PAI&V0?T@IS);,SRAW=O`E'`#9VT^Z!-C2,`#
+MW0L#6N]61S[HR"!O^9"\^H``GZH$/RR8R=DOVC"+27F2Z"A].@"<E`XV`!W[
+MI$RR!$!/R0"/=,\0W,YY0`"9'Z)G#(AWM\IR,.)&>/9(#U`SL4#M#G"29(]\
+MH2(@29/8HV-BGJ.T[IG#TG=`-,0W&?:43@Z"1J,')3.PPC^KY39`]C@^Z!P6
+MP<;^Z;MW[IS,$AWU*0F2<!`V8G`A.W$D1E.(CY2<X&)@1&R!Q,D.(QE.);C8
+M'!GA"UL9))X1G\QB806ZUG:MZ+1O67M-]>H]S7VP:060<'WD*FP;C'R1*.LW
+M)#H!&9!0-(:XG3(CN@)T$0/J!RBTC6886G_#RHX!_-()W2)@S/\`F$!O>3F(
+MDS`$(#(/'RG:9.2?<)%OTR@1W_1$"X/:0)0T\D_NB+=9@`YW0+(G5B0G/YRX
+M#XW3D:J<$NG?;W2U$$S!&^/KR@31+-0(D',82$:-I,X"=K26''Z[IP(=!=C;
+M<_Y(IQ/L9C*)C9:!)SPE,8TS/;9/2:X#L9F5=(3@X``#'"-S8.7<*32UU*)R
+M.R'2';MF,RHIG4R,$1&^=D_I`&,C*DIM+O:<H7:FNVV32&(UAA=.>4=4`50U
+MC1,3/=,,.D`X_J*>OZB=R1G]%+RL5WODZ1W&,B$X<YIP9U[R=T[V&#J.V,('
+M-B()E72K%!Q:&N:8TYG?,JQ>75>_O*EY=NFI4RX@``G[*I1IR#@>YE2-_P"8
+M""94T@2[33,`R!A/2.LZS@#9-<5'U:HF2[8'X&%)IAH:(]/92KHC(VF>X128
+M$GU>R%V'`<\II.DP"H$]TOB?5VV2<9&J3'9-!+06DB3ND`YQ)B>#[JD+5!!^
+MIRDQVH@.<1[IIC9N/W2&",`DG<H:,20^03.^2F<0(#9^4X):`Z9B2<80@<DH
+ML$YP#1!SRD#G\T=D,``:O@2?9)\AL''8]T$]Y1J4"P/+':FBH-+@<'/W4#7D
+MR282<6C`,%VW*$D-)@&)'/*0.7G43P/9#K<1,C)W'*;?;[IL#DXW5#Z\:=1S
+MW3/)U#)CL=@F$N<&EPR([H2TAK@?J@(%T8=]$Y>7-`D1*3,?F<)2):_!@>RJ
+M&&,Q@;J#S`YP=ICV5BXKTS0TM;\E5?1,D'XE('J/$Q(@\!(U(;&H#ZIBT`3]
+M0AC$%TJAR\E\"#'.R;4!L,X3`AN(XR4(C)@[J`G.)&`W`^$['2\@C(&(*9I@
+M3!/RG8UFF9W1`EV9D8V2EQ=O*8MGMVW3Y$@1/[H'UX2:X3!/W3``M^".4VSA
+MJ]0]T!DB3`QLF>['.>R;TZ@0=^4PWD@F>`$#ZL1!@29[(FN(//P90$3,@0>"
+MEIT^D9CD(B4.'((CWW4U")]IG95F07'2I:8`]6Y$*PZ:U!X=1:9C'9'J_P"K
+M]%%;,8Z@USG"2I/*I_X@IJ)Q^%>\@2,3[\*G4]3L`EQ=/*O7H&HXP.9W56A7
+M=;W(JM]4;`JU52K&DP#W`0/!TC(R.>%(]TDDF0=T!,NF?NH!.^<@X3<;P2G&
+M"#''=.W2=6K&,(&+L'`SW2&_"8DN@0!'LF9@QD#V0.#B0V9X1,:PYTY/NASP
+M"DW?)_1!)`!@SGF4U,G3S]$1!+@&`D^PDH6983G"J'):3GYW2DS\(=68(&$1
+MP=OD%%*8$9R>$[SO'9,TPX#VYY0D^QR@7],$E/3U!TZL^R8`:9RDT9,X]T03
+MR"P;GC!W34R2X8^Z1([8Y2#@#Z.$4XG9VR0<3)SE)I!$$`9V'"0/](D"5`[7
+M'5+C!'LE4<7.`WX3&/,+@/NE4@B6SODH!;D3J/.R<[S((Y2&G5D3/,I#\\EV
+M_(0+48F1":>=H/=.XX/J@H9&0#]^4#D[N)D\IB&D>K<G$#"$D$#;4B=+J(]4
+M0<!`G:M\"4AG,#'*%ID;\(VCTZ?N@8.`YCLBWR(E,6@-[IP"0`#GL$#M/JP?
+MHB+BT1CV0AL-D;!*,R1\90'J!!).3ND"-67`-Y($H<EI$0$JA:'P!I;VF4$E
+M4L:]S*1+F#\KR()'PHBWTZL0A!!/:/='Z=1AVH#F$#`RZ=S[(R=3VAVW.5&"
+M-1D[(J=33K#`(<()*`B8,`9"=A@!TG[H9D?YI.R`$$K"?*F/9)KA,P$$^C3/
+M.#E$T`28P,*@Z>\3G[),P@88$"3^RD8)]_K*`F#\HTJ:@TEQ#),]LRE86]6X
+MJM928YQ=@87J/X=?A_YY9<WU.2=@6[)&<L_5Q'2^@WE[6:VC;N(//^PNQM/P
+MZJFS];8=`V^5[!TGPO:VM`-IT6-@1`"U&=+9Y<:`I<Y.G*Y99/G6^\$7MLXM
+M;2+O=/TWP'U"X`=4HD-'?NOHJGT&E4?E@*OV_0*#:8_EA3WA[9/F^\\`WS&%
+MS:9)VC"I6O@7JE1QFB6^T[+ZAJ]!HZ/RC[*J[H%!ACRQE/>+,LX\!L_PUJNM
+M]=1V>T#O\KG_`!7X3J=(`<8=G;_9]U]']4MF6EN8;@+R[Q9:U>J=<IT?+AC3
+M^8+/OOX8V[WMYYT_PU=W-EY[:1B"29QNL.^M7VU=S'M(B0%](])Z)2I=)#?+
+M`;IB(7E/XM]&IVMX:E,0#N`KN6-8YW;SZBW2#G!*,["#./LB<'#,R)Y4=36U
+MP(R#NHZA<[47$G/)2],2TP(2(Q+DS<M```[*J>F?3VSLDT""9,'L4+02=.T=
+MDX.D$9^2@:63J:=^)3/#0,'/'LF<(,`3'ND[6!$R`-]T41_(,XA,T`MXP/[I
+M9&(!`0C83"H<Y$'Z)$-!`)GVG`2>.8$'CLE3INU"/LIMJ8VG:W40,8F$VG!/
+M;:5.VVJ.W!RI_P"#TB"TR>W98]X[3]/E5(0[D8'"8@-(@Y[=LJ^RS#:8)$_!
+MW41LW;_V5F<,OTV<4F:-0SGLB>9=C,;'LC?;5&Y:PP.0=_=1.I.#B-,'?LM2
+MQQN&4-Z`T$S*CJN)>&C')37))=C!43B0)'9:C(G!L8@@\(2T`P<1W3-,Y<,>
+MQ2<Z`<[X506ML@$`0/H@IMR`?V0@SG(5[I5A5O*=>HQ]%C:-,N.MX;/L)W*@
+MID8D&$.!DC/:4[QB3./="#O\;2G_`%#@1.?OA$T&1/*!L3`XW`*<R&X_?"*$
+M@D$=_NEB),]DP(U29B$1AS2[,#W1#``D@\)VC&`@!DD*6UKU+>X;6HF',Y(!
+M_0_*&T9;G&VR+&C^W=-J).)!/9-+L$\]R@?2"Z&R`=I2:1ORFGN8^B=OYL">
+M4!TIF6_*E:,R-Q^JB;F"7P/A2TR"<2"#NK$:-!O\H8_4H]/M^I0VU1IH-+G-
+MGW"/73_QM^RNS:.[@SJ='SF51KENJ`(^JNWS0'&#,?,*C5B-QV2FD3C!P/NA
+M<0'3(1.D849(Y60GYV"8@!^EY"9GYM]CLGJM(JG5AWNBFEL@!(#.0CI/%-^L
+ML94'(=,(6[B-CW0(!NN#D)-_-$8&Z;!?,?5$&P/<Y]D06H:P`Z('=(G,QN4)
+M(VREZ1!C94.8)G^Z(!I.K5D[H6@'(3@%K0`,*!G$:O44B!$SONAJ`.=$A(M:
+MP01QO*H(8R4XTQM/U3AK74\[\A,2'1/`C`"&BALR"1&92(R8_P`D/]$$_HG:
+MV&]OE`4;;DI1G?U!"<`:1$_5,P'.,J`OZN\)$M+3DI?TEO$]T,$[H";$3.X3
+MC?<;(3\Q]4MAC[RJ=$6P2-0W^Z%PTG<=MD3@0W>.Y*`[$%Q^94-$./5&=TY]
+M6"YL<Y2HZ)/F:M,?TF,IFQH@$S\IM3@\DY'9&V`)P2HHQV/>43>8)RB#P7',
+M`I,,`_XB<&4V(@G*$>DY!)E!(UKC@[)SO&J!W(0M#B<S":3QF-I5!-:[:-^R
+M8[P8,)A)G@A(MG<[<HIW-!P0DT;`1!00`1,_*-HDF'8^Z&R>,Z0-]Y3M;!$P
+MA=(P(3`N/930D='E9F>R3!Z)(0N)P)Y1ET",#LJ'@AHC:.Z=F6\Y]T+"Z2.%
+M)1:[2=O9$%3:&-V$GDJS86M6XJMI,:7.<<>ZKTR3Z0)/<3*]#_"+PU4O+IMW
+M49+`9`(/^2,Y9>LVZW\*_`=O2M67-S3#ZKA.1M^B]8Z+T^G;L#13`^%'X7Z>
+M*5!C-,8[+H*-L)S"YY9;<9-\T-.V!`X/LK5&U!&6Y5FVH>D<*W3I:6KFZ2*=
+MO;!KXC!5YM$%L$;?JCI4I,E6649XW4VU%-U$`:0%!7MQH/9:IH]A@J&K2$?L
+MAIS'4^GBLTC.?9<S6\/4OXT/#?RG>%Z+7MY9$96==V6=7]E=LW%S->W%.V@!
+M>7?BY8&O:/>`1&Y"]BZE2#&$'8;KS+\5*M)G3*PQLI+I9'@]]3%.I$[<*M5'
+MI;DS[*U?5'/JN(V!RJ[W=FQV7:.J"7:I:<)Y,1@E.3#C.4.2=DL4XYVQ@X2@
+M%N9)]MD+I`PW"0=N8,G$(0Q(B-N\),#G#TR>PWE6;*SK5ZA#*9"V^G]'#-+J
+MH),<KGY/+CCV]?Z?]'GYNIPPJ5K7J$0U7*72:P$.<#';9=*VVI,8T-:,(W4Q
+M@:<+RY?J;>GU?'_Y>,_LYUG23H]69[*Q;],8QTP#P!V6Z*=,4<MR=D+:3=1V
+M6+YK7JP_1>/'J,T68G#1",6L[;K1TM),`)F@%T8F%CWKK^S)\9K[2&@C'OW0
+M&W@"1[[\K7T^DN@`*&JP.PWZ*S-,O#BQZMOI=(;+NTJI6L0]Q!$'B.5N5:32
+M)(4%1AG`77'-YL_T\O<<]<=-)+LQ*IUNG5&9'J75>6W4<`_51.H!P@M77'S7
+MZ\?D_0XWF.3-N]H/I46DY#1F5TEQ;,)TN;LJ-S:-)D-A=\?)*\/D_27'IC:3
+MO@\PCHO<UXTG'(G=6*UK!D;J$L+"?3D"#(727;RW"X]ANG:ZAT1!V#0H7ZHS
+MN.4;AR1`[$IMR&@B!WX59,R0(B$JC3H!@P=O=$X`#?[%,2"`V8[1B%$T$;&.
+M$+7$"!,?LI'L+'%A,GF#,H'QJW0#,&`#)1&"/3(^J=I9`,F1M"$`$R#E`[3!
+M@B2@$]RI-W9D]T,2^3A`GOU&3GW2:[)U`3\)/P9DIZ?Y/S$]C.R`Z8!$@?JI
+MZ,3"B:`UL$`2I*>F>%J(T:+Z8I`&E/\`^)%YE/\`^'_^=/1T>4/[(_0FDVCN
+MR&/CN=E1KM+GX`#5=O<./:?LJ=?6`!CVREYK2*N"&Y:-/=0`1O!5BYK&I3:S
+M2T>6W3@;_*KDSF!\+*[`\`"<]R$CDYA.7&#(&4P=),`(A^Y`S^Z1)!G0/A,=
+M]Q\)&#C5&$#P#Z2V9X",`!LM)'_2=T#7%K@1N<R41<XF9SQ*!C.F(,_"<-);
+M._(2#AF1/]DYB=A@<(A-(TSRD6D`&##ML)R=+<_IRDYSB!,EHVRJI.#1I.#(
+MRFCU2X9X[)$MU#&W"=S@1!;[94`2)&"B;@Q&1R2CMWTZ-4.=3#\'!)$'O]$S
+MSK)+B23V0`1F0#_FB:8;MGB4TM!RG=AOI=/?*H9QSDDG=,/4,XSR44EID8CN
+MFJ/UO+XR=XP$#[@;F#'T0N#0?E.UPTG)U<)G$8).5.@PTQ/Z)R<8.#PFF`3*
+M?TDS.$#/`QZOHA/IR8^Z,D1!^Z9SN`90"T[%I_5(CG4$<&20<\?"0'HG(A`!
+MCO/N$1+9].P3-!,G)^B,$;($((D&<;%-I([81,AN9G.R<]S'N@'$#)2:6@GD
+M)CM&"=Y2TS)]\%-!.:0V3LG;H(]3@R!RF</1._&H)R&@X/V"`>#,2B;!SB`/
+MNF8-6J8QF$YTGL([H%)B"A8)SC")FF9)D>R1&"-`^Z!FZ2!,(V#!C9`S)C`"
+MDIZ<D!`J>)&H`*:@T:B'\;94+&@G8#WV5FDT;8'U07NAV#[J]ITJ8U&HZ.Z^
+MCOPPZ"VRZ328&@$-$XB5X_\`@OTT777FU32U"G]>R^DO#EJVG;LP-EC*Z<L^
+M;IJ]+M]+0TC;W6@RB0Z>!W0VS0T",?"O-8'-Y*YTD*W;F8PK0IEPD;)K.E(R
+MKK&%K0UH$$9*EK2O18(^%<HTY;@<(64X*LTQZ/[J-:1!DQ*"I1!G4"K(:/CV
+M3$`^R&F?5I`">W"IW5'!ANZUJS`!)^%4N*;2,$PAIRW7*.JF6E>7?B%TFI>T
+MJE!LC5B3E>S=3H-=3,#9>=>-*#VO<ZEN#V4MXX23EXZS\-R\ZGW&_LJ/B?P$
+M+&U%6WJ%[HV*ZSJMWX@H5GOI4'>4WDA8W4^MUG4]%VUTQDA<9Y?)/CO/'OJO
+M-Z]I5H.TU&$9@X4-1@$P?ONO0NFVEEUB62))W6%XR\+W/3'&K2!=3<=X.%Z,
+M?-+=4]+O3EG#T0>5+:TV.J,&K$[!.:,#U'`XA36+:0K!I#H_ISRM7+AZ,/!K
+M*>SI^AVM(46QDE:1MVZ9)CYV6?TJHUE-K08+1F<J[4NB[!(@]E\O/?L_5^&8
+MS"2#K:1#0&^G$@;J!\<?HC+@3OOOVA`/S;Q!VE9D=./@BW@N.`AT@$S@QW1S
+MQJ0%PU$`[JZ2TU(@$SOV3XU;X0GY3L.8_NK(S:(&<$X0`28F?JCI^IV`#/NI
+M&-(,N(CNAPKU&X.3*K/;+B3^ZON#"X2H:M,3(:?E:QK&6*F*8`)X*%U,:O2,
+MJT6:A(S\H"P`8'ZK<KE<5%],&2,=U!6IM..ZT7MEN,2H33!DQ,+4R<<O&RJE
+ML)G2/B54NK=K3J#9G"V31+@8;!*KU:+7&'M,QN,+MCF\OD_3RL"M99]+8',E
+M4Z]%S,1)/*Z!U,ZC#3'=05*#"TRPN]EVGD>#R?I9\8);D$@X]DM.T_<K3JVK
+M"8S`56M;N!,"0NLRV\>?BN*N0-+3DGDJ,@&9;]U+5:1(((`P(49;%,N&\K3E
+MK0?2#@(FMD3^Q0AI,G^Z)K=HDQP@0`/TX2.()!`*6-X..4BW$Y^VR`<$R?OE
+M'#=>MS0?;9#_`%R<_=2>HC),#[HA4V@O`!&3B2K+:3J54L?^8;P0?V5=@+7X
+M&_<*9H((F9[+2::-$GRADHI/<IJ!FBTYVY1K)PCO8#03$DP1'"IO+7#3'&X*
+MNWAET-[[]U1J@9S@SRK5TKN.#&^V2FU`,DP>$50P)(!08#)].4`!P:9W2,'(
+M@?"1;!QM"1T[&8X4"F>('LD73N,\)$2<"$@UL[[=@AH[@W!&4\@`2AF,-$=D
+M0((/I*!1/'PGJ`#&_NAV`(^ONB@3,Q`RD*6F(S[%,(C!V1`@$1&4(.8T[?J@
+M)P&#P3PDZ"9@;X3$0X#(3;-R1WA`W<GOM"DHEC6G6P/!$"9$'OA"=)<3D3RD
+M6>G5M&=^(0(PYT`&#R2F`@S(ENV)3P,D&1$(20?2#))A`[LM.WP.4M+3`#ON
+MD/T30"[&)0.1,YVRFB&#YP$@")$_F2C3SE#1')P/CLG@;;)FQL#LG9&,F!C?
+M=`G&#!,@]D,>WU3Z(&K,3"8?E(!S*!P($Y^=TX`(._<Y0G4($F-\I`$`'7(]
+MR@=N''2GTX!.24IWGD=TL:B03E`^F'1V]TSH+AJP.4A&K#DQ'O\`=`FALP#C
+MNC/I(@QVA1P6SZH@I\H$<@R[Z]TV8+2222D8!B/HGAH;((<>QP`@;40R`1ZA
+MF0EN`(R,$IB"!NB:-I/&R!B"#N91-P"<D)1!C]$,'43DRBK8J6W_``KRO()N
+M/,U"J''#8_+'SR@#G"WT;MU3]5".YW^45/5,;3V4T@F`AVX([>ZN6=)U6JUC
+M#)<8WA4QJ#N-UT/@6W;5Z_:-J:2USQ/;<*I7M7X)>'/X/IE*K48-=4!Q/.P]
+ME[#TVDQE!HD;!<GX2ITZ-A2T@"`/V746=4'2&F5QSRY<<8U:#"2(,1NM&UI2
+M`9,'=4^FM$!QC9:UM3!`@X66XEITVC:?96*;?Y1)Q'"&G3B".%(3(`;V4K41
+M'+H!*FHR8$Y]T-.D7.!*LTF!A$Y[*:5&01$HFM&G5^ZE=3!$\)FLTM@YE%0U
+M&F/95+FG(GLK]8D^F8,*A>`G`A+1F7X]!,?*Y?JMHRO6ES!`.5T=_4+9!&.Z
+MQ+BH"7$B(&%C*FF/UNWL'VIM/+:`X:0LNX_":UZ_T\MH-(J.&".Z@N:[W^)M
+M)R`?[KU#\,NMT+6[;0K.&1&5Y/W+A=UWF&^GR?XZ\+=;_#SQ2VUNVO;2<[4U
+MYF")_P!%V'2[=OB7PT0&@E[(F)S"]6_\9%C8=:\(_P`51:UU>V]0<W>,KRK\
+M`:NJU?:U#,9S\#"WGG,M91N;T\?\7]/N.D=<K65=NG0XP8W$[K*MWO-0.&8]
+MEZG_`.(GI/\`_<MO6IL'\RG!('N5QW3^FM92:"UTC,%>F>7&8O3X?#Y//=AZ
+M?4K-HB08]E>I:B(_0\*:VM<#TG'SA7&VA#@"V9]EY,LY:^]XO'<<=519J),Y
+M/"-I>(EH6M;]-J.:!Y#B79P%J],\*W-=H?HT@;:@LSGIK/R8X3=KE1KW@Y2#
+M:T?E.>5WMGX.`<#6=,C(6W;>%K%E-H-%KL025VQ\-KP^7_T?'A>.7DP9<$C0
+MUP=QA6Z'2NHU73Y+W%QDSRO6*/ARRI^H4F`]EHVUE;,9'EB`NV/@GVO'Y/\`
+MUO\`\QY78^&.IU&RYND^ZL,\)W^DAQ:#[+U&E;T22)$=D?ETI(C$+7[.#SW_
+M`-3S?'EG_E&]TE^H'YW0N\)7;F>H@$GY7J-S3IM&1QM*K-8P.VW[J_LX)_\`
+M4\SR^X\*7S#Z:<@'$*"MX:OV"32,[X7K;J3'#\L(76M$M]7V3]C%9_ZOE^O%
+M;SI=W1,&@<;JE5H.#9#")]E[7==.MZH(-,&5F77A^S>Z/*;]E+X)\KMC_P"K
+M_P#J/'GM(RX`905*(\LN*]4OO"-BYN*<$]ES_6/"#X)MS_\`A_V%F^'+X[X_
+M^CXLN+PX"XI2,CN5"6MC@SA=+?\`AJ^I./\`+)![2L6YLJM#4'TG-+=Y!_R4
+M]<HZ_N89\RLNK3(<9`(G_94+V!S",Z09VW5RX$R"9C8*.F*8.W$K<KAGC*SZ
+MELTTY,0J-Q0(>0&Q[+<K,!(`[[J"K2!F``%N9O-Y/!*PJP+7Z2/RA,UWM"TK
+MRW#Q``(6?6IAE2(D3DE=L<MO%Y/'<48@R<X1;'<QR$5%K#J#Z@;,^J#PA<`#
+M`(`[K3D$:2,?:=U+2<`-R%&#)U'/8(@&F26^V#$%5!-<"[.2IJ)$ZR"H`&AH
+M..>5-0@$`^^%4:M`32:0#$(M+O\`"5!39%,>MR+1_P!;E4W"OC,@M.,22>ZI
+M5#L#B`K=WO+HDA4J@:79'ZK-:1O.O/W05`2X"!`'Z(X(,EP@CNHWY(P84.@N
+M@'WW3$F(Y3R0[OREJ:701`Y0"Z0\&1!Q*41#6QMV3.,NW.F4@<9&=D#_`&WV
+M*D86B3@8Q$84<B(V")D;DY0*,X_[(GY=)B-I":9($#Z;)YR1O"(?!`@2`>Z9
+MPTD2/LG!YS&WPD\!I<6@ELX*<*%HB=]L>Z?!+2XR(C'"9I/`YW31!COE`1]+
+M8WD\X2<"1Q`.R$EI@`00<E.8\LG(C]4"<T2#L/;)A*)',SOLF)$B),^R0(B?
+MR^R!#`F3V3.$.)S]4Y,C`3#!GWD\H''S@#;NG<6N@1D[(2<D@C=,XP`<24"A
+MP+FDG)@A24VDX+@,')/*!I!;O]43G"=P@3XDC;/="V2T2!ON4Y$@"4P,-Y^?
+M9`S7=C(2+A,$[8,I2#J[>Q31Z<9`]T$A@S,8&$FY(`CZ(0!(V$[3.?A%)C?<
+M(O18#?HG`+6->Z(?C[(=Q^B=Q,MD^PE$,[5P<#DIH)GDI5!#N/@)-'K$.F$!
+M.;I9)<"(G!W48G5O$G8HW3DNV03!11D-T$`@&$@[88QN"DT"&S@E*/1C;LH@
+M00\P=OA&=R!C.)0T\&(W3D'5![JZ#N/!,(V.B21GN$+0TF"G.V=D!TX)U#E;
+MGA&N+?KEO6C#'`[_``L*1O'T6ATUPIUF/R8,YPI87E]0>$KYMQTN@YCL.`./
+M@+LNBTW5-,X$;]UY5^"5S5O^ET0=F`"3/LO9NA6^BFS&?V7&SEQC7Z;2AH)X
+M[+5M@T&.V%2M6P,<]U;HNR,@?59;BU(:TYGZI4A+O;E1.S$!3VPVD*?6DU)H
+M&)4C&2-X2:T1Q,(I:,3[?"H<#2()F#RF(G`10#A,[TLD&(10.I>C5&V.RH7[
+MM+'9V"MW-7T?FC'?=9=\^=0=RH.8\27[:3RW(E4NG!UY(:/S<!0>--+:X(=R
+MH/#W6:-D]_G/`U#!*\N>?K77'';,\16'\'U`U?ZAF"LZTZQ4M[X.UF0Y:/C?
+MK-"ZJZZ+A`9!(^JXVR+ZO4`."9)7&WV:ZKM?%]Z>I^&JE*H20ZGM]%P'X:6M
+M>RZQ7-++02/U"[#J=84^D>6#)+8CZ*CX2LC1I5*C6$N>2DWKUC4NG+?B]KO.
+MJT61EC>WN5@]'Z#>73FZ:+HVD@KTR[\/_P`=U'^)JTR2T1!"UK#HK+>D`RFU
+MH[0N^'AM_L]W_P!#'PX3'"<N(Z1X.>S2^X/P`%NT/"UK3A[J;3\C_1=2VTEA
+M):.P3_P[BT""N^/AQG.GES_]'RY_63;]%MFL;I8W`X"M4[)M.G#6A:5M;9DE
+M7&6[-.6B1NNDQD>7+S99=USKK5_F3"E%M4$`K;-!NK`1?PX+IC;E:<K:QJMF
+MXM,#)0T;,S&X*WFVX.XD0B-NP"=@K-);6&VR.HN.(X4IL<2(@96KY0+@0%.R
+MCZ0`?HJCGZE@7G/"B_@M.=,KI'T1,!JBJV[7$@A6)ISC[>IO^BCKVSRXZ9A=
+M#Y!DB(X0U;<#>)A!S+J%5C9(.$#608(GE=)5M6.'TX5.I9-+L(;8E1C2[2!^
+MJ@N*`$P-^ZV+NPT9`YW55]!PG5'=-&V'5M&5'9:(&=EG]5Z!95J;@ZBP`C<!
+M=$ZFW7@?9!68'$`-SLKTLRL>:]:\#TGS4HXGC_87-=7\)7EHPN:USF^R]IK6
+M\#+1GV52O9TJK-+F@@[@J62]QVQ_4YX_7@%6UK47N\QKA'>5#7T"F8&W=>S=
+M?\,6EXPN--K701@;K@O$_@^ZM];Z#"?CLLWP[YCUX?K9>*XQ_J!=V&RJWE(.
+M;@;[+4NK*O;^FJTMA4[ILM(VCCNL3<KKGK/';*:`TGV3M<`UX+=3G#!).$SP
+M?,Q(0O'^&?HN[YUT%FG6(!$^Z,;0),(8(@9]RB.#Z096D$&QG)_93T`T/@F(
+M&R@!AAD%2TCD>J!SA-)PTZ6D4P"[CN44L_Q_J5%3<\,`#G$!/KJ=W*^IN%>;
+M%Q<X=O=4ZK6A\R3"MWC1F-]U2JD`ZHRLUI&2!(^JC<8GG^Z-T:/?Y0R`T&#*
+M:0!+3S!CE,=,X,]T[FGG,IN<?6"H!D0`""C$%\'O!S"9TMXWY*$9@@GZ(IW`
+M`;_*3?I'$)-TDAIU1.1,)V!@>700R<2J@VMP,GV[)%PWD`QLE(V$I.;F"443
+M1C3(E#4WXSPGW.!OB4+L`0B':1_LIY'F#\H'OE`W?:/=.!P<\H=E4TS$G*6)
+MTB3!W)0U`9B1&Z<!LS(C_"$#X$C8>R3_`$`01!V*$@%WM[2A($3!440+8DP>
+M_P`)-]7'LFW$@@$<).:X$#(,3"(<Q`,)C@R3LD1D28"9X$;P@0@-C5&K>$3=
+MI]D.B&CU;IZ8/.?9%.W2'1$F.4Y'9XD\H""`08![RA`)[0/=!+H`HZB]KB3^
+M4`RFQIB!\%#"<?E`+C/(*0.T3`+H]Y1:<8.$&[MOH"GWF(&(D(:2`L-/3$.D
+MYE"3G(VRG8)S*C=^;V"`B`8[\IM()X)2.`3,?W3%I:[5)&-R@,8;G890.:"[
+M`]TXUQC>3E-SEKI*`FQHQCB>R>(&#N4)V`[CE.X>GCY"!VCU2,]D[@#.\I@)
+M`EQ(`[IVF!/*(36_U"!",@:-6!/"C:7-.F)&$[LY&P/*`P&^V=RM"P8YSF@3
+M/8+.8'<NTQ^JT.CG_P!2UI."X82CZ5_`*Q90Z!0)`EPF3]%Z]8M](`_1>7_@
+MY58WHMLT<,'[!>G=/K#2#.ZY95QQC2HXW^ZN4F8&RIT'<\*U3<(X6'18IM$[
+M^RLT0`/T56F9((^JMTHP9V14S8#"XCZ*"I5/F1SW4I&(!4+J<&82K$U"H8,G
+M,IJK_>4-,:<G)2JQ'*""Y<2/U5&\`@DX/"N5G2=\CLJ%T0>0%#3A_'#'AP<)
+MB<X7"=>N"2&,)'<SLO5O$5@RO0=.>5YSXDZ.ZD7/:V2"N.?B]NFL<],FI;U/
+MX)I+IGNJ@K,MGR-QE3BI</\`Y.DG^RT_#_ANO>W#7U&G3,Y7*>'*_&O:`Z1;
+M7?5*S1I(9O\`*[OH_2&6UJT:1,=E>Z#T2C96[0&B5IOIZ6B&X7HP\4Q8N5K/
+M99TV@@`;3LH*U%C7:8"TVTGF1D(769).)*ZQBLKR#'ID\J7R/3^671@+4IV9
+MQNK%*P<2"6R5H8M&T=@Z4?\`#/#IC!X"Z&GT\\L'LI!T\NC"I(YW^#($[J6C
+M;3(^JZ.GTX!OY0C;TYK0(:$-.=_@L;;H76T#9=2;`%L`2H*G3`'9"&G/?PX:
+MV0W"C-/3,#]UT3NG']5!4Z:X@]BJ:8+J<_EYW1A@B#QPM&K8.!(TE!_!G)VQ
+ME$9KZ0&PW0NH-+<P!W5^I0]6)P@J48;M'LJC/K46Z?3RJOD>K:%JBF9(C=15
+MJ0DGE!F/HASH.QY*AK6D[#9:3:<.)_=1U</CCE$8%?I_J=$2JS[)[28;(!70
+MUJ37"0`JU1C6B9E7;.F!<4R`0YIVV*HUJ#@[6`8[!=%<V[*GJPJEQ;`43!QV
+M*2&W.UVDN`@]U5N*`J>E[9"V:E"23'W52K3@D!NRHX[Q'X9MKT.+&!K_`&Y7
+MGWB?PO<V)<X,);G9>TU6%C28A9E[:T;MQ:^#\J\7MO#RY8=/G6_MWTZQU@@[
+M0JO'8KU[QQX.I50^M;MAR\RZSTRXL:[A49!G'LM7'4W&YY)E6<W!B)([A/,N
+MV&G9/4`D3QOW2C.28696M'@@',X[*6W!U`#G*!L!HQ[94M`-<2X"(*J::--\
+M4P"V?<!%K'^`_8(:#:?E-U.@QME'II?X_P!U/:&J"Y]+BT&`<$0J56)G<<1*
+MMW3?ZI$\A57B=GP)R>RM`U6T&V;'MJEU5SB',TX`[RH!^;=$\^K!!08=.8(X
+M[J!-;_U2.R$M<)D0>Z=I@YPED`D.SW)0"]I(DB?JK+`*-E4(>TOK```;CNJY
+M+B28VY3.RV!QPH&#3&J/U2`C,'X28<29^Z?7F0<'NJ#IR':Q,QC.R%P).`#/
+M,H28.TH@2'9.-E#9Z>VYE)XW!;/:$MAV0ZR3/?94/Q[)#3W^H0Y<#`VWA,"#
+M+H(*`G279PEI,;[YQE,2!DRBD:9S\!`+MH/Z(28<"6Z@/U1-TCN/=-5<=69A
+M-*$'?LB)<,SN(3.(@P)0D@`X/RH"DY,<)'CW*8G,?=(.,Q&P0.)C3LG)(,-.
+M=Y".JRFV@VHVL'$[MY"BU:=\]E=!W%SO<C<I,&#,^T'E,'N:)82)WSNGU8D?
+MHH&,9$9E,Z!RG+R#IS'N83AP@;#W"!-D-Y^919T%QVVSRF!&Z(N;Y<;'5R@$
+M$`R/T1``N,3!V3M>TM(."$@0?A`+H!@S\H2'''[HR3IC!^0F(@F?M*H;3F2Z
+M`4OS2(!]TX(CB1PD`.,=RH%`$22DV(Y/LE@G/T3C3&((F,HAVB"1]$M#MHG2
+M<PFV(`'W3@D2/\7NBGDHBXEA:W8J-Q),$B>(1@P,X]Y0)N/G96^G/+:@)G&%
+M4P3),@^ZFH%K8,Q"#Z&_!#K!J=.I4M660-U[3T>L'4FS\[KYE_`GJM*C>>2Y
+MP&H[DKZ&Z-=--%I:<'LN.3EU77V]00.)X4VO.)PLBQN6N$3^JTK7U#<P?=9L
+M:B_;N)*NT2-,;*C1@*Y2(,2BI2Z,)4W3(*9S03C*8PTS$2BI'1&^.ZBK/$0A
+M?&C_`#4);//V2@*ASA0NIR=1W5P4H;LF%'!(4T,R\I![(E<]U?IU.H3S*ZNZ
+M8!L#*RZU#SGD?LJECDK+P_1_BB[2")76=*Z73HT!I8!"NVG3PUH<0M&WM]+`
+M%I-*3+8Z1B$S[<G@+5\K`'9*I18UA)5TK,%J`)`"E9:@P3"FJUJ5-N7"/E9_
+M4.M6U!I'F-GY6+G(U,;6C3HTPV3IPG#Z+($@#W7*7OBBBUITU6_=8=YXO9KT
+MBKGB2N67GDZ=,?%:]&JWM%HPYJB'4:6K\X`7F%UXO#&F:F!S*JT?&]`U-)JM
+M!^5SOGKI/"]=;U&F'?F&R1ZBT\[+SFS\34*K`?-!GW5ZVZY3?4TBJ%)Y:U^U
+M'=4^IM$Y4M._:\#(*Y&A>^9EKMU8%RYH_,M3R5+XXZS^)I.;^Z<5J+O3@2N7
+MI7;MR25)_%/U#2\_"W/-6?VG2>73?S]$#K)AF`/HL2EU"HSG;E6:/5SR2MSS
+M?EB^);K=/;I,#/95:UA(,"`K5/J5-[<D%6:=U1>W,`+I/)*YWQU@/LBV7053
+MN:'(;E=:ZE1>"1"K5NGTG">%N65BXZ<DZWAIE5JM$3!P%TUS8$$@`1*HU;`@
+MDQNJS8Q:E!@;@'OV5"XI>HY,!;=:W<#$3"JU[:0=059L8D!KLY"CN*;7D#A:
+M56VD$8^57\HM.1*(RZ]!H!`;"H5:$/)B%NW%$ETZ57N:`TR1*K+G[ND"TF-E
+MEUK5S27[+JW6[<N(A4KVW#@0UL2J.9K4V/I$/R2N4\6^%;?J+7/@:O;_`+KO
+M+BV:#D2JE:BSZ+4RLZ37U\\>).B5;"Z>W3(G!A8P8`2"(CB%[_XEZ%:7])S0
+MP!W=>7>,?"E6SJN?2G3RKZR\QVQ\F^*Y-K00!.VZFI?F&<IKBD*3S3$R.4=$
+MC!,++HOT0/*$X/RBTCO^J:B!Y0D@(]+?\014%T&C())[*I4(U06P1A7K@`O,
+MP>-U4>X$1@-X'*"#3/J@X0.QQ,J4O@$MVV(0.#0!ZM]S*B(R!$@E"8(C@*1[
+MB7N>3E,6^D.<TP=O=!&WW._NGJ=VDM`2[XF$CF)CX4-!!C`._='2@RV(Q,RF
+M=$3&W"9F&QGZ\*FC@$?U$GV2<8Q()*4AVY2T@$,@^Y0-)!S'NDT-G'.R*8<9
+M2&F`#@\J&@M`,F4QPX@[*2&[@?5,6^@F!.T*P1N.<9E$T`,F8C=#IAW^PC:&
+MQ`P.5%)@`IZI;OM.4!GC3D=D6`US8,[C*3P'8;P%0VG`C'LF+27]D36X#G3G
+MDIBX!T@@>Z`8&"4T0=_UW1%H.9)!0N@$_M*@:8WRGUY@?J4MX'*3&M[Q_=#1
+M$G3,QW1%Q&<Q\I@T;;$^Z3`Z8,GX0T3A@8X[ICW('Q*DTAV=1S[IJC0(TN(^
+MJIH(/$@)'8NW]TX``R0$HD&<B>ZAHFX.-N?=.TY'LD1O'";\L'<E4&(.Z;)D
+M$(1)!S]TH.H;GW"&CD`9V1..8'VWE,&EQWCY38D0=T!8+0`(*:/3L8Y1.:9R
+M1MRF`@YGW(4".!L430]L5`"T;C";?Y&Z1F2V20$31.C5(3.,-`2`=&^Z3PW2
+M,S[*KHPTC(A24JFDZH!`X,J(F#QA.-LF1[%#3;\+=2J=.ZC2K4GQ!V[+Z2_#
+MOQ'1O>ETWBL"Z,B>?NOE<&#.K]5U7@OQ;>='J-#3%/V)/]USSQ^Q+CM]8]+Z
+MB-7YC]UU/2:XJ-!#XYW7A7@?QG;=0H-+ZH;4Y;,2?NO1/#O7FD@&JT_!7&\,
+MZL[>E47M#<E6:-1O*Y.AUIAAKB!*T;?J+7'+@1\J;5T+7MYA!7JR-A[K/H7`
+M<?S?53ZQO*JI6^KW"GI4S@`_HH6.;N/NIJ=3L1CE6"4-#1.?A,UYIG5@SB"$
+M+W@'?=0U:K3/L@JW>7$CE*RMM3M1PC#=1WGV5RR8`?=6(L4[66`A05'LIDR1
+MA6ZUS3I4"=0PN"\:^(_X?4VD<[83+.2+CCMTU[U:WH-)-1N/=<OXB\;VU&D6
+M4WR1OE>:]>\0]3JEVESH/RL!]:[N'ES]><%>3/RVN^/CCM.K>.JKI#7&/E<C
+MU[Q;?.)<"\@^_P#JH6]+KOD^HS&\JQ0Z`^JPAU,GY"QWVZS4857Q5?UC'K`F
+M)@PJUQU#J3G"H*CBWVE;=;PHYM:(.G?'?[+8Z5T6EH%-S,C@C">L=/:3IA])
+M=7O:!;5)SC.ZSO$/2KRE4-6DXP.R[K_@[:%0.:W3_97*G2V5Z&DM!/=)-,^U
+MEW'GO1+R^H/%-Y,#NM^EU&XIM\S45?J=":VM(;'O""KTPAT:#'"Q8U[;;'AS
+MKS]3653@>ZZZROJ==DM(7GM&P?3:'#$+6Z76K4:@:7'"2V+K;M#5WAREIO?/
+ML%C65Q,:C'NM>@6%HV^JU*FA.JNU0)1M>X['9`Z"TY^B!WY/VRM,Z6F5W-Q)
+M@X4S+EXR'&5EE\#>5(VIZ<.A39ILTNIU6`2?U5ZWZN',+7+FJ;R2<IV/(Q*W
+M,[&,L)75"]HU!N/?*E#:-5F'#X7)&X<UI+7J2VZI4IF!4)"ZX^9ROA=#=6-/
+MR]0`^RS;FQS$?9%;=8U,`<<*V+RG5;!+9]UWQ\LKCEX[&#7LBT&`9E4;FV>!
+M,976NI4ZC<*C=V6#NNDRCE<7*.:0XR,J"Y@;-"W[BQ.LDM69=6NX`"TSID5Y
+MF(4+V@L@MB5JOM"&203"IU:0!C9&6'?48.V%0N;.6X"WZX(&P@\JG6AWHB6K
+M6T<M<T"'D`'[RLKJW3_XBD]I8#/LNON[7/RJ-S2TMTZ?N%>D[>&>-^@5:-R:
+ME*GCF%RX9I?!!@<+WCQ'TUMQ:U&.`)(PO)O%/1JEE<NFG@[0MV;YCKX\_E5*
+M#6FDTZ@/NBTM_P`8_5-0I12`!'V1^6>X^RY:CLJ.KUJ;*U*G5=396:&5`#AP
+MD'/V_14GP#`D_P":M7@;JF/]54J#B=\[JT`2UI`G_)';5*#!4%>FY^K8M,05
+M&R`PB.VR$.]4Q*E@,TF/>UM$N<7;`[J*."XHW!NJ0"UO!E"8(`.4`#Y^DIL"
+M)._;*.(.ES3(.W9,6MWDQ\H:"[V)'PD08TS(Y3@`S&!W"1`+<X*0"R2[)/NB
+MDAPW3M#=^R48VGX0`?9R=X#<@G/&Z=S00(&)A._3P#@(!#3IDN$?*>2)D[;)
+M@V#G>82!D<_912!@Y.`G=$C.`F($@$004[P)W]T`@2[*=QQ)^GRGB`29^(W3
+M:0002<?1`Q@QI^D).!F>/V3F0()(0N_,1G/'=`+B8B1',)LGW[)W?_<?IV2F
+M'#)309H=].R=I=M.#E.=.G=Q[(0V02#LBGU.+B<92$@?(W2S`D1"8S.#QL$!
+ML+IQWV3DQ$'9"P@-S)^41`P=39.,<(@9DR7#.8E2-UAD84;0-S@#V3M,#)D`
+MH#+O2&D9]E&XSB-S]D;3#?S2#&$X8"T\CN@`.],N'W3M_/+A',IRTR`8&R8,
+MW`+23PKHV>'9CG.R$.,SCX"-K8P3OCE!'IG[[J`B1(C8[2G$EI`'ND9D;93&
+M9S$H'GU&!`A,XNS`PC:"3$A`X<M."J!U9^46LZHC9!._^:(&<#/N5)`+7'5L
+M"3NC)`@X^J%\AP(B-L=D37<P,"/9-`B_@@83^81$8/*9NH@_?=-,CO[`HK3Z
+M9U2ZLG--*L\'V)7;>#/Q%NK2X:+EY<WN2??W7F[2Z03@GA2,G@F#NLW"9=KM
+M],=#\?6%^&!MP&NC8F%V?1>LFI3:X5<?*^0+2]JT'!U.J]KA/*[3PQ^)'4.G
+MT/*J.+QP7%<KX[&;C^'U3:]9VE^?E:_3NK-JL$/E?,O2?Q4\VMIJ@M;R05W_
+M`(8\=V):T^<,C8X7/5G998]OHWI(&=E,V[;OK`(W"\UH>.>G>2)KM/O*+_SI
+M;&`VN"1C&?[)N4>B5[T-F'?""A<^>,.,''RN/Z1U5_4BT,G2>2NOZ=3;2I#6
+MMR;9VNL>6'\TC]D]UU"G:T2XN&%5N:K:=-Q)^ZX[Q7U-Y8ZFUT`*97U:DVF\
+M4>,0POITWKC']5=?57>8=S*C?:/NJ^27%:%ET.'MAL^Z\F6[7IQD@;>QIW#-
+M6G)Y5BUZ'1G\@QG9;G3^G!M/0=BM.C9L:P`"/E6+_P`<]2Z12`/IB.5<M.FL
+M;_\`HP/HM@46@&0(_=)K6MSP4Z)&1<=+80?2"=YA5Z73-+I#1[0%T+FA^^_9
+M'3MVZ.,)VO3FZUI(+7MDA/0H:29&!PN@KV;8,#V*A;9@G9+"5AU+9K7!V,;J
+M,VS702``#PMNXLQH=ME56VWKF)E9K49-Q:CRS`_U06-LUY$B2MPV8>'$]E5M
+M[44JF<05&H@T.:T@#A:/3ZCO*#3NBI6S2Z2%*VV,^D?"B[2M,C8?=([R%+3I
+MP())"*E193:0R0)P.RK*HYA)QO\`LH7/(,*[59I$JI787.V1=&I5#\(C5]4!
+M0GTDD[A1O=I((,PIH6WU!I$#=0.<"R1(^%%K+G`26I`D:H)DY24T-]9S8TJ>
+MC=O8R0X[\*EL3G!4-2L6OC/PM)9MTEEU8C#C';*U[:_H5J8@B8R9W7$4WDLP
+M[Y4E"[J4LASH^5TQ\ECCEXY7;U&4ZK2&@+.N[`#(;NLNSZN6.ASX[Y6O;]1I
+M7#1Z@O3CY97GR\5C(N:)8XM((E9EQ;.+R<P5UC[9E:7`A4;RP@#&/E=Y7#+%
+MRMU0=I/99E9NA\1GA=1>VS@8`$+,OK?09Y"U&+-,>H/3J+=/UE4KVF'QZ<E:
+M-U3<7%NK2!^J@K,I@8=!"LX1SG4K,E\'DRN;\4])IW=B6%@<Z-NR[CJ%'4#I
+M[\+*N;4#&_<]U9=7;.GD%3P_58\M;L#W3?\``:W;]5Z?4Z30>\N(R>Z;_@]O
+MV"W_`!=?W*\#NZD/APSR=U4JOAT@8XY4U?\`.2,859PAW8>Y7.NVR+H.TB,%
+M,7##HC'>0FQ$B$S@`T=SQMA0V,U6;-!@C8H?,TD.8=MI0%H`F9A`(,AT^R"5
+M]8OJE[R2YYDDF24.L3JSGNA@:9!Q/^PE&(S!W0V(5#N`2"93O>#F<GD(/3R=
+MT.`"V-C\0AM*7@MD[[$RG;4&PD:0HP`>8]O9+$S.P022,DG!2#O1$X''NHY`
+M[YV2:(S.>Z`@0'0-MLIVN9D\;*,@S,XC9-`!DN@'A%'Z9U<#$HG.86CG/*BW
+M><E'JW`.3RB;.'-V=(/()V17#6MKEK*S*FQU-F-O<!009,0$HD';Y)1=I:=1
+MC7`U`'CELD<>R'6W0TM.1PHL@`0(&Y3`8,9A#:1SFAN\CO*4LC\V3R@CD8"1
+M&<X@("V$1CV*)IQ!A1Q)DGA(-,?JIHV,/;J.8!3ES"Z3IGVQ"`M(WV]D)C)G
+MX"NC:1I;F#PD).9;@[J(Z@8//9.<`'ME!(UP@B`>9*(P&@%TQNHH@9VYRDT2
+M,J&TNKT_F&/T14:C09G'90`&,?;NFC.VWNJ;62[UR#(D)FS&#C]E"&.B8._&
+M82(AOPH;3D`#?9"/SQ,R@9L3,':"A&"8P=O=4VL?TB=C[)B?4,S.)4+2YVD@
+MP!LE,;*&UAI$!TB/=`XC!^ZB'IP23W3.RT#GE71M)N![)X'$Y*A`(?F8GNG!
+M(GV]T5,WL.$F<-])A0DGO\P48,N:9G"(E#23,_24X+2!`CVC"BUG,DMQPFU8
+MR0"-LY1=IZ<9''S!2:2)$P.Q4<@/W,<Y3:SH&=_=#:>3K&))X3AQ)D;;C*AI
+MN(V.0B$ZMX]DT;3-J.;D3@YA7+?J%RT'2\YB<K.:2V"#F.Z*B20-.2EY-MBA
+MU;J#RUIN:AT["=EZ3^%5AU7J-VRK4<[R1./NN9_#SPA5OZK:U8$L]QNOH7\.
+M^@LLZ%.*8@#Z+%D[9S\GR.O\%]*9:V;!O`75@-#!(@0J73J8I4?8+.\3=6;;
+M,--A`G`6,LM,XQ7\5=6;1I%C=SV*X[1<WUQ.8[*6GYW4+HO<XQVA;=E092I;
+M+R9WV[>G&:!TKIS*(!?$K5H46X`&%%2:YYX4]'7OL>5C;K(MT6L`G2G>0'"%
+M$R3N8Y1!CC`F5-[;D.XZA&`0F;3^P1BF=)[J;R@Y@/[J=KPA\OG,*Q1;!C>$
+M@SB"/HC8P$AORKK25)I:3D`=_9(6[=)@#Z)B0-A/"-KQMA:E9TJ5:68CY4#[
+M?2"8RM%PE^P3:0Z,*5J*`I13_=4G4?YA@8'=;GDRT[2JXMP2#A9UPUM4MZ`@
+M$_JIJ5-IF0K#J):.WU0-9$08/92B(,=JP,!3-I_RSW4]&F3EQ^JE%.7#N4D&
+M959B'#!Q"J7-*#$;=EM5J30W;*HUZ4O@<;H,FNQT$D0J[R)C<[+3NZ3B-L+,
+MK-WWQRHL!4R#$9[)A4+1,92;)$&$&\JE-4<">Q]TWE!XF2/JD]P!@CZH758:
+M2W*=()K0UF#,IY;$"%!YH&"=NZ'S@';_`$2&DSAWW1T*]2B):2(XE1MJ@GF4
+M;(>V"5N,6-CIO6"QH8]Q$]EN4;FE7HX>,KAZC8!(,?W3V74*U$^EQ@<%=</)
+M<7'/QRNMNK9NIVD2LN_M"701]5-TKJ].M#:KOKW6MY5*NS4UPSLO5AY)D\F?
+MCL<1?VV@06K(J4'/J3)#5VW4["23I,'E8E]9.`)&'+MVXN<J4X>03C*KW%LW
+M20&_5:5S;Z'Z@JM5P+M)V"#--"F#!R4WD4NQ6LRC1+`2""47D4?=.%?)%<D/
+M,#.T`JN]LNQL=@58?!=),2>56?O.XXRH])J@@$.TRWLHV@@',GA&]QYR.4SO
+MRDR,<(R"1)`W^4B(V!2G>&IB3JVX[HIW:-1`.`<80.B9!(([IYDP2)[IB2)Q
+M]D#.=/(^Z)H@:L'ZH3&G#3O\)<B<E`39:,Y]QPG#1#G2,8B<E"UY`<8$GLB9
+MP"WXA`L-,&9&$],-Y=$<QND0X$AS((.QX38@J@2#N)W1-:-.<#ND#C;"=VG2
+M1[;J`,D\!(F6$&2!P4X+9&K,_1"XM#L9212@=I([)J@.K?YY2$1L?ND70TB,
+M)$,0W`)VQA,&[P41(G/ZI-B,(I@0`1PGDG)^$SC!$<]D\CB90.T'8'<[(23_
+M`$[%/J[DQNGQO*!G<9"3M)$_W2!@2'1W"0@\?K"!:6YQ$\)I@<_*)Q]&YC@'
+M9,=(=D?J@1$B!GNF:!IWA.(!F<)0W,2F@B!&9RA!&F#PC^<I/T%H`$)H,PP?
+M?E."9W28!`$P">R8B#G]$"=J(_U3M$X;B>$YD`MF0=QW3-@C+9(Y0%I;K)VC
+M8=T-0R[_`%3AH<[N!PF>-),B/8H'8).9,(!IF/;!)1$ZL>R$B,<]T#@0P'?@
+MI@9/Z).,-B4FG&V_*!P`1C/LD-0!]1CV3,.(`^,HFF),8^4#C5N/L$[9/IC,
+MY(2$@8Q^J*1(:1]$"(],0<<)J>F-DY@M$?ND(F0J&/I<5+3(,3D$IG!I!.QV
+MA.&P-ON4"(!$#E=A^'7AQW4;MKZC)8TA8/0.FOO[UC`PP2O>?PZ\/FTM6`L'
+MRLWIG+/UZ=#X.Z*RA2IT*;,-'*]+Z);MH4`-`"R?#5@U@!(B-PNB>6TZ>-EB
+MN<YY1=5Z@*%%P:1C<RN)ZC5J]1O8W"TO$UWG0WE0])HAPUN$GW7#.N^$VNV-
+M'RJ0B)A7K>F7#U)K2F2-ONKU"G!CLO/>7IQA6S`QHA3&F0"4@`'2<*1[FAN(
+M6:Z2$P"6XP5,T#&%`Q[2_)_56&'MNIMK22FUNG_52M'HQRH&$ZO2K5`#3_=6
+M4T`,,0!",LT@3^BD$.F,)G./*K*%T\H28B``GJG_`*E&7`[S`02XB)1-B9C"
+MB8X1,DX1TG09/*FQ/'I@#Y3^6,N)@CV3-)(D(Z?YC^J*!](O?`0U:`;D?16&
+M-&8'Z)ZE)KP`X2.45'08W1&".Y5@4QH,)4F'4('W4^D:)'*1*I5*0!DSE5*U
+M(:C&Q6C6:!.)A5JC6S);E18S*U"096?=6S1_3!6[7``^50K,#R3C"G2L-]')
+M)&`@\HP?9;#K=A&!@JLZD!(`0K(?3)!&WNJ]1C]/I(^@6S5H`B`,JM_#:21^
+MB:1EO;IG!*A#'ZCO'*TW6WJ,A)M!H$&$TNU&DPG?CW5BG3@;[J846L),<HF:
+M8$Q\*LVH'M.QS`55WI<9'U6D8C>)5:O2#G;;K4K"FUY:[4Q\%;/1.LN8[RWN
+MU`+*=;$B<J&M1<W:<96ION,927MZ'9W%"\H2""85#JMB8)CX7,]%ZL^V>&/<
+M1_OX7765[3O*(@CW*]/B\ORO+Y/%KIR74K0LEQ'ZK%K6O\S6Z3"[OK/3@6DM
+MV7-=4M2*1TSCL%ZIR\MX9#7M`B?U2\QO<?=,*)Y!E/Y/S]TTO#Y%K-)F71]5
+M6>0<`!6*YEQ;`&..56>1Q"E>H+@2/8)/!D2-TSMA)QV2(F'$X]D-&<,X[(2)
+M))&41`'9"X]T#$&9@&=BD9`B"2$PSMQPB<US1+AID2)Y1`$&1.Z1!X!!/&R=
+MND9<X^R8D2/[HIP)]61"(2.#'>4)@#_1%3=+80/+MR23WE-LTZ@DZ8&04TX@
+ME$.!Z9S!2W[I9#1.4I+M.?:.Z`?3S,<)`$DX=/`2AP<<X2/<[#;**>J^GJ;H
+MU;>K.Y00TGL=DY!C4[Z2D3)D'/NA#`@..GM&R0)`^>R=I+<S![)R22),H&@`
+M3E-B!E.23DY]TG0.?H@89=CZ!/.,9[I@Z,#*0)$^Z+H[-B8D]PD<MS'=-,@1
+M]@G;OB"B%@'\T=DQ[!%D[QGB4P_(80-&8.",(C@;`I-D@`&9X2&\0#V*!MAE
+M-N,\\HX)'!.^R$N&G;/[(:.&XX,?JF(,SLD)&0TY"0$MQQNH:.#B(&4BT<$'
+MW3@B-Y`V"0#]<M$&,C*!`#<\]D\M%0.C4`>4T>F"W=)L!ID?14/=.I.JEU*G
+MH:=FZIA1N]@B#26ET8:)WV0F-!Q]5`Y!<=1&!RF&Y@>^Z0(E.8!$85":V3D'
+M"<`:<#;=+4T99(/)*=H$=^Z!"1$#*<09F?HD1MRG='(DSW0T8>SI]D]-OJ@D
+M;[)@,;F?E&(&^_LBB.6P>58M[>I7<UDD@;*!NDD9,_J5VGX?]#JWMPTFF0T'
+ME&;Q'5_A/X:T-;6JLDD"%[?X8Z;%`'1"Q/`_164[:FP#``7H/3:#*-'3V6<J
+MXR;NTEK1--L]O95.LWS:--T.B%-?W+*-)V8A<AU2Z?=7&ACC!*Y99:=,<=GH
+MNJ7=QK<XD$KH>F6^&B%3Z):!K!A;MM3T`8^<KS97;TX8Z3T:(:,2`IF-QG9"
+MT@-P<!4NJ7].WIDS"Q:[1+U"Z;2!,C"R:O7*4D!T%9?5;]URYS:<Y[*G1Z/7
+MN8<7EL]ESN[>'3<C:/6FR2Q\QV5RRZR'D`N+21W6=:>'SI:,GW5D=%J-R&N!
+M"OK4]HWK6[-5FH'Z`JRR^+?S#3[K!L35M7!KP8^%J4GT[AHV"FMK&A0O&N/Y
+ME-4K!W]06-68^B8&J!V34;PZH.RSNSMJS?34>Z7%`V=63`4=&J'"094K3Z@1
+M&5N7;-@R('I&ZDH@ALB$PRTG]$3()&(5TSM-3P8S"GIM`,A1,(.5*#VQW32[
+M6*8!@2BTMG?.TJ&B1JVE66%IW"!1`P[[(RW!F4@0`)&/E-J&<H(J[/JH'4X^
+MJLN+2TF=N`JU1P#8,E18JW32-C*KADF"%;())2;2C?=.U4JC-(5=U*72X0M%
+M]+42!.%%5HC/9$9M6F&["0JM5H`)G*T+EI$P-N2J5<8/?E39I1JN]6X5.ZK%
+MA)/"N7`QJB#^ZS+MI<W)(!X4BZ05>IX(:[([JG5ZH8!UPH;\-9(:!/*R+IQ)
+M@'*9$TZ&WZDW5)=,JT.HTW-`7$UKEU/)<<=E2NNN/I,($^V84F5B7';T,WK#
+MC4/;*FIUJ+VP0"?E>3?^9;IE3,Z?=:?2O&#&U`*U32??*U,_\2^*NZOZ/]30
+M1_=6>@=0=0KBF2L;IO7;6ZIC2Z9&_P#L*=[VEVMI'>97693+IQN/RO1[6LV[
+MMPT1)&95'J=DT,((^BQO"G4SK:U[L]UV+A1N+4.!X7J\7DWQ7C\OCTXBK9-%
+M0Y_1-_!-[_HNCJ6)+S#"A_@3_@*].W#3X'N-RZ8G"KDP2"#&RL5\F&S([*"I
+MZJ8@F1B%-O2A._<#W2.H#V3NB3N1&2FJ.);OM$?"J<!P9@_KNF<"<[9A.6Q@
+MQ*8YQLHIO[[IG;03/U2(W&>R:">3\J!#,G*7S(A-M[)\=\<D(',1DR`8R433
+MG]X*`C)!G='@;<JAIGCZ%)P'"1'SC=$/RSNG8$D=\#(3">Z=P:!D^^$HD2=O
+MV0)F#R)3NB,QGNDX``9'U"1'I,<(IA^4$B>Z9\``C]D_],S!A*0/^H>R`(SB
+M<>R=@)G.Z3@``Z1GA,#/$(%IC'"49!V^4XR)/"3))$9[94"(R2(3/V_=&0#.
+M-]\JUTQ_3J=.Y%^RJ\NI$4?+,0^1!/M$JBG!C<=HW3"=6#D(FDZI`GZIRZ6P
+M2/D;H$"TN`<(2<,P)^$((@#;Y*=TD8?C_P"Y`\C3_ACLG:X@[S*;$$S@=RFU
+M9E`1)X.=D)!C"67&>P3`8,9*!`^J28'<<)R0-G<]LIB!/")C27`-&78B$"&/
+M43@IP#!,'2>3RF<V)U-@_97.FCS[6K;%S6@`U6$[D@9'U'[(*U3`$['OA"<X
+MQE.-,R,SRD[3J@;(`,C_`"3MB/5^Z3@#!VA,&R#E#9`Z3@;B-DWJGG"+9I2_
+MJS]4`QB3RB9&9F/V1%HD!IG*<-CT.G4TY:$"IZ=>DR6]P8,IB0#I&8[%.6Z9
+M;I(/,H?S/COR25%$QQ+H(1F3&G[E-0\LEQ<7#'')4]M3+W-`R51?\.]/?>7;
+M`&D@D?NO>_PS\-MHVE*:?J@25QOX.^'75`*[Z4S!R/A>[>%NG-H4VC3&%,KI
+MPROM=-+HMCY%)N`"KUU<BF,$2G>YE.F-6(7.^(;\'4QA@^Q7*UJ17\0=1=5<
+MZFUQ2Z#8N)#WY)]]E3Z=;FXN`]V5U%E1;3HC'U7FRRV]&&.EJW8&L;Z=E:%0
+M!NX5%U9K6Q(V[IA4<YN(*Y6N\BS>WK*=(Y7-7?G]0N-+7'3^RT;MCZSX.%H]
+M.LZ5"V#C^8Y6=;:WIF].Z2*;AJ))706%FP``-5>B[75@;<+5MW,ITY<8"WC'
+M/+)<M+1@:!I&%:9:4Y@M6%?^(;6U<9J"1[K/_P#.M`5L5!VR5O<CENNHN^D4
+MJK(T`#NLVXZ54MI<P$CV5OH/B"VNP/6"3[KH&TZ5Q0($%/69-XYV.1I14ECQ
+MD8(*I]0LG4W:V;>RZ+J?3#2JZV<=E5%)E1A:=QW7++'7%=\<M\QA6M5[:D1A
+M:5JX&">56NK1U*H2V?E-;EP,+E.'2S;7ID%L0$MLZH"BH$>7JG=-4>5U[<UJ
+MD\'=RFU`"`LRG4@R2K+*X(W51=IU(;&I3MJ@;'99@K09+AE%4N1$"")R5EIH
+MBI()F$+GQN539<0!D3\IZERV03G"HLNKL&^ZA<\..3E47U'.,CNIK9WJTJ-K
+M=-@+D]:0?9-3<!F$3G`CU&%649<`8]MU#7>W3A'4(+L9&RC>!&<H:4KD@M)A
+M9U8')C&T+3JL:X$!5GT@UL&,*:V,VJWDK.OFAQ(;_HM:Y87/V5.M1(.RD@Q*
+MED'DR<'.5G7UK;TY[KHZU%P8=(63=60?5.K.54<U>6C"XN#C]`L3K-E+"0S8
+MKN+BP@2!C9974NGZV$'93U-V/,>LNJT7END$#W61<U*A)>:;@!R.3\+TOJ72
+MNF^0T>5_,`AQX/NN3\06MM3)T,<Z<XG"W+)PW[[Z9W0.O5K9[6ZX`&>?[+OO
+M#GB)MT/++B';1_L+R/JE-S'DM'I.T!2^'>LU;&Z`.8Q!6KX_N+%LO%?072KK
+M14:X;E=]X6OQ5IM#SF(7C'@[KM.\HL!/JB8/_9=]X=O13<TAVZ8WZ\_DQ^5Z
+M0&L(G4,I:&?XPJ5I=TS;,/<*3^)IKU?NO+^T_/.Y=+W%I$959T`[J>M(,0(V
+MF=U7=(?&5Z;VTC<09`XY2JX`;!VS*?&L`NB3DH.=\;)0F@D3G?=,\.!S@^_*
+M:21OE(N)(!=/RH&R7<;)`ZN3D<!.R"9G,[(<;YP@6)D%WLD#@$.^Z4`F9,>P
+MA(#$?N@/`<07`\2G<X08V*%O],IW#TB!'=%(D3(Q.Z>1G.Q0_P!'O.<HL:=4
+M(AB)_JSV2,%H2<?Y@`A.0)'$A(I@.Y3F.#.,IB0#B/V2`)J$;#]40Q;G)^XW
+M2[B=TAI+^XXRFIP,@(&(EV_Y<)&0)`P$9PS!R3&$!)P3(A%+$23]$[9QF`F#
+M)P!A(@8$P@)O?^Z1:"TQ`)Q\IRUHI?F:3.R%X&D\QE`Q&./NFGD1"<`D8!A,
+M,G,Q[H'/R.Q3'!DQCE,X;QC,)VM$-<1,^_9`61C`QW2:(!.#P@(B!C[IP1.T
+MSB$!.D-$;I-QP2F@:=X]DXC7^9#9$`),:"=($SPF@2<(@&Z@9QR4$A?-'0Z(
+M&=1&4?3'`7U&HX2UC@YPC@&5"XR,$P[V3#?'&$!W`FK);OD#="-QC<X1!V`3
+MVB24S@=/^J)L,08YC!"4278,@(B#(CD(2,X/**8`8P8W3D8[X2`W))@F,IRX
+MG$J!?TB!SV2$@@B3';E/IDF)CB4;6PW/.RH0))DG+M\IH:1DF`4AJ)B2`?=.
+MT$@-YE`@PDMW(_5=E^'/AYW4;YA<UT!P,?9<_P!$Z=6O+EK&MC(SE>^_A%X:
+M%K:L?49ZW9_92UC.ZXCJ_`'0:=G0IL;3#0!L%VU.FV@WM"@Z?0IV](#:`J?6
+M^I-8TZ'?<KEEDSC#=;ZB&^EIR0>5SH=4N:Y,[G=!7J5;FK(G=:G2K4,:"3\K
+MSYY;>C#!;Z72\ILN&%>KW(IT\QLJP(`+01E*C1-5^J?NN5=I(9M1]5\SOE7:
+M;M-,9(,*2VLQL&G.ZLUK2&&!!C*QITE9U2JXU>ZN![BP1C$8*J_P[F5@8WV*
+MMTQI;D9(315RQ9Z0X1C<]UC^-.M&RM##@#NMFU>T4R-B!"\]_%-Y<2!\_.ZZ
+MXS;SY\./\5>,',8Y[ZPF<"?E<A7\>5_-.ET@']%E>/7/IW08`=)Q^Z[C\"_P
+MUZ'XDZ"[JW7*]8L-7RV4Z#@#L#)D>Z[SQX_6-^LW6A^'/CJJ^JP&J02=I^%]
+M#?AYUD7UJPN?F!SLOE?QIX9'@_\`$3^`Z=4?4MG$/ID_FTDX!@;KW7\%;ISP
+MUAF,?V7++#URX:W+'KMS2;5I;;KG[^D;:L2``"9E=-2@T&&(D+*ZU1:^FXM&
+M96?)-QT\=U6-69Y]/4(E4*E`M>8^JNL?Y;P#SA2.HM>=0V.5P[>GI3HU#^7?
+MV4E:"W/'*CKTS3<2!`05*X#(..Y5G"6`,->/A.ZH&[''RJU>LT2951]R"V`X
+M+>V%]]UIP24A>-`!!SV60^L7$29E.R9P%A6Q_&`DP2)2_BIP=OE9;6NB#REJ
+M+9.P_=%;3:NJ>ZFMG'5JGZ+$IW`!''.ZNVESD'ZJ+MO4R-`DH75`2J(NP6[I
+MZ58ND?JKM(MM<&NB=T-;).1'LHR^`)2\S8?9:A2:UN3LH:C2YY4KW`@D'/RH
+MWNS@_P"BJ*U2B`<M'OE0OHM/?93O?ZB21'LHO,SW4%2K1&F(E5&VC"XD[RM-
+MSVZ5`ZF0XN_I(51FW5NT>ENRR.IV9+3S/"Z&HTZHX56XI!P((B$B;</U&Q=4
+MEI;@[K'ZGT2CY)<\Z3W.5WMY:NF0W?*P.N=.JUIW'NKZRL[>:=<Z9T]C=)R9
+MSNN1ZI94Z3M=,F"3$<+U>\\.VOJJ5@7.))DSNN;\3=`M?))INTD;2%<<KCVW
+M;*PO`?539W+070,"%[%X=Z@*K&%KP9@@KPBO:NM;LN#@(,2O1OPYZBY[`VH[
+M((&-E<YJ^T3*;CVNQOHM*8UC92_QW_6%SMK=4A;L!)F.ZD_BJ7<_=.7+A\97
+M4$G<?[[*!PEA=!`&)!V5BX=AV?;Z*H\DN&,;A?1KA0/$R1A#48=$R43_`%'X
+M0SQL2B!`]<">Z3@!!$DSRG+CI`!PF=)`QL-^ZBFEQ)=RG@`Q)DH0X3(G*<.(
+M.)5#P8&?LG:3$1DI,)&"XP0FGW_5`8#-&"9!VXA(^IQP`$.-0DY'9'J!,.)#
+M9RE@$_\`+`G=(B!EVV(2`]6#A$0)W!$2B`R&X.4Y+HB?NG<0.,).@"/K\(&U
+M&>2>4.2#D=OE/(`:3N<)SD`@B>W"*0@.D)F@EV-Y1`C3&`)V"3G1,``H$0`Z
+M3A!$CL$3C!R`3\I`Y&=NZ!A.@-G!X3&200C=+8$C*;4)WCL.Z!MP)CY3QB9V
+M_1,2)WPD\C&-T#.DY.\II@DD`GW[IM7J[)SET[(IR9C`$#;W2`)@3!2&!@"4
+MQ+-,YDH$<>G$1E+`<(XV3-P?=.</VG"B"DZ3.9*8SJVVW3ZAH]TA&G!@JAFE
+MT&,<)Q.DG)">)!]\X2C$NR?9`P)TD'(E.R3)T_=,V"#@R>.R=A8706F3L`@0
+M)D1,S&R-XGX[(0<2/L43W"((R$`@F0`)3$SD#`12.1$A,<F.Y4"R<$!-D';"
+M-D8!X3^D@X,0J$QIU?[A/480-Y,;2K-&"S2?H.R,4QDD3_=39I4$`Y'PIK:B
+MY]1L`G40("E;2R`T&3Q*ZOP'T7^*O:9T@@D?1$MU'5?A3X9;7:RO5H[$1C?W
+M7MW0K>E8VP,M$#E<CT1]ITFR8`X-$#<_[[*'J7BHUII6[RYNTB<KGGGIRQQN
+M5V[+JO7&!NBFX2L1]:I=.EQ.5D6#JE=P<Z3,86YTZD!$S"\^63T8X:7>FT&L
+M:'$;=UH,=C?8J&BV:8#0K5K0+M^%RM=I#TF%S_>%HVC(`'9-;T&Z1)QV5FDT
+M8(*RUI9H1N["DR[!4(@;'=.7EA$<HNDAH-<2-N94;[4G\J)E729[*:C6!,G?
+M=3VTNE-UH\;3)X6!XCZ#4O!G)RNS\^D<0GI&@1F/E;QRC&4V^=OQ$\`=0K.-
+M6@PN(R&_=9/@A_C7PH*E.UI.\EQDTWM)`]P/HOI^M9V%=OJ:T\255O/#O2ZS
+M8+&2?8+K++-5C6G@W3[3J'5^HNZAU4E]=[L3.,^_"]E_"+IKZ6EY$`QQ\(AX
+M5L:;P6TVD#;"Z#HC&]/;I&D!8RR28<[=I3.FD![1\JKU!H=2,1E9(ZL(`+\#
+MW05NJZFX.3ME8RSCMCARSNHO<RYP8@JQ;U=5,&=MU2NW.JOU<3/=#;57-,$1
+M.%YYQ=O19PN]0JT_)P<K!NKD1E6.J5#!))'PLBJ`YQ]1,+6]UF22%6N'.P#A
+M`P%SMC!PG93D@QGA3T*328.YY6ML]AMK<N?);GY6I3MP&R6P0$=C0B.<*Z:?
+MIR``H2,NI3TDX]U'7I.(P%JFB!E"^BW1($*1JL"Y!IGU3E#;7<'\V%/UL!K?
+MCE<_6N7,J:0=UK3#HZ-T7P9PKUK7]_=<Q85G",K8LJI=$HK<\_\`E",Y0&M(
+M_,J>LP/TA#YT'3Q\K;*\:H([?L@J5=(D@J$.!V=[[IJSI&#*!5JL`F!["<*(
+MOD\9"'U&9W0M!C.,;**D80.<3LG>[U1Q*B<^-MR4SB=."`FTT*070<*-S!).
+MQ0PZ<G?A-#B"<_Y*RI8&HQIQ`,\E5;BU8]L1^4*VZ.^5%(D`X6MLZ<[UCII<
+MQVEL\K@O&'1:F@N8#MG/RO6ZU,/;!&%C=8LJ3@X.#=E>*SO3YSZK3K6US%0'
+MT&22/=;G@WJ#J5W3;,-D3&W"['QMT2TJT7O$!PG^ZXFPMA1OAI$M!WGW6[JQ
+MJ7;UBROV.M*;IF1O*E_C6+G+"NT6=($G#0%+_$-[E8W7-\XU]#JN#`YY*J52
+M`?1'NK5?#N!.ZK5A$C$;1"^C]<43R9/?LE$24[],3J@SV0N_)^B@8G.(2;#B
+M9@XPDT$X`^R$MAV1D(%$)",SLGW$%+2=])`]T"D:0($^R<QP"D6ZG?ETE*#'
+M&-U=!XC)/*<@:>Q]TH@"?V3M&=X^$0TDQ@8"=S1&=B)3R`-`!GNF('Y3OO**
+M;WTQ\)&#G!^4OZ)PD-\8'NB:,!)C8)$!I(TIV``;_?*8S.P^RBIKAU%PIMHT
+MG4H:`X%^K49WVPH7;[9/9.\&-XG@'=,9+AD&.Y5^AMC),I;X**1)*%D:23,H
+M'='/Q,IH&DR-]DPY=)GY3R=$G"!F@-)G;^Z0@D\A.&NXS*?4?+#8&.VZ`,!Q
+MB/A(D!I:0`/;=)P,9,2-I3<>R*(1$;'O"8M$1!'RA^3"(S&Y"@&)^B7>(!*(
+MQI&3JY28`7?YJH3)B&F`[!2#6"?4F]IPG<UP&D_*!"`"!E$6F-HGW3-;!W'N
+MGTSL,'E`@)S#1PD&@$3_`-TQ&0!E$X''Y8WE(':`W/V/=/I))@),$@Y$^YE)
+MK2!,!`SFAS9!SM\IPSN)2_,2(A$<.&,`1E1"#<D@1!3-&IOYAOPIJ;):2""1
+M_2HWB'X@HK0LZ8-,8))4[J(+<&",Y3=*!-,8!!5]E.7CL."L4Z1]-L/XBO38
+M#^8P3E>B^'*-'I%@*E0LUZ>#"Y3I56E9GS-/JX&Z;JO4;FYJ>6"X#L"8"7+7
+M3%Q]JWNI]<NKR[\JE4=H!C3.X71^&K-Y8'/G,87/>#^DNJ%M2H)F,%>@=&M6
+ML#1'/'*\^==<9\B_TRVT@'*W+2B2T0(5>RHDN&_T6U:41H!7&NV,%;4,-&RM
+MTJ3ALBH49:%9;3TK-=)!4F0-M]Q*GMV0#(F-E'1'J^BMT6SB$;]0BF=\'V*)
+MU*?@(W,=P)"E#'Z?=#U5C1&D"#(,PCIVIU=L*=K3,'=3T6:LB%E?52?;.!].
+M%&:-0#?Z+6%$N(X1FVD@IK:,;RZ\2'$0$%6K<M:`#*W76S2(&?91FS!!!:KI
+M&)Y]PT9+DS;JL<$;+2KVS0(A5:U.FSME32\*O\35=@G"=MP\'*&KH#C#=^R@
+MJ%[L?LII=M!EZQH.5'6Z@S3+?NLXL,[GZI-I^KY33-J6K7?6W,IF-$>RD`:U
+MD",**H^?3W34B;V=PEWIW"NV%`ZQC*AZ?2+W_P":W[&T@`D96:W.#VM$0`!M
+MN5:-`%L3(CNK=&UTL!")[`QIDJQ&<^F&S`V]U4NZH:R-NZL]2JM#?W6!U&X]
+M4S]%-M:VI=;K:FN`&^ZYBZ=-:/HMOJ+]605AW&*DD+6*7A=Z>X-<"?JMBV?`
+M&GNN>LZGKAI"V[)P<-BMZ8K2IO)]1V'"($&(Y5=@=C.ZFI%F))'$*[1,T&1B
+M$;((AT(?,:!L-E&*X)SLLVM2;2^ANWU"@J/DF-/R4-2K(,<>Z@:USB3G"SMJ
+M8K6(GORFIQJ@C?*9@EN94K&9!Y5BZ$6C$#(0&!,B%9IT^"AJ4A&)`5TS5&L1
+M.^_*A=`,GA37+!JGMW4%3+(!6F=&UC29$*EU1NJD((D^RM0`-Y3,8'-AQ)2,
+MY.!\3].N:E-^EARO/+VWN+/J$U&:0X[+WGJ-JQ[(`PO._'/2AJ+PW:2"%TQD
+M8]K.&+:5&FV9SCNI-;>Q_P#WE5MZ3FT6MTNP$?EN_P`+E=)IX77R_`C.ZKO`
+M$G^ZL5LDD'_55ZL`[8/=>ZUP0C,YPF>#`C9$`-)W33$S*&C#`$2F&X<,)3+I
+M!33G=0.7!SB?V3`8W,>Z33F"/[I\3DC*H+29#C!]A,I&.3E-`G3LG/I&2/F4
+MV'#0,A,<SN>Z0<(,PG)#0-L[A`A(,GG9(G&YRG).F!L$C!W,1W1##`P?HF!@
+MD(@(GLFQO)12'Y)'ZIB9;JGZ2G&TSD8^4S]Y;(4")X(@H7&#/ZS*-P)$Z8)4
+M>G)(/UX5-$UQ&0?E(?/*0!!,)X@3!0/Q";U;$[)V_E2'YIF90."9"3S@<I]G
+M1N?=`9+23)0*#I,#/[(2""#_`&1;`B29Y*4%W'L@'>$P[&81Z8(S*0;V,^Z*
+M`?XA&$33Z?=+CG[;)-V]E`^H:?RC='3+7.EY(&TJ,`!TI^<!#0W#GCW1>=-J
+M*,-`F=49*$X$<)G-!@,G43D*H0,8@?5.3D0!A#ICM@_=.TD?1021,>G?&4Q`
+M:>X3R8`:9`R,X0PX.#?U[($R7&(E$6[%KI)W'9#!$Y1,^,'L@-KO1V*`3YFH
+M\H@)`TPDS$3QN$&MT4C?A;-)M-U,Z78Y'=9/0:>JH&MS\K6KL%%FB#/*Q>S_
+M`!7J57`G\P'>5I^&[9UW>`@.C&^ZS&`UZN@-R>Z[OP/TLTRUY'N"N>66HW(Z
+MCH%B*=!GH"Z;IU!K6B&K/Z;1R#$+>Z;1B#W7GW]=,8T.FT1`)"U*%/(@0.RJ
+MV+-+1[K3MV"97.NTB:VIG3MA6&4=42E2;#8VE6Z%/GA&I$=&@)_+A6*=&?\`
+M)6:3,[05-3I@[?:$:5V40.(4C*$M]O96J5)IYW1BF&Q&Z:6549:B1.%+3HMD
+MX.%:;1Q*D9;SM,J6&T#*>WLC\LAL!6Z=N1`1&B-6VRK%40P@$\J-P(_=:3J`
+M!.,*)]($0%$VR;BGJ!G=4*]OK.0<+=KT@W,`JI7:UL]NRB,2K;1@&/A15:+`
+M!`,K0N2)G[`*G5(CU*;%5U(;DPH*M5K<3A'?W#6-C^ZS!4=7>0V3QA#6UD5"
+M]_IX5NTMGU*@)'T3=+L7ZI=RM_I]F`084M;F)=(L,R1]5OV%`#C906-NYKI#
+ML'A:5`0`8^4C5B3RVAA[PJ'4,-*OU9T[0LCJU0L80,CNE28L/JM1NDDOCV7/
+M7E4%QRM'K-7)B9*YZL^:D:E(TDNCJ9`S*R+X#(,"%L4V:QE9?5Z6G8+>+GE5
+M6Q)\[?"W^G@B).%A]-9#YX[+:Z</-K!K<*7)J8\-$/.`!,)5''5V]EI6]BUM
+M`.]LJ%]JUS\K7.F9K:L:@T1V4+GSN0!W5FK;ALZ9^JJNIN`/IF5S;#4J`"!$
+M=Y4M"LT$`'?NH7V]1QP"HJE&JW+6II6JVHTB-0^BL4-,@@Y7-BI7823("GI=
+M4=3YE7E+'347-&T)5C.Y6-9=3IO(!=G=777`&1F?=:QK-B*[;),95-_H!P%9
+MK52XA15QC!E;W$5"[>3[HF;D@_W4-W@]Q\IJ)<>?T4+$U;+28_58W7K(UZ#B
+M6SV6N#)@;?LH[MWE#N"NF-TX91Y[4L'-J%N@X*;^!?\`X"NKK,INJEQ:)*'R
+MJ7^$+ON./+Y#N3+M,$_)4%PYQ`&HD#8RK-<:B`V/5Q,0JE<-V:\P,;1*]*(Q
+MS!]T,^DA%B#F$V6ND.RTJ(`D`$PEQ[]T[AOD?"3V@#!,';"!-/JD&(]T0,NP
+MXXV*9\BL[4S3F=/9-`/,R>$$C=()F2?F$PR<X3.;#LS([[A(DY$X[JA-C3D2
+MB&G&)([\H1R"92:`>3[!`3B-6!$Y2:-3PWOW2B=,'/(3$Y@!02,)#2,04&-6
+MV$G1\^R:0!M,@*A'2TDDQ]$P(P=,Q]D0PWN@&#)P(WA%27%4UG%[PV2(,"`H
+MQI@[>Z08#OEQY2`(<"Z80+TC&)3#:0!"=H.QC*<M],_E0,-H@2G9I+@1GV2#
+M8),[)-@51)QL2@=S<P8Y2]ASV3/,&`9PFG&\F)D(AL\F4FC)WRG<"?;^Z0&8
+M!Q[J*0@F8P>2F<`<@X]RG(.G\WT";8Y$`_JJ$V)SB/=,W_ISRGTD-G'/*0C2
+M21!X'=`Y$_/LDQOJ`G/ORF!!`Q]47],0-D!%HV).>R$M@0#ORI/3_0'("/27
+M&2=D#.$1P.Z>F/5D[);#;ZA"S88WYA`8$F92@2#KR>Z>89@2A`R<">R((MD`
+MZA]$3.T'ZE-&T[=@G+73IB>%`;/2\0`83MU5*FV_8(*9),;*WT^BUQD3\(-C
+MP\?*8"X>P@*]>.#OZM@=E2IQ3<UL&#M[*Y:4C5J-;!(&\97+*\K/RN>%+(U;
+MQKVM)@\C*]1Z%;>70:V`)7(^#[-K7M<6@>\?*[_IE.=.!G]%Y\[MTD:-E1V&
+MRV[&F!`P)6?8T@&:C)_NM6RI%Q`C/=<[7;&-&U9Z1Z<+3M:8E5[2EB./=7[6
+ME#?=9=(L4:8A6Z+(XPHJ#/3D25=MV>K))114VQCG?"G93<8Y*EMZ4%6Z5(1@
+M1"U$VAHTR"9`^ZG;1U#:)1LI@'T[JU09W(CE-%J"C;Q(`R%89;D"8W4LM#AG
+M=$XC3(X2QG=1>6!QL@J,Y4T^O'U0N!T$`[J"/2(@M4-9K8]U8,=Y"AK"9VGA
+M04KPAHP2LN]+0"=EIW8AIET3W6)U.X:`YH(6+6I%.ZJ9QRLZ\KX(`SV1UJI<
+MXL;RHA:U'ND@=UF+I2<U]R\``K7Z1TF(&G=3V%BSS`0`#.P72]&L2Q@Y(.ZO
+M:ZTIV?3M+1+-EH4;4-`&GZK6I6T>F`$YMI;/*UI95*@UC3^ZL$`-D;%)M&'3
+MI@)G@AN\J-\`KO.GNL;JKO3(XY6K6.!WX67U$@L,]E$TX_KKX><PL&?,K&"M
+MOQ/N[]I6!:`FN-HE).&,FO;L)I2?HLSK8:&;[+<M@#1(*Q^N-),0(A:CGW6=
+MTN/-#"=UO]/HAE36TB%SEJ_RZX.-\9716Y-6E((QF0L6.DK?I7-)M/\`-^J=
+M]U;L'])*YRNZL/3/J'NM#I%C7J-!J$GZJ7*QOTFMIZ]\UQ<0V(Y47\4V-7EG
+MXA:]GT=I:"6@K1H](H`QH`^G^BZ3#*N.7DQCE*ES5+891)G?"@UW;G8HNCL0
+MNV-A:4A+F-'T4+J5H)/H('"Z_MW\N?[T_#BKA[J=,^=2(/PL2[O*9K:0`%W_
+M`%*QI7-,@`01D+E.K>'',<:E*,"<J7"SIO#R2]LO348T5&.Q\JWTGJTU?+J.
+M_58G4*]>WFDX.^(5>T=J.O7Z@N=GX=-_EWS:K7LF9!Y4;JD3)E8/1NH#%)YY
+MV6M4<'&6D0=H3_#1KIH(G49]D#):V=7NB![C/LG#=+=$9*U"](VN!=`&?<IZ
+MS`6.U9[05$?^87<A/5J_RR=EO'MQRBF^BPN,0F\AG8)JCZ9>200?E-KI^_W7
+M5P?'U8`%QU2,S_95JA&GXXY&5/<..HD<?15ZCGEQ$-''U7L<T;P-@`T("(.,
+M%$\C)D8.0FQZ2@1EL@[C$H,<E$8F0A=!$D\=T#@M=JG4#P1_=)HDSC=,-.DD
+MB'#MLG!`!`_50%'J@A%#2_.!'W0L(B)`.\RD"6G`G*H0:/I/V3AO)'V2:Z-C
+M[83`M$X[YE$.UHTQG.V41$#O[E,-(R.4[C/]6/9%-@N_=+2T$['^Z:1J.G9*
+M>W"`B`6D3/\`=-I$3N-MR4X@M$P=/!35B`XM!+AR=N$-A(AN$VF1@?=%4+=)
+M$1]4FX!P2@30=MCV2<W>?E(F#&\8RFG!SG9%(F!`^"D!Q/T2!`/,I2-<@$0B
+M'<-1!!@3"%['DP!MB`B);]1RB)D2XR9Y0`<-DREI$0=D\MUD@[;2B9),B".0
+MH!P03,<IBT:3!F43C,Y^R$F`?5^7"JA:-P1O[[(],4QZ@9[(0?41!*1DG),2
+M40\`'&!RD=42#[B4[)+,$9,I-,GU'`V^44Y._J1:AL"A,C(/.Z6I-(8B792-
+M-]/)!`(D2,(\AN")&4,N,R0.X"!VZM\PGI8<)_[I3`&W<RFV``.1.2@<F9`R
+M.Z,NG<GZ(6-)J`3MRE`:)!$<*"2W8Y[A^JVNG6;VMD_U+/Z6QSZLC!G,+K7V
+MWEVS06Y/`4J6LVWH5:K@P3''8+4Z?;%K@UNHYS!Q^R'IM-H9#X).<\+7Z!0#
+MZ_J$CD0N.5KIBZ?P?1:UNIQ@]IGNNSZ=1)<#`6+T.V93I"0T<X73=/IYWCX7
+MF[NVXT;*D-#<$!:_3Z.F"(@*GT^CZ=EKVC`,K-=8O6E/V5VA2`SRHK-HR`K]
+M!HD#*-Q)0IC8A7:%.(TA0V[,XV5ZWF`8E%2V[,#"M4VDC`P@I`00,0IZ$C*T
+MR36$>EW_`'4],#5'9+^K?/.$3()$;'E0-4;.3VW2`,`$R1V1.`'NG8W2Z=_?
+MNH!T`YXV2%/<SE2N`D#NI&,!&?N@JFD0"0)5>[+:8DK0KD-,3L.5S_7;P,81
+M.2LVZ63;/ZU>L@C4N>J^96JG<B5<<RI<UI()^JUNF=-$:G-$^ZY[;Z9%GTUQ
+M!<]ICC"MNMVTF_E@]H6\;=K&Q_9974=+'&9V5TDNT?3F#7LNHZ32:&C&`N;Z
+M29JY75=.+6@"-L*XM5H4:(<,`&$-2EI)@84]"JI:;0\C&ZZ\5GIFNI2/<*K=
+M-TCU2NA;:MVC!5.\M`9V'NI<5F3F[QL#&RQNI$$.]ALNAOZ8;J@RN?ZH``3V
+MS\KG>'1QGB9S1JS]UB]-@W!+5K>+B"TG;V6-T36^Z[:3]E/C&3IZ+8MS!SLL
+M?K+<Z=^5NTJ?\@>_=95_3)?EOR4E<Y&#<T?+$B)&0M+HEZ&#0\S*:O;:J<PJ
+M#0659_1:LVL_#H@UCJQ>#/;*Z+H=:EITN@G;"XVRN@)#S@>ZU;*Z-.'-/U6=
+M:JW>M.\M7-;3D\*AU_KUITZF2^HT1W(6-=>(&6]@XN<!]5XO^)GB^O6JU&-J
+MNP-@3C]5Z<.MO'GWIV_BS\2Z%,O;3K8'O_JN0J?BJ[SQ%<P8V/\`JO#?$/7K
+MVXNG`5*AD_E!.5GW1ZE9U6/NJ%:FTY&MI`(7;]JWA)I]3>&OQ#IW,!]5IG$$
+M[+N>F7]#J%MJ:09XE?'OA?KE=I:"^#N).R]P_"#Q,ZJ!1K.]0.<_*XV7"\EU
+M\=OXIZ2RJ'56B"1A>=]6\WI]SZB=(.0O6[A[:M"<&>%P_C?I+:]%[@T`1O\`
+M93/'<X=?'Y/E8]K=,JL948\2.Q72])O?.HQJ]2\ZZ9-G?FWJ$@'8<+H^CW6B
+M]:T.])[_``N%M^O3K5U'74G;F!"EID'(.>P4-L-5,3!'96*3(GNMPM0W!#7P
+M<SNH:M5NF0,*>X:&DR>%0N7RSM"Z1QR+1J]0>(*7E'_&%6\QO^RG\QO;]5UT
+MX;?(=S`,`AP[]U7J"'=YR)4]P6DQ(D<GE5ZI;`(,[X[+UUA'/&X&X49``!&Q
+M1G3)GE"8@;'X40TDP#E,,.G8A&7$L#3$`R$((&44H$D&)PD\`G?(38U2)SLC
+M/Y8QG]40P(R!]^Z<:0Z7'&^R83SMPD021P@.D01$[\I0)V&W?=,W;$@_*;82
+M52G#6C(^436MW<^,;^Z$9.R7^X0)V7D[Y3N;!.KC:$@V$S]I/^PD#Z6D1D$(
+M2UHF<E(CF9GA(-)('/RH&`U'YQW1`$?/=`"0>Y[(].F6ELGB#_DJ:"0!L#'M
+MRD(_*1F-TGD%VXPD"-,R"XJ*0'H@[CE*'3,_ZI/`(Y^J=FDND?9`@&:#).K@
+M<)23C4E5!D'(D2A8`AH36G8'/L81`[`B.Q/"%H(_F3@8[IH9)R8^TH$3`B"G
+MR6:9'>4_Y6G;`Y*%@D9)]D0S6#`GZH]()R=DP!`&84M33H])@#@JJC$`=@,`
+M),P[5!(V*1,Y,`%,W<F=^$#P[D(@TZ2-O[I1$"?HF.#Z3"`FMDY(G"9S"XR"
+M??"<#`DP1[ILCU<?V1"`]A[>Z<`.=$B?V2`#L1_V3DD`8CA3H,`<F28V[!.W
+M43E*"=L2IK5H-5K-LS*#J/`71ZMY5%31+>_?V6_XP8VW:VFT-U;=H5[\.6&C
+MTXAK,D3\+/\`&.IUZ6NF)V"XYYS?JUAX[;[52Z)0\RL=8+I@8*[+HEGH:V`-
+MYPLCPO:%T$@D[X7:],M0W3I&%P\EVZ3AI]%MA`U<KI;"B&M@9[+,Z50AHU#E
+M;UE3U$2V,+FU(O6#7:1@`K6LJ4C\H5&SIF=_JM>UI'3[^RC<6;9@:9$*];4S
+MA0T&`$*_;T8Y*-;36],`CLK5!D-'LHZ;6@*>UAPGCL5=":F`0`3/]E8IC$<0
+MHF`[*:EL!,!6"4-]0,HP!'RHV[_W4U(2)[J$`W>8(A3,9)S`A%2IAQRIPT!N
+M4T(6TQ,$\)5H8P03E2NTB?=4>HW+64YD*45>IW(IL))7+WCC<5=,D\JQU>\=
+M4):UQ*/I5`$!SQ)]UQRNZZ2:2],LVL`)9GW6DXTZ+,B!W0.<VF))B,K!Z]U-
+MP!:TF"/LIO2^NQ^)?$-O947_`,QH`]UPM;QK1N+WR65&DSM/^JI>+A<7K7_S
+M#GW*\TNJ%?I/7J=1Y(:X[Y@J7VK>&..^7T;X:K"XI"I,SW74VE2(_P!RO//P
+MVOA6L6YS_P!UW-O5D`XE7&\)9RV*50"`#A7;6L0T$D2L:C4(]2M4:OW"Z3)F
+MQLMN1IW56]N?285&M<:6D3^JI75X,@._T6KFDQ!U&JV""`9"Y[JC_28C*T;J
+MX$.AP^%E=1?JIGV7.UN<.,\6.!!D[K&Z!J%R6G'Z+>\3T=3'/VC]5B=#9%WM
+M,G"EZ+'7VXF@`<E5+F@#QMA:EC2/E@XRD:0-0J1AB/H$B%E7=!P>2W8KJJ]$
+M1$+.O+8%LP`MQ&`ZB1D'/[([>X=3<`XF#[J]5MP&$9SLJ%S1+7'>.86M;5<N
+MZ(O+:`001C=>4?BAX9NF:JUNS#1D0<[>R]/Z9=.;4#7-AOLMFKTVTZE;::C&
+MND=EK#+5<<\?KY)\,7%ET_QI:W'5Z`?;TJFI[*@X'RO6/_$'XT\$7_X?OL>G
+MT[*YO:VGR'TF-FB`X$Y&TA:?XE?A9:W=%]6VH-94`P6#_1>&>(?!W5.FU1Y[
+M/29&QD1$S]UZ\?[>^WGRQF4U^&9T6L0]I<W\OZKTW\)^HO=UIM,-($P8^JX3
+MIUAY3,,)=M'NO5/P=\-5*50755K@7&<K/FU88O9[2HYUJV=U0ZN/,HN:3L%<
+M8X4J$3LLN]N&ZCI=@<+E>(N$W>'`>+Z1H5!5:(`.3"BZ7?,?7I"#+MCP5T7B
+M.C2K6K]0W"Y#H]L*74H!($X,KAY/R]F&OKU+I%8.MV@X,+0:26R<K'Z!'ELW
+MD>ZW'M`9(/RKBEJG>G+O5CGW61>U0`8=\+2OGQ.?E<]UNNUC'&?U7?&..5X5
+M*MZX5"&D0A_CG_X@N?KW;_-=IV0?Q=3_`&%W<O6O`[D#S"7&>%5JN].D1!5N
+MX+7OEK(G8`[JM<L+':7"(X7>N2%S2,@$2)R@&>%(X3$ND(1`/?V32ASI(@I@
+M.-T<NR!]<I.+2[`COE`!,[82>T"(TDD?;V1L$B"\#G)_1!IYS]U`B#JV^R0D
+M081-`#ADHB!L'?W5@8DNW'Z0$VDGZ(X])WA)S=D#`^F,S^Z8"<1E.W:"=L)$
+M$"90(3$29W14C#FRT'V.Q33@YB.1RF(@9A`3RW=I4=:=3B'&"BS`@@2F<YP&
+MG60#N$$;OM*<3L2?H4G:NZ0S$_LBF#=YF$\3.EWNDWDDGX3Q!,$*:#$G;4G:
+M3.)38)C=(G$*AR3P283M)/=,"WE.2)B`1WV4#ZCR3&TIL[`PGYV$!,_?@]I0
+M(EP&28"%KB8!/W18#>-LYW09)[J@I))@Y"D-1SZ(IP(;[*-C9,$@>Z)H])R$
+M#$&3V[A(`B#"D;ALD84CRP6X:6#6#.J=QV326H7$XS/PF(=));]`C:9(,#/=
+M'#8@E#8!J()&0/9"3#I<V>Z.!IF"`>$PQ^5L'N@.D3.!N-TU0D-@3*3=X@9V
+ME$]D@X'R%`!(#F@".,KHO`/0J_5NIL;HEDY*PK.AYM9M,'\R]J_">SM[#IS"
+MYK=3LRN7FS]8Z>/';J.B]!I6/3?+#`-(R>ZX+QC18[K@I,=MV*]*ZSU.C0Z:
+M\AW&%Y:*K[[K3JO+BO)CSD[R:EM;_ANWAK1I=\KM.C420"6K!Z#1`<UIQ`C;
+M===TFC#`,I]<JO\`3Z1:X`C=;-K3../=5+*EM#25K6K02TD05*W%NPIY$A:=
+MMC(W5*WVQPM&U:3[?*C2Y;#8PKU!P)WA5[=@(^%88T#8*JLTCZ@)QRK-NX0?
+M3"@I`R.59IB1/*"1F'3F%,P^J)^JA;JGCY4]$9R,%3:I&@D[%3T],?""@UQC
+M;Z*5[6CA!(VII:9":I7QARJU*P!W52O<-;JSO[J6FEB]N]$S@+G.L]1)!:#O
+MV1=9OP`6@Y*Q&%U:M!,K%R=,<4ULQ]:KJ,Z>RW["GHI1!/NJ_1[<""0"%M4Z
+M;`T8CX7.1M0N*3GB<_"PNOT&LIE[H$!=:]K0"3PN3\8%S@\,)5LU-D_#A^J>
+MNJ?9<=XXZ36NJ(-,B09&%W#Z+G5O4/JAO.GBK3RS"LO&EN.KM%^$[:M&T8RH
+M22`O3K0N\L?YKA_"EJ;>H.R[6U>&@">-RL8S2Y]M"DX3$J1U9K6Y.0L\W`8"
+M2<!9/6^N4K5A+G#'NM[9TV+R^`)`<LVO>R<N"\W\2_B):VE8M=6`(]UF67XA
+MT;FM#:HRL7+ZZ8X5ZHZXU[F5%7<U\@1\K`\/=8IWK`=>>RZ&VIE\'/\`FI,C
+M+'3'ZW2!ID1PN>Z93#;T;[KM>K6Q-N[&ZYFE;Q?2-YRM6\,1NV3]-,@[*>FT
+MG)RGZ=;%U,$;J\R@`?WE,>6:SZM,021"KOH!_P#3]5JW%`$8(*A%&&G"VS6%
+M<VL&#PJ=U;`4W-Q+AN5T%Q0;JG]U4JT&G<#/*VPY*ZHFD9`(*N=$Z@^F0UQV
+MV"N]4M@>!LL>O0<QVMLA6KW.74FM3KVY!`]EQWC'PQ;7K7.\D$GL-EK=*NM1
+MTDG_`#6D]OF-@D&<JXYZXKEEAJO'*/@ZA9]2#ZM,%L\KO.A.M+2@&L`"TNL]
+M-;5S`^BQ;JS-*F&M`$<K6^>$]99RT;[J%)S(:1]UF0ZM4]!D$K+N65&F"=D5
+ME>BW?+B2`N>6=^MX82+?B"U>RS+78D+DK*FZGU(:OZ3L%U=[U)MVP,F)[K.?
+M9M%<505G/5G#4NNW0]`+6M!)(A;AJ,-'4:D'@+G>GO#6#U+294+F$$C(X*WB
+M9(>HU@1#>5QWC"[\J@:<P7X"Z7JA&@DN/U7"^)W.N.JT;=N9*]&$^O/E?BST
+MOICZEA2J%IEPG]58_P"$N_P%=?T3I8'2J`.^E6O^&-3ES]WQS5W]APJIW$`R
+M5:O=+JA<&``F855X,D@8]BO6RC>3J,[\@I2P$Z09XGLDX3).23W0/W'<;H$(
+MP3E,8G?!1`9Q"'?!_P"R!O8@GW2';!1%C@W+26S`(3.:0^1('$JAA^6(B$6\
+MG?;"%HR`"B(@Y"@+4<'@)#\LF4#<MW1-((&-^Y5#YVXY3/'<$)H)"9X).24!
+M.<!L9^B0.?9!F$T3$H)`6')&3NA?#N8'NF)SN,)MW3!'>$4HVQCW"<'./W3`
+M83!L'`4!.;!("8>[3LEI`,28`S*<`:?9`P@`04@)G"1$B-B$6F&_"H;TPGF!
+MQ]TS0=<3&.4S=Y/'NH@CEHS@)`#5ONG`(W`^)3&)[>R*?T@'T@CY03Q)E&V(
+M(WG;A,V`_@9V"(:28R8E'ID0-TY)#2UKX!W2!@QW0)H.DR["+4(@Y*#$P#NG
+M`;OPJ&D`@?NB<2`1&>$((!QD)<G5'V0&R'-C?Z)G-C))]A":D&EI#0G@C&([
+MR@>F)=)!PC,MQ*"F,F!"EIM+GC2TDSG*@N]%8'7;7`1G.%ZAX4N7&M3H:H!@
+M&%S'@SI%-U'4X&>Z[?PCT84KP5BTX&Y7F\MEX=\,O6+_`(W:VAT@PXDQB%R7
+MA.@'W7FR22>5L_B9=Z6-H!RK^!+?8N$Y[+CCQ+8U;P[+H=``-DB>5U73*>1C
+M'98O1Z0$8PNCZ53AX)V4VQ(T[)D1Q[+1H`$P!E5[1H!SSP5>H-;M*S6XMVS9
+M`,%:5N,#"J6K1I_-^JT+1L-U;A%6J$Z5:8`(]U7H@=HE6!M`$JJGIQ,*=CL1
+M.%!1VR-E8HB8,9*C6DM$?4*S1:V1.%#0:6^TJ1U4-W5V:25:[J;]-.GC<F=E
+M%=5XY_55[BY`$ZMUGW-QO!E9M)%FYNL8&8PLZYN"9R@=4)D:OHHG-+C]%BMR
+M,[J=1SGXF$NFD`^KNK56VUSB%&VV<P86+MVFKPV>G5FM``6BVKK:`#"P[*F\
+M/`+EJVI(@?ND6Q/4<\LCA974+!UP3JV6TQNJ!PIQ;-TSI]UO6V.G%U.BM827
+M#G=05K#2=L=UV%[0:&N`;E9-S1;L`5-2+.6/96K6OD"(6FU[64LIVTAO^JAN
+MA#2&\+#2IU6ZTT708,+@?%+[BZ<YH<0%V=_2?5:1)@K'N[`YU-3MJ33PSQST
+M2]JW#G4VN<)69;=+OK=C7.:6EO*]TN>F6[YU,&5D]1Z-0J/T:0>(6KG9#'*N
+M:_#?JU>C=,HUB8G&/]%[CX=N&W%HTC)/N5Y=;^'Q1K-J,:`9X7HW@FF:=%K2
+MXDQNN/,K6=F4;74&`T"#&0N9-$-ZA`&Y767PBD8,K`#`;[5L96ZY1M]-MR+<
+M1V4WDN`("L=,9_(:8X4U1OK.%TG3G6<ZE`@A0U:9#96C5I3PHJM*`<$K49K*
+MK-!/^2J7%/>/LM>K1&8'TE4[FF0#`!51AWM)L:EDWM$`D@[KH[JB(]0"RKRB
+M-41^BC6G.D/IU"YI+2M3I=X'0UYSLH[VD`2"/LJ<>55#FR"J6;=`ZFVI3G"S
+M.J6@+.ZGZ=<RT!SOD*2X<UV(P597"RQR?4K8"8"P[VB0X@$Y79]1H,<3PL*]
+MMOYI`B%N\PG#&HL<*@)V[E:#'M%/)!)&Y45S2%/U1D*M4K@-U2,=UPUJNO<;
+M%BX&)S/NM`NTLV6'T5_FD$#$[K8K5&LI29F%UPY8RX9G7*Y92<22,+B>AU#>
+M^,=.N6M.P71>+K@MMGD/CTG=9OX2VC+KK!K%N=>Y7HG3SW\O7>EVX'3Z0#?Z
+M>58\@?X5IV=LP6K`!B%)_#M[+K'E?`-Q)JDSG=5WP<RW'ZJS>.EY@0"<<JK5
+M?G!)^5VKLB<)&,DH,[`C*E<1.VWO*!Q;I(@ZIP>(4-!R3,),'I(D=Y3EPC`G
+M*$F3/]D#N)VC?A.2YP&HR>QW0!_^RG)Q&^-^Z:#9!B-NX1GU.)(P3F,)@8G;
+M/UA-)D-)$#8*AY$!K6DG^R4XVV288[#Y3N)!(TX]U`S2=XA-F8C'=&S:8QLA
+M<[.V1A`T@-T@9F9X2).F83NRT2`8"%Q((B$T'`)]1!SRF<T@Z<&1/=2LK.;E
+MI=MI^G9`]V9C`[*J$@Q^6?A-)&8*)N4S@-YE`HR"3]-D],P8C8II'=(QI'Z$
+M*!R2-FE,3&THI`&\D'9`"),H$>#.R<9]Y2!W`&4[2([_``J"#HD&"(V*8$;C
+MZX3$B,3GW3LB,$SQA1"8`23JX3ZMH/RG%0:1Z8@S,)C^>50\C5P)2)$$P/9,
+M=$A$S000XG:0BADX[)-/9.V"1.>"B,:1IC"`&"&_*?&Y@?"=X$0(CDII!,DX
+M&\)H)OOE$.^`WMLD0-YS&1[)@?5B/[J(=ADB`M#H],5+EK($SLLX1W``XE:O
+MAMS/^(4]1`$J9<19-UZ1X.L7-HTSGX'*[BT'D6VJ,`25B^#:;/X.F&Z5T=YI
+M9T^I$8;_`&7S[;M[,L9T\W\;W)N.M>6TB-41]5T?A"EY=LR2)A<E??S?$3R9
+MPZ%W/0*(9;TY'J,!:O&+'DXNG5]%&V%T?3&-P,@\K#Z'2'E`RNCL6P!@CX48
+MC1M)#8._RK]!N0"JELW`D-^5>MFR!$K-:BW:#(,+0MP!OM/=4K89VV5RD1GD
+ME%7*)C`&RFI27*O;\*S1&>))V2M1:I``S*LT0,$E04FY$J9KH&!NHJ8OTM]X
+M5*]N!,:LA/=U@![K)O*^IQB=E+5B2M<:G$`D*(ND$3E5M0D[H@2,0IMK20X^
+MB-CA`(.5&R'$#E6[>A)]E%!2]3NZLTK<$"1NI:%L"9A7;>B(`(5TOLJTK6(+
+M6Y5RWMR`)^RMV]NT1'"L,I,V5F)[(*5,1B,=U.UOHB%,RB`,DPK%.@(!W6IB
+MSMD75OJ!.!&2LJ]MW-<3NNJJ46EN6CX5.YMFO#F@#/)4RP=,<G*Z71@;*-])
+MSC,86[5Z<W48F"D;"&@=NZY>KI[1S_\`!S,A4>IV8T'V755+8`0,E9G5*/H(
+MVQ*FE<)U!OE.?MA95-CJESM,E=9>=,\^L0)AVZL]/\.`.U8.%B[IN1B6=H*F
+MEI$+J>B6XI4P1`14NF"B[;;E:%*D`P8XX*2<I:J]3>!1,[A8]@T5;V?=:/5W
+MM#2TF.%#X>M]5?61REYJ=1TMC1B@&B9C=$]FY.ZFM9%,<!'5`[_5=XXJ%1I4
+M>B2<A6:S=1C:%`6_>%4JI6G@?*HUQ.P^5I5VAQA4Z[(;I)"J1GW=.1,02LVY
+MHF3^F%MU&C3@@_*IW3&AIG[J::CF^H4C!D[%8US#:G(@[A=-?T0YC@!(6+>V
+MA#C@&5-MQ3MW.#Y!P<K0;5)9E9_ENIO@"0,Y4[7X@'C95SRA7A]&#*R[]OIG
+MGV5ZX(#9CA4*YD<@+<<K&7?,+V1)G^RY[J-9].OY;1.5TMZV-7[KF^K4GNN`
+M1,$\8A9RGTQO+H_#%*+=I(@]U9ZM4`)@^RJ]!?Y=D&@Y`0]2\VK3JN8,,;)R
+MKXYPF=<GXVO/Y6AID@[RND_!GIU6F&W#@<G405POB=YKW[:8[QDG)7J7X14Z
+MK;*DUV,`8^B[R<R.&?$>CV];31:)B`C\_P!U7&!&HI9_Q%>OT>/V?!5TT3J(
+M#>T&56J@1+3DXRK-P/YG`/;=05&>K2X<*O2K$Z9`!!^4GMTZ02<Y([(W-&WN
+MA,2>W!)4`;B,;IB,Y*>!ID83Z,$E`+A.!*9A)&Y`]D<YF8^J8@S,G[*A-;NX
+ML($Q,)#\T'[!(`<S,I1+H`_T0(&=X!"3L#<X/T*)H&C;,[IC+G:B20@9KO3C
+M.$B&G.T\=DA`W"(@;9A`.YSA,[#2/H%(X2?D90M:)(C$_9`+#C]4=Q1J4M(J
+M-`-1H>,S@[)F$MJ-J`!VGAPWA27ES7N7,\^IJ%,0T1L-X_5!`P-`,C!_5.(X
+M&R>!`<4Y:-.HC?*`($[3E-$$1RB($8R)[*ST9]K1ZE;U[VV-S;,>#4HM=H+V
+MSD3PH*PS.,A,YQU2[C`^%/<FD^ZJOH4C3IN<2QDSH$X$\X43J<F.(^R``T$8
+M(F=DP',<J0T26X!_R3FF2XC(X$E4"TPXB`0<2DW$1D1OS",L(=Q[%,6$^HN`
+M^2H&$1./ND^`8&>^488=,EPC@S"8@#5(:<1GA`+FM!'O[I-TD=C\I.:#_4`#
+MV1A@+8)&_=4"($<HZ+A3J:NV1'?A,QD"-6Z=S&B(<!B4`AH.2[)RFD2?43]$
+M>EHD:D_E,`:2_A-@&'W/=%&29.W?=/Y;/\8']T!#)F3"!L<D[[J:W>:-9KQ\
+MSV0!LMEDCZH(PX3LH=/6OPTZ[2T4Z%=X#L`2?9=IUR_I4^CU"QP&L0#*^?>G
+MWE:U>'TW?E[+;/BB[KT!2=4,;+RY^&WIZL/++_9TW2GBKU\O)D:H_5>B]*IB
+M&,#3``@RO-/`G\VY%0Y)S\Y"]0Z,"6,Q&Q7/.:X8SR]KMU'0V::;<[?JNBL6
+MRT`F,<K$Z.STMG.%OV`P#N3NL$7;8`&(5^V&`Z?H%4H,]0,>Q]U?MP<3\J-1
+M98(`D0K-%IB95<$@YV'Z*:@\GX^4VU%VB,`JU1C"K6WJ`5IK0`,J5J+`VU9"
+M:I4`:/5!0EVD8^RS[^X+=7J@!2J'J-Q!/JQLLXU2Z<SF=T%U<&H^)4=!QUP=
+ME!8:TDP"9^59H4G:<R5'0;)!!]U=I#/;W[*:78K2E+CQ')6A;TAJ$`3ONHK>
+MGD3E7Z%)P:.<YA61+4E&D&R>RL460)A-2!=@8PK%(-@3\0M*DIB`TR,J2F`7
+M)`!V&P2G9^;M"HL4Q(]E-3$-Q^JBIG/]E/3$C/PMQ*"KANP,JNX'=6ZC-\J(
+MLA^)"595>G3#B<"$]6D"V8E3,`#\B/=/6(`(^JFFMLRM1R8$86;U"WD'`6S6
+M[XB%1O=);@;KG9'25S;J.BX@C8K4L-#F`1E5NHTAKU-$'=*SJ^7$F5QZKI9N
+M+US2U<1"JW3A3IND20,*=]VSRSZA*PNMW^#DB>)2UF1G]6K&I<Z`5K^'*9#!
+M(^JP++57N)W!.ZZKI+0RFULS&%,>TRZ:U'#!S")X!&-^R"BZ![E2O`^(79R0
+M5&&9(QW4%1F8C'=6G-QOME05)F`JE5JK`!@&53>S)5^O.D\*K4VF=E44JS-!
+M)5"\!+<8E:=U!DA4;B#@G)4VU&749)(GC*S[VE`+8,E;%8-!D$*A=AIU1OOE
+M-*YZ[IN;4)B.,*`@;[$\K3O!/ITX69=L]4@^\*QJS:.JXND$<*M5T@1"FDD^
+M^RCN"-)!&58Y933*ZBZ).^E85R]KJL-'J&5O=3PUTD?*PJ[8?M))PY,JXKMC
+M6+:6(B),*OU*\++=[FGU.!!,*%U;2T,&_99_7*[OX9P$1&4PIE&+;UA5Z\V?
+M5Z^WNO:_P_I>784_3!(F>Z\2\)M?<]>:3_34X^B]Y\*L?3Z6QH&-*].$_D\W
+MFO#3?<L#R'-,C!A-_%4_\)5.L[^:['*'5[+W/%M\35H#B2#CE5JH!XF%8K1)
+M@P%`^`X:_4#N`N;VHG$Z"T_E&0/=`?B92?ZC[#8)H..(,[H&V!E)H`/_`'3O
+M:00!VF4P&0$");DP!/Z)B"6@QCO*)^<F7$<RA<XP`0"`H&W.HDRG!`9G&(W3
+MD@DN``D[#9(P)VGYA4)H]4B8Y$H@.QVW3-,[`RB$'!*!M(T@YG:2FG$2?E$T
+M8[I%IT$D2$#;@0XR1E(8&'9.$FP.9]TG;",>T)0YTD[X]D`&X+D0@-$B?HA.
+M1(R@=C?<P<I">2-LE-Q,0@:=.YQ\(">WW3`Z<R92CTS'SA(9.1L@+4Z!)3!Q
+MU'8)W`$`C*1#7-)_+`^Z@$O=J,.,)M9<6R2F,S(28UKJ@:7:9_J*JB#G`G(P
+MF!=@G<I#!P9,[IB1VE`@2#P.,)VN,&2,8RE(G_-,T2=(]1C;=0*?3G;X1AQ.
+M&D1*!PSP([E*??Z*B1I,C&R<ENG!!GW0-!:S43B8W3P)B1!4!`D`GO\`5,\G
+M5G)XDI2.\`>Z1D@095(4F/68E(NA^_QE+20(SG8]D[A#@`\N`R3L@=CM.'-D
+M1M*$.,'3NF:`1C]24I]1C"B#IG`](GY4M!H\P0/U45,8!G92VT>:-P)RBQZ)
+M^'E.&LU8C$_9>H]$;AITXP%YK^'%+66DN:.TGE>G]$IP]LU&G"\7EFZZ1U71
+M_P#EMF8XA;MF8`GY6)TMHAI6W:-,`X^JYUN-*UG`S&ZOT(`ET_'94K1I(GZ*
+MVP:>2HTG)D8)D[*2T$B!^ZAI9V<KUHP'.!\HU%RS8("L#;V_=16\-`SPC+PT
+M079[J:VJ*\J:)D\+&ZA<:I;B>5<ZE6'JTG*Q[DDN)/*R!8XET'\OPK=O3U.S
+M\JI2@>TJ]9MDC8B4T;7+1AU?FPKE$MF9D[95>@P`[1"O6H$[X*?\6+-J#J#B
+MM*W`(`GW5&DX-P#*O4*@:!ZLA6"Q2;Z"1]DS"2^",;RDVLTX$2I&:"Z9&RTL
+M'2,84[#J(,`2HZ6DN$?=3!XF`=D@D8R/ZL`*1CHV.V84#*ITZ<;J1KLC:2M"
+M<.PF$%V^/=1.?I^4[*D"<*[-)7QI)D;J*KZA&)3O?)F,)M33)4%2L#JSG*HW
+M^)P%H5R-EGWC06F2L9-XL;J-32#E8ES=Z'D-,0M?K`Q`.>"N=NZ9+BX$X7#)
+MZ<$M:^/DDZ\]ED7]TZK5#9QLFO2]F!RJUI2<ZM,<_=0L=%T"D"QN`)72=/;I
+M8,+$Z*P-IMANVZW[0[?"UBXY+=`$NF,*R6:LGLH+8^KG*M2"(E=,7.H'M(DA
+M0/$YB%9K8!C*K$R2(SRM(AJ06D'M]E3K`D?*N/@XE05&C/8<H,^X(F#B%3N6
+MMTDM_17;QH@F=\`E4:\G!/'=*U%.LT#X&%2N\B>>`KU;_P"["S[P2^`?JC3.
+MKB7$[$9^JH7%,:'<DK2N6@`DJC<B!$ITK+>0UY]E!5?+C[*6[:0XF8$K/JO`
+M>?4<JLW$-Z&$96)U!HF)^/=:]T\%N3@+&ZD[USJQ]T^//E-51KO`>0[A8OB>
+MX:*+@2&X[9`6G6,N=J<X97,^*;@.+QJ)SF5OQSEFM/\`"JFRKUD.=!@_W"]X
+MLGLH=+9IWT_V7A?X1@#J`<7""<#[+V,UGML(.T?9>GPS^3Q>>HJUZSS734C*
+M'^-I_P#R+!N6N-=YU')[H-#O\7ZKW/*^7:WY@V`3['=5J[8)])'"M7`!)*KU
+M-);.?A<'N5WD;<E#I&Y'V1G)W.$Q(TX)^J@"0';?0I$X+I(G$)P`1F04SVN`
+MQA`&HDXF$^II$"?JB(`=.1^J8P3OCW0(E@,M)]TL:I((!2(!Q'WQ*38`,EWT
+M*0.TMP&X]T[B`,Q.R=L;[RF@3B539PZ/C9(N]\;0FV.GNDYHWV0+$2#OA*I&
+MF2A`$P#$(J[FOJ:F-#1VW40AI#07&(]DB0YL`3R7<I?U>K./A,YIX./A#H+0
+M"(V^4B2T07$CLG@1G<;82<UIYD(I'8P=T(@1+D\#/JE,6R>ZH(")@X3.+=)W
+M)3_T[(7-,?FQLH%)$PF$\$%/I($2DSG2=MT#`NDG5D^Z6VQ`QLD0>=NR$P!W
+M^J51$_U2,\2GI5#3,M,&(D'9`UH.Z<@!`MR8.V8)2/M@]DT`'O\`!2`$D;_*
+M!QN<A.`(()CX30=P0B:#J.HH'+=X.R;8C$_">,Q&0DYL8@;;('#LG`@IHSF`
+M/A.1C:#O@H3ODX0$T8P`4B)$:1/>4FMS,.A)S9=@[(@F2/\`)3V7_/;C"@`X
+M*L].$5`,CX18]'\`:139(X7IW0A(;"\R\`L,,#B97J?06AM-N%X?)/Y.N^'5
+M=+@-:3QNMRR`+1DQ&%B=*@M&(6Y:B",;K%:C3MP/+YV5BGAH[2H+0"/[J<8?
+M"RTEI.@C'*T+,`JE2:(SLKE"&MW1J+3<`DE5KNL`(!QV2K5@&F52N*FL0TY4
+M$-R^2294%>JPT@UM.'3.N=PFNW$-,G94PYSZ@RB=K5`-DP9/*O6T-V!PJ-!N
+MD@;*4U-.2Y1J1KTZK0T$NA2_Q8#0)(GNN;K]2T#33,RL^ZO+UP])(:<[)MOU
+MUV[=G463ET0IJ?4Z9P'97FU"^NGW'EZX.RZ+IM"Y>`XN*;OX6W&?77TK]D@Z
+ML0K=&^8X8=E<U_!5A3U:R.52_B;BC7(!)"F5N/:XW'+IZ!97$M!+E<I%AR25
+MP=GUBJP`..GY6]TGJK7#-3]5<<I6K@Z$C`@!."X9;((4%O=TWM$'=66%I_JE
+M=8Y]&U.)R3E&S:$P`W)1@8C.%-&PAP)_,F<^.4SF&)&%&1!&5`U0^J95.]!+
+M294URYPG*KU'@CU'/99JQC]3I%TD;K%KTR)&)*W[_208"Q;W!)W]ESR=<:RK
+MVAJ'PH[:AIJ`E77Y:3*%C0'3N5AO:[T^`0MRR/I$\+$LL'?"V+(@D$'A:CG6
+MI:C$85AS@UN`%6M.8(]RIJH],S"W&$55QR=E`70"#N5)4V)!5>JTB3,%79H%
+M5\'A1/<'#(^O">J8YG]E!DDPJF@5@'-@B0J=S3GLKSF0"3^BJ5ORF/W6NS;+
+MNF.DPJ%<<1E:=WALK-KCU?KA--13N&!X^%F7Q@XV'ZK6K2LV\``D@(LK)O2W
+M<A9%T(<2!CNMB_:",+)OB6@I9LM9]S5()@0LV_?J)$3W5^ZRPZL`<+(O'#6[
+M,]HX68XY,SJ3RQCO+S\C9<;XCJ@U<Q._^\+INN5Z8:0V,\E<5URO-0EIF#E>
+MCQSEQSZ=W^"[7/ZJTN_5>N=>J"E99($!>-?@<[5>NK:X`"]!\47HJ4"P/]EZ
+MO!/Y5X/U%Y57]0I%Y))W3?Q]+N5GTM)I@D$HH9V*[^S$E?/=RQH&.>.RK/TD
+MF6\<*U6>X$_E^55KETB2`2N3U(@!JDX"&-1_9)Y)=O\`5($ENXQPB&<T9C/U
+M0F=/?W1N)`$D9V0:N1PJ&`,;_JGQ(/"=CRUI(`AV"#E,QS2!'?9#9CET9/LB
+MTZ1L1\*2N^B7@TJ98`T3J=.>3L.4S3+0[3M]D`P9V/T2$P2EK)))W]BF:X:C
+MG`0/EISRFV/*69$[#ND2,`2BF&<@Q*0!W,E$8G/Z)AC9N_,H&VD$2-X3-ATY
+M._=$8,<?)3:1N9@\DX4"&WMW"9TS.93M@DC8(G$`?"HB^=BCHN+'!['9;D'L
+MG<1/I!CLFY)@XX0/4<ZI5<][M3GYE#'L,(RYI,B"0$VIH,Y01P7/P9/LD#P?
+ME.^!$RWL4(TSOO[IH.<QD3RF'<D$I2#B-T[2'`P<CA129G)A"=AV1-(C3CV*
+M3O*X)_U5`-WA/!!W"3@`2)3P)WXR@<`#U#CND#!S]D0PZ&_E&Z0WWV[J(;?M
+M\IR(>,[^Z4,F&X!Q\)W@#TDS'(0,R(W@^Z3BX^WR4WHT[_3**6EH&G(Y!.55
+M$2#$-(')W09`AN?=)L3))QC9.<&<91#AW$X"N]*@U@=PJ;6D@$`1W6GTND&D
+M.QE9H]`_#_-8`#CE>J=`TEC,%>5?A\0;IC2['*]7Z""UC>5X\_[.KING;@<+
+M?M22T2L/I>X&WRMVT:-(GCA<ZW%VW.!A6*8+G0H:(!SL%;MF@0866XLT&0P<
+MJ1QTB04-(#Y$(+AP!@%*H:SS!(*KO=IR8E%4<#Z95>X(#42JUV^7$3A16U,%
+MP=ND]NNI`,@*8Z:5/=-$A[BJ*3,X7->)/$C+)CB7[<)>*^JTZ-LXO>`!LO)/
+M$G6!>W[J;JI%.=X6+;O4>KQ>/?->A>'_`!11N+R7/G,1_L+L_P"-H5K::?(7
+M@5O6I4&MJ4ZI#VKL?#_BNG2L]-:H!A;PMG%<O-XK>8V/$76V],OA5J&&3NM[
+MH/XA].9;-#Z[!`Y*\:_$7Q"WJ5?RJ#O2WGNN0;<U!@//W5F][CI/TTRQGMV^
+MIJ'XAV%W6;;4:S7N=L`5UO0;47E`5WMW7R1^'O5FV/B*E5K/].Q/9?6'X<]<
+ML[GIM)K:C2(WE+;E=9.'E\7[7]6A?='!;+1E46V]S:O],D+L:9I5&#U`SV*!
+M]A3JR"``LWQ_@P\UCGK+JSZ9#7DA;O3>JLJ0-2IW'A[S:T4P/E4K_I%[TT"H
+M#+0?=3^6/;M,\<G7T+AKA((A6:;Q&XA<7T[J;F0UYCY*W++J#:@`:X'X.RZ3
+M*5FXZ;3].GB56JF780,K2,$''=.XDB1"5-:15!J:>ZJW+0`3*GJ.TD@G"AKP
+MYF%C8RKQYU0`LR\;(,B/=:MY3AI)P%DW67;[_HL5O%2TG5$?5/I/,(B3&-^Z
+M"8=!^ZQIO:S0QMN%J]/.`0<E8]%P)Q@E:O3#!]E6:VK-WLIZ@!$E5:#HCD'L
+MIG.B"MQD#Q!.57K&3A2UG3,*"I).TJJK5"2["'28D[*7TAV<E`YPV_NM1FHG
+M.)&^_NJU?(CA3U7>G.55NG#20`M(IW+001NLZLT`GM[J[7<1,*C=.,F/T15.
+MY=!,RLZ_@L)!A7;DDM67=O=M,@H,^NTQ(*R;W+HB/A:5Y4`$;$+*NZX)))D*
+MEJA?._EF<!8M^ZF&;Y&X"UKYS7,W'U7-=4J$^81$#<K.OPY=L#Q!7,.8'3V(
+M)"X^_+JI?.0<96OU^L#4>`[/"PJCCHYEQR5Z_'-.'DKT7\'!Y5HXC[_5=3UI
+MI>#J)$9PLK\+K`T^@,K3EXG]5<ZS<^LM8X%TP0%Z/#.-O!YN<DEO3;Y+<\*3
+MRQW5>E5'EC40"B\UG<+>DV\%N7;-('IDY"IU6'))&#RKE8>C42T@=U4KQ,<3
+M*YO6@)B1B"F<P`'41V1.T@X(S,SPA(!F2)'=$H`)@#8<IG[_`"C'Y2.^R&`2
+M8B/<HH'!LB`?A$S0:<:I)X[)$>KO"36@/@=OE4.\$'26P0<@J>G7;3MRQE*G
+M+CE[A)^D[*`-`)./F=D0D-@$`(&`],DC!2(;J<B:T?$)B,[1]4#5(:[(V3$#
+M223'PDX;(@R8:(S(W4`B08#1(_1,_#>_`1%HG83O*8-$@$?7V0T89:?G,)X(
+M,C'9,1C';=2.CR\B%0,Z6D#&K=)Y=`U2G#89,?FV/=(M$80,9)B4(:?>$^B1
+M@0$;8TF9S[H!#009"8-_I.(1N:(=F$+FD@#4<\H&<P1!S'NHB(=)^REJ.&F`
+M[[('#83'*B@<(.^R1!=DF.,(R"'&9@]BG8T%QU:BT;P@%H+=."/A-^5^[H[J
+M2&D$"?JA=$S'TA`TR?S"#WX1%S2(@`]^Z%D1S*8;Q.P0&#Z3D"<)-TDDDXW[
+MI/:)P<;Y1.:0R2[=$"V1L[8X2))F#+DW!.K=.T`.W/T[H'@D3(2`)D<$)JAU
+M$Z73/!2:-,912CU8'^^Z/2(@B#V*`CTET@'@#E$,G5JB"J@K=LU/\UT%C1_D
+MM,0.%B]/:75=/?E=3:TW"T)(PW("YY5J1T'@`?\`JVM$"<97KG1)\IN,PO(O
+M`LBZG'SVW7KO0/\`EM)7ES_LVZ?I@@#/ZK;M1L9C&Y6/TULM!F%LV[8;.KA<
+MJW%ZCF<J_0;GD^TK/H`F#(RM.S;@9E9;2%Q#-B%4J23)5FJ[$;0H'9$'"NC:
+M%Q!R!LJEVZ9$[JQ<``0%4=)))2I"I-@:MH69XEZBRUMW0[*L]1O!0HDD@0O.
+M_%M]<]1N#:VY)DY/99SRF,>CQ>/VKCO'WB&XN*[F4G'1/=<5_P"H=4)=J))^
+MB]1;X)-2B:M7+CG*Q;OPH^WNH#-0!Y*S/),7T,)CU'-65.N6B7[9C=/=U"PP
+MYQ`[%=)5Z=</=HT&6M$``<*.MX4O:C"]M$GY*D\D^MZD<9=/<XZI,'&^ZJ5-
+M1$SD;KL7>$KL/=J88!'U4'4/#%U;VVMU+*Z8^6.>>,_+D[>L^G4EKB,[KN/`
+M7C_J'0WL8Y[WT@-B<A<;5I:7DZ1`*C:)$?7==;)DX98_*^EO#7XT6!#65ZP8
+MZ/ZCA=WX?_$WI=X6M;=TSJ'^(?YKXQ:7LR.V#W5SIW4;FVJ"K3K5&Z#OJ(4]
+M;.JX7]/C>GZ`>&NKVUZR6O!'L5J]0H"[MM+&APXY7R9^%GXM_P`$&6_4*NGC
+M43@[^Z^A/!7C_I'4K&F]M]2!C(UC_-7'/?%>?/QY8)NI]*+=1##J"S)KV57)
+MP/==1<=2L[QYJ4GM+3[[K!ZZ^@X$-<TGY6+X_L,?+9PL]/ZH*@$.RM6C<AXC
+M5MA<&XUK5WG-=Z>ZV.C]4%0:=0D?JL[LXKO-7F.ANJG:%&:GIR1\JLVL'4HE
+M`:@.)]E*:*\+3.HX65=,TM*T*YEL<*A=G$AWT6:D4:DM)(Q[*"H\`8E'<.WQ
+MD*M4J0V94;3T7CS/E;72WN(#L^ZY^W?_`#-UN])&IN^R%;5&IM`^ZD+B<'Z*
+M*W`@2,J5Q:<R%4@'1F/^R@N'%H($J4D..\*"JZ'9=ONM0JMYD/DF4!?ZIVG=
+M$\"<"`@=I@P<JQFHZCH:"3OPJM=V5-7C^DX/=5:T8$_Y%:16K'(RJ-V\3\*W
+M7=B"0,K.OW`M(!$HBE=U0V<Q&ZRKZN`XD#=6[Z9)+C)Y61?O+FNC_LK%4.H5
+M]6RS;EY,Y)"N5PTSWE9]\XMIEJM8M9G4[EM.1J]BN3\17H-+TC2)S!6WU60!
+MDX).2N.Z[>.8[:`PG(S*N$Y2ZDX875GDO<"9Y`5*@WS;UM!LET]X*>^KN=4E
+MHEP)VPM#\.;=USXIIO$PS,_0KU7C';S7F[>R^&Z!LO#5*B['IR5A=4JL-P7&
+M)GX6_P!8J.IV082`(Y7%=0JDDN(F-EZO%CK&1\_+^657V=2H!L2?NG_XE0_Q
+M?JN6>^7DR<^Z;6>Y^ZZZ:]8X2X9',G;<JK6:7$RT;8"T[H>2#Z!K/YIV'^:J
+M5:[)E[6O`P1IC[+S/4H5`X"=B@:(SOPK3G-8QX:^6NSIC*KD@B>WZ(!J%SW%
+M[@.V/]^R",_F@SO*DC;4=QE"6AQ!(^@4`$&`"?@=D_IW=O\`*<S$D;93;'V/
+MU5!@!T^G_1(M!RUV)(]T-/)B?U1,(0L,"=X`GW3M$&3M[I/<"^-(`X`3O:0=
+M+I!!RWE#02-+CS'9,"X8.W9$TM!!+2?JGJ5-1+G'/=%`!IW$RA?!='T4D>DD
+M"4QT<S@930$3L"9&R,MD#@./.R8:"8[[IHT8RF@XGD;X2SJ`)."B8!&3PB+1
+MJS.1B4T@0SW/V3B0=XX12)TI@[!$H(W[\DE)[B223]E(`#."/=`\N`TF.\HH
+M,:M\$H07$0<A.';<G9)D8!X13.U9V^J<.#1C20?NBT@R)`"8P0`<'NH@2XAT
+M-/\`9(O!.70GX!D&>83>K,"1\H&:]P)(P"$W!).4^\"82()$3@I5.3@9D'A%
+M4)D&2!P/[H&M`=C[IVN,^HC!W1#28QGX^48)W:0.P3:0,@X3L#9&H2#P#'ZJ
+M@8(=)@E.Q[PSRPYL.()!'(_[I&(Q$#&R1$G3C3V"@)@)D'2"9WY^J1!Q(]Y'
+MPF`Y..Z<CU`MB"9@H-'H30ZX)+=PNF+'MHC2L;PE0\VKS/M]5T=>GI:!N3L.
+MRXY]MQ>\#S_&R-H7K_0!#6#;"\B\$M#;_3.)V^B]=\/D:6CLN&?:NKZ61H$#
+M$+6MB2(_99'33D"`?JMBS:/3&WNN5;C2M&@C;]%HT@UK?=4[6=H5@XGWX4;.
+M\->\F<=D-=H&>>45(-&\R%#?/`9RFH6JE5TO+0=E!5.AA<?U4X#34))^JQ/$
+M]\*%`M!S\J6Z;PQW7.^-NJ$%U.CDS$?9#X/Z4W4;FHSU.S^Z;H/2JO4KXUZC
+M26S*Z\VC*%`4Z8V[+S[W=U[Y)C/6*%VVF&>6P?943TEMR\D#=;=G85:]3;=;
+M-KTSRHD9"7^79N8].:Z3X48^MYCV8/9=E;^#;+^!DDZHSA6*#&TFB!GNIA=7
+M'_*\PZ2NF,QCCE<KU7']0\.T&U7`-!,[KA_Q&Z>*'3W,IXC$KV2YL:CJ+WR`
+M<8*Y'Q7T(WC7`M)XPLWCITPRW>7R]U2VT5G`;?XEDUFAH&73)7K7BKP15I7!
+M=3:8G:%Q_4/#-Y2.H4G.S!PNF'FDXKV98XYSAR]-Q>T3.,J1N@X!,QNM"YZ3
+M5MVZGTG"/LLRK2J-,220N\RF73SY>*PJC@&2''.)'T6MT+Q'U+IM1AM;VJS$
+MEH<8^RQ'3N1'M$)WD:,8'9:NJY^M>P^&?QDO;6W#+ISW$#=I/WW6YTK\7&W_
+M`%1E#4X,.[G'_5>!!Y`#I$D3(&ZEMKFI2>"UV1V*SZZG%<_VL+\?973>HV_4
+M>EC0YI,<+.I7KK._`+S&VZ\6\`?B(ZRMA2KU':F",G??W6[?^-FWEXTL<2'<
+MA9\EEF[VY>/PY89:^/=^G7C*EN#KD0KC*HD1LN'\$]4-Q9M:7#@[_*ZNA4;I
+MR5REVWE-+I=S,A5;P#08&/V1!YU3M*KWE26D?LCE5"[AI,;*@^H3G:.5+?U#
+MF8*HEQ\S<%9K<7+,GS02<%=)TD^F`87/=+IEU0?JNGZ:`U@GG@++5:=-WIP4
+M-1QA,PB,[H*AR?=:VD(N[[%05W<3*3G9CA05'K4*%S\\QW*C>^!_JB,<S)5>
+MX<!@G"U&*BKO.HSM[JI7J;F?U4M5Y,R#]51N'N&6XG?*J`JN`)^51NS&21]%
+M-7>8D[G=9O4:@`,.CE61%;J#Y;`B?98M[6`:X=^2KMW7#3_=8O4:I>Z!WE;2
+MHJ[A$G8K(ZE5AKAJGOE7;IX;1(<[/>5SW6;EK6%PF7;DJ,=UG=;N@V3/I'9<
+M5UJX#I@2"<-E;?7[II9`P`5QO5;B:CSK)/)7;";Y9SOQ1N:LO'IDCV7:?A/;
+MG^.%PUD!HWWX*X>QMW7%ZRF&DS/&Z]L\*=(H]-Z*P-/K(EV9[_YKOK=F+R^3
+M+UQVGZ_?L<!3<<M^O9<CUV\ITNGN/G>H]QM_N5=\05A1N708U&%POBJZUO(!
+M)R9A>SC%Y<,?:B_BJE3U^:&SPEY]3_YPJ%NXFBTM+H^`CEW=WV"Y>U_+U^D=
+MGXR\-]/;3\QE,LJ'AIW7$]0Z8UCR((/*],ZXUUQU`TW?E'U4;NC6M>@T.'U7
+M.8UPGD]>WC]W1=1`U-)CF5"_2*8,[S(A>H=7\&"Z9_)<1&=ES]WX!OZ;=37A
+MXY$;*;=9GC?KC3$("<SO*Z]OA"I3=_.!!(V*AN_"=<#71W/!57VCEB0!F<I2
+M#CGNKG4NG7-G4BM3<!W"J,;)+9`GNG#1SIU[X/=+T$-TM`(W=&Z?T>G&P[I,
+MR<'[!4,79QQC9.(F7O,DIZE-["0]KFD\0F@3.<\(&!&0#)E,2.,RB:&YU`F=
+MDTCL80(#G>$PTG`$0B@2E`T[01RH'I`!DDB>ZF)HFU`'IJ-,&,Z@?V4&S!P1
+MN40B<';.RH<@#'VSDHGZ0"21)0D@SEL>R9VEP`D;=T"D$$`1/NA><!H?,)P0
+M<')X3`$/,('80T;QE-<5"][JCX)=V$)8\N5&X@C`R/=`+LQ&D0B;#3L(GE"X
+M#&1LF`':<**E!/&W*#5!+M2?<`2?9,6P"`<3L@0$\X3%N)B.(2]H'W2@MP8S
+MVR@0`#`G@AA`&"A'[>Z378.<[9*!H)VPG$#!PD!P?NEIF3.RH36@8)!E2!H<
+M,`F/=`W><%2-<6[8/SNH!:!D@$CY3:!S(/.$5327$M$#M,H#$D"3]4!M!([_
+M`*I])#])!!Y!3"-')?QV2+3.<(.H\&4PTS`&)(717=-C6^IO')6+X+HES0X+
+M<ZDUV@/.RX9]M1;\%TP+T$N$+UCH(_E-D$87E?@5A-ZQN"<KUKH;1Y;1,D+A
+MEVTZ'IHV&J%N6#1@%8O3P0=]^ZVK!IQ$@=ESK<:UH,3(PIVRX`<*O0/V^5;H
+M3IE1H4%C252N*PIU0_2UPWTNY5RZ=%,R8^%B7U:*AS"59-@OKEM.B7%WJ/9<
+MK4I5NJ=3\O<3PKW5*[GU0P$$=UM>$[*G2?YM025RSYX>O#'TFVQTKHM'I_2Y
+M;AP"IN:*M<ZFR"KW6+TN8VDQ^.<H^F6C0SS7%3+FZACQ-T_3K713D`2KF'&.
+MW"JU;JG3)9J$!5+OJ]K;-U/JM[0L[TWVTG@-9`!D=D5&EJ."!]5P'B7\0K"R
+M:[36:2/J?V7'W_XK=2)BQM2_$!SC_HF.6VYX<[-Q]!4*376Q:^X$Q.RSOXJQ
+M94?2-1I=L25\U=1_$[Q@7.:ZN:378AK1_DI?"?CV\KWX9=522[NMY7C<A/TN
+M7VO<.N6-I=UWFF`0=EC5/#=*H(\L$'V1]"ZD:U)KM0,A=/T<4[BHVF2/4N.U
+MLN,>:^+/!U&I;.#*0'NT+R+Q)X9N;*LX%AB5]8=?Z53IT6N:X%K^.RX/Q+X<
+MHW#S_+&<_"L]L:WXO+^7S1>652FZ/+/RH1:U0X^C=>T]7\$->\%M,#/`_P!%
+M?Z1^'=@*7G/I^8Y@D-=^7]EUQ\UO&G3.X2;>"5:%2GAS7=LC*K.U,<=M)^Z]
+MF_$;P>RM;:>FVK35:9FFWCM@+R:^M*E&X=2J4]+V&"'`@A>B>25QF,RFX73?
+M56;F`<+T+HMK:LZ<7ZAJB?=>>VC8,!H!'NMNRZC4#6,U&!N)6<VKX[9.7M_X
+M:W3C08V9PO1;![B!J.5Y)^$]VUP#>8WE>KV-352;F)"X8=/-Y>*O/?+("IW-
+M0EN\2CK/AF<*G7<)SF5IYZI7U0P1.Z@MP7P3GV1WCB7'&$]AETD$_5*U&QTE
+MD$%RZ*S_`"?V67TJB-`*UZ#2T`]ME%3,;Z!Q&Z"L1IV1>8,Y^?=15GG3GA6*
+M@<<^RBJ[%&^I(Q$*M4<2[E6):=[X=&?E5JQ;$20B?4P2JUQ5):)@?"TQ45P\
+M]RJ55X;JER*XJ9+MRJ-P]V2#*H"ZK9*R>H59!`/W5B]K2>0LB^J03E:D9V@N
+MZI#?4LN[<8)!W5BYJDR#.-EEW=:)DA7>F;RH=5N]!+3D+F>K7;7`AQU#D+6Z
+MS6`$G\IW]ER75GZB'`B)5QA(RNNUAJ(#L@[^ZYVYESBX!IG`!5[JM8/J.:2"
+M>\RJ=C1=5JZ&YU87IFI''+FNB_#OHW\5U-A<P1$_&Z]0ZC3=;64%T0(E8WX=
+M=*=:6HK/;ZG#D*]XPOV,MRRI`G?*Z^#'=V\?GRW=.!\3]4TN>73O`=/^BXR\
+MN]=4OW,_HM#Q;<L-PX,?,GDK$IG^9G29Q*[9WG37CQU&K0)=2:X:A/9%![N3
+M6Y`HM!,?5'+>_P"JQMWY>K6W3!<.-2X>XU%H-Z;28T`-..96YTCI8-/43G=7
+M*]FUHPI:\#EZMI4;)8^?91:'-9_,9`_=;M>@T..<+/ZRP4[7,$\*;-;85:T_
+MC+OTLAE/<R@%FTUWAK9#$[>HNMP6MI2J5;K+V/-3RW#V"LARAZST6WNF%KV"
+M7=PN(\0^%:UO4+[<2)VR?[+MZOB&B7#S*+E`_KMH]_\`,C3&Y"GK^'3'.QY7
+M6MW4ZNFH-)&(.$#6Z3(=MR%W/B2PZ=?,-2W+14W[+BKVVK6]8L<S8[PD_P!=
+M\<YET9]6K4@/J/>!MJ,P@,EQ2+\>EH$)M3HB/]56B>-O5MV3AIQ!W33[!(.,
+M[!$V<9R8$>R4NT@H2XDF44D#;]478>9G?L40&-\?"',_ZHVF#,1[;H'8#ITB
+M!_=)C!)`$^X3`N<Z`W/ZHV4JKGDMI.).ZB;@1,8@<(?ZL*=UO6:#+'`*!\ZH
+M<"JNX8<QD'A`\2["?4T"8(E,]P(](,G=#87"!!'J2:V3$'/*=CF\@DI?U;%`
+M0;'R$+@.\%&YS-,^W`0AS2W$_0*:-E3:/_Q#W3/(G@)PX#(D'YE`7!QW**9W
+MN?LG.D-RGJ5`8@#TB)0:P3)X0/$@)0?@)]0G&R3BW$?H@-P;$S,I-.DB=O9`
+M"W3$[;82U`YF(VP@>#J_*G<"3B#'NF+F@"<%$"W2<CY5#!H'])4E,'S(.!*C
+M8YNTX4U)S-;9<`/=!V_@YH;:`[GN"M+J$:2"J7A,--C(``*L7[FMSJGA>:_V
+M:G3:\!,+KO5F?=>J=$PUIC/LO./P\I:GDD;<KTSHX!(@B%QR[:=!8-#F#=;M
+MBV6[!8UBW8@X"VK+`S]I7.MQH4&D-C!5JD1$QRJ=(G?5CW*G+@UO/U*C1NH.
+MQIG98/53II.).5K/<ZH[=9'7ITZ=62=I6:Z^/'EA])HON;_(P#RNUITZ=M:#
+MXW4'A'HU/RA6>-U9\0!M"F6@X"Y:O=>GVENHSF`UKD.X'"N]6ZK3LK')&`L2
+MCU!E%I).W*X?\1O$%9[#3H.=]%-Z;F'M4WBGQZRA4>RD[4Z8@'_1<9U#K_6.
+MK.TAYIM=V_[+.Z;;NNKXU*HF3RNCI6S:8V(Q]UC+6+V888RZ8M#HQJ.#J[I)
+MXW72]'Z=;4:`UM#BW]$UG3!>T`\\!=1:VE(V>&B8F96,;<JWY<M33C>I=,H7
+M37%M(-DKF3T?^%ZH"#&<+OKRF*3W`-()Y7$>+;Q]"\:[8`Y*WC;T87EZ]^']
+MH;JFRD'0T-G==)U.>DO8\5</&/9>0^"/';+*BUKC!;B94OBWQ_5NS.LN<-HV
+MA)-37USR\65R_P`>K'Q#Y\,K5AC:2KEM9U.HTP:,OC:,KYLNO%767534HEQY
+MQ*[O\(OQ;%A<ML^L![6N(]2W,;]<O+X[C/XO3[FS-$^5<42UW8A36%"DRF0X
+M@`]U<ZUXCZ-UGIC*MK7HOJDRTL()`6795]>IIX4RGK7+&^^/*+J-K0\Y^FG@
+M\B%Y'^+'A9U6[-]0HQK)+H$?V7LKP"X&"8[Y5#Q#T^E>6I:Y@)`X"U,JL_AE
+MN/ERM;NH5M&W&$SG%A!'!7:?B%X?-A>NK"GAQ.RXVY&C):"?NNN%]GJLFMQZ
+M3^#MVYU4#)(`PO;.E5?Y`)'"\)_!>/XDQOC9>Y=.`%N.#"QCW7A_4]IKFL9P
+MJWFD.)F92KDZL$2H8=&ZUIY052Y[HTX'<J[TJB9'(/"JL`+S(6OTEA=&F%+R
+MU&QTVD6P3VX5]N)B3"BM!I8./JCJ')`D(L)SA\A5ZSW.$94PES<E07$`Y5@B
+M+AM*KU7[G.5)4(C4#E5JAF3*K-15'G3NJU9\C.?E2U'B#PJ-T=M)*L9J"O4.
+MHJG<U1!X5BN]K1G'O*SKVL)QL5J1*K7E3<+)NGSL=U9N*AEPX'*S;JIZG9$+
+M<9M4KUQ;,;+%OZT.,DRM.]?.`?N5A]5J,:TESC[*5F,?J]P\M@@QRN8Z_6;2
+MID`DK<O[F9,;#>5R'B"OYM<Z<&5UPG*Y<,BLYKZAF)/*Z#\/>E5+SJC2&RS<
+MF5D6W3:CR):<G=>N_A;T5MOTQM0@A[ARNEN^(XYSUFZZBUMJ=I8-)(V7EGXM
+M]3+'FFS(G/9>D^)KC^$L'DO&.Z\$\?=1-UU%X#@9*]N$]<=O!)[YN?KU75GE
+MSC]$]#)AT.4#!!)@94]`MU`M])YDKGMZHV+=H\AL=NZ/2/=!;$B@T3PCU.[_
+M`**.D?45M0T4Q&,*/J-.*1(.0K]-N(4?4*8-J9WA9KP1S=.F*E4DF0J/6;8U
+M(80(6U:V\`F/=4[JWKOJDM<T#X5HY>O8-&/+6)U:TA[FL!E==?VEQ@4W>HGZ
+M*B[H]:"?,:2<J[9U8XFO8F"33#C[=U4JV&J"VWJ./,-PNZ_X34UD/=3(`[*I
+M?V[Z3'4F:)(_I!E7AKVTX6XM'3Z:+A\"52O;'63K9QRNO=;>HSF3F6_Z*K6L
+M2]^DAHWB$TU,G$U^E,V#=NRIW'3:@P&Q'.<KN:G209`U$A5Z_399#HD8W33I
+M[N#JV]6G+G-/91D8,@CV7:U>E-."R6QA4KCH#:KCY8R3QNHU[1S5O;5*];12
+M:7$]EOV'ABYJTVNJ,/\`DNU\%>'+6SH.?5HZZIYA=*;5M8M92HD1R,*V:<KY
+M?P\YI>#GZ-;F&&Y(!2H^'Z#20:1WB)S^Z],N[#RK1VH9/SE5K'IE%C-3F^LP
+MI$][]<YT#H/3"]K'6GU)*ZRT\,]/`&FB,J3IEL&WD1$8E=)1I!N,1&W9,F)=
+MUSQ\,=/J`M?;-CZK+ZQX!Z7<L,4-+CF05W(9$@#*;RR?21G98;FX\0\2?AY7
+MM230!>W<>RXWJ'2+RT>0^BX#.87U`^R;4'J:"L3Q#X5M+NB6FD,[8Y3==,<_
+ME?-;VEIS@;Y0$D#!F>R](\9^`JM#55MFN,3L"8"\^O+2M;5C3JM<TCN%9DZ2
+MR]`+2&X>'2.%$Z0<'Z(BT#(G[)B,SJ)^55"X&9+OT3C3J.N8CC>4[L`2XSW"
+M"#/O/952@SB)2(.=@"EDDP=N2E!F"5-!S&H$'YGNDXX@"`G,$C(R4H,Y('8(
+M&D:<8*6(.<!.0-(,C/"8`C,-A`[M0EID>R9L`?F$)5!)&!\)RXN`DM$"!"&B
+M)VV4U#);)D_"KM!QLIK=L.;)[0@[[PN\LL8+1D0K5;0^L`=_94O#CW"PV`("
+MOV-(FZ$B<KSWMJ1V_@:CHI-.,Q*[_HU.3Q(7(^&;;R[9I;'==IT%I)!WG=>?
+MMJ.AZ:,`&/JM>U$#(!]UG63)B``5J6W&%ATBW2$-DQ*BKO!@$A$ZH6L,B<[*
+MM2FK7C@<)5BU1IC27'D;KG>M5F_\0#?Z6E=)>'R+0_\`V]UP?6;E[KXEN=.,
+M+.7$>KPX[=M9=5HT;)H:1JB-UG]2O/XPD`R5SEK6K5G`$D#Y6WTNAL9RIOV;
+MN/HJOZ:Y[#C#ED7WAAE4Z],'.05VM-@TQ"<T6:9@9"OI*Y_N91Y77\-MLZVN
+MG3@9G"HWS-+2US8/Q*]2O[:F]A!;L%Q_B7I#7-+Z8((,X7#R^.]Q[/TWGYUD
+MP.E-+GAX$`+>I533`@[X61:L%$QI,K1H5-5.<_!7#"/5Y;LUX!4<97(>-.A"
+M[I%U+<<+IKRLYK@!LJ3[C,O$3W6\;-[8FYT\O'2;NA<Z#J`"Z?H?1`ZBVK6>
+M2#C*W+NE1J'4*<&9E2L#!18QQB<+>7DV[;W!6?1+048+`0<KFO%7AGUNN;6F
+M6B3M*ZZA=0P4VF>)5_R`^U@P9R0ICDX7^-V\M\.^(NJ=!O12KU:A9.Q<5['X
+M-\56W4J+7-JC5&1J7F'C?HYKZZC&Z2V<PN;\,=7O.D=5;3\UX;J$S*ZY8S*;
+MC7KM]26EPVJV0<*VRDUX@E<;X$ZFV_Z?3J!Q)+05W/1]+HU''NN.-<?)CIP'
+MXI](\RS?Z>"05X7UND:5RX"9E?47C^WI5>GN`'J((V7SCXUM'TNKN9W<?W77
+M&ZR;\-]L=.F_!JDYM0/)[?5>SVE0>0-UY?\`A+:Z;5K]($QO]%Z53=%./9;Q
+M_+Q_J;R.J0XD\\H@TEL]E'1)>\S@?NK=)GNJ\RO0HN=5Q^RZ#I%#0T$B%1LZ
+M4OGW6Q;"&QNI)^6MKU(C3[IB<2@&1GA)VV^$#/>0<%5[I^KE%7V@*K5<"2)C
+MY5*&JXD$*O6=C*.K4$C.54N'DM*UME'<$`95*LXP7$XX4E=Y.Y52Y=#<A6<L
+MJUW5ANDF?A9ES4()SRK-P\@N_NLZ[J``\>Q6XEJO<OP<CW67=5-),00IKVZ:
+M`<B?8K-K5-1SLM6L(+BI+7%P``7/];K0,EO^BTNK5@QCX,_5<CUZ]TM=+IB4
+MDW5GY9O7;T-+F-W)[I>%^@UNI5#6<UVG>2L_IUN_J74FM@F#W7KWAFP%ITVF
+MQS0T@!/)EZ_QC>$O]JY9WAUE*HQH8#GOLN\Z#:BUZ<R&_E;M*A%JVI7'>96G
+M=N;0L]($$!=/T\W=O-^KSNM.%_%2_=2Z8\N]P#_L+P?J-P:UT]SA.>2O3/QA
+MZPQSC0.'YDYRO*ZT$EPF">^Z^CGQ)'D\,[IPX:9C8Y$J6@X:MB1QE04CISG(
+M4U$G8$D;KD[[:M&L/*;B,(_.:E0/\EOIX1R?\*;:?6]-LF..4/5`T6)(^(E3
+M`>O"BZL6_P`.&]S"S7AG;,MJ9-N9W*KW-,LIDD+5I4P:0T]E3ZE2.(Y*7LC$
+M;1+W&JX8]U'79`@">ZUC0B/A5G4QR$5C5K=[S$P-EEW5JW4=()DY*Z.ZHN/I
+M`W45O92XN<`594L<Z_IX8UQT@GGG[JC6M@7D"F#$X"ZV_I,93(@#N0N?OG_S
+M6L;!^%J7;-FF<+`5(B!.Z*MTBFYVD-(,296UTZWU09$QRIZK#J+6MGN4I.>7
+M*5^B:1E7?#GA^:HJ5J<P<"/A;K+=]2J)87-:03A;EG6ITP&BE$>VZFVN?K,;
+M:.&&T84U&W=2;JV'*U_.81D0J]=XK.AC<`9*RK'=1?6JESR8&P'"(6S7'5L5
+MI.I:<D83,I2X''O[JIJ,RU8/XX`+HJ5!Q:#Q^JR*["VZ#N/W6_THM=3`SVE-
+M<$%2M&.;*;^#@X&W"TK5D-@1'RH[JO3I#W66E;R&-9(C*I73J3<$H.I]2@$-
+MQ"R;DUZX#FEQ)/"*+J%.A5U-(:00N"\;^#;2^I/JT`&O,YC"[BA87`>7/<2#
+MPK(L6%N1/>5+(2V/F?Q!T:ZZ9<&G68=(Q,++R!!_5?1GBCPU9=2M7T:E(:\E
+MKH&\?"\+\7=&K]'ZF^A581DQ@Y"2_*[XY>S'(+78^$,N$"20#LB,9,-2](=.
+MX6V]@R9!.-TAZ20<@H\-<<80^GDJ!GD;MVE//!(^4G``CU2$B)/:!RJ'+70"
+M!B,IMP9@GZH@,.]0CLF@`X(RH!9)SR$0+L@@;93M'NT)R`08*`#.W^JFMI;4
+M`/?91[GTCVGLI[8$UFD[R,RE'9=!J.;;MP(WRM[H=$U+QI(_J!/W6'X?IOKL
+M`I@O($P,PNN\+VX_BV:A#@X#3O[KS^2M8NXZ-3BDQL8@0NOZ#3.(;(7,]-I_
+MECV76]`I;'(G9>:UN1OV3(;"T*8B3A4[1L08.(W5L;;_`"L5LUQ5TCYV1=+R
+M^8&ZI7+R*D;B5<MG>30<\[*SMJ(O%%X*=$M#L[;KCG-%2H29"T>M7/\`$WD2
+M2`=Y]U'2IB=ESSNZ^AX<?6%TZC&\86W9PT<2JEA2(C]EI4Z8+06C;*8L^6I:
+M#CJ$[>ZGJN`$3*@I@QOD(WCT223B5TCR54NH<9V!]UD=8`%)P.=X6U5I'1)[
+MK'ZO0<_N1"-8WEQ%_5-.[+'0!,J0NBC(.47BBR=3)<!)X/98_P#$5&MTF8B#
+MV7BN/K;'U,,IGC+&E0BHZ=4S[*MU:V>'C0?S=UG'J8MZT-S`R"-E;/5VU'-<
+M3'?W6'>8V78+BDZB1JW/"AJ5#(SC?"FO7F\IRPR&_<*G>-JMI8!!GA734_U-
+M9OU5&Y]MUJMOBR*>'?!7/VCJK()'/V5FGY[7E^C$R=U>9TSG)>TO6'^8QYT@
+M3*X3Q'8-;4\YH#3JE=TYS7L=KXGZ+G/$S6N<"TG=;PRU3&?':_@W=/%G!,@1
+M$[[!>O>'#YC))7BGX4/\MI&"TD<?"]K\+$&BV(V"S)RX_J`^+@W^$/!SM\+P
+M3\0:;*GB!C6B<P?NO=_&50?PS@#!*\2\16GF>)VG4YTNQ\RNF5TQ^G=K^']D
+M*=BR!B`?V74D-C3MA9OA6CY5@P_].WT"U`S4[;`77#B/%Y;NI+2E+I/)W6C2
+MIPV4%E0),1]5<;3.T;>ZUVXGLVB<#"T[<"`)RJEM2@29@JVQS6CMP5`6N9$[
+M)B_?$H7GU$YRA=F/]PB[1UR""(S.ZK57"3D*6L8=`XE5;ATR`?[*I45PX<%4
+MZKY)"EN'&<'95:E4##HSR545[MXW!]QE4+BK(._,*:\=(D'?LLZZ<1SLM1$-
+M=X$DG'*R[^J"QP`^JL75:6D?NL:[J$DMG'RM=,54N/4^2,;X56\K"G3($`_*
+MENJ@8'.]BN=ZY?8(F.P":3M3\1=1\LG0<D0N.ZI=NN*WE@;G,*WUZ[<7RXDG
+M/TRJ/1;*YNKD5-#BP$$PNLGK-M2>U]7>_A;T!FD7503,;_1>@/MJ;6P'8'9<
+MIX,O!0H-IC@!='6N@ZEK!P5Y9=VV]O1EQ1VA:VY@GZRL7\4.N4NF])J.%0-<
+M00T'X*H=8Z_3L:[YJB0"=_\`1>1_B=XG?UB[+!4FFTD8/N5]'])CJ7*OF?J?
+MYY:C!Z_U*O?WKZI>XAQ,_$K-,$`R<=D(<2<G?W39,9]EVMW>222<)A!S$1[J
+M2W$.S('N>5`S#Y))5BEE^J<!0:K':6ANO[)_,_ZRAI.)IM/MW12>WZJZ:T^N
+MZ5P`_2[!E1=5J--:FTG!5VO;LJNEV"/U697LWU+\14):-@?E3?/+PZ:%%@T2
+M"J-PT.N''LM"V86TG`G8*"G2PXD*6<D4:S(DJE7#9U#9:UQ2!]BJ=2FT/,B<
+M_1%4J5N7NSM*DKTFTV$#`.%:)8P>F>Y"J7+BYQ(!QL@Q^L-F2(CW7-R/XL@R
+M#,1WRNCZXXMI'(F#PL7HEK1JW)J57$F<`;!:Q9R:?2K1SAD>DCLM!UJ&TPP9
+M)Y[*?IC=1\M@)^FRV[7IH%*7"7.4JSABVS&V].`V4KDT=.K+2MJM94J#2YX5
+M2C:-NJWJ9Z`5!B4Z=6M7`)=H!6E3%%K`QC8`WE;#K.C2ID,:-L*O4LV:,B"4
+M5EUF-VD(6-&DJ6[MW-?#23W4894:<Y[HJM=TX.<+:Z.T?PXU3[>ZS;H$L,XA
+M:70P'4!WV*J+6IT0'!5ZM`.)+R8]U?%(D845R`QDQ/NIH8]]9TMW.`5FRL6>
+M6W2,#"&HWS@2[8<*WTTZJ!#>$4%:U]`[JK5MXD8^%K.!B")Y45:GJ&`)4(Y_
+MJ-$-,F)',+S/\;^B-K]/;>TZ8ULG41]5ZQU>EI:0N5\6VS;OH-PQPDACL`9V
+M6;TWC=5\W/;I,$B9Y0-(@AQCV5[K5NZAU"HPA^'$01PJ+V^H&<>RW.GH,?S`
+MR/2A&6Y."G<T9]1CW3L9+9!V_54*`1!@F<%,]I!'WRD`(*(N+_S'`;`Y4"9@
+MDEH'9,9#R,&.Z<M;@ZYE,UH(_,&D($))&!"FKTZ;'!M.HVH"`Z0"(/8_"B8R
+M=C(]EUWX<>!^H>)KYHI,+*,P7P/\U+=08?0^BW74KHMMZ1<UHDQE37UDZUO!
+M0<S0\8(/<%?4O@[P!T_HG074Z5(`Z,GDF%X+^+5B+;QC5#,`N)CZE8F5MTF-
+MW4?A.C4#)!@#<@PN\\(VN14)!G<KDO"]G_(8=4!T`E>A^&[4-MVSN0O/Y+RZ
+M3IO](8?,!P5VWA^A+`2,_LN4Z'3]07==$I!M`$`SN5QR;Q6Z3-.3A*X?Z8'*
+M*J0%2NGR)#MEG;8*9UW')&Y5CK;W4.GD`XC*J].!=6U$\\H/&%8LM(#MQ&ZO
+M4M;\>.\I&'9ESZI<3N>ZT;9A<X"%0Z6PZ0MRU8-$[+CV^E>(L6=(M'NKU)L&
+M%6HZME<H`XX]EK_CSY#HTB'$Q\IZS0)QGA6J8]$(+BCZ9WG*Z:T\][47@EL$
+M2J56AK!!VC"T7T^9F<(6TAIR03^R:1R/B6Q-5FV!V7'7UJ:50M<"TSNO5;NT
+M\T&`/DK#ZIT%CS,;K'DP]NG;P^;UXKS*^Z8*M,U&$XS'99CJ#V/($QL2.%Z+
+M6Z&6N=3@D$=U7'A-KGA^D_,KA<*^AA^KQDY<WT%M1M,`G<YGD+:LNE"N2X@^
+MK)"W+7H+*8$@86MT^R;3&G$RMX^+\O-Y/U/M>&%9^'62)IQRKI\.T6TSZ=^%
+MNM9#X4PIE[<@CA;F$CC?)E?K@>J=`EITLW[!<5XDZ)=4*@<X%S9WS_DO<:MF
+MPMG3)YPL3KO2*3V.+VM."5F^/['?Q_J;B\^\!:V513F(_39>Q>'+AM"RSVE>
+M?=%Z0:5\1387$NG&5UEKYC6:#+2,0N,WOEZ/+E,X+Q1>.KL>&C2`"%P%C:ON
+MO$)>[=KI_5=]U.AIZ<XXDCGX7.>'*&GJ+ZK@)G?ZJY3=C&.4QETZCI](4K=L
+MB/3'Z*]:L+GB,RJE-X>0T;+9Z31)C"[O#GVOV-,-I@$94H:"XP8]U(U@#1,=
+MT3609X*TY'8W2/\`-$^8C]T1<W3G!4<@SGX3I2,<G]4#WMCB4SB)[*O6>`"`
+M,H%6JX.)5.L\03'ZHGN#N?DJK<56Z2B(KBH!/;]EGW575($;_HCNJS9)G?"S
+M;JX`;@\PKH#=5B)$X`6=<U23B3WE'7KR<E4>HW#:=(P86Y&;=*_4:T&>RQKR
+MY$$@?)"DO+K7))^ZQKZZ9)]7^15W^&-;#U.]`:>T&%RG6+AS@XD%L;$]E<ZK
+M=M=K=J.!&ZPZ[JES6\NGZN)E;G'-:F-MU%$TVW=T*8&[@)]EZ!T2UM;#I0UA
+MLEN9^%SUMX??;VPN2(.\^\(>I7]RZU-(/(XPLV^]FG2X>N*]:]491Z@[2,3M
+M/NK'7?%3;7ISW`^K,"?9<97J&C3+G.)>>5S'B;J=2XJ>7K,-VDK>'BWEM/)Y
+M)ZGZYU^ZO[FJ35(:02<K#>_5,YY.4QD&)^@,IG.$@-Q`7M^/%KD[8&0$[RQK
+M@&O+\"3$0>R8`:9#OLA;^8`$F-U`='<XV'=3T##QNH&EI@@$"5/0_/B=UH:M
+M(#RQ.Z*&]D%%H\IN"<;A'I'^%RJOLL@&J9V"IVK1_%/<),E77B0\S&-U7Z1^
+M=YW,K,>&I+B&MTX!*<TXI0,)5Z3S6EIE1W#KH`Z::G`K78@9[*C4C)G[E6+V
+MG>53);I`506+R^:E0_"32AK/I@$:A.RI7-TQH(:`>_*T*EE2&3)/N50O*3=6
+MFFT0J.;ZW4N*N=`T9WRKG0K)CZ8].8RFZPUK7BGB5J>%J!=5;3`QW5B5O^&N
+MF-ILUEGW6U492I,U&('"*U93MK8<+-ZC<&ZJ>32&.2I2(JC?XRX@1H!^ZOT;
+M>C09@!!8VH8,1`W4M;TC3,E155[0ZH2!@*"Z:`PGG97"W0P[R51NG3,\JHS+
+MIH%6#GW3TZ>)`VRB<-50XDJQ18XMC^GV**HWUN#3EI(/96/#?J:6<C"?J\V[
+M=%5A:Z)`/94_#URX7;FAIR@Z,T_*&=EF=0>Y[M+0=,[K2%.K5:2_TM[*C?-#
+M7P%"*XI#RH4?3JGDW9IDX)5ZFV:4++ZAJHW#7C8%%;=1P#9(5=M:GD$B5:Z6
+M^E=VW$J.[M:;7X_109?7'-\B8V'*YSJ`\RRJ0W)!X6]U\.;2ADD'NL^C95;J
+MB:;&N<7!17A'COH-?S'W%*@2"XR0-\KC*E+2Z#+?HOIR\\-U*5!S*M#6'`_F
+M;*\_\5_AN;FL:MHUM.<D`0!^BF].V.?&J\?C,DR$VD:1E=]<_AS?TR22"!M'
+M_99MWX(ZE287"DYP'8+7LW,I^7)P-6P/MW2<!I@?57>H6%Q9U-->DYL<PJ;@
+M-]L;%6-!QK.>>45-OI=$']$0;(&?T75_AEX2K^(^LL9I(H`C4Z-PK>$WI9_"
+MSP1=>).I-<ZG%NPC6[[+ZF\"^%;/H]A3H4:8:&`9"@\`^&;/HW3:="A2:T@1
+MA=93<R@WW[KE>>7*Y;1=5IM9TU^G,-*^4?Q?/F^-JH@8,9XR5]5>(;@CH]4,
+MSZ297R=XOHU*WC"NYY!)J']UCZZ>.-?PI2.BFTNYE>A].IM%```+C_!EK+VQ
+M]EW=M3`8T2%PR[=&QX;I@U@/<;+M;4BG2:-H"YGPQ;RX'3]ET55NEH`Q\KCD
+MZXP=>I+`%3O'@-V$]RIFN&C:52O8\P;JQ?J[T:FXF=_W5#QDX:6,`,DP95RT
+MK&A;:R`/JL._KNONH`?TM]UG.ZFGJ_3X;RVGZ91@-$'LMJTMB0(V5/IU+T@Q
+M_9;MDP!@$+GC-O7G=!HTM+L[^RN4:)<V3QW293&J0(5NA3C<+I(\V5/0I@LV
+MDCE%6;#/RSP%+1(:T>Z50R#Q[+;A8S7L()0O:3!.`K5>GG=,&R,$I$JN:+8]
+M2J7K&M.`K]P2,;\3*H/IFH\N!='*;28LZM;ZG!WU1"FW#2-NRO5J$"&E1>3[
+M&=U-JJ5*#29`,<J*JW20&E7GL)$94;+<N<)G=2UN0-"E+`1O\J>FPATDJQ0M
+MM+,C[IZM/&\=D`Z`:9(^JSNJ4QY1['=6*M=P<03$+,ZM<GR2)5M;QQJY^'=[
+MTJTZA5IWIHL>?R/JQ&ZTO$PZ?==8\[ISF.9I&HTXTS*\RZK6)J:P3*Z3PG?A
+MMMH>^2=LKA<I?XZ>C]OUOOM9\37&FEY;")VA9W1[5P`>`02K5S2==W9=F`M+
+MI]O``(Q*J9743=,M"\@E=%84`T"1@*M8T=#0(6C1EL#=:CRV[$`V8C"*DW.>
+M43&S!3NAN%I@-0"/\E$Z&A&\Z22>5#5J>F$5#7=P"55JEP:23*.O5@02%3KU
+MH;$B40-:J(*SKJX&H]D]W7P1*R[NJ2-4[85@&]KC.5F5ZQ<?S)KJOOG"S+V[
+M`;(,+<B6Z3W=T*;")"P[Z\=5<?5A/?575!!,K/NZV@:<''"?XYU7ZE=:&D#5
+M\A874KT-8XU)&.%-U2Z#"Z'`;Y.RX[Q3U@4P0'#7V:=UT\?CW2W4375R^XN?
+M*I9<[Z+KO!'AT!@N+AGNO-?"G6:8ZLPW$%I<,_5>U=%O[:I8M=2<TM+>"L^>
+M7>OCT>*R8[G:;J5"D+8LC`&%PGB&B*1/EM[GMA=C?7M,L<'>I<=XDKZJ;I/!
+MQV6,.W3UX<QU6YHBDZ1Z@.%Q/4JC:E<EP((*V_$5PX.(;L1W7/53KGB=U[\,
+M=3;Y_DYH(`R#E)P).<E("-@3"=WYL2/JNC`=OWRG8#$YVS"<@Q,8^$PD.Q.?
+MHB':#M^ZL4V[$;;J$''OQE3VSL[X5&E0>11:(=MW1^8[L[[I4"XT6D(O7V1M
+M]F5?3;O=(&%!TMKC3)!$%7.HLTV3S,$JG9.#;2?ZE,?KYU6J4OJ;?9&]I&(2
+MLF$-DG)4CPA%.YIEPR85-],`&8A:-9IF(5"\:0[_`%05+MS!1C)*R;V&TRYW
+M/"T[W3IP?HL7JSI],S/"JL:ZEU:??'*Z_P`(6IHVGG/$2N;Z=:FXOZ=-K,DY
+M7<NI"VL0V,Q`"O42\HJ[Z]S4\JF[`W*M65JRD!OJY)2Z;;AE+5&2K3*1W=LL
+MA.`:TZ2846D$R1`4U:#`&WLA<TC\PW156X=B-XQA4*Y&5=KY&WV6;>.+JF@8
+M[H(J=,N?JB)1UZNEHILF5*QH92AIU/.V5:L[+0-=;+BJ*%*R#O75R>R+I=%M
+M/J0`:%I5:0TQ&>,JG1IM'4&NGE23E=MFLS^6LJXI!SR3]%NFGKI<Q'=9EW1(
+MJ$PHJ&VI@TX#54ZGTZI4&IHD%;%C1DR-E>;2:1D)2.8Z!3JVKWMJ,(DX5FY-
+M4U-3*9/T70"U9,EH@)5J=-E.=()^$',5+$W+@:K?HMOHW2Z5O2UAH!4EM::Z
+MWFG;MPM+'EC2<++4C&ZM0IN&6C[+`O+"FYQ$#/9=)?R\XY[JA<4/23"NDKG*
+M_014&IH#F\J,="I`8:%U/32R2QYP<*6M:,95D9:5FPCSCQ1X$L.JVCV5+=@>
+M1AP&0O"O'_@^[\/7SFOIS2=^5R^NZENWY6!XT\,6G6NG5*->DT^GE9LLNXZX
+M9ZXKY2\*]%N.K]7HVE"G)>[/LOJO\*/!MMT/HU-OE@.W)]USGX0_AU2Z/U&I
+M7>`XEV">`O7Z5$4F!K1@!:WLRRW=*8(MQ)&!R@LA4O[L!OY0>%!UFMYERVWH
+MB9Q*ZCPMTSR:#7$03[J;33.\74*=KT&H2-F%?*GB(,K>*JU3TEH<>_=?4OXO
+MW'\/T&KG=I"^4[C57ZR]Q.SID!<LKR[83AVO@JB`T:0876TZ9!$A8O@^W#+9
+MDB)726S`ZJT3A<;RZ3MTWA>D/)!`W[+6O6CRLJMX?IBG;C"N=0,49PN.3K&;
+M3J0TF8`X51[Q4K_F^B5W5TT#IF?E5+)Q<\F?F5N1<>:T.HU&LM-!,X[JETRB
+M7.UD`2E>N=4](SQNKW3[?32&%QSO+Z/AQU&A94\<+5M\-`5.PI>D2`M&DP1`
+MW2+FFI9=LK;*9T=YY4-%F0K=('3S\+>+SY(:0=,%3-9(DIJC2,!/!;DG*TS0
+M5:9[B%$:4L_+[S*FUY@_HA.,]ME6;BK/IDB"-R@-`,W$>RLB29V^J187?V*)
+MZJYH!P@Q*B?9D'MW"O4Z+FOS]T=0"0T*4TSFV8`,G*=EKZQ`C*O!AG`D!.&!
+MIWV[IK:JM6CIID\K-NM9&,GNM.[>`#E9E<@$P2LY5O#%0JAVJ7=UE=8<T&!`
+M*U+]Q;3)E8%ZXU'[K%NGJ\>&V3?6H=+A.1PBZ#1N/XEK1,3]U?9;.JNT`3*Z
+M;P[TAK*0<6C'LN4F[PZ^3*8SE/TRR8*'J;G=7+2T'F;):2RH!,`*[8N$\+M'
+M@SR34*1!5AK2$31$<RHJCBTD#A5R3C#25#4>>\!1FJ2%#4JQ*J)*M0!BIW%8
+M@;&4-Q7);^8?"HU[@Q'=5-E=W!!PJ%W7+@3C"&ZN"2086=<5_>1/":0US7,D
+MG99EY=','Z(KRO`,&%D7EP)))6I"TKRN2#J.0LFZK9,';WV3W=R).8"S:U23
+M&J2[LJQLU]=%K<&"5D=3O@*;BYT$!2=1K:)#IQE<7XJZU3:XM:2#V73#Q^R6
+MR`\3]::QI&K(D`RN'ZC>5*]Q)D@\(NIW;KBI+I@]YRJ]2A79:MN'47-I/<6M
+M?!@GM*]>.,CE:BUEKI`),]UTGAOQ=?=.:&.+BP<$E<T3OV^$+26@B,JW&9=K
+M,[CT]*_\[4*U,EQ+7G=9/5O$5*HPZ'9(7&EPB1@)M4#G[K,\6,=+Y\K-)[ZZ
+M-6K(R"JSW9D2DT^O.1[I2"0<PMN)Z=32US=+7!_^+A!J[3["43HB>/W0[R)F
+M-@@8OQIS'.4^H[Y^Z1T_[RG&?4Z8[JH=IYS]2K-&"\`X]U`(@<_"FMY#R).>
+MQ5&M1@4FC*/[I6[6^0V>W)1Z&?[*;:?:'60!8NGLLSI;?->`#("T_$.EO37$
+M%4?#M-K:0,J8_7@R:#0`(V1.`DGA$\B1G=(@:<E5%:I$E9]U!<9,#Y6C7])]
+MEE]1=`(`^R#+Z@YK)),E85>KYCB2W`*TNIESG\85-E#55#`,NQA6*N>%:?EU
+MG7+VP-Y(72VVN]J:W`AC=IY63;TVTZE.V$`'\RZ&T=3I,T,B$J)6L+3MLI*_
+M_*$X1B"W!4%5Q<=,X4$=,'6FN'0$B[2('T*@KNTMU$[(*U[5#&',$JI38(\Q
+M\DHG'S:VMQP%<Z;:_P`36#B/0TX44_2[,Z?.=)[!7*Y@:0KKF!M.&P%4J":D
+M`;>Z:@A#26`*C<TRRNUY&)6I3'&TJM?M.@G.%>CMJVH#K4$%5;IDF85SI0!L
+MT-XR,B)4O;416C<>P5RBR20H[2F="L._EL01UG-I#\RCMJ3Z[];YTC:4=&W?
+M</+W`Z6K0ITM+0&B`LUJ(A3:&`8^%4NVFC)!QVX6FZF0.RS.J.@:>Z:&:ZH7
+MOS./9*JS6R-_A2NHZQ,9Y1T&#06$`'L56637I.8Z1A7^D5#<,\MYDH.HTP6$
+M90]#FC7$P!*"Q=6]2W,\*"Y<W0&-(DK0\27-.C8$C>,+"\+"K?76MTZ0>5G3
+M5='T2S;3M-3ADH[VIY5N]^TA:=*C%O`.0,!9'7V'0VF.5*U%/PS9&ZO?.>WG
+M"[VWI>70T1$+&\.6C;>FUVDR5T$Q0D;PIIJ/)_\`Q`W?E=&J`.$EI'[+YPZ:
+M[7U02`2797N?_B7NG-LW-:?Z<_<+PCP\[7U8-``),R.5PO-KT83AZEX<<T6[
+M<1[%=#TTS7;_`'6!TBGY=LW$1M/*W^@4]=9L'E<=K([GHS!_#C&3PFZV0R@<
+M*?HC?Y#1F54\6U`RD1@#E8=(YSJ5P&LTF9*KVM=S*9(E5+^MKJ;DJ(UW0&,6
+MLOXQV\./M6_TX><\8.ZZ"TM"&3DK'\,4R6M>=B>5VMM1::(@-&-UY^Z^A;ZS
+M2I:4X;C4>,J[2IG$H&L+2,`_"L,!C,$+>G*U-0:T>DA3M#1@'=5Z32!NI6!S
+M7;Y6XYT;FP9,$(G`8(&$6B6Y.R"H9(`B$9TK5B0XZ1(0@@L@;GA2O;/Y1LH@
+M-+P)E&]"#2!WGE'0G,D_(34LMC*EIB0!QPC-@3JX$DIPP1$0C+=3_;A&&0-I
+M[^R)I$ZE@&=E6N7.#?=7GDAA`5"Y>.5;-$FU"YJ:R1"J5`T?"NUV:C,*M78`
+MTR1LN==\69U&'`MGCA8E>A#IW6S>&'23\++NWZG:0L5WQXB]X<LYJ:G9![+J
+M*<4&<".5B^'&Z*8<<0M*XJ.J8V^5O&:CR>;R;IGD5'X,\JW9,=J!/"KVE/@[
+MA:5JS2((DJO/M-3:`U1U0`XRBJU"`H'O<79A/\0-2-.#"JUZL$PY'>5`UORL
+MNYK@@@`>ZTSLKVN6YF2%G7%<:3!37=8Y=B>\K-N:I</3B-U8EIKFMJ?^;'.%
+M0O+H#`A#?W6@&0%B7U\03.5J326I;^[&3(6+>79#LE#>7;B<9G8=U3J@EI&O
+M'<IRR&I6+X("I7UPUE,D^G$RBNKC^'9ZX=I_5<IXFZN3,$@0<+>&-M+0^)^K
+MM;2=H=$#$%>>]5NG7%1SG$F3DA2]:OJM9Y`<8[`K+)).1^J]F&,QCC>:=P`X
+M$_=*<:9D'B=D!'J.)^J>9SO\+89S=($[;E)PDDZ<I.)D"`?C9"23/MOE$%IE
+MIS'L$(B<A*`&P/U*9TEW,!11/'JQ,)LANVW=,79A,?S$`G'?E$(N;IR"2=BF
+M;,X!]LI.P!$CY14ZCJ=5M0.<2PR#G=`)$',CME/`.VI7>J]6O^H4:-*[J@TZ
+M,^6QM-K`V=\-`[*@V0Z)(![JB5KCI@R/E34!/)4+7#<3CLI:3FSN?\E4;EJ/
+M_3MWV[*2/]PH[8?R&R\C&R.!_P#(FF]?X^Q_$MQ3/3G2HN@U*?D@2$7BVSI'
+MISGM,$]I5+H%G4\D'S2.<A3"SEX,ITW6@.$@X"3V@^K95F4[FF/\34?\20V*
+MC?JM=],GN0TM6+U4AK9!'NM6[N*>@N!$+F^MWU+\C#K)]U-#/N#JJ%Q=Z1[I
+M^A_S*YJ$'2W8JJYEQ6<*;6D:CL970](LQ3<VC'I&^%J<%6;*S;5)KN!#CM[*
+M:W;5H5`720KUK3U,T-`@*T^U8RD)`^JR(FW#31P<E"X-U9._NJ]>@X.EAV0T
+MZ[FXJCW!14M0MY.RSKVKYCO+;L-U)=73?RM,RH&-QI&7%`]I0=7J"DP>GDKI
+M+2W;;VX8`H>B6;:%#(EQY6@X2PJ-2*M>?RCE4Z@(82095YXSN20J5T2!IDF5
+M4I4&R-7U073&FD3P5-;M'EQ&WNBK,UL)[)2#Z'#J43E6*](YDJKT0AM9S8V.
+MRT;AL.]7=2K$=)K0R20$U*FZXJ<Z0A8QU:J&M.!N5K6ENVBV.%%D*G1:QI:,
+M#E$UA&2I`W&221NFJD-9`)^4:5[AX:R3@']%D5CY]<O$84_5:Y)+&DGA16M,
+M@#NFD/2:-(VD80WS&M];3A3R&"2Y9G6[VFR@X`B?9-)M#5J!]73/PHV`"Z9'
+M)6+;7U6I<G1)"U.EMK7%X-4Z6JT6O$6A])M,NEQ6EX-L&T:37$1*QFT7W?5M
+M,^AJ[?HMJ:=%K3@#99:BPZFW0%B]58']1IL70560#G]%S5]6/_%&9B"LZ;=3
+M96X%L-+MDGU"UCFN_53=-=JMAO$+*Z[<BC2>[4`(W1?CPO\`\2ET"7-Q!;S]
+M%XYX5<:O56D.'I(R!$;KO_\`Q#]0=7N@`8D',_"X?P#;/-1U:K^7G=>?\UZL
+M9_&/3[*NT6S079CE=7X/+7U&D@#A<1T>7,:!!^5WW@JU.L%IDKS^UJR.\Z/1
+M!I@Z<+G_`!^2!Z=EUO3:99;\97)>/P"3)V"-1P=S4_G<GV5WIU`U:D`@E8]R
+MYQN`ULR2NK\,VYTZM..ZQY;\>O\`2X\[=%T2T+*;=+,<0MZU<ZG`,_!4/1*0
+M;2:'85^LU@!=(GN"N>,^O3E=W14BW?96:5-I;@XW5!CS/$!7K=W8;KHYU/2P
+M(($*2!EW/""D,"<^RD<<;+;G]1ZCO&$+SF2/U1@-Q*BN,-#P-E&H=VV9RHW4
+MM<D1*!E36_1ME6'4W-9C9NY3M>D`8YN)Q[JRP>D2HHU1J"FI.]7J&.R%$/4R
+M9",-T-R@!C\F`FJO])@E(Q8CK/R2/LJ-:2(W5MQ#B9.ZJ.#G/ALD]E+6\8B<
+M#"S^HOAI,&0M&Y):V'`A9/4)<TQMW7.UVQC+O'G2=1Y5.V::UR/?=3=0+0T^
+MJ3PFZ+2.L%W&5,9MKRY>N+;M@&4H!5BW.NIL<*M1:Z!$96E84QB5UKYN]K=I
+M1!9)*M1I;*%C6L:-_IRFJF>R@"L\:H)^ZK5ZH:./E-7J`'*S+NX&HYF58E-?
+MW!+3GA9-:O+LE27-=VHQ^JSKBHW(QE73-I[ROS)$>ZRKVX#?ZL=D=]<-ILR=
+MN5B=1NY80TA;D8M0]2NI=^;`637K%YTS`15R:CC+C(Y[(6T);!&(A3:(:C`U
+MNJ<_*J7-0MIR,&)5RY#:5-V#LL3J-UIEI]..=U9RU&5UR](IG5L)S*\]\2]1
+M?5K.#2<]N5O>+NJ-\WRZ;I:9!65UWH%RSI+.JM:31(RX<9A>OQX^O;CEERYH
+MO<Z>?J@?^:<=MT>`=L]Y0`QOW[KLAC$>_P`I?E;`S\%(EG<?=(``R0JAA^8P
+M3('?(3.U'GY`2)S![1A(D!T#;E1=FDG8%)TET@QV3@#(!3-@NB2@88,B4B0T
+MXR"E`)(2<`<3@(!(+0'$'.<IHSZ=65;K6[J5A0KBM3?3KET-:X$M((F1N-PJ
+MP@0-D0),';]4IF.W9.UH!U3OA.UK1I=P>90)CHVWVA6*1(=C]5'+<F0`>RGH
+M#U#V*HUK:J]M!HEN.Z/SJG_3]D%`@T@8E'([+6XV^TO%;0>F.'^PL_P[_P`L
+M"9'RM/Q.#_PJH0=@L7PNZ*4#)4P^OG9]1T8P)$0HZK&/&6S*.EJ\J>.%#=7`
+MITMP(5L3;-ZI19&D$_`*P+FA3IOTM9)]UJ7UT^L_2P$D<J&G:O=#W-CM*2&U
+M3IE'37#RPN=L`5T5A9.\N7_F/94K2W)JA[!^5;-J7$!L`'=7OI!VU,4\%N%)
+M=/UB&YCL4]2?Z5`"6`EVZRU$=0P!C"SK]S9CM^JNUZH#<C99[YJ522W`053;
+MAK"XSJ5WH-K48_S:[3DX4G3K9UU<`D>@+H:5"F*`;IA/^+"H%I9C=$\>G8@'
+ME0U*;Z1EFW924ZP>!J])45&]AQ`^0LN^<-8CNM>K_P`LDDK'O/\`F'4>58E3
+MVAU`"9]U:`#V2852S,F&F%?8V&^RM2*MF!2O(F)*T;SUO;39,K)NWZ;IND[E
+M=!TBTEK:KSD]U*U!]/M6TJ7N5<#0/=$RF!Z3DI3&_P!U&P/.D&52O:H8UQ)B
+M%9KOWW"Q>HW&I^C[HBO/F/UN[\J5CS(:P%T]DK:B:D3^5:-M2938`&B?=$4*
+MEO4=3+G'3[+"Z_;,;2.9]I74=0(93);V7*]5_FG1).5-+M1Z;;AL0T">0MNU
+MJ,M*!U"'.V4-E0#&C4,!,VF;R_:&Y:-RKIEJ^&K5AJ^:3))77V09Y>.%D='L
+M138(PMRC1(9B%ETQB&](;;N)/RN=KVIJ%U4#(X6QUA[F4RW,E4@1Y!EN_*G%
+M6M3IU;18YSA<IXTO/-!IL.85]U_IHN8/A<_U:LUE&I5J?F(P25G*Z;QCPO\`
+M%*SK7G664=XSN)51EHVPLFTJ;@71F>2NLZY2:^^?7<`3Q(6#?AKZ[7'U0?C"
+M\-RWV]V/4C0\-TJH8'O81)WG=>K?A_1+@WT[KSCHM>E6+*;-Y7K?@&U#*32>
+M5F,]UV5*D!:ZH'RN%\?,+M1G`"]#T`6A/ML%PGCD-%&J=.S5:L>;63!5ZAQN
+MO0.@6[6T6X_1<+T"GYG5"\#E>D>';9U>JRBW!)A<,KNOH>''UQ=/TNQ8ZT#M
+M)(.9"H79T5BSCW76T>D5+?I[@VL-31D+D>I:?XHNR(V&RU9K289>UIJ>'#.Z
+MNVT-<`-E0H/$B2/;*M4G<R5N0K2I.D2`$!/K/J_51T7P(G/RAJ.`F)E6LQ+5
+M)$004#WX#3E/3@Q.W;NHKCTF1PC4#Y9;5D#E7&D/9I=B51:YSR!J4P=!&K,*
+M3A;-K)IC>(`3ADM47F%P``F$[7'(),)M-"B&2,PHS(:9*EIND$_NF>T8Y515
+M>9!`RBM7"F"YQ@]RGJ`#)/RH:QEQ`/PLUJ3:/K3FN#0QP)&Y"Q;L@$CLK]T0
+MT03!63U!T@Y"YY5WPFF3>>JY`X6KTNCZ!&25G6U#76!/!70V%(``Q$<PM^-Y
+M?U66[I+0H00T\K1MJ$$.^\(+6E)D@?96B"T95KS0-5VD#*JUJ@9(DJ6Y>!B1
+M]5EW]S#3G*NDM1WEP?\`%,+)OJA+B2BN[B!(.^ZSZU0N<07+4C--<5)#A@E4
+M;ZLV)$3RCNZP:T@"7'8K#ZE=#45J1BU#U.X.HRX9X6-6+S4@9A25JCZM6&B?
+MJK]I9`L#G?7*MUU$_P"L^VMI;J<,3N5*X"FWW&<K6\H4V0&[JC=4F@DF/HLR
+M*R;TC0=9@%<)XXZ@VG_*H@ESA"ZSQ%7+R*%$R]QB%9\*_AY5OO\`U=ZPF3,%
+M=/'J7;.5U-O(KSHM]7L#=&D3&1/9=YX*Z>[J7X77%G<4]3F@P7#?,KM>O=$M
+M[6Q=0\H"!M"'P-2HNZ54M:;(;D1]UZM7BUXO+G[33YDN:;J-5S"`(,*)V@.Y
+M!Y!V7:_B=X5NNG==N'T*+G43D$3C9<;4IO:8<WXE=7HQNYM'4:-P#CE#J/Y9
+MSV"<-.H"/NC#"''L.0II0`2V8^J9P&DNX]U(&^J=TY83Z=)D*B$`8TDD)0"I
+M',>UWY7`=BA<QTP6GZJ`&-U/@<IH!.)RCTD&-N<I-V$C=$`&@XDXSA)P](WQ
+M^B+)RV4@S68:/B%0#`??*>!(W&?=+DP9^J42WOW[H"C_`#^%/:'^9,[F5"(`
+M!$J:V.0=O=4C8M@/)'K(1X_^0_9-;TM5%IU0C\G_`*_U3AO3[7Z\&CIM68(A
+M<SX;K14=I&)73]6:76%03@A<;T9XI7;PX_U<*^/^UCYV7]74U*SA3C5E4KAK
+MZQU$P.5-1.MD[\JK?URZ:;#'NM,@-2E2>0P!Q4U%M2Z<&Z2!W&$NF6)?#GB!
+M[\K6IT]`A@&.%!#2HBF!3:/JK-.F*7J&8_1/38&C41G]DY(+3$**4SF/HJMS
+M5;.<$J6H^*>2J%1VIW*`*I+]R@ITW5J@IM^I4E41#0,G[K4Z/9AK-1W/=%6>
+MFVM.G2$1CW5MVT):0/3`3&<A12:!IRH:]`.RS!'NK5-LB=DY;`W164^JZF=-
+M14;\ZQJ:/HM>[HZVD`Q*Q.IM?1)$F%8E3]-/IW@JS7N#^5@U%9O3ZA<W0V2Y
+MW*V;.V#*8)R3OE6I&8^WK&NRJXXG9=3TNH/X5H)F%CW[99IB(5[H3@:0:3)4
+M:TU14$`[!"XR8!^Z1:(B("`@"0HJIU*IHHN,@%9%O3\VKJF0KG4B7U]`_52V
+MM)K&B-E`5NS```"M,8`PD;I[>F`)_1*N-+)!.>$Z5E=9J0V))_LL$TV/K!V5
+ML=6]1(G/L%G/I"F`9(`[HS0W]44Z&EL:CA:OA&RTTP]PDG*PZ+?XSJ+6@$M"
+M[GH=J&46B%2-/I]!H&0KS&CX06M.&A2O&)V*Q76,OJK`^N&G8*&[L@ZW.@YA
+M3U&.==XV5C20R#F%F1IQK[8T[QS7C=8/C)F/*;@0NP\0T]%3S&[KCO$+@0YS
+MG9A</+=1T\<Y>=^(_P"43F,96!6BJP%@D$?,K7\7GS*[H,">.5CVY<TY>(G=
+M>2UZI&WX-M3_`!3!$97N/@FWBBTD+R/P%0\V[86[2O;_``G0TVS!"2G^M.Z)
+M;;P5Y]^)#PSI]1Q/'"]!ZE#6%OLO+OQ?N!2L7:3GLF5:QFZYWP8QKW^:<">5
+MW?0JKZ->G4IG+3*X7P++[1KSSW7>=+#6,'JSW"XU].36.G=W'7VOL"`P,JN&
+M2=ER-U4UO<00?=*K</?2T`[?=5M7K)*W<O:\N6.$PG"S1V&J/NK=(G3_`&5"
+MF_\`57:#Q`<5N)8)U1S7<E$RL3APPA@.).84%5S61E0D7//(V"9[M;2!D\JJ
+M*V)G*-E?'I^J;:TG80P@$?93_G;JW]EG^=#P8RK5.I(DX/LA9I;HZ#(..RGI
+MTQ,QC]E6I2'`J:H[F8^%8Q1UH!WD'D*$N`&-@E4>(E15'2PD'=*2`JU),`J$
+M@G,IC(='[HZ/JQ$+%NW233,Z@3J*R;U[B[3_`(EN=1$'+868]@=<#G*PZ[U#
+M]*MOZHF%LVM+`&TJ.RI`4Q$+1MZ0%.0NLFGSL[NBI-T#'^B&K4&DGMV*)[]+
+M2.ZS+^XT-,.581]1K[P0L>\KZOZI*>^N'/<<_JJGYR25IBJUP79$JO5>13)F
+M25;KPUI61U&L&M):8A61+57J=Q!.5AW0=7?#3NI[ISJCHSGE6.GVLN!(@\%:
+MZ9[5^GV#F&3![K4HT3I@&(4KJ6P@8]E(P!@+B`?=9:TK730UF7K!ZY<BG2=&
+M8V6MU2XW.RJ]#Z2_J]WZAZ&E;D8RJO\`ASX9J=4ZBV]N&R`=BO7J-C2MK44V
+ML'I'94/"EC3LJ8I,:!&\!=,ZFU]&7+T>/#5W7G\F7L\L\>6_\U\",+E/!\V_
+M4JE'(ER[_P`?4--8D`0N&L6AG7`T")*]&4X>?MI=1Z+9W=9[;FG3>U^\B5S7
+M7?PNZ#U"7TJ(I./_`,<#^R[2\?Y-9A?B0I[2K2B<'NL^NXN.5G3R"]_!>W-3
+M^7>5&CW`/]E4J?@T&"6WI/R/]%[57N*+CAID=E`74W3_`$G=3T_UT_=R>.4/
+MPFHTGDU*I<!MC_16[3P!TNA5`=3U'W`_R7J51@-,N!:%G.MP^J7D3'*>C/O:
+MXRKX"Z-5;B@-7PLGK'X;6A8]U`"8P"O2J5,3D94SJ;'B"$F$/>O!^I>!ZUO.
+MJ@<C<+(/AMNK14+FQW7T-=VE%TRT97/]9Z!:W#B13$QN,+4A^[8\<=X9H/;Z
+M*Y!(C(5>Y\(W6DFE4#CM$_ZKONJ=/K650TZE,5*8YY"ITJ+`X.HO)_Z7*S%?
+MW*\^NO#G4Z+=3[<Z8P05GU+>O2=%2FX+UP.8ZF&5&9_Q`S"AN.G=,KRVX:\]
+ML$)JK^]^7DNSMM]@3LIZ.H.DC<Y(7?WOAGI+WS3>1[95&IX9I-.IKYC`3IJ>
+M25D4`31:>_\`U(])[?\`\RZ*U\.,_AVS,J3_`,N4^[D=/:/K6^:W^&>R=PO/
+MVN\KK3Z9.-2]"K@&F1R5YWXCFCXA='*N/&;Q=XUT/GGRPUF\836%)KKW^8=O
+MW5?I`<:#71+W;+6%J*3!4_J.ZW>W.=-"FP.,```*5S6LDG)4=D[^3)^Z:H35
+MJXV"BFJ/)_+LA,P,Q\*8LXC`[*"X=VQA18K7;H!]4A5V_EU&9.%([UO/8<RI
+M+*@:U4..P,!!)TRT<YPJO:MVC3#&2`HK.D&,4SW9QV4:#!U8A.T$GX29ZMU-
+M3I@-D@HIVCTB,%,=6RE:T`2>$M(,]D%2J`<1*R^MTP:;OW6R]H</A9O6:;?*
+M,E!B="TMN2'#(VE=-0:2`5S5I2TUBZ>5O=-K.<P!VVRTS$E]3/E8^471Z;O*
+MEIAREN&ZJ9(E+HIBH6QSLLM+]&I,,<(3W6EE,N'".K;@C6TY6?U2J:=$L,@E
+M3;2G2'FW!<KU*EZA"AZ=3]&J1E7J(&%4'39I:)PHKH0P[_56(],C"KWDZ8D>
+MZBL*^AMQDA4>K/#:&D8QPM#JE,MEVK*QJSG7-<,,C,>R1FM+P99E[Q4(.3RN
+M[L*(`!"Q/"]IY=HR`%T=K3TP05:UBM4F@#9#6`\MW"E8"1)05VQ3<3V6'1ET
+M\W!/NIW/8X`(K!@=4),;H[JUQJ'V4I&-U^D/X9SNP7FOB*N6U*K2=EZ=U8'R
+M'@Q,=UY'^(#C3O*@:=UP\_3KXN].`\1UVU.H&F9WX4E&G0ITVET"?U4%_3F^
+MU5G`1QW5RQMO,+.>9]E\[*[>WIW'X=T:3M#M`B5Z[T$-;;M;,%>8?A];-8YA
+MU8X"]3Z7`MQC/==<.&:;J[P6.',+Q+\=[TT:08#NX8GW7L_6'^EQ'9?.O_B+
+MOGLZA3IXC4#/U6<N>';P3>4=#X`J"ITNFX?HNYZ;4EK9`"\W_"R\+^E4A@X7
+MJ'A:C3N'?S-@)*YV;NH^A>)RF?&F<`JM4_Y@.!*T^M6].DT/ID@.X*QZKX=$
+MS/NK.*QW-Q:HN&_"M:Q&%1MX(F2IVB#Q"Z,)A5(."(]T-=X<))^@43@1L9"C
+MU.U;*+(.K^3='T]PDSO[JO6<"S!0VCB'<J-?&A4IESM6!"LT9#=]\JDRN2\>
+MD1W5NDX1M/96,W:Y;D@"5+4>-$!5Z3O2/V3U'?TCE;VP(.D=Y451X:=X33#9
+MSV4-2"9!6;6I#O?DG<*[TNDPTY<[?.5#T:R=>W0IC\NY*UNM6++"T#V&8QNL
+MR6\KE9.'.^)G-94U,)(B%F68-2IJE%UBX=6J%L[&,*ST6V)`EIRLSFL^7+6.
+MFA8LP,X^5=:0`FITFLI@`9]U%<U6M8<Y'NNCQH;ZJ&ATN'W7/]3N"3&J?JK'
+M6+S=K2L@/=4?F8/NM=,6D22.$+R&MU$P0IQI:S.ZI7CX:8._=6,U5O*YSG"Q
+M+]Y>XMU%7+VKDC.ZJ"GYE9;ZC-Y1V%MK()$_*V*%NUC,#Z)K*B`P#<A6Z3`#
+M*Q:U(B=1&@.(D=E1OB`TAIA:-V\,81C98]^_5R""M8I:S;\O?5T-`+G;0N__
+M``VZ.ZATWS'M]3LY"YKPG84Z_5&OJD`#(!"]8Z;191Z>WRF8C?9=L)NN.=X4
+MNG6>FIJ(6@]C0PCE*SI/J58+H"O/MVLI0!PO1BXUYSX_I>H_"\_<P4^N4W'&
+M5Z?^(-$&F2%YIUX>1<-K[@$+K>8X_6CXBIN?4I%DY@2M+I/36?PP<Z7%16KJ
+M=YTVC6'8&5LV,"FUHCX6,>4G6E<6-$"?+'U5>[L*4$%FGW"U_*+Z\`>GVRI;
+MJU_D$QF%K45PU\U]"OY>[95JVHM?0@@94G6+=SKO26R)S*&F*M!H:YDM[A7I
+M$-6S-,ZL?11MI@&(RM*F!5IX<,]U6O;&HT%[:GZJ+M2KL83#CMPHGT*;S@82
+MJ6M<F=4J2A;5"(.Z49O5.D4KFBYKMB(B5R'6/#->A5UT&E_QB%Z!4IO9)()A
+M05-3@<%!YY0Z=7HO]=J]I';*GI=.96U$5'!Q]LKMWTVU6!I;OP4%3I;20ZF"
+M"-T''4>@-!Q&_(W4KNENI^GRI`Y74_P=1M3(EH.R3J0#3CZ(,&VLZ`H-&DCV
+ME2?PE#L?NMIMO3+9+,^Q3_PU+_`?NIMMZY4,@8_5<%XWIZ.N-<=L2%UM1]RS
+MTN$B,$+B_'-U5;U2DXC3ZAGMNKQ[1RF]5=Z#6<VOK`):/==+:UVW%(D[A9'A
+MVG2?:MJ-.HD29Y*5:J^SK'3^4E=;^'*?XUV5''T9A6K6-/,CE9?3K@5,\E:#
+MG!E.0<D*58*XJ:9R1]54>2[`F/=)S_,?OONE6BF(F2[A1H+6^8\4:>>\+6Z?
+M1;28`&Y'95>EVX#0?ZB=UI-`:R.0%")`X-&Z0.=]T#3("-F7`1]4:'3W5EC2
+M6B3A!3IC$J<M``@;!"&.&X1#(_5,``?E)K0TG/T45'5',8^5G]5:?X=QV@+3
+M+#DJCUK_`-J8`GG*NASMF8N-,;E:[:?H#FXA9);HN`<"2M[ISM5$-$;*]QCZ
+M.A4!I:7;@)^E@"]([IZE+3+H06A'\:W.5(TWAAO$+)ZZ6N(&)Y6M2&I@B<X"
+MRNML/\8&$1\J6-%9,;H:1F%<I,&F8P2J%'S*,?X96I;%KJ0)*J`=(;$!5;LD
+M[#;]%?J@!N(GY6;<NA[M]LJ*Q^MU=-,MC)PJ72:#JMZW`W_NCZF_S;O<D-.0
+MM3PK::Z^N-MH2,NGZ/;AENW2!C"U*+3&`%7L6AM.#@J[1TG!QA+'2)J5,%LB
+M3"BO0/(*L4YV#OJJ]^#_``Y,94L:4>GP">,K18&N9&9*HVK"6<25(*CJ9VE9
+MI%3KMN!2<X@G$2O&_P`2@*5R\D1[KU[Q%=`6KAC(Y7C/XJ/;HJ.<[<1/W7#S
+M3^+KX[_)PURQS[L:1K;/YE?LXIC><QA9%A=4F/\`+<[.\A:=*H'Z"W;W7RKQ
+MR]_^/1?P\'F:)GY/T7I_3PUEO]%Y;^'E;2QDD!>@65Q-,2Z2[W7?&QBINID.
+MIOD<+YN_\2E$"_%3,2/[KZ1N`'4C.<+P#_Q,VSO(+QGU#'W4R[CO^GO\F?\`
+M@RZ;)D/QVE>O=#N3;N#VNC$+P[\&[HL:RGL/^R];Z;<>D!T_=<[Q:^E9MTEY
+M>/N1J<Z8&W"S*KOYI,_=2L<30U`@M_55+AP\S;Z>Z3EC6EVA6!<!LKDMT>DK
+M.L=P>/U5LDP%T8L2!PF2>-E'6>-/LH7EQ,;?"=C9(U<HNATX>Z,Y4[;<F%%3
+M(9L=E9HW.(.<Y"FB_P"!8QU,R1@*Q3J`C<YX3-(J20-Q$)FT7,=JX*K-6!5=
+MIR83BKB3N%6#CJ+<A.YT!HV5J:35:TMRX?9`'ET"8E0U7[@_=14GN+Q'=9M:
+MQCJ?#]-ULWSR#)X]E4\8=6?4I_P[1I`R24-'J1I6H!,:6[%8%W6=>7I$[NRE
+MRUCJ,^O.Z:QH/N*\D3)W72=/H-I40,*/HMDVE1#W#*LUR=6(`3"</)Y<_:AK
+MU&@D8D=EB=8N=#3Z@K?5KEM*F270?E<MU"Y=<5]+7$CLNCC;L%>L^I5,J2DR
+M,F?D)4:$L!/Z(JKQ386SLG:!NG13X"R+UY<2-6.,JQ?7!((D;+,N:DB)W]UN
+M33*O5ESR.ZM]/M3Z<2HJ#/4#"VNGL'DCNIE5D!3MRT`#,!,^6$P,JU5<`P25
+M1N3(,&.RS%JCU)Q+MU5I4#4N&R<?NKCJ+GD^G;:2M?POTCSZH?4'I77%C+B+
+M'A>TFYIM:S;V7H-"A%D,0LCI%E1H51`C*WZSPRV$8PO3C.7FO2.WI:&^D94D
+M.+<]E7HUB3Z1(1U+@Z8(A=)IFN9\=40^@[&87F7B6V!H.&C9>J>*:M-UL^1Z
+MEP'7J0?:O<NT<*@\)MU]&:R/RF%N6],,<'.$-`V)65X-`_AWL/!6EUJH*-H"
+M)&,KEA^!._J="@XZ0TGNHG]3>^G+0(Y"P>FU?/O-&DD2MQENP4M.B0=UOCX*
+M!J4KB[FJ`"=E:JVI%/+99"S[^DZC6EHD+0Z3U!C6-HUY@XRE@HU;-NN6%U,E
+M0W5K=END5);\KJOX>TKT=3"V2JQL:8.G4/B5!R[NG78;K:9CA!0JOI5--PR/
+M==DZSBG`V5&\Z8'`@,!:>%(U6,/(J-P0<*&I1I%D`R#PKM?H;2=36EHY$H']
+M%!8-+W-([*HQ;FTAY+3[A%:$D:2#(4W4K*[M07AY>WW""Q>RHV8AP&0B'>!/
+MJ9'NJMU39O/Z+5#6N"SNH,TUH:@&C;!U(.C='_"#L%=M:8-NP[84GE!1T=G5
+M,%<1^)=,Z6U`V8(S]UVCW>@YA<OX_+7=/(WS_FKE/KCC>6-X.ZDZF^G3<[#@
+MMN]J-KO)X&RXOHD^:]S2!IF(^BZ"TKO-#)RN^7+G)ILV#=#-3#*M-N'.(8_'
+MRJ?2ZAT@`_=7Z=%KQ),%9O"K#0P-U2-DUNPU;CS#EC3"H7KZC*C:+#(G.5I]
+M&<TM#70(WRLJT;=C6M4PR)R@:6P(B%(2!@#=1HFCCA6*#6G`G"A9)<,;<*Y1
+M#?+!P$!`0V91S+?E1ZO7@!2@<@3E%.QN<F91$1S)2:2!M/LGW=//9%"]L4R2
+M8`69=-%:K$>EONK5]4'_`"FS+C&$5&AY=#(]2@YSJE,-NM0&!LM'H9#@#V5?
+MKU,Z]1[[H_#]08`SE;G3-[;>F:4.,R(6=48:5Z"#`GE:E/+0X[1W5+JC)<'1
+MGNLUIKV;IHM(.1&0J/4ZI=?-;4;J]^58Z6X&V$G80LV_JM?UIK9D#=-<FVB:
+M;:E(`-CY0T`ZD_3/I4U/_E@!&^F'4I&Z6$!6RS$K'ZS5\NFX`Y(CX6P7#06N
+MQI7-=:>:MYY3<@&2IV54MZ1<=9,EQB5U7AV@&4FD\CA<]1+16:P8B-ETO2:G
+MH:`-A"TD:](P(E6[8:HD*A2U/<,;*W2K-IMAQ`^JRZ1>D!H"K7IFB04S;NB3
+MFHU*L]KF2""#[I8NXBLY#<I[HCRSC,*2BWT*M?/+:9:3A<ZTY7Q=U`6]-Y=L
+MW)7BWXA=9%U5?3#9:XXS\^R]-_$5TV]0AQ!C*\CZGT^G4OQ6>\D3@2O+^HRN
+MM._@QF]U@6M"I4NVN8--/N976].M&^2(&&@'4J[:5(D#$MV"U>D.;YK&U7^D
+M<!?/RG+V>VVWX<>ZCH.D@=UVW1;HN:TQD^ZY6VITQ3:X'!V70]&T!K8,X6I:
+M.B8XN89[+R'_`,2EH7]"K/#3B#^Z];MJA\L'8+S[\=[7^)\-W0&?23/T*WE>
+M-M^+C./#?PHN-/4&4W.V);O\+V?ICR:3#@[8"\%\"5C:^(RTNP'G'U7M_0[A
+MKK>F=0R%C.:R?5EX=+;52*<']%#6J%U6&[3E5_XE@I_F`.ZCH5BZH07;]BI.
+M$L;%K(:"T$_*NMAP'ZJA9$E@!*U^EVS'M+GDXX[KHY7A!;T"^I,'*.[I.8!(
+MCLMRUL0(((@Y^$U_T[6PD)IGWFW-D$OP=U(Q@!X^%+>T#2K:3N$`@OSN%&JM
+M6YTN!CV5BJ[T@1&%2`TM!:<'=3:G&C$B2M2L6!?I$GG<J%U<9'8(JKQY1&Q5
+M4M=J(;$0LVZ638JE4ET"2/W5GIU/S'YP/=5F4'/$Q(Y4[*O\,PR,0N>]UTLD
+MG`NM5!2HEHW*;PQ;^;4;5<#,[K,O+G^.O6TF8$]UU70J'D6K3$8W6I-W3S^;
+M/UQTTWD,MPP=EE]0O&4:3BXY'<JU>5M-)QD!<9XLZ@?4&N_WE=G@1=9ZJ:]<
+MTVG?;*&PH_U/YRLCHS:E>^#GB1,KI&4PRD!$'NL[VMD@:C@QF/NLV\JAP(GE
+M6>H513ENQ]BL>]K@N.DQ*Z2.=J"XK9/)F,%1T6>94!,Y.$J=)U1\1N5J].LF
+MAHG$%+=$@;:T`#7#*T&4PVF2!PC93]$!"_5&D+';>E.]?K):!!04+<N&?OW5
+MZC:ZJDD21V6AT_IY>1`5VK-MK`U*C6-;DE=9T;IE2E:^EF8W6SX4\.M?IK5&
+M9&<KIKBVH6UL1I`@+T>'"V[</)8XRRLZ_P#$@/!PM8VKS&MTA';Q4ZB8',*]
+M<4F@PT\+O)S7&J]M;TVL@#=-7MV%A@*S3IP/=-7;+8(72,5Q_BZRFW>6[[0O
+M/NJL>R@]CL`=UZKXAI3:N)W`.%Y_UNU\RC5])F"NL<,F;X*I,9YCBX%HG^R?
+MQ=<MJM%*CM[(O#E,4K>JUW_4J%"D:_5'-W:#,;KECQ;3ZO>%K$-8'O'J//*U
+MKNEI8=,XX4_3K<,H-],8E27,-89:/9:QBUS_`%!@<"1(C=/2M65[4.:WU`J>
+MJR7N!R"I+-GE&!!!6F8KT65K?U-V&X**\K^8T5&.+7#A:/EM<9PJG4+1NG6W
+M#AE(JWT^J]]JQQ,PBKO<<M=![*CT>L0UU([[JS6)TR#$%9BU%<W%=H@`%0'J
+M88"*M+`W,I7M3\Q)W&RSKC4UA+<RJRLUNH6=:)=A5+CIMK=M\VVJ`/.T'E4B
+M*51Q%003A0UZ52A%2A4<V#P5=;76DM6A>6N:E/S&C^I475!5O`'2)X6K:WUS
+M4I-:7!W!;"L46VCW@7%#23RLW_"?Z&A3:*31JX1Z&_XBM"CTN@:32RLX-(QE
+M%_PJG_\`,Y9Y::A_(3SRN0_$"H?*+)R=PNPJO#:)<<>RX#QK<"I7<W4-UTLV
+MY1SG2G>34>3C4Y;G37%^ESI(VE<S<5=->0W'<+8Z-<#0)=[+K.6'5=-)V[;+
+M4IU&M:23@#*Q^FU6BFT@F85NM4):Q@_J.5*JU:-;5KNJ1O@`K2IVNBF'L.2%
+M6L&C4UK0(`X6D'`0LJ@I5*K'^MN.ZO4:@J1ZOI*#0'M@Q]4!MW,'H)G=1II4
+M```=_E2^PPL^VN-(#*@,\K0H.8YHC,H"8R#G/U4]/#8RHFCU[X*E9@RBG,"%
+M%=5@QIB22E=56L$\H+&@:K_.J_0=E*"Z=;D@UJDEQ.)5IS<;*1H`;M[).8"F
+MFF%UVEZ"9Q\*ET,AMQ$E;'6J0=0=!("P^G'1=:"0KBSDZ>AMLHNH@&B9.45!
+M[12;D!4^IUS4864Q)VE+`NGW#W$4VF<IKZAY5XRJ<DPI/#E$M$OB?=6/$;"&
+M,J3@%9O<K4BU;N_DM/LI35TC,`%5+6LT6@?[<J"XK5:A)9L%JZC,!UZ[;0HN
+M>TY/=<Z;G42\G+E)UZX?4KB@TZC.T*3I_3*KM#WR&[G"FTLVO^&>GNN'-JU&
+MG/\`B73M;9V%`.J/:(WDPL2XZQ;=+Z<8(U,$+S#Q_P",>H7E1]"VJN8V<1*S
+ME9.VL>>(]0ZYXWZ1TYKP:["1PTA>?^(_Q5#ZSFV@)'!7G)H]0O:DU/,>3DDR
+M59L^@7-3/E.'T7.^61TF'Y=/2_$+JKJX=.Y[KUG\/NI5^J6%*K6.7"87@W_#
+M:MM5;YK2!*]I_"FH&](I0=FQ^RZX9^T8RFK'H#&M;2WX6;UHL;;N).P4C[T,
+M9I<%E=9KMKV[@'QC98M='EWXD=4)NGT6NVW*\PZ[U5[:[J=&-1W<<PNI_$^N
+M6=6JLD[[C*\\ZG5=3K/>XX(P%X/-=W3V_I\&GTF\>)>YQ+B<GV6YT>Y>ZY:X
+M;-./=<9TBX#JHU8@X`Q'Z+I["\8`-1DCL%X\YIZ;B[SI]R'4VDNR8$+KO#Y9
+MY()[+S7I-RX!CXQ[E=MX>NBZDT%PRDNT^.OI/:*<R8'NN3_%,BIT&NW?T\?!
+M6LZ[T-TD[\2L?Q1_ZGI]1LXA==[B3M\R4G&T\759)`\R9^J]>\+70?94R"XD
+M`+RKQ_1-CXKJ%HTC5(79_A[?-N+.F)F-\K.<W)7U,,IIZ`*NMH/ZJWTZ'502
+MTK';5TAH$GZK6Z&6EP)D9^5B=NG<=!8C8!I]EN=,MZ@RTYWT^RR[*`T;`KI.
+MF-IFV!`AY'YETG;CG=1+3<:='\Q#@%-3NF&A+CQ$*A4=5=4T-!(G\P17-`,M
+MYF5TE_#E<65U9X-T2,@E5<:HC*DN3+B>Z@=4@97.NLG"1E6&$DR=D+ZY#8#B
+M/E0"K+S$QM*DHTC4=),#NEIZ_DF/<YQS[RIJ$:NQ4]C;AE4ZA)C"BZO5ITVA
+MVSMOHL7=:FMZ7*-:FREZB``,E<[XAZA2UN93=SQRJW5>J"G3=+_JL7I51W4^
+MK-`)T3_=+>$O\>:ZCPA;&I7\Y^Q/^2[#S0R@&@[+-Z70IVMH-,`ANZK=0OO+
+MGU+IC-3E\WR9^]V;Q%U5M*FYH<N1NWONJ^K<'A-XHOO-N"UCIDI^BO$AKAQ,
+MJ^WQCU^M/I5NVFP.(C"L7ER&M(!V"`U6,I`A974;L:B&D%;QC&5V:_NYD$C]
+MUGL:^M5`&)/='2I/N*L@8[+5Z;9.:YIE,LM+,!=/L88#RM*E;Z&Z8YF5/;T(
+M:V.%8<R&9"Y[VUI3\LZ829;N>\`*ZRE+HCZJW;6_J`T_*"O:V<1V72^&>F>9
+M5#W``*#IMGYE1K0,=EV71K-M"DT@?HNOCP]JSGEJ+5NQMM:@8!`63UBO4J4W
+M!H^5LUJ>OTX`*I=1IT:5N[5!@+WS\1Y<O]871VN_B"\X([K0KO/F=_=1=+`-
+M1Y`D&5.`'5H4Q^I4E`%S=TJ]/TE6*;(`$H*NQ$+I&*P.O,U6[P-X7#7].75F
+MQ.Z]!ZW3BV<>X7$W0:+FJUP,96_CCDXJ_N'6=2HUN)G`*M>&:?F-\\@R3.57
+MZ_08Z_,@Y*Z+P?:4VVX:1*F?%3&?5AE9K:<.!P%!7J-,\8D3B5JW-*E!!8/A
+M9UU;4RTC;L0K"L]P!=B-U/1I8"@J4*U)X+3J:%8M:S7$-?A#0].E\#`'9'5`
+M=0@C=&\LQD05%</:QDS]$&'6?_"7VHGTDPK=6NVI1U-S.5G>)'!S-;-Y5#IU
+M>Y?3+6C9+J4DW&N\-R7X:JM:M2:XR=DS:5>H`'NW3U+*B!ZQJGA38I7-:B>!
+M"EL_+J"!D)7EC0\LPU4J5LZF^:57(&`@NNM?(JBHT0KU+16H9RY4@;A]`-D$
+MC*&UN*E"IZV$`*&FM1;IIALG"*/^IR"E>TC3!T%%_&4O\!472[U2MIH$`K@^
+MN4Q6O7@977]5JM;0).ZY&Y9I>^N2<[+KIRC&N+:F:+B2"6\!0=*<&W$.P.$]
+MPZN*C]/Y">55:]S;C!@<*XI796-;32&95_IKWU;@N)PU<[T^M-O'*VNAU)8&
+M\G<K5'3V0T4M<Y5RF[DR3[*A;N`IM;(GLK=`^H&,+-(O6YD`@25:9I)@JK:`
+M:I"L`D/S!E9:AZE"F\2!!4;15H.D$Z58IR?=3:06YB44UE7:YL..5)=W#:;/
+M2<E4;MC:3"YF'*'I_F/J!]PTZ9VE/^B_9T'UJ@JU!(!Q*U&M],`0$%L6%@TG
+M"E$`*::$T`X)PG<`&Y*>DV3V")PD^R"C?-+Z3MX7,OTT^H$KK+K\D@;^ZY7K
+M32V]R2)/9)Q4O32M"^Y(IM..5IMM:;*>1J57H3`R@T@[K5()8>ZMF^R*%DT,
+MN=(Q[*WUNCYG321N%6J?RKH%:<"O9EL[A9O,:G;`Z.36_EDX:CZ[<LM+<AN^
+MP4+'&RO*C!R56\NIU&_TD$M!W5[FV;WI'T#IK[NY_B:HD$SE:O6:].UMO+80
+M"!$A:#13M+04J;1('"S+BQ==-<YP.5G*Z61QW417OJSJ+0XR?NAZ?X(;6KBK
+M7:3.5U%'I[+>K(`!F,K4L<;F5R_;WVU/XL3IWA"RI``4FS\+5H>'K9@@4FX]
+MEL6WE@"",JVQK2TA:](UNO)_Q(L*5N_T-`@RM7\*+F:;:,F1PG_%>AZ-0V!6
+M3^&-P*75=&I3QW5,IN/4KJV+Z7R%R_B?S[2@_1+AMNNXM&"K;-/$;K'\16#:
+MM-P(A7+A9'SKX\>^O?U'N$9WW7#=;#R2X&1PO3_Q=L76E>H^F/LO+ZC_`#[@
+MAP).T+Y^<YV]_@Z#T(-%4FH1)VSLMVP<VG<M]8+1F8C*S+"S+JL.,=L;(NHV
+MM:B\/F9P#/\`9<,N:]'%^NKI]19J:UF3MA='T+J9IM`U9[+A^A4`RD'.<-16
+MYTFK-8,&0#NN?MJL:=Q2NGW#06DRK%7U6A8YVX63TZLT-`D"%:JUGD0W]%TE
+M'CGXW='>ZN^YI-<2#Q]5RG@WKU?IMTUCCI#=]2]\ZWT:AU2B6U`TDC*\T\8_
+MAM7:Y]>T'N!G_):QS]?XY=/5AE,HZ'P_U^VZA38#5:'GW78]&K-#&D.'W7S[
+M1I]6Z-7TO%0:>#*ZKPQXVK47,96+H&#J)3+#[B]>%^/?.C515J-#CCY704JX
+MIT8IN.<$+RGPSXE9<L:^E6S\KLNC]3J5ZC==01[%8EC6>-[=[TS3_#AY:-,2
+M5F=>O&"F]K#`*O65]19TUI;49$>J2N-Z_?\`G7E3089.%TO$>;#'VR-6NQD<
+MJM5N!)@JK4J9&=U&YY7.UZIC%VE5.J,Y5NC<BDW.5DBX:R"@NNHT:5$N>0/A
+M,=UG*1KWG5&`_F((&`L#K76&,8Y]2J,=RN7\5^*[>W+A1,N7+VESU'Q!?AFA
+MY8XYB``EXYJ=3<=)4ZG6ZI?BA2RR8*[WP;T@6U-M5P`<<B53\%>%Z5G;TZM1
+MGJC)*W[RY%NT-;B"LXS?->'S>7VXC4JUPRE!VC9<AXCZE%TYC3[0KG5^J:+,
+MP<A<7U&Y?7K/<Z<G"ZY7AY\9ROEOGUFOU3E:UFQE*FTD[+%Z54&C)DA7A7JU
+M7>2P$SRIA/M,N>(LW=X#+&9/R@LNG5[BL'%KH/RM+H?1'O<*E5H]Y746=G1I
+M,#6M'9:N=O$)AID=.Z2&4Q(`*T*%DUIVA7]#)`#83N89VA8_ZJLVFUH['LB%
+M(N$$85AE(`3^Z.DT:M/Z*[-(K>C#@%=H4X(`&2DUK6>H[K4\.VKKBY#W#!6L
+M>6<N&IX:L"&M>YJZ*BSTP1@)NG6X92#1B%.88W,+W^/'4>;*[0W!#:1("YKK
+M]9SY:"M?JURUK"-6.RYVX<:UQ`[KK>(Y]KG2:1;;22K-%DGOE*VIAEL`,'E6
+M;>F/S'/LIC.$O9]):T#90U`(.5<+?;"@N&@C"Z1BLKJ4&@X?NN*ZG2!OGEHE
+M=O?L_E.7(WC)OG'&ZU\<[VXCQ/1(N-78R5I^%:X#6Y/O*C\749:\Q$<JCX3K
+M?S=#G;8^5<^F,>W77;0YD@JE4;(B<JZ1KI"56<R'_E)455#3EA'NA_A*;S,`
+M?"LN8-4@*5C`!@A15!]CZ?SG[J"O9G,N)`[K5J>QW5.],TRFD8M]0I-IN@3A
+M<_3J5*%_#?R$PNDK,+CN85'J-F`TO&%=<:B3B\I:(:]D@&3RIB*9;#L$")4?
+M2ZK74])&6A2UF@L@%2<K>%*M3T&?U5.YH^OS&.WX`5[4`\M)P5!4!:#!,3A5
+M%>@\TZH!_+SG9:;6-JT0[3)'*H56,+)#H/*N=+J!A%-QP5FK%BE2:*8V1^4W
+MV5RG2E@(B"B\D^RFFW/WU4O86Q)=C=<]U5YIWE.@X8G(6[6:ZG2-3<KFNL^9
+M<WFMN"W==OKA.BZNUH9Z&8A<[=.;JB(SRN@OZI=3#"[(P5SW4SHJ$F(2<5.X
+MU.G5`VVWVPNB\+EKJ@!G&5Q?3JY#0"0/A=7X;J"=7<8RM_4EW'5:OYD`K0LW
+MS`[+$H5"XR"M6R<0T+"M:U,;#`5EIU&3F%2MWD`<CM*N4':A&T<RHU%FG^5*
+MK7%*GJ=":=+"=AW5-I==W.G.AOZJ">VINNJ_F/)T\`J^ZV8]H;`$;)6S0P`-
+M$0IVD3)32J['.MSI=,*];N#X(*A<T5-QGNA#7T7ZOZ?9/^JTF"?;W3._+NH[
+M:J'"`I*A&(&ZBHGM$9(7-^**0;7#\Y/*Z9QEAU;;+#\5TOY0.P[J43=!=JH-
+M![8RMJBWTSLL#PW4!I@?1=#2/IV6ZS&?U)I:9A6K*N&VH+B$_4:;749B%BU[
+MAS`:<X"D_#5_(>J5&UKP^6,DK4Z9:MM;85'1J=[*MT.V:]_G5`M-C?XBN*;/
+MRA9O";#;6[[FM);CX6@;1C*4"![*]96S*3!C,)ZS6DSCZ+$YY:CF^I6T5):W
+MY52E+70X8707ENTM)61=T@QVQE6+I);@1A6QKTX*HV[A$?NK5!QVU*K'*?B9
+M:5JG3G."\X\,=0?8^(*;7XET$KV3Q6UE3IKVG.%XCU.@]G67O$@,=(*Y?UR;
+M[CZ-\*UVW/3J9!F0I^I6VIAYE>7_`(=^,6V[&V]P^"T1)PO3;#J=M>V^L/!)
+M"ZW'?,9QRUQ7D?XRV+74G0T3*\9O^EOI52ZFV9.Q7TCX_P"DF]+BT%P.87GS
+MO";J]8@LQV7B\F.WHPR]7EUC8UZ3R\M!+OT1=0#C#=,M9[KNNL=!%E4TO!PN
+M>ZS;LI"8E>;R8<;=\<[:PQ7K4Z;:5$?)[+5Z+6=3:"1]5F&[IMJEI_-V0UKT
+M0-#H:,0O/=[>C3LNGWTU`7GT^Q706U1E:@W3$_JO/>FW!(:[4872=-OFTPV7
+M3([[*XUF\.@MB16R<!:-1M"M1T.:#/*Q:%85:6K5$[%.RZJ,<3J=`]]UTQ%?
+MK?@^QOR2:329DX'^2XCQE^';J-N^M9L+"-H_[+U#IM\:K@TO`'*OW;+>YMM#
+M@""IZ\[CKCY+B^;>D=4ONB]1\BJ'-T&#)*].\'^+;>X:T.<&NCD[JKXY\(4;
+MNXJ.8S29D0%Y]U/I?5.C5O,87.8W&"KN9?Y7MP\TO;W@=8\VCZ:L@C@J(W8>
+M8)V]UXOTCQG=6Q%.J\^G'JV"V:?CINQ(^04N-=98])JWD'!D;)S>4VLU%T%>
+M5U_'#G$N8=("H5O%5_>$TJ3WDGM_V4F%[I<OP]$\1>);2V#O5+EPW6?$M[U%
+MQHVVHR<`(^C>&.K]6JZZ[BUIS!7HO@OP1:V9:^I3U.[DG_-+G)QBXY>3&=N*
+M\%^#.H=6KMK7FKR]].R]:\+^&+/IM$!M,!S1(6Q:6]I9T6A@#<+.ZCU`TZA#
+M7XE9U]R>7/RW/B-2YN:5&EI]H@+G^IW,DOF8V6?U7J3R]I!CZJM<W9?:@B)*
+M991SF.@7M9U1VAVTRJ/4&-8V0<E,^Z!P5<Z3T^KU&H`1(7/W_+7JAZ%9W-P^
+M*;2X'LNY\.>'W@!]2G!]UL^#N@T+>@V6#;>%U=&UIM:`UHA;QW8S;)TPK6P>
+MUH;IP%:IV&Q=,?9:YI@&0(]D+FM;D\K<C#.-NU@P,^^R!]$<J[5J,!@E4KJY
+M8S^H)H`6-F"8/=":C*1))`]U3NKS,"?99E[6N;EXHTVNSRDT5O6%0WMZVG3<
+M2V<KO>@68H4FXX7+^!>EMMZ+:K_SD<KLZ%:E3;&J%[/#A]KS^3+XNF&[*AU.
+M\#&&#E0=0Z@T,(&_=9-5[[A\`E>K>G'LUS7+R7.,A1=.+7W.H$#Y3U[-Q$$+
+M0Z59-I4]3FB8WA3OAG_4]-[7O+,$MY5R@W$X*KLI@NW*M-U#&WNNC)WC&57J
+MC)GA3N?@M*KW)`&#NM1*S^H-!I.7(W-/_P!<XCOLNNO3_(/=<S4IZKUXC,K5
+MZ<[VYOQ51F@Z1NN.Z56-KU@:MB5Z#XEMYH.,+SCK`-&^U@[%:LW'/JO1+7^9
+M;->T[A.ZGZN,[*KX4N6W'2Z<'40%HPX[[+#:LYF2T-D<F4`8&RK<`"57JD!T
+MSNFA#5``$1*HW;FD$&%<JO`$Y5&YR_:545"WU$CCA1WC-=$A712EH)P@<P$E
+MKCC@HE8%H[RKPT\D']%<JZ9@*/K%`4ZPJM.`5(8JVPJ-@'V4ZIW%:YIP[6?W
+M3!K7,]RIW,#FC5B$%.F`X@&$%"K3-*KOZ2KMJP8)X37#`XG&45C^:)&%*L;%
+M!W\EN>$6L]T-`M\H8E%+?\*SIMS'7#_"T-'(W6/08]@-0Y:_*VO&8_D'&9X5
+M2WJ,=TP-J-`<!A=9=[<.G/7CFN>0W:>5E==93=1@CV6SU*DQO\P+%\05)MAB
+M3*L*HVE336;JTXSA=5X<J>EW^:XUSBQ[3'W70^'ZY#)X/*WWRQU7963]39!V
+M6ST]P(&#E<MTNL=/YACW71=-JCR@0%&FS1=ZA&5H42-.^5DT'ZS(!$J\*P;3
+M+CVV6=+M)=UG/<*#'23N95ZRHBE2`@@\K/Z8T%YKOW=LM:@X$"3A3_524XVD
+MR5,`(B%$V-X.RE9"*DIMB"5+`<R`HP/3,J1IG$J*A+32=K9LK%O4:YH)&2DU
+MH."9)45>D6NUL)]PBIR)S&/=9OB6GKM25?H5P6AKL%!U5GF6C@"I2,#PP\A^
+M@KJ;=PB97(]#>&WSV.X.RW[F[93H:0X2>RVST;KO4`QOEL,E8_3Z56]N(&W=
+M2/8ZXJ:1))Y6Q94*=C:`F-<96=G8+RX;:4VVU*#4/9;'1*7E6X?4&2-UC],Z
+M<ZOU$W54SR`2ND`:VAI[8PN.5VMA6W4:+ZYI!XD<2K#R'`&?C*Y#K6JPN?/I
+MOPXY6ST'J3+F@PEV2%TQU8U_5I56`M@Y6;?6[2"M34'>D$&5#>-:*>8(2QIS
+ME;^6X[@!1MO=!C)6C<6KJ[SL`HSTQ@&1*SLTSKBJ^Y)9_2N'\9=&+*AJTF3.
+M\+T6I:-9^4`:55N;!E5A%2,K&<]N6L;IXU7IU*;I`(A;OACQ3==.J-94>2S9
+M=/USPO2JM)I0#[+C.L]&KVE8RWTM*QCG<6[C,GI?0_%%EU!@8]PF,_[A;0MK
+M.K3-1L`N"\-M*M>UK:V/+7!=#TGQ?=4G>75>=/O_`-ET_CFQSCTWO'?1YHOK
+M"!"\?\4U88^F<0=Y7J/5?$'_`!"R\L.ES@N&ZIT2K<5'.#=1)PO+YO'KAW\7
+MDWV\SJ>8ZLY\&3M"EL];ZKJ;@`!GY787GA_RO4YD+&N.G,I7&L-+?A>:XZCU
+M3RRHQ7=;T#/S`1]-ZG4J7`;J,3D2J'4V.<[,CY0V3/*)>[Z<+EZZCK-6.^Z7
+M?@4@TG/`*UZ%1M2D)$D[A>?],OG/N@ULS\G9==0J.IV8<(EWNM2\.=XK>MPQ
+MFI[2/H5:97]49C]UA=*K.JB,GYY5FI=EC\DAW8JR+[).IU`XF0-^5E=7Z1;7
+ME(L<P>K*DZK=--,&23\IK*O4J.$\>ZQE_KI*X[K7X=LN-3Z(ATJI2_#6NZD&
+MAH:!S.Z];MS3%LQK@)(S*FHUJ+1,1"LEZVZ3S61X_5\"%CJ=)]-H:S^H#+OE
+M=3X=\&6=HYM3R?4-B0NHJ%E2Z!#>58K564P,PI9;Q:7RVK]E:6MK:M+:8!(Y
+M2-^QC/Y9,MX"R;SJ;74M,RJHNV`Q*MLG#G.>URYZH75C)52[NP\ZM655O&:S
+M+"<YCLLZM3>&%KB09Q*X^_QK2Q<D5-0+S*JUKC0R`X]E4N+MU(QW"CI5C7:'
+M05F_E6C86C[CU9C==YX&HLIM;+0>ZY;PM2\QH;J))7:]`L:E(X^JU).TMXT[
+M'IU2F&`#$J[YX#=\++Z?:52P1)6I9](NZ[L@@+K+\<D52]$F"H*MP\MELF5O
+MT?#C6MFJ8A&_IMO281HSPNL\.>7QB^3&.2J&XJ.@`Y3#IM>J9=NNG%I2!.EH
+M1,H-G\L+>/Z>WMB^;\,&UZ&TP7"?E:-CT2D'`BGMW"V+>BR>,*[;AH'$+T8_
+MI\8Y9>6U4M+(TV@-=MV4E>W>6@-*O4V:L@Q[*5C`)E=ICKIC;#=T^JXYRAJ4
+M*MN=0^RZ-K`L_J3FG`[K4P9M8E2\J.=!D`=@H.J>)[?I]-IJU`/;_82\075.
+MSMG/YX7E'B^[N^HO>:;"6@K>YC/;)PSMZQ>DV?X@]+J5=)KM!^O^2Z#I_7K>
+M]IAU&H#.<%?*U6G5J7]2F7O8YIG!73^#_$'4>D5!3J/+V#:5G]W&W34E?1_\
+M4',`)RHZE8/V,KB/"OBVVZBQM-]0!T97327MUT7RU=$V.]>=!@A8%)Y_CG$@
+M22KO4KE[00X$++L:[7WD#E6])]-X@;-$X^B\U\5TCYSH:!!X7J75!KHG'T7G
+MWBVVRXYP5J.=['^'%V^7473@?W78R8_U7G/@ZLZWZL!,:L'*]!+R:8(^\[K'
+M^-"J.`;D*E<D:L%25GD#=5JIY*NA'6+B,C?@*![1&0IBX.RW<;!!IDYWY01!
+MQ@B,)XU-@%3>3Z9E`69G909O5:6J@[.%1Z82&.89QMA:O48-$\@<+&L"16>$
+MO42?5DAAV'R(2:P`AP">F`XR1A3M8W3@04%&YSB)16M,@SIW4KZ<UM)D?"FI
+M4BR6G'92D6*#1Y39(E%#>X2;HC;]4O3V_59TZ.:94'4>JN8XC0P_=#U6A2I5
+M2`0.T*#I3?(NJM7,%1]7>XN:^<NS,KMJ<//]9G4*9`QD%8'5Z(#20=MPNGNO
+M5;SB=Y6!=L<*CIR2=DJN:NJC)]6Q.RV/#]0TZ,D@SD_=9'6&:+B>"5?Z0X:0
+MP5/:>ZU.F:ZBP?',+H>DW$4XQ\KDK*J`X;SW*W>G58@DXC9(O;KK!P(&=QDJ
+M6O4#M-!I!)W6=T^J74QZN%/T]X?=NJ$&!LI81OV+6M8!V5ZG^2(*SK)PF!`5
+MZF^!NHTM429W*EIB8[*O3<)B=U9;@"%%3,_+`W4C6X'WE14\22I9$1.%%/F,
+M#/RBI@_U94-2LVGDN`4)OJ0$`?571M8KVQ)U,*@KUBRF14G'*=G46:=L+/ZK
+M<NN&:*;9E/4VPK:JYO5ZK@#"T#7?7N!3&RDL.FM:34K'?DJ*Y=3;<AEL-3AV
+M4EX9URW["A3I4\Y="N4[2I49KJ?EWA#X=LGZ&UJ_(E;59H%.`V`LWGIN37:C
+MTUA!(C'"NENH22`%%0`8X@_='4JRW2"#*QC-W:3MG=5Z>+UODM@CNL=UG<])
+MKB"[RUV/3Z$`N</S)NI6=.YH%K@#'W4LU?:.LFYJJ/3;ME2B"")[*2X(JPT;
+M$K%(JV%=S7M.DGW6CTVX:^H"3LNDLRG#.M7E?I46,IP<J&XI`;`*SJ!`A/ID
+M=Y4TTRZ](&<9[JM5ID&2`MA]$OVG*J5J,&#/LH,]S/0!&#RJ?4NDT+AGJ:(*
+MU:U.)$%`YL".`LW&7M97%]8\*T:C"YC1/LN.ZQT6O:53#,-)7L7EEPP)"S.K
+M]+94!=IB=UROCUTW,M]O)J3:E%^DL,!;_1ZC'LBH`#O*Z#_@%.LXZFJG>>'Z
+MM&KJI20F[]2R?%&_Z?0N*9.D+E^K=!8ZF][6R`<GLNW?9W%)H#ME5O*#VV[Z
+M5,"'X=A8N,R[:F6GDO4^E>K2UNTJK=='<:<AI!7=WO2*QJ'33+IWPH+NV<RE
+MIJ4B#W(7&>&?EV_=KA[*W=1J@D!L8GNM6ZZEHH"'Y'=:0L*=1I&`%G]5Z:#3
+M);&3O"YY>*QN>297E?Z)>NI40YQ;!&X4M[=MK/:01)]]ED68<RB*;C):,[J&
+MO5J4ZX:""TXP5PO'#K&C4J^:[3J,3O*N6E44-.?JL>G5`:'2`!O/*EI5W5'[
+MRUJQMN.CI7CG$MG"G_B(GU3.ZR*%9E%AAPF%4N;\@N>"M^Q&ZRZ:VO@SW17E
+M?73UMW/'9<C_`![G7,AYS[K09?.--H:<_NI<MQ=:37-4AQ<#OPJ]*N[SR2X^
+MR@N*SG"2`0/=5*M<"H2TG*YMR.@L+L.J-:YVVZ;KU;R6BHT$25SM*^%*LQY=
+M$')!70731=V!<001LDE6\=L*XJ><]KFY]N5IV5`LM]>K$8PL[I]J\7D%OY<#
+M*ZNTL:E2ES'*LPW>&<LI(+PV'4ZP=.^R]6\%61NV!SMB/\UR/AGPW5K!L-7I
+M_@WIE>SI!KFQ`V^Z]7B\4MU7G\GDNN&_TKIE*E3:"`5JQ1H4Y;I4%K3?H#LA
+M!>M<&Y)7OP\>./3S7*U6N[MU1Y:W;N%4KU''!XW*FH#+DSK6K5K:H,+>^&5<
+M9,P$3`XG`6C0Z<XMET_57:'3F-W&>RD&71I.DR,*W3MW2M-EDT$G=2MMVZ<?
+M9:D%!E)P;G*)C#VA:'DC3(2%$1(51FW>IE/98=[<>6"3]ET'5"&4\[E<CXAJ
+M[M89DK7QBN7\47=2YK.IC8'[)>'NGVE>V=J:-495^WZ9K::CQ^9+H-D:5Y4`
+M/I/"X>3^4W6<IIR'5/`M.OU5]>B(U'98G7/#%U9G#-0!WA>ULLAIU-/Z*M=]
+M/;5:6/:#VE9GBUS&Y>-/":1K6=>6RQS5VW@OQFZ@YEO>.G&Y4GC7PN&36H,@
+M@SA<!U!IH52UP+7-,;K>&5Q,IM[O5JVO5;'72(DB5R5RVM9=3)$Q*X_PCXMK
+M=,K>35J33(@2=EWEA=VW5XJ4RUQB=_\`?9=]RS;EK5%_Q!M1FEXAWV7.^)F-
+MJ,>=,@G=;/7K(T3YC20?98EW7<:#V.`U'&1LND<LHY*UJ?P_4@X8@XRN_MZQ
+MJV;'-=DCNO.NI_R[N1O*ZGP_=N=9MU'("EG*R\-USCR?LH*CAM.$#'^8-SE(
+MR'&(4:`0!ZN5+3F-DWECVD^ZD9@94#U!#1F57KN]/8\*U5;Z0[,JA7=G?<]U
+M8BM=D&GI)_18ML?+O7C43/9;%T<.))E8E0D7Q[)>D:=(2V=X[*3;`5>W=``"
+MLTFF>9]U%-1:#5]0(A3/`!E$QL"2<]D-P0,_HHJ6F\!@&4_F#W4+3Z1ZH]D\
+M_P#6HUIS=9WH;3(`,969U!AT$QE2U;DFLZH1`.TJM<UBYLR"5VTX(:A(H_F^
+MBS;RF7DN=PKM=SM!XGA4ZHFE,PI5<MUUI;7#G3I!P0=DW2ZCVB7MD'^J5<ZQ
+M2Q,3RJ5H#5I.#I`&V=EJ=,ULT*Q#P2[!'!716%4:6DE<9:5=/I+7XY*Z+IUP
+M7TFB=MH*I+RZNA<%E(0Z0!PM?I`TVV3NN5H5XIM'O]UTE@_^0WX4L5NV3\9S
+M]5I6[NZP[6J`X0?JM2SJ:FR)[Y4I&A2_.3E66.TA5:3I8`%-3)U1*C46J9Q(
+M0WEVRA1DD!)BYS\0J%[5LV_P9(/LL7*8\UJ3?#8I5*%4:GU9!15KNPH`RYI7
+MF-!_B*D-&A_;A07=MX@N`Z7.SVA7W\?Y/V\^GH%]XAZ52!UO:`-LE8UYXZZ;
+M2D4(<?:?\ES'2/P\ZO?U/.O*[P#F%UG1/PVM*+PZOZOF?\UC]Z7^N+7[6O[5
+MF#Q/?]1J1286TW<E=KX*M*9#:CQJ<X<I7G0+2TZ=_)IAL)>&:S:;M$P1A:QM
+MR_LSE).G;68``;$1PIZT"F52LGZF`RI[JJ'-#1E,NEVJ5ZFFF23NI.E4C4=J
+M>=ME'6HZR)..RO\`3@&P,=LK&*XS2]28(P43FM.WZI,&(!!^JD:.\?=:;C(Z
+MYTYMQ2/^+VP%SEN]UG<EE21P%V]0!P(,$+"\1=+;4I&HQN1V7/\`K=Q;-\4U
+MI=,<T>K=7*+VD#..,KF;-[J=4TW...Y6Q;O)8,KKVQTTP6G8P.RCJL;"@IBI
+M.ZE.N((6:T@K4L3&ZJNIGX6B[20,;J)P;R/NH*@!&!A#6IMJ@#WW5I],3,?9
+M`8;N84[X7I49;,INGGNF%!M5T$81N>:C](`5R@QK&#W4OX(R.H=/%1D:<+'J
+M]/'FZ=/LNV\K53P%5J6+7/U1NL7%K;F[;HM$PXLRL[Q'X=HUJ9T4Q)W@+O:-
+MG@`C"CO;!I&V%+A%V\AN/"M1C"6@_*Y?K?3*M'4T3\+W>ZLV"@06CZKS/QI0
+M\JY=%,@$]EBX-3)Y6\/9=%A#AGE-6B"8,CLNDONF-J.+].%B7ED6/+73'[KQ
+MY^/5>G'/;+\XNJN9I<X*Y2,-D8CA3-M&ADEH'>!NJX8]I=`7++#3I,]E5N7;
+M!Q'<JA?W%1KBQI)*G;3>*IEI=K*LML*;FESV$D]UC4CK+(R+8/#C4)]+LD+6
+MIUM&F3Z>Z"[I4J,`"&G@*(.<ZEIF,)9M;EM9K7-)M$^H@E9#*IJ7!!=`'8*V
+M+2I48&.;^;F5:L.F-#=9:1ODJS$]Y(:QM&U"Q]29WA=7TI](46L.\05D6-J'
+M.#6%=7X:Z,0W6[)/9;PP<<\_R'IO2:=2Y#V,D'*[#H_098':-ALM7PKT9C0/
+M0<CE=OT7I3&LDMX7J\?B<,\]L;PG;-HU-#F1A=I9TF!H(`677L/+K:J8`6G8
+MM+:8D_*]6.,G#BM&H6F&C`5;J#I&1'PIG.;JPJ?4GM#9#I]ETTSM6K5*=&GY
+MCR%-T[K=C&EU1I(]PN+\:]5N:5`TZ3'?(E><OZIU6E<N+*M2"=I*7+#'BIK*
+M]/HAO6K,`$5&?=/_`,=LA_\`I&_=?/K.M]>B-50_=%5ZGXAB07Q]4_<\9Z9O
+M?CXBL6X\YN?^I1N\46`.:K?NOGQW4NM:CYCJP^Z.C==1?E]6H([DJ_NX)<,W
+MT+1\1V53`K-^ZF/6+73+7A?/G3KCJ8KRVN\`^ZWJ?5+UC`U]5T_*OOA4UE'I
+M?6^L4G-TM=)'995"B;EQ>X$MW7/^'?/O*\U2=*ZI[A0H!C#^JS<O>\=+)Z]J
+MM^\,864QD*'HC-%4ZOS%6J-L:KBYQGE2T:(:\F!]%G/IG2_;N](DRCTMDF<%
+M0T8$P1*DDQD[+>/,%>_MJ55A8YLA>??B#X,IW=-U6U;I?/'T7HU0@C;*JW+`
+MX$`*Y8S)9=/FCK?3[VQK&C589:2%K?A_XDK=*Z@UM;46$1GC_<KU#QKX8H]0
+MIN>U@U[X7E?6NBU>GW+@]A`G=<;[8UOC)[(:E#JW3&U*3VY$X*Y#Q#:/HM=$
+MS.ZS_P`.^O/L[@6E<S3(ALE=EXIM&W-AYU$#(7IPRV\^>.GDW52YU7,`@[K9
+M\*LJ7%N=#H@+.ZY1ASLPX'NM/\.G#SC3C!5RK.+7:+BD0"?96*3*[@,`+5K6
+M8.0`HV6[V]L;++2@:-P)!<$#FW#,3*U7"&B?U4%9NF85&;6J7.VP"S[JM7!]
+M;/LM>Y,.R`53N2UXG2$1E?QF/5QW6=4J-=?AS7<E;%U18_\`ISW6'<6T7GI/
+MT5O236VO;N&K>)Y5VAIF)^BPPRNS.DJS:7548<W'S*RK<C21G"@N'`.V*K.O
+MV@:7#]$GW%)P!F.R"VP`M">&J%CVZ!Z@G\QO<*-.$NZL^GA.`/+!=E1U634P
+M/=2L:U]/2.%V<5:H095:J=0CG967`ZCC95ZS03'ZJ:5E]8I`T-``E9UFP`.:
+M0/HM/JC9JM9V"IT*(:\P))25FHJC21#A\:5=L:CJ+@W8#@R%7SKCCOPK)IEU
+M-N-7N$&O85IK-DF"NJL:HAN9PN#L*FF[%,SA=/T^O#1"U.5=/:UB7!H^RV;*
+MI#<?J5RO3J\O,N)6]TVKKC&R6(W*-0``!6K=S7.D+-H.DB/W6@R&@`?HLUJ5
+M<:X<HJC6ED$`J"G!(_NBN*PIT_[J:VNT%X;=@@,"CL;1E:KK<T`)4*+KE^M^
+M!P%IV[(`;VV"QJ7IKI:M&-:T-T@*WH]/^JKVXSMLK;!_W6A4ZC0\RU(7+V+1
+M;]1<TCE=F]DM(7(=<I>1U`5`8DY69=9+>G4VEPQM)L<C96[`&JXE<_T>J;D1
+M&RZ;I5(LI9Y4SO.G.<I:K`&>Z&V)#O4%/5&H?Z*!PAPSE633JT:)$-RI9RJU
+MLZ6@&`58:"8[(T51H&905F![()!"F+0/<CN5&1@D`945S'B#IQIN-9@(^%5Z
+M==%I`<2/8KJKJF*C-+A([+E.OV+[>IYM,&%B7UO^+9MM6M5KP,R2.%;`$9Y7
+M,](O@3I(R-UMT*NH`@[KK6)5M]-I]E&ZU!R$;*DP.49]+20<]EC4K6V=>L-,
+M8*I-IU:C@'#'>5HO'FU2(Y[JRVB`T#3"S_D5FV])E-V<E66,&L$%2U*()QNA
+M-(@X36A("`I:36P2>55<2",8[J1E8!%70UH;`E#4:"S*A%6=M_E.'M(R<H*M
+MS;![B-*Y?Q?T*G7H.<&Y/^^RZVK4`<51ZE4%1FF-UC+':[>54>A52]S(Q*Y[
+MQ7X?NJ)+@TP3_OA>P4NFN;5-7RR0[L%'U3I-.[9#J6X[+A<-NLRT\";1J4_3
+M4$_*MV?3&5R-`A>B^(_"5%E$O:T`_94/#/0CYI#FX"Q<-\6+[:YC`M_"DT-?
+MUCLLWJ/3S0FF*9G9>K'IS@T4VTXGE"/#--_JJ,U'E+XI.B>2WMXG<]'N*KQ_
+M*<0I+3H=Q(:6GY*]J'A^V;Z13_15+SPZ)!8T1\+$\<VW^Y7F+NAOIM#N5/;]
+M-<ZG!/U7?O\`#-6J(`PK%OX4,1IRM?MQ/>N#Z'T>+F2["[CI%G2I:-3H5YGA
+M&L!+"1[PM&T\)W3J0ESI[PM8R8LY6UK]!JVK&CU`'NNCHWM!K``]H7`7W0NI
+M6DF@]Q473K;KSW'4XPO1/)XYVY^N5=U>=2MV/!+Q]%/2ZM;>0#J'W7#5>C]6
+MKN#B]TCLIZ'0^JD:75'1]4_=PV>F3H[_`*]0H_\`Z1L?*CL+\WSBX&0L^S\+
+MU:@FKJ=\K>Z7T06S!N,*_N;XD/37U%>V%K<VY#J8<3W"P3X-MZU<O\IH;VC_
+M`$7;T;-K0"3)/NK#:=-K8`!4R\<R673E+3PA9,:)I-D>P_R5O_RY9-;'D-(^
+M`N@JQ*@KN<!NK/#BERKGJWAJQ+3_`"69]@LOJ'A>U+8ITQ]%U^E]3$84M*U!
+M;D9*7Q8_#VKSEWA]U(D"F`HO_+UQ5J`Y#5Z3<65-PF(*SKLMH'3`,]E/V[\I
+M[,KI=HVQH``>H"%;HT'U'R9,J6A2=6=J<,%:-O1#0!"WC-349O/**WHPS+8^
+MJC<P"K!G*O\`EC3@*C=RVMR,K>N&:8M+78S*1,$J:MFB#'U55\SE2<5DG.TG
+MY4535F"C)$@'"9\&8*TJK5;JF2N;\3]"HW])PT#5W74%A)V0OI:FQI4N,IMX
+M7UGI]QTN])@B#Z2N\\!=7;U'I9MJSO6T`>I:7CGH-.\LR]K1K&<+S[P]<U^D
+M]:\IQ+6DP=USG\:MGM%OQYTS^%K/>UITN."%E>`JOE=4+"XYXE>@>)[6GU/H
+M@JL&0)QRO-^AAUKXA@-YA>B_U<9V]4I`.IC!P.2HW,R5-8Q4MV$DB0AK@#(4
+MBJ58.S.P4%9TM^5;J@0>52KG!C"(I7,D20J+R-6ZO5RTX"I730(`PJB"X.#(
+MGLLDLF[U`+3N7.GV673,7I(VY4O5)VO,$S_DIZ+!AT#"CI`;S$\J>FT3@J*"
+MM1IO&VRK5;5IR'*XX$-R@=!,CA-$0LM:VD14PB_A:_\`\BMTP-`S">!_B4:<
+M"XESH."$=$D3!D!0L)#<R/D*Q;Z@V8&Z[:<:BKR'%5JC0'YY[*U=CU856X(`
+M)D2%"LZX:7W3G#@PH:K);`P?E:'E11G:52JL(>2,SA155K)'<'LK-`Z</&^P
+MF%"YI#L_]U/0=K<,25>TZ)S1_$-)D&-PM.QK.:((.>5E7NJE4:YAF3$'A7K"
+MIKPX3"16]87!'($KH^DUM%,$'=<10JNI51.&KH^B7(>T'5*TS766+Y=RM:W<
+MV,RL#IM4$3NM:V>20I2-$U64J9<H+1C[NOJ(A@]U6K$UZXHM)CF%L6K12IM:
+MP<+%_#<_*9C`Q@#>$QD&0-^Q4K#QV35#`C)*:5-:N.L$G`V5ZFXG/<K.MW28
+M5Z@9$;*BP(+8`*YSQC:N>&^4W<KI*8_EX,*I>L#ZC01)W6,HTH^&+-U"V;K!
+MD[KI:+=#`J=NP`-``$*9]R&D-E8QYK&.EID'!450#5A/1JM)B4540<+I72#M
+MR!LK=%V),+/8=.T^ZN4G`MXPBQ+(WWE(Q_LI-!/;'9"T0XP5&C.`TD2J74*-
+M.I1+7<A6W&-S/LJEZ?4`.<%2P<?=6=:SNW56DZ25H],NPX`ZMEN7=FRM;:2!
+MLN5ZA:U;*O+0=)*SC=<4RF^736U8.&_"EKU(9A8/3+T.:!^ZTC6;4<!.5NLR
+MM"RICR]4F5.6>KTRH;?_`)<B!^BL4L-EWW4D:1NIR,F$!IP`K`;.=D[62X!2
+MJINIRA%"1*OE@R(`^JC-/U0.5!4%(@9PC\H1,[JP:8&#$%-H[#"FE5:E&1*J
+MBW\RN`6\[+4-,QV3]/M0ZKJ*6$2T+.F*0&D;<!17'3F/`AH^RT]/&F?92AK6
+MMG$=E-1IR'7.B.?2.`0LSIO1/)=ZFC[+MNH5&Z(T_14&^HQH4])O9MF?P#`V
+M7`>REHVC'0(5ZI:U'$P"FM[=]-TP5C*S>ED9]QTX%X@<\*:GT<.8"6+:L;37
+M4!<%K4K6F&Z>RSCA]:<H.D`-!TA2T.E0Z2V%TE:V;B!E)M(-`.5UF++,H],:
+M6B1GW1/HMH-($>ZO5:S&LW.%F79J57P-N%9C(EY0/8VK4RR<JU2M*#698)04
+M:19$@R=U,`Z<@Y34MY7F#9:TA_0$YHTFMPW*"7!V)3OU&-)*:B<B&D-``@IG
+M/(=AOV2;3/\`4EY3@XPKP:)SC&WW3EV,G]5(*9:R0/HJ=:I_,CCLLY9Z60US
+M<EHP%';.-P^`FKEDP>>ZFZ-0&LOXE<YEEMK46J%#2,C*D<`T2?U15:M.F#D+
+M.O;IS_2QR[R,6FZA=ALAOZ+.%)U6KJ>,%7*-N7G4XRK+*``@<)645O1TMPV%
+M*&CD*4@S`,)0"?=601D'Y_LJ74Z0@&/HKYDG!5?J(]`D[*I4=`:K<3!`4;J0
+MG"L=/&JC.T(W"#')6/\`4RXK.J4(<9"C-/@K1J"./E15&3D05L4_+$8RDQD-
+MSCY5O2(E1$;@B$%2XHM<TM(!E>7_`(E]'_AKD75`$23,!>JOP")E8?B^PIWE
+MBX.:"8*93<3>G,>![L7O2G4'.ES1&3[+D.J6O\)XK((_,XG]5T/@B@^TZI5I
+M%IB?@)O&]H1U>C7:`?5O]5,,MXV,Y363H>GD?P=.,X&R>XVSA*P'_HVF!D!#
+M=P`,K>*5!5V/"HW!,D#]%/6J0)!E4Z[B3+9515JCU<?=4[C\V3"N5R)!.%2J
+MD$Y^Z(JU3Z\SLJ=O+KEQ@QRKEUAITR<*IT\S6=D_52]$7&P'1'V*D9J<[T[H
+M-!C8%'0@;-GX0&[\OJ.R:F&X.K9.(G,DHBT$=CPHL.`XB92AW=&S3I$RG]/<
+MJ;C3S.W=+)[JY;5!@'8\+-MWEM*-6KO"N6;IS/W7=P6KR"1IR%0O&X#!_4<J
+MY6>($[*O0;YUPZI,M;@+-6'>P>3@Y5.L``2<=CW5\!KG$=N5!>4Y9EON$JL:
+MH)J`D$CLI:!)<``(]U)=42*0=F?904FD.F00#L"D2Q)>M:6M#@"?CE'8.#'X
+M&?V454&I4B<`<Y*>FZ'"00-B4B5IL+:A!&/E7>GW+J#X;^4\K,L7S5+85YD:
+MM)C/Z*SAJNQZ+=M?3$>PRMNG<::)*XGICWTP"PB)6W:7IJU&42XQO"U6>G4=
+M%8\36=C4M:VENY]UFV-1OE-`.!'[+1HN$3,+&FEEASE&X225%1@Y)4\0T&8!
+M4L:!)!`"M6U0$Q]%3<,GD_*DMW:'=Y1&K2)B8A-#7OGLH#6TT9$!3V)+J>HQ
+ME8S+4VK13+BJCGMJF0=BH?$UT;:UP=U1Z1<A[0=6_NIA.$C7H574G2<A:%"L
+M*C1,3^BHT--294C&.9EI6W2+FKT[;?<J>BX0J=%X=$JS2QSNHTM!P`ALGW2/
+M>4+-.F!^J*0!D*:4%:&LU$!5*`-6L29@84EZ_P!):'&45C3AD]TJIF@2#V5#
+MK-DVYI$`96BXP0(3.`TZ5,IM8X*X8^SN""#$JW:70\T21D2MOKG3V7#"=.RY
+M.X94M;C27>F=UG&WJLY3['96E753W5JG4).-AV6!TBZ#FB7+9MWR)E;TDJVP
+MR(/=3,@`S/95J!>7^IP(XQLK$<\J-G:)R#(1`<D)J8/;*D;DPI1$^F79!`4C
+M:+0WW`4K&M&#NC;N,84TJK7;I:K-C3#:<[2HZK2]P&%:8T,I:8'T2K#B!RHJ
+MSC'8J0R_`4EO1#R9E.A3;9NJD$@Y5@6+&Y(`^5ITJ(IT\H;EHTDA36U4J=$:
+MM$*9UI2+?RH*!FI,X"LZ].Y4UNJK-I%CI`,*1E<@P>RL`->T84;J(>9[%76A
+M(QP>-]E7ZA#:>#**K2=3RT_=4:M74_0\\[J\!J-)U0YF%890:!NI:+0U@&_P
+MIV!H"FOR*OD-[;G=%Y`U1"LD-F>4_I@<IHVJFW`3MH-Q"LB"-N$FAF"#":$+
+M:`)V^4A;@[[#LK!+0))CZJ)U:FW=X31LWE\8"JW%BTNU$HZ]]39L9CW69?\`
+M5M.&3E+A+V>^AW]M2;#IV04:[:=.&!0-K/N0"\DCWX4U*FP-RDQDZ2VWL%4U
+M:SX)PCIV^@[`^ZL,\L#>041@[%:TR&FP!IGA.-_A.X`#TF4$#5NJ'?G(*$3`
+M)^$SS,]PA>2UL]T!8&(S\JMU""S&2IM7V4-_`IRWZJQFGZ8TZ3&RDK$:@=H4
+M/2\-,E2UXF9P5SLX3()<")(]D#R`(*7[#*$YAP*W.E"8TX0.&9`GY4D3_4AB
+M'^RHKUJ<CTR%4N:.JD0[E:#Q.1N%6O\`_EX5B5Q=>V%IU8O:/S'^Z#Q/0\SR
+MGZ3W*UNI4`ZX#SIE4>OF&,`B9W4DUMF\E;PR@P#``4%TZ2<?JB#M5,`G<<*K
+M=/B8(GE:G#*M4)DP<*"H9&)SPI'.!F#\@J"J=.SAW1%>OEWM[JM4#OS<=E8N
+M#J]H*A<($`_=!3KM9I)DY53IP'GO),$<*Y<?D=..,*ITP.\UT']4O1%YF<1^
+MJD83$1`'NG#1,D'W'=.3+<84#.(<!!RG`[DIZ1:1/^J:L"#G"BI&_EW/V2^O
+MZ*)E1FD22$7FL[E--;>/4*CJ;]1)<.%KVM21,P>RR[?3IT.SV4],NHF=Y^Z[
+M;_+RK=P]U)AP>PRKG3F>7:^K!(G*RJU7^)N:=,8:W)6L'?R2W[*::@0"PD\(
+M+C\H$3/Z*5Y/E1$DJ/3.\[(TJ5VX`@[S*I!A95(G[K3J`D9:%0>#Y\:02HE5
+MVG_U;A)!`P%-J(.9]E#/_JR,R,;84]P"TSIGXE$Y%0<:;I#MSW6@Q[?S+*HE
+MSJ@!&^P"M^8-@[E58W.GUG>6<K2Z('U+AU:?RX7.V58Y$D!;OABJ,@DS*UV5
+MU=A<NI.&HF%MV=TVHT9SNL&U#:@$1*O4&.9!;Q[K(Z*W,MW5RG.W985G=EK@
+MT\[Y6Q:56N`,R%*U*DJ@`2H9<TR)^JM/TO@JL]L'(E94=2Y'I870MBP_Y+3.
+M!"YZDTOO6[P%T5`%M&0N67-9O;'\8--2E(_I_P!5B=&K%A,SV71==;JH'4N6
+M:UU*K.5O#BMV<.MZ96U$0>%J4CJ;PN;Z/<9`G*W;>HV`9)6S%8JLV<U'1JC`
+M(3M=Z1"&I3CU`+/3HM4W9PI'/&GC"J4:P@ZC":K6!PWE4V6H5KF-X*O4QB`J
+MW3Z`8TEW*M`9V69^6C%V0$;8(D'Z)B&XG=$&C<X0156M(((GA<YXEZ=K];1L
+MNG.^57O*8>V"-\+-QVNW#V-1U"KI=N/==!TZZ!8)/LJ/6[!K'ZX&%5L;G2^.
+MWNM2[<[-.JMZDG#L*SK$]UCV=QJY6C1>#C=-+*OL)+)E24CE5Z1ALD>TJ>B9
+M(SMP%&ED$$`;]T1_+,[**8*)YTL10T0'5YD8'=6JL0%7LBV22`K+&!SO]5EH
+M]"G!E7K6F&[0@HTP8PK--H:`"Y%AW`!D2H;HPS^RF>0!G?Y5.\)TQ/NJE0V@
+M'F&45P8YP5%9_F),[JQ<4R1A94%"M!A7:3FGZY654)IU([*S0J>D=RM)%NZT
+MZ3`&VZQ<&N8V6A7J$TX<#"S[9NJL7`*6+M:+](QPA_B-)DIBSDG'9`:8]\\J
+M@_XKD9A+^+&5`:?J@=^`F92`=D%-HE_BS)]TOXP\%0BG\I"D,%-B2I7J$1)4
+M%057XU8*L-:`$X89SB%-BF+5Q,.<?NJUW:`$20M8#95.H0"`2%=<`+:V:*(+
+M2?NC%)VTQ[RI:0!I8.GX3GL2DG`K.;4:3#DPJ/8W.1"M$#<J-S`>$1"VYP9'
+M$)V5Z9`(*9]$'X41M@#@P$1/K;O,<?*(D1CXW5*I2>T^DJ-[JH]U=C0])&-P
+MJ]_4;Y<$A5Q=N!R%6NZFL:M7W59J[TUQ<V.#LK-5I$$\JMTA[?*)F5,^H"Z)
+MPN<EJ7@)'I$3*0$"/JDXM/*0$N@25U:(0/E(029"<3&R=\#VQLH`>`#E4[PC
+M3."%8JN),-5&[<6X,JLUD=3J,;5!,?*Q.MW-.I48UI$K9ZM0UTBXN`PN.>QP
+MORTN)SS\J\:VQ]:A<!3EHX5.L_48$X/*G+O1`.0JCYUD"2K.D`[O,J"IVB.5
+M*^1&-U`_5L4$3AJV*C<V1!^BFB#(0P8@")4-J-TUVDDJOT9I-1T?NK=P?26@
+M1'NJG1!ING@_U83+HC1+0'$@9(SPA:UWY01'<<*<M'"B+PPGE`5*F!'8*.KD
+MD/E2,=$$2HG3K+CO\J0(-;`]2?2S_$G#C'"6H^R-</%;%\.&J"9V6HUPTR1(
+M&<&%B4*K34])(]IV6D:W\EK&B2XP(7:O*.U#]3ZS01G"T[.X!;H><^Z"@P,I
+M!@R.4C0TN)&^ZSTWI>)!@`(7,(4/3K@ZB'JW4#33F55BM6U:2T`'4%GO]%:)
+M(/Z+3=3BFXSNLRZ#6.)=R,+/TO2L[2ZL7N`[*\UC74X':!E5K>D#1U%NYY4U
+M":9`F0>2D$%9HHUO?=*K^77,%3WC&N]>F>ZJD#3I,P$5;L7M-,ZCQN%L]`J^
+M6S5.YY7-V=3^2X@''"U.EUY8`>/=;B1WG2K@$`D_9;UE5#V@E<3T:YD!DKI+
+M&X``$E+$Z;HIAX)8`3Q+E:M0^F1G!5+I]68]2U*+@6C"SIK:W;W`C2XY/NGJ
+MP&EP)4'D2-0W[H;ASVLT[GY6+56>F4O,?J$[K9HP*<3LJ'1O10]6>5/5KR=`
+MY7/&;J3M!U-IKO#&G"Q.K4-!CLNGMK>*9>[<[++ZQ;R#CZJUUTQNG5?+JPZ5
+MT=A5+V@`Q]5R[V^74Y"U>E7&(E=)S&.JZBW,B)'RIW?EG*S[-X+9F58K50QD
+M24TW*@OJ@;L<GCNFL&U''4Z8G9-1H&N\.=QW6A3IM:W"QW5UI.RHV!*G:X$S
+MC"IFG)D'V1`/:%6EL&3@I`D_TJNVH08E2,K-,@N@*:5*,"80U!(E(/9,RC.6
+MX<IH9?4:(>PXRN5ZA3\FXD8GW7:5@'",K`Z[:C27#CE2\%Y4^FW):()6S:5@
+M1)*Y6D]S'D=BM;I]>0`=EISZ=-;U1Y6\PK5JZ3LLFRJ2)F%HT*D[%--RKP=*
+M&L\!D3/U0-*&X+@WY45-8"3NM.U8/^ZSNEL<6:EJVH)B2/A9;6:;0.,A%(.$
+M&2-PD>$43^TRJMZ`*9(W5C;)A5;QTMP/F54J.Q:1)5MP!'!56RGZ*RXB5%5+
+MFE+I4+':7Z<J[6$@E4JC9=LB):S@:<3A06Y(>0"5)`TX(4-%T5HW,H+1[$2%
+M%IWR84CL9P4SLA`,`@;X]T@V$1_+,(3G?ZJFS?LD`",<).$#!320`-,('#<3
+MRD02(U$)]1_[II_W*!<PJ?41D9/RKA^BI]0R.?H4B4=MFF,(G1)R906CAY6D
+M.E&Z`Y(4Q)C!E-.=]TQP<[)$PJ$X1S^JC<Z793DF$$YRB$3G&2@JP3F/E$XB
+M20<J(DF<JA&FPB("I]0H-T^DB2KK=P)D>RK=1ANG/U1**RHFG0])*"X:\OU!
+M6*+HM1/*$9V"QC$O:G-5IR20=E+2KN&3LI7@")3M:TB0/NNF@S;@!LN3>=JF
+M(2JT6N,!/2HM`01OJ!K3/[JE5.H%Q..ZM7H#1M"P.M]290IEK'"0K)MFW2KX
+MEZ@RA;%K2"3[KF>G!U6H:A$F9RH^KW%:M5-1P,#Y1V5RT,R(E7.SB1B;[JV_
+M>0%`]I.VZD=7IEH@P9V4?FLF0X#W01OU<M]E&]LC;93RUSB%$\>J`0@A=CZJ
+M-X`._P!U-4B%"1G,HBM<!L8,E5>D0+I\DY/=6KD0<D#'94NED,OS/)^ZF72X
+M]M>L,X4)@G(W5BL0!$[Y5=\[?W1",QJ#20.?91U#J=+?DIR^&1_=1%X@D['&
+MZ*E#V`;):V=D#64M(EZ6BE_C4TKP6RJ.=4`)F.ZU^D5/-O9T@Z%SMN[RP3R-
+MY6OX5KZ*+]0`<YR]&GFRFG3T@-4G)]BIM&KLJ=M5)`/!PKE/5/=JPW+#OHX]
+M,`^R!M6HUVEPPIJ4M.43J9>W.Y4_XI$BJP1/NL[J@T-/I$*U4:^D^!.RI]4J
+M"H!3$R=PE4]L!_#"#S*CR:F&F%.&^4P-X]E6I:C7<9(XD8E/J?%QH#J<1,*G
+M<MBK$1*NT7'`.GZ*.[I"-8`^B56>WT%X!C'!16-=S>25#<G0YSCL@MW%M/;=
+M6,V.IZ+<D$23A=/8W&IH/*X7IMQI#9'RNBZ==MU-,_"T.WZ94=`),!;E"H"T
+M&5R72KEKFC,_5=%8U`6`YCY4L)6U;/DY".YA[QA5;1QU29A6:)UU`)GY7+.<
+M-;7A4;2MY<(1=+:*S]9.)5+J1)#6"<J_T2`P-DPL83:XS35T@-C@+.ZG3U`X
+M6HT`MB56Z@P%NTK5CK'(]0H^H]@JMI5\NL!)W6MU.D=1QA9%>F6U)3'AG*.D
+MZ;=`L!R/E60\UZX:#(Y7-VUWHI[Y6_T&IJ9YA,F5K),:VK9C6TX&X4A@;**@
+MZ1DB%)CV'PLNAJ3C)P5-3,NS*!C94C(X&%%%H8[?"B=0G,*8D:1.Z)H&TQ*:
+M7:J*;QWA-JJ,<1RK;?S1PG<QA&=PIH4W5C'K"H]3+'4CW*U*M%GL5G=4HC1$
+MH5S%W2TN+FF`FMZL8E7[RV=Y9F)6/6.A\'ORF-8KHNGW'I&5L658%H]4+D[&
+MKH`DX6[85P<`[JTE;U%TYD(;EY`B5#;O$?W37+QJ:)Y4^--KI##HR=^Q6G3`
+M')5'I&D41C)5X`D<J-Q)_3";.F9"'5"<ND(I.(D2Y5+XB-U8)Y@*C>D:A@JI
+M4]CLK#`W5E5[/##*DU2[!A9X5)5$[*K69ZIX4SW$Y`RA<)YA+5T@J,=C2J-=
+MYI5I<M72"V8(*R>M4\2-PB5<H50]LSA&X[$+(LJ^1.X6BUX>T=U;$E2@F#E-
+MQD)-&.4Q'ND4YC>4+R3]$VXG8)I;R@/)$(9XA,#`W3:H!$H@I/('LJO4/R8,
+M'MW5C5[_`'5;J1:&AP.?96)36GY.WPI`9)4-HX&EN4^J#`<2D*)YEVZ1SNY,
+M82;AI&Z!W;;H,3_FDXD&>4+B1F50G$`PA<(.^2FY]3D-0R\"9A$.YPVC/RJ-
+M\[4\-/W5RI@;Y6?=NU5P-XY3XB]3;_(!S]TB0#A$`?X<0<J!KH!!68E[/5?&
+MQ4;J^@`RFKNG?A5:ID:9D+H-%M]0T22)5.[ZU;T\%P^BR[NB]WY7D*A<=-UY
+M=4/W3<GQ-6_4W6>O"H=%&2?99++>K<O-2N70KK+"E2=,!Q]U,6^F-*>UR_Q-
+M2,'J]*FQS6!H3LLZ;J8AH4_5VS6;D8*F9I%,-VE+VDZ9=Q9-.Q`52M9N:8:Y
+MRVJNDC;E0N;JR)51CNIUV<DJ!U2LTD03[K:J#!("KOIM<"8V09;[N!F1"8W3
+M8,'"MW%!E1TD"57?9-),&$Z0-6HQP`+N.ZS*3VLZFP^_=6[FSJ`#2Y8M_3K4
+MKYICG<)>J8]NJK$%N^%"20W.RH-K5O*!T\*$WE0$M+<!2:*O/=+M(43H&)@#
+MW55E^#,H:]XS8X[JBTTG3N$\GN/NJU.O3+`0=T_G4^ZC3YZ-8L!#P<X"W>AN
+M:VBWM/)7-5W/\QI+0X.,#NMZPK^50:<^K@B/U7HG+AG/PZ?I]1KR`=S^BU6'
+M`$GZ+!Z56@@XSB5LVQ+@-UFLXK31G)4OJT@`?51")D'Z*3U&F`0LNAVL:\$N
+M"RKFBX7\TY<UJUFN#:3B<X5>PHE]$U';.*E(I/K-=Z9D]U&_\Y!,8[J2[H$5
+M"]@)X@*J'Q(,[[#9)R7<7;9N"=D;Y<PP@M]1$X@<*RP?R)0C#OV`%X))4-`@
+MTSDB%H=0IP"3&!A9MJ^:1)`P4B5<MWF=1,1PM6TN-+!!E8]"HUP,">P*FM*L
+M-`)6H1W71+@%C3JRNGZ;7D`2=UP'1K@!@`<NKZ;<-('JQW*U4=C0K&-P(5_I
+M7J.KLN>HUVB@!,:N5T/1V_\`I@[NN&<^&]KE=@(/[(NG.#'@2F+01NHZ/HJ@
+MY2<.K?H'4T$.2O6C3)W473ZC74\%3U1(D\)6XPNJ4SJE8E^P]ETO4J3BQ8=[
+M1,%PS"R5B7#GC=Q71^':Y\@#5^JYWJ3"!($?*T.AU7LIB7+<Y8Z=E;U3H`@*
+MQ3?Q*R+*L74A!_57Z#I$R5+&Y5T.,D`HV.D>RAIOYVA22`#.RC21CI.1A&3]
+ME#(#2`G:??;A%2B)WD]T61B?LHVD;''PI0#&%E35`",F0LZ_C6&@Y6C4.EA)
+M,?"S6M-6ZB9'=+T`KT/Y.DB5S/6;?2^=.W9=A480-IGE8O6:1F8GW4TE<W0J
+M:7Z286[TNJ"X96'>TRRIQ)W*L=)N0QX!=.5J<L7AV=J[4P#9$2'7+6JIT^XI
+MEDEPD[*>B[5=MC:4:CJNG@>0(_16@Z/8?*K63HH`@B5.YT`3"SIT@Y!'"37"
+M(.Z`NGLG<X"(0.3E9U[.H9(SPKU4R"!B%FW%2:H$B42KML?Y??W4X@"5!0D,
+M$%3[A2-!)$@`;)';;=(X$PD".<JAGF/3$K-ZN06F>RTA#L0L[K#0)D<=TB5E
+MT029&ZLV]32?4HNG`Y!;';*>NW2[5)3%*T:;Q(`.Z-L]BL^UK1)<-E=H5`6^
+MGE4V+3B?T0.W_P`U(_NHW$``P44B0-DP,`2GYG2?E"3/QV*B"+LQCZ*MU*#3
+M[J5[VXB(4%[4!I20K"HK7\ASGV1"=?S[J*T=Z20$;3)F$A4T3W*8$SC]4['"
+M"!PFD?EC!50GNRHW'NB>8X4;CE`_]/NHR9=(Y12W3N<(,.$Q]4`UC)A4:\&X
+M:-2NDR8./94;@`W(R$O2?6I3]5'_`#59PTO+295B@[TCD?917C3(.RG255KG
+M.ZK//T4UP02JM4B,GZ+:!JNE0U?R[?52$MX*BJ?EW05G_G3.YX1N#2<2FTG3
+MC[(C$ZP'><V))]U.S\C0[]$'6F36:[B<J4-'EB"E[2=(JS0.,%0_E=$?JK-1
+MH.)^JKU&@'4(A5$-4G6.Q05(R/V4E9DG&%$6DSW')05:V'C,IO3!AN2BKM^Q
+M0-CR\?4HB"N1.DC[+'ZV--9KNQ6PYLS[;+*ZX/3.K961%NW>U]%IC"CN:3)P
+M(!3=+<'6HD9/NBJ%PQJ6<>BH/(I%OY<_94[NU8X%TP>5>&3O!5>Z$-R95TLJ
+MJRU>&@-=A/\`PU3NK--P#``#]T^O_I6--OFRL\%U,F"),9D+2MJWE46%L25D
+M6]1QN00=4;85ZFXEY$@C<$8XRO5'/*.IZ74U071@\%=%T^JT;F5R/2JCF@`.
+M#F]PNAZ;6AVDY@;I7GG%;K2"V8B=BI'.@1S\JO0<2R(V"L`$MC$!8KK*K=2>
+M!1#`8<]7+-I9;@"1`*HN:;B_V]-,85^K+*9C'UW655(EQU?94.I4"'!S)$[K
+M3:T:9,2H7L\QV<II5.QKC4&ND'NKQ+0V=7YN52J4OYA#9!.93N<^G3:UYD=E
+M>V9P+J30ZF0YQ(CE8K612).-6(6W6+:EE+I$X*K.MVFF(:#"EFE[9MNXZH8V
+M#/)4VKR\.,_W356.%:`2)454D3[;95EVS9IL=)NF@`.)$''NNIZ1=%S!GE<!
+M85'-J`N,2<KI>F78:WTSGB5M([SIU<5+RE3F<_"[OIL-MPWO[KS+P54-QU$'
+MM&?NO2*#]+!G"XY<Y).UZFZ'$<_*&I$DSLH&U1JW4Q=J:3W2QVE6^E5G!Y#X
+M@+8IN!:N>MW0_!P%J6U:&Y,(W!7K)D1A9%]2(:0MUYUTYD!9G4&3(V^%FM.8
+MZC3_`)>)^JALJA#8$"%I=3H^@[8652@2!NK&*W^F7&T\\%;%G5U-B8"Y2WJN
+MIQN?<K8Z9<2(U#/NJDX;['[05,P@O$N,K/IOQCE6J+YRI8Z1;VA.P'^K*C![
+MYA3!T#&5+%@Y(_+*-I.2H@1DDR4GU-(.>%-+LUV^!'*"S9$N)RH*8\^MC(5Y
+MF`!P%+VLZ"Z<D<+/OJ)()B3[K1=(!Q\*L]FMI/"5'*]6H@2<>ZRV-#:@>.%T
+MW6;?4TB..%SMRPM?&PY4C-;_`$DEU$96G8!PO!/"Y[H%<L<&D@Y6[0K`74RM
+M:25V5J0&C/ZJ9SAI$K)MKN&`<*2I>0T;J.D:0?'^92U0-RL]MWJ$DS\)VW8`
+M[&47:]4?Z2>2%EUG?SQ.<J5]T`WU$?(6;4NVFY'J/Q*7I/K?H.EC<1W"E8X$
+M0-EG6UTT4]U-_%-VF%--;6R1`$?=)A$;[<*I_$9P04[:XTZIB.%=&UI[L@RJ
+M'5WRTP),*=UPSO)^50ZC4#FELB$D2U7LWB8Y*FJ-!!)&5#T\DY@*9S3D!T<J
+M155P(,S,%6[.K``,8455ON3[J%C]#@"K$:I,B1N@.(YRHJ521DJ02=R%*'J.
+MQ(,A1N,B>41`!.<>Z!T"2,2@!Y'!*@O'>C'&V5,\S_HH+LS2.\K42H+)X(R=
+MU8:9)AQSQPJ=G!,?NK3(F`T!2%2?TX3SP<2HW.(P-D[7%Q"H*3![*.I,P70B
+M).GM"#5ZT"QHW2,`1.R=WY=Q@(()!,R50!B2`250N#INACGNK9_,8(RJ-VXF
+MZ"EZ3ZTZ>6M]MD;B'TR"H6$!@/9%4=Z-RFMI5*X&DQ./=5JK3J@*W7`)SQNJ
+MS\XP4B17((.(0UL#<%2EHE1U&@B()(5$%)L@DC)3$>HC92AIG/V2+1N$1B];
+M!!F$5*'4`8"DZXTFB2,$#=5.F/#K>!CV5O;,$]PU>W)4=1@=&DX2=.J"(^$X
+M<-,;=E16KC)!W"AJ/AT$*6X/K.256J&0<Y50-8`D\*%H(YW4DRW<X4=9TNW@
+MC]5!#7(GB2L_K-+5;DM(VW5]SM39=NJUZ"ZV(/8K4[9O2ET5Y-H<G!4U:3_F
+MJG0W0]["=NY5JMB=2QCQPN78=+-BH+@SS@IV$Z\R@NL'2)GNK2!&D!/+5$TC
+M2,IY'=&]OF*PN'&H(VQ!E:UM5)=!B3@`KF[)W\SU8C8+8HU&G3!D`X*ZXW:9
+MQO\`37D/'OL/==)TZJ&!LC)W7*].<'56`20WE=!2>W0'$B=ENO-DZFTJ-=3W
+M`]E9?5TT"XC99O3Z@-,,]E)6>^K=T[<'$R5BS2XU>Z73_D:_5+E9KSH`.=\I
+M42&-T[`;IJH:XQE9TZ2A8!`!(PHJI]4@XYA2M`CDJ!Q]L<IH05/34QWR"BO@
+MVI0@-SNG?3#G2/U4=8PTMDQ""@ZMH:V@Z9)RKH>S6T2J-)@KWT$0&^ZFNFFE
+M6D"8@)WVR*]IM\T;*G=LTLD.$*[2+'TSJ'JW454!U,YSP5-::WMF$`D:'1W*
+MT^GW/I`:<=E1=1)!(`QRH:%7RFNP1GG8K4K%>G_A:\N]7_5]MUZ'YS=($PO+
+M?PGN`ZDYWO/[KNZEP=8.K[K&N7/&\MNA5EX$A7J#@6[_`%7/V-?.#]5KV521
+M\K5CO*N#TO!!*TK3348%F_F9A6.G57!\3CW6-?&]KSWN83,PH*SFU,X'=7G-
+M#V9"I7=+2/3LLUME=69%-T9`"Q6LF1L#WX6QU1Y%-P&)"SZ--II&3]D1")D@
+M&85JPK>7'=0-I%K^W*3AI=^:0KM'16MQJ8,G=7Z%3T8."N8L:Y;`U+9MJXC!
+MW5TLK8I/)&\_*F:Z,`X6?0J3_4K-*H8_-NHU*M:]B0J]W6]6D<IZM6&2J=`^
+M;6F<=E+=*T;*GIIB-^RL,&J)_=04G`"(RIFGTRI(HG-Q"C+8$QNCU"-TU0X`
+M[;(*%]2;I(.,+G.I6VDN&.ZZ>Y!..5D=3I&/RY42L2U(I58SA;W07BO<YSE8
+M56F6O[+:\&MFM(Y.ZK.G94:#-&R8T6]P5)1EH&?HD3G*CH`4&`P$G6LGF=U*
+M#&V2B=4,Q":%6M;PTP1`6;Y`_BP,F5LW+H9DY6?2<UUP$LX/JTRW@0W"-EL0
+M/4284K'8V1M,M@<*:Y57-)W'*847F<JV8W'=.".0J,^I1J#+3$*CU`O:"'2M
+MUQ]N%D]9@`DI$JK852&",JU_$M<<E5;-@T?*G--KR3D$I-B5M1N<X05=.X,R
+MHGL<T8G"!^L8""S2?I<`7`JVUP,$+*%2,D*S:UQB"G8OG2=RHJF"8,@H@X$"
+M#&-BHGX.#A`G$D08^JK7?Y#LIPX1NH:PP03NJ52LGCS")&.95L$`$%4*9T79
+M$B.ZMN</E(B0$3(.W=$"<F84(<)PI`09<1A4$T]T!,&?V1C#2-X4-4[CA0$7
+M%PPEO.$&J(A.W:`51'5Q*S;@D7329Q[+0J&'8_19UX?YX,0EZ1I4ZD,!A#<5
+M`QFZ&D\>4!^JBNAKIQRD2HVU=>=_JF/'<(;1GELR[/[J0C,I.C2*K@J,>H'(
+M"DJ\XCV40D"`(E4"X0XC.?JFJMC:8'=%$$H71O\`NB,_J@_E.!Y"Q^FU-+W-
+M@A;G4&!U,SD$+G+8Z.H$#<[JWIF=K]1LF<D]]U&\;QA3U`-*KN=N-R@@K@@C
+M]E5?CZJW7,;'(5-T$Y)PJ@7G8C`*"KR!(CNCJNTM`DPH7DO$DR0H(*IGC/=`
+M^#3/J@_*58;EI48.X=&1N%48E(MI=6+7.Y&/HM&JX3!V*S>I?R^JM>"`!RKE
+M9XJ-#@GVK>9"<8.3,(:KMYW"![W:#&Z!SG%GJ;!0@J8]`X3Q[H&5'!H&H?9/
+MYKO\0^RSPV^3`XT[@M(TP>2M*RJ!SA)&>52\04G6_57B``?=*TJG!C=:\>6X
+MZ>3%U73GAQ$'!,;K>L7:G""<87*=%KM:R9]<X706=RXU?-<07'?W7H[>/.<N
+MJZ97#1)XY6ET?34J/N"!DP(7,6%V_4*;1+G&,+JK!OE6[&`#(6,HQCVOM,D.
+MF$1F8*C;M,_1$VH($@D^RRZGJ8;@084+6@B2#[*2HYNF-C[J-KB[`B`H'8T!
+MWPHZU/T&<%2T"T3(_1-7V)SLFC;$M2?^(OC$=E=<[U$.$CNH+>F75GU=,294
+MCVN!![I.D#4ID-<YFT*NRH1NZ1[X6@W%#D^RJW5`-9J`(^BBJ]5H<):)![*I
+M>TM%)SHB1"TK<@M@`PFZC3#K5Y9G'"6Z339_"2MIH.&6GG]5VG\9J>?5/NN`
+M_#:6TCP5T3KDMNBV2IC=UQDYKJ>G7/JP2NCZ?5!I#,%<%TR['FAL[KK.DW&H
+M`+I8Z8UTMLZ`#RI@[34!&%1M:LQE7(!9@K%=8V;)X?2&4=5@.ZS>FU8=!,1P
+M5HNJ!M,PX+-;E87B"C+]`&5GFDZDP!TB.RV:X%>[WD!1WULTL,#(V)6-ZJZ8
+MU,MU[D^Y0W+9.,#N%+6I.IU%'4>-B<JHKTWEKA'ZK1LZX']1*RW`%^(^5/1>
+M0))B.RJ.AMJH+!ZE>H50(Y7.V=?.ZTZ%Q#-6T(LJ]>5Q.D&"5/T]NFG/<K)M
+MZHK7.^!RM>DX!D8@<RLWMJ+32`8V4S2(!!WX55CP#ONIJ3\YR/=54[7@C)RF
+MJ$`'8!#,F>>R3AJ.5`#H<W&96??4YY6D_`@!4[EH<TD[I1@WM(!T*[X/?IKG
+MY0W;!!D!'X7HD5R1W4G:.N95$9V(3FJ"0(5=H=M*(`$JM18UB,%(U"23(5=L
+MZ=TVH\<()+I\M(F`%0MW.-S@QE2W+SIG95+*3=:IE+T3MNL=#1'*?S.)5=A@
+M!$)F2@L:\&$XJ'`4'P$XE*HR\3@DA9G6'2KQ$YF%F=7B8!1*:P<?+A3:HB8P
+MJMD3HC]U*7>HSL/=2+5B01\I.`)[J)K]N$X=ZXVE4#4I`@Z5&T%IV*L.<2-T
+M'YO=30=M=^,?*=U8Z<@X2`;&=RC+&'$A!`;G,1]E&^X&))RI32;MA15*(&QE
+M7E%&YJQ<"(A6VO#VZEG=29IJ!P,0C8][:8$E/J-!CNY4S"2/[=UF,N8)#E:H
+MUPX`2J+<^D]U#5._!"<5!W4=4C:440=.Z?4)WV46J!$I!W(1!/P(&RS+V/-!
+M.RT:A=!!/T6=U&=8DCX3XE7J('E`C8>Z"H01&?NAMG?R(0ETG*3I:>(9)2ID
+M%N9"$N@1O\IFD:>RJ(ZL%Q`.$+\#L$3_`,RB>X]P40HR$U68PG),25$Z9,_<
+M((+PM\LCV7,51IZD-]UTET'%IPN:ZK++L.`"U>F?K5>6Z!C?NJ=4EM0S'PIJ
+M;II!P,JM>._F$@_5(@*I,F1NJQ$O#I/NCJ.)`,('.,1C*(:H6G$?JJ\Z26G8
+MJ6KC(,_"KU<9B?JFBH*NVD?=0L=O.$=PXD_YJN7DNW,!5&5UP@W#3SW5FG/E
+M`\_NJ?6S-P#LK5)W\EI:F7]B='.7GE)SF^7`@>RCJ.=(+HREK+A)<-ONI5A`
+MXPV0GD_X/T4)J'M/NF\P_P"%&]/G7\3K1U#J!JMD-/8KG+*J69:8E>A_B=:"
+MI0D4B33;F=I7FK*FG^61)#MYX7+"ZNG:?RQE;G3*HD#_`&%T?3JQ+6F3(W7)
+M=+K%WHG![E;_`$JX`IZ&G;D+UX7<T\_DCH>CU75>J-$X;P%VG3G.=I+CMC=<
+M'X8=_P"L<^",KL^F58TD#;NK7GO%;1(:R2!E%2;B8^O"AI.+QJ+I4K"XG$Y7
+M-T!4R3RFEL<[(JC2#O\`W4%0%KMP<HM6;4&#,`>R#J)T6QAVZ>D\Z(U3*@O'
+M:[BFP3[I4/2`;;-$"2)4-;43'?@*^YLB(&.RK5&Q6$;J=*B$M#02E=-'EF#&
+M%-4_F;B"%7N\-W)0X-94&NIG.4UV"V@\$3(X*DZ406D@G>([*W<40^V?,S&R
+MF71`^!7<D`;A7.O56T[X`'/RH/"-'RG2YI'9+Q93+*WF2N6-U8Y2?RJSTZZA
+M[7$C_-=?T"]&D9_5>:V]R0X=@NC\/7\.:"9GZ+TSE7IW3J^K25L4'@LCNN3Z
+M%<A])OJ"Z"SJC2#*QDWC6BTEKA&5.ZX.C1R52:^6$S$)K5X?=^HX!6-ND:ME
+M3:*>HSGNGN&G5Q$*6E&@:3'U2J@&9(E8UIT95U1#G$EJS+RA!."MVJP'_54+
+MQD-/)[*#`?-.MA&UVK\RN5K=KP21!Y5&LQS3`.`DK.DU-^G'NK#[K13^5G:_
+M?9-=51Y>J<^RW*E;O0JFHR5N,J;`F?A<GT"Y&@`GZRMVSKAS?[J:7;6ID!H,
+M$*Q3+0!*SJ3Q$R?E6:-2>?HHUM<8[U8^%)J(,JNUX.?V4DPV>Z*)[OJH*Y!,
+M']$;G=B@<&D&5!GWP]!PI?"P)J?7DJ'J`ACC)]D?AA^DR2D1T8P,Y'=$"-U&
+MRH"S@2A+@2(<C25[@,0@),GU0A<<ZB90%T$R=_=`-T_^61./=5.GNFYW$*2]
+M=_+F?N55Z:[^>2,Y5J3MNM.-T8=`_P!55UX&8]DFOG))459+S.\A)K^2?HH"
+M9P$.HPBK?F-R!CNLKJCLF"85DU#!S/LLSJ50_E,2JB2V>`TD291.?F05!;._
+MD[_5$TB?41)YV4@LM>0-YY1!\G=5R[:<)P[.=D$X?G=&Q\#C*KL>"-]D[70X
+M9^J;%EQ.-DB\]U"7\&8*<.X!_54$YY&9E`7DNVW35'@;G*&0[Z>ZNT5.HG.4
+MJ8'D[8_9-U+4!N82M_\`D`[J7L+RVDX*3:3ID(FN]4(VNAW"I`/UM[X4=2X<
+MP9G/Z*V#+5%5I-.[=]U!"VY$_P":E;<-)`U0H*M$<*$TG;#A!H>:TM]+I5/J
+M)`$G"B:VJUQSA0=2-9M/97:5I6;@:`)=$(7D`K/Z;<5?X>'`X[(GW1+O4DUH
+MJ\'`P#PA<3&VRJLNFZO[(S781C'=42U'2V3NHAOLA\YFG\WU0"H)W.564Q/I
+MRHJFQC"=U1L;J-[@6SJPFC:O=.,?(6!UO)+APMN]<TM*Q^K4]5J\YF%J,4/3
+MWZK82?LFN8`R0JW17_RBPG93W#?5CA9QZ6J[I`)[*%SR02IZ^&D[#LJCC)C[
+MK2#+@84%PYLD#[J7&DE4[N0=0VY0VCJ/F<3\JJ]Y#_S`2I7N)$@J"L?0851G
+M==)!#I,*2S?JM!_4>ZK=7ES/92=/(;:-!S\E3+N+.DI+6M)(CA"8%,$'"5:"
+MTXSPAI@^5I!!]U%@FM!;)A/H'M]T+#Z1A%/LHV\H\54&UZ%9KID-/RO)NK4'
+M4.I50T`M!R9VRO;.I4P]CG')CO@+R?Q[9NI]0>Z<3,1[KGGQE*O@O<9EKEH+
+M0=.X^%K=-K@4M((_NL*SJ/IO:`>^3PM2E6UR74Q(WC"[X7ZN4VZGPW6+*+<Y
+M)[KLNF5I#(F8_J*\]Z3<:'TFC`,;KL^A5?,8S(GWX7;6X\?DFJZJU?-,"02K
+M=-SM,`"/=9=C5@ADM='97J+IP=6T^RYV+C4U0GYA5ZF7C?\`S1N>[41OV3&2
+M3P2HT3JFEA."!N`HNG@5*KJAG?"#J-0TK8GDX1],+V6K=49[IVB_JVF=E%4$
+MG494EN\$?/":K),D;84L:B*W'J)WE07;7:S&/W5JF"!,;SRJU1NMSL\[H([3
+M4PYG)VA7GUO_`$;@,8YG"JZ((+)!XRGZA4<VQ?\`:5/B-3PPWT!\R2=T_C$.
+MQ!/J$84_A%O_`/3Z;].$WB!HJ5MH"XR<L8=N-+G,K$;*]TF]\NJV<0=Y4O5+
+M3^67,&0=UDO+J+@3E=\,ERCU#POU!II,SE=ATZNU[&Y7D/A;J1#V`E>A]!O=
+M=-I!6\HDKJC6#:;CJW]U'9U7&[!`QW6>^Y\RJVDV<K6LJ>B@)B>5RRNN(ZX_
+MEMVM<&G'>%8W&3/NLBUJZ7`$C_):-!VHCU+-CI*-S1I,`RJ=VQI?[_"O'/[*
+M"HT203)E8L:9=>FT$B"J=>CC;'*U+ML$@`RJU>GB2D1D7%N#)!R?T5&^8YM*
+M"86U780TJA?M#J#I'"L2JG3*^FGI!S^ZV>G78TCDA8%M3(!(5BA6-+>!E:E9
+MKK+6X!;DQ[*];U/2,Q*YCI]WL)_5:MI<:B#@_5+&I6W3J>H0<?*G;4`],E9M
+MM5'W4X?MWA9K6UPNU")W2<!I&?HH`_;(E23C915+JIBBXCA%X<8#!2ZEFFX1
+MB,INA/%,B-I4G:5MP(&3[HVPJXJ-/RB#C,3*TJ8]^/E!4,'>2A)!Q@%#4)V.
+M%-"O>F1OLJ_3?^;GNBOG>@J'I9]9DR9PK?B1L:A&_P`)M>(*B:XS&(A,>ZBI
+MVU1.Z(O:3,CO"JD'![^Z3@[28)E-B<OW,8Y63U:L-?I/*MR[;?V65U)SM>!R
+MK\%RS>?+!"F+O>51M"?*!,A2TZDNW*S%6'.)_IPEN,&"HW.!.1A#J'?'""P-
+M38!VB<J2F1_LJOYFWV3M.,F/JD-K#G.(QPD''<E1%_I@<)PZ!))5B)9D&4PD
+M'>9]U%YF-T@Z>51%U%WH*&S(\D9^B;J+OY1SCY4=B[^5OE*BP9!D)&0Z5&YW
+M\R9^B?428)Y06&ET`IY)89CWRHV.QI"9S^$#5=\)##8[H7G.#$IJ;G28*"5D
+M$[1]5%?-#J9D3[2B:XYXY0ULTR"58*E@QLN`:BK46.>3&>RCM'1=.!.)4]0R
+M3"D*K/H>J02F=0<6G=3EQ`"?4-)Y*ND4'4G`8)4(-459E7RX:C.Q*!S0'3CZ
+MHBD]]4&0#!*%U:H&Q!5\M#C^7Y05J8#<-'U5FRZ8M]<U!@@A4KJX+Z#A)V6Q
+M=TV$F0,]@J-S;,-)PTC/NM[K%8'3KK1=/;)WV)5\W;2(.ZSS;:>J1."5:K63
+MB-33"F]6GP5Q7'ED[PJ8JM+Y!W2K6]700"9A9U5M:F2-/RJC4+\&#]%!<P6D
+M@*@VXK,;$%*I?#\IY5@-[@UWJ^%#7<TCG&RB=<MU;IA<,=Z79^$T*76@TT-1
+M'R5'TFI_Z8MG"GZH6NMW1)"K=$$-<X1"F7PGU:.XSNG)]#C(PGU-=(+=D#IX
+M&#PI5AFL)$A/Y9]TFS&/W3Y_V5G<=7!7+8HF1@B=]EY_^(MJUU.H]HR.>Y7H
+M5=S2")XW7,^*K8U*%0;R)SPKY)N.'COKE'E#7Z*A:!B<%:%FX/8T["9A4^JT
+MO(O:E,-(@]]U-TYWY6C`:5,+N/7G&S1=H\J3G<05U7ARZ#FX<&D#OGA<E0@,
+M9]EK]*<:=1LM(;NO3A?CS>3'<>@]-JM--H[[S\+3M*Y+1,&-ES72;IKVL`)'
+MN5MT*LZ<[*6:>>72^QS#4DD[X4FN2!G[JJTOB8GLI2X8<=Q@SA8TZ2JW5'M?
+M=MI-<7'!(4U`C0&;`=E7HM-;J#JN(;C]58JC0>T_HH)Z=8!P`/U[JVW2ZGD;
+M+,HN+KAH)P5ITX-(-[CE6K#`'1@G_)02!4(GE6)`:0)PJE4^H_=1=I6YR3[R
+MHNJ`&R?C=34W32$MVPJ?6ZFBV&PEP^J6);PZ3PSZ.D4LQ#0._P!$_6&R0^9`
+MX1=!+?\`@E$[2T)^I#52U8@'"XSMQPO+.K-8^CI`XA<_URSECM.^\+HJ;'`C
+MW5>_HM-%T@+=_+O'+=(N'6U=K3JP<P5WWAOJ8\IIU;#NO/\`K5%UO<:Q@:E<
+MZ/U(TF-!='$CNNV%]HYY<5['X;J.KU14)!$KIJ-3,`_JN#\"WH-FPATF%UMK
+M5!`,Y.5BQO&_&I)#ICZA7;&K.'$`K+IO);$J:D_R^=NRPZ;;FK`G>$P@YG*J
+M6U<.&2(4X>"!$9]UG3<H*S=\!5JS);&`5<J9!RH7,$&=PLJSJ[0`=4+-ZCB@
+M[$>ZV;FFPZ@=BLKJ[!Y+@WZ*Q*SK.F8U`XF)1U*32XQ"EZ?2/D`]T=>G#HXX
+M1&<7NI'\V!Q"N6=X6ELF9S!45:D2Z`,E5JE/2[&",K4J5U%E=A])L'?]%HV]
+M66@3NN+M+MU&J`[8+>Z;?->&RX!+$E=`Q_$J859:/?A9M*J'`:73/96:3IS.
+MRPZ'OS-%P!E'TADT_A17#AY)4W29%(P0I!?;@'V1-=GE1S]$3=.)("JI(,S^
+MZ%QW#M]DY+8WW4;S`B1\H*G47``CE1],`UDCOPBZ@X!AR%%TQ_/<\*4:E-V)
+MTHO4<[*)CL"%(TXGNFC9.<`(Q/=$'PTB5&Z9G&$%28[JB34P-=.>ZR>JN:*G
+M;*OU'::1XX63U1\U0`('LH59MG#R0)&`DYWJWP5#1GRP,YY3N,.P3ME)2IQ4
+M'!^B37^M0%VT)B\@S@#A!:+AW3L><*&0<1N$F'2X23'RE%L.YF>Z;4`2?[JN
+M7^DQ*(/P)@H)=?`!^4=-X+<[RH`Y)K@,JANHP*9SB%'8QY>#LFZ@_P#DD$'Y
+M4=@[^6=]E:D67:B<NWPDTG5_=1ZCPGU'G=!8:Z`F+LG"A:21O"1=F.$$CC.[
+MH3<^ZCJ.Q]$U-V!`03TW2<(JQ;H,Y(P%`UWJE&[;O[*Q%`NB]B8DJVXR/251
+MNSINFD#G[JW3/\H.D!3Z?#N,L@1"83VR$!C48*1/JW6@-3#MDP,;Y0UIR2F9
+M')W[HB1Q`9@E0UZA((RC<<?N57>9G,951#6$_P!U4<!K<2..%:J`Y!.%#5RZ
+M08[JHP.JM\J^%08RKU-VN@UT3(W4/B.G_*\S<A-85&NLFP<A6][2='N)`('*
+MS;EDF3WV6E7/HQ]U0NYTG)A5*J&DPDM(F2H:UI3,X&5*\Z3@F9Y3N(?3+MHP
+MIH95U8AQ);QV51]K5:202#)@K8>88=,QRJ]1WJQVV5T;8M\VNRA4D$X5'I=W
+M4;4<#N"8"Z&[:W09SC*Q:%)C>H/$R'$J7>EG:9MV7&2WY4E&Z:1$[)A;L\QQ
+M@9PDVT&J1O\`NFUFEFFYI9.I%+?\85=ML^-T_P##/[K&W3U>?U'.IO`/T69>
+MO\T/#I@<+7T4WTGM>\-<!(QNLKJ5!\%P@#OV73_*\TT\X\;T!2Z@^IOJS[[K
+M*LVP!B#J6_XX:Y];.=(R>%S=*J]M00/RY/NN&/%T]TN\8WK<^EI<TNB%K6+V
+MN+3!(`[K"LJKG-`.0=BM;I]0Z@20(A>G&N&3I>FO+;A@:YP&"(."NBMKJ:@;
+M+=+<8*YKHU:=,P=(6E;^:UX>X`P9PMWAYLG46Q,B<M.1[)7]8,ID1I.V%1L;
+M@AK9,\917-8UKAK6F8=)@%8L)?BYTR6TA+8<[)5@@NR<SQV5:A5DP9AN%8ID
+MEATX6=-2HJ;=-WJ/!6C1,CZ+.>?YD.:<G>5=M7%D#@H;'6UBF2=O90522PQO
+M&P5BK4EL$*O6)R-(AR+L]&12C:5E^(7PRF`[=X!$;Y6C.BC)9!]C,K)O#YE7
+M46R`X1/=3+IFV.SZ"7'H]$$02!CA7*P+J7T5;H'_`.44]P(5UP::6VZY3MSP
+MJC5;#Q,#B945>D#D;=E<(!(&)35F12F1]%T=]N8Z[9BI,M&W*Y:Z;4MK@B3H
+M:X;KT"ZH!U-Q+<KENOV;2UX#2!*DOK5LE=/X#ZF&T:;=63&)7HG2[EKZ33(S
+M[KPSP[U#^'NPR'-CNO4_"M^*MNPZAMRNV4W'*<73MK>J(`!D[X4XJ$C$A9-K
+M7Q\8PKC*DLP9D+C765?H7!:[2=CRK]O5&D#NL4/`$SE6K.YAP`4:VU]6(E-N
+M#PH*+PX27$`YW4@>"["S6Y456F223A9G5VC0["U:SN%E]0<'5`SDE016E/3;
+M"!&.Z&H-1DC?=6PS32;.T0HG!H<?\TA5"HT:H`V]U7K,W)P>RT*K?48C?ZJ*
+MM3EL@_,J[33*?3.K41"*G6=3J`M5FK3(SLJSQ`WRK*EC9Z;?D:6..8W6S;7;
+M2!#IGW7&4ZFDB)'LK]M=EH&8@)4G#I;JN#2)D#ZJWTJN/(&09]UR]3J`>UK-
+M6^`MSI&;=N8E2\++MK^=VVV1LK>KC*H.<X")QV0FJ01+I4;:1JC$;]D+JA._
+M[J@;@-<&NJ-DG`E$:GJW'S*`>IU/1NAZ62*8,[E5NIU"6&"$73'N=2:`=^4J
+M1K4ZO!/ZHV5H'YC$JD"<P90&JX$RD%\5B[,H75M+M_NJ3*CBZ)PE5<X@Y&%5
+M7*E8%D;K'ZC7!J1/*F=4=P5FWM7^<)G!V4O2?6I0J#R1C]4G%N_'*K47#R@0
+M>)"/6",G=1I*7[ANZ1=&5`7PW!A(O^I518;5)[(M6TE56N``(")M01ML@N!P
+M@0=TXRV9"K!\B$8?!03AS28!D]D;2.3]%5UQSGV1!Y@[_P":H74'?RS$=H45
+MBXMIP"`=U'?/EARFLG2W$B$OPBTY\&-^4P=F4$CG?N@U0X[0B++7$'Y2>[B5
+M`U_<)R\[$G"HDJ.(;,IZ;QIB,PH''N8RE3)F)*@G#R3'',*<&6B`53U28W^B
+ME;4<&C*HK=5!:=4;*6SJAUL#]"HNI^JD>?[*'IK_`$P3LE2+-5^<?,RF:<@G
+M=!4(U'GV3-B-U055Q+C!PA!`;G=*<!,XB<G*(,/.G;<('@<G=(.`$S,>ZAJN
+M$$QE6%!4_.<X]U7J.;J)DQSE'4)+]XGA152V8G9:95.K--6T<`($8[E972GQ
+MKIG:=I6U<^J@0(_NL"B2SJ+VD[DI>CZOUI`(!PJ542\R(5QS<;Y*IW1C$F=E
+M652NS\QF>5%2=#H.WRK#SCW5:!)SLBCK-;HB9"INC.^^59>3IWSL0H:[=+"1
+M^Z(K5(/YLK)NJIHWIIZ1I>9)T@G[Q*UZPEL3GV67UNBXT!4$XSNEG&EG%3R"
+MT%G92VY)!(WXA4[.MKMF'^IHC=3VKH):5)>%URL!S>93ZF^ZCAO8E*&]BG#;
+MSU].##9PH:E%AR>-YQ*NG<J/AWPFWGLURX7Q'T]MS6N',88:#.%PUQ;BG7<U
+MP.Y^B]-J?^WN_P#\2\_ZO_[I_P`KCEQD]OBN\0].AKH)P1NM6T(UM:`2%E6W
+MYA\K4L_R!=I4LVZ3HWDMI:P7:B(&<#Y6[TP4_+&LDEQX,RL#I7Y`NCZ?_P`T
+M?"Z6O)FN>7%+6#('TE0=*+_.?4<##R0`<PKM/_V[_A5+7_V_W6-\Z9G3086B
+M7%P@[@*]9AKFB(G>5E'\K?A:5CO]%?B]4]=O\V3NK%$0S)GLJ];=6:/Y6_19
+MC6^3U`00.Z3@=6G.VZ=WYDF_\QORFC?*&^;IHR"1'M*RJM)PI"I/YGXE:W5/
+M^0LZO_[+_P#&%G*_$RX==T)I;TFG)G&5=R:>TJGT7_\`+6?`_966;!8P[KEC
+M"PT^Y4PIZJ$]U!5W5NG_`,KZ+HZJ5:B13.096%U2U+R8`RNDK?\`*/RLFZV*
+MS>G2.#ZO;.MK[S`3`,KJO!?5X;387Q[2L+Q9_P"X8@\'?\_ZKKX[PQGC]>Q=
+M&O&U*;3KS&RV:%0:1ZOU7(^'/RM736WY&K.4Y7%?U%WI_9)KG-=$_5#;[!)_
+MY'+G6XT+2O,`NV]U=8_TR#ORL:WV/PM*A_R6I5B6I4'EJA3FK>DQLK5S_P`L
+MJ/IGYS\KG;J-3E.6C1![1"@>V1&_NK+]C\J`_F5-*[VD/.,;[H'@.:0)^B.X
+M_*F=_P`GZ(:5*U,M!Q/RJE5AR5H5/RGZ*H[^KZIM*I5&&<843JKV'NK=;\BI
+MW'YEK:6!97?_`!5/W*[/I%QIM&#&RX1O_O&?*Z[IO_MF_16I.&UYX+2F+F$`
+MJK3_`.2/E)OY?J5C36UN*<C(U)Y^L*&G^9ORB'YOJ4L:5;]I<TY.^REZ;(IY
+M,*.[Y16FP^%/HO!TXGZI1G*C9^<IV[JU(,``R("8D:D)V1'<?"BHZ\"7+%O7
+M`W7"V7_E6#=?^^<K>D^K]*I#!&Z/63_4HJ?_`"_HC;^8_`5THR\8@)]<[2@&
+M[OA*EL4T"-4#&X"0>.^_91'_`)A3MW;\J"PUY`B81!Y.02H'?\M'0V^B"5KB
+M#*E;4QO!]E`/S?1,W<J]ANH/&G=/TXQ3,S]5!>?F^BDZ?^1+\2+#R4QC5,IG
+M[_5#4_YGV5@<.`,GZ(I,@]U$/^8%,>/HJD,YQWDIVNS$H*WY/JDS\P45('>K
+M!PI`YI&V-L*!OYDXW*J&OS%$MD0JG3':JQ$JS=_^V<J?2_\`W04I%UY_F8(3
+M.=&1V2J_G*&K_P`L*@P_T=IY0.B<F)3-_($]7=JJ%,"%%5<'-@2"B?\`D4#O
+MS'X50%0D=_E!4=+4]3\H^4Q_*541.,MX*P.I`T>HM?L"<RM]NRP_$G_-^H5^
+M)]6`Z6`MSB5%<`NSS"*V_P#;,^$U;=2=(IOB8^JJ/<6U<#G=6G_\PJI=_F"T
+MARYK21.ZA<09`4K?Z5'<_D*;57J-QMD=U!<L\R@ZF>5/PAY*#%LIIWCJ)(`)
+I("O4!#X(DJA<_P#YE_\`B6G2_P">U9ZNFXE:#I_,$^EW^,*4;)(T_]EK
+`
+end
+
+
diff --git a/rt/lib/t/data/rt-send-cc b/rt/lib/t/data/rt-send-cc
new file mode 100644
index 0000000..da8c4da
--- /dev/null
+++ b/rt/lib/t/data/rt-send-cc
@@ -0,0 +1,5 @@
+From: rt@example.com
+subject: testing send-cc headers
+RT-Send-Cc: this-is-a-sample-test1e@example.com, second-this-is-a-sample-test2@example.com, test-sample-sample-sample-test3@example.com,
+ afourthtest4@example.com,
+ test5@example.com
diff --git a/rt/lib/t/data/russian-subject-no-content-type b/rt/lib/t/data/russian-subject-no-content-type
new file mode 100644
index 0000000..03d95b8
--- /dev/null
+++ b/rt/lib/t/data/russian-subject-no-content-type
@@ -0,0 +1,42 @@
+Return-Path: <mitya@fling-wing.example.com>
+X-Real-To: <mitya@second.example.com>
+Received: from [194.87.5.31] (HELO sinbin.example.com)
+ by cgp.second.example.com (CommuniGate Pro SMTP 4.0.5/D)
+ with ESMTP-TLS id 69661026 for mitya@second.example.com; Wed, 18 Jun 2003 11:14:49 +0400
+Received: (from daemon@localhost)
+ by sinbin.example.com (8.12.8/8.11.6) id h5I7EfOj096595
+ for mitya@second.example.com; Wed, 18 Jun 2003 11:14:41 +0400 (MSD)
+ (envelope-from mitya@fling-wing.example.com)
+Received: from example.com by sinbin.example.com with ESMTP id h5I7Ee8K096580;
+ (8.12.9/D) Wed, 18 Jun 2003 11:14:40 +0400 (MSD)
+X-Real-To: <mitya@second.example.com>
+Received: from [194.87.0.31] (HELO mail.example.com)
+ by example.com (CommuniGate Pro SMTP 4.1b7/D)
+ with ESMTP id 76217696 for mitya@example.com; Wed, 18 Jun 2003 11:14:40 +0400
+Received: by mail.example.com (CommuniGate Pro PIPE 4.1b7/D)
+ with PIPE id 63920083; Wed, 18 Jun 2003 11:14:40 +0400
+Received: from [194.87.5.69] (HELO fling-wing.example.com)
+ by mail.example.com (CommuniGate Pro SMTP 4.1b7/D)
+ with ESMTP-TLS id 63920055 for mitya@example.com; Wed, 18 Jun 2003 11:14:38 +0400
+Received: from fling-wing.example.com (localhost [127.0.0.1])
+ by fling-wing.example.com (8.12.9/8.12.6) with ESMTP id h5I7Ec5R000153
+ for <mitya@example.com>; Wed, 18 Jun 2003 11:14:38 +0400 (MSD)
+ (envelope-from mitya@fling-wing.example.com)
+Received: (from mitya@localhost)
+ by fling-wing.example.com (8.12.9/8.12.6/Submit) id h5I7Ec0J000152
+ for mitya@example.com; Wed, 18 Jun 2003 11:14:38 +0400 (MSD)
+Date: Wed, 18 Jun 2003 11:14:38 +0400 (MSD)
+From: "Dmitry S. Sivachenko" <mitya@fling-wing.example.com>
+Message-Id: <200306180714.h5I7Ec0J000152@fling-wing.example.com>
+To: mitya@example.com
+Subject: ÔÅÓÔ ÔÅÓÔ
+X-Spam-Checker-Version: SpamAssassin 2.60-cvs-mail.demos (1.193-2003-06-13-exp)
+X-Spam-Level: +
+X-Spam-Status: No, hits=1.0 required=5.0 tests=SUBJ_ILLEGAL_CHARS autolearn=no
+ version=2.60-cvs-mail.demos
+X-Spam-Report: * SUBJ_ILLEGAL_CHARS 1.0 (Subject contains too many raw illegal characters)
+
+Content-Length: 6
+
+ôåóô
+
diff --git a/rt/lib/t/data/subject-with-folding-ws b/rt/lib/t/data/subject-with-folding-ws
new file mode 100644
index 0000000..c082632
--- /dev/null
+++ b/rt/lib/t/data/subject-with-folding-ws
@@ -0,0 +1,10 @@
+Subject: =?ISO-8859-1?Q?te?=
+ =?ISO-8859-1?Q?st?=
+Date: Mon, 02 Jun 2003 20:58:30 +0200
+To: rt@example.com
+From: foo@example.com
+Mime-Version: 1.0
+Content-Type: text/plain; charset="iso-8859-1"
+Content-Transfer-Encoding: 8bit
+
+test
diff --git a/rt/lib/t/data/text-html-in-russian b/rt/lib/t/data/text-html-in-russian
new file mode 100644
index 0000000..b965b1b
--- /dev/null
+++ b/rt/lib/t/data/text-html-in-russian
@@ -0,0 +1,87 @@
+From rickt@other-example.com Tue Jun 17 20:39:13 2003
+Return-Path: <rickt@other-example.com>
+X-Original-To: info
+Delivered-To: mitya@vh.example.com
+Received: from example.com (mx.example.com [194.87.0.32])
+ by vh.example.com (Postfix) with ESMTP id 8D77B16E6BD
+ for <info>; Tue, 17 Jun 2003 20:39:05 +0400 (MSD)
+Received: from hotline@example.com
+ by example.com (CommuniGate Pro GROUP 4.1b7/D)
+ with GROUP id 76033026; Tue, 17 Jun 2003 20:38:00 +0400
+Received: by example.com (CommuniGate Pro PIPE 4.1b7/D)
+ with PIPE id 76033052; Tue, 17 Jun 2003 20:38:00 +0400
+Received: from [217.132.49.75] (HELO compuserve.com)
+ by example.com (CommuniGate Pro SMTP 4.1b7/D)
+ with SMTP id 76032971 for info@example.com; Tue, 17 Jun 2003 20:37:41 +0400
+Date: Wed, 18 Jun 2003 01:41:01 +0000
+From: Ó÷åáíûé Öåíòð <rickt@other-example.com>
+Subject: Ïðèãëàøàåì ðóêîâîäèòåëÿ, íà÷àëüíèêîâ ïîäðàçäåëåíèé íà òðåíèíã YXLWLJ3LPT9UHuLyGTzyuKQc06eIZ96Y6RVTCZFt
+To: Info <info@example.com>
+References: <0ID97EGL951H1907@example.com>
+In-Reply-To: <0ID97EGL951H1907@example.com>
+Message-ID: <HDE46LIK8GGJJ72I@other-example.com>
+MIME-Version: 1.0
+Content-Type: text/html; charset=Windows-1251
+Content-Transfer-Encoding: 8bit
+X-Spam-Flag: YES
+X-Spam-Checker-Version: SpamAssassin 2.60-cvs-jumbo.demos (1.190-2003-06-01-exp)
+X-Spam-Level: ++++++++++++++
+X-Spam-Status: Yes, hits=14.9 required=5.0 tests=BAYES_99,DATE_IN_FUTURE_06_12
+ FROM_ILLEGAL_CHARS,HTML_10_20,HTML_FONTCOLOR_UNKNOWN,HTML_FONT_BIG
+ MIME_HTML_ONLY,RCVD_IN_NJABL,SUBJ_HAS_SPACES,SUBJ_HAS_UNIQ_ID
+ SUBJ_ILLEGAL_CHARS autolearn=no version=2.60-cvs-jumbo.demos
+X-Spam-Report: 14.9 points, 5.0 required;
+ * 2.3 -- Subject contains lots of white space
+ * 1.0 -- BODY: HTML font color is unknown to us
+ * 0.3 -- BODY: FONT Size +2 and up or 3 and up
+ [score: 1.0000]
+ * 2.8 -- BODY: Bayesian classifier spam probability is 99 to 100%
+ * 1.0 -- BODY: Message is 10% to 20% HTML
+ * 1.0 -- From contains too many raw illegal characters
+ * 1.0 -- Subject contains a unique ID
+ * 1.0 -- Subject contains too many raw illegal characters
+ * 1.2 -- Date: is 6 to 12 hours after Received: date
+ [217.132.49.75 listed in dnsbl.njabl.org]
+ * 1.2 -- RBL: Received via a relay in dnsbl.njabl.org
+ * 2.0 -- Message only has text/html MIME parts
+Status: RO
+Content-Length: 2743
+Lines: 36
+
+<html><body><basefont face="times new roman, times, serif" size="2">
+<center>Ó÷eáíûé Öeíòp "ÊÀÄÐÛ ÄÅËÎÂÎÃÎ ÌÈÐÀ" ïpèãëaøaeò ía òpeíèíã:<br>
+<font size="5"><b>ÌÎÒÈÂÀÖÈß ÊÀÊ ÈÍÑÒÐÓÌÅÍÒ ÓÏÐÀÂËÅÍÈß ÏÅÐÑÎÍÀËÎÌ</b></font><br>
+<font color="red"><b>19 èþíÿ 2003 ã.</b></font><br>
+<b><i>Òpeíèíã ïpeäíaçía÷eí äëÿ âûcøeão è cpeäíeão óïpaâëeí÷ecêoão ïepcoíaëa.</i></b><br></center><br>
+<p align="justify"><b>Òpeíep: Áopìoòoâ Ïaâeë.</b> Ïpaêòè÷ecêèé ïcèõoëoã, oïûò paáoòû áoëee 10 ëeò â oáëacòè ïcèõoëoãèè è áèçíec-òpeíèíãoâ. Àâòop pÿäa ïóáëèêaöèé è ìeòoäè÷ecêèõ ïocoáèé paçëè÷íûõ íaïpaâëeíèé ïcèõoëoãèè, â òoì ÷ècëe: “Òeõíoëoãèÿ äeëoâoão oáùeíèÿ”, “Òeõíèêè è ïpèeìû ýôôeêòèâíûõ ïepeãoâopoâ”, “Ñòpaòeãèè ôopìèpoâaíèÿ êopïopaòèâíoão èìèäæa” è äp. Çaêoí÷èë ËÃÓ ôaêóëüòeò coöèaëüíoé ïcèõoëoãèè, Ðoccèécêóþ Àêaäeìèþ ãocóäapcòâeííoé cëóæáû ïpè Ïpeçèäeíòe ÐÔ, êópcû MBA.<br><br>
+<b><u>Öeëè òpeíèíãa:</u></b><br>
+1. Îcâoèòü ïpèeìû óïpaâëeíèÿ ìoòèâaöèeé;<br>
+2. Ïoëó÷èòü ïpaêòè÷ecêèe íaâûêè ìoòèâaöèè ïepcoíaëa ê paáoòe;<br>
+3. Îcâoèòü ocíoâíûe íaâûêè êoìaíäooápaçoâaíèÿ;<br>
+4. Îâëaäeòü ïpaêòè÷ecêèìè ìeòoäaìè coçäaíèÿ è ócèëeíèÿ paáo÷eé ìoòèâaöèè, êoìaíäooápaçoâaíèÿ.<br><br>
+<b><u>Çaäa÷è òpeíèíãa:</u></b><br>
+&nbsp;- Îcâoèòü ìeòoäû ïoáóæäeíèÿ äpóãèõ ëþäeé ê âûïoëíeíèþ oïpeäeëeííoé äeÿòeëüíocòè;<br>
+&nbsp;- Íaó÷èòücÿ íaïpaâëÿòü ïoáóæäeíèÿ coòpóäíèêoâ â cooòâeòcòâèe c çaäa÷aìè opãaíèçaöèè.<br><br>
+<b><u>Ñoäepæaíèe ïpoãpaììû:</u></b><br>
+<b>I. Ìaòepèaëüíûe è íeìaòepèaëüíûe ôopìû ìoòèâaöèè:</b><br>
+1. Ìecòo è poëü ìoòèâaöèè â óïpaâëeíèè ïepcoíaëoì;<br>
+2. Ïpaêòèêa óïpaâëeíèÿ opãaíèçaöèÿìè.<br>
+<b>II. Ïpaêòè÷ecêoe ïpèìeíeíèe ìoòèâaöèè â óïpaâëeíèè ïepcoíaëoì:</b><br>
+1. Àíòèìoòèâèpóþùèe pacïopÿæeíèÿ;<br>
+2. Ìoòèâaöèÿ è oöeíêa äeÿòeëüíocòè (poëü aòòecòaöèè coòpóäíèêoâ);<br>
+3. Ìoòèâaöèÿ è ïpaêòèêa íaêaçaíèé.<br><br>
+<b><u>Â çaâepøeíèè ïpoãpaììû ó÷acòíèêè cìoãóò:</u></b><br>
+1. Îpèeíòèpoâaòü coòpóäíèêoâ ía äocòèæeíèe oïpeäeëeííoão peçóëüòaòa;<br>
+2. Îâëaäeòü íeoáõoäèìûìè íaâûêaìè óïpaâëeíèÿ ìoòèâaöèeé ïepcoíaëa;<br>
+3. Ïpèìeíÿòü ïoëó÷eííûe çíaíèÿ â ïpaêòèêe óïpaâëeíèÿ ïepcoíaëoì;<br>
+4. Îïpeäeëÿòü èíäèâèäóaëüíûe ocoáeííocòè (ïpeäïo÷òeíèÿ) ìoòèâaöèè coòpóäíèêoâ â opãaíèçaöèè.<br>
+<i> õoäe òpeíèíãa ècïoëüçóeòcÿ paáo÷èé è cïpaâo÷íûé ìaòepèaë ïo ìoòèâaöèè è còèìóëèpoâaíèþ ïepcoíaëa poccèécêèõ êoìïaíèé. Ïo oêoí÷aíèè âûäaeòcÿ cepòèôèêaò.</i><br><br>
+<center>Ïpoäoëæèòeëüíocòü: 1 äeíü, 8 ÷acoâ (äâa ïepepûâa, oáeä)<br>
+<b>Ñòoèìocòü ó÷acòèÿ: 4 700 póáëeé áeç ÍÄÑ.</b><br>
+921-5862, 928-4156, 928-4200, 928-5321</center><br>
+<font size=1> Åcëè èíôopìaöèÿ ïoäoáíoão poäa Âac íe èíòepecóeò è ïo äpóãèì âoïpocaì - ïèøèòe: <a href="mailto:motiv@mailje.nl">seminar</a></font>
+<br><font size="1" color="#ffffff">3ZkRPb60QBbiHef1IRVl</font>
+</body></html>
+
+
+
diff --git a/rt/lib/t/data/text-html-with-umlaut b/rt/lib/t/data/text-html-with-umlaut
new file mode 100644
index 0000000..90e5d3f
--- /dev/null
+++ b/rt/lib/t/data/text-html-with-umlaut
@@ -0,0 +1,35 @@
+Return-Path: <gst@example.com>
+Delivered-To: j@pallas.eruditorum.org
+Received: from vis.example.com (vis.example.com [212.68.68.251])
+ by pallas.eruditorum.org (Postfix) with SMTP id 59236111C3
+ for <jesse@example.com>; Thu, 12 Jun 2003 02:14:44 -0400 (EDT)
+Received: (qmail 29541 invoked by uid 502); 12 Jun 2003 06:14:42 -0000
+Received: from sivd.example.com (HELO example.com) (192.168.42.1)
+ by 192.168.42.42 with SMTP; 12 Jun 2003 06:14:42 -0000
+Received: received from 172.20.72.174 by odie.example.com; Thu, 12 Jun 2003 08:14:27 +0200
+Received: by mailserver.example.com with Internet Mail Service (5.5.2653.19) id <LJSB7T54>; Thu, 12 Jun 2003 08:14:39 +0200
+Message-ID: <50362EC956CBD411A339009027F6257E013DD495@mailserver.example.com>
+Date: Thu, 12 Jun 2003 08:14:39 +0200
+From: "Stever, Gregor" <gst@example.com>
+MIME-Version: 1.0
+X-Mailer: Internet Mail Service (5.5.2653.19)
+To: "'jesse@example.com'" <jesse@example.com>
+Subject: An example of mail containing text-html with an umlaut in the content
+Date: Thu, 12 Jun 2003 08:14:39 +0200
+Content-Type: text/html;
+ charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD>
+<META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; charset=3Diso-8859-=
+1">
+
+
+<META content=3D"MSHTML 6.00.2800.1170" name=3DGENERATOR></HEAD>
+<BODY>
+<DIV><FONT face=3DArial><FONT size=3D2>Hello,<BR><BR>ist this kind of Messa=
+ges, that=20
+causes rt to crash.<BR><BR>Mit freundlichen Gr=FC=DFen<BR>Gregor=20
+Stever&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^^causes Error<SPAN=20
+class=3D975501206-12062003>!!</SPAN></FONT></FONT></DIV></BODY></HTML>
diff --git a/rt/lib/t/data/very-long-subject b/rt/lib/t/data/very-long-subject
new file mode 100644
index 0000000..ad420d0
--- /dev/null
+++ b/rt/lib/t/data/very-long-subject
@@ -0,0 +1,12 @@
+Subject: 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+Date: Mon, 02 Jun 2003 20:58:30 +0200
+To: rt@example.com
+From: foo@example.com
+Mime-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+This email has a very long subject. Our DB allows you to use subject
+no longer than 200 chars, but we creat ticket, don't generate an
+error and trancate long line.
+
diff --git a/rt/lib/t/regression/00-mason-syntax.t b/rt/lib/t/regression/00-mason-syntax.t
new file mode 100644
index 0000000..a94c7ef
--- /dev/null
+++ b/rt/lib/t/regression/00-mason-syntax.t
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+my $ok = 1;
+
+use File::Find;
+find( {
+ no_chdir => 1,
+ wanted => sub {
+ return if /\.(?:jpe?g|png|gif|rej|\~)$/i;
+ if (m!/\.svn$!) {
+ $File::Find::prune = 1;
+ return;
+ }
+ return unless -f $_;
+ diag "testing $_" if $ENV{'TEST_VERBOSE'};
+ eval { compile_file($_) } and return;
+ $ok = 0;
+ diag "error in ${File::Find::name}:\n$@";
+ },
+}, 'html');
+ok($ok, "mason syntax is ok");
+
+use HTML::Mason;
+use HTML::Mason::Compiler;
+use HTML::Mason::Compiler::ToObject;
+
+sub compile_file {
+ my $file = shift;
+
+ open my $fh, '<:utf8', $file or die "couldn't open '$file': $!";
+ my $text = do { local $/; <$fh> };
+ close $fh or die "couldn't close '$file': $!";
+
+ my $compiler = new HTML::Mason::Compiler::ToObject;
+ $compiler->compile(
+ comp_source => $text,
+ name => 'my',
+ $HTML::Mason::VERSION >= 1.36? (comp_path => 'my'): (),
+ );
+ return 1;
+}
+
diff --git a/rt/lib/t/regression/01ticket_link_searching.t b/rt/lib/t/regression/01ticket_link_searching.t
new file mode 100644
index 0000000..a402c73
--- /dev/null
+++ b/rt/lib/t/regression/01ticket_link_searching.t
@@ -0,0 +1,159 @@
+#!/usr/bin/perl -w
+
+use Test::More tests => 30;
+use strict;
+use RT;
+
+# Load the config file
+RT::LoadConfig();
+
+#Connect to the database and get RT::SystemUser and RT::Nobody loaded
+RT::Init();
+
+#Get the current user all loaded
+my $CurrentUser = $RT::SystemUser;
+
+my $queue = new RT::Queue($CurrentUser);
+$queue->Load('General') || Abort(loc("Queue could not be loaded."));
+
+my $child_ticket = new RT::Ticket( $CurrentUser );
+my ($childid) = $child_ticket->Create(
+ Subject => 'test child',
+ Queue => $queue->Id,
+);
+ok($childid, "We created a child ticket");
+
+my $parent_ticket = new RT::Ticket( $CurrentUser );
+my ($parentid) = $parent_ticket->Create(
+ Subject => 'test parent',
+ Children => [ $childid ],
+ Queue => $queue->Id,
+);
+ok($parentid, "We created a parent ticket");
+
+
+my $Collection = RT::Tickets->new($CurrentUser);
+$Collection->LimitMemberOf( $parentid );
+is($Collection->Count,1, "We found only one result");
+ok($Collection->First);
+is($Collection->First->id, $childid, "We found the collection of all children of $parentid with Limit");
+
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->FromSQL("MemberOf = $parentid");
+is($Collection->Count, 1, "We found only one result");
+ok($Collection->First);
+is($Collection->First->id, $childid, "We found the collection of all children of $parentid with TicketSQL");
+
+
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->LimitHasMember ($childid);
+is($Collection->Count,1, "We found only one result");
+ok($Collection->First);
+is($Collection->First->id, $parentid, "We found the collection of all parents of $childid with Limit");
+
+
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->FromSQL("HasMember = $childid");
+is($Collection->Count,1, "We found only one result");
+ok($Collection->First);
+is($Collection->First->id, $parentid, "We found the collection of all parents of $childid with TicketSQL");
+
+
+# Now we find a collection of all the tickets which have no members. they should have no children.
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->LimitHasMember('');
+# must contain child; must not contain parent
+my %has;
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok( $has{$childid}, "The collection has our child - $childid");
+ok( !$has{$parentid}, "The collection doesn't have our parent - $parentid");
+
+
+# Now we find a collection of all the tickets which are not members of anything. they should have no parents.
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->LimitMemberOf('');
+# must contain parent; must not contain child
+%has = ();
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok ($has{$parentid} , "The collection has our parent - $parentid");
+ok( !$has{$childid}, "The collection doesn't have our child - $childid");
+
+
+# Do it all over with TicketSQL
+#
+
+
+
+# Now we find a collection of all the tickets which have no members. they should have no children.
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->FromSQL ("HasMember IS NULL");
+# must contain parent; must not contain child
+%has = ();
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok( !$has{$parentid}, "The collection doesn't have our parent - $parentid");
+ok( $has{$childid}, "The collection has our child - $childid");
+
+
+# Now we find a collection of all the tickets which have no members. they should have no children.
+# Alternate syntax
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->FromSQL("HasMember = ''");
+# must contain parent; must not contain child
+%has = ();
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok( !$has{$parentid}, "The collection doesn't have our parent - $parentid");
+ok( $has{$childid}, "The collection has our child - $childid");
+
+
+# Now we find a collection of all the tickets which are not members of anything. they should have no parents.
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->FromSQL("MemberOf IS NULL");
+# must not contain parent; must contain parent
+%has = ();
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok( $has{$parentid}, "The collection has our parent - $parentid");
+ok( !$has{$childid}, "The collection doesn't have our child - $childid");
+
+
+# Now we find a collection of all the tickets which are not members of anything. they should have no parents.
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->FromSQL("MemberOf = ''");
+# must not contain parent; must contain parent
+%has = ();
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok( $has{$parentid}, "The collection has our parent - $parentid");
+ok( !$has{$childid}, "The collection doesn't have our child - $childid");
+
+
+# Now we find a collection of all the tickets which are not members of the parent ticket
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->FromSQL("MemberOf != $parentid");
+%has = ();
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok( $has{$parentid}, "The collection has our parent - $parentid");
+ok( !$has{$childid}, "The collection doesn't have our child - $childid");
+
+$Collection = RT::Tickets->new($CurrentUser);
+$Collection->LimitMemberOf($parentid, OPERATOR => '!=');
+%has = ();
+while (my $t = $Collection->Next) {
+ ++$has{$t->id};
+}
+ok( $has{$parentid}, "The collection has our parent - $parentid");
+ok( !$has{$childid}, "The collection doesn't have our child - $childid");
+
+1;
diff --git a/rt/lib/t/regression/02basic_web.t b/rt/lib/t/regression/02basic_web.t
new file mode 100644
index 0000000..3b8619b
--- /dev/null
+++ b/rt/lib/t/regression/02basic_web.t
@@ -0,0 +1,159 @@
+#!/usr/bin/perl
+
+use strict;
+use Test::More tests => 19;
+use WWW::Mechanize;
+use HTTP::Request::Common;
+use HTTP::Cookies;
+use LWP;
+use Encode;
+
+my $cookie_jar = HTTP::Cookies->new;
+my $agent = WWW::Mechanize->new();
+
+# give the agent a place to stash the cookies
+
+$agent->cookie_jar($cookie_jar);
+
+use RT;
+RT::LoadConfig();
+# get the top page
+my $url = $RT::WebURL;
+diag $url;
+$agent->get($url);
+
+is ($agent->{'status'}, 200, "Loaded a page");
+
+
+# {{{ test a login
+
+# follow the link marked "Login"
+
+ok($agent->{form}->find_input('user'));
+
+ok($agent->{form}->find_input('pass'));
+ok ($agent->{'content'} =~ /username:/i);
+$agent->field( 'user' => 'root' );
+$agent->field( 'pass' => 'password' );
+# the field isn't named, so we have to click link 0
+$agent->click(0);
+is($agent->{'status'}, 200, "Fetched the page ok");
+ok( $agent->{'content'} =~ /Logout/i, "Found a logout link");
+
+
+
+$agent->get($url."Ticket/Create.html?Queue=1");
+is ($agent->{'status'}, 200, "Loaded Create.html");
+$agent->form_number(3);
+# Start with a string containing characters in latin1
+my $string = "I18N Web Testing æøå";
+Encode::from_to($string, 'iso-8859-1', 'utf8');
+$agent->field('Subject' => "Ticket with utf8 body");
+$agent->field('Content' => $string);
+ok($agent->submit(), "Created new ticket with $string as Content");
+like( $agent->{'content'}, qr{$string} , "Found the content");
+ok($agent->{redirected_uri}, "Did redirection");
+
+
+$agent->get($url."Ticket/Create.html?Queue=1");
+is ($agent->{'status'}, 200, "Loaded Create.html");
+$agent->form_number(3);
+# Start with a string containing characters in latin1
+my $string = "I18N Web Testing æøå";
+Encode::from_to($string, 'iso-8859-1', 'utf8');
+$agent->field('Subject' => $string);
+$agent->field('Content' => "Ticket with utf8 subject");
+ok($agent->submit(), "Created new ticket with $string as Subject");
+
+like( $agent->{'content'}, qr{$string} , "Found the content");
+
+# Update time worked in hours
+$agent->follow_link( text_regex => qr/Basics/ );
+$agent->submit_form( form_number => 3,
+ fields => { TimeWorked => 5, 'TimeWorked-TimeUnits' => "hours" }
+);
+
+like ($agent->{'content'}, qr/to &#39;300&#39;/, "5 hours is 300 minutes");
+
+# }}}
+
+# {{{ Query Builder tests
+
+my $response = $agent->get($url."Search/Build.html");
+ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
+
+# Parsing TicketSQL
+#
+# Adding items
+
+# set the first value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "aaa");
+$agent->submit("AddClause");
+
+# set the next value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "bbb");
+$agent->submit("AddClause");
+
+ok($agent->form_name('BuildQuery'));
+
+# get the query
+my $query = $agent->current_form->find_input("Query")->value;
+# strip whitespace from ends
+$query =~ s/^\s*//g;
+$query =~ s/\s*$//g;
+
+# collapse other whitespace
+$query =~ s/\s+/ /g;
+
+is ($query, "Subject LIKE 'aaa' AND Subject LIKE 'bbb'");
+
+# - new items go one level down
+# - add items at currently selected level
+# - if nothing is selected, add at end, one level down
+#
+# move left
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move left if you're at the top level
+#
+# move right
+# - error if nothing selected
+# - same item should be selected after move
+# - can always move right (no max depth...should there be?)
+#
+# move up
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move up if you're first in the list
+#
+# move down
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move down if you're last in the list
+#
+# toggle
+# - error if nothing selected
+# - change all aggregators in the grouping
+# - don't change any others
+#
+# delete
+# - error if nothing selected
+# - delete currently selected item
+# - delete all children of a grouping
+# - if delete leaves a node with no children, delete that, too
+# - what should be selected?
+#
+# Clear
+# - clears entire query
+# - clears it from the session, too
+
+# }}}
+
+
+1;
diff --git a/rt/lib/t/regression/03web_compiliation_errors.t b/rt/lib/t/regression/03web_compiliation_errors.t
new file mode 100644
index 0000000..29e56d6
--- /dev/null
+++ b/rt/lib/t/regression/03web_compiliation_errors.t
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+use strict;
+use Test::More qw/no_plan/;
+use WWW::Mechanize;
+use HTTP::Request::Common;
+use HTTP::Cookies;
+use LWP;
+use Encode;
+
+my $cookie_jar = HTTP::Cookies->new;
+my $agent = WWW::Mechanize->new();
+
+# give the agent a place to stash the cookies
+$agent->cookie_jar($cookie_jar);
+
+use RT;
+RT::LoadConfig();
+
+# get the top page
+my $url = $RT::WebURL;
+diag "Base URL is '$url'" if $ENV{TEST_VERBOSE};
+$agent->get($url);
+
+is ($agent->{'status'}, 200, "Loaded a page");
+
+# {{{ test a login
+
+# follow the link marked "Login"
+
+ok($agent->{form}->find_input('user'));
+
+ok($agent->{form}->find_input('pass'));
+ok ($agent->{'content'} =~ /username:/i);
+$agent->field( 'user' => 'root' );
+$agent->field( 'pass' => 'password' );
+# the field isn't named, so we have to click link 0
+$agent->click(0);
+is($agent->{'status'}, 200, "Fetched the page ok");
+ok( $agent->{'content'} =~ /Logout/i, "Found a logout link");
+
+
+use File::Find;
+find ( \&wanted , 'html/');
+
+sub wanted {
+ -f && /\.html$/ && $_ !~ /Logout.html$/ && test_get($File::Find::name);
+}
+
+sub test_get {
+ my $file = shift;
+
+ $file =~ s#^html/##;
+ diag( "testing $url/$file" ) if $ENV{TEST_VERBOSE};
+ ok ($agent->get("$url/$file", "GET $url/$file"));
+ is ($agent->{'status'}, 200, "Loaded $file");
+# ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
+ ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file");
+ ok( $agent->{'content'} !~ /raw error/i, "Didn't get a Mason compilation error on $file");
+}
+
+# }}}
+
+1;
diff --git a/rt/lib/t/regression/04send_email.t b/rt/lib/t/regression/04send_email.t
new file mode 100644
index 0000000..a175ffa
--- /dev/null
+++ b/rt/lib/t/regression/04send_email.t
@@ -0,0 +1,549 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More tests => 142;
+
+use RT;
+RT::LoadConfig();
+RT::Init;
+
+use RT::EmailParser;
+use RT::Tickets;
+use RT::Action::SendEmail;
+
+my @_outgoing_messages;
+my @scrips_fired;
+
+#We're not testing acls here.
+my $everyone = RT::Group->new($RT::SystemUser);
+$everyone->LoadSystemInternalGroup('Everyone');
+$everyone->PrincipalObj->GrantRight(Right =>'SuperUser');
+
+
+is (__PACKAGE__, 'main', "We're operating in the main package");
+
+{
+ no warnings qw/redefine/;
+ sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+
+ main::_fired_scrip($self->ScripObj);
+ main::ok(ref($MIME) eq 'MIME::Entity', "hey, look. it's a mime entity");
+ }
+}
+
+# some utils
+sub first_txn { return $_[0]->Transactions->First }
+sub first_attach { return first_txn($_[0])->Attachments->First }
+
+sub count_txns { return $_[0]->Transactions->Count }
+sub count_attachs { return first_txn($_[0])->Attachments->Count }
+
+sub file_content
+{
+ open my $fh, "<:raw", $_[0] or die "couldn't open file '$_[0]': $!";
+ local $/;
+ return scalar <$fh>;
+}
+
+# instrument SendEmail to pass us what it's about to send.
+# create a regular ticket
+
+my $parser = RT::EmailParser->new();
+
+
+# Let's test to make sure a multipart/report is processed correctly
+my $content = file_content("$RT::BasePath/lib/t/data/multipart-report");
+# be as much like the mail gateway as possible.
+use RT::Interface::Email;
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick= $tickets->First();
+isa_ok($tick, "RT::Ticket", "got a ticket object");
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_txn($tick)->Content =~ /The original message was received/, "It's the bounce");
+
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+
+undef @scrips_fired;
+
+
+
+
+$parser->ParseMIMEEntityFromScalar('From: root@localhost
+To: rt@example.com
+Subject: This is a test of new ticket creation as an unknown user
+
+Blah!
+Foob!');
+
+
+use Data::Dumper;
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id, undef, $msg ) = $ticket->Create(Requestor => ['root@localhost'], Queue => 'general', Subject => 'I18NTest', MIMEObj => $parser->Entity);
+ok ($id,$msg);
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+ $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+ok ($tick->Subject eq 'I18NTest', "failed to create the new ticket from an unprivileged account");
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+# make sure it sends an autoreply
+# make sure it sends a notification to adminccs
+
+
+# we need to swap out SendMessage to test the new things we care about;
+&utf8_redef_sendmessage;
+
+# create an iso 8859-1 ticket
+@scrips_fired = ();
+
+$content = file_content("$RT::BasePath/lib/t/data/new-ticket-from-iso-8859-1");
+
+
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+use RT::Interface::Email;
+
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+ $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+ $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_txn($tick)->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
+
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+# make sure it sends an autoreply
+
+
+# make sure it sends a notification to adminccs
+
+# If we correspond, does it do the right thing to the outbound messages?
+
+$parser->ParseMIMEEntityFromScalar($content);
+ ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+$parser->ParseMIMEEntityFromScalar($content);
+($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+
+
+
+
+# we need to swap out SendMessage to test the new things we care about;
+&iso8859_redef_sendmessage;
+$RT::EmailOutputEncoding = 'iso-8859-1';
+# create an iso 8859-1 ticket
+@scrips_fired = ();
+
+ $content = file_content("$RT::BasePath/lib/t/data/new-ticket-from-iso-8859-1");
+# be as much like the mail gateway as possible.
+use RT::Interface::Email;
+
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+ $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_txn($tick)->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
+
+
+# make sure it fires scrips.
+is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
+# make sure it sends an autoreply
+
+
+# make sure it sends a notification to adminccs
+
+
+# If we correspond, does it do the right thing to the outbound messages?
+
+$parser->ParseMIMEEntityFromScalar($content);
+ ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+$parser->ParseMIMEEntityFromScalar($content);
+($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
+ok ($id, $msg);
+
+
+sub _fired_scrip {
+ my $scrip = shift;
+ push @scrips_fired, $scrip;
+}
+
+sub utf8_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval '
+ sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+
+ my $scrip = $self->ScripObj->id;
+ ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
+ main::_fired_scrip($self->ScripObj);
+ $MIME->make_singlepart;
+ main::ok( ref($MIME) eq \'MIME::Entity\',
+ "hey, look. it\'s a mime entity" );
+ main::ok( ref( $MIME->head ) eq \'MIME::Head\',
+ "its mime header is a mime header. yay" );
+ main::ok( $MIME->head->get(\'Content-Type\') =~ /utf-8/,
+ "Its content type is utf-8" );
+ my $message_as_string = $MIME->bodyhandle->as_string();
+ use Encode;
+ $message_as_string = Encode::decode_utf8($message_as_string);
+ main::ok(
+ $message_as_string =~ /H\x{e5}vard/,
+"The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
+
+ }';
+}
+
+sub iso8859_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval '
+ sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+
+ my $scrip = $self->ScripObj->id;
+ ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
+ main::_fired_scrip($self->ScripObj);
+ $MIME->make_singlepart;
+ main::ok( ref($MIME) eq \'MIME::Entity\',
+ "hey, look. it\'s a mime entity" );
+ main::ok( ref( $MIME->head ) eq \'MIME::Head\',
+ "its mime header is a mime header. yay" );
+ main::ok( $MIME->head->get(\'Content-Type\') =~ /iso-8859-1/,
+ "Its content type is iso-8859-1 - " . $MIME->head->get("Content-Type") );
+ my $message_as_string = $MIME->bodyhandle->as_string();
+ use Encode;
+ $message_as_string = Encode::decode("iso-8859-1",$message_as_string);
+ main::ok(
+ $message_as_string =~ /H\x{e5}vard/, "The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
+
+ }';
+}
+
+# {{{ test a multipart alternative containing a text-html part with an umlaut
+
+ $content = file_content("$RT::BasePath/lib/t/data/multipart-alternative-with-umlaut");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&umlauts_redef_sendmessage;
+
+%args = (message => $content, queue => 1, action => 'correspond');
+RT::Interface::Email::Gateway(\%args);
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick = $tickets->First();
+
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_txn($tick)->Content =~ /causes Error/, "We recorded the content right as text-plain");
+is (count_attachs($tick) , 3 , "Has three attachments, presumably a text-plain, a text-html and a multipart alternative");
+
+sub umlauts_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage { }';
+}
+
+# }}}
+
+# {{{ test a text-html message with an umlaut
+
+ $content = file_content("$RT::BasePath/lib/t/data/text-html-with-umlaut");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_html_umlauts_redef_sendmessage;
+
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+ $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+ $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_attach($tick)->Content =~ /causes Error/, "We recorded the content as containing 'causes error'") or diag( first_attach($tick)->Content );
+ok (first_attach($tick)->ContentType =~ /text\/html/, "We recorded the content as text/html");
+is (count_attachs($tick), 1 , "Has one attachment, presumably a text-html and a multipart alternative");
+
+sub text_html_umlauts_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ is ($MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
+ is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
+ is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
+ is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
+ }';
+}
+
+# }}}
+
+# {{{ test a text-html message with russian characters
+
+ $content = file_content("$RT::BasePath/lib/t/data/text-html-in-russian");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_html_russian_redef_sendmessage;
+
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+ $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+ $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_attach($tick)->ContentType =~ /text\/html/, "We recorded the content right as text-html");
+ok (count_attachs($tick) ==1 , "Has one attachment, presumably a text-html and a multipart alternative");
+
+sub text_html_russian_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ use Data::Dumper;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ ok (is $MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
+ is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
+ is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
+ is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
+ my $content_1251;
+ $content_1251 = $MIME->parts(1)->bodyhandle->as_string();
+ ok ($content_1251 =~ qr{Ó÷eáíûé Öeíòp "ÊÀÄÐÛ ÄÅËÎÂÎÃÎ ÌÈÐÀ" ïpèãëaøaeò ía òpeíèíã:},
+"Content matches drugim in codepage 1251" );
+ }';
+}
+
+# }}}
+
+# {{{ test a message containing a russian subject and NO content type
+
+unshift (@RT::EmailInputEncodings, 'koi8-r');
+$RT::EmailOutputEncoding = 'koi8-r';
+$content = file_content("$RT::BasePath/lib/t/data/russian-subject-no-content-type");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_plain_russian_redef_sendmessage;
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+ $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick= $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_attach($tick)->ContentType =~ /text\/plain/, "We recorded the content type right");
+ok (count_attachs($tick) ==1 , "Has one attachment, presumably a text-plain");
+is ($tick->Subject, "\x{442}\x{435}\x{441}\x{442} \x{442}\x{435}\x{441}\x{442}", "Recorded the subject right");
+sub text_plain_russian_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ is ($MIME->head->mime_type , "text/plain", "The only part is text/plain ");
+ my $subject = $MIME->head->get("subject");
+ chomp($subject);
+ #is( $subject , /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly");
+ };
+ ';
+}
+
+shift @RT::EmailInputEncodings;
+$RT::EmailOutputEncoding = 'utf-8';
+# }}}
+
+
+# {{{ test a message containing a nested RFC 822 message
+
+ $content = file_content("$RT::BasePath/lib/t/data/nested-rfc-822");
+ok ($content, "Loaded nested-rfc-822 to test");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&text_plain_nested_redef_sendmessage;
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+ $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick= $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+is ($tick->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?");
+ok (first_attach($tick)->ContentType =~ /multipart\/mixed/, "We recorded the content type right");
+is (count_attachs($tick) , 5 , "Has one attachment, presumably a text-plain and a message RFC 822 and another plain");
+sub text_plain_nested_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage {
+ my $self = shift;
+ my $MIME = shift;
+ return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
+ is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart");
+ my $subject = $MIME->head->get("subject");
+ $subject = MIME::Base64::decode_base64( $subject);
+ chomp($subject);
+ # TODO, why does this test fail
+ #ok($subject =~ qr{Niv\x{e5}er}, "The subject matches the word - $subject");
+ 1;
+ }';
+}
+
+# }}}
+
+
+# {{{ test a multipart alternative containing a uuencoded mesage generated by lotus notes
+
+ $content = file_content("$RT::BasePath/lib/t/data/notes-uuencoded");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&notes_redef_sendmessage;
+
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick= $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_txn($tick)->Content =~ /from Lotus Notes/, "We recorded the content right");
+is (count_attachs($tick) , 3 , "Has three attachments");
+
+sub notes_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage { }';
+}
+
+# }}}
+
+# {{{ test a multipart that crashes the file-based mime-parser works
+
+ $content = file_content("$RT::BasePath/lib/t/data/crashes-file-based-parser");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+# be as much like the mail gateway as possible.
+&crashes_redef_sendmessage;
+
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+ $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick= $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+ok (first_txn($tick)->Content =~ /FYI/, "We recorded the content right");
+is (count_attachs($tick) , 5 , "Has three attachments");
+
+sub crashes_redef_sendmessage {
+ no warnings qw/redefine/;
+ eval 'sub RT::Action::SendEmail::SendMessage { }';
+}
+
+
+
+# }}}
+
+# {{{ test a multi-line RT-Send-CC header
+
+ $content = file_content("$RT::BasePath/lib/t/data/rt-send-cc");
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+
+ %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+ $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick= $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+my $cc = first_attach($tick)->GetHeader('RT-Send-Cc');
+ok ($cc =~ /test1/, "Found test 1");
+ok ($cc =~ /test2/, "Found test 2");
+ok ($cc =~ /test3/, "Found test 3");
+ok ($cc =~ /test4/, "Found test 4");
+ok ($cc =~ /test5/, "Found test 5");
+
+# }}}
+
+diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE};
+{
+ my $content = file_content("$RT::BasePath/lib/t/data/subject-with-folding-ws");
+ my ($status, $msg, $ticket) = RT::Interface::Email::Gateway(
+ { message => $content, queue => 1, action => 'correspond' }
+ );
+ ok ($status, 'created ticket') or diag "error: $msg";
+ ok ($ticket->id, "found ticket ". $ticket->id);
+ is ($ticket->Subject, 'test', 'correct subject');
+}
+
+diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE};
+{
+ my $content = file_content("$RT::BasePath/lib/t/data/very-long-subject");
+ my ($status, $msg, $ticket) = RT::Interface::Email::Gateway(
+ { message => $content, queue => 1, action => 'correspond' }
+ );
+ ok ($status, 'created ticket') or diag "error: $msg";
+ ok ($ticket->id, "found ticket ". $ticket->id);
+ is ($ticket->Subject, '0123456789'x20, 'correct subject');
+}
+
+
+
+# Don't taint the environment
+$everyone->PrincipalObj->RevokeRight(Right =>'SuperUser');
+1;
diff --git a/rt/lib/t/regression/05cronsupport.t b/rt/lib/t/regression/05cronsupport.t
new file mode 100644
index 0000000..8e5bd75
--- /dev/null
+++ b/rt/lib/t/regression/05cronsupport.t
@@ -0,0 +1,91 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More qw/no_plan/;
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+### Set up some testing data. Test the testing data because why not?
+
+# Create a user with rights, a queue, and some tickets.
+my $user_obj = RT::User->new($RT::SystemUser);
+my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('tara@example.com');
+ok($ret, 'record test user creation');
+$user_obj->SetName('tara');
+$user_obj->PrincipalObj->GrantRight(Right => 'SuperUser');
+my $CurrentUser = RT::CurrentUser->new('tara');
+
+# Create our template, which will be used for tests of RT::Action::Record*.
+
+my $template_content = 'RT-Send-Cc: tla@example.com
+RT-Send-Bcc: jesse@example.com
+
+This is a content string with no content.';
+
+my $template_obj = RT::Template->new($CurrentUser);
+$template_obj->Create(Queue => '0',
+ Name => 'recordtest',
+ Description => 'testing Record actions',
+ Content => $template_content,
+ );
+
+# Create a queue and some tickets.
+
+my $queue_obj = RT::Queue->new($CurrentUser);
+($ret, $msg) = $queue_obj->Create(Name => 'recordtest', Description => 'queue for Action::Record testing');
+ok($ret, 'record test queue creation');
+
+my $ticket1 = RT::Ticket->new($CurrentUser);
+my ($id, $tobj, $msg2) = $ticket1->Create(Queue => $queue_obj,
+ Requestor => ['tara@example.com'],
+ Subject => 'bork bork bork',
+ Priority => 22,
+ );
+ok($id, 'record test ticket creation 1');
+my $ticket2 = RT::Ticket->new($CurrentUser);
+($id, $tobj, $msg2) = $ticket2->Create(Queue => $queue_obj,
+ Requestor => ['root@localhost'],
+ Subject => 'hurdy gurdy'
+ );
+ok($id, 'record test ticket creation 2');
+
+
+### OK. Have data, will travel.
+
+# First test the search.
+
+ok(require RT::Search::FromSQL, "Search::FromSQL loaded");
+my $ticketsqlstr = "Requestor.EmailAddress = '" . $CurrentUser->EmailAddress .
+ "' AND Priority > '20'";
+my $search = RT::Search::FromSQL->new(Argument => $ticketsqlstr, TicketsObj => RT::Tickets->new($CurrentUser),
+ );
+is(ref($search), 'RT::Search::FromSQL', "search created");
+ok($search->Prepare(), "fromsql search run");
+my $counter = 0;
+while(my $t = $search->TicketsObj->Next() ) {
+ is($t->Id(), $ticket1->Id(), "fromsql search results 1");
+ $counter++;
+}
+is ($counter, 1, "fromsql search results 2");
+
+# Right. Now test the actions.
+
+ok(require RT::Action::RecordComment);
+ok(require RT::Action::RecordCorrespondence);
+
+my ($comment_act, $correspond_act);
+ok($comment_act = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordComment created");
+ok($correspond_act = RT::Action::RecordCorrespondence->new(TicketObj => $ticket2, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordCorrespondence created");
+ok($comment_act->Prepare(), "Comment prepared");
+ok($correspond_act->Prepare(), "Correspond prepared");
+ok($comment_act->Commit(), "Comment committed");
+ok($correspond_act->Commit(), "Correspondence committed");
+
+# Now test for loop suppression.
+my ($trans, $desc, $transaction) = $ticket2->Comment(MIMEObj => $template_obj->MIMEObj);
+my $bogus_action = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, TransactionObj => $transaction, CurrentUser => $CurrentUser);
+ok(!$bogus_action->Prepare(), "Comment aborted to prevent loop");
+
+1;
diff --git a/rt/lib/t/regression/06-mime_decoding.t b/rt/lib/t/regression/06-mime_decoding.t
new file mode 100644
index 0000000..2dca4f1
--- /dev/null
+++ b/rt/lib/t/regression/06-mime_decoding.t
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Test::More tests => 7;
+
+use_ok("RT");
+
+RT::LoadConfig();
+RT::Init();
+
+use_ok('RT::I18N');
+
+diag q{'=' char in a leading part before an encoded part} if $ENV{TEST_VERBOSE};
+{
+ my $str = 'key="plain"; key="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="';
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ 'key="plain"; key="мой_файл.bin"',
+ "right decoding"
+ );
+}
+
+diag q{not compliant with standards, but MUAs send such field when attachment has non-ascii in name}
+ if $ENV{TEST_VERBOSE};
+{
+ my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="';
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ 'attachment; filename="мой_файл.bin"',
+ "right decoding"
+ );
+}
+
+diag q{'=' char in a trailing part after an encoded part} if $ENV{TEST_VERBOSE};
+{
+ my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="; some_prop="value"';
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ 'attachment; filename="мой_файл.bin"; some_prop="value"',
+ "right decoding"
+ );
+}
+
+diag q{regression test for #5248 from rt3.fsck.com} if $ENV{TEST_VERBOSE};
+{
+ my $str = qq{Subject: =?ISO-8859-1?Q?Re=3A_=5BXXXXXX=23269=5D_=5BComment=5D_Frag?=}
+ . qq{\n =?ISO-8859-1?Q?e_zu_XXXXXX--xxxxxx_/_Xxxxx=FCxxxxxxxxxx?=};
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ qq{Subject: Re: [XXXXXX#269] [Comment] Frage zu XXXXXX--xxxxxx / Xxxxxüxxxxxxxxxx},
+ "right decoding"
+ );
+}
+
+diag q{newline and encoded file name} if $ENV{TEST_VERBOSE};
+{
+ my $str = qq{application/vnd.ms-powerpoint;\n\tname="=?ISO-8859-1?Q?Main_presentation.ppt?="};
+ is(
+ RT::I18N::DecodeMIMEWordsToUTF8($str),
+ qq{application/vnd.ms-powerpoint;\tname="Main presentation.ppt"},
+ "right decoding"
+ );
+}
+
diff --git a/rt/lib/t/regression/06mailgateway.t b/rt/lib/t/regression/06mailgateway.t
new file mode 100644
index 0000000..5fc5029
--- /dev/null
+++ b/rt/lib/t/regression/06mailgateway.t
@@ -0,0 +1,663 @@
+#!/usr/bin/perl -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
+# <jesse.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+=head1 NAME
+
+rt-mailgate - Mail interface to RT3.
+
+=cut
+
+use strict;
+use Test::More tests => 109;
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+use RT::I18N;
+use Digest::MD5 qw(md5_base64);
+
+no warnings 'once';
+my $url = join( ':', grep $_, "http://localhost", $RT::WebPort ) . $RT::WebPath ."/";
+
+# Make sure that when we call the mailgate wrong, it tempfails
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url http://this.test.for.non-connection.is.expected.to.generate.an.error"), "Opened the mailgate - The error below is expected - $@");
+print MAIL <<EOF;
+From: root\@localhost
+To: rt\@$RT::rtname
+Subject: This is a test of new ticket creation
+
+Foob!
+EOF
+close (MAIL);
+
+# Check the return value
+is ( $? >> 8, 75, "The error message above is expected The mail gateway exited with a failure. yay");
+
+
+# {{{ Test new ticket creation by root who is privileged and superuser
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --debug --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: root\@localhost
+To: rt\@$RT::rtname
+Subject: This is a test of new ticket creation
+
+Blah!
+Foob!
+EOF
+close (MAIL);
+
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+use RT::Tickets;
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id', OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok (UNIVERSAL::isa($tick,'RT::Ticket'));
+ok ($tick->Id, "found ticket ".$tick->Id);
+ok ($tick->Subject eq 'This is a test of new ticket creation', "Created the ticket");
+
+# }}}
+
+# {{{ Test new ticket creation without --action argument
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --debug --url $url --queue general"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: root\@localhost
+To: rt\@$RT::rtname
+Subject: using mailgate without --action arg
+
+Blah!
+Foob!
+EOF
+close (MAIL);
+
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id', OPERATOR => '>', VALUE => '0');
+$tick = $tickets->First;
+isa_ok ($tick,'RT::Ticket');
+ok ($tick->Id, "found ticket ".$tick->Id);
+is ($tick->Subject, 'using mailgate without --action arg', "using mailgate without --action arg");
+
+# }}}
+
+# {{{This is a test of new ticket creation as an unknown user
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: doesnotexist\@$RT::rtname
+To: rt\@$RT::rtname
+Subject: This is a test of new ticket creation as an unknown user
+
+Blah!
+Foob!
+EOF
+close (MAIL);
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+ok ($tick->Subject ne 'This is a test of new ticket creation as an unknown user', "failed to create the new ticket from an unprivileged account");
+my $u = RT::User->new($RT::SystemUser);
+$u->Load("doesnotexist\@$RT::rtname");
+ok( !$u->Id, " user does not exist and was not created by failed ticket submission");
+
+
+# }}}
+
+# {{{ now everybody can create tickets. can a random unkown user create tickets?
+
+my $g = RT::Group->new($RT::SystemUser);
+$g->LoadSystemInternalGroup('Everyone');
+ok( $g->Id, "Found 'everybody'");
+
+my ($val,$msg) = $g->PrincipalObj->GrantRight(Right => 'CreateTicket');
+ok ($val, "Granted everybody the right to create tickets - $msg");
+
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: doesnotexist\@$RT::rtname
+To: rt\@$RT::rtname
+Subject: This is a test of new ticket creation as an unknown user
+
+Blah!
+Foob!
+EOF
+close (MAIL);
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+$tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+ok ($tick->Subject eq 'This is a test of new ticket creation as an unknown user', "failed to create the new ticket from an unprivileged account");
+ $u = RT::User->new($RT::SystemUser);
+$u->Load("doesnotexist\@$RT::rtname");
+ok( $u->Id != 0, " user does not exist and was created by ticket submission");
+
+# }}}
+
+
+# {{{ can another random reply to a ticket without being granted privs? answer should be no.
+
+
+#($val,$msg) = $g->PrincipalObj->GrantRight(Right => 'CreateTicket');
+#ok ($val, "Granted everybody the right to create tickets - $msg");
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: doesnotexist-2\@$RT::rtname
+To: rt\@$RT::rtname
+Subject: [$RT::rtname #@{[$tick->Id]}] This is a test of a reply as an unknown user
+
+Blah! (Should not work.)
+Foob!
+EOF
+close (MAIL);
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+$u = RT::User->new($RT::SystemUser);
+$u->Load('doesnotexist-2@$RT::rtname');
+ok( !$u->Id, " user does not exist and was not created by ticket correspondence submission");
+# }}}
+
+
+# {{{ can another random reply to a ticket after being granted privs? answer should be yes
+
+
+($val,$msg) = $g->PrincipalObj->GrantRight(Right => 'ReplyToTicket');
+ok ($val, "Granted everybody the right to reply to tickets - $msg");
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: doesnotexist-2\@$RT::rtname
+To: rt\@$RT::rtname
+Subject: [$RT::rtname #@{[$tick->Id]}] This is a test of a reply as an unknown user
+
+Blah!
+Foob!
+EOF
+close (MAIL);
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+
+$u = RT::User->new($RT::SystemUser);
+$u->Load("doesnotexist-2\@$RT::rtname");
+ok( $u->Id != 0, " user exists and was created by ticket correspondence submission");
+
+# }}}
+
+# {{{ can another random comment on a ticket without being granted privs? answer should be no.
+
+
+#($val,$msg) = $g->PrincipalObj->GrantRight(Right => 'CreateTicket');
+#ok ($val, "Granted everybody the right to create tickets - $msg");
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action comment"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: doesnotexist-3\@$RT::rtname
+To: rt\@$RT::rtname
+Subject: [$RT::rtname #@{[$tick->Id]}] This is a test of a comment as an unknown user
+
+Blah! (Should not work.)
+Foob!
+EOF
+close (MAIL);
+
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+$u = RT::User->new($RT::SystemUser);
+$u->Load("doesnotexist-3\@$RT::rtname");
+ok( !$u->Id, " user does not exist and was not created by ticket comment submission");
+
+# }}}
+# {{{ can another random reply to a ticket after being granted privs? answer should be yes
+
+
+($val,$msg) = $g->PrincipalObj->GrantRight(Right => 'CommentOnTicket');
+ok ($val, "Granted everybody the right to reply to tickets - $msg");
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action comment"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: doesnotexist-3\@$RT::rtname
+To: rt\@$RT::rtname
+Subject: [$RT::rtname #@{[$tick->Id]}] This is a test of a comment as an unknown user
+
+Blah!
+Foob!
+EOF
+close (MAIL);
+
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+$u = RT::User->new($RT::SystemUser);
+$u->Load("doesnotexist-3\@$RT::rtname");
+ok( $u->Id != 0, " user exists and was created by ticket comment submission");
+
+# }}}
+
+# {{{ Testing preservation of binary attachments
+
+# Get a binary blob (Best Practical logo)
+
+# Create a mime entity with an attachment
+
+use MIME::Entity;
+my $entity = MIME::Entity->build( From => 'root@localhost',
+ To => 'rt@localhost',
+ Subject => 'binary attachment test',
+ Data => ['This is a test of a binary attachment']);
+
+# currently in lib/t/autogen
+
+my $LOGO_FILE = $RT::MasonComponentRoot.'/NoAuth/images/bplogo.gif';
+
+$entity->attach(Path => $LOGO_FILE,
+ Type => 'image/gif',
+ Encoding => 'base64');
+
+# Create a ticket with a binary attachment
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+
+$entity->print(\*MAIL);
+
+close (MAIL);
+
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+$tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id', OPERATOR => '>', VALUE => '0');
+ $tick = $tickets->First();
+ok (UNIVERSAL::isa($tick,'RT::Ticket'));
+ok ($tick->Id, "found ticket ".$tick->Id);
+ok ($tick->Subject eq 'binary attachment test', "Created the ticket - ".$tick->Id);
+
+my $file = `cat $LOGO_FILE`;
+ok ($file, "Read in the logo image");
+
+
+diag( "for the raw file the content is ". md5_base64($file) );
+
+
+
+# Verify that the binary attachment is valid in the database
+my $attachments = RT::Attachments->new($RT::SystemUser);
+$attachments->Limit(FIELD => 'ContentType', VALUE => 'image/gif');
+ok ($attachments->Count == 1, 'Found only one gif in the database');
+my $attachment = $attachments->First;
+ok($attachment->Id);
+my $acontent = $attachment->Content;
+
+diag( "coming from the database, the content is ". md5_base64($acontent) );
+
+is( $acontent, $file, 'The attachment isn\'t screwed up in the database.');
+# Log in as root
+use Getopt::Long;
+use LWP::UserAgent;
+
+
+# Grab the binary attachment via the web ui
+my $ua = LWP::UserAgent->new();
+
+my $full_url = "$url/Ticket/Attachment/".$attachment->TransactionId."/".$attachment->id."/bplogo.gif?&user=root&pass=password";
+my $r = $ua->get( $full_url);
+
+
+# Verify that the downloaded attachment is the same as what we uploaded.
+is($file, $r->content, 'The attachment isn\'t screwed up in download');
+
+
+
+# }}}
+
+# {{{ Simple I18N testing
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+
+print MAIL <<EOF;
+From: root\@localhost
+To: rtemail\@$RT::rtname
+Subject: This is a test of I18N ticket creation
+Content-Type: text/plain; charset="utf-8"
+
+2 accented lines
+\303\242\303\252\303\256\303\264\303\273
+\303\241\303\251\303\255\303\263\303\272
+bye
+EOF
+close (MAIL);
+
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+my $unitickets = RT::Tickets->new($RT::SystemUser);
+$unitickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$unitickets->Limit(FIELD => 'id', OPERATOR => '>', VALUE => '0');
+my $unitick = $unitickets->First();
+ok (UNIVERSAL::isa($unitick,'RT::Ticket'));
+ok ($unitick->Id, "found ticket ".$unitick->Id);
+ok ($unitick->Subject eq 'This is a test of I18N ticket creation', "Created the ticket - ". $unitick->Subject);
+
+
+
+my $unistring = "\303\241\303\251\303\255\303\263\303\272";
+Encode::_utf8_on($unistring);
+is ($unitick->Transactions->First->Content, $unitick->Transactions->First->Attachments->First->Content, "Content is ". $unitick->Transactions->First->Attachments->First->Content);
+ok($unitick->Transactions->First->Attachments->First->Content =~ /$unistring/i, $unitick->Id." appears to be unicode ". $unitick->Transactions->First->Attachments->First->Id);
+# supposedly I18N fails on the second message sent in.
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue general --action correspond"), "Opened the mailgate - $!");
+
+print MAIL <<EOF;
+From: root\@localhost
+To: rtemail\@$RT::rtname
+Subject: This is a test of I18N ticket creation
+Content-Type: text/plain; charset="utf-8"
+
+2 accented lines
+\303\242\303\252\303\256\303\264\303\273
+\303\241\303\251\303\255\303\263\303\272
+bye
+EOF
+close (MAIL);
+
+#Check the return value
+is ($? >> 8, 0, "The mail gateway exited normally. yay");
+
+my $tickets2 = RT::Tickets->new($RT::SystemUser);
+$tickets2->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets2->Limit(FIELD => 'id', OPERATOR => '>', VALUE => '0');
+my $tick2 = $tickets2->First();
+ok (UNIVERSAL::isa($tick2,'RT::Ticket'));
+ok ($tick2->Id, "found ticket ".$tick2->Id);
+ok ($tick2->Subject eq 'This is a test of I18N ticket creation', "Created the ticket");
+
+
+
+$unistring = "\303\241\303\251\303\255\303\263\303\272";
+Encode::_utf8_on($unistring);
+
+ok ($tick2->Transactions->First->Content =~ $unistring, "It appears to be unicode - ".$tick2->Transactions->First->Content);
+
+# }}}
+
+
+($val,$msg) = $g->PrincipalObj->RevokeRight(Right => 'CreateTicket');
+ok ($val, $msg);
+
+##=for later
+
+SKIP: {
+skip "Advanced mailgate actions require an unsafe configuration", 47 unless $RT::UnsafeEmailCommands;
+
+#create new queue to be shure we don't mess with rights
+use RT::Queue;
+my $queue = RT::Queue->new($RT::SystemUser);
+my ($qid) = $queue->Create( Name => 'ext-mailgate');
+ok( $qid, 'queue created for ext-mailgate tests' );
+
+# {{{ Check take and resolve actions
+
+# create ticket that is owned by nobody
+use RT::Ticket;
+$tick = RT::Ticket->new($RT::SystemUser);
+my ($id) = $tick->Create( Queue => 'ext-mailgate', Subject => 'test');
+ok( $id, 'new ticket created' );
+is( $tick->Owner, $RT::Nobody->Id, 'owner of the new ticket is nobody' );
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action take"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: root\@localhost
+Subject: [$RT::rtname \#$id] test
+
+EOF
+close (MAIL);
+is ($? >> 8, 0, "The mail gateway exited normally");
+
+$tick = RT::Ticket->new($RT::SystemUser);
+$tick->Load( $id );
+is( $tick->Id, $id, 'load correct ticket');
+is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket via email');
+
+# check that there is no text transactions writen
+is( $tick->Transactions->Count, 2, 'no superfluous transactions');
+
+my $status;
+($status, $msg) = $tick->SetOwner( $RT::Nobody->Id, 'Force' );
+ok( $status, 'successfuly changed owner: '. ($msg||'') );
+is( $tick->Owner, $RT::Nobody->Id, 'set owner back to nobody');
+
+
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $RT::WebURL --queue ext-mailgate --action take-correspond"), "Opened the mailgate - $@");
+print MAIL <<EOF;
+From: root\@localhost
+Subject: [$RT::rtname \#$id] correspondence
+
+test
+EOF
+close (MAIL);
+is ($? >> 8, 0, "The mail gateway exited normally");
+
+DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
+$tick = RT::Ticket->new($RT::SystemUser);
+$tick->Load( $id );
+is( $tick->Id, $id, "load correct ticket #$id");
+is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket via email');
+my $txns = $tick->Transactions;
+$txns->Limit( FIELD => 'Type', VALUE => 'Correspond');
+$txns->OrderBy( FIELD => 'id', ORDER => 'DESC' );
+# +1 because of auto open
+is( $tick->Transactions->Count, 6, 'no superfluous transactions');
+is( $txns->First->Subject, "[$RT::rtname \#$id] correspondence", 'successfuly add correspond within take via email' );
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action resolve --debug"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: root\@localhost
+Subject: [$RT::rtname \#$id] test
+
+EOF
+close (MAIL);
+is ($? >> 8, 0, "The mail gateway exited normally");
+
+DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
+$tick = RT::Ticket->new($RT::SystemUser);
+$tick->Load( $id );
+is( $tick->Id, $id, 'load correct ticket');
+is( $tick->Status, 'resolved', 'successfuly resolved ticket via email');
+is( $tick->Transactions->Count, 7, 'no superfluous transactions');
+
+use RT::User;
+my $user = RT::User->new( $RT::SystemUser );
+my ($uid) = $user->Create( Name => 'ext-mailgate',
+ EmailAddress => 'ext-mailgate@localhost',
+ Privileged => 1,
+ Password => 'qwe123',
+ );
+ok( $uid, 'user created for ext-mailgate tests' );
+ok( !$user->HasRight( Right => 'OwnTicket', Object => $queue ), "User can't own ticket" );
+
+$tick = RT::Ticket->new($RT::SystemUser);
+($id) = $tick->Create( Queue => $qid, Subject => 'test' );
+ok( $id, 'create new ticket' );
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action take"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: ext-mailgate\@localhost
+Subject: [example.com \#$id] test
+
+EOF
+close (MAIL);
+is ( $? >> 8, 0, "mailgate exited normally" );
+DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
+cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" );
+
+($status, $msg) = $user->PrincipalObj->GrantRight( Object => $queue, Right => 'ReplyToTicket' );
+ok( $status, "successfuly granted right: $msg" );
+my $ace_id = $status;
+ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can reply to ticket" );
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action correspond-take"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: ext-mailgate\@localhost
+Subject: [example.com \#$id] test
+
+correspond-take
+EOF
+close (MAIL);
+is ( $? >> 8, 0, "mailgate exited normally" );
+DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
+cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" );
+is( $tick->Transactions->Count, 3, "one transactions added" );
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action take-correspond"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: ext-mailgate\@localhost
+Subject: [example.com \#$id] test
+
+correspond-take
+EOF
+close (MAIL);
+is ( $? >> 8, 0, "mailgate exited normally" );
+DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
+cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" );
+is( $tick->Transactions->Count, 3, "no transactions added, user can't take ticket first" );
+
+# revoke ReplyToTicket right
+use RT::ACE;
+my $ace = RT::ACE->new($RT::SystemUser);
+$ace->Load( $ace_id );
+$ace->Delete;
+my $acl = RT::ACL->new($RT::SystemUser);
+$acl->Limit( FIELD => 'RightName', VALUE => 'ReplyToTicket' );
+$acl->LimitToObject( $RT::System );
+while( my $ace = $acl->Next ) {
+ $ace->Delete;
+}
+
+ok( !$user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can't reply to ticket any more" );
+
+
+my $group = RT::Group->new( $RT::SystemUser );
+ok( $group->LoadQueueRoleGroup( Queue => $qid, Type=> 'Owner' ), "load queue owners role group" );
+$ace = RT::ACE->new( $RT::SystemUser );
+($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ReplyToTicket', Object => $queue );
+ok( $ace_id, "Granted queue owners role group with ReplyToTicket right" );
+
+($status, $msg) = $user->PrincipalObj->GrantRight( Object => $queue, Right => 'OwnTicket' );
+ok( $status, "successfuly granted right: $msg" );
+($status, $msg) = $user->PrincipalObj->GrantRight( Object => $queue, Right => 'TakeTicket' );
+ok( $status, "successfuly granted right: $msg" );
+
+$! = 0;
+ok(open(MAIL, "|$RT::BinPath/rt-mailgate --url $url --queue ext-mailgate --action take-correspond"), "Opened the mailgate - $!");
+print MAIL <<EOF;
+From: ext-mailgate\@localhost
+Subject: [example.com \#$id] test
+
+take-correspond with reply right granted to owner role
+EOF
+close (MAIL);
+is ( $? >> 8, 0, "mailgate exited normally" );
+DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
+$tick->Load( $id );
+is( $tick->Owner, $user->id, "we changed owner" );
+ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "owner can reply to ticket" );
+is( $tick->Transactions->Count, 5, "transactions added" );
+
+
+# }}}
+};
+
+
+1;
+
diff --git a/rt/lib/t/regression/07acl.t b/rt/lib/t/regression/07acl.t
new file mode 100644
index 0000000..efd8701
--- /dev/null
+++ b/rt/lib/t/regression/07acl.t
@@ -0,0 +1,138 @@
+#!/usr/bin/perl -w
+use strict;
+use WWW::Mechanize;
+use HTTP::Cookies;
+
+use Test::More tests => 34;
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+# Create a user with basically no rights, to start.
+my $user_obj = RT::User->new($RT::SystemUser);
+my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('customer-'.$$.'@example.com');
+ok($ret, 'ACL test user creation');
+$user_obj->SetName('customer-'.$$);
+$user_obj->SetPrivileged(1);
+($ret, $msg) = $user_obj->SetPassword('customer');
+ok($ret, "ACL test password set. $msg");
+
+# Now test the web interface, making sure objects come and go as
+# required.
+
+
+my $cookie_jar = HTTP::Cookies->new;
+my $agent = WWW::Mechanize->new();
+
+# give the agent a place to stash the cookies
+
+$agent->cookie_jar($cookie_jar);
+
+no warnings 'once';
+# get the top page
+login($agent, $user_obj);
+
+# Test for absence of Configure and Preferences tabs.
+ok(!$agent->find_link( url => $RT::WebPath . "/Admin/",
+ text => 'Configuration'), "No config tab" );
+ok(!$agent->find_link( url => $RT::WebPath . "/User/Prefs.html",
+ text => 'Preferences'), "No prefs pane" );
+
+# Now test for their presence, one at a time. Sleep for a bit after
+# ACL changes, thanks to the 10s ACL cache.
+my ($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ShowConfigTab', Object => $RT::System);
+
+ok($grantid,$grantmsg);
+
+$agent->reload;
+
+ok($agent->{'content'} =~ /Logout/i, "Reloaded page successfully");
+ok($agent->find_link( url => $RT::WebPath . "/Admin/",
+ text => 'Configuration'), "Found config tab" );
+my ($revokeid,$revokemsg) =$user_obj->PrincipalObj->RevokeRight(Right => 'ShowConfigTab');
+ok ($revokeid,$revokemsg);
+($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf');
+ok ($grantid,$grantmsg);
+$agent->reload();
+ok($agent->{'content'} =~ /Logout/i, "Reloaded page successfully");
+ok($agent->find_link( url => $RT::WebPath . "/User/Prefs.html",
+ text => 'Preferences'), "Found prefs pane" );
+($revokeid,$revokemsg) = $user_obj->PrincipalObj->RevokeRight(Right => 'ModifySelf');
+ok ($revokeid,$revokemsg);
+# Good. Now load the search page and test Load/Save Search.
+$agent->follow_link( url => $RT::WebPath . "/Search/Build.html",
+ text => 'Tickets');
+is($agent->{'status'}, 200, "Fetched search builder page");
+ok($agent->{'content'} !~ /Load saved search/i, "No search loading box");
+ok($agent->{'content'} !~ /Saved searches/i, "No saved searches box");
+
+($grantid,$grantmsg) = $user_obj->PrincipalObj->GrantRight(Right => 'LoadSavedSearch');
+ok($grantid,$grantmsg);
+$agent->reload();
+ok($agent->{'content'} =~ /Load saved search/i, "Search loading box exists");
+ok($agent->{'content'} !~ /input\s+type=.submit.\s+name=.Save./i,
+ "Still no saved searches box");
+
+($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'CreateSavedSearch');
+ok ($grantid,$grantmsg);
+$agent->reload();
+ok($agent->{'content'} =~ /Load saved search/i,
+ "Search loading box still exists");
+ok($agent->{'content'} =~ /input\s+type=.submit.\s+name=.Save./i,
+ "Saved searches box exists");
+
+# Create a group, and a queue, so we can test limited user visibility
+# via SelectOwner.
+
+my $queue_obj = RT::Queue->new($RT::SystemUser);
+($ret, $msg) = $queue_obj->Create(Name => 'CustomerQueue-'.$$,
+ Description => 'queue for SelectOwner testing');
+ok($ret, "SelectOwner test queue creation. $msg");
+my $group_obj = RT::Group->new($RT::SystemUser);
+($ret, $msg) = $group_obj->CreateUserDefinedGroup(Name => 'CustomerGroup-'.$$,
+ Description => 'group for SelectOwner testing');
+ok($ret, "SelectOwner test group creation. $msg");
+
+# Add our customer to the customer group, and give it queue rights.
+($ret, $msg) = $group_obj->AddMember($user_obj->PrincipalObj->Id());
+ok($ret, "Added customer to its group. $msg");
+($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'OwnTicket',
+ Object => $queue_obj);
+
+ok($grantid,$grantmsg);
+($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'SeeQueue',
+ Object => $queue_obj);
+ok ($grantid,$grantmsg);
+# Now. When we look at the search page we should be able to see
+# ourself in the list of possible owners.
+
+$agent->reload();
+ok($agent->form_name('BuildQuery'), "Yep, form is still there");
+my $input = $agent->current_form->find_input('ValueOfActor');
+ok(grep(/customer-$$/, $input->value_names()), "Found self in the actor listing");
+
+sub login {
+ my $agent = shift;
+
+ my $url = $RT::WebURL;
+ $agent->get($url);
+ is( $agent->{'status'}, 200,
+ "Loaded a page - $url" );
+
+ # {{{ test a login
+
+ # follow the link marked "Login"
+
+ ok( $agent->{form}->find_input('user') );
+
+ ok( $agent->{form}->find_input('pass') );
+ ok( $agent->{'content'} =~ /username:/i );
+ $agent->field( 'user' => $user_obj->Name );
+ $agent->field( 'pass' => 'customer' );
+
+ # the field isn't named, so we have to click link 0
+ $agent->click(0);
+ is( $agent->{'status'}, 200, "Fetched the page ok" );
+ ok( $agent->{'content'} =~ /Logout/i, "Found a logout link" );
+}
+1;
diff --git a/rt/lib/t/regression/07rights.t b/rt/lib/t/regression/07rights.t
new file mode 100644
index 0000000..6c35a07
--- /dev/null
+++ b/rt/lib/t/regression/07rights.t
@@ -0,0 +1,140 @@
+#!/usr/bin/perl -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+# <jesse.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+use Test::More tests => 26;
+use RT;
+RT::LoadConfig();
+RT::Init();
+use RT::I18N;
+use strict;
+no warnings 'once';
+
+use RT::Queue;
+use RT::ACE;
+use RT::User;
+use RT::Group;
+use RT::Ticket;
+
+
+# clear all global right
+my $acl = RT::ACL->new($RT::SystemUser);
+$acl->Limit( FIELD => 'RightName', OPERATOR => '!=', VALUE => 'SuperUser' );
+$acl->LimitToObject( $RT::System );
+while( my $ace = $acl->Next ) {
+ $ace->Delete;
+}
+
+# create new queue to be shure we don't mess with rights
+my $queue = RT::Queue->new($RT::SystemUser);
+my ($queue_id) = $queue->Create( Name => 'rights');
+ok( $queue_id, 'queue created for rights tests' );
+
+# new privileged user to check rights
+my $user = RT::User->new( $RT::SystemUser );
+my ($user_id) = $user->Create( Name => 'rights',
+ EmailAddress => 'rights@localhost',
+ Privileged => 1,
+ Password => 'qwe123',
+ );
+ok( !$user->HasRight( Right => 'OwnTicket', Object => $queue ), "user can't own ticket" );
+ok( !$user->HasRight( Right => 'ReplyToTicket', Object => $queue ), "user can't reply to ticket" );
+
+my $group = RT::Group->new( $RT::SystemUser );
+ok( $group->LoadQueueRoleGroup( Queue => $queue_id, Type=> 'Owner' ), "load queue owners role group" );
+my $ace = RT::ACE->new( $RT::SystemUser );
+my ($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ReplyToTicket', Object => $queue );
+ok( $ace_id, "Granted queue owners role group with ReplyToTicket right: $msg" );
+ok( $group->PrincipalObj->HasRight( Right => 'ReplyToTicket', Object => $queue ), "role group can reply to ticket" );
+ok( !$user->HasRight( Right => 'ReplyToTicket', Object => $queue ), "user can't reply to ticket" );
+
+# new ticket
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($ticket_id) = $ticket->Create( Queue => $queue_id, Subject => 'test');
+ok( $ticket_id, 'new ticket created' );
+is( $ticket->Owner, $RT::Nobody->Id, 'owner of the new ticket is nobody' );
+
+my $status;
+($status, $msg) = $user->PrincipalObj->GrantRight( Object => $queue, Right => 'OwnTicket' );
+ok( $status, "successfuly granted right: $msg" );
+ok( $user->HasRight( Right => 'OwnTicket', Object => $queue ), "user can own ticket" );
+
+($status, $msg) = $ticket->SetOwner( $user_id );
+ok( $status, "successfuly set owner: $msg" );
+is( $ticket->Owner, $user_id, "set correct owner" );
+
+ok( $user->HasRight( Right => 'ReplyToTicket', Object => $ticket ), "user is owner and can reply to ticket" );
+
+# Testing of EquivObjects
+$group = RT::Group->new( $RT::SystemUser );
+ok( $group->LoadQueueRoleGroup( Queue => $queue_id, Type=> 'AdminCc' ), "load queue AdminCc role group" );
+$ace = RT::ACE->new( $RT::SystemUser );
+($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ModifyTicket', Object => $queue );
+ok( $ace_id, "Granted queue AdminCc role group with ModifyTicket right: $msg" );
+ok( $group->PrincipalObj->HasRight( Right => 'ModifyTicket', Object => $queue ), "role group can modify ticket" );
+ok( !$user->HasRight( Right => 'ModifyTicket', Object => $ticket ), "user is not AdminCc and can't modify ticket" );
+($status, $msg) = $ticket->AddWatcher(Type => 'AdminCc', PrincipalId => $user->PrincipalId);
+ok( $status, "successfuly added user as AdminCc");
+ok( $user->HasRight( Right => 'ModifyTicket', Object => $ticket ), "user is AdminCc and can modify ticket" );
+
+my $ticket2 = RT::Ticket->new($RT::SystemUser);
+my ($ticket2_id) = $ticket2->Create( Queue => $queue_id, Subject => 'test2');
+ok( $ticket2_id, 'new ticket created' );
+ok( !$user->HasRight( Right => 'ModifyTicket', Object => $ticket2 ), "user is not AdminCc and can't modify ticket2" );
+
+# now we can finally test EquivObjects
+my $equiv = [ $ticket ];
+ok( $user->HasRight( Right => 'ModifyTicket', Object => $ticket2, EquivObjects => $equiv ),
+ "user is not AdminCc but can modify ticket2 because of EquivObjects" );
+
+# the first a third test below are the same, so they should both pass
+my $equiv2 = [];
+ok( !$user->HasRight( Right => 'ModifyTicket', Object => $ticket2, EquivObjects => $equiv2 ),
+ "user is not AdminCc and can't modify ticket2" );
+ok( $user->HasRight( Right => 'ModifyTicket', Object => $ticket, EquivObjects => $equiv2 ),
+ "user is AdminCc and can modify ticket" );
+ok( !$user->HasRight( Right => 'ModifyTicket', Object => $ticket2, EquivObjects => $equiv2 ),
+ "user is not AdminCc and can't modify ticket2 (same question different answer)" );
diff --git a/rt/lib/t/regression/08web_cf_access.t b/rt/lib/t/regression/08web_cf_access.t
new file mode 100644
index 0000000..c352bbc
--- /dev/null
+++ b/rt/lib/t/regression/08web_cf_access.t
@@ -0,0 +1,119 @@
+#!/usr/bin/perl -w
+use strict;
+
+use Test::More tests => 15;
+BEGIN {
+ use RT;
+ RT::LoadConfig;
+ RT::Init;
+}
+use Test::WWW::Mechanize;
+
+use constant BaseURL => $RT::WebURL;
+use constant ImageFile => $RT::MasonComponentRoot .'/NoAuth/images/bplogo.gif';
+use constant ImageFileContent => do {
+ local $/;
+ open my $fh, '<', ImageFile or die $!;
+ binmode($fh);
+ scalar <$fh>;
+};
+
+my $m = Test::WWW::Mechanize->new;
+isa_ok($m, 'Test::WWW::Mechanize');
+
+$m->get( BaseURL."?user=root;pass=password" );
+$m->content_like(qr/Logout/, 'we did log in');
+$m->follow_link( text => 'Configuration' );
+$m->title_is(q/RT Administration/, 'admin screen');
+$m->follow_link( text => 'Custom Fields' );
+$m->title_is(q/Select a Custom Field/, 'admin-cf screen');
+$m->follow_link( text => 'New custom field' );
+$m->submit_form(
+ form_name => "ModifyCustomField",
+ fields => {
+ TypeComposite => 'Image-0',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Name => 'img',
+ Description => 'img',
+ },
+);
+$m->title_is(q/Created CustomField img/, 'admin-cf created');
+$m->follow_link( text => 'Queues' );
+$m->title_is(q/Admin queues/, 'admin-queues screen');
+$m->follow_link( text => 'General' );
+$m->title_is(q/Editing Configuration for queue General/, 'admin-queue: general');
+$m->follow_link( text => 'Ticket Custom Fields' );
+
+$m->title_is(q/Edit Custom Fields for General/, 'admin-queue: general tcf');
+$m->form_name('EditCustomFields');
+
+# Sort by numeric IDs in names
+my @names = map { $_->[1] }
+ sort { $a->[0] <=> $b->[0] }
+ map { /Object-1-CF-(\d+)/ ? [ $1 => $_ ] : () }
+ map $_->name, $m->current_form->inputs;
+my $tcf = pop(@names);
+$m->field( $tcf => 1 ); # Associate the new CF with this queue
+$m->field( $_ => undef ) for @names; # ...and not any other. ;-)
+$m->submit;
+
+$m->content_like( qr/Object created/, 'TCF added to the queue' );
+
+$m->submit_form(
+ form_name => "CreateTicketInQueue",
+ fields => { Queue => 'General' },
+);
+
+$m->content_like(qr/Upload multiple images/, 'has a upload image field');
+
+$tcf =~ /(\d+)$/ or die "Hey this is impossible dude";
+my $upload_field = "Object-RT::Ticket--CustomField-$1-Upload";
+
+$m->submit_form(
+ form_name => "TicketCreate",
+ fields => {
+ $upload_field => ImageFile,
+ Subject => 'testing img cf creation',
+ },
+);
+
+$m->content_like(qr/Ticket \d+ created/, "a ticket is created succesfully");
+
+my $id = $1 if $m->content =~ /Ticket (\d+) created/;
+
+$m->title_like(qr/testing img cf creation/, "its title is the Subject");
+
+$m->follow_link( text => 'bplogo.gif' );
+$m->content_is(ImageFileContent, "it links to the uploaded image");
+
+$m->get( BaseURL );
+
+$m->follow_link( text => 'Tickets' );
+$m->follow_link( text => 'New Query' );
+
+$m->title_is(q/Query Builder/, 'Query building');
+$m->submit_form(
+ form_name => "BuildQuery",
+ fields => {
+ idOp => '=',
+ ValueOfid => $id,
+ ValueOfQueue => 'General',
+ },
+ button => 'AddClause',
+);
+
+$m->form_name('BuildQuery');
+
+my $col = ($m->current_form->find_input('SelectDisplayColumns'))[-1];
+$col->value( ($col->possible_values)[-1] );
+
+$m->click('AddCol');
+
+$m->form_name('BuildQuery');
+$m->click('DoSearch');
+
+$m->follow_link( text_regex => qr/bplogo\.gif/ );
+$m->content_is(ImageFileContent, "it links to the uploaded image");
+
+__END__
+[FC] Bulk Update does not have custom fields.
diff --git a/rt/lib/t/regression/09record_cf_api.t b/rt/lib/t/regression/09record_cf_api.t
new file mode 100644
index 0000000..78f111b
--- /dev/null
+++ b/rt/lib/t/regression/09record_cf_api.t
@@ -0,0 +1,204 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings FATAL => 'all';
+use Test::More tests => 133;
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+# Before we get going, ditch all object_cfs; this will remove
+# all custom fields systemwide;
+my $object_cfs = RT::ObjectCustomFields->new($RT::SystemUser);
+$object_cfs->UnLimit();
+while (my $ocf = $object_cfs->Next) {
+ $ocf->Delete();
+}
+
+
+my $queue = RT::Queue->new( $RT::SystemUser );
+$queue->Create( Name => 'RecordCustomFields-'.$$ );
+ok ($queue->id, "Created the queue");
+
+my $queue2 = RT::Queue->new( $RT::SystemUser );
+$queue2->Create( Name => 'RecordCustomFields2' );
+
+my $ticket = RT::Ticket->new( $RT::SystemUser );
+$ticket->Create(
+ Queue => $queue->Id,
+ Requestor => 'root@localhost',
+ Subject => 'RecordCustomFields1',
+);
+
+my $cfs = $ticket->CustomFields;
+is( $cfs->Count, 0 );
+
+# Check that record has no any CF values yet {{{
+my $cfvs = $ticket->CustomFieldValues;
+is( $cfvs->Count, 0 );
+is( $ticket->FirstCustomFieldValue, undef );
+
+my $local_cf1 = RT::CustomField->new( $RT::SystemUser );
+$local_cf1->Create( Name => 'RecordCustomFields1-'.$$, Type => 'SelectSingle', Queue => $queue->id );
+$local_cf1->AddValue( Name => 'RecordCustomFieldValues11' );
+$local_cf1->AddValue( Name => 'RecordCustomFieldValues12' );
+
+my $local_cf2 = RT::CustomField->new( $RT::SystemUser );
+$local_cf2->Create( Name => 'RecordCustomFields2-'.$$, Type => 'SelectSingle', Queue => $queue->id );
+$local_cf2->AddValue( Name => 'RecordCustomFieldValues21' );
+$local_cf2->AddValue( Name => 'RecordCustomFieldValues22' );
+
+my $global_cf3 = RT::CustomField->new( $RT::SystemUser );
+$global_cf3->Create( Name => 'RecordCustomFields3-'.$$, Type => 'SelectSingle', Queue => 0 );
+$global_cf3->AddValue( Name => 'RecordCustomFieldValues31' );
+$global_cf3->AddValue( Name => 'RecordCustomFieldValues32' );
+
+my $local_cf4 = RT::CustomField->new( $RT::SystemUser );
+$local_cf4->Create( Name => 'RecordCustomFields4', Type => 'SelectSingle', Queue => $queue2->id );
+$local_cf4->AddValue( Name => 'RecordCustomFieldValues41' );
+$local_cf4->AddValue( Name => 'RecordCustomFieldValues42' );
+
+
+my @custom_fields = ($local_cf1, $local_cf2, $global_cf3);
+
+
+$cfs = $ticket->CustomFields;
+is( $cfs->Count, 3 );
+
+# Check that record has no any CF values yet {{{
+$cfvs = $ticket->CustomFieldValues;
+is( $cfvs->Count, 0 );
+is( $ticket->FirstCustomFieldValue, undef );
+
+# CF with ID -1 shouldnt exist at all
+$cfvs = $ticket->CustomFieldValues( -1 );
+is( $cfvs->Count, 0 );
+is( $ticket->FirstCustomFieldValue( -1 ), undef );
+
+$cfvs = $ticket->CustomFieldValues( 'SomeUnexpedCustomFieldName' );
+is( $cfvs->Count, 0 );
+is( $ticket->FirstCustomFieldValue( 'SomeUnexpedCustomFieldName' ), undef );
+
+for (@custom_fields) {
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 0 );
+
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 0 );
+ is( $ticket->FirstCustomFieldValue( $_->id ), undef );
+ is( $ticket->FirstCustomFieldValue( $_->Name ), undef );
+}
+# }}}
+
+# try to add field value with fields that do not exist {{{
+my ($status, $msg) = $ticket->AddCustomFieldValue( Field => -1 , Value => 'foo' );
+ok(!$status, "shouldn't add value" );
+($status, $msg) = $ticket->AddCustomFieldValue( Field => 'SomeUnexpedCustomFieldName' , Value => 'foo' );
+ok(!$status, "shouldn't add value" );
+# }}}
+
+# {{{
+SKIP: {
+
+ skip "TODO: We want fields that are not allowed to set unexpected values", 10;
+ for (@custom_fields) {
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'SomeUnexpectedCFValue' );
+ ok( !$status, 'value doesn\'t exist');
+
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->id , Value => 'SomeUnexpectedCFValue' );
+ ok( !$status, 'value doesn\'t exist');
+
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_->Name , Value => 'SomeUnexpectedCFValue' );
+ ok( !$status, 'value doesn\'t exist');
+ }
+
+ # Let check that we did not add value to be sure
+ # using only FirstCustomFieldValue sub because
+ # we checked other variants allready
+ for (@custom_fields) {
+ is( $ticket->FirstCustomFieldValue( $_->id ), undef );
+ }
+
+}
+# Add some values to our custom fields
+for (@custom_fields) {
+ # this should be tested elsewhere
+ $_->AddValue( Name => 'Foo' );
+ $_->AddValue( Name => 'Bar' );
+}
+
+my $test_add_delete_cycle = sub {
+ my $cb = shift;
+ for (@custom_fields) {
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $cb->($_) , Value => 'Foo' );
+ ok( $status, "message: $msg");
+ }
+
+ # does it exist?
+ $cfvs = $ticket->CustomFieldValues;
+ is( $cfvs->Count, 3, "We found all three custom fields on our ticket" );
+ for (@custom_fields) {
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 1 , "we found one custom field when searching by id");
+
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 1 , " We found one custom field when searching by name for " . $_->Name);
+ is( $ticket->FirstCustomFieldValue( $_->id ), 'Foo' , "first value by id is foo");
+ is( $ticket->FirstCustomFieldValue( $_->Name ), 'Foo' , "first value by name is foo");
+ }
+ # because our CFs are SingleValue then new value addition should override
+ for (@custom_fields) {
+ ($status, $msg) = $ticket->AddCustomFieldValue( Field => $_ , Value => 'Bar' );
+ ok( $status, "message: $msg");
+ }
+ $cfvs = $ticket->CustomFieldValues;
+ is( $cfvs->Count, 3 );
+ for (@custom_fields) {
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 1 );
+
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 1 );
+ is( $ticket->FirstCustomFieldValue( $_->id ), 'Bar' );
+ is( $ticket->FirstCustomFieldValue( $_->Name ), 'Bar' );
+ }
+ # delete it
+ for (@custom_fields ) {
+ ($status, $msg) = $ticket->DeleteCustomFieldValue( Field => $_ , Value => 'Bar' );
+ ok( $status, "Deleted a custom field value 'Bar' for field ".$_->id.": $msg");
+ }
+ $cfvs = $ticket->CustomFieldValues;
+ is( $cfvs->Count, 0, "The ticket (".$ticket->id.") no longer has any custom field values" );
+ for (@custom_fields) {
+ $cfvs = $ticket->CustomFieldValues( $_->id );
+ is( $cfvs->Count, 0, $ticket->id." has no values for cf ".$_->id );
+
+ $cfvs = $ticket->CustomFieldValues( $_->Name );
+ is( $cfvs->Count, 0 , $ticket->id." has no values for cf '".$_->Name. "'" );
+ is( $ticket->FirstCustomFieldValue( $_->id ), undef , "There is no first custom field value when loading by id" );
+ is( $ticket->FirstCustomFieldValue( $_->Name ), undef, "There is no first custom field value when loading by Name" );
+ }
+};
+
+# lets test cycle via CF id
+$test_add_delete_cycle->( sub { return $_[0]->id } );
+# lets test cycle via CF object reference
+$test_add_delete_cycle->( sub { return $_[0] } );
+
+$ticket->AddCustomFieldValue( Field => $local_cf2->id , Value => 'Baz' );
+$ticket->AddCustomFieldValue( Field => $global_cf3->id , Value => 'Baz' );
+# now if we ask for cf values on RecordCustomFields4 we should not get any
+$cfvs = $ticket->CustomFieldValues( 'RecordCustomFields4' );
+is( $cfvs->Count, 0, "No custom field values for non-Queue cf" );
+is( $ticket->FirstCustomFieldValue( 'RecordCustomFields4' ), undef, "No first custom field value for non-Queue cf" );
+
+
+#SKIP: {
+# skip "TODO: should we add CF values to objects via CF Name?", 48;
+# names are not unique
+ # lets test cycle via CF Name
+# $test_add_delete_cycle->( sub { return $_[0]->Name } );
+#}
+
+
diff --git a/rt/lib/t/regression/10merge.t b/rt/lib/t/regression/10merge.t
new file mode 100644
index 0000000..8bca952
--- /dev/null
+++ b/rt/lib/t/regression/10merge.t
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+
+#
+# This test script validates that when merging two tickets, the comments from both tickets
+# are integrated into the new ticket
+
+use Test::More tests => 13;
+use RT;
+RT::LoadConfig;
+RT::Init;
+
+use_ok('RT::Ticket');
+use_ok('RT::Queue');
+
+my $queue = RT::Queue->new($RT::SystemUser);
+my ($id,$msg) = $queue->Create(Name => 'MergeTest-'.rand(25));
+ok ($id,$msg);
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ($tid,$transid, $t1msg) =$t1->Create ( Queue => $queue->Name, Subject => 'Merge test. orig');
+ok ($tid, $t1msg);
+($id, $msg) = $t1->Comment(Content => 'This is a Comment on the original');
+ok($id,$msg);
+
+my $txns = $t1->Transactions;
+my $Comments = 0;
+while (my $txn = $txns->Next) {
+$Comments++ if ($txn->Type eq 'Comment');
+}
+is($Comments,1, "our first ticket has only one Comment");
+
+my $t2 = RT::Ticket->new($RT::SystemUser);
+my ($t2id,$t2transid, $t2msg) =$t2->Create ( Queue => $queue->Name, Subject => 'Merge test. duplicate');
+ok ($t2id, $t2msg);
+
+
+
+($id, $msg) = $t2->Comment(Content => 'This is a commet on the duplicate');
+ok($id,$msg);
+
+
+$txns = $t2->Transactions;
+ $Comments = 0;
+while (my $txn = $txns->Next) {
+ $Comments++ if ($txn->Type eq 'Comment');
+}
+is($Comments,1, "our second ticket has only one Comment");
+
+($id, $msg) = $t1->Comment(Content => 'This is a second Comment on the original');
+ok($id,$msg);
+
+$txns = $t1->Transactions;
+$Comments = 0;
+while (my $txn = $txns->Next) {
+ $Comments++ if ($txn->Type eq 'Comment');
+}
+is($Comments,2, "our first ticket now has two Comments");
+
+($id,$msg) = $t2->MergeInto($t1->id);
+
+ok($id,$msg);
+$txns = $t1->Transactions;
+$Comments = 0;
+while (my $txn = $txns->Next) {
+ $Comments++ if ($txn->Type eq 'Comment');
+}
+is($Comments,3, "our first ticket now has three Comments - we merged safely");
+
diff --git a/rt/lib/t/regression/11-template-insert.t b/rt/lib/t/regression/11-template-insert.t
new file mode 100644
index 0000000..8681ce6
--- /dev/null
+++ b/rt/lib/t/regression/11-template-insert.t
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More tests => 7;
+
+use RT;
+RT::LoadConfig();
+RT::Init;
+
+
+# This tiny little test script triggers an interaction bug between DBD::Oracle 1.16, SB 1.15 and RT 3.4
+
+use_ok('RT::Template');
+my $template = RT::Template->new($RT::SystemUser);
+
+isa_ok($template, 'RT::Template');
+my ($val,$msg) = $template->Create(Queue => 1,
+ Name => 'InsertTest',
+ Content => 'This is template content');
+ok($val,$msg);
+is($template->Name, 'InsertTest');
+is($template->Content, 'This is template content', "We created the object right");
+($val, $msg) = $template->SetContent( 'This is new template content');
+ok($val,$msg);
+is($template->Content, 'This is new template content', "We managed to _Set_ the content");
diff --git a/rt/lib/t/regression/12-search.t b/rt/lib/t/regression/12-search.t
new file mode 100644
index 0000000..210d4fe
--- /dev/null
+++ b/rt/lib/t/regression/12-search.t
@@ -0,0 +1,266 @@
+#!/opt/perl/bin/perl -w
+
+# tests relating to searching. Especially around custom fields, and
+# corner cases.
+
+use strict;
+use warnings;
+
+use Test::More tests => 44;
+use_ok('RT');
+RT::LoadConfig();
+RT::Init();
+
+# setup the queue
+
+my $q = RT::Queue->new($RT::SystemUser);
+my $queue = 'SearchTests-'.$$;
+$q->Create(Name => $queue);
+ok ($q->id, "Created the queue");
+
+
+# and setup the CFs
+# we believe the Type shouldn't matter.
+
+my $cf = RT::CustomField->new($RT::SystemUser);
+$cf->Create(Name => 'SearchTest', Type => 'Freeform', MaxValues => 0, Queue => $q->id);
+ok($cf->id, "Created the SearchTest CF");
+my $cflabel = "CustomField-".$cf->id;
+
+my $cf2 = RT::CustomField->new($RT::SystemUser);
+$cf2->Create(Name => 'SearchTest2', Type => 'Freeform', MaxValues => 0, Queue => $q->id);
+ok($cf2->id, "Created the SearchTest2 CF");
+my $cflabel2 = "CustomField-".$cf2->id;
+
+my $cf3 = RT::CustomField->new($RT::SystemUser);
+$cf3->Create(Name => 'SearchTest3', Type => 'Freeform', MaxValues => 0, Queue => $q->id);
+ok($cf3->id, "Created the SearchTest3 CF");
+my $cflabel3 = "CustomField-".$cf3->id;
+
+
+# There was a bug involving a missing join to ObjectCustomFields that
+# caused spurious results on negative searches if another custom field
+# with the same name existed on a different queue. Hence, we make
+# duplicate CFs on a different queue here
+my $dup = RT::Queue->new($RT::SystemUser);
+$dup->Create(Name => $queue . "-Copy");
+ok ($dup->id, "Created the duplicate queue");
+my $dupcf = RT::CustomField->new($RT::SystemUser);
+$dupcf->Create(Name => 'SearchTest', Type => 'Freeform', MaxValues => 0, Queue => $dup->id);
+ok($dupcf->id, "Created the duplicate SearchTest CF");
+$dupcf = RT::CustomField->new($RT::SystemUser);
+$dupcf->Create(Name => 'SearchTest2', Type => 'Freeform', MaxValues => 0, Queue => $dup->id);
+ok($dupcf->id, "Created the SearchTest2 CF");
+$dupcf = RT::CustomField->new($RT::SystemUser);
+$dupcf->Create(Name => 'SearchTest3', Type => 'Freeform', MaxValues => 0, Queue => $dup->id);
+ok($dupcf->id, "Created the SearchTest3 CF");
+
+
+# setup some tickets
+# we'll need a small pile of them, to test various combinations and nulls.
+# there's probably a way to think harder and do this with fewer
+
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ( $id, undef $msg ) = $t1->Create(
+ Queue => $q->id,
+ Subject => 'SearchTest1',
+ Requestor => ['search1@example.com'],
+ $cflabel => 'foo1',
+ $cflabel2 => 'bar1',
+ $cflabel3 => 'qux1',
+);
+ok( $id, $msg );
+
+
+my $t2 = RT::Ticket->new($RT::SystemUser);
+( $id, undef, $msg ) = $t2->Create(
+ Queue => $q->id,
+ Subject => 'SearchTest2',
+ Requestor => ['search2@example.com'],
+# $cflabel => 'foo2',
+ $cflabel2 => 'bar2',
+ $cflabel3 => 'qux2',
+);
+ok( $id, $msg );
+
+my $t3 = RT::Ticket->new($RT::SystemUser);
+( $id, undef, $msg ) = $t3->Create(
+ Queue => $q->id,
+ Subject => 'SearchTest3',
+ Requestor => ['search3@example.com'],
+ $cflabel => 'foo3',
+# $cflabel2 => 'bar3',
+ $cflabel3 => 'qux3',
+);
+ok( $id, $msg );
+
+my $t4 = RT::Ticket->new($RT::SystemUser);
+( $id, undef, $msg ) = $t4->Create(
+ Queue => $q->id,
+ Subject => 'SearchTest4',
+ Requestor => ['search4@example.com'],
+ $cflabel => 'foo4',
+ $cflabel2 => 'bar4',
+# $cflabel3 => 'qux4',
+);
+ok( $id, $msg );
+
+my $t5 = RT::Ticket->new($RT::SystemUser);
+( $id, undef, $msg ) = $t5->Create(
+ Queue => $q->id,
+# Subject => 'SearchTest5',
+ Requestor => ['search5@example.com'],
+ $cflabel => 'foo5',
+ $cflabel2 => 'bar5',
+ $cflabel3 => 'qux5',
+);
+ok( $id, $msg );
+
+my $t6 = RT::Ticket->new($RT::SystemUser);
+( $id, undef, $msg ) = $t6->Create(
+ Queue => $q->id,
+ Subject => 'SearchTest6',
+# Requestor => ['search6@example.com'],
+ $cflabel => 'foo6',
+ $cflabel2 => 'bar6',
+ $cflabel3 => 'qux6',
+);
+ok( $id, $msg );
+
+my $t7 = RT::Ticket->new($RT::SystemUser);
+( $id, undef, $msg ) = $t7->Create(
+ Queue => $q->id,
+ Subject => 'SearchTest7',
+ Requestor => ['search7@example.com'],
+# $cflabel => 'foo7',
+# $cflabel2 => 'bar7',
+ $cflabel3 => 'qux7',
+);
+ok( $id, $msg );
+
+# we have tickets. start searching
+my $tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue'");
+is($tix->Count, 7, "found all the tickets");
+
+
+# very simple searches. both CF and normal
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND CF.SearchTest = 'foo1'");
+is($tix->Count, 1, "matched identical subject");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND CF.SearchTest LIKE 'foo1'");
+is($tix->Count, 1, "matched LIKE subject");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND CF.SearchTest = 'foo'");
+is($tix->Count, 0, "IS a regexp match");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND CF.SearchTest LIKE 'foo'");
+is($tix->Count, 5, "matched LIKE subject");
+
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND CF.SearchTest IS NULL");
+is($tix->Count, 2, "IS null CF");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Requestors LIKE 'search1'");
+is($tix->Count, 1, "LIKE requestor");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Requestors = 'search1\@example.com'");
+is($tix->Count, 1, "IS requestor");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Requestors LIKE 'search'");
+is($tix->Count, 6, "LIKE requestor");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Requestors IS NULL");
+is($tix->Count, 1, "Search for no requestor");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Subject = 'SearchTest1'");
+is($tix->Count, 1, "IS subject");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Subject LIKE 'SearchTest1'");
+is($tix->Count, 1, "LIKE subject");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Subject = ''");
+is($tix->Count, 1, "found one ticket");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Subject LIKE 'SearchTest'");
+is($tix->Count, 6, "found two ticket");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND Subject LIKE 'qwerty'");
+is($tix->Count, 0, "found zero ticket");
+
+
+
+
+# various combinations
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest LIKE 'foo' AND CF.SearchTest2 LIKE 'bar1'");
+is($tix->Count, 1, "LIKE cf and LIKE cf");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest = 'foo1' AND CF.SearchTest2 = 'bar1'");
+is($tix->Count, 1, "is cf and is cf");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest = 'foo' AND CF.SearchTest2 LIKE 'bar1'");
+is($tix->Count, 0, "is cf and like cf");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest LIKE 'foo' AND CF.SearchTest2 LIKE 'bar' AND CF.SearchTest3 LIKE 'qux'");
+is($tix->Count, 3, "like cf and like cf and like cf");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest LIKE 'foo' AND CF.SearchTest2 LIKE 'bar' AND CF.SearchTest3 LIKE 'qux6'");
+is($tix->Count, 1, "like cf and like cf and is cf");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest LIKE 'foo' AND Subject LIKE 'SearchTest'");
+is($tix->Count, 4, "like cf and like subject");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest IS NULL AND CF.SearchTest2 = 'bar2'");
+is($tix->Count, 1, "null cf and is cf");
+
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("Queue = '$queue' AND CF.SearchTest IS NULL AND CF.SearchTest2 IS NULL");
+is($tix->Count, 1, "null cf and null cf");
+
+# tests with the same CF listed twice
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.{SearchTest} = 'foo1'");
+is($tix->Count, 1, "is cf.{name} format");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest = 'foo1' OR CF.SearchTest = 'foo3'");
+is($tix->Count, 2, "is cf1 or is cf1");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest = 'foo1' OR CF.SearchTest IS NULL");
+is($tix->Count, 3, "is cf1 or null cf1");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("(CF.SearchTest = 'foo1' OR CF.SearchTest = 'foo3') AND (CF.SearchTest2 = 'bar1' OR CF.SearchTest2 = 'bar2')");
+is($tix->Count, 1, "(is cf1 or is cf1) and (is cf2 or is cf2)");
+
+$tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL("CF.SearchTest = 'foo1' OR CF.SearchTest = 'foo3' OR CF.SearchTest2 = 'bar1' OR CF.SearchTest2 = 'bar2'");
+is($tix->Count, 3, "is cf1 or is cf1 or is cf2 or is cf2");
+
diff --git a/rt/lib/t/regression/13-attribute-tests.t b/rt/lib/t/regression/13-attribute-tests.t
new file mode 100644
index 0000000..fdac94e
--- /dev/null
+++ b/rt/lib/t/regression/13-attribute-tests.t
@@ -0,0 +1,87 @@
+use strict;
+use warnings;
+use Test::More tests => 34;
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+
+my $runid = rand(200);
+
+my $attribute = "squelch-$runid";
+
+ok(require RT::Attributes);
+
+my $user = RT::User->new($RT::SystemUser);
+ok (UNIVERSAL::isa($user, 'RT::User'));
+my ($id,$msg) = $user->Create(Name => 'attrtest-'.$runid);
+ok ($id, $msg);
+ok($user->id, "Created a test user");
+
+ok(1, $user->Attributes->BuildSelectQuery);
+my $attr = $user->Attributes;
+# XXX: Order by id as some tests depend on it
+$attr->OrderByCols({ FIELD => 'id' });
+
+ok(1, $attr->BuildSelectQuery);
+
+
+ok (UNIVERSAL::isa($attr,'RT::Attributes'), 'got the attributes object');
+
+($id, $msg) = $user->AddAttribute(Name => 'TestAttr', Content => 'The attribute has content');
+ok ($id, $msg);
+is ($attr->Count,1, " One attr after adidng a first one");
+
+my $first_attr = $user->FirstAttribute('TestAttr');
+ok($first_attr, "got some sort of attribute");
+isa_ok($first_attr, 'RT::Attribute');
+is($first_attr->Content, 'The attribute has content', "got the right content back");
+
+($id, $msg) = $attr->DeleteEntry(Name => $runid);
+ok(!$id, "Deleted non-existant entry - $msg");
+is ($attr->Count,1, "1 attr after deleting an empty attr");
+
+my @names = $attr->Names;
+is ("@names", "TestAttr");
+
+
+($id, $msg) = $user->AddAttribute(Name => $runid, Content => "First");
+ok($id, $msg);
+
+my $runid_attr = $user->FirstAttribute($runid);
+ok($runid_attr, "got some sort of attribute");
+isa_ok($runid_attr, 'RT::Attribute');
+is($runid_attr->Content, 'First', "got the right content back");
+
+is ($attr->Count,2, " Two attrs after adding an attribute named $runid");
+($id, $msg) = $user->AddAttribute(Name => $runid, Content => "Second");
+ok($id, $msg);
+
+$runid_attr = $user->FirstAttribute($runid);
+ok($runid_attr, "got some sort of attribute");
+isa_ok($runid_attr, 'RT::Attribute');
+is($runid_attr->Content, 'First', "got the first content back still");
+
+is ($attr->Count,3, " Three attrs after adding a secondvalue to $runid");
+($id, $msg) = $attr->DeleteEntry(Name => $runid, Content => "First");
+ok($id, $msg);
+is ($attr->Count,2);
+
+#$attr->_DoSearch();
+($id, $msg) = $attr->DeleteEntry(Name => $runid, Content => "Second");
+ok($id, $msg);
+is ($attr->Count,1);
+
+#$attr->_DoSearch();
+ok(1, $attr->BuildSelectQuery);
+($id, $msg) = $attr->DeleteEntry(Name => "moose");
+ok(!$id, "Deleted non-existant entry - $msg");
+is ($attr->Count,1);
+
+ok(1, $attr->BuildSelectQuery);
+@names = $attr->Names;
+is("@names", "TestAttr");
+
+
+
+1;
diff --git a/rt/lib/t/regression/14linking.t b/rt/lib/t/regression/14linking.t
new file mode 100644
index 0000000..c8e57ea
--- /dev/null
+++ b/rt/lib/t/regression/14linking.t
@@ -0,0 +1,243 @@
+use Test::More tests => '70';
+use_ok('RT');
+use_ok('RT::Ticket');
+use_ok('RT::ScripConditions');
+use_ok('RT::ScripActions');
+use_ok('RT::Template');
+use_ok('RT::Scrips');
+use_ok('RT::Scrip');
+RT::LoadConfig();
+RT::Init();
+
+use File::Temp qw/tempfile/;
+my ($fh, $filename) = tempfile( UNLINK => 1, SUFFIX => '.rt');
+my $link_scrips_orig = $RT::LinkTransactionsRun1Scrip;
+my $link_acl_chacks_orig = $RT::StrictLinkACL;
+$RT::LinkTransactionsRun1Scrip = 1;
+$RT::StrictLinkACL = 1;
+
+my $condition = RT::ScripCondition->new( $RT::SystemUser );
+$condition->Load('User Defined');
+ok($condition->id);
+my $action = RT::ScripAction->new( $RT::SystemUser );
+$action->Load('User Defined');
+ok($action->id);
+my $template = RT::Template->new( $RT::SystemUser );
+$template->Load('Blank');
+ok($template->id);
+
+my $q1 = RT::Queue->new($RT::SystemUser);
+my ($id,$msg) = $q1->Create(Name => "LinkTest1.$$");
+ok ($id,$msg);
+my $q2 = RT::Queue->new($RT::SystemUser);
+($id,$msg) = $q2->Create(Name => "LinkTest2.$$");
+ok ($id,$msg);
+
+my $commit_code = <<END;
+open(FILE, "<$filename");
+my \$data = <FILE>;
+chomp \$data;
+close FILE;
+open(FILE, ">$filename");
+if (\$self->TransactionObj->Type eq 'AddLink') {
+ print FILE \$data+1, "\n";
+}
+else {
+ print FILE \$data-1, "\n";
+}
+close FILE;
+1;
+END
+
+my $Scrips = RT::Scrips->new( $RT::SystemUser );
+$Scrips->UnLimit;
+while ( my $Scrip = $Scrips->Next ) {
+ $Scrip->Delete if $Scrip->Description =~ /Add or Delete Link \d+/;
+}
+
+
+my $scrip = RT::Scrip->new($RT::SystemUser);
+($id,$msg) = $scrip->Create( Description => "Add or Delete Link $$",
+ ScripCondition => $condition->id,
+ ScripAction => $action->id,
+ Template => $template->id,
+ Stage => 'TransactionCreate',
+ Queue => 0,
+ CustomIsApplicableCode => '$self->TransactionObj->Type =~ /(Add|Delete)Link/;',
+ CustomPrepareCode => '1;',
+ CustomCommitCode => $commit_code,
+ );
+ok($id, "Scrip created");
+
+my $u1 = RT::User->new($RT::SystemUser);
+($id,$msg) = $u1->Create(Name => "LinkTestUser.$$");
+ok ($id,$msg);
+
+my $creator = RT::CurrentUser->new($u1->id);
+
+($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'CreateTicket');
+ok ($id,$msg);
+
+diag('Create tickets without rights to link') if $ENV{'TEST_VERBOSE'};
+{
+ # on q2 we have no rights, yet
+ my $parent = RT::Ticket->new( $RT::SystemUser );
+ ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
+ ok($id,$msg);
+ my $child = RT::Ticket->new( $creator );
+ ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id, MemberOf => $parent->id );
+ ok($id,$msg);
+ $child->CurrentUser( $RT::SystemUser );
+ is($child->_Links('Base')->Count, 0, 'link was not created, no permissions');
+ is($child->_Links('Target')->Count, 0, 'link was not create, no permissions');
+}
+
+diag('Create tickets with rights checks on one end of a link') if $ENV{'TEST_VERBOSE'};
+{
+ # on q2 we have no rights, but use checking one only on thing
+ local $RT::StrictLinkACL = 0;
+ my $parent = RT::Ticket->new( $RT::SystemUser );
+ ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
+ ok($id,$msg);
+ my $child = RT::Ticket->new( $creator );
+ ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id, MemberOf => $parent->id );
+ ok($id,$msg);
+ $child->CurrentUser( $RT::SystemUser );
+ is($child->_Links('Base')->Count, 1, 'link was created');
+ is($child->_Links('Target')->Count, 0, 'link was created only one');
+ # no scrip run on second ticket accroding to config option
+ is(link_count($filename), 0, "scrips ok");
+}
+
+($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'ModifyTicket');
+ok ($id,$msg);
+
+diag('try to add link without rights') if $ENV{'TEST_VERBOSE'};
+{
+ # on q2 we have no rights, yet
+ my $parent = RT::Ticket->new( $RT::SystemUser );
+ ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
+ ok($id,$msg);
+ my $child = RT::Ticket->new( $creator );
+ ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id );
+ ok($id,$msg);
+ my ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
+ ok(!$id, $msg);
+ is(link_count($filename), 0, "scrips ok");
+ $child->CurrentUser( $RT::SystemUser );
+ is($child->_Links('Base')->Count, 0, 'link was not created, no permissions');
+ is($child->_Links('Target')->Count, 0, 'link was not create, no permissions');
+}
+
+diag('add link with rights only on base') if $ENV{'TEST_VERBOSE'};
+{
+ # on q2 we have no rights, but use checking one only on thing
+ local $RT::StrictLinkACL = 0;
+ my $parent = RT::Ticket->new( $RT::SystemUser );
+ ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
+ ok($id,$msg);
+ my $child = RT::Ticket->new( $creator );
+ ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id );
+ ok($id,$msg);
+ my ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
+ ok($id, $msg);
+ is(link_count($filename), 1, "scrips ok");
+ $child->CurrentUser( $RT::SystemUser );
+ is($child->_Links('Base')->Count, 1, 'link was created');
+ is($child->_Links('Target')->Count, 0, 'link was created only one');
+ $child->CurrentUser( $creator );
+
+ # turn off feature and try to delete link, we should fail
+ $RT::StrictLinkACL = 1;
+ my ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
+ ok(!$id, $msg);
+ is(link_count($filename), 1, "scrips ok");
+ $child->CurrentUser( $RT::SystemUser );
+ $child->_Links('Base')->_DoCount;
+ is($child->_Links('Base')->Count, 1, 'link was not deleted');
+ $child->CurrentUser( $creator );
+
+ # try to delete link, we should success as feature is active
+ $RT::StrictLinkACL = 0;
+ my ($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id);
+ ok($id, $msg);
+ is(link_count($filename), 0, "scrips ok");
+ $child->CurrentUser( $RT::SystemUser );
+ $child->_Links('Base')->_DoCount;
+ is($child->_Links('Base')->Count, 0, 'link was deleted');
+}
+
+my $tid;
+my $ticket = RT::Ticket->new( $creator);
+ok($ticket->isa('RT::Ticket'));
+($id,$tid, $msg) = $ticket->Create(Subject => 'Link test 1', Queue => $q1->id);
+ok ($id,$msg);
+
+diag('try link to itself') if $ENV{'TEST_VERBOSE'};
+{
+ my ($id, $msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket->id);
+ ok(!$id, $msg);
+ is(link_count($filename), 0, "scrips ok");
+}
+
+my $ticket2 = RT::Ticket->new($RT::SystemUser);
+($id, $tid, $msg) = $ticket2->Create(Subject => 'Link test 2', Queue => $q2->id);
+ok ($id, $msg);
+($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
+ok(!$id,$msg);
+ok(link_count($filename) == 0, "scrips ok");
+
+($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'CreateTicket');
+ok ($id,$msg);
+($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'ModifyTicket');
+ok ($id,$msg);
+($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
+ok($id,$msg);
+ok(link_count($filename) == 1, "scrips ok");
+($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => -1);
+ok(!$id,$msg);
+ok(link_count($filename) == 1, "scrips ok");
+($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
+ok($id,$msg);
+is(link_count($filename), 1, "scrips ok");
+
+my $transactions = $ticket2->Transactions;
+$transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
+ok( $transactions->Count == 1, "Transaction found in other ticket" );
+ok( $transactions->First->Field eq 'ReferredToBy');
+ok( $transactions->First->NewValue eq $ticket->URI );
+
+($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id);
+ok($id,$msg);
+ok(link_count($filename) == 0, "scrips ok");
+$transactions = $ticket2->Transactions;
+$transactions->Limit( FIELD => 'Type', VALUE => 'DeleteLink' );
+ok( $transactions->Count == 1, "Transaction found in other ticket" );
+ok( $transactions->First->Field eq 'ReferredToBy');
+ok( $transactions->First->OldValue eq $ticket->URI );
+
+$RT::LinkTransactionsRun1Scrip = 0;
+
+($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
+ok($id,$msg);
+ok(link_count($filename) == 2, "scrips ok");
+($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id);
+ok($id,$msg);
+ok(link_count($filename) == 0, "scrips ok");
+
+# restore
+$RT::LinkTransactionsRun1Scrip = $link_scrips_orig;
+$RT::StrictLinkACL = $link_acl_checks_orig;
+
+exit(0);
+
+sub link_count {
+
+ my $file = shift;
+ open(FILE, "<$file");
+ my $data = <FILE>;
+ chomp $data;
+ return $data + 0;
+ close FILE;
+
+}
diff --git a/rt/lib/t/regression/14merge.t b/rt/lib/t/regression/14merge.t
new file mode 100644
index 0000000..c916251
--- /dev/null
+++ b/rt/lib/t/regression/14merge.t
@@ -0,0 +1,31 @@
+
+use Test::More tests => '6';
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+# when you try to merge duplicate links on postgres, eveyrything goes to hell due to referential integrity constraints.
+
+
+my $t = RT::Ticket->new($RT::SystemUser);
+$t->Create(Subject => 'Main', Queue => 'general');
+
+ok ($t->id);
+my $t2 = RT::Ticket->new($RT::SystemUser);
+$t2->Create(Subject => 'Second', Queue => 'general');
+ok ($t2->id);
+
+my $t3 = RT::Ticket->new($RT::SystemUser);
+$t3->Create(Subject => 'Third', Queue => 'general');
+
+ok ($t3->id);
+
+my ($id,$val);
+($id,$val) = $t->AddLink(Type => 'DependsOn', Target => $t3->id);
+ok($id,$val);
+($id,$val) = $t2->AddLink(Type => 'DependsOn', Target => $t3->id);
+ok($id,$val);
+
+
+($id,$val) = $t->MergeInto($t2->id);
+ok($id,$val);
diff --git a/rt/lib/t/regression/15cf_combo_cascade.t b/rt/lib/t/regression/15cf_combo_cascade.t
new file mode 100644
index 0000000..df663a1
--- /dev/null
+++ b/rt/lib/t/regression/15cf_combo_cascade.t
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+use Test::More tests => 11;
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+sub fails { ok(!$_[0], "This should fail: $_[1]") }
+sub works { ok($_[0], $_[1] || 'This works') }
+
+sub new (*) {
+ my $class = shift;
+ return $class->new($RT::SystemUser);
+}
+
+my $q = new(RT::Queue);
+works($q->Create(Name => "CF-Pattern-".$$));
+
+my $cf = new(RT::CustomField);
+my @cf_args = (Name => $q->Name, Type => 'Combobox', Queue => $q->id);
+
+works($cf->Create(@cf_args));
+
+# Set some CFVs with Category markers
+
+my $t = new(RT::Ticket);
+my ($id,undef,$msg) = $t->Create(Queue => $q->id, Subject => 'CF Test');
+works($id,$msg);
+
+sub add_works {
+ works(
+ $cf->AddValue(Name => $_[0], Description => $_[0], Category => $_[1])
+ );
+};
+
+add_works('value1', '1. Category A');
+add_works('value2');
+add_works('value3', '1.1. A-sub one');
+add_works('value4', '1.2. A-sub two');
+add_works('value5', '');
+
+my $cfv = $cf->Values->First;
+is($cfv->Category, '1. Category A');
+works($cfv->SetCategory('1. Category AAA'));
+is($cfv->Category, '1. Category AAA');
+
+1;
diff --git a/rt/lib/t/regression/15cf_pattern.t b/rt/lib/t/regression/15cf_pattern.t
new file mode 100644
index 0000000..ea2b5b8
--- /dev/null
+++ b/rt/lib/t/regression/15cf_pattern.t
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+use Test::More tests => 17;
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+sub fails { ok(!$_[0], "This should fail: $_[1]") }
+sub works { ok($_[0], $_[1] || 'This works') }
+
+sub new (*) {
+ my $class = shift;
+ return $class->new($RT::SystemUser);
+}
+
+my $q = new(RT::Queue);
+works($q->Create(Name => "CF-Pattern-".$$));
+
+my $cf = new(RT::CustomField);
+my @cf_args = (Name => $q->Name, Type => 'Freeform', Queue => $q->id, MaxValues => 1);
+
+fails($cf->Create(@cf_args, Pattern => ')))bad!regex((('));
+works($cf->Create(@cf_args, Pattern => 'good regex'));
+
+my $t = new(RT::Ticket);
+my ($id,undef,$msg) = $t->Create(Queue => $q->id, Subject => 'CF Test');
+works($id,$msg);
+
+# OK, I'm thoroughly brain washed by HOP at this point now...
+sub cnt { $t->CustomFieldValues($cf->id)->Count };
+sub add { $t->AddCustomFieldValue(Field => $cf->id, Value => $_[0]) };
+sub del { $t->DeleteCustomFieldValue(Field => $cf->id, Value => $_[0]) };
+
+is(cnt(), 0, "No values yet");
+fails(add('not going to match'));
+is(cnt(), 0, "No values yet");
+works(add('here is a good regex'));
+is(cnt(), 1, "Value filled");
+fails(del('here is a good regex'));
+is(cnt(), 1, "Single CF - Value _not_ deleted");
+
+$cf->SetMaxValues(0); # Unlimited MaxValues
+
+works(del('here is a good regex'));
+is(cnt(), 0, "Multiple CF - Value deleted");
+
+fails($cf->SetPattern('(?{ "insert evil code here" })'));
+works($cf->SetPattern('(?!)')); # reject everything
+fails(add(''));
+fails(add('...'));
+
+1;
diff --git a/rt/lib/t/regression/15cf_single_values_are_single.t b/rt/lib/t/regression/15cf_single_values_are_single.t
new file mode 100644
index 0000000..dcfa2e5
--- /dev/null
+++ b/rt/lib/t/regression/15cf_single_values_are_single.t
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+use Test::More tests => 8;
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+
+my $q = RT::Queue->new($RT::SystemUser);
+my ($id,$msg) =$q->Create(Name => "CF-Single-".$$);
+ok($id,$msg);
+
+my $cf = RT::CustomField->new($RT::SystemUser);
+($id,$msg) = $cf->Create(Name => 'Single-'.$$, Type => 'Select', MaxValues => '1', Queue => $q->id);
+ok($id,$msg);
+
+
+($id,$msg) =$cf->AddValue(Name => 'First');
+ok($id,$msg);
+
+($id,$msg) =$cf->AddValue(Name => 'Second');
+ok($id,$msg);
+
+
+my $t = RT::Ticket->new($RT::SystemUser);
+($id,undef,$msg) = $t->Create(Queue => $q->id,
+ Subject => 'CF Test');
+
+ok($id,$msg);
+is($t->CustomFieldValues($cf->id)->Count, 0, "No values yet");
+$t->AddCustomFieldValue(Field => $cf->id, Value => 'First');
+is($t->CustomFieldValues($cf->id)->Count, 1, "One now");
+
+$t->AddCustomFieldValue(Field => $cf->id, Value => 'Second');
+is($t->CustomFieldValues($cf->id)->Count, 1, "Still one");
+
+1;
diff --git a/rt/lib/t/regression/16-transaction_cf_tests.t b/rt/lib/t/regression/16-transaction_cf_tests.t
new file mode 100644
index 0000000..9e1e86c
--- /dev/null
+++ b/rt/lib/t/regression/16-transaction_cf_tests.t
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use Data::Dumper;
+use Test::More qw/no_plan/;
+
+use_ok('RT');
+use_ok('RT::Transactions');
+RT::LoadConfig();
+RT::Init();
+
+my $q = RT::Queue->new($RT::SystemUser);
+my ($id,$msg) = $q->Create( Name => 'TxnCFTest'.$$);
+ok($id,$msg);
+
+my $cf = RT::CustomField->new($RT::SystemUser);
+($id,$msg) = $cf->Create(Name => 'Txnfreeform-'.$$, Type => 'Freeform', MaxValues => '0', LookupType => RT::Transaction->CustomFieldLookupType );
+
+ok($id,$msg);
+
+($id,$msg) = $cf->AddToObject($q);
+
+ok($id,$msg);
+
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+
+my $transid;
+($id,$transid, $msg) = $ticket->Create(Queue => $q->id,
+ Subject => 'TxnCF test',
+ );
+ok($id,$msg);
+
+my $trans = RT::Transaction->new($RT::SystemUser);
+$trans->Load($transid);
+
+is($trans->ObjectId,$id);
+is ($trans->ObjectType, 'RT::Ticket');
+is ($trans->Type, 'Create');
+my $txncfs = $trans->CustomFields;
+is ($txncfs->Count, 1, "We have one custom field");
+my $txn_cf = $txncfs->First;
+is ($txn_cf->id, $cf->id, "It's the right custom field");
+my $values = $trans->CustomFieldValues($txn_cf->id);
+is ($values->Count, 0, "It has no values");
+
+# Old API
+my %cf_updates = ( 'CustomField-'.$cf->id => 'Testing');
+$trans->UpdateCustomFields( ARGSRef => \%cf_updates);
+
+ $values = $trans->CustomFieldValues($txn_cf->id);
+is ($values->Count, 1, "It has one value");
+
+# New API
+
+$trans->UpdateCustomFields( 'CustomField-'.$cf->id => 'Test two');
+ $values = $trans->CustomFieldValues($txn_cf->id);
+is ($values->Count, 2, "it has two values");
+
+# TODO ok(0, "Should updating custom field values remove old values?");
diff --git a/rt/lib/t/regression/17custom_search.t b/rt/lib/t/regression/17custom_search.t
new file mode 100644
index 0000000..8e53f44
--- /dev/null
+++ b/rt/lib/t/regression/17custom_search.t
@@ -0,0 +1,88 @@
+#!/usr/bin/perl -w
+use strict;
+
+use Test::More tests => 10;
+BEGIN {
+ use RT;
+ RT::LoadConfig;
+ RT::Init;
+}
+use Test::WWW::Mechanize;
+
+use constant BaseURL => $RT::WebURL;
+
+# reset preferences for easier test?
+
+my $t = RT::Ticket->new($RT::SystemUser);
+$t->Create(Subject => 'for custom search', Queue => 'general',
+ Owner => 'root', Requestor => 'customsearch@localhost');
+ok(my $id = $t->id, 'created ticket for custom search');
+
+my $m = Test::WWW::Mechanize->new ( autocheck => 1 );
+isa_ok($m, 'Test::WWW::Mechanize');
+
+$m->get( BaseURL."?user=root;pass=password" );
+$m->content_like(qr/Logout/, 'we did log in');
+
+my $t_link = $m->find_link( text => "for custom search" );
+like ($t_link->url, qr/$id/, 'link to the ticket we created');
+
+$m->content_lacks ('customsearch@localhost', 'requestor not displayed ');
+$m->get ( BaseURL.'Prefs/MyRT.html' );
+my $cus_hp = $m->find_link( text => "My Tickets" );
+my $cus_qs = $m->find_link( text => "Quick search" );
+$m->get ($cus_hp);
+$m->content_like (qr'highest priority tickets');
+
+# add Requestor to the fields
+$m->form_name ('BuildQuery');
+# can't use submit form for mutli-valued select as it uses set_fields
+$m->field (SelectDisplayColumns => ['Requestors']);
+$m->click_button (name => 'AddCol') ;
+
+$m->form_name ('BuildQuery');
+$m->click_button (name => 'Save');
+
+$m->get( BaseURL );
+$m->content_contains ('customsearch@localhost', 'requestor now displayed ');
+
+
+# now remove Requestor from the fields
+$m->get ($cus_hp);
+
+$m->form_name ('BuildQuery');
+$m->field (CurrentDisplayColumns => 'Requestors');
+$m->click_button (name => 'RemoveCol') ;
+
+$m->form_name ('BuildQuery');
+$m->click_button (name => 'Save');
+
+$m->get( BaseURL );
+$m->content_lacks ('customsearch@localhost', 'requestor not displayed ');
+
+
+# try to disable General from quick search
+
+# Note that there's a small problem in the current implementation,
+# since ticked quese are wanted, we do the invesrsion. So any
+# queue added during the quicksearch setting will be unticked.
+my $nlinks = $#{$m->find_all_links( text => "General" )};
+warn $nlinks;
+$m->get ($cus_qs);
+$m->form_name ('Preferences');
+$m->untick('Want-General', '1');
+$m->click_button (name => 'Save');
+
+$m->get( BaseURL );
+is ($#{$m->find_all_links( text => "General" )}, $nlinks - 1,
+ 'General gone from quicksearch list');
+
+# get it back
+$m->get ($cus_qs);
+$m->form_name ('Preferences');
+$m->tick('Want-General', '1');
+$m->click_button (name => 'Save');
+
+$m->get( BaseURL );
+is ($#{$m->find_all_links( text => "General" )}, $nlinks,
+ 'General back in quicksearch list');
diff --git a/rt/lib/t/regression/17multiple_deleg_revocation.t b/rt/lib/t/regression/17multiple_deleg_revocation.t
new file mode 100644
index 0000000..1ed0404
--- /dev/null
+++ b/rt/lib/t/regression/17multiple_deleg_revocation.t
@@ -0,0 +1,135 @@
+#!/usr/bin/perl -w
+
+use Test::More qw(no_plan);
+
+use RT;
+
+ok( RT::LoadConfig, "Locating config files" );
+ok( RT::Init, "Basic initialization and DB connectivity" );
+
+my ($u1, $g1, $pg1, $pg2, $ace, @groups, @users, @principals);
+@groups = (\$g1, \$pg1, \$pg2);
+@users = (\$u1);
+@principals = (@groups, @users);
+
+my($ret, $msg);
+
+$u1 = RT::User->new($RT::SystemUser);
+( $ret, $msg ) = $u1->LoadOrCreateByEmail('delegtest1@example.com');
+ok( $ret, "Load / Create test user 1: $msg" );
+$u1->SetPrivileged(1);
+
+$g1 = RT::Group->new($RT::SystemUser);
+( $ret, $msg) = $g1->LoadUserDefinedGroup('dg1');
+unless ($ret) {
+ ( $ret, $msg ) = $g1->CreateUserDefinedGroup( Name => 'dg1' );
+}
+$pg1 = RT::Group->new($RT::SystemUser);
+( $ret, $msg ) = $pg1->LoadPersonalGroup( Name => 'dpg1',
+ User => $u1->PrincipalId );
+unless ($ret) {
+ ( $ret, $msg ) = $pg1->CreatePersonalGroup( Name => 'dpg1',
+ PrincipalId => $u1->PrincipalId );
+}
+ok( $ret, "Load / Create test personal group 1: $msg" );
+$pg2 = RT::Group->new($RT::SystemUser);
+( $ret, $msg ) = $pg2->LoadPersonalGroup( Name => 'dpg2',
+ User => $u1->PrincipalId );
+unless ($ret) {
+ ( $ret, $msg ) = $pg2->CreatePersonalGroup( Name => 'dpg2',
+ PrincipalId => $u1->PrincipalId );
+}
+ok( $ret, "Load / Create test personal group 2: $msg" );
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $u1->PrincipalObj->GrantRight( Right => 'DelegateRights' );
+ok( $ret, "Grant DelegateRights to u1: $msg" );
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'ShowConfigTab' );
+ok( $ret, "Grant ShowConfigTab to g1: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g1->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg2->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg2: $msg" );
+
+ok(( $pg1->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System ) and
+ $pg2->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System )),
+ "Test personal groups have ShowConfigTab right after delegation" );
+
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+
+ok( not( $pg1->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System )),
+ "Test personal group 1 lacks ShowConfigTab after user removed from g1" );
+ok( not( $pg2->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System )),
+ "Test personal group 2 lacks ShowConfigTab after user removed from g1" );
+
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg2->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg2: $msg" );
+
+ok(( $pg1->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System ) and
+ $pg2->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System )),
+ "Test personal groups have ShowConfigTab right after delegation" );
+
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight( Right => 'ShowConfigTab' );
+ok( $ret, "Revoke ShowConfigTab from g1: $msg" );
+
+ok( not( $pg1->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System )),
+ "Test personal group 1 lacks ShowConfigTab after user removed from g1" );
+ok( not( $pg2->PrincipalObj->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System )),
+ "Test personal group 2 lacks ShowConfigTab after user removed from g1" );
+
+
+
+#######
+
+sub clear_acls_and_groups {
+ # Revoke all rights granted to our cast
+ my $acl = RT::ACL->new($RT::SystemUser);
+ foreach (@principals) {
+ $acl->LimitToPrincipal(Type => $$_->PrincipalObj->PrincipalType,
+ Id => $$_->PrincipalObj->Id);
+ }
+ while (my $ace = $acl->Next()) {
+ $ace->Delete();
+ }
+
+ # Remove all group memberships
+ my $members = RT::GroupMembers->new($RT::SystemUser);
+ foreach (@groups) {
+ $members->LimitToMembersOfGroup( $$_->PrincipalId );
+ }
+ while (my $member = $members->Next()) {
+ $member->Delete();
+ }
+
+ $acl->RedoSearch();
+ ok( $acl->Count() == 0,
+ "All principals have no rights after clearing ACLs" );
+ $members->RedoSearch();
+ ok( $members->Count() == 0,
+ "All groups have no members after clearing groups" );
+}
diff --git a/rt/lib/t/regression/18custom_frontpage.t b/rt/lib/t/regression/18custom_frontpage.t
new file mode 100644
index 0000000..cf77e35
--- /dev/null
+++ b/rt/lib/t/regression/18custom_frontpage.t
@@ -0,0 +1,75 @@
+#!/usr/bin/perl -w
+use strict;
+
+use Test::More tests => 7;
+BEGIN {
+ use RT;
+ RT::LoadConfig;
+ RT::Init;
+}
+use Test::WWW::Mechanize;
+
+use constant BaseURL => $RT::WebURL;
+
+
+my $user_obj = RT::User->new($RT::SystemUser);
+my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('customer@example.com');
+ok($ret, 'ACL test user creation');
+$user_obj->SetName('customer');
+$user_obj->SetPrivileged(1);
+($ret, $msg) = $user_obj->SetPassword('customer');
+$user_obj->PrincipalObj->GrantRight(Right => 'LoadSavedSearch');
+$user_obj->PrincipalObj->GrantRight(Right => 'EditSavedSearch');
+$user_obj->PrincipalObj->GrantRight(Right => 'CreateSavedSearch');
+$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf');
+
+my $m = Test::WWW::Mechanize->new ( autocheck => 1 );
+isa_ok($m, 'Test::WWW::Mechanize');
+
+$m->get( BaseURL."?user=customer;pass=customer" );
+
+$m->content_like(qr/Logout/, 'we did log in');
+
+$m->get ( BaseURL."Search/Build.html");
+
+#create a saved search
+$m->form_name ('BuildQuery');
+
+$m->field ( "ValueOfAttachment" => 'stupid');
+$m->field ( "Description" => 'stupid tickets');
+$m->click_button (name => 'Save');
+
+$m->get ( BaseURL.'Prefs/MyRT.html' );
+$m->content_like (qr/stupid tickets/, 'saved search listed in rt at a glance items');
+
+$m->follow_link (text => 'Logout');
+
+$m->get( BaseURL."?user=root;pass=password" );
+$m->content_like(qr/Logout/, 'we did log in');
+
+$m->get ( BaseURL.'Prefs/MyRT.html' );
+$m->form_name ('SelectionBox-body');
+# can't use submit form for mutli-valued select as it uses set_fields
+$m->field ('body-Selected' => ['component-QuickCreate', 'system-Unowned Tickets', 'system-My Tickets']);
+$m->click_button (name => 'remove');
+$m->form_name ('SelectionBox-body');
+#$m->click_button (name => 'body-Save');
+$m->get ( BaseURL );
+$m->content_lacks ('highest priority tickets', 'remove everything from body pane');
+
+$m->get ( BaseURL.'Prefs/MyRT.html' );
+$m->form_name ('SelectionBox-body');
+$m->field ('body-Available' => ['component-QuickCreate', 'system-Unowned Tickets', 'system-My Tickets']);
+$m->click_button (name => 'add');
+
+$m->form_name ('SelectionBox-body');
+$m->field ('body-Selected' => ['component-QuickCreate']);
+$m->click_button (name => 'movedown');
+
+$m->form_name ('SelectionBox-body');
+$m->click_button (name => 'movedown');
+
+$m->form_name ('SelectionBox-body');
+#$m->click_button (name => 'body-Save');
+$m->get ( BaseURL );
+$m->content_like (qr'highest priority tickets', 'adds them back');
diff --git a/rt/lib/t/regression/18stale_delegations_cleanup.t b/rt/lib/t/regression/18stale_delegations_cleanup.t
new file mode 100644
index 0000000..84e666e
--- /dev/null
+++ b/rt/lib/t/regression/18stale_delegations_cleanup.t
@@ -0,0 +1,458 @@
+#!/usr/bin/perl -w
+
+# Regression test suite for http://rt3.fsck.com/Ticket/Display.html?id=6184
+# and related corner cases related to cleanup of delegated ACEs when
+# the delegator loses the right to delegate. This causes complexities
+# due to the fact that multiple ACEs can grant different delegation
+# rights to a principal, and because DelegateRights and SuperUser can
+# themselves be delegated.
+
+# The case where the "parent" delegated ACE is removed is handled in
+# the embedded regression tests in lib/RT/ACE_Overlay.pm .
+
+use Test::More qw(no_plan);
+
+use RT;
+
+ok( RT::LoadConfig, "Locating config files" );
+ok( RT::Init, "Basic initialization and DB connectivity" );
+
+my ($u1, $u2, $g1, $g2, $g3, $pg1, $pg2, $ace, @groups, @users, @principals);
+@groups = (\$g1, \$g2, \$g3, \$pg1, \$pg2);
+@users = (\$u1, \$u2);
+@principals = (@groups, @users);
+
+my($ret, $msg);
+
+$u1 = RT::User->new($RT::SystemUser);
+( $ret, $msg ) = $u1->LoadOrCreateByEmail('delegtest1@example.com');
+ok( $ret, "Load / Create test user 1: $msg" );
+$u1->SetPrivileged(1);
+$u2 = RT::User->new($RT::SystemUser);
+( $ret, $msg ) = $u2->LoadOrCreateByEmail('delegtest2@example.com');
+ok( $ret, "Load / Create test user 2: $msg" );
+$u2->SetPrivileged(1);
+$g1 = RT::Group->new($RT::SystemUser);
+( $ret, $msg) = $g1->LoadUserDefinedGroup('dg1');
+unless ($ret) {
+ ( $ret, $msg ) = $g1->CreateUserDefinedGroup( Name => 'dg1' );
+}
+ok( $ret, "Load / Create test group 1: $msg" );
+$g2 = RT::Group->new($RT::SystemUser);
+( $ret, $msg) = $g2->LoadUserDefinedGroup('dg2');
+unless ($ret) {
+ ( $ret, $msg ) = $g2->CreateUserDefinedGroup( Name => 'dg2' );
+}
+ok( $ret, "Load / Create test group 2: $msg" );
+$g3 = RT::Group->new($RT::SystemUser);
+( $ret, $msg) = $g3->LoadUserDefinedGroup('dg3');
+unless ($ret) {
+ ( $ret, $msg ) = $g3->CreateUserDefinedGroup( Name => 'dg3' );
+}
+ok( $ret, "Load / Create test group 3: $msg" );
+$pg1 = RT::Group->new($RT::SystemUser);
+( $ret, $msg ) = $pg1->LoadPersonalGroup( Name => 'dpg1',
+ User => $u1->PrincipalId );
+unless ($ret) {
+ ( $ret, $msg ) = $pg1->CreatePersonalGroup( Name => 'dpg1',
+ PrincipalId => $u1->PrincipalId );
+}
+ok( $ret, "Load / Create test personal group 1: $msg" );
+$pg2 = RT::Group->new($RT::SystemUser);
+( $ret, $msg ) = $pg2->LoadPersonalGroup( Name => 'dpg2',
+ User => $u2->PrincipalId );
+unless ($ret) {
+ ( $ret, $msg ) = $pg2->CreatePersonalGroup( Name => 'dpg2',
+ PrincipalId => $u2->PrincipalId );
+}
+ok( $ret, "Load / Create test personal group 2: $msg" );
+
+
+
+# Basic case: u has global DelegateRights through g1 and ShowConfigTab
+# through g2; then u is removed from g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights' );
+ok( $ret, "Grant DelegateRights to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'ShowConfigTab' );
+ok( $ret, "Grant ShowConfigTab to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+ok(
+ $u1->PrincipalObj->HasRight(
+ Right => 'DelegateRights',
+ Object => $RT::System
+ ),
+ "test user 1 has DelegateRights after joining g1"
+);
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+ok(
+ $u1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ ),
+ "test user 1 has ShowConfigTab after joining g2"
+);
+
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+ok(
+ $pg1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ ),
+ "Test personal group 1 has ShowConfigTab right after delegation"
+);
+
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok(
+ not(
+ $pg1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ )
+ ),
+ "Test personal group 1 lacks ShowConfigTab right after user removed from g1"
+);
+
+# Basic case: u has global DelegateRights through g1 and ShowConfigTab
+# through g2; then DelegateRights revoked from g1.
+
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight( Right => 'DelegateRights' );
+ok( $ret, "Revoke DelegateRights from g1: $msg" );
+ok(
+ not(
+ $pg1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ )
+ ),
+ "Test personal group 1 lacks ShowConfigTab right after DelegateRights revoked from g1"
+);
+
+
+
+# Corner case - restricted delegation: u has DelegateRights on pg1
+# through g1 and AdminGroup on pg1 through g2; then DelegateRights
+# revoked from g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $pg1);
+ok( $ret, "Grant DelegateRights on pg1 to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'AdminGroup',
+ Object => $pg1);
+ok( $ret, "Grant AdminGroup on pg1 to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+ok( $u1->PrincipalObj->HasRight(
+ Right => 'DelegateRights',
+ Object => $pg1 ),
+ "test user 1 has DelegateRights on pg1 after joining g1" );
+ok( not( $u1->PrincipalObj->HasRight(
+ Right => 'DelegateRights',
+ Object => $RT::System )),
+ "Test personal group 1 lacks global DelegateRights after joining g1" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'AdminGroup',
+ Object => $pg1,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+ok( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 ),
+ "Test personal group 1 has AdminGroup right on pg1 after delegation" );
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight ( Right => 'DelegateRights',
+ Object => $pg1 );
+ok( $ret, "Revoke DelegateRights on pg1 from g1: $msg" );
+ok( not( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 )),
+ "Test personal group 1 lacks AdminGroup right on pg1 after DelegateRights revoked from g1" );
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $pg1);
+
+# Corner case - restricted delegation: u has DelegateRights on pg1
+# through g1 and AdminGroup on pg1 through g2; then u removed from g1.
+
+ok( $ret, "Grant DelegateRights on pg1 to g1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+ok( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 ),
+ "Test personal group 1 has AdminGroup right on pg1 after delegation" );
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok( not( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 )),
+ "Test personal group 1 lacks AdminGroup right on pg1 after user removed from g1" );
+
+clear_acls_and_groups();
+
+
+
+# Corner case - multiple delegation rights: u has global
+# DelegateRights directly and DelegateRights on pg1 through g1, and
+# AdminGroup on pg1 through g2; then u removed from g1 (delegation
+# should remain); then DelegateRights revoked from u (delegation
+# should not remain).
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $pg1);
+ok( $ret, "Grant DelegateRights on pg1 to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'AdminGroup',
+ Object => $pg1);
+ok( $ret, "Grant AdminGroup on pg1 to g2: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $RT::System);
+ok( $ret, "Grant DelegateRights to user: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'AdminGroup',
+ Object => $pg1,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok( $pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1),
+ "Test personal group 1 retains AdminGroup right on pg1 after user removed from g1" );
+( $ret, $msg ) = $u1->PrincipalObj->RevokeRight( Right => 'DelegateRights',
+ Object => $RT::System );
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1)),
+ "Test personal group 1 lacks AdminGroup right on pg1 after DelegateRights revoked");
+
+# Corner case - multiple delegation rights and selectivity: u has
+# DelegateRights globally and on g2 directly and DelegateRights on pg1
+# through g1, and AdminGroup on pg1 through g2; then global
+# DelegateRights revoked from u (delegation should remain),
+# DelegateRights on g2 revoked from u (delegation should remain), and
+# u removed from g1 (delegation should not remain).
+
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $RT::System);
+ok( $ret, "Grant DelegateRights to user: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $g2);
+ok( $ret, "Grant DelegateRights on g2 to user: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->RevokeRight( Right => 'DelegateRights',
+ Object => $RT::System );
+ok( $pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1),
+ "Test personal group 1 retains AdminGroup right on pg1 after global DelegateRights revoked" );
+( $ret, $msg ) = $u1->PrincipalObj->RevokeRight( Right => 'DelegateRights',
+ Object => $g2 );
+ok( $pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1),
+ "Test personal group 1 retains AdminGroup right on pg1 after DelegateRights on g2 revoked" );
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1)),
+ "Test personal group 1 lacks AdminGroup right on pg1 after user removed from g1");
+
+
+
+# Corner case - indirect delegation rights: u has DelegateRights
+# through g1 via g3, and ShowConfigTab via g2; then g3 removed from
+# g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights' );
+ok( $ret, "Grant DelegateRights to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'ShowConfigTab' );
+ok( $ret, "Grant ShowConfigTab to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $g3->PrincipalId );
+ok( $ret, "Add g3 to g1: $msg" );
+( $ret, $msg ) = $g3->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g3: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+
+( $ret, $msg ) = $g1->DeleteMember( $g3->PrincipalId );
+ok( $ret, "Delete g3 from g1: $msg" );
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 1 lacks ShowConfigTab right after g3 removed from g1");
+
+# Corner case - indirect delegation rights: u has DelegateRights
+# through g1 via g3, and ShowConfigTab via g2; then DelegateRights
+# revoked from g1.
+
+( $ret, $msg ) = $g1->AddMember( $g3->PrincipalId );
+ok( $ret, "Add g3 to g1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight ( Right => 'DelegateRights' );
+ok( $ret, "Revoke DelegateRights from g1: $msg" );
+
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 1 lacks ShowConfigTab right after DelegateRights revoked from g1");
+
+
+
+# Corner case - delegation of DelegateRights: u1 has DelegateRights
+# via g1 and delegates DelegateRights to pg1; u2 has DelegateRights
+# via pg1 and ShowConfigTab via g2; then u1 removed from g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights' );
+ok( $ret, "Grant DelegateRights to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'ShowConfigTab' );
+ok( $ret, "Grant ShowConfigTab to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'DelegateRights',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g1->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate DelegateRights to pg1: $msg" );
+
+( $ret, $msg ) = $pg1->AddMember( $u2->PrincipalId );
+ok( $ret, "Add test user 2 to pg1: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u2->PrincipalId );
+ok( $ret, "Add test user 2 to g2: $msg" );
+$ace = RT::ACE->new($u2);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg2->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg2: $msg" );
+
+ok( $pg2->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System),
+ "Test personal group 2 has ShowConfigTab right after delegation");
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete u1 from g1: $msg" );
+ok( not ($pg2->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 2 lacks ShowConfigTab right after u1 removed from g1");
+
+# Corner case - delegation of DelegateRights: u1 has DelegateRights
+# via g1 and delegates DelegateRights to pg1; u2 has DelegateRights
+# via pg1 and ShowConfigTab via g2; then DelegateRights revoked from
+# g1.
+
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add u1 to g1: $msg" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'DelegateRights',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g1->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate DelegateRights to pg1: $msg" );
+$ace = RT::ACE->new($u2);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg2->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg2: $msg" );
+
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight ( Right => 'DelegateRights' );
+ok( $ret, "Revoke DelegateRights from g1: $msg" );
+ok( not ($pg2->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 2 lacks ShowConfigTab right after DelegateRights revoked from g1");
+
+
+
+
+#######
+
+sub clear_acls_and_groups {
+ # Revoke all rights granted to our cast
+ my $acl = RT::ACL->new($RT::SystemUser);
+ foreach (@principals) {
+ $acl->LimitToPrincipal(Type => $$_->PrincipalObj->PrincipalType,
+ Id => $$_->PrincipalObj->Id);
+ }
+ while (my $ace = $acl->Next()) {
+ $ace->Delete();
+ }
+
+ # Remove all group memberships
+ my $members = RT::GroupMembers->new($RT::SystemUser);
+ foreach (@groups) {
+ $members->LimitToMembersOfGroup( $$_->PrincipalId );
+ }
+ while (my $member = $members->Next()) {
+ $member->Delete();
+ }
+
+ $acl->RedoSearch();
+ ok( $acl->Count() == 0,
+ "All principals have no rights after clearing ACLs" );
+ $members->RedoSearch();
+ ok( $members->Count() == 0,
+ "All groups have no members after clearing groups" );
+}
diff --git a/rt/lib/t/regression/19-rtname.t b/rt/lib/t/regression/19-rtname.t
new file mode 100644
index 0000000..b654df2
--- /dev/null
+++ b/rt/lib/t/regression/19-rtname.t
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Test::More qw/no_plan/;
+
+use_ok("RT");
+
+RT::LoadConfig();
+RT::Init();
+
+use RT::Interface::Email;
+
+# normal use case, regexp set to rtname
+$RT::rtname = "site";
+$RT::EmailSubjectTagRegex = qr/$RT::rtname/ ;
+$RT::rtname = undef;
+is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123);
+is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef);
+
+# oops usecase, where the regexp is scragged
+$RT::rtname = "site";
+$RT::EmailSubjectTagRegex = undef;
+is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123);
+is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef);
+
+# set to a simple regexp. NOTE: we no longer match "site"
+$RT::rtname = "site";
+$RT::EmailSubjectTagRegex = qr/newsite/;
+is(RT::Interface::Email::ParseTicketId("[site #123] test"), undef);
+is(RT::Interface::Email::ParseTicketId("[newsite #123] test"), 123);
+
+# set to a more complex regexp
+$RT::rtname = "site";
+$RT::EmailSubjectTagRegex = qr/newsite||site/;
+is(RT::Interface::Email::ParseTicketId("[site #123] test"), 123);
+is(RT::Interface::Email::ParseTicketId("[newsite #123] test"), 123);
+is(RT::Interface::Email::ParseTicketId("[othersite #123] test"), undef);
+
diff --git a/rt/lib/t/regression/19quicksearch.t b/rt/lib/t/regression/19quicksearch.t
new file mode 100644
index 0000000..7744787
--- /dev/null
+++ b/rt/lib/t/regression/19quicksearch.t
@@ -0,0 +1,39 @@
+
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use Test::More qw/no_plan/;
+use_ok('RT');
+RT::LoadConfig();
+RT::Init();
+
+my $q = RT::Queue->new($RT::SystemUser);
+my $queue = 'SearchTests-'.$$;
+$q->Create(Name => $queue);
+ok ($q->id, "Created the queue");
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ( $id, undef, $msg ) = $t1->Create(
+ Queue => $q->id,
+ Subject => 'SearchTest1',
+ Requestor => ['search2@example.com'],
+);
+ok( $id, $msg );
+
+use_ok("RT::Search::Googleish");
+my $tickets = RT::Tickets->new($RT::SystemUser);
+my $quick = RT::Search::Googleish->new(Argument => "",
+ TicketsObj => $tickets);
+my @tests = (
+ "General new open root" => "( Owner = 'root' ) AND ( Queue = 'General' ) AND ( Status = 'new' OR Status = 'open' )",
+ "fulltext:jesse" => "( Content LIKE 'jesse' )",
+ $queue => "( Queue = '$queue' )",
+ "root $queue" => "( Owner = 'root' ) AND ( Queue = '$queue' )",
+ "notauser $queue" => "( Queue = '$queue' ) AND ( Subject LIKE 'notauser' )",
+ "notauser $queue root" => "( Owner = 'root' ) AND ( Queue = '$queue' ) AND ( Subject LIKE 'notauser' )");
+
+while (my ($from, $to) = splice @tests, 0, 2) {
+ is($quick->QueryToSQL($from), $to, "<$from> -> <$to>");
+}
diff --git a/rt/lib/t/regression/20-sort-by-requestor.t b/rt/lib/t/regression/20-sort-by-requestor.t
new file mode 100644
index 0000000..e6903b4
--- /dev/null
+++ b/rt/lib/t/regression/20-sort-by-requestor.t
@@ -0,0 +1,143 @@
+#!/usr/bin/perl -w
+use strict; use warnings;
+
+use Test::More qw/no_plan/;
+use_ok('RT');
+RT::LoadConfig();
+RT::Init();
+use RT::Ticket;
+
+my $q = RT::Queue->new($RT::SystemUser);
+my $queue = 'SearchTests-'.rand(200);
+$q->Create(Name => $queue);
+
+my @requestors = ( ('bravo@example.com') x 6, ('alpha@example.com') x 6,
+ ('delta@example.com') x 6, ('charlie@example.com') x 6,
+ (undef) x 6);
+my @subjects = ("first test", "second test", "third test", "fourth test", "fifth test") x 6;
+while (@requestors) {
+ my $t = RT::Ticket->new($RT::SystemUser);
+ my ( $id, undef $msg ) = $t->Create(
+ Queue => $q->id,
+ Subject => shift @subjects,
+ Requestor => [ shift @requestors ]
+ );
+ ok( $id, $msg );
+}
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue'");
+ is($tix->Count, 30, "found thirty tickets");
+}
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND requestor = 'alpha\@example.com'");
+ $tix->OrderByCols({ FIELD => "Subject" });
+ my @subjects;
+ while (my $t = $tix->Next) { push @subjects, $t->Subject; }
+ is(@subjects, 6, "found six tickets");
+ is_deeply( \@subjects, [ sort @subjects ], "Subjects are sorted");
+}
+
+sub check_emails_order
+{
+ my ($tix,$count,$order) = (@_);
+ my @mails;
+ while (my $t = $tix->Next) { push @mails, $t->RequestorAddresses; }
+ is(@mails, $count, "found $count tickets for ". $tix->Query);
+ my @required_order;
+ if( $order =~ /asc/i ) {
+ @required_order = sort { $a? ($b? ($a cmp $b) : -1) : 1} @mails;
+ } else {
+ @required_order = sort { $a? ($b? ($b cmp $a) : -1) : 1} @mails;
+ }
+ foreach( reverse splice @mails ) {
+ if( $_ ) { unshift @mails, $_ }
+ else { push @mails, $_ }
+ }
+ is_deeply( \@mails, \@required_order, "Addresses are sorted");
+}
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND subject = 'first test' AND Requestor.EmailAddress LIKE 'example.com'");
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" });
+ check_emails_order($tix, 5, 'ASC');
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' });
+ check_emails_order($tix, 5, 'DESC');
+}
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Subject = 'first test'");
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" });
+ check_emails_order($tix, 6, 'ASC');
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' });
+ check_emails_order($tix, 6, 'DESC');
+}
+
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Subject = 'first test'");
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" });
+ check_emails_order($tix, 6, 'ASC');
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' });
+ check_emails_order($tix, 6, 'DESC');
+}
+
+{
+ # create ticket with group as member of the requestors group
+ my $t = RT::Ticket->new($RT::SystemUser);
+ my ( $id, $msg ) = $t->Create(
+ Queue => $q->id,
+ Subject => "first test",
+ Requestor => 'badaboom@example.com',
+ );
+ ok( $id, "ticket created" ) or diag( "error: $msg" );
+
+ my $g = RT::Group->new($RT::SystemUser);
+
+ my ($gid);
+ ($gid, $msg) = $g->CreateUserDefinedGroup(Name => '20-sort-by-requestor.t-'.rand(200));
+ ok($gid, "created group") or diag("error: $msg");
+
+ ($id, $msg) = $t->Requestors->AddMember( $gid );
+ ok($id, "added group to requestors group") or diag("error: $msg");
+}
+
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Subject = 'first test'");
+TODO: {
+ local $TODO = "if group has non users members we get wrong order";
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" });
+ check_emails_order($tix, 7, 'ASC');
+}
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress", ORDER => 'DESC' });
+ check_emails_order($tix, 7, 'DESC');
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue'");
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" });
+ $tix->RowsPerPage(30);
+ my @mails;
+ while (my $t = $tix->Next) { push @mails, $t->RequestorAddresses; }
+ is(@mails, 30, "found thirty tickets");
+ is_deeply( [grep {$_} @mails], [ sort grep {$_} @mails ], "Paging works (exclude nulls, which are db-dependant)");
+}
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue'");
+ $tix->OrderByCols({ FIELD => "Requestor.EmailAddress" });
+ $tix->RowsPerPage(30);
+ my @mails;
+ while (my $t = $tix->Next) { push @mails, $t->RequestorAddresses; }
+ is(@mails, 30, "found thirty tickets");
+ is_deeply( [grep {$_} @mails], [ sort grep {$_} @mails ], "Paging works (exclude nulls, which are db-dependant)");
+}
+
+# vim:ft=perl:
diff --git a/rt/lib/t/regression/20savedsearch.t b/rt/lib/t/regression/20savedsearch.t
new file mode 100644
index 0000000..f4439f9
--- /dev/null
+++ b/rt/lib/t/regression/20savedsearch.t
@@ -0,0 +1,180 @@
+use RT;
+use Test::More tests => 26;
+use RT::User;
+use RT::Group;
+use RT::Ticket;
+use RT::Queue;
+
+use_ok(RT::SavedSearch);
+use_ok(RT::SavedSearches);
+
+RT::LoadConfig();
+RT::Init();
+
+# Set up some infrastructure. These calls are tested elsewhere.
+
+my $searchuser = RT::User->new($RT::SystemUser);
+my ($ret, $msg) = $searchuser->Create(Name => 'searchuser'.$$,
+ Privileged => 1,
+ EmailAddress => "searchuser\@p$$.example.com",
+ RealName => 'Search user');
+ok($ret, "created searchuser: $msg");
+$searchuser->PrincipalObj->GrantRight(Right => 'LoadSavedSearch');
+$searchuser->PrincipalObj->GrantRight(Right => 'CreateSavedSearch');
+$searchuser->PrincipalObj->GrantRight(Right => 'ModifySelf');
+
+# This is the group whose searches searchuser should be able to see.
+my $ingroup = RT::Group->new($RT::SystemUser);
+$ingroup->CreateUserDefinedGroup(Name => 'searchgroup1'.$$);
+$ingroup->AddMember($searchuser->Id);
+$searchuser->PrincipalObj->GrantRight(Right => 'EditSavedSearches',
+ Object => $ingroup);
+$searchuser->PrincipalObj->GrantRight(Right => 'ShowSavedSearches',
+ Object => $ingroup);
+
+# This is the group whose searches searchuser should not be able to see.
+my $outgroup = RT::Group->new($RT::SystemUser);
+$outgroup->CreateUserDefinedGroup(Name => 'searchgroup2'.$$);
+$outgroup->AddMember($RT::SystemUser->Id);
+
+my $queue = RT::Queue->new($RT::SystemUser);
+$queue->Create(Name => 'SearchQueue'.$$);
+$searchuser->PrincipalObj->GrantRight(Right => 'SeeQueue', Object => $queue);
+$searchuser->PrincipalObj->GrantRight(Right => 'ShowTicket', Object => $queue);
+$searchuser->PrincipalObj->GrantRight(Right => 'OwnTicket', Object => $queue);
+
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+$ticket->Create(Queue => $queue->Id,
+ Requestor => [ $searchuser->Name ],
+ Owner => $searchuser,
+ Subject => 'saved search test');
+
+
+# Now start the search madness.
+my $curruser = RT::CurrentUser->new($searchuser);
+my $format = '\' <b><a href="/Ticket/Display.html?id=__id__">__id__</a></b>/TITLE:#\',
+\'<b><a href="/Ticket/Display.html?id=__id__">__Subject__</a></b>/TITLE:Subject\',
+\'__Status__\',
+\'__QueueName__\',
+\'__OwnerName__\',
+\'__Priority__\',
+\'__NEWLINE__\',
+\'\',
+\'<small>__Requestors__</small>\',
+\'<small>__CreatedRelative__</small>\',
+\'<small>__ToldRelative__</small>\',
+\'<small>__LastUpdatedRelative__</small>\',
+\'<small>__TimeLeft__</small>\'';
+
+my ($ret, $msg);
+my $mysearch = RT::SavedSearch->new($curruser);
+($ret, $msg) = $mysearch->Save(Privacy => 'RT::User-' . $searchuser->Id,
+ Type => 'Ticket',
+ Name => 'owned by me',
+ SearchParams => {'Format' => $format,
+ 'Query' => "Owner = '"
+ . $searchuser->Name
+ . "'"});
+ok($ret, "mysearch was created");
+
+
+my $groupsearch = RT::SavedSearch->new($curruser);
+($ret, $msg) = $groupsearch->Save(Privacy => 'RT::Group-' . $ingroup->Id,
+ Type => 'Ticket',
+ Name => 'search queue',
+ SearchParams => {'Format' => $format,
+ 'Query' => "Queue = '"
+ . $queue->Name . "'"});
+ok($ret, "groupsearch was created");
+
+my $othersearch = RT::SavedSearch->new($curruser);
+($ret, $msg) = $othersearch->Save(Privacy => 'RT::Group-' . $outgroup->Id,
+ Type => 'Ticket',
+ Name => 'searchuser requested',
+ SearchParams => {'Format' => $format,
+ 'Query' =>
+ "Requestor.Name LIKE 'search'"});
+ok(!$ret, "othersearch NOT created");
+like($msg, qr/Failed to load object for/, "...for the right reason");
+
+$othersearch = RT::SavedSearch->new($RT::SystemUser);
+($ret, $msg) = $othersearch->Save(Privacy => 'RT::Group-' . $outgroup->Id,
+ Type => 'Ticket',
+ Name => 'searchuser requested',
+ SearchParams => {'Format' => $format,
+ 'Query' =>
+ "Requestor.Name LIKE 'search'"});
+ok($ret, "othersearch created by systemuser");
+
+# Now try to load some searches.
+
+# This should work.
+my $loadedsearch1 = RT::SavedSearch->new($curruser);
+$loadedsearch1->Load('RT::User-'.$curruser->Id, $mysearch->Id);
+is($loadedsearch1->Id, $mysearch->Id, "Loaded mysearch");
+like($loadedsearch1->GetParameter('Query'), qr/Owner/,
+ "Retrieved query of mysearch");
+# Check through the other accessor methods.
+is($loadedsearch1->Privacy, 'RT::User-' . $curruser->Id,
+ "Privacy of mysearch correct");
+is($loadedsearch1->Name, 'owned by me', "Name of mysearch correct");
+is($loadedsearch1->Type, 'Ticket', "Type of mysearch correct");
+
+# See if it can be used to search for tickets.
+my $tickets = RT::Tickets->new($curruser);
+$tickets->FromSQL($loadedsearch1->GetParameter('Query'));
+is($tickets->Count, 1, "Found a ticket");
+
+# This should fail -- wrong object.
+# my $loadedsearch2 = RT::SavedSearch->new($curruser);
+# $loadedsearch2->Load('RT::User-'.$curruser->Id, $groupsearch->Id);
+# isnt($loadedsearch2->Id, $othersearch->Id, "Didn't load groupsearch as mine");
+# ...but this should succeed.
+my $loadedsearch3 = RT::SavedSearch->new($curruser);
+$loadedsearch3->Load('RT::Group-'.$ingroup->Id, $groupsearch->Id);
+is($loadedsearch3->Id, $groupsearch->Id, "Loaded groupsearch");
+like($loadedsearch3->GetParameter('Query'), qr/Queue/,
+ "Retrieved query of groupsearch");
+# Can it get tickets?
+$tickets = RT::Tickets->new($curruser);
+$tickets->FromSQL($loadedsearch3->GetParameter('Query'));
+is($tickets->Count, 1, "Found a ticket");
+
+# This should fail -- no permission.
+my $loadedsearch4 = RT::SavedSearch->new($curruser);
+$loadedsearch4->Load($othersearch->Privacy, $othersearch->Id);
+isnt($loadedsearch4->Id, $othersearch->Id, "Did not load othersearch");
+
+# Try to update an existing search.
+$loadedsearch1->Update( SearchParams => {'Format' => $format,
+ 'Query' => "Queue = '" . $queue->Name . "'" } );
+like($loadedsearch1->GetParameter('Query'), qr/Queue/,
+ "Updated mysearch parameter");
+is($loadedsearch1->Type, 'Ticket', "mysearch is still for tickets");
+is($loadedsearch1->Privacy, 'RT::User-'.$curruser->Id,
+ "mysearch still belongs to searchuser");
+like($mysearch->GetParameter('Query'), qr/Queue/, "other mysearch object updated");
+
+
+## Right ho. Test the pseudo-collection object.
+
+my $genericsearch = RT::SavedSearch->new($curruser);
+$genericsearch->Save(Name => 'generic search',
+ Type => 'all',
+ SearchParams => {'Query' => "Queue = 'General'"});
+
+my $ticketsearches = RT::SavedSearches->new($curruser);
+$ticketsearches->LimitToPrivacy('RT::User-'.$curruser->Id, 'Ticket');
+is($ticketsearches->Count, 1, "Found searchuser's ticket searches");
+
+my $allsearches = RT::SavedSearches->new($curruser);
+$allsearches->LimitToPrivacy('RT::User-'.$curruser->Id);
+is($allsearches->Count, 2, "Found all searchuser's searches");
+
+# Delete a search.
+($ret, $msg) = $genericsearch->Delete;
+ok($ret, "Deleted genericsearch");
+$allsearches->LimitToPrivacy('RT::User-'.$curruser->Id);
+is($allsearches->Count, 1, "Found all searchuser's searches after deletion");
+
diff --git a/rt/lib/t/regression/21query-builder.t b/rt/lib/t/regression/21query-builder.t
new file mode 100644
index 0000000..a0cecb2
--- /dev/null
+++ b/rt/lib/t/regression/21query-builder.t
@@ -0,0 +1,247 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 39;
+use Test::WWW::Mechanize;
+use HTTP::Request::Common;
+use HTTP::Cookies;
+use LWP;
+use Encode;
+
+my $cookie_jar = HTTP::Cookies->new;
+my $agent = Test::WWW::Mechanize->new();
+
+# give the agent a place to stash the cookies
+
+$agent->cookie_jar($cookie_jar);
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+# create a regression queue if it doesn't exist
+{
+ my $queue = RT::Queue->new( $RT::SystemUser );
+ $queue->Load( 'Regression' );
+ if ( $queue->id ) {
+ ok(1, "queue 'Regression' exists - #". $queue->id );
+ } else {
+ $queue->Create( Name => 'Regression' );
+ ok($queue->id, "created queue 'Regression'");
+ }
+}
+
+# get the top page
+my $url = $RT::WebURL;
+$agent->get($url);
+
+is ($agent->{'status'}, 200, "Loaded a page");
+
+
+# {{{ test a login
+
+# follow the link marked "Login"
+
+ok($agent->{form}->find_input('user'));
+
+ok($agent->{form}->find_input('pass'));
+ok ($agent->{'content'} =~ /username:/i);
+$agent->field( 'user' => 'root' );
+$agent->field( 'pass' => 'password' );
+# the field isn't named, so we have to click link 0
+$agent->click(0);
+is($agent->{'status'}, 200, "Fetched the page ok");
+ok( $agent->{'content'} =~ /Logout/i, "Found a logout link");
+
+# }}}
+
+# {{{ Query Builder tests
+
+my $response = $agent->get($url."Search/Build.html");
+ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
+
+# Adding items
+
+# set the first value
+ok($agent->form_name('BuildQuery'), "found the form once");
+$agent->field("ActorField", "Owner");
+$agent->field("ActorOp", "=");
+$agent->field("ValueOfActor", "Nobody");
+$agent->submit();
+
+# set the next value
+ok($agent->form_name('BuildQuery'), "found the form again");
+$agent->field("QueueOp", "!=");
+$agent->field("ValueOfQueue", "Regression");
+$agent->submit();
+
+ok($agent->form_name('BuildQuery'), "found the form a third time");
+
+sub getQueryFromForm {
+ $agent->form_name('BuildQuery');
+ # This pulls out the "hidden input" query from the page
+ my $q = $agent->current_form->find_input("Query")->value;
+ $q =~ s/^\s+//g;
+ $q =~ s/\s+$//g;
+ $q =~ s/\s+/ /g;
+ return $q;
+}
+
+is (getQueryFromForm, "Owner = 'Nobody' AND Queue != 'Regression'");
+
+# We're going to delete the owner
+
+$agent->select("clauses", ["0"] );
+
+$agent->click("DeleteClause");
+
+ok($agent->form_name('BuildQuery'), "found the form a fourth time");
+
+is (getQueryFromForm, "Queue != 'Regression'");
+
+$agent->field("AndOr", "OR");
+
+$agent->select("idOp", ">");
+
+$agent->field("ValueOfid" => "1234");
+
+$agent->click("AddClause");
+
+ok($agent->form_name('BuildQuery'), "found the form again");
+TODO: {
+ local $TODO = "query builder incorrectly quotes numbers";
+ is(getQueryFromForm, "Queue != 'Regression' OR id > 1234", "added something as OR, and number not quoted");
+}
+
+sub selectedClauses {
+ my @clauses = grep { defined } map { $_->value } $agent->current_form->find_input("clauses");
+ return [ @clauses ];
+}
+
+
+is_deeply(selectedClauses, ["1"], 'the id that we just entered is still selected');
+
+# Move the second one up a level
+$agent->click("Up");
+
+ok($agent->form_name('BuildQuery'), "found the form again");
+is(getQueryFromForm, "id > 1234 OR Queue != 'Regression'", "moved up one");
+
+is_deeply(selectedClauses, ["0"], 'the one we moved up is selected');
+
+$agent->click("Right");
+
+ok($agent->form_name('BuildQuery'), "found the form again");
+is(getQueryFromForm, "Queue != 'Regression' OR ( id > 1234 )", "moved over to the right (and down)");
+is_deeply(selectedClauses, ["2"], 'the one we moved right is selected');
+
+$agent->select("clauses", ["1"]);
+
+$agent->click("Up");
+
+ok($agent->form_name('BuildQuery'), "found the form again");
+is(getQueryFromForm, "( id > 1234 ) OR Queue != 'Regression'", "moved up");
+
+$agent->select("clauses", ["0"]); # this is a null clause
+$agent->click("Up");
+ok($agent->form_name('BuildQuery'), "found the form again");
+$agent->content_like(qr/error: can\S+t move up/, "i shouldn't have been able to hit up");
+
+$agent->click("Left");
+ok($agent->form_name('BuildQuery'), "found the form again");
+$agent->content_like(qr/error: can\S+t move left/, "i shouldn't have been able to hit left");
+
+$agent->select("clauses", ["1"]);
+$agent->select("ValueOfStatus" => "stalled");
+$agent->submit;
+ok($agent->form_name('BuildQuery'), "found the form again");
+is_deeply(selectedClauses, ["2"], 'the one we added is selected');
+is( getQueryFromForm, "( id > 1234 AND Status = 'stalled' ) OR Queue != 'Regression'", "added new one" );
+
+# click advanced, enter "C1 OR ( C2 AND C3 )", apply, aggregators should stay the same.
+{
+ my $response = $agent->get($url."Search/Edit.html");
+ ok( $response->is_success, "Fetched /Search/Edit.html" );
+ ok($agent->form_number(3), "found the form");
+ $agent->field("Query", "Status = 'new' OR ( Status = 'open' AND Subject LIKE 'office' )");
+ $agent->submit;
+ is( getQueryFromForm,
+ "Status = 'new' OR ( Status = 'open' AND Subject LIKE 'office' )",
+ "no aggregators change"
+ );
+}
+
+# - new items go one level down
+# - add items at currently selected level
+# - if nothing is selected, add at end, one level down
+#
+# move left
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move left if you're at the top level
+#
+# move right
+# - error if nothing selected
+# - same item should be selected after move
+# - can always move right (no max depth...should there be?)
+#
+# move up
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move up if you're first in the list
+#
+# move down
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move down if you're last in the list
+#
+# toggle
+# - error if nothing selected
+# - change all aggregators in the grouping
+# - don't change any others
+#
+# delete
+# - error if nothing selected
+# - delete currently selected item
+# - delete all children of a grouping
+# - if delete leaves a node with no children, delete that, too
+# - what should be selected?
+#
+# Clear
+# - clears entire query
+# - clears it from the session, too
+
+# }}}
+
+# create a custom field with nonascii name and try to add a condition
+{
+ my $cf = RT::CustomField->new( $RT::SystemUser );
+ $cf->LoadByName( Name => "\x{442}", Queue => 0 );
+ if ( $cf->id ) {
+ is($cf->Type, 'Freeform', 'loaded and type is correct');
+ } else {
+ my ($return, $msg) = $cf->Create(
+ Name => "\x{442}",
+ Queue => 0,
+ Type => 'Freeform',
+ );
+ ok($return, 'created CF') or diag "error: $msg";
+ }
+
+ my $response = $agent->get($url."Search/Build.html?NewQuery=1");
+ ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
+
+ ok($agent->form_name('BuildQuery'), "found the form once");
+ $agent->field("ValueOf'CF.{\321\202}'", "\321\201");
+ $agent->submit();
+ is( getQueryFromForm,
+ "'CF.{\321\202}' LIKE '\321\201'",
+ "no changes, no duplicate condition with badly encoded text"
+ );
+
+ $cf->delete();
+}
+
+1;
diff --git a/rt/lib/t/regression/22search_tix_by_txn.t b/rt/lib/t/regression/22search_tix_by_txn.t
new file mode 100644
index 0000000..bec61b5
--- /dev/null
+++ b/rt/lib/t/regression/22search_tix_by_txn.t
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use Test::More tests => 10;
+
+BEGIN{ $ENV{'TZ'} = 'GMT'};
+
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+my $SUBJECT = "Search test - ".$$;
+
+use_ok('RT::Tickets');
+my $tix = RT::Tickets->new($RT::SystemUser);
+can_ok($tix, 'FromSQL');
+$tix->FromSQL('Updated = "2005-08-05" AND Subject = "$SUBJECT"');
+
+ok(! $tix->Count, "Searching for tickets updated on a random date finds nothing" . $tix->Count);
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+$ticket->Create(Queue => 'General', Subject => $SUBJECT);
+ok ($ticket->id, "We created a ticket");
+my ($id, $txnid, $txnobj) = $ticket->Comment( Content => 'A comment that happend on 2004-01-01');
+
+isa_ok($txnobj, 'RT::Transaction');
+
+ok($txnobj->CreatedObj->ISO);
+my ( $sid,$smsg) = $txnobj->__Set(Field => 'Created', Value => '2005-08-05 20:00:56');
+ok($sid,$smsg);
+is($txnobj->Created,'2005-08-05 20:00:56');
+is($txnobj->CreatedObj->ISO,'2005-08-05 20:00:56');
+
+$tix->FromSQL(qq{Updated = "2005-08-05" AND Subject = "$SUBJECT"});
+is( $tix->Count, 1);
+
diff --git a/rt/lib/t/regression/22search_tix_by_watcher.t b/rt/lib/t/regression/22search_tix_by_watcher.t
new file mode 100644
index 0000000..4dd11af
--- /dev/null
+++ b/rt/lib/t/regression/22search_tix_by_watcher.t
@@ -0,0 +1,228 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use Test::More tests => 79;
+use_ok('RT');
+RT::LoadConfig();
+RT::Init();
+use RT::Ticket;
+
+my $q = RT::Queue->new( $RT::SystemUser );
+my $queue = 'SearchTests-'. rand(200);
+$q->Create( Name => $queue );
+
+my ($total, @data, @tickets, %test) = (0, ());
+
+sub add_tix_from_data {
+ my @res = ();
+ while (@data) {
+ my $t = RT::Ticket->new($RT::SystemUser);
+ my ( $id, undef $msg ) = $t->Create(
+ Queue => $q->id,
+ %{ shift(@data) },
+ );
+ ok( $id, "ticket created" ) or diag("error: $msg");
+ push @res, $t;
+ $total++;
+ }
+ return @res;
+}
+
+sub run_tests {
+ my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets;
+ foreach my $key ( sort keys %test ) {
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL( "( $query_prefix ) AND ( $key )" );
+
+ my $error = 0;
+
+ my $count = 0;
+ $count++ foreach grep $_, values %{ $test{$key} };
+ is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1;
+
+ my $good_tickets = 1;
+ while ( my $ticket = $tix->Next ) {
+ next if $test{$key}->{ $ticket->Subject };
+ diag $ticket->Subject ." ticket has been found when it's not expected";
+ $good_tickets = 0;
+ }
+ ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1;
+
+ diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error;
+ }
+}
+
+@data = (
+ { Subject => 'xy', Requestor => ['x@example.com', 'y@example.com'] },
+ { Subject => 'x', Requestor => 'x@example.com' },
+ { Subject => 'y', Requestor => 'y@example.com' },
+ { Subject => '-', },
+ { Subject => 'z', Requestor => 'z@example.com' },
+);
+%test = (
+ 'Requestor = "x@example.com"' => { xy => 1, x => 1, y => 0, '-' => 0, z => 0 },
+ 'Requestor != "x@example.com"' => { xy => 0, x => 0, y => 1, '-' => 1, z => 1 },
+
+ 'Requestor = "y@example.com"' => { xy => 1, x => 0, y => 1, '-' => 0, z => 0 },
+ 'Requestor != "y@example.com"' => { xy => 0, x => 1, y => 0, '-' => 1, z => 1 },
+
+ 'Requestor LIKE "@example.com"' => { xy => 1, x => 1, y => 1, '-' => 0, z => 1 },
+ 'Requestor NOT LIKE "@example.com"' => { xy => 0, x => 0, y => 0, '-' => 1, z => 0 },
+
+ 'Requestor IS NULL' => { xy => 0, x => 0, y => 0, '-' => 1, z => 0 },
+ 'Requestor IS NOT NULL' => { xy => 1, x => 1, y => 1, '-' => 0, z => 1 },
+
+# this test is a todo, we run it later
+# 'Requestor = "x@example.com" AND Requestor = "y@example.com"' => { xy => 1, x => 0, y => 0, '-' => 0, z => 0 },
+ 'Requestor = "x@example.com" OR Requestor = "y@example.com"' => { xy => 1, x => 1, y => 1, '-' => 0, z => 0 },
+
+ 'Requestor != "x@example.com" AND Requestor != "y@example.com"' => { xy => 0, x => 0, y => 0, '-' => 1, z => 1 },
+ 'Requestor != "x@example.com" OR Requestor != "y@example.com"' => { xy => 0, x => 1, y => 1, '-' => 1, z => 1 },
+
+ 'Requestor = "x@example.com" AND Requestor != "y@example.com"' => { xy => 0, x => 1, y => 0, '-' => 0, z => 0 },
+ 'Requestor = "x@example.com" OR Requestor != "y@example.com"' => { xy => 1, x => 1, y => 0, '-' => 1, z => 1 },
+
+ 'Requestor != "x@example.com" AND Requestor = "y@example.com"' => { xy => 0, x => 0, y => 1, '-' => 0, z => 0 },
+ 'Requestor != "x@example.com" OR Requestor = "y@example.com"' => { xy => 1, x => 0, y => 1, '-' => 1, z => 1 },
+);
+@tickets = add_tix_from_data();
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue'");
+ is($tix->Count, $total, "found $total tickets");
+}
+run_tests();
+
+TODO: {
+ local $TODO = "we can't generate this query yet";
+ %test = (
+ 'Requestor = "x@example.com" AND Requestor = "y@example.com"'
+ => { xy => 1, x => 0, y => 0, '-' => 0, z => 0 },
+ );
+ run_tests();
+}
+
+@data = (
+ { Subject => 'xy', Cc => ['x@example.com'], Requestor => [ 'y@example.com' ] },
+ { Subject => 'x-', Cc => ['x@example.com'], Requestor => [] },
+ { Subject => '-y', Cc => [], Requestor => [ 'y@example.com' ] },
+ { Subject => '-', },
+ { Subject => 'zz', Cc => ['z@example.com'], Requestor => [ 'z@example.com' ] },
+ { Subject => 'z-', Cc => ['z@example.com'], Requestor => [] },
+ { Subject => '-z', Cc => [], Requestor => [ 'z@example.com' ] },
+);
+%test = (
+ 'Cc = "x@example.com" AND Requestor = "y@example.com"' =>
+ { xy => 1, 'x-' => 0, '-y' => 0, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 },
+ 'Cc = "x@example.com" OR Requestor = "y@example.com"' =>
+ { xy => 1, 'x-' => 1, '-y' => 1, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 },
+
+ 'Cc != "x@example.com" AND Requestor = "y@example.com"' =>
+ { xy => 0, 'x-' => 0, '-y' => 1, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 },
+ 'Cc != "x@example.com" OR Requestor = "y@example.com"' =>
+ { xy => 1, 'x-' => 0, '-y' => 1, '-' => 1, zz => 1, 'z-' => 1, '-z' => 1 },
+
+ 'Cc IS NULL AND Requestor = "y@example.com"' =>
+ { xy => 0, 'x-' => 0, '-y' => 1, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 },
+ 'Cc IS NULL OR Requestor = "y@example.com"' =>
+ { xy => 1, 'x-' => 0, '-y' => 1, '-' => 1, zz => 0, 'z-' => 0, '-z' => 1 },
+
+ 'Cc IS NOT NULL AND Requestor = "y@example.com"' =>
+ { xy => 1, 'x-' => 0, '-y' => 0, '-' => 0, zz => 0, 'z-' => 0, '-z' => 0 },
+ 'Cc IS NOT NULL OR Requestor = "y@example.com"' =>
+ { xy => 1, 'x-' => 1, '-y' => 1, '-' => 0, zz => 1, 'z-' => 1, '-z' => 0 },
+);
+@tickets = add_tix_from_data();
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue'");
+ is($tix->Count, $total, "found $total tickets");
+}
+run_tests();
+
+# owner is special watcher because reference is duplicated in two places,
+# owner was an ENUM field now it's WATCHERFIELD, but should support old
+# style ENUM searches for backward compatibility
+my $nobody = RT::Nobody();
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Owner = '". $nobody->id ."'");
+ ok($tix->Count, "found ticket(s)");
+}
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Owner = '". $nobody->Name ."'");
+ ok($tix->Count, "found ticket(s)");
+}
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Owner != '". $nobody->id ."'");
+ is($tix->Count, 0, "found ticket(s)");
+}
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Owner != '". $nobody->Name ."'");
+ is($tix->Count, 0, "found ticket(s)");
+}
+
+{
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Owner.Name LIKE 'nob'");
+ ok($tix->Count, "found ticket(s)");
+}
+
+{
+ # create ticket and force type to not a 'ticket' value
+ # bug #6898@rt3.fsck.com
+ # and http://marc.theaimsgroup.com/?l=rt-devel&m=112662934627236&w=2
+ @data = ( { Subject => 'not a ticket' } );
+ my($t) = add_tix_from_data();
+ $t->_Set( Field => 'Type',
+ Value => 'not a ticket',
+ CheckACL => 0,
+ RecordTransaction => 0,
+ );
+ $total--;
+
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND Owner = 'Nobody'");
+ is($tix->Count, $total, "found ticket(s)");
+}
+
+{
+ my $everyone = RT::Group->new( $RT::SystemUser );
+ $everyone->LoadSystemInternalGroup('Everyone');
+ ok($everyone->id, "loaded 'everyone' group");
+ my($id, $msg) = $everyone->PrincipalObj->GrantRight( Right => 'OwnTicket',
+ Object => $q
+ );
+ ok($id, "granted OwnTicket right to Everyone on '$queue'") or diag("error: $msg");
+
+ my $u = RT::User->new( $RT::SystemUser );
+ $u->LoadOrCreateByEmail('alpha@example.com');
+ ok($u->id, "loaded user");
+ @data = ( { Subject => '4', Owner => $u->id } );
+ my($t) = add_tix_from_data();
+ is( $t->Owner, $u->id, "created ticket with custom owner" );
+ my $u_alpha_id = $u->id;
+
+ $u = RT::User->new( $RT::SystemUser );
+ $u->LoadOrCreateByEmail('bravo@example.com');
+ ok($u->id, "loaded user");
+ @data = ( { Subject => '5', Owner => $u->id } );
+ ($t) = add_tix_from_data();
+ is( $t->Owner, $u->id, "created ticket with custom owner" );
+ my $u_bravo_id = $u->id;
+
+ my $tix = RT::Tickets->new($RT::SystemUser);
+ $tix->FromSQL("Queue = '$queue' AND
+ ( Owner = '$u_alpha_id' OR
+ Owner = '$u_bravo_id' )"
+ );
+ is($tix->Count, 2, "found ticket(s)");
+}
+
+
+exit(0)
diff --git a/rt/lib/t/regression/23-batch-upload-csv.t b/rt/lib/t/regression/23-batch-upload-csv.t
new file mode 100644
index 0000000..fc9436a
--- /dev/null
+++ b/rt/lib/t/regression/23-batch-upload-csv.t
@@ -0,0 +1,47 @@
+#!/usr/bin/perl -w
+use strict; use warnings;
+
+use Test::More qw/no_plan/;
+use_ok('RT');
+RT::LoadConfig();
+RT::Init();
+use_ok('RT::Action::CreateTickets');
+
+my $QUEUE = 'uploadtest-'.$$;
+
+my $queue_obj = RT::Queue->new($RT::SystemUser);
+$queue_obj->Create(Name => $QUEUE);
+
+my $cf = RT::CustomField->new($RT::SystemUser);
+my ($val,$msg) = $cf->Create(Name => 'Work Package-'.$$, Type => 'Freeform', LookupType => RT::Ticket->CustomFieldLookupType, MaxValues => 1);
+ok($cf->id);
+ok($val,$msg);
+($val, $msg) = $cf->AddToObject($queue_obj);
+ok($val,$msg);
+ok($queue_obj->TicketCustomFields()->Count, "We have a custom field, at least");
+
+
+my $data = <<EOF;
+id,Queue,Subject,Status,Requestor,@{[$cf->Name]}
+create-1,$QUEUE,hi,new,root,2.0
+create-2,$QUEUE,hello,new,root,3.0
+EOF
+
+my $action = RT::Action::CreateTickets->new(CurrentUser => RT::CurrentUser->new('root'));
+ok ($action->CurrentUser->id , "WE have a current user");
+
+$action->Parse(Content => $data);
+my @results = $action->CreateByTemplate();
+
+my $tix = RT::Tickets->new($RT::SystemUser);
+$tix->FromSQL ("Queue = '". $QUEUE."'");
+$tix->OrderBy( FIELD => 'id', ORDER => 'ASC' );
+ok($tix->Count);
+my $first = $tix->First();
+is($first->Subject(), 'hi');
+is($first->FirstCustomFieldValue($cf->id), '2.0');
+
+my $second = $tix->Next;
+is($second->Subject(), 'hello');
+is($second->FirstCustomFieldValue($cf->id), '3.0');
+1;
diff --git a/rt/lib/t/regression/23-web_attachments.t b/rt/lib/t/regression/23-web_attachments.t
new file mode 100644
index 0000000..adc38ad
--- /dev/null
+++ b/rt/lib/t/regression/23-web_attachments.t
@@ -0,0 +1,60 @@
+#!/usr/bin/perl -w
+use strict;
+
+use Test::More tests => 15;
+use RT;
+RT::LoadConfig;
+RT::Init;
+use Test::WWW::Mechanize;
+
+$RT::WebURL ||= 0; # avoid stupid warning
+my $BaseURL = $RT::WebURL;
+use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bplogo.gif';
+use constant FaviconFile => $RT::MasonComponentRoot .'/NoAuth/images/favicon.png';
+
+my $queue_name = 'General';
+
+my $m = Test::WWW::Mechanize->new;
+isa_ok($m, 'Test::WWW::Mechanize');
+
+$m->get_ok( $BaseURL."?user=root;pass=password" );
+$m->content_like(qr/Logout/, 'we did log in');
+
+my $qid;
+{
+ $m->content =~ /<SELECT\s+NAME\s*="Queue"\s*>.*?<OPTION\s+VALUE="(\d+)".*?>\s*\Q$queue_name\E\s*<\/OPTION>/msig;
+ ok( $qid = $1, "found id of the '$queue_name' queue");
+}
+
+$m->form_name('CreateTicketInQueue');
+$m->field('Queue', $qid);
+$m->submit;
+is($m->status, 200, "request successful");
+$m->content_like(qr/Create a new ticket/, 'ticket create page');
+
+$m->form_name('TicketCreate');
+$m->field('Subject', 'Attachments test');
+$m->field('Attach', LogoFile);
+$m->field('Content', 'Some content');
+$m->submit;
+is($m->status, 200, "request successful");
+
+$m->content_like(qr/Attachments test/, 'we have subject on the page');
+$m->content_like(qr/Some content/, 'and content');
+$m->content_like(qr/Download bplogo\.gif/, 'page has file name');
+
+$m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+$m->form_name('TicketUpdate');
+$m->field('Attach', LogoFile);
+$m->click('AddMoreAttach');
+is($m->status, 200, "request successful");
+
+$m->form_name('TicketUpdate');
+$m->field('Attach', FaviconFile);
+$m->field('UpdateContent', 'Message');
+$m->click('SubmitTicket');
+is($m->status, 200, "request successful");
+
+$m->content_like(qr/Download bplogo\.gif/, 'page has file name');
+$m->content_like(qr/Download favicon\.png/, 'page has file name');
+
diff --git a/rt/lib/t/regression/23cfsort.t b/rt/lib/t/regression/23cfsort.t
new file mode 100644
index 0000000..85decc7
--- /dev/null
+++ b/rt/lib/t/regression/23cfsort.t
@@ -0,0 +1,192 @@
+#!/usr/bin/perl
+
+use Test::More tests => 21;
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+use strict;
+use warnings;
+
+use RT::Tickets;
+use RT::Queue;
+use RT::CustomField;
+
+my($ret,$msg);
+
+
+# Test Sorting by custom fields.
+
+# ---- Create a queue to test with.
+my $queue = "CFSortQueue-$$";
+my $queue_obj = RT::Queue->new( $RT::SystemUser );
+($ret, $msg) = $queue_obj->Create(
+ Name => $queue,
+ Description => 'queue for custom field sort testing'
+);
+ok($ret, "$queue test queue creation. $msg");
+
+# ---- Create some custom fields. We're not currently using all of
+# them to test with, but the more the merrier.
+my $cfO = RT::CustomField->new($RT::SystemUser);
+my $cfA = RT::CustomField->new($RT::SystemUser);
+my $cfB = RT::CustomField->new($RT::SystemUser);
+my $cfC = RT::CustomField->new($RT::SystemUser);
+
+($ret, $msg) = $cfO->Create( Name => 'Order',
+ Queue => 0,
+ SortOrder => 1,
+ Description => q{Something to compare results for, since we can't guarantee ticket ID},
+ Type=> 'FreeformSingle');
+ok($ret, "Custom Field Order created");
+
+($ret, $msg) = $cfA->Create( Name => 'Alpha',
+ Queue => $queue_obj->id,
+ SortOrder => 1,
+ Description => 'A Testing custom field',
+ Type=> 'FreeformSingle');
+ok($ret, "Custom Field Alpha created");
+
+($ret, $msg) = $cfB->Create( Name => 'Beta',
+ Queue => $queue_obj->id,
+ Description => 'A Testing custom field',
+ Type=> 'FreeformSingle');
+ok($ret, "Custom Field Beta created");
+
+($ret, $msg) = $cfC->Create( Name => 'Charlie',
+ Queue => $queue_obj->id,
+ Description => 'A Testing custom field',
+ Type=> 'FreeformSingle');
+ok($ret, "Custom Field Charlie created");
+
+# ----- Create some tickets to test with. Assign them some values to
+# make it easy to sort with.
+my $t1 = RT::Ticket->new($RT::SystemUser);
+$t1->Create( Queue => $queue_obj->Id,
+ Subject => 'One',
+ );
+$t1->AddCustomFieldValue(Field => $cfO->Id, Value => '1');
+$t1->AddCustomFieldValue(Field => $cfA->Id, Value => '2');
+$t1->AddCustomFieldValue(Field => $cfB->Id, Value => '1');
+$t1->AddCustomFieldValue(Field => $cfC->Id, Value => 'BBB');
+
+my $t2 = RT::Ticket->new($RT::SystemUser);
+$t2->Create( Queue => $queue_obj->Id,
+ Subject => 'Two',
+ );
+$t2->AddCustomFieldValue(Field => $cfO->Id, Value => '2');
+$t2->AddCustomFieldValue(Field => $cfA->Id, Value => '1');
+$t2->AddCustomFieldValue(Field => $cfB->Id, Value => '2');
+$t2->AddCustomFieldValue(Field => $cfC->Id, Value => 'AAA');
+
+# helper
+sub check_order {
+ my ($tx, @order) = @_;
+ my @results;
+ while (my $t = $tx->Next) {
+ push @results, $t->CustomFieldValues($cfO->Id)->First->Content;
+ }
+ my $results = join (" ",@results);
+ my $order = join(" ",@order);
+ @_ = ($results, $order , "Ordered correctly: $order");
+ goto \&is;
+}
+
+# The real tests start here
+my $tx = new RT::Tickets( $RT::SystemUser );
+
+
+# Make sure we can sort in both directions on a queue specific field.
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderBy( FIELD => "CF.${queue}.{Charlie}", ORDER => 'DES' );
+is($tx->Count,2 ,"We found 2 tickets when lookign for cf charlie");
+check_order( $tx, 1, 2);
+
+$tx = new RT::Tickets( $RT::SystemUser );
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderBy( FIELD => "CF.${queue}.{Charlie}", ORDER => 'ASC' );
+is($tx->Count,2, "We found two tickets when sorting by cf charlie without limiting to it" );
+check_order( $tx, 2, 1);
+
+# When ordering by _global_ CustomFields, if more than one queue has a
+# CF named Charlie, things will go bad. So, these results are uniqued
+# in Tickets_Overlay.
+$tx = new RT::Tickets( $RT::SystemUser );
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderBy( FIELD => "CF.{Charlie}", ORDER => 'DESC' );
+diag $tx->BuildSelectQuery;
+is($tx->Count,2);
+TODO: {
+ local $TODO = 'order by CF fail';
+check_order( $tx, 1, 2);
+}
+
+$tx = new RT::Tickets( $RT::SystemUser );
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderBy( FIELD => "CF.{Charlie}", ORDER => 'ASC' );
+diag $tx->BuildSelectQuery;
+is($tx->Count,2);
+TODO: {
+ local $TODO = 'order by CF fail';
+check_order( $tx, 2, 1);
+}
+
+# Add a new ticket, to test sorting on multiple columns.
+my $t3 = RT::Ticket->new($RT::SystemUser);
+$t3->Create( Queue => $queue_obj->Id,
+ Subject => 'Three',
+ );
+$t3->AddCustomFieldValue(Field => $cfO->Id, Value => '3');
+$t3->AddCustomFieldValue(Field => $cfA->Id, Value => '3');
+$t3->AddCustomFieldValue(Field => $cfB->Id, Value => '2');
+$t3->AddCustomFieldValue(Field => $cfC->Id, Value => 'AAA');
+
+$tx = new RT::Tickets( $RT::SystemUser );
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderByCols(
+ { FIELD => "CF.${queue}.{Charlie}", ORDER => 'ASC' },
+ { FIELD => "CF.${queue}.{Alpha}", ORDER => 'DES' },
+);
+is($tx->Count,3);
+TODO: {
+ local $TODO = 'order by CF fail';
+check_order( $tx, 3, 2, 1);
+}
+
+$tx = new RT::Tickets( $RT::SystemUser );
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderByCols(
+ { FIELD => "CF.${queue}.{Charlie}", ORDER => 'DES' },
+ { FIELD => "CF.${queue}.{Alpha}", ORDER => 'ASC' },
+);
+is($tx->Count,3);
+TODO: {
+ local $TODO = 'order by CF fail';
+check_order( $tx, 1, 2, 3);
+}
+
+# Reverse the order of the secondary column, which changes the order
+# of the first two tickets.
+$tx = new RT::Tickets( $RT::SystemUser );
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderByCols(
+ { FIELD => "CF.${queue}.{Charlie}", ORDER => 'ASC' },
+ { FIELD => "CF.${queue}.{Alpha}", ORDER => 'ASC' },
+);
+is($tx->Count,3);
+TODO: {
+ local $TODO = 'order by CF fail';
+check_order( $tx, 2, 3, 1);
+}
+
+$tx = new RT::Tickets( $RT::SystemUser );
+$tx->FromSQL(qq[queue="$queue"] );
+$tx->OrderByCols(
+ { FIELD => "CF.${queue}.{Charlie}", ORDER => 'DES' },
+ { FIELD => "CF.${queue}.{Alpha}", ORDER => 'DES' },
+);
+is($tx->Count,3);
+TODO: {
+ local $TODO = 'order by CF fail';
+check_order( $tx, 1, 3, 2);
+}
diff --git a/rt/lib/t/regression/24pawsort.t b/rt/lib/t/regression/24pawsort.t
new file mode 100644
index 0000000..665c325
--- /dev/null
+++ b/rt/lib/t/regression/24pawsort.t
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+
+use Test::More qw/no_plan/;
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+use strict;
+use warnings;
+
+use RT::Tickets;
+use RT::Queue;
+use RT::CustomField;
+
+my($ret,$msg);
+
+# Test Paw Sort
+
+
+
+# ---- Create a queue to test with.
+my $queue = "PAWSortQueue-$$";
+my $queue_obj = RT::Queue->new($RT::SystemUser);
+($ret, $msg) = $queue_obj->Create(Name => $queue,
+ Description => 'queue for custom field sort testing');
+ok($ret, "$queue test queue creation. $msg");
+
+
+# ---- Create some users
+
+my $me = RT::User->new($RT::SystemUser);
+($ret, $msg) = $me->Create(Name => "Me$$", EmailAddress => $$.'create-me-1@example.com');
+($ret, $msg) = $me->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'OwnTicket');
+($ret, $msg) = $me->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'SeeQueue');
+($ret, $msg) = $me->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'ShowTicket');
+my $you = RT::User->new($RT::SystemUser);
+($ret, $msg) = $you->Create(Name => "You$$", EmailAddress => $$.'create-you-1@example.com');
+($ret, $msg) = $you->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'OwnTicket');
+($ret, $msg) = $you->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'SeeQueue');
+($ret, $msg) = $you->PrincipalObj->GrantRight(Object =>$queue_obj, Right => 'ShowTicket');
+
+my $nobody = RT::User->new($RT::SystemUser);
+$nobody->Load('nobody');
+
+
+# ----- Create some tickets to test with. Assign them some values to
+# make it easy to sort with.
+
+my @tickets = (
+ [qw[1 10], $me],
+ [qw[2 20], $me],
+ [qw[3 20], $you],
+ [qw[4 30], $you],
+ [qw[5 5], $nobody],
+ [qw[6 55], $nobody],
+ );
+for (@tickets) {
+ my $t = RT::Ticket->new($RT::SystemUser);
+ $t->Create( Queue => $queue_obj->Id,
+ Subject => $_->[0],
+ Owner => $_->[2]->Id,
+ Priority => $_->[1],
+ );
+}
+
+sub check_order {
+ my ($tx, @order) = @_;
+ my @results;
+ while (my $t = $tx->Next) {
+ push @results, $t->Subject;
+ }
+ my $results = join (" ",@results);
+ my $order = join(" ",@order);
+ is( $results, $order );
+}
+
+
+# The real tests start here
+
+my $cme = new RT::CurrentUser( $me );
+my $metx = new RT::Tickets( $cme );
+# Make sure we can sort in both directions on a queue specific field.
+$metx->FromSQL(qq[queue="$queue"] );
+$metx->OrderBy( FIELD => "Custom.Ownership", ORDER => 'ASC' );
+is($metx->Count,6);
+check_order( $metx, qw[2 1 6 5 4 3]);
+
+$metx->OrderBy( FIELD => "Custom.Ownership", ORDER => 'DESC' );
+is($metx->Count,6);
+check_order( $metx, reverse qw[2 1 6 5 4 3]);
+
+
+
+my $cyou = new RT::CurrentUser( $you );
+my $youtx = new RT::Tickets( $cyou );
+# Make sure we can sort in both directions on a queue specific field.
+$youtx->FromSQL(qq[queue="$queue"] );
+$youtx->OrderBy( FIELD => "Custom.Ownership", ORDER => 'ASC' );
+is($youtx->Count,6);
+check_order( $youtx, qw[4 3 6 5 2 1]);
+
+__END__
+
+
diff --git a/rt/lib/t/regression/25scrip_order.t b/rt/lib/t/regression/25scrip_order.t
new file mode 100644
index 0000000..0e11989
--- /dev/null
+++ b/rt/lib/t/regression/25scrip_order.t
@@ -0,0 +1,57 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More tests => 7;
+
+use RT;
+RT::LoadConfig();
+RT::Init;
+
+# {{{ test scrip ordering based on description
+
+my $scrip_queue = RT::Queue->new($RT::SystemUser);
+my ($queue_id, $msg) = $scrip_queue->Create( Name => "ScripOrdering-$$",
+ Description => 'Test scrip ordering by description' );
+ok($queue_id, "Created scrip-ordering test queue? ".$msg);
+
+my $priority_ten_scrip = RT::Scrip->new($RT::SystemUser);
+(my $id, $msg) = $priority_ten_scrip->Create(
+ Description => "10 set priority $$",
+ Queue => $queue_id,
+ ScripCondition => 'On Create',
+ ScripAction => 'User Defined',
+ CustomPrepareCode => '$RT::Logger->debug("Setting priority to 10..."); return 1;',
+ CustomCommitCode => '$self->TicketObj->SetPriority(10);',
+ Template => 'Blank',
+ Stage => 'TransactionCreate',
+);
+ok($id, "Created priority-10 scrip? ".$msg);
+
+my $priority_five_scrip = RT::Scrip->new($RT::SystemUser);
+($id, $msg) = $priority_ten_scrip->Create(
+ Description => "05 set priority $$",
+ Queue => $queue_id,
+ ScripCondition => 'On Create',
+ ScripAction => 'User Defined',
+ CustomPrepareCode => '$RT::Logger->debug("Setting priority to 5..."); return 1;',
+ CustomCommitCode => '$self->TicketObj->SetPriority(5);',
+ Template => 'Blank',
+ Stage => 'TransactionCreate',
+);
+ok($id, "Created priority-5 scrip? ".$msg);
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+($id, $msg) = $ticket->Create(
+ Queue => $queue_id,
+ Requestor => 'order@example.com',
+ Subject => "Scrip order test $$",
+);
+ok($ticket->id, "Created ticket? id=$id");
+
+ok($ticket->Priority != 0, "Ticket shouldn't be priority 0");
+ok($ticket->Priority != 5, "Ticket shouldn't be priority 5");
+ok($ticket->Priority == 10, "Ticket should be priority 10");
+
+# }}}
+
+1;
diff --git a/rt/lib/t/regression/26command_line.t b/rt/lib/t/regression/26command_line.t
new file mode 100644
index 0000000..457c63a
--- /dev/null
+++ b/rt/lib/t/regression/26command_line.t
@@ -0,0 +1,445 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::Expect;
+#use Test::More qw/no_plan/;
+use Test::More tests => 218;
+
+use RT;
+RT::LoadConfig();
+RT::Init;
+
+use RT::User;
+use RT::Queue;
+
+my $rt_tool_path = "$RT::BinPath/rt";
+
+# {{{ test configuration options
+
+# config directives:
+# (in $CWD/.rtrc)
+# - server <URL> URL to RT server.
+# - user <username> RT username.
+# - passwd <passwd> RT user's password.
+# - query <RT Query> Default RT Query for list action
+# - orderby <order> Default RT order for list action
+#
+# Blank and #-commented lines are ignored.
+
+# environment variables
+# The following environment variables override any corresponding
+# values defined in configuration files:
+#
+# - RTUSER
+$ENV{'RTUSER'} = 'root';
+# - RTPASSWD
+$ENV{'RTPASSWD'} = 'password';
+# - RTSERVER
+$RT::Logger->debug("Connecting to server at $RT::WebBaseURL...");
+$ENV{'RTSERVER'} = $RT::WebBaseURL;
+# - RTDEBUG Numeric debug level. (Set to 3 for full logs.)
+$ENV{'RTDEBUG'} = '1';
+# - RTCONFIG Specifies a name other than ".rtrc" for the
+# configuration file.
+#
+# - RTQUERY Default RT Query for rt list
+# - RTORDERBY Default order for rt list
+
+
+# }}}
+
+# {{{ test ticket manipulation
+
+# create a ticket
+expect_run(
+ command => "$rt_tool_path shell",
+ prompt => 'rt> ',
+ quit => 'quit',
+);
+expect_send(q{create -t ticket set subject='new ticket' add cc=foo@example.com}, "Creating a ticket...");
+expect_like(qr/Ticket \d+ created/, "Created the ticket");
+expect_handle->before() =~ /Ticket (\d+) created/;
+my $ticket_id = $1;
+ok($ticket_id, "Got ticket id=$ticket_id");
+expect_send(q{create -t ticket set subject='new ticket'}, "Creating a ticket as just a subject...");
+expect_like(qr/Ticket \d+ created/, "Created the ticket");
+
+# make sure we can request things as 'rt foo'
+expect_send(q{rt create -t ticket set subject='rt ticket'}, "Creating a ticket with 'rt create'...");
+expect_like(qr/Ticket \d+ created/, "Created the ticket");
+
+# {{{ test queue manipulation
+
+# creating queues
+expect_send("create -t queue set Name='NewQueue$$'", 'Creating a queue...');
+expect_like(qr/Queue \d+ created/, 'Created the queue');
+expect_handle->before() =~ /Queue (\d+) created/;
+my $queue_id = $1;
+ok($queue_id, "Got queue id=$queue_id");
+# updating users
+expect_send("edit queue/$queue_id set Name='EditedQueue$$'", 'Editing the queue');
+expect_like(qr/Queue $queue_id updated/, 'Edited the queue');
+expect_send("show queue/$queue_id", 'Showing the queue...');
+expect_like(qr/id: queue\/$queue_id/, 'Saw the queue');
+expect_like(qr/Name: EditedQueue$$/, 'Saw the modification');
+TODO: {
+ todo_skip "Listing non-ticket items doesn't work", 2;
+ expect_send("list -t queue 'id > 0'", 'Listing the queues...');
+ expect_like(qr/$queue_id: EditedQueue$$/, 'Found the queue');
+}
+
+# }}}
+
+
+# Set up a custom field for editing tests
+my $cf = RT::CustomField->new($RT::SystemUser);
+my ($val,$msg) = $cf->Create(Name => 'MyCF'.$$, Type => 'FreeformSingle', Queue => $queue_id);
+ok($val,$msg);
+
+my $othercf = RT::CustomField->new($RT::SystemUser);
+($val,$msg) = $othercf->Create(Name => 'My CF'.$$, Type => 'FreeformSingle', Queue => $queue_id);
+ok($val,$msg);
+
+
+
+# add a comment to ticket
+ expect_send("comment -m 'comment-$$' $ticket_id", "Adding a comment...");
+ expect_like(qr/Message recorded/, "Added the comment");
+ ### should test to make sure it actually got added
+ # add correspondance to ticket (?)
+ expect_send("correspond -m 'correspond-$$' $ticket_id", "Adding correspondence...");
+ expect_like(qr/Message recorded/, "Added the correspondence");
+ ### should test to make sure it actually got added
+
+ # add attachments to a ticket
+ # text attachment
+ check_attachment("$RT::BasePath/lib/t/data/lorem-ipsum");
+ # binary attachment
+ check_attachment($RT::MasonComponentRoot.'/NoAuth/images/bplogo.gif');
+
+# change a ticket's Owner
+expect_send("edit ticket/$ticket_id set owner=root", 'Changing owner...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed owner');
+expect_send("show ticket/$ticket_id -f owner", 'Verifying change...');
+expect_like(qr/Owner: root/, 'Verified change');
+# change a ticket's Requestor
+expect_send("edit ticket/$ticket_id set requestors=foo\@example.com", 'Changing Requestor...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed Requestor');
+expect_send("show ticket/$ticket_id -f requestors", 'Verifying change...');
+expect_like(qr/Requestors: foo\@example.com/, 'Verified change');
+# change a ticket's Cc
+expect_send("edit ticket/$ticket_id set cc=bar\@example.com", 'Changing Cc...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed Cc');
+expect_send("show ticket/$ticket_id -f cc", 'Verifying change...');
+expect_like(qr/Cc: bar\@example.com/, 'Verified change');
+# change a ticket's priority
+expect_send("edit ticket/$ticket_id set priority=10", 'Changing priority...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed priority');
+expect_send("show ticket/$ticket_id -f priority", 'Verifying change...');
+expect_like(qr/Priority: 10/, 'Verified change');
+# move a ticket to a different queue
+expect_send("edit ticket/$ticket_id set queue=EditedQueue$$", 'Changing queue...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed queue');
+expect_send("show ticket/$ticket_id -f queue", 'Verifying change...');
+expect_like(qr/Queue: EditedQueue$$/, 'Verified change');
+# cannot move ticket to a nonexistent queue
+expect_send("edit ticket/$ticket_id set queue=nonexistent-$$", 'Changing to nonexistent queue...');
+expect_like(qr/queue does not exist/i, 'Errored out');
+expect_send("show ticket/$ticket_id -f queue", 'Verifying lack of change...');
+expect_like(qr/Queue: EditedQueue$$/, 'Verified lack of change');
+
+# Test reading and setting custom fields without spaces
+expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking initial value');
+expect_like(qr/CF-myCF$$:/i, 'Verified initial empty value');
+expect_send("edit ticket/$ticket_id set 'CF-myCF$$=VALUE' ", 'Changing CF...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
+expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
+expect_like(qr/CF-myCF$$: VALUE/i, 'Verified change');
+# Test reading and setting custom fields with spaces
+expect_send("show ticket/$ticket_id -f 'CF-my CF$$'", 'Checking initial value');
+expect_like(qr/my CF$$:/i, 'Verified change');
+expect_send("edit ticket/$ticket_id set 'CF-my CF$$=VALUE' ", 'Changing CF...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
+expect_send("show ticket/$ticket_id -f 'CF-my CF$$'", 'Checking new value');
+expect_like(qr/my CF$$: VALUE/i, 'Verified change');
+expect_send("ls 'id = $ticket_id' -f 'CF-my CF$$'", 'Checking new value');
+expect_like(qr/my CF$$: VALUE/i, 'Verified change');
+
+# ...
+# change a ticket's ...[other properties]...
+# ...
+# stall a ticket
+expect_send("edit ticket/$ticket_id set status=stalled", 'Changing status to "stalled"...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed status');
+expect_send("show ticket/$ticket_id -f status", 'Verifying change...');
+expect_like(qr/Status: stalled/, 'Verified change');
+# resolve a ticket
+expect_send("edit ticket/$ticket_id set status=resolved", 'Changing status to "resolved"...');
+expect_like(qr/Ticket $ticket_id updated/, 'Changed status');
+expect_send("show ticket/$ticket_id -f status", 'Verifying change...');
+expect_like(qr/Status: resolved/, 'Verified change');
+# try to set status to an illegal value
+expect_send("edit ticket/$ticket_id set status=quux", 'Changing status to an illegal value...');
+expect_like(qr/illegal value/i, 'Errored out');
+expect_send("show ticket/$ticket_id -f status", 'Verifying lack of change...');
+expect_like(qr/Status: resolved/, 'Verified change');
+
+# }}}
+
+# {{{ display
+
+# show ticket list
+expect_send("ls -s -t ticket -o +id \"Status='resolved'\"", 'Listing resolved tickets...');
+expect_like(qr/$ticket_id: new ticket/, 'Found our ticket');
+# show ticket list verbosely
+expect_send("ls -l -t ticket -o +id \"Status='resolved'\"", 'Listing resolved tickets verbosely...');
+expect_like(qr/id: ticket\/$ticket_id/, 'Found our ticket');
+# show ticket
+expect_send("show -t ticket $ticket_id", 'Showing our ticket...');
+expect_like(qr/id: ticket\/$ticket_id/, 'Got our ticket');
+# show ticket history
+expect_send("show ticket/$ticket_id/history", 'Showing our ticket\'s history...');
+expect_like(qr/Ticket created by root/, 'Got our history');
+TODO: {
+ local $TODO = "Cannot show verbose ticket history right now";
+ # show ticket history verbosely
+ expect_send("show -v ticket/$ticket_id/history", 'Showing our ticket\'s history verbosely...');
+ expect_like(qr/Ticket created by root/, 'Got our history');
+}
+# get attachments from a ticket
+expect_send("show ticket/$ticket_id/attachments", 'Showing ticket attachments...');
+expect_like(qr/id: ticket\/$ticket_id\/attachments/, 'Got our ticket\'s attachments');
+expect_like(qr/Attachments: \d+:\s*\(\S+ \/ \d+\w+\)/, 'Our ticket has an attachment');
+expect_handle->before() =~ /Attachments: (\d+):\s*\((\S+)/;
+my $attachment_id = $1;
+my $attachment_type = $2;
+ok($attachment_id, "Got attachment id=$attachment_id $attachment_type");
+expect_send("show ticket/$ticket_id/attachments/$attachment_id", "Showing attachment $attachment_id...");
+expect_like(qr/ContentType: $attachment_type/, 'Got the attachment');
+
+# }}}
+
+# {{{ test user manipulation
+
+# creating users
+expect_send("create -t user set Name='NewUser$$' EmailAddress='fbar$$\@example.com'", 'Creating a user...');
+expect_like(qr/User \d+ created/, 'Created the user');
+expect_handle->before() =~ /User (\d+) created/;
+my $user_id = $1;
+ok($user_id, "Got user id=$user_id");
+# updating users
+expect_send("edit user/$user_id set Name='EditedUser$$'", 'Editing the user');
+expect_like(qr/User $user_id updated/, 'Edited the user');
+expect_send("show user/$user_id", 'Showing the user...');
+expect_like(qr/id: user\/$user_id/, 'Saw the user');
+expect_like(qr/Name: EditedUser$$/, 'Saw the modification');
+TODO: {
+ todo_skip "Listing non-ticket items doesn't work", 2;
+ expect_send("list -t user 'id > 0'", 'Listing the users...');
+ expect_like(qr/$user_id: EditedUser$$/, 'Found the user');
+}
+
+# }}}
+
+# {{{ test group manipulation
+
+TODO: {
+todo_skip "Group manipulation doesn't work right now", 8;
+# creating groups
+expect_send("create -t group set Name='NewGroup$$'", 'Creating a group...');
+expect_like(qr/Group \d+ created/, 'Created the group');
+expect_handle->before() =~ /Group (\d+) created/;
+my $group_id = $1;
+ok($group_id, "Got group id=$group_id");
+# updating groups
+expect_send("edit group/$group_id set Name='EditedGroup$$'", 'Editing the group');
+expect_like(qr/Group $group_id updated/, 'Edited the group');
+expect_send("show group/$group_id", 'Showing the group...');
+expect_like(qr/id: group\/$group_id/, 'Saw the group');
+expect_like(qr/Name: EditedGroup$$/, 'Saw the modification');
+TODO: {
+ local $TODO = "Listing non-ticket items doesn't work";
+ expect_send("list -t group 'id > 0'", 'Listing the groups...');
+ expect_like(qr/$group_id: EditedGroup$$/, 'Found the group');
+}
+}
+
+# }}}
+
+TODO: {
+todo_skip "Custom field manipulation not yet implemented", 8;
+# {{{ test custom field manipulation
+
+# creating custom fields
+expect_send("create -t custom_field set Name='NewCF$$'", 'Creating a custom field...');
+expect_like(qr/Custom Field \d+ created/, 'Created the custom field');
+expect_handle->before() =~ /Custom Field (\d+) created/;
+my $cf_id = $1;
+ok($cf_id, "Got custom field id=$cf_id");
+# updating custom fields
+expect_send("edit cf/$cf_id set Name='EditedCF$$'", 'Editing the custom field');
+expect_like(qr/Custom field $cf_id updated/, 'Edited the custom field');
+expect_send("show cf/$cf_id", 'Showing the queue...');
+expect_like(qr/id: custom_field\/$cf_id/, 'Saw the custom field');
+expect_like(qr/Name: EditedCF$$/, 'Saw the modification');
+TODO: {
+ todo_skip "Listing non-ticket items doesn't work", 2;
+ expect_send("list -t custom_field 'id > 0'", 'Listing the CFs...');
+ expect_like(qr/$cf_id: EditedCF$$/, 'Found the custom field');
+}
+}
+
+# }}}
+
+# {{{ test merging tickets
+expect_send("create -t ticket set subject='CLIMergeTest1-$$'", 'Creating first ticket to merge...');
+expect_like(qr/Ticket \d+ created/, 'Created first ticket');
+expect_handle->before() =~ /Ticket (\d+) created/;
+my $merge_ticket_A = $1;
+ok($merge_ticket_A, "Got first ticket to merge id=$merge_ticket_A");
+expect_send("create -t ticket set subject='CLIMergeTest2-$$'", 'Creating second ticket to merge...');
+expect_like(qr/Ticket \d+ created/, 'Created second ticket');
+expect_handle->before() =~ /Ticket (\d+) created/;
+my $merge_ticket_B = $1;
+ok($merge_ticket_B, "Got second ticket to merge id=$merge_ticket_B");
+expect_send("merge $merge_ticket_B $merge_ticket_A", 'Merging the tickets...');
+expect_like(qr/Merge completed/, 'Merged the tickets');
+expect_send("show ticket/$merge_ticket_A/history", 'Checking merge on first ticket');
+expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in first ticket');
+expect_send("show ticket/$merge_ticket_B/history", 'Checking merge on second ticket');
+expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in second ticket');
+# }}}
+
+# {{{ test taking/stealing tickets
+{
+ # create a user; give them privileges to take and steal
+ ### TODO: implement 'grant' in the CLI tool; use that here instead.
+ ### this breaks the abstraction barrier, like, a lot.
+ my $steal_user = RT::User->new($RT::SystemUser);
+ my ($steal_user_id, $msg) = $steal_user->Create( Name => "fooser$$",
+ EmailAddress => "fooser$$\@localhost",
+ Privileged => 1,
+ Password => 'foobar',
+ );
+ ok($steal_user_id, "Created the user? $msg");
+ my $steal_queue = RT::Queue->new($RT::SystemUser);
+ my $steal_queue_id;
+ ($steal_queue_id, $msg) = $steal_queue->Create( Name => "Steal$$" );
+ ok($steal_queue_id, "Got the queue? $msg");
+ ok($steal_queue->id, "queue obj has id");
+ my $status;
+ ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'ShowTicket', Object => $steal_queue );
+ ok($status, "Gave 'SeeTicket' to our user? $msg");
+ ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'OwnTicket', Object => $steal_queue );
+ ok($status, "Gave 'OwnTicket' to our user? $msg");
+ ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'StealTicket', Object => $steal_queue );
+ ok($status, "Gave 'StealTicket' to our user? $msg");
+ ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'TakeTicket', Object => $steal_queue );
+ ok($status, "Gave 'TakeTicket' to our user? $msg");
+
+ # create a ticket to take/steal
+ expect_send("create -t ticket set queue=$steal_queue_id subject='CLIStealTest-$$'", 'Creating ticket to steal...');
+ expect_like(qr/Ticket \d+ created/, 'Created ticket');
+ expect_handle->before() =~ /Ticket (\d+) created/;
+ my $steal_ticket_id = $1;
+ ok($steal_ticket_id, "Got ticket to steal id=$steal_ticket_id");
+
+ # root takes the ticket
+ expect_send("take $steal_ticket_id", 'root takes the ticket...');
+ expect_like(qr/Owner changed from Nobody to root/, 'root took the ticket');
+
+ # log in as the non-root user
+ #expect_quit(); # this is apparently unnecessary, but I'll leave it in
+ # until I'm sure
+ $ENV{'RTUSER'} = "fooser$$";
+ $ENV{'RTPASSWD'} = 'foobar';
+ expect_run( command => "$rt_tool_path shell", prompt => 'rt> ', quit => 'quit',);
+
+ # user tries to take the ticket, fails
+ # shouldn't be able to 'take' a ticket which someone else has taken out from
+ # under you; that should produce an error. should have to explicitly
+ # 'steal' it back from them. 'steal' can automatically 'take' a ticket,
+ # though.
+ expect_send("take $steal_ticket_id", 'user tries to take the ticket...');
+ expect_like(qr/You can only take tickets that are unowned/, '...and fails.');
+ expect_send("show ticket/$steal_ticket_id -f owner", 'Double-checking...');
+ expect_like(qr/Owner: root/, '...no change.');
+
+ # user steals the ticket
+ expect_send("steal $steal_ticket_id", 'user tries to *steal* the ticket...');
+ expect_like(qr/Owner changed from root to fooser$$/, '...and succeeds!');
+ expect_send("show ticket/$steal_ticket_id -f owner", 'Double-checking...');
+ expect_like(qr/Owner: fooser$$/, '...yup, it worked.');
+
+ # log back in as root
+ #expect_quit(); # ditto
+ $ENV{'RTUSER'} = 'root';
+ $ENV{'RTPASSWD'} = 'password';
+ expect_run( command => "$rt_tool_path shell", prompt => 'rt> ', quit => 'quit',);
+
+ # root steals the ticket back
+ expect_send("steal $steal_ticket_id", 'root steals the ticket back...');
+ expect_like(qr/Owner changed from fooser$$ to root/, '...and succeeds.');
+}
+# }}}
+
+# {{{ test ticket linking
+ my @link_relns = ( 'DependsOn', 'DependedOnBy', 'RefersTo', 'ReferredToBy',
+ 'MemberOf', 'HasMember', );
+ my %display_relns = map { $_ => $_ } @link_relns;
+ $display_relns{HasMember} = 'Members';
+
+ my $link1_id = ok_create_ticket( "LinkTicket1-$$" );
+ my $link2_id = ok_create_ticket( "LinkTicket2-$$" );
+
+ foreach my $reln (@link_relns) {
+ # create link
+ expect_send("link $link1_id $reln $link2_id", "Link by $reln...");
+ expect_like(qr/Created link $link1_id $reln $link2_id/, 'Linked');
+ expect_send("show ticket/$link1_id/links", "Checking creation of $reln...");
+ expect_like(qr/$display_relns{reln}: [\w\d\.\-]+:\/\/[\w\d\.]+\/ticket\/$link2_id/, "Created link $reln");
+
+ # delete link
+ expect_send("link -d $link1_id $reln $link2_id", "Delete $reln...");
+ expect_like(qr/Deleted link $link1_id $reln $link2_id/, 'Deleted');
+ expect_send("show ticket/$link1_id/links", "Checking removal of $reln...");
+ ok( expect_handle->before() !~ /\Q$display_relns{$reln}: \E[\w\d\.\-]+:\/\/[w\d\.]+\/ticket\/$link2_id/, "Removed link $reln" );
+ #expect_unlike(qr/\Q$reln: \E[\w\d\.]+\Q://\E[w\d\.]+\/ticket\/$link2_id/, "Removed link $reln");
+
+ }
+# }}}
+
+
+# helper function
+sub ok_create_ticket {
+ my $subject = shift;
+
+ expect_send("create -t ticket set subject='$subject'", 'Creating ticket...');
+ expect_like(qr/Ticket \d+ created/, "Created ticket '$subject'");
+ expect_handle->before() =~ /Ticket (\d+) created/;
+ my $id = $1;
+ ok($id, "Got ticket id=$id");
+
+ return $id;
+}
+
+# wrap up all the file handling stuff for attachment testing
+sub check_attachment {
+ my $attachment_path = shift;
+ (my $filename = $attachment_path) =~ s/.*\/(.*?)$/$1/;
+ expect_send("comment -m 'attach file' -a $attachment_path $ticket_id", "Adding an attachment ($filename)");
+ expect_like(qr/Message recorded/, "Added the attachment");
+ expect_send("show ticket/$ticket_id/attachments","Finding Attachment");
+ my $attachment_regex = qr/(\d+):\s+$filename/;
+ expect_like($attachment_regex,"Attachment Uploaded");
+ expect_handle->before() =~ $attachment_regex;
+ my $attachment_id = $1;
+ expect_send("show ticket/$ticket_id/attachments/$attachment_id/content","Fetching Attachment");
+ open (my $fh, $attachment_path) or die "Can't open $attachment_path: $!";
+ my $attachment_content = do { local($/); <$fh> };
+ close $fh;
+ chomp $attachment_content;
+ expect_is($attachment_content,"Attachment contains original text");
+}
+
+1;
diff --git a/rt/lib/t/regression/27verp.t b/rt/lib/t/regression/27verp.t
new file mode 100644
index 0000000..856681b
--- /dev/null
+++ b/rt/lib/t/regression/27verp.t
@@ -0,0 +1,9 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More tests => 1;
+
+TODO: {
+ todo_skip "No tests written for VERP yet", 1;
+ ok(1,"a test to skip");
+}
diff --git a/rt/lib/t/regression/mime_tests b/rt/lib/t/regression/mime_tests
new file mode 100644
index 0000000..26e4dbf
--- /dev/null
+++ b/rt/lib/t/regression/mime_tests
@@ -0,0 +1,19 @@
+use RT::Ticket;
+use RT::Queue;
+
+use MIME::Parser;
+use File::Temp;
+use RT::EmailParser;
+
+open (HANDLE, "data/nested-mime-sample");
+my $parser = RT::EmailParser->new()
+ $parser->ParseMIMEEntityFromFileHandle(\*HANDLE);
+my $entity = $parser->Entity;
+
+my $q = RT::Queue->new($RT::SystemUser);
+$q->Load('general');
+ok ($q->Id, "Queue is loaded");
+my $Ticket = RT::Ticket->new($RT::SystemUser);
+my ($tid, $ttid, $msg) =$Ticket->Create( Queue => $q->Id, Subject => "Nested mime test", MIMEObj => $entity);
+ok ($tid, $msg);
+ok($Ticket->Id);
diff --git a/rt/lib/t/setup_regression.t b/rt/lib/t/setup_regression.t
new file mode 100644
index 0000000..36f809b
--- /dev/null
+++ b/rt/lib/t/setup_regression.t
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+use Test::More qw(no_plan);
+
+use RT;
+ok(RT::LoadConfig);
+ok(RT::Init, "Basic initialization and DB connectivity");
+
+# Create a new queue
+use_ok(RT::Queue);
+my $q = RT::Queue->new($RT::SystemUser);
+
+$q->Load('regression');
+if ($q->id != 0) {
+ die "Regression tests not starting with a clean DB. Bailing";
+}
+
+my ($id, $msg) = $q->Create( Name => 'Regression',
+ Description => 'A regression test queue',
+ CorrespondAddress => 'correspond@a',
+ CommentAddress => 'comment@a');
+
+isnt($id, 0, "Queue was created sucessfully - $msg");
+
+my $q2 = RT::Queue->new($RT::SystemUser);
+
+ok($q2->Load($id));
+is($q2->id, $id, "Sucessfully loaded the queue again");
+is($q2->Name, 'Regression');
+is($q2->Description, 'A regression test queue');
+is($q2->CorrespondAddress, 'correspond@a');
+is($q2->CommentAddress, 'comment@a');
+
+
diff --git a/rt/m4/rt_enable_layout.m4 b/rt/m4/rt_enable_layout.m4
new file mode 100644
index 0000000..cadec1c
--- /dev/null
+++ b/rt/m4/rt_enable_layout.m4
@@ -0,0 +1,36 @@
+dnl
+dnl @synopsis RT_ENABLE_LAYOUT()
+dnl
+dnl Enable a specific directory layout for the installation to use.
+dnl This configures a command-line parameter that can be specified
+dnl at ./configure invocation.
+dnl
+dnl The use of this feature in this way is a little hackish, but
+dnl better than a heap of options for every directory.
+dnl
+dnl This code is heavily borrowed *cough* from the Apache 2 code.
+dnl
+
+AC_DEFUN([RT_ENABLE_LAYOUT],[
+AC_ARG_ENABLE(layout,
+ AC_HELP_STRING([--enable-layout=LAYOUT],
+ [Use a specific directory layout (Default: RT3)]),
+ LAYOUT=$enableval)
+
+if test "x$LAYOUT" = "x"; then
+ LAYOUT="RT3"
+fi
+RT_LAYOUT($srcdir/config.layout, $LAYOUT)
+AC_MSG_CHECKING(for chosen layout)
+if test "x$rt_layout_name" = "xno"; then
+ if test "x$LAYOUT" = "xno"; then
+ AC_MSG_RESULT(none)
+ else
+ AC_MSG_RESULT($LAYOUT)
+ fi
+ AC_MSG_ERROR([a valid layout must be specified (or the default used)])
+else
+ AC_SUBST(rt_layout_name)
+ AC_MSG_RESULT($rt_layout_name)
+fi
+])
diff --git a/rt/m4/rt_expand_var.m4 b/rt/m4/rt_expand_var.m4
new file mode 100644
index 0000000..cec884a
--- /dev/null
+++ b/rt/m4/rt_expand_var.m4
@@ -0,0 +1,18 @@
+dnl
+dnl @synopsis RT_EXPAND_VAR(baz, $fraz)
+dnl
+dnl Iteratively expands the second parameter, until successive iterations
+dnl yield no change. The result is then assigned to the first parameter.
+dnl
+dnl This code is heavily borrowed from the Apache 2 codebase.
+dnl
+
+AC_DEFUN([RT_EXPAND_VAR],[
+ ap_last=''
+ ap_cur='$2'
+ while test "x${ap_cur}" != "x${ap_last}"; do
+ ap_last="${ap_cur}"
+ ap_cur=`eval "echo ${ap_cur}"`
+ done
+ $1="${ap_cur}"
+])
diff --git a/rt/m4/rt_layout.m4 b/rt/m4/rt_layout.m4
new file mode 100644
index 0000000..c92a108
--- /dev/null
+++ b/rt/m4/rt_layout.m4
@@ -0,0 +1,75 @@
+dnl
+dnl @synopsis RT_LAYOUT(configlayout, layoutname)
+dnl
+dnl This macro reads an Apache-style layout file (specified as the
+dnl configlayout parameter), and searches for a specific layout
+dnl (named using the layoutname parameter).
+dnl
+dnl The entries for a given layout are then inserted into the
+dnl environment such that they become available as substitution
+dnl variables. In addition, the rt_layout_name variable is set
+dnl (but not exported) if the layout is valid.
+dnl
+dnl This code is heavily borrowed *cough* from the Apache 2 codebase.
+dnl
+
+AC_DEFUN([RT_LAYOUT],[
+ if test ! -f $srcdir/config.layout; then
+ AC_MSG_WARN([Layout file $srcdir/config.layout not found])
+ rt_layout_name=no
+ else
+ pldconf=./config.pld
+ $PERL -0777 -p -e "\$layout = '$2';" -e '
+ s/.*<Layout\s+$layout>//gims;
+ s/<\/Layout>.*//s;
+ s/^#.*$//gm;
+ s/^\s+//gim;
+ s/\s+$/\n/gim;
+ s/\+$/\/rt3/gim;
+ # m4 will not let us just use $1, we need @S|@1
+# s/^((?:bin|sbin|libexec|data|sysconf|sharedstate|localstate|lib|include|oldinclude|info|man)dir)\s*:\s*(.*)$/@S|@1=@S|@2/gim;
+ # uh, should be [:=], but m4 apparently substitutes something...
+ s/^(.*?)\s*(?::|=)\s*(.*)$/\(test "x\@S|@@S|@1" = "xNONE" || test "x\@S|@@S|@1" = "x") && @S|@1=@S|@2/gim;
+ ' < $1 > $pldconf
+
+ if test -s $pldconf; then
+ rt_layout_name=$2
+ . $pldconf
+ changequote({,})
+ for var in prefix exec_prefix bindir sbindir \
+ sysconfdir mandir libdir datadir htmldir \
+ localstatedir logfiledir masonstatedir \
+ sessionstatedir customdir custometcdir customhtmldir \
+ customlexdir customlibdir manualdir; do
+ eval "val=\"\$$var\""
+ val=`echo $val | sed -e 's:\(.\)/*$:\1:'`
+ val=`echo $val |
+ sed -e 's:[\$]\([a-z_]*\):${\1}:g'`
+ eval "$var='$val'"
+ done
+ changequote([,])
+ else
+ rt_layout_name=no
+ fi
+ #rm $pldconf
+ fi
+ RT_SUBST_EXPANDED_ARG(prefix)
+ RT_SUBST_EXPANDED_ARG(exec_prefix)
+ RT_SUBST_EXPANDED_ARG(bindir)
+ RT_SUBST_EXPANDED_ARG(sbindir)
+ RT_SUBST_EXPANDED_ARG(sysconfdir)
+ RT_SUBST_EXPANDED_ARG(mandir)
+ RT_SUBST_EXPANDED_ARG(libdir)
+ RT_SUBST_EXPANDED_ARG(datadir)
+ RT_SUBST_EXPANDED_ARG(htmldir)
+ RT_SUBST_EXPANDED_ARG(manualdir)
+ RT_SUBST_EXPANDED_ARG(localstatedir)
+ RT_SUBST_EXPANDED_ARG(logfiledir)
+ RT_SUBST_EXPANDED_ARG(masonstatedir)
+ RT_SUBST_EXPANDED_ARG(sessionstatedir)
+ RT_SUBST_EXPANDED_ARG(customdir)
+ RT_SUBST_EXPANDED_ARG(custometcdir)
+ RT_SUBST_EXPANDED_ARG(customhtmldir)
+ RT_SUBST_EXPANDED_ARG(customlexdir)
+ RT_SUBST_EXPANDED_ARG(customlibdir)
+])dnl
diff --git a/rt/m4/rt_subst_expanded_arg.m4 b/rt/m4/rt_subst_expanded_arg.m4
new file mode 100644
index 0000000..02002b0
--- /dev/null
+++ b/rt/m4/rt_subst_expanded_arg.m4
@@ -0,0 +1,14 @@
+dnl
+dnl @synopsis RT_SUBST_EXPANDED_ARG(var)
+dnl
+dnl Export (via AC_SUBST) a given variable, along with an expanded
+dnl version of the variable (same name, but with exp_ prefix).
+dnl
+dnl This code is heavily borrowed *cough* from the Apache 2 source.
+dnl
+
+AC_DEFUN([RT_SUBST_EXPANDED_ARG],[
+ RT_EXPAND_VAR(exp_$1, [$]$1)
+ AC_SUBST($1)
+ AC_SUBST(exp_$1)
+])
diff --git a/rt/sbin/extract-message-catalog b/rt/sbin/extract-message-catalog
new file mode 100644
index 0000000..34d44ed
--- /dev/null
+++ b/rt/sbin/extract-message-catalog
@@ -0,0 +1,274 @@
+#!/usr/bin/perl -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+# Portions Copyright 2002 Autrijus Tang <autrijus@autrijus.org>
+
+use strict;
+
+use File::Find;
+use File::Copy;
+use Regexp::Common;
+use Carp;
+
+use vars qw($DEBUG $FILECAT);
+
+$DEBUG = 1;
+
+@ARGV = <lib/RT/I18N/*.po> unless @ARGV;
+
+$FILECAT = {};
+
+# extract all strings and stuff them into $FILECAT
+File::Find::find( { wanted => \&extract_strings_from_code, follow => 1 }, '.' );
+
+# ensure proper escaping and [_1] => %1 transformation
+foreach my $str ( sort keys %{$FILECAT} ) {
+ my $entry = $FILECAT->{$str};
+ my $oldstr = $str;
+
+ $str =~ s/\\/\\\\/g;
+ $str =~ s/\"/\\"/g;
+ $str =~ s/((?<!~)(?:~~)*)\[_(\d+)\]/$1%$2/g;
+ $str =~ s/((?<!~)(?:~~)*)\[([A-Za-z#*]\w*),([^\]]+)\]/"$1%$2(".escape($3).")"/eg;
+ $str =~ s/~([\[\]])/$1/g;
+
+ delete $FILECAT->{$oldstr};
+ $FILECAT->{$str} = $entry;
+}
+
+# update all language dictionaries
+foreach my $dict (@ARGV) {
+ $dict = "lib/RT/I18N/$dict.po" unless -f $dict or $dict =~ m!/!;
+
+ my $lang = $dict;
+ $lang =~ s|.*/||;
+ $lang =~ s|\.po$||;
+
+ update($lang, $dict);
+}
+
+
+# {{{ pull strings out of the code.
+
+sub extract_strings_from_code {
+ my $file = $_;
+
+ local $/;
+ return if ( -d $_ );
+ return if ( $File::Find::dir =~ 'lib/blib|lib/t/autogen|var|m4|local|\.svn' );
+ return if ( /\.po$|\.bak$|~|,D|,B$|extract-message-catalog$/ );
+ return if ( /^[\.#]/ );
+ return if ( -f "$_.in" );
+
+ print "Looking at $File::Find::name\n";
+ my $filename = $File::Find::name;
+ $filename =~ s'^\./'';
+ $filename =~ s'\.in$'';
+
+ unless (open _, $file) {
+ print "Cannot open $file for reading ($!), skipping.\n";
+ return;
+ }
+
+ $_ = <_>;
+
+ # Mason filter: <&|/l>...</&>
+ my $line = 1;
+ while (m!\G.*?<&\|/l(.*?)&>(.*?)</&>!sg) {
+ my ( $vars, $str ) = ( $1, $2 );
+ $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
+ $str =~ s/\\'/\'/g;
+ #print "STR IS $str\n";
+ push @{ $FILECAT->{$str} }, [ $filename, $line, $vars ];
+ }
+
+ # Localization function: loc(...)
+ $line = 1;
+ pos($_) = 0;
+ while (m/\G.*?\bloc$RE{balanced}{-parens=>'()'}{-keep}/sg) {
+ my $match = $1;
+ $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
+
+ my ( $vars, $str );
+ if ( $match =~
+ /\(\s*($RE{delimited}{-delim=>q{'"}}{-keep})(.*?)\s*\)$/ ) {
+
+ $str = substr( $1, 1, -1 ); # $str comes before $vars now
+ $vars = $9;
+ }
+ else {
+ next;
+ }
+
+ $vars =~ s/[\n\r]//g;
+ $str =~ s/\\'/\'/g;
+
+ push @{ $FILECAT->{$str} }, [ $filename, $line, $vars ];
+ }
+
+ # Comment-based mark: "..." # loc
+ $line = 1;
+ pos($_) = 0;
+ while (m/\G.*?($RE{delimited}{-delim=>q{'"}}{-keep})[\}\)\],]*\s*\#\s*loc\s*$/smg) {
+ my $str = substr($1, 1, -1);
+ $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
+ $str =~ s/\\'/\'/g;
+ push @{ $FILECAT->{$str} }, [ $filename, $line, '' ];
+ }
+
+ # Comment-based pair mark: "..." => "..." # loc_pair
+ $line = 1;
+ pos($_) = 0;
+ while (m/\G.*?(\w+)\s*=>\s*($RE{delimited}{-delim=>q{'"}}{-keep})[\}\)\],]*\s*\#\s*loc_pair\s*$/smg) {
+ my $key = $1;
+ my $val = substr($2, 1, -1);
+ $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
+ $key =~ s/\\'/\'/g;
+ $val =~ s/\\'/\'/g;
+ push @{ $FILECAT->{$key} }, [ $filename, $line, '' ];
+ push @{ $FILECAT->{$val} }, [ $filename, $line, '' ];
+ }
+
+ close (_);
+}
+# }}} extract from strings
+
+sub update {
+ my $lang = shift;
+ my $file = shift;
+ my ( %Lexicon, %Header);
+ my $out = '';
+
+ unless (!-e $file or -w $file) {
+ warn "Can't write to $lang, skipping...\n";
+ return;
+ }
+
+ print "Updating $lang...\n";
+
+ my @lines;
+ @lines = (<LEXICON>) if open (LEXICON, $file);
+ @lines = grep { !/^(#(:|\.)\s*|$)/ } @lines;
+ while (@lines) {
+ my $msghdr = "";
+ $msghdr .= shift @lines while ( $lines[0] && $lines[0] !~ /^msgid/ );
+
+ my $msgid = shift @lines;
+ my $msgstr = "";
+ $msgstr .= shift @lines while ( $lines[0] && $lines[0] =~ /^(msgstr|")/ );
+
+ last unless $msgid;
+
+ chomp $msgid;
+ chomp $msgstr;
+ $msgid =~ s/^msgid "(.*)"\s*?$/$1/ms or warn "$msgid in $file";
+ $msgstr =~ s/^msgstr "(.*)"\s*?$/$1/ms or warn "$msgstr in $file";
+
+ $Lexicon{$msgid} = $msgstr;
+ $Header{$msgid} = $msghdr;
+ }
+
+ my $is_english = ( $lang =~ /^en(?:[^A-Za-z]|$)/ );
+
+ foreach my $str ( sort keys %{$FILECAT} ) {
+ $Lexicon{$str} ||= '';;
+ }
+ foreach ( sort keys %Lexicon ) {
+ my $f = join ( ' ', sort map $_->[0].":".$_->[1], @{ $FILECAT->{$_} } );
+ my $nospace = $_;
+ $nospace =~ s/ +$//;
+
+ if ( !$Lexicon{$_} and $Lexicon{$nospace} ) {
+ $Lexicon{$_} =
+ $Lexicon{$nospace} . ( ' ' x ( length($_) - length($nospace) ) );
+ }
+
+ next if !length( $Lexicon{$_} ) and $is_english;
+
+ my %seen;
+ $out .= $Header{$_} if exists $Header{$_};
+
+
+
+ next if (!$f && $_ && !$Lexicon{$_});
+ if ( $f && $f !~ /^\s+$/ ) {
+
+ $out .= "#: $f\n";
+ }
+ elsif ($_) {
+ $out .= "#: NOT FOUND IN SOURCE\n";
+ }
+ foreach my $entry ( grep { $_->[2] } @{ $FILECAT->{$_} } ) {
+ my ( $file, $line, $var ) = @{$entry};
+ $var =~ s/^\s*,\s*//;
+ $var =~ s/\s*$//;
+ $out .= "#. ($var)\n" unless $seen{$var}++;
+ }
+ $out .= "msgid \"$_\"\nmsgstr \"$Lexicon{$_}\"\n\n";
+ }
+
+ open PO, ">$file" or die $!;
+ print PO $out;
+ close PO;
+
+ return 1;
+}
+
+sub escape {
+ my $text = shift;
+ $text =~ s/\b_(\d+)/%$1/;
+ return $text;
+}
+
+__END__
+# Local variables:
+# c-indentation-style: bsd
+# c-basic-offset: 4
+# indent-tabs-mode: nil
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/rt/sbin/extract_pod_tests b/rt/sbin/extract_pod_tests
new file mode 100644
index 0000000..616560b
--- /dev/null
+++ b/rt/sbin/extract_pod_tests
@@ -0,0 +1,159 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.06';
+
+use Pod::Tests;
+use Symbol;
+
+=pod
+
+=head1 NAME
+
+extract_pod_tests - RT-specific variant of pod2tests
+
+=head1 SYNOPSIS
+
+ pod2test [-Mmodule] [input [output]]
+
+=head1 DESCRIPTION
+
+B<pod2test> is a front-end for Test::Inline. It generates the
+"Bodies" of MakeMaker style .t testing files from embedded tests and
+code examples.
+
+If output is not specified, the resulting .t file will go to STDOUT.
+Otherwise, it will go to the given output file. If input is not
+given, it will draw from STDIN.
+
+If the given file contains no tests or code examples, no output will
+be given and no output file will be created.
+
+=cut
+
+my($infile, $outfile) = @ARGV;
+my($infh,$outfh);
+
+
+if( defined $infile ) {
+ $infh = gensym;
+ open($infh, $infile) or
+ die "Can't open the POD file $infile: $!";
+}
+else {
+ $infh = \*STDIN;
+}
+
+unless ($outfile) {
+ ( my $test = $infile ) =~ s/\.(pm|pod)$//;
+ $test =~ s/^lib\W//;
+ $test =~ s/\W/-/;
+ $test =~ s/\//__/g;
+
+ $outfile = "lib/t/autogen/autogen-$test.t";
+}
+
+
+my $p = Pod::Tests->new;
+$p->parse_fh($infh);
+
+# XXX Hack to put the filename into the #line directive
+$p->{file} = $infile || '';
+
+my @tests = $p->build_tests($p->tests);
+my @examples = $p->build_examples($p->examples);
+
+exit unless @tests or @examples;
+
+
+if( defined $outfile) {
+ $outfh = gensym;
+ open($outfh, ">$outfile") or
+ die "Can't open the test file $outfile: $!";
+}
+else {
+ $outfh = \*STDOUT;
+}
+
+
+print $outfh <<EOF;
+
+use Test::More qw/no_plan/;
+use RT;
+RT::LoadConfig();
+RT::Init();
+
+EOF
+foreach my $test (@tests, @examples) {
+ print $outfh "$test\n";
+}
+
+print $outfh "1;\n";
+
+=pod
+
+=head1 BUGS and CAVEATS
+
+This is a very simple rough cut. It only does very rudimentary tests
+on the examples.
+
+=head1 AUTHOR
+
+
+
+Based on pod2tests by Michael G Schwern <schwern@pobox.com>
+
+=head1 SEE ALSO
+
+L<Test::Inline>
+
+=cut
+
+1;
diff --git a/rt/sbin/factory b/rt/sbin/factory
new file mode 100644
index 0000000..f72a296
--- /dev/null
+++ b/rt/sbin/factory
@@ -0,0 +1,515 @@
+#!/usr/bin/perl -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use DBI;
+
+my $database = shift;
+my $namespace = shift;
+
+my $CollectionBaseclass = 'RT::SearchBuilder';
+my $RecordBaseclass = 'RT::Record';
+
+my $driver = 'mysql';
+my $hostname = 'localhost';
+my $user = 'root';
+my $password = '';
+
+
+my $LicenseBlock = << '.';
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+.
+
+my $Attribution = << '.';
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+use strict;
+.
+
+my $dsn = "DBI:$driver:database=$database;host=$hostname";
+
+my $dbh = DBI->connect( $dsn, $user, $password );
+
+#get all tables out of database
+my @tables = $dbh->tables();
+
+my ( %tablemap, $typemap, %modulemap );
+
+foreach my $table (@tables) {
+ $table =~ s/\`//g;
+ next if ($table eq 'sessions');
+ $table = ucfirst($table);
+ $table =~ s/field/Field/;
+ $table =~ s/group/Group/;
+ $table =~ s/custom/Custom/;
+ $table =~ s/member/Member/;
+ $table =~ s/Scripaction/ScripAction/g;
+ $table =~ s/condition/Condition/g;
+ $table =~ s/value/Value/;
+ $table =~ s/Acl/ACL/g;
+ $tablemap{$table} = $table;
+ $modulemap{$table} = $table;
+ if ( $table =~ /^(.*)s$/ ) {
+ $tablemap{$1} = $table;
+ $modulemap{$1} = $1;
+ }
+}
+$tablemap{'CreatedBy'} = 'User';
+$tablemap{'UpdatedBy'} = 'User';
+
+my %typemap;
+$typemap{'id'} = 'ro';
+$typemap{'Creator'} = 'auto';
+$typemap{'Created'} = 'auto';
+$typemap{'Updated'} = 'auto';
+$typemap{'UpdatedBy'} = 'auto';
+$typemap{'LastUpdated'} = 'auto';
+$typemap{'LastUpdatedBy'} = 'auto';
+
+foreach my $table (@tables) {
+ next if ($table eq 'sessions');
+ my $tablesingle = $table;
+ $tablesingle =~ s/s$//;
+ my $tableplural = $tablesingle . "s";
+
+ if ( $tablesingle eq 'ACL' ) {
+ $tablesingle = "ACE";
+ $tableplural = "ACL";
+ }
+
+ my %requirements;
+
+ my $CollectionClassName = $namespace . "::" . $tableplural;
+ my $RecordClassName = $namespace . "::" . $tablesingle;
+
+ my $path = $namespace;
+ $path =~ s/::/\//g;
+
+ my $RecordClassPath = $path . "/" . $tablesingle . ".pm";
+ my $CollectionClassPath = $path . "/" . $tableplural . ".pm";
+
+ #create a collection class
+ my $CreateInParams;
+ my $CreateOutParams;
+ my $ClassAccessible = "";
+ my $FieldsPod = "";
+ my $CreatePod = "";
+ my $RecordInit = "";
+ my %fields;
+
+
+ my $introspection = $dbh->prepare("SELECT * from $table where id is null");
+ $introspection->execute();
+ my @names =@{ $introspection->{'NAME'}};
+ my @types = @{$introspection->{'TYPE'}};
+ my @is_blob = @{$introspection->{'mysql_is_blob'}};
+ my @is_num = @{$introspection->{'mysql_is_num'}};
+
+ my %blobness = ();
+ my %sqltypes = ();
+ my %numeric = ();
+ foreach my $name (@names) {
+ $sqltypes{$name} = shift @types;
+ $blobness{$name} = (shift @is_blob || "0");
+ $numeric{$name} = (shift @is_num || "0");
+ }
+
+
+ my $sth = $dbh->prepare("DESCRIBE $table");
+ $sth->execute;
+
+ while ( my $row = $sth->fetchrow_hashref() ) {
+ my $field = $row->{'Field'};
+ my $type = $row->{'Type'};
+ my $default = $row->{'Default'};
+ my $length = 0;
+ if ($type =~ /^(?:.*?)\((\d+)\)$/) {
+ $length = $1;
+ }
+ $fields{$field} = 1;
+
+ #generate the 'accessible' datastructure
+
+ no warnings 'uninitialized';
+
+ if ( $typemap{$field} eq 'auto' ) {
+ $ClassAccessible .= " $field =>
+ {read => 1, auto => 1,";
+ }
+ elsif ( $typemap{$field} eq 'ro' ) {
+ $ClassAccessible .= " $field =>
+ {read => 1,";
+ }
+ else {
+ $ClassAccessible .= " $field =>
+ {read => 1, write => 1,";
+
+ }
+ $ClassAccessible .= " sql_type => $sqltypes{$field}, length => $length, is_blob => $blobness{$field}, is_numeric => $numeric{$field}, ";
+ $ClassAccessible .= " type => '$type', default => '$default'},\n";
+
+ #generate pod for the accessible fields
+ $FieldsPod .= "
+=head2 $field
+
+Returns the current value of $field.
+(In the database, $field is stored as $type.)
+
+";
+
+ unless ( exists $typemap{$field} && ( $typemap{$field} eq 'auto' || $typemap{$field} eq 'ro' )) {
+ $FieldsPod .= "
+
+=head2 Set$field VALUE
+
+
+Set $field to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, $field will be stored as a $type.)
+
+";
+ }
+
+ $FieldsPod .= "
+=cut
+
+";
+
+ if ( $modulemap{$field} ) {
+ $FieldsPod .= "
+=head2 ${field}Obj
+
+Returns the $modulemap{$field} Object which has the id returned by $field
+
+
+=cut
+
+sub ${field}Obj {
+ my \$self = shift;
+ my \$$field = ${namespace}::$modulemap{$field}->new(\$self->CurrentUser);
+ \$$field->Load(\$self->__Value('$field'));
+ return(\$$field);
+}
+";
+ $requirements{ $tablemap{$field} } =
+ "use ${namespace}::$modulemap{$field};";
+
+ }
+
+ unless ( $typemap{$field} eq 'auto' || $field eq 'id' ) {
+
+ #generate create statement
+ $CreateInParams .= " $field => '$default',\n";
+ $CreateOutParams .=
+ " $field => \$args{'$field'},\n";
+
+ #gerenate pod for the create statement
+ $CreatePod .= " $type '$field'";
+ $CreatePod .= " defaults to '$default'" if ($default);
+ $CreatePod .= ".\n";
+
+ }
+
+ }
+
+ my $Create = "
+sub Create {
+ my \$self = shift;
+ my \%args = (
+$CreateInParams
+ \@_);
+ \$self->SUPER::Create(
+$CreateOutParams);
+
+}
+";
+ $CreatePod .= "\n=cut\n\n";
+
+ my $CollectionClass = $LicenseBlock . $Attribution .
+
+ "
+
+=head1 NAME
+
+ $CollectionClassName -- Class Description
+
+=head1 SYNOPSIS
+
+ use $CollectionClassName
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package $CollectionClassName;
+
+use $CollectionBaseclass;
+use $RecordClassName;
+
+use vars qw( \@ISA );
+\@ISA= qw($CollectionBaseclass);
+
+
+sub _Init {
+ my \$self = shift;
+ \$self->{'table'} = '$table';
+ \$self->{'primary_key'} = 'id';
+
+";
+
+ if ( $fields{'SortOrder'} ) {
+
+ $CollectionClass .= "
+
+ # By default, order by SortOrder
+ \$self->OrderByCols(
+ { ALIAS => 'main',
+ FIELD => 'SortOrder',
+ ORDER => 'ASC' },
+ { ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'ASC' },
+ );
+";
+ }
+ $CollectionClass .= "
+ return ( \$self->SUPER::_Init(\@_) );
+}
+
+
+=head2 NewItem
+
+Returns an empty new $RecordClassName item
+
+=cut
+
+sub NewItem {
+ my \$self = shift;
+ return($RecordClassName->new(\$self->CurrentUser));
+}
+" . MagicImport($CollectionClassName);
+
+ my $RecordClassHeader = $Attribution . "
+
+=head1 NAME
+
+$RecordClassName
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package $RecordClassName;
+use $RecordBaseclass;
+";
+
+ foreach my $key ( keys %requirements ) {
+ $RecordClassHeader .= $requirements{$key} . "\n";
+ }
+ $RecordClassHeader .= "
+
+use vars qw( \@ISA );
+\@ISA= qw( $RecordBaseclass );
+
+sub _Init {
+ my \$self = shift;
+
+ \$self->Table('$table');
+ \$self->SUPER::_Init(\@_);
+}
+
+";
+
+ my $RecordClass = $LicenseBlock . $RecordClassHeader . "
+
+$RecordInit
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+$CreatePod
+
+$Create
+
+$FieldsPod
+
+sub _CoreAccessible {
+ {
+
+$ClassAccessible
+ }
+};
+
+" . MagicImport($RecordClassName);
+
+ print "About to make $RecordClassPath, $CollectionClassPath\n";
+ `mkdir -p $path`;
+
+ open( RECORD, ">$RecordClassPath" );
+ print RECORD $RecordClass;
+ close(RECORD);
+
+ open( COL, ">$CollectionClassPath" );
+ print COL $CollectionClass;
+ close(COL);
+
+}
+
+sub MagicImport {
+ my $class = shift;
+
+ #if (exists \$warnings::{unimport}) {
+ # no warnings qw(redefine);
+
+ my $path = $class;
+ $path =~ s#::#/#gi;
+
+
+ my $content = "
+ eval \"require @{[$class]}_Overlay\";
+ if (\$@ && \$@ !~ qr{^Can't locate ".$path."_Overlay.pm}) {
+ die \$@;
+ };
+
+ eval \"require @{[$class]}_Vendor\";
+ if (\$@ && \$@ !~ qr{^Can't locate ".$path."_Vendor.pm}) {
+ die \$@;
+ };
+
+ eval \"require @{[$class]}_Local\";
+ if (\$@ && \$@ !~ qr{^Can't locate ".$path."_Local.pm}) {
+ die \$@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows \"overlay\" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+Each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+@{[$class]}_Overlay, @{[$class]}_Vendor, @{[$class]}_Local
+
+=cut
+
+
+1;
+";
+
+ return $content;
+}
+
+# }}}
+
diff --git a/rt/sbin/license_tag b/rt/sbin/license_tag
new file mode 100644
index 0000000..ed1d4eb
--- /dev/null
+++ b/rt/sbin/license_tag
@@ -0,0 +1,243 @@
+#!/usr/bin/perl
+
+
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+my $LICENSE = <<'EOL';
+
+COPYRIGHT:
+
+This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+ <jesse@bestpractical.com>
+
+(Except where explicitly superseded by other copyright notices)
+
+
+LICENSE:
+
+This work is made available to you under the terms of Version 2 of
+the GNU General Public License. A copy of that license should have
+been provided with this software, but in any event can be snarfed
+from www.gnu.org.
+
+This work is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 or visit their web page on the internet at
+http://www.gnu.org/copyleft/gpl.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.
+
+EOL
+
+use File::Find;
+
+my @MAKE = qw(Makefile);
+
+File::Find::find({ no_chdir => 1, wanted => \&tag_pm}, 'lib');
+File::Find::find({ no_chdir => 1, wanted => \&tag_mason}, 'html');
+File::Find::find({ no_chdir => 1, wanted => \&tag_script}, 'sbin');
+File::Find::find({ no_chdir => 1, wanted => \&tag_script}, 'bin');
+tag_makefile ('Makefile.in');
+tag_makefile ('README');
+
+
+sub tag_mason {
+ my $pm = $_;
+ next unless (-f $pm);
+ next if ($pm =~ /images/);
+ open(FILE,"<$pm") || die "Failed to open $pm";
+ my $file = (join "", <FILE>);
+ close (FILE);
+ my $pmlic = $LICENSE;
+ $pmlic =~ s/^/%# /mg;
+
+
+ print "$pm - ";
+ if ($file =~ /^%# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ print "has license section";
+ $file =~ s/^%# BEGIN BPS TAGGED BLOCK {{{(.*?)%# END BPS TAGGED BLOCK }}}/%# BEGIN BPS TAGGED BLOCK {{{\n$pmlic%# END BPS TAGGED BLOCK }}}/ms;
+
+
+ } else {
+ print "no license section";
+ $file ="%# BEGIN BPS TAGGED BLOCK {{{\n$pmlic%# END BPS TAGGED BLOCK }}}\n". $file;
+ }
+ $file =~ s/%# END BPS TAGGED BLOCK }}}(\n+)/%# END BPS TAGGED BLOCK }}}\n/mg;
+ print "\n";
+
+
+
+
+ open (FILE, ">$pm") || die "couldn't write new file";
+ print FILE $file;
+ close FILE;
+
+}
+
+
+sub tag_makefile {
+ my $pm = shift;
+ open(FILE,"<$pm") || die "Failed to open $pm";
+ my $file = (join "", <FILE>);
+ close (FILE);
+ my $pmlic = $LICENSE;
+ $pmlic =~ s/^/# /mg;
+
+
+ print "$pm - ";
+ if ($file =~ /^# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ print "has license section";
+ $file =~ s/^# BEGIN BPS TAGGED BLOCK {{{(.*?)# END BPS TAGGED BLOCK }}}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic# END BPS TAGGED BLOCK }}}/ms;
+
+
+ } else {
+ print "no license section";
+ $file ="# BEGIN BPS TAGGED BLOCK {{{\n$pmlic# END BPS TAGGED BLOCK }}}\n". $file;
+ }
+ $file =~ s/# END BPS TAGGED BLOCK }}}(\n+)/# END BPS TAGGED BLOCK }}}\n/mg;
+ print "\n";
+
+
+
+
+ open (FILE, ">$pm") || die "couldn't write new file";
+ print FILE $file;
+ close FILE;
+
+}
+
+
+sub tag_pm {
+ my $pm = $_;
+ next unless $pm =~ /\.pm/s;
+ open(FILE,"<$pm") || die "Failed to open $pm";
+ my $file = (join "", <FILE>);
+ close (FILE);
+ my $pmlic = $LICENSE;
+ $pmlic =~ s/^/# /mg;
+
+
+ print "$pm - ";
+ if ($file =~ /^# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ print "has license section";
+ $file =~ s/^# BEGIN BPS TAGGED BLOCK {{{(.*?)# END BPS TAGGED BLOCK }}}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic# END BPS TAGGED BLOCK }}}/ms;
+
+
+ } else {
+ print "no license section";
+ $file ="# BEGIN BPS TAGGED BLOCK {{{\n$pmlic# END BPS TAGGED BLOCK }}}\n". $file;
+ }
+ $file =~ s/# END BPS TAGGED BLOCK }}}(\n+)/# END BPS TAGGED BLOCK }}}\n/mg;
+ print "\n";
+
+
+
+
+ open (FILE, ">$pm") || die "couldn't write new file $pm";
+ print FILE $file;
+ close FILE;
+
+}
+
+
+sub tag_script {
+ my $pm = $_;
+ return unless (-f $pm);
+ open(FILE,"<$pm") || die "Failed to open $pm";
+ my $file = (join "", <FILE>);
+ close (FILE);
+ my $pmlic = $LICENSE;
+ $pmlic =~ s/^/# /msg;
+
+ print "$pm - ";
+ if ($file =~ /^# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ print "has license section";
+ $file =~ s/^# BEGIN BPS TAGGED BLOCK {{{(.*?)# END BPS TAGGED BLOCK }}}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic# END BPS TAGGED BLOCK }}}/ms;
+
+
+ } else {
+ print "no license section";
+ if ($file =~ /^(#!.*?)\n/) {
+
+ my $lic ="# BEGIN BPS TAGGED BLOCK {{{\n$pmlic# END BPS TAGGED BLOCK }}}\n";
+ $file =~ s/^(#!.*?)\n/$1\n$lic/;
+
+ }
+ }
+ $file =~ s/# END BPS TAGGED BLOCK }}}(\n+)/# END BPS TAGGED BLOCK }}}\n/mg;
+ print "\n";
+
+
+ open (FILE, ">$pm") || die "couldn't write new file";
+ print FILE $file;
+ close FILE;
+
+}
+
diff --git a/rt/sbin/regression_harness b/rt/sbin/regression_harness
new file mode 100644
index 0000000..1e97a29
--- /dev/null
+++ b/rt/sbin/regression_harness
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.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 }}}
+open (FH,"make regression|");
+
+my $skip_frontmatter = 1;
+while (<FH>) {
+ next if /^ok/;
+ $skip_frontmatter = 0 if (/autogen/);
+ print $_ unless ($skip_frontmatter);
+}
diff --git a/rt/sbin/rt-dump-database.in b/rt/sbin/rt-dump-database.in
new file mode 100755
index 0000000..734e00b
--- /dev/null
+++ b/rt/sbin/rt-dump-database.in
@@ -0,0 +1,173 @@
+#!@PERL@ -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+
+use lib "@LOCAL_LIB_PATH@";
+use lib "@RT_LIB_PATH@";
+
+use RT;
+use XML::Simple;
+
+RT::LoadConfig();
+RT::Init();
+
+my $LocalOnly = @ARGV ? shift(@ARGV) : 1;
+
+my %RV;
+my %Ignore = (
+ All => [qw(
+ id Created Creator LastUpdated LastUpdatedBy
+ )],
+ Templates => [qw(
+ TranslationOf
+ )],
+);
+
+my $SystemUserId = $RT::SystemUser->Id;
+my @classes = qw(
+ Users Groups Queues ScripActions ScripConditions
+ Templates Scrips ACL CustomFields
+);
+foreach my $class (@classes) {
+ require "RT/$class.pm";
+ my $objects = "RT::$class"->new($RT::SystemUser);
+ $objects->{find_disabled_rows} = 1;
+ $objects->UnLimit;
+
+ if ($class eq 'CustomFields') {
+ $objects->OrderByCols(
+ { FIELD => 'LookupType' },
+ { FIELD => 'SortOrder' },
+ { FIELD => 'Id' },
+ );
+ }
+ else {
+ $objects->OrderBy( FIELD => 'Id' );
+ }
+
+ if ($LocalOnly) {
+ next if $class eq 'ACL'; # XXX - would go into infinite loop - XXX
+ $objects->Limit( FIELD => 'LastUpdatedBy', OPERATOR => '!=', VALUE => $SystemUserId )
+ unless $class eq 'Groups';
+ $objects->Limit( FIELD => 'Id', OPERATOR => '!=', VALUE => $SystemUserId )
+ if $class eq 'Users';
+ $objects->Limit( FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined' )
+ if $class eq 'Groups';
+ }
+
+ my %fields;
+ while (my $obj = $objects->Next) {
+ next if $obj->can('LastUpdatedBy') and $obj->LastUpdatedBy == $SystemUserId;
+
+ if (!%fields) {
+ %fields = map { $_ => 1 } keys %{$obj->_ClassAccessible};
+ delete @fields{
+ @{$Ignore{$class}||=[]},
+ @{$Ignore{All}||=[]},
+ };
+ }
+
+ my $rv;
+ # next if $obj-> # skip default names
+ foreach my $field (sort keys %fields) {
+ my $value = $obj->__Value($field);
+ $rv->{$field} = $value if length($value);
+ }
+ delete $rv->{Disabled} unless $rv->{Disabled};
+
+ foreach my $record (map { /ACL/ ? 'ACE' : substr($_, 0, -1) } @classes) {
+ foreach my $key (map "$record$_", ('', 'Id')) {
+ next unless exists $rv->{$key};
+ my $id = $rv->{$key} or next;
+ my $obj = "RT::$record"->new($RT::SystemUser);
+ $obj->LoadByCols( Id => $id ) or next;
+ $rv->{$key} = $obj->__Value('Name') || 0;
+ }
+ }
+
+ if ($class eq 'Users' and defined $obj->Privileged) {
+ $rv->{Privileged} = int($obj->Privileged);
+ }
+ elsif ($class eq 'CustomFields') {
+ my $values = $obj->Values;
+ while (my $value = $values->Next) {
+ push @{$rv->{Values}}, {
+ map { ($_ => $value->__Value($_)) } qw(
+ Name Description SortOrder
+ ),
+ };
+ }
+ }
+
+ if (eval { require RT::Attributes; 1 }) {
+ my $attributes = $obj->Attributes;
+ while (my $attribute = $attributes->Next) {
+ my $content = $attribute->Content;
+ $rv->{Attributes}{$attribute->Name} = $content if length($content);
+ }
+ }
+
+ push @{$RV{$class}}, $rv;
+ }
+}
+
+print(<< ".");
+no strict; use XML::Simple; *_ = XMLin(do { local \$/; readline(DATA) }, ForceArray => [qw(
+ @classes Values
+)], NoAttr => 1, SuppressEmpty => ''); *\$_ = (\$_{\$_} || []) for keys \%_; 1; # vim: ft=xml
+__DATA__
+.
+
+print XMLout(
+ { map { ($_ => ($RV{$_} || [])) } @classes },
+ RootName => 'InitialData',
+ NoAttr => 1,
+ SuppressEmpty => '',
+ XMLDecl => '<?xml version="1.0" encoding="UTF-8"?>',
+);
diff --git a/rt/sbin/rt-setup-database.in b/rt/sbin/rt-setup-database.in
new file mode 100644
index 0000000..cf607e2
--- /dev/null
+++ b/rt/sbin/rt-setup-database.in
@@ -0,0 +1,712 @@
+#!@PERL@ -w
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use vars qw($PROMPT $VERSION $Handle $Nobody $SystemUser $item);
+use vars
+ qw(@Groups @Users @ACL @Queues @ScripActions @ScripConditions @Templates @CustomFields @Scrips @Attributes);
+
+use lib "@LOCAL_LIB_PATH@";
+use lib "@RT_LIB_PATH@";
+
+#This drags in RT's config.pm
+# We do it in a begin block because RT::Handle needs to know the type to do its
+# inheritance
+use RT;
+use Carp;
+use RT::User;
+use RT::CurrentUser;
+use RT::Template;
+use RT::ScripAction;
+use RT::ACE;
+use RT::Group;
+use RT::User;
+use RT::Queue;
+use RT::ScripCondition;
+use RT::CustomField;
+use RT::Scrip;
+
+RT::LoadConfig();
+use Term::ReadKey;
+use Getopt::Long;
+
+my %args;
+
+GetOptions(
+ \%args,
+ 'prompt-for-dba-password', 'force', 'debug',
+ 'action=s', 'dba=s', 'dba-password=s', 'datafile=s',
+ 'datadir=s'
+);
+
+unless ( $args{'action'} ) {
+ help();
+ exit(-1);
+}
+
+$| = 1; #unbuffer that output.
+
+require RT::Handle;
+my $Handle = RT::Handle->new($RT::DatabaseType);
+$Handle->BuildDSN;
+my $dbh;
+
+if ( $args{'prompt-for-dba-password'} ) {
+ $args{'dba-password'} = get_dba_password();
+ chomp( $args{'dba-password'} );
+}
+
+if ( $args{'action'} eq 'init' ) {
+ $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} )
+ || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
+ print "Now creating a database for RT.\n";
+ if ( $RT::DatabaseType ne 'Oracle' || $args{'dba'} ne $RT::DatabaseUser ) {
+ create_db();
+ } else {
+ print "...skipped as ".$args{'dba'} ." is not " . $RT::DatabaseUser . " or we're working with Oracle.\n";
+ }
+
+ if ( $RT::DatabaseType eq "mysql" ) {
+ # Check which version we're running
+ my ($version) = $dbh->selectrow_hashref("show variables like 'version'")->{Value} =~ /^(\d\.\d+)/;
+ print "*** Warning: RT is unsupported on MySQL versions before 4.0.x\n" if $version < 4;
+
+ # MySQL must have InnoDB support
+ my $innodb = $dbh->selectrow_hashref("show variables like 'have_innodb'")->{Value};
+ if ( $innodb eq "NO" ) {
+ die "RT requires that MySQL be compiled with InnoDB table support.\n".
+ "See http://dev.mysql.com/doc/mysql/en/InnoDB.html\n";
+ } elsif ( $innodb eq "DISABLED" ) {
+ die "RT requires that MySQL InnoDB table support be enabled.\n".
+ ($version < 4
+ ? "Add 'innodb_data_file_path=ibdata1:10M:autoextend' to the [mysqld] section of my.cnf\n"
+ : "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
+ }
+ }
+
+ # SQLite can't deal with the disconnect/reconnect
+ unless ( $RT::DatabaseType eq 'SQLite' ) {
+
+ $dbh->disconnect;
+
+ if ( $RT::DatabaseType eq "Oracle" ) {
+ $RT::DatabasePassword = $RT::DatabasePassword; #Warning avidance
+ $dbh = DBI->connect( $Handle->DSN, ${RT::DatabaseUser}, ${RT::DatabasePassword} ) || die $DBI::errstr;
+ } else {
+ $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die $DBI::errstr;
+ }
+ }
+ print "Now populating database schema.\n";
+ insert_schema();
+ print "Now inserting database ACLs\n";
+ insert_acl() unless $RT::DatabaseType eq 'Oracle';
+ print "Now inserting RT core system objects\n";
+ insert_initial_data();
+ print "Now inserting RT data\n";
+ insert_data( $RT::EtcPath . "/initialdata" );
+}
+elsif ( $args{'action'} eq 'drop' ) {
+ unless ( $dbh =
+ DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) )
+ {
+ warn $DBI::errstr;
+ warn "Database doesn't appear to exist. Aborting database drop.";
+ exit;
+ }
+ drop_db();
+}
+elsif ( $args{'action'} eq 'insert_initial' ) {
+ insert_initial_data();
+}
+elsif ( $args{'action'} eq 'insert' ) {
+ insert_data( $args{'datafile'} || ($args{'datadir'}."/content") );
+}
+elsif ( $args{'action'} eq 'acl' ) {
+ $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
+ || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
+ insert_acl($args{'datadir'});
+}
+elsif ( $args{'action'} eq 'schema' ) {
+ $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
+ || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
+ insert_schema($args{'datadir'});
+}
+else {
+ print STDERR "$0 called with an invalid --action parameter\n";
+ exit(-1);
+}
+
+# {{{ sub insert_schema
+sub insert_schema {
+ my $base_path = (shift || $RT::EtcPath);
+ my (@schema);
+ print "Creating database schema.\n";
+
+ my $schema_file = $base_path . "/schema." . $RT::DatabaseType;
+ if ( -f $schema_file ) {
+ open( SCHEMA, "<$schema_file" ) or die "Can't open $schema_file: $!";
+ my @lines = <SCHEMA>;
+
+ my $local_schema_file = $RT::LocalEtcPath . "/schema." . $RT::DatabaseType;
+ if (-f $local_schema_file) {
+ open( SCHEMA_LOCAL, "<$local_schema_file" )
+ or die "Can't open $local_schema_file: $!";
+ push @lines, ';;', <SCHEMA_LOCAL>;
+ }
+
+ my $statement = "";
+ foreach my $line (@lines) {
+ $line =~ s/\#.*//g;
+ $line =~ s/--.*//g;
+ $statement .= $line;
+ if ( $line =~ /;(\s*)$/ ) {
+ $statement =~ s/;(\s*)$//g;
+ push @schema, $statement;
+ $statement = "";
+ }
+ }
+
+ local $SIG{__WARN__} = sub {};
+ my $is_local = 0; # local/etc/schema needs to be nonfatal.
+ $dbh->begin_work or die $dbh->errstr;
+ foreach my $statement (@schema) {
+ if ( $statement =~ /^\s*;$/ ) { $is_local = 1; next; }
+
+ print STDERR "SQL: $statement\n" if defined $args{'debug'};
+ my $sth = $dbh->prepare($statement) or die $dbh->errstr;
+ unless ( $sth->execute or $is_local ) {
+ die "Problem with statement:\n $statement\n" . $sth->errstr;
+ }
+ }
+ $dbh->commit or die $dbh->errstr;
+ }
+ else {
+ die "Couldn't find schema file for " . $RT::DatabaseType . "\n";
+ }
+ print "Done setting up database schema.\n";
+}
+
+# }}}
+
+# {{{ sub drop_db
+sub drop_db {
+ if ( $RT::DatabaseType eq 'Oracle' ) {
+ print <<END;
+
+To delete the tables and sequences of the RT Oracle database by running
+ \@etc/drop.Oracle
+through SQLPlus.
+
+END
+ return;
+ }
+ unless ( $args{'force'} ) {
+ print <<END;
+
+About to drop $RT::DatabaseType database $RT::DatabaseName on $RT::DatabaseHost.
+WARNING: This will erase all data in $RT::DatabaseName.
+
+END
+ exit unless _yesno();
+
+ }
+
+ print "Dropping $RT::DatabaseType database $RT::DatabaseName.\n";
+
+ if ( $RT::DatabaseType eq 'SQLite' ) {
+ unlink $RT::DatabaseName or warn $!;
+ return;
+ }
+ $dbh->do("Drop DATABASE $RT::DatabaseName") or warn $DBI::errstr;
+}
+
+# }}}
+
+# {{{ sub create_db
+sub create_db {
+ print "Creating $RT::DatabaseType database $RT::DatabaseName.\n";
+ if ( $RT::DatabaseType eq 'SQLite' ) {
+ return;
+ }
+ elsif ( $RT::DatabaseType eq 'Pg' ) {
+ $dbh->do("CREATE DATABASE $RT::DatabaseName WITH ENCODING='UNICODE'");
+ if ( $DBI::errstr ) {
+ $dbh->do("CREATE DATABASE $RT::DatabaseName") || die $DBI::errstr;
+ }
+ }
+ elsif ( $RT::DatabaseType eq 'Oracle' ) {
+ insert_acl();
+ }
+ elsif ( $RT::DatabaseType eq 'Informix' ) {
+ $ENV{DB_LOCALE} = 'en_us.utf8';
+ $dbh->do("CREATE DATABASE $RT::DatabaseName WITH BUFFERED LOG");
+ }
+ else {
+ $dbh->do("CREATE DATABASE $RT::DatabaseName") or die $DBI::errstr;
+ }
+}
+
+# }}}
+
+sub get_dba_password {
+ print "In order to create or update your RT database,";
+ print "this script needs to connect to your "
+ . $RT::DatabaseType
+ . " instance on "
+ . $RT::DatabaseHost . " as "
+ . $args{'dba'} . ".\n";
+ print "Please specify that user's database password below. If the user has no database\n";
+ print "password, just press return.\n\n";
+ print "Password: ";
+ ReadMode('noecho');
+ my $password = ReadLine(0);
+ ReadMode('normal');
+ print "\n";
+ return ($password);
+}
+
+# {{{ sub _yesno
+sub _yesno {
+ print "Proceed [y/N]:";
+ my $x = scalar(<STDIN>);
+ $x =~ /^y/i;
+}
+
+# }}}
+
+# {{{ insert_acls
+sub insert_acl {
+ my $base_path = (shift || $RT::EtcPath);
+
+ if ( $RT::DatabaseType =~ /^oracle$/i ) {
+ do $base_path . "/acl.Oracle"
+ || die "Couldn't find ACLS for Oracle\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^pg$/i ) {
+ do $base_path . "/acl.Pg" || die "Couldn't find ACLS for Pg\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^mysql$/i ) {
+ do $base_path . "/acl.mysql"
+ || die "Couldn't find ACLS for mysql in $base_path\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^Sybase$/i ) {
+ do $base_path . "/acl.Sybase"
+ || die "Couldn't find ACLS for Sybase in $base_path\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^informix$/i ) {
+ do $base_path . "/acl.Informix"
+ || die "Couldn't find ACLS for Informix in $base_path\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^SQLite$/i ) {
+ return;
+ }
+ else {
+ die "Unknown RT database type";
+ }
+
+ my @acl = acl($dbh);
+ foreach my $statement (@acl) {
+ print STDERR $statement if $args{'debug'};
+ my $sth = $dbh->prepare($statement) or die $dbh->errstr;
+ unless ( $sth->execute ) {
+ die "Problem with statement:\n $statement\n" . $sth->errstr;
+ }
+ }
+ print "Done setting up database ACLs.\n";
+}
+
+# }}}
+
+=head2 get_system_dsn
+
+Returns a dsn suitable for database creates and drops
+and user creates and drops
+
+=cut
+
+sub get_system_dsn {
+
+ my $dsn = $Handle->DSN;
+
+ #with mysql, you want to connect sans database to funge things
+ if ( $RT::DatabaseType eq 'mysql' ) {
+ $dsn =~ s/dbname=$RT::DatabaseName//;
+
+ # with postgres, you want to connect to database1
+ }
+ elsif ( $RT::DatabaseType eq 'Pg' ) {
+ $dsn =~ s/dbname=$RT::DatabaseName/dbname=template1/;
+ }
+ elsif ( $RT::DatabaseType eq 'Informix' ) {
+ # with Informix, you want to connect sans database:
+ $dsn =~ s/Informix:$RT::DatabaseName/Informix:/;
+ }
+ return $dsn;
+}
+
+sub insert_initial_data {
+
+ RT::InitLogging();
+
+ #connect to the db, for actual RT work
+ require RT::Handle;
+ $RT::Handle = RT::Handle->new();
+ $RT::Handle->Connect();
+
+ #Put together a current user object so we can create a User object
+ my $CurrentUser = new RT::CurrentUser();
+
+ print "Checking for existing system user...";
+ my $test_user = RT::User->new($CurrentUser);
+ $test_user->Load('RT_System');
+ if ( $test_user->id ) {
+ print "found!\n\nYou appear to have a functional RT database.\n"
+ . "Exiting, so as not to clobber your existing data.\n";
+ exit(-1);
+
+ }
+ else {
+ print "not found. This appears to be a new installation.\n";
+ }
+
+ print "Creating system user...";
+ my $RT_System = new RT::User($CurrentUser);
+
+ my ( $val, $msg ) = $RT_System->_BootstrapCreate(
+ Name => 'RT_System',
+ RealName => 'The RT System itself',
+ Comments =>
+'Do not delete or modify this user. It is integral to RT\'s internal database structures',
+ Creator => '1',
+ LastUpdatedBy => '1',
+ );
+
+ unless ( $val ) {
+ print "$msg\n";
+ exit(-1);
+ }
+ print "done.\n";
+ $RT::Handle->Disconnect() unless $RT::DatabaseType eq 'SQLite';
+
+}
+
+# load some sort of data into the database
+
+sub insert_data {
+ my $datafile = shift;
+
+ #Connect to the database and get RT::SystemUser and RT::Nobody loaded
+ RT::Init;
+
+ my $CurrentUser = RT::CurrentUser->new();
+ $CurrentUser->LoadByName('RT_System');
+
+ if ( $datafile eq $RT::EtcPath . "/initialdata" ) {
+
+ print "Creating Superuser ACL...";
+
+ my $superuser_ace = RT::ACE->new($CurrentUser);
+ $superuser_ace->_BootstrapCreate(
+ PrincipalId => ACLEquivGroupId( $CurrentUser->Id ),
+ PrincipalType => 'Group',
+ RightName => 'SuperUser',
+ ObjectType => 'RT::System',
+ ObjectId => '1' );
+
+ print "done.\n";
+ }
+
+ # Slurp in stuff to insert from the datafile. Possible things to go in here:-
+ # @groups, @users, @acl, @queues, @ScripActions, @ScripConditions, @templates
+
+ require $datafile
+ || die "Couldn't find initial data for import\n" . $@;
+
+ if ( @Groups ) {
+ print "Creating groups...";
+ foreach $item (@Groups) {
+ my $new_entry = RT::Group->new($CurrentUser);
+ my ( $return, $msg ) = $new_entry->_Create(%$item);
+ print "(Error: $msg)" unless $return;
+ print $return. ".";
+ }
+ print "done.\n";
+ }
+ if ( @Users ) {
+ print "Creating users...";
+ foreach $item (@Users) {
+ my $new_entry = new RT::User($CurrentUser);
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ print "(Error: $msg)" unless $return;
+ print $return. ".";
+ }
+ print "done.\n";
+ }
+ if ( @Queues ) {
+ print "Creating queues...";
+ for $item (@Queues) {
+ my $new_entry = new RT::Queue($CurrentUser);
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ print "(Error: $msg)" unless $return;
+ print $return. ".";
+ }
+ print "done.\n";
+ }
+ if ( @ACL ) {
+ print "Creating ACL...";
+ for my $item (@ACL) {
+
+ my ($princ, $object);
+
+ # Global rights or Queue rights?
+ if ( $item->{'Queue'} ) {
+ $object = RT::Queue->new($CurrentUser);
+ $object->Load( $item->{'Queue'} );
+ } else {
+ $object = $RT::System;
+ }
+
+ # Group rights or user rights?
+ if ( $item->{'GroupDomain'} ) {
+ $princ = RT::Group->new($CurrentUser);
+ if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
+ $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
+ } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
+ $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
+ } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
+ $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
+ } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
+ $item->{'Queue'} )
+ {
+ $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
+ Queue => $object->id);
+ } else {
+ $princ->Load( $item->{'GroupId'} );
+ }
+ } else {
+ $princ = RT::User->new($CurrentUser);
+ $princ->Load( $item->{'UserId'} );
+ }
+
+ # Grant it
+ my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
+ Right => $item->{'Right'},
+ Object => $object );
+
+ if ( $return ) {
+ print $return. ".";
+ }
+ else {
+ print $msg . ".";
+
+ }
+
+ }
+ print "done.\n";
+ }
+ if ( @CustomFields ) {
+ print "Creating custom fields...";
+ for $item (@CustomFields) {
+ my $new_entry = new RT::CustomField($CurrentUser);
+ my $values = $item->{'Values'};
+ delete $item->{'Values'};
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ unless( $return ) {
+ print "(Error: $msg)\n";
+ next;
+ }
+
+ foreach my $value ( @{$values} ) {
+ my ( $eval, $emsg ) = $new_entry->AddValue(%$value);
+ print "(Error: $emsg)\n" unless $eval;
+ }
+
+ if ( $item->{LookupType} && !exists $item->{'Queue'} ) { # enable by default
+ my $ocf = RT::ObjectCustomField->new($CurrentUser);
+ $ocf->Create( CustomField => $new_entry->Id );
+ }
+
+ print "(Error: $msg)\n" unless $return;
+ print $return. ".";
+ }
+
+ print "done.\n";
+ }
+
+ if ( @ScripActions ) {
+ print "Creating ScripActions...";
+
+ for $item (@ScripActions) {
+ my $new_entry = RT::ScripAction->new($CurrentUser);
+ my ($return,$msg) = $new_entry->Create(%$item);
+ unless ($return) {
+ print "(Error: $msg)\n";
+ next;
+ }
+ print $return. ".";
+ }
+
+ print "done.\n";
+ }
+
+ if ( @ScripConditions ) {
+ print "Creating ScripConditions...";
+
+ for $item (@ScripConditions) {
+ my $new_entry = RT::ScripCondition->new($CurrentUser);
+ my ($return,$msg) = $new_entry->Create(%$item);
+ unless ($return) {
+ print "(Error: $msg)\n";
+ next;
+ }
+ print $return. ".";
+ }
+
+ print "done.\n";
+ }
+
+ if ( @Templates ) {
+ print "Creating templates...";
+
+ for $item (@Templates) {
+ my $new_entry = new RT::Template($CurrentUser);
+ my $return = $new_entry->Create(%$item);
+ print $return. ".";
+ }
+ print "done.\n";
+ }
+ if ( @Scrips ) {
+ print "Creating scrips...";
+
+ for $item (@Scrips) {
+ my $new_entry = new RT::Scrip($CurrentUser);
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ if ( $return ) {
+ print $return. ".";
+ }
+ else {
+ print "(Error: $msg)\n";
+ }
+ }
+ print "done.\n";
+ }
+ if ( @Attributes ) {
+ print "Creating predefined searches...";
+ my $sys = RT::System->new($CurrentUser);
+
+ for $item (@Attributes) {
+ my $obj = delete $item->{Object}; # XXX: make this something loadable
+ $obj ||= $sys;
+ my ( $return, $msg ) = $obj->AddAttribute (%$item);
+ if ( $return ) {
+ print $return. ".";
+ }
+ else {
+ print "(Error: $msg)\n";
+ }
+ }
+ print "done.\n";
+ }
+ $RT::Handle->Disconnect() unless $RT::DatabaseType eq 'SQLite';
+ print "Done setting up database content.\n";
+}
+
+=head2 ACLEquivGroupId
+
+Given a userid, return that user's acl equivalence group
+
+=cut
+
+sub ACLEquivGroupId {
+ my $username = shift;
+ my $user = RT::User->new($RT::SystemUser);
+ $user->Load($username);
+ my $equiv_group = RT::Group->new($RT::SystemUser);
+ $equiv_group->LoadACLEquivalenceGroup($user);
+ return ( $equiv_group->Id );
+}
+
+sub help {
+
+ print <<EOF;
+
+$0: Set up RT's database
+
+--action init Initialize the database
+ drop Drop the database.
+ This will ERASE ALL YOUR DATA
+ insert_initial
+ Insert RT's core system objects
+ insert Insert data into RT's database.
+ By default, will use RT's installation data.
+ To use a local or supplementary datafile, specify it
+ using the '--datafile' option below.
+
+ acl Initialize only the database ACLs
+ To use a local or supplementary datafile, specify it
+ using the '--datadir' option below.
+
+ schema Initialize only the database schema
+ To use a local or supplementary datafile, specify it
+ using the '--datadir' option below.
+
+--datafile /path/to/datafile
+--datadir /path/to/ Used to specify a path to find the local
+ database schema and acls to be installed.
+
+
+--dba dba's username
+--dba-password dba's password
+--prompt-for-dba-password Ask for the database administrator's password interactively
+
+
+EOF
+
+}
+
+1;
diff --git a/rt/sbin/rt-test-dependencies.in b/rt/sbin/rt-test-dependencies.in
new file mode 100644
index 0000000..ea6b9d1
--- /dev/null
+++ b/rt/sbin/rt-test-dependencies.in
@@ -0,0 +1,479 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/copyleft/gpl.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+#
+# This is just a basic script that checks to make sure that all
+# the modules needed by RT before you can install it.
+#
+
+use strict;
+no warnings qw(numeric redefine);
+use Getopt::Long;
+my %args;
+my %deps;
+GetOptions(
+ \%args, 'v|verbose',
+ 'install', 'with-MYSQL',
+ 'with-POSTGRESQL|with-pg|with-pgsql', 'with-SQLITE',
+ 'with-ORACLE', 'with-FASTCGI',
+ 'with-SPEEDYCGI', 'with-MODPERL1',
+ 'with-MODPERL2', 'with-DEV',
+ 'with-STANDALONE',
+ 'download=s',
+ 'repository=s'
+);
+
+unless (keys %args) {
+ help();
+ exit(0);
+}
+# Set up defaults
+my %default = (
+ 'with-MASON' => 1,
+ 'with-CORE' => 1,
+ 'with-CLI' => 1,
+ 'with-MAILGATE' => 1,
+ 'with-DEV' => @RT_DEVEL_MODE@,
+ 'with-STANDALONE' => @RT_STANDALONE@,
+);
+$args{$_} = $default{$_} foreach grep !exists $args{$_}, keys %default;
+
+
+{
+ my $section;
+ my %always_show_sections = (
+ perl => 1,
+ users => 1,
+ );
+
+ sub section {
+ my $s = shift;
+ $section = $s;
+ print "$s:\n";
+ }
+
+ my $any_missing = 0;
+ sub found {
+ my $msg = shift;
+ my $test = shift;
+ my $extra = shift;
+
+ $any_missing = 1 unless $test;
+ if ($args{'v'} or not $test or $always_show_sections{$section}) {
+ print "\t$msg...";
+ print $test ? "found" : "MISSING";
+ print "\n";
+ }
+
+ print "\t\t$extra\n" if defined $extra;
+ }
+
+ sub conclude {
+ if ($any_missing) {
+ print "\nSOMETHING WAS MISSING!\n";
+ } else {
+ print "\nEverything was found.\n";
+ }
+ }
+}
+
+sub help {
+
+ print <<'.';
+
+By default, testdeps determine whether you have
+installed all the perl modules RT needs to run.
+
+ --install Install missing modules
+
+The following switches will tell the tool to check for specific dependencies
+
+ --with-mysql Database interface for MySQL
+ --with-postgresql Database interface for PostgreSQL
+ --with-sqlite Database interface and driver for SQLite (unsupported)
+ --with-oracle Database interface for oracle (unsupported)
+
+ --with-standalone Libraries needed to support the standalone simple pure perl server
+ --with-fastcgi Libraries needed to support the fastcgi handler
+ --with-speedycgi Libraries needed to support the speedycgi handler
+ --with-modperl1 Libraries needed to support the modperl 1 handler
+ --with-modperl2 Libraries needed to support the modperl 2 handler
+
+ --with-dev Tools needed for RT development
+
+You can also specify -v or --verbose to list the status of all dependencies,
+rather than just the missing ones.
+
+The "RT_FIX_DEPS_CMD" environment variable, if set, will be used
+instead of the standard CPAN shell by --install to install any
+required modules. It will be called with the module name, or, if
+"RT_FIX_DEPS_CMD" contains a "%s", will replace the "%s" with the
+module name before calling the program.
+.
+}
+
+
+sub text_to_hash {
+ my %hash;
+ for my $line ( split /\n/, $_[0] ) {
+ my($key, $value) = $line =~ /(\S+)\s*(\S*)/;
+ $value ||= '';
+ $hash{$key} = $value;
+ }
+
+ return %hash;
+}
+
+$deps{'CORE'} = [ text_to_hash( << '.') ];
+Digest::base
+Digest::MD5 2.27
+DBI 1.37
+Class::ReturnValue 0.40
+Date::Format
+DBIx::SearchBuilder 1.50
+Text::Template
+File::Spec 0.8
+HTML::Entities
+HTML::Scrubber 0.08
+Log::Dispatch 2.0
+Locale::Maketext 1.06
+Locale::Maketext::Lexicon 0.32
+Locale::Maketext::Fuzzy
+MIME::Entity 5.108
+Mail::Mailer 1.57
+Net::SMTP
+Text::Wrapper
+Time::ParseDate
+Time::HiRes
+File::Temp
+Text::Autoformat
+Text::Quoted 2.02
+Tree::Simple 1.04
+Regexp::Common
+Scalar::Util
+Module::Versions::Report 1.03
+Cache::Simple::TimedExpiry
+UNIVERSAL::require
+Calendar::Simple
+CSS::Squish 0.06
+.
+
+$deps{'MASON'} = [ text_to_hash( << '.') ];
+HTML::Mason 1.23
+Errno
+Digest::MD5 2.27
+CGI::Cookie 1.20
+Storable 2.08
+Apache::Session 1.53
+XML::RSS 1.05
+GD
+GD::Graph
+GD::Text
+Text::WikiFormat 0.76
+CSS::Squish 0.06
+.
+
+$deps{'STANDALONE'} = [ text_to_hash( << '.') ];
+HTTP::Server::Simple 0.07
+HTTP::Server::Simple::Mason 0.09
+.
+
+$deps{'MAILGATE'} = [ text_to_hash( << '.') ];
+HTML::TreeBuilder
+HTML::FormatText
+Getopt::Long
+LWP::UserAgent
+Pod::Usage
+.
+
+$deps{'CLI'} = [ text_to_hash( << '.') ];
+Getopt::Long 2.24
+LWP
+HTTP::Request::Common
+Text::ParseWords
+Term::ReadLine
+Term::ReadKey
+.
+
+$deps{'DEV'} = [ text_to_hash( << '.') ];
+Test::Inline
+Apache::Test
+HTML::Form
+HTML::TokeParser
+WWW::Mechanize
+Test::WWW::Mechanize 1.04
+Module::Refresh 0.03
+Test::Expect 0.30
+XML::Simple
+File::Find
+.
+
+$deps{'FASTCGI'} = [ text_to_hash( << '.') ];
+CGI 2.92
+FCGI
+CGI::Fast
+.
+
+$deps{'SPEEDYCGI'} = [ text_to_hash( << '.') ];
+CGI 2.92
+CGI::SpeedyCGI
+.
+
+
+$deps{'MODPERL1'} = [ text_to_hash( << '.') ];
+CGI 2.92
+Apache::Request
+Apache::DBI 0.92
+.
+
+$deps{'MODPERL2'} = [ text_to_hash( << '.') ];
+CGI 2.92
+Apache::DBI
+HTML::Mason 1.31
+.
+
+$deps{'MYSQL'} = [ text_to_hash( << '.') ];
+DBD::mysql 2.1018
+.
+$deps{'ORACLE'} = [ text_to_hash( << '.') ];
+DBD::Oracle
+.
+$deps{'POSTGRESQL'} = [ text_to_hash( << '.') ];
+DBD::Pg 1.43
+.
+
+$deps{'SQLITE'} = [ text_to_hash( << '.') ];
+DBD::SQLite 1.00
+.
+
+if ($args{'download'}) {
+
+ download_mods();
+}
+
+
+check_perl_version();
+
+check_users();
+
+
+foreach my $type (sort grep $args{$_}, keys %args) {
+ next unless ($type =~ /^with-(.*?)$/);
+
+ $type = $1;
+ section("$type dependencies");
+
+ my @missing;
+ my @deps = @{ $deps{$type} };
+ while (@deps) {
+ my $module = shift @deps;
+ my $version = shift @deps;
+ my $ret = test_dep($module, $version);
+
+ push @missing, $module, $version unless $ret;
+ }
+ if ( $args{'install'} ) {
+ while( @missing ) {
+ resolve_dep(shift @missing, shift @missing);
+ }
+ }
+}
+
+conclude();
+
+sub test_dep {
+ my $module = shift;
+ my $version = shift;
+
+ eval "use $module $version ()";
+ if ($@) {
+ my $error = $@;
+ $error =~ s/\n(.*)$//s;
+ undef $error unless $error =~ /this is only/;
+ found("$module $version", 0, $error);
+
+ return undef;
+ } else {
+ my $msg = "$module";
+ $msg .= " >=$version" if $version;
+ found($msg, 1);
+ return 1;
+ }
+}
+
+sub resolve_dep {
+ my $module = shift;
+ my $version = shift;
+
+ print "\nInstall module $module\n";
+
+ my $ext = $ENV{'RT_FIX_DEPS_CMD'};
+ unless( $ext ) {
+ my $configured = 1;
+ {
+ local @INC = @INC;
+ if ( $ENV{'HOME'} ) {
+ unshift @INC, "$ENV{'HOME'}/.cpan";
+ }
+ $configured = eval { require CPAN::MyConfig } || eval { require CPAN::Config };
+ }
+ unless ( $configured ) {
+ print <<END;
+You didn't configure CPAN shell yet.
+Please run `@PERL@ -MCPAN -e shell` tool and configure it.
+END
+ exit(1);
+ }
+ my $rv = eval { require CPAN; CPAN::Shell->install($module) };
+ return $rv unless $@;
+
+ print <<END;
+Failed to load module CPAN.
+
+-------- Error ---------
+$@
+------------------------
+
+When we tried to start installing RT's perl dependencies,
+we were unable to load the CPAN client. This module is usually distributed
+with Perl. This usually indicates that your vendor has shipped an unconfigured
+or incorrectly configured CPAN client.
+The error above may (or may not) give you a hint about what went wrong
+
+You have several choices about how to install dependencies in
+this situatation:
+
+1) use a different tool to install dependencies by running setting the following
+ shell environment variable and rerunning this tool:
+ RT_FIX_DEPS_CMD='@PERL@ -MCPAN -e"install %s"'
+2) Attempt to configure CPAN by running:
+ `@PERL@ -MCPAN -e shell` program from shell.
+ If this fails, you may have to manually upgrade CPAN (see below)
+3) Try to update the CPAN client. Download it from:
+ http://search.cpan.org/dist/CPAN and try again
+4) Install each dependency manually by downloading them one by one from
+ http://search.cpan.org
+
+END
+ exit(1);
+ }
+
+ if( $ext =~ /\%s/) {
+ $ext =~ s/\%s/$module/g; # sprintf( $ext, $module );
+ } else {
+ $ext .= " $module";
+ }
+ print "\t\tcommand: '$ext'\n";
+ return scalar `$ext 1>&2`;
+}
+
+sub download_mods {
+ my %modules;
+ use CPAN;
+
+ foreach my $key (keys %deps) {
+ my @deps = (@{$deps{$key}});
+ while (@deps) {
+ my $mod = shift @deps;
+ my $ver = shift @deps;
+ next if ($mod =~ /^(DBD-|Apache-Request)/);
+ $modules{$mod} = $ver;
+ }
+ }
+ my @mods = keys %modules;
+ CPAN::get();
+ my $moddir = $args{'download'};
+ foreach my $mod (@mods) {
+ $CPAN::Config->{'build_dir'} = $moddir;
+ CPAN::get($mod);
+ }
+
+ opendir(DIR, $moddir);
+ while ( my $dir = readdir(DIR)) {
+ print "Dir is $dir\n";
+ next if ( $dir =~ /^\.\.?$/);
+
+ # Skip things we've previously tagged
+ my $out = `svn ls $args{'repository'}/tags/$dir`;
+ next if ($out);
+
+ if ($dir =~ /^(.*)-(.*?)$/) {
+ `svn_load_dirs -no_user_input -t tags/$dir -v $args{'repository'} dists/$1 $moddir/$dir`;
+ `rm -rf $moddir/$dir`;
+
+ }
+
+ }
+ closedir(DIR);
+ exit;
+}
+
+sub check_perl_version {
+ section("perl");
+ eval {require 5.008003};
+ if ($@) {
+ found("5.8.3", 0,"RT is known to be non-functional on versions of perl older than 5.8.3. Please upgrade to 5.8.3 or newer.");
+ exit(1);
+ } else {
+ found( ">=5.8.3($])", 1);
+ }
+}
+
+sub check_users {
+ section("users");
+ found("rt group (@RTGROUP@)", defined getgrnam("@RTGROUP@"));
+ found("bin owner (@BIN_OWNER@)", defined getpwnam("@BIN_OWNER@"));
+ found("libs owner (@LIBS_OWNER@)", defined getpwnam("@LIBS_OWNER@"));
+ found("libs group (@LIBS_GROUP@)", defined getgrnam("@LIBS_GROUP@"));
+ found("web owner (@WEB_USER@)", defined getpwnam("@WEB_USER@"));
+ found("web group (@WEB_GROUP@)", defined getgrnam("@WEB_GROUP@"));
+}
+
+
+
+1;
diff --git a/site_perl/Bill.pm b/site_perl/Bill.pm
deleted file mode 100644
index 4d7e059..0000000
--- a/site_perl/Bill.pm
+++ /dev/null
@@ -1,44 +0,0 @@
-package FS::Bill;
-
-use strict;
-use vars qw(@ISA);
-use FS::cust_main;
-
-@ISA = qw(FS::cust_main);
-
-warn "FS::Bill depriciated\n";
-
-=head1 NAME
-
-FS::Bill - Legacy stub
-
-=head1 SYNOPSIS
-
-The functionality of FS::Bill has been integrated into FS::cust_main.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-24 - 25 - 28
-
-use Safe; evaluate all fees with perl (still on TODO list until I write
-some examples & test opmask to see if we can read db)
-%hash=$obj->hash later ivan@sisd.com 98-mar-13
-
-packages with no next bill date start at $time not time, this should
-eliminate the last of the problems with billing at a past date
-also rewrite the invoice priting logic not to print invoices for things
-that haven't happended yet and update $cust_bill->printed when we print
-so PAST DUE notices work, and s/date/_date/
-ivan@sisd.com 98-jun-4
-
-more logic for past due stuff - packages with no next bill date start
-at $cust_pkg->setup || $time ivan@sisd.com 98-jul-13
-
-moved a few things in collection logic; negative charges should work
-ivan@sisd.com 98-aug-6
-
-pod, moved everything to FS::cust_main ivan@sisd.com 98-sep-19
-
-=cut
-
-1;
diff --git a/site_perl/CGI.pm b/site_perl/CGI.pm
deleted file mode 100644
index d2ed521..0000000
--- a/site_perl/CGI.pm
+++ /dev/null
@@ -1,143 +0,0 @@
-package FS::CGI;
-
-use strict;
-use vars qw(@EXPORT_OK @ISA);
-use Exporter;
-use CGI::Base;
-use CGI::Carp qw(fatalsToBrowser);
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(header menubar idiot eidiot);
-
-=head1 NAME
-
-FS::CGI - Subroutines for the web interface
-
-=head1 SYNOPSIS
-
- use FS::CGI qw(header menubar idiot eidiot);
-
- print header( 'Title', '' );
- print header( 'Title', menubar('item', 'URL', ... ) );
-
- idiot "error message";
- eidiot "error message";
-
-=head1 DESCRIPTION
-
-Provides a few common subroutines for the web interface.
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item header TITLE, MENUBAR
-
-Returns an HTML header.
-
-=cut
-
-sub header {
- my($title,$menubar)=@_;
-
- <<END;
- <HTML>
- <HEAD>
- <TITLE>
- $title
- </TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>
- $title
- </H1>
- $menubar
- </CENTER>
- <HR>
-END
-}
-
-=item menubar ITEM, URL, ...
-
-Returns an HTML menubar.
-
-=cut
-
-sub menubar { #$menubar=menubar('Main Menu', '../', 'Item', 'url', ... );
- my($item,$url,@html);
- while (@_) {
- ($item,$url)=splice(@_,0,2);
- push @html, qq!<A HREF="$url">$item</A>!;
- }
- join(' | ',@html);
-}
-
-=item idiot ERROR
-
-Sends headers and an HTML error message.
-
-=cut
-
-sub idiot {
- my($error)=@_;
- CGI::Base::SendHeaders();
- print <<END;
-<HTML>
- <HEAD>
- <TITLE>Error processing your request</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H4>Error processing your request</H4>
- </CENTER>
- Your request could not be processed because of the following error:
- <P><B>$error</B>
- <P>Hit the <I>Back</I> button in your web browser, correct this mistake, and try again.
- </BODY>
-</HTML>
-END
-
-}
-
-=item eidiot ERROR
-
-Sends headers and an HTML error message, then exits.
-
-=cut
-
-sub eidiot {
- idiot(@_);
- exit;
-}
-
-=back
-
-=head1 BUGS
-
-Not OO.
-
-Not complete.
-
-Uses CGI-modules instead of CGI.pm
-
-=head1 SEE ALSO
-
-L<CGI::Base>
-
-=head1 HISTORY
-
-subroutines for the HTML/CGI GUI, not properly OO. :(
-
-ivan@sisd.com 98-apr-16
-ivan@sisd.com 98-jun-22
-
-lose the background, eidiot ivan@sisd.com 98-sep-2
-
-pod ivan@sisd.com 98-sep-12
-
-=cut
-
-1;
-
-
diff --git a/site_perl/Conf.pm b/site_perl/Conf.pm
deleted file mode 100644
index d3ef307..0000000
--- a/site_perl/Conf.pm
+++ /dev/null
@@ -1,113 +0,0 @@
-package FS::Conf;
-
-use vars qw($default_dir);
-use IO::File;
-
-$default_dir='/var/spool/freeside/conf';
-
-=head1 NAME
-
-FS::Conf - Read access to Freeside configuration values
-
-=head1 SYNOPSIS
-
- use FS::Conf;
-
- $conf = new FS::Conf;
- $conf = new FS::Conf "/non/standard/config/directory";
-
- $dir = $conf->dir;
-
- $value = $conf->config('key');
- @list = $conf->config('key');
- $bool = $conf->exists('key');
-
-=head1 DESCRIPTION
-
-Read access to Freeside configuration values. Keys currently map to filenames,
-but this may change in the future.
-
-=head1 METHODS
-
-=over 4
-
-=item new [ DIRECTORY ]
-
-Create a new configuration object. Optionally, a non-default directory may
-be specified.
-
-=cut
-
-sub new {
- my($proto,$dir) = @_;
- my($class) = ref($proto) || $proto;
- my($self) = { 'dir' => $dir || $default_dir } ;
- bless ($self, $class);
-}
-
-=item dir
-
-Returns the directory.
-
-=cut
-
-sub dir {
- my($self) = @_;
- $self->{dir};
-}
-
-=item config
-
-Returns the configuration value or values (depending on context) for key.
-
-=cut
-
-sub config {
- my($self,$file)=@_;
- my($dir)=$self->dir;
- my $fh = new IO::File "<$dir/$file" or return;
- if ( wantarray ) {
- map {
- /^(.*)$/ or die "Illegal line in $dir/$file:\n$_\n";
- $1;
- } <$fh>;
- } else {
- <$fh> =~ /^(.*)$/ or die "Illegal line in $dir/$file:\n$_\n";
- $1;
- }
-}
-
-=item exists
-
-Returns true if the specified key exists, even if the corresponding value
-is undefined.
-
-=cut
-
-sub exists {
- my($self,$file)=@_;
- my($dir) = $self->dir;
- -e "$dir/$file";
-}
-
-=back
-
-=head1 BUGS
-
-The option to specify a non-default directory should probably be removed.
-
-Write access (with locking) should be implemented.
-
-=head1 SEE ALSO
-
-config.html from the base documentation contains a list of configuration files.
-
-=head1 HISTORY
-
-Ivan Kohler <ivan@sisd.com> 98-sep-6
-
-sub exists forgot to fetch $dir ivan@sisd.com 98-sep-27
-
-=cut
-
-1;
diff --git a/site_perl/Invoice.pm b/site_perl/Invoice.pm
deleted file mode 100644
index 5eb596f..0000000
--- a/site_perl/Invoice.pm
+++ /dev/null
@@ -1,45 +0,0 @@
-package FS::Invoice;
-
-use strict;
-use vars qw(@ISA);
-use FS::cust_bill;
-
-@ISA = qw(FS::cust_bill);
-
-#warn "FS::Invoice depriciated\n";
-
-=head1 NAME
-
-FS::Invoice - Legacy stub
-
-=head1 SYNOPSIS
-
-The functioanlity of FS::invoice has been integrated in FS::cust_bill.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jun-25 - 27
-
-maybe should be changed to be OO-functions on $cust_bill objects?
-(instead of passing invnum, ugh).
-
-ISA cust_bill and return inovice instead of passing filehandle
-ivan@sisd.com 98-mar-13
-
-(add postscript output!)
-
-close our kid when we're done ivan@sisd.com 98-jun-4
-
-separated code which shuffled data from code which formatted.
-(so i could) fixed past due notices showing up when balance due =< 0
-return address comes from /var/spool/freeside/conf/address
-ivan@sisd.com 98-jul-2
-
-pod ivan@sisd.com 98-sep-20something
-
-s/ISA/@ISA/ in use vars ivan@sisd.com 98-sep-27
-
-=cut
-
-1;
-
diff --git a/site_perl/Record.pm b/site_perl/Record.pm
deleted file mode 100644
index 9b30850..0000000
--- a/site_perl/Record.pm
+++ /dev/null
@@ -1,868 +0,0 @@
-package FS::Record;
-
-use strict;
-use vars qw($dbdef_file $dbdef $setup_hack $AUTOLOAD @ISA @EXPORT_OK);
-use subs qw(reload_dbdef);
-use Exporter;
-use Carp;
-use File::CounterFile;
-use FS::UID qw(dbh checkruid swapuid getotaker datasrc);
-use FS::dbdef;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef);
-
-$File::CounterFile::DEFAULT_DIR = "/var/spool/freeside/counters" ;
-
-$dbdef_file = "/var/spool/freeside/dbdef.". datasrc;
-
-reload_dbdef unless $setup_hack;
-
-=head1 NAME
-
-FS::Record - Database record objects
-
-=head1 SYNOPSIS
-
- use FS::Record;
- use FS::Record qw(dbh fields hfields qsearch qsearchs dbdef);
-
- $record = new FS::Record 'table', \%hash;
- $record = new FS::Record 'table', { 'column' => 'value', ... };
-
- $record = qsearchs FS::Record 'table', \%hash;
- $record = qsearchs FS::Record 'table', { 'column' => 'value', ... };
- @records = qsearch FS::Record 'table', \%hash;
- @records = qsearch FS::Record 'table', { 'column' => 'value', ... };
-
- $table = $record->table;
- $dbdef_table = $record->dbdef_table;
-
- $value = $record->get('column');
- $value = $record->getfield('column');
- $value = $record->column;
-
- $record->set( 'column' => 'value' );
- $record->setfield( 'column' => 'value' );
- $record->column('value');
-
- %hash = $record->hash;
-
- $hashref = $record->hashref;
-
- $error = $record->add;
-
- $error = $record->del;
-
- $error = $new_record->rep($old_record);
-
- $value = $record->unique('column');
-
- $value = $record->ut_float('column');
- $value = $record->ut_number('column');
- $value = $record->ut_numbern('column');
- $value = $record->ut_money('column');
- $value = $record->ut_text('column');
- $value = $record->ut_textn('column');
- $value = $record->ut_alpha('column');
- $value = $record->ut_alphan('column');
- $value = $record->ut_phonen('column');
- $value = $record->ut_anythingn('column');
-
- $dbdef = reload_dbdef;
- $dbdef = reload_dbdef "/non/standard/filename";
- $dbdef = dbdef;
-
- $quoted_value = _quote($value,'table','field');
-
- #depriciated
- $fields = hfields('table');
- if ( $fields->{Field} ) { # etc.
-
- @fields = fields 'table';
-
-
-=head1 DESCRIPTION
-
-(Mostly) object-oriented interface to database records. Records are currently
-implemented on top of DBI. FS::Record is intended as a base class for
-table-specific classes to inherit from, i.e. FS::cust_main.
-
-=head1 METHODS
-
-=over 4
-
-=item new TABLE, HASHREF
-
-Creates a new record. It doesn't store it in the database, though. See
-L<"add"> for that.
-
-Note that the object stores this hash reference, not a distinct copy of the
-hash it points to. You can ask the object for a copy with the I<hash>
-method.
-
-=cut
-
-sub new {
- my($proto,$table,$hashref) = @_;
- confess "Second arguement to FS::Record->new is not a HASH ref: ",
- ref($hashref), " ", $hashref, "\n"
- unless ref($hashref) eq 'HASH'; #bad practice?
-
- #check to make sure $table exists? (ask dbdef)
-
- foreach my $field ( FS::Record::fields $table ) {
- $hashref->{$field}='' unless defined $hashref->{$field};
- }
-
- # mySQL must rtrim the inbound text strings or store them z-terminated
- # I simulate this for Postgres below
- # Turned off in favor of ChopBlanks in UID.pm (see man DBI)
- #if (datasrc =~ m/Pg/)
- #{
- # foreach my $index (keys %$hashref)
- # {
- # $$hashref{$index} = unpack("A255", $$hashref{$index})
- # if ($$hashref{$index} =~ m/ $/) ;
- # }
- #}
-
- foreach my $column (keys %{$hashref}) {
- #trim the '$' from money fields for Pg (beong HERE?)
- #(what about Pg i18n?)
- if ( datasrc =~ m/Pg/
- && $dbdef->table($table)->column($column)->type eq 'money' ) {
- ${$hashref}{$column} =~ s/^\$//;
- }
- #foreach my $column ( grep $dbdef->table($table)->column($_)->type eq 'money', keys %{$hashref} ) {
- # ${$hashref}{$column} =~ s/^\$//;
- #}
- }
-
- my $class = ref($proto) || $proto;
- my $self = { 'Table' => $table,
- 'Hash' => $hashref,
- };
-
- bless ($self, $class);
-
-}
-
-=item qsearch TABLE, HASHREF
-
-Searches the database for all records matching (at least) the key/value pairs
-in HASHREF. Returns all the records found as FS::Record objects.
-
-=cut
-
-# Usage: @records = &FS::Search::qsearch($table,\%hash);
-# Each element of @records is a FS::Record object.
-sub qsearch {
- my($table,$record) = @_;
- my($dbh) = dbh;
-
- my(@fields)=grep exists($record->{$_}), fields($table);
-
- my($sth);
- my($statement) = "SELECT * FROM $table". ( @fields
- ? " WHERE ". join(' AND ',
- map("$_ = ". _quote($record->{$_},$table,$_), @fields)
- )
- : ''
- );
- $sth=$dbh->prepare($statement)
- or croak $dbh->errstr; #is that a little too harsh? hmm.
-
- map {
- new FS::Record ($table,$sth->fetchrow_hashref);
- } ( 1 .. $sth->execute );
-
-}
-
-=item qsearchs TABLE, HASHREF
-
-Searches the database for a record matching (at least) the key/value pairs
-in HASHREF, and returns the record found as an FS::Record object. If more than
-one record matches, it B<carp>s but returns the first. If this happens, you
-either made a logic error in asking for a single item, or your data is
-corrupted.
-
-=cut
-
-sub qsearchs { # $result_record = &FS::Record:qsearchs('table',\%hash);
- my(@result) = qsearch(@_);
- carp "Multiple records in scalar search!" if scalar(@result) > 1;
- #should warn more vehemently if the search was on a primary key?
- $result[0];
-}
-
-=item table
-
-Returns the table name.
-
-=cut
-
-sub table {
- my($self) = @_;
- $self -> {'Table'};
-}
-
-=item dbdef_table
-
-Returns the FS::dbdef_table object for the table.
-
-=cut
-
-sub dbdef_table {
- my($self)=@_;
- my($table)=$self->table;
- $dbdef->table($table);
-}
-
-=item get, getfield COLUMN
-
-Returns the value of the column/field/key COLUMN.
-
-=cut
-
-sub get {
- my($self,$field) = @_;
- # to avoid "Use of unitialized value" errors
- if ( defined ( $self->{Hash}->{$field} ) ) {
- $self->{Hash}->{$field};
- } else {
- '';
- }
-}
-sub getfield {
- get(@_);
-}
-
-=item set, setfield COLUMN, VALUE
-
-Sets the value of the column/field/key COLUMN to VALUE. Returns VALUE.
-
-=cut
-
-sub set {
- my($self,$field,$value) = @_;
- $self->{'Hash'}->{$field} = $value;
-}
-sub setfield {
- set(@_);
-}
-
-=item AUTLOADED METHODS
-
-$record->column is a synonym for $record->get('column');
-
-$record->column('value') is a synonym for $record->set('column','value');
-
-=cut
-
-sub AUTOLOAD {
- my($self,$value)=@_;
- my($field)=$AUTOLOAD;
- $field =~ s/.*://;
- if ( defined($value) ) {
- $self->setfield($field,$value);
- } else {
- $self->getfield($field);
- }
-}
-
-=item hash
-
-Returns a list of the column/value pairs, usually for assigning to a new hash.
-
-To make a distinct duplicate of an FS::Record object, you can do:
-
- $new = new FS::Record ( $old->table, { $old->hash } );
-
-=cut
-
-sub hash {
- my($self) = @_;
- %{ $self->{'Hash'} };
-}
-
-=item hashref
-
-Returns a reference to the column/value hash.
-
-=cut
-
-sub hashref {
- my($self) = @_;
- $self->{'Hash'};
-}
-
-=item add
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub add {
- my($self) = @_;
- my($dbh)=dbh;
- my($table)=$self->table;
-
- #single-field unique keys are given a value if false
- #(like MySQL's AUTO_INCREMENT)
- foreach ( $dbdef->table($table)->unique->singles ) {
- $self->unique($_) unless $self->getfield($_);
- }
- #and also the primary key
- my($primary_key)=$dbdef->table($table)->primary_key;
- $self->unique($primary_key)
- if $primary_key && ! $self->getfield($primary_key);
-
- my (@fields) =
- grep defined($self->getfield($_)) && $self->getfield($_) ne "",
- fields($table)
- ;
-
- my($sth);
- my($statement)="INSERT INTO $table ( ".
- join(', ',@fields ).
- ") VALUES (".
- join(', ',map(_quote($self->getfield($_),$table,$_), @fields)).
- ")"
- ;
- $sth = $dbh->prepare($statement) or return $dbh->errstr;
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $sth->execute or return $sth->errstr;
-
- '';
-}
-
-=item del
-
-Delete this record from the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub del {
- my($self) = @_;
- my($dbh)=dbh;
- my($table)=$self->table;
-
- my($sth);
- my($statement)="DELETE FROM $table WHERE ". join(' AND ',
- map {
- $self->getfield($_) eq ''
- ? "$_ IS NULL"
- : "$_ = ". _quote($self->getfield($_),$table,$_)
- } ( $dbdef->table($table)->primary_key )
- ? ($dbdef->table($table)->primary_key)
- : fields($table)
- );
- $sth = $dbh->prepare($statement) or return $dbh->errstr;
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- my($rc);
- $rc=$sth->execute or return $sth->errstr;
- #not portable #return "Record not found, statement:\n$statement" if $rc eq "0E0";
-
- undef $self; #no need to keep object!
-
- '';
-}
-
-=item rep OLD_RECORD
-
-Replace the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub rep {
- my($new,$old)=@_;
- my($dbh)=dbh;
- my($table)=$old->table;
- my(@fields)=fields($table);
- my(@diff)=grep $new->getfield($_) ne $old->getfield($_), @fields;
-
- if ( scalar(@diff) == 0 ) {
- carp "Records identical";
- return '';
- }
-
- return "Records not in same table!" unless $new->table eq $table;
-
- my($sth);
- my($statement)="UPDATE $table SET ". join(', ',
- map {
- "$_ = ". _quote($new->getfield($_),$table,$_)
- } @diff
- ). ' WHERE '.
- join(' AND ',
- map {
- $old->getfield($_) eq ''
- ? "$_ IS NULL"
- : "$_ = ". _quote($old->getfield($_),$table,$_)
-# } @fields
-# } ( primary_key($table) ? (primary_key($table)) : @fields )
- } ( $dbdef->table($table)->primary_key
- ? ($dbdef->table($table)->primary_key)
- : @fields
- )
- )
- ;
- #warn $statement;
- $sth = $dbh->prepare($statement) or return $dbh->errstr;
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- my($rc);
- $rc=$sth->execute or return $sth->errstr;
- #not portable #return "Record not found (or records identical)." if $rc eq "0E0";
-
- '';
-
-}
-
-=item unique COLUMN
-
-Replaces COLUMN in record with a unique number. Called by the B<add> method
-on primary keys and single-field unique columns (see L<FS::dbdef_table>).
-Returns the new value.
-
-=cut
-
-sub unique {
- my($self,$field) = @_;
- my($table)=$self->table;
-
- croak("&FS::UID::checkruid failed") unless &checkruid;
-
- croak "Unique called on field $field, but it is ",
- $self->getfield($field),
- ", not null!"
- if $self->getfield($field);
-
- #warn "table $table is tainted" if is_tainted($table);
- #warn "field $field is tainted" if is_tainted($field);
-
- &swapuid;
- my($counter) = new File::CounterFile "$table.$field",0;
-# hack for web demo
-# getotaker() =~ /^([\w\-]{1,16})$/ or die "Illegal CGI REMOTE_USER!";
-# my($user)=$1;
-# my($counter) = new File::CounterFile "$user/$table.$field",0;
-# endhack
-
- my($index)=$counter->inc;
- $index=$counter->inc
- while qsearchs($table,{$field=>$index}); #just in case
- &swapuid;
-
- $index =~ /^(\d*)$/;
- $index=$1;
-
- $self->setfield($field,$index);
-
-}
-
-=item ut_float COLUMN
-
-Check/untaint floating point numeric data: 1.1, 1, 1.1e10, 1e10. May not be
-null. If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_float {
- my($self,$field)=@_ ;
- ($self->getfield($field) =~ /^(\d+\.\d+)$/ ||
- $self->getfield($field) =~ /^(\d+)$/ ||
- $self->getfield($field) =~ /^(\d+\.\d+e\d+)$/ ||
- $self->getfield($field) =~ /^(\d+e\d+)$/)
- or return "Illegal or empty (float) $field!";
- $self->setfield($field,$1);
- '';
-}
-
-=item ut_number COLUMN
-
-Check/untaint simple numeric data (whole numbers). May not be null. If there
-is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_number {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^(\d+)$/
- or return "Illegal or empty (numeric) $field!";
- $self->setfield($field,$1);
- '';
-}
-
-=item ut_numbern COLUMN
-
-Check/untaint simple numeric data (whole numbers). May be null. If there is
-an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_numbern {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^(\d*)$/
- or return "Illegal (numeric) $field!";
- $self->setfield($field,$1);
- '';
-}
-
-=item ut_money COLUMN
-
-Check/untaint monetary numbers. May be negative. Set to 0 if null. If there
-is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_money {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^(\-)? ?(\d*)(\.\d{2})?$/
- or return "Illegal (money) $field!";
- $self->setfield($field,"$1$2$3" || 0);
- '';
-}
-
-=item ut_text COLUMN
-
-Check/untaint text. Alphanumerics, spaces, and the following punctuation
-symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? /
-May not be null. If there is an error, returns the error, otherwise returns
-false.
-
-=cut
-
-sub ut_text {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/]+)$/
- or return "Illegal or empty (text) $field";
- $self->setfield($field,$1);
- '';
-}
-
-=item ut_textn COLUMN
-
-Check/untaint text. Alphanumerics, spaces, and the following punctuation
-symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? /
-May be null. If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_textn {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/]*)$/
- or return "Illegal (text) $field";
- $self->setfield($field,$1);
- '';
-}
-
-=item ut_alpha COLUMN
-
-Check/untaint alphanumeric strings (no spaces). May not be null. If there is
-an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_alpha {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^(\w+)$/
- or return "Illegal or empty (alphanumeric) $field!";
- $self->setfield($field,$1);
- '';
-}
-
-=item ut_alpha COLUMN
-
-Check/untaint alphanumeric strings (no spaces). May be null. If there is an
-error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_alphan {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^(\w*)$/
- or return "Illegal (alphanumeric) $field!";
- $self->setfield($field,$1);
- '';
-}
-
-=item ut_phonen COLUMN
-
-Check/untaint phone numbers. May be null. If there is an error, returns
-the error, otherwise returns false.
-
-=cut
-
-sub ut_phonen {
- my($self,$field)=@_;
- my $phonen = $self->getfield($field);
- if ( $phonen eq '' ) {
- $self->setfield($field,'');
- } else {
- $phonen =~ s/\D//g;
- $phonen =~ /^(\d{3})(\d{3})(\d{4})(\d*)$/
- or return "Illegal (phone) $field!";
- $phonen = "$1-$2-$3";
- $phonen .= " x$4" if $4;
- $self->setfield($field,$phonen);
- }
- '';
-}
-
-=item ut_anything COLUMN
-
-Untaints arbitrary data. Be careful.
-
-=cut
-
-sub ut_anything {
- my($self,$field)=@_;
- $self->getfield($field) =~ /^(.*)$/ or return "Illegal $field!";
- $self->setfield($field,$1);
- '';
-}
-
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item reload_dbdef([FILENAME])
-
-Load a database definition (see L<FS::dbdef>), optionally from a non-default
-filename. This command is executed at startup unless
-I<$FS::Record::setup_hack> is true. Returns a FS::dbdef object.
-
-=cut
-
-sub reload_dbdef {
- my $file = shift || $dbdef_file;
- $dbdef = load FS::dbdef ($file);
-}
-
-=item dbdef
-
-Returns the current database definition. See L<FS::dbdef>.
-
-=cut
-
-sub dbdef { $dbdef; }
-
-=item _quote VALUE, TABLE, COLUMN
-
-This is an internal function used to construct SQL statements. It returns
-VALUE DBI-quoted (see L<DBI/"quote">) unless VALUE is a number and the column
-type (see L<dbdef_column>) does not end in `char' or `binary'.
-
-=cut
-
-sub _quote {
- my($value,$table,$field)=@_;
- my($dbh)=dbh;
- if ( $value =~ /^\d+(\.\d+)?$/ &&
-# ! ( datatype($table,$field) =~ /^char/ )
- ! ( $dbdef->table($table)->column($field)->type =~ /(char|binary)$/i )
- ) {
- $value;
- } else {
- $dbh->quote($value);
- }
-}
-
-=item hfields TABLE
-
-This is depriciated. Don't use it.
-
-It returns a hash-type list with the fields of this record's table set true.
-
-=cut
-
-sub hfields {
- carp "hfields is depriciated";
- my($table)=@_;
- my(%hash);
- foreach (fields($table)) {
- $hash{$_}=1;
- }
- \%hash;
-}
-
-=item fields TABLE
-
-This returns a list of the columns in this record's table
-(See L<dbdef_table>).
-
-=cut
-
-# Usage: @fields = fields($table);
-sub fields {
- my($table) = @_;
- #my(@fields) = $dbdef->table($table)->columns;
- croak "Usage: \@fields = fields(\$table)" unless $table;
- my($table_obj) = $dbdef->table($table);
- croak "Unknown table $table" unless $table_obj;
- $table_obj->columns;
-}
-
-#sub _dump {
-# my($self)=@_;
-# join("\n", map {
-# "$_: ". $self->getfield($_). "|"
-# } (fields($self->table)) );
-#}
-
-#sub DESTROY {
-# my $self = shift;
-# #use Carp qw(cluck);
-# #cluck "DESTROYING $self";
-# warn "DESTROYING $self";
-#}
-
-#sub is_tainted {
-# return ! eval { join('',@_), kill 0; 1; };
-# }
-
-=back
-
-=head1 BUGS
-
-This module should probably be renamed, since much of the functionality is
-of general use. It is not completely unlike Adapter::DBI (see below).
-
-Exported qsearch and qsearchs should be depriciated in favor of method calls
-(against an FS::Record object like the old search and searchs that qsearch
-and qsearchs were on top of.)
-
-The whole fields / hfields mess should be removed.
-
-The various WHERE clauses should be subroutined.
-
-table string should be depriciated in favor of FS::dbdef_table.
-
-No doubt we could benefit from a Tied hash. Documenting how exists / defined
-true maps to the database (and WHERE clauses) would also help.
-
-The ut_ methods should ask the dbdef for a default length.
-
-ut_sqltype (like ut_varchar) should all be defined
-
-A fallback check method should be provided with uses the dbdef.
-
-The ut_money method assumes money has two decimal digits.
-
-The Pg money kludge in the new method only strips `$'.
-
-The ut_phonen method assumes US-style phone numbers.
-
-The _quote function should probably use ut_float instead of a regex.
-
-All the subroutines probably should be methods, here or elsewhere.
-
-=head1 SEE ALSO
-
-L<FS::dbdef>, L<FS::UID>, L<DBI>
-
-Adapter::DBI from Ch. 11 of Advanced Perl Programming by Sriram Srinivasan.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jun-2 - 9, 19, 25, 27, 30
-
-DBI version
-ivan@sisd.com 97-nov-8 - 12
-
-cleaned up, added autoloaded $self->any_field calls, moved DBI login stuff
-to FS::UID
-ivan@sisd.com 97-nov-21-23
-
-since AUTO_INCREMENT is MySQL specific, use my own unique number generator
-(again)
-ivan@sisd.com 97-dec-4
-
-untaint $user in unique (web demo hack...bah)
-make unique skip multiple-field unique's from dbdef
-ivan@sisd.com 97-dec-11
-
-merge with FS::Search, which after all was just alternate constructors for
-FS::Record objects. Makes lots of things cleaner. :)
-ivan@sisd.com 97-dec-13
-
-use FS::dbdef::primary key in replace searches, hopefully for all practical
-purposes the string/number problem in SQL statements should be gone?
-(SQL bites)
-ivan@sisd.com 98-jan-20
-
-Put all SQL statments in $statment before we $sth=$dbh->prepare( them,
-for debugging reasons (warn $statement) ivan@sisd.com 98-feb-19
-
-(sigh)... use dbdef type (char, etc.) instead of a regex to decide
-what to quote in _quote (more sillines...) SQL bites.
-ivan@sisd.com 98-feb-20
-
-more friendly error messages ivan@sisd.com 98-mar-13
-
-Added import of datasrc from FS::UID to allow Pg6.3 to work
-Added code to right-trim strings read from Pg6.3 databases
-Modified 'add' to only insert fields that actually have data
-Added ut_float to handle floating point numbers (for sales tax).
-Pg6.3 does not have a "SHOW FIELDS" statement, so I faked it 8).
- bmccane@maxbaud.net 98-apr-3
-
-commented out Pg wrapper around `` Modified 'add' to only insert fields that
-actually have data '' ivan@sisd.com 98-apr-16
-
-dbdef usage changes ivan@sisd.com 98-jun-1
-
-sub fields now asks dbdef, not database ivan@sisd.com 98-jun-2
-
-added debugging method ->_dump ivan@sisd.com 98-jun-16
-
-use FS::dbdef::primary key in delete searches as well as replace
-searches (SQL still bites) ivan@sisd.com 98-jun-22
-
-sub dbdef_table ivan@sisd.com 98-jun-28
-
-removed Pg wrapper around `` Modified 'add' to only insert fields that
-actually have data '' ivan@sisd.com 98-jul-14
-
-sub fields croaks on errors ivan@sisd.com 98-jul-17
-
-$rc eq '0E0' doesn't mean we couldn't delete for all rdbmss
-ivan@sisd.com 98-jul-18
-
-commented out code to right-trim strings read from Pg6.3 databases;
-ChopBlanks is in UID.pm ivan@sisd.com 98-aug-16
-
-added code (with Pg wrapper) to deal with Pg money fields
-ivan@sisd.com 98-aug-18
-
-added pod documentation ivan@sisd.com 98-sep-6
-
-ut_phonen got ''; at the end ivan@sisd.com 98-sep-27
-
-=cut
-
-1;
-
diff --git a/site_perl/SSH.pm b/site_perl/SSH.pm
deleted file mode 100644
index d5a0df6..0000000
--- a/site_perl/SSH.pm
+++ /dev/null
@@ -1,157 +0,0 @@
-package FS::SSH;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK $ssh $scp);
-use Exporter;
-use IPC::Open2;
-use IPC::Open3;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(ssh scp issh iscp sshopen2 sshopen3);
-
-$ssh="ssh";
-$scp="scp";
-
-=head1 NAME
-
-FS::SSH - Subroutines to call ssh and scp
-
-=head1 SYNOPSIS
-
- use FS::SSH qw(ssh scp issh iscp sshopen2 sshopen3);
-
- ssh($host, $command);
-
- issh($host, $command);
-
- scp($source, $destination);
-
- iscp($source, $destination);
-
- sshopen2($host, $reader, $writer, $command);
-
- sshopen3($host, $reader, $writer, $error, $command);
-
-=head1 DESCRIPTION
-
- Simple wrappers around ssh and scp commands.
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item ssh HOST, COMMAND
-
-Calls ssh in batch mode.
-
-=cut
-
-sub ssh {
- my($host,$command)=@_;
- my(@cmd)=($ssh, "-o", "BatchMode yes", $host, $command);
-# print join(' ',@cmd),"\n";
-#0;
- system(@cmd);
-}
-
-=item issh HOST, COMMAND
-
-Prints the ssh command to be executed, waits for the user to confirm, and
-(optionally) executes the command.
-
-=cut
-
-sub issh {
- my($host,$command)=@_;
- my(@cmd)=($ssh, $host, $command);
- print join(' ',@cmd),"\n";
- if ( &_yesno ) {
- ###print join(' ',@cmd),"\n";
- system(@cmd);
- }
-}
-
-=item scp SOURCE, DESTINATION
-
-Calls scp in batch mode.
-
-=cut
-
-sub scp {
- my($src,$dest)=@_;
- my(@cmd)=($scp,"-Bprq",$src,$dest);
-# print join(' ',@cmd),"\n";
-#0;
- system(@cmd);
-}
-
-=item iscp SOURCE, DESTINATION
-
-Prints the scp command to be executed, waits for the user to confirm, and
-(optionally) executes the command.
-
-=cut
-
-sub iscp {
- my($src,$dest)=@_;
- my(@cmd)=($scp,"-pr",$src,$dest);
- print join(' ',@cmd),"\n";
- if ( &_yesno ) {
- ###print join(' ',@cmd),"\n";
- system(@cmd);
- }
-}
-
-=item sshopen2 HOST, READER, WRITER, COMMAND
-
-Connects the supplied filehandles to the ssh process (in batch mode).
-
-=cut
-
-sub sshopen2 {
- my($host,$reader,$writer,$command)=@_;
- open2($reader,$writer,$ssh,'-o','Batchmode yes',$host,$command);
-}
-
-=item sshopen3 HOST, WRITER, READER, ERROR, COMMAND
-
-Connects the supplied filehandles to the ssh process (in batch mode).
-
-=cut
-
-sub sshopen3 {
- my($host,$writer,$reader,$error,$command)=@_;
- open3($writer,$reader,$error,$ssh,'-o','Batchmode yes',$host,$command);
-}
-
-sub _yesno {
- print "Proceed [y/N]:";
- my($x)=scalar(<STDIN>);
- $x =~ /^y/i;
-}
-
-=head1 BUGS
-
-Not OO.
-
-scp stuff should transparantly use rsync-over-ssh instead.
-
-=head1 SEE ALSO
-
-L<ssh>, L<scp>, L<IPC::Open2>, L<IPC::Open3>
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-17
-
-added sshopen2 and sshopen3 ivan@sisd.com 98-mar-9
-
-added iscp ivan@sisd.com 98-jul-25
-now iscp asks y/n, issh and took out path ivan@sisd.com 98-jul-30
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/UID.pm b/site_perl/UID.pm
deleted file mode 100644
index 16f03a0..0000000
--- a/site_perl/UID.pm
+++ /dev/null
@@ -1,209 +0,0 @@
-package FS::UID;
-
-use strict;
-use vars qw(
- @ISA @EXPORT_OK $cgi $dbh $freeside_uid $conf $datasrc $db_user $db_pass
-);
-use Exporter;
-use Carp;
-use DBI;
-use FS::Conf;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(checkeuid checkruid swapuid cgisuidsetup
- adminsuidsetup getotaker dbh datasrc);
-
-$freeside_uid = scalar(getpwnam('freeside'));
-
-my $conf = new FS::Conf;
-($datasrc, $db_user, $db_pass) = $conf->config('secrets')
- or die "Can't get secrets: $!";
-
-=head1 NAME
-
-FS::UID - Subroutines for database login and assorted other stuff
-
-=head1 SYNOPSIS
-
- use FS::UID qw(adminsuidsetup cgisuidsetup dbh datasrc getotaker
- checkeuid checkruid swapuid);
-
- adminsuidsetup;
-
- $cgi = new CGI::Base;
- $cgi->get;
- $dbh = cgisuidsetup($cgi);
-
- $dbh = dbh;
-
- $datasrc = datasrc;
-
-=head1 DESCRIPTION
-
-Provides a hodgepodge of subroutines.
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item adminsuidsetup
-
-Cleans the environment.
-Make sure the script is running as freeside, or setuid freeside.
-Opens a connection to the database.
-Swaps real and effective UIDs.
-Returns the DBI database handle (usually you don't need this).
-
-=cut
-
-sub adminsuidsetup {
-
- $ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin';
- $ENV{'SHELL'} = '/bin/sh';
- $ENV{'IFS'} = " \t\n";
- $ENV{'CDPATH'} = '';
- $ENV{'ENV'} = '';
- $ENV{'BASH_ENV'} = '';
-
- croak "Not running uid freeside!" unless checkeuid();
- $dbh = DBI->connect($datasrc,$db_user,$db_pass, {
- # hack for web demo
- # my($user)=getotaker();
- # $dbh = DBI->connect("$datasrc:$user",$db_user,$db_pass, {
- 'AutoCommit' => 'true',
- 'ChopBlanks' => 'true',
- } ) or die "DBI->connect error: $DBI::errstr\n";;
-
- swapuid(); #go to non-privledged user if running setuid freeside
-
- $dbh;
-}
-=item cgisuidsetup CGI::Base_OBJECT
-
-Stores the CGI::Base_OBJECT for later use.
-Runs adminsuidsetup.
-
-=cut
-
-sub cgisuidsetup {
- $cgi=$_[0];
- adminsuidsetup;
-}
-
-=item dbh
-
-Returns the DBI database handle.
-
-=cut
-
-sub dbh {
- $dbh;
-}
-
-=item datasrc
-
-Returns the DBI data source.
-
-=cut
-
-sub datasrc {
- $datasrc;
-}
-
-#hack for web demo
-#sub setdbh {
-# $dbh=$_[0];
-#}
-
-sub suidsetup {
- croak "suidsetup depriciated";
-}
-
-=item getotaker
-
-Returns the current Freeside user. Currently that means the CGI REMOTE_USER,
-or 'freeside'.
-
-=cut
-
-sub getotaker {
- if ($cgi && defined $cgi->var('REMOTE_USER')) {
- return $cgi->var('REMOTE_USER'); #for now
- } else {
- 'freeside';
- }
-}
-
-=item checkeuid
-
-Returns true if effective UID is that of the freeside user.
-
-=cut
-
-sub checkeuid {
- ( $> == $freeside_uid );
-}
-
-=item checkruid
-
-Returns true if the real UID is that of the freeside user.
-
-=cut
-
-sub checkruid {
- ( $< == $freeside_uid );
-}
-
-=item swapuid
-
-Swaps real and effective UIDs.
-
-=cut
-
-sub swapuid {
- ($<,$>) = ($>,$<);
-}
-
-=back
-
-=head1 BUGS
-
-Not OO.
-
-No capabilities yet. When mod_perl and Authen::DBI are implemented,
-cgisuidsetup will go away as well.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<CGI::Base>, L<DBI>
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jun-4 - 9
-
-untaint otaker ivan@voicenet.com 97-jul-7
-
-generalize and auto-get uid (getotaker still needs to be db'ed)
-ivan@sisd.com 97-nov-10
-
-&cgisuidsetup logs into database. other cleaning.
-ivan@sisd.com 97-nov-22,23
-
-&adminsuidsetup logs into database with otaker='freeside' (for
-automated tasks like billing)
-ivan@sisd.com 97-dec-13
-
-added sub datasrc for fs-setup ivan@sisd.com 98-feb-21
-
-datasrc, user and pass now come from conf/secrets ivan@sisd.com 98-jun-28
-
-added ChopBlanks to DBI call (see man DBI) ivan@sisd.com 98-aug-16
-
-pod, use FS::Conf, implemented cgisuidsetup as adminsuidsetup,
-inlined suidsetup
-ivan@sisd.com 98-sep-12
-
-=cut
-
-1;
-
diff --git a/site_perl/agent.pm b/site_perl/agent.pm
deleted file mode 100644
index 7fc370e..0000000
--- a/site_perl/agent.pm
+++ /dev/null
@@ -1,166 +0,0 @@
-package FS::agent;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields qsearch qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::agent - Object methods for agent records
-
-=head1 SYNOPSIS
-
- use FS::agent;
-
- $record = create FS::agent \%hash;
- $record = create FS::agent { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::agent object represents an agent. Every customer has an agent. Agents
-can be used to track things like resellers or salespeople. FS::agent inherits
-from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item agemtnum - primary key (assigned automatically for new agents)
-
-=item agent - Text name of this agent
-
-=item typenum - Agent type. See L<FS::agent_type>
-
-=item prog - For future use.
-
-=item freq - For future use.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new agent. To add the agent to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('agent')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('agent',$hashref);
-}
-
-=item insert
-
-Adds this agent to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Deletes this agent from the database. Only agents with no customers can be
-deleted. If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub delete {
- my($self)=@_;
- return "Can't delete an agent with customers!"
- if qsearch('cust_main',{'agentnum' => $self->agentnum});
- $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not an agent record!" unless $old->table eq "agent";
- return "Can't change agentnum!"
- unless $old->getfield('agentnum') eq $new->getfield('agentnum');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid agent. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a agent record!" unless $self->table eq "agent";
-
- my($error)=
- $self->ut_numbern('agentnum')
- or $self->ut_text('agent')
- or $self->ut_number('typenum')
- or $self->ut_numbern('freq')
- or $self->ut_textn('prog')
- ;
- return $error if $error;
-
- return "Unknown typenum!"
- unless qsearchs('agent_type',{'typenum'=> $self->getfield('typenum') });
-
- '';
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::agent_type>, L<FS::cust_main>, schema.html from the base
-documentation.
-
-=head1 HISTORY
-
-Class dealing with agent (resellers)
-
-ivan@sisd.com 97-nov-13, 97-dec-10
-
-pod, added check in ->delete ivan@sisd.com 98-sep-22
-
-=cut
-
-1;
-
diff --git a/site_perl/agent_type.pm b/site_perl/agent_type.pm
deleted file mode 100644
index 002c36f..0000000
--- a/site_perl/agent_type.pm
+++ /dev/null
@@ -1,161 +0,0 @@
-package FS::agent_type;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(qsearch fields);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::agent_type - Object methods for agent_type records
-
-=head1 SYNOPSIS
-
- use FS::agent_type;
-
- $record = create FS::agent_type \%hash;
- $record = create FS::agent_type { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::agent_type object represents an agent type. Every agent (see
-L<FS::agent>) has an agent type. Agent types define which packages (see
-L<FS::part_pkg>) may be purchased by customers (see L<FS::cust_main>), via
-FS::type_pkgs records (see L<FS::type_pkgs>). FS::agent_type inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item typenum - primary key (assigned automatically for new agent types)
-
-=item atype - Text name of this agent type
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new agent type. To add the agent type to the database, see
-L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('agent_type')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('agent_type',$hashref);
-
-}
-
-=item insert
-
-Adds this agent type to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Deletes this agent type from the database. Only agent types with no agents
-can be deleted. If there is an error, returns the error, otherwise returns
-false.
-
-=cut
-
-sub delete {
- my($self)=@_;
- return "Can't delete an agent_type with agents!"
- if qsearch('agent',{'typenum' => $self->typenum});
- $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a agent_type record!" unless $old->table eq "agent_type";
- return "Can't change typenum!"
- unless $old->getfield('typenum') eq $new->getfield('typenum');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid agent type. If there is an
-error, returns the error, otherwise returns false. Called by the insert and
-replace methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a agent_type record!" unless $self->table eq "agent_type";
-
- $self->ut_numbern('typenum')
- or $self->ut_text('atype');
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::agent>, L<FS::type_pkgs>, L<FS::cust_main>,
-L<FS::part_pkg>, schema.html from the base documentation.
-
-=head1 HISTORY
-
-Class for the different sets of allowable packages you can assign to an
-agent.
-
-ivan@sisd.com 97-nov-13
-
-ut_ FS::Record methods
-ivan@sisd.com 97-dec-10
-
-Changed 'type' to 'atype' because Pg6.3 reserves the type word
- bmccane@maxbaud.net 98-apr-3
-
-pod, added check in delete ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_bill.pm b/site_perl/cust_bill.pm
deleted file mode 100644
index 0023451..0000000
--- a/site_perl/cust_bill.pm
+++ /dev/null
@@ -1,495 +0,0 @@
-package FS::cust_bill;
-
-use strict;
-use vars qw(@ISA $conf $add1 $add2 $add3 $add4);
-use Exporter;
-use Date::Format;
-use FS::Record qw(fields qsearch qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-
-$conf = new FS::Conf;
-
-($add1,$add2,$add3,$add4) = $conf->config('address');
-
-=head1 NAME
-
-FS::cust_bill - Object methods for cust_bill records
-
-=head1 SYNOPSIS
-
- use FS::cust_bill;
-
- $record = create FS::cust_bill \%hash;
- $record = create FS::cust_bill { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- ( $total_previous_balance, @previous_cust_bill ) = $record->previous;
-
- @cust_bill_pkg_objects = $cust_bill->cust_bill_pkg;
-
- ( $total_previous_credits, @previous_cust_credit ) = $record->cust_credit;
-
- @cust_pay_objects = $cust_bill->cust_pay;
-
- @lines = $cust_bill->print_text;
- @lines = $cust_bill->print_text $time;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill object represents an invoice. FS::cust_bill inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item invnum - primary key (assigned automatically for new invoices)
-
-=item custnum - customer (see L<FS::cust_main>)
-
-=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=item charged - amount of this invoice
-
-=item owed - amount still outstanding on this invoice, which is charged minus
-all payments (see L<FS::cust_pay>).
-
-=item printed - how many times this invoice has been printed automatically
-(see L<FS::cust_main/"collect">).
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new invoice. To add the invoice to the database, see L<"insert">.
-Invoices are normally created by calling the bill method of a customer object
-(see L<FS::cust_main>).
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_bill')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_bill',$hashref);
-}
-
-=item insert
-
-Adds this invoice to the database ("Posts" the invoice). If there is an error,
-returns the error, otherwise returns false.
-
-When adding new invoices, owed must be charged (or null, in which case it is
-automatically set to charged).
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->setfield('owed',$self->charged) if $self->owed eq '';
- return "owed != charged!"
- unless $self->owed == $self->charged;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented. I don't remove invoices because there would then be
-no record you ever posted this invoice (which is bad, no?)
-
-=cut
-
-sub delete {
- return "Can't remove invoice!"
- #my($self)=@_;
- #$self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-Only owed and printed may be changed. Owed is normally updated by creating and
-inserting a payment (see L<FS::cust_pay>). Printed is normally updated by
-calling the collect method of a customer object (see L<FS::cust_main>).
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_bill record!" unless $old->table eq "cust_bill";
- return "Can't change invnum!"
- unless $old->getfield('invnum') eq $new->getfield('invnum');
- return "Can't change custnum!"
- unless $old->getfield('custnum') eq $new->getfield('custnum');
- return "Can't change _date!"
- unless $old->getfield('_date') eq $new->getfield('_date');
- return "Can't change charged!"
- unless $old->getfield('charged') eq $new->getfield('charged');
- return "(New) owed can't be > (new) charged!"
- if $new->getfield('owed') > $new->getfield('charged');
-
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid invoice. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_bill record!" unless $self->table eq "cust_bill";
- my($recref) = $self->hashref;
-
- $recref->{invnum} =~ /^(\d*)$/ or return "Illegal invnum";
- $recref->{invnum} = $1;
-
- $recref->{custnum} =~ /^(\d+)$/ or return "Illegal custnum";
- $recref->{custnum} = $1;
- return "Unknown customer"
- unless qsearchs('cust_main',{'custnum'=>$recref->{custnum}});
-
- $recref->{_date} =~ /^(\d*)$/ or return "Illegal date";
- $recref->{_date} = $recref->{_date} ? $1 : time;
-
- #$recref->{charged} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal charged";
- $recref->{charged} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal charged";
- $recref->{charged} = $1;
-
- $recref->{owed} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal owed";
- $recref->{owed} = $1;
-
- $recref->{printed} =~ /^(\d*)$/ or return "Illegal printed";
- $recref->{printed} = $1 || '0';
-
- ''; #no error
-}
-
-=item previous
-
-Returns a list consisting of the total previous balance for this customer,
-followed by the previous outstanding invoices (as FS::cust_bill objects also).
-
-=cut
-
-sub previous {
- my($self)=@_;
- my($total)=0;
- my(@cust_bill) = sort { $a->_date <=> $b->_date }
- grep { $_->owed != 0 && $_->_date < $self->_date }
- qsearch('cust_bill',{ 'custnum' => $self->custnum } )
- ;
- foreach (@cust_bill) { $total += $_->owed; }
- $total, @cust_bill;
-}
-
-=item cust_bill_pkg
-
-Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
-
-=cut
-
-sub cust_bill_pkg {
- my($self)=@_;
- qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
-}
-
-=item cust_credit
-
-Returns a list consisting of the total previous credited (see
-L<FS::cust_credit>) for this customer, followed by the previous outstanding
-credits (FS::cust_credit objects).
-
-=cut
-
-sub cust_credit {
- my($self)=@_;
- my($total)=0;
- my(@cust_credit) = sort { $a->_date <=> $b->date }
- grep { $_->credited != 0 && $_->_date < $self->_date }
- qsearch('cust_credit', { 'custnum' => $self->custnum } )
- ;
- foreach (@cust_credit) { $total += $_->credited; }
- $total, @cust_credit;
-}
-
-=item cust_pay
-
-Returns all payments (see L<FS::cust_pay>) for this invoice.
-
-=cut
-
-sub cust_pay {
- my($self)=@_;
- sort { $a->_date <=> $b->date }
- qsearch( 'cust_pay', { 'invnum' => $self->invnum } )
- ;
-}
-
-=item print_text [TIME];
-
-Returns an ASCII invoice, as a list of lines.
-
-TIME an optional value used to control the printing of overdue messages. The
-default is now. It isn't the date of the invoice; that's the `_date' field.
-It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=cut
-
-sub print_text {
-
- my($self,$today)=@_;
- $today ||= time;
- my($invnum)=$self->invnum;
- my($cust_main) = qsearchs('cust_main',
- { 'custnum', $self->custnum } );
- $cust_main->setfield('payname',
- $cust_main->first. ' '. $cust_main->getfield('last')
- ) unless $cust_main->payname;
-
- my($pr_total,@pr_cust_bill) = $self->previous; #previous balance
- my($cr_total,@cr_cust_credit) = $self->cust_credit; #credits
- my($balance_due) = $self->owed + $pr_total - $cr_total;
-
- #overdue?
- my($overdue) = (
- $balance_due > 0
- && $today > $self->_date
- && $self->printed > 1
- );
-
- #printing bits here
-
- local($SIG{CHLD}) = sub { wait() };
- $|=1;
- my($pid)=open(CHILD,"-|");
- die "Can't fork: $!" unless defined($pid);
-
- if ($pid) { #parent
- my(@collect)=<CHILD>;
- close CHILD;
- return @collect;
- } else { #child
-
- my($description,$amount);
- my(@buf);
-
- #define format stuff
- $%=0;
- $= = 35;
- local($^L) = <<END;
-
-
-
-
-
-
-
-END
-
- #format address
- my($l,@address)=(0,'','','','','');
- $address[$l++]=$cust_main->company if $cust_main->company;
- $address[$l++]=$cust_main->address1;
- $address[$l++]=$cust_main->address2 if $cust_main->address2;
- $address[$l++]=$cust_main->city. ", ". $cust_main->state. " ".
- $cust_main->zip;
- $address[$l++]=$cust_main->country unless $cust_main->country eq 'US';
-
- #previous balance
- foreach ( @pr_cust_bill ) {
- push @buf, (
- "Previous Balance, Invoice #". $_->invnum.
- " (". time2str("%x",$_->_date). ")",
- '$'. sprintf("%10.2f",$_->owed)
- );
- }
- if (@pr_cust_bill) {
- push @buf,('','-----------');
- push @buf,('Total Previous Balance','$' . sprintf("%10.2f",$pr_total ) );
- push @buf,('','');
- }
-
- #new charges
- foreach ( $self->cust_bill_pkg ) {
-
- if ( $_->pkgnum ) {
-
- my($cust_pkg)=qsearchs('cust_pkg', { 'pkgnum', $_->pkgnum } );
- my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->pkgpart});
- my($pkg)=$part_pkg->pkg;
-
- push @buf, ( "$pkg Setup",'$' . sprintf("%10.2f",$_->setup) )
- if $_->setup != 0;
- push @buf, (
- "$pkg (" . time2str("%x",$_->sdate) . " - " .
- time2str("%x",$_->edate) . ")",
- '$' . sprintf("%10.2f",$_->recur)
- ) if $_->recur != 0;
-
- } else { #pkgnum Tax
- push @buf,("Tax",'$' . sprintf("%10.2f",$_->setup) )
- if $_->setup != 0;
- }
- }
-
- push @buf,('','-----------');
- push @buf,('Total New Charges',
- '$' . sprintf("%10.2f",$self->charged) );
- push @buf,('','');
-
- push @buf,('','-----------');
- push @buf,('Total Charges',
- '$' . sprintf("%10.2f",$self->charged + $pr_total) );
- push @buf,('','');
-
- #credits
- foreach ( @cr_cust_credit ) {
- push @buf,(
- "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
- '$' . sprintf("%10.2f",$_->credited)
- );
- }
-
- #get & print payments
- foreach ( $self->cust_pay ) {
- push @buf,(
- "Payment received ". time2str("%x",$_->_date ),
- '$' . sprintf("%10.2f",$_->paid )
- );
- }
-
- #balance due
- push @buf,('','-----------');
- push @buf,('Balance Due','$' .
- sprintf("%10.2f",$self->owed + $pr_total - $cr_total ) );
-
- #now print
-
- my($tot_pages)=int(scalar(@buf)/30); #15 lines, 2 values per line
- $tot_pages++ if scalar(@buf) % 30;
-
- while (@buf) {
- $description=shift(@buf);
- $amount=shift(@buf);
- write;
- }
- ($description,$amount)=('','');
- write while ( $- );
- print $^L;
-
- exit; #kid
-
- format STDOUT_TOP =
-
- @|||||||||||||||||||
- "Invoice"
- @||||||||||||||||||| @<<<<<<< @<<<<<<<<<<<
-{
- ( $tot_pages != 1 ) ? "Page $% of $tot_pages" : '',
- time2str("%x",( $self->_date )), "FS-$invnum"
-}
-
-
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add1
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add2
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add3
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add4
-
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-{ $cust_main->payname,
- ( ( $cust_main->payby eq 'BILL' ) && $cust_main->payinfo )
- ? "P.O. #". $cust_main->payinfo : ''
-}
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[0],''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[1],$overdue ? "* This invoice is now PAST DUE! *" : ''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[2],$overdue ? " Please forward payment promptly " : ''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[3],$overdue ? "to avoid interruption of service." : ''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[4],''
-
-
-
-.
-
- format STDOUT =
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<
- $description,$amount
-.
-
- } #endchild
-
-}
-
-=back
-
-=head1 BUGS
-
-The delete method.
-
-It doesn't properly override FS::Record yet.
-
-print_text formatting (and some logic :/) is in source as a format declaration,
-which needs to be slurped in from a file. the fork is rather kludgy as well.
-It could be cleaned with swrite from man perlform, and the picture could be
-put in a /var/spool/freeside/conf file. Also number of lines ($=).
-
-missing print_ps for a nice postscript copy (maybe HylaFAX-cover-page-style
-or something similar so the look can be completely customized?)
-
-There is an off-by-one error in print_text which causes a visual error: "Page 1
-of 2" printed on some single-page invoices?
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_main>, L<FS::cust_pay>, L<FS::cust_bill_pkg>,
-L<FS::cust_credit>, schema.html from the base documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-1
-
-small fix for new API ivan@sisd.com 98-mar-14
-
-charges can be negative ivan@sisd.com 98-jul-13
-
-pod, ingegrate with FS::Invoice ivan@sisd.com 98-sep-20
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_bill_pkg.pm b/site_perl/cust_bill_pkg.pm
deleted file mode 100644
index e41d7c1..0000000
--- a/site_perl/cust_bill_pkg.pm
+++ /dev/null
@@ -1,177 +0,0 @@
-package FS::cust_bill_pkg;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::cust_bill_pkg - Object methods for cust_bill_pkg records
-
-=head1 SYNOPSIS
-
- use FS::cust_bill_pkg;
-
- $record = create FS::cust_bill_pkg \%hash;
- $record = create FS::cust_bill_pkg { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_pkg object represents an invoice line item.
-FS::cust_bill_pkg inherits from FS::Record. The following fields are currently
-supported:
-
-=over 4
-
-=item invnum - invoice (see L<FS::cust_bill>)
-
-=item pkgnum - package (see L<FS::cust_pkg>)
-
-=item setup - setup fee
-
-=item recur - recurring fee
-
-=item sdate - starting date of recurring fee
-
-=item edate - ending date of recurring fee
-
-=back
-
-sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">. Also
-see L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new line item. To add the line item to the database, see
-L<"insert">. Line items are normally created by calling the bill method of a
-customer object (see L<FS::cust_main>).
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_bill_pkg')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_bill_pkg',$hashref);
-
-}
-
-=item insert
-
-Adds this line item to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented. I don't remove line items because there would then be
-no record the items ever existed (which is bad, no?)
-
-=cut
-
-sub delete {
- return "Can't delete cust_bill_pkg records!";
- #my($self)=@_;
- #$self->del;
-}
-
-=item replace OLD_RECORD
-
-Currently unimplemented. This would be even more of an accounting nightmare
-than deleteing the items. Just don't do it.
-
-=cut
-
-sub replace {
- return "Can't modify cust_bill_pkg records!";
- #my($new,$old)=@_;
- #return "(Old) Not a cust_bill_pkg record!"
- # unless $old->table eq "cust_bill_pkg";
- #
- #$new->check or
- #$new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid line item. If there is an
-error, returns the error, otherwise returns false. Called by the insert
-method.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_bill_pkg record!" unless $self->table eq "cust_bill_pkg";
-
- my($error)=
- $self->ut_number('pkgnum')
- or $self->ut_number('invnum')
- or $self->ut_money('setup')
- or $self->ut_money('recur')
- or $self->ut_numbern('sdate')
- or $self->ut_numbern('edate')
- ;
- return $error if $error;
-
- if ( $self->pkgnum != 0 ) { #allow unchecked pkgnum 0 for tax! (add to part_pkg?)
- return "Unknown pkgnum ".$self->pkgnum
- unless qsearchs('cust_pkg',{'pkgnum'=> $self->pkgnum });
- }
-
- return "Unknown invnum"
- unless qsearchs('cust_bill',{'invnum'=> $self->invnum });
-
- ''; #no error
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_bill>, L<FS::cust_pkg>, L<FS::cust_main>, schema.html
-from the base documentation.
-
-=head1 HISTORY
-
-ivan@sisd.com 98-mar-13
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_credit.pm b/site_perl/cust_credit.pm
deleted file mode 100644
index b1a5e16..0000000
--- a/site_perl/cust_credit.pm
+++ /dev/null
@@ -1,199 +0,0 @@
-package FS::cust_credit;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::UID qw(getotaker);
-use FS::Record qw(fields qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::cust_credit - Object methods for cust_credit records
-
-=head1 SYNOPSIS
-
- use FS::cust_credit;
-
- $record = create FS::cust_credit \%hash;
- $record = create FS::cust_credit { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_credit object represents a credit. FS::cust_credit inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item crednum - primary key (assigned automatically for new credits)
-
-=item custnum - customer (see L<FS::cust_main>)
-
-=item amount - amount of the credit
-
-=item credited - how much of this credit that is still outstanding, which is
-amount minus all refunds (see L<FS::cust_refund>).
-
-=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=item otaker - order taker (assigned automatically, see L<FS::UID>)
-
-=item reason - text
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new credit. To add the credit to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_credit')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_credit',$hashref);
-}
-
-=item insert
-
-Adds this credit to the database ("Posts" the credit). If there is an error,
-returns the error, otherwise returns false.
-
-When adding new invoices, credited must be amount (or null, in which case it is
-automatically set to amount).
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->setfield('credited',$self->amount) if $self->credited eq '';
- return "credited != amount!"
- unless $self->credited == $self->amount;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented.
-
-=cut
-
-sub delete {
- return "Can't remove credit!"
- #my($self)=@_;
- #$self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-Only credited may be changed. Credited is normally updated by creating and
-inserting a refund (see L<FS::cust_refund>).
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_credit record!" unless $old->table eq "cust_credit";
- return "Can't change crednum!"
- unless $old->getfield('crednum') eq $new->getfield('crednum');
- return "Can't change custnum!"
- unless $old->getfield('custnum') eq $new->getfield('custnum');
- return "Can't change date!"
- unless $old->getfield('_date') eq $new->getfield('_date');
- return "Can't change amount!"
- unless $old->getfield('amount') eq $new->getfield('amount');
- return "(New) credited can't be > (new) amount!"
- if $new->getfield('credited') > $new->getfield('amount');
-
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid credit. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_credit record!" unless $self->table eq "cust_credit";
- my($recref) = $self->hashref;
-
- $recref->{crednum} =~ /^(\d*)$/ or return "Illegal crednum";
- $recref->{crednum} = $1;
-
- $recref->{custnum} =~ /^(\d+)$/ or return "Illegal custnum";
- $recref->{custnum} = $1;
- return "Unknown customer"
- unless qsearchs('cust_main',{'custnum'=>$recref->{custnum}});
-
- $recref->{_date} =~ /^(\d*)$/ or return "Illegal date";
- $recref->{_date} = $recref->{_date} ? $1 : time;
-
- $recref->{amount} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal amount";
- $recref->{amount} = $1;
-
- $recref->{credited} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal credited";
- $recref->{credited} = $1;
-
- #$recref->{otaker} =~ /^(\w+)$/ or return "Illegal otaker";
- #$recref->{otaker} = $1;
- $self->otaker(getotaker);
-
- $self->ut_textn('reason');
-
-}
-
-=back
-
-=head1 BUGS
-
-The delete method.
-
-It doesn't properly override FS::Record yet.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_refund>, L<FS::cust_bill>, schema.html from the base
-documentation.
-
-=head1 HISTORY
-
-ivan@sisd.com 98-mar-17
-
-pod, otaker from FS::UID ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_main.pm b/site_perl/cust_main.pm
deleted file mode 100644
index ec28273..0000000
--- a/site_perl/cust_main.pm
+++ /dev/null
@@ -1,868 +0,0 @@
-#this is so kludgy i'd be embarassed if it wasn't cybercash's fault
-package main;
-use vars qw($paymentserversecret $paymentserverport $paymentserverhost);
-
-package FS::cust_main;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK $conf $lpr $processor $xaction $E_NoErr);
-use Safe;
-use Exporter;
-use Carp;
-use Time::Local;
-use Date::Format;
-use Date::Manip;
-use Business::CreditCard;
-use FS::UID qw(getotaker);
-use FS::Record qw(fields hfields qsearchs qsearch);
-use FS::cust_pkg;
-use FS::cust_bill;
-use FS::cust_bill_pkg;
-use FS::cust_pay;
-#use FS::cust_pay_batch;
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(hfields);
-
-$conf = new FS::Conf;
-$lpr = $conf->config('lpr');
-
-if ( $conf->exists('cybercash3.2') ) {
- require CCMckLib3_2;
- #qw($MCKversion %Config InitConfig CCError CCDebug CCDebug2);
- require CCMckDirectLib3_2;
- #qw(SendCC2_1Server);
- require CCMckErrno3_2;
- #qw(MCKGetErrorMessage $E_NoErr);
- import CCMckErrno3_2 qw($E_NoErr);
- my $merchant_conf;
- ($merchant_conf,$xaction)= $conf->config('cybercash3.2');
- my $status = &CCMckLib3_2::InitConfig($merchant_conf);
- if ( $status != $E_NoErr ) {
- warn "CCMckLib3_2::InitConfig error:\n";
- foreach my $key (keys %CCMckLib3_2::Config) {
- warn " $key => $CCMckLib3_2::Config{$key}\n"
- }
- my($errmsg) = &CCMckErrno3_2::MCKGetErrorMessage($status);
- die "CCMckLib3_2::InitConfig fatal error: $errmsg\n";
- }
- $processor='cybercash3.2';
-} elsif ( $conf->exists('cybercash2') ) {
- require CCLib;
- #qw(sendmserver);
- ( $main::paymentserverhost,
- $main::paymentserverport,
- $main::paymentserversecret,
- $xaction,
- ) = $conf->config('cybercash2');
- $processor='cybercash2';
-}
-
-=head1 NAME
-
-FS::cust_main - Object methods for cust_main records
-
-=head1 SYNOPSIS
-
- use FS::cust_main;
-
- $record = create FS::cust_main \%hash;
- $record = create FS::cust_main { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- @cust_pkg = $record->all_pkgs;
-
- @cust_pkg = $record->ncancelled_pkgs;
-
- $error = $record->bill;
- $error = $record->bill %options;
- $error = $record->bill 'time' => $time;
-
- $error = $record->collect;
- $error = $record->collect %options;
- $error = $record->collect 'invoice_time' => $time,
- 'batch_card' => 'yes',
- 'report_badcard' => 'yes',
- ;
-
-=head1 DESCRIPTION
-
-An FS::cust_main object represents a customer. FS::cust_main inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item custnum - primary key (assigned automatically for new customers)
-
-=item agentnum - agent (see L<FS::agent>)
-
-=item refnum - referral (see L<FS::part_referral>)
-
-=item first - name
-
-=item last - name
-
-=item ss - social security number (optional)
-
-=item company - (optional)
-
-=item address1
-
-=item address2 - (optional)
-
-=item city
-
-=item county - (optional, see L<FS::cust_main_county>)
-
-=item state - (see L<FS::cust_main_county>)
-
-=item zip
-
-=item country - (see L<FS::cust_main_county>)
-
-=item daytime - phone (optional)
-
-=item night - phone (optional)
-
-=item payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free)
-
-=item payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username)
-
-=item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
-
-=item payname - name on card or billing name
-
-=item tax - tax exempt, empty or `Y'
-
-=item otaker - order taker (assigned automatically, see L<FS::UID>)
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new customer. To add the customer to the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my $field;
- #foreach $field (fields('cust_main')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_main',$hashref);
-}
-
-=item insert
-
-Adds this customer to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- #no callbacks in check, only data checks
- #local $SIG{HUP} = 'IGNORE';
- #local $SIG{INT} = 'IGNORE';
- #local $SIG{QUIT} = 'IGNORE';
- #local $SIG{TERM} = 'IGNORE';
- #local $SIG{TSTP} = 'IGNORE';
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented. Maybe cancel all of this customer's
-packages (cust_pkg)?
-
-I don't remove the customer record in the database because there would then
-be no record the customer ever existed (which is bad, no?)
-
-=cut
-
-# Usage: $error = $record -> delete;
-sub delete {
- return "Can't (yet?) delete customers.";
-# my($self)=@_;
-#
-# $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_main record!" unless $old->table eq "cust_main";
- return "Can't change custnum!"
- unless $old->getfield('custnum') eq $new->getfield('custnum');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid customer record. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and repalce methods.
-
-=cut
-
-sub check {
- my($self)=@_;
-
- return "Not a cust_main record!" unless $self->table eq "cust_main";
-
- my $error =
- $self->ut_number('agentnum')
- || $self->ut_number('refnum')
- || $self->ut_textn('company')
- || $self->ut_text('address1')
- || $self->ut_textn('address2')
- || $self->ut_text('city')
- || $self->ut_textn('county')
- || $self->ut_text('state')
- || $self->ut_phonen('daytime')
- || $self->ut_phonen('night')
- || $self->ut_phonen('fax')
- ;
- return $error if $error;
-
- return "Unknown agent"
- unless qsearchs('agent',{'agentnum'=>$self->agentnum});
-
- return "Unknown referral"
- unless qsearchs('part_referral',{'refnum'=>$self->refnum});
-
- $self->getfield('last') =~ /^([\w \,\.\-\']+)$/ or return "Illegal last name";
- $self->setfield('last',$1);
-
- $self->first =~ /^([\w \,\.\-\']+)$/ or return "Illegal first name";
- $self->first($1);
-
- if ( $self->ss eq '' ) {
- $self->ss('');
- } else {
- my $ss = $self->ss;
- $ss =~ s/\D//g;
- $ss =~ /^(\d{3})(\d{2})(\d{4})$/
- or return "Illegal social security number";
- $self->ss("$1-$2-$3");
- }
-
- return "Unknown state/county/country"
- unless qsearchs('cust_main_county',{
- 'state' => $self->state,
- 'county' => $self->county,
- } );
-
- #int'l zips?
- $self->zip =~ /^(\d{5}(-\d{4})?)$/ or return "Illegal zip";
- $self->zip($1);
-
- #int'l countries!
- $self->country =~ /^(US)$/ or return "Illegal country";
- $self->country($1);
-
- $self->payby =~ /^(CARD|BILL|COMP)$/ or return "Illegal payby";
- $self->payby($1);
-
- if ( $self->payby eq 'CARD' ) {
-
- my $payinfo = $self->payinfo;
- $payinfo =~ s/\D//g;
- $payinfo =~ /^(\d{13,16})$/
- or return "Illegal credit card number";
- $payinfo = $1;
- $self->payinfo($payinfo);
- validate($payinfo) or return "Illegal credit card number";
- my $type = cardtype($payinfo);
- return "Unknown credit card type"
- unless ( $type =~ /^VISA/ ||
- $type =~ /^MasterCard/ ||
- $type =~ /^American Express/ ||
- $type =~ /^Discover/ );
-
- } elsif ( $self->payby eq 'BILL' ) {
-
- $self->payinfo =~ /^([\w \-]*)$/ or return "Illegal P.O. number";
- $self->payinfo($1);
-
- } elsif ( $self->payby eq 'COMP' ) {
-
- $self->payinfo =~ /^(\w{2,8})$/ or return "Illegal comp account issuer";
- $self->payinfo($1);
-
- }
-
- if ( $self->paydate eq '' ) {
- return "Expriation date required" unless $self->payby eq 'BILL';
- $self->paydate('');
- } else {
- $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/
- or return "Illegal expiration date";
- if ( length($2) == 4 ) {
- $self->paydate("$2-$1-01");
- } elsif ( $2 > 97 ) { #should pry change to check for "this year"
- $self->paydate("19$2-$1-01");
- } else {
- $self->paydate("20$2-$1-01");
- }
- }
-
- if ( $self->payname eq '' ) {
- $self->payname( $self->first. " ". $self->getfield('last') );
- } else {
- $self->payname =~ /^([\w \,\.\-\']+)$/
- or return "Illegal billing name";
- $self->payname($1);
- }
-
- $self->tax =~ /^(Y?)$/ or return "Illegal tax";
- $self->tax($1);
-
- $self->otaker(getotaker);
-
- ''; #no error
-}
-
-=item all_pkgs
-
-Returns all packages (see L<FS::cust_pkg>) for this customer.
-
-=cut
-
-sub all_pkgs {
- my($self)=@_;
- qsearch( 'cust_pkg', { 'custnum' => $self->custnum });
-}
-
-=item ncancelled_pkgs
-
-Returns all non-cancelled packages (see L<FS::cust_pkg>) for this customer.
-
-=cut
-
-sub ncancelled_pkgs {
- my($self)=@_;
- qsearch( 'cust_pkg', {
- 'custnum' => $self->custnum,
- 'cancel' => '',
- });
-}
-
-=item bill OPTIONS
-
-Generates invoices (see L<FS::cust_bill>) for this customer. Usually used in
-conjunction with the collect method.
-
-The only currently available option is `time', which bills the customer as if
-it were that time. It is specified as a UNIX timestamp; see
-L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion
-functions.
-
-If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub bill {
- my($self,%options)=@_;
- my($time) = $options{'time'} || $^T;
-
- my($error);
-
- #put below somehow?
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- # find the packages which are due for billing, find out how much they are
- # & generate invoice database.
-
- my($total_setup,$total_recur)=(0,0);
-
- my(@cust_bill_pkg);
-
- my($cust_pkg);
- foreach $cust_pkg (
- qsearch('cust_pkg',{'custnum'=> $self->getfield('custnum') } )
- ) {
-
- bless($cust_pkg,"FS::cust_pkg");
-
- next if ( $cust_pkg->getfield('cancel') );
-
- #? to avoid use of uninitialized value errors... ?
- $cust_pkg->setfield('bill', '')
- unless defined($cust_pkg->bill);
-
- my($part_pkg)=
- qsearchs('part_pkg',{'pkgpart'=> $cust_pkg->pkgpart } );
-
- #so we don't modify cust_pkg record unnecessarily
- my($cust_pkg_mod_flag)=0;
- my(%hash)=$cust_pkg->hash;
- my($old_cust_pkg)=create FS::cust_pkg(\%hash);
-
- # bill setup
- my($setup)=0;
- unless ( $cust_pkg->setup ) {
- my($setup_prog)=$part_pkg->getfield('setup');
- my($cpt) = new Safe;
- #$cpt->permit(); #what is necessary?
- $cpt->share(qw($cust_pkg)); #can $cpt now use $cust_pkg methods?
- $setup = $cpt->reval($setup_prog);
- unless ( defined($setup) ) {
- warn "Error reval-ing part_pkg->setup pkgpart ",
- $part_pkg->pkgpart, ": $@";
- } else {
- $cust_pkg->setfield('setup',$time);
- $cust_pkg_mod_flag=1;
- }
- }
-
- #bill recurring fee
- my($recur)=0;
- my($sdate);
- if ( $part_pkg->getfield('freq') > 0 &&
- ! $cust_pkg->getfield('susp') &&
- ( $cust_pkg->getfield('bill') || 0 ) < $time
- ) {
- my($recur_prog)=$part_pkg->getfield('recur');
- my($cpt) = new Safe;
- #$cpt->permit(); #what is necessary?
- $cpt->share(qw($cust_pkg)); #can $cpt now use $cust_pkg methods?
- $recur = $cpt->reval($recur_prog);
- unless ( defined($recur) ) {
- warn "Error reval-ing part_pkg->recur pkgpart ",
- $part_pkg->pkgpart, ": $@";
- } else {
- #change this bit to use Date::Manip?
- #$sdate=$cust_pkg->bill || time;
- #$sdate=$cust_pkg->bill || $time;
- $sdate=$cust_pkg->bill || $cust_pkg->setup || $time;
- my($sec,$min,$hour,$mday,$mon,$year)=
- (localtime($sdate) )[0,1,2,3,4,5];
- $mon += $part_pkg->getfield('freq');
- until ( $mon < 12 ) { $mon -= 12; $year++; }
- $cust_pkg->setfield('bill',timelocal($sec,$min,$hour,$mday,$mon,$year));
- $cust_pkg_mod_flag=1;
- }
- }
-
- warn "setup is undefinded" unless defined($setup);
- warn "recur is undefinded" unless defined($recur);
- warn "cust_pkg bill is undefinded" unless defined($cust_pkg->bill);
-
- if ($cust_pkg_mod_flag) {
- $error=$cust_pkg->replace($old_cust_pkg);
- if ( $error ) {
- warn "Error modifying pkgnum ", $cust_pkg->pkgnum, ": $error";
- } else {
- #just in case
- $setup=sprintf("%.2f",$setup);
- $recur=sprintf("%.2f",$recur);
- my($cust_bill_pkg)=create FS::cust_bill_pkg ({
- 'pkgnum' => $cust_pkg->pkgnum,
- 'setup' => $setup,
- 'recur' => $recur,
- 'sdate' => $sdate,
- 'edate' => $cust_pkg->bill,
- });
- push @cust_bill_pkg, $cust_bill_pkg;
- $total_setup += $setup;
- $total_recur += $recur;
- }
- }
-
- }
-
- my($charged)=sprintf("%.2f",$total_setup + $total_recur);
-
- return '' if scalar(@cust_bill_pkg) == 0;
-
- unless ( $self->getfield('tax') eq 'Y' ||
- $self->getfield('tax') eq 'y' ||
- $self->getfield('payby') eq 'COMP'
- ) {
- my($cust_main_county) = qsearchs('cust_main_county',{
- 'county' => $self->getfield('county'),
- 'state' => $self->getfield('state'),
- } );
- my($tax) = sprintf("%.2f",
- $charged * ( $cust_main_county->getfield('tax') / 100 )
- );
- $charged = sprintf("%.2f",$charged+$tax);
-
- my($cust_bill_pkg)=create FS::cust_bill_pkg ({
- 'pkgnum' => 0,
- 'setup' => $tax,
- 'recur' => 0,
- 'sdate' => '',
- 'edate' => '',
- });
- push @cust_bill_pkg, $cust_bill_pkg;
- }
-
- my($cust_bill) = create FS::cust_bill ( {
- 'custnum' => $self->getfield('custnum'),
- '_date' => $time,
- 'charged' => $charged,
- } );
- $error=$cust_bill->insert;
- #shouldn't happen, but how else to handle this? (wrap me in eval, to catch
- # fatal errors)
- die "Error creating cust_bill record: $error!\n",
- "Check updated but unbilled packages for customer", $self->custnum, "\n"
- if $error;
-
- my($invnum)=$cust_bill->invnum;
- my($cust_bill_pkg);
- foreach $cust_bill_pkg ( @cust_bill_pkg ) {
- $cust_bill_pkg->setfield('invnum',$invnum);
- $error=$cust_bill_pkg->insert;
- #shouldn't happen, but how else tohandle this?
- die "Error creating cust_bill_pkg record: $error!\n",
- "Check incomplete invoice ", $invnum, "\n"
- if $error;
- }
-
- ''; #no error
-}
-
-=item collect OPTIONS
-
-(Attempt to) collect money for this customer's outstanding invoices (see
-L<FS::cust_bill>). Usually used after the bill method.
-
-Depending on the value of `payby', this may print an invoice (`BILL'), charge
-a credit card (`CARD'), or just add any necessary (pseudo-)payment (`COMP').
-
-If there is an error, returns the error, otherwise returns false.
-
-Currently available options are:
-
-invoice_time - Use this time when deciding when to print invoices and
-late notices on those invoices. The default is now. It is specified as a UNIX timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse>
-for conversion functions.
-
-batch_card - Set this true to batch cards (see L<cust_pay_batch>). By
-default, cards are processed immediately, which will generate an error if
-CyberCash is not installed.
-
-report_badcard - Set this true if you want bad card transactions to
-return an error. By default, they don't.
-
-=cut
-
-sub collect {
- my($self,%options)=@_;
- my($invoice_time) = $options{'invoice_time'} || $^T;
-
- my($total_owed) = $self->balance;
- return '' unless $total_owed > 0; #redundant?????
-
- #put below somehow?
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- foreach my $cust_bill ( qsearch('cust_bill', {
- 'custnum' => $self->getfield('custnum'),
- } ) ) {
-
- #this has to be before next's
- my($amount) = sprintf("%.2f", $total_owed < $cust_bill->owed
- ? $total_owed
- : $cust_bill->owed
- );
- $total_owed = sprintf("%.2f",$total_owed-$amount);
-
- next unless $cust_bill->owed > 0;
-
- next if qsearchs('cust_pay_batch',{'invnum'=> $cust_bill->invnum });
-
- #warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ", amount $amount, total_owed $total_owed)";
-
- next unless $amount > 0;
-
- if ( $self->getfield('payby') eq 'BILL' ) {
-
- #30 days 2592000
- my($since)=$invoice_time - ( $cust_bill->_date || 0 );
- #warn "$invoice_time ", $cust_bill->_date, " $since";
- if ( $since >= 0 #don't print future invoices
- && ( $cust_bill->printed * 2592000 ) <= $since
- ) {
-
- open(LPR,$lpr) or die "Can't open $lpr: $!";
- print LPR $cust_bill->print_text; #( date )
- close LPR
- or die $! ? "Error closing $lpr: $!"
- : "Exit status $? from $lpr";
-
- my(%hash)=$cust_bill->hash;
- $hash{'printed'}++;
- my($new_cust_bill)=create FS::cust_bill(\%hash);
- my($error)=$new_cust_bill->replace($cust_bill);
- if ( $error ) {
- warn "Error updating $cust_bill->printed: $error";
- }
-
- }
-
- } elsif ( $self->getfield('payby') eq 'COMP' ) {
- my($cust_pay) = create FS::cust_pay ( {
- 'invnum' => $cust_bill->getfield('invnum'),
- 'paid' => $amount,
- '_date' => '',
- 'payby' => 'COMP',
- 'payinfo' => $self->getfield('payinfo'),
- 'paybatch' => ''
- } );
- my($error)=$cust_pay->insert;
- return 'Error COMPing invnum #' . $cust_bill->getfield('invnum') .
- ':' . $error if $error;
- } elsif ( $self->getfield('payby') eq 'CARD' ) {
-
- if ( $options{'batch_card'} ne 'yes' ) {
-
- return "Real time card processing not enabled!" unless $processor;
-
- if ( $processor =~ /cybercash/ ) {
-
- #fix exp. date for cybercash
- $self->getfield('paydate') =~ /^(\d+)\/\d*(\d{2})$/;
- my($exp)="$1/$2";
-
- my($paybatch)= $cust_bill->getfield('invnum') .
- '-' . time2str("%y%m%d%H%M%S",time);
-
- my($payname)= $self->getfield('payname') ||
- $self->getfield('first') . ' ' .$self->getfield('last');
-
- my($address)= $self->getfield('address1');
- $address .= ", " . $self->getfield('address2')
- if $self->getfield('address2');
-
- my($country) = $self->getfield('country') eq 'US' ?
- 'USA' : $self->getfield('country');
-
- my(@full_xaction)=($xaction,
- 'Order-ID' => $paybatch,
- 'Amount' => "usd $amount",
- 'Card-Number' => $self->getfield('payinfo'),
- 'Card-Name' => $payname,
- 'Card-Address' => $address,
- 'Card-City' => $self->getfield('city'),
- 'Card-State' => $self->getfield('state'),
- 'Card-Zip' => $self->getfield('zip'),
- 'Card-Country' => $country,
- 'Card-Exp' => $exp,
- );
-
- my(%result);
- if ( $processor eq 'cybercash2' ) {
- $^W=0; #CCLib isn't -w safe, ugh!
- %result = &CCLib::sendmserver(@full_xaction);
- $^W=1;
- } elsif ( $processor eq 'cybercash3.2' ) {
- %result = &CCMckDirectLib3_2::SendCC2_1Server(@full_xaction);
- } else {
- return "Unkonwn real-time processor $processor\n";
- }
-
- #if ( $result{'MActionCode'} == 7 ) { #cybercash smps v.1.1.3
- #if ( $result{'action-code'} == 7 ) { #cybercash smps v.2.1
- if ( $result{'MStatus'} eq 'success' ) { #cybercash smps v.2 or 3
- my($cust_pay) = create FS::cust_pay ( {
- 'invnum' => $cust_bill->getfield('invnum'),
- 'paid' => $amount,
- '_date' => '',
- 'payby' => 'CARD',
- 'payinfo' => $self->getfield('payinfo'),
- 'paybatch' => "$processor:$paybatch",
- } );
- my($error)=$cust_pay->insert;
- return 'Error applying payment, invnum #' .
- $cust_bill->getfield('invnum') . ':' . $error if $error;
- } elsif ( $result{'Mstatus'} ne 'failure-bad-money'
- || $options{'report_badcard'} ) {
- return 'Cybercash error, invnum #' .
- $cust_bill->getfield('invnum') . ':' . $result{'MErrMsg'};
- } else {
- return '';
- }
-
- } else {
- return "Unkonwn real-time processor $processor\n";
- }
-
- } else { #batch card
-
-# my($cust_pay_batch) = create FS::cust_pay_batch ( {
- my($cust_pay_batch) = new FS::Record ('cust_pay_batch', {
- 'invnum' => $cust_bill->getfield('invnum'),
- 'custnum' => $self->getfield('custnum'),
- 'last' => $self->getfield('last'),
- 'first' => $self->getfield('first'),
- 'address1' => $self->getfield('address1'),
- 'address2' => $self->getfield('address2'),
- 'city' => $self->getfield('city'),
- 'state' => $self->getfield('state'),
- 'zip' => $self->getfield('zip'),
- 'country' => $self->getfield('country'),
- 'trancode' => 77,
- 'cardnum' => $self->getfield('payinfo'),
- 'exp' => $self->getfield('paydate'),
- 'payname' => $self->getfield('payname'),
- 'amount' => $amount,
- } );
-# my($error)=$cust_pay_batch->insert;
- my($error)=$cust_pay_batch->add;
- return "Error adding to cust_pay_batch: $error" if $error;
-
- }
-
- } else {
- return "Unknown payment type ".$self->getfield('payby');
- }
-
- }
- '';
-
-}
-
-=item total_owed
-
-Returns the total owed for this customer on all invoices
-(see L<FS::cust_bill>).
-
-=cut
-
-sub total_owed {
- my($self) = @_;
- my($total_bill) = 0;
- my($cust_bill);
- foreach $cust_bill ( qsearch('cust_bill', {
- 'custnum' => $self->getfield('custnum'),
- } ) ) {
- $total_bill += $cust_bill->getfield('owed');
- }
- sprintf("%.2f",$total_bill);
-}
-
-=item total_credited
-
-Returns the total credits (see L<FS::cust_credit>) for this customer.
-
-=cut
-
-sub total_credited {
- my($self) = @_;
- my($total_credit) = 0;
- my($cust_credit);
- foreach $cust_credit ( qsearch('cust_credit', {
- 'custnum' => $self->getfield('custnum'),
- } ) ) {
- $total_credit += $cust_credit->getfield('credited');
- }
- sprintf("%.2f",$total_credit);
-}
-
-=item balance
-
-Returns the balance for this customer (total owed minus total credited).
-
-=cut
-
-sub balance {
- my($self) = @_;
- sprintf("%.2f",$self->total_bill - $self->total_credit);
-}
-
-=back
-
-=head1 BUGS
-
-The delete method.
-
-It doesn't properly override FS::Record yet.
-
-hfields should be removed.
-
-Bill and collect options should probably be passed as references instead of a
-list.
-
-CyberCash v2 forces us to define some variables in package main.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_pkg>, L<FS::cust_bill>, L<FS::cust_credit>
-L<FS::cust_pay_batch>, L<FS::agent>, L<FS::part_referral>,
-L<FS::cust_main_county>, L<FS::UID>, schema.html from the base documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-28
-
-Changed to standard Business::CreditCard
-no more TableUtil
-EXPORT_OK FS::Record's hfields
-removed unique calls and locking (not needed here now)
-wrapped the (now) optional fields in if statements in sub check (notyetdone!)
-ivan@sisd.com 97-nov-12
-
-updated paydate with SQL-type date info ivan@sisd.com 98-mar-5
-
-Added export of datasrc from UID.pm for Pg6.3
-changed 'day' to 'daytime' because Pg6.3 reserves the day word
- bmccane@maxbaud.net 98-apr-3
-
-in ->create, s/svc_acct/cust_main/, now it should actually eliminate the
-warnings it was meant to ivan@sisd.com 98-jul-16
-
-don't require a phone number and allow '/' in company names
-ivan@sisd.com 98-jul-18
-
-use ut_ and rewrite &check, &*_pkgs ivan@sisd.com 98-sep-5
-
-pod, merge with FS::Bill (about time!), total_owed, total_credited and balance
-methods, cleaned collect method, source modifications no longer necessary to
-enable cybercash, cybercash v3 support, don't need to import
-FS::UID::{datasrc,checkruid} ivan@sisd.com 98-sep-19-21
-
-=cut
-
-1;
-
-
diff --git a/site_perl/cust_main_county.pm b/site_perl/cust_main_county.pm
deleted file mode 100644
index f4b4595..0000000
--- a/site_perl/cust_main_county.pm
+++ /dev/null
@@ -1,161 +0,0 @@
-package FS::cust_main_county;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields hfields qsearch qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(hfields);
-
-=head1 NAME
-
-FS::cust_main_county - Object methods for cust_main_county objects
-
-=head1 SYNOPSIS
-
- use FS::cust_main_county;
-
- $record = create FS::cust_main_county \%hash;
- $record = create FS::cust_main_county { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_main_county object represents a tax rate, defined by locale.
-FS::cust_main_county inherits from FS::Record. The following fields are
-currently supported:
-
-=over 4
-
-=item taxnum - primary key (assigned automatically for new tax rates)
-
-=item state
-
-=item county
-
-=item tax - percentage
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new tax rate. To add the tax rate to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_main_county')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_main_county',$hashref);
-}
-
-=item insert
-
-Adds this tax rate to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Deletes this tax rate from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=cut
-
-sub delete {
- my($self)=@_;
-
- $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_main_county record!"
- unless $old->table eq "cust_main_county";
- return "Can't change taxnum!"
- unless $old->getfield('taxnum') eq $new->getfield('taxnum');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid tax rate. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_main_county record!"
- unless $self->table eq "cust_main_county";
- my($recref) = $self->hashref;
-
- $self->ut_numbern('taxnum')
- or $self->ut_text('state')
- or $self->ut_textn('county')
- or $self->ut_float('tax')
- ;
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-A country field (and possibly a currency field) should be added.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_main>, L<FS::cust_bill>, schema.html from the base
-documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-dec-16
-
-Changed check for 'tax' to use the new ut_float subroutine
- bmccane@maxbaud.net 98-apr-3
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_pay.pm b/site_perl/cust_pay.pm
deleted file mode 100644
index 6e30c59..0000000
--- a/site_perl/cust_pay.pm
+++ /dev/null
@@ -1,235 +0,0 @@
-package FS::cust_pay;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use Business::CreditCard;
-use FS::Record qw(fields qsearchs);
-use FS::cust_bill;
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::cust_pay - Object methods for cust_pay objects
-
-=head1 SYNOPSIS
-
- use FS::cust_pay;
-
- $record = create FS::cust_pay \%hash;
- $record = create FS::cust_pay { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_pay object represents a payment. FS::cust_pay inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item paynum - primary key (assigned automatically for new payments)
-
-=item invnum - Invoice (see L<FS::cust_bill>)
-
-=item paid - Amount of this payment
-
-=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=item payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free)
-
-=item payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username)
-
-=item paybatch - text field for tracking card processing
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new payment. To add the payment to the databse, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_pay')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_pay',$hashref);
-
-}
-
-=item insert
-
-Adds this payment to the databse, and updates the invoice (see
-L<FS::cust_bill>).
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- my($error);
-
- $error=$self->check;
- return $error if $error;
-
- my($old_cust_bill) = qsearchs('cust_bill', {
- 'invnum' => $self->getfield('invnum')
- } );
- return "Unknown invnum" unless $old_cust_bill;
- my(%hash)=$old_cust_bill->hash;
- $hash{owed} = sprintf("%.2f",$hash{owed} - $self->getfield('paid') );
- my($new_cust_bill) = create FS::cust_bill ( \%hash );
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error=$new_cust_bill -> replace($old_cust_bill);
- return "Error modifying cust_bill: $error" if $error;
-
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented (accounting reasons).
-
-=cut
-
-sub delete {
- return "Can't (yet?) delete cust_pay records!";
-#template code below
-# my($self)=@_;
-#
-# $self->del;
-}
-
-=item replace OLD_RECORD
-
-Currently unimplemented (accounting reasons).
-
-=cut
-
-sub replace {
- return "Can't (yet?) modify cust_pay records!";
-#template code below
-# my($new,$old)=@_;
-# return "(Old) Not a cust_pay record!" unless $old->table eq "cust_pay";
-#
-# $new->check or
-# $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid payment. If there is an error,
-returns the error, otherwise returns false. Called by the insert method.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_pay record!" unless $self->table eq "cust_pay";
- my($recref) = $self->hashref;
-
- $recref->{paynum} =~ /^(\d*)$/ or return "Illegal paynum";
- $recref->{paynum} = $1;
-
- $recref->{invnum} =~ /^(\d+)$/ or return "Illegal invnum";
- $recref->{invnum} = $1;
-
- $recref->{paid} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal paid";
- $recref->{paid} = $1;
-
- $recref->{_date} =~ /^(\d*)$/ or return "Illegal date";
- $recref->{_date} = $recref->{_date} ? $1 : time;
-
- $recref->{payby} =~ /^(CARD|BILL|COMP)$/ or return "Illegal payby";
- $recref->{payby} = $1;
-
- if ( $recref->{payby} eq 'CARD' ) {
-
- $recref->{payinfo} =~ s/\D//g;
- if ( $recref->{payinfo} ) {
- $recref->{payinfo} =~ /^(\d{13,16})$/
- or return "Illegal (mistyped?) credit card number (payinfo)";
- $recref->{payinfo} = $1;
- #validate($recref->{payinfo})
- # or return "Illegal credit card number";
- my($type)=cardtype($recref->{payinfo});
- return "Unknown credit card type"
- unless ( $type =~ /^VISA/ ||
- $type =~ /^MasterCard/ ||
- $type =~ /^American Express/ ||
- $type =~ /^Discover/ );
- } else {
- $recref->{payinfo}='N/A';
- }
-
- } elsif ( $recref->{payby} eq 'BILL' ) {
-
- $recref->{payinfo} =~ /^([\w \-]*)$/
- or return "Illegal P.O. number (payinfo)";
- $recref->{payinfo} = $1;
-
- } elsif ( $recref->{payby} eq 'COMP' ) {
-
- $recref->{payinfo} =~ /^([\w]{2,8})$/
- or return "Illegal comp account issuer (payinfo)";
- $recref->{payinfo} = $1;
-
- }
-
- $recref->{paybatch} =~ /^([\w\-\:]*)$/
- or return "Illegal paybatch";
- $recref->{paybatch} = $1;
-
- ''; #no error
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-Delete and replace methods.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_bill>, schema.html from the base documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-1 - 25 - 29
-
-new api ivan@sisd.com 98-mar-13
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_pkg.pm b/site_perl/cust_pkg.pm
deleted file mode 100644
index 7dc5aa7..0000000
--- a/site_perl/cust_pkg.pm
+++ /dev/null
@@ -1,507 +0,0 @@
-package FS::cust_pkg;
-
-use strict;
-use vars qw(@ISA);
-use Exporter;
-use FS::UID qw(getotaker);
-use FS::Record qw(fields qsearch qsearchs);
-use FS::cust_svc;
-
-@ISA = qw(FS::Record Exporter);
-
-=head1 NAME
-
-FS::cust_pkg - Object methods for cust_pkg objects
-
-=head1 SYNOPSIS
-
- use FS::cust_pkg;
-
- $record = create FS::cust_pkg \%hash;
- $record = create FS::cust_pkg { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->cancel;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = FS::cust_pkg::order( $custnum, \@pkgparts );
- $error = FS::cust_pkg::order( $custnum, \@pkgparts, \@remove_pkgnums ] );
-
-=head1 DESCRIPTION
-
-An FS::cust_pkg object represents a customer billing item. FS::cust_pkg
-inherits from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item pkgnum - primary key (assigned automatically for new billing items)
-
-=item custnum - Customer (see L<FS::cust_main>)
-
-=item pkgpart - Billing item definition (see L<FS::part_pkg>)
-
-=item setup - date
-
-=item bill - date
-
-=item susp - date
-
-=item expire - date
-
-=item cancel - date
-
-=item otaker - order taker (assigned automatically if null, see L<FS::UID>)
-
-=back
-
-Note: setup, bill, susp, expire and cancel are specified as UNIX timestamps;
-see L<perlfunc/"time">. Also see L<Time::Local> and L<Date::Parse> for
-conversion functions.
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Create a new billing item. To add the item to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_pkg')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_pkg',$hashref);
-}
-
-=item insert
-
-Adds this billing item to the database ("Orders" the item). If there is an
-error, returns the error, otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented. You don't want to delete billing items, because there
-would then be no record the customer ever purchased the item. Instead, see
-the cancel method.
-
-sub delete {
- return "Can't delete cust_pkg records!";
-}
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-Currently, custnum, setup, bill, susp, expire, and cancel may be changed.
-
-pkgpart may not be changed, but see the order subroutine.
-
-setup and bill are normally updated by calling the bill method of a customer
-object (see L<FS::cust_main>).
-
-suspend is normally updated by the suspend and unsuspend methods.
-
-cancel is normally updated by the cancel method (and also the order subroutine
-in some cases).
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_pkg record!" if $old->table ne "cust_pkg";
- return "Can't change pkgnum!"
- if $old->getfield('pkgnum') ne $new->getfield('pkgnum');
- return "Can't (yet?) change pkgpart!"
- if $old->getfield('pkgpart') ne $new->getfield('pkgpart');
- return "Can't change otaker!"
- if $old->getfield('otaker') ne $new->getfield('otaker');
- return "Can't change setup once it exists!"
- if $old->getfield('setup') &&
- $old->getfield('setup') != $new->getfield('setup');
- #some logic for bill, susp, cancel?
-
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid billing item. If there is an
-error, returns the error, otherwise returns false. Called by the insert and
-replace methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_pkg record!" if $self->table ne "cust_pkg";
- my($recref) = $self->hashref;
-
- $recref->{pkgnum} =~ /^(\d*)$/ or return "Illegal pkgnum";
- $recref->{pkgnum}=$1;
-
- $recref->{custnum} =~ /^(\d+)$/ or return "Illegal custnum";
- $recref->{custnum}=$1;
- return "Unknown customer"
- unless qsearchs('cust_main',{'custnum'=>$recref->{custnum}});
-
- $recref->{pkgpart} =~ /^(\d+)$/ or return "Illegal pkgpart";
- $recref->{pkgpart}=$1;
- return "Unknown pkgpart"
- unless qsearchs('part_pkg',{'pkgpart'=>$recref->{pkgpart}});
-
- $recref->{otaker} ||= &getotaker;
- $recref->{otaker} =~ /^(\w{0,8})$/ or return "Illegal otaker";
- $recref->{otaker}=$1;
-
- $recref->{setup} =~ /^(\d*)$/ or return "Illegal setup date";
- $recref->{setup}=$1;
-
- $recref->{bill} =~ /^(\d*)$/ or return "Illegal bill date";
- $recref->{bill}=$1;
-
- $recref->{susp} =~ /^(\d*)$/ or return "Illegal susp date";
- $recref->{susp}=$1;
-
- $recref->{cancel} =~ /^(\d*)$/ or return "Illegal cancel date";
- $recref->{cancel}=$1;
-
- ''; #no error
-}
-
-=item cancel
-
-Cancels and removes all services (see L<FS::cust_svc> and L<FS::part_svc>)
-in this package, then cancels the package itself (sets the cancel field to
-now).
-
-If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub cancel {
- my($self)=@_;
- my($error);
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- my($cust_svc);
- foreach $cust_svc (
- qsearch('cust_svc',{'pkgnum'=> $self->pkgnum } )
- ) {
- my($part_svc)=
- qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } );
-
- $part_svc->getfield('svcdb') =~ /^([\w\-]+)$/
- or return "Illegal svcdb value in part_svc!";
- my($svcdb) = $1;
- require "FS/$svcdb.pm";
-
- my($svc) = qsearchs($svcdb,{'svcnum' => $cust_svc->svcnum } );
- if ($svc) {
- bless($svc,"FS::$svcdb");
- $error = $svc->cancel;
- return "Error cancelling service: $error" if $error;
- $error = $svc->delete;
- return "Error deleting service: $error" if $error;
- }
-
- bless($cust_svc,"FS::cust_svc");
- $error = $cust_svc->delete;
- return "Error deleting cust_svc: $error" if $error;
-
- }
-
- unless ( $self->getfield('cancel') ) {
- my(%hash) = $self->hash;
- $hash{'cancel'}=$^T;
- my($new) = create FS::cust_pkg ( \%hash );
- $error=$new->replace($self);
- return $error if $error;
- }
-
- ''; #no errors
-}
-
-=item suspend
-
-Suspends all services (see L<FS::cust_svc> and L<FS::part_svc>) in this
-package, then suspends the package itself (sets the susp field to now).
-
-If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub suspend {
- my($self)=@_;
- my($error);
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- my($cust_svc);
- foreach $cust_svc (
- qsearch('cust_svc',{'pkgnum'=> $self->getfield('pkgnum') } )
- ) {
- my($part_svc)=
- qsearchs('part_svc',{'svcpart'=> $cust_svc->getfield('svcpart') } );
-
- $part_svc->getfield('svcdb') =~ /^([\w\-]+)$/
- or return "Illegal svcdb value in part_svc!";
- my($svcdb) = $1;
- require "FS/$svcdb.pm";
-
- my($svc) = qsearchs($svcdb,{'svcnum' => $cust_svc->getfield('svcnum') } );
-
- if ($svc) {
- bless($svc,"FS::$svcdb");
- $error = $svc->suspend;
- return $error if $error;
- }
-
- }
-
- unless ( $self->getfield('susp') ) {
- my(%hash) = $self->hash;
- $hash{'susp'}=$^T;
- my($new) = create FS::cust_pkg ( \%hash );
- $error=$new->replace($self);
- return $error if $error;
- }
-
- ''; #no errors
-}
-
-=item unsuspend
-
-Unsuspends all services (see L<FS::cust_svc> and L<FS::part_svc>) in this
-package, then unsuspends the package itself (clears the susp field).
-
-If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub unsuspend {
- my($self)=@_;
- my($error);
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- my($cust_svc);
- foreach $cust_svc (
- qsearch('cust_svc',{'pkgnum'=> $self->getfield('pkgnum') } )
- ) {
- my($part_svc)=
- qsearchs('part_svc',{'svcpart'=> $cust_svc->getfield('svcpart') } );
-
- $part_svc->getfield('svcdb') =~ /^([\w\-]+)$/
- or return "Illegal svcdb value in part_svc!";
- my($svcdb) = $1;
- require "FS/$svcdb.pm";
-
- my($svc) = qsearchs($svcdb,{'svcnum' => $cust_svc->getfield('svcnum') } );
- if ($svc) {
- bless($svc,"FS::$svcdb");
- $error = $svc->unsuspend;
- return $error if $error;
- }
-
- }
-
- unless ( ! $self->getfield('susp') ) {
- my(%hash) = $self->hash;
- $hash{'susp'}='';
- my($new) = create FS::cust_pkg ( \%hash );
- $error=$new->replace($self);
- return $error if $error;
- }
-
- ''; #no errors
-}
-
-=back
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item order CUSTNUM, PKGPARTS_ARYREF, [ REMOVE_PKGNUMS_ARYREF ]
-
-CUSTNUM is a customer (see L<FS::cust_main>)
-
-PKGPARTS is a list of pkgparts specifying the the billing item definitions (see
-L<FS::part_pkg>) to order for this customer. Duplicates are of course
-permitted.
-
-REMOVE_PKGNUMS is an optional list of pkgnums specifying the billing items to
-remove for this customer. The services (see L<FS::cust_svc>) are moved to the
-new billing items. An error is returned if this is not possible (see
-L<FS::pkg_svc>).
-
-=cut
-
-sub order {
- my($custnum,$pkgparts,$remove_pkgnums)=@_;
-
- my(%part_pkg);
- # generate %part_pkg
- # $part_pkg{$pkgpart} is true iff $custnum may purchase $pkgpart
- my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum});
- my($agent)=qsearchs('agent',{'agentnum'=> $cust_main->agentnum });
-
- my($type_pkgs);
- foreach $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) {
- my($pkgpart)=$type_pkgs->pkgpart;
- $part_pkg{$pkgpart}++;
- }
- #
-
- my(%svcnum);
- # generate %svcnum
- # for those packages being removed:
- #@{ $svcnum{$svcpart} } goes from a svcpart to a list of FS::Record
- # objects (table eq 'cust_svc')
- my($pkgnum);
- foreach $pkgnum ( @{$remove_pkgnums} ) {
- my($cust_svc);
- foreach $cust_svc (qsearch('cust_svc',{'pkgnum'=>$pkgnum})) {
- push @{ $svcnum{$cust_svc->getfield('svcpart')} }, $cust_svc;
- }
- }
-
- my(@cust_svc);
- #generate @cust_svc
- # for those packages the customer is purchasing:
- # @{$pkgparts} is a list of said packages, by pkgpart
- # @cust_svc is a corresponding list of lists of FS::Record objects
- my($pkgpart);
- foreach $pkgpart ( @{$pkgparts} ) {
- return "Customer not permitted to purchase pkgpart $pkgpart!"
- unless $part_pkg{$pkgpart};
- push @cust_svc, [
- map {
- ( $svcnum{$_} && @{ $svcnum{$_} } ) ? shift @{ $svcnum{$_} } : ();
- } (split(/,/,
- qsearchs('part_pkg',{'pkgpart'=>$pkgpart})->getfield('services')
- ))
- ];
- }
-
- #check for leftover services
- foreach (keys %svcnum) {
- next unless @{ $svcnum{$_} };
- return "Leftover services!";
- }
-
- #no leftover services, let's make changes.
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- #first cancel old packages
-# my($pkgnum);
- foreach $pkgnum ( @{$remove_pkgnums} ) {
- my($old) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
- return "Package $pkgnum not found to remove!" unless $old;
- my(%hash) = $old->hash;
- $hash{'cancel'}=$^T;
- my($new) = create FS::cust_pkg ( \%hash );
- my($error)=$new->replace($old);
- return $error if $error;
- }
-
- #now add new packages, changing cust_svc records if necessary
-# my($pkgpart);
- while ($pkgpart=shift @{$pkgparts} ) {
-
- my($new) = create FS::cust_pkg ( {
- 'custnum' => $custnum,
- 'pkgpart' => $pkgpart,
- } );
- my($error) = $new->insert;
- return $error if $error;
- my($pkgnum)=$new->getfield('pkgnum');
-
- my($cust_svc);
- foreach $cust_svc ( @{ shift @cust_svc } ) {
- my(%hash) = $cust_svc->hash;
- $hash{'pkgnum'}=$pkgnum;
- my($new) = create FS::cust_svc ( \%hash );
- my($error)=$new->replace($cust_svc);
- return $error if $error;
- }
- }
-
- ''; #no errors
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-sub order is not OO. Perhaps it should be moved to FS::cust_main and made so?
-
-In sub order, the @pkgparts array (passed by reference) is clobbered.
-
-Also in sub order, no money is adjusted. Once FS::part_pkg defines a standard
-method to pass dates to the recur_prog expression, it should do so.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_main>, L<FS::part_pkg>, L<FS::cust_svc>
-, L<FS::pkg_svc>, schema.html from the base documentation
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-1 - 21
-
-fixed for new agent->agent_type->type_pkgs in &order ivan@sisd.com 98-mar-7
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_refund.pm b/site_perl/cust_refund.pm
deleted file mode 100644
index a30f217..0000000
--- a/site_perl/cust_refund.pm
+++ /dev/null
@@ -1,233 +0,0 @@
-package FS::cust_refund;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use Business::CreditCard;
-use FS::Record qw(fields qsearchs);
-use FS::UID qw(getotaker);
-use FS::cust_credit;
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::cust_refund - Object method for cust_refund objects
-
-=head1 SYNOPSIS
-
- use FS::cust_refund;
-
- $record = create FS::cust_refund \%hash;
- $record = create FS::cust_refund { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_refund represents a refund. FS::cust_refund inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item refundnum - primary key (assigned automatically for new refunds)
-
-=item crednum - Credit (see L<FS::cust_credit>)
-
-=item refund - Amount of the refund
-
-=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=item payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free)
-
-=item payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username)
-
-=item otaker - order taker (assigned automatically, see L<FS::UID>)
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new refund. To add the refund to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_refund')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_refund',$hashref);
-
-}
-
-=item insert
-
-Adds this refund to the database, and updates the credit (see
-L<FS::cust_credit>).
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- my($error);
-
- $error=$self->check;
- return $error if $error;
-
- my($old_cust_credit) = qsearchs('cust_credit', {
- 'crednum' => $self->getfield('crednum')
- } );
- return "Unknown crednum" unless $old_cust_credit;
- my(%hash)=$old_cust_credit->hash;
- $hash{credited} = sprintf("%.2f",$hash{credited} - $self->getfield('refund') );
- my($new_cust_credit) = create FS::cust_credit ( \%hash );
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error=$new_cust_credit -> replace($old_cust_credit);
- return "Error modifying cust_credit: $error" if $error;
-
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented (accounting reasons).
-
-=cut
-
-sub delete {
- return "Can't (yet?) delete cust_refund records!";
-#template code below
-# my($self)=@_;
-#
-# $self->del;
-}
-
-=item replace OLD_RECORD
-
-Currently unimplemented (accounting reasons).
-
-=cut
-
-sub replace {
- return "Can't (yet?) modify cust_refund records!";
-#template code below
-# my($new,$old)=@_;
-# return "(Old) Not a cust_refund record!" unless $old->table eq "cust_refund";
-#
-# $new->check or
-# $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid refund. If there is an error,
-returns the error, otherwise returns false. Called by the insert method.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_refund record!" unless $self->table eq "cust_refund";
-
- my $error =
- $self->ut_number('refundnum')
- || $self->ut_number('crednum')
- || $self->ut_money('amount')
- || $self->ut_numbern('_date')
- ;
- return $error if $error;
-
- my($recref) = $self->hashref;
-
- $recref->{_date} ||= time;
-
- $recref->{payby} =~ /^(CARD|BILL|COMP)$/ or return "Illegal payby";
- $recref->{payby} = $1;
-
- if ( $recref->{payby} eq 'CARD' ) {
-
- $recref->{payinfo} =~ s/\D//g;
- if ( $recref->{payinfo} ) {
- $recref->{payinfo} =~ /^(\d{13,16})$/
- or return "Illegal (mistyped?) credit card number (payinfo)";
- $recref->{payinfo} = $1;
- #validate($recref->{payinfo})
- # or return "Illegal (checksum) credit card number (payinfo)";
- my($type)=cardtype($recref->{payinfo});
- return "Unknown credit card type"
- unless ( $type =~ /^VISA/ ||
- $type =~ /^MasterCard/ ||
- $type =~ /^American Express/ ||
- $type =~ /^Discover/ );
- } else {
- $recref->{payinfo}='N/A';
- }
-
- } elsif ( $recref->{payby} eq 'BILL' ) {
-
- $recref->{payinfo} =~ /^([\w \-]*)$/
- or return "Illegal P.O. number (payinfo)";
- $recref->{payinfo} = $1;
-
- } elsif ( $recref->{payby} eq 'COMP' ) {
-
- $recref->{payinfo} =~ /^([\w]{2,8})$/
- or return "Illegal comp account issuer (payinfo)";
- $recref->{payinfo} = $1;
-
- }
-
- $self->otaker(getotaker);
-
- ''; #no error
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-Delete and replace methods.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_credit>, schema.html from the base documentation.
-
-=head1 HISTORY
-
-ivan@sisd.com 98-mar-18
-
-->create had wrong tablename ivan@sisd.com 98-jun-16
-(finish me!)
-
-pod and finish up ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/cust_svc.pm b/site_perl/cust_svc.pm
deleted file mode 100644
index 1d5051b..0000000
--- a/site_perl/cust_svc.pm
+++ /dev/null
@@ -1,168 +0,0 @@
-package FS::cust_svc;
-
-use strict;
-use vars qw(@ISA);
-use Exporter;
-use FS::Record qw(fields qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-
-=head1 NAME
-
-FS::cust_svc - Object method for cust_svc objects
-
-=head1 SYNOPSIS
-
- use FS::cust_svc;
-
- $record = create FS::cust_svc \%hash
- $record = create FS::cust_svc { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_svc represents a service. FS::cust_svc inherits from FS::Record.
-The following fields are currently supported:
-
-=over 4
-
-=item svcnum - primary key (assigned automatically for new services)
-
-=item pkgnum - Package (see L<FS::cust_pkg>)
-
-=item svcpart - Service definition (see L<FS::part_svc>)
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new service. To add the refund to the database, see L<"insert">.
-Services are normally created by creating FS::svc_ objects (see
-L<FS::svc_acct>, L<FS::svc_domain>, and L<FS::svc_acct_sm>, among others).
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_svc')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_svc',$hashref);
-}
-
-=item insert
-
-Adds this service to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Deletes this service from the database. If there is an error, returns the
-error, otherwise returns false.
-
-Called by the cancel method of the package (see L<FS::cust_pkg>).
-
-=cut
-
-sub delete {
- my($self)=@_;
- # anything else here?
- $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_svc record!" unless $old->table eq "cust_svc";
- return "Can't change svcnum!"
- unless $old->getfield('svcnum') eq $new->getfield('svcnum');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid service. If there is an error,
-returns the error, otehrwise returns false. Called by the insert and
-replace methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a cust_svc record!" unless $self->table eq "cust_svc";
- my($recref) = $self->hashref;
-
- $recref->{svcnum} =~ /^(\d*)$/ or return "Illegal svcnum";
- $recref->{svcnum}=$1;
-
- $recref->{pkgnum} =~ /^(\d*)$/ or return "Illegal pkgnum";
- $recref->{pkgnum}=$1;
- return "Unknown pkgnum" unless
- ! $recref->{pkgnum} ||
- qsearchs('cust_pkg',{'pkgnum'=>$recref->{pkgnum}});
-
- $recref->{svcpart} =~ /^(\d+)$/ or return "Illegal svcpart";
- $recref->{svcpart}=$1;
- return "Unknown svcpart" unless
- qsearchs('part_svc',{'svcpart'=>$recref->{svcpart}});
-
- ''; #no error
-}
-
-=back
-
-=head1 BUGS
-
-Behaviour of changing the svcpart of cust_svc records is undefined and should
-possibly be prohibited, and pkg_svc records are not checked.
-
-pkg_svc records are not checket in general (here).
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_pkg>, L<FS::part_svc>, L<FS::pkg_svc>,
-schema.html from the base documentation
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-10,14
-
-no TableUtil, no FS::Lock ivan@sisd.com 98-mar-7
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/dbdef.pm b/site_perl/dbdef.pm
deleted file mode 100644
index ac31bff..0000000
--- a/site_perl/dbdef.pm
+++ /dev/null
@@ -1,174 +0,0 @@
-package FS::dbdef;
-
-use strict;
-use vars qw(@ISA);
-use Exporter;
-use Carp;
-use FreezeThaw qw(freeze thaw cmpStr);
-use FS::dbdef_table;
-use FS::dbdef_unique;
-use FS::dbdef_index;
-use FS::dbdef_column;
-
-@ISA = qw(Exporter);
-
-=head1 NAME
-
-FS::dbdef - Database objects
-
-=head1 SYNOPSIS
-
- use FS::dbdef;
-
- $dbdef = new FS::dbdef (@dbdef_table_objects);
- $dbdef = load FS::dbdef "filename";
-
- $dbdef->save("filename");
-
- $dbdef->addtable($dbdef_table_object);
-
- @table_names = $dbdef->tables;
-
- $FS_dbdef_table_object = $dbdef->table;
-
-=head1 DESCRIPTION
-
-FS::dbdef objects are collections of FS::dbdef_table objects and represnt
-a database (a collection of tables).
-
-=head1 METHODS
-
-=over 4
-
-=item new TABLE, TABLE, ...
-
-Creates a new FS::dbdef object
-
-=cut
-
-sub new {
- my($proto,@tables)=@_;
- my(%tables)=map { $_->name, $_ } @tables; #check for duplicates?
-
- my($class) = ref($proto) || $proto;
- my($self) = {
- 'tables' => \%tables,
- };
-
- bless ($self, $class);
-
-}
-
-=item load FILENAME
-
-Loads an FS::dbdef object from a file.
-
-=cut
-
-sub load {
- my($proto,$file)=@_; #use $proto ?
- open(FILE,"<$file") or die "Can't open $file: $!";
- my($string)=join('',<FILE>); #can $string have newlines? pry not?
- close FILE or die "Can't close $file: $!";
- my($self)=thaw $string;
- #no bless needed?
- $self;
-}
-
-=item save FILENAME
-
-Saves an FS::dbdef object to a file.
-
-=cut
-
-sub save {
- my($self,$file)=@_;
- my($string)=freeze $self;
- open(FILE,">$file") or die "Can't open $file: $!";
- print FILE $string;
- close FILE or die "Can't close file: $!";
- my($check_self)=thaw $string;
- die "Verify error: Can't freeze and thaw dbdef $self"
- if (cmpStr($self,$check_self));
-}
-
-=item addtable TABLE
-
-Adds this FS::dbdef_table object.
-
-=cut
-
-sub addtable {
- my($self,$table)=@_;
- ${$self->{'tables'}}{$table->name}=$table; #check for dupliates?
-}
-
-=item tables
-
-Returns the names of all tables.
-
-=cut
-
-sub tables {
- my($self)=@_;
- keys %{$self->{'tables'}};
-}
-
-=item table TABLENAME
-
-Returns the named FS::dbdef_table object.
-
-=cut
-
-sub table {
- my($self,$table)=@_;
- $self->{'tables'}->{$table};
-}
-
-=head1 BUGS
-
-Each FS::dbdef object should have a name which corresponds to its name within
-the SQL database engine.
-
-=head1 SEE ALSO
-
-L<FS::dbdef_table>, L<FS::Record>,
-
-=head1 HISTORY
-
-beginning of abstraction into a class (not really)
-
-ivan@sisd.com 97-dec-4
-
-added primary_key
-ivan@sisd.com 98-jan-20
-
-added datatype (very kludgy and needs to be cleaned)
-ivan@sisd.com 98-feb-21
-
-perltrap (sigh) masked by mysql 3.20->3,21 ivan@sisd.com 98-mar-2
-
-Change 'type' to 'atype' in agent_type
-Changed attributes to special words which are changed in fs-setup
- ie. double(10,2) <=> MONEYTYPE
-Changed order of some of the field definitions because Pg6.3 is picky
-Changed 'day' to 'daytime' in cust_main
-Changed type of tax from tinyint to real
-Change 'password' to '_password' in svc_acct
-Pg6.3 does not allow 'field char(x) NULL'
- bmccane@maxbaud.net 98-apr-3
-
-rewrite: now properly OO. See also FS::dbdef_{table,column,unique,index}
-
-ivan@sisd.com 98-apr-17
-
-gained some extra functions ivan@sisd.com 98-may-11
-
-now knows how to Freeze and Thaw itself ivan@sisd.com 98-jun-2
-
-pod ivan@sisd.com 98-sep-23
-
-=cut
-
-1;
-
diff --git a/site_perl/dbdef_colgroup.pm b/site_perl/dbdef_colgroup.pm
deleted file mode 100644
index 64f2e30..0000000
--- a/site_perl/dbdef_colgroup.pm
+++ /dev/null
@@ -1,107 +0,0 @@
-package FS::dbdef_colgroup;
-
-use strict;
-use vars qw(@ISA);
-
-@ISA = qw(Exporter);
-
-=head1 NAME
-
-FS::dbdef_colgroup - Column group objects
-
-=head1 SYNOPSIS
-
- use FS::dbdef_colgroup;
-
- $colgroup = new FS::dbdef_colgroup ( $lol );
- $colgroup = new FS::dbdef_colgroup (
- [
- [ 'single_column' ],
- [ 'multiple_columns', 'another_column', ],
- ]
- );
-
- @sql_lists = $colgroup->sql_list;
-
- @singles = $colgroup->singles;
-
-=head1 DESCRIPTION
-
-FS::dbdef_colgroup objects represent sets of sets of columns.
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-Creates a new FS::dbdef_colgroup object.
-
-=cut
-
-sub new {
- my($proto, $lol) = @_;
-
- my $class = ref($proto) || $proto;
- my $self = {
- 'lol' => $lol,
- };
-
- bless ($self, $class);
-
-}
-
-=item sql_list
-
-Returns a flat list of comma-separated values, for SQL statements.
-
-=cut
-
-sub sql_list { #returns a flat list of comman-separates lists (for sql)
- my($self)=@_;
- grep $_ ne '', map join(', ', @{$_}), @{$self->{'lol'}};
-}
-
-=item singles
-
-Returns a flat list of all single item lists.
-
-=cut
-
-sub singles { #returns single-field groups as a flat list
- my($self)=@_;
- #map ${$_}[0], grep scalar(@{$_}) == 1, @{$self->{'lol'}};
- map {
- ${$_}[0] =~ /^(\w+)$/
- #aah!
- or die "Illegal column ", ${$_}[0], " in colgroup!";
- $1;
- } grep scalar(@{$_}) == 1, @{$self->{'lol'}};
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::dbdef_table>, L<FS::dbdef_unique>, L<FS::dbdef_index>,
-L<FS::dbdef_column>, L<FS::dbdef>, L<perldsc>
-
-=head1 HISTORY
-
-class for dealing with groups of groups of columns (used as a base class by
-FS::dbdef_{unique,index} )
-
-ivan@sisd.com 98-apr-19
-
-added singles, fixed sql_list to skip empty lists ivan@sisd.com 98-jun-2
-
-untaint things we're returning in sub singels ivan@sisd.com 98-jun-4
-
-pod ivan@sisd.com 98-sep-24
-
-=cut
-
-1;
-
diff --git a/site_perl/dbdef_column.pm b/site_perl/dbdef_column.pm
deleted file mode 100644
index 023b57d..0000000
--- a/site_perl/dbdef_column.pm
+++ /dev/null
@@ -1,175 +0,0 @@
-package FS::dbdef_column;
-
-use strict;
-#use Carp;
-use Exporter;
-use vars qw(@ISA);
-
-@ISA = qw(Exporter);
-
-=head1 NAME
-
-FS::dbdef_column - Column object
-
-=head1 SYNOPSIS
-
- use FS::dbdef_column;
-
- $column_object = new FS::dbdef_column ( $name, $sql_type, '' );
- $column_object = new FS::dbdef_column ( $name, $sql_type, 'NULL' );
- $column_object = new FS::dbdef_column ( $name, $sql_type, '', $length );
- $column_object = new FS::dbdef_column ( $name, $sql_type, 'NULL', $length );
-
- $name = $column_object->name;
- $column_object->name ( 'name' );
-
- $name = $column_object->type;
- $column_object->name ( 'sql_type' );
-
- $name = $column_object->null;
- $column_object->name ( 'NOT NULL' );
-
- $name = $column_object->length;
- $column_object->name ( $length );
-
- $sql_line = $column->line;
- $sql_line = $column->line $datasrc;
-
-=head1 DESCRIPTION
-
-FS::dbdef::column objects represend columns in tables (see L<FS::dbdef_table>).
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-Creates a new FS::dbdef_column object.
-
-=cut
-
-sub new {
- my($proto,$name,$type,$null,$length)=@_;
-
- #croak "Illegal name: $name" if grep $name eq $_, @reserved_words;
-
- $null =~ s/^NOT NULL$//i;
-
- my $class = ref($proto) || $proto;
- my $self = {
- 'name' => $name,
- 'type' => $type,
- 'null' => $null,
- 'length' => $length,
- };
-
- bless ($self, $class);
-
-}
-
-=item name
-
-Returns or sets the column name.
-
-=cut
-
-sub name {
- my($self,$value)=@_;
- if ( defined($value) ) {
- #croak "Illegal name: $name" if grep $name eq $_, @reserved_words;
- $self->{'name'} = $value;
- } else {
- $self->{'name'};
- }
-}
-
-=item type
-
-Returns or sets the column type.
-
-=cut
-
-sub type {
- my($self,$value)=@_;
- if ( defined($value) ) {
- $self->{'type'} = $value;
- } else {
- $self->{'type'};
- }
-}
-
-=item null
-
-Returns or sets the column null flag.
-
-=cut
-
-sub null {
- my($self,$value)=@_;
- if ( defined($value) ) {
- $value =~ s/^NOT NULL$//i;
- $self->{'null'} = $value;
- } else {
- $self->{'null'};
- }
-}
-
-=item type
-
-Returns or sets the column length.
-
-=cut
-
-sub length {
- my($self,$value)=@_;
- if ( defined($value) ) {
- $self->{'length'} = $value;
- } else {
- $self->{'length'};
- }
-}
-
-=item line [ $datasrc ]
-
-Returns an SQL column definition.
-
-If passed a DBI $datasrc specifying L<DBD::mysql>, will use MySQL-specific
-syntax. Non-standard syntax for other engines (if applicable) may also be
-supported in the future.
-
-=cut
-
-sub line {
- my($self,$datasrc)=@_;
- my($null)=$self->null;
- $null ||= "NOT NULL" if $datasrc =~ /mysql/; #yucky mysql hack
- join(' ',
- $self->name,
- $self->type. ( $self->length ? '('.$self->length.')' : '' ),
- $null,
- );
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::dbdef_table>, L<FS::dbdef>, L<DBI>
-
-=head1 HISTORY
-
-class for dealing with column definitions
-
-ivan@sisd.com 98-apr-17
-
-now methods can be used to get or set data ivan@sisd.com 98-may-11
-
-mySQL-specific hack for null (what should be default?) ivan@sisd.com 98-jun-2
-
-=cut
-
-1;
-
diff --git a/site_perl/dbdef_index.pm b/site_perl/dbdef_index.pm
deleted file mode 100644
index 2097db1..0000000
--- a/site_perl/dbdef_index.pm
+++ /dev/null
@@ -1,43 +0,0 @@
-package FS::dbdef_index;
-
-use strict;
-use vars qw(@ISA);
-use FS::dbdef_colgroup;
-
-@ISA=qw(FS::dbdef_colgroup);
-
-=head1 NAME
-
-FS::dbdef_unique.pm - Index object
-
-=head1 SYNOPSIS
-
- use FS::dbdef_index;
-
- # see FS::dbdef_colgroup methods
-
-=head1 DESCRIPTION
-
-FS::dbdef_unique objects represent the (non-unique) indices of a table
-(L<FS::dbdef_table>). FS::dbdef_unique inherits from FS::dbdef_colgroup.
-
-=head1 BUGS
-
-Is this empty subclass needed?
-
-=head1 SEE ALSO
-
-L<FS::dbdef_colgroup>, L<FS::dbdef_record>, L<FS::Record>
-
-=head1 HISTORY
-
-class for dealing with index definitions
-
-ivan@sisd.com 98-apr-19
-
-pod ivan@sisd.com 98-sep-24
-
-=cut
-
-1;
-
diff --git a/site_perl/dbdef_table.pm b/site_perl/dbdef_table.pm
deleted file mode 100644
index bc1454d..0000000
--- a/site_perl/dbdef_table.pm
+++ /dev/null
@@ -1,249 +0,0 @@
-package FS::dbdef_table;
-
-use strict;
-#use Carp;
-use Exporter;
-use vars qw(@ISA);
-use FS::dbdef_column;
-
-@ISA = qw(Exporter);
-
-=head1 NAME
-
-FS::dbdef_table - Table objects
-
-=head1 SYNOPSIS
-
- use FS::dbdef_table;
-
- $dbdef_table = new FS::dbdef_table (
- "table_name",
- "primary_key",
- $FS_dbdef_unique_object,
- $FS_dbdef_index_object,
- @FS_dbdef_column_objects,
- );
-
- $dbdef_table->addcolumn ( $FS_dbdef_column_object );
-
- $table_name = $dbdef_table->name;
- $dbdef_table->name ("table_name");
-
- $table_name = $dbdef_table->primary_keye;
- $dbdef_table->primary_key ("primary_key");
-
- $FS_dbdef_unique_object = $dbdef_table->unique;
- $dbdef_table->unique ( $FS_dbdef_unique_object );
-
- $FS_dbdef_index_object = $dbdef_table->index;
- $dbdef_table->index ( $FS_dbdef_index_object );
-
- @column_names = $dbdef->columns;
-
- $FS_dbdef_column_object = $dbdef->column;
-
- @sql_statements = $dbdef->sql_create_table;
- @sql_statements = $dbdef->sql_create_table $datasrc;
-
-=head1 DESCRIPTION
-
-FS::dbdef_table objects represent a single database table.
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-Creates a new FS::dbdef_table object.
-
-=cut
-
-sub new {
- my($proto,$name,$primary_key,$unique,$index,@columns)=@_;
-
- my(%columns) = map { $_->name, $_ } @columns;
-
- #check $primary_key, $unique and $index to make sure they are $columns ?
- # (and sanity check?)
-
- my $class = ref($proto) || $proto;
- my $self = {
- 'name' => $name,
- 'primary_key' => $primary_key,
- 'unique' => $unique,
- 'index' => $index,
- 'columns' => \%columns,
- };
-
- bless ($self, $class);
-
-}
-
-=item addcolumn
-
-Adds this FS::dbdef_column object.
-
-=cut
-
-sub addcolumn {
- my($self,$column)=@_;
- ${$self->{'columns'}}{$column->name}=$column; #sanity check?
-}
-
-=item name
-
-Returns or sets the table name.
-
-=cut
-
-sub name {
- my($self,$value)=@_;
- if ( defined($value) ) {
- $self->{name} = $value;
- } else {
- $self->{name};
- }
-}
-
-=item primary_key
-
-Returns or sets the primary key.
-
-=cut
-
-sub primary_key {
- my($self,$value)=@_;
- if ( defined($value) ) {
- $self->{primary_key} = $value;
- } else {
- #$self->{primary_key};
- #hmm. maybe should untaint the entire structure when it comes off disk
- # cause if you don't trust that, ?
- $self->{primary_key} =~ /^(\w*)$/
- #aah!
- or die "Illegal primary key ", $self->{primary_key}, " in dbdef!\n";
- $1;
- }
-}
-
-=item unique
-
-Returns or sets the FS::dbdef_unique object.
-
-=cut
-
-sub unique {
- my($self,$value)=@_;
- if ( defined($value) ) {
- $self->{unique} = $value;
- } else {
- $self->{unique};
- }
-}
-
-=item index
-
-Returns or sets the FS::dbdef_index object.
-
-=cut
-
-sub index {
- my($self,$value)=@_;
- if ( defined($value) ) {
- $self->{'index'} = $value;
- } else {
- $self->{'index'};
- }
-}
-
-=item columns
-
-Returns a list consisting of the names of all columns.
-
-=cut
-
-sub columns {
- my($self)=@_;
- keys %{$self->{'columns'}};
-}
-
-=item column "column"
-
-Returns the column object (see L<FS::dbdef_column>) for "column".
-
-=cut
-
-sub column {
- my($self,$column)=@_;
- $self->{'columns'}->{$column};
-}
-
-=item sql_create_table [ $datasrc ]
-
-Returns an array of SQL statments to create this table.
-
-If passed a DBI $datasrc specifying L<DBD::mysql>, will use MySQL-specific
-syntax. Non-standard syntax for other engines (if applicable) may also be
-supported in the future.
-
-=cut
-
-sub sql_create_table {
- my($self,$datasrc)=@_;
-
- my(@columns)=map { $self->column($_)->line($datasrc) } $self->columns;
- push @columns, "PRIMARY KEY (". $self->primary_key. ")"
- if $self->primary_key;
- if ( $datasrc =~ /mysql/ ) { #yucky mysql hack
- push @columns, map "UNIQUE ($_)", $self->unique->sql_list;
- push @columns, map "INDEX ($_)", $self->index->sql_list;
- }
-
- "CREATE TABLE ". $self->name. " ( ". join(", ", @columns). " )",
- ( map {
- my($index) = $_ . "_index";
- $index =~ s/,\s*/_/g;
- "CREATE UNIQUE INDEX $index ON ". $self->name. " ($_)"
- } $self->unique->sql_list ),
- ( map {
- my($index) = $_ . "_index";
- $index =~ s/,\s*/_/g;
- "CREATE INDEX $index ON ". $self->name. " ($_)"
- } $self->index->sql_list ),
- ;
-
-
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::dbdef>, L<FS::dbdef_unique>, L<FS::dbdef_index>, L<FS::dbdef_unique>,
-L<DBI>
-
-=head1 HISTORY
-
-class for dealing with table definitions
-
-ivan@sisd.com 98-apr-18
-
-gained extra functions (should %columns be an IxHash?)
-ivan@sisd.com 98-may-11
-
-sql_create_table returns a list of statments, not just one, and now it
-does indices (plus mysql hack) ivan@sisd.com 98-jun-2
-
-untaint primary_key... hmm. is this a hack around a bigger problem?
-looks like, did the same thing singles in colgroup!
-ivan@sisd.com 98-jun-4
-
-pod ivan@sisd.com 98-sep-24
-
-=cut
-
-1;
-
diff --git a/site_perl/dbdef_unique.pm b/site_perl/dbdef_unique.pm
deleted file mode 100644
index 4ec40de..0000000
--- a/site_perl/dbdef_unique.pm
+++ /dev/null
@@ -1,44 +0,0 @@
-package FS::dbdef_unique;
-
-use strict;
-use vars qw(@ISA);
-use FS::dbdef_colgroup;
-
-@ISA=qw(FS::dbdef_colgroup);
-
-=head1 NAME
-
-FS::dbdef_unique.pm - Unique object
-
-=head1 SYNOPSIS
-
- use FS::dbdef_unique;
-
- # see FS::dbdef_colgroup methods
-
-=head1 DESCRIPTION
-
-FS::dbdef_unique objects represent the unique indices of a database table
-(L<FS::dbdef_table>). FS::dbdef_unique inherits from FS::dbdef_colgroup.
-
-=head1 BUGS
-
-Is this empty subclass needed?
-
-=head1 SEE ALSO
-
-L<FS::dbdef_colgroup>, L<FS::dbdef_record>, L<FS::Record>
-
-=head1 HISTORY
-
-class for dealing with unique definitions
-
-ivan@sisd.com 98-apr-19
-
-pod ivan@sisd.com 98-sep-24
-
-=cut
-
-1;
-
-
diff --git a/site_perl/part_pkg.pm b/site_perl/part_pkg.pm
deleted file mode 100644
index d1c12e4..0000000
--- a/site_perl/part_pkg.pm
+++ /dev/null
@@ -1,168 +0,0 @@
-package FS::part_pkg;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields hfields);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(hfields fields);
-
-=head1 NAME
-
-FS::part_pkg - Object methods for part_pkg objects
-
-=head1 SYNOPSIS
-
- use FS::part_pkg;
-
- $record = create FS::part_pkg \%hash
- $record = create FS::part_pkg { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::part_pkg represents a billing item definition. FS::part_pkg inherits
-from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item pkgpart - primary key (assigned automatically for new billing item definitions)
-
-=item pkg - Text name of this billing item definition (customer-viewable)
-
-=item comment - Text name of this billing item definition (non-customer-viewable)
-
-=item setup - Setup fee
-
-=item freq - Frequency of recurring fee
-
-=item recur - Recurring fee
-
-=back
-
-setup and recur are evaluated as Safe perl expressions. You can use numbers
-just as you would normally. More advanced semantics are not yet defined.
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new billing item definition. To add the billing item definition to
-the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('part_pkg')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('part_pkg',$hashref);
-}
-
-=item insert
-
-Adds this billing item definition to the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented.
-
-=cut
-
-sub delete {
- return "Can't (yet?) delete package definitions.";
-# maybe check & make sure the pkgpart isn't in cust_pkg or type_pkgs?
-# my($self)=@_;
-#
-# $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a part_pkg record!" unless $old->table eq "part_pkg";
- return "Can't change pkgpart!"
- unless $old->getfield('pkgpart') eq $new->getfield('pkgpart');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid billing item definition. If
-there is an error, returns the error, otherwise returns false. Called by the
-insert and replace methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a part_pkg record!" unless $self->table eq "part_pkg";
-
- $self->ut_numbern('pkgpart')
- or $self->ut_text('pkg')
- or $self->ut_text('comment')
- or $self->ut_anything('setup')
- or $self->ut_number('freq')
- or $self->ut_anything('recur')
- ;
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-The delete method is unimplemented.
-
-setup and recur semantics are not yet defined (and are implemented in
-FS::cust_bill. hmm.).
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_pkg>, L<FS::type_pkgs>, L<FS::pkg_svc>, L<Safe>.
-schema.html from the base documentation.
-
-=head1 HISTORY
-
-ivan@sisd.com 97-dec-5
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/part_referral.pm b/site_perl/part_referral.pm
deleted file mode 100644
index 1b4a1b6..0000000
--- a/site_perl/part_referral.pm
+++ /dev/null
@@ -1,155 +0,0 @@
-package FS::part_referral;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::part_referral - Object methods for part_referral objects
-
-=head1 SYNOPSIS
-
- use FS::part_referral;
-
- $record = create FS::part_referral \%hash
- $record = create FS::part_referral { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::part_referral represents a referral - where a customer heard of your
-services. This can be used to track the effectiveness of a particular piece of
-advertising, for example. FS::part_referral inherits from FS::Record. The
-following fields are currently supported:
-
-=over 4
-
-=item refnum - primary key (assigned automatically for new referrals)
-
-=item referral - Text name of this referral
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new referral. To add the referral to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('part_referral')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('part_referral',$hashref);
-}
-
-=item insert
-
-Adds this referral to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented.
-
-=cut
-
-sub delete {
- my($self)=@_;
- return "Can't (yet?) delete part_referral records";
- #$self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not an part_referral record!"
- unless $old->table eq "part_referral";
- return "Can't change refnum!"
- unless $old->getfield('refnum') eq $new->getfield('refnum');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid referral. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a part_referral record!" unless $self->table eq "part_referral";
-
- my($error)=
- $self->ut_numbern('refnum')
- or $self->ut_text('referral')
- ;
- return $error if $error;
-
- '';
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-The delete method is unimplemented.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_main>, schema.html from the base documentation.
-
-=head1 HISTORY
-
-Class dealing with referrals
-
-ivan@sisd.com 98-feb-23
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/part_svc.pm b/site_perl/part_svc.pm
deleted file mode 100644
index 0fd8ee4..0000000
--- a/site_perl/part_svc.pm
+++ /dev/null
@@ -1,199 +0,0 @@
-package FS::part_svc;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields hfields);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(hfields fields);
-
-=head1 NAME
-
-FS::part_svc - Object methods for part_svc objects
-
-=head1 SYNOPSIS
-
- use FS::part_svc;
-
- $record = create FS::part_referral \%hash
- $record = create FS::part_referral { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::part_svc represents a service definition. FS::part_svc inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item svcpart - primary key (assigned automatically for new service definitions)
-
-=item svc - text name of this service definition
-
-=item svcdb - table used for this service. See L<FS::svc_acct>,
-L<FS::svc_domain>, and L<FS::svc_acct_sm>, among others.
-
-=item I<svcdb>__I<field> - Default or fixed value for I<field> in I<svcdb>.
-
-=item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null, `D' for default, or `F' for fixed
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new service definition. To add the service definition to the
-database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('part_svc')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('part_svc',$hashref);
-}
-
-=item insert
-
-Adds this service definition to the database. If there is an error, returns
-the error, otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented.
-
-=cut
-
-sub delete {
- return "Can't (yet?) delete service definitions.";
-# maybe check & make sure the svcpart isn't in cust_svc or (in any packages)?
-# my($self)=@_;
-#
-# $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a part_svc record!" unless $old->table eq "part_svc";
- return "Can't change svcpart!"
- unless $old->getfield('svcpart') eq $new->getfield('svcpart');
- return "Can't change svcdb!"
- unless $old->getfield('svcdb') eq $new->getfield('svcdb');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid service definition. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a part_svc record!" unless $self->table eq "part_svc";
- my($recref) = $self->hashref;
-
- my($error);
- return $error if $error=
- $self->ut_numbern('svcpart')
- || $self->ut_text('svc')
- || $self->ut_alpha('svcdb')
- ;
-
- my(@fields) = eval { fields($recref->{svcdb}) }; #might die
- return "Unknown svcdb!" unless @fields;
-
- my($svcdb);
- foreach $svcdb ( qw(
- svc_acct svc_acct_sm svc_charge svc_domain svc_wo
- ) ) {
- my(@rows)=map { /^${svcdb}__(.*)$/; $1 }
- grep ! /_flag$/,
- grep /^${svcdb}__/,
- fields('part_svc');
- my($row);
- foreach $row (@rows) {
- unless ( $svcdb eq $recref->{svcdb} ) {
- $recref->{$svcdb.'__'.$row}='';
- $recref->{$svcdb.'__'.$row.'_flag'}='';
- next;
- }
- $recref->{$svcdb.'__'.$row.'_flag'} =~ /^([DF]?)$/
- or return "Illegal flag for $svcdb $row";
- $recref->{$svcdb.'__'.$row.'_flag'} = $1;
-
-# $recref->{$svcdb.'__'.$row} =~ /^(.*)$/ #not restrictive enough?
-# or return "Illegal value for $svcdb $row";
-# $recref->{$svcdb.'__'.$row} = $1;
- my($error);
- return $error if $error=$self->ut_anything($svcdb.'__'.$row);
-
- }
- }
-
- ''; #no error
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-Delete is unimplemented.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::part_pkg>, L<FS::pkg_svc>, L<FS::cust_svc>,
-L<FS::svc_acct>, L<FS::svc_acct_sm>, L<FS::svc_domain>, schema.html from the
-base documentation.
-
-=head1 HISTORY
-
-ivan@sisd.com 97-nov-14
-
-data checking/untainting calls into FS::Record added
-ivan@sisd.com 97-dec-6
-
-pod ivan@sisd.com 98-sep-21
-
-=cut
-
-1;
-
diff --git a/site_perl/pkg_svc.pm b/site_perl/pkg_svc.pm
deleted file mode 100644
index 517125c..0000000
--- a/site_perl/pkg_svc.pm
+++ /dev/null
@@ -1,168 +0,0 @@
-package FS::pkg_svc;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields hfields qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(hfields);
-
-=head1 NAME
-
-FS::pkg_svc - Object methods for pkg_svc records
-
-=head1 SYNOPSIS
-
- use FS::pkg_svc;
-
- $record = create FS::pkg_svc \%hash;
- $record = create FS::pkg_svc { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::pkg_svc record links a billing item definition (see L<FS::part_pkg>) to
-a service definition (see L<FS::part_svc>). FS::pkg_svc inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item pkgpart - Billing item definition (see L<FS::part_pkg>)
-
-=item svcpart - Service definition (see L<FS::part_svc>)
-
-=item quantity - Quantity of this service definition that this billing item
-definition includes
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Create a new record. To add the record to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('pkg_svc')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('pkg_svc',$hashref);
-
-}
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=cut
-
-sub delete {
- my($self)=@_;
-
- $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a pkg_svc record!" unless $old->table eq "pkg_svc";
- return "Can't change pkgpart!"
- if $old->getfield('pkgpart') ne $new->getfield('pkgpart');
- return "Can't change svcpart!"
- if $old->getfield('svcpart') ne $new->getfield('svcpart');
-
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid record. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a pkg_svc record!" unless $self->table eq "pkg_svc";
- my($recref) = $self->hashref;
-
- my($error);
- return $error if $error =
- $self->ut_number('pkgpart')
- || $self->ut_number('svcpart')
- || $self->ut_number('quantity')
- ;
-
- return "Unknown pkgpart!"
- unless qsearchs('part_pkg',{'pkgpart'=> $self->getfield('pkgpart')});
-
- return "Unknown svcpart!"
- unless qsearchs('part_svc',{'svcpart'=> $self->getfield('svcpart')});
-
- ''; #no error
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::part_pkg>, L<FS::part_svc>, schema.html from the base
-documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-1
-
-added hfields
-ivan@sisd.com 97-nov-13
-
-pod ivan@sisd.com 98-sep-22
-
-=cut
-
-1;
-
diff --git a/site_perl/svc_acct.pm b/site_perl/svc_acct.pm
deleted file mode 100644
index a43af6b..0000000
--- a/site_perl/svc_acct.pm
+++ /dev/null
@@ -1,557 +0,0 @@
-package FS::svc_acct;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK $nossh_hack $conf $dir_prefix @shells
- $shellmachine @saltset @pw_set);
-use Exporter;
-use FS::Conf;
-use FS::Record qw(fields qsearchs);
-use FS::SSH qw(ssh);
-use FS::cust_svc;
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-$conf = new FS::Conf;
-$dir_prefix = $conf->config('home');
-@shells = $conf->config('shells');
-$shellmachine = $conf->config('shellmachine');
-
-@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
-@pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '!', '.', ',' );
-
-#not needed in 5.004 #srand($$|time);
-
-=head1 NAME
-
-FS::svc_acct - Object methods for svc_acct records
-
-=head1 SYNOPSIS
-
- use FS::svc_acct;
-
- $record = create FS::svc_acct \%hash;
- $record = create FS::svc_acct { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-=head1 DESCRIPTION
-
-An FS::svc_acct object represents an account. FS::svc_acct inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item svcnum - primary key (assigned automatcially for new accounts)
-
-=item username
-
-=item _password - generated if blank
-
-=item popnum - Point of presence (see L<FS::svc_acct_pop>)
-
-=item uid
-
-=item gid
-
-=item finger - GECOS
-
-=item dir - set automatically if blank (and uid is not)
-
-=item shell
-
-=item quota - (unimplementd)
-
-=item slipip - IP address
-
-=item radius_I<Radius_Attribute> - I<Radius-Attribute>
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new account. To add the account to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('svc_acct')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('svc_acct',$hashref);
-
-}
-
-=item insert
-
-Adds this account to the database. If there is an error, returns the error,
-otherwise returns false.
-
-The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
-defined. An FS::cust_svc record will be created and inserted.
-
-If the configuration value (see L<FS::Conf>) shellmachine exists, and the
-username, uid, and dir fields are defined, the command
-
- useradd -d $dir -m -s $shell -u $uid $username
-
-is executed on shellmachine via ssh. This behaviour can be surpressed by
-setting $FS::svc_acct::nossh_hack true.
-
-=cut
-
-sub insert {
- my($self)=@_;
- my($error);
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error=$self->check;
- return $error if $error;
-
- return "Username ". $self->username. " in use"
- if qsearchs('svc_acct',{'username'=> $self->username } );
-
- my($part_svc) = qsearchs('part_svc',{ 'svcpart' => $self->svcpart });
- return "Unkonwn svcpart" unless $part_svc;
- return "uid in use"
- if $part_svc->svc_acct__uid_flag ne 'F'
- && qsearchs('svc_acct',{'uid'=> $self->uid } )
- && $self->username !~ /^(hyla)?fax$/
- ;
-
- my($svcnum)=$self->svcnum;
- my($cust_svc);
- unless ( $svcnum ) {
- $cust_svc=create FS::cust_svc ( {
- 'svcnum' => $svcnum,
- 'pkgnum' => $self->pkgnum,
- 'svcpart' => $self->svcpart,
- } );
- my($error) = $cust_svc->insert;
- return $error if $error;
- $svcnum = $self->svcnum($cust_svc->svcnum);
- }
-
- $error = $self->add;
- if ($error) {
- #$cust_svc->del if $cust_svc;
- $cust_svc->delete if $cust_svc;
- return $error;
- }
-
- my($username,$uid,$dir,$shell) = (
- $self->username,
- $self->uid,
- $self->dir,
- $self->shell,
- );
- if ( $username
- && $uid
- && $dir
- && $shellmachine
- && ! $nossh_hack ) {
- #one way
- ssh("root\@$shellmachine",
- "useradd -d $dir -m -s $shell -u $uid $username"
- );
- #another way
- #ssh("root\@$shellmachine","/bin/mkdir $dir; /bin/chmod 711 $dir; ".
- # "/bin/cp -p /etc/skel/.* $dir 2>/dev/null; ".
- # "/bin/cp -pR /etc/skel/Maildir $dir 2>/dev/null; ".
- # "/bin/chown -R $uid $dir") unless $nossh_hack;
- }
-
- ''; #no error
-}
-
-=item delete
-
-Deletes this account from the database. If there is an error, returns the
-error, otherwise returns false.
-
-The corresponding FS::cust_svc record will be deleted as well.
-
-If the configuration value (see L<FS::Conf>) shellmachine exists, the command:
-
- userdel $username
-
-is executed on shellmachine via ssh. This behaviour can be surpressed by
-setting $FS::svc_acct::nossh_hack true.
-
-=cut
-
-sub delete {
- my($self)=@_;
- my($error);
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- my($svcnum)=$self->getfield('svcnum');
-
- $error = $self->del;
- return $error if $error;
-
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
- $error = $cust_svc->del;
- return $error if $error;
-
- my($username) = $self->getfield('username');
- if ( $username && $shellmachine && ! $nossh_hack ) {
- ssh("root\@$shellmachine","userdel $username");
- }
-
- '';
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-If the configuration value (see L<FS::Conf>) shellmachine exists, and the
-dir field has changed, the command:
-
- [ -d $old_dir ] && (
- chmod u+t $old_dir;
- umask 022;
- mkdir $new_dir;
- cd $old_dir;
- find . -depth -print | cpio -pdm $new_dir;
- chmod u-t $new_dir;
- chown -R $uid.$gid $new_dir;
- rm -rf $old_dir
- )
-
-is executed on shellmachine via ssh. This behaviour can be surpressed by
-setting $FS::svc_acct::nossh_hack true.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- my($error);
-
- return "(Old) Not a svc_acct record!" unless $old->table eq "svc_acct";
- return "Can't change svcnum!"
- unless $old->getfield('svcnum') eq $new->getfield('svcnum');
-
- return "Username in use"
- if $old->getfield('username') ne $new->getfield('username') &&
- qsearchs('svc_acct',{'username'=> $new->getfield('username') } );
-
- return "Can't change uid!"
- if $old->getfield('uid') ne $new->getfield('uid');
-
- #change homdir when we change username
- if ( $old->getfield('username') ne $new->getfield('username') ) {
- $new->setfield('dir','');
- }
-
- $error=$new->check;
- return $error if $error;
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error = $new->rep($old);
- return $error if $error;
-
- my($old_dir,$new_dir)=( $old->getfield('dir'),$new->getfield('dir') );
- my($uid,$gid)=( $new->getfield('uid'), $new->getfield('gid') );
- if ( $old_dir
- && $new_dir
- && $old_dir ne $new_dir
- && ! $nossh_hack
- ) {
- ssh("root\@$shellmachine","[ -d $old_dir ] && ".
- "( chmod u+t $old_dir; ". #turn off qmail delivery
- "umask 022; mkdir $new_dir; cd $old_dir; ".
- "find . -depth -print | cpio -pdm $new_dir; ".
- "chmod u-t $new_dir; chown -R $uid.$gid $new_dir; ".
- "rm -rf $old_dir".
- ")"
- );
- }
-
- ''; #no error
-}
-
-=item suspend
-
-Suspends this account by prefixing *SUSPENDED* to the password. If there is an
-error, returns the error, otherwise returns false.
-
-Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub suspend {
- my($old) = @_;
- my(%hash) = $old->hash;
- unless ( $hash{_password} =~ /^\*SUSPENDED\* / ) {
- $hash{_password} = '*SUSPENDED* '.$hash{_password};
- my($new) = create FS::svc_acct ( \%hash );
-# $new->replace($old);
- $new->rep($old); #to avoid password checking :)
- } else {
- ''; #no error (already suspended)
- }
-
-}
-
-=item unsuspend
-
-Unsuspends this account by removing *SUSPENDED* from the password. If there is
-an error, returns the error, otherwise returns false.
-
-Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub unsuspend {
- my($old) = @_;
- my(%hash) = $old->hash;
- if ( $hash{_password} =~ /^\*SUSPENDED\* (.*)$/ ) {
- $hash{_password} = $1;
- my($new) = create FS::svc_acct ( \%hash );
-# $new->replace($old);
- $new->rep($old); #to avoid password checking :)
- } else {
- ''; #no error (already unsuspended)
- }
-}
-
-=item cancel
-
-Just returns false (no error) for now.
-
-Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-# Usage: $error = $record -> cancel;
-sub cancel {
- ''; #stub (no error) - taken care of in delete
-}
-
-=item check
-
-Checks all fields to make sure this is a valid service. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-Sets any fixed values; see L<FS::part_svc>.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a svc_acct record!" unless $self->table eq "svc_acct";
- my($recref) = $self->hashref;
-
- $recref->{svcnum} =~ /^(\d*)$/ or return "Illegal svcnum";
- $recref->{svcnum} = $1;
-
- #get part_svc
- my($svcpart);
- my($svcnum)=$self->getfield('svcnum');
- if ($svcnum) {
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
- return "Unknown svcnum" unless $cust_svc;
- $svcpart=$cust_svc->svcpart;
- } else {
- $svcpart=$self->getfield('svcpart');
- }
- my($part_svc)=qsearchs('part_svc',{'svcpart'=>$svcpart});
- return "Unkonwn svcpart" unless $part_svc;
-
- #set fixed fields from part_svc
- my($field);
- foreach $field ( fields('svc_acct') ) {
- if ( $part_svc->getfield('svc_acct__'. $field. '_flag') eq 'F' ) {
- $self->setfield($field,$part_svc->getfield('svc_acct__'. $field) );
- }
- }
-
- my($ulen)=$self->dbdef_table->column('username')->length;
- $recref->{username} =~ /^([a-z0-9_\-]{2,$ulen})$/
- or return "Illegal username";
- $recref->{username} = $1;
- $recref->{username} =~ /[a-z]/ or return "Illegal username";
-
- $recref->{popnum} =~ /^(\d*)$/ or return "Illegal popnum";
- $recref->{popnum} = $1;
- return "Unkonwn popnum" unless
- ! $recref->{popnum} ||
- qsearchs('svc_acct_pop',{'popnum'=> $recref->{popnum} } );
-
- unless ( $part_svc->getfield('svc_acct__uid_flag') eq 'F' ) {
-
- $recref->{uid} =~ /^(\d*)$/ or return "Illegal uid";
- $recref->{uid} = $1 eq '' ? $self->unique('uid') : $1;
-
- $recref->{gid} =~ /^(\d*)$/ or return "Illegal gid";
- $recref->{gid} = $1 eq '' ? $recref->{uid} : $1;
- #not all systems use gid=uid
- #you can set a fixed gid in part_svc
-
- return "Only root can have uid 0"
- if $recref->{uid} == 0 && $recref->{username} ne 'root';
-
- my($error);
- return $error if $error=$self->ut_textn('finger');
-
- $recref->{dir} =~ /^([\/\w\-]*)$/
- or return "Illegal directory";
- $recref->{dir} = $1 ||
- $dir_prefix . '/' . $recref->{username}
- #$dir_prefix . '/' . substr($recref->{username},0,1). '/' . $recref->{username}
- ;
-
- unless ( $recref->{username} eq 'sync' ) {
- my($shell);
- if ( $shell = (grep $_ eq $recref->{shell}, @shells)[0] ) {
- $recref->{shell} = $shell;
- } else {
- return "Illegal shell ". $self->shell;
- }
- } else {
- $recref->{shell} = '/bin/sync';
- }
-
- $recref->{quota} =~ /^(\d*)$/ or return "Illegal quota (unimplemented)";
- $recref->{quota} = $1;
-
- } else {
- $recref->{gid} ne '' ?
- return "Can't have gid without uid" : ( $recref->{gid}='' );
- $recref->{finger} ne '' ?
- return "Can't have finger-name without uid" : ( $recref->{finger}='' );
- $recref->{dir} ne '' ?
- return "Can't have directory without uid" : ( $recref->{dir}='' );
- $recref->{shell} ne '' ?
- return "Can't have shell without uid" : ( $recref->{shell}='' );
- $recref->{quota} ne '' ?
- return "Can't have quota without uid" : ( $recref->{quota}='' );
- }
-
- unless ( $part_svc->getfield('svc_acct__slipip_flag') eq 'F' ) {
- unless ( $recref->{slipip} eq '0e0' ) {
- $recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/
- or return "Illegal slipip". $self->slipip;
- $recref->{slipip} = $1;
- } else {
- $recref->{slipip} = '0e0';
- }
-
- }
-
- #arbitrary RADIUS stuff; allow ut_textn for now
- foreach ( grep /^radius_/, fields('svc_acct') ) {
- $self->ut_textn($_);
- }
-
- #generate a password if it is blank
- $recref->{_password} = join('',map($pw_set[ int(rand $#pw_set) ], (0..7) ) )
- unless ( $recref->{_password} );
-
- #if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{4,16})$/ ) {
- if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{4,8})$/ ) {
- $recref->{_password} = $1.$3;
- #uncomment this to encrypt password immediately upon entry, or run
- #bin/crypt_pw in cron to give new users a window during which their
- #password is available to techs, for faxing, etc. (also be aware of
- #radius issues!)
- #$recref->{password} = $1.
- # crypt($3,$saltset[int(rand(64))].$saltset[int(rand(64))]
- #;
- } elsif ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([\w\.\/]{13,24})$/ ) {
- $recref->{_password} = $1.$3;
- } elsif ( $recref->{_password} eq '*' ) {
- $recref->{_password} = '*';
- } else {
- return "Illegal password";
- }
-
- ''; #no error
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-The remote commands should be configurable.
-
-The create method should set defaults from part_svc (like the check method
-sets fixed values).
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>,
-L<FS::SSH>, L<ssh>, L<FS::svc_acct_pop>, schema.html from the base
-documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-16 - 21
-
-rewrite (among other things, now know about part_svc) ivan@sisd.com 98-mar-8
-
-Changed 'password' to '_password' because Pg6.3 reserves the password word
- bmccane@maxbaud.net 98-apr-3
-
-username length and shell no longer hardcoded ivan@sisd.com 98-jun-28
-
-eww but needed: ignore uid duplicates for 'fax' and 'hylafax'
-ivan@sisd.com 98-jun-29
-
-$nossh_hack ivan@sisd.com 98-jul-13
-
-protections against UID/GID of 0 for incorrectly-setup RDBMSs (also
-in bin/svc_acct.export) ivan@sisd.com 98-jul-13
-
-arbitrary radius attributes ivan@sisd.com 98-aug-13
-
-/var/spool/freeside/conf/shellmachine ivan@sisd.com 98-aug-13
-
-pod and FS::conf ivan@sisd.com 98-sep-22
-
-=cut
-
-1;
-
diff --git a/site_perl/svc_acct_pop.pm b/site_perl/svc_acct_pop.pm
deleted file mode 100644
index a6f801f..0000000
--- a/site_perl/svc_acct_pop.pm
+++ /dev/null
@@ -1,163 +0,0 @@
-package FS::svc_acct_pop;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::svc_acct_pop - Object methods for svc_acct_pop records
-
-=head1 SYNOPSIS
-
- use FS::svc_acct_pop;
-
- $record = create FS::svc_acct_pop \%hash;
- $record = create FS::svc_acct_pop { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::svc_acct object represents an point of presence. FS::svc_acct_pop
-inherits from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item popnum - primary key (assigned automatically for new accounts)
-
-=item city
-
-=item state
-
-=item ac - area code
-
-=item exch - exchange
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new point of presence (if only it were that easy!). To add the
-point of presence to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('svc_acct_pop')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('svc_acct_pop',$hashref);
-}
-
-=item insert
-
-Adds this point of presence to the databaes. If there is an error, returns the
-error, otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Currently unimplemented.
-
-=cut
-
-sub delete {
- my($self)=@_;
- return "Can't (yet) delete POPs!";
- #$self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not an svc_acct_pop record!"
- unless $old->table eq "svc_acct_pop";
- return "Can't change popnum!"
- unless $old->getfield('popnum') eq $new->getfield('popnum');
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid point of presence. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a svc_acct_pop record!" unless $self->table eq "svc_acct_pop";
-
- my($error)=
- $self->ut_numbern('popnum')
- or $self->ut_text('city')
- or $self->ut_text('state')
- or $self->ut_number('ac')
- or $self->ut_number('exch')
- ;
- return $error if $error;
-
- '';
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-It should be renamed to part_pop.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<svc_acct>, schema.html from the base documentation.
-
-=head1 HISTORY
-
-Class dealing with pops
-
-ivan@sisd.com 98-mar-8
-
-pod ivan@sisd.com 98-sep-23
-
-=cut
-
-1;
-
diff --git a/site_perl/svc_acct_sm.pm b/site_perl/svc_acct_sm.pm
deleted file mode 100644
index c87ed2c..0000000
--- a/site_perl/svc_acct_sm.pm
+++ /dev/null
@@ -1,350 +0,0 @@
-package FS::svc_acct_sm;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK $nossh_hack $conf $shellmachine @qmailmachines);
-use Exporter;
-use FS::Record qw(fields qsearch qsearchs);
-use FS::cust_svc;
-use FS::SSH qw(ssh);
-use FS::Conf;
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-$conf = new FS::Conf;
-
-$shellmachine = $conf->exists('qmailmachines')
- ? $conf->config('shellmachine')
- : '';
-
-=head1 NAME
-
-FS::svc_acct_sm - Object methods for svc_acct_sm records
-
-=head1 SYNOPSIS
-
- use FS::svc_acct_sm;
-
- $record = create FS::svc_acct_sm \%hash;
- $record = create FS::svc_acct_sm { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-=head1 DESCRIPTION
-
-An FS::svc_acct object represents a virtual mail alias. FS::svc_acct inherits
-from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item svcnum - primary key (assigned automatcially for new accounts)
-
-=item domsvc - svcnum of the virtual domain (see L<FS::svc_domain>)
-
-=item domuid - uid of the target account (see L<FS::svc_acct>)
-
-=item domuser - virtual username
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new virtual mail alias. To add the virtual mail alias to the
-database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('svc_acct_sm')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('svc_acct_sm',$hashref);
-
-}
-
-=item insert
-
-Adds this virtual mail alias to the database. If there is an error, returns
-the error, otherwise returns false.
-
-The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
-defined. An FS::cust_svc record will be created and inserted.
-
-If the configuration values (see L<FS::Conf>) shellmachine and qmailmachines
-exist, and domuser is `*' (meaning a catch-all mailbox), the command:
-
- [ -e $dir/.qmail-$qdomain-default ] || {
- touch $dir/.qmail-$qdomain-default;
- chown $uid:$gid $dir/.qmail-$qdomain-default;
- }
-
-is executed on shellmachine via ssh (see L<dot-qmail/"EXTENSION ADDRESSES">).
-This behaviour can be surpressed by setting $FS::svc_acct_sm::nossh_hack true.
-
-=cut
-
-sub insert {
- my($self)=@_;
- my($error);
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error=$self->check;
- return $error if $error;
-
- return "Domain username (domuser) in use for this domain (domsvc)"
- if qsearchs('svc_acct_sm',{ 'domuser'=> $self->domuser,
- 'domsvc' => $self->domsvc,
- } );
-
- return "First domain username (domuser) for domain (domsvc) must be " .
- qq='*' (catch-all)!=
- if $self->domuser ne '*' &&
- ! qsearch('svc_acct_sm',{ 'domsvc' => $self->domsvc } );
-
- my($svcnum)=$self->getfield('svcnum');
- my($cust_svc);
- unless ( $svcnum ) {
- $cust_svc=create FS::cust_svc ( {
- 'svcnum' => $svcnum,
- 'pkgnum' => $self->getfield('pkgnum'),
- 'svcpart' => $self->getfield('svcpart'),
- } );
- my($error) = $cust_svc->insert;
- return $error if $error;
- $svcnum = $self->setfield('svcnum',$cust_svc->getfield('svcnum'));
- }
-
- $error = $self->add;
- if ($error) {
- $cust_svc->del if $cust_svc;
- return $error;
- }
-
- my $svc_domain = qsearchs('svc_domain',{'svcnum'=> $self->domsvc } );
- my $svc_acct = qsearchs('svc_acct',{'uid'=> $self->domuid } );
- my($uid,$gid,$dir,$domain)=(
- $svc_acct->getfield('uid'),
- $svc_acct->getfield('gid'),
- $svc_acct->getfield('dir'),
- $svc_domain->getfield('domain')
- );
- my($qdomain)=$domain;
- $qdomain =~ s/\./:/g; #see manpage for 'dot-qmail': EXTENSION ADDRESSES
- ssh("root\@$shellmachine","[ -e $dir/.qmail-$qdomain-default ] || { touch $dir/.qmail-$qdomain-default; chown $uid:$gid $dir/.qmail-$qdomain-default; }")
- if ( ! $nossh_hack && $shellmachine && $dir && $self->domuser eq '*' );
-
- ''; #no error
-
-}
-
-=item delete
-
-Deletes this virtual mail alias from the database. If there is an error,
-returns the error, otherwise returns false.
-
-The corresponding FS::cust_svc record will be deleted as well.
-
-=cut
-
-sub delete {
- my($self)=@_;
- my($error);
-
- my($svcnum)=$self->getfield('svcnum');
-
- $error = $self->del;
- return $error if $error;
-
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
- $error = $cust_svc->del;
- return $error if $error;
-
- '';
-
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- my($error);
-
- return "(Old) Not a svc_acct_sm record!" unless $old->table eq "svc_acct_sm";
- return "Can't change svcnum!"
- unless $old->getfield('svcnum') eq $new->getfield('svcnum');
-
- return "Domain username (domuser) in use for this domain (domsvc)"
- if ( $old->domuser ne $new->domuser
- || $old->domsvc ne $new->domsvc
- ) && qsearchs('svc_acct_sm',{
- 'domuser'=> $new->domuser,
- 'domsvc' => $new->domsvc,
- } )
- ;
-
- $error=$new->check;
- return $error if $error;
-
- $error = $new->rep($old);
- return $error if $error;
-
- ''; #no error
-}
-
-=item suspend
-
-Just returns false (no error) for now.
-
-Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub suspend {
- ''; #no error (stub)
-}
-
-=item unsuspend
-
-Just returns false (no error) for now.
-
-Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub unsuspend {
- ''; #no error (stub)
-}
-
-=item cancel
-
-Just returns false (no error) for now.
-
-Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub cancel {
- ''; #no error (stub)
-}
-
-=item check
-
-Checks all fields to make sure this is a valid virtual mail alias. If there is
-an error, returns the error, otherwise returns false. Called by the insert and
-replace methods.
-
-Sets any fixed values; see L<FS::part_svc>.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a svc_acct_sm record!" unless $self->table eq "svc_acct_sm";
- my($recref) = $self->hashref;
-
- $recref->{svcnum} =~ /^(\d*)$/ or return "Illegal svcnum";
- $recref->{svcnum} = $1;
-
- #get part_svc
- my($svcpart);
- my($svcnum)=$self->getfield('svcnum');
- if ($svcnum) {
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
- return "Unknown svcnum" unless $cust_svc;
- $svcpart=$cust_svc->svcpart;
- } else {
- $svcpart=$self->getfield('svcpart');
- }
- my($part_svc)=qsearchs('part_svc',{'svcpart'=>$svcpart});
- return "Unkonwn svcpart" unless $part_svc;
-
- #set fixed fields from part_svc
- my($field);
- foreach $field ( fields('svc_acct_sm') ) {
- if ( $part_svc->getfield('svc_acct_sm__'. $field. '_flag') eq 'F' ) {
- $self->setfield($field,$part_svc->getfield('svc_acct_sm__'. $field) );
- }
- }
-
- $recref->{domuser} =~ /^(\*|[a-z0-9_\-]{2,32})$/
- or return "Illegal domain username (domuser)";
- $recref->{domuser} = $1;
-
- $recref->{domsvc} =~ /^(\d+)$/ or return "Illegal domsvc";
- $recref->{domsvc} = $1;
- my($svc_domain);
- return "Unknown domsvc" unless
- $svc_domain=qsearchs('svc_domain',{'svcnum'=> $recref->{domsvc} } );
-
- $recref->{domuid} =~ /^(\d+)$/ or return "Illegal uid";
- $recref->{domuid} = $1;
- my($svc_acct);
- return "Unknown uid" unless
- $svc_acct=qsearchs('svc_acct',{'uid'=> $recref->{domuid} } );
-
- ''; #no error
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-The remote commands should be configurable.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>,
-L<FS::svc_acct>, L<FS::svc_domain>, L<FS::SSH>, L<ssh>, L<dot-qmail>,
-schema.html from the base documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-16 - 21
-
-rewrite ivan@sisd.com 98-mar-10
-
-s/qsearchs/qsearch/ to eliminate warning ivan@sisd.com 98-apr-19
-
-uses conf/shellmachine and has an nossh_hack ivan@sisd.com 98-jul-14
-
-s/\./:/g in .qmail-domain:com ivan@sisd.com 98-aug-13
-
-pod, FS::Conf, moved .qmail file from check to insert 98-sep-23
-
-=cut
-
-1;
-
diff --git a/site_perl/svc_domain.pm b/site_perl/svc_domain.pm
deleted file mode 100644
index 1ddd5b2..0000000
--- a/site_perl/svc_domain.pm
+++ /dev/null
@@ -1,539 +0,0 @@
-package FS::svc_domain;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK $whois_hack $conf $mydomain $smtpmachine);
-use Exporter;
-use Carp;
-use Mail::Internet;
-use Mail::Header;
-use Date::Format;
-use FS::Record qw(fields qsearch qsearchs);
-use FS::cust_svc;
-use FS::Conf;
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-$conf = new FS::Conf;
-
-$mydomain = $conf->config('domain');
-$smtpmachine = $conf->config('smtpmachine');
-
-my($internic)="/var/spool/freeside/conf/registries/internic";
-my($conf_tech)="$internic/tech_contact";
-my($conf_from)="$internic/from";
-my($conf_to)="$internic/to";
-my($nameservers)="$internic/nameservers";
-my($template)="$internic/template";
-
-open(TECH_CONTACT,$conf_tech) or die "Can't open $conf_tech: $!";
-my($tech_contact)=map {
- /^(.*)$/ or die "Illegal line in $conf_tech!"; #yes, we trust the file
- $1;
-} grep $_ !~ /^(#|$)/, <TECH_CONTACT>;
-close TECH_CONTACT;
-
-open(FROM,$conf_from) or die "Can't open $conf_from: $!";
-my($from)=map {
- /^(.*)$/ or die "Illegal line in $conf_from!"; #yes, we trust the file
- $1;
-} grep $_ !~ /^(#|$)/, <FROM>;
-close FROM;
-
-open(TO,$conf_to) or die "Can't open $conf_to: $!";
-my($to)=map {
- /^(.*)$/ or die "Illegal line in $conf_to!"; #yes, we trust the file
- $1;
-} grep $_ !~ /^(#|$)/, <TO>;
-close TO;
-
-open(NAMESERVERS,$nameservers) or die "Can't open $nameservers: $!";
-my(@nameservers)=map {
- /^\s*\d+\.\d+\.\d+\.\d+\s+([^\s]+)\s*$/
- or die "Illegal line in $nameservers!"; #yes, we trust the file
- $1;
-} grep $_ !~ /^(#|$)/, <NAMESERVERS>;
-close NAMESERVERS;
-open(NAMESERVERS,$nameservers) or die "Can't open $nameservers: $!";
-my(@nameserver_ips)=map {
- /^\s*(\d+\.\d+\.\d+\.\d+)\s+([^\s]+)\s*$/
- or die "Illegal line in $nameservers!"; #yes, we trust the file
- $1;
-} grep $_ !~ /^(#|$)/, <NAMESERVERS>;
-close NAMESERVERS;
-
-open(TEMPLATE,$template) or die "Can't open $template: $!";
-my(@template)=map {
- /^(.*)$/ or die "Illegal line in $to!"; #yes, we trust the file
- $1. "\n";
-} <TEMPLATE>;
-close TEMPLATE;
-
-=head1 NAME
-
-FS::svc_domain - Object methods for svc_domain records
-
-=head1 SYNOPSIS
-
- use FS::svc_domain;
-
- $record = create FS::svc_domain \%hash;
- $record = create FS::svc_domain { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-=head1 DESCRIPTION
-
-An FS::svc_domain object represents a domain. FS::svc_domain inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item svcnum - primary key (assigned automatically for new accounts)
-
-=item domain
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Creates a new domain. To add the domain to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('svc_domain')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('svc_domain',$hashref);
-
-}
-
-=item insert
-
-Adds this domain to the database. If there is an error, returns the error,
-otherwise returns false.
-
-The additional fields I<pkgnum> and I<svcpart> (see L<FS::cust_svc>) should be
-defined. An FS::cust_svc record will be created and inserted.
-
-The additional field I<action> should be set to I<N> for new domains or I<M>
-for transfers.
-
-A registration or transfer email will be submitted unless
-$FS::svc_domain::whois_hack is true.
-
-=cut
-
-sub insert {
- my($self)=@_;
- my($error);
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error=$self->check;
- return $error if $error;
-
- return "Domain in use (here)"
- if qsearchs('svc_domain',{'domain'=> $self->domain } );
-
- my($whois)=(($self->_whois)[0]);
- return "Domain in use (see whois)"
- if ( $self->action eq "N" && $whois !~ /^No match for/ );
- return "Domain not found (see whois)"
- if ( $self->action eq "M" && $whois =~ /^No match for/ );
-
- my($svcnum)=$self->getfield('svcnum');
- my($cust_svc);
- unless ( $svcnum ) {
- $cust_svc=create FS::cust_svc ( {
- 'svcnum' => $svcnum,
- 'pkgnum' => $self->getfield('pkgnum'),
- 'svcpart' => $self->getfield('svcpart'),
- } );
- my($error) = $cust_svc->insert;
- return $error if $error;
- $svcnum = $self->setfield('svcnum',$cust_svc->getfield('svcnum'));
- }
-
- $error = $self->add;
- if ($error) {
- $cust_svc->del if $cust_svc;
- return $error;
- }
-
- $self->submit_internic unless $whois_hack;
-
- ''; #no error
-}
-
-=item delete
-
-Deletes this domain from the database. If there is an error, returns the
-error, otherwise returns false.
-
-The corresponding FS::cust_svc record will be deleted as well.
-
-=cut
-
-sub delete {
- my($self)=@_;
- my($error);
-
- my($svcnum)=$self->getfield('svcnum');
-
- $error = $self->del;
- return $error if $error;
-
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
- $error = $cust_svc->del;
- return $error if $error;
-
- '';
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- my($error);
-
- return "(Old) Not a svc_domain record!" unless $old->table eq "svc_domain";
- return "Can't change svcnum!"
- unless $old->getfield('svcnum') eq $new->getfield('svcnum');
-
- return "Can't change domain - reorder."
- if $old->getfield('domain') ne $new->getfield('domain');
-
- $error=$new->check;
- return $error if $error;
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error = $new->rep($old);
- return $error if $error;
-
- '';
-
-}
-
-=item suspend
-
-Just returns false (no error) for now.
-
-Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub suspend {
- ''; #no error (stub)
-}
-
-=item unsuspend
-
-Just returns false (no error) for now.
-
-Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub unsuspend {
- ''; #no error (stub)
-}
-
-=item cancel
-
-Just returns false (no error) for now.
-
-Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=cut
-
-sub cancel {
- ''; #no error (stub)
-}
-
-=item check
-
-Checks all fields to make sure this is a valid domain. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-Sets any fixed values; see L<FS::part_svc>.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a svc_domain record!" unless $self->table eq "svc_domain";
- my($recref) = $self->hashref;
-
- $recref->{svcnum} =~ /^(\d*)$/ or return "Illegal svcnum";
- $recref->{svcnum} = $1;
-
- #get part_svc (and pkgnum)
- my($svcpart,$pkgnum);
- my($svcnum)=$self->getfield('svcnum');
- if ($svcnum) {
- my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum});
- return "Unknown svcnum" unless $cust_svc;
- $svcpart=$cust_svc->svcpart;
- $pkgnum=$cust_svc->pkgnum;
- } else {
- $svcpart=$self->svcpart;
- $pkgnum=$self->pkgnum;
- }
- my($part_svc)=qsearchs('part_svc',{'svcpart'=>$svcpart});
- return "Unkonwn svcpart" unless $part_svc;
-
- #set fixed fields from part_svc
- my($field);
- foreach $field ( fields('svc_acct') ) {
- if ( $part_svc->getfield('svc_domain__'. $field. '_flag') eq 'F' ) {
- $self->setfield($field,$part_svc->getfield('svc_domain__'. $field) );
- }
- }
-
- unless ( $whois_hack ) {
- unless ( $self->email ) { #find out an email address
- my(@svc_acct);
- foreach ( qsearch('cust_svc',{'pkgnum'=>$pkgnum}) ) {
- my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$_->svcnum});
- push @svc_acct, $svc_acct if $svc_acct;
- }
-
- if ( scalar(@svc_acct) == 0 ) {
- return "Must order an account first";
- } elsif ( scalar(@svc_acct) > 1 ) {
- return "More than one account in package ". $pkgnum. ": specify admin contact email";
- } else {
- $self->email($svc_acct[0]->username. '@'. $mydomain);
- }
- }
- }
-
- #if ( $recref->{domain} =~ /^([\w\-\.]{1,22})\.(com|net|org|edu)$/ ) {
- if ( $recref->{domain} =~ /^([\w\-]{1,22})\.(com|net|org|edu)$/ ) {
- $recref->{domain} = "$1.$2";
- # hmmmmmmmm.
- } elsif ( $whois_hack && $recref->{domain} =~ /^([\w\-\.]+)$/ ) {
- $recref->{domain} = $1;
- } else {
- return "Illegal domain ". $recref->{domain}.
- " (or unknown registry - try \$whois_hack)";
- }
-
- $recref->{action} =~ /^(M|N)$/ or return "Illegal action";
- $recref->{action} = $1;
-
- $self->ut_textn('purpose');
-
-}
-
-=item _whois
-
-Executes the command:
-
- whois do $domain
-
-and returns the output.
-
-(Always returns I<No match for domian "$domain".> if
-$FS::svc_domain::whois_hack is set true.)
-
-=cut
-
-sub _whois {
- my($self)=@_;
- my($domain)=$self->domain;
- return ( "No match for domain \"$domain\"." ) if $whois_hack;
- open(WHOIS,"whois do $domain |");
- return <WHOIS>;
-}
-
-=item submit_internic
-
-Submits a registration email for this domain.
-
-=cut
-
-sub submit_internic {
- my($self)=@_;
-
- my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$self->pkgnum});
- return unless $cust_pkg;
- my($cust_main)=qsearchs('cust_main',{'custnum'=>$cust_pkg->custnum});
- return unless $cust_main;
-
- my(%subs)=(
- 'action' => $self->action,
- 'purpose' => $self->purpose,
- 'domain' => $self->domain,
- 'company' => $cust_main->company
- || $cust_main->getfield('first'). ' '.
- $cust_main->getfield('last')
- ,
- 'city' => $cust_main->city,
- 'state' => $cust_main->state,
- 'zip' => $cust_main->zip,
- 'country' => $cust_main->country,
- 'last' => $cust_main->getfield('last'),
- 'first' => $cust_main->getfield('first'),
- 'daytime' => $cust_main->daytime,
- 'fax' => $cust_main->fax,
- 'email' => $self->email,
- 'tech_contact' => $tech_contact,
- 'primary' => shift @nameservers,
- 'primary_ip' => shift @nameserver_ips,
- );
-
- #yuck
- my(@xtemplate)=@template;
- my(@body);
- my($line);
- OLOOP: while ( defined($line = shift @xtemplate) ) {
-
- if ( $line =~ /^###LOOP###$/ ) {
- my(@buffer);
- LOADBUF: while ( defined($line = shift @xtemplate) ) {
- last LOADBUF if ( $line =~ /^###ENDLOOP###$/ );
- push @buffer, $line;
- }
- my(%lubs)=(
- 'address' => $cust_main->address2
- ? [ $cust_main->address1, $cust_main->address2 ]
- : [ $cust_main->address1 ]
- ,
- 'secondary' => [ @nameservers ],
- 'secondary_ip' => [ @nameserver_ips ],
- );
- LOOP: while (1) {
- my(@xbuffer)=@buffer;
- SUBLOOP: while ( defined($line = shift @xbuffer) ) {
- if ( $line =~ /###(\w+)###/ ) {
- #last LOOP unless my($lub)=shift@{$lubs{$1}};
- next OLOOP unless my $lub = shift @{$lubs{$1}};
- $line =~ s/###(\w+)###/$lub/e;
- redo SUBLOOP;
- } else {
- push @body, $line;
- }
- } #SUBLOOP
- } #LOOP
-
- }
-
- if ( $line =~ /###(\w+)###/ ) {
- #$line =~ s/###(\w+)###/$subs{$1}/eg;
- $line =~ s/###(\w+)###/$subs{$1}/e;
- redo OLOOP;
- } else {
- push @body, $line;
- }
-
- } #OLOOP
-
- my($subject);
- if ( $self->action eq "M" ) {
- $subject = "MODIFY DOMAIN ". $self->domain;
- } elsif ($self->action eq "N" ) {
- $subject = "NEW DOMAIN ". $self->domain;
- } else {
- croak "submit_internic called with action ". $self->action;
- }
-
- $ENV{SMTPHOSTS}=$smtpmachine;
- $ENV{MAILADDRESS}=$from;
- my($header)=Mail::Header->new( [
- "From: $from",
- "To: $to",
- "Sender: $from",
- "Reply-To: $from",
- "Date: ". time2str("%a, %d %b %Y %X %z",time),
- "Subject: $subject",
- ] );
-
- my($msg)=Mail::Internet->new(
- 'Header' => $header,
- 'Body' => \@body,
- );
-
- $msg->smtpsend or die "Can't send registration email"; #die? warn?
-
-}
-
-=back
-
-=head1 BUGS
-
-It doesn't properly override FS::Record yet.
-
-All BIND/DNS fields should be included (and exported).
-
-All registries should be supported.
-
-Not all configuration access is through FS::Conf!
-
-Should change action to a real field.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>,
-L<FS::SSH>, L<ssh>, L<dot-qmail>, schema.html from the base documentation,
-config.html from the base documentation.
-
-=head1 HISTORY
-
-ivan@voicenet.com 97-jul-21
-
-rewrite ivan@sisd.com 98-mar-10
-
-add internic bits ivan@sisd.com 98-mar-14
-
-Changed 'day' to 'daytime' because Pg6.3 reserves the day word
- bmccane@maxbaud.net 98-apr-3
-
-/var/spool/freeside/conf/registries/internic/, Mail::Internet, etc.
-ivan@sisd.com 98-jul-17-19
-
-pod, some FS::Conf (not complete) ivan@sisd.com 98-sep-23
-
-=cut
-
-1;
-
-
diff --git a/site_perl/table_template-svc.pm b/site_perl/table_template-svc.pm
deleted file mode 100644
index a8cbaed..0000000
--- a/site_perl/table_template-svc.pm
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/local/bin/perl -Tw
-#
-# ivan@voicenet.com 97-jul-21
-
-package FS::svc_table;
-
-use strict;
-use Exporter;
-use FS::Record qw(fields qsearchs);
-
-@FS::svc_table::ISA = qw(FS::Record Exporter);
-
-# Usage: $record = create FS::svc_table ( \%hash );
-# $record = create FS::svc_table ( { field=>value, ... } );
-sub create {
- my($proto,$hashref)=@_;
-
- my($field);
- foreach $field (fields('svc_table')) {
- $hashref->{$field}='' unless defined $hashref->{$field};
- }
-
- $proto->new('svc_table',$hashref);
-
-}
-
-# Usage: $error = $record -> insert;
-sub insert {
- my($self)=@_;
- my($error);
-
- local $SIG{HUP} = 'IGNORE';
- local $SIG{INT} = 'IGNORE';
- local $SIG{QUIT} = 'IGNORE';
- local $SIG{TERM} = 'IGNORE';
- local $SIG{TSTP} = 'IGNORE';
-
- $error=$self->check;
- return $error if $error;
-
- $error = $self->add;
- return $error if $error;
-
- ''; #no error
-}
-
-# Usage: $error = $record -> delete;
-sub delete {
- my($self)=@_;
- my($error);
-
- $error = $self->del;
- return $error if $error;
-
-}
-
-# Usage: $error = $newrecord -> replace($oldrecord)
-sub replace {
- my($new,$old)=@_;
- my($error);
-
- return "(Old) Not a svc_table record!" unless $old->table eq "svc_table";
- return "Can't change svcnum!"
- unless $old->getfield('svcnum') eq $new->getfield('svcnum');
-
- $error=$new->check;
- return $error if $error;
-
- $error = $new->rep($old);
- return $error if $error;
-
- ''; #no error
-}
-
-# Usage: $error = $record -> suspend;
-sub suspend {
- ''; #no error (stub)
-}
-
-# Usage: $error = $record -> unsuspend;
-sub unsuspend {
- ''; #no error (stub)
-}
-
-# Usage: $error = $record -> cancel;
-sub cancel {
- ''; #no error (stub)
-}
-
-# Usage: $error = $record -> check;
-sub check {
- my($self)=@_;
- return "Not a svc_table record!" unless $self->table eq "svc_table";
- my($recref) = $self->hashref;
-
- $recref->{svcnum} =~ /^(\d+)$/ or return "Illegal svcnum";
- $recref->{svcnum} = $1;
- return "Unknown svcnum" unless
- qsearchs('cust_svc',{'svcnum'=> $recref->{svcnum} } );
-
- #DATA CHECKS GO HERE!
-
- ''; #no error
-}
-
-1;
-
diff --git a/site_perl/table_template-unique.pm b/site_perl/table_template-unique.pm
deleted file mode 100644
index 32b7e69..0000000
--- a/site_perl/table_template-unique.pm
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/local/bin/perl -Tw
-#
-# ivan@voicenet.com 97-jul-1
-#
-# added hfields
-# ivan@sisd.com 97-nov-13
-
-package FS::table_name;
-
-use strict;
-use Exporter;
-#use FS::UID qw(getotaker);
-use FS::Record qw(fields hfields qsearch qsearchs);
-
-@FS::table_name::ISA = qw(FS::Record Exporter);
-@FS::table_name::EXPORT_OK = qw(hfields);
-
-# Usage: $record = create FS::table_name ( \%hash );
-# $record = create FS::table_name ( { field=>value, ... } );
-sub create {
- my($proto,$hashref)=@_;
-
- my($field);
- foreach $field (fields('table_name')) {
- $hashref->{$field}='' unless defined $hashref->{$field};
- }
-
- $proto->new('table_name',$hashref);
-}
-
-# Usage: $error = $record -> insert;
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-# Usage: $error = $record -> delete;
-sub delete {
- my($self)=@_;
-
- $self->del;
-}
-
-# Usage: $error = $newrecord -> replace($oldrecord)
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a table_name record!" unless $old->table eq "table_name";
- return "Can't change keyfield!"
- unless $old->getfield('keyfield') eq $new->getfield('keyfield');
- $new->check or
- $new->rep($old);
-}
-
-# Usage: $error = $record -> check;
-sub check {
- my($self)=@_;
- return "Not a table_name record!" unless $self->table eq "table_name";
- my($recref) = $self->hashref;
-
- ''; #no error
-}
-
-1;
-
diff --git a/site_perl/table_template.pm b/site_perl/table_template.pm
deleted file mode 100644
index cef2d92..0000000
--- a/site_perl/table_template.pm
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/local/bin/perl -Tw
-#
-# ivan@voicenet.com 97-jul-1
-#
-# added hfields
-# ivan@sisd.com 97-nov-13
-
-package FS::table_name;
-
-use strict;
-use Exporter;
-#use FS::UID qw(getotaker);
-use FS::Record qw(hfields qsearch qsearchs);
-
-@FS::table_name::ISA = qw(FS::Record Exporter);
-@FS::table_name::EXPORT_OK = qw(hfields);
-
-# Usage: $record = create FS::table_name ( \%hash );
-# $record = create FS::table_name ( { field=>value, ... } );
-sub create {
- my($proto,$hashref)=@_;
-
- my($field);
- foreach $field (fields('table_name')) {
- $hashref->{$field}='' unless defined $hashref->{$field};
- }
-
- $proto->new('table_name',$hashref);
-
-}
-
-# Usage: $error = $record -> insert;
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-# Usage: $error = $record -> delete;
-sub delete {
- my($self)=@_;
-
- $self->del;
-}
-
-# Usage: $error = $newrecord -> replace($oldrecord)
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a table_name record!" unless $old->table eq "table_name";
-
- $new->check or
- $new->rep($old);
-}
-
-# Usage: $error = $record -> check;
-sub check {
- my($self)=@_;
- return "Not a table_name record!" unless $self->table eq "table_name";
- my($recref) = $self->hashref;
-
- ''; #no error
-}
-
-1;
-
diff --git a/site_perl/type_pkgs.pm b/site_perl/type_pkgs.pm
deleted file mode 100644
index a715796..0000000
--- a/site_perl/type_pkgs.pm
+++ /dev/null
@@ -1,150 +0,0 @@
-package FS::type_pkgs;
-
-use strict;
-use vars qw(@ISA @EXPORT_OK);
-use Exporter;
-use FS::Record qw(fields qsearchs);
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(fields);
-
-=head1 NAME
-
-FS::type_pkgs - Object methods for type_pkgs records
-
-=head1 SYNOPSIS
-
- use FS::type_pkgs;
-
- $record = create FS::type_pkgs \%hash;
- $record = create FS::type_pkgs { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::type_pkgs record links an agent type (see L<FS::agent_type>) to a
-billing item definition (see L<FS::part_pkg>). FS::type_pkgs inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item typenum - Agent type, see L<FS::agent_type>
-
-=item pkgpart - Billing item definition, see L<FS::part_pkg>
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item create HASHREF
-
-Create a new record. To add the record to the database, see L<"insert">.
-
-=cut
-
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('type_pkgs')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('type_pkgs',$hashref);
-
-}
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
- my($self)=@_;
-
- $self->check or
- $self->add;
-}
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=cut
-
-sub delete {
- my($self)=@_;
-
- $self->del;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a type_pkgs record!" unless $old->table eq "type_pkgs";
-
- $new->check or
- $new->rep($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid record. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my($self)=@_;
- return "Not a type_pkgs record!" unless $self->table eq "type_pkgs";
- my($recref) = $self->hashref;
-
- $recref->{typenum} =~ /^(\d+)$/ or return "Illegal typenum";
- $recref->{typenum} = $1;
- return "Unknown typenum"
- unless qsearchs('agent_type',{'typenum'=>$recref->{typenum}});
-
- $recref->{pkgpart} =~ /^(\d+)$/ or return "Illegal pkgpart";
- $recref->{pkgpart} = $1;
- return "Unknown pkgpart"
- unless qsearchs('part_pkg',{'pkgpart'=>$recref->{pkgpart}});
-
- ''; #no error
-}
-
-=back
-
-=head1 HISTORY
-
-Defines the relation between agent types and pkgparts
-(Which pkgparts can the different [types of] agents sell?)
-
-ivan@sisd.com 97-nov-13
-
-change to ut_ FS::Record, fixed bugs
-ivan@sisd.com 97-dec-10
-
-=cut
-
-1;
-
diff --git a/test/cgi-test b/test/cgi-test
new file mode 100755
index 0000000..85074d2
--- /dev/null
+++ b/test/cgi-test
@@ -0,0 +1,558 @@
+#!/usr/bin/perl -Tw
+#
+# This is the beginning of a test suite for the web interface.
+# It's also excellent for populating your database with some meaningful test
+# data. (a derivative is used by the web demo)
+# It only works on an empty database (probably need empty counters too, and
+# no arbirary RADIUS attributes).
+# Usage: cgi-test http://base.freeside.url/with/path/ username password
+# (Yes, if you were properly paranoid and are using SSL, you'll need to get
+# libwww-perl working with SSL to use this.)
+
+use strict;
+#use diagnostics;
+use subs qw( big_ugly_data_structure );
+use CGI;
+use LWP::UserAgent;
+
+my ( $base_url, $username, $password ) = ( shift, shift, shift );
+#trust 'em
+$base_url =~ /^(.*)$/; $base_url = $1;
+$username =~ /^(.*)$/; $username = $1;
+$password =~ /^(.*)$/; $password = $1;
+
+my @data = &big_ugly_data_structure;
+
+my $ua = new LWP::UserAgent;
+{
+ local $^W = 0;
+ eval '
+ sub LWP::UserAgent::get_basic_credentials {
+ #my $self = shift;
+ ( $username, $password );
+ }
+ ';
+}
+
+my $data;
+while ( $data = shift @data ) {
+ my $cgi = new CGI ( $data->{'params'} );
+ my $full_url = $base_url. $data->{'url'}. '?'. $cgi->query_string;
+ #my $request = new HTTP::Request( 'POST', $full_url );
+ my $request = new HTTP::Request( 'GET', $full_url );
+ my $response = $ua->request( $request );
+ if ( $response->is_redirect ) {
+ die "Unexpected redirect!\n".
+ "URL: $full_url\n".
+ "To: ". $response->base. "\n"
+ ;
+ } elsif ( $response->is_success ) {
+ my $location = $response->base;
+ my $expected_location = $data->{'location'};
+ #if ( $location =~ /^$base_url$expected_location$/ ) {
+ if ( $location eq $base_url. $expected_location ) {
+ #warn "cool, got expected response $location from $full_url\n";
+ } else {
+ die "Strange, regular response, but unexpected base!\n".
+ "URL: $full_url\n".
+ "Base : ". $response->base. "\n".
+ "Expected: $base_url$expected_location\n".
+ "Output: ". $response->content. "\n"
+ ;
+ }
+ } elsif ( $response->is_error ) {
+ die "Strange, I got an error\n".
+ "URL: $full_url\n".
+ "Error: ". $response->error_as_HTML. "\n".
+ "Output: ". $response->content. "\n"
+ ;
+ } elsif ( $response->is_info ) {
+ die "Strange, I got an info reponse\n".
+ "URL: $full_url\n".
+ "Output: ". $response->content. "\n"
+ ;
+ } else {
+ die "Really strange, got an unrecognized response from LWP::UserAgent!\n";
+ }
+}
+
+#---
+
+sub big_ugly_data_structure {
+
+ (
+ { 'url' => 'edit/process/part_svc.cgi',
+ 'params' => {
+ 'svcpart' => '',
+ 'svc' => 'Shell',
+ 'svcdb' => 'svc_acct',
+ 'svc_acct__popnum_flag' => '',
+ 'svc_acct__popnum' => '',
+ 'svc_acct__dir_flag' => '',
+ 'svc_acct__dir' => '',
+ 'svc_acct__username_flag' => '',
+ 'svc_acct__username' => '',
+ 'svc_acct__uid_flag' => '',
+ 'svc_acct__uid' => '',
+ 'svc_acct__quota_flag' => 'F',
+ 'svc_acct__quota' => '10',
+ 'svc_acct__slipip_flag' => 'F',
+ 'svc_acct__slipip' => '',
+ 'svc_acct___password_flag' => '',
+ 'svc_acct___password' => '',
+ 'svc_acct__gid_flag' => '',
+ 'svc_acct__gid' => '',
+ 'svc_acct__shell_flag' => 'D',
+ 'svc_acct__shell' => '/bin/sh',
+ 'svc_acct__finger_flag' => '',
+ 'svc_acct__finger' => '',
+ 'svc_domain__domain_flag' => '',
+ 'svc_domain__domain' => '',
+ 'svc_acct_sm__domuser_flag' => '',
+ 'svc_acct_sm__domuser' => '',
+ 'svc_acct_sm__domuid_flag' => '',
+ 'svc_acct_sm__domuid' => '',
+ 'svc_acct_sm__domsvc_flag' => '',
+ 'svc_acct_sm__domsvc' => '',
+ },
+ 'location' => 'browse/part_svc.cgi',
+ },
+ { 'url' => 'edit/process/part_svc.cgi',
+ 'params' => {
+ 'svcpart' => '',
+ 'svc' => 'SLIP/PPP',
+ 'svcdb' => 'svc_acct',
+ 'svc_acct__popnum_flag' => '',
+ 'svc_acct__popnum' => '',
+ 'svc_acct__dir_flag' => '',
+ 'svc_acct__dir' => '',
+ 'svc_acct__username_flag' => '',
+ 'svc_acct__username' => '',
+ 'svc_acct__uid_flag' => '',
+ 'svc_acct__uid' => '',
+ 'svc_acct__quota_flag' => 'F',
+ 'svc_acct__quota' => '10',
+ 'svc_acct__slipip_flag' => 'D',
+ 'svc_acct__slipip' => '0.0.0.0',
+ 'svc_acct___password_flag' => '',
+ 'svc_acct___password' => '',
+ 'svc_acct__gid_flag' => '',
+ 'svc_acct__gid' => '',
+ 'svc_acct__shell_flag' => 'D',
+ 'svc_acct__shell' => '/bin/sh',
+ 'svc_acct__finger_flag' => '',
+ 'svc_acct__finger' => '',
+ 'svc_domain__domain_flag' => '',
+ 'svc_domain__domain' => '',
+ 'svc_acct_sm__domuser_flag' => '',
+ 'svc_acct_sm__domuser' => '',
+ 'svc_acct_sm__domuid_flag' => '',
+ 'svc_acct_sm__domuid' => '',
+ 'svc_acct_sm__domsvc_flag' => '',
+ 'svc_acct_sm__domsvc' => '',
+ },
+ 'location' => 'browse/part_svc.cgi',
+ },
+ { 'url' => 'edit/process/part_svc.cgi',
+ 'params' => {
+ 'svcpart' => '',
+ 'svc' => 'POP Mailbox',
+ 'svcdb' => 'svc_acct',,
+ 'svc_acct__popnum_flag' => 'F',
+ 'svc_acct__popnum' => '',
+ 'svc_acct__dir_flag' => '',
+ 'svc_acct__dir' => '',
+ 'svc_acct__username_flag' => '',
+ 'svc_acct__username' => '',
+ 'svc_acct__uid_flag' => '',
+ 'svc_acct__uid' => '',
+ 'svc_acct__quota_flag' => 'F',
+ 'svc_acct__quota' => '10',
+ 'svc_acct__slipip_flag' => 'F',
+ 'svc_acct__slipip' => '',
+ 'svc_acct___password_flag' => '',
+ 'svc_acct___password' => '',
+ 'svc_acct__gid_flag' => '',
+ 'svc_acct__gid' => '',
+ 'svc_acct__shell_flag' => 'F',
+ 'svc_acct__shell' => '/bin/passwd',
+ 'svc_acct__finger_flag' => '',
+ 'svc_acct__finger' => '',
+ 'svc_domain__domain_flag' => '',
+ 'svc_domain__domain' => '',
+ 'svc_acct_sm__domuser_flag' => '',
+ 'svc_acct_sm__domuser' => '',
+ 'svc_acct_sm__domuid_flag' => '',
+ 'svc_acct_sm__domuid' => '',
+ 'svc_acct_sm__domsvc_flag' => '',
+ 'svc_acct_sm__domsvc' => '',
+ },
+ 'location' => 'browse/part_svc.cgi',
+ },
+ { 'url' => 'edit/process/part_svc.cgi',
+ 'params' => {
+ 'svcpart' => '',
+ 'svc' => 'Domain',
+ 'svcdb' => 'svc_domain',,
+ 'svc_acct__popnum_flag' => '',
+ 'svc_acct__popnum' => '',
+ 'svc_acct__dir_flag' => '',
+ 'svc_acct__dir' => '',
+ 'svc_acct__username_flag' => '',
+ 'svc_acct__username' => '',
+ 'svc_acct__uid_flag' => '',
+ 'svc_acct__uid' => '',
+ 'svc_acct__quota_flag' => '',
+ 'svc_acct__quota' => '',
+ 'svc_acct__slipip_flag' => '',
+ 'svc_acct__slipip' => '',
+ 'svc_acct___password_flag' => '',
+ 'svc_acct___password' => '',
+ 'svc_acct__gid_flag' => '',
+ 'svc_acct__gid' => '',
+ 'svc_acct__shell_flag' => '',
+ 'svc_acct__shell' => '',
+ 'svc_acct__finger_flag' => '',
+ 'svc_acct__finger' => '',
+ 'svc_domain__domain_flag' => '',
+ 'svc_domain__domain' => '',
+ 'svc_acct_sm__domuser_flag' => '',
+ 'svc_acct_sm__domuser' => '',
+ 'svc_acct_sm__domuid_flag' => '',
+ 'svc_acct_sm__domuid' => '',
+ 'svc_acct_sm__domsvc_flag' => '',
+ 'svc_acct_sm__domsvc' => '',
+ },
+ 'location' => 'browse/part_svc.cgi',
+ },
+ { 'url' => 'edit/process/part_svc.cgi',
+ 'params' => {
+ 'svcpart' => '',
+ 'svc' => 'Domain email alias',
+ 'svcdb' => 'svc_acct_sm',,
+ 'svc_acct__popnum_flag' => '',
+ 'svc_acct__popnum' => '',
+ 'svc_acct__dir_flag' => '',
+ 'svc_acct__dir' => '',
+ 'svc_acct__username_flag' => '',
+ 'svc_acct__username' => '',
+ 'svc_acct__uid_flag' => '',
+ 'svc_acct__uid' => '',
+ 'svc_acct__quota_flag' => '',
+ 'svc_acct__quota' => '',
+ 'svc_acct__slipip_flag' => '',
+ 'svc_acct__slipip' => '',
+ 'svc_acct___password_flag' => '',
+ 'svc_acct___password' => '',
+ 'svc_acct__gid_flag' => '',
+ 'svc_acct__gid' => '',
+ 'svc_acct__shell_flag' => '',
+ 'svc_acct__shell' => '',
+ 'svc_acct__finger_flag' => '',
+ 'svc_acct__finger' => '',
+ 'svc_domain__domain_flag' => '',
+ 'svc_domain__domain' => '',
+ 'svc_acct_sm__domuser_flag' => '',
+ 'svc_acct_sm__domuser' => '',
+ 'svc_acct_sm__domuid_flag' => '',
+ 'svc_acct_sm__domuid' => '',
+ 'svc_acct_sm__domsvc_flag' => '',
+ 'svc_acct_sm__domsvc' => '',
+ },
+ 'location' => 'browse/part_svc.cgi',
+ },
+
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Personal SLIP/PPP',
+ 'comment' => '$30/setup, $19.99/month',
+ 'setup' => '30',
+ 'recur' => '19.99',
+ 'freq' => '1',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '1',
+ 'pkg_svc3' => '0',
+ 'pkg_svc4' => '0',
+ 'pkg_svc5' => '0',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Personal SLIP/PPP',
+ 'comment' => '$0/setup, $179.88/year',
+ 'setup' => '0',
+ 'recur' => '179.88',
+ 'freq' => '12',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '1',
+ 'pkg_svc3' => '0',
+ 'pkg_svc4' => '0',
+ 'pkg_svc5' => '0',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Personal POP mailbox',
+ 'comment' => '$10/setup, $5/month',
+ 'setup' => '10',
+ 'recur' => '5',
+ 'freq' => '1',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '0',
+ 'pkg_svc3' => '1',
+ 'pkg_svc4' => '0',
+ 'pkg_svc5' => '0',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Business SLIP/PPP',
+ 'comment' => '$30/setup, $29.99/month',
+ 'setup' => '30',
+ 'recur' => '29.99',
+ 'freq' => '1',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '1',
+ 'pkg_svc3' => '0',
+ 'pkg_svc4' => '1',
+ 'pkg_svc5' => '1',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Business SLIP/PPP',
+ 'comment' => '$0/setup, $299.88/year',
+ 'setup' => '0',
+ 'recur' => '299.88',
+ 'freq' => '12',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '1',
+ 'pkg_svc3' => '0',
+ 'pkg_svc4' => '1',
+ 'pkg_svc5' => '1',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Business POP mailbox',
+ 'comment' => '$10/setup, $5/month',
+ 'setup' => '10',
+ 'recur' => '5',
+ 'freq' => '1',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '0',
+ 'pkg_svc3' => '1',
+ 'pkg_svc4' => '0',
+ 'pkg_svc5' => '1',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'UNIX shell',
+ 'comment' => '$20/setup, $9.99/month',
+ 'setup' => '20',
+ 'recur' => '9.99',
+ 'freq' => '1',
+ 'pkg_svc1' => '1',
+ 'pkg_svc2' => '0',
+ 'pkg_svc3' => '0',
+ 'pkg_svc4' => '0',
+ 'pkg_svc5' => '0',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Point-to-point T1',
+ 'comment' => '$1000/setup, $1000/month',
+ 'setup' => '1000',
+ 'recur' => '1000',
+ 'freq' => '1',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '0',
+ 'pkg_svc3' => '5',
+ 'pkg_svc4' => '1',
+ 'pkg_svc5' => '5',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+ { 'url' => 'edit/process/part_pkg.cgi',
+ 'params' => {
+ 'pkgpart' => '',
+ 'pkg' => 'Cisco 2501 Router',
+ 'comment' => '$2500',
+ 'setup' => '2500',
+ 'recur' => '0',
+ 'freq' => '0',
+ 'pkg_svc1' => '0',
+ 'pkg_svc2' => '0',
+ 'pkg_svc3' => '0',
+ 'pkg_svc4' => '0',
+ 'pkg_svc5' => '0',
+ },
+ 'location' => 'browse/part_pkg.cgi',
+ },
+
+ { 'url' => 'edit/process/agent_type.cgi',
+ 'params' => {
+ 'typenum' => '',
+ 'atype' => 'Internal Sales',
+ 'pkgpart1' => 'ON',
+ 'pkgpart2' => 'ON',
+ 'pkgpart3' => 'ON',
+ 'pkgpart4' => 'ON',
+ 'pkgpart5' => 'ON',
+ 'pkgpart6' => 'ON',
+ 'pkgpart7' => 'ON',
+ 'pkgpart8' => 'ON',
+ 'pkgpart9' => 'ON',
+ },
+ 'location' => 'browse/agent_type.cgi',
+ },
+
+ { 'url' => 'edit/process/agent.cgi',
+ 'params' => {
+ 'agentnum' => '',
+ 'agent' => 'Internal Sales',
+ 'typenum' => '1',
+ 'freq' => '',
+ 'prog' => '',
+ },
+ 'location' => 'browse/agent.cgi',
+ },
+
+ { 'url' => 'edit/process/part_referral.cgi',
+ 'params' => {
+ 'refnum' => '',
+ 'referral' => 'Another customer',
+ },
+ 'location' => 'browse/part_referral.cgi',
+ },
+ { 'url' => 'edit/process/part_referral.cgi',
+ 'params' => {
+ 'refnum' => '',
+ 'referral' => 'Newspaper ad',
+ },
+ 'location' => 'browse/part_referral.cgi',
+ },
+
+ { 'url' => 'edit/process/svc_acct_pop.cgi',
+ 'params' => {
+ 'popnum' => '',
+ 'city' => 'Line Lexington',
+ 'state' => 'PA',
+ 'ac' => '215',
+ 'exch' => '996',
+ },
+ 'location' => 'browse/svc_acct_pop.cgi',
+ },
+ { 'url' => 'edit/process/svc_acct_pop.cgi',
+ 'params' => {
+ 'popnum' => '',
+ 'city' => 'Oakland',
+ 'state' => 'CA',
+ 'ac' => '510',
+ 'exch' => '208',
+ },
+ 'location' => 'browse/svc_acct_pop.cgi',
+ },
+
+ { 'url' => 'edit/process/cust_main.cgi',
+ 'params' => {
+ 'custnum' => '',
+ 'agentnum' => '1',
+ 'refnum' => '1',
+ 'last' => 'Hogan',
+ 'first' => 'Shawn D.',
+ 'ss' => '',
+ 'company' => 'Digital Point Solutions',
+ 'address1' => '3570 Tony Drive',
+ 'address2' => '',
+ 'city' => 'San Diego',
+ 'state' => 'CA / US',
+ 'zip' => '92122-2307',
+ 'daytime' => '',
+ 'night' => '',
+ 'fax' => '',
+ 'tax' => '',
+ 'invoicing_list_POST' => '',
+ 'invoicing_list' => '',
+ 'payby' => 'BILL',
+ 'CARD_payinfo' => '',
+ 'CARD_month' => '1',
+ 'CARD_year' => '1999',
+ 'CARD_payname' => '',
+ 'BILL_payinfo' => '',
+ 'BILL_month' => '12',
+ 'BILL_year' => '2037',
+ 'BILL_payname' => 'Accounts Payable',
+ 'COMP_payinfo' => '',
+ 'COMP_month' => '1',
+ 'COMP_year' => '1999',
+ 'pkgpart_svcpart' => '1_2',
+ 'username' => 'cyborg',
+ '_password' => '',
+ 'popnum' => '1',
+ 'otaker' => 'example',
+ },
+ 'location' => 'view/cust_main.cgi?1',
+ },
+ { 'url' => 'edit/process/cust_main.cgi',
+ 'params' => {
+ 'custnum' => '',
+ 'agentnum' => '1',
+ 'refnum' => '2',
+ 'last' => 'Ford',
+ 'first' => 'Bill',
+ 'ss' => '',
+ 'company' => 'Boardtown Corporation',
+ 'address1' => '116 East Main Street',
+ 'address2' => '',
+ 'city' => 'Starkville',
+ 'state' => 'MS / US',
+ 'zip' => '39759',
+ 'daytime' => '',
+ 'night' => '',
+ 'fax' => '',
+ 'tax' => '',
+ 'invoicing_list_POST' => '',
+ 'invoicing_list' => '',
+ 'payby' => 'BILL',
+ 'CARD_payinfo' => '',
+ 'CARD_month' => '1',
+ 'CARD_year' => '1999',
+ 'CARD_payname' => '',
+ 'BILL_payinfo' => '',
+ 'BILL_month' => '12',
+ 'BILL_year' => '2037',
+ 'BILL_payname' => 'Accounts Payable',
+ 'COMP_payinfo' => '',
+ 'COMP_month' => '1',
+ 'COMP_year' => '1999',
+ 'pkgpart_svcpart' => '3_3',
+ 'username' => 'billf',
+ '_password' => '',
+ 'popnum' => '',
+ 'otaker' => 'example',
+ },
+ 'location' => 'view/cust_main.cgi?2',
+ },
+
+
+ );
+}
+
diff --git a/test/dup-test b/test/dup-test
new file mode 100755
index 0000000..b073cee
--- /dev/null
+++ b/test/dup-test
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+use FS::UID qw(adminsuidsetup);
+use FS::svc_acct;
+
+my $user = 'ivan';
+my $svcpart = '2';
+
+my $counter = 10;
+
+my $pid = open(KID_TO_WRITE, "-|");
+
+if ( $pid ) { #parent
+ doit();
+} else { #kid
+ doit();
+ exit;
+}
+
+sub doit {
+
+ adminsuidsetup $user or die;
+
+ my $svc_acct = new FS::svc_acct ( {
+ 'svcpart' => $svcpart,
+ 'username' => "dup$counter",
+ } );
+ my $error = $svc_acct->insert;
+ warn $error if $error;
+
+}
+